[Lldb-commits] [lldb] r349718 - [lldb] Retrieve currently handled Obj-C exception via __cxa_current_exception_type and add GetCurrentExceptionBacktrace SB ABI

Kuba Mracek via lldb-commits lldb-commits at lists.llvm.org
Wed Dec 19 18:01:59 PST 2018


Author: kuba.brecka
Date: Wed Dec 19 18:01:59 2018
New Revision: 349718

URL: http://llvm.org/viewvc/llvm-project?rev=349718&view=rev
Log:
[lldb] Retrieve currently handled Obj-C exception via __cxa_current_exception_type and add GetCurrentExceptionBacktrace SB ABI

This builds on https://reviews.llvm.org/D43884 and https://reviews.llvm.org/D43886 and extends LLDB support of Obj-C exceptions to also look for a "current exception" for a thread in the C++ exception handling runtime metadata (via call to __cxa_current_exception_type). We also construct an actual historical SBThread/ThreadSP that contains frames from the backtrace in the Obj-C exception object.

The high level goal this achieves is that when we're already crashed (because an unhandled exception occurred), we can still access the exception object and retrieve the backtrace from the throw point. In Obj-C, this is particularly useful because a catch+rethrow is very common and in those cases you currently don't have any access to the throw point backtrace.

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


Added:
    lldb/trunk/packages/Python/lldbsuite/test/lang/objc/exceptions/main.mm
      - copied, changed from r349717, lldb/trunk/packages/Python/lldbsuite/test/lang/objc/exceptions/main.m
Removed:
    lldb/trunk/packages/Python/lldbsuite/test/lang/objc/exceptions/main.m
Modified:
    lldb/trunk/include/lldb/API/SBThread.h
    lldb/trunk/include/lldb/Target/LanguageRuntime.h
    lldb/trunk/include/lldb/Target/Thread.h
    lldb/trunk/packages/Python/lldbsuite/test/lang/objc/exceptions/Makefile
    lldb/trunk/packages/Python/lldbsuite/test/lang/objc/exceptions/TestObjCExceptions.py
    lldb/trunk/scripts/interface/SBThread.i
    lldb/trunk/source/API/SBThread.cpp
    lldb/trunk/source/Commands/CommandObjectThread.cpp
    lldb/trunk/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.cpp
    lldb/trunk/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.h
    lldb/trunk/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.cpp
    lldb/trunk/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.h
    lldb/trunk/source/Target/Thread.cpp

Modified: lldb/trunk/include/lldb/API/SBThread.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/include/lldb/API/SBThread.h?rev=349718&r1=349717&r2=349718&view=diff
==============================================================================
--- lldb/trunk/include/lldb/API/SBThread.h (original)
+++ lldb/trunk/include/lldb/API/SBThread.h Wed Dec 19 18:01:59 2018
@@ -200,8 +200,7 @@ public:
 
   SBValue GetCurrentException();
 
-  // TODO(kubamracek): Extract backtrace from SBValue into SBThread
-  // SBThread GetCurrentExceptionBacktrace();
+  SBThread GetCurrentExceptionBacktrace();
 
   bool SafeToCallFunctions();
 

Modified: lldb/trunk/include/lldb/Target/LanguageRuntime.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/include/lldb/Target/LanguageRuntime.h?rev=349718&r1=349717&r2=349718&view=diff
==============================================================================
--- lldb/trunk/include/lldb/Target/LanguageRuntime.h (original)
+++ lldb/trunk/include/lldb/Target/LanguageRuntime.h Wed Dec 19 18:01:59 2018
@@ -119,6 +119,17 @@ public:
   static Breakpoint::BreakpointPreconditionSP
   CreateExceptionPrecondition(lldb::LanguageType language, bool catch_bp,
                               bool throw_bp);
+
+  virtual lldb::ValueObjectSP GetExceptionObjectForThread(
+      lldb::ThreadSP thread_sp) {
+    return lldb::ValueObjectSP();
+  }
+
+  virtual lldb::ThreadSP GetBacktraceThreadFromException(
+      lldb::ValueObjectSP thread_sp) {
+    return lldb::ThreadSP();
+  }
+
   Process *GetProcess() { return m_process; }
 
   Target &GetTargetRef() { return m_process->GetTarget(); }

Modified: lldb/trunk/include/lldb/Target/Thread.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/include/lldb/Target/Thread.h?rev=349718&r1=349717&r2=349718&view=diff
==============================================================================
--- lldb/trunk/include/lldb/Target/Thread.h (original)
+++ lldb/trunk/include/lldb/Target/Thread.h Wed Dec 19 18:01:59 2018
@@ -1255,8 +1255,7 @@ public:
 
   lldb::ValueObjectSP GetCurrentException();
 
-  // TODO(kubamracek): Extract backtrace from ValueObjectSP into ThreadSP
-  // lldb::ThreadSP GetCurrentExceptionBacktrace();
+  lldb::ThreadSP GetCurrentExceptionBacktrace();
 
 protected:
   friend class ThreadPlan;

Modified: lldb/trunk/packages/Python/lldbsuite/test/lang/objc/exceptions/Makefile
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/packages/Python/lldbsuite/test/lang/objc/exceptions/Makefile?rev=349718&r1=349717&r2=349718&view=diff
==============================================================================
--- lldb/trunk/packages/Python/lldbsuite/test/lang/objc/exceptions/Makefile (original)
+++ lldb/trunk/packages/Python/lldbsuite/test/lang/objc/exceptions/Makefile Wed Dec 19 18:01:59 2018
@@ -1,6 +1,6 @@
 LEVEL = ../../../make
 
-OBJC_SOURCES := main.m
+OBJCXX_SOURCES := main.mm
 
 CFLAGS_EXTRAS += -w
 

Modified: lldb/trunk/packages/Python/lldbsuite/test/lang/objc/exceptions/TestObjCExceptions.py
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/packages/Python/lldbsuite/test/lang/objc/exceptions/TestObjCExceptions.py?rev=349718&r1=349717&r2=349718&view=diff
==============================================================================
--- lldb/trunk/packages/Python/lldbsuite/test/lang/objc/exceptions/TestObjCExceptions.py (original)
+++ lldb/trunk/packages/Python/lldbsuite/test/lang/objc/exceptions/TestObjCExceptions.py Wed Dec 19 18:01:59 2018
@@ -17,13 +17,14 @@ class ObjCExceptionsTestCase(TestBase):
     mydir = TestBase.compute_mydir(__file__)
 
     @skipUnlessDarwin
-    def test_objc_exceptions_1(self):
+    def test_objc_exceptions_at_throw(self):
         self.build()
 
         target = self.dbg.CreateTarget(self.getBuildArtifact("a.out"))
         self.assertTrue(target, VALID_TARGET)
 
-        lldbutil.run_to_name_breakpoint(self, "objc_exception_throw")
+        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'])
@@ -33,7 +34,7 @@ class ObjCExceptionsTestCase(TestBase):
                 'name: "ThrownException" - reason: "SomeReason"',
             ])
 
-        lldbutil.run_to_source_breakpoint(self, "// Set break point at this line.", lldb.SBFileSpec("main.m"))
+        lldbutil.run_to_source_breakpoint(self, "// Set break point at this line.", lldb.SBFileSpec("main.mm"), launch_info=launch_info)
 
         self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT,
                     substrs=['stopped', 'stop reason = breakpoint'])
@@ -42,19 +43,23 @@ class ObjCExceptionsTestCase(TestBase):
         thread = target.GetProcess().GetSelectedThread()
         frame = thread.GetSelectedFrame()
 
+        # No exception being currently thrown/caught at this point
+        self.assertFalse(thread.GetCurrentException().IsValid())
+        self.assertFalse(thread.GetCurrentExceptionBacktrace().IsValid())
+
         self.expect(
             'frame variable e1',
             substrs=[
                 '(NSException *) e1 = ',
-                'name: @"ExceptionName" - reason: @"SomeReason"'
+                'name: "ExceptionName" - reason: "SomeReason"'
             ])
 
         self.expect(
             'frame variable --dynamic-type no-run-target *e1',
             substrs=[
                 '(NSException) *e1 = ',
-                'name = ', '@"ExceptionName"',
-                'reason = ', '@"SomeReason"',
+                'name = ', '"ExceptionName"',
+                'reason = ', '"SomeReason"',
                 'userInfo = ', '1 key/value pair',
                 'reserved = ', 'nil',
             ])
@@ -62,7 +67,7 @@ class ObjCExceptionsTestCase(TestBase):
         e1 = frame.FindVariable("e1")
         self.assertTrue(e1)
         self.assertEqual(e1.type.name, "NSException *")
-        self.assertEqual(e1.GetSummary(), 'name: @"ExceptionName" - reason: @"SomeReason"')
+        self.assertEqual(e1.GetSummary(), 'name: "ExceptionName" - reason: "SomeReason"')
         self.assertEqual(e1.GetChildMemberWithName("name").description, "ExceptionName")
         self.assertEqual(e1.GetChildMemberWithName("reason").description, "SomeReason")
         userInfo = e1.GetChildMemberWithName("userInfo").dynamic
@@ -75,15 +80,15 @@ class ObjCExceptionsTestCase(TestBase):
             'frame variable e2',
             substrs=[
                 '(NSException *) e2 = ',
-                'name: @"ThrownException" - reason: @"SomeReason"'
+                'name: "ThrownException" - reason: "SomeReason"'
             ])
 
         self.expect(
             'frame variable --dynamic-type no-run-target *e2',
             substrs=[
                 '(NSException) *e2 = ',
-                'name = ', '@"ThrownException"',
-                'reason = ', '@"SomeReason"',
+                'name = ', '"ThrownException"',
+                'reason = ', '"SomeReason"',
                 'userInfo = ', '1 key/value pair',
                 'reserved = ',
             ])
@@ -91,7 +96,7 @@ class ObjCExceptionsTestCase(TestBase):
         e2 = frame.FindVariable("e2")
         self.assertTrue(e2)
         self.assertEqual(e2.type.name, "NSException *")
-        self.assertEqual(e2.GetSummary(), 'name: @"ThrownException" - reason: @"SomeReason"')
+        self.assertEqual(e2.GetSummary(), 'name: "ThrownException" - reason: "SomeReason"')
         self.assertEqual(e2.GetChildMemberWithName("name").description, "ThrownException")
         self.assertEqual(e2.GetChildMemberWithName("reason").description, "SomeReason")
         userInfo = e2.GetChildMemberWithName("userInfo").dynamic
@@ -106,5 +111,84 @@ class ObjCExceptionsTestCase(TestBase):
 
         pcs = [i.unsigned for i in children]
         names = [target.ResolveSymbolContextForAddress(lldb.SBAddress(pc, target), lldb.eSymbolContextSymbol).GetSymbol().name for pc in pcs]
-        for n in ["objc_exception_throw", "foo", "main"]:
+        for n in ["objc_exception_throw", "foo(int)", "main"]:
             self.assertTrue(n in names, "%s is in the exception backtrace (%s)" % (n, names))
+
+    @skipUnlessDarwin
+    def test_objc_exceptions_at_abort(self):
+        self.build()
+
+        target = self.dbg.CreateTarget(self.getBuildArtifact("a.out"))
+        self.assertTrue(target, VALID_TARGET)
+
+        self.runCmd("run 0")
+
+        # We should be stopped at pthread_kill because of an unhandled exception
+        self.expect("thread list",
+            substrs=['stopped', 'stop reason = signal SIGABRT'])
+
+        self.expect('thread exception', substrs=[
+                '(NSException *) exception = ',
+                'name: "ThrownException" - reason: "SomeReason"',
+                'libobjc.A.dylib`objc_exception_throw',
+                'a.out`foo', 'at main.mm:25',
+                'a.out`rethrow', 'at main.mm:36',
+                'a.out`main',
+            ])
+
+        process = self.dbg.GetSelectedTarget().process
+        thread = process.GetSelectedThread()
+
+        # There is an exception being currently processed at this point
+        self.assertTrue(thread.GetCurrentException().IsValid())
+        self.assertTrue(thread.GetCurrentExceptionBacktrace().IsValid())
+
+        history_thread = thread.GetCurrentExceptionBacktrace()
+        self.assertGreaterEqual(history_thread.num_frames, 4)
+        for n in ["objc_exception_throw", "foo(int)", "rethrow(int)", "main"]:
+            self.assertEqual(len([f for f in history_thread.frames if f.GetFunctionName() == n]), 1)
+
+        self.runCmd("kill")
+
+        self.runCmd("run 1")
+        # We should be stopped at pthread_kill because of an unhandled exception
+        self.expect("thread list",
+            substrs=['stopped', 'stop reason = signal SIGABRT'])
+
+        self.expect('thread exception', substrs=[
+                '(MyCustomException *) exception = ',
+                'libobjc.A.dylib`objc_exception_throw',
+                'a.out`foo', 'at main.mm:27',
+                'a.out`rethrow', 'at main.mm:36',
+                'a.out`main',
+            ])
+
+        process = self.dbg.GetSelectedTarget().process
+        thread = process.GetSelectedThread()
+
+        history_thread = thread.GetCurrentExceptionBacktrace()
+        self.assertGreaterEqual(history_thread.num_frames, 4)
+        for n in ["objc_exception_throw", "foo(int)", "rethrow(int)", "main"]:
+            self.assertEqual(len([f for f in history_thread.frames if f.GetFunctionName() == n]), 1)
+
+    @skipUnlessDarwin
+    def test_cxx_exceptions_at_abort(self):
+        self.build()
+
+        target = self.dbg.CreateTarget(self.getBuildArtifact("a.out"))
+        self.assertTrue(target, VALID_TARGET)
+
+        self.runCmd("run 2")
+
+        # We should be stopped at pthread_kill because of an unhandled exception
+        self.expect("thread list",
+            substrs=['stopped', 'stop reason = signal SIGABRT'])
+
+        self.expect('thread exception', substrs=[])
+
+        process = self.dbg.GetSelectedTarget().process
+        thread = process.GetSelectedThread()
+
+        # C++ exceptions are not exposed in the API (yet).
+        self.assertFalse(thread.GetCurrentException().IsValid())
+        self.assertFalse(thread.GetCurrentExceptionBacktrace().IsValid())

Removed: lldb/trunk/packages/Python/lldbsuite/test/lang/objc/exceptions/main.m
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/packages/Python/lldbsuite/test/lang/objc/exceptions/main.m?rev=349717&view=auto
==============================================================================
--- lldb/trunk/packages/Python/lldbsuite/test/lang/objc/exceptions/main.m (original)
+++ lldb/trunk/packages/Python/lldbsuite/test/lang/objc/exceptions/main.m (removed)
@@ -1,36 +0,0 @@
-//===-- main.m ------------------------------------------------*- ObjC -*-===//
-//
-//                     The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-
-#import <Foundation/Foundation.h>
-
-void foo()
-{
-    NSDictionary *info = [NSDictionary dictionaryWithObjectsAndKeys:@"some_value", @"some_key", nil];
-    @throw [[NSException alloc] initWithName:@"ThrownException" reason:@"SomeReason" userInfo:info];
-}
-
-int main (int argc, const char * argv[])
-{
-    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
-
-    NSDictionary *info = [NSDictionary dictionaryWithObjectsAndKeys:@"some_value", @"some_key", nil];
-    NSException *e1 = [[NSException alloc] initWithName:@"ExceptionName" reason:@"SomeReason" userInfo:info];
-    NSException *e2;
-
-    @try {
-        foo();
-    } @catch(NSException *e) {
-        e2 = e;
-    }
-
-    NSLog(@"1"); // Set break point at this line.
-    [pool drain];
-    return 0;
-}
-

Copied: lldb/trunk/packages/Python/lldbsuite/test/lang/objc/exceptions/main.mm (from r349717, lldb/trunk/packages/Python/lldbsuite/test/lang/objc/exceptions/main.m)
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/packages/Python/lldbsuite/test/lang/objc/exceptions/main.mm?p2=lldb/trunk/packages/Python/lldbsuite/test/lang/objc/exceptions/main.mm&p1=lldb/trunk/packages/Python/lldbsuite/test/lang/objc/exceptions/main.m&r1=349717&r2=349718&rev=349718&view=diff
==============================================================================
--- lldb/trunk/packages/Python/lldbsuite/test/lang/objc/exceptions/main.m (original)
+++ lldb/trunk/packages/Python/lldbsuite/test/lang/objc/exceptions/main.mm Wed Dec 19 18:01:59 2018
@@ -9,13 +9,37 @@
 
 #import <Foundation/Foundation.h>
 
-void foo()
+#import <exception>
+#import <stdexcept>
+
+ at interface MyCustomException: NSException
+ at end
+ at implementation MyCustomException
+ at end
+
+void foo(int n)
 {
     NSDictionary *info = [NSDictionary dictionaryWithObjectsAndKeys:@"some_value", @"some_key", nil];
-    @throw [[NSException alloc] initWithName:@"ThrownException" reason:@"SomeReason" userInfo:info];
+    switch (n) {
+        case 0:
+            @throw [[NSException alloc] initWithName:@"ThrownException" reason:@"SomeReason" userInfo:info];
+        case 1:
+            @throw [[MyCustomException alloc] initWithName:@"ThrownException" reason:@"SomeReason" userInfo:info];
+        case 2:
+            throw std::runtime_error("C++ exception");
+    }
 }
 
-int main (int argc, const char * argv[])
+void rethrow(int n)
+{
+    @try {
+        foo(n);
+    } @catch(NSException *e) {
+        @throw;
+    }
+}
+
+int main(int argc, const char * argv[])
 {
     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
 
@@ -24,12 +48,15 @@ int main (int argc, const char * argv[])
     NSException *e2;
 
     @try {
-        foo();
+        foo(atoi(argv[1]));
     } @catch(NSException *e) {
         e2 = e;
     }
 
     NSLog(@"1"); // Set break point at this line.
+
+    rethrow(atoi(argv[1]));
+
     [pool drain];
     return 0;
 }

Modified: lldb/trunk/scripts/interface/SBThread.i
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/scripts/interface/SBThread.i?rev=349718&r1=349717&r2=349718&view=diff
==============================================================================
--- lldb/trunk/scripts/interface/SBThread.i (original)
+++ lldb/trunk/scripts/interface/SBThread.i Wed Dec 19 18:01:59 2018
@@ -397,6 +397,24 @@ public:
     ") GetExtendedBacktraceOriginatingIndexID;
     uint32_t
     GetExtendedBacktraceOriginatingIndexID();
+    
+    %feature("autodoc","
+    Returns an SBValue object represeting the current exception for the thread,
+    if there is any. Currently, this works for Obj-C code and returns an SBValue
+    representing the NSException object at the throw site or that's currently
+    being processes.
+    ") GetCurrentException;
+    lldb::SBValue
+    GetCurrentException();
+
+    %feature("autodoc","
+    Returns a historical (fake) SBThread representing the stack trace of an
+    exception, if there is one for the thread. Currently, this works for Obj-C
+    code, and can retrieve the throw-site backtrace of an NSException object
+    even when the program is no longer at the throw site.
+    ") GetCurrentExceptionBacktrace;
+    lldb::SBThread
+    GetCurrentExceptionBacktrace();
 
     %feature("autodoc","
     Takes no arguments, returns a bool.

Modified: lldb/trunk/source/API/SBThread.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/API/SBThread.cpp?rev=349718&r1=349717&r2=349718&view=diff
==============================================================================
--- lldb/trunk/source/API/SBThread.cpp (original)
+++ lldb/trunk/source/API/SBThread.cpp Wed Dec 19 18:01:59 2018
@@ -1491,13 +1491,12 @@ SBValue SBThread::GetCurrentException()
   return SBValue(thread_sp->GetCurrentException());
 }
 
-/* TODO(kubamracek)
 SBThread SBThread::GetCurrentExceptionBacktrace() {
   ThreadSP thread_sp(m_opaque_sp->GetThreadSP());
   if (!thread_sp) return SBThread();
 
   return SBThread(thread_sp->GetCurrentExceptionBacktrace());
-}*/
+}
 
 bool SBThread::SafeToCallFunctions() {
   ThreadSP thread_sp(m_opaque_sp->GetThreadSP());

Modified: lldb/trunk/source/Commands/CommandObjectThread.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Commands/CommandObjectThread.cpp?rev=349718&r1=349717&r2=349718&view=diff
==============================================================================
--- lldb/trunk/source/Commands/CommandObjectThread.cpp (original)
+++ lldb/trunk/source/Commands/CommandObjectThread.cpp Wed Dec 19 18:01:59 2018
@@ -1552,14 +1552,13 @@ class CommandObjectThreadException : pub
       exception_object_sp->Dump(strm);
     }
 
-    /* TODO(kubamracek)
     ThreadSP exception_thread_sp = thread_sp->GetCurrentExceptionBacktrace();
     if (exception_thread_sp && exception_thread_sp->IsValid()) {
       const uint32_t num_frames_with_source = 0;
       const bool stop_format = false;
-      exception_thread_sp->GetStatus(strm, m_options.m_start, m_options.m_count,
+      exception_thread_sp->GetStatus(strm, 0, UINT32_MAX,
                                      num_frames_with_source, stop_format);
-    }*/
+    }
 
     return true;
   }

Modified: lldb/trunk/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.cpp?rev=349718&r1=349717&r2=349718&view=diff
==============================================================================
--- lldb/trunk/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.cpp (original)
+++ lldb/trunk/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.cpp Wed Dec 19 18:01:59 2018
@@ -16,6 +16,9 @@
 #include "lldb/Core/PluginManager.h"
 #include "lldb/Core/ValueObject.h"
 #include "lldb/Core/ValueObjectMemory.h"
+#include "lldb/DataFormatters/FormattersHelpers.h"
+#include "lldb/Expression/DiagnosticManager.h"
+#include "lldb/Expression/FunctionCaller.h"
 #include "lldb/Interpreter/CommandObject.h"
 #include "lldb/Interpreter/CommandObjectMultiword.h"
 #include "lldb/Interpreter/CommandReturnObject.h"
@@ -549,6 +552,61 @@ bool ItaniumABILanguageRuntime::Exceptio
       break_site_id, m_cxx_exception_bp_sp->GetID());
 }
 
+ValueObjectSP ItaniumABILanguageRuntime::GetExceptionObjectForThread(
+    ThreadSP thread_sp) {
+  ClangASTContext *clang_ast_context =
+      m_process->GetTarget().GetScratchClangASTContext();
+  CompilerType voidstar =
+      clang_ast_context->GetBasicType(eBasicTypeVoid).GetPointerType();
+
+  DiagnosticManager diagnostics;
+  ExecutionContext exe_ctx;
+  EvaluateExpressionOptions options;
+
+  options.SetUnwindOnError(true);
+  options.SetIgnoreBreakpoints(true);
+  options.SetStopOthers(true);
+  options.SetTimeout(std::chrono::milliseconds(500));
+  options.SetTryAllThreads(false);
+  thread_sp->CalculateExecutionContext(exe_ctx);
+
+  const ModuleList &modules = m_process->GetTarget().GetImages();
+  SymbolContextList contexts;
+  SymbolContext context;
+
+  modules.FindSymbolsWithNameAndType(
+      ConstString("__cxa_current_exception_type"), eSymbolTypeCode, contexts);
+  contexts.GetContextAtIndex(0, context);
+  Address addr = context.symbol->GetAddress();
+
+  Status error;
+  FunctionCaller *function_caller =
+      m_process->GetTarget().GetFunctionCallerForLanguage(
+          eLanguageTypeC, voidstar, addr, ValueList(), "caller", error);
+
+  ExpressionResults func_call_ret;
+  Value results;
+  func_call_ret = function_caller->ExecuteFunction(exe_ctx, nullptr, options,
+                                                   diagnostics, results);
+  if (func_call_ret != eExpressionCompleted || !error.Success()) {
+    return ValueObjectSP();
+  }
+
+  size_t ptr_size = m_process->GetAddressByteSize();
+  addr_t result_ptr = results.GetScalar().ULongLong(LLDB_INVALID_ADDRESS);
+  addr_t exception_addr =
+      m_process->ReadPointerFromMemory(result_ptr - ptr_size, error);
+
+  lldb_private::formatters::InferiorSizedWord exception_isw(exception_addr,
+                                                            *m_process);
+  ValueObjectSP exception = ValueObject::CreateValueObjectFromData(
+      "exception", exception_isw.GetAsData(m_process->GetByteOrder()), exe_ctx,
+      voidstar);
+  exception = exception->GetDynamicValue(eDynamicDontRunTarget);
+
+  return exception;
+}
+
 TypeAndOrName ItaniumABILanguageRuntime::GetDynamicTypeInfo(
     const lldb_private::Address &vtable_addr) {
   std::lock_guard<std::mutex> locker(m_dynamic_type_map_mutex);

Modified: lldb/trunk/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.h?rev=349718&r1=349717&r2=349718&view=diff
==============================================================================
--- lldb/trunk/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.h (original)
+++ lldb/trunk/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.h Wed Dec 19 18:01:59 2018
@@ -65,6 +65,9 @@ public:
                                                      bool throw_bp) override;
 
   lldb::SearchFilterSP CreateExceptionSearchFilter() override;
+  
+  lldb::ValueObjectSP GetExceptionObjectForThread(
+      lldb::ThreadSP thread_sp) override;
 
   //------------------------------------------------------------------
   // PluginInterface protocol

Modified: lldb/trunk/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.cpp?rev=349718&r1=349717&r2=349718&view=diff
==============================================================================
--- lldb/trunk/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.cpp (original)
+++ lldb/trunk/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.cpp Wed Dec 19 18:01:59 2018
@@ -19,10 +19,13 @@
 #include "lldb/Core/PluginManager.h"
 #include "lldb/Core/Section.h"
 #include "lldb/Core/ValueObject.h"
+#include "lldb/Core/ValueObjectConstResult.h"
+#include "lldb/DataFormatters/FormattersHelpers.h"
 #include "lldb/Expression/DiagnosticManager.h"
 #include "lldb/Expression/FunctionCaller.h"
 #include "lldb/Symbol/ClangASTContext.h"
 #include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Target/CPPLanguageRuntime.h"
 #include "lldb/Target/ExecutionContext.h"
 #include "lldb/Target/Process.h"
 #include "lldb/Target/RegisterContext.h"
@@ -35,6 +38,9 @@
 #include "lldb/Utility/Status.h"
 #include "lldb/Utility/StreamString.h"
 
+#include "Plugins/Process/Utility/HistoryThread.h"
+#include "Plugins/Language/ObjC/NSString.h"
+
 #include <vector>
 
 using namespace lldb;
@@ -459,6 +465,102 @@ lldb::SearchFilterSP AppleObjCRuntime::C
   }
 }
 
+ValueObjectSP AppleObjCRuntime::GetExceptionObjectForThread(
+    ThreadSP thread_sp) {
+  auto cpp_runtime = m_process->GetCPPLanguageRuntime();
+  if (!cpp_runtime) return ValueObjectSP();
+  auto cpp_exception = cpp_runtime->GetExceptionObjectForThread(thread_sp);
+  if (!cpp_exception) return ValueObjectSP();
+  
+  auto descriptor = GetClassDescriptor(*cpp_exception.get());
+  if (!descriptor || !descriptor->IsValid()) return ValueObjectSP();
+  
+  while (descriptor) {
+    ConstString class_name(descriptor->GetClassName());
+    if (class_name == ConstString("NSException")) return cpp_exception;
+    descriptor = descriptor->GetSuperclass();
+  }
+
+  return ValueObjectSP();
+}
+
+ThreadSP AppleObjCRuntime::GetBacktraceThreadFromException(
+    lldb::ValueObjectSP exception_sp) {
+  ValueObjectSP reserved_dict =
+      exception_sp->GetChildMemberWithName(ConstString("reserved"), true);
+  if (!reserved_dict) return ThreadSP();
+
+  reserved_dict = reserved_dict->GetSyntheticValue();
+  if (!reserved_dict) return ThreadSP();
+
+  CompilerType objc_id =
+      exception_sp->GetTargetSP()->GetScratchClangASTContext()->GetBasicType(
+          lldb::eBasicTypeObjCID);
+  ValueObjectSP return_addresses;
+
+  auto objc_object_from_address = [&exception_sp, &objc_id](uint64_t addr,
+                                                            const char *name) {
+    Value value(addr);
+    value.SetCompilerType(objc_id);
+    auto object = ValueObjectConstResult::Create(
+        exception_sp->GetTargetSP().get(), value, ConstString(name));
+    object = object->GetDynamicValue(eDynamicDontRunTarget);
+    return object;
+  };
+
+  for (size_t idx = 0; idx < reserved_dict->GetNumChildren(); idx++) {
+    ValueObjectSP dict_entry = reserved_dict->GetChildAtIndex(idx, true);
+
+    DataExtractor data;
+    data.SetAddressByteSize(dict_entry->GetProcessSP()->GetAddressByteSize());
+    Status error;
+    dict_entry->GetData(data, error);
+    if (error.Fail()) return ThreadSP();
+
+    lldb::offset_t data_offset = 0;
+    auto dict_entry_key = data.GetPointer(&data_offset);
+    auto dict_entry_value = data.GetPointer(&data_offset);
+
+    auto key_nsstring = objc_object_from_address(dict_entry_key, "key");
+    StreamString key_summary;
+    if (lldb_private::formatters::NSStringSummaryProvider(
+            *key_nsstring, key_summary, TypeSummaryOptions()) &&
+        !key_summary.Empty()) {
+      if (key_summary.GetString() == "\"callStackReturnAddresses\"") {
+        return_addresses = objc_object_from_address(dict_entry_value,
+                                                    "callStackReturnAddresses");
+        break;
+      }
+    }
+  }
+
+  if (!return_addresses) return ThreadSP();
+  auto frames_value =
+      return_addresses->GetChildMemberWithName(ConstString("_frames"), true);
+  addr_t frames_addr = frames_value->GetValueAsUnsigned(0);
+  auto count_value =
+      return_addresses->GetChildMemberWithName(ConstString("_cnt"), true);
+  size_t count = count_value->GetValueAsUnsigned(0);
+  auto ignore_value =
+      return_addresses->GetChildMemberWithName(ConstString("_ignore"), true);
+  size_t ignore = ignore_value->GetValueAsUnsigned(0);
+
+  size_t ptr_size = m_process->GetAddressByteSize();
+  std::vector<lldb::addr_t> pcs;
+  for (size_t idx = 0; idx < count; idx++) {
+    Status error;
+    addr_t pc = m_process->ReadPointerFromMemory(
+        frames_addr + (ignore + idx) * ptr_size, error);
+    pcs.push_back(pc);
+  }
+
+  if (pcs.empty()) return ThreadSP();
+
+  ThreadSP new_thread_sp(new HistoryThread(*m_process, 0, pcs, 0, false));
+  m_process->GetExtendedThreadList().AddThread(new_thread_sp);
+  return new_thread_sp;
+}
+
 std::tuple<FileSpec, ConstString>
 AppleObjCRuntime::GetExceptionThrowLocation() {
   return std::make_tuple(

Modified: lldb/trunk/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.h?rev=349718&r1=349717&r2=349718&view=diff
==============================================================================
--- lldb/trunk/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.h (original)
+++ lldb/trunk/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.h Wed Dec 19 18:01:59 2018
@@ -89,6 +89,12 @@ public:
   
   static std::tuple<FileSpec, ConstString> GetExceptionThrowLocation();
 
+  lldb::ValueObjectSP GetExceptionObjectForThread(
+      lldb::ThreadSP thread_sp) override;
+
+  lldb::ThreadSP GetBacktraceThreadFromException(
+      lldb::ValueObjectSP thread_sp) override;
+
   uint32_t GetFoundationVersion();
 
   virtual void GetValuesForGlobalCFBooleans(lldb::addr_t &cf_true,

Modified: lldb/trunk/source/Target/Thread.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Target/Thread.cpp?rev=349718&r1=349717&r2=349718&view=diff
==============================================================================
--- lldb/trunk/source/Target/Thread.cpp (original)
+++ lldb/trunk/source/Target/Thread.cpp Wed Dec 19 18:01:59 2018
@@ -23,6 +23,7 @@
 #include "lldb/Target/ABI.h"
 #include "lldb/Target/DynamicLoader.h"
 #include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/ObjCLanguageRuntime.h"
 #include "lldb/Target/Process.h"
 #include "lldb/Target/RegisterContext.h"
 #include "lldb/Target/StackFrameRecognizer.h"
@@ -2202,16 +2203,30 @@ Status Thread::StepOut() {
 }
 
 ValueObjectSP Thread::GetCurrentException() {
-  StackFrameSP frame_sp(GetStackFrameAtIndex(0));
-  if (!frame_sp) return ValueObjectSP();
+  if (auto frame_sp = GetStackFrameAtIndex(0))
+    if (auto recognized_frame = frame_sp->GetRecognizedFrame())
+      if (auto e = recognized_frame->GetExceptionObject())
+        return e;
+
+  // FIXME: For now, only ObjC exceptions are supported. This should really
+  // iterate over all language runtimes and ask them all to give us the current
+  // exception.
+  if (auto runtime = GetProcess()->GetObjCLanguageRuntime())
+    if (auto e = runtime->GetExceptionObjectForThread(shared_from_this()))
+      return e;
 
-  RecognizedStackFrameSP recognized_frame(frame_sp->GetRecognizedFrame());
-  if (!recognized_frame) return ValueObjectSP();
-
-  return recognized_frame->GetExceptionObject();
+  return ValueObjectSP();
 }
 
-/* TODO(kubamracek)
 ThreadSP Thread::GetCurrentExceptionBacktrace() {
-  return ThreadSP();
-}*/
+  ValueObjectSP exception = GetCurrentException();
+  if (!exception) return ThreadSP();
+
+  // FIXME: For now, only ObjC exceptions are supported. This should really
+  // iterate over all language runtimes and ask them all to give us the current
+  // exception.
+  auto runtime = GetProcess()->GetObjCLanguageRuntime();
+  if (!runtime) return ThreadSP();
+
+  return runtime->GetBacktraceThreadFromException(exception);
+}




More information about the lldb-commits mailing list