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 "util/u_thread.h" 47 48int debug_refcnt_state; 49 50static FILE *stream; 51 52/* TODO: maybe move this serial machinery to a stand-alone module and 53 * expose it? 54 */ 55#ifdef PIPE_OS_WINDOWS 56static mtx_t serials_mutex; 57#else 58static mtx_t serials_mutex = _MTX_INITIALIZER_NP; 59#endif 60 61static struct hash_table *serials_hash; 62static unsigned serials_last; 63 64 65/** 66 * Return a small integer serial number for the given pointer. 67 */ 68static boolean 69debug_serial(void *p, unsigned *pserial) 70{ 71 unsigned serial; 72 boolean found = TRUE; 73#ifdef PIPE_OS_WINDOWS 74 static boolean first = TRUE; 75 76 if (first) { 77 (void) mtx_init(&serials_mutex, mtx_plain); 78 first = FALSE; 79 } 80#endif 81 82 mtx_lock(&serials_mutex); 83 if (!serials_hash) 84 serials_hash = util_hash_table_create_ptr_keys(); 85 86 serial = (unsigned) (uintptr_t) util_hash_table_get(serials_hash, p); 87 if (!serial) { 88 /* time to stop logging... (you'll have a 100 GB logfile at least at 89 * this point) TODO: avoid this 90 */ 91 serial = ++serials_last; 92 if (!serial) { 93 debug_error("More than 2^32 objects detected, aborting.\n"); 94 os_abort(); 95 } 96 97 _mesa_hash_table_insert(serials_hash, p, (void *) (uintptr_t) serial); 98 found = FALSE; 99 } 100 mtx_unlock(&serials_mutex); 101 102 *pserial = serial; 103 104 return found; 105} 106 107 108/** 109 * Free the serial number for the given pointer. 110 */ 111static void 112debug_serial_delete(void *p) 113{ 114 mtx_lock(&serials_mutex); 115 _mesa_hash_table_remove_key(serials_hash, p); 116 mtx_unlock(&serials_mutex); 117} 118 119 120#if defined(PIPE_OS_WINDOWS) 121#define STACK_LEN 60 122#else 123#define STACK_LEN 64 124#endif 125 126/** 127 * Log a reference count change to the log file (if enabled). 128 * This is called via the pipe_reference() and debug_reference() functions, 129 * basically whenever a reference count is initialized or changed. 130 * 131 * \param p the refcount being changed (the value is not changed here) 132 * \param get_desc a function which will be called to print an object's 133 * name/pointer into a string buffer during logging 134 * \param change the reference count change which must be +/-1 or 0 when 135 * creating the object and initializing the refcount. 136 */ 137void 138debug_reference_slowpath(const struct pipe_reference *p, 139 debug_reference_descriptor get_desc, int change) 140{ 141 assert(change >= -1); 142 assert(change <= 1); 143 144 if (debug_refcnt_state < 0) 145 return; 146 147 if (!debug_refcnt_state) { 148 const char *filename = debug_get_option("GALLIUM_REFCNT_LOG", NULL); 149 if (filename && filename[0]) 150 stream = fopen(filename, "wt"); 151 152 if (stream) 153 debug_refcnt_state = 1; 154 else 155 debug_refcnt_state = -1; 156 } 157 158 if (debug_refcnt_state > 0) { 159 struct debug_stack_frame frames[STACK_LEN]; 160 char buf[1024]; 161 unsigned i; 162 unsigned refcnt = p->count; 163 unsigned serial; 164 boolean existing = debug_serial((void *) p, &serial); 165 166 debug_backtrace_capture(frames, 1, STACK_LEN); 167 168 get_desc(buf, p); 169 170 if (!existing) { 171 fprintf(stream, "<%s> %p %u Create\n", buf, (void *) p, serial); 172 debug_backtrace_print(stream, frames, STACK_LEN); 173 174 /* this is here to provide a gradual change even if we don't see 175 * the initialization 176 */ 177 if (refcnt <= 10) { 178 for (i = 1; i <= refcnt - change; ++i) { 179 fprintf(stream, "<%s> %p %u AddRef %u\n", buf, (void *) p, 180 serial, i); 181 debug_backtrace_print(stream, frames, STACK_LEN); 182 } 183 } 184 } 185 186 if (change) { 187 fprintf(stream, "<%s> %p %u %s %u\n", buf, (void *) p, serial, 188 change > 0 ? "AddRef" : "Release", refcnt); 189 debug_backtrace_print(stream, frames, STACK_LEN); 190 } 191 192 if (!refcnt) { 193 debug_serial_delete((void *) p); 194 fprintf(stream, "<%s> %p %u Destroy\n", buf, (void *) p, serial); 195 debug_backtrace_print(stream, frames, STACK_LEN); 196 } 197 198 fflush(stream); 199 } 200} 201 202#endif /* DEBUG */ 203