//
//===----------------------------------------------------------------------===//
-#include "Windows.h"
-#include <psapi.h>
+#include "llvm/Support/Allocator.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/WindowsError.h"
#include <malloc.h>
-#include <io.h>
+
+// The Windows.h header must be after LLVM and standard headers.
+#include "WindowsSupport.h"
+
#include <direct.h>
+#include <io.h>
+#include <psapi.h>
+#include <shellapi.h>
#ifdef __MINGW32__
#if (HAVE_LIBPSAPI != 1)
#error "libpsapi.a should be present"
#endif
+ #if (HAVE_LIBSHELL32 != 1)
+ #error "libshell32.a should be present"
+ #endif
#else
#pragma comment(lib, "psapi.lib")
+ #pragma comment(lib, "shell32.lib")
#endif
//===----------------------------------------------------------------------===//
# define _HEAPOK (-2)
#endif
-namespace llvm {
+using namespace llvm;
using namespace sys;
-// This function retrieves the page size using GetSystemInfo and is present
-// solely so it can be called once in Process::GetPageSize to initialize the
-// static variable PageSize.
-inline unsigned GetPageSizeOnce() {
- // NOTE: A 32-bit application running under WOW64 is supposed to use
- // GetNativeSystemInfo. However, this interface is not present prior
- // to Windows XP so to use it requires dynamic linking. It is not clear
- // how this affects the reported page size, if at all. One could argue
- // that LLVM ought to run as 64-bits on a 64-bit system, anyway.
+process::id_type self_process::get_id() {
+ return GetCurrentProcessId();
+}
+
+static TimeValue getTimeValueFromFILETIME(FILETIME Time) {
+ ULARGE_INTEGER TimeInteger;
+ TimeInteger.LowPart = Time.dwLowDateTime;
+ TimeInteger.HighPart = Time.dwHighDateTime;
+
+ // FILETIME's are # of 100 nanosecond ticks (1/10th of a microsecond)
+ return TimeValue(
+ static_cast<TimeValue::SecondsType>(TimeInteger.QuadPart / 10000000),
+ static_cast<TimeValue::NanoSecondsType>(
+ (TimeInteger.QuadPart % 10000000) * 100));
+}
+
+TimeValue self_process::get_user_time() const {
+ FILETIME ProcCreate, ProcExit, KernelTime, UserTime;
+ if (GetProcessTimes(GetCurrentProcess(), &ProcCreate, &ProcExit, &KernelTime,
+ &UserTime) == 0)
+ return TimeValue();
+
+ return getTimeValueFromFILETIME(UserTime);
+}
+
+TimeValue self_process::get_system_time() const {
+ FILETIME ProcCreate, ProcExit, KernelTime, UserTime;
+ if (GetProcessTimes(GetCurrentProcess(), &ProcCreate, &ProcExit, &KernelTime,
+ &UserTime) == 0)
+ return TimeValue();
+
+ return getTimeValueFromFILETIME(KernelTime);
+}
+
+// This function retrieves the page size using GetNativeSystemInfo() and is
+// present solely so it can be called once to initialize the self_process member
+// below.
+static unsigned getPageSize() {
+ // GetNativeSystemInfo() provides the physical page size which may differ
+ // from GetSystemInfo() in 32-bit applications running under WOW64.
SYSTEM_INFO info;
- GetSystemInfo(&info);
+ GetNativeSystemInfo(&info);
+ // FIXME: FileOffset in MapViewOfFile() should be aligned to not dwPageSize,
+ // but dwAllocationGranularity.
return static_cast<unsigned>(info.dwPageSize);
}
-unsigned
-Process::GetPageSize() {
- static const unsigned PageSize = GetPageSizeOnce();
- return PageSize;
+// This constructor guaranteed to be run exactly once on a single thread, and
+// sets up various process invariants that can be queried cheaply from then on.
+self_process::self_process() : PageSize(getPageSize()) {
}
+
size_t
Process::GetMallocUsage()
{
return size;
}
-size_t
-Process::GetTotalMemoryUsage()
-{
- PROCESS_MEMORY_COUNTERS pmc;
- GetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc));
- return pmc.PagefileUsage;
-}
-
-void
-Process::GetTimeUsage(
- TimeValue& elapsed, TimeValue& user_time, TimeValue& sys_time)
-{
+void Process::GetTimeUsage(TimeValue &elapsed, TimeValue &user_time,
+ TimeValue &sys_time) {
elapsed = TimeValue::now();
- uint64_t ProcCreate, ProcExit, KernelTime, UserTime;
- GetProcessTimes(GetCurrentProcess(), (FILETIME*)&ProcCreate,
- (FILETIME*)&ProcExit, (FILETIME*)&KernelTime,
- (FILETIME*)&UserTime);
+ FILETIME ProcCreate, ProcExit, KernelTime, UserTime;
+ if (GetProcessTimes(GetCurrentProcess(), &ProcCreate, &ProcExit, &KernelTime,
+ &UserTime) == 0)
+ return;
- // FILETIME's are # of 100 nanosecond ticks (1/10th of a microsecond)
- user_time.seconds( UserTime / 10000000 );
- user_time.nanoseconds( unsigned(UserTime % 10000000) * 100 );
- sys_time.seconds( KernelTime / 10000000 );
- sys_time.nanoseconds( unsigned(KernelTime % 10000000) * 100 );
-}
-
-int Process::GetCurrentUserId()
-{
- return 65536;
-}
-
-int Process::GetCurrentGroupId()
-{
- return 65536;
+ user_time = getTimeValueFromFILETIME(UserTime);
+ sys_time = getTimeValueFromFILETIME(KernelTime);
}
// Some LLVM programs such as bugpoint produce core files as a normal part of
-// their operation. To prevent the disk from filling up, this configuration item
-// does what's necessary to prevent their generation.
+// their operation. To prevent the disk from filling up, this configuration
+// item does what's necessary to prevent their generation.
void Process::PreventCoreFiles() {
- // Windows doesn't do core files, but it does do modal pop-up message
- // boxes. As this method is used by bugpoint, preventing these pop-ups
- // is the moral equivalent of suppressing core files.
+ // Windows does have the concept of core files, called minidumps. However,
+ // disabling minidumps for a particular application extends past the lifetime
+ // of that application, which is the incorrect behavior for this API.
+ // Additionally, the APIs require elevated privileges to disable and re-
+ // enable minidumps, which makes this untenable. For more information, see
+ // WerAddExcludedApplication and WerRemoveExcludedApplication (Vista and
+ // later).
+ //
+ // Windows also has modal pop-up message boxes. As this method is used by
+ // bugpoint, preventing these pop-ups is additionally important.
SetErrorMode(SEM_FAILCRITICALERRORS |
SEM_NOGPFAULTERRORBOX |
SEM_NOOPENFILEERRORBOX);
}
+/// Returns the environment variable \arg Name's value as a string encoded in
+/// UTF-8. \arg Name is assumed to be in UTF-8 encoding.
+Optional<std::string> Process::GetEnv(StringRef Name) {
+ // Convert the argument to UTF-16 to pass it to _wgetenv().
+ SmallVector<wchar_t, 128> NameUTF16;
+ if (windows::UTF8ToUTF16(Name, NameUTF16))
+ return None;
+
+ // Environment variable can be encoded in non-UTF8 encoding, and there's no
+ // way to know what the encoding is. The only reliable way to look up
+ // multibyte environment variable is to use GetEnvironmentVariableW().
+ SmallVector<wchar_t, MAX_PATH> Buf;
+ size_t Size = MAX_PATH;
+ do {
+ Buf.reserve(Size);
+ Size =
+ GetEnvironmentVariableW(NameUTF16.data(), Buf.data(), Buf.capacity());
+ if (Size == 0)
+ return None;
+
+ // Try again with larger buffer.
+ } while (Size > Buf.capacity());
+ Buf.set_size(Size);
+
+ // Convert the result from UTF-16 to UTF-8.
+ SmallVector<char, MAX_PATH> Res;
+ if (windows::UTF16ToUTF8(Buf.data(), Size, Res))
+ return None;
+ return std::string(Res.data());
+}
+
+static std::error_code windows_error(DWORD E) {
+ return mapWindowsError(E);
+}
+
+static void AllocateAndPush(const SmallVectorImpl<char> &S,
+ SmallVectorImpl<const char *> &Vector,
+ SpecificBumpPtrAllocator<char> &Allocator) {
+ char *Buffer = Allocator.Allocate(S.size() + 1);
+ ::memcpy(Buffer, S.data(), S.size());
+ Buffer[S.size()] = '\0';
+ Vector.push_back(Buffer);
+}
+
+/// Convert Arg from UTF-16 to UTF-8 and push it onto Args.
+static std::error_code
+ConvertAndPushArg(const wchar_t *Arg, SmallVectorImpl<const char *> &Args,
+ SpecificBumpPtrAllocator<char> &Allocator) {
+ SmallVector<char, MAX_PATH> ArgString;
+ if (std::error_code ec = windows::UTF16ToUTF8(Arg, wcslen(Arg), ArgString))
+ return ec;
+ AllocateAndPush(ArgString, Args, Allocator);
+ return std::error_code();
+}
+
+/// \brief Perform wildcard expansion of Arg, or just push it into Args if it
+/// doesn't have wildcards or doesn't match any files.
+static std::error_code
+WildcardExpand(const wchar_t *Arg, SmallVectorImpl<const char *> &Args,
+ SpecificBumpPtrAllocator<char> &Allocator) {
+ if (!wcspbrk(Arg, L"*?")) {
+ // Arg does not contain any wildcard characters. This is the common case.
+ return ConvertAndPushArg(Arg, Args, Allocator);
+ }
+
+ if (wcscmp(Arg, L"/?") == 0 || wcscmp(Arg, L"-?") == 0) {
+ // Don't wildcard expand /?. Always treat it as an option.
+ return ConvertAndPushArg(Arg, Args, Allocator);
+ }
+
+ // Extract any directory part of the argument.
+ SmallVector<char, MAX_PATH> Dir;
+ if (std::error_code ec = windows::UTF16ToUTF8(Arg, wcslen(Arg), Dir))
+ return ec;
+ sys::path::remove_filename(Dir);
+ const int DirSize = Dir.size();
+
+ // Search for matching files.
+ WIN32_FIND_DATAW FileData;
+ HANDLE FindHandle = FindFirstFileW(Arg, &FileData);
+ if (FindHandle == INVALID_HANDLE_VALUE) {
+ return ConvertAndPushArg(Arg, Args, Allocator);
+ }
+
+ std::error_code ec;
+ do {
+ SmallVector<char, MAX_PATH> FileName;
+ ec = windows::UTF16ToUTF8(FileData.cFileName, wcslen(FileData.cFileName),
+ FileName);
+ if (ec)
+ break;
+
+ // Push the filename onto Dir, and remove it afterwards.
+ llvm::sys::path::append(Dir, StringRef(FileName.data(), FileName.size()));
+ AllocateAndPush(Dir, Args, Allocator);
+ Dir.resize(DirSize);
+ } while (FindNextFileW(FindHandle, &FileData));
+
+ FindClose(FindHandle);
+ return ec;
+}
+
+std::error_code
+Process::GetArgumentVector(SmallVectorImpl<const char *> &Args,
+ ArrayRef<const char *>,
+ SpecificBumpPtrAllocator<char> &ArgAllocator) {
+ int ArgCount;
+ wchar_t **UnicodeCommandLine =
+ CommandLineToArgvW(GetCommandLineW(), &ArgCount);
+ if (!UnicodeCommandLine)
+ return windows_error(::GetLastError());
+
+ Args.reserve(ArgCount);
+ std::error_code ec;
+
+ for (int i = 0; i < ArgCount; ++i) {
+ ec = WildcardExpand(UnicodeCommandLine[i], Args, ArgAllocator);
+ if (ec)
+ break;
+ }
+
+ LocalFree(UnicodeCommandLine);
+ return ec;
+}
+
bool Process::StandardInIsUserInput() {
return FileDescriptorIsDisplayed(0);
}
return FileDescriptorHasColors(2);
}
+static bool UseANSI = false;
+void Process::UseANSIEscapeCodes(bool enable) {
+ UseANSI = enable;
+}
+
namespace {
class DefaultColors
{
}
bool Process::ColorNeedsFlush() {
- return true;
+ return !UseANSI;
}
const char *Process::OutputBold(bool bg) {
+ if (UseANSI) return "\033[1m";
+
WORD colors = DefaultColors::GetCurrentColor();
if (bg)
colors |= BACKGROUND_INTENSITY;
}
const char *Process::OutputColor(char code, bool bold, bool bg) {
+ if (UseANSI) return colorcodes[bg?1:0][bold?1:0][code&7];
+
WORD colors;
if (bg) {
colors = ((code&1) ? BACKGROUND_RED : 0) |
}
const char *Process::OutputReverse() {
+ if (UseANSI) return "\033[7m";
+
const WORD attributes
= GetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE));
}
const char *Process::ResetColor() {
+ if (UseANSI) return "\033[0m";
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), defaultColors());
return 0;
}
+unsigned Process::GetRandomNumber() {
+ HCRYPTPROV HCPC;
+ if (!::CryptAcquireContextW(&HCPC, NULL, NULL, PROV_RSA_FULL,
+ CRYPT_VERIFYCONTEXT))
+ report_fatal_error("Could not acquire a cryptographic context");
+
+ ScopedCryptContext CryptoProvider(HCPC);
+ unsigned Ret;
+ if (!::CryptGenRandom(CryptoProvider, sizeof(Ret),
+ reinterpret_cast<BYTE *>(&Ret)))
+ report_fatal_error("Could not generate a random number");
+ return Ret;
}