Index: include/lldb/Target/SystemRuntime.h =================================================================== --- include/lldb/Target/SystemRuntime.h +++ include/lldb/Target/SystemRuntime.h @@ -327,6 +327,16 @@ virtual void AddThreadExtendedInfoPacketHints( lldb_private::StructuredData::ObjectSP dict) {} + virtual lldb::ValueObjectSP GetExceptionObjectForThread( + lldb::ThreadSP thread_sp) { + return lldb::ValueObjectSP(); + } + + virtual lldb::ThreadSP GetBacktraceThreadFromException( + lldb::ValueObjectSP thread_sp) { + return lldb::ThreadSP(); + } + /// Determine whether it is safe to run an expression on a given thread /// /// If a system must not run functions on a thread in some particular state, 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 @@ -26,11 +26,12 @@ def test_objc_exceptions_1(self): self.build() - self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + target = self.dbg.CreateTarget(self.getBuildArtifact("a.out")) + self.assertTrue(target, VALID_TARGET) lldbutil.run_break_set_by_file_and_line( self, "main.m", self.line, num_expected_locations=1, loc_exact=True) - lldbutil.run_break_set_by_symbol(self, "objc_exception_throw") + bp = lldbutil.run_break_set_by_symbol(self, "objc_exception_throw") self.runCmd("run", RUN_SUCCEEDED) @@ -77,3 +78,29 @@ 'userInfo = ', '1 key/value pair', 'reserved = ', ]) + + self.runCmd("breakpoint delete %d" % bp) + + self.runCmd("continue") + + # 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.m:15', + 'a.out`rethrow at main.m:21', + '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", "rethrow", "main"]: + self.assertEqual(len([f for f in history_thread.frames + if f.GetFunctionName() == n]), 1) 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 @@ -15,6 +15,15 @@ @throw [[NSException alloc] initWithName:@"ThrownException" reason:@"SomeReason" userInfo:info]; } +void rethrow() +{ + @try { + foo(); + } @catch(NSException *e) { + @throw; + } +} + int main (int argc, const char * argv[]) { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; @@ -30,6 +39,9 @@ } NSLog(@"1"); // Set break point at this line. + + rethrow(); + [pool drain]; return 0; } Index: scripts/interface/SBThread.i =================================================================== --- scripts/interface/SBThread.i +++ scripts/interface/SBThread.i @@ -368,6 +368,12 @@ uint32_t GetExtendedBacktraceOriginatingIndexID(); + lldb::SBThread + GetCurrentExceptionBacktrace(); + + lldb::SBValue + GetCurrentException(); + %feature("autodoc"," Takes no arguments, returns a bool. lldb may be able to detect that function calls should not be executed Index: source/Plugins/SystemRuntime/MacOSX/SystemRuntimeMacOSX.h =================================================================== --- source/Plugins/SystemRuntime/MacOSX/SystemRuntimeMacOSX.h +++ source/Plugins/SystemRuntime/MacOSX/SystemRuntimeMacOSX.h @@ -103,6 +103,12 @@ void AddThreadExtendedInfoPacketHints( lldb_private::StructuredData::ObjectSP dict) override; + virtual lldb::ValueObjectSP + GetExceptionObjectForThread(lldb::ThreadSP thread_sp) override; + + virtual lldb::ThreadSP + GetBacktraceThreadFromException(lldb::ValueObjectSP thread_sp) override; + bool SafeToCallFunctionsOnThisThread(lldb::ThreadSP thread_sp) override; //------------------------------------------------------------------ Index: source/Plugins/SystemRuntime/MacOSX/SystemRuntimeMacOSX.cpp =================================================================== --- source/Plugins/SystemRuntime/MacOSX/SystemRuntimeMacOSX.cpp +++ source/Plugins/SystemRuntime/MacOSX/SystemRuntimeMacOSX.cpp @@ -13,6 +13,9 @@ #include "lldb/Core/ModuleSpec.h" #include "lldb/Core/PluginManager.h" #include "lldb/Core/Section.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/Symbol/SymbolContext.h" @@ -224,6 +227,116 @@ } } +ValueObjectSP +SystemRuntimeMacOSX::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; +} + +ThreadSP SystemRuntimeMacOSX::GetBacktraceThreadFromException( + lldb::ValueObjectSP exception_sp) { + ValueObjectSP reserved = + exception_sp->GetChildMemberWithName(ConstString("reserved"), true); + if (!reserved) + return ThreadSP(); + + reserved = reserved->GetSyntheticValue(); + if (!reserved) + return ThreadSP(); + + ValueObjectSP return_addresses; + for (size_t idx = 0; idx < reserved->GetNumChildren(); idx++) { + ValueObjectSP pair = reserved->GetChildAtIndex(idx, true); + auto key = pair->GetChildAtIndex(0, true); + std::string key_string; + key->GetSummaryAsCString(key_string, TypeSummaryOptions()); + if (key_string == "\"callStackReturnAddresses\"") { + auto value = pair->GetChildAtIndex(1, true); + return_addresses = value; + break; + } + } + + if (!return_addresses) + return ThreadSP(); + + return_addresses = return_addresses->GetDynamicValue(eDynamicDontRunTarget); + 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 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; +} + bool SystemRuntimeMacOSX::SafeToCallFunctionsOnThisThread(ThreadSP thread_sp) { if (thread_sp && thread_sp->GetStackFrameCount() > 0 && thread_sp->GetFrameWithConcreteFrameIndex(0)) { Index: source/Target/Thread.cpp =================================================================== --- source/Target/Thread.cpp +++ source/Target/Thread.cpp @@ -2212,17 +2212,37 @@ ValueObjectSP Thread::GetCurrentException() { StackFrameSP frame_sp(GetStackFrameAtIndex(0)); - if (!frame_sp) return ValueObjectSP(); - - RecognizedStackFrameSP recognized_frame(frame_sp->GetRecognizedFrame()); - if (!recognized_frame) return ValueObjectSP(); + if (frame_sp) { + RecognizedStackFrameSP recognized_frame(frame_sp->GetRecognizedFrame()); + if (recognized_frame && recognized_frame->IsThrowingObjCException()) { + ValueObjectSP exception_from_recognizer = + recognized_frame->GetObjCExceptionObject(); + if (exception_from_recognizer) { + return exception_from_recognizer; + } + } + } - if (!recognized_frame->IsThrowingObjCException()) return ValueObjectSP(); + SystemRuntime *runtime = GetProcess()->GetSystemRuntime(); + if (runtime) { + ValueObjectSP exception_from_runtime = + runtime->GetExceptionObjectForThread(shared_from_this()); + if (exception_from_runtime) { + return exception_from_runtime; + } + } - return recognized_frame->GetObjCExceptionObject(); + return ValueObjectSP(); } ThreadSP Thread::GetCurrentExceptionBacktrace() { - // TODO - return ThreadSP(); + ValueObjectSP exception = GetCurrentException(); + if (!exception) + return ThreadSP(); + + SystemRuntime *runtime = GetProcess()->GetSystemRuntime(); + if (!runtime) + return ThreadSP(); + + return runtime->GetBacktraceThreadFromException(exception); }