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