Go to most recent revision | Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
4358 | Serge | 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 |
||
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/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 | |||
62 | struct pb_debug_manager; |
||
63 | |||
64 | |||
65 | /** |
||
66 | * Wrapper around a pipe buffer which adds delayed destruction. |
||
67 | */ |
||
68 | struct 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 | |||
88 | struct 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 | |||
102 | static INLINE struct pb_debug_buffer * |
||
103 | pb_debug_buffer(struct pb_buffer *buf) |
||
104 | { |
||
105 | assert(buf); |
||
106 | return (struct pb_debug_buffer *)buf; |
||
107 | } |
||
108 | |||
109 | |||
110 | static INLINE struct pb_debug_manager * |
||
111 | pb_debug_manager(struct pb_manager *mgr) |
||
112 | { |
||
113 | assert(mgr); |
||
114 | return (struct pb_debug_manager *)mgr; |
||
115 | } |
||
116 | |||
117 | |||
118 | static 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 | |||
126 | static INLINE void |
||
127 | fill_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 | |||
137 | static INLINE boolean |
||
138 | check_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 | |||
156 | static void |
||
157 | pb_debug_buffer_fill(struct pb_debug_buffer *buf) |
||
158 | { |
||
159 | uint8_t *map; |
||
160 | |||
161 | map = pb_map(buf->buffer, PB_USAGE_CPU_WRITE, NULL); |
||
162 | assert(map); |
||
163 | if(map) { |
||
164 | fill_random_pattern(map, buf->underflow_size); |
||
165 | fill_random_pattern(map + buf->underflow_size + buf->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 | */ |
||
177 | static void |
||
178 | pb_debug_buffer_check(struct pb_debug_buffer *buf) |
||
179 | { |
||
180 | uint8_t *map; |
||
181 | |||
182 | map = pb_map(buf->buffer, |
||
183 | PB_USAGE_CPU_READ | |
||
184 | PB_USAGE_UNSYNCHRONIZED, NULL); |
||
185 | assert(map); |
||
186 | if(map) { |
||
187 | boolean underflow, overflow; |
||
188 | pb_size min_ofs, max_ofs; |
||
189 | |||
190 | underflow = !check_random_pattern(map, buf->underflow_size, |
||
191 | &min_ofs, &max_ofs); |
||
192 | if(underflow) { |
||
193 | debug_printf("buffer underflow (offset -%u%s to -%u bytes) detected\n", |
||
194 | buf->underflow_size - min_ofs, |
||
195 | min_ofs == 0 ? "+" : "", |
||
196 | buf->underflow_size - max_ofs); |
||
197 | } |
||
198 | |||
199 | overflow = !check_random_pattern(map + buf->underflow_size + buf->base.size, |
||
200 | buf->overflow_size, |
||
201 | &min_ofs, &max_ofs); |
||
202 | if(overflow) { |
||
203 | debug_printf("buffer overflow (size %u plus offset %u to %u%s bytes) detected\n", |
||
204 | buf->base.size, |
||
205 | min_ofs, |
||
206 | max_ofs, |
||
207 | max_ofs == buf->overflow_size - 1 ? "+" : ""); |
||
208 | } |
||
209 | |||
210 | if(underflow || overflow) |
||
211 | debug_backtrace_dump(buf->create_backtrace, PB_DEBUG_CREATE_BACKTRACE); |
||
212 | |||
213 | debug_assert(!underflow); |
||
214 | debug_assert(!overflow); |
||
215 | |||
216 | /* re-fill if not aborted */ |
||
217 | if(underflow) |
||
218 | fill_random_pattern(map, buf->underflow_size); |
||
219 | if(overflow) |
||
220 | fill_random_pattern(map + buf->underflow_size + buf->base.size, |
||
221 | buf->overflow_size); |
||
222 | |||
223 | pb_unmap(buf->buffer); |
||
224 | } |
||
225 | } |
||
226 | |||
227 | |||
228 | static void |
||
229 | pb_debug_buffer_destroy(struct pb_buffer *_buf) |
||
230 | { |
||
231 | struct pb_debug_buffer *buf = pb_debug_buffer(_buf); |
||
232 | struct pb_debug_manager *mgr = buf->mgr; |
||
233 | |||
234 | assert(!pipe_is_referenced(&buf->base.reference)); |
||
235 | |||
236 | pb_debug_buffer_check(buf); |
||
237 | |||
238 | pipe_mutex_lock(mgr->mutex); |
||
239 | LIST_DEL(&buf->head); |
||
240 | pipe_mutex_unlock(mgr->mutex); |
||
241 | |||
242 | pipe_mutex_destroy(buf->mutex); |
||
243 | |||
244 | pb_reference(&buf->buffer, NULL); |
||
245 | FREE(buf); |
||
246 | } |
||
247 | |||
248 | |||
249 | static void * |
||
250 | pb_debug_buffer_map(struct pb_buffer *_buf, |
||
251 | unsigned flags, void *flush_ctx) |
||
252 | { |
||
253 | struct pb_debug_buffer *buf = pb_debug_buffer(_buf); |
||
254 | void *map; |
||
255 | |||
256 | pb_debug_buffer_check(buf); |
||
257 | |||
258 | map = pb_map(buf->buffer, flags, flush_ctx); |
||
259 | if(!map) |
||
260 | return NULL; |
||
261 | |||
262 | if(map) { |
||
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 | |||
269 | return (uint8_t *)map + buf->underflow_size; |
||
270 | } |
||
271 | |||
272 | |||
273 | static void |
||
274 | pb_debug_buffer_unmap(struct pb_buffer *_buf) |
||
275 | { |
||
276 | struct pb_debug_buffer *buf = pb_debug_buffer(_buf); |
||
277 | |||
278 | pipe_mutex_lock(buf->mutex); |
||
279 | assert(buf->map_count); |
||
280 | if(buf->map_count) |
||
281 | --buf->map_count; |
||
282 | pipe_mutex_unlock(buf->mutex); |
||
283 | |||
284 | pb_unmap(buf->buffer); |
||
285 | |||
286 | pb_debug_buffer_check(buf); |
||
287 | } |
||
288 | |||
289 | |||
290 | static void |
||
291 | pb_debug_buffer_get_base_buffer(struct pb_buffer *_buf, |
||
292 | struct pb_buffer **base_buf, |
||
293 | pb_size *offset) |
||
294 | { |
||
295 | struct pb_debug_buffer *buf = pb_debug_buffer(_buf); |
||
296 | pb_get_base_buffer(buf->buffer, base_buf, offset); |
||
297 | *offset += buf->underflow_size; |
||
298 | } |
||
299 | |||
300 | |||
301 | static enum pipe_error |
||
302 | pb_debug_buffer_validate(struct pb_buffer *_buf, |
||
303 | struct pb_validate *vl, |
||
304 | unsigned flags) |
||
305 | { |
||
306 | struct pb_debug_buffer *buf = pb_debug_buffer(_buf); |
||
307 | |||
308 | pipe_mutex_lock(buf->mutex); |
||
309 | if(buf->map_count) { |
||
310 | debug_printf("%s: attempting to validate a mapped buffer\n", __FUNCTION__); |
||
311 | debug_printf("last map backtrace is\n"); |
||
312 | debug_backtrace_dump(buf->map_backtrace, PB_DEBUG_MAP_BACKTRACE); |
||
313 | } |
||
314 | pipe_mutex_unlock(buf->mutex); |
||
315 | |||
316 | pb_debug_buffer_check(buf); |
||
317 | |||
318 | return pb_validate(buf->buffer, vl, flags); |
||
319 | } |
||
320 | |||
321 | |||
322 | static void |
||
323 | pb_debug_buffer_fence(struct pb_buffer *_buf, |
||
324 | struct pipe_fence_handle *fence) |
||
325 | { |
||
326 | struct pb_debug_buffer *buf = pb_debug_buffer(_buf); |
||
327 | pb_fence(buf->buffer, fence); |
||
328 | } |
||
329 | |||
330 | |||
331 | const struct pb_vtbl |
||
332 | pb_debug_buffer_vtbl = { |
||
333 | pb_debug_buffer_destroy, |
||
334 | pb_debug_buffer_map, |
||
335 | pb_debug_buffer_unmap, |
||
336 | pb_debug_buffer_validate, |
||
337 | pb_debug_buffer_fence, |
||
338 | pb_debug_buffer_get_base_buffer |
||
339 | }; |
||
340 | |||
341 | |||
342 | static void |
||
343 | pb_debug_manager_dump_locked(struct pb_debug_manager *mgr) |
||
344 | { |
||
345 | struct list_head *curr, *next; |
||
346 | struct pb_debug_buffer *buf; |
||
347 | |||
348 | curr = mgr->list.next; |
||
349 | next = curr->next; |
||
350 | while(curr != &mgr->list) { |
||
351 | buf = LIST_ENTRY(struct pb_debug_buffer, curr, head); |
||
352 | |||
353 | debug_printf("buffer = %p\n", (void *) buf); |
||
354 | debug_printf(" .size = 0x%x\n", buf->base.size); |
||
355 | debug_backtrace_dump(buf->create_backtrace, PB_DEBUG_CREATE_BACKTRACE); |
||
356 | |||
357 | curr = next; |
||
358 | next = curr->next; |
||
359 | } |
||
360 | |||
361 | } |
||
362 | |||
363 | |||
364 | static struct pb_buffer * |
||
365 | pb_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 | assert(size); |
||
375 | assert(desc->alignment); |
||
376 | |||
377 | buf = CALLOC_STRUCT(pb_debug_buffer); |
||
378 | if(!buf) |
||
379 | return NULL; |
||
380 | |||
381 | real_size = mgr->underflow_size + size + mgr->overflow_size; |
||
382 | real_desc = *desc; |
||
383 | real_desc.usage |= PB_USAGE_CPU_WRITE; |
||
384 | real_desc.usage |= PB_USAGE_CPU_READ; |
||
385 | |||
386 | buf->buffer = mgr->provider->create_buffer(mgr->provider, |
||
387 | real_size, |
||
388 | &real_desc); |
||
389 | if(!buf->buffer) { |
||
390 | FREE(buf); |
||
391 | #if 0 |
||
392 | pipe_mutex_lock(mgr->mutex); |
||
393 | debug_printf("%s: failed to create buffer\n", __FUNCTION__); |
||
394 | if(!LIST_IS_EMPTY(&mgr->list)) |
||
395 | pb_debug_manager_dump_locked(mgr); |
||
396 | pipe_mutex_unlock(mgr->mutex); |
||
397 | #endif |
||
398 | return NULL; |
||
399 | } |
||
400 | |||
401 | assert(pipe_is_referenced(&buf->buffer->reference)); |
||
402 | assert(pb_check_alignment(real_desc.alignment, buf->buffer->alignment)); |
||
403 | assert(pb_check_usage(real_desc.usage, buf->buffer->usage)); |
||
404 | assert(buf->buffer->size >= real_size); |
||
405 | |||
406 | pipe_reference_init(&buf->base.reference, 1); |
||
407 | buf->base.alignment = desc->alignment; |
||
408 | buf->base.usage = desc->usage; |
||
409 | buf->base.size = size; |
||
410 | |||
411 | buf->base.vtbl = &pb_debug_buffer_vtbl; |
||
412 | buf->mgr = mgr; |
||
413 | |||
414 | buf->underflow_size = mgr->underflow_size; |
||
415 | buf->overflow_size = buf->buffer->size - buf->underflow_size - size; |
||
416 | |||
417 | debug_backtrace_capture(buf->create_backtrace, 1, PB_DEBUG_CREATE_BACKTRACE); |
||
418 | |||
419 | pb_debug_buffer_fill(buf); |
||
420 | |||
421 | pipe_mutex_init(buf->mutex); |
||
422 | |||
423 | pipe_mutex_lock(mgr->mutex); |
||
424 | LIST_ADDTAIL(&buf->head, &mgr->list); |
||
425 | pipe_mutex_unlock(mgr->mutex); |
||
426 | |||
427 | return &buf->base; |
||
428 | } |
||
429 | |||
430 | |||
431 | static void |
||
432 | pb_debug_manager_flush(struct pb_manager *_mgr) |
||
433 | { |
||
434 | struct pb_debug_manager *mgr = pb_debug_manager(_mgr); |
||
435 | assert(mgr->provider->flush); |
||
436 | if(mgr->provider->flush) |
||
437 | mgr->provider->flush(mgr->provider); |
||
438 | } |
||
439 | |||
440 | |||
441 | static void |
||
442 | pb_debug_manager_destroy(struct pb_manager *_mgr) |
||
443 | { |
||
444 | struct pb_debug_manager *mgr = pb_debug_manager(_mgr); |
||
445 | |||
446 | pipe_mutex_lock(mgr->mutex); |
||
447 | if(!LIST_IS_EMPTY(&mgr->list)) { |
||
448 | debug_printf("%s: unfreed buffers\n", __FUNCTION__); |
||
449 | pb_debug_manager_dump_locked(mgr); |
||
450 | } |
||
451 | pipe_mutex_unlock(mgr->mutex); |
||
452 | |||
453 | pipe_mutex_destroy(mgr->mutex); |
||
454 | mgr->provider->destroy(mgr->provider); |
||
455 | FREE(mgr); |
||
456 | } |
||
457 | |||
458 | |||
459 | struct pb_manager * |
||
460 | pb_debug_manager_create(struct pb_manager *provider, |
||
461 | pb_size underflow_size, pb_size overflow_size) |
||
462 | { |
||
463 | struct pb_debug_manager *mgr; |
||
464 | |||
465 | if(!provider) |
||
466 | return NULL; |
||
467 | |||
468 | mgr = CALLOC_STRUCT(pb_debug_manager); |
||
469 | if (!mgr) |
||
470 | return NULL; |
||
471 | |||
472 | mgr->base.destroy = pb_debug_manager_destroy; |
||
473 | mgr->base.create_buffer = pb_debug_manager_create_buffer; |
||
474 | mgr->base.flush = pb_debug_manager_flush; |
||
475 | mgr->provider = provider; |
||
476 | mgr->underflow_size = underflow_size; |
||
477 | mgr->overflow_size = overflow_size; |
||
478 | |||
479 | pipe_mutex_init(mgr->mutex); |
||
480 | LIST_INITHEAD(&mgr->list); |
||
481 | |||
482 | return &mgr->base; |
||
483 | } |
||
484 | |||
485 | |||
486 | #else /* !DEBUG */ |
||
487 | |||
488 | |||
489 | struct pb_manager * |
||
490 | pb_debug_manager_create(struct pb_manager *provider, |
||
491 | pb_size underflow_size, pb_size overflow_size) |
||
492 | { |
||
493 | return provider; |
||
494 | } |
||
495 | |||
496 | |||
497 | #endif /* !DEBUG */> |