From e1b3620ec763cf3cd116570556cc3d7ca1771ad2 Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Tue, 25 Oct 2016 20:12:09 -0400 Subject: [PATCH] minidump_dump: dump stack memory like hexdump The current stack output is one line byte string which is not easy for humans to parse. Extend the print mode to support a hexdump-like view and switch to that by default. Now we get something like: Stack 00000000 20 67 7b 53 94 7f 00 00 01 00 00 00 00 00 00 00 | g{S...........| 00000010 00 70 c4 44 9a 25 00 00 08 65 7a 53 94 7f 00 00 |.p.D.%...ezS...| BUG=chromium:598947 Change-Id: I868e1cf4faa435a14c5f1c35f94a5db4a49b6a6d Reviewed-on: https://chromium-review.googlesource.com/404008 Reviewed-by: Mark Mentovai --- src/google_breakpad/processor/minidump.h | 16 ++++- src/processor/minidump.cc | 78 +++++++++++++++++++++--- src/processor/minidump_dump.cc | 17 ++++-- 3 files changed, 97 insertions(+), 14 deletions(-) diff --git a/src/google_breakpad/processor/minidump.h b/src/google_breakpad/processor/minidump.h index a5d32c8b..2651a323 100644 --- a/src/google_breakpad/processor/minidump.h +++ b/src/google_breakpad/processor/minidump.h @@ -236,6 +236,7 @@ class MinidumpMemoryRegion : public MinidumpObject, // Print a human-readable representation of the object to stdout. void Print() const; + void SetPrintMode(bool hexdump, unsigned int width); protected: explicit MinidumpMemoryRegion(Minidump* minidump); @@ -252,6 +253,10 @@ class MinidumpMemoryRegion : public MinidumpObject, template bool GetMemoryAtAddressInternal(uint64_t address, T* value) const; + // Knobs for controlling display of memory printing. + bool hexdump_; + unsigned int hexdump_width_; + // The largest memory region that will be read from a minidump. The // default is 1MB. static uint32_t max_bytes_; @@ -1104,7 +1109,9 @@ class MinidumpLinuxMapsList : public MinidumpStream { class Minidump { public: // path is the pathname of a file containing the minidump. - explicit Minidump(const string& path); + explicit Minidump(const string& path, + bool hexdump=false, + unsigned int hexdump_width=16); // input is an istream wrapping minidump data. Minidump holds a // weak pointer to input, and the caller must ensure that the stream // is valid as long as the Minidump object is. @@ -1214,6 +1221,9 @@ class Minidump { // Is the OS Android. bool IsAndroid(); + // Get current hexdump display settings. + unsigned int HexdumpMode() const { return hexdump_ ? hexdump_width_ : 0; } + private: // MinidumpStreamInfo is used in the MinidumpStreamMap. It lets // the Minidump object locate interesting streams quickly, and @@ -1275,6 +1285,10 @@ class Minidump { // Read(). bool valid_; + // Knobs for controlling display of memory printing. + bool hexdump_; + unsigned int hexdump_width_; + DISALLOW_COPY_AND_ASSIGN(Minidump); }; diff --git a/src/processor/minidump.cc b/src/processor/minidump.cc index 10400047..7fae7fb7 100644 --- a/src/processor/minidump.cc +++ b/src/processor/minidump.cc @@ -1202,6 +1202,8 @@ MinidumpMemoryRegion::MinidumpMemoryRegion(Minidump* minidump) : MinidumpObject(minidump), descriptor_(NULL), memory_(NULL) { + hexdump_width_ = minidump->HexdumpMode(); + hexdump_ = hexdump_width_ != 0; } @@ -1360,19 +1362,77 @@ void MinidumpMemoryRegion::Print() const { const uint8_t* memory = GetMemory(); if (memory) { - printf("0x"); - for (unsigned int byte_index = 0; - byte_index < descriptor_->memory.data_size; - byte_index++) { - printf("%02x", memory[byte_index]); + if (hexdump_) { + // Pretty hexdump view. + for (unsigned int byte_index = 0; + byte_index < descriptor_->memory.data_size; + byte_index += hexdump_width_) { + // In case the memory won't fill a whole line. + unsigned int num_bytes = std::min( + descriptor_->memory.data_size - byte_index, hexdump_width_); + + // Display the leading address. + printf("%08x ", byte_index); + + // Show the bytes in hex. + for (unsigned int i = 0; i < hexdump_width_; ++i) { + if (i < num_bytes) { + // Show the single byte of memory in hex. + printf("%02x ", memory[byte_index + i]); + } else { + // If this line doesn't fill up, pad it out. + printf(" "); + } + + // Insert a space every 8 bytes to make it more readable. + if (((i + 1) % 8) == 0) { + printf(" "); + } + } + + // Decode the line as ASCII. + printf("|"); + for (unsigned int i = 0; i < hexdump_width_; ++i) { + if (i < num_bytes) { + uint8_t byte = memory[byte_index + i]; + printf("%c", isprint(byte) ? byte : '.'); + } else { + // If this line doesn't fill up, pad it out. + printf(" "); + } + } + printf("|\n"); + } + } else { + // Ugly raw string view. + printf("0x"); + for (unsigned int i = 0; + i < descriptor_->memory.data_size; + i++) { + printf("%02x", memory[i]); + } + printf("\n"); } - printf("\n"); } else { printf("No memory\n"); } } +void MinidumpMemoryRegion::SetPrintMode(bool hexdump, + unsigned int hexdump_width) { + // Require the width to be a multiple of 8 bytes. + if (hexdump_width == 0 || (hexdump_width % 8) != 0) { + BPLOG(ERROR) << "MinidumpMemoryRegion print hexdump_width must be " + "multiple of 8, not " << hexdump_width; + return; + } + + hexdump_ = hexdump; + hexdump_width_ = hexdump_width; +} + + // // MinidumpThread // @@ -4694,14 +4754,16 @@ uint32_t Minidump::max_streams_ = 128; unsigned int Minidump::max_string_length_ = 1024; -Minidump::Minidump(const string& path) +Minidump::Minidump(const string& path, bool hexdump, unsigned int hexdump_width) : header_(), directory_(NULL), stream_map_(new MinidumpStreamMap()), path_(path), stream_(NULL), swap_(false), - valid_(false) { + valid_(false), + hexdump_(hexdump), + hexdump_width_(hexdump_width) { } Minidump::Minidump(istream& stream) diff --git a/src/processor/minidump_dump.cc b/src/processor/minidump_dump.cc index d6342cc4..a21ae5a7 100644 --- a/src/processor/minidump_dump.cc +++ b/src/processor/minidump_dump.cc @@ -55,9 +55,11 @@ using google_breakpad::MinidumpBreakpadInfo; struct Options { Options() - : minidumpPath() {} + : minidumpPath(), hexdump(false), hexdump_width(hexdump_width) {} string minidumpPath; + bool hexdump; + unsigned int hexdump_width; }; static void DumpRawStream(Minidump *minidump, @@ -99,8 +101,9 @@ static void DumpRawStream(Minidump *minidump, printf("\n\n"); } -static bool PrintMinidumpDump(const string& minidump_file) { - Minidump minidump(minidump_file); +static bool PrintMinidumpDump(const Options& options) { + Minidump minidump(options.minidumpPath, + options.hexdump); if (!minidump.Read()) { BPLOG(ERROR) << "minidump.Read() failed"; return false; @@ -218,6 +221,7 @@ Usage(int argc, const char *argv[], bool error) { "\n" "Options:\n" " should be a minidump.\n" + " -x:\t Display memory in a hexdump like format\n" " -h:\t Usage\n", argv[0]); } @@ -227,8 +231,11 @@ static void SetupOptions(int argc, const char *argv[], Options *options) { int ch; - while ((ch = getopt(argc, (char * const *)argv, "h")) != -1) { + while ((ch = getopt(argc, (char * const *)argv, "xh")) != -1) { switch (ch) { + case 'x': + options->hexdump = true; + break; case 'h': Usage(argc, argv, false); exit(0); @@ -254,5 +261,5 @@ int main(int argc, const char *argv[]) { Options options; BPLOG_INIT(&argc, &argv); SetupOptions(argc, argv, &options); - return PrintMinidumpDump(options.minidumpPath) ? 0 : 1; + return PrintMinidumpDump(options) ? 0 : 1; }