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