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 : }
|