Home | History | Annotate | Download | only in pipebuffer
      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 (at) tungstengraphics.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/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 */
    498