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 32 #include <assert.h> 33 #include <avrfsdk.h> 34 35 #include <algorithm> 36 #include <iterator> 37 #include <list> 38 #include <vector> 39 40 #include "client/windows/common/auto_critical_section.h" 41 #include "common/scoped_ptr.h" 42 #include "common/windows/guid_string.h" 43 44 using std::wstring; 45 46 namespace { 47 48 // A helper class used to collect handle operations data. Unlike 49 // |MiniDumpWithHandleData| it records the operations for a single handle value 50 // only, making it possible to include this information to a minidump. 51 class HandleTraceData { 52 public: 53 HandleTraceData(); 54 ~HandleTraceData(); 55 56 // Collects the handle operations data and formats a user stream to be added 57 // to the minidump. 58 bool CollectHandleData(HANDLE process_handle, 59 EXCEPTION_POINTERS* exception_pointers); 60 61 // Fills the user dump entry with a pointer to the collected handle operations 62 // data. Returns |true| if the entry was initialized successfully, or |false| 63 // if no trace data is available. 64 bool GetUserStream(MINIDUMP_USER_STREAM* user_stream); 65 66 private: 67 // Reads the exception code from the client process's address space. 68 // This routine assumes that the client process's pointer width matches ours. 69 static bool ReadExceptionCode(HANDLE process_handle, 70 EXCEPTION_POINTERS* exception_pointers, 71 DWORD* exception_code); 72 73 // Stores handle operations retrieved by VerifierEnumerateResource(). 74 static ULONG CALLBACK RecordHandleOperations(void* resource_description, 75 void* enumeration_context, 76 ULONG* enumeration_level); 77 78 // Function pointer type for VerifierEnumerateResource, which is looked up 79 // dynamically. 80 typedef BOOL (WINAPI* VerifierEnumerateResourceType)( 81 HANDLE Process, 82 ULONG Flags, 83 ULONG ResourceType, 84 AVRF_RESOURCE_ENUMERATE_CALLBACK ResourceCallback, 85 PVOID EnumerationContext); 86 87 // Handle to dynamically loaded verifier.dll. 88 HMODULE verifier_module_; 89 90 // Pointer to the VerifierEnumerateResource function. 91 VerifierEnumerateResourceType enumerate_resource_; 92 93 // Handle value to look for. 94 ULONG64 handle_; 95 96 // List of handle operations for |handle_|. 97 std::list<AVRF_HANDLE_OPERATION> operations_; 98 99 // Minidump stream data. 100 std::vector<char> stream_; 101 }; 102 103 HandleTraceData::HandleTraceData() 104 : verifier_module_(NULL), 105 enumerate_resource_(NULL), 106 handle_(NULL) { 107 } 108 109 HandleTraceData::~HandleTraceData() { 110 if (verifier_module_) { 111 FreeLibrary(verifier_module_); 112 } 113 } 114 115 bool HandleTraceData::CollectHandleData( 116 HANDLE process_handle, 117 EXCEPTION_POINTERS* exception_pointers) { 118 DWORD exception_code; 119 if (!ReadExceptionCode(process_handle, exception_pointers, &exception_code)) { 120 return false; 121 } 122 123 // Verify whether the execption is STATUS_INVALID_HANDLE. Do not record any 124 // handle information if it is a different exception to keep the minidump 125 // small. 126 if (exception_code != STATUS_INVALID_HANDLE) { 127 return true; 128 } 129 130 // Load verifier!VerifierEnumerateResource() dynamically. 131 verifier_module_ = LoadLibrary(TEXT("verifier.dll")); 132 if (!verifier_module_) { 133 return false; 134 } 135 136 enumerate_resource_ = reinterpret_cast<VerifierEnumerateResourceType>( 137 GetProcAddress(verifier_module_, "VerifierEnumerateResource")); 138 if (!enumerate_resource_) { 139 return false; 140 } 141 142 // STATUS_INVALID_HANDLE does not provide the offending handle value in 143 // the exception parameters so we have to guess. At the moment we scan 144 // the handle operations trace looking for the last invalid handle operation 145 // and record only the operations for that handle value. 146 if (enumerate_resource_(process_handle, 147 0, 148 AvrfResourceHandleTrace, 149 &RecordHandleOperations, 150 this) != ERROR_SUCCESS) { 151 // The handle tracing must have not been enabled. 152 return true; 153 } 154 155 // Now that |handle_| is initialized, purge all irrelevant operations. 156 std::list<AVRF_HANDLE_OPERATION>::iterator i = operations_.begin(); 157 std::list<AVRF_HANDLE_OPERATION>::iterator i_end = operations_.end(); 158 while (i != i_end) { 159 if (i->Handle == handle_) { 160 ++i; 161 } else { 162 i = operations_.erase(i); 163 } 164 } 165 166 // Convert the list of recorded operations to a minidump stream. 167 stream_.resize(sizeof(MINIDUMP_HANDLE_OPERATION_LIST) + 168 sizeof(AVRF_HANDLE_OPERATION) * operations_.size()); 169 170 MINIDUMP_HANDLE_OPERATION_LIST* stream_data = 171 reinterpret_cast<MINIDUMP_HANDLE_OPERATION_LIST*>( 172 &stream_.front()); 173 stream_data->SizeOfHeader = sizeof(MINIDUMP_HANDLE_OPERATION_LIST); 174 stream_data->SizeOfEntry = sizeof(AVRF_HANDLE_OPERATION); 175 stream_data->NumberOfEntries = static_cast<ULONG32>(operations_.size()); 176 stream_data->Reserved = 0; 177 std::copy(operations_.begin(), 178 operations_.end(), 179 #ifdef _MSC_VER 180 stdext::checked_array_iterator<AVRF_HANDLE_OPERATION*>( 181 reinterpret_cast<AVRF_HANDLE_OPERATION*>(stream_data + 1), 182 operations_.size()) 183 #else 184 reinterpret_cast<AVRF_HANDLE_OPERATION*>(stream_data + 1) 185 #endif 186 ); 187 188 return true; 189 } 190 191 bool HandleTraceData::GetUserStream(MINIDUMP_USER_STREAM* user_stream) { 192 if (stream_.empty()) { 193 return false; 194 } else { 195 user_stream->Type = HandleOperationListStream; 196 user_stream->BufferSize = static_cast<ULONG>(stream_.size()); 197 user_stream->Buffer = &stream_.front(); 198 return true; 199 } 200 } 201 202 bool HandleTraceData::ReadExceptionCode( 203 HANDLE process_handle, 204 EXCEPTION_POINTERS* exception_pointers, 205 DWORD* exception_code) { 206 EXCEPTION_POINTERS pointers; 207 if (!ReadProcessMemory(process_handle, 208 exception_pointers, 209 &pointers, 210 sizeof(pointers), 211 NULL)) { 212 return false; 213 } 214 215 if (!ReadProcessMemory(process_handle, 216 pointers.ExceptionRecord, 217 exception_code, 218 sizeof(*exception_code), 219 NULL)) { 220 return false; 221 } 222 223 return true; 224 } 225 226 ULONG CALLBACK HandleTraceData::RecordHandleOperations( 227 void* resource_description, 228 void* enumeration_context, 229 ULONG* enumeration_level) { 230 AVRF_HANDLE_OPERATION* description = 231 reinterpret_cast<AVRF_HANDLE_OPERATION*>(resource_description); 232 HandleTraceData* self = 233 reinterpret_cast<HandleTraceData*>(enumeration_context); 234 235 // Remember the last invalid handle operation. 236 if (description->OperationType == OperationDbBADREF) { 237 self->handle_ = description->Handle; 238 } 239 240 // Record all handle operations. 241 self->operations_.push_back(*description); 242 243 *enumeration_level = HeapEnumerationEverything; 244 return ERROR_SUCCESS; 245 } 246 247 } // namespace 248 249 namespace google_breakpad { 250 251 MinidumpGenerator::MinidumpGenerator( 252 const std::wstring& dump_path, 253 const HANDLE process_handle, 254 const DWORD process_id, 255 const DWORD thread_id, 256 const DWORD requesting_thread_id, 257 EXCEPTION_POINTERS* exception_pointers, 258 MDRawAssertionInfo* assert_info, 259 const MINIDUMP_TYPE dump_type, 260 const bool is_client_pointers) 261 : dbghelp_module_(NULL), 262 rpcrt4_module_(NULL), 263 dump_path_(dump_path), 264 process_handle_(process_handle), 265 process_id_(process_id), 266 thread_id_(thread_id), 267 requesting_thread_id_(requesting_thread_id), 268 exception_pointers_(exception_pointers), 269 assert_info_(assert_info), 270 dump_type_(dump_type), 271 is_client_pointers_(is_client_pointers), 272 dump_file_(INVALID_HANDLE_VALUE), 273 full_dump_file_(INVALID_HANDLE_VALUE), 274 dump_file_is_internal_(false), 275 full_dump_file_is_internal_(false), 276 additional_streams_(NULL), 277 callback_info_(NULL), 278 write_dump_(NULL), 279 create_uuid_(NULL) { 280 InitializeCriticalSection(&module_load_sync_); 281 InitializeCriticalSection(&get_proc_address_sync_); 282 } 283 284 MinidumpGenerator::~MinidumpGenerator() { 285 if (dump_file_is_internal_ && dump_file_ != INVALID_HANDLE_VALUE) { 286 CloseHandle(dump_file_); 287 } 288 289 if (full_dump_file_is_internal_ && full_dump_file_ != INVALID_HANDLE_VALUE) { 290 CloseHandle(full_dump_file_); 291 } 292 293 if (dbghelp_module_) { 294 FreeLibrary(dbghelp_module_); 295 } 296 297 if (rpcrt4_module_) { 298 FreeLibrary(rpcrt4_module_); 299 } 300 301 DeleteCriticalSection(&get_proc_address_sync_); 302 DeleteCriticalSection(&module_load_sync_); 303 } 304 305 bool MinidumpGenerator::WriteMinidump() { 306 bool full_memory_dump = (dump_type_ & MiniDumpWithFullMemory) != 0; 307 if (dump_file_ == INVALID_HANDLE_VALUE || 308 (full_memory_dump && full_dump_file_ == INVALID_HANDLE_VALUE)) { 309 return false; 310 } 311 312 MiniDumpWriteDumpType write_dump = GetWriteDump(); 313 if (!write_dump) { 314 return false; 315 } 316 317 MINIDUMP_EXCEPTION_INFORMATION* dump_exception_pointers = NULL; 318 MINIDUMP_EXCEPTION_INFORMATION dump_exception_info; 319 320 // Setup the exception information object only if it's a dump 321 // due to an exception. 322 if (exception_pointers_) { 323 dump_exception_pointers = &dump_exception_info; 324 dump_exception_info.ThreadId = thread_id_; 325 dump_exception_info.ExceptionPointers = exception_pointers_; 326 dump_exception_info.ClientPointers = is_client_pointers_; 327 } 328 329 // Add an MDRawBreakpadInfo stream to the minidump, to provide additional 330 // information about the exception handler to the Breakpad processor. 331 // The information will help the processor determine which threads are 332 // relevant. The Breakpad processor does not require this information but 333 // can function better with Breakpad-generated dumps when it is present. 334 // The native debugger is not harmed by the presence of this information. 335 MDRawBreakpadInfo breakpad_info = {0}; 336 if (!is_client_pointers_) { 337 // Set the dump thread id and requesting thread id only in case of 338 // in-process dump generation. 339 breakpad_info.validity = MD_BREAKPAD_INFO_VALID_DUMP_THREAD_ID | 340 MD_BREAKPAD_INFO_VALID_REQUESTING_THREAD_ID; 341 breakpad_info.dump_thread_id = thread_id_; 342 breakpad_info.requesting_thread_id = requesting_thread_id_; 343 } 344 345 int additional_streams_count = additional_streams_ ? 346 additional_streams_->UserStreamCount : 0; 347 scoped_array<MINIDUMP_USER_STREAM> user_stream_array( 348 new MINIDUMP_USER_STREAM[3 + additional_streams_count]); 349 user_stream_array[0].Type = MD_BREAKPAD_INFO_STREAM; 350 user_stream_array[0].BufferSize = sizeof(breakpad_info); 351 user_stream_array[0].Buffer = &breakpad_info; 352 353 MINIDUMP_USER_STREAM_INFORMATION user_streams; 354 user_streams.UserStreamCount = 1; 355 user_streams.UserStreamArray = user_stream_array.get(); 356 357 MDRawAssertionInfo* actual_assert_info = assert_info_; 358 MDRawAssertionInfo client_assert_info = {{0}}; 359 360 if (assert_info_) { 361 // If the assertion info object lives in the client process, 362 // read the memory of the client process. 363 if (is_client_pointers_) { 364 SIZE_T bytes_read = 0; 365 if (!ReadProcessMemory(process_handle_, 366 assert_info_, 367 &client_assert_info, 368 sizeof(client_assert_info), 369 &bytes_read)) { 370 if (dump_file_is_internal_) 371 CloseHandle(dump_file_); 372 if (full_dump_file_is_internal_ && 373 full_dump_file_ != INVALID_HANDLE_VALUE) 374 CloseHandle(full_dump_file_); 375 return false; 376 } 377 378 if (bytes_read != sizeof(client_assert_info)) { 379 if (dump_file_is_internal_) 380 CloseHandle(dump_file_); 381 if (full_dump_file_is_internal_ && 382 full_dump_file_ != INVALID_HANDLE_VALUE) 383 CloseHandle(full_dump_file_); 384 return false; 385 } 386 387 actual_assert_info = &client_assert_info; 388 } 389 390 user_stream_array[1].Type = MD_ASSERTION_INFO_STREAM; 391 user_stream_array[1].BufferSize = sizeof(MDRawAssertionInfo); 392 user_stream_array[1].Buffer = actual_assert_info; 393 ++user_streams.UserStreamCount; 394 } 395 396 if (additional_streams_) { 397 for (size_t i = 0; 398 i < additional_streams_->UserStreamCount; 399 i++, user_streams.UserStreamCount++) { 400 user_stream_array[user_streams.UserStreamCount].Type = 401 additional_streams_->UserStreamArray[i].Type; 402 user_stream_array[user_streams.UserStreamCount].BufferSize = 403 additional_streams_->UserStreamArray[i].BufferSize; 404 user_stream_array[user_streams.UserStreamCount].Buffer = 405 additional_streams_->UserStreamArray[i].Buffer; 406 } 407 } 408 409 // If the process is terminated by STATUS_INVALID_HANDLE exception store 410 // the trace of operations for the offending handle value. Do nothing special 411 // if the client already requested the handle trace to be stored in the dump. 412 HandleTraceData handle_trace_data; 413 if (exception_pointers_ && (dump_type_ & MiniDumpWithHandleData) == 0) { 414 if (!handle_trace_data.CollectHandleData(process_handle_, 415 exception_pointers_)) { 416 if (dump_file_is_internal_) 417 CloseHandle(dump_file_); 418 if (full_dump_file_is_internal_ && 419 full_dump_file_ != INVALID_HANDLE_VALUE) 420 CloseHandle(full_dump_file_); 421 return false; 422 } 423 } 424 425 bool result_full_memory = true; 426 if (full_memory_dump) { 427 result_full_memory = write_dump( 428 process_handle_, 429 process_id_, 430 full_dump_file_, 431 static_cast<MINIDUMP_TYPE>((dump_type_ & (~MiniDumpNormal)) 432 | MiniDumpWithHandleData), 433 exception_pointers_ ? &dump_exception_info : NULL, 434 &user_streams, 435 NULL) != FALSE; 436 } 437 438 // Add handle operations trace stream to the minidump if it was collected. 439 if (handle_trace_data.GetUserStream( 440 &user_stream_array[user_streams.UserStreamCount])) { 441 ++user_streams.UserStreamCount; 442 } 443 444 bool result_minidump = write_dump( 445 process_handle_, 446 process_id_, 447 dump_file_, 448 static_cast<MINIDUMP_TYPE>((dump_type_ & (~MiniDumpWithFullMemory)) 449 | MiniDumpNormal), 450 exception_pointers_ ? &dump_exception_info : NULL, 451 &user_streams, 452 callback_info_) != FALSE; 453 454 return result_minidump && result_full_memory; 455 } 456 457 bool MinidumpGenerator::GenerateDumpFile(wstring* dump_path) { 458 // The dump file was already set by handle or this function was previously 459 // called. 460 if (dump_file_ != INVALID_HANDLE_VALUE) { 461 return false; 462 } 463 464 wstring dump_file_path; 465 if (!GenerateDumpFilePath(&dump_file_path)) { 466 return false; 467 } 468 469 dump_file_ = CreateFile(dump_file_path.c_str(), 470 GENERIC_WRITE, 471 0, 472 NULL, 473 CREATE_NEW, 474 FILE_ATTRIBUTE_NORMAL, 475 NULL); 476 if (dump_file_ == INVALID_HANDLE_VALUE) { 477 return false; 478 } 479 480 dump_file_is_internal_ = true; 481 *dump_path = dump_file_path; 482 return true; 483 } 484 485 bool MinidumpGenerator::GenerateFullDumpFile(wstring* full_dump_path) { 486 // A full minidump was not requested. 487 if ((dump_type_ & MiniDumpWithFullMemory) == 0) { 488 return false; 489 } 490 491 // The dump file was already set by handle or this function was previously 492 // called. 493 if (full_dump_file_ != INVALID_HANDLE_VALUE) { 494 return false; 495 } 496 497 wstring full_dump_file_path; 498 if (!GenerateDumpFilePath(&full_dump_file_path)) { 499 return false; 500 } 501 full_dump_file_path.resize(full_dump_file_path.size() - 4); // strip .dmp 502 full_dump_file_path.append(TEXT("-full.dmp")); 503 504 full_dump_file_ = CreateFile(full_dump_file_path.c_str(), 505 GENERIC_WRITE, 506 0, 507 NULL, 508 CREATE_NEW, 509 FILE_ATTRIBUTE_NORMAL, 510 NULL); 511 if (full_dump_file_ == INVALID_HANDLE_VALUE) { 512 return false; 513 } 514 515 full_dump_file_is_internal_ = true; 516 *full_dump_path = full_dump_file_path; 517 return true; 518 } 519 520 HMODULE MinidumpGenerator::GetDbghelpModule() { 521 AutoCriticalSection lock(&module_load_sync_); 522 if (!dbghelp_module_) { 523 dbghelp_module_ = LoadLibrary(TEXT("dbghelp.dll")); 524 } 525 526 return dbghelp_module_; 527 } 528 529 MinidumpGenerator::MiniDumpWriteDumpType MinidumpGenerator::GetWriteDump() { 530 AutoCriticalSection lock(&get_proc_address_sync_); 531 if (!write_dump_) { 532 HMODULE module = GetDbghelpModule(); 533 if (module) { 534 FARPROC proc = GetProcAddress(module, "MiniDumpWriteDump"); 535 write_dump_ = reinterpret_cast<MiniDumpWriteDumpType>(proc); 536 } 537 } 538 539 return write_dump_; 540 } 541 542 HMODULE MinidumpGenerator::GetRpcrt4Module() { 543 AutoCriticalSection lock(&module_load_sync_); 544 if (!rpcrt4_module_) { 545 rpcrt4_module_ = LoadLibrary(TEXT("rpcrt4.dll")); 546 } 547 548 return rpcrt4_module_; 549 } 550 551 MinidumpGenerator::UuidCreateType MinidumpGenerator::GetCreateUuid() { 552 AutoCriticalSection lock(&module_load_sync_); 553 if (!create_uuid_) { 554 HMODULE module = GetRpcrt4Module(); 555 if (module) { 556 FARPROC proc = GetProcAddress(module, "UuidCreate"); 557 create_uuid_ = reinterpret_cast<UuidCreateType>(proc); 558 } 559 } 560 561 return create_uuid_; 562 } 563 564 bool MinidumpGenerator::GenerateDumpFilePath(wstring* file_path) { 565 UUID id = {0}; 566 567 UuidCreateType create_uuid = GetCreateUuid(); 568 if (!create_uuid) { 569 return false; 570 } 571 572 create_uuid(&id); 573 wstring id_str = GUIDString::GUIDToWString(&id); 574 575 *file_path = dump_path_ + TEXT("\\") + id_str + TEXT(".dmp"); 576 return true; 577 } 578 579 } // namespace google_breakpad 580