1 /* 2 * Copyright (C) the libgit2 contributors. All rights reserved. 3 * 4 * This file is part of libgit2, distributed under the GNU GPL v2 with 5 * a Linking Exception. For full terms see the included COPYING file. 6 */ 7 8 #ifndef INCLUDE_win32_leakcheck_h__ 9 #define INCLUDE_win32_leakcheck_h__ 10 11 #include "common.h" 12 13 /* Initialize the win32 leak checking system. */ 14 int git_win32_leakcheck_global_init(void); 15 16 #if defined(GIT_WIN32_LEAKCHECK) 17 18 #include <stdlib.h> 19 #include <crtdbg.h> 20 21 #include "git2/errors.h" 22 #include "strnlen.h" 23 24 bool git_win32_leakcheck_has_leaks(void); 25 26 /* Stack frames (for stack tracing, below) */ 27 28 /** 29 * This type defines a callback to be used to augment a C stacktrace 30 * with "aux" data. This can be used, for example, to allow LibGit2Sharp 31 * (or other interpreted consumer libraries) to give us C# stacktrace 32 * data for the PInvoke. 33 * 34 * This callback will be called during crtdbg-instrumented allocs. 35 * 36 * @param aux_id [out] A returned "aux_id" representing a unique 37 * (de-duped at the C# layer) stacktrace. "aux_id" 0 is reserved 38 * to mean no aux stacktrace data. 39 */ 40 typedef void (*git_win32_leakcheck_stack_aux_cb_alloc)(unsigned int *aux_id); 41 42 /** 43 * This type defines a callback to be used to augment the output of 44 * a stacktrace. This will be used to request the C# layer format 45 * the C# stacktrace associated with "aux_id" into the provided 46 * buffer. 47 * 48 * This callback will be called during leak reporting. 49 * 50 * @param aux_id The "aux_id" key associated with a stacktrace. 51 * @param aux_msg A buffer where a formatted message should be written. 52 * @param aux_msg_len The size of the buffer. 53 */ 54 typedef void (*git_win32_leakcheck_stack_aux_cb_lookup)(unsigned int aux_id, char *aux_msg, size_t aux_msg_len); 55 56 /** 57 * Register an "aux" data provider to augment our C stacktrace data. 58 * 59 * This can be used, for example, to allow LibGit2Sharp (or other 60 * interpreted consumer libraries) to give us the C# stacktrace of 61 * the PInvoke. 62 * 63 * If you choose to use this feature, it should be registered during 64 * initialization and not changed for the duration of the process. 65 */ 66 int git_win32_leakcheck_stack_set_aux_cb( 67 git_win32_leakcheck_stack_aux_cb_alloc cb_alloc, 68 git_win32_leakcheck_stack_aux_cb_lookup cb_lookup); 69 70 /** 71 * Maximum number of stackframes to record for a 72 * single stacktrace. 73 */ 74 #define GIT_WIN32_LEAKCHECK_STACK_MAX_FRAMES 30 75 76 /** 77 * Wrapper containing the raw unprocessed stackframe 78 * data for a single stacktrace and any "aux_id". 79 * 80 * I put the aux_id first so leaks will be sorted by it. 81 * So, for example, if a specific callstack in C# leaks 82 * a repo handle, all of the pointers within the associated 83 * repo pointer will be grouped together. 84 */ 85 typedef struct { 86 unsigned int aux_id; 87 unsigned int nr_frames; 88 void *frames[GIT_WIN32_LEAKCHECK_STACK_MAX_FRAMES]; 89 } git_win32_leakcheck_stack_raw_data; 90 91 /** 92 * Capture raw stack trace data for the current process/thread. 93 * 94 * @param skip Number of initial frames to skip. Pass 0 to 95 * begin with the caller of this routine. Pass 1 to begin 96 * with its caller. And so on. 97 */ 98 int git_win32_leakcheck_stack_capture(git_win32_leakcheck_stack_raw_data *pdata, int skip); 99 100 /** 101 * Compare 2 raw stacktraces with the usual -1,0,+1 result. 102 * This includes any "aux_id" values in the comparison, so that 103 * our de-dup is also "aux" context relative. 104 */ 105 int git_win32_leakcheck_stack_compare( 106 git_win32_leakcheck_stack_raw_data *d1, 107 git_win32_leakcheck_stack_raw_data *d2); 108 109 /** 110 * Format raw stacktrace data into buffer WITHOUT using any mallocs. 111 * 112 * @param prefix String written before each frame; defaults to "\t". 113 * @param suffix String written after each frame; defaults to "\n". 114 */ 115 int git_win32_leakcheck_stack_format( 116 char *pbuf, size_t buf_len, 117 const git_win32_leakcheck_stack_raw_data *pdata, 118 const char *prefix, const char *suffix); 119 120 /** 121 * Convenience routine to capture and format stacktrace into 122 * a buffer WITHOUT using any mallocs. This is primarily a 123 * wrapper for testing. 124 * 125 * @param skip Number of initial frames to skip. Pass 0 to 126 * begin with the caller of this routine. Pass 1 to begin 127 * with its caller. And so on. 128 * @param prefix String written before each frame; defaults to "\t". 129 * @param suffix String written after each frame; defaults to "\n". 130 */ 131 int git_win32_leakcheck_stack( 132 char * pbuf, size_t buf_len, 133 int skip, 134 const char *prefix, const char *suffix); 135 136 /* Stack tracing */ 137 138 /* MSVC CRTDBG memory leak reporting. 139 * 140 * We DO NOT use the "_CRTDBG_MAP_ALLOC" macro described in the MSVC 141 * documentation because all allocs/frees in libgit2 already go through 142 * the "git__" routines defined in this file. Simply using the normal 143 * reporting mechanism causes all leaks to be attributed to a routine 144 * here in util.h (ie, the actual call to calloc()) rather than the 145 * caller of git__calloc(). 146 * 147 * Therefore, we declare a set of "git__crtdbg__" routines to replace 148 * the corresponding "git__" routines and re-define the "git__" symbols 149 * as macros. This allows us to get and report the file:line info of 150 * the real caller. 151 * 152 * We DO NOT replace the "git__free" routine because it needs to remain 153 * a function pointer because it is used as a function argument when 154 * setting up various structure "destructors". 155 * 156 * We also DO NOT use the "_CRTDBG_MAP_ALLOC" macro because it causes 157 * "free" to be remapped to "_free_dbg" and this causes problems for 158 * structures which define a field named "free". 159 * 160 * Finally, CRTDBG must be explicitly enabled and configured at program 161 * startup. See tests/main.c for an example. 162 */ 163 164 /** 165 * Checkpoint options. 166 */ 167 typedef enum git_win32_leakcheck_stacktrace_options { 168 /** 169 * Set checkpoint marker. 170 */ 171 GIT_WIN32_LEAKCHECK_STACKTRACE_SET_MARK = (1 << 0), 172 173 /** 174 * Dump leaks since last checkpoint marker. 175 * May not be combined with _LEAKS_TOTAL. 176 * 177 * Note that this may generate false positives for global TLS 178 * error state and other global caches that aren't cleaned up 179 * until the thread/process terminates. So when using this 180 * around a region of interest, also check the final (at exit) 181 * dump before digging into leaks reported here. 182 */ 183 GIT_WIN32_LEAKCHECK_STACKTRACE_LEAKS_SINCE_MARK = (1 << 1), 184 185 /** 186 * Dump leaks since init. May not be combined 187 * with _LEAKS_SINCE_MARK. 188 */ 189 GIT_WIN32_LEAKCHECK_STACKTRACE_LEAKS_TOTAL = (1 << 2), 190 191 /** 192 * Suppress printing during dumps. 193 * Just return leak count. 194 */ 195 GIT_WIN32_LEAKCHECK_STACKTRACE_QUIET = (1 << 3), 196 197 } git_win32_leakcheck_stacktrace_options; 198 199 /** 200 * Checkpoint memory state and/or dump unique stack traces of 201 * current memory leaks. 202 * 203 * @return number of unique leaks (relative to requested starting 204 * point) or error. 205 */ 206 int git_win32_leakcheck_stacktrace_dump( 207 git_win32_leakcheck_stacktrace_options opt, 208 const char *label); 209 210 /** 211 * Construct stacktrace and append it to the global buffer. 212 * Return pointer to start of this string. On any error or 213 * lack of buffer space, just return the given file buffer 214 * so it will behave as usual. 215 * 216 * This should ONLY be called by our internal memory allocations 217 * routines. 218 */ 219 const char *git_win32_leakcheck_stacktrace(int skip, const char *file); 220 221 #endif 222 #endif 223