aboutsummaryrefslogtreecommitdiff
path: root/src/processor
diff options
context:
space:
mode:
authorJoshua Peraza <jperaza@chromium.org>2016-12-16 12:10:33 -0800
committerJoshua Peraza <jperaza@chromium.org>2016-12-16 20:15:04 +0000
commitc2d969cb1050803961a53cfdbbcff5c69e579ebb (patch)
treed820694a130c38d7ab4898e26db38b13d606434b /src/processor
parentFix unit tests expecting no output when a microdump is suppressed. (diff)
downloadbreakpad-c2d969cb1050803961a53cfdbbcff5c69e579ebb.tar.xz
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>
Diffstat (limited to 'src/processor')
-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
4 files changed, 558 insertions, 1 deletions
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.