augusto2112 created this revision.
augusto2112 added reviewers: aprantl, jingham, dblaikie.
Herald added a reviewer: shafik.
Herald added a project: All.
augusto2112 requested review of this revision.
Herald added a project: LLDB.
Herald added a subscriber: lldb-commits.

This patch adds support for the DW_AT_trampoline attribute whose value
is a boolean. Which is a "generic trampoline". Stepping into a generic
trampoline by default will step through the function, checking
at every branch, until we stop in a function which makes sense to stop
at (a function with debug info, which isn't a trampoline, for example).


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D147292

Files:
  lldb/include/lldb/Symbol/Function.h
  lldb/include/lldb/Target/Target.h
  lldb/include/lldb/Target/Thread.h
  lldb/include/lldb/Target/ThreadPlan.h
  lldb/include/lldb/Target/ThreadPlanStepOverRange.h
  lldb/include/lldb/Target/ThreadPlanStepRange.h
  lldb/include/lldb/Target/ThreadPlanStepThroughGenericTrampoline.h
  lldb/source/Core/Module.cpp
  lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp
  lldb/source/Plugins/SymbolFile/DWARF/DWARFDIE.cpp
  lldb/source/Plugins/SymbolFile/DWARF/DWARFDIE.h
  lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.cpp
  lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.h
  lldb/source/Symbol/Function.cpp
  lldb/source/Target/CMakeLists.txt
  lldb/source/Target/Target.cpp
  lldb/source/Target/TargetProperties.td
  lldb/source/Target/Thread.cpp
  lldb/source/Target/ThreadPlanShouldStopHere.cpp
  lldb/source/Target/ThreadPlanStepInRange.cpp
  lldb/source/Target/ThreadPlanStepOverRange.cpp
  lldb/source/Target/ThreadPlanStepRange.cpp
  lldb/source/Target/ThreadPlanStepThrough.cpp
  lldb/source/Target/ThreadPlanStepThroughGenericTrampoline.cpp
  lldb/test/API/lang/c/trampoline_stepping/Makefile
  lldb/test/API/lang/c/trampoline_stepping/TestTrampolineStepping.py
  lldb/test/API/lang/c/trampoline_stepping/main.c

Index: lldb/test/API/lang/c/trampoline_stepping/main.c
===================================================================
--- /dev/null
+++ lldb/test/API/lang/c/trampoline_stepping/main.c
@@ -0,0 +1,52 @@
+void foo(void) {}
+
+__attribute__((transparent_stepping))
+void bar(void) {
+  foo();
+}
+
+__attribute__((transparent_stepping))
+void baz(void) {
+  bar();
+}
+
+__attribute__((nodebug))
+void nodebug(void) {}
+
+__attribute__((transparent_stepping))
+void nodebug_then_trampoline(void) {
+  nodebug();
+  baz();
+}
+
+__attribute__((transparent_stepping))
+void doesnt_call_trampoline(void) {}
+
+void direct_trampoline_call(void) {
+  bar(); // Break here for direct 
+  bar();
+}
+
+void chained_trampoline_call(void) {
+  baz(); // Break here for chained
+  baz();
+}
+
+void trampoline_after_nodebug(void) {
+  nodebug_then_trampoline(); // Break here for nodebug then trampoline
+  nodebug_then_trampoline();
+}
+
+void unused_target(void) {
+  doesnt_call_trampoline(); // Break here for unused
+}
+
+
+int main(void) {
+  direct_trampoline_call();
+  chained_trampoline_call();
+  trampoline_after_nodebug();
+  unused_target();
+  return 0;
+}
+
Index: lldb/test/API/lang/c/trampoline_stepping/TestTrampolineStepping.py
===================================================================
--- /dev/null
+++ lldb/test/API/lang/c/trampoline_stepping/TestTrampolineStepping.py
@@ -0,0 +1,104 @@
+"""Test that stepping in/out of trampolines works as expected.
+"""
+
+
+
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+
+class TestTrampoline(TestBase):
+    def setup(self, bkpt_str):
+        self.build()
+        
+        _, _, thread, _ = lldbutil.run_to_source_breakpoint(
+            self, bkpt_str, lldb.SBFileSpec('main.c'))
+        return thread
+
+    def test_direct_call(self):
+        thread = self.setup('Break here for direct')
+
+        # Sanity check that we start out in the correct function.
+        name = thread.frames[0].GetFunctionName()
+        self.assertIn('direct_trampoline_call', name)
+
+        # Check that stepping in will take us directly to the trampoline target.
+        thread.StepInto()
+        name = thread.frames[0].GetFunctionName()
+        self.assertIn('foo', name)
+
+        # Check that stepping out takes us back to the trampoline caller.
+        thread.StepOut()
+        name = thread.frames[0].GetFunctionName()
+        self.assertIn('direct_trampoline_call', name)
+
+        # Check that stepping over the end of the trampoline target 
+        # takes us back to the trampoline caller.
+        thread.StepInto()
+        thread.StepOver()
+        name = thread.frames[0].GetFunctionName()
+        self.assertIn('direct_trampoline_call', name)
+
+
+    def test_chained_call(self):
+        thread = self.setup('Break here for chained')
+
+        # Sanity check that we start out in the correct function.
+        name = thread.frames[0].GetFunctionName()
+        self.assertIn('chained_trampoline_call', name)
+
+        # Check that stepping in will take us directly to the trampoline target.
+        thread.StepInto()
+        name = thread.frames[0].GetFunctionName()
+        self.assertIn('foo', name)
+
+        # Check that stepping out takes us back to the trampoline caller.
+        thread.StepOut()
+        name = thread.frames[0].GetFunctionName()
+        self.assertIn('chained_trampoline_call', name)
+
+        # Check that stepping over the end of the trampoline target 
+        # takes us back to the trampoline caller.
+        thread.StepInto()
+        thread.StepOver()
+        name = thread.frames[0].GetFunctionName()
+        self.assertIn('chained_trampoline_call', name)
+
+    def test_trampoline_after_nodebug(self):
+        thread = self.setup('Break here for nodebug then trampoline')
+
+        # Sanity check that we start out in the correct function.
+        name = thread.frames[0].GetFunctionName()
+        self.assertIn('trampoline_after_nodebug', name)
+
+        # Check that stepping in will take us directly to the trampoline target.
+        thread.StepInto()
+        name = thread.frames[0].GetFunctionName()
+        self.assertIn('foo', name)
+
+        # Check that stepping out takes us back to the trampoline caller.
+        thread.StepOut()
+        name = thread.frames[0].GetFunctionName()
+        self.assertIn('trampoline_after_nodebug', name)
+
+        # Check that stepping over the end of the trampoline target 
+        # takes us back to the trampoline caller.
+        thread.StepInto()
+        thread.StepOver()
+        name = thread.frames[0].GetFunctionName()
+        self.assertIn('trampoline_after_nodebug', name)
+
+    def test_unused_target(self):
+        thread = self.setup('Break here for unused')
+
+        # Sanity check that we start out in the correct function.
+        name = thread.frames[0].GetFunctionName()
+        self.assertIn('unused_target', name)
+
+        # Check that stepping into a trampoline that doesn't call its target
+        # jumps back to its caller.
+        thread.StepInto()
+        name = thread.frames[0].GetFunctionName()
+        self.assertIn('unused_target', name)
+        
Index: lldb/test/API/lang/c/trampoline_stepping/Makefile
===================================================================
--- /dev/null
+++ lldb/test/API/lang/c/trampoline_stepping/Makefile
@@ -0,0 +1,3 @@
+C_SOURCES := main.c
+
+include Makefile.rules
Index: lldb/source/Target/ThreadPlanStepThroughGenericTrampoline.cpp
===================================================================
--- /dev/null
+++ lldb/source/Target/ThreadPlanStepThroughGenericTrampoline.cpp
@@ -0,0 +1,129 @@
+//===-- ThreadPlanStepThroughGenericTrampoline.cpp
+//-----------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Target/ThreadPlanStepThroughGenericTrampoline.h"
+#include "lldb/Symbol/Function.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Utility/LLDBLog.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/Stream.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+uint32_t ThreadPlanStepThroughGenericTrampoline::s_default_flag_values =
+    ThreadPlanShouldStopHere::eStepInAvoidNoDebug;
+
+ThreadPlanStepThroughGenericTrampoline::ThreadPlanStepThroughGenericTrampoline(
+    Thread &thread, lldb::RunMode stop_others)
+    : ThreadPlanStepRange(ThreadPlan::eKindStepThroughGenericTrampoline,
+                          "Step through generic trampoline", thread, {}, {},
+                          stop_others),
+      ThreadPlanShouldStopHere(this) {
+
+  SetFlagsToDefault();
+}
+
+ThreadPlanStepThroughGenericTrampoline::
+    ~ThreadPlanStepThroughGenericTrampoline() = default;
+
+void ThreadPlanStepThroughGenericTrampoline::GetDescription(
+    Stream *s, lldb::DescriptionLevel level) {
+
+  auto PrintFailureIfAny = [&]() {
+    if (m_status.Success())
+      return;
+    s->Printf(" failed (%s)", m_status.AsCString());
+  };
+
+  if (level == lldb::eDescriptionLevelBrief) {
+    s->Printf("step through generic trampoline");
+    PrintFailureIfAny();
+    return;
+  }
+
+  auto frame = GetThread().GetFrameWithStackID(m_stack_id);
+  if (!frame) {
+    s->Printf("<error, frame not available>");
+    return;
+  }
+
+  SymbolContext sc = frame->GetSymbolContext(eSymbolContextFunction);
+  if (!sc.function) {
+    s->Printf("<error, function not available>");
+    return;
+  }
+
+  s->Printf("Stepping through generic trampoline %s",
+            sc.function->GetName().AsCString());
+
+  lldb::StackFrameSP curr_frame = GetThread().GetStackFrameAtIndex(0);
+  if (!curr_frame)
+    return;
+
+  SymbolContext curr_frame_sc =
+      curr_frame->GetSymbolContext(eSymbolContextFunction);
+  if (!curr_frame_sc.function)
+    return;
+  s->Printf(", current function: %s",
+            curr_frame_sc.function->GetName().GetCString());
+
+  PrintFailureIfAny();
+
+  s->PutChar('.');
+}
+
+bool ThreadPlanStepThroughGenericTrampoline::ShouldStop(Event *event_ptr) {
+  Log *log = GetLog(LLDBLog::Step);
+
+  if (log) {
+    StreamString s;
+    DumpAddress(s.AsRawOstream(), GetThread().GetRegisterContext()->GetPC(),
+                GetTarget().GetArchitecture().GetAddressByteSize());
+    LLDB_LOGF(log, "ThreadPlanStepThroughGenericTrampoline reached %s.",
+              s.GetData());
+  }
+
+  if (IsPlanComplete())
+    return true;
+
+  m_no_more_plans = false;
+
+  Thread &thread = GetThread();
+  lldb::StackFrameSP curr_frame = thread.GetStackFrameAtIndex(0);
+  if (!curr_frame)
+    return false;
+
+  SymbolContext sc = curr_frame->GetSymbolContext(eSymbolContextFunction);
+
+  if (sc.function && sc.function->IsGenericTrampoline() &&
+      SetNextBranchBreakpoint()) {
+    // While whatever frame we're in is a generic trampoline,
+    // continue stepping to the next branch, until we
+    // end up in a function which isn't a trampoline.
+    return false;
+  }
+
+  m_no_more_plans = true;
+  SetPlanComplete();
+  return true;
+}
+
+bool ThreadPlanStepThroughGenericTrampoline::ValidatePlan(Stream *error) {
+  // If trampoline support is disabled, there's nothing for us to do.
+  if (!Target::GetGlobalProperties().GetEnableTrampolineSupport())
+    return false;
+
+  auto frame = GetThread().GetFrameWithStackID(m_stack_id);
+  if (!frame)
+    return false;
+
+  SymbolContext sc = frame->GetSymbolContext(eSymbolContextFunction);
+  return sc.function && sc.function->IsGenericTrampoline();
+}
Index: lldb/source/Target/ThreadPlanStepThrough.cpp
===================================================================
--- lldb/source/Target/ThreadPlanStepThrough.cpp
+++ lldb/source/Target/ThreadPlanStepThrough.cpp
@@ -33,6 +33,10 @@
       m_start_address(0), m_backstop_bkpt_id(LLDB_INVALID_BREAK_ID),
       m_backstop_addr(LLDB_INVALID_ADDRESS), m_return_stack_id(m_stack_id),
       m_stop_others(stop_others) {
+  // If trampoline support is disabled, there's nothing for us to do.
+  if (!Target::GetGlobalProperties().GetEnableTrampolineSupport())
+    return;
+
   LookForPlanToStepThroughFromCurrentPC();
 
   // If we don't get a valid step through plan, don't bother to set up a
Index: lldb/source/Target/ThreadPlanStepRange.cpp
===================================================================
--- lldb/source/Target/ThreadPlanStepRange.cpp
+++ lldb/source/Target/ThreadPlanStepRange.cpp
@@ -494,3 +494,35 @@
   }
   return false;
 }
+
+
+bool ThreadPlanStepRange::DoPlanExplainsStop(Event *event_ptr) {
+  // For crashes, breakpoint hits, signals, etc, let the base plan (or some
+  // plan above us) handle the stop.  That way the user can see the stop, step
+  // around, and then when they are done, continue and have their step
+  // complete.  The exception is if we've hit our "run to next branch"
+  // breakpoint. Note, unlike the step in range plan, we don't mark ourselves
+  // complete if we hit an unexplained breakpoint/crash.
+
+  Log *log = GetLog(LLDBLog::Step);
+  StopInfoSP stop_info_sp = GetPrivateStopInfo();
+  bool return_value;
+
+  if (stop_info_sp) {
+    StopReason reason = stop_info_sp->GetStopReason();
+
+    if (reason == eStopReasonTrace) {
+      return_value = true;
+    } else if (reason == eStopReasonBreakpoint) {
+      return_value = NextRangeBreakpointExplainsStop(stop_info_sp);
+    } else {
+      if (log)
+        log->PutCString("ThreadPlanStepRange got asked if it explains the "
+                        "stop for some reason other than step.");
+      return_value = false;
+    }
+  } else
+    return_value = true;
+
+  return return_value;
+}
Index: lldb/source/Target/ThreadPlanStepOverRange.cpp
===================================================================
--- lldb/source/Target/ThreadPlanStepOverRange.cpp
+++ lldb/source/Target/ThreadPlanStepOverRange.cpp
@@ -334,37 +334,6 @@
     return false;
 }
 
-bool ThreadPlanStepOverRange::DoPlanExplainsStop(Event *event_ptr) {
-  // For crashes, breakpoint hits, signals, etc, let the base plan (or some
-  // plan above us) handle the stop.  That way the user can see the stop, step
-  // around, and then when they are done, continue and have their step
-  // complete.  The exception is if we've hit our "run to next branch"
-  // breakpoint. Note, unlike the step in range plan, we don't mark ourselves
-  // complete if we hit an unexplained breakpoint/crash.
-
-  Log *log = GetLog(LLDBLog::Step);
-  StopInfoSP stop_info_sp = GetPrivateStopInfo();
-  bool return_value;
-
-  if (stop_info_sp) {
-    StopReason reason = stop_info_sp->GetStopReason();
-
-    if (reason == eStopReasonTrace) {
-      return_value = true;
-    } else if (reason == eStopReasonBreakpoint) {
-      return_value = NextRangeBreakpointExplainsStop(stop_info_sp);
-    } else {
-      if (log)
-        log->PutCString("ThreadPlanStepInRange got asked if it explains the "
-                        "stop for some reason other than step.");
-      return_value = false;
-    }
-  } else
-    return_value = true;
-
-  return return_value;
-}
-
 bool ThreadPlanStepOverRange::DoWillResume(lldb::StateType resume_state,
                                            bool current_plan) {
   if (resume_state != eStateSuspended && m_first_resume) {
Index: lldb/source/Target/ThreadPlanStepInRange.cpp
===================================================================
--- lldb/source/Target/ThreadPlanStepInRange.cpp
+++ lldb/source/Target/ThreadPlanStepInRange.cpp
@@ -217,6 +217,17 @@
 
     // We may have set the plan up above in the FrameIsOlder section:
 
+    if (!m_sub_plan_sp)
+      m_sub_plan_sp = thread.QueueThreadPlanForStepThroughGenericTrampoline(
+          false, m_stop_others, m_status);
+    if (log) {
+      if (m_sub_plan_sp)
+        LLDB_LOGF(log, "Found a generic step through plan: %s",
+                  m_sub_plan_sp->GetName());
+      else
+        LLDB_LOGF(log, "No generic step through plan found.");
+    }
+
     if (!m_sub_plan_sp)
       m_sub_plan_sp = thread.QueueThreadPlanForStepThrough(
           m_stack_id, false, stop_others, m_status);
@@ -428,33 +439,7 @@
   // branch" in which case if we hit our branch breakpoint we don't set the
   // plan to complete.
 
-  bool return_value = false;
-
-  if (m_virtual_step) {
-    return_value = true;
-  } else {
-    StopInfoSP stop_info_sp = GetPrivateStopInfo();
-    if (stop_info_sp) {
-      StopReason reason = stop_info_sp->GetStopReason();
-
-      if (reason == eStopReasonBreakpoint) {
-        if (NextRangeBreakpointExplainsStop(stop_info_sp)) {
-          return_value = true;
-        }
-      } else if (IsUsuallyUnexplainedStopReason(reason)) {
-        Log *log = GetLog(LLDBLog::Step);
-        if (log)
-          log->PutCString("ThreadPlanStepInRange got asked if it explains the "
-                          "stop for some reason other than step.");
-        return_value = false;
-      } else {
-        return_value = true;
-      }
-    } else
-      return_value = true;
-  }
-
-  return return_value;
+  return m_virtual_step || ThreadPlanStepRange::DoPlanExplainsStop(event_ptr);
 }
 
 bool ThreadPlanStepInRange::DoWillResume(lldb::StateType resume_state,
Index: lldb/source/Target/ThreadPlanShouldStopHere.cpp
===================================================================
--- lldb/source/Target/ThreadPlanShouldStopHere.cpp
+++ lldb/source/Target/ThreadPlanShouldStopHere.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "lldb/Target/ThreadPlanShouldStopHere.h"
+#include "lldb/Symbol/Function.h"
 #include "lldb/Symbol/Symbol.h"
 #include "lldb/Target/RegisterContext.h"
 #include "lldb/Target/Thread.h"
@@ -81,10 +82,16 @@
   // independently.  If this ever
   // becomes expensive (this one isn't) we can try to have this set a state
   // that the StepFromHere can use.
-  if (frame) {
-    SymbolContext sc;
-    sc = frame->GetSymbolContext(eSymbolContextLineEntry);
-    if (sc.line_entry.line == 0)
+  SymbolContext sc;
+  sc = frame->GetSymbolContext(eSymbolContextLineEntry);
+
+  if (sc.line_entry.line == 0)
+    should_stop_here = false;
+
+  // If we're in a trampoline, don't stop by default.
+  if (Target::GetGlobalProperties().GetEnableTrampolineSupport()) {
+    sc = frame->GetSymbolContext(lldb::eSymbolContextFunction);
+    if (sc.function && sc.function->IsGenericTrampoline())
       should_stop_here = false;
   }
 
Index: lldb/source/Target/Thread.cpp
===================================================================
--- lldb/source/Target/Thread.cpp
+++ lldb/source/Target/Thread.cpp
@@ -41,6 +41,7 @@
 #include "lldb/Target/ThreadPlanStepOverBreakpoint.h"
 #include "lldb/Target/ThreadPlanStepOverRange.h"
 #include "lldb/Target/ThreadPlanStepThrough.h"
+#include "lldb/Target/ThreadPlanStepThroughGenericTrampoline.h"
 #include "lldb/Target/ThreadPlanStepUntil.h"
 #include "lldb/Target/ThreadSpec.h"
 #include "lldb/Target/UnwindLLDB.h"
@@ -1368,6 +1369,17 @@
   return thread_plan_sp;
 }
 
+ThreadPlanSP Thread::QueueThreadPlanForStepThroughGenericTrampoline(
+    bool abort_other_plans, lldb::RunMode stop_other_threads, Status &status) {
+  ThreadPlanSP thread_plan_sp(
+      new ThreadPlanStepThroughGenericTrampoline(*this, stop_other_threads));
+
+  if (!thread_plan_sp || !thread_plan_sp->ValidatePlan(nullptr))
+    return ThreadPlanSP();
+  status = QueueThreadPlan(thread_plan_sp, abort_other_plans);
+  return thread_plan_sp;
+}
+
 ThreadPlanSP Thread::QueueThreadPlanForRunToAddress(bool abort_other_plans,
                                                     Address &target_addr,
                                                     bool stop_other_threads,
Index: lldb/source/Target/TargetProperties.td
===================================================================
--- lldb/source/Target/TargetProperties.td
+++ lldb/source/Target/TargetProperties.td
@@ -182,6 +182,9 @@
   def DebugUtilityExpression: Property<"debug-utility-expression", "Boolean">,
     DefaultFalse,
     Desc<"Enable debugging of LLDB-internal utility expressions.">;
+  def EnableTrampolineSupport: Property<"enable-trampoline-support", "Boolean">,
+    Global, DefaultTrue,
+    Desc<"Enable trampoline support in LLDB. Trampoline support includes stepping through trampolines directly to their targets, stepping out of trampolines directly to their callers, and automatically filtering out trampolines as possible breakpoint locations when set by name.">;
 }
 
 let Definition = "process_experimental" in {
Index: lldb/source/Target/Target.cpp
===================================================================
--- lldb/source/Target/Target.cpp
+++ lldb/source/Target/Target.cpp
@@ -4770,6 +4770,12 @@
       nullptr, idx, g_target_properties[idx].default_uint_value != 0);
 }
 
+bool TargetProperties::GetEnableTrampolineSupport() const {
+  const uint32_t idx = ePropertyEnableTrampolineSupport;
+  return m_collection_sp->GetPropertyAtIndexAsBoolean(
+      nullptr, idx, g_target_properties[idx].default_uint_value != 0);
+}
+
 void TargetProperties::SetDebugUtilityExpression(bool debug) {
   const uint32_t idx = ePropertyDebugUtilityExpression;
   m_collection_sp->SetPropertyAtIndexAsBoolean(nullptr, idx, debug);
Index: lldb/source/Target/CMakeLists.txt
===================================================================
--- lldb/source/Target/CMakeLists.txt
+++ lldb/source/Target/CMakeLists.txt
@@ -66,6 +66,7 @@
   ThreadPlanStepOverRange.cpp
   ThreadPlanStepRange.cpp
   ThreadPlanStepThrough.cpp
+  ThreadPlanStepThroughGenericTrampoline.cpp
   ThreadPlanStepUntil.cpp
   ThreadPlanTracer.cpp
   ThreadPlanStack.cpp
Index: lldb/source/Symbol/Function.cpp
===================================================================
--- lldb/source/Symbol/Function.cpp
+++ lldb/source/Symbol/Function.cpp
@@ -231,10 +231,11 @@
 //
 Function::Function(CompileUnit *comp_unit, lldb::user_id_t func_uid,
                    lldb::user_id_t type_uid, const Mangled &mangled, Type *type,
-                   const AddressRange &range)
+                   const AddressRange &range, bool is_generic_trampoline)
     : UserID(func_uid), m_comp_unit(comp_unit), m_type_uid(type_uid),
-      m_type(type), m_mangled(mangled), m_block(func_uid), m_range(range),
-      m_frame_base(), m_flags(), m_prologue_byte_size(0) {
+      m_type(type), m_mangled(mangled),
+      m_is_generic_trampoline(is_generic_trampoline), m_block(func_uid),
+      m_range(range), m_frame_base(), m_flags(), m_prologue_byte_size(0) {
   m_block.SetParentScope(this);
   assert(comp_unit != nullptr);
 }
Index: lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.h
===================================================================
--- lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.h
+++ lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.h
@@ -99,6 +99,8 @@
   const char *GetMangledName(const DWARFUnit *cu,
                              bool substitute_name_allowed = true) const;
 
+  bool GetIsGenericTrampoline(const DWARFUnit *cu) const;
+
   const char *GetPubname(const DWARFUnit *cu) const;
 
   bool GetDIENamesAndRanges(
Index: lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.cpp
===================================================================
--- lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.cpp
+++ lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.cpp
@@ -685,6 +685,12 @@
   return name;
 }
 
+bool
+DWARFDebugInfoEntry::GetIsGenericTrampoline(const DWARFUnit *cu) const {
+  DWARFFormValue form_value;
+  return GetAttributeValue(cu, DW_AT_trampoline, form_value) != 0;
+}
+
 // GetPubname
 //
 // Get value the name for a DIE as it should appear for a .debug_pubnames or
Index: lldb/source/Plugins/SymbolFile/DWARF/DWARFDIE.h
===================================================================
--- lldb/source/Plugins/SymbolFile/DWARF/DWARFDIE.h
+++ lldb/source/Plugins/SymbolFile/DWARF/DWARFDIE.h
@@ -28,6 +28,8 @@
   // Accessing information about a DIE
   const char *GetMangledName() const;
 
+  bool IsGenericTrampoline() const;
+
   const char *GetPubname() const;
 
   using DWARFBaseDIE::GetName;
Index: lldb/source/Plugins/SymbolFile/DWARF/DWARFDIE.cpp
===================================================================
--- lldb/source/Plugins/SymbolFile/DWARF/DWARFDIE.cpp
+++ lldb/source/Plugins/SymbolFile/DWARF/DWARFDIE.cpp
@@ -203,6 +203,13 @@
     return nullptr;
 }
 
+bool DWARFDIE::IsGenericTrampoline() const {
+  if (IsValid())
+    return m_die->GetIsGenericTrampoline(m_cu);
+  else
+    return false;
+}
+
 const char *DWARFDIE::GetPubname() const {
   if (IsValid())
     return m_die->GetPubname(m_cu);
Index: lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp
===================================================================
--- lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp
+++ lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp
@@ -2440,12 +2440,15 @@
 
     assert(func_type == nullptr || func_type != DIE_IS_BEING_PARSED);
 
+    bool is_generic_trampoline = die.IsGenericTrampoline();
+
     const user_id_t func_user_id = die.GetID();
     func_sp =
         std::make_shared<Function>(&comp_unit,
                                    func_user_id, // UserID is the DIE offset
                                    func_user_id, func_name, func_type,
-                                   func_range); // first address range
+                                   func_range, // first address range
+                                   is_generic_trampoline);
 
     if (func_sp.get() != nullptr) {
       if (frame_base.IsValid())
Index: lldb/source/Core/Module.cpp
===================================================================
--- lldb/source/Core/Module.cpp
+++ lldb/source/Core/Module.cpp
@@ -776,7 +776,12 @@
       if (!sc_list.GetContextAtIndex(i, sc))
         break;
 
+      bool is_trampoline =
+          Target::GetGlobalProperties().GetEnableTrampolineSupport() &&
+          sc.function && sc.function->IsGenericTrampoline();
+
       bool keep_it =
+          !is_trampoline &&
           NameMatchesLookupInfo(sc.GetFunctionName(), sc.GetLanguage());
       if (keep_it)
         ++i;
Index: lldb/include/lldb/Target/ThreadPlanStepThroughGenericTrampoline.h
===================================================================
--- /dev/null
+++ lldb/include/lldb/Target/ThreadPlanStepThroughGenericTrampoline.h
@@ -0,0 +1,52 @@
+//===-- ThreadPlanStepInRange.h ---------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_TARGET_THREADPLANSTEPTHROUGHGENERICTRAMPOLINE_H
+#define LLDB_TARGET_THREADPLANSTEPTHROUGHGENERICTRAMPOLINE_H
+
+#include "lldb/Target/Thread.h"
+#include "lldb/Target/ThreadPlanShouldStopHere.h"
+#include "lldb/Target/ThreadPlanStepRange.h"
+
+namespace lldb_private {
+
+class ThreadPlanStepThroughGenericTrampoline : public ThreadPlanStepRange,
+                                               public ThreadPlanShouldStopHere {
+public:
+  ThreadPlanStepThroughGenericTrampoline(Thread &thread,
+                                         lldb::RunMode stop_others);
+
+  ~ThreadPlanStepThroughGenericTrampoline() override;
+
+  void GetDescription(Stream *s, lldb::DescriptionLevel level) override;
+
+  bool ShouldStop(Event *event_ptr) override;
+  bool ValidatePlan(Stream *error) override;
+
+protected:
+  void SetFlagsToDefault() override {
+    GetFlags().Set(
+        ThreadPlanStepThroughGenericTrampoline::s_default_flag_values);
+  }
+
+private:
+  // Need an appropriate marker for the current stack so we can tell step out
+  // from step in.
+
+  static uint32_t
+      s_default_flag_values; // These are the default flag values
+                             // for the ThreadPlanStepThroughGenericTrampoline.
+  ThreadPlanStepThroughGenericTrampoline(
+      const ThreadPlanStepThroughGenericTrampoline &) = delete;
+  const ThreadPlanStepThroughGenericTrampoline &
+  operator=(const ThreadPlanStepThroughGenericTrampoline &) = delete;
+};
+
+} // namespace lldb_private
+
+#endif // LLDB_TARGET_THREADPLANSTEPTHROUGHGENERICTRAMPOLINE_H
Index: lldb/include/lldb/Target/ThreadPlanStepRange.h
===================================================================
--- lldb/include/lldb/Target/ThreadPlanStepRange.h
+++ lldb/include/lldb/Target/ThreadPlanStepRange.h
@@ -41,6 +41,7 @@
   void AddRange(const AddressRange &new_range);
 
 protected:
+  bool DoPlanExplainsStop(Event *event_ptr) override;
   bool InRange();
   lldb::FrameComparison CompareCurrentFrameToStartFrame();
   bool InSymbol();
Index: lldb/include/lldb/Target/ThreadPlanStepOverRange.h
===================================================================
--- lldb/include/lldb/Target/ThreadPlanStepOverRange.h
+++ lldb/include/lldb/Target/ThreadPlanStepOverRange.h
@@ -30,7 +30,6 @@
   bool ShouldStop(Event *event_ptr) override;
 
 protected:
-  bool DoPlanExplainsStop(Event *event_ptr) override;
   bool DoWillResume(lldb::StateType resume_state, bool current_plan) override;
 
   void SetFlagsToDefault() override {
Index: lldb/include/lldb/Target/ThreadPlan.h
===================================================================
--- lldb/include/lldb/Target/ThreadPlan.h
+++ lldb/include/lldb/Target/ThreadPlan.h
@@ -302,6 +302,7 @@
     eKindStepInRange,
     eKindRunToAddress,
     eKindStepThrough,
+    eKindStepThroughGenericTrampoline,
     eKindStepUntil
   };
 
Index: lldb/include/lldb/Target/Thread.h
===================================================================
--- lldb/include/lldb/Target/Thread.h
+++ lldb/include/lldb/Target/Thread.h
@@ -897,6 +897,27 @@
                                 bool abort_other_plans, bool stop_other_threads,
                                 Status &status);
 
+  /// Gets the plan used to step through a function with a generic trampoline. A
+  /// generic trampoline is one without a function target, which the thread plan
+  /// will attempt to step through until it finds a place where it makes sense
+  /// to stop at. 
+  /// \param[in] abort_other_plans
+  ///    \b true if we discard the currently queued plans and replace them with
+  ///    this one.
+  ///    Otherwise this plan will go on the end of the plan stack.
+  ///
+  /// \param[in] stop_other_threads
+  ///    \b true if we will stop other threads while we single step this one.
+  ///
+  /// \param[out] status
+  ///     A status with an error if queuing failed.
+  ///
+  /// \return
+  ///     A shared pointer to the newly queued thread plan, or nullptr if the
+  ///     plan could not be queued.
+  virtual lldb::ThreadPlanSP QueueThreadPlanForStepThroughGenericTrampoline(
+      bool abort_other_plans, lldb::RunMode stop_other_threads, Status &status);
+
   /// Gets the plan used to continue from the current PC.
   /// This is a simple plan, mostly useful as a backstop when you are continuing
   /// for some particular purpose.
Index: lldb/include/lldb/Target/Target.h
===================================================================
--- lldb/include/lldb/Target/Target.h
+++ lldb/include/lldb/Target/Target.h
@@ -250,6 +250,12 @@
 
   bool GetDebugUtilityExpression() const;
 
+  /// Trampoline support includes stepping through trampolines directly to their
+  /// targets, stepping out of trampolines directly to their callers, and
+  /// automatically filtering out trampolines as possible breakpoint locations
+  /// when set by name.
+  bool GetEnableTrampolineSupport() const;
+
 private:
   // Callbacks for m_launch_info.
   void Arg0ValueChangedCallback();
Index: lldb/include/lldb/Symbol/Function.h
===================================================================
--- lldb/include/lldb/Symbol/Function.h
+++ lldb/include/lldb/Symbol/Function.h
@@ -436,9 +436,14 @@
   ///
   /// \param[in] range
   ///     The section offset based address for this function.
+  /// \param[in] generic_trampoline
+  ///     If this function is a generic trampoline. A generic trampoline 
+  ///     is a function without any annotations on what the trampoline 
+  ///     target is.
   Function(CompileUnit *comp_unit, lldb::user_id_t func_uid,
            lldb::user_id_t func_type_uid, const Mangled &mangled,
-           Type *func_type, const AddressRange &range);
+           Type *func_type, const AddressRange &range,
+           bool generic_trampoline = false);
 
   /// Destructor.
   ~Function() override;
@@ -550,6 +555,10 @@
   ///     A type object pointer.
   Type *GetType();
 
+  bool IsGenericTrampoline() const {
+    return m_is_generic_trampoline;
+  }
+
   /// Get const accessor for the type that describes the function return value
   /// type, and parameter types.
   ///
@@ -650,6 +659,8 @@
   /// information.
   Mangled m_mangled;
 
+  bool m_is_generic_trampoline;
+
   /// All lexical blocks contained in this function.
   Block m_block;
 
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to