[edk2] [RFC v5 1/8] UefiCpuPkg/CpuExceptionHandlerLib/X64: Add stack trace support

Paulo Alcantara posted 8 patches 6 years, 11 months ago
[edk2] [RFC v5 1/8] UefiCpuPkg/CpuExceptionHandlerLib/X64: Add stack trace support
Posted by Paulo Alcantara 6 years, 11 months ago
This patch adds stack trace support during a X64 CPU exception.

It will dump out back trace, stack contents as well as image module
names that were part of the call stack.

Contributed-under: TianoCore Contribution Agreement 1.1
Cc: Eric Dong <eric.dong@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Signed-off-by: Paulo Alcantara <paulo@paulo.ac>
---
 UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c | 401 +++++++++++++++++++-
 1 file changed, 393 insertions(+), 8 deletions(-)

diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c b/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
index 56180f4c17..4db9f6465e 100644
--- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
+++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
@@ -14,6 +14,11 @@
 
 #include "CpuExceptionCommon.h"
 
+//
+// Unknown PDB file name
+//
+GLOBAL_REMOVE_IF_UNREFERENCED CONST CHAR8 *mUnknownPdbFileName = "????";
+
 /**
   Return address map of exception handler template so that C code can generate
   exception tables.
@@ -399,20 +404,281 @@ DumpCpuContext (
 }
 
 /**
-  Display CPU information.
+  Get absolute path and file name of PDB file in PE/COFF image.
 
-  @param ExceptionType  Exception type.
-  @param SystemContext  Pointer to EFI_SYSTEM_CONTEXT.
+  @param[in]  ImageBase            Base address of PE/COFF image.
+  @param[out] PdbAbsoluteFilePath  Absolute path of PDB file.
+  @param[out] PdbFileName          File name of PDB file.
 **/
+STATIC
 VOID
-DumpImageAndCpuContent (
+GetPdbFileName (
+  IN  UINTN    ImageBase,
+  OUT CHAR8    **PdbAbsoluteFilePath,
+  OUT CHAR8    **PdbFileName
+  )
+{
+  VOID   *PdbPointer;
+  CHAR8  *Str;
+
+  //
+  // Get PDB file name from PE/COFF image
+  //
+  PdbPointer = PeCoffLoaderGetPdbPointer ((VOID *)ImageBase);
+  if (PdbPointer == NULL) {
+    //
+    // No PDB file name found. Set it to an unknown file name.
+    //
+    *PdbFileName = (CHAR8 *)mUnknownPdbFileName;
+    if (PdbAbsoluteFilePath != NULL) {
+      *PdbAbsoluteFilePath = NULL;
+    }
+  } else {
+    //
+    // Get file name portion out of PDB file in PE/COFF image
+    //
+    Str = (CHAR8 *)((UINTN)PdbPointer +
+                    AsciiStrLen ((CHAR8 *)PdbPointer) - sizeof *Str);
+    for (; *Str != '/' && *Str != '\\'; Str--) {
+      ;
+    }
+
+    //
+    // Set PDB file name (also skip trailing path separator: '/' or '\\')
+    //
+    *PdbFileName = Str + 1;
+
+    if (PdbAbsoluteFilePath != NULL) {
+      //
+      // Set absolute file path of PDB file
+      //
+      *PdbAbsoluteFilePath = PdbPointer;
+    }
+  }
+}
+
+/**
+  Dump stack contents.
+
+  @param[in]  CurrentRsp         Current stack pointer address.
+  @param[in]  UnwoundStacksCount  Count of unwound stack frames.
+**/
+STATIC
+VOID
+DumpStackContents (
+  IN UINT64  CurrentRsp,
+  IN INTN    UnwoundStacksCount
+  )
+{
+  //
+  // Check for proper stack pointer alignment
+  //
+  if (((UINTN)CurrentRsp & (CPU_STACK_ALIGNMENT - 1)) != 0) {
+    InternalPrintMessage ("!!!! Unaligned stack pointer. !!!!\n");
+    return;
+  }
+
+  //
+  // Dump out stack contents
+  //
+  InternalPrintMessage ("\nStack dump:\n");
+  while (UnwoundStacksCount-- > 0) {
+    InternalPrintMessage (
+      "0x%016lx: %016lx %016lx\n",
+      CurrentRsp,
+      *(UINT64 *)CurrentRsp,
+      *(UINT64 *)((UINTN)CurrentRsp + 8)
+      );
+
+    //
+    // Point to next stack
+    //
+    CurrentRsp += CPU_STACK_ALIGNMENT;
+  }
+}
+
+/**
+  Dump all image module names from call stack.
+
+  @param[in]  ExceptionType  Exception type.
+  @param[in]  SystemContext  Pointer to EFI_SYSTEM_CONTEXT.
+**/
+STATIC
+VOID
+DumpImageModuleNames (
   IN EFI_EXCEPTION_TYPE   ExceptionType,
   IN EFI_SYSTEM_CONTEXT   SystemContext
   )
 {
-  DumpCpuContext (ExceptionType, SystemContext);
+  EFI_STATUS  Status;
+  UINT64      Rip;
+  UINTN       ImageBase;
+  VOID        *EntryPoint;
+  CHAR8       *PdbAbsoluteFilePath;
+  CHAR8       *PdbFileName;
+  UINT64      Rbp;
+  UINTN       LastImageBase;
+
+  //
+  // Set current RIP address
+  //
+  if ((ExceptionType == EXCEPT_IA32_PAGE_FAULT) &&
+      ((SystemContext.SystemContextX64->ExceptionData & IA32_PF_EC_ID) != 0)) {
+    //
+    // The RIP in SystemContext could not be used
+    // if it is page fault with I/D set.
+    //
+    Rip = *(UINT64 *)(UINTN)SystemContext.SystemContextX64->Rsp;
+  } else {
+    Rip = SystemContext.SystemContextX64->Rip;
+  }
+
+  //
+  // Set current frame pointer address
+  //
+  Rbp = SystemContext.SystemContextX64->Rbp;
+
+  //
+  // Check for proper frame pointer alignment
+  //
+  if (((UINTN)Rbp & (CPU_STACK_ALIGNMENT - 1)) != 0) {
+    InternalPrintMessage ("!!!! Unaligned frame pointer. !!!!\n");
+    return;
+  }
+
+  //
+  // Get initial PE/COFF image base address from current RIP
+  //
+  ImageBase = PeCoffSearchImageBase (Rip);
+  if (ImageBase == 0) {
+    InternalPrintMessage ("!!!! Could not find image module names. !!!!");
+    return;
+  }
+
+  //
+  // Set last PE/COFF image base address
+  //
+  LastImageBase = ImageBase;
+
+  //
+  // Get initial PE/COFF image's entry point
+  //
+  Status = PeCoffLoaderGetEntryPoint ((VOID *)ImageBase, &EntryPoint);
+  if (EFI_ERROR (Status)) {
+    EntryPoint = NULL;
+  }
+
+  //
+  // Get file name and absolute path of initial PDB file
+  //
+  GetPdbFileName (ImageBase, &PdbAbsoluteFilePath, &PdbFileName);
+
+  //
+  // Print out initial image module name (if any)
+  //
+  if (PdbAbsoluteFilePath != NULL) {
+    InternalPrintMessage (
+      "\n%a (ImageBase=0x%016lx, EntryPoint=0x%016lx):\n",
+      PdbFileName,
+      ImageBase,
+      (UINTN)EntryPoint
+      );
+    InternalPrintMessage ("%a\n", PdbAbsoluteFilePath);
+  }
+
+  //
+  // Walk through call stack and find next module names
+  //
+  for (;;) {
+    //
+    // Set RIP with return address from current stack frame
+    //
+    Rip = *(UINT64 *)((UINTN)Rbp + 8);
+
+    //
+    // If RIP is zero, then stop unwinding the stack
+    //
+    if (Rip == 0) {
+      break;
+    }
+
+    //
+    // Search for the respective PE/COFF image based on RIP
+    //
+    ImageBase = PeCoffSearchImageBase (Rip);
+    if (ImageBase == 0) {
+      //
+      // Stop stack trace
+      //
+      break;
+    }
+
+    //
+    // If RIP points to another PE/COFF image, then find its respective PDB file
+    // name.
+    //
+    if (LastImageBase != ImageBase) {
+      //
+      // Get PE/COFF image's entry point
+      //
+      Status = PeCoffLoaderGetEntryPoint ((VOID *)ImageBase, &EntryPoint);
+      if (EFI_ERROR (Status)) {
+        EntryPoint = NULL;
+      }
+
+      //
+      // Get file name and absolute path of PDB file
+      //
+      GetPdbFileName (ImageBase, &PdbAbsoluteFilePath, &PdbFileName);
+
+      //
+      // Print out image module name (if any)
+      //
+      if (PdbAbsoluteFilePath != NULL) {
+        InternalPrintMessage (
+          "%a (ImageBase=0x%016lx, EntryPoint=0x%016lx):\n",
+          PdbFileName,
+          ImageBase,
+          (UINTN)EntryPoint
+          );
+        InternalPrintMessage ("%a\n", PdbAbsoluteFilePath);
+      }
+
+      //
+      // Save last PE/COFF image base address
+      //
+      LastImageBase = ImageBase;
+    }
+
+    //
+    // Unwind the stack
+    //
+    Rbp = *(UINT64 *)(UINTN)Rbp;
+  }
+}
+
+/**
+  Dump stack trace.
+
+  @param[in]  ExceptionType      Exception type.
+  @param[in]  SystemContext      Pointer to EFI_SYSTEM_CONTEXT.
+  @param[out] UnwoundStacksCount Count of unwound stack frames.
+**/
+STATIC
+VOID
+DumpStacktrace (
+  IN  EFI_EXCEPTION_TYPE   ExceptionType,
+  IN  EFI_SYSTEM_CONTEXT   SystemContext,
+  OUT INTN                 *UnwoundStacksCount
+  )
+{
+  UINT64  Rip;
+  UINT64  Rbp;
+  UINTN   ImageBase;
+  CHAR8   *PdbFileName;
+
   //
-  // Dump module image base and module entry point by RIP
+  // Set current RIP address
   //
   if ((ExceptionType == EXCEPT_IA32_PAGE_FAULT) &&
       ((SystemContext.SystemContextX64->ExceptionData & IA32_PF_EC_ID) != 0)) {
@@ -420,8 +686,127 @@ DumpImageAndCpuContent (
     // The RIP in SystemContext could not be used
     // if it is page fault with I/D set.
     //
-    DumpModuleImageInfo ((*(UINTN *)(UINTN)SystemContext.SystemContextX64->Rsp));
+    Rip = *(UINT64 *)(UINTN)SystemContext.SystemContextX64->Rsp;
   } else {
-    DumpModuleImageInfo (SystemContext.SystemContextX64->Rip);
+    Rip = SystemContext.SystemContextX64->Rip;
+  }
+
+  //
+  // Set current frame pointer address
+  //
+  Rbp = SystemContext.SystemContextX64->Rbp;
+
+  //
+  // Get initial PE/COFF image base address from current RIP
+  //
+  ImageBase = PeCoffSearchImageBase (Rip);
+  if (ImageBase == 0) {
+    InternalPrintMessage ("!!!! Could not find backtrace information. !!!!");
+    return;
+  }
+
+  //
+  // Get PDB file name from initial PE/COFF image
+  //
+  GetPdbFileName (ImageBase, NULL, &PdbFileName);
+
+  //
+  // Initialize count of unwound stacks
+  //
+  *UnwoundStacksCount = 1;
+
+  //
+  // Print out back trace
+  //
+  InternalPrintMessage ("\nCall trace:\n");
+
+  for (;;) {
+    //
+    // Print stack frame in the following format:
+    //
+    // # <RIP> @ <ImageBase>+<RelOffset> (RBP) in [<ModuleName> | ????]
+    //
+    InternalPrintMessage (
+      "%d 0x%016lx @ 0x%016lx+0x%x (0x%016lx) in %a\n",
+      *UnwoundStacksCount - 1,
+      Rip,
+      ImageBase,
+      Rip - ImageBase - 1,
+      Rbp,
+      PdbFileName
+      );
+
+    //
+    // Set RIP with return address from current stack frame
+    //
+    Rip = *(UINT64 *)((UINTN)Rbp + 8);
+
+    //
+    // If RIP is zero, then stop unwinding the stack
+    //
+    if (Rip == 0) {
+      break;
+    }
+
+    //
+    // Search for the respective PE/COFF image based on RIP
+    //
+    ImageBase = PeCoffSearchImageBase (Rip);
+    if (ImageBase == 0) {
+      //
+      // Stop stack trace
+      //
+      break;
+    }
+
+    //
+    // Get PDB file name
+    //
+    GetPdbFileName (ImageBase, NULL, &PdbFileName);
+
+    //
+    // Unwind the stack
+    //
+    Rbp = *(UINT64 *)(UINTN)Rbp;
+
+    //
+    // Increment count of unwound stacks
+    //
+    (*UnwoundStacksCount)++;
   }
 }
+
+/**
+  Display CPU information.
+
+  @param ExceptionType  Exception type.
+  @param SystemContext  Pointer to EFI_SYSTEM_CONTEXT.
+**/
+VOID
+DumpImageAndCpuContent (
+  IN EFI_EXCEPTION_TYPE   ExceptionType,
+  IN EFI_SYSTEM_CONTEXT   SystemContext
+  )
+{
+  INTN UnwoundStacksCount;
+
+  //
+  // Dump CPU context
+  //
+  DumpCpuContext (ExceptionType, SystemContext);
+
+  //
+  // Dump stack trace
+  //
+  DumpStacktrace (ExceptionType, SystemContext, &UnwoundStacksCount);
+
+  //
+  // Dump image module names
+  //
+  DumpImageModuleNames (ExceptionType, SystemContext);
+
+  //
+  // Dump stack contents
+  //
+  DumpStackContents (SystemContext.SystemContextX64->Rsp, UnwoundStacksCount);
+}
-- 
2.14.3

_______________________________________________
edk2-devel mailing list
edk2-devel@lists.01.org
https://lists.01.org/mailman/listinfo/edk2-devel