]>
Commit | Line | Data |
---|---|---|
d0e5dd3a JT |
1 | /* minidumper.cc |
2 | ||
d0e5dd3a JT |
3 | This file is part of Cygwin. |
4 | ||
5 | This program is free software; you can redistribute it and/or modify | |
6 | it under the terms of the GNU General Public License as published by | |
6e623e93 | 7 | the Free Software Foundation; either version 3 of the License, or |
d0e5dd3a JT |
8 | (at your option) any later version. |
9 | ||
10 | This program is distributed in the hope that it will be useful, | |
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | GNU General Public License (file COPYING.dumper) for more details. | |
14 | ||
15 | You should have received a copy of the GNU General Public License | |
16 | along with this program; if not, write to the Free Software | |
17 | Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. | |
18 | */ | |
19 | ||
20 | #include <sys/cygwin.h> | |
21 | #include <cygwin/version.h> | |
22 | #include <getopt.h> | |
23 | #include <errno.h> | |
24 | #include <stdio.h> | |
25 | #include <stdlib.h> | |
26 | #include <windows.h> | |
8f8e7757 | 27 | #include <dbghelp.h> |
d0e5dd3a | 28 | |
2915feb8 JT |
29 | DEFINE_ENUM_FLAG_OPERATORS(MINIDUMP_TYPE); |
30 | ||
d0e5dd3a JT |
31 | BOOL verbose = FALSE; |
32 | BOOL nokill = FALSE; | |
33 | ||
2915feb8 JT |
34 | /* Not yet in dbghelp.h */ |
35 | #define MiniDumpWithModuleHeaders (static_cast<MINIDUMP_TYPE>(0x00080000)) | |
36 | #define MiniDumpFilterTriage (static_cast<MINIDUMP_TYPE>(0x00100000)) | |
37 | ||
38 | static MINIDUMP_TYPE | |
39 | filter_minidump_type(MINIDUMP_TYPE dump_type) | |
40 | { | |
41 | API_VERSION build_version = { 6, 0, API_VERSION_NUMBER, 0 }; | |
42 | API_VERSION *v = ImagehlpApiVersionEx(&build_version); | |
43 | ||
44 | if (verbose) | |
45 | printf ("dbghelp version %d.%d.%d.%d\n", v->MajorVersion, | |
46 | v->MinorVersion, v->Revision, v->Reserved); | |
47 | ||
48 | MINIDUMP_TYPE supported_types = MiniDumpNormal | MiniDumpWithDataSegs | |
49 | | MiniDumpWithFullMemory | MiniDumpWithHandleData | MiniDumpFilterMemory | |
50 | | MiniDumpScanMemory; | |
51 | ||
52 | /* | |
53 | This mainly trial and error and guesswork, as the MSDN documentation only | |
54 | says what version of "Debugging Tools for Windows" added these flags, but | |
55 | doesn't actually tell us the dbghelp.dll version which was contained in that | |
56 | (and seems to have errors as well) | |
57 | */ | |
58 | ||
59 | if (v->MajorVersion >= 5) | |
60 | supported_types |= MiniDumpWithUnloadedModules | |
61 | | MiniDumpWithIndirectlyReferencedMemory | MiniDumpFilterModulePaths | |
62 | | MiniDumpWithProcessThreadData | MiniDumpWithPrivateReadWriteMemory; | |
63 | ||
64 | if (v->MajorVersion >= 6) | |
65 | supported_types |= MiniDumpWithoutOptionalData | MiniDumpWithFullMemoryInfo | |
66 | | MiniDumpWithThreadInfo | MiniDumpWithCodeSegs | |
67 | | MiniDumpWithoutAuxiliaryState | MiniDumpWithFullAuxiliaryState // seems to be documentation error that these two aren't listed as 'Not supported prior to 6.1' | |
68 | | MiniDumpWithPrivateWriteCopyMemory | MiniDumpIgnoreInaccessibleMemory | |
69 | | MiniDumpWithTokenInformation; | |
70 | ||
71 | if ((v->MajorVersion*10 + v->MinorVersion) >= 62) | |
72 | supported_types |= MiniDumpWithModuleHeaders | MiniDumpFilterTriage; // seems to be documentation error that these two are listed as 'Not supported prior to 6.1' | |
73 | ||
74 | if (verbose) | |
75 | printf ("supported MINIDUMP_TYPE flags 0x%x\n", supported_types); | |
76 | ||
77 | return (dump_type & supported_types); | |
78 | } | |
79 | ||
d0e5dd3a JT |
80 | static void |
81 | minidump(DWORD pid, MINIDUMP_TYPE dump_type, const char *minidump_file) | |
82 | { | |
83 | HANDLE dump_file; | |
84 | HANDLE process; | |
d0e5dd3a JT |
85 | |
86 | dump_file = CreateFile(minidump_file, | |
87 | GENERIC_READ | GENERIC_WRITE, | |
88 | 0, | |
89 | NULL, | |
90 | CREATE_ALWAYS, | |
91 | FILE_FLAG_BACKUP_SEMANTICS, | |
92 | NULL); | |
93 | if (dump_file == INVALID_HANDLE_VALUE) | |
94 | { | |
95 | fprintf (stderr, "error opening file\n"); | |
96 | return; | |
97 | } | |
98 | ||
99 | process = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ | PROCESS_TERMINATE, | |
100 | FALSE, | |
101 | pid); | |
638f0ebf | 102 | if (process == NULL) |
d0e5dd3a JT |
103 | { |
104 | fprintf (stderr, "error opening process\n"); | |
105 | return; | |
106 | } | |
107 | ||
8f8e7757 JT |
108 | BOOL success = MiniDumpWriteDump(process, |
109 | pid, | |
110 | dump_file, | |
111 | dump_type, | |
112 | NULL, | |
113 | NULL, | |
114 | NULL); | |
d0e5dd3a JT |
115 | if (success) |
116 | { | |
117 | if (verbose) | |
118 | printf ("minidump created successfully\n"); | |
119 | } | |
120 | else | |
121 | { | |
122 | fprintf (stderr, "error creating minidump\n"); | |
123 | } | |
124 | ||
125 | /* Unless nokill is given, behave like dumper and terminate the dumped | |
126 | process */ | |
127 | if (!nokill) | |
128 | { | |
129 | TerminateProcess(process, 128 + 9); | |
130 | WaitForSingleObject(process, INFINITE); | |
131 | } | |
132 | ||
133 | CloseHandle(process); | |
134 | CloseHandle(dump_file); | |
d0e5dd3a JT |
135 | } |
136 | ||
e7fca6f8 | 137 | static void __attribute__ ((__noreturn__)) |
d0e5dd3a JT |
138 | usage (FILE *stream, int status) |
139 | { | |
140 | fprintf (stream, "\ | |
141 | Usage: %s [OPTION] FILENAME WIN32PID\n\ | |
142 | \n\ | |
143 | Write minidump from WIN32PID to FILENAME.dmp\n\ | |
144 | \n\ | |
145 | -t, --type minidump type flags\n\ | |
146 | -n, --nokill don't terminate the dumped process\n\ | |
147 | -d, --verbose be verbose while dumping\n\ | |
148 | -h, --help output help information and exit\n\ | |
149 | -q, --quiet be quiet while dumping (default)\n\ | |
150 | -V, --version output version information and exit\n\ | |
151 | \n", program_invocation_short_name); | |
152 | exit (status); | |
153 | } | |
154 | ||
155 | struct option longopts[] = { | |
156 | {"type", required_argument, NULL, 't'}, | |
157 | {"nokill", no_argument, NULL, 'n'}, | |
158 | {"verbose", no_argument, NULL, 'd'}, | |
159 | {"help", no_argument, NULL, 'h'}, | |
160 | {"quiet", no_argument, NULL, 'q'}, | |
161 | {"version", no_argument, 0, 'V'}, | |
162 | {0, no_argument, NULL, 0} | |
163 | }; | |
bdcad00b | 164 | const char *opts = "t:ndhqV"; |
d0e5dd3a JT |
165 | |
166 | static void | |
167 | print_version () | |
168 | { | |
169 | printf ("minidumper (cygwin) %d.%d.%d\n" | |
170 | "Minidump write for Cygwin\n" | |
6e623e93 | 171 | "Copyright (C) 1999 - %s Cygwin Authors\n" |
d0e5dd3a JT |
172 | "This is free software; see the source for copying conditions. There is NO\n" |
173 | "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n", | |
174 | CYGWIN_VERSION_DLL_MAJOR / 1000, | |
175 | CYGWIN_VERSION_DLL_MAJOR % 1000, | |
176 | CYGWIN_VERSION_DLL_MINOR, | |
177 | strrchr (__DATE__, ' ') + 1); | |
178 | } | |
179 | ||
180 | int | |
181 | main (int argc, char **argv) | |
182 | { | |
183 | int opt; | |
184 | const char *p = ""; | |
185 | DWORD pid; | |
2915feb8 | 186 | BOOL default_dump_type = TRUE; |
8f8e7757 | 187 | MINIDUMP_TYPE dump_type = MiniDumpNormal; |
d0e5dd3a JT |
188 | |
189 | while ((opt = getopt_long (argc, argv, opts, longopts, NULL) ) != EOF) | |
190 | switch (opt) | |
191 | { | |
192 | case 't': | |
193 | { | |
194 | char *endptr; | |
2915feb8 | 195 | default_dump_type = FALSE; |
8f8e7757 | 196 | dump_type = (MINIDUMP_TYPE)strtoul(optarg, &endptr, 0); |
d0e5dd3a JT |
197 | if (*endptr != '\0') |
198 | { | |
199 | fprintf (stderr, "syntax error in minidump type \"%s\" near character #%d.\n", optarg, (int) (endptr - optarg)); | |
200 | exit(1); | |
201 | } | |
202 | } | |
203 | break; | |
204 | case 'n': | |
205 | nokill = TRUE; | |
206 | break; | |
207 | case 'd': | |
208 | verbose = TRUE; | |
209 | break; | |
210 | case 'q': | |
211 | verbose = FALSE; | |
212 | break; | |
213 | case 'h': | |
214 | usage (stdout, 0); | |
215 | case 'V': | |
216 | print_version (); | |
217 | exit (0); | |
218 | default: | |
219 | fprintf (stderr, "Try `%s --help' for more information.\n", | |
220 | program_invocation_short_name); | |
221 | exit (1); | |
222 | } | |
223 | ||
224 | if (argv && *(argv + optind) && *(argv + optind +1)) | |
225 | { | |
226 | ssize_t len = cygwin_conv_path (CCP_POSIX_TO_WIN_A | CCP_RELATIVE, | |
227 | *(argv + optind), NULL, 0); | |
228 | char *win32_name = (char *) alloca (len); | |
229 | cygwin_conv_path (CCP_POSIX_TO_WIN_A | CCP_RELATIVE, *(argv + optind), | |
230 | win32_name, len); | |
231 | if ((p = strrchr (win32_name, '\\'))) | |
232 | p++; | |
233 | else | |
234 | p = win32_name; | |
235 | ||
236 | pid = strtoul (*(argv + optind + 1), NULL, 10); | |
237 | } | |
238 | else | |
239 | { | |
240 | usage (stderr, 1); | |
241 | return -1; | |
242 | } | |
243 | ||
244 | char *minidump_file = (char *) malloc (strlen (p) + sizeof (".dmp")); | |
245 | if (!minidump_file) | |
246 | { | |
247 | fprintf (stderr, "error allocating memory\n"); | |
248 | return -1; | |
249 | } | |
250 | sprintf (minidump_file, "%s.dmp", p); | |
251 | ||
2915feb8 JT |
252 | if (default_dump_type) |
253 | { | |
254 | dump_type = MiniDumpWithHandleData | MiniDumpWithFullMemoryInfo | |
255 | | MiniDumpWithThreadInfo | MiniDumpWithFullAuxiliaryState | |
256 | | MiniDumpIgnoreInaccessibleMemory | MiniDumpWithTokenInformation | |
257 | | MiniDumpWithIndirectlyReferencedMemory; | |
258 | ||
259 | /* | |
260 | Only filter out unsupported dump_type flags if we are using the default | |
261 | dump type, so that future dump_type flags can be explicitly used even if | |
262 | we don't know about them | |
263 | */ | |
264 | dump_type = filter_minidump_type(dump_type); | |
265 | } | |
266 | ||
d0e5dd3a JT |
267 | if (verbose) |
268 | printf ("dumping process %u to %s using dump type flags 0x%x\n", (unsigned int)pid, minidump_file, (unsigned int)dump_type); | |
269 | ||
270 | minidump(pid, dump_type, minidump_file); | |
271 | ||
272 | free (minidump_file); | |
273 | }; |