mirror of
https://git.suyu.dev/suyu/breakpad.git
synced 2025-12-31 11:44:48 +01:00
ARM support, with some build system changes to support x86-64, arm, and i386 in an autoconf style build in Linux. The O2 build for the unit tests is still broken but I'm checking this in to unblock people
A=nealsid R=ajwong, hannahtang, ted.mielczarek git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@541 4c0a9323-5329-0410-9bdc-e9ce6186880e
This commit is contained in:
parent
841ad48a37
commit
de545c09d0
18 changed files with 1768 additions and 158 deletions
|
|
@ -1,7 +1,7 @@
|
|||
CXX=g++
|
||||
CC=gcc
|
||||
|
||||
CXXFLAGS=-gstabs+ -I../../ -I../../testing/gtest/include -I../../testing -I../../third_party/linux/include -I../../testing/include -I../../testing/gtest -D_REENTRANT -m32 -Wall
|
||||
CXXFLAGS=-gstabs+ -I../../ -I../../testing/gtest/include -I../../testing -I../../third_party/linux/include -I../../testing/include -I../../testing/gtest -D_REENTRANT -Wall
|
||||
CFLAGS=$(CXXFLAGS)
|
||||
LDFLAGS=-lpthread
|
||||
|
||||
|
|
@ -37,7 +37,13 @@ LIB_C_SRC = ../../common/convert_UTF.c
|
|||
LIB_CC_OBJ=$(patsubst %.cc, $(OBJ_DIR)/%.o,$(LIB_CC_SRC))
|
||||
LIB_C_OBJ=$(patsubst %.c, $(OBJ_DIR)/%.o, $(LIB_C_SRC))
|
||||
|
||||
DUMPER_HELPER_TEST_C_SRC=minidump_writer/linux_dumper_unittest_helper.c
|
||||
|
||||
DUMPER_HELPER_TEST_C_OBJ=$(patsubst %.cc, $(OBJ_DIR)/%.o, \
|
||||
$(DUMPER_HELPER_TEST_C_SRC))
|
||||
|
||||
# Unit tests for client library
|
||||
|
||||
TEST_CC_SRC=handler/exception_handler_unittest.cc \
|
||||
minidump_writer/directory_reader_unittest.cc \
|
||||
minidump_writer/line_reader_unittest.cc \
|
||||
|
|
@ -72,9 +78,11 @@ SENDER_UNITTEST_BIN=$(BIN_DIR)/google_crashdump_uploader_test
|
|||
# Sender CLI tool binary
|
||||
SENDER_CLI_TOOL_BIN=$(BIN_DIR)/google_crashdump_uploader
|
||||
|
||||
DUMPER_HELPER_TEST_BIN=$(BIN_DIR)/linux_dumper_unittest_helper
|
||||
|
||||
.PHONY:all clean
|
||||
|
||||
all:$(BREAKPAD_LIBRARY) $(UNITTEST_BIN) $(SENDER_LIBRARY) $(SENDER_UNITTEST_BIN) $(SENDER_CLI_TOOL_BIN)
|
||||
all:$(BREAKPAD_LIBRARY) $(UNITTEST_BIN) #$(SENDER_LIBRARY) $(SENDER_UNITTEST_BIN) $(SENDER_CLI_TOOL_BIN)
|
||||
|
||||
check:$(UNITTEST_BIN) $(SENDER_UNITTEST_BIN)
|
||||
$(UNITTEST_BIN)
|
||||
|
|
@ -83,9 +91,12 @@ check:$(UNITTEST_BIN) $(SENDER_UNITTEST_BIN)
|
|||
$(BIN_DIR)/libbreakpad.a:$(LIB_CC_OBJ) $(LIB_C_OBJ)
|
||||
$(AR) rcs $@ $^
|
||||
|
||||
$(BIN_DIR)/linux_client_test:$(TEST_CC_OBJ) $(BREAKPAD_LIBRARY)
|
||||
$(BIN_DIR)/linux_dumper_unittest_helper:$(DUMPER_HELPER_TEST_C_OBJ)
|
||||
$(CXX) $(CXXFLAGS) $(LDFLAGS) $^ -o $@
|
||||
|
||||
$(BIN_DIR)/linux_client_test:$(TEST_CC_OBJ) $(BREAKPAD_LIBRARY) $(DUMPER_HELPER_TEST_BIN)
|
||||
$(CXX) $(CXXFLAGS) $(LDFLAGS) $(TEST_CC_OBJ) $(BREAKPAD_LIBRARY) -o $@
|
||||
|
||||
$(BIN_DIR)/libcrash_sender.a:$(SENDER_LIBRARY_OBJ)
|
||||
$(AR) rcs $@ $^
|
||||
|
||||
|
|
@ -100,4 +111,5 @@ $(BIN_DIR)/google_crashdump_uploader:$(SENDER_TOOL_OBJ) $(SENDER_LIBRARY) \
|
|||
clean:
|
||||
rm -f $(UNITTEST_BIN) $(BREAKPAD_LIBRARY) $(LIB_CC_OBJ) $(LIB_C_OBJ) \
|
||||
$(TEST_CC_OBJ) $(SENDER_LIBRARY_OBJ) $(SENDER_LIBRARY) \
|
||||
$(SENDER_TOOL_OBJ) $(SENDER_CLI_TOOL_BIN) $(SENDER_UNITTEST_BIN) core
|
||||
$(SENDER_TOOL_OBJ) $(SENDER_CLI_TOOL_BIN) $(SENDER_UNITTEST_BIN) \
|
||||
$(DUMPER_HELPER_TEST_BIN) core
|
||||
|
|
|
|||
|
|
@ -27,6 +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.
|
||||
|
||||
#include <stdio.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
|
|
@ -44,7 +45,6 @@ CrashGenerationClient::RequestDump(const void* blob, size_t blob_size)
|
|||
{
|
||||
int fds[2];
|
||||
sys_socketpair(AF_UNIX, SOCK_STREAM, 0, fds);
|
||||
|
||||
static const unsigned kControlMsgSize = CMSG_SPACE(sizeof(int));
|
||||
|
||||
struct kernel_msghdr msg;
|
||||
|
|
|
|||
|
|
@ -80,6 +80,9 @@
|
|||
#include <ucontext.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
#include "common/linux/linux_libc_support.h"
|
||||
#include "common/linux/linux_syscall_support.h"
|
||||
#include "common/linux/memory.h"
|
||||
|
|
@ -88,7 +91,7 @@
|
|||
|
||||
// A wrapper for the tgkill syscall: send a signal to a specific thread.
|
||||
static int tgkill(pid_t tgid, pid_t tid, int sig) {
|
||||
syscall(__NR_tgkill, tgid, tid, sig);
|
||||
return syscall(__NR_tgkill, tgid, tid, sig);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -145,7 +148,6 @@ void ExceptionHandler::Init(const std::string &dump_path,
|
|||
const int server_fd)
|
||||
{
|
||||
crash_handler_ = NULL;
|
||||
|
||||
if (0 <= server_fd)
|
||||
crash_generation_client_
|
||||
.reset(CrashGenerationClient::TryCreate(server_fd));
|
||||
|
|
@ -209,7 +211,11 @@ void ExceptionHandler::UninstallHandlers() {
|
|||
sigaction(old_handlers_[i].first, action, NULL);
|
||||
delete action;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&handler_stack_mutex_);
|
||||
std::vector<ExceptionHandler*>::iterator handler =
|
||||
std::find(handler_stack_->begin(), handler_stack_->end(), this);
|
||||
handler_stack_->erase(handler);
|
||||
pthread_mutex_unlock(&handler_stack_mutex_);
|
||||
old_handlers_.clear();
|
||||
}
|
||||
|
||||
|
|
@ -231,12 +237,15 @@ void ExceptionHandler::UpdateNextID() {
|
|||
}
|
||||
}
|
||||
|
||||
// void ExceptionHandler::set_crash_handler(HandlerCallback callback) {
|
||||
// crash_handler_ = callback;
|
||||
// }
|
||||
|
||||
// This function runs in a compromised context: see the top of the file.
|
||||
// Runs on the crashing thread.
|
||||
// static
|
||||
void ExceptionHandler::SignalHandler(int sig, siginfo_t* info, void* uc) {
|
||||
// All the exception signals are blocked at this point.
|
||||
|
||||
pthread_mutex_lock(&handler_stack_mutex_);
|
||||
|
||||
if (!handler_stack_->size()) {
|
||||
|
|
@ -288,18 +297,25 @@ bool ExceptionHandler::HandleSignal(int sig, siginfo_t* info, void* uc) {
|
|||
|
||||
// Allow ourselves to be dumped.
|
||||
sys_prctl(PR_SET_DUMPABLE, 1);
|
||||
|
||||
CrashContext context;
|
||||
memcpy(&context.siginfo, info, sizeof(siginfo_t));
|
||||
memcpy(&context.context, uc, sizeof(struct ucontext));
|
||||
memcpy(&context.float_state, ((struct ucontext *)uc)->uc_mcontext.fpregs,
|
||||
sizeof(context.float_state));
|
||||
#if !defined(__ARM_EABI__)
|
||||
// FP state is not part of user ABI on ARM Linux.
|
||||
struct ucontext *uc_ptr = (struct ucontext*)uc;
|
||||
if (uc_ptr->uc_mcontext.fpregs) {
|
||||
memcpy(&context.float_state,
|
||||
uc_ptr->uc_mcontext.fpregs,
|
||||
sizeof(context.float_state));
|
||||
}
|
||||
#endif
|
||||
context.tid = sys_gettid();
|
||||
|
||||
if (crash_handler_ && crash_handler_(&context, sizeof(context),
|
||||
callback_context_))
|
||||
return true;
|
||||
|
||||
if (crash_handler_ != NULL) {
|
||||
if (crash_handler_(&context, sizeof(context),
|
||||
callback_context_)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return GenerateDump(&context);
|
||||
}
|
||||
|
||||
|
|
@ -364,6 +380,7 @@ bool ExceptionHandler::WriteMinidump(const std::string &dump_path,
|
|||
}
|
||||
|
||||
bool ExceptionHandler::WriteMinidump() {
|
||||
#if !defined(__ARM_EABI__)
|
||||
// Allow ourselves to be dumped.
|
||||
sys_prctl(PR_SET_DUMPABLE, 1);
|
||||
|
||||
|
|
@ -378,6 +395,9 @@ bool ExceptionHandler::WriteMinidump() {
|
|||
bool success = GenerateDump(&context);
|
||||
UpdateNextID();
|
||||
return success;
|
||||
#else
|
||||
return false;
|
||||
#endif // !defined(__ARM_EABI__)
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@
|
|||
#include <string>
|
||||
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "client/linux/crash_generation/crash_generation_client.h"
|
||||
#include "processor/scoped_ptr.h"
|
||||
|
|
@ -42,6 +43,8 @@ struct sigaction;
|
|||
|
||||
namespace google_breakpad {
|
||||
|
||||
class ExceptionHandler;
|
||||
|
||||
// ExceptionHandler
|
||||
//
|
||||
// ExceptionHandler can write a minidump file when an exception occurs,
|
||||
|
|
@ -163,7 +166,10 @@ class ExceptionHandler {
|
|||
siginfo_t siginfo;
|
||||
pid_t tid; // the crashing thread.
|
||||
struct ucontext context;
|
||||
#if !defined(__ARM_EABI__)
|
||||
// #ifdef this out because FP state is not part of user ABI for Linux ARM.
|
||||
struct _libc_fpstate float_state;
|
||||
#endif
|
||||
};
|
||||
|
||||
// Returns whether out-of-process dump generation is used or not.
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@
|
|||
#include <sys/socket.h>
|
||||
#include <sys/uio.h>
|
||||
|
||||
#include "client/linux/handler//exception_handler.h"
|
||||
#include "client/linux/handler/exception_handler.h"
|
||||
#include "client/linux/minidump_writer/minidump_writer.h"
|
||||
#include "common/linux/eintr_wrapper.h"
|
||||
#include "common/linux/linux_libc_support.h"
|
||||
|
|
@ -112,8 +112,8 @@ TEST(ExceptionHandlerTest, ChildCrash) {
|
|||
ASSERT_TRUE(pfd.revents & POLLIN);
|
||||
|
||||
uint32_t len;
|
||||
ASSERT_EQ(read(fds[0], &len, sizeof(len)), sizeof(len));
|
||||
ASSERT_LT(len, 2048);
|
||||
ASSERT_EQ(read(fds[0], &len, sizeof(len)), (ssize_t)sizeof(len));
|
||||
ASSERT_LT(len, (uint32_t)2048);
|
||||
char* filename = reinterpret_cast<char*>(malloc(len + 1));
|
||||
ASSERT_EQ(read(fds[0], filename, len), len);
|
||||
filename[len] = 0;
|
||||
|
|
@ -137,12 +137,10 @@ CrashHandler(const void* crash_context, size_t crash_context_size,
|
|||
const int fd = (intptr_t) context;
|
||||
int fds[2];
|
||||
pipe(fds);
|
||||
|
||||
struct kernel_msghdr msg = {0};
|
||||
struct kernel_iovec iov;
|
||||
iov.iov_base = const_cast<void*>(crash_context);
|
||||
iov.iov_len = crash_context_size;
|
||||
|
||||
msg.msg_iov = &iov;
|
||||
msg.msg_iovlen = 1;
|
||||
char cmsg[kControlMsgSize];
|
||||
|
|
@ -183,11 +181,10 @@ TEST(ExceptionHandlerTest, ExternalDumper) {
|
|||
const pid_t child = fork();
|
||||
if (child == 0) {
|
||||
close(fds[0]);
|
||||
ExceptionHandler handler("/tmp", NULL, NULL, (void*) fds[1], true);
|
||||
ExceptionHandler handler("/tmp1", NULL, NULL, (void*) fds[1], true);
|
||||
handler.set_crash_handler(CrashHandler);
|
||||
*reinterpret_cast<int*>(NULL) = 0;
|
||||
}
|
||||
|
||||
close(fds[1]);
|
||||
struct msghdr msg = {0};
|
||||
struct iovec iov;
|
||||
|
|
|
|||
|
|
@ -69,9 +69,9 @@ TEST(LineReaderTest, OneLineTerminated) {
|
|||
LineReader reader(fd);
|
||||
|
||||
const char *line;
|
||||
unsigned len;
|
||||
unsigned int len;
|
||||
ASSERT_TRUE(reader.GetNextLine(&line, &len));
|
||||
ASSERT_EQ(len, 1);
|
||||
ASSERT_EQ(len, (unsigned int)1);
|
||||
ASSERT_EQ(line[0], 'a');
|
||||
ASSERT_EQ(line[1], 0);
|
||||
reader.PopLine(len);
|
||||
|
|
@ -90,7 +90,7 @@ TEST(LineReaderTest, OneLine) {
|
|||
const char *line;
|
||||
unsigned len;
|
||||
ASSERT_TRUE(reader.GetNextLine(&line, &len));
|
||||
ASSERT_EQ(len, 1);
|
||||
ASSERT_EQ(len, (unsigned)1);
|
||||
ASSERT_EQ(line[0], 'a');
|
||||
ASSERT_EQ(line[1], 0);
|
||||
reader.PopLine(len);
|
||||
|
|
@ -109,13 +109,13 @@ TEST(LineReaderTest, TwoLinesTerminated) {
|
|||
const char *line;
|
||||
unsigned len;
|
||||
ASSERT_TRUE(reader.GetNextLine(&line, &len));
|
||||
ASSERT_EQ(len, 1);
|
||||
ASSERT_EQ(len, (unsigned)1);
|
||||
ASSERT_EQ(line[0], 'a');
|
||||
ASSERT_EQ(line[1], 0);
|
||||
reader.PopLine(len);
|
||||
|
||||
ASSERT_TRUE(reader.GetNextLine(&line, &len));
|
||||
ASSERT_EQ(len, 1);
|
||||
ASSERT_EQ(len, (unsigned)1);
|
||||
ASSERT_EQ(line[0], 'b');
|
||||
ASSERT_EQ(line[1], 0);
|
||||
reader.PopLine(len);
|
||||
|
|
@ -134,13 +134,13 @@ TEST(LineReaderTest, TwoLines) {
|
|||
const char *line;
|
||||
unsigned len;
|
||||
ASSERT_TRUE(reader.GetNextLine(&line, &len));
|
||||
ASSERT_EQ(len, 1);
|
||||
ASSERT_EQ(len, (unsigned)1);
|
||||
ASSERT_EQ(line[0], 'a');
|
||||
ASSERT_EQ(line[1], 0);
|
||||
reader.PopLine(len);
|
||||
|
||||
ASSERT_TRUE(reader.GetNextLine(&line, &len));
|
||||
ASSERT_EQ(len, 1);
|
||||
ASSERT_EQ(len, (unsigned)1);
|
||||
ASSERT_EQ(line[0], 'b');
|
||||
ASSERT_EQ(line[1], 0);
|
||||
reader.PopLine(len);
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@ namespace google_breakpad {
|
|||
|
||||
LinuxDumper::LinuxDumper(int pid)
|
||||
: pid_(pid),
|
||||
threads_suspened_(false),
|
||||
threads_suspended_(false),
|
||||
threads_(&allocator_, 8),
|
||||
mappings_(&allocator_) {
|
||||
}
|
||||
|
|
@ -96,22 +96,22 @@ bool LinuxDumper::Init() {
|
|||
}
|
||||
|
||||
bool LinuxDumper::ThreadsSuspend() {
|
||||
if (threads_suspened_)
|
||||
if (threads_suspended_)
|
||||
return true;
|
||||
bool good = true;
|
||||
for (size_t i = 0; i < threads_.size(); ++i)
|
||||
good &= SuspendThread(threads_[i]);
|
||||
threads_suspened_ = true;
|
||||
threads_suspended_ = true;
|
||||
return good;
|
||||
}
|
||||
|
||||
bool LinuxDumper::ThreadsResume() {
|
||||
if (!threads_suspened_)
|
||||
if (!threads_suspended_)
|
||||
return false;
|
||||
bool good = true;
|
||||
for (size_t i = 0; i < threads_.size(); ++i)
|
||||
good &= ResumeThread(threads_[i]);
|
||||
threads_suspened_ = false;
|
||||
threads_suspended_ = false;
|
||||
return good;
|
||||
}
|
||||
|
||||
|
|
@ -312,9 +312,9 @@ bool LinuxDumper::EnumerateThreads(wasteful_vector<pid_t>* result) const {
|
|||
}
|
||||
|
||||
// Read thread info from /proc/$pid/status.
|
||||
// Fill out the |tgid|, |ppid| and |pid| members of |info|. If unavailible,
|
||||
// Fill out the |tgid|, |ppid| and |pid| members of |info|. If unavailable,
|
||||
// these members are set to -1. Returns true iff all three members are
|
||||
// availible.
|
||||
// available.
|
||||
bool LinuxDumper::ThreadInfoGet(pid_t tid, ThreadInfo* info) {
|
||||
assert(info != NULL);
|
||||
char status_path[80];
|
||||
|
|
@ -371,6 +371,8 @@ bool LinuxDumper::ThreadInfoGet(pid_t tid, ThreadInfo* info) {
|
|||
memcpy(&stack_pointer, &info->regs.esp, sizeof(info->regs.esp));
|
||||
#elif defined(__x86_64)
|
||||
memcpy(&stack_pointer, &info->regs.rsp, sizeof(info->regs.rsp));
|
||||
#elif defined(__ARM_EABI__)
|
||||
memcpy(&stack_pointer, &info->regs.uregs[R13], sizeof(info->regs.uregs[R13]));
|
||||
#else
|
||||
#error "This code hasn't been ported to your platform yet."
|
||||
#endif
|
||||
|
|
@ -387,13 +389,9 @@ bool LinuxDumper::ThreadInfoGet(pid_t tid, ThreadInfo* info) {
|
|||
// unwind. So we just grab, up to, 32k of stack.
|
||||
bool LinuxDumper::GetStackInfo(const void** stack, size_t* stack_len,
|
||||
uintptr_t int_stack_pointer) {
|
||||
#if defined(__i386) || defined(__x86_64)
|
||||
static const bool stack_grows_down = true;
|
||||
static const uintptr_t page_size = 4096;
|
||||
#else
|
||||
#error "This code has not been ported to your platform yet."
|
||||
#endif
|
||||
// Move the stack pointer to the bottom of the page that it's in.
|
||||
const uintptr_t page_size = getpagesize();
|
||||
|
||||
uint8_t* const stack_pointer =
|
||||
reinterpret_cast<uint8_t*>(int_stack_pointer & ~(page_size - 1));
|
||||
|
||||
|
|
@ -403,26 +401,19 @@ bool LinuxDumper::GetStackInfo(const void** stack, size_t* stack_len,
|
|||
const MappingInfo* mapping = FindMapping(stack_pointer);
|
||||
if (!mapping)
|
||||
return false;
|
||||
if (stack_grows_down) {
|
||||
const ptrdiff_t offset = stack_pointer - (uint8_t*) mapping->start_addr;
|
||||
const ptrdiff_t distance_to_end =
|
||||
static_cast<ptrdiff_t>(mapping->size) - offset;
|
||||
*stack_len = distance_to_end > kStackToCapture ?
|
||||
kStackToCapture : distance_to_end;
|
||||
*stack = stack_pointer;
|
||||
} else {
|
||||
const ptrdiff_t offset = stack_pointer - (uint8_t*) mapping->start_addr;
|
||||
*stack_len = offset > kStackToCapture ? kStackToCapture : offset;
|
||||
*stack = stack_pointer - *stack_len;
|
||||
}
|
||||
|
||||
const ptrdiff_t offset = stack_pointer - (uint8_t*) mapping->start_addr;
|
||||
const ptrdiff_t distance_to_end =
|
||||
static_cast<ptrdiff_t>(mapping->size) - offset;
|
||||
*stack_len = distance_to_end > kStackToCapture ?
|
||||
kStackToCapture : distance_to_end;
|
||||
*stack = stack_pointer;
|
||||
return true;
|
||||
}
|
||||
|
||||
// static
|
||||
void LinuxDumper::CopyFromProcess(void* dest, pid_t child, const void* src,
|
||||
size_t length) {
|
||||
unsigned long tmp;
|
||||
unsigned long tmp = 55;
|
||||
size_t done = 0;
|
||||
static const size_t word_size = sizeof(tmp);
|
||||
uint8_t* const local = (uint8_t*) dest;
|
||||
|
|
@ -430,8 +421,9 @@ void LinuxDumper::CopyFromProcess(void* dest, pid_t child, const void* src,
|
|||
|
||||
while (done < length) {
|
||||
const size_t l = length - done > word_size ? word_size : length - done;
|
||||
if (sys_ptrace(PTRACE_PEEKDATA, child, remote + done, &tmp) == -1)
|
||||
if (sys_ptrace(PTRACE_PEEKDATA, child, remote + done, &tmp) == -1) {
|
||||
tmp = 0;
|
||||
}
|
||||
memcpy(local + done, &tmp, l);
|
||||
done += l;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ namespace google_breakpad {
|
|||
typedef typeof(((struct user*) 0)->u_debugreg[0]) debugreg_t;
|
||||
|
||||
// Typedef for our parsing of the auxv variables in /proc/pid/auxv.
|
||||
#if defined(__i386)
|
||||
#if defined(__i386) || defined(__ARM_EABI__)
|
||||
typedef Elf32_auxv_t elf_aux_entry;
|
||||
#elif defined(__x86_64__)
|
||||
typedef Elf64_auxv_t elf_aux_entry;
|
||||
|
|
@ -64,16 +64,20 @@ struct ThreadInfo {
|
|||
const void* stack; // pointer to the stack area
|
||||
size_t stack_len; // length of the stack to copy
|
||||
|
||||
user_regs_struct regs;
|
||||
user_fpregs_struct fpregs;
|
||||
#if defined(__i386)
|
||||
user_fpxregs_struct fpxregs;
|
||||
#endif
|
||||
|
||||
#if defined(__i386) || defined(__x86_64)
|
||||
|
||||
user_regs_struct regs;
|
||||
user_fpregs_struct fpregs;
|
||||
static const unsigned kNumDebugRegisters = 8;
|
||||
debugreg_t dregs[8];
|
||||
#if defined(__i386)
|
||||
user_fpxregs_struct fpxregs;
|
||||
#endif // defined(__i386)
|
||||
|
||||
#elif defined(__ARM_EABI__)
|
||||
// Mimicking how strace does this(see syscall.c, search for GETREGS)
|
||||
struct user_regs regs;
|
||||
struct user_fpregs fpregs;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
|
@ -141,7 +145,7 @@ class LinuxDumper {
|
|||
|
||||
mutable PageAllocator allocator_;
|
||||
|
||||
bool threads_suspened_;
|
||||
bool threads_suspended_;
|
||||
wasteful_vector<pid_t> threads_; // the ids of all the threads
|
||||
wasteful_vector<MappingInfo*> mappings_; // info from /proc/<pid>/maps
|
||||
};
|
||||
|
|
|
|||
|
|
@ -29,10 +29,13 @@
|
|||
|
||||
#include <limits.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "breakpad_googletest_includes.h"
|
||||
#include "client/linux/minidump_writer/linux_dumper.h"
|
||||
#include "common/linux/file_id.h"
|
||||
#include "breakpad_googletest_includes.h"
|
||||
#include "common/linux/memory.h"
|
||||
|
||||
using namespace google_breakpad;
|
||||
|
||||
|
|
@ -67,7 +70,7 @@ TEST(LinuxDumperTest, ThreadList) {
|
|||
LinuxDumper dumper(getpid());
|
||||
ASSERT_TRUE(dumper.Init());
|
||||
|
||||
ASSERT_GE(dumper.threads().size(), 1);
|
||||
ASSERT_GE(dumper.threads().size(), (size_t)1);
|
||||
bool found = false;
|
||||
for (size_t i = 0; i < dumper.threads().size(); ++i) {
|
||||
if (dumper.threads()[i] == getpid()) {
|
||||
|
|
@ -77,6 +80,55 @@ TEST(LinuxDumperTest, ThreadList) {
|
|||
}
|
||||
}
|
||||
|
||||
TEST(LinuxDumperTest, VerifyStackReadWithMultipleThreads) {
|
||||
static const int kNumberOfThreadsInHelperProgram = 5;
|
||||
char kNumberOfThreadsArgument[2];
|
||||
sprintf(kNumberOfThreadsArgument, "%d", kNumberOfThreadsInHelperProgram);
|
||||
|
||||
pid_t child_pid = fork();
|
||||
if (child_pid == 0) {
|
||||
// Set the number of threads
|
||||
execl("src/client/linux/linux_dumper_unittest_helper",
|
||||
"linux_dumper_unittest_helper",
|
||||
kNumberOfThreadsArgument,
|
||||
NULL);
|
||||
// Kill if we get here.
|
||||
printf("Errno from exec: %d", errno);
|
||||
FAIL() << "Exec failed: " << strerror(errno);
|
||||
exit(0);
|
||||
}
|
||||
// The sleep is flaky, but prevents us from reading
|
||||
// the child process before all threads have been created.
|
||||
sleep(1);
|
||||
LinuxDumper dumper(child_pid);
|
||||
EXPECT_TRUE(dumper.Init());
|
||||
EXPECT_EQ((size_t)kNumberOfThreadsInHelperProgram, dumper.threads().size());
|
||||
EXPECT_TRUE(dumper.ThreadsSuspend());
|
||||
|
||||
ThreadInfo one_thread;
|
||||
for(size_t i = 0; i < dumper.threads().size(); ++i) {
|
||||
EXPECT_TRUE(dumper.ThreadInfoGet(dumper.threads()[i], &one_thread));
|
||||
// We know the threads are in a function which has allocated exactly
|
||||
// one word off the stack to store its thread id.
|
||||
#if defined(__ARM_EABI__)
|
||||
void* process_tid_location = (void *)(one_thread.regs.uregs[11] - 8);
|
||||
#elif defined(__i386)
|
||||
void* process_tid_location = (void *)(one_thread.regs.ebp - 4);
|
||||
#elif defined(__x86_64)
|
||||
void* process_tid_location = (void *)(one_thread.regs.rbp - 4);
|
||||
#else
|
||||
#error Platform not supported!
|
||||
#endif
|
||||
pid_t one_thread_id;
|
||||
dumper.CopyFromProcess(&one_thread_id,
|
||||
dumper.threads()[i],
|
||||
process_tid_location,
|
||||
4);
|
||||
EXPECT_EQ(dumper.threads()[i], one_thread_id);
|
||||
}
|
||||
kill(child_pid, SIGKILL);
|
||||
}
|
||||
|
||||
TEST(LinuxDumperTest, BuildProcPath) {
|
||||
const pid_t pid = getpid();
|
||||
LinuxDumper dumper(pid);
|
||||
|
|
@ -106,28 +158,29 @@ TEST(LinuxDumperTest, BuildProcPath) {
|
|||
#endif
|
||||
}
|
||||
|
||||
#if !defined(__ARM_EABI__)
|
||||
TEST(LinuxDumperTest, MappingsIncludeLinuxGate) {
|
||||
LinuxDumper dumper(getpid());
|
||||
ASSERT_TRUE(dumper.Init());
|
||||
|
||||
void* linux_gate_loc = dumper.FindBeginningOfLinuxGateSharedLibrary(getpid());
|
||||
if (linux_gate_loc) {
|
||||
bool found_linux_gate = false;
|
||||
ASSERT_TRUE(linux_gate_loc);
|
||||
bool found_linux_gate = false;
|
||||
|
||||
const wasteful_vector<MappingInfo*> mappings = dumper.mappings();
|
||||
const MappingInfo* mapping;
|
||||
for (unsigned i = 0; i < mappings.size(); ++i) {
|
||||
mapping = mappings[i];
|
||||
if (!strcmp(mapping->name, kLinuxGateLibraryName)) {
|
||||
found_linux_gate = true;
|
||||
break;
|
||||
}
|
||||
const wasteful_vector<MappingInfo*> mappings = dumper.mappings();
|
||||
const MappingInfo* mapping;
|
||||
for (unsigned i = 0; i < mappings.size(); ++i) {
|
||||
mapping = mappings[i];
|
||||
if (!strcmp(mapping->name, kLinuxGateLibraryName)) {
|
||||
found_linux_gate = true;
|
||||
break;
|
||||
}
|
||||
EXPECT_TRUE(found_linux_gate);
|
||||
EXPECT_EQ(linux_gate_loc, reinterpret_cast<void*>(mapping->start_addr));
|
||||
EXPECT_EQ(0, memcmp(linux_gate_loc, ELFMAG, SELFMAG));
|
||||
}
|
||||
EXPECT_TRUE(found_linux_gate);
|
||||
EXPECT_EQ(linux_gate_loc, reinterpret_cast<void*>(mapping->start_addr));
|
||||
EXPECT_EQ(0, memcmp(linux_gate_loc, ELFMAG, SELFMAG));
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST(LinuxDumperTest, FileIDsMatch) {
|
||||
// Calculate the File ID of our binary using both
|
||||
|
|
|
|||
|
|
@ -0,0 +1,64 @@
|
|||
// 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.
|
||||
//
|
||||
// Helper program for the linux_dumper class, which creates a bunch of
|
||||
// threads. The first word of each thread's stack is set to the thread
|
||||
// id.
|
||||
|
||||
#include <pthread.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#pragma GCC optimize ("O0")
|
||||
void *thread_function(void *data) __attribute__((noinline, optimize("O2")));
|
||||
|
||||
void *thread_function(void *data) {
|
||||
pid_t thread_id = syscall(SYS_gettid);
|
||||
while (true) ;
|
||||
asm("");
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
int num_threads = atoi(argv[1]);
|
||||
if (num_threads < 1) {
|
||||
fprintf(stderr, "ERROR: number of threads is 0");
|
||||
return 1;
|
||||
}
|
||||
pthread_t threads[num_threads];
|
||||
pthread_attr_t thread_attributes;
|
||||
pthread_attr_init(&thread_attributes);
|
||||
pthread_attr_setdetachstate(&thread_attributes, PTHREAD_CREATE_DETACHED);
|
||||
for (int i = 1; i < num_threads; i++) {
|
||||
pthread_create(&threads[i], &thread_attributes, &thread_function, NULL);
|
||||
}
|
||||
thread_function(NULL);
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -61,7 +61,7 @@
|
|||
|
||||
#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/linux_dumper.h"
|
||||
#include "common/linux/linux_libc_support.h"
|
||||
#include "common/linux/linux_syscall_support.h"
|
||||
|
||||
|
|
@ -307,6 +307,54 @@ static void CPUFillFromUContext(MDRawContextAMD64 *out, const ucontext *uc,
|
|||
memcpy(&out->flt_save.xmm_registers, &fpregs->_xmm, 16 * 16);
|
||||
}
|
||||
|
||||
#elif defined(__ARMEL__)
|
||||
typedef MDRawContextARM RawContextCPU;
|
||||
|
||||
static void CPUFillFromThreadInfo(MDRawContextARM *out,
|
||||
const google_breakpad::ThreadInfo &info) {
|
||||
out->context_flags = MD_CONTEXT_ARM_FULL;
|
||||
|
||||
for (int i = 0; i < MD_CONTEXT_ARM_GPR_COUNT; ++i)
|
||||
out->iregs[i] = info.regs.uregs[i];
|
||||
// No CPSR register in ThreadInfo(it's not accessible via ptrace)
|
||||
out->cpsr = 0;
|
||||
out->float_save.fpscr = info.fpregs.fpsr |
|
||||
(static_cast<u_int64_t>(info.fpregs.fpcr) << 32);
|
||||
//TODO: sort this out, actually collect floating point registers
|
||||
memset(&out->float_save.regs, 0, sizeof(out->float_save.regs));
|
||||
memset(&out->float_save.extra, 0, sizeof(out->float_save.extra));
|
||||
}
|
||||
|
||||
static void CPUFillFromUContext(MDRawContextARM *out, const ucontext *uc,
|
||||
const struct _libc_fpstate* fpregs) {
|
||||
out->context_flags = MD_CONTEXT_ARM_FULL;
|
||||
|
||||
out->iregs[0] = uc->uc_mcontext.arm_r0;
|
||||
out->iregs[1] = uc->uc_mcontext.arm_r1;
|
||||
out->iregs[2] = uc->uc_mcontext.arm_r2;
|
||||
out->iregs[3] = uc->uc_mcontext.arm_r3;
|
||||
out->iregs[4] = uc->uc_mcontext.arm_r4;
|
||||
out->iregs[5] = uc->uc_mcontext.arm_r5;
|
||||
out->iregs[6] = uc->uc_mcontext.arm_r6;
|
||||
out->iregs[7] = uc->uc_mcontext.arm_r7;
|
||||
out->iregs[8] = uc->uc_mcontext.arm_r8;
|
||||
out->iregs[9] = uc->uc_mcontext.arm_r9;
|
||||
out->iregs[10] = uc->uc_mcontext.arm_r10;
|
||||
|
||||
out->iregs[11] = uc->uc_mcontext.arm_fp;
|
||||
out->iregs[12] = uc->uc_mcontext.arm_ip;
|
||||
out->iregs[13] = uc->uc_mcontext.arm_sp;
|
||||
out->iregs[14] = uc->uc_mcontext.arm_lr;
|
||||
out->iregs[15] = uc->uc_mcontext.arm_pc;
|
||||
|
||||
out->cpsr = uc->uc_mcontext.arm_cpsr;
|
||||
|
||||
//TODO: fix this after fixing ExceptionHandler
|
||||
out->float_save.fpscr = 0;
|
||||
memset(&out->float_save.regs, 0, sizeof(out->float_save.regs));
|
||||
memset(&out->float_save.extra, 0, sizeof(out->float_save.extra));
|
||||
}
|
||||
|
||||
#else
|
||||
#error "This code has not been ported to your platform yet."
|
||||
#endif
|
||||
|
|
@ -321,7 +369,12 @@ class MinidumpWriter {
|
|||
: filename_(filename),
|
||||
siginfo_(&context->siginfo),
|
||||
ucontext_(&context->context),
|
||||
#if !defined(__ARM_EABI__)
|
||||
float_state_(&context->float_state),
|
||||
#else
|
||||
//TODO: fix this after fixing ExceptionHandler
|
||||
float_state_(NULL),
|
||||
#endif
|
||||
crashing_tid_(context->tid),
|
||||
dumper_(crashing_pid) {
|
||||
}
|
||||
|
|
@ -612,6 +665,10 @@ class MinidumpWriter {
|
|||
uintptr_t GetStackPointer() {
|
||||
return ucontext_->uc_mcontext.gregs[REG_RSP];
|
||||
}
|
||||
#elif defined(__ARM_EABI__)
|
||||
uintptr_t GetStackPointer() {
|
||||
return ucontext_->uc_mcontext.arm_sp;
|
||||
}
|
||||
#else
|
||||
#error "This code has not been ported to your platform yet."
|
||||
#endif
|
||||
|
|
@ -644,6 +701,8 @@ class MinidumpWriter {
|
|||
MD_CPU_ARCHITECTURE_X86;
|
||||
#elif defined(__x86_64)
|
||||
MD_CPU_ARCHITECTURE_AMD64;
|
||||
#elif defined(__arm__)
|
||||
MD_CPU_ARCHITECTURE_ARM;
|
||||
#else
|
||||
#error "Unknown CPU arch"
|
||||
#endif
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue