gallium/util: replace pipe_mutex_unlock() with mtx_unlock()
[mesa.git] / src / gallium / auxiliary / util / u_debug_refcnt.c
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 static mtx_t serials_mutex = _MTX_INITIALIZER_NP;
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 (void) mtx_init(&serials_mutex, mtx_plain);
93 first = FALSE;
94 }
95 #endif
96
97 mtx_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 mtx_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 mtx_lock(&serials_mutex);
130 util_hash_table_remove(serials_hash, p);
131 mtx_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 */