Mesa (nvfx-next-7f): reference debugging code

Luca Barbieri lb at kemper.freedesktop.org
Tue Aug 17 21:22:34 UTC 2010


Module: Mesa
Branch: nvfx-next-7f
Commit: 2c62ce2789ff3a133b74ff2d96574dfcfe2ed8c1
URL:    http://cgit.freedesktop.org/mesa/mesa/commit/?id=2c62ce2789ff3a133b74ff2d96574dfcfe2ed8c1

Author: Luca Barbieri <luca at luca-barbieri.com>
Date:   Mon Aug  9 05:05:41 2010 +0200

reference debugging code

---

 src/gallium/auxiliary/Makefile                |    2 +
 src/gallium/auxiliary/SConscript              |    2 +
 src/gallium/auxiliary/util/u_debug_describe.c |   43 +++++++
 src/gallium/auxiliary/util/u_debug_describe.h |   17 +++
 src/gallium/auxiliary/util/u_debug_refcnt.c   |  149 +++++++++++++++++++++++++
 src/gallium/auxiliary/util/u_debug_refcnt.h   |   21 ++++
 src/gallium/auxiliary/util/u_debug_symbol.c   |   55 +++++++--
 src/gallium/auxiliary/util/u_debug_symbol.h   |    3 +
 src/gallium/auxiliary/util/u_inlines.h        |   22 +++-
 src/gallium/tools/addr2line.sh                |   27 +++++
 10 files changed, 323 insertions(+), 18 deletions(-)

diff --git a/src/gallium/auxiliary/Makefile b/src/gallium/auxiliary/Makefile
index 3533526..215ffb3 100644
--- a/src/gallium/auxiliary/Makefile
+++ b/src/gallium/auxiliary/Makefile
@@ -92,6 +92,8 @@ C_SOURCES = \
 	translate/translate.c \
 	translate/translate_cache.c \
 	util/u_debug.c \
+	util/u_debug_describe.c \
+	util/u_debug_refcnt.c \
 	util/u_debug_symbol.c \
 	util/u_debug_stack.c \
 	util/u_dump_defines.c \
diff --git a/src/gallium/auxiliary/SConscript b/src/gallium/auxiliary/SConscript
index 30e5d02..992732f 100644
--- a/src/gallium/auxiliary/SConscript
+++ b/src/gallium/auxiliary/SConscript
@@ -146,7 +146,9 @@ source = [
     'util/u_caps.c',
     'util/u_cpu_detect.c',
     'util/u_debug.c',
+    'util/u_debug_describe.c',
     'util/u_debug_memory.c',
+    'util/u_debug_refcnt.c',
     'util/u_debug_stack.c',
     'util/u_debug_symbol.c',
     'util/u_dump_defines.c',
diff --git a/src/gallium/auxiliary/util/u_debug_describe.c b/src/gallium/auxiliary/util/u_debug_describe.c
new file mode 100644
index 0000000..ba87e1c
--- /dev/null
+++ b/src/gallium/auxiliary/util/u_debug_describe.c
@@ -0,0 +1,43 @@
+#include <pipe/p_state.h>
+#include <util/u_format.h>
+#include <util/u_debug_describe.h>
+#include <stdio.h>
+
+void
+util_debug_describe_reference(char* buf, const struct pipe_reference*ptr)
+{
+	strcpy(buf, "pipe_object");
+}
+
+void
+util_debug_describe_resource(char* buf, const struct pipe_resource *ptr)
+{
+	if(ptr->target == PIPE_BUFFER)
+		sprintf(buf, "pipe_buffer<%u>", util_format_get_stride(ptr->format, ptr->width0));
+	else if(ptr->target == PIPE_TEXTURE_1D)
+		sprintf(buf, "pipe_texture1d<%u,%s,%u>", ptr->width0, util_format_short_name(ptr->format), ptr->last_level);
+	else if(ptr->target == PIPE_TEXTURE_2D)
+		sprintf(buf, "pipe_texture2d<%u,%u,%s,%u>", ptr->width0, ptr->height0, util_format_short_name(ptr->format), ptr->last_level);
+	else if(ptr->target == PIPE_TEXTURE_CUBE)
+		sprintf(buf, "pipe_texture_cube<%u,%u,%s,%u>", ptr->width0, ptr->height0, util_format_short_name(ptr->format), ptr->last_level);
+	else if(ptr->target == PIPE_TEXTURE_3D)
+		sprintf(buf, "pipe_texture3d<%u,%u,%u,%s,%u>", ptr->width0, ptr->height0, ptr->depth0, util_format_short_name(ptr->format), ptr->last_level);
+	else
+		sprintf(buf, "pipe_martian_resource<%u>", ptr->target);
+}
+
+void
+util_debug_describe_surface(char* buf, const struct pipe_surface *ptr)
+{
+	char res[128];
+	util_debug_describe_resource(res, ptr->texture);
+	sprintf(buf, "pipe_surface<%s,%u,%u,%u>", res, ptr->face, ptr->level, ptr->zslice);
+}
+
+void
+util_debug_describe_sampler_view(char* buf, const struct pipe_sampler_view *ptr)
+{
+	char res[128];
+	util_debug_describe_resource(res, ptr->texture);
+	sprintf(buf, "pipe_sampler_view<%s,%s>", res, util_format_short_name(ptr->format));
+}
diff --git a/src/gallium/auxiliary/util/u_debug_describe.h b/src/gallium/auxiliary/util/u_debug_describe.h
new file mode 100644
index 0000000..4271195
--- /dev/null
+++ b/src/gallium/auxiliary/util/u_debug_describe.h
@@ -0,0 +1,17 @@
+/*
+ * u_debug_describe.h
+ *
+ *  Created on: Aug 17, 2010
+ *      Author: lb
+ */
+
+#ifndef U_DEBUG_DESCRIBE_H_
+#define U_DEBUG_DESCRIBE_H_
+
+/* a 256-byte buffer must be enough */
+void util_debug_describe_reference(char* buf, const struct pipe_reference*ptr);
+void util_debug_describe_resource(char* buf, const struct pipe_resource *ptr);
+void util_debug_describe_surface(char* buf, const struct pipe_surface *ptr);
+void util_debug_describe_sampler_view(char* buf, const struct pipe_sampler_view *ptr);
+
+#endif /* U_DEBUG_DESCRIBE_H_ */
diff --git a/src/gallium/auxiliary/util/u_debug_refcnt.c b/src/gallium/auxiliary/util/u_debug_refcnt.c
new file mode 100644
index 0000000..b758002
--- /dev/null
+++ b/src/gallium/auxiliary/util/u_debug_refcnt.c
@@ -0,0 +1,149 @@
+#if defined(DEBUG) && (!defined(PIPE_OS_WINDOWS) || defined(PIPE_SUBSYSTEM_WINDOWS_USER))
+
+/* see http://www.mozilla.org/performance/refcnt-balancer.html for what do with the output
+ * on Linux, use u_debug_symbol_addr2line.sh to postprocess it before anything else
+ **/
+#include <util/u_debug.h>
+#include <util/u_debug_refcnt.h>
+#include <util/u_debug_stack.h>
+#include <util/u_debug_symbol.h>
+#include <stdio.h>
+#include <util/u_hash_table.h>
+#include <os/os_thread.h>
+
+static int state;
+static FILE* fp;
+
+/* TODO: maybe move this serial machinery to a stand-alone module and expose it? */
+pipe_mutex serials_mutex;
+struct util_hash_table* serials_hash;
+unsigned serials_last;
+
+static unsigned hash_ptr(void* p)
+{
+	return (unsigned)(uintptr_t)p;
+}
+
+static int compare_ptr(void* a, void* b)
+{
+	if(a == b)
+		return 0;
+	else if(a < b)
+		return -1;
+	else
+		return 1;
+}
+
+static boolean util_debug_serial(void* p, unsigned* pserial)
+{
+	unsigned serial;
+	boolean found = TRUE;
+	pipe_mutex_lock(serials_mutex);
+	if(!serials_hash)
+		serials_hash = util_hash_table_create(hash_ptr, compare_ptr);
+	serial = (unsigned)(uintptr_t)util_hash_table_get(serials_hash, p);
+	if(!serial)
+	{
+		/* time to stop logging... (you'll have a 100 GB logfile at least at this point)
+		 * TODO: avoid this
+		 */
+		serial = ++serials_last;
+		if(!serial)
+		{
+			fprintf(stderr, "More than 2^32 objects detected, aborting.\n");
+			abort();
+		}
+
+		util_hash_table_set(serials_hash, p, (void*)(uintptr_t)serial);
+		found = FALSE;
+	}
+	pipe_mutex_unlock(serials_mutex);
+	*pserial = serial;
+	return found;
+}
+
+static void util_debug_serial_delete(void* p)
+{
+	pipe_mutex_lock(serials_mutex);
+	util_hash_table_remove(serials_hash, p);
+	pipe_mutex_unlock(serials_mutex);
+}
+
+#define STACK_LEN 16
+
+static void dump_stack(char symbols[STACK_LEN][1024])
+{
+	unsigned i;
+	for(i = 0; i < STACK_LEN; ++i)
+	{
+		if(symbols[i][0])
+			fprintf(fp, "%s\n", symbols[i]);
+	}
+	fprintf(fp, "\n");
+}
+
+void util_debug_reference(const struct pipe_reference* p, void* pget_desc, const char* op)
+{
+	if(state < 0)
+		return;
+
+	if(!state)
+	{
+		const char* filename = debug_get_option("GALLIUM_REFCNT_LOG", NULL);
+		state = -1;
+		if(filename && filename[0])
+			fp = fopen(filename, "w");
+
+		if(fp)
+			state = 1;
+	}
+
+	if(state > 0)
+	{
+		struct debug_stack_frame frames[STACK_LEN];
+		char symbols[STACK_LEN][1024];
+		char buf[1024];
+
+		void (*get_desc)(char*, const struct pipe_reference*) = pget_desc;
+		unsigned i;
+		unsigned refcnt = p->count;
+		unsigned serial;
+		boolean existing = util_debug_serial((void*)p, &serial);
+
+		debug_backtrace_capture(frames, 1, STACK_LEN);
+		for(i = 0; i < STACK_LEN; ++i)
+		{
+			if(frames[i].function)
+				debug_symbol_name(frames[i].function, symbols[i], sizeof(symbols[i]));
+			else
+				symbols[i][0] = 0;
+		}
+
+		get_desc(buf, p);
+
+		if(!existing)
+		{
+			fprintf(fp, "<%s> %p %u Create\n", buf, p, serial);
+			dump_stack(symbols);
+
+			for(i = 1; i < refcnt; ++i)
+			{
+				fprintf(fp, "<%s> %p %u AddRef %u\n", buf, p, serial, i);
+				dump_stack(symbols);
+			}
+		}
+
+		fprintf(fp, "<%s> %p %u %s %u\n", buf, p, serial, op, refcnt);
+		dump_stack(symbols);
+
+		if(!refcnt)
+		{
+			util_debug_serial_delete((void*)p);
+			fprintf(fp, "<%s> %p %u Destroy\n", buf, p, serial);
+			dump_stack(symbols);
+		}
+
+		fflush(fp);
+	}
+}
+#endif
diff --git a/src/gallium/auxiliary/util/u_debug_refcnt.h b/src/gallium/auxiliary/util/u_debug_refcnt.h
new file mode 100644
index 0000000..878bacf
--- /dev/null
+++ b/src/gallium/auxiliary/util/u_debug_refcnt.h
@@ -0,0 +1,21 @@
+/*
+ * u_debug_refcnt.h
+ *
+ *  Created on: Aug 17, 2010
+ *      Author: lb
+ */
+
+#ifndef U_DEBUG_REFCNT_H_
+#define U_DEBUG_REFCNT_H_
+
+#include <pipe/p_config.h>
+#include <pipe/p_state.h>
+
+#if defined(DEBUG) && (!defined(PIPE_OS_WINDOWS) || defined(PIPE_SUBSYSTEM_WINDOWS_USER))
+void util_debug_reference(const struct pipe_reference* p, void* get_desc, const char* op);
+#else
+static INLINE void util_debug_reference(const struct pipe_reference* p, void* get_desc, const char* op)
+{}
+#endif
+
+#endif /* U_DEBUG_REFCNT_H_ */
diff --git a/src/gallium/auxiliary/util/u_debug_symbol.c b/src/gallium/auxiliary/util/u_debug_symbol.c
index 6e25057..174d7c5 100644
--- a/src/gallium/auxiliary/util/u_debug_symbol.c
+++ b/src/gallium/auxiliary/util/u_debug_symbol.c
@@ -33,6 +33,7 @@
  */
 
 #include "pipe/p_compiler.h"
+#include <stdio.h>
 
 #include "u_debug.h"
 #include "u_debug_symbol.h"
@@ -113,8 +114,8 @@ BOOL WINAPI j_SymGetSymFromAddr(HANDLE hProcess, DWORD Address, PDWORD Displacem
 }
 
 
-static INLINE boolean
-debug_symbol_print_imagehlp(const void *addr)
+static INLINE void
+debug_symbol_name_imagehlp(const void *addr, char* buf, unsigned size)
 {
    HANDLE hProcess;
    BYTE symbolBuffer[1024];
@@ -133,23 +134,55 @@ debug_symbol_print_imagehlp(const void *addr)
    }
       
    if(!j_SymGetSymFromAddr(hProcess, (DWORD)addr, &dwDisplacement, pSymbol))
-      return FALSE;
+      buf[0] = 0;
+   else
+   {
+      strncpy(buf, pSymbol->Name, size);
+      buf[size - 1] = 0;
+   }
+}
+#endif
 
-   debug_printf("\t%s\n", pSymbol->Name);
+#ifdef __GLIBC__
+#include <execinfo.h>
 
-   return TRUE;
-   
+/* This can only provide dynamic symbols, or binary offsets into a file.
+ *
+ * To fix this, post-process the output with tools/addr2line.sh
+ */
+static INLINE void
+debug_symbol_name_glibc(const void *addr, char* buf, unsigned size)
+{
+   char** syms = backtrace_symbols((void**)&addr, 1);
+   strncpy(buf, syms[0], size);
+   buf[size - 1] = 0;
+   free(syms);
 }
 #endif
 
-
 void
-debug_symbol_print(const void *addr)
+debug_symbol_name(const void *addr, char* buf, unsigned size)
 {
 #if defined(PIPE_SUBSYSTEM_WINDOWS_USER) && defined(PIPE_ARCH_X86)
-   if(debug_symbol_print_imagehlp(addr))
+   debug_symbol_name_imagehlp(addr, buf, size);
+   if(buf[0])
       return;
 #endif
-   
-   debug_printf("\t%p\n", addr);
+
+#ifdef __GLIBC__
+   debug_symbol_name_glibc(addr, buf, size);
+   if(buf[0])
+      return;
+#endif
+
+   snprintf(buf, size, "%p", addr);
+   buf[size - 1] = 0;
+}
+
+void
+debug_symbol_print(const void *addr)
+{
+   char buf[1024];
+   debug_symbol_name(addr, buf, sizeof(buf));
+   debug_printf("\t%s\n", buf);
 }
diff --git a/src/gallium/auxiliary/util/u_debug_symbol.h b/src/gallium/auxiliary/util/u_debug_symbol.h
index 0215869..5e283e5 100644
--- a/src/gallium/auxiliary/util/u_debug_symbol.h
+++ b/src/gallium/auxiliary/util/u_debug_symbol.h
@@ -43,6 +43,9 @@ extern "C" {
 
 
 void
+debug_symbol_name(const void *addr, char* buf, unsigned size);
+
+void
 debug_symbol_print(const void *addr);
 
 
diff --git a/src/gallium/auxiliary/util/u_inlines.h b/src/gallium/auxiliary/util/u_inlines.h
index 540305c..e216791 100644
--- a/src/gallium/auxiliary/util/u_inlines.h
+++ b/src/gallium/auxiliary/util/u_inlines.h
@@ -33,6 +33,8 @@
 #include "pipe/p_state.h"
 #include "pipe/p_screen.h"
 #include "util/u_debug.h"
+#include "util/u_debug_describe.h"
+#include "util/u_debug_refcnt.h"
 #include "util/u_atomic.h"
 #include "util/u_box.h"
 #include "util/u_math.h"
@@ -67,15 +69,17 @@ pipe_is_referenced(struct pipe_reference *reference)
  * \return TRUE if the object's refcount hits zero and should be destroyed.
  */
 static INLINE boolean
-pipe_reference(struct pipe_reference *ptr, struct pipe_reference *reference)
+pipe_reference_described(struct pipe_reference *ptr, struct pipe_reference *reference, void* get_desc)
 {
    boolean destroy = FALSE;
 
    if(ptr != reference) {
-      /* bump the reference.count first */
+
+	   /* bump the reference.count first */
       if (reference) {
          assert(pipe_is_referenced(reference));
          p_atomic_inc(&reference->count);
+       	 util_debug_reference(reference, get_desc, "AddRef");
       }
 
       if (ptr) {
@@ -83,41 +87,45 @@ pipe_reference(struct pipe_reference *ptr, struct pipe_reference *reference)
          if (p_atomic_dec_zero(&ptr->count)) {
             destroy = TRUE;
          }
+         util_debug_reference(ptr, get_desc, "Release");
       }
    }
 
    return destroy;
 }
 
+static INLINE boolean
+pipe_reference(struct pipe_reference *ptr, struct pipe_reference *reference)
+{
+	return pipe_reference_described(ptr, reference, util_debug_describe_reference);
+}
 
 static INLINE void
 pipe_surface_reference(struct pipe_surface **ptr, struct pipe_surface *surf)
 {
    struct pipe_surface *old_surf = *ptr;
 
-   if (pipe_reference(&(*ptr)->reference, &surf->reference))
+   if (pipe_reference_described(&(*ptr)->reference, &surf->reference, util_debug_describe_surface))
       old_surf->texture->screen->tex_surface_destroy(old_surf);
    *ptr = surf;
 }
 
-
 static INLINE void
 pipe_resource_reference(struct pipe_resource **ptr, struct pipe_resource *tex)
 {
    struct pipe_resource *old_tex = *ptr;
 
-   if (pipe_reference(&(*ptr)->reference, &tex->reference))
+   if (pipe_reference_described(&(*ptr)->reference, &tex->reference, util_debug_describe_resource))
       old_tex->screen->resource_destroy(old_tex->screen, old_tex);
    *ptr = tex;
 }
 
-
 static INLINE void
 pipe_sampler_view_reference(struct pipe_sampler_view **ptr, struct pipe_sampler_view *view)
 {
    struct pipe_sampler_view *old_view = *ptr;
 
-   if (pipe_reference(&(*ptr)->reference, &view->reference))
+   if (pipe_reference_described(&(*ptr)->reference, &view->reference, util_debug_describe_sampler_view))
       old_view->context->sampler_view_destroy(old_view->context, old_view);
    *ptr = view;
 }
diff --git a/src/gallium/tools/addr2line.sh b/src/gallium/tools/addr2line.sh
new file mode 100755
index 0000000..5573803
--- /dev/null
+++ b/src/gallium/tools/addr2line.sh
@@ -0,0 +1,27 @@
+#!/bin/bash
+# This script processes symbols output by Gallium using glibc to human-readable function names
+
+lastbin=
+i=-1
+dir="$(mktemp -d)"
+input="$1"
+
+# Gather all unique addresses for each binary
+sed -nre 's|([^ ]*/[^ ]*)\(\+0x([^)]*).*|\1 \2|p' "$input"|sort|uniq|while read bin addr; do
+	if test "$lastbin" != "$bin"; then
+		((++i))
+		lastbin="$bin"
+		echo "$bin" > "$dir/$i.addrs.bin"
+	fi
+	echo "$addr" >> "$dir/$i.addrs"
+done
+
+# Construct a sed script to convert hex address to human readable form, and apply it
+for i in "$dir"/*.addrs; do
+	bin="$(<"$i.bin")"
+	addr2line -p -e "$bin" -a -f < "$i"|sed -nre 's@^0x0*([^:]*): ([^?]*)$@s|'"$bin"'(+0x\1)|\2|g at gp' 
+	rm -f "$i" "$i.bin"
+done|sed -f - "$input"
+
+rmdir "$dir"
+




More information about the mesa-commit mailing list