[edk2] [RFC v4 1/6] UefiCpuPkg/CpuExceptionHandlerLib/X64: Add stack trace support

Paulo Alcantara posted 6 patches 6 years, 11 months ago
There is a newer version of this series
[edk2] [RFC v4 1/6] 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 | 376 +++++++++++++++++++-
 1 file changed, 374 insertions(+), 2 deletions(-)

diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c b/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
index 1dcf4277de..19bfaa329a 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.
@@ -398,6 +403,357 @@ DumpCpuContext (
     );
 }
 
+/**
+  Get absolute path and file name of PDB file in PE/COFF image.
+
+  @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
+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]  SystemContext  Pointer to EFI_SYSTEM_CONTEXT.
+**/
+STATIC
+VOID
+DumpImageModuleNames (
+  IN EFI_SYSTEM_CONTEXT   SystemContext
+  )
+{
+  EFI_STATUS  Status;
+  UINT64      Rip;
+  UINTN       ImageBase;
+  VOID        *EntryPoint;
+  CHAR8       *PdbAbsoluteFilePath;
+  CHAR8       *PdbFileName;
+  UINT64      Rbp;
+  UINTN       LastImageBase;
+
+  //
+  // Set current RIP address
+  //
+  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]  SystemContext      Pointer to EFI_SYSTEM_CONTEXT.
+  @param[out] UnwoundStacksCount  Count of unwound stack frames.
+**/
+STATIC
+VOID
+DumpStackTrace (
+  IN  EFI_SYSTEM_CONTEXT   SystemContext,
+  OUT INTN                 *UnwoundStacksCount
+  )
+{
+  UINT64  Rip;
+  UINT64  Rbp;
+  UINTN   ImageBase;
+  CHAR8   *PdbFileName;
+
+  //
+  // Set current RIP address
+  //
+  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.
 
@@ -410,9 +766,25 @@ DumpImageAndCpuContent (
   IN EFI_SYSTEM_CONTEXT   SystemContext
   )
 {
+  INTN UnwoundStacksCount;
+
+  //
+  // Dump CPU context
+  //
   DumpCpuContext (ExceptionType, SystemContext);
+
+  //
+  // Dump stack trace
+  //
+  DumpStackTrace (SystemContext, &UnwoundStacksCount);
+
+  //
+  // Dump image module names
+  //
+  DumpImageModuleNames (SystemContext);
+
   //
-  // Dump module image base and module entry point by RIP
+  // Dump stack contents
   //
-  DumpModuleImageInfo (SystemContext.SystemContextX64->Rip);
+  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
[edk2] 答复: [RFC v4 1/6] UefiCpuPkg/CpuExceptionHandlerLib/X64: Add stack trace support
Posted by Fan Jeff 6 years, 11 months ago
Paulo,



I did not receive your #0 patch. So, I reply #1 here.



Your serial of patches should work for IA32/X64 GCC and IA32 MSFT. I suggest you could push this serial of patches as soon as pass UefiCpuPkg owner/reviewers’ review and validation,



And then I could send my x64 MSFT support later.



Thanks!

Jeff

________________________________
From: edk2-devel <edk2-devel-bounces@lists.01.org> on behalf of Paulo Alcantara <paulo@paulo.ac>
Sent: Friday, December 29, 2017 12:39:34 PM
To: edk2-devel@lists.01.org
Cc: Laszlo Ersek; Eric Dong
Subject: [edk2] [RFC v4 1/6] UefiCpuPkg/CpuExceptionHandlerLib/X64: Add stack trace support

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 | 376 +++++++++++++++++++-
 1 file changed, 374 insertions(+), 2 deletions(-)

diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c b/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
index 1dcf4277de..19bfaa329a 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.
@@ -398,6 +403,357 @@ DumpCpuContext (
     );
 }

+/**
+  Get absolute path and file name of PDB file in PE/COFF image.
+
+  @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
+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]  SystemContext  Pointer to EFI_SYSTEM_CONTEXT.
+**/
+STATIC
+VOID
+DumpImageModuleNames (
+  IN EFI_SYSTEM_CONTEXT   SystemContext
+  )
+{
+  EFI_STATUS  Status;
+  UINT64      Rip;
+  UINTN       ImageBase;
+  VOID        *EntryPoint;
+  CHAR8       *PdbAbsoluteFilePath;
+  CHAR8       *PdbFileName;
+  UINT64      Rbp;
+  UINTN       LastImageBase;
+
+  //
+  // Set current RIP address
+  //
+  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]  SystemContext      Pointer to EFI_SYSTEM_CONTEXT.
+  @param[out] UnwoundStacksCount  Count of unwound stack frames.
+**/
+STATIC
+VOID
+DumpStackTrace (
+  IN  EFI_SYSTEM_CONTEXT   SystemContext,
+  OUT INTN                 *UnwoundStacksCount
+  )
+{
+  UINT64  Rip;
+  UINT64  Rbp;
+  UINTN   ImageBase;
+  CHAR8   *PdbFileName;
+
+  //
+  // Set current RIP address
+  //
+  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.

@@ -410,9 +766,25 @@ DumpImageAndCpuContent (
   IN EFI_SYSTEM_CONTEXT   SystemContext
   )
 {
+  INTN UnwoundStacksCount;
+
+  //
+  // Dump CPU context
+  //
   DumpCpuContext (ExceptionType, SystemContext);
+
+  //
+  // Dump stack trace
+  //
+  DumpStackTrace (SystemContext, &UnwoundStacksCount);
+
+  //
+  // Dump image module names
+  //
+  DumpImageModuleNames (SystemContext);
+
   //
-  // Dump module image base and module entry point by RIP
+  // Dump stack contents
   //
-  DumpModuleImageInfo (SystemContext.SystemContextX64->Rip);
+  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
_______________________________________________
edk2-devel mailing list
edk2-devel@lists.01.org
https://lists.01.org/mailman/listinfo/edk2-devel
Re: [edk2] 答复: [RFC v4 1/6] UefiCpuPkg/CpuExceptionHandlerLib/X64: Add stack trace support
Posted by Paulo Alcantara 6 years, 11 months ago
Hi Jeff,

On 1/3/2018 6:53 AM, Fan Jeff wrote:
> Paulo,
> 
> I did not receive your #0 patch. So, I reply #1 here.
> 
> Your serial of patches should work for IA32/X64 GCC and IA32 MSFT. I 
> suggest you could push this serial of patches as soon as pass UefiCpuPkg 
> owner/reviewers’ review and validation,

OK - That would be really appreciable if you could test it with IA32 
MSFT toolchain and see if the stacktrace is printed out correctly when 
passing the '/Oy-' compile option.

> 
> And then I could send my x64 MSFT support later.

Cool.

Thanks
Paulo

> ------------------------------------------------------------------------
> *From:* edk2-devel <edk2-devel-bounces@lists.01.org> on behalf of Paulo 
> Alcantara <paulo@paulo.ac>
> *Sent:* Friday, December 29, 2017 12:39:34 PM
> *To:* edk2-devel@lists.01.org
> *Cc:* Laszlo Ersek; Eric Dong
> *Subject:* [edk2] [RFC v4 1/6] UefiCpuPkg/CpuExceptionHandlerLib/X64: 
> Add stack trace support
> 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 | 
> 376 +++++++++++++++++++-
>   1 file changed, 374 insertions(+), 2 deletions(-)
> 
> diff --git 
> a/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c 
> b/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
> index 1dcf4277de..19bfaa329a 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.
> @@ -398,6 +403,357 @@ DumpCpuContext (
>       );
>   }
> 
> +/**
> +  Get absolute path and file name of PDB file in PE/COFF image.
> +
> +  @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
> +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]  SystemContext  Pointer to EFI_SYSTEM_CONTEXT.
> +**/
> +STATIC
> +VOID
> +DumpImageModuleNames (
> +  IN EFI_SYSTEM_CONTEXT   SystemContext
> +  )
> +{
> +  EFI_STATUS  Status;
> +  UINT64      Rip;
> +  UINTN       ImageBase;
> +  VOID        *EntryPoint;
> +  CHAR8       *PdbAbsoluteFilePath;
> +  CHAR8       *PdbFileName;
> +  UINT64      Rbp;
> +  UINTN       LastImageBase;
> +
> +  //
> +  // Set current RIP address
> +  //
> +  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]  SystemContext      Pointer to EFI_SYSTEM_CONTEXT.
> +  @param[out] UnwoundStacksCount  Count of unwound stack frames.
> +**/
> +STATIC
> +VOID
> +DumpStackTrace (
> +  IN  EFI_SYSTEM_CONTEXT   SystemContext,
> +  OUT INTN                 *UnwoundStacksCount
> +  )
> +{
> +  UINT64  Rip;
> +  UINT64  Rbp;
> +  UINTN   ImageBase;
> +  CHAR8   *PdbFileName;
> +
> +  //
> +  // Set current RIP address
> +  //
> +  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.
> 
> @@ -410,9 +766,25 @@ DumpImageAndCpuContent (
>     IN EFI_SYSTEM_CONTEXT   SystemContext
>     )
>   {
> +  INTN UnwoundStacksCount;
> +
> +  //
> +  // Dump CPU context
> +  //
>     DumpCpuContext (ExceptionType, SystemContext);
> +
> +  //
> +  // Dump stack trace
> +  //
> +  DumpStackTrace (SystemContext, &UnwoundStacksCount);
> +
> +  //
> +  // Dump image module names
> +  //
> +  DumpImageModuleNames (SystemContext);
> +
>     //
> -  // Dump module image base and module entry point by RIP
> +  // Dump stack contents
>     //
> -  DumpModuleImageInfo (SystemContext.SystemContextX64->Rip);
> +  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

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