983 lines · cpp
1//===-- ProcessMinidump.cpp -----------------------------------------------===//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-exception6//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/DynamicLoader.h"25#include "lldb/Target/JITLoaderList.h"26#include "lldb/Target/MemoryRegionInfo.h"27#include "lldb/Target/SectionLoadList.h"28#include "lldb/Target/Target.h"29#include "lldb/Target/UnixSignals.h"30#include "lldb/Utility/DataBufferHeap.h"31#include "lldb/Utility/LLDBAssert.h"32#include "lldb/Utility/LLDBLog.h"33#include "lldb/Utility/Log.h"34#include "lldb/Utility/State.h"35#include "llvm/BinaryFormat/Magic.h"36#include "llvm/Support/MemoryBuffer.h"37#include "llvm/Support/Threading.h"38 39#include "Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.h"40#include "Plugins/ObjectFile/Placeholder/ObjectFilePlaceholder.h"41#include "Plugins/Process/Utility/StopInfoMachException.h"42 43#include <memory>44#include <optional>45 46using namespace lldb;47using namespace lldb_private;48using namespace minidump;49 50LLDB_PLUGIN_DEFINE(ProcessMinidump)51 52namespace {53 54/// Duplicate the HashElfTextSection() from the breakpad sources.55///56/// Breakpad, a Google crash log reporting tool suite, creates minidump files57/// for many different architectures. When using Breakpad to create ELF58/// minidumps, it will check for a GNU build ID when creating a minidump file59/// and if one doesn't exist in the file, it will say the UUID of the file is a60/// checksum of up to the first 4096 bytes of the .text section. Facebook also61/// uses breakpad and modified this hash to avoid collisions so we can62/// calculate and check for this as well.63///64/// The breakpad code might end up hashing up to 15 bytes that immediately65/// follow the .text section in the file, so this code must do exactly what it66/// does so we can get an exact match for the UUID.67///68/// \param[in] module_sp The module to grab the .text section from.69///70/// \param[in,out] breakpad_uuid A vector that will receive the calculated71/// breakpad .text hash.72///73/// \param[in,out] facebook_uuid A vector that will receive the calculated74/// facebook .text hash.75///76void HashElfTextSection(ModuleSP module_sp, std::vector<uint8_t> &breakpad_uuid,77 std::vector<uint8_t> &facebook_uuid) {78 SectionList *sect_list = module_sp->GetSectionList();79 if (sect_list == nullptr)80 return;81 SectionSP sect_sp = sect_list->FindSectionByName(ConstString(".text"));82 if (!sect_sp)83 return;84 constexpr size_t kMDGUIDSize = 16;85 constexpr size_t kBreakpadPageSize = 4096;86 // The breakpad code has a bug where it might access beyond the end of a87 // .text section by up to 15 bytes, so we must ensure we round up to the88 // next kMDGUIDSize byte boundary.89 DataExtractor data;90 const size_t text_size = sect_sp->GetFileSize();91 const size_t read_size = std::min<size_t>(92 llvm::alignTo(text_size, kMDGUIDSize), kBreakpadPageSize);93 sect_sp->GetObjectFile()->GetData(sect_sp->GetFileOffset(), read_size, data);94 95 breakpad_uuid.assign(kMDGUIDSize, 0);96 facebook_uuid.assign(kMDGUIDSize, 0);97 98 // The only difference between the breakpad hash and the facebook hash is the99 // hashing of the text section size into the hash prior to hashing the .text100 // contents.101 for (size_t i = 0; i < kMDGUIDSize; i++)102 facebook_uuid[i] ^= text_size % 255;103 104 // This code carefully duplicates how the hash was created in Breakpad105 // sources, including the error where it might has an extra 15 bytes past the106 // end of the .text section if the .text section is less than a page size in107 // length.108 const uint8_t *ptr = data.GetDataStart();109 const uint8_t *ptr_end = data.GetDataEnd();110 while (ptr < ptr_end) {111 for (unsigned i = 0; i < kMDGUIDSize; i++) {112 breakpad_uuid[i] ^= ptr[i];113 facebook_uuid[i] ^= ptr[i];114 }115 ptr += kMDGUIDSize;116 }117}118 119} // namespace120 121llvm::StringRef ProcessMinidump::GetPluginDescriptionStatic() {122 return "Minidump plug-in.";123}124 125lldb::ProcessSP ProcessMinidump::CreateInstance(lldb::TargetSP target_sp,126 lldb::ListenerSP listener_sp,127 const FileSpec *crash_file,128 bool can_connect) {129 if (!crash_file || can_connect)130 return nullptr;131 132 lldb::ProcessSP process_sp;133 // Read enough data for the Minidump header134 constexpr size_t header_size = sizeof(Header);135 auto DataPtr = FileSystem::Instance().CreateDataBuffer(crash_file->GetPath(),136 header_size, 0);137 if (!DataPtr)138 return nullptr;139 140 lldbassert(DataPtr->GetByteSize() == header_size);141 if (identify_magic(toStringRef(DataPtr->GetData())) != llvm::file_magic::minidump)142 return nullptr;143 144 auto AllData =145 FileSystem::Instance().CreateDataBuffer(crash_file->GetPath(), -1, 0);146 if (!AllData)147 return nullptr;148 149 return std::make_shared<ProcessMinidump>(target_sp, listener_sp, *crash_file,150 std::move(AllData));151}152 153bool ProcessMinidump::CanDebug(lldb::TargetSP target_sp,154 bool plugin_specified_by_name) {155 return true;156}157 158ProcessMinidump::ProcessMinidump(lldb::TargetSP target_sp,159 lldb::ListenerSP listener_sp,160 const FileSpec &core_file,161 DataBufferSP core_data)162 : PostMortemProcess(target_sp, listener_sp, core_file),163 m_core_data(std::move(core_data)), m_is_wow64(false) {}164 165ProcessMinidump::~ProcessMinidump() {166 Clear();167 // We need to call finalize on the process before destroying ourselves to168 // make sure all of the broadcaster cleanup goes as planned. If we destruct169 // this class, then Process::~Process() might have problems trying to fully170 // destroy the broadcaster.171 Finalize(true /* destructing */);172}173 174void ProcessMinidump::Initialize() {175 static llvm::once_flag g_once_flag;176 177 llvm::call_once(g_once_flag, []() {178 PluginManager::RegisterPlugin(GetPluginNameStatic(),179 GetPluginDescriptionStatic(),180 ProcessMinidump::CreateInstance);181 });182}183 184void ProcessMinidump::Terminate() {185 PluginManager::UnregisterPlugin(ProcessMinidump::CreateInstance);186}187 188Status ProcessMinidump::DoLoadCore() {189 auto expected_parser = MinidumpParser::Create(m_core_data);190 if (!expected_parser)191 return Status::FromError(expected_parser.takeError());192 m_minidump_parser = std::move(*expected_parser);193 194 Status error;195 196 // Do we support the minidump's architecture?197 ArchSpec arch = GetArchitecture();198 switch (arch.GetMachine()) {199 case llvm::Triple::x86:200 case llvm::Triple::x86_64:201 case llvm::Triple::arm:202 case llvm::Triple::aarch64:203 // Any supported architectures must be listed here and also supported in204 // ThreadMinidump::CreateRegisterContextForFrame().205 break;206 default:207 error = Status::FromErrorStringWithFormat(208 "unsupported minidump architecture: %s", arch.GetArchitectureName());209 return error;210 }211 GetTarget().SetArchitecture(arch, true /*set_platform*/);212 213 m_thread_list = m_minidump_parser->GetThreads();214 auto exception_stream_it = m_minidump_parser->GetExceptionStreams();215 for (auto exception_stream : exception_stream_it) {216 // If we can't read an exception stream skip it217 // We should probably serve a warning218 if (!exception_stream)219 continue;220 221 if (!m_exceptions_by_tid222 .try_emplace(exception_stream->ThreadId, exception_stream.get())223 .second) {224 return Status::FromErrorStringWithFormatv(225 "Duplicate exception stream for tid {0}", exception_stream->ThreadId);226 }227 }228 229 SetUnixSignals(UnixSignals::Create(GetArchitecture()));230 231 ReadModuleList();232 if (ModuleSP module = GetTarget().GetExecutableModule())233 GetTarget().MergeArchitecture(module->GetArchitecture());234 std::optional<lldb::pid_t> pid = m_minidump_parser->GetPid();235 if (!pid) {236 Debugger::ReportWarning("unable to retrieve process ID from minidump file, "237 "setting process ID to 1",238 GetTarget().GetDebugger().GetID());239 pid = 1;240 }241 SetID(*pid);242 243 return error;244}245 246Status ProcessMinidump::DoDestroy() { return Status(); }247 248void ProcessMinidump::RefreshStateAfterStop() {249 250 for (const auto &[_, exception_stream] : m_exceptions_by_tid) {251 constexpr uint32_t BreakpadDumpRequested = 0xFFFFFFFF;252 if (exception_stream.ExceptionRecord.ExceptionCode ==253 BreakpadDumpRequested) {254 // This "ExceptionCode" value is a sentinel that is sometimes used255 // when generating a dump for a process that hasn't crashed.256 257 // TODO: The definition and use of this "dump requested" constant258 // in Breakpad are actually Linux-specific, and for similar use259 // cases on Mac/Windows it defines different constants, referring260 // to them as "simulated" exceptions; consider moving this check261 // down to the OS-specific paths and checking each OS for its own262 // constant.263 return;264 }265 266 lldb::StopInfoSP stop_info;267 lldb::ThreadSP stop_thread;268 269 Process::m_thread_list.SetSelectedThreadByID(exception_stream.ThreadId);270 stop_thread = Process::m_thread_list.GetSelectedThread();271 ArchSpec arch = GetArchitecture();272 273 if (arch.GetTriple().getOS() == llvm::Triple::Linux) {274 uint32_t signo = exception_stream.ExceptionRecord.ExceptionCode;275 if (signo == 0) {276 // No stop.277 return;278 }279 const char *description = nullptr;280 if (exception_stream.ExceptionRecord.ExceptionFlags ==281 llvm::minidump::Exception::LLDB_FLAG)282 description = reinterpret_cast<const char *>(283 exception_stream.ExceptionRecord.ExceptionInformation);284 285 llvm::StringRef description_str(description,286 Exception::MaxParameterBytes);287 stop_info = StopInfo::CreateStopReasonWithSignal(288 *stop_thread, signo, description_str.str().c_str());289 } else if (arch.GetTriple().getVendor() == llvm::Triple::Apple) {290 stop_info = StopInfoMachException::CreateStopReasonWithMachException(291 *stop_thread, exception_stream.ExceptionRecord.ExceptionCode, 2,292 exception_stream.ExceptionRecord.ExceptionFlags,293 exception_stream.ExceptionRecord.ExceptionAddress, 0);294 } else {295 std::string desc;296 llvm::raw_string_ostream desc_stream(desc);297 desc_stream << "Exception "298 << llvm::format_hex(299 exception_stream.ExceptionRecord.ExceptionCode, 8)300 << " encountered at address "301 << llvm::format_hex(302 exception_stream.ExceptionRecord.ExceptionAddress, 8);303 stop_info =304 StopInfo::CreateStopReasonWithException(*stop_thread, desc.c_str());305 }306 307 stop_thread->SetStopInfo(stop_info);308 }309}310 311bool ProcessMinidump::IsAlive() { return true; }312 313bool ProcessMinidump::WarnBeforeDetach() const { return false; }314 315size_t ProcessMinidump::ReadMemory(lldb::addr_t addr, void *buf, size_t size,316 Status &error) {317 // Don't allow the caching that lldb_private::Process::ReadMemory does since318 // we have it all cached in our dump file anyway.319 return DoReadMemory(addr, buf, size, error);320}321 322size_t ProcessMinidump::DoReadMemory(lldb::addr_t addr, void *buf, size_t size,323 Status &error) {324 325 llvm::Expected<llvm::ArrayRef<uint8_t>> mem_maybe =326 m_minidump_parser->GetMemory(addr, size);327 if (!mem_maybe) {328 error = Status::FromError(mem_maybe.takeError());329 return 0;330 }331 332 llvm::ArrayRef<uint8_t> mem = *mem_maybe;333 334 std::memcpy(buf, mem.data(), mem.size());335 return mem.size();336}337 338ArchSpec ProcessMinidump::GetArchitecture() {339 if (!m_is_wow64) {340 return m_minidump_parser->GetArchitecture();341 }342 343 llvm::Triple triple;344 triple.setVendor(llvm::Triple::VendorType::UnknownVendor);345 triple.setArch(llvm::Triple::ArchType::x86);346 triple.setOS(llvm::Triple::OSType::Win32);347 return ArchSpec(triple);348}349 350DataExtractor ProcessMinidump::GetAuxvData() {351 std::optional<llvm::ArrayRef<uint8_t>> auxv =352 m_minidump_parser->GetStream(StreamType::LinuxAuxv);353 if (!auxv)354 return DataExtractor();355 356 return DataExtractor(auxv->data(), auxv->size(), GetByteOrder(),357 GetAddressByteSize(), GetAddressByteSize());358}359 360bool ProcessMinidump::IsLLDBMinidump() {361 std::optional<llvm::ArrayRef<uint8_t>> lldb_generated_section =362 m_minidump_parser->GetRawStream(StreamType::LLDBGenerated);363 return lldb_generated_section.has_value();364}365 366DynamicLoader *ProcessMinidump::GetDynamicLoader() {367 // This is a workaround for the dynamic loader not playing nice in issue368 // #119598. The specific reason we use the dynamic loader is to get the TLS369 // info sections, which we can assume are not being written to the minidump370 // unless it's an LLDB generate minidump.371 if (IsLLDBMinidump())372 return PostMortemProcess::GetDynamicLoader();373 return nullptr;374}375 376void ProcessMinidump::BuildMemoryRegions() {377 if (m_memory_regions)378 return;379 m_memory_regions.emplace();380 bool is_complete;381 std::tie(*m_memory_regions, is_complete) =382 m_minidump_parser->BuildMemoryRegions();383 384 if (is_complete)385 return;386 387 MemoryRegionInfos to_add;388 ModuleList &modules = GetTarget().GetImages();389 Target &target = GetTarget();390 modules.ForEach([&](const ModuleSP &module_sp) {391 SectionList *sections = module_sp->GetSectionList();392 for (size_t i = 0; i < sections->GetSize(); ++i) {393 SectionSP section_sp = sections->GetSectionAtIndex(i);394 addr_t load_addr = target.GetSectionLoadAddress(section_sp);395 if (load_addr == LLDB_INVALID_ADDRESS)396 continue;397 MemoryRegionInfo::RangeType section_range(load_addr,398 section_sp->GetByteSize());399 MemoryRegionInfo region =400 MinidumpParser::GetMemoryRegionInfo(*m_memory_regions, load_addr);401 if (region.GetMapped() != MemoryRegionInfo::eYes &&402 region.GetRange().GetRangeBase() <= section_range.GetRangeBase() &&403 section_range.GetRangeEnd() <= region.GetRange().GetRangeEnd()) {404 to_add.emplace_back();405 to_add.back().GetRange() = section_range;406 to_add.back().SetLLDBPermissions(section_sp->GetPermissions());407 to_add.back().SetMapped(MemoryRegionInfo::eYes);408 to_add.back().SetName(module_sp->GetFileSpec().GetPath().c_str());409 }410 }411 return IterationAction::Continue;412 });413 m_memory_regions->insert(m_memory_regions->end(), to_add.begin(),414 to_add.end());415 llvm::sort(*m_memory_regions);416}417 418Status ProcessMinidump::DoGetMemoryRegionInfo(lldb::addr_t load_addr,419 MemoryRegionInfo ®ion) {420 BuildMemoryRegions();421 region = MinidumpParser::GetMemoryRegionInfo(*m_memory_regions, load_addr);422 return Status();423}424 425Status ProcessMinidump::GetMemoryRegions(MemoryRegionInfos ®ion_list) {426 BuildMemoryRegions();427 region_list = *m_memory_regions;428 return Status();429}430 431void ProcessMinidump::Clear() { Process::m_thread_list.Clear(); }432 433bool ProcessMinidump::DoUpdateThreadList(ThreadList &old_thread_list,434 ThreadList &new_thread_list) {435 for (const minidump::Thread &thread : m_thread_list) {436 LocationDescriptor context_location = thread.Context;437 438 // If the minidump contains an exception context, use it439 if (auto it = m_exceptions_by_tid.find(thread.ThreadId);440 it != m_exceptions_by_tid.end())441 context_location = it->second.ThreadContext;442 443 llvm::ArrayRef<uint8_t> context;444 if (!m_is_wow64)445 context = m_minidump_parser->GetThreadContext(context_location);446 else447 context = m_minidump_parser->GetThreadContextWow64(thread);448 449 lldb::ThreadSP thread_sp(new ThreadMinidump(*this, thread, context));450 new_thread_list.AddThread(thread_sp);451 }452 return new_thread_list.GetSize(false) > 0;453}454 455ModuleSP ProcessMinidump::GetOrCreateModule(UUID minidump_uuid,456 llvm::StringRef name,457 ModuleSpec module_spec) {458 Log *log = GetLog(LLDBLog::DynamicLoader);459 Status error;460 461 ModuleSP module_sp =462 GetTarget().GetOrCreateModule(module_spec, true /* notify */, &error);463 if (!module_sp)464 return module_sp;465 // We consider the module to be a match if the minidump UUID is a466 // prefix of the actual UUID, or if either of the UUIDs are empty.467 const auto dmp_bytes = minidump_uuid.GetBytes();468 const auto mod_bytes = module_sp->GetUUID().GetBytes();469 const bool match = dmp_bytes.empty() || mod_bytes.empty() ||470 mod_bytes.take_front(dmp_bytes.size()) == dmp_bytes;471 if (match) {472 LLDB_LOG(log, "Partial uuid match for {0}.", name);473 return module_sp;474 }475 476 // Breakpad generates minindump files, and if there is no GNU build477 // ID in the binary, it will calculate a UUID by hashing first 4096478 // bytes of the .text section and using that as the UUID for a module479 // in the minidump. Facebook uses a modified breakpad client that480 // uses a slightly modified this hash to avoid collisions. Check for481 // UUIDs from the minindump that match these cases and accept the482 // module we find if they do match.483 std::vector<uint8_t> breakpad_uuid;484 std::vector<uint8_t> facebook_uuid;485 HashElfTextSection(module_sp, breakpad_uuid, facebook_uuid);486 if (dmp_bytes == llvm::ArrayRef<uint8_t>(breakpad_uuid)) {487 LLDB_LOG(log, "Breakpad .text hash match for {0}.", name);488 return module_sp;489 }490 if (dmp_bytes == llvm::ArrayRef<uint8_t>(facebook_uuid)) {491 LLDB_LOG(log, "Facebook .text hash match for {0}.", name);492 return module_sp;493 }494 // The UUID wasn't a partial match and didn't match the .text hash495 // so remove the module from the target, we will need to create a496 // placeholder object file.497 GetTarget().GetImages().Remove(module_sp);498 module_sp.reset();499 return module_sp;500}501 502void ProcessMinidump::ReadModuleList() {503 std::vector<const minidump::Module *> filtered_modules =504 m_minidump_parser->GetFilteredModuleList();505 506 Log *log = GetLog(LLDBLog::DynamicLoader);507 508 for (auto module : filtered_modules) {509 std::string name = cantFail(m_minidump_parser->GetMinidumpFile().getString(510 module->ModuleNameRVA));511 const uint64_t load_addr = module->BaseOfImage;512 const uint64_t load_size = module->SizeOfImage;513 LLDB_LOG(log, "found module: name: {0} {1:x10}-{2:x10} size: {3}", name,514 load_addr, load_addr + load_size, load_size);515 516 // check if the process is wow64 - a 32 bit windows process running on a517 // 64 bit windows518 if (llvm::StringRef(name).ends_with_insensitive("wow64.dll")) {519 m_is_wow64 = true;520 }521 522 const auto uuid = m_minidump_parser->GetModuleUUID(module);523 auto file_spec = FileSpec(name, GetArchitecture().GetTriple());524 ModuleSpec module_spec(file_spec, uuid);525 module_spec.GetArchitecture() = GetArchitecture();526 Status error;527 // Try and find a module with a full UUID that matches. This function will528 // add the module to the target if it finds one.529 lldb::ModuleSP module_sp = GetTarget().GetOrCreateModule(module_spec,530 true /* notify */, &error);531 if (module_sp) {532 LLDB_LOG(log, "Full uuid match for {0}.", name);533 } else {534 // We couldn't find a module with an exactly-matching UUID. Sometimes535 // a minidump UUID is only a partial match or is a hash. So try again536 // without specifying the UUID, then again without specifying the537 // directory if that fails. This will allow us to find modules with538 // partial matches or hash UUIDs in user-provided sysroots or search539 // directories (target.exec-search-paths).540 ModuleSpec partial_module_spec = module_spec;541 partial_module_spec.GetUUID().Clear();542 module_sp = GetOrCreateModule(uuid, name, partial_module_spec);543 if (!module_sp) {544 partial_module_spec.GetFileSpec().ClearDirectory();545 module_sp = GetOrCreateModule(uuid, name, partial_module_spec);546 }547 }548 if (module_sp) {549 // Watch out for place holder modules that have different paths, but the550 // same UUID. If the base address is different, create a new module. If551 // we don't then we will end up setting the load address of a different552 // ObjectFilePlaceholder and an assertion will fire.553 auto *objfile = module_sp->GetObjectFile();554 if (objfile &&555 objfile->GetPluginName() ==556 ObjectFilePlaceholder::GetPluginNameStatic()) {557 if (((ObjectFilePlaceholder *)objfile)->GetBaseImageAddress() !=558 load_addr)559 module_sp.reset();560 }561 }562 if (!module_sp) {563 // We failed to locate a matching local object file. Fortunately, the564 // minidump format encodes enough information about each module's memory565 // range to allow us to create placeholder modules.566 //567 // This enables most LLDB functionality involving address-to-module568 // translations (ex. identifing the module for a stack frame PC) and569 // modules/sections commands (ex. target modules list, ...)570 LLDB_LOG(log,571 "Unable to locate the matching object file, creating a "572 "placeholder module for: {0}",573 name);574 575 module_sp = Module::CreateModuleFromObjectFile<ObjectFilePlaceholder>(576 module_spec, load_addr, load_size);577 // If we haven't loaded a main executable yet, set the first module to be578 // main executable579 if (!GetTarget().GetExecutableModule())580 GetTarget().SetExecutableModule(module_sp);581 else582 GetTarget().GetImages().Append(module_sp, true /* notify */);583 }584 585 bool load_addr_changed = false;586 module_sp->SetLoadAddress(GetTarget(), load_addr, false,587 load_addr_changed);588 }589}590 591bool ProcessMinidump::GetProcessInfo(ProcessInstanceInfo &info) {592 info.Clear();593 info.SetProcessID(GetID());594 info.SetArchitecture(GetArchitecture());595 lldb::ModuleSP module_sp = GetTarget().GetExecutableModule();596 if (module_sp) {597 const bool add_exe_file_as_first_arg = false;598 info.SetExecutableFile(GetTarget().GetExecutableModule()->GetFileSpec(),599 add_exe_file_as_first_arg);600 }601 return true;602}603 604// For minidumps there's no runtime generated code so we don't need JITLoader(s)605// Avoiding them will also speed up minidump loading since JITLoaders normally606// try to set up symbolic breakpoints, which in turn may force loading more607// debug information than needed.608JITLoaderList &ProcessMinidump::GetJITLoaders() {609 if (!m_jit_loaders_up) {610 m_jit_loaders_up = std::make_unique<JITLoaderList>();611 }612 return *m_jit_loaders_up;613}614 615#define INIT_BOOL(VAR, LONG, SHORT, DESC) \616 VAR(LLDB_OPT_SET_1, false, LONG, SHORT, DESC, false, true)617#define APPEND_OPT(VAR) \618 m_option_group.Append(&VAR, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1)619 620class CommandObjectProcessMinidumpDump : public CommandObjectParsed {621private:622 OptionGroupOptions m_option_group;623 OptionGroupBoolean m_dump_all;624 OptionGroupBoolean m_dump_directory;625 OptionGroupBoolean m_dump_linux_cpuinfo;626 OptionGroupBoolean m_dump_linux_proc_status;627 OptionGroupBoolean m_dump_linux_lsb_release;628 OptionGroupBoolean m_dump_linux_cmdline;629 OptionGroupBoolean m_dump_linux_environ;630 OptionGroupBoolean m_dump_linux_auxv;631 OptionGroupBoolean m_dump_linux_maps;632 OptionGroupBoolean m_dump_linux_proc_stat;633 OptionGroupBoolean m_dump_linux_proc_uptime;634 OptionGroupBoolean m_dump_linux_proc_fd;635 OptionGroupBoolean m_dump_linux_all;636 OptionGroupBoolean m_fb_app_data;637 OptionGroupBoolean m_fb_build_id;638 OptionGroupBoolean m_fb_version;639 OptionGroupBoolean m_fb_java_stack;640 OptionGroupBoolean m_fb_dalvik;641 OptionGroupBoolean m_fb_unwind;642 OptionGroupBoolean m_fb_error_log;643 OptionGroupBoolean m_fb_app_state;644 OptionGroupBoolean m_fb_abort;645 OptionGroupBoolean m_fb_thread;646 OptionGroupBoolean m_fb_logcat;647 OptionGroupBoolean m_fb_all;648 649 void SetDefaultOptionsIfNoneAreSet() {650 if (m_dump_all.GetOptionValue().GetCurrentValue() ||651 m_dump_linux_all.GetOptionValue().GetCurrentValue() ||652 m_fb_all.GetOptionValue().GetCurrentValue() ||653 m_dump_directory.GetOptionValue().GetCurrentValue() ||654 m_dump_linux_cpuinfo.GetOptionValue().GetCurrentValue() ||655 m_dump_linux_proc_status.GetOptionValue().GetCurrentValue() ||656 m_dump_linux_lsb_release.GetOptionValue().GetCurrentValue() ||657 m_dump_linux_cmdline.GetOptionValue().GetCurrentValue() ||658 m_dump_linux_environ.GetOptionValue().GetCurrentValue() ||659 m_dump_linux_auxv.GetOptionValue().GetCurrentValue() ||660 m_dump_linux_maps.GetOptionValue().GetCurrentValue() ||661 m_dump_linux_proc_stat.GetOptionValue().GetCurrentValue() ||662 m_dump_linux_proc_uptime.GetOptionValue().GetCurrentValue() ||663 m_dump_linux_proc_fd.GetOptionValue().GetCurrentValue() ||664 m_fb_app_data.GetOptionValue().GetCurrentValue() ||665 m_fb_build_id.GetOptionValue().GetCurrentValue() ||666 m_fb_version.GetOptionValue().GetCurrentValue() ||667 m_fb_java_stack.GetOptionValue().GetCurrentValue() ||668 m_fb_dalvik.GetOptionValue().GetCurrentValue() ||669 m_fb_unwind.GetOptionValue().GetCurrentValue() ||670 m_fb_error_log.GetOptionValue().GetCurrentValue() ||671 m_fb_app_state.GetOptionValue().GetCurrentValue() ||672 m_fb_abort.GetOptionValue().GetCurrentValue() ||673 m_fb_thread.GetOptionValue().GetCurrentValue() ||674 m_fb_logcat.GetOptionValue().GetCurrentValue())675 return;676 // If no options were set, then dump everything677 m_dump_all.GetOptionValue().SetCurrentValue(true);678 }679 bool DumpAll() const {680 return m_dump_all.GetOptionValue().GetCurrentValue();681 }682 bool DumpDirectory() const {683 return DumpAll() ||684 m_dump_directory.GetOptionValue().GetCurrentValue();685 }686 bool DumpLinux() const {687 return DumpAll() || m_dump_linux_all.GetOptionValue().GetCurrentValue();688 }689 bool DumpLinuxCPUInfo() const {690 return DumpLinux() ||691 m_dump_linux_cpuinfo.GetOptionValue().GetCurrentValue();692 }693 bool DumpLinuxProcStatus() const {694 return DumpLinux() ||695 m_dump_linux_proc_status.GetOptionValue().GetCurrentValue();696 }697 bool DumpLinuxProcStat() const {698 return DumpLinux() ||699 m_dump_linux_proc_stat.GetOptionValue().GetCurrentValue();700 }701 bool DumpLinuxLSBRelease() const {702 return DumpLinux() ||703 m_dump_linux_lsb_release.GetOptionValue().GetCurrentValue();704 }705 bool DumpLinuxCMDLine() const {706 return DumpLinux() ||707 m_dump_linux_cmdline.GetOptionValue().GetCurrentValue();708 }709 bool DumpLinuxEnviron() const {710 return DumpLinux() ||711 m_dump_linux_environ.GetOptionValue().GetCurrentValue();712 }713 bool DumpLinuxAuxv() const {714 return DumpLinux() ||715 m_dump_linux_auxv.GetOptionValue().GetCurrentValue();716 }717 bool DumpLinuxMaps() const {718 return DumpLinux() ||719 m_dump_linux_maps.GetOptionValue().GetCurrentValue();720 }721 bool DumpLinuxProcUptime() const {722 return DumpLinux() ||723 m_dump_linux_proc_uptime.GetOptionValue().GetCurrentValue();724 }725 bool DumpLinuxProcFD() const {726 return DumpLinux() ||727 m_dump_linux_proc_fd.GetOptionValue().GetCurrentValue();728 }729 bool DumpFacebook() const {730 return DumpAll() || m_fb_all.GetOptionValue().GetCurrentValue();731 }732 bool DumpFacebookAppData() const {733 return DumpFacebook() || m_fb_app_data.GetOptionValue().GetCurrentValue();734 }735 bool DumpFacebookBuildID() const {736 return DumpFacebook() || m_fb_build_id.GetOptionValue().GetCurrentValue();737 }738 bool DumpFacebookVersionName() const {739 return DumpFacebook() || m_fb_version.GetOptionValue().GetCurrentValue();740 }741 bool DumpFacebookJavaStack() const {742 return DumpFacebook() || m_fb_java_stack.GetOptionValue().GetCurrentValue();743 }744 bool DumpFacebookDalvikInfo() const {745 return DumpFacebook() || m_fb_dalvik.GetOptionValue().GetCurrentValue();746 }747 bool DumpFacebookUnwindSymbols() const {748 return DumpFacebook() || m_fb_unwind.GetOptionValue().GetCurrentValue();749 }750 bool DumpFacebookErrorLog() const {751 return DumpFacebook() || m_fb_error_log.GetOptionValue().GetCurrentValue();752 }753 bool DumpFacebookAppStateLog() const {754 return DumpFacebook() || m_fb_app_state.GetOptionValue().GetCurrentValue();755 }756 bool DumpFacebookAbortReason() const {757 return DumpFacebook() || m_fb_abort.GetOptionValue().GetCurrentValue();758 }759 bool DumpFacebookThreadName() const {760 return DumpFacebook() || m_fb_thread.GetOptionValue().GetCurrentValue();761 }762 bool DumpFacebookLogcat() const {763 return DumpFacebook() || m_fb_logcat.GetOptionValue().GetCurrentValue();764 }765public:766 CommandObjectProcessMinidumpDump(CommandInterpreter &interpreter)767 : CommandObjectParsed(interpreter, "process plugin dump",768 "Dump information from the minidump file.", nullptr),769 m_option_group(),770 INIT_BOOL(m_dump_all, "all", 'a',771 "Dump the everything in the minidump."),772 INIT_BOOL(m_dump_directory, "directory", 'd',773 "Dump the minidump directory map."),774 INIT_BOOL(m_dump_linux_cpuinfo, "cpuinfo", 'C',775 "Dump linux /proc/cpuinfo."),776 INIT_BOOL(m_dump_linux_proc_status, "status", 's',777 "Dump linux /proc/<pid>/status."),778 INIT_BOOL(m_dump_linux_lsb_release, "lsb-release", 'r',779 "Dump linux /etc/lsb-release."),780 INIT_BOOL(m_dump_linux_cmdline, "cmdline", 'c',781 "Dump linux /proc/<pid>/cmdline."),782 INIT_BOOL(m_dump_linux_environ, "environ", 'e',783 "Dump linux /proc/<pid>/environ."),784 INIT_BOOL(m_dump_linux_auxv, "auxv", 'x',785 "Dump linux /proc/<pid>/auxv."),786 INIT_BOOL(m_dump_linux_maps, "maps", 'm',787 "Dump linux /proc/<pid>/maps."),788 INIT_BOOL(m_dump_linux_proc_stat, "stat", 'S',789 "Dump linux /proc/<pid>/stat."),790 INIT_BOOL(m_dump_linux_proc_uptime, "uptime", 'u',791 "Dump linux process uptime."),792 INIT_BOOL(m_dump_linux_proc_fd, "fd", 'f',793 "Dump linux /proc/<pid>/fd."),794 INIT_BOOL(m_dump_linux_all, "linux", 'l',795 "Dump all linux streams."),796 INIT_BOOL(m_fb_app_data, "fb-app-data", 1,797 "Dump Facebook application custom data."),798 INIT_BOOL(m_fb_build_id, "fb-build-id", 2,799 "Dump the Facebook build ID."),800 INIT_BOOL(m_fb_version, "fb-version", 3,801 "Dump Facebook application version string."),802 INIT_BOOL(m_fb_java_stack, "fb-java-stack", 4,803 "Dump Facebook java stack."),804 INIT_BOOL(m_fb_dalvik, "fb-dalvik-info", 5,805 "Dump Facebook Dalvik info."),806 INIT_BOOL(m_fb_unwind, "fb-unwind-symbols", 6,807 "Dump Facebook unwind symbols."),808 INIT_BOOL(m_fb_error_log, "fb-error-log", 7,809 "Dump Facebook error log."),810 INIT_BOOL(m_fb_app_state, "fb-app-state-log", 8,811 "Dump Facebook java stack."),812 INIT_BOOL(m_fb_abort, "fb-abort-reason", 9,813 "Dump Facebook abort reason."),814 INIT_BOOL(m_fb_thread, "fb-thread-name", 10,815 "Dump Facebook thread name."),816 INIT_BOOL(m_fb_logcat, "fb-logcat", 11,817 "Dump Facebook logcat."),818 INIT_BOOL(m_fb_all, "facebook", 12, "Dump all Facebook streams.") {819 APPEND_OPT(m_dump_all);820 APPEND_OPT(m_dump_directory);821 APPEND_OPT(m_dump_linux_cpuinfo);822 APPEND_OPT(m_dump_linux_proc_status);823 APPEND_OPT(m_dump_linux_lsb_release);824 APPEND_OPT(m_dump_linux_cmdline);825 APPEND_OPT(m_dump_linux_environ);826 APPEND_OPT(m_dump_linux_auxv);827 APPEND_OPT(m_dump_linux_maps);828 APPEND_OPT(m_dump_linux_proc_stat);829 APPEND_OPT(m_dump_linux_proc_uptime);830 APPEND_OPT(m_dump_linux_proc_fd);831 APPEND_OPT(m_dump_linux_all);832 APPEND_OPT(m_fb_app_data);833 APPEND_OPT(m_fb_build_id);834 APPEND_OPT(m_fb_version);835 APPEND_OPT(m_fb_java_stack);836 APPEND_OPT(m_fb_dalvik);837 APPEND_OPT(m_fb_unwind);838 APPEND_OPT(m_fb_error_log);839 APPEND_OPT(m_fb_app_state);840 APPEND_OPT(m_fb_abort);841 APPEND_OPT(m_fb_thread);842 APPEND_OPT(m_fb_logcat);843 APPEND_OPT(m_fb_all);844 m_option_group.Finalize();845 }846 847 ~CommandObjectProcessMinidumpDump() override = default;848 849 Options *GetOptions() override { return &m_option_group; }850 851 void DoExecute(Args &command, CommandReturnObject &result) override {852 const size_t argc = command.GetArgumentCount();853 if (argc > 0) {854 result.AppendErrorWithFormat("'%s' take no arguments, only options",855 m_cmd_name.c_str());856 return;857 }858 SetDefaultOptionsIfNoneAreSet();859 860 ProcessMinidump *process = static_cast<ProcessMinidump *>(861 m_interpreter.GetExecutionContext().GetProcessPtr());862 result.SetStatus(eReturnStatusSuccessFinishResult);863 Stream &s = result.GetOutputStream();864 MinidumpParser &minidump = *process->m_minidump_parser;865 if (DumpDirectory()) {866 s.Printf("RVA SIZE TYPE StreamType\n");867 s.Printf("---------- ---------- ---------- --------------------------\n");868 for (const auto &stream_desc : minidump.GetMinidumpFile().streams())869 s.Printf(870 "0x%8.8x 0x%8.8x 0x%8.8x %s\n", (uint32_t)stream_desc.Location.RVA,871 (uint32_t)stream_desc.Location.DataSize,872 (unsigned)(StreamType)stream_desc.Type,873 MinidumpParser::GetStreamTypeAsString(stream_desc.Type).data());874 s.Printf("\n");875 }876 auto DumpTextStream = [&](StreamType stream_type,877 llvm::StringRef label) -> void {878 auto bytes = minidump.GetStream(stream_type);879 if (!bytes.empty()) {880 if (label.empty())881 label = MinidumpParser::GetStreamTypeAsString(stream_type);882 s.Printf("%s:\n%s\n\n", label.data(), bytes.data());883 }884 };885 auto DumpBinaryStream = [&](StreamType stream_type,886 llvm::StringRef label) -> void {887 auto bytes = minidump.GetStream(stream_type);888 if (!bytes.empty()) {889 if (label.empty())890 label = MinidumpParser::GetStreamTypeAsString(stream_type);891 s.Printf("%s:\n", label.data());892 DataExtractor data(bytes.data(), bytes.size(), eByteOrderLittle,893 process->GetAddressByteSize());894 DumpDataExtractor(data, &s, 0, lldb::eFormatBytesWithASCII, 1,895 bytes.size(), 16, 0, 0, 0);896 s.Printf("\n\n");897 }898 };899 900 if (DumpLinuxCPUInfo())901 DumpTextStream(StreamType::LinuxCPUInfo, "/proc/cpuinfo");902 if (DumpLinuxProcStatus())903 DumpTextStream(StreamType::LinuxProcStatus, "/proc/PID/status");904 if (DumpLinuxLSBRelease())905 DumpTextStream(StreamType::LinuxLSBRelease, "/etc/lsb-release");906 if (DumpLinuxCMDLine())907 DumpTextStream(StreamType::LinuxCMDLine, "/proc/PID/cmdline");908 if (DumpLinuxEnviron())909 DumpTextStream(StreamType::LinuxEnviron, "/proc/PID/environ");910 if (DumpLinuxAuxv())911 DumpBinaryStream(StreamType::LinuxAuxv, "/proc/PID/auxv");912 if (DumpLinuxMaps())913 DumpTextStream(StreamType::LinuxMaps, "/proc/PID/maps");914 if (DumpLinuxProcStat())915 DumpTextStream(StreamType::LinuxProcStat, "/proc/PID/stat");916 if (DumpLinuxProcUptime())917 DumpTextStream(StreamType::LinuxProcUptime, "uptime");918 if (DumpLinuxProcFD())919 DumpTextStream(StreamType::LinuxProcFD, "/proc/PID/fd");920 if (DumpFacebookAppData())921 DumpTextStream(StreamType::FacebookAppCustomData,922 "Facebook App Data");923 if (DumpFacebookBuildID()) {924 auto bytes = minidump.GetStream(StreamType::FacebookBuildID);925 if (bytes.size() >= 4) {926 DataExtractor data(bytes.data(), bytes.size(), eByteOrderLittle,927 process->GetAddressByteSize());928 lldb::offset_t offset = 0;929 uint32_t build_id = data.GetU32(&offset);930 s.Printf("Facebook Build ID:\n");931 s.Printf("%u\n", build_id);932 s.Printf("\n");933 }934 }935 if (DumpFacebookVersionName())936 DumpTextStream(StreamType::FacebookAppVersionName,937 "Facebook Version String");938 if (DumpFacebookJavaStack())939 DumpTextStream(StreamType::FacebookJavaStack,940 "Facebook Java Stack");941 if (DumpFacebookDalvikInfo())942 DumpTextStream(StreamType::FacebookDalvikInfo,943 "Facebook Dalvik Info");944 if (DumpFacebookUnwindSymbols())945 DumpBinaryStream(StreamType::FacebookUnwindSymbols,946 "Facebook Unwind Symbols Bytes");947 if (DumpFacebookErrorLog())948 DumpTextStream(StreamType::FacebookDumpErrorLog,949 "Facebook Error Log");950 if (DumpFacebookAppStateLog())951 DumpTextStream(StreamType::FacebookAppStateLog,952 "Faceook Application State Log");953 if (DumpFacebookAbortReason())954 DumpTextStream(StreamType::FacebookAbortReason,955 "Facebook Abort Reason");956 if (DumpFacebookThreadName())957 DumpTextStream(StreamType::FacebookThreadName,958 "Facebook Thread Name");959 if (DumpFacebookLogcat())960 DumpTextStream(StreamType::FacebookLogcat, "Facebook Logcat");961 }962};963 964class CommandObjectMultiwordProcessMinidump : public CommandObjectMultiword {965public:966 CommandObjectMultiwordProcessMinidump(CommandInterpreter &interpreter)967 : CommandObjectMultiword(interpreter, "process plugin",968 "Commands for operating on a ProcessMinidump process.",969 "process plugin <subcommand> [<subcommand-options>]") {970 LoadSubCommand("dump",971 CommandObjectSP(new CommandObjectProcessMinidumpDump(interpreter)));972 }973 974 ~CommandObjectMultiwordProcessMinidump() override = default;975};976 977CommandObject *ProcessMinidump::GetPluginCommandObject() {978 if (!m_command_sp)979 m_command_sp = std::make_shared<CommandObjectMultiwordProcessMinidump>(980 GetTarget().GetDebugger().GetCommandInterpreter());981 return m_command_sp.get();982}983