|
12 | 12 |
|
13 | 13 | #include "DebugServer2/GDBRemote/Mixins/ProcessLaunchMixin.h"
|
14 | 14 |
|
| 15 | +#include <algorithm> |
| 16 | + |
15 | 17 | namespace ds2 {
|
16 | 18 | namespace GDBRemote {
|
17 | 19 |
|
@@ -46,8 +48,8 @@ ProcessLaunchMixin<T>::onQueryWorkingDirectory(Session &,
|
46 | 48 | }
|
47 | 49 |
|
48 | 50 | template <typename T>
|
49 |
| -ErrorCode ProcessLaunchMixin<T>::onSetEnvironmentVariable( |
50 |
| - Session &, std::string const &name, std::string const &value) { |
| 51 | +ErrorCode ProcessLaunchMixin<T>::onSetEnvironmentVariable(Session &, |
| 52 | + std::string const &name, std::string const &value) { |
51 | 53 | if (value.empty()) {
|
52 | 54 | _environment.erase(name);
|
53 | 55 | } else {
|
@@ -88,15 +90,129 @@ ErrorCode
|
88 | 90 | ProcessLaunchMixin<T>::onSetProgramArguments(Session &,
|
89 | 91 | StringCollection const &args) {
|
90 | 92 | _arguments = args;
|
91 |
| - for (auto const &arg : _arguments) { |
92 |
| - DS2LOG(Debug, "arg=%s", arg.c_str()); |
| 93 | + if (isDebugServer(args)) { |
| 94 | + // Propagate current logging settings when launching an instance of |
| 95 | + // the debug server to match qLaunchGDBServer behavior. |
| 96 | + if (GetLogLevel() == kLogLevelDebug) |
| 97 | + _arguments.push_back("--debug"); |
| 98 | + else if (GetLogLevel() == kLogLevelPacket) |
| 99 | + _arguments.push_back("--remote-debug"); |
93 | 100 | }
|
94 |
| - return kSuccess; |
| 101 | + |
| 102 | + return spawnProcess(); |
| 103 | + |
95 | 104 | }
|
96 | 105 |
|
97 | 106 | template <typename T>
|
98 | 107 | ErrorCode ProcessLaunchMixin<T>::onQueryLaunchSuccess(Session &,
|
99 | 108 | ProcessId) const {
|
| 109 | + return _lastLaunchResult; |
| 110 | +} |
| 111 | + |
| 112 | +template <typename T> |
| 113 | +ErrorCode |
| 114 | +ProcessLaunchMixin<T>::onQueryProcessInfo(Session &, |
| 115 | + ProcessInfo &info) const { |
| 116 | + if (_processList.empty()) |
| 117 | + return kErrorProcessNotFound; |
| 118 | + |
| 119 | + const ProcessId pid = _processList.back(); |
| 120 | + if (!Platform::GetProcessInfo(pid, info)) |
| 121 | + return kErrorUnknown; |
| 122 | + |
| 123 | + return kSuccess; |
| 124 | +} |
| 125 | + |
| 126 | +template <typename T> |
| 127 | +ErrorCode ProcessLaunchMixin<T>::onTerminate(Session &session, |
| 128 | + ProcessThreadId const &ptid, |
| 129 | + StopInfo &stop) { |
| 130 | + auto it = std::find(_processList.begin(), _processList.end(), ptid.pid); |
| 131 | + if (it == _processList.end()) |
| 132 | + return kErrorNotFound; |
| 133 | + |
| 134 | + if (!Platform::TerminateProcess(ptid.pid)) |
| 135 | + return kErrorUnknown; |
| 136 | + |
| 137 | + DS2LOG(Debug, "killed spawned process %" PRI_PID, *it); |
| 138 | + _processList.erase(it); |
| 139 | + |
| 140 | + // StopInfo is not populated by this implemenation of onTerminate since it is |
| 141 | + // only ever called in the context of a platform session in response to a |
| 142 | + // qKillSpawnedProcess packet. |
| 143 | + stop.clear(); |
| 144 | + return kSuccess; |
| 145 | +} |
| 146 | + |
| 147 | +// Some test cases use this code path to launch additional instances of the |
| 148 | +// debug server rather than sending a qLaunchGDBServer packet. Allow detection |
| 149 | +// of this scenario so we can propagate logging settings and make debugging |
| 150 | +// test failures easier. |
| 151 | +template <typename T> |
| 152 | +bool ProcessLaunchMixin<T>::isDebugServer(StringCollection const &args) { |
| 153 | + return (args.size() > 1) |
| 154 | + && (args[0] == Platform::GetSelfExecutablePath()) |
| 155 | + && (args[1] == "gdbserver"); |
| 156 | +} |
| 157 | + |
| 158 | +template <typename T> |
| 159 | +ErrorCode ProcessLaunchMixin<T>::spawnProcess() { |
| 160 | + const bool displayArgs = _arguments.size() > 1; |
| 161 | + const bool displayEnv = !_environment.empty(); |
| 162 | + auto it = _arguments.begin(); |
| 163 | + DS2LOG(Debug, "spawning process '%s'%s", (it++)->c_str(), |
| 164 | + displayArgs ? " with args:" : ""); |
| 165 | + while (it != _arguments.end()) { |
| 166 | + DS2LOG(Debug, " %s", (it++)->c_str()); |
| 167 | + } |
| 168 | + |
| 169 | + if (displayEnv) { |
| 170 | + DS2LOG(Debug, "%swith environment:", displayArgs ? "and " : ""); |
| 171 | + for (auto const &val : _environment) { |
| 172 | + DS2LOG(Debug, " %s=%s", val.first.c_str(), val.second.c_str()); |
| 173 | + } |
| 174 | + } |
| 175 | + |
| 176 | + if (!_workingDirectory.empty()) { |
| 177 | + DS2LOG(Debug, "%swith working directory: %s", |
| 178 | + displayArgs || displayEnv ? "and " : "", |
| 179 | + _workingDirectory.c_str()); |
| 180 | + } |
| 181 | + |
| 182 | + Host::ProcessSpawner ps; |
| 183 | + if (!ps.setExecutable(_arguments[0]) || |
| 184 | + !ps.setArguments(StringCollection(_arguments.begin() + 1, |
| 185 | + _arguments.end())) || |
| 186 | + !ps.setWorkingDirectory(_workingDirectory) || |
| 187 | + !ps.setEnvironment(_environment)) |
| 188 | + return kErrorInvalidArgument; |
| 189 | + |
| 190 | + if (isDebugServer(_arguments)) { |
| 191 | + // Always log to the console when launching an instance of the debug server |
| 192 | + // to match qLaunchGDBServer behavior. |
| 193 | + ps.redirectInputToNull(); |
| 194 | + ps.redirectOutputToConsole(); |
| 195 | + ps.redirectErrorToConsole(); |
| 196 | + } else { |
| 197 | + if (!_stdFile[0].empty() && !ps.redirectInputToFile(_stdFile[0])) |
| 198 | + return kErrorInvalidArgument; |
| 199 | + if (!_stdFile[1].empty() && !ps.redirectOutputToFile(_stdFile[1])) |
| 200 | + return kErrorInvalidArgument; |
| 201 | + if (!_stdFile[2].empty() && !ps.redirectErrorToFile(_stdFile[2])) |
| 202 | + return kErrorInvalidArgument; |
| 203 | + } |
| 204 | + |
| 205 | + _lastLaunchResult = ps.run(); |
| 206 | + if (_lastLaunchResult != kSuccess) |
| 207 | + return _lastLaunchResult; |
| 208 | + |
| 209 | + // Add the pid to the list of launched processes. It will be removed when |
| 210 | + // onTerminate is called. |
| 211 | + _processList.push_back(ps.pid()); |
| 212 | + |
| 213 | + DS2LOG(Debug, "launched %s as process %" PRI_PID, _arguments[0].c_str(), |
| 214 | + ps.pid()); |
| 215 | + |
100 | 216 | return kSuccess;
|
101 | 217 | }
|
102 | 218 | } // namespace GDBRemote
|
|
0 commit comments