def get_modules_access_object(self):
'''An accessor function that returns a modules_access() object which allows lazy module access from a lldb.SBTarget object.'''
- return self.modules_access (self)
+ return self.modules_access(self)
def get_modules_array(self):
'''An accessor function that returns a list() that contains all modules in a lldb.SBTarget object.'''
object.'''
return lldb_iter(self, 'GetNumBreakpoints', 'GetBreakpointAtIndex')
+ class bkpts_access(object):
+ '''A helper object that will lazily hand out bkpts for a target when supplied an index.'''
+ def __init__(self, sbtarget):
+ self.sbtarget = sbtarget
+
+ def __len__(self):
+ if self.sbtarget:
+ return int(self.sbtarget.GetNumBreakpoints())
+ return 0
+
+ def __getitem__(self, key):
+ if isinstance(key, int):
+ count = len(self)
+ if -count <= key < count:
+ key %= count
+ return self.sbtarget.GetBreakpointAtIndex(key)
+ return None
+
+ def get_bkpts_access_object(self):
+ '''An accessor function that returns a bkpts_access() object which allows lazy bkpt access from a lldb.SBtarget object.'''
+ return self.bkpts_access(self)
+
+ def get_target_bkpts(self):
+ '''An accessor function that returns a list() that contains all bkpts in a lldb.SBtarget object.'''
+ bkpts = []
+ for idx in range(self.GetNumBreakpoints()):
+ bkpts.append(self.GetBreakpointAtIndex(idx))
+ return bkpts
+
def watchpoint_iter(self):
'''Returns an iterator over all watchpoints in a lldb.SBTarget
object.'''
return lldb_iter(self, 'GetNumWatchpoints', 'GetWatchpointAtIndex')
+ class watchpoints_access(object):
+ '''A helper object that will lazily hand out watchpoints for a target when supplied an index.'''
+ def __init__(self, sbtarget):
+ self.sbtarget = sbtarget
+
+ def __len__(self):
+ if self.sbtarget:
+ return int(self.sbtarget.GetNumWatchpoints())
+ return 0
+
+ def __getitem__(self, key):
+ if isinstance(key, int):
+ count = len(self)
+ if -count <= key < count:
+ key %= count
+ return self.sbtarget.GetWatchpointAtIndex(key)
+ return None
+
+ def get_watchpoints_access_object(self):
+ '''An accessor function that returns a watchpoints_access() object which allows lazy watchpoint access from a lldb.SBtarget object.'''
+ return self.watchpoints_access(self)
+
+ def get_target_watchpoints(self):
+ '''An accessor function that returns a list() that contains all watchpoints in a lldb.SBtarget object.'''
+ watchpoints = []
+ for idx in range(self.GetNumWatchpoints()):
+ bkpts.append(self.GetWatchpointAtIndex(idx))
+ return watchpoints
+
modules = property(get_modules_array, None, doc='''A read only property that returns a list() of lldb.SBModule objects contained in this target. This list is a list all modules that the target currently is tracking (the main executable and all dependent shared libraries).''')
module = property(get_modules_access_object, None, doc=r'''A read only property that returns an object that implements python operator overloading with the square brackets().\n target.module[<int>] allows array access to any modules.\n target.module[<str>] allows access to modules by basename, full path, or uuid string value.\n target.module[uuid.UUID()] allows module access by UUID.\n target.module[re] allows module access using a regular expression that matches the module full path.''')
process = property(GetProcess, None, doc='''A read only property that returns an lldb object that represents the process (lldb.SBProcess) that this target owns.''')
executable = property(GetExecutable, None, doc='''A read only property that returns an lldb object that represents the main executable module (lldb.SBModule) for this target.''')
debugger = property(GetDebugger, None, doc='''A read only property that returns an lldb object that represents the debugger (lldb.SBDebugger) that owns this target.''')
num_breakpoints = property(GetNumBreakpoints, None, doc='''A read only property that returns the number of breakpoints that this target has as an integer.''')
+ breakpoints = property(get_target_bkpts, None, doc='''A read only property that returns a list() of lldb.SBBreakpoint objects for all breakpoints in this target.''')
+ breakpoint = property(get_bkpts_access_object, None, doc='''A read only property that returns an object that can be used to access breakpoints as an array ("bkpt_12 = lldb.target.bkpt[12]").''')
num_watchpoints = property(GetNumWatchpoints, None, doc='''A read only property that returns the number of watchpoints that this target has as an integer.''')
+ watchpoints = property(get_target_watchpoints, None, doc='''A read only property that returns a list() of lldb.SBwatchpoint objects for all watchpoints in this target.''')
+ watchpoint = property(get_watchpoints_access_object, None, doc='''A read only property that returns an object that can be used to access watchpoints as an array ("watchpoint_12 = lldb.target.watchpoint[12]").''')
broadcaster = property(GetBroadcaster, None, doc='''A read only property that an lldb object that represents the broadcaster (lldb.SBBroadcaster) for this target.''')
byte_order = property(GetByteOrder, None, doc='''A read only property that returns an lldb enumeration value (lldb.eByteOrderLittle, lldb.eByteOrderBig, lldb.eByteOrderInvalid) that represents the byte order for this target.''')
addr_size = property(GetAddressByteSize, None, doc='''A read only property that returns the size in bytes of an address for this target.''')
return sb_ptr;
}
+void *lldb_private::LLDBSWIGPython_CastPyObjectToSBBreakpoint(PyObject * data) {
+ lldb::SBBreakpoint *sb_ptr = nullptr;
+
+ int valid_cast =
+ SWIG_ConvertPtr(data, (void **)&sb_ptr, SWIGTYPE_p_lldb__SBBreakpoint, 0);
+
+ if (valid_cast == -1)
+ return NULL;
+
+ return sb_ptr;
+}
+
void *lldb_private::LLDBSWIGPython_CastPyObjectToSBAttachInfo(PyObject * data) {
lldb::SBAttachInfo *sb_ptr = nullptr;
"""
return self.metadata
+ def create_breakpoint(self, addr, error):
+ """ Create a breakpoint in the scripted process from an address.
+ This is mainly used with interactive scripted process debugging.
+
+ Args:
+ addr (int): Address at which the breakpoint should be set.
+ error (lldb.SBError): Error object.
+
+ Returns:
+ SBBreakpoint: A valid breakpoint object that was created a the specified
+ address. None if the breakpoint creation failed.
+ """
+ error.SetErrorString("%s doesn't support creating breakpoints."
+ % self.__class__.__name__)
+ return False
+
+
class ScriptedThread(metaclass=ABCMeta):
"""
class SBBreakpointListImpl;
+namespace lldb_private {
+class ScriptInterpreter;
+}
+
namespace lldb {
class LLDB_API SBBreakpoint {
friend class SBBreakpointName;
friend class SBTarget;
+ friend class lldb_private::ScriptInterpreter;
+
lldb::BreakpointSP GetSP() const;
lldb::BreakpointWP m_opaque_wp;
#define LLDB_INTERPRETER_SCRIPTINTERPRETER_H
#include "lldb/API/SBAttachInfo.h"
+#include "lldb/API/SBBreakpoint.h"
#include "lldb/API/SBData.h"
#include "lldb/API/SBError.h"
#include "lldb/API/SBLaunchInfo.h"
Status GetStatusFromSBError(const lldb::SBError &error) const;
+ lldb::BreakpointSP
+ GetOpaqueTypeFromSBBreakpoint(const lldb::SBBreakpoint &breakpoint) const;
+
lldb::ProcessAttachInfoSP
GetOpaqueTypeFromSBAttachInfo(const lldb::SBAttachInfo &attach_info) const;
virtual StructuredData::DictionarySP GetThreadsInfo() { return {}; }
+ virtual bool CreateBreakpoint(lldb::addr_t addr, Status &error) {
+ error.SetErrorString("ScriptedProcess don't support creating breakpoints.");
+ return {};
+ }
+
virtual lldb::DataExtractorSP
ReadMemoryAtAddress(lldb::addr_t address, size_t size, Status &error) {
return {};
return data.m_opaque_sp;
}
+lldb::BreakpointSP ScriptInterpreter::GetOpaqueTypeFromSBBreakpoint(
+ const lldb::SBBreakpoint &breakpoint) const {
+ return breakpoint.m_opaque_wp.lock();
+}
+
lldb::ProcessAttachInfoSP ScriptInterpreter::GetOpaqueTypeFromSBAttachInfo(
const lldb::SBAttachInfo &attach_info) const {
return attach_info.m_opaque_sp;
return Status("Scripted Processes don't support hardware breakpoints");
}
- return EnableSoftwareBreakpoint(bp_site);
+ Status error;
+ GetInterface().CreateBreakpoint(bp_site->GetLoadAddress(), error);
+
+ return error;
}
ArchSpec ScriptedProcess::GetArchitecture() {
} // namespace python
void *LLDBSWIGPython_CastPyObjectToSBData(PyObject *data);
+void *LLDBSWIGPython_CastPyObjectToSBBreakpoint(PyObject *data);
void *LLDBSWIGPython_CastPyObjectToSBAttachInfo(PyObject *data);
void *LLDBSWIGPython_CastPyObjectToSBLaunchInfo(PyObject *data);
void *LLDBSWIGPython_CastPyObjectToSBError(PyObject *data);
return dict;
}
+bool ScriptedProcessPythonInterface::CreateBreakpoint(lldb::addr_t addr,
+ Status &error) {
+ Status py_error;
+ StructuredData::ObjectSP obj =
+ Dispatch("create_breakpoint", py_error, addr, error);
+
+ // If there was an error on the python call, surface it to the user.
+ if (py_error.Fail())
+ error = py_error;
+
+ if (!CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj, error))
+ return {};
+
+ return obj->GetBooleanValue();
+}
+
lldb::DataExtractorSP ScriptedProcessPythonInterface::ReadMemoryAtAddress(
lldb::addr_t address, size_t size, Status &error) {
Status py_error;
StructuredData::DictionarySP GetThreadsInfo() override;
+ bool CreateBreakpoint(lldb::addr_t addr, Status &error) override;
+
lldb::DataExtractorSP ReadMemoryAtAddress(lldb::addr_t address, size_t size,
Status &error) override;
}
template <>
+lldb::BreakpointSP
+ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::BreakpointSP>(
+ python::PythonObject &p, Status &error) {
+ lldb::SBBreakpoint *sb_breakpoint = reinterpret_cast<lldb::SBBreakpoint *>(
+ LLDBSWIGPython_CastPyObjectToSBBreakpoint(p.get()));
+
+ if (!sb_breakpoint) {
+ error.SetErrorString(
+ "Couldn't cast lldb::SBBreakpoint to lldb::BreakpointSP.");
+ return nullptr;
+ }
+
+ return m_interpreter.GetOpaqueTypeFromSBBreakpoint(*sb_breakpoint);
+}
+
+template <>
lldb::ProcessAttachInfoSP ScriptedPythonInterface::ExtractValueFromPythonObject<
lldb::ProcessAttachInfoSP>(python::PythonObject &p, Status &error) {
lldb::SBAttachInfo *sb_attach_info = reinterpret_cast<lldb::SBAttachInfo *>(
python::PythonObject &p, Status &error);
template <>
+lldb::BreakpointSP
+ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::BreakpointSP>(
+ python::PythonObject &p, Status &error);
+
+template <>
lldb::ProcessAttachInfoSP ScriptedPythonInterface::ExtractValueFromPythonObject<
lldb::ProcessAttachInfoSP>(python::PythonObject &p, Status &error);
self.assertEqual(real_pc, mux_pc, f"PC's equal for {id}")
lldbutil.run_break_set_by_source_regexp(self, "also break here")
- self.assertEqual(mux_target.GetNumBreakpoints(), 1)
+ self.assertEqual(mux_target.GetNumBreakpoints(), 2)
error = mux_process.Continue()
self.assertSuccess(error, "Resuming multiplexer scripted process")
self.assertTrue(mux_process.IsValid(), "Got a valid process")
# -o "create_sub" \
# -o "br set -p 'also break here'" -o 'continue'
-import os, json, struct, signal
+import os, json, struct, signal, tempfile
from threading import Thread
from typing import Any, Dict
)
)
+ def create_breakpoint(self, addr, error, pid=None):
+ if not self.multiplexer:
+ error.SetErrorString("Multiplexer is not set.")
+ return self.multiplexer.create_breakpoint(addr, error, self.get_process_id())
+
def get_scripted_thread_plugin(self) -> str:
return f"{MultiplexedScriptedThread.__module__}.{MultiplexedScriptedThread.__name__}"
)
self.multiplexed_processes = {}
+ # Copy breakpoints from real target to passthrough
+ with tempfile.NamedTemporaryFile() as tf:
+ bkpt_file = lldb.SBFileSpec(tf.name)
+ error = self.driving_target.BreakpointsWriteToFile(bkpt_file)
+ if error.Fail():
+ log(
+ "Failed to save breakpoints from driving target (%s)"
+ % error.GetCString()
+ )
+ bkpts_list = lldb.SBBreakpointList(self.target)
+ error = self.target.BreakpointsCreateFromFile(bkpt_file, bkpts_list)
+ if error.Fail():
+ log(
+ "Failed create breakpoints from driving target \
+ (bkpt file: %s)"
+ % tf.name
+ )
+
+ # Copy breakpoint from passthrough to real target
+ if error.Success():
+ self.driving_target.DeleteAllBreakpoints()
+ for bkpt in self.target.breakpoints:
+ if bkpt.IsValid():
+ for bl in bkpt:
+ real_bpkt = self.driving_target.BreakpointCreateBySBAddress(
+ bl.GetAddress()
+ )
+ if not real_bpkt.IsValid():
+ log(
+ "Failed to set breakpoint at address %s in \
+ driving target"
+ % hex(bl.GetLoadAddress())
+ )
+
self.listener_thread = Thread(
target=self.wait_for_driving_process_to_stop, daemon=True
)
parity = pid % 2
return dict(filter(lambda pair: pair[0] % 2 == parity, self.threads.items()))
+ def create_breakpoint(self, addr, error, pid=None):
+ if not self.driving_target:
+ error.SetErrorString("%s has no driving target." % self.__class__.__name__)
+ return False
+
+ def create_breakpoint_with_name(target, load_addr, name, error):
+ addr = lldb.SBAddress(load_addr, target)
+ if not addr.IsValid():
+ error.SetErrorString("Invalid breakpoint address %s" % hex(load_addr))
+ return False
+ bkpt = target.BreakpointCreateBySBAddress(addr)
+ if not bkpt.IsValid():
+ error.SetErrorString(
+ "Failed to create breakpoint at address %s"
+ % hex(addr.GetLoadAddress())
+ )
+ return False
+ error = bkpt.AddNameWithErrorHandling(name)
+ return error.Success()
+
+ name = (
+ "multiplexer_scripted_process"
+ if not pid
+ else f"multiplexed_scripted_process_{pid}"
+ )
+
+ if pid is not None:
+ # This means that this method has been called from one of the
+ # multiplexed scripted process. That also means that the multiplexer
+ # target doesn't have this breakpoint created.
+ mux_error = lldb.SBError()
+ bkpt = create_breakpoint_with_name(self.target, addr, name, mux_error)
+ if mux_error.Fail():
+ error.SetError(
+ "Failed to create breakpoint in multiplexer \
+ target: %s"
+ % mux_error.GetCString()
+ )
+ return False
+ return create_breakpoint_with_name(self.driving_target, addr, name, error)
+
def multiplex(mux_process, muxed_process):
muxed_process.GetScriptedImplementation().multiplexer = (
return nullptr;
}
+void *lldb_private::LLDBSWIGPython_CastPyObjectToSBBreakpoint(PyObject *data) {
+ return nullptr;
+}
+
void *lldb_private::LLDBSWIGPython_CastPyObjectToSBAttachInfo(PyObject *data) {
return nullptr;
}