• 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 static void
SplitString(std::vector<std::string> & out_segments,const std::string & input,char splitToken)115 SplitString(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 
CreateDirectoryPath(const std::string & path)130 void 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
ExecCmd(const std::string & cmd,const char * pOptEnvStrings,std::string * pOptStdOut,std::string * pOptStdErr,const std::string * pOptStdIn)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
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