| 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 |