Refactor LinuxDumper and MinidumpWriter.

This patch is part of a bigger patch that helps merging the breakpad code
with the modified version in Chromium OS.

Specifically, this patch makes the following changes:
1. Add two convenient methods, back() and empty(), to the wasteful_vector
   class.
2. Refactor the LinuxDumper class such that it can later be splitted into
   a base class and two derived classes, one uses the current ptrace
   implementation and one uses a core file.
3. Refactor the MinidumpWriter class such that it can later use different
   derived implementations of LinuxDumper.

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.
Review URL: http://breakpad.appspot.com/340001

git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@902 4c0a9323-5329-0410-9bdc-e9ce6186880e
This commit is contained in:
benchan@chromium.org 2012-01-11 01:31:35 +00:00
parent 384c078d2e
commit 577304f02a
6 changed files with 146 additions and 89 deletions

View File

@ -118,16 +118,21 @@ inline static bool IsMappedFileOpenUnsafe(
namespace google_breakpad { namespace google_breakpad {
LinuxDumper::LinuxDumper(int pid) LinuxDumper::LinuxDumper(pid_t pid)
: pid_(pid), : pid_(pid),
crash_address_(0),
crash_signal_(0),
crash_thread_(0),
threads_suspended_(false), threads_suspended_(false),
threads_(&allocator_, 8), threads_(&allocator_, 8),
mappings_(&allocator_) { mappings_(&allocator_) {
} }
LinuxDumper::~LinuxDumper() {
}
bool LinuxDumper::Init() { bool LinuxDumper::Init() {
return EnumerateThreads(&threads_) && return EnumerateThreads() && EnumerateMappings();
EnumerateMappings(&mappings_);
} }
bool LinuxDumper::ThreadsSuspend() { bool LinuxDumper::ThreadsSuspend() {
@ -283,8 +288,7 @@ LinuxDumper::FindBeginningOfLinuxGateSharedLibrary(const pid_t pid) const {
return NULL; return NULL;
} }
bool bool LinuxDumper::EnumerateMappings() {
LinuxDumper::EnumerateMappings(wasteful_vector<MappingInfo*>* result) const {
char maps_path[NAME_MAX]; char maps_path[NAME_MAX];
BuildProcPath(maps_path, pid_, "maps"); BuildProcPath(maps_path, pid_, "maps");
@ -323,8 +327,8 @@ LinuxDumper::EnumerateMappings(wasteful_vector<MappingInfo*>* result) const {
} }
// Merge adjacent mappings with the same name into one module, // Merge adjacent mappings with the same name into one module,
// assuming they're a single library mapped by the dynamic linker // assuming they're a single library mapped by the dynamic linker
if (name && result->size()) { if (name && !mappings_.empty()) {
MappingInfo* module = (*result)[result->size() - 1]; MappingInfo* module = mappings_.back();
if ((start_addr == module->start_addr + module->size) && if ((start_addr == module->start_addr + module->size) &&
(my_strlen(name) == my_strlen(module->name)) && (my_strlen(name) == my_strlen(module->name)) &&
(my_strncmp(name, module->name, my_strlen(name)) == 0)) { (my_strncmp(name, module->name, my_strlen(name)) == 0)) {
@ -343,7 +347,7 @@ LinuxDumper::EnumerateMappings(wasteful_vector<MappingInfo*>* result) const {
if (l < sizeof(module->name)) if (l < sizeof(module->name))
memcpy(module->name, name, l); memcpy(module->name, name, l);
} }
result->push_back(module); mappings_.push_back(module);
} }
} }
} }
@ -352,12 +356,12 @@ LinuxDumper::EnumerateMappings(wasteful_vector<MappingInfo*>* result) const {
sys_close(fd); sys_close(fd);
return result->size() > 0; return !mappings_.empty();
} }
// Parse /proc/$pid/task to list all the threads of the process identified by // Parse /proc/$pid/task to list all the threads of the process identified by
// pid. // pid.
bool LinuxDumper::EnumerateThreads(wasteful_vector<pid_t>* result) const { bool LinuxDumper::EnumerateThreads() {
char task_path[NAME_MAX]; char task_path[NAME_MAX];
BuildProcPath(task_path, pid_, "task"); BuildProcPath(task_path, pid_, "task");
@ -377,7 +381,7 @@ bool LinuxDumper::EnumerateThreads(wasteful_vector<pid_t>* result) const {
if (my_strtoui(&tid, dent_name) && if (my_strtoui(&tid, dent_name) &&
last_tid != tid) { last_tid != tid) {
last_tid = tid; last_tid = tid;
result->push_back(tid); threads_.push_back(tid);
} }
} }
dir_reader->PopEntry(); dir_reader->PopEntry();
@ -391,12 +395,17 @@ bool LinuxDumper::EnumerateThreads(wasteful_vector<pid_t>* result) const {
// Fill out the |tgid|, |ppid| and |pid| members of |info|. If unavailable, // Fill out the |tgid|, |ppid| and |pid| members of |info|. If unavailable,
// these members are set to -1. Returns true iff all three members are // these members are set to -1. Returns true iff all three members are
// available. // available.
bool LinuxDumper::ThreadInfoGet(pid_t tid, ThreadInfo* info) { bool LinuxDumper::GetThreadInfoByIndex(size_t index, ThreadInfo* info) {
if (index >= threads_.size())
return false;
pid_t tid = threads_[index];
assert(info != NULL); assert(info != NULL);
char status_path[NAME_MAX]; char status_path[NAME_MAX];
BuildProcPath(status_path, tid, "status"); BuildProcPath(status_path, tid, "status");
const int fd = open(status_path, O_RDONLY); const int fd = sys_open(status_path, O_RDONLY, 0);
if (fd < 0) if (fd < 0)
return false; return false;
@ -488,7 +497,6 @@ bool LinuxDumper::GetStackInfo(const void** stack, size_t* stack_len,
return true; return true;
} }
// static
void LinuxDumper::CopyFromProcess(void* dest, pid_t child, const void* src, void LinuxDumper::CopyFromProcess(void* dest, pid_t child, const void* src,
size_t length) { size_t length) {
unsigned long tmp = 55; unsigned long tmp = 55;

View File

@ -66,7 +66,7 @@ typedef struct
#define AT_SYSINFO_EHDR 33 #define AT_SYSINFO_EHDR 33
#endif #endif
#endif // __ANDROID__ #endif // __ANDROID__
#elif defined(__x86_64__) #elif defined(__x86_64)
typedef Elf64_auxv_t elf_aux_entry; typedef Elf64_auxv_t elf_aux_entry;
#endif #endif
// When we find the VDSO mapping in the process's address space, this // When we find the VDSO mapping in the process's address space, this
@ -118,6 +118,8 @@ class LinuxDumper {
public: public:
explicit LinuxDumper(pid_t pid); explicit LinuxDumper(pid_t pid);
virtual ~LinuxDumper();
// Parse the data for |threads| and |mappings|. // Parse the data for |threads| and |mappings|.
bool Init(); bool Init();
@ -125,9 +127,9 @@ class LinuxDumper {
bool ThreadsSuspend(); bool ThreadsSuspend();
bool ThreadsResume(); bool ThreadsResume();
// Read information about the given thread. Returns true on success. One must // Read information about the |index|-th thread of |threads_|.
// have called |ThreadsSuspend| first. // Returns true on success. One must have called |ThreadsSuspend| first.
bool ThreadInfoGet(pid_t tid, ThreadInfo* info); virtual bool GetThreadInfoByIndex(size_t index, ThreadInfo* info);
// These are only valid after a call to |Init|. // These are only valid after a call to |Init|.
const wasteful_vector<pid_t> &threads() { return threads_; } const wasteful_vector<pid_t> &threads() { return threads_; }
@ -142,9 +144,10 @@ class LinuxDumper {
PageAllocator* allocator() { return &allocator_; } PageAllocator* allocator() { return &allocator_; }
// memcpy from a remote process. // Copy content of |length| bytes from a given process |child|,
static void CopyFromProcess(void* dest, pid_t child, const void* src, // starting from |src|, into |dest|.
size_t length); void CopyFromProcess(void* dest, pid_t child, const void* src,
size_t length);
// Builds a proc path for a certain pid for a node. path is a // Builds a proc path for a certain pid for a node. path is a
// character array that is overwritten, and node is the final node // character array that is overwritten, and node is the final node
@ -164,9 +167,21 @@ class LinuxDumper {
// shared object. Parsing the auxilary vector for AT_SYSINFO_EHDR // shared object. Parsing the auxilary vector for AT_SYSINFO_EHDR
// is the safest way to go.) // is the safest way to go.)
void* FindBeginningOfLinuxGateSharedLibrary(const pid_t pid) const; void* FindBeginningOfLinuxGateSharedLibrary(const pid_t pid) const;
uintptr_t crash_address() const { return crash_address_; }
void set_crash_address(uintptr_t crash_address) {
crash_address_ = crash_address;
}
int crash_signal() const { return crash_signal_; }
void set_crash_signal(int crash_signal) { crash_signal_ = crash_signal; }
pid_t crash_thread() const { return crash_thread_; }
void set_crash_thread(pid_t crash_thread) { crash_thread_ = crash_thread; }
private: private:
bool EnumerateMappings(wasteful_vector<MappingInfo*>* result) const; bool EnumerateMappings();
bool EnumerateThreads(wasteful_vector<pid_t>* result) const; bool EnumerateThreads();
// For the case where a running program has been deleted, it'll show up in // For the case where a running program has been deleted, it'll show up in
// /proc/pid/maps as "/path/to/program (deleted)". If this is the case, then // /proc/pid/maps as "/path/to/program (deleted)". If this is the case, then
@ -179,8 +194,18 @@ class LinuxDumper {
// Returns true if |path| is modified. // Returns true if |path| is modified.
bool HandleDeletedFileInMapping(char* path) const; bool HandleDeletedFileInMapping(char* path) const;
// ID of the crashed process.
const pid_t pid_; const pid_t pid_;
// Virtual address at which the process crashed.
uintptr_t crash_address_;
// Signal that terminated the crashed process.
int crash_signal_;
// ID of the crashed thread.
pid_t crash_thread_;
mutable PageAllocator allocator_; mutable PageAllocator allocator_;
bool threads_suspended_; bool threads_suspended_;

View File

@ -225,7 +225,7 @@ TEST(LinuxDumperTest, VerifyStackReadWithMultipleThreads) {
ThreadInfo one_thread; ThreadInfo one_thread;
for(size_t i = 0; i < dumper.threads().size(); ++i) { for(size_t i = 0; i < dumper.threads().size(); ++i) {
EXPECT_TRUE(dumper.ThreadInfoGet(dumper.threads()[i], &one_thread)); EXPECT_TRUE(dumper.GetThreadInfoByIndex(i, &one_thread));
// In the helper program, we stored a pointer to the thread id in a // In the helper program, we stored a pointer to the thread id in a
// specific register. Check that we can recover its value. // specific register. Check that we can recover its value.
#if defined(__ARM_EABI__) #if defined(__ARM_EABI__)

View File

@ -368,11 +368,10 @@ namespace google_breakpad {
class MinidumpWriter { class MinidumpWriter {
public: public:
MinidumpWriter(const char* filename, MinidumpWriter(const char* filename,
pid_t crashing_pid,
const ExceptionHandler::CrashContext* context, const ExceptionHandler::CrashContext* context,
const MappingList& mappings) const MappingList& mappings,
LinuxDumper* dumper)
: filename_(filename), : filename_(filename),
siginfo_(&context->siginfo),
ucontext_(&context->context), ucontext_(&context->context),
#if !defined(__ARM_EABI__) #if !defined(__ARM_EABI__)
float_state_(&context->float_state), float_state_(&context->float_state),
@ -380,20 +379,19 @@ class MinidumpWriter {
// TODO: fix this after fixing ExceptionHandler // TODO: fix this after fixing ExceptionHandler
float_state_(NULL), float_state_(NULL),
#endif #endif
crashing_tid_(context->tid), dumper_(dumper),
dumper_(crashing_pid), memory_blocks_(dumper_->allocator()),
memory_blocks_(dumper_.allocator()),
mapping_list_(mappings) { mapping_list_(mappings) {
} }
bool Init() { bool Init() {
return dumper_.Init() && minidump_writer_.Open(filename_) && return dumper_->Init() && minidump_writer_.Open(filename_) &&
dumper_.ThreadsSuspend(); dumper_->ThreadsSuspend();
} }
~MinidumpWriter() { ~MinidumpWriter() {
minidump_writer_.Close(); minidump_writer_.Close();
dumper_.ThreadsResume(); dumper_->ThreadsResume();
} }
bool Dump() { bool Dump() {
@ -408,7 +406,8 @@ class MinidumpWriter {
for (int i = 0;;) { for (int i = 0;;) {
ElfW(Dyn) dyn; ElfW(Dyn) dyn;
dynamic_length += sizeof(dyn); dynamic_length += sizeof(dyn);
dumper_.CopyFromProcess(&dyn, crashing_tid_, _DYNAMIC+i++, sizeof(dyn)); dumper_->CopyFromProcess(&dyn, GetCrashThread(), _DYNAMIC+i++,
sizeof(dyn));
if (dyn.d_tag == DT_DEBUG) { if (dyn.d_tag == DT_DEBUG) {
r_debug = (struct r_debug*)dyn.d_un.d_ptr; r_debug = (struct r_debug*)dyn.d_un.d_ptr;
continue; continue;
@ -467,7 +466,7 @@ class MinidumpWriter {
dir.CopyIndex(dir_index++, &dirent); dir.CopyIndex(dir_index++, &dirent);
dirent.stream_type = MD_LINUX_PROC_STATUS; dirent.stream_type = MD_LINUX_PROC_STATUS;
if (!WriteProcFile(&dirent.location, crashing_tid_, "status")) if (!WriteProcFile(&dirent.location, GetCrashThread(), "status"))
NullifyDirectoryEntry(&dirent); NullifyDirectoryEntry(&dirent);
dir.CopyIndex(dir_index++, &dirent); dir.CopyIndex(dir_index++, &dirent);
@ -477,22 +476,22 @@ class MinidumpWriter {
dir.CopyIndex(dir_index++, &dirent); dir.CopyIndex(dir_index++, &dirent);
dirent.stream_type = MD_LINUX_CMD_LINE; dirent.stream_type = MD_LINUX_CMD_LINE;
if (!WriteProcFile(&dirent.location, crashing_tid_, "cmdline")) if (!WriteProcFile(&dirent.location, GetCrashThread(), "cmdline"))
NullifyDirectoryEntry(&dirent); NullifyDirectoryEntry(&dirent);
dir.CopyIndex(dir_index++, &dirent); dir.CopyIndex(dir_index++, &dirent);
dirent.stream_type = MD_LINUX_ENVIRON; dirent.stream_type = MD_LINUX_ENVIRON;
if (!WriteProcFile(&dirent.location, crashing_tid_, "environ")) if (!WriteProcFile(&dirent.location, GetCrashThread(), "environ"))
NullifyDirectoryEntry(&dirent); NullifyDirectoryEntry(&dirent);
dir.CopyIndex(dir_index++, &dirent); dir.CopyIndex(dir_index++, &dirent);
dirent.stream_type = MD_LINUX_AUXV; dirent.stream_type = MD_LINUX_AUXV;
if (!WriteProcFile(&dirent.location, crashing_tid_, "auxv")) if (!WriteProcFile(&dirent.location, GetCrashThread(), "auxv"))
NullifyDirectoryEntry(&dirent); NullifyDirectoryEntry(&dirent);
dir.CopyIndex(dir_index++, &dirent); dir.CopyIndex(dir_index++, &dirent);
dirent.stream_type = MD_LINUX_MAPS; dirent.stream_type = MD_LINUX_MAPS;
if (!WriteProcFile(&dirent.location, crashing_tid_, "maps")) if (!WriteProcFile(&dirent.location, GetCrashThread(), "maps"))
NullifyDirectoryEntry(&dirent); NullifyDirectoryEntry(&dirent);
dir.CopyIndex(dir_index++, &dirent); dir.CopyIndex(dir_index++, &dirent);
@ -506,7 +505,7 @@ class MinidumpWriter {
// If you add more directory entries, don't forget to update kNumWriters, // If you add more directory entries, don't forget to update kNumWriters,
// above. // above.
dumper_.ThreadsResume(); dumper_->ThreadsResume();
return true; return true;
} }
@ -629,7 +628,7 @@ class MinidumpWriter {
// Write information about the threads. // Write information about the threads.
bool WriteThreadListStream(MDRawDirectory* dirent) { bool WriteThreadListStream(MDRawDirectory* dirent) {
const unsigned num_threads = dumper_.threads().size(); const unsigned num_threads = dumper_->threads().size();
TypedMDRVA<uint32_t> list(&minidump_writer_); TypedMDRVA<uint32_t> list(&minidump_writer_);
if (!list.AllocateObjectAndArray(num_threads, sizeof(MDRawThread))) if (!list.AllocateObjectAndArray(num_threads, sizeof(MDRawThread)))
@ -643,21 +642,22 @@ class MinidumpWriter {
for (unsigned i = 0; i < num_threads; ++i) { for (unsigned i = 0; i < num_threads; ++i) {
MDRawThread thread; MDRawThread thread;
my_memset(&thread, 0, sizeof(thread)); my_memset(&thread, 0, sizeof(thread));
thread.thread_id = dumper_.threads()[i]; thread.thread_id = dumper_->threads()[i];
// We have a different source of information for the crashing thread. If // We have a different source of information for the crashing thread. If
// we used the actual state of the thread we would find it running in the // we used the actual state of the thread we would find it running in the
// signal handler with the alternative stack, which would be deeply // signal handler with the alternative stack, which would be deeply
// unhelpful. // unhelpful.
if ((pid_t)thread.thread_id == crashing_tid_) { if ((pid_t)thread.thread_id == GetCrashThread()) {
const void* stack; const void* stack;
size_t stack_len; size_t stack_len;
if (!dumper_.GetStackInfo(&stack, &stack_len, GetStackPointer())) if (!dumper_->GetStackInfo(&stack, &stack_len, GetStackPointer()))
return false; return false;
UntypedMDRVA memory(&minidump_writer_); UntypedMDRVA memory(&minidump_writer_);
if (!memory.Allocate(stack_len)) if (!memory.Allocate(stack_len))
return false; return false;
uint8_t* stack_copy = (uint8_t*) dumper_.allocator()->Alloc(stack_len); uint8_t* stack_copy = reinterpret_cast<uint8_t*>(Alloc(stack_len));
dumper_.CopyFromProcess(stack_copy, thread.thread_id, stack, stack_len); dumper_->CopyFromProcess(stack_copy, thread.thread_id, stack,
stack_len);
memory.Copy(stack_copy, stack_len); memory.Copy(stack_copy, stack_len);
thread.stack.start_of_memory_range = (uintptr_t) (stack); thread.stack.start_of_memory_range = (uintptr_t) (stack);
thread.stack.memory = memory.location(); thread.stack.memory = memory.location();
@ -671,8 +671,8 @@ class MinidumpWriter {
// don't bother trying to write it. // don't bother trying to write it.
bool ip_is_mapped = false; bool ip_is_mapped = false;
MDMemoryDescriptor ip_memory_d; MDMemoryDescriptor ip_memory_d;
for (unsigned j = 0; j < dumper_.mappings().size(); ++j) { for (unsigned j = 0; j < dumper_->mappings().size(); ++j) {
const MappingInfo& mapping = *dumper_.mappings()[j]; const MappingInfo& mapping = *dumper_->mappings()[j];
if (ip >= mapping.start_addr && if (ip >= mapping.start_addr &&
ip < mapping.start_addr + mapping.size) { ip < mapping.start_addr + mapping.size) {
ip_is_mapped = true; ip_is_mapped = true;
@ -695,12 +695,12 @@ class MinidumpWriter {
if (!ip_memory.Allocate(ip_memory_d.memory.data_size)) if (!ip_memory.Allocate(ip_memory_d.memory.data_size))
return false; return false;
uint8_t* memory_copy = uint8_t* memory_copy =
(uint8_t*) dumper_.allocator()->Alloc(ip_memory_d.memory.data_size); reinterpret_cast<uint8_t*>(Alloc(ip_memory_d.memory.data_size));
dumper_.CopyFromProcess( dumper_->CopyFromProcess(
memory_copy, memory_copy,
thread.thread_id, thread.thread_id,
reinterpret_cast<void*>(ip_memory_d.start_of_memory_range), reinterpret_cast<void*>(ip_memory_d.start_of_memory_range),
ip_memory_d.memory.data_size); ip_memory_d.memory.data_size);
ip_memory.Copy(memory_copy, ip_memory_d.memory.data_size); ip_memory.Copy(memory_copy, ip_memory_d.memory.data_size);
ip_memory_d.memory = ip_memory.location(); ip_memory_d.memory = ip_memory.location();
memory_blocks_.push_back(ip_memory_d); memory_blocks_.push_back(ip_memory_d);
@ -716,15 +716,14 @@ class MinidumpWriter {
crashing_thread_context_ = cpu.location(); crashing_thread_context_ = cpu.location();
} else { } else {
ThreadInfo info; ThreadInfo info;
if (!dumper_.ThreadInfoGet(dumper_.threads()[i], &info)) if (!dumper_->GetThreadInfoByIndex(i, &info))
return false; return false;
UntypedMDRVA memory(&minidump_writer_); UntypedMDRVA memory(&minidump_writer_);
if (!memory.Allocate(info.stack_len)) if (!memory.Allocate(info.stack_len))
return false; return false;
uint8_t* stack_copy = uint8_t* stack_copy = reinterpret_cast<uint8_t*>(Alloc(info.stack_len));
(uint8_t*) dumper_.allocator()->Alloc(info.stack_len); dumper_->CopyFromProcess(stack_copy, thread.thread_id, info.stack,
dumper_.CopyFromProcess(stack_copy, thread.thread_id, info.stack, info.stack_len);
info.stack_len);
memory.Copy(stack_copy, info.stack_len); memory.Copy(stack_copy, info.stack_len);
thread.stack.start_of_memory_range = (uintptr_t)(info.stack); thread.stack.start_of_memory_range = (uintptr_t)(info.stack);
thread.stack.memory = memory.location(); thread.stack.memory = memory.location();
@ -777,11 +776,11 @@ class MinidumpWriter {
// Because of this, we also include the full, unparsed, /proc/$x/maps file in // Because of this, we also include the full, unparsed, /proc/$x/maps file in
// another stream in the file. // another stream in the file.
bool WriteMappings(MDRawDirectory* dirent) { bool WriteMappings(MDRawDirectory* dirent) {
const unsigned num_mappings = dumper_.mappings().size(); const unsigned num_mappings = dumper_->mappings().size();
unsigned num_output_mappings = mapping_list_.size(); unsigned num_output_mappings = mapping_list_.size();
for (unsigned i = 0; i < dumper_.mappings().size(); ++i) { for (unsigned i = 0; i < dumper_->mappings().size(); ++i) {
const MappingInfo& mapping = *dumper_.mappings()[i]; const MappingInfo& mapping = *dumper_->mappings()[i];
if (ShouldIncludeMapping(mapping) && !HaveMappingInfo(mapping)) if (ShouldIncludeMapping(mapping) && !HaveMappingInfo(mapping))
num_output_mappings++; num_output_mappings++;
} }
@ -797,7 +796,7 @@ class MinidumpWriter {
// First write all the mappings from the dumper // First write all the mappings from the dumper
unsigned int j = 0; unsigned int j = 0;
for (unsigned i = 0; i < num_mappings; ++i) { for (unsigned i = 0; i < num_mappings; ++i) {
const MappingInfo& mapping = *dumper_.mappings()[i]; const MappingInfo& mapping = *dumper_->mappings()[i];
if (!ShouldIncludeMapping(mapping) || HaveMappingInfo(mapping)) if (!ShouldIncludeMapping(mapping) || HaveMappingInfo(mapping))
continue; continue;
@ -859,8 +858,8 @@ class MinidumpWriter {
// GUID was provided by caller. // GUID was provided by caller.
memcpy(signature, identifier, sizeof(MDGUID)); memcpy(signature, identifier, sizeof(MDGUID));
} else { } else {
dumper_.ElfFileIdentifierForMapping(mapping, member, dumper_->ElfFileIdentifierForMapping(mapping, member,
mapping_id, signature); mapping_id, signature);
} }
my_memset(cv_ptr, 0, sizeof(uint32_t)); // Set age to 0 on Linux. my_memset(cv_ptr, 0, sizeof(uint32_t)); // Set age to 0 on Linux.
cv_ptr += sizeof(uint32_t); cv_ptr += sizeof(uint32_t);
@ -905,10 +904,9 @@ class MinidumpWriter {
dirent->stream_type = MD_EXCEPTION_STREAM; dirent->stream_type = MD_EXCEPTION_STREAM;
dirent->location = exc.location(); dirent->location = exc.location();
exc.get()->thread_id = crashing_tid_; exc.get()->thread_id = GetCrashThread();
exc.get()->exception_record.exception_code = siginfo_->si_signo; exc.get()->exception_record.exception_code = dumper_->crash_signal();
exc.get()->exception_record.exception_address = exc.get()->exception_record.exception_address = dumper_->crash_address();
(uintptr_t) siginfo_->si_addr;
exc.get()->thread_context = crashing_thread_context_; exc.get()->thread_context = crashing_thread_context_;
return true; return true;
@ -945,11 +943,11 @@ class MinidumpWriter {
// Count the number of loaded DSOs // Count the number of loaded DSOs
int dso_count = 0; int dso_count = 0;
struct r_debug debug_entry; struct r_debug debug_entry;
dumper_.CopyFromProcess(&debug_entry, crashing_tid_, r_debug, dumper_->CopyFromProcess(&debug_entry, GetCrashThread(), r_debug,
sizeof(debug_entry)); sizeof(debug_entry));
for (struct link_map* ptr = debug_entry.r_map; ptr; ) { for (struct link_map* ptr = debug_entry.r_map; ptr; ) {
struct link_map map; struct link_map map;
dumper_.CopyFromProcess(&map, crashing_tid_, ptr, sizeof(map)); dumper_->CopyFromProcess(&map, GetCrashThread(), ptr, sizeof(map));
ptr = map.l_next; ptr = map.l_next;
dso_count++; dso_count++;
} }
@ -967,12 +965,12 @@ class MinidumpWriter {
// Iterate over DSOs and write their information to mini dump // Iterate over DSOs and write their information to mini dump
for (struct link_map* ptr = debug_entry.r_map; ptr; ) { for (struct link_map* ptr = debug_entry.r_map; ptr; ) {
struct link_map map; struct link_map map;
dumper_.CopyFromProcess(&map, crashing_tid_, ptr, sizeof(map)); dumper_->CopyFromProcess(&map, GetCrashThread(), ptr, sizeof(map));
ptr = map.l_next; ptr = map.l_next;
char filename[257] = { 0 }; char filename[257] = { 0 };
if (map.l_name) { if (map.l_name) {
dumper_.CopyFromProcess(filename, crashing_tid_, map.l_name, dumper_->CopyFromProcess(filename, GetCrashThread(), map.l_name,
sizeof(filename) - 1); sizeof(filename) - 1);
} }
MDLocationDescriptor location; MDLocationDescriptor location;
if (!minidump_writer_.WriteString(filename, 0, &location)) if (!minidump_writer_.WriteString(filename, 0, &location))
@ -1001,8 +999,8 @@ class MinidumpWriter {
debug.get()->dynamic = (void*)&_DYNAMIC; debug.get()->dynamic = (void*)&_DYNAMIC;
char *dso_debug_data = new char[dynamic_length]; char *dso_debug_data = new char[dynamic_length];
dumper_.CopyFromProcess(dso_debug_data, crashing_tid_, &_DYNAMIC, dumper_->CopyFromProcess(dso_debug_data, GetCrashThread(), &_DYNAMIC,
dynamic_length); dynamic_length);
debug.CopyIndexAfterObject(0, dso_debug_data, dynamic_length); debug.CopyIndexAfterObject(0, dso_debug_data, dynamic_length);
delete[] dso_debug_data; delete[] dso_debug_data;
@ -1011,6 +1009,14 @@ class MinidumpWriter {
} }
private: private:
void* Alloc(unsigned bytes) {
return dumper_->allocator()->Alloc(bytes);
}
pid_t GetCrashThread() const {
return dumper_->crash_thread();
}
#if defined(__i386) #if defined(__i386)
uintptr_t GetStackPointer() { uintptr_t GetStackPointer() {
return ucontext_->uc_mcontext.gregs[REG_ESP]; return ucontext_->uc_mcontext.gregs[REG_ESP];
@ -1175,16 +1181,15 @@ class MinidumpWriter {
// to read as much as we can into a buffer. // to read as much as we can into a buffer.
static const unsigned kBufSize = 1024 - 2*sizeof(void*); static const unsigned kBufSize = 1024 - 2*sizeof(void*);
struct Buffers { struct Buffers {
struct Buffers* next; Buffers* next;
size_t len; size_t len;
uint8_t data[kBufSize]; uint8_t data[kBufSize];
} *buffers = } *buffers = reinterpret_cast<Buffers*>(Alloc(sizeof(Buffers)));
(struct Buffers*) dumper_.allocator()->Alloc(sizeof(struct Buffers));
buffers->next = NULL; buffers->next = NULL;
buffers->len = 0; buffers->len = 0;
size_t total = 0; size_t total = 0;
for (struct Buffers* bufptr = buffers;;) { for (Buffers* bufptr = buffers;;) {
ssize_t r; ssize_t r;
do { do {
r = sys_read(fd, &bufptr->data[bufptr->len], kBufSize - bufptr->len); r = sys_read(fd, &bufptr->data[bufptr->len], kBufSize - bufptr->len);
@ -1196,8 +1201,7 @@ class MinidumpWriter {
total += r; total += r;
bufptr->len += r; bufptr->len += r;
if (bufptr->len == kBufSize) { if (bufptr->len == kBufSize) {
bufptr->next = bufptr->next = reinterpret_cast<Buffers*>(Alloc(sizeof(Buffers)));
(struct Buffers*) dumper_.allocator()->Alloc(sizeof(struct Buffers));
bufptr = bufptr->next; bufptr = bufptr->next;
bufptr->next = NULL; bufptr->next = NULL;
bufptr->len = 0; bufptr->len = 0;
@ -1277,16 +1281,14 @@ class MinidumpWriter {
bool WriteProcFile(MDLocationDescriptor* result, pid_t pid, bool WriteProcFile(MDLocationDescriptor* result, pid_t pid,
const char* filename) { const char* filename) {
char buf[NAME_MAX]; char buf[NAME_MAX];
dumper_.BuildProcPath(buf, pid, filename); dumper_->BuildProcPath(buf, pid, filename);
return WriteFile(result, buf); return WriteFile(result, buf);
} }
const char* const filename_; // output filename const char* const filename_; // output filename
const siginfo_t* const siginfo_; // from the signal handler (see sigaction)
const struct ucontext* const ucontext_; // also from the signal handler const struct ucontext* const ucontext_; // also from the signal handler
const struct _libc_fpstate* const float_state_; // ditto const struct _libc_fpstate* const float_state_; // ditto
const pid_t crashing_tid_; // the process which actually crashed LinuxDumper* dumper_;
LinuxDumper dumper_;
MinidumpFileWriter minidump_writer_; MinidumpFileWriter minidump_writer_;
MDLocationDescriptor crashing_thread_context_; MDLocationDescriptor crashing_thread_context_;
// Blocks of memory written to the dump. These are all currently // Blocks of memory written to the dump. These are all currently
@ -1310,7 +1312,12 @@ bool WriteMinidump(const char* filename, pid_t crashing_process,
return false; return false;
const ExceptionHandler::CrashContext* context = const ExceptionHandler::CrashContext* context =
reinterpret_cast<const ExceptionHandler::CrashContext*>(blob); reinterpret_cast<const ExceptionHandler::CrashContext*>(blob);
MinidumpWriter writer(filename, crashing_process, context, mappings); LinuxDumper dumper(crashing_process);
dumper.set_crash_address(
reinterpret_cast<uintptr_t>(context->siginfo.si_addr));
dumper.set_crash_signal(context->siginfo.si_signo);
dumper.set_crash_thread(context->tid);
MinidumpWriter writer(filename, context, mappings, &dumper);
if (!writer.Init()) if (!writer.Init())
return false; return false;
return writer.Dump(); return writer.Dump();

View File

@ -145,6 +145,18 @@ class wasteful_vector {
used_(0) { used_(0) {
} }
T& back() {
return a_[used_ - 1];
}
const T& back() const {
return a_[used_ - 1];
}
bool empty() const {
return used_ == 0;
}
void push_back(const T& new_element) { void push_back(const T& new_element) {
if (used_ == allocated_) if (used_ == allocated_)
Realloc(allocated_ * 2); Realloc(allocated_ * 2);

View File

@ -69,6 +69,7 @@ typedef testing::Test WastefulVectorTest;
TEST(WastefulVectorTest, Setup) { TEST(WastefulVectorTest, Setup) {
PageAllocator allocator_; PageAllocator allocator_;
wasteful_vector<int> v(&allocator_); wasteful_vector<int> v(&allocator_);
ASSERT_TRUE(v.empty());
ASSERT_EQ(v.size(), 0u); ASSERT_EQ(v.size(), 0u);
} }
@ -76,8 +77,12 @@ TEST(WastefulVectorTest, Simple) {
PageAllocator allocator_; PageAllocator allocator_;
wasteful_vector<unsigned> v(&allocator_); wasteful_vector<unsigned> v(&allocator_);
for (unsigned i = 0; i < 256; ++i) for (unsigned i = 0; i < 256; ++i) {
v.push_back(i); v.push_back(i);
ASSERT_EQ(i, v.back());
ASSERT_EQ(&v.back(), &v[i]);
}
ASSERT_FALSE(v.empty());
ASSERT_EQ(v.size(), 256u); ASSERT_EQ(v.size(), 256u);
for (unsigned i = 0; i < 256; ++i) for (unsigned i = 0; i < 256; ++i)
ASSERT_EQ(v[i], i); ASSERT_EQ(v[i], i);