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 /* see http://www.mozilla.org/performance/refcnt-balancer.html for what do with the output 30 * on Linux, use tools/addr2line.sh to postprocess it before anything else 31 **/ 32 33 #include <stdio.h> 34 35 #include "util/u_debug.h" 36 #include "util/u_debug_refcnt.h" 37 #include "util/u_debug_stack.h" 38 #include "util/u_debug_symbol.h" 39 #include "util/u_string.h" 40 #include "util/u_hash_table.h" 41 #include "os/os_thread.h" 42 43 int debug_refcnt_state; 44 45 FILE* stream; 46 47 /* TODO: maybe move this serial machinery to a stand-alone module and expose it? */ 48 pipe_static_mutex(serials_mutex); 49 50 static struct util_hash_table* serials_hash; 51 static unsigned serials_last; 52 53 static unsigned hash_ptr(void* p) 54 { 55 return (unsigned)(uintptr_t)p; 56 } 57 58 static int compare_ptr(void* a, void* b) 59 { 60 if(a == b) 61 return 0; 62 else if(a < b) 63 return -1; 64 else 65 return 1; 66 } 67 68 static boolean debug_serial(void* p, unsigned* pserial) 69 { 70 unsigned serial; 71 boolean found = TRUE; 72 #ifdef PIPE_SUBSYSTEM_WINDOWS_USER 73 static boolean first = TRUE; 74 75 if (first) { 76 pipe_mutex_init(serials_mutex); 77 first = FALSE; 78 } 79 #endif 80 81 pipe_mutex_lock(serials_mutex); 82 if(!serials_hash) 83 serials_hash = util_hash_table_create(hash_ptr, compare_ptr); 84 serial = (unsigned)(uintptr_t)util_hash_table_get(serials_hash, p); 85 if(!serial) 86 { 87 /* time to stop logging... (you'll have a 100 GB logfile at least at this point) 88 * TODO: avoid this 89 */ 90 serial = ++serials_last; 91 if(!serial) 92 { 93 debug_error("More than 2^32 objects detected, aborting.\n"); 94 os_abort(); 95 } 96 97 util_hash_table_set(serials_hash, p, (void*)(uintptr_t)serial); 98 found = FALSE; 99 } 100 pipe_mutex_unlock(serials_mutex); 101 *pserial = serial; 102 return found; 103 } 104 105 static void debug_serial_delete(void* p) 106 { 107 pipe_mutex_lock(serials_mutex); 108 util_hash_table_remove(serials_hash, p); 109 pipe_mutex_unlock(serials_mutex); 110 } 111 112 #define STACK_LEN 64 113 114 static void dump_stack(const char* symbols[STACK_LEN]) 115 { 116 unsigned i; 117 for(i = 0; i < STACK_LEN; ++i) 118 { 119 if(symbols[i]) 120 fprintf(stream, "%s\n", symbols[i]); 121 } 122 fprintf(stream, "\n"); 123 } 124 125 void debug_reference_slowpath(const struct pipe_reference* p, debug_reference_descriptor get_desc, int change) 126 { 127 if(debug_refcnt_state < 0) 128 return; 129 130 if(!debug_refcnt_state) 131 { 132 const char* filename = debug_get_option("GALLIUM_REFCNT_LOG", NULL); 133 if(filename && filename[0]) 134 stream = fopen(filename, "wt"); 135 136 if(stream) 137 debug_refcnt_state = 1; 138 else 139 debug_refcnt_state = -1; 140 } 141 142 if(debug_refcnt_state > 0) 143 { 144 struct debug_stack_frame frames[STACK_LEN]; 145 const char* symbols[STACK_LEN]; 146 char buf[1024]; 147 148 unsigned i; 149 unsigned refcnt = p->count; 150 unsigned serial; 151 boolean existing = debug_serial((void*)p, &serial); 152 153 debug_backtrace_capture(frames, 1, STACK_LEN); 154 for(i = 0; i < STACK_LEN; ++i) 155 { 156 if(frames[i].function) 157 symbols[i] = debug_symbol_name_cached(frames[i].function); 158 else 159 symbols[i] = 0; 160 } 161 162 get_desc(buf, p); 163 164 if(!existing) 165 { 166 fprintf(stream, "<%s> %p %u Create\n", buf, (void *) p, serial); 167 dump_stack(symbols); 168 169 /* this is there to provide a gradual change even if we don't see the initialization */ 170 for(i = 1; i <= refcnt - change; ++i) 171 { 172 fprintf(stream, "<%s> %p %u AddRef %u\n", buf, (void *) p, 173 serial, i); 174 dump_stack(symbols); 175 } 176 } 177 178 if(change) 179 { 180 fprintf(stream, "<%s> %p %u %s %u\n", buf, (void *) p, serial, 181 change > 0 ? "AddRef" : "Release", refcnt); 182 dump_stack(symbols); 183 } 184 185 if(!refcnt) 186 { 187 debug_serial_delete((void*)p); 188 fprintf(stream, "<%s> %p %u Destroy\n", buf, (void *) p, serial); 189 dump_stack(symbols); 190 } 191 192 fflush(stream); 193 } 194 } 195 #endif 196