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
37DumpAnalysis::~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
50void 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
79bool 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
96bool 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
112bool 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
118size_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
130bool 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