Walking the Callstack

Introduction

In some cases, you need to display the callstack of the current thread or you are just interested in the callstack of other threads/processes. Therefore, I wrote this project.

The goal for this project was the following:


  • Simple interface to generate a callstack

  • C++ based to allow overwrites of several methods

  • Hide the implementation details (API) from the class’ interface

  • Support of x86, x64, and IA64 architecture

  • Default output to debugger-output window (but can be customized)

  • Support of user-provided read-memory-function

  • Support of the widest range of development-IDEs (VC5-VC8)

  • Most portable solution to walk the callstack

Background

To walk the callstack, there is a documented interface: StackWalk64. Starting with Win9x/W2K, this interface is in the dbghelp.dll library (on NT, it is in imagehlp.dll). But, the function name (StackWalk64) has changed starting with W2K (before it was called StackWalk—without the 64)! This project only supports the newer Xxx64-funtions. If you need to use it on older systems, you can download the redistributable for NT/W9x.

The latest dbghelp.dll can always be downloaded with the Debugging Tools for Windows. This also contains the symsrv.dll that enables the use of the public Microsoft symbols-server (can be used to retrieve debugging information for system-files; see below).

Using the Code

The usage of the class is very simple. For example, if you want to display the callstack of the current thread, just instantiate a StackWalk object and call the ShowCallstack member:


#include <windows.h>
#include “StackWalker.h”

void Func5() { StackWalker sw; sw.ShowCallstack(); }
void Func4() { Func5(); }
void Func3() { Func4(); }
void Func2() { Func3(); }
void Func1() { Func2(); }

int main()
{
Func1();
return 0;
}

This produces the following output in the debugger-output window:


[…] (output stripped)
d:\privat\Articles\stackwalker\stackwalker.cpp (736):
StackWalker::ShowCallstack
d:\privat\Articles\stackwalker\main.cpp (4): Func5
d:\privat\Articles\stackwalker\main.cpp (5): Func4
d:\privat\Articles\stackwalker\main.cpp (6): Func3
d:\privat\Articles\stackwalker\main.cpp (7): Func2
d:\privat\Articles\stackwalker\main.cpp (8): Func1
d:\privat\Articles\stackwalker\main.cpp (13): main
f:\vs70builds\3077\vc\crtbld\crt\src\crt0.c (259): mainCRTStartup
77E614C7 (kernel32): (filename not available): _BaseProcessStart@4

You can now double-click on a line and the IDE will automatically jump to the desired file/line.

Providing Your Own Output Mechanism

If you want to direct the output in a file or want to use some other output mechanism, you simply need to derive from the StackWalker class. You have two options to do this: only overwrite the OnOutput method or overwrite each OnXxx function. The first solution (OnOutput) is very easy and uses the default implementation of the other OnXxx functions (which should be enough for most cases). To output the the console also, you need to do the following:


class MyStackWalker : public StackWalker
{
public:
MyStackWalker() : StackWalker() {}
protected:
virtual void OnOutput(LPCSTR szText)
{ printf(szText); StackWalker::OnOutput(szText); }
};

Retrieving detailed callstack info

If you want detailed info about the callstack (like loaded modules, addresses, errors, and so forth), you can overwrite the corresponding methods. The following methods are provided:


class StackWalker
{
protected:
virtual void OnSymInit(LPCSTR szSearchPath, DWORD symOptions,
LPCSTR szUserName);
virtual void OnLoadModule(LPCSTR img, LPCSTR mod,
DWORD64 baseAddr, DWORD size,
DWORD result, LPCSTR symType,
LPCSTR pdbName,
ULONGLONG fileVersion);
virtual void OnCallstackEntry(CallstackEntryType eType,
CallstackEntry &entry);
virtual void OnDbgHelpErr(LPCSTR szFuncName, DWORD gle,
DWORD64 addr);
};

These methods are called during the generation of the callstack.

Various kinds of callstacks

In the constructor of the class, you need to specify whether you want to generate callstacks for the current process or for another process. The following constructors are available:


class StackWalker
{
public:
StackWalker(
int options = OptionsAll,
LPCSTR szSymPath = NULL,
DWORD dwProcessId = GetCurrentProcessId(),
HANDLE hProcess = GetCurrentProcess()
);
// Just for other processes with default values for options
// and symPath
StackWalker(
DWORD dwProcessId,
HANDLE hProcess
);
};

To do the actual stack walking, you need to call the following functions:


class StackWalker
{
public:
BOOL ShowCallstack(
HANDLE hThread = GetCurrentThread(),
CONTEXT *context = NULL,
PReadProcessMemoryRoutine readMemoryFunction = NULL,
LPVOID pUserData = NULL
);
};

Dislaying the Callstack of an Exception

With this StackWalker, you also can display the callstack inside an exception handler. You only need to write a filter function that does the stack walking:


// The exception filter function:
LONG WINAPI ExpFilter(EXCEPTION_POINTERS* pExp, DWORD dwExpCode)
{
StackWalker sw;
sw.ShowCallstack(GetCurrentThread(), pExp->ContextRecord);
return EXCEPTION_EXECUTE_HANDLER;
}

// This is how to catch an exception:
__try
{
// do some ugly stuff…
}
__except (ExpFilter(GetExceptionInformation(), GetExceptionCode()))
{
}

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read