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) 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(_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__) 115static void 116SplitString(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 132void 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 159int 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