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