Index: include/lldb/API/SBThread.h =================================================================== --- include/lldb/API/SBThread.h +++ include/lldb/API/SBThread.h @@ -198,6 +198,10 @@ uint32_t GetExtendedBacktraceOriginatingIndexID(); + SBThread GetCurrentExceptionBacktrace(); + + SBValue GetCurrentException(); + bool SafeToCallFunctions(); #ifndef SWIG Index: include/lldb/Target/StackFrameRecognizer.h =================================================================== --- include/lldb/Target/StackFrameRecognizer.h +++ include/lldb/Target/StackFrameRecognizer.h @@ -34,6 +34,9 @@ virtual lldb::ValueObjectListSP GetRecognizedArguments() { return m_arguments; } + virtual lldb::ValueObjectSP GetExceptionObject() { + return lldb::ValueObjectSP(); + } virtual ~RecognizedStackFrame(){}; protected: @@ -101,7 +104,7 @@ 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, Index: include/lldb/Target/Thread.h =================================================================== --- include/lldb/Target/Thread.h +++ include/lldb/Target/Thread.h @@ -1236,6 +1236,10 @@ //---------------------------------------------------------------------- virtual uint64_t GetExtendedBacktraceToken() { return LLDB_INVALID_ADDRESS; } + lldb::ThreadSP GetCurrentExceptionBacktrace(); + + lldb::ValueObjectSP GetCurrentException(); + protected: friend class ThreadPlan; friend class ThreadList; Index: source/API/SBThread.cpp =================================================================== --- source/API/SBThread.cpp +++ source/API/SBThread.cpp @@ -1466,6 +1466,20 @@ return LLDB_INVALID_INDEX32; } +SBThread SBThread::GetCurrentExceptionBacktrace() { + ThreadSP thread_sp(m_opaque_sp->GetThreadSP()); + if (!thread_sp) return SBThread(); + + return SBThread(thread_sp->GetCurrentExceptionBacktrace()); +} + +SBValue SBThread::GetCurrentException() { + ThreadSP thread_sp(m_opaque_sp->GetThreadSP()); + if (!thread_sp) return SBValue(); + + return SBValue(thread_sp->GetCurrentException()); +} + bool SBThread::SafeToCallFunctions() { ThreadSP thread_sp(m_opaque_sp->GetThreadSP()); if (thread_sp) Index: source/Commands/CommandObjectThread.cpp =================================================================== --- source/Commands/CommandObjectThread.cpp +++ source/Commands/CommandObjectThread.cpp @@ -1515,6 +1515,113 @@ }; //------------------------------------------------------------------------- +// CommandObjectThreadException +//------------------------------------------------------------------------- + +static OptionDefinition g_thread_exception_options[] = { + // clang-format off + { LLDB_OPT_SET_1, false, "count", 'c', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeCount, "How many frames to display (-1 for all)" }, + { LLDB_OPT_SET_1, false, "start", 's', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeFrameIndex, "Frame in which to start the backtrace" }, + // clang-format on +}; + +class CommandObjectThreadException : public CommandObjectIterateOverThreads { + public: + class CommandOptions : public Options { + public: + CommandOptions() : Options() { OptionParsingStarting(nullptr); } + + ~CommandOptions() override = default; + + Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, + ExecutionContext *execution_context) override { + Status error; + const int short_option = m_getopt_table[option_idx].val; + + switch (short_option) { + case 'c': { + int32_t input_count = 0; + if (option_arg.getAsInteger(0, m_count)) { + m_count = UINT32_MAX; + error.SetErrorStringWithFormat( + "invalid integer value for option '%c'", short_option); + } else if (input_count < 0) + m_count = UINT32_MAX; + } break; + case 's': + if (option_arg.getAsInteger(0, m_start)) + error.SetErrorStringWithFormat( + "invalid integer value for option '%c'", short_option); + break; + default: + error.SetErrorStringWithFormat("invalid short option character '%c'", + short_option); + break; + } + return error; + } + + void OptionParsingStarting(ExecutionContext *execution_context) override { + m_count = UINT32_MAX; + m_start = 0; + } + + llvm::ArrayRef GetDefinitions() override { + return llvm::makeArrayRef(g_thread_exception_options); + } + + // Instance variables to hold the values for command options. + uint32_t m_count; + uint32_t m_start; + }; + + 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), + m_options() { + m_add_return = false; + } + + ~CommandObjectThreadException() override = default; + + Options *GetOptions() override { return &m_options; } + + 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); + } + + 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; + } + + CommandOptions m_options; +}; + +//------------------------------------------------------------------------- // CommandObjectThreadReturn //------------------------------------------------------------------------- @@ -2059,6 +2166,9 @@ 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", Index: source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp =================================================================== --- source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp +++ source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp @@ -29,6 +29,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" @@ -43,10 +44,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" @@ -377,6 +380,8 @@ } } +static void RegisterObjCExceptionRecognizer(); + AppleObjCRuntimeV2::AppleObjCRuntimeV2(Process *process, const ModuleSP &objc_module_sp) : AppleObjCRuntime(process), m_get_class_info_code(), @@ -397,6 +402,7 @@ 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( @@ -2596,3 +2602,57 @@ } 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, []() { + StackFrameRecognizerManager::AddRecognizer( + StackFrameRecognizerSP(new ObjCExceptionThrowFrameRecognizer()), + ConstString("libobjc.A.dylib"), ConstString("objc_exception_throw"), + /*first_instruction_only*/ true); + }); +} Index: source/Target/StackFrameRecognizer.cpp =================================================================== --- source/Target/StackFrameRecognizer.cpp +++ source/Target/StackFrameRecognizer.cpp @@ -53,8 +53,8 @@ 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}); @@ -156,7 +156,7 @@ } void StackFrameRecognizerManager::AddRecognizer( - StackFrameRecognizerSP recognizer, ConstString &module, ConstString &symbol, + StackFrameRecognizerSP recognizer, const ConstString &module, const ConstString &symbol, bool first_instruction_only) { GetStackFrameRecognizerManagerImpl().AddRecognizer(recognizer, module, symbol, first_instruction_only); Index: source/Target/Thread.cpp =================================================================== --- source/Target/Thread.cpp +++ source/Target/Thread.cpp @@ -29,6 +29,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" @@ -2190,3 +2191,18 @@ } 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(); +} + +ThreadSP Thread::GetCurrentExceptionBacktrace() { + // TODO + return ThreadSP(); +}