• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #if defined(__APPLE__) || defined(FORCE_LINUX) || defined(__linux__) || defined(__gnu_linux__)
115 static void
SplitString(std::vector<std::string> & out_segments,const std::string & input,char splitToken)116 SplitString(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 
CreateDirectoryPath(const std::string & path)132 void 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
ExecCmd(const std::string & cmd,const char * pOptEnvStrings,std::string * pOptStdOut,std::string * pOptStdErr,const std::string * pOptStdIn)159 int 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