[Lldb-commits] [lldb] 03a6b85 - [lldb/Target] Add Assert StackFrame Recognizer

Med Ismail Bennani via lldb-commits lldb-commits at lists.llvm.org
Tue Jan 28 09:21:56 PST 2020


Author: Med Ismail Bennani
Date: 2020-01-28T18:21:29+01:00
New Revision: 03a6b858fde5c644ec16b1fddd8e10aa9ef3f0ad

URL: https://github.com/llvm/llvm-project/commit/03a6b858fde5c644ec16b1fddd8e10aa9ef3f0ad
DIFF: https://github.com/llvm/llvm-project/commit/03a6b858fde5c644ec16b1fddd8e10aa9ef3f0ad.diff

LOG: [lldb/Target] Add Assert StackFrame Recognizer

When a thread stops, this checks depending on the platform if the top frame is
an abort stack frame. If so, it looks for an assert stack frame in the upper
frames and set it as the most relavant frame when found.

To do so, the StackFrameRecognizer class holds a "Most Relevant Frame" and a
"cooked" stop reason description. 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

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

Signed-off-by: Med Ismail Bennani <medismail.bennani at gmail.com>

Added: 
    lldb/include/lldb/Target/AssertFrameRecognizer.h
    lldb/source/Target/AssertFrameRecognizer.cpp
    lldb/test/Shell/Recognizer/Inputs/assert.c
    lldb/test/Shell/Recognizer/assert.test

Modified: 
    lldb/docs/use/formatting.rst
    lldb/include/lldb/Core/FormatEntity.h
    lldb/include/lldb/Target/StackFrameRecognizer.h
    lldb/include/lldb/Target/Thread.h
    lldb/packages/Python/lldbsuite/test/functionalities/inferior-assert/TestInferiorAssert.py
    lldb/packages/Python/lldbsuite/test/lang/objc/exceptions/TestObjCExceptions.py
    lldb/source/API/SBThread.cpp
    lldb/source/Core/FormatEntity.cpp
    lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp
    lldb/source/Target/CMakeLists.txt
    lldb/source/Target/Process.cpp
    lldb/source/Target/StackFrameRecognizer.cpp
    lldb/source/Target/Thread.cpp

Removed: 
    


################################################################################
diff  --git a/lldb/docs/use/formatting.rst b/lldb/docs/use/formatting.rst
index 939c4e18f749..e2644d50217b 100644
--- a/lldb/docs/use/formatting.rst
+++ b/lldb/docs/use/formatting.rst
@@ -134,7 +134,9 @@ A complete list of currently supported format string variables is listed below:
 +---------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
 | ``thread.queue``                                  | The queue name of the thread if the target OS supports dispatch queues                                                                                                                                                                                                                      |
 +---------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
-| ``thread.stop-reason``                            | A textual reason each thread stopped                                                                                                                                                                                                                                                        |
+| ``thread.stop-reason``                            | A textual reason why the thread stopped. If the thread have a recognized frame, this displays its recognized stop reason. Otherwise, gets the stop info description.                                                                                                                        |
++---------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+| ``thread.stop-reason-raw``                        | A textual reason why the thread stopped. Always returns stop info description.                                                                                                                                                                                                              |
 +---------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
 | ``thread.return-value``                           | The return value of the latest step operation (currently only for step-out.)                                                                                                                                                                                                                |
 +---------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+

diff  --git a/lldb/include/lldb/Core/FormatEntity.h b/lldb/include/lldb/Core/FormatEntity.h
index 8ee320b0ebb1..106b0706ebf9 100644
--- a/lldb/include/lldb/Core/FormatEntity.h
+++ b/lldb/include/lldb/Core/FormatEntity.h
@@ -61,6 +61,7 @@ class FormatEntity {
       ThreadName,
       ThreadQueue,
       ThreadStopReason,
+      ThreadStopReasonRaw,
       ThreadReturnValue,
       ThreadCompletedExpression,
       ScriptThread,

diff  --git a/lldb/include/lldb/Target/AssertFrameRecognizer.h b/lldb/include/lldb/Target/AssertFrameRecognizer.h
new file mode 100644
index 000000000000..4acfefa9edfb
--- /dev/null
+++ b/lldb/include/lldb/Target/AssertFrameRecognizer.h
@@ -0,0 +1,55 @@
+//===-- AssertFrameRecognizer.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_AssertFrameRecognizer_h_
+#define liblldb_AssertFrameRecognizer_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 {
+
+/// Registers the assert stack frame recognizer.
+///
+/// \param[in] process
+///    The process that is currently asserting. This will give us information on
+///    the target and the platform.
+void RegisterAssertFrameRecognizer(Process *process);
+
+/// \class AssertRecognizedStackFrame
+///
+/// Holds the stack frame where the assert is called from.
+class AssertRecognizedStackFrame : public RecognizedStackFrame {
+public:
+  AssertRecognizedStackFrame(lldb::StackFrameSP most_relevant_frame_sp);
+  lldb::StackFrameSP GetMostRelevantFrame() override;
+  llvm::StringRef GetStopDescription() override;
+
+private:
+  lldb::StackFrameSP m_most_relevant_frame;
+};
+
+/// \class AssertFrameRecognizer
+///
+/// When a thread stops, it checks depending on the platform if the top frame is
+/// an abort stack frame. If so, it looks for an assert stack frame in the upper
+/// frames and set it as the most relavant frame when found.
+class AssertFrameRecognizer : public StackFrameRecognizer {
+public:
+  std::string GetName() override { return "Assert StackFrame Recognizer"; }
+  lldb::RecognizedStackFrameSP
+  RecognizeFrame(lldb::StackFrameSP frame_sp) override;
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_AssertFrameRecognizer_h_

diff  --git a/lldb/include/lldb/Target/StackFrameRecognizer.h b/lldb/include/lldb/Target/StackFrameRecognizer.h
index 2021ac5215f3..59febd5c1f4e 100644
--- a/lldb/include/lldb/Target/StackFrameRecognizer.h
+++ b/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 @@ class RecognizedStackFrame
   virtual lldb::ValueObjectSP GetExceptionObject() {
     return lldb::ValueObjectSP();
   }
+  virtual lldb::StackFrameSP GetMostRelevantFrame() { return nullptr; };
+  virtual llvm::StringRef GetStopDescription() { return ""; }
   virtual ~RecognizedStackFrame(){};
 
 protected:

diff  --git a/lldb/include/lldb/Target/Thread.h b/lldb/include/lldb/Target/Thread.h
index cdc8d39251c6..1a24e2c2fc6c 100644
--- a/lldb/include/lldb/Target/Thread.h
+++ b/lldb/include/lldb/Target/Thread.h
@@ -216,6 +216,12 @@ class Thread : public std::enable_shared_from_this<Thread>,
 
   virtual void RefreshStateAfterStop() = 0;
 
+  void SelectMostRelevantFrame();
+
+  llvm::StringRef GetStopDescription();
+
+  llvm::StringRef GetStopDescriptionRaw();
+
   void WillStop();
 
   bool ShouldStop(Event *event_ptr);

diff  --git a/lldb/packages/Python/lldbsuite/test/functionalities/inferior-assert/TestInferiorAssert.py b/lldb/packages/Python/lldbsuite/test/functionalities/inferior-assert/TestInferiorAssert.py
index 36fdc8622948..ab81bde771d1 100644
--- a/lldb/packages/Python/lldbsuite/test/functionalities/inferior-assert/TestInferiorAssert.py
+++ b/lldb/packages/Python/lldbsuite/test/functionalities/inferior-assert/TestInferiorAssert.py
@@ -104,12 +104,13 @@ def set_breakpoint(self, line):
     def check_stop_reason(self):
         matched = lldbplatformutil.match_android_device(
             self.getArchitecture(), valid_api_levels=list(range(1, 16 + 1)))
-        if matched:
-            # On android until API-16 the abort() call ended in a sigsegv
-            # instead of in a sigabrt
-            stop_reason = 'stop reason = signal SIGSEGV'
-        else:
-            stop_reason = 'stop reason = signal SIGABRT'
+
+        target = self.dbg.GetTargetAtIndex(0)
+        process = target.GetProcess()
+
+        thread = lldbutil.get_stopped_thread(process, lldb.eStopReasonSignal)
+
+        stop_reason = 'stop reason = ' + thread.GetStopDescription(256)
 
         # The stop reason of the thread should be an abort signal or exception.
         self.expect("thread list", STOPPED_DUE_TO_ASSERT,
@@ -173,6 +174,22 @@ def inferior_asserting_registers(self):
         self.runCmd("run", RUN_SUCCEEDED)
         self.check_stop_reason()
 
+        # change current frame to frame 0, since recognizer might have selected
+        # 
diff erent frame.
+        target = self.dbg.GetTargetAtIndex(0)
+        self.assertTrue(target, VALID_TARGET)
+
+        process = target.GetProcess()
+        self.assertTrue(process.IsValid(), "current process is valid")
+
+        thread = process.GetSelectedThread()
+        self.assertTrue(thread.IsValid(), "current thread is valid")
+
+        thread.SetSelectedFrame(0)
+        frame = thread.GetSelectedFrame()
+
+        self.assertTrue(thread.GetFrameAtIndex(0) == frame, "Frame #0 selected")
+
         # lldb should be able to read from registers from the inferior after
         # asserting.
         lldbplatformutil.check_first_register_readable(self)

diff  --git a/lldb/packages/Python/lldbsuite/test/lang/objc/exceptions/TestObjCExceptions.py b/lldb/packages/Python/lldbsuite/test/lang/objc/exceptions/TestObjCExceptions.py
index ce9ee8e027ff..f7d18e626e01 100644
--- a/lldb/packages/Python/lldbsuite/test/lang/objc/exceptions/TestObjCExceptions.py
+++ b/lldb/packages/Python/lldbsuite/test/lang/objc/exceptions/TestObjCExceptions.py
@@ -25,8 +25,8 @@ def test_objc_exceptions_at_throw(self):
         launch_info = lldb.SBLaunchInfo(["a.out", "0"])
         lldbutil.run_to_name_breakpoint(self, "objc_exception_throw", launch_info=launch_info)
 
-        self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT,
-                    substrs=['stopped', 'stop reason = breakpoint'])
+        self.expect("thread list",
+            substrs=['stopped', 'stop reason = hit Objective-C exception'])
 
         self.expect('thread exception', substrs=[
                 '(NSException *) exception = ',

diff  --git a/lldb/source/API/SBThread.cpp b/lldb/source/API/SBThread.cpp
index f90e93130960..9516d72da047 100644
--- a/lldb/source/API/SBThread.cpp
+++ b/lldb/source/API/SBThread.cpp
@@ -325,7 +325,8 @@ size_t SBThread::GetStopDescription(char *dst, size_t dst_len) {
 
       StopInfoSP stop_info_sp = exe_ctx.GetThreadPtr()->GetStopInfo();
       if (stop_info_sp) {
-        const char *stop_desc = stop_info_sp->GetDescription();
+        const char *stop_desc =
+            exe_ctx.GetThreadPtr()->GetStopDescription().data();
         if (stop_desc) {
           if (dst)
             return ::snprintf(dst, dst_len, "%s", stop_desc);

diff  --git a/lldb/source/Core/FormatEntity.cpp b/lldb/source/Core/FormatEntity.cpp
index 4d43df9c3a63..e47f109eb41a 100644
--- a/lldb/source/Core/FormatEntity.cpp
+++ b/lldb/source/Core/FormatEntity.cpp
@@ -166,6 +166,7 @@ static FormatEntity::Entry::Definition g_thread_child_entries[] = {
     ENTRY("queue", ThreadQueue),
     ENTRY("name", ThreadName),
     ENTRY("stop-reason", ThreadStopReason),
+    ENTRY("stop-reason-raw", ThreadStopReasonRaw),
     ENTRY("return-value", ThreadReturnValue),
     ENTRY("completed-expression", ThreadCompletedExpression),
 };
@@ -328,6 +329,7 @@ const char *FormatEntity::Entry::TypeToCString(Type t) {
     ENUM_TO_CSTR(ThreadName);
     ENUM_TO_CSTR(ThreadQueue);
     ENUM_TO_CSTR(ThreadStopReason);
+    ENUM_TO_CSTR(ThreadStopReasonRaw);
     ENUM_TO_CSTR(ThreadReturnValue);
     ENUM_TO_CSTR(ThreadCompletedExpression);
     ENUM_TO_CSTR(ScriptThread);
@@ -1273,15 +1275,23 @@ bool FormatEntity::Format(const Entry &entry, Stream &s,
 
   case Entry::Type::ThreadStopReason:
     if (exe_ctx) {
-      Thread *thread = exe_ctx->GetThreadPtr();
-      if (thread) {
-        StopInfoSP stop_info_sp = thread->GetStopInfo();
-        if (stop_info_sp && stop_info_sp->IsValid()) {
-          const char *cstr = stop_info_sp->GetDescription();
-          if (cstr && cstr[0]) {
-            s.PutCString(cstr);
-            return true;
-          }
+      if (Thread *thread = exe_ctx->GetThreadPtr()) {
+        llvm::StringRef stop_description = thread->GetStopDescription();
+        if (!stop_description.empty()) {
+          s.PutCString(stop_description);
+          return true;
+        }
+      }
+    }
+    return false;
+
+  case Entry::Type::ThreadStopReasonRaw:
+    if (exe_ctx) {
+      if (Thread *thread = exe_ctx->GetThreadPtr()) {
+        llvm::StringRef stop_description = thread->GetStopDescriptionRaw();
+        if (!stop_description.empty()) {
+          s.PutCString(stop_description);
+          return true;
         }
       }
     }

diff  --git a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp
index e5c816d7eef5..beb5b13d4950 100644
--- a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp
+++ b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp
@@ -2673,6 +2673,10 @@ class ObjCExceptionRecognizedStackFrame : public RecognizedStackFrame {
   ValueObjectSP exception;
 
   lldb::ValueObjectSP GetExceptionObject() override { return exception; }
+
+  llvm::StringRef GetStopDescription() override {
+    return "hit Objective-C exception";
+  }
 };
 
 class ObjCExceptionThrowFrameRecognizer : public StackFrameRecognizer {

diff  --git a/lldb/source/Target/AssertFrameRecognizer.cpp b/lldb/source/Target/AssertFrameRecognizer.cpp
new file mode 100644
index 000000000000..489cba19ab13
--- /dev/null
+++ b/lldb/source/Target/AssertFrameRecognizer.cpp
@@ -0,0 +1,173 @@
+#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/AssertFrameRecognizer.h"
+
+using namespace llvm;
+using namespace lldb;
+using namespace lldb_private;
+
+namespace lldb_private {
+/// Fetches the abort frame location depending on the current platform.
+///
+/// \param[in] process_sp
+///    The process that is currently aborting. This will give us information on
+///    the target and the platform.
+/// \return
+///    If the platform is supported, returns an optional tuple containing
+///    the abort module as a \a FileSpec and the symbol name as a \a StringRef.
+///    Otherwise, returns \a llvm::None.
+llvm::Optional<std::tuple<FileSpec, StringRef>>
+GetAbortLocation(Process *process) {
+  Target &target = process->GetTarget();
+
+  StringRef module_name;
+  StringRef 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, "AssertFrameRecognizer::GetAbortLocation Unsupported OS");
+    return llvm::None;
+  }
+
+  return std::make_tuple(FileSpec(module_name), symbol_name);
+}
+
+/// Fetches the assert frame location depending on the current platform.
+///
+/// \param[in] process_sp
+///    The process that is currently asserting. This will give us information on
+///    the target and the platform.
+/// \return
+///    If the platform is supported, returns an optional tuple containing
+///    the asserting frame module as  a \a FileSpec and the symbol name as a \a
+///    StringRef.
+///    Otherwise, returns \a llvm::None.
+llvm::Optional<std::tuple<FileSpec, StringRef>>
+GetAssertLocation(Process *process) {
+  Target &target = process->GetTarget();
+
+  StringRef module_name;
+  StringRef 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, "AssertFrameRecognizer::GetAbortLocation Unsupported OS");
+    return llvm::None;
+  }
+
+  return std::make_tuple(FileSpec(module_name), symbol_name);
+}
+
+void RegisterAssertFrameRecognizer(Process *process) {
+  static llvm::once_flag g_once_flag;
+  llvm::call_once(g_once_flag, [process]() {
+    auto abort_location = GetAbortLocation(process);
+
+    if (!abort_location.hasValue())
+      return;
+
+    FileSpec module_spec;
+    StringRef function_name;
+    std::tie(module_spec, function_name) = *abort_location;
+
+    StackFrameRecognizerManager::AddRecognizer(
+        StackFrameRecognizerSP(new AssertFrameRecognizer()),
+        module_spec.GetFilename(), ConstString(function_name), false);
+  });
+}
+
+} // namespace lldb_private
+
+AssertRecognizedStackFrame::AssertRecognizedStackFrame(
+    StackFrameSP most_relevant_frame_sp)
+    : m_most_relevant_frame(most_relevant_frame_sp) {}
+
+lldb::RecognizedStackFrameSP
+AssertFrameRecognizer::RecognizeFrame(lldb::StackFrameSP frame_sp) {
+  ThreadSP thread_sp = frame_sp->GetThread();
+  ProcessSP process_sp = thread_sp->GetProcess();
+
+  auto assert_location = GetAssertLocation(process_sp.get());
+
+  if (!assert_location.hasValue())
+    return RecognizedStackFrameSP();
+
+  FileSpec module_spec;
+  StringRef function_name;
+  std::tie(module_spec, function_name) = *assert_location;
+
+  const uint32_t frames_to_fetch = 5;
+  const uint32_t last_frame_index = frames_to_fetch - 1;
+  StackFrameSP prev_frame_sp = nullptr;
+
+  // Fetch most relevant frame
+  for (uint32_t frame_index = 0; frame_index < frames_to_fetch; frame_index++) {
+    prev_frame_sp = thread_sp->GetStackFrameAtIndex(frame_index);
+
+    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().FileEquals(module_spec) &&
+        sym_ctx.GetFunctionName() == ConstString(function_name)) {
+
+      // We go a frame beyond the assert location because the most relevant
+      // frame for the user is the one in which the assert function was called.
+      // If the assert location is the last frame fetched, then it is set as
+      // the most relevant frame.
+
+      StackFrameSP most_relevant_frame_sp = thread_sp->GetStackFrameAtIndex(
+          std::min(frame_index + 1, last_frame_index));
+
+      // Pass assert location to AbortRecognizedStackFrame to set as most
+      // relevant frame.
+      return lldb::RecognizedStackFrameSP(
+          new AssertRecognizedStackFrame(most_relevant_frame_sp));
+    }
+  }
+
+  return RecognizedStackFrameSP();
+};
+
+lldb::StackFrameSP AssertRecognizedStackFrame::GetMostRelevantFrame() {
+  return m_most_relevant_frame;
+}
+
+llvm::StringRef AssertRecognizedStackFrame::GetStopDescription() {
+  return "hit program assert";
+}

diff  --git a/lldb/source/Target/CMakeLists.txt b/lldb/source/Target/CMakeLists.txt
index 12ae2a9dd9da..893065442e80 100644
--- a/lldb/source/Target/CMakeLists.txt
+++ b/lldb/source/Target/CMakeLists.txt
@@ -8,6 +8,7 @@ lldb_tablegen(TargetPropertiesEnum.inc -gen-lldb-property-enum-defs
 
 add_lldb_library(lldbTarget
   ABI.cpp
+  AssertFrameRecognizer.cpp
   ExecutionContext.cpp
   InstrumentationRuntime.cpp
   InstrumentationRuntimeStopInfo.cpp

diff  --git a/lldb/source/Target/Process.cpp b/lldb/source/Target/Process.cpp
index b2213d7fc962..9dd6af9143a0 100644
--- a/lldb/source/Target/Process.cpp
+++ b/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/AssertFrameRecognizer.h"
 #include "lldb/Target/DynamicLoader.h"
 #include "lldb/Target/InstrumentationRuntime.h"
 #include "lldb/Target/JITLoader.h"
@@ -538,6 +539,8 @@ Process::Process(lldb::TargetSP target_sp, ListenerSP listener_sp,
       target_sp->GetPlatform()->GetDefaultMemoryCacheLineSize();
   if (!value_sp->OptionWasSet() && platform_cache_line_size != 0)
     value_sp->SetUInt64Value(platform_cache_line_size);
+
+  RegisterAssertFrameRecognizer(this);
 }
 
 Process::~Process() {
@@ -934,11 +937,17 @@ bool Process::HandleProcessStateChangedEvent(const EventSP &event_sp,
         Debugger &debugger = process_sp->GetTarget().GetDebugger();
         if (debugger.GetTargetList().GetSelectedTarget().get() ==
             &process_sp->GetTarget()) {
+          ThreadSP thread_sp = process_sp->GetThreadList().GetSelectedThread();
+
+          if (!thread_sp || !thread_sp->IsValid())
+            return false;
+
           const bool only_threads_with_stop_reason = true;
-          const uint32_t start_frame = 0;
+          const uint32_t start_frame = thread_sp->GetSelectedFrameIndex();
           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,

diff  --git a/lldb/source/Target/StackFrameRecognizer.cpp b/lldb/source/Target/StackFrameRecognizer.cpp
index fa71ef7362d9..296f2e4366f3 100644
--- a/lldb/source/Target/StackFrameRecognizer.cpp
+++ b/lldb/source/Target/StackFrameRecognizer.cpp
@@ -92,7 +92,7 @@ class StackFrameRecognizerManagerImpl {
 
   StackFrameRecognizerSP GetRecognizerForFrame(StackFrameSP frame) {
     const SymbolContext &symctx =
-        frame->GetSymbolContext(eSymbolContextModule | eSymbolContextFunction);
+        frame->GetSymbolContext(eSymbolContextEverything);
     ConstString function_name = symctx.GetFunctionName();
     ModuleSP module_sp = symctx.module_sp;
     if (!module_sp) return StackFrameRecognizerSP();

diff  --git a/lldb/source/Target/Thread.cpp b/lldb/source/Target/Thread.cpp
index 82930ded21a1..75df5310f488 100644
--- a/lldb/source/Target/Thread.cpp
+++ b/lldb/source/Target/Thread.cpp
@@ -573,9 +573,64 @@ void Thread::SetState(StateType state) {
   m_state = state;
 }
 
+llvm::StringRef Thread::GetStopDescription() {
+  StackFrameSP frame_sp = GetStackFrameAtIndex(0);
+
+  if (!frame_sp)
+    return GetStopDescriptionRaw();
+
+  auto recognized_frame_sp = frame_sp->GetRecognizedFrame();
+
+  if (!recognized_frame_sp)
+    return GetStopDescriptionRaw();
+
+  llvm::StringRef recognized_stop_description =
+      recognized_frame_sp->GetStopDescription();
+
+  if (!recognized_stop_description.empty())
+    return recognized_stop_description;
+
+  return GetStopDescriptionRaw();
+}
+
+llvm::StringRef Thread::GetStopDescriptionRaw() {
+  StopInfoSP stop_info_sp = GetStopInfo();
+  llvm::StringRef raw_stop_description;
+  if (stop_info_sp && stop_info_sp->IsValid())
+    raw_stop_description = stop_info_sp->GetDescription();
+  return raw_stop_description;
+}
+
+void Thread::SelectMostRelevantFrame() {
+  Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_THREAD);
+
+  auto frames_list_sp = GetStackFrameList();
+
+  // Only the top frame should be recognized.
+  auto frame_sp = frames_list_sp->GetFrameAtIndex(0);
+
+  auto recognized_frame_sp = frame_sp->GetRecognizedFrame();
+
+  if (!recognized_frame_sp) {
+    LLDB_LOG(log, "Frame #0 not recognized");
+    return;
+  }
+
+  if (StackFrameSP most_relevant_frame_sp =
+          recognized_frame_sp->GetMostRelevantFrame()) {
+    LLDB_LOG(log, "Found most relevant frame at index {0}",
+             most_relevant_frame_sp->GetFrameIndex());
+    SetSelectedFrame(most_relevant_frame_sp.get());
+  } else {
+    LLDB_LOG(log, "No relevant frame!");
+  }
+}
+
 void Thread::WillStop() {
   ThreadPlan *current_plan = GetCurrentPlan();
 
+  SelectMostRelevantFrame();
+
   // FIXME: I may decide to disallow threads with no plans.  In which
   // case this should go to an assert.
 

diff  --git a/lldb/test/Shell/Recognizer/Inputs/assert.c b/lldb/test/Shell/Recognizer/Inputs/assert.c
new file mode 100644
index 000000000000..612133502915
--- /dev/null
+++ b/lldb/test/Shell/Recognizer/Inputs/assert.c
@@ -0,0 +1,9 @@
+#include <assert.h>
+
+int main() {
+  int a = 42;
+  assert(a == 42);
+  a--;
+  assert(a == 42);
+  return 0;
+}

diff  --git a/lldb/test/Shell/Recognizer/assert.test b/lldb/test/Shell/Recognizer/assert.test
new file mode 100644
index 000000000000..9b4aa21611e0
--- /dev/null
+++ b/lldb/test/Shell/Recognizer/assert.test
@@ -0,0 +1,13 @@
+# UNSUPPORTED: system-windows
+# RUN: %clang_host -g -O0 %S/Inputs/assert.c -o %t.out
+# RUN: %lldb -b -s %s %t.out | FileCheck %s
+run
+# CHECK: thread #{{.*}}stop reason = hit program assert
+frame info
+# CHECK: frame #{{.*}}`main at assert.c
+frame recognizer info 0
+# CHECK: frame 0 is recognized by Assert StackFrame Recognizer
+set set thread-format "{${thread.stop-reason-raw}}\n"
+thread info
+# CHECK: signal SIGABRT
+q


        


More information about the lldb-commits mailing list