1/**************************************************************************
2 *
3 * Copyright 2007-2008 VMware, Inc.
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 VMWARE 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 <jfonseca@vmware.com>
33 */
34
35
36#include "pipe/p_compiler.h"
37#include "util/u_debug.h"
38#include "os/os_thread.h"
39#include "util/u_math.h"
40#include "util/u_memory.h"
41#include "util/list.h"
42#include "util/u_time.h"
43#include "util/u_debug_stack.h"
44#include <inttypes.h>
45
46#include "pb_buffer.h"
47#include "pb_bufmgr.h"
48
49
50#ifdef DEBUG
51
52
53#define PB_DEBUG_CREATE_BACKTRACE 8
54#define PB_DEBUG_MAP_BACKTRACE 8
55
56
57/**
58 * Convenience macro (type safe).
59 */
60#define SUPER(__derived) (&(__derived)->base)
61
62
63struct pb_debug_manager;
64
65
66/**
67 * Wrapper around a pipe buffer which adds delayed destruction.
68 */
69struct pb_debug_buffer
70{
71   struct pb_buffer base;
72
73   struct pb_buffer *buffer;
74   struct pb_debug_manager *mgr;
75
76   pb_size underflow_size;
77   pb_size overflow_size;
78
79   struct debug_stack_frame create_backtrace[PB_DEBUG_CREATE_BACKTRACE];
80
81   pipe_mutex mutex;
82   unsigned map_count;
83   struct debug_stack_frame map_backtrace[PB_DEBUG_MAP_BACKTRACE];
84
85   struct list_head head;
86};
87
88
89struct pb_debug_manager
90{
91   struct pb_manager base;
92
93   struct pb_manager *provider;
94
95   pb_size underflow_size;
96   pb_size overflow_size;
97
98   pipe_mutex mutex;
99   struct list_head list;
100};
101
102
103static inline struct pb_debug_buffer *
104pb_debug_buffer(struct pb_buffer *buf)
105{
106   assert(buf);
107   return (struct pb_debug_buffer *)buf;
108}
109
110
111static inline struct pb_debug_manager *
112pb_debug_manager(struct pb_manager *mgr)
113{
114   assert(mgr);
115   return (struct pb_debug_manager *)mgr;
116}
117
118
119static const uint8_t random_pattern[32] = {
120   0xaf, 0xcf, 0xa5, 0xa2, 0xc2, 0x63, 0x15, 0x1a,
121   0x7e, 0xe2, 0x7e, 0x84, 0x15, 0x49, 0xa2, 0x1e,
122   0x49, 0x63, 0xf5, 0x52, 0x74, 0x66, 0x9e, 0xc4,
123   0x6d, 0xcf, 0x2c, 0x4a, 0x74, 0xe6, 0xfd, 0x94
124};
125
126
127static inline void
128fill_random_pattern(uint8_t *dst, pb_size size)
129{
130   pb_size i = 0;
131   while(size--) {
132      *dst++ = random_pattern[i++];
133      i &= sizeof(random_pattern) - 1;
134   }
135}
136
137
138static inline boolean
139check_random_pattern(const uint8_t *dst, pb_size size,
140                     pb_size *min_ofs, pb_size *max_ofs)
141{
142   boolean result = TRUE;
143   pb_size i;
144   *min_ofs = size;
145   *max_ofs = 0;
146   for(i = 0; i < size; ++i) {
147      if(*dst++ != random_pattern[i % sizeof(random_pattern)]) {
148         *min_ofs = MIN2(*min_ofs, i);
149         *max_ofs = MAX2(*max_ofs, i);
150	 result = FALSE;
151      }
152   }
153   return result;
154}
155
156
157static void
158pb_debug_buffer_fill(struct pb_debug_buffer *buf)
159{
160   uint8_t *map;
161
162   map = pb_map(buf->buffer, PB_USAGE_CPU_WRITE, NULL);
163   assert(map);
164   if (map) {
165      fill_random_pattern(map, buf->underflow_size);
166      fill_random_pattern(map + buf->underflow_size + buf->base.size,
167                          buf->overflow_size);
168      pb_unmap(buf->buffer);
169   }
170}
171
172
173/**
174 * Check for under/over flows.
175 *
176 * Should be called with the buffer unmaped.
177 */
178static void
179pb_debug_buffer_check(struct pb_debug_buffer *buf)
180{
181   uint8_t *map;
182
183   map = pb_map(buf->buffer,
184                PB_USAGE_CPU_READ |
185                PB_USAGE_UNSYNCHRONIZED, NULL);
186   assert(map);
187   if (map) {
188      boolean underflow, overflow;
189      pb_size min_ofs, max_ofs;
190
191      underflow = !check_random_pattern(map, buf->underflow_size,
192                                        &min_ofs, &max_ofs);
193      if(underflow) {
194         debug_printf("buffer underflow (offset -%"PRIu64"%s to -%"PRIu64" bytes) detected\n",
195                      buf->underflow_size - min_ofs,
196                      min_ofs == 0 ? "+" : "",
197                      buf->underflow_size - max_ofs);
198      }
199
200      overflow = !check_random_pattern(map + buf->underflow_size + buf->base.size,
201                                       buf->overflow_size,
202                                       &min_ofs, &max_ofs);
203      if(overflow) {
204         debug_printf("buffer overflow (size %"PRIu64" plus offset %"PRIu64" to %"PRIu64"%s bytes) detected\n",
205                      buf->base.size,
206                      min_ofs,
207                      max_ofs,
208                      max_ofs == buf->overflow_size - 1 ? "+" : "");
209      }
210
211      if(underflow || overflow)
212         debug_backtrace_dump(buf->create_backtrace, PB_DEBUG_CREATE_BACKTRACE);
213
214      debug_assert(!underflow);
215      debug_assert(!overflow);
216
217      /* re-fill if not aborted */
218      if(underflow)
219         fill_random_pattern(map, buf->underflow_size);
220      if(overflow)
221         fill_random_pattern(map + buf->underflow_size + buf->base.size,
222                             buf->overflow_size);
223
224      pb_unmap(buf->buffer);
225   }
226}
227
228
229static void
230pb_debug_buffer_destroy(struct pb_buffer *_buf)
231{
232   struct pb_debug_buffer *buf = pb_debug_buffer(_buf);
233   struct pb_debug_manager *mgr = buf->mgr;
234
235   assert(!pipe_is_referenced(&buf->base.reference));
236
237   pb_debug_buffer_check(buf);
238
239   pipe_mutex_lock(mgr->mutex);
240   LIST_DEL(&buf->head);
241   pipe_mutex_unlock(mgr->mutex);
242
243   pipe_mutex_destroy(buf->mutex);
244
245   pb_reference(&buf->buffer, NULL);
246   FREE(buf);
247}
248
249
250static void *
251pb_debug_buffer_map(struct pb_buffer *_buf,
252                    unsigned flags, void *flush_ctx)
253{
254   struct pb_debug_buffer *buf = pb_debug_buffer(_buf);
255   void *map;
256
257   pb_debug_buffer_check(buf);
258
259   map = pb_map(buf->buffer, flags, flush_ctx);
260   if (!map)
261      return NULL;
262
263   pipe_mutex_lock(buf->mutex);
264   ++buf->map_count;
265   debug_backtrace_capture(buf->map_backtrace, 1, PB_DEBUG_MAP_BACKTRACE);
266   pipe_mutex_unlock(buf->mutex);
267
268   return (uint8_t *)map + buf->underflow_size;
269}
270
271
272static void
273pb_debug_buffer_unmap(struct pb_buffer *_buf)
274{
275   struct pb_debug_buffer *buf = pb_debug_buffer(_buf);
276
277   pipe_mutex_lock(buf->mutex);
278   assert(buf->map_count);
279   if(buf->map_count)
280      --buf->map_count;
281   pipe_mutex_unlock(buf->mutex);
282
283   pb_unmap(buf->buffer);
284
285   pb_debug_buffer_check(buf);
286}
287
288
289static void
290pb_debug_buffer_get_base_buffer(struct pb_buffer *_buf,
291                                struct pb_buffer **base_buf,
292                                pb_size *offset)
293{
294   struct pb_debug_buffer *buf = pb_debug_buffer(_buf);
295   pb_get_base_buffer(buf->buffer, base_buf, offset);
296   *offset += buf->underflow_size;
297}
298
299
300static enum pipe_error
301pb_debug_buffer_validate(struct pb_buffer *_buf,
302                         struct pb_validate *vl,
303                         unsigned flags)
304{
305   struct pb_debug_buffer *buf = pb_debug_buffer(_buf);
306
307   pipe_mutex_lock(buf->mutex);
308   if(buf->map_count) {
309      debug_printf("%s: attempting to validate a mapped buffer\n", __FUNCTION__);
310      debug_printf("last map backtrace is\n");
311      debug_backtrace_dump(buf->map_backtrace, PB_DEBUG_MAP_BACKTRACE);
312   }
313   pipe_mutex_unlock(buf->mutex);
314
315   pb_debug_buffer_check(buf);
316
317   return pb_validate(buf->buffer, vl, flags);
318}
319
320
321static void
322pb_debug_buffer_fence(struct pb_buffer *_buf,
323                      struct pipe_fence_handle *fence)
324{
325   struct pb_debug_buffer *buf = pb_debug_buffer(_buf);
326   pb_fence(buf->buffer, fence);
327}
328
329
330const struct pb_vtbl
331pb_debug_buffer_vtbl = {
332      pb_debug_buffer_destroy,
333      pb_debug_buffer_map,
334      pb_debug_buffer_unmap,
335      pb_debug_buffer_validate,
336      pb_debug_buffer_fence,
337      pb_debug_buffer_get_base_buffer
338};
339
340
341static void
342pb_debug_manager_dump_locked(struct pb_debug_manager *mgr)
343{
344   struct list_head *curr, *next;
345   struct pb_debug_buffer *buf;
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", (void *) buf);
353      debug_printf("    .size = 0x%"PRIx64"\n", buf->base.size);
354      debug_backtrace_dump(buf->create_backtrace, PB_DEBUG_CREATE_BACKTRACE);
355
356      curr = next;
357      next = curr->next;
358   }
359
360}
361
362
363static struct pb_buffer *
364pb_debug_manager_create_buffer(struct pb_manager *_mgr,
365                               pb_size size,
366                               const struct pb_desc *desc)
367{
368   struct pb_debug_manager *mgr = pb_debug_manager(_mgr);
369   struct pb_debug_buffer *buf;
370   struct pb_desc real_desc;
371   pb_size real_size;
372
373   assert(size);
374   assert(desc->alignment);
375
376   buf = CALLOC_STRUCT(pb_debug_buffer);
377   if (!buf)
378      return NULL;
379
380   real_size = mgr->underflow_size + size + mgr->overflow_size;
381   real_desc = *desc;
382   real_desc.usage |= PB_USAGE_CPU_WRITE;
383   real_desc.usage |= PB_USAGE_CPU_READ;
384
385   buf->buffer = mgr->provider->create_buffer(mgr->provider,
386                                              real_size,
387                                              &real_desc);
388   if(!buf->buffer) {
389      FREE(buf);
390#if 0
391      pipe_mutex_lock(mgr->mutex);
392      debug_printf("%s: failed to create buffer\n", __FUNCTION__);
393      if(!LIST_IS_EMPTY(&mgr->list))
394         pb_debug_manager_dump_locked(mgr);
395      pipe_mutex_unlock(mgr->mutex);
396#endif
397      return NULL;
398   }
399
400   assert(pipe_is_referenced(&buf->buffer->reference));
401   assert(pb_check_alignment(real_desc.alignment, buf->buffer->alignment));
402   assert(pb_check_usage(real_desc.usage, buf->buffer->usage));
403   assert(buf->buffer->size >= real_size);
404
405   pipe_reference_init(&buf->base.reference, 1);
406   buf->base.alignment = desc->alignment;
407   buf->base.usage = desc->usage;
408   buf->base.size = size;
409
410   buf->base.vtbl = &pb_debug_buffer_vtbl;
411   buf->mgr = mgr;
412
413   buf->underflow_size = mgr->underflow_size;
414   buf->overflow_size = buf->buffer->size - buf->underflow_size - size;
415
416   debug_backtrace_capture(buf->create_backtrace, 1, PB_DEBUG_CREATE_BACKTRACE);
417
418   pb_debug_buffer_fill(buf);
419
420   pipe_mutex_init(buf->mutex);
421
422   pipe_mutex_lock(mgr->mutex);
423   LIST_ADDTAIL(&buf->head, &mgr->list);
424   pipe_mutex_unlock(mgr->mutex);
425
426   return &buf->base;
427}
428
429
430static void
431pb_debug_manager_flush(struct pb_manager *_mgr)
432{
433   struct pb_debug_manager *mgr = pb_debug_manager(_mgr);
434   assert(mgr->provider->flush);
435   if(mgr->provider->flush)
436      mgr->provider->flush(mgr->provider);
437}
438
439
440static void
441pb_debug_manager_destroy(struct pb_manager *_mgr)
442{
443   struct pb_debug_manager *mgr = pb_debug_manager(_mgr);
444
445   pipe_mutex_lock(mgr->mutex);
446   if(!LIST_IS_EMPTY(&mgr->list)) {
447      debug_printf("%s: unfreed buffers\n", __FUNCTION__);
448      pb_debug_manager_dump_locked(mgr);
449   }
450   pipe_mutex_unlock(mgr->mutex);
451
452   pipe_mutex_destroy(mgr->mutex);
453   mgr->provider->destroy(mgr->provider);
454   FREE(mgr);
455}
456
457
458struct pb_manager *
459pb_debug_manager_create(struct pb_manager *provider,
460                        pb_size underflow_size, pb_size overflow_size)
461{
462   struct pb_debug_manager *mgr;
463
464   if (!provider)
465      return NULL;
466
467   mgr = CALLOC_STRUCT(pb_debug_manager);
468   if (!mgr)
469      return NULL;
470
471   mgr->base.destroy = pb_debug_manager_destroy;
472   mgr->base.create_buffer = pb_debug_manager_create_buffer;
473   mgr->base.flush = pb_debug_manager_flush;
474   mgr->provider = provider;
475   mgr->underflow_size = underflow_size;
476   mgr->overflow_size = overflow_size;
477
478   pipe_mutex_init(mgr->mutex);
479   LIST_INITHEAD(&mgr->list);
480
481   return &mgr->base;
482}
483
484
485#else /* !DEBUG */
486
487
488struct pb_manager *
489pb_debug_manager_create(struct pb_manager *provider,
490                        pb_size underflow_size, pb_size overflow_size)
491{
492   return provider;
493}
494
495
496#endif /* !DEBUG */
497