OLD | NEW |
| (Empty) |
1 // Copyright (c) 2008, Google Inc. | |
2 // All rights reserved. | |
3 // | |
4 // Redistribution and use in source and binary forms, with or without | |
5 // modification, are permitted provided that the following conditions are | |
6 // met: | |
7 // | |
8 // * Redistributions of source code must retain the above copyright | |
9 // notice, this list of conditions and the following disclaimer. | |
10 // * Redistributions in binary form must reproduce the above | |
11 // copyright notice, this list of conditions and the following disclaimer | |
12 // in the documentation and/or other materials provided with the | |
13 // distribution. | |
14 // * Neither the name of Google Inc. nor the names of its | |
15 // contributors may be used to endorse or promote products derived from | |
16 // this software without specific prior written permission. | |
17 // | |
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
29 | |
30 #include "client/windows/crash_generation/minidump_generator.h" | |
31 #include <cassert> | |
32 #include "client/windows/common/auto_critical_section.h" | |
33 #include "common/windows/guid_string.h" | |
34 | |
35 using std::wstring; | |
36 | |
37 namespace google_breakpad { | |
38 | |
39 MinidumpGenerator::MinidumpGenerator(const wstring& dump_path) | |
40 : dbghelp_module_(NULL), | |
41 rpcrt4_module_(NULL), | |
42 dump_path_(dump_path), | |
43 write_dump_(NULL), | |
44 create_uuid_(NULL) { | |
45 InitializeCriticalSection(&module_load_sync_); | |
46 InitializeCriticalSection(&get_proc_address_sync_); | |
47 } | |
48 | |
49 MinidumpGenerator::~MinidumpGenerator() { | |
50 if (dbghelp_module_) { | |
51 FreeLibrary(dbghelp_module_); | |
52 } | |
53 | |
54 if (rpcrt4_module_) { | |
55 FreeLibrary(rpcrt4_module_); | |
56 } | |
57 | |
58 DeleteCriticalSection(&get_proc_address_sync_); | |
59 DeleteCriticalSection(&module_load_sync_); | |
60 } | |
61 | |
62 bool MinidumpGenerator::WriteMinidump(HANDLE process_handle, | |
63 DWORD process_id, | |
64 DWORD thread_id, | |
65 DWORD requesting_thread_id, | |
66 EXCEPTION_POINTERS* exception_pointers, | |
67 MDRawAssertionInfo* assert_info, | |
68 MINIDUMP_TYPE dump_type, | |
69 bool is_client_pointers, | |
70 wstring* dump_path) { | |
71 // Just call the full WriteMinidump with NULL as the full_dump_path. | |
72 return this->WriteMinidump(process_handle, process_id, thread_id, | |
73 requesting_thread_id, exception_pointers, | |
74 assert_info, dump_type, is_client_pointers, | |
75 dump_path, NULL); | |
76 } | |
77 | |
78 bool MinidumpGenerator::WriteMinidump(HANDLE process_handle, | |
79 DWORD process_id, | |
80 DWORD thread_id, | |
81 DWORD requesting_thread_id, | |
82 EXCEPTION_POINTERS* exception_pointers, | |
83 MDRawAssertionInfo* assert_info, | |
84 MINIDUMP_TYPE dump_type, | |
85 bool is_client_pointers, | |
86 wstring* dump_path, | |
87 wstring* full_dump_path) { | |
88 MiniDumpWriteDumpType write_dump = GetWriteDump(); | |
89 if (!write_dump) { | |
90 return false; | |
91 } | |
92 | |
93 wstring dump_file_path; | |
94 if (!GenerateDumpFilePath(&dump_file_path)) { | |
95 return false; | |
96 } | |
97 | |
98 // If the client requests a full memory dump, we will write a normal mini | |
99 // dump and a full memory dump. Both dump files use the same uuid as file | |
100 // name prefix. | |
101 bool full_memory_dump = (dump_type & MiniDumpWithFullMemory) != 0; | |
102 wstring full_dump_file_path; | |
103 if (full_memory_dump) { | |
104 full_dump_file_path.assign(dump_file_path); | |
105 full_dump_file_path.resize(full_dump_file_path.size() - 4); // strip .dmp | |
106 full_dump_file_path.append(TEXT("-full.dmp")); | |
107 } | |
108 | |
109 HANDLE dump_file = CreateFile(dump_file_path.c_str(), | |
110 GENERIC_WRITE, | |
111 0, | |
112 NULL, | |
113 CREATE_NEW, | |
114 FILE_ATTRIBUTE_NORMAL, | |
115 NULL); | |
116 | |
117 if (dump_file == INVALID_HANDLE_VALUE) { | |
118 return false; | |
119 } | |
120 | |
121 HANDLE full_dump_file = INVALID_HANDLE_VALUE; | |
122 if (full_memory_dump) { | |
123 full_dump_file = CreateFile(full_dump_file_path.c_str(), | |
124 GENERIC_WRITE, | |
125 0, | |
126 NULL, | |
127 CREATE_NEW, | |
128 FILE_ATTRIBUTE_NORMAL, | |
129 NULL); | |
130 | |
131 if (full_dump_file == INVALID_HANDLE_VALUE) { | |
132 CloseHandle(dump_file); | |
133 return false; | |
134 } | |
135 } | |
136 | |
137 MINIDUMP_EXCEPTION_INFORMATION* dump_exception_pointers = NULL; | |
138 MINIDUMP_EXCEPTION_INFORMATION dump_exception_info; | |
139 | |
140 // Setup the exception information object only if it's a dump | |
141 // due to an exception. | |
142 if (exception_pointers) { | |
143 dump_exception_pointers = &dump_exception_info; | |
144 dump_exception_info.ThreadId = thread_id; | |
145 dump_exception_info.ExceptionPointers = exception_pointers; | |
146 dump_exception_info.ClientPointers = is_client_pointers; | |
147 } | |
148 | |
149 // Add an MDRawBreakpadInfo stream to the minidump, to provide additional | |
150 // information about the exception handler to the Breakpad processor. | |
151 // The information will help the processor determine which threads are | |
152 // relevant. The Breakpad processor does not require this information but | |
153 // can function better with Breakpad-generated dumps when it is present. | |
154 // The native debugger is not harmed by the presence of this information. | |
155 MDRawBreakpadInfo breakpad_info = {0}; | |
156 if (!is_client_pointers) { | |
157 // Set the dump thread id and requesting thread id only in case of | |
158 // in-process dump generation. | |
159 breakpad_info.validity = MD_BREAKPAD_INFO_VALID_DUMP_THREAD_ID | | |
160 MD_BREAKPAD_INFO_VALID_REQUESTING_THREAD_ID; | |
161 breakpad_info.dump_thread_id = thread_id; | |
162 breakpad_info.requesting_thread_id = requesting_thread_id; | |
163 } | |
164 | |
165 // Leave room in user_stream_array for a possible assertion info stream. | |
166 MINIDUMP_USER_STREAM user_stream_array[2]; | |
167 user_stream_array[0].Type = MD_BREAKPAD_INFO_STREAM; | |
168 user_stream_array[0].BufferSize = sizeof(breakpad_info); | |
169 user_stream_array[0].Buffer = &breakpad_info; | |
170 | |
171 MINIDUMP_USER_STREAM_INFORMATION user_streams; | |
172 user_streams.UserStreamCount = 1; | |
173 user_streams.UserStreamArray = user_stream_array; | |
174 | |
175 MDRawAssertionInfo* actual_assert_info = assert_info; | |
176 MDRawAssertionInfo client_assert_info = {0}; | |
177 | |
178 if (assert_info) { | |
179 // If the assertion info object lives in the client process, | |
180 // read the memory of the client process. | |
181 if (is_client_pointers) { | |
182 SIZE_T bytes_read = 0; | |
183 if (!ReadProcessMemory(process_handle, | |
184 assert_info, | |
185 &client_assert_info, | |
186 sizeof(client_assert_info), | |
187 &bytes_read)) { | |
188 CloseHandle(dump_file); | |
189 if (full_dump_file != INVALID_HANDLE_VALUE) | |
190 CloseHandle(full_dump_file); | |
191 return false; | |
192 } | |
193 | |
194 if (bytes_read != sizeof(client_assert_info)) { | |
195 CloseHandle(dump_file); | |
196 if (full_dump_file != INVALID_HANDLE_VALUE) | |
197 CloseHandle(full_dump_file); | |
198 return false; | |
199 } | |
200 | |
201 actual_assert_info = &client_assert_info; | |
202 } | |
203 | |
204 user_stream_array[1].Type = MD_ASSERTION_INFO_STREAM; | |
205 user_stream_array[1].BufferSize = sizeof(MDRawAssertionInfo); | |
206 user_stream_array[1].Buffer = actual_assert_info; | |
207 ++user_streams.UserStreamCount; | |
208 } | |
209 | |
210 bool result_minidump = write_dump( | |
211 process_handle, | |
212 process_id, | |
213 dump_file, | |
214 static_cast<MINIDUMP_TYPE>((dump_type & (~MiniDumpWithFullMemory)) | |
215 | MiniDumpNormal), | |
216 exception_pointers ? &dump_exception_info : NULL, | |
217 &user_streams, | |
218 NULL) != FALSE; | |
219 | |
220 bool result_full_memory = true; | |
221 if (full_memory_dump) { | |
222 result_full_memory = write_dump( | |
223 process_handle, | |
224 process_id, | |
225 full_dump_file, | |
226 static_cast<MINIDUMP_TYPE>(dump_type & (~MiniDumpNormal)), | |
227 exception_pointers ? &dump_exception_info : NULL, | |
228 &user_streams, | |
229 NULL) != FALSE; | |
230 } | |
231 | |
232 bool result = result_minidump && result_full_memory; | |
233 | |
234 CloseHandle(dump_file); | |
235 if (full_dump_file != INVALID_HANDLE_VALUE) | |
236 CloseHandle(full_dump_file); | |
237 | |
238 // Store the path of the dump file in the out parameter if dump generation | |
239 // succeeded. | |
240 if (result && dump_path) { | |
241 *dump_path = dump_file_path; | |
242 } | |
243 if (result && full_memory_dump && full_dump_path) { | |
244 *full_dump_path = full_dump_file_path; | |
245 } | |
246 | |
247 return result; | |
248 } | |
249 | |
250 HMODULE MinidumpGenerator::GetDbghelpModule() { | |
251 AutoCriticalSection lock(&module_load_sync_); | |
252 if (!dbghelp_module_) { | |
253 dbghelp_module_ = LoadLibrary(TEXT("dbghelp.dll")); | |
254 } | |
255 | |
256 return dbghelp_module_; | |
257 } | |
258 | |
259 MinidumpGenerator::MiniDumpWriteDumpType MinidumpGenerator::GetWriteDump() { | |
260 AutoCriticalSection lock(&get_proc_address_sync_); | |
261 if (!write_dump_) { | |
262 HMODULE module = GetDbghelpModule(); | |
263 if (module) { | |
264 FARPROC proc = GetProcAddress(module, "MiniDumpWriteDump"); | |
265 write_dump_ = reinterpret_cast<MiniDumpWriteDumpType>(proc); | |
266 } | |
267 } | |
268 | |
269 return write_dump_; | |
270 } | |
271 | |
272 HMODULE MinidumpGenerator::GetRpcrt4Module() { | |
273 AutoCriticalSection lock(&module_load_sync_); | |
274 if (!rpcrt4_module_) { | |
275 rpcrt4_module_ = LoadLibrary(TEXT("rpcrt4.dll")); | |
276 } | |
277 | |
278 return rpcrt4_module_; | |
279 } | |
280 | |
281 MinidumpGenerator::UuidCreateType MinidumpGenerator::GetCreateUuid() { | |
282 AutoCriticalSection lock(&module_load_sync_); | |
283 if (!create_uuid_) { | |
284 HMODULE module = GetRpcrt4Module(); | |
285 if (module) { | |
286 FARPROC proc = GetProcAddress(module, "UuidCreate"); | |
287 create_uuid_ = reinterpret_cast<UuidCreateType>(proc); | |
288 } | |
289 } | |
290 | |
291 return create_uuid_; | |
292 } | |
293 | |
294 bool MinidumpGenerator::GenerateDumpFilePath(wstring* file_path) { | |
295 UUID id = {0}; | |
296 | |
297 UuidCreateType create_uuid = GetCreateUuid(); | |
298 if (!create_uuid) { | |
299 return false; | |
300 } | |
301 | |
302 create_uuid(&id); | |
303 wstring id_str = GUIDString::GUIDToWString(&id); | |
304 | |
305 *file_path = dump_path_ + TEXT("\\") + id_str + TEXT(".dmp"); | |
306 return true; | |
307 } | |
308 | |
309 } // namespace google_breakpad | |
OLD | NEW |