1 //===-- MinidumpTypesTest.cpp -----------------------------------*- C++ -*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
10 // Project includes
11 #include "Plugins/Process/Utility/RegisterContextLinux_i386.h"
12 #include "Plugins/Process/Utility/RegisterContextLinux_x86_64.h"
13 #include "Plugins/Process/minidump/MinidumpParser.h"
14 #include "Plugins/Process/minidump/MinidumpTypes.h"
15 #include "Plugins/Process/minidump/RegisterContextMinidump_x86_32.h"
16 #include "Plugins/Process/minidump/RegisterContextMinidump_x86_64.h"
17 
18 #include "TestingSupport/TestUtilities.h"
19 #include "lldb/Target/MemoryRegionInfo.h"
20 #include "lldb/Utility/ArchSpec.h"
21 #include "lldb/Utility/DataBufferLLVM.h"
22 #include "lldb/Utility/DataExtractor.h"
23 #include "lldb/Utility/FileSpec.h"
24 #include "llvm/ADT/ArrayRef.h"
25 #include "llvm/ADT/Optional.h"
26 #include "llvm/Support/FileSystem.h"
27 #include "llvm/Support/MemoryBuffer.h"
28 #include "llvm/Support/Path.h"
29 #include "gtest/gtest.h"
30 
31 // C includes
32 
33 // C++ includes
34 #include <memory>
35 
36 using namespace lldb_private;
37 using namespace minidump;
38 
39 class MinidumpParserTest : public testing::Test {
40 public:
41   void SetUpData(const char *minidump_filename) {
42     std::string filename = GetInputFilePath(minidump_filename);
43     auto BufferPtr = DataBufferLLVM::CreateSliceFromPath(filename, -1, 0);
44     ASSERT_NE(BufferPtr, nullptr);
45     llvm::Optional<MinidumpParser> optional_parser =
46         MinidumpParser::Create(BufferPtr);
47     ASSERT_TRUE(optional_parser.hasValue());
48     parser.reset(new MinidumpParser(optional_parser.getValue()));
49     ASSERT_GT(parser->GetData().size(), 0UL);
50     auto result = parser->Initialize();
51     ASSERT_TRUE(result.Success()) << result.AsCString();
52   }
53 
54   void InvalidMinidump(const char *minidump_filename, uint64_t load_size) {
55     std::string filename = GetInputFilePath(minidump_filename);
56     auto BufferPtr =
57         DataBufferLLVM::CreateSliceFromPath(filename, load_size, 0);
58     ASSERT_NE(BufferPtr, nullptr);
59 
60     llvm::Optional<MinidumpParser> optional_parser =
61         MinidumpParser::Create(BufferPtr);
62     ASSERT_TRUE(optional_parser.hasValue());
63     parser.reset(new MinidumpParser(optional_parser.getValue()));
64     ASSERT_GT(parser->GetData().size(), 0UL);
65     auto result = parser->Initialize();
66     ASSERT_TRUE(result.Fail());
67   }
68 
69   std::unique_ptr<MinidumpParser> parser;
70 };
71 
72 TEST_F(MinidumpParserTest, GetThreadsAndGetThreadContext) {
73   SetUpData("linux-x86_64.dmp");
74   llvm::ArrayRef<MinidumpThread> thread_list;
75 
76   thread_list = parser->GetThreads();
77   ASSERT_EQ(1UL, thread_list.size());
78 
79   const MinidumpThread thread = thread_list[0];
80 
81   EXPECT_EQ(16001UL, thread.thread_id);
82 
83   llvm::ArrayRef<uint8_t> context = parser->GetThreadContext(thread);
84   EXPECT_EQ(1232UL, context.size());
85 }
86 
87 TEST_F(MinidumpParserTest, TruncatedMinidumps) {
88   InvalidMinidump("linux-x86_64.dmp", 32);
89   InvalidMinidump("linux-x86_64.dmp", 100);
90   InvalidMinidump("linux-x86_64.dmp", 20 * 1024);
91 }
92 
93 TEST_F(MinidumpParserTest, IllFormedMinidumps) {
94   InvalidMinidump("bad_duplicate_streams.dmp", -1);
95   InvalidMinidump("bad_overlapping_streams.dmp", -1);
96 }
97 
98 TEST_F(MinidumpParserTest, GetArchitecture) {
99   SetUpData("linux-x86_64.dmp");
100   ASSERT_EQ(llvm::Triple::ArchType::x86_64,
101             parser->GetArchitecture().GetMachine());
102   ASSERT_EQ(llvm::Triple::OSType::Linux,
103             parser->GetArchitecture().GetTriple().getOS());
104 }
105 
106 TEST_F(MinidumpParserTest, GetMiscInfo) {
107   SetUpData("linux-x86_64.dmp");
108   const MinidumpMiscInfo *misc_info = parser->GetMiscInfo();
109   ASSERT_EQ(nullptr, misc_info);
110 }
111 
112 TEST_F(MinidumpParserTest, GetLinuxProcStatus) {
113   SetUpData("linux-x86_64.dmp");
114   llvm::Optional<LinuxProcStatus> proc_status = parser->GetLinuxProcStatus();
115   ASSERT_TRUE(proc_status.hasValue());
116   lldb::pid_t pid = proc_status->GetPid();
117   ASSERT_EQ(16001UL, pid);
118 }
119 
120 TEST_F(MinidumpParserTest, GetPid) {
121   SetUpData("linux-x86_64.dmp");
122   llvm::Optional<lldb::pid_t> pid = parser->GetPid();
123   ASSERT_TRUE(pid.hasValue());
124   ASSERT_EQ(16001UL, pid.getValue());
125 }
126 
127 TEST_F(MinidumpParserTest, GetModuleList) {
128   SetUpData("linux-x86_64.dmp");
129   llvm::ArrayRef<MinidumpModule> modules = parser->GetModuleList();
130   ASSERT_EQ(8UL, modules.size());
131   std::string module_names[8] = {
132       "/usr/local/google/home/dvlahovski/projects/test_breakpad/a.out",
133       "/lib/x86_64-linux-gnu/libm-2.19.so",
134       "/lib/x86_64-linux-gnu/libc-2.19.so",
135       "/lib/x86_64-linux-gnu/libgcc_s.so.1",
136       "/usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.19",
137       "/lib/x86_64-linux-gnu/libpthread-2.19.so",
138       "/lib/x86_64-linux-gnu/ld-2.19.so",
139       "linux-gate.so",
140   };
141 
142   for (int i = 0; i < 8; ++i) {
143     llvm::Optional<std::string> name =
144         parser->GetMinidumpString(modules[i].module_name_rva);
145     ASSERT_TRUE(name.hasValue());
146     EXPECT_EQ(module_names[i], name.getValue());
147   }
148 }
149 
150 TEST_F(MinidumpParserTest, GetFilteredModuleList) {
151   SetUpData("linux-x86_64_not_crashed.dmp");
152   llvm::ArrayRef<MinidumpModule> modules = parser->GetModuleList();
153   std::vector<const MinidumpModule *> filtered_modules =
154       parser->GetFilteredModuleList();
155   EXPECT_EQ(10UL, modules.size());
156   EXPECT_EQ(9UL, filtered_modules.size());
157   // EXPECT_GT(modules.size(), filtered_modules.size());
158   bool found = false;
159   for (size_t i = 0; i < filtered_modules.size(); ++i) {
160     llvm::Optional<std::string> name =
161         parser->GetMinidumpString(filtered_modules[i]->module_name_rva);
162     ASSERT_TRUE(name.hasValue());
163     if (name.getValue() == "/tmp/test/linux-x86_64_not_crashed") {
164       ASSERT_FALSE(found) << "There should be only one module with this name "
165                              "in the filtered module list";
166       found = true;
167       ASSERT_EQ(0x400000UL, filtered_modules[i]->base_of_image);
168     }
169   }
170 }
171 
172 TEST_F(MinidumpParserTest, GetExceptionStream) {
173   SetUpData("linux-x86_64.dmp");
174   const MinidumpExceptionStream *exception_stream =
175       parser->GetExceptionStream();
176   ASSERT_NE(nullptr, exception_stream);
177   ASSERT_EQ(11UL, exception_stream->exception_record.exception_code);
178 }
179 
180 void check_mem_range_exists(std::unique_ptr<MinidumpParser> &parser,
181                             const uint64_t range_start,
182                             const uint64_t range_size) {
183   llvm::Optional<minidump::Range> range = parser->FindMemoryRange(range_start);
184   ASSERT_TRUE(range.hasValue()) << "There is no range containing this address";
185   EXPECT_EQ(range_start, range->start);
186   EXPECT_EQ(range_start + range_size, range->start + range->range_ref.size());
187 }
188 
189 TEST_F(MinidumpParserTest, FindMemoryRange) {
190   SetUpData("linux-x86_64.dmp");
191   // There are two memory ranges in the file (size is in bytes, decimal):
192   // 1) 0x401d46 256
193   // 2) 0x7ffceb34a000 12288
194   EXPECT_FALSE(parser->FindMemoryRange(0x00).hasValue());
195   EXPECT_FALSE(parser->FindMemoryRange(0x2a).hasValue());
196 
197   check_mem_range_exists(parser, 0x401d46, 256);
198   EXPECT_FALSE(parser->FindMemoryRange(0x401d46 + 256).hasValue());
199 
200   check_mem_range_exists(parser, 0x7ffceb34a000, 12288);
201   EXPECT_FALSE(parser->FindMemoryRange(0x7ffceb34a000 + 12288).hasValue());
202 }
203 
204 TEST_F(MinidumpParserTest, GetMemory) {
205   SetUpData("linux-x86_64.dmp");
206 
207   EXPECT_EQ(128UL, parser->GetMemory(0x401d46, 128).size());
208   EXPECT_EQ(256UL, parser->GetMemory(0x401d46, 512).size());
209 
210   EXPECT_EQ(12288UL, parser->GetMemory(0x7ffceb34a000, 12288).size());
211   EXPECT_EQ(1024UL, parser->GetMemory(0x7ffceb34a000, 1024).size());
212 
213   EXPECT_TRUE(parser->GetMemory(0x500000, 512).empty());
214 }
215 
216 TEST_F(MinidumpParserTest, FindMemoryRangeWithFullMemoryMinidump) {
217   SetUpData("fizzbuzz_wow64.dmp");
218 
219   // There are a lot of ranges in the file, just testing with some of them
220   EXPECT_FALSE(parser->FindMemoryRange(0x00).hasValue());
221   EXPECT_FALSE(parser->FindMemoryRange(0x2a).hasValue());
222   check_mem_range_exists(parser, 0x10000, 65536); // first range
223   check_mem_range_exists(parser, 0x40000, 4096);
224   EXPECT_FALSE(parser->FindMemoryRange(0x40000 + 4096).hasValue());
225   check_mem_range_exists(parser, 0x77c12000, 8192);
226   check_mem_range_exists(parser, 0x7ffe0000, 4096); // last range
227   EXPECT_FALSE(parser->FindMemoryRange(0x7ffe0000 + 4096).hasValue());
228 }
229 
230 void check_region_info(std::unique_ptr<MinidumpParser> &parser,
231                        const uint64_t addr, MemoryRegionInfo::OptionalBool read,
232                        MemoryRegionInfo::OptionalBool write,
233                        MemoryRegionInfo::OptionalBool exec) {
234   auto range_info = parser->GetMemoryRegionInfo(addr);
235   ASSERT_TRUE(range_info.hasValue());
236   EXPECT_EQ(read, range_info->GetReadable());
237   EXPECT_EQ(write, range_info->GetWritable());
238   EXPECT_EQ(exec, range_info->GetExecutable());
239 }
240 
241 TEST_F(MinidumpParserTest, GetMemoryRegionInfo) {
242   SetUpData("fizzbuzz_wow64.dmp");
243 
244   const auto yes = MemoryRegionInfo::eYes;
245   const auto no = MemoryRegionInfo::eNo;
246 
247   check_region_info(parser, 0x00000, no, no, no);
248   check_region_info(parser, 0x10000, yes, yes, no);
249   check_region_info(parser, 0x20000, yes, yes, no);
250   check_region_info(parser, 0x30000, yes, yes, no);
251   check_region_info(parser, 0x31000, no, no, no);
252   check_region_info(parser, 0x40000, yes, no, no);
253 }
254 
255 // Windows Minidump tests
256 // fizzbuzz_no_heap.dmp is copied from the WinMiniDump tests
257 TEST_F(MinidumpParserTest, GetArchitectureWindows) {
258   SetUpData("fizzbuzz_no_heap.dmp");
259   ASSERT_EQ(llvm::Triple::ArchType::x86,
260             parser->GetArchitecture().GetMachine());
261   ASSERT_EQ(llvm::Triple::OSType::Win32,
262             parser->GetArchitecture().GetTriple().getOS());
263 }
264 
265 TEST_F(MinidumpParserTest, GetLinuxProcStatusWindows) {
266   SetUpData("fizzbuzz_no_heap.dmp");
267   llvm::Optional<LinuxProcStatus> proc_status = parser->GetLinuxProcStatus();
268   ASSERT_FALSE(proc_status.hasValue());
269 }
270 
271 TEST_F(MinidumpParserTest, GetMiscInfoWindows) {
272   SetUpData("fizzbuzz_no_heap.dmp");
273   const MinidumpMiscInfo *misc_info = parser->GetMiscInfo();
274   ASSERT_NE(nullptr, misc_info);
275   llvm::Optional<lldb::pid_t> pid = misc_info->GetPid();
276   ASSERT_TRUE(pid.hasValue());
277   ASSERT_EQ(4440UL, pid.getValue());
278 }
279 
280 TEST_F(MinidumpParserTest, GetPidWindows) {
281   SetUpData("fizzbuzz_no_heap.dmp");
282   llvm::Optional<lldb::pid_t> pid = parser->GetPid();
283   ASSERT_TRUE(pid.hasValue());
284   ASSERT_EQ(4440UL, pid.getValue());
285 }
286 
287 // wow64
288 TEST_F(MinidumpParserTest, GetPidWow64) {
289   SetUpData("fizzbuzz_wow64.dmp");
290   llvm::Optional<lldb::pid_t> pid = parser->GetPid();
291   ASSERT_TRUE(pid.hasValue());
292   ASSERT_EQ(7836UL, pid.getValue());
293 }
294 
295 TEST_F(MinidumpParserTest, GetModuleListWow64) {
296   SetUpData("fizzbuzz_wow64.dmp");
297   llvm::ArrayRef<MinidumpModule> modules = parser->GetModuleList();
298   ASSERT_EQ(16UL, modules.size());
299   std::string module_names[16] = {
300       R"(D:\src\llvm\llvm\tools\lldb\packages\Python\lldbsuite\test\functionalities\postmortem\wow64_minidump\fizzbuzz.exe)",
301       R"(C:\Windows\System32\ntdll.dll)",
302       R"(C:\Windows\System32\wow64.dll)",
303       R"(C:\Windows\System32\wow64win.dll)",
304       R"(C:\Windows\System32\wow64cpu.dll)",
305       R"(D:\src\llvm\llvm\tools\lldb\packages\Python\lldbsuite\test\functionalities\postmortem\wow64_minidump\fizzbuzz.exe)",
306       R"(C:\Windows\SysWOW64\ntdll.dll)",
307       R"(C:\Windows\SysWOW64\kernel32.dll)",
308       R"(C:\Windows\SysWOW64\KERNELBASE.dll)",
309       R"(C:\Windows\SysWOW64\advapi32.dll)",
310       R"(C:\Windows\SysWOW64\msvcrt.dll)",
311       R"(C:\Windows\SysWOW64\sechost.dll)",
312       R"(C:\Windows\SysWOW64\rpcrt4.dll)",
313       R"(C:\Windows\SysWOW64\sspicli.dll)",
314       R"(C:\Windows\SysWOW64\CRYPTBASE.dll)",
315       R"(C:\Windows\System32\api-ms-win-core-synch-l1-2-0.DLL)",
316   };
317 
318   for (int i = 0; i < 16; ++i) {
319     llvm::Optional<std::string> name =
320         parser->GetMinidumpString(modules[i].module_name_rva);
321     ASSERT_TRUE(name.hasValue());
322     EXPECT_EQ(module_names[i], name.getValue());
323   }
324 }
325 
326 // Register tests
327 #define REG_VAL32(x) *(reinterpret_cast<uint32_t *>(x))
328 #define REG_VAL64(x) *(reinterpret_cast<uint64_t *>(x))
329 
330 TEST_F(MinidumpParserTest, ConvertMinidumpContext_x86_32) {
331   SetUpData("linux-i386.dmp");
332   llvm::ArrayRef<MinidumpThread> thread_list = parser->GetThreads();
333   const MinidumpThread thread = thread_list[0];
334   llvm::ArrayRef<uint8_t> registers(parser->GetThreadContext(thread));
335 
336   ArchSpec arch = parser->GetArchitecture();
337   auto reg_interface = llvm::make_unique<RegisterContextLinux_i386>(arch);
338   lldb::DataBufferSP buf =
339       ConvertMinidumpContext_x86_32(registers, reg_interface.get());
340   ASSERT_EQ(reg_interface->GetGPRSize(), buf->GetByteSize());
341 
342   const RegisterInfo *reg_info = reg_interface->GetRegisterInfo();
343 
344   std::map<uint64_t, uint32_t> reg_values;
345 
346   reg_values[lldb_eax_i386] = 0x00000000;
347   reg_values[lldb_ebx_i386] = 0xf7778000;
348   reg_values[lldb_ecx_i386] = 0x00000001;
349   reg_values[lldb_edx_i386] = 0xff9dd4a3;
350   reg_values[lldb_edi_i386] = 0x080482a8;
351   reg_values[lldb_esi_i386] = 0xff9dd55c;
352   reg_values[lldb_ebp_i386] = 0xff9dd53c;
353   reg_values[lldb_esp_i386] = 0xff9dd52c;
354   reg_values[lldb_eip_i386] = 0x080482a0;
355   reg_values[lldb_eflags_i386] = 0x00010282;
356   reg_values[lldb_cs_i386] = 0x00000023;
357   reg_values[lldb_fs_i386] = 0x00000000;
358   reg_values[lldb_gs_i386] = 0x00000063;
359   reg_values[lldb_ss_i386] = 0x0000002b;
360   reg_values[lldb_ds_i386] = 0x0000002b;
361   reg_values[lldb_es_i386] = 0x0000002b;
362 
363   for (uint32_t reg_index = 0; reg_index < reg_interface->GetRegisterCount();
364        ++reg_index) {
365     if (reg_values.find(reg_index) != reg_values.end()) {
366       EXPECT_EQ(reg_values[reg_index],
367                 REG_VAL32(buf->GetBytes() + reg_info[reg_index].byte_offset));
368     }
369   }
370 }
371 
372 TEST_F(MinidumpParserTest, ConvertMinidumpContext_x86_64) {
373   SetUpData("linux-x86_64.dmp");
374   llvm::ArrayRef<MinidumpThread> thread_list = parser->GetThreads();
375   const MinidumpThread thread = thread_list[0];
376   llvm::ArrayRef<uint8_t> registers(parser->GetThreadContext(thread));
377 
378   ArchSpec arch = parser->GetArchitecture();
379   auto reg_interface = llvm::make_unique<RegisterContextLinux_x86_64>(arch);
380   lldb::DataBufferSP buf =
381       ConvertMinidumpContext_x86_64(registers, reg_interface.get());
382   ASSERT_EQ(reg_interface->GetGPRSize(), buf->GetByteSize());
383 
384   const RegisterInfo *reg_info = reg_interface->GetRegisterInfo();
385 
386   std::map<uint64_t, uint64_t> reg_values;
387 
388   reg_values[lldb_rax_x86_64] = 0x0000000000000000;
389   reg_values[lldb_rbx_x86_64] = 0x0000000000000000;
390   reg_values[lldb_rcx_x86_64] = 0x0000000000000010;
391   reg_values[lldb_rdx_x86_64] = 0x0000000000000000;
392   reg_values[lldb_rdi_x86_64] = 0x00007ffceb349cf0;
393   reg_values[lldb_rsi_x86_64] = 0x0000000000000000;
394   reg_values[lldb_rbp_x86_64] = 0x00007ffceb34a210;
395   reg_values[lldb_rsp_x86_64] = 0x00007ffceb34a210;
396   reg_values[lldb_r8_x86_64] = 0x00007fe9bc1aa9c0;
397   reg_values[lldb_r9_x86_64] = 0x0000000000000000;
398   reg_values[lldb_r10_x86_64] = 0x00007fe9bc3f16a0;
399   reg_values[lldb_r11_x86_64] = 0x0000000000000246;
400   reg_values[lldb_r12_x86_64] = 0x0000000000401c92;
401   reg_values[lldb_r13_x86_64] = 0x00007ffceb34a430;
402   reg_values[lldb_r14_x86_64] = 0x0000000000000000;
403   reg_values[lldb_r15_x86_64] = 0x0000000000000000;
404   reg_values[lldb_rip_x86_64] = 0x0000000000401dc6;
405   reg_values[lldb_rflags_x86_64] = 0x0000000000010206;
406   reg_values[lldb_cs_x86_64] = 0x0000000000000033;
407   reg_values[lldb_fs_x86_64] = 0x0000000000000000;
408   reg_values[lldb_gs_x86_64] = 0x0000000000000000;
409   reg_values[lldb_ss_x86_64] = 0x0000000000000000;
410   reg_values[lldb_ds_x86_64] = 0x0000000000000000;
411   reg_values[lldb_es_x86_64] = 0x0000000000000000;
412 
413   for (uint32_t reg_index = 0; reg_index < reg_interface->GetRegisterCount();
414        ++reg_index) {
415     if (reg_values.find(reg_index) != reg_values.end()) {
416       EXPECT_EQ(reg_values[reg_index],
417                 REG_VAL64(buf->GetBytes() + reg_info[reg_index].byte_offset));
418     }
419   }
420 }
421 
422 TEST_F(MinidumpParserTest, ConvertMinidumpContext_x86_32_wow64) {
423   SetUpData("fizzbuzz_wow64.dmp");
424   llvm::ArrayRef<MinidumpThread> thread_list = parser->GetThreads();
425   const MinidumpThread thread = thread_list[0];
426   llvm::ArrayRef<uint8_t> registers(parser->GetThreadContextWow64(thread));
427 
428   ArchSpec arch = parser->GetArchitecture();
429   auto reg_interface = llvm::make_unique<RegisterContextLinux_i386>(arch);
430   lldb::DataBufferSP buf =
431       ConvertMinidumpContext_x86_32(registers, reg_interface.get());
432   ASSERT_EQ(reg_interface->GetGPRSize(), buf->GetByteSize());
433 
434   const RegisterInfo *reg_info = reg_interface->GetRegisterInfo();
435 
436   std::map<uint64_t, uint32_t> reg_values;
437 
438   reg_values[lldb_eax_i386] = 0x00000000;
439   reg_values[lldb_ebx_i386] = 0x0037f608;
440   reg_values[lldb_ecx_i386] = 0x00e61578;
441   reg_values[lldb_edx_i386] = 0x00000008;
442   reg_values[lldb_edi_i386] = 0x00000000;
443   reg_values[lldb_esi_i386] = 0x00000002;
444   reg_values[lldb_ebp_i386] = 0x0037f654;
445   reg_values[lldb_esp_i386] = 0x0037f5b8;
446   reg_values[lldb_eip_i386] = 0x77ce01fd;
447   reg_values[lldb_eflags_i386] = 0x00000246;
448   reg_values[lldb_cs_i386] = 0x00000023;
449   reg_values[lldb_fs_i386] = 0x00000053;
450   reg_values[lldb_gs_i386] = 0x0000002b;
451   reg_values[lldb_ss_i386] = 0x0000002b;
452   reg_values[lldb_ds_i386] = 0x0000002b;
453   reg_values[lldb_es_i386] = 0x0000002b;
454 
455   for (uint32_t reg_index = 0; reg_index < reg_interface->GetRegisterCount();
456        ++reg_index) {
457     if (reg_values.find(reg_index) != reg_values.end()) {
458       EXPECT_EQ(reg_values[reg_index],
459                 REG_VAL32(buf->GetBytes() + reg_info[reg_index].byte_offset));
460     }
461   }
462 }
463