kubamracek updated this revision to Diff 178991.
kubamracek added a comment.

Addressed comment 2. Changed the test to use an Obj-C++ source file, and added 
a case that throws a plain C++ exception.


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D44072/new/

https://reviews.llvm.org/D44072

Files:
  include/lldb/API/SBThread.h
  include/lldb/Target/LanguageRuntime.h
  include/lldb/Target/Thread.h
  packages/Python/lldbsuite/test/lang/objc/exceptions/Makefile
  packages/Python/lldbsuite/test/lang/objc/exceptions/TestObjCExceptions.py
  packages/Python/lldbsuite/test/lang/objc/exceptions/main.m
  packages/Python/lldbsuite/test/lang/objc/exceptions/main.mm
  scripts/interface/SBThread.i
  source/API/SBThread.cpp
  source/Commands/CommandObjectThread.cpp
  
source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.cpp
  
source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.h
  source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.cpp
  source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.h
  source/Target/Thread.cpp

Index: source/Target/Thread.cpp
===================================================================
--- source/Target/Thread.cpp
+++ source/Target/Thread.cpp
@@ -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 @@
 }
 
 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);
+}
Index: source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.h
===================================================================
--- source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.h
+++ source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.h
@@ -89,6 +89,12 @@
   
   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,
Index: source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.cpp
===================================================================
--- source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.cpp
+++ source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.cpp
@@ -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 @@
   }
 }
 
+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(
Index: source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.h
===================================================================
--- source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.h
+++ source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.h
@@ -65,6 +65,9 @@
                                                      bool throw_bp) override;
 
   lldb::SearchFilterSP CreateExceptionSearchFilter() override;
+  
+  lldb::ValueObjectSP GetExceptionObjectForThread(
+      lldb::ThreadSP thread_sp) override;
 
   //------------------------------------------------------------------
   // PluginInterface protocol
Index: source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.cpp
===================================================================
--- source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.cpp
+++ source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.cpp
@@ -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 @@
       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);
Index: source/Commands/CommandObjectThread.cpp
===================================================================
--- source/Commands/CommandObjectThread.cpp
+++ source/Commands/CommandObjectThread.cpp
@@ -1552,14 +1552,13 @@
       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;
   }
Index: source/API/SBThread.cpp
===================================================================
--- source/API/SBThread.cpp
+++ source/API/SBThread.cpp
@@ -1491,13 +1491,12 @@
   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());
Index: scripts/interface/SBThread.i
===================================================================
--- scripts/interface/SBThread.i
+++ scripts/interface/SBThread.i
@@ -397,6 +397,24 @@
     ") 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.
Index: packages/Python/lldbsuite/test/lang/objc/exceptions/main.mm
===================================================================
--- packages/Python/lldbsuite/test/lang/objc/exceptions/main.mm
+++ packages/Python/lldbsuite/test/lang/objc/exceptions/main.mm
@@ -9,13 +9,37 @@
 
 #import <Foundation/Foundation.h>
 
-void foo()
+#import <exception>
+#import <stdexcept>
+
+@interface MyCustomException: NSException
+@end
+@implementation MyCustomException
+@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 @@
     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;
 }
Index: packages/Python/lldbsuite/test/lang/objc/exceptions/main.m
===================================================================
--- packages/Python/lldbsuite/test/lang/objc/exceptions/main.m
+++ packages/Python/lldbsuite/test/lang/objc/exceptions/main.m
@@ -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;
-}
-
Index: packages/Python/lldbsuite/test/lang/objc/exceptions/TestObjCExceptions.py
===================================================================
--- packages/Python/lldbsuite/test/lang/objc/exceptions/TestObjCExceptions.py
+++ packages/Python/lldbsuite/test/lang/objc/exceptions/TestObjCExceptions.py
@@ -17,13 +17,14 @@
     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 @@
                 '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 @@
         thread = target.GetProcess().GetSelectedThread()
         frame = thread.GetSelectedFrame()
 
+        # No exception being currently thrown/caught at this point
+        self.assertEqual(thread.GetCurrentException().IsValid(), False)
+        self.assertEqual(thread.GetCurrentExceptionBacktrace().IsValid(), False)
+
         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 @@
         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 @@
             '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 @@
         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 @@
 
         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.assertEqual(thread.GetCurrentException().IsValid(), True)
+        self.assertEqual(thread.GetCurrentExceptionBacktrace().IsValid(), True)
+
+        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.assertEqual(thread.GetCurrentException().IsValid(), False)
+        self.assertEqual(thread.GetCurrentExceptionBacktrace().IsValid(), False)
Index: packages/Python/lldbsuite/test/lang/objc/exceptions/Makefile
===================================================================
--- packages/Python/lldbsuite/test/lang/objc/exceptions/Makefile
+++ packages/Python/lldbsuite/test/lang/objc/exceptions/Makefile
@@ -1,6 +1,6 @@
 LEVEL = ../../../make
 
-OBJC_SOURCES := main.m
+OBJCXX_SOURCES := main.mm
 
 CFLAGS_EXTRAS += -w
 
Index: include/lldb/Target/Thread.h
===================================================================
--- include/lldb/Target/Thread.h
+++ include/lldb/Target/Thread.h
@@ -1255,8 +1255,7 @@
 
   lldb::ValueObjectSP GetCurrentException();
 
-  // TODO(kubamracek): Extract backtrace from ValueObjectSP into ThreadSP
-  // lldb::ThreadSP GetCurrentExceptionBacktrace();
+  lldb::ThreadSP GetCurrentExceptionBacktrace();
 
 protected:
   friend class ThreadPlan;
Index: include/lldb/Target/LanguageRuntime.h
===================================================================
--- include/lldb/Target/LanguageRuntime.h
+++ include/lldb/Target/LanguageRuntime.h
@@ -119,6 +119,17 @@
   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(); }
Index: include/lldb/API/SBThread.h
===================================================================
--- include/lldb/API/SBThread.h
+++ include/lldb/API/SBThread.h
@@ -200,8 +200,7 @@
 
   SBValue GetCurrentException();
 
-  // TODO(kubamracek): Extract backtrace from SBValue into SBThread
-  // SBThread GetCurrentExceptionBacktrace();
+  SBThread GetCurrentExceptionBacktrace();
 
   bool SafeToCallFunctions();
 
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to