swr/rast: Better ExecCmd (i.e. system()) implmentation
[mesa.git] / src / gallium / drivers / swr / rasterizer / common / os.cpp
1 /****************************************************************************
2 * Copyright (C) 2017 Intel Corporation. All Rights Reserved.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 * IN THE SOFTWARE.
22 ****************************************************************************/
23
24 #include "common/os.h"
25 #include <vector>
26 #include <array>
27 #include <sstream>
28
29 #if defined(_WIN32)
30 #include <shlobj.h>
31 #endif // Windows
32
33 #if defined(__APPLE__) || defined(FORCE_LINUX) || defined(__linux__) || defined(__gnu_linux__)
34 #include <pthread.h>
35 #endif // Linux
36
37
38
39 #if defined(_WIN32)
40 static const DWORD MS_VC_EXCEPTION = 0x406D1388;
41
42 #pragma pack(push,8)
43 typedef struct tagTHREADNAME_INFO
44 {
45 DWORD dwType; // Must be 0x1000.
46 LPCSTR szName; // Pointer to name (in user addr space).
47 DWORD dwThreadID; // Thread ID (-1=caller thread).
48 DWORD dwFlags; // Reserved for future use, must be zero.
49 } THREADNAME_INFO;
50 #pragma pack(pop)
51
52 void LegacySetThreadName(const char* pThreadName)
53 {
54 THREADNAME_INFO info;
55 info.dwType = 0x1000;
56 info.szName = pThreadName;
57 info.dwThreadID = GetCurrentThreadId();
58 info.dwFlags = 0;
59
60 if (!IsDebuggerPresent())
61 {
62 // No debugger attached to interpret exception, no need to actually do it
63 return;
64 }
65
66 #pragma warning(push)
67 #pragma warning(disable: 6320 6322)
68 __try {
69 RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), (ULONG_PTR*)&info);
70 }
71 __except (EXCEPTION_EXECUTE_HANDLER) {
72 }
73 #pragma warning(pop)
74 }
75 #endif // _WIN32
76
77 void SWR_API SetCurrentThreadName(const char* pThreadName)
78 {
79 #if defined(_WIN32)
80 // The SetThreadDescription API was brought in version 1607 of Windows 10.
81 typedef HRESULT(WINAPI* PFNSetThreadDescription)(HANDLE hThread, PCWSTR lpThreadDescription);
82 // The SetThreadDescription API works even if no debugger is attached.
83 auto pfnSetThreadDescription =
84 reinterpret_cast<PFNSetThreadDescription>(
85 GetProcAddress(GetModuleHandleA("Kernel32.dll"), "SetThreadDescription"));
86
87 if (!pfnSetThreadDescription)
88 {
89 // try KernelBase.dll
90 pfnSetThreadDescription =
91 reinterpret_cast<PFNSetThreadDescription>(
92 GetProcAddress(GetModuleHandleA("KernelBase.dll"), "SetThreadDescription"));
93 }
94
95 if (pfnSetThreadDescription)
96 {
97 std::string utf8Name = pThreadName;
98 std::wstring wideName;
99 wideName.resize(utf8Name.size() + 1);
100 swprintf_s(&(wideName.front()), wideName.size(), L"%S", utf8Name.c_str());
101 HRESULT hr = pfnSetThreadDescription(GetCurrentThread(), wideName.c_str());
102 SWR_ASSERT(SUCCEEDED(hr), "Failed to set thread name to %s", pThreadName);
103
104 // Fall through - it seems like some debuggers only recognize the exception
105 }
106
107 // Fall back to exception based hack
108 LegacySetThreadName(pThreadName);
109 #endif // _WIN32
110
111 #if defined(FORCE_LINUX) || defined(__linux__) || defined(__gnu_linux__)
112 pthread_setname_np(pthread_self(), pThreadName);
113 #endif // Linux
114 }
115
116 static void SplitString(std::vector<std::string>& out_segments, const std::string& input, char splitToken)
117 {
118 out_segments.clear();
119
120 std::istringstream f(input);
121 std::string s;
122 while (std::getline(f, s, splitToken))
123 {
124 if (s.size())
125 {
126 out_segments.push_back(s);
127 }
128 }
129 }
130
131 void SWR_API CreateDirectoryPath(const std::string& path)
132 {
133 #if defined(_WIN32)
134 SHCreateDirectoryExA(nullptr, path.c_str(), nullptr);
135 #endif // Windows
136
137 #if defined(__APPLE__) || defined(FORCE_LINUX) || defined(__linux__) || defined(__gnu_linux__)
138 std::vector<std::string> pathSegments;
139 SplitString(pathSegments, path, '/');
140
141 std::string tmpPath;
142 for (auto const& segment : pathSegments)
143 {
144 tmpPath.push_back('/');
145 tmpPath += segment;
146
147 int result = mkdir(tmpPath.c_str(), 0777);
148 if (result == -1 && errno != EEXIST)
149 {
150 break;
151 }
152 }
153 #endif // Unix
154 }
155
156 /// Execute Command (block until finished)
157 /// @returns process exit value
158 int SWR_API ExecCmd(
159 const std::string& cmd, ///< (In) Command line string
160 const char* pOptEnvStrings, ///< (Optional In) Environment block for new process
161 std::string* pOptStdOut, ///< (Optional Out) Standard Output text
162 std::string* pOptStdErr, ///< (Optional Out) Standard Error text
163 const std::string* pOptStdIn) ///< (Optional In) Standard Input text
164 {
165 int rvalue = -1;
166
167 #if defined(_WIN32)
168 struct WinPipe
169 {
170 HANDLE hRead;
171 HANDLE hWrite;
172 };
173 std::array<WinPipe, 3> hPipes = {};
174
175 SECURITY_ATTRIBUTES saAttr = { sizeof(SECURITY_ATTRIBUTES) };
176 saAttr.bInheritHandle = TRUE; //Pipe handles are inherited by child process.
177 saAttr.lpSecurityDescriptor = NULL;
178
179 {
180 bool bFail = false;
181 for (WinPipe& p : hPipes)
182 {
183 if (!CreatePipe(&p.hRead, &p.hWrite, &saAttr, 0))
184 {
185 bFail = true;
186 }
187 }
188
189 if (bFail)
190 {
191 for (WinPipe& p : hPipes)
192 {
193 CloseHandle(p.hRead);
194 CloseHandle(p.hWrite);
195 }
196 return rvalue;
197 }
198 }
199
200 STARTUPINFOA StartupInfo{};
201 StartupInfo.cb = sizeof(STARTUPINFOA);
202 StartupInfo.dwFlags = STARTF_USESTDHANDLES;
203 StartupInfo.dwFlags |= STARTF_USESHOWWINDOW;
204 StartupInfo.wShowWindow = SW_HIDE;
205 if (pOptStdIn)
206 {
207 StartupInfo.hStdInput = hPipes[0].hRead;
208 }
209 StartupInfo.hStdOutput = hPipes[1].hWrite;
210 StartupInfo.hStdError = hPipes[2].hWrite;
211 PROCESS_INFORMATION procInfo{};
212
213 // CreateProcess can modify the string
214 std::string local_cmd = cmd;
215
216 BOOL ProcessValue = CreateProcessA(
217 NULL,
218 (LPSTR)local_cmd.c_str(),
219 NULL,
220 NULL,
221 TRUE,
222 0,
223 (LPVOID)pOptEnvStrings,
224 NULL,
225 &StartupInfo,
226 &procInfo);
227
228 if (ProcessValue && procInfo.hProcess)
229 {
230 auto ReadFromPipe = [](HANDLE hPipe, std::string* pOutStr)
231 {
232 char buf[1024];
233 DWORD dwRead = 0;
234 DWORD dwAvail = 0;
235 while (true)
236 {
237 if (!::PeekNamedPipe(hPipe, NULL, 0, NULL, &dwAvail, NULL))
238 {
239 break;
240 }
241
242 if (!dwAvail) // no data available, return
243 {
244 break;
245 }
246
247 if (!::ReadFile(hPipe, buf, std::min<size_t>(sizeof(buf) - 1, size_t(dwAvail)), &dwRead, NULL) || !dwRead)
248 {
249 // error, the child process might ended
250 break;
251 }
252
253 buf[dwRead] = 0;
254 if (pOutStr)
255 {
256 (*pOutStr) += buf;
257 }
258 }
259 };
260 bool bProcessEnded = false;
261 size_t bytesWritten = 0;
262 do
263 {
264 if (pOptStdIn && (pOptStdIn->size() > bytesWritten))
265 {
266 DWORD bytesToWrite = static_cast<DWORD>(pOptStdIn->size()) - bytesWritten;
267 if (!::WriteFile(
268 hPipes[0].hWrite,
269 pOptStdIn->data() + bytesWritten,
270 bytesToWrite, &bytesToWrite, nullptr))
271 {
272 // Failed to write to pipe
273 break;
274 }
275 bytesWritten += bytesToWrite;
276 }
277
278 // Give some timeslice (50ms), so we won't waste 100% cpu.
279 bProcessEnded = (WaitForSingleObject(procInfo.hProcess, 50) == WAIT_OBJECT_0);
280
281 ReadFromPipe(hPipes[1].hRead, pOptStdOut);
282 ReadFromPipe(hPipes[2].hRead, pOptStdErr);
283 }
284 while (!bProcessEnded);
285
286 DWORD exitVal = 0;
287 if (!GetExitCodeProcess(procInfo.hProcess, &exitVal))
288 {
289 exitVal = 1;
290 }
291
292 CloseHandle(procInfo.hProcess);
293 CloseHandle(procInfo.hThread);
294
295 rvalue = exitVal;
296 }
297
298 for (WinPipe& p : hPipes)
299 {
300 CloseHandle(p.hRead);
301 CloseHandle(p.hWrite);
302 }
303
304 #else
305
306 // Non-Windows implementation
307
308 #endif
309
310 return rvalue;
311 }