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