Add "dump" command as a custom "process plugin" subcommand when ProcessMinidump is used.

Each process plug-in can create its own custom commands. I figured it would be nice to be able to dump things from the minidump file from the lldb command line, so I added the start of the some custom commands.

Currently you can dump:

minidump stream directory
all linux specifc streams, most of which are strings
each linux stream individually if desired, or all with --linux
The idea is we can expand the command set to dump more things, search for data in the core file, and much more. This patch gets us started.

Differential Revision: https://reviews.llvm.org/D55727

llvm-svn: 349429
This commit is contained in:
Greg Clayton 2018-12-18 00:50:11 +00:00
parent 44ea4f5744
commit 48a28c1665
8 changed files with 386 additions and 1 deletions

Binary file not shown.

View File

@ -0,0 +1,86 @@
# RUN: %lldb -c %p/Inputs/dump-content.dmp -o 'process plugin dump --all' | \
# RUN: FileCheck --check-prefix=CHECKDIR --check-prefix=CHECKCPU \
# RUN: --check-prefix=CHECKSTATUS --check-prefix=CHECKLSB \
# RUN: --check-prefix=CHECKCMD --check-prefix=CHECKENV \
# RUN: --check-prefix=CHECKAUX --check-prefix=CHECKMAP \
# RUN: --check-prefix=CHECKSTAT --check-prefix=CHECKUP --check-prefix=CHECKFD %s
# RUN: %lldb -c %p/Inputs/dump-content.dmp -o 'process plugin dump -a' | \
# RUN: FileCheck --check-prefix=CHECKDIR --check-prefix=CHECKCPU \
# RUN: --check-prefix=CHECKSTATUS --check-prefix=CHECKLSB \
# RUN: --check-prefix=CHECKCMD --check-prefix=CHECKENV \
# RUN: --check-prefix=CHECKAUX --check-prefix=CHECKMAP \
# RUN: --check-prefix=CHECKSTAT --check-prefix=CHECKUP --check-prefix=CHECKFD %s
# RUN: %lldb -c %p/Inputs/dump-content.dmp -o 'process plugin dump --directory' | FileCheck --check-prefix=CHECKDIR %s
# RUN: %lldb -c %p/Inputs/dump-content.dmp -o 'process plugin dump --d' | FileCheck --check-prefix=CHECKDIR %s
# RUN: %lldb -c %p/Inputs/dump-content.dmp -o 'process plugin dump --linux' | \
# RUN: FileCheck --check-prefix=CHECKCPU --check-prefix=CHECKSTATUS \
# RUN: --check-prefix=CHECKLSB --check-prefix=CHECKCMD --check-prefix=CHECKENV \
# RUN: --check-prefix=CHECKAUX --check-prefix=CHECKMAP --check-prefix=CHECKSTAT \
# RUN: --check-prefix=CHECKUP --check-prefix=CHECKFD %s
# RUN: %lldb -c %p/Inputs/dump-content.dmp -o 'process plugin dump --cpuinfo' | FileCheck --check-prefix=CHECKCPU %s
# RUN: %lldb -c %p/Inputs/dump-content.dmp -o 'process plugin dump --C' | FileCheck --check-prefix=CHECKCPU %s
# RUN: %lldb -c %p/Inputs/dump-content.dmp -o 'process plugin dump --status' | FileCheck --check-prefix=CHECKSTATUS %s
# RUN: %lldb -c %p/Inputs/dump-content.dmp -o 'process plugin dump --s' | FileCheck --check-prefix=CHECKSTATUS %s
# RUN: %lldb -c %p/Inputs/dump-content.dmp -o 'process plugin dump --lsb-release' | FileCheck --check-prefix=CHECKLSB %s
# RUN: %lldb -c %p/Inputs/dump-content.dmp -o 'process plugin dump --r' | FileCheck --check-prefix=CHECKLSB %s
# RUN: %lldb -c %p/Inputs/dump-content.dmp -o 'process plugin dump --cmdline' | FileCheck --check-prefix=CHECKCMD %s
# RUN: %lldb -c %p/Inputs/dump-content.dmp -o 'process plugin dump --c' | FileCheck --check-prefix=CHECKCMD %s
# RUN: %lldb -c %p/Inputs/dump-content.dmp -o 'process plugin dump --environ' | FileCheck --check-prefix=CHECKENV %s
# RUN: %lldb -c %p/Inputs/dump-content.dmp -o 'process plugin dump --e' | FileCheck --check-prefix=CHECKENV %s
# RUN: %lldb -c %p/Inputs/dump-content.dmp -o 'process plugin dump --auxv' | FileCheck --check-prefix=CHECKAUX %s
# RUN: %lldb -c %p/Inputs/dump-content.dmp -o 'process plugin dump --x' | FileCheck --check-prefix=CHECKAUX %s
# RUN: %lldb -c %p/Inputs/dump-content.dmp -o 'process plugin dump --maps' | FileCheck --check-prefix=CHECKMAP %s
# RUN: %lldb -c %p/Inputs/dump-content.dmp -o 'process plugin dump --m' | FileCheck --check-prefix=CHECKMAP %s
# RUN: %lldb -c %p/Inputs/dump-content.dmp -o 'process plugin dump --stat' | FileCheck --check-prefix=CHECKSTAT %s
# RUN: %lldb -c %p/Inputs/dump-content.dmp -o 'process plugin dump --S' | FileCheck --check-prefix=CHECKSTAT %s
# RUN: %lldb -c %p/Inputs/dump-content.dmp -o 'process plugin dump --uptime' | FileCheck --check-prefix=CHECKUP %s
# RUN: %lldb -c %p/Inputs/dump-content.dmp -o 'process plugin dump --u' | FileCheck --check-prefix=CHECKUP %s
# RUN: %lldb -c %p/Inputs/dump-content.dmp -o 'process plugin dump --fd' | FileCheck --check-prefix=CHECKFD %s
# RUN: %lldb -c %p/Inputs/dump-content.dmp -o 'process plugin dump --f' | FileCheck --check-prefix=CHECKFD %s
# CHECKDIR: RVA SIZE TYPE MinidumpStreamType
# CHECKDIR-NEXT: ---------- ---------- ---------- --------------------------
# CHECKDIR-NEXT: 0x000000b0 0x00000038 0x00000007 SystemInfo
# CHECKDIR-NEXT: 0x0000015d 0x0000001b 0x47670007 LinuxEnviron
# CHECKDIR-NEXT: 0x00000190 0x000000bc 0x47670009 LinuxMaps
# CHECKDIR-NEXT: 0x00000110 0x0000001a 0x47670004 LinuxProcStatus
# CHECKDIR-NEXT: 0x0000024c 0x00000018 0x4767000b LinuxProcStat
# CHECKDIR-NEXT: 0x00000142 0x0000001b 0x47670006 LinuxCMDLine
# CHECKDIR-NEXT: 0x00000272 0x00000016 0x4767000d LinuxProcFD
# CHECKDIR-NEXT: 0x00000178 0x00000018 0x47670008 LinuxAuxv
# CHECKDIR-NEXT: 0x000000e8 0x00000018 0x0000000f MiscInfo
# CHECKDIR-NEXT: 0x00000100 0x00000010 0x47670003 LinuxCPUInfo
# CHECKDIR-NEXT: 0x0000012a 0x00000018 0x47670005 LinuxLSBRelease
# CHECKDIR-NEXT: 0x00000264 0x0000000e 0x4767000c LinuxProcUptime
# CHECKCPU: /proc/cpuinfo:
# CHECKCPU-NEXT: cpu info output
# CHECKSTATUS: /proc/PID/status:
# CHECKSTATUS-NEXT: /proc/<pid>/status output
# CHECKLSB: /etc/lsb-release:
# CHECKLSB-NEXT: /etc/lsb-release output
# CHECKCMD: /proc/PID/cmdline:
# CHECKCMD-NEXT: /proc/<pid>/cmdline output
# CHECKENV: /proc/PID/environ:
# CHECKENV-NEXT: /proc/<pid>/environ output
# CHECKAUX: /proc/PID/auxv:
# CHECKAUX-NEXT: 0x00000000: 2f 70 72 6f 63 2f 3c 70 69 64 3e 2f 61 75 78 76 /proc/<pid>/auxv
# CHECKAUX-NEXT: 0x00000010: 20 6f 75 74 70 75 74 00 output.
# CHECKMAP: /proc/PID/maps:
# CHECKMAP-NEXT: 400d9000-400db000 r-xp 00000000 b3:04 227 /system/bin/app_process
# CHECKMAP-NEXT: 400db000-400dc000 r--p 00001000 b3:04 227 /system/bin/app_process
# CHECKMAP-NEXT: 400dc000-400dd000 rw-p 00000000 00:00 0
# CHECKSTAT: /proc/PID/stat:
# CHECKSTAT-NEXT: /proc/<pid>/stat output
# CHECKUP: uptime:
# CHECKUP-NEXT: uptime output
# CHECKFD: /proc/PID/fd:
# CHECKFD-NEXT: /proc/<pid>/fd output

View File

@ -660,3 +660,48 @@ Status MinidumpParser::Initialize() {
return error;
}
#define ENUM_TO_CSTR(ST) case (uint32_t)MinidumpStreamType::ST: return #ST
llvm::StringRef
MinidumpParser::GetStreamTypeAsString(uint32_t stream_type) {
switch (stream_type) {
ENUM_TO_CSTR(Unused);
ENUM_TO_CSTR(Reserved0);
ENUM_TO_CSTR(Reserved1);
ENUM_TO_CSTR(ThreadList);
ENUM_TO_CSTR(ModuleList);
ENUM_TO_CSTR(MemoryList);
ENUM_TO_CSTR(Exception);
ENUM_TO_CSTR(SystemInfo);
ENUM_TO_CSTR(ThreadExList);
ENUM_TO_CSTR(Memory64List);
ENUM_TO_CSTR(CommentA);
ENUM_TO_CSTR(CommentW);
ENUM_TO_CSTR(HandleData);
ENUM_TO_CSTR(FunctionTable);
ENUM_TO_CSTR(UnloadedModuleList);
ENUM_TO_CSTR(MiscInfo);
ENUM_TO_CSTR(MemoryInfoList);
ENUM_TO_CSTR(ThreadInfoList);
ENUM_TO_CSTR(HandleOperationList);
ENUM_TO_CSTR(Token);
ENUM_TO_CSTR(JavascriptData);
ENUM_TO_CSTR(SystemMemoryInfo);
ENUM_TO_CSTR(ProcessVMCounters);
ENUM_TO_CSTR(BreakpadInfo);
ENUM_TO_CSTR(AssertionInfo);
ENUM_TO_CSTR(LinuxCPUInfo);
ENUM_TO_CSTR(LinuxProcStatus);
ENUM_TO_CSTR(LinuxLSBRelease);
ENUM_TO_CSTR(LinuxCMDLine);
ENUM_TO_CSTR(LinuxEnviron);
ENUM_TO_CSTR(LinuxAuxv);
ENUM_TO_CSTR(LinuxMaps);
ENUM_TO_CSTR(LinuxDSODebug);
ENUM_TO_CSTR(LinuxProcStat);
ENUM_TO_CSTR(LinuxProcUptime);
ENUM_TO_CSTR(LinuxProcFD);
}
return "unknown stream type";
}

View File

@ -90,6 +90,13 @@ public:
// Perform consistency checks and initialize internal data structures
Status Initialize();
static llvm::StringRef GetStreamTypeAsString(uint32_t stream_type);
const llvm::DenseMap<uint32_t, MinidumpLocationDescriptor> &
GetDirectoryMap() const {
return m_directory_map;
}
private:
MinidumpParser(const lldb::DataBufferSP &data_buf_sp);

View File

@ -96,7 +96,10 @@ enum class MinidumpStreamType : uint32_t {
LinuxEnviron = 0x47670007, /* /proc/$x/environ */
LinuxAuxv = 0x47670008, /* /proc/$x/auxv */
LinuxMaps = 0x47670009, /* /proc/$x/maps */
LinuxDSODebug = 0x4767000A
LinuxDSODebug = 0x4767000A,
LinuxProcStat = 0x4767000B, /* /proc/$x/stat */
LinuxProcUptime = 0x4767000C, /* uptime */
LinuxProcFD = 0x4767000D, /* /proc/$x/fb */
};
// for MinidumpSystemInfo.processor_arch

View File

@ -10,10 +10,17 @@
#include "ProcessMinidump.h"
#include "ThreadMinidump.h"
#include "lldb/Core/DumpDataExtractor.h"
#include "lldb/Core/Module.h"
#include "lldb/Core/ModuleSpec.h"
#include "lldb/Core/PluginManager.h"
#include "lldb/Core/Section.h"
#include "lldb/Interpreter/CommandInterpreter.h"
#include "lldb/Interpreter/CommandObject.h"
#include "lldb/Interpreter/CommandObjectMultiword.h"
#include "lldb/Interpreter/CommandReturnObject.h"
#include "lldb/Interpreter/OptionArgParser.h"
#include "lldb/Interpreter/OptionGroupBoolean.h"
#include "lldb/Target/JITLoaderList.h"
#include "lldb/Target/MemoryRegionInfo.h"
#include "lldb/Target/SectionLoadList.h"
@ -398,3 +405,237 @@ JITLoaderList &ProcessMinidump::GetJITLoaders() {
}
return *m_jit_loaders_ap;
}
#define INIT_BOOL(VAR, LONG, SHORT, DESC) \
VAR(LLDB_OPT_SET_1, false, LONG, SHORT, DESC, false, true)
#define APPEND_OPT(VAR) \
m_option_group.Append(&VAR, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1)
class CommandObjectProcessMinidumpDump : public CommandObjectParsed {
private:
OptionGroupOptions m_option_group;
OptionGroupBoolean m_dump_all;
OptionGroupBoolean m_dump_directory;
OptionGroupBoolean m_dump_linux_cpuinfo;
OptionGroupBoolean m_dump_linux_proc_status;
OptionGroupBoolean m_dump_linux_lsb_release;
OptionGroupBoolean m_dump_linux_cmdline;
OptionGroupBoolean m_dump_linux_environ;
OptionGroupBoolean m_dump_linux_auxv;
OptionGroupBoolean m_dump_linux_maps;
OptionGroupBoolean m_dump_linux_proc_stat;
OptionGroupBoolean m_dump_linux_proc_uptime;
OptionGroupBoolean m_dump_linux_proc_fd;
OptionGroupBoolean m_dump_linux_all;
void SetDefaultOptionsIfNoneAreSet() {
if (m_dump_all.GetOptionValue().GetCurrentValue() ||
m_dump_linux_all.GetOptionValue().GetCurrentValue() ||
m_dump_directory.GetOptionValue().GetCurrentValue() ||
m_dump_linux_cpuinfo.GetOptionValue().GetCurrentValue() ||
m_dump_linux_proc_status.GetOptionValue().GetCurrentValue() ||
m_dump_linux_lsb_release.GetOptionValue().GetCurrentValue() ||
m_dump_linux_cmdline.GetOptionValue().GetCurrentValue() ||
m_dump_linux_environ.GetOptionValue().GetCurrentValue() ||
m_dump_linux_auxv.GetOptionValue().GetCurrentValue() ||
m_dump_linux_maps.GetOptionValue().GetCurrentValue() ||
m_dump_linux_proc_stat.GetOptionValue().GetCurrentValue() ||
m_dump_linux_proc_uptime.GetOptionValue().GetCurrentValue() ||
m_dump_linux_proc_fd.GetOptionValue().GetCurrentValue())
return;
// If no options were set, then dump everything
m_dump_all.GetOptionValue().SetCurrentValue(true);
}
bool DumpAll() const {
return m_dump_all.GetOptionValue().GetCurrentValue();
}
bool DumpDirectory() const {
return DumpAll() ||
m_dump_directory.GetOptionValue().GetCurrentValue();
}
bool DumpLinux() const {
return DumpAll() || m_dump_linux_all.GetOptionValue().GetCurrentValue();
}
bool DumpLinuxCPUInfo() const {
return DumpLinux() ||
m_dump_linux_cpuinfo.GetOptionValue().GetCurrentValue();
}
bool DumpLinuxProcStatus() const {
return DumpLinux() ||
m_dump_linux_proc_status.GetOptionValue().GetCurrentValue();
}
bool DumpLinuxProcStat() const {
return DumpLinux() ||
m_dump_linux_proc_stat.GetOptionValue().GetCurrentValue();
}
bool DumpLinuxLSBRelease() const {
return DumpLinux() ||
m_dump_linux_lsb_release.GetOptionValue().GetCurrentValue();
}
bool DumpLinuxCMDLine() const {
return DumpLinux() ||
m_dump_linux_cmdline.GetOptionValue().GetCurrentValue();
}
bool DumpLinuxEnviron() const {
return DumpLinux() ||
m_dump_linux_environ.GetOptionValue().GetCurrentValue();
}
bool DumpLinuxAuxv() const {
return DumpLinux() ||
m_dump_linux_auxv.GetOptionValue().GetCurrentValue();
}
bool DumpLinuxMaps() const {
return DumpLinux() ||
m_dump_linux_maps.GetOptionValue().GetCurrentValue();
}
bool DumpLinuxProcUptime() const {
return DumpLinux() ||
m_dump_linux_proc_uptime.GetOptionValue().GetCurrentValue();
}
bool DumpLinuxProcFD() const {
return DumpLinux() ||
m_dump_linux_proc_fd.GetOptionValue().GetCurrentValue();
}
public:
CommandObjectProcessMinidumpDump(CommandInterpreter &interpreter)
: CommandObjectParsed(interpreter, "process plugin dump",
"Dump information from the minidump file.", NULL),
m_option_group(),
INIT_BOOL(m_dump_all, "all", 'a',
"Dump the everything in the minidump."),
INIT_BOOL(m_dump_directory, "directory", 'd',
"Dump the minidump directory map."),
INIT_BOOL(m_dump_linux_cpuinfo, "cpuinfo", 'C',
"Dump linux /proc/cpuinfo."),
INIT_BOOL(m_dump_linux_proc_status, "status", 's',
"Dump linux /proc/<pid>/status."),
INIT_BOOL(m_dump_linux_lsb_release, "lsb-release", 'r',
"Dump linux /etc/lsb-release."),
INIT_BOOL(m_dump_linux_cmdline, "cmdline", 'c',
"Dump linux /proc/<pid>/cmdline."),
INIT_BOOL(m_dump_linux_environ, "environ", 'e',
"Dump linux /proc/<pid>/environ."),
INIT_BOOL(m_dump_linux_auxv, "auxv", 'x',
"Dump linux /proc/<pid>/auxv."),
INIT_BOOL(m_dump_linux_maps, "maps", 'm',
"Dump linux /proc/<pid>/maps."),
INIT_BOOL(m_dump_linux_proc_stat, "stat", 'S',
"Dump linux /proc/<pid>/stat."),
INIT_BOOL(m_dump_linux_proc_uptime, "uptime", 'u',
"Dump linux process uptime."),
INIT_BOOL(m_dump_linux_proc_fd, "fd", 'f',
"Dump linux /proc/<pid>/fd."),
INIT_BOOL(m_dump_linux_all, "linux", 'l',
"Dump all linux streams.") {
APPEND_OPT(m_dump_all);
APPEND_OPT(m_dump_directory);
APPEND_OPT(m_dump_linux_cpuinfo);
APPEND_OPT(m_dump_linux_proc_status);
APPEND_OPT(m_dump_linux_lsb_release);
APPEND_OPT(m_dump_linux_cmdline);
APPEND_OPT(m_dump_linux_environ);
APPEND_OPT(m_dump_linux_auxv);
APPEND_OPT(m_dump_linux_maps);
APPEND_OPT(m_dump_linux_proc_stat);
APPEND_OPT(m_dump_linux_proc_uptime);
APPEND_OPT(m_dump_linux_proc_fd);
APPEND_OPT(m_dump_linux_all);
m_option_group.Finalize();
}
~CommandObjectProcessMinidumpDump() {}
Options *GetOptions() override { return &m_option_group; }
bool DoExecute(Args &command, CommandReturnObject &result) override {
const size_t argc = command.GetArgumentCount();
if (argc > 0) {
result.AppendErrorWithFormat("'%s' take no arguments, only options",
m_cmd_name.c_str());
result.SetStatus(eReturnStatusFailed);
return false;
}
SetDefaultOptionsIfNoneAreSet();
ProcessMinidump *process = static_cast<ProcessMinidump *>(
m_interpreter.GetExecutionContext().GetProcessPtr());
result.SetStatus(eReturnStatusSuccessFinishResult);
Stream &s = result.GetOutputStream();
MinidumpParser &minidump = process->m_minidump_parser;
if (DumpDirectory()) {
s.Printf("RVA SIZE TYPE MinidumpStreamType\n");
s.Printf("---------- ---------- ---------- --------------------------\n");
for (const auto &pair: minidump.GetDirectoryMap())
s.Printf("0x%8.8x 0x%8.8x 0x%8.8x %s\n", (uint32_t)pair.second.rva,
(uint32_t)pair.second.data_size, pair.first,
MinidumpParser::GetStreamTypeAsString(pair.first).data());
s.Printf("\n");
}
auto DumpTextStream = [&](MinidumpStreamType stream_type,
llvm::StringRef label = llvm::StringRef()) -> void {
auto bytes = minidump.GetStream(stream_type);
if (!bytes.empty()) {
if (label.empty())
label = MinidumpParser::GetStreamTypeAsString((uint32_t)stream_type);
s.Printf("%s:\n%s\n\n", label.data(), bytes.data());
}
};
auto DumpBinaryStream = [&](MinidumpStreamType stream_type,
llvm::StringRef label = llvm::StringRef()) -> void {
auto bytes = minidump.GetStream(stream_type);
if (!bytes.empty()) {
if (label.empty())
label = MinidumpParser::GetStreamTypeAsString((uint32_t)stream_type);
s.Printf("%s:\n", label.data());
DataExtractor data(bytes.data(), bytes.size(), eByteOrderLittle,
process->GetAddressByteSize());
DumpDataExtractor(data, &s, 0, lldb::eFormatBytesWithASCII, 1,
bytes.size(), 16, 0, 0, 0);
s.Printf("\n\n");
}
};
if (DumpLinuxCPUInfo())
DumpTextStream(MinidumpStreamType::LinuxCPUInfo, "/proc/cpuinfo");
if (DumpLinuxProcStatus())
DumpTextStream(MinidumpStreamType::LinuxProcStatus, "/proc/PID/status");
if (DumpLinuxLSBRelease())
DumpTextStream(MinidumpStreamType::LinuxLSBRelease, "/etc/lsb-release");
if (DumpLinuxCMDLine())
DumpTextStream(MinidumpStreamType::LinuxCMDLine, "/proc/PID/cmdline");
if (DumpLinuxEnviron())
DumpTextStream(MinidumpStreamType::LinuxEnviron, "/proc/PID/environ");
if (DumpLinuxAuxv())
DumpBinaryStream(MinidumpStreamType::LinuxAuxv, "/proc/PID/auxv");
if (DumpLinuxMaps())
DumpTextStream(MinidumpStreamType::LinuxMaps, "/proc/PID/maps");
if (DumpLinuxProcStat())
DumpTextStream(MinidumpStreamType::LinuxProcStat, "/proc/PID/stat");
if (DumpLinuxProcUptime())
DumpTextStream(MinidumpStreamType::LinuxProcUptime, "uptime");
if (DumpLinuxProcFD())
DumpTextStream(MinidumpStreamType::LinuxProcFD, "/proc/PID/fd");
return true;
}
};
class CommandObjectMultiwordProcessMinidump : public CommandObjectMultiword {
public:
CommandObjectMultiwordProcessMinidump(CommandInterpreter &interpreter)
: CommandObjectMultiword(interpreter, "process plugin",
"Commands for operating on a ProcessMinidump process.",
"process plugin <subcommand> [<subcommand-options>]") {
LoadSubCommand("dump",
CommandObjectSP(new CommandObjectProcessMinidumpDump(interpreter)));
}
~CommandObjectMultiwordProcessMinidump() {}
};
CommandObject *ProcessMinidump::GetPluginCommandObject() {
if (!m_command_sp)
m_command_sp.reset(new CommandObjectMultiwordProcessMinidump(
GetTarget().GetDebugger().GetCommandInterpreter()));
return m_command_sp.get();
}

View File

@ -49,6 +49,8 @@ public:
bool CanDebug(lldb::TargetSP target_sp,
bool plugin_specified_by_name) override;
CommandObject *GetPluginCommandObject() override;
Status DoLoadCore() override;
DynamicLoader *GetDynamicLoader() override { return nullptr; }
@ -104,6 +106,7 @@ private:
FileSpec m_core_file;
llvm::ArrayRef<MinidumpThread> m_thread_list;
const MinidumpExceptionStream *m_active_exception;
lldb::CommandObjectSP m_command_sp;
bool m_is_wow64;
};