Added classes to support reading unloaded module lists in minidumps.
The implementations of Module/UnloadedModule and ModuleList/UnloadedModuleList are very similar. They have been made separate classes because they operate on different structs, complicating factoring code into a base class and have sufficiently different implementation that templates would not be suitable. When unloaded modules have partially overlapping ranges, the module shrink down feature is used to move the start of the higher range to the end of the lower range. If two unloaded modules overlap identically, the second module will not be added to the range map and the failure ignored. Places where MinidumpUnloadedModule differs from MinidumpModule: code_identifier: the android/linux case is deleted since cv_records never exist. debug_file/debug_identifier/version: always return empty strings. Read: an expected size is provided as opposed to MD_MODULE_SIZE. A seek is used if there are extra, unused bytes. Places where MinidumpUnloadedModuleList differs from MinidumpModuleList: Read: entry and header size is provided in the header in addition to count. This changes the checks and handling of padding. Failures from StoreRange are ignored. GetMainModule: always returns NULL. BUG= Change-Id: I52e93d3ccc38483f50a6418fede8b506ec879aaa Reviewed-on: https://chromium-review.googlesource.com/421566 Reviewed-by: Joshua Peraza <jperaza@chromium.org>
This commit is contained in:
parent
f78d953511
commit
c2d969cb10
6 changed files with 685 additions and 1 deletions
|
@ -676,6 +676,20 @@ typedef enum {
|
|||
MD_OS_NACL = 0x8205 /* Native Client (NaCl) */
|
||||
} MDOSPlatform;
|
||||
|
||||
typedef struct {
|
||||
uint64_t base_of_image;
|
||||
uint32_t size_of_image;
|
||||
uint32_t checksum;
|
||||
uint32_t time_date_stamp;
|
||||
MDRVA module_name_rva;
|
||||
} MDRawUnloadedModule;
|
||||
|
||||
typedef struct {
|
||||
uint32_t size_of_header;
|
||||
uint32_t size_of_entry;
|
||||
uint32_t number_of_entries;
|
||||
} MDRawUnloadedModuleList; /* MINIDUMP_UNLOADED_MODULE_LIST */
|
||||
|
||||
typedef struct {
|
||||
uint16_t year;
|
||||
uint16_t month;
|
||||
|
|
|
@ -752,6 +752,118 @@ class MinidumpSystemInfo : public MinidumpStream {
|
|||
};
|
||||
|
||||
|
||||
// MinidumpUnloadedModule wraps MDRawUnloadedModule
|
||||
class MinidumpUnloadedModule : public MinidumpObject,
|
||||
public CodeModule {
|
||||
public:
|
||||
~MinidumpUnloadedModule() override;
|
||||
|
||||
const MDRawUnloadedModule* module() const {
|
||||
return valid_ ? &unloaded_module_ : NULL;
|
||||
}
|
||||
|
||||
// CodeModule implementation
|
||||
uint64_t base_address() const override {
|
||||
return valid_ ? unloaded_module_.base_of_image : 0;
|
||||
}
|
||||
uint64_t size() const override {
|
||||
return valid_ ? unloaded_module_.size_of_image : 0;
|
||||
}
|
||||
string code_file() const override;
|
||||
string code_identifier() const override;
|
||||
string debug_file() const override;
|
||||
string debug_identifier() const override;
|
||||
string version() const override;
|
||||
CodeModule* Copy() const override;
|
||||
uint64_t shrink_down_delta() const override;
|
||||
void SetShrinkDownDelta(uint64_t shrink_down_delta) override;
|
||||
|
||||
protected:
|
||||
explicit MinidumpUnloadedModule(Minidump* minidump);
|
||||
|
||||
private:
|
||||
// These objects are managed by MinidumpUnloadedModuleList
|
||||
friend class MinidumpUnloadedModuleList;
|
||||
|
||||
// This works like MinidumpStream::Read, but is driven by
|
||||
// MinidumpUnloadedModuleList.
|
||||
bool Read(uint32_t expected_size);
|
||||
|
||||
// Reads the module name. This is done separately from Read to
|
||||
// allow contiguous reading of code modules by MinidumpUnloadedModuleList.
|
||||
bool ReadAuxiliaryData();
|
||||
|
||||
// True after a successful Read. This is different from valid_, which
|
||||
// is not set true until ReadAuxiliaryData also completes successfully.
|
||||
// module_valid_ is only used by ReadAuxiliaryData and the functions it
|
||||
// calls to determine whether the object is ready for auxiliary data to
|
||||
// be read.
|
||||
bool module_valid_;
|
||||
|
||||
MDRawUnloadedModule unloaded_module_;
|
||||
|
||||
// Cached module name
|
||||
const string* name_;
|
||||
};
|
||||
|
||||
|
||||
// MinidumpUnloadedModuleList contains all the unloaded code modules for a
|
||||
// process in the form of MinidumpUnloadedModules. It maintains a map of
|
||||
// these modules so that it may easily provide a code module corresponding
|
||||
// to a specific address. If multiple modules in the list have identical
|
||||
// ranges, only the first module encountered is recorded in the range map.
|
||||
class MinidumpUnloadedModuleList : public MinidumpStream,
|
||||
public CodeModules {
|
||||
public:
|
||||
~MinidumpUnloadedModuleList() override;
|
||||
|
||||
static void set_max_modules(uint32_t max_modules) {
|
||||
max_modules_ = max_modules;
|
||||
}
|
||||
static uint32_t max_modules() { return max_modules_; }
|
||||
|
||||
// CodeModules implementation.
|
||||
unsigned int module_count() const override {
|
||||
return valid_ ? module_count_ : 0;
|
||||
}
|
||||
const MinidumpUnloadedModule*
|
||||
GetModuleForAddress(uint64_t address) const override;
|
||||
const MinidumpUnloadedModule* GetMainModule() const override;
|
||||
const MinidumpUnloadedModule*
|
||||
GetModuleAtSequence(unsigned int sequence) const override;
|
||||
const MinidumpUnloadedModule*
|
||||
GetModuleAtIndex(unsigned int index) const override;
|
||||
const CodeModules* Copy() const override;
|
||||
vector<linked_ptr<const CodeModule>> GetShrunkRangeModules() const override;
|
||||
bool IsModuleShrinkEnabled() const override;
|
||||
|
||||
protected:
|
||||
explicit MinidumpUnloadedModuleList(Minidump* minidump_);
|
||||
|
||||
private:
|
||||
friend class Minidump;
|
||||
|
||||
typedef vector<MinidumpUnloadedModule> MinidumpUnloadedModules;
|
||||
|
||||
static const uint32_t kStreamType = MD_UNLOADED_MODULE_LIST_STREAM;
|
||||
|
||||
|
||||
bool Read(uint32_t expected_size_);
|
||||
|
||||
// The largest number of modules that will be read from a minidump. The
|
||||
// default is 1024.
|
||||
static uint32_t max_modules_;
|
||||
|
||||
// Access to module indices using addresses as the key.
|
||||
RangeMap<uint64_t, unsigned int> *range_map_;
|
||||
|
||||
MinidumpUnloadedModules *unloaded_modules_;
|
||||
uint32_t module_count_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(MinidumpUnloadedModuleList);
|
||||
};
|
||||
|
||||
|
||||
// MinidumpMiscInfo wraps MDRawMiscInfo and provides information about
|
||||
// the process that generated the minidump, and optionally additional system
|
||||
// information. See also MinidumpSystemInfo.
|
||||
|
@ -1041,6 +1153,7 @@ class Minidump {
|
|||
virtual MinidumpException* GetException();
|
||||
virtual MinidumpAssertion* GetAssertion();
|
||||
virtual MinidumpSystemInfo* GetSystemInfo();
|
||||
virtual MinidumpUnloadedModuleList* GetUnloadedModuleList();
|
||||
virtual MinidumpMiscInfo* GetMiscInfo();
|
||||
virtual MinidumpBreakpadInfo* GetBreakpadInfo();
|
||||
virtual MinidumpMemoryInfoList* GetMemoryInfoList();
|
||||
|
|
|
@ -3509,6 +3509,390 @@ void MinidumpSystemInfo::Print() {
|
|||
}
|
||||
|
||||
|
||||
//
|
||||
// MinidumpUnloadedModule
|
||||
//
|
||||
|
||||
|
||||
MinidumpUnloadedModule::MinidumpUnloadedModule(Minidump* minidump)
|
||||
: MinidumpObject(minidump),
|
||||
module_valid_(false),
|
||||
unloaded_module_(),
|
||||
name_(NULL) {
|
||||
|
||||
}
|
||||
|
||||
MinidumpUnloadedModule::~MinidumpUnloadedModule() {
|
||||
delete name_;
|
||||
}
|
||||
|
||||
string MinidumpUnloadedModule::code_file() const {
|
||||
if (!valid_) {
|
||||
BPLOG(ERROR) << "Invalid MinidumpUnloadedModule for code_file";
|
||||
return "";
|
||||
}
|
||||
|
||||
return *name_;
|
||||
}
|
||||
|
||||
string MinidumpUnloadedModule::code_identifier() const {
|
||||
if (!valid_) {
|
||||
BPLOG(ERROR) << "Invalid MinidumpUnloadedModule for code_identifier";
|
||||
return "";
|
||||
}
|
||||
|
||||
MinidumpSystemInfo *minidump_system_info = minidump_->GetSystemInfo();
|
||||
if (!minidump_system_info) {
|
||||
BPLOG(ERROR) << "MinidumpUnloadedModule code_identifier requires "
|
||||
"MinidumpSystemInfo";
|
||||
return "";
|
||||
}
|
||||
|
||||
const MDRawSystemInfo *raw_system_info = minidump_system_info->system_info();
|
||||
if (!raw_system_info) {
|
||||
BPLOG(ERROR) << "MinidumpUnloadedModule code_identifier requires "
|
||||
<< "MDRawSystemInfo";
|
||||
return "";
|
||||
}
|
||||
|
||||
string identifier;
|
||||
|
||||
switch (raw_system_info->platform_id) {
|
||||
case MD_OS_WIN32_NT:
|
||||
case MD_OS_WIN32_WINDOWS: {
|
||||
// Use the same format that the MS symbol server uses in filesystem
|
||||
// hierarchies.
|
||||
char identifier_string[17];
|
||||
snprintf(identifier_string, sizeof(identifier_string), "%08X%x",
|
||||
unloaded_module_.time_date_stamp,
|
||||
unloaded_module_.size_of_image);
|
||||
identifier = identifier_string;
|
||||
break;
|
||||
}
|
||||
|
||||
case MD_OS_ANDROID:
|
||||
case MD_OS_LINUX:
|
||||
case MD_OS_MAC_OS_X:
|
||||
case MD_OS_IOS:
|
||||
case MD_OS_SOLARIS:
|
||||
case MD_OS_NACL:
|
||||
case MD_OS_PS3: {
|
||||
// TODO(mmentovai): support uuid extension if present, otherwise fall
|
||||
// back to version (from LC_ID_DYLIB?), otherwise fall back to something
|
||||
// else.
|
||||
identifier = "id";
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
// Without knowing what OS generated the dump, we can't generate a good
|
||||
// identifier. Return an empty string, signalling failure.
|
||||
BPLOG(ERROR) << "MinidumpUnloadedModule code_identifier requires known "
|
||||
<< "platform, found "
|
||||
<< HexString(raw_system_info->platform_id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return identifier;
|
||||
}
|
||||
|
||||
string MinidumpUnloadedModule::debug_file() const {
|
||||
return ""; // No debug info provided with unloaded modules
|
||||
}
|
||||
|
||||
string MinidumpUnloadedModule::debug_identifier() const {
|
||||
return ""; // No debug info provided with unloaded modules
|
||||
}
|
||||
|
||||
string MinidumpUnloadedModule::version() const {
|
||||
return ""; // No version info provided with unloaded modules
|
||||
}
|
||||
|
||||
CodeModule* MinidumpUnloadedModule::Copy() const {
|
||||
return new BasicCodeModule(this);
|
||||
}
|
||||
|
||||
uint64_t MinidumpUnloadedModule::shrink_down_delta() const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void MinidumpUnloadedModule::SetShrinkDownDelta(uint64_t shrink_down_delta) {
|
||||
// Not implemented
|
||||
assert(false);
|
||||
}
|
||||
|
||||
bool MinidumpUnloadedModule::Read(uint32_t expected_size) {
|
||||
|
||||
delete name_;
|
||||
valid_ = false;
|
||||
|
||||
if (expected_size < sizeof(unloaded_module_)) {
|
||||
BPLOG(ERROR) << "MinidumpUnloadedModule expected size is less than size "
|
||||
<< "of struct " << expected_size << " < "
|
||||
<< sizeof(unloaded_module_);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!minidump_->ReadBytes(&unloaded_module_, sizeof(unloaded_module_))) {
|
||||
BPLOG(ERROR) << "MinidumpUnloadedModule cannot read module";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (expected_size > sizeof(unloaded_module_)) {
|
||||
uint32_t module_bytes_remaining = expected_size - sizeof(unloaded_module_);
|
||||
off_t pos = minidump_->Tell();
|
||||
if (!minidump_->SeekSet(pos + module_bytes_remaining)) {
|
||||
BPLOG(ERROR) << "MinidumpUnloadedModule unable to seek to end of module";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (minidump_->swap()) {
|
||||
Swap(&unloaded_module_.base_of_image);
|
||||
Swap(&unloaded_module_.size_of_image);
|
||||
Swap(&unloaded_module_.checksum);
|
||||
Swap(&unloaded_module_.time_date_stamp);
|
||||
Swap(&unloaded_module_.module_name_rva);
|
||||
}
|
||||
|
||||
// Check for base + size overflow or undersize.
|
||||
if (unloaded_module_.size_of_image == 0 ||
|
||||
unloaded_module_.size_of_image >
|
||||
numeric_limits<uint64_t>::max() - unloaded_module_.base_of_image) {
|
||||
BPLOG(ERROR) << "MinidumpUnloadedModule has a module problem, " <<
|
||||
HexString(unloaded_module_.base_of_image) << "+" <<
|
||||
HexString(unloaded_module_.size_of_image);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
module_valid_ = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MinidumpUnloadedModule::ReadAuxiliaryData() {
|
||||
if (!module_valid_) {
|
||||
BPLOG(ERROR) << "Invalid MinidumpUnloadedModule for ReadAuxiliaryData";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Each module must have a name.
|
||||
name_ = minidump_->ReadString(unloaded_module_.module_name_rva);
|
||||
if (!name_) {
|
||||
BPLOG(ERROR) << "MinidumpUnloadedModule could not read name";
|
||||
return false;
|
||||
}
|
||||
|
||||
// At this point, we have enough info for the module to be valid.
|
||||
valid_ = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// MinidumpUnloadedModuleList
|
||||
//
|
||||
|
||||
|
||||
uint32_t MinidumpUnloadedModuleList::max_modules_ = 1024;
|
||||
|
||||
|
||||
MinidumpUnloadedModuleList::MinidumpUnloadedModuleList(Minidump* minidump)
|
||||
: MinidumpStream(minidump),
|
||||
range_map_(new RangeMap<uint64_t, unsigned int>()),
|
||||
unloaded_modules_(NULL),
|
||||
module_count_(0) {
|
||||
range_map_->SetEnableShrinkDown(true);
|
||||
}
|
||||
|
||||
MinidumpUnloadedModuleList::~MinidumpUnloadedModuleList() {
|
||||
delete range_map_;
|
||||
delete unloaded_modules_;
|
||||
}
|
||||
|
||||
|
||||
bool MinidumpUnloadedModuleList::Read(uint32_t expected_size) {
|
||||
range_map_->Clear();
|
||||
delete unloaded_modules_;
|
||||
unloaded_modules_ = NULL;
|
||||
module_count_ = 0;
|
||||
|
||||
valid_ = false;
|
||||
|
||||
uint32_t size_of_header;
|
||||
if (!minidump_->ReadBytes(&size_of_header, sizeof(size_of_header))) {
|
||||
BPLOG(ERROR) << "MinidumpUnloadedModuleList could not read header size";
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t size_of_entry;
|
||||
if (!minidump_->ReadBytes(&size_of_entry, sizeof(size_of_entry))) {
|
||||
BPLOG(ERROR) << "MinidumpUnloadedModuleList could not read entry size";
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t number_of_entries;
|
||||
if (!minidump_->ReadBytes(&number_of_entries, sizeof(number_of_entries))) {
|
||||
BPLOG(ERROR) <<
|
||||
"MinidumpUnloadedModuleList could not read number of entries";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (minidump_->swap()) {
|
||||
Swap(&size_of_header);
|
||||
Swap(&size_of_entry);
|
||||
Swap(&number_of_entries);
|
||||
}
|
||||
|
||||
uint32_t header_bytes_remaining = size_of_header - sizeof(size_of_header) -
|
||||
sizeof(size_of_entry) - sizeof(number_of_entries);
|
||||
if (header_bytes_remaining) {
|
||||
off_t pos = minidump_->Tell();
|
||||
if (!minidump_->SeekSet(pos + header_bytes_remaining)) {
|
||||
BPLOG(ERROR) << "MinidumpUnloadedModuleList could not read header sized "
|
||||
<< size_of_header;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (expected_size != size_of_header + (size_of_entry * number_of_entries)) {
|
||||
BPLOG(ERROR) << "MinidumpUnloadedModuleList expected_size mismatch " <<
|
||||
expected_size << " != " << size_of_header << " + (" <<
|
||||
size_of_entry << " * " << number_of_entries << ")";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (number_of_entries > max_modules_) {
|
||||
BPLOG(ERROR) << "MinidumpUnloadedModuleList count " <<
|
||||
number_of_entries << " exceeds maximum " << max_modules_;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (number_of_entries != 0) {
|
||||
scoped_ptr<MinidumpUnloadedModules> modules(
|
||||
new MinidumpUnloadedModules(number_of_entries,
|
||||
MinidumpUnloadedModule(minidump_)));
|
||||
|
||||
for (unsigned int module_index = 0;
|
||||
module_index < number_of_entries;
|
||||
++module_index) {
|
||||
MinidumpUnloadedModule* module = &(*modules)[module_index];
|
||||
|
||||
if (!module->Read(size_of_entry)) {
|
||||
BPLOG(ERROR) << "MinidumpUnloadedModuleList could not read module " <<
|
||||
module_index << "/" << number_of_entries;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned int module_index = 0;
|
||||
module_index < number_of_entries;
|
||||
++module_index) {
|
||||
MinidumpUnloadedModule* module = &(*modules)[module_index];
|
||||
|
||||
if (!module->ReadAuxiliaryData()) {
|
||||
BPLOG(ERROR) << "MinidumpUnloadedModuleList could not read required "
|
||||
"module auxiliary data for module " <<
|
||||
module_index << "/" << number_of_entries;
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t base_address = module->base_address();
|
||||
uint64_t module_size = module->size();
|
||||
|
||||
// Ignore any failures for conflicting address ranges
|
||||
range_map_->StoreRange(base_address, module_size, module_index);
|
||||
|
||||
}
|
||||
unloaded_modules_ = modules.release();
|
||||
}
|
||||
|
||||
module_count_ = number_of_entries;
|
||||
valid_ = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
const MinidumpUnloadedModule* MinidumpUnloadedModuleList::GetModuleForAddress(
|
||||
uint64_t address) const {
|
||||
if (!valid_) {
|
||||
BPLOG(ERROR)
|
||||
<< "Invalid MinidumpUnloadedModuleList for GetModuleForAddress";
|
||||
return NULL;
|
||||
}
|
||||
|
||||
unsigned int module_index;
|
||||
if (!range_map_->RetrieveRange(address, &module_index, NULL /* base */,
|
||||
NULL /* delta */, NULL /* size */)) {
|
||||
BPLOG(INFO) << "MinidumpUnloadedModuleList has no module at "
|
||||
<< HexString(address);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return GetModuleAtIndex(module_index);
|
||||
}
|
||||
|
||||
const MinidumpUnloadedModule*
|
||||
MinidumpUnloadedModuleList::GetMainModule() const {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const MinidumpUnloadedModule*
|
||||
MinidumpUnloadedModuleList::GetModuleAtSequence(unsigned int sequence) const {
|
||||
if (!valid_) {
|
||||
BPLOG(ERROR)
|
||||
<< "Invalid MinidumpUnloadedModuleList for GetModuleAtSequence";
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (sequence >= module_count_) {
|
||||
BPLOG(ERROR) << "MinidumpUnloadedModuleList sequence out of range: "
|
||||
<< sequence << "/" << module_count_;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
unsigned int module_index;
|
||||
if (!range_map_->RetrieveRangeAtIndex(sequence, &module_index,
|
||||
NULL /* base */, NULL /* delta */,
|
||||
NULL /* size */)) {
|
||||
BPLOG(ERROR) << "MinidumpUnloadedModuleList has no module at sequence "
|
||||
<< sequence;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return GetModuleAtIndex(module_index);
|
||||
}
|
||||
|
||||
const MinidumpUnloadedModule*
|
||||
MinidumpUnloadedModuleList::GetModuleAtIndex(
|
||||
unsigned int index) const {
|
||||
if (!valid_) {
|
||||
BPLOG(ERROR) << "Invalid MinidumpUnloadedModuleList for GetModuleAtIndex";
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (index >= module_count_) {
|
||||
BPLOG(ERROR) << "MinidumpUnloadedModuleList index out of range: "
|
||||
<< index << "/" << module_count_;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return &(*unloaded_modules_)[index];
|
||||
}
|
||||
|
||||
const CodeModules* MinidumpUnloadedModuleList::Copy() const {
|
||||
return new BasicCodeModules(this);
|
||||
}
|
||||
|
||||
vector<linked_ptr<const CodeModule>>
|
||||
MinidumpUnloadedModuleList::GetShrunkRangeModules() const {
|
||||
return vector<linked_ptr<const CodeModule> >();
|
||||
}
|
||||
|
||||
bool MinidumpUnloadedModuleList::IsModuleShrinkEnabled() const {
|
||||
return range_map_->IsShrinkDownEnabled();
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// MinidumpMiscInfo
|
||||
//
|
||||
|
@ -4603,6 +4987,12 @@ MinidumpSystemInfo* Minidump::GetSystemInfo() {
|
|||
}
|
||||
|
||||
|
||||
MinidumpUnloadedModuleList* Minidump::GetUnloadedModuleList() {
|
||||
MinidumpUnloadedModuleList* unloaded_module_list;
|
||||
return GetStream(&unloaded_module_list);
|
||||
}
|
||||
|
||||
|
||||
MinidumpMiscInfo* Minidump::GetMiscInfo() {
|
||||
MinidumpMiscInfo* misc_info;
|
||||
return GetStream(&misc_info);
|
||||
|
|
|
@ -56,6 +56,8 @@ using google_breakpad::MinidumpMemoryRegion;
|
|||
using google_breakpad::MinidumpModule;
|
||||
using google_breakpad::MinidumpModuleList;
|
||||
using google_breakpad::MinidumpSystemInfo;
|
||||
using google_breakpad::MinidumpUnloadedModule;
|
||||
using google_breakpad::MinidumpUnloadedModuleList;
|
||||
using google_breakpad::MinidumpThread;
|
||||
using google_breakpad::MinidumpThreadList;
|
||||
using google_breakpad::SynthMinidump::Context;
|
||||
|
@ -63,6 +65,7 @@ using google_breakpad::SynthMinidump::Dump;
|
|||
using google_breakpad::SynthMinidump::Exception;
|
||||
using google_breakpad::SynthMinidump::Memory;
|
||||
using google_breakpad::SynthMinidump::Module;
|
||||
using google_breakpad::SynthMinidump::UnloadedModule;
|
||||
using google_breakpad::SynthMinidump::Section;
|
||||
using google_breakpad::SynthMinidump::Stream;
|
||||
using google_breakpad::SynthMinidump::String;
|
||||
|
@ -394,6 +397,61 @@ TEST(Dump, ThreadMissingContext) {
|
|||
ASSERT_EQ(reinterpret_cast<MinidumpContext*>(NULL), md_context);
|
||||
}
|
||||
|
||||
TEST(Dump, OneUnloadedModule) {
|
||||
Dump dump(0, kBigEndian);
|
||||
String module_name(dump, "unloaded module");
|
||||
|
||||
String csd_version(dump, "Windows 9000");
|
||||
SystemInfo system_info(dump, SystemInfo::windows_x86, csd_version);
|
||||
|
||||
UnloadedModule unloaded_module(
|
||||
dump,
|
||||
0xa90206ca83eb2852ULL,
|
||||
0xada542bd,
|
||||
module_name,
|
||||
0x34571371,
|
||||
0xb1054d2a);
|
||||
|
||||
dump.Add(&unloaded_module);
|
||||
dump.Add(&module_name);
|
||||
dump.Add(&system_info);
|
||||
dump.Add(&csd_version);
|
||||
dump.Finish();
|
||||
|
||||
string contents;
|
||||
ASSERT_TRUE(dump.GetContents(&contents));
|
||||
istringstream minidump_stream(contents);
|
||||
Minidump minidump(minidump_stream);
|
||||
ASSERT_TRUE(minidump.Read());
|
||||
ASSERT_EQ(2U, minidump.GetDirectoryEntryCount());
|
||||
|
||||
const MDRawDirectory *dir = minidump.GetDirectoryEntryAtIndex(1);
|
||||
ASSERT_TRUE(dir != NULL);
|
||||
EXPECT_EQ((uint32_t) MD_UNLOADED_MODULE_LIST_STREAM, dir->stream_type);
|
||||
|
||||
MinidumpUnloadedModuleList *md_unloaded_module_list =
|
||||
minidump.GetUnloadedModuleList();
|
||||
ASSERT_TRUE(md_unloaded_module_list != NULL);
|
||||
ASSERT_EQ(1U, md_unloaded_module_list->module_count());
|
||||
|
||||
const MinidumpUnloadedModule *md_unloaded_module =
|
||||
md_unloaded_module_list->GetModuleAtIndex(0);
|
||||
ASSERT_TRUE(md_unloaded_module != NULL);
|
||||
ASSERT_EQ(0xa90206ca83eb2852ULL, md_unloaded_module->base_address());
|
||||
ASSERT_EQ(0xada542bd, md_unloaded_module->size());
|
||||
ASSERT_EQ("unloaded module", md_unloaded_module->code_file());
|
||||
ASSERT_EQ("", md_unloaded_module->debug_file());
|
||||
// time_date_stamp and size_of_image concatenated
|
||||
ASSERT_EQ("B1054D2Aada542bd", md_unloaded_module->code_identifier());
|
||||
ASSERT_EQ("", md_unloaded_module->debug_identifier());
|
||||
|
||||
const MDRawUnloadedModule *md_raw_unloaded_module =
|
||||
md_unloaded_module->module();
|
||||
ASSERT_TRUE(md_raw_unloaded_module != NULL);
|
||||
ASSERT_EQ(0xb1054d2aU, md_raw_unloaded_module->time_date_stamp);
|
||||
ASSERT_EQ(0x34571371U, md_raw_unloaded_module->checksum);
|
||||
}
|
||||
|
||||
static const MDVSFixedFileInfo fixed_file_info = {
|
||||
0xb2fba33a, // signature
|
||||
0x33d7a728, // struct_version
|
||||
|
@ -813,6 +871,32 @@ TEST(Dump, BigDump) {
|
|||
dump.Add(&module3_name);
|
||||
dump.Add(&module3);
|
||||
|
||||
// Unloaded modules!
|
||||
uint64_t umodule1_base = 0xeb77da57b5d4cbdaULL;
|
||||
uint32_t umodule1_size = 0x83cd5a37;
|
||||
String umodule1_name(dump, "unloaded module one");
|
||||
UnloadedModule unloaded_module1(dump, umodule1_base, umodule1_size,
|
||||
umodule1_name);
|
||||
dump.Add(&umodule1_name);
|
||||
dump.Add(&unloaded_module1);
|
||||
|
||||
uint64_t umodule2_base = 0xeb77da57b5d4cbdaULL;
|
||||
uint32_t umodule2_size = 0x83cd5a37;
|
||||
String umodule2_name(dump, "unloaded module two");
|
||||
UnloadedModule unloaded_module2(dump, umodule2_base, umodule2_size,
|
||||
umodule2_name);
|
||||
dump.Add(&umodule2_name);
|
||||
dump.Add(&unloaded_module2);
|
||||
|
||||
uint64_t umodule3_base = 0xeb77da5839a20000ULL;
|
||||
uint32_t umodule3_size = 0x83cd5a37;
|
||||
String umodule3_name(dump, "unloaded module three");
|
||||
UnloadedModule unloaded_module3(dump, umodule3_base, umodule3_size,
|
||||
umodule3_name);
|
||||
dump.Add(&umodule3_name);
|
||||
dump.Add(&unloaded_module3);
|
||||
|
||||
|
||||
// Add one more memory region, on top of the five stacks.
|
||||
Memory memory5(dump, 0x61979e828040e564ULL);
|
||||
memory5.Append("contents of memory 5");
|
||||
|
@ -825,7 +909,7 @@ TEST(Dump, BigDump) {
|
|||
istringstream minidump_stream(contents);
|
||||
Minidump minidump(minidump_stream);
|
||||
ASSERT_TRUE(minidump.Read());
|
||||
ASSERT_EQ(4U, minidump.GetDirectoryEntryCount());
|
||||
ASSERT_EQ(5U, minidump.GetDirectoryEntryCount());
|
||||
|
||||
// Check the threads.
|
||||
MinidumpThreadList *thread_list = minidump.GetThreadList();
|
||||
|
@ -882,6 +966,29 @@ TEST(Dump, BigDump) {
|
|||
md_module_list->GetModuleAtIndex(1)->base_address());
|
||||
EXPECT_EQ(0x95fc1544da321b6cULL,
|
||||
md_module_list->GetModuleAtIndex(2)->base_address());
|
||||
|
||||
// Check unloaded modules
|
||||
MinidumpUnloadedModuleList *md_unloaded_module_list =
|
||||
minidump.GetUnloadedModuleList();
|
||||
ASSERT_TRUE(md_unloaded_module_list != NULL);
|
||||
ASSERT_EQ(3U, md_unloaded_module_list->module_count());
|
||||
EXPECT_EQ(umodule1_base,
|
||||
md_unloaded_module_list->GetModuleAtIndex(0)->base_address());
|
||||
EXPECT_EQ(umodule2_base,
|
||||
md_unloaded_module_list->GetModuleAtIndex(1)->base_address());
|
||||
EXPECT_EQ(umodule3_base,
|
||||
md_unloaded_module_list->GetModuleAtIndex(2)->base_address());
|
||||
|
||||
const MinidumpUnloadedModule *umodule =
|
||||
md_unloaded_module_list->GetModuleForAddress(
|
||||
umodule1_base + umodule1_size / 2);
|
||||
EXPECT_EQ(umodule1_base, umodule->base_address());
|
||||
|
||||
umodule = md_unloaded_module_list->GetModuleAtSequence(0);
|
||||
EXPECT_EQ(umodule1_base, umodule->base_address());
|
||||
|
||||
EXPECT_EQ(NULL, md_unloaded_module_list->GetMainModule());
|
||||
|
||||
}
|
||||
|
||||
TEST(Dump, OneMemoryInfo) {
|
||||
|
|
|
@ -297,6 +297,26 @@ const MDVSFixedFileInfo Module::stock_version_info = {
|
|||
0 // file_date_lo
|
||||
};
|
||||
|
||||
UnloadedModule::UnloadedModule(const Dump &dump,
|
||||
uint64_t base_of_image,
|
||||
uint32_t size_of_image,
|
||||
const String &name,
|
||||
uint32_t checksum,
|
||||
uint32_t time_date_stamp) : Section(dump) {
|
||||
D64(base_of_image);
|
||||
D32(size_of_image);
|
||||
D32(checksum);
|
||||
D32(time_date_stamp);
|
||||
name.CiteStringIn(this);
|
||||
}
|
||||
|
||||
UnloadedModuleList::UnloadedModuleList(const Dump &dump, uint32_t type)
|
||||
: List<UnloadedModule>(dump, type, false) {
|
||||
D32(sizeof(MDRawUnloadedModuleList));
|
||||
D32(sizeof(MDRawUnloadedModule));
|
||||
D32(count_label_);
|
||||
}
|
||||
|
||||
Exception::Exception(const Dump &dump,
|
||||
const Context &context,
|
||||
uint32_t thread_id,
|
||||
|
@ -328,6 +348,7 @@ Dump::Dump(uint64_t flags,
|
|||
stream_count_(0),
|
||||
thread_list_(*this, MD_THREAD_LIST_STREAM),
|
||||
module_list_(*this, MD_MODULE_LIST_STREAM),
|
||||
unloaded_module_list_(*this, MD_UNLOADED_MODULE_LIST_STREAM),
|
||||
memory_list_(*this, MD_MEMORY_LIST_STREAM)
|
||||
{
|
||||
D32(MD_HEADER_SIGNATURE);
|
||||
|
@ -375,9 +396,15 @@ Dump &Dump::Add(Module *module) {
|
|||
return *this;
|
||||
}
|
||||
|
||||
Dump &Dump::Add(UnloadedModule *unloaded_module) {
|
||||
unloaded_module_list_.Add(unloaded_module);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void Dump::Finish() {
|
||||
if (!thread_list_.Empty()) Add(&thread_list_);
|
||||
if (!module_list_.Empty()) Add(&module_list_);
|
||||
if (!unloaded_module_list_.Empty()) Add(&unloaded_module_list_);
|
||||
if (!memory_list_.Empty()) Add(&memory_list_);
|
||||
|
||||
// Create the stream directory. We don't use
|
||||
|
|
|
@ -138,6 +138,13 @@ class Section: public test_assembler::Section {
|
|||
explicit Section(const Dump &dump);
|
||||
|
||||
// Append an MDLocationDescriptor referring to this section to SECTION.
|
||||
// If 'this' is NULL, append a descriptor with a zero length and MDRVA.
|
||||
//
|
||||
// (I couldn't find the language in the C++ standard that says that
|
||||
// invoking member functions of a NULL pointer to a class type is
|
||||
// bad, if such language exists. Having this function handle NULL
|
||||
// 'this' is convenient, but if it causes trouble, it's not hard to
|
||||
// do differently.)
|
||||
void CiteLocationIn(test_assembler::Section *section) const;
|
||||
|
||||
// Note that this section's contents are complete, and that it has
|
||||
|
@ -263,6 +270,16 @@ class Module: public Section {
|
|||
static const MDVSFixedFileInfo stock_version_info;
|
||||
};
|
||||
|
||||
class UnloadedModule: public Section {
|
||||
public:
|
||||
UnloadedModule(const Dump &dump,
|
||||
uint64_t base_of_image,
|
||||
uint32_t size_of_image,
|
||||
const String &name,
|
||||
uint32_t checksum = 0,
|
||||
uint32_t time_date_stamp = 1262805309);
|
||||
};
|
||||
|
||||
class Exception : public Stream {
|
||||
public:
|
||||
Exception(const Dump &dump,
|
||||
|
@ -301,9 +318,20 @@ class List: public Stream {
|
|||
|
||||
private:
|
||||
size_t count_;
|
||||
|
||||
protected:
|
||||
// This constructor allows derived lists to specify their own layout
|
||||
// rather than starting with count as specified in the public constructor.
|
||||
List(const Dump &dump, uint32_t type, bool) : Stream(dump, type), count_(0) {}
|
||||
|
||||
Label count_label_;
|
||||
};
|
||||
|
||||
class UnloadedModuleList : public List<UnloadedModule> {
|
||||
public:
|
||||
UnloadedModuleList(const Dump &dump, uint32_t type);
|
||||
};
|
||||
|
||||
class Dump: public test_assembler::Section {
|
||||
public:
|
||||
|
||||
|
@ -326,6 +354,7 @@ class Dump: public test_assembler::Section {
|
|||
Dump &Add(Memory *object); // append, record in memory list
|
||||
Dump &Add(Thread *object); // append, record in thread list
|
||||
Dump &Add(Module *object); // append, record in module list
|
||||
Dump &Add(UnloadedModule *object); // append, record in unloaded module list
|
||||
|
||||
// Complete the construction of the minidump, given the Add calls
|
||||
// we've seen up to this point. After this call, this Dump's
|
||||
|
@ -352,6 +381,10 @@ class Dump: public test_assembler::Section {
|
|||
// Add(Module *) calls.
|
||||
List<Module> module_list_;
|
||||
|
||||
// This minidump's unloaded module list. We construct this incrementally from
|
||||
// Add(UnloadedModule *) calls.
|
||||
UnloadedModuleList unloaded_module_list_;
|
||||
|
||||
// This minidump's memory list. We construct this incrementally from
|
||||
// Add(Memory *) calls. This is actually a list of MDMemoryDescriptors,
|
||||
// not memory ranges --- thus the odd type.
|
||||
|
|
Loading…
Reference in a new issue