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__)
40 static const DWORD MS_VC_EXCEPTION
= 0x406D1388;
43 typedef struct tagTHREADNAME_INFO
45 DWORD dwType
; // Must be 0x1000.
46 LPCSTR szName
; // Pointer to name (in user addr space).
47 DWORD dwThreadID
; // Thread ID (-1=caller thread).
48 DWORD dwFlags
; // Reserved for future use, must be zero.
52 void LegacySetThreadName(const char* pThreadName
)
56 info
.szName
= pThreadName
;
57 info
.dwThreadID
= GetCurrentThreadId();
60 if (!IsDebuggerPresent())
62 // No debugger attached to interpret exception, no need to actually do it
67 #pragma warning(disable: 6320 6322)
69 RaiseException(MS_VC_EXCEPTION
, 0, sizeof(info
) / sizeof(ULONG_PTR
), (ULONG_PTR
*)&info
);
71 __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
=
84 reinterpret_cast<PFNSetThreadDescription
>(
85 GetProcAddress(GetModuleHandleA("Kernel32.dll"), "SetThreadDescription"));
87 if (!pfnSetThreadDescription
)
90 pfnSetThreadDescription
=
91 reinterpret_cast<PFNSetThreadDescription
>(
92 GetProcAddress(GetModuleHandleA("KernelBase.dll"), "SetThreadDescription"));
95 if (pfnSetThreadDescription
)
97 std::string utf8Name
= pThreadName
;
98 std::wstring wideName
;
99 wideName
.resize(utf8Name
.size() + 1);
100 swprintf_s(&(wideName
.front()), wideName
.size(), L
"%S", utf8Name
.c_str());
101 HRESULT hr
= pfnSetThreadDescription(GetCurrentThread(), wideName
.c_str());
102 SWR_ASSERT(SUCCEEDED(hr
), "Failed to set thread name to %s", pThreadName
);
104 // Fall through - it seems like some debuggers only recognize the exception
107 // Fall back to exception based hack
108 LegacySetThreadName(pThreadName
);
111 #if defined(FORCE_LINUX) || defined(__linux__) || defined(__gnu_linux__)
112 pthread_setname_np(pthread_self(), pThreadName
);
116 static void SplitString(std::vector
<std::string
>& out_segments
, const std::string
& input
, char splitToken
)
118 out_segments
.clear();
120 std::istringstream
f(input
);
122 while (std::getline(f
, s
, splitToken
))
126 out_segments
.push_back(s
);
131 void SWR_API
CreateDirectoryPath(const std::string
& path
)
134 SHCreateDirectoryExA(nullptr, path
.c_str(), nullptr);
137 #if defined(__APPLE__) || defined(FORCE_LINUX) || defined(__linux__) || defined(__gnu_linux__)
138 std::vector
<std::string
> pathSegments
;
139 SplitString(pathSegments
, path
, '/');
142 for (auto const& segment
: pathSegments
)
144 tmpPath
.push_back('/');
147 int result
= mkdir(tmpPath
.c_str(), 0777);
148 if (result
== -1 && errno
!= EEXIST
)
156 /// Execute Command (block until finished)
157 /// @returns process exit value
159 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
173 std::array
<WinPipe
, 3> hPipes
= {};
175 SECURITY_ATTRIBUTES saAttr
= { sizeof(SECURITY_ATTRIBUTES
) };
176 saAttr
.bInheritHandle
= TRUE
; //Pipe handles are inherited by child process.
177 saAttr
.lpSecurityDescriptor
= NULL
;
181 for (WinPipe
& p
: hPipes
)
183 if (!CreatePipe(&p
.hRead
, &p
.hWrite
, &saAttr
, 0))
191 for (WinPipe
& p
: hPipes
)
193 CloseHandle(p
.hRead
);
194 CloseHandle(p
.hWrite
);
200 STARTUPINFOA StartupInfo
{};
201 StartupInfo
.cb
= sizeof(STARTUPINFOA
);
202 StartupInfo
.dwFlags
= STARTF_USESTDHANDLES
;
203 StartupInfo
.dwFlags
|= STARTF_USESHOWWINDOW
;
204 StartupInfo
.wShowWindow
= SW_HIDE
;
207 StartupInfo
.hStdInput
= hPipes
[0].hRead
;
209 StartupInfo
.hStdOutput
= hPipes
[1].hWrite
;
210 StartupInfo
.hStdError
= hPipes
[2].hWrite
;
211 PROCESS_INFORMATION procInfo
{};
213 // CreateProcess can modify the string
214 std::string local_cmd
= cmd
;
216 BOOL ProcessValue
= CreateProcessA(
218 (LPSTR
)local_cmd
.c_str(),
223 (LPVOID
)pOptEnvStrings
,
228 if (ProcessValue
&& procInfo
.hProcess
)
230 auto ReadFromPipe
= [](HANDLE hPipe
, std::string
* pOutStr
)
237 if (!::PeekNamedPipe(hPipe
, NULL
, 0, NULL
, &dwAvail
, NULL
))
242 if (!dwAvail
) // no data available, return
247 if (!::ReadFile(hPipe
, buf
, std::min
<size_t>(sizeof(buf
) - 1, size_t(dwAvail
)), &dwRead
, NULL
) || !dwRead
)
249 // error, the child process might ended
260 bool bProcessEnded
= false;
261 size_t bytesWritten
= 0;
264 if (pOptStdIn
&& (pOptStdIn
->size() > bytesWritten
))
266 DWORD bytesToWrite
= static_cast<DWORD
>(pOptStdIn
->size()) - bytesWritten
;
269 pOptStdIn
->data() + bytesWritten
,
270 bytesToWrite
, &bytesToWrite
, nullptr))
272 // Failed to write to pipe
275 bytesWritten
+= bytesToWrite
;
278 // Give some timeslice (50ms), so we won't waste 100% cpu.
279 bProcessEnded
= (WaitForSingleObject(procInfo
.hProcess
, 50) == WAIT_OBJECT_0
);
281 ReadFromPipe(hPipes
[1].hRead
, pOptStdOut
);
282 ReadFromPipe(hPipes
[2].hRead
, pOptStdErr
);
284 while (!bProcessEnded
);
287 if (!GetExitCodeProcess(procInfo
.hProcess
, &exitVal
))
292 CloseHandle(procInfo
.hProcess
);
293 CloseHandle(procInfo
.hThread
);
298 for (WinPipe
& p
: hPipes
)
300 CloseHandle(p
.hRead
);
301 CloseHandle(p
.hWrite
);
306 // Non-Windows implementation