Skip to content

Commit 273a2d3

Browse files
committed
[lldb] Move PassthroughScriptedProcess to lldb.scripted_process module
This patch moves the `PassthroughScriptedProcess` & `PassthroughScriptedThread` classes from the `interactive_scripted_process.py` test implementation to the `lldb.scripted_process` python module. This class is very versatile so it makes more sense to ship it with the python module to make it easier for our adopters to derive their class from it instead of copying it. During the "migration", I've also noticed some bugs in the `PassthroughScriptedThread` creation and update, so I also fixed that as part of this patch. Differential Revision: https://reviews.llvm.org/D151044 Signed-off-by: Med Ismail Bennani <[email protected]>
1 parent 8f407b8 commit 273a2d3

File tree

2 files changed

+177
-193
lines changed

2 files changed

+177
-193
lines changed

lldb/examples/python/scripted_process/scripted_process.py

Lines changed: 157 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from abc import ABCMeta, abstractmethod
22

33
import lldb
4+
import json, struct, signal
45

56
class ScriptedProcess(metaclass=ABCMeta):
67

@@ -225,7 +226,6 @@ def create_breakpoint(self, addr, error):
225226
% self.__class__.__name__)
226227
return False
227228

228-
229229
class ScriptedThread(metaclass=ABCMeta):
230230

231231
"""
@@ -376,6 +376,162 @@ def get_extended_info(self):
376376
"""
377377
return self.extended_info
378378

379+
380+
class PassthroughScriptedProcess(ScriptedProcess):
381+
driving_target = None
382+
driving_process = None
383+
384+
def __init__(self, exe_ctx, args, launched_driving_process=True):
385+
super().__init__(exe_ctx, args)
386+
387+
self.driving_target = None
388+
self.driving_process = None
389+
390+
self.driving_target_idx = args.GetValueForKey("driving_target_idx")
391+
if self.driving_target_idx and self.driving_target_idx.IsValid():
392+
idx = self.driving_target_idx.GetUnsignedIntegerValue(42)
393+
self.driving_target = self.target.GetDebugger().GetTargetAtIndex(idx)
394+
395+
if launched_driving_process:
396+
self.driving_process = self.driving_target.GetProcess()
397+
for driving_thread in self.driving_process:
398+
structured_data = lldb.SBStructuredData()
399+
structured_data.SetFromJSON(
400+
json.dumps(
401+
{
402+
"driving_target_idx": idx,
403+
"thread_idx": driving_thread.GetIndexID(),
404+
}
405+
)
406+
)
407+
408+
self.threads[
409+
driving_thread.GetThreadID()
410+
] = PassthroughScriptedThread(self, structured_data)
411+
412+
for module in self.driving_target.modules:
413+
path = module.file.fullpath
414+
load_addr = module.GetObjectFileHeaderAddress().GetLoadAddress(
415+
self.driving_target
416+
)
417+
self.loaded_images.append({"path": path, "load_addr": load_addr})
418+
419+
def get_memory_region_containing_address(self, addr):
420+
mem_region = lldb.SBMemoryRegionInfo()
421+
error = self.driving_process.GetMemoryRegionInfo(addr, mem_region)
422+
if error.Fail():
423+
return None
424+
return mem_region
425+
426+
def read_memory_at_address(self, addr, size, error):
427+
data = lldb.SBData()
428+
bytes_read = self.driving_process.ReadMemory(addr, size, error)
429+
430+
if error.Fail():
431+
return data
432+
433+
data.SetDataWithOwnership(
434+
error,
435+
bytes_read,
436+
self.driving_target.GetByteOrder(),
437+
self.driving_target.GetAddressByteSize(),
438+
)
439+
440+
return data
441+
442+
def write_memory_at_address(self, addr, data, error):
443+
return self.driving_process.WriteMemory(
444+
addr, bytearray(data.uint8.all()), error
445+
)
446+
447+
def get_process_id(self):
448+
return self.driving_process.GetProcessID()
449+
450+
def is_alive(self):
451+
return True
452+
453+
def get_scripted_thread_plugin(self):
454+
return f"{PassthroughScriptedThread.__module__}.{PassthroughScriptedThread.__name__}"
455+
456+
457+
class PassthroughScriptedThread(ScriptedThread):
458+
def __init__(self, process, args):
459+
super().__init__(process, args)
460+
driving_target_idx = args.GetValueForKey("driving_target_idx")
461+
thread_idx = args.GetValueForKey("thread_idx")
462+
463+
# TODO: Change to Walrus operator (:=) with oneline if assignment
464+
# Requires python 3.8
465+
val = thread_idx.GetUnsignedIntegerValue()
466+
if val is not None:
467+
self.idx = val
468+
469+
self.driving_target = None
470+
self.driving_process = None
471+
self.driving_thread = None
472+
473+
# TODO: Change to Walrus operator (:=) with oneline if assignment
474+
# Requires python 3.8
475+
val = driving_target_idx.GetUnsignedIntegerValue()
476+
if val is not None:
477+
self.driving_target = self.target.GetDebugger().GetTargetAtIndex(val)
478+
self.driving_process = self.driving_target.GetProcess()
479+
self.driving_thread = self.driving_process.GetThreadByIndexID(self.idx)
480+
481+
if self.driving_thread:
482+
self.id = self.driving_thread.GetThreadID()
483+
484+
def get_thread_id(self):
485+
return self.id
486+
487+
def get_name(self):
488+
return f"{PassthroughScriptedThread.__name__}.thread-{self.idx}"
489+
490+
def get_stop_reason(self):
491+
stop_reason = {"type": lldb.eStopReasonInvalid, "data": {}}
492+
493+
if (
494+
self.driving_thread
495+
and self.driving_thread.IsValid()
496+
and self.get_thread_id() == self.driving_thread.GetThreadID()
497+
):
498+
stop_reason["type"] = lldb.eStopReasonNone
499+
500+
# TODO: Passthrough stop reason from driving process
501+
if self.driving_thread.GetStopReason() != lldb.eStopReasonNone:
502+
if "arm64" in self.scripted_process.arch:
503+
stop_reason["type"] = lldb.eStopReasonException
504+
stop_reason["data"][
505+
"desc"
506+
] = self.driving_thread.GetStopDescription(100)
507+
elif self.scripted_process.arch == "x86_64":
508+
stop_reason["type"] = lldb.eStopReasonSignal
509+
stop_reason["data"]["signal"] = signal.SIGTRAP
510+
else:
511+
stop_reason["type"] = self.driving_thread.GetStopReason()
512+
513+
return stop_reason
514+
515+
def get_register_context(self):
516+
if not self.driving_thread or self.driving_thread.GetNumFrames() == 0:
517+
return None
518+
frame = self.driving_thread.GetFrameAtIndex(0)
519+
520+
GPRs = None
521+
registerSet = frame.registers # Returns an SBValueList.
522+
for regs in registerSet:
523+
if "general purpose" in regs.name.lower():
524+
GPRs = regs
525+
break
526+
527+
if not GPRs:
528+
return None
529+
530+
for reg in GPRs:
531+
self.register_ctx[reg.name] = int(reg.value, base=16)
532+
533+
return struct.pack(f"{len(self.register_ctx)}Q", *self.register_ctx.values())
534+
379535
ARM64_GPR = [ {'name': 'x0', 'bitsize': 64, 'offset': 0, 'encoding': 'uint', 'format': 'hex', 'set': 0, 'gcc': 0, 'dwarf': 0, 'generic': 'arg0', 'alt-name': 'arg0'},
380536
{'name': 'x1', 'bitsize': 64, 'offset': 8, 'encoding': 'uint', 'format': 'hex', 'set': 0, 'gcc': 1, 'dwarf': 1, 'generic': 'arg1', 'alt-name': 'arg1'},
381537
{'name': 'x2', 'bitsize': 64, 'offset': 16, 'encoding': 'uint', 'format': 'hex', 'set': 0, 'gcc': 2, 'dwarf': 2, 'generic': 'arg2', 'alt-name': 'arg2'},

0 commit comments

Comments
 (0)