LanguageRuntime can provide an UnwindPlan for special occasions

Add a facility in the LanguageRuntime to provide a special
UnwindPlan based on the register values in a RegisterContext,
instead of using the return-pc to find a function and use its
normal UnwindPlans.

Needed when the runtime has special stack frames that we want
to show the user, but aren't actually on the real stack.
Specifically for Swift asynchronous functions.

With feedback from Greg Clayton, Jonas Devlieghere, Dave Lee

<rdar://problem/70398009>

Differential Revision: https://reviews.llvm.org/D96839

GitOrigin-RevId: d82ecb0ac72261189ec3a5dccd3ad55aeafe07b4
diff --git a/include/lldb/Target/LanguageRuntime.h b/include/lldb/Target/LanguageRuntime.h
index a3897ad..a8fc3ee 100644
--- a/include/lldb/Target/LanguageRuntime.h
+++ b/include/lldb/Target/LanguageRuntime.h
@@ -173,7 +173,30 @@
   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
+  /// asynchronous calls they made, but are part of a logical backtrace that
+  /// we want to show the developer because that's how they think of the
+  /// program flow.
+  static lldb::UnwindPlanSP
+  GetRuntimeUnwindPlan(lldb_private::Thread &thread,
+                       lldb_private::RegisterContext *regctx);
+
 protected:
+  // The static GetRuntimeUnwindPlan method above is only implemented in the
+  // base class; subclasses may override this protected member if they can
+  // provide one of these UnwindPlans.
+  virtual lldb::UnwindPlanSP
+  GetRuntimeUnwindPlan(lldb::ProcessSP process_sp,
+                       lldb_private::RegisterContext *regctx) {
+    return lldb::UnwindPlanSP();
+  }
+
   LanguageRuntime(Process *process);
 };
 
diff --git a/source/Target/LanguageRuntime.cpp b/source/Target/LanguageRuntime.cpp
index f2e2feb..0b3b0e1 100644
--- a/source/Target/LanguageRuntime.cpp
+++ b/source/Target/LanguageRuntime.cpp
@@ -259,6 +259,21 @@
   return exc_breakpt_sp;
 }
 
+UnwindPlanSP LanguageRuntime::GetRuntimeUnwindPlan(Thread &thread,
+                                                   RegisterContext *regctx) {
+  ProcessSP process_sp = thread.GetProcess();
+  if (!process_sp.get())
+    return UnwindPlanSP();
+  for (const lldb::LanguageType lang_type : Language::GetSupportedLanguages()) {
+    if (LanguageRuntime *runtime = process_sp->GetLanguageRuntime(lang_type)) {
+      UnwindPlanSP plan_sp = runtime->GetRuntimeUnwindPlan(process_sp, regctx);
+      if (plan_sp.get())
+        return plan_sp;
+    }
+  }
+  return UnwindPlanSP();
+}
+
 void LanguageRuntime::InitializeCommands(CommandObject *parent) {
   if (!parent)
     return;
diff --git a/source/Target/RegisterContextUnwind.cpp b/source/Target/RegisterContextUnwind.cpp
index a677121..355d189 100644
--- a/source/Target/RegisterContextUnwind.cpp
+++ b/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,22 @@
     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 LanguageRuntime may provide an UnwindPlan that is used in this
+  // stack trace base on the RegisterContext contents, intsead
+  // of the normal UnwindPlans we would use for the return-pc.
+  UnwindPlanSP lang_runtime_plan_sp =
+      LanguageRuntime::GetRuntimeUnwindPlan(m_thread, this);
+  if (lang_runtime_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 +307,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 +537,43 @@
     }
   }
 
-  // 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 LanguageRuntime UnwindPlan for this unwind, 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 (lang_runtime_plan_sp.get()) {
+    active_row =
+        lang_runtime_plan_sp->GetRowForFunctionOffset(m_current_offset);
+    row_register_kind = lang_runtime_plan_sp->GetRegisterKind();
+    if (!ReadFrameAddress(row_register_kind, active_row->GetCFAValue(),
+                          m_cfa)) {
+      UnwindLogMsg("Cannot set cfa");
+    } else {
+      m_full_unwind_plan_sp = lang_runtime_plan_sp;
+      if (log) {
+        StreamString active_row_strm;
+        active_row->Dump(active_row_strm, lang_runtime_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.)