pb_bufmgr_debug.c revision 2af0173e9e4eefe910c6011038e7346091a9b2a4
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 = %p\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