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(_WIN32) 38static const DWORD MS_VC_EXCEPTION = 0x406D1388; 39 40#pragma pack(push, 8) 41typedef 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 50void 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 77void 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 = 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 114static void 115SplitString(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 130void 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 157int 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