mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-16 14:55:47 +00:00
bug 603592 - add a breakpad api to add info about known modules. r=mwu a=blocking-fennec
This commit is contained in:
parent
0eb0d2c885
commit
ff5e15723d
@ -88,11 +88,13 @@
|
||||
#include <unistd.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "common/linux/linux_libc_support.h"
|
||||
#include "common/linux/linux_syscall_support.h"
|
||||
#include "common/memory.h"
|
||||
#include "client/linux/minidump_writer/linux_dumper.h"
|
||||
#include "client/linux/minidump_writer/minidump_writer.h"
|
||||
#include "common/linux/guid_creator.h"
|
||||
#include "common/linux/eintr_wrapper.h"
|
||||
@ -437,8 +439,11 @@ void ExceptionHandler::WaitForContinueSignal() {
|
||||
// Runs on the cloned process.
|
||||
bool ExceptionHandler::DoDump(pid_t crashing_process, const void* context,
|
||||
size_t context_size) {
|
||||
return google_breakpad::WriteMinidump(
|
||||
next_minidump_path_c_, crashing_process, context, context_size);
|
||||
return google_breakpad::WriteMinidump(next_minidump_path_c_,
|
||||
crashing_process,
|
||||
context,
|
||||
context_size,
|
||||
mapping_info_);
|
||||
}
|
||||
|
||||
// static
|
||||
@ -515,4 +520,21 @@ bool ExceptionHandler::WriteMinidumpForChild(pid_t child,
|
||||
callback_context, true) : true;
|
||||
}
|
||||
|
||||
void ExceptionHandler::AddMappingInfo(const std::string& name,
|
||||
const u_int8_t identifier[sizeof(MDGUID)],
|
||||
uintptr_t start_address,
|
||||
size_t mapping_size,
|
||||
size_t file_offset) {
|
||||
MappingInfo info;
|
||||
info.start_addr = start_address;
|
||||
info.size = mapping_size;
|
||||
info.offset = file_offset;
|
||||
strncpy(info.name, name.c_str(), std::min(name.size() + 1, sizeof(info)));
|
||||
|
||||
std::pair<MappingInfo, u_int8_t[sizeof(MDGUID)]> mapping;
|
||||
mapping.first = info;
|
||||
memcpy(mapping.second, identifier, sizeof(MDGUID));
|
||||
mapping_info_.push_back(mapping);
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
@ -30,15 +30,18 @@
|
||||
#ifndef CLIENT_LINUX_HANDLER_EXCEPTION_HANDLER_H_
|
||||
#define CLIENT_LINUX_HANDLER_EXCEPTION_HANDLER_H_
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "client/linux/android_ucontext.h"
|
||||
#include "client/linux/crash_generation/crash_generation_client.h"
|
||||
#include "client/linux/minidump_writer/minidump_writer.h"
|
||||
#include "google_breakpad/common/minidump_format.h"
|
||||
#include "processor/scoped_ptr.h"
|
||||
|
||||
struct sigaction;
|
||||
@ -207,6 +210,15 @@ class ExceptionHandler {
|
||||
return crash_generation_client_.get() != NULL;
|
||||
}
|
||||
|
||||
// Add information about a memory mapping. This can be used if
|
||||
// a custom library loader is used that maps things in a way
|
||||
// that the linux dumper can't handle by reading the maps file.
|
||||
void AddMappingInfo(const std::string& name,
|
||||
const u_int8_t identifier[sizeof(MDGUID)],
|
||||
uintptr_t start_address,
|
||||
size_t mapping_size,
|
||||
size_t file_offset);
|
||||
|
||||
private:
|
||||
void Init(const std::string &dump_path,
|
||||
const int server_fd);
|
||||
@ -262,6 +274,10 @@ class ExceptionHandler {
|
||||
// cloned process after creating it, until we have explicitly enabled
|
||||
// ptrace. This is used to store the file descriptors for the pipe
|
||||
int fdes[2];
|
||||
|
||||
// Callers can add extra info about mappings for cases where the
|
||||
// dumper code cannot extract enough information from /proc/<pid>/maps.
|
||||
MappingList mapping_info_;
|
||||
};
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
@ -567,6 +567,87 @@ TEST(ExceptionHandlerTest, InstructionPointerMemoryNullPointer) {
|
||||
free(filename);
|
||||
}
|
||||
|
||||
static bool SimpleCallback(const char* dump_path,
|
||||
const char* minidump_id,
|
||||
void* context,
|
||||
bool succeeded) {
|
||||
if (!succeeded)
|
||||
return succeeded;
|
||||
|
||||
string* minidump_file = reinterpret_cast<string*>(context);
|
||||
minidump_file->append(dump_path);
|
||||
minidump_file->append("/");
|
||||
minidump_file->append(minidump_id);
|
||||
minidump_file->append(".dmp");
|
||||
return true;
|
||||
}
|
||||
|
||||
// Test that anonymous memory maps can be annotated with names and IDs.
|
||||
TEST(ExceptionHandlerTest, ModuleInfo) {
|
||||
// These are defined here so the parent can use them to check the
|
||||
// data from the minidump afterwards.
|
||||
const u_int32_t kMemorySize = sysconf(_SC_PAGESIZE);
|
||||
const char* kMemoryName = "a fake module";
|
||||
const u_int8_t kModuleGUID[sizeof(MDGUID)] = {
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
|
||||
0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF
|
||||
};
|
||||
char module_identifier_buffer[37];
|
||||
FileID::ConvertIdentifierToString(kModuleGUID,
|
||||
module_identifier_buffer,
|
||||
sizeof(module_identifier_buffer));
|
||||
string module_identifier(module_identifier_buffer);
|
||||
// Strip out dashes
|
||||
size_t pos;
|
||||
while ((pos = module_identifier.find('-')) != string::npos) {
|
||||
module_identifier.erase(pos, 1);
|
||||
}
|
||||
// And append a zero, because module IDs include an "age" field
|
||||
// which is always zero on Linux.
|
||||
module_identifier += "0";
|
||||
|
||||
// Get some memory.
|
||||
char* memory =
|
||||
reinterpret_cast<char*>(mmap(NULL,
|
||||
kMemorySize,
|
||||
PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANON,
|
||||
-1,
|
||||
0));
|
||||
const u_int64_t kMemoryAddress = reinterpret_cast<u_int64_t>(memory);
|
||||
ASSERT_TRUE(memory);
|
||||
|
||||
string minidump_filename;
|
||||
ExceptionHandler handler(TEMPDIR, NULL, SimpleCallback,
|
||||
(void*)&minidump_filename, true);
|
||||
// Add info about the anonymous memory mapping.
|
||||
handler.AddMappingInfo(kMemoryName,
|
||||
kModuleGUID,
|
||||
kMemoryAddress,
|
||||
kMemorySize,
|
||||
0);
|
||||
handler.WriteMinidump();
|
||||
|
||||
// Read the minidump. Load the module list, and ensure that
|
||||
// the mmap'ed |memory| is listed with the given module name
|
||||
// and debug ID.
|
||||
Minidump minidump(minidump_filename);
|
||||
ASSERT_TRUE(minidump.Read());
|
||||
|
||||
MinidumpModuleList* module_list = minidump.GetModuleList();
|
||||
ASSERT_TRUE(module_list);
|
||||
const MinidumpModule* module =
|
||||
module_list->GetModuleForAddress(kMemoryAddress);
|
||||
ASSERT_TRUE(module);
|
||||
|
||||
EXPECT_EQ(kMemoryAddress, module->base_address());
|
||||
EXPECT_EQ(kMemorySize, module->size());
|
||||
EXPECT_EQ(kMemoryName, module->code_file());
|
||||
EXPECT_EQ(module_identifier, module->debug_identifier());
|
||||
|
||||
unlink(minidump_filename.c_str());
|
||||
}
|
||||
|
||||
static const unsigned kControlMsgSize =
|
||||
CMSG_SPACE(sizeof(int)) + CMSG_SPACE(sizeof(struct ucred));
|
||||
|
||||
|
@ -106,12 +106,12 @@ bool DetachThread(pid_t pid) {
|
||||
}
|
||||
|
||||
inline bool IsMappedFileOpenUnsafe(
|
||||
const google_breakpad::MappingInfo* mapping) {
|
||||
const google_breakpad::MappingInfo& mapping) {
|
||||
// It is unsafe to attempt to open a mapped file that lives under /dev,
|
||||
// because the semantics of the open may be driver-specific so we'd risk
|
||||
// hanging the crash dumper. And a file in /dev/ almost certainly has no
|
||||
// ELF file identifier anyways.
|
||||
return my_strncmp(mapping->name,
|
||||
return my_strncmp(mapping.name,
|
||||
kMappedFileUnsafePrefix,
|
||||
sizeof(kMappedFileUnsafePrefix) - 1) == 0;
|
||||
}
|
||||
@ -237,16 +237,14 @@ LinuxDumper::BuildProcPath(char* path, pid_t pid, const char* node) const {
|
||||
}
|
||||
|
||||
bool
|
||||
LinuxDumper::ElfFileIdentifierForMapping(unsigned int mapping_id,
|
||||
LinuxDumper::ElfFileIdentifierForMapping(const MappingInfo& mapping,
|
||||
uint8_t identifier[sizeof(MDGUID)])
|
||||
{
|
||||
assert(mapping_id < mappings_.size());
|
||||
my_memset(identifier, 0, sizeof(MDGUID));
|
||||
const MappingInfo* mapping = mappings_[mapping_id];
|
||||
if (IsMappedFileOpenUnsafe(mapping)) {
|
||||
return false;
|
||||
}
|
||||
int fd = sys_open(mapping->name, O_RDONLY, 0);
|
||||
int fd = sys_open(mapping.name, O_RDONLY, 0);
|
||||
if (fd < 0)
|
||||
return false;
|
||||
struct kernel_stat st;
|
||||
|
@ -163,7 +163,7 @@ class LinuxDumper {
|
||||
void BuildProcPath(char* path, pid_t pid, const char* node) const;
|
||||
|
||||
// Generate a File ID from the .text section of a mapped entry
|
||||
bool ElfFileIdentifierForMapping(unsigned int mapping_id,
|
||||
bool ElfFileIdentifierForMapping(const MappingInfo& mapping,
|
||||
uint8_t identifier[sizeof(MDGUID)]);
|
||||
|
||||
// Utility method to find the location of where the kernel has
|
||||
|
@ -222,7 +222,7 @@ TEST(LinuxDumperTest, FileIDsMatch) {
|
||||
|
||||
uint8_t identifier1[sizeof(MDGUID)];
|
||||
uint8_t identifier2[sizeof(MDGUID)];
|
||||
EXPECT_TRUE(dumper.ElfFileIdentifierForMapping(i, identifier1));
|
||||
EXPECT_TRUE(dumper.ElfFileIdentifierForMapping(*mappings[i], identifier1));
|
||||
FileID fileid(exe_name);
|
||||
EXPECT_TRUE(fileid.ElfFileIdentifier(identifier2));
|
||||
char identifier_string1[37];
|
||||
|
@ -416,7 +416,8 @@ class MinidumpWriter {
|
||||
// case (1) above
|
||||
MinidumpWriter(const char* filename,
|
||||
pid_t crashing_pid,
|
||||
const ExceptionHandler::CrashContext* context)
|
||||
const ExceptionHandler::CrashContext* context,
|
||||
const MappingList& mappings)
|
||||
: filename_(filename),
|
||||
siginfo_(&context->siginfo),
|
||||
ucontext_(&context->context),
|
||||
@ -429,7 +430,8 @@ class MinidumpWriter {
|
||||
crashing_tid_(context->tid),
|
||||
crashing_tid_pc_(0),
|
||||
dumper_(crashing_pid),
|
||||
memory_blocks_(dumper_.allocator()) {
|
||||
memory_blocks_(dumper_.allocator()),
|
||||
mapping_info_(mappings) {
|
||||
}
|
||||
|
||||
// case (2) above
|
||||
@ -829,13 +831,27 @@ class MinidumpWriter {
|
||||
return true;
|
||||
}
|
||||
|
||||
// If there is caller-provided information about this mapping
|
||||
// in the mapping_info_ list, return true. Otherwise, return false.
|
||||
bool HaveMappingInfo(const MappingInfo& mapping) {
|
||||
for (MappingList::const_iterator iter = mapping_info_.begin();
|
||||
iter != mapping_info_.end();
|
||||
++iter) {
|
||||
if (iter->first.start_addr == mapping.start_addr &&
|
||||
iter->first.size == mapping.size) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Write information about the mappings in effect. Because we are using the
|
||||
// minidump format, the information about the mappings is pretty limited.
|
||||
// Because of this, we also include the full, unparsed, /proc/$x/maps file in
|
||||
// another stream in the file.
|
||||
bool WriteMappings(MDRawDirectory* dirent) {
|
||||
const unsigned num_mappings = dumper_.mappings().size();
|
||||
unsigned num_output_mappings = 0;
|
||||
unsigned num_output_mappings = mapping_info_.size();
|
||||
|
||||
for (unsigned i = 0; i < dumper_.mappings().size(); ++i) {
|
||||
const MappingInfo& mapping = *dumper_.mappings()[i];
|
||||
@ -851,58 +867,87 @@ class MinidumpWriter {
|
||||
dirent->location = list.location();
|
||||
*list.get() = num_output_mappings;
|
||||
|
||||
for (unsigned i = 0, j = 0; i < num_mappings; ++i) {
|
||||
// First write all the mappings from the dumper
|
||||
unsigned int j = 0;
|
||||
for (unsigned i = 0; i < num_mappings; ++i) {
|
||||
const MappingInfo& mapping = *dumper_.mappings()[i];
|
||||
if (!ShouldIncludeMapping(mapping))
|
||||
if (!ShouldIncludeMapping(mapping) || HaveMappingInfo(mapping))
|
||||
continue;
|
||||
|
||||
MDRawModule mod;
|
||||
my_memset(&mod, 0, MD_MODULE_SIZE);
|
||||
mod.base_of_image = mapping.start_addr;
|
||||
mod.size_of_image = mapping.size;
|
||||
const size_t filepath_len = my_strlen(mapping.name);
|
||||
|
||||
// Figure out file name from path
|
||||
const char* filename_ptr = mapping.name + filepath_len - 1;
|
||||
while (filename_ptr >= mapping.name) {
|
||||
if (*filename_ptr == '/')
|
||||
break;
|
||||
filename_ptr--;
|
||||
}
|
||||
filename_ptr++;
|
||||
const size_t filename_len = mapping.name + filepath_len - filename_ptr;
|
||||
uint8_t cv_buf[MDCVInfoPDB70_minsize + NAME_MAX];
|
||||
uint8_t* cv_ptr = cv_buf;
|
||||
UntypedMDRVA cv(&minidump_writer_);
|
||||
if (!cv.Allocate(MDCVInfoPDB70_minsize + filename_len + 1))
|
||||
if (!FillRawModule(mapping, mod, NULL))
|
||||
return false;
|
||||
|
||||
const uint32_t cv_signature = MD_CVINFOPDB70_SIGNATURE;
|
||||
memcpy(cv_ptr, &cv_signature, sizeof(cv_signature));
|
||||
cv_ptr += sizeof(cv_signature);
|
||||
uint8_t* signature = cv_ptr;
|
||||
cv_ptr += sizeof(MDGUID);
|
||||
dumper_.ElfFileIdentifierForMapping(i, signature);
|
||||
my_memset(cv_ptr, 0, sizeof(uint32_t)); // Set age to 0 on Linux.
|
||||
cv_ptr += sizeof(uint32_t);
|
||||
|
||||
// Write pdb_file_name
|
||||
memcpy(cv_ptr, filename_ptr, filename_len + 1);
|
||||
cv.Copy(cv_buf, MDCVInfoPDB70_minsize + filename_len + 1);
|
||||
|
||||
mod.cv_record = cv.location();
|
||||
|
||||
MDLocationDescriptor ld;
|
||||
if (!minidump_writer_.WriteString(mapping.name, filepath_len, &ld))
|
||||
return false;
|
||||
mod.module_name_rva = ld.rva;
|
||||
|
||||
list.CopyIndexAfterObject(j++, &mod, MD_MODULE_SIZE);
|
||||
}
|
||||
// Next write all the mappings provided by the caller
|
||||
for (MappingList::const_iterator iter = mapping_info_.begin();
|
||||
iter != mapping_info_.end();
|
||||
++iter) {
|
||||
MDRawModule mod;
|
||||
if (!FillRawModule(iter->first, mod, iter->second))
|
||||
return false;
|
||||
list.CopyIndexAfterObject(j++, &mod, MD_MODULE_SIZE);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Fill the MDRawModule mod with information about the provided
|
||||
// mapping. If identifier is non-NULL, use it instead of calculating
|
||||
// a file ID from the mapping.
|
||||
bool FillRawModule(const MappingInfo& mapping,
|
||||
MDRawModule& mod,
|
||||
const u_int8_t* identifier) {
|
||||
my_memset(&mod, 0, MD_MODULE_SIZE);
|
||||
|
||||
mod.base_of_image = mapping.start_addr;
|
||||
mod.size_of_image = mapping.size;
|
||||
const size_t filepath_len = my_strlen(mapping.name);
|
||||
|
||||
// Figure out file name from path
|
||||
const char* filename_ptr = mapping.name + filepath_len - 1;
|
||||
while (filename_ptr >= mapping.name) {
|
||||
if (*filename_ptr == '/')
|
||||
break;
|
||||
filename_ptr--;
|
||||
}
|
||||
filename_ptr++;
|
||||
|
||||
size_t filename_len = mapping.name + filepath_len - filename_ptr;
|
||||
|
||||
uint8_t cv_buf[MDCVInfoPDB70_minsize + NAME_MAX];
|
||||
uint8_t* cv_ptr = cv_buf;
|
||||
UntypedMDRVA cv(&minidump_writer_);
|
||||
if (!cv.Allocate(MDCVInfoPDB70_minsize + filename_len + 1))
|
||||
return false;
|
||||
|
||||
const uint32_t cv_signature = MD_CVINFOPDB70_SIGNATURE;
|
||||
memcpy(cv_ptr, &cv_signature, sizeof(cv_signature));
|
||||
cv_ptr += sizeof(cv_signature);
|
||||
uint8_t* signature = cv_ptr;
|
||||
cv_ptr += sizeof(MDGUID);
|
||||
if (identifier) {
|
||||
// GUID was provided by caller.
|
||||
memcpy(signature, identifier, sizeof(MDGUID));
|
||||
} else {
|
||||
dumper_.ElfFileIdentifierForMapping(mapping, signature);
|
||||
}
|
||||
my_memset(cv_ptr, 0, sizeof(uint32_t)); // Set age to 0 on Linux.
|
||||
cv_ptr += sizeof(uint32_t);
|
||||
|
||||
// Write pdb_file_name
|
||||
memcpy(cv_ptr, filename_ptr, filename_len + 1);
|
||||
cv.Copy(cv_buf, MDCVInfoPDB70_minsize + filename_len + 1);
|
||||
|
||||
mod.cv_record = cv.location();
|
||||
|
||||
MDLocationDescriptor ld;
|
||||
if (!minidump_writer_.WriteString(mapping.name, filepath_len, &ld))
|
||||
return false;
|
||||
mod.module_name_rva = ld.rva;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WriteMemoryListStream(MDRawDirectory* dirent) {
|
||||
TypedMDRVA<uint32_t> list(&minidump_writer_);
|
||||
if (!list.AllocateObjectAndArray(memory_blocks_.size(),
|
||||
@ -1309,15 +1354,24 @@ popline:
|
||||
// written while writing the thread list stream, but saved here
|
||||
// so a memory list stream can be written afterwards.
|
||||
wasteful_vector<MDMemoryDescriptor> memory_blocks_;
|
||||
// Additional information about some mappings provided by the caller.
|
||||
const MappingList& mapping_info_;
|
||||
};
|
||||
|
||||
bool WriteMinidump(const char* filename, pid_t crashing_process,
|
||||
const void* blob, size_t blob_size) {
|
||||
MappingList m;
|
||||
return WriteMinidump(filename, crashing_process, blob, blob_size, m);
|
||||
}
|
||||
|
||||
bool WriteMinidump(const char* filename, pid_t crashing_process,
|
||||
const void* blob, size_t blob_size,
|
||||
const MappingList& mappings) {
|
||||
if (blob_size != sizeof(ExceptionHandler::CrashContext))
|
||||
return false;
|
||||
const ExceptionHandler::CrashContext* context =
|
||||
reinterpret_cast<const ExceptionHandler::CrashContext*>(blob);
|
||||
MinidumpWriter writer(filename, crashing_process, context);
|
||||
MinidumpWriter writer(filename, crashing_process, context, mappings);
|
||||
if (!writer.Init())
|
||||
return false;
|
||||
return writer.Dump();
|
||||
|
@ -30,11 +30,20 @@
|
||||
#ifndef CLIENT_LINUX_MINIDUMP_WRITER_MINIDUMP_WRITER_H_
|
||||
#define CLIENT_LINUX_MINIDUMP_WRITER_MINIDUMP_WRITER_H_
|
||||
|
||||
#include <list>
|
||||
#include <utility>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "google_breakpad/common/minidump_format.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
// A list of <MappingInfo, GUID>
|
||||
typedef std::pair<struct MappingInfo, u_int8_t[sizeof(MDGUID)]> MappingEntry;
|
||||
typedef std::list<MappingEntry> MappingList;
|
||||
|
||||
// Write a minidump to the filesystem. This function does not malloc nor use
|
||||
// libc functions which may. Thus, it can be used in contexts where the state
|
||||
// of the heap may be corrupt.
|
||||
@ -56,6 +65,11 @@ bool WriteMinidump(const char* filename, pid_t crashing_process,
|
||||
bool WriteMinidump(const char* filename, pid_t process,
|
||||
pid_t process_blamed_thread);
|
||||
|
||||
// This overload also allows passing a list of known mappings.
|
||||
bool WriteMinidump(const char* filename, pid_t crashing_process,
|
||||
const void* blob, size_t blob_size,
|
||||
const MappingList& mappings);
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
#endif // CLIENT_LINUX_MINIDUMP_WRITER_MINIDUMP_WRITER_H_
|
||||
|
@ -68,3 +68,91 @@ TEST(MinidumpWriterTest, Setup) {
|
||||
|
||||
close(fds[1]);
|
||||
}
|
||||
|
||||
TEST(MinidumpWriterTest, MappingInfo) {
|
||||
int fds[2];
|
||||
ASSERT_NE(-1, pipe(fds));
|
||||
|
||||
// These are defined here so the parent can use them to check the
|
||||
// data from the minidump afterwards.
|
||||
const u_int32_t kMemorySize = sysconf(_SC_PAGESIZE);
|
||||
const char* kMemoryName = "a fake module";
|
||||
const u_int8_t kModuleGUID[sizeof(MDGUID)] = {
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
|
||||
0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF
|
||||
};
|
||||
char module_identifier_buffer[37];
|
||||
FileID::ConvertIdentifierToString(kModuleGUID,
|
||||
module_identifier_buffer,
|
||||
sizeof(module_identifier_buffer));
|
||||
string module_identifier(module_identifier_buffer);
|
||||
// Strip out dashes
|
||||
size_t pos;
|
||||
while ((pos = module_identifier.find('-')) != string::npos) {
|
||||
module_identifier.erase(pos, 1);
|
||||
}
|
||||
// And append a zero, because module IDs include an "age" field
|
||||
// which is always zero on Linux.
|
||||
module_identifier += "0";
|
||||
|
||||
// Get some memory.
|
||||
char* memory =
|
||||
reinterpret_cast<char*>(mmap(NULL,
|
||||
kMemorySize,
|
||||
PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANON,
|
||||
-1,
|
||||
0));
|
||||
const u_int64_t kMemoryAddress = reinterpret_cast<u_int64_t>(memory);
|
||||
ASSERT_TRUE(memory);
|
||||
|
||||
const pid_t child = fork();
|
||||
if (child == 0) {
|
||||
close(fds[1]);
|
||||
char b;
|
||||
HANDLE_EINTR(read(fds[0], &b, sizeof(b)));
|
||||
close(fds[0]);
|
||||
syscall(__NR_exit);
|
||||
}
|
||||
close(fds[0]);
|
||||
|
||||
ExceptionHandler::CrashContext context;
|
||||
memset(&context, 0, sizeof(context));
|
||||
|
||||
char templ[] = TEMPDIR "/minidump-writer-unittest-XXXXXX";
|
||||
mktemp(templ);
|
||||
|
||||
// Add information about the mapped memory.
|
||||
MappingInfo info;
|
||||
info.start_addr = kMemoryAddress;
|
||||
info.size = kMemorySize;
|
||||
info.offset = 0;
|
||||
strcpy(info.name, kMemoryName);
|
||||
|
||||
MappingList mappings;
|
||||
std::pair<MappingInfo, u_int8_t[sizeof(MDGUID)]> mapping;
|
||||
mapping.first = info;
|
||||
memcpy(mapping.second, kModuleGUID, sizeof(MDGUID));
|
||||
mappings.push_back(mapping);
|
||||
ASSERT_TRUE(WriteMinidump(templ, child, &context, sizeof(context), mappings));
|
||||
|
||||
// Read the minidump. Load the module list, and ensure that
|
||||
// the mmap'ed |memory| is listed with the given module name
|
||||
// and debug ID.
|
||||
Minidump minidump(templ);
|
||||
ASSERT_TRUE(minidump.Read());
|
||||
|
||||
MinidumpModuleList* module_list = minidump.GetModuleList();
|
||||
ASSERT_TRUE(module_list);
|
||||
const MinidumpModule* module =
|
||||
module_list->GetModuleForAddress(kMemoryAddress);
|
||||
ASSERT_TRUE(module);
|
||||
|
||||
EXPECT_EQ(kMemoryAddress, module->base_address());
|
||||
EXPECT_EQ(kMemorySize, module->size());
|
||||
EXPECT_EQ(kMemoryName, module->code_file());
|
||||
EXPECT_EQ(module_identifier, module->debug_identifier());
|
||||
|
||||
unlink(templ);
|
||||
close(fds[1]);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user