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 #if defined(_MSC_VER)
38 static const DWORD MS_VC_EXCEPTION = 0x406D1388;
39
40 #pragma pack(push, 8)
41 typedef struct tagTHREADNAME_INFO
42 {
43 DWORD dwType; // Must be 0x1000.
44 LPCSTR szName; // Pointer to name (in user addr space).
45 DWORD dwThreadID; // Thread ID (-1=caller thread).
46 DWORD dwFlags; // Reserved for future use, must be zero.
47 } THREADNAME_INFO;
48 #pragma pack(pop)
49
LegacySetThreadName(const char * pThreadName)50 void LegacySetThreadName(const char* pThreadName)
51 {
52 THREADNAME_INFO info;
53 info.dwType = 0x1000;
54 info.szName = pThreadName;
55 info.dwThreadID = GetCurrentThreadId();
56 info.dwFlags = 0;
57
58 if (!IsDebuggerPresent())
59 {
60 // No debugger attached to interpret exception, no need to actually do it
61 return;
62 }
63
64 #pragma warning(push)
65 #pragma warning(disable : 6320 6322)
66 __try
67 {
68 RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), (ULONG_PTR*)&info);
69 }
70 __except (EXCEPTION_EXECUTE_HANDLER)
71 {
72 }
73 #pragma warning(pop)
74 }
75 #endif // _WIN32
76
SetCurrentThreadName(const char * pThreadName)77 void SWR_API SetCurrentThreadName(const char* pThreadName)
78 {
79 #if defined(_MSC_VER)
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 = reinterpret_cast<PFNSetThreadDescription>(
84 GetProcAddress(GetModuleHandleA("Kernel32.dll"), "SetThreadDescription"));
85
86 if (!pfnSetThreadDescription)
87 {
88 // try KernelBase.dll
89 pfnSetThreadDescription = reinterpret_cast<PFNSetThreadDescription>(
90 GetProcAddress(GetModuleHandleA("KernelBase.dll"), "SetThreadDescription"));
91 }
92
93 if (pfnSetThreadDescription)
94 {
95 std::string utf8Name = pThreadName;
96 std::wstring wideName;
97 wideName.resize(utf8Name.size() + 1);
98 swprintf_s(&(wideName.front()), wideName.size(), L"%S", utf8Name.c_str());
99 HRESULT hr = pfnSetThreadDescription(GetCurrentThread(), wideName.c_str());
100 SWR_ASSERT(SUCCEEDED(hr), "Failed to set thread name to %s", pThreadName);
101
102 // Fall through - it seems like some debuggers only recognize the exception
103 }
104
105 // Fall back to exception based hack
106 LegacySetThreadName(pThreadName);
107 #endif // _WIN32
108
109 #if defined(FORCE_LINUX) || defined(__linux__) || defined(__gnu_linux__)
110 pthread_setname_np(pthread_self(), pThreadName);
111 #endif // Linux
112 }
113
114 #if defined(__APPLE__) || defined(FORCE_LINUX) || defined(__linux__) || defined(__gnu_linux__)
115 static void
SplitString(std::vector<std::string> & out_segments,const std::string & input,char splitToken)116 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 #endif // Unix
131
CreateDirectoryPath(const std::string & path)132 void SWR_API CreateDirectoryPath(const std::string& path)
133 {
134 #if defined(_WIN32)
135 SHCreateDirectoryExA(nullptr, path.c_str(), nullptr);
136 #endif // Windows
137
138 #if defined(__APPLE__) || defined(FORCE_LINUX) || defined(__linux__) || defined(__gnu_linux__)
139 std::vector<std::string> pathSegments;
140 SplitString(pathSegments, path, '/');
141
142 std::string tmpPath;
143 for (auto const& segment : pathSegments)
144 {
145 tmpPath.push_back('/');
146 tmpPath += segment;
147
148 int result = mkdir(tmpPath.c_str(), 0777);
149 if (result == -1 && errno != EEXIST)
150 {
151 break;
152 }
153 }
154 #endif // Unix
155 }
156
157 /// Execute Command (block until finished)
158 /// @returns process exit value
ExecCmd(const std::string & cmd,const char * pOptEnvStrings,std::string * pOptStdOut,std::string * pOptStdErr,const std::string * pOptStdIn)159 int SWR_API ExecCmd(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(NULL,
217 (LPSTR)local_cmd.c_str(),
218 NULL,
219 NULL,
220 TRUE,
221 0,
222 (LPVOID)pOptEnvStrings,
223 NULL,
224 &StartupInfo,
225 &procInfo);
226
227 if (ProcessValue && procInfo.hProcess)
228 {
229 auto ReadFromPipe = [](HANDLE hPipe, std::string* pOutStr) {
230 char buf[1024];
231 DWORD dwRead = 0;
232 DWORD dwAvail = 0;
233 while (true)
234 {
235 if (!::PeekNamedPipe(hPipe, NULL, 0, NULL, &dwAvail, NULL))
236 {
237 break;
238 }
239
240 if (!dwAvail) // no data available, return
241 {
242 break;
243 }
244
245 if (!::ReadFile(hPipe,
246 buf,
247 std::min<size_t>(sizeof(buf) - 1, size_t(dwAvail)),
248 &dwRead,
249 NULL) ||
250 !dwRead)
251 {
252 // error, the child process might ended
253 break;
254 }
255
256 buf[dwRead] = 0;
257 if (pOutStr)
258 {
259 (*pOutStr) += buf;
260 }
261 }
262 };
263 bool bProcessEnded = false;
264 size_t bytesWritten = 0;
265 do
266 {
267 if (pOptStdIn && (pOptStdIn->size() > bytesWritten))
268 {
269 DWORD bytesToWrite = static_cast<DWORD>(pOptStdIn->size()) - bytesWritten;
270 if (!::WriteFile(hPipes[0].hWrite,
271 pOptStdIn->data() + bytesWritten,
272 bytesToWrite,
273 &bytesToWrite,
274 nullptr))
275 {
276 // Failed to write to pipe
277 break;
278 }
279 bytesWritten += bytesToWrite;
280 }
281
282 // Give some timeslice (50ms), so we won't waste 100% cpu.
283 bProcessEnded = (WaitForSingleObject(procInfo.hProcess, 50) == WAIT_OBJECT_0);
284
285 ReadFromPipe(hPipes[1].hRead, pOptStdOut);
286 ReadFromPipe(hPipes[2].hRead, pOptStdErr);
287 } while (!bProcessEnded);
288
289 DWORD exitVal = 0;
290 if (!GetExitCodeProcess(procInfo.hProcess, &exitVal))
291 {
292 exitVal = 1;
293 }
294
295 CloseHandle(procInfo.hProcess);
296 CloseHandle(procInfo.hThread);
297
298 rvalue = exitVal;
299 }
300
301 for (WinPipe& p : hPipes)
302 {
303 CloseHandle(p.hRead);
304 CloseHandle(p.hWrite);
305 }
306
307 #else
308
309 // Non-Windows implementation
310
311 #endif
312
313 return rvalue;
314 }
315