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
This commit is contained in:
ted.mielczarek 2011-01-13 19:05:33 +00:00
parent 80745c59de
commit 7b8e2b7e09
4 changed files with 400 additions and 0 deletions

View File

@ -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

View File

@ -3439,6 +3439,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_) {

View File

@ -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",

View File

@ -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