Add support for building the Linux client code using the Android NDK

r=mwu at http://breakpad.appspot.com/212001/show

git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@716 4c0a9323-5329-0410-9bdc-e9ce6186880e
This commit is contained in:
ted.mielczarek 2010-10-20 15:51:38 +00:00
parent 9c30407f7f
commit cfc8628092
15 changed files with 248 additions and 26 deletions

2
configure vendored
View file

@ -5092,7 +5092,7 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu
# Only build Linux client libs when compiling for Linux
case $host in
*-*-linux*)
*-*-linux* | *-android* )
LINUX_HOST=true
;;
esac

View file

@ -52,7 +52,7 @@ AX_PTHREAD
# Only build Linux client libs when compiling for Linux
case $host in
*-*-linux*)
*-*-linux* | *-android* )
LINUX_HOST=true
;;
esac

View file

@ -0,0 +1,44 @@
// 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.
// The Android NDK doesn't have link.h. Fortunately, the only thing
// that Breakpad uses from it is the ElfW macro, so define it here.
#ifndef GOOGLE_BREAKPAD_CLIENT_LINUX_ANDROID_LINK_H_
#define GOOGLE_BREAKPAD_CLIENT_LINUX_ANDROID_LINK_H_
#include <sys/exec_elf.h>
#ifndef ElfW
#define ElfW(type) _ElfW (Elf, ELFSIZE, type)
#define _ElfW(e,w,t) _ElfW_1 (e, w, _##t)
#define _ElfW_1(e,w,t) e##w##t
#endif
#endif // GOOGLE_BREAKPAD_CLIENT_LINUX_ANDROID_LINK_H_

View file

@ -0,0 +1,77 @@
// Copyright (c) 2009, 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.
// Android runs a fairly new Linux kernel, so signal info is there,
// but the NDK doesn't have the structs defined, so define
// them here.
// Adapted from platform-linux.cc in V8
#ifndef GOOGLE_BREAKPAD_CLIENT_LINUX_ANDROID_UCONTEXT_H_
#define GOOGLE_BREAKPAD_CLIENT_LINUX_ANDROID_UCONTEXT_H_
#include <signal.h>
#if !defined(__GLIBC__) && (defined(__arm__) || defined(__thumb__))
struct sigcontext {
uint32_t trap_no;
uint32_t error_code;
uint32_t oldmask;
uint32_t arm_r0;
uint32_t arm_r1;
uint32_t arm_r2;
uint32_t arm_r3;
uint32_t arm_r4;
uint32_t arm_r5;
uint32_t arm_r6;
uint32_t arm_r7;
uint32_t arm_r8;
uint32_t arm_r9;
uint32_t arm_r10;
uint32_t arm_fp;
uint32_t arm_ip;
uint32_t arm_sp;
uint32_t arm_lr;
uint32_t arm_pc;
uint32_t arm_cpsr;
uint32_t fault_address;
};
typedef uint32_t __sigset_t;
typedef struct sigcontext mcontext_t;
typedef struct ucontext {
uint32_t uc_flags;
struct ucontext* uc_link;
stack_t uc_stack;
mcontext_t uc_mcontext;
__sigset_t uc_sigmask;
} ucontext_t;
#endif
#endif // GOOGLE_BREAKPAD_CLIENT_LINUX_ANDROID_UCONTEXT_H_

View file

@ -73,12 +73,18 @@
#include <stdio.h>
#include <sys/mman.h>
#include <sys/prctl.h>
#if !defined(__ANDROID__)
#include <sys/signal.h>
#endif
#include <sys/syscall.h>
#if !defined(__ANDROID__)
#include <sys/ucontext.h>
#include <sys/user.h>
#endif
#include <sys/wait.h>
#if !defined(__ANDROID__)
#include <ucontext.h>
#endif
#include <unistd.h>
#include <algorithm>
@ -186,7 +192,7 @@ bool ExceptionHandler::InstallHandlers() {
stack.ss_sp = signal_stack;
stack.ss_size = kSigStackSize;
if (sigaltstack(&stack, NULL) == -1)
if (sys_sigaltstack(&stack, NULL) == -1)
return false;
struct sigaction sa;
@ -322,7 +328,7 @@ bool ExceptionHandler::HandleSignal(int sig, siginfo_t* info, void* uc) {
return false;
// Allow ourselves to be dumped.
prctl(PR_SET_DUMPABLE, 1);
sys_prctl(PR_SET_DUMPABLE, 1);
CrashContext context;
memcpy(&context.siginfo, info, sizeof(siginfo_t));
memcpy(&context.context, uc, sizeof(struct ucontext));

View file

@ -33,9 +33,13 @@
#include <vector>
#include <string>
#include <pthread.h>
#include <signal.h>
#include <stdio.h>
#if defined(__ANDROID__)
#include "client/linux/android_ucontext.h"
#endif
#include "client/linux/crash_generation/crash_generation_client.h"
#include "processor/scoped_ptr.h"

View file

@ -36,6 +36,7 @@
#include <sys/poll.h>
#include <sys/socket.h>
#include <sys/uio.h>
#include <sys/wait.h>
#include "breakpad_googletest_includes.h"
#include "client/linux/handler/exception_handler.h"
@ -47,6 +48,12 @@
using namespace google_breakpad;
#if !defined(__ANDROID__)
#define TEMPDIR "/tmp"
#else
#define TEMPDIR "/data/local/tmp"
#endif
static void sigchld_handler(int signo) { }
class ExceptionHandlerTest : public ::testing::Test {
@ -67,7 +74,7 @@ class ExceptionHandlerTest : public ::testing::Test {
};
TEST(ExceptionHandlerTest, Simple) {
ExceptionHandler handler("/tmp", NULL, NULL, NULL, true);
ExceptionHandler handler(TEMPDIR, NULL, NULL, NULL, true);
}
static bool DoneCallback(const char* dump_path,
@ -93,7 +100,7 @@ TEST(ExceptionHandlerTest, ChildCrash) {
const pid_t child = fork();
if (child == 0) {
close(fds[0]);
ExceptionHandler handler("/tmp", NULL, DoneCallback, (void*) fds[1],
ExceptionHandler handler(TEMPDIR, NULL, DoneCallback, (void*) fds[1],
true);
*reinterpret_cast<int*>(NULL) = 0;
}
@ -121,7 +128,7 @@ TEST(ExceptionHandlerTest, ChildCrash) {
filename[len] = 0;
close(fds[0]);
const std::string minidump_filename = std::string("/tmp/") + filename +
const std::string minidump_filename = std::string(TEMPDIR) + "/" + filename +
".dmp";
struct stat st;
@ -146,7 +153,7 @@ TEST(ExceptionHandlerTest, InstructionPointerMemory) {
const pid_t child = fork();
if (child == 0) {
close(fds[0]);
ExceptionHandler handler("/tmp", NULL, DoneCallback, (void*) fds[1],
ExceptionHandler handler(TEMPDIR, NULL, DoneCallback, (void*) fds[1],
true);
// Get some executable memory.
char* memory =
@ -194,7 +201,7 @@ TEST(ExceptionHandlerTest, InstructionPointerMemory) {
filename[len] = 0;
close(fds[0]);
const std::string minidump_filename = std::string("/tmp/") + filename +
const std::string minidump_filename = std::string(TEMPDIR) + "/" + filename +
".dmp";
struct stat st;
@ -270,7 +277,7 @@ TEST(ExceptionHandlerTest, InstructionPointerMemoryMinBound) {
const pid_t child = fork();
if (child == 0) {
close(fds[0]);
ExceptionHandler handler("/tmp", NULL, DoneCallback, (void*) fds[1],
ExceptionHandler handler(TEMPDIR, NULL, DoneCallback, (void*) fds[1],
true);
// Get some executable memory.
char* memory =
@ -318,7 +325,7 @@ TEST(ExceptionHandlerTest, InstructionPointerMemoryMinBound) {
filename[len] = 0;
close(fds[0]);
const std::string minidump_filename = std::string("/tmp/") + filename +
const std::string minidump_filename = std::string(TEMPDIR) + "/" + filename +
".dmp";
struct stat st;
@ -394,7 +401,7 @@ TEST(ExceptionHandlerTest, InstructionPointerMemoryMaxBound) {
const pid_t child = fork();
if (child == 0) {
close(fds[0]);
ExceptionHandler handler("/tmp", NULL, DoneCallback, (void*) fds[1],
ExceptionHandler handler(TEMPDIR, NULL, DoneCallback, (void*) fds[1],
true);
// Get some executable memory.
char* memory =
@ -442,7 +449,7 @@ TEST(ExceptionHandlerTest, InstructionPointerMemoryMaxBound) {
filename[len] = 0;
close(fds[0]);
const std::string minidump_filename = std::string("/tmp/") + filename +
const std::string minidump_filename = std::string(TEMPDIR) + "/" + filename +
".dmp";
struct stat st;
@ -510,7 +517,7 @@ TEST(ExceptionHandlerTest, InstructionPointerMemoryNullPointer) {
const pid_t child = fork();
if (child == 0) {
close(fds[0]);
ExceptionHandler handler("/tmp", NULL, DoneCallback, (void*) fds[1],
ExceptionHandler handler(TEMPDIR, NULL, DoneCallback, (void*) fds[1],
true);
// Try calling a NULL pointer.
typedef void (*void_function)(void);
@ -542,7 +549,7 @@ TEST(ExceptionHandlerTest, InstructionPointerMemoryNullPointer) {
filename[len] = 0;
close(fds[0]);
const std::string minidump_filename = std::string("/tmp/") + filename +
const std::string minidump_filename = std::string(TEMPDIR) + "/" + filename +
".dmp";
struct stat st;
@ -663,7 +670,7 @@ TEST(ExceptionHandlerTest, ExternalDumper) {
ASSERT_NE(crashing_pid, -1);
ASSERT_NE(signal_fd, -1);
char templ[] = "/tmp/exception-handler-unittest-XXXXXX";
char templ[] = TEMPDIR "/exception-handler-unittest-XXXXXX";
mktemp(templ);
ASSERT_TRUE(WriteMinidump(templ, crashing_pid, context,
kCrashContextSize));

View file

@ -36,8 +36,14 @@
using namespace google_breakpad;
#if !defined(__ANDROID__)
#define TEMPDIR "/tmp"
#else
#define TEMPDIR "/data/local/tmp"
#endif
static int TemporaryFile() {
static const char templ[] = "/tmp/line-reader-unittest-XXXXXX";
static const char templ[] = TEMPDIR "/line-reader-unittest-XXXXXX";
char templ_copy[sizeof(templ)];
memcpy(templ_copy, templ, sizeof(templ));
const int fd = mkstemp(templ_copy);

View file

@ -45,7 +45,9 @@
#include <elf.h>
#include <errno.h>
#include <fcntl.h>
#if !defined(__ANDROID__)
#include <link.h>
#endif
#include <sys/types.h>
#include <sys/ptrace.h>
@ -388,11 +390,16 @@ bool LinuxDumper::ThreadInfoGet(pid_t tid, ThreadInfo* info) {
if (info->ppid == -1 || info->tgid == -1)
return false;
if (sys_ptrace(PTRACE_GETREGS, tid, NULL, &info->regs) == -1 ||
sys_ptrace(PTRACE_GETFPREGS, tid, NULL, &info->fpregs) == -1) {
if (sys_ptrace(PTRACE_GETREGS, tid, NULL, &info->regs) == -1) {
return false;
}
#if !defined(__ANDROID__)
if (sys_ptrace(PTRACE_GETFPREGS, tid, NULL, &info->fpregs) == -1) {
return false;
}
#endif
#if defined(__i386)
if (sys_ptrace(PTRACE_GETFPXREGS, tid, NULL, &info->fpxregs) == -1)
return false;
@ -417,7 +424,7 @@ bool LinuxDumper::ThreadInfoGet(pid_t tid, ThreadInfo* info) {
#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]));
memcpy(&stack_pointer, &info->regs.ARM_sp, sizeof(info->regs.ARM_sp));
#else
#error "This code hasn't been ported to your platform yet."
#endif

View file

@ -34,18 +34,38 @@
#include <linux/limits.h>
#include <stdint.h>
#include <sys/types.h>
#if !defined(__ANDROID__)
#include <sys/user.h>
#endif
#include "common/memory.h"
#include "google_breakpad/common/minidump_format.h"
namespace google_breakpad {
#if defined(__i386) || defined(__x86_64)
typedef typeof(((struct user*) 0)->u_debugreg[0]) debugreg_t;
#endif
// Typedef for our parsing of the auxv variables in /proc/pid/auxv.
#if defined(__i386) || defined(__ARM_EABI__)
#if !defined(__ANDROID__)
typedef Elf32_auxv_t elf_aux_entry;
#else
// Android is missing this structure definition
typedef struct
{
uint32_t a_type; /* Entry type */
union
{
uint32_t a_val; /* Integer value */
} a_un;
} elf_aux_entry;
#if !defined(AT_SYSINFO_EHDR)
#define AT_SYSINFO_EHDR 33
#endif
#endif // __ANDROID__
#elif defined(__x86_64__)
typedef Elf64_auxv_t elf_aux_entry;
#endif
@ -76,8 +96,12 @@ struct ThreadInfo {
#elif defined(__ARM_EABI__)
// Mimicking how strace does this(see syscall.c, search for GETREGS)
#if defined(__ANDROID__)
struct pt_regs regs;
#else
struct user_regs regs;
struct user_fpregs fpregs;
#endif // __ANDROID__
#endif
};

View file

@ -27,6 +27,8 @@
// (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 <string>
#include <limits.h>
#include <unistd.h>
#include <signal.h>
@ -37,6 +39,7 @@
#include "common/linux/file_id.h"
#include "common/memory.h"
using std::string;
using namespace google_breakpad;
// This provides a wrapper around system calls which may be
@ -87,21 +90,36 @@ TEST(LinuxDumperTest, VerifyStackReadWithMultipleThreads) {
pid_t child_pid = fork();
if (child_pid == 0) {
// Locate helper binary next to the current binary.
char self_path[PATH_MAX];
if (readlink("/proc/self/exe", self_path, sizeof(self_path) - 1) == -1) {
FAIL() << "readlink failed: " << strerror(errno);
exit(1);
}
string helper_path(self_path);
size_t pos = helper_path.rfind('/');
if (pos == string::npos) {
FAIL() << "no trailing slash in path: " << helper_path;
exit(1);
}
helper_path.erase(pos + 1);
helper_path += "linux_dumper_unittest_helper";
// Set the number of threads
execl("src/client/linux/linux_dumper_unittest_helper",
execl(helper_path.c_str(),
"linux_dumper_unittest_helper",
kNumberOfThreadsArgument,
NULL);
// Kill if we get here.
printf("Errno from exec: %d", errno);
FAIL() << "Exec failed: " << strerror(errno);
FAIL() << "Exec of " << helper_path << " 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());
ASSERT_TRUE(dumper.Init());
EXPECT_EQ((size_t)kNumberOfThreadsInHelperProgram, dumper.threads().size());
EXPECT_TRUE(dumper.ThreadsSuspend());

View file

@ -37,6 +37,8 @@
#include <sys/syscall.h>
#include <unistd.h>
#include "third_party/lss/linux_syscall_support.h"
#if defined(__ARM_EABI__)
#define TID_PTR_REGISTER "r3"
#elif defined(__i386)
@ -48,7 +50,7 @@
#endif
void *thread_function(void *data) {
volatile pid_t thread_id = syscall(SYS_gettid);
volatile pid_t thread_id = syscall(__NR_gettid);
register volatile pid_t *thread_id_ptr asm(TID_PTR_REGISTER) = &thread_id;
while (true)
asm volatile ("" : : "r" (thread_id_ptr));

View file

@ -50,11 +50,15 @@
#include <errno.h>
#include <fcntl.h>
#if !defined(__ANDROID__)
#include <link.h>
#endif
#include <stdio.h>
#include <unistd.h>
#if !defined(__ANDROID__)
#include <sys/ucontext.h>
#include <sys/user.h>
#endif
#include <sys/utsname.h>
#include "client/minidump_file_writer.h"
@ -62,6 +66,10 @@
#include "google_breakpad/common/minidump_cpu_amd64.h"
#include "google_breakpad/common/minidump_cpu_x86.h"
#if defined(__ANDROID__)
#include "client/linux/android_link.h"
#include "client/linux/android_ucontext.h"
#endif
#include "client/linux/handler/exception_handler.h"
#include "client/linux/minidump_writer/line_reader.h"
#include "client/linux/minidump_writer/linux_dumper.h"
@ -311,11 +319,13 @@ static void CPUFillFromThreadInfo(MDRawContextARM *out,
out->iregs[i] = info.regs.uregs[i];
// No CPSR register in ThreadInfo(it's not accessible via ptrace)
out->cpsr = 0;
#if !defined(__ANDROID__)
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));
#endif
}
static void CPUFillFromUContext(MDRawContextARM *out, const ucontext *uc,
@ -389,7 +399,9 @@ class MinidumpWriter {
// it to a MD_LINUX_DSO_DEBUG stream.
struct r_debug* r_debug = NULL;
uint32_t dynamic_length = 0;
#if !defined(__ANDROID__)
// The Android NDK is missing structure definitions for most of this.
// For now, it's simpler just to skip it.
for (int i = 0;;) {
ElfW(Dyn) dyn;
dynamic_length += sizeof(dyn);
@ -400,6 +412,7 @@ class MinidumpWriter {
} else if (dyn.d_tag == DT_NULL)
break;
}
#endif
// A minidump file contains a number of tagged streams. This is the number
// of stream which we write.
@ -866,6 +879,9 @@ class MinidumpWriter {
bool WriteDSODebugStream(MDRawDirectory* dirent, struct r_debug* r_debug,
uint32_t dynamic_length) {
#if defined(__ANDROID__)
return false;
#else
// 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
@ -939,6 +955,7 @@ class MinidumpWriter {
delete[] dso_debug_data;
return true;
#endif
}
private:

View file

@ -37,6 +37,12 @@
using namespace google_breakpad;
#if !defined(__ANDROID__)
#define TEMPDIR "/tmp"
#else
#define TEMPDIR "/data/local/tmp"
#endif
namespace {
typedef testing::Test MinidumpWriterTest;
}
@ -58,7 +64,7 @@ TEST(MinidumpWriterTest, Setup) {
ExceptionHandler::CrashContext context;
memset(&context, 0, sizeof(context));
char templ[] = "/tmp/minidump-writer-unittest-XXXXXX";
char templ[] = TEMPDIR "/minidump-writer-unittest-XXXXXX";
mktemp(templ);
ASSERT_TRUE(WriteMinidump(templ, child, &context, sizeof(context)));
struct stat st;

View file

@ -38,7 +38,11 @@
#include <assert.h>
#include <elf.h>
#include <fcntl.h>
#if defined(__ANDROID__)
#include "client/linux/android_link.h"
#else
#include <link.h>
#endif
#include <stdio.h>
#include <string.h>
#include <sys/mman.h>