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
This commit is contained in:
parent
bd76cd035c
commit
f480ba1169
11 changed files with 925 additions and 40 deletions
|
@ -23,7 +23,10 @@ GOOGLELOG_LIB=../../third_party/linux/lib/glog/libglog.a
|
||||||
GOOGLEFLAGS_LIB=../../third_party/linux/lib/gflags/libgflags.a
|
GOOGLEFLAGS_LIB=../../third_party/linux/lib/gflags/libgflags.a
|
||||||
|
|
||||||
# Client exception handling & minidump writing library
|
# 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/linux_dumper.cc \
|
||||||
minidump_writer/minidump_writer.cc \
|
minidump_writer/minidump_writer.cc \
|
||||||
../minidump_file_writer.cc \
|
../minidump_file_writer.cc \
|
||||||
|
|
44
src/client/linux/crash_generation/client_info.h
Normal file
44
src/client/linux/crash_generation/client_info.h
Normal file
|
@ -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_
|
88
src/client/linux/crash_generation/crash_generation_client.cc
Normal file
88
src/client/linux/crash_generation/crash_generation_client.cc
Normal file
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
69
src/client/linux/crash_generation/crash_generation_client.h
Normal file
69
src/client/linux/crash_generation/crash_generation_client.h
Normal file
|
@ -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_
|
466
src/client/linux/crash_generation/crash_generation_server.cc
Normal file
466
src/client/linux/crash_generation/crash_generation_server.cc
Normal file
|
@ -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
|
133
src/client/linux/crash_generation/crash_generation_server.h
Normal file
133
src/client/linux/crash_generation/crash_generation_server.h
Normal file
|
@ -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_
|
|
@ -117,26 +117,51 @@ ExceptionHandler::ExceptionHandler(const std::string &dump_path,
|
||||||
: filter_(filter),
|
: filter_(filter),
|
||||||
callback_(callback),
|
callback_(callback),
|
||||||
callback_context_(callback_context),
|
callback_context_(callback_context),
|
||||||
dump_path_(),
|
handler_installed_(install_handler)
|
||||||
handler_installed_(install_handler),
|
{
|
||||||
crash_handler_(NULL) {
|
Init(dump_path, -1);
|
||||||
set_dump_path(dump_path);
|
}
|
||||||
|
|
||||||
if (install_handler) {
|
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.
|
||||||
|
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();
|
InstallHandlers();
|
||||||
|
|
||||||
|
if (!IsOutOfProcess())
|
||||||
|
set_dump_path(dump_path);
|
||||||
|
|
||||||
pthread_mutex_lock(&handler_stack_mutex_);
|
pthread_mutex_lock(&handler_stack_mutex_);
|
||||||
if (handler_stack_ == NULL)
|
if (handler_stack_ == NULL)
|
||||||
handler_stack_ = new std::vector<ExceptionHandler *>;
|
handler_stack_ = new std::vector<ExceptionHandler *>;
|
||||||
handler_stack_->push_back(this);
|
handler_stack_->push_back(this);
|
||||||
pthread_mutex_unlock(&handler_stack_mutex_);
|
pthread_mutex_unlock(&handler_stack_mutex_);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Runs before crashing: normal context.
|
|
||||||
ExceptionHandler::~ExceptionHandler() {
|
|
||||||
UninstallHandlers();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Runs before crashing: normal context.
|
// Runs before crashing: normal context.
|
||||||
bool ExceptionHandler::InstallHandlers() {
|
bool ExceptionHandler::InstallHandlers() {
|
||||||
|
@ -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.
|
// This function may run in a compromised context: see the top of the file.
|
||||||
bool ExceptionHandler::GenerateDump(CrashContext *context) {
|
bool ExceptionHandler::GenerateDump(CrashContext *context) {
|
||||||
|
if (IsOutOfProcess())
|
||||||
|
return crash_generation_client_->RequestDump(context, sizeof(*context));
|
||||||
|
|
||||||
static const unsigned kChildStackSize = 8000;
|
static const unsigned kChildStackSize = 8000;
|
||||||
PageAllocator allocator;
|
PageAllocator allocator;
|
||||||
uint8_t* stack = (uint8_t*) allocator.Alloc(kChildStackSize);
|
uint8_t* stack = (uint8_t*) allocator.Alloc(kChildStackSize);
|
||||||
|
|
|
@ -35,6 +35,11 @@
|
||||||
|
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
|
|
||||||
|
#include "client/linux/crash_generation/crash_generation_client.h"
|
||||||
|
#include "processor/scoped_ptr.h"
|
||||||
|
|
||||||
|
struct sigaction;
|
||||||
|
|
||||||
namespace google_breakpad {
|
namespace google_breakpad {
|
||||||
|
|
||||||
// ExceptionHandler
|
// ExceptionHandler
|
||||||
|
@ -116,6 +121,18 @@ class ExceptionHandler {
|
||||||
FilterCallback filter, MinidumpCallback callback,
|
FilterCallback filter, MinidumpCallback callback,
|
||||||
void *callback_context,
|
void *callback_context,
|
||||||
bool install_handler);
|
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();
|
~ExceptionHandler();
|
||||||
|
|
||||||
// Get and set the minidump path.
|
// Get and set the minidump path.
|
||||||
|
@ -149,7 +166,14 @@ class ExceptionHandler {
|
||||||
struct _libc_fpstate float_state;
|
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:
|
private:
|
||||||
|
void Init(const std::string &dump_path,
|
||||||
|
const int server_fd);
|
||||||
bool InstallHandlers();
|
bool InstallHandlers();
|
||||||
void UninstallHandlers();
|
void UninstallHandlers();
|
||||||
void PreresolveSymbols();
|
void PreresolveSymbols();
|
||||||
|
@ -166,6 +190,8 @@ class ExceptionHandler {
|
||||||
const MinidumpCallback callback_;
|
const MinidumpCallback callback_;
|
||||||
void* const callback_context_;
|
void* const callback_context_;
|
||||||
|
|
||||||
|
scoped_ptr<CrashGenerationClient> crash_generation_client_;
|
||||||
|
|
||||||
std::string dump_path_;
|
std::string dump_path_;
|
||||||
std::string next_minidump_path_;
|
std::string next_minidump_path_;
|
||||||
std::string next_minidump_id_;
|
std::string next_minidump_id_;
|
||||||
|
@ -189,9 +215,8 @@ class ExceptionHandler {
|
||||||
static unsigned handler_stack_index_;
|
static unsigned handler_stack_index_;
|
||||||
static pthread_mutex_t handler_stack_mutex_;
|
static pthread_mutex_t handler_stack_mutex_;
|
||||||
|
|
||||||
// A vector of the old signal handlers. The void* is a pointer to a newly
|
// A vector of the old signal handlers.
|
||||||
// allocated sigaction structure to avoid pulling in too many includes.
|
std::vector<std::pair<int, struct sigaction *> > old_handlers_;
|
||||||
std::vector<std::pair<int, void *> > old_handlers_;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace google_breakpad
|
} // namespace google_breakpad
|
||||||
|
|
|
@ -38,20 +38,11 @@
|
||||||
|
|
||||||
#include "client/linux/handler//exception_handler.h"
|
#include "client/linux/handler//exception_handler.h"
|
||||||
#include "client/linux/minidump_writer/minidump_writer.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_libc_support.h"
|
||||||
#include "common/linux/linux_syscall_support.h"
|
#include "common/linux/linux_syscall_support.h"
|
||||||
#include "breakpad_googletest_includes.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;
|
using namespace google_breakpad;
|
||||||
|
|
||||||
static void sigchld_handler(int signo) { }
|
static void sigchld_handler(int signo) { }
|
||||||
|
|
|
@ -32,20 +32,11 @@
|
||||||
|
|
||||||
#include "client/linux/handler/exception_handler.h"
|
#include "client/linux/handler/exception_handler.h"
|
||||||
#include "client/linux/minidump_writer/minidump_writer.h"
|
#include "client/linux/minidump_writer/minidump_writer.h"
|
||||||
|
#include "common/linux/eintr_wrapper.h"
|
||||||
#include "breakpad_googletest_includes.h"
|
#include "breakpad_googletest_includes.h"
|
||||||
|
|
||||||
using namespace google_breakpad;
|
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 {
|
namespace {
|
||||||
typedef testing::Test MinidumpWriterTest;
|
typedef testing::Test MinidumpWriterTest;
|
||||||
}
|
}
|
||||||
|
|
47
src/common/linux/eintr_wrapper.h
Normal file
47
src/common/linux/eintr_wrapper.h
Normal file
|
@ -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_
|
Loading…
Add table
Add a link
Reference in a new issue