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