aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/google_breakpad/common/minidump_format.h14
-rw-r--r--src/google_breakpad/processor/minidump.h113
-rw-r--r--src/processor/minidump.cc390
-rw-r--r--src/processor/minidump_unittest.cc109
-rw-r--r--src/processor/synth_minidump.cc27
-rw-r--r--src/processor/synth_minidump.h33
6 files changed, 685 insertions, 1 deletions
diff --git a/src/google_breakpad/common/minidump_format.h b/src/google_breakpad/common/minidump_format.h
index 251e503d..b99c8f7c 100644
--- a/src/google_breakpad/common/minidump_format.h
+++ b/src/google_breakpad/common/minidump_format.h
@@ -677,6 +677,20 @@ typedef enum {
} 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;
uint16_t day_of_week;
diff --git a/src/google_breakpad/processor/minidump.h b/src/google_breakpad/processor/minidump.h
index c8c3cd48..1bfc7d9e 100644
--- a/src/google_breakpad/processor/minidump.h
+++ b/src/google_breakpad/processor/minidump.h
@@ -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();
diff --git a/src/processor/minidump.cc b/src/processor/minidump.cc
index 929913c0..7226d34a 100644
--- a/src/processor/minidump.cc
+++ b/src/processor/minidump.cc
@@ -3510,6 +3510,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);
diff --git a/src/processor/minidump_unittest.cc b/src/processor/minidump_unittest.cc
index d29e9f4e..036d03f1 100644
--- a/src/processor/minidump_unittest.cc
+++ b/src/processor/minidump_unittest.cc
@@ -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) {
diff --git a/src/processor/synth_minidump.cc b/src/processor/synth_minidump.cc
index ade150ef..aa86d248 100644
--- a/src/processor/synth_minidump.cc
+++ b/src/processor/synth_minidump.cc
@@ -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
diff --git a/src/processor/synth_minidump.h b/src/processor/synth_minidump.h
index f1635b93..8f49cfff 100644
--- a/src/processor/synth_minidump.h
+++ b/src/processor/synth_minidump.h
@@ -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.