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