util_bug.c (5853B)
1 /* Copyright (c) 2003, Roger Dingledine 2 * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. 3 * Copyright (c) 2007-2021, The Tor Project, Inc. */ 4 /* See LICENSE for licensing information */ 5 6 /** 7 * \file util_bug.c 8 **/ 9 10 #include "orconfig.h" 11 #include "lib/log/util_bug.h" 12 #include "lib/log/log.h" 13 #include "lib/err/backtrace.h" 14 #include "lib/err/torerr.h" 15 #ifdef TOR_UNIT_TESTS 16 #include "lib/smartlist_core/smartlist_core.h" 17 #include "lib/smartlist_core/smartlist_foreach.h" 18 #endif 19 #include "lib/malloc/malloc.h" 20 #include "lib/string/printf.h" 21 #include "lib/thread/threads.h" 22 23 #include <string.h> 24 #include <stdlib.h> 25 26 #ifdef TOR_UNIT_TESTS 27 static void (*failed_assertion_cb)(void) = NULL; 28 static int n_bugs_to_capture = 0; 29 static smartlist_t *bug_messages = NULL; 30 #define capturing_bugs() (bug_messages != NULL && n_bugs_to_capture) 31 void 32 tor_capture_bugs_(int n) 33 { 34 tor_end_capture_bugs_(); 35 bug_messages = smartlist_new(); 36 n_bugs_to_capture = n; 37 } 38 void 39 tor_end_capture_bugs_(void) 40 { 41 n_bugs_to_capture = 0; 42 if (!bug_messages) 43 return; 44 SMARTLIST_FOREACH(bug_messages, char *, cp, tor_free(cp)); 45 smartlist_free(bug_messages); 46 bug_messages = NULL; 47 } 48 const smartlist_t * 49 tor_get_captured_bug_log_(void) 50 { 51 return bug_messages; 52 } 53 static void 54 add_captured_bug(const char *s) 55 { 56 --n_bugs_to_capture; 57 smartlist_add_strdup(bug_messages, s); 58 } 59 /** Set a callback to be invoked when we get any tor_bug_occurred_ 60 * invocation. We use this in the unit tests so that a nonfatal 61 * assertion failure can also count as a test failure. 62 */ 63 void 64 tor_set_failed_assertion_callback(void (*fn)(void)) 65 { 66 failed_assertion_cb = fn; 67 } 68 #else /* !defined(TOR_UNIT_TESTS) */ 69 #define capturing_bugs() (0) 70 #define add_captured_bug(s) do { } while (0) 71 #endif /* defined(TOR_UNIT_TESTS) */ 72 73 /** Helper for tor_assert: report the assertion failure. */ 74 void 75 tor_assertion_failed_(const char *fname, unsigned int line, 76 const char *func, const char *expr, 77 const char *fmt, ...) 78 { 79 char *buf = NULL; 80 char *extra = NULL; 81 va_list ap; 82 83 #ifdef __clang__ 84 #pragma clang diagnostic push 85 #pragma clang diagnostic ignored "-Wformat-nonliteral" 86 #endif 87 if (fmt) { 88 va_start(ap,fmt); 89 tor_vasprintf(&extra, fmt, ap); 90 va_end(ap); 91 } 92 #ifdef __clang__ 93 #pragma clang diagnostic pop 94 #endif 95 96 log_err(LD_BUG, "%s:%u: %s: Assertion %s failed; aborting.", 97 fname, line, func, expr); 98 tor_asprintf(&buf, "Assertion %s failed in %s at %s:%u: %s", 99 expr, func, fname, line, extra ? extra : ""); 100 tor_free(extra); 101 log_backtrace(LOG_ERR, LD_BUG, buf); 102 tor_free(buf); 103 } 104 105 static atomic_counter_t total_bug_reached; 106 107 void 108 tor_bug_init_counter(void) 109 { 110 atomic_counter_init(&total_bug_reached); 111 } 112 113 /** Helper to update BUG count in metrics. */ 114 void 115 tor_bug_increment_count_(void) 116 { 117 atomic_counter_add(&total_bug_reached, 1); 118 } 119 120 size_t 121 tor_bug_get_count(void) 122 { 123 return atomic_counter_get(&total_bug_reached); 124 } 125 126 /** Helper for tor_assert_nonfatal: report the assertion failure. */ 127 void 128 tor_bug_occurred_(const char *fname, unsigned int line, 129 const char *func, const char *expr, 130 int once, const char *fmt, ...) 131 { 132 char *buf = NULL; 133 const char *once_str = once ? 134 " (Future instances of this warning will be silenced.)": ""; 135 if (! once) { 136 // _once assertions count from the macro directly so we count them as many 137 // times as they are reached, and not just once. 138 tor_bug_increment_count_(); 139 } 140 if (! expr) { 141 if (capturing_bugs()) { 142 add_captured_bug("This line should not have been reached."); 143 return; 144 } 145 log_warn(LD_BUG, "%s:%u: %s: This line should not have been reached.%s", 146 fname, line, func, once_str); 147 tor_asprintf(&buf, 148 "Line unexpectedly reached at %s at %s:%u", 149 func, fname, line); 150 } else { 151 if (capturing_bugs()) { 152 add_captured_bug(expr); 153 return; 154 } 155 156 va_list ap; 157 char *extra = NULL; 158 159 #ifdef __clang__ 160 #pragma clang diagnostic push 161 #pragma clang diagnostic ignored "-Wformat-nonliteral" 162 #endif 163 if (fmt) { 164 va_start(ap,fmt); 165 tor_vasprintf(&extra, fmt, ap); 166 va_end(ap); 167 } 168 #ifdef __clang__ 169 #pragma clang diagnostic pop 170 #endif 171 172 log_warn(LD_BUG, "%s:%u: %s: Non-fatal assertion %s failed.%s", 173 fname, line, func, expr, once_str); 174 tor_asprintf(&buf, "Non-fatal assertion %s failed in %s at %s:%u%s%s", 175 expr, func, fname, line, fmt ? " : " : "", 176 extra ? extra : ""); 177 tor_free(extra); 178 } 179 log_backtrace(LOG_WARN, LD_BUG, buf); 180 tor_free(buf); 181 182 #ifdef TOR_UNIT_TESTS 183 if (failed_assertion_cb) { 184 failed_assertion_cb(); 185 } 186 #endif 187 } 188 189 /** 190 * Call the tor_raw_abort_() function to close raw logs, then kill the current 191 * process with a fatal error. But first, close the file-based log file 192 * descriptors, so error messages are written before process termination. 193 * 194 * (This is a separate function so that we declare it in util_bug.h without 195 * including torerr.h in all the users of util_bug.h) 196 **/ 197 void 198 tor_abort_(void) 199 { 200 logs_flush_sigsafe(); 201 tor_raw_abort_(); 202 } 203 204 #ifdef _WIN32 205 /** Take a filename and return a pointer to its final element. This 206 * function is called on __FILE__ to fix a MSVC nit where __FILE__ 207 * contains the full path to the file. This is bad, because it 208 * confuses users to find the home directory of the person who 209 * compiled the binary in their warning messages. 210 */ 211 const char * 212 tor_fix_source_file(const char *fname) 213 { 214 const char *cp1, *cp2, *r; 215 cp1 = strrchr(fname, '/'); 216 cp2 = strrchr(fname, '\\'); 217 if (cp1 && cp2) { 218 r = (cp1<cp2)?(cp2+1):(cp1+1); 219 } else if (cp1) { 220 r = cp1+1; 221 } else if (cp2) { 222 r = cp2+1; 223 } else { 224 r = fname; 225 } 226 return r; 227 } 228 #endif /* defined(_WIN32) */