Win64异常堆栈跟踪未显示条目。

6

在阅读Win64结构化异常跟踪(来自针对x64异常处理支持编程,第7部分:将其全部组合起来,或构建堆栈遍历例程)时,我转换了代码StackWalk64.cpp

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一个入口,但我期望有四个(三个嵌套异常和一个finally)。可能是我误解了DumpExceptionStack的作用,只有在抛出异常时才会得到我感兴趣的结果吗?如果是这样,那么如何进行必要的更改以获取所有异常堆栈(如果可能)-即对于Main有四个输出?

出于好奇,你在这里尝试构建什么? - David Heffernan
使用C++代码不是更容易吗? - David Heffernan
1个回答

4

x64异常模型是基于表格的,与基于栈的x86模型不同。这意味着异常堆栈不存在。无论如何,我从未见过一个试图包括异常和finally块的堆栈行走例程。这个例程也不例外。它遍历函数调用堆栈。

单个函数内部的异常流由作用域表控制。在您的函数中,如果您的代码在调用时引发异常,则多个作用域表条目匹配异常位置。最内层匹配的范围处理异常。作用域开始和结束地址之间的距离可用于推断最内层的作用域。如果最内层的作用域没有处理异常或重新引发异常,则会请求下一个最内层的作用域处理它。依此类推,直到用于该函数的所有匹配作用域都用完为止。


1
我知道x64异常模型与x86模型是不同的。换个角度来看,操作系统如何知道如果当前的try-except语句块不能处理当前的异常,应该调用哪个异常处理程序……还是说这应该是一个不同的问题? - Nicholas Ring
我认为这可能是一个不同的问题,但我仍然添加了一段话来解决它。 - David Heffernan
@Nicholas 我回答了你的问题吗? - David Heffernan
从我的角度来看,不过从问题来看,是的。因此我会选择这个并提出另一个问题。 - Nicholas Ring
@Nicholas 你是否对异常模型基于表格的含义感到困惑?也许你可以观察一下,在x64中,try代码块不会执行任何操作。与x86不同,x86会将信息推送到堆栈上。 - David Heffernan
这个问题解决了一些问题,但也引发了其他问题(以防止范围扩展)。我已经深入到汇编级别并看到了差异。如果您感兴趣,我的下一个问题在这里:http://stackoverflow.com/questions/14254193/retrieving-all-the-entries-in-the-win64-exception-table - Nicholas Ring

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接