/************************************************************************** * * Copyright 2010-2015 VMware, Inc. * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * **************************************************************************/ #ifdef _WIN32 #include #include #include #include #include #include #include "os.hpp" #include "os_string.hpp" namespace os { String getProcessName(void) { String path; DWORD size = MAX_PATH; char *buf = path.buf(size); DWORD nWritten = GetModuleFileNameA(NULL, buf, size); (void)nWritten; path.truncate(); return path; } String getCurrentDir(void) { String path; DWORD size = MAX_PATH; char *buf = path.buf(size); DWORD ret = GetCurrentDirectoryA(size, buf); (void)ret; buf[size - 1] = 0; path.truncate(); return path; } String getConfigDir(void) { String path; char *buf = path.buf(MAX_PATH); HRESULT hr = SHGetFolderPathA(NULL, CSIDL_LOCAL_APPDATA, NULL, 0, buf); if (SUCCEEDED(hr)) { path.truncate(); } else { path.truncate(0); } return path; } bool createDirectory(const String &path) { return CreateDirectoryA(path, NULL); } bool String::exists(void) const { DWORD attrs = GetFileAttributesA(str()); return attrs != INVALID_FILE_ATTRIBUTES; } bool copyFile(const String &srcFileName, const String &dstFileName, bool override) { return CopyFileA(srcFileName, dstFileName, !override); } bool removeFile(const String &srcFilename) { return DeleteFileA(srcFilename); } /** * Determine whether an argument should be quoted. */ static bool needsQuote(const char *arg) { char c; while (true) { c = *arg++; if (c == '\0') { break; } if (c == ' ' || c == '\t' || c == '\"') { return true; } if (c == '\\') { c = *arg++; if (c == '\0') { break; } if (c == '"') { return true; } } } return false; } static void quoteArg(std::string &s, const char *arg) { char c; unsigned backslashes = 0; s.push_back('"'); while (true) { c = *arg++; if (c == '\0') { break; } else if (c == '"') { while (backslashes) { s.push_back('\\'); --backslashes; } s.push_back('\\'); } else { if (c == '\\') { ++backslashes; } else { backslashes = 0; } } s.push_back(c); } s.push_back('"'); } int execute(char * const * args) { std::string commandLine; const char *arg0 = *args; const char *arg; char sep = 0; while ((arg = *args++) != NULL) { if (sep) { commandLine.push_back(sep); } if (needsQuote(arg)) { quoteArg(commandLine, arg); } else { commandLine.append(arg); } sep = ' '; } STARTUPINFOA startupInfo; memset(&startupInfo, 0, sizeof(startupInfo)); startupInfo.cb = sizeof(startupInfo); PROCESS_INFORMATION processInformation; if (!CreateProcessA(NULL, const_cast(commandLine.c_str()), // only modified by CreateProcessW 0, // process attributes 0, // thread attributes FALSE, // inherit handles 0, // creation flags, NULL, // environment NULL, // current directory &startupInfo, &processInformation )) { log("error: failed to execute %s\n", arg0); return -1; } WaitForSingleObject(processInformation.hProcess, INFINITE); DWORD exitCode = ~0UL; GetExitCodeProcess(processInformation.hProcess, &exitCode); CloseHandle(processInformation.hProcess); CloseHandle(processInformation.hThread); return (int)exitCode; } #ifndef HAVE_EXTERNAL_OS_LOG void log(const char *format, ...) { char buf[4096]; va_list ap; va_start(ap, format); fflush(stdout); vsnprintf(buf, sizeof buf, format, ap); va_end(ap); OutputDebugStringA(buf); /* * Also write the message to stderr, when a debugger is not present (to * avoid duplicate messages in command line debuggers). */ #if _WIN32_WINNT > 0x0400 if (!IsDebuggerPresent()) { fflush(stdout); fputs(buf, stderr); fflush(stderr); } #endif } #endif /* !HAVE_EXTERNAL_OS_LOG */ long long timeFrequency = 0LL; void abort(void) { TerminateProcess(GetCurrentProcess(), 3); #if defined(__GNUC__) __builtin_unreachable(); #endif } void breakpoint(void) { #if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) asm("int3"); #elif defined(_MSC_VER) __debugbreak(); #else DebugBreak(); #endif } #ifndef DBG_PRINTEXCEPTION_C #define DBG_PRINTEXCEPTION_C 0x40010006 #endif #ifndef DBG_PRINTEXCEPTION_WIDE_C #define DBG_PRINTEXCEPTION_WIDE_C 0x4001000A #endif static PVOID prevExceptionFilter = NULL; static void (*gCallback)(void) = NULL; static LONG CALLBACK unhandledExceptionHandler(PEXCEPTION_POINTERS pExceptionInfo) { /* * Before Vista KiUserExceptionDispatcher does not clear the direction * flag. * * See also: * - https://bugs.chromium.org/p/nativeclient/issues/detail?id=1495 */ #ifdef _MSC_VER #ifndef _WIN64 __asm cld; #endif #else asm("cld"); #endif PEXCEPTION_RECORD pExceptionRecord = pExceptionInfo->ExceptionRecord; DWORD ExceptionCode = pExceptionRecord->ExceptionCode; // https://msdn.microsoft.com/en-us/library/het71c37.aspx switch (ExceptionCode >> 30) { case 0: // success return EXCEPTION_CONTINUE_SEARCH; case 1: // informational case 2: // warning case 3: // error break; } /* * Ignore OutputDebugStringA/W exceptions. */ if (ExceptionCode == DBG_PRINTEXCEPTION_C || ExceptionCode == DBG_PRINTEXCEPTION_WIDE_C) { return EXCEPTION_CONTINUE_SEARCH; } /* * Ignore C++ exceptions, as some applications generate a lot of these with * no harm. But don't ignore them on debug builds, as bugs in apitrace can * easily lead the applicationa and/or runtime to raise them, and catching * them helps debugging. * * See also: * - http://blogs.msdn.com/b/oldnewthing/archive/2010/07/30/10044061.aspx * - http://support.microsoft.com/kb/185294 */ #ifdef NDEBUG if (ExceptionCode == 0xe06d7363) { return EXCEPTION_CONTINUE_SEARCH; } #endif /* * Ignore thread naming exception. * * http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx */ if (ExceptionCode == 0x406d1388) { return EXCEPTION_CONTINUE_SEARCH; } /* * Ignore .NET exceptions. * * http://ig2600.blogspot.co.uk/2011/01/why-do-i-keep-getting-exception-code.html * https://github.com/dotnet/coreclr/blob/master/src/jit/importer.cpp */ if (ExceptionCode == 0xe0434352 || ExceptionCode == 0xe0564552) { return EXCEPTION_CONTINUE_SEARCH; } log("apitrace: warning: caught exception 0x%08lx\n", pExceptionRecord->ExceptionCode); static int recursion_count = 0; if (recursion_count) { fputs("apitrace: warning: recursion handling exception\n", stderr); } else { if (gCallback) { ++recursion_count; gCallback(); --recursion_count; } } return EXCEPTION_CONTINUE_SEARCH; } void setExceptionCallback(void (*callback)(void)) { assert(!gCallback); if (!gCallback) { gCallback = callback; assert(!prevExceptionFilter); prevExceptionFilter = AddVectoredExceptionHandler(0, unhandledExceptionHandler); } } void resetExceptionCallback(void) { if (gCallback) { RemoveVectoredExceptionHandler(prevExceptionFilter); gCallback = NULL; } } } /* namespace os */ #endif // defined(_WIN32)