[lldb] Introduce StackFrameRecognizer

This patch introduces a concept of "frame recognizer" and "recognized frame". This should be an extensible mechanism that retrieves information about special frames based on ABI, arguments or other special properties of that frame, even without source code. A few examples where that could be useful could be 1) objc_exception_throw, where we'd like to get the current exception, 2) terminate_with_reason and extracting the current terminate string, 3) recognizing Objective-C frames and automatically extracting the receiver+selector, or perhaps all arguments (based on selector).

Differential Revision: https://reviews.llvm.org/D44603

llvm-svn: 345678
This commit is contained in:
Kuba Mracek 2018-10-31 00:21:03 +00:00
parent 1079d7ccfe
commit ac0ba8c524
24 changed files with 1162 additions and 16 deletions

View File

@ -33,6 +33,10 @@ public:
void SetIncludeArguments(bool);
bool GetIncludeRecognizedArguments() const;
void SetIncludeRecognizedArguments(bool);
bool GetIncludeLocals() const;
void SetIncludeLocals(bool);

View File

@ -39,6 +39,8 @@ public:
bool include_frame_options : 1,
show_args : 1, // Frame option only (include_frame_options == true)
show_recognized_args : 1, // Frame option only (include_frame_options ==
// true)
show_locals : 1, // Frame option only (include_frame_options == true)
show_globals : 1, // Frame option only (include_frame_options == true)
use_regex : 1, show_scope : 1, show_decl : 1;

View File

@ -173,6 +173,17 @@ public:
return StructuredData::GenericSP();
}
virtual StructuredData::GenericSP
CreateFrameRecognizer(const char *class_name) {
return StructuredData::GenericSP();
}
virtual lldb::ValueObjectListSP GetRecognizedArguments(
const StructuredData::ObjectSP &implementor,
lldb::StackFrameSP frame_sp) {
return lldb::ValueObjectListSP();
}
virtual StructuredData::GenericSP
OSPlugin_CreatePluginObject(const char *class_name,
lldb::ProcessSP process_sp) {

View File

@ -544,6 +544,8 @@ public:
void CalculateExecutionContext(ExecutionContext &exe_ctx) override;
lldb::RecognizedStackFrameSP GetRecognizedFrame();
protected:
friend class StackFrameList;
@ -578,6 +580,7 @@ private:
ValueObjectList m_variable_list_value_objects; // Value objects for each
// variable in
// m_variable_list_sp
lldb::RecognizedStackFrameSP m_recognized_frame_sp;
StreamString m_disassembly;
std::recursive_mutex m_mutex;

View File

@ -0,0 +1,129 @@
//===-- StackFrameRecognizer.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_StackFrameRecognizer_h_
#define liblldb_StackFrameRecognizer_h_
// C Includes
// C++ Includes
// Other libraries and framework includes
// Project includes
#include "lldb/Core/ValueObjectList.h"
#include "lldb/Symbol/VariableList.h"
#include "lldb/Utility/StructuredData.h"
#include "lldb/lldb-private-forward.h"
#include "lldb/lldb-public.h"
namespace lldb_private {
/// @class RecognizedStackFrame
///
/// This class provides extra information about a stack frame that was
/// provided by a specific stack frame recognizer. Right now, this class only
/// holds recognized arguments (via GetRecognizedArguments).
class RecognizedStackFrame
: std::enable_shared_from_this<RecognizedStackFrame> {
public:
virtual lldb::ValueObjectListSP GetRecognizedArguments() {
return m_arguments;
}
virtual ~RecognizedStackFrame(){};
protected:
lldb::ValueObjectListSP m_arguments;
};
/// @class StackFrameRecognizer
///
/// A base class for frame recognizers. Subclasses (actual frame recognizers)
/// should implement RecognizeFrame to provide a RecognizedStackFrame for a
/// given stack frame.
class StackFrameRecognizer
: std::enable_shared_from_this<StackFrameRecognizer> {
public:
virtual lldb::RecognizedStackFrameSP RecognizeFrame(
lldb::StackFrameSP frame) {
return lldb::RecognizedStackFrameSP();
};
virtual std::string GetName() {
return "";
}
virtual ~StackFrameRecognizer(){};
};
#ifndef LLDB_DISABLE_PYTHON
/// @class ScriptedStackFrameRecognizer
///
/// Python implementation for frame recognizers. An instance of this class
/// tracks a particular Python classobject, which will be asked to recognize
/// stack frames.
class ScriptedStackFrameRecognizer : public StackFrameRecognizer {
lldb_private::ScriptInterpreter *m_interpreter;
lldb_private::StructuredData::ObjectSP m_python_object_sp;
std::string m_python_class;
public:
ScriptedStackFrameRecognizer(lldb_private::ScriptInterpreter *interpreter,
const char *pclass);
~ScriptedStackFrameRecognizer() {}
std::string GetName() override {
return GetPythonClassName();
}
const char *GetPythonClassName() { return m_python_class.c_str(); }
lldb::RecognizedStackFrameSP RecognizeFrame(
lldb::StackFrameSP frame) override;
private:
DISALLOW_COPY_AND_ASSIGN(ScriptedStackFrameRecognizer);
};
#endif
/// @class StackFrameRecognizerManager
///
/// Static class that provides a registry of known stack frame recognizers.
/// Has static methods to add, enumerate, remove, query and invoke recognizers.
class StackFrameRecognizerManager {
public:
static void AddRecognizer(lldb::StackFrameRecognizerSP recognizer,
ConstString &module, ConstString &symbol,
bool first_instruction_only = true);
static void AddRecognizer(lldb::StackFrameRecognizerSP recognizer,
lldb::RegularExpressionSP module,
lldb::RegularExpressionSP symbol,
bool first_instruction_only = true);
static void ForEach(
std::function<void(uint32_t recognizer_id, std::string recognizer_name,
std::string module, std::string symbol,
bool regexp)> const &callback);
static bool RemoveRecognizerWithID(uint32_t recognizer_id);
static void RemoveAllRecognizers();
static lldb::StackFrameRecognizerSP GetRecognizerForFrame(
lldb::StackFrameSP frame);
static lldb::RecognizedStackFrameSP RecognizeFrame(lldb::StackFrameSP frame);
};
} // namespace lldb_private
#endif // liblldb_StackFrameRecognizer_h_

View File

@ -184,6 +184,7 @@ class ProcessInstanceInfoMatch;
class ProcessLaunchInfo;
class Property;
struct PropertyDefinition;
class RecognizedStackFrame;
class RegisterCheckpoint;
class RegisterContext;
class RegisterLocation;
@ -208,6 +209,8 @@ class SourceManagerImpl;
class StackFrame;
class StackFrameImpl;
class StackFrameList;
class StackFrameRecognizer;
class StackFrameRecognizerManager;
class StackID;
class StopInfo;
class Stoppoint;
@ -414,6 +417,8 @@ typedef std::shared_ptr<lldb_private::Queue> QueueSP;
typedef std::weak_ptr<lldb_private::Queue> QueueWP;
typedef std::shared_ptr<lldb_private::QueueItem> QueueItemSP;
typedef std::shared_ptr<lldb_private::REPL> REPLSP;
typedef std::shared_ptr<lldb_private::RecognizedStackFrame>
RecognizedStackFrameSP;
typedef std::shared_ptr<lldb_private::ScriptSummaryFormat>
ScriptSummaryFormatSP;
typedef std::shared_ptr<lldb_private::ScriptInterpreter> ScriptInterpreterSP;
@ -429,6 +434,8 @@ typedef std::shared_ptr<lldb_private::StackFrame> StackFrameSP;
typedef std::unique_ptr<lldb_private::StackFrame> StackFrameUP;
typedef std::weak_ptr<lldb_private::StackFrame> StackFrameWP;
typedef std::shared_ptr<lldb_private::StackFrameList> StackFrameListSP;
typedef std::shared_ptr<lldb_private::StackFrameRecognizer>
StackFrameRecognizerSP;
typedef std::shared_ptr<lldb_private::StopInfo> StopInfoSP;
typedef std::shared_ptr<lldb_private::StoppointLocation> StoppointLocationSP;
typedef std::shared_ptr<lldb_private::Stream> StreamSP;

View File

@ -875,6 +875,7 @@
2689004C13353E0400698AC0 /* SourceManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E8F10F1B85900F91463 /* SourceManager.cpp */; };
268900F313353E6F00698AC0 /* StackFrame.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7F3810F1B90C00F91463 /* StackFrame.cpp */; };
268900F413353E6F00698AC0 /* StackFrameList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7F3910F1B90C00F91463 /* StackFrameList.cpp */; };
8CF46A6220522A9800423DDF /* StackFrameRecognizer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CF46A6120522A9000423DDF /* StackFrameRecognizer.cpp */; };
268900F513353E6F00698AC0 /* StackID.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7F3A10F1B90C00F91463 /* StackID.cpp */; };
2689004D13353E0400698AC0 /* State.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E9010F1B85900F91463 /* State.cpp */; };
9A3D43ED1F3237F900EB767C /* StateTest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9A3D43E21F3237D500EB767C /* StateTest.cpp */; };
@ -2935,6 +2936,8 @@
26BC7DF510F1B81A00F91463 /* StackFrame.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = StackFrame.h; path = include/lldb/Target/StackFrame.h; sourceTree = "<group>"; };
26BC7F3910F1B90C00F91463 /* StackFrameList.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = StackFrameList.cpp; path = source/Target/StackFrameList.cpp; sourceTree = "<group>"; };
26BC7DF610F1B81A00F91463 /* StackFrameList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = StackFrameList.h; path = include/lldb/Target/StackFrameList.h; sourceTree = "<group>"; };
8CF46A6120522A9000423DDF /* StackFrameRecognizer.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = StackFrameRecognizer.cpp; path = source/Target/StackFrameRecognizer.cpp; sourceTree = "<group>"; };
8CFDB67920467B390052B399 /* StackFrameRecognizer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = StackFrameRecognizer.h; path = include/lldb/Target/StackFrameRecognizer.h; sourceTree = "<group>"; };
26BC7F3A10F1B90C00F91463 /* StackID.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = StackID.cpp; path = source/Target/StackID.cpp; sourceTree = "<group>"; };
26BC7DF710F1B81A00F91463 /* StackID.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = StackID.h; path = include/lldb/Target/StackID.h; sourceTree = "<group>"; };
26BC7E9010F1B85900F91463 /* State.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = State.cpp; path = source/Utility/State.cpp; sourceTree = "<group>"; };
@ -5687,6 +5690,8 @@
26BC7F3810F1B90C00F91463 /* StackFrame.cpp */,
26BC7DF610F1B81A00F91463 /* StackFrameList.h */,
26BC7F3910F1B90C00F91463 /* StackFrameList.cpp */,
8CFDB67920467B390052B399 /* StackFrameRecognizer.h */,
8CF46A6120522A9000423DDF /* StackFrameRecognizer.cpp */,
26BC7DF710F1B81A00F91463 /* StackID.h */,
26BC7F3A10F1B90C00F91463 /* StackID.cpp */,
2615DB841208A9C90021781D /* StopInfo.h */,
@ -8241,6 +8246,7 @@
268900F213353E6F00698AC0 /* SectionLoadList.cpp in Sources */,
268900F313353E6F00698AC0 /* StackFrame.cpp in Sources */,
268900F413353E6F00698AC0 /* StackFrameList.cpp in Sources */,
8CF46A6220522A9800423DDF /* StackFrameRecognizer.cpp in Sources */,
268900F513353E6F00698AC0 /* StackID.cpp in Sources */,
228B1B672113340200E61C70 /* ClangHighlighter.cpp in Sources */,
268900F613353E6F00698AC0 /* StopInfo.cpp in Sources */,

View File

@ -0,0 +1,10 @@
LEVEL = ../../make
OBJC_SOURCES := main.m
CFLAGS_EXTRAS += -g0 # No debug info.
MAKE_DSYM := NO
include $(LEVEL)/Makefile.rules
LDFLAGS += -framework Foundation

View File

@ -0,0 +1,102 @@
# encoding: utf-8
"""
Test lldb's frame recognizers.
"""
import lldb
from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import *
from lldbsuite.test import lldbutil
import recognizer
class FrameRecognizerTestCase(TestBase):
mydir = TestBase.compute_mydir(__file__)
NO_DEBUG_INFO_TESTCASE = True
@skipUnlessDarwin
def test_frame_recognizer_1(self):
self.build()
target = self.dbg.CreateTarget(self.getBuildArtifact("a.out"))
self.assertTrue(target, VALID_TARGET)
self.runCmd("command script import " + os.path.join(self.getSourceDir(), "recognizer.py"))
self.expect("frame recognizer list",
substrs=['no matching results found.'])
self.runCmd("frame recognizer add -l recognizer.MyFrameRecognizer -s a.out -n foo")
self.expect("frame recognizer list",
substrs=['0: recognizer.MyFrameRecognizer, module a.out, function foo'])
self.runCmd("frame recognizer add -l recognizer.MyOtherFrameRecognizer -s a.out -n bar -x")
self.expect("frame recognizer list",
substrs=['0: recognizer.MyFrameRecognizer, module a.out, function foo',
'1: recognizer.MyOtherFrameRecognizer, module a.out, function bar (regexp)'
])
self.runCmd("frame recognizer delete 0")
self.expect("frame recognizer list",
substrs=['1: recognizer.MyOtherFrameRecognizer, module a.out, function bar (regexp)'])
self.runCmd("frame recognizer clear")
self.expect("frame recognizer list",
substrs=['no matching results found.'])
self.runCmd("frame recognizer add -l recognizer.MyFrameRecognizer -s a.out -n foo")
lldbutil.run_break_set_by_symbol(self, "foo")
self.runCmd("r")
self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT,
substrs=['stopped', 'stop reason = breakpoint'])
process = target.GetProcess()
thread = process.GetSelectedThread()
frame = thread.GetSelectedFrame()
self.assertEqual(frame.GetSymbol().GetName(), "foo")
self.assertFalse(frame.GetLineEntry().IsValid())
self.expect("frame variable",
substrs=['(int) a = 42', '(int) b = 56'])
opts = lldb.SBVariablesOptions();
opts.SetIncludeRecognizedArguments(True);
variables = frame.GetVariables(opts);
self.assertEqual(variables.GetSize(), 2)
self.assertEqual(variables.GetValueAtIndex(0).name, "a")
self.assertEqual(variables.GetValueAtIndex(0).signed, 42)
self.assertEqual(variables.GetValueAtIndex(1).name, "b")
self.assertEqual(variables.GetValueAtIndex(1).signed, 56)
self.expect("frame recognizer info 0",
substrs=['frame 0 is recognized by recognizer.MyFrameRecognizer'])
self.expect("frame recognizer info 999", error=True,
substrs=['no frame with index 999'])
self.expect("frame recognizer info 1",
substrs=['frame 1 not recognized by any recognizer'])
# FIXME: The following doesn't work yet, but should be fixed.
"""
lldbutil.run_break_set_by_symbol(self, "bar")
self.runCmd("c")
self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT,
substrs=['stopped', 'stop reason = breakpoint'])
self.expect("frame variable -t",
substrs=['(int *) a = '])
self.expect("frame variable -t *a",
substrs=['*a = 78'])
"""

View File

@ -0,0 +1,28 @@
//===-- main.m ------------------------------------------------*- ObjC -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#import <Foundation/Foundation.h>
void foo(int a, int b)
{
printf("%d %d\n", a, b);
}
void bar(int *ptr)
{
printf("%d\n", *ptr);
}
int main (int argc, const char * argv[])
{
foo(42, 56);
int i = 78;
bar(&i);
return 0;
}

View File

@ -0,0 +1,21 @@
# encoding: utf-8
import lldb
class MyFrameRecognizer(object):
def get_recognized_arguments(self, frame):
if frame.name == "foo":
arg1 = frame.EvaluateExpression("$arg1").signed
arg2 = frame.EvaluateExpression("$arg2").signed
val1 = lldb.target.CreateValueFromExpression("a", "%d" % arg1)
val2 = lldb.target.CreateValueFromExpression("b", "%d" % arg2)
return [val1, val2]
elif frame.name == "bar":
arg1 = frame.EvaluateExpression("$arg1").signed
val1 = lldb.target.CreateValueFromExpression("a", "(int *)%d" % arg1)
return [val1]
return []
class MyOtherFrameRecognizer(object):
def get_recognized_arguments(self, frame):
return []

View File

@ -784,6 +784,52 @@ LLDBSWIGPythonCreateOSPlugin
Py_RETURN_NONE;
}
SWIGEXPORT void*
LLDBSWIGPython_CreateFrameRecognizer
(
const char *python_class_name,
const char *session_dictionary_name
)
{
using namespace lldb_private;
if (python_class_name == NULL || python_class_name[0] == '\0' || !session_dictionary_name)
Py_RETURN_NONE;
PyErr_Cleaner py_err_cleaner(true);
auto dict = PythonModule::MainModule().ResolveName<PythonDictionary>(session_dictionary_name);
auto pfunc = PythonObject::ResolveNameWithDictionary<PythonCallable>(python_class_name, dict);
if (!pfunc.IsAllocated())
Py_RETURN_NONE;
auto result = pfunc();
if (result.IsAllocated())
return result.release();
Py_RETURN_NONE;
}
SWIGEXPORT PyObject*
LLDBSwigPython_GetRecognizedArguments
(
PyObject *implementor,
const lldb::StackFrameSP& frame_sp
)
{
using namespace lldb_private;
static char callee_name[] = "get_recognized_arguments";
lldb::SBFrame frame_sb(frame_sp);
PyObject *arg = SBTypeToSWIGWrapper(frame_sb);
PyObject* result = PyObject_CallMethodObjArgs(implementor, PyString_FromString(callee_name), arg, NULL);
return result;
}
SWIGEXPORT void*
LLDBSWIGPython_GetDynamicSetting (void* module, const char* setting, const lldb::TargetSP& target_sp)
{

View File

@ -27,6 +27,12 @@ public:
void
SetIncludeArguments (bool);
bool
GetIncludeRecognizedArguments () const;
void
SetIncludeRecognizedArguments (bool);
bool
GetIncludeLocals () const;

View File

@ -36,6 +36,7 @@
#include "lldb/Target/Process.h"
#include "lldb/Target/RegisterContext.h"
#include "lldb/Target/StackFrame.h"
#include "lldb/Target/StackFrameRecognizer.h"
#include "lldb/Target/StackID.h"
#include "lldb/Target/Target.h"
#include "lldb/Target/Thread.h"
@ -960,6 +961,7 @@ SBValueList SBFrame::GetVariables(const lldb::SBVariablesOptions &options) {
const bool statics = options.GetIncludeStatics();
const bool arguments = options.GetIncludeArguments();
const bool recognized_arguments = options.GetIncludeRecognizedArguments();
const bool locals = options.GetIncludeLocals();
const bool in_scope_only = options.GetInScopeOnly();
const bool include_runtime_support_values =
@ -967,9 +969,10 @@ SBValueList SBFrame::GetVariables(const lldb::SBVariablesOptions &options) {
const lldb::DynamicValueType use_dynamic = options.GetUseDynamic();
if (log)
log->Printf("SBFrame::GetVariables (arguments=%i, locals=%i, statics=%i, "
"in_scope_only=%i runtime=%i dynamic=%i)",
arguments, locals, statics, in_scope_only,
log->Printf(
"SBFrame::GetVariables (arguments=%i, recognized_arguments=%i, "
"locals=%i, statics=%i, in_scope_only=%i runtime=%i dynamic=%i)",
arguments, recognized_arguments, locals, statics, in_scope_only,
include_runtime_support_values, use_dynamic);
std::set<VariableSP> variable_set;
@ -1032,6 +1035,20 @@ SBValueList SBFrame::GetVariables(const lldb::SBVariablesOptions &options) {
}
}
}
if (recognized_arguments) {
auto recognized_frame = frame->GetRecognizedFrame();
if (recognized_frame) {
ValueObjectListSP recognized_arg_list =
recognized_frame->GetRecognizedArguments();
if (recognized_arg_list) {
for (auto &rec_value_sp : recognized_arg_list->GetObjects()) {
SBValue value_sb;
value_sb.SetSP(rec_value_sp, use_dynamic);
value_list.Append(value_sb);
}
}
}
}
} else {
if (log)
log->Printf("SBFrame::GetVariables () => error: could not "

View File

@ -16,9 +16,9 @@ using namespace lldb_private;
class VariablesOptionsImpl {
public:
VariablesOptionsImpl()
: m_include_arguments(false), m_include_locals(false),
m_include_statics(false), m_in_scope_only(false),
m_include_runtime_support_values(false),
: m_include_arguments(false), m_include_recognized_arguments(false),
m_include_locals(false), m_include_statics(false),
m_in_scope_only(false), m_include_runtime_support_values(false),
m_use_dynamic(lldb::eNoDynamicValues) {}
VariablesOptionsImpl(const VariablesOptionsImpl &) = default;
@ -31,6 +31,14 @@ public:
void SetIncludeArguments(bool b) { m_include_arguments = b; }
bool GetIncludeRecognizedArguments() const {
return m_include_recognized_arguments;
}
void SetIncludeRecognizedArguments(bool b) {
m_include_recognized_arguments = b;
}
bool GetIncludeLocals() const { return m_include_locals; }
void SetIncludeLocals(bool b) { m_include_locals = b; }
@ -57,6 +65,7 @@ public:
private:
bool m_include_arguments : 1;
bool m_include_recognized_arguments : 1;
bool m_include_locals : 1;
bool m_include_statics : 1;
bool m_in_scope_only : 1;
@ -90,6 +99,14 @@ void SBVariablesOptions::SetIncludeArguments(bool arguments) {
m_opaque_ap->SetIncludeArguments(arguments);
}
bool SBVariablesOptions::GetIncludeRecognizedArguments() const {
return m_opaque_ap->GetIncludeRecognizedArguments();
}
void SBVariablesOptions::SetIncludeRecognizedArguments(bool arguments) {
m_opaque_ap->SetIncludeRecognizedArguments(arguments);
}
bool SBVariablesOptions::GetIncludeLocals() const {
return m_opaque_ap->GetIncludeLocals();
}

View File

@ -235,6 +235,13 @@ LLDBSWIGPythonCreateOSPlugin(const char *python_class_name,
const char *session_dictionary_name,
const lldb::ProcessSP &process_sp);
extern "C" void *LLDBSWIGPython_CreateFrameRecognizer(
const char *python_class_name,
const char *session_dictionary_name);
extern "C" void *LLDBSwigPython_GetRecognizedArguments(void *implementor,
const lldb::StackFrameSP& frame_sp);
extern "C" bool LLDBSWIGPythonRunScriptKeywordProcess(
const char *python_function_name, const char *session_dictionary_name,
lldb::ProcessSP &process, std::string &output);
@ -423,7 +430,9 @@ void SystemInitializerFull::InitializeSWIG() {
LLDBSwigPython_MightHaveChildrenSynthProviderInstance,
LLDBSwigPython_GetValueSynthProviderInstance, LLDBSwigPythonCallCommand,
LLDBSwigPythonCallCommandObject, LLDBSwigPythonCallModuleInit,
LLDBSWIGPythonCreateOSPlugin, LLDBSWIGPythonRunScriptKeywordProcess,
LLDBSWIGPythonCreateOSPlugin, LLDBSWIGPython_CreateFrameRecognizer,
LLDBSwigPython_GetRecognizedArguments,
LLDBSWIGPythonRunScriptKeywordProcess,
LLDBSWIGPythonRunScriptKeywordThread,
LLDBSWIGPythonRunScriptKeywordTarget, LLDBSWIGPythonRunScriptKeywordFrame,
LLDBSWIGPythonRunScriptKeywordValue, LLDBSWIGPython_GetDynamicSetting,

View File

@ -24,6 +24,7 @@
#include "lldb/DataFormatters/ValueObjectPrinter.h"
#include "lldb/Host/Host.h"
#include "lldb/Host/OptionParser.h"
#include "lldb/Host/StringConvert.h"
#include "lldb/Interpreter/CommandInterpreter.h"
#include "lldb/Interpreter/CommandReturnObject.h"
#include "lldb/Interpreter/OptionGroupFormat.h"
@ -40,6 +41,7 @@
#include "lldb/Symbol/VariableList.h"
#include "lldb/Target/Process.h"
#include "lldb/Target/StackFrame.h"
#include "lldb/Target/StackFrameRecognizer.h"
#include "lldb/Target/StopInfo.h"
#include "lldb/Target/Target.h"
#include "lldb/Target/Thread.h"
@ -715,6 +717,23 @@ protected:
result.SetStatus(eReturnStatusSuccessFinishResult);
}
if (m_option_variable.show_recognized_args) {
auto recognized_frame = frame->GetRecognizedFrame();
if (recognized_frame) {
ValueObjectListSP recognized_arg_list =
recognized_frame->GetRecognizedArguments();
if (recognized_arg_list) {
for (auto &rec_value_sp : recognized_arg_list->GetObjects()) {
options.SetFormat(m_option_format.GetFormat());
options.SetVariableFormatDisplayLanguage(
rec_value_sp->GetPreferredDisplayLanguage());
options.SetRootValueObjectName(rec_value_sp->GetName().AsCString());
rec_value_sp->Dump(result.GetOutputStream(), options);
}
}
}
}
if (m_interpreter.TruncationWarningNecessary()) {
result.GetOutputStream().Printf(m_interpreter.TruncationWarningText(),
m_cmd_name.c_str());
@ -738,6 +757,368 @@ protected:
OptionGroupValueObjectDisplay m_varobj_options;
};
#pragma mark CommandObjectFrameRecognizer
static OptionDefinition g_frame_recognizer_add_options[] = {
// clang-format off
{ LLDB_OPT_SET_ALL, false, "shlib", 's', OptionParser::eRequiredArgument, nullptr, {}, CommandCompletions::eModuleCompletion, eArgTypeShlibName, "Name of the module or shared library that this recognizer applies to." },
{ LLDB_OPT_SET_ALL, false, "function", 'n', OptionParser::eRequiredArgument, nullptr, {}, CommandCompletions::eSymbolCompletion, eArgTypeName, "Name of the function that this recognizer applies to." },
{ LLDB_OPT_SET_2, false, "python-class", 'l', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypePythonClass, "Give the name of a Python class to use for this frame recognizer." },
{ LLDB_OPT_SET_ALL, false, "regex", 'x', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "Function name and module name are actually regular expressions." }
// clang-format on
};
class CommandObjectFrameRecognizerAdd : public CommandObjectParsed {
private:
class CommandOptions : public Options {
public:
CommandOptions() : Options() {}
~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 'l':
m_class_name = std::string(option_arg);
break;
case 's':
m_module = std::string(option_arg);
break;
case 'n':
m_function = std::string(option_arg);
break;
case 'x':
m_regex = true;
break;
default:
error.SetErrorStringWithFormat("unrecognized option '%c'",
short_option);
break;
}
return error;
}
void OptionParsingStarting(ExecutionContext *execution_context) override {
m_module = "";
m_function = "";
m_class_name = "";
m_regex = false;
}
llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
return llvm::makeArrayRef(g_frame_recognizer_add_options);
}
// Instance variables to hold the values for command options.
std::string m_class_name;
std::string m_module;
std::string m_function;
bool m_regex;
};
CommandOptions m_options;
Options *GetOptions() override { return &m_options; }
protected:
bool DoExecute(Args &command, CommandReturnObject &result) override;
public:
CommandObjectFrameRecognizerAdd(CommandInterpreter &interpreter)
: CommandObjectParsed(interpreter, "frame recognizer add",
"Add a new frame recognizer.", nullptr),
m_options() {
SetHelpLong(R"(
Frame recognizers allow for retrieving information about special frames based on
ABI, arguments or other special properties of that frame, even without source
code or debug info. Currently, one use case is to extract function arguments
that would otherwise be unaccesible, or augment existing arguments.
Adding a custom frame recognizer is possible by implementing a Python class
and using the 'frame recognizer add' command. The Python class should have a
'get_recognized_arguments' method and it will receive an argument of type
lldb.SBFrame representing the current frame that we are trying to recognize.
The method should return a (possibly empty) list of lldb.SBValue objects that
represent the recognized arguments.
An example of a recognizer that retrieves the file descriptor values from libc
functions 'read', 'write' and 'close' follows:
class LibcFdRecognizer(object):
def get_recognized_arguments(self, frame):
if frame.name in ["read", "write", "close"]:
fd = frame.EvaluateExpression("$arg1").unsigned
value = lldb.target.CreateValueFromExpression("fd", "(int)%d" % fd)
return [value]
return []
The file containing this implementation can be imported via 'command script
import' and then we can register this recognizer with 'frame recognizer add'.
It's important to restrict the recognizer to the libc library (which is
libsystem_kernel.dylib on macOS) to avoid matching functions with the same name
in other modules:
(lldb) command script import .../fd_recognizer.py
(lldb) frame recognizer add -l fd_recognizer.LibcFdRecognizer -n read -s libsystem_kernel.dylib
When the program is stopped at the beginning of the 'read' function in libc, we
can view the recognizer arguments in 'frame variable':
(lldb) b read
(lldb) r
Process 1234 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.3
frame #0: 0x00007fff06013ca0 libsystem_kernel.dylib`read
(lldb) frame variable
(int) fd = 3
)");
}
~CommandObjectFrameRecognizerAdd() override = default;
};
bool CommandObjectFrameRecognizerAdd::DoExecute(Args &command,
CommandReturnObject &result) {
if (m_options.m_class_name.empty()) {
result.AppendErrorWithFormat(
"%s needs a Python class name (-l argument).\n", m_cmd_name.c_str());
result.SetStatus(eReturnStatusFailed);
return false;
}
if (m_options.m_module.empty()) {
result.AppendErrorWithFormat("%s needs a module name (-s argument).\n",
m_cmd_name.c_str());
result.SetStatus(eReturnStatusFailed);
return false;
}
if (m_options.m_function.empty()) {
result.AppendErrorWithFormat("%s needs a function name (-n argument).\n",
m_cmd_name.c_str());
result.SetStatus(eReturnStatusFailed);
return false;
}
ScriptInterpreter *interpreter = m_interpreter.GetScriptInterpreter();
if (interpreter &&
!interpreter->CheckObjectExists(m_options.m_class_name.c_str())) {
result.AppendWarning(
"The provided class does not exist - please define it "
"before attempting to use this frame recognizer");
}
StackFrameRecognizerSP recognizer_sp =
StackFrameRecognizerSP(new ScriptedStackFrameRecognizer(
interpreter, m_options.m_class_name.c_str()));
if (m_options.m_regex) {
auto module =
RegularExpressionSP(new RegularExpression(m_options.m_module));
auto func =
RegularExpressionSP(new RegularExpression(m_options.m_function));
StackFrameRecognizerManager::AddRecognizer(recognizer_sp, module, func);
} else {
auto module = ConstString(m_options.m_module);
auto func = ConstString(m_options.m_function);
StackFrameRecognizerManager::AddRecognizer(recognizer_sp, module, func);
}
result.SetStatus(eReturnStatusSuccessFinishNoResult);
return result.Succeeded();
}
class CommandObjectFrameRecognizerClear : public CommandObjectParsed {
public:
CommandObjectFrameRecognizerClear(CommandInterpreter &interpreter)
: CommandObjectParsed(interpreter, "frame recognizer clear",
"Delete all frame recognizers.", nullptr) {}
~CommandObjectFrameRecognizerClear() override = default;
protected:
bool DoExecute(Args &command, CommandReturnObject &result) override {
StackFrameRecognizerManager::RemoveAllRecognizers();
result.SetStatus(eReturnStatusSuccessFinishResult);
return result.Succeeded();
}
};
class CommandObjectFrameRecognizerDelete : public CommandObjectParsed {
public:
CommandObjectFrameRecognizerDelete(CommandInterpreter &interpreter)
: CommandObjectParsed(interpreter, "frame recognizer delete",
"Delete an existing frame recognizer.", nullptr) {}
~CommandObjectFrameRecognizerDelete() override = default;
protected:
bool DoExecute(Args &command, CommandReturnObject &result) override {
if (command.GetArgumentCount() == 0) {
if (!m_interpreter.Confirm(
"About to delete all frame recognizers, do you want to do that?",
true)) {
result.AppendMessage("Operation cancelled...");
result.SetStatus(eReturnStatusFailed);
return false;
}
StackFrameRecognizerManager::RemoveAllRecognizers();
result.SetStatus(eReturnStatusSuccessFinishResult);
return result.Succeeded();
}
if (command.GetArgumentCount() != 1) {
result.AppendErrorWithFormat("'%s' takes zero or one arguments.\n",
m_cmd_name.c_str());
result.SetStatus(eReturnStatusFailed);
return false;
}
uint32_t recognizer_id =
StringConvert::ToUInt32(command.GetArgumentAtIndex(0), 0, 0);
StackFrameRecognizerManager::RemoveRecognizerWithID(recognizer_id);
result.SetStatus(eReturnStatusSuccessFinishResult);
return result.Succeeded();
}
};
class CommandObjectFrameRecognizerList : public CommandObjectParsed {
public:
CommandObjectFrameRecognizerList(CommandInterpreter &interpreter)
: CommandObjectParsed(interpreter, "frame recognizer list",
"Show a list of active frame recognizers.",
nullptr) {}
~CommandObjectFrameRecognizerList() override = default;
protected:
bool DoExecute(Args &command, CommandReturnObject &result) override {
bool any_printed = false;
StackFrameRecognizerManager::ForEach(
[&result, &any_printed](uint32_t recognizer_id, std::string name,
std::string function, std::string symbol,
bool regexp) {
if (name == "") name = "(internal)";
result.GetOutputStream().Printf(
"%d: %s, module %s, function %s%s\n", recognizer_id, name.c_str(),
function.c_str(), symbol.c_str(), regexp ? " (regexp)" : "");
any_printed = true;
});
if (any_printed)
result.SetStatus(eReturnStatusSuccessFinishResult);
else {
result.GetOutputStream().PutCString("no matching results found.\n");
result.SetStatus(eReturnStatusSuccessFinishNoResult);
}
return result.Succeeded();
}
};
class CommandObjectFrameRecognizerInfo : public CommandObjectParsed {
public:
CommandObjectFrameRecognizerInfo(CommandInterpreter &interpreter)
: CommandObjectParsed(
interpreter, "frame recognizer info",
"Show which frame recognizer is applied a stack frame (if any).",
nullptr) {
CommandArgumentEntry arg;
CommandArgumentData index_arg;
// Define the first (and only) variant of this arg.
index_arg.arg_type = eArgTypeFrameIndex;
index_arg.arg_repetition = eArgRepeatPlain;
// There is only one variant this argument could be; put it into the
// argument entry.
arg.push_back(index_arg);
// Push the data for the first argument into the m_arguments vector.
m_arguments.push_back(arg);
}
~CommandObjectFrameRecognizerInfo() override = default;
protected:
bool DoExecute(Args &command, CommandReturnObject &result) override {
Process *process = m_exe_ctx.GetProcessPtr();
if (process == nullptr) {
result.AppendError("no process");
result.SetStatus(eReturnStatusFailed);
return false;
}
Thread *thread = m_exe_ctx.GetThreadPtr();
if (thread == nullptr) {
result.AppendError("no thread");
result.SetStatus(eReturnStatusFailed);
return false;
}
if (command.GetArgumentCount() != 1) {
result.AppendErrorWithFormat(
"'%s' takes exactly one frame index argument.\n", m_cmd_name.c_str());
result.SetStatus(eReturnStatusFailed);
return false;
}
uint32_t frame_index =
StringConvert::ToUInt32(command.GetArgumentAtIndex(0), 0, 0);
StackFrameSP frame_sp = thread->GetStackFrameAtIndex(frame_index);
if (!frame_sp) {
result.AppendErrorWithFormat("no frame with index %u", frame_index);
result.SetStatus(eReturnStatusFailed);
return false;
}
auto recognizer =
StackFrameRecognizerManager::GetRecognizerForFrame(frame_sp);
Stream &output_stream = result.GetOutputStream();
output_stream.Printf("frame %d ", frame_index);
if (recognizer) {
output_stream << "is recognized by ";
output_stream << recognizer->GetName();
} else {
output_stream << "not recognized by any recognizer";
}
output_stream.EOL();
result.SetStatus(eReturnStatusSuccessFinishResult);
return result.Succeeded();
}
};
class CommandObjectFrameRecognizer : public CommandObjectMultiword {
public:
CommandObjectFrameRecognizer(CommandInterpreter &interpreter)
: CommandObjectMultiword(
interpreter, "frame recognizer",
"Commands for editing and viewing frame recognizers.",
"frame recognizer [<sub-command-options>] ") {
LoadSubCommand(
"add",
CommandObjectSP(new CommandObjectFrameRecognizerAdd(interpreter)));
LoadSubCommand(
"clear",
CommandObjectSP(new CommandObjectFrameRecognizerClear(interpreter)));
LoadSubCommand(
"delete",
CommandObjectSP(new CommandObjectFrameRecognizerDelete(interpreter)));
LoadSubCommand(
"list",
CommandObjectSP(new CommandObjectFrameRecognizerList(interpreter)));
LoadSubCommand(
"info",
CommandObjectSP(new CommandObjectFrameRecognizerInfo(interpreter)));
}
~CommandObjectFrameRecognizer() override = default;
};
#pragma mark CommandObjectMultiwordFrame
//-------------------------------------------------------------------------
@ -758,6 +1139,11 @@ CommandObjectMultiwordFrame::CommandObjectMultiwordFrame(
CommandObjectSP(new CommandObjectFrameSelect(interpreter)));
LoadSubCommand("variable",
CommandObjectSP(new CommandObjectFrameVariable(interpreter)));
#ifndef LLDB_DISABLE_PYTHON
LoadSubCommand(
"recognizer",
CommandObjectSP(new CommandObjectFrameRecognizer(interpreter)));
#endif
}
CommandObjectMultiwordFrame::~CommandObjectMultiwordFrame() = default;

View File

@ -28,6 +28,9 @@ static constexpr OptionDefinition g_variable_options[] = {
{LLDB_OPT_SET_1 | LLDB_OPT_SET_2, false, "no-args", 'a',
OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone,
"Omit function arguments."},
{LLDB_OPT_SET_1 | LLDB_OPT_SET_2, false, "no-recognized-args", 't',
OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone,
"Omit recognized function arguments."},
{LLDB_OPT_SET_1 | LLDB_OPT_SET_2, false, "no-locals", 'l',
OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone,
"Omit local variables."},
@ -101,6 +104,9 @@ OptionGroupVariable::SetOptionValue(uint32_t option_idx,
case 's':
show_scope = true;
break;
case 't':
show_recognized_args = false;
break;
case 'y':
error = summary.SetCurrentValue(option_arg);
break;
@ -119,6 +125,7 @@ OptionGroupVariable::SetOptionValue(uint32_t option_idx,
void OptionGroupVariable::OptionParsingStarting(
ExecutionContext *execution_context) {
show_args = true; // Frame option only
show_recognized_args = true; // Frame option only
show_locals = true; // Frame option only
show_globals = false; // Frame option only
show_decl = false;

View File

@ -27,6 +27,7 @@
#include <string>
#include "lldb/API/SBValue.h"
#include "lldb/API/SBFrame.h"
#include "lldb/Breakpoint/BreakpointLocation.h"
#include "lldb/Breakpoint/StoppointCallbackContext.h"
#include "lldb/Breakpoint/WatchpointOptions.h"
@ -91,6 +92,10 @@ static ScriptInterpreterPython::SWIGPythonCallModuleInit
g_swig_call_module_init = nullptr;
static ScriptInterpreterPython::SWIGPythonCreateOSPlugin
g_swig_create_os_plugin = nullptr;
static ScriptInterpreterPython::SWIGPythonCreateFrameRecognizer
g_swig_create_frame_recognizer = nullptr;
static ScriptInterpreterPython::SWIGPythonGetRecognizedArguments
g_swig_get_recognized_arguments = nullptr;
static ScriptInterpreterPython::SWIGPythonScriptKeyword_Process
g_swig_run_script_keyword_process = nullptr;
static ScriptInterpreterPython::SWIGPythonScriptKeyword_Thread
@ -1498,6 +1503,61 @@ bool ScriptInterpreterPython::GenerateTypeSynthClass(StringList &user_input,
return true;
}
StructuredData::GenericSP ScriptInterpreterPython::CreateFrameRecognizer(
const char *class_name) {
if (class_name == nullptr || class_name[0] == '\0')
return StructuredData::GenericSP();
void *ret_val;
{
Locker py_lock(this, Locker::AcquireLock | Locker::NoSTDIN,
Locker::FreeLock);
ret_val =
g_swig_create_frame_recognizer(class_name, m_dictionary_name.c_str());
}
return StructuredData::GenericSP(new StructuredPythonObject(ret_val));
}
lldb::ValueObjectListSP ScriptInterpreterPython::GetRecognizedArguments(
const StructuredData::ObjectSP &os_plugin_object_sp,
lldb::StackFrameSP frame_sp) {
Locker py_lock(this, Locker::AcquireLock | Locker::NoSTDIN, Locker::FreeLock);
if (!os_plugin_object_sp) return ValueObjectListSP();
StructuredData::Generic *generic = os_plugin_object_sp->GetAsGeneric();
if (!generic) return nullptr;
PythonObject implementor(PyRefType::Borrowed,
(PyObject *)generic->GetValue());
if (!implementor.IsAllocated()) return ValueObjectListSP();
PythonObject py_return(
PyRefType::Owned,
(PyObject *)g_swig_get_recognized_arguments(implementor.get(), frame_sp));
// if it fails, print the error but otherwise go on
if (PyErr_Occurred()) {
PyErr_Print();
PyErr_Clear();
}
if (py_return.get()) {
PythonList result_list(PyRefType::Borrowed, py_return.get());
ValueObjectListSP result = ValueObjectListSP(new ValueObjectList());
for (int i = 0; i < result_list.GetSize(); i++) {
PyObject *item = result_list.GetItemAtIndex(i).get();
lldb::SBValue *sb_value_ptr =
(lldb::SBValue *)g_swig_cast_to_sbvalue(item);
if (sb_value_ptr->IsValid()) result->Append(sb_value_ptr->GetSP());
}
return result;
}
return ValueObjectListSP();
}
StructuredData::GenericSP ScriptInterpreterPython::OSPlugin_CreatePluginObject(
const char *class_name, lldb::ProcessSP process_sp) {
if (class_name == nullptr || class_name[0] == '\0')
@ -3185,6 +3245,8 @@ void ScriptInterpreterPython::InitializeInterpreter(
SWIGPythonCallCommandObject swig_call_command_object,
SWIGPythonCallModuleInit swig_call_module_init,
SWIGPythonCreateOSPlugin swig_create_os_plugin,
SWIGPythonCreateFrameRecognizer swig_create_frame_recognizer,
SWIGPythonGetRecognizedArguments swig_get_recognized_arguments,
SWIGPythonScriptKeyword_Process swig_run_script_keyword_process,
SWIGPythonScriptKeyword_Thread swig_run_script_keyword_thread,
SWIGPythonScriptKeyword_Target swig_run_script_keyword_target,
@ -3213,6 +3275,8 @@ void ScriptInterpreterPython::InitializeInterpreter(
g_swig_call_command_object = swig_call_command_object;
g_swig_call_module_init = swig_call_module_init;
g_swig_create_os_plugin = swig_create_os_plugin;
g_swig_create_frame_recognizer = swig_create_frame_recognizer;
g_swig_get_recognized_arguments = swig_get_recognized_arguments;
g_swig_run_script_keyword_process = swig_run_script_keyword_process;
g_swig_run_script_keyword_thread = swig_run_script_keyword_thread;
g_swig_run_script_keyword_target = swig_run_script_keyword_target;

View File

@ -94,6 +94,12 @@ public:
const char *session_dictionary_name,
const lldb::ProcessSP &process_sp);
typedef void *(*SWIGPythonCreateFrameRecognizer)(
const char *python_class_name, const char *session_dictionary_name);
typedef void *(*SWIGPythonGetRecognizedArguments)(
void *implementor, const lldb::StackFrameSP &frame_sp);
typedef size_t (*SWIGPythonCalculateNumChildren)(void *implementor,
uint32_t max);
@ -231,6 +237,13 @@ public:
ScriptedBreakpointResolverSearchDepth(StructuredData::GenericSP
implementor_sp) override;
StructuredData::GenericSP
CreateFrameRecognizer(const char *class_name) override;
lldb::ValueObjectListSP
GetRecognizedArguments(const StructuredData::ObjectSP &implementor,
lldb::StackFrameSP frame_sp) override;
StructuredData::GenericSP
OSPlugin_CreatePluginObject(const char *class_name,
lldb::ProcessSP process_sp) override;
@ -426,6 +439,8 @@ public:
SWIGPythonCallCommandObject swig_call_command_object,
SWIGPythonCallModuleInit swig_call_module_init,
SWIGPythonCreateOSPlugin swig_create_os_plugin,
SWIGPythonCreateFrameRecognizer swig_create_frame_recognizer,
SWIGPythonGetRecognizedArguments swig_get_recognized_arguments,
SWIGPythonScriptKeyword_Process swig_run_script_keyword_process,
SWIGPythonScriptKeyword_Thread swig_run_script_keyword_thread,
SWIGPythonScriptKeyword_Target swig_run_script_keyword_target,

View File

@ -28,6 +28,7 @@ add_lldb_library(lldbTarget
SectionLoadList.cpp
StackFrame.cpp
StackFrameList.cpp
StackFrameRecognizer.cpp
StackID.cpp
StopInfo.cpp
StructuredDataPlugin.cpp

View File

@ -31,6 +31,7 @@
#include "lldb/Target/ExecutionContext.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/RegisterValue.h"
@ -58,7 +59,8 @@ StackFrame::StackFrame(const ThreadSP &thread_sp, user_id_t frame_idx,
m_id(pc, cfa, nullptr), m_frame_code_addr(pc), m_sc(), m_flags(),
m_frame_base(), m_frame_base_error(), m_cfa_is_valid(cfa_is_valid),
m_stack_frame_kind(kind), m_variable_list_sp(),
m_variable_list_value_objects(), m_disassembly(), m_mutex() {
m_variable_list_value_objects(), m_recognized_frame_sp(), m_disassembly(),
m_mutex() {
// If we don't have a CFA value, use the frame index for our StackID so that
// recursive functions properly aren't confused with one another on a history
// stack.
@ -82,7 +84,8 @@ StackFrame::StackFrame(const ThreadSP &thread_sp, user_id_t frame_idx,
m_frame_code_addr(pc), m_sc(), m_flags(), m_frame_base(),
m_frame_base_error(), m_cfa_is_valid(true),
m_stack_frame_kind(StackFrame::Kind::Regular), m_variable_list_sp(),
m_variable_list_value_objects(), m_disassembly(), m_mutex() {
m_variable_list_value_objects(), m_recognized_frame_sp(), m_disassembly(),
m_mutex() {
if (sc_ptr != nullptr) {
m_sc = *sc_ptr;
m_flags.Set(m_sc.GetResolvedMask());
@ -107,7 +110,8 @@ StackFrame::StackFrame(const ThreadSP &thread_sp, user_id_t frame_idx,
m_frame_code_addr(pc_addr), m_sc(), m_flags(), m_frame_base(),
m_frame_base_error(), m_cfa_is_valid(true),
m_stack_frame_kind(StackFrame::Kind::Regular), m_variable_list_sp(),
m_variable_list_value_objects(), m_disassembly(), m_mutex() {
m_variable_list_value_objects(), m_recognized_frame_sp(), m_disassembly(),
m_mutex() {
if (sc_ptr != nullptr) {
m_sc = *sc_ptr;
m_flags.Set(m_sc.GetResolvedMask());
@ -1952,3 +1956,11 @@ bool StackFrame::GetStatus(Stream &strm, bool show_frame_info, bool show_source,
}
return true;
}
RecognizedStackFrameSP StackFrame::GetRecognizedFrame() {
if (!m_recognized_frame_sp) {
m_recognized_frame_sp =
StackFrameRecognizerManager::RecognizeFrame(CalculateStackFrame());
}
return m_recognized_frame_sp;
}

View File

@ -0,0 +1,190 @@
//===-- StackFrameRecognizer.cpp --------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
// C Includes
// C++ Includes
#include <vector>
// Other libraries and framework includes
// Project includes
#include "lldb/Core/Module.h"
#include "lldb/Interpreter/ScriptInterpreter.h"
#include "lldb/Symbol/Symbol.h"
#include "lldb/Target/StackFrame.h"
#include "lldb/Target/StackFrameRecognizer.h"
#include "lldb/Utility/RegularExpression.h"
using namespace lldb;
using namespace lldb_private;
class ScriptedRecognizedStackFrame : public RecognizedStackFrame {
public:
ScriptedRecognizedStackFrame(ValueObjectListSP args) {
m_arguments = args;
}
};
ScriptedStackFrameRecognizer::ScriptedStackFrameRecognizer(
ScriptInterpreter *interpreter, const char *pclass)
: m_interpreter(interpreter), m_python_class(pclass) {
m_python_object_sp =
m_interpreter->CreateFrameRecognizer(m_python_class.c_str());
}
RecognizedStackFrameSP
ScriptedStackFrameRecognizer::RecognizeFrame(lldb::StackFrameSP frame) {
if (!m_python_object_sp || !m_interpreter)
return RecognizedStackFrameSP();
ValueObjectListSP args =
m_interpreter->GetRecognizedArguments(m_python_object_sp, frame);
return RecognizedStackFrameSP(new ScriptedRecognizedStackFrame(args));
}
class StackFrameRecognizerManagerImpl {
public:
void AddRecognizer(StackFrameRecognizerSP recognizer, ConstString &module,
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});
}
void AddRecognizer(StackFrameRecognizerSP recognizer,
RegularExpressionSP module, RegularExpressionSP symbol,
bool first_instruction_only) {
m_recognizers.push_front({(uint32_t)m_recognizers.size(), false, recognizer, true, ConstString(), module,
ConstString(), symbol, first_instruction_only});
}
void ForEach(
std::function<void(uint32_t recognized_id, std::string recognizer_name, std::string module,
std::string symbol, bool regexp)> const &callback) {
for (auto entry : m_recognizers) {
if (entry.is_regexp) {
callback(entry.recognizer_id, entry.recognizer->GetName(), entry.module_regexp->GetText(),
entry.symbol_regexp->GetText(), true);
} else {
callback(entry.recognizer_id, entry.recognizer->GetName(), entry.module.GetCString(),
entry.symbol.GetCString(), false);
}
}
}
bool RemoveRecognizerWithID(uint32_t recognizer_id) {
if (recognizer_id >= m_recognizers.size()) return false;
if (m_recognizers[recognizer_id].deleted) return false;
m_recognizers[recognizer_id].deleted = true;
return true;
}
void RemoveAllRecognizers() {
m_recognizers.clear();
}
StackFrameRecognizerSP GetRecognizerForFrame(StackFrameSP frame) {
const SymbolContext &symctx =
frame->GetSymbolContext(eSymbolContextModule | eSymbolContextFunction);
ConstString function_name = symctx.GetFunctionName();
ModuleSP module_sp = symctx.module_sp;
if (!module_sp) return StackFrameRecognizerSP();
ConstString module_name = module_sp->GetFileSpec().GetFilename();
Symbol *symbol = symctx.symbol;
if (!symbol) return StackFrameRecognizerSP();
Address start_addr = symbol->GetAddress();
Address current_addr = frame->GetFrameCodeAddress();
for (auto entry : m_recognizers) {
if (entry.deleted) continue;
if (entry.module)
if (entry.module != module_name) continue;
if (entry.module_regexp)
if (!entry.module_regexp->Execute(module_name.GetStringRef())) continue;
if (entry.symbol)
if (entry.symbol != function_name) continue;
if (entry.symbol_regexp)
if (!entry.symbol_regexp->Execute(function_name.GetStringRef()))
continue;
if (entry.first_instruction_only)
if (start_addr != current_addr) continue;
return entry.recognizer;
}
return StackFrameRecognizerSP();
}
RecognizedStackFrameSP RecognizeFrame(StackFrameSP frame) {
auto recognizer = GetRecognizerForFrame(frame);
if (!recognizer) return RecognizedStackFrameSP();
return recognizer->RecognizeFrame(frame);
}
private:
struct RegisteredEntry {
uint32_t recognizer_id;
bool deleted;
StackFrameRecognizerSP recognizer;
bool is_regexp;
ConstString module;
RegularExpressionSP module_regexp;
ConstString symbol;
RegularExpressionSP symbol_regexp;
bool first_instruction_only;
};
std::deque<RegisteredEntry> m_recognizers;
};
StackFrameRecognizerManagerImpl &GetStackFrameRecognizerManagerImpl() {
static StackFrameRecognizerManagerImpl instance =
StackFrameRecognizerManagerImpl();
return instance;
}
void StackFrameRecognizerManager::AddRecognizer(
StackFrameRecognizerSP recognizer, ConstString &module, ConstString &symbol,
bool first_instruction_only) {
GetStackFrameRecognizerManagerImpl().AddRecognizer(recognizer, module, symbol,
first_instruction_only);
}
void StackFrameRecognizerManager::AddRecognizer(
StackFrameRecognizerSP recognizer, RegularExpressionSP module,
RegularExpressionSP symbol, bool first_instruction_only) {
GetStackFrameRecognizerManagerImpl().AddRecognizer(recognizer, module, symbol,
first_instruction_only);
}
void StackFrameRecognizerManager::ForEach(
std::function<void(uint32_t recognized_id, std::string recognizer_name, std::string module,
std::string symbol, bool regexp)> const &callback) {
GetStackFrameRecognizerManagerImpl().ForEach(callback);
}
void StackFrameRecognizerManager::RemoveAllRecognizers() {
GetStackFrameRecognizerManagerImpl().RemoveAllRecognizers();
}
bool StackFrameRecognizerManager::RemoveRecognizerWithID(uint32_t recognizer_id) {
return GetStackFrameRecognizerManagerImpl().RemoveRecognizerWithID(recognizer_id);
}
RecognizedStackFrameSP StackFrameRecognizerManager::RecognizeFrame(
StackFrameSP frame) {
return GetStackFrameRecognizerManagerImpl().RecognizeFrame(frame);
}
StackFrameRecognizerSP StackFrameRecognizerManager::GetRecognizerForFrame(
lldb::StackFrameSP frame) {
return GetStackFrameRecognizerManagerImpl().GetRecognizerForFrame(frame);
}

View File

@ -930,8 +930,61 @@ if target:
</tt></pre></code>
</div>
<div class="postfooter"></div>
</div>
<div class="post">
<h1 class ="postheader">Writing LLDB frame recognizers in Python</h1>
<div class="postcontent">
<p>Frame recognizers allow for retrieving information about special frames based on
ABI, arguments or other special properties of that frame, even without source
code or debug info. Currently, one use case is to extract function arguments
that would otherwise be unaccesible, or augment existing arguments.</p>
<p>Adding a custom frame recognizer is done by implementing a Python class
and using the '<b>frame recognizer add</b>' command. The Python class should have a
'<b>get_recognized_arguments</b>' method and it will receive an argument of type
<b>lldb.SBFrame</b> representing the current frame that we are trying to recognize.
The method should return a (possibly empty) list of <b>lldb.SBValue</b> objects that
represent the recognized arguments.</p>
<p>An example of a recognizer that retrieves the file descriptor values from libc
functions '<b>read</b>', '<b>write</b>' and '<b>close</b>' follows:</p>
<code><pre><tt> class LibcFdRecognizer(object):
def get_recognized_arguments(self, frame):
if frame.name in ["read", "write", "close"]:
fd = frame.EvaluateExpression("$arg1").unsigned
value = lldb.target.CreateValueFromExpression("fd", "(int)%d" % fd)
return [value]
return []
</tt></pre></code>
<p>The file containing this implementation can be imported via '<b>command script
import</b>' and then we can register this recognizer with '<b>frame recognizer add</b>'.
It's important to restrict the recognizer to the libc library (which is
libsystem_kernel.dylib on macOS) to avoid matching functions with the same name in other modules:</p>
<code><pre><tt>(lldb) <b>command script import .../fd_recognizer.py</b>
(lldb) <b>frame recognizer add -l fd_recognizer.LibcFdRecognizer -n read -s libsystem_kernel.dylib</b>
</tt></pre></code>
<p>When the program is stopped at the beginning of the '<b>read</b>' function in libc, we
can view the recognizer arguments in '<b>frame variable</b>':</p>
<code><pre><tt>(lldb) <b>b read</b>
(lldb) <b>r</b>
Process 1234 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.3
frame #0: 0x00007fff06013ca0 libsystem_kernel.dylib`read
(lldb) <b>frame variable</b>
(int) fd = 3
</tt></pre></code>
</div>
<div class="postfooter"></div>
</div>
</div>
</div>
</body>