/* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "src/profiling/symbolizer/subprocess.h" #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) #include #include #include #include "perfetto/base/logging.h" namespace perfetto { namespace profiling { Subprocess::Subprocess(const std::string& file, std::vector args) { std::stringstream cmd; cmd << file; for (auto arg : args) { cmd << " " << arg; } SECURITY_ATTRIBUTES attr; attr.nLength = sizeof(SECURITY_ATTRIBUTES); attr.bInheritHandle = true; attr.lpSecurityDescriptor = nullptr; // Create a pipe for the child process's STDOUT. if (!CreatePipe(&child_pipe_out_read_, &child_pipe_out_write_, &attr, 0) || !SetHandleInformation(child_pipe_out_read_, HANDLE_FLAG_INHERIT, 0)) { PERFETTO_ELOG("Failed to create stdout pipe"); return; } if (!CreatePipe(&child_pipe_in_read_, &child_pipe_in_write_, &attr, 0) || !SetHandleInformation(child_pipe_in_write_, HANDLE_FLAG_INHERIT, 0)) { PERFETTO_ELOG("Failed to create stdin pipe"); return; } PROCESS_INFORMATION proc_info; STARTUPINFOA start_info; bool success = false; // Set up members of the PROCESS_INFORMATION structure. ZeroMemory(&proc_info, sizeof(PROCESS_INFORMATION)); // Set up members of the STARTUPINFO structure. // This structure specifies the STDIN and STDOUT handles for redirection. ZeroMemory(&start_info, sizeof(STARTUPINFOA)); start_info.cb = sizeof(STARTUPINFOA); start_info.hStdError = child_pipe_out_write_; start_info.hStdOutput = child_pipe_out_write_; start_info.hStdInput = child_pipe_in_read_; start_info.dwFlags |= STARTF_USESTDHANDLES; // Create the child process. success = CreateProcessA(nullptr, &(cmd.str()[0]), // command line nullptr, // process security attributes nullptr, // primary thread security attributes TRUE, // handles are inherited 0, // creation flags nullptr, // use parent's environment nullptr, // use parent's current directory &start_info, // STARTUPINFO pointer &proc_info); // receives PROCESS_INFORMATION // If an error occurs, exit the application. if (success) { CloseHandle(proc_info.hProcess); CloseHandle(proc_info.hThread); // Close handles to the stdin and stdout pipes no longer needed by the child // process. If they are not explicitly closed, there is no way to recognize // that the child process has ended. CloseHandle(child_pipe_out_write_); CloseHandle(child_pipe_in_read_); } else { PERFETTO_ELOG("Failed to launch: %s", cmd.str().c_str()); child_pipe_in_read_ = nullptr; child_pipe_in_write_ = nullptr; child_pipe_out_write_ = nullptr; child_pipe_out_read_ = nullptr; } } Subprocess::~Subprocess() { CloseHandle(child_pipe_out_read_); CloseHandle(child_pipe_in_write_); } int64_t Subprocess::Write(const char* buffer, size_t size) { if (child_pipe_in_write_ == nullptr) { return -1; } DWORD bytes_written; if (WriteFile(child_pipe_in_write_, buffer, static_cast(size), &bytes_written, nullptr)) { return static_cast(bytes_written); } return -1; } int64_t Subprocess::Read(char* buffer, size_t size) { if (child_pipe_out_read_ == nullptr) { return -1; } DWORD bytes_read; if (ReadFile(child_pipe_out_read_, buffer, static_cast(size), &bytes_read, nullptr)) { return static_cast(bytes_read); } return -1; } } // namespace profiling } // namespace perfetto #endif // PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)