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
~DumpAnalysis()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
EnsureDumpMapped()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
HasTebs() const79 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
HasPeb() const96 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
HasStream(ULONG stream_number) const112 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
GetStreamImpl(ULONG stream_number,void ** stream) const118 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
HasMemoryImpl(const void * addr_in,size_t structuresize,void ** structure) const130 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