| 1 | // SPDX-License-Identifier: GPL-3.0-or-later |
| 2 | |
| 3 | #ifndef NETDATA_DICTIONARY_DEBUG_H |
| 4 | #define NETDATA_DICTIONARY_DEBUG_H |
| 5 | |
| 6 | #include "dictionary.h" |
| 7 | |
| 8 | #ifdef FSANITIZE_ADDRESS |
| 9 | |
| 10 | // Initialize dictionary debugging |
| 11 | void dictionary_debug_init(void); |
| 12 | |
| 13 | // Register a dictionary for tracking (when FSANITIZE_ADDRESS is defined) |
| 14 | void dictionary_debug_track_dict(DICTIONARY *dict); |
| 15 | |
| 16 | // Unregister a dictionary from tracking |
| 17 | void dictionary_debug_untrack_dict(DICTIONARY *dict); |
| 18 | |
| 19 | // Print information about dictionaries that are still allocated |
| 20 | void dictionary_print_still_allocated_stacktraces(void); |
| 21 | |
| 22 | // Print information about dictionaries that have delayed destruction due to referenced items |
| 23 | void dictionary_debug_print_delayed_dictionaries(size_t destroyed_dicts); |
| 24 | |
| 25 | // Clean up resources used for tracking |
| 26 | void dictionary_debug_shutdown(void); |
| 27 | |
| 28 | void dictionary_debug_internal_check_with_trace(DICTIONARY *dict, DICTIONARY_ITEM *item, const char *function, bool allow_null_dict, bool allow_null_item); |
| 29 | #define dictionary_debug_internal_check(dict, item, allow_null_dict, allow_null_item) \ |
| 30 | dictionary_debug_internal_check_with_trace(dict, item, __FUNCTION__, allow_null_dict, allow_null_item) |
| 31 | |
| 32 | // For internal_error with dictionary stacktrace |
| 33 | #define dictionary_internal_error(condition, dict, fmt, args...) do { \ |
| 34 | if(unlikely(condition)) { \ |
| 35 | BUFFER *__wb = buffer_create(1024, NULL); \ |
| 36 | if ((dict) && ((dict)->stacktraces.num_stacktraces > 0)) { \ |
| 37 | buffer_sprintf(__wb, fmt " Dictionary accessed at:\n", ##args); \ |
| 38 | for (int i = 0; i < (dict)->stacktraces.num_stacktraces && i < 3; i++) { \ |
| 39 | if ((dict)->stacktraces.stacktraces[i]) { \ |
| 40 | buffer_sprintf(__wb, "Stacktrace #%d:\n", i+1); \ |
| 41 | stacktrace_to_buffer((dict)->stacktraces.stacktraces[i], __wb); \ |
| 42 | buffer_strcat(__wb, "\n"); \ |
| 43 | } \ |
| 44 | } \ |
| 45 | if ((dict)->stacktraces.num_stacktraces > 3) \ |
| 46 | buffer_sprintf(__wb, "...and %d more stacktraces\n", (dict)->stacktraces.num_stacktraces - 3); \ |
| 47 | } else { \ |
| 48 | buffer_sprintf(__wb, fmt " Dictionary stacktrace not available", ##args); \ |
| 49 | } \ |
| 50 | netdata_logger(NDLS_DAEMON, NDLP_DEBUG, __FILE__, __FUNCTION__, __LINE__, "BEGIN --\n%s\n-- END", buffer_tostring(__wb)); \ |
| 51 | buffer_free(__wb); \ |
| 52 | } \ |
| 53 | } while(0) |
| 54 | |
| 55 | // For internal_fatal with dictionary stacktrace |
| 56 | #define dictionary_internal_fatal(condition, dict, fmt, args...) do { \ |
| 57 | if(unlikely(condition)) { \ |
| 58 | BUFFER *__wb = buffer_create(1024, NULL); \ |
| 59 | if ((dict) && ((dict)->stacktraces.num_stacktraces > 0)) { \ |
| 60 | buffer_sprintf(__wb, fmt " Dictionary accessed at:\n", ##args); \ |
| 61 | for (int i = 0; i < (dict)->stacktraces.num_stacktraces && i < 3; i++) { \ |
| 62 | if ((dict)->stacktraces.stacktraces[i]) { \ |
| 63 | buffer_sprintf(__wb, "Stacktrace #%d:\n", i+1); \ |
| 64 | stacktrace_to_buffer((dict)->stacktraces.stacktraces[i], __wb); \ |
| 65 | buffer_strcat(__wb, "\n"); \ |
| 66 | } \ |
| 67 | } \ |
| 68 | if ((dict)->stacktraces.num_stacktraces > 3) \ |
| 69 | buffer_sprintf(__wb, "...and %d more stacktraces\n", (dict)->stacktraces.num_stacktraces - 3); \ |
| 70 | } else { \ |
| 71 | buffer_sprintf(__wb, fmt " Dictionary stacktrace not available", ##args); \ |
| 72 | } \ |
| 73 | netdata_logger_fatal(__FILE__, __FUNCTION__, __LINE__, "BEGIN --\n%s\n--END", buffer_tostring(__wb)); \ |
| 74 | buffer_free(__wb); \ |
| 75 | } \ |
| 76 | } while(0) |
| 77 | |
| 78 | #else |
| 79 | |
| 80 | #define dictionary_debug_init(void) debug_dummy() |
| 81 | #define dictionary_debug_track_dict(dict) debug_dummy() |
| 82 | #define dictionary_debug_untrack_dict(dict) debug_dummy() |
| 83 | #define dictionary_print_still_allocated_stacktraces(void) debug_dummy() |
| 84 | #define dictionary_debug_print_delayed_dictionaries(destroyed_dicts) debug_dummy() |
| 85 | #define dictionary_debug_shutdown(void) debug_dummy() |
| 86 | |
| 87 | #define dictionary_debug_internal_check(dict, item, allow_null_dict, allow_null_item) debug_dummy() |
| 88 | #define dictionary_debug_internal_check_with_trace(dict, item, function, allow_null_dict, allow_null_item) debug_dummy() |
| 89 | |
| 90 | #define dictionary_internal_error(condition, dict, fmt, args...) debug_dummy() |
| 91 | #define dictionary_internal_fatal(condition, dict, fmt, args...) debug_dummy() |
| 92 | #endif |
| 93 | |
| 94 | #endif // NETDATA_DICTIONARY_DEBUG_H |