jasonmolenda created this revision.
jasonmolenda added a reviewer: jasonmolenda.
jasonmolenda added a project: LLDB.
Herald added a subscriber: JDevlieghere.
jasonmolenda requested review of this revision.

Swift is gaining asynchronous functions, which may not be executing when a 
function they call is executing.  In the backtrace, we want to show the logical 
caller of this currently-executing function even though it is not present on 
the stack like a real frame.  The LanguageRuntime can provide an UnwindPlan 
that will provide that function.

When the async function is actually executing, we'll use its normal 
UnwindPlans.  But when it is not executing, we'll use the LanguageRuntime's 
UnwindPlan to show it in the stack.

The way this is actually implemented by the swift runtime is that the stack has 
a chain of these async-not-running functions and pointers to a data block with 
saved context for them, and as we walk the chain we will get back to real stack 
functions again.  The swift language runtime uses a flag in the spilled fp 
register to tell when it should provide its special unwind plan.

This patch adds code to LanguageRuntime to see if any LanguageRuntime plugins 
can provide an UnwindPlan given a RegisterContext.  It adds code to 
RegisterContextUnwind to see if the LanguageRuntime can provide an UnwindPlan 
before we start looking at the function's normal UnwindPlans, and uses the 
async unwind plan if it was provided.  Pretty simple hooks out to the 
LanguageRuntime where the details are encapsulated.

Without interjecting a LanguageRuntime plugin in a live debug session, this 
isn't testable in standard lldb; in the swift branch of lldb I can have an API 
test that has some async calls on the stack and do a stack walk.  But I need 
the LanguageRuntimeSwift support for one of these to get that to work.

Open to any comments or suggestions, not sure if anyone is really into the 
unwinder enough to spend time reviewing it.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D96839

Files:
  lldb/include/lldb/Target/LanguageRuntime.h
  lldb/source/Target/LanguageRuntime.cpp
  lldb/source/Target/RegisterContextUnwind.cpp

Index: lldb/source/Target/RegisterContextUnwind.cpp
===================================================================
--- lldb/source/Target/RegisterContextUnwind.cpp
+++ lldb/source/Target/RegisterContextUnwind.cpp
@@ -24,6 +24,7 @@
 #include "lldb/Target/ABI.h"
 #include "lldb/Target/DynamicLoader.h"
 #include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/LanguageRuntime.h"
 #include "lldb/Target/Platform.h"
 #include "lldb/Target/Process.h"
 #include "lldb/Target/SectionLoadList.h"
@@ -283,6 +284,24 @@
     return;
   }
 
+  ExecutionContext exe_ctx(m_thread.shared_from_this());
+  Process *process = exe_ctx.GetProcessPtr();
+
+  // Some languages may have a logical parent stack frame which is
+  // not a real stack frame, but the programmer would consider it to
+  // be the caller of the frame, e.g. Swift asynchronous frames.
+  //
+  // A Swift async function will have a normal UnwindPlan when it is 
+  // executing, but it may be unscheduled (or on a different thread)
+  // when the function it calls asynchronously, and we need to use an
+  // UnwindPlan from the LanguageRuntime to present this frame to the
+  // user in the backtrace.
+  UnwindPlanSP async_plan_sp =
+      LanguageRuntime::GetAsyncedFrameUnwindPlan(m_thread, this);
+  if (async_plan_sp.get()) {
+    UnwindLogMsg("This is an async frame");
+  }
+
   addr_t pc;
   if (!ReadGPRValue(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, pc)) {
     UnwindLogMsg("could not get pc value");
@@ -290,8 +309,6 @@
     return;
   }
 
-  ExecutionContext exe_ctx(m_thread.shared_from_this());
-  Process *process = exe_ctx.GetProcessPtr();
   // Let ABIs fixup code addresses to make sure they are valid. In ARM ABIs
   // this will strip bit zero in case we read a PC from memory or from the LR.
   ABI *abi = process->GetABI().get();
@@ -522,12 +539,42 @@
     }
   }
 
-  // We've set m_frame_type and m_sym_ctx before this call.
-  m_fast_unwind_plan_sp = GetFastUnwindPlanForFrame();
-
   UnwindPlan::RowSP active_row;
   RegisterKind row_register_kind = eRegisterKindGeneric;
 
+  // If we have an asynchronous unwind plan from the LanguageRuntime,
+  // use those rules to find the caller frame instead of the function's
+  // normal UnwindPlans.  The full unwind plan for this frame will be
+  // the LanguageRuntime-provided unwind plan, and there will not be a
+  // fast unwind plan.
+  if (async_plan_sp.get()) {
+    active_row = async_plan_sp->GetRowForFunctionOffset(m_current_offset);
+    row_register_kind = async_plan_sp->GetRegisterKind();
+    if (!ReadFrameAddress(row_register_kind, active_row->GetCFAValue(),
+                          m_cfa)) {
+      UnwindLogMsg("Cannot set cfa");
+    } else {
+      m_full_unwind_plan_sp = async_plan_sp;
+      if (active_row.get() && log) {
+        StreamString active_row_strm;
+        active_row->Dump(active_row_strm, async_plan_sp.get(), &m_thread,
+                         m_start_pc.GetLoadAddress(exe_ctx.GetTargetPtr()));
+        UnwindLogMsg("async active row: %s", active_row_strm.GetData());
+      }
+      UnwindLogMsg("m_cfa = 0x%" PRIx64 " m_afa = 0x%" PRIx64, m_cfa, m_afa);
+      UnwindLogMsg(
+          "initialized async frame current pc is 0x%" PRIx64
+          " cfa is 0x%" PRIx64 " afa is 0x%" PRIx64,
+          (uint64_t)m_current_pc.GetLoadAddress(exe_ctx.GetTargetPtr()),
+          (uint64_t)m_cfa, (uint64_t)m_afa);
+
+      return;
+    }
+  }
+
+  // We've set m_frame_type and m_sym_ctx before this call.
+  m_fast_unwind_plan_sp = GetFastUnwindPlanForFrame();
+
   // Try to get by with just the fast UnwindPlan if possible - the full
   // UnwindPlan may be expensive to get (e.g. if we have to parse the entire
   // eh_frame section of an ObjectFile for the first time.)
Index: lldb/source/Target/LanguageRuntime.cpp
===================================================================
--- lldb/source/Target/LanguageRuntime.cpp
+++ lldb/source/Target/LanguageRuntime.cpp
@@ -259,6 +259,21 @@
   return exc_breakpt_sp;
 }
 
+UnwindPlanSP
+LanguageRuntime::GetAsyncedFrameUnwindPlan(Thread &thread,
+                                           RegisterContext *regctx) {
+  for (const lldb::LanguageType lang_type : Language::GetSupportedLanguages()) {
+    if (LanguageRuntime *runtime =
+            thread.GetProcess()->GetLanguageRuntime(lang_type)) {
+      UnwindPlanSP plan_sp =
+          runtime->GetAsyncedFrameForThisLanguage(thread, regctx);
+      if (plan_sp.get())
+        return plan_sp;
+    }
+  }
+  return UnwindPlanSP();
+}
+
 void LanguageRuntime::InitializeCommands(CommandObject *parent) {
   if (!parent)
     return;
Index: lldb/include/lldb/Target/LanguageRuntime.h
===================================================================
--- lldb/include/lldb/Target/LanguageRuntime.h
+++ lldb/include/lldb/Target/LanguageRuntime.h
@@ -18,6 +18,7 @@
 #include "lldb/Expression/LLVMUserExpression.h"
 #include "lldb/Symbol/DeclVendor.h"
 #include "lldb/Target/ExecutionContextScope.h"
+#include "lldb/Target/RegisterContext.h"
 #include "lldb/Target/Runtime.h"
 #include "lldb/lldb-private.h"
 #include "lldb/lldb-public.h"
@@ -173,7 +174,26 @@
   virtual bool isA(const void *ClassID) const { return ClassID == &ID; }
   static char ID;
 
+  // A language runtime may be able to provide a special UnwindPlan for
+  // the frame represented by the register contents \a regctx when that
+  // frame is not following the normal ABI conventions.
+  // Instead of using the normal UnwindPlan for the function, we will use
+  // this special UnwindPlan for this one backtrace. 
+  // One example of this would be a language that has asynchronous functions,
+  // functions that may not be currently-executing, while waiting on other
+  // asynchornous calls they made, but are part of a logical backtrace how
+  // the programmer views the call stack.
+  static lldb::UnwindPlanSP
+  GetAsyncedFrameUnwindPlan(lldb_private::Thread &thread,
+                            lldb_private::RegisterContext *regctx);
+
 protected:
+  virtual lldb::UnwindPlanSP
+  GetAsyncedFrameForThisLanguage(lldb_private::Thread &thread,
+                                 lldb_private::RegisterContext *regctx) {
+    return lldb::UnwindPlanSP();
+  }
+
   LanguageRuntime(Process *process);
 };
 
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to