mirror of
https://github.com/yuzu-emu/breakpad.git
synced 2024-11-28 02:04:15 +01:00
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:
parent
80745c59de
commit
7b8e2b7e09
@ -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
|
||||
|
@ -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_) {
|
||||
|
@ -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",
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user