1 /****************************************************************************
2 * Copyright (C) 2017 Intel Corporation. All Rights Reserved.
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:
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
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
22 ****************************************************************************/
24 #include "common/os.h"
33 #if defined(__APPLE__) || defined(FORCE_LINUX) || defined(__linux__) || defined(__gnu_linux__)
38 static const DWORD MS_VC_EXCEPTION
= 0x406D1388;
41 typedef struct tagTHREADNAME_INFO
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.
50 void LegacySetThreadName(const char* pThreadName
)
54 info
.szName
= pThreadName
;
55 info
.dwThreadID
= GetCurrentThreadId();
58 if (!IsDebuggerPresent())
60 // No debugger attached to interpret exception, no need to actually do it
65 #pragma warning(disable : 6320 6322)
68 RaiseException(MS_VC_EXCEPTION
, 0, sizeof(info
) / sizeof(ULONG_PTR
), (ULONG_PTR
*)&info
);
70 __except (EXCEPTION_EXECUTE_HANDLER
)
77 void SWR_API
SetCurrentThreadName(const char* pThreadName
)
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"));
86 if (!pfnSetThreadDescription
)
89 pfnSetThreadDescription
= reinterpret_cast<PFNSetThreadDescription
>(
90 GetProcAddress(GetModuleHandleA("KernelBase.dll"), "SetThreadDescription"));
93 if (pfnSetThreadDescription
)
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
);
102 // Fall through - it seems like some debuggers only recognize the exception
105 // Fall back to exception based hack
106 LegacySetThreadName(pThreadName
);
109 #if defined(FORCE_LINUX) || defined(__linux__) || defined(__gnu_linux__)
110 pthread_setname_np(pthread_self(), pThreadName
);
115 SplitString(std::vector
<std::string
>& out_segments
, const std::string
& input
, char splitToken
)
117 out_segments
.clear();
119 std::istringstream
f(input
);
121 while (std::getline(f
, s
, splitToken
))
125 out_segments
.push_back(s
);
130 void SWR_API
CreateDirectoryPath(const std::string
& path
)
133 SHCreateDirectoryExA(nullptr, path
.c_str(), nullptr);
136 #if defined(__APPLE__) || defined(FORCE_LINUX) || defined(__linux__) || defined(__gnu_linux__)
137 std::vector
<std::string
> pathSegments
;
138 SplitString(pathSegments
, path
, '/');
141 for (auto const& segment
: pathSegments
)
143 tmpPath
.push_back('/');
146 int result
= mkdir(tmpPath
.c_str(), 0777);
147 if (result
== -1 && errno
!= EEXIST
)
155 /// Execute Command (block until finished)
156 /// @returns process exit value
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
171 std::array
<WinPipe
, 3> hPipes
= {};
173 SECURITY_ATTRIBUTES saAttr
= {sizeof(SECURITY_ATTRIBUTES
)};
174 saAttr
.bInheritHandle
= TRUE
; // Pipe handles are inherited by child process.
175 saAttr
.lpSecurityDescriptor
= NULL
;
179 for (WinPipe
& p
: hPipes
)
181 if (!CreatePipe(&p
.hRead
, &p
.hWrite
, &saAttr
, 0))
189 for (WinPipe
& p
: hPipes
)
191 CloseHandle(p
.hRead
);
192 CloseHandle(p
.hWrite
);
198 STARTUPINFOA StartupInfo
{};
199 StartupInfo
.cb
= sizeof(STARTUPINFOA
);
200 StartupInfo
.dwFlags
= STARTF_USESTDHANDLES
;
201 StartupInfo
.dwFlags
|= STARTF_USESHOWWINDOW
;
202 StartupInfo
.wShowWindow
= SW_HIDE
;
205 StartupInfo
.hStdInput
= hPipes
[0].hRead
;
207 StartupInfo
.hStdOutput
= hPipes
[1].hWrite
;
208 StartupInfo
.hStdError
= hPipes
[2].hWrite
;
209 PROCESS_INFORMATION procInfo
{};
211 // CreateProcess can modify the string
212 std::string local_cmd
= cmd
;
214 BOOL ProcessValue
= CreateProcessA(NULL
,
215 (LPSTR
)local_cmd
.c_str(),
220 (LPVOID
)pOptEnvStrings
,
225 if (ProcessValue
&& procInfo
.hProcess
)
227 auto ReadFromPipe
= [](HANDLE hPipe
, std::string
* pOutStr
) {
233 if (!::PeekNamedPipe(hPipe
, NULL
, 0, NULL
, &dwAvail
, NULL
))
238 if (!dwAvail
) // no data available, return
243 if (!::ReadFile(hPipe
,
245 std::min
<size_t>(sizeof(buf
) - 1, size_t(dwAvail
)),
250 // error, the child process might ended
261 bool bProcessEnded
= false;
262 size_t bytesWritten
= 0;
265 if (pOptStdIn
&& (pOptStdIn
->size() > bytesWritten
))
267 DWORD bytesToWrite
= static_cast<DWORD
>(pOptStdIn
->size()) - bytesWritten
;
268 if (!::WriteFile(hPipes
[0].hWrite
,
269 pOptStdIn
->data() + bytesWritten
,
274 // Failed to write to pipe
277 bytesWritten
+= bytesToWrite
;
280 // Give some timeslice (50ms), so we won't waste 100% cpu.
281 bProcessEnded
= (WaitForSingleObject(procInfo
.hProcess
, 50) == WAIT_OBJECT_0
);
283 ReadFromPipe(hPipes
[1].hRead
, pOptStdOut
);
284 ReadFromPipe(hPipes
[2].hRead
, pOptStdErr
);
285 } while (!bProcessEnded
);
288 if (!GetExitCodeProcess(procInfo
.hProcess
, &exitVal
))
293 CloseHandle(procInfo
.hProcess
);
294 CloseHandle(procInfo
.hThread
);
299 for (WinPipe
& p
: hPipes
)
301 CloseHandle(p
.hRead
);
302 CloseHandle(p
.hWrite
);
307 // Non-Windows implementation