pb_bufmgr_debug.c revision fca7f384418fa6e353d41b2e05117e0553526053
1/**************************************************************************
2 *
3 * Copyright 2007-2008 Tungsten Graphics, Inc., Cedar Park, Texas.
4 * All Rights Reserved.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sub license, and/or sell copies of the Software, and to
11 * permit persons to whom the Software is furnished to do so, subject to
12 * the following conditions:
13 *
14 * The above copyright notice and this permission notice (including the
15 * next paragraph) shall be included in all copies or substantial portions
16 * of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
21 * IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR
22 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
23 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 *
26 **************************************************************************/
27
28/**
29 * \file
30 * Debug buffer manager to detect buffer under- and overflows.
31 *
32 * \author Jose Fonseca <jrfonseca@tungstengraphics.com>
33 */
34
35
36#include "pipe/p_compiler.h"
37#include "util/u_debug.h"
38#include "pipe/p_thread.h"
39#include "util/u_math.h"
40#include "util/u_memory.h"
41#include "util/u_double_list.h"
42#include "util/u_time.h"
43#include "util/u_debug_stack.h"
44
45#include "pb_buffer.h"
46#include "pb_bufmgr.h"
47
48
49#ifdef DEBUG
50
51
52#define PB_DEBUG_CREATE_BACKTRACE 8
53#define PB_DEBUG_MAP_BACKTRACE 8
54
55
56/**
57 * Convenience macro (type safe).
58 */
59#define SUPER(__derived) (&(__derived)->base)
60
61
62struct pb_debug_manager;
63
64
65/**
66 * Wrapper around a pipe buffer which adds delayed destruction.
67 */
68struct pb_debug_buffer
69{
70   struct pb_buffer base;
71
72   struct pb_buffer *buffer;
73   struct pb_debug_manager *mgr;
74
75   pb_size underflow_size;
76   pb_size overflow_size;
77
78   struct debug_stack_frame create_backtrace[PB_DEBUG_CREATE_BACKTRACE];
79
80   pipe_mutex mutex;
81   unsigned map_count;
82   struct debug_stack_frame map_backtrace[PB_DEBUG_MAP_BACKTRACE];
83
84   struct list_head head;
85};
86
87
88struct pb_debug_manager
89{
90   struct pb_manager base;
91
92   struct pb_manager *provider;
93
94   pb_size underflow_size;
95   pb_size overflow_size;
96
97   pipe_mutex mutex;
98   struct list_head list;
99};
100
101
102static INLINE struct pb_debug_buffer *
103pb_debug_buffer(struct pb_buffer *buf)
104{
105   assert(buf);
106   return (struct pb_debug_buffer *)buf;
107}
108
109
110static INLINE struct pb_debug_manager *
111pb_debug_manager(struct pb_manager *mgr)
112{
113   assert(mgr);
114   return (struct pb_debug_manager *)mgr;
115}
116
117
118static const uint8_t random_pattern[32] = {
119   0xaf, 0xcf, 0xa5, 0xa2, 0xc2, 0x63, 0x15, 0x1a,
120   0x7e, 0xe2, 0x7e, 0x84, 0x15, 0x49, 0xa2, 0x1e,
121   0x49, 0x63, 0xf5, 0x52, 0x74, 0x66, 0x9e, 0xc4,
122   0x6d, 0xcf, 0x2c, 0x4a, 0x74, 0xe6, 0xfd, 0x94
123};
124
125
126static INLINE void
127fill_random_pattern(uint8_t *dst, pb_size size)
128{
129   pb_size i = 0;
130   while(size--) {
131      *dst++ = random_pattern[i++];
132      i &= sizeof(random_pattern) - 1;
133   }
134}
135
136
137static INLINE boolean
138check_random_pattern(const uint8_t *dst, pb_size size,
139                     pb_size *min_ofs, pb_size *max_ofs)
140{
141   boolean result = TRUE;
142   pb_size i;
143   *min_ofs = size;
144   *max_ofs = 0;
145   for(i = 0; i < size; ++i) {
146      if(*dst++ != random_pattern[i % sizeof(random_pattern)]) {
147         *min_ofs = MIN2(*min_ofs, i);
148         *max_ofs = MAX2(*max_ofs, i);
149	 result = FALSE;
150      }
151   }
152   return result;
153}
154
155
156static void
157pb_debug_buffer_fill(struct pb_debug_buffer *buf)
158{
159   uint8_t *map;
160
161   map = pb_map(buf->buffer, PIPE_BUFFER_USAGE_CPU_WRITE);
162   assert(map);
163   if(map) {
164      fill_random_pattern(map, buf->underflow_size);
165      fill_random_pattern(map + buf->underflow_size + buf->base.base.size,
166                          buf->overflow_size);
167      pb_unmap(buf->buffer);
168   }
169}
170
171
172/**
173 * Check for under/over flows.
174 *
175 * Should be called with the buffer unmaped.
176 */
177static void
178pb_debug_buffer_check(struct pb_debug_buffer *buf)
179{
180   uint8_t *map;
181
182   map = pb_map(buf->buffer, PIPE_BUFFER_USAGE_CPU_READ);
183   assert(map);
184   if(map) {
185      boolean underflow, overflow;
186      pb_size min_ofs, max_ofs;
187
188      underflow = !check_random_pattern(map, buf->underflow_size,
189                                        &min_ofs, &max_ofs);
190      if(underflow) {
191         debug_printf("buffer underflow (offset -%u%s to -%u bytes) detected\n",
192                      buf->underflow_size - min_ofs,
193                      min_ofs == 0 ? "+" : "",
194                      buf->underflow_size - max_ofs);
195      }
196
197      overflow = !check_random_pattern(map + buf->underflow_size + buf->base.base.size,
198                                       buf->overflow_size,
199                                       &min_ofs, &max_ofs);
200      if(overflow) {
201         debug_printf("buffer overflow (size %u plus offset %u to %u%s bytes) detected\n",
202                      buf->base.base.size,
203                      min_ofs,
204                      max_ofs,
205                      max_ofs == buf->overflow_size - 1 ? "+" : "");
206      }
207
208      if(underflow || overflow)
209         debug_backtrace_dump(buf->create_backtrace, PB_DEBUG_CREATE_BACKTRACE);
210
211      debug_assert(!underflow && !overflow);
212
213      /* re-fill if not aborted */
214      if(underflow)
215         fill_random_pattern(map, buf->underflow_size);
216      if(overflow)
217         fill_random_pattern(map + buf->underflow_size + buf->base.base.size,
218                             buf->overflow_size);
219
220      pb_unmap(buf->buffer);
221   }
222}
223
224
225static void
226pb_debug_buffer_destroy(struct pb_buffer *_buf)
227{
228   struct pb_debug_buffer *buf = pb_debug_buffer(_buf);
229   struct pb_debug_manager *mgr = buf->mgr;
230
231   assert(!pipe_is_referenced(&buf->base.base.reference));
232
233   pb_debug_buffer_check(buf);
234
235   pipe_mutex_lock(mgr->mutex);
236   LIST_DEL(&buf->head);
237   pipe_mutex_unlock(mgr->mutex);
238
239   pipe_mutex_destroy(buf->mutex);
240
241   pb_reference(&buf->buffer, NULL);
242   FREE(buf);
243}
244
245
246static void *
247pb_debug_buffer_map(struct pb_buffer *_buf,
248                    unsigned flags)
249{
250   struct pb_debug_buffer *buf = pb_debug_buffer(_buf);
251   void *map;
252
253   pb_debug_buffer_check(buf);
254
255   map = pb_map(buf->buffer, flags);
256   if(!map)
257      return NULL;
258
259   if(map) {
260      pipe_mutex_lock(buf->mutex);
261      ++buf->map_count;
262      debug_backtrace_capture(buf->map_backtrace, 1, PB_DEBUG_MAP_BACKTRACE);
263      pipe_mutex_unlock(buf->mutex);
264   }
265
266   return (uint8_t *)map + buf->underflow_size;
267}
268
269
270static void
271pb_debug_buffer_unmap(struct pb_buffer *_buf)
272{
273   struct pb_debug_buffer *buf = pb_debug_buffer(_buf);
274
275   pipe_mutex_lock(buf->mutex);
276   assert(buf->map_count);
277   if(buf->map_count)
278      --buf->map_count;
279   pipe_mutex_unlock(buf->mutex);
280
281   pb_unmap(buf->buffer);
282
283   pb_debug_buffer_check(buf);
284}
285
286
287static void
288pb_debug_buffer_get_base_buffer(struct pb_buffer *_buf,
289                                struct pb_buffer **base_buf,
290                                pb_size *offset)
291{
292   struct pb_debug_buffer *buf = pb_debug_buffer(_buf);
293   pb_get_base_buffer(buf->buffer, base_buf, offset);
294   *offset += buf->underflow_size;
295}
296
297
298static enum pipe_error
299pb_debug_buffer_validate(struct pb_buffer *_buf,
300                         struct pb_validate *vl,
301                         unsigned flags)
302{
303   struct pb_debug_buffer *buf = pb_debug_buffer(_buf);
304
305   pipe_mutex_lock(buf->mutex);
306   if(buf->map_count) {
307      debug_printf("%s: attempting to validate a mapped buffer\n", __FUNCTION__);
308      debug_printf("last map backtrace is\n");
309      debug_backtrace_dump(buf->map_backtrace, PB_DEBUG_MAP_BACKTRACE);
310   }
311   pipe_mutex_unlock(buf->mutex);
312
313   pb_debug_buffer_check(buf);
314
315   return pb_validate(buf->buffer, vl, flags);
316}
317
318
319static void
320pb_debug_buffer_fence(struct pb_buffer *_buf,
321                      struct pipe_fence_handle *fence)
322{
323   struct pb_debug_buffer *buf = pb_debug_buffer(_buf);
324   pb_fence(buf->buffer, fence);
325}
326
327
328const struct pb_vtbl
329pb_debug_buffer_vtbl = {
330      pb_debug_buffer_destroy,
331      pb_debug_buffer_map,
332      pb_debug_buffer_unmap,
333      pb_debug_buffer_validate,
334      pb_debug_buffer_fence,
335      pb_debug_buffer_get_base_buffer
336};
337
338
339static void
340pb_debug_manager_dump(struct pb_debug_manager *mgr)
341{
342   struct list_head *curr, *next;
343   struct pb_debug_buffer *buf;
344
345   pipe_mutex_lock(mgr->mutex);
346
347   curr = mgr->list.next;
348   next = curr->next;
349   while(curr != &mgr->list) {
350      buf = LIST_ENTRY(struct pb_debug_buffer, curr, head);
351
352      debug_printf("buffer = %p\n", buf);
353      debug_printf("    .size = 0x%x\n", buf->base.base.size);
354      debug_backtrace_dump(buf->create_backtrace, PB_DEBUG_CREATE_BACKTRACE);
355
356      curr = next;
357      next = curr->next;
358   }
359
360   pipe_mutex_unlock(mgr->mutex);
361}
362
363
364static struct pb_buffer *
365pb_debug_manager_create_buffer(struct pb_manager *_mgr,
366                               pb_size size,
367                               const struct pb_desc *desc)
368{
369   struct pb_debug_manager *mgr = pb_debug_manager(_mgr);
370   struct pb_debug_buffer *buf;
371   struct pb_desc real_desc;
372   pb_size real_size;
373
374   buf = CALLOC_STRUCT(pb_debug_buffer);
375   if(!buf)
376      return NULL;
377
378   real_size = mgr->underflow_size + size + mgr->overflow_size;
379   real_desc = *desc;
380   real_desc.usage |= PIPE_BUFFER_USAGE_CPU_WRITE;
381   real_desc.usage |= PIPE_BUFFER_USAGE_CPU_READ;
382
383   buf->buffer = mgr->provider->create_buffer(mgr->provider,
384                                              real_size,
385                                              &real_desc);
386   if(!buf->buffer) {
387      FREE(buf);
388#if 0
389      pipe_mutex_lock(mgr->mutex);
390      debug_printf("%s: failed to create buffer\n", __FUNCTION__);
391      if(!LIST_IS_EMPTY(&mgr->list))
392         pb_debug_manager_dump(mgr);
393      pipe_mutex_unlock(mgr->mutex);
394#endif
395      return NULL;
396   }
397
398   assert(pipe_is_referenced(&buf->buffer->base.reference));
399   assert(pb_check_alignment(real_desc.alignment, buf->buffer->base.alignment));
400   assert(pb_check_usage(real_desc.usage, buf->buffer->base.usage));
401   assert(buf->buffer->base.size >= real_size);
402
403   pipe_reference_init(&buf->base.base.reference, 1);
404   buf->base.base.alignment = desc->alignment;
405   buf->base.base.usage = desc->usage;
406   buf->base.base.size = size;
407
408   buf->base.vtbl = &pb_debug_buffer_vtbl;
409   buf->mgr = mgr;
410
411   buf->underflow_size = mgr->underflow_size;
412   buf->overflow_size = buf->buffer->base.size - buf->underflow_size - size;
413
414   debug_backtrace_capture(buf->create_backtrace, 1, PB_DEBUG_CREATE_BACKTRACE);
415
416   pb_debug_buffer_fill(buf);
417
418   pipe_mutex_init(buf->mutex);
419
420   pipe_mutex_lock(mgr->mutex);
421   LIST_ADDTAIL(&buf->head, &mgr->list);
422   pipe_mutex_unlock(mgr->mutex);
423
424   return &buf->base;
425}
426
427
428static void
429pb_debug_manager_flush(struct pb_manager *_mgr)
430{
431   struct pb_debug_manager *mgr = pb_debug_manager(_mgr);
432   assert(mgr->provider->flush);
433   if(mgr->provider->flush)
434      mgr->provider->flush(mgr->provider);
435}
436
437
438static void
439pb_debug_manager_destroy(struct pb_manager *_mgr)
440{
441   struct pb_debug_manager *mgr = pb_debug_manager(_mgr);
442
443   pipe_mutex_lock(mgr->mutex);
444   if(!LIST_IS_EMPTY(&mgr->list)) {
445      debug_printf("%s: unfreed buffers\n", __FUNCTION__);
446      pb_debug_manager_dump(mgr);
447   }
448   pipe_mutex_unlock(mgr->mutex);
449
450   pipe_mutex_destroy(mgr->mutex);
451   mgr->provider->destroy(mgr->provider);
452   FREE(mgr);
453}
454
455
456struct pb_manager *
457pb_debug_manager_create(struct pb_manager *provider,
458                        pb_size underflow_size, pb_size overflow_size)
459{
460   struct pb_debug_manager *mgr;
461
462   if(!provider)
463      return NULL;
464
465   mgr = CALLOC_STRUCT(pb_debug_manager);
466   if (!mgr)
467      return NULL;
468
469   mgr->base.destroy = pb_debug_manager_destroy;
470   mgr->base.create_buffer = pb_debug_manager_create_buffer;
471   mgr->base.flush = pb_debug_manager_flush;
472   mgr->provider = provider;
473   mgr->underflow_size = underflow_size;
474   mgr->overflow_size = overflow_size;
475
476   pipe_mutex_init(mgr->mutex);
477   LIST_INITHEAD(&mgr->list);
478
479   return &mgr->base;
480}
481
482
483#else /* !DEBUG */
484
485
486struct pb_manager *
487pb_debug_manager_create(struct pb_manager *provider,
488                        pb_size underflow_size, pb_size overflow_size)
489{
490   return provider;
491}
492
493
494#endif /* !DEBUG */
495