16cf66801SMed Ismail Bennani# Usage:
26cf66801SMed Ismail Bennani# ./bin/lldb $LLVM/lldb/test/API/functionalities/interactive_scripted_process/main \
36cf66801SMed Ismail Bennani#   -o "br set -p 'Break here'" \
46cf66801SMed Ismail Bennani#   -o "command script import $LLVM/lldb/test/API/functionalities/interactive_scripted_process/interactive_scripted_process.py" \
56cf66801SMed Ismail Bennani#   -o "create_mux" \
66cf66801SMed Ismail Bennani#   -o "create_sub" \
76cf66801SMed Ismail Bennani#   -o "br set -p 'also break here'" -o 'continue'
86cf66801SMed Ismail Bennani
9e31d0c20SMed Ismail Bennaniimport os, json, struct, signal, tempfile
106cf66801SMed Ismail Bennani
116cf66801SMed Ismail Bennanifrom threading import Thread
126cf66801SMed Ismail Bennanifrom typing import Any, Dict
136cf66801SMed Ismail Bennani
146cf66801SMed Ismail Bennaniimport lldb
15*0e90ac9cSMed Ismail Bennanifrom lldb.plugins.scripted_process import PassthroughScriptedProcess
16*0e90ac9cSMed Ismail Bennanifrom lldb.plugins.scripted_process import PassthroughScriptedThread
176cf66801SMed Ismail Bennani
18429e7483SMed Ismail Bennani
19*0e90ac9cSMed Ismail Bennaniclass MultiplexedScriptedProcess(PassthroughScriptedProcess):
206cf66801SMed Ismail Bennani    def __init__(self, exe_ctx: lldb.SBExecutionContext, args: lldb.SBStructuredData):
216cf66801SMed Ismail Bennani        super().__init__(exe_ctx, args)
226cf66801SMed Ismail Bennani        self.multiplexer = None
236cf66801SMed Ismail Bennani        if isinstance(self.driving_process, lldb.SBProcess) and self.driving_process:
246cf66801SMed Ismail Bennani            parity = args.GetValueForKey("parity")
256cf66801SMed Ismail Bennani            # TODO: Change to Walrus operator (:=) with oneline if assignment
266cf66801SMed Ismail Bennani            # Requires python 3.8
27*0e90ac9cSMed Ismail Bennani            val = parity.GetUnsignedIntegerValue()
286cf66801SMed Ismail Bennani            if val is not None:
296cf66801SMed Ismail Bennani                self.parity = val
306cf66801SMed Ismail Bennani
31*0e90ac9cSMed Ismail Bennani            # Turn PassthroughScriptedThread into MultiplexedScriptedThread
326cf66801SMed Ismail Bennani            for thread in self.threads.values():
336cf66801SMed Ismail Bennani                thread.__class__ = MultiplexedScriptedThread
346cf66801SMed Ismail Bennani
356cf66801SMed Ismail Bennani    def get_process_id(self) -> int:
366cf66801SMed Ismail Bennani        return self.parity + 420
376cf66801SMed Ismail Bennani
386cf66801SMed Ismail Bennani    def launch(self, should_stop: bool = True) -> lldb.SBError:
396cf66801SMed Ismail Bennani        self.first_launch = True
406cf66801SMed Ismail Bennani        return lldb.SBError()
416cf66801SMed Ismail Bennani
426cf66801SMed Ismail Bennani    def resume(self, should_stop: bool) -> lldb.SBError:
436cf66801SMed Ismail Bennani        if self.first_launch:
446cf66801SMed Ismail Bennani            self.first_launch = False
456cf66801SMed Ismail Bennani            return super().resume()
466cf66801SMed Ismail Bennani        else:
476cf66801SMed Ismail Bennani            if not self.multiplexer:
486cf66801SMed Ismail Bennani                error = lldb.SBError("Multiplexer is not set.")
496cf66801SMed Ismail Bennani                return error
506cf66801SMed Ismail Bennani            return self.multiplexer.resume(should_stop)
516cf66801SMed Ismail Bennani
526cf66801SMed Ismail Bennani    def get_threads_info(self) -> Dict[int, Any]:
536cf66801SMed Ismail Bennani        if not self.multiplexer:
546cf66801SMed Ismail Bennani            return super().get_threads_info()
556cf66801SMed Ismail Bennani        filtered_threads = self.multiplexer.get_threads_info(pid=self.get_process_id())
56*0e90ac9cSMed Ismail Bennani        # Update the filtered thread class from PassthroughScriptedThread to MultiplexedScriptedThread
576cf66801SMed Ismail Bennani        return dict(
586cf66801SMed Ismail Bennani            map(
596cf66801SMed Ismail Bennani                lambda pair: (pair[0], MultiplexedScriptedThread(pair[1])),
606cf66801SMed Ismail Bennani                filtered_threads.items(),
616cf66801SMed Ismail Bennani            )
626cf66801SMed Ismail Bennani        )
636cf66801SMed Ismail Bennani
64e31d0c20SMed Ismail Bennani    def create_breakpoint(self, addr, error, pid=None):
65e31d0c20SMed Ismail Bennani        if not self.multiplexer:
66e31d0c20SMed Ismail Bennani            error.SetErrorString("Multiplexer is not set.")
67e31d0c20SMed Ismail Bennani        return self.multiplexer.create_breakpoint(addr, error, self.get_process_id())
68e31d0c20SMed Ismail Bennani
696cf66801SMed Ismail Bennani    def get_scripted_thread_plugin(self) -> str:
706cf66801SMed Ismail Bennani        return f"{MultiplexedScriptedThread.__module__}.{MultiplexedScriptedThread.__name__}"
716cf66801SMed Ismail Bennani
72429e7483SMed Ismail Bennani
73*0e90ac9cSMed Ismail Bennaniclass MultiplexedScriptedThread(PassthroughScriptedThread):
746cf66801SMed Ismail Bennani    def get_name(self) -> str:
756cf66801SMed Ismail Bennani        parity = "Odd" if self.scripted_process.parity % 2 else "Even"
766cf66801SMed Ismail Bennani        return f"{parity}{MultiplexedScriptedThread.__name__}.thread-{self.idx}"
776cf66801SMed Ismail Bennani
786cf66801SMed Ismail Bennani
79*0e90ac9cSMed Ismail Bennaniclass MultiplexerScriptedProcess(PassthroughScriptedProcess):
806cf66801SMed Ismail Bennani    listener = None
816cf66801SMed Ismail Bennani    multiplexed_processes = None
826cf66801SMed Ismail Bennani
836cf66801SMed Ismail Bennani    def wait_for_driving_process_to_stop(self):
846cf66801SMed Ismail Bennani        def handle_process_state_event():
856cf66801SMed Ismail Bennani            # Update multiplexer process
866cf66801SMed Ismail Bennani            log("Updating interactive scripted process threads")
876cf66801SMed Ismail Bennani            dbg = self.driving_target.GetDebugger()
88*0e90ac9cSMed Ismail Bennani            new_driving_thread_ids = []
896cf66801SMed Ismail Bennani            for driving_thread in self.driving_process:
90*0e90ac9cSMed Ismail Bennani                new_driving_thread_ids.append(driving_thread.id)
916cf66801SMed Ismail Bennani                log(f"{len(self.threads)} New thread {hex(driving_thread.id)}")
926cf66801SMed Ismail Bennani                structured_data = lldb.SBStructuredData()
936cf66801SMed Ismail Bennani                structured_data.SetFromJSON(
946cf66801SMed Ismail Bennani                    json.dumps(
956cf66801SMed Ismail Bennani                        {
966cf66801SMed Ismail Bennani                            "driving_target_idx": dbg.GetIndexOfTarget(
976cf66801SMed Ismail Bennani                                self.driving_target
986cf66801SMed Ismail Bennani                            ),
996cf66801SMed Ismail Bennani                            "thread_idx": driving_thread.GetIndexID(),
1006cf66801SMed Ismail Bennani                        }
1016cf66801SMed Ismail Bennani                    )
1026cf66801SMed Ismail Bennani                )
1036cf66801SMed Ismail Bennani
104*0e90ac9cSMed Ismail Bennani                self.threads[driving_thread.id] = PassthroughScriptedThread(
1056cf66801SMed Ismail Bennani                    self, structured_data
1066cf66801SMed Ismail Bennani                )
1076cf66801SMed Ismail Bennani
108*0e90ac9cSMed Ismail Bennani            for thread_id in self.threads:
109*0e90ac9cSMed Ismail Bennani                if thread_id not in new_driving_thread_ids:
110*0e90ac9cSMed Ismail Bennani                    log(f"Removing old thread {hex(thread_id)}")
111*0e90ac9cSMed Ismail Bennani                    del self.threads[thread_id]
112*0e90ac9cSMed Ismail Bennani
113*0e90ac9cSMed Ismail Bennani            print(f"New thread count: {len(self.threads)}")
114*0e90ac9cSMed Ismail Bennani
1156cf66801SMed Ismail Bennani            mux_process = self.target.GetProcess()
1166cf66801SMed Ismail Bennani            mux_process.ForceScriptedState(lldb.eStateRunning)
1176cf66801SMed Ismail Bennani            mux_process.ForceScriptedState(lldb.eStateStopped)
1186cf66801SMed Ismail Bennani
1196cf66801SMed Ismail Bennani            for child_process in self.multiplexed_processes.values():
1206cf66801SMed Ismail Bennani                child_process.ForceScriptedState(lldb.eStateRunning)
1216cf66801SMed Ismail Bennani                child_process.ForceScriptedState(lldb.eStateStopped)
1226cf66801SMed Ismail Bennani
1236cf66801SMed Ismail Bennani        event = lldb.SBEvent()
1246cf66801SMed Ismail Bennani        while True:
125*0e90ac9cSMed Ismail Bennani            if not self.driving_process:
126*0e90ac9cSMed Ismail Bennani                continue
1276cf66801SMed Ismail Bennani            if self.listener.WaitForEvent(1, event):
1286cf66801SMed Ismail Bennani                event_mask = event.GetType()
1296cf66801SMed Ismail Bennani                if event_mask & lldb.SBProcess.eBroadcastBitStateChanged:
1306cf66801SMed Ismail Bennani                    state = lldb.SBProcess.GetStateFromEvent(event)
1316cf66801SMed Ismail Bennani                    log(f"Received public process state event: {state}")
1326cf66801SMed Ismail Bennani                    if state == lldb.eStateStopped:
1336cf66801SMed Ismail Bennani                        # If it's a stop event, iterate over the driving process
1346cf66801SMed Ismail Bennani                        # thread, looking for a breakpoint stop reason, if internal
1356cf66801SMed Ismail Bennani                        # continue.
1366cf66801SMed Ismail Bennani                        handle_process_state_event()
1376cf66801SMed Ismail Bennani            else:
1386cf66801SMed Ismail Bennani                continue
1396cf66801SMed Ismail Bennani
1406cf66801SMed Ismail Bennani    def __init__(self, exe_ctx: lldb.SBExecutionContext, args: lldb.SBStructuredData):
1416cf66801SMed Ismail Bennani        super().__init__(exe_ctx, args, launched_driving_process=False)
1426cf66801SMed Ismail Bennani        if isinstance(self.driving_target, lldb.SBTarget) and self.driving_target:
1436cf66801SMed Ismail Bennani            self.listener = lldb.SBListener(
1446cf66801SMed Ismail Bennani                "lldb.listener.multiplexer-scripted-process"
1456cf66801SMed Ismail Bennani            )
1466cf66801SMed Ismail Bennani            self.multiplexed_processes = {}
1476cf66801SMed Ismail Bennani
148e31d0c20SMed Ismail Bennani            # Copy breakpoints from real target to passthrough
149e31d0c20SMed Ismail Bennani            with tempfile.NamedTemporaryFile() as tf:
150e31d0c20SMed Ismail Bennani                bkpt_file = lldb.SBFileSpec(tf.name)
151e31d0c20SMed Ismail Bennani                error = self.driving_target.BreakpointsWriteToFile(bkpt_file)
152e31d0c20SMed Ismail Bennani                if error.Fail():
153e31d0c20SMed Ismail Bennani                    log(
154e31d0c20SMed Ismail Bennani                        "Failed to save breakpoints from driving target (%s)"
155e31d0c20SMed Ismail Bennani                        % error.GetCString()
156e31d0c20SMed Ismail Bennani                    )
157e31d0c20SMed Ismail Bennani                bkpts_list = lldb.SBBreakpointList(self.target)
158e31d0c20SMed Ismail Bennani                error = self.target.BreakpointsCreateFromFile(bkpt_file, bkpts_list)
159e31d0c20SMed Ismail Bennani                if error.Fail():
160e31d0c20SMed Ismail Bennani                    log(
161e31d0c20SMed Ismail Bennani                        "Failed create breakpoints from driving target \
162e31d0c20SMed Ismail Bennani                        (bkpt file: %s)"
163e31d0c20SMed Ismail Bennani                        % tf.name
164e31d0c20SMed Ismail Bennani                    )
165e31d0c20SMed Ismail Bennani
166e31d0c20SMed Ismail Bennani            # Copy breakpoint from passthrough to real target
167e31d0c20SMed Ismail Bennani            if error.Success():
168e31d0c20SMed Ismail Bennani                self.driving_target.DeleteAllBreakpoints()
169e31d0c20SMed Ismail Bennani                for bkpt in self.target.breakpoints:
170e31d0c20SMed Ismail Bennani                    if bkpt.IsValid():
171e31d0c20SMed Ismail Bennani                        for bl in bkpt:
172e31d0c20SMed Ismail Bennani                            real_bpkt = self.driving_target.BreakpointCreateBySBAddress(
173e31d0c20SMed Ismail Bennani                                bl.GetAddress()
174e31d0c20SMed Ismail Bennani                            )
175e31d0c20SMed Ismail Bennani                            if not real_bpkt.IsValid():
176e31d0c20SMed Ismail Bennani                                log(
177e31d0c20SMed Ismail Bennani                                    "Failed to set breakpoint at address %s in \
178e31d0c20SMed Ismail Bennani                                    driving target"
179e31d0c20SMed Ismail Bennani                                    % hex(bl.GetLoadAddress())
180e31d0c20SMed Ismail Bennani                                )
181e31d0c20SMed Ismail Bennani
1826cf66801SMed Ismail Bennani            self.listener_thread = Thread(
1836cf66801SMed Ismail Bennani                target=self.wait_for_driving_process_to_stop, daemon=True
1846cf66801SMed Ismail Bennani            )
1856cf66801SMed Ismail Bennani            self.listener_thread.start()
1866cf66801SMed Ismail Bennani
1876cf66801SMed Ismail Bennani    def launch(self, should_stop: bool = True) -> lldb.SBError:
1886cf66801SMed Ismail Bennani        if not self.driving_target:
1896cf66801SMed Ismail Bennani            return lldb.SBError(
1906cf66801SMed Ismail Bennani                f"{self.__class__.__name__}.resume: Invalid driving target."
1916cf66801SMed Ismail Bennani            )
1926cf66801SMed Ismail Bennani
1936cf66801SMed Ismail Bennani        if self.driving_process:
1946cf66801SMed Ismail Bennani            return lldb.SBError(
1956cf66801SMed Ismail Bennani                f"{self.__class__.__name__}.resume: Invalid driving process."
1966cf66801SMed Ismail Bennani            )
1976cf66801SMed Ismail Bennani
1986cf66801SMed Ismail Bennani        error = lldb.SBError()
1996cf66801SMed Ismail Bennani        launch_info = lldb.SBLaunchInfo(None)
2006cf66801SMed Ismail Bennani        launch_info.SetListener(self.listener)
2016cf66801SMed Ismail Bennani        driving_process = self.driving_target.Launch(launch_info, error)
2026cf66801SMed Ismail Bennani
2036cf66801SMed Ismail Bennani        if not driving_process or error.Fail():
2046cf66801SMed Ismail Bennani            return error
2056cf66801SMed Ismail Bennani
2066cf66801SMed Ismail Bennani        self.driving_process = driving_process
2076cf66801SMed Ismail Bennani
2086cf66801SMed Ismail Bennani        for module in self.driving_target.modules:
2096cf66801SMed Ismail Bennani            path = module.file.fullpath
2106cf66801SMed Ismail Bennani            load_addr = module.GetObjectFileHeaderAddress().GetLoadAddress(
2116cf66801SMed Ismail Bennani                self.driving_target
2126cf66801SMed Ismail Bennani            )
2136cf66801SMed Ismail Bennani            self.loaded_images.append({"path": path, "load_addr": load_addr})
2146cf66801SMed Ismail Bennani
2156cf66801SMed Ismail Bennani        self.first_resume = True
2166cf66801SMed Ismail Bennani        return error
2176cf66801SMed Ismail Bennani
2186cf66801SMed Ismail Bennani    def resume(self, should_stop: bool = True) -> lldb.SBError:
2196cf66801SMed Ismail Bennani        if self.first_resume:
2206cf66801SMed Ismail Bennani            # When we resume the multiplexer process for the first time,
2216cf66801SMed Ismail Bennani            # we shouldn't do anything because lldb's execution machinery
2226cf66801SMed Ismail Bennani            # will resume the driving process by itself.
2236cf66801SMed Ismail Bennani
2246cf66801SMed Ismail Bennani            # Also, no need to update the multiplexer scripted process state
2256cf66801SMed Ismail Bennani            # here because since it's listening for the real process stop events.
2266cf66801SMed Ismail Bennani            # Once it receives the stop event from the driving process,
2276cf66801SMed Ismail Bennani            # `wait_for_driving_process_to_stop` will update the multiplexer
2286cf66801SMed Ismail Bennani            # state for us.
2296cf66801SMed Ismail Bennani
2306cf66801SMed Ismail Bennani            self.first_resume = False
2316cf66801SMed Ismail Bennani            return lldb.SBError()
2326cf66801SMed Ismail Bennani
2336cf66801SMed Ismail Bennani        if not self.driving_process:
2346cf66801SMed Ismail Bennani            return lldb.SBError(
2356cf66801SMed Ismail Bennani                f"{self.__class__.__name__}.resume: Invalid driving process."
2366cf66801SMed Ismail Bennani            )
2376cf66801SMed Ismail Bennani
2386cf66801SMed Ismail Bennani        return self.driving_process.Continue()
2396cf66801SMed Ismail Bennani
2406cf66801SMed Ismail Bennani    def get_threads_info(self, pid: int = None) -> Dict[int, Any]:
2416cf66801SMed Ismail Bennani        if not pid:
2426cf66801SMed Ismail Bennani            return super().get_threads_info()
2436cf66801SMed Ismail Bennani        parity = pid % 2
2446cf66801SMed Ismail Bennani        return dict(filter(lambda pair: pair[0] % 2 == parity, self.threads.items()))
2456cf66801SMed Ismail Bennani
246e31d0c20SMed Ismail Bennani    def create_breakpoint(self, addr, error, pid=None):
247e31d0c20SMed Ismail Bennani        if not self.driving_target:
248e31d0c20SMed Ismail Bennani            error.SetErrorString("%s has no driving target." % self.__class__.__name__)
249e31d0c20SMed Ismail Bennani            return False
250e31d0c20SMed Ismail Bennani
251e31d0c20SMed Ismail Bennani        def create_breakpoint_with_name(target, load_addr, name, error):
252e31d0c20SMed Ismail Bennani            addr = lldb.SBAddress(load_addr, target)
253e31d0c20SMed Ismail Bennani            if not addr.IsValid():
254e31d0c20SMed Ismail Bennani                error.SetErrorString("Invalid breakpoint address %s" % hex(load_addr))
255e31d0c20SMed Ismail Bennani                return False
256e31d0c20SMed Ismail Bennani            bkpt = target.BreakpointCreateBySBAddress(addr)
257e31d0c20SMed Ismail Bennani            if not bkpt.IsValid():
258e31d0c20SMed Ismail Bennani                error.SetErrorString(
259e31d0c20SMed Ismail Bennani                    "Failed to create breakpoint at address %s"
260e31d0c20SMed Ismail Bennani                    % hex(addr.GetLoadAddress())
261e31d0c20SMed Ismail Bennani                )
262e31d0c20SMed Ismail Bennani                return False
263e31d0c20SMed Ismail Bennani            error = bkpt.AddNameWithErrorHandling(name)
264e31d0c20SMed Ismail Bennani            return error.Success()
265e31d0c20SMed Ismail Bennani
266e31d0c20SMed Ismail Bennani        name = (
267e31d0c20SMed Ismail Bennani            "multiplexer_scripted_process"
268e31d0c20SMed Ismail Bennani            if not pid
269e31d0c20SMed Ismail Bennani            else f"multiplexed_scripted_process_{pid}"
270e31d0c20SMed Ismail Bennani        )
271e31d0c20SMed Ismail Bennani
272e31d0c20SMed Ismail Bennani        if pid is not None:
273e31d0c20SMed Ismail Bennani            # This means that this method has been called from one of the
274e31d0c20SMed Ismail Bennani            # multiplexed scripted process. That also means that the multiplexer
275e31d0c20SMed Ismail Bennani            # target doesn't have this breakpoint created.
276e31d0c20SMed Ismail Bennani            mux_error = lldb.SBError()
277e31d0c20SMed Ismail Bennani            bkpt = create_breakpoint_with_name(self.target, addr, name, mux_error)
278e31d0c20SMed Ismail Bennani            if mux_error.Fail():
279e31d0c20SMed Ismail Bennani                error.SetError(
280e31d0c20SMed Ismail Bennani                    "Failed to create breakpoint in multiplexer \
281e31d0c20SMed Ismail Bennani                               target: %s"
282e31d0c20SMed Ismail Bennani                    % mux_error.GetCString()
283e31d0c20SMed Ismail Bennani                )
284e31d0c20SMed Ismail Bennani                return False
285e31d0c20SMed Ismail Bennani        return create_breakpoint_with_name(self.driving_target, addr, name, error)
286e31d0c20SMed Ismail Bennani
2876cf66801SMed Ismail Bennani
2886cf66801SMed Ismail Bennanidef multiplex(mux_process, muxed_process):
2896cf66801SMed Ismail Bennani    muxed_process.GetScriptedImplementation().multiplexer = (
2906cf66801SMed Ismail Bennani        mux_process.GetScriptedImplementation()
2916cf66801SMed Ismail Bennani    )
2926cf66801SMed Ismail Bennani    mux_process.GetScriptedImplementation().multiplexed_processes[
2936cf66801SMed Ismail Bennani        muxed_process.GetProcessID()
2946cf66801SMed Ismail Bennani    ] = muxed_process
2956cf66801SMed Ismail Bennani
2966cf66801SMed Ismail Bennani
2976cf66801SMed Ismail Bennanidef launch_scripted_process(target, class_name, dictionary):
2986cf66801SMed Ismail Bennani    structured_data = lldb.SBStructuredData()
2996cf66801SMed Ismail Bennani    structured_data.SetFromJSON(json.dumps(dictionary))
3006cf66801SMed Ismail Bennani
3016cf66801SMed Ismail Bennani    launch_info = lldb.SBLaunchInfo(None)
3026cf66801SMed Ismail Bennani    launch_info.SetProcessPluginName("ScriptedProcess")
3036cf66801SMed Ismail Bennani    launch_info.SetScriptedProcessClassName(class_name)
3046cf66801SMed Ismail Bennani    launch_info.SetScriptedProcessDictionary(structured_data)
3056cf66801SMed Ismail Bennani
3066cf66801SMed Ismail Bennani    error = lldb.SBError()
3076cf66801SMed Ismail Bennani    return target.Launch(launch_info, error)
3086cf66801SMed Ismail Bennani
3096cf66801SMed Ismail Bennani
3106cf66801SMed Ismail Bennanidef duplicate_target(driving_target):
3116cf66801SMed Ismail Bennani    error = lldb.SBError()
3126cf66801SMed Ismail Bennani    exe = driving_target.executable.fullpath
3136cf66801SMed Ismail Bennani    triple = driving_target.triple
3146cf66801SMed Ismail Bennani    debugger = driving_target.GetDebugger()
3156cf66801SMed Ismail Bennani    return debugger.CreateTargetWithFileAndTargetTriple(exe, triple)
3166cf66801SMed Ismail Bennani
317429e7483SMed Ismail Bennani
3186cf66801SMed Ismail Bennanidef create_mux_process(debugger, command, exe_ctx, result, dict):
3196cf66801SMed Ismail Bennani    if not debugger.GetNumTargets() > 0:
3206cf66801SMed Ismail Bennani        return result.SetError(
3216cf66801SMed Ismail Bennani            "Interactive scripted processes requires one non scripted process."
3226cf66801SMed Ismail Bennani        )
3236cf66801SMed Ismail Bennani
3246cf66801SMed Ismail Bennani    debugger.SetAsync(True)
3256cf66801SMed Ismail Bennani
3266cf66801SMed Ismail Bennani    driving_target = debugger.GetSelectedTarget()
3276cf66801SMed Ismail Bennani    if not driving_target:
3286cf66801SMed Ismail Bennani        return result.SetError("Driving target is invalid")
3296cf66801SMed Ismail Bennani
3306cf66801SMed Ismail Bennani    # Create a seconde target for the multiplexer scripted process
3316cf66801SMed Ismail Bennani    mux_target = duplicate_target(driving_target)
3326cf66801SMed Ismail Bennani    if not mux_target:
3336cf66801SMed Ismail Bennani        return result.SetError(
3346cf66801SMed Ismail Bennani            "Couldn't duplicate driving target to launch multiplexer scripted process"
3356cf66801SMed Ismail Bennani        )
3366cf66801SMed Ismail Bennani
3376cf66801SMed Ismail Bennani    class_name = f"{__name__}.{MultiplexerScriptedProcess.__name__}"
3386cf66801SMed Ismail Bennani    dictionary = {"driving_target_idx": debugger.GetIndexOfTarget(driving_target)}
3396cf66801SMed Ismail Bennani    mux_process = launch_scripted_process(mux_target, class_name, dictionary)
3406cf66801SMed Ismail Bennani    if not mux_process:
3416cf66801SMed Ismail Bennani        return result.SetError("Couldn't launch multiplexer scripted process")
3426cf66801SMed Ismail Bennani
3436cf66801SMed Ismail Bennani
3446cf66801SMed Ismail Bennanidef create_child_processes(debugger, command, exe_ctx, result, dict):
3456cf66801SMed Ismail Bennani    if not debugger.GetNumTargets() >= 2:
3466cf66801SMed Ismail Bennani        return result.SetError("Scripted Multiplexer process not setup")
3476cf66801SMed Ismail Bennani
3486cf66801SMed Ismail Bennani    debugger.SetAsync(True)
3496cf66801SMed Ismail Bennani
3506cf66801SMed Ismail Bennani    # Create a seconde target for the multiplexer scripted process
3516cf66801SMed Ismail Bennani    mux_target = debugger.GetSelectedTarget()
3526cf66801SMed Ismail Bennani    if not mux_target:
3536cf66801SMed Ismail Bennani        return result.SetError("Couldn't get multiplexer scripted process target")
3546cf66801SMed Ismail Bennani    mux_process = mux_target.GetProcess()
3556cf66801SMed Ismail Bennani    if not mux_process:
3566cf66801SMed Ismail Bennani        return result.SetError("Couldn't get multiplexer scripted process")
3576cf66801SMed Ismail Bennani
3586cf66801SMed Ismail Bennani    driving_target = mux_process.GetScriptedImplementation().driving_target
3596cf66801SMed Ismail Bennani    if not driving_target:
3606cf66801SMed Ismail Bennani        return result.SetError("Driving target is invalid")
3616cf66801SMed Ismail Bennani
3626cf66801SMed Ismail Bennani    # Create a target for the multiplexed even scripted process
3636cf66801SMed Ismail Bennani    even_target = duplicate_target(driving_target)
3646cf66801SMed Ismail Bennani    if not even_target:
3656cf66801SMed Ismail Bennani        return result.SetError(
3666cf66801SMed Ismail Bennani            "Couldn't duplicate driving target to launch multiplexed even scripted process"
3676cf66801SMed Ismail Bennani        )
3686cf66801SMed Ismail Bennani
3696cf66801SMed Ismail Bennani    class_name = f"{__name__}.{MultiplexedScriptedProcess.__name__}"
3706cf66801SMed Ismail Bennani    dictionary = {"driving_target_idx": debugger.GetIndexOfTarget(mux_target)}
3716cf66801SMed Ismail Bennani    dictionary["parity"] = 0
3726cf66801SMed Ismail Bennani    even_process = launch_scripted_process(even_target, class_name, dictionary)
3736cf66801SMed Ismail Bennani    if not even_process:
3746cf66801SMed Ismail Bennani        return result.SetError("Couldn't launch multiplexed even scripted process")
3756cf66801SMed Ismail Bennani    multiplex(mux_process, even_process)
3766cf66801SMed Ismail Bennani
3776cf66801SMed Ismail Bennani    # Create a target for the multiplexed odd scripted process
3786cf66801SMed Ismail Bennani    odd_target = duplicate_target(driving_target)
3796cf66801SMed Ismail Bennani    if not odd_target:
3806cf66801SMed Ismail Bennani        return result.SetError(
3816cf66801SMed Ismail Bennani            "Couldn't duplicate driving target to launch multiplexed odd scripted process"
3826cf66801SMed Ismail Bennani        )
3836cf66801SMed Ismail Bennani
3846cf66801SMed Ismail Bennani    dictionary["parity"] = 1
3856cf66801SMed Ismail Bennani    odd_process = launch_scripted_process(odd_target, class_name, dictionary)
3866cf66801SMed Ismail Bennani    if not odd_process:
3876cf66801SMed Ismail Bennani        return result.SetError("Couldn't launch multiplexed odd scripted process")
3886cf66801SMed Ismail Bennani    multiplex(mux_process, odd_process)
3896cf66801SMed Ismail Bennani
3906cf66801SMed Ismail Bennani
3916cf66801SMed Ismail Bennanidef log(message):
3926cf66801SMed Ismail Bennani    # FIXME: For now, we discard the log message until we can pass it to an lldb
3936cf66801SMed Ismail Bennani    # logging channel.
3946cf66801SMed Ismail Bennani    should_log = False
3956cf66801SMed Ismail Bennani    if should_log:
3966cf66801SMed Ismail Bennani        print(message)
3976cf66801SMed Ismail Bennani
3986cf66801SMed Ismail Bennani
3996cf66801SMed Ismail Bennanidef __lldb_init_module(dbg, dict):
4006cf66801SMed Ismail Bennani    dbg.HandleCommand(
4016cf66801SMed Ismail Bennani        "command script add -o -f interactive_scripted_process.create_mux_process create_mux"
4026cf66801SMed Ismail Bennani    )
4036cf66801SMed Ismail Bennani    dbg.HandleCommand(
4046cf66801SMed Ismail Bennani        "command script add -o -f interactive_scripted_process.create_child_processes create_sub"
4056cf66801SMed Ismail Bennani    )
406