summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKuba Mracek <mracek@apple.com>2018-12-20 02:01:59 +0000
committerKuba Mracek <mracek@apple.com>2018-12-20 02:01:59 +0000
commit6bbb3cfa15d0be93c417ea5b89b9213f4ca08b6b (patch)
tree077c80854a711f6ca234aad1e1b856af7cf2acd9
parent9ec54ea2cd92922c0268526798a3b6e6518506ae (diff)
[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
-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);
+}