This revision was automatically updated to reflect the committed changes. Closed by commit rL345693: [lldb] Introduce StackFrameRecognizer [take 3] (authored by kuba.brecka, committed by ).
Changed prior to commit: https://reviews.llvm.org/D44603?vs=171848&id=171860#toc Repository: rL LLVM https://reviews.llvm.org/D44603 Files: lldb/trunk/include/lldb/API/SBVariablesOptions.h lldb/trunk/include/lldb/Interpreter/OptionGroupVariable.h lldb/trunk/include/lldb/Interpreter/ScriptInterpreter.h lldb/trunk/include/lldb/Target/StackFrame.h lldb/trunk/include/lldb/Target/StackFrameRecognizer.h lldb/trunk/include/lldb/lldb-forward.h lldb/trunk/lldb.xcodeproj/project.pbxproj lldb/trunk/packages/Python/lldbsuite/test/functionalities/frame-recognizer/Makefile lldb/trunk/packages/Python/lldbsuite/test/functionalities/frame-recognizer/TestFrameRecognizer.py lldb/trunk/packages/Python/lldbsuite/test/functionalities/frame-recognizer/main.m lldb/trunk/packages/Python/lldbsuite/test/functionalities/frame-recognizer/recognizer.py lldb/trunk/scripts/Python/python-wrapper.swig lldb/trunk/scripts/interface/SBVariablesOptions.i lldb/trunk/source/API/SBFrame.cpp lldb/trunk/source/API/SBVariablesOptions.cpp lldb/trunk/source/API/SystemInitializerFull.cpp lldb/trunk/source/Commands/CommandObjectFrame.cpp lldb/trunk/source/Interpreter/OptionGroupVariable.cpp lldb/trunk/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp lldb/trunk/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.h lldb/trunk/source/Target/CMakeLists.txt lldb/trunk/source/Target/StackFrame.cpp lldb/trunk/source/Target/StackFrameRecognizer.cpp lldb/trunk/www/python-reference.html
Index: lldb/trunk/packages/Python/lldbsuite/test/functionalities/frame-recognizer/recognizer.py =================================================================== --- lldb/trunk/packages/Python/lldbsuite/test/functionalities/frame-recognizer/recognizer.py +++ lldb/trunk/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 [] Index: lldb/trunk/packages/Python/lldbsuite/test/functionalities/frame-recognizer/Makefile =================================================================== --- lldb/trunk/packages/Python/lldbsuite/test/functionalities/frame-recognizer/Makefile +++ lldb/trunk/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 Index: lldb/trunk/packages/Python/lldbsuite/test/functionalities/frame-recognizer/TestFrameRecognizer.py =================================================================== --- lldb/trunk/packages/Python/lldbsuite/test/functionalities/frame-recognizer/TestFrameRecognizer.py +++ lldb/trunk/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']) + """ Index: lldb/trunk/packages/Python/lldbsuite/test/functionalities/frame-recognizer/main.m =================================================================== --- lldb/trunk/packages/Python/lldbsuite/test/functionalities/frame-recognizer/main.m +++ lldb/trunk/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; +} Index: lldb/trunk/lldb.xcodeproj/project.pbxproj =================================================================== --- lldb/trunk/lldb.xcodeproj/project.pbxproj +++ lldb/trunk/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 */, Index: lldb/trunk/source/API/SystemInitializerFull.cpp =================================================================== --- lldb/trunk/source/API/SystemInitializerFull.cpp +++ lldb/trunk/source/API/SystemInitializerFull.cpp @@ -235,6 +235,13 @@ 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 @@ LLDBSwigPython_MightHaveChildrenSynthProviderInstance, LLDBSwigPython_GetValueSynthProviderInstance, LLDBSwigPythonCallCommand, LLDBSwigPythonCallCommandObject, LLDBSwigPythonCallModuleInit, - LLDBSWIGPythonCreateOSPlugin, LLDBSWIGPythonRunScriptKeywordProcess, + LLDBSWIGPythonCreateOSPlugin, LLDBSWIGPython_CreateFrameRecognizer, + LLDBSwigPython_GetRecognizedArguments, + LLDBSWIGPythonRunScriptKeywordProcess, LLDBSWIGPythonRunScriptKeywordThread, LLDBSWIGPythonRunScriptKeywordTarget, LLDBSWIGPythonRunScriptKeywordFrame, LLDBSWIGPythonRunScriptKeywordValue, LLDBSWIGPython_GetDynamicSetting, Index: lldb/trunk/source/API/SBVariablesOptions.cpp =================================================================== --- lldb/trunk/source/API/SBVariablesOptions.cpp +++ lldb/trunk/source/API/SBVariablesOptions.cpp @@ -16,9 +16,9 @@ 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 @@ 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 @@ 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 @@ 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(); } Index: lldb/trunk/source/API/SBFrame.cpp =================================================================== --- lldb/trunk/source/API/SBFrame.cpp +++ lldb/trunk/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,17 +961,19 @@ 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 = options.GetIncludeRuntimeSupportValues(); 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 @@ } } } + 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 " Index: lldb/trunk/source/Target/StackFrameRecognizer.cpp =================================================================== --- lldb/trunk/source/Target/StackFrameRecognizer.cpp +++ lldb/trunk/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); +} Index: lldb/trunk/source/Target/CMakeLists.txt =================================================================== --- lldb/trunk/source/Target/CMakeLists.txt +++ lldb/trunk/source/Target/CMakeLists.txt @@ -28,6 +28,7 @@ SectionLoadList.cpp StackFrame.cpp StackFrameList.cpp + StackFrameRecognizer.cpp StackID.cpp StopInfo.cpp StructuredDataPlugin.cpp Index: lldb/trunk/source/Target/StackFrame.cpp =================================================================== --- lldb/trunk/source/Target/StackFrame.cpp +++ lldb/trunk/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 @@ 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 @@ 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 @@ 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 @@ } return true; } + +RecognizedStackFrameSP StackFrame::GetRecognizedFrame() { + if (!m_recognized_frame_sp) { + m_recognized_frame_sp = + StackFrameRecognizerManager::RecognizeFrame(CalculateStackFrame()); + } + return m_recognized_frame_sp; +} Index: lldb/trunk/source/Interpreter/OptionGroupVariable.cpp =================================================================== --- lldb/trunk/source/Interpreter/OptionGroupVariable.cpp +++ lldb/trunk/source/Interpreter/OptionGroupVariable.cpp @@ -28,6 +28,9 @@ {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 @@ 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 @@ 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; Index: lldb/trunk/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp =================================================================== --- lldb/trunk/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp +++ lldb/trunk/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 @@ 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,62 @@ 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); + auto valobj_sp = g_swig_get_valobj_sp_from_sbvalue(sb_value_ptr); + if (valobj_sp) result->Append(valobj_sp); + } + 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 +3246,8 @@ 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 +3276,8 @@ 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; Index: lldb/trunk/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.h =================================================================== --- lldb/trunk/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.h +++ lldb/trunk/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.h @@ -94,6 +94,12 @@ 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 @@ 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 @@ 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, Index: lldb/trunk/source/Commands/CommandObjectFrame.cpp =================================================================== --- lldb/trunk/source/Commands/CommandObjectFrame.cpp +++ lldb/trunk/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 @@ 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 @@ 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 @@ CommandObjectSP(new CommandObjectFrameSelect(interpreter))); LoadSubCommand("variable", CommandObjectSP(new CommandObjectFrameVariable(interpreter))); +#ifndef LLDB_DISABLE_PYTHON + LoadSubCommand( + "recognizer", + CommandObjectSP(new CommandObjectFrameRecognizer(interpreter))); +#endif } CommandObjectMultiwordFrame::~CommandObjectMultiwordFrame() = default; Index: lldb/trunk/scripts/interface/SBVariablesOptions.i =================================================================== --- lldb/trunk/scripts/interface/SBVariablesOptions.i +++ lldb/trunk/scripts/interface/SBVariablesOptions.i @@ -26,7 +26,13 @@ void SetIncludeArguments (bool); - + + bool + GetIncludeRecognizedArguments () const; + + void + SetIncludeRecognizedArguments (bool); + bool GetIncludeLocals () const; Index: lldb/trunk/scripts/Python/python-wrapper.swig =================================================================== --- lldb/trunk/scripts/Python/python-wrapper.swig +++ lldb/trunk/scripts/Python/python-wrapper.swig @@ -785,6 +785,52 @@ } 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; Index: lldb/trunk/www/python-reference.html =================================================================== --- lldb/trunk/www/python-reference.html +++ lldb/trunk/www/python-reference.html @@ -928,11 +928,64 @@ <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> + + <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> + </div> + <div class="postfooter"></div> + </div> + + </div> </div> </body> </html> Index: lldb/trunk/include/lldb/API/SBVariablesOptions.h =================================================================== --- lldb/trunk/include/lldb/API/SBVariablesOptions.h +++ lldb/trunk/include/lldb/API/SBVariablesOptions.h @@ -33,6 +33,10 @@ void SetIncludeArguments(bool); + bool GetIncludeRecognizedArguments() const; + + void SetIncludeRecognizedArguments(bool); + bool GetIncludeLocals() const; void SetIncludeLocals(bool); Index: lldb/trunk/include/lldb/lldb-forward.h =================================================================== --- lldb/trunk/include/lldb/lldb-forward.h +++ lldb/trunk/include/lldb/lldb-forward.h @@ -184,6 +184,7 @@ class ProcessLaunchInfo; class Property; struct PropertyDefinition; +class RecognizedStackFrame; class RegisterCheckpoint; class RegisterContext; class RegisterLocation; @@ -208,6 +209,8 @@ class StackFrame; class StackFrameImpl; class StackFrameList; +class StackFrameRecognizer; +class StackFrameRecognizerManager; class StackID; class StopInfo; class Stoppoint; @@ -414,6 +417,8 @@ 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::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; Index: lldb/trunk/include/lldb/Interpreter/ScriptInterpreter.h =================================================================== --- lldb/trunk/include/lldb/Interpreter/ScriptInterpreter.h +++ lldb/trunk/include/lldb/Interpreter/ScriptInterpreter.h @@ -174,6 +174,17 @@ } 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(); Index: lldb/trunk/include/lldb/Interpreter/OptionGroupVariable.h =================================================================== --- lldb/trunk/include/lldb/Interpreter/OptionGroupVariable.h +++ lldb/trunk/include/lldb/Interpreter/OptionGroupVariable.h @@ -39,6 +39,8 @@ 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; Index: lldb/trunk/include/lldb/Target/StackFrame.h =================================================================== --- lldb/trunk/include/lldb/Target/StackFrame.h +++ lldb/trunk/include/lldb/Target/StackFrame.h @@ -544,6 +544,8 @@ void CalculateExecutionContext(ExecutionContext &exe_ctx) override; + lldb::RecognizedStackFrameSP GetRecognizedFrame(); + protected: friend class StackFrameList; @@ -578,6 +580,7 @@ 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; Index: lldb/trunk/include/lldb/Target/StackFrameRecognizer.h =================================================================== --- lldb/trunk/include/lldb/Target/StackFrameRecognizer.h +++ lldb/trunk/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 + : public 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 + : public 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_
_______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits