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 static mtx_t serials_mutex = _MTX_INITIALIZER_NP; 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 (void) mtx_init(&serials_mutex, mtx_plain); 93 first = FALSE; 94 } 95 #endif 96 97 mtx_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 mtx_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 mtx_lock(&serials_mutex); 130 util_hash_table_remove(serials_hash, p); 131 mtx_unlock(&serials_mutex); 132 } 133 134 135 #define STACK_LEN 64 136 137 /** 138 * Log a reference count change to the log file (if enabled). 139 * This is called via the pipe_reference() and debug_reference() functions, 140 * basically whenever a reference count is initialized or changed. 141 * 142 * \param p the refcount being changed (the value is not changed here) 143 * \param get_desc a function which will be called to print an object's 144 * name/pointer into a string buffer during logging 145 * \param change the reference count change which must be +/-1 or 0 when 146 * creating the object and initializing the refcount. 147 */ 148 void 149 debug_reference_slowpath(const struct pipe_reference *p, 150 debug_reference_descriptor get_desc, int change) 151 { 152 assert(change >= -1); 153 assert(change <= 1); 154 155 if (debug_refcnt_state < 0) 156 return; 157 158 if (!debug_refcnt_state) { 159 const char *filename = debug_get_option("GALLIUM_REFCNT_LOG", NULL); 160 if (filename && filename[0]) 161 stream = fopen(filename, "wt"); 162 163 if (stream) 164 debug_refcnt_state = 1; 165 else 166 debug_refcnt_state = -1; 167 } 168 169 if (debug_refcnt_state > 0) { 170 struct debug_stack_frame frames[STACK_LEN]; 171 char buf[1024]; 172 unsigned i; 173 unsigned refcnt = p->count; 174 unsigned serial; 175 boolean existing = debug_serial((void *) p, &serial); 176 177 debug_backtrace_capture(frames, 1, STACK_LEN); 178 179 get_desc(buf, p); 180 181 if (!existing) { 182 fprintf(stream, "<%s> %p %u Create\n", buf, (void *) p, serial); 183 debug_backtrace_print(stream, frames, STACK_LEN); 184 185 /* this is here to provide a gradual change even if we don't see 186 * the initialization 187 */ 188 for (i = 1; i <= refcnt - change; ++i) { 189 fprintf(stream, "<%s> %p %u AddRef %u\n", buf, (void *) p, 190 serial, i); 191 debug_backtrace_print(stream, frames, STACK_LEN); 192 } 193 } 194 195 if (change) { 196 fprintf(stream, "<%s> %p %u %s %u\n", buf, (void *) p, serial, 197 change > 0 ? "AddRef" : "Release", refcnt); 198 debug_backtrace_print(stream, frames, STACK_LEN); 199 } 200 201 if (!refcnt) { 202 debug_serial_delete((void *) p); 203 fprintf(stream, "<%s> %p %u Destroy\n", buf, (void *) p, serial); 204 debug_backtrace_print(stream, frames, STACK_LEN); 205 } 206 207 fflush(stream); 208 } 209 } 210 211 #endif /* DEBUG */ 212