summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lldb/include/lldb/API/SBThread.h3
-rw-r--r--lldb/include/lldb/Target/LanguageRuntime.h11
-rw-r--r--lldb/include/lldb/Target/Thread.h3
-rw-r--r--lldb/packages/Python/lldbsuite/test/lang/objc/exceptions/Makefile2
-rw-r--r--lldb/packages/Python/lldbsuite/test/lang/objc/exceptions/TestObjCExceptions.py108
-rw-r--r--lldb/packages/Python/lldbsuite/test/lang/objc/exceptions/main.mm (renamed from lldb/packages/Python/lldbsuite/test/lang/objc/exceptions/main.m)35
-rw-r--r--lldb/scripts/interface/SBThread.i18
-rw-r--r--lldb/source/API/SBThread.cpp3
-rw-r--r--lldb/source/Commands/CommandObjectThread.cpp5
-rw-r--r--lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.cpp58
-rw-r--r--lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.h3
-rw-r--r--lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.cpp102
-rw-r--r--lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.h6
-rw-r--r--lldb/source/Target/Thread.cpp33
14 files changed, 355 insertions, 35 deletions
diff --git a/lldb/include/lldb/API/SBThread.h b/lldb/include/lldb/API/SBThread.h
index 24224cd8170..63816eef51b 100644
--- a/lldb/include/lldb/API/SBThread.h
+++ b/lldb/include/lldb/API/SBThread.h
@@ -200,8 +200,7 @@ public:
SBValue GetCurrentException();
- // TODO(kubamracek): Extract backtrace from SBValue into SBThread
- // SBThread GetCurrentExceptionBacktrace();
+ SBThread GetCurrentExceptionBacktrace();
bool SafeToCallFunctions();
diff --git a/lldb/include/lldb/Target/LanguageRuntime.h b/lldb/include/lldb/Target/LanguageRuntime.h
index d330d6db335..2a2f47b8535 100644
--- a/lldb/include/lldb/Target/LanguageRuntime.h
+++ b/lldb/include/lldb/Target/LanguageRuntime.h
@@ -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(); }
diff --git a/lldb/include/lldb/Target/Thread.h b/lldb/include/lldb/Target/Thread.h
index 4a697e3785a..0d14b10c651 100644
--- a/lldb/include/lldb/Target/Thread.h
+++ b/lldb/include/lldb/Target/Thread.h
@@ -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;
diff --git a/lldb/packages/Python/lldbsuite/test/lang/objc/exceptions/Makefile b/lldb/packages/Python/lldbsuite/test/lang/objc/exceptions/Makefile
index 9f7fb1ca623..261658b10ae 100644
--- a/lldb/packages/Python/lldbsuite/test/lang/objc/exceptions/Makefile
+++ b/lldb/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
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 6959f2ef75d..b5511549fd5 100644
--- a/lldb/packages/Python/lldbsuite/test/lang/objc/exceptions/TestObjCExceptions.py
+++ b/lldb/packages/Python/lldbsuite/test/lang/objc/exceptions/TestObjCExceptions.py
@@ -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())
diff --git a/lldb/packages/Python/lldbsuite/test/lang/objc/exceptions/main.m b/lldb/packages/Python/lldbsuite/test/lang/objc/exceptions/main.mm
index 93ad7266256..5683882486d 100644
--- a/lldb/packages/Python/lldbsuite/test/lang/objc/exceptions/main.m
+++ b/lldb/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 @@ 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;
}
diff --git a/lldb/scripts/interface/SBThread.i b/lldb/scripts/interface/SBThread.i
index 60d77c783bb..c12f176e42e 100644
--- a/lldb/scripts/interface/SBThread.i
+++ b/lldb/scripts/interface/SBThread.i
@@ -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.
diff --git a/lldb/source/API/SBThread.cpp b/lldb/source/API/SBThread.cpp
index 97a9df62566..2c859d5222d 100644
--- a/lldb/source/API/SBThread.cpp
+++ b/lldb/source/API/SBThread.cpp
@@ -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());
diff --git a/lldb/source/Commands/CommandObjectThread.cpp b/lldb/source/Commands/CommandObjectThread.cpp
index 57dd2a33226..e792887d4ff 100644
--- a/lldb/source/Commands/CommandObjectThread.cpp
+++ b/lldb/source/Commands/CommandObjectThread.cpp
@@ -1552,14 +1552,13 @@ class CommandObjectThreadException : public CommandObjectIterateOverThreads {
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;
}
diff --git a/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.cpp b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.cpp
index 3f9ae937b73..8fcf33c758c 100644
--- a/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.cpp
+++ b/lldb/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 @@ bool ItaniumABILanguageRuntime::ExceptionBreakpointsExplainStop(
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);
diff --git a/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.h b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.h
index 149fe05fcc3..abed3706c6b 100644
--- a/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.h
+++ b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.h
@@ -65,6 +65,9 @@ public:
bool throw_bp) override;
lldb::SearchFilterSP CreateExceptionSearchFilter() override;
+
+ lldb::ValueObjectSP GetExceptionObjectForThread(
+ lldb::ThreadSP thread_sp) override;
//------------------------------------------------------------------
// PluginInterface protocol
diff --git a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.cpp b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.cpp
index f55a7e8dda4..ed47b481a81 100644
--- a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.cpp
+++ b/lldb/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 @@ lldb::SearchFilterSP AppleObjCRuntime::CreateExceptionSearchFilter() {
}
}
+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(
diff --git a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.h b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.h
index ac2f5c83ca7..86606460014 100644
--- a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.h
+++ b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.h
@@ -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,
diff --git a/lldb/source/Target/Thread.cpp b/lldb/source/Target/Thread.cpp
index 65aca860fc3..3662f1bea62 100644
--- a/lldb/source/Target/Thread.cpp
+++ b/lldb/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 @@ 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);
+}
OpenPOWER on IntegriCloud