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