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:
nealsid 2010-03-02 00:39:48 +00:00
parent 841ad48a37
commit de545c09d0
18 changed files with 1768 additions and 158 deletions

View file

@ -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

View file

@ -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;

View file

@ -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

View file

@ -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.

View file

@ -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;

View file

@ -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);

View file

@ -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;
}

View file

@ -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
};

View file

@ -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

View file

@ -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;
}

View file

@ -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

View file

@ -77,7 +77,8 @@
* Porting to other related platforms should not be difficult.
*/
#if (defined(__i386__) || defined(__x86_64__) || defined(__ARM_ARCH_3__) || \
defined(__mips__) || defined(__PPC__)) && defined(__linux)
defined(__mips__) || defined(__PPC__) || defined(__ARM_EABI__)) \
&& defined(__linux)
#ifndef SYS_CPLUSPLUS
#ifdef __cplusplus
@ -105,7 +106,6 @@ extern "C" {
/* Include definitions of the ABI currently in use. */
#include <sgidefs.h>
#endif
#endif
/* As glibc often provides subtly incompatible data structures (and implicit
@ -217,7 +217,8 @@ struct kernel_rusage {
};
struct siginfo;
#if defined(__i386__) || defined(__ARM_ARCH_3__) || defined(__PPC__)
#if defined(__i386__) || defined(__ARM_EABI__) || defined(__ARM_ARCH_3__) \
|| defined(__PPC__)
/* include/asm-{arm,i386,mips,ppc}/signal.h */
struct kernel_old_sigaction {
@ -354,7 +355,7 @@ struct kernel_stat64 {
#endif
/* include/asm-{arm,i386,mips,x86_64,ppc}/stat.h */
#if defined(__i386__) || defined(__ARM_ARCH_3__)
#if defined(__i386__) || defined(__ARM_ARCH_3__) || defined(__ARM_EABI__)
struct kernel_stat {
/* The kernel headers suggest that st_dev and st_rdev should be 32bit
* quantities encoding 12bit major and 20bit minor numbers in an interleaved
@ -520,7 +521,7 @@ struct kernel_statfs {
/* Definitions missing from the standard header files */
#ifndef O_DIRECTORY
#if defined(__ARM_ARCH_3__)
#if defined(__ARM_ARCH_3__) || defined(__ARM_EABI__)
#define O_DIRECTORY 0040000
#else
#define O_DIRECTORY 0200000
@ -641,7 +642,7 @@ struct kernel_statfs {
#define __NR_move_pages 317
#endif
/* End of i386 definitions */
#elif defined(__ARM_ARCH_3__)
#elif defined(__ARM_ARCH_3__) || defined(__ARM_EABI__)
#ifndef __NR_setresuid
#define __NR_setresuid (__NR_SYSCALL_BASE + 164)
#define __NR_setresgid (__NR_SYSCALL_BASE + 170)
@ -715,7 +716,7 @@ struct kernel_statfs {
#ifndef __NR_move_pages
#define __NR_move_pages (__NR_SYSCALL_BASE + 344)
#endif
/* End of ARM 3 definitions */
/* End of ARM 3/EABI definitions */
#elif defined(__x86_64__)
#ifndef __NR_setresuid
#define __NR_setresuid 117
@ -1087,7 +1088,8 @@ struct kernel_statfs {
#endif
#undef LSS_RETURN
#if (defined(__i386__) || defined(__x86_64__) || defined(__ARM_ARCH_3__))
#if (defined(__i386__) || defined(__x86_64__) || defined(__ARM_ARCH_3__) \
|| defined(__ARM_EABI__))
/* Failing system calls return a negative result in the range of
* -1..-4095. These are "errno" values with the sign inverted.
*/
@ -1526,11 +1528,6 @@ struct kernel_statfs {
return res;
}
#elif defined(__ARM_ARCH_3__)
/* Most definitions of _syscallX() neglect to mark "memory" as being
* clobbered. This causes problems with compilers, that do a better job
* at optimizing across __asm__ calls.
* So, we just have to redefine all fo the _syscallX() macros.
*/
#undef LSS_REG
#define LSS_REG(r,a) register long __r##r __asm__("r"#r) = (long)a
#undef LSS_BODY
@ -1646,6 +1643,135 @@ struct kernel_statfs {
}
LSS_RETURN(int, __res);
}
#elif defined(__ARM_EABI__)
/* Most definitions of _syscallX() neglect to mark "memory" as being
* clobbered. This causes problems with compilers, that do a better job
* at optimizing across __asm__ calls.
* So, we just have to redefine all fo the _syscallX() macros.
*/
#undef LSS_REG
#define LSS_REG(r,a) register long __r##r __asm__("r"#r) = (long)a
#undef LSS_BODY
#define LSS_BODY(type,name,args...) \
register long __res_r0 __asm__("r0"); \
long __res; \
__asm__ __volatile__ ("push {r7}\n" \
"mov r7, %1\n" \
"swi 0x0\n" \
"pop {r7}\n" \
: "=r"(__res_r0) \
: "i"(__NR_##name) , ## args \
: "lr", "memory"); \
__res = __res_r0; \
LSS_RETURN(type, __res)
#undef _syscall0
#define _syscall0(type, name) \
type LSS_NAME(name)() { \
LSS_BODY(type, name); \
}
#undef _syscall1
#define _syscall1(type, name, type1, arg1) \
type LSS_NAME(name)(type1 arg1) { \
LSS_REG(0, arg1); LSS_BODY(type, name, "r"(__r0)); \
}
#undef _syscall2
#define _syscall2(type, name, type1, arg1, type2, arg2) \
type LSS_NAME(name)(type1 arg1, type2 arg2) { \
LSS_REG(0, arg1); LSS_REG(1, arg2); \
LSS_BODY(type, name, "r"(__r0), "r"(__r1)); \
}
#undef _syscall3
#define _syscall3(type, name, type1, arg1, type2, arg2, type3, arg3) \
type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3) { \
LSS_REG(0, arg1); LSS_REG(1, arg2); LSS_REG(2, arg3); \
LSS_BODY(type, name, "r"(__r0), "r"(__r1), "r"(__r2)); \
}
#undef _syscall4
#define _syscall4(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4) \
type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4) { \
LSS_REG(0, arg1); LSS_REG(1, arg2); LSS_REG(2, arg3); \
LSS_REG(3, arg4); \
LSS_BODY(type, name, "r"(__r0), "r"(__r1), "r"(__r2), "r"(__r3)); \
}
#undef _syscall5
#define _syscall5(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \
type5,arg5) \
type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \
type5 arg5) { \
LSS_REG(0, arg1); LSS_REG(1, arg2); LSS_REG(2, arg3); \
LSS_REG(3, arg4); LSS_REG(4, arg5); \
LSS_BODY(type, name, "r"(__r0), "r"(__r1), "r"(__r2), "r"(__r3), \
"r"(__r4)); \
}
#undef _syscall6
#define _syscall6(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \
type5,arg5,type6,arg6) \
type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \
type5 arg5, type6 arg6) { \
LSS_REG(0, arg1); LSS_REG(1, arg2); LSS_REG(2, arg3); \
LSS_REG(3, arg4); LSS_REG(4, arg5); LSS_REG(5, arg6); \
LSS_BODY(type, name, "r"(__r0), "r"(__r1), "r"(__r2), "r"(__r3), \
"r"(__r4), "r"(__r5)); \
}
LSS_INLINE int LSS_NAME(clone)(int (*fn)(void *), void *child_stack,
int flags, void *arg, int *parent_tidptr,
void *newtls, int *child_tidptr) {
long __res;
{
register int __flags __asm__("r0") = flags;
register void *__stack __asm__("r1") = child_stack;
register void *__ptid __asm__("r2") = parent_tidptr;
register void *__tls __asm__("r3") = newtls;
register int *__ctid __asm__("r4") = child_tidptr;
__asm__ __volatile__(/* if (fn == NULL || child_stack == NULL)
* return -EINVAL;
*/
"cmp %2,#0\n"
"cmpne %3,#0\n"
"moveq %0,%1\n"
"beq 1f\n"
/* Push "arg" and "fn" onto the stack that will be
* used by the child.
*/
"str %5,[%3,#-4]!\n"
"str %2,[%3,#-4]!\n"
/* %r0 = syscall(%r0 = flags,
* %r1 = child_stack,
* %r2 = parent_tidptr,
* %r3 = newtls,
* %r4 = child_tidptr)
*/
"mov r7, %9\n"
"swi 0x0\n"
/* if (%r0 != 0)
* return %r0;
*/
"movs %0,r0\n"
"bne 1f\n"
/* In the child, now. Call "fn(arg)".
*/
"ldr r0,[sp, #4]\n"
"mov lr,pc\n"
"ldr pc,[sp]\n"
/* Call _exit(%r0).
*/
"mov r7, %10\n"
"swi 0x0\n"
"1:\n"
: "=r" (__res)
: "i"(-EINVAL),
"r"(fn), "r"(__stack), "r"(__flags), "r"(arg),
"r"(__ptid), "r"(__tls), "r"(__ctid),
"i"(__NR_clone), "i"(__NR_exit)
: "lr", "memory");
}
LSS_RETURN(int, __res);
}
#elif defined(__mips__)
#undef LSS_REG
#define LSS_REG(r,a) register unsigned long __r##r __asm__("$"#r) = \
@ -2082,8 +2208,10 @@ struct kernel_statfs {
LSS_INLINE _syscall0(pid_t, getppid)
LSS_INLINE _syscall2(int, getpriority, int, a,
int, b)
#if !defined(__ARM_EABI__)
LSS_INLINE _syscall2(int, getrlimit, int, r,
struct kernel_rlimit*, l)
#endif
LSS_INLINE _syscall1(pid_t, getsid, pid_t, p)
LSS_INLINE _syscall0(pid_t, _gettid)
LSS_INLINE _syscall5(int, setxattr, const char *,p,
@ -2237,6 +2365,7 @@ struct kernel_statfs {
}
#endif
#if defined(__x86_64__) || defined(__ARM_ARCH_3__) || \
defined(__ARM_EABI__) || \
(defined(__mips__) && _MIPS_SIM != _MIPS_SIM_ABI32)
LSS_INLINE _syscall4(pid_t, wait4, pid_t, p,
int*, s, int, o,
@ -2250,13 +2379,15 @@ struct kernel_statfs {
LSS_INLINE _syscall4(int, openat, int, d, const char *, p, int, f, int, m)
LSS_INLINE _syscall3(int, unlinkat, int, d, const char *, p, int, f)
#endif
#if defined(__i386__) || defined(__ARM_ARCH_3__)
#if defined(__i386__) || defined(__ARM_ARCH_3__) || defined(__ARM_EABI__)
#define __NR__setfsgid32 __NR_setfsgid32
#define __NR__setfsuid32 __NR_setfsuid32
#define __NR__setresgid32 __NR_setresgid32
#define __NR__setresuid32 __NR_setresuid32
#if defined(__ARM_EABI__)
LSS_INLINE _syscall2(int, ugetrlimit, int, r,
struct kernel_rlimit*, l)
#endif
LSS_INLINE _syscall1(int, _setfsgid32, gid_t, f)
LSS_INLINE _syscall1(int, _setfsuid32, uid_t, f)
LSS_INLINE _syscall3(int, _setresgid32, gid_t, r,
@ -2365,6 +2496,7 @@ struct kernel_statfs {
}
}
#if defined(__i386__) || defined(__ARM_ARCH_3__) || \
defined(__ARM_EABI__) || \
(defined(__mips__) && _MIPS_SIM == _MIPS_SIM_ABI32) || defined(__PPC__)
#define __NR__sigaction __NR_sigaction
#define __NR__sigpending __NR_sigpending
@ -2375,7 +2507,9 @@ struct kernel_statfs {
struct kernel_stat64 *, b)
LSS_INLINE _syscall5(int, _llseek, uint, fd, ulong, hi, ulong, lo,
loff_t *, res, uint, wh)
#if !defined(__ARM_EABI__)
LSS_INLINE _syscall1(void*, mmap, void*, a)
#endif
LSS_INLINE _syscall6(void*, mmap2, void*, s,
size_t, l, int, p,
int, f, int, d,
@ -2590,12 +2724,24 @@ struct kernel_statfs {
LSS_SC_BODY(4, int, 8, d, type, protocol, sv);
}
#endif
#if defined(__i386__) || defined(__ARM_ARCH_3__) || \
#if defined(__ARM_EABI__)
LSS_INLINE _syscall3(ssize_t, recvmsg, int, s, struct kernel_msghdr*, msg,
int, flags);
LSS_INLINE _syscall3(ssize_t, sendmsg, int, s, const struct kernel_msghdr*,
msg, int, flags);
LSS_INLINE _syscall6(ssize_t, sendto, int, s, const void*, buf, size_t, len,
int, falgs, const struct kernel_sockaddr*, to,
unsigned int, tolen);
LSS_INLINE _syscall2(int, shutdown, int, s, int, how);
LSS_INLINE _syscall3(int, socket, int, domain, int, type, int, protocol);
LSS_INLINE _syscall4(int, socketpair, int, d, int, type, int, protocol,
int*, sv);
#endif
#if defined(__i386__) || defined(__ARM_ARCH_3__) || \
(defined(__mips__) && _MIPS_SIM == _MIPS_SIM_ABI32)
#define __NR__socketcall __NR_socketcall
LSS_INLINE _syscall2(int, _socketcall, int, c,
va_list, a)
LSS_INLINE int LSS_NAME(socketcall)(int op, ...) {
int rc;
va_list ap;
@ -2604,29 +2750,24 @@ struct kernel_statfs {
va_end(ap);
return rc;
}
LSS_INLINE ssize_t LSS_NAME(recvmsg)(int s,struct kernel_msghdr *msg,
int flags){
return (ssize_t)LSS_NAME(socketcall)(17, s, msg, flags);
}
LSS_INLINE ssize_t LSS_NAME(sendmsg)(int s,
const struct kernel_msghdr *msg,
int flags) {
return (ssize_t)LSS_NAME(socketcall)(16, s, msg, flags);
}
LSS_INLINE ssize_t LSS_NAME(sendto)(int s, const void *buf, size_t len,
int flags,
const struct kernel_sockaddr *to,
unsigned int tolen) {
return (ssize_t)LSS_NAME(socketcall)(11, s, buf, len, flags, to, tolen);
}
LSS_INLINE int LSS_NAME(shutdown)(int s, int how) {
return LSS_NAME(socketcall)(13, s, how);
}
LSS_INLINE int LSS_NAME(socket)(int domain, int type, int protocol) {
return LSS_NAME(socketcall)(1, domain, type, protocol);
}
@ -2673,6 +2814,7 @@ struct kernel_statfs {
#endif
/* TODO(csilvers): see if ppc can/should support this as well */
#if defined(__i386__) || defined(__ARM_ARCH_3__) || \
defined(__ARM_EABI__) || \
(defined(__mips__) && _MIPS_SIM != _MIPS_SIM_ABI64)
#define __NR__statfs64 __NR_statfs64
#define __NR__fstatfs64 __NR_fstatfs64
@ -2743,8 +2885,13 @@ struct kernel_statfs {
switch (name) {
case _SC_OPEN_MAX: {
struct kernel_rlimit limit;
#if defined(__ARM_EABI__)
return LSS_NAME(ugetrlimit)(RLIMIT_NOFILE, &limit) < 0
? 8192 : limit.rlim_cur;
#else
return LSS_NAME(getrlimit)(RLIMIT_NOFILE, &limit) < 0
? 8192 : limit.rlim_cur;
? 8192 : limit.rlim_cur;
#endif
}
case _SC_PAGESIZE:
return __getpagesize();

View file

@ -9,6 +9,9 @@
/* Define to 1 if you have the <memory.h> header file. */
#undef HAVE_MEMORY_H
/* Define if you have POSIX threads libraries and header files. */
#undef HAVE_PTHREAD
/* Define to 1 if you have the <stdint.h> header file. */
#undef HAVE_STDINT_H
@ -30,6 +33,10 @@
/* Define to 1 if you have the <unistd.h> header file. */
#undef HAVE_UNISTD_H
/* Define to the sub-directory in which libtool stores uninstalled libraries.
*/
#undef LT_OBJDIR
/* Name of package */
#undef PACKAGE
@ -45,9 +52,16 @@
/* Define to the one symbol short name of this package. */
#undef PACKAGE_TARNAME
/* Define to the home page for this package. */
#undef PACKAGE_URL
/* Define to the version of this package. */
#undef PACKAGE_VERSION
/* Define to necessary symbol if this constant uses a non-standard name on
your system. */
#undef PTHREAD_CREATE_JOINABLE
/* Define to 1 if you have the ANSI C header files. */
#undef STDC_HEADERS