johnsonjh/duma

View on GitHub
stacktrace/internal/StackTrace.cpp

Summary

Maintainability
Test Coverage
/*
 * Copyright (c) 2006 Michael Eddington
 * Copyright (c) 2001 Jani Kajala
 *
 * Permission to use, copy, modify, distribute and sell this
 * software and its documentation for any purpose is hereby
 * granted without fee, provided that the above copyright notice
 * appear in all copies and that both that copyright notice and
 * this permission notice appear in supporting documentation.
 * Jani Kajala makes no representations about the suitability
 * of this software for any purpose. It is provided "as is"
 * without express or implied warranty.
 */

#include "StackTrace.h"
#include "MapFile.h"
#include "MapFileEntry.h"
#include <string.h>
#include <stdio.h>

//-----------------------------------------------------------------------------

static const char* internalFunctions[] = {
    "__duma_malloc",
    "__duma_calloc",
    "__duma_realloc",
    "__duma_valloc",
    "__duma_allocate",
    "_printStackTrace",
    0
    };

#define MAX_DEPTH 32

//-----------------------------------------------------------------------------

namespace dev
{

static int IsInternalFunction(const char* func)
{
    for(int i=0; internalFunctions[i]; i++)
    {
        if(!strcmp(func, internalFunctions[i]))
            return 1;
    }

    return 0;
}


/*
EBP points to stack frame (at least 2 DWORDs) which looks something like this:
DWORD[EBP+4] = return address (=calling function)
DWORD[EBP+0] = calling function stack frame

So we
1) get the EBP to EBX
2) take calling function address to EAX
3) parent stack frame to EBX and
4) loop 2,3,4 until desired level in calling stack is reached.

Beware that compiler does not always make stack frame (at least if s.c. "omit stack frame"
optimization is enabled), so at least I'd use this code only in debug build.
*/
static long getCaller( int index )
{
#if defined(_DEBUG) && defined(_MSC_VER) && defined(_M_IX86)

    // Changed a couple things here to prevent some
    // access violations, esp with release code that
    // doesn't always have a stack frame.
    //
    // 1. Check next frame to see if we are below 10000
    // 2. If we are forcing a stop make the return addr 0

    long caller = 0;
    __asm
    {
        mov ebx, ebp
        mov ecx, index
        inc ecx
        xor eax, eax
StackTrace_getCaller_next:
        mov eax, [ebx+4]
        mov ebx, [ebx]
        cmp ebx, 10000
        jb StackTrace_getCaller_stop
        dec ecx
        jnz StackTrace_getCaller_next
        jmp StackTrace_getCaller_end
StackTrace_getCaller_stop:
        mov eax, 0
StackTrace_getCaller_end:
        mov caller, eax
    }
    return caller;

#else

    return 0;

#endif
}

int StackTrace::printStackTrace( MapFile** map, int maps,
    int initLevel, int maxDepth, char* buffer, int bufferSize )
{
    if ( maxDepth > MAX_DEPTH )
        maxDepth = MAX_DEPTH;

    // list callers
    long callersAddr[MAX_DEPTH];
    MapFileEntry* callersEntry[MAX_DEPTH];
    int callers;
    int i;
    int entry;
    long addr = -1;
    int flag = 0;
    int j;

    for (callers = 0, i = initLevel ; i < maxDepth && addr ; i++)
    {
        addr = getCaller( i );

        callersEntry[callers] = NULL;
        for (entry = -1, j = 0 ; j < maps ; ++j )
        {
            entry = map[j]->findEntry( addr );

            if ( entry != -1 )
            {
                callersEntry[callers] = map[j]->getEntry(entry);
                break;
            }
        }

        if(entry == -1)
            continue;

        callersAddr[callers] = addr;

        callers++;
    }

    // output call stack
    if ( bufferSize > 0 )
        *buffer = 0;

    int needed = 0;

    for ( i = 1 ; i <= callers ; ++i )
    {
        addr = callersAddr[callers-i];

        if(IsInternalFunction(callersEntry[callers-i]->name()))
            continue;

        // format entry to tempory buf
        char buf[MapFileEntry::MAX_NAME+MAX_DEPTH+20];    // name + margin + hex number
        buf[0] = 0;

        for ( int k = initLevel-1 ; k < i ; ++k )
            strcat( buf, " " );

        if ( callersEntry[callers-i] == NULL )
            sprintf( buf+strlen(buf), "0x%x\n", addr );
        else
            sprintf( buf+strlen(buf), "%s (%x)\n", callersEntry[callers-i]->name(), addr );

        // append temporary buf to output buffer if space left
        needed += (int)strlen( buf );
        if ( needed < bufferSize )
            strcat( buffer, buf );
    }

    // terminate output buffer
    if ( needed < bufferSize )
        buffer[needed] = 0;
    else if ( bufferSize > 0 )
        buffer[bufferSize-1] = 0;

    return needed;
}


} // dev