aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lldb/include/lldb/API/SBVariablesOptions.h4
-rw-r--r--lldb/include/lldb/Interpreter/OptionGroupVariable.h2
-rw-r--r--lldb/include/lldb/Interpreter/ScriptInterpreter.h11
-rw-r--r--lldb/include/lldb/Target/StackFrame.h3
-rw-r--r--lldb/include/lldb/Target/StackFrameRecognizer.h129
-rw-r--r--lldb/include/lldb/lldb-forward.h7
-rw-r--r--lldb/lldb.xcodeproj/project.pbxproj6
-rw-r--r--lldb/packages/Python/lldbsuite/test/functionalities/frame-recognizer/Makefile10
-rw-r--r--lldb/packages/Python/lldbsuite/test/functionalities/frame-recognizer/TestFrameRecognizer.py102
-rw-r--r--lldb/packages/Python/lldbsuite/test/functionalities/frame-recognizer/main.m28
-rw-r--r--lldb/packages/Python/lldbsuite/test/functionalities/frame-recognizer/recognizer.py21
-rw-r--r--lldb/scripts/Python/python-wrapper.swig46
-rw-r--r--lldb/scripts/interface/SBVariablesOptions.i8
-rw-r--r--lldb/source/API/SBFrame.cpp25
-rw-r--r--lldb/source/API/SBVariablesOptions.cpp23
-rw-r--r--lldb/source/API/SystemInitializerFull.cpp11
-rw-r--r--lldb/source/Commands/CommandObjectFrame.cpp386
-rw-r--r--lldb/source/Interpreter/OptionGroupVariable.cpp7
-rw-r--r--lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp64
-rw-r--r--lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.h15
-rw-r--r--lldb/source/Target/CMakeLists.txt1
-rw-r--r--lldb/source/Target/StackFrame.cpp18
-rw-r--r--lldb/source/Target/StackFrameRecognizer.cpp190
-rwxr-xr-xlldb/www/python-reference.html61
24 files changed, 1162 insertions, 16 deletions
diff --git a/lldb/include/lldb/API/SBVariablesOptions.h b/lldb/include/lldb/API/SBVariablesOptions.h
index 756da64..23ff426 100644
--- a/lldb/include/lldb/API/SBVariablesOptions.h
+++ b/lldb/include/lldb/API/SBVariablesOptions.h
@@ -33,6 +33,10 @@ public:
void SetIncludeArguments(bool);
+ bool GetIncludeRecognizedArguments() const;
+
+ void SetIncludeRecognizedArguments(bool);
+
bool GetIncludeLocals() const;
void SetIncludeLocals(bool);
diff --git a/lldb/include/lldb/Interpreter/OptionGroupVariable.h b/lldb/include/lldb/Interpreter/OptionGroupVariable.h
index c9e9032..f5902c5 100644
--- a/lldb/include/lldb/Interpreter/OptionGroupVariable.h
+++ b/lldb/include/lldb/Interpreter/OptionGroupVariable.h
@@ -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;
diff --git a/lldb/include/lldb/Interpreter/ScriptInterpreter.h b/lldb/include/lldb/Interpreter/ScriptInterpreter.h
index f7fbb81..a1b5180 100644
--- a/lldb/include/lldb/Interpreter/ScriptInterpreter.h
+++ b/lldb/include/lldb/Interpreter/ScriptInterpreter.h
@@ -174,6 +174,17 @@ public:
}
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) {
return StructuredData::GenericSP();
diff --git a/lldb/include/lldb/Target/StackFrame.h b/lldb/include/lldb/Target/StackFrame.h
index 2a736f7..3eda70c3 100644
--- a/lldb/include/lldb/Target/StackFrame.h
+++ b/lldb/include/lldb/Target/StackFrame.h
@@ -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;
diff --git a/lldb/include/lldb/Target/StackFrameRecognizer.h b/lldb/include/lldb/Target/StackFrameRecognizer.h
new file mode 100644
index 0000000..db3f125
--- /dev/null
+++ b/lldb/include/lldb/Target/StackFrameRecognizer.h
@@ -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_
diff --git a/lldb/include/lldb/lldb-forward.h b/lldb/include/lldb/lldb-forward.h
index 5bb9107..dc5b4ef6 100644
--- a/lldb/include/lldb/lldb-forward.h
+++ b/lldb/include/lldb/lldb-forward.h
@@ -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;
diff --git a/lldb/lldb.xcodeproj/project.pbxproj b/lldb/lldb.xcodeproj/project.pbxproj
index 92f4210..bdee876 100644
--- a/lldb/lldb.xcodeproj/project.pbxproj
+++ b/lldb/lldb.xcodeproj/project.pbxproj
@@ -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 */,
diff --git a/lldb/packages/Python/lldbsuite/test/functionalities/frame-recognizer/Makefile b/lldb/packages/Python/lldbsuite/test/functionalities/frame-recognizer/Makefile
new file mode 100644
index 0000000..45f00b3
--- /dev/null
+++ b/lldb/packages/Python/lldbsuite/test/functionalities/frame-recognizer/Makefile
@@ -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
diff --git a/lldb/packages/Python/lldbsuite/test/functionalities/frame-recognizer/TestFrameRecognizer.py b/lldb/packages/Python/lldbsuite/test/functionalities/frame-recognizer/TestFrameRecognizer.py
new file mode 100644
index 0000000..abd4ae8
--- /dev/null
+++ b/lldb/packages/Python/lldbsuite/test/functionalities/frame-recognizer/TestFrameRecognizer.py
@@ -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'])
+ """
diff --git a/lldb/packages/Python/lldbsuite/test/functionalities/frame-recognizer/main.m b/lldb/packages/Python/lldbsuite/test/functionalities/frame-recognizer/main.m
new file mode 100644
index 0000000..51103ae
--- /dev/null
+++ b/lldb/packages/Python/lldbsuite/test/functionalities/frame-recognizer/main.m
@@ -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;
+}
diff --git a/lldb/packages/Python/lldbsuite/test/functionalities/frame-recognizer/recognizer.py b/lldb/packages/Python/lldbsuite/test/functionalities/frame-recognizer/recognizer.py
new file mode 100644
index 0000000..a8a5067
--- /dev/null
+++ b/lldb/packages/Python/lldbsuite/test/functionalities/frame-recognizer/recognizer.py
@@ -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 []
diff --git a/lldb/scripts/Python/python-wrapper.swig b/lldb/scripts/Python/python-wrapper.swig
index 1679d89..e6a6b7a 100644
--- a/lldb/scripts/Python/python-wrapper.swig
+++ b/lldb/scripts/Python/python-wrapper.swig
@@ -785,6 +785,52 @@ LLDBSWIGPythonCreateOSPlugin
}
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)
{
using namespace lldb_private;
diff --git a/lldb/scripts/interface/SBVariablesOptions.i b/lldb/scripts/interface/SBVariablesOptions.i
index 3941a58..1d58a4d 100644
--- a/lldb/scripts/interface/SBVariablesOptions.i
+++ b/lldb/scripts/interface/SBVariablesOptions.i
@@ -26,7 +26,13 @@ public:
void
SetIncludeArguments (bool);
-
+
+ bool
+ GetIncludeRecognizedArguments () const;
+
+ void
+ SetIncludeRecognizedArguments (bool);
+
bool
GetIncludeLocals () const;
diff --git a/lldb/source/API/SBFrame.cpp b/lldb/source/API/SBFrame.cpp
index 8a9088d..33348f8 100644
--- a/lldb/source/API/SBFrame.cpp
+++ b/lldb/source/API/SBFrame.cpp
@@ -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,10 +969,11 @@ 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,
- include_runtime_support_values, use_dynamic);
+ 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;
Process *process = exe_ctx.GetProcessPtr();
@@ -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 "
diff --git a/lldb/source/API/SBVariablesOptions.cpp b/lldb/source/API/SBVariablesOptions.cpp
index e12b969..ae06975 100644
--- a/lldb/source/API/SBVariablesOptions.cpp
+++ b/lldb/source/API/SBVariablesOptions.cpp
@@ -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();
}
diff --git a/lldb/source/API/SystemInitializerFull.cpp b/lldb/source/API/SystemInitializerFull.cpp
index fab9171..2e649d5 100644
--- a/lldb/source/API/SystemInitializerFull.cpp
+++ b/lldb/source/API/SystemInitializerFull.cpp
@@ -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,
diff --git a/lldb/source/Commands/CommandObjectFrame.cpp b/lldb/source/Commands/CommandObjectFrame.cpp
index 5424bcd..0dface6 100644
--- a/lldb/source/Commands/CommandObjectFrame.cpp
+++ b/lldb/source/Commands/CommandObjectFrame.cpp
@@ -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;
diff --git a/lldb/source/Interpreter/OptionGroupVariable.cpp b/lldb/source/Interpreter/OptionGroupVariable.cpp
index 1bcddb4..f35aeb6 100644
--- a/lldb/source/Interpreter/OptionGroupVariable.cpp
+++ b/lldb/source/Interpreter/OptionGroupVariable.cpp
@@ -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;
diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
index 0db2829..af3687a 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
@@ -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;
diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.h b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.h
index ef29a96..7314bba 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.h
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.h
@@ -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);
@@ -232,6 +238,13 @@ public:
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,
diff --git a/lldb/source/Target/CMakeLists.txt b/lldb/source/Target/CMakeLists.txt
index df137c3..265b3a8 100644
--- a/lldb/source/Target/CMakeLists.txt
+++ b/lldb/source/Target/CMakeLists.txt
@@ -28,6 +28,7 @@ add_lldb_library(lldbTarget
SectionLoadList.cpp
StackFrame.cpp
StackFrameList.cpp
+ StackFrameRecognizer.cpp
StackID.cpp
StopInfo.cpp
StructuredDataPlugin.cpp
diff --git a/lldb/source/Target/StackFrame.cpp b/lldb/source/Target/StackFrame.cpp
index 00be79d..6340459d 100644
--- a/lldb/source/Target/StackFrame.cpp
+++ b/lldb/source/Target/StackFrame.cpp
@@ -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;
+}
diff --git a/lldb/source/Target/StackFrameRecognizer.cpp b/lldb/source/Target/StackFrameRecognizer.cpp
new file mode 100644
index 0000000..cb86036
--- /dev/null
+++ b/lldb/source/Target/StackFrameRecognizer.cpp
@@ -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);
+}
diff --git a/lldb/www/python-reference.html b/lldb/www/python-reference.html
index cbf8921..bde728f 100755
--- a/lldb/www/python-reference.html
+++ b/lldb/www/python-reference.html
@@ -928,11 +928,64 @@ if target:
<font color=green># We do have a symbol, print some info for the symbol</font>
print symbol
</tt></pre></code>
- </div>
- <div class="postfooter"></div>
+ </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>
- </div>
- </div>
+ <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>
</html>