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