Skip to content

Commit 7e51f69

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 2f223c4 commit 7e51f69

File tree

2 files changed

+177
-189
lines changed

2 files changed

+177
-189
lines changed

lldb/examples/python/scripted_process/scripted_process.py

Lines changed: 157 additions & 0 deletions
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

67
class ScriptedProcess(metaclass=ABCMeta):
@@ -382,6 +383,162 @@ def get_extended_info(self):
382383
return self.extended_info
383384

384385

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

0 commit comments

Comments
 (0)