mib created this revision.
mib added reviewers: jingham, friss, LLDB.
mib added a project: LLDB.
Herald added subscribers: lldb-commits, jfb, mgorny.

When a thread stops on an abort frame, the recognizer will unwind the stack to
look for the frame from which the assert was triggered.
Once located, it will set this frame as the current one and change the stop
reason.

To do so, the StackFrameRecognizer class holds a "Most Relevant Frame" and a
StopInfo attribute. When the thread is about to stop, it checks if the
current frame is recognized, and if so, it fetches the recognized frame's
attributes and applies them.

rdar://58528686

Signed-off-by: Med Ismail Bennani <medismail.benn...@gmail.com>


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D73303

Files:
  lldb/include/lldb/Target/AbortRecognizer.h
  lldb/include/lldb/Target/StackFrameRecognizer.h
  lldb/include/lldb/Target/StopInfo.h
  lldb/include/lldb/Target/Thread.h
  lldb/source/Target/AbortRecognizer.cpp
  lldb/source/Target/CMakeLists.txt
  lldb/source/Target/Process.cpp
  lldb/source/Target/StopInfo.cpp
  lldb/source/Target/Thread.cpp
  lldb/test/Shell/Recognizer/Inputs/abort.c
  lldb/test/Shell/Recognizer/abort.test

Index: lldb/test/Shell/Recognizer/abort.test
===================================================================
--- /dev/null
+++ lldb/test/Shell/Recognizer/abort.test
@@ -0,0 +1,10 @@
+# UNSUPPORTED: system-windows
+# RUN: %clang_host -g -O0 %S/Inputs/abort.c -o %t.out
+# RUN: %lldb -b -s %s %t.out | FileCheck %s
+run
+# CHECK: thread #{{.*}}stop reason = signal SIGABRT
+frame info
+# CHECK: frame #4: {{.*}}`main at abort.c
+frame recognizer info 0
+# CHECK: frame 0 is recognized by Abort StackFrame Recognizer
+q
Index: lldb/test/Shell/Recognizer/Inputs/abort.c
===================================================================
--- /dev/null
+++ lldb/test/Shell/Recognizer/Inputs/abort.c
@@ -0,0 +1,9 @@
+#include <assert.h>
+
+int main() {
+  int a = 42;
+  assert(a == 42);
+  a--;
+  assert(a == 42);
+  return 0;
+}
Index: lldb/source/Target/Thread.cpp
===================================================================
--- lldb/source/Target/Thread.cpp
+++ lldb/source/Target/Thread.cpp
@@ -573,9 +573,42 @@
   m_state = state;
 }
 
+void Thread::ApplyMostRelevantFrames() {
+  if (!m_curr_frames_sp)
+    return;
+
+  auto cur_frame = m_curr_frames_sp->GetFrameAtIndex(0);
+  SymbolContext sym_ctx = cur_frame->GetSymbolContext(eSymbolContextEverything);
+
+  auto recognized_frame_sp = cur_frame->GetRecognizedFrame();
+
+  if (!recognized_frame_sp)
+    return;
+
+  Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_TARGET);
+
+  if (StackFrameSP most_relevant_frame_sp =
+          recognized_frame_sp->GetMostRelevantFrame()) {
+    LLDB_LOG(log, "Found most relevant frame {0}",
+             most_relevant_frame_sp->GetFrameIndex());
+    SetSelectedFrame(most_relevant_frame_sp.get());
+  } else {
+    LLDB_LOG(log, "No relevant frame!");
+  }
+
+  if (StopInfoSP stop_info_sp = recognized_frame_sp->GetStopInfo()) {
+    LLDB_LOG(log, "Found stop reason {0}", stop_info_sp->GetStopReason());
+    SetStopInfo(stop_info_sp);
+  } else {
+    LLDB_LOG(log, "No stop reason for recognized frame!");
+  }
+}
+
 void Thread::WillStop() {
   ThreadPlan *current_plan = GetCurrentPlan();
 
+  ApplyMostRelevantFrames();
+
   // FIXME: I may decide to disallow threads with no plans.  In which
   // case this should go to an assert.
 
Index: lldb/source/Target/StopInfo.cpp
===================================================================
--- lldb/source/Target/StopInfo.cpp
+++ lldb/source/Target/StopInfo.cpp
@@ -1016,6 +1016,23 @@
   }
 };
 
+// StopInfoRecognizedFrame
+
+class StopInfoRecognizedFrame : public StopInfoException {
+public:
+  StopInfoRecognizedFrame(Thread &thread, const char *description)
+      : StopInfoException(thread, description) {}
+
+  ~StopInfoRecognizedFrame() override = default;
+
+  const char *GetDescription() override {
+    if (m_description.empty())
+      return "recognized frame:";
+    else
+      return m_description.c_str();
+  }
+};
+
 // StopInfoThreadPlan
 
 class StopInfoThreadPlan : public StopInfo {
@@ -1133,6 +1150,12 @@
   return StopInfoSP(new StopInfoException(thread, description));
 }
 
+StopInfoSP
+StopInfo::CreateStopReasonForRecognizedFrame(Thread &thread,
+                                             const char *description) {
+  return StopInfoSP(new StopInfoRecognizedFrame(thread, description));
+}
+
 StopInfoSP StopInfo::CreateStopReasonWithExec(Thread &thread) {
   return StopInfoSP(new StopInfoExec(thread));
 }
Index: lldb/source/Target/Process.cpp
===================================================================
--- lldb/source/Target/Process.cpp
+++ lldb/source/Target/Process.cpp
@@ -38,6 +38,7 @@
 #include "lldb/Symbol/Function.h"
 #include "lldb/Symbol/Symbol.h"
 #include "lldb/Target/ABI.h"
+#include "lldb/Target/AbortRecognizer.h"
 #include "lldb/Target/DynamicLoader.h"
 #include "lldb/Target/InstrumentationRuntime.h"
 #include "lldb/Target/JITLoader.h"
@@ -538,6 +539,8 @@
       target_sp->GetPlatform()->GetDefaultMemoryCacheLineSize();
   if (!value_sp->OptionWasSet() && platform_cache_line_size != 0)
     value_sp->SetUInt64Value(platform_cache_line_size);
+
+  RegisterAbortRecognizer(this);
 }
 
 Process::~Process() {
@@ -934,11 +937,27 @@
         Debugger &debugger = process_sp->GetTarget().GetDebugger();
         if (debugger.GetTargetList().GetSelectedTarget().get() ==
             &process_sp->GetTarget()) {
+          ThreadSP cur_thread = process_sp->GetThreadList().GetSelectedThread();
+
+          if (!cur_thread || !cur_thread->IsValid())
+            return false;
+
+          uint32_t mrf_idx = 0;
+
+          auto frame_sp = cur_thread->GetStackFrameAtIndex(mrf_idx);
+
+          if (RecognizedStackFrameSP recognized_frame_sp =
+                  frame_sp->GetRecognizedFrame())
+            if (StackFrameSP mrf_sp =
+                    recognized_frame_sp->GetMostRelevantFrame())
+              mrf_idx = mrf_sp->GetFrameIndex();
+
           const bool only_threads_with_stop_reason = true;
-          const uint32_t start_frame = 0;
+          const uint32_t start_frame = mrf_idx;
           const uint32_t num_frames = 1;
           const uint32_t num_frames_with_source = 1;
           const bool stop_format = true;
+
           process_sp->GetStatus(*stream);
           process_sp->GetThreadStatus(*stream, only_threads_with_stop_reason,
                                       start_frame, num_frames,
Index: lldb/source/Target/CMakeLists.txt
===================================================================
--- lldb/source/Target/CMakeLists.txt
+++ lldb/source/Target/CMakeLists.txt
@@ -8,6 +8,7 @@
 
 add_lldb_library(lldbTarget
   ABI.cpp
+  AbortRecognizer.cpp
   ExecutionContext.cpp
   InstrumentationRuntime.cpp
   InstrumentationRuntimeStopInfo.cpp
Index: lldb/source/Target/AbortRecognizer.cpp
===================================================================
--- /dev/null
+++ lldb/source/Target/AbortRecognizer.cpp
@@ -0,0 +1,186 @@
+//===-- AbortRecognizer.cpp -------------------------------------*- 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/Module.h"
+#include "lldb/Symbol/Function.h"
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/StackFrameList.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/Logging.h"
+
+#include "lldb/Target/AbortRecognizer.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+llvm::Optional<std::tuple<FileSpec, ConstString>>
+AbortRecognizerHandler::GetAbortLocation(Process *process) {
+  Target &target = process->GetTarget();
+
+  std::string module_name;
+  std::string symbol_name;
+
+  switch (target.GetArchitecture().GetTriple().getOS()) {
+  case llvm::Triple::Darwin:
+  case llvm::Triple::MacOSX:
+    module_name = "libsystem_kernel.dylib";
+    symbol_name = "__pthread_kill";
+    break;
+  case llvm::Triple::Linux:
+    module_name = "libc.so.6";
+    symbol_name = "__GI_raise";
+    break;
+  default:
+    Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND));
+    LLDB_LOG(log, "Abort Recognizer::GetAbortLocation Unsupported OS");
+    return llvm::None;
+  }
+
+  return std::make_tuple(FileSpec(module_name), ConstString(symbol_name));
+}
+
+llvm::Optional<std::tuple<FileSpec, ConstString>>
+AbortRecognizerHandler::GetAssertLocation(Process *process) {
+  Target &target = process->GetTarget();
+
+  std::string module_name;
+  std::string symbol_name;
+
+  switch (target.GetArchitecture().GetTriple().getOS()) {
+  case llvm::Triple::Darwin:
+  case llvm::Triple::MacOSX:
+    module_name = "libsystem_c.dylib";
+    symbol_name = "__assert_rtn";
+    break;
+  case llvm::Triple::Linux:
+    module_name = "libc.so.6";
+    symbol_name = "__GI___assert_fail";
+    break;
+  default:
+    Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND));
+    LLDB_LOG(log, "Abort Recognizer::GetAbortLocation Unsupported OS");
+    return llvm::None;
+  }
+
+  return std::make_tuple(FileSpec(module_name), ConstString(symbol_name));
+}
+
+#pragma mark Frame recognizers
+
+AbortRecognizedStackFrame::AbortRecognizedStackFrame(
+    ThreadSP thread_sp, FileSpec module_spec, ConstString function_name) {
+  const uint32_t frames_to_fetch = 10;
+  StackFrameSP prev_frame_sp = nullptr;
+
+  // Fetch most relevant frame
+  for (uint32_t i = 0; i < frames_to_fetch; i++) {
+    prev_frame_sp = thread_sp->GetStackFrameAtIndex(i);
+
+    if (!prev_frame_sp) {
+      Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND));
+      LLDB_LOG(log, "Abort Recognizer: Hit unwinding bound ({1} frames)!",
+               frames_to_fetch);
+      break;
+    }
+
+    SymbolContext sym_ctx =
+        prev_frame_sp->GetSymbolContext(eSymbolContextEverything);
+
+    if (sym_ctx.module_sp->GetFileSpec().GetFilename() ==
+            module_spec.GetFilename() &&
+        sym_ctx.GetFunctionName() == function_name) {
+      if (i < frames_to_fetch - 1)
+        m_most_relevant_frame = thread_sp->GetStackFrameAtIndex(i + 1);
+      else
+        m_most_relevant_frame = thread_sp->GetStackFrameAtIndex(i);
+      break;
+    }
+  }
+
+  // FIXME: This breaks several tests.
+  //  m_stop_info_sp = StopInfo::CreateStopReasonForRecognizedFrame(
+  //      *thread_sp.get(), "hit program assert");
+
+  m_stop_info_sp = nullptr;
+}
+
+lldb::RecognizedStackFrameSP
+AbortFrameRecognizer::RecognizeFrame(lldb::StackFrameSP frame) {
+  // Check if called with Frame #0
+  if (frame->GetFrameIndex())
+    return RecognizedStackFrameSP();
+
+  ThreadSP thread_sp = frame->GetThread();
+  ProcessSP process_sp = thread_sp->GetProcess();
+
+  // Fetch abort location
+  auto abort_location =
+      AbortRecognizerHandler::GetAbortLocation(process_sp.get());
+
+  if (!abort_location.hasValue())
+    return RecognizedStackFrameSP();
+
+  FileSpec module_spec;
+  ConstString function_name;
+  std::tie(module_spec, function_name) = *abort_location;
+
+  SymbolContext sc = frame->GetSymbolContext(eSymbolContextModule);
+
+  // Check if frame is in abort module
+  if (!sc.module_sp ||
+      sc.module_sp->GetFileSpec().GetFilename() != module_spec.GetFilename())
+    return RecognizedStackFrameSP();
+
+  // Fetch assert location
+  auto assert_location =
+      AbortRecognizerHandler::GetAssertLocation(process_sp.get());
+
+  if (!assert_location.hasValue())
+    return RecognizedStackFrameSP();
+
+  std::tie(module_spec, function_name) = *assert_location;
+
+  // Pass assert location to AbortRecognizedStackFrame to set as most relevant
+  // frame.
+  return lldb::RecognizedStackFrameSP(
+      new AbortRecognizedStackFrame(thread_sp, module_spec, function_name));
+};
+
+lldb::StackFrameSP AbortRecognizedStackFrame::GetMostRelevantFrame() {
+  return m_most_relevant_frame;
+}
+
+lldb::StopInfoSP AbortRecognizedStackFrame::GetStopInfo() {
+  return m_stop_info_sp;
+}
+
+namespace lldb_private {
+
+void RegisterAbortRecognizer(Process *process) {
+  static llvm::once_flag g_once_flag;
+  llvm::call_once(g_once_flag, [process]() {
+    auto abort_location = AbortRecognizerHandler::GetAbortLocation(process);
+
+    if (!abort_location.hasValue())
+      return;
+
+    FileSpec module_spec;
+    ConstString function_name;
+    std::tie(module_spec, function_name) = *abort_location;
+
+    StackFrameRecognizerManager::AddRecognizer(
+        StackFrameRecognizerSP(new AbortFrameRecognizer()),
+        module_spec.GetFilename(), function_name, false);
+  });
+}
+
+} // namespace lldb_private
Index: lldb/include/lldb/Target/Thread.h
===================================================================
--- lldb/include/lldb/Target/Thread.h
+++ lldb/include/lldb/Target/Thread.h
@@ -216,6 +216,8 @@
 
   virtual void RefreshStateAfterStop() = 0;
 
+  void ApplyMostRelevantFrames();
+
   void WillStop();
 
   bool ShouldStop(Event *event_ptr);
Index: lldb/include/lldb/Target/StopInfo.h
===================================================================
--- lldb/include/lldb/Target/StopInfo.h
+++ lldb/include/lldb/Target/StopInfo.h
@@ -127,6 +127,9 @@
   static lldb::StopInfoSP
   CreateStopReasonWithException(Thread &thread, const char *description);
 
+  static lldb::StopInfoSP
+  CreateStopReasonForRecognizedFrame(Thread &thread, const char *description);
+
   static lldb::StopInfoSP CreateStopReasonWithExec(Thread &thread);
 
   static lldb::ValueObjectSP
Index: lldb/include/lldb/Target/StackFrameRecognizer.h
===================================================================
--- lldb/include/lldb/Target/StackFrameRecognizer.h
+++ lldb/include/lldb/Target/StackFrameRecognizer.h
@@ -12,6 +12,7 @@
 #include "lldb/Core/ValueObject.h"
 #include "lldb/Core/ValueObjectList.h"
 #include "lldb/Symbol/VariableList.h"
+#include "lldb/Target/StopInfo.h"
 #include "lldb/Utility/StructuredData.h"
 #include "lldb/lldb-private-forward.h"
 #include "lldb/lldb-public.h"
@@ -33,6 +34,8 @@
   virtual lldb::ValueObjectSP GetExceptionObject() {
     return lldb::ValueObjectSP();
   }
+  virtual lldb::StackFrameSP GetMostRelevantFrame() { return nullptr; };
+  virtual lldb::StopInfoSP GetStopInfo() { return nullptr; }
   virtual ~RecognizedStackFrame(){};
 
 protected:
Index: lldb/include/lldb/Target/AbortRecognizer.h
===================================================================
--- /dev/null
+++ lldb/include/lldb/Target/AbortRecognizer.h
@@ -0,0 +1,56 @@
+//===-- AbortRecognizer.cpp -------------------------------------*- 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 liblldb_AbortRegognizer_h_
+#define liblldb_AbortRegognizer_h_
+
+#include "lldb/Target/Process.h"
+#include "lldb/Target/StackFrameRecognizer.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/FileSpec.h"
+
+#include <tuple>
+
+namespace lldb_private {
+
+void RegisterAbortRecognizer(Process *process);
+
+class AbortRecognizerHandler {
+public:
+  ~AbortRecognizerHandler() = default;
+
+  static llvm::Optional<std::tuple<FileSpec, ConstString>>
+  GetAbortLocation(Process *process_sp);
+  static llvm::Optional<std::tuple<FileSpec, ConstString>>
+  GetAssertLocation(Process *process_sp);
+};
+
+#pragma mark Frame recognizers
+
+class AbortRecognizedStackFrame : public RecognizedStackFrame {
+public:
+  AbortRecognizedStackFrame(lldb::ThreadSP thread_sp, FileSpec module_spec,
+                            ConstString function_name);
+  lldb::StackFrameSP GetMostRelevantFrame() override;
+  lldb::StopInfoSP GetStopInfo() override;
+
+private:
+  lldb::StackFrameSP m_most_relevant_frame;
+  lldb::StopInfoSP m_stop_info_sp;
+};
+
+class AbortFrameRecognizer : public StackFrameRecognizer {
+public:
+  std::string GetName() override { return "Abort StackFrame Recognizer"; }
+  lldb::RecognizedStackFrameSP
+  RecognizeFrame(lldb::StackFrameSP frame) override;
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_AbortRecognizer_h_
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to