johnsonjh/duma

View on GitHub
stacktrace/internal/MapFile.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.
 */

/* $Id$ */

#include "MapFile.h"
#include "MapFileEntry.h"
#include "TextFile.h"
#include "Array.h"
#include <algorithm>
#include <string.h>
#include <ctype.h>
#ifdef WIN32
#include <windows.h>
#endif

/*---------------------------------------------------------------------------*/

namespace dev
{

/* Lots of changes from origional to fix bugs    */
/* improve spead, and clean things up.           */
/* We are only parsing function entries now, but */
/* should add support for line numbers.          */
class MapFile::MapFileImpl
{
public:
    long                loadAddr;
    char                name[256];
    Array<MapFileEntry*> entries;

    MapFileImpl( const char* filename ) :
        loadAddr(0), m_file( filename ), m_err( MapFile::ERROR_NONE )
    {
        m_file.readString( name, sizeof(name) );

        char buf[1024];
        while ( m_file.readString(buf,sizeof(buf)) )
        {
            if ( !strcmp("Address",buf) )
                parseEntries();
            else
                m_file.skipLine();
        }

        std::sort( entries.begin(), entries.end() );
    }

    ~MapFileImpl()
    {
        for(int i = entries.size(); i; i--)
            delete entries[i];
    }

    ErrorType error() const
    {
        if ( m_err != MapFile::ERROR_NONE )
            return m_err;

        switch ( m_file.error() )
        {
        case TextFile::ERROR_OPEN:    return MapFile::ERROR_OPEN;
        case TextFile::ERROR_READ:    return MapFile::ERROR_READ;
        case TextFile::ERROR_PARSE:    return MapFile::ERROR_PARSE;
        default:                    return MapFile::ERROR_NONE;
        }
    }

    int line() const
    {
        if ( m_err != MapFile::ERROR_NONE )
            return m_errLine;

        return m_file.line();
    }

private:
    TextFile            m_file;
    MapFile::ErrorType    m_err;
    int                    m_errLine;

    /**
     * Returns true if the next line is empty.
     */
    bool nextLineEmpty()
    {
        m_file.skipLine();
        char ch;
        while ( m_file.peekChar(&ch) && isspace(ch) && ch != '\n' )
            m_file.readChar( &ch );
        if ( m_file.peekChar(&ch) && ch == '\n' )
            return true;
        return false;
    }

    /**
     * Parses specified string.
     * Sets error if parsed string doesnt match.
     */
    void parse( const char* str )
    {
        char buf[256];
        m_file.readString( buf, sizeof(buf) );
        if ( strcmp(str,buf) )
        {
            m_err = MapFile::ERROR_PARSE;
            m_errLine = m_file.line();
        }
    }

    /**
     * Parses specified character.
     * Sets error if parsed character doesnt match.
     */
    void parse( char ch )
    {
        char ch2;
        if ( !m_file.readChar(&ch2) || ch2 != ch )
        {
            m_err = MapFile::ERROR_PARSE;
            m_errLine = m_file.line();
        }
    }

    /**
     * Example:
     * (Preferred) load address is 00400000
     */
    void parseLoadAddress()
    {
        parse( "load" ); parse( "address" ); parse( "is" );
        loadAddr = m_file.readHex();
    }

    /**
     * Example:
     * (Address)       Publics by Value           Rva+Base     Lib:Object
     * 0001:000001a0   ?stackTrace@@YAXXZ         004011a0 f   main.obj
     */
    void parseEntries()
    {
        unsigned int seg;
        unsigned int offs;
        unsigned int rvabase;
        char    buf[256];
        char*    entryname;
        char    lib[256];
        char*    str;

        parse( "Publics" ); parse( "by" ); parse( "Value" );
        parse( "Rva+Base" );
        parse( "Lib:Object" );
        m_file.skipWhitespace();

        while ( !error() )
        {
            seg = m_file.readHex();
            parse( ':' );
            offs = m_file.readHex();
            m_file.readString( buf, sizeof(buf) );
            entryname = buf;
            rvabase = m_file.readHex();
            m_file.readString( lib, sizeof(lib) );
            if(!strcmp(lib, "f"))
                m_file.readString( lib, sizeof(lib) );

            /* chop entry name at @@ */
            str = strstr( entryname, "@@" );
            if ( str )
                *str = 0;

            /* skip preceding ?01.. */
            while ( isdigit(*entryname) || *entryname == '?' || *entryname == '$' )
                ++entryname;

            /* conv @ -> . */
            for ( str = entryname ; *str ; ++str )
                if ( *str == '@' )
                    *str = '.';

            entries.add( new MapFileEntry(seg,offs,0,entryname, rvabase, lib) );

            // break at empty line
            if ( nextLineEmpty() )
                break;
        }
    }
};

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

MapFile::MapFile( const char* filename )
{
    m_this = new MapFileImpl( filename );
}

MapFile::~MapFile()
{
    delete m_this;
}

long MapFile::loadAddress() const
{
    return m_this->loadAddr;
}

MapFileEntry*    MapFile::getEntry( int i ) const
{
    return m_this->entries[i];
}

int MapFile::entries() const
{
    return m_this->entries.size();
}

MapFile::ErrorType MapFile::error() const
{
    return m_this->error();
}

int MapFile::line() const
{
    return m_this->line();
}

int MapFile::findEntry( long addr ) const
{
    // Changed this to use the rvabase instead
    // of calculating things based on segment.

    // Addresses of zero are bogus
    if(addr == 0)
        return -1;

    // Check and see if our addr is way larger then
    // the highest rva+base address we have.
    if(addr > (getEntry(entries()-1)->rvabase() + 10000))
        return -1;

    // Entries are sorted, so searching from last
    // to first will work well
    for ( int i = entries()-1 ; i >= 0  ; --i )
    {
        if(getEntry( i )->rvabase() <= addr)
            return i;
    }

    return -1;
}

void MapFile::getModuleMapFilename( char* buffer, int bufferSize )
{
    int len = 0;
    buffer[len] = 0;

#ifdef WIN32
    // get name of the exe/dll
    len = GetModuleFileName( GetModuleHandle(0), buffer, bufferSize-1 );
    buffer[len] = 0;
#endif

    // remove .exe or .dll extension
    if ( len > 3 &&
        (!strcmp(buffer+len-4,".exe") || !strcmp(buffer+len-4,".EXE") ||
        !strcmp(buffer+len-4,".DLL") || !strcmp(buffer+len-4,".dll")) )
    {
        buffer[len-4] = 0;
    }

    // append .map extension
    if ( (int)strlen(buffer)+4 < bufferSize )
    {
        strcat( buffer, ".map" );
    }
}



} // dev