aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorted.mielczarek <ted.mielczarek@4c0a9323-5329-0410-9bdc-e9ce6186880e>2011-01-13 19:05:33 +0000
committerted.mielczarek <ted.mielczarek@4c0a9323-5329-0410-9bdc-e9ce6186880e>2011-01-13 19:05:33 +0000
commit7b8e2b7e090b2d1223d0944d1e7da1d4c571bb5d (patch)
treeb181c7a3ebd4af86a2d1e2cbd3d10561660c7a29 /src
parentAdd structure definitions for the memory info list, as well as some other new... (diff)
downloadbreakpad-7b8e2b7e090b2d1223d0944d1e7da1d4c571bb5d.tar.xz
Add MinidumpMemoryInfo / MinidumpMemoryInfoList classes to expose MDRawMemoryInfo / MDRawMemoryInfoList via the Minidump class
R=mark at http://breakpad.appspot.com/255001 git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@755 4c0a9323-5329-0410-9bdc-e9ce6186880e
Diffstat (limited to 'src')
-rw-r--r--src/google_breakpad/processor/minidump.h71
-rw-r--r--src/processor/minidump.cc263
-rw-r--r--src/processor/minidump_dump.cc9
-rw-r--r--src/processor/minidump_unittest.cc57
4 files changed, 400 insertions, 0 deletions
diff --git a/src/google_breakpad/processor/minidump.h b/src/google_breakpad/processor/minidump.h
index f3f77677..012a8135 100644
--- a/src/google_breakpad/processor/minidump.h
+++ b/src/google_breakpad/processor/minidump.h
@@ -794,6 +794,76 @@ class MinidumpBreakpadInfo : public MinidumpStream {
MDRawBreakpadInfo breakpad_info_;
};
+// MinidumpMemoryInfo wraps MDRawMemoryInfo, which provides information
+// about mapped memory regions in a process, including their ranges
+// and protection.
+class MinidumpMemoryInfo : public MinidumpObject {
+ public:
+ const MDRawMemoryInfo* info() const { return valid_ ? &memory_info_ : NULL; }
+
+ // The address of the base of the memory region.
+ u_int64_t GetBase() const { return valid_ ? memory_info_.base_address : 0; }
+
+ // The size, in bytes, of the memory region.
+ u_int32_t GetSize() const { return valid_ ? memory_info_.region_size : 0; }
+
+ // Return true if the memory protection allows execution.
+ bool IsExecutable() const;
+
+ // Return true if the memory protection allows writing.
+ bool IsWritable() const;
+
+ // Print a human-readable representation of the object to stdout.
+ void Print();
+
+ private:
+ // These objects are managed by MinidumpMemoryInfoList.
+ friend class MinidumpMemoryInfoList;
+
+ explicit MinidumpMemoryInfo(Minidump* minidump);
+
+ // This works like MinidumpStream::Read, but is driven by
+ // MinidumpMemoryInfoList. No size checking is done, because
+ // MinidumpMemoryInfoList handles that directly.
+ bool Read();
+
+ MDRawMemoryInfo memory_info_;
+};
+
+// MinidumpMemoryInfoList contains a list of information about
+// mapped memory regions for a process in the form of MDRawMemoryInfo.
+// It maintains a map of these structures so that it may easily provide
+// info corresponding to a specific address.
+class MinidumpMemoryInfoList : public MinidumpStream {
+ public:
+ virtual ~MinidumpMemoryInfoList();
+
+ unsigned int info_count() const { return valid_ ? info_count_ : 0; }
+
+ const MinidumpMemoryInfo* GetMemoryInfoForAddress(u_int64_t address) const;
+ const MinidumpMemoryInfo* GetMemoryInfoAtIndex(unsigned int index) const;
+
+ // Print a human-readable representation of the object to stdout.
+ void Print();
+
+ private:
+ friend class Minidump;
+
+ typedef vector<MinidumpMemoryInfo> MinidumpMemoryInfos;
+
+ static const u_int32_t kStreamType = MD_MEMORY_INFO_LIST_STREAM;
+
+ explicit MinidumpMemoryInfoList(Minidump* minidump);
+
+ bool Read(u_int32_t expected_size);
+
+ // Access to memory info using addresses as the key.
+ RangeMap<u_int64_t, unsigned int> *range_map_;
+
+ MinidumpMemoryInfos* infos_;
+ u_int32_t info_count_;
+};
+
// Minidump is the user's interface to a minidump file. It wraps MDRawHeader
// and provides access to the minidump's top-level stream directory.
@@ -842,6 +912,7 @@ class Minidump {
MinidumpSystemInfo* GetSystemInfo();
MinidumpMiscInfo* GetMiscInfo();
MinidumpBreakpadInfo* GetBreakpadInfo();
+ MinidumpMemoryInfoList* GetMemoryInfoList();
// The next set of methods are provided for users who wish to access
// data in minidump files directly, while leveraging the rest of
diff --git a/src/processor/minidump.cc b/src/processor/minidump.cc
index 3d42c428..9e084401 100644
--- a/src/processor/minidump.cc
+++ b/src/processor/minidump.cc
@@ -3440,6 +3440,264 @@ void MinidumpBreakpadInfo::Print() {
//
+// MinidumpMemoryInfo
+//
+
+
+MinidumpMemoryInfo::MinidumpMemoryInfo(Minidump* minidump)
+ : MinidumpObject(minidump),
+ memory_info_() {
+}
+
+
+bool MinidumpMemoryInfo::IsExecutable() const {
+ u_int32_t protection =
+ memory_info_.protection & MD_MEMORY_PROTECTION_ACCESS_MASK;
+ return protection == MD_MEMORY_PROTECT_EXECUTE ||
+ protection == MD_MEMORY_PROTECT_EXECUTE_READ ||
+ protection == MD_MEMORY_PROTECT_EXECUTE_READWRITE;
+}
+
+
+bool MinidumpMemoryInfo::IsWritable() const {
+ u_int32_t protection =
+ memory_info_.protection & MD_MEMORY_PROTECTION_ACCESS_MASK;
+ return protection == MD_MEMORY_PROTECT_READWRITE ||
+ protection == MD_MEMORY_PROTECT_WRITECOPY ||
+ protection == MD_MEMORY_PROTECT_EXECUTE_READWRITE ||
+ protection == MD_MEMORY_PROTECT_EXECUTE_WRITECOPY;
+}
+
+
+bool MinidumpMemoryInfo::Read() {
+ valid_ = false;
+
+ if (!minidump_->ReadBytes(&memory_info_, sizeof(memory_info_))) {
+ BPLOG(ERROR) << "MinidumpMemoryInfo cannot read memory info";
+ return false;
+ }
+
+ if (minidump_->swap()) {
+ Swap(&memory_info_.base_address);
+ Swap(&memory_info_.allocation_base);
+ Swap(&memory_info_.allocation_protection);
+ Swap(&memory_info_.region_size);
+ Swap(&memory_info_.state);
+ Swap(&memory_info_.protection);
+ Swap(&memory_info_.type);
+ }
+
+ // Check for base + size overflow or undersize.
+ if (memory_info_.region_size == 0 ||
+ memory_info_.region_size > numeric_limits<u_int64_t>::max() -
+ memory_info_.base_address) {
+ BPLOG(ERROR) << "MinidumpMemoryInfo has a memory region problem, " <<
+ HexString(memory_info_.base_address) << "+" <<
+ HexString(memory_info_.region_size);
+ return false;
+ }
+
+ valid_ = true;
+ return true;
+}
+
+
+void MinidumpMemoryInfo::Print() {
+ if (!valid_) {
+ BPLOG(ERROR) << "MinidumpMemoryInfo cannot print invalid data";
+ return;
+ }
+
+ printf("MDRawMemoryInfo\n");
+ printf(" base_address = 0x%" PRIx64 "\n",
+ memory_info_.base_address);
+ printf(" allocation_base = 0x%" PRIx64 "\n",
+ memory_info_.allocation_base);
+ printf(" allocation_protection = 0x%x\n",
+ memory_info_.allocation_protection);
+ printf(" region_size = 0x%" PRIx64 "\n", memory_info_.region_size);
+ printf(" state = 0x%x\n", memory_info_.state);
+ printf(" protection = 0x%x\n", memory_info_.protection);
+ printf(" type = 0x%x\n", memory_info_.type);
+}
+
+
+//
+// MinidumpMemoryInfoList
+//
+
+
+MinidumpMemoryInfoList::MinidumpMemoryInfoList(Minidump* minidump)
+ : MinidumpStream(minidump),
+ range_map_(new RangeMap<u_int64_t, unsigned int>()),
+ infos_(NULL),
+ info_count_(0) {
+}
+
+
+MinidumpMemoryInfoList::~MinidumpMemoryInfoList() {
+ delete range_map_;
+ delete infos_;
+}
+
+
+bool MinidumpMemoryInfoList::Read(u_int32_t expected_size) {
+ // Invalidate cached data.
+ delete infos_;
+ infos_ = NULL;
+ range_map_->Clear();
+ info_count_ = 0;
+
+ valid_ = false;
+
+ MDRawMemoryInfoList header;
+ if (expected_size < sizeof(MDRawMemoryInfoList)) {
+ BPLOG(ERROR) << "MinidumpMemoryInfoList header size mismatch, " <<
+ expected_size << " < " << sizeof(MDRawMemoryInfoList);
+ return false;
+ }
+ if (!minidump_->ReadBytes(&header, sizeof(header))) {
+ BPLOG(ERROR) << "MinidumpMemoryInfoList could not read header";
+ return false;
+ }
+
+ if (minidump_->swap()) {
+ Swap(&header.size_of_header);
+ Swap(&header.size_of_entry);
+ Swap(&header.number_of_entries);
+ }
+
+ // Sanity check that the header is the expected size.
+ //TODO(ted): could possibly handle this more gracefully, assuming
+ // that future versions of the structs would be backwards-compatible.
+ if (header.size_of_header != sizeof(MDRawMemoryInfoList)) {
+ BPLOG(ERROR) << "MinidumpMemoryInfoList header size mismatch, " <<
+ header.size_of_header << " != " <<
+ sizeof(MDRawMemoryInfoList);
+ return false;
+ }
+
+ // Sanity check that the entries are the expected size.
+ if (header.size_of_entry != sizeof(MDRawMemoryInfo)) {
+ BPLOG(ERROR) << "MinidumpMemoryInfoList entry size mismatch, " <<
+ header.size_of_entry << " != " <<
+ sizeof(MDRawMemoryInfo);
+ return false;
+ }
+
+ if (header.number_of_entries >
+ numeric_limits<u_int32_t>::max() / sizeof(MDRawMemoryInfo)) {
+ BPLOG(ERROR) << "MinidumpMemoryInfoList info count " <<
+ header.number_of_entries <<
+ " would cause multiplication overflow";
+ return false;
+ }
+
+ if (expected_size != sizeof(MDRawMemoryInfoList) +
+ header.number_of_entries * sizeof(MDRawMemoryInfo)) {
+ BPLOG(ERROR) << "MinidumpMemoryInfoList size mismatch, " << expected_size <<
+ " != " << sizeof(MDRawMemoryInfoList) +
+ header.number_of_entries * sizeof(MDRawMemoryInfo);
+ return false;
+ }
+
+ if (header.number_of_entries != 0) {
+ scoped_ptr<MinidumpMemoryInfos> infos(
+ new MinidumpMemoryInfos(header.number_of_entries,
+ MinidumpMemoryInfo(minidump_)));
+
+ for (unsigned int index = 0;
+ index < header.number_of_entries;
+ ++index) {
+ MinidumpMemoryInfo* info = &(*infos)[index];
+
+ // Assume that the file offset is correct after the last read.
+ if (!info->Read()) {
+ BPLOG(ERROR) << "MinidumpMemoryInfoList cannot read info " <<
+ index << "/" << header.number_of_entries;
+ return false;
+ }
+
+ u_int64_t base_address = info->GetBase();
+ u_int32_t region_size = info->GetSize();
+
+ if (!range_map_->StoreRange(base_address, region_size, index)) {
+ BPLOG(ERROR) << "MinidumpMemoryInfoList could not store"
+ " memory region " <<
+ index << "/" << header.number_of_entries << ", " <<
+ HexString(base_address) << "+" <<
+ HexString(region_size);
+ return false;
+ }
+ }
+
+ infos_ = infos.release();
+ }
+
+ info_count_ = header.number_of_entries;
+
+ valid_ = true;
+ return true;
+}
+
+
+const MinidumpMemoryInfo* MinidumpMemoryInfoList::GetMemoryInfoAtIndex(
+ unsigned int index) const {
+ if (!valid_) {
+ BPLOG(ERROR) << "Invalid MinidumpMemoryInfoList for GetMemoryInfoAtIndex";
+ return NULL;
+ }
+
+ if (index >= info_count_) {
+ BPLOG(ERROR) << "MinidumpMemoryInfoList index out of range: " <<
+ index << "/" << info_count_;
+ return NULL;
+ }
+
+ return &(*infos_)[index];
+}
+
+
+const MinidumpMemoryInfo* MinidumpMemoryInfoList::GetMemoryInfoForAddress(
+ u_int64_t address) const {
+ if (!valid_) {
+ BPLOG(ERROR) << "Invalid MinidumpMemoryInfoList for"
+ " GetMemoryInfoForAddress";
+ return NULL;
+ }
+
+ unsigned int info_index;
+ if (!range_map_->RetrieveRange(address, &info_index, NULL, NULL)) {
+ BPLOG(INFO) << "MinidumpMemoryInfoList has no memory info at " <<
+ HexString(address);
+ return NULL;
+ }
+
+ return GetMemoryInfoAtIndex(info_index);
+}
+
+
+void MinidumpMemoryInfoList::Print() {
+ if (!valid_) {
+ BPLOG(ERROR) << "MinidumpMemoryInfoList cannot print invalid data";
+ return;
+ }
+
+ printf("MinidumpMemoryInfoList\n");
+ printf(" info_count = %d\n", info_count_);
+ printf("\n");
+
+ for (unsigned int info_index = 0;
+ info_index < info_count_;
+ ++info_index) {
+ printf("info[%d]\n", info_index);
+ (*infos_)[info_index].Print();
+ printf("\n");
+ }
+}
+
+
+//
// Minidump
//
@@ -3681,6 +3939,11 @@ MinidumpBreakpadInfo* Minidump::GetBreakpadInfo() {
return GetStream(&breakpad_info);
}
+MinidumpMemoryInfoList* Minidump::GetMemoryInfoList() {
+ MinidumpMemoryInfoList* memory_info_list;
+ return GetStream(&memory_info_list);
+}
+
void Minidump::Print() {
if (!valid_) {
diff --git a/src/processor/minidump_dump.cc b/src/processor/minidump_dump.cc
index 9581da9e..3da2b903 100644
--- a/src/processor/minidump_dump.cc
+++ b/src/processor/minidump_dump.cc
@@ -45,6 +45,7 @@ namespace {
using google_breakpad::Minidump;
using google_breakpad::MinidumpThreadList;
using google_breakpad::MinidumpModuleList;
+using google_breakpad::MinidumpMemoryInfoList;
using google_breakpad::MinidumpMemoryList;
using google_breakpad::MinidumpException;
using google_breakpad::MinidumpAssertion;
@@ -160,6 +161,14 @@ static bool PrintMinidumpDump(const char *minidump_file) {
breakpad_info->Print();
}
+ MinidumpMemoryInfoList *memory_info_list = minidump.GetMemoryInfoList();
+ if (!memory_info_list) {
+ ++errors;
+ BPLOG(ERROR) << "minidump.GetMemoryInfoList() failed";
+ } else {
+ memory_info_list->Print();
+ }
+
DumpRawStream(&minidump,
MD_LINUX_CMD_LINE,
"MD_LINUX_CMD_LINE",
diff --git a/src/processor/minidump_unittest.cc b/src/processor/minidump_unittest.cc
index 9b459ccc..13876df5 100644
--- a/src/processor/minidump_unittest.cc
+++ b/src/processor/minidump_unittest.cc
@@ -46,6 +46,8 @@ namespace {
using google_breakpad::Minidump;
using google_breakpad::MinidumpContext;
+using google_breakpad::MinidumpMemoryInfo;
+using google_breakpad::MinidumpMemoryInfoList;
using google_breakpad::MinidumpMemoryList;
using google_breakpad::MinidumpMemoryRegion;
using google_breakpad::MinidumpModule;
@@ -531,4 +533,59 @@ TEST(Dump, BigDump) {
md_module_list->GetModuleAtIndex(2)->base_address());
}
+TEST(Dump, OneMemoryInfo) {
+ Dump dump(0, kBigEndian);
+ Stream stream(dump, MD_MEMORY_INFO_LIST_STREAM);
+
+ // Add the MDRawMemoryInfoList header.
+ const u_int64_t kNumberOfEntries = 1;
+ stream.D32(sizeof(MDRawMemoryInfoList)) // size_of_header
+ .D32(sizeof(MDRawMemoryInfo)) // size_of_entry
+ .D64(kNumberOfEntries); // number_of_entries
+
+
+ // Now add a MDRawMemoryInfo entry.
+ const u_int64_t kBaseAddress = 0x1000;
+ const u_int64_t kRegionSize = 0x2000;
+ stream.D64(kBaseAddress) // base_address
+ .D64(kBaseAddress) // allocation_base
+ .D32(MD_MEMORY_PROTECT_EXECUTE_READWRITE) // allocation_protection
+ .D32(0) // __alignment1
+ .D64(kRegionSize) // region_size
+ .D32(MD_MEMORY_STATE_COMMIT) // state
+ .D32(MD_MEMORY_PROTECT_EXECUTE_READWRITE) // protection
+ .D32(MD_MEMORY_TYPE_PRIVATE) // type
+ .D32(0); // __alignment2
+
+ dump.Add(&stream);
+ dump.Finish();
+
+ string contents;
+ ASSERT_TRUE(dump.GetContents(&contents));
+ istringstream minidump_stream(contents);
+ Minidump minidump(minidump_stream);
+ ASSERT_TRUE(minidump.Read());
+ ASSERT_EQ(1U, minidump.GetDirectoryEntryCount());
+
+ const MDRawDirectory *dir = minidump.GetDirectoryEntryAtIndex(0);
+ ASSERT_TRUE(dir != NULL);
+ EXPECT_EQ((u_int32_t) MD_MEMORY_INFO_LIST_STREAM, dir->stream_type);
+
+ MinidumpMemoryInfoList *info_list = minidump.GetMemoryInfoList();
+ ASSERT_TRUE(info_list != NULL);
+ ASSERT_EQ(1U, info_list->info_count());
+
+ const MinidumpMemoryInfo *info1 = info_list->GetMemoryInfoAtIndex(0);
+ ASSERT_EQ(kBaseAddress, info1->GetBase());
+ ASSERT_EQ(kRegionSize, info1->GetSize());
+ ASSERT_TRUE(info1->IsExecutable());
+ ASSERT_TRUE(info1->IsWritable());
+
+ // Should get back the same memory region here.
+ const MinidumpMemoryInfo *info2 =
+ info_list->GetMemoryInfoForAddress(kBaseAddress + kRegionSize / 2);
+ ASSERT_EQ(kBaseAddress, info2->GetBase());
+ ASSERT_EQ(kRegionSize, info2->GetSize());
+}
+
} // namespace