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