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