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