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 static void
SplitString(std::vector<std::string> & out_segments,const std::string & input,char splitToken)115 SplitString(std::vector<std::string>& out_segments, const std::string& input, char splitToken)
116 {
117 out_segments.clear();
118
119 std::istringstream f(input);
120 std::string s;
121 while (std::getline(f, s, splitToken))
122 {
123 if (s.size())
124 {
125 out_segments.push_back(s);
126 }
127 }
128 }
129
CreateDirectoryPath(const std::string & path)130 void SWR_API CreateDirectoryPath(const std::string& path)
131 {
132 #if defined(_WIN32)
133 SHCreateDirectoryExA(nullptr, path.c_str(), nullptr);
134 #endif // Windows
135
136 #if defined(__APPLE__) || defined(FORCE_LINUX) || defined(__linux__) || defined(__gnu_linux__)
137 std::vector<std::string> pathSegments;
138 SplitString(pathSegments, path, '/');
139
140 std::string tmpPath;
141 for (auto const& segment : pathSegments)
142 {
143 tmpPath.push_back('/');
144 tmpPath += segment;
145
146 int result = mkdir(tmpPath.c_str(), 0777);
147 if (result == -1 && errno != EEXIST)
148 {
149 break;
150 }
151 }
152 #endif // Unix
153 }
154
155 /// Execute Command (block until finished)
156 /// @returns process exit value
ExecCmd(const std::string & cmd,const char * pOptEnvStrings,std::string * pOptStdOut,std::string * pOptStdErr,const std::string * pOptStdIn)157 int SWR_API ExecCmd(const std::string& cmd, ///< (In) Command line string
158 const char* pOptEnvStrings, ///< (Optional In) Environment block for new process
159 std::string* pOptStdOut, ///< (Optional Out) Standard Output text
160 std::string* pOptStdErr, ///< (Optional Out) Standard Error text
161 const std::string* pOptStdIn) ///< (Optional In) Standard Input text
162 {
163 int rvalue = -1;
164
165 #if defined(_WIN32)
166 struct WinPipe
167 {
168 HANDLE hRead;
169 HANDLE hWrite;
170 };
171 std::array<WinPipe, 3> hPipes = {};
172
173 SECURITY_ATTRIBUTES saAttr = {sizeof(SECURITY_ATTRIBUTES)};
174 saAttr.bInheritHandle = TRUE; // Pipe handles are inherited by child process.
175 saAttr.lpSecurityDescriptor = NULL;
176
177 {
178 bool bFail = false;
179 for (WinPipe& p : hPipes)
180 {
181 if (!CreatePipe(&p.hRead, &p.hWrite, &saAttr, 0))
182 {
183 bFail = true;
184 }
185 }
186
187 if (bFail)
188 {
189 for (WinPipe& p : hPipes)
190 {
191 CloseHandle(p.hRead);
192 CloseHandle(p.hWrite);
193 }
194 return rvalue;
195 }
196 }
197
198 STARTUPINFOA StartupInfo{};
199 StartupInfo.cb = sizeof(STARTUPINFOA);
200 StartupInfo.dwFlags = STARTF_USESTDHANDLES;
201 StartupInfo.dwFlags |= STARTF_USESHOWWINDOW;
202 StartupInfo.wShowWindow = SW_HIDE;
203 if (pOptStdIn)
204 {
205 StartupInfo.hStdInput = hPipes[0].hRead;
206 }
207 StartupInfo.hStdOutput = hPipes[1].hWrite;
208 StartupInfo.hStdError = hPipes[2].hWrite;
209 PROCESS_INFORMATION procInfo{};
210
211 // CreateProcess can modify the string
212 std::string local_cmd = cmd;
213
214 BOOL ProcessValue = CreateProcessA(NULL,
215 (LPSTR)local_cmd.c_str(),
216 NULL,
217 NULL,
218 TRUE,
219 0,
220 (LPVOID)pOptEnvStrings,
221 NULL,
222 &StartupInfo,
223 &procInfo);
224
225 if (ProcessValue && procInfo.hProcess)
226 {
227 auto ReadFromPipe = [](HANDLE hPipe, std::string* pOutStr) {
228 char buf[1024];
229 DWORD dwRead = 0;
230 DWORD dwAvail = 0;
231 while (true)
232 {
233 if (!::PeekNamedPipe(hPipe, NULL, 0, NULL, &dwAvail, NULL))
234 {
235 break;
236 }
237
238 if (!dwAvail) // no data available, return
239 {
240 break;
241 }
242
243 if (!::ReadFile(hPipe,
244 buf,
245 std::min<size_t>(sizeof(buf) - 1, size_t(dwAvail)),
246 &dwRead,
247 NULL) ||
248 !dwRead)
249 {
250 // error, the child process might ended
251 break;
252 }
253
254 buf[dwRead] = 0;
255 if (pOutStr)
256 {
257 (*pOutStr) += buf;
258 }
259 }
260 };
261 bool bProcessEnded = false;
262 size_t bytesWritten = 0;
263 do
264 {
265 if (pOptStdIn && (pOptStdIn->size() > bytesWritten))
266 {
267 DWORD bytesToWrite = static_cast<DWORD>(pOptStdIn->size()) - bytesWritten;
268 if (!::WriteFile(hPipes[0].hWrite,
269 pOptStdIn->data() + bytesWritten,
270 bytesToWrite,
271 &bytesToWrite,
272 nullptr))
273 {
274 // Failed to write to pipe
275 break;
276 }
277 bytesWritten += bytesToWrite;
278 }
279
280 // Give some timeslice (50ms), so we won't waste 100% cpu.
281 bProcessEnded = (WaitForSingleObject(procInfo.hProcess, 50) == WAIT_OBJECT_0);
282
283 ReadFromPipe(hPipes[1].hRead, pOptStdOut);
284 ReadFromPipe(hPipes[2].hRead, pOptStdErr);
285 } while (!bProcessEnded);
286
287 DWORD exitVal = 0;
288 if (!GetExitCodeProcess(procInfo.hProcess, &exitVal))
289 {
290 exitVal = 1;
291 }
292
293 CloseHandle(procInfo.hProcess);
294 CloseHandle(procInfo.hThread);
295
296 rvalue = exitVal;
297 }
298
299 for (WinPipe& p : hPipes)
300 {
301 CloseHandle(p.hRead);
302 CloseHandle(p.hWrite);
303 }
304
305 #else
306
307 // Non-Windows implementation
308
309 #endif
310
311 return rvalue;
312 }
313