diff options
author | Kuba Mracek <mracek@apple.com> | 2018-11-28 22:01:52 +0000 |
---|---|---|
committer | Kuba Mracek <mracek@apple.com> | 2018-11-28 22:01:52 +0000 |
commit | 67f5950908cd0be8335de95cf321f01b2abce206 (patch) | |
tree | e754a779b5d55b072acfc3b0a967b1b5111c5389 | |
parent | 87edb33db4785fd7d4ef64dc4f58dbee5a1c4cf4 (diff) |
[lldb] Add GetCurrentException APIs to SBThread, add frame recognizer for objc_exception_throw for Obj-C runtimes
This adds new APIs and a command to deal with exceptions (mostly Obj-C exceptions): SBThread and Thread get GetCurrentException API, which returns an SBValue/ValueObjectSP with the current exception for a thread. "Current" means an exception that is currently being thrown, caught or otherwise processed. In this patch, we only know about the exception when in objc_exception_throw, but subsequent patches will expand this (and add GetCurrentExceptionBacktrace, which will return an SBThread/ThreadSP containing a historical thread backtrace retrieved from the exception object. Currently unimplemented, subsequent patches will implement this).
Extracting the exception from objc_exception_throw is implemented by adding a frame recognizer.
This also add a new sub-command "thread exception", which prints the current exception.
Differential Revision: https://reviews.llvm.org/D43886
12 files changed, 187 insertions, 10 deletions
diff --git a/lldb/include/lldb/API/SBThread.h b/lldb/include/lldb/API/SBThread.h index afc05d2c61a..24224cd8170 100644 --- a/lldb/include/lldb/API/SBThread.h +++ b/lldb/include/lldb/API/SBThread.h @@ -198,6 +198,11 @@ public: uint32_t GetExtendedBacktraceOriginatingIndexID(); + SBValue GetCurrentException(); + + // TODO(kubamracek): Extract backtrace from SBValue into SBThread + // SBThread GetCurrentExceptionBacktrace(); + bool SafeToCallFunctions(); #ifndef SWIG diff --git a/lldb/include/lldb/Target/StackFrameRecognizer.h b/lldb/include/lldb/Target/StackFrameRecognizer.h index 6ee613bb21a..35ec23b754f 100644 --- a/lldb/include/lldb/Target/StackFrameRecognizer.h +++ b/lldb/include/lldb/Target/StackFrameRecognizer.h @@ -30,6 +30,9 @@ public: virtual lldb::ValueObjectListSP GetRecognizedArguments() { return m_arguments; } + virtual lldb::ValueObjectSP GetExceptionObject() { + return lldb::ValueObjectSP(); + } virtual ~RecognizedStackFrame(){}; protected: @@ -97,7 +100,8 @@ private: class StackFrameRecognizerManager { public: static void AddRecognizer(lldb::StackFrameRecognizerSP recognizer, - ConstString &module, ConstString &symbol, + const ConstString &module, + const ConstString &symbol, bool first_instruction_only = true); static void AddRecognizer(lldb::StackFrameRecognizerSP recognizer, diff --git a/lldb/include/lldb/Target/Thread.h b/lldb/include/lldb/Target/Thread.h index 47d3b087ef0..be8f7789cc7 100644 --- a/lldb/include/lldb/Target/Thread.h +++ b/lldb/include/lldb/Target/Thread.h @@ -1253,6 +1253,11 @@ public: //---------------------------------------------------------------------- virtual uint64_t GetExtendedBacktraceToken() { return LLDB_INVALID_ADDRESS; } + lldb::ValueObjectSP GetCurrentException(); + + // TODO(kubamracek): Extract backtrace from ValueObjectSP into ThreadSP + // lldb::ThreadSP GetCurrentExceptionBacktrace(); + protected: friend class ThreadPlan; friend class ThreadList; 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 8d260b235fc..6959f2ef75d 100644 --- a/lldb/packages/Python/lldbsuite/test/lang/objc/exceptions/TestObjCExceptions.py +++ b/lldb/packages/Python/lldbsuite/test/lang/objc/exceptions/TestObjCExceptions.py @@ -23,6 +23,16 @@ class ObjCExceptionsTestCase(TestBase): target = self.dbg.CreateTarget(self.getBuildArtifact("a.out")) self.assertTrue(target, VALID_TARGET) + lldbutil.run_to_name_breakpoint(self, "objc_exception_throw") + + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs=['stopped', 'stop reason = breakpoint']) + + self.expect('thread exception', substrs=[ + '(NSException *) exception = ', + 'name: "ThrownException" - reason: "SomeReason"', + ]) + lldbutil.run_to_source_breakpoint(self, "// Set break point at this line.", lldb.SBFileSpec("main.m")) self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, diff --git a/lldb/source/API/SBThread.cpp b/lldb/source/API/SBThread.cpp index a38b9f6a310..5771b37ea8b 100644 --- a/lldb/source/API/SBThread.cpp +++ b/lldb/source/API/SBThread.cpp @@ -1484,6 +1484,21 @@ uint32_t SBThread::GetExtendedBacktraceOriginatingIndexID() { return LLDB_INVALID_INDEX32; } +SBValue SBThread::GetCurrentException() { + ThreadSP thread_sp(m_opaque_sp->GetThreadSP()); + if (!thread_sp) return SBValue(); + + 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()); if (thread_sp) diff --git a/lldb/source/Commands/CommandObjectThread.cpp b/lldb/source/Commands/CommandObjectThread.cpp index 95c5666a56f..57dd2a33226 100644 --- a/lldb/source/Commands/CommandObjectThread.cpp +++ b/lldb/source/Commands/CommandObjectThread.cpp @@ -1520,6 +1520,52 @@ public: }; //------------------------------------------------------------------------- +// CommandObjectThreadException +//------------------------------------------------------------------------- + +class CommandObjectThreadException : public CommandObjectIterateOverThreads { + public: + CommandObjectThreadException(CommandInterpreter &interpreter) + : CommandObjectIterateOverThreads( + interpreter, "thread exception", + "Display the current exception object for a thread. Defaults to " + "the current thread.", + "thread exception", + eCommandRequiresProcess | eCommandTryTargetAPILock | + eCommandProcessMustBeLaunched | eCommandProcessMustBePaused) {} + + ~CommandObjectThreadException() override = default; + + bool HandleOneThread(lldb::tid_t tid, CommandReturnObject &result) override { + ThreadSP thread_sp = + m_exe_ctx.GetProcessPtr()->GetThreadList().FindThreadByID(tid); + if (!thread_sp) { + result.AppendErrorWithFormat("thread no longer exists: 0x%" PRIx64 "\n", + tid); + result.SetStatus(eReturnStatusFailed); + return false; + } + + Stream &strm = result.GetOutputStream(); + ValueObjectSP exception_object_sp = thread_sp->GetCurrentException(); + if (exception_object_sp) { + 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, + num_frames_with_source, stop_format); + }*/ + + return true; + } +}; + +//------------------------------------------------------------------------- // CommandObjectThreadReturn //------------------------------------------------------------------------- @@ -2064,6 +2110,9 @@ CommandObjectMultiwordThread::CommandObjectMultiwordThread( CommandObjectSP(new CommandObjectThreadUntil(interpreter))); LoadSubCommand("info", CommandObjectSP(new CommandObjectThreadInfo(interpreter))); + LoadSubCommand( + "exception", + CommandObjectSP(new CommandObjectThreadException(interpreter))); LoadSubCommand("step-in", CommandObjectSP(new CommandObjectThreadStepWithTypeAndScope( interpreter, "thread step-in", diff --git a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.cpp b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.cpp index bc574808415..74b17f539d9 100644 --- a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.cpp +++ b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.cpp @@ -455,13 +455,19 @@ lldb::SearchFilterSP AppleObjCRuntime::CreateExceptionSearchFilter() { if (target.GetArchitecture().GetTriple().getVendor() == llvm::Triple::Apple) { FileSpecList filter_modules; - filter_modules.Append(FileSpec("libobjc.A.dylib")); + filter_modules.Append(std::get<0>(GetExceptionThrowLocation())); return target.GetSearchFilterForModuleList(&filter_modules); } else { return LanguageRuntime::CreateExceptionSearchFilter(); } } +std::tuple<FileSpec, ConstString> +AppleObjCRuntime::GetExceptionThrowLocation() { + return std::make_tuple( + FileSpec("libobjc.A.dylib"), ConstString("objc_exception_throw")); +} + void AppleObjCRuntime::ReadObjCLibraryIfNeeded(const ModuleList &module_list) { if (!HasReadObjCLibrary()) { std::lock_guard<std::recursive_mutex> guard(module_list.GetMutex()); diff --git a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.h b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.h index 46ab11d0da6..ac2f5c83ca7 100644 --- a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.h +++ b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.h @@ -86,6 +86,8 @@ public: bool ExceptionBreakpointsExplainStop(lldb::StopInfoSP stop_reason) override; lldb::SearchFilterSP CreateExceptionSearchFilter() override; + + static std::tuple<FileSpec, ConstString> GetExceptionThrowLocation(); uint32_t GetFoundationVersion(); diff --git a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV1.cpp b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV1.cpp index dc3955db9a5..aaecfb27e90 100644 --- a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV1.cpp +++ b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV1.cpp @@ -113,8 +113,9 @@ AppleObjCRuntimeV1::CreateExceptionResolver(Breakpoint *bkpt, bool catch_bp, if (throw_bp) resolver_sp.reset(new BreakpointResolverName( - bkpt, "objc_exception_throw", eFunctionNameTypeBase, - eLanguageTypeUnknown, Breakpoint::Exact, 0, eLazyBoolNo)); + bkpt, std::get<1>(GetExceptionThrowLocation()).AsCString(), + eFunctionNameTypeBase, eLanguageTypeUnknown, Breakpoint::Exact, 0, + eLazyBoolNo)); // FIXME: don't do catch yet. return resolver_sp; } diff --git a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp index b644db41dbf..bbe6dd8e8d1 100644 --- a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp +++ b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp @@ -25,6 +25,7 @@ #include "lldb/Core/Module.h" #include "lldb/Core/PluginManager.h" #include "lldb/Core/Section.h" +#include "lldb/Core/ValueObjectConstResult.h" #include "lldb/Core/ValueObjectVariable.h" #include "lldb/Expression/DiagnosticManager.h" #include "lldb/Expression/FunctionCaller.h" @@ -39,10 +40,12 @@ #include "lldb/Symbol/Symbol.h" #include "lldb/Symbol/TypeList.h" #include "lldb/Symbol/VariableList.h" +#include "lldb/Target/ABI.h" #include "lldb/Target/ExecutionContext.h" #include "lldb/Target/Platform.h" #include "lldb/Target/Process.h" #include "lldb/Target/RegisterContext.h" +#include "lldb/Target/StackFrameRecognizer.h" #include "lldb/Target/Target.h" #include "lldb/Target/Thread.h" #include "lldb/Utility/ConstString.h" @@ -373,6 +376,8 @@ ExtractRuntimeGlobalSymbol(Process *process, ConstString name, } } +static void RegisterObjCExceptionRecognizer(); + AppleObjCRuntimeV2::AppleObjCRuntimeV2(Process *process, const ModuleSP &objc_module_sp) : AppleObjCRuntime(process), m_get_class_info_code(), @@ -393,6 +398,7 @@ AppleObjCRuntimeV2::AppleObjCRuntimeV2(Process *process, static const ConstString g_gdb_object_getClass("gdb_object_getClass"); m_has_object_getClass = (objc_module_sp->FindFirstSymbolWithNameAndType( g_gdb_object_getClass, eSymbolTypeCode) != NULL); + RegisterObjCExceptionRecognizer(); } bool AppleObjCRuntimeV2::GetDynamicTypeAndAddress( @@ -799,8 +805,9 @@ AppleObjCRuntimeV2::CreateExceptionResolver(Breakpoint *bkpt, bool catch_bp, if (throw_bp) resolver_sp.reset(new BreakpointResolverName( - bkpt, "objc_exception_throw", eFunctionNameTypeBase, - eLanguageTypeUnknown, Breakpoint::Exact, 0, eLazyBoolNo)); + bkpt, std::get<1>(GetExceptionThrowLocation()).AsCString(), + eFunctionNameTypeBase, eLanguageTypeUnknown, Breakpoint::Exact, 0, + eLazyBoolNo)); // FIXME: We don't do catch breakpoints for ObjC yet. // Should there be some way for the runtime to specify what it can do in this // regard? @@ -2592,3 +2599,59 @@ void AppleObjCRuntimeV2::GetValuesForGlobalCFBooleans(lldb::addr_t &cf_true, } else this->AppleObjCRuntime::GetValuesForGlobalCFBooleans(cf_true, cf_false); } + +#pragma mark Frame recognizers + +class ObjCExceptionRecognizedStackFrame : public RecognizedStackFrame { + public: + ObjCExceptionRecognizedStackFrame(StackFrameSP frame_sp) { + ThreadSP thread_sp = frame_sp->GetThread(); + ProcessSP process_sp = thread_sp->GetProcess(); + + const lldb::ABISP &abi = process_sp->GetABI(); + if (!abi) return; + + CompilerType voidstar = process_sp->GetTarget() + .GetScratchClangASTContext() + ->GetBasicType(lldb::eBasicTypeVoid) + .GetPointerType(); + + ValueList args; + Value input_value; + input_value.SetCompilerType(voidstar); + args.PushValue(input_value); + + if (!abi->GetArgumentValues(*thread_sp, args)) return; + + addr_t exception_addr = args.GetValueAtIndex(0)->GetScalar().ULongLong(); + + Value value(exception_addr); + value.SetCompilerType(voidstar); + exception = ValueObjectConstResult::Create(frame_sp.get(), value, + ConstString("exception")); + exception = exception->GetDynamicValue(eDynamicDontRunTarget); + } + + ValueObjectSP exception; + + lldb::ValueObjectSP GetExceptionObject() override { return exception; } +}; + +class ObjCExceptionThrowFrameRecognizer : public StackFrameRecognizer { + lldb::RecognizedStackFrameSP RecognizeFrame(lldb::StackFrameSP frame) { + return lldb::RecognizedStackFrameSP( + new ObjCExceptionRecognizedStackFrame(frame)); + }; +}; + +static void RegisterObjCExceptionRecognizer() { + static llvm::once_flag g_once_flag; + llvm::call_once(g_once_flag, []() { + FileSpec module; + ConstString function; + std::tie(module, function) = AppleObjCRuntime::GetExceptionThrowLocation(); + StackFrameRecognizerManager::AddRecognizer( + StackFrameRecognizerSP(new ObjCExceptionThrowFrameRecognizer()), + module.GetFilename(), function, /*first_instruction_only*/ true); + }); +} diff --git a/lldb/source/Target/StackFrameRecognizer.cpp b/lldb/source/Target/StackFrameRecognizer.cpp index f3f090a2a03..152f4a198e2 100644 --- a/lldb/source/Target/StackFrameRecognizer.cpp +++ b/lldb/source/Target/StackFrameRecognizer.cpp @@ -49,8 +49,9 @@ ScriptedStackFrameRecognizer::RecognizeFrame(lldb::StackFrameSP frame) { class StackFrameRecognizerManagerImpl { public: - void AddRecognizer(StackFrameRecognizerSP recognizer, ConstString &module, - ConstString &symbol, bool first_instruction_only) { + void AddRecognizer(StackFrameRecognizerSP recognizer, + const ConstString &module, const ConstString &symbol, + bool first_instruction_only) { m_recognizers.push_front({(uint32_t)m_recognizers.size(), false, recognizer, false, module, RegularExpressionSP(), symbol, RegularExpressionSP(), first_instruction_only}); @@ -152,8 +153,8 @@ StackFrameRecognizerManagerImpl &GetStackFrameRecognizerManagerImpl() { } void StackFrameRecognizerManager::AddRecognizer( - StackFrameRecognizerSP recognizer, ConstString &module, ConstString &symbol, - bool first_instruction_only) { + StackFrameRecognizerSP recognizer, const ConstString &module, + const ConstString &symbol, bool first_instruction_only) { GetStackFrameRecognizerManagerImpl().AddRecognizer(recognizer, module, symbol, first_instruction_only); } diff --git a/lldb/source/Target/Thread.cpp b/lldb/source/Target/Thread.cpp index 404847eabe0..65aca860fc3 100644 --- a/lldb/source/Target/Thread.cpp +++ b/lldb/source/Target/Thread.cpp @@ -25,6 +25,7 @@ #include "lldb/Target/ExecutionContext.h" #include "lldb/Target/Process.h" #include "lldb/Target/RegisterContext.h" +#include "lldb/Target/StackFrameRecognizer.h" #include "lldb/Target/StopInfo.h" #include "lldb/Target/SystemRuntime.h" #include "lldb/Target/Target.h" @@ -2199,3 +2200,18 @@ Status Thread::StepOut() { } return error; } + +ValueObjectSP Thread::GetCurrentException() { + StackFrameSP frame_sp(GetStackFrameAtIndex(0)); + if (!frame_sp) return ValueObjectSP(); + + RecognizedStackFrameSP recognized_frame(frame_sp->GetRecognizedFrame()); + if (!recognized_frame) return ValueObjectSP(); + + return recognized_frame->GetExceptionObject(); +} + +/* TODO(kubamracek) +ThreadSP Thread::GetCurrentExceptionBacktrace() { + return ThreadSP(); +}*/ |