|
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/crash_generation/minidump_generator.h" |
|
35 #include "client/windows/unittests/dump_analysis.h" // NOLINT |
|
36 |
|
37 #include "gtest/gtest.h" |
|
38 |
|
39 namespace { |
|
40 |
|
41 // Minidump with stacks, PEB, TEB, and unloaded module list. |
|
42 const MINIDUMP_TYPE kSmallDumpType = static_cast<MINIDUMP_TYPE>( |
|
43 MiniDumpWithProcessThreadData | // Get PEB and TEB. |
|
44 MiniDumpWithUnloadedModules); // Get unloaded modules when available. |
|
45 |
|
46 // Minidump with all of the above, plus memory referenced from stack. |
|
47 const MINIDUMP_TYPE kLargerDumpType = static_cast<MINIDUMP_TYPE>( |
|
48 MiniDumpWithProcessThreadData | // Get PEB and TEB. |
|
49 MiniDumpWithUnloadedModules | // Get unloaded modules when available. |
|
50 MiniDumpWithIndirectlyReferencedMemory); // Get memory referenced by stack. |
|
51 |
|
52 // Large dump with all process memory. |
|
53 const MINIDUMP_TYPE kFullDumpType = static_cast<MINIDUMP_TYPE>( |
|
54 MiniDumpWithFullMemory | // Full memory from process. |
|
55 MiniDumpWithProcessThreadData | // Get PEB and TEB. |
|
56 MiniDumpWithHandleData | // Get all handle information. |
|
57 MiniDumpWithUnloadedModules); // Get unloaded modules when available. |
|
58 |
|
59 class MinidumpTest: public testing::Test { |
|
60 public: |
|
61 MinidumpTest() { |
|
62 wchar_t temp_dir_path[ MAX_PATH ] = {0}; |
|
63 ::GetTempPath(MAX_PATH, temp_dir_path); |
|
64 dump_path_ = temp_dir_path; |
|
65 } |
|
66 |
|
67 virtual void SetUp() { |
|
68 // Make sure URLMon isn't loaded into our process. |
|
69 ASSERT_EQ(NULL, ::GetModuleHandle(L"urlmon.dll")); |
|
70 |
|
71 // Then load and unload it to ensure we have something to |
|
72 // stock the unloaded module list with. |
|
73 HMODULE urlmon = ::LoadLibrary(L"urlmon.dll"); |
|
74 ASSERT_TRUE(urlmon != NULL); |
|
75 ASSERT_TRUE(::FreeLibrary(urlmon)); |
|
76 } |
|
77 |
|
78 virtual void TearDown() { |
|
79 if (!dump_file_.empty()) { |
|
80 ::DeleteFile(dump_file_.c_str()); |
|
81 dump_file_ = L""; |
|
82 } |
|
83 if (!full_dump_file_.empty()) { |
|
84 ::DeleteFile(full_dump_file_.c_str()); |
|
85 full_dump_file_ = L""; |
|
86 } |
|
87 } |
|
88 |
|
89 bool WriteDump(ULONG flags) { |
|
90 using google_breakpad::MinidumpGenerator; |
|
91 |
|
92 // Fake exception is access violation on write to this. |
|
93 EXCEPTION_RECORD ex_record = { |
|
94 STATUS_ACCESS_VIOLATION, // ExceptionCode |
|
95 0, // ExceptionFlags |
|
96 NULL, // ExceptionRecord; |
|
97 reinterpret_cast<void*>(0xCAFEBABE), // ExceptionAddress; |
|
98 2, // NumberParameters; |
|
99 { EXCEPTION_WRITE_FAULT, reinterpret_cast<ULONG_PTR>(this) } |
|
100 }; |
|
101 CONTEXT ctx_record = {}; |
|
102 EXCEPTION_POINTERS ex_ptrs = { |
|
103 &ex_record, |
|
104 &ctx_record, |
|
105 }; |
|
106 |
|
107 MinidumpGenerator generator(dump_path_); |
|
108 |
|
109 // And write a dump |
|
110 bool result = generator.WriteMinidump(::GetCurrentProcess(), |
|
111 ::GetCurrentProcessId(), |
|
112 ::GetCurrentThreadId(), |
|
113 ::GetCurrentThreadId(), |
|
114 &ex_ptrs, |
|
115 NULL, |
|
116 static_cast<MINIDUMP_TYPE>(flags), |
|
117 TRUE, |
|
118 &dump_file_, |
|
119 &full_dump_file_); |
|
120 return result == TRUE; |
|
121 } |
|
122 |
|
123 protected: |
|
124 std::wstring dump_file_; |
|
125 std::wstring full_dump_file_; |
|
126 |
|
127 std::wstring dump_path_; |
|
128 }; |
|
129 |
|
130 // We need to be able to get file information from Windows |
|
131 bool HasFileInfo(const std::wstring& file_path) { |
|
132 DWORD dummy; |
|
133 const wchar_t* path = file_path.c_str(); |
|
134 DWORD length = ::GetFileVersionInfoSize(path, &dummy); |
|
135 if (length == 0) |
|
136 return NULL; |
|
137 |
|
138 void* data = calloc(length, 1); |
|
139 if (!data) |
|
140 return false; |
|
141 |
|
142 if (!::GetFileVersionInfo(path, dummy, length, data)) { |
|
143 free(data); |
|
144 return false; |
|
145 } |
|
146 |
|
147 void* translate = NULL; |
|
148 UINT page_count; |
|
149 BOOL query_result = VerQueryValue( |
|
150 data, |
|
151 L"\\VarFileInfo\\Translation", |
|
152 static_cast<void**>(&translate), |
|
153 &page_count); |
|
154 |
|
155 free(data); |
|
156 if (query_result && translate) { |
|
157 return true; |
|
158 } else { |
|
159 return false; |
|
160 } |
|
161 } |
|
162 |
|
163 TEST_F(MinidumpTest, Version) { |
|
164 API_VERSION* version = ::ImagehlpApiVersion(); |
|
165 |
|
166 HMODULE dbg_help = ::GetModuleHandle(L"dbghelp.dll"); |
|
167 ASSERT_TRUE(dbg_help != NULL); |
|
168 |
|
169 wchar_t dbg_help_file[1024] = {}; |
|
170 ASSERT_TRUE(::GetModuleFileName(dbg_help, |
|
171 dbg_help_file, |
|
172 sizeof(dbg_help_file) / |
|
173 sizeof(*dbg_help_file))); |
|
174 ASSERT_TRUE(HasFileInfo(std::wstring(dbg_help_file)) != NULL); |
|
175 |
|
176 // LOG(INFO) << "DbgHelp.dll version: " << file_info->file_version(); |
|
177 } |
|
178 |
|
179 TEST_F(MinidumpTest, Normal) { |
|
180 EXPECT_TRUE(WriteDump(MiniDumpNormal)); |
|
181 DumpAnalysis mini(dump_file_); |
|
182 |
|
183 // We expect threads, modules and some memory. |
|
184 EXPECT_TRUE(mini.HasStream(ThreadListStream)); |
|
185 EXPECT_TRUE(mini.HasStream(ModuleListStream)); |
|
186 EXPECT_TRUE(mini.HasStream(MemoryListStream)); |
|
187 EXPECT_TRUE(mini.HasStream(ExceptionStream)); |
|
188 EXPECT_TRUE(mini.HasStream(SystemInfoStream)); |
|
189 EXPECT_TRUE(mini.HasStream(MiscInfoStream)); |
|
190 |
|
191 EXPECT_FALSE(mini.HasStream(ThreadExListStream)); |
|
192 EXPECT_FALSE(mini.HasStream(Memory64ListStream)); |
|
193 EXPECT_FALSE(mini.HasStream(CommentStreamA)); |
|
194 EXPECT_FALSE(mini.HasStream(CommentStreamW)); |
|
195 EXPECT_FALSE(mini.HasStream(HandleDataStream)); |
|
196 EXPECT_FALSE(mini.HasStream(FunctionTableStream)); |
|
197 EXPECT_FALSE(mini.HasStream(UnloadedModuleListStream)); |
|
198 EXPECT_FALSE(mini.HasStream(MemoryInfoListStream)); |
|
199 EXPECT_FALSE(mini.HasStream(ThreadInfoListStream)); |
|
200 EXPECT_FALSE(mini.HasStream(HandleOperationListStream)); |
|
201 EXPECT_FALSE(mini.HasStream(TokenStream)); |
|
202 |
|
203 // We expect no PEB nor TEBs in this dump. |
|
204 EXPECT_FALSE(mini.HasTebs()); |
|
205 EXPECT_FALSE(mini.HasPeb()); |
|
206 |
|
207 // We expect no off-stack memory in this dump. |
|
208 EXPECT_FALSE(mini.HasMemory(this)); |
|
209 } |
|
210 |
|
211 TEST_F(MinidumpTest, SmallDump) { |
|
212 ASSERT_TRUE(WriteDump(kSmallDumpType)); |
|
213 DumpAnalysis mini(dump_file_); |
|
214 |
|
215 EXPECT_TRUE(mini.HasStream(ThreadListStream)); |
|
216 EXPECT_TRUE(mini.HasStream(ModuleListStream)); |
|
217 EXPECT_TRUE(mini.HasStream(MemoryListStream)); |
|
218 EXPECT_TRUE(mini.HasStream(ExceptionStream)); |
|
219 EXPECT_TRUE(mini.HasStream(SystemInfoStream)); |
|
220 EXPECT_TRUE(mini.HasStream(UnloadedModuleListStream)); |
|
221 EXPECT_TRUE(mini.HasStream(MiscInfoStream)); |
|
222 |
|
223 // We expect PEB and TEBs in this dump. |
|
224 EXPECT_TRUE(mini.HasTebs()); |
|
225 EXPECT_TRUE(mini.HasPeb()); |
|
226 |
|
227 EXPECT_FALSE(mini.HasStream(ThreadExListStream)); |
|
228 EXPECT_FALSE(mini.HasStream(Memory64ListStream)); |
|
229 EXPECT_FALSE(mini.HasStream(CommentStreamA)); |
|
230 EXPECT_FALSE(mini.HasStream(CommentStreamW)); |
|
231 EXPECT_FALSE(mini.HasStream(HandleDataStream)); |
|
232 EXPECT_FALSE(mini.HasStream(FunctionTableStream)); |
|
233 EXPECT_FALSE(mini.HasStream(MemoryInfoListStream)); |
|
234 EXPECT_FALSE(mini.HasStream(ThreadInfoListStream)); |
|
235 EXPECT_FALSE(mini.HasStream(HandleOperationListStream)); |
|
236 EXPECT_FALSE(mini.HasStream(TokenStream)); |
|
237 |
|
238 // We expect no off-stack memory in this dump. |
|
239 EXPECT_FALSE(mini.HasMemory(this)); |
|
240 } |
|
241 |
|
242 TEST_F(MinidumpTest, LargerDump) { |
|
243 ASSERT_TRUE(WriteDump(kLargerDumpType)); |
|
244 DumpAnalysis mini(dump_file_); |
|
245 |
|
246 // The dump should have all of these streams. |
|
247 EXPECT_TRUE(mini.HasStream(ThreadListStream)); |
|
248 EXPECT_TRUE(mini.HasStream(ModuleListStream)); |
|
249 EXPECT_TRUE(mini.HasStream(MemoryListStream)); |
|
250 EXPECT_TRUE(mini.HasStream(ExceptionStream)); |
|
251 EXPECT_TRUE(mini.HasStream(SystemInfoStream)); |
|
252 EXPECT_TRUE(mini.HasStream(UnloadedModuleListStream)); |
|
253 EXPECT_TRUE(mini.HasStream(MiscInfoStream)); |
|
254 |
|
255 // We expect memory referenced by stack in this dump. |
|
256 EXPECT_TRUE(mini.HasMemory(this)); |
|
257 |
|
258 // We expect PEB and TEBs in this dump. |
|
259 EXPECT_TRUE(mini.HasTebs()); |
|
260 EXPECT_TRUE(mini.HasPeb()); |
|
261 |
|
262 EXPECT_FALSE(mini.HasStream(ThreadExListStream)); |
|
263 EXPECT_FALSE(mini.HasStream(Memory64ListStream)); |
|
264 EXPECT_FALSE(mini.HasStream(CommentStreamA)); |
|
265 EXPECT_FALSE(mini.HasStream(CommentStreamW)); |
|
266 EXPECT_FALSE(mini.HasStream(HandleDataStream)); |
|
267 EXPECT_FALSE(mini.HasStream(FunctionTableStream)); |
|
268 EXPECT_FALSE(mini.HasStream(MemoryInfoListStream)); |
|
269 EXPECT_FALSE(mini.HasStream(ThreadInfoListStream)); |
|
270 EXPECT_FALSE(mini.HasStream(HandleOperationListStream)); |
|
271 EXPECT_FALSE(mini.HasStream(TokenStream)); |
|
272 } |
|
273 |
|
274 TEST_F(MinidumpTest, FullDump) { |
|
275 ASSERT_TRUE(WriteDump(kFullDumpType)); |
|
276 ASSERT_TRUE(dump_file_ != L""); |
|
277 ASSERT_TRUE(full_dump_file_ != L""); |
|
278 DumpAnalysis mini(dump_file_); |
|
279 DumpAnalysis full(full_dump_file_); |
|
280 |
|
281 // Either dumps can contain part of the information. |
|
282 |
|
283 // The dump should have all of these streams. |
|
284 EXPECT_TRUE(mini.HasStream(ThreadListStream)); |
|
285 EXPECT_TRUE(full.HasStream(ThreadListStream)); |
|
286 EXPECT_TRUE(mini.HasStream(ModuleListStream)); |
|
287 EXPECT_TRUE(full.HasStream(ModuleListStream)); |
|
288 EXPECT_TRUE(mini.HasStream(ExceptionStream)); |
|
289 EXPECT_TRUE(full.HasStream(ExceptionStream)); |
|
290 EXPECT_TRUE(mini.HasStream(SystemInfoStream)); |
|
291 EXPECT_TRUE(full.HasStream(SystemInfoStream)); |
|
292 EXPECT_TRUE(mini.HasStream(UnloadedModuleListStream)); |
|
293 EXPECT_TRUE(full.HasStream(UnloadedModuleListStream)); |
|
294 EXPECT_TRUE(mini.HasStream(MiscInfoStream)); |
|
295 EXPECT_TRUE(full.HasStream(MiscInfoStream)); |
|
296 EXPECT_TRUE(mini.HasStream(HandleDataStream)); |
|
297 EXPECT_TRUE(full.HasStream(HandleDataStream)); |
|
298 |
|
299 // We expect memory referenced by stack in this dump. |
|
300 EXPECT_FALSE(mini.HasMemory(this)); |
|
301 EXPECT_TRUE(full.HasMemory(this)); |
|
302 |
|
303 // We expect PEB and TEBs in this dump. |
|
304 EXPECT_TRUE(mini.HasTebs() || full.HasTebs()); |
|
305 EXPECT_TRUE(mini.HasPeb() || full.HasPeb()); |
|
306 |
|
307 EXPECT_TRUE(mini.HasStream(MemoryListStream)); |
|
308 EXPECT_TRUE(full.HasStream(Memory64ListStream)); |
|
309 EXPECT_FALSE(mini.HasStream(Memory64ListStream)); |
|
310 EXPECT_FALSE(full.HasStream(MemoryListStream)); |
|
311 |
|
312 // This is the only place we don't use OR because we want both not |
|
313 // to have the streams. |
|
314 EXPECT_FALSE(mini.HasStream(ThreadExListStream)); |
|
315 EXPECT_FALSE(full.HasStream(ThreadExListStream)); |
|
316 EXPECT_FALSE(mini.HasStream(CommentStreamA)); |
|
317 EXPECT_FALSE(full.HasStream(CommentStreamA)); |
|
318 EXPECT_FALSE(mini.HasStream(CommentStreamW)); |
|
319 EXPECT_FALSE(full.HasStream(CommentStreamW)); |
|
320 EXPECT_FALSE(mini.HasStream(FunctionTableStream)); |
|
321 EXPECT_FALSE(full.HasStream(FunctionTableStream)); |
|
322 EXPECT_FALSE(mini.HasStream(MemoryInfoListStream)); |
|
323 EXPECT_FALSE(full.HasStream(MemoryInfoListStream)); |
|
324 EXPECT_FALSE(mini.HasStream(ThreadInfoListStream)); |
|
325 EXPECT_FALSE(full.HasStream(ThreadInfoListStream)); |
|
326 EXPECT_FALSE(mini.HasStream(HandleOperationListStream)); |
|
327 EXPECT_FALSE(full.HasStream(HandleOperationListStream)); |
|
328 EXPECT_FALSE(mini.HasStream(TokenStream)); |
|
329 EXPECT_FALSE(full.HasStream(TokenStream)); |
|
330 } |
|
331 |
|
332 } // namespace |