mirror of
https://git.suyu.dev/suyu/breakpad.git
synced 2026-01-01 20:24:40 +01:00
Replace MMappedRange with MinidumpMemoryRange.
This patch is part of a bigger patch that helps merging the breakpad code with the modified version in Chromium OS. The MemoryRange class was added in r895 (http://breakpad.appspot.com/332001), which is largely based on MMappedRange but generalized to be used in other code. However, MemoryRange does not support minidump data structures. This patch adds a MinidumpMemoryRange class that extends MemoryRange to handle minidump data structures, which can then replace MMappedRange. As with MemoryRange, MinidumpMemoryRange is unit tested. BUG=455 TEST=Tested the following: 1. Build on 32-bit and 64-bit Linux with gcc 4.4.3 and gcc 4.6. 2. Build on Mac OS X 10.6.8 with gcc 4.2 and clang 3.0 (with latest gmock). 3. All unit tests pass. 4. Run minidump-2-core to covnert a minidump file to a core file. Review URL: http://breakpad.appspot.com/335001 git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@898 4c0a9323-5329-0410-9bdc-e9ce6186880e
This commit is contained in:
parent
5951dd28af
commit
33c135a16f
6 changed files with 523 additions and 123 deletions
|
|
@ -48,9 +48,10 @@
|
|||
|
||||
#include "client/linux/minidump_writer/minidump_extension_linux.h"
|
||||
#include "common/linux/memory_mapped_file.h"
|
||||
#include "google_breakpad/common/minidump_format.h"
|
||||
#include "google_breakpad/common/minidump_cpu_x86.h"
|
||||
#include "google_breakpad/common/minidump_format.h"
|
||||
#include "third_party/lss/linux_syscall_support.h"
|
||||
#include "tools/linux/md2core/minidump_memory_range.h"
|
||||
|
||||
|
||||
#if __WORDSIZE == 64
|
||||
|
|
@ -76,6 +77,7 @@
|
|||
#endif
|
||||
|
||||
using google_breakpad::MemoryMappedFile;
|
||||
using google_breakpad::MinidumpMemoryRange;
|
||||
|
||||
static const MDRVA kInvalidMDRVA = static_cast<MDRVA>(-1);
|
||||
static bool verbose;
|
||||
|
|
@ -106,63 +108,6 @@ writea(int fd, const void* idata, size_t length) {
|
|||
return true;
|
||||
}
|
||||
|
||||
// A range of a mmaped file.
|
||||
class MMappedRange {
|
||||
public:
|
||||
MMappedRange(const void* data, size_t length)
|
||||
: data_(reinterpret_cast<const uint8_t*>(data)),
|
||||
length_(length) {
|
||||
}
|
||||
|
||||
// Get an object of |length| bytes at |offset| and return a pointer to it
|
||||
// unless it's out of bounds.
|
||||
const void* GetObject(size_t offset, size_t length) const {
|
||||
if (offset + length < offset)
|
||||
return NULL;
|
||||
if (offset + length > length_)
|
||||
return NULL;
|
||||
return data_ + offset;
|
||||
}
|
||||
|
||||
// Get element |index| of an array of objects of length |length| starting at
|
||||
// |offset| bytes. Return NULL if out of bounds.
|
||||
const void* GetArrayElement(size_t offset, size_t length,
|
||||
unsigned index) const {
|
||||
const size_t element_offset = offset + index * length;
|
||||
return GetObject(element_offset, length);
|
||||
}
|
||||
|
||||
// Get a zero-terminated string. This method only works correctly for ASCII
|
||||
// characters and does not convert between UTF-16 and UTF-8.
|
||||
const std::string GetString(size_t offset) const {
|
||||
const MDString* s = (const MDString*) GetObject(offset, sizeof(MDString));
|
||||
const u_int16_t* buf = &s->buffer[0];
|
||||
std::string str;
|
||||
for (unsigned i = 0; i < s->length && buf[i]; ++i) {
|
||||
str.push_back(buf[i]);
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
// Return a new range which is a subset of this range.
|
||||
MMappedRange Subrange(const MDLocationDescriptor& location) const {
|
||||
if (location.rva > length_ ||
|
||||
location.rva + location.data_size < location.rva ||
|
||||
location.rva + location.data_size > length_) {
|
||||
return MMappedRange(NULL, 0);
|
||||
}
|
||||
|
||||
return MMappedRange(data_ + location.rva, location.data_size);
|
||||
}
|
||||
|
||||
const uint8_t* data() const { return data_; }
|
||||
size_t length() const { return length_; }
|
||||
|
||||
private:
|
||||
const uint8_t* const data_;
|
||||
const size_t length_;
|
||||
};
|
||||
|
||||
/* Dynamically determines the byte sex of the system. Returns non-zero
|
||||
* for big-endian machines.
|
||||
*/
|
||||
|
|
@ -290,9 +235,9 @@ U16(const uint8_t* data) {
|
|||
}
|
||||
|
||||
static void
|
||||
ParseThreadRegisters(CrashedProcess::Thread* thread, MMappedRange range) {
|
||||
const MDRawContextX86* rawregs =
|
||||
(const MDRawContextX86*) range.GetObject(0, sizeof(MDRawContextX86));
|
||||
ParseThreadRegisters(CrashedProcess::Thread* thread,
|
||||
const MinidumpMemoryRange& range) {
|
||||
const MDRawContextX86* rawregs = range.GetData<MDRawContextX86>(0);
|
||||
|
||||
thread->regs.ebx = rawregs->ebx;
|
||||
thread->regs.ecx = rawregs->ecx;
|
||||
|
|
@ -336,9 +281,9 @@ ParseThreadRegisters(CrashedProcess::Thread* thread, MMappedRange range) {
|
|||
}
|
||||
#elif defined(__x86_64__)
|
||||
static void
|
||||
ParseThreadRegisters(CrashedProcess::Thread* thread, MMappedRange range) {
|
||||
const MDRawContextAMD64* rawregs =
|
||||
(const MDRawContextAMD64*) range.GetObject(0, sizeof(MDRawContextAMD64));
|
||||
ParseThreadRegisters(CrashedProcess::Thread* thread,
|
||||
const MinidumpMemoryRange& range) {
|
||||
const MDRawContextAMD64* rawregs = range.GetData<MDRawContextAMD64>(0);
|
||||
|
||||
thread->regs.r15 = rawregs->r15;
|
||||
thread->regs.r14 = rawregs->r14;
|
||||
|
|
@ -384,10 +329,9 @@ ParseThreadRegisters(CrashedProcess::Thread* thread, MMappedRange range) {
|
|||
#endif
|
||||
|
||||
static void
|
||||
ParseThreadList(CrashedProcess* crashinfo, MMappedRange range,
|
||||
const MMappedRange& full_file) {
|
||||
const uint32_t num_threads =
|
||||
*(const uint32_t*) range.GetObject(0, sizeof(uint32_t));
|
||||
ParseThreadList(CrashedProcess* crashinfo, const MinidumpMemoryRange& range,
|
||||
const MinidumpMemoryRange& full_file) {
|
||||
const uint32_t num_threads = *range.GetData<uint32_t>(0);
|
||||
if (verbose) {
|
||||
fprintf(stderr,
|
||||
"MD_THREAD_LIST_STREAM:\n"
|
||||
|
|
@ -399,11 +343,11 @@ ParseThreadList(CrashedProcess* crashinfo, MMappedRange range,
|
|||
CrashedProcess::Thread thread;
|
||||
memset(&thread, 0, sizeof(thread));
|
||||
const MDRawThread* rawthread =
|
||||
(MDRawThread*) range.GetArrayElement(sizeof(uint32_t),
|
||||
sizeof(MDRawThread), i);
|
||||
range.GetArrayElement<MDRawThread>(sizeof(uint32_t), i);
|
||||
thread.tid = rawthread->thread_id;
|
||||
thread.stack_addr = rawthread->stack.start_of_memory_range;
|
||||
MMappedRange stack_range = full_file.Subrange(rawthread->stack.memory);
|
||||
MinidumpMemoryRange stack_range =
|
||||
full_file.Subrange(rawthread->stack.memory);
|
||||
thread.stack = stack_range.data();
|
||||
thread.stack_length = rawthread->stack.memory.data_size;
|
||||
|
||||
|
|
@ -415,10 +359,9 @@ ParseThreadList(CrashedProcess* crashinfo, MMappedRange range,
|
|||
}
|
||||
|
||||
static void
|
||||
ParseSystemInfo(CrashedProcess* crashinfo, MMappedRange range,
|
||||
const MMappedRange &full_file) {
|
||||
const MDRawSystemInfo* sysinfo =
|
||||
(MDRawSystemInfo*) range.GetObject(0, sizeof(MDRawSystemInfo));
|
||||
ParseSystemInfo(CrashedProcess* crashinfo, const MinidumpMemoryRange& range,
|
||||
const MinidumpMemoryRange& full_file) {
|
||||
const MDRawSystemInfo* sysinfo = range.GetData<MDRawSystemInfo>(0);
|
||||
if (!sysinfo) {
|
||||
fprintf(stderr, "Failed to access MD_SYSTEM_INFO_STREAM\n");
|
||||
_exit(1);
|
||||
|
|
@ -442,7 +385,8 @@ ParseSystemInfo(CrashedProcess* crashinfo, MMappedRange range,
|
|||
#else
|
||||
#error "This code has not been ported to your platform yet"
|
||||
#endif
|
||||
if (!strstr(full_file.GetString(sysinfo->csd_version_rva).c_str(), "Linux")) {
|
||||
if (!strstr(full_file.GetAsciiMDString(sysinfo->csd_version_rva).c_str(),
|
||||
"Linux")) {
|
||||
fprintf(stderr, "This minidump was not generated by Linux.\n");
|
||||
_exit(1);
|
||||
}
|
||||
|
|
@ -478,13 +422,13 @@ ParseSystemInfo(CrashedProcess* crashinfo, MMappedRange range,
|
|||
fputs("\n", stderr);
|
||||
}
|
||||
fprintf(stderr, "OS: %s\n",
|
||||
full_file.GetString(sysinfo->csd_version_rva).c_str());
|
||||
full_file.GetAsciiMDString(sysinfo->csd_version_rva).c_str());
|
||||
fputs("\n\n", stderr);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
ParseCPUInfo(CrashedProcess* crashinfo, MMappedRange range) {
|
||||
ParseCPUInfo(CrashedProcess* crashinfo, const MinidumpMemoryRange& range) {
|
||||
if (verbose) {
|
||||
fputs("MD_LINUX_CPU_INFO:\n", stderr);
|
||||
fwrite(range.data(), range.length(), 1, stderr);
|
||||
|
|
@ -493,7 +437,8 @@ ParseCPUInfo(CrashedProcess* crashinfo, MMappedRange range) {
|
|||
}
|
||||
|
||||
static void
|
||||
ParseProcessStatus(CrashedProcess* crashinfo, MMappedRange range) {
|
||||
ParseProcessStatus(CrashedProcess* crashinfo,
|
||||
const MinidumpMemoryRange& range) {
|
||||
if (verbose) {
|
||||
fputs("MD_LINUX_PROC_STATUS:\n", stderr);
|
||||
fwrite(range.data(), range.length(), 1, stderr);
|
||||
|
|
@ -502,7 +447,7 @@ ParseProcessStatus(CrashedProcess* crashinfo, MMappedRange range) {
|
|||
}
|
||||
|
||||
static void
|
||||
ParseLSBRelease(CrashedProcess* crashinfo, MMappedRange range) {
|
||||
ParseLSBRelease(CrashedProcess* crashinfo, const MinidumpMemoryRange& range) {
|
||||
if (verbose) {
|
||||
fputs("MD_LINUX_LSB_RELEASE:\n", stderr);
|
||||
fwrite(range.data(), range.length(), 1, stderr);
|
||||
|
|
@ -511,7 +456,7 @@ ParseLSBRelease(CrashedProcess* crashinfo, MMappedRange range) {
|
|||
}
|
||||
|
||||
static void
|
||||
ParseMaps(CrashedProcess* crashinfo, MMappedRange range) {
|
||||
ParseMaps(CrashedProcess* crashinfo, const MinidumpMemoryRange& range) {
|
||||
if (verbose) {
|
||||
fputs("MD_LINUX_MAPS:\n", stderr);
|
||||
fwrite(range.data(), range.length(), 1, stderr);
|
||||
|
|
@ -557,7 +502,7 @@ ParseMaps(CrashedProcess* crashinfo, MMappedRange range) {
|
|||
}
|
||||
|
||||
static void
|
||||
ParseEnvironment(CrashedProcess* crashinfo, MMappedRange range) {
|
||||
ParseEnvironment(CrashedProcess* crashinfo, const MinidumpMemoryRange& range) {
|
||||
if (verbose) {
|
||||
fputs("MD_LINUX_ENVIRON:\n", stderr);
|
||||
char *env = new char[range.length()];
|
||||
|
|
@ -595,7 +540,7 @@ ParseEnvironment(CrashedProcess* crashinfo, MMappedRange range) {
|
|||
}
|
||||
|
||||
static void
|
||||
ParseAuxVector(CrashedProcess* crashinfo, MMappedRange range) {
|
||||
ParseAuxVector(CrashedProcess* crashinfo, const MinidumpMemoryRange& range) {
|
||||
// Some versions of Chrome erroneously used the MD_LINUX_AUXV stream value
|
||||
// when dumping /proc/$x/maps
|
||||
if (range.length() > 17) {
|
||||
|
|
@ -616,7 +561,7 @@ ParseAuxVector(CrashedProcess* crashinfo, MMappedRange range) {
|
|||
}
|
||||
|
||||
static void
|
||||
ParseCmdLine(CrashedProcess* crashinfo, MMappedRange range) {
|
||||
ParseCmdLine(CrashedProcess* crashinfo, const MinidumpMemoryRange& range) {
|
||||
// The command line is supposed to use NUL bytes to separate arguments.
|
||||
// As Chrome rewrites its own command line and (incorrectly) substitutes
|
||||
// spaces, this is often not the case in our minidump files.
|
||||
|
|
@ -664,10 +609,9 @@ ParseCmdLine(CrashedProcess* crashinfo, MMappedRange range) {
|
|||
}
|
||||
|
||||
static void
|
||||
ParseDSODebugInfo(CrashedProcess* crashinfo, MMappedRange range,
|
||||
const MMappedRange &full_file) {
|
||||
const MDRawDebug* debug =
|
||||
(MDRawDebug*) range.GetObject(0, sizeof(MDRawDebug));
|
||||
ParseDSODebugInfo(CrashedProcess* crashinfo, const MinidumpMemoryRange& range,
|
||||
const MinidumpMemoryRange& full_file) {
|
||||
const MDRawDebug* debug = range.GetData<MDRawDebug>(0);
|
||||
if (!debug) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -694,14 +638,13 @@ ParseDSODebugInfo(CrashedProcess* crashinfo, MMappedRange range,
|
|||
if (debug->map != kInvalidMDRVA) {
|
||||
for (int i = 0; i < debug->dso_count; ++i) {
|
||||
const MDRawLinkMap* link_map =
|
||||
(MDRawLinkMap*) full_file.GetArrayElement(debug->map,
|
||||
sizeof(MDRawLinkMap), i);
|
||||
full_file.GetArrayElement<MDRawLinkMap>(debug->map, i);
|
||||
if (link_map) {
|
||||
if (verbose) {
|
||||
fprintf(stderr,
|
||||
"#%03d: %p, %p, \"%s\"\n",
|
||||
i, link_map->addr, link_map->ld,
|
||||
full_file.GetString(link_map->name).c_str());
|
||||
full_file.GetAsciiMDString(link_map->name).c_str());
|
||||
}
|
||||
crashinfo->link_map.push_back(*link_map);
|
||||
}
|
||||
|
|
@ -713,9 +656,9 @@ ParseDSODebugInfo(CrashedProcess* crashinfo, MMappedRange range,
|
|||
}
|
||||
|
||||
static void
|
||||
ParseExceptionStream(CrashedProcess* crashinfo, MMappedRange range) {
|
||||
const MDRawExceptionStream* exp =
|
||||
(MDRawExceptionStream*) range.GetObject(0, sizeof(MDRawExceptionStream));
|
||||
ParseExceptionStream(CrashedProcess* crashinfo,
|
||||
const MinidumpMemoryRange& range) {
|
||||
const MDRawExceptionStream* exp = range.GetData<MDRawExceptionStream>(0);
|
||||
crashinfo->crashing_tid = exp->thread_id;
|
||||
crashinfo->fatal_signal = (int) exp->exception_record.exception_code;
|
||||
}
|
||||
|
|
@ -763,18 +706,16 @@ WriteThread(const CrashedProcess::Thread& thread, int fatal_signal) {
|
|||
}
|
||||
|
||||
static void
|
||||
ParseModuleStream(CrashedProcess* crashinfo, MMappedRange range,
|
||||
const MMappedRange &full_file) {
|
||||
ParseModuleStream(CrashedProcess* crashinfo, const MinidumpMemoryRange& range,
|
||||
const MinidumpMemoryRange& full_file) {
|
||||
if (verbose) {
|
||||
fputs("MD_MODULE_LIST_STREAM:\n", stderr);
|
||||
}
|
||||
const uint32_t num_mappings =
|
||||
*(const uint32_t*) range.GetObject(0, sizeof(uint32_t));
|
||||
const uint32_t num_mappings = *range.GetData<uint32_t>(0);
|
||||
for (unsigned i = 0; i < num_mappings; ++i) {
|
||||
CrashedProcess::Mapping mapping;
|
||||
const MDRawModule* rawmodule =
|
||||
(MDRawModule*) range.GetArrayElement(sizeof(uint32_t),
|
||||
MD_MODULE_SIZE, i);
|
||||
const MDRawModule* rawmodule = reinterpret_cast<const MDRawModule*>(
|
||||
range.GetArrayElement(sizeof(uint32_t), MD_MODULE_SIZE, i));
|
||||
mapping.start_address = rawmodule->base_of_image;
|
||||
mapping.end_address = rawmodule->size_of_image + rawmodule->base_of_image;
|
||||
|
||||
|
|
@ -785,9 +726,8 @@ ParseModuleStream(CrashedProcess* crashinfo, MMappedRange range,
|
|||
crashinfo->mappings[mapping.start_address] = mapping;
|
||||
}
|
||||
|
||||
const MDCVInfoPDB70* record =
|
||||
(const MDCVInfoPDB70*)full_file.GetObject(rawmodule->cv_record.rva,
|
||||
MDCVInfoPDB70_minsize);
|
||||
const MDCVInfoPDB70* record = reinterpret_cast<const MDCVInfoPDB70*>(
|
||||
full_file.GetData(rawmodule->cv_record.rva, MDCVInfoPDB70_minsize));
|
||||
char guid[40];
|
||||
sprintf(guid, "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X",
|
||||
record->signature.data1, record->signature.data2,
|
||||
|
|
@ -796,7 +736,8 @@ ParseModuleStream(CrashedProcess* crashinfo, MMappedRange range,
|
|||
record->signature.data4[2], record->signature.data4[3],
|
||||
record->signature.data4[4], record->signature.data4[5],
|
||||
record->signature.data4[6], record->signature.data4[7]);
|
||||
std::string filename = full_file.GetString(rawmodule->module_name_rva);
|
||||
std::string filename =
|
||||
full_file.GetAsciiMDString(rawmodule->module_name_rva);
|
||||
size_t slash = filename.find_last_of('/');
|
||||
std::string basename = slash == std::string::npos ?
|
||||
filename : filename.substr(slash + 1);
|
||||
|
|
@ -869,7 +810,7 @@ AddDataToMapping(CrashedProcess* crashinfo, const std::string& data,
|
|||
|
||||
static void
|
||||
AugmentMappings(CrashedProcess* crashinfo,
|
||||
const MMappedRange &full_file) {
|
||||
const MinidumpMemoryRange& full_file) {
|
||||
// For each thread, find the memory mapping that matches the thread's stack.
|
||||
// Then adjust the mapping to include the stack dump.
|
||||
for (unsigned i = 0; i < crashinfo->threads.size(); ++i) {
|
||||
|
|
@ -903,7 +844,7 @@ AugmentMappings(CrashedProcess* crashinfo,
|
|||
link_map.l_ld = (ElfW(Dyn)*)iter->ld;
|
||||
link_map.l_prev = prev;
|
||||
prev = (struct link_map*)(start_addr + data.size());
|
||||
std::string filename = full_file.GetString(iter->name);
|
||||
std::string filename = full_file.GetAsciiMDString(iter->name);
|
||||
|
||||
// Look up signature for this filename. If available, change filename
|
||||
// to point to GUID, instead.
|
||||
|
|
@ -976,10 +917,9 @@ main(int argc, char** argv) {
|
|||
return 1;
|
||||
}
|
||||
|
||||
MMappedRange dump(mapped_file.data(), mapped_file.size());
|
||||
MinidumpMemoryRange dump(mapped_file.data(), mapped_file.size());
|
||||
|
||||
const MDRawHeader* header =
|
||||
(const MDRawHeader*) dump.GetObject(0, sizeof(MDRawHeader));
|
||||
const MDRawHeader* header = dump.GetData<MDRawHeader>(0);
|
||||
|
||||
CrashedProcess crashinfo;
|
||||
|
||||
|
|
@ -988,8 +928,7 @@ main(int argc, char** argv) {
|
|||
bool ok = false;
|
||||
for (unsigned i = 0; i < header->stream_count; ++i) {
|
||||
const MDRawDirectory* dirent =
|
||||
(const MDRawDirectory*) dump.GetArrayElement(
|
||||
header->stream_directory_rva, sizeof(MDRawDirectory), i);
|
||||
dump.GetArrayElement<MDRawDirectory>(header->stream_directory_rva, i);
|
||||
switch (dirent->stream_type) {
|
||||
case MD_SYSTEM_INFO_STREAM:
|
||||
ParseSystemInfo(&crashinfo, dump.Subrange(dirent->location), dump);
|
||||
|
|
@ -1006,8 +945,7 @@ main(int argc, char** argv) {
|
|||
|
||||
for (unsigned i = 0; i < header->stream_count; ++i) {
|
||||
const MDRawDirectory* dirent =
|
||||
(const MDRawDirectory*) dump.GetArrayElement(
|
||||
header->stream_directory_rva, sizeof(MDRawDirectory), i);
|
||||
dump.GetArrayElement<MDRawDirectory>(header->stream_directory_rva, i);
|
||||
switch (dirent->stream_type) {
|
||||
case MD_THREAD_LIST_STREAM:
|
||||
ParseThreadList(&crashinfo, dump.Subrange(dirent->location), dump);
|
||||
|
|
|
|||
89
src/tools/linux/md2core/minidump_memory_range.h
Normal file
89
src/tools/linux/md2core/minidump_memory_range.h
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
// Copyright (c) 2011, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// minidump_memory_range.h: Define the google_breakpad::MinidumpMemoryRange
|
||||
// class, which adds methods for handling minidump specific data structures
|
||||
// on top of google_breakpad::MemoryRange. See common/memory_range.h for
|
||||
// more details on MemoryRange.
|
||||
|
||||
#ifndef TOOLS_LINUX_MD2CORE_MINIDUMP_MEMORY_RANGE_H_
|
||||
#define TOOLS_LINUX_MD2CORE_MINIDUMP_MEMORY_RANGE_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "common/memory_range.h"
|
||||
#include "google_breakpad/common/minidump_format.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
// A derived class of MemoryRange with added methods for handling minidump
|
||||
// specific data structures. To avoid virtual functions, it is not designed
|
||||
// to be used polymorphically.
|
||||
class MinidumpMemoryRange : public MemoryRange {
|
||||
public:
|
||||
MinidumpMemoryRange() {}
|
||||
|
||||
MinidumpMemoryRange(const void* data, size_t length)
|
||||
: MemoryRange(data, length) {}
|
||||
|
||||
// Returns a subrange of |length| bytes at |offset| bytes of this memory
|
||||
// range, or an empty range if the subrange is out of bounds.
|
||||
// This methods overrides the base implemementation in order to return
|
||||
// an instance of MinidumpMemoryRange instead of MemoryRange.
|
||||
MinidumpMemoryRange Subrange(size_t sub_offset, size_t sub_length) const {
|
||||
if (Covers(sub_offset, sub_length))
|
||||
return MinidumpMemoryRange(data() + sub_offset, sub_length);
|
||||
return MinidumpMemoryRange();
|
||||
}
|
||||
|
||||
// Returns a subrange that covers the offset and length specified by
|
||||
// |location|, or an empty range if the subrange is out of bounds.
|
||||
MinidumpMemoryRange Subrange(const MDLocationDescriptor& location) const {
|
||||
return MinidumpMemoryRange::Subrange(location.rva, location.data_size);
|
||||
}
|
||||
|
||||
// Gets a STL string from a MDString at |sub_offset| bytes of this memory
|
||||
// range. This method only works correctly for ASCII characters and does
|
||||
// not convert between UTF-16 and UTF-8.
|
||||
const std::string GetAsciiMDString(size_t sub_offset) const {
|
||||
std::string str;
|
||||
const MDString* md_str = GetData<MDString>(sub_offset);
|
||||
if (md_str) {
|
||||
const u_int16_t* buffer = &md_str->buffer[0];
|
||||
for (u_int32_t i = 0; i < md_str->length && buffer[i]; ++i) {
|
||||
str.push_back(buffer[i]);
|
||||
}
|
||||
}
|
||||
return str;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
#endif // TOOLS_LINUX_MD2CORE_MINIDUMP_MEMORY_RANGE_H_
|
||||
258
src/tools/linux/md2core/minidump_memory_range_unittest.cc
Normal file
258
src/tools/linux/md2core/minidump_memory_range_unittest.cc
Normal file
|
|
@ -0,0 +1,258 @@
|
|||
// Copyright (c) 2011, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// minidump_memory_range_unittest.cc:
|
||||
// Unit tests for google_breakpad::MinidumpMemoryRange.
|
||||
|
||||
#include "breakpad_googletest_includes.h"
|
||||
#include "tools/linux/md2core/minidump_memory_range.h"
|
||||
|
||||
using google_breakpad::MinidumpMemoryRange;
|
||||
using testing::Message;
|
||||
|
||||
namespace {
|
||||
|
||||
const u_int32_t kBuffer[10] = { 0 };
|
||||
const size_t kBufferSize = sizeof(kBuffer);
|
||||
const u_int8_t* kBufferPointer = reinterpret_cast<const u_int8_t*>(kBuffer);
|
||||
|
||||
// Test vectors for verifying Covers, GetData, and Subrange.
|
||||
const struct {
|
||||
bool valid;
|
||||
size_t offset;
|
||||
size_t length;
|
||||
} kSubranges[] = {
|
||||
{ true, 0, 0 },
|
||||
{ true, 0, 2 },
|
||||
{ true, 0, kBufferSize },
|
||||
{ true, 2, 0 },
|
||||
{ true, 2, 4 },
|
||||
{ true, 2, kBufferSize - 2 },
|
||||
{ true, kBufferSize - 1, 1 },
|
||||
{ false, kBufferSize, 0 },
|
||||
{ false, kBufferSize, -1 },
|
||||
{ false, kBufferSize + 1, 0 },
|
||||
{ false, -1, 2 },
|
||||
{ false, 1, kBufferSize },
|
||||
{ false, kBufferSize - 1, 2 },
|
||||
{ false, 0, -1 },
|
||||
{ false, 1, -1 },
|
||||
};
|
||||
const size_t kNumSubranges = sizeof(kSubranges) / sizeof(kSubranges[0]);
|
||||
|
||||
// Test vectors for verifying GetArrayElement.
|
||||
const struct {
|
||||
size_t offset;
|
||||
size_t size;
|
||||
size_t index;
|
||||
const void* const pointer;
|
||||
} kElements[] = {
|
||||
// Valid array elemenets
|
||||
{ 0, 1, 0, kBufferPointer },
|
||||
{ 0, 1, 1, kBufferPointer + 1 },
|
||||
{ 0, 1, kBufferSize - 1, kBufferPointer + kBufferSize - 1 },
|
||||
{ 0, 2, 1, kBufferPointer + 2 },
|
||||
{ 0, 4, 2, kBufferPointer + 8 },
|
||||
{ 0, 4, 9, kBufferPointer + 36 },
|
||||
{ kBufferSize - 1, 1, 0, kBufferPointer + kBufferSize - 1 },
|
||||
// Invalid array elemenets
|
||||
{ 0, 1, kBufferSize, NULL },
|
||||
{ 0, 4, 10, NULL },
|
||||
{ kBufferSize - 1, 1, 1, NULL },
|
||||
{ kBufferSize - 1, 2, 0, NULL },
|
||||
{ kBufferSize, 1, 0, NULL },
|
||||
};
|
||||
const size_t kNumElements = sizeof(kElements) / sizeof(kElements[0]);
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST(MinidumpMemoryRangeTest, DefaultConstructor) {
|
||||
MinidumpMemoryRange range;
|
||||
EXPECT_EQ(NULL, range.data());
|
||||
EXPECT_EQ(0, range.length());
|
||||
}
|
||||
|
||||
TEST(MinidumpMemoryRangeTest, ConstructorWithDataAndLength) {
|
||||
MinidumpMemoryRange range(kBuffer, kBufferSize);
|
||||
EXPECT_EQ(kBufferPointer, range.data());
|
||||
EXPECT_EQ(kBufferSize, range.length());
|
||||
}
|
||||
|
||||
TEST(MinidumpMemoryRangeTest, Reset) {
|
||||
MinidumpMemoryRange range;
|
||||
range.Reset();
|
||||
EXPECT_EQ(NULL, range.data());
|
||||
EXPECT_EQ(0, range.length());
|
||||
|
||||
range.Set(kBuffer, kBufferSize);
|
||||
EXPECT_EQ(kBufferPointer, range.data());
|
||||
EXPECT_EQ(kBufferSize, range.length());
|
||||
|
||||
range.Reset();
|
||||
EXPECT_EQ(NULL, range.data());
|
||||
EXPECT_EQ(0, range.length());
|
||||
}
|
||||
|
||||
TEST(MinidumpMemoryRangeTest, Set) {
|
||||
MinidumpMemoryRange range;
|
||||
range.Set(kBuffer, kBufferSize);
|
||||
EXPECT_EQ(kBufferPointer, range.data());
|
||||
EXPECT_EQ(kBufferSize, range.length());
|
||||
|
||||
range.Set(NULL, 0);
|
||||
EXPECT_EQ(NULL, range.data());
|
||||
EXPECT_EQ(0, range.length());
|
||||
}
|
||||
|
||||
TEST(MinidumpMemoryRangeTest, SubrangeOfEmptyMemoryRange) {
|
||||
MinidumpMemoryRange range;
|
||||
MinidumpMemoryRange subrange = range.Subrange(0, 10);
|
||||
EXPECT_EQ(NULL, subrange.data());
|
||||
EXPECT_EQ(0, subrange.length());
|
||||
}
|
||||
|
||||
TEST(MinidumpMemoryRangeTest, SubrangeAndGetData) {
|
||||
MinidumpMemoryRange range(kBuffer, kBufferSize);
|
||||
for (size_t i = 0; i < kNumSubranges; ++i) {
|
||||
bool valid = kSubranges[i].valid;
|
||||
size_t sub_offset = kSubranges[i].offset;
|
||||
size_t sub_length = kSubranges[i].length;
|
||||
SCOPED_TRACE(Message() << "offset=" << sub_offset
|
||||
<< ", length=" << sub_length);
|
||||
|
||||
MinidumpMemoryRange subrange = range.Subrange(sub_offset, sub_length);
|
||||
if (valid) {
|
||||
EXPECT_TRUE(range.Covers(sub_offset, sub_length));
|
||||
EXPECT_EQ(kBufferPointer + sub_offset,
|
||||
range.GetData(sub_offset, sub_length));
|
||||
EXPECT_EQ(kBufferPointer + sub_offset, subrange.data());
|
||||
EXPECT_EQ(sub_length, subrange.length());
|
||||
} else {
|
||||
EXPECT_FALSE(range.Covers(sub_offset, sub_length));
|
||||
EXPECT_EQ(NULL, range.GetData(sub_offset, sub_length));
|
||||
EXPECT_EQ(NULL, subrange.data());
|
||||
EXPECT_EQ(0, subrange.length());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(MinidumpMemoryRangeTest, SubrangeWithMDLocationDescriptor) {
|
||||
MinidumpMemoryRange range(kBuffer, kBufferSize);
|
||||
for (size_t i = 0; i < kNumSubranges; ++i) {
|
||||
bool valid = kSubranges[i].valid;
|
||||
size_t sub_offset = kSubranges[i].offset;
|
||||
size_t sub_length = kSubranges[i].length;
|
||||
SCOPED_TRACE(Message() << "offset=" << sub_offset
|
||||
<< ", length=" << sub_length);
|
||||
|
||||
MDLocationDescriptor location;
|
||||
location.rva = sub_offset;
|
||||
location.data_size = sub_length;
|
||||
MinidumpMemoryRange subrange = range.Subrange(location);
|
||||
if (valid) {
|
||||
EXPECT_TRUE(range.Covers(sub_offset, sub_length));
|
||||
EXPECT_EQ(kBufferPointer + sub_offset,
|
||||
range.GetData(sub_offset, sub_length));
|
||||
EXPECT_EQ(kBufferPointer + sub_offset, subrange.data());
|
||||
EXPECT_EQ(sub_length, subrange.length());
|
||||
} else {
|
||||
EXPECT_FALSE(range.Covers(sub_offset, sub_length));
|
||||
EXPECT_EQ(NULL, range.GetData(sub_offset, sub_length));
|
||||
EXPECT_EQ(NULL, subrange.data());
|
||||
EXPECT_EQ(0, subrange.length());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(MinidumpMemoryRangeTest, GetDataWithTemplateType) {
|
||||
MinidumpMemoryRange range(kBuffer, kBufferSize);
|
||||
const char* char_pointer = range.GetData<char>(0);
|
||||
EXPECT_EQ(reinterpret_cast<const char*>(kBufferPointer), char_pointer);
|
||||
const int* int_pointer = range.GetData<int>(0);
|
||||
EXPECT_EQ(reinterpret_cast<const int*>(kBufferPointer), int_pointer);
|
||||
}
|
||||
|
||||
TEST(MinidumpMemoryRangeTest, GetArrayElement) {
|
||||
MinidumpMemoryRange range(kBuffer, kBufferSize);
|
||||
for (size_t i = 0; i < kNumElements; ++i) {
|
||||
size_t element_offset = kElements[i].offset;
|
||||
size_t element_size = kElements[i].size;
|
||||
unsigned element_index = kElements[i].index;
|
||||
const void* const element_pointer = kElements[i].pointer;
|
||||
SCOPED_TRACE(Message() << "offset=" << element_offset
|
||||
<< ", size=" << element_size
|
||||
<< ", index=" << element_index);
|
||||
EXPECT_EQ(element_pointer, range.GetArrayElement(
|
||||
element_offset, element_size, element_index));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(MinidumpMemoryRangeTest, GetArrayElmentWithTemplateType) {
|
||||
MinidumpMemoryRange range(kBuffer, kBufferSize);
|
||||
const char* char_pointer = range.GetArrayElement<char>(0, 0);
|
||||
EXPECT_EQ(reinterpret_cast<const char*>(kBufferPointer), char_pointer);
|
||||
const int* int_pointer = range.GetArrayElement<int>(0, 0);
|
||||
EXPECT_EQ(reinterpret_cast<const int*>(kBufferPointer), int_pointer);
|
||||
}
|
||||
|
||||
TEST(MinidumpMemoryRangeTest, GetAsciiMDString) {
|
||||
u_int8_t buffer[100] = { 0 };
|
||||
|
||||
MDString* md_str = reinterpret_cast<MDString*>(buffer);
|
||||
md_str->length = 4;
|
||||
md_str->buffer[0] = 'T';
|
||||
md_str->buffer[1] = 'e';
|
||||
md_str->buffer[2] = 's';
|
||||
md_str->buffer[3] = 't';
|
||||
md_str->buffer[4] = '\0';
|
||||
|
||||
size_t str2_offset =
|
||||
sizeof(MDString) + (md_str->length + 1) * sizeof(u_int16_t);
|
||||
|
||||
md_str = reinterpret_cast<MDString*>(buffer + str2_offset);
|
||||
md_str->length = 9; // Test length larger than actual string
|
||||
md_str->buffer[0] = 'S';
|
||||
md_str->buffer[1] = 't';
|
||||
md_str->buffer[2] = 'r';
|
||||
md_str->buffer[3] = 'i';
|
||||
md_str->buffer[4] = 'n';
|
||||
md_str->buffer[5] = 'g';
|
||||
md_str->buffer[6] = '\0';
|
||||
md_str->buffer[7] = '1';
|
||||
md_str->buffer[8] = '2';
|
||||
|
||||
MinidumpMemoryRange range(buffer, sizeof(buffer));
|
||||
EXPECT_EQ("Test", range.GetAsciiMDString(0));
|
||||
EXPECT_EQ("String", range.GetAsciiMDString(str2_offset));
|
||||
|
||||
// Test out-of-bounds cases.
|
||||
EXPECT_EQ("", range.GetAsciiMDString(
|
||||
sizeof(buffer) - sizeof(MDString) + 1));
|
||||
EXPECT_EQ("", range.GetAsciiMDString(sizeof(buffer)));
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue