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 /* see http://www.mozilla.org/performance/refcnt-balancer.html for what do with the output
     30  * on Linux, use tools/addr2line.sh to postprocess it before anything else
     31  **/
     32 
     33 #include <stdio.h>
     34 
     35 #include "util/u_debug.h"
     36 #include "util/u_debug_refcnt.h"
     37 #include "util/u_debug_stack.h"
     38 #include "util/u_debug_symbol.h"
     39 #include "util/u_string.h"
     40 #include "util/u_hash_table.h"
     41 #include "os/os_thread.h"
     42 
     43 int debug_refcnt_state;
     44 
     45 FILE* stream;
     46 
     47 /* TODO: maybe move this serial machinery to a stand-alone module and expose it? */
     48 pipe_static_mutex(serials_mutex);
     49 
     50 static struct util_hash_table* serials_hash;
     51 static unsigned serials_last;
     52 
     53 static unsigned hash_ptr(void* p)
     54 {
     55    return (unsigned)(uintptr_t)p;
     56 }
     57 
     58 static int compare_ptr(void* a, void* b)
     59 {
     60    if(a == b)
     61       return 0;
     62    else if(a < b)
     63       return -1;
     64    else
     65       return 1;
     66 }
     67 
     68 static boolean debug_serial(void* p, unsigned* pserial)
     69 {
     70    unsigned serial;
     71    boolean found = TRUE;
     72 #ifdef PIPE_SUBSYSTEM_WINDOWS_USER
     73    static boolean first = TRUE;
     74 
     75    if (first) {
     76       pipe_mutex_init(serials_mutex);
     77       first = FALSE;
     78    }
     79 #endif
     80 
     81    pipe_mutex_lock(serials_mutex);
     82    if(!serials_hash)
     83       serials_hash = util_hash_table_create(hash_ptr, compare_ptr);
     84    serial = (unsigned)(uintptr_t)util_hash_table_get(serials_hash, p);
     85    if(!serial)
     86    {
     87       /* time to stop logging... (you'll have a 100 GB logfile at least at this point)
     88        * TODO: avoid this
     89        */
     90       serial = ++serials_last;
     91       if(!serial)
     92       {
     93          debug_error("More than 2^32 objects detected, aborting.\n");
     94          os_abort();
     95       }
     96 
     97       util_hash_table_set(serials_hash, p, (void*)(uintptr_t)serial);
     98       found = FALSE;
     99    }
    100    pipe_mutex_unlock(serials_mutex);
    101    *pserial = serial;
    102    return found;
    103 }
    104 
    105 static void debug_serial_delete(void* p)
    106 {
    107    pipe_mutex_lock(serials_mutex);
    108    util_hash_table_remove(serials_hash, p);
    109    pipe_mutex_unlock(serials_mutex);
    110 }
    111 
    112 #define STACK_LEN 64
    113 
    114 static void dump_stack(const char* symbols[STACK_LEN])
    115 {
    116    unsigned i;
    117    for(i = 0; i < STACK_LEN; ++i)
    118    {
    119       if(symbols[i])
    120          fprintf(stream, "%s\n", symbols[i]);
    121    }
    122    fprintf(stream, "\n");
    123 }
    124 
    125 void debug_reference_slowpath(const struct pipe_reference* p, debug_reference_descriptor get_desc, int change)
    126 {
    127    if(debug_refcnt_state < 0)
    128       return;
    129 
    130    if(!debug_refcnt_state)
    131    {
    132       const char* filename = debug_get_option("GALLIUM_REFCNT_LOG", NULL);
    133       if(filename && filename[0])
    134          stream = fopen(filename, "wt");
    135 
    136       if(stream)
    137          debug_refcnt_state = 1;
    138       else
    139          debug_refcnt_state = -1;
    140    }
    141 
    142    if(debug_refcnt_state > 0)
    143    {
    144       struct debug_stack_frame frames[STACK_LEN];
    145       const char* symbols[STACK_LEN];
    146       char buf[1024];
    147 
    148       unsigned i;
    149       unsigned refcnt = p->count;
    150       unsigned serial;
    151       boolean existing = debug_serial((void*)p, &serial);
    152 
    153       debug_backtrace_capture(frames, 1, STACK_LEN);
    154       for(i = 0; i < STACK_LEN; ++i)
    155       {
    156          if(frames[i].function)
    157             symbols[i] = debug_symbol_name_cached(frames[i].function);
    158          else
    159             symbols[i] = 0;
    160       }
    161 
    162       get_desc(buf, p);
    163 
    164       if(!existing)
    165       {
    166          fprintf(stream, "<%s> %p %u Create\n", buf, (void *) p, serial);
    167          dump_stack(symbols);
    168 
    169          /* this is there to provide a gradual change even if we don't see the initialization */
    170          for(i = 1; i <= refcnt - change; ++i)
    171          {
    172             fprintf(stream, "<%s> %p %u AddRef %u\n", buf, (void *) p,
    173                     serial, i);
    174             dump_stack(symbols);
    175          }
    176       }
    177 
    178       if(change)
    179       {
    180          fprintf(stream, "<%s> %p %u %s %u\n", buf, (void *) p, serial,
    181                  change > 0 ? "AddRef" : "Release", refcnt);
    182          dump_stack(symbols);
    183       }
    184 
    185       if(!refcnt)
    186       {
    187          debug_serial_delete((void*)p);
    188          fprintf(stream, "<%s> %p %u Destroy\n", buf, (void *) p, serial);
    189          dump_stack(symbols);
    190       }
    191 
    192       fflush(stream);
    193    }
    194 }
    195 #endif
    196