276 lines
		
	
	
		
			8.4 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			276 lines
		
	
	
		
			8.4 KiB
		
	
	
	
		
			C++
		
	
	
	
| /* minidumper.cc
 | |
| 
 | |
|    Copyright 2014 Red Hat Inc.
 | |
| 
 | |
|    This file is part of Cygwin.
 | |
| 
 | |
|    This program is free software; you can redistribute it and/or modify
 | |
|    it under the terms of the GNU General Public License as published by
 | |
|    the Free Software Foundation; either version 2 of the License, or
 | |
|    (at your option) any later version.
 | |
| 
 | |
|    This program is distributed in the hope that it will be useful,
 | |
|    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
|    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | |
|    GNU General Public License (file COPYING.dumper) for more details.
 | |
| 
 | |
|    You should have received a copy of the GNU General Public License
 | |
|    along with this program; if not, write to the Free Software
 | |
|    Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
 | |
| */
 | |
| 
 | |
| #include <sys/cygwin.h>
 | |
| #include <cygwin/version.h>
 | |
| #include <getopt.h>
 | |
| #include <errno.h>
 | |
| #include <stdio.h>
 | |
| #include <stdlib.h>
 | |
| #include <windows.h>
 | |
| #include <dbghelp.h>
 | |
| 
 | |
| DEFINE_ENUM_FLAG_OPERATORS(MINIDUMP_TYPE);
 | |
| 
 | |
| BOOL verbose = FALSE;
 | |
| BOOL nokill = FALSE;
 | |
| 
 | |
| /* Not yet in dbghelp.h */
 | |
| #define MiniDumpWithModuleHeaders (static_cast<MINIDUMP_TYPE>(0x00080000))
 | |
| #define MiniDumpFilterTriage      (static_cast<MINIDUMP_TYPE>(0x00100000))
 | |
| 
 | |
| static MINIDUMP_TYPE
 | |
| filter_minidump_type(MINIDUMP_TYPE dump_type)
 | |
| {
 | |
|   API_VERSION build_version = { 6, 0, API_VERSION_NUMBER, 0 };
 | |
|   API_VERSION *v = ImagehlpApiVersionEx(&build_version);
 | |
| 
 | |
|   if (verbose)
 | |
|     printf ("dbghelp version %d.%d.%d.%d\n", v->MajorVersion,
 | |
|             v->MinorVersion, v->Revision, v->Reserved);
 | |
| 
 | |
|   MINIDUMP_TYPE supported_types = MiniDumpNormal | MiniDumpWithDataSegs
 | |
|     | MiniDumpWithFullMemory | MiniDumpWithHandleData | MiniDumpFilterMemory
 | |
|     | MiniDumpScanMemory;
 | |
| 
 | |
|   /*
 | |
|     This mainly trial and error and guesswork, as the MSDN documentation only
 | |
|     says what version of "Debugging Tools for Windows" added these flags, but
 | |
|     doesn't actually tell us the dbghelp.dll version which was contained in that
 | |
|     (and seems to have errors as well)
 | |
|   */
 | |
| 
 | |
|   if (v->MajorVersion >= 5)
 | |
|     supported_types |= MiniDumpWithUnloadedModules
 | |
|       | MiniDumpWithIndirectlyReferencedMemory | MiniDumpFilterModulePaths
 | |
|       | MiniDumpWithProcessThreadData | MiniDumpWithPrivateReadWriteMemory;
 | |
| 
 | |
|   if (v->MajorVersion >= 6)
 | |
|     supported_types |= MiniDumpWithoutOptionalData | MiniDumpWithFullMemoryInfo
 | |
|       | MiniDumpWithThreadInfo | MiniDumpWithCodeSegs
 | |
|       | MiniDumpWithoutAuxiliaryState | MiniDumpWithFullAuxiliaryState // seems to be documentation error that these two aren't listed as 'Not supported prior to 6.1'
 | |
|       | MiniDumpWithPrivateWriteCopyMemory | MiniDumpIgnoreInaccessibleMemory
 | |
|       | MiniDumpWithTokenInformation;
 | |
| 
 | |
|   if ((v->MajorVersion*10 + v->MinorVersion) >= 62)
 | |
|     supported_types |= MiniDumpWithModuleHeaders | MiniDumpFilterTriage; // seems to be documentation error that these two are listed as 'Not supported prior to 6.1'
 | |
| 
 | |
|   if (verbose)
 | |
|     printf ("supported MINIDUMP_TYPE flags 0x%x\n", supported_types);
 | |
| 
 | |
|   return (dump_type & supported_types);
 | |
| }
 | |
| 
 | |
| static void
 | |
| minidump(DWORD pid, MINIDUMP_TYPE dump_type, const char *minidump_file)
 | |
| {
 | |
|   HANDLE dump_file;
 | |
|   HANDLE process;
 | |
| 
 | |
|   dump_file = CreateFile(minidump_file,
 | |
|                          GENERIC_READ | GENERIC_WRITE,
 | |
|                          0,
 | |
|                          NULL,
 | |
|                          CREATE_ALWAYS,
 | |
|                          FILE_FLAG_BACKUP_SEMANTICS,
 | |
|                          NULL);
 | |
|   if (dump_file == INVALID_HANDLE_VALUE)
 | |
|     {
 | |
|       fprintf (stderr, "error opening file\n");
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|   process = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ | PROCESS_TERMINATE,
 | |
|                         FALSE,
 | |
|                         pid);
 | |
|   if (process == NULL)
 | |
|     {
 | |
|       fprintf (stderr, "error opening process\n");
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|   BOOL success = MiniDumpWriteDump(process,
 | |
|                                    pid,
 | |
|                                    dump_file,
 | |
|                                    dump_type,
 | |
|                                    NULL,
 | |
|                                    NULL,
 | |
|                                    NULL);
 | |
|   if (success)
 | |
|     {
 | |
|       if (verbose)
 | |
|         printf ("minidump created successfully\n");
 | |
|     }
 | |
|   else
 | |
|     {
 | |
|       fprintf (stderr, "error creating minidump\n");
 | |
|     }
 | |
| 
 | |
|   /* Unless nokill is given, behave like dumper and terminate the dumped
 | |
|      process */
 | |
|   if (!nokill)
 | |
|     {
 | |
|       TerminateProcess(process, 128 + 9);
 | |
|       WaitForSingleObject(process, INFINITE);
 | |
|     }
 | |
| 
 | |
|   CloseHandle(process);
 | |
|   CloseHandle(dump_file);
 | |
| }
 | |
| 
 | |
| static void
 | |
| usage (FILE *stream, int status)
 | |
| {
 | |
|   fprintf (stream, "\
 | |
| Usage: %s [OPTION] FILENAME WIN32PID\n\
 | |
| \n\
 | |
| Write minidump from WIN32PID to FILENAME.dmp\n\
 | |
| \n\
 | |
|  -t, --type     minidump type flags\n\
 | |
|  -n, --nokill   don't terminate the dumped process\n\
 | |
|  -d, --verbose  be verbose while dumping\n\
 | |
|  -h, --help     output help information and exit\n\
 | |
|  -q, --quiet    be quiet while dumping (default)\n\
 | |
|  -V, --version  output version information and exit\n\
 | |
| \n", program_invocation_short_name);
 | |
|   exit (status);
 | |
| }
 | |
| 
 | |
| struct option longopts[] = {
 | |
|   {"type", required_argument, NULL, 't'},
 | |
|   {"nokill", no_argument, NULL, 'n'},
 | |
|   {"verbose", no_argument, NULL, 'd'},
 | |
|   {"help", no_argument, NULL, 'h'},
 | |
|   {"quiet", no_argument, NULL, 'q'},
 | |
|   {"version", no_argument, 0, 'V'},
 | |
|   {0, no_argument, NULL, 0}
 | |
| };
 | |
| const char *opts = "t:ndhqV";
 | |
| 
 | |
| static void
 | |
| print_version ()
 | |
| {
 | |
|   printf ("minidumper (cygwin) %d.%d.%d\n"
 | |
|           "Minidump write for Cygwin\n"
 | |
|           "Copyright (C) 1999 - %s Red Hat, Inc.\n"
 | |
|           "This is free software; see the source for copying conditions.  There is NO\n"
 | |
|           "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n",
 | |
|           CYGWIN_VERSION_DLL_MAJOR / 1000,
 | |
|           CYGWIN_VERSION_DLL_MAJOR % 1000,
 | |
|           CYGWIN_VERSION_DLL_MINOR,
 | |
|           strrchr (__DATE__, ' ') + 1);
 | |
| }
 | |
| 
 | |
| int
 | |
| main (int argc, char **argv)
 | |
| {
 | |
|   int opt;
 | |
|   const char *p = "";
 | |
|   DWORD pid;
 | |
|   BOOL default_dump_type = TRUE;
 | |
|   MINIDUMP_TYPE dump_type = MiniDumpNormal;
 | |
| 
 | |
|   while ((opt = getopt_long (argc, argv, opts, longopts, NULL) ) != EOF)
 | |
|     switch (opt)
 | |
|       {
 | |
|       case 't':
 | |
|         {
 | |
|           char *endptr;
 | |
|           default_dump_type = FALSE;
 | |
|           dump_type = (MINIDUMP_TYPE)strtoul(optarg, &endptr, 0);
 | |
|           if (*endptr != '\0')
 | |
|             {
 | |
|               fprintf (stderr, "syntax error in minidump type \"%s\" near character #%d.\n", optarg, (int) (endptr - optarg));
 | |
|               exit(1);
 | |
|             }
 | |
|         }
 | |
|         break;
 | |
|       case 'n':
 | |
|         nokill = TRUE;
 | |
|         break;
 | |
|       case 'd':
 | |
|         verbose = TRUE;
 | |
|         break;
 | |
|       case 'q':
 | |
|         verbose = FALSE;
 | |
|         break;
 | |
|       case 'h':
 | |
|         usage (stdout, 0);
 | |
|       case 'V':
 | |
|         print_version ();
 | |
|         exit (0);
 | |
|       default:
 | |
|         fprintf (stderr, "Try `%s --help' for more information.\n",
 | |
|                  program_invocation_short_name);
 | |
|         exit (1);
 | |
|       }
 | |
| 
 | |
|   if (argv && *(argv + optind) && *(argv + optind +1))
 | |
|     {
 | |
|       ssize_t len = cygwin_conv_path (CCP_POSIX_TO_WIN_A | CCP_RELATIVE,
 | |
|                                       *(argv + optind), NULL, 0);
 | |
|       char *win32_name = (char *) alloca (len);
 | |
|       cygwin_conv_path (CCP_POSIX_TO_WIN_A | CCP_RELATIVE,  *(argv + optind),
 | |
|                         win32_name, len);
 | |
|       if ((p = strrchr (win32_name, '\\')))
 | |
|         p++;
 | |
|       else
 | |
|         p = win32_name;
 | |
| 
 | |
|       pid = strtoul (*(argv + optind + 1), NULL, 10);
 | |
|     }
 | |
|   else
 | |
|     {
 | |
|       usage (stderr, 1);
 | |
|       return -1;
 | |
|     }
 | |
| 
 | |
|   char *minidump_file = (char *) malloc (strlen (p) + sizeof (".dmp"));
 | |
|   if (!minidump_file)
 | |
|     {
 | |
|       fprintf (stderr, "error allocating memory\n");
 | |
|       return -1;
 | |
|     }
 | |
|   sprintf (minidump_file, "%s.dmp", p);
 | |
| 
 | |
|   if (default_dump_type)
 | |
|     {
 | |
|       dump_type = MiniDumpWithHandleData | MiniDumpWithFullMemoryInfo
 | |
|         | MiniDumpWithThreadInfo | MiniDumpWithFullAuxiliaryState
 | |
|         | MiniDumpIgnoreInaccessibleMemory | MiniDumpWithTokenInformation
 | |
|         | MiniDumpWithIndirectlyReferencedMemory;
 | |
| 
 | |
|       /*
 | |
|         Only filter out unsupported dump_type flags if we are using the default
 | |
|         dump type, so that future dump_type flags can be explicitly used even if
 | |
|         we don't know about them
 | |
|       */
 | |
|       dump_type = filter_minidump_type(dump_type);
 | |
|     }
 | |
| 
 | |
|   if (verbose)
 | |
|     printf ("dumping process %u to %s using dump type flags 0x%x\n", (unsigned int)pid, minidump_file, (unsigned int)dump_type);
 | |
| 
 | |
|   minidump(pid, dump_type, minidump_file);
 | |
| 
 | |
|   free (minidump_file);
 | |
| };
 |