xref: /llvm-project/lldb/test/API/functionalities/scripted_process/stack_core_scripted_process.py (revision 2238dcc39358353cac21df75c3c3286ab20b8f53)
1import os, json, struct, signal
2
3from typing import Any, Dict
4
5import lldb
6from lldb.plugins.scripted_process import ScriptedProcess
7from lldb.plugins.scripted_process import ScriptedThread
8
9
10class StackCoreScriptedProcess(ScriptedProcess):
11    def get_module_with_name(self, target, name):
12        for module in target.modules:
13            if name in module.GetFileSpec().GetFilename():
14                return module
15        return None
16
17    def __init__(self, exe_ctx: lldb.SBExecutionContext, args: lldb.SBStructuredData):
18        super().__init__(exe_ctx, args)
19
20        self.corefile_target = None
21        self.corefile_process = None
22
23        self.backing_target_idx = args.GetValueForKey("backing_target_idx")
24        if self.backing_target_idx and self.backing_target_idx.IsValid():
25            if self.backing_target_idx.GetType() == lldb.eStructuredDataTypeInteger:
26                idx = self.backing_target_idx.GetIntegerValue(42)
27            if self.backing_target_idx.GetType() == lldb.eStructuredDataTypeString:
28                idx = int(self.backing_target_idx.GetStringValue(100))
29            self.corefile_target = self.target.GetDebugger().GetTargetAtIndex(idx)
30            self.corefile_process = self.corefile_target.GetProcess()
31            for corefile_thread in self.corefile_process:
32                structured_data = lldb.SBStructuredData()
33                structured_data.SetFromJSON(
34                    json.dumps(
35                        {
36                            "backing_target_idx": idx,
37                            "thread_idx": corefile_thread.GetIndexID(),
38                        }
39                    )
40                )
41
42                self.threads[corefile_thread.GetThreadID()] = StackCoreScriptedThread(
43                    self, structured_data
44                )
45
46        if len(self.threads) == 2:
47            self.threads[len(self.threads) - 1].is_stopped = True
48
49        corefile_module = self.get_module_with_name(
50            self.corefile_target, "libbaz.dylib"
51        )
52        if not corefile_module or not corefile_module.IsValid():
53            return
54        module_path = os.path.join(
55            corefile_module.GetFileSpec().GetDirectory(),
56            corefile_module.GetFileSpec().GetFilename(),
57        )
58        if not os.path.exists(module_path):
59            return
60        module_load_addr = corefile_module.GetObjectFileHeaderAddress().GetLoadAddress(
61            self.corefile_target
62        )
63
64        self.loaded_images.append({"path": module_path, "load_addr": module_load_addr})
65
66    def get_memory_region_containing_address(
67        self, addr: int
68    ) -> lldb.SBMemoryRegionInfo:
69        mem_region = lldb.SBMemoryRegionInfo()
70        error = self.corefile_process.GetMemoryRegionInfo(addr, mem_region)
71        if error.Fail():
72            return None
73        return mem_region
74
75    def read_memory_at_address(
76        self, addr: int, size: int, error: lldb.SBError
77    ) -> lldb.SBData:
78        data = lldb.SBData()
79        bytes_read = self.corefile_process.ReadMemory(addr, size, error)
80
81        if error.Fail():
82            return data
83
84        data.SetDataWithOwnership(
85            error,
86            bytes_read,
87            self.corefile_target.GetByteOrder(),
88            self.corefile_target.GetAddressByteSize(),
89        )
90
91        return data
92
93    def get_loaded_images(self):
94        return self.loaded_images
95
96    def get_process_id(self) -> int:
97        return 42
98
99    def should_stop(self) -> bool:
100        return True
101
102    def is_alive(self) -> bool:
103        return True
104
105    def get_scripted_thread_plugin(self):
106        return (
107            StackCoreScriptedThread.__module__ + "." + StackCoreScriptedThread.__name__
108        )
109
110
111class StackCoreScriptedThread(ScriptedThread):
112    def __init__(self, process, args):
113        super().__init__(process, args)
114        backing_target_idx = args.GetValueForKey("backing_target_idx")
115        thread_idx = args.GetValueForKey("thread_idx")
116        self.is_stopped = False
117
118        def extract_value_from_structured_data(data, default_val):
119            if data and data.IsValid():
120                if data.GetType() == lldb.eStructuredDataTypeInteger:
121                    return data.GetIntegerValue(default_val)
122                if data.GetType() == lldb.eStructuredDataTypeString:
123                    return int(data.GetStringValue(100))
124            return None
125
126        # TODO: Change to Walrus operator (:=) with oneline if assignment
127        # Requires python 3.8
128        val = extract_value_from_structured_data(thread_idx, 0)
129        if val is not None:
130            self.idx = val
131
132        self.corefile_target = None
133        self.corefile_process = None
134        self.corefile_thread = None
135
136        # TODO: Change to Walrus operator (:=) with oneline if assignment
137        # Requires python 3.8
138        val = extract_value_from_structured_data(backing_target_idx, 42)
139        if val is not None:
140            self.corefile_target = self.target.GetDebugger().GetTargetAtIndex(val)
141            self.corefile_process = self.corefile_target.GetProcess()
142            self.corefile_thread = self.corefile_process.GetThreadByIndexID(self.idx)
143
144        if self.corefile_thread:
145            self.id = self.corefile_thread.GetThreadID()
146
147    def get_thread_id(self) -> int:
148        return self.id
149
150    def get_name(self) -> str:
151        return StackCoreScriptedThread.__name__ + ".thread-" + str(self.id)
152
153    def get_stop_reason(self) -> Dict[str, Any]:
154        stop_reason = {"type": lldb.eStopReasonInvalid, "data": {}}
155
156        if (
157            self.corefile_thread
158            and self.corefile_thread.IsValid()
159            and self.get_thread_id() == self.corefile_thread.GetThreadID()
160        ):
161            stop_reason["type"] = lldb.eStopReasonNone
162
163            if self.is_stopped:
164                if "arm64" in self.scripted_process.arch:
165                    stop_reason["type"] = lldb.eStopReasonException
166                    stop_reason["data"][
167                        "desc"
168                    ] = self.corefile_thread.GetStopDescription(100)
169                elif self.scripted_process.arch == "x86_64":
170                    stop_reason["type"] = lldb.eStopReasonSignal
171                    stop_reason["data"]["signal"] = signal.SIGTRAP
172                else:
173                    stop_reason["type"] = self.corefile_thread.GetStopReason()
174
175        return stop_reason
176
177    def get_register_context(self) -> str:
178        if not self.corefile_thread or self.corefile_thread.GetNumFrames() == 0:
179            return None
180        frame = self.corefile_thread.GetFrameAtIndex(0)
181
182        GPRs = None
183        registerSet = frame.registers  # Returns an SBValueList.
184        for regs in registerSet:
185            if "general purpose" in regs.name.lower():
186                GPRs = regs
187                break
188
189        if not GPRs:
190            return None
191
192        for reg in GPRs:
193            self.register_ctx[reg.name] = int(reg.value, base=16)
194
195        return struct.pack(
196            "{}Q".format(len(self.register_ctx)), *self.register_ctx.values()
197        )
198
199
200def __lldb_init_module(debugger, dict):
201    if not "SKIP_SCRIPTED_PROCESS_LAUNCH" in os.environ:
202        debugger.HandleCommand(
203            "process launch -C %s.%s" % (__name__, StackCoreScriptedProcess.__name__)
204        )
205    else:
206        print(
207            "Name of the class that will manage the scripted process: '%s.%s'"
208            % (__name__, StackCoreScriptedProcess.__name__)
209        )
210