Home | History | Annotate | Download | only in crash_generation
      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