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 }