aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorted.mielczarek <ted.mielczarek@4c0a9323-5329-0410-9bdc-e9ce6186880e>2010-02-05 18:21:31 +0000
committerted.mielczarek <ted.mielczarek@4c0a9323-5329-0410-9bdc-e9ce6186880e>2010-02-05 18:21:31 +0000
commitf480ba116971a56d4de25ae1df2369e3d5503d16 (patch)
tree8623b5348123d092c2ac7111d9140db933c1bac1
parentBreakpad processor: Move STACK WIN record parsing into its own function. (diff)
downloadbreakpad-f480ba116971a56d4de25ae1df2369e3d5503d16.tar.xz
Refactor Chrome's out-of-process Linux code into CrashGeneration{Server,Client} classes. Upstreamed from the Mozilla repository. Patch by Chris Jones <jones.chris.g@gmail.com> r=me at https://bugzilla.mozilla.org/show_bug.cgi?id=516759
git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@515 4c0a9323-5329-0410-9bdc-e9ce6186880e
-rw-r--r--src/client/linux/Makefile5
-rw-r--r--src/client/linux/crash_generation/client_info.h44
-rw-r--r--src/client/linux/crash_generation/crash_generation_client.cc88
-rw-r--r--src/client/linux/crash_generation/crash_generation_client.h69
-rw-r--r--src/client/linux/crash_generation/crash_generation_server.cc466
-rw-r--r--src/client/linux/crash_generation/crash_generation_server.h133
-rw-r--r--src/client/linux/handler/exception_handler.cc60
-rw-r--r--src/client/linux/handler/exception_handler.h31
-rw-r--r--src/client/linux/handler/exception_handler_unittest.cc11
-rw-r--r--src/client/linux/minidump_writer/minidump_writer_unittest.cc11
-rw-r--r--src/common/linux/eintr_wrapper.h47
11 files changed, 925 insertions, 40 deletions
diff --git a/src/client/linux/Makefile b/src/client/linux/Makefile
index 594244dd..eb5a1f28 100644
--- a/src/client/linux/Makefile
+++ b/src/client/linux/Makefile
@@ -23,7 +23,10 @@ GOOGLELOG_LIB=../../third_party/linux/lib/glog/libglog.a
GOOGLEFLAGS_LIB=../../third_party/linux/lib/gflags/libgflags.a
# Client exception handling & minidump writing library
-LIB_CC_SRC=handler/exception_handler.cc \
+LIB_CC_SRC= \
+ crash_generation/crash_generation_client.cc \
+ crash_generation/crash_generation_server.cc \
+ handler/exception_handler.cc \
minidump_writer/linux_dumper.cc \
minidump_writer/minidump_writer.cc \
../minidump_file_writer.cc \
diff --git a/src/client/linux/crash_generation/client_info.h b/src/client/linux/crash_generation/client_info.h
new file mode 100644
index 00000000..11deee12
--- /dev/null
+++ b/src/client/linux/crash_generation/client_info.h
@@ -0,0 +1,44 @@
+// 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.
+
+#ifndef CLIENT_LINUX_CRASH_GENERATION_CLIENT_INFO_H_
+#define CLIENT_LINUX_CRASH_GENERATION_CLIENT_INFO_H_
+
+namespace google_breakpad {
+
+class CrashGenerationServer;
+
+struct ClientInfo {
+ CrashGenerationServer* crash_server_;
+ pid_t pid_;
+};
+
+}
+
+#endif // CLIENT_LINUX_CRASH_GENERATION_CLIENT_INFO_H_
diff --git a/src/client/linux/crash_generation/crash_generation_client.cc b/src/client/linux/crash_generation/crash_generation_client.cc
new file mode 100644
index 00000000..b9c00b10
--- /dev/null
+++ b/src/client/linux/crash_generation/crash_generation_client.cc
@@ -0,0 +1,88 @@
+// 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.
+
+#include <sys/socket.h>
+#include <sys/types.h>
+
+#include <algorithm>
+
+#include "client/linux/crash_generation/crash_generation_client.h"
+#include "common/linux/eintr_wrapper.h"
+#include "common/linux/linux_libc_support.h"
+#include "common/linux/linux_syscall_support.h"
+
+namespace google_breakpad {
+
+bool
+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;
+ my_memset(&msg, 0, sizeof(struct kernel_msghdr));
+ struct kernel_iovec iov[1];
+ iov[0].iov_base = const_cast<void*>(blob);
+ iov[0].iov_len = blob_size;
+
+ msg.msg_iov = iov;
+ msg.msg_iovlen = sizeof(iov) / sizeof(iov[0]);
+ char cmsg[kControlMsgSize];
+ my_memset(cmsg, 0, kControlMsgSize);
+ msg.msg_control = cmsg;
+ msg.msg_controllen = sizeof(cmsg);
+
+ struct cmsghdr* hdr = CMSG_FIRSTHDR(&msg);
+ hdr->cmsg_level = SOL_SOCKET;
+ hdr->cmsg_type = SCM_RIGHTS;
+ hdr->cmsg_len = CMSG_LEN(sizeof(int));
+ *((int*) CMSG_DATA(hdr)) = fds[1];
+
+ HANDLE_EINTR(sys_sendmsg(server_fd_, &msg, 0));
+ sys_close(fds[1]);
+
+ // wait for an ACK from the server
+ char b;
+ HANDLE_EINTR(sys_read(fds[0], &b, 1));
+
+ return true;
+}
+
+//static
+CrashGenerationClient*
+CrashGenerationClient::TryCreate(int server_fd)
+{
+ if (0 > server_fd)
+ return NULL;
+ return new CrashGenerationClient(server_fd);
+}
+
+}
diff --git a/src/client/linux/crash_generation/crash_generation_client.h b/src/client/linux/crash_generation/crash_generation_client.h
new file mode 100644
index 00000000..bba91ec7
--- /dev/null
+++ b/src/client/linux/crash_generation/crash_generation_client.h
@@ -0,0 +1,69 @@
+// 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.
+
+#ifndef CLIENT_LINUX_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H_
+#define CLIENT_LINUX_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H_
+
+#include <stddef.h>
+
+namespace google_breakpad {
+
+class CrashGenerationClient {
+public:
+ ~CrashGenerationClient()
+ {
+ }
+
+ // Request the crash server to generate a dump. |blob| is a hack,
+ // see exception_handler.h and minidump_writer.h
+ //
+ // Return true if the dump was successful; false otherwise.
+ bool RequestDump(const void* blob, size_t blob_size);
+
+ // Return a new CrashGenerationClient if |server_fd| is valid and
+ // connects to a CrashGenerationServer. Otherwise, return NULL.
+ // The returned CrashGenerationClient* is owned by the caller of
+ // this function.
+ static CrashGenerationClient* TryCreate(int server_fd);
+
+private:
+ CrashGenerationClient(int server_fd) : server_fd_(server_fd)
+ {
+ }
+
+ int server_fd_;
+
+ // prevent copy construction and assignment
+ CrashGenerationClient(const CrashGenerationClient&);
+ CrashGenerationClient& operator=(const CrashGenerationClient&);
+};
+
+} // namespace google_breakpad
+
+#endif // CLIENT_LINUX_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H_
diff --git a/src/client/linux/crash_generation/crash_generation_server.cc b/src/client/linux/crash_generation/crash_generation_server.cc
new file mode 100644
index 00000000..b2020a66
--- /dev/null
+++ b/src/client/linux/crash_generation/crash_generation_server.cc
@@ -0,0 +1,466 @@
+// 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.
+
+#include <assert.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <poll.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "client/linux/crash_generation/crash_generation_server.h"
+#include "client/linux/crash_generation/client_info.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/guid_creator.h"
+
+static const char kCommandQuit = 'x';
+
+static bool
+GetInodeForFileDescriptor(ino_t* inode_out, int fd)
+{
+ assert(inode_out);
+
+ struct stat buf;
+ if (fstat(fd, &buf) < 0)
+ return false;
+
+ if (!S_ISSOCK(buf.st_mode))
+ return false;
+
+ *inode_out = buf.st_ino;
+ return true;
+}
+
+// expected prefix of the target of the /proc/self/fd/%d link for a socket
+static const char kSocketLinkPrefix[] = "socket:[";
+
+// Parse a symlink in /proc/pid/fd/$x and return the inode number of the
+// socket.
+// inode_out: (output) set to the inode number on success
+// path: e.g. /proc/1234/fd/5 (must be a UNIX domain socket descriptor)
+static bool
+GetInodeForProcPath(ino_t* inode_out, const char* path)
+{
+ assert(inode_out);
+ assert(path);
+
+ char buf[256];
+ const ssize_t n = readlink(path, buf, sizeof(buf) - 1);
+ if (n == -1) {
+ return false;
+ }
+ buf[n] = 0;
+
+ if (0 != memcmp(kSocketLinkPrefix, buf, sizeof(kSocketLinkPrefix) - 1)) {
+ return false;
+ }
+
+ char* endptr;
+ const u_int64_t inode_ul =
+ strtoull(buf + sizeof(kSocketLinkPrefix) - 1, &endptr, 10);
+ if (*endptr != ']')
+ return false;
+
+ if (inode_ul == ULLONG_MAX) {
+ return false;
+ }
+
+ *inode_out = inode_ul;
+ return true;
+}
+
+static bool
+FindProcessHoldingSocket(pid_t* pid_out, ino_t socket_inode)
+{
+ assert(pid_out);
+ bool already_found = false;
+
+ DIR* proc = opendir("/proc");
+ if (!proc) {
+ return false;
+ }
+
+ std::vector<pid_t> pids;
+
+ struct dirent* dent;
+ while ((dent = readdir(proc))) {
+ char* endptr;
+ const unsigned long int pid_ul = strtoul(dent->d_name, &endptr, 10);
+ if (pid_ul == ULONG_MAX || '\0' != *endptr)
+ continue;
+ pids.push_back(pid_ul);
+ }
+ closedir(proc);
+
+ for (std::vector<pid_t>::const_iterator
+ i = pids.begin(); i != pids.end(); ++i) {
+ const pid_t current_pid = *i;
+ char buf[256];
+ snprintf(buf, sizeof(buf), "/proc/%d/fd", current_pid);
+ DIR* fd = opendir(buf);
+ if (!fd)
+ continue;
+
+ while ((dent = readdir(fd))) {
+ if (snprintf(buf, sizeof(buf), "/proc/%d/fd/%s", current_pid,
+ dent->d_name) >= static_cast<int>(sizeof(buf))) {
+ continue;
+ }
+
+ ino_t fd_inode;
+ if (GetInodeForProcPath(&fd_inode, buf)
+ && fd_inode == socket_inode) {
+ if (already_found) {
+ closedir(fd);
+ return false;
+ }
+
+ already_found = true;
+ *pid_out = current_pid;
+ break;
+ }
+ }
+
+ closedir(fd);
+ }
+
+ return already_found;
+}
+
+namespace google_breakpad {
+
+CrashGenerationServer::CrashGenerationServer(
+ const int listen_fd,
+ OnClientDumpRequestCallback dump_callback,
+ void* dump_context,
+ OnClientExitingCallback exit_callback,
+ void* exit_context,
+ bool generate_dumps,
+ const std::string* dump_path) :
+ server_fd_(listen_fd),
+ dump_callback_(dump_callback),
+ dump_context_(dump_context),
+ exit_callback_(exit_callback),
+ exit_context_(exit_context),
+ generate_dumps_(generate_dumps),
+ started_(false)
+{
+ if (dump_path)
+ dump_dir_ = *dump_path;
+ else
+ dump_dir_ = "/tmp";
+}
+
+CrashGenerationServer::~CrashGenerationServer()
+{
+ if (started_)
+ Stop();
+}
+
+bool
+CrashGenerationServer::Start()
+{
+ if (started_ || 0 > server_fd_)
+ return false;
+
+ int control_pipe[2];
+ if (pipe(control_pipe))
+ return false;
+
+ if (fcntl(control_pipe[0], F_SETFD, FD_CLOEXEC))
+ return false;
+ if (fcntl(control_pipe[1], F_SETFD, FD_CLOEXEC))
+ return false;
+
+ if (fcntl(control_pipe[0], F_SETFL, O_NONBLOCK))
+ return false;
+
+ control_pipe_in_ = control_pipe[0];
+ control_pipe_out_ = control_pipe[1];
+
+ if (pthread_create(&thread_, NULL,
+ ThreadMain, reinterpret_cast<void*>(this)))
+ return false;
+
+ started_ = true;
+ return true;
+}
+
+void
+CrashGenerationServer::Stop()
+{
+ assert(pthread_self() != thread_);
+
+ if (!started_)
+ return;
+
+ HANDLE_EINTR(write(control_pipe_out_, &kCommandQuit, 1));
+
+ void* dummy;
+ pthread_join(thread_, &dummy);
+
+ started_ = false;
+}
+
+//static
+bool
+CrashGenerationServer::CreateReportChannel(int* server_fd, int* client_fd)
+{
+ int fds[2];
+
+ if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds))
+ return false;
+
+ static const int on = 1;
+ // Enable passcred on the server end of the socket
+ if (setsockopt(fds[1], SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)))
+ return false;
+
+ if (fcntl(fds[1], F_SETFL, O_NONBLOCK))
+ return false;
+ if (fcntl(fds[1], F_SETFD, FD_CLOEXEC))
+ return false;
+
+ *client_fd = fds[0];
+ *server_fd = fds[1];
+ return true;
+}
+
+// The following methods/functions execute on the server thread
+
+void
+CrashGenerationServer::Run()
+{
+ struct pollfd pollfds[2];
+ memset(&pollfds, 0, sizeof(pollfds));
+
+ pollfds[0].fd = server_fd_;
+ pollfds[0].events = POLLIN;
+
+ pollfds[1].fd = control_pipe_in_;
+ pollfds[1].events = POLLIN;
+
+ while (true) {
+ // infinite timeout
+ int nevents = poll(pollfds, sizeof(pollfds)/sizeof(pollfds[0]), -1);
+ if (-1 == nevents) {
+ if (EINTR == errno) {
+ continue;
+ } else {
+ return;
+ }
+ }
+
+ if (pollfds[0].revents && !ClientEvent(pollfds[0].revents))
+ return;
+
+ if (pollfds[1].revents && !ControlEvent(pollfds[1].revents))
+ return;
+ }
+}
+
+bool
+CrashGenerationServer::ClientEvent(short revents)
+{
+ if (POLLHUP & revents)
+ return false;
+ assert(POLLIN & revents);
+
+ // A process has crashed and has signaled us by writing a datagram
+ // to the death signal socket. The datagram contains the crash context needed
+ // for writing the minidump as well as a file descriptor and a credentials
+ // block so that they can't lie about their pid.
+
+ // The length of the control message:
+ static const unsigned kControlMsgSize =
+ CMSG_SPACE(sizeof(int)) + CMSG_SPACE(sizeof(struct ucred));
+ // The length of the regular payload:
+ static const unsigned kCrashContextSize =
+ sizeof(google_breakpad::ExceptionHandler::CrashContext);
+
+ struct msghdr msg = {0};
+ struct iovec iov[1];
+ char crash_context[kCrashContextSize];
+ char control[kControlMsgSize];
+ const ssize_t expected_msg_size = sizeof(crash_context);
+
+ iov[0].iov_base = crash_context;
+ iov[0].iov_len = sizeof(crash_context);
+ msg.msg_iov = iov;
+ msg.msg_iovlen = sizeof(iov)/sizeof(iov[0]);
+ msg.msg_control = control;
+ msg.msg_controllen = kControlMsgSize;
+
+ const ssize_t msg_size = HANDLE_EINTR(recvmsg(server_fd_, &msg, 0));
+ if (msg_size != expected_msg_size)
+ return true;
+
+ if (msg.msg_controllen != kControlMsgSize ||
+ msg.msg_flags & ~MSG_TRUNC)
+ return true;
+
+ // Walk the control payload and extract the file descriptor and validated pid.
+ pid_t crashing_pid = -1;
+ int signal_fd = -1;
+ for (struct cmsghdr *hdr = CMSG_FIRSTHDR(&msg); hdr;
+ hdr = CMSG_NXTHDR(&msg, hdr)) {
+ if (hdr->cmsg_level != SOL_SOCKET)
+ continue;
+ if (hdr->cmsg_type == SCM_RIGHTS) {
+ const unsigned len = hdr->cmsg_len -
+ (((uint8_t*)CMSG_DATA(hdr)) - (uint8_t*)hdr);
+ assert(len % sizeof(int) == 0u);
+ const unsigned num_fds = len / sizeof(int);
+ if (num_fds > 1 || num_fds == 0) {
+ // A nasty process could try and send us too many descriptors and
+ // force a leak.
+ for (unsigned i = 0; i < num_fds; ++i)
+ HANDLE_EINTR(close(reinterpret_cast<int*>(CMSG_DATA(hdr))[i]));
+ return true;
+ } else {
+ signal_fd = reinterpret_cast<int*>(CMSG_DATA(hdr))[0];
+ }
+ } else if (hdr->cmsg_type == SCM_CREDENTIALS) {
+ const struct ucred *cred =
+ reinterpret_cast<struct ucred*>(CMSG_DATA(hdr));
+ crashing_pid = cred->pid;
+ }
+ }
+
+ if (crashing_pid == -1 || signal_fd == -1) {
+ if (signal_fd)
+ HANDLE_EINTR(close(signal_fd));
+ return true;
+ }
+
+ // Kernel bug workaround (broken in 2.6.30 at least):
+ // The kernel doesn't translate PIDs in SCM_CREDENTIALS across PID
+ // namespaces. Thus |crashing_pid| might be garbage from our point of view.
+ // In the future we can remove this workaround, but we have to wait a couple
+ // of years to be sure that it's worked its way out into the world.
+
+ ino_t inode_number;
+ if (!GetInodeForFileDescriptor(&inode_number, signal_fd)) {
+ HANDLE_EINTR(close(signal_fd));
+ return true;
+ }
+
+ if (!FindProcessHoldingSocket(&crashing_pid, inode_number - 1)) {
+ HANDLE_EINTR(close(signal_fd));
+ return true;
+ }
+
+ std::string minidump_filename;
+ if (!MakeMinidumpFilename(minidump_filename))
+ return true;
+
+ if (!google_breakpad::WriteMinidump(minidump_filename.c_str(),
+ crashing_pid, crash_context,
+ kCrashContextSize)) {
+ HANDLE_EINTR(close(signal_fd));
+ return true;
+ }
+
+ if (dump_callback_) {
+ ClientInfo info;
+
+ info.crash_server_ = this;
+ info.pid_ = crashing_pid;
+
+ dump_callback_(dump_context_, &info, &minidump_filename);
+ }
+
+ // Send the done signal to the process: it can exit now.
+ memset(&msg, 0, sizeof(msg));
+ struct iovec done_iov;
+ done_iov.iov_base = const_cast<char*>("\x42");
+ done_iov.iov_len = 1;
+ msg.msg_iov = &done_iov;
+ msg.msg_iovlen = 1;
+
+ HANDLE_EINTR(sendmsg(signal_fd, &msg, MSG_DONTWAIT | MSG_NOSIGNAL));
+ HANDLE_EINTR(close(signal_fd));
+
+ return true;
+}
+
+bool
+CrashGenerationServer::ControlEvent(short revents)
+{
+ if (POLLHUP & revents)
+ return false;
+ assert(POLLIN & revents);
+
+ char command;
+ if (read(control_pipe_in_, &command, 1))
+ return false;
+
+ switch (command) {
+ case kCommandQuit:
+ return false;
+ default:
+ assert(0);
+ }
+
+ return true;
+}
+
+bool
+CrashGenerationServer::MakeMinidumpFilename(std::string& outFilename)
+{
+ GUID guid;
+ char guidString[kGUIDStringLength+1];
+
+ if (!(CreateGUID(&guid)
+ && GUIDToString(&guid, guidString, sizeof(guidString))))
+ return false;
+
+ char path[PATH_MAX];
+ snprintf(path, sizeof(path), "%s/%s.dmp", dump_dir_.c_str(), guidString);
+
+ outFilename = path;
+ return true;
+}
+
+// static
+void*
+CrashGenerationServer::ThreadMain(void *arg)
+{
+ reinterpret_cast<CrashGenerationServer*>(arg)->Run();
+ return NULL;
+}
+
+} // namespace google_breakpad
diff --git a/src/client/linux/crash_generation/crash_generation_server.h b/src/client/linux/crash_generation/crash_generation_server.h
new file mode 100644
index 00000000..a28b6f0f
--- /dev/null
+++ b/src/client/linux/crash_generation/crash_generation_server.h
@@ -0,0 +1,133 @@
+// 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.
+
+#ifndef CLIENT_LINUX_CRASH_GENERATION_CRASH_GENERATION_SERVER_H_
+#define CLIENT_LINUX_CRASH_GENERATION_CRASH_GENERATION_SERVER_H_
+
+#include <pthread.h>
+
+#include <string>
+
+namespace google_breakpad {
+
+class ClientInfo;
+
+class CrashGenerationServer {
+public:
+ // WARNING: callbacks may be invoked on a different thread
+ // than that which creates the CrashGenerationServer. They must
+ // be thread safe.
+ typedef void (*OnClientDumpRequestCallback)(void* context,
+ const ClientInfo* client_info,
+ const std::string* file_path);
+
+ typedef void (*OnClientExitingCallback)(void* context,
+ const ClientInfo* client_info);
+
+ // Create an instance with the given parameters.
+ //
+ // Parameter listen_fd: The server fd created by CreateReportChannel().
+ // Parameter dump_callback: Callback for a client crash dump request.
+ // Parameter dump_context: Context for client crash dump request callback.
+ // Parameter exit_callback: Callback for client process exit.
+ // Parameter exit_context: Context for client exit callback.
+ // Parameter generate_dumps: Whether to automatically generate dumps.
+ // Client code of this class might want to generate dumps explicitly
+ // in the crash dump request callback. In that case, false can be
+ // passed for this parameter.
+ // Parameter dump_path: Path for generating dumps; required only if true is
+ // passed for generateDumps parameter; NULL can be passed otherwise.
+ CrashGenerationServer(const int listen_fd,
+ OnClientDumpRequestCallback dump_callback,
+ void* dump_context,
+ OnClientExitingCallback exit_callback,
+ void* exit_context,
+ bool generate_dumps,
+ const std::string* dump_path);
+
+ ~CrashGenerationServer();
+
+ // Perform initialization steps needed to start listening to clients.
+ //
+ // Return true if initialization is successful; false otherwise.
+ bool Start();
+
+ // Stop the server.
+ void Stop();
+
+ // Create a "channel" that can be used by clients to report crashes
+ // to a CrashGenerationServer. |*server_fd| should be passed to
+ // this class's constructor, and |*client_fd| should be passed to
+ // the ExceptionHandler constructor in the client process.
+ static bool CreateReportChannel(int* server_fd, int* client_fd);
+
+private:
+ // Run the server's event loop
+ void Run();
+
+ // Invoked when an child process (client) event occurs
+ // Returning true => "keep running", false => "exit loop"
+ bool ClientEvent(short revents);
+
+ // Invoked when the controlling thread (main) event occurs
+ // Returning true => "keep running", false => "exit loop"
+ bool ControlEvent(short revents);
+
+ // Return a unique filename at which a minidump can be written
+ bool MakeMinidumpFilename(std::string& outFilename);
+
+ // Trampoline to |Run()|
+ static void* ThreadMain(void* arg);
+
+ int server_fd_;
+
+ OnClientDumpRequestCallback dump_callback_;
+ void* dump_context_;
+
+ OnClientExitingCallback exit_callback_;
+ void* exit_context_;
+
+ bool generate_dumps_;
+
+ std::string dump_dir_;
+
+ bool started_;
+
+ pthread_t thread_;
+ int control_pipe_in_;
+ int control_pipe_out_;
+
+ // disable these
+ CrashGenerationServer(const CrashGenerationServer&);
+ CrashGenerationServer& operator=(const CrashGenerationServer&);
+};
+
+} // namespace google_breakpad
+
+#endif // CLIENT_LINUX_CRASH_GENERATION_CRASH_GENERATION_SERVER_H_
diff --git a/src/client/linux/handler/exception_handler.cc b/src/client/linux/handler/exception_handler.cc
index a9e2fca2..7cb85473 100644
--- a/src/client/linux/handler/exception_handler.cc
+++ b/src/client/linux/handler/exception_handler.cc
@@ -114,23 +114,26 @@ ExceptionHandler::ExceptionHandler(const std::string &dump_path,
MinidumpCallback callback,
void *callback_context,
bool install_handler)
- : filter_(filter),
- callback_(callback),
- callback_context_(callback_context),
- dump_path_(),
- handler_installed_(install_handler),
- crash_handler_(NULL) {
- set_dump_path(dump_path);
-
- if (install_handler) {
- InstallHandlers();
+ : filter_(filter),
+ callback_(callback),
+ callback_context_(callback_context),
+ handler_installed_(install_handler)
+{
+ Init(dump_path, -1);
+}
- pthread_mutex_lock(&handler_stack_mutex_);
- if (handler_stack_ == NULL)
- handler_stack_ = new std::vector<ExceptionHandler *>;
- handler_stack_->push_back(this);
- pthread_mutex_unlock(&handler_stack_mutex_);
- }
+ExceptionHandler::ExceptionHandler(const std::string &dump_path,
+ FilterCallback filter,
+ MinidumpCallback callback,
+ void* callback_context,
+ bool install_handler,
+ const int server_fd)
+ : filter_(filter),
+ callback_(callback),
+ callback_context_(callback_context),
+ handler_installed_(install_handler)
+{
+ Init(dump_path, server_fd);
}
// Runs before crashing: normal context.
@@ -138,6 +141,28 @@ ExceptionHandler::~ExceptionHandler() {
UninstallHandlers();
}
+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));
+
+ if (handler_installed_)
+ InstallHandlers();
+
+ if (!IsOutOfProcess())
+ set_dump_path(dump_path);
+
+ pthread_mutex_lock(&handler_stack_mutex_);
+ if (handler_stack_ == NULL)
+ handler_stack_ = new std::vector<ExceptionHandler *>;
+ handler_stack_->push_back(this);
+ pthread_mutex_unlock(&handler_stack_mutex_);
+}
+
// Runs before crashing: normal context.
bool ExceptionHandler::InstallHandlers() {
// We run the signal handlers on an alternative stack because we might have
@@ -280,6 +305,9 @@ bool ExceptionHandler::HandleSignal(int sig, siginfo_t* info, void* uc) {
// This function may run in a compromised context: see the top of the file.
bool ExceptionHandler::GenerateDump(CrashContext *context) {
+ if (IsOutOfProcess())
+ return crash_generation_client_->RequestDump(context, sizeof(*context));
+
static const unsigned kChildStackSize = 8000;
PageAllocator allocator;
uint8_t* stack = (uint8_t*) allocator.Alloc(kChildStackSize);
diff --git a/src/client/linux/handler/exception_handler.h b/src/client/linux/handler/exception_handler.h
index 34ce5e19..978bb010 100644
--- a/src/client/linux/handler/exception_handler.h
+++ b/src/client/linux/handler/exception_handler.h
@@ -35,6 +35,11 @@
#include <signal.h>
+#include "client/linux/crash_generation/crash_generation_client.h"
+#include "processor/scoped_ptr.h"
+
+struct sigaction;
+
namespace google_breakpad {
// ExceptionHandler
@@ -116,6 +121,18 @@ class ExceptionHandler {
FilterCallback filter, MinidumpCallback callback,
void *callback_context,
bool install_handler);
+
+ // Creates a new ExceptionHandler instance that can attempt to
+ // perform out-of-process dump generation if server_fd is valid. If
+ // server_fd is invalid, in-process dump generation will be
+ // used. See the above ctor for a description of the other
+ // parameters.
+ ExceptionHandler(const std::string& dump_path,
+ FilterCallback filter, MinidumpCallback callback,
+ void* callback_context,
+ bool install_handler,
+ const int server_fd);
+
~ExceptionHandler();
// Get and set the minidump path.
@@ -149,7 +166,14 @@ class ExceptionHandler {
struct _libc_fpstate float_state;
};
+ // Returns whether out-of-process dump generation is used or not.
+ bool IsOutOfProcess() const {
+ return crash_generation_client_.get() != NULL;
+ }
+
private:
+ void Init(const std::string &dump_path,
+ const int server_fd);
bool InstallHandlers();
void UninstallHandlers();
void PreresolveSymbols();
@@ -166,6 +190,8 @@ class ExceptionHandler {
const MinidumpCallback callback_;
void* const callback_context_;
+ scoped_ptr<CrashGenerationClient> crash_generation_client_;
+
std::string dump_path_;
std::string next_minidump_path_;
std::string next_minidump_id_;
@@ -189,9 +215,8 @@ class ExceptionHandler {
static unsigned handler_stack_index_;
static pthread_mutex_t handler_stack_mutex_;
- // A vector of the old signal handlers. The void* is a pointer to a newly
- // allocated sigaction structure to avoid pulling in too many includes.
- std::vector<std::pair<int, void *> > old_handlers_;
+ // A vector of the old signal handlers.
+ std::vector<std::pair<int, struct sigaction *> > old_handlers_;
};
} // namespace google_breakpad
diff --git a/src/client/linux/handler/exception_handler_unittest.cc b/src/client/linux/handler/exception_handler_unittest.cc
index 2f4e1045..cf44b2f7 100644
--- a/src/client/linux/handler/exception_handler_unittest.cc
+++ b/src/client/linux/handler/exception_handler_unittest.cc
@@ -38,20 +38,11 @@
#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"
#include "common/linux/linux_syscall_support.h"
#include "breakpad_googletest_includes.h"
-// This provides a wrapper around system calls which may be
-// interrupted by a signal and return EINTR. See man 7 signal.
-#define HANDLE_EINTR(x) ({ \
- typeof(x) __eintr_result__; \
- do { \
- __eintr_result__ = x; \
- } while (__eintr_result__ == -1 && errno == EINTR); \
- __eintr_result__;\
-})
-
using namespace google_breakpad;
static void sigchld_handler(int signo) { }
diff --git a/src/client/linux/minidump_writer/minidump_writer_unittest.cc b/src/client/linux/minidump_writer/minidump_writer_unittest.cc
index 5ff336ce..22ad9909 100644
--- a/src/client/linux/minidump_writer/minidump_writer_unittest.cc
+++ b/src/client/linux/minidump_writer/minidump_writer_unittest.cc
@@ -32,20 +32,11 @@
#include "client/linux/handler/exception_handler.h"
#include "client/linux/minidump_writer/minidump_writer.h"
+#include "common/linux/eintr_wrapper.h"
#include "breakpad_googletest_includes.h"
using namespace google_breakpad;
-// This provides a wrapper around system calls which may be
-// interrupted by a signal and return EINTR. See man 7 signal.
-#define HANDLE_EINTR(x) ({ \
- typeof(x) __eintr_result__; \
- do { \
- __eintr_result__ = x; \
- } while (__eintr_result__ == -1 && errno == EINTR); \
- __eintr_result__;\
-})
-
namespace {
typedef testing::Test MinidumpWriterTest;
}
diff --git a/src/common/linux/eintr_wrapper.h b/src/common/linux/eintr_wrapper.h
new file mode 100644
index 00000000..59febfb8
--- /dev/null
+++ b/src/common/linux/eintr_wrapper.h
@@ -0,0 +1,47 @@
+// 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.
+
+#ifndef COMMON_LINUX_EINTR_WRAPPER_H_
+#define COMMON_LINUX_EINTR_WRAPPER_H_
+
+#include <errno.h>
+
+// This provides a wrapper around system calls which may be interrupted by a
+// signal and return EINTR. See man 7 signal.
+//
+
+#define HANDLE_EINTR(x) ({ \
+ typeof(x) __eintr_result__; \
+ do { \
+ __eintr_result__ = x; \
+ } while (__eintr_result__ == -1 && errno == EINTR); \
+ __eintr_result__;\
+})
+
+#endif // ifndef COMMON_LINUX_EINTR_WRAPPER_H_