LLVM 20.0.0git
Process.inc
Go to the documentation of this file.
1//===- Win32/Process.cpp - Win32 Process Implementation ------- -*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This file provides the Win32 specific implementation of the Process class.
10//
11//===----------------------------------------------------------------------===//
12
19#include <malloc.h>
20#include <optional>
21
22// The Windows.h header must be after LLVM and standard headers.
24
25#include <direct.h>
26#include <io.h>
27#include <psapi.h>
28#include <shellapi.h>
29
30#if !defined(__MINGW32__)
31#pragma comment(lib, "psapi.lib")
32#pragma comment(lib, "shell32.lib")
33#endif
34
35//===----------------------------------------------------------------------===//
36//=== WARNING: Implementation here must contain only Win32 specific code
37//=== and must not be UNIX code
38//===----------------------------------------------------------------------===//
39
40#ifdef __MINGW32__
41// This ban should be lifted when MinGW 1.0+ has defined this value.
42#define _HEAPOK (-2)
43#endif
44
45using namespace llvm;
46
47Process::Pid Process::getProcessId() {
48 static_assert(sizeof(Pid) >= sizeof(DWORD),
49 "Process::Pid should be big enough to store DWORD");
50 return Pid(::GetCurrentProcessId());
51}
52
53// This function retrieves the page size using GetNativeSystemInfo() and is
54// present solely so it can be called once to initialize the self_process member
55// below.
56static unsigned computePageSize() {
57 // GetNativeSystemInfo() provides the physical page size which may differ
58 // from GetSystemInfo() in 32-bit applications running under WOW64.
59 SYSTEM_INFO info;
60 GetNativeSystemInfo(&info);
61 // FIXME: FileOffset in MapViewOfFile() should be aligned to not dwPageSize,
62 // but dwAllocationGranularity.
63 return static_cast<unsigned>(info.dwPageSize);
64}
65
66Expected<unsigned> Process::getPageSize() {
67 static unsigned Ret = computePageSize();
68 return Ret;
69}
70
71size_t Process::GetMallocUsage() {
72 _HEAPINFO hinfo;
73 hinfo._pentry = NULL;
74
75 size_t size = 0;
76
77 while (_heapwalk(&hinfo) == _HEAPOK)
78 size += hinfo._size;
79
80 return size;
81}
82
83void Process::GetTimeUsage(TimePoint<> &elapsed,
84 std::chrono::nanoseconds &user_time,
85 std::chrono::nanoseconds &sys_time) {
86 elapsed = std::chrono::system_clock::now();
87 ;
88
89 FILETIME ProcCreate, ProcExit, KernelTime, UserTime;
90 if (GetProcessTimes(GetCurrentProcess(), &ProcCreate, &ProcExit, &KernelTime,
91 &UserTime) == 0)
92 return;
93
94 user_time = toDuration(UserTime);
95 sys_time = toDuration(KernelTime);
96}
97
98// Some LLVM programs such as bugpoint produce core files as a normal part of
99// their operation. To prevent the disk from filling up, this configuration
100// item does what's necessary to prevent their generation.
101void Process::PreventCoreFiles() {
102 // Windows does have the concept of core files, called minidumps. However,
103 // disabling minidumps for a particular application extends past the lifetime
104 // of that application, which is the incorrect behavior for this API.
105 // Additionally, the APIs require elevated privileges to disable and re-
106 // enable minidumps, which makes this untenable. For more information, see
107 // WerAddExcludedApplication and WerRemoveExcludedApplication (Vista and
108 // later).
109 //
110 // Windows also has modal pop-up message boxes. As this method is used by
111 // bugpoint, preventing these pop-ups is additionally important.
112 SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX |
113 SEM_NOOPENFILEERRORBOX);
114
115 coreFilesPrevented = true;
116}
117
118/// Returns the environment variable \arg Name's value as a string encoded in
119/// UTF-8. \arg Name is assumed to be in UTF-8 encoding.
120std::optional<std::string> Process::GetEnv(StringRef Name) {
121 // Convert the argument to UTF-16 to pass it to _wgetenv().
123 if (windows::UTF8ToUTF16(Name, NameUTF16))
124 return std::nullopt;
125
126 // Environment variable can be encoded in non-UTF8 encoding, and there's no
127 // way to know what the encoding is. The only reliable way to look up
128 // multibyte environment variable is to use GetEnvironmentVariableW().
130 size_t Size = MAX_PATH;
131 do {
133 SetLastError(NO_ERROR);
134 Size = GetEnvironmentVariableW(NameUTF16.data(), Buf.data(), Buf.size());
135 if (Size == 0 && GetLastError() == ERROR_ENVVAR_NOT_FOUND)
136 return std::nullopt;
137
138 // Try again with larger buffer.
139 } while (Size > Buf.size());
140 Buf.truncate(Size);
141
142 // Convert the result from UTF-16 to UTF-8.
144 if (windows::UTF16ToUTF8(Buf.data(), Size, Res))
145 return std::nullopt;
146 return std::string(Res.data());
147}
148
149/// Perform wildcard expansion of Arg, or just push it into Args if it doesn't
150/// have wildcards or doesn't match any files.
151static std::error_code WildcardExpand(StringRef Arg,
153 StringSaver &Saver) {
154 std::error_code EC;
155
156 // Don't expand Arg if it does not contain any wildcard characters. This is
157 // the common case. Also don't wildcard expand /?. Always treat it as an
158 // option. Paths that start with \\?\ are absolute paths, and aren't
159 // expected to be used with wildcard expressions.
160 if (Arg.find_first_of("*?") == StringRef::npos || Arg == "/?" ||
161 Arg == "-?" || Arg.starts_with("\\\\?\\")) {
162 Args.push_back(Arg.data());
163 return EC;
164 }
165
166 // Convert back to UTF-16 so we can call FindFirstFileW.
168 EC = windows::UTF8ToUTF16(Arg, ArgW);
169 if (EC)
170 return EC;
171
172 // Search for matching files.
173 // FIXME: This assumes the wildcard is only in the file name and not in the
174 // directory portion of the file path. For example, it doesn't handle
175 // "*\foo.c" nor "s?c\bar.cpp".
176 WIN32_FIND_DATAW FileData;
177 HANDLE FindHandle = FindFirstFileW(ArgW.data(), &FileData);
178 if (FindHandle == INVALID_HANDLE_VALUE) {
179 Args.push_back(Arg.data());
180 return EC;
181 }
182
183 // Extract any directory part of the argument.
184 SmallString<MAX_PATH> Dir = Arg;
185 sys::path::remove_filename(Dir);
186 const int DirSize = Dir.size();
187
188 do {
189 SmallString<MAX_PATH> FileName;
190 EC = windows::UTF16ToUTF8(FileData.cFileName, wcslen(FileData.cFileName),
191 FileName);
192 if (EC)
193 break;
194
195 // Append FileName to Dir, and remove it afterwards.
196 llvm::sys::path::append(Dir, FileName);
197 Args.push_back(Saver.save(Dir.str()).data());
198 Dir.resize(DirSize);
199 } while (FindNextFileW(FindHandle, &FileData));
200
201 FindClose(FindHandle);
202 return EC;
203}
204
205static std::error_code GetExecutableName(SmallVectorImpl<char> &Filename) {
206 // The first argument may contain just the name of the executable (e.g.,
207 // "clang") rather than the full path, so swap it with the full path.
208 wchar_t ModuleName[MAX_PATH];
209 size_t Length = ::GetModuleFileNameW(NULL, ModuleName, MAX_PATH);
210 if (Length == 0 || Length == MAX_PATH) {
211 return mapWindowsError(GetLastError());
212 }
213
214 // If the first argument is a shortened (8.3) name (which is possible even
215 // if we got the module name), the driver will have trouble distinguishing it
216 // (e.g., clang.exe v. clang++.exe), so expand it now.
217 Length = GetLongPathNameW(ModuleName, ModuleName, MAX_PATH);
218 if (Length == 0)
219 return mapWindowsError(GetLastError());
220 if (Length > MAX_PATH) {
221 // We're not going to try to deal with paths longer than MAX_PATH, so we'll
222 // treat this as an error. GetLastError() returns ERROR_SUCCESS, which
223 // isn't useful, so we'll hardcode an appropriate error value.
224 return mapWindowsError(ERROR_INSUFFICIENT_BUFFER);
225 }
226
227 std::error_code EC = windows::UTF16ToUTF8(ModuleName, Length, Filename);
228 if (EC)
229 return EC;
230
231 // Make a copy of the filename since assign makes the StringRef invalid.
232 std::string Base = sys::path::filename(Filename.data()).str();
233 Filename.assign(Base.begin(), Base.end());
234 return std::error_code();
235}
236
237std::error_code
238windows::GetCommandLineArguments(SmallVectorImpl<const char *> &Args,
239 BumpPtrAllocator &Alloc) {
240 const wchar_t *CmdW = GetCommandLineW();
241 assert(CmdW);
242 std::error_code EC;
244 EC = windows::UTF16ToUTF8(CmdW, wcslen(CmdW), Cmd);
245 if (EC)
246 return EC;
247
249 StringSaver Saver(Alloc);
250 cl::TokenizeWindowsCommandLineFull(Cmd, Saver, TmpArgs, /*MarkEOLs=*/false);
251
252 for (const char *Arg : TmpArgs) {
253 EC = WildcardExpand(Arg, Args, Saver);
254 if (EC)
255 return EC;
256 }
257
258 if (Args.size() == 0)
259 return std::make_error_code(std::errc::invalid_argument);
260
261 SmallVector<char, MAX_PATH> Arg0(Args[0], Args[0] + strlen(Args[0]));
263 sys::path::remove_filename(Arg0);
264 EC = GetExecutableName(Filename);
265 if (EC)
266 return EC;
267 sys::path::make_preferred(Arg0);
268 sys::path::append(Arg0, Filename);
269 Args[0] = Saver.save(Arg0).data();
270 return std::error_code();
271}
272
273std::error_code Process::FixupStandardFileDescriptors() {
274 return std::error_code();
275}
276
277std::error_code Process::SafelyCloseFileDescriptor(int FD) {
278 if (::close(FD) < 0)
279 return errnoAsErrorCode();
280 return std::error_code();
281}
282
283bool Process::StandardInIsUserInput() { return FileDescriptorIsDisplayed(0); }
284
285bool Process::StandardOutIsDisplayed() { return FileDescriptorIsDisplayed(1); }
286
287bool Process::StandardErrIsDisplayed() { return FileDescriptorIsDisplayed(2); }
288
289bool Process::FileDescriptorIsDisplayed(int fd) {
290 DWORD Mode; // Unused
291 return (GetConsoleMode((HANDLE)_get_osfhandle(fd), &Mode) != 0);
292}
293
294unsigned Process::StandardOutColumns() {
295 unsigned Columns = 0;
296 CONSOLE_SCREEN_BUFFER_INFO csbi;
297 if (GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi))
298 Columns = csbi.dwSize.X;
299 return Columns;
300}
301
302unsigned Process::StandardErrColumns() {
303 unsigned Columns = 0;
304 CONSOLE_SCREEN_BUFFER_INFO csbi;
305 if (GetConsoleScreenBufferInfo(GetStdHandle(STD_ERROR_HANDLE), &csbi))
306 Columns = csbi.dwSize.X;
307 return Columns;
308}
309
310// The terminal always has colors.
311bool Process::FileDescriptorHasColors(int fd) {
312 return FileDescriptorIsDisplayed(fd);
313}
314
315bool Process::StandardOutHasColors() { return FileDescriptorHasColors(1); }
316
317bool Process::StandardErrHasColors() { return FileDescriptorHasColors(2); }
318
319static bool UseANSI = false;
320void Process::UseANSIEscapeCodes(bool enable) {
321#if defined(ENABLE_VIRTUAL_TERMINAL_PROCESSING)
322 if (enable) {
323 HANDLE Console = GetStdHandle(STD_OUTPUT_HANDLE);
324 DWORD Mode;
325 GetConsoleMode(Console, &Mode);
326 Mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
327 SetConsoleMode(Console, Mode);
328 }
329#endif
330 UseANSI = enable;
331}
332
333namespace {
334class DefaultColors {
335private:
336 WORD defaultColor;
337
338public:
339 DefaultColors() : defaultColor(GetCurrentColor()) {}
340 static unsigned GetCurrentColor() {
341 CONSOLE_SCREEN_BUFFER_INFO csbi;
342 if (GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi))
343 return csbi.wAttributes;
344 return 0;
345 }
346 WORD operator()() const { return defaultColor; }
347};
348
349DefaultColors defaultColors;
350
351WORD fg_color(WORD color) {
352 return color & (FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_INTENSITY |
353 FOREGROUND_RED);
354}
355
356WORD bg_color(WORD color) {
357 return color & (BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_INTENSITY |
358 BACKGROUND_RED);
359}
360} // namespace
361
362bool Process::ColorNeedsFlush() { return !UseANSI; }
363
364const char *Process::OutputBold(bool bg) {
365 if (UseANSI)
366 return "\033[1m";
367
368 WORD colors = DefaultColors::GetCurrentColor();
369 if (bg)
370 colors |= BACKGROUND_INTENSITY;
371 else
372 colors |= FOREGROUND_INTENSITY;
373 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), colors);
374 return 0;
375}
376
377const char *Process::OutputColor(char code, bool bold, bool bg) {
378 if (UseANSI)
379 return colorcodes[bg ? 1 : 0][bold ? 1 : 0][code & 15];
380
381 WORD current = DefaultColors::GetCurrentColor();
382 WORD colors;
383 if (bg) {
384 colors = ((code & 1) ? BACKGROUND_RED : 0) |
385 ((code & 2) ? BACKGROUND_GREEN : 0) |
386 ((code & 4) ? BACKGROUND_BLUE : 0);
387 if (bold)
388 colors |= BACKGROUND_INTENSITY;
389 colors |= fg_color(current);
390 } else {
391 colors = ((code & 1) ? FOREGROUND_RED : 0) |
392 ((code & 2) ? FOREGROUND_GREEN : 0) |
393 ((code & 4) ? FOREGROUND_BLUE : 0);
394 if (bold)
395 colors |= FOREGROUND_INTENSITY;
396 colors |= bg_color(current);
397 }
398 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), colors);
399 return 0;
400}
401
402static WORD GetConsoleTextAttribute(HANDLE hConsoleOutput) {
403 CONSOLE_SCREEN_BUFFER_INFO info;
404 GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &info);
405 return info.wAttributes;
406}
407
408const char *Process::OutputReverse() {
409 if (UseANSI)
410 return "\033[7m";
411
412 const WORD attributes =
413 GetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE));
414
415 const WORD foreground_mask = FOREGROUND_BLUE | FOREGROUND_GREEN |
416 FOREGROUND_RED | FOREGROUND_INTENSITY;
417 const WORD background_mask = BACKGROUND_BLUE | BACKGROUND_GREEN |
418 BACKGROUND_RED | BACKGROUND_INTENSITY;
419 const WORD color_mask = foreground_mask | background_mask;
420
421 WORD new_attributes =
422 ((attributes & FOREGROUND_BLUE) ? BACKGROUND_BLUE : 0) |
423 ((attributes & FOREGROUND_GREEN) ? BACKGROUND_GREEN : 0) |
424 ((attributes & FOREGROUND_RED) ? BACKGROUND_RED : 0) |
425 ((attributes & FOREGROUND_INTENSITY) ? BACKGROUND_INTENSITY : 0) |
426 ((attributes & BACKGROUND_BLUE) ? FOREGROUND_BLUE : 0) |
427 ((attributes & BACKGROUND_GREEN) ? FOREGROUND_GREEN : 0) |
428 ((attributes & BACKGROUND_RED) ? FOREGROUND_RED : 0) |
429 ((attributes & BACKGROUND_INTENSITY) ? FOREGROUND_INTENSITY : 0) | 0;
430 new_attributes = (attributes & ~color_mask) | (new_attributes & color_mask);
431
432 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), new_attributes);
433 return 0;
434}
435
436const char *Process::ResetColor() {
437 if (UseANSI)
438 return "\033[0m";
439 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), defaultColors());
440 return 0;
441}
442
443static unsigned GetRandomNumberSeed() {
444 // Generate a random number seed from the millisecond-resolution Windows
445 // system clock and the current process id.
446 FILETIME Time;
447 GetSystemTimeAsFileTime(&Time);
448 DWORD Pid = GetCurrentProcessId();
449 return hash_combine(Time.dwHighDateTime, Time.dwLowDateTime, Pid);
450}
451
452static unsigned GetPseudoRandomNumber() {
453 // Arrange to call srand once when this function is first used, and
454 // otherwise (if GetRandomNumber always succeeds in using
455 // CryptGenRandom) don't bother at all.
456 static int x = (static_cast<void>(::srand(GetRandomNumberSeed())), 0);
457 (void)x;
458 return ::rand();
459}
460
461unsigned Process::GetRandomNumber() {
462 // Try to use CryptGenRandom.
463 HCRYPTPROV HCPC;
464 if (::CryptAcquireContextW(&HCPC, NULL, NULL, PROV_RSA_FULL,
465 CRYPT_VERIFYCONTEXT)) {
466 ScopedCryptContext CryptoProvider(HCPC);
467 unsigned Ret;
468 if (::CryptGenRandom(CryptoProvider, sizeof(Ret),
469 reinterpret_cast<BYTE *>(&Ret)))
470 return Ret;
471 }
472
473 // If that fails, fall back to pseudo-random numbers.
474 return GetPseudoRandomNumber();
475}
476
477typedef NTSTATUS(WINAPI *RtlGetVersionPtr)(PRTL_OSVERSIONINFOW);
478#define STATUS_SUCCESS ((NTSTATUS)0x00000000L)
479
480static RTL_OSVERSIONINFOEXW GetWindowsVer() {
481 auto getVer = []() -> RTL_OSVERSIONINFOEXW {
482 HMODULE hMod = ::GetModuleHandleW(L"ntdll.dll");
483 assert(hMod);
484
485 auto getVer =
486 (RtlGetVersionPtr)(void *)::GetProcAddress(hMod, "RtlGetVersion");
487 assert(getVer);
488
489 RTL_OSVERSIONINFOEXW info{};
490 info.dwOSVersionInfoSize = sizeof(info);
491 NTSTATUS r = getVer((PRTL_OSVERSIONINFOW)&info);
492 (void)r;
493 assert(r == STATUS_SUCCESS);
494
495 return info;
496 };
497 static RTL_OSVERSIONINFOEXW info = getVer();
498 return info;
499}
500
502 RTL_OSVERSIONINFOEXW info = GetWindowsVer();
503 return llvm::VersionTuple(info.dwMajorVersion, info.dwMinorVersion, 0,
504 info.dwBuildNumber);
505}
506
508 // Windows 8 is version 6.2, service pack 0.
509 return GetWindowsOSVersion() >= llvm::VersionTuple(6, 2, 0, 0);
510}
511
513 RTL_OSVERSIONINFOEXW info = GetWindowsVer();
514 auto ver = llvm::VersionTuple(info.dwMajorVersion, info.dwMinorVersion, 0,
515 info.dwBuildNumber);
516
517 // Windows Server 2022
518 if (info.wProductType == VER_NT_SERVER)
519 return ver >= llvm::VersionTuple(10, 0, 0, 20348);
520
521 // Windows 11
522 return ver >= llvm::VersionTuple(10, 0, 0, 22000);
523}
524
525[[noreturn]] void Process::ExitNoCleanup(int RetCode) {
526 TerminateProcess(GetCurrentProcess(), RetCode);
527 llvm_unreachable("TerminateProcess doesn't return");
528}
This file defines the BumpPtrAllocator interface.
std::string Name
uint64_t Size
lazy value info
static bool coreFilesPrevented
Definition: Process.cpp:107
static const char colorcodes[2][2][16][11]
Definition: Process.cpp:99
static cl::opt< RegAllocEvictionAdvisorAnalysis::AdvisorMode > Mode("regalloc-enable-advisor", cl::Hidden, cl::init(RegAllocEvictionAdvisorAnalysis::AdvisorMode::Default), cl::desc("Enable regalloc advisor mode"), cl::values(clEnumValN(RegAllocEvictionAdvisorAnalysis::AdvisorMode::Default, "default", "Default"), clEnumValN(RegAllocEvictionAdvisorAnalysis::AdvisorMode::Release, "release", "precompiled"), clEnumValN(RegAllocEvictionAdvisorAnalysis::AdvisorMode::Development, "development", "for training")))
assert(ImpDefSCC.getReg()==AMDGPU::SCC &&ImpDefSCC.isDef())
Allocate memory in an ever growing pool, as if by bump-pointer.
Definition: Allocator.h:66
Tagged union holding either a T or a Error.
Definition: Error.h:481
SmallString - A SmallString is just a SmallVector with methods and accessors that make it work better...
Definition: SmallString.h:26
StringRef str() const
Explicit conversion to StringRef.
Definition: SmallString.h:254
size_t size() const
Definition: SmallVector.h:91
This class consists of common code factored out of the SmallVector class to reduce code duplication b...
Definition: SmallVector.h:586
void resize_for_overwrite(size_type N)
Like resize, but T is POD, the new values won't be initialized.
Definition: SmallVector.h:654
void truncate(size_type N)
Like resize, but requires that N is less than size().
Definition: SmallVector.h:657
void resize(size_type N)
Definition: SmallVector.h:651
pointer data()
Return a pointer to the vector's buffer, even if empty().
Definition: SmallVector.h:299
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
Definition: SmallVector.h:1209
StringRef - Represent a constant reference to a string, i.e.
Definition: StringRef.h:50
bool starts_with(StringRef Prefix) const
Check if this string starts with the given Prefix.
Definition: StringRef.h:250
constexpr const char * data() const
data - Get a pointer to the start of the string (which may not be null terminated).
Definition: StringRef.h:131
size_t find_first_of(char C, size_t From=0) const
Find the first character in the string that is C, or npos if not found.
Definition: StringRef.h:362
Saves strings in the provided stable storage and returns a StringRef with a stable character pointer.
Definition: StringSaver.h:21
StringRef save(const char *S)
Definition: StringSaver.h:30
Represents a version number in the form major[.minor[.subminor[.build]]].
Definition: VersionTuple.h:29
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
constexpr char Args[]
Key for Kernel::Metadata::mArgs.
void append(SmallVectorImpl< char > &path, const Twine &a, const Twine &b="", const Twine &c="", const Twine &d="")
Append to path.
Definition: Path.cpp:457
std::chrono::nanoseconds toDuration(FILETIME Time)
std::chrono::time_point< std::chrono::system_clock, D > TimePoint
A time point on the system clock.
Definition: Chrono.h:34
This is an optimization pass for GlobalISel generic memory operations.
Definition: AddressRanges.h:18
@ Length
Definition: DWP.cpp:480
auto size(R &&Range, std::enable_if_t< std::is_base_of< std::random_access_iterator_tag, typename std::iterator_traits< decltype(Range.begin())>::iterator_category >::value, void > *=nullptr)
Get the size of a range.
Definition: STLExtras.h:1680
llvm::VersionTuple GetWindowsOSVersion()
Returns the Windows version as Major.Minor.0.BuildNumber.
bool RunningWindows8OrGreater()
Determines if the program is running on Windows 8 or newer.
bool RunningWindows11OrGreater()
Determines if the program is running on Windows 11 or Windows Server 2022.
hash_code hash_combine(const Ts &...args)
Combine values into a single hash_code.
Definition: Hashing.h:593
std::error_code errnoAsErrorCode()
Helper to get errno as an std::error_code.
Definition: Error.h:1221
std::error_code mapWindowsError(unsigned EV)