Coverage Report

Created: 2023-09-30 09:22

/Users/buildslave/jenkins/workspace/coverage/llvm-project/lldb/source/Host/common/ProcessLaunchInfo.cpp
Line
Count
Source (jump to first uncovered line)
1
//===-- ProcessLaunchInfo.cpp ---------------------------------------------===//
2
//
3
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4
// See https://llvm.org/LICENSE.txt for license information.
5
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6
//
7
//===----------------------------------------------------------------------===//
8
9
#include <climits>
10
11
#include "lldb/Host/Config.h"
12
#include "lldb/Host/FileAction.h"
13
#include "lldb/Host/FileSystem.h"
14
#include "lldb/Host/HostInfo.h"
15
#include "lldb/Host/ProcessLaunchInfo.h"
16
#include "lldb/Utility/LLDBLog.h"
17
#include "lldb/Utility/Log.h"
18
#include "lldb/Utility/StreamString.h"
19
20
#include "llvm/Support/ConvertUTF.h"
21
#include "llvm/Support/FileSystem.h"
22
23
#if !defined(_WIN32)
24
#include <climits>
25
#endif
26
27
using namespace lldb;
28
using namespace lldb_private;
29
30
// ProcessLaunchInfo member functions
31
32
ProcessLaunchInfo::ProcessLaunchInfo()
33
30.5k
    : ProcessInfo(), m_working_dir(), m_plugin_name(), m_flags(0),
34
30.5k
      m_file_actions(), m_pty(new PseudoTerminal), m_monitor_callback(nullptr) {
35
30.5k
}
36
37
ProcessLaunchInfo::ProcessLaunchInfo(const FileSpec &stdin_file_spec,
38
                                     const FileSpec &stdout_file_spec,
39
                                     const FileSpec &stderr_file_spec,
40
                                     const FileSpec &working_directory,
41
                                     uint32_t launch_flags)
42
18
    : ProcessInfo(), m_working_dir(), m_plugin_name(), m_flags(launch_flags),
43
18
      m_file_actions(), m_pty(new PseudoTerminal) {
44
18
  if (stdin_file_spec) {
45
1
    FileAction file_action;
46
1
    const bool read = true;
47
1
    const bool write = false;
48
1
    if (file_action.Open(STDIN_FILENO, stdin_file_spec, read, write))
49
1
      AppendFileAction(file_action);
50
1
  }
51
18
  if (stdout_file_spec) {
52
2
    FileAction file_action;
53
2
    const bool read = false;
54
2
    const bool write = true;
55
2
    if (file_action.Open(STDOUT_FILENO, stdout_file_spec, read, write))
56
2
      AppendFileAction(file_action);
57
2
  }
58
18
  if (stderr_file_spec) {
59
1
    FileAction file_action;
60
1
    const bool read = false;
61
1
    const bool write = true;
62
1
    if (file_action.Open(STDERR_FILENO, stderr_file_spec, read, write))
63
1
      AppendFileAction(file_action);
64
1
  }
65
18
  if (working_directory)
66
5
    SetWorkingDirectory(working_directory);
67
18
}
68
69
6.36k
bool ProcessLaunchInfo::AppendCloseFileAction(int fd) {
70
6.36k
  FileAction file_action;
71
6.36k
  if (file_action.Close(fd)) {
72
6.36k
    AppendFileAction(file_action);
73
6.36k
    return true;
74
6.36k
  }
75
0
  return false;
76
6.36k
}
77
78
3.58k
bool ProcessLaunchInfo::AppendDuplicateFileAction(int fd, int dup_fd) {
79
3.58k
  FileAction file_action;
80
3.58k
  if (file_action.Duplicate(fd, dup_fd)) {
81
3.58k
    AppendFileAction(file_action);
82
3.58k
    return true;
83
3.58k
  }
84
0
  return false;
85
3.58k
}
86
87
bool ProcessLaunchInfo::AppendOpenFileAction(int fd, const FileSpec &file_spec,
88
35.1k
                                             bool read, bool write) {
89
35.1k
  FileAction file_action;
90
35.1k
  if (file_action.Open(fd, file_spec, read, write)) {
91
8.32k
    AppendFileAction(file_action);
92
8.32k
    return true;
93
8.32k
  }
94
26.8k
  return false;
95
35.1k
}
96
97
bool ProcessLaunchInfo::AppendSuppressFileAction(int fd, bool read,
98
9.09k
                                                 bool write) {
99
9.09k
  FileAction file_action;
100
9.09k
  if (file_action.Open(fd, FileSpec(FileSystem::DEV_NULL), read, write)) {
101
9.09k
    AppendFileAction(file_action);
102
9.09k
    return true;
103
9.09k
  }
104
0
  return false;
105
9.09k
}
106
107
27.4k
const FileAction *ProcessLaunchInfo::GetFileActionAtIndex(size_t idx) const {
108
27.4k
  if (idx < m_file_actions.size())
109
27.4k
    return &m_file_actions[idx];
110
0
  return nullptr;
111
27.4k
}
112
113
14.8k
const FileAction *ProcessLaunchInfo::GetFileActionForFD(int fd) const {
114
15.4k
  for (size_t idx = 0, count = m_file_actions.size(); idx < count; 
++idx597
) {
115
876
    if (m_file_actions[idx].GetFD() == fd)
116
279
      return &m_file_actions[idx];
117
876
  }
118
14.5k
  return nullptr;
119
14.8k
}
120
121
6.96k
const FileSpec &ProcessLaunchInfo::GetWorkingDirectory() const {
122
6.96k
  return m_working_dir;
123
6.96k
}
124
125
1.95k
void ProcessLaunchInfo::SetWorkingDirectory(const FileSpec &working_dir) {
126
1.95k
  m_working_dir = working_dir;
127
1.95k
}
128
129
2.10k
llvm::StringRef ProcessLaunchInfo::GetProcessPluginName() const {
130
2.10k
  return llvm::StringRef(m_plugin_name);
131
2.10k
}
132
133
5
void ProcessLaunchInfo::SetProcessPluginName(llvm::StringRef plugin) {
134
5
  m_plugin_name = std::string(plugin);
135
5
}
136
137
6.32k
const FileSpec &ProcessLaunchInfo::GetShell() const { return m_shell; }
138
139
689
void ProcessLaunchInfo::SetShell(const FileSpec &shell) {
140
689
  m_shell = shell;
141
689
  if (m_shell) {
142
689
    FileSystem::Instance().ResolveExecutableLocation(m_shell);
143
689
    m_flags.Set(lldb::eLaunchFlagLaunchInShell);
144
689
  } else
145
0
    m_flags.Clear(lldb::eLaunchFlagLaunchInShell);
146
689
}
147
148
4.23k
void ProcessLaunchInfo::SetLaunchInSeparateProcessGroup(bool separate) {
149
4.23k
  if (separate)
150
4.23k
    m_flags.Set(lldb::eLaunchFlagLaunchInSeparateProcessGroup);
151
0
  else
152
0
    m_flags.Clear(lldb::eLaunchFlagLaunchInSeparateProcessGroup);
153
4.23k
}
154
155
631
void ProcessLaunchInfo::SetShellExpandArguments(bool expand) {
156
631
  if (expand)
157
631
    m_flags.Set(lldb::eLaunchFlagShellExpandArguments);
158
0
  else
159
0
    m_flags.Clear(lldb::eLaunchFlagShellExpandArguments);
160
631
}
161
162
25.1k
void ProcessLaunchInfo::Clear() {
163
25.1k
  ProcessInfo::Clear();
164
25.1k
  m_working_dir.Clear();
165
25.1k
  m_plugin_name.clear();
166
25.1k
  m_shell.Clear();
167
25.1k
  m_flags.Clear();
168
25.1k
  m_file_actions.clear();
169
25.1k
  m_resume_count = 0;
170
25.1k
  m_listener_sp.reset();
171
25.1k
  m_hijack_listener_sp.reset();
172
25.1k
}
173
174
void ProcessLaunchInfo::NoOpMonitorCallback(lldb::pid_t pid, int signal,
175
2.10k
                                            int status) {
176
2.10k
  Log *log = GetLog(LLDBLog::Process);
177
2.10k
  LLDB_LOG(log, "pid = {0}, signal = {1}, status = {2}", pid, signal, status);
178
2.10k
}
179
180
6.32k
bool ProcessLaunchInfo::MonitorProcess() const {
181
6.32k
  if (m_monitor_callback && ProcessIDIsValid()) {
182
6.32k
    llvm::Expected<HostThread> maybe_thread =
183
6.32k
        Host::StartMonitoringChildProcess(m_monitor_callback, GetProcessID());
184
6.32k
    if (!maybe_thread)
185
0
      LLDB_LOG_ERROR(GetLog(LLDBLog::Host), maybe_thread.takeError(),
186
6.32k
                     "failed to launch host thread: {0}");
187
6.32k
    return true;
188
6.32k
  }
189
0
  return false;
190
6.32k
}
191
192
0
void ProcessLaunchInfo::SetDetachOnError(bool enable) {
193
0
  if (enable)
194
0
    m_flags.Set(lldb::eLaunchFlagDetachOnError);
195
0
  else
196
0
    m_flags.Clear(lldb::eLaunchFlagDetachOnError);
197
0
}
198
199
2.11k
llvm::Error ProcessLaunchInfo::SetUpPtyRedirection() {
200
2.11k
  Log *log = GetLog(LLDBLog::Process);
201
202
2.11k
  bool stdin_free = GetFileActionForFD(STDIN_FILENO) == nullptr;
203
2.11k
  bool stdout_free = GetFileActionForFD(STDOUT_FILENO) == nullptr;
204
2.11k
  bool stderr_free = GetFileActionForFD(STDERR_FILENO) == nullptr;
205
2.11k
  bool any_free = stdin_free || 
stdout_free10
||
stderr_free9
;
206
2.11k
  if (!any_free)
207
9
    return llvm::Error::success();
208
209
2.10k
  LLDB_LOG(log, "Generating a pty to use for stdin/out/err");
210
211
2.10k
  int open_flags = O_RDWR | O_NOCTTY;
212
2.10k
#if !defined(_WIN32)
213
  // We really shouldn't be specifying platform specific flags that are
214
  // intended for a system call in generic code.  But this will have to
215
  // do for now.
216
2.10k
  open_flags |= O_CLOEXEC;
217
2.10k
#endif
218
2.10k
  if (llvm::Error Err = m_pty->OpenFirstAvailablePrimary(open_flags))
219
0
    return Err;
220
221
2.10k
  const FileSpec secondary_file_spec(m_pty->GetSecondaryName());
222
223
2.10k
  if (stdin_free)
224
2.10k
    AppendOpenFileAction(STDIN_FILENO, secondary_file_spec, true, false);
225
226
2.10k
  if (stdout_free)
227
2.00k
    AppendOpenFileAction(STDOUT_FILENO, secondary_file_spec, false, true);
228
229
2.10k
  if (stderr_free)
230
2.10k
    AppendOpenFileAction(STDERR_FILENO, secondary_file_spec, false, true);
231
2.10k
  return llvm::Error::success();
232
2.10k
}
233
234
bool ProcessLaunchInfo::ConvertArgumentsForLaunchingInShell(
235
    Status &error, bool will_debug, bool first_arg_is_full_shell_command,
236
689
    uint32_t num_resumes) {
237
689
  error.Clear();
238
239
689
  if (GetFlags().Test(eLaunchFlagLaunchInShell)) {
240
689
    if (m_shell) {
241
689
      std::string shell_executable = m_shell.GetPath();
242
243
689
      const char **argv = GetArguments().GetConstArgumentVector();
244
689
      if (argv == nullptr || argv[0] == nullptr)
245
0
        return false;
246
689
      Args shell_arguments;
247
689
      shell_arguments.AppendArgument(shell_executable);
248
689
      const llvm::Triple &triple = GetArchitecture().GetTriple();
249
689
      if (triple.getOS() == llvm::Triple::Win32 &&
250
689
          
!triple.isWindowsCygwinEnvironment()0
)
251
0
        shell_arguments.AppendArgument(llvm::StringRef("/C"));
252
689
      else
253
689
        shell_arguments.AppendArgument(llvm::StringRef("-c"));
254
255
689
      StreamString shell_command;
256
689
      if (will_debug) {
257
        // Add a modified PATH environment variable in case argv[0] is a
258
        // relative path.
259
0
        const char *argv0 = argv[0];
260
0
        FileSpec arg_spec(argv0);
261
0
        if (arg_spec.IsRelative()) {
262
          // We have a relative path to our executable which may not work if we
263
          // just try to run "a.out" (without it being converted to "./a.out")
264
0
          FileSpec working_dir = GetWorkingDirectory();
265
          // Be sure to put quotes around PATH's value in case any paths have
266
          // spaces...
267
0
          std::string new_path("PATH=\"");
268
0
          const size_t empty_path_len = new_path.size();
269
270
0
          if (working_dir) {
271
0
            new_path += working_dir.GetPath();
272
0
          } else {
273
0
            llvm::SmallString<64> cwd;
274
0
            if (! llvm::sys::fs::current_path(cwd))
275
0
              new_path += cwd;
276
0
          }
277
0
          std::string curr_path;
278
0
          if (HostInfo::GetEnvironmentVar("PATH", curr_path)) {
279
0
            if (new_path.size() > empty_path_len)
280
0
              new_path += ':';
281
0
            new_path += curr_path;
282
0
          }
283
0
          new_path += "\" ";
284
0
          shell_command.PutCString(new_path);
285
0
        }
286
287
0
        if (triple.getOS() != llvm::Triple::Win32 ||
288
0
            triple.isWindowsCygwinEnvironment())
289
0
          shell_command.PutCString("exec");
290
291
        // Only Apple supports /usr/bin/arch being able to specify the
292
        // architecture
293
0
        if (GetArchitecture().IsValid() && // Valid architecture
294
0
            GetArchitecture().GetTriple().getVendor() ==
295
0
                llvm::Triple::Apple && // Apple only
296
0
            GetArchitecture().GetCore() !=
297
0
                ArchSpec::eCore_x86_64_x86_64h) // Don't do this for x86_64h
298
0
        {
299
0
          shell_command.Printf(" /usr/bin/arch -arch %s",
300
0
                               GetArchitecture().GetArchitectureName());
301
          // Set the resume count to 2:
302
          // 1 - stop in shell
303
          // 2 - stop in /usr/bin/arch
304
          // 3 - then we will stop in our program
305
0
          SetResumeCount(num_resumes + 1);
306
0
        } else {
307
          // Set the resume count to 1:
308
          // 1 - stop in shell
309
          // 2 - then we will stop in our program
310
0
          SetResumeCount(num_resumes);
311
0
        }
312
0
      }
313
314
689
      if (first_arg_is_full_shell_command) {
315
        // There should only be one argument that is the shell command itself
316
        // to be used as is
317
0
        if (argv[0] && !argv[1])
318
0
          shell_command.Printf("%s", argv[0]);
319
0
        else
320
0
          return false;
321
689
      } else {
322
2.09k
        for (size_t i = 0; argv[i] != nullptr; 
++i1.40k
) {
323
1.40k
          std::string safe_arg = Args::GetShellSafeArgument(m_shell, argv[i]);
324
1.40k
          if (safe_arg.empty())
325
1
            safe_arg = "\"\"";
326
          // Add a space to separate this arg from the previous one.
327
1.40k
          shell_command.PutCString(" ");
328
1.40k
          shell_command.PutCString(safe_arg);
329
1.40k
        }
330
689
      }
331
689
      shell_arguments.AppendArgument(shell_command.GetString());
332
689
      m_executable = m_shell;
333
689
      m_arguments = shell_arguments;
334
689
      return true;
335
689
    } else {
336
0
      error.SetErrorString("invalid shell path");
337
0
    }
338
689
  } else {
339
0
    error.SetErrorString("not launching in shell");
340
0
  }
341
0
  return false;
342
689
}