[Lldb-commits] [lldb] 2b7f328 - [lldb/Target] Add Assert StackFrame Recognizer

Med Ismail Bennani via lldb-commits lldb-commits at lists.llvm.org
Wed Feb 5 08:49:48 PST 2020


Author: Med Ismail Bennani
Date: 2020-02-05T17:49:13+01:00
New Revision: 2b7f32892b76cdfbe075300a5bf4a52e1b674bc7

URL: https://github.com/llvm/llvm-project/commit/2b7f32892b76cdfbe075300a5bf4a52e1b674bc7
DIFF: https://github.com/llvm/llvm-project/commit/2b7f32892b76cdfbe075300a5bf4a52e1b674bc7.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..02899b8d0450
--- /dev/null
+++ b/lldb/include/lldb/Target/AssertFrameRecognizer.h
@@ -0,0 +1,54 @@
+//===-- 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;
+
+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..b509e0760b31 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,10 +34,14 @@ class RecognizedStackFrame
   virtual lldb::ValueObjectSP GetExceptionObject() {
     return lldb::ValueObjectSP();
   }
+  virtual lldb::StackFrameSP GetMostRelevantFrame() { return nullptr; };
   virtual ~RecognizedStackFrame(){};
 
+  std::string GetStopDescription() { return m_stop_desc; }
+
 protected:
   lldb::ValueObjectListSP m_arguments;
+  std::string m_stop_desc;
 };
 
 /// \class StackFrameRecognizer

diff  --git a/lldb/include/lldb/Target/Thread.h b/lldb/include/lldb/Target/Thread.h
index cdc8d39251c6..e68ef08dd7d5 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();
+
+  std::string GetStopDescription();
+
+  std::string 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..edbfe70f3794 100644
--- a/lldb/packages/Python/lldbsuite/test/functionalities/inferior-assert/TestInferiorAssert.py
+++ b/lldb/packages/Python/lldbsuite/test/functionalities/inferior-assert/TestInferiorAssert.py
@@ -107,16 +107,22 @@ def check_stop_reason(self):
         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'
+            stop_reason = 'signal SIGSEGV'
         else:
-            stop_reason = 'stop reason = signal SIGABRT'
+            stop_reason = 'signal SIGABRT'
+
+        target = self.dbg.GetTargetAtIndex(0)
+        process = target.GetProcess()
+        thread = lldbutil.get_stopped_thread(process, lldb.eStopReasonSignal)
+
+        pattern = "stop reason = (" + stop_reason + "|hit program assert)"
 
         # The stop reason of the thread should be an abort signal or exception.
         self.expect("thread list", STOPPED_DUE_TO_ASSERT,
-                    substrs=['stopped',
-                             stop_reason])
+                    patterns=[pattern],
+                    substrs=['stopped'])
 
-        return stop_reason
+        return pattern
 
     def setUp(self):
         # Call super's setUp().
@@ -134,12 +140,13 @@ def inferior_asserting(self):
 
         # And it should report a backtrace that includes the assert site.
         self.expect("thread backtrace all",
-                    substrs=[stop_reason, 'main', 'argc', 'argv'])
+                    patterns=[stop_reason],
+                    substrs=['main', 'argc', 'argv'])
 
         # And it should report the correct line number.
         self.expect("thread backtrace all",
-                    substrs=[stop_reason,
-                             'main.c:%d' % self.line])
+                    patterns=[stop_reason],
+                    substrs=['main.c:%d' % self.line])
 
     def inferior_asserting_python(self):
         """Inferior asserts upon launching; lldb should catch the event and stop."""
@@ -173,6 +180,10 @@ 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.
+        self.runCmd("frame select 0", RUN_SUCCEEDED)
+
         # lldb should be able to read from registers from the inferior after
         # asserting.
         lldbplatformutil.check_first_register_readable(self)
@@ -311,5 +322,5 @@ def inferior_asserting_step(self):
 
         # And it should report the correct line number.
         self.expect("thread backtrace all",
-                    substrs=[stop_reason,
-                             'main.c:%d' % self.line])
+                    patterns=[stop_reason],
+                    substrs=['main.c:%d' % self.line])

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 6fe4f66763cb..b215cddb4ca8 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 0ba333ca08e5..442d3d12a1a7 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()) {
+        std::string 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()) {
+        std::string 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 3331a5f5aa3d..6acc23176248 100644
--- a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp
+++ b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp
@@ -2666,6 +2666,8 @@ class ObjCExceptionRecognizedStackFrame : public RecognizedStackFrame {
 
     m_arguments = ValueObjectListSP(new ValueObjectList());
     m_arguments->Append(exception);
+
+    m_stop_desc = "hit Objective-C exception";
   }
 
   ValueObjectSP exception;

diff  --git a/lldb/source/Target/AssertFrameRecognizer.cpp b/lldb/source/Target/AssertFrameRecognizer.cpp
new file mode 100644
index 000000000000..58829be93816
--- /dev/null
+++ b/lldb/source/Target/AssertFrameRecognizer.cpp
@@ -0,0 +1,195 @@
+#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 {
+/// Checkes if the module containing a symbol has debug info.
+///
+/// \param[in] target
+///    The target containing the module.
+/// \param[in] module_spec
+///    The module spec that should contain the symbol.
+/// \param[in] symbol_name
+///    The symbol's name that should be contained in the debug info.
+/// \return
+///    If  \b true the symbol was found, \b false otherwise.
+bool ModuleHasDebugInfo(Target &target, FileSpec &module_spec,
+                        StringRef symbol_name) {
+  ModuleSP module_sp = target.GetImages().FindFirstModule(module_spec);
+
+  if (!module_sp)
+    return false;
+
+  return module_sp->FindFirstSymbolWithNameAndType(ConstString(symbol_name));
+}
+
+/// 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();
+
+  FileSpec module_spec;
+  StringRef symbol_name;
+
+  switch (target.GetArchitecture().GetTriple().getOS()) {
+  case llvm::Triple::Darwin:
+  case llvm::Triple::MacOSX:
+    module_spec = FileSpec("libsystem_kernel.dylib");
+    symbol_name = "__pthread_kill";
+    break;
+  case llvm::Triple::Linux:
+    module_spec = FileSpec("libc.so.6");
+    symbol_name = "__GI_raise";
+    if (!ModuleHasDebugInfo(target, module_spec, symbol_name))
+      symbol_name = "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(module_spec, 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();
+
+  FileSpec module_spec;
+  StringRef symbol_name;
+
+  switch (target.GetArchitecture().GetTriple().getOS()) {
+  case llvm::Triple::Darwin:
+  case llvm::Triple::MacOSX:
+    module_spec = FileSpec("libsystem_c.dylib");
+    symbol_name = "__assert_rtn";
+    break;
+  case llvm::Triple::Linux:
+    module_spec = FileSpec("libc.so.6");
+    symbol_name = "__GI___assert_fail";
+    if (!ModuleHasDebugInfo(target, module_spec, symbol_name))
+      symbol_name = "__assert_fail";
+    break;
+  default:
+    Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND));
+    LLDB_LOG(log, "AssertFrameRecognizer::GetAssertLocation Unsupported OS");
+    return llvm::None;
+  }
+
+  return std::make_tuple(module_spec, 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
+
+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();
+};
+
+AssertRecognizedStackFrame::AssertRecognizedStackFrame(
+    StackFrameSP most_relevant_frame_sp)
+    : m_most_relevant_frame(most_relevant_frame_sp) {
+  m_stop_desc = "hit program assert";
+}
+
+lldb::StackFrameSP AssertRecognizedStackFrame::GetMostRelevantFrame() {
+  return m_most_relevant_frame;
+}

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 3de6fe266d12..80382007c329 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 57d1b3dd06f7..67a320a29712 100644
--- a/lldb/source/Target/StackFrameRecognizer.cpp
+++ b/lldb/source/Target/StackFrameRecognizer.cpp
@@ -92,8 +92,8 @@ class StackFrameRecognizerManagerImpl {
   }
 
   StackFrameRecognizerSP GetRecognizerForFrame(StackFrameSP frame) {
-    const SymbolContext &symctx =
-        frame->GetSymbolContext(eSymbolContextModule | eSymbolContextFunction);
+    const SymbolContext &symctx = frame->GetSymbolContext(
+        eSymbolContextModule | eSymbolContextFunction | eSymbolContextSymbol);
     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 331bbed60185..00f8b5ae276e 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;
 }
 
+std::string 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();
+
+  std::string recognized_stop_description =
+      recognized_frame_sp->GetStopDescription();
+
+  if (!recognized_stop_description.empty())
+    return recognized_stop_description;
+
+  return GetStopDescriptionRaw();
+}
+
+std::string Thread::GetStopDescriptionRaw() {
+  StopInfoSP stop_info_sp = GetStopInfo();
+  std::string 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