1218885Sdim//===- Win32/Signals.cpp - Win32 Signals Implementation ---------*- C++ -*-===//
2218885Sdim//
3353358Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4353358Sdim// See https://llvm.org/LICENSE.txt for license information.
5353358Sdim// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6218885Sdim//
7218885Sdim//===----------------------------------------------------------------------===//
8218885Sdim//
9218885Sdim// This file provides the Win32 specific implementation of the Signals class.
10218885Sdim//
11218885Sdim//===----------------------------------------------------------------------===//
12341825Sdim#include "llvm/Support/ConvertUTF.h"
13261991Sdim#include "llvm/Support/FileSystem.h"
14309124Sdim#include "llvm/Support/Path.h"
15309124Sdim#include "llvm/Support/Process.h"
16309124Sdim#include "llvm/Support/WindowsError.h"
17249423Sdim#include <algorithm>
18309124Sdim#include <io.h>
19288943Sdim#include <signal.h>
20218885Sdim#include <stdio.h>
21218885Sdim
22288943Sdim#include "llvm/Support/Format.h"
23288943Sdim#include "llvm/Support/raw_ostream.h"
24288943Sdim
25276479Sdim// The Windows.h header must be after LLVM and standard headers.
26360784Sdim#include "llvm/Support/Windows/WindowsSupport.h"
27276479Sdim
28218885Sdim#ifdef __MINGW32__
29218885Sdim #include <imagehlp.h>
30218885Sdim#else
31314564Sdim #include <crtdbg.h>
32218885Sdim #include <dbghelp.h>
33218885Sdim#endif
34218885Sdim#include <psapi.h>
35218885Sdim
36226633Sdim#ifdef _MSC_VER
37226633Sdim #pragma comment(lib, "psapi.lib")
38226633Sdim#elif __MINGW32__
39226633Sdim // The version of g++ that comes with MinGW does *not* properly understand
40226633Sdim // the ll format specifier for printf. However, MinGW passes the format
41226633Sdim // specifiers on to the MSVCRT entirely, and the CRT understands the ll
42226633Sdim // specifier. So these warnings are spurious in this case. Since we compile
43226633Sdim // with -Wall, this will generate these warnings which should be ignored. So
44226633Sdim // we will turn off the warnings for this just file. However, MinGW also does
45226633Sdim // not support push and pop for diagnostics, so we have to manually turn it
46226633Sdim // back on at the end of the file.
47226633Sdim #pragma GCC diagnostic ignored "-Wformat"
48226633Sdim #pragma GCC diagnostic ignored "-Wformat-extra-args"
49218885Sdim
50226633Sdim #if !defined(__MINGW64_VERSION_MAJOR)
51226633Sdim // MinGW.org does not have updated support for the 64-bit versions of the
52226633Sdim // DebugHlp APIs. So we will have to load them manually. The structures and
53226633Sdim // method signatures were pulled from DbgHelp.h in the Windows Platform SDK,
54226633Sdim // and adjusted for brevity.
55226633Sdim typedef struct _IMAGEHLP_LINE64 {
56226633Sdim   DWORD    SizeOfStruct;
57226633Sdim   PVOID    Key;
58226633Sdim   DWORD    LineNumber;
59226633Sdim   PCHAR    FileName;
60226633Sdim   DWORD64  Address;
61226633Sdim } IMAGEHLP_LINE64, *PIMAGEHLP_LINE64;
62226633Sdim
63226633Sdim typedef struct _IMAGEHLP_SYMBOL64 {
64226633Sdim   DWORD   SizeOfStruct;
65226633Sdim   DWORD64 Address;
66226633Sdim   DWORD   Size;
67226633Sdim   DWORD   Flags;
68226633Sdim   DWORD   MaxNameLength;
69226633Sdim   CHAR    Name[1];
70226633Sdim } IMAGEHLP_SYMBOL64, *PIMAGEHLP_SYMBOL64;
71226633Sdim
72226633Sdim typedef struct _tagADDRESS64 {
73226633Sdim   DWORD64       Offset;
74226633Sdim   WORD          Segment;
75226633Sdim   ADDRESS_MODE  Mode;
76226633Sdim } ADDRESS64, *LPADDRESS64;
77226633Sdim
78226633Sdim typedef struct _KDHELP64 {
79226633Sdim   DWORD64   Thread;
80226633Sdim   DWORD   ThCallbackStack;
81226633Sdim   DWORD   ThCallbackBStore;
82226633Sdim   DWORD   NextCallback;
83226633Sdim   DWORD   FramePointer;
84226633Sdim   DWORD64   KiCallUserMode;
85226633Sdim   DWORD64   KeUserCallbackDispatcher;
86226633Sdim   DWORD64   SystemRangeStart;
87226633Sdim   DWORD64   KiUserExceptionDispatcher;
88226633Sdim   DWORD64   StackBase;
89226633Sdim   DWORD64   StackLimit;
90226633Sdim   DWORD64   Reserved[5];
91226633Sdim } KDHELP64, *PKDHELP64;
92226633Sdim
93226633Sdim typedef struct _tagSTACKFRAME64 {
94226633Sdim   ADDRESS64   AddrPC;
95226633Sdim   ADDRESS64   AddrReturn;
96226633Sdim   ADDRESS64   AddrFrame;
97226633Sdim   ADDRESS64   AddrStack;
98226633Sdim   ADDRESS64   AddrBStore;
99226633Sdim   PVOID       FuncTableEntry;
100226633Sdim   DWORD64     Params[4];
101226633Sdim   BOOL        Far;
102226633Sdim   BOOL        Virtual;
103226633Sdim   DWORD64     Reserved[3];
104226633Sdim   KDHELP64    KdHelp;
105226633Sdim } STACKFRAME64, *LPSTACKFRAME64;
106288943Sdim #endif // !defined(__MINGW64_VERSION_MAJOR)
107288943Sdim#endif // __MINGW32__
108226633Sdim
109226633Sdimtypedef BOOL (__stdcall *PREAD_PROCESS_MEMORY_ROUTINE64)(HANDLE hProcess,
110226633Sdim                      DWORD64 qwBaseAddress, PVOID lpBuffer, DWORD nSize,
111226633Sdim                      LPDWORD lpNumberOfBytesRead);
112226633Sdim
113226633Sdimtypedef PVOID (__stdcall *PFUNCTION_TABLE_ACCESS_ROUTINE64)( HANDLE ahProcess,
114226633Sdim                      DWORD64 AddrBase);
115226633Sdim
116226633Sdimtypedef DWORD64 (__stdcall *PGET_MODULE_BASE_ROUTINE64)(HANDLE hProcess,
117226633Sdim                      DWORD64 Address);
118226633Sdim
119226633Sdimtypedef DWORD64 (__stdcall *PTRANSLATE_ADDRESS_ROUTINE64)(HANDLE hProcess,
120226633Sdim                      HANDLE hThread, LPADDRESS64 lpaddr);
121226633Sdim
122309124Sdimtypedef BOOL(WINAPI *fpMiniDumpWriteDump)(HANDLE, DWORD, HANDLE, MINIDUMP_TYPE,
123309124Sdim                                          PMINIDUMP_EXCEPTION_INFORMATION,
124309124Sdim                                          PMINIDUMP_USER_STREAM_INFORMATION,
125309124Sdim                                          PMINIDUMP_CALLBACK_INFORMATION);
126309124Sdimstatic fpMiniDumpWriteDump fMiniDumpWriteDump;
127309124Sdim
128226633Sdimtypedef BOOL (WINAPI *fpStackWalk64)(DWORD, HANDLE, HANDLE, LPSTACKFRAME64,
129226633Sdim                      PVOID, PREAD_PROCESS_MEMORY_ROUTINE64,
130226633Sdim                      PFUNCTION_TABLE_ACCESS_ROUTINE64,
131226633Sdim                      PGET_MODULE_BASE_ROUTINE64,
132226633Sdim                      PTRANSLATE_ADDRESS_ROUTINE64);
133288943Sdimstatic fpStackWalk64 fStackWalk64;
134226633Sdim
135226633Sdimtypedef DWORD64 (WINAPI *fpSymGetModuleBase64)(HANDLE, DWORD64);
136288943Sdimstatic fpSymGetModuleBase64 fSymGetModuleBase64;
137226633Sdim
138226633Sdimtypedef BOOL (WINAPI *fpSymGetSymFromAddr64)(HANDLE, DWORD64,
139226633Sdim                      PDWORD64, PIMAGEHLP_SYMBOL64);
140288943Sdimstatic fpSymGetSymFromAddr64 fSymGetSymFromAddr64;
141226633Sdim
142226633Sdimtypedef BOOL (WINAPI *fpSymGetLineFromAddr64)(HANDLE, DWORD64,
143226633Sdim                      PDWORD, PIMAGEHLP_LINE64);
144288943Sdimstatic fpSymGetLineFromAddr64 fSymGetLineFromAddr64;
145226633Sdim
146296417Sdimtypedef BOOL(WINAPI *fpSymGetModuleInfo64)(HANDLE hProcess, DWORD64 dwAddr,
147296417Sdim                                           PIMAGEHLP_MODULE64 ModuleInfo);
148296417Sdimstatic fpSymGetModuleInfo64 fSymGetModuleInfo64;
149296417Sdim
150226633Sdimtypedef PVOID (WINAPI *fpSymFunctionTableAccess64)(HANDLE, DWORD64);
151288943Sdimstatic fpSymFunctionTableAccess64 fSymFunctionTableAccess64;
152226633Sdim
153288943Sdimtypedef DWORD (WINAPI *fpSymSetOptions)(DWORD);
154288943Sdimstatic fpSymSetOptions fSymSetOptions;
155288943Sdim
156288943Sdimtypedef BOOL (WINAPI *fpSymInitialize)(HANDLE, PCSTR, BOOL);
157288943Sdimstatic fpSymInitialize fSymInitialize;
158288943Sdim
159296417Sdimtypedef BOOL (WINAPI *fpEnumerateLoadedModules)(HANDLE,PENUMLOADED_MODULES_CALLBACK64,PVOID);
160296417Sdimstatic fpEnumerateLoadedModules fEnumerateLoadedModules;
161296417Sdim
162226633Sdimstatic bool load64BitDebugHelp(void) {
163261991Sdim  HMODULE hLib = ::LoadLibraryW(L"Dbghelp.dll");
164226633Sdim  if (hLib) {
165309124Sdim    fMiniDumpWriteDump = (fpMiniDumpWriteDump)
166309124Sdim                      ::GetProcAddress(hLib, "MiniDumpWriteDump");
167288943Sdim    fStackWalk64 = (fpStackWalk64)
168226633Sdim                      ::GetProcAddress(hLib, "StackWalk64");
169288943Sdim    fSymGetModuleBase64 = (fpSymGetModuleBase64)
170226633Sdim                      ::GetProcAddress(hLib, "SymGetModuleBase64");
171288943Sdim    fSymGetSymFromAddr64 = (fpSymGetSymFromAddr64)
172226633Sdim                      ::GetProcAddress(hLib, "SymGetSymFromAddr64");
173288943Sdim    fSymGetLineFromAddr64 = (fpSymGetLineFromAddr64)
174226633Sdim                      ::GetProcAddress(hLib, "SymGetLineFromAddr64");
175296417Sdim    fSymGetModuleInfo64 = (fpSymGetModuleInfo64)
176296417Sdim                      ::GetProcAddress(hLib, "SymGetModuleInfo64");
177288943Sdim    fSymFunctionTableAccess64 = (fpSymFunctionTableAccess64)
178226633Sdim                     ::GetProcAddress(hLib, "SymFunctionTableAccess64");
179288943Sdim    fSymSetOptions = (fpSymSetOptions)::GetProcAddress(hLib, "SymSetOptions");
180288943Sdim    fSymInitialize = (fpSymInitialize)::GetProcAddress(hLib, "SymInitialize");
181296417Sdim    fEnumerateLoadedModules = (fpEnumerateLoadedModules)
182296417Sdim      ::GetProcAddress(hLib, "EnumerateLoadedModules64");
183226633Sdim  }
184309124Sdim  return fStackWalk64 && fSymInitialize && fSymSetOptions && fMiniDumpWriteDump;
185226633Sdim}
186226633Sdim
187296417Sdimusing namespace llvm;
188296417Sdim
189218885Sdim// Forward declare.
190218885Sdimstatic LONG WINAPI LLVMUnhandledExceptionFilter(LPEXCEPTION_POINTERS ep);
191218885Sdimstatic BOOL WINAPI LLVMConsoleCtrlHandler(DWORD dwCtrlType);
192218885Sdim
193341825Sdim// The function to call if ctrl-c is pressed.
194218885Sdimstatic void (*InterruptFunction)() = 0;
195218885Sdim
196261991Sdimstatic std::vector<std::string> *FilesToRemove = NULL;
197218885Sdimstatic bool RegisteredUnhandledExceptionFilter = false;
198218885Sdimstatic bool CleanupExecuted = false;
199218885Sdimstatic PTOP_LEVEL_EXCEPTION_FILTER OldFilter = NULL;
200218885Sdim
201218885Sdim// Windows creates a new thread to execute the console handler when an event
202218885Sdim// (such as CTRL/C) occurs.  This causes concurrency issues with the above
203218885Sdim// globals which this critical section addresses.
204218885Sdimstatic CRITICAL_SECTION CriticalSection;
205288943Sdimstatic bool CriticalSectionInitialized = false;
206218885Sdim
207309124Sdimstatic StringRef Argv0;
208309124Sdim
209296417Sdimenum {
210288943Sdim#if defined(_M_X64)
211296417Sdim  NativeMachineType = IMAGE_FILE_MACHINE_AMD64
212327952Sdim#elif defined(_M_ARM64)
213327952Sdim  NativeMachineType = IMAGE_FILE_MACHINE_ARM64
214327952Sdim#elif defined(_M_IX86)
215327952Sdim  NativeMachineType = IMAGE_FILE_MACHINE_I386
216327952Sdim#elif defined(_M_ARM)
217327952Sdim  NativeMachineType = IMAGE_FILE_MACHINE_ARMNT
218288943Sdim#else
219327952Sdim  NativeMachineType = IMAGE_FILE_MACHINE_UNKNOWN
220288943Sdim#endif
221296417Sdim};
222288943Sdim
223296417Sdimstatic bool printStackTraceWithLLVMSymbolizer(llvm::raw_ostream &OS,
224296417Sdim                                              HANDLE hProcess, HANDLE hThread,
225296417Sdim                                              STACKFRAME64 &StackFrameOrig,
226296417Sdim                                              CONTEXT *ContextOrig) {
227296417Sdim  // StackWalk64 modifies the incoming stack frame and context, so copy them.
228296417Sdim  STACKFRAME64 StackFrame = StackFrameOrig;
229296417Sdim
230296417Sdim  // Copy the register context so that we don't modify it while we unwind. We
231296417Sdim  // could use InitializeContext + CopyContext, but that's only required to get
232296417Sdim  // at AVX registers, which typically aren't needed by StackWalk64. Reduce the
233296417Sdim  // flag set to indicate that there's less data.
234296417Sdim  CONTEXT Context = *ContextOrig;
235296417Sdim  Context.ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER;
236296417Sdim
237296417Sdim  static void *StackTrace[256];
238296417Sdim  size_t Depth = 0;
239296417Sdim  while (fStackWalk64(NativeMachineType, hProcess, hThread, &StackFrame,
240296417Sdim                      &Context, 0, fSymFunctionTableAccess64,
241296417Sdim                      fSymGetModuleBase64, 0)) {
242296417Sdim    if (StackFrame.AddrFrame.Offset == 0)
243296417Sdim      break;
244296417Sdim    StackTrace[Depth++] = (void *)(uintptr_t)StackFrame.AddrPC.Offset;
245296417Sdim    if (Depth >= array_lengthof(StackTrace))
246296417Sdim      break;
247296417Sdim  }
248296417Sdim
249309124Sdim  return printSymbolizedStackTrace(Argv0, &StackTrace[0], Depth, OS);
250296417Sdim}
251296417Sdim
252296417Sdimnamespace {
253296417Sdimstruct FindModuleData {
254296417Sdim  void **StackTrace;
255296417Sdim  int Depth;
256296417Sdim  const char **Modules;
257296417Sdim  intptr_t *Offsets;
258296417Sdim  StringSaver *StrPool;
259296417Sdim};
260296417Sdim}
261296417Sdim
262309124Sdimstatic BOOL CALLBACK findModuleCallback(PCSTR ModuleName,
263296417Sdim                                        DWORD64 ModuleBase, ULONG ModuleSize,
264296417Sdim                                        void *VoidData) {
265296417Sdim  FindModuleData *Data = (FindModuleData*)VoidData;
266296417Sdim  intptr_t Beg = ModuleBase;
267296417Sdim  intptr_t End = Beg + ModuleSize;
268296417Sdim  for (int I = 0; I < Data->Depth; I++) {
269296417Sdim    if (Data->Modules[I])
270296417Sdim      continue;
271296417Sdim    intptr_t Addr = (intptr_t)Data->StackTrace[I];
272296417Sdim    if (Beg <= Addr && Addr < End) {
273314564Sdim      Data->Modules[I] = Data->StrPool->save(ModuleName).data();
274296417Sdim      Data->Offsets[I] = Addr - Beg;
275296417Sdim    }
276296417Sdim  }
277296417Sdim  return TRUE;
278296417Sdim}
279296417Sdim
280296417Sdimstatic bool findModulesAndOffsets(void **StackTrace, int Depth,
281296417Sdim                                  const char **Modules, intptr_t *Offsets,
282296417Sdim                                  const char *MainExecutableName,
283296417Sdim                                  StringSaver &StrPool) {
284296417Sdim  if (!fEnumerateLoadedModules)
285296417Sdim    return false;
286296417Sdim  FindModuleData Data;
287296417Sdim  Data.StackTrace = StackTrace;
288296417Sdim  Data.Depth = Depth;
289296417Sdim  Data.Modules = Modules;
290296417Sdim  Data.Offsets = Offsets;
291296417Sdim  Data.StrPool = &StrPool;
292296417Sdim  fEnumerateLoadedModules(GetCurrentProcess(), findModuleCallback, &Data);
293296417Sdim  return true;
294296417Sdim}
295296417Sdim
296296417Sdimstatic void PrintStackTraceForThread(llvm::raw_ostream &OS, HANDLE hProcess,
297296417Sdim                                     HANDLE hThread, STACKFRAME64 &StackFrame,
298296417Sdim                                     CONTEXT *Context) {
299288943Sdim  // Initialize the symbol handler.
300288943Sdim  fSymSetOptions(SYMOPT_DEFERRED_LOADS | SYMOPT_LOAD_LINES);
301288943Sdim  fSymInitialize(hProcess, NULL, TRUE);
302288943Sdim
303296417Sdim  // Try llvm-symbolizer first. llvm-symbolizer knows how to deal with both PDBs
304296417Sdim  // and DWARF, so it should do a good job regardless of what debug info or
305296417Sdim  // linker is in use.
306296417Sdim  if (printStackTraceWithLLVMSymbolizer(OS, hProcess, hThread, StackFrame,
307296417Sdim                                        Context)) {
308296417Sdim    return;
309296417Sdim  }
310296417Sdim
311288943Sdim  while (true) {
312296417Sdim    if (!fStackWalk64(NativeMachineType, hProcess, hThread, &StackFrame,
313296417Sdim                      Context, 0, fSymFunctionTableAccess64,
314296417Sdim                      fSymGetModuleBase64, 0)) {
315288943Sdim      break;
316288943Sdim    }
317288943Sdim
318288943Sdim    if (StackFrame.AddrFrame.Offset == 0)
319288943Sdim      break;
320288943Sdim
321288943Sdim    using namespace llvm;
322288943Sdim    // Print the PC in hexadecimal.
323288943Sdim    DWORD64 PC = StackFrame.AddrPC.Offset;
324327952Sdim#if defined(_M_X64) || defined(_M_ARM64)
325288943Sdim    OS << format("0x%016llX", PC);
326327952Sdim#elif defined(_M_IX86) || defined(_M_ARM)
327288943Sdim    OS << format("0x%08lX", static_cast<DWORD>(PC));
328288943Sdim#endif
329288943Sdim
330288943Sdim// Print the parameters.  Assume there are four.
331327952Sdim#if defined(_M_X64) || defined(_M_ARM64)
332288943Sdim    OS << format(" (0x%016llX 0x%016llX 0x%016llX 0x%016llX)",
333288943Sdim            StackFrame.Params[0], StackFrame.Params[1], StackFrame.Params[2],
334288943Sdim            StackFrame.Params[3]);
335327952Sdim#elif defined(_M_IX86) || defined(_M_ARM)
336288943Sdim    OS << format(" (0x%08lX 0x%08lX 0x%08lX 0x%08lX)",
337288943Sdim            static_cast<DWORD>(StackFrame.Params[0]),
338288943Sdim            static_cast<DWORD>(StackFrame.Params[1]),
339288943Sdim            static_cast<DWORD>(StackFrame.Params[2]),
340288943Sdim            static_cast<DWORD>(StackFrame.Params[3]));
341288943Sdim#endif
342288943Sdim    // Verify the PC belongs to a module in this process.
343288943Sdim    if (!fSymGetModuleBase64(hProcess, PC)) {
344288943Sdim      OS << " <unknown module>\n";
345288943Sdim      continue;
346288943Sdim    }
347288943Sdim
348288943Sdim    // Print the symbol name.
349288943Sdim    char buffer[512];
350288943Sdim    IMAGEHLP_SYMBOL64 *symbol = reinterpret_cast<IMAGEHLP_SYMBOL64 *>(buffer);
351288943Sdim    memset(symbol, 0, sizeof(IMAGEHLP_SYMBOL64));
352288943Sdim    symbol->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64);
353288943Sdim    symbol->MaxNameLength = 512 - sizeof(IMAGEHLP_SYMBOL64);
354288943Sdim
355288943Sdim    DWORD64 dwDisp;
356288943Sdim    if (!fSymGetSymFromAddr64(hProcess, PC, &dwDisp, symbol)) {
357288943Sdim      OS << '\n';
358288943Sdim      continue;
359288943Sdim    }
360288943Sdim
361288943Sdim    buffer[511] = 0;
362288943Sdim    if (dwDisp > 0)
363288943Sdim      OS << format(", %s() + 0x%llX bytes(s)", (const char*)symbol->Name,
364288943Sdim                   dwDisp);
365288943Sdim    else
366288943Sdim      OS << format(", %s", (const char*)symbol->Name);
367288943Sdim
368288943Sdim    // Print the source file and line number information.
369288943Sdim    IMAGEHLP_LINE64 line = {};
370288943Sdim    DWORD dwLineDisp;
371288943Sdim    line.SizeOfStruct = sizeof(line);
372288943Sdim    if (fSymGetLineFromAddr64(hProcess, PC, &dwLineDisp, &line)) {
373288943Sdim      OS << format(", %s, line %lu", line.FileName, line.LineNumber);
374288943Sdim      if (dwLineDisp > 0)
375288943Sdim        OS << format(" + 0x%lX byte(s)", dwLineDisp);
376288943Sdim    }
377288943Sdim
378288943Sdim    OS << '\n';
379288943Sdim  }
380288943Sdim}
381288943Sdim
382218885Sdimnamespace llvm {
383218885Sdim
384218885Sdim//===----------------------------------------------------------------------===//
385218885Sdim//=== WARNING: Implementation here must contain only Win32 specific code
386218885Sdim//===          and must not be UNIX code
387218885Sdim//===----------------------------------------------------------------------===//
388218885Sdim
389218885Sdim#ifdef _MSC_VER
390341825Sdim/// Emulates hitting "retry" from an "abort, retry, ignore" CRT debug report
391341825Sdim/// dialog. "retry" raises an exception which ultimately triggers our stack
392341825Sdim/// dumper.
393288943Sdimstatic LLVM_ATTRIBUTE_UNUSED int
394288943SdimAvoidMessageBoxHook(int ReportType, char *Message, int *Return) {
395251662Sdim  // Set *Return to the retry code for the return value of _CrtDbgReport:
396251662Sdim  // http://msdn.microsoft.com/en-us/library/8hyw4sy7(v=vs.71).aspx
397251662Sdim  // This may also trigger just-in-time debugging via DebugBreak().
398251662Sdim  if (Return)
399251662Sdim    *Return = 1;
400251662Sdim  // Don't call _CrtDbgReport.
401251662Sdim  return TRUE;
402251662Sdim}
403251662Sdim
404218885Sdim#endif
405218885Sdim
406288943Sdimextern "C" void HandleAbort(int Sig) {
407288943Sdim  if (Sig == SIGABRT) {
408288943Sdim    LLVM_BUILTIN_TRAP;
409288943Sdim  }
410288943Sdim}
411288943Sdim
412288943Sdimstatic void InitializeThreading() {
413288943Sdim  if (CriticalSectionInitialized)
414288943Sdim    return;
415288943Sdim
416288943Sdim  // Now's the time to create the critical section. This is the first time
417288943Sdim  // through here, and there's only one thread.
418288943Sdim  InitializeCriticalSection(&CriticalSection);
419288943Sdim  CriticalSectionInitialized = true;
420288943Sdim}
421288943Sdim
422218885Sdimstatic void RegisterHandler() {
423288943Sdim  // If we cannot load up the APIs (which would be unexpected as they should
424288943Sdim  // exist on every version of Windows we support), we will bail out since
425288943Sdim  // there would be nothing to report.
426226633Sdim  if (!load64BitDebugHelp()) {
427226633Sdim    assert(false && "These APIs should always be available");
428226633Sdim    return;
429226633Sdim  }
430226633Sdim
431218885Sdim  if (RegisteredUnhandledExceptionFilter) {
432218885Sdim    EnterCriticalSection(&CriticalSection);
433218885Sdim    return;
434218885Sdim  }
435218885Sdim
436288943Sdim  InitializeThreading();
437218885Sdim
438218885Sdim  // Enter it immediately.  Now if someone hits CTRL/C, the console handler
439218885Sdim  // can't proceed until the globals are updated.
440218885Sdim  EnterCriticalSection(&CriticalSection);
441218885Sdim
442218885Sdim  RegisteredUnhandledExceptionFilter = true;
443218885Sdim  OldFilter = SetUnhandledExceptionFilter(LLVMUnhandledExceptionFilter);
444218885Sdim  SetConsoleCtrlHandler(LLVMConsoleCtrlHandler, TRUE);
445218885Sdim
446218885Sdim  // IMPORTANT NOTE: Caller must call LeaveCriticalSection(&CriticalSection) or
447218885Sdim  // else multi-threading problems will ensue.
448218885Sdim}
449218885Sdim
450341825Sdim// The public API
451261991Sdimbool sys::RemoveFileOnSignal(StringRef Filename, std::string* ErrMsg) {
452218885Sdim  RegisterHandler();
453218885Sdim
454218885Sdim  if (CleanupExecuted) {
455218885Sdim    if (ErrMsg)
456218885Sdim      *ErrMsg = "Process terminating -- cannot register for removal";
457218885Sdim    return true;
458218885Sdim  }
459218885Sdim
460218885Sdim  if (FilesToRemove == NULL)
461261991Sdim    FilesToRemove = new std::vector<std::string>;
462218885Sdim
463218885Sdim  FilesToRemove->push_back(Filename);
464218885Sdim
465218885Sdim  LeaveCriticalSection(&CriticalSection);
466218885Sdim  return false;
467218885Sdim}
468218885Sdim
469341825Sdim// The public API
470261991Sdimvoid sys::DontRemoveFileOnSignal(StringRef Filename) {
471218885Sdim  if (FilesToRemove == NULL)
472218885Sdim    return;
473218885Sdim
474218885Sdim  RegisterHandler();
475218885Sdim
476261991Sdim  std::vector<std::string>::reverse_iterator I =
477314564Sdim      find(reverse(*FilesToRemove), Filename);
478218885Sdim  if (I != FilesToRemove->rend())
479218885Sdim    FilesToRemove->erase(I.base()-1);
480218885Sdim
481218885Sdim  LeaveCriticalSection(&CriticalSection);
482218885Sdim}
483218885Sdim
484288943Sdimvoid sys::DisableSystemDialogsOnCrash() {
485288943Sdim  // Crash to stack trace handler on abort.
486288943Sdim  signal(SIGABRT, HandleAbort);
487288943Sdim
488288943Sdim  // The following functions are not reliably accessible on MinGW.
489288943Sdim#ifdef _MSC_VER
490288943Sdim  // We're already handling writing a "something went wrong" message.
491288943Sdim  _set_abort_behavior(0, _WRITE_ABORT_MSG);
492288943Sdim  // Disable Dr. Watson.
493288943Sdim  _set_abort_behavior(0, _CALL_REPORTFAULT);
494288943Sdim  _CrtSetReportHook(AvoidMessageBoxHook);
495288943Sdim#endif
496288943Sdim
497288943Sdim  // Disable standard error dialog box.
498288943Sdim  SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX |
499288943Sdim               SEM_NOOPENFILEERRORBOX);
500288943Sdim  _set_error_mode(_OUT_TO_STDERR);
501288943Sdim}
502288943Sdim
503341825Sdim/// When an error signal (such as SIGABRT or SIGSEGV) is delivered to the
504341825Sdim/// process, print a stack trace and then exit.
505309124Sdimvoid sys::PrintStackTraceOnErrorSignal(StringRef Argv0,
506309124Sdim                                       bool DisableCrashReporting) {
507309124Sdim  ::Argv0 = Argv0;
508309124Sdim
509309124Sdim  if (DisableCrashReporting || getenv("LLVM_DISABLE_CRASH_REPORT"))
510309124Sdim    Process::PreventCoreFiles();
511309124Sdim
512288943Sdim  DisableSystemDialogsOnCrash();
513218885Sdim  RegisterHandler();
514218885Sdim  LeaveCriticalSection(&CriticalSection);
515218885Sdim}
516288943Sdim}
517218885Sdim
518288943Sdim#if defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR)
519288943Sdim// Provide a prototype for RtlCaptureContext, mingw32 from mingw.org is
520288943Sdim// missing it but mingw-w64 has it.
521288943Sdimextern "C" VOID WINAPI RtlCaptureContext(PCONTEXT ContextRecord);
522288943Sdim#endif
523288943Sdim
524360784Sdimstatic void LocalPrintStackTrace(raw_ostream &OS, PCONTEXT C) {
525360784Sdim  STACKFRAME64 StackFrame{};
526360784Sdim  CONTEXT Context{};
527360784Sdim  if (!C) {
528360784Sdim    ::RtlCaptureContext(&Context);
529360784Sdim    C = &Context;
530360784Sdim  }
531288943Sdim#if defined(_M_X64)
532288943Sdim  StackFrame.AddrPC.Offset = Context.Rip;
533288943Sdim  StackFrame.AddrStack.Offset = Context.Rsp;
534288943Sdim  StackFrame.AddrFrame.Offset = Context.Rbp;
535327952Sdim#elif defined(_M_IX86)
536288943Sdim  StackFrame.AddrPC.Offset = Context.Eip;
537288943Sdim  StackFrame.AddrStack.Offset = Context.Esp;
538288943Sdim  StackFrame.AddrFrame.Offset = Context.Ebp;
539341825Sdim#elif defined(_M_ARM64)
540327952Sdim  StackFrame.AddrPC.Offset = Context.Pc;
541327952Sdim  StackFrame.AddrStack.Offset = Context.Sp;
542327952Sdim  StackFrame.AddrFrame.Offset = Context.Fp;
543341825Sdim#elif defined(_M_ARM)
544341825Sdim  StackFrame.AddrPC.Offset = Context.Pc;
545341825Sdim  StackFrame.AddrStack.Offset = Context.Sp;
546341825Sdim  StackFrame.AddrFrame.Offset = Context.R11;
547288943Sdim#endif
548288943Sdim  StackFrame.AddrPC.Mode = AddrModeFlat;
549288943Sdim  StackFrame.AddrStack.Mode = AddrModeFlat;
550288943Sdim  StackFrame.AddrFrame.Mode = AddrModeFlat;
551288943Sdim  PrintStackTraceForThread(OS, GetCurrentProcess(), GetCurrentThread(),
552360784Sdim                           StackFrame, C);
553249423Sdim}
554218885Sdim
555360784Sdimvoid llvm::sys::PrintStackTrace(raw_ostream &OS) {
556360784Sdim  LocalPrintStackTrace(OS, nullptr);
557360784Sdim}
558249423Sdim
559288943Sdimvoid llvm::sys::SetInterruptFunction(void (*IF)()) {
560218885Sdim  RegisterHandler();
561218885Sdim  InterruptFunction = IF;
562218885Sdim  LeaveCriticalSection(&CriticalSection);
563218885Sdim}
564218885Sdim
565353358Sdimvoid llvm::sys::SetInfoSignalFunction(void (*Handler)()) {
566353358Sdim  // Unimplemented.
567353358Sdim}
568218885Sdim
569360784Sdimvoid llvm::sys::SetOneShotPipeSignalFunction(void (*Handler)()) {
570360784Sdim  // Unimplemented.
571360784Sdim}
572353358Sdim
573360784Sdimvoid llvm::sys::DefaultOneShotPipeSignalHandler() {
574360784Sdim  // Unimplemented.
575360784Sdim}
576360784Sdim
577341825Sdim/// Add a function to be called when a signal is delivered to the process. The
578341825Sdim/// handler can have a cookie passed to it to identify what instance of the
579341825Sdim/// handler it is.
580341825Sdimvoid llvm::sys::AddSignalHandler(sys::SignalHandlerCallback FnPtr,
581341825Sdim                                 void *Cookie) {
582341825Sdim  insertSignalHandler(FnPtr, Cookie);
583218885Sdim  RegisterHandler();
584218885Sdim  LeaveCriticalSection(&CriticalSection);
585218885Sdim}
586218885Sdim
587218885Sdimstatic void Cleanup() {
588288943Sdim  if (CleanupExecuted)
589288943Sdim    return;
590288943Sdim
591218885Sdim  EnterCriticalSection(&CriticalSection);
592218885Sdim
593218885Sdim  // Prevent other thread from registering new files and directories for
594218885Sdim  // removal, should we be executing because of the console handler callback.
595218885Sdim  CleanupExecuted = true;
596218885Sdim
597218885Sdim  // FIXME: open files cannot be deleted.
598218885Sdim  if (FilesToRemove != NULL)
599218885Sdim    while (!FilesToRemove->empty()) {
600276479Sdim      llvm::sys::fs::remove(FilesToRemove->back());
601218885Sdim      FilesToRemove->pop_back();
602218885Sdim    }
603296417Sdim  llvm::sys::RunSignalHandlers();
604218885Sdim  LeaveCriticalSection(&CriticalSection);
605218885Sdim}
606218885Sdim
607218885Sdimvoid llvm::sys::RunInterruptHandlers() {
608288943Sdim  // The interrupt handler may be called from an interrupt, but it may also be
609288943Sdim  // called manually (such as the case of report_fatal_error with no registered
610288943Sdim  // error handler). We must ensure that the critical section is properly
611288943Sdim  // initialized.
612288943Sdim  InitializeThreading();
613218885Sdim  Cleanup();
614218885Sdim}
615218885Sdim
616341825Sdim/// Find the Windows Registry Key for a given location.
617309124Sdim///
618309124Sdim/// \returns a valid HKEY if the location exists, else NULL.
619309124Sdimstatic HKEY FindWERKey(const llvm::Twine &RegistryLocation) {
620309124Sdim  HKEY Key;
621309124Sdim  if (ERROR_SUCCESS != ::RegOpenKeyExA(HKEY_LOCAL_MACHINE,
622309124Sdim                                       RegistryLocation.str().c_str(), 0,
623309124Sdim                                       KEY_QUERY_VALUE | KEY_READ, &Key))
624309124Sdim    return NULL;
625309124Sdim
626309124Sdim  return Key;
627309124Sdim}
628309124Sdim
629341825Sdim/// Populate ResultDirectory with the value for "DumpFolder" for a given
630309124Sdim/// Windows Registry key.
631309124Sdim///
632309124Sdim/// \returns true if a valid value for DumpFolder exists, false otherwise.
633309124Sdimstatic bool GetDumpFolder(HKEY Key,
634309124Sdim                          llvm::SmallVectorImpl<char> &ResultDirectory) {
635309124Sdim  using llvm::sys::windows::UTF16ToUTF8;
636309124Sdim
637309124Sdim  if (!Key)
638309124Sdim    return false;
639309124Sdim
640309124Sdim  DWORD BufferLengthBytes = 0;
641309124Sdim
642309124Sdim  if (ERROR_SUCCESS != ::RegGetValueW(Key, 0, L"DumpFolder", REG_EXPAND_SZ,
643309124Sdim                                      NULL, NULL, &BufferLengthBytes))
644309124Sdim    return false;
645309124Sdim
646309124Sdim  SmallVector<wchar_t, MAX_PATH> Buffer(BufferLengthBytes);
647309124Sdim
648309124Sdim  if (ERROR_SUCCESS != ::RegGetValueW(Key, 0, L"DumpFolder", REG_EXPAND_SZ,
649309124Sdim                                      NULL, Buffer.data(), &BufferLengthBytes))
650309124Sdim    return false;
651309124Sdim
652309124Sdim  DWORD ExpandBufferSize = ::ExpandEnvironmentStringsW(Buffer.data(), NULL, 0);
653309124Sdim
654309124Sdim  if (!ExpandBufferSize)
655309124Sdim    return false;
656309124Sdim
657309124Sdim  SmallVector<wchar_t, MAX_PATH> ExpandBuffer(ExpandBufferSize);
658309124Sdim
659309124Sdim  if (ExpandBufferSize != ::ExpandEnvironmentStringsW(Buffer.data(),
660309124Sdim                                                      ExpandBuffer.data(),
661309124Sdim                                                      ExpandBufferSize))
662309124Sdim    return false;
663309124Sdim
664309124Sdim  if (UTF16ToUTF8(ExpandBuffer.data(), ExpandBufferSize - 1, ResultDirectory))
665309124Sdim    return false;
666309124Sdim
667309124Sdim  return true;
668309124Sdim}
669309124Sdim
670341825Sdim/// Populate ResultType with a valid MINIDUMP_TYPE based on the value of
671309124Sdim/// "DumpType" for a given Windows Registry key.
672309124Sdim///
673309124Sdim/// According to
674309124Sdim/// https://msdn.microsoft.com/en-us/library/windows/desktop/bb787181(v=vs.85).aspx
675309124Sdim/// valid values for DumpType are:
676309124Sdim///   * 0: Custom dump
677309124Sdim///   * 1: Mini dump
678309124Sdim///   * 2: Full dump
679309124Sdim/// If "Custom dump" is specified then the "CustomDumpFlags" field is read
680309124Sdim/// containing a bitwise combination of MINIDUMP_TYPE values.
681309124Sdim///
682309124Sdim/// \returns true if a valid value for ResultType can be set, false otherwise.
683309124Sdimstatic bool GetDumpType(HKEY Key, MINIDUMP_TYPE &ResultType) {
684309124Sdim  if (!Key)
685309124Sdim    return false;
686309124Sdim
687309124Sdim  DWORD DumpType;
688309124Sdim  DWORD TypeSize = sizeof(DumpType);
689309124Sdim  if (ERROR_SUCCESS != ::RegGetValueW(Key, NULL, L"DumpType", RRF_RT_REG_DWORD,
690309124Sdim                                      NULL, &DumpType,
691309124Sdim                                      &TypeSize))
692309124Sdim    return false;
693309124Sdim
694309124Sdim  switch (DumpType) {
695309124Sdim  case 0: {
696309124Sdim    DWORD Flags = 0;
697309124Sdim    if (ERROR_SUCCESS != ::RegGetValueW(Key, NULL, L"CustomDumpFlags",
698309124Sdim                                        RRF_RT_REG_DWORD, NULL, &Flags,
699309124Sdim                                        &TypeSize))
700309124Sdim      return false;
701309124Sdim
702309124Sdim    ResultType = static_cast<MINIDUMP_TYPE>(Flags);
703309124Sdim    break;
704309124Sdim  }
705309124Sdim  case 1:
706309124Sdim    ResultType = MiniDumpNormal;
707309124Sdim    break;
708309124Sdim  case 2:
709309124Sdim    ResultType = MiniDumpWithFullMemory;
710309124Sdim    break;
711309124Sdim  default:
712309124Sdim    return false;
713309124Sdim  }
714309124Sdim  return true;
715309124Sdim}
716309124Sdim
717341825Sdim/// Write a Windows dump file containing process information that can be
718309124Sdim/// used for post-mortem debugging.
719309124Sdim///
720309124Sdim/// \returns zero error code if a mini dump created, actual error code
721309124Sdim/// otherwise.
722309124Sdimstatic std::error_code WINAPI
723309124SdimWriteWindowsDumpFile(PMINIDUMP_EXCEPTION_INFORMATION ExceptionInfo) {
724309124Sdim  using namespace llvm;
725309124Sdim  using namespace llvm::sys;
726309124Sdim
727309124Sdim  std::string MainExecutableName = fs::getMainExecutable(nullptr, nullptr);
728309124Sdim  StringRef ProgramName;
729309124Sdim
730309124Sdim  if (MainExecutableName.empty()) {
731309124Sdim    // If we can't get the executable filename,
732309124Sdim    // things are in worse shape than we realize
733309124Sdim    // and we should just bail out.
734309124Sdim    return mapWindowsError(::GetLastError());
735309124Sdim  }
736309124Sdim
737309124Sdim  ProgramName = path::filename(MainExecutableName.c_str());
738309124Sdim
739309124Sdim  // The Windows Registry location as specified at
740309124Sdim  // https://msdn.microsoft.com/en-us/library/windows/desktop/bb787181%28v=vs.85%29.aspx
741309124Sdim  // "Collecting User-Mode Dumps" that may optionally be set to collect crash
742309124Sdim  // dumps in a specified location.
743309124Sdim  StringRef LocalDumpsRegistryLocation =
744309124Sdim      "SOFTWARE\\Microsoft\\Windows\\Windows Error Reporting\\LocalDumps";
745309124Sdim
746309124Sdim  // The key pointing to the Registry location that may contain global crash
747309124Sdim  // dump settings.  This will be NULL if the location can not be found.
748309124Sdim  ScopedRegHandle DefaultLocalDumpsKey(FindWERKey(LocalDumpsRegistryLocation));
749309124Sdim
750309124Sdim  // The key pointing to the Registry location that may contain
751309124Sdim  // application-specific crash dump settings.  This will be NULL if the
752309124Sdim  // location can not be found.
753309124Sdim  ScopedRegHandle AppSpecificKey(
754309124Sdim      FindWERKey(Twine(LocalDumpsRegistryLocation) + "\\" + ProgramName));
755309124Sdim
756309124Sdim  // Look to see if a dump type is specified in the registry; first with the
757309124Sdim  // app-specific key and failing that with the global key.  If none are found
758309124Sdim  // default to a normal dump (GetDumpType will return false either if the key
759309124Sdim  // is NULL or if there is no valid DumpType value at its location).
760309124Sdim  MINIDUMP_TYPE DumpType;
761309124Sdim  if (!GetDumpType(AppSpecificKey, DumpType))
762309124Sdim    if (!GetDumpType(DefaultLocalDumpsKey, DumpType))
763309124Sdim      DumpType = MiniDumpNormal;
764309124Sdim
765309124Sdim  // Look to see if a dump location is specified in the registry; first with the
766309124Sdim  // app-specific key and failing that with the global key.  If none are found
767309124Sdim  // we'll just create the dump file in the default temporary file location
768309124Sdim  // (GetDumpFolder will return false either if the key is NULL or if there is
769309124Sdim  // no valid DumpFolder value at its location).
770309124Sdim  bool ExplicitDumpDirectorySet = true;
771309124Sdim  SmallString<MAX_PATH> DumpDirectory;
772309124Sdim  if (!GetDumpFolder(AppSpecificKey, DumpDirectory))
773309124Sdim    if (!GetDumpFolder(DefaultLocalDumpsKey, DumpDirectory))
774309124Sdim      ExplicitDumpDirectorySet = false;
775309124Sdim
776309124Sdim  int FD;
777309124Sdim  SmallString<MAX_PATH> DumpPath;
778309124Sdim
779309124Sdim  if (ExplicitDumpDirectorySet) {
780309124Sdim    if (std::error_code EC = fs::create_directories(DumpDirectory))
781309124Sdim      return EC;
782309124Sdim    if (std::error_code EC = fs::createUniqueFile(
783309124Sdim            Twine(DumpDirectory) + "\\" + ProgramName + ".%%%%%%.dmp", FD,
784309124Sdim            DumpPath))
785309124Sdim      return EC;
786309124Sdim  } else if (std::error_code EC =
787309124Sdim                 fs::createTemporaryFile(ProgramName, "dmp", FD, DumpPath))
788309124Sdim    return EC;
789309124Sdim
790309124Sdim  // Our support functions return a file descriptor but Windows wants a handle.
791309124Sdim  ScopedCommonHandle FileHandle(reinterpret_cast<HANDLE>(_get_osfhandle(FD)));
792309124Sdim
793309124Sdim  if (!fMiniDumpWriteDump(::GetCurrentProcess(), ::GetCurrentProcessId(),
794309124Sdim                          FileHandle, DumpType, ExceptionInfo, NULL, NULL))
795309124Sdim    return mapWindowsError(::GetLastError());
796309124Sdim
797309124Sdim  llvm::errs() << "Wrote crash dump file \"" << DumpPath << "\"\n";
798309124Sdim  return std::error_code();
799309124Sdim}
800309124Sdim
801360784Sdimvoid sys::CleanupOnSignal(uintptr_t Context) {
802360784Sdim  LLVMUnhandledExceptionFilter((LPEXCEPTION_POINTERS)Context);
803360784Sdim}
804360784Sdim
805218885Sdimstatic LONG WINAPI LLVMUnhandledExceptionFilter(LPEXCEPTION_POINTERS ep) {
806218885Sdim  Cleanup();
807218885Sdim
808309124Sdim  // We'll automatically write a Minidump file here to help diagnose
809309124Sdim  // the nasty sorts of crashes that aren't 100% reproducible from a set of
810309124Sdim  // inputs (or in the event that the user is unable or unwilling to provide a
811309124Sdim  // reproducible case).
812321369Sdim  if (!llvm::sys::Process::AreCoreFilesPrevented()) {
813309124Sdim    MINIDUMP_EXCEPTION_INFORMATION ExceptionInfo;
814309124Sdim    ExceptionInfo.ThreadId = ::GetCurrentThreadId();
815309124Sdim    ExceptionInfo.ExceptionPointers = ep;
816309124Sdim    ExceptionInfo.ClientPointers = FALSE;
817309124Sdim
818309124Sdim    if (std::error_code EC = WriteWindowsDumpFile(&ExceptionInfo))
819309124Sdim      llvm::errs() << "Could not write crash dump file: " << EC.message()
820309124Sdim                   << "\n";
821309124Sdim  }
822309124Sdim
823360784Sdim  // Stack unwinding appears to modify the context. Copy it to preserve the
824360784Sdim  // caller's context.
825360784Sdim  CONTEXT ContextCopy;
826360784Sdim  if (ep)
827360784Sdim    memcpy(&ContextCopy, ep->ContextRecord, sizeof(ContextCopy));
828218885Sdim
829360784Sdim  LocalPrintStackTrace(llvm::errs(), ep ? &ContextCopy : nullptr);
830218885Sdim
831360784Sdim  return EXCEPTION_EXECUTE_HANDLER;
832218885Sdim}
833218885Sdim
834218885Sdimstatic BOOL WINAPI LLVMConsoleCtrlHandler(DWORD dwCtrlType) {
835218885Sdim  // We are running in our very own thread, courtesy of Windows.
836218885Sdim  EnterCriticalSection(&CriticalSection);
837218885Sdim  Cleanup();
838218885Sdim
839218885Sdim  // If an interrupt function has been set, go and run one it; otherwise,
840218885Sdim  // the process dies.
841218885Sdim  void (*IF)() = InterruptFunction;
842218885Sdim  InterruptFunction = 0;      // Don't run it on another CTRL-C.
843218885Sdim
844218885Sdim  if (IF) {
845218885Sdim    // Note: if the interrupt function throws an exception, there is nothing
846218885Sdim    // to catch it in this thread so it will kill the process.
847218885Sdim    IF();                     // Run it now.
848218885Sdim    LeaveCriticalSection(&CriticalSection);
849218885Sdim    return TRUE;              // Don't kill the process.
850218885Sdim  }
851218885Sdim
852218885Sdim  // Allow normal processing to take place; i.e., the process dies.
853218885Sdim  LeaveCriticalSection(&CriticalSection);
854218885Sdim  return FALSE;
855218885Sdim}
856226633Sdim
857226633Sdim#if __MINGW32__
858226633Sdim // We turned these warnings off for this file so that MinGW-g++ doesn't
859226633Sdim // complain about the ll format specifiers used.  Now we are turning the
860226633Sdim // warnings back on.  If MinGW starts to support diagnostic stacks, we can
861226633Sdim // replace this with a pop.
862226633Sdim #pragma GCC diagnostic warning "-Wformat"
863226633Sdim #pragma GCC diagnostic warning "-Wformat-extra-args"
864226633Sdim#endif
865