Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Improve handling of fatal errors on Windows, support creation of mini…

…dumps
  • Loading branch information...
commit 755c501084412a0896312af05cda6d4227136174 1 parent 6a09b96
Jiri Moudry authored

Showing 3 changed files with 120 additions and 0 deletions. Show diff stats Hide diff stats

  1. +35 0 src/ninja.cc
  2. +78 0 src/util.cc
  3. +7 0 src/util.h
35 src/ninja.cc
@@ -614,7 +614,42 @@ int RunBuild(Globals* globals, int argc, char** argv) {
614 614
615 615 } // anonymous namespace
616 616
  617 +
  618 +#ifdef _WIN32
  619 +
  620 +/// This handler processes fatal crashes that you can't catch
  621 +/// Test example: C++ exception in a stack-unwind-block
  622 +/// Real-world example: ninja launched a compiler to process a tricky C++ input file.
  623 +/// The compiler got itself into a state where it generated 3 GB of output and caused ninja to crash
  624 +void ninja_terminate_fct() {
  625 + Create_Win32_MiniDump(NULL);
  626 + Fatal("terminate handler called");
  627 +}
  628 +
  629 +/// main_unsafe is called from within an exception handling block
  630 +int main_unsafe(int argc, char** argv);
  631 +
  632 +/// Windows main() uses SEH (Structured exception handling)
  633 +int main(int argc, char** argv) {
  634 + // set a handler to catch crashes not caught by the __try..__except block (e.g. an exception in a stack-unwind-block)
  635 + set_terminate(ninja_terminate_fct);
  636 + __try {
  637 + // running inside __try ... __except suppresses any Windows error dialogs for errors such as bad_alloc
  638 + return main_unsafe(argc, argv);
  639 + }
  640 + __except(exception_filter(GetExceptionCode(), GetExceptionInformation())) {
  641 + // you will land here e.g. if you run out of memory, or run inside a distribution environment that fails
  642 + fprintf(stderr, "ninja: exception, exiting with error code 2\n");
  643 + // common error situations below return exitCode=1, 2 was chosen to indicate a more serious problem
  644 + return 2;
  645 + }
  646 +}
  647 +
  648 +int main_unsafe (int argc, char** argv) {
  649 +#else
  650 +//on Linux, we have no exception handling
617 651 int main(int argc, char** argv) {
  652 +#endif
618 653 Globals globals;
619 654 globals.ninja_command = argv[0];
620 655 const char* input_file = "build.ninja";
78 src/util.cc
@@ -34,6 +34,7 @@
34 34 #include <vector>
35 35
36 36 #ifdef _WIN32
  37 +#include <DbgHelp.h> // for minidump
37 38 #include <direct.h> // _mkdir
38 39 #endif
39 40
@@ -292,3 +293,80 @@ string StripAnsiEscapeCodes(const string& in) {
292 293 }
293 294 return stripped;
294 295 }
  296 +
  297 +#ifdef _WIN32
  298 +typedef BOOL (WINAPI *MiniDumpWriteDumpFunc) (
  299 + IN HANDLE,
  300 + IN DWORD,
  301 + IN HANDLE,
  302 + IN MINIDUMP_TYPE,
  303 + IN CONST PMINIDUMP_EXCEPTION_INFORMATION, OPTIONAL
  304 + IN CONST PMINIDUMP_USER_STREAM_INFORMATION, OPTIONAL
  305 + IN CONST PMINIDUMP_CALLBACK_INFORMATION OPTIONAL
  306 + );
  307 +
  308 +/// this function creates a windows minidump in temp folder.
  309 +void Create_Win32_MiniDump( _EXCEPTION_POINTERS* pep ) {
  310 + char tempPathBuff[MAX_PATH];
  311 + GetTempPath(sizeof(tempPathBuff), tempPathBuff);
  312 + char tempFileName[MAX_PATH];
  313 + sprintf(tempFileName, "%s\\ninja_crash_dump_%d.dmp", tempPathBuff, GetCurrentProcessId());
  314 +
  315 + //delete any previous minidump of the same name
  316 + DeleteFile(tempFileName);
  317 +
  318 + // load DbgHelp.dll dynamically, as library is not present on all windows versions
  319 + HMODULE hModDbgHelp = LoadLibrary("dbghelp.dll");
  320 + if (hModDbgHelp == NULL) {
  321 + fprintf(stderr, "ninja: failed to create minidump, failed to load dbghelp.dll, error %s \n",
  322 + GetLastErrorString().c_str());
  323 + return;
  324 + }
  325 +
  326 + MiniDumpWriteDumpFunc pfnMiniDumpWriteDump = (MiniDumpWriteDumpFunc)GetProcAddress(hModDbgHelp, "MiniDumpWriteDump");
  327 + if (pfnMiniDumpWriteDump == NULL) {
  328 + fprintf(stderr, "ninja: failed to create minidump, failed on GetProcAddress('MiniDumpWriteDump'), error %s \n",
  329 + GetLastErrorString().c_str());
  330 + return;
  331 + }
  332 +
  333 + // Open the file
  334 + HANDLE hFile = CreateFileA( tempFileName, GENERIC_READ | GENERIC_WRITE,
  335 + 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
  336 + if (hFile == NULL) {
  337 + fprintf(stderr, "ninja: failed to create minidump, failed on CreateFileA(%s), error %s \n",
  338 + tempFileName, GetLastErrorString().c_str());
  339 + return;
  340 + }
  341 +
  342 + // Create the mini dump
  343 + MINIDUMP_EXCEPTION_INFORMATION mdei;
  344 + mdei.ThreadId = GetCurrentThreadId();
  345 + mdei.ExceptionPointers = pep;
  346 + mdei.ClientPointers = FALSE;
  347 + MINIDUMP_TYPE mdt = (MINIDUMP_TYPE) (MiniDumpWithDataSegs | MiniDumpWithHandleData);
  348 +
  349 + BOOL rv = pfnMiniDumpWriteDump( GetCurrentProcess(), GetCurrentProcessId(),
  350 + hFile, mdt, (pep != 0) ? &mdei : 0, 0, 0 );
  351 +
  352 + if ( !rv )
  353 + fprintf(stderr, "ninja: MiniDumpWriteDump failed. Error: %s \n", GetLastErrorString().c_str() );
  354 + else
  355 + fprintf(stderr, "ninja: Minidump created: %s\n", tempFileName );
  356 +
  357 + // Close the file
  358 + CloseHandle( hFile );
  359 +}
  360 +
  361 +/// On Windows, we want to prevent error dialogs in case of exceptions.
  362 +/// This function handles the exception, and writes a minidump.
  363 +int exception_filter(unsigned int code, struct _EXCEPTION_POINTERS *ep) {
  364 + fprintf(stderr, "ninja.exe fatal error: 0x%X \n", code); //e.g. EXCEPTION_ACCESS_VIOLATION
  365 + fflush(stderr);
  366 + Create_Win32_MiniDump(ep);
  367 + return EXCEPTION_EXECUTE_HANDLER;
  368 +}
  369 +#else
  370 + //on Linux, core dumps are created automatically, no code needed
  371 +#endif
  372 +
7 src/util.h
@@ -76,6 +76,13 @@ string StripAnsiEscapeCodes(const string& in);
76 76 #endif
77 77
78 78 #ifdef _WIN32
  79 +
  80 +/// Handler for __except block
  81 +int exception_filter(unsigned int code, struct _EXCEPTION_POINTERS *ep);
  82 +
  83 +/// Write a windows minidump file in temp directory. User can specify null 'pep' argument.
  84 +void Create_Win32_MiniDump( struct _EXCEPTION_POINTERS* pep );
  85 +
79 86 /// Convert the value returned by GetLastError() into a string.
80 87 string GetLastErrorString();
81 88 #endif

0 comments on commit 755c501

Luis Lavena

What about GCC (MinGW)? It doesn't support SEH (try, except) so this will make compilation fail on Windows.

Jiri Moudry
Please sign in to comment.
Something went wrong with that request. Please try again.