Dump Crashpad extension structures in minidump_dump

This is currently mostly useful to expose the annotations that Crashpad
stores in minidumps.

Example output:

MDRawCrashpadInfo
  version = 1
  report_id = 01234567-89ab-cdef-0123-456789abcdef
  client_id = fedcba98-7654-3210-fedc-ba9876543210
  simple_annotations["channel"] = canary
  simple_annotations["plat"] = OS X
  simple_annotations["prod"] = Chrome_Mac
  simple_annotations["ver"] = 59.0.3069.0
  module_list[0].minidump_module_list_index = 0
  module_list[0].version = 1
  module_list[0].simple_annotations["ptype"] = crashpad-handler
  module_list[1].minidump_module_list_index = 28
  module_list[1].version = 1
  module_list[1].list_annotations[0] = abort() called

Change-Id: I00ba291f93ea3a37fc3754c651b3ccc542e5b8b2
Reviewed-on: https://chromium-review.googlesource.com/688416
Reviewed-by: Robert Sesek <rsesek@chromium.org>
This commit is contained in:
Mark Mentovai 2017-09-27 17:13:47 -04:00
parent cbd7bb4cd2
commit 6d0287851f
4 changed files with 463 additions and 18 deletions

View file

@ -346,7 +346,11 @@ typedef enum {
MD_LINUX_ENVIRON = 0x47670007, /* /proc/$x/environ */ MD_LINUX_ENVIRON = 0x47670007, /* /proc/$x/environ */
MD_LINUX_AUXV = 0x47670008, /* /proc/$x/auxv */ MD_LINUX_AUXV = 0x47670008, /* /proc/$x/auxv */
MD_LINUX_MAPS = 0x47670009, /* /proc/$x/maps */ MD_LINUX_MAPS = 0x47670009, /* /proc/$x/maps */
MD_LINUX_DSO_DEBUG = 0x4767000A /* MDRawDebug{32,64} */ MD_LINUX_DSO_DEBUG = 0x4767000A, /* MDRawDebug{32,64} */
/* Crashpad extension types. 0x4350 = "CP"
* See Crashpad's minidump/minidump_extensions.h. */
MD_CRASHPAD_INFO_STREAM = 0x43500001, /* MDRawCrashpadInfo */
} MDStreamType; /* MINIDUMP_STREAM_TYPE */ } MDStreamType; /* MINIDUMP_STREAM_TYPE */
@ -1051,6 +1055,42 @@ typedef struct {
uint64_t dynamic; uint64_t dynamic;
} MDRawDebug64; } MDRawDebug64;
/* Crashpad extension types. See Crashpad's minidump/minidump_extensions.h. */
typedef struct {
MDRVA key;
MDRVA value;
} MDRawSimpleStringDictionaryEntry;
typedef struct {
uint32_t count;
MDRawSimpleStringDictionaryEntry entries[0];
} MDRawSimpleStringDictionary;
typedef struct {
uint32_t version;
MDLocationDescriptor list_annotations;
MDLocationDescriptor simple_annotations; /* MDRawSimpleStringDictionary */
} MDRawModuleCrashpadInfo;
typedef struct {
uint32_t minidump_module_list_index;
MDLocationDescriptor location; /* MDRawModuleCrashpadInfo */
} MDRawModuleCrashpadInfoLink;
typedef struct {
uint32_t count;
MDLocationDescriptor modules[0]; /* MDRawModuleCrashpadInfoLink */
} MDRawModuleCrashpadInfoList;
typedef struct {
uint32_t version;
MDGUID report_id;
MDGUID client_id;
MDLocationDescriptor simple_annotations; /* MDRawSimpleStringDictionary */
MDLocationDescriptor module_list; /* MDRawModuleCrashpadInfoList */
} MDRawCrashpadInfo;
#if defined(_MSC_VER) #if defined(_MSC_VER)
#pragma warning(pop) #pragma warning(pop)
#endif /* _MSC_VER */ #endif /* _MSC_VER */

View file

@ -1103,6 +1103,37 @@ class MinidumpLinuxMapsList : public MinidumpStream {
DISALLOW_COPY_AND_ASSIGN(MinidumpLinuxMapsList); DISALLOW_COPY_AND_ASSIGN(MinidumpLinuxMapsList);
}; };
// MinidumpCrashpadInfo wraps MDRawCrashpadInfo, which is an optional stream in
// a minidump that provides additional information about the process state
// at the time the minidump was generated.
class MinidumpCrashpadInfo : public MinidumpStream {
public:
const MDRawCrashpadInfo* crashpad_info() const {
return valid_ ? &crashpad_info_ : NULL;
}
// Print a human-readable representation of the object to stdout.
void Print();
private:
friend class Minidump;
static const uint32_t kStreamType = MD_CRASHPAD_INFO_STREAM;
explicit MinidumpCrashpadInfo(Minidump* minidump_);
bool Read(uint32_t expected_size);
MDRawCrashpadInfo crashpad_info_;
std::vector<uint32_t> module_crashpad_info_links_;
std::vector<MDRawModuleCrashpadInfo> module_crashpad_info_;
std::vector<std::vector<std::string>> module_crashpad_info_list_annotations_;
std::vector<std::map<std::string, std::string>>
module_crashpad_info_simple_annotations_;
std::map<std::string, std::string> simple_annotations_;
};
// Minidump is the user's interface to a minidump file. It wraps MDRawHeader // Minidump is the user's interface to a minidump file. It wraps MDRawHeader
// and provides access to the minidump's top-level stream directory. // and provides access to the minidump's top-level stream directory.
class Minidump { class Minidump {
@ -1164,6 +1195,7 @@ class Minidump {
virtual MinidumpMiscInfo* GetMiscInfo(); virtual MinidumpMiscInfo* GetMiscInfo();
virtual MinidumpBreakpadInfo* GetBreakpadInfo(); virtual MinidumpBreakpadInfo* GetBreakpadInfo();
virtual MinidumpMemoryInfoList* GetMemoryInfoList(); virtual MinidumpMemoryInfoList* GetMemoryInfoList();
MinidumpCrashpadInfo* GetCrashpadInfo();
// The next method also calls GetStream, but is exclusive for Linux dumps. // The next method also calls GetStream, but is exclusive for Linux dumps.
virtual MinidumpLinuxMapsList *GetLinuxMapsList(); virtual MinidumpLinuxMapsList *GetLinuxMapsList();
@ -1191,13 +1223,21 @@ class Minidump {
// Returns the current position of the minidump file. // Returns the current position of the minidump file.
off_t Tell(); off_t Tell();
// The next 2 methods are medium-level I/O routines. // Medium-level I/O routines.
// ReadString returns a string which is owned by the caller! offset // ReadString returns a string which is owned by the caller! offset
// specifies the offset that a length-encoded string is stored at in the // specifies the offset that a length-encoded string is stored at in the
// minidump file. // minidump file.
string* ReadString(off_t offset); string* ReadString(off_t offset);
bool ReadUTF8String(off_t offset, string* string_utf8);
bool ReadStringList(off_t offset, std::vector<std::string>* string_list);
bool ReadSimpleStringDictionary(
off_t offset,
std::map<std::string, std::string>* simple_string_dictionary);
// SeekToStreamType positions the file at the beginning of a stream // SeekToStreamType positions the file at the beginning of a stream
// identified by stream_type, and informs the caller of the stream's // identified by stream_type, and informs the caller of the stream's
// length by setting *stream_length. Because stream_map maps each stream // length by setting *stream_length. Because stream_map maps each stream

View file

@ -49,10 +49,8 @@
#include <algorithm> #include <algorithm>
#include <fstream> #include <fstream>
#include <iostream>
#include <limits> #include <limits>
#include <map> #include <utility>
#include <vector>
#include "processor/range_map-inl.h" #include "processor/range_map-inl.h"
@ -212,6 +210,11 @@ static inline void Swap(MDXStateConfigFeatureMscInfo* xstate_feature_info) {
} }
} }
static inline void Swap(MDRawSimpleStringDictionaryEntry* entry) {
Swap(&entry->key);
Swap(&entry->value);
}
static inline void Swap(uint16_t* data, size_t size_in_bytes) { static inline void Swap(uint16_t* data, size_t size_in_bytes) {
size_t data_length = size_in_bytes / sizeof(data[0]); size_t data_length = size_in_bytes / sizeof(data[0]);
for (size_t i = 0; i < data_length; i++) { for (size_t i = 0; i < data_length; i++) {
@ -371,7 +374,7 @@ static void PrintValueOrInvalid(bool valid,
} }
// Converts a time_t to a string showing the time in UTC. // Converts a time_t to a string showing the time in UTC.
string TimeTToUTCString(time_t tt) { static string TimeTToUTCString(time_t tt) {
struct tm timestruct; struct tm timestruct;
#ifdef _WIN32 #ifdef _WIN32
gmtime_s(&timestruct, &tt); gmtime_s(&timestruct, &tt);
@ -389,6 +392,24 @@ string TimeTToUTCString(time_t tt) {
} }
static string MDGUIDToString(const MDGUID& uuid) {
char buf[37];
snprintf(buf, sizeof(buf), "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
uuid.data1,
uuid.data2,
uuid.data3,
uuid.data4[0],
uuid.data4[1],
uuid.data4[2],
uuid.data4[3],
uuid.data4[4],
uuid.data4[5],
uuid.data4[6],
uuid.data4[7]);
return std::string(buf);
}
// //
// MinidumpObject // MinidumpObject
// //
@ -2481,17 +2502,8 @@ void MinidumpModule::Print() {
printf(" (cv_record).cv_signature = 0x%x\n", printf(" (cv_record).cv_signature = 0x%x\n",
cv_record_70->cv_signature); cv_record_70->cv_signature);
printf(" (cv_record).signature = %08x-%04x-%04x-%02x%02x-", printf(" (cv_record).signature = %s\n",
cv_record_70->signature.data1, MDGUIDToString(cv_record_70->signature).c_str());
cv_record_70->signature.data2,
cv_record_70->signature.data3,
cv_record_70->signature.data4[0],
cv_record_70->signature.data4[1]);
for (unsigned int guidIndex = 2;
guidIndex < 8;
++guidIndex) {
printf("%02x", cv_record_70->signature.data4[guidIndex]);
}
printf("\n"); printf("\n");
printf(" (cv_record).age = %d\n", printf(" (cv_record).age = %d\n",
cv_record_70->age); cv_record_70->age);
@ -4745,6 +4757,192 @@ void MinidumpLinuxMapsList::Print() const {
} }
} }
//
// MinidumpCrashpadInfo
//
MinidumpCrashpadInfo::MinidumpCrashpadInfo(Minidump* minidump)
: MinidumpStream(minidump),
crashpad_info_(),
module_crashpad_info_links_(),
module_crashpad_info_(),
module_crashpad_info_list_annotations_(),
module_crashpad_info_simple_annotations_(),
simple_annotations_() {
}
bool MinidumpCrashpadInfo::Read(uint32_t expected_size) {
valid_ = false;
if (expected_size != sizeof(crashpad_info_)) {
BPLOG(ERROR) << "MinidumpCrashpadInfo size mismatch, " << expected_size <<
" != " << sizeof(crashpad_info_);
return false;
}
if (!minidump_->ReadBytes(&crashpad_info_, sizeof(crashpad_info_))) {
BPLOG(ERROR) << "MinidumpCrashpadInfo cannot read Crashpad info";
return false;
}
if (minidump_->swap()) {
Swap(&crashpad_info_.version);
Swap(&crashpad_info_.report_id);
Swap(&crashpad_info_.client_id);
Swap(&crashpad_info_.simple_annotations);
Swap(&crashpad_info_.module_list);
}
if (crashpad_info_.simple_annotations.data_size) {
if (!minidump_->ReadSimpleStringDictionary(
crashpad_info_.simple_annotations.rva,
&simple_annotations_)) {
BPLOG(ERROR) << "MinidumpCrashpadInfo cannot read simple_annotations";
return false;
}
}
if (crashpad_info_.module_list.data_size) {
if (!minidump_->SeekSet(crashpad_info_.module_list.rva)) {
BPLOG(ERROR) << "MinidumpCrashpadInfo cannot seek to module_list";
return false;
}
uint32_t count;
if (!minidump_->ReadBytes(&count, sizeof(count))) {
BPLOG(ERROR) << "MinidumpCrashpadInfo cannot read module_list count";
return false;
}
if (minidump_->swap()) {
Swap(&count);
}
scoped_array<MDRawModuleCrashpadInfoLink> module_crashpad_info_links(
new MDRawModuleCrashpadInfoLink[count]);
// Read the entire array in one fell swoop, instead of reading one entry
// at a time in the loop.
if (!minidump_->ReadBytes(
&module_crashpad_info_links[0],
sizeof(MDRawModuleCrashpadInfoLink) * count)) {
BPLOG(ERROR)
<< "MinidumpCrashpadInfo could not read Crashpad module links";
return false;
}
for (uint32_t index = 0; index < count; ++index) {
if (minidump_->swap()) {
Swap(&module_crashpad_info_links[index].minidump_module_list_index);
Swap(&module_crashpad_info_links[index].location);
}
if (!minidump_->SeekSet(module_crashpad_info_links[index].location.rva)) {
BPLOG(ERROR)
<< "MinidumpCrashpadInfo cannot seek to Crashpad module info";
return false;
}
MDRawModuleCrashpadInfo module_crashpad_info;
if (!minidump_->ReadBytes(&module_crashpad_info,
sizeof(module_crashpad_info))) {
BPLOG(ERROR) << "MinidumpCrashpadInfo cannot read Crashpad module info";
return false;
}
if (minidump_->swap()) {
Swap(&module_crashpad_info.version);
Swap(&module_crashpad_info.list_annotations);
Swap(&module_crashpad_info.simple_annotations);
}
std::vector<std::string> list_annotations;
if (module_crashpad_info.list_annotations.data_size) {
if (!minidump_->ReadStringList(
module_crashpad_info.list_annotations.rva,
&list_annotations)) {
BPLOG(ERROR) << "MinidumpCrashpadInfo cannot read Crashpad module "
"info list annotations";
return false;
}
}
std::map<std::string, std::string> simple_annotations;
if (module_crashpad_info.simple_annotations.data_size) {
if (!minidump_->ReadSimpleStringDictionary(
module_crashpad_info.simple_annotations.rva,
&simple_annotations)) {
BPLOG(ERROR) << "MinidumpCrashpadInfo cannot read Crashpad module "
"info simple annotations";
return false;
}
}
module_crashpad_info_links_.push_back(
module_crashpad_info_links[index].minidump_module_list_index);
module_crashpad_info_.push_back(module_crashpad_info);
module_crashpad_info_list_annotations_.push_back(list_annotations);
module_crashpad_info_simple_annotations_.push_back(simple_annotations);
}
}
valid_ = true;
return true;
}
void MinidumpCrashpadInfo::Print() {
if (!valid_) {
BPLOG(ERROR) << "MinidumpCrashpadInfo cannot print invalid data";
return;
}
printf("MDRawCrashpadInfo\n");
printf(" version = %d\n", crashpad_info_.version);
printf(" report_id = %s\n",
MDGUIDToString(crashpad_info_.report_id).c_str());
printf(" client_id = %s\n",
MDGUIDToString(crashpad_info_.client_id).c_str());
for (std::map<string, string>::const_iterator iterator =
simple_annotations_.begin();
iterator != simple_annotations_.end();
++iterator) {
printf(" simple_annotations[\"%s\"] = %s\n",
iterator->first.c_str(), iterator->second.c_str());
}
for (uint32_t module_index = 0;
module_index < module_crashpad_info_links_.size();
++module_index) {
printf(" module_list[%d].minidump_module_list_index = %d\n",
module_index, module_crashpad_info_links_[module_index]);
printf(" module_list[%d].version = %d\n",
module_index, module_crashpad_info_[module_index].version);
for (uint32_t annotation_index = 0;
annotation_index <
module_crashpad_info_list_annotations_[module_index].size();
++annotation_index) {
printf(" module_list[%d].list_annotations[%d] = %s\n",
module_index,
annotation_index,
module_crashpad_info_list_annotations_
[module_index][annotation_index].c_str());
}
for (std::map<string, string>::const_iterator iterator =
module_crashpad_info_simple_annotations_[module_index].begin();
iterator !=
module_crashpad_info_simple_annotations_[module_index].end();
++iterator) {
printf(" module_list[%d].simple_annotations[\"%s\"] = %s\n",
module_index, iterator->first.c_str(), iterator->second.c_str());
}
}
printf("\n");
}
// //
// Minidump // Minidump
// //
@ -4993,7 +5191,8 @@ bool Minidump::Read() {
case MD_EXCEPTION_STREAM: case MD_EXCEPTION_STREAM:
case MD_SYSTEM_INFO_STREAM: case MD_SYSTEM_INFO_STREAM:
case MD_MISC_INFO_STREAM: case MD_MISC_INFO_STREAM:
case MD_BREAKPAD_INFO_STREAM: { case MD_BREAKPAD_INFO_STREAM:
case MD_CRASHPAD_INFO_STREAM: {
if (stream_map_->find(stream_type) != stream_map_->end()) { if (stream_map_->find(stream_type) != stream_map_->end()) {
// Another stream with this type was already found. A minidump // Another stream with this type was already found. A minidump
// file should contain at most one of each of these stream types. // file should contain at most one of each of these stream types.
@ -5100,6 +5299,11 @@ bool Minidump::IsAndroid() {
return system_info && system_info->platform_id == MD_OS_ANDROID; return system_info && system_info->platform_id == MD_OS_ANDROID;
} }
MinidumpCrashpadInfo* Minidump::GetCrashpadInfo() {
MinidumpCrashpadInfo* crashpad_info;
return GetStream(&crashpad_info);
}
static const char* get_stream_name(uint32_t stream_type) { static const char* get_stream_name(uint32_t stream_type) {
switch (stream_type) { switch (stream_type) {
case MD_UNUSED_STREAM: case MD_UNUSED_STREAM:
@ -5170,6 +5374,8 @@ static const char* get_stream_name(uint32_t stream_type) {
return "MD_LINUX_MAPS"; return "MD_LINUX_MAPS";
case MD_LINUX_DSO_DEBUG: case MD_LINUX_DSO_DEBUG:
return "MD_LINUX_DSO_DEBUG"; return "MD_LINUX_DSO_DEBUG";
case MD_CRASHPAD_INFO_STREAM:
return "MD_CRASHPAD_INFO_STREAM";
default: default:
return "unknown"; return "unknown";
} }
@ -5351,6 +5557,158 @@ string* Minidump::ReadString(off_t offset) {
} }
bool Minidump::ReadUTF8String(off_t offset, string* string_utf8) {
if (!valid_) {
BPLOG(ERROR) << "Invalid Minidump for ReadString";
return false;
}
if (!SeekSet(offset)) {
BPLOG(ERROR) << "ReadUTF8String could not seek to string at offset "
<< offset;
return false;
}
uint32_t bytes;
if (!ReadBytes(&bytes, sizeof(bytes))) {
BPLOG(ERROR) << "ReadUTF8String could not read string size at offset " <<
offset;
return false;
}
if (swap_) {
Swap(&bytes);
}
if (bytes > max_string_length_) {
BPLOG(ERROR) << "ReadUTF8String string length " << bytes <<
" exceeds maximum " << max_string_length_ <<
" at offset " << offset;
return false;
}
string_utf8->resize(bytes);
if (!ReadBytes(&(*string_utf8)[0], bytes)) {
BPLOG(ERROR) << "ReadUTF8String could not read " << bytes <<
"-byte string at offset " << offset;
return false;
}
return true;
}
bool Minidump::ReadStringList(
off_t offset,
std::vector<std::string>* string_list) {
string_list->clear();
if (!SeekSet(offset)) {
BPLOG(ERROR) << "Minidump cannot seek to string_list";
return false;
}
uint32_t count;
if (!ReadBytes(&count, sizeof(count))) {
BPLOG(ERROR) << "Minidump cannot read string_list count";
return false;
}
if (swap_) {
Swap(&count);
}
scoped_array<MDRVA> rvas(new MDRVA[count]);
// Read the entire array in one fell swoop, instead of reading one entry
// at a time in the loop.
if (!ReadBytes(&rvas[0], sizeof(MDRVA) * count)) {
BPLOG(ERROR) << "Minidump could not read string_list";
return false;
}
for (uint32_t index = 0; index < count; ++index) {
if (swap()) {
Swap(&rvas[index]);
}
string entry;
if (!ReadUTF8String(rvas[index], &entry)) {
BPLOG(ERROR) << "Minidump could not read string_list entry";
return false;
}
string_list->push_back(entry);
}
return true;
}
bool Minidump::ReadSimpleStringDictionary(
off_t offset,
std::map<std::string, std::string>* simple_string_dictionary) {
simple_string_dictionary->clear();
if (!SeekSet(offset)) {
BPLOG(ERROR) << "Minidump cannot seek to simple_string_dictionary";
return false;
}
uint32_t count;
if (!ReadBytes(&count, sizeof(count))) {
BPLOG(ERROR)
<< "Minidump cannot read simple_string_dictionary count";
return false;
}
if (swap()) {
Swap(&count);
}
scoped_array<MDRawSimpleStringDictionaryEntry> entries(
new MDRawSimpleStringDictionaryEntry[count]);
// Read the entire array in one fell swoop, instead of reading one entry
// at a time in the loop.
if (!ReadBytes(
&entries[0],
sizeof(MDRawSimpleStringDictionaryEntry) * count)) {
BPLOG(ERROR) << "Minidump could not read simple_string_dictionary";
return false;
}
for (uint32_t index = 0; index < count; ++index) {
if (swap()) {
Swap(&entries[index]);
}
string key;
if (!ReadUTF8String(entries[index].key, &key)) {
BPLOG(ERROR) << "Minidump could not read simple_string_dictionary key";
return false;
}
string value;
if (!ReadUTF8String(entries[index].value, &value)) {
BPLOG(ERROR) << "Minidump could not read simple_string_dictionary value";
return false;
}
if (simple_string_dictionary->find(key) !=
simple_string_dictionary->end()) {
BPLOG(ERROR)
<< "Minidump: discarding duplicate simple_string_dictionary value "
<< value << " for key " << key;
} else {
simple_string_dictionary->insert(std::make_pair(key, value));
}
}
return true;
}
bool Minidump::SeekToStreamType(uint32_t stream_type, bool Minidump::SeekToStreamType(uint32_t stream_type,
uint32_t* stream_length) { uint32_t* stream_length) {
BPLOG_IF(ERROR, !stream_length) << "Minidump::SeekToStreamType requires " BPLOG_IF(ERROR, !stream_length) << "Minidump::SeekToStreamType requires "

View file

@ -52,6 +52,7 @@ using google_breakpad::MinidumpAssertion;
using google_breakpad::MinidumpSystemInfo; using google_breakpad::MinidumpSystemInfo;
using google_breakpad::MinidumpMiscInfo; using google_breakpad::MinidumpMiscInfo;
using google_breakpad::MinidumpBreakpadInfo; using google_breakpad::MinidumpBreakpadInfo;
using google_breakpad::MinidumpCrashpadInfo;
struct Options { struct Options {
Options() Options()
@ -182,6 +183,12 @@ static bool PrintMinidumpDump(const Options& options) {
memory_info_list->Print(); memory_info_list->Print();
} }
MinidumpCrashpadInfo *crashpad_info = minidump.GetCrashpadInfo();
if (crashpad_info) {
// Crashpad info is optional, so don't treat absence as an error.
crashpad_info->Print();
}
DumpRawStream(&minidump, DumpRawStream(&minidump,
MD_LINUX_CMD_LINE, MD_LINUX_CMD_LINE,
"MD_LINUX_CMD_LINE", "MD_LINUX_CMD_LINE",