  | 
   Java Style Call Stack Printing In C++ 
   Submitted by  |   
  
  
This small C++ library implements "Java-style" call stack printing using stack frames and map files. Makes
for example assertions more useful as you can see the context where the assertion failed. The library
is very easy to use (you need only one function, printStackTrace) and the library can be freely
used in any applications.
Regards, 
Jani Kajala 
Email: jani@sumea.com 
Homepage: http://www.helsinki.fi/~kajala/
 
 
  Editor's note: See /doc/printStackTrace.html within the zip file, for more information about the library.
  
 | 
 
 
 
Currently browsing [stacktrace.zip] (38,843 bytes) - [internal/Array.h] - (2,394 bytes)
 
 #ifndef _DEV_ARRAY_H
#define _DEV_ARRAY_H
  
namespace dev
{
  
/** Very simple dynamic array. */
template <class T> class Array
{
public:
	/** Creates an empty array. */
	Array() :
		m_data(0), m_len(0), m_cap(0)
	{
	}
  	/** Creates an array of specified size. */
	explicit Array( int size ) :
		m_data(0), m_len(0), m_cap(0)
	{
		setSize( size );
	}
  	///
	~Array()
	{
		delete[] m_data;
	}
  	/** Appends an item at the end of the array. */
	void add( const T& item )
	{
		if ( m_len+1 > m_cap )
			setCapacity( m_len + 1 );
		m_data[m_len++] = item;
	}
  	/** Resizes the array. */
	void setSize( int size )
	{
		if ( size > m_cap )
			setCapacity( size );
		m_len = size;
	}
  	/** Returns ith item. */
	T& operator[]( int i )
	{
		return m_data[i];
	}
  	/** Returns pointer to the first element in the vector. */
	T* begin() 
	{
		return m_data;
	}
  	/** Returns pointer to one beyond the last element in the vector. */
	T* end() 
	{
		return m_data + m_len;
	}
  	/** Returns number of items in the array. */
	int size() const
	{
		return m_len;
	}
  	/** Returns ith item. */
	const T& operator[]( int i ) const
	{
		return m_data[i];
	}
  	/** Returns pointer to the first element in the vector. */
	const T* begin() const														
	{
		return m_data;
	}
  	/** Returns pointer to one beyond the last element in the vector. */
	const T* end() const															
	{
		return m_data + m_len;
	}
  private:
	T*		m_data;
	int		m_len;
	int		m_cap;
  	void setCapacity( int cap )
	{
		++cap;
		if ( cap < 8 )
			cap = 8;
		else if ( cap < m_cap*2 )
			cap = m_cap*2;
		m_cap = cap;
  		T* data = new T[cap];
		for ( int i = 0 ; i < m_len ; ++i )
			data[i] = m_data[i];
		delete[] m_data;
		m_data = data;
	}
};
  
} // dev
  #endif // _DEV_ARRAY_H
/*
 * 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.
 */
   |  
  
 | 
 
  
Currently browsing [stacktrace.zip] (38,843 bytes) - [internal/MapFile.cpp] - (6,832 bytes)
 
 #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
{
  
class MapFile::MapFileImpl
{
public:
	long				loadAddr;
	char				name[256];
	Array<MapFileEntry> segments;
	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("Preferred",buf) )
				parseLoadAddress();
			else if ( !strcmp("Start",buf) )
				parseSegments();
			else if ( !strcmp("Address",buf) )
				parseEntries();
			else
				m_file.skipLine();
		}
  		std::sort( segments.begin(), segments.end() );
		std::sort( entries.begin(), entries.end() );
	}
  	~MapFileImpl()
	{
	}
  	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:
	 * (Start)       Length     Name                   Class
	 * 0001:00000000 00002c05H .text                   CODE
	 */
	void parseSegments()
	{
		parse( "Length" );
		parse( "Name" );
		parse( "Class" );
		m_file.skipWhitespace();
		
		while ( !error() )
		{
			int seg = m_file.readHex();
			parse( ':' );
			int offs = m_file.readHex();
			int len = m_file.readHex();
			parse( 'H' );
			char buf[256];
			m_file.readString( buf, sizeof(buf) );
			segments.add( MapFileEntry(seg,offs,len,buf) );
  			// break at empty line
			if ( nextLineEmpty() )
				break;
		}
	}
  	/**
	 * Example:
	 * (Address)       Publics by Value           Rva+Base     Lib:Object
	 * 0001:000001a0   ?stackTrace@@YAXXZ         004011a0 f   main.obj
	 */
	void parseEntries()
	{
		parse( "Publics" ); parse( "by" ); parse( "Value" );
		parse( "Rva+Base" );
		parse( "Lib:Object" );
		m_file.skipWhitespace();
		
		while ( !error() )
		{
			int seg = m_file.readHex();
			parse( ':' );
			int offs = m_file.readHex();
			char buf[256];
			m_file.readString( buf, sizeof(buf) );
			char* entryname = buf;
  			// chop entry name at @@
			char* end = strstr( entryname, "@@" );
			if ( end )
				*end = 0;
			// skip preceding ?01..
			while ( isdigit(*entryname) || *entryname == '?' || *entryname == '$' )
				++entryname;
			// conv @ -> .
			for ( char* str = entryname ; *str ; ++str )
				if ( *str == '@' )
					*str = '.';
  			entries.add( MapFileEntry(seg,offs,0,entryname) );
  			// 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;
}
  const MapFileEntry&	MapFile::getSegment( int i ) const
{
	return m_this->segments[i];
}
  const MapFileEntry&	MapFile::getEntry( int i ) const
{
	return m_this->entries[i];
}
  int MapFile::segments() const
{
	return m_this->segments.size();
}
  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
{
	for ( int j = 0 ; j < segments() ; ++j )
	{
		const MapFileEntry& segment = getSegment( j );
		long section = segment.section();
		long segmentBegin = loadAddress() + (segment.section() << 12) + segment.offset();
		long segmentEnd = segmentBegin + segment.length();
  		if ( addr >= segmentBegin && addr < segmentEnd )
		{
			for ( int i = entries()-1 ; i >= 0  ; --i )
			{
				const MapFileEntry entry = getEntry( i );
				if ( entry.section() == section )
				{
					long entryAddr = loadAddress() + (entry.section() << 12) + entry.offset();
					if ( entryAddr <= 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
/*
 * 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.
 */
   |  
  
 | 
 
  
Currently browsing [stacktrace.zip] (38,843 bytes) - [internal/MapFile.h] - (2,144 bytes)
 
 #ifndef _DEV_MAPFILE_H
#define _DEV_MAPFILE_H
  
namespace dev
{
  
class MapFileEntry;
  
/** 
 * Linker generated module map file parser.
 */
class MapFile
{
public:
	/** Error code. */
	enum ErrorType
	{
		/** No error. */
		ERROR_NONE,
		/** File open failed. */
		ERROR_OPEN,
		/** File reading failed. */
		ERROR_READ,
		/** Syntax error. */
		ERROR_PARSE
	};
  	/** Reads a map file. */
	explicit MapFile( const char* filename );
  	///
	~MapFile();
  	/** Returns preferred load address. */
	long				loadAddress() const;
  	/** Returns ith entry from the map file. */
	const MapFileEntry&	getEntry( int i ) const;
  	/** Returns ith segment from the map file. */
	const MapFileEntry&	getSegment( int i ) const;
  	/** Returns number of segments in the map file. */
	int					segments() const;
  	/** Returns number of entries in the map file. */
	int					entries() const;
  	/** Returns error code or 0 (ERROR_NONE) if no error. */
	ErrorType			error() const;
  	/** Returns line number of last successful read character. */
	int					line() const;
  	/** 
	 * Finds entry which contains specified address. 
	 * @return Entry index or -1 if not found.
	 */
	int					findEntry( long addr ) const;
  	/** 
	 * Returns current module name, with map extension.
	 * The output buffer is always 0-terminated.
	 */
	static void			getModuleMapFilename( char* buffer, int bufferSize );
  private:
	class MapFileImpl;
	MapFileImpl* m_this;
  	MapFile( const MapFile& );
	MapFile& operator=( const MapFile& );
};
  
} // dev
  #endif // _DEV_MAPFILE_H
/*
 * 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.
 */
   |  
  
 | 
 
  
Currently browsing [stacktrace.zip] (38,843 bytes) - [internal/MapFileEntry.cpp] - (1,451 bytes)
 
 #include "MapFileEntry.h"
#include <string.h>
  //-----------------------------------------------------------------------------
namespace dev
{
  
MapFileEntry::MapFileEntry()
{
	m_sec = 0;
	m_addr = 0; 
	m_len = 0;
	m_name[0] = 0;
}
  MapFileEntry::MapFileEntry( long section, long offset, long length, const char* name )
{
	m_sec = section;
	m_addr = offset;
	m_len = length;
  	strncpy( m_name, name, MAX_NAME ); 
	m_name[MAX_NAME] = 0;
}
  long MapFileEntry::section() const
{
	return m_sec;
}
  long MapFileEntry::offset() const
{
	return m_addr;
}
  long MapFileEntry::length() const
{
	return m_len;
}
  const char* MapFileEntry::name() const
{
	return m_name;
}
  bool MapFileEntry::operator<( const MapFileEntry& other ) const
{
	if ( m_sec < other.m_sec )
		return true;
	if ( m_sec > other.m_sec )
		return false;
	return m_addr < other.m_addr;
}
  
} // dev
/*
 * 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.
 */
   |  
  
 | 
 
  
Currently browsing [stacktrace.zip] (38,843 bytes) - [internal/MapFileEntry.h] - (1,548 bytes)
 
 #ifndef _DEV_MAPFILEENTRY_H
#define _DEV_MAPFILEENTRY_H
  
namespace dev
{
  
/** An entry in the map file. */
class MapFileEntry
{
public:
	/** Class constants. */
	enum Constants
	{
		/** Maximum number of characters in map file entry name. */
		MAX_NAME = 256
	};
  	///
	MapFileEntry();
  	/** Creates an entry with specified section, offset, length and name. */
	MapFileEntry( long section, long offset, long length, const char* name );
  	/** Returns section of the entry. */
	long		section() const;
  	/** Returns offset of the entry. */
	long		offset() const;
  	/** Returns length of the entry (only defined for segments). */
	long		length() const;
  	/** Returns name of the entry. */
	const char*	name() const;
  	/** Returns true if the offset of this entry is before the other one. */
	bool		operator<( const MapFileEntry& other ) const;
  private:
	long	m_sec;
	long	m_addr;
	long	m_len;
	char	m_name[MAX_NAME+1];
};
  
} // dev
  #endif // _DEV_MAPFILEENTRY_H
/*
 * 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.
 */
   |  
  
 | 
 
  
Currently browsing [stacktrace.zip] (38,843 bytes) - [internal/StackTrace.cpp] - (2,945 bytes)
 
 #include "StackTrace.h"
#include "MapFile.h"
#include "MapFileEntry.h"
#include <string.h>
#include <stdio.h>
  //-----------------------------------------------------------------------------
#define MAX_DEPTH 32
  //-----------------------------------------------------------------------------
namespace dev
{
  
static long getCaller( int index )
{
#if defined(_DEBUG) && defined(_MSC_VER) && defined(_M_IX86)
  	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]
		dec ecx
		jnz StackTrace_getCaller_next
		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];
	int callers = 0;
	int i;
	for ( i = initLevel ; i < maxDepth ; ++i )
	{
		long addr = getCaller( i );
		callersAddr[callers++] = addr;
  		// end tracing here if the entry is not in a map file
		int entry = -1;
		for ( int j = 0 ; j < maps ; ++j )
		{
			entry = map[j]->findEntry( addr );
			if ( -1 != entry )
				break;
		}
		if ( -1 == entry )
			break;
	}
  	// output call stack
	if ( bufferSize > 0 )
		*buffer = 0;
	int needed = 0;
	for ( i = initLevel ; i < callers ; ++i )
	{
		long addr = callersAddr[callers-i-1];
  		// find entry info
		int entry = -1;
		const MapFile* entryMap = 0;
		for ( int j = 0 ; j < maps ; ++j )
		{
			entry = map[j]->findEntry( addr );
			if ( -1 != entry )
			{
				entryMap = map[j];
				break;
			}
		}
  		// 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 ( !entryMap )
			sprintf( buf+strlen(buf), "0x%x\n", addr );
		else
			sprintf( buf+strlen(buf), "%s (%x)\n", entryMap->getEntry(entry).name(), addr );
  		// append temporary buf to output buffer if space left
		needed += 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
/*
 * 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.
 */
   |  
  
 | 
 
  
Currently browsing [stacktrace.zip] (38,843 bytes) - [internal/StackTrace.h] - (1,373 bytes)
 
 #ifndef _DEV_STACKTRACE_H
#define _DEV_STACKTRACE_H
  
namespace dev
{
  
class MapFile;
  
/** Stack tracing utility. */
class StackTrace
{
public:
	/**
	 * Prints formatted call stack to the user buffer.
	 * Always terminates the user buffer with 0.
	 *
	 * @param map Array of pointers to map files.
	 * @param maps Number of map files.
	 * @param initLevel Number of functions to skip before starting the tracing.
	 * @param maxDepth Maximum number of levels in the stack trace.
	 * @param buffer [out] Output buffer for the formatted stack trace.
	 * @param bufferSize Size of the output buffer.
	 * @return Needed buffer size.
	 */
	static int	printStackTrace( MapFile** map, int maps,
					int initLevel, int maxDepth,
					char* buffer, int bufferSize );
};
  
} // dev
  #endif // _DEV_STACKTRACE_H
/*
 * 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.
 */
   |  
  
 | 
 
  
Currently browsing [stacktrace.zip] (38,843 bytes) - [internal/TextFile.cpp] - (4,554 bytes)
 
 #include "TextFile.h"
#include <stdio.h>
#include <ctype.h>
  //-----------------------------------------------------------------------------
namespace dev
{
  
class TextFile::TextFileImpl
{
public:
	TextFile::ErrorType		err;
	int						line;
  	explicit TextFileImpl( const char* filename )
	{
		err				= TextFile::ERROR_NONE;
		line			= 1;
		m_peeked		= false;
		m_peekedChar	= 0;
		m_file			= fopen( filename, "rt" );
  		if ( !m_file )
			err = TextFile::ERROR_OPEN;
	}
  	~TextFileImpl()
	{
		if ( m_file )
		{
			fclose( m_file );
			m_file = 0;
		}
	}
  	bool eof() const
	{
		if ( err )
			return true;
		return 0 != feof(m_file);
	}
  	bool peekChar( char* ch )
	{
		if ( err )
			return false;
  		if ( !m_peeked )
		{
			int c = getc( m_file );
			if ( EOF != c )
			{
				m_peeked = true;
				m_peekedChar = (char)c;
			}
			else
			{
				if ( ferror(m_file) )
					err = TextFile::ERROR_READ;
			}
		}
  		if ( m_peeked )
			*ch = m_peekedChar;
		return m_peeked;
	}
  	bool readChar( char* ch )
	{
		if ( err )
			return false;
  		bool more = peekChar( ch );
		m_peeked = false;
		if ( more && *ch == '\n' )
			++line;
		return more;
	}
  	bool skipWhitespace()
	{
		if ( err )
			return false;
  		char ch;
		while ( peekChar(&ch) )
		{
			if ( !isspace(ch) )
				break;
			readChar( &ch );
		}
		return !eof();
	}
  	bool readString( char* buf, int size )
	{
		if ( err )
			return false;
  		skipWhitespace();
  		int count = 0;
		char ch;
		while ( peekChar(&ch) )
		{
			if ( isspace(ch) )
				break;
			if ( count+1 < size )
				buf[count++] = ch;
			readChar( &ch );
		}
		if ( size > 0 )
			buf[count] = 0;
		return count > 0;
	}
  	void skipLine()
	{
		if ( err )
			return;
  		char ch;
		while ( readChar(&ch) )
		{
			if ( ch == '\n' )
				break;
		}
	}
  	long readHex()
	{
		if ( err )
			return 0;
  		skipWhitespace();
  		// hex must start with alphanumeric character
		char ch;
		if ( !peekChar(&ch) || !isalnum(ch) )
		{
			err = TextFile::ERROR_PARSE;
			return 0;
		}
  		long x = 0;
		while ( peekChar(&ch) )
		{
			switch ( ch )
			{
			case '0':	x <<= 4; x += 0; break;
			case '1':	x <<= 4; x += 1; break;
			case '2':	x <<= 4; x += 2; break;
			case '3':	x <<= 4; x += 3; break;
			case '4':	x <<= 4; x += 4; break;
			case '5':	x <<= 4; x += 5; break;
			case '6':	x <<= 4; x += 6; break;
			case '7':	x <<= 4; x += 7; break;
			case '8':	x <<= 4; x += 8; break;
			case '9':	x <<= 4; x += 9; break;
			case 'a':
			case 'A':	x <<= 4; x += 0xA; break;
			case 'b':
			case 'B':	x <<= 4; x += 0xB; break;
			case 'c':
			case 'C':	x <<= 4; x += 0xC; break;
			case 'd':
			case 'D':	x <<= 4; x += 0xD; break;
			case 'e':
			case 'E':	x <<= 4; x += 0xE; break;
			case 'f':
			case 'F':	x <<= 4; x += 0xF; break;
			default:	return x;
			}
			readChar( &ch );
		}
		return x;
	}
  private:
	bool	m_peeked;
	char	m_peekedChar;
	FILE*	m_file;
  	TextFileImpl( const TextFileImpl& );
	TextFileImpl& operator=( const TextFileImpl& );
};
  //-----------------------------------------------------------------------------
TextFile::TextFile( const char* filename )
{
	m_this = new TextFileImpl( filename );
}
  TextFile::~TextFile()
{
	delete m_this;
}
  bool TextFile::readString( char* buf, int size )
{
	return m_this->readString( buf, size );
}
  void TextFile::skipLine()
{
	m_this->skipLine();
}
  long TextFile::readHex()
{
	return m_this->readHex();
}
  bool TextFile::skipWhitespace()
{
	return m_this->skipWhitespace();
}
  TextFile::ErrorType TextFile::error() const
{
	return m_this->err;
}
  bool TextFile::readChar( char* ch )
{
	return m_this->readChar( ch );
}
  bool TextFile::peekChar( char* ch )
{
	return m_this->peekChar( ch );
}
  bool TextFile::eof() const
{
	return m_this->eof();
}
  int TextFile::line() const
{
	return m_this->line;
}
  
} // dev
/*
 * 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.
 */
   |  
  
 | 
 
  
Currently browsing [stacktrace.zip] (38,843 bytes) - [internal/TextFile.h] - (2,316 bytes)
 
 #ifndef _DEV_TEXTFILE_H
#define _DEV_TEXTFILE_H
  
namespace dev
{
  
/** 
 * ASCII-7 text file parser. Doesnt throw exceptions.
 */
class TextFile
{
public:
	/** Error code. */
	enum ErrorType
	{
		/** No error. */
		ERROR_NONE,
		/** File open failed. */
		ERROR_OPEN,
		/** File reading failed. */
		ERROR_READ,
		/** Syntax error. */
		ERROR_PARSE
	};
  	/** Opens a file. */
	explicit TextFile( const char* filename );
  	///
	~TextFile();
  	/** 
	 * Reads a single character. 
	 * @return true if read ok.
	 */
	bool		readChar( char* ch );
  	/** 
	 * Peeks a single character. 
	 * @return true if peek ok.
	 */
	bool		peekChar( char* ch );
  	/** 
	 * Reads whitespace delimited string.
	 * If the string doesnt fit to the buffer then
	 * the rest of the string is skipped. Buffer
	 * is always 0-terminated.
	 * @param buf [out] Pointer to string buffer.
	 * @param size String buffer size. Must be larger than 0.
	 * @return false if end-of-file reached before any characters was read.
	 */
	bool		readString( char* buf, int size );
  	/** Skips the rest of the line. */
	void		skipLine();
  	/** Reads hex integer. Skips preceding whitespace. */
	long		readHex();
  	/** 
	 * Skips whitespace characters. 
	 * @return false if end-of-file reached.
	 */
	bool		skipWhitespace();
  	/** Returns true if end-of-file have been reached. */
	bool		eof() const;
  	/** Returns error code or 0 (ERROR_NONE) if no error. */
	ErrorType	error() const;
  	/** Returns line number of last successful read character. */
	int			line() const;
  private:
	class TextFileImpl;
	TextFileImpl* m_this;
  	TextFile( const TextFile& );
	TextFile& operator=( const TextFile& );
};
  
} // dev
  #endif // _DEV_TEXTFILE_H
/*
 * 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.
 */
   |  
  
 | 
 
  
Currently browsing [stacktrace.zip] (38,843 bytes) - [test/main.cpp] - (937 bytes)
 
 #include "../printStackTrace.h"
#include <stdio.h>
  //-----------------------------------------------------------------------------
#define testAssert( EXPR ) if (EXPR) {} else {testAssertFailed(__FILE__,__LINE__,#EXPR);}
  //-----------------------------------------------------------------------------
void testAssertFailed( const char* fname, int line, const char* expr )
{
	char buf[1000];
	printStackTrace( buf, sizeof(buf) );
  	printf( "Test assertion: %s(%i)\n", fname, line );
	printf( "Expression: %s\n", expr );
	printf( "Stack trace:\n" );
	puts( buf );
}
  //-----------------------------------------------------------------------------
void testfunc3( int x )
{
	testAssert( x == 3 );
}
  void testfunc2( int arg1 )
{
	testfunc3( arg1 );
}
  void testfunc1( int arg1 )
{
	testfunc2( arg1 );
}
  int main()
{
	testfunc1( 4 );
	puts( "Press enter to exit" );
	getchar();
	return 0;
}
   |  
  
 | 
 
  
Currently browsing [stacktrace.zip] (38,843 bytes) - [printStackTrace.cpp] - (1,835 bytes)
 
 #include "printStackTrace.h"
#include "StackTrace.h"
#include "MapFile.h"
#include <stdio.h>
#include <string.h>
  //-----------------------------------------------------------------------------
using namespace dev;
  //-----------------------------------------------------------------------------
/**
 * Prints stack trace to user defined buffer.
 * Always terminates the buffer with 0.
 */
void printStackTrace( char* buffer, int bufferSize )
{
	// find out map file name
	char modname[500];
	MapFile::getModuleMapFilename( modname, sizeof(modname) );
  	// parse map file
	char buf[600];
	MapFile map( modname );
	switch ( map.error() )
	{
	case MapFile::ERROR_OPEN:	sprintf( buf, "Failed to open map file %s\n", modname ); break;
	case MapFile::ERROR_READ:	sprintf( buf, "Error while reading map file %s(%i)\n", modname, map.line() ); break;
	case MapFile::ERROR_PARSE:	sprintf( buf, "Parse error in map file %s(%i)\n", modname, map.line() ); break;
	default:					break;
	}
  	// print stack trace to buffer
	if ( !map.error() )
	{
		MapFile* maps[] = {&map};
		StackTrace::printStackTrace( maps, 1, 1, 16, buf, sizeof(buf) );
	}
  	// copy to user buffer
	if ( bufferSize > 0 )
	{
		strncpy( buffer, buf, bufferSize );
		buffer[bufferSize-1] = 0;
	}
}
  /*
 * 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.
 */
   |  
  
 | 
 
  
Currently browsing [stacktrace.zip] (38,843 bytes) - [printStackTrace.h] - (907 bytes)
 
 #ifndef _PRINTSTACKTRACE_H
#define _PRINTSTACKTRACE_H
  
/**
 * Prints formatted call stack to the user defined buffer,
 * always terminating the buffer with 0.
 * Uses stack frame to find out the caller function address and 
 * the map file to find out the function name.
 */
void printStackTrace( char* buffer, int bufferSize );
  
#endif // _PRINTSTACKTRACE_H
/*
 * 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.
 */
   |  
  
 | 
 
 
 
The zip file viewer built into the Developer Toolbox made use
of the zlib library, as well as the zlibdll source additions.
 
 
 |