Skip to content

Commit

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

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

llvm-svn: 345686
  • Loading branch information
kubamracek committed Oct 31, 2018
1 parent 3e27306 commit 8fddd98
Show file tree
Hide file tree
Showing 24 changed files with 1,162 additions and 16 deletions.
4 changes: 4 additions & 0 deletions lldb/include/lldb/API/SBVariablesOptions.h
Expand Up @@ -33,6 +33,10 @@ class LLDB_API SBVariablesOptions {

void SetIncludeArguments(bool);

bool GetIncludeRecognizedArguments() const;

void SetIncludeRecognizedArguments(bool);

bool GetIncludeLocals() const;

void SetIncludeLocals(bool);
Expand Down
2 changes: 2 additions & 0 deletions lldb/include/lldb/Interpreter/OptionGroupVariable.h
Expand Up @@ -39,6 +39,8 @@ class OptionGroupVariable : public OptionGroup {

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;
Expand Down
11 changes: 11 additions & 0 deletions lldb/include/lldb/Interpreter/ScriptInterpreter.h
Expand Up @@ -173,6 +173,17 @@ class ScriptInterpreter : public PluginInterface {
return StructuredData::GenericSP();
}

virtual StructuredData::GenericSP
CreateFrameRecognizer(const char *class_name) {
return StructuredData::GenericSP();
}

virtual lldb::ValueObjectListSP GetRecognizedArguments(
const StructuredData::ObjectSP &implementor,
lldb::StackFrameSP frame_sp) {
return lldb::ValueObjectListSP();
}

virtual StructuredData::GenericSP
OSPlugin_CreatePluginObject(const char *class_name,
lldb::ProcessSP process_sp) {
Expand Down
3 changes: 3 additions & 0 deletions lldb/include/lldb/Target/StackFrame.h
Expand Up @@ -544,6 +544,8 @@ class StackFrame : public ExecutionContextScope,

void CalculateExecutionContext(ExecutionContext &exe_ctx) override;

lldb::RecognizedStackFrameSP GetRecognizedFrame();

protected:
friend class StackFrameList;

Expand Down Expand Up @@ -578,6 +580,7 @@ class StackFrame : public ExecutionContextScope,
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;

Expand Down
129 changes: 129 additions & 0 deletions 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
: 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_
7 changes: 7 additions & 0 deletions lldb/include/lldb/lldb-forward.h
Expand Up @@ -184,6 +184,7 @@ class ProcessInstanceInfoMatch;
class ProcessLaunchInfo;
class Property;
struct PropertyDefinition;
class RecognizedStackFrame;
class RegisterCheckpoint;
class RegisterContext;
class RegisterLocation;
Expand All @@ -208,6 +209,8 @@ class SourceManagerImpl;
class StackFrame;
class StackFrameImpl;
class StackFrameList;
class StackFrameRecognizer;
class StackFrameRecognizerManager;
class StackID;
class StopInfo;
class Stoppoint;
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand Down
6 changes: 6 additions & 0 deletions lldb/lldb.xcodeproj/project.pbxproj
Expand Up @@ -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 */; };
Expand Down Expand Up @@ -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>"; };
Expand Down Expand Up @@ -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 */,
Expand Down Expand Up @@ -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 */,
Expand Down
@@ -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
@@ -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'])
"""
@@ -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;
}

0 comments on commit 8fddd98

Please sign in to comment.