Skip to content

Commit e48ad6a

Browse files
committed
refactor
1 parent 3ecad7a commit e48ad6a

File tree

2 files changed

+219
-177
lines changed

2 files changed

+219
-177
lines changed

src/support/process.h

Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
/*
2+
* Copyright 2021 WebAssembly Community Group participants
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
//
18+
// Process helpers.
19+
//
20+
21+
#ifndef wasm_support_process_h
22+
#define wasm_support_process_h
23+
24+
#include <string>
25+
#include <iostream>
26+
27+
#include "support/timing.h"
28+
29+
#ifdef _WIN32
30+
#ifndef NOMINMAX
31+
#define NOMINMAX
32+
#endif
33+
#include <windows.h>
34+
// Create a string with last error message
35+
std::string GetLastErrorStdStr() {
36+
DWORD error = GetLastError();
37+
if (error) {
38+
LPVOID lpMsgBuf;
39+
DWORD bufLen = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
40+
FORMAT_MESSAGE_FROM_SYSTEM |
41+
FORMAT_MESSAGE_IGNORE_INSERTS,
42+
NULL,
43+
error,
44+
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
45+
(LPTSTR)&lpMsgBuf,
46+
0,
47+
NULL);
48+
if (bufLen) {
49+
LPCSTR lpMsgStr = (LPCSTR)lpMsgBuf;
50+
std::string result(lpMsgStr, lpMsgStr + bufLen);
51+
LocalFree(lpMsgBuf);
52+
return result;
53+
}
54+
}
55+
return std::string();
56+
}
57+
#endif
58+
59+
namespace wasm {
60+
61+
struct ProgramResult {
62+
size_t timeout;
63+
64+
int code;
65+
std::string output;
66+
double time;
67+
68+
ProgramResult(size_t timeout = -1) : timeout(timeout) {}
69+
ProgramResult(std::string command, size_t timeout) : timeout(timeout) {
70+
getFromExecution(command);
71+
}
72+
73+
#ifdef _WIN32
74+
void getFromExecution(std::string command) {
75+
Timer timer;
76+
timer.start();
77+
SECURITY_ATTRIBUTES saAttr;
78+
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
79+
saAttr.bInheritHandle = TRUE;
80+
saAttr.lpSecurityDescriptor = NULL;
81+
82+
HANDLE hChildStd_OUT_Rd;
83+
HANDLE hChildStd_OUT_Wr;
84+
85+
if (
86+
// Create a pipe for the child process's STDOUT.
87+
!CreatePipe(&hChildStd_OUT_Rd, &hChildStd_OUT_Wr, &saAttr, 0) ||
88+
// Ensure the read handle to the pipe for STDOUT is not inherited.
89+
!SetHandleInformation(hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0)) {
90+
Fatal() << "CreatePipe \"" << command
91+
<< "\" failed: " << GetLastErrorStdStr() << ".\n";
92+
}
93+
94+
STARTUPINFO si;
95+
PROCESS_INFORMATION pi;
96+
97+
ZeroMemory(&si, sizeof(si));
98+
si.cb = sizeof(si);
99+
si.hStdError = hChildStd_OUT_Wr;
100+
si.hStdOutput = hChildStd_OUT_Wr;
101+
si.dwFlags |= STARTF_USESTDHANDLES;
102+
ZeroMemory(&pi, sizeof(pi));
103+
104+
// Start the child process.
105+
if (!CreateProcess(NULL, // No module name (use command line)
106+
(LPSTR)command.c_str(), // Command line
107+
NULL, // Process handle not inheritable
108+
NULL, // Thread handle not inheritable
109+
TRUE, // Set handle inheritance to TRUE
110+
0, // No creation flags
111+
NULL, // Use parent's environment block
112+
NULL, // Use parent's starting directory
113+
&si, // Pointer to STARTUPINFO structure
114+
&pi) // Pointer to PROCESS_INFORMATION structure
115+
) {
116+
Fatal() << "CreateProcess \"" << command
117+
<< "\" failed: " << GetLastErrorStdStr() << ".\n";
118+
}
119+
120+
// Wait until child process exits.
121+
DWORD retVal = WaitForSingleObject(pi.hProcess, timeout * 1000);
122+
if (retVal == WAIT_TIMEOUT) {
123+
printf("Command timeout: %s", command.c_str());
124+
TerminateProcess(pi.hProcess, -1);
125+
}
126+
DWORD dwordExitCode;
127+
if (!GetExitCodeProcess(pi.hProcess, &dwordExitCode)) {
128+
Fatal() << "GetExitCodeProcess failed: " << GetLastErrorStdStr() << ".\n";
129+
}
130+
code = (int)dwordExitCode;
131+
132+
// Close process and thread handles.
133+
CloseHandle(pi.hProcess);
134+
CloseHandle(pi.hThread);
135+
136+
// Read output from the child process's pipe for STDOUT
137+
// Stop when there is no more data.
138+
{
139+
const int BUFSIZE = 4096;
140+
DWORD dwRead, dwTotal, dwTotalRead = 0;
141+
CHAR chBuf[BUFSIZE];
142+
BOOL bSuccess = FALSE;
143+
144+
PeekNamedPipe(hChildStd_OUT_Rd, NULL, 0, NULL, &dwTotal, NULL);
145+
while (dwTotalRead < dwTotal) {
146+
bSuccess =
147+
ReadFile(hChildStd_OUT_Rd, chBuf, BUFSIZE - 1, &dwRead, NULL);
148+
if (!bSuccess || dwRead == 0)
149+
break;
150+
chBuf[dwRead] = 0;
151+
dwTotalRead += dwRead;
152+
output.append(chBuf);
153+
}
154+
}
155+
timer.stop();
156+
time = timer.getTotal();
157+
}
158+
#else // POSIX
159+
// runs the command and notes the output
160+
// TODO: also stderr, not just stdout?
161+
void getFromExecution(std::string command) {
162+
Timer timer;
163+
timer.start();
164+
// do this using just core stdio.h and stdlib.h, for portability
165+
// sadly this requires two invokes
166+
code = system(("timeout " + std::to_string(timeout) + "s " + command +
167+
" > /dev/null 2> /dev/null")
168+
.c_str());
169+
const int MAX_BUFFER = 1024;
170+
char buffer[MAX_BUFFER];
171+
FILE* stream = popen(
172+
("timeout " + std::to_string(timeout) + "s " + command + " 2> /dev/null")
173+
.c_str(),
174+
"r");
175+
while (fgets(buffer, MAX_BUFFER, stream) != NULL) {
176+
output.append(buffer);
177+
}
178+
pclose(stream);
179+
timer.stop();
180+
time = timer.getTotal() / 2;
181+
}
182+
#endif // _WIN32
183+
184+
bool operator==(ProgramResult& other) {
185+
return code == other.code && output == other.output;
186+
}
187+
bool operator!=(ProgramResult& other) { return !(*this == other); }
188+
189+
bool failed() { return code != 0; }
190+
191+
void dump(std::ostream& o) {
192+
o << "[ProgramResult] code: " << code << " stdout: \n"
193+
<< output << "[====]\nin " << time << " seconds\n[/ProgramResult]\n";
194+
}
195+
};
196+
197+
} // namespace wasm
198+
199+
namespace std {
200+
201+
inline std::ostream& operator<<(std::ostream& o, wasm::ProgramResult& result) {
202+
result.dump(o);
203+
return o;
204+
}
205+
206+
} // namespace std
207+
208+
#endif // wasm_support_process_h
209+

0 commit comments

Comments
 (0)