<div dir="ltr">Apologies in advance for the long read, tried to write this up as clearly as possible.  At a minimum I'd like to complete the HostProcess abstraction and the LaunchProcess signature change, but I think the part that follows (which I've listed as optional) is a net gain for the codebase and improves the maintainability and clarity of all of this code.<div><br></div><div><br></div><div><span id="docs-internal-guid-9c0f6166-a437-6826-400e-29c4a7412013"><p dir="ltr" style="line-height:1.15;margin-top:0pt;margin-bottom:0pt"><span style="font-family:Arial;color:rgb(0,0,0);vertical-align:baseline;white-space:pre-wrap;background-color:transparent">This will discuss a plan for refactoring process management and launching code in the Host layer into a set of reusable abstractions.  This is part of a continued effort to refactor Host.cpp in a way that lends itself to better re-usability, better platform-separation (code specific to platform X is isolated to a file specific to platform X), and a stronger barrier between generic code and platform specific code.  One of the biggest difficulties in my efforts to port LLDB to Windows so far has been locating and fixing places in LLDB where an operation was assumed to be generic (either logically or through the use of a particular API) when it actually was not.  By re-thinking the Host layer from the bottom up with Windows in mind, we can arrive at a set of abstractions that is truly generic enough to be used across all platforms, while still making it possible to use platform specific features from other platform specific code.</span></p><br><p dir="ltr" style="line-height:1.15;margin-top:0pt;margin-bottom:0pt"><span style="font-family:Arial;color:rgb(0,0,0);font-weight:bold;vertical-align:baseline;white-space:pre-wrap;background-color:transparent">The HostProcess abstraction</span></p><br><p dir="ltr" style="line-height:1.15;margin-top:0pt;margin-bottom:0pt"><span style="font-family:Arial;color:rgb(0,0,0);vertical-align:baseline;white-space:pre-wrap;background-color:transparent">It's probably easiest to start with the core abstraction itself, and then discuss the other supporting changes needed to arrive at that.  At the core of this refactor is the |HostProcess| abstraction.  This is a replacement for what is currently an lldb::process_t, and serves as an abstraction that allows one to manipulate a process running on the system.  In order to create a strong barrier between generic code and platform specific code, |HostProcess| will use a facade pattern similar to that used by |HostThread|, whereby |HostProcess| provides only methods which are generic enough to work on every platform, and wraps a |HostNativeProcessBase| for which there might be many derived implementations, each implementing the generic options for the particular platform, as well as potentially offering any relevant platform specific operations so that other platform specific code can take full advantage of the platform.</span></p><br><p dir="ltr" style="line-height:1.15;margin-top:0pt;margin-bottom:0pt"><span style="font-family:Arial;color:rgb(0,0,0);vertical-align:baseline;white-space:pre-wrap;background-color:transparent">A generic interface for process will provide at a minimum the following methods:</span></p><br><ul style="margin-top:0pt;margin-bottom:0pt"><li dir="ltr" style="list-style-type:disc;font-family:Arial;color:rgb(0,0,0);vertical-align:baseline;background-color:transparent"><p dir="ltr" style="line-height:1.15;margin-top:0pt;margin-bottom:0pt"><span style="vertical-align:baseline;white-space:pre-wrap;background-color:transparent">Terminate</span></p></li><li dir="ltr" style="list-style-type:disc;font-family:Arial;color:rgb(0,0,0);vertical-align:baseline;background-color:transparent"><p dir="ltr" style="line-height:1.15;margin-top:0pt;margin-bottom:0pt"><span style="vertical-align:baseline;white-space:pre-wrap;background-color:transparent">GetMainModule</span></p></li><li dir="ltr" style="list-style-type:disc;font-family:Arial;color:rgb(0,0,0);vertical-align:baseline;background-color:transparent"><p dir="ltr" style="line-height:1.15;margin-top:0pt;margin-bottom:0pt"><span style="vertical-align:baseline;white-space:pre-wrap;background-color:transparent">GetProcessId</span></p></li><li dir="ltr" style="list-style-type:disc;font-family:Arial;color:rgb(0,0,0);vertical-align:baseline;background-color:transparent"><p dir="ltr" style="line-height:1.15;margin-top:0pt;margin-bottom:0pt"><span style="vertical-align:baseline;white-space:pre-wrap;background-color:transparent">IsRunning</span></p></li></ul><br><p dir="ltr" style="line-height:1.15;margin-top:0pt;margin-bottom:0pt"><span style="font-family:Arial;color:rgb(0,0,0);vertical-align:baseline;white-space:pre-wrap;background-color:transparent">Additionally, there may be use for methods to inspect the process while running, such as methods like the following:</span></p><br><ul style="margin-top:0pt;margin-bottom:0pt"><li dir="ltr" style="list-style-type:disc;font-family:Arial;color:rgb(0,0,0);vertical-align:baseline;background-color:transparent"><p dir="ltr" style="line-height:1.15;margin-top:0pt;margin-bottom:0pt"><span style="vertical-align:baseline;white-space:pre-wrap;background-color:transparent">ReadProcessMemory</span></p></li><li dir="ltr" style="list-style-type:disc;font-family:Arial;color:rgb(0,0,0);vertical-align:baseline;background-color:transparent"><p dir="ltr" style="line-height:1.15;margin-top:0pt;margin-bottom:0pt"><span style="vertical-align:baseline;white-space:pre-wrap;background-color:transparent">WriteProcessMemory</span></p></li></ul><br><p dir="ltr" style="line-height:1.15;margin-top:0pt;margin-bottom:0pt"><span style="font-family:Arial;color:rgb(0,0,0);vertical-align:baseline;white-space:pre-wrap;background-color:transparent">Additional platform-specific methods will be exposed through the corresponding subclasses of |HostNativeProcessBase|.  The most obvious example of this is a Signal method that will be exposed through |HostProcessPosix|, although the proposed design provides the flexibility for this set of platform-specific methods to be arbitrary.</span></p><br><br><p dir="ltr" style="line-height:1.15;margin-top:0pt;margin-bottom:0pt"><span style="font-family:Arial;color:rgb(0,0,0);font-weight:bold;vertical-align:baseline;white-space:pre-wrap;background-color:transparent">Launching Processes</span></p><p dir="ltr" style="line-height:1.15;margin-top:0pt;margin-bottom:0pt"><span style="font-family:Arial;color:rgb(0,0,0);vertical-align:baseline;white-space:pre-wrap;background-color:transparent">Currently the ability to launch a process is exposed through a single method Host::LaunchProcess which takes a |ProcessLaunchInfo| argument.  Upon completion, LaunchProcess writes the pid of the newly created process into the |ProcessLaunchInfo|.  Because of this, the ProcessLaunchInfo structure is required to be passed in as a non-const reference so it can be modified.  To address this, the launch method will be modified to return a HostProcess.  The PID will not be written back into the |ProcessLaunchInfo| (and if possible the pid field will ideally be removed from |ProcessLaunchInfo| entirely), allowing it to be passed const.</span></p><br><p dir="ltr" style="line-height:1.15;margin-top:0pt;margin-bottom:0pt"><span style="font-family:Arial;color:rgb(0,0,0);font-weight:bold;font-style:italic;vertical-align:baseline;white-space:pre-wrap;background-color:transparent">Optional code re-org related to process launching</span></p><p dir="ltr" style="line-height:1.15;margin-top:0pt;margin-bottom:0pt"><span style="font-family:Arial;color:rgb(0,0,0);vertical-align:baseline;white-space:pre-wrap;background-color:transparent">The process launching code is relatively confusing and difficult to maintain.  Because the code is not modular and not written to be re-usable at a granular level, some hackery exists to make the correct methods be found by the linker on various platforms.  The following sections describes a possible code re-organization that makes everything more modular and re-usable, and makes the code easier to understand and maintain as a result.</span></p><br><p dir="ltr" style="line-height:1.15;margin-top:0pt;margin-bottom:0pt"><span style="font-family:Arial;color:rgb(0,0,0);vertical-align:baseline;white-space:pre-wrap;background-color:transparent">The basis of this code re-organization is the creation of a |ProcessLauncher| interface will be created.  Instead of using Host::LaunchProcess, anyone wishing to launch a process will instantiate some subclass of |ProcessLauncher|, and then invoke the Launch method on this interface, which will return a HostProcess.  For those just wishing to get the default behavior on a particular platform (i.e. get the behavior equivalent to calling Host::LaunchProcess now), |DefaultProcessLauncher| will be typedef'ed to the most sensible process launcher on each platform.  This allows existing code to be written by simply instantiating a |DefaultProcessLauncher| and using it.  The interface of |ProcessLauncher| will provide the following methods:</span></p><br><ul style="margin-top:0pt;margin-bottom:0pt"><li dir="ltr" style="list-style-type:disc;font-family:Arial;color:rgb(0,0,0);vertical-align:baseline;background-color:transparent"><p dir="ltr" style="line-height:1.15;margin-top:0pt;margin-bottom:0pt"><span style="vertical-align:baseline;white-space:pre-wrap;background-color:transparent">LaunchProcess</span></p></li><li dir="ltr" style="list-style-type:disc;font-family:Arial;color:rgb(0,0,0);vertical-align:baseline;background-color:transparent"><p dir="ltr" style="line-height:1.15;margin-top:0pt;margin-bottom:0pt"><span style="vertical-align:baseline;white-space:pre-wrap;background-color:transparent">AddExitCallback</span></p></li></ul><br><p dir="ltr" style="line-height:1.15;margin-top:0pt;margin-bottom:0pt"><span style="font-family:Arial;color:rgb(0,0,0);vertical-align:baseline;white-space:pre-wrap;background-color:transparent">The second of these two methods, AddExitCallback, replaces and improves upon the existing mechanism in |ProcessLaunchInfo| which allows the user to specify an exit callback.  The improvement comes from the fact that multiple exit callbacks can be specified.</span></p><br><p dir="ltr" style="line-height:1.15;margin-top:0pt;margin-bottom:0pt"><span style="font-family:Arial;color:rgb(0,0,0);vertical-align:baseline;white-space:pre-wrap;background-color:transparent">This approach yields a number of benefits.  For starters, it allows us to abstract out the notion of the launching strategy into a reusable piece of code so that platforms which have multiple launching strategies have the ability to make use of specific strategies in other platform specific code, or so that remote process launches can be treated the same as local process launches.  As an example, on MacOSX there are three different launching strategies:  Launch with XPC services, launch with Applescript, and launch with posix_spawn.  Each of these could be independent implementations of |ProcessLauncher|, with the default implementation on MacOSX being a </span><span style="font-family:Arial;color:rgb(0,0,0);font-style:italic;vertical-align:baseline;white-space:pre-wrap;background-color:transparent">fourth</span><span style="font-family:Arial;color:rgb(0,0,0);vertical-align:baseline;white-space:pre-wrap;background-color:transparent"> implementation that chooses one of the first 3 implementations to delegate to at runtime.  As a result, the 3 implementations can be used independently of each other should the need arise.</span></p><br><p dir="ltr" style="line-height:1.15;margin-top:0pt;margin-bottom:0pt"><span style="font-family:Arial;color:rgb(0,0,0);vertical-align:baseline;white-space:pre-wrap;background-color:transparent">A second benefit is that it opens up the possibility of treating remote process launch transparently with local process launch -- just have a |RemoteProcessLauncher| class.</span></p><br><p dir="ltr" style="line-height:1.15;margin-top:0pt;margin-bottom:0pt"><span style="font-family:Arial;color:rgb(0,0,0);vertical-align:baseline;white-space:pre-wrap;background-color:transparent">There are additional benefits in the form of overall code health, platform separation, and just generally improving the modularity of the codebase, although those are perhaps less tangible.</span></p><br><p dir="ltr" style="line-height:1.15;margin-top:0pt;margin-bottom:0pt"><span style="font-family:Arial;color:rgb(0,0,0);font-weight:bold;vertical-align:baseline;white-space:pre-wrap;background-color:transparent">Monitoring for process exit</span></p><p dir="ltr" style="line-height:1.15;margin-top:0pt;margin-bottom:0pt"><span style="font-family:Arial;color:rgb(0,0,0);vertical-align:baseline;white-space:pre-wrap;background-color:transparent">It is important to know when a process exits, for 2 reasons.  First, on some platforms the process needs to be reaped.  Second, asynchronous exit callbacks need to be invoked.  The implementation of the monitoring is platform dependent, and a strategy for each platform can be summarized as follows:</span></p><br><ul style="margin-top:0pt;margin-bottom:0pt"><li dir="ltr" style="list-style-type:disc;font-family:Arial;color:rgb(0,0,0);vertical-align:baseline;background-color:transparent"><p dir="ltr" style="line-height:1.15;margin-top:0pt;margin-bottom:0pt"><span style="vertical-align:baseline;white-space:pre-wrap;background-color:transparent">Windows</span></p></li><ol style="margin-top:0pt;margin-bottom:0pt"><li dir="ltr" style="list-style-type:lower-alpha;font-family:Arial;color:rgb(0,0,0);vertical-align:baseline;background-color:transparent"><p dir="ltr" style="line-height:1.15;margin-top:0pt;margin-bottom:0pt"><span style="vertical-align:baseline;white-space:pre-wrap;background-color:transparent">Create a background thread, use WaitForSingleObject</span></p></li><li dir="ltr" style="list-style-type:lower-alpha;font-family:Arial;color:rgb(0,0,0);vertical-align:baseline;background-color:transparent"><p dir="ltr" style="line-height:1.15;margin-top:0pt;margin-bottom:0pt"><span style="vertical-align:baseline;white-space:pre-wrap;background-color:transparent">[Better, alternative implementation] Create one "static" background thread for all processes, use WaitForMultipleObjects() to monitor many processes at once, like select().</span></p></li></ol><li dir="ltr" style="list-style-type:disc;font-family:Arial;color:rgb(0,0,0);vertical-align:baseline;background-color:transparent"><p dir="ltr" style="line-height:1.15;margin-top:0pt;margin-bottom:0pt"><span style="vertical-align:baseline;white-space:pre-wrap;background-color:transparent">MacOSX</span></p></li><ol style="margin-top:0pt;margin-bottom:0pt"><li dir="ltr" style="list-style-type:lower-alpha;font-family:Arial;color:rgb(0,0,0);vertical-align:baseline;background-color:transparent"><p dir="ltr" style="line-height:1.15;margin-top:0pt;margin-bottom:0pt"><span style="vertical-align:baseline;white-space:pre-wrap;background-color:transparent">Processes spawned with posix_spawn - Use MacOSX "dispatch" library to queue monitoring code on a system thread.</span></p></li><li dir="ltr" style="list-style-type:lower-alpha;font-family:Arial;color:rgb(0,0,0);vertical-align:baseline;background-color:transparent"><p dir="ltr" style="line-height:1.15;margin-top:0pt;margin-bottom:0pt"><span style="vertical-align:baseline;white-space:pre-wrap;background-color:transparent">Processes spawned with XPC - Use MacOSX "dispatch" library to queue monitoring code on a system thread.</span></p></li><li dir="ltr" style="list-style-type:lower-alpha;font-family:Arial;color:rgb(0,0,0);vertical-align:baseline;background-color:transparent"><p dir="ltr" style="line-height:1.15;margin-top:0pt;margin-bottom:0pt"><span style="vertical-align:baseline;white-space:pre-wrap;background-color:transparent">Processes spawned with Applescript - No monitoring necessary</span></p></li></ol><li dir="ltr" style="list-style-type:disc;font-family:Arial;color:rgb(0,0,0);vertical-align:baseline;background-color:transparent"><p dir="ltr" style="line-height:1.15;margin-top:0pt;margin-bottom:0pt"><span style="vertical-align:baseline;white-space:pre-wrap;background-color:transparent">Non-MacOSX posix systems</span></p></li><ol style="margin-top:0pt;margin-bottom:0pt"><li dir="ltr" style="list-style-type:lower-alpha;font-family:Arial;color:rgb(0,0,0);vertical-align:baseline;background-color:transparent"><p dir="ltr" style="line-height:1.15;margin-top:0pt;margin-bottom:0pt"><span style="vertical-align:baseline;white-space:pre-wrap;background-color:transparent">Processes spawned with posix_spawn - Create a background thread, use waitpid in the background thread.</span></p></li></ol></ul><p dir="ltr" style="line-height:1.15;margin-top:0pt;margin-bottom:0pt"><span style="font-family:Arial;color:rgb(0,0,0);vertical-align:baseline;white-space:pre-wrap;background-color:transparent">  </span></p><p dir="ltr" style="line-height:1.15;margin-top:0pt;margin-bottom:0pt"><span style="font-family:Arial;color:rgb(0,0,0);vertical-align:baseline;white-space:pre-wrap;background-color:transparent">To handle this, a |ProcessExitMonitor| abstraction will be created, with implementations for each of the above strategies.  Individual implementations of |ProcessLauncher| will provide a method CreateProcessMonitor() which returns an instance to the correct monitor type, and the resulting |ProcessExitMonitor| will be stored in the |HostProcess|.</span></p><br><p dir="ltr" style="line-height:1.15;margin-top:0pt;margin-bottom:0pt"><span style="font-family:Arial;color:rgb(0,0,0);vertical-align:baseline;white-space:pre-wrap;background-color:transparent">It’s possible to see some of the benefit in code re-organization from the above breakdown of implementations.  All posix-based systems can sometimes use a posix_spawn based strategy for launching processes, but MacOSX </span><span style="font-family:Arial;color:rgb(0,0,0);font-style:italic;vertical-align:baseline;white-space:pre-wrap;background-color:transparent">might </span><span style="font-family:Arial;color:rgb(0,0,0);vertical-align:baseline;white-space:pre-wrap;background-color:transparent">use other strategies depending on the situation.  Even when MacOSX uses posix_spawn though, it still uses a different exit monitoring implementation.  This set of modular abstractions handles this nicely.  |ProcessLauncherPosixSpawn| goes in host/posix, and takes a |ProcessExitMonitor| to its constructor.  If MacOSX decides at runtime to launch using posix_spawn, it passes a |DispatchExitMonitor|, which lives in host/macosx to the constructor.  All code always lives at the most generic level where it makes sense for everyone, and there’s no code pollution or pre-processor hackery to get certain functions to be visible.</span></p><br><p dir="ltr" style="line-height:1.15;margin-top:0pt;margin-bottom:0pt"><span style="font-family:Arial;color:rgb(0,0,0);vertical-align:baseline;white-space:pre-wrap;background-color:transparent">Since the |HostProcess| can go out of scope while the process is still running on the system, it is important that the lifetime of the |ProcessExitMonitor| be tied to the lifetime of the process running on the system, and not to the lifetime of the |HostProcess| object inside of LLDB.  This can be achieved by having |HostProcess| store an std::weak_ptr<ProcessExitMonitor>, and having the background thread own the |ProcessExitMonitor| through an std::shared_ptr<ProcessExitMonitor>.  This way, even if LLDB decides it no longer needs the |HostProcess|, if the process is still running in the background it will get reaped and callbacks fired correctly.</span></p><br><p dir="ltr" style="line-height:1.15;margin-top:0pt;margin-bottom:0pt"><span style="font-family:Arial;color:rgb(0,0,0);font-weight:bold;vertical-align:baseline;white-space:pre-wrap;background-color:transparent">Getting it done</span></p><p dir="ltr" style="line-height:1.15;margin-top:0pt;margin-bottom:0pt"><span style="font-family:Arial;color:rgb(0,0,0);vertical-align:baseline;white-space:pre-wrap;background-color:transparent">In an effort to keep the CLs manageable, this work will be split into multiple changes, in the order described below:</span></p><ol style="margin-top:0pt;margin-bottom:0pt"><li dir="ltr" style="list-style-type:decimal;font-family:Arial;color:rgb(0,0,0);vertical-align:baseline;background-color:transparent"><p dir="ltr" style="line-height:1.15;margin-top:0pt;margin-bottom:0pt"><span style="vertical-align:baseline;white-space:pre-wrap;background-color:transparent">Create the HostProcess abstraction, implement it for all platforms.  Do not update any code to use HostProcess yet.</span></p></li><li dir="ltr" style="list-style-type:decimal;font-family:Arial;color:rgb(0,0,0);vertical-align:baseline;background-color:transparent"><p dir="ltr" style="line-height:1.15;margin-top:0pt;margin-bottom:0pt"><span style="vertical-align:baseline;white-space:pre-wrap;background-color:transparent">Modify the signature of Host::LaunchProcess to return a HostProcess, and remove the pid from ProcessLaunchInfo.  Update calling code accordingly.</span></p></li><li dir="ltr" style="list-style-type:decimal;font-family:Arial;color:rgb(0,0,0);vertical-align:baseline;background-color:transparent"><p dir="ltr" style="line-height:1.15;margin-top:0pt;margin-bottom:0pt"><span style="vertical-align:baseline;white-space:pre-wrap;background-color:transparent">Create the various implementations of ProcessExitMonitor, update Host::LaunchProcess to use the appropriate ProcessExitMonitor instead of calling StartMonitoringChildProcess.</span></p></li><li dir="ltr" style="list-style-type:decimal;font-family:Arial;color:rgb(0,0,0);vertical-align:baseline;background-color:transparent"><span style="vertical-align:baseline;white-space:pre-wrap;background-color:transparent">Create the various implementations of ProcessLauncher, update calling code to use them instead of Host::LaunchProcess, delete Host::LaunchProcess.</span></li></ol></span></div></div>