|  | //===-- source/Host/windows/Host.cpp ----------------------------*- C++ -*-===// | 
|  | // | 
|  | //                     The LLVM Compiler Infrastructure | 
|  | // | 
|  | // This file is distributed under the University of Illinois Open Source | 
|  | // License. See LICENSE.TXT for details. | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  |  | 
|  | // C Includes | 
|  | #include "lldb/Host/windows/AutoHandle.h" | 
|  | #include "lldb/Host/windows/windows.h" | 
|  | #include <stdio.h> | 
|  |  | 
|  | // C++ Includes | 
|  | // Other libraries and framework includes | 
|  | // Project includes | 
|  | #include "lldb/Core/Error.h" | 
|  | #include "lldb/Core/Log.h" | 
|  | #include "lldb/Target/Process.h" | 
|  |  | 
|  | #include "lldb/Core/DataBufferHeap.h" | 
|  | #include "lldb/Core/DataExtractor.h" | 
|  | #include "lldb/Core/StreamFile.h" | 
|  | #include "lldb/Core/StructuredData.h" | 
|  | #include "lldb/Host/Host.h" | 
|  | #include "lldb/Host/HostInfo.h" | 
|  |  | 
|  | #include "llvm/Support/ConvertUTF.h" | 
|  |  | 
|  | // Windows includes | 
|  | #include <TlHelp32.h> | 
|  |  | 
|  | using namespace lldb; | 
|  | using namespace lldb_private; | 
|  |  | 
|  | namespace { | 
|  | bool GetTripleForProcess(const FileSpec &executable, llvm::Triple &triple) { | 
|  | // Open the PE File as a binary file, and parse just enough information to | 
|  | // determine the | 
|  | // machine type. | 
|  | File imageBinary(executable.GetPath().c_str(), File::eOpenOptionRead, | 
|  | lldb::eFilePermissionsUserRead); | 
|  | imageBinary.SeekFromStart(0x3c); | 
|  | int32_t peOffset = 0; | 
|  | uint32_t peHead = 0; | 
|  | uint16_t machineType = 0; | 
|  | size_t readSize = sizeof(peOffset); | 
|  | imageBinary.Read(&peOffset, readSize); | 
|  | imageBinary.SeekFromStart(peOffset); | 
|  | imageBinary.Read(&peHead, readSize); | 
|  | if (peHead != 0x00004550) // "PE\0\0", little-endian | 
|  | return false;           // Error: Can't find PE header | 
|  | readSize = 2; | 
|  | imageBinary.Read(&machineType, readSize); | 
|  | triple.setVendor(llvm::Triple::PC); | 
|  | triple.setOS(llvm::Triple::Win32); | 
|  | triple.setArch(llvm::Triple::UnknownArch); | 
|  | if (machineType == 0x8664) | 
|  | triple.setArch(llvm::Triple::x86_64); | 
|  | else if (machineType == 0x14c) | 
|  | triple.setArch(llvm::Triple::x86); | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool GetExecutableForProcess(const AutoHandle &handle, std::string &path) { | 
|  | // Get the process image path.  MAX_PATH isn't long enough, paths can actually | 
|  | // be up to 32KB. | 
|  | std::vector<wchar_t> buffer(PATH_MAX); | 
|  | DWORD dwSize = buffer.size(); | 
|  | if (!::QueryFullProcessImageNameW(handle.get(), 0, &buffer[0], &dwSize)) | 
|  | return false; | 
|  | return llvm::convertWideToUTF8(buffer.data(), path); | 
|  | } | 
|  |  | 
|  | void GetProcessExecutableAndTriple(const AutoHandle &handle, | 
|  | ProcessInstanceInfo &process) { | 
|  | // We may not have permissions to read the path from the process.  So start | 
|  | // off by | 
|  | // setting the executable file to whatever Toolhelp32 gives us, and then try | 
|  | // to | 
|  | // enhance this with more detailed information, but fail gracefully. | 
|  | std::string executable; | 
|  | llvm::Triple triple; | 
|  | triple.setVendor(llvm::Triple::PC); | 
|  | triple.setOS(llvm::Triple::Win32); | 
|  | triple.setArch(llvm::Triple::UnknownArch); | 
|  | if (GetExecutableForProcess(handle, executable)) { | 
|  | FileSpec executableFile(executable.c_str(), false); | 
|  | process.SetExecutableFile(executableFile, true); | 
|  | GetTripleForProcess(executableFile, triple); | 
|  | } | 
|  | process.SetArchitecture(ArchSpec(triple)); | 
|  |  | 
|  | // TODO(zturner): Add the ability to get the process user name. | 
|  | } | 
|  | } | 
|  |  | 
|  | lldb::DataBufferSP Host::GetAuxvData(lldb_private::Process *process) { | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | lldb::tid_t Host::GetCurrentThreadID() { | 
|  | return lldb::tid_t(::GetCurrentThreadId()); | 
|  | } | 
|  |  | 
|  | lldb::thread_t Host::GetCurrentThread() { | 
|  | return lldb::thread_t(::GetCurrentThread()); | 
|  | } | 
|  |  | 
|  | lldb::thread_key_t | 
|  | Host::ThreadLocalStorageCreate(ThreadLocalStorageCleanupCallback callback) { | 
|  | return TlsAlloc(); | 
|  | } | 
|  |  | 
|  | void *Host::ThreadLocalStorageGet(lldb::thread_key_t key) { | 
|  | return ::TlsGetValue(key); | 
|  | } | 
|  |  | 
|  | void Host::ThreadLocalStorageSet(lldb::thread_key_t key, void *value) { | 
|  | ::TlsSetValue(key, value); | 
|  | } | 
|  |  | 
|  | void Host::Kill(lldb::pid_t pid, int signo) { | 
|  | TerminateProcess((HANDLE)pid, 1); | 
|  | } | 
|  |  | 
|  | const char *Host::GetSignalAsCString(int signo) { return NULL; } | 
|  |  | 
|  | FileSpec Host::GetModuleFileSpecForHostAddress(const void *host_addr) { | 
|  | FileSpec module_filespec; | 
|  |  | 
|  | HMODULE hmodule = NULL; | 
|  | if (!::GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, | 
|  | (LPCTSTR)host_addr, &hmodule)) | 
|  | return module_filespec; | 
|  |  | 
|  | std::vector<wchar_t> buffer(PATH_MAX); | 
|  | DWORD chars_copied = 0; | 
|  | do { | 
|  | chars_copied = ::GetModuleFileNameW(hmodule, &buffer[0], buffer.size()); | 
|  | if (chars_copied == buffer.size() && | 
|  | ::GetLastError() == ERROR_INSUFFICIENT_BUFFER) | 
|  | buffer.resize(buffer.size() * 2); | 
|  | } while (chars_copied >= buffer.size()); | 
|  | std::string path; | 
|  | if (!llvm::convertWideToUTF8(buffer.data(), path)) | 
|  | return module_filespec; | 
|  | module_filespec.SetFile(path, false); | 
|  | return module_filespec; | 
|  | } | 
|  |  | 
|  | uint32_t Host::FindProcesses(const ProcessInstanceInfoMatch &match_info, | 
|  | ProcessInstanceInfoList &process_infos) { | 
|  | process_infos.Clear(); | 
|  |  | 
|  | AutoHandle snapshot(CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0)); | 
|  | if (!snapshot.IsValid()) | 
|  | return 0; | 
|  |  | 
|  | PROCESSENTRY32W pe = {0}; | 
|  | pe.dwSize = sizeof(PROCESSENTRY32W); | 
|  | if (Process32FirstW(snapshot.get(), &pe)) { | 
|  | do { | 
|  | AutoHandle handle(::OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, | 
|  | pe.th32ProcessID), | 
|  | nullptr); | 
|  |  | 
|  | ProcessInstanceInfo process; | 
|  | std::string exeFile; | 
|  | llvm::convertWideToUTF8(pe.szExeFile, exeFile); | 
|  | process.SetExecutableFile(FileSpec(exeFile, false), true); | 
|  | process.SetProcessID(pe.th32ProcessID); | 
|  | process.SetParentProcessID(pe.th32ParentProcessID); | 
|  | GetProcessExecutableAndTriple(handle, process); | 
|  |  | 
|  | if (match_info.MatchAllProcesses() || match_info.Matches(process)) | 
|  | process_infos.Append(process); | 
|  | } while (Process32NextW(snapshot.get(), &pe)); | 
|  | } | 
|  | return process_infos.GetSize(); | 
|  | } | 
|  |  | 
|  | bool Host::GetProcessInfo(lldb::pid_t pid, ProcessInstanceInfo &process_info) { | 
|  | process_info.Clear(); | 
|  |  | 
|  | AutoHandle handle( | 
|  | ::OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid), | 
|  | nullptr); | 
|  | if (!handle.IsValid()) | 
|  | return false; | 
|  |  | 
|  | process_info.SetProcessID(pid); | 
|  | GetProcessExecutableAndTriple(handle, process_info); | 
|  |  | 
|  | // Need to read the PEB to get parent process and command line arguments. | 
|  | return true; | 
|  | } | 
|  |  | 
|  | HostThread Host::StartMonitoringChildProcess( | 
|  | const Host::MonitorChildProcessCallback &callback, lldb::pid_t pid, | 
|  | bool monitor_signals) { | 
|  | return HostThread(); | 
|  | } | 
|  |  | 
|  | Error Host::ShellExpandArguments(ProcessLaunchInfo &launch_info) { | 
|  | Error error; | 
|  | if (launch_info.GetFlags().Test(eLaunchFlagShellExpandArguments)) { | 
|  | FileSpec expand_tool_spec; | 
|  | if (!HostInfo::GetLLDBPath(lldb::ePathTypeSupportExecutableDir, | 
|  | expand_tool_spec)) { | 
|  | error.SetErrorString("could not find support executable directory for " | 
|  | "the lldb-argdumper tool"); | 
|  | return error; | 
|  | } | 
|  | expand_tool_spec.AppendPathComponent("lldb-argdumper.exe"); | 
|  | if (!expand_tool_spec.Exists()) { | 
|  | error.SetErrorString("could not find the lldb-argdumper tool"); | 
|  | return error; | 
|  | } | 
|  |  | 
|  | std::string quoted_cmd_string; | 
|  | launch_info.GetArguments().GetQuotedCommandString(quoted_cmd_string); | 
|  | std::replace(quoted_cmd_string.begin(), quoted_cmd_string.end(), '\\', '/'); | 
|  | StreamString expand_command; | 
|  |  | 
|  | expand_command.Printf("\"%s\" %s", expand_tool_spec.GetPath().c_str(), | 
|  | quoted_cmd_string.c_str()); | 
|  |  | 
|  | int status; | 
|  | std::string output; | 
|  | RunShellCommand(expand_command.GetData(), launch_info.GetWorkingDirectory(), | 
|  | &status, nullptr, &output, 10); | 
|  |  | 
|  | if (status != 0) { | 
|  | error.SetErrorStringWithFormat("lldb-argdumper exited with error %d", | 
|  | status); | 
|  | return error; | 
|  | } | 
|  |  | 
|  | auto data_sp = StructuredData::ParseJSON(output); | 
|  | if (!data_sp) { | 
|  | error.SetErrorString("invalid JSON"); | 
|  | return error; | 
|  | } | 
|  |  | 
|  | auto dict_sp = data_sp->GetAsDictionary(); | 
|  | if (!data_sp) { | 
|  | error.SetErrorString("invalid JSON"); | 
|  | return error; | 
|  | } | 
|  |  | 
|  | auto args_sp = dict_sp->GetObjectForDotSeparatedPath("arguments"); | 
|  | if (!args_sp) { | 
|  | error.SetErrorString("invalid JSON"); | 
|  | return error; | 
|  | } | 
|  |  | 
|  | auto args_array_sp = args_sp->GetAsArray(); | 
|  | if (!args_array_sp) { | 
|  | error.SetErrorString("invalid JSON"); | 
|  | return error; | 
|  | } | 
|  |  | 
|  | launch_info.GetArguments().Clear(); | 
|  |  | 
|  | for (size_t i = 0; i < args_array_sp->GetSize(); i++) { | 
|  | auto item_sp = args_array_sp->GetItemAtIndex(i); | 
|  | if (!item_sp) | 
|  | continue; | 
|  | auto str_sp = item_sp->GetAsString(); | 
|  | if (!str_sp) | 
|  | continue; | 
|  |  | 
|  | launch_info.GetArguments().AppendArgument(str_sp->GetValue().c_str()); | 
|  | } | 
|  | } | 
|  |  | 
|  | return error; | 
|  | } | 
|  |  | 
|  | size_t Host::GetEnvironment(StringList &env) { | 
|  | // The environment block on Windows is a contiguous buffer of NULL terminated | 
|  | // strings, | 
|  | // where the end of the environment block is indicated by two consecutive | 
|  | // NULLs. | 
|  | LPWCH environment_block = ::GetEnvironmentStringsW(); | 
|  | env.Clear(); | 
|  | while (*environment_block != L'\0') { | 
|  | std::string current_var; | 
|  | auto current_var_size = wcslen(environment_block) + 1; | 
|  | if (!llvm::convertWideToUTF8(environment_block, current_var)) { | 
|  | environment_block += current_var_size; | 
|  | continue; | 
|  | } | 
|  | if (current_var[0] != '=') | 
|  | env.AppendString(current_var); | 
|  |  | 
|  | environment_block += current_var_size; | 
|  | } | 
|  | return env.GetSize(); | 
|  | } |