[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 llvm-svn: 347813
This commit is contained in:
parent
a3e7a167c4
commit
e60bc53b46
|
@ -198,6 +198,11 @@ public:
|
|||
|
||||
uint32_t GetExtendedBacktraceOriginatingIndexID();
|
||||
|
||||
SBValue GetCurrentException();
|
||||
|
||||
// TODO(kubamracek): Extract backtrace from SBValue into SBThread
|
||||
// SBThread GetCurrentExceptionBacktrace();
|
||||
|
||||
bool SafeToCallFunctions();
|
||||
|
||||
#ifndef SWIG
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -1519,6 +1519,52 @@ public:
|
|||
CommandOptions m_options;
|
||||
};
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// 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",
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}*/
|
||||
|
|
Loading…
Reference in New Issue