Skip to content

Commit 1e6108d

Browse files
committed
[lldb] Introduce ScriptedFrame affordance (llvm#149622)
This patch introduces a new scripting affordance in lldb: `ScriptedFrame`. This allows user to produce mock stackframes in scripted threads and scripted processes from a python script. With this change, StackFrame can be synthetized from different sources: - Either from a dictionary containing a load address, and a frame index, which is the legacy way. - Or by creating a ScriptedFrame python object. One particularity of synthezising stackframes from the ScriptedFrame python object, is that these frame have an optional PC, meaning that they don't have a report a valid PC and they can act as shells that just contain static information, like the frame function name, the list of variables or registers, etc. It can also provide a symbol context. rdar://157260006 (cherry picked from commit 84b5620) Signed-off-by: Med Ismail Bennani <[email protected]>
1 parent 2eee7ce commit 1e6108d

23 files changed

+871
-22
lines changed

lldb/examples/python/templates/scripted_process.py

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,142 @@ def get_extended_info(self):
383383
"""
384384
return self.extended_info
385385

386+
def get_scripted_frame_plugin(self):
387+
"""Get scripted frame plugin name.
388+
389+
Returns:
390+
str: Name of the scripted frame plugin.
391+
"""
392+
return None
393+
394+
395+
class ScriptedFrame(metaclass=ABCMeta):
396+
"""
397+
The base class for a scripted frame.
398+
399+
Most of the base class methods are `@abstractmethod` that need to be
400+
overwritten by the inheriting class.
401+
"""
402+
403+
@abstractmethod
404+
def __init__(self, thread, args):
405+
"""Construct a scripted frame.
406+
407+
Args:
408+
thread (ScriptedThread): The thread owning this frame.
409+
args (lldb.SBStructuredData): A Dictionary holding arbitrary
410+
key/value pairs used by the scripted frame.
411+
"""
412+
self.target = None
413+
self.originating_thread = None
414+
self.thread = None
415+
self.args = None
416+
self.id = None
417+
self.name = None
418+
self.register_info = None
419+
self.register_ctx = {}
420+
self.variables = []
421+
422+
if (
423+
isinstance(thread, ScriptedThread)
424+
or isinstance(thread, lldb.SBThread)
425+
and thread.IsValid()
426+
):
427+
self.target = thread.target
428+
self.process = thread.process
429+
self.originating_thread = thread
430+
self.thread = self.process.GetThreadByIndexID(thread.tid)
431+
self.get_register_info()
432+
433+
@abstractmethod
434+
def get_id(self):
435+
"""Get the scripted frame identifier.
436+
437+
Returns:
438+
int: The identifier of the scripted frame in the scripted thread.
439+
"""
440+
pass
441+
442+
def get_pc(self):
443+
"""Get the scripted frame address.
444+
445+
Returns:
446+
int: The optional address of the scripted frame in the scripted thread.
447+
"""
448+
return None
449+
450+
def get_symbol_context(self):
451+
"""Get the scripted frame symbol context.
452+
453+
Returns:
454+
lldb.SBSymbolContext: The symbol context of the scripted frame in the scripted thread.
455+
"""
456+
return None
457+
458+
def is_inlined(self):
459+
"""Check if the scripted frame is inlined.
460+
461+
Returns:
462+
bool: True if scripted frame is inlined. False otherwise.
463+
"""
464+
return False
465+
466+
def is_artificial(self):
467+
"""Check if the scripted frame is artificial.
468+
469+
Returns:
470+
bool: True if scripted frame is artificial. False otherwise.
471+
"""
472+
return True
473+
474+
def is_hidden(self):
475+
"""Check if the scripted frame is hidden.
476+
477+
Returns:
478+
bool: True if scripted frame is hidden. False otherwise.
479+
"""
480+
return False
481+
482+
def get_function_name(self):
483+
"""Get the scripted frame function name.
484+
485+
Returns:
486+
str: The function name of the scripted frame.
487+
"""
488+
return self.name
489+
490+
def get_display_function_name(self):
491+
"""Get the scripted frame display function name.
492+
493+
Returns:
494+
str: The display function name of the scripted frame.
495+
"""
496+
return self.get_function_name()
497+
498+
def get_variables(self, filters):
499+
"""Get the scripted thread state type.
500+
501+
Args:
502+
filter (lldb.SBVariablesOptions): The filter used to resolve the variables
503+
Returns:
504+
lldb.SBValueList: The SBValueList containing the SBValue for each resolved variable.
505+
Returns None by default.
506+
"""
507+
return None
508+
509+
def get_register_info(self):
510+
if self.register_info is None:
511+
self.register_info = self.originating_thread.get_register_info()
512+
return self.register_info
513+
514+
@abstractmethod
515+
def get_register_context(self):
516+
"""Get the scripted thread register context
517+
518+
Returns:
519+
str: A byte representing all register's value.
520+
"""
521+
pass
386522

387523
class PassthroughScriptedProcess(ScriptedProcess):
388524
driving_target = None

lldb/include/lldb/API/SBSymbolContext.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ class LLDB_API SBSymbolContext {
6666
friend class SBTarget;
6767
friend class SBSymbolContextList;
6868

69+
friend class lldb_private::ScriptInterpreter;
6970
friend class lldb_private::python::SWIGBridge;
7071

7172
SBSymbolContext(const lldb_private::SymbolContext &sc_ptr);
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef LLDB_INTERPRETER_INTERFACES_SCRIPTEDFRAMEINTERFACE_H
10+
#define LLDB_INTERPRETER_INTERFACES_SCRIPTEDFRAMEINTERFACE_H
11+
12+
#include "ScriptedInterface.h"
13+
#include "lldb/Core/StructuredDataImpl.h"
14+
#include "lldb/Symbol/SymbolContext.h"
15+
#include "lldb/lldb-private.h"
16+
#include <optional>
17+
#include <string>
18+
19+
namespace lldb_private {
20+
class ScriptedFrameInterface : virtual public ScriptedInterface {
21+
public:
22+
virtual llvm::Expected<StructuredData::GenericSP>
23+
CreatePluginObject(llvm::StringRef class_name, ExecutionContext &exe_ctx,
24+
StructuredData::DictionarySP args_sp,
25+
StructuredData::Generic *script_obj = nullptr) = 0;
26+
27+
virtual lldb::user_id_t GetID() { return LLDB_INVALID_FRAME_ID; }
28+
29+
virtual lldb::addr_t GetPC() { return LLDB_INVALID_ADDRESS; }
30+
31+
virtual std::optional<SymbolContext> GetSymbolContext() {
32+
return std::nullopt;
33+
}
34+
35+
virtual std::optional<std::string> GetFunctionName() { return std::nullopt; }
36+
37+
virtual std::optional<std::string> GetDisplayFunctionName() {
38+
return std::nullopt;
39+
}
40+
41+
virtual bool IsInlined() { return false; }
42+
43+
virtual bool IsArtificial() { return false; }
44+
45+
virtual bool IsHidden() { return false; }
46+
47+
virtual StructuredData::DictionarySP GetRegisterInfo() { return {}; }
48+
49+
virtual std::optional<std::string> GetRegisterContext() {
50+
return std::nullopt;
51+
}
52+
};
53+
} // namespace lldb_private
54+
55+
#endif // LLDB_INTERPRETER_INTERFACES_SCRIPTEDFRAMEINTERFACE_H

lldb/include/lldb/Interpreter/Interfaces/ScriptedThreadInterface.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,16 @@ class ScriptedThreadInterface : virtual public ScriptedInterface {
4444
}
4545

4646
virtual StructuredData::ArraySP GetExtendedInfo() { return {}; }
47+
48+
virtual std::optional<std::string> GetScriptedFramePluginName() {
49+
return std::nullopt;
50+
}
51+
52+
protected:
53+
friend class ScriptedFrame;
54+
virtual lldb::ScriptedFrameInterfaceSP CreateScriptedFrameInterface() {
55+
return {};
56+
}
4757
};
4858
} // namespace lldb_private
4959

lldb/include/lldb/Interpreter/ScriptInterpreter.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include "lldb/Host/PseudoTerminal.h"
2727
#include "lldb/Host/StreamFile.h"
2828
#include "lldb/Interpreter/Interfaces/OperatingSystemInterface.h"
29+
#include "lldb/Interpreter/Interfaces/ScriptedFrameInterface.h"
2930
#include "lldb/Interpreter/Interfaces/ScriptedPlatformInterface.h"
3031
#include "lldb/Interpreter/Interfaces/ScriptedProcessInterface.h"
3132
#include "lldb/Interpreter/Interfaces/ScriptedThreadInterface.h"
@@ -533,6 +534,10 @@ class ScriptInterpreter : public PluginInterface {
533534
return {};
534535
}
535536

537+
virtual lldb::ScriptedFrameInterfaceSP CreateScriptedFrameInterface() {
538+
return {};
539+
}
540+
536541
virtual lldb::ScriptedThreadPlanInterfaceSP
537542
CreateScriptedThreadPlanInterface() {
538543
return {};

lldb/include/lldb/Target/StackFrame.h

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -398,7 +398,7 @@ class StackFrame : public ExecutionContextScope,
398398
///
399399
/// \return
400400
/// true if this is an inlined frame.
401-
bool IsInlined();
401+
virtual bool IsInlined();
402402

403403
/// Query whether this frame is synthetic.
404404
bool IsSynthetic() const;
@@ -409,12 +409,12 @@ class StackFrame : public ExecutionContextScope,
409409
/// Query whether this frame is artificial (e.g a synthesized result of
410410
/// inferring missing tail call frames from a backtrace). Artificial frames
411411
/// may have limited support for inspecting variables.
412-
bool IsArtificial() const;
412+
virtual bool IsArtificial() const;
413413

414414
/// Query whether this frame should be hidden from backtraces. Frame
415415
/// recognizers can customize this behavior and hide distracting
416416
/// system implementation details this way.
417-
bool IsHidden();
417+
virtual bool IsHidden();
418418

419419
/// Query whether this frame is a Swift Thunk.
420420
LLDB_DEPRECATED("Use IsHidden() instead.")
@@ -429,13 +429,13 @@ class StackFrame : public ExecutionContextScope,
429429
///
430430
/// /// \return
431431
/// A C-String containing the function demangled name. Can be null.
432-
const char *GetFunctionName();
432+
virtual const char *GetFunctionName();
433433

434434
/// Get the frame's demangled display name.
435435
///
436436
/// /// \return
437437
/// A C-String containing the function demangled display name. Can be null.
438-
const char *GetDisplayFunctionName();
438+
virtual const char *GetDisplayFunctionName();
439439

440440
/// Query this frame to find what frame it is in this Thread's
441441
/// StackFrameList.
@@ -547,8 +547,7 @@ class StackFrame : public ExecutionContextScope,
547547

548548
bool HasCachedData() const;
549549

550-
private:
551-
/// For StackFrame only.
550+
/// For StackFrame and derived classes only.
552551
/// \{
553552
lldb::ThreadWP m_thread_wp;
554553
uint32_t m_frame_index;
@@ -585,6 +584,17 @@ class StackFrame : public ExecutionContextScope,
585584
StreamString m_disassembly;
586585
std::recursive_mutex m_mutex;
587586

587+
private:
588+
/// Private methods, called from GetValueForVariableExpressionPath.
589+
/// See that method for documentation of parameters and return value.
590+
lldb::ValueObjectSP LegacyGetValueForVariableExpressionPath(
591+
llvm::StringRef var_expr, lldb::DynamicValueType use_dynamic,
592+
uint32_t options, lldb::VariableSP &var_sp, Status &error);
593+
594+
lldb::ValueObjectSP DILGetValueForVariableExpressionPath(
595+
llvm::StringRef var_expr, lldb::DynamicValueType use_dynamic,
596+
uint32_t options, lldb::VariableSP &var_sp, Status &error);
597+
588598
StackFrame(const StackFrame &) = delete;
589599
const StackFrame &operator=(const StackFrame &) = delete;
590600
};

lldb/include/lldb/lldb-forward.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,7 @@ class SaveCoreOptions;
187187
class Scalar;
188188
class ScriptInterpreter;
189189
class ScriptInterpreterLocker;
190+
class ScriptedFrameInterface;
190191
class ScriptedMetadata;
191192
class ScriptedBreakpointInterface;
192193
class ScriptedPlatformInterface;
@@ -407,6 +408,8 @@ typedef std::shared_ptr<lldb_private::RecognizedStackFrame>
407408
typedef std::shared_ptr<lldb_private::ScriptSummaryFormat>
408409
ScriptSummaryFormatSP;
409410
typedef std::shared_ptr<lldb_private::ScriptInterpreter> ScriptInterpreterSP;
411+
typedef std::shared_ptr<lldb_private::ScriptedFrameInterface>
412+
ScriptedFrameInterfaceSP;
410413
typedef std::shared_ptr<lldb_private::ScriptedMetadata> ScriptedMetadataSP;
411414
typedef std::unique_ptr<lldb_private::ScriptedPlatformInterface>
412415
ScriptedPlatformInterfaceUP;

lldb/source/Core/FormatEntity.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1691,7 +1691,7 @@ bool FormatEntity::Format(const Entry &entry, Stream &s,
16911691
StackFrame *frame = exe_ctx->GetFramePtr();
16921692
if (frame) {
16931693
const Address &pc_addr = frame->GetFrameCodeAddress();
1694-
if (pc_addr.IsValid()) {
1694+
if (pc_addr.IsValid() || frame->IsSynthetic()) {
16951695
if (DumpAddressAndContent(s, sc, exe_ctx, pc_addr, false))
16961696
return true;
16971697
}

lldb/source/Plugins/Process/scripted/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
add_lldb_library(lldbPluginScriptedProcess PLUGIN
22
ScriptedProcess.cpp
33
ScriptedThread.cpp
4+
ScriptedFrame.cpp
45

56
LINK_LIBS
67
lldbCore

0 commit comments

Comments
 (0)