/src/libtorrent/src/assert.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | |
3 | | Copyright (c) 2007-2020, Arvid Norberg |
4 | | Copyright (c) 2008, Andrew Resch |
5 | | Copyright (c) 2016-2017, Alden Torres |
6 | | Copyright (c) 2017, Steven Siloti |
7 | | Copyright (c) 2020, Tiger Wang |
8 | | All rights reserved. |
9 | | |
10 | | Redistribution and use in source and binary forms, with or without |
11 | | modification, are permitted provided that the following conditions |
12 | | are met: |
13 | | |
14 | | * Redistributions of source code must retain the above copyright |
15 | | notice, this list of conditions and the following disclaimer. |
16 | | * Redistributions in binary form must reproduce the above copyright |
17 | | notice, this list of conditions and the following disclaimer in |
18 | | the documentation and/or other materials provided with the distribution. |
19 | | * Neither the name of the author nor the names of its |
20 | | contributors may be used to endorse or promote products derived |
21 | | from this software without specific prior written permission. |
22 | | |
23 | | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
24 | | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
25 | | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
26 | | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
27 | | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
28 | | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
29 | | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
30 | | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
31 | | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
32 | | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
33 | | POSSIBILITY OF SUCH DAMAGE. |
34 | | |
35 | | */ |
36 | | |
37 | | #include "libtorrent/config.hpp" |
38 | | #include "libtorrent/assert.hpp" |
39 | | |
40 | | #include "libtorrent/aux_/disable_warnings_push.hpp" |
41 | | |
42 | | #ifdef TORRENT_PRODUCTION_ASSERTS |
43 | | #include <atomic> |
44 | | #endif |
45 | | |
46 | | #if TORRENT_USE_ASSERTS \ |
47 | | || defined TORRENT_ASIO_DEBUGGING \ |
48 | | || defined TORRENT_PROFILE_CALLS \ |
49 | | || defined TORRENT_DEBUG_BUFFERS |
50 | | |
51 | | #ifdef __APPLE__ |
52 | | #include <AvailabilityMacros.h> |
53 | | #endif |
54 | | |
55 | | #include <string> |
56 | | #include <cstring> |
57 | | #include <cstdlib> |
58 | | #include <cstdarg> |
59 | | #include <cstdio> // for snprintf |
60 | | #include <cinttypes> // for PRId64 et.al. |
61 | | #include <array> |
62 | | |
63 | | #include "libtorrent/aux_/disable_warnings_pop.hpp" |
64 | | |
65 | | // uClibc++ doesn't have cxxabi.h |
66 | | #if defined __GNUC__ && __GNUC__ >= 3 \ |
67 | | && !defined __UCLIBCXX_MAJOR__ |
68 | | |
69 | | #include <cxxabi.h> |
70 | | |
71 | | namespace libtorrent { |
72 | | std::string demangle(char const* name) |
73 | 0 | { |
74 | | // in case this string comes |
75 | | // this is needed on linux |
76 | 0 | char const* start = std::strchr(name, '('); |
77 | 0 | if (start != nullptr) |
78 | 0 | { |
79 | 0 | ++start; |
80 | 0 | } |
81 | 0 | else |
82 | 0 | { |
83 | | // this is needed on macos x |
84 | 0 | start = strstr(name, "0x"); |
85 | 0 | if (start != nullptr) |
86 | 0 | { |
87 | 0 | start = std::strchr(start, ' '); |
88 | 0 | if (start != nullptr) ++start; |
89 | 0 | else start = name; |
90 | 0 | } |
91 | 0 | else start = name; |
92 | 0 | } |
93 | |
|
94 | 0 | char const* end = std::strchr(start, '+'); |
95 | 0 | if (end) while (*(end-1) == ' ') --end; |
96 | |
|
97 | 0 | std::string in; |
98 | 0 | if (end == nullptr) in.assign(start); |
99 | 0 | else in.assign(start, end); |
100 | |
|
101 | 0 | size_t len; |
102 | 0 | int status; |
103 | 0 | char* unmangled = ::abi::__cxa_demangle(in.c_str(), nullptr, &len, &status); |
104 | 0 | if (unmangled == nullptr) return in; |
105 | 0 | std::string ret(unmangled); |
106 | 0 | ::free(unmangled); |
107 | 0 | return ret; |
108 | 0 | } |
109 | | } |
110 | | #elif defined _WIN32 && !defined TORRENT_WINRT |
111 | | |
112 | | #include "libtorrent/aux_/windows.hpp" |
113 | | #include <DbgHelp.h> |
114 | | |
115 | | namespace libtorrent { |
116 | | std::string demangle(char const* name) |
117 | | { |
118 | | char demangled_name[256]; |
119 | | if (UnDecorateSymbolName(name, demangled_name, sizeof(demangled_name), UNDNAME_NO_THROW_SIGNATURES) == 0) |
120 | | demangled_name[0] = 0; |
121 | | return demangled_name; |
122 | | } |
123 | | } |
124 | | |
125 | | #else |
126 | | namespace libtorrent { |
127 | | std::string demangle(char const* name) { return name; } |
128 | | } |
129 | | #endif |
130 | | |
131 | | #include <cstdlib> |
132 | | #include <cstdio> |
133 | | #include <csignal> |
134 | | #include "libtorrent/version.hpp" |
135 | | |
136 | | #if TORRENT_USE_EXECINFO |
137 | | #include <execinfo.h> |
138 | | |
139 | | namespace libtorrent { |
140 | | |
141 | | TORRENT_EXPORT void print_backtrace(char* out, int len, int max_depth, void*) |
142 | 0 | { |
143 | 0 | void* stack[50]; |
144 | 0 | int size = ::backtrace(stack, 50); |
145 | 0 | char** symbols = ::backtrace_symbols(stack, size); |
146 | |
|
147 | 0 | for (int i = 1; i < size && len > 0; ++i) |
148 | 0 | { |
149 | 0 | int ret = std::snprintf(out, std::size_t(len), "%d: %s\n", i, demangle(symbols[i]).c_str()); |
150 | 0 | out += ret; |
151 | 0 | len -= ret; |
152 | 0 | if (i - 1 == max_depth && max_depth > 0) break; |
153 | 0 | } |
154 | |
|
155 | 0 | ::free(symbols); |
156 | 0 | } |
157 | | } |
158 | | |
159 | | #elif defined _WIN32 && !defined TORRENT_WINRT |
160 | | |
161 | | #include "libtorrent/aux_/windows.hpp" |
162 | | #include "libtorrent/utf8.hpp" |
163 | | #include <mutex> |
164 | | |
165 | | #include <WinBase.h> |
166 | | #include <DbgHelp.h> |
167 | | |
168 | | namespace libtorrent { |
169 | | |
170 | | TORRENT_EXPORT void print_backtrace(char* out, int len, int max_depth |
171 | | , void* ctx) |
172 | | { |
173 | | // all calls to DbgHlp.dll are thread-unsafe. i.e. they all need to be |
174 | | // synchronized and not called concurrently. This mutex serializes access |
175 | | static std::mutex dbghlp_mutex; |
176 | | std::lock_guard<std::mutex> l(dbghlp_mutex); |
177 | | |
178 | | CONTEXT context_record; |
179 | | if (ctx) |
180 | | { |
181 | | context_record = *static_cast<CONTEXT*>(ctx); |
182 | | } |
183 | | else |
184 | | { |
185 | | // use the current thread's context |
186 | | RtlCaptureContext(&context_record); |
187 | | } |
188 | | |
189 | | int size = 0; |
190 | | std::array<void*, 50> stack; |
191 | | |
192 | | STACKFRAME64 stack_frame = {}; |
193 | | #if defined(_M_IX86) |
194 | | int const machine_type = IMAGE_FILE_MACHINE_I386; |
195 | | stack_frame.AddrPC.Offset = context_record.Eip; |
196 | | stack_frame.AddrFrame.Offset = context_record.Ebp; |
197 | | stack_frame.AddrStack.Offset = context_record.Esp; |
198 | | #elif defined(_M_X64) |
199 | | int const machine_type = IMAGE_FILE_MACHINE_AMD64; |
200 | | stack_frame.AddrPC.Offset = context_record.Rip; |
201 | | stack_frame.AddrFrame.Offset = context_record.Rbp; |
202 | | stack_frame.AddrStack.Offset = context_record.Rsp; |
203 | | #elif defined(_M_ARM64) |
204 | | int const machine_type = IMAGE_FILE_MACHINE_ARM64; |
205 | | stack_frame.AddrPC.Offset = context_record.Pc; |
206 | | stack_frame.AddrFrame.Offset = context_record.Fp; |
207 | | stack_frame.AddrStack.Offset = context_record.Sp; |
208 | | #endif |
209 | | stack_frame.AddrPC.Mode = AddrModeFlat; |
210 | | stack_frame.AddrFrame.Mode = AddrModeFlat; |
211 | | stack_frame.AddrStack.Mode = AddrModeFlat; |
212 | | while (StackWalk64(machine_type, |
213 | | GetCurrentProcess(), |
214 | | GetCurrentThread(), |
215 | | &stack_frame, |
216 | | &context_record, |
217 | | nullptr, |
218 | | &SymFunctionTableAccess64, |
219 | | &SymGetModuleBase64, |
220 | | nullptr) && size < int(stack.size())) |
221 | | { |
222 | | stack[size++] = reinterpret_cast<void*>(stack_frame.AddrPC.Offset); |
223 | | } |
224 | | |
225 | | struct symbol_bundle : SYMBOL_INFO |
226 | | { |
227 | | wchar_t name[MAX_SYM_NAME]; |
228 | | }; |
229 | | |
230 | | HANDLE p = GetCurrentProcess(); |
231 | | static bool sym_initialized = false; |
232 | | if (!sym_initialized) |
233 | | { |
234 | | sym_initialized = true; |
235 | | SymInitialize(p, nullptr, true); |
236 | | } |
237 | | SymRefreshModuleList(p); |
238 | | for (int i = 0; i < size && len > 0; ++i) |
239 | | { |
240 | | DWORD_PTR frame_ptr = reinterpret_cast<DWORD_PTR>(stack[i]); |
241 | | |
242 | | DWORD64 displacement = 0; |
243 | | symbol_bundle symbol; |
244 | | symbol.MaxNameLen = MAX_SYM_NAME; |
245 | | symbol.SizeOfStruct = sizeof(SYMBOL_INFO); |
246 | | BOOL const has_symbol = SymFromAddr(p, frame_ptr, &displacement, &symbol); |
247 | | |
248 | | DWORD line_displacement = 0; |
249 | | IMAGEHLP_LINE64 line = {}; |
250 | | line.SizeOfStruct = sizeof(IMAGEHLP_LINE64); |
251 | | BOOL const has_line = SymGetLineFromAddr64(GetCurrentProcess(), frame_ptr, |
252 | | &line_displacement, &line); |
253 | | |
254 | | int ret = std::snprintf(out, len, "%2d: %p", i, stack[i]); |
255 | | out += ret; len -= ret; if (len <= 0) break; |
256 | | |
257 | | if (has_symbol) |
258 | | { |
259 | | ret = std::snprintf(out, len, " %s +%-4" PRId64 |
260 | | , demangle(symbol.Name).c_str(), displacement); |
261 | | out += ret; len -= ret; if (len <= 0) break; |
262 | | } |
263 | | |
264 | | if (has_line) |
265 | | { |
266 | | ret = std::snprintf(out, len, " %s:%d" |
267 | | , line.FileName, int(line.LineNumber)); |
268 | | out += ret; len -= ret; if (len <= 0) break; |
269 | | } |
270 | | |
271 | | |
272 | | ret = std::snprintf(out, len, "\n"); |
273 | | out += ret; |
274 | | len -= ret; |
275 | | |
276 | | if (i == max_depth && max_depth > 0) break; |
277 | | } |
278 | | } |
279 | | } |
280 | | |
281 | | #else |
282 | | |
283 | | namespace libtorrent { |
284 | | |
285 | | TORRENT_EXPORT void print_backtrace(char* out, int len, int /*max_depth*/, void* /* ctx */) |
286 | | { |
287 | | out[0] = 0; |
288 | | std::strncat(out, "<not supported>", std::size_t(len)); |
289 | | } |
290 | | |
291 | | } |
292 | | |
293 | | #endif |
294 | | |
295 | | #endif |
296 | | |
297 | | #if (TORRENT_USE_ASSERTS || defined TORRENT_ASIO_DEBUGGING) && \ |
298 | | defined TORRENT_PRODUCTION_ASSERTS |
299 | | char const* libtorrent_assert_log = "asserts.log"; |
300 | | namespace { |
301 | | // the number of asserts we've printed to the log |
302 | | std::atomic<int> assert_counter(0); |
303 | | } |
304 | | #endif |
305 | | |
306 | | namespace libtorrent { |
307 | | |
308 | | #if TORRENT_USE_ASSERTS || defined TORRENT_ASIO_DEBUGGING |
309 | | |
310 | | TORRENT_FORMAT(1,2) |
311 | | TORRENT_EXPORT void assert_print(char const* fmt, ...) |
312 | 0 | { |
313 | | #ifdef TORRENT_PRODUCTION_ASSERTS |
314 | | if (assert_counter > 500) return; |
315 | | |
316 | | FILE* out = fopen(libtorrent_assert_log, "a+"); |
317 | | if (out == nullptr) out = stderr; |
318 | | #else |
319 | 0 | FILE* out = stderr; |
320 | 0 | #endif |
321 | 0 | va_list va; |
322 | 0 | va_start(va, fmt); |
323 | 0 | std::vfprintf(out, fmt, va); |
324 | 0 | va_end(va); |
325 | |
|
326 | | #ifdef TORRENT_PRODUCTION_ASSERTS |
327 | | if (out != stderr) fclose(out); |
328 | | #endif |
329 | 0 | } |
330 | | |
331 | | // we deliberately don't want asserts to be marked as no-return, since that |
332 | | // would trigger warnings in debug builds of any code coming after the assert |
333 | | #ifdef __clang__ |
334 | | #pragma clang diagnostic push |
335 | | #pragma clang diagnostic ignored "-Wmissing-noreturn" |
336 | | #endif |
337 | | |
338 | | TORRENT_EXPORT void assert_fail(char const* expr, int line |
339 | | , char const* file, char const* function, char const* value, int kind) |
340 | 0 | { |
341 | | #ifdef TORRENT_PRODUCTION_ASSERTS |
342 | | // no need to flood the assert log with infinite number of asserts |
343 | | if (assert_counter.fetch_add(1) + 1 > 500) return; |
344 | | #endif |
345 | |
|
346 | 0 | char stack[8192]; |
347 | 0 | stack[0] = '\0'; |
348 | 0 | print_backtrace(stack, sizeof(stack), 0); |
349 | |
|
350 | 0 | char const* message = "assertion failed. Please file a bugreport at " |
351 | 0 | "https://github.com/arvidn/libtorrent/issues\n" |
352 | 0 | "Please include the following information:\n\n" |
353 | 0 | "version: " LIBTORRENT_VERSION "-" LIBTORRENT_REVISION "\n"; |
354 | |
|
355 | 0 | switch (kind) |
356 | 0 | { |
357 | 0 | case 1: |
358 | 0 | message = "A precondition of a libtorrent function has been violated.\n" |
359 | 0 | "This indicates a bug in the client application using libtorrent\n"; |
360 | 0 | } |
361 | |
|
362 | 0 | assert_print("%s\n" |
363 | | #ifdef TORRENT_PRODUCTION_ASSERTS |
364 | | "#: %d\n" |
365 | | #endif |
366 | 0 | "file: '%s'\n" |
367 | 0 | "line: %d\n" |
368 | 0 | "function: %s\n" |
369 | 0 | "expression: %s\n" |
370 | 0 | "%s%s\n" |
371 | 0 | "stack:\n" |
372 | 0 | "%s\n" |
373 | 0 | , message |
374 | | #ifdef TORRENT_PRODUCTION_ASSERTS |
375 | | , assert_counter.load() |
376 | | #endif |
377 | 0 | , file, line, function, expr |
378 | 0 | , value ? value : "", value ? "\n" : "" |
379 | 0 | , stack); |
380 | | |
381 | | // if production asserts are defined, don't abort, just print the error |
382 | 0 | #ifndef TORRENT_PRODUCTION_ASSERTS |
383 | | #ifdef TORRENT_WINDOWS |
384 | | // SIGABRT doesn't trigger a break with msvc |
385 | | __debugbreak(); |
386 | | #else |
387 | | // send SIGABRT to the current process |
388 | | // to break into the debugger |
389 | 0 | std::raise(SIGABRT); |
390 | 0 | #endif |
391 | 0 | std::abort(); |
392 | 0 | #endif |
393 | 0 | } |
394 | | |
395 | | #ifdef __clang__ |
396 | | #pragma clang diagnostic pop |
397 | | #endif |
398 | | |
399 | | #elif !TORRENT_USE_ASSERTS |
400 | | |
401 | | // these are just here to make it possible for a client that built with debug |
402 | | // enable to be able to link against a release build (just possible, not |
403 | | // necessarily supported) |
404 | | TORRENT_FORMAT(1,2) |
405 | | TORRENT_EXPORT void assert_print(char const*, ...) {} |
406 | | TORRENT_EXPORT void assert_fail(char const*, int, char const* |
407 | | , char const*, char const*, int) {} |
408 | | |
409 | | #endif |
410 | | |
411 | | } // libtorrent namespace |
412 | | |