I am using Stackdumps with Win32, to write all return adresses into my logfile. I match these with a mapfile later on (see my article [Post Mortem Debugging][1]).
<
I finally found a reliable way to log the stack frames in x64, using the Windows function CaptureStackBackTrace()
. As I did not want to update my SDK, I call it via GetProcAddress(LoadLibrary());
typedef USHORT (WINAPI *CaptureStackBackTraceType)(__in ULONG, __in ULONG, __out PVOID*, __out_opt PULONG);
CaptureStackBackTraceType func = (CaptureStackBackTraceType)(GetProcAddress(LoadLibrary("kernel32.dll"), "RtlCaptureStackBackTrace"));
if(func == NULL)
return; // WOE 29.SEP.2010
// Quote from Microsoft Documentation:
// ## Windows Server 2003 and Windows XP:
// ## The sum of the FramesToSkip and FramesToCapture parameters must be less than 63.
const int kMaxCallers = 62;
void* callers[kMaxCallers];
int count = (func)(0, kMaxCallers, callers, NULL);
for(i = 0; i < count; i++)
printf(TraceFile, "*** %d called from %016I64LX\n", i, callers[i]);
For vs2008 x64: Based on https://msdn.microsoft.com/en-us/library/windows/desktop/bb204633%28v=vs.85%29.aspx and RED SOFT ADAIR:
#if defined DEBUG_SAMPLES_MANAGEMENT
#include "DbgHelp.h"
#include <WinBase.h>
#pragma comment(lib, "Dbghelp.lib")
void printStack( void* sample_address, std::fstream& out )
{
typedef USHORT (WINAPI *CaptureStackBackTraceType)(__in ULONG, __in ULONG, __out PVOID*, __out_opt PULONG);
CaptureStackBackTraceType func = (CaptureStackBackTraceType)(GetProcAddress(LoadLibrary(L"kernel32.dll"), "RtlCaptureStackBackTrace"));
if(func == NULL)
return; // WOE 29.SEP.2010
// Quote from Microsoft Documentation:
// ## Windows Server 2003 and Windows XP:
// ## The sum of the FramesToSkip and FramesToCapture parameters must be less than 63.
const int kMaxCallers = 62;
void * callers_stack[ kMaxCallers ];
unsigned short frames;
SYMBOL_INFO * symbol;
HANDLE process;
process = GetCurrentProcess();
SymInitialize( process, NULL, TRUE );
frames = (func)( 0, kMaxCallers, callers_stack, NULL );
symbol = ( SYMBOL_INFO * )calloc( sizeof( SYMBOL_INFO ) + 256 * sizeof( char ), 1 );
symbol->MaxNameLen = 255;
symbol->SizeOfStruct = sizeof( SYMBOL_INFO );
out << "(" << sample_address << "): " << std::endl;
const unsigned short MAX_CALLERS_SHOWN = 6;
frames = frames < MAX_CALLERS_SHOWN? frames : MAX_CALLERS_SHOWN;
for( unsigned int i = 0; i < frames; i++ )
{
SymFromAddr( process, ( DWORD64 )( callers_stack[ i ] ), 0, symbol );
out << "*** " << i << ": " << callers_stack[i] << " " << symbol->Name << " - 0x" << symbol->Address << std::endl;
}
free( symbol );
}
#endif
Called here:
#if defined DEBUG_SAMPLES_MANAGEMENT
if(owner_ != 0)
{
std::fstream& out = owner_->get_debug_file();
printStack( this, out );
}
#endif
In Trial 3, you may be using CaptureStackBackTrace() incorrectly. According to the documentation, on Windows XP and Windows Server 2003, the sum of the first and second parameters must be less than 63, but, in your case, the sum would be 128.
http://msdn.microsoft.com/en-us/library/windows/desktop/bb204633%28v=vs.85%29.aspx
When using StackWalk64
you are iterating through the thread's entire stack whether there is valid data or not. Once you hit a return address of 0 you should terminate the walk, like so:
for (ULONG Frame = 0; ; Frame++)
{
if (FALSE == StackWalk64(...))
{
printf("Stack walk failed!\n");
break;
}
if (stackFrame.AddrPC.Offset == 0)
{
printf("Stack walk complete!\n");
break;
}
do_something();
}
StackWalk64 is the right choice, the first call will give you the caller's address.
Your problem might be that in release you have a lot of inlining going on. The return address may not be what you expect.
edit : you only need to set AddrPC and AddrFrame. Just make sure that your rbp and rip are the ones corresponding to your callee context.
Disassembling RtlCaptureStackBackTrace() I noticed that maximum value passed to RtlCaptureStackBackTrace() should be: framesToSkip+framesToCapture+1 should be less than 64. otherwise it returns 0 and there is no other error codes.