2013-01-06 25 views
4

在Win64結構化異常跟蹤(從Programming against the x64 exception handling support, part 7: Putting it all together, or building a stack walk routine)讀取時,我轉換了代碼StackWalk64.cppWin64異常堆棧走不顯示條目

procedure DumpExceptionStack(); 
var 
    LContext : CONTEXT; 
    LUnwindHistoryTable : _UNWIND_HISTORY_TABLE; 
    LRuntimeFunction : Pointer; 
    LImageBase : ULONGLONG; 
    HandlerData : Pointer; 
    EstablisherFrame : ULONG64; 
    NvContext : KNONVOLATILE_CONTEXT_POINTERS; 

    LLineNumber     : integer; 
    LModuleName     : UnicodeString; 
    LPublicAddr     : pointer; 
    LPublicName     : UnicodeString; 
    LUnitName      : UnicodeString; 
begin 
    // 
    // First, we'll get the caller's context. 
    // 
    RtlCaptureContext(LContext); 

    // 
    // Initialize the (optional) unwind history table. 
    // 
    LUnwindHistoryTable := Default(_UNWIND_HISTORY_TABLE); 

    // LUnwindHistoryTable.Unwind := True; 

    // 
    // This unwind loop intentionally skips the first call frame, as it shall 
    // correspond to the call to StackTrace64, which we aren't interested in. 
    // 
    repeat 
     // 
     // Try to look up unwind metadata for the current function. 
     // 
     LRuntimeFunction := RtlLookupFunctionEntry(LContext.Rip, 
               LImageBase, 
               LUnwindHistoryTable); 

    NvContext := Default(KNONVOLATILE_CONTEXT_POINTERS); 

    if not Assigned(LRuntimeFunction) then 
    begin 
      // 
      // If we don't have a RUNTIME_FUNCTION, then we've encountered 
      // a leaf function. Adjust the stack approprately. 
      // 

     //LContext.Rip := (ULONG64)(*(PULONG64)Context.Rsp); 
     LContext.Rip := ULONG64(Pointer(LContext.Rsp)^); 
      LContext.Rsp := LContext.Rsp + 8; 
    end 
    else 
    begin 
      // 
      // Otherwise, call upon RtlVirtualUnwind to execute the unwind for 
      // us. 
      // 
      RtlVirtualUnwind(UNW_FLAG_NHANDLER, 
         LImageBase, 
         LContext.Rip, 
         LRuntimeFunction, 
         LContext, 
         HandlerData, 
         EstablisherFrame, 
         NvContext); 
    end; 

     // 
     // If we reach an RIP of zero, this means that we've walked off the end 
     // of the call stack and are done. 
     // 
    if LContext.Rip = 0 then 
     Break; 

     // 
     // Display the context. Note that we don't bother showing the XMM 
     // context, although we have the nonvolatile portion of it. 
     // 
    if madMapFile.GetMapFileInfos(Pointer(LContext.Rip), 
            LModuleName, 
            LUnitName, 
            LPublicName, 
            LPublicAddr, 
            LLineNumber) then 
    begin 
     Writeln(Format('%p %s.%s %d', [Pointer(LContext.Rip), LUnitName, LPublicName, LLineNumber{, LSEHType}])); 
    end; 
    until LContext.Rip = 0; 
end; 

然後我把它改爲:

procedure Main(); 
begin 
    try 
    try 
     try 
     try 
      DumpExceptionStack(); 
     finally 
      // 
     end; 
     except 
     on E : Exception do 
     raise 
     end; 
    except 
     on E : Exception do 
     raise 
    end; 
    except 
    on E : Exception do 
    raise 
    end; 
end; 

當我運行應用程序(只是一個控制檯應用程序),我只得到一個條目Main,但我期待有是四(三個嵌套異常,最後一個)。

難道是我誤解了,並且DumpExceptionStack只會給出我在引發異常時感興趣的結果嗎?如果是這樣的話,那麼需要進行的修改將會得到所有的異常堆棧(如果可能的話) - 也就是說。有四個輸出爲Main

+0

出於好奇,在這裏試圖建立什麼? –

+0

@大衛:理解。 –

+0

使用C++代碼不會更容易嗎? –

回答

3

與基於堆棧的x86模型相反,x64異常模型是基於表的。這意味着異常堆棧不存在。無論如何,我從來沒有見過一個試圖包含異常並最終阻止的stalk walk例程。這個沒什麼不同。它遍歷函數調用堆棧。

單個函數內的異常流程由範圍表控制。在您的函數中,如果您的代碼在調用DumpExceptionStack的位置發起異常,則多個範圍表條目與異常位置匹配。該異常由最內層的匹配範圍處理。範圍的開始和結束地址之間的距離可以用來推斷哪個範圍是最內層的。如果最內層的範圍沒有處理異常,或者重新引發異常,那麼會要求下一個最內層的範圍處理它。依此類推,直到該函數的所有匹配範圍都用盡。

+1

我知道x64異常模型與x86模型不同。爲了解決這個問題,操作系統如何知道,如果當前的try-except子句不處理當前異常,哪個異常處理程序要調用...或者這應該是一個不同的問題? –

+0

我認爲這可能是一個不同的問題,但我添加了一個段落來解決它。 –

+0

@Nicholas我回答了這個問題嗎? –