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 @@ -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; 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> GetShrunkRangeModules() const override; + bool IsModuleShrinkEnabled() const override; + + protected: + explicit MinidumpUnloadedModuleList(Minidump* minidump_); + + private: + friend class Minidump; + + typedef vector 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 *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 @@ -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::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()), + 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 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> +MinidumpUnloadedModuleList::GetShrunkRangeModules() const { + return vector >(); +} + +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(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(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 { + 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_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.