Home | History | Annotate | Download | only in unittests
      1 // Copyright (c) 2010, 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 <windows.h>
     31 #include <objbase.h>
     32 #include <dbghelp.h>
     33 
     34 #include "client/windows/unittests/dump_analysis.h"  // NOLINT
     35 #include "testing/gtest/include/gtest/gtest.h"
     36 
     37 DumpAnalysis::~DumpAnalysis() {
     38   if (dump_file_view_ != NULL) {
     39     EXPECT_TRUE(::UnmapViewOfFile(dump_file_view_));
     40     ::CloseHandle(dump_file_mapping_);
     41     dump_file_mapping_ = NULL;
     42   }
     43 
     44   if (dump_file_handle_ != NULL) {
     45     ::CloseHandle(dump_file_handle_);
     46     dump_file_handle_ = NULL;
     47   }
     48 }
     49 
     50 void DumpAnalysis::EnsureDumpMapped() {
     51   if (dump_file_view_ == NULL) {
     52     dump_file_handle_ = ::CreateFile(dump_file_.c_str(),
     53       GENERIC_READ,
     54       0,
     55       NULL,
     56       OPEN_EXISTING,
     57       0,
     58       NULL);
     59     ASSERT_TRUE(dump_file_handle_ != NULL);
     60     ASSERT_TRUE(dump_file_mapping_ == NULL);
     61 
     62     dump_file_mapping_ = ::CreateFileMapping(dump_file_handle_,
     63       NULL,
     64       PAGE_READONLY,
     65       0,
     66       0,
     67       NULL);
     68     ASSERT_TRUE(dump_file_mapping_ != NULL);
     69 
     70     dump_file_view_ = ::MapViewOfFile(dump_file_mapping_,
     71       FILE_MAP_READ,
     72       0,
     73       0,
     74       0);
     75     ASSERT_TRUE(dump_file_view_ != NULL);
     76   }
     77 }
     78 
     79 bool DumpAnalysis::HasTebs() const {
     80   MINIDUMP_THREAD_LIST* thread_list = NULL;
     81   size_t thread_list_size = GetStream(ThreadListStream, &thread_list);
     82 
     83   if (thread_list_size > 0 && thread_list != NULL) {
     84     for (ULONG i = 0; i < thread_list->NumberOfThreads; ++i) {
     85       if (!HasMemory(thread_list->Threads[i].Teb))
     86         return false;
     87     }
     88 
     89     return true;
     90   }
     91 
     92   // No thread list, no TEB info.
     93   return false;
     94 }
     95 
     96 bool DumpAnalysis::HasPeb() const {
     97   MINIDUMP_THREAD_LIST* thread_list = NULL;
     98   size_t thread_list_size = GetStream(ThreadListStream, &thread_list);
     99 
    100   if (thread_list_size > 0 && thread_list != NULL &&
    101       thread_list->NumberOfThreads > 0) {
    102     FakeTEB* teb = NULL;
    103     if (!HasMemory(thread_list->Threads[0].Teb, &teb))
    104       return false;
    105 
    106     return HasMemory(teb->peb);
    107   }
    108 
    109   return false;
    110 }
    111 
    112 bool DumpAnalysis::HasStream(ULONG stream_number) const {
    113   void* stream = NULL;
    114   size_t stream_size = GetStreamImpl(stream_number, &stream);
    115   return stream_size > 0 && stream != NULL;
    116 }
    117 
    118 size_t DumpAnalysis::GetStreamImpl(ULONG stream_number, void** stream) const {
    119   MINIDUMP_DIRECTORY* directory = NULL;
    120   ULONG memory_list_size = 0;
    121   BOOL ret = ::MiniDumpReadDumpStream(dump_file_view_,
    122                                       stream_number,
    123                                       &directory,
    124                                       stream,
    125                                       &memory_list_size);
    126 
    127   return ret ? memory_list_size : 0;
    128 }
    129 
    130 bool DumpAnalysis::HasMemoryImpl(const void *addr_in, size_t structuresize,
    131                                  void **structure) const {
    132   uintptr_t address = reinterpret_cast<uintptr_t>(addr_in);
    133   MINIDUMP_MEMORY_LIST* memory_list = NULL;
    134   size_t memory_list_size = GetStream(MemoryListStream, &memory_list);
    135   if (memory_list_size > 0 && memory_list != NULL) {
    136     for (ULONG i = 0; i < memory_list->NumberOfMemoryRanges; ++i) {
    137       MINIDUMP_MEMORY_DESCRIPTOR& descr = memory_list->MemoryRanges[i];
    138       const uintptr_t range_start =
    139           static_cast<uintptr_t>(descr.StartOfMemoryRange);
    140       uintptr_t range_end = range_start + descr.Memory.DataSize;
    141 
    142       if (address >= range_start &&
    143           address + structuresize < range_end) {
    144         // The start address falls in the range, and the end address is
    145         // in bounds, return a pointer to the structure if requested.
    146         if (structure != NULL)
    147           *structure = RVA_TO_ADDR(dump_file_view_, descr.Memory.Rva);
    148 
    149         return true;
    150       }
    151     }
    152   }
    153 
    154   // We didn't find the range in a MINIDUMP_MEMORY_LIST, so maybe this
    155   // is a full dump using MINIDUMP_MEMORY64_LIST with all the memory at the
    156   // end of the dump file.
    157   MINIDUMP_MEMORY64_LIST* memory64_list = NULL;
    158   memory_list_size = GetStream(Memory64ListStream, &memory64_list);
    159   if (memory_list_size > 0 && memory64_list != NULL) {
    160     // Keep track of where the current descriptor maps to.
    161     RVA64 curr_rva = memory64_list->BaseRva;
    162     for (ULONG i = 0; i < memory64_list->NumberOfMemoryRanges; ++i) {
    163       MINIDUMP_MEMORY_DESCRIPTOR64& descr = memory64_list->MemoryRanges[i];
    164       uintptr_t range_start =
    165           static_cast<uintptr_t>(descr.StartOfMemoryRange);
    166       uintptr_t range_end = range_start + static_cast<size_t>(descr.DataSize);
    167 
    168       if (address >= range_start &&
    169           address + structuresize < range_end) {
    170         // The start address falls in the range, and the end address is
    171         // in bounds, return a pointer to the structure if requested.
    172         if (structure != NULL)
    173           *structure = RVA_TO_ADDR(dump_file_view_, curr_rva);
    174 
    175         return true;
    176       }
    177 
    178       // Advance the current RVA.
    179       curr_rva += descr.DataSize;
    180     }
    181   }
    182 
    183   return false;
    184 }
    185