From de545c09d0363e6964822ec92529a80feaca152d Mon Sep 17 00:00:00 2001 From: nealsid Date: Tue, 2 Mar 2010 00:39:48 +0000 Subject: 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 --- src/client/linux/Makefile | 20 ++++- .../crash_generation/crash_generation_client.cc | 2 +- src/client/linux/handler/exception_handler.cc | 44 ++++++++--- src/client/linux/handler/exception_handler.h | 6 ++ .../linux/handler/exception_handler_unittest.cc | 11 +-- .../linux/minidump_writer/line_reader_unittest.cc | 14 ++-- src/client/linux/minidump_writer/linux_dumper.cc | 48 +++++------- src/client/linux/minidump_writer/linux_dumper.h | 18 +++-- .../linux/minidump_writer/linux_dumper_unittest.cc | 85 ++++++++++++++++++---- .../linux_dumper_unittest_helper.cc | 64 ++++++++++++++++ .../linux/minidump_writer/minidump_writer.cc | 61 +++++++++++++++- 11 files changed, 290 insertions(+), 83 deletions(-) create mode 100644 src/client/linux/minidump_writer/linux_dumper_unittest_helper.cc (limited to 'src/client') diff --git a/src/client/linux/Makefile b/src/client/linux/Makefile index eb5a1f28..53e3b0f5 100644 --- a/src/client/linux/Makefile +++ b/src/client/linux/Makefile @@ -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 diff --git a/src/client/linux/crash_generation/crash_generation_client.cc b/src/client/linux/crash_generation/crash_generation_client.cc index 2463f1ed..af02fdbd 100644 --- a/src/client/linux/crash_generation/crash_generation_client.cc +++ b/src/client/linux/crash_generation/crash_generation_client.cc @@ -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 #include #include @@ -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; diff --git a/src/client/linux/handler/exception_handler.cc b/src/client/linux/handler/exception_handler.cc index 01081878..038612f2 100644 --- a/src/client/linux/handler/exception_handler.cc +++ b/src/client/linux/handler/exception_handler.cc @@ -80,6 +80,9 @@ #include #include +#include +#include + #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::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 diff --git a/src/client/linux/handler/exception_handler.h b/src/client/linux/handler/exception_handler.h index 6182a6c1..2a497037 100644 --- a/src/client/linux/handler/exception_handler.h +++ b/src/client/linux/handler/exception_handler.h @@ -34,6 +34,7 @@ #include #include +#include #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. diff --git a/src/client/linux/handler/exception_handler_unittest.cc b/src/client/linux/handler/exception_handler_unittest.cc index 2e8a6dbe..9747fe71 100644 --- a/src/client/linux/handler/exception_handler_unittest.cc +++ b/src/client/linux/handler/exception_handler_unittest.cc @@ -36,7 +36,7 @@ #include #include -#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(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(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(NULL) = 0; } - close(fds[1]); struct msghdr msg = {0}; struct iovec iov; diff --git a/src/client/linux/minidump_writer/line_reader_unittest.cc b/src/client/linux/minidump_writer/line_reader_unittest.cc index 222a098e..7c4c3ced 100644 --- a/src/client/linux/minidump_writer/line_reader_unittest.cc +++ b/src/client/linux/minidump_writer/line_reader_unittest.cc @@ -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); diff --git a/src/client/linux/minidump_writer/linux_dumper.cc b/src/client/linux/minidump_writer/linux_dumper.cc index 41929b8a..be199f7b 100644 --- a/src/client/linux/minidump_writer/linux_dumper.cc +++ b/src/client/linux/minidump_writer/linux_dumper.cc @@ -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* 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(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(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(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; } diff --git a/src/client/linux/minidump_writer/linux_dumper.h b/src/client/linux/minidump_writer/linux_dumper.h index 3e8869b5..7a4cd3a3 100644 --- a/src/client/linux/minidump_writer/linux_dumper.h +++ b/src/client/linux/minidump_writer/linux_dumper.h @@ -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 + +#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 - -#if defined(__i386) || defined(__x86_64) +#endif // defined(__i386) - static const unsigned kNumDebugRegisters = 8; - debugreg_t dregs[8]; +#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 threads_; // the ids of all the threads wasteful_vector mappings_; // info from /proc//maps }; diff --git a/src/client/linux/minidump_writer/linux_dumper_unittest.cc b/src/client/linux/minidump_writer/linux_dumper_unittest.cc index e060e53e..6f1043c3 100644 --- a/src/client/linux/minidump_writer/linux_dumper_unittest.cc +++ b/src/client/linux/minidump_writer/linux_dumper_unittest.cc @@ -29,10 +29,13 @@ #include #include +#include +#include +#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; - - const wasteful_vector 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; - } + ASSERT_TRUE(linux_gate_loc); + bool found_linux_gate = false; + + const wasteful_vector 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(mapping->start_addr)); - EXPECT_EQ(0, memcmp(linux_gate_loc, ELFMAG, SELFMAG)); } + EXPECT_TRUE(found_linux_gate); + EXPECT_EQ(linux_gate_loc, reinterpret_cast(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 diff --git a/src/client/linux/minidump_writer/linux_dumper_unittest_helper.cc b/src/client/linux/minidump_writer/linux_dumper_unittest_helper.cc new file mode 100644 index 00000000..f744d72c --- /dev/null +++ b/src/client/linux/minidump_writer/linux_dumper_unittest_helper.cc @@ -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 +#include +#include +#include +#include + +#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; +} diff --git a/src/client/linux/minidump_writer/minidump_writer.cc b/src/client/linux/minidump_writer/minidump_writer.cc index 166b8e5c..1ee90981 100644 --- a/src/client/linux/minidump_writer/minidump_writer.cc +++ b/src/client/linux/minidump_writer/minidump_writer.cc @@ -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(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 -- cgit v1.2.1