src/test/SDL_test_memory.c
author Sam Lantinga <slouken@libsdl.org>
Thu, 12 Oct 2017 14:46:28 -0700
changeset 11617 8cd666139071
parent 11616 ed240b398305
child 11621 8c8e18a2c390
permissions -rw-r--r--
Use the lower-case hex output to match other stack trace printouts
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2017 Sam Lantinga <slouken@libsdl.org>
     4 
     5   This software is provided 'as-is', without any express or implied
     6   warranty.  In no event will the authors be held liable for any damages
     7   arising from the use of this software.
     8 
     9   Permission is granted to anyone to use this software for any purpose,
    10   including commercial applications, and to alter it and redistribute it
    11   freely, subject to the following restrictions:
    12 
    13   1. The origin of this software must not be misrepresented; you must not
    14      claim that you wrote the original software. If you use this software
    15      in a product, an acknowledgment in the product documentation would be
    16      appreciated but is not required.
    17   2. Altered source versions must be plainly marked as such, and must not be
    18      misrepresented as being the original software.
    19   3. This notice may not be removed or altered from any source distribution.
    20 */
    21 #include "SDL_config.h"
    22 #include "SDL_assert.h"
    23 #include "SDL_stdinc.h"
    24 #include "SDL_log.h"
    25 #include "SDL_test_crc32.h"
    26 #include "SDL_test_memory.h"
    27 
    28 #ifdef HAVE_LIBUNWIND_H
    29 #include <libunwind.h>
    30 #endif
    31 
    32 /* This is a simple tracking allocator to demonstrate the use of SDL's
    33    memory allocation replacement functionality.
    34 
    35    It gets slow with large numbers of allocations and shouldn't be used
    36    for production code.
    37 */
    38 
    39 typedef struct SDL_tracked_allocation
    40 {
    41     void *mem;
    42     size_t size;
    43     Uint64 stack[10];
    44     char stack_names[10][256];
    45     struct SDL_tracked_allocation *next;
    46 } SDL_tracked_allocation;
    47 
    48 static SDLTest_Crc32Context s_crc32_context;
    49 static SDL_malloc_func SDL_malloc_orig = NULL;
    50 static SDL_calloc_func SDL_calloc_orig = NULL;
    51 static SDL_realloc_func SDL_realloc_orig = NULL;
    52 static SDL_free_func SDL_free_orig = NULL;
    53 static int s_previous_allocations = 0;
    54 static SDL_tracked_allocation *s_tracked_allocations[256];
    55 
    56 static unsigned int get_allocation_bucket(void *mem)
    57 {
    58     CrcUint32 crc_value;
    59     unsigned int index;
    60     SDLTest_Crc32Calc(&s_crc32_context, (CrcUint8 *)&mem, sizeof(mem), &crc_value);
    61     index = (crc_value & (SDL_arraysize(s_tracked_allocations) - 1));
    62     return index;
    63 }
    64  
    65 static SDL_bool SDL_IsAllocationTracked(void *mem)
    66 {
    67     SDL_tracked_allocation *entry;
    68     int index = get_allocation_bucket(mem);
    69     for (entry = s_tracked_allocations[index]; entry; entry = entry->next) {
    70         if (mem == entry->mem) {
    71             return SDL_TRUE;
    72         }
    73     }
    74     return SDL_FALSE;
    75 }
    76 
    77 static void SDL_TrackAllocation(void *mem, size_t size)
    78 {
    79     SDL_tracked_allocation *entry;
    80     int index = get_allocation_bucket(mem);
    81 
    82     if (SDL_IsAllocationTracked(mem)) {
    83         return;
    84     }
    85     entry = (SDL_tracked_allocation *)SDL_malloc_orig(sizeof(*entry));
    86     if (!entry) {
    87         return;
    88     }
    89     entry->mem = mem;
    90     entry->size = size;
    91 
    92     /* Generate the stack trace for the allocation */
    93     SDL_zero(entry->stack);
    94 #ifdef HAVE_LIBUNWIND_H
    95     {
    96         int stack_index;
    97         unw_cursor_t cursor;
    98         unw_context_t context;
    99 
   100         unw_getcontext(&context);
   101         unw_init_local(&cursor, &context);
   102 
   103         stack_index = 0;
   104         while (unw_step(&cursor) > 0) {
   105             unw_word_t offset, pc;
   106             char sym[256];
   107 
   108             unw_get_reg(&cursor, UNW_REG_IP, &pc);
   109             entry->stack[stack_index] = pc;
   110 
   111             if (unw_get_proc_name(&cursor, sym, sizeof(sym), &offset) == 0) {
   112                 snprintf(entry->stack_names[stack_index], sizeof(entry->stack_names[stack_index]), "%s+0x%llx", sym, offset);
   113             }
   114             ++stack_index;
   115 
   116             if (stack_index == SDL_arraysize(entry->stack)) {
   117                 break;
   118             }
   119         }
   120     }
   121 #endif /* HAVE_LIBUNWIND_H */
   122 
   123     entry->next = s_tracked_allocations[index];
   124     s_tracked_allocations[index] = entry;
   125 }
   126 
   127 static void SDL_UntrackAllocation(void *mem)
   128 {
   129     SDL_tracked_allocation *entry, *prev;
   130     int index = get_allocation_bucket(mem);
   131 
   132     prev = NULL;
   133     for (entry = s_tracked_allocations[index]; entry; entry = entry->next) {
   134         if (mem == entry->mem) {
   135             if (prev) {
   136                 prev->next = entry->next;
   137             } else {
   138                 s_tracked_allocations[index] = entry->next;
   139             }
   140             SDL_free_orig(entry);
   141             return;
   142         }
   143         prev = entry;
   144     }
   145 }
   146 
   147 static void *SDLTest_TrackedMalloc(size_t size)
   148 {
   149     void *mem;
   150 
   151     mem = SDL_malloc_orig(size);
   152     if (mem) {
   153         SDL_TrackAllocation(mem, size);
   154     }
   155     return mem;
   156 }
   157 
   158 static void *SDLTest_TrackedCalloc(size_t nmemb, size_t size)
   159 {
   160     void *mem;
   161 
   162     mem = SDL_calloc_orig(nmemb, size);
   163     if (mem) {
   164         SDL_TrackAllocation(mem, nmemb * size);
   165     }
   166     return mem;
   167 }
   168 
   169 static void *SDLTest_TrackedRealloc(void *ptr, size_t size)
   170 {
   171     void *mem;
   172 
   173     SDL_assert(!ptr || SDL_IsAllocationTracked(ptr));
   174     mem = SDL_realloc_orig(ptr, size);
   175     if (mem && mem != ptr) {
   176         if (ptr) {
   177             SDL_UntrackAllocation(ptr);
   178         }
   179         SDL_TrackAllocation(mem, size);
   180     }
   181     return mem;
   182 }
   183 
   184 static void SDLTest_TrackedFree(void *ptr)
   185 {
   186     if (!ptr) {
   187         return;
   188     }
   189 
   190     if (!s_previous_allocations) {
   191         SDL_assert(SDL_IsAllocationTracked(ptr));
   192     }
   193     SDL_UntrackAllocation(ptr);
   194     SDL_free_orig(ptr);
   195 }
   196 
   197 int SDLTest_TrackAllocations()
   198 {
   199     if (SDL_malloc_orig) {
   200         return 0;
   201     }
   202 
   203     SDLTest_Crc32Init(&s_crc32_context);
   204 
   205     s_previous_allocations = SDL_GetNumAllocations();
   206     if (s_previous_allocations != 0) {
   207         SDL_Log("SDLTest_TrackAllocations(): There are %d previous allocations, disabling free() validation", s_previous_allocations);
   208     }
   209 
   210     SDL_GetMemoryFunctions(&SDL_malloc_orig,
   211                            &SDL_calloc_orig,
   212                            &SDL_realloc_orig,
   213                            &SDL_free_orig);
   214 
   215     SDL_SetMemoryFunctions(SDLTest_TrackedMalloc,
   216                            SDLTest_TrackedCalloc,
   217                            SDLTest_TrackedRealloc,
   218                            SDLTest_TrackedFree);
   219     return 0;
   220 }
   221 
   222 void SDLTest_LogAllocations()
   223 {
   224     char *message = NULL;
   225     size_t message_size = 0;
   226     char line[128], *tmp;
   227     SDL_tracked_allocation *entry;
   228     int index, count, stack_index;
   229     Uint64 total_allocated;
   230 
   231     if (!SDL_malloc_orig) {
   232         return;
   233     }
   234 
   235 #define ADD_LINE() \
   236     message_size += (SDL_strlen(line) + 1); \
   237     tmp = (char *)SDL_realloc_orig(message, message_size); \
   238     if (!tmp) { \
   239         return; \
   240     } \
   241     message = tmp; \
   242     SDL_strlcat(message, line, message_size)
   243 
   244     SDL_strlcpy(line, "Memory allocations:\n", sizeof(line));
   245     ADD_LINE();
   246     SDL_strlcpy(line, "Expect 2 allocations from within SDL_GetErrBuf()\n", sizeof(line));
   247     ADD_LINE();
   248 
   249     count = 0;
   250     total_allocated = 0;
   251     for (index = 0; index < SDL_arraysize(s_tracked_allocations); ++index) {
   252         for (entry = s_tracked_allocations[index]; entry; entry = entry->next) {
   253             SDL_snprintf(line, sizeof(line), "Allocation %d: %d bytes\n", count, (int)entry->size);
   254             ADD_LINE();
   255             /* Start at stack index 1 to skip our tracking functions */
   256             for (stack_index = 1; stack_index < SDL_arraysize(entry->stack); ++stack_index) {
   257                 if (!entry->stack[stack_index]) {
   258                     break;
   259                 }
   260                 SDL_snprintf(line, sizeof(line), "\t0x%"SDL_PRIx64": %s\n", entry->stack[stack_index], entry->stack_names[stack_index]);
   261                 ADD_LINE();
   262             }
   263             total_allocated += entry->size;
   264             ++count;
   265         }
   266     }
   267     SDL_snprintf(line, sizeof(line), "Total: %.2f Kb in %d allocations\n", (float)total_allocated / 1024, count);
   268     ADD_LINE();
   269 #undef ADD_LINE
   270 
   271     SDL_Log("%s", message);
   272 }
   273 
   274 /* vi: set ts=4 sw=4 expandtab: */