[lldb] Add OperatingSystem base class to the lldb python module
This patch introduces an `OperatingSystem` base implementation in the `lldb` python module to make it easier for lldb users to write their own implementation. The `OperatingSystem` base implementation is derived itself from the `ScriptedThread` base implementation since they share some common grounds. To achieve that, this patch makes changes to the `ScriptedThread` initializer since it gets called by the `OperatingSystem` initializer. I also took the opportunity to document the `OperatingSystem` base class and methods. Differential Revision: https://reviews.llvm.org/D159315 Signed-off-by: Med Ismail Bennani <ismail@bennani.ma>
This commit is contained in:
@@ -104,7 +104,8 @@ function(finish_swig_python swig_target lldb_python_bindings_dir lldb_python_tar
|
||||
"plugins"
|
||||
FILES
|
||||
"${LLDB_SOURCE_DIR}/examples/python/templates/scripted_process.py"
|
||||
"${LLDB_SOURCE_DIR}/examples/python/templates/scripted_platform.py")
|
||||
"${LLDB_SOURCE_DIR}/examples/python/templates/scripted_platform.py"
|
||||
"${LLDB_SOURCE_DIR}/examples/python/templates/operating_system.py")
|
||||
|
||||
if(APPLE)
|
||||
create_python_package(
|
||||
|
||||
103
lldb/examples/python/templates/operating_system.py
Normal file
103
lldb/examples/python/templates/operating_system.py
Normal file
@@ -0,0 +1,103 @@
|
||||
from abc import abstractmethod
|
||||
|
||||
import lldb
|
||||
import struct
|
||||
|
||||
from lldb.plugins.scripted_process import ScriptedThread
|
||||
|
||||
|
||||
class OperatingSystem(ScriptedThread):
|
||||
"""
|
||||
Class that provides data for an instance of a LLDB 'OperatingSystemPython' plug-in class.
|
||||
|
||||
```
|
||||
thread_info = {
|
||||
"tid": tid,
|
||||
"name": "four",
|
||||
"queue": "queue4",
|
||||
"state": "stopped",
|
||||
"stop_reason": "none",
|
||||
"core" : 2
|
||||
}
|
||||
```
|
||||
|
||||
- tid : thread ID (mandatory)
|
||||
- name : thread name (optional key/value pair)
|
||||
- queue : thread dispatch queue name (optional key/value pair)
|
||||
- state : thread state (mandatory, set to 'stopped' for now)
|
||||
- core : the index of the core (lldb) thread that this OS Thread should shadow
|
||||
- stop_reason : thread stop reason. (mandatory, usually set to 'none')
|
||||
Possible values include:
|
||||
- 'breakpoint': thread is stopped at a breakpoint
|
||||
- 'none': thread is stopped because the process is stopped
|
||||
- 'trace': thread is stopped after single stepping
|
||||
The usual value for this while threads are in memory is 'none'
|
||||
- register_data_addr : the address of the register data in memory (optional key/value pair)
|
||||
Specifying this key/value pair for a thread will avoid a call to get_register_data()
|
||||
and can be used when your registers are in a thread context structure that is contiguous
|
||||
in memory. Don't specify this if your register layout in memory doesn't match the layout
|
||||
described by the dictionary returned from a call to the get_register_info() method.
|
||||
"""
|
||||
|
||||
def __init__(self, process):
|
||||
"""Initialization needs a valid lldb.SBProcess object. This plug-in
|
||||
will get created after a live process is valid and has stopped for the
|
||||
first time.
|
||||
|
||||
Args:
|
||||
process (lldb.SBProcess): The process owning this thread.
|
||||
"""
|
||||
self.registers = None
|
||||
super().__init__(process, None)
|
||||
self.registers = self.register_info
|
||||
self.threads = []
|
||||
|
||||
def create_thread(self, tid, context):
|
||||
"""Lazily create an operating system thread using a thread information
|
||||
dictionary and an optional operating system thread context address.
|
||||
This method is called manually, using the SBAPI
|
||||
`lldb.SBProcess.CreateOSPluginThread` affordance.
|
||||
|
||||
Args:
|
||||
tid (int): Thread ID to get `thread_info` dictionary for.
|
||||
context (int): Address of the operating system thread struct.
|
||||
|
||||
Returns:
|
||||
Dict: The `thread_info` dictionary containing the various information
|
||||
for lldb to create a Thread object and add it to the process thread list.
|
||||
"""
|
||||
return None
|
||||
|
||||
@abstractmethod
|
||||
def get_thread_info(self):
|
||||
"""Get the list of operating system threads. This method gets called
|
||||
automatically every time the process stops and it needs to update its
|
||||
thread list.
|
||||
|
||||
Returns:
|
||||
List[thread_info]: A list of `os_thread` dictionaries
|
||||
containing at least for each entry, the thread id, it's name,
|
||||
queue, state, stop reason. It can also contain a
|
||||
`register_data_addr`. The list can be empty.
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def get_register_data(self, tid):
|
||||
"""Get the operating system thread register context for given a thread
|
||||
id. This method is called when unwinding the stack of one of the
|
||||
operating system threads.
|
||||
|
||||
Args:
|
||||
tid (int): Thread ID to get register context for.
|
||||
|
||||
Returns:
|
||||
str: A byte representing all register's value.
|
||||
"""
|
||||
pass
|
||||
|
||||
def get_register_context(self):
|
||||
pass
|
||||
|
||||
def get_stop_reason(self):
|
||||
pass
|
||||
@@ -244,16 +244,16 @@ class ScriptedThread(metaclass=ABCMeta):
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def __init__(self, scripted_process, args):
|
||||
def __init__(self, process, args):
|
||||
"""Construct a scripted thread.
|
||||
|
||||
Args:
|
||||
process (ScriptedProcess): The scripted process owning this thread.
|
||||
process (ScriptedProcess/lldb.SBProcess): The process owning this thread.
|
||||
args (lldb.SBStructuredData): A Dictionary holding arbitrary
|
||||
key/value pairs used by the scripted thread.
|
||||
"""
|
||||
self.target = None
|
||||
self.scripted_process = None
|
||||
self.originating_process = None
|
||||
self.process = None
|
||||
self.args = None
|
||||
self.idx = 0
|
||||
@@ -268,9 +268,13 @@ class ScriptedThread(metaclass=ABCMeta):
|
||||
self.frames = []
|
||||
self.extended_info = []
|
||||
|
||||
if isinstance(scripted_process, ScriptedProcess):
|
||||
self.target = scripted_process.target
|
||||
self.scripted_process = scripted_process
|
||||
if (
|
||||
isinstance(process, ScriptedProcess)
|
||||
or isinstance(process, lldb.SBProcess)
|
||||
and process.IsValid()
|
||||
):
|
||||
self.target = process.target
|
||||
self.originating_process = process
|
||||
self.process = self.target.GetProcess()
|
||||
self.get_register_info()
|
||||
|
||||
@@ -354,14 +358,14 @@ class ScriptedThread(metaclass=ABCMeta):
|
||||
def get_register_info(self):
|
||||
if self.register_info is None:
|
||||
self.register_info = dict()
|
||||
if self.scripted_process.arch == "x86_64":
|
||||
if self.originating_process.arch == "x86_64":
|
||||
self.register_info["sets"] = ["General Purpose Registers"]
|
||||
self.register_info["registers"] = INTEL64_GPR
|
||||
elif "arm64" in self.scripted_process.arch:
|
||||
elif "arm64" in self.originating_process.arch:
|
||||
self.register_info["sets"] = ["General Purpose Registers"]
|
||||
self.register_info["registers"] = ARM64_GPR
|
||||
else:
|
||||
raise ValueError("Unknown architecture", self.scripted_process.arch)
|
||||
raise ValueError("Unknown architecture", self.originating_process.arch)
|
||||
return self.register_info
|
||||
|
||||
@abstractmethod
|
||||
@@ -505,12 +509,12 @@ class PassthroughScriptedThread(ScriptedThread):
|
||||
|
||||
# TODO: Passthrough stop reason from driving process
|
||||
if self.driving_thread.GetStopReason() != lldb.eStopReasonNone:
|
||||
if "arm64" in self.scripted_process.arch:
|
||||
if "arm64" in self.originating_process.arch:
|
||||
stop_reason["type"] = lldb.eStopReasonException
|
||||
stop_reason["data"][
|
||||
"desc"
|
||||
] = self.driving_thread.GetStopDescription(100)
|
||||
elif self.scripted_process.arch == "x86_64":
|
||||
elif self.originating_process.arch == "x86_64":
|
||||
stop_reason["type"] = lldb.eStopReasonSignal
|
||||
stop_reason["data"]["signal"] = signal.SIGTRAP
|
||||
else:
|
||||
|
||||
@@ -1,29 +1,14 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import lldb
|
||||
import struct
|
||||
|
||||
from lldb.plugins.operating_system import OperatingSystem
|
||||
|
||||
class OperatingSystemPlugIn(object):
|
||||
|
||||
class OperatingSystemPlugIn(OperatingSystem):
|
||||
"""Class that provides data for an instance of a LLDB 'OperatingSystemPython' plug-in class"""
|
||||
|
||||
def __init__(self, process):
|
||||
"""Initialization needs a valid.SBProcess object.
|
||||
|
||||
This plug-in will get created after a live process is valid and has stopped for the
|
||||
first time."""
|
||||
self.process = None
|
||||
self.registers = None
|
||||
self.threads = None
|
||||
if isinstance(process, lldb.SBProcess) and process.IsValid():
|
||||
self.process = process
|
||||
self.threads = None # Will be an dictionary containing info for each thread
|
||||
|
||||
def get_target(self):
|
||||
# NOTE: Don't use "lldb.target" when trying to get your target as the "lldb.target"
|
||||
# tracks the current target in the LLDB command interpreter which isn't the
|
||||
# correct thing to use for this plug-in.
|
||||
return self.process.target
|
||||
super().__init__(process)
|
||||
|
||||
def create_thread(self, tid, context):
|
||||
if tid == 0x444444444:
|
||||
@@ -40,23 +25,6 @@ class OperatingSystemPlugIn(object):
|
||||
|
||||
def get_thread_info(self):
|
||||
if not self.threads:
|
||||
# The sample dictionary below shows the values that can be returned for a thread
|
||||
# tid => thread ID (mandatory)
|
||||
# name => thread name (optional key/value pair)
|
||||
# queue => thread dispatch queue name (optional key/value pair)
|
||||
# state => thred state (mandatory, set to 'stopped' for now)
|
||||
# stop_reason => thread stop reason. (mandatory, usually set to 'none')
|
||||
# Possible values include:
|
||||
# 'breakpoint' if the thread is stopped at a breakpoint
|
||||
# 'none' thread is just stopped because the process is stopped
|
||||
# 'trace' the thread just single stepped
|
||||
# The usual value for this while threads are in memory is 'none'
|
||||
# register_data_addr => the address of the register data in memory (optional key/value pair)
|
||||
# Specifying this key/value pair for a thread will avoid a call to get_register_data()
|
||||
# and can be used when your registers are in a thread context structure that is contiguous
|
||||
# in memory. Don't specify this if your register layout in memory doesn't match the layout
|
||||
# described by the dictionary returned from a call to the
|
||||
# get_register_info() method.
|
||||
self.threads = [
|
||||
{
|
||||
"tid": 0x111111111,
|
||||
|
||||
Reference in New Issue
Block a user