1 
2 /*
3  * Copyright (C) 2020 The Android Open Source Project
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 
18 #include "src/profiling/symbolizer/subprocess.h"
19 
20 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
21 
22 #include <sstream>
23 #include <string>
24 
25 #include <Windows.h>
26 
27 #include "perfetto/base/logging.h"
28 
29 namespace perfetto {
30 namespace profiling {
31 
Subprocess(const std::string & file,std::vector<std::string> args)32 Subprocess::Subprocess(const std::string& file, std::vector<std::string> args) {
33   std::stringstream cmd;
34   cmd << file;
35   for (auto arg : args) {
36     cmd << " " << arg;
37   }
38   SECURITY_ATTRIBUTES attr;
39   attr.nLength = sizeof(SECURITY_ATTRIBUTES);
40   attr.bInheritHandle = true;
41   attr.lpSecurityDescriptor = nullptr;
42   // Create a pipe for the child process's STDOUT.
43   if (!CreatePipe(&child_pipe_out_read_, &child_pipe_out_write_, &attr, 0) ||
44       !SetHandleInformation(child_pipe_out_read_, HANDLE_FLAG_INHERIT, 0)) {
45     PERFETTO_ELOG("Failed to create stdout pipe");
46     return;
47   }
48   if (!CreatePipe(&child_pipe_in_read_, &child_pipe_in_write_, &attr, 0) ||
49       !SetHandleInformation(child_pipe_in_write_, HANDLE_FLAG_INHERIT, 0)) {
50     PERFETTO_ELOG("Failed to create stdin pipe");
51     return;
52   }
53 
54   PROCESS_INFORMATION proc_info;
55   STARTUPINFOA start_info;
56   bool success = false;
57   // Set up members of the PROCESS_INFORMATION structure.
58   ZeroMemory(&proc_info, sizeof(PROCESS_INFORMATION));
59 
60   // Set up members of the STARTUPINFO structure.
61   // This structure specifies the STDIN and STDOUT handles for redirection.
62   ZeroMemory(&start_info, sizeof(STARTUPINFOA));
63   start_info.cb = sizeof(STARTUPINFOA);
64   start_info.hStdError = child_pipe_out_write_;
65   start_info.hStdOutput = child_pipe_out_write_;
66   start_info.hStdInput = child_pipe_in_read_;
67   start_info.dwFlags |= STARTF_USESTDHANDLES;
68 
69   // Create the child process.
70   success = CreateProcessA(nullptr,
71                            &(cmd.str()[0]),  // command line
72                            nullptr,          // process security attributes
73                            nullptr,      // primary thread security attributes
74                            TRUE,         // handles are inherited
75                            0,            // creation flags
76                            nullptr,      // use parent's environment
77                            nullptr,      // use parent's current directory
78                            &start_info,  // STARTUPINFO pointer
79                            &proc_info);  // receives PROCESS_INFORMATION
80 
81   // If an error occurs, exit the application.
82   if (success) {
83     CloseHandle(proc_info.hProcess);
84     CloseHandle(proc_info.hThread);
85 
86     // Close handles to the stdin and stdout pipes no longer needed by the child
87     // process. If they are not explicitly closed, there is no way to recognize
88     // that the child process has ended.
89 
90     CloseHandle(child_pipe_out_write_);
91     CloseHandle(child_pipe_in_read_);
92   } else {
93     PERFETTO_ELOG("Failed to launch: %s", cmd.str().c_str());
94     child_pipe_in_read_ = nullptr;
95     child_pipe_in_write_ = nullptr;
96     child_pipe_out_write_ = nullptr;
97     child_pipe_out_read_ = nullptr;
98   }
99 }
100 
~Subprocess()101 Subprocess::~Subprocess() {
102   CloseHandle(child_pipe_out_read_);
103   CloseHandle(child_pipe_in_write_);
104 }
105 
Write(const char * buffer,size_t size)106 int64_t Subprocess::Write(const char* buffer, size_t size) {
107   if (child_pipe_in_write_ == nullptr) {
108     return -1;
109   }
110   DWORD bytes_written;
111   if (WriteFile(child_pipe_in_write_, buffer, static_cast<DWORD>(size),
112                 &bytes_written, nullptr)) {
113     return static_cast<int64_t>(bytes_written);
114   }
115   return -1;
116 }
117 
Read(char * buffer,size_t size)118 int64_t Subprocess::Read(char* buffer, size_t size) {
119   if (child_pipe_out_read_ == nullptr) {
120     return -1;
121   }
122   DWORD bytes_read;
123   if (ReadFile(child_pipe_out_read_, buffer, static_cast<DWORD>(size),
124                &bytes_read, nullptr)) {
125     return static_cast<int64_t>(bytes_read);
126   }
127   return -1;
128 }
129 
130 }  // namespace profiling
131 }  // namespace perfetto
132 
133 #endif  // PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
134