Index: include/lldb/API/SBThread.h =================================================================== --- include/lldb/API/SBThread.h +++ include/lldb/API/SBThread.h @@ -184,6 +184,10 @@ uint32_t GetExtendedBacktraceOriginatingIndexID(); + SBThread GetCurrentExceptionBacktrace(); + + SBValue GetCurrentException(); + bool SafeToCallFunctions(); #ifndef SWIG Index: include/lldb/Target/FrameRecognizer.h =================================================================== --- /dev/null +++ include/lldb/Target/FrameRecognizer.h @@ -0,0 +1,50 @@ +//===-- FrameRecognizer.h ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_FrameRecognizer_h_ +#define liblldb_FrameRecognizer_h_ + +// C Includes +// C++ Includes +#include +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private-forward.h" +#include "lldb/lldb-public.h" + +namespace lldb_private { + +class RecognizedStackFrame + : std::enable_shared_from_this { + public: + virtual bool IsThrowingObjCException() { return false; } + + virtual lldb::ValueObjectSP GetObjCExceptionObject() { + return lldb::ValueObjectSP(); + } + + virtual ~RecognizedStackFrame(){}; +}; + +typedef std::shared_ptr RecognizedStackFrameSP; + +class FrameRecognizer : std::enable_shared_from_this { + public: + virtual RecognizedStackFrameSP RecognizeFrame(lldb::StackFrameSP frame) { + return RecognizedStackFrameSP(new RecognizedStackFrame()); + }; + + virtual ~FrameRecognizer(){}; +}; + +typedef std::shared_ptr FrameRecognizerSP; + +} // namespace lldb_private + +#endif // liblldb_FrameRecognizer_h_ Index: include/lldb/Target/Platform.h =================================================================== --- include/lldb/Target/Platform.h +++ include/lldb/Target/Platform.h @@ -24,6 +24,7 @@ #include "lldb/Core/PluginInterface.h" #include "lldb/Core/UserSettingsController.h" #include "lldb/Interpreter/Options.h" +#include "lldb/Target/FrameRecognizer.h" #include "lldb/Utility/ArchSpec.h" #include "lldb/Utility/ConstString.h" #include "lldb/Utility/FileSpec.h" @@ -872,6 +873,10 @@ virtual size_t ConnectToWaitingProcesses(lldb_private::Debugger &debugger, lldb_private::Status &error); + virtual FrameRecognizerSP GetFrameRecognizer() { + return FrameRecognizerSP(); + } + protected: bool m_is_host; // Set to true when we are able to actually set the OS version while Index: include/lldb/Target/StackFrame.h =================================================================== --- include/lldb/Target/StackFrame.h +++ include/lldb/Target/StackFrame.h @@ -23,6 +23,7 @@ #include "lldb/Core/ValueObjectList.h" #include "lldb/Symbol/SymbolContext.h" #include "lldb/Target/ExecutionContextScope.h" +#include "lldb/Target/FrameRecognizer.h" #include "lldb/Target/StackID.h" #include "lldb/Utility/Status.h" #include "lldb/Utility/StreamString.h" @@ -516,6 +517,8 @@ void CalculateExecutionContext(ExecutionContext &exe_ctx) override; + lldb_private::RecognizedStackFrameSP GetRecognizedFrame(); + protected: friend class StackFrameList; @@ -553,6 +556,7 @@ ValueObjectList m_variable_list_value_objects; // Value objects for each // variable in // m_variable_list_sp + RecognizedStackFrameSP m_recognized_frame; StreamString m_disassembly; std::recursive_mutex m_mutex; Index: include/lldb/Target/Thread.h =================================================================== --- include/lldb/Target/Thread.h +++ include/lldb/Target/Thread.h @@ -1249,6 +1249,10 @@ //---------------------------------------------------------------------- virtual uint64_t GetExtendedBacktraceToken() { return LLDB_INVALID_ADDRESS; } + lldb::ThreadSP GetCurrentExceptionBacktrace(); + + lldb::ValueObjectSP GetCurrentException(); + protected: friend class ThreadPlan; friend class ThreadList; Index: lldb.xcodeproj/project.pbxproj =================================================================== --- lldb.xcodeproj/project.pbxproj +++ lldb.xcodeproj/project.pbxproj @@ -784,6 +784,7 @@ 8CF02AEF19DD16B100B14BE0 /* InstrumentationRuntimeStopInfo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CF02AED19DD15CF00B14BE0 /* InstrumentationRuntimeStopInfo.cpp */; }; 9404957A1BEC497E00926025 /* NSError.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 940495781BEC497E00926025 /* NSError.cpp */; }; 9404957B1BEC497E00926025 /* NSException.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 940495791BEC497E00926025 /* NSException.cpp */; }; + 8CFDB678204677DA0052B399 /* DarwinFrameRecognizer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CFDB676204677D10052B399 /* DarwinFrameRecognizer.cpp */; }; 94094C6B163B6F840083A547 /* ValueObjectCast.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 94094C69163B6CD90083A547 /* ValueObjectCast.cpp */; }; 940B02F619DC96E700AD0F52 /* SBExecutionContext.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 940B02F519DC96E700AD0F52 /* SBExecutionContext.cpp */; }; 940B04D91A8984FF0045D5F7 /* argdumper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 940B04D81A8984FF0045D5F7 /* argdumper.cpp */; }; @@ -2695,6 +2696,9 @@ 8CF02AE619DCBF8400B14BE0 /* ASanRuntime.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASanRuntime.h; sourceTree = ""; }; 8CF02AED19DD15CF00B14BE0 /* InstrumentationRuntimeStopInfo.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = InstrumentationRuntimeStopInfo.cpp; path = source/Target/InstrumentationRuntimeStopInfo.cpp; sourceTree = ""; }; 8CF02AEE19DD15CF00B14BE0 /* InstrumentationRuntimeStopInfo.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = InstrumentationRuntimeStopInfo.h; path = include/lldb/Target/InstrumentationRuntimeStopInfo.h; sourceTree = ""; }; + 8CFDB676204677D10052B399 /* DarwinFrameRecognizer.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = DarwinFrameRecognizer.cpp; sourceTree = ""; }; + 8CFDB677204677D10052B399 /* DarwinFrameRecognizer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DarwinFrameRecognizer.h; sourceTree = ""; }; + 8CFDB67920467B390052B399 /* FrameRecognizer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = FrameRecognizer.h; path = include/lldb/Target/FrameRecognizer.h; sourceTree = ""; }; 94005E0313F438DF001EF42D /* python-wrapper.swig */ = {isa = PBXFileReference; lastKnownFileType = text; path = "python-wrapper.swig"; sourceTree = ""; }; 94005E0513F45A1B001EF42D /* embedded_interpreter.py */ = {isa = PBXFileReference; lastKnownFileType = text.script.python; name = embedded_interpreter.py; path = source/Interpreter/embedded_interpreter.py; sourceTree = ""; }; 940495781BEC497E00926025 /* NSError.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = NSError.cpp; path = Language/ObjC/NSError.cpp; sourceTree = ""; }; @@ -5406,6 +5410,7 @@ 26BC7DF210F1B81A00F91463 /* ExecutionContext.h */, 26BC7F3510F1B90C00F91463 /* ExecutionContext.cpp */, 26DAFD9711529BC7005A394E /* ExecutionContextScope.h */, + 8CFDB67920467B390052B399 /* FrameRecognizer.h */, 26BC179B18C7F2CB00D2196D /* JITLoader.h */, 26BC179718C7F2B300D2196D /* JITLoader.cpp */, 26BC179C18C7F2CB00D2196D /* JITLoaderList.h */, @@ -5604,6 +5609,8 @@ AF8AD6361BEC28C400150209 /* PlatformRemoteAppleWatch.h */, 2675F6FE1332BE690067997B /* PlatformRemoteiOS.cpp */, 2675F6FF1332BE690067997B /* PlatformRemoteiOS.h */, + 8CFDB676204677D10052B399 /* DarwinFrameRecognizer.cpp */, + 8CFDB677204677D10052B399 /* DarwinFrameRecognizer.h */, ); path = MacOSX; sourceTree = ""; @@ -7824,6 +7831,7 @@ 2689010813353E6F00698AC0 /* ThreadPlanStepUntil.cpp in Sources */, 2689010A13353E6F00698AC0 /* ThreadPlanTracer.cpp in Sources */, AF37E10A17C861F20061E18E /* ProcessRunLock.cpp in Sources */, + 8CFDB678204677DA0052B399 /* DarwinFrameRecognizer.cpp in Sources */, 26474CAA18D0CB070073DEBA /* RegisterContextFreeBSD_mips64.cpp in Sources */, 94A5B3971AB9FE8D00A5EE7F /* EmulateInstructionMIPS64.cpp in Sources */, 256CBDC01ADD11C000BC6CDC /* RegisterContextPOSIX_arm.cpp in Sources */, 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 @@ -30,12 +30,20 @@ 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") self.runCmd("run", RUN_SUCCEEDED) self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, substrs=['stopped', 'stop reason = breakpoint']) + self.expect('thread exception', substrs=[ + '(NSException *) exception = ', + 'name: "ThrownException" - reason: "SomeReason"', + ]) + + self.runCmd("continue") + self.expect( 'frame variable e1', substrs=[ Index: source/API/SBThread.cpp =================================================================== --- source/API/SBThread.cpp +++ source/API/SBThread.cpp @@ -1416,6 +1416,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 @@ -1511,6 +1511,113 @@ CommandOptions m_options; }; +//------------------------------------------------------------------------- +// CommandObjectThreadException +//------------------------------------------------------------------------- + +static OptionDefinition g_thread_exception_options[] = { + // clang-format off + { LLDB_OPT_SET_1, false, "count", 'c', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeCount, "How many frames to display (-1 for all)" }, + { LLDB_OPT_SET_1, false, "start", 's', OptionParser::eRequiredArgument, nullptr, 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 //------------------------------------------------------------------------- @@ -2054,6 +2161,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/Platform/MacOSX/CMakeLists.txt =================================================================== --- source/Plugins/Platform/MacOSX/CMakeLists.txt +++ source/Plugins/Platform/MacOSX/CMakeLists.txt @@ -1,4 +1,5 @@ list(APPEND PLUGIN_PLATFORM_MACOSX_SOURCES + DarwinFrameRecognizer.cpp PlatformDarwin.cpp PlatformDarwinKernel.cpp PlatformMacOSX.cpp Index: source/Plugins/Platform/MacOSX/DarwinFrameRecognizer.h =================================================================== --- /dev/null +++ source/Plugins/Platform/MacOSX/DarwinFrameRecognizer.h @@ -0,0 +1,27 @@ +//===-- DarwinFrameRecognizer.h ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_DarwinFrameRecognizer_h_ +#define liblldb_DarwinFrameRecognizer_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Target/FrameRecognizer.h" + +class DarwinFrameRecognizer + : public lldb_private::FrameRecognizer, + std::enable_shared_from_this { + public: + lldb_private::RecognizedStackFrameSP RecognizeFrame( + lldb::StackFrameSP frame) override; +}; + +#endif // liblldb_DarwinFrameRecognizer_h_ Index: source/Plugins/Platform/MacOSX/DarwinFrameRecognizer.cpp =================================================================== --- /dev/null +++ source/Plugins/Platform/MacOSX/DarwinFrameRecognizer.cpp @@ -0,0 +1,79 @@ +//===-- DarwinFrameRecognizer.cpp -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "DarwinFrameRecognizer.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/DataFormatters/FormattersHelpers.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; + +class DarwinObjCExceptionRecognizedStackFrame : public RecognizedStackFrame { + public: + DarwinObjCExceptionRecognizedStackFrame(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(); + + lldb_private::formatters::InferiorSizedWord exception_isw(exception_addr, + *process_sp); + exception = ValueObject::CreateValueObjectFromData( + "exception", exception_isw.GetAsData(process_sp->GetByteOrder()), + *frame_sp, voidstar); + exception = exception->GetDynamicValue(eDynamicDontRunTarget); + } + + ValueObjectSP exception; + + bool IsThrowingObjCException() override { return true; } + + lldb::ValueObjectSP GetObjCExceptionObject() override { return exception; } +}; + +RecognizedStackFrameSP DarwinFrameRecognizer::RecognizeFrame( + StackFrameSP frame) { + const SymbolContext &symctx = + frame->GetSymbolContext(eSymbolContextModule | eSymbolContextFunction); + ConstString func = symctx.GetFunctionName(); + ConstString mod = symctx.module_sp->GetFileSpec().GetFilename(); + + if (mod == ConstString("libobjc.A.dylib")) { + if (func == ConstString("objc_exception_throw")) { + return RecognizedStackFrameSP( + new DarwinObjCExceptionRecognizedStackFrame(frame)); + } + } + + return RecognizedStackFrameSP(new RecognizedStackFrame()); +} Index: source/Plugins/Platform/MacOSX/PlatformDarwin.h =================================================================== --- source/Plugins/Platform/MacOSX/PlatformDarwin.h +++ source/Plugins/Platform/MacOSX/PlatformDarwin.h @@ -85,6 +85,8 @@ static std::tuple ParseVersionBuildDir(llvm::StringRef str); + lldb_private::FrameRecognizerSP GetFrameRecognizer() override; + protected: void ReadLibdispatchOffsetsAddress(lldb_private::Process *process); @@ -137,6 +139,7 @@ std::string m_developer_directory; + lldb_private::FrameRecognizerSP m_frame_recognizer; private: DISALLOW_COPY_AND_ASSIGN(PlatformDarwin); Index: source/Plugins/Platform/MacOSX/PlatformDarwin.cpp =================================================================== --- source/Plugins/Platform/MacOSX/PlatformDarwin.cpp +++ source/Plugins/Platform/MacOSX/PlatformDarwin.cpp @@ -42,6 +42,7 @@ #include "llvm/ADT/STLExtras.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Threading.h" +#include "Plugins/Platform/MacOSX/DarwinFrameRecognizer.h" #if defined(__APPLE__) #include // for TARGET_OS_TV, TARGET_OS_WATCH @@ -1825,3 +1826,9 @@ } return Status(); } + +FrameRecognizerSP PlatformDarwin::GetFrameRecognizer() { + if (!m_frame_recognizer) + m_frame_recognizer.reset(new DarwinFrameRecognizer()); + return m_frame_recognizer; +} Index: source/Target/StackFrame.cpp =================================================================== --- source/Target/StackFrame.cpp +++ source/Target/StackFrame.cpp @@ -1918,3 +1918,14 @@ } return true; } + +RecognizedStackFrameSP StackFrame::GetRecognizedFrame() { + if (!m_recognized_frame) { + lldb_private::FrameRecognizerSP recognizer_sp = + CalculateTarget()->GetPlatform()->GetFrameRecognizer(); + if (recognizer_sp) { + m_recognized_frame = recognizer_sp->RecognizeFrame(CalculateStackFrame()); + } + } + return m_recognized_frame; +} Index: source/Target/Thread.cpp =================================================================== --- source/Target/Thread.cpp +++ source/Target/Thread.cpp @@ -2209,3 +2209,20 @@ } 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(); + + if (!recognized_frame->IsThrowingObjCException()) return ValueObjectSP(); + + return recognized_frame->GetObjCExceptionObject(); +} + +ThreadSP Thread::GetCurrentExceptionBacktrace() { + // TODO + return ThreadSP(); +}