1 /************************************************************************** 2 * 3 * Copyright 2010 Luca Barbieri 4 * 5 * Permission is hereby granted, free of charge, to any person obtaining 6 * a copy of this software and associated documentation files (the 7 * "Software"), to deal in the Software without restriction, including 8 * without limitation the rights to use, copy, modify, merge, publish, 9 * distribute, sublicense, and/or sell copies of the Software, and to 10 * permit persons to whom the Software is furnished to do so, subject to 11 * the following conditions: 12 * 13 * The above copyright notice and this permission notice (including the 14 * next paragraph) shall be included in all copies or substantial 15 * portions of the Software. 16 * 17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE 21 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 22 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 23 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 * 25 **************************************************************************/ 26 27 #if defined(DEBUG) 28 29 /** 30 * If the GALLIUM_REFCNT_LOG env var is defined as a filename, gallium 31 * reference counting will be logged to the file. 32 * 33 * See http://www-archive.mozilla.org/performance/refcnt-balancer.html 34 * for what to do with the output on Linux, use tools/addr2line.sh to 35 * postprocess it before anything else. 36 */ 37 38 #include <stdio.h> 39 40 #include "util/u_debug.h" 41 #include "util/u_debug_refcnt.h" 42 #include "util/u_debug_stack.h" 43 #include "util/u_debug_symbol.h" 44 #include "util/u_string.h" 45 #include "util/u_hash_table.h" 46 #include "os/os_thread.h" 47 48 int debug_refcnt_state; 49 50 static FILE *stream; 51 52 /* TODO: maybe move this serial machinery to a stand-alone module and 53 * expose it? 54 */ 55 pipe_static_mutex(serials_mutex); 56 57 static struct util_hash_table *serials_hash; 58 static unsigned serials_last; 59 60 61 static unsigned 62 hash_ptr(void *p) 63 { 64 return (unsigned) (uintptr_t) p; 65 } 66 67 68 static int 69 compare_ptr(void *a, void *b) 70 { 71 if (a == b) 72 return 0; 73 else if (a < b) 74 return -1; 75 else 76 return 1; 77 } 78 79 80 /** 81 * Return a small integer serial number for the given pointer. 82 */ 83 static boolean 84 debug_serial(void *p, unsigned *pserial) 85 { 86 unsigned serial; 87 boolean found = TRUE; 88 #ifdef PIPE_SUBSYSTEM_WINDOWS_USER 89 static boolean first = TRUE; 90 91 if (first) { 92 pipe_mutex_init(serials_mutex); 93 first = FALSE; 94 } 95 #endif 96 97 pipe_mutex_lock(serials_mutex); 98 if (!serials_hash) 99 serials_hash = util_hash_table_create(hash_ptr, compare_ptr); 100 101 serial = (unsigned) (uintptr_t) util_hash_table_get(serials_hash, p); 102 if (!serial) { 103 /* time to stop logging... (you'll have a 100 GB logfile at least at 104 * this point) TODO: avoid this 105 */ 106 serial = ++serials_last; 107 if (!serial) { 108 debug_error("More than 2^32 objects detected, aborting.\n"); 109 os_abort(); 110 } 111 112 util_hash_table_set(serials_hash, p, (void *) (uintptr_t) serial); 113 found = FALSE; 114 } 115 pipe_mutex_unlock(serials_mutex); 116 117 *pserial = serial; 118 119 return found; 120 } 121 122 123 /** 124 * Free the serial number for the given pointer. 125 */ 126 static void 127 debug_serial_delete(void *p) 128 { 129 pipe_mutex_lock(serials_mutex); 130 util_hash_table_remove(serials_hash, p); 131 pipe_mutex_unlock(serials_mutex); 132 } 133 134 135 #define STACK_LEN 64 136 137 static void 138 dump_stack(const char *symbols[STACK_LEN]) 139 { 140 unsigned i; 141 for (i = 0; i < STACK_LEN; ++i) { 142 if (symbols[i]) 143 fprintf(stream, "%s\n", symbols[i]); 144 } 145 fprintf(stream, "\n"); 146 } 147 148 149 /** 150 * Log a reference count change to the log file (if enabled). 151 * This is called via the pipe_reference() and debug_reference() functions, 152 * basically whenever a reference count is initialized or changed. 153 * 154 * \param p the refcount being changed (the value is not changed here) 155 * \param get_desc a function which will be called to print an object's 156 * name/pointer into a string buffer during logging 157 * \param change the reference count change which must be +/-1 or 0 when 158 * creating the object and initializing the refcount. 159 */ 160 void 161 debug_reference_slowpath(const struct pipe_reference *p, 162 debug_reference_descriptor get_desc, int change) 163 { 164 assert(change >= -1); 165 assert(change <= 1); 166 167 if (debug_refcnt_state < 0) 168 return; 169 170 if (!debug_refcnt_state) { 171 const char *filename = debug_get_option("GALLIUM_REFCNT_LOG", NULL); 172 if (filename && filename[0]) 173 stream = fopen(filename, "wt"); 174 175 if (stream) 176 debug_refcnt_state = 1; 177 else 178 debug_refcnt_state = -1; 179 } 180 181 if (debug_refcnt_state > 0) { 182 struct debug_stack_frame frames[STACK_LEN]; 183 const char *symbols[STACK_LEN]; 184 char buf[1024]; 185 unsigned i; 186 unsigned refcnt = p->count; 187 unsigned serial; 188 boolean existing = debug_serial((void *) p, &serial); 189 190 debug_backtrace_capture(frames, 1, STACK_LEN); 191 for (i = 0; i < STACK_LEN; ++i) { 192 if (frames[i].function) 193 symbols[i] = debug_symbol_name_cached(frames[i].function); 194 else 195 symbols[i] = 0; 196 } 197 198 get_desc(buf, p); 199 200 if (!existing) { 201 fprintf(stream, "<%s> %p %u Create\n", buf, (void *) p, serial); 202 dump_stack(symbols); 203 204 /* this is here to provide a gradual change even if we don't see 205 * the initialization 206 */ 207 for (i = 1; i <= refcnt - change; ++i) { 208 fprintf(stream, "<%s> %p %u AddRef %u\n", buf, (void *) p, 209 serial, i); 210 dump_stack(symbols); 211 } 212 } 213 214 if (change) { 215 fprintf(stream, "<%s> %p %u %s %u\n", buf, (void *) p, serial, 216 change > 0 ? "AddRef" : "Release", refcnt); 217 dump_stack(symbols); 218 } 219 220 if (!refcnt) { 221 debug_serial_delete((void *) p); 222 fprintf(stream, "<%s> %p %u Destroy\n", buf, (void *) p, serial); 223 dump_stack(symbols); 224 } 225 226 fflush(stream); 227 } 228 } 229 230 #endif /* DEBUG */ 231