17ec681f3Smrg/************************************************************************** 27ec681f3Smrg * 37ec681f3Smrg * Copyright 2010 Luca Barbieri 47ec681f3Smrg * 57ec681f3Smrg * Permission is hereby granted, free of charge, to any person obtaining 67ec681f3Smrg * a copy of this software and associated documentation files (the 77ec681f3Smrg * "Software"), to deal in the Software without restriction, including 87ec681f3Smrg * without limitation the rights to use, copy, modify, merge, publish, 97ec681f3Smrg * distribute, sublicense, and/or sell copies of the Software, and to 107ec681f3Smrg * permit persons to whom the Software is furnished to do so, subject to 117ec681f3Smrg * the following conditions: 127ec681f3Smrg * 137ec681f3Smrg * The above copyright notice and this permission notice (including the 147ec681f3Smrg * next paragraph) shall be included in all copies or substantial 157ec681f3Smrg * portions of the Software. 167ec681f3Smrg * 177ec681f3Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 187ec681f3Smrg * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 197ec681f3Smrg * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 207ec681f3Smrg * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE 217ec681f3Smrg * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 227ec681f3Smrg * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 237ec681f3Smrg * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 247ec681f3Smrg * 257ec681f3Smrg **************************************************************************/ 267ec681f3Smrg 277ec681f3Smrg#if defined(DEBUG) 287ec681f3Smrg 297ec681f3Smrg/** 307ec681f3Smrg * If the GALLIUM_REFCNT_LOG env var is defined as a filename, gallium 317ec681f3Smrg * reference counting will be logged to the file. 327ec681f3Smrg * 337ec681f3Smrg * See http://www-archive.mozilla.org/performance/refcnt-balancer.html 347ec681f3Smrg * for what to do with the output on Linux, use tools/addr2line.sh to 357ec681f3Smrg * postprocess it before anything else. 367ec681f3Smrg */ 377ec681f3Smrg 387ec681f3Smrg#include <stdio.h> 397ec681f3Smrg 407ec681f3Smrg#include "util/u_debug.h" 417ec681f3Smrg#include "util/u_debug_refcnt.h" 427ec681f3Smrg#include "util/u_debug_stack.h" 437ec681f3Smrg#include "util/u_debug_symbol.h" 447ec681f3Smrg#include "util/u_string.h" 457ec681f3Smrg#include "util/u_hash_table.h" 467ec681f3Smrg#include "util/u_thread.h" 477ec681f3Smrg 487ec681f3Smrgint debug_refcnt_state; 497ec681f3Smrg 507ec681f3Smrgstatic FILE *stream; 517ec681f3Smrg 527ec681f3Smrg/* TODO: maybe move this serial machinery to a stand-alone module and 537ec681f3Smrg * expose it? 547ec681f3Smrg */ 557ec681f3Smrg#ifdef PIPE_OS_WINDOWS 567ec681f3Smrgstatic mtx_t serials_mutex; 577ec681f3Smrg#else 587ec681f3Smrgstatic mtx_t serials_mutex = _MTX_INITIALIZER_NP; 597ec681f3Smrg#endif 607ec681f3Smrg 617ec681f3Smrgstatic struct hash_table *serials_hash; 627ec681f3Smrgstatic unsigned serials_last; 637ec681f3Smrg 647ec681f3Smrg 657ec681f3Smrg/** 667ec681f3Smrg * Return a small integer serial number for the given pointer. 677ec681f3Smrg */ 687ec681f3Smrgstatic boolean 697ec681f3Smrgdebug_serial(void *p, unsigned *pserial) 707ec681f3Smrg{ 717ec681f3Smrg unsigned serial; 727ec681f3Smrg boolean found = TRUE; 737ec681f3Smrg#ifdef PIPE_OS_WINDOWS 747ec681f3Smrg static boolean first = TRUE; 757ec681f3Smrg 767ec681f3Smrg if (first) { 777ec681f3Smrg (void) mtx_init(&serials_mutex, mtx_plain); 787ec681f3Smrg first = FALSE; 797ec681f3Smrg } 807ec681f3Smrg#endif 817ec681f3Smrg 827ec681f3Smrg mtx_lock(&serials_mutex); 837ec681f3Smrg if (!serials_hash) 847ec681f3Smrg serials_hash = util_hash_table_create_ptr_keys(); 857ec681f3Smrg 867ec681f3Smrg serial = (unsigned) (uintptr_t) util_hash_table_get(serials_hash, p); 877ec681f3Smrg if (!serial) { 887ec681f3Smrg /* time to stop logging... (you'll have a 100 GB logfile at least at 897ec681f3Smrg * this point) TODO: avoid this 907ec681f3Smrg */ 917ec681f3Smrg serial = ++serials_last; 927ec681f3Smrg if (!serial) { 937ec681f3Smrg debug_error("More than 2^32 objects detected, aborting.\n"); 947ec681f3Smrg os_abort(); 957ec681f3Smrg } 967ec681f3Smrg 977ec681f3Smrg _mesa_hash_table_insert(serials_hash, p, (void *) (uintptr_t) serial); 987ec681f3Smrg found = FALSE; 997ec681f3Smrg } 1007ec681f3Smrg mtx_unlock(&serials_mutex); 1017ec681f3Smrg 1027ec681f3Smrg *pserial = serial; 1037ec681f3Smrg 1047ec681f3Smrg return found; 1057ec681f3Smrg} 1067ec681f3Smrg 1077ec681f3Smrg 1087ec681f3Smrg/** 1097ec681f3Smrg * Free the serial number for the given pointer. 1107ec681f3Smrg */ 1117ec681f3Smrgstatic void 1127ec681f3Smrgdebug_serial_delete(void *p) 1137ec681f3Smrg{ 1147ec681f3Smrg mtx_lock(&serials_mutex); 1157ec681f3Smrg _mesa_hash_table_remove_key(serials_hash, p); 1167ec681f3Smrg mtx_unlock(&serials_mutex); 1177ec681f3Smrg} 1187ec681f3Smrg 1197ec681f3Smrg 1207ec681f3Smrg#if defined(PIPE_OS_WINDOWS) 1217ec681f3Smrg#define STACK_LEN 60 1227ec681f3Smrg#else 1237ec681f3Smrg#define STACK_LEN 64 1247ec681f3Smrg#endif 1257ec681f3Smrg 1267ec681f3Smrg/** 1277ec681f3Smrg * Log a reference count change to the log file (if enabled). 1287ec681f3Smrg * This is called via the pipe_reference() and debug_reference() functions, 1297ec681f3Smrg * basically whenever a reference count is initialized or changed. 1307ec681f3Smrg * 1317ec681f3Smrg * \param p the refcount being changed (the value is not changed here) 1327ec681f3Smrg * \param get_desc a function which will be called to print an object's 1337ec681f3Smrg * name/pointer into a string buffer during logging 1347ec681f3Smrg * \param change the reference count change which must be +/-1 or 0 when 1357ec681f3Smrg * creating the object and initializing the refcount. 1367ec681f3Smrg */ 1377ec681f3Smrgvoid 1387ec681f3Smrgdebug_reference_slowpath(const struct pipe_reference *p, 1397ec681f3Smrg debug_reference_descriptor get_desc, int change) 1407ec681f3Smrg{ 1417ec681f3Smrg assert(change >= -1); 1427ec681f3Smrg assert(change <= 1); 1437ec681f3Smrg 1447ec681f3Smrg if (debug_refcnt_state < 0) 1457ec681f3Smrg return; 1467ec681f3Smrg 1477ec681f3Smrg if (!debug_refcnt_state) { 1487ec681f3Smrg const char *filename = debug_get_option("GALLIUM_REFCNT_LOG", NULL); 1497ec681f3Smrg if (filename && filename[0]) 1507ec681f3Smrg stream = fopen(filename, "wt"); 1517ec681f3Smrg 1527ec681f3Smrg if (stream) 1537ec681f3Smrg debug_refcnt_state = 1; 1547ec681f3Smrg else 1557ec681f3Smrg debug_refcnt_state = -1; 1567ec681f3Smrg } 1577ec681f3Smrg 1587ec681f3Smrg if (debug_refcnt_state > 0) { 1597ec681f3Smrg struct debug_stack_frame frames[STACK_LEN]; 1607ec681f3Smrg char buf[1024]; 1617ec681f3Smrg unsigned i; 1627ec681f3Smrg unsigned refcnt = p->count; 1637ec681f3Smrg unsigned serial; 1647ec681f3Smrg boolean existing = debug_serial((void *) p, &serial); 1657ec681f3Smrg 1667ec681f3Smrg debug_backtrace_capture(frames, 1, STACK_LEN); 1677ec681f3Smrg 1687ec681f3Smrg get_desc(buf, p); 1697ec681f3Smrg 1707ec681f3Smrg if (!existing) { 1717ec681f3Smrg fprintf(stream, "<%s> %p %u Create\n", buf, (void *) p, serial); 1727ec681f3Smrg debug_backtrace_print(stream, frames, STACK_LEN); 1737ec681f3Smrg 1747ec681f3Smrg /* this is here to provide a gradual change even if we don't see 1757ec681f3Smrg * the initialization 1767ec681f3Smrg */ 1777ec681f3Smrg for (i = 1; i <= refcnt - change; ++i) { 1787ec681f3Smrg fprintf(stream, "<%s> %p %u AddRef %u\n", buf, (void *) p, 1797ec681f3Smrg serial, i); 1807ec681f3Smrg debug_backtrace_print(stream, frames, STACK_LEN); 1817ec681f3Smrg } 1827ec681f3Smrg } 1837ec681f3Smrg 1847ec681f3Smrg if (change) { 1857ec681f3Smrg fprintf(stream, "<%s> %p %u %s %u\n", buf, (void *) p, serial, 1867ec681f3Smrg change > 0 ? "AddRef" : "Release", refcnt); 1877ec681f3Smrg debug_backtrace_print(stream, frames, STACK_LEN); 1887ec681f3Smrg } 1897ec681f3Smrg 1907ec681f3Smrg if (!refcnt) { 1917ec681f3Smrg debug_serial_delete((void *) p); 1927ec681f3Smrg fprintf(stream, "<%s> %p %u Destroy\n", buf, (void *) p, serial); 1937ec681f3Smrg debug_backtrace_print(stream, frames, STACK_LEN); 1947ec681f3Smrg } 1957ec681f3Smrg 1967ec681f3Smrg fflush(stream); 1977ec681f3Smrg } 1987ec681f3Smrg} 1997ec681f3Smrg 2007ec681f3Smrg#endif /* DEBUG */ 201