Path: blob/main/contrib/llvm-project/lldb/source/Plugins/Process/minidump/ProcessMinidump.cpp
39644 views
//===-- ProcessMinidump.cpp -----------------------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "ProcessMinidump.h"910#include "ThreadMinidump.h"1112#include "lldb/Core/DumpDataExtractor.h"13#include "lldb/Core/Module.h"14#include "lldb/Core/ModuleSpec.h"15#include "lldb/Core/PluginManager.h"16#include "lldb/Core/Section.h"17#include "lldb/Interpreter/CommandInterpreter.h"18#include "lldb/Interpreter/CommandObject.h"19#include "lldb/Interpreter/CommandObjectMultiword.h"20#include "lldb/Interpreter/CommandReturnObject.h"21#include "lldb/Interpreter/OptionArgParser.h"22#include "lldb/Interpreter/OptionGroupBoolean.h"23#include "lldb/Target/JITLoaderList.h"24#include "lldb/Target/MemoryRegionInfo.h"25#include "lldb/Target/SectionLoadList.h"26#include "lldb/Target/Target.h"27#include "lldb/Target/UnixSignals.h"28#include "lldb/Utility/LLDBAssert.h"29#include "lldb/Utility/LLDBLog.h"30#include "lldb/Utility/Log.h"31#include "lldb/Utility/State.h"32#include "llvm/BinaryFormat/Magic.h"33#include "llvm/Support/MemoryBuffer.h"34#include "llvm/Support/Threading.h"3536#include "Plugins/ObjectFile/Placeholder/ObjectFilePlaceholder.h"37#include "Plugins/Process/Utility/StopInfoMachException.h"3839#include <memory>40#include <optional>4142using namespace lldb;43using namespace lldb_private;44using namespace minidump;4546LLDB_PLUGIN_DEFINE(ProcessMinidump)4748namespace {4950/// Duplicate the HashElfTextSection() from the breakpad sources.51///52/// Breakpad, a Google crash log reporting tool suite, creates minidump files53/// for many different architectures. When using Breakpad to create ELF54/// minidumps, it will check for a GNU build ID when creating a minidump file55/// and if one doesn't exist in the file, it will say the UUID of the file is a56/// checksum of up to the first 4096 bytes of the .text section. Facebook also57/// uses breakpad and modified this hash to avoid collisions so we can58/// calculate and check for this as well.59///60/// The breakpad code might end up hashing up to 15 bytes that immediately61/// follow the .text section in the file, so this code must do exactly what it62/// does so we can get an exact match for the UUID.63///64/// \param[in] module_sp The module to grab the .text section from.65///66/// \param[in,out] breakpad_uuid A vector that will receive the calculated67/// breakpad .text hash.68///69/// \param[in,out] facebook_uuid A vector that will receive the calculated70/// facebook .text hash.71///72void HashElfTextSection(ModuleSP module_sp, std::vector<uint8_t> &breakpad_uuid,73std::vector<uint8_t> &facebook_uuid) {74SectionList *sect_list = module_sp->GetSectionList();75if (sect_list == nullptr)76return;77SectionSP sect_sp = sect_list->FindSectionByName(ConstString(".text"));78if (!sect_sp)79return;80constexpr size_t kMDGUIDSize = 16;81constexpr size_t kBreakpadPageSize = 4096;82// The breakpad code has a bug where it might access beyond the end of a83// .text section by up to 15 bytes, so we must ensure we round up to the84// next kMDGUIDSize byte boundary.85DataExtractor data;86const size_t text_size = sect_sp->GetFileSize();87const size_t read_size = std::min<size_t>(88llvm::alignTo(text_size, kMDGUIDSize), kBreakpadPageSize);89sect_sp->GetObjectFile()->GetData(sect_sp->GetFileOffset(), read_size, data);9091breakpad_uuid.assign(kMDGUIDSize, 0);92facebook_uuid.assign(kMDGUIDSize, 0);9394// The only difference between the breakpad hash and the facebook hash is the95// hashing of the text section size into the hash prior to hashing the .text96// contents.97for (size_t i = 0; i < kMDGUIDSize; i++)98facebook_uuid[i] ^= text_size % 255;99100// This code carefully duplicates how the hash was created in Breakpad101// sources, including the error where it might has an extra 15 bytes past the102// end of the .text section if the .text section is less than a page size in103// length.104const uint8_t *ptr = data.GetDataStart();105const uint8_t *ptr_end = data.GetDataEnd();106while (ptr < ptr_end) {107for (unsigned i = 0; i < kMDGUIDSize; i++) {108breakpad_uuid[i] ^= ptr[i];109facebook_uuid[i] ^= ptr[i];110}111ptr += kMDGUIDSize;112}113}114115} // namespace116117llvm::StringRef ProcessMinidump::GetPluginDescriptionStatic() {118return "Minidump plug-in.";119}120121lldb::ProcessSP ProcessMinidump::CreateInstance(lldb::TargetSP target_sp,122lldb::ListenerSP listener_sp,123const FileSpec *crash_file,124bool can_connect) {125if (!crash_file || can_connect)126return nullptr;127128lldb::ProcessSP process_sp;129// Read enough data for the Minidump header130constexpr size_t header_size = sizeof(Header);131auto DataPtr = FileSystem::Instance().CreateDataBuffer(crash_file->GetPath(),132header_size, 0);133if (!DataPtr)134return nullptr;135136lldbassert(DataPtr->GetByteSize() == header_size);137if (identify_magic(toStringRef(DataPtr->GetData())) != llvm::file_magic::minidump)138return nullptr;139140auto AllData =141FileSystem::Instance().CreateDataBuffer(crash_file->GetPath(), -1, 0);142if (!AllData)143return nullptr;144145return std::make_shared<ProcessMinidump>(target_sp, listener_sp, *crash_file,146std::move(AllData));147}148149bool ProcessMinidump::CanDebug(lldb::TargetSP target_sp,150bool plugin_specified_by_name) {151return true;152}153154ProcessMinidump::ProcessMinidump(lldb::TargetSP target_sp,155lldb::ListenerSP listener_sp,156const FileSpec &core_file,157DataBufferSP core_data)158: PostMortemProcess(target_sp, listener_sp, core_file),159m_core_data(std::move(core_data)), m_active_exception(nullptr),160m_is_wow64(false) {}161162ProcessMinidump::~ProcessMinidump() {163Clear();164// We need to call finalize on the process before destroying ourselves to165// make sure all of the broadcaster cleanup goes as planned. If we destruct166// this class, then Process::~Process() might have problems trying to fully167// destroy the broadcaster.168Finalize(true /* destructing */);169}170171void ProcessMinidump::Initialize() {172static llvm::once_flag g_once_flag;173174llvm::call_once(g_once_flag, []() {175PluginManager::RegisterPlugin(GetPluginNameStatic(),176GetPluginDescriptionStatic(),177ProcessMinidump::CreateInstance);178});179}180181void ProcessMinidump::Terminate() {182PluginManager::UnregisterPlugin(ProcessMinidump::CreateInstance);183}184185Status ProcessMinidump::DoLoadCore() {186auto expected_parser = MinidumpParser::Create(m_core_data);187if (!expected_parser)188return Status(expected_parser.takeError());189m_minidump_parser = std::move(*expected_parser);190191Status error;192193// Do we support the minidump's architecture?194ArchSpec arch = GetArchitecture();195switch (arch.GetMachine()) {196case llvm::Triple::x86:197case llvm::Triple::x86_64:198case llvm::Triple::arm:199case llvm::Triple::aarch64:200// Any supported architectures must be listed here and also supported in201// ThreadMinidump::CreateRegisterContextForFrame().202break;203default:204error.SetErrorStringWithFormat("unsupported minidump architecture: %s",205arch.GetArchitectureName());206return error;207}208GetTarget().SetArchitecture(arch, true /*set_platform*/);209210m_thread_list = m_minidump_parser->GetThreads();211m_active_exception = m_minidump_parser->GetExceptionStream();212213SetUnixSignals(UnixSignals::Create(GetArchitecture()));214215ReadModuleList();216if (ModuleSP module = GetTarget().GetExecutableModule())217GetTarget().MergeArchitecture(module->GetArchitecture());218std::optional<lldb::pid_t> pid = m_minidump_parser->GetPid();219if (!pid) {220Debugger::ReportWarning("unable to retrieve process ID from minidump file, "221"setting process ID to 1",222GetTarget().GetDebugger().GetID());223pid = 1;224}225SetID(*pid);226227return error;228}229230Status ProcessMinidump::DoDestroy() { return Status(); }231232void ProcessMinidump::RefreshStateAfterStop() {233234if (!m_active_exception)235return;236237constexpr uint32_t BreakpadDumpRequested = 0xFFFFFFFF;238if (m_active_exception->ExceptionRecord.ExceptionCode ==239BreakpadDumpRequested) {240// This "ExceptionCode" value is a sentinel that is sometimes used241// when generating a dump for a process that hasn't crashed.242243// TODO: The definition and use of this "dump requested" constant244// in Breakpad are actually Linux-specific, and for similar use245// cases on Mac/Windows it defines different constants, referring246// to them as "simulated" exceptions; consider moving this check247// down to the OS-specific paths and checking each OS for its own248// constant.249return;250}251252lldb::StopInfoSP stop_info;253lldb::ThreadSP stop_thread;254255Process::m_thread_list.SetSelectedThreadByID(m_active_exception->ThreadId);256stop_thread = Process::m_thread_list.GetSelectedThread();257ArchSpec arch = GetArchitecture();258259if (arch.GetTriple().getOS() == llvm::Triple::Linux) {260uint32_t signo = m_active_exception->ExceptionRecord.ExceptionCode;261262if (signo == 0) {263// No stop.264return;265}266267stop_info = StopInfo::CreateStopReasonWithSignal(268*stop_thread, signo);269} else if (arch.GetTriple().getVendor() == llvm::Triple::Apple) {270stop_info = StopInfoMachException::CreateStopReasonWithMachException(271*stop_thread, m_active_exception->ExceptionRecord.ExceptionCode, 2,272m_active_exception->ExceptionRecord.ExceptionFlags,273m_active_exception->ExceptionRecord.ExceptionAddress, 0);274} else {275std::string desc;276llvm::raw_string_ostream desc_stream(desc);277desc_stream << "Exception "278<< llvm::format_hex(279m_active_exception->ExceptionRecord.ExceptionCode, 8)280<< " encountered at address "281<< llvm::format_hex(282m_active_exception->ExceptionRecord.ExceptionAddress, 8);283stop_info = StopInfo::CreateStopReasonWithException(284*stop_thread, desc_stream.str().c_str());285}286287stop_thread->SetStopInfo(stop_info);288}289290bool ProcessMinidump::IsAlive() { return true; }291292bool ProcessMinidump::WarnBeforeDetach() const { return false; }293294size_t ProcessMinidump::ReadMemory(lldb::addr_t addr, void *buf, size_t size,295Status &error) {296// Don't allow the caching that lldb_private::Process::ReadMemory does since297// we have it all cached in our dump file anyway.298return DoReadMemory(addr, buf, size, error);299}300301size_t ProcessMinidump::DoReadMemory(lldb::addr_t addr, void *buf, size_t size,302Status &error) {303304llvm::ArrayRef<uint8_t> mem = m_minidump_parser->GetMemory(addr, size);305if (mem.empty()) {306error.SetErrorString("could not parse memory info");307return 0;308}309310std::memcpy(buf, mem.data(), mem.size());311return mem.size();312}313314ArchSpec ProcessMinidump::GetArchitecture() {315if (!m_is_wow64) {316return m_minidump_parser->GetArchitecture();317}318319llvm::Triple triple;320triple.setVendor(llvm::Triple::VendorType::UnknownVendor);321triple.setArch(llvm::Triple::ArchType::x86);322triple.setOS(llvm::Triple::OSType::Win32);323return ArchSpec(triple);324}325326void ProcessMinidump::BuildMemoryRegions() {327if (m_memory_regions)328return;329m_memory_regions.emplace();330bool is_complete;331std::tie(*m_memory_regions, is_complete) =332m_minidump_parser->BuildMemoryRegions();333334if (is_complete)335return;336337MemoryRegionInfos to_add;338ModuleList &modules = GetTarget().GetImages();339SectionLoadList &load_list = GetTarget().GetSectionLoadList();340modules.ForEach([&](const ModuleSP &module_sp) {341SectionList *sections = module_sp->GetSectionList();342for (size_t i = 0; i < sections->GetSize(); ++i) {343SectionSP section_sp = sections->GetSectionAtIndex(i);344addr_t load_addr = load_list.GetSectionLoadAddress(section_sp);345if (load_addr == LLDB_INVALID_ADDRESS)346continue;347MemoryRegionInfo::RangeType section_range(load_addr,348section_sp->GetByteSize());349MemoryRegionInfo region =350MinidumpParser::GetMemoryRegionInfo(*m_memory_regions, load_addr);351if (region.GetMapped() != MemoryRegionInfo::eYes &&352region.GetRange().GetRangeBase() <= section_range.GetRangeBase() &&353section_range.GetRangeEnd() <= region.GetRange().GetRangeEnd()) {354to_add.emplace_back();355to_add.back().GetRange() = section_range;356to_add.back().SetLLDBPermissions(section_sp->GetPermissions());357to_add.back().SetMapped(MemoryRegionInfo::eYes);358to_add.back().SetName(module_sp->GetFileSpec().GetPath().c_str());359}360}361return true;362});363m_memory_regions->insert(m_memory_regions->end(), to_add.begin(),364to_add.end());365llvm::sort(*m_memory_regions);366}367368Status ProcessMinidump::DoGetMemoryRegionInfo(lldb::addr_t load_addr,369MemoryRegionInfo ®ion) {370BuildMemoryRegions();371region = MinidumpParser::GetMemoryRegionInfo(*m_memory_regions, load_addr);372return Status();373}374375Status ProcessMinidump::GetMemoryRegions(MemoryRegionInfos ®ion_list) {376BuildMemoryRegions();377region_list = *m_memory_regions;378return Status();379}380381void ProcessMinidump::Clear() { Process::m_thread_list.Clear(); }382383bool ProcessMinidump::DoUpdateThreadList(ThreadList &old_thread_list,384ThreadList &new_thread_list) {385for (const minidump::Thread &thread : m_thread_list) {386LocationDescriptor context_location = thread.Context;387388// If the minidump contains an exception context, use it389if (m_active_exception != nullptr &&390m_active_exception->ThreadId == thread.ThreadId) {391context_location = m_active_exception->ThreadContext;392}393394llvm::ArrayRef<uint8_t> context;395if (!m_is_wow64)396context = m_minidump_parser->GetThreadContext(context_location);397else398context = m_minidump_parser->GetThreadContextWow64(thread);399400lldb::ThreadSP thread_sp(new ThreadMinidump(*this, thread, context));401new_thread_list.AddThread(thread_sp);402}403return new_thread_list.GetSize(false) > 0;404}405406ModuleSP ProcessMinidump::GetOrCreateModule(UUID minidump_uuid,407llvm::StringRef name,408ModuleSpec module_spec) {409Log *log = GetLog(LLDBLog::DynamicLoader);410Status error;411412ModuleSP module_sp =413GetTarget().GetOrCreateModule(module_spec, true /* notify */, &error);414if (!module_sp)415return module_sp;416// We consider the module to be a match if the minidump UUID is a417// prefix of the actual UUID, or if either of the UUIDs are empty.418const auto dmp_bytes = minidump_uuid.GetBytes();419const auto mod_bytes = module_sp->GetUUID().GetBytes();420const bool match = dmp_bytes.empty() || mod_bytes.empty() ||421mod_bytes.take_front(dmp_bytes.size()) == dmp_bytes;422if (match) {423LLDB_LOG(log, "Partial uuid match for {0}.", name);424return module_sp;425}426427// Breakpad generates minindump files, and if there is no GNU build428// ID in the binary, it will calculate a UUID by hashing first 4096429// bytes of the .text section and using that as the UUID for a module430// in the minidump. Facebook uses a modified breakpad client that431// uses a slightly modified this hash to avoid collisions. Check for432// UUIDs from the minindump that match these cases and accept the433// module we find if they do match.434std::vector<uint8_t> breakpad_uuid;435std::vector<uint8_t> facebook_uuid;436HashElfTextSection(module_sp, breakpad_uuid, facebook_uuid);437if (dmp_bytes == llvm::ArrayRef<uint8_t>(breakpad_uuid)) {438LLDB_LOG(log, "Breakpad .text hash match for {0}.", name);439return module_sp;440}441if (dmp_bytes == llvm::ArrayRef<uint8_t>(facebook_uuid)) {442LLDB_LOG(log, "Facebook .text hash match for {0}.", name);443return module_sp;444}445// The UUID wasn't a partial match and didn't match the .text hash446// so remove the module from the target, we will need to create a447// placeholder object file.448GetTarget().GetImages().Remove(module_sp);449module_sp.reset();450return module_sp;451}452453void ProcessMinidump::ReadModuleList() {454std::vector<const minidump::Module *> filtered_modules =455m_minidump_parser->GetFilteredModuleList();456457Log *log = GetLog(LLDBLog::DynamicLoader);458459for (auto module : filtered_modules) {460std::string name = cantFail(m_minidump_parser->GetMinidumpFile().getString(461module->ModuleNameRVA));462const uint64_t load_addr = module->BaseOfImage;463const uint64_t load_size = module->SizeOfImage;464LLDB_LOG(log, "found module: name: {0} {1:x10}-{2:x10} size: {3}", name,465load_addr, load_addr + load_size, load_size);466467// check if the process is wow64 - a 32 bit windows process running on a468// 64 bit windows469if (llvm::StringRef(name).ends_with_insensitive("wow64.dll")) {470m_is_wow64 = true;471}472473const auto uuid = m_minidump_parser->GetModuleUUID(module);474auto file_spec = FileSpec(name, GetArchitecture().GetTriple());475ModuleSpec module_spec(file_spec, uuid);476module_spec.GetArchitecture() = GetArchitecture();477Status error;478// Try and find a module with a full UUID that matches. This function will479// add the module to the target if it finds one.480lldb::ModuleSP module_sp = GetTarget().GetOrCreateModule(module_spec,481true /* notify */, &error);482if (module_sp) {483LLDB_LOG(log, "Full uuid match for {0}.", name);484} else {485// We couldn't find a module with an exactly-matching UUID. Sometimes486// a minidump UUID is only a partial match or is a hash. So try again487// without specifying the UUID, then again without specifying the488// directory if that fails. This will allow us to find modules with489// partial matches or hash UUIDs in user-provided sysroots or search490// directories (target.exec-search-paths).491ModuleSpec partial_module_spec = module_spec;492partial_module_spec.GetUUID().Clear();493module_sp = GetOrCreateModule(uuid, name, partial_module_spec);494if (!module_sp) {495partial_module_spec.GetFileSpec().ClearDirectory();496module_sp = GetOrCreateModule(uuid, name, partial_module_spec);497}498}499if (module_sp) {500// Watch out for place holder modules that have different paths, but the501// same UUID. If the base address is different, create a new module. If502// we don't then we will end up setting the load address of a different503// ObjectFilePlaceholder and an assertion will fire.504auto *objfile = module_sp->GetObjectFile();505if (objfile &&506objfile->GetPluginName() ==507ObjectFilePlaceholder::GetPluginNameStatic()) {508if (((ObjectFilePlaceholder *)objfile)->GetBaseImageAddress() !=509load_addr)510module_sp.reset();511}512}513if (!module_sp) {514// We failed to locate a matching local object file. Fortunately, the515// minidump format encodes enough information about each module's memory516// range to allow us to create placeholder modules.517//518// This enables most LLDB functionality involving address-to-module519// translations (ex. identifing the module for a stack frame PC) and520// modules/sections commands (ex. target modules list, ...)521LLDB_LOG(log,522"Unable to locate the matching object file, creating a "523"placeholder module for: {0}",524name);525526module_sp = Module::CreateModuleFromObjectFile<ObjectFilePlaceholder>(527module_spec, load_addr, load_size);528GetTarget().GetImages().Append(module_sp, true /* notify */);529}530531bool load_addr_changed = false;532module_sp->SetLoadAddress(GetTarget(), load_addr, false,533load_addr_changed);534}535}536537bool ProcessMinidump::GetProcessInfo(ProcessInstanceInfo &info) {538info.Clear();539info.SetProcessID(GetID());540info.SetArchitecture(GetArchitecture());541lldb::ModuleSP module_sp = GetTarget().GetExecutableModule();542if (module_sp) {543const bool add_exe_file_as_first_arg = false;544info.SetExecutableFile(GetTarget().GetExecutableModule()->GetFileSpec(),545add_exe_file_as_first_arg);546}547return true;548}549550// For minidumps there's no runtime generated code so we don't need JITLoader(s)551// Avoiding them will also speed up minidump loading since JITLoaders normally552// try to set up symbolic breakpoints, which in turn may force loading more553// debug information than needed.554JITLoaderList &ProcessMinidump::GetJITLoaders() {555if (!m_jit_loaders_up) {556m_jit_loaders_up = std::make_unique<JITLoaderList>();557}558return *m_jit_loaders_up;559}560561#define INIT_BOOL(VAR, LONG, SHORT, DESC) \562VAR(LLDB_OPT_SET_1, false, LONG, SHORT, DESC, false, true)563#define APPEND_OPT(VAR) \564m_option_group.Append(&VAR, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1)565566class CommandObjectProcessMinidumpDump : public CommandObjectParsed {567private:568OptionGroupOptions m_option_group;569OptionGroupBoolean m_dump_all;570OptionGroupBoolean m_dump_directory;571OptionGroupBoolean m_dump_linux_cpuinfo;572OptionGroupBoolean m_dump_linux_proc_status;573OptionGroupBoolean m_dump_linux_lsb_release;574OptionGroupBoolean m_dump_linux_cmdline;575OptionGroupBoolean m_dump_linux_environ;576OptionGroupBoolean m_dump_linux_auxv;577OptionGroupBoolean m_dump_linux_maps;578OptionGroupBoolean m_dump_linux_proc_stat;579OptionGroupBoolean m_dump_linux_proc_uptime;580OptionGroupBoolean m_dump_linux_proc_fd;581OptionGroupBoolean m_dump_linux_all;582OptionGroupBoolean m_fb_app_data;583OptionGroupBoolean m_fb_build_id;584OptionGroupBoolean m_fb_version;585OptionGroupBoolean m_fb_java_stack;586OptionGroupBoolean m_fb_dalvik;587OptionGroupBoolean m_fb_unwind;588OptionGroupBoolean m_fb_error_log;589OptionGroupBoolean m_fb_app_state;590OptionGroupBoolean m_fb_abort;591OptionGroupBoolean m_fb_thread;592OptionGroupBoolean m_fb_logcat;593OptionGroupBoolean m_fb_all;594595void SetDefaultOptionsIfNoneAreSet() {596if (m_dump_all.GetOptionValue().GetCurrentValue() ||597m_dump_linux_all.GetOptionValue().GetCurrentValue() ||598m_fb_all.GetOptionValue().GetCurrentValue() ||599m_dump_directory.GetOptionValue().GetCurrentValue() ||600m_dump_linux_cpuinfo.GetOptionValue().GetCurrentValue() ||601m_dump_linux_proc_status.GetOptionValue().GetCurrentValue() ||602m_dump_linux_lsb_release.GetOptionValue().GetCurrentValue() ||603m_dump_linux_cmdline.GetOptionValue().GetCurrentValue() ||604m_dump_linux_environ.GetOptionValue().GetCurrentValue() ||605m_dump_linux_auxv.GetOptionValue().GetCurrentValue() ||606m_dump_linux_maps.GetOptionValue().GetCurrentValue() ||607m_dump_linux_proc_stat.GetOptionValue().GetCurrentValue() ||608m_dump_linux_proc_uptime.GetOptionValue().GetCurrentValue() ||609m_dump_linux_proc_fd.GetOptionValue().GetCurrentValue() ||610m_fb_app_data.GetOptionValue().GetCurrentValue() ||611m_fb_build_id.GetOptionValue().GetCurrentValue() ||612m_fb_version.GetOptionValue().GetCurrentValue() ||613m_fb_java_stack.GetOptionValue().GetCurrentValue() ||614m_fb_dalvik.GetOptionValue().GetCurrentValue() ||615m_fb_unwind.GetOptionValue().GetCurrentValue() ||616m_fb_error_log.GetOptionValue().GetCurrentValue() ||617m_fb_app_state.GetOptionValue().GetCurrentValue() ||618m_fb_abort.GetOptionValue().GetCurrentValue() ||619m_fb_thread.GetOptionValue().GetCurrentValue() ||620m_fb_logcat.GetOptionValue().GetCurrentValue())621return;622// If no options were set, then dump everything623m_dump_all.GetOptionValue().SetCurrentValue(true);624}625bool DumpAll() const {626return m_dump_all.GetOptionValue().GetCurrentValue();627}628bool DumpDirectory() const {629return DumpAll() ||630m_dump_directory.GetOptionValue().GetCurrentValue();631}632bool DumpLinux() const {633return DumpAll() || m_dump_linux_all.GetOptionValue().GetCurrentValue();634}635bool DumpLinuxCPUInfo() const {636return DumpLinux() ||637m_dump_linux_cpuinfo.GetOptionValue().GetCurrentValue();638}639bool DumpLinuxProcStatus() const {640return DumpLinux() ||641m_dump_linux_proc_status.GetOptionValue().GetCurrentValue();642}643bool DumpLinuxProcStat() const {644return DumpLinux() ||645m_dump_linux_proc_stat.GetOptionValue().GetCurrentValue();646}647bool DumpLinuxLSBRelease() const {648return DumpLinux() ||649m_dump_linux_lsb_release.GetOptionValue().GetCurrentValue();650}651bool DumpLinuxCMDLine() const {652return DumpLinux() ||653m_dump_linux_cmdline.GetOptionValue().GetCurrentValue();654}655bool DumpLinuxEnviron() const {656return DumpLinux() ||657m_dump_linux_environ.GetOptionValue().GetCurrentValue();658}659bool DumpLinuxAuxv() const {660return DumpLinux() ||661m_dump_linux_auxv.GetOptionValue().GetCurrentValue();662}663bool DumpLinuxMaps() const {664return DumpLinux() ||665m_dump_linux_maps.GetOptionValue().GetCurrentValue();666}667bool DumpLinuxProcUptime() const {668return DumpLinux() ||669m_dump_linux_proc_uptime.GetOptionValue().GetCurrentValue();670}671bool DumpLinuxProcFD() const {672return DumpLinux() ||673m_dump_linux_proc_fd.GetOptionValue().GetCurrentValue();674}675bool DumpFacebook() const {676return DumpAll() || m_fb_all.GetOptionValue().GetCurrentValue();677}678bool DumpFacebookAppData() const {679return DumpFacebook() || m_fb_app_data.GetOptionValue().GetCurrentValue();680}681bool DumpFacebookBuildID() const {682return DumpFacebook() || m_fb_build_id.GetOptionValue().GetCurrentValue();683}684bool DumpFacebookVersionName() const {685return DumpFacebook() || m_fb_version.GetOptionValue().GetCurrentValue();686}687bool DumpFacebookJavaStack() const {688return DumpFacebook() || m_fb_java_stack.GetOptionValue().GetCurrentValue();689}690bool DumpFacebookDalvikInfo() const {691return DumpFacebook() || m_fb_dalvik.GetOptionValue().GetCurrentValue();692}693bool DumpFacebookUnwindSymbols() const {694return DumpFacebook() || m_fb_unwind.GetOptionValue().GetCurrentValue();695}696bool DumpFacebookErrorLog() const {697return DumpFacebook() || m_fb_error_log.GetOptionValue().GetCurrentValue();698}699bool DumpFacebookAppStateLog() const {700return DumpFacebook() || m_fb_app_state.GetOptionValue().GetCurrentValue();701}702bool DumpFacebookAbortReason() const {703return DumpFacebook() || m_fb_abort.GetOptionValue().GetCurrentValue();704}705bool DumpFacebookThreadName() const {706return DumpFacebook() || m_fb_thread.GetOptionValue().GetCurrentValue();707}708bool DumpFacebookLogcat() const {709return DumpFacebook() || m_fb_logcat.GetOptionValue().GetCurrentValue();710}711public:712CommandObjectProcessMinidumpDump(CommandInterpreter &interpreter)713: CommandObjectParsed(interpreter, "process plugin dump",714"Dump information from the minidump file.", nullptr),715m_option_group(),716INIT_BOOL(m_dump_all, "all", 'a',717"Dump the everything in the minidump."),718INIT_BOOL(m_dump_directory, "directory", 'd',719"Dump the minidump directory map."),720INIT_BOOL(m_dump_linux_cpuinfo, "cpuinfo", 'C',721"Dump linux /proc/cpuinfo."),722INIT_BOOL(m_dump_linux_proc_status, "status", 's',723"Dump linux /proc/<pid>/status."),724INIT_BOOL(m_dump_linux_lsb_release, "lsb-release", 'r',725"Dump linux /etc/lsb-release."),726INIT_BOOL(m_dump_linux_cmdline, "cmdline", 'c',727"Dump linux /proc/<pid>/cmdline."),728INIT_BOOL(m_dump_linux_environ, "environ", 'e',729"Dump linux /proc/<pid>/environ."),730INIT_BOOL(m_dump_linux_auxv, "auxv", 'x',731"Dump linux /proc/<pid>/auxv."),732INIT_BOOL(m_dump_linux_maps, "maps", 'm',733"Dump linux /proc/<pid>/maps."),734INIT_BOOL(m_dump_linux_proc_stat, "stat", 'S',735"Dump linux /proc/<pid>/stat."),736INIT_BOOL(m_dump_linux_proc_uptime, "uptime", 'u',737"Dump linux process uptime."),738INIT_BOOL(m_dump_linux_proc_fd, "fd", 'f',739"Dump linux /proc/<pid>/fd."),740INIT_BOOL(m_dump_linux_all, "linux", 'l',741"Dump all linux streams."),742INIT_BOOL(m_fb_app_data, "fb-app-data", 1,743"Dump Facebook application custom data."),744INIT_BOOL(m_fb_build_id, "fb-build-id", 2,745"Dump the Facebook build ID."),746INIT_BOOL(m_fb_version, "fb-version", 3,747"Dump Facebook application version string."),748INIT_BOOL(m_fb_java_stack, "fb-java-stack", 4,749"Dump Facebook java stack."),750INIT_BOOL(m_fb_dalvik, "fb-dalvik-info", 5,751"Dump Facebook Dalvik info."),752INIT_BOOL(m_fb_unwind, "fb-unwind-symbols", 6,753"Dump Facebook unwind symbols."),754INIT_BOOL(m_fb_error_log, "fb-error-log", 7,755"Dump Facebook error log."),756INIT_BOOL(m_fb_app_state, "fb-app-state-log", 8,757"Dump Facebook java stack."),758INIT_BOOL(m_fb_abort, "fb-abort-reason", 9,759"Dump Facebook abort reason."),760INIT_BOOL(m_fb_thread, "fb-thread-name", 10,761"Dump Facebook thread name."),762INIT_BOOL(m_fb_logcat, "fb-logcat", 11,763"Dump Facebook logcat."),764INIT_BOOL(m_fb_all, "facebook", 12, "Dump all Facebook streams.") {765APPEND_OPT(m_dump_all);766APPEND_OPT(m_dump_directory);767APPEND_OPT(m_dump_linux_cpuinfo);768APPEND_OPT(m_dump_linux_proc_status);769APPEND_OPT(m_dump_linux_lsb_release);770APPEND_OPT(m_dump_linux_cmdline);771APPEND_OPT(m_dump_linux_environ);772APPEND_OPT(m_dump_linux_auxv);773APPEND_OPT(m_dump_linux_maps);774APPEND_OPT(m_dump_linux_proc_stat);775APPEND_OPT(m_dump_linux_proc_uptime);776APPEND_OPT(m_dump_linux_proc_fd);777APPEND_OPT(m_dump_linux_all);778APPEND_OPT(m_fb_app_data);779APPEND_OPT(m_fb_build_id);780APPEND_OPT(m_fb_version);781APPEND_OPT(m_fb_java_stack);782APPEND_OPT(m_fb_dalvik);783APPEND_OPT(m_fb_unwind);784APPEND_OPT(m_fb_error_log);785APPEND_OPT(m_fb_app_state);786APPEND_OPT(m_fb_abort);787APPEND_OPT(m_fb_thread);788APPEND_OPT(m_fb_logcat);789APPEND_OPT(m_fb_all);790m_option_group.Finalize();791}792793~CommandObjectProcessMinidumpDump() override = default;794795Options *GetOptions() override { return &m_option_group; }796797void DoExecute(Args &command, CommandReturnObject &result) override {798const size_t argc = command.GetArgumentCount();799if (argc > 0) {800result.AppendErrorWithFormat("'%s' take no arguments, only options",801m_cmd_name.c_str());802return;803}804SetDefaultOptionsIfNoneAreSet();805806ProcessMinidump *process = static_cast<ProcessMinidump *>(807m_interpreter.GetExecutionContext().GetProcessPtr());808result.SetStatus(eReturnStatusSuccessFinishResult);809Stream &s = result.GetOutputStream();810MinidumpParser &minidump = *process->m_minidump_parser;811if (DumpDirectory()) {812s.Printf("RVA SIZE TYPE StreamType\n");813s.Printf("---------- ---------- ---------- --------------------------\n");814for (const auto &stream_desc : minidump.GetMinidumpFile().streams())815s.Printf(816"0x%8.8x 0x%8.8x 0x%8.8x %s\n", (uint32_t)stream_desc.Location.RVA,817(uint32_t)stream_desc.Location.DataSize,818(unsigned)(StreamType)stream_desc.Type,819MinidumpParser::GetStreamTypeAsString(stream_desc.Type).data());820s.Printf("\n");821}822auto DumpTextStream = [&](StreamType stream_type,823llvm::StringRef label) -> void {824auto bytes = minidump.GetStream(stream_type);825if (!bytes.empty()) {826if (label.empty())827label = MinidumpParser::GetStreamTypeAsString(stream_type);828s.Printf("%s:\n%s\n\n", label.data(), bytes.data());829}830};831auto DumpBinaryStream = [&](StreamType stream_type,832llvm::StringRef label) -> void {833auto bytes = minidump.GetStream(stream_type);834if (!bytes.empty()) {835if (label.empty())836label = MinidumpParser::GetStreamTypeAsString(stream_type);837s.Printf("%s:\n", label.data());838DataExtractor data(bytes.data(), bytes.size(), eByteOrderLittle,839process->GetAddressByteSize());840DumpDataExtractor(data, &s, 0, lldb::eFormatBytesWithASCII, 1,841bytes.size(), 16, 0, 0, 0);842s.Printf("\n\n");843}844};845846if (DumpLinuxCPUInfo())847DumpTextStream(StreamType::LinuxCPUInfo, "/proc/cpuinfo");848if (DumpLinuxProcStatus())849DumpTextStream(StreamType::LinuxProcStatus, "/proc/PID/status");850if (DumpLinuxLSBRelease())851DumpTextStream(StreamType::LinuxLSBRelease, "/etc/lsb-release");852if (DumpLinuxCMDLine())853DumpTextStream(StreamType::LinuxCMDLine, "/proc/PID/cmdline");854if (DumpLinuxEnviron())855DumpTextStream(StreamType::LinuxEnviron, "/proc/PID/environ");856if (DumpLinuxAuxv())857DumpBinaryStream(StreamType::LinuxAuxv, "/proc/PID/auxv");858if (DumpLinuxMaps())859DumpTextStream(StreamType::LinuxMaps, "/proc/PID/maps");860if (DumpLinuxProcStat())861DumpTextStream(StreamType::LinuxProcStat, "/proc/PID/stat");862if (DumpLinuxProcUptime())863DumpTextStream(StreamType::LinuxProcUptime, "uptime");864if (DumpLinuxProcFD())865DumpTextStream(StreamType::LinuxProcFD, "/proc/PID/fd");866if (DumpFacebookAppData())867DumpTextStream(StreamType::FacebookAppCustomData,868"Facebook App Data");869if (DumpFacebookBuildID()) {870auto bytes = minidump.GetStream(StreamType::FacebookBuildID);871if (bytes.size() >= 4) {872DataExtractor data(bytes.data(), bytes.size(), eByteOrderLittle,873process->GetAddressByteSize());874lldb::offset_t offset = 0;875uint32_t build_id = data.GetU32(&offset);876s.Printf("Facebook Build ID:\n");877s.Printf("%u\n", build_id);878s.Printf("\n");879}880}881if (DumpFacebookVersionName())882DumpTextStream(StreamType::FacebookAppVersionName,883"Facebook Version String");884if (DumpFacebookJavaStack())885DumpTextStream(StreamType::FacebookJavaStack,886"Facebook Java Stack");887if (DumpFacebookDalvikInfo())888DumpTextStream(StreamType::FacebookDalvikInfo,889"Facebook Dalvik Info");890if (DumpFacebookUnwindSymbols())891DumpBinaryStream(StreamType::FacebookUnwindSymbols,892"Facebook Unwind Symbols Bytes");893if (DumpFacebookErrorLog())894DumpTextStream(StreamType::FacebookDumpErrorLog,895"Facebook Error Log");896if (DumpFacebookAppStateLog())897DumpTextStream(StreamType::FacebookAppStateLog,898"Faceook Application State Log");899if (DumpFacebookAbortReason())900DumpTextStream(StreamType::FacebookAbortReason,901"Facebook Abort Reason");902if (DumpFacebookThreadName())903DumpTextStream(StreamType::FacebookThreadName,904"Facebook Thread Name");905if (DumpFacebookLogcat())906DumpTextStream(StreamType::FacebookLogcat, "Facebook Logcat");907}908};909910class CommandObjectMultiwordProcessMinidump : public CommandObjectMultiword {911public:912CommandObjectMultiwordProcessMinidump(CommandInterpreter &interpreter)913: CommandObjectMultiword(interpreter, "process plugin",914"Commands for operating on a ProcessMinidump process.",915"process plugin <subcommand> [<subcommand-options>]") {916LoadSubCommand("dump",917CommandObjectSP(new CommandObjectProcessMinidumpDump(interpreter)));918}919920~CommandObjectMultiwordProcessMinidump() override = default;921};922923CommandObject *ProcessMinidump::GetPluginCommandObject() {924if (!m_command_sp)925m_command_sp = std::make_shared<CommandObjectMultiwordProcessMinidump>(926GetTarget().GetDebugger().GetCommandInterpreter());927return m_command_sp.get();928}929930931