LLDB mainline
windows/Host.cpp
Go to the documentation of this file.
1//===-- source/Host/windows/Host.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
11#include <cstdio>
12
14#include "lldb/Host/Host.h"
15#include "lldb/Host/HostInfo.h"
19#include "lldb/Utility/Log.h"
21#include "lldb/Utility/Status.h"
24
25#include "llvm/Support/ConvertUTF.h"
26
27// Windows includes
28#include <tlhelp32.h>
29
30using namespace lldb;
31using namespace lldb_private;
32
33static bool GetTripleForProcess(const FileSpec &executable,
34 llvm::Triple &triple) {
35 // Open the PE File as a binary file, and parse just enough information to
36 // determine the machine type.
37 auto imageBinaryP = FileSystem::Instance().Open(
38 executable, File::eOpenOptionReadOnly, lldb::eFilePermissionsUserRead);
39 if (!imageBinaryP)
40 return llvm::errorToBool(imageBinaryP.takeError());
41 File &imageBinary = *imageBinaryP.get();
42 imageBinary.SeekFromStart(0x3c);
43 int32_t peOffset = 0;
44 uint32_t peHead = 0;
45 uint16_t machineType = 0;
46 size_t readSize = sizeof(peOffset);
47 imageBinary.Read(&peOffset, readSize);
48 imageBinary.SeekFromStart(peOffset);
49 imageBinary.Read(&peHead, readSize);
50 if (peHead != 0x00004550) // "PE\0\0", little-endian
51 return false; // Status: Can't find PE header
52 readSize = 2;
53 imageBinary.Read(&machineType, readSize);
54 triple.setVendor(llvm::Triple::PC);
55 triple.setOS(llvm::Triple::Win32);
56 triple.setArch(llvm::Triple::UnknownArch);
57 if (machineType == 0x8664)
58 triple.setArch(llvm::Triple::x86_64);
59 else if (machineType == 0x14c)
60 triple.setArch(llvm::Triple::x86);
61 else if (machineType == 0x1c4)
62 triple.setArch(llvm::Triple::arm);
63 else if (machineType == 0xaa64)
64 triple.setArch(llvm::Triple::aarch64);
65
66 return true;
67}
68
69static bool GetExecutableForProcess(const AutoHandle &handle,
70 std::string &path) {
71 // Get the process image path. MAX_PATH isn't long enough, paths can
72 // actually be up to 32KB.
73 std::vector<wchar_t> buffer(PATH_MAX);
74 DWORD dwSize = buffer.size();
75 if (!::QueryFullProcessImageNameW(handle.get(), 0, &buffer[0], &dwSize))
76 return false;
77 return llvm::convertWideToUTF8(buffer.data(), path);
78}
79
80static void GetProcessExecutableAndTriple(const AutoHandle &handle,
81 ProcessInstanceInfo &process) {
82 // We may not have permissions to read the path from the process. So start
83 // off by setting the executable file to whatever Toolhelp32 gives us, and
84 // then try to enhance this with more detailed information, but fail
85 // gracefully.
86 std::string executable;
87 llvm::Triple triple;
88 triple.setVendor(llvm::Triple::PC);
89 triple.setOS(llvm::Triple::Win32);
90 triple.setArch(llvm::Triple::UnknownArch);
91 if (GetExecutableForProcess(handle, executable)) {
92 FileSpec executableFile(executable.c_str());
93 process.SetExecutableFile(executableFile, true);
94 GetTripleForProcess(executableFile, triple);
95 }
96 process.SetArchitecture(ArchSpec(triple));
97
98 // TODO(zturner): Add the ability to get the process user name.
99}
100
103}
104
105void Host::Kill(lldb::pid_t pid, int signo) {
106 AutoHandle handle(::OpenProcess(PROCESS_TERMINATE, FALSE, pid), nullptr);
107 if (handle.IsValid())
108 ::TerminateProcess(handle.get(), 1);
109}
110
111const char *Host::GetSignalAsCString(int signo) { return NULL; }
112
113FileSpec Host::GetModuleFileSpecForHostAddress(const void *host_addr) {
114 FileSpec module_filespec;
115
116 HMODULE hmodule = NULL;
117 if (!::GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
118 (LPCTSTR)host_addr, &hmodule))
119 return module_filespec;
120
121 std::vector<wchar_t> buffer(PATH_MAX);
122 DWORD chars_copied = 0;
123 do {
124 chars_copied = ::GetModuleFileNameW(hmodule, &buffer[0], buffer.size());
125 if (chars_copied == buffer.size() &&
126 ::GetLastError() == ERROR_INSUFFICIENT_BUFFER)
127 buffer.resize(buffer.size() * 2);
128 } while (chars_copied >= buffer.size());
129 std::string path;
130 if (!llvm::convertWideToUTF8(buffer.data(), path))
131 return module_filespec;
132 module_filespec.SetFile(path, FileSpec::Style::native);
133 return module_filespec;
134}
135
136uint32_t Host::FindProcessesImpl(const ProcessInstanceInfoMatch &match_info,
137 ProcessInstanceInfoList &process_infos) {
138 process_infos.clear();
139
140 AutoHandle snapshot(CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0));
141 if (!snapshot.IsValid())
142 return 0;
143
144 PROCESSENTRY32W pe = {};
145 pe.dwSize = sizeof(PROCESSENTRY32W);
146 if (Process32FirstW(snapshot.get(), &pe)) {
147 do {
148 AutoHandle handle(::OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE,
149 pe.th32ProcessID),
150 nullptr);
151
152 ProcessInstanceInfo process;
153 std::string exeFile;
154 llvm::convertWideToUTF8(pe.szExeFile, exeFile);
155 process.SetExecutableFile(FileSpec(exeFile), true);
156 process.SetProcessID(pe.th32ProcessID);
157 process.SetParentProcessID(pe.th32ParentProcessID);
158 GetProcessExecutableAndTriple(handle, process);
159
160 if (match_info.MatchAllProcesses() || match_info.Matches(process))
161 process_infos.push_back(process);
162 } while (Process32NextW(snapshot.get(), &pe));
163 }
164 return process_infos.size();
165}
166
168 process_info.Clear();
169
170 AutoHandle handle(
171 ::OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid),
172 nullptr);
173 if (!handle.IsValid())
174 return false;
175
176 process_info.SetProcessID(pid);
177 GetProcessExecutableAndTriple(handle, process_info);
178
179 // Need to read the PEB to get parent process and command line arguments.
180
181 AutoHandle snapshot(CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0));
182 if (!snapshot.IsValid())
183 return false;
184
185 PROCESSENTRY32W pe;
186 pe.dwSize = sizeof(PROCESSENTRY32W);
187 if (Process32FirstW(snapshot.get(), &pe)) {
188 do {
189 if (pe.th32ProcessID == pid) {
190 process_info.SetParentProcessID(pe.th32ParentProcessID);
191 return true;
192 }
193 } while (Process32NextW(snapshot.get(), &pe));
194 }
195
196 return false;
197}
198
199llvm::Expected<HostThread> Host::StartMonitoringChildProcess(
200 const Host::MonitorChildProcessCallback &callback, lldb::pid_t pid) {
201 return HostThread();
202}
203
206 if (launch_info.GetFlags().Test(eLaunchFlagShellExpandArguments)) {
207 FileSpec expand_tool_spec = HostInfo::GetSupportExeDir();
208 if (!expand_tool_spec) {
210 "could not find support executable directory for "
211 "the lldb-argdumper tool");
212 return error;
213 }
214 expand_tool_spec.AppendPathComponent("lldb-argdumper.exe");
215 if (!FileSystem::Instance().Exists(expand_tool_spec)) {
216 error = Status::FromErrorString("could not find the lldb-argdumper tool");
217 return error;
218 }
219
220 std::string quoted_cmd_string;
221 launch_info.GetArguments().GetQuotedCommandString(quoted_cmd_string);
222 std::replace(quoted_cmd_string.begin(), quoted_cmd_string.end(), '\\', '/');
223 StreamString expand_command;
224
225 expand_command.Printf("\"%s\" %s", expand_tool_spec.GetPath().c_str(),
226 quoted_cmd_string.c_str());
227
228 int status;
229 std::string output;
230 std::string command = expand_command.GetString().str();
231 Status e =
232 RunShellCommand(command.c_str(), launch_info.GetWorkingDirectory(),
233 &status, nullptr, &output, std::chrono::seconds(10));
234
235 if (e.Fail())
236 return e;
237
238 if (status != 0) {
240 "lldb-argdumper exited with error %d", status);
241 return error;
242 }
243
244 auto data_sp = StructuredData::ParseJSON(output);
245 if (!data_sp) {
246 error = Status::FromErrorString("invalid JSON");
247 return error;
248 }
249
250 auto dict_sp = data_sp->GetAsDictionary();
251 if (!dict_sp) {
252 error = Status::FromErrorString("invalid JSON");
253 return error;
254 }
255
256 auto args_sp = dict_sp->GetObjectForDotSeparatedPath("arguments");
257 if (!args_sp) {
258 error = Status::FromErrorString("invalid JSON");
259 return error;
260 }
261
262 auto args_array_sp = args_sp->GetAsArray();
263 if (!args_array_sp) {
264 error = Status::FromErrorString("invalid JSON");
265 return error;
266 }
267
268 launch_info.GetArguments().Clear();
269
270 for (size_t i = 0; i < args_array_sp->GetSize(); i++) {
271 auto item_sp = args_array_sp->GetItemAtIndex(i);
272 if (!item_sp)
273 continue;
274 auto str_sp = item_sp->GetAsString();
275 if (!str_sp)
276 continue;
277
278 launch_info.GetArguments().AppendArgument(str_sp->GetValue());
279 }
280 }
281
282 return error;
283}
284
286 Environment env;
287 // The environment block on Windows is a contiguous buffer of NULL terminated
288 // strings, where the end of the environment block is indicated by two
289 // consecutive NULLs.
290 LPWCH environment_block = ::GetEnvironmentStringsW();
291 while (*environment_block != L'\0') {
292 std::string current_var;
293 auto current_var_size = wcslen(environment_block) + 1;
294 if (!llvm::convertWideToUTF8(environment_block, current_var)) {
295 environment_block += current_var_size;
296 continue;
297 }
298 if (current_var[0] != '=')
299 env.insert(current_var);
300
301 environment_block += current_var_size;
302 }
303 return env;
304}
static llvm::raw_ostream & error(Stream &strm)
An architecture specification class.
Definition: ArchSpec.h:31
void AppendArgument(llvm::StringRef arg_str, char quote_char='\0')
Appends a new argument to the end of the list argument list.
Definition: Args.cpp:332
bool GetQuotedCommandString(std::string &command) const
Definition: Args.cpp:232
void Clear()
Clear the arguments.
Definition: Args.cpp:388
HANDLE get() const
Definition: AutoHandle.h:28
std::pair< iterator, bool > insert(llvm::StringRef KeyEqValue)
Definition: Environment.h:71
A file utility class.
Definition: FileSpec.h:56
void SetFile(llvm::StringRef path, Style style)
Change the file specified with a new path.
Definition: FileSpec.cpp:174
void AppendPathComponent(llvm::StringRef component)
Definition: FileSpec.cpp:447
size_t GetPath(char *path, size_t max_path_length, bool denormalize=true) const
Extract the full path to the file.
Definition: FileSpec.cpp:367
int Open(const char *path, int flags, int mode=0600)
Wraps ::open in a platform-independent way.
static FileSystem & Instance()
An abstract base class for files.
Definition: File.h:36
Status Read(void *buf, size_t &num_bytes) override
Read bytes from a file from the current file position into buf.
Definition: File.cpp:106
virtual off_t SeekFromStart(off_t offset, Status *error_ptr=nullptr)
Seek to an offset relative to the beginning of the file.
Definition: File.cpp:130
@ eOpenOptionReadOnly
Definition: File.h:51
bool Test(ValueType bit) const
Test a single flag bit.
Definition: Flags.h:96
static Status ShellExpandArguments(ProcessLaunchInfo &launch_info)
Perform expansion of the command-line for this launch info This can potentially involve wildcard expa...
static Status RunShellCommand(llvm::StringRef command, const FileSpec &working_dir, int *status_ptr, int *signo_ptr, std::string *command_output, const Timeout< std::micro > &timeout, bool run_in_shell=true, bool hide_stderr=false)
Run a shell command.
static lldb::thread_t GetCurrentThread()
Get the thread token (the one returned by ThreadCreate when the thread was created) for the calling t...
static Environment GetEnvironment()
static uint32_t FindProcessesImpl(const ProcessInstanceInfoMatch &match_info, ProcessInstanceInfoList &proc_infos)
static bool GetProcessInfo(lldb::pid_t pid, ProcessInstanceInfo &proc_info)
static FileSpec GetModuleFileSpecForHostAddress(const void *host_addr)
Given an address in the current process (the process that is running the LLDB code),...
std::function< void(lldb::pid_t pid, int signal, int status)> MonitorChildProcessCallback
Definition: Host.h:88
static llvm::Expected< HostThread > StartMonitoringChildProcess(const MonitorChildProcessCallback &callback, lldb::pid_t pid)
Start monitoring a child process.
static void Kill(lldb::pid_t pid, int signo)
static const char * GetSignalAsCString(int signo)
void SetExecutableFile(const FileSpec &exe_file, bool add_exe_file_as_first_arg)
Definition: ProcessInfo.cpp:65
void SetArchitecture(const ArchSpec &arch)
Definition: ProcessInfo.h:66
void SetProcessID(lldb::pid_t pid)
Definition: ProcessInfo.h:70
bool Matches(const ProcessInstanceInfo &proc_info) const
void SetParentProcessID(lldb::pid_t pid)
Definition: ProcessInfo.h:174
const FileSpec & GetWorkingDirectory() const
An error handling class.
Definition: Status.h:115
static Status FromErrorStringWithFormat(const char *format,...) __attribute__((format(printf
Definition: Status.cpp:106
static Status FromErrorString(const char *str)
Definition: Status.h:138
bool Fail() const
Test for error condition.
Definition: Status.cpp:270
llvm::StringRef GetString() const
size_t Printf(const char *format,...) __attribute__((format(printf
Output printf formatted output to the stream.
Definition: Stream.cpp:134
static ObjectSP ParseJSON(llvm::StringRef json_text)
A class that represents a running process on the host machine.
std::vector< ProcessInstanceInfo > ProcessInstanceInfoList
Definition: Host.h:32
Definition: SBAddress.h:15
pthread_t thread_t
Definition: lldb-types.h:58
uint64_t pid_t
Definition: lldb-types.h:83
static void GetProcessExecutableAndTriple(const AutoHandle &handle, ProcessInstanceInfo &process)
static bool GetTripleForProcess(const FileSpec &executable, llvm::Triple &triple)
static bool GetExecutableForProcess(const AutoHandle &handle, std::string &path)
#define PATH_MAX