Click to See Complete Forum and Search --> : Using StackWalk


gradyc
May 4th, 1999, 05:28 PM
Does anyone know how to use the StackWalk function? The documentation is not that clear. A short example would be greatly appreciated.

Thanks!

Chuck Grady

MPSullivan
May 4th, 1999, 06:36 PM
Here's an example that I cobbled together from various sources. It's a function that you can call to find the symbolic name of the function that called the one your calling from. I hope that the wordwrap doesn't mess it up too badly.

//****************************************************************************
// FILE : ShowStack.C
// DATE: 03/29/99
// AUTHOR : Michael Sullivan (Z92943)
//
// PURPOSE: This file implements a stack trace using the ImageHlp dll.
// The one function GetCallingFunctionName will return the text name
// of the function that called the function calling GetCallingFunctionName.
// ie Function A calls Function B.
// Function B needs the text name "Function A" so
// Function B calls GetCallingFunctionName which returns
// "Function A"
// PARAMETERS: OUT CString callingFunctionName
//
//****************************************************************************

#include "stdafx.h"
#include <windows.h>
#include <imagehlp.h> // link with imagehlp.lib as well...
#include <stdio.h>
#include <stdlib.h>
#include <winerror.h>
// SymCleanup()
typedef BOOL (__stdcall *tSC)( IN HANDLE hProcess );
tSC pSC = NULL;

// SymFunctionTableAccess()
typedef PVOID (__stdcall *tSFTA)( HANDLE hProcess, DWORD AddrBase );
tSFTA pSFTA = NULL;

#ifdef NT50
// SymGetLineFromAddr()
typedef BOOL (__stdcall *tSGLFA)( IN HANDLE hProcess, IN DWORD dwAddr,
OUT PDWORD pdwDisplacement, OUT PIMAGEHLP_LINE Line );
tSGLFA pSGLFA = NULL;
#endif

// SymGetModuleBase()
typedef DWORD (__stdcall *tSGMB)( IN HANDLE hProcess, IN DWORD dwAddr );
tSGMB pSGMB = NULL;

// SymGetModuleInfo()
typedef BOOL (__stdcall *tSGMI)( IN HANDLE hProcess, IN DWORD dwAddr, OUT PIMAGEHLP_MODULE ModuleInfo );
tSGMI pSGMI = NULL;

// SymGetOptions()
typedef DWORD (__stdcall *tSGO)( VOID );
tSGO pSGO = NULL;

// SymGetSymFromAddr()
typedef BOOL (__stdcall *tSGSFA)( IN HANDLE hProcess, IN DWORD dwAddr,
OUT PDWORD pdwDisplacement, OUT PIMAGEHLP_SYMBOL Symbol );
tSGSFA pSGSFA = NULL;

// SymInitialize()
typedef BOOL (__stdcall *tSI)( IN HANDLE hProcess, IN PSTR UserSearchPath, IN BOOL fInvadeProcess );
tSI pSI = NULL;

// SymSetOptions()
typedef DWORD (__stdcall *tSSO)( IN DWORD SymOptions );
tSSO pSSO = NULL;

// StackWalk()
typedef BOOL (__stdcall *tSW)( DWORD MachineType, HANDLE hProcess,
HANDLE hThread, LPSTACKFRAME StackFrame, PVOID ContextRecord,
PREAD_PROCESS_MEMORY_ROUTINE ReadMemoryRoutine,
PFUNCTION_TABLE_ACCESS_ROUTINE FunctionTableAccessRoutine,
PGET_MODULE_BASE_ROUTINE GetModuleBaseRoutine,
PTRANSLATE_ADDRESS_ROUTINE TranslateAddress );
tSW pSW = NULL;

// UnDecorateSymbolName()
typedef DWORD (__stdcall WINAPI *tUDSN)( PCSTR DecoratedName, PSTR UnDecoratedName,
DWORD UndecoratedLength, DWORD Flags );
tUDSN pUDSN = NULL;

#define gle GetLastError()


#define sizeof_Name 128
#define sizeof_CONTEXT sizeof(CONTEXT)+96
#define sizeof_STACKFRAME sizeof(STACKFRAME)+16
#define sizeof_symbol sizeof(IMAGEHLP_SYMBOL)+sizeof_Name


int GetCallingFunctionName(CString &callingFunctionName)
{
HANDLE hProc, hThread;
CONTEXT *cxt;
IMAGEHLP_SYMBOL *sym;
STACKFRAME *frm;
DWORD machType, symDisp, lastErr, filepathlen;
BOOL stat;
int i;
char filepath[MAX_PATH], *lastdir, *pPath;

HINSTANCE hImagehlpDll = NULL;

// we load imagehlp.dll dynamically because the NT4-version does not
// offer all the functions that are in the NT5 lib
hImagehlpDll = LoadLibrary( "imagehlp.dll" );
if ( hImagehlpDll == NULL )
{
printf( "LoadLibrary( \"imagehlp.dll\" ): gle = %lu\n", gle );
return 0;
}

pSC = (tSC) GetProcAddress( hImagehlpDll, "SymCleanup" );
pSFTA = (tSFTA) GetProcAddress( hImagehlpDll, "SymFunctionTableAccess" );
pSGMB = (tSGMB) GetProcAddress( hImagehlpDll, "SymGetModuleBase" );
pSGMI = (tSGMI) GetProcAddress( hImagehlpDll, "SymGetModuleInfo" );
pSGO = (tSGO) GetProcAddress( hImagehlpDll, "SymGetOptions" );
pSGSFA = (tSGSFA) GetProcAddress( hImagehlpDll, "SymGetSymFromAddr" );
pSI = (tSI) GetProcAddress( hImagehlpDll, "SymInitialize" );
pSSO = (tSSO) GetProcAddress( hImagehlpDll, "SymSetOptions" );
pSW = (tSW) GetProcAddress( hImagehlpDll, "StackWalk" );
pUDSN = (tUDSN) GetProcAddress( hImagehlpDll, "UnDecorateSymbolName" );

if ( pSC == NULL || pSFTA == NULL || pSGMB == NULL || pSGMI == NULL ||
pSGO == NULL || pSGSFA == NULL || pSI == NULL || pSSO == NULL ||
pSW == NULL || pUDSN == NULL )
{
puts( "GetProcAddress(): some required function not found." );
FreeLibrary( hImagehlpDll );
}



// Initialize the IMAGEHLP package to decode addresses to symbols
//

// Get image filename of the main executable

filepathlen = GetModuleFileName ( NULL, filepath, sizeof(filepath));
if (filepathlen == 0)
printf ("NtStackTrace: Failed to get pathname for program\n");

// Strip the filename, leaving the path to the executable

lastdir = strrchr (filepath, '/');
if (lastdir == NULL)
lastdir = strrchr (filepath, '\\');
if (lastdir != NULL)
lastdir[0] = '\0';

// Initialize the symbol table routines, supplying a pointer to the path

pPath = filepath;
if (strlen (filepath) == 0)
pPath = NULL;

hProc = GetCurrentProcess ();
hThread = GetCurrentThread ();
if ( !pSI(hProc, pPath, TRUE) )
printf ("NtStackTrace: failed to initialize symbols\n");

// Allocate and initialize frame and symbol structures

frm = (STACKFRAME*)malloc (sizeof_STACKFRAME);
memset (frm, 0, sizeof(STACKFRAME));

sym = (IMAGEHLP_SYMBOL*)malloc (sizeof_symbol);
memset (sym, 0, sizeof_symbol);
sym->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL);
sym->MaxNameLength = sizeof_Name-1;


// Initialize the starting point based on the architecture of the current machine

machType = IMAGE_FILE_MACHINE_I386;

// The CONTEXT structure is not used on x86 systems

cxt = NULL;

// Initialize the STACKFRAME to describe the current routine

frm->AddrPC.Mode = AddrModeFlat;
frm->AddrStack.Mode = AddrModeFlat;
frm->AddrFrame.Mode = AddrModeFlat;

// If we were called from an exception handler, the exception
// structure would contain an embedded CONTEXT structure. We
// could initialize the following addresses from the CONTEXT
// registers passed to us.

// For this example, use _asm to fetch the processor register values

_asm mov i, esp // Stack pointer (CONTEXT .Esp field)
frm->AddrStack.Offset = i;

_asm mov i, ebp // Frame pointer (CONTEXT .Ebp field)
frm->AddrFrame.Offset = i;

// We'd like to fetch the current instruction pointer, but the x86 IP
// register is a bit special. Use roughly the current offset instead
// of a dynamic fetch (use offset because address should be past the prologue).

// _asm mov i, ip // ip is a special register, this is illegal
// frm->AddrPC.Offset = i;

frm->AddrPC.Offset = ((DWORD) &GetCallingFunctionName) + 0x08c;



// The top stack frame is the call to this routine itself -
// the next is the routine that called us, and the third is
// the one that called our caller. That's the one we want to
// return.


for (i=0; i<3; i++)
{

// Call the routine to trace to the next frame

stat = pSW( machType, hProc, hThread, frm, cxt, NULL, pSFTA, pSGMB, NULL );
if ( !stat )
{
lastErr = GetLastError ();
if (lastErr == ERROR_NOACCESS | lastErr == ERROR_INVALID_ADDRESS)
printf (" <done>\n"); // Normal end-of-stack code
else
printf (" <stack walk terminated with error %d>\n",
lastErr);
break;
}
}

// Decode the closest routine symbol name

if ( pSGSFA( hProc, frm->AddrPC.Offset, &symDisp, sym ) )
callingFunctionName = sym->Name;
else
{
lastErr = GetLastError ();
if (lastErr == ERROR_INVALID_ADDRESS) // Seems normal for last frame on Intel
callingFunctionName = "<No Symbol Available>";
else
callingFunctionName.Format("<no symbol available - error %d>",lastErr);
}



free (cxt); // If on Intel, freeing the NULL CONTEXT is a no-op...
free (frm);
free (sym);

return 0;
}

Sang_kd2
October 23rd, 2009, 02:56 AM
Calling SatckWalk is not working for release mode. I think this is because release mode does not have the pdb(symbol files). Correct me if wrong.
Also let know how can we get the stack trace in release mode in when excpetion is thrown.

Thanks in advance for ur help....

Regards
Sang

Krishnaa
October 23rd, 2009, 04:13 AM
Well it would only not show the function names, but it will work for release mode too.