1 //===-- ProcessMinidump.cpp -------------------------------------*- C++ -*-===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "ProcessMinidump.h" 10 11 #include "ThreadMinidump.h" 12 13 #include "lldb/Core/DumpDataExtractor.h" 14 #include "lldb/Core/Module.h" 15 #include "lldb/Core/ModuleSpec.h" 16 #include "lldb/Core/PluginManager.h" 17 #include "lldb/Core/Section.h" 18 #include "lldb/Interpreter/CommandInterpreter.h" 19 #include "lldb/Interpreter/CommandObject.h" 20 #include "lldb/Interpreter/CommandObjectMultiword.h" 21 #include "lldb/Interpreter/CommandReturnObject.h" 22 #include "lldb/Interpreter/OptionArgParser.h" 23 #include "lldb/Interpreter/OptionGroupBoolean.h" 24 #include "lldb/Target/JITLoaderList.h" 25 #include "lldb/Target/MemoryRegionInfo.h" 26 #include "lldb/Target/SectionLoadList.h" 27 #include "lldb/Target/Target.h" 28 #include "lldb/Target/UnixSignals.h" 29 #include "lldb/Utility/LLDBAssert.h" 30 #include "lldb/Utility/Log.h" 31 #include "lldb/Utility/State.h" 32 33 #include "llvm/Support/MemoryBuffer.h" 34 #include "llvm/Support/Threading.h" 35 36 #include "Plugins/Process/Utility/StopInfoMachException.h" 37 38 #include <memory> 39 40 using namespace lldb; 41 using namespace lldb_private; 42 using namespace minidump; 43 44 //------------------------------------------------------------------ 45 /// A placeholder module used for minidumps, where the original 46 /// object files may not be available (so we can't parse the object 47 /// files to extract the set of sections/segments) 48 /// 49 /// This placeholder module has a single synthetic section (.module_image) 50 /// which represents the module memory range covering the whole module. 51 //------------------------------------------------------------------ 52 class PlaceholderModule : public Module { 53 public: 54 PlaceholderModule(const ModuleSpec &module_spec) : 55 Module(module_spec.GetFileSpec(), module_spec.GetArchitecture()) { 56 if (module_spec.GetUUID().IsValid()) 57 SetUUID(module_spec.GetUUID()); 58 } 59 60 // Creates a synthetic module section covering the whole module image (and 61 // sets the section load address as well) 62 void CreateImageSection(const MinidumpModule *module, Target& target) { 63 const ConstString section_name(".module_image"); 64 lldb::SectionSP section_sp(new Section( 65 shared_from_this(), // Module to which this section belongs. 66 nullptr, // ObjectFile 67 0, // Section ID. 68 section_name, // Section name. 69 eSectionTypeContainer, // Section type. 70 module->base_of_image, // VM address. 71 module->size_of_image, // VM size in bytes of this section. 72 0, // Offset of this section in the file. 73 module->size_of_image, // Size of the section as found in the file. 74 12, // Alignment of the section (log2) 75 0, // Flags for this section. 76 1)); // Number of host bytes per target byte 77 section_sp->SetPermissions(ePermissionsExecutable | ePermissionsReadable); 78 GetSectionList()->AddSection(section_sp); 79 target.GetSectionLoadList().SetSectionLoadAddress( 80 section_sp, module->base_of_image); 81 } 82 83 ObjectFile *GetObjectFile() override { return nullptr; } 84 85 SectionList *GetSectionList() override { 86 return Module::GetUnifiedSectionList(); 87 } 88 }; 89 90 ConstString ProcessMinidump::GetPluginNameStatic() { 91 static ConstString g_name("minidump"); 92 return g_name; 93 } 94 95 const char *ProcessMinidump::GetPluginDescriptionStatic() { 96 return "Minidump plug-in."; 97 } 98 99 lldb::ProcessSP ProcessMinidump::CreateInstance(lldb::TargetSP target_sp, 100 lldb::ListenerSP listener_sp, 101 const FileSpec *crash_file) { 102 if (!crash_file) 103 return nullptr; 104 105 lldb::ProcessSP process_sp; 106 // Read enough data for the Minidump header 107 constexpr size_t header_size = sizeof(MinidumpHeader); 108 auto DataPtr = FileSystem::Instance().CreateDataBuffer(crash_file->GetPath(), 109 header_size, 0); 110 if (!DataPtr) 111 return nullptr; 112 113 lldbassert(DataPtr->GetByteSize() == header_size); 114 115 // first, only try to parse the header, beacuse we need to be fast 116 llvm::ArrayRef<uint8_t> HeaderBytes = DataPtr->GetData(); 117 const MinidumpHeader *header = MinidumpHeader::Parse(HeaderBytes); 118 if (header == nullptr) 119 return nullptr; 120 121 auto AllData = 122 FileSystem::Instance().CreateDataBuffer(crash_file->GetPath(), -1, 0); 123 if (!AllData) 124 return nullptr; 125 126 auto minidump_parser = MinidumpParser::Create(AllData); 127 // check if the parser object is valid 128 if (!minidump_parser) 129 return nullptr; 130 131 return std::make_shared<ProcessMinidump>(target_sp, listener_sp, *crash_file, 132 minidump_parser.getValue()); 133 } 134 135 bool ProcessMinidump::CanDebug(lldb::TargetSP target_sp, 136 bool plugin_specified_by_name) { 137 return true; 138 } 139 140 ProcessMinidump::ProcessMinidump(lldb::TargetSP target_sp, 141 lldb::ListenerSP listener_sp, 142 const FileSpec &core_file, 143 MinidumpParser minidump_parser) 144 : Process(target_sp, listener_sp), m_minidump_parser(minidump_parser), 145 m_core_file(core_file), m_is_wow64(false) {} 146 147 ProcessMinidump::~ProcessMinidump() { 148 Clear(); 149 // We need to call finalize on the process before destroying ourselves to 150 // make sure all of the broadcaster cleanup goes as planned. If we destruct 151 // this class, then Process::~Process() might have problems trying to fully 152 // destroy the broadcaster. 153 Finalize(); 154 } 155 156 void ProcessMinidump::Initialize() { 157 static llvm::once_flag g_once_flag; 158 159 llvm::call_once(g_once_flag, []() { 160 PluginManager::RegisterPlugin(GetPluginNameStatic(), 161 GetPluginDescriptionStatic(), 162 ProcessMinidump::CreateInstance); 163 }); 164 } 165 166 void ProcessMinidump::Terminate() { 167 PluginManager::UnregisterPlugin(ProcessMinidump::CreateInstance); 168 } 169 170 Status ProcessMinidump::DoLoadCore() { 171 Status error; 172 173 // Minidump parser initialization & consistency checks 174 error = m_minidump_parser.Initialize(); 175 if (error.Fail()) 176 return error; 177 178 // Do we support the minidump's architecture? 179 ArchSpec arch = GetArchitecture(); 180 switch (arch.GetMachine()) { 181 case llvm::Triple::x86: 182 case llvm::Triple::x86_64: 183 case llvm::Triple::arm: 184 case llvm::Triple::aarch64: 185 // Any supported architectures must be listed here and also supported in 186 // ThreadMinidump::CreateRegisterContextForFrame(). 187 break; 188 default: 189 error.SetErrorStringWithFormat("unsupported minidump architecture: %s", 190 arch.GetArchitectureName()); 191 return error; 192 } 193 GetTarget().SetArchitecture(arch, true /*set_platform*/); 194 195 m_thread_list = m_minidump_parser.GetThreads(); 196 m_active_exception = m_minidump_parser.GetExceptionStream(); 197 ReadModuleList(); 198 199 llvm::Optional<lldb::pid_t> pid = m_minidump_parser.GetPid(); 200 if (!pid) { 201 error.SetErrorString("failed to parse PID"); 202 return error; 203 } 204 SetID(pid.getValue()); 205 206 return error; 207 } 208 209 ConstString ProcessMinidump::GetPluginName() { return GetPluginNameStatic(); } 210 211 uint32_t ProcessMinidump::GetPluginVersion() { return 1; } 212 213 Status ProcessMinidump::DoDestroy() { return Status(); } 214 215 void ProcessMinidump::RefreshStateAfterStop() { 216 if (!m_active_exception) 217 return; 218 219 if (m_active_exception->exception_record.exception_code == 220 MinidumpException::DumpRequested) { 221 return; 222 } 223 224 lldb::StopInfoSP stop_info; 225 lldb::ThreadSP stop_thread; 226 227 Process::m_thread_list.SetSelectedThreadByID(m_active_exception->thread_id); 228 stop_thread = Process::m_thread_list.GetSelectedThread(); 229 ArchSpec arch = GetArchitecture(); 230 231 if (arch.GetTriple().getOS() == llvm::Triple::Linux) { 232 stop_info = StopInfo::CreateStopReasonWithSignal( 233 *stop_thread, m_active_exception->exception_record.exception_code); 234 } else if (arch.GetTriple().getVendor() == llvm::Triple::Apple) { 235 stop_info = StopInfoMachException::CreateStopReasonWithMachException( 236 *stop_thread, m_active_exception->exception_record.exception_code, 2, 237 m_active_exception->exception_record.exception_flags, 238 m_active_exception->exception_record.exception_address, 0); 239 } else { 240 std::string desc; 241 llvm::raw_string_ostream desc_stream(desc); 242 desc_stream << "Exception " 243 << llvm::format_hex( 244 m_active_exception->exception_record.exception_code, 8) 245 << " encountered at address " 246 << llvm::format_hex( 247 m_active_exception->exception_record.exception_address, 248 8); 249 stop_info = StopInfo::CreateStopReasonWithException( 250 *stop_thread, desc_stream.str().c_str()); 251 } 252 253 stop_thread->SetStopInfo(stop_info); 254 } 255 256 bool ProcessMinidump::IsAlive() { return true; } 257 258 bool ProcessMinidump::WarnBeforeDetach() const { return false; } 259 260 size_t ProcessMinidump::ReadMemory(lldb::addr_t addr, void *buf, size_t size, 261 Status &error) { 262 // Don't allow the caching that lldb_private::Process::ReadMemory does since 263 // we have it all cached in our dump file anyway. 264 return DoReadMemory(addr, buf, size, error); 265 } 266 267 size_t ProcessMinidump::DoReadMemory(lldb::addr_t addr, void *buf, size_t size, 268 Status &error) { 269 270 llvm::ArrayRef<uint8_t> mem = m_minidump_parser.GetMemory(addr, size); 271 if (mem.empty()) { 272 error.SetErrorString("could not parse memory info"); 273 return 0; 274 } 275 276 std::memcpy(buf, mem.data(), mem.size()); 277 return mem.size(); 278 } 279 280 ArchSpec ProcessMinidump::GetArchitecture() { 281 if (!m_is_wow64) { 282 return m_minidump_parser.GetArchitecture(); 283 } 284 285 llvm::Triple triple; 286 triple.setVendor(llvm::Triple::VendorType::UnknownVendor); 287 triple.setArch(llvm::Triple::ArchType::x86); 288 triple.setOS(llvm::Triple::OSType::Win32); 289 return ArchSpec(triple); 290 } 291 292 Status ProcessMinidump::GetMemoryRegionInfo(lldb::addr_t load_addr, 293 MemoryRegionInfo &range_info) { 294 range_info = m_minidump_parser.GetMemoryRegionInfo(load_addr); 295 return Status(); 296 } 297 298 Status ProcessMinidump::GetMemoryRegions( 299 lldb_private::MemoryRegionInfos ®ion_list) { 300 region_list = m_minidump_parser.GetMemoryRegions(); 301 return Status(); 302 } 303 304 void ProcessMinidump::Clear() { Process::m_thread_list.Clear(); } 305 306 bool ProcessMinidump::UpdateThreadList(ThreadList &old_thread_list, 307 ThreadList &new_thread_list) { 308 for (const MinidumpThread& thread : m_thread_list) { 309 MinidumpLocationDescriptor context_location = thread.thread_context; 310 311 // If the minidump contains an exception context, use it 312 if (m_active_exception != nullptr && 313 m_active_exception->thread_id == thread.thread_id) { 314 context_location = m_active_exception->thread_context; 315 } 316 317 llvm::ArrayRef<uint8_t> context; 318 if (!m_is_wow64) 319 context = m_minidump_parser.GetThreadContext(context_location); 320 else 321 context = m_minidump_parser.GetThreadContextWow64(thread); 322 323 lldb::ThreadSP thread_sp(new ThreadMinidump(*this, thread, context)); 324 new_thread_list.AddThread(thread_sp); 325 } 326 return new_thread_list.GetSize(false) > 0; 327 } 328 329 void ProcessMinidump::ReadModuleList() { 330 std::vector<const MinidumpModule *> filtered_modules = 331 m_minidump_parser.GetFilteredModuleList(); 332 333 for (auto module : filtered_modules) { 334 llvm::Optional<std::string> name = 335 m_minidump_parser.GetMinidumpString(module->module_name_rva); 336 337 if (!name) 338 continue; 339 340 Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_MODULES)); 341 if (log) { 342 log->Printf("ProcessMinidump::%s found module: name: %s %#010" PRIx64 343 "-%#010" PRIx64 " size: %" PRIu32, 344 __FUNCTION__, name.getValue().c_str(), 345 uint64_t(module->base_of_image), 346 module->base_of_image + module->size_of_image, 347 uint32_t(module->size_of_image)); 348 } 349 350 // check if the process is wow64 - a 32 bit windows process running on a 351 // 64 bit windows 352 if (llvm::StringRef(name.getValue()).endswith_lower("wow64.dll")) { 353 m_is_wow64 = true; 354 } 355 356 const auto uuid = m_minidump_parser.GetModuleUUID(module); 357 auto file_spec = FileSpec(name.getValue(), GetArchitecture().GetTriple()); 358 FileSystem::Instance().Resolve(file_spec); 359 ModuleSpec module_spec(file_spec, uuid); 360 Status error; 361 lldb::ModuleSP module_sp = GetTarget().GetSharedModule(module_spec, &error); 362 if (!module_sp || error.Fail()) { 363 // We failed to locate a matching local object file. Fortunately, the 364 // minidump format encodes enough information about each module's memory 365 // range to allow us to create placeholder modules. 366 // 367 // This enables most LLDB functionality involving address-to-module 368 // translations (ex. identifing the module for a stack frame PC) and 369 // modules/sections commands (ex. target modules list, ...) 370 if (log) { 371 log->Printf("Unable to locate the matching object file, creating a " 372 "placeholder module for: %s", 373 name.getValue().c_str()); 374 } 375 376 auto placeholder_module = 377 std::make_shared<PlaceholderModule>(module_spec); 378 placeholder_module->CreateImageSection(module, GetTarget()); 379 module_sp = placeholder_module; 380 GetTarget().GetImages().Append(module_sp); 381 } 382 383 if (log) { 384 log->Printf("ProcessMinidump::%s load module: name: %s", __FUNCTION__, 385 name.getValue().c_str()); 386 } 387 388 bool load_addr_changed = false; 389 module_sp->SetLoadAddress(GetTarget(), module->base_of_image, false, 390 load_addr_changed); 391 } 392 } 393 394 bool ProcessMinidump::GetProcessInfo(ProcessInstanceInfo &info) { 395 info.Clear(); 396 info.SetProcessID(GetID()); 397 info.SetArchitecture(GetArchitecture()); 398 lldb::ModuleSP module_sp = GetTarget().GetExecutableModule(); 399 if (module_sp) { 400 const bool add_exe_file_as_first_arg = false; 401 info.SetExecutableFile(GetTarget().GetExecutableModule()->GetFileSpec(), 402 add_exe_file_as_first_arg); 403 } 404 return true; 405 } 406 407 // For minidumps there's no runtime generated code so we don't need JITLoader(s) 408 // Avoiding them will also speed up minidump loading since JITLoaders normally 409 // try to set up symbolic breakpoints, which in turn may force loading more 410 // debug information than needed. 411 JITLoaderList &ProcessMinidump::GetJITLoaders() { 412 if (!m_jit_loaders_up) { 413 m_jit_loaders_up = llvm::make_unique<JITLoaderList>(); 414 } 415 return *m_jit_loaders_up; 416 } 417 418 #define INIT_BOOL(VAR, LONG, SHORT, DESC) \ 419 VAR(LLDB_OPT_SET_1, false, LONG, SHORT, DESC, false, true) 420 #define APPEND_OPT(VAR) \ 421 m_option_group.Append(&VAR, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1) 422 423 class CommandObjectProcessMinidumpDump : public CommandObjectParsed { 424 private: 425 OptionGroupOptions m_option_group; 426 OptionGroupBoolean m_dump_all; 427 OptionGroupBoolean m_dump_directory; 428 OptionGroupBoolean m_dump_linux_cpuinfo; 429 OptionGroupBoolean m_dump_linux_proc_status; 430 OptionGroupBoolean m_dump_linux_lsb_release; 431 OptionGroupBoolean m_dump_linux_cmdline; 432 OptionGroupBoolean m_dump_linux_environ; 433 OptionGroupBoolean m_dump_linux_auxv; 434 OptionGroupBoolean m_dump_linux_maps; 435 OptionGroupBoolean m_dump_linux_proc_stat; 436 OptionGroupBoolean m_dump_linux_proc_uptime; 437 OptionGroupBoolean m_dump_linux_proc_fd; 438 OptionGroupBoolean m_dump_linux_all; 439 440 void SetDefaultOptionsIfNoneAreSet() { 441 if (m_dump_all.GetOptionValue().GetCurrentValue() || 442 m_dump_linux_all.GetOptionValue().GetCurrentValue() || 443 m_dump_directory.GetOptionValue().GetCurrentValue() || 444 m_dump_linux_cpuinfo.GetOptionValue().GetCurrentValue() || 445 m_dump_linux_proc_status.GetOptionValue().GetCurrentValue() || 446 m_dump_linux_lsb_release.GetOptionValue().GetCurrentValue() || 447 m_dump_linux_cmdline.GetOptionValue().GetCurrentValue() || 448 m_dump_linux_environ.GetOptionValue().GetCurrentValue() || 449 m_dump_linux_auxv.GetOptionValue().GetCurrentValue() || 450 m_dump_linux_maps.GetOptionValue().GetCurrentValue() || 451 m_dump_linux_proc_stat.GetOptionValue().GetCurrentValue() || 452 m_dump_linux_proc_uptime.GetOptionValue().GetCurrentValue() || 453 m_dump_linux_proc_fd.GetOptionValue().GetCurrentValue()) 454 return; 455 // If no options were set, then dump everything 456 m_dump_all.GetOptionValue().SetCurrentValue(true); 457 } 458 bool DumpAll() const { 459 return m_dump_all.GetOptionValue().GetCurrentValue(); 460 } 461 bool DumpDirectory() const { 462 return DumpAll() || 463 m_dump_directory.GetOptionValue().GetCurrentValue(); 464 } 465 bool DumpLinux() const { 466 return DumpAll() || m_dump_linux_all.GetOptionValue().GetCurrentValue(); 467 } 468 bool DumpLinuxCPUInfo() const { 469 return DumpLinux() || 470 m_dump_linux_cpuinfo.GetOptionValue().GetCurrentValue(); 471 } 472 bool DumpLinuxProcStatus() const { 473 return DumpLinux() || 474 m_dump_linux_proc_status.GetOptionValue().GetCurrentValue(); 475 } 476 bool DumpLinuxProcStat() const { 477 return DumpLinux() || 478 m_dump_linux_proc_stat.GetOptionValue().GetCurrentValue(); 479 } 480 bool DumpLinuxLSBRelease() const { 481 return DumpLinux() || 482 m_dump_linux_lsb_release.GetOptionValue().GetCurrentValue(); 483 } 484 bool DumpLinuxCMDLine() const { 485 return DumpLinux() || 486 m_dump_linux_cmdline.GetOptionValue().GetCurrentValue(); 487 } 488 bool DumpLinuxEnviron() const { 489 return DumpLinux() || 490 m_dump_linux_environ.GetOptionValue().GetCurrentValue(); 491 } 492 bool DumpLinuxAuxv() const { 493 return DumpLinux() || 494 m_dump_linux_auxv.GetOptionValue().GetCurrentValue(); 495 } 496 bool DumpLinuxMaps() const { 497 return DumpLinux() || 498 m_dump_linux_maps.GetOptionValue().GetCurrentValue(); 499 } 500 bool DumpLinuxProcUptime() const { 501 return DumpLinux() || 502 m_dump_linux_proc_uptime.GetOptionValue().GetCurrentValue(); 503 } 504 bool DumpLinuxProcFD() const { 505 return DumpLinux() || 506 m_dump_linux_proc_fd.GetOptionValue().GetCurrentValue(); 507 } 508 public: 509 510 CommandObjectProcessMinidumpDump(CommandInterpreter &interpreter) 511 : CommandObjectParsed(interpreter, "process plugin dump", 512 "Dump information from the minidump file.", NULL), 513 m_option_group(), 514 INIT_BOOL(m_dump_all, "all", 'a', 515 "Dump the everything in the minidump."), 516 INIT_BOOL(m_dump_directory, "directory", 'd', 517 "Dump the minidump directory map."), 518 INIT_BOOL(m_dump_linux_cpuinfo, "cpuinfo", 'C', 519 "Dump linux /proc/cpuinfo."), 520 INIT_BOOL(m_dump_linux_proc_status, "status", 's', 521 "Dump linux /proc/<pid>/status."), 522 INIT_BOOL(m_dump_linux_lsb_release, "lsb-release", 'r', 523 "Dump linux /etc/lsb-release."), 524 INIT_BOOL(m_dump_linux_cmdline, "cmdline", 'c', 525 "Dump linux /proc/<pid>/cmdline."), 526 INIT_BOOL(m_dump_linux_environ, "environ", 'e', 527 "Dump linux /proc/<pid>/environ."), 528 INIT_BOOL(m_dump_linux_auxv, "auxv", 'x', 529 "Dump linux /proc/<pid>/auxv."), 530 INIT_BOOL(m_dump_linux_maps, "maps", 'm', 531 "Dump linux /proc/<pid>/maps."), 532 INIT_BOOL(m_dump_linux_proc_stat, "stat", 'S', 533 "Dump linux /proc/<pid>/stat."), 534 INIT_BOOL(m_dump_linux_proc_uptime, "uptime", 'u', 535 "Dump linux process uptime."), 536 INIT_BOOL(m_dump_linux_proc_fd, "fd", 'f', 537 "Dump linux /proc/<pid>/fd."), 538 INIT_BOOL(m_dump_linux_all, "linux", 'l', 539 "Dump all linux streams.") { 540 APPEND_OPT(m_dump_all); 541 APPEND_OPT(m_dump_directory); 542 APPEND_OPT(m_dump_linux_cpuinfo); 543 APPEND_OPT(m_dump_linux_proc_status); 544 APPEND_OPT(m_dump_linux_lsb_release); 545 APPEND_OPT(m_dump_linux_cmdline); 546 APPEND_OPT(m_dump_linux_environ); 547 APPEND_OPT(m_dump_linux_auxv); 548 APPEND_OPT(m_dump_linux_maps); 549 APPEND_OPT(m_dump_linux_proc_stat); 550 APPEND_OPT(m_dump_linux_proc_uptime); 551 APPEND_OPT(m_dump_linux_proc_fd); 552 APPEND_OPT(m_dump_linux_all); 553 m_option_group.Finalize(); 554 } 555 556 ~CommandObjectProcessMinidumpDump() {} 557 558 Options *GetOptions() override { return &m_option_group; } 559 560 bool DoExecute(Args &command, CommandReturnObject &result) override { 561 const size_t argc = command.GetArgumentCount(); 562 if (argc > 0) { 563 result.AppendErrorWithFormat("'%s' take no arguments, only options", 564 m_cmd_name.c_str()); 565 result.SetStatus(eReturnStatusFailed); 566 return false; 567 } 568 SetDefaultOptionsIfNoneAreSet(); 569 570 ProcessMinidump *process = static_cast<ProcessMinidump *>( 571 m_interpreter.GetExecutionContext().GetProcessPtr()); 572 result.SetStatus(eReturnStatusSuccessFinishResult); 573 Stream &s = result.GetOutputStream(); 574 MinidumpParser &minidump = process->m_minidump_parser; 575 if (DumpDirectory()) { 576 s.Printf("RVA SIZE TYPE MinidumpStreamType\n"); 577 s.Printf("---------- ---------- ---------- --------------------------\n"); 578 for (const auto &pair: minidump.GetDirectoryMap()) 579 s.Printf("0x%8.8x 0x%8.8x 0x%8.8x %s\n", (uint32_t)pair.second.rva, 580 (uint32_t)pair.second.data_size, pair.first, 581 MinidumpParser::GetStreamTypeAsString(pair.first).data()); 582 s.Printf("\n"); 583 } 584 auto DumpTextStream = [&](MinidumpStreamType stream_type, 585 llvm::StringRef label) -> void { 586 auto bytes = minidump.GetStream(stream_type); 587 if (!bytes.empty()) { 588 if (label.empty()) 589 label = MinidumpParser::GetStreamTypeAsString((uint32_t)stream_type); 590 s.Printf("%s:\n%s\n\n", label.data(), bytes.data()); 591 } 592 }; 593 auto DumpBinaryStream = [&](MinidumpStreamType stream_type, 594 llvm::StringRef label) -> void { 595 auto bytes = minidump.GetStream(stream_type); 596 if (!bytes.empty()) { 597 if (label.empty()) 598 label = MinidumpParser::GetStreamTypeAsString((uint32_t)stream_type); 599 s.Printf("%s:\n", label.data()); 600 DataExtractor data(bytes.data(), bytes.size(), eByteOrderLittle, 601 process->GetAddressByteSize()); 602 DumpDataExtractor(data, &s, 0, lldb::eFormatBytesWithASCII, 1, 603 bytes.size(), 16, 0, 0, 0); 604 s.Printf("\n\n"); 605 } 606 }; 607 608 if (DumpLinuxCPUInfo()) 609 DumpTextStream(MinidumpStreamType::LinuxCPUInfo, "/proc/cpuinfo"); 610 if (DumpLinuxProcStatus()) 611 DumpTextStream(MinidumpStreamType::LinuxProcStatus, "/proc/PID/status"); 612 if (DumpLinuxLSBRelease()) 613 DumpTextStream(MinidumpStreamType::LinuxLSBRelease, "/etc/lsb-release"); 614 if (DumpLinuxCMDLine()) 615 DumpTextStream(MinidumpStreamType::LinuxCMDLine, "/proc/PID/cmdline"); 616 if (DumpLinuxEnviron()) 617 DumpTextStream(MinidumpStreamType::LinuxEnviron, "/proc/PID/environ"); 618 if (DumpLinuxAuxv()) 619 DumpBinaryStream(MinidumpStreamType::LinuxAuxv, "/proc/PID/auxv"); 620 if (DumpLinuxMaps()) 621 DumpTextStream(MinidumpStreamType::LinuxMaps, "/proc/PID/maps"); 622 if (DumpLinuxProcStat()) 623 DumpTextStream(MinidumpStreamType::LinuxProcStat, "/proc/PID/stat"); 624 if (DumpLinuxProcUptime()) 625 DumpTextStream(MinidumpStreamType::LinuxProcUptime, "uptime"); 626 if (DumpLinuxProcFD()) 627 DumpTextStream(MinidumpStreamType::LinuxProcFD, "/proc/PID/fd"); 628 return true; 629 } 630 }; 631 632 class CommandObjectMultiwordProcessMinidump : public CommandObjectMultiword { 633 public: 634 CommandObjectMultiwordProcessMinidump(CommandInterpreter &interpreter) 635 : CommandObjectMultiword(interpreter, "process plugin", 636 "Commands for operating on a ProcessMinidump process.", 637 "process plugin <subcommand> [<subcommand-options>]") { 638 LoadSubCommand("dump", 639 CommandObjectSP(new CommandObjectProcessMinidumpDump(interpreter))); 640 } 641 642 ~CommandObjectMultiwordProcessMinidump() {} 643 }; 644 645 CommandObject *ProcessMinidump::GetPluginCommandObject() { 646 if (!m_command_sp) 647 m_command_sp = std::make_shared<CommandObjectMultiwordProcessMinidump>( 648 GetTarget().GetDebugger().GetCommandInterpreter()); 649 return m_command_sp.get(); 650 } 651