LCOV - code coverage report
Current view: top level - core/core/utils - SPRef.cc (source / functions) Hit Total Coverage
Test: coverage.info Lines: 128 138 92.8 %
Date: 2024-05-12 00:16:13 Functions: 18 20 90.0 %

          Line data    Source code
       1             : /**
       2             : Copyright (c) 2017-2022 Roman Katuntsev <sbkarr@stappler.org>
       3             : Copyright (c) 2023 Stappler LLC <admin@stappler.dev>
       4             : 
       5             : Permission is hereby granted, free of charge, to any person obtaining a copy
       6             : of this software and associated documentation files (the "Software"), to deal
       7             : in the Software without restriction, including without limitation the rights
       8             : to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
       9             : copies of the Software, and to permit persons to whom the Software is
      10             : furnished to do so, subject to the following conditions:
      11             : 
      12             : The above copyright notice and this permission notice shall be included in
      13             : all copies or substantial portions of the Software.
      14             : 
      15             : THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
      16             : IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      17             : FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
      18             : AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      19             : LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
      20             : OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
      21             : THE SOFTWARE.
      22             : **/
      23             : 
      24             : #include "SPRef.h"
      25             : #include "SPLog.h"
      26             : #include "SPSubscription.h"
      27             : #include "SPTime.h"
      28             : 
      29             : #if WIN32
      30             : #else
      31             : #include <cxxabi.h>
      32             : #endif
      33             : 
      34             : namespace STAPPLER_VERSIONIZED stappler::backtrace {
      35             : 
      36        2550 : static StringView filepath_lastComponent(StringView path) {
      37        2550 :         size_t pos = path.rfind('/');
      38        2550 :         if (pos != maxOf<size_t>()) {
      39        2550 :                 return path.sub(pos + 1);
      40             :         } else {
      41           0 :                 return path;
      42             :         }
      43             : }
      44             : 
      45        2550 : static StringView filepath_name(StringView path) {
      46        2550 :         auto cmp = filepath_lastComponent(path);
      47             : 
      48        2550 :         size_t pos = cmp.find('.');
      49        2550 :         if (pos == maxOf<size_t>()) {
      50           0 :                 return cmp;
      51             :         } else {
      52        2550 :                 return cmp.sub(0, pos);
      53             :         }
      54             : }
      55             : 
      56        2725 : SPUNUSED static size_t print(char *buf, size_t bufLen, uintptr_t pc, StringView filename, int lineno, StringView function) {
      57        2725 :         char *target = buf;
      58        2725 :         auto w = ::snprintf(target, bufLen, "[%p]", (void *)pc);
      59        2725 :         bufLen -= w;
      60        2725 :         target += w;
      61             : 
      62        2725 :         if (!filename.empty()) {
      63        2550 :                 auto name = filepath_name(filename);
      64        2550 :                 if (lineno >= 0) {
      65        2550 :                         w = ::snprintf(target, bufLen, " %.*s:%d", int(name.size()), name.data(), lineno);
      66             :                 } else {
      67           0 :                         w = ::snprintf(target, bufLen, " %.*s", int(name.size()), name.data());
      68             :                 }
      69        2550 :                 bufLen -= w;
      70        2550 :                 target += w;
      71             :         }
      72             : 
      73        2725 :         if (!function.empty()) {
      74             : #if WIN32
      75             :                 w = ::snprintf(target, bufLen, " - %.*s", int(function.size()), function.data());
      76             :                 bufLen -= w;
      77             :                 target += w;
      78             : #else
      79        2500 :                 int status = 0;
      80        2500 :                 auto ptr = abi::__cxa_demangle(function.data(), nullptr, nullptr, &status);
      81        2500 :                 if (ptr) {
      82        1675 :                         w = ::snprintf(target, bufLen, " - %s", ptr);
      83        1675 :                         bufLen -= w;
      84        1675 :                         target += w;
      85        1675 :                         ::free(ptr);
      86             :                 } else {
      87         825 :                         w = ::snprintf(target, bufLen, " - %.*s", int(function.size()), function.data());
      88         825 :                         bufLen -= w;
      89         825 :                         target += w;
      90             :                 }
      91             : #endif
      92             :         }
      93        2725 :         return target - buf;
      94             : }
      95             : 
      96             : }
      97             : 
      98             : #if MODULE_STAPPLER_BACKTRACE
      99             : 
     100             : #include "backtrace.h"
     101             : 
     102             : namespace STAPPLER_VERSIONIZED stappler {
     103             : 
     104           0 : static void debug_backtrace_error(void *data, const char *msg, int errnum) {
     105           0 :         std::cout << "[Backtrace] error: " << msg << "\n";
     106           0 : }
     107             : 
     108        2900 : static int debug_backtrace_full_callback(void *data, uintptr_t pc, const char *filename, int lineno, const char *function) {
     109        2900 :         if (pc != uintptr_t(0xffffffffffffffffLLU)) {
     110        2725 :                 auto ret = (const Callback<void(StringView)> *)data;
     111        2725 :                 char buf[1024] = { 0 };
     112        2725 :                 auto size = backtrace::print(buf, 1024, pc, filename, lineno, function);
     113        2725 :                 (*ret)(StringView(buf, size));
     114             :         }
     115        2900 :         return 0;
     116             : }
     117             : 
     118             : struct BacktraceState {
     119         175 :         static BacktraceState *getInstance() {
     120             :                 static std::mutex s_mutex;
     121             :                 static BacktraceState * s_instance = nullptr;
     122             : 
     123         175 :                 s_mutex.lock();
     124         175 :                 if (!s_instance) {
     125          25 :                         s_instance = new BacktraceState();
     126             :                 }
     127         175 :                 s_mutex.unlock();
     128         175 :                 return s_instance;
     129             :         }
     130             : 
     131          25 :         BacktraceState() {
     132          25 :                 _backtraceState = ::backtrace_create_state(nullptr, 1, debug_backtrace_error, nullptr);
     133          25 :         }
     134             : 
     135         175 :         void getBacktrace(size_t offset, const Callback<void(StringView)> &cb) {
     136         175 :                 ::backtrace_full(_backtraceState, 2 + offset, debug_backtrace_full_callback, debug_backtrace_error, (void *)&cb);
     137         175 :         }
     138             : 
     139             :         ::backtrace_state *_backtraceState;
     140             : };
     141             : 
     142         175 : void getBacktrace(size_t offset, const Callback<void(StringView)> &cb) {
     143         175 :         BacktraceState::getInstance()->getBacktrace(offset, cb);
     144         175 : }
     145             : 
     146             : }
     147             : 
     148             : #elif LINUX
     149             : 
     150             : #include <execinfo.h>
     151             : 
     152             : namespace STAPPLER_VERSIONIZED stappler {
     153             : 
     154             : static constexpr int LinuxBacktraceSize = 128;
     155             : static constexpr int LinuxBacktraceOffset = 2;
     156             : 
     157             : void getBacktrace(size_t offset, const Callback<void(StringView)> &cb) {
     158             :         void *bt[LinuxBacktraceSize + LinuxBacktraceOffset + offset];
     159             :         char **bt_syms;
     160             :         int bt_size;
     161             : 
     162             :         bt_size = ::backtrace(bt, LinuxBacktraceSize + LinuxBacktraceOffset + offset);
     163             :         bt_syms = ::backtrace_symbols(bt, bt_size);
     164             : 
     165             :         for (int i = LinuxBacktraceOffset + offset; i < bt_size; i++) {
     166             :                 StringView str(bt_syms[i]);
     167             : 
     168             :                 auto first = str.find('(');
     169             :                 auto second = str.rfind('+');
     170             : 
     171             :                 char buf[1024] = { 0 };
     172             :                 auto size = backtrace::print(buf, 1024, (uintptr_t) bt[i], StringView(str, first), -1, StringView(str, first + 1, second - first - 1));
     173             : 
     174             :                 cb(StringView(buf, size));
     175             :         }
     176             : 
     177             :         ::free(bt_syms);
     178             : }
     179             : 
     180             : }
     181             : 
     182             : #elif __APPLE__
     183             : 
     184             : #include <libunwind.h>
     185             : #include <inttypes.h>
     186             : 
     187             : namespace STAPPLER_VERSIONIZED stappler {
     188             : 
     189             : void getBacktrace(size_t offset, const Callback<void(StringView)> &cb) {
     190             :         unw_cursor_t cursor;
     191             :         unw_context_t context;
     192             :         unw_getcontext(&context);
     193             :         unw_init_local(&cursor, &context);
     194             : 
     195             :         char buf[1024] = { 0 };
     196             : 
     197             :         while (unw_step(&cursor)) {
     198             :                 if (offset > 0) {
     199             :                         -- offset;
     200             :                         continue;
     201             :                 }
     202             : 
     203             :                 unw_word_t ip, sp, off;
     204             : 
     205             :                 unw_get_reg(&cursor, UNW_REG_IP, &ip);
     206             :                 unw_get_reg(&cursor, UNW_REG_SP, &sp);
     207             : 
     208             :                 char symbol[1024] = { "<unknown>" };
     209             : 
     210             :                 unw_get_proc_name(&cursor, symbol, sizeof(symbol), &off);
     211             :                 auto size = backtrace::print(buf, 1024, (uintptr_t) off, StringView(), -1, StringView(symbol));
     212             : 
     213             :                 cb(StringView(buf, size));
     214             :         }
     215             : }
     216             : 
     217             : }
     218             : 
     219             : #else
     220             : 
     221             : namespace STAPPLER_VERSIONIZED stappler {
     222             : 
     223             : void getBacktrace(size_t offset, const Callback<void(StringView)> &cb) { }
     224             : 
     225             : }
     226             : 
     227             : #endif
     228             : 
     229             : 
     230             : namespace STAPPLER_VERSIONIZED stappler::memleak {
     231             : 
     232             : static std::mutex s_mutex;
     233             : static std::atomic<uint64_t> s_refId = 1;
     234             : 
     235             : struct BackraceInfo {
     236             :         Time t;
     237             :         std::vector<std::string> backtrace;
     238             : };
     239             : 
     240             : static std::map<const RefBase<memory::StandartInterface> *, std::map<uint64_t, BackraceInfo>> s_retainStdMap;
     241             : static std::map<const RefBase<memory::PoolInterface> *, std::map<uint64_t, BackraceInfo>> s_retainPoolMap;
     242             : 
     243         100 : uint64_t getNextRefId() {
     244         100 :         return s_refId.fetch_add(1);
     245             : }
     246             : 
     247          50 : uint64_t retainBacktrace(const RefBase<memory::StandartInterface> *ptr) {
     248          50 :         auto id = getNextRefId();
     249          50 :         std::vector<std::string> bt;
     250          50 :         getBacktrace(0, [&] (StringView str) {
     251         550 :                 bt.emplace_back(str.str<memory::StandartInterface>());
     252         550 :         });
     253          50 :         s_mutex.lock();
     254             : 
     255          50 :         auto it = s_retainStdMap.find(ptr);
     256          50 :         if (it == s_retainStdMap.end()) {
     257          25 :                 it = s_retainStdMap.emplace(ptr, std::map<uint64_t, BackraceInfo>()).first;
     258             :         }
     259             : 
     260          50 :         auto iit = it->second.find(id);
     261          50 :         if (iit == it->second.end()) {
     262          50 :                 it->second.emplace(id, BackraceInfo{Time::now(), move(bt)});
     263             :         }
     264             : 
     265          50 :         s_mutex.unlock();
     266          50 :         return id;
     267          50 : }
     268             : 
     269          50 : void releaseBacktrace(const RefBase<memory::StandartInterface> *ptr, uint64_t id) {
     270          50 :         if (!id) {
     271           0 :                 return;
     272             :         }
     273             : 
     274          50 :         s_mutex.lock();
     275             : 
     276          50 :         auto it = s_retainStdMap.find(ptr);
     277          50 :         if (it != s_retainStdMap.end()) {
     278          50 :                 auto iit = it->second.find(id);
     279          50 :                 if (iit != it->second.end()) {
     280          50 :                         it->second.erase(iit);
     281             :                 }
     282          50 :                 if (it->second.size() == 0) {
     283          25 :                         s_retainStdMap.erase(it);
     284             :                 }
     285             :         }
     286             : 
     287          50 :         s_mutex.unlock();
     288             : }
     289             : 
     290          50 : void foreachBacktrace(const RefBase<memory::StandartInterface> *ptr,
     291             :                 const Callback<void(uint64_t, Time, const std::vector<std::string> &)> &cb) {
     292          50 :         s_mutex.lock();
     293             : 
     294          50 :         auto it = s_retainStdMap.find(ptr);
     295          50 :         if (it != s_retainStdMap.end()) {
     296         125 :                 for (auto &iit : it->second) {
     297          75 :                         cb(iit.first, iit.second.t, iit.second.backtrace);
     298             :                 }
     299             :         }
     300             : 
     301          50 :         s_mutex.unlock();
     302          50 : }
     303             : 
     304          50 : uint64_t retainBacktrace(const RefBase<memory::PoolInterface> *ptr) {
     305          50 :         auto id = getNextRefId();
     306          50 :         std::vector<std::string> bt;
     307          50 :         getBacktrace(0, [&] (StringView str) {
     308         550 :                 bt.emplace_back(str.str<memory::StandartInterface>());
     309         550 :         });
     310          50 :         s_mutex.lock();
     311             : 
     312          50 :         auto it = s_retainPoolMap.find(ptr);
     313          50 :         if (it == s_retainPoolMap.end()) {
     314          25 :                 it = s_retainPoolMap.emplace(ptr, std::map<uint64_t, BackraceInfo>()).first;
     315             :         }
     316             : 
     317          50 :         auto iit = it->second.find(id);
     318          50 :         if (iit == it->second.end()) {
     319          50 :                 it->second.emplace(id, BackraceInfo{Time::now(), move(bt)});
     320             :         }
     321             : 
     322          50 :         s_mutex.unlock();
     323          50 :         return id;
     324          50 : }
     325             : 
     326          50 : void releaseBacktrace(const RefBase<memory::PoolInterface> *ptr, uint64_t id) {
     327          50 :         if (!id) {
     328           0 :                 return;
     329             :         }
     330             : 
     331          50 :         s_mutex.lock();
     332             : 
     333          50 :         auto it = s_retainPoolMap.find(ptr);
     334          50 :         if (it != s_retainPoolMap.end()) {
     335          50 :                 auto iit = it->second.find(id);
     336          50 :                 if (iit != it->second.end()) {
     337          50 :                         it->second.erase(iit);
     338             :                 }
     339          50 :                 if (it->second.size() == 0) {
     340          25 :                         s_retainPoolMap.erase(it);
     341             :                 }
     342             :         }
     343             : 
     344          50 :         s_mutex.unlock();
     345             : }
     346             : 
     347          50 : void foreachBacktrace(const RefBase<memory::PoolInterface> *ptr,
     348             :                 const Callback<void(uint64_t, Time, const std::vector<std::string> &)> &cb) {
     349          50 :         s_mutex.lock();
     350             : 
     351          50 :         auto it = s_retainPoolMap.find(ptr);
     352          50 :         if (it != s_retainPoolMap.end()) {
     353         125 :                 for (auto &iit : it->second) {
     354          75 :                         cb(iit.first, iit.second.t, iit.second.backtrace);
     355             :                 }
     356             :         }
     357             : 
     358          50 :         s_mutex.unlock();
     359          50 : }
     360             : 
     361             : }
     362             : 
     363             : namespace STAPPLER_VERSIONIZED stappler {
     364             : 
     365             : template <>
     366           0 : SubscriptionId SubscriptionTemplate<memory::PoolInterface>::getNextId() {
     367             :         static std::atomic<SubscriptionId::Type> nextId(0);
     368           0 :         return Id(nextId.fetch_add(1));
     369             : }
     370             : 
     371             : template <>
     372        1166 : SubscriptionId SubscriptionTemplate<memory::StandartInterface>::getNextId() {
     373             :         static std::atomic<SubscriptionId::Type> nextId(0);
     374        1166 :         return Id(nextId.fetch_add(1));
     375             : }
     376             : 
     377             : }

Generated by: LCOV version 1.14