Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
333 views
in Technique[技术] by (71.8m points)

c++ - How to Log Stack Frames with Windows x64

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]).

EDIT:: Problem solved - see my own answer below.

With Windows x64 i do not find a reliable way to write only the return adresses into the logfile. I tried several ways:

Trial 1: Pointer Arithmetic:

   CONTEXT Context;
   RtlCaptureContext(&Context);
   char *eNextBP  = (char *)Context.Rdi;
   for(ULONG Frame = 0; eNextBP ; Frame++)
   {        
       char *pBP = eNextBP;
       eNextBP = *(char **)pBP; // Next BP in Stack
       fprintf(LogFile, "*** %2d called from %016LX  (pBP at %016LX)
", Frame, 
              (ULONG64)*(char **)(pBP + 8), (ULONG64)pBP);

    }

This works fine in the debug version - but it crashes in the release version. The value of Context.Rdi has no usable value there. I did check for differences in the compiler settings (visual Studio 2005). I have not found anything suspicious.

Trial 2: Using StackWalk64

RtlCaptureContext(&Context);
STACKFRAME64 stk;
memset(&stk, 0, sizeof(stk));

stk.AddrPC.Offset       = Context.Rip;
stk.AddrPC.Mode         = AddrModeFlat;
stk.AddrStack.Offset    = Context.Rsp;
stk.AddrStack.Mode      = AddrModeFlat;
stk.AddrFrame.Offset    = Context.Rbp;
stk.AddrFrame.Mode      = AddrModeFlat;


for(ULONG Frame = 0; ; Frame++)
{
    BOOL result = StackWalk64(
                            IMAGE_FILE_MACHINE_AMD64,   // __in      DWORD MachineType,
                            GetCurrentProcess(),        // __in      HANDLE hProcess,
                            GetCurrentThread(),         // __in      HANDLE hThread,
                            &stk,                       // __inout   LP STACKFRAME64 StackFrame,
                            &Context,                  // __inout   PVOID ContextRecord,
                             NULL,                     // __in_opt  PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryRoutine,
                             SymFunctionTableAccess64,                      // __in_opt  PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccessRoutine,
                             SymGetModuleBase64,                     // __in_opt  PGET_MODULE_BASE_ROUTINE64 GetModuleBaseRoutine,
                             NULL                       // __in_opt  PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress
                             );

    fprintf(gApplSetup.TraceFile, "*** %2d called from %016LX   STACK %016LX    FRAME %016LX
", Frame, (ULONG64)stk.AddrPC.Offset, (ULONG64)stk.AddrStack.Offset, (ULONG64)stk.AddrFrame.Offset);
    if(! result)
        break;
}

This does not only dump the return addresses, but the WHOLE STACK. I receive about 1000 lines in my log file using this approach. I can use this, but i have to search trough the lines and some data of the stacks happens to be a valid code address.

Trial 3: Using Backtrace

static USHORT (WINAPI
*s_pfnCaptureStackBackTrace)(ULONG, ULONG, PVOID*, PULONG) = 0;  
    if (s_pfnCaptureStackBackTrace == 0)  
    {  
        const HMODULE hNtDll = ::GetModuleHandle("ntdll.dll");  
        reinterpret_cast<void*&>(s_pfnCaptureStackBackTrace)
=  ::GetProcAddress(hNtDll, "RtlCaptureStackBackTrace");  
    }  
    PVOID myFrames[128];
    s_pfnCaptureStackBackTrace(0, 128, myFrames, NULL);

    for(int ndx = 0; ndx < 128; ndx++)
        fprintf(gApplSetup.TraceFile, "*** BackTrace %3d %016LX
", ndx,  (ULONG64)myFrames[ndx]);

Results in no usable information.

Has anyone implemented such a stack walk in x64 that does only write out the return adresses in the stack? Ive seen the approaches [StackTrace64][2], [StackWalker][3] and other ones. They either do not compile or they are much too much comlicated. It basically is a simple task!

Sample StackDump64.cpp

#include <Windows.h>
#include <DbgHelp.h>
#include <Winbase.h>

#include <stdio.h>

void WriteStackDump()

{

    FILE *myFile = fopen("StackDump64.log", "w+t");

    CONTEXT                       Context;
    memset(&Context, 0, sizeof(Context));
    RtlCaptureContext(&Context);

    RtlCaptureContext(&Context);
    STACKFRAME64 stk;
    memset(&stk, 0, sizeof(stk));

    stk.AddrPC.Offset       = Context.Rip;
    stk.AddrPC.Mode         = AddrModeFlat;
    stk.AddrStack.Offset    = Context.Rsp;
    stk.AddrStack.Mode      = AddrModeFlat;
    stk.AddrFrame.Offset    = Context.Rbp;
    stk.AddrFrame.Mode      = AddrModeFlat;


    for(ULONG Frame = 0; ; Frame++)
    {
        BOOL result = StackWalk64(
                                IMAGE_FILE_MACHINE_AMD64,   // __in      DWORD MachineType,
                                GetCurrentProcess(),        // __in      HANDLE hProcess,
                                GetCurrentThread(),         // __in      HANDLE hThread,
                                &stk,                       // __inout   LP STACKFRAME64 StackFrame,
                                &Context,                  // __inout   PVOID ContextRecord,
                                 NULL,                     // __in_opt  PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryRoutine,
                                 SymFunctionTableAccess64,                      // __in_opt  PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccessRoutine,
                                 SymGetModuleBase64,                     // __in_opt  PGET_MODULE_BASE_ROUTINE64 GetModuleBaseRoutine,
                                 NULL                       // __in_opt  PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress
                                 );

        fprintf(myFile, "*** %2d called from %016I64LX   STACK %016I64LX    AddrReturn %016I64LX
", Frame, stk.AddrPC.Offset, stk.AddrStack.Offset, stk.AddrReturn.Offset);
        if(! result)
            break;
    }

    fclose(myFile);
}


void funcC()
{
    WriteStackDump();
}


void funcB()
{
    funcC();
}


void funcA()

{
    funcB();
}


int main(int argc, char *argv[])

{

    funcA();
}

Running this sample results in the follwing log file content:

***  0 called from 000000014000109E   STACK 000000000012F780    AddrReturn 0000000140005798
***  1 called from 000000001033D160   STACK 000000000012F788    AddrReturn 00000001400057B0
***  2 called from 00000001400057B0   STACK 000000000012F790    AddrReturn 0000000000000001
***  3 called from 0000000000000002   STACK 000000000012F798    AddrReturn 00000001400057B0
***  4 called from 0000000000000002   STACK 000000000012F7A0    AddrReturn 000000000012F7F0
***  5 called from 000000000012F7F0   STACK 000000000012F7A8    AddrReturn 0000000000000000
***  6 called from 0000000000000000   STACK 000000000012F7B0    AddrReturn 000007FF7250CF40
***  7 called from 000007FF7250CF40   STACK 000000000012F7B8    AddrReturn 000007FF7250D390
***  8 called from 000007FF7250D390   STACK 000000000012F7C0    AddrReturn 000007FF725B6950
***  9 called from 000007FF725B6950   STACK 000000000012F7C8    AddrReturn CCCCCCCCCCCCCCCC
*** 10 called from CCCCCCCCCCCCCCCC   STACK 000000000012F7D0    AddrReturn 000000001033D160
*** 11 called from 000000001033D160   STACK 000000000012F7D8    AddrReturn CCCCCCCCCCCCCCCC
*** 12 called from CCCCCCCCCCCCCCCC   STACK 000000000012F7E0    AddrReturn CCCCCCCCCCCCCCCC
*** 13 called from CCCCCCCCCCCCCCCC   STACK 000000000012F7E8    AddrReturn CCCCCCCCCCCCCCCC
*** 14 called from CCCCCCCCCCCCCCCC   STACK 000000000012F7F0    AddrReturn 0000000000000000
*** 15 called from 0000000000000000   STACK 000000000012F7F8    AddrReturn 0000000000000000
*** 16 called from 0000000000000000   STACK 000000000012F800    AddrReturn 0000000000000000
*** 17 called from 0000000000000000   STACK 000000000012F808    AddrReturn 0000000000000000
*** 18 called from 0000000000000000   STACK 000000000012F810    AddrReturn 0000000000000000
*** 19 called from 0000000000000000   STACK 000000000012F818    AddrReturn 0000000000000000
*** 20 called from 0000000000000000   STACK 000000000012F820    AddrReturn 00001F800010000F
*** 21 called from 00001F800010000F   STACK 000000000012F828    AddrReturn 0053002B002B0033
*** 22 called from 0053002B002B0033   STACK 000000000012F830    AddrReturn 00000206002B002B
*** 23 called from 00000206002B002B   STACK 000000000012F838    AddrReturn 0000000000000000
*** 24 called from 0000000000000000   STACK 000000000012F840    AddrReturn 0000000000000000
*** 25 called from 0000000000000000   STACK 000000000012F848    AddrReturn 0000000000000000
*** 26 called from 0000000000000000   STACK 000000000012F850    AddrReturn 0000000000000000
*** 27 called from 0000000000000000   STACK 000000000012F858    AddrReturn 0000000000000000
*** 28 called from 0000000000000000   STACK 000000000012F860    AddrReturn 0000000000000000
*** 29 called from 0000000000000000   STACK 000000000012F868    AddrReturn 0000000000000246
*** 30 called from 0000000000000246   STACK 000000000012F870    AddrReturn 000000000012F7F0
*** 31 called from 000000000012F7F0   STACK 000000000012F878    AddrReturn 0000000000000000
*** 32 called from 0000000000000000   STACK 000000000012F880    AddrReturn 0000000000000000
*** 33 called from 0000000000000000   STACK 000000000012F888    AddrReturn 000000000012F888
*** 34 called from 000000000012F888   STACK 000000000012F890    AddrReturn 0000000000000000
*** 35 called from 0000000000000000   STACK 000000000012F898    AddrReturn 0000000000000000
*** 36 called from 0000000000000000   STACK 000000000012F8A0    AddrReturn 000000000012FE10
*** 37 called from 000000000012FE10   STACK 000000000012F8A8    AddrReturn 0000000000000000
*** 38 called from 0000000000000000   STACK 000000000012F8B0    AddrReturn 0000000000000000
*** 39 called from 0000000000000000   STACK 000000000012F8B8    AddrReturn 0000000000000000
*** 40 called from 0000000000000000   STACK 000000000012F8C0    AddrReturn 0000000000000246
*** 41 called from 0000000000000246   STACK 000000000012F8C8    AddrReturn 0000000000000000
*** 42 called from 0000000000000000   STACK 000000000012F8D0    AddrReturn 0000000000000000
*** 43 called from 0000000000000000   STACK 000000000012F8D8    AddrReturn 0000000000000000
*** 44 called from 0000000000000000   STACK 000000000012F8E0    AddrReturn 0000000000000000
*** 45 called from 0000000000000000   STACK 000000000012F8E8    AddrReturn 0000000000000000
*** 46 called from 0000000000000000   STACK 000000000012F8F0    AddrReturn 000000000000027F
*** 47 called from 000000000000027F   STACK 000000000012F8F8    AddrReturn 0000000000000000
*** 48 called from 0000000000000000   STACK 000000000012F900    AddrReturn 0000000000000000
*** 49 called from 0000000000000000   STACK 000000000012F908    AddrReturn 0000FFFF00001F80
*** 50 called from 0000FFFF00001F80   STACK 000000000012F910    AddrReturn 0000000000000000
*** 51 called from 0000000000000000   STACK 000000000012F918    AddrReturn 0000000000000000
*** 52 called from 0000000000000000   STACK 000000000012F920    AddrReturn 0000000000000000
*** 53 called from 0000000000000000   STACK 000000000012F928    AddrReturn 0000000000000000
*** 54 called from 0000000000000000   STACK 000000000012F930    AddrReturn 0000000000000000
*** 55 called from 0000000000000000   STACK 000000000012F938    AddrReturn 0000000000000000
*** 56 called from 0000000000000000   STACK 000000000012F940    AddrReturn 0000000000000000
*** 57 called from 0000000000000000   STACK 000000000012F948    AddrReturn 0000000000000000
*** 58 called from 0000000000000000   STACK 000000000012F950    AddrRet

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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
", i, callers[i]);

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...