зеркало из https://github.com/mozilla/pjs.git
bug 559228 - Update Breakpad to r652
--HG-- extra : rebase_source : bec760bf86aa27731fcced3bfea3dcfaf5213293
This commit is contained in:
Родитель
c780782545
Коммит
f87aa69d2c
|
@ -0,0 +1,5 @@
|
|||
repo: aa80aeafa44f5c17c84e1dac5a7119a6d1ef4341
|
||||
node: 2645d42a92c4144ec095d774a32d2fcaec1afa0b
|
||||
branch: default
|
||||
latesttag: null
|
||||
latesttagdistance: 581
|
|
@ -0,0 +1,7 @@
|
|||
[.]
|
||||
src/testing -r175 http://googlemock.googlecode.com/svn/trunk/
|
||||
src/tools/gyp -r762 http://gyp.googlecode.com/svn/trunk
|
||||
[src/third_party/glog]
|
||||
glog http://google-glog.googlecode.com/svn/trunk
|
||||
[src/third_party/protobuf]
|
||||
protobuf http://protobuf.googlecode.com/svn/trunk
|
|
@ -1,2 +1,43 @@
|
|||
Breakpad is a set of client and server components which implement a
|
||||
crash-reporting system.
|
||||
|
||||
|
||||
-----
|
||||
Getting started in 32-bit mode (from trunk)
|
||||
Configure: CXXFLAGS=-m32 CFLAGS=-m32 CPPFLAGS=-m32 ./configure
|
||||
Build: make
|
||||
Test: make check
|
||||
Install: make install
|
||||
|
||||
If you need to reconfigure your build be sure to run "make distclean" first.
|
||||
|
||||
|
||||
-----
|
||||
To request change review:
|
||||
0. Get access to a read-write copy of source.
|
||||
Owners at http://code.google.com/p/google-breakpad/ are able to grant
|
||||
this access.
|
||||
|
||||
1. Check out a read-write copy of source using instructions at
|
||||
http://code.google.com/p/google-breakpad/source/checkout
|
||||
|
||||
2. Make changes. Build and test your changes.
|
||||
For core code like processor use methods above.
|
||||
For linux/mac/windows, there are test targets in each project file.
|
||||
|
||||
3. Download http://codereview.appspot.com/static/upload.py
|
||||
|
||||
4. Run upload.py from the 'src' directory:
|
||||
upload.py --server=breakpad.appspot.com
|
||||
|
||||
You will be prompted for credential and a description.
|
||||
|
||||
5. At http://breakpad.appspot.com you'll find your issue listed; click on it,
|
||||
and select Publish+Mail, and enter in the code reviewer and CC
|
||||
google-breakpad-dev@googlegroups.com
|
||||
|
||||
6. When applying code review feedback, specify the '-i' option when running
|
||||
upload.py again and pass the issue number so it updates the existing issue,
|
||||
rather than creating a new one.
|
||||
Be sure to rerun upload.py from the same directory as you did for previous
|
||||
uploads to allow for proper diff calculations.
|
||||
|
|
|
@ -72,6 +72,7 @@
|
|||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <sys/signal.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/ucontext.h>
|
||||
|
@ -268,7 +269,10 @@ void ExceptionHandler::SignalHandler(int sig, siginfo_t* info, void* uc) {
|
|||
// crashed. The default action for all the signals which we catch is Core, so
|
||||
// this is the end of us.
|
||||
signal(sig, SIG_DFL);
|
||||
tgkill(getpid(), sys_gettid(), sig);
|
||||
|
||||
// TODO(markus): mask signal and return to caller
|
||||
tgkill(getpid(), syscall(__NR_gettid), sig);
|
||||
_exit(1);
|
||||
|
||||
// not reached.
|
||||
}
|
||||
|
@ -296,7 +300,7 @@ bool ExceptionHandler::HandleSignal(int sig, siginfo_t* info, void* uc) {
|
|||
return false;
|
||||
|
||||
// Allow ourselves to be dumped.
|
||||
sys_prctl(PR_SET_DUMPABLE, 1);
|
||||
prctl(PR_SET_DUMPABLE, 1);
|
||||
CrashContext context;
|
||||
memcpy(&context.siginfo, info, sizeof(siginfo_t));
|
||||
memcpy(&context.context, uc, sizeof(struct ucontext));
|
||||
|
@ -309,7 +313,7 @@ bool ExceptionHandler::HandleSignal(int sig, siginfo_t* info, void* uc) {
|
|||
sizeof(context.float_state));
|
||||
}
|
||||
#endif
|
||||
context.tid = sys_gettid();
|
||||
context.tid = syscall(__NR_gettid);
|
||||
if (crash_handler_ != NULL) {
|
||||
if (crash_handler_(&context, sizeof(context),
|
||||
callback_context_)) {
|
||||
|
|
|
@ -76,6 +76,26 @@ bool AttachThread(pid_t pid) {
|
|||
return false;
|
||||
}
|
||||
}
|
||||
#if defined(__i386) || defined(__x86_64)
|
||||
// On x86, the stack pointer is NULL or -1, when executing trusted code in
|
||||
// the seccomp sandbox. Not only does this cause difficulties down the line
|
||||
// when trying to dump the thread's stack, it also results in the minidumps
|
||||
// containing information about the trusted threads. This information is
|
||||
// generally completely meaningless and just pollutes the minidumps.
|
||||
// We thus test the stack pointer and exclude any threads that are part of
|
||||
// the seccomp sandbox's trusted code.
|
||||
user_regs_struct regs;
|
||||
if (sys_ptrace(PTRACE_GETREGS, pid, NULL, ®s) == -1 ||
|
||||
#if defined(__i386)
|
||||
!regs.esp
|
||||
#elif defined(__x86_64)
|
||||
!regs.rsp
|
||||
#endif
|
||||
) {
|
||||
sys_ptrace(PTRACE_DETACH, pid, NULL, NULL);
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -138,11 +158,19 @@ bool LinuxDumper::Init() {
|
|||
bool LinuxDumper::ThreadsAttach() {
|
||||
if (threads_suspended_)
|
||||
return true;
|
||||
bool good = true;
|
||||
for (size_t i = 0; i < threads_.size(); ++i)
|
||||
good &= AttachThread(threads_[i]);
|
||||
for (size_t i = 0; i < threads_.size(); ++i) {
|
||||
if (!AttachThread(threads_[i])) {
|
||||
// If the thread either disappeared before we could attach to it, or if
|
||||
// it was part of the seccomp sandbox's trusted code, it is OK to
|
||||
// silently drop it from the minidump.
|
||||
memmove(&threads_[i], &threads_[i+1],
|
||||
(threads_.size() - i - 1) * sizeof(threads_[i]));
|
||||
threads_.resize(threads_.size() - 1);
|
||||
--i;
|
||||
}
|
||||
}
|
||||
threads_suspended_ = true;
|
||||
return good;
|
||||
return threads_.size() > 0;
|
||||
}
|
||||
|
||||
bool LinuxDumper::ThreadsDetach() {
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
/* Copyright (c) 2010, 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_extension_linux.h: A definition of exception codes for
|
||||
* Linux
|
||||
*
|
||||
* (This is C99 source, please don't corrupt it with C++.)
|
||||
*
|
||||
* Author: Adam Langley
|
||||
* Split into its own file: Markus Gutschke */
|
||||
|
||||
|
||||
#ifndef SRC_CLIENT_LINUX_MINIDUMP_WRITER_MINIDUMP_EXTENSION_LINUX_H_
|
||||
#define SRC_CLIENT_LINUX_MINIDUMP_WRITER_MINIDUMP_EXTENSION_LINUX_H_
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "google_breakpad/common/breakpad_types.h"
|
||||
|
||||
// These are additional minidump stream values which are specific to the linux
|
||||
// breakpad implementation.
|
||||
enum {
|
||||
MD_LINUX_CPU_INFO = 0x47670003, /* /proc/cpuinfo */
|
||||
MD_LINUX_PROC_STATUS = 0x47670004, /* /proc/$x/status */
|
||||
MD_LINUX_LSB_RELEASE = 0x47670005, /* /etc/lsb-release */
|
||||
MD_LINUX_CMD_LINE = 0x47670006, /* /proc/$x/cmdline */
|
||||
MD_LINUX_ENVIRON = 0x47670007, /* /proc/$x/environ */
|
||||
MD_LINUX_AUXV = 0x47670008, /* /proc/$x/auxv */
|
||||
MD_LINUX_MAPS = 0x47670009, /* /proc/$x/maps */
|
||||
MD_LINUX_DSO_DEBUG = 0x4767000A /* DSO data */
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
void* addr;
|
||||
MDRVA name;
|
||||
void* ld;
|
||||
} MDRawLinkMap;
|
||||
|
||||
typedef struct {
|
||||
u_int32_t version;
|
||||
MDRVA map;
|
||||
u_int32_t dso_count;
|
||||
void* brk;
|
||||
void* ldbase;
|
||||
void* dynamic;
|
||||
} MDRawDebug;
|
||||
|
||||
#endif // SRC_CLIENT_LINUX_MINIDUMP_WRITER_MINIDUMP_EXTENSION_LINUX_H_
|
|
@ -48,6 +48,7 @@
|
|||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <link.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/ucontext.h>
|
||||
|
@ -62,22 +63,12 @@
|
|||
#include "client/linux/handler/exception_handler.h"
|
||||
#include "client/linux/minidump_writer/line_reader.h"
|
||||
#include "client/linux/minidump_writer/linux_dumper.h"
|
||||
#include "client/linux/minidump_writer/minidump_extension_linux.h"
|
||||
#include "common/linux/linux_libc_support.h"
|
||||
#include "common/linux/linux_syscall_support.h"
|
||||
|
||||
using google_breakpad::ThreadInfo;
|
||||
|
||||
// These are additional minidump stream values which are specific to the linux
|
||||
// breakpad implementation.
|
||||
enum {
|
||||
MD_LINUX_CPU_INFO = 0x47670003, /* /proc/cpuinfo */
|
||||
MD_LINUX_PROC_STATUS = 0x47670004, /* /proc/$x/status */
|
||||
MD_LINUX_LSB_RELEASE = 0x47670005, /* /etc/lsb-release */
|
||||
MD_LINUX_CMD_LINE = 0x47670006, /* /proc/$x/cmdline */
|
||||
MD_LINUX_ENVIRON = 0x47670007, /* /proc/$x/environ */
|
||||
MD_LINUX_AUXV = 0x47670008 /* /proc/$x/auxv */
|
||||
};
|
||||
|
||||
// Minidump defines register structures which are different from the raw
|
||||
// structures which we get from the kernel. These are platform specific
|
||||
// functions to juggle the ucontext and user structures into minidump format.
|
||||
|
@ -457,9 +448,26 @@ class MinidumpWriter {
|
|||
}
|
||||
|
||||
bool Dump() {
|
||||
// The dynamic linker makes information available that helps gdb find all
|
||||
// DSOs loaded into the program. If we can access this information, we dump
|
||||
// it to a MD_LINUX_DSO_DEBUG stream.
|
||||
struct r_debug* r_debug = NULL;
|
||||
uint32_t dynamic_length = 0;
|
||||
|
||||
for (int i = 0;;) {
|
||||
ElfW(Dyn) dyn;
|
||||
dynamic_length += sizeof(dyn);
|
||||
dumper_.CopyFromProcess(&dyn, crashing_tid_, _DYNAMIC+i++, sizeof(dyn));
|
||||
if (dyn.d_tag == DT_DEBUG) {
|
||||
r_debug = (struct r_debug*)dyn.d_un.d_ptr;
|
||||
continue;
|
||||
} else if (dyn.d_tag == DT_NULL)
|
||||
break;
|
||||
}
|
||||
|
||||
// A minidump file contains a number of tagged streams. This is the number
|
||||
// of stream which we write.
|
||||
static const unsigned kNumWriters = 11;
|
||||
const unsigned kNumWriters = 11 + !!r_debug;
|
||||
|
||||
TypedMDRVA<MDRawHeader> header(&minidump_writer_);
|
||||
TypedMDRVA<MDRawDirectory> dir(&minidump_writer_);
|
||||
|
@ -526,11 +534,18 @@ class MinidumpWriter {
|
|||
NullifyDirectoryEntry(&dirent);
|
||||
dir.CopyIndex(dir_index++, &dirent);
|
||||
|
||||
dirent.stream_type = MD_LINUX_AUXV;
|
||||
dirent.stream_type = MD_LINUX_MAPS;
|
||||
if (!WriteProcFile(&dirent.location, crashing_tid_, "maps"))
|
||||
NullifyDirectoryEntry(&dirent);
|
||||
dir.CopyIndex(dir_index++, &dirent);
|
||||
|
||||
if (r_debug) {
|
||||
dirent.stream_type = MD_LINUX_DSO_DEBUG;
|
||||
if (!WriteDSODebugStream(&dirent, r_debug, dynamic_length))
|
||||
NullifyDirectoryEntry(&dirent);
|
||||
dir.CopyIndex(dir_index++, &dirent);
|
||||
}
|
||||
|
||||
// If you add more directory entries, don't forget to update kNumWriters,
|
||||
// above.
|
||||
|
||||
|
@ -538,6 +553,123 @@ class MinidumpWriter {
|
|||
return true;
|
||||
}
|
||||
|
||||
// Check if the top of the stack is part of a system call that has been
|
||||
// redirected by the seccomp sandbox. If so, try to pop the stack frames
|
||||
// all the way back to the point where the interception happened.
|
||||
void PopSeccompStackFrame(RawContextCPU* cpu, const MDRawThread& thread,
|
||||
uint8_t* stack_copy) {
|
||||
#if defined(__x86_64)
|
||||
u_int64_t bp = cpu->rbp;
|
||||
u_int64_t top = thread.stack.start_of_memory_range;
|
||||
for (int i = 4; i--; ) {
|
||||
if (bp < top ||
|
||||
bp + sizeof(bp) > thread.stack.start_of_memory_range +
|
||||
thread.stack.memory.data_size ||
|
||||
bp & 1) {
|
||||
break;
|
||||
}
|
||||
uint64_t old_top = top;
|
||||
top = bp;
|
||||
u_int8_t* bp_addr = stack_copy + bp - thread.stack.start_of_memory_range;
|
||||
memcpy(&bp, bp_addr, sizeof(bp));
|
||||
if (bp == 0xDEADBEEFDEADBEEFull) {
|
||||
struct {
|
||||
uint64_t r15;
|
||||
uint64_t r14;
|
||||
uint64_t r13;
|
||||
uint64_t r12;
|
||||
uint64_t r11;
|
||||
uint64_t r10;
|
||||
uint64_t r9;
|
||||
uint64_t r8;
|
||||
uint64_t rdi;
|
||||
uint64_t rsi;
|
||||
uint64_t rdx;
|
||||
uint64_t rcx;
|
||||
uint64_t rbx;
|
||||
uint64_t deadbeef;
|
||||
uint64_t rbp;
|
||||
uint64_t fakeret;
|
||||
uint64_t ret;
|
||||
/* char redzone[128]; */
|
||||
} seccomp_stackframe;
|
||||
if (top - offsetof(typeof(seccomp_stackframe), deadbeef) < old_top ||
|
||||
top - offsetof(typeof(seccomp_stackframe), deadbeef) +
|
||||
sizeof(seccomp_stackframe) >
|
||||
thread.stack.start_of_memory_range+thread.stack.memory.data_size) {
|
||||
break;
|
||||
}
|
||||
memcpy(&seccomp_stackframe,
|
||||
bp_addr - offsetof(typeof(seccomp_stackframe), deadbeef),
|
||||
sizeof(seccomp_stackframe));
|
||||
cpu->rbx = seccomp_stackframe.rbx;
|
||||
cpu->rcx = seccomp_stackframe.rcx;
|
||||
cpu->rdx = seccomp_stackframe.rdx;
|
||||
cpu->rsi = seccomp_stackframe.rsi;
|
||||
cpu->rdi = seccomp_stackframe.rdi;
|
||||
cpu->rbp = seccomp_stackframe.rbp;
|
||||
cpu->rsp = top + 4*sizeof(uint64_t) + 128;
|
||||
cpu->r8 = seccomp_stackframe.r8;
|
||||
cpu->r9 = seccomp_stackframe.r9;
|
||||
cpu->r10 = seccomp_stackframe.r10;
|
||||
cpu->r11 = seccomp_stackframe.r11;
|
||||
cpu->r12 = seccomp_stackframe.r12;
|
||||
cpu->r13 = seccomp_stackframe.r13;
|
||||
cpu->r14 = seccomp_stackframe.r14;
|
||||
cpu->r15 = seccomp_stackframe.r15;
|
||||
cpu->rip = seccomp_stackframe.fakeret;
|
||||
return;
|
||||
}
|
||||
}
|
||||
#elif defined(__i386)
|
||||
u_int32_t bp = cpu->ebp;
|
||||
u_int32_t top = thread.stack.start_of_memory_range;
|
||||
for (int i = 4; i--; ) {
|
||||
if (bp < top ||
|
||||
bp + sizeof(bp) > thread.stack.start_of_memory_range +
|
||||
thread.stack.memory.data_size ||
|
||||
bp & 1) {
|
||||
break;
|
||||
}
|
||||
uint32_t old_top = top;
|
||||
top = bp;
|
||||
u_int8_t* bp_addr = stack_copy + bp - thread.stack.start_of_memory_range;
|
||||
memcpy(&bp, bp_addr, sizeof(bp));
|
||||
if (bp == 0xDEADBEEFu) {
|
||||
struct {
|
||||
uint32_t edi;
|
||||
uint32_t esi;
|
||||
uint32_t edx;
|
||||
uint32_t ecx;
|
||||
uint32_t ebx;
|
||||
uint32_t deadbeef;
|
||||
uint32_t ebp;
|
||||
uint32_t fakeret;
|
||||
uint32_t ret;
|
||||
} seccomp_stackframe;
|
||||
if (top - offsetof(typeof(seccomp_stackframe), deadbeef) < old_top ||
|
||||
top - offsetof(typeof(seccomp_stackframe), deadbeef) +
|
||||
sizeof(seccomp_stackframe) >
|
||||
thread.stack.start_of_memory_range+thread.stack.memory.data_size) {
|
||||
break;
|
||||
}
|
||||
memcpy(&seccomp_stackframe,
|
||||
bp_addr - offsetof(typeof(seccomp_stackframe), deadbeef),
|
||||
sizeof(seccomp_stackframe));
|
||||
cpu->ebx = seccomp_stackframe.ebx;
|
||||
cpu->ecx = seccomp_stackframe.ecx;
|
||||
cpu->edx = seccomp_stackframe.edx;
|
||||
cpu->esi = seccomp_stackframe.esi;
|
||||
cpu->edi = seccomp_stackframe.edi;
|
||||
cpu->ebp = seccomp_stackframe.ebp;
|
||||
cpu->esp = top + 4*sizeof(void*);
|
||||
cpu->eip = seccomp_stackframe.fakeret;
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// Write information about the threads.
|
||||
bool WriteThreadListStream(MDRawDirectory* dirent) {
|
||||
const unsigned num_threads = dumper_.threads().size();
|
||||
|
@ -578,6 +710,7 @@ class MinidumpWriter {
|
|||
return false;
|
||||
my_memset(cpu.get(), 0, sizeof(RawContextCPU));
|
||||
CPUFillFromUContext(cpu.get(), ucontext_, float_state_);
|
||||
PopSeccompStackFrame(cpu.get(), thread, stack_copy);
|
||||
thread.thread_context = cpu.location();
|
||||
crashing_thread_context_ = cpu.location();
|
||||
} else {
|
||||
|
@ -600,6 +733,7 @@ class MinidumpWriter {
|
|||
return false;
|
||||
my_memset(cpu.get(), 0, sizeof(RawContextCPU));
|
||||
CPUFillFromThreadInfo(cpu.get(), info);
|
||||
PopSeccompStackFrame(cpu.get(), thread, stack_copy);
|
||||
thread.thread_context = cpu.location();
|
||||
|
||||
if ((pid_t)thread.thread_id == crashing_tid_) {
|
||||
|
@ -738,6 +872,83 @@ class MinidumpWriter {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool WriteDSODebugStream(MDRawDirectory* dirent, struct r_debug* r_debug,
|
||||
uint32_t dynamic_length) {
|
||||
// The caller provided us with a pointer to "struct r_debug". We can
|
||||
// look up the "r_map" field to get a linked list of all loaded DSOs.
|
||||
// Our list of DSOs potentially is different from the ones in the crashing
|
||||
// process. So, we have to be careful to never dereference pointers
|
||||
// directly. Instead, we use CopyFromProcess() everywhere.
|
||||
// See <link.h> for a more detailed discussion of the how the dynamic
|
||||
// loader communicates with debuggers.
|
||||
|
||||
// Count the number of loaded DSOs
|
||||
int dso_count = 0;
|
||||
struct r_debug debug_entry;
|
||||
dumper_.CopyFromProcess(&debug_entry, crashing_tid_, r_debug,
|
||||
sizeof(debug_entry));
|
||||
for (struct link_map* ptr = debug_entry.r_map; ptr; ) {
|
||||
struct link_map map;
|
||||
dumper_.CopyFromProcess(&map, crashing_tid_, ptr, sizeof(map));
|
||||
ptr = map.l_next;
|
||||
dso_count++;
|
||||
}
|
||||
|
||||
MDRVA linkmap_rva = minidump_writer_.kInvalidMDRVA;
|
||||
if (dso_count > 0) {
|
||||
// If we have at least one DSO, create an array of MDRawLinkMap
|
||||
// entries in the minidump file.
|
||||
TypedMDRVA<MDRawLinkMap> linkmap(&minidump_writer_);
|
||||
if (!linkmap.AllocateArray(dso_count))
|
||||
return false;
|
||||
linkmap_rva = linkmap.location().rva;
|
||||
int idx = 0;
|
||||
|
||||
// Iterate over DSOs and write their information to mini dump
|
||||
for (struct link_map* ptr = debug_entry.r_map; ptr; ) {
|
||||
struct link_map map;
|
||||
dumper_.CopyFromProcess(&map, crashing_tid_, ptr, sizeof(map));
|
||||
ptr = map.l_next;
|
||||
char filename[257] = { 0 };
|
||||
if (map.l_name) {
|
||||
dumper_.CopyFromProcess(filename, crashing_tid_, map.l_name,
|
||||
sizeof(filename) - 1);
|
||||
}
|
||||
MDLocationDescriptor location;
|
||||
if (!minidump_writer_.WriteString(filename, 0, &location))
|
||||
return false;
|
||||
MDRawLinkMap entry;
|
||||
entry.name = location.rva;
|
||||
entry.addr = (void*)map.l_addr;
|
||||
entry.ld = (void*)map.l_ld;
|
||||
linkmap.CopyIndex(idx++, &entry);
|
||||
}
|
||||
}
|
||||
|
||||
// Write MD_LINUX_DSO_DEBUG record
|
||||
TypedMDRVA<MDRawDebug> debug(&minidump_writer_);
|
||||
if (!debug.AllocateObjectAndArray(1, dynamic_length))
|
||||
return false;
|
||||
my_memset(debug.get(), 0, sizeof(MDRawDebug));
|
||||
dirent->stream_type = MD_LINUX_DSO_DEBUG;
|
||||
dirent->location = debug.location();
|
||||
|
||||
debug.get()->version = debug_entry.r_version;
|
||||
debug.get()->map = linkmap_rva;
|
||||
debug.get()->dso_count = dso_count;
|
||||
debug.get()->brk = (void*)debug_entry.r_brk;
|
||||
debug.get()->ldbase = (void*)debug_entry.r_ldbase;
|
||||
debug.get()->dynamic = (void*)&_DYNAMIC;
|
||||
|
||||
char *dso_debug_data = new char[dynamic_length];
|
||||
dumper_.CopyFromProcess(dso_debug_data, crashing_tid_, &_DYNAMIC,
|
||||
dynamic_length);
|
||||
debug.CopyIndexAfterObject(0, dso_debug_data, dynamic_length);
|
||||
delete[] dso_debug_data;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
void NullifyDirectoryEntry(MDRawDirectory* dirent) {
|
||||
dirent->stream_type = 0;
|
||||
|
@ -787,7 +998,7 @@ class MinidumpWriter {
|
|||
i < sizeof(cpu_info_table) / sizeof(cpu_info_table[0]);
|
||||
i++) {
|
||||
CpuInfoEntry* entry = &cpu_info_table[i];
|
||||
if (entry->found)
|
||||
if (entry->found && i)
|
||||
continue;
|
||||
if (!strncmp(line, entry->info_name, strlen(entry->info_name))) {
|
||||
const char* value = strchr(line, ':');
|
||||
|
@ -873,29 +1084,48 @@ popline:
|
|||
// We can't stat the files because several of the files that we want to
|
||||
// read are kernel seqfiles, which always have a length of zero. So we have
|
||||
// to read as much as we can into a buffer.
|
||||
static const unsigned kMaxFileSize = 1024;
|
||||
uint8_t* data = (uint8_t*) dumper_.allocator()->Alloc(kMaxFileSize);
|
||||
static const unsigned kBufSize = 1024 - 2*sizeof(void*);
|
||||
struct Buffers {
|
||||
struct Buffers* next;
|
||||
size_t len;
|
||||
uint8_t data[kBufSize];
|
||||
} *buffers =
|
||||
(struct Buffers*) dumper_.allocator()->Alloc(sizeof(struct Buffers));
|
||||
buffers->next = NULL;
|
||||
buffers->len = 0;
|
||||
|
||||
size_t done = 0;
|
||||
while (done < kMaxFileSize) {
|
||||
size_t total = 0;
|
||||
for (struct Buffers* bufptr = buffers;;) {
|
||||
ssize_t r;
|
||||
do {
|
||||
r = sys_read(fd, data + done, kMaxFileSize - done);
|
||||
r = sys_read(fd, &bufptr->data[bufptr->len], kBufSize - bufptr->len);
|
||||
} while (r == -1 && errno == EINTR);
|
||||
|
||||
if (r < 1)
|
||||
break;
|
||||
done += r;
|
||||
|
||||
total += r;
|
||||
bufptr->len += r;
|
||||
if (bufptr->len == kBufSize) {
|
||||
bufptr->next =
|
||||
(struct Buffers*) dumper_.allocator()->Alloc(sizeof(struct Buffers));
|
||||
bufptr = bufptr->next;
|
||||
bufptr->next = NULL;
|
||||
bufptr->len = 0;
|
||||
}
|
||||
}
|
||||
sys_close(fd);
|
||||
|
||||
if (!done)
|
||||
if (!total)
|
||||
return false;
|
||||
|
||||
UntypedMDRVA memory(&minidump_writer_);
|
||||
if (!memory.Allocate(done))
|
||||
if (!memory.Allocate(total))
|
||||
return false;
|
||||
memory.Copy(data, done);
|
||||
for (MDRVA pos = memory.position(); buffers; buffers = buffers->next) {
|
||||
memory.Copy(pos, &buffers->data, buffers->len);
|
||||
pos += buffers->len;
|
||||
}
|
||||
*result = memory.location();
|
||||
return true;
|
||||
}
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -110,16 +110,19 @@ typedef bool (*BreakpadFilterCallback)(int exception_type,
|
|||
// Key: Value:
|
||||
// BREAKPAD_PRODUCT Product name (e.g., "MyAwesomeProduct")
|
||||
// This one is used as the key to identify
|
||||
// the product when uploading
|
||||
// the product when uploading. Falls back to
|
||||
// CFBundleName if not specified.
|
||||
// REQUIRED
|
||||
//
|
||||
// BREAKPAD_PRODUCT_DISPLAY This is the display name, e.g. a pretty
|
||||
// name for the product when the crash_sender
|
||||
// pops up UI for the user. Falls back to
|
||||
// pops up UI for the user. Falls back first to
|
||||
// CFBundleDisplayName and then to
|
||||
// BREAKPAD_PRODUCT if not specified.
|
||||
//
|
||||
// BREAKPAD_VERSION Product version (e.g., 1.2.3), used
|
||||
// as metadata for crash report
|
||||
// as metadata for crash report. Falls back to
|
||||
// CFBundleVersion if not specified.
|
||||
// REQUIRED
|
||||
//
|
||||
// BREAKPAD_VENDOR Vendor name, used in UI (e.g. "A report has
|
||||
|
|
|
@ -55,6 +55,10 @@
|
|||
|
||||
|
||||
using google_breakpad::KeyValueEntry;
|
||||
using google_breakpad::MachPortSender;
|
||||
using google_breakpad::MachReceiveMessage;
|
||||
using google_breakpad::MachSendMessage;
|
||||
using google_breakpad::ReceivePort;
|
||||
using google_breakpad::SimpleStringDictionary;
|
||||
using google_breakpad::SimpleStringDictionaryIterator;
|
||||
|
||||
|
@ -265,7 +269,7 @@ bool Breakpad::ExceptionHandlerDirectCallback(void *context,
|
|||
//=============================================================================
|
||||
#pragma mark -
|
||||
|
||||
#include <mach-o/dyld.h>
|
||||
#include <dlfcn.h>
|
||||
|
||||
//=============================================================================
|
||||
// Returns the pathname to the Resources directory for this version of
|
||||
|
@ -286,23 +290,16 @@ NSString * GetResourcePath() {
|
|||
//
|
||||
|
||||
// Get the pathname to the code which contains this function
|
||||
void *address = nil;
|
||||
NSModule module = nil;
|
||||
_dyld_lookup_and_bind_fully("_GetResourcePath",
|
||||
&address,
|
||||
&module);
|
||||
|
||||
if (module && address) {
|
||||
const char* moduleName = NSNameOfModule(module);
|
||||
if (moduleName) {
|
||||
Dl_info info;
|
||||
if (dladdr((const void*)GetResourcePath, &info) != 0) {
|
||||
NSFileManager *filemgr = [NSFileManager defaultManager];
|
||||
NSString *filePath =
|
||||
[filemgr stringWithFileSystemRepresentation:info.dli_fname
|
||||
length:strlen(info.dli_fname)];
|
||||
NSString *bundlePath = [filePath stringByDeletingLastPathComponent];
|
||||
// The "Resources" directory should be in the same directory as the
|
||||
// executable code, since that's how the Breakpad framework is built.
|
||||
resourcePath = [NSString stringWithUTF8String:moduleName];
|
||||
resourcePath = [resourcePath stringByDeletingLastPathComponent];
|
||||
resourcePath = [resourcePath stringByAppendingPathComponent:@"Resources/"];
|
||||
} else {
|
||||
DEBUGLOG(stderr, "Missing moduleName\n");
|
||||
}
|
||||
resourcePath = [bundlePath stringByAppendingPathComponent:@"Resources/"];
|
||||
} else {
|
||||
DEBUGLOG(stderr, "Could not find GetResourcePath\n");
|
||||
// fallback plan
|
||||
|
@ -371,7 +368,8 @@ bool Breakpad::Initialize(NSDictionary *parameters) {
|
|||
|
||||
// Create the handler (allocating it in our special protected pool)
|
||||
handler_ =
|
||||
new (gBreakpadAllocator->Allocate(sizeof(google_breakpad::ExceptionHandler)))
|
||||
new (gBreakpadAllocator->Allocate(
|
||||
sizeof(google_breakpad::ExceptionHandler)))
|
||||
google_breakpad::ExceptionHandler(
|
||||
Breakpad::ExceptionHandlerDirectCallback, this, true);
|
||||
return true;
|
||||
|
@ -408,7 +406,8 @@ bool Breakpad::ExtractParameters(NSDictionary *parameters) {
|
|||
[parameters objectForKey:@BREAKPAD_REPORTER_EXE_LOCATION];
|
||||
NSString *timeout = [parameters objectForKey:@BREAKPAD_CONFIRM_TIMEOUT];
|
||||
NSArray *logFilePaths = [parameters objectForKey:@BREAKPAD_LOGFILES];
|
||||
NSString *logFileTailSize = [parameters objectForKey:@BREAKPAD_LOGFILE_UPLOAD_SIZE];
|
||||
NSString *logFileTailSize =
|
||||
[parameters objectForKey:@BREAKPAD_LOGFILE_UPLOAD_SIZE];
|
||||
NSString *requestUserText =
|
||||
[parameters objectForKey:@BREAKPAD_REQUEST_COMMENTS];
|
||||
NSString *requestEmail = [parameters objectForKey:@BREAKPAD_REQUEST_EMAIL];
|
||||
|
@ -431,8 +430,12 @@ bool Breakpad::ExtractParameters(NSDictionary *parameters) {
|
|||
if (!product)
|
||||
product = [parameters objectForKey:@"CFBundleName"];
|
||||
|
||||
if (!display)
|
||||
if (!display) {
|
||||
display = [parameters objectForKey:@"CFBundleDisplayName"];
|
||||
if (!display) {
|
||||
display = product;
|
||||
}
|
||||
}
|
||||
|
||||
if (!version)
|
||||
version = [parameters objectForKey:@"CFBundleVersion"];
|
||||
|
@ -512,8 +515,10 @@ bool Breakpad::ExtractParameters(NSDictionary *parameters) {
|
|||
// Find Reporter.
|
||||
if (!reporterPathString) {
|
||||
reporterPathString =
|
||||
[resourcePath stringByAppendingPathComponent:@"crash_report_sender.app"];
|
||||
reporterPathString = [[NSBundle bundleWithPath:reporterPathString] executablePath];
|
||||
[resourcePath
|
||||
stringByAppendingPathComponent:@"crash_report_sender.app"];
|
||||
reporterPathString =
|
||||
[[NSBundle bundleWithPath:reporterPathString] executablePath];
|
||||
}
|
||||
|
||||
// Verify that there is a Reporter application.
|
||||
|
@ -573,7 +578,7 @@ bool Breakpad::ExtractParameters(NSDictionary *parameters) {
|
|||
struct timeval tv;
|
||||
gettimeofday(&tv, NULL);
|
||||
char timeStartedString[32];
|
||||
sprintf(timeStartedString, "%d", tv.tv_sec);
|
||||
sprintf(timeStartedString, "%zd", tv.tv_sec);
|
||||
dictionary.SetKeyValue(BREAKPAD_PROCESS_START_TIME,
|
||||
timeStartedString);
|
||||
|
||||
|
@ -591,7 +596,7 @@ bool Breakpad::ExtractParameters(NSDictionary *parameters) {
|
|||
// For each key-value pair, call BreakpadAddUploadParameter()
|
||||
NSEnumerator *keyEnumerator = [serverParameters keyEnumerator];
|
||||
NSString *aParameter;
|
||||
while (aParameter = [keyEnumerator nextObject]) {
|
||||
while ((aParameter = [keyEnumerator nextObject])) {
|
||||
BreakpadAddUploadParameter(this, aParameter,
|
||||
[serverParameters objectForKey:aParameter]);
|
||||
}
|
||||
|
@ -609,7 +614,7 @@ void Breakpad::SetKeyValue(NSString *key, NSString *value) {
|
|||
}
|
||||
|
||||
//=============================================================================
|
||||
NSString * Breakpad::KeyValue(NSString *key) {
|
||||
NSString *Breakpad::KeyValue(NSString *key) {
|
||||
if (!config_params_ || !key)
|
||||
return nil;
|
||||
|
||||
|
@ -619,8 +624,7 @@ NSString * Breakpad::KeyValue(NSString *key) {
|
|||
|
||||
//=============================================================================
|
||||
void Breakpad::RemoveKeyValue(NSString *key) {
|
||||
if (!config_params_ || !key)
|
||||
return;
|
||||
if (!config_params_ || !key) return;
|
||||
|
||||
config_params_->RemoveKey([key UTF8String]);
|
||||
}
|
||||
|
@ -712,8 +716,7 @@ bool Breakpad::HandleException(int exception_type,
|
|||
|
||||
// If we don't want any forwarding, return true here to indicate that we've
|
||||
// processed things as much as we want.
|
||||
if (send_and_exit_)
|
||||
return true;
|
||||
if (send_and_exit_) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -116,7 +116,7 @@ void OnDemandServer::LaunchOnDemand() {
|
|||
// and holding on to this port delays launching until the current process
|
||||
// exits!
|
||||
mach_port_deallocate(mach_task_self(), server_port_);
|
||||
server_port_ = NULL;
|
||||
server_port_ = MACH_PORT_DEAD;
|
||||
|
||||
// Now, the service is still registered and all we need to do is send
|
||||
// a mach message to the service port in order to launch the server.
|
||||
|
|
|
@ -44,6 +44,8 @@
|
|||
#import "common/mac/SimpleStringDictionary.h"
|
||||
#import "common/mac/MachIPC.h"
|
||||
|
||||
#import "GTMDefines.h"
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#if VERBOSE
|
||||
|
@ -91,14 +93,14 @@ static BOOL EnsureDirectoryPathExists(NSString *dirPath) {
|
|||
// Break up the difference into components
|
||||
NSString *diff = [dirPath substringFromIndex:[common length] + 1];
|
||||
NSArray *components = [diff pathComponents];
|
||||
unsigned count = [components count];
|
||||
NSUInteger count = [components count];
|
||||
|
||||
// Rebuild the path one component at a time
|
||||
NSDictionary *attrs =
|
||||
[NSDictionary dictionaryWithObject:[NSNumber numberWithUnsignedLong:0750]
|
||||
forKey:NSFilePosixPermissions];
|
||||
path = common;
|
||||
for (unsigned i = 0; i < count; ++i) {
|
||||
for (NSUInteger i = 0; i < count; ++i) {
|
||||
path = [path stringByAppendingPathComponent:[components objectAtIndex:i]];
|
||||
|
||||
if (![mgr createDirectoryAtPath:path attributes:attrs])
|
||||
|
@ -329,12 +331,12 @@ kern_return_t Inspector::ReadMessages() {
|
|||
// we are expected to read.
|
||||
// Read each key/value pair, one mach message per key/value pair.
|
||||
for (unsigned int i = 0; i < info.parameter_count; ++i) {
|
||||
MachReceiveMessage message;
|
||||
result = receive_port.WaitForMessage(&message, 1000);
|
||||
MachReceiveMessage parameter_message;
|
||||
result = receive_port.WaitForMessage(¶meter_message, 1000);
|
||||
|
||||
if(result == KERN_SUCCESS) {
|
||||
KeyValueMessageData &key_value_data =
|
||||
(KeyValueMessageData&)*message.GetData();
|
||||
(KeyValueMessageData&)*parameter_message.GetData();
|
||||
// If we get a blank key, make sure we don't increment the
|
||||
// parameter count; in some cases (notably on-demand generation
|
||||
// many times in a short period of time) caused the Mach IPC
|
||||
|
@ -376,11 +378,11 @@ void Inspector::SetCrashTimeParameters() {
|
|||
if (processStartTimeString) {
|
||||
time_t processStartTime = strtol(processStartTimeString, NULL, 10);
|
||||
time_t processUptime = tv.tv_sec - processStartTime;
|
||||
sprintf(processUptimeString, "%d", processUptime);
|
||||
sprintf(processUptimeString, "%zd", processUptime);
|
||||
config_params_.SetKeyValue(BREAKPAD_PROCESS_UP_TIME, processUptimeString);
|
||||
}
|
||||
|
||||
sprintf(processCrashtimeString, "%d", tv.tv_sec);
|
||||
sprintf(processCrashtimeString, "%zd", tv.tv_sec);
|
||||
config_params_.SetKeyValue(BREAKPAD_PROCESS_CRASH_TIME,
|
||||
processCrashtimeString);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
// Copyright (c) 2010 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.
|
||||
|
||||
#ifndef CLIENT_MAC_CRASH_GENERATION_CLIENT_INFO_H_
|
||||
#define CLIENT_MAC_CRASH_GENERATION_CLIENT_INFO_H_
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
class ClientInfo {
|
||||
public:
|
||||
explicit ClientInfo(pid_t pid) : pid_(pid) {}
|
||||
|
||||
pid_t pid() const { return pid_; }
|
||||
|
||||
private:
|
||||
pid_t pid_;
|
||||
};
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
#endif // CLIENT_MAC_CRASH_GENERATION_CLIENT_INFO_H_
|
|
@ -0,0 +1,73 @@
|
|||
// Copyright (c) 2010 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.
|
||||
|
||||
#include "client/mac/crash_generation/crash_generation_client.h"
|
||||
|
||||
#include "client/mac/crash_generation/crash_generation_server.h"
|
||||
#include "common/mac/MachIPC.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
bool CrashGenerationClient::RequestDumpForException(
|
||||
int exception_type,
|
||||
int exception_code,
|
||||
int exception_subcode,
|
||||
mach_port_t crashing_thread) {
|
||||
// The server will send a message to this port indicating that it
|
||||
// has finished its work.
|
||||
ReceivePort acknowledge_port;
|
||||
|
||||
MachSendMessage message(kDumpRequestMessage);
|
||||
message.AddDescriptor(mach_task_self()); // this task
|
||||
message.AddDescriptor(crashing_thread); // crashing thread
|
||||
message.AddDescriptor(mach_thread_self()); // handler thread
|
||||
message.AddDescriptor(acknowledge_port.GetPort()); // message receive port
|
||||
|
||||
ExceptionInfo info;
|
||||
info.exception_type = exception_type;
|
||||
info.exception_code = exception_code;
|
||||
info.exception_subcode = exception_subcode;
|
||||
message.SetData(&info, sizeof(info));
|
||||
|
||||
const mach_msg_timeout_t kSendTimeoutMs = 2 * 1000;
|
||||
kern_return_t result = sender_.SendMessage(message, kSendTimeoutMs);
|
||||
if (result != KERN_SUCCESS)
|
||||
return false;
|
||||
|
||||
// Give the server slightly longer to reply since it has to
|
||||
// inspect this task and write the minidump.
|
||||
const mach_msg_timeout_t kReceiveTimeoutMs = 5 * 1000;
|
||||
MachReceiveMessage acknowledge_message;
|
||||
result = acknowledge_port.WaitForMessage(&acknowledge_message,
|
||||
kReceiveTimeoutMs);
|
||||
|
||||
return result == KERN_SUCCESS;
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
|
@ -0,0 +1,65 @@
|
|||
// Copyright (c) 2010 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.
|
||||
|
||||
#ifndef GOOGLE_BREAKPAD_CLIENT_MAC_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H_
|
||||
#define GOOGLE_BREAKPAD_CLIENT_MAC_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H_
|
||||
|
||||
#include "common/mac/MachIPC.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
class CrashGenerationClient {
|
||||
public:
|
||||
explicit CrashGenerationClient(const char* mach_port_name)
|
||||
: sender_(mach_port_name) {
|
||||
}
|
||||
|
||||
// Request the crash server to generate a dump.
|
||||
//
|
||||
// Return true if the dump was successful; false otherwise.
|
||||
bool RequestDumpForException(int exception_type,
|
||||
int exception_code,
|
||||
int exception_subcode,
|
||||
mach_port_t crashing_thread);
|
||||
|
||||
bool RequestDump() {
|
||||
return RequestDumpForException(0, 0, 0, MACH_PORT_NULL);
|
||||
}
|
||||
|
||||
private:
|
||||
MachPortSender sender_;
|
||||
|
||||
// Prevent copy construction and assignment.
|
||||
CrashGenerationClient(const CrashGenerationClient&);
|
||||
CrashGenerationClient& operator=(const CrashGenerationClient&);
|
||||
};
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
#endif // GOOGLE_BREAKPAD_CLIENT_MAC_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H_
|
|
@ -0,0 +1,160 @@
|
|||
// Copyright (c) 2010 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.
|
||||
|
||||
#include "client/mac/crash_generation/crash_generation_server.h"
|
||||
|
||||
#include "client/mac/crash_generation/client_info.h"
|
||||
#include "client/mac/handler/minidump_generator.h"
|
||||
#include "common/mac/scoped_task_suspend-inl.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
CrashGenerationServer::CrashGenerationServer(
|
||||
const char *mach_port_name,
|
||||
OnClientDumpRequestCallback dump_callback,
|
||||
void *dump_context,
|
||||
OnClientExitingCallback exit_callback,
|
||||
void *exit_context,
|
||||
bool generate_dumps,
|
||||
const std::string &dump_path)
|
||||
: dump_callback_(dump_callback),
|
||||
dump_context_(dump_context),
|
||||
exit_callback_(exit_callback),
|
||||
exit_context_(exit_context),
|
||||
generate_dumps_(generate_dumps),
|
||||
dump_dir_(dump_path.empty() ? "/tmp" : dump_path),
|
||||
started_(false),
|
||||
receive_port_(mach_port_name),
|
||||
mach_port_name_(mach_port_name) {
|
||||
}
|
||||
|
||||
CrashGenerationServer::~CrashGenerationServer() {
|
||||
if (started_)
|
||||
Stop();
|
||||
}
|
||||
|
||||
bool CrashGenerationServer::Start() {
|
||||
int thread_create_result = pthread_create(&server_thread_, NULL,
|
||||
&WaitForMessages, this);
|
||||
started_ = thread_create_result == 0;
|
||||
return started_;
|
||||
}
|
||||
|
||||
bool CrashGenerationServer::Stop() {
|
||||
if (!started_)
|
||||
return false;
|
||||
|
||||
// Send a quit message to the background thread, and then join it.
|
||||
MachPortSender sender(mach_port_name_.c_str());
|
||||
MachSendMessage quit_message(kQuitMessage);
|
||||
const mach_msg_timeout_t kSendTimeoutMs = 2 * 1000;
|
||||
kern_return_t result = sender.SendMessage(quit_message, kSendTimeoutMs);
|
||||
if (result == KERN_SUCCESS) {
|
||||
int thread_join_result = pthread_join(server_thread_, NULL);
|
||||
started_ = thread_join_result != 0;
|
||||
}
|
||||
|
||||
return !started_;
|
||||
}
|
||||
|
||||
// static
|
||||
void *CrashGenerationServer::WaitForMessages(void *server) {
|
||||
CrashGenerationServer *self =
|
||||
reinterpret_cast<CrashGenerationServer*>(server);
|
||||
while (self->WaitForOneMessage()) {}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool CrashGenerationServer::WaitForOneMessage() {
|
||||
MachReceiveMessage message;
|
||||
kern_return_t result = receive_port_.WaitForMessage(&message,
|
||||
MACH_MSG_TIMEOUT_NONE);
|
||||
if (result == KERN_SUCCESS) {
|
||||
switch (message.GetMessageID()) {
|
||||
case kDumpRequestMessage: {
|
||||
ExceptionInfo &info = (ExceptionInfo &)*message.GetData();
|
||||
|
||||
mach_port_t remote_task = message.GetTranslatedPort(0);
|
||||
mach_port_t crashing_thread = message.GetTranslatedPort(1);
|
||||
mach_port_t handler_thread = message.GetTranslatedPort(2);
|
||||
mach_port_t ack_port = message.GetTranslatedPort(3);
|
||||
pid_t remote_pid = -1;
|
||||
pid_for_task(remote_task, &remote_pid);
|
||||
ClientInfo client(remote_pid);
|
||||
|
||||
bool result;
|
||||
std::string dump_path;
|
||||
if (generate_dumps_) {
|
||||
ScopedTaskSuspend suspend(remote_task);
|
||||
|
||||
MinidumpGenerator generator(remote_task, handler_thread);
|
||||
dump_path = generator.UniqueNameInDirectory(dump_dir_, NULL);
|
||||
|
||||
if (info.exception_type && info.exception_code) {
|
||||
generator.SetExceptionInformation(info.exception_type,
|
||||
info.exception_code,
|
||||
info.exception_subcode,
|
||||
crashing_thread);
|
||||
}
|
||||
result = generator.Write(dump_path.c_str());
|
||||
} else {
|
||||
result = true;
|
||||
}
|
||||
|
||||
if (result && dump_callback_) {
|
||||
dump_callback_(dump_context_, client, dump_path);
|
||||
}
|
||||
|
||||
// TODO(ted): support a way for the client to send additional data,
|
||||
// perhaps with a callback so users of the server can read the data
|
||||
// themselves?
|
||||
|
||||
if (ack_port != MACH_PORT_DEAD && ack_port != MACH_PORT_NULL) {
|
||||
MachPortSender sender(ack_port);
|
||||
MachSendMessage ack_message(kAcknowledgementMessage);
|
||||
const mach_msg_timeout_t kSendTimeoutMs = 2 * 1000;
|
||||
|
||||
sender.SendMessage(ack_message, kSendTimeoutMs);
|
||||
}
|
||||
|
||||
if (exit_callback_) {
|
||||
exit_callback_(exit_context_, client);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case kQuitMessage:
|
||||
return false;
|
||||
}
|
||||
} else { // result != KERN_SUCCESS
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
|
@ -0,0 +1,139 @@
|
|||
// Copyright (c) 2010 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.
|
||||
|
||||
#ifndef GOOGLE_BREAKPAD_CLIENT_MAC_CRASH_GENERATION_CRASH_GENERATION_SERVER_H_
|
||||
#define GOOGLE_BREAKPAD_CLIENT_MAC_CRASH_GENERATION_CRASH_GENERATION_SERVER_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "common/mac/MachIPC.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
class ClientInfo;
|
||||
|
||||
// Messages the server can read via its mach port
|
||||
enum {
|
||||
kDumpRequestMessage = 1,
|
||||
kAcknowledgementMessage = 2,
|
||||
kQuitMessage = 3
|
||||
};
|
||||
|
||||
// Exception details sent by the client when requesting a dump.
|
||||
struct ExceptionInfo {
|
||||
int exception_type;
|
||||
int exception_code;
|
||||
int exception_subcode;
|
||||
};
|
||||
|
||||
class CrashGenerationServer {
|
||||
public:
|
||||
// WARNING: callbacks may be invoked on a different thread
|
||||
// than that which creates the CrashGenerationServer. They must
|
||||
// be thread safe.
|
||||
typedef void (*OnClientDumpRequestCallback)(void *context,
|
||||
const ClientInfo &client_info,
|
||||
const std::string &file_path);
|
||||
|
||||
typedef void (*OnClientExitingCallback)(void *context,
|
||||
const ClientInfo &client_info);
|
||||
|
||||
// Create an instance with the given parameters.
|
||||
//
|
||||
// mach_port_name: Named server port to listen on.
|
||||
// dump_callback: Callback for a client crash dump request.
|
||||
// dump_context: Context for client crash dump request callback.
|
||||
// exit_callback: Callback for client process exit.
|
||||
// exit_context: Context for client exit callback.
|
||||
// generate_dumps: Whether to automatically generate dumps.
|
||||
// Client code of this class might want to generate dumps explicitly
|
||||
// in the crash dump request callback. In that case, false can be
|
||||
// passed for this parameter.
|
||||
// dump_path: Path for generating dumps; required only if true is
|
||||
// passed for generateDumps parameter; NULL can be passed otherwise.
|
||||
CrashGenerationServer(const char *mach_port_name,
|
||||
OnClientDumpRequestCallback dump_callback,
|
||||
void *dump_context,
|
||||
OnClientExitingCallback exit_callback,
|
||||
void *exit_context,
|
||||
bool generate_dumps,
|
||||
const std::string &dump_path);
|
||||
|
||||
~CrashGenerationServer();
|
||||
|
||||
// Perform initialization steps needed to start listening to clients.
|
||||
//
|
||||
// Return true if initialization is successful; false otherwise.
|
||||
bool Start();
|
||||
|
||||
// Stop the server.
|
||||
bool Stop();
|
||||
|
||||
private:
|
||||
// Return a unique filename at which a minidump can be written.
|
||||
bool MakeMinidumpFilename(std::string &outFilename);
|
||||
|
||||
// Loop reading client messages and responding to them until
|
||||
// a quit message is received.
|
||||
static void *WaitForMessages(void *server);
|
||||
|
||||
// Wait for a single client message and respond to it. Returns false
|
||||
// if a quit message was received or if an error occurred.
|
||||
bool WaitForOneMessage();
|
||||
|
||||
OnClientDumpRequestCallback dump_callback_;
|
||||
void *dump_context_;
|
||||
|
||||
OnClientExitingCallback exit_callback_;
|
||||
void *exit_context_;
|
||||
|
||||
bool generate_dumps_;
|
||||
|
||||
std::string dump_dir_;
|
||||
|
||||
bool started_;
|
||||
|
||||
// The mach port that receives requests to dump from child processes.
|
||||
ReceivePort receive_port_;
|
||||
|
||||
// The name of the mach port. Stored so the Stop method can message
|
||||
// the background thread to shut it down.
|
||||
std::string mach_port_name_;
|
||||
|
||||
// The thread that waits on the receive port.
|
||||
pthread_t server_thread_;
|
||||
|
||||
// Disable copy constructor and operator=.
|
||||
CrashGenerationServer(const CrashGenerationServer&);
|
||||
CrashGenerationServer& operator=(const CrashGenerationServer&);
|
||||
};
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
#endif // GOOGLE_BREAKPAD_CLIENT_MAC_CRASH_GENERATION_CRASH_GENERATION_SERVER_H_
|
|
@ -137,7 +137,7 @@ __breakpad_fdnlist_64(int fd, breakpad_nlist *list, const char **symbolNames) {
|
|||
breakpad_nlist space[BUFSIZ/sizeof (breakpad_nlist)];
|
||||
|
||||
const register char *s1, *s2;
|
||||
register int n, m;
|
||||
register register_t n, m;
|
||||
int maxlen, nreq;
|
||||
off_t sa; /* symbol address */
|
||||
off_t ss; /* start of strings */
|
||||
|
@ -160,14 +160,14 @@ __breakpad_fdnlist_64(int fd, breakpad_nlist *list, const char **symbolNames) {
|
|||
(N_BADMAG(buf) && *((long *)&buf) != MH_MAGIC &&
|
||||
NXSwapBigLongToHost(*((long *)&buf)) != FAT_MAGIC) &&
|
||||
/* nealsid: The following is the big-endian ppc64 check */
|
||||
(*((uint32_t*)&buf)) != FAT_MAGIC) {
|
||||
(*((long*)&buf)) != FAT_MAGIC) {
|
||||
return (-1);
|
||||
}
|
||||
|
||||
/* Deal with fat file if necessary */
|
||||
if (NXSwapBigLongToHost(*((long *)&buf)) == FAT_MAGIC ||
|
||||
/* nealsid: The following is the big-endian ppc64 check */
|
||||
*((int*)&buf) == FAT_MAGIC) {
|
||||
*((unsigned int *)&buf) == FAT_MAGIC) {
|
||||
struct host_basic_info hbi;
|
||||
struct fat_header fh;
|
||||
struct fat_arch *fat_archs, *fap;
|
||||
|
@ -191,7 +191,7 @@ __breakpad_fdnlist_64(int fd, breakpad_nlist *list, const char **symbolNames) {
|
|||
}
|
||||
|
||||
/* Convert fat_narchs to host byte order */
|
||||
fh.nfat_arch = NXSwapBigLongToHost(fh.nfat_arch);
|
||||
fh.nfat_arch = NXSwapBigIntToHost(fh.nfat_arch);
|
||||
|
||||
/* Read in the fat archs */
|
||||
fat_archs = (struct fat_arch *)malloc(fh.nfat_arch *
|
||||
|
@ -201,7 +201,7 @@ __breakpad_fdnlist_64(int fd, breakpad_nlist *list, const char **symbolNames) {
|
|||
}
|
||||
if (read(fd, (char *)fat_archs,
|
||||
sizeof(struct fat_arch) * fh.nfat_arch) !=
|
||||
sizeof(struct fat_arch) * fh.nfat_arch) {
|
||||
(ssize_t)sizeof(struct fat_arch) * fh.nfat_arch) {
|
||||
free(fat_archs);
|
||||
return (-1);
|
||||
}
|
||||
|
@ -212,15 +212,15 @@ __breakpad_fdnlist_64(int fd, breakpad_nlist *list, const char **symbolNames) {
|
|||
*/
|
||||
for (i = 0; i < fh.nfat_arch; i++) {
|
||||
fat_archs[i].cputype =
|
||||
NXSwapBigLongToHost(fat_archs[i].cputype);
|
||||
NXSwapBigIntToHost(fat_archs[i].cputype);
|
||||
fat_archs[i].cpusubtype =
|
||||
NXSwapBigLongToHost(fat_archs[i].cpusubtype);
|
||||
NXSwapBigIntToHost(fat_archs[i].cpusubtype);
|
||||
fat_archs[i].offset =
|
||||
NXSwapBigLongToHost(fat_archs[i].offset);
|
||||
NXSwapBigIntToHost(fat_archs[i].offset);
|
||||
fat_archs[i].size =
|
||||
NXSwapBigLongToHost(fat_archs[i].size);
|
||||
NXSwapBigIntToHost(fat_archs[i].size);
|
||||
fat_archs[i].align =
|
||||
NXSwapBigLongToHost(fat_archs[i].align);
|
||||
NXSwapBigIntToHost(fat_archs[i].align);
|
||||
}
|
||||
|
||||
fap = NULL;
|
||||
|
@ -257,7 +257,7 @@ __breakpad_fdnlist_64(int fd, breakpad_nlist *list, const char **symbolNames) {
|
|||
}
|
||||
}
|
||||
|
||||
if (*((int *)&buf) == MH_MAGIC_64) {
|
||||
if (*((unsigned int *)&buf) == MH_MAGIC_64) {
|
||||
struct mach_header_64 mh;
|
||||
struct load_command *load_commands, *lcp;
|
||||
struct symtab_command *stp;
|
||||
|
|
|
@ -34,6 +34,7 @@ extern "C" { // needed to compile on Leopard
|
|||
}
|
||||
|
||||
#include "breakpad_nlist_64.h"
|
||||
#include <assert.h>
|
||||
#include <dlfcn.h>
|
||||
#include <mach/mach_vm.h>
|
||||
#include <algorithm>
|
||||
|
@ -129,7 +130,7 @@ static void* ReadTaskString(task_port_t target_task,
|
|||
size_to_end > kMaxStringLength ? kMaxStringLength : size_to_end;
|
||||
|
||||
kern_return_t kr;
|
||||
return ReadTaskMemory(target_task, address, size_to_read, &kr);
|
||||
return ReadTaskMemory(target_task, address, (size_t)size_to_read, &kr);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
|
@ -276,13 +277,11 @@ void DynamicImage::Print() {
|
|||
//==============================================================================
|
||||
// Loads information about dynamically loaded code in the given task.
|
||||
DynamicImages::DynamicImages(mach_port_t task)
|
||||
: task_(task) {
|
||||
: task_(task), image_list_() {
|
||||
ReadImageInfoForTask();
|
||||
}
|
||||
|
||||
void* DynamicImages::GetDyldAllImageInfosPointer()
|
||||
{
|
||||
|
||||
void* DynamicImages::GetDyldAllImageInfosPointer() {
|
||||
const char *imageSymbolName = "_dyld_all_image_infos";
|
||||
const char *dyldPath = "/usr/lib/dyld";
|
||||
#ifndef __LP64__
|
||||
|
@ -298,6 +297,8 @@ void* DynamicImages::GetDyldAllImageInfosPointer()
|
|||
if(list.n_value) {
|
||||
return reinterpret_cast<void*>(list.n_value);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
#else
|
||||
struct nlist_64 l[8];
|
||||
struct nlist_64 &list = l[0];
|
||||
|
@ -311,11 +312,10 @@ void* DynamicImages::GetDyldAllImageInfosPointer()
|
|||
if(invalidEntriesCount != 0) {
|
||||
return NULL;
|
||||
}
|
||||
if (list.n_value) {
|
||||
assert(list.n_value);
|
||||
return reinterpret_cast<void*>(list.n_value);
|
||||
}
|
||||
#endif
|
||||
return NULL;
|
||||
|
||||
}
|
||||
//==============================================================================
|
||||
// This code was written using dyld_debug.c (from Darwin) as a guide.
|
||||
|
@ -363,7 +363,7 @@ void DynamicImages::ReadImageInfoForTask() {
|
|||
// Now determine the total amount we really want to read based on the
|
||||
// size of the load commands. We need the header plus all of the
|
||||
// load commands.
|
||||
unsigned int header_size =
|
||||
size_t header_size =
|
||||
sizeof(breakpad_mach_header) + header->sizeofcmds;
|
||||
|
||||
free(header);
|
||||
|
|
|
@ -103,7 +103,7 @@ class MachHeader {
|
|||
class DynamicImage {
|
||||
public:
|
||||
DynamicImage(breakpad_mach_header *header, // we take ownership
|
||||
int header_size, // includes load commands
|
||||
size_t header_size, // includes load commands
|
||||
breakpad_mach_header *load_address,
|
||||
char *inFilePath,
|
||||
uintptr_t image_mod_date,
|
||||
|
@ -111,6 +111,11 @@ class DynamicImage {
|
|||
: header_(header),
|
||||
header_size_(header_size),
|
||||
load_address_(load_address),
|
||||
vmaddr_(0),
|
||||
vmsize_(0),
|
||||
slide_(0),
|
||||
version_(0),
|
||||
file_path_(NULL),
|
||||
file_mod_date_(image_mod_date),
|
||||
task_(task) {
|
||||
InitializeFilePath(inFilePath);
|
||||
|
@ -128,7 +133,7 @@ class DynamicImage {
|
|||
breakpad_mach_header *GetMachHeader() {return header_;}
|
||||
|
||||
// Size of mach_header plus load commands
|
||||
int GetHeaderSize() const {return header_size_;}
|
||||
size_t GetHeaderSize() const {return header_size_;}
|
||||
|
||||
// Full path to mach-o binary
|
||||
char *GetFilePath() {return file_path_;}
|
||||
|
@ -160,6 +165,9 @@ class DynamicImage {
|
|||
void Print();
|
||||
|
||||
private:
|
||||
DynamicImage(const DynamicImage &);
|
||||
DynamicImage &operator=(const DynamicImage &);
|
||||
|
||||
friend class DynamicImages;
|
||||
|
||||
// Sanity checking
|
||||
|
@ -180,7 +188,7 @@ class DynamicImage {
|
|||
void CalculateMemoryAndVersionInfo();
|
||||
|
||||
breakpad_mach_header *header_; // our local copy of the header
|
||||
int header_size_; // mach_header plus load commands
|
||||
size_t header_size_; // mach_header plus load commands
|
||||
breakpad_mach_header *load_address_; // base address image is mapped into
|
||||
mach_vm_address_t vmaddr_;
|
||||
mach_vm_size_t vmsize_;
|
||||
|
@ -231,13 +239,13 @@ class DynamicImages {
|
|||
explicit DynamicImages(mach_port_t task);
|
||||
|
||||
~DynamicImages() {
|
||||
for (int i = 0; i < (int)image_list_.size(); ++i) {
|
||||
for (int i = 0; i < GetImageCount(); ++i) {
|
||||
delete image_list_[i];
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the number of dynamically loaded mach-o images.
|
||||
int GetImageCount() const {return image_list_.size();}
|
||||
int GetImageCount() const {return static_cast<int>(image_list_.size());}
|
||||
|
||||
// Returns an individual image.
|
||||
DynamicImage *GetImage(int i) {
|
||||
|
@ -256,14 +264,14 @@ class DynamicImages {
|
|||
|
||||
// Debugging
|
||||
void Print() {
|
||||
for (int i = 0; i < (int)image_list_.size(); ++i) {
|
||||
for (int i = 0; i < GetImageCount(); ++i) {
|
||||
image_list_[i]->Print();
|
||||
}
|
||||
}
|
||||
|
||||
void TestPrint() {
|
||||
const breakpad_mach_header *header;
|
||||
for (int i = 0; i < (int)image_list_.size(); ++i) {
|
||||
for (int i = 0; i < GetImageCount(); ++i) {
|
||||
printf("dyld: %p: name = %s\n", _dyld_get_image_header(i),
|
||||
_dyld_get_image_name(i) );
|
||||
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
#include "client/mac/handler/exception_handler.h"
|
||||
#include "client/mac/handler/minidump_generator.h"
|
||||
#include "common/mac/macho_utilities.h"
|
||||
#include "common/mac/scoped_task_suspend-inl.h"
|
||||
|
||||
#ifndef USE_PROTECTED_ALLOCATIONS
|
||||
#define USE_PROTECTED_ALLOCATIONS 0
|
||||
|
@ -221,7 +222,8 @@ ExceptionHandler::ExceptionHandler(const string &dump_path,
|
|||
FilterCallback filter,
|
||||
MinidumpCallback callback,
|
||||
void *callback_context,
|
||||
bool install_handler)
|
||||
bool install_handler,
|
||||
const char *port_name)
|
||||
: dump_path_(),
|
||||
filter_(filter),
|
||||
callback_(callback),
|
||||
|
@ -237,6 +239,8 @@ ExceptionHandler::ExceptionHandler(const string &dump_path,
|
|||
// This will update to the ID and C-string pointers
|
||||
set_dump_path(dump_path);
|
||||
MinidumpGenerator::GatherSystemInformation();
|
||||
if (port_name)
|
||||
crash_generation_client_.reset(new CrashGenerationClient(port_name));
|
||||
Setup(install_handler);
|
||||
}
|
||||
|
||||
|
@ -293,10 +297,42 @@ bool ExceptionHandler::WriteMinidump() {
|
|||
bool ExceptionHandler::WriteMinidump(const string &dump_path,
|
||||
MinidumpCallback callback,
|
||||
void *callback_context) {
|
||||
ExceptionHandler handler(dump_path, NULL, callback, callback_context, false);
|
||||
ExceptionHandler handler(dump_path, NULL, callback, callback_context, false,
|
||||
NULL);
|
||||
return handler.WriteMinidump();
|
||||
}
|
||||
|
||||
// static
|
||||
bool ExceptionHandler::WriteMinidumpForChild(mach_port_t child,
|
||||
mach_port_t child_blamed_thread,
|
||||
const string &dump_path,
|
||||
MinidumpCallback callback,
|
||||
void *callback_context) {
|
||||
ScopedTaskSuspend suspend(child);
|
||||
|
||||
MinidumpGenerator generator(child, MACH_PORT_NULL);
|
||||
string dump_id;
|
||||
string dump_filename = generator.UniqueNameInDirectory(dump_path, &dump_id);
|
||||
|
||||
generator.SetExceptionInformation(EXC_BREAKPOINT,
|
||||
#if defined (__i386__) || defined(__x86_64__)
|
||||
EXC_I386_BPT,
|
||||
#elif defined (__ppc__) || defined (__ppc64__)
|
||||
EXC_PPC_BREAKPOINT,
|
||||
#else
|
||||
#error architecture not supported
|
||||
#endif
|
||||
0,
|
||||
child_blamed_thread);
|
||||
bool result = generator.Write(dump_filename.c_str());
|
||||
|
||||
if (callback) {
|
||||
return callback(dump_path.c_str(), dump_id.c_str(),
|
||||
callback_context, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool ExceptionHandler::WriteMinidumpWithException(int exception_type,
|
||||
int exception_code,
|
||||
int exception_subcode,
|
||||
|
@ -312,6 +348,18 @@ bool ExceptionHandler::WriteMinidumpWithException(int exception_type,
|
|||
if (exception_type && exception_code)
|
||||
_exit(exception_type);
|
||||
}
|
||||
} else if (IsOutOfProcess()) {
|
||||
if (exception_type && exception_code) {
|
||||
// If this is a real exception, give the filter (if any) a chance to
|
||||
// decide if this should be sent.
|
||||
if (filter_ && !filter_(callback_context_))
|
||||
return false;
|
||||
return crash_generation_client_->RequestDumpForException(
|
||||
exception_type,
|
||||
exception_code,
|
||||
exception_subcode,
|
||||
thread_name);
|
||||
}
|
||||
} else {
|
||||
string minidump_id;
|
||||
|
||||
|
@ -321,7 +369,7 @@ bool ExceptionHandler::WriteMinidumpWithException(int exception_type,
|
|||
MinidumpGenerator md;
|
||||
if (exception_type && exception_code) {
|
||||
// If this is a real exception, give the filter (if any) a chance to
|
||||
// decided if this should be sent
|
||||
// decide if this should be sent.
|
||||
if (filter_ && !filter_(callback_context_))
|
||||
return false;
|
||||
|
||||
|
@ -453,10 +501,11 @@ void *ExceptionHandler::WaitForMessage(void *exception_handler_class) {
|
|||
// Wait for the exception info
|
||||
while (1) {
|
||||
receive.header.msgh_local_port = self->handler_port_;
|
||||
receive.header.msgh_size = sizeof(receive);
|
||||
receive.header.msgh_size = static_cast<mach_msg_size_t>(sizeof(receive));
|
||||
kern_return_t result = mach_msg(&(receive.header),
|
||||
MACH_RCV_MSG | MACH_RCV_LARGE, 0,
|
||||
sizeof(receive), self->handler_port_,
|
||||
receive.header.msgh_size,
|
||||
self->handler_port_,
|
||||
MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
|
||||
|
||||
|
||||
|
|
|
@ -40,6 +40,9 @@
|
|||
|
||||
#include <string>
|
||||
|
||||
#include "client/mac/crash_generation/crash_generation_client.h"
|
||||
#include "processor/scoped_ptr.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
using std::string;
|
||||
|
@ -86,9 +89,12 @@ class ExceptionHandler {
|
|||
// If install_handler is true, then a minidump will be written whenever
|
||||
// an unhandled exception occurs. If it is false, minidumps will only
|
||||
// be written when WriteMinidump is called.
|
||||
// If port_name is non-NULL, attempt to perform out-of-process dump generation
|
||||
// If port_name is NULL, in-process dump generation will be used.
|
||||
ExceptionHandler(const string &dump_path,
|
||||
FilterCallback filter, MinidumpCallback callback,
|
||||
void *callback_context, bool install_handler);
|
||||
void *callback_context, bool install_handler,
|
||||
const char *port_name);
|
||||
|
||||
// A special constructor if we want to bypass minidump writing and
|
||||
// simply get a callback with the exception information.
|
||||
|
@ -115,6 +121,19 @@ class ExceptionHandler {
|
|||
static bool WriteMinidump(const string &dump_path, MinidumpCallback callback,
|
||||
void *callback_context);
|
||||
|
||||
// Write a minidump of child immediately. This can be used to capture
|
||||
// the execution state of a child process independently of a crash.
|
||||
static bool WriteMinidumpForChild(mach_port_t child,
|
||||
mach_port_t child_blamed_thread,
|
||||
const std::string &dump_path,
|
||||
MinidumpCallback callback,
|
||||
void *callback_context);
|
||||
|
||||
// Returns whether out-of-process dump generation is used or not.
|
||||
bool IsOutOfProcess() const {
|
||||
return crash_generation_client_.get() != NULL;
|
||||
}
|
||||
|
||||
private:
|
||||
// Install the mach exception handler
|
||||
bool InstallHandler();
|
||||
|
@ -206,6 +225,9 @@ class ExceptionHandler {
|
|||
|
||||
// True, if we're using the mutext to indicate when mindump writing occurs
|
||||
bool use_minidump_write_mutex_;
|
||||
|
||||
// Client for out-of-process dump generation.
|
||||
scoped_ptr<CrashGenerationClient> crash_generation_client_;
|
||||
};
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
|
|
@ -1,99 +0,0 @@
|
|||
// Copyright (c) 2006, 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.
|
||||
|
||||
/*
|
||||
g++ -framework CoreFoundation -I../../.. ../../minidump_file_writer.cc ../../../common/convert_UTF.c ../../../common/string_conversion.cc ../../../common/mac/string_utilities.cc exception_handler.cc minidump_generator.cc exception_handler_test.cc -o exception_handler_test -mmacosx-version-min=10.4 ../../../common/mac/file_id.cc dynamic_images.cc ../../../common/mac/macho_id.cc ../../../common/mac/macho_walker.cc -lcrypto ../../../common/mac/macho_utilities.cc
|
||||
*/
|
||||
|
||||
#include <pthread.h>
|
||||
#include <pwd.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
|
||||
#include "exception_handler.h"
|
||||
#include "minidump_generator.h"
|
||||
|
||||
using std::string;
|
||||
using google_breakpad::ExceptionHandler;
|
||||
|
||||
static void *SleepyFunction(void *) {
|
||||
while (1) {
|
||||
sleep(10000);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void Crasher() {
|
||||
int *a = (int*)0x42;
|
||||
|
||||
fprintf(stdout, "Going to crash...\n");
|
||||
fprintf(stdout, "A = %d", *a);
|
||||
}
|
||||
|
||||
static void SoonToCrash() {
|
||||
Crasher();
|
||||
}
|
||||
|
||||
bool MDCallback(const char *dump_dir, const char *file_name,
|
||||
void *context, bool success) {
|
||||
string path(dump_dir);
|
||||
string dest(dump_dir);
|
||||
path.append(file_name);
|
||||
path.append(".dmp");
|
||||
|
||||
fprintf(stdout, "Minidump: %s\n", path.c_str());
|
||||
// Indicate that we've handled the callback
|
||||
exit(0);
|
||||
}
|
||||
|
||||
int main(int argc, char * const argv[]) {
|
||||
char buffer[PATH_MAX];
|
||||
|
||||
// Home dir
|
||||
snprintf(buffer, sizeof(buffer), "/tmp/");
|
||||
|
||||
string path(buffer);
|
||||
ExceptionHandler eh(path, NULL, MDCallback, NULL, true);
|
||||
pthread_t t;
|
||||
|
||||
if (pthread_create(&t, NULL, SleepyFunction, NULL) == 0) {
|
||||
pthread_detach(t);
|
||||
} else {
|
||||
perror("pthread_create");
|
||||
}
|
||||
|
||||
// // Dump a test
|
||||
// eh.WriteMinidump();
|
||||
|
||||
// Test the handler
|
||||
SoonToCrash();
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -57,7 +57,8 @@ namespace google_breakpad {
|
|||
|
||||
// constructor when generating from within the crashed process
|
||||
MinidumpGenerator::MinidumpGenerator()
|
||||
: exception_type_(0),
|
||||
: writer_(),
|
||||
exception_type_(0),
|
||||
exception_code_(0),
|
||||
exception_subcode_(0),
|
||||
exception_thread_(0),
|
||||
|
@ -71,12 +72,14 @@ MinidumpGenerator::MinidumpGenerator()
|
|||
// crashed process
|
||||
MinidumpGenerator::MinidumpGenerator(mach_port_t crashing_task,
|
||||
mach_port_t handler_thread)
|
||||
: exception_type_(0),
|
||||
: writer_(),
|
||||
exception_type_(0),
|
||||
exception_code_(0),
|
||||
exception_subcode_(0),
|
||||
exception_thread_(0),
|
||||
crashing_task_(crashing_task),
|
||||
handler_thread_(handler_thread) {
|
||||
handler_thread_(handler_thread),
|
||||
dynamic_images_(NULL) {
|
||||
if (crashing_task != mach_task_self()) {
|
||||
dynamic_images_ = new DynamicImages(crashing_task_);
|
||||
} else {
|
||||
|
@ -191,7 +194,7 @@ bool MinidumpGenerator::Write(const char *path) {
|
|||
if (!header.Allocate())
|
||||
return false;
|
||||
|
||||
int writer_count = sizeof(writers) / sizeof(writers[0]);
|
||||
int writer_count = static_cast<int>(sizeof(writers) / sizeof(writers[0]));
|
||||
|
||||
// If we don't have exception information, don't write out the
|
||||
// exception stream
|
||||
|
@ -488,7 +491,7 @@ bool MinidumpGenerator::WriteContext(breakpad_thread_state_data_t state,
|
|||
// not used in the flags register. Since the minidump format
|
||||
// specifies 32 bits for the flags register, we can truncate safely
|
||||
// with no loss.
|
||||
context_ptr->eflags = machine_state->__rflags;
|
||||
context_ptr->eflags = static_cast<u_int32_t>(machine_state->__rflags);
|
||||
AddReg(cs);
|
||||
AddReg(fs);
|
||||
AddReg(gs);
|
||||
|
@ -502,7 +505,8 @@ bool MinidumpGenerator::WriteContext(breakpad_thread_state_data_t state,
|
|||
bool MinidumpGenerator::WriteThreadStream(mach_port_t thread_id,
|
||||
MDRawThread *thread) {
|
||||
breakpad_thread_state_data_t state;
|
||||
mach_msg_type_number_t state_count = sizeof(state);
|
||||
mach_msg_type_number_t state_count
|
||||
= static_cast<mach_msg_type_number_t>(sizeof(state));
|
||||
|
||||
if (thread_get_state(thread_id, BREAKPAD_MACHINE_THREAD_STATE,
|
||||
state, &state_count) ==
|
||||
|
@ -532,7 +536,10 @@ bool MinidumpGenerator::WriteThreadListStream(
|
|||
return false;
|
||||
|
||||
// Don't include the generator thread
|
||||
if (handler_thread_ != MACH_PORT_NULL)
|
||||
non_generator_thread_count = thread_count - 1;
|
||||
else
|
||||
non_generator_thread_count = thread_count;
|
||||
if (!list.AllocateObjectAndArray(non_generator_thread_count,
|
||||
sizeof(MDRawThread)))
|
||||
return false;
|
||||
|
@ -577,7 +584,8 @@ MinidumpGenerator::WriteExceptionStream(MDRawDirectory *exception_stream) {
|
|||
exception_ptr->exception_record.exception_flags = exception_code_;
|
||||
|
||||
breakpad_thread_state_data_t state;
|
||||
mach_msg_type_number_t stateCount = sizeof(state);
|
||||
mach_msg_type_number_t stateCount
|
||||
= static_cast<mach_msg_type_number_t>(sizeof(state));
|
||||
|
||||
if (thread_get_state(exception_thread_,
|
||||
BREAKPAD_MACHINE_THREAD_STATE,
|
||||
|
@ -727,7 +735,7 @@ bool MinidumpGenerator::WriteModuleStream(unsigned int index,
|
|||
return false;
|
||||
|
||||
module->base_of_image = image->GetVMAddr() + image->GetVMAddrSlide();
|
||||
module->size_of_image = image->GetVMSize();
|
||||
module->size_of_image = static_cast<u_int32_t>(image->GetVMSize());
|
||||
module->module_name_rva = string_location.rva;
|
||||
|
||||
// We'll skip the executable module, because they don't have
|
||||
|
@ -794,7 +802,7 @@ bool MinidumpGenerator::WriteModuleStream(unsigned int index,
|
|||
return false;
|
||||
|
||||
module->base_of_image = seg->vmaddr + slide;
|
||||
module->size_of_image = seg->vmsize;
|
||||
module->size_of_image = static_cast<u_int32_t>(seg->vmsize);
|
||||
module->module_name_rva = string_location.rva;
|
||||
|
||||
if (!WriteCVRecord(module, cpu_type, name))
|
||||
|
@ -931,7 +939,7 @@ bool MinidumpGenerator::WriteMiscInfoStream(MDRawDirectory *misc_info_stream) {
|
|||
misc_info_stream->location = info.location();
|
||||
|
||||
MDRawMiscInfo *info_ptr = info.get();
|
||||
info_ptr->size_of_info = sizeof(MDRawMiscInfo);
|
||||
info_ptr->size_of_info = static_cast<u_int32_t>(sizeof(MDRawMiscInfo));
|
||||
info_ptr->flags1 = MD_MISCINFO_FLAGS1_PROCESS_ID |
|
||||
MD_MISCINFO_FLAGS1_PROCESS_TIMES |
|
||||
MD_MISCINFO_FLAGS1_PROCESSOR_POWER_INFO;
|
||||
|
@ -943,33 +951,38 @@ bool MinidumpGenerator::WriteMiscInfoStream(MDRawDirectory *misc_info_stream) {
|
|||
struct rusage usage;
|
||||
if (getrusage(RUSAGE_SELF, &usage) != -1) {
|
||||
// Omit the fractional time since the MDRawMiscInfo only wants seconds
|
||||
info_ptr->process_user_time = usage.ru_utime.tv_sec;
|
||||
info_ptr->process_kernel_time = usage.ru_stime.tv_sec;
|
||||
info_ptr->process_user_time =
|
||||
static_cast<u_int32_t>(usage.ru_utime.tv_sec);
|
||||
info_ptr->process_kernel_time =
|
||||
static_cast<u_int32_t>(usage.ru_stime.tv_sec);
|
||||
}
|
||||
int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, info_ptr->process_id };
|
||||
u_int mibsize = static_cast<u_int>(sizeof(mib) / sizeof(mib[0]));
|
||||
size_t size;
|
||||
if (!sysctl(mib, sizeof(mib) / sizeof(mib[0]), NULL, &size, NULL, 0)) {
|
||||
if (!sysctl(mib, mibsize, NULL, &size, NULL, 0)) {
|
||||
mach_vm_address_t addr;
|
||||
if (mach_vm_allocate(mach_task_self(),
|
||||
&addr,
|
||||
size,
|
||||
true) == KERN_SUCCESS) {
|
||||
struct kinfo_proc *proc = (struct kinfo_proc *)addr;
|
||||
if (!sysctl(mib, sizeof(mib) / sizeof(mib[0]), proc, &size, NULL, 0))
|
||||
info_ptr->process_create_time = proc->kp_proc.p_starttime.tv_sec;
|
||||
if (!sysctl(mib, mibsize, proc, &size, NULL, 0))
|
||||
info_ptr->process_create_time =
|
||||
static_cast<u_int32_t>(proc->kp_proc.p_starttime.tv_sec);
|
||||
mach_vm_deallocate(mach_task_self(), addr, size);
|
||||
}
|
||||
}
|
||||
|
||||
// Speed
|
||||
uint64_t speed;
|
||||
const uint64_t kOneMillion = 1000 * 1000;
|
||||
size = sizeof(speed);
|
||||
sysctlbyname("hw.cpufrequency_max", &speed, &size, NULL, 0);
|
||||
info_ptr->processor_max_mhz = speed / (1000 * 1000);
|
||||
info_ptr->processor_mhz_limit = speed / (1000 * 1000);
|
||||
info_ptr->processor_max_mhz = static_cast<u_int32_t>(speed / kOneMillion);
|
||||
info_ptr->processor_mhz_limit = static_cast<u_int32_t>(speed / kOneMillion);
|
||||
size = sizeof(speed);
|
||||
sysctlbyname("hw.cpufrequency", &speed, &size, NULL, 0);
|
||||
info_ptr->processor_current_mhz = speed / (1000 * 1000);
|
||||
info_ptr->processor_current_mhz = static_cast<u_int32_t>(speed / kOneMillion);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -3,10 +3,24 @@
|
|||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 42;
|
||||
objectVersion = 46;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
8BFC813F11FF9A58002CB4DC /* libcrypto.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 8BFC813E11FF9A58002CB4DC /* libcrypto.dylib */; };
|
||||
8BFC814411FF9A9C002CB4DC /* libcrypto.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 8BFC813E11FF9A58002CB4DC /* libcrypto.dylib */; };
|
||||
8BFC814511FF9A9D002CB4DC /* libcrypto.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 8BFC813E11FF9A58002CB4DC /* libcrypto.dylib */; };
|
||||
8BFC814811FF9B13002CB4DC /* libcrypto.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 8BFC813E11FF9A58002CB4DC /* libcrypto.dylib */; };
|
||||
8BFC814911FF9B13002CB4DC /* libcrypto.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 8BFC813E11FF9A58002CB4DC /* libcrypto.dylib */; };
|
||||
8BFC814A11FF9B13002CB4DC /* libcrypto.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 8BFC813E11FF9A58002CB4DC /* libcrypto.dylib */; };
|
||||
8BFC814B11FF9B3F002CB4DC /* SenTestingKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F9721FA10E8B0E2300D7E813 /* SenTestingKit.framework */; };
|
||||
8BFC814C11FF9B3F002CB4DC /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F9721F6B0E8B0D7000D7E813 /* Cocoa.framework */; };
|
||||
8BFC81A211FF9C2E002CB4DC /* CPlusTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8BFC819211FF9C23002CB4DC /* CPlusTest.framework */; };
|
||||
8BFC81A311FF9C2F002CB4DC /* CPlusTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8BFC819211FF9C23002CB4DC /* CPlusTest.framework */; };
|
||||
8BFC81AD11FF9C8A002CB4DC /* breakpad_nlist_64.cc in Sources */ = {isa = PBXBuildFile; fileRef = F98208A10DB32CAE0017AECA /* breakpad_nlist_64.cc */; };
|
||||
8BFC81AE11FF9C8C002CB4DC /* breakpad_nlist_64.cc in Sources */ = {isa = PBXBuildFile; fileRef = F98208A10DB32CAE0017AECA /* breakpad_nlist_64.cc */; };
|
||||
8BFC81AF11FF9C8C002CB4DC /* breakpad_nlist_64.cc in Sources */ = {isa = PBXBuildFile; fileRef = F98208A10DB32CAE0017AECA /* breakpad_nlist_64.cc */; };
|
||||
8BFC81B011FF9C8D002CB4DC /* breakpad_nlist_64.cc in Sources */ = {isa = PBXBuildFile; fileRef = F98208A10DB32CAE0017AECA /* breakpad_nlist_64.cc */; };
|
||||
9B35FF5A0B267D5F008DE8C7 /* convert_UTF.c in Sources */ = {isa = PBXBuildFile; fileRef = 9B35FF560B267D5F008DE8C7 /* convert_UTF.c */; };
|
||||
9B35FF5B0B267D5F008DE8C7 /* string_conversion.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9B35FF580B267D5F008DE8C7 /* string_conversion.cc */; };
|
||||
9B37CEEC0AF98ECD00FA4BD4 /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9B37CEEB0AF98ECD00FA4BD4 /* CoreFoundation.framework */; };
|
||||
|
@ -94,12 +108,18 @@
|
|||
/* End PBXCopyFilesBuildPhase section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
8BFC812011FF99D5002CB4DC /* Breakpad.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Breakpad.xcconfig; path = ../../../common/mac/Breakpad.xcconfig; sourceTree = SOURCE_ROOT; };
|
||||
8BFC812111FF99D5002CB4DC /* BreakpadDebug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = BreakpadDebug.xcconfig; path = ../../../common/mac/BreakpadDebug.xcconfig; sourceTree = SOURCE_ROOT; };
|
||||
8BFC812211FF99D5002CB4DC /* BreakpadRelease.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = BreakpadRelease.xcconfig; path = ../../../common/mac/BreakpadRelease.xcconfig; sourceTree = SOURCE_ROOT; };
|
||||
8BFC813E11FF9A58002CB4DC /* libcrypto.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libcrypto.dylib; path = usr/lib/libcrypto.dylib; sourceTree = SDKROOT; };
|
||||
8BFC815411FF9B7F002CB4DC /* Carbon.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Carbon.framework; path = System/Library/Frameworks/Carbon.framework; sourceTree = SDKROOT; };
|
||||
8BFC819211FF9C23002CB4DC /* CPlusTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CPlusTest.framework; path = Library/Frameworks/CPlusTest.framework; sourceTree = DEVELOPER_DIR; };
|
||||
8DD76F6C0486A84900D96B5E /* generator_test */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = generator_test; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
9B35FF560B267D5F008DE8C7 /* convert_UTF.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = convert_UTF.c; path = ../../../common/convert_UTF.c; sourceTree = SOURCE_ROOT; };
|
||||
9B35FF570B267D5F008DE8C7 /* convert_UTF.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = convert_UTF.h; path = ../../../common/convert_UTF.h; sourceTree = SOURCE_ROOT; };
|
||||
9B35FF580B267D5F008DE8C7 /* string_conversion.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = string_conversion.cc; path = ../../../common/string_conversion.cc; sourceTree = SOURCE_ROOT; };
|
||||
9B35FF590B267D5F008DE8C7 /* string_conversion.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = string_conversion.h; path = ../../../common/string_conversion.h; sourceTree = SOURCE_ROOT; };
|
||||
9B37CEEB0AF98ECD00FA4BD4 /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = /System/Library/Frameworks/CoreFoundation.framework; sourceTree = "<absolute>"; };
|
||||
9B37CEEB0AF98ECD00FA4BD4 /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; };
|
||||
9B7CA84E0B1297F200CD3A1D /* unit_test */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = unit_test; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
9B7CA8530B12989000CD3A1D /* minidump_file_writer_unittest.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = minidump_file_writer_unittest.cc; path = ../../minidump_file_writer_unittest.cc; sourceTree = "<group>"; };
|
||||
9BD82A9B0B00267E0055103E /* handler_test */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = handler_test; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
|
@ -132,11 +152,11 @@
|
|||
F9721F310E8B07E800D7E813 /* dwarftests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = dwarftests.mm; sourceTree = "<group>"; };
|
||||
F9721F380E8B0CFC00D7E813 /* dump_syms.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = dump_syms.h; path = ../../../common/mac/dump_syms.h; sourceTree = SOURCE_ROOT; };
|
||||
F9721F390E8B0D0D00D7E813 /* dump_syms.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = dump_syms.mm; path = ../../../common/mac/dump_syms.mm; sourceTree = SOURCE_ROOT; };
|
||||
F9721F6B0E8B0D7000D7E813 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = "<absolute>"; };
|
||||
F9721F6B0E8B0D7000D7E813 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; };
|
||||
F9721F760E8B0DC700D7E813 /* bytereader.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = bytereader.cc; path = ../../../common/dwarf/bytereader.cc; sourceTree = SOURCE_ROOT; };
|
||||
F9721F770E8B0DC700D7E813 /* dwarf2reader.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = dwarf2reader.cc; path = ../../../common/dwarf/dwarf2reader.cc; sourceTree = SOURCE_ROOT; };
|
||||
F9721F780E8B0DC700D7E813 /* functioninfo.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = functioninfo.cc; path = ../../../common/dwarf/functioninfo.cc; sourceTree = SOURCE_ROOT; };
|
||||
F9721FA10E8B0E2300D7E813 /* SenTestingKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SenTestingKit.framework; path = /System/Library/Frameworks/SenTestingKit.framework; sourceTree = "<absolute>"; };
|
||||
F9721FA10E8B0E2300D7E813 /* SenTestingKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SenTestingKit.framework; path = Library/Frameworks/SenTestingKit.framework; sourceTree = DEVELOPER_DIR; };
|
||||
F9721FA80E8B0E4800D7E813 /* md5.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = md5.c; path = ../../../common/md5.c; sourceTree = SOURCE_ROOT; };
|
||||
F982089A0DB3280D0017AECA /* breakpad_nlist_test.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = breakpad_nlist_test.h; sourceTree = "<group>"; };
|
||||
F982089B0DB3280D0017AECA /* breakpad_nlist_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = breakpad_nlist_test.cc; sourceTree = "<group>"; };
|
||||
|
@ -156,6 +176,7 @@
|
|||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
9B37CEEC0AF98ECD00FA4BD4 /* CoreFoundation.framework in Frameworks */,
|
||||
8BFC813F11FF9A58002CB4DC /* libcrypto.dylib in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
@ -163,6 +184,7 @@
|
|||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
8BFC814511FF9A9D002CB4DC /* libcrypto.dylib in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
@ -171,6 +193,7 @@
|
|||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
9BD82AC10B0029DF0055103E /* CoreFoundation.framework in Frameworks */,
|
||||
8BFC814411FF9A9C002CB4DC /* libcrypto.dylib in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
@ -178,6 +201,9 @@
|
|||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
8BFC814A11FF9B13002CB4DC /* libcrypto.dylib in Frameworks */,
|
||||
8BFC814B11FF9B3F002CB4DC /* SenTestingKit.framework in Frameworks */,
|
||||
8BFC814C11FF9B3F002CB4DC /* Cocoa.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
@ -185,6 +211,8 @@
|
|||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
8BFC814811FF9B13002CB4DC /* libcrypto.dylib in Frameworks */,
|
||||
8BFC81A211FF9C2E002CB4DC /* CPlusTest.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
@ -194,6 +222,8 @@
|
|||
files = (
|
||||
F9721F6C0E8B0D7000D7E813 /* Cocoa.framework in Frameworks */,
|
||||
F9721FA20E8B0E2300D7E813 /* SenTestingKit.framework in Frameworks */,
|
||||
8BFC814911FF9B13002CB4DC /* libcrypto.dylib in Frameworks */,
|
||||
8BFC81A311FF9C2F002CB4DC /* CPlusTest.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
@ -203,6 +233,9 @@
|
|||
08FB7794FE84155DC02AAC07 /* MinidumpWriter */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
8BFC812011FF99D5002CB4DC /* Breakpad.xcconfig */,
|
||||
8BFC812111FF99D5002CB4DC /* BreakpadDebug.xcconfig */,
|
||||
8BFC812211FF99D5002CB4DC /* BreakpadRelease.xcconfig */,
|
||||
F9721FA80E8B0E4800D7E813 /* md5.c */,
|
||||
F9721F760E8B0DC700D7E813 /* bytereader.cc */,
|
||||
F9721F770E8B0DC700D7E813 /* dwarf2reader.cc */,
|
||||
|
@ -261,9 +294,12 @@
|
|||
9B37CEEA0AF98EB600FA4BD4 /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
8BFC813E11FF9A58002CB4DC /* libcrypto.dylib */,
|
||||
8BFC815411FF9B7F002CB4DC /* Carbon.framework */,
|
||||
F9721FA10E8B0E2300D7E813 /* SenTestingKit.framework */,
|
||||
F9721F6B0E8B0D7000D7E813 /* Cocoa.framework */,
|
||||
9B37CEEB0AF98ECD00FA4BD4 /* CoreFoundation.framework */,
|
||||
8BFC819211FF9C23002CB4DC /* CPlusTest.framework */,
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
|
@ -414,7 +450,7 @@
|
|||
08FB7793FE84155DC02AAC07 /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
buildConfigurationList = 1DEB923508733DC60010E9CD /* Build configuration list for PBXProject "minidump_test" */;
|
||||
compatibilityVersion = "Xcode 2.4";
|
||||
compatibilityVersion = "Xcode 3.2";
|
||||
hasScannedForEncodings = 1;
|
||||
mainGroup = 08FB7794FE84155DC02AAC07 /* MinidumpWriter */;
|
||||
projectDirPath = "";
|
||||
|
@ -513,6 +549,7 @@
|
|||
D2F6510E0BEF94EB00920385 /* macho_walker.cc in Sources */,
|
||||
D2F651110BEF951700920385 /* string_conversion.cc in Sources */,
|
||||
D2F651150BEF953000920385 /* convert_UTF.c in Sources */,
|
||||
8BFC81B011FF9C8D002CB4DC /* breakpad_nlist_64.cc in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
@ -524,6 +561,7 @@
|
|||
9B7CA8550B1298A100CD3A1D /* minidump_file_writer.cc in Sources */,
|
||||
9BC1D2940B336F2300F2A2B4 /* convert_UTF.c in Sources */,
|
||||
9BC1D2950B336F2500F2A2B4 /* string_conversion.cc in Sources */,
|
||||
8BFC81AE11FF9C8C002CB4DC /* breakpad_nlist_64.cc in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
@ -543,6 +581,7 @@
|
|||
D2F6511E0BEF973600920385 /* macho_id.cc in Sources */,
|
||||
D2F6511F0BEF973900920385 /* macho_utilities.cc in Sources */,
|
||||
D2F651210BEF975400920385 /* macho_walker.cc in Sources */,
|
||||
8BFC81AF11FF9C8C002CB4DC /* breakpad_nlist_64.cc in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
@ -580,6 +619,7 @@
|
|||
files = (
|
||||
F9AE5B390DBFDBDB00505983 /* dynamic_images.cc in Sources */,
|
||||
F9AE5B3A0DBFDBDB00505983 /* DynamicImagesTests.cc in Sources */,
|
||||
8BFC81AD11FF9C8A002CB4DC /* breakpad_nlist_64.cc in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
@ -589,154 +629,78 @@
|
|||
1DEB923208733DC60010E9CD /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
COPY_PHASE_STRIP = NO;
|
||||
GCC_CW_ASM_SYNTAX = NO;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_ENABLE_FIX_AND_CONTINUE = YES;
|
||||
GCC_ENABLE_PASCAL_STRINGS = NO;
|
||||
GCC_MODEL_TUNING = G5;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_THREADSAFE_STATICS = NO;
|
||||
INSTALL_PATH = "$(HOME)/bin";
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"\"$(DEVELOPER_FRAMEWORKS_DIR)\"",
|
||||
);
|
||||
PRODUCT_NAME = generator_test;
|
||||
USER_HEADER_SEARCH_PATHS = "../../../** $(inherited)";
|
||||
ZERO_LINK = NO;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
1DEB923308733DC60010E9CD /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ARCHS = (
|
||||
ppc,
|
||||
i386,
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"\"$(DEVELOPER_FRAMEWORKS_DIR)\"",
|
||||
);
|
||||
GCC_CW_ASM_SYNTAX = NO;
|
||||
GCC_ENABLE_PASCAL_STRINGS = NO;
|
||||
GCC_GENERATE_DEBUGGING_SYMBOLS = NO;
|
||||
GCC_MODEL_TUNING = G5;
|
||||
GCC_THREADSAFE_STATICS = NO;
|
||||
INSTALL_PATH = "$(HOME)/bin";
|
||||
PRODUCT_NAME = generator_test;
|
||||
USER_HEADER_SEARCH_PATHS = "../../../** $(inherited)";
|
||||
ZERO_LINK = NO;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
1DEB923608733DC60010E9CD /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 8BFC812111FF99D5002CB4DC /* BreakpadDebug.xcconfig */;
|
||||
buildSettings = {
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
OTHER_LDFLAGS = "-lcrypto";
|
||||
PREBINDING = NO;
|
||||
SDKROOT = /Developer/SDKs/MacOSX10.4u.sdk;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
1DEB923708733DC60010E9CD /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 8BFC812211FF99D5002CB4DC /* BreakpadRelease.xcconfig */;
|
||||
buildSettings = {
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
OTHER_LDFLAGS = "-lcrypto";
|
||||
PREBINDING = NO;
|
||||
SDKROOT = /Developer/SDKs/MacOSX10.4u.sdk;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
9B7CA8510B12984300CD3A1D /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
COPY_PHASE_STRIP = NO;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_ENABLE_FIX_AND_CONTINUE = YES;
|
||||
GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
|
||||
GCC_MODEL_TUNING = G5;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
INSTALL_PATH = "$(HOME)/bin";
|
||||
PREBINDING = NO;
|
||||
PRODUCT_NAME = unit_test;
|
||||
USER_HEADER_SEARCH_PATHS = "../../../** $(inherited)";
|
||||
ZERO_LINK = NO;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
9B7CA8520B12984300CD3A1D /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
COPY_PHASE_STRIP = YES;
|
||||
GCC_ENABLE_FIX_AND_CONTINUE = NO;
|
||||
GCC_GENERATE_DEBUGGING_SYMBOLS = NO;
|
||||
GCC_MODEL_TUNING = G5;
|
||||
INSTALL_PATH = "$(HOME)/bin";
|
||||
PREBINDING = NO;
|
||||
PRODUCT_NAME = unit_test;
|
||||
USER_HEADER_SEARCH_PATHS = "../../../** $(inherited)";
|
||||
ZERO_LINK = NO;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
9BD82AA70B0026BF0055103E /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ARCHS = "$(NATIVE_ARCH)";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_ENABLE_FIX_AND_CONTINUE = YES;
|
||||
GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
|
||||
GCC_MODEL_TUNING = G5;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
|
||||
INSTALL_PATH = "$(HOME)/bin";
|
||||
OTHER_CFLAGS = "-Wall";
|
||||
PREBINDING = NO;
|
||||
PRODUCT_NAME = handler_test;
|
||||
USER_HEADER_SEARCH_PATHS = "../../.. $(inherited)";
|
||||
ZERO_LINK = NO;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
9BD82AA80B0026BF0055103E /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ARCHS = "$(NATIVE_ARCH)";
|
||||
COPY_PHASE_STRIP = YES;
|
||||
GCC_ENABLE_FIX_AND_CONTINUE = NO;
|
||||
GCC_GENERATE_DEBUGGING_SYMBOLS = NO;
|
||||
GCC_MODEL_TUNING = G5;
|
||||
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
|
||||
INSTALL_PATH = "$(HOME)/bin";
|
||||
OTHER_CFLAGS = "-Wall";
|
||||
PREBINDING = NO;
|
||||
PRODUCT_NAME = handler_test;
|
||||
USER_HEADER_SEARCH_PATHS = "../../.. $(inherited)";
|
||||
ZERO_LINK = NO;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
F93A88770E8B4C700026AF89 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
FRAMEWORK_SEARCH_PATHS = "$(DEVELOPER_LIBRARY_DIR)/Frameworks";
|
||||
GCC_CHAR_IS_UNSIGNED_CHAR = YES;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_ENABLE_FIX_AND_CONTINUE = NO;
|
||||
GCC_ENABLE_OBJC_EXCEPTIONS = YES;
|
||||
GCC_MODEL_TUNING = G5;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
INFOPLIST_FILE = "obj-cTestCases-Info.plist";
|
||||
INSTALL_PATH = "$(USER_LIBRARY_DIR)/Bundles";
|
||||
OTHER_LDFLAGS = (
|
||||
"-lcrypto",
|
||||
"-framework",
|
||||
Cocoa,
|
||||
"-framework",
|
||||
SenTestingKit,
|
||||
);
|
||||
PREBINDING = NO;
|
||||
PRODUCT_NAME = octestcases;
|
||||
USER_HEADER_SEARCH_PATHS = "../../../..//**";
|
||||
WRAPPER_EXTENSION = octest;
|
||||
|
@ -746,58 +710,20 @@
|
|||
F93A88780E8B4C700026AF89 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
COPY_PHASE_STRIP = YES;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
FRAMEWORK_SEARCH_PATHS = "$(DEVELOPER_LIBRARY_DIR)/Frameworks";
|
||||
GCC_CHAR_IS_UNSIGNED_CHAR = YES;
|
||||
GCC_ENABLE_FIX_AND_CONTINUE = NO;
|
||||
GCC_ENABLE_OBJC_EXCEPTIONS = YES;
|
||||
GCC_MODEL_TUNING = G5;
|
||||
INFOPLIST_FILE = "obj-cTestCases-Info.plist";
|
||||
INSTALL_PATH = "$(USER_LIBRARY_DIR)/Bundles";
|
||||
OTHER_LDFLAGS = (
|
||||
"-lcrypto",
|
||||
"-framework",
|
||||
Cocoa,
|
||||
"-framework",
|
||||
SenTestingKit,
|
||||
);
|
||||
PREBINDING = NO;
|
||||
PRODUCT_NAME = octestcases;
|
||||
USER_HEADER_SEARCH_PATHS = "../../../..//**";
|
||||
WRAPPER_EXTENSION = octest;
|
||||
ZERO_LINK = NO;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
F9AE19C40DB04A9500C98454 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ARCHS = (
|
||||
"$(NATIVE_ARCH_64_BIT)",
|
||||
ppc64,
|
||||
);
|
||||
COPY_PHASE_STRIP = NO;
|
||||
FRAMEWORK_SEARCH_PATHS = "$(DEVELOPER_LIBRARY_DIR)/Frameworks";
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_ENABLE_FIX_AND_CONTINUE = NO;
|
||||
GCC_MODEL_TUNING = G5;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
||||
GCC_PREFIX_HEADER = "$(SYSTEM_LIBRARY_DIR)/Frameworks/Carbon.framework/Headers/Carbon.h";
|
||||
INFOPLIST_FILE = "minidump_tests64-Info.plist";
|
||||
INSTALL_PATH = "$(USER_LIBRARY_DIR)/Bundles";
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.5;
|
||||
OTHER_LDFLAGS = (
|
||||
"-framework",
|
||||
Carbon,
|
||||
"-framework",
|
||||
CPlusTest,
|
||||
);
|
||||
PREBINDING = NO;
|
||||
PRODUCT_NAME = minidump_tests64;
|
||||
SDKROOT = /Developer/SDKs/MacOSX10.5.sdk;
|
||||
USER_HEADER_SEARCH_PATHS = "../../../**";
|
||||
WRAPPER_EXTENSION = cptest;
|
||||
};
|
||||
|
@ -806,57 +732,19 @@
|
|||
F9AE19C50DB04A9500C98454 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ARCHS = (
|
||||
"$(NATIVE_ARCH_64_BIT)",
|
||||
ppc64,
|
||||
);
|
||||
COPY_PHASE_STRIP = YES;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
FRAMEWORK_SEARCH_PATHS = "$(DEVELOPER_LIBRARY_DIR)/Frameworks";
|
||||
GCC_ENABLE_FIX_AND_CONTINUE = NO;
|
||||
GCC_MODEL_TUNING = G5;
|
||||
GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
||||
GCC_PREFIX_HEADER = "$(SYSTEM_LIBRARY_DIR)/Frameworks/Carbon.framework/Headers/Carbon.h";
|
||||
INFOPLIST_FILE = "minidump_tests64-Info.plist";
|
||||
INSTALL_PATH = "$(USER_LIBRARY_DIR)/Bundles";
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.5;
|
||||
OTHER_LDFLAGS = (
|
||||
"-framework",
|
||||
Carbon,
|
||||
"-framework",
|
||||
CPlusTest,
|
||||
);
|
||||
PREBINDING = NO;
|
||||
PRODUCT_NAME = minidump_tests64;
|
||||
SDKROOT = /Developer/SDKs/MacOSX10.5.sdk;
|
||||
USER_HEADER_SEARCH_PATHS = "../../../**";
|
||||
WRAPPER_EXTENSION = cptest;
|
||||
ZERO_LINK = NO;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
F9AE5B350DBFDBA300505983 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ARCHS = "$(NATIVE_ARCH)";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
FRAMEWORK_SEARCH_PATHS = "$(DEVELOPER_LIBRARY_DIR)/Frameworks";
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_ENABLE_FIX_AND_CONTINUE = NO;
|
||||
GCC_MODEL_TUNING = G5;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
||||
GCC_PREFIX_HEADER = "$(SYSTEM_LIBRARY_DIR)/Frameworks/Carbon.framework/Headers/Carbon.h";
|
||||
INFOPLIST_FILE = "minidump_tests32-Info.plist";
|
||||
INSTALL_PATH = "$(USER_LIBRARY_DIR)/Bundles";
|
||||
OTHER_LDFLAGS = (
|
||||
"-framework",
|
||||
Carbon,
|
||||
"-framework",
|
||||
CPlusTest,
|
||||
"-lcrypto",
|
||||
);
|
||||
PREBINDING = NO;
|
||||
PRODUCT_NAME = minidump_tests32;
|
||||
USER_HEADER_SEARCH_PATHS = "../../../**";
|
||||
WRAPPER_EXTENSION = cptest;
|
||||
|
@ -866,28 +754,11 @@
|
|||
F9AE5B370DBFDBA300505983 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ARCHS = "$(NATIVE_ARCH)";
|
||||
COPY_PHASE_STRIP = YES;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
FRAMEWORK_SEARCH_PATHS = "$(DEVELOPER_LIBRARY_DIR)/Frameworks";
|
||||
GCC_ENABLE_FIX_AND_CONTINUE = NO;
|
||||
GCC_MODEL_TUNING = G5;
|
||||
GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
||||
GCC_PREFIX_HEADER = "$(SYSTEM_LIBRARY_DIR)/Frameworks/Carbon.framework/Headers/Carbon.h";
|
||||
INFOPLIST_FILE = "minidump_tests32-Info.plist";
|
||||
INSTALL_PATH = "$(USER_LIBRARY_DIR)/Bundles";
|
||||
OTHER_LDFLAGS = (
|
||||
"-lcrypto",
|
||||
"-framework",
|
||||
Carbon,
|
||||
"-framework",
|
||||
CPlusTest,
|
||||
);
|
||||
PREBINDING = NO;
|
||||
PRODUCT_NAME = minidump_tests32;
|
||||
USER_HEADER_SEARCH_PATHS = "../../../**";
|
||||
WRAPPER_EXTENSION = cptest;
|
||||
ZERO_LINK = NO;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
|
|
|
@ -59,7 +59,7 @@ ProtectedMemoryAllocator::~ProtectedMemoryAllocator() {
|
|||
}
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
char *ProtectedMemoryAllocator::Allocate(size_t bytes) {
|
||||
char *ProtectedMemoryAllocator::Allocate(vm_size_t bytes) {
|
||||
if (valid_ && next_alloc_offset_ + bytes <= pool_size_) {
|
||||
char *p = (char*)base_address_ + next_alloc_offset_;
|
||||
next_alloc_offset_ += bytes;
|
||||
|
|
|
@ -53,7 +53,7 @@ class ProtectedMemoryAllocator {
|
|||
// Fails by returning NULL is no more space is available.
|
||||
// Please note that the pointers returned from this method should not
|
||||
// be freed in any way (for example by calling free() on them ).
|
||||
char * Allocate(size_t n);
|
||||
char * Allocate(vm_size_t n);
|
||||
|
||||
// Returns the base address of the allocation pool.
|
||||
char * GetBaseAddress() { return (char*)base_address_; }
|
||||
|
@ -78,7 +78,7 @@ class ProtectedMemoryAllocator {
|
|||
private:
|
||||
vm_size_t pool_size_;
|
||||
vm_address_t base_address_;
|
||||
int next_alloc_offset_;
|
||||
vm_size_t next_alloc_offset_;
|
||||
bool valid_;
|
||||
};
|
||||
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
#include <Foundation/Foundation.h>
|
||||
|
||||
#include "client/mac/Framework/Breakpad.h"
|
||||
#import "GTMDefines.h"
|
||||
|
||||
#define kClientIdPreferenceKey @"clientid"
|
||||
|
||||
|
@ -53,10 +54,10 @@ extern NSString *const kDefaultServerType;
|
|||
// work in the middle of a validation.
|
||||
@interface LengthLimitingTextField : NSTextField {
|
||||
@private
|
||||
unsigned int maximumLength_;
|
||||
NSUInteger maximumLength_;
|
||||
}
|
||||
|
||||
- (void) setMaximumLength:(unsigned int)maxLength;
|
||||
- (void)setMaximumLength:(NSUInteger)maxLength;
|
||||
@end
|
||||
|
||||
@interface Reporter : NSObject {
|
||||
|
|
|
@ -56,20 +56,20 @@ NSString *const kDefaultServerType = @"google";
|
|||
|
||||
@interface NSView (ResizabilityExtentions)
|
||||
// Shifts the view vertically by the given amount.
|
||||
- (void)breakpad_shiftVertically:(float)offset;
|
||||
- (void)breakpad_shiftVertically:(CGFloat)offset;
|
||||
|
||||
// Shifts the view horizontally by the given amount.
|
||||
- (void)breakpad_shiftHorizontally:(float)offset;
|
||||
- (void)breakpad_shiftHorizontally:(CGFloat)offset;
|
||||
@end
|
||||
|
||||
@implementation NSView (ResizabilityExtentions)
|
||||
- (void)breakpad_shiftVertically:(float)offset {
|
||||
- (void)breakpad_shiftVertically:(CGFloat)offset {
|
||||
NSPoint origin = [self frame].origin;
|
||||
origin.y += offset;
|
||||
[self setFrameOrigin:origin];
|
||||
}
|
||||
|
||||
- (void)breakpad_shiftHorizontally:(float)offset {
|
||||
- (void)breakpad_shiftHorizontally:(CGFloat)offset {
|
||||
NSPoint origin = [self frame].origin;
|
||||
origin.x += offset;
|
||||
[self setFrameOrigin:origin];
|
||||
|
@ -79,11 +79,11 @@ NSString *const kDefaultServerType = @"google";
|
|||
@interface NSWindow (ResizabilityExtentions)
|
||||
// Adjusts the window height by heightDelta relative to its current height,
|
||||
// keeping all the content at the same size.
|
||||
- (void)breakpad_adjustHeight:(float)heightDelta;
|
||||
- (void)breakpad_adjustHeight:(CGFloat)heightDelta;
|
||||
@end
|
||||
|
||||
@implementation NSWindow (ResizabilityExtentions)
|
||||
- (void)breakpad_adjustHeight:(float)heightDelta {
|
||||
- (void)breakpad_adjustHeight:(CGFloat)heightDelta {
|
||||
[[self contentView] setAutoresizesSubviews:NO];
|
||||
|
||||
NSRect windowFrame = [self frame];
|
||||
|
@ -101,16 +101,16 @@ NSString *const kDefaultServerType = @"google";
|
|||
// Grows or shrinks the height of the field to the minimum required to show the
|
||||
// current text, preserving the existing width and origin.
|
||||
// Returns the change in height.
|
||||
- (float)breakpad_adjustHeightToFit;
|
||||
- (CGFloat)breakpad_adjustHeightToFit;
|
||||
|
||||
// Grows or shrinks the width of the field to the minimum required to show the
|
||||
// current text, preserving the existing height and origin.
|
||||
// Returns the change in width.
|
||||
- (float)breakpad_adjustWidthToFit;
|
||||
- (CGFloat)breakpad_adjustWidthToFit;
|
||||
@end
|
||||
|
||||
@implementation NSTextField (ResizabilityExtentions)
|
||||
- (float)breakpad_adjustHeightToFit {
|
||||
- (CGFloat)breakpad_adjustHeightToFit {
|
||||
NSRect oldFrame = [self frame];
|
||||
// Starting with the 10.5 SDK, height won't grow, so make it huge to start.
|
||||
NSRect presizeFrame = oldFrame;
|
||||
|
@ -125,7 +125,7 @@ NSString *const kDefaultServerType = @"google";
|
|||
return newSize.height - NSHeight(oldFrame);
|
||||
}
|
||||
|
||||
- (float)breakpad_adjustWidthToFit {
|
||||
- (CGFloat)breakpad_adjustWidthToFit {
|
||||
NSRect oldFrame = [self frame];
|
||||
[self sizeToFit];
|
||||
return NSWidth([self frame]) - NSWidth(oldFrame);
|
||||
|
@ -136,11 +136,11 @@ NSString *const kDefaultServerType = @"google";
|
|||
// Resizes to fit the label using IB-style size-to-fit metrics and enforcing a
|
||||
// minimum width of 70, while preserving the right edge location.
|
||||
// Returns the change in width.
|
||||
- (float)breakpad_smartSizeToFit;
|
||||
- (CGFloat)breakpad_smartSizeToFit;
|
||||
@end
|
||||
|
||||
@implementation NSButton (ResizabilityExtentions)
|
||||
- (float)breakpad_smartSizeToFit {
|
||||
- (CGFloat)breakpad_smartSizeToFit {
|
||||
NSRect oldFrame = [self frame];
|
||||
[self sizeToFit];
|
||||
NSRect newFrame = [self frame];
|
||||
|
@ -218,7 +218,8 @@ NSString *const kDefaultServerType = @"google";
|
|||
// Run an alert window with the given timeout. Returns
|
||||
// NSRunStoppedResponse if the timeout is exceeded. A timeout of 0
|
||||
// queues the message immediately in the modal run loop.
|
||||
- (int)runModalWindow:(NSWindow*)window withTimeout:(NSTimeInterval)timeout;
|
||||
- (NSInteger)runModalWindow:(NSWindow*)window
|
||||
withTimeout:(NSTimeInterval)timeout;
|
||||
|
||||
// Returns a unique client id (user-specific), creating a persistent
|
||||
// one in the user defaults, if necessary.
|
||||
|
@ -386,7 +387,7 @@ NSString *const kDefaultServerType = @"google";
|
|||
}
|
||||
|
||||
// Otherwise, if we have no client id, generate one!
|
||||
srandom([[NSDate date] timeIntervalSince1970]);
|
||||
srandom((int)[[NSDate date] timeIntervalSince1970]);
|
||||
long clientId1 = random();
|
||||
long clientId2 = random();
|
||||
long clientId3 = random();
|
||||
|
@ -403,8 +404,8 @@ NSString *const kDefaultServerType = @"google";
|
|||
unsigned int logFileCounter = 0;
|
||||
|
||||
NSString *logPath;
|
||||
int logFileTailSize = [[parameters_ objectForKey:@BREAKPAD_LOGFILE_UPLOAD_SIZE]
|
||||
intValue];
|
||||
size_t logFileTailSize =
|
||||
[[parameters_ objectForKey:@BREAKPAD_LOGFILE_UPLOAD_SIZE] intValue];
|
||||
|
||||
NSMutableArray *logFilenames; // An array of NSString, one per log file
|
||||
logFilenames = [[NSMutableArray alloc] init];
|
||||
|
@ -544,7 +545,7 @@ NSString *const kDefaultServerType = @"google";
|
|||
// Get the timeout value for the notification.
|
||||
NSTimeInterval timeout = [self messageTimeout];
|
||||
|
||||
int buttonPressed = NSAlertAlternateReturn;
|
||||
NSInteger buttonPressed = NSAlertAlternateReturn;
|
||||
// Determine whether we should create a text box for user feedback.
|
||||
if ([self shouldRequestComments]) {
|
||||
BOOL didLoadNib = [NSBundle loadNibNamed:@"Breakpad" owner:self];
|
||||
|
@ -592,7 +593,7 @@ NSString *const kDefaultServerType = @"google";
|
|||
[commentMessage_ setStringValue:[NSString stringWithFormat:@"%@\n\n%@",
|
||||
[self explanatoryDialogText],
|
||||
NSLocalizedString(@"commentsMsg", @"")]];
|
||||
float commentHeightDelta = [commentMessage_ breakpad_adjustHeightToFit];
|
||||
CGFloat commentHeightDelta = [commentMessage_ breakpad_adjustHeightToFit];
|
||||
[headerBox_ breakpad_shiftVertically:commentHeightDelta];
|
||||
[alertWindow_ breakpad_adjustHeight:commentHeightDelta];
|
||||
|
||||
|
@ -600,7 +601,7 @@ NSString *const kDefaultServerType = @"google";
|
|||
// section depending on whether or not we are asking for email.
|
||||
if (includeEmail) {
|
||||
[emailMessage_ setStringValue:NSLocalizedString(@"emailMsg", @"")];
|
||||
float emailHeightDelta = [emailMessage_ breakpad_adjustHeightToFit];
|
||||
CGFloat emailHeightDelta = [emailMessage_ breakpad_adjustHeightToFit];
|
||||
[preEmailBox_ breakpad_shiftVertically:emailHeightDelta];
|
||||
[alertWindow_ breakpad_adjustHeight:emailHeightDelta];
|
||||
} else {
|
||||
|
@ -609,7 +610,7 @@ NSString *const kDefaultServerType = @"google";
|
|||
|
||||
// Localize the email label, and shift the associated text field.
|
||||
[emailLabel_ setStringValue:NSLocalizedString(@"emailLabel", @"")];
|
||||
float emailLabelWidthDelta = [emailLabel_ breakpad_adjustWidthToFit];
|
||||
CGFloat emailLabelWidthDelta = [emailLabel_ breakpad_adjustWidthToFit];
|
||||
[emailEntryField_ breakpad_shiftHorizontally:emailLabelWidthDelta];
|
||||
|
||||
// Localize the placeholder text.
|
||||
|
@ -620,12 +621,12 @@ NSString *const kDefaultServerType = @"google";
|
|||
|
||||
// Localize the privacy policy label, and keep it right-aligned to the arrow.
|
||||
[privacyLinkLabel_ setStringValue:NSLocalizedString(@"privacyLabel", @"")];
|
||||
float privacyLabelWidthDelta = [privacyLinkLabel_ breakpad_adjustWidthToFit];
|
||||
CGFloat privacyLabelWidthDelta = [privacyLinkLabel_ breakpad_adjustWidthToFit];
|
||||
[privacyLinkLabel_ breakpad_shiftHorizontally:(-privacyLabelWidthDelta)];
|
||||
|
||||
// Localize the buttons, and keep the cancel button at the right distance.
|
||||
[sendButton_ setTitle:NSLocalizedString(@"sendReportButton", @"")];
|
||||
float sendButtonWidthDelta = [sendButton_ breakpad_smartSizeToFit];
|
||||
CGFloat sendButtonWidthDelta = [sendButton_ breakpad_smartSizeToFit];
|
||||
[cancelButton_ breakpad_shiftHorizontally:(-sendButtonWidthDelta)];
|
||||
[cancelButton_ setTitle:NSLocalizedString(@"cancelButton", @"")];
|
||||
[cancelButton_ breakpad_smartSizeToFit];
|
||||
|
@ -633,12 +634,13 @@ NSString *const kDefaultServerType = @"google";
|
|||
|
||||
- (void)removeEmailPrompt {
|
||||
[emailSectionBox_ setHidden:YES];
|
||||
float emailSectionHeight = NSHeight([emailSectionBox_ frame]);
|
||||
CGFloat emailSectionHeight = NSHeight([emailSectionBox_ frame]);
|
||||
[preEmailBox_ breakpad_shiftVertically:(-emailSectionHeight)];
|
||||
[alertWindow_ breakpad_adjustHeight:(-emailSectionHeight)];
|
||||
}
|
||||
|
||||
- (int)runModalWindow:(NSWindow*)window withTimeout:(NSTimeInterval)timeout {
|
||||
- (NSInteger)runModalWindow:(NSWindow*)window
|
||||
withTimeout:(NSTimeInterval)timeout {
|
||||
// Queue a |stopModal| message to be performed in |timeout| seconds.
|
||||
if (timeout > 0.001) {
|
||||
remainingDialogTime_ = timeout;
|
||||
|
@ -653,7 +655,7 @@ NSString *const kDefaultServerType = @"google";
|
|||
// Run the window modally and wait for either a |stopModal| message or a
|
||||
// button click.
|
||||
[NSApp activateIgnoringOtherApps:YES];
|
||||
int returnMethod = [NSApp runModalForWindow:window];
|
||||
NSInteger returnMethod = [NSApp runModalForWindow:window];
|
||||
|
||||
return returnMethod;
|
||||
}
|
||||
|
@ -717,7 +719,7 @@ doCommandBySelector:(SEL)commandSelector {
|
|||
|
||||
if (remainingDialogTime_ > 59) {
|
||||
// calculate minutes remaining for UI purposes
|
||||
displayedTimeLeft = (remainingDialogTime_ / 60);
|
||||
displayedTimeLeft = (int)(remainingDialogTime_ / 60);
|
||||
|
||||
if (displayedTimeLeft == 1) {
|
||||
formatString = NSLocalizedString(@"countdownMsgMinuteSingular", @"");
|
||||
|
@ -725,8 +727,8 @@ doCommandBySelector:(SEL)commandSelector {
|
|||
formatString = NSLocalizedString(@"countdownMsgMinutesPlural", @"");
|
||||
}
|
||||
} else {
|
||||
displayedTimeLeft = remainingDialogTime_;
|
||||
if (remainingDialogTime_ == 1) {
|
||||
displayedTimeLeft = (int)remainingDialogTime_;
|
||||
if (displayedTimeLeft == 1) {
|
||||
formatString = NSLocalizedString(@"countdownMsgSecondSingular", @"");
|
||||
} else {
|
||||
formatString = NSLocalizedString(@"countdownMsgSecondsPlural", @"");
|
||||
|
@ -797,7 +799,8 @@ doCommandBySelector:(SEL)commandSelector {
|
|||
NSTimeInterval now = CFAbsoluteTimeGetCurrent();
|
||||
NSTimeInterval spanSeconds = (now - lastTime);
|
||||
|
||||
[programDict setObject:[NSNumber numberWithFloat:now] forKey:kLastSubmission];
|
||||
[programDict setObject:[NSNumber numberWithDouble:now]
|
||||
forKey:kLastSubmission];
|
||||
[ud setObject:programDict forKey:program];
|
||||
[ud synchronize];
|
||||
|
||||
|
@ -1055,7 +1058,7 @@ doCommandBySelector:(SEL)commandSelector {
|
|||
//=============================================================================
|
||||
@implementation LengthLimitingTextField
|
||||
|
||||
- (void) setMaximumLength:(unsigned int)maxLength {
|
||||
- (void)setMaximumLength:(NSUInteger)maxLength {
|
||||
maximumLength_ = maxLength;
|
||||
}
|
||||
|
||||
|
@ -1072,7 +1075,7 @@ shouldChangeTextInRange:(NSRange)affectedCharRange
|
|||
}
|
||||
// Figure out what the new string length would be, taking into
|
||||
// account user selections.
|
||||
int newStringLength =
|
||||
NSUInteger newStringLength =
|
||||
[[textView string] length] - affectedCharRange.length +
|
||||
[replacementString length];
|
||||
if (newStringLength > maximumLength_) {
|
||||
|
@ -1088,7 +1091,7 @@ shouldChangeTextInRange:(NSRange)affectedCharRange
|
|||
NSText* fieldEditor = [self currentEditor];
|
||||
if (fieldEditor != nil) {
|
||||
// Check for a single "Command" modifier
|
||||
unsigned int modifiers = [event modifierFlags];
|
||||
NSUInteger modifiers = [event modifierFlags];
|
||||
modifiers &= NSDeviceIndependentModifierFlagsMask;
|
||||
if (modifiers == NSCommandKeyMask) {
|
||||
// Now, check for Select All, Cut, Copy, or Paste key equivalents.
|
||||
|
|
Двоичные данные
toolkit/crashreporter/google-breakpad/src/client/mac/sender/da.lproj/InfoPlist.strings
Normal file
Двоичные данные
toolkit/crashreporter/google-breakpad/src/client/mac/sender/da.lproj/InfoPlist.strings
Normal file
Двоичный файл не отображается.
Двоичные данные
toolkit/crashreporter/google-breakpad/src/client/mac/sender/da.lproj/Localizable.strings
Normal file
Двоичные данные
toolkit/crashreporter/google-breakpad/src/client/mac/sender/da.lproj/Localizable.strings
Normal file
Двоичный файл не отображается.
Двоичные данные
toolkit/crashreporter/google-breakpad/src/client/mac/sender/de.lproj/InfoPlist.strings
Normal file
Двоичные данные
toolkit/crashreporter/google-breakpad/src/client/mac/sender/de.lproj/InfoPlist.strings
Normal file
Двоичный файл не отображается.
Двоичные данные
toolkit/crashreporter/google-breakpad/src/client/mac/sender/de.lproj/Localizable.strings
Normal file
Двоичные данные
toolkit/crashreporter/google-breakpad/src/client/mac/sender/de.lproj/Localizable.strings
Normal file
Двоичный файл не отображается.
Двоичные данные
toolkit/crashreporter/google-breakpad/src/client/mac/sender/es.lproj/InfoPlist.strings
Normal file
Двоичные данные
toolkit/crashreporter/google-breakpad/src/client/mac/sender/es.lproj/InfoPlist.strings
Normal file
Двоичный файл не отображается.
Двоичные данные
toolkit/crashreporter/google-breakpad/src/client/mac/sender/es.lproj/Localizable.strings
Normal file
Двоичные данные
toolkit/crashreporter/google-breakpad/src/client/mac/sender/es.lproj/Localizable.strings
Normal file
Двоичный файл не отображается.
Двоичные данные
toolkit/crashreporter/google-breakpad/src/client/mac/sender/fr.lproj/InfoPlist.strings
Normal file
Двоичные данные
toolkit/crashreporter/google-breakpad/src/client/mac/sender/fr.lproj/InfoPlist.strings
Normal file
Двоичный файл не отображается.
Двоичные данные
toolkit/crashreporter/google-breakpad/src/client/mac/sender/fr.lproj/Localizable.strings
Normal file
Двоичные данные
toolkit/crashreporter/google-breakpad/src/client/mac/sender/fr.lproj/Localizable.strings
Normal file
Двоичный файл не отображается.
Двоичные данные
toolkit/crashreporter/google-breakpad/src/client/mac/sender/it.lproj/InfoPlist.strings
Normal file
Двоичные данные
toolkit/crashreporter/google-breakpad/src/client/mac/sender/it.lproj/InfoPlist.strings
Normal file
Двоичный файл не отображается.
Двоичные данные
toolkit/crashreporter/google-breakpad/src/client/mac/sender/it.lproj/Localizable.strings
Normal file
Двоичные данные
toolkit/crashreporter/google-breakpad/src/client/mac/sender/it.lproj/Localizable.strings
Normal file
Двоичный файл не отображается.
Двоичные данные
toolkit/crashreporter/google-breakpad/src/client/mac/sender/ja.lproj/InfoPlist.strings
Normal file
Двоичные данные
toolkit/crashreporter/google-breakpad/src/client/mac/sender/ja.lproj/InfoPlist.strings
Normal file
Двоичный файл не отображается.
Двоичные данные
toolkit/crashreporter/google-breakpad/src/client/mac/sender/ja.lproj/Localizable.strings
Normal file
Двоичные данные
toolkit/crashreporter/google-breakpad/src/client/mac/sender/ja.lproj/Localizable.strings
Normal file
Двоичный файл не отображается.
Двоичные данные
toolkit/crashreporter/google-breakpad/src/client/mac/sender/nl.lproj/InfoPlist.strings
Normal file
Двоичные данные
toolkit/crashreporter/google-breakpad/src/client/mac/sender/nl.lproj/InfoPlist.strings
Normal file
Двоичный файл не отображается.
Двоичные данные
toolkit/crashreporter/google-breakpad/src/client/mac/sender/nl.lproj/Localizable.strings
Normal file
Двоичные данные
toolkit/crashreporter/google-breakpad/src/client/mac/sender/nl.lproj/Localizable.strings
Normal file
Двоичный файл не отображается.
Двоичные данные
toolkit/crashreporter/google-breakpad/src/client/mac/sender/no.lproj/InfoPlist.strings
Normal file
Двоичные данные
toolkit/crashreporter/google-breakpad/src/client/mac/sender/no.lproj/InfoPlist.strings
Normal file
Двоичный файл не отображается.
Двоичные данные
toolkit/crashreporter/google-breakpad/src/client/mac/sender/no.lproj/Localizable.strings
Normal file
Двоичные данные
toolkit/crashreporter/google-breakpad/src/client/mac/sender/no.lproj/Localizable.strings
Normal file
Двоичный файл не отображается.
Двоичные данные
toolkit/crashreporter/google-breakpad/src/client/mac/sender/sl.lproj/InfoPlist.strings
Normal file
Двоичные данные
toolkit/crashreporter/google-breakpad/src/client/mac/sender/sl.lproj/InfoPlist.strings
Normal file
Двоичный файл не отображается.
Двоичные данные
toolkit/crashreporter/google-breakpad/src/client/mac/sender/sl.lproj/Localizable.strings
Normal file
Двоичные данные
toolkit/crashreporter/google-breakpad/src/client/mac/sender/sl.lproj/Localizable.strings
Normal file
Двоичный файл не отображается.
Двоичные данные
toolkit/crashreporter/google-breakpad/src/client/mac/sender/sv.lproj/InfoPlist.strings
Normal file
Двоичные данные
toolkit/crashreporter/google-breakpad/src/client/mac/sender/sv.lproj/InfoPlist.strings
Normal file
Двоичный файл не отображается.
Двоичные данные
toolkit/crashreporter/google-breakpad/src/client/mac/sender/sv.lproj/Localizable.strings
Normal file
Двоичные данные
toolkit/crashreporter/google-breakpad/src/client/mac/sender/sv.lproj/Localizable.strings
Normal file
Двоичный файл не отображается.
Двоичные данные
toolkit/crashreporter/google-breakpad/src/client/mac/sender/tr.lproj/InfoPlist.strings
Normal file
Двоичные данные
toolkit/crashreporter/google-breakpad/src/client/mac/sender/tr.lproj/InfoPlist.strings
Normal file
Двоичный файл не отображается.
Двоичные данные
toolkit/crashreporter/google-breakpad/src/client/mac/sender/tr.lproj/Localizable.strings
Normal file
Двоичные данные
toolkit/crashreporter/google-breakpad/src/client/mac/sender/tr.lproj/Localizable.strings
Normal file
Двоичный файл не отображается.
|
@ -31,6 +31,7 @@
|
|||
|
||||
#import "Controller.h"
|
||||
#import "TestClass.h"
|
||||
#import "GTMDefines.h"
|
||||
#include <unistd.h>
|
||||
#include <mach/mach.h>
|
||||
|
||||
|
@ -51,7 +52,7 @@
|
|||
}
|
||||
|
||||
- (IBAction)forkTestOptions:(id)sender {
|
||||
int tag = [[sender selectedCell] tag];
|
||||
NSInteger tag = [[sender selectedCell] tag];
|
||||
NSLog(@"sender tag: %d", tag);
|
||||
if (tag <= 2) {
|
||||
bpForkOption = tag;
|
||||
|
@ -75,7 +76,7 @@
|
|||
|
||||
NSString *resourcePath = [[NSBundle bundleForClass:
|
||||
[self class]] resourcePath];
|
||||
NSString *execProgname;
|
||||
NSString *execProgname = nil;
|
||||
if (progCrashPoint == DURINGLAUNCH) {
|
||||
execProgname = [resourcePath stringByAppendingString:@"/crashduringload"];
|
||||
} else if (progCrashPoint == AFTERLAUNCH) {
|
||||
|
@ -129,11 +130,11 @@
|
|||
}
|
||||
|
||||
- (IBAction)crash:(id)sender {
|
||||
int tag = [sender tag];
|
||||
NSInteger tag = [sender tag];
|
||||
|
||||
if (tag == 1) {
|
||||
[NSObject cancelPreviousPerformRequestsWithTarget:self];
|
||||
[self performSelector:@selector(causeCrash) withObject:nil afterDelay:10];
|
||||
[self performSelector:@selector(causeCrash) withObject:nil afterDelay:10.0];
|
||||
[sender setState:NSOnState];
|
||||
return;
|
||||
}
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -78,7 +78,7 @@ void InternalTestClass::InternalFunction(AStruct &s) {
|
|||
float InternalTestClass::kStaticFloatValue = 42;
|
||||
|
||||
static float PlainOldFunction() {
|
||||
return 3.14145;
|
||||
return 3.14145f;
|
||||
}
|
||||
|
||||
@implementation TestClass
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#import <GTMSenTestCase.h>
|
||||
#import "GTMSenTestCase.h"
|
||||
#import "SimpleStringDictionary.h"
|
||||
|
||||
@interface SimpleStringDictionaryTest : GTMTestCase {
|
||||
|
|
|
@ -0,0 +1,72 @@
|
|||
// Copyright (c) 2010, 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.
|
||||
|
||||
// Utility class for creating a temporary directory for unit tests
|
||||
// that is deleted in the destructor.
|
||||
#ifndef GOOGLE_BREAKPAD_CLIENT_MAC_TESTS_AUTO_TEMPDIR
|
||||
#define GOOGLE_BREAKPAD_CLIENT_MAC_TESTS_AUTO_TEMPDIR
|
||||
|
||||
#include <dirent.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
class AutoTempDir {
|
||||
public:
|
||||
AutoTempDir() {
|
||||
char tempDir[16] = "/tmp/XXXXXXXXXX";
|
||||
mkdtemp(tempDir);
|
||||
path = tempDir;
|
||||
}
|
||||
|
||||
~AutoTempDir() {
|
||||
// First remove any files in the dir
|
||||
DIR* dir = opendir(path.c_str());
|
||||
if (!dir)
|
||||
return;
|
||||
|
||||
dirent* entry;
|
||||
while ((entry = readdir(dir)) != NULL) {
|
||||
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
|
||||
continue;
|
||||
std::string entryPath = path + "/" + entry->d_name;
|
||||
unlink(entryPath.c_str());
|
||||
}
|
||||
closedir(dir);
|
||||
rmdir(path.c_str());
|
||||
}
|
||||
|
||||
std::string path;
|
||||
};
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
#endif // GOOGLE_BREAKPAD_CLIENT_MAC_TESTS_AUTO_TEMPDIR
|
|
@ -0,0 +1,223 @@
|
|||
// Copyright (c) 2010, 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.
|
||||
//
|
||||
// crash_generation_server_test.cc
|
||||
// Unit tests for CrashGenerationServer
|
||||
|
||||
#include <dirent.h>
|
||||
#include <glob.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "breakpad_googletest_includes.h"
|
||||
#include "client/mac/crash_generation/client_info.h"
|
||||
#include "client/mac/crash_generation/crash_generation_client.h"
|
||||
#include "client/mac/crash_generation/crash_generation_server.h"
|
||||
#include "client/mac/handler/exception_handler.h"
|
||||
#include "client/mac/tests/auto_tempdir.h"
|
||||
|
||||
namespace {
|
||||
using std::string;
|
||||
using google_breakpad::AutoTempDir;
|
||||
using google_breakpad::ClientInfo;
|
||||
using google_breakpad::CrashGenerationClient;
|
||||
using google_breakpad::CrashGenerationServer;
|
||||
using google_breakpad::ExceptionHandler;
|
||||
using testing::Test;
|
||||
|
||||
class CrashGenerationServerTest : public Test {
|
||||
public:
|
||||
// The port name to receive messages on
|
||||
char mach_port_name[128];
|
||||
// Filename of the last dump that was generated
|
||||
string last_dump_name;
|
||||
// PID of the child process
|
||||
pid_t child_pid;
|
||||
// A temp dir
|
||||
AutoTempDir temp_dir;
|
||||
// Counter just to ensure that we don't hit the same port again
|
||||
static int i;
|
||||
|
||||
void SetUp() {
|
||||
sprintf(mach_port_name,
|
||||
"com.google.breakpad.ServerTest.%d.%d", getpid(),
|
||||
CrashGenerationServerTest::i++);
|
||||
child_pid = (pid_t)-1;
|
||||
}
|
||||
};
|
||||
int CrashGenerationServerTest::i = 0;
|
||||
|
||||
// Test that starting and stopping a server works
|
||||
TEST_F(CrashGenerationServerTest, testStartStopServer) {
|
||||
CrashGenerationServer server(mach_port_name,
|
||||
NULL, // dump callback
|
||||
NULL, // dump context
|
||||
NULL, // exit callback
|
||||
NULL, // exit context
|
||||
false, // generate dumps
|
||||
""); // dump path
|
||||
ASSERT_TRUE(server.Start());
|
||||
ASSERT_TRUE(server.Stop());
|
||||
}
|
||||
|
||||
// Test that requesting a dump via CrashGenerationClient works
|
||||
// Test without actually dumping
|
||||
TEST_F(CrashGenerationServerTest, testRequestDumpNoDump) {
|
||||
CrashGenerationServer server(mach_port_name,
|
||||
NULL, // dump callback
|
||||
NULL, // dump context
|
||||
NULL, // exit callback
|
||||
NULL, // exit context
|
||||
false, // don't generate dumps
|
||||
temp_dir.path); // dump path
|
||||
ASSERT_TRUE(server.Start());
|
||||
|
||||
pid_t pid = fork();
|
||||
ASSERT_NE(-1, pid);
|
||||
if (pid == 0) {
|
||||
CrashGenerationClient client(mach_port_name);
|
||||
bool result = client.RequestDump();
|
||||
exit(result ? 0 : 1);
|
||||
}
|
||||
|
||||
int ret;
|
||||
ASSERT_EQ(pid, waitpid(pid, &ret, 0));
|
||||
EXPECT_TRUE(WIFEXITED(ret));
|
||||
EXPECT_EQ(0, WEXITSTATUS(ret));
|
||||
EXPECT_TRUE(server.Stop());
|
||||
// check that no minidump was written
|
||||
string pattern = temp_dir.path + "/*";
|
||||
glob_t dirContents;
|
||||
ret = glob(pattern.c_str(), GLOB_NOSORT, NULL, &dirContents);
|
||||
EXPECT_EQ(GLOB_NOMATCH, ret);
|
||||
if (ret != GLOB_NOMATCH)
|
||||
globfree(&dirContents);
|
||||
}
|
||||
|
||||
void dumpCallback(void *context, const ClientInfo &client_info,
|
||||
const std::string &file_path) {
|
||||
if (context) {
|
||||
CrashGenerationServerTest* self =
|
||||
reinterpret_cast<CrashGenerationServerTest*>(context);
|
||||
if (!file_path.empty())
|
||||
self->last_dump_name = file_path;
|
||||
self->child_pid = client_info.pid();
|
||||
}
|
||||
}
|
||||
|
||||
void *RequestDump(void *context) {
|
||||
CrashGenerationClient client((const char*)context);
|
||||
bool result = client.RequestDump();
|
||||
return (void*)(result ? 0 : 1);
|
||||
}
|
||||
|
||||
// Test that actually writing a minidump works
|
||||
TEST_F(CrashGenerationServerTest, testRequestDump) {
|
||||
CrashGenerationServer server(mach_port_name,
|
||||
dumpCallback, // dump callback
|
||||
this, // dump context
|
||||
NULL, // exit callback
|
||||
NULL, // exit context
|
||||
true, // generate dumps
|
||||
temp_dir.path); // dump path
|
||||
ASSERT_TRUE(server.Start());
|
||||
|
||||
pid_t pid = fork();
|
||||
ASSERT_NE(-1, pid);
|
||||
if (pid == 0) {
|
||||
// Have to spawn off a separate thread to request the dump,
|
||||
// because MinidumpGenerator assumes the handler thread is not
|
||||
// the only thread
|
||||
pthread_t thread;
|
||||
if (pthread_create(&thread, NULL, RequestDump, (void*)mach_port_name) != 0)
|
||||
exit(1);
|
||||
void* result;
|
||||
pthread_join(thread, &result);
|
||||
exit(reinterpret_cast<intptr_t>(result));
|
||||
}
|
||||
|
||||
int ret;
|
||||
ASSERT_EQ(pid, waitpid(pid, &ret, 0));
|
||||
EXPECT_TRUE(WIFEXITED(ret));
|
||||
EXPECT_EQ(0, WEXITSTATUS(ret));
|
||||
EXPECT_TRUE(server.Stop());
|
||||
// check that minidump was written
|
||||
ASSERT_FALSE(last_dump_name.empty());
|
||||
struct stat st;
|
||||
EXPECT_EQ(0, stat(last_dump_name.c_str(), &st));
|
||||
EXPECT_LT(0, st.st_size);
|
||||
// check client's PID
|
||||
ASSERT_EQ(pid, child_pid);
|
||||
}
|
||||
|
||||
static void Crasher() {
|
||||
int *a = (int*)0x42;
|
||||
|
||||
fprintf(stdout, "Going to crash...\n");
|
||||
fprintf(stdout, "A = %d", *a);
|
||||
}
|
||||
|
||||
// Test that crashing a child process with an OOP ExceptionHandler installed
|
||||
// results in a minidump being written by the CrashGenerationServer in
|
||||
// the parent.
|
||||
TEST_F(CrashGenerationServerTest, testChildProcessCrash) {
|
||||
CrashGenerationServer server(mach_port_name,
|
||||
dumpCallback, // dump callback
|
||||
this, // dump context
|
||||
NULL, // exit callback
|
||||
NULL, // exit context
|
||||
true, // generate dumps
|
||||
temp_dir.path); // dump path
|
||||
ASSERT_TRUE(server.Start());
|
||||
|
||||
pid_t pid = fork();
|
||||
ASSERT_NE(-1, pid);
|
||||
if (pid == 0) {
|
||||
// Instantiate an OOP exception handler.
|
||||
ExceptionHandler eh("", NULL, NULL, NULL, true, mach_port_name);
|
||||
Crasher();
|
||||
// not reached
|
||||
exit(0);
|
||||
}
|
||||
|
||||
int ret;
|
||||
ASSERT_EQ(pid, waitpid(pid, &ret, 0));
|
||||
EXPECT_FALSE(WIFEXITED(ret));
|
||||
EXPECT_TRUE(server.Stop());
|
||||
// check that minidump was written
|
||||
ASSERT_FALSE(last_dump_name.empty());
|
||||
struct stat st;
|
||||
EXPECT_EQ(0, stat(last_dump_name.c_str(), &st));
|
||||
EXPECT_LT(0, st.st_size);
|
||||
}
|
||||
|
||||
} // namespace
|
|
@ -0,0 +1,198 @@
|
|||
// Copyright (c) 2010, 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.
|
||||
|
||||
// exception_handler_test.cc: Unit tests for google_breakpad::ExceptionHandler
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "breakpad_googletest_includes.h"
|
||||
#include "client/mac/handler/exception_handler.h"
|
||||
#include "client/mac/tests/auto_tempdir.h"
|
||||
#include "common/mac/MachIPC.h"
|
||||
|
||||
namespace {
|
||||
using std::string;
|
||||
using google_breakpad::AutoTempDir;
|
||||
using google_breakpad::ExceptionHandler;
|
||||
using google_breakpad::MachPortSender;
|
||||
using google_breakpad::MachReceiveMessage;
|
||||
using google_breakpad::MachSendMessage;
|
||||
using google_breakpad::ReceivePort;
|
||||
using testing::Test;
|
||||
|
||||
class ExceptionHandlerTest : public Test {
|
||||
public:
|
||||
AutoTempDir tempDir;
|
||||
string lastDumpName;
|
||||
};
|
||||
|
||||
static void Crasher() {
|
||||
int *a = (int*)0x42;
|
||||
|
||||
fprintf(stdout, "Going to crash...\n");
|
||||
fprintf(stdout, "A = %d", *a);
|
||||
}
|
||||
|
||||
static void SoonToCrash() {
|
||||
Crasher();
|
||||
}
|
||||
|
||||
static bool MDCallback(const char *dump_dir, const char *file_name,
|
||||
void *context, bool success) {
|
||||
string path(dump_dir);
|
||||
path.append("/");
|
||||
path.append(file_name);
|
||||
path.append(".dmp");
|
||||
|
||||
int fd = *reinterpret_cast<int*>(context);
|
||||
(void)write(fd, path.c_str(), path.length() + 1);
|
||||
close(fd);
|
||||
exit(0);
|
||||
// not reached
|
||||
return true;
|
||||
}
|
||||
|
||||
TEST_F(ExceptionHandlerTest, InProcess) {
|
||||
AutoTempDir tempDir;
|
||||
// Give the child process a pipe to report back on.
|
||||
int fds[2];
|
||||
ASSERT_EQ(0, pipe(fds));
|
||||
// Fork off a child process so it can crash.
|
||||
pid_t pid = fork();
|
||||
if (pid == 0) {
|
||||
// In the child process.
|
||||
close(fds[0]);
|
||||
ExceptionHandler eh(tempDir.path, NULL, MDCallback, &fds[1], true, NULL);
|
||||
// crash
|
||||
SoonToCrash();
|
||||
// not reached
|
||||
exit(1);
|
||||
}
|
||||
// In the parent process.
|
||||
ASSERT_NE(-1, pid);
|
||||
// Wait for the background process to return the minidump file.
|
||||
close(fds[1]);
|
||||
char minidump_file[PATH_MAX];
|
||||
ssize_t nbytes = read(fds[0], minidump_file, sizeof(minidump_file));
|
||||
ASSERT_NE(0, nbytes);
|
||||
// Ensure that minidump file exists and is > 0 bytes.
|
||||
struct stat st;
|
||||
ASSERT_EQ(0, stat(minidump_file, &st));
|
||||
ASSERT_LT(0, st.st_size);
|
||||
|
||||
// Child process should have exited with a zero status.
|
||||
int ret;
|
||||
ASSERT_EQ(pid, waitpid(pid, &ret, 0));
|
||||
EXPECT_NE(0, WIFEXITED(ret));
|
||||
EXPECT_EQ(0, WEXITSTATUS(ret));
|
||||
}
|
||||
|
||||
static bool ChildMDCallback(const char *dump_dir, const char *file_name,
|
||||
void *context, bool success) {
|
||||
ExceptionHandlerTest *self = reinterpret_cast<ExceptionHandlerTest*>(context);
|
||||
if (dump_dir && file_name) {
|
||||
self->lastDumpName = dump_dir;
|
||||
self->lastDumpName += "/";
|
||||
self->lastDumpName += file_name;
|
||||
self->lastDumpName += ".dmp";
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
TEST_F(ExceptionHandlerTest, DumpChildProcess) {
|
||||
const int kTimeoutMs = 2000;
|
||||
// Create a mach port to receive the child task on.
|
||||
char machPortName[128];
|
||||
sprintf(machPortName, "ExceptionHandlerTest.%d", getpid());
|
||||
ReceivePort parent_recv_port(machPortName);
|
||||
|
||||
// Give the child process a pipe to block on.
|
||||
int fds[2];
|
||||
ASSERT_EQ(0, pipe(fds));
|
||||
|
||||
// Fork off a child process to dump.
|
||||
pid_t pid = fork();
|
||||
if (pid == 0) {
|
||||
// In the child process
|
||||
close(fds[0]);
|
||||
|
||||
// Send parent process the task and thread ports.
|
||||
MachSendMessage child_message(0);
|
||||
child_message.AddDescriptor(mach_task_self());
|
||||
child_message.AddDescriptor(mach_thread_self());
|
||||
|
||||
MachPortSender child_sender(machPortName);
|
||||
if (child_sender.SendMessage(child_message, kTimeoutMs) != KERN_SUCCESS)
|
||||
exit(1);
|
||||
|
||||
// Wait for the parent process.
|
||||
uint8_t data;
|
||||
read(fds[1], &data, 1);
|
||||
exit(0);
|
||||
}
|
||||
// In the parent process.
|
||||
ASSERT_NE(-1, pid);
|
||||
close(fds[1]);
|
||||
|
||||
// Read the child's task and thread ports.
|
||||
MachReceiveMessage child_message;
|
||||
ASSERT_EQ(KERN_SUCCESS,
|
||||
parent_recv_port.WaitForMessage(&child_message, kTimeoutMs));
|
||||
mach_port_t child_task = child_message.GetTranslatedPort(0);
|
||||
mach_port_t child_thread = child_message.GetTranslatedPort(1);
|
||||
ASSERT_NE(MACH_PORT_NULL, child_task);
|
||||
ASSERT_NE(MACH_PORT_NULL, child_thread);
|
||||
|
||||
// Write a minidump of the child process.
|
||||
bool result = ExceptionHandler::WriteMinidumpForChild(child_task,
|
||||
child_thread,
|
||||
tempDir.path,
|
||||
ChildMDCallback,
|
||||
this);
|
||||
ASSERT_EQ(true, result);
|
||||
|
||||
// Ensure that minidump file exists and is > 0 bytes.
|
||||
ASSERT_FALSE(lastDumpName.empty());
|
||||
struct stat st;
|
||||
ASSERT_EQ(0, stat(lastDumpName.c_str(), &st));
|
||||
ASSERT_LT(0, st.st_size);
|
||||
|
||||
// Unblock child process
|
||||
uint8_t data = 1;
|
||||
(void)write(fds[0], &data, 1);
|
||||
|
||||
// Child process should have exited with a zero status.
|
||||
int ret;
|
||||
ASSERT_EQ(pid, waitpid(pid, &ret, 0));
|
||||
EXPECT_NE(0, WIFEXITED(ret));
|
||||
EXPECT_EQ(0, WEXITSTATUS(ret));
|
||||
}
|
||||
|
||||
}
|
|
@ -61,27 +61,30 @@ inline bool TypedMDRVA<MDType>::AllocateArray(size_t count) {
|
|||
}
|
||||
|
||||
template<typename MDType>
|
||||
inline bool TypedMDRVA<MDType>::AllocateObjectAndArray(unsigned int count,
|
||||
size_t size) {
|
||||
assert(count && size);
|
||||
inline bool TypedMDRVA<MDType>::AllocateObjectAndArray(size_t count,
|
||||
size_t length) {
|
||||
assert(count && length);
|
||||
allocation_state_ = SINGLE_OBJECT_WITH_ARRAY;
|
||||
return UntypedMDRVA::Allocate(minidump_size<MDType>::size() + count * size);
|
||||
return UntypedMDRVA::Allocate(minidump_size<MDType>::size() + count * length);
|
||||
}
|
||||
|
||||
template<typename MDType>
|
||||
inline bool TypedMDRVA<MDType>::CopyIndex(unsigned int index, MDType *item) {
|
||||
assert(allocation_state_ == ARRAY);
|
||||
return writer_->Copy(position_ + index * minidump_size<MDType>::size(), item,
|
||||
minidump_size<MDType>::size());
|
||||
return writer_->Copy(
|
||||
static_cast<MDRVA>(position_ + index * minidump_size<MDType>::size()),
|
||||
item, minidump_size<MDType>::size());
|
||||
}
|
||||
|
||||
template<typename MDType>
|
||||
inline bool TypedMDRVA<MDType>::CopyIndexAfterObject(unsigned int index,
|
||||
const void *src,
|
||||
size_t size) {
|
||||
size_t length) {
|
||||
assert(allocation_state_ == SINGLE_OBJECT_WITH_ARRAY);
|
||||
return writer_->Copy(position_ + minidump_size<MDType>::size() + index * size,
|
||||
src, size);
|
||||
return writer_->Copy(
|
||||
static_cast<MDRVA>(position_ + minidump_size<MDType>::size()
|
||||
+ index * length),
|
||||
src, length);
|
||||
}
|
||||
|
||||
template<typename MDType>
|
||||
|
|
|
@ -107,7 +107,7 @@ bool MinidumpFileWriter::CopyStringToMDString(const wchar_t *str,
|
|||
// zero, but the second one may be zero, depending on the conversion from
|
||||
// UTF-32.
|
||||
int out_count = out[1] ? 2 : 1;
|
||||
int out_size = sizeof(u_int16_t) * out_count;
|
||||
size_t out_size = sizeof(u_int16_t) * out_count;
|
||||
result = mdstring->CopyIndexAfterObject(out_idx, out, out_size);
|
||||
out_idx += out_count;
|
||||
}
|
||||
|
@ -134,7 +134,7 @@ bool MinidumpFileWriter::CopyStringToMDString(const char *str,
|
|||
|
||||
// Append the one or two UTF-16 characters
|
||||
int out_count = out[1] ? 2 : 1;
|
||||
int out_size = sizeof(u_int16_t) * out_count;
|
||||
size_t out_size = sizeof(u_int16_t) * out_count;
|
||||
result = mdstring->CopyIndexAfterObject(out_idx, out, out_size);
|
||||
out_idx += out_count;
|
||||
}
|
||||
|
@ -161,7 +161,8 @@ bool MinidumpFileWriter::WriteStringCore(const CharType *str,
|
|||
return false;
|
||||
|
||||
// Set length excluding the NULL and copy the string
|
||||
mdstring.get()->length = mdstring_length * sizeof(u_int16_t);
|
||||
mdstring.get()->length =
|
||||
static_cast<u_int32_t>(mdstring_length * sizeof(u_int16_t));
|
||||
bool result = CopyStringToMDString(str, mdstring_length, &mdstring);
|
||||
|
||||
// NULL terminate
|
||||
|
@ -235,7 +236,7 @@ bool MinidumpFileWriter::Copy(MDRVA position, const void *src, ssize_t size) {
|
|||
assert(file_ != -1);
|
||||
|
||||
// Ensure that the data will fit in the allocated space
|
||||
if (size + position > size_)
|
||||
if (static_cast<size_t>(size + position) > size_)
|
||||
return false;
|
||||
|
||||
// Seek and write the data
|
||||
|
@ -260,11 +261,11 @@ bool UntypedMDRVA::Allocate(size_t size) {
|
|||
return position_ != MinidumpFileWriter::kInvalidMDRVA;
|
||||
}
|
||||
|
||||
bool UntypedMDRVA::Copy(MDRVA position, const void *src, size_t size) {
|
||||
bool UntypedMDRVA::Copy(MDRVA pos, const void *src, size_t size) {
|
||||
assert(src);
|
||||
assert(size);
|
||||
assert(position + size <= position_ + size_);
|
||||
return writer_->Copy(position, src, size);
|
||||
assert(pos + size <= position_ + size_);
|
||||
return writer_->Copy(pos, src, size);
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
|
|
@ -151,7 +151,7 @@ class UntypedMDRVA {
|
|||
|
||||
// Return size and position
|
||||
inline MDLocationDescriptor location() const {
|
||||
MDLocationDescriptor location = { size_, position_ };
|
||||
MDLocationDescriptor location = { static_cast<int>(size_), position_ };
|
||||
return location;
|
||||
}
|
||||
|
||||
|
@ -218,7 +218,7 @@ class TypedMDRVA : public UntypedMDRVA {
|
|||
// Allocate an array of |count| elements of |size| after object of MDType
|
||||
// Must not call more than once.
|
||||
// Return true on success, or false on failure
|
||||
bool AllocateObjectAndArray(unsigned int count, size_t size);
|
||||
bool AllocateObjectAndArray(size_t count, size_t size);
|
||||
|
||||
// Copy |item| to |index|
|
||||
// Must have been allocated using AllocateArray().
|
||||
|
|
|
@ -144,22 +144,23 @@ static bool CompareFile(const char *path) {
|
|||
0x0000000a, 0x000a1c09, 0x0000000b, 0x00000000,
|
||||
#endif
|
||||
};
|
||||
unsigned int expected_byte_count = sizeof(expected);
|
||||
size_t expected_byte_count = sizeof(expected);
|
||||
int fd = open(path, O_RDONLY, 0600);
|
||||
void *buffer = malloc(expected_byte_count);
|
||||
ASSERT_NE(fd, -1);
|
||||
ASSERT_TRUE(buffer);
|
||||
ASSERT_EQ(read(fd, buffer, expected_byte_count), expected_byte_count);
|
||||
ASSERT_EQ(read(fd, buffer, expected_byte_count),
|
||||
static_cast<ssize_t>(expected_byte_count));
|
||||
|
||||
char *b1, *b2;
|
||||
b1 = (char*)buffer;
|
||||
b2 = (char*)expected;
|
||||
b1 = reinterpret_cast<char*>(buffer);
|
||||
b2 = reinterpret_cast<char*>(expected);
|
||||
while (*b1 == *b2) {
|
||||
b1++;
|
||||
b2++;
|
||||
}
|
||||
|
||||
printf("%d\n",b1 - (char*)buffer);
|
||||
printf("%p\n", reinterpret_cast<void*>(b1 - (char*)buffer));
|
||||
|
||||
ASSERT_EQ(memcmp(buffer, expected, expected_byte_count), 0);
|
||||
return true;
|
||||
|
|
|
@ -125,7 +125,7 @@ uint64 ByteReader::ReadEncodedPointer(const char *buffer,
|
|||
// address.
|
||||
size_t skew = section_base_ & (AddressSize() - 1);
|
||||
// Now find the offset from that aligned address to buffer.
|
||||
size_t offset = skew + (buffer - buffer_base_);
|
||||
off_t offset = skew + (buffer - buffer_base_);
|
||||
// Round up to the next boundary.
|
||||
size_t aligned = (offset + AddressSize() - 1) & -AddressSize();
|
||||
// Convert back to a pointer.
|
||||
|
|
|
@ -301,7 +301,7 @@ class ByteReader {
|
|||
// Base addresses for Linux C++ exception handling data's encoded pointers.
|
||||
bool have_section_base_, have_text_base_, have_data_base_;
|
||||
bool have_function_base_;
|
||||
uint64 section_base_, text_base_, data_base_, function_base_;
|
||||
size_t section_base_, text_base_, data_base_, function_base_;
|
||||
const char *buffer_base_;
|
||||
};
|
||||
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
#include "common/dwarf/dwarf2reader.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
|
@ -90,7 +91,7 @@ void CompilationUnit::ReadAbbrevs() {
|
|||
while (1) {
|
||||
CompilationUnit::Abbrev abbrev;
|
||||
size_t len;
|
||||
const uint32 number = reader_->ReadUnsignedLEB128(abbrevptr, &len);
|
||||
const uint64 number = reader_->ReadUnsignedLEB128(abbrevptr, &len);
|
||||
|
||||
if (number == 0)
|
||||
break;
|
||||
|
@ -98,7 +99,7 @@ void CompilationUnit::ReadAbbrevs() {
|
|||
abbrevptr += len;
|
||||
|
||||
assert(abbrevptr < abbrev_start + abbrev_length);
|
||||
const uint32 tag = reader_->ReadUnsignedLEB128(abbrevptr, &len);
|
||||
const uint64 tag = reader_->ReadUnsignedLEB128(abbrevptr, &len);
|
||||
abbrevptr += len;
|
||||
abbrev.tag = static_cast<enum DwarfTag>(tag);
|
||||
|
||||
|
@ -109,11 +110,11 @@ void CompilationUnit::ReadAbbrevs() {
|
|||
assert(abbrevptr < abbrev_start + abbrev_length);
|
||||
|
||||
while (1) {
|
||||
const uint32 nametemp = reader_->ReadUnsignedLEB128(abbrevptr, &len);
|
||||
const uint64 nametemp = reader_->ReadUnsignedLEB128(abbrevptr, &len);
|
||||
abbrevptr += len;
|
||||
|
||||
assert(abbrevptr < abbrev_start + abbrev_length);
|
||||
const uint32 formtemp = reader_->ReadUnsignedLEB128(abbrevptr, &len);
|
||||
const uint64 formtemp = reader_->ReadUnsignedLEB128(abbrevptr, &len);
|
||||
abbrevptr += len;
|
||||
if (nametemp == 0 && formtemp == 0)
|
||||
break;
|
||||
|
@ -515,7 +516,7 @@ void CompilationUnit::ProcessDIEs() {
|
|||
continue;
|
||||
}
|
||||
|
||||
const Abbrev& abbrev = abbrevs_->at(abbrev_num);
|
||||
const Abbrev& abbrev = abbrevs_->at(static_cast<size_t>(abbrev_num));
|
||||
const enum DwarfTag tag = abbrev.tag;
|
||||
if (!handler_->StartDIE(absolute_offset, tag, abbrev.attributes)) {
|
||||
dieptr = SkipDIE(dieptr, abbrev);
|
||||
|
@ -618,8 +619,8 @@ void LineInfo::ReadHeader() {
|
|||
|
||||
uint64 filelength = reader_->ReadUnsignedLEB128(lineptr, &len);
|
||||
lineptr += len;
|
||||
handler_->DefineFile(filename, fileindex, dirindex, mod_time,
|
||||
filelength);
|
||||
handler_->DefineFile(filename, fileindex, static_cast<uint32>(dirindex),
|
||||
mod_time, filelength);
|
||||
fileindex++;
|
||||
}
|
||||
}
|
||||
|
@ -649,7 +650,7 @@ bool LineInfo::ProcessOneOpcode(ByteReader* reader,
|
|||
opcode -= header.opcode_base;
|
||||
const int64 advance_address = (opcode / header.line_range)
|
||||
* header.min_insn_length;
|
||||
const int64 advance_line = (opcode % header.line_range)
|
||||
const int32 advance_line = (opcode % header.line_range)
|
||||
+ header.line_base;
|
||||
|
||||
// Check if the lsm passes "pc". If so, mark it as passed.
|
||||
|
@ -689,7 +690,7 @@ bool LineInfo::ProcessOneOpcode(ByteReader* reader,
|
|||
case DW_LNS_advance_line: {
|
||||
const int64 advance_line = reader->ReadSignedLEB128(start, &templen);
|
||||
oplen += templen;
|
||||
lsm->line_num += advance_line;
|
||||
lsm->line_num += static_cast<int32>(advance_line);
|
||||
|
||||
// With gcc 4.2.1, we can get the line_no here for the first time
|
||||
// since DW_LNS_advance_line is called after DW_LNE_set_address is
|
||||
|
@ -703,13 +704,13 @@ bool LineInfo::ProcessOneOpcode(ByteReader* reader,
|
|||
case DW_LNS_set_file: {
|
||||
const uint64 fileno = reader->ReadUnsignedLEB128(start, &templen);
|
||||
oplen += templen;
|
||||
lsm->file_num = fileno;
|
||||
lsm->file_num = static_cast<uint32>(fileno);
|
||||
}
|
||||
break;
|
||||
case DW_LNS_set_column: {
|
||||
const uint64 colno = reader->ReadUnsignedLEB128(start, &templen);
|
||||
oplen += templen;
|
||||
lsm->column_num = colno;
|
||||
lsm->column_num = static_cast<uint32>(colno);
|
||||
}
|
||||
break;
|
||||
case DW_LNS_negate_stmt: {
|
||||
|
@ -748,7 +749,7 @@ bool LineInfo::ProcessOneOpcode(ByteReader* reader,
|
|||
}
|
||||
break;
|
||||
case DW_LNS_extended_op: {
|
||||
const size_t extended_op_len = reader->ReadUnsignedLEB128(start,
|
||||
const uint64 extended_op_len = reader->ReadUnsignedLEB128(start,
|
||||
&templen);
|
||||
start += templen;
|
||||
oplen += templen + extended_op_len;
|
||||
|
@ -790,8 +791,8 @@ bool LineInfo::ProcessOneOpcode(ByteReader* reader,
|
|||
oplen += templen;
|
||||
|
||||
if (handler) {
|
||||
handler->DefineFile(filename, -1, dirindex, mod_time,
|
||||
filelength);
|
||||
handler->DefineFile(filename, -1, static_cast<uint32>(dirindex),
|
||||
mod_time, filelength);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
@ -803,7 +804,6 @@ bool LineInfo::ProcessOneOpcode(ByteReader* reader,
|
|||
// Ignore unknown opcode silently
|
||||
if (header.std_opcode_lengths) {
|
||||
for (int i = 0; i < (*header.std_opcode_lengths)[opcode]; i++) {
|
||||
size_t templen;
|
||||
reader->ReadUnsignedLEB128(start, &templen);
|
||||
start += templen;
|
||||
oplen += templen;
|
||||
|
@ -1940,7 +1940,7 @@ bool CallFrameInfo::ReadCIEFields(CIE *cie) {
|
|||
// If we have a 'z' augmentation string, find the augmentation data and
|
||||
// use the augmentation string to parse it.
|
||||
if (cie->has_z_augmentation) {
|
||||
size_t data_size = reader_->ReadUnsignedLEB128(cursor, &len);
|
||||
uint64_t data_size = reader_->ReadUnsignedLEB128(cursor, &len);
|
||||
if (size_t(cie->end - cursor) < len + data_size)
|
||||
return ReportIncomplete(cie);
|
||||
cursor += len;
|
||||
|
@ -2060,7 +2060,7 @@ bool CallFrameInfo::ReadFDEFields(FDE *fde) {
|
|||
// If the CIE has a 'z' augmentation string, then augmentation data
|
||||
// appears here.
|
||||
if (fde->cie->has_z_augmentation) {
|
||||
size_t data_size = reader_->ReadUnsignedLEB128(cursor, &size);
|
||||
uint64_t data_size = reader_->ReadUnsignedLEB128(cursor, &size);
|
||||
if (size_t(fde->end - cursor) < size + data_size)
|
||||
return ReportIncomplete(fde);
|
||||
cursor += size;
|
||||
|
|
|
@ -242,7 +242,7 @@ class CompilationUnit {
|
|||
// The abbreviation tells how to read a DWARF2/3 DIE, and consist of a
|
||||
// tag and a list of attributes, as well as the data form of each attribute.
|
||||
struct Abbrev {
|
||||
uint32 number;
|
||||
uint64 number;
|
||||
enum DwarfTag tag;
|
||||
bool has_children;
|
||||
AttributeList attributes;
|
||||
|
|
|
@ -48,7 +48,7 @@ struct LineStateMachine {
|
|||
|
||||
uint32 file_num;
|
||||
uint64 address;
|
||||
uint64 line_num;
|
||||
uint32 line_num;
|
||||
uint32 column_num;
|
||||
bool is_stmt; // stmt means statement.
|
||||
bool basic_block;
|
||||
|
|
|
@ -117,7 +117,7 @@ bool DwarfCFIToModule::Entry(size_t offset, uint64 address, uint64 length,
|
|||
// address on entry to the function. So establish an initial .ra
|
||||
// rule citing the return address register.
|
||||
if (return_address_ < register_names_.size())
|
||||
entry_->initial_rules[".ra"] = register_names_[return_address_];
|
||||
entry_->initial_rules[ra_name_] = register_names_[return_address_];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -126,11 +126,11 @@ string DwarfCFIToModule::RegisterName(int i) {
|
|||
assert(entry_);
|
||||
if (i < 0) {
|
||||
assert(i == kCFARegister);
|
||||
return ".cfa";
|
||||
return cfa_name_;
|
||||
}
|
||||
unsigned reg = i;
|
||||
if (reg == return_address_)
|
||||
return ".ra";
|
||||
return ra_name_;
|
||||
|
||||
if (0 <= reg && reg < register_names_.size())
|
||||
return register_names_[reg];
|
||||
|
@ -144,12 +144,21 @@ string DwarfCFIToModule::RegisterName(int i) {
|
|||
void DwarfCFIToModule::Record(Module::Address address, int reg,
|
||||
const string &rule) {
|
||||
assert(entry_);
|
||||
|
||||
// Place the name in our global set of strings, and then use the string
|
||||
// from the set. Even though the assignment looks like a copy, all the
|
||||
// major std::string implementations use reference counting internally,
|
||||
// so the effect is to have all our data structures share copies of rules
|
||||
// whenever possible. Since register names are drawn from a
|
||||
// vector<string>, register names are already shared.
|
||||
string shared_rule = *common_strings_.insert(rule).first;
|
||||
|
||||
// Is this one of this entry's initial rules?
|
||||
if (address == entry_->address)
|
||||
entry_->initial_rules[RegisterName(reg)] = rule;
|
||||
entry_->initial_rules[RegisterName(reg)] = shared_rule;
|
||||
// File it under the appropriate address.
|
||||
else
|
||||
entry_->rule_changes[address][RegisterName(reg)] = rule;
|
||||
entry_->rule_changes[address][RegisterName(reg)] = shared_rule;
|
||||
}
|
||||
|
||||
bool DwarfCFIToModule::UndefinedRule(uint64 address, int reg) {
|
||||
|
|
|
@ -40,7 +40,9 @@
|
|||
#define COMMON_LINUX_DWARF_CFI_TO_MODULE_H
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
|
@ -51,6 +53,7 @@ namespace google_breakpad {
|
|||
|
||||
using dwarf2reader::CallFrameInfo;
|
||||
using google_breakpad::Module;
|
||||
using std::set;
|
||||
using std::string;
|
||||
using std::vector;
|
||||
|
||||
|
@ -124,7 +127,8 @@ class DwarfCFIToModule: public CallFrameInfo::Handler {
|
|||
DwarfCFIToModule(Module *module, const vector<string> ®ister_names,
|
||||
Reporter *reporter)
|
||||
: module_(module), register_names_(register_names), reporter_(reporter),
|
||||
entry_(NULL), return_address_(-1) { }
|
||||
entry_(NULL), return_address_(-1), cfa_name_(".cfa"), ra_name_(".ra") {
|
||||
}
|
||||
virtual ~DwarfCFIToModule() { delete entry_; }
|
||||
|
||||
virtual bool Entry(size_t offset, uint64 address, uint64 length,
|
||||
|
@ -168,6 +172,23 @@ class DwarfCFIToModule: public CallFrameInfo::Handler {
|
|||
|
||||
// The return address column for that entry.
|
||||
unsigned return_address_;
|
||||
|
||||
// The names of the return address and canonical frame address. Putting
|
||||
// these here instead of using string literals allows us to share their
|
||||
// texts in reference-counted std::string implementations (all the
|
||||
// popular ones). Many, many rules cite these strings.
|
||||
string cfa_name_, ra_name_;
|
||||
|
||||
// A set of strings used by this CFI. Before storing a string in one of
|
||||
// our data structures, insert it into this set, and then use the string
|
||||
// from the set.
|
||||
//
|
||||
// Because std::string uses reference counting internally, simply using
|
||||
// strings from this set, even if passed by value, assigned, or held
|
||||
// directly in structures and containers (map<string, ...>, for example),
|
||||
// causes those strings to share a single instance of each distinct piece
|
||||
// of text.
|
||||
set<string> common_strings_;
|
||||
};
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
|
|
@ -36,12 +36,16 @@
|
|||
#include <assert.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <set>
|
||||
#include <utility>
|
||||
|
||||
#include "common/dwarf_line_to_module.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
using std::map;
|
||||
using std::pair;
|
||||
using std::set;
|
||||
using std::vector;
|
||||
|
||||
// Data provided by a DWARF specification DIE.
|
||||
|
@ -83,6 +87,17 @@ typedef map<uint64, AbstractOrigin> AbstractOriginByOffset;
|
|||
// Data global to the DWARF-bearing file that is private to the
|
||||
// DWARF-to-Module process.
|
||||
struct DwarfCUToModule::FilePrivate {
|
||||
// A set of strings used in this CU. Before storing a string in one of
|
||||
// our data structures, insert it into this set, and then use the string
|
||||
// from the set.
|
||||
//
|
||||
// Because std::string uses reference counting internally, simply using
|
||||
// strings from this set, even if passed by value, assigned, or held
|
||||
// directly in structures and containers (map<string, ...>, for example),
|
||||
// causes those strings to share a single instance of each distinct piece
|
||||
// of text.
|
||||
set<string> common_strings;
|
||||
|
||||
// A map from offsets of DIEs within the .debug_info section to
|
||||
// Specifications describing those DIEs. Specification references can
|
||||
// cross compilation unit boundaries.
|
||||
|
@ -256,7 +271,17 @@ void DwarfCUToModule::GenericDIEHandler::ProcessAttributeString(
|
|||
enum DwarfForm form,
|
||||
const string &data) {
|
||||
switch (attr) {
|
||||
case dwarf2reader::DW_AT_name: name_attribute_ = data; break;
|
||||
case dwarf2reader::DW_AT_name: {
|
||||
// Place the name in our global set of strings, and then use the
|
||||
// string from the set. Even though the assignment looks like a copy,
|
||||
// all the major std::string implementations use reference counting
|
||||
// internally, so the effect is to have all our data structures share
|
||||
// copies of strings whenever possible.
|
||||
pair<set<string>::iterator, bool> result =
|
||||
cu_context_->file_context->file_private->common_strings.insert(data);
|
||||
name_attribute_ = *result.first;
|
||||
break;
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -46,7 +46,10 @@
|
|||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "common/dwarf/bytereader-inl.h"
|
||||
#include "common/dwarf/dwarf2diehandler.h"
|
||||
|
@ -67,6 +70,64 @@ using google_breakpad::DwarfLineToModule;
|
|||
using google_breakpad::Module;
|
||||
using google_breakpad::StabsToModule;
|
||||
|
||||
//
|
||||
// FDWrapper
|
||||
//
|
||||
// Wrapper class to make sure opened file is closed.
|
||||
//
|
||||
class FDWrapper {
|
||||
public:
|
||||
explicit FDWrapper(int fd) :
|
||||
fd_(fd) {}
|
||||
~FDWrapper() {
|
||||
if (fd_ != -1)
|
||||
close(fd_);
|
||||
}
|
||||
int get() {
|
||||
return fd_;
|
||||
}
|
||||
int release() {
|
||||
int fd = fd_;
|
||||
fd_ = -1;
|
||||
return fd;
|
||||
}
|
||||
private:
|
||||
int fd_;
|
||||
};
|
||||
|
||||
//
|
||||
// MmapWrapper
|
||||
//
|
||||
// Wrapper class to make sure mapped regions are unmapped.
|
||||
//
|
||||
class MmapWrapper {
|
||||
public:
|
||||
MmapWrapper() : is_set_(false) {}
|
||||
~MmapWrapper() {
|
||||
assert(is_set_);
|
||||
if (base_ != NULL) {
|
||||
assert(size_ > 0);
|
||||
munmap(base_, size_);
|
||||
}
|
||||
}
|
||||
void set(void *mapped_address, size_t mapped_size) {
|
||||
is_set_ = true;
|
||||
base_ = mapped_address;
|
||||
size_ = mapped_size;
|
||||
}
|
||||
void release() {
|
||||
assert(is_set_);
|
||||
base_ = NULL;
|
||||
size_ = 0;
|
||||
}
|
||||
|
||||
private:
|
||||
bool is_set_;
|
||||
void *base_;
|
||||
size_t size_;
|
||||
};
|
||||
|
||||
|
||||
// Fix offset into virtual address by adding the mapped base into offsets.
|
||||
// Make life easier when want to find something by offset.
|
||||
static void FixAddress(void *obj_base) {
|
||||
|
@ -118,27 +179,24 @@ static const ElfW(Shdr) *FindSectionByName(const char *name,
|
|||
const char *section_name =
|
||||
reinterpret_cast<char*>(section_names->sh_offset + sections[i].sh_name);
|
||||
if (names_end - section_name >= name_len + 1 &&
|
||||
strcmp(name, section_name) == 0)
|
||||
strcmp(name, section_name) == 0) {
|
||||
if (sections[i].sh_type == SHT_NOBITS) {
|
||||
fprintf(stderr,
|
||||
"Section %s found, but ignored because type=SHT_NOBITS.\n",
|
||||
name);
|
||||
return NULL;
|
||||
}
|
||||
return sections + i;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool LoadStabs(const ElfW(Ehdr) *elf_header,
|
||||
const ElfW(Shdr) *stab_section,
|
||||
const ElfW(Shdr) *stabstr_section,
|
||||
const bool big_endian,
|
||||
Module *module) {
|
||||
// Figure out what endianness this file is.
|
||||
bool big_endian;
|
||||
if (elf_header->e_ident[EI_DATA] == ELFDATA2LSB)
|
||||
big_endian = false;
|
||||
else if (elf_header->e_ident[EI_DATA] == ELFDATA2MSB)
|
||||
big_endian = true;
|
||||
else {
|
||||
fprintf(stderr, "bad data encoding in ELF header: %d\n",
|
||||
elf_header->e_ident[EI_DATA]);
|
||||
return false;
|
||||
}
|
||||
// A callback object to handle data from the STABS reader.
|
||||
StabsToModule handler(module);
|
||||
// Find the addresses of the STABS data, and create a STABS reader object.
|
||||
|
@ -163,7 +221,7 @@ static bool LoadStabs(const ElfW(Ehdr) *elf_header,
|
|||
class DumperLineToModule: public DwarfCUToModule::LineToModuleFunctor {
|
||||
public:
|
||||
// Create a line-to-module converter using BYTE_READER.
|
||||
DumperLineToModule(dwarf2reader::ByteReader *byte_reader)
|
||||
explicit DumperLineToModule(dwarf2reader::ByteReader *byte_reader)
|
||||
: byte_reader_(byte_reader) { }
|
||||
void operator()(const char *program, uint64 length,
|
||||
Module *module, vector<Module::Line> *lines) {
|
||||
|
@ -177,18 +235,10 @@ class DumperLineToModule: public DwarfCUToModule::LineToModuleFunctor {
|
|||
|
||||
static bool LoadDwarf(const string &dwarf_filename,
|
||||
const ElfW(Ehdr) *elf_header,
|
||||
const bool big_endian,
|
||||
Module *module) {
|
||||
// Figure out what endianness this file is.
|
||||
dwarf2reader::Endianness endianness;
|
||||
if (elf_header->e_ident[EI_DATA] == ELFDATA2LSB)
|
||||
endianness = dwarf2reader::ENDIANNESS_LITTLE;
|
||||
else if (elf_header->e_ident[EI_DATA] == ELFDATA2MSB)
|
||||
endianness = dwarf2reader::ENDIANNESS_BIG;
|
||||
else {
|
||||
fprintf(stderr, "%s: bad data encoding in ELF header: %d\n",
|
||||
dwarf_filename.c_str(), elf_header->e_ident[EI_DATA]);
|
||||
return false;
|
||||
}
|
||||
const dwarf2reader::Endianness endianness = big_endian ?
|
||||
dwarf2reader::ENDIANNESS_BIG : dwarf2reader::ENDIANNESS_LITTLE;
|
||||
dwarf2reader::ByteReader byte_reader(endianness);
|
||||
|
||||
// Construct a context for this file.
|
||||
|
@ -260,9 +310,10 @@ static bool LoadDwarfCFI(const string &dwarf_filename,
|
|||
const ElfW(Ehdr) *elf_header,
|
||||
const char *section_name,
|
||||
const ElfW(Shdr) *section,
|
||||
bool eh_frame,
|
||||
const bool eh_frame,
|
||||
const ElfW(Shdr) *got_section,
|
||||
const ElfW(Shdr) *text_section,
|
||||
const bool big_endian,
|
||||
Module *module) {
|
||||
// Find the appropriate set of register names for this file's
|
||||
// architecture.
|
||||
|
@ -274,17 +325,8 @@ static bool LoadDwarfCFI(const string &dwarf_filename,
|
|||
return false;
|
||||
}
|
||||
|
||||
// Figure out what endianness this file is.
|
||||
dwarf2reader::Endianness endianness;
|
||||
if (elf_header->e_ident[EI_DATA] == ELFDATA2LSB)
|
||||
endianness = dwarf2reader::ENDIANNESS_LITTLE;
|
||||
else if (elf_header->e_ident[EI_DATA] == ELFDATA2MSB)
|
||||
endianness = dwarf2reader::ENDIANNESS_BIG;
|
||||
else {
|
||||
fprintf(stderr, "%s: bad data encoding in ELF header: %d\n",
|
||||
dwarf_filename.c_str(), elf_header->e_ident[EI_DATA]);
|
||||
return false;
|
||||
}
|
||||
const dwarf2reader::Endianness endianness = big_endian ?
|
||||
dwarf2reader::ENDIANNESS_BIG : dwarf2reader::ENDIANNESS_LITTLE;
|
||||
|
||||
// Find the call frame information and its size.
|
||||
const char *cfi = reinterpret_cast<const char *>(section->sh_offset);
|
||||
|
@ -323,7 +365,159 @@ static bool LoadDwarfCFI(const string &dwarf_filename,
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool LoadSymbols(const std::string &obj_file, ElfW(Ehdr) *elf_header,
|
||||
bool LoadELF(const std::string &obj_file, MmapWrapper* map_wrapper,
|
||||
ElfW(Ehdr) **elf_header) {
|
||||
int obj_fd = open(obj_file.c_str(), O_RDONLY);
|
||||
if (obj_fd < 0) {
|
||||
fprintf(stderr, "Failed to open ELF file '%s': %s\n",
|
||||
obj_file.c_str(), strerror(errno));
|
||||
return false;
|
||||
}
|
||||
FDWrapper obj_fd_wrapper(obj_fd);
|
||||
struct stat st;
|
||||
if (fstat(obj_fd, &st) != 0 && st.st_size <= 0) {
|
||||
fprintf(stderr, "Unable to fstat ELF file '%s': %s\n",
|
||||
obj_file.c_str(), strerror(errno));
|
||||
return false;
|
||||
}
|
||||
void *obj_base = mmap(NULL, st.st_size,
|
||||
PROT_READ | PROT_WRITE, MAP_PRIVATE, obj_fd, 0);
|
||||
if (obj_base == MAP_FAILED) {
|
||||
fprintf(stderr, "Failed to mmap ELF file '%s': %s\n",
|
||||
obj_file.c_str(), strerror(errno));
|
||||
return false;
|
||||
}
|
||||
map_wrapper->set(obj_base, st.st_size);
|
||||
*elf_header = reinterpret_cast<ElfW(Ehdr) *>(obj_base);
|
||||
if (!IsValidElf(*elf_header)) {
|
||||
fprintf(stderr, "Not a valid ELF file: %s\n", obj_file.c_str());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Get the endianness of ELF_HEADER. If it's invalid, return false.
|
||||
bool ElfEndianness(const ElfW(Ehdr) *elf_header, bool *big_endian) {
|
||||
if (elf_header->e_ident[EI_DATA] == ELFDATA2LSB) {
|
||||
*big_endian = false;
|
||||
return true;
|
||||
}
|
||||
if (elf_header->e_ident[EI_DATA] == ELFDATA2MSB) {
|
||||
*big_endian = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
fprintf(stderr, "bad data encoding in ELF header: %d\n",
|
||||
elf_header->e_ident[EI_DATA]);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read the .gnu_debuglink and get the debug file name. If anything goes
|
||||
// wrong, return an empty string.
|
||||
static std::string ReadDebugLink(const ElfW(Shdr) *debuglink_section,
|
||||
const std::string &obj_file,
|
||||
const std::string &debug_dir) {
|
||||
char *debuglink = reinterpret_cast<char *>(debuglink_section->sh_offset);
|
||||
size_t debuglink_len = strlen(debuglink) + 5; // '\0' + CRC32.
|
||||
debuglink_len = 4 * ((debuglink_len + 3) / 4); // Round to nearest 4 bytes.
|
||||
|
||||
// Sanity check.
|
||||
if (debuglink_len != debuglink_section->sh_size) {
|
||||
fprintf(stderr, "Mismatched .gnu_debuglink string / section size: "
|
||||
"%zx %zx\n", debuglink_len, debuglink_section->sh_size);
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string debuglink_path = debug_dir + "/" + debuglink;
|
||||
int debuglink_fd = open(debuglink_path.c_str(), O_RDONLY);
|
||||
if (debuglink_fd < 0) {
|
||||
fprintf(stderr, "Failed to open debug ELF file '%s' for '%s': %s\n",
|
||||
debuglink_path.c_str(), obj_file.c_str(), strerror(errno));
|
||||
return "";
|
||||
}
|
||||
FDWrapper debuglink_fd_wrapper(debuglink_fd);
|
||||
// TODO(thestig) check the CRC-32 at the end of the .gnu_debuglink
|
||||
// section.
|
||||
|
||||
return debuglink_path;
|
||||
}
|
||||
|
||||
//
|
||||
// LoadSymbolsInfo
|
||||
//
|
||||
// Holds the state between the two calls to LoadSymbols() in case we have to
|
||||
// follow the .gnu_debuglink section and load debug information from a
|
||||
// different file.
|
||||
//
|
||||
class LoadSymbolsInfo {
|
||||
public:
|
||||
explicit LoadSymbolsInfo(const std::string &dbg_dir) :
|
||||
debug_dir_(dbg_dir),
|
||||
has_loading_addr_(false) {}
|
||||
|
||||
// Keeps track of which sections have been loaded so we don't accidentally
|
||||
// load it twice from two different files.
|
||||
void LoadedSection(const std::string §ion) {
|
||||
if (loaded_sections_.count(section) == 0) {
|
||||
loaded_sections_.insert(section);
|
||||
} else {
|
||||
fprintf(stderr, "Section %s has already been loaded.\n",
|
||||
section.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
// We expect the ELF file and linked debug file to have the same prefered
|
||||
// loading address.
|
||||
void set_loading_addr(ElfW(Addr) addr, const std::string &filename) {
|
||||
if (!has_loading_addr_) {
|
||||
loading_addr_ = addr;
|
||||
loaded_file_ = filename;
|
||||
return;
|
||||
}
|
||||
|
||||
if (addr != loading_addr_) {
|
||||
fprintf(stderr,
|
||||
"ELF file '%s' and debug ELF file '%s' "
|
||||
"have different load addresses.\n",
|
||||
loaded_file_.c_str(), filename.c_str());
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
// Setters and getters
|
||||
const std::string &debug_dir() const {
|
||||
return debug_dir_;
|
||||
}
|
||||
|
||||
std::string debuglink_file() const {
|
||||
return debuglink_file_;
|
||||
}
|
||||
void set_debuglink_file(std::string file) {
|
||||
debuglink_file_ = file;
|
||||
}
|
||||
|
||||
private:
|
||||
const std::string &debug_dir_; // Directory with the debug ELF file.
|
||||
|
||||
std::string debuglink_file_; // Full path to the debug ELF file.
|
||||
|
||||
bool has_loading_addr_; // Indicate if LOADING_ADDR_ is valid.
|
||||
|
||||
ElfW(Addr) loading_addr_; // Saves the prefered loading address from the
|
||||
// first call to LoadSymbols().
|
||||
|
||||
std::string loaded_file_; // Name of the file loaded from the first call to
|
||||
// LoadSymbols().
|
||||
|
||||
std::set<std::string> loaded_sections_; // Tracks the Loaded ELF sections
|
||||
// between calls to LoadSymbols().
|
||||
};
|
||||
|
||||
static bool LoadSymbols(const std::string &obj_file,
|
||||
const bool big_endian,
|
||||
ElfW(Ehdr) *elf_header,
|
||||
const bool read_gnu_debug_link,
|
||||
LoadSymbolsInfo *info,
|
||||
Module *module) {
|
||||
// Translate all offsets in section headers into address.
|
||||
FixAddress(elf_header);
|
||||
|
@ -331,6 +525,7 @@ static bool LoadSymbols(const std::string &obj_file, ElfW(Ehdr) *elf_header,
|
|||
reinterpret_cast<ElfW(Phdr) *>(elf_header->e_phoff),
|
||||
elf_header->e_phnum);
|
||||
module->SetLoadAddress(loading_addr);
|
||||
info->set_loading_addr(loading_addr, obj_file);
|
||||
|
||||
const ElfW(Shdr) *sections =
|
||||
reinterpret_cast<ElfW(Shdr) *>(elf_header->e_shoff);
|
||||
|
@ -345,11 +540,14 @@ static bool LoadSymbols(const std::string &obj_file, ElfW(Ehdr) *elf_header,
|
|||
const ElfW(Shdr) *stabstr_section = stab_section->sh_link + sections;
|
||||
if (stabstr_section) {
|
||||
found_debug_info_section = true;
|
||||
if (!LoadStabs(elf_header, stab_section, stabstr_section, module))
|
||||
info->LoadedSection(".stab");
|
||||
if (!LoadStabs(elf_header, stab_section, stabstr_section, big_endian,
|
||||
module)) {
|
||||
fprintf(stderr, "%s: \".stab\" section found, but failed to load STABS"
|
||||
" debugging information\n", obj_file.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Look for DWARF debugging information, and load it if present.
|
||||
const ElfW(Shdr) *dwarf_section
|
||||
|
@ -357,7 +555,8 @@ static bool LoadSymbols(const std::string &obj_file, ElfW(Ehdr) *elf_header,
|
|||
elf_header->e_shnum);
|
||||
if (dwarf_section) {
|
||||
found_debug_info_section = true;
|
||||
if (!LoadDwarf(obj_file, elf_header, module))
|
||||
info->LoadedSection(".debug_info");
|
||||
if (!LoadDwarf(obj_file, elf_header, big_endian, module))
|
||||
fprintf(stderr, "%s: \".debug_info\" section found, but failed to load "
|
||||
"DWARF debugging information\n", obj_file.c_str());
|
||||
}
|
||||
|
@ -371,8 +570,9 @@ static bool LoadSymbols(const std::string &obj_file, ElfW(Ehdr) *elf_header,
|
|||
// Ignore the return value of this function; even without call frame
|
||||
// information, the other debugging information could be perfectly
|
||||
// useful.
|
||||
info->LoadedSection(".debug_frame");
|
||||
LoadDwarfCFI(obj_file, elf_header, ".debug_frame",
|
||||
dwarf_cfi_section, false, 0, 0, module);
|
||||
dwarf_cfi_section, false, 0, 0, big_endian, module);
|
||||
}
|
||||
|
||||
// Linux C++ exception handling information can also provide
|
||||
|
@ -388,72 +588,42 @@ static bool LoadSymbols(const std::string &obj_file, ElfW(Ehdr) *elf_header,
|
|||
const ElfW(Shdr) *text_section =
|
||||
FindSectionByName(".text", sections, section_names,
|
||||
elf_header->e_shnum);
|
||||
info->LoadedSection(".eh_frame");
|
||||
// As above, ignore the return value of this function.
|
||||
LoadDwarfCFI(obj_file, elf_header, ".eh_frame",
|
||||
eh_frame_section, true, got_section, text_section, module);
|
||||
LoadDwarfCFI(obj_file, elf_header, ".eh_frame", eh_frame_section, true,
|
||||
got_section, text_section, big_endian, module);
|
||||
}
|
||||
|
||||
if (!found_debug_info_section) {
|
||||
fprintf(stderr, "%s: file contains no debugging information"
|
||||
" (no \".stab\" or \".debug_info\" sections)\n",
|
||||
obj_file.c_str());
|
||||
|
||||
// Failed, but maybe we can find a .gnu_debuglink section?
|
||||
if (read_gnu_debug_link) {
|
||||
const ElfW(Shdr) *gnu_debuglink_section
|
||||
= FindSectionByName(".gnu_debuglink", sections, section_names,
|
||||
elf_header->e_shnum);
|
||||
if (gnu_debuglink_section) {
|
||||
if (!info->debug_dir().empty()) {
|
||||
std::string debuglink_file =
|
||||
ReadDebugLink(gnu_debuglink_section, obj_file, info->debug_dir());
|
||||
info->set_debuglink_file(debuglink_file);
|
||||
} else {
|
||||
fprintf(stderr, ".gnu_debuglink section found in '%s', "
|
||||
"but no debug path specified.\n", obj_file.c_str());
|
||||
}
|
||||
} else {
|
||||
fprintf(stderr, "%s does not contain a .gnu_debuglink section.\n",
|
||||
obj_file.c_str());
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// FDWrapper
|
||||
//
|
||||
// Wrapper class to make sure opened file is closed.
|
||||
//
|
||||
class FDWrapper {
|
||||
public:
|
||||
explicit FDWrapper(int fd) :
|
||||
fd_(fd) {
|
||||
}
|
||||
~FDWrapper() {
|
||||
if (fd_ != -1)
|
||||
close(fd_);
|
||||
}
|
||||
int get() {
|
||||
return fd_;
|
||||
}
|
||||
int release() {
|
||||
int fd = fd_;
|
||||
fd_ = -1;
|
||||
return fd;
|
||||
}
|
||||
private:
|
||||
int fd_;
|
||||
};
|
||||
|
||||
//
|
||||
// MmapWrapper
|
||||
//
|
||||
// Wrapper class to make sure mapped regions are unmapped.
|
||||
//
|
||||
class MmapWrapper {
|
||||
public:
|
||||
MmapWrapper(void *mapped_address, size_t mapped_size) :
|
||||
base_(mapped_address), size_(mapped_size) {
|
||||
}
|
||||
~MmapWrapper() {
|
||||
if (base_ != NULL) {
|
||||
assert(size_ > 0);
|
||||
munmap(base_, size_);
|
||||
}
|
||||
}
|
||||
void release() {
|
||||
base_ = NULL;
|
||||
size_ = 0;
|
||||
}
|
||||
|
||||
private:
|
||||
void *base_;
|
||||
size_t size_;
|
||||
};
|
||||
|
||||
// Return the breakpad symbol file identifier for the architecture of
|
||||
// ELF_HEADER.
|
||||
const char *ElfArchitecture(const ElfW(Ehdr) *elf_header) {
|
||||
|
@ -506,37 +676,16 @@ std::string BaseFileName(const std::string &filename) {
|
|||
|
||||
namespace google_breakpad {
|
||||
|
||||
bool WriteSymbolFile(const std::string &obj_file, FILE *sym_file) {
|
||||
int obj_fd = open(obj_file.c_str(), O_RDONLY);
|
||||
if (obj_fd < 0) {
|
||||
fprintf(stderr, "Failed to open ELF file '%s': %s\n",
|
||||
obj_file.c_str(), strerror(errno));
|
||||
bool WriteSymbolFile(const std::string &obj_file,
|
||||
const std::string &debug_dir, FILE *sym_file) {
|
||||
MmapWrapper map_wrapper;
|
||||
ElfW(Ehdr) *elf_header = NULL;
|
||||
if (!LoadELF(obj_file, &map_wrapper, &elf_header))
|
||||
return false;
|
||||
}
|
||||
FDWrapper obj_fd_wrapper(obj_fd);
|
||||
struct stat st;
|
||||
if (fstat(obj_fd, &st) != 0 && st.st_size <= 0) {
|
||||
fprintf(stderr, "Unable to fstat ELF file '%s': %s\n",
|
||||
obj_file.c_str(), strerror(errno));
|
||||
return false;
|
||||
}
|
||||
void *obj_base = mmap(NULL, st.st_size,
|
||||
PROT_READ | PROT_WRITE, MAP_PRIVATE, obj_fd, 0);
|
||||
if (obj_base == MAP_FAILED) {
|
||||
fprintf(stderr, "Failed to mmap ELF file '%s': %s\n",
|
||||
obj_file.c_str(), strerror(errno));
|
||||
return false;
|
||||
}
|
||||
MmapWrapper map_wrapper(obj_base, st.st_size);
|
||||
ElfW(Ehdr) *elf_header = reinterpret_cast<ElfW(Ehdr) *>(obj_base);
|
||||
if (!IsValidElf(elf_header)) {
|
||||
fprintf(stderr, "Not a valid ELF file: %s\n", obj_file.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned char identifier[16];
|
||||
google_breakpad::FileID file_id(obj_file.c_str());
|
||||
if (!file_id.ElfFileIdentifier(identifier)) {
|
||||
if (!file_id.ElfFileIdentifierFromMappedFile(elf_header, identifier)) {
|
||||
fprintf(stderr, "%s: unable to generate file identifier\n",
|
||||
obj_file.c_str());
|
||||
return false;
|
||||
|
@ -549,13 +698,57 @@ bool WriteSymbolFile(const std::string &obj_file, FILE *sym_file) {
|
|||
return false;
|
||||
}
|
||||
|
||||
// Figure out what endianness this file is.
|
||||
bool big_endian;
|
||||
if (!ElfEndianness(elf_header, &big_endian))
|
||||
return false;
|
||||
|
||||
std::string name = BaseFileName(obj_file);
|
||||
std::string os = "Linux";
|
||||
std::string id = FormatIdentifier(identifier);
|
||||
|
||||
LoadSymbolsInfo info(debug_dir);
|
||||
Module module(name, os, architecture, id);
|
||||
if (!LoadSymbols(obj_file, elf_header, &module))
|
||||
if (!LoadSymbols(obj_file, big_endian, elf_header, true, &info, &module)) {
|
||||
const std::string debuglink_file = info.debuglink_file();
|
||||
if (debuglink_file.empty())
|
||||
return false;
|
||||
|
||||
// Load debuglink ELF file.
|
||||
fprintf(stderr, "Found debugging info in %s\n", debuglink_file.c_str());
|
||||
MmapWrapper debug_map_wrapper;
|
||||
ElfW(Ehdr) *debug_elf_header = NULL;
|
||||
if (!LoadELF(debuglink_file, &debug_map_wrapper, &debug_elf_header))
|
||||
return false;
|
||||
// Sanity checks to make sure everything matches up.
|
||||
const char *debug_architecture = ElfArchitecture(debug_elf_header);
|
||||
if (!debug_architecture) {
|
||||
fprintf(stderr, "%s: unrecognized ELF machine architecture: %d\n",
|
||||
debuglink_file.c_str(), debug_elf_header->e_machine);
|
||||
return false;
|
||||
}
|
||||
if (strcmp(architecture, debug_architecture)) {
|
||||
fprintf(stderr, "%s with ELF machine architecture %s does not match "
|
||||
"%s with ELF architecture %s\n",
|
||||
debuglink_file.c_str(), debug_architecture,
|
||||
obj_file.c_str(), architecture);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool debug_big_endian;
|
||||
if (!ElfEndianness(debug_elf_header, &debug_big_endian))
|
||||
return false;
|
||||
if (debug_big_endian != big_endian) {
|
||||
fprintf(stderr, "%s and %s does not match in endianness\n",
|
||||
obj_file.c_str(), debuglink_file.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!LoadSymbols(debuglink_file, debug_big_endian, debug_elf_header,
|
||||
false, &info, &module)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!module.Write(sym_file))
|
||||
return false;
|
||||
|
||||
|
|
|
@ -44,7 +44,10 @@ namespace google_breakpad {
|
|||
// Find all the debugging information in OBJ_FILE, an ELF executable
|
||||
// or shared library, and write it to SYM_FILE in the Breakpad symbol
|
||||
// file format.
|
||||
bool WriteSymbolFile(const std::string &obj_file, FILE *sym_file);
|
||||
// If OBJ_FILE has been stripped but contains a .gnu_debuglink section,
|
||||
// then look for the debug file in DEBUG_DIR.
|
||||
bool WriteSymbolFile(const std::string &obj_file,
|
||||
const std::string &debug_dir, FILE *sym_file);
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
|
|
|
@ -148,6 +148,24 @@ class wasteful_vector {
|
|||
return used_;
|
||||
}
|
||||
|
||||
void resize(unsigned sz, T c = T()) {
|
||||
// No need to test "sz >= 0", as "sz" is unsigned.
|
||||
if (sz <= used_) {
|
||||
used_ = sz;
|
||||
} else {
|
||||
unsigned a = allocated_;
|
||||
if (sz > a) {
|
||||
while (sz > a) {
|
||||
a *= 2;
|
||||
}
|
||||
Realloc(a);
|
||||
}
|
||||
while (sz > used_) {
|
||||
a_[used_++] = c;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
T& operator[](size_t index) {
|
||||
return a_[index];
|
||||
}
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
// Copyright (c) 2010, 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.
|
||||
|
||||
ARCHS = $(ARCHS_STANDARD_32_64_BIT)
|
||||
SDKROOT = macosx10.5
|
||||
SDKROOT[arch=i386] = macosx10.4
|
||||
SDKROOT[arch=ppc] = macosx10.4
|
||||
|
||||
GCC_VERSION = 4.2
|
||||
GCC_VERSION[sdk=macosx10.4][arch=*] = 4.0
|
||||
|
||||
GCC_C_LANGUAGE_STANDARD = c99
|
||||
|
||||
GCC_WARN_CHECK_SWITCH_STATEMENTS = YES
|
||||
// TODO(nealsid): Get the code so we can turn on the 64_TO_32 warning.
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = NO
|
||||
GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES
|
||||
GCC_WARN_MISSING_PARENTHESES = YES
|
||||
GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES
|
||||
GCC_WARN_ABOUT_MISSING_NEWLINE = YES
|
||||
GCC_WARN_SIGN_COMPARE = YES
|
||||
GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES
|
||||
GCC_WARN_UNKNOWN_PRAGMAS = YES
|
||||
GCC_WARN_UNUSED_VARIABLE = YES
|
||||
GCC_TREAT_WARNINGS_AS_ERRORS = YES
|
||||
|
||||
DEBUG_INFORMATION_FORMAT = dwarf-with-dsym
|
||||
|
||||
ALWAYS_SEARCH_USER_PATHS = NO
|
|
@ -0,0 +1,32 @@
|
|||
// Copyright (c) 2010, 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.
|
||||
|
||||
#include "Breakpad.xcconfig"
|
||||
|
||||
GCC_OPTIMIZATION_LEVEL = 0
|
|
@ -0,0 +1,33 @@
|
|||
// Copyright (c) 2010, 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.
|
||||
|
||||
#include "Breakpad.xcconfig"
|
||||
|
||||
GCC_OPTIMIZATION_LEVEL = s
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES
|
|
@ -28,6 +28,7 @@
|
|||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#import "HTTPMultipartUpload.h"
|
||||
#import "GTMDefines.h"
|
||||
|
||||
@interface HTTPMultipartUpload(PrivateMethods)
|
||||
- (NSString *)multipartBoundary;
|
||||
|
@ -148,7 +149,6 @@
|
|||
timeoutInterval:10.0 ];
|
||||
|
||||
NSMutableData *postBody = [NSMutableData data];
|
||||
int i, count;
|
||||
|
||||
[req setValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@",
|
||||
boundary_] forHTTPHeaderField:@"Content-type"];
|
||||
|
@ -157,8 +157,8 @@
|
|||
NSArray *parameterKeys = [parameters_ allKeys];
|
||||
NSString *key;
|
||||
|
||||
count = [parameterKeys count];
|
||||
for (i = 0; i < count; ++i) {
|
||||
NSInteger count = [parameterKeys count];
|
||||
for (NSInteger i = 0; i < count; ++i) {
|
||||
key = [parameterKeys objectAtIndex:i];
|
||||
[postBody appendData:[self formDataForKey:key
|
||||
value:[parameters_ objectForKey:key]]];
|
||||
|
@ -167,7 +167,7 @@
|
|||
// Add any files to the message
|
||||
NSArray *fileNames = [files_ allKeys];
|
||||
count = [fileNames count];
|
||||
for (i = 0; i < count; ++i) {
|
||||
for (NSInteger i = 0; i < count; ++i) {
|
||||
NSString *name = [fileNames objectAtIndex:i];
|
||||
id fileOrData = [files_ objectForKey:name];
|
||||
NSData *fileData;
|
||||
|
|
|
@ -94,6 +94,7 @@
|
|||
// kern_return_t result = sender.SendMessage(message, 1000); // timeout 1000ms
|
||||
//
|
||||
|
||||
namespace google_breakpad {
|
||||
#define PRINT_MACH_RESULT(result_, message_) \
|
||||
printf(message_" %s (%d)\n", mach_error_string(result_), result_ );
|
||||
|
||||
|
@ -224,7 +225,7 @@ class MachMessage {
|
|||
void SetDescriptor(int n, const MachMsgPortDescriptor &desc);
|
||||
|
||||
// Returns total message size setting msgh_size in the header to this value
|
||||
int CalculateSize();
|
||||
mach_msg_size_t CalculateSize();
|
||||
|
||||
mach_msg_header_t head;
|
||||
mach_msg_body_t body;
|
||||
|
@ -255,11 +256,11 @@ class MachSendMessage : public MachMessage {
|
|||
class ReceivePort {
|
||||
public:
|
||||
// Creates a new mach port for receiving messages and registers a name for it
|
||||
ReceivePort(const char *receive_port_name);
|
||||
explicit ReceivePort(const char *receive_port_name);
|
||||
|
||||
// Given an already existing mach port, use it. We take ownership of the
|
||||
// port and deallocate it in our destructor.
|
||||
ReceivePort(mach_port_t receive_port);
|
||||
explicit ReceivePort(mach_port_t receive_port);
|
||||
|
||||
// Create a new mach port for receiving messages
|
||||
ReceivePort();
|
||||
|
@ -285,11 +286,11 @@ class ReceivePort {
|
|||
class MachPortSender {
|
||||
public:
|
||||
// get a port with send rights corresponding to a named registered service
|
||||
MachPortSender(const char *receive_port_name);
|
||||
explicit MachPortSender(const char *receive_port_name);
|
||||
|
||||
|
||||
// Given an already existing mach port, use it.
|
||||
MachPortSender(mach_port_t send_port);
|
||||
explicit MachPortSender(mach_port_t send_port);
|
||||
|
||||
kern_return_t SendMessage(MachSendMessage &message,
|
||||
mach_msg_timeout_t timeout);
|
||||
|
@ -301,4 +302,6 @@ class MachPortSender {
|
|||
kern_return_t init_result_;
|
||||
};
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
#endif // MACH_IPC_H__
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
#import <stdio.h>
|
||||
#import "MachIPC.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
//==============================================================================
|
||||
MachSendMessage::MachSendMessage(int32_t message_id) : MachMessage() {
|
||||
head.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0);
|
||||
|
@ -53,10 +54,10 @@ MachSendMessage::MachSendMessage(int32_t message_id) : MachMessage() {
|
|||
bool MachMessage::SetData(void *data,
|
||||
int32_t data_length) {
|
||||
// first check to make sure we have enough space
|
||||
int size = CalculateSize();
|
||||
int new_size = size + data_length;
|
||||
size_t size = CalculateSize();
|
||||
size_t new_size = size + data_length;
|
||||
|
||||
if ((unsigned)new_size > sizeof(MachMessage)) {
|
||||
if (new_size > sizeof(MachMessage)) {
|
||||
return false; // not enough space
|
||||
}
|
||||
|
||||
|
@ -72,8 +73,8 @@ bool MachMessage::SetData(void *data,
|
|||
// calculates and returns the total size of the message
|
||||
// Currently, the entire message MUST fit inside of the MachMessage
|
||||
// messsage size <= sizeof(MachMessage)
|
||||
int MachMessage::CalculateSize() {
|
||||
int size = sizeof(mach_msg_header_t) + sizeof(mach_msg_body_t);
|
||||
mach_msg_size_t MachMessage::CalculateSize() {
|
||||
size_t size = sizeof(mach_msg_header_t) + sizeof(mach_msg_body_t);
|
||||
|
||||
// add space for MessageDataPacket
|
||||
int32_t alignedDataLength = (GetDataLength() + 3) & ~0x3;
|
||||
|
@ -82,14 +83,14 @@ int MachMessage::CalculateSize() {
|
|||
// add space for descriptors
|
||||
size += GetDescriptorCount() * sizeof(MachMsgPortDescriptor);
|
||||
|
||||
head.msgh_size = size;
|
||||
head.msgh_size = static_cast<mach_msg_size_t>(size);
|
||||
|
||||
return size;
|
||||
return head.msgh_size;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
MachMessage::MessageDataPacket *MachMessage::GetDataPacket() {
|
||||
int desc_size = sizeof(MachMsgPortDescriptor)*GetDescriptorCount();
|
||||
size_t desc_size = sizeof(MachMsgPortDescriptor)*GetDescriptorCount();
|
||||
MessageDataPacket *packet =
|
||||
reinterpret_cast<MessageDataPacket*>(padding + desc_size);
|
||||
|
||||
|
@ -109,9 +110,9 @@ void MachMessage::SetDescriptor(int n,
|
|||
bool MachMessage::AddDescriptor(const MachMsgPortDescriptor &desc) {
|
||||
// first check to make sure we have enough space
|
||||
int size = CalculateSize();
|
||||
int new_size = size + sizeof(MachMsgPortDescriptor);
|
||||
size_t new_size = size + sizeof(MachMsgPortDescriptor);
|
||||
|
||||
if ((unsigned)new_size > sizeof(MachMessage)) {
|
||||
if (new_size > sizeof(MachMessage)) {
|
||||
return false; // not enough space
|
||||
}
|
||||
|
||||
|
@ -180,8 +181,8 @@ ReceivePort::ReceivePort(const char *receive_port_name) {
|
|||
if (init_result_ != KERN_SUCCESS)
|
||||
return;
|
||||
|
||||
mach_port_t bootstrap_port = 0;
|
||||
init_result_ = task_get_bootstrap_port(current_task, &bootstrap_port);
|
||||
mach_port_t task_bootstrap_port = 0;
|
||||
init_result_ = task_get_bootstrap_port(current_task, &task_bootstrap_port);
|
||||
|
||||
if (init_result_ != KERN_SUCCESS)
|
||||
return;
|
||||
|
@ -240,8 +241,11 @@ kern_return_t ReceivePort::WaitForMessage(MachReceiveMessage *out_message,
|
|||
out_message->head.msgh_reserved = 0;
|
||||
out_message->head.msgh_id = 0;
|
||||
|
||||
mach_msg_option_t options = MACH_RCV_MSG;
|
||||
if (timeout != MACH_MSG_TIMEOUT_NONE)
|
||||
options |= MACH_RCV_TIMEOUT;
|
||||
kern_return_t result = mach_msg(&out_message->head,
|
||||
MACH_RCV_MSG | MACH_RCV_TIMEOUT,
|
||||
options,
|
||||
0,
|
||||
sizeof(MachMessage),
|
||||
port_,
|
||||
|
@ -256,13 +260,14 @@ kern_return_t ReceivePort::WaitForMessage(MachReceiveMessage *out_message,
|
|||
//==============================================================================
|
||||
// get a port with send rights corresponding to a named registered service
|
||||
MachPortSender::MachPortSender(const char *receive_port_name) {
|
||||
mach_port_t bootstrap_port = 0;
|
||||
init_result_ = task_get_bootstrap_port(mach_task_self(), &bootstrap_port);
|
||||
mach_port_t task_bootstrap_port = 0;
|
||||
init_result_ = task_get_bootstrap_port(mach_task_self(),
|
||||
&task_bootstrap_port);
|
||||
|
||||
if (init_result_ != KERN_SUCCESS)
|
||||
return;
|
||||
|
||||
init_result_ = bootstrap_look_up(bootstrap_port,
|
||||
init_result_ = bootstrap_look_up(task_bootstrap_port,
|
||||
const_cast<char*>(receive_port_name),
|
||||
&send_port_);
|
||||
}
|
||||
|
@ -295,3 +300,5 @@ kern_return_t MachPortSender::SendMessage(MachSendMessage &message,
|
|||
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
|
|
@ -84,6 +84,16 @@ class DumpSymbols {
|
|||
// architecture matches that of this dumper program.
|
||||
bool SetArchitecture(cpu_type_t cpu_type, cpu_subtype_t cpu_subtype);
|
||||
|
||||
// If this dumper's file includes an object file for |arch_name|, then select
|
||||
// that object file for dumping, and return true. Otherwise, return false,
|
||||
// and leave this dumper's selected architecture unchanged.
|
||||
//
|
||||
// By default, if this dumper's file contains only one object file, then
|
||||
// the dumper will dump those symbols; and if it contains more than one
|
||||
// object file, then the dumper will dump the object file whose
|
||||
// architecture matches that of this dumper program.
|
||||
bool SetArchitecture(const std::string &arch_name);
|
||||
|
||||
// Return a pointer to an array of 'struct fat_arch' structures,
|
||||
// describing the object files contained in this dumper's file. Set
|
||||
// *|count| to the number of elements in the array. The returned array is
|
||||
|
|
|
@ -54,6 +54,10 @@
|
|||
#include "common/stabs_reader.h"
|
||||
#include "common/stabs_to_module.h"
|
||||
|
||||
#ifndef CPU_TYPE_ARM
|
||||
#define CPU_TYPE_ARM (static_cast<cpu_type_t>(12))
|
||||
#endif // CPU_TYPE_ARM
|
||||
|
||||
using dwarf2reader::ByteReader;
|
||||
using google_breakpad::DwarfCUToModule;
|
||||
using google_breakpad::DwarfLineToModule;
|
||||
|
@ -176,7 +180,7 @@ bool DumpSymbols::SetArchitecture(cpu_type_t cpu_type,
|
|||
// Find the best match for the architecture the user requested.
|
||||
const struct fat_arch *best_match
|
||||
= NXFindBestFatArch(cpu_type, cpu_subtype, &object_files_[0],
|
||||
object_files_.size());
|
||||
static_cast<uint32_t>(object_files_.size()));
|
||||
if (!best_match) return false;
|
||||
|
||||
// Record the selected object file.
|
||||
|
@ -184,6 +188,15 @@ bool DumpSymbols::SetArchitecture(cpu_type_t cpu_type,
|
|||
return true;
|
||||
}
|
||||
|
||||
bool DumpSymbols::SetArchitecture(const std::string &arch_name) {
|
||||
bool arch_set = false;
|
||||
const NXArchInfo *arch_info = NXGetArchInfoFromName(arch_name.c_str());
|
||||
if (arch_info) {
|
||||
arch_set = SetArchitecture(arch_info->cputype, arch_info->cpusubtype);
|
||||
}
|
||||
return arch_set;
|
||||
}
|
||||
|
||||
string DumpSymbols::Identifier() {
|
||||
FileID file_id([object_filename_ fileSystemRepresentation]);
|
||||
unsigned char identifier_bytes[16];
|
||||
|
@ -436,12 +449,16 @@ bool DumpSymbols::WriteSymbolFile(FILE *stream) {
|
|||
= NXGetArchInfoFromCpuType(selected_object_file_->cputype,
|
||||
selected_object_file_->cpusubtype);
|
||||
|
||||
const char *selected_arch_name = selected_arch_info->name;
|
||||
if (strcmp(selected_arch_name, "i386") == 0)
|
||||
selected_arch_name = "x86";
|
||||
|
||||
// Produce a name to use in error messages that includes the
|
||||
// filename, and the architecture, if there is more than one.
|
||||
selected_object_name_ = [object_filename_ UTF8String];
|
||||
if (object_files_.size() > 1) {
|
||||
selected_object_name_ += ", architecture ";
|
||||
selected_object_name_ + selected_arch_info->name;
|
||||
selected_object_name_ + selected_arch_name;
|
||||
}
|
||||
|
||||
// Compute a module name, to appear in the MODULE record.
|
||||
|
@ -454,7 +471,7 @@ bool DumpSymbols::WriteSymbolFile(FILE *stream) {
|
|||
identifier += "0";
|
||||
|
||||
// Create a module to hold the debugging information.
|
||||
Module module([module_name UTF8String], "mac", selected_arch_info->name,
|
||||
Module module([module_name UTF8String], "mac", selected_arch_name,
|
||||
identifier);
|
||||
|
||||
// Parse the selected object file.
|
||||
|
|
|
@ -53,7 +53,12 @@ extern "C" { // necessary for Leopard
|
|||
|
||||
namespace MacFileUtilities {
|
||||
|
||||
MachoID::MachoID(const char *path) {
|
||||
MachoID::MachoID(const char *path)
|
||||
: file_(0),
|
||||
crc_(0),
|
||||
md5_context_(),
|
||||
sha1_context_(),
|
||||
update_function_(NULL) {
|
||||
strlcpy(path_, path, sizeof(path_));
|
||||
file_ = open(path, O_RDONLY);
|
||||
}
|
||||
|
@ -119,7 +124,7 @@ void MachoID::UpdateSHA1(unsigned char *bytes, size_t size) {
|
|||
SHA_Update(&sha1_context_, bytes, size);
|
||||
}
|
||||
|
||||
void MachoID::Update(MachoWalker *walker, unsigned long offset, size_t size) {
|
||||
void MachoID::Update(MachoWalker *walker, off_t offset, size_t size) {
|
||||
if (!update_function_ || !size)
|
||||
return;
|
||||
|
||||
|
@ -182,7 +187,7 @@ bool MachoID::IDCommand(int cpu_type, unsigned char identifier[16]) {
|
|||
identifier[2] = 0;
|
||||
identifier[3] = 0;
|
||||
|
||||
for (int j = 0, i = strlen(path_)-1; i >= 0 && path_[i]!='/'; ++j, --i) {
|
||||
for (int j = 0, i = (int)strlen(path_)-1; i>=0 && path_[i]!='/'; ++j, --i) {
|
||||
identifier[j%4] += path_[i];
|
||||
}
|
||||
|
||||
|
@ -313,7 +318,9 @@ bool MachoID::WalkerCB(MachoWalker *walker, load_command *cmd, off_t offset,
|
|||
// sections of type S_ZEROFILL are "virtual" and contain no data
|
||||
// in the file itself
|
||||
if ((sec64.flags & SECTION_TYPE) != S_ZEROFILL && sec64.offset != 0)
|
||||
macho_id->Update(walker, header_offset + sec64.offset, sec64.size);
|
||||
macho_id->Update(walker,
|
||||
header_offset + sec64.offset,
|
||||
(size_t)sec64.size);
|
||||
|
||||
offset += sizeof(struct section_64);
|
||||
}
|
||||
|
|
|
@ -86,7 +86,7 @@ class MachoID {
|
|||
void UpdateSHA1(unsigned char *bytes, size_t size);
|
||||
|
||||
// Bottleneck for update routines
|
||||
void Update(MachoWalker *walker, unsigned long offset, size_t size);
|
||||
void Update(MachoWalker *walker, off_t offset, size_t size);
|
||||
|
||||
// The callback from the MachoWalker for CRC, MD5, and SHA1
|
||||
static bool WalkerCB(MachoWalker *walker, load_command *cmd, off_t offset,
|
||||
|
|
|
@ -130,7 +130,7 @@ bool FatReader::Read(const uint8_t *buffer, size_t size) {
|
|||
}
|
||||
|
||||
object_files_[0].offset = 0;
|
||||
object_files_[0].size = buffer_.Size();
|
||||
object_files_[0].size = static_cast<uint32_t>(buffer_.Size());
|
||||
// This alignment is correct for 32 and 64-bit x86 and ppc.
|
||||
// See get_align in the lipo source for other architectures:
|
||||
// http://www.opensource.apple.com/source/cctools/cctools-773/misc/lipo.c
|
||||
|
|
|
@ -992,7 +992,7 @@ TEST_F(LoadCommand, None) {
|
|||
EXPECT_FALSE(reader.big_endian());
|
||||
EXPECT_EQ(CPU_TYPE_X86, reader.cpu_type());
|
||||
EXPECT_EQ(CPU_SUBTYPE_I386_ALL, reader.cpu_subtype());
|
||||
EXPECT_EQ(MH_EXECUTE, reader.file_type());
|
||||
EXPECT_EQ(static_cast<uint32_t>(MH_EXECUTE), reader.file_type());
|
||||
EXPECT_EQ(FileFlags(MH_TWOLEVEL |
|
||||
MH_DYLDLINK |
|
||||
MH_NOUNDEFS),
|
||||
|
@ -1018,7 +1018,7 @@ TEST_F(LoadCommand, Unknown) {
|
|||
EXPECT_TRUE(reader.big_endian());
|
||||
EXPECT_EQ(CPU_TYPE_X86, reader.cpu_type());
|
||||
EXPECT_EQ(CPU_SUBTYPE_I386_ALL, reader.cpu_subtype());
|
||||
EXPECT_EQ(MH_EXECUTE, reader.file_type());
|
||||
EXPECT_EQ(static_cast<uint32_t>(MH_EXECUTE), reader.file_type());
|
||||
EXPECT_EQ(FileFlags(MH_TWOLEVEL |
|
||||
MH_DYLDLINK |
|
||||
MH_NOUNDEFS),
|
||||
|
|
|
@ -51,8 +51,12 @@ namespace MacFileUtilities {
|
|||
|
||||
MachoWalker::MachoWalker(const char *path, LoadCommandCallback callback,
|
||||
void *context)
|
||||
: callback_(callback),
|
||||
callback_context_(context) {
|
||||
: file_(0),
|
||||
callback_(callback),
|
||||
callback_context_(context),
|
||||
current_header_(NULL),
|
||||
current_header_size_(0),
|
||||
current_header_offset_(0) {
|
||||
file_ = open(path, O_RDONLY);
|
||||
}
|
||||
|
||||
|
|
|
@ -99,6 +99,10 @@ class MachoWalker {
|
|||
struct mach_header_64 *current_header_;
|
||||
unsigned long current_header_size_;
|
||||
off_t current_header_offset_;
|
||||
|
||||
private:
|
||||
MachoWalker(const MachoWalker &);
|
||||
MachoWalker &operator=(const MachoWalker &);
|
||||
};
|
||||
|
||||
} // namespace MacFileUtilities
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
// Copyright (c) 2010 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.
|
||||
|
||||
// Inline implementation of ScopedTaskSuspend, which suspends a Mach
|
||||
// task for the duration of its scope.
|
||||
|
||||
#ifndef GOOGLE_BREAKPAD_COMMON_MAC_SCOPED_TASK_SUSPEND_H_
|
||||
#define GOOGLE_BREAKPAD_COMMON_MAC_SCOPED_TASK_SUSPEND_H_
|
||||
|
||||
#include <mach/mach.h>
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
class ScopedTaskSuspend {
|
||||
public:
|
||||
explicit ScopedTaskSuspend(mach_port_t target) : target_(target) {
|
||||
task_suspend(target_);
|
||||
}
|
||||
|
||||
~ScopedTaskSuspend() {
|
||||
task_resume(target_);
|
||||
}
|
||||
|
||||
private:
|
||||
mach_port_t target_;
|
||||
};
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
#endif // GOOGLE_BREAKPAD_COMMON_MAC_SCOPED_TASK_SUSPEND_H_
|
|
@ -58,7 +58,7 @@ unsigned int IntegerValueAtIndex(string &str, unsigned int idx) {
|
|||
size_t start = 0;
|
||||
size_t end;
|
||||
size_t found = 0;
|
||||
size_t result = 0;
|
||||
unsigned int result = 0;
|
||||
|
||||
for (; found <= idx; ++found) {
|
||||
end = str.find_first_not_of(digits, start);
|
||||
|
|
|
@ -49,7 +49,7 @@ Module::Module(const string &name, const string &os,
|
|||
Module::~Module() {
|
||||
for (FileByNameMap::iterator it = files_.begin(); it != files_.end(); it++)
|
||||
delete it->second;
|
||||
for (vector<Function *>::iterator it = functions_.begin();
|
||||
for (FunctionSet::iterator it = functions_.begin();
|
||||
it != functions_.end(); it++)
|
||||
delete *it;
|
||||
for (vector<StackFrameEntry *>::iterator it = stack_frame_entries_.begin();
|
||||
|
@ -62,12 +62,17 @@ void Module::SetLoadAddress(Address address) {
|
|||
}
|
||||
|
||||
void Module::AddFunction(Function *function) {
|
||||
functions_.push_back(function);
|
||||
std::pair<FunctionSet::iterator,bool> ret = functions_.insert(function);
|
||||
if (!ret.second) {
|
||||
// Free the duplicate we failed to insert because we own it.
|
||||
delete function;
|
||||
}
|
||||
}
|
||||
|
||||
void Module::AddFunctions(vector<Function *>::iterator begin,
|
||||
vector<Function *>::iterator end) {
|
||||
functions_.insert(functions_.end(), begin, end);
|
||||
for (vector<Function *>::iterator it = begin; it != end; it++)
|
||||
AddFunction(*it);
|
||||
}
|
||||
|
||||
void Module::AddStackFrameEntry(StackFrameEntry *stack_frame_entry) {
|
||||
|
@ -130,7 +135,7 @@ void Module::AssignSourceIds() {
|
|||
|
||||
// Next, mark all files actually cited by our functions' line number
|
||||
// info, by setting each one's source id to zero.
|
||||
for (vector<Function *>::const_iterator func_it = functions_.begin();
|
||||
for (FunctionSet::const_iterator func_it = functions_.begin();
|
||||
func_it != functions_.end(); func_it++) {
|
||||
Function *func = *func_it;
|
||||
for (vector<Line>::iterator line_it = func->lines.begin();
|
||||
|
@ -145,13 +150,13 @@ void Module::AssignSourceIds() {
|
|||
int next_source_id = 0;
|
||||
for (FileByNameMap::iterator file_it = files_.begin();
|
||||
file_it != files_.end(); file_it++)
|
||||
if (! file_it->second->source_id)
|
||||
if (!file_it->second->source_id)
|
||||
file_it->second->source_id = next_source_id++;
|
||||
}
|
||||
|
||||
bool Module::ReportError() {
|
||||
fprintf(stderr, "error writing symbol file: %s\n",
|
||||
strerror (errno));
|
||||
strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -187,7 +192,7 @@ bool Module::Write(FILE *stream) {
|
|||
}
|
||||
|
||||
// Write out functions and their lines.
|
||||
for (vector<Function *>::const_iterator func_it = functions_.begin();
|
||||
for (FunctionSet::const_iterator func_it = functions_.begin();
|
||||
func_it != functions_.end(); func_it++) {
|
||||
Function *func = *func_it;
|
||||
if (0 > fprintf(stream, "FUNC %llx %llx %llx %s\n",
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
#include <stdio.h>
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
|
@ -48,6 +49,7 @@
|
|||
|
||||
namespace google_breakpad {
|
||||
|
||||
using std::set;
|
||||
using std::string;
|
||||
using std::vector;
|
||||
using std::map;
|
||||
|
@ -144,6 +146,15 @@ class Module {
|
|||
RuleChangeMap rule_changes;
|
||||
};
|
||||
|
||||
struct FunctionCompare {
|
||||
bool operator() (const Function *lhs,
|
||||
const Function *rhs) const {
|
||||
if (lhs->address == rhs->address)
|
||||
return lhs->name < rhs->name;
|
||||
return lhs->address < rhs->address;
|
||||
}
|
||||
};
|
||||
|
||||
// Create a new module with the given name, operating system,
|
||||
// architecture, and ID string.
|
||||
Module(const string &name, const string &os, const string &architecture,
|
||||
|
@ -258,12 +269,13 @@ class Module {
|
|||
// A map from filenames to File structures. The map's keys are
|
||||
// pointers to the Files' names.
|
||||
typedef map<const string *, File *, CompareStringPtrs> FileByNameMap;
|
||||
typedef set<Function *, FunctionCompare> FunctionSet;
|
||||
|
||||
// The module owns all the files and functions that have been added
|
||||
// to it; destroying the module frees the Files and Functions these
|
||||
// point to.
|
||||
FileByNameMap files_; // This module's source files.
|
||||
vector<Function *> functions_; // This module's functions.
|
||||
FunctionSet functions_; // This module's functions.
|
||||
|
||||
// The module owns all the call frame info entries that have been
|
||||
// added to it.
|
||||
|
|
|
@ -49,7 +49,7 @@ using testing::ContainerEq;
|
|||
|
||||
// Return a FILE * referring to a temporary file that will be deleted
|
||||
// automatically when the stream is closed or the program exits.
|
||||
FILE *checked_tmpfile() {
|
||||
static FILE *checked_tmpfile() {
|
||||
FILE *f = tmpfile();
|
||||
if (!f) {
|
||||
fprintf(stderr, "error creating temporary file: %s\n", strerror(errno));
|
||||
|
@ -60,7 +60,7 @@ FILE *checked_tmpfile() {
|
|||
|
||||
// Read from STREAM until end of file, and return the contents as a
|
||||
// string.
|
||||
string checked_read(FILE *stream) {
|
||||
static string checked_read(FILE *stream) {
|
||||
string contents;
|
||||
int c;
|
||||
while ((c = getc(stream)) != EOF)
|
||||
|
@ -74,7 +74,7 @@ string checked_read(FILE *stream) {
|
|||
}
|
||||
|
||||
// Apply 'fflush' to STREAM, and check for errors.
|
||||
void checked_fflush(FILE *stream) {
|
||||
static void checked_fflush(FILE *stream) {
|
||||
if (fflush(stream) == EOF) {
|
||||
fprintf(stderr, "error flushing temporary file stream: %s\n",
|
||||
strerror(errno));
|
||||
|
@ -83,7 +83,7 @@ void checked_fflush(FILE *stream) {
|
|||
}
|
||||
|
||||
// Apply 'fclose' to STREAM, and check for errors.
|
||||
void checked_fclose(FILE *stream) {
|
||||
static void checked_fclose(FILE *stream) {
|
||||
if (fclose(stream) == EOF) {
|
||||
fprintf(stderr, "error closing temporary file stream: %s\n",
|
||||
strerror(errno));
|
||||
|
@ -91,6 +91,19 @@ void checked_fclose(FILE *stream) {
|
|||
}
|
||||
}
|
||||
|
||||
static Module::Function *generate_duplicate_function(const string &name) {
|
||||
const Module::Address DUP_ADDRESS = 0xd35402aac7a7ad5cLL;
|
||||
const Module::Address DUP_SIZE = 0x200b26e605f99071LL;
|
||||
const Module::Address DUP_PARAMETER_SIZE = 0xf14ac4fed48c4a99LL;
|
||||
|
||||
Module::Function *function = new(Module::Function);
|
||||
function->name = name;
|
||||
function->address = DUP_ADDRESS;
|
||||
function->size = DUP_SIZE;
|
||||
function->parameter_size = DUP_PARAMETER_SIZE;
|
||||
return function;
|
||||
}
|
||||
|
||||
#define MODULE_NAME "name with spaces"
|
||||
#define MODULE_OS "os-name"
|
||||
#define MODULE_ARCH "architecture"
|
||||
|
@ -280,10 +293,10 @@ TEST(Construct, AddFunctions) {
|
|||
string contents = checked_read(f);
|
||||
checked_fclose(f);
|
||||
EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n"
|
||||
"FUNC d35024aa7ca7da5c 200b26e605f99071 f14ac4fed48c4a99"
|
||||
" _without_form\n"
|
||||
"FUNC 2987743d0b35b13f b369db048deb3010 938e556cb5a79988"
|
||||
" _and_void\n",
|
||||
" _and_void\n"
|
||||
"FUNC d35024aa7ca7da5c 200b26e605f99071 f14ac4fed48c4a99"
|
||||
" _without_form\n",
|
||||
contents.c_str());
|
||||
|
||||
// Check that m.GetFunctions returns the functions we expect.
|
||||
|
@ -396,3 +409,49 @@ TEST(Construct, UniqueFiles) {
|
|||
EXPECT_EQ(file1, m.FindExistingFile("foo"));
|
||||
EXPECT_TRUE(m.FindExistingFile("baz") == NULL);
|
||||
}
|
||||
|
||||
TEST(Construct, DuplicateFunctions) {
|
||||
FILE *f = checked_tmpfile();
|
||||
Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID);
|
||||
|
||||
// Two functions.
|
||||
Module::Function *function1 = generate_duplicate_function("_without_form");
|
||||
Module::Function *function2 = generate_duplicate_function("_without_form");
|
||||
|
||||
m.AddFunction(function1);
|
||||
m.AddFunction(function2);
|
||||
|
||||
m.Write(f);
|
||||
checked_fflush(f);
|
||||
rewind(f);
|
||||
string contents = checked_read(f);
|
||||
checked_fclose(f);
|
||||
EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n"
|
||||
"FUNC d35402aac7a7ad5c 200b26e605f99071 f14ac4fed48c4a99"
|
||||
" _without_form\n",
|
||||
contents.c_str());
|
||||
}
|
||||
|
||||
TEST(Construct, FunctionsWithSameAddress) {
|
||||
FILE *f = checked_tmpfile();
|
||||
Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID);
|
||||
|
||||
// Two functions.
|
||||
Module::Function *function1 = generate_duplicate_function("_without_form");
|
||||
Module::Function *function2 = generate_duplicate_function("_and_void");
|
||||
|
||||
m.AddFunction(function1);
|
||||
m.AddFunction(function2);
|
||||
|
||||
m.Write(f);
|
||||
checked_fflush(f);
|
||||
rewind(f);
|
||||
string contents = checked_read(f);
|
||||
checked_fclose(f);
|
||||
EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n"
|
||||
"FUNC d35402aac7a7ad5c 200b26e605f99071 f14ac4fed48c4a99"
|
||||
" _and_void\n"
|
||||
"FUNC d35402aac7a7ad5c 200b26e605f99071 f14ac4fed48c4a99"
|
||||
" _without_form\n",
|
||||
contents.c_str());
|
||||
}
|
||||
|
|
|
@ -58,7 +58,7 @@ static string Demangle(const string &mangled) {
|
|||
|
||||
StabsToModule::~StabsToModule() {
|
||||
// Free any functions we've accumulated but not added to the module.
|
||||
for (vector<Module::Function *>::iterator func_it = functions_.begin();
|
||||
for (vector<Module::Function *>::const_iterator func_it = functions_.begin();
|
||||
func_it != functions_.end(); func_it++)
|
||||
delete *func_it;
|
||||
// Free any function that we're currently within.
|
||||
|
@ -104,16 +104,8 @@ bool StabsToModule::EndFunction(uint64_t address) {
|
|||
assert(current_function_);
|
||||
// Functions in this compilation unit should have address bigger
|
||||
// than the compilation unit's starting address. There may be a lot
|
||||
// of duplicated entries for functions in the STABS data; only one
|
||||
// entry can meet this requirement.
|
||||
//
|
||||
// (I don't really understand the above comment; just bringing it along
|
||||
// from the previous code, and leaving the behavior unchanged. GCC marks
|
||||
// the end of each function with an N_FUN entry with no name, whose value
|
||||
// is the size of the function; perhaps this test was concerned with
|
||||
// skipping those. Now StabsReader interprets them properly. If you know
|
||||
// the whole story, please patch this comment. --jimb)
|
||||
//
|
||||
// of duplicated entries for functions in the STABS data. We will
|
||||
// count on the Module to remove the duplicates.
|
||||
if (current_function_->address >= comp_unit_base_address_)
|
||||
functions_.push_back(current_function_);
|
||||
else
|
||||
|
@ -153,12 +145,13 @@ void StabsToModule::Finalize() {
|
|||
// Sort all functions by address, just for neatness.
|
||||
sort(functions_.begin(), functions_.end(),
|
||||
Module::Function::CompareByAddress);
|
||||
for (vector<Module::Function *>::iterator func_it = functions_.begin();
|
||||
|
||||
for (vector<Module::Function *>::const_iterator func_it = functions_.begin();
|
||||
func_it != functions_.end();
|
||||
func_it++) {
|
||||
Module::Function *f = *func_it;
|
||||
// Compute the function f's size.
|
||||
vector<Module::Address>::iterator boundary
|
||||
vector<Module::Address>::const_iterator boundary
|
||||
= std::upper_bound(boundaries_.begin(), boundaries_.end(), f->address);
|
||||
if (boundary != boundaries_.end())
|
||||
f->size = *boundary - f->address;
|
||||
|
|
|
@ -74,6 +74,38 @@ TEST(StabsToModule, SimpleCU) {
|
|||
EXPECT_EQ(174823314, line->number);
|
||||
}
|
||||
|
||||
TEST(StabsToModule, DuplicateFunctionNames) {
|
||||
Module m("name", "os", "arch", "id");
|
||||
StabsToModule h(&m);
|
||||
|
||||
// Compilation unit with one function, mangled name.
|
||||
EXPECT_TRUE(h.StartCompilationUnit("compilation-unit", 0xf2cfda36ecf7f46cLL,
|
||||
"build-directory"));
|
||||
EXPECT_TRUE(h.StartFunction("funcfoo",
|
||||
0xf2cfda36ecf7f46dLL));
|
||||
EXPECT_TRUE(h.EndFunction(0));
|
||||
EXPECT_TRUE(h.StartFunction("funcfoo",
|
||||
0xf2cfda36ecf7f46dLL));
|
||||
EXPECT_TRUE(h.EndFunction(0));
|
||||
EXPECT_TRUE(h.EndCompilationUnit(0));
|
||||
|
||||
h.Finalize();
|
||||
|
||||
// Now check to see what has been added to the Module.
|
||||
Module::File *file = m.FindExistingFile("compilation-unit");
|
||||
ASSERT_TRUE(file != NULL);
|
||||
|
||||
vector<Module::Function *> functions;
|
||||
m.GetFunctions(&functions, functions.end());
|
||||
ASSERT_EQ(1U, functions.size());
|
||||
|
||||
Module::Function *function = functions[0];
|
||||
EXPECT_EQ(0xf2cfda36ecf7f46dLL, function->address);
|
||||
EXPECT_LT(0U, function->size); // should have used dummy size
|
||||
EXPECT_EQ(0U, function->parameter_size);
|
||||
ASSERT_EQ(0U, function->lines.size());
|
||||
}
|
||||
|
||||
TEST(InferSizes, LineSize) {
|
||||
Module m("name", "os", "arch", "id");
|
||||
StabsToModule h(&m);
|
||||
|
|
|
@ -68,7 +68,7 @@ int UTF8ToUTF16Char(const char *in, int in_length, u_int16_t out[2]) {
|
|||
strictConversion);
|
||||
|
||||
if (result == conversionOK)
|
||||
return source_ptr - reinterpret_cast<const UTF8 *>(in);
|
||||
return static_cast<int>(source_ptr - reinterpret_cast<const UTF8 *>(in));
|
||||
|
||||
// Add another character to the input stream and try again
|
||||
source_ptr = reinterpret_cast<const UTF8 *>(in);
|
||||
|
@ -135,7 +135,7 @@ string UTF16ToUTF8(const vector<u_int16_t> &in, bool swap) {
|
|||
|
||||
// The maximum expansion would be 4x the size of the input string.
|
||||
const UTF16 *source_end_ptr = source_ptr + in.size();
|
||||
int target_capacity = in.size() * 4;
|
||||
size_t target_capacity = in.size() * 4;
|
||||
scoped_array<UTF8> target_buffer(new UTF8[target_capacity]);
|
||||
UTF8 *target_ptr = target_buffer.get();
|
||||
UTF8 *target_end_ptr = target_ptr + target_capacity;
|
||||
|
@ -145,8 +145,7 @@ string UTF16ToUTF8(const vector<u_int16_t> &in, bool swap) {
|
|||
|
||||
if (result == conversionOK) {
|
||||
const char *targetPtr = reinterpret_cast<const char *>(target_buffer.get());
|
||||
string result(targetPtr);
|
||||
return result;
|
||||
return targetPtr;
|
||||
}
|
||||
|
||||
return "";
|
||||
|
|
|
@ -96,7 +96,7 @@ u_int64_t Label::operator-(const Label &label) const {
|
|||
}
|
||||
|
||||
u_int64_t Label::Value() const {
|
||||
u_int64_t v;
|
||||
u_int64_t v = 0;
|
||||
ALWAYS_EVALUATE_AND_ASSERT(IsKnownConstant(&v));
|
||||
return v;
|
||||
};
|
||||
|
|
|
@ -37,10 +37,10 @@
|
|||
#include "breakpad_googletest_includes.h"
|
||||
#include "common/test_assembler.h"
|
||||
|
||||
using google_breakpad::TestAssembler::Label;
|
||||
using google_breakpad::TestAssembler::Section;
|
||||
using google_breakpad::TestAssembler::kBigEndian;
|
||||
using google_breakpad::TestAssembler::kLittleEndian;
|
||||
using google_breakpad::test_assembler::Label;
|
||||
using google_breakpad::test_assembler::Section;
|
||||
using google_breakpad::test_assembler::kBigEndian;
|
||||
using google_breakpad::test_assembler::kLittleEndian;
|
||||
using std::string;
|
||||
using testing::Test;
|
||||
|
||||
|
|
|
@ -61,8 +61,8 @@ using google_breakpad::SynthMinidump::Stream;
|
|||
using google_breakpad::SynthMinidump::String;
|
||||
using google_breakpad::SynthMinidump::SystemInfo;
|
||||
using google_breakpad::SynthMinidump::Thread;
|
||||
using google_breakpad::TestAssembler::kBigEndian;
|
||||
using google_breakpad::TestAssembler::kLittleEndian;
|
||||
using google_breakpad::test_assembler::kBigEndian;
|
||||
using google_breakpad::test_assembler::kLittleEndian;
|
||||
using std::ifstream;
|
||||
using std::istringstream;
|
||||
using std::string;
|
||||
|
|
|
@ -51,9 +51,9 @@ using google_breakpad::StackFrame;
|
|||
using google_breakpad::StackFrameAMD64;
|
||||
using google_breakpad::StackwalkerAMD64;
|
||||
using google_breakpad::SystemInfo;
|
||||
using google_breakpad::TestAssembler::kLittleEndian;
|
||||
using google_breakpad::TestAssembler::Label;
|
||||
using google_breakpad::TestAssembler::Section;
|
||||
using google_breakpad::test_assembler::kLittleEndian;
|
||||
using google_breakpad::test_assembler::Label;
|
||||
using google_breakpad::test_assembler::Section;
|
||||
using std::string;
|
||||
using std::vector;
|
||||
using testing::_;
|
||||
|
|
|
@ -53,9 +53,9 @@ using google_breakpad::StackFrameARM;
|
|||
using google_breakpad::StackwalkerARM;
|
||||
using google_breakpad::SystemInfo;
|
||||
using google_breakpad::WindowsFrameInfo;
|
||||
using google_breakpad::TestAssembler::kLittleEndian;
|
||||
using google_breakpad::TestAssembler::Label;
|
||||
using google_breakpad::TestAssembler::Section;
|
||||
using google_breakpad::test_assembler::kLittleEndian;
|
||||
using google_breakpad::test_assembler::Label;
|
||||
using google_breakpad::test_assembler::Section;
|
||||
using std::string;
|
||||
using std::vector;
|
||||
using testing::_;
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче