diff options
Diffstat (limited to 'toolkit/crashreporter/google-breakpad/src/client/linux')
48 files changed, 0 insertions, 11347 deletions
diff --git a/toolkit/crashreporter/google-breakpad/src/client/linux/crash_generation/client_info.h b/toolkit/crashreporter/google-breakpad/src/client/linux/crash_generation/client_info.h deleted file mode 100644 index d0a184a63..000000000 --- a/toolkit/crashreporter/google-breakpad/src/client/linux/crash_generation/client_info.h +++ /dev/null @@ -1,53 +0,0 @@ -// 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. - -#ifndef CLIENT_LINUX_CRASH_GENERATION_CLIENT_INFO_H_ -#define CLIENT_LINUX_CRASH_GENERATION_CLIENT_INFO_H_ - -namespace google_breakpad { - -class CrashGenerationServer; - -class ClientInfo { - public: - ClientInfo(pid_t pid, CrashGenerationServer* crash_server) - : crash_server_(crash_server), - pid_(pid) {} - - CrashGenerationServer* crash_server() const { return crash_server_; } - pid_t pid() const { return pid_; } - - private: - CrashGenerationServer* crash_server_; - pid_t pid_; -}; - -} - -#endif // CLIENT_LINUX_CRASH_GENERATION_CLIENT_INFO_H_ diff --git a/toolkit/crashreporter/google-breakpad/src/client/linux/crash_generation/crash_generation_client.cc b/toolkit/crashreporter/google-breakpad/src/client/linux/crash_generation/crash_generation_client.cc deleted file mode 100644 index d8bfbbad2..000000000 --- a/toolkit/crashreporter/google-breakpad/src/client/linux/crash_generation/crash_generation_client.cc +++ /dev/null @@ -1,105 +0,0 @@ -// 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. - -#include "client/linux/crash_generation/crash_generation_client.h" - -#include <stdio.h> -#include <sys/socket.h> -#include <sys/types.h> - -#include <algorithm> - -#include "common/linux/eintr_wrapper.h" -#include "common/linux/ignore_ret.h" -#include "third_party/lss/linux_syscall_support.h" - -namespace google_breakpad { - -namespace { - -class CrashGenerationClientImpl : public CrashGenerationClient { - public: - explicit CrashGenerationClientImpl(int server_fd) : server_fd_(server_fd) {} - virtual ~CrashGenerationClientImpl() {} - - virtual bool RequestDump(const void* blob, size_t blob_size) { - int fds[2]; - if (sys_pipe(fds) < 0) - return false; - static const unsigned kControlMsgSize = CMSG_SPACE(sizeof(int)); - - struct kernel_iovec iov; - iov.iov_base = const_cast<void*>(blob); - iov.iov_len = blob_size; - - struct kernel_msghdr msg = { 0 }; - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - char cmsg[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* p = reinterpret_cast<int*>(CMSG_DATA(hdr)); - *p = fds[1]; - - ssize_t ret = HANDLE_EINTR(sys_sendmsg(server_fd_, &msg, 0)); - sys_close(fds[1]); - if (ret < 0) { - sys_close(fds[0]); - return false; - } - - // Wait for an ACK from the server. - char b; - IGNORE_RET(HANDLE_EINTR(sys_read(fds[0], &b, 1))); - sys_close(fds[0]); - - return true; - } - - private: - int server_fd_; - - DISALLOW_COPY_AND_ASSIGN(CrashGenerationClientImpl); -}; - -} // namespace - -// static -CrashGenerationClient* CrashGenerationClient::TryCreate(int server_fd) { - if (server_fd < 0) - return NULL; - return new CrashGenerationClientImpl(server_fd); -} - -} // namespace google_breakpad diff --git a/toolkit/crashreporter/google-breakpad/src/client/linux/crash_generation/crash_generation_client.h b/toolkit/crashreporter/google-breakpad/src/client/linux/crash_generation/crash_generation_client.h deleted file mode 100644 index 4e68424ae..000000000 --- a/toolkit/crashreporter/google-breakpad/src/client/linux/crash_generation/crash_generation_client.h +++ /dev/null @@ -1,65 +0,0 @@ -// 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. - -#ifndef CLIENT_LINUX_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H_ -#define CLIENT_LINUX_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H_ - -#include "common/basictypes.h" - -#include <stddef.h> - -namespace google_breakpad { - -// CrashGenerationClient is an interface for implementing out-of-process crash -// dumping. The default implementation, accessed via the TryCreate() factory, -// works in conjunction with the CrashGenerationServer to generate a minidump -// via a remote process. -class CrashGenerationClient { - public: - CrashGenerationClient() {} - virtual ~CrashGenerationClient() {} - - // Request the crash server to generate a dump. |blob| is an opaque - // CrashContext pointer from exception_handler.h. - // Returns true if the dump was successful; false otherwise. - virtual bool RequestDump(const void* blob, size_t blob_size) = 0; - - // Returns 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: - DISALLOW_COPY_AND_ASSIGN(CrashGenerationClient); -}; - -} // namespace google_breakpad - -#endif // CLIENT_LINUX_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H_ diff --git a/toolkit/crashreporter/google-breakpad/src/client/linux/crash_generation/crash_generation_server.cc b/toolkit/crashreporter/google-breakpad/src/client/linux/crash_generation/crash_generation_server.cc deleted file mode 100644 index 1d7d93b99..000000000 --- a/toolkit/crashreporter/google-breakpad/src/client/linux/crash_generation/crash_generation_server.cc +++ /dev/null @@ -1,333 +0,0 @@ -// 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. - -#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 <vector> - -#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" -#include "common/linux/safe_readlink.h" - -static const char kCommandQuit = 'x'; - -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 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); - - close(control_pipe_in_); - close(control_pipe_out_); - - 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) - 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) - close(signal_fd); - return true; - } - - string minidump_filename; - if (!MakeMinidumpFilename(minidump_filename)) - return true; - - if (!google_breakpad::WriteMinidump(minidump_filename.c_str(), - crashing_pid, crash_context, - kCrashContextSize)) { - close(signal_fd); - return true; - } - - if (dump_callback_) { - ClientInfo info(crashing_pid, this); - - dump_callback_(dump_context_, &info, &minidump_filename); - } - - // Send the done signal to the process: it can exit now. - // (Closing this will make the child's sys_read unblock and return 0.) - 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(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/toolkit/crashreporter/google-breakpad/src/client/linux/crash_generation/crash_generation_server.h b/toolkit/crashreporter/google-breakpad/src/client/linux/crash_generation/crash_generation_server.h deleted file mode 100644 index 483fb709b..000000000 --- a/toolkit/crashreporter/google-breakpad/src/client/linux/crash_generation/crash_generation_server.h +++ /dev/null @@ -1,135 +0,0 @@ -// 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. - -#ifndef CLIENT_LINUX_CRASH_GENERATION_CRASH_GENERATION_SERVER_H_ -#define CLIENT_LINUX_CRASH_GENERATION_CRASH_GENERATION_SERVER_H_ - -#include <pthread.h> - -#include <string> - -#include "common/using_std_string.h" - -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 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 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(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_; - - 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/toolkit/crashreporter/google-breakpad/src/client/linux/data/linux-gate-amd.sym b/toolkit/crashreporter/google-breakpad/src/client/linux/data/linux-gate-amd.sym deleted file mode 100644 index e042a5ec4..000000000 --- a/toolkit/crashreporter/google-breakpad/src/client/linux/data/linux-gate-amd.sym +++ /dev/null @@ -1,3 +0,0 @@ -MODULE Linux x86 B8CFDE93002D54DA1900A40AA1BD67690 linux-gate.so -PUBLIC 400 0 __kernel_vsyscall -STACK WIN 4 400 100 1 1 0 0 0 0 0 1 diff --git a/toolkit/crashreporter/google-breakpad/src/client/linux/data/linux-gate-intel.sym b/toolkit/crashreporter/google-breakpad/src/client/linux/data/linux-gate-intel.sym deleted file mode 100644 index c209c2375..000000000 --- a/toolkit/crashreporter/google-breakpad/src/client/linux/data/linux-gate-intel.sym +++ /dev/null @@ -1,3 +0,0 @@ -MODULE Linux x86 4FBDA58B5A1DF5A379E3CF19A235EA090 linux-gate.so -PUBLIC 400 0 __kernel_vsyscall -STACK WIN 4 400 200 3 3 0 0 0 0 0 1
\ No newline at end of file diff --git a/toolkit/crashreporter/google-breakpad/src/client/linux/dump_writer_common/mapping_info.h b/toolkit/crashreporter/google-breakpad/src/client/linux/dump_writer_common/mapping_info.h deleted file mode 100644 index 5f247cfd4..000000000 --- a/toolkit/crashreporter/google-breakpad/src/client/linux/dump_writer_common/mapping_info.h +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright (c) 2014, 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_DUMP_WRITER_COMMON_MAPPING_INFO_H_ -#define CLIENT_LINUX_DUMP_WRITER_COMMON_MAPPING_INFO_H_ - -#include <limits.h> -#include <list> -#include <stdint.h> - -#include "google_breakpad/common/minidump_format.h" - -namespace google_breakpad { - -// One of these is produced for each mapping in the process (i.e. line in -// /proc/$x/maps). -struct MappingInfo { - uintptr_t start_addr; - size_t size; - size_t offset; // offset into the backed file. - bool exec; // true if the mapping has the execute bit set. - char name[NAME_MAX]; -}; - -struct MappingEntry { - MappingInfo first; - uint8_t second[sizeof(MDGUID)]; -}; - -// A list of <MappingInfo, GUID> -typedef std::list<MappingEntry> MappingList; - -} // namespace google_breakpad - -#endif // CLIENT_LINUX_DUMP_WRITER_COMMON_MAPPING_INFO_H_ diff --git a/toolkit/crashreporter/google-breakpad/src/client/linux/dump_writer_common/raw_context_cpu.h b/toolkit/crashreporter/google-breakpad/src/client/linux/dump_writer_common/raw_context_cpu.h deleted file mode 100644 index e2ef45df5..000000000 --- a/toolkit/crashreporter/google-breakpad/src/client/linux/dump_writer_common/raw_context_cpu.h +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright (c) 2014, 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_DUMP_WRITER_COMMON_RAW_CONTEXT_CPU_H -#define CLIENT_LINUX_DUMP_WRITER_COMMON_RAW_CONTEXT_CPU_H - -#include "google_breakpad/common/minidump_format.h" - -namespace google_breakpad { - -#if defined(__i386__) -typedef MDRawContextX86 RawContextCPU; -#elif defined(__x86_64) -typedef MDRawContextAMD64 RawContextCPU; -#elif defined(__ARM_EABI__) -typedef MDRawContextARM RawContextCPU; -#elif defined(__aarch64__) -typedef MDRawContextARM64 RawContextCPU; -#elif defined(__mips__) -typedef MDRawContextMIPS RawContextCPU; -#else -#error "This code has not been ported to your platform yet." -#endif - -} // namespace google_breakpad - -#endif // CLIENT_LINUX_DUMP_WRITER_COMMON_RAW_CONTEXT_CPU_H diff --git a/toolkit/crashreporter/google-breakpad/src/client/linux/dump_writer_common/thread_info.cc b/toolkit/crashreporter/google-breakpad/src/client/linux/dump_writer_common/thread_info.cc deleted file mode 100644 index 0a1041d62..000000000 --- a/toolkit/crashreporter/google-breakpad/src/client/linux/dump_writer_common/thread_info.cc +++ /dev/null @@ -1,305 +0,0 @@ -// Copyright (c) 2014, 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 "client/linux/dump_writer_common/thread_info.h" - -#include <string.h> -#include <assert.h> - -#include "common/linux/linux_libc_support.h" -#include "google_breakpad/common/minidump_format.h" - -namespace { - -#if defined(__i386__) -// Write a uint16_t to memory -// out: memory location to write to -// v: value to write. -void U16(void* out, uint16_t v) { - my_memcpy(out, &v, sizeof(v)); -} - -// Write a uint32_t to memory -// out: memory location to write to -// v: value to write. -void U32(void* out, uint32_t v) { - my_memcpy(out, &v, sizeof(v)); -} -#endif - -} - -namespace google_breakpad { - -#if defined(__i386__) - -uintptr_t ThreadInfo::GetInstructionPointer() const { - return regs.eip; -} - -void ThreadInfo::FillCPUContext(RawContextCPU* out) const { - out->context_flags = MD_CONTEXT_X86_ALL; - - out->dr0 = dregs[0]; - out->dr1 = dregs[1]; - out->dr2 = dregs[2]; - out->dr3 = dregs[3]; - // 4 and 5 deliberatly omitted because they aren't included in the minidump - // format. - out->dr6 = dregs[6]; - out->dr7 = dregs[7]; - - out->gs = regs.xgs; - out->fs = regs.xfs; - out->es = regs.xes; - out->ds = regs.xds; - - out->edi = regs.edi; - out->esi = regs.esi; - out->ebx = regs.ebx; - out->edx = regs.edx; - out->ecx = regs.ecx; - out->eax = regs.eax; - - out->ebp = regs.ebp; - out->eip = regs.eip; - out->cs = regs.xcs; - out->eflags = regs.eflags; - out->esp = regs.esp; - out->ss = regs.xss; - - out->float_save.control_word = fpregs.cwd; - out->float_save.status_word = fpregs.swd; - out->float_save.tag_word = fpregs.twd; - out->float_save.error_offset = fpregs.fip; - out->float_save.error_selector = fpregs.fcs; - out->float_save.data_offset = fpregs.foo; - out->float_save.data_selector = fpregs.fos; - - // 8 registers * 10 bytes per register. - my_memcpy(out->float_save.register_area, fpregs.st_space, 10 * 8); - - // This matches the Intel fpsave format. - U16(out->extended_registers + 0, fpregs.cwd); - U16(out->extended_registers + 2, fpregs.swd); - U16(out->extended_registers + 4, fpregs.twd); - U16(out->extended_registers + 6, fpxregs.fop); - U32(out->extended_registers + 8, fpxregs.fip); - U16(out->extended_registers + 12, fpxregs.fcs); - U32(out->extended_registers + 16, fpregs.foo); - U16(out->extended_registers + 20, fpregs.fos); - U32(out->extended_registers + 24, fpxregs.mxcsr); - - my_memcpy(out->extended_registers + 32, &fpxregs.st_space, 128); - my_memcpy(out->extended_registers + 160, &fpxregs.xmm_space, 128); -} - -#elif defined(__x86_64) - -uintptr_t ThreadInfo::GetInstructionPointer() const { - return regs.rip; -} - -void ThreadInfo::FillCPUContext(RawContextCPU* out) const { - out->context_flags = MD_CONTEXT_AMD64_FULL | - MD_CONTEXT_AMD64_SEGMENTS; - - out->cs = regs.cs; - - out->ds = regs.ds; - out->es = regs.es; - out->fs = regs.fs; - out->gs = regs.gs; - - out->ss = regs.ss; - out->eflags = regs.eflags; - - out->dr0 = dregs[0]; - out->dr1 = dregs[1]; - out->dr2 = dregs[2]; - out->dr3 = dregs[3]; - // 4 and 5 deliberatly omitted because they aren't included in the minidump - // format. - out->dr6 = dregs[6]; - out->dr7 = dregs[7]; - - out->rax = regs.rax; - out->rcx = regs.rcx; - out->rdx = regs.rdx; - out->rbx = regs.rbx; - - out->rsp = regs.rsp; - - out->rbp = regs.rbp; - out->rsi = regs.rsi; - out->rdi = regs.rdi; - out->r8 = regs.r8; - out->r9 = regs.r9; - out->r10 = regs.r10; - out->r11 = regs.r11; - out->r12 = regs.r12; - out->r13 = regs.r13; - out->r14 = regs.r14; - out->r15 = regs.r15; - - out->rip = regs.rip; - - out->flt_save.control_word = fpregs.cwd; - out->flt_save.status_word = fpregs.swd; - out->flt_save.tag_word = fpregs.ftw; - out->flt_save.error_opcode = fpregs.fop; - out->flt_save.error_offset = fpregs.rip; - out->flt_save.error_selector = 0; // We don't have this. - out->flt_save.data_offset = fpregs.rdp; - out->flt_save.data_selector = 0; // We don't have this. - out->flt_save.mx_csr = fpregs.mxcsr; - out->flt_save.mx_csr_mask = fpregs.mxcr_mask; - - my_memcpy(&out->flt_save.float_registers, &fpregs.st_space, 8 * 16); - my_memcpy(&out->flt_save.xmm_registers, &fpregs.xmm_space, 16 * 16); -} - -#elif defined(__ARM_EABI__) - -uintptr_t ThreadInfo::GetInstructionPointer() const { - return regs.uregs[15]; -} - -void ThreadInfo::FillCPUContext(RawContextCPU* out) const { - out->context_flags = MD_CONTEXT_ARM_FULL; - - for (int i = 0; i < MD_CONTEXT_ARM_GPR_COUNT; ++i) - out->iregs[i] = regs.uregs[i]; - // No CPSR register in ThreadInfo(it's not accessible via ptrace) - out->cpsr = 0; -#if !defined(__ANDROID__) - out->float_save.fpscr = fpregs.fpsr | - (static_cast<uint64_t>(fpregs.fpcr) << 32); - // TODO: sort this out, actually collect floating point registers - my_memset(&out->float_save.regs, 0, sizeof(out->float_save.regs)); - my_memset(&out->float_save.extra, 0, sizeof(out->float_save.extra)); -#endif -} - -#elif defined(__aarch64__) - -uintptr_t ThreadInfo::GetInstructionPointer() const { - return regs.pc; -} - -void ThreadInfo::FillCPUContext(RawContextCPU* out) const { - out->context_flags = MD_CONTEXT_ARM64_FULL; - - out->cpsr = static_cast<uint32_t>(regs.pstate); - for (int i = 0; i < MD_CONTEXT_ARM64_REG_SP; ++i) - out->iregs[i] = regs.regs[i]; - out->iregs[MD_CONTEXT_ARM64_REG_SP] = regs.sp; - out->iregs[MD_CONTEXT_ARM64_REG_PC] = regs.pc; - - out->float_save.fpsr = fpregs.fpsr; - out->float_save.fpcr = fpregs.fpcr; - my_memcpy(&out->float_save.regs, &fpregs.vregs, - MD_FLOATINGSAVEAREA_ARM64_FPR_COUNT * 16); -} - -#elif defined(__mips__) - -uintptr_t ThreadInfo::GetInstructionPointer() const { - return mcontext.pc; -} - -void ThreadInfo::FillCPUContext(RawContextCPU* out) const { -#if _MIPS_SIM == _ABI64 - out->context_flags = MD_CONTEXT_MIPS64_FULL; -#elif _MIPS_SIM == _ABIO32 - out->context_flags = MD_CONTEXT_MIPS_FULL; -#else -# error "This mips ABI is currently not supported (n32)" -#endif - - for (int i = 0; i < MD_CONTEXT_MIPS_GPR_COUNT; ++i) - out->iregs[i] = mcontext.gregs[i]; - - out->mdhi = mcontext.mdhi; - out->mdlo = mcontext.mdlo; - out->dsp_control = mcontext.dsp; - - out->hi[0] = mcontext.hi1; - out->lo[0] = mcontext.lo1; - out->hi[1] = mcontext.hi2; - out->lo[1] = mcontext.lo2; - out->hi[2] = mcontext.hi3; - out->lo[2] = mcontext.lo3; - - out->epc = mcontext.pc; - out->badvaddr = 0; // Not stored in mcontext - out->status = 0; // Not stored in mcontext - out->cause = 0; // Not stored in mcontext - - for (int i = 0; i < MD_FLOATINGSAVEAREA_MIPS_FPR_COUNT; ++i) - out->float_save.regs[i] = mcontext.fpregs.fp_r.fp_fregs[i]._fp_fregs; - - out->float_save.fpcsr = mcontext.fpc_csr; -#if _MIPS_SIM == _ABIO32 - out->float_save.fir = mcontext.fpc_eir; -#endif -} -#endif // __mips__ - -void ThreadInfo::GetGeneralPurposeRegisters(void** gp_regs, size_t* size) { - assert(gp_regs || size); -#if defined(__mips__) - if (gp_regs) - *gp_regs = mcontext.gregs; - if (size) - *size = sizeof(mcontext.gregs); -#else - if (gp_regs) - *gp_regs = ®s; - if (size) - *size = sizeof(regs); -#endif -} - -void ThreadInfo::GetFloatingPointRegisters(void** fp_regs, size_t* size) { - assert(fp_regs || size); -#if defined(__mips__) - if (fp_regs) - *fp_regs = &mcontext.fpregs; - if (size) - *size = sizeof(mcontext.fpregs); -#else - if (fp_regs) - *fp_regs = &fpregs; - if (size) - *size = sizeof(fpregs); -#endif -} - -} // namespace google_breakpad diff --git a/toolkit/crashreporter/google-breakpad/src/client/linux/dump_writer_common/thread_info.h b/toolkit/crashreporter/google-breakpad/src/client/linux/dump_writer_common/thread_info.h deleted file mode 100644 index 99093d2e0..000000000 --- a/toolkit/crashreporter/google-breakpad/src/client/linux/dump_writer_common/thread_info.h +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright (c) 2014, 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_DUMP_WRITER_COMMON_THREAD_INFO_H_ -#define CLIENT_LINUX_DUMP_WRITER_COMMON_THREAD_INFO_H_ - -#include <sys/ucontext.h> -#include <sys/user.h> - -#include "client/linux/dump_writer_common/raw_context_cpu.h" -#include "common/memory.h" -#include "google_breakpad/common/minidump_format.h" - -namespace google_breakpad { - -#if defined(__i386) || defined(__x86_64) -typedef __typeof__(((struct user*) 0)->u_debugreg[0]) debugreg_t; -#endif - -// We produce one of these structures for each thread in the crashed process. -struct ThreadInfo { - pid_t tgid; // thread group id - pid_t ppid; // parent process - - uintptr_t stack_pointer; // thread stack pointer - - -#if defined(__i386) || defined(__x86_64) - user_regs_struct regs; - user_fpregs_struct fpregs; - static const unsigned kNumDebugRegisters = 8; - debugreg_t dregs[8]; -#if defined(__i386) - user_fpxregs_struct fpxregs; -#endif // defined(__i386) - -#elif defined(__ARM_EABI__) - // Mimicking how strace does this(see syscall.c, search for GETREGS) - struct user_regs regs; - struct user_fpregs fpregs; -#elif defined(__aarch64__) - // Use the structures defined in <sys/user.h> - struct user_regs_struct regs; - struct user_fpsimd_struct fpregs; -#elif defined(__mips__) - // Use the structure defined in <sys/ucontext.h>. - mcontext_t mcontext; -#endif - - // Returns the instruction pointer (platform-dependent impl.). - uintptr_t GetInstructionPointer() const; - - // Fills a RawContextCPU using the context in the ThreadInfo object. - void FillCPUContext(RawContextCPU* out) const; - - // Returns the pointer and size of general purpose register area. - void GetGeneralPurposeRegisters(void** gp_regs, size_t* size); - - // Returns the pointer and size of float point register area. - void GetFloatingPointRegisters(void** fp_regs, size_t* size); -}; - -} // namespace google_breakpad - -#endif // CLIENT_LINUX_DUMP_WRITER_COMMON_THREAD_INFO_H_ diff --git a/toolkit/crashreporter/google-breakpad/src/client/linux/dump_writer_common/ucontext_reader.cc b/toolkit/crashreporter/google-breakpad/src/client/linux/dump_writer_common/ucontext_reader.cc deleted file mode 100644 index 93b4d9f85..000000000 --- a/toolkit/crashreporter/google-breakpad/src/client/linux/dump_writer_common/ucontext_reader.cc +++ /dev/null @@ -1,259 +0,0 @@ -// Copyright (c) 2014, 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 "client/linux/dump_writer_common/ucontext_reader.h" - -#include "common/linux/linux_libc_support.h" -#include "google_breakpad/common/minidump_format.h" - -namespace google_breakpad { - -// Minidump defines register structures which are different from the raw -// structures which we get from the kernel. These are platform specific -// functions to juggle the ucontext and user structures into minidump format. - -#if defined(__i386__) - -uintptr_t UContextReader::GetStackPointer(const ucontext_t* uc) { - return uc->uc_mcontext.gregs[REG_ESP]; -} - -uintptr_t UContextReader::GetInstructionPointer(const ucontext_t* uc) { - return uc->uc_mcontext.gregs[REG_EIP]; -} - -void UContextReader::FillCPUContext(RawContextCPU *out, const ucontext_t *uc, - const struct _libc_fpstate* fp) { - const greg_t* regs = uc->uc_mcontext.gregs; - - out->context_flags = MD_CONTEXT_X86_FULL | - MD_CONTEXT_X86_FLOATING_POINT; - - out->gs = regs[REG_GS]; - out->fs = regs[REG_FS]; - out->es = regs[REG_ES]; - out->ds = regs[REG_DS]; - - out->edi = regs[REG_EDI]; - out->esi = regs[REG_ESI]; - out->ebx = regs[REG_EBX]; - out->edx = regs[REG_EDX]; - out->ecx = regs[REG_ECX]; - out->eax = regs[REG_EAX]; - - out->ebp = regs[REG_EBP]; - out->eip = regs[REG_EIP]; - out->cs = regs[REG_CS]; - out->eflags = regs[REG_EFL]; - out->esp = regs[REG_UESP]; - out->ss = regs[REG_SS]; - - out->float_save.control_word = fp->cw; - out->float_save.status_word = fp->sw; - out->float_save.tag_word = fp->tag; - out->float_save.error_offset = fp->ipoff; - out->float_save.error_selector = fp->cssel; - out->float_save.data_offset = fp->dataoff; - out->float_save.data_selector = fp->datasel; - - // 8 registers * 10 bytes per register. - my_memcpy(out->float_save.register_area, fp->_st, 10 * 8); -} - -#elif defined(__x86_64) - -uintptr_t UContextReader::GetStackPointer(const ucontext_t* uc) { - return uc->uc_mcontext.gregs[REG_RSP]; -} - -uintptr_t UContextReader::GetInstructionPointer(const ucontext_t* uc) { - return uc->uc_mcontext.gregs[REG_RIP]; -} - -void UContextReader::FillCPUContext(RawContextCPU *out, const ucontext_t *uc, - const struct _libc_fpstate* fpregs) { - const greg_t* regs = uc->uc_mcontext.gregs; - - out->context_flags = MD_CONTEXT_AMD64_FULL; - - out->cs = regs[REG_CSGSFS] & 0xffff; - - out->fs = (regs[REG_CSGSFS] >> 32) & 0xffff; - out->gs = (regs[REG_CSGSFS] >> 16) & 0xffff; - - out->eflags = regs[REG_EFL]; - - out->rax = regs[REG_RAX]; - out->rcx = regs[REG_RCX]; - out->rdx = regs[REG_RDX]; - out->rbx = regs[REG_RBX]; - - out->rsp = regs[REG_RSP]; - out->rbp = regs[REG_RBP]; - out->rsi = regs[REG_RSI]; - out->rdi = regs[REG_RDI]; - out->r8 = regs[REG_R8]; - out->r9 = regs[REG_R9]; - out->r10 = regs[REG_R10]; - out->r11 = regs[REG_R11]; - out->r12 = regs[REG_R12]; - out->r13 = regs[REG_R13]; - out->r14 = regs[REG_R14]; - out->r15 = regs[REG_R15]; - - out->rip = regs[REG_RIP]; - - out->flt_save.control_word = fpregs->cwd; - out->flt_save.status_word = fpregs->swd; - out->flt_save.tag_word = fpregs->ftw; - out->flt_save.error_opcode = fpregs->fop; - out->flt_save.error_offset = fpregs->rip; - out->flt_save.data_offset = fpregs->rdp; - out->flt_save.error_selector = 0; // We don't have this. - out->flt_save.data_selector = 0; // We don't have this. - out->flt_save.mx_csr = fpregs->mxcsr; - out->flt_save.mx_csr_mask = fpregs->mxcr_mask; - my_memcpy(&out->flt_save.float_registers, &fpregs->_st, 8 * 16); - my_memcpy(&out->flt_save.xmm_registers, &fpregs->_xmm, 16 * 16); -} - -#elif defined(__ARM_EABI__) - -uintptr_t UContextReader::GetStackPointer(const ucontext_t* uc) { - return uc->uc_mcontext.arm_sp; -} - -uintptr_t UContextReader::GetInstructionPointer(const ucontext_t* uc) { - return uc->uc_mcontext.arm_pc; -} - -void UContextReader::FillCPUContext(RawContextCPU *out, const ucontext_t *uc) { - 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; - my_memset(&out->float_save.regs, 0, sizeof(out->float_save.regs)); - my_memset(&out->float_save.extra, 0, sizeof(out->float_save.extra)); -} - -#elif defined(__aarch64__) - -uintptr_t UContextReader::GetStackPointer(const ucontext_t* uc) { - return uc->uc_mcontext.sp; -} - -uintptr_t UContextReader::GetInstructionPointer(const ucontext_t* uc) { - return uc->uc_mcontext.pc; -} - -void UContextReader::FillCPUContext(RawContextCPU *out, const ucontext_t *uc, - const struct fpsimd_context* fpregs) { - out->context_flags = MD_CONTEXT_ARM64_FULL; - - out->cpsr = static_cast<uint32_t>(uc->uc_mcontext.pstate); - for (int i = 0; i < MD_CONTEXT_ARM64_REG_SP; ++i) - out->iregs[i] = uc->uc_mcontext.regs[i]; - out->iregs[MD_CONTEXT_ARM64_REG_SP] = uc->uc_mcontext.sp; - out->iregs[MD_CONTEXT_ARM64_REG_PC] = uc->uc_mcontext.pc; - - out->float_save.fpsr = fpregs->fpsr; - out->float_save.fpcr = fpregs->fpcr; - my_memcpy(&out->float_save.regs, &fpregs->vregs, - MD_FLOATINGSAVEAREA_ARM64_FPR_COUNT * 16); -} - -#elif defined(__mips__) - -uintptr_t UContextReader::GetStackPointer(const ucontext_t* uc) { - return uc->uc_mcontext.gregs[MD_CONTEXT_MIPS_REG_SP]; -} - -uintptr_t UContextReader::GetInstructionPointer(const ucontext_t* uc) { - return uc->uc_mcontext.pc; -} - -void UContextReader::FillCPUContext(RawContextCPU *out, const ucontext_t *uc) { -#if _MIPS_SIM == _ABI64 - out->context_flags = MD_CONTEXT_MIPS64_FULL; -#elif _MIPS_SIM == _ABIO32 - out->context_flags = MD_CONTEXT_MIPS_FULL; -#else -#error "This mips ABI is currently not supported (n32)" -#endif - - for (int i = 0; i < MD_CONTEXT_MIPS_GPR_COUNT; ++i) - out->iregs[i] = uc->uc_mcontext.gregs[i]; - - out->mdhi = uc->uc_mcontext.mdhi; - out->mdlo = uc->uc_mcontext.mdlo; - - out->hi[0] = uc->uc_mcontext.hi1; - out->hi[1] = uc->uc_mcontext.hi2; - out->hi[2] = uc->uc_mcontext.hi3; - out->lo[0] = uc->uc_mcontext.lo1; - out->lo[1] = uc->uc_mcontext.lo2; - out->lo[2] = uc->uc_mcontext.lo3; - out->dsp_control = uc->uc_mcontext.dsp; - - out->epc = uc->uc_mcontext.pc; - out->badvaddr = 0; // Not reported in signal context. - out->status = 0; // Not reported in signal context. - out->cause = 0; // Not reported in signal context. - - for (int i = 0; i < MD_FLOATINGSAVEAREA_MIPS_FPR_COUNT; ++i) - out->float_save.regs[i] = uc->uc_mcontext.fpregs.fp_r.fp_dregs[i]; - - out->float_save.fpcsr = uc->uc_mcontext.fpc_csr; -#if _MIPS_SIM == _ABIO32 - out->float_save.fir = uc->uc_mcontext.fpc_eir; // Unused. -#endif -} -#endif - -} // namespace google_breakpad diff --git a/toolkit/crashreporter/google-breakpad/src/client/linux/dump_writer_common/ucontext_reader.h b/toolkit/crashreporter/google-breakpad/src/client/linux/dump_writer_common/ucontext_reader.h deleted file mode 100644 index 2369a9ad3..000000000 --- a/toolkit/crashreporter/google-breakpad/src/client/linux/dump_writer_common/ucontext_reader.h +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright (c) 2014, 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_DUMP_WRITER_COMMON_UCONTEXT_READER_H -#define CLIENT_LINUX_DUMP_WRITER_COMMON_UCONTEXT_READER_H - -#include <sys/ucontext.h> -#include <sys/user.h> - -#include "client/linux/dump_writer_common/raw_context_cpu.h" -#include "common/memory.h" -#include "google_breakpad/common/minidump_format.h" - -namespace google_breakpad { - -// Wraps platform-dependent implementations of accessors to ucontext structs. -struct UContextReader { - static uintptr_t GetStackPointer(const ucontext_t* uc); - - static uintptr_t GetInstructionPointer(const ucontext_t* uc); - - // Juggle a arch-specific ucontext into a minidump format - // out: the minidump structure - // info: the collection of register structures. -#if defined(__i386__) || defined(__x86_64) - static void FillCPUContext(RawContextCPU *out, const ucontext_t *uc, - const struct _libc_fpstate* fp); -#elif defined(__aarch64__) - static void FillCPUContext(RawContextCPU *out, const ucontext_t *uc, - const struct fpsimd_context* fpregs); -#else - static void FillCPUContext(RawContextCPU *out, const ucontext_t *uc); -#endif -}; - -} // namespace google_breakpad - -#endif // CLIENT_LINUX_DUMP_WRITER_COMMON_UCONTEXT_READER_H diff --git a/toolkit/crashreporter/google-breakpad/src/client/linux/handler/exception_handler.cc b/toolkit/crashreporter/google-breakpad/src/client/linux/handler/exception_handler.cc deleted file mode 100644 index 288e0bb69..000000000 --- a/toolkit/crashreporter/google-breakpad/src/client/linux/handler/exception_handler.cc +++ /dev/null @@ -1,789 +0,0 @@ -// Copyright (c) 2010 Google Inc. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -// The ExceptionHandler object installs signal handlers for a number of -// signals. We rely on the signal handler running on the thread which crashed -// in order to identify it. This is true of the synchronous signals (SEGV etc), -// but not true of ABRT. Thus, if you send ABRT to yourself in a program which -// uses ExceptionHandler, you need to use tgkill to direct it to the current -// thread. -// -// The signal flow looks like this: -// -// SignalHandler (uses a global stack of ExceptionHandler objects to find -// | one to handle the signal. If the first rejects it, try -// | the second etc...) -// V -// HandleSignal ----------------------------| (clones a new process which -// | | shares an address space with -// (wait for cloned | the crashed process. This -// process) | allows us to ptrace the crashed -// | | process) -// V V -// (set signal handler to ThreadEntry (static function to bounce -// SIG_DFL and rethrow, | back into the object) -// killing the crashed | -// process) V -// DoDump (writes minidump) -// | -// V -// sys_exit -// - -// This code is a little fragmented. Different functions of the ExceptionHandler -// class run in a number of different contexts. Some of them run in a normal -// context and are easy to code, others run in a compromised context and the -// restrictions at the top of minidump_writer.cc apply: no libc and use the -// alternative malloc. Each function should have comment above it detailing the -// context which it runs in. - -#include "client/linux/handler/exception_handler.h" - -#include <errno.h> -#include <fcntl.h> -#include <linux/limits.h> -#include <pthread.h> -#include <sched.h> -#include <signal.h> -#include <stdio.h> -#include <sys/mman.h> -#include <sys/prctl.h> -#include <sys/syscall.h> -#include <sys/wait.h> -#include <unistd.h> - -#include <sys/signal.h> -#include <sys/ucontext.h> -#include <sys/user.h> -#include <ucontext.h> - -#include <algorithm> -#include <utility> -#include <vector> - -#include "common/basictypes.h" -#include "common/linux/linux_libc_support.h" -#include "common/memory.h" -#include "client/linux/log/log.h" -#include "client/linux/microdump_writer/microdump_writer.h" -#include "client/linux/minidump_writer/linux_dumper.h" -#include "client/linux/minidump_writer/minidump_writer.h" -#include "common/linux/eintr_wrapper.h" -#include "third_party/lss/linux_syscall_support.h" - -#if defined(__ANDROID__) -#include "linux/sched.h" -#endif - -#ifndef PR_SET_PTRACER -#define PR_SET_PTRACER 0x59616d61 -#endif - -// A wrapper for the tgkill syscall: send a signal to a specific thread. -static int tgkill(pid_t tgid, pid_t tid, int sig) { - return syscall(__NR_tgkill, tgid, tid, sig); - return 0; -} - -namespace google_breakpad { - -namespace { -// The list of signals which we consider to be crashes. The default action for -// all these signals must be Core (see man 7 signal) because we rethrow the -// signal after handling it and expect that it'll be fatal. -const int kExceptionSignals[] = { - SIGSEGV, SIGABRT, SIGFPE, SIGILL, SIGBUS, SIGTRAP -}; -const int kNumHandledSignals = - sizeof(kExceptionSignals) / sizeof(kExceptionSignals[0]); -struct sigaction old_handlers[kNumHandledSignals]; -bool handlers_installed = false; - -// InstallAlternateStackLocked will store the newly installed stack in new_stack -// and (if it exists) the previously installed stack in old_stack. -stack_t old_stack; -stack_t new_stack; -bool stack_installed = false; - -// Create an alternative stack to run the signal handlers on. This is done since -// the signal might have been caused by a stack overflow. -// Runs before crashing: normal context. -void InstallAlternateStackLocked() { - if (stack_installed) - return; - - memset(&old_stack, 0, sizeof(old_stack)); - memset(&new_stack, 0, sizeof(new_stack)); - - // SIGSTKSZ may be too small to prevent the signal handlers from overrunning - // the alternative stack. Ensure that the size of the alternative stack is - // large enough. - static const unsigned kSigStackSize = std::max(16384, SIGSTKSZ); - - // Only set an alternative stack if there isn't already one, or if the current - // one is too small. - if (sys_sigaltstack(NULL, &old_stack) == -1 || !old_stack.ss_sp || - old_stack.ss_size < kSigStackSize) { - new_stack.ss_sp = calloc(1, kSigStackSize); - new_stack.ss_size = kSigStackSize; - - if (sys_sigaltstack(&new_stack, NULL) == -1) { - free(new_stack.ss_sp); - return; - } - stack_installed = true; - } -} - -// Runs before crashing: normal context. -void RestoreAlternateStackLocked() { - if (!stack_installed) - return; - - stack_t current_stack; - if (sys_sigaltstack(NULL, ¤t_stack) == -1) - return; - - // Only restore the old_stack if the current alternative stack is the one - // installed by the call to InstallAlternateStackLocked. - if (current_stack.ss_sp == new_stack.ss_sp) { - if (old_stack.ss_sp) { - if (sys_sigaltstack(&old_stack, NULL) == -1) - return; - } else { - stack_t disable_stack; - disable_stack.ss_flags = SS_DISABLE; - if (sys_sigaltstack(&disable_stack, NULL) == -1) - return; - } - } - - free(new_stack.ss_sp); - stack_installed = false; -} - -void InstallDefaultHandler(int sig) { -#if defined(__ANDROID__) - // Android L+ expose signal and sigaction symbols that override the system - // ones. There is a bug in these functions where a request to set the handler - // to SIG_DFL is ignored. In that case, an infinite loop is entered as the - // signal is repeatedly sent to breakpad's signal handler. - // To work around this, directly call the system's sigaction. - struct kernel_sigaction sa; - memset(&sa, 0, sizeof(sa)); - sys_sigemptyset(&sa.sa_mask); - sa.sa_handler_ = SIG_DFL; - sa.sa_flags = SA_RESTART; - sys_rt_sigaction(sig, &sa, NULL, sizeof(kernel_sigset_t)); -#else - signal(sig, SIG_DFL); -#endif -} - -// The global exception handler stack. This is needed because there may exist -// multiple ExceptionHandler instances in a process. Each will have itself -// registered in this stack. -std::vector<ExceptionHandler*>* g_handler_stack_ = NULL; -pthread_mutex_t g_handler_stack_mutex_ = PTHREAD_MUTEX_INITIALIZER; - -// sizeof(CrashContext) can be too big w.r.t the size of alternatate stack -// for SignalHandler(). Keep the crash context as a .bss field. Exception -// handlers are serialized by the |g_handler_stack_mutex_| and at most one at a -// time can use |g_crash_context_|. -ExceptionHandler::CrashContext g_crash_context_; - -} // namespace - -// Runs before crashing: normal context. -ExceptionHandler::ExceptionHandler(const MinidumpDescriptor& descriptor, - FilterCallback filter, - MinidumpCallback callback, - void* callback_context, - bool install_handler, - const int server_fd) - : filter_(filter), - callback_(callback), - callback_context_(callback_context), - minidump_descriptor_(descriptor), - crash_handler_(NULL) { - if (server_fd >= 0) - crash_generation_client_.reset(CrashGenerationClient::TryCreate(server_fd)); - - if (!IsOutOfProcess() && !minidump_descriptor_.IsFD() && - !minidump_descriptor_.IsMicrodumpOnConsole()) - minidump_descriptor_.UpdatePath(); - -#if defined(__ANDROID__) - if (minidump_descriptor_.IsMicrodumpOnConsole()) - logger::initializeCrashLogWriter(); -#endif - - pthread_mutex_lock(&g_handler_stack_mutex_); - - // Pre-fault the crash context struct. This is to avoid failing due to OOM - // if handling an exception when the process ran out of virtual memory. - memset(&g_crash_context_, 0, sizeof(g_crash_context_)); - - if (!g_handler_stack_) - g_handler_stack_ = new std::vector<ExceptionHandler*>; - if (install_handler) { - InstallAlternateStackLocked(); - InstallHandlersLocked(); - } - g_handler_stack_->push_back(this); - pthread_mutex_unlock(&g_handler_stack_mutex_); -} - -// Runs before crashing: normal context. -ExceptionHandler::~ExceptionHandler() { - pthread_mutex_lock(&g_handler_stack_mutex_); - std::vector<ExceptionHandler*>::iterator handler = - std::find(g_handler_stack_->begin(), g_handler_stack_->end(), this); - g_handler_stack_->erase(handler); - if (g_handler_stack_->empty()) { - delete g_handler_stack_; - g_handler_stack_ = NULL; - RestoreAlternateStackLocked(); - RestoreHandlersLocked(); - } - pthread_mutex_unlock(&g_handler_stack_mutex_); -} - -// Runs before crashing: normal context. -// static -bool ExceptionHandler::InstallHandlersLocked() { - if (handlers_installed) - return false; - - // Fail if unable to store all the old handlers. - for (int i = 0; i < kNumHandledSignals; ++i) { - if (sigaction(kExceptionSignals[i], NULL, &old_handlers[i]) == -1) - return false; - } - - struct sigaction sa; - memset(&sa, 0, sizeof(sa)); - sigemptyset(&sa.sa_mask); - - // Mask all exception signals when we're handling one of them. - for (int i = 0; i < kNumHandledSignals; ++i) - sigaddset(&sa.sa_mask, kExceptionSignals[i]); - - sa.sa_sigaction = SignalHandler; - sa.sa_flags = SA_ONSTACK | SA_SIGINFO; - - for (int i = 0; i < kNumHandledSignals; ++i) { - if (sigaction(kExceptionSignals[i], &sa, NULL) == -1) { - // At this point it is impractical to back out changes, and so failure to - // install a signal is intentionally ignored. - } - } - handlers_installed = true; - return true; -} - -// This function runs in a compromised context: see the top of the file. -// Runs on the crashing thread. -// static -void ExceptionHandler::RestoreHandlersLocked() { - if (!handlers_installed) - return; - - for (int i = 0; i < kNumHandledSignals; ++i) { - if (sigaction(kExceptionSignals[i], &old_handlers[i], NULL) == -1) { - InstallDefaultHandler(kExceptionSignals[i]); - } - } - handlers_installed = false; -} - -// 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(&g_handler_stack_mutex_); - - // Sometimes, Breakpad runs inside a process where some other buggy code - // saves and restores signal handlers temporarily with 'signal' - // instead of 'sigaction'. This loses the SA_SIGINFO flag associated - // with this function. As a consequence, the values of 'info' and 'uc' - // become totally bogus, generally inducing a crash. - // - // The following code tries to detect this case. When it does, it - // resets the signal handlers with sigaction + SA_SIGINFO and returns. - // This forces the signal to be thrown again, but this time the kernel - // will call the function with the right arguments. - struct sigaction cur_handler; - if (sigaction(sig, NULL, &cur_handler) == 0 && - (cur_handler.sa_flags & SA_SIGINFO) == 0) { - // Reset signal handler with the right flags. - sigemptyset(&cur_handler.sa_mask); - sigaddset(&cur_handler.sa_mask, sig); - - cur_handler.sa_sigaction = SignalHandler; - cur_handler.sa_flags = SA_ONSTACK | SA_SIGINFO; - - if (sigaction(sig, &cur_handler, NULL) == -1) { - // When resetting the handler fails, try to reset the - // default one to avoid an infinite loop here. - InstallDefaultHandler(sig); - } - pthread_mutex_unlock(&g_handler_stack_mutex_); - return; - } - - bool handled = false; - for (int i = g_handler_stack_->size() - 1; !handled && i >= 0; --i) { - handled = (*g_handler_stack_)[i]->HandleSignal(sig, info, uc); - } - - // Upon returning from this signal handler, sig will become unmasked and then - // it will be retriggered. If one of the ExceptionHandlers handled it - // successfully, restore the default handler. Otherwise, restore the - // previously installed handler. Then, when the signal is retriggered, it will - // be delivered to the appropriate handler. - if (handled) { - InstallDefaultHandler(sig); - } else { - RestoreHandlersLocked(); - } - - pthread_mutex_unlock(&g_handler_stack_mutex_); - - // info->si_code <= 0 iff SI_FROMUSER (SI_FROMKERNEL otherwise). - if (info->si_code <= 0 || sig == SIGABRT) { - // This signal was triggered by somebody sending us the signal with kill(). - // In order to retrigger it, we have to queue a new signal by calling - // kill() ourselves. The special case (si_pid == 0 && sig == SIGABRT) is - // due to the kernel sending a SIGABRT from a user request via SysRQ. - if (tgkill(getpid(), syscall(__NR_gettid), sig) < 0) { - // If we failed to kill ourselves (e.g. because a sandbox disallows us - // to do so), we instead resort to terminating our process. This will - // result in an incorrect exit code. - _exit(1); - } - } else { - // This was a synchronous signal triggered by a hard fault (e.g. SIGSEGV). - // No need to reissue the signal. It will automatically trigger again, - // when we return from the signal handler. - } -} - -struct ThreadArgument { - pid_t pid; // the crashing process - const MinidumpDescriptor* minidump_descriptor; - ExceptionHandler* handler; - const void* context; // a CrashContext structure - size_t context_size; -}; - -// This is the entry function for the cloned process. We are in a compromised -// context here: see the top of the file. -// static -int ExceptionHandler::ThreadEntry(void *arg) { - const ThreadArgument *thread_arg = reinterpret_cast<ThreadArgument*>(arg); - - // Block here until the crashing process unblocks us when - // we're allowed to use ptrace - thread_arg->handler->WaitForContinueSignal(); - - return thread_arg->handler->DoDump(thread_arg->pid, thread_arg->context, - thread_arg->context_size) == false; -} - -// This function runs in a compromised context: see the top of the file. -// Runs on the crashing thread. -bool ExceptionHandler::HandleSignal(int sig, siginfo_t* info, void* uc) { - if (filter_ && !filter_(callback_context_)) - return false; - - // Allow ourselves to be dumped if the signal is trusted. - bool signal_trusted = info->si_code > 0; - bool signal_pid_trusted = info->si_code == SI_USER || - info->si_code == SI_TKILL; - if (signal_trusted || (signal_pid_trusted && info->si_pid == getpid())) { - sys_prctl(PR_SET_DUMPABLE, 1, 0, 0, 0); - } - - // Fill in all the holes in the struct to make Valgrind happy. - memset(&g_crash_context_, 0, sizeof(g_crash_context_)); - memcpy(&g_crash_context_.siginfo, info, sizeof(siginfo_t)); - memcpy(&g_crash_context_.context, uc, sizeof(ucontext_t)); -#if defined(__aarch64__) - ucontext_t* uc_ptr = (ucontext_t*)uc; - struct fpsimd_context* fp_ptr = - (struct fpsimd_context*)&uc_ptr->uc_mcontext.__reserved; - if (fp_ptr->head.magic == FPSIMD_MAGIC) { - memcpy(&g_crash_context_.float_state, fp_ptr, - sizeof(g_crash_context_.float_state)); - } -#elif !defined(__ARM_EABI__) && !defined(__mips__) - // FP state is not part of user ABI on ARM Linux. - // In case of MIPS Linux FP state is already part of ucontext_t - // and 'float_state' is not a member of CrashContext. - ucontext_t* uc_ptr = (ucontext_t*)uc; - if (uc_ptr->uc_mcontext.fpregs) { - memcpy(&g_crash_context_.float_state, uc_ptr->uc_mcontext.fpregs, - sizeof(g_crash_context_.float_state)); - } -#endif - g_crash_context_.tid = syscall(__NR_gettid); - if (crash_handler_ != NULL) { - if (crash_handler_(&g_crash_context_, sizeof(g_crash_context_), - callback_context_)) { - return true; - } - } - return GenerateDump(&g_crash_context_); -} - -// This is a public interface to HandleSignal that allows the client to -// generate a crash dump. This function may run in a compromised context. -bool ExceptionHandler::SimulateSignalDelivery(int sig) { - siginfo_t siginfo = {}; - // Mimic a trusted signal to allow tracing the process (see - // ExceptionHandler::HandleSignal(). - siginfo.si_code = SI_USER; - siginfo.si_pid = getpid(); - ucontext_t context; - getcontext(&context); - return HandleSignal(sig, &siginfo, &context); -} - -// 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)); - - // Allocating too much stack isn't a problem, and better to err on the side - // of caution than smash it into random locations. - static const unsigned kChildStackSize = 16000; - PageAllocator allocator; - uint8_t* stack = reinterpret_cast<uint8_t*>(allocator.Alloc(kChildStackSize)); - if (!stack) - return false; - // clone() needs the top-most address. (scrub just to be safe) - stack += kChildStackSize; - my_memset(stack - 16, 0, 16); - - ThreadArgument thread_arg; - thread_arg.handler = this; - thread_arg.minidump_descriptor = &minidump_descriptor_; - thread_arg.pid = getpid(); - thread_arg.context = context; - thread_arg.context_size = sizeof(*context); - - // We need to explicitly enable ptrace of parent processes on some - // kernels, but we need to know the PID of the cloned process before we - // can do this. Create a pipe here which we can use to block the - // cloned process after creating it, until we have explicitly enabled ptrace - if (sys_pipe(fdes) == -1) { - // Creating the pipe failed. We'll log an error but carry on anyway, - // as we'll probably still get a useful crash report. All that will happen - // is the write() and read() calls will fail with EBADF - static const char no_pipe_msg[] = "ExceptionHandler::GenerateDump " - "sys_pipe failed:"; - logger::write(no_pipe_msg, sizeof(no_pipe_msg) - 1); - logger::write(strerror(errno), strlen(strerror(errno))); - logger::write("\n", 1); - - // Ensure fdes[0] and fdes[1] are invalid file descriptors. - fdes[0] = fdes[1] = -1; - } - - const pid_t child = sys_clone( - ThreadEntry, stack, CLONE_FILES | CLONE_FS | CLONE_UNTRACED, - &thread_arg, NULL, NULL, NULL); - if (child == -1) { - sys_close(fdes[0]); - sys_close(fdes[1]); - return false; - } - - if (child != 0) { - static const char clonedMsg[] = - "ExceptionHandler::GenerateDump cloned child "; - char pidMsg[32]; - - unsigned int pidLen = my_uint_len(child); - my_uitos(pidMsg, child, pidLen); - - logger::write(clonedMsg, my_strlen(clonedMsg)); - logger::write(pidMsg, pidLen); - logger::write("\n", 1); - } else { - static const char childMsg[] = - "ExceptionHandler::GenerateDump I'm the child\n"; - logger::write(childMsg, my_strlen(childMsg)); - } - - // Allow the child to ptrace us - sys_prctl(PR_SET_PTRACER, child, 0, 0, 0); - SendContinueSignalToChild(); - int status; - const int r = HANDLE_EINTR(sys_waitpid(child, &status, __WALL)); - - sys_close(fdes[0]); - sys_close(fdes[1]); - - if (r == -1) { - static const char msg[] = "ExceptionHandler::GenerateDump waitpid failed:"; - logger::write(msg, sizeof(msg) - 1); - logger::write(strerror(errno), strlen(strerror(errno))); - logger::write("\n", 1); - } - - bool success = r != -1 && WIFEXITED(status) && WEXITSTATUS(status) == 0; - if (callback_) - success = callback_(minidump_descriptor_, callback_context_, success); - return success; -} - -// This function runs in a compromised context: see the top of the file. -void ExceptionHandler::SendContinueSignalToChild() { - static const char okToContinueMessage = 'a'; - int r; - r = HANDLE_EINTR(sys_write(fdes[1], &okToContinueMessage, sizeof(char))); - if (r == -1) { - static const char msg[] = "ExceptionHandler::SendContinueSignalToChild " - "sys_write failed:"; - logger::write(msg, sizeof(msg) - 1); - logger::write(strerror(errno), strlen(strerror(errno))); - logger::write("\n", 1); - } - - const char* msg = "ExceptionHandler::SendContinueSignalToChild sent continue signal to child\n"; - logger::write(msg, my_strlen(msg)); -} - -// This function runs in a compromised context: see the top of the file. -// Runs on the cloned process. -void ExceptionHandler::WaitForContinueSignal() { - int r; - char receivedMessage; - - const char* waitMsg = "ExceptionHandler::WaitForContinueSignal waiting for continue signal...\n"; - logger::write(waitMsg, my_strlen(waitMsg)); - - r = HANDLE_EINTR(sys_read(fdes[0], &receivedMessage, sizeof(char))); - if (r == -1) { - static const char msg[] = "ExceptionHandler::WaitForContinueSignal " - "sys_read failed:"; - logger::write(msg, sizeof(msg) - 1); - logger::write(strerror(errno), strlen(strerror(errno))); - logger::write("\n", 1); - } -} - -// This function runs in a compromised context: see the top of the file. -// Runs on the cloned process. -bool ExceptionHandler::DoDump(pid_t crashing_process, const void* context, - size_t context_size) { - if (minidump_descriptor_.IsMicrodumpOnConsole()) { - return google_breakpad::WriteMicrodump( - crashing_process, - context, - context_size, - mapping_list_, - *minidump_descriptor_.microdump_extra_info()); - } - if (minidump_descriptor_.IsFD()) { - return google_breakpad::WriteMinidump(minidump_descriptor_.fd(), - minidump_descriptor_.size_limit(), - crashing_process, - context, - context_size, - mapping_list_, - app_memory_list_); - } - return google_breakpad::WriteMinidump(minidump_descriptor_.path(), - minidump_descriptor_.size_limit(), - crashing_process, - context, - context_size, - mapping_list_, - app_memory_list_); -} - -// static -bool ExceptionHandler::WriteMinidump(const string& dump_path, - MinidumpCallback callback, - void* callback_context) { - MinidumpDescriptor descriptor(dump_path); - ExceptionHandler eh(descriptor, NULL, callback, callback_context, false, -1); - return eh.WriteMinidump(); -} - -// In order to making using EBP to calculate the desired value for ESP -// a valid operation, ensure that this function is compiled with a -// frame pointer using the following attribute. This attribute -// is supported on GCC but not on clang. -#if defined(__i386__) && defined(__GNUC__) && !defined(__clang__) -__attribute__((optimize("no-omit-frame-pointer"))) -#endif -bool ExceptionHandler::WriteMinidump() { - if (!IsOutOfProcess() && !minidump_descriptor_.IsFD() && - !minidump_descriptor_.IsMicrodumpOnConsole()) { - // Update the path of the minidump so that this can be called multiple times - // and new files are created for each minidump. This is done before the - // generation happens, as clients may want to access the MinidumpDescriptor - // after this call to find the exact path to the minidump file. - minidump_descriptor_.UpdatePath(); - } else if (minidump_descriptor_.IsFD()) { - // Reposition the FD to its beginning and resize it to get rid of the - // previous minidump info. - lseek(minidump_descriptor_.fd(), 0, SEEK_SET); - ignore_result(ftruncate(minidump_descriptor_.fd(), 0)); - } - - // Allow this process to be dumped. - sys_prctl(PR_SET_DUMPABLE, 1, 0, 0, 0); - - CrashContext context; - int getcontext_result = getcontext(&context.context); - if (getcontext_result) - return false; - -#if defined(__i386__) - // In CPUFillFromUContext in minidumpwriter.cc the stack pointer is retrieved - // from REG_UESP instead of from REG_ESP. REG_UESP is the user stack pointer - // and it only makes sense when running in kernel mode with a different stack - // pointer. When WriteMiniDump is called during normal processing REG_UESP is - // zero which leads to bad minidump files. - if (!context.context.uc_mcontext.gregs[REG_UESP]) { - // If REG_UESP is set to REG_ESP then that includes the stack space for the - // CrashContext object in this function, which is about 128 KB. Since the - // Linux dumper only records 32 KB of stack this would mean that nothing - // useful would be recorded. A better option is to set REG_UESP to REG_EBP, - // perhaps with a small negative offset in case there is any code that - // objects to them being equal. - context.context.uc_mcontext.gregs[REG_UESP] = - context.context.uc_mcontext.gregs[REG_EBP] - 16; - // The stack saving is based off of REG_ESP so it must be set to match the - // new REG_UESP. - context.context.uc_mcontext.gregs[REG_ESP] = - context.context.uc_mcontext.gregs[REG_UESP]; - } -#endif - -#if !defined(__ARM_EABI__) && !defined(__aarch64__) && !defined(__mips__) - // FPU state is not part of ARM EABI ucontext_t. - memcpy(&context.float_state, context.context.uc_mcontext.fpregs, - sizeof(context.float_state)); -#endif - context.tid = sys_gettid(); - - // Add an exception stream to the minidump for better reporting. - memset(&context.siginfo, 0, sizeof(context.siginfo)); - context.siginfo.si_signo = MD_EXCEPTION_CODE_LIN_DUMP_REQUESTED; -#if defined(__i386__) - context.siginfo.si_addr = - reinterpret_cast<void*>(context.context.uc_mcontext.gregs[REG_EIP]); -#elif defined(__x86_64__) - context.siginfo.si_addr = - reinterpret_cast<void*>(context.context.uc_mcontext.gregs[REG_RIP]); -#elif defined(__arm__) - context.siginfo.si_addr = - reinterpret_cast<void*>(context.context.uc_mcontext.arm_pc); -#elif defined(__aarch64__) - context.siginfo.si_addr = - reinterpret_cast<void*>(context.context.uc_mcontext.pc); -#elif defined(__mips__) - context.siginfo.si_addr = - reinterpret_cast<void*>(context.context.uc_mcontext.pc); -#else -#error "This code has not been ported to your platform yet." -#endif - - return GenerateDump(&context); -} - -void ExceptionHandler::AddMappingInfo(const string& name, - const uint8_t identifier[sizeof(MDGUID)], - uintptr_t start_address, - size_t mapping_size, - size_t file_offset) { - MappingInfo info; - info.start_addr = start_address; - info.size = mapping_size; - info.offset = file_offset; - strncpy(info.name, name.c_str(), sizeof(info.name) - 1); - info.name[sizeof(info.name) - 1] = '\0'; - - MappingEntry mapping; - mapping.first = info; - memcpy(mapping.second, identifier, sizeof(MDGUID)); - mapping_list_.push_back(mapping); -} - -void ExceptionHandler::RegisterAppMemory(void* ptr, size_t length) { - AppMemoryList::iterator iter = - std::find(app_memory_list_.begin(), app_memory_list_.end(), ptr); - if (iter != app_memory_list_.end()) { - // Don't allow registering the same pointer twice. - return; - } - - AppMemory app_memory; - app_memory.ptr = ptr; - app_memory.length = length; - app_memory_list_.push_back(app_memory); -} - -void ExceptionHandler::UnregisterAppMemory(void* ptr) { - AppMemoryList::iterator iter = - std::find(app_memory_list_.begin(), app_memory_list_.end(), ptr); - if (iter != app_memory_list_.end()) { - app_memory_list_.erase(iter); - } -} - -// static -bool ExceptionHandler::WriteMinidumpForChild(pid_t child, - pid_t child_blamed_thread, - const string& dump_path, - MinidumpCallback callback, - void* callback_context) { - // This function is not run in a compromised context. - MinidumpDescriptor descriptor(dump_path); - descriptor.UpdatePath(); - if (!google_breakpad::WriteMinidump(descriptor.path(), - child, - child_blamed_thread)) - return false; - - return callback ? callback(descriptor, callback_context, true) : true; -} - -} // namespace google_breakpad diff --git a/toolkit/crashreporter/google-breakpad/src/client/linux/handler/exception_handler.h b/toolkit/crashreporter/google-breakpad/src/client/linux/handler/exception_handler.h deleted file mode 100644 index 846df772f..000000000 --- a/toolkit/crashreporter/google-breakpad/src/client/linux/handler/exception_handler.h +++ /dev/null @@ -1,278 +0,0 @@ -// 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. - -#ifndef CLIENT_LINUX_HANDLER_EXCEPTION_HANDLER_H_ -#define CLIENT_LINUX_HANDLER_EXCEPTION_HANDLER_H_ - -#include <signal.h> -#include <stdint.h> -#include <stdio.h> -#include <sys/ucontext.h> - -#include <string> - -#include "client/linux/crash_generation/crash_generation_client.h" -#include "client/linux/handler/minidump_descriptor.h" -#include "client/linux/minidump_writer/minidump_writer.h" -#include "common/scoped_ptr.h" -#include "common/using_std_string.h" -#include "google_breakpad/common/minidump_format.h" - -namespace google_breakpad { - -// ExceptionHandler -// -// ExceptionHandler can write a minidump file when an exception occurs, -// or when WriteMinidump() is called explicitly by your program. -// -// To have the exception handler write minidumps when an uncaught exception -// (crash) occurs, you should create an instance early in the execution -// of your program, and keep it around for the entire time you want to -// have crash handling active (typically, until shutdown). -// (NOTE): There should be only be one this kind of exception handler -// object per process. -// -// If you want to write minidumps without installing the exception handler, -// you can create an ExceptionHandler with install_handler set to false, -// then call WriteMinidump. You can also use this technique if you want to -// use different minidump callbacks for different call sites. -// -// In either case, a callback function is called when a minidump is written, -// which receives the full path or file descriptor of the minidump. The -// caller can collect and write additional application state to that minidump, -// and launch an external crash-reporting application. -// -// Caller should try to make the callbacks as crash-friendly as possible, -// it should avoid use heap memory allocation as much as possible. - -class ExceptionHandler { - public: - // A callback function to run before Breakpad performs any substantial - // processing of an exception. A FilterCallback is called before writing - // a minidump. |context| is the parameter supplied by the user as - // callback_context when the handler was created. - // - // If a FilterCallback returns true, Breakpad will continue processing, - // attempting to write a minidump. If a FilterCallback returns false, - // Breakpad will immediately report the exception as unhandled without - // writing a minidump, allowing another handler the opportunity to handle it. - typedef bool (*FilterCallback)(void *context); - - // A callback function to run after the minidump has been written. - // |descriptor| contains the file descriptor or file path containing the - // minidump. |context| is the parameter supplied by the user as - // callback_context when the handler was created. |succeeded| indicates - // whether a minidump file was successfully written. - // - // If an exception occurred and the callback returns true, Breakpad will - // treat the exception as fully-handled, suppressing any other handlers from - // being notified of the exception. If the callback returns false, Breakpad - // will treat the exception as unhandled, and allow another handler to handle - // it. If there are no other handlers, Breakpad will report the exception to - // the system as unhandled, allowing a debugger or native crash dialog the - // opportunity to handle the exception. Most callback implementations - // should normally return the value of |succeeded|, or when they wish to - // not report an exception of handled, false. Callbacks will rarely want to - // return true directly (unless |succeeded| is true). - typedef bool (*MinidumpCallback)(const MinidumpDescriptor& descriptor, - void* context, - bool succeeded); - - // In certain cases, a user may wish to handle the generation of the minidump - // themselves. In this case, they can install a handler callback which is - // called when a crash has occurred. If this function returns true, no other - // processing of occurs and the process will shortly be crashed. If this - // returns false, the normal processing continues. - typedef bool (*HandlerCallback)(const void* crash_context, - size_t crash_context_size, - void* context); - - // Creates a new ExceptionHandler instance to handle writing minidumps. - // Before writing a minidump, the optional |filter| callback will be called. - // Its return value determines whether or not Breakpad should write a - // minidump. The minidump content will be written to the file path or file - // descriptor from |descriptor|, and the optional |callback| is called after - // writing the dump file, as described above. - // If install_handler is true, then a minidump will be written whenever - // an unhandled exception occurs. If it is false, minidumps will only - // be written when WriteMinidump is called. - // If |server_fd| is valid, the minidump is generated out-of-process. If it - // is -1, in-process generation will always be used. - ExceptionHandler(const MinidumpDescriptor& descriptor, - FilterCallback filter, - MinidumpCallback callback, - void* callback_context, - bool install_handler, - const int server_fd); - ~ExceptionHandler(); - - const MinidumpDescriptor& minidump_descriptor() const { - return minidump_descriptor_; - } - - void set_minidump_descriptor(const MinidumpDescriptor& descriptor) { - minidump_descriptor_ = descriptor; - } - - void set_crash_handler(HandlerCallback callback) { - crash_handler_ = callback; - } - - void set_crash_generation_client(CrashGenerationClient* client) { - crash_generation_client_.reset(client); - } - - // Writes a minidump immediately. This can be used to capture the execution - // state independently of a crash. - // Returns true on success. - // If the ExceptionHandler has been created with a path, a new file is - // generated for each minidump. The file path can be retrieved in the - // MinidumpDescriptor passed to the MinidumpCallback or by accessing the - // MinidumpDescriptor directly from the ExceptionHandler (with - // minidump_descriptor()). - // If the ExceptionHandler has been created with a file descriptor, the file - // descriptor is repositioned to its beginning and the previous generated - // minidump is overwritten. - // Note that this method is not supposed to be called from a compromised - // context as it uses the heap. - bool WriteMinidump(); - - // Convenience form of WriteMinidump which does not require an - // ExceptionHandler instance. - static bool WriteMinidump(const string& dump_path, - MinidumpCallback callback, - void* callback_context); - - // Write a minidump of |child| immediately. This can be used to - // capture the execution state of |child| independently of a crash. - // Pass a meaningful |child_blamed_thread| to make that thread in - // the child process the one from which a crash signature is - // extracted. - // - // WARNING: the return of this function *must* happen before - // the code that will eventually reap |child| executes. - // Otherwise there's a pernicious race condition in which |child| - // exits, is reaped, another process created with its pid, then that - // new process dumped. - static bool WriteMinidumpForChild(pid_t child, - pid_t child_blamed_thread, - const string& dump_path, - MinidumpCallback callback, - void* callback_context); - - // This structure is passed to minidump_writer.h:WriteMinidump via an opaque - // blob. It shouldn't be needed in any user code. - struct CrashContext { - siginfo_t siginfo; - pid_t tid; // the crashing thread. - ucontext_t context; -#if !defined(__ARM_EABI__) && !defined(__mips__) - // #ifdef this out because FP state is not part of user ABI for Linux ARM. - // In case of MIPS Linux FP state is already part of struct - // ucontext so 'float_state' is not required. - fpstate_t float_state; -#endif - }; - - // Returns whether out-of-process dump generation is used or not. - bool IsOutOfProcess() const { - return crash_generation_client_.get() != NULL; - } - - // Add information about a memory mapping. This can be used if - // a custom library loader is used that maps things in a way - // that the linux dumper can't handle by reading the maps file. - void AddMappingInfo(const string& name, - const uint8_t identifier[sizeof(MDGUID)], - uintptr_t start_address, - size_t mapping_size, - size_t file_offset); - - // Register a block of memory of length bytes starting at address ptr - // to be copied to the minidump when a crash happens. - void RegisterAppMemory(void* ptr, size_t length); - - // Unregister a block of memory that was registered with RegisterAppMemory. - void UnregisterAppMemory(void* ptr); - - // Force signal handling for the specified signal. - bool SimulateSignalDelivery(int sig); - - // Report a crash signal from an SA_SIGINFO signal handler. - bool HandleSignal(int sig, siginfo_t* info, void* uc); - - private: - // Save the old signal handlers and install new ones. - static bool InstallHandlersLocked(); - // Restore the old signal handlers. - static void RestoreHandlersLocked(); - - void PreresolveSymbols(); - bool GenerateDump(CrashContext *context); - void SendContinueSignalToChild(); - void WaitForContinueSignal(); - - static void SignalHandler(int sig, siginfo_t* info, void* uc); - static int ThreadEntry(void* arg); - bool DoDump(pid_t crashing_process, const void* context, - size_t context_size); - - const FilterCallback filter_; - const MinidumpCallback callback_; - void* const callback_context_; - - scoped_ptr<CrashGenerationClient> crash_generation_client_; - - MinidumpDescriptor minidump_descriptor_; - - // Must be volatile. The compiler is unaware of the code which runs in - // the signal handler which reads this variable. Without volatile the - // compiler is free to optimise away writes to this variable which it - // believes are never read. - volatile HandlerCallback crash_handler_; - - // We need to explicitly enable ptrace of parent processes on some - // kernels, but we need to know the PID of the cloned process before we - // can do this. We create a pipe which we can use to block the - // cloned process after creating it, until we have explicitly enabled - // ptrace. This is used to store the file descriptors for the pipe - int fdes[2]; - - // Callers can add extra info about mappings for cases where the - // dumper code cannot extract enough information from /proc/<pid>/maps. - MappingList mapping_list_; - - // Callers can request additional memory regions to be included in - // the dump. - AppMemoryList app_memory_list_; -}; - -} // namespace google_breakpad - -#endif // CLIENT_LINUX_HANDLER_EXCEPTION_HANDLER_H_ diff --git a/toolkit/crashreporter/google-breakpad/src/client/linux/handler/exception_handler_unittest.cc b/toolkit/crashreporter/google-breakpad/src/client/linux/handler/exception_handler_unittest.cc deleted file mode 100644 index 17d84cf7b..000000000 --- a/toolkit/crashreporter/google-breakpad/src/client/linux/handler/exception_handler_unittest.cc +++ /dev/null @@ -1,1179 +0,0 @@ -// 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. - -#include <stdint.h> -#include <unistd.h> -#include <signal.h> -#include <sys/mman.h> -#include <sys/poll.h> -#include <sys/socket.h> -#include <sys/uio.h> -#include <sys/wait.h> -#if defined(__mips__) -#include <sys/cachectl.h> -#endif - -#include <string> - -#include "breakpad_googletest_includes.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/ignore_ret.h" -#include "common/linux/linux_libc_support.h" -#include "common/tests/auto_tempdir.h" -#include "common/using_std_string.h" -#include "third_party/lss/linux_syscall_support.h" -#include "google_breakpad/processor/minidump.h" - -using namespace google_breakpad; - -namespace { - -// Flush the instruction cache for a given memory range. -// Only required on ARM and mips. -void FlushInstructionCache(const char* memory, uint32_t memory_size) { -#if defined(__arm__) - long begin = reinterpret_cast<long>(memory); - long end = begin + static_cast<long>(memory_size); -# if defined(__ANDROID__) - // Provided by Android's <unistd.h> - cacheflush(begin, end, 0); -# elif defined(__linux__) - // GLibc/ARM doesn't provide a wrapper for it, do a direct syscall. -# ifndef __ARM_NR_cacheflush -# define __ARM_NR_cacheflush 0xf0002 -# endif - syscall(__ARM_NR_cacheflush, begin, end, 0); -# else -# error "Your operating system is not supported yet" -# endif -#elif defined(__mips__) -# if defined(__ANDROID__) - // Provided by Android's <unistd.h> - long begin = reinterpret_cast<long>(memory); - long end = begin + static_cast<long>(memory_size); -#if _MIPS_SIM == _ABIO32 - cacheflush(begin, end, 0); -#else - syscall(__NR_cacheflush, begin, end, ICACHE); -#endif -# elif defined(__linux__) - // See http://www.linux-mips.org/wiki/Cacheflush_Syscall. - cacheflush(const_cast<char*>(memory), memory_size, ICACHE); -# else -# error "Your operating system is not supported yet" -# endif -#endif -} - -void sigchld_handler(int signo) { } - -int CreateTMPFile(const string& dir, string* path) { - string file = dir + "/exception-handler-unittest.XXXXXX"; - const char* c_file = file.c_str(); - // Copy that string, mkstemp needs a C string it can modify. - char* c_path = strdup(c_file); - const int fd = mkstemp(c_path); - if (fd >= 0) - *path = c_path; - free(c_path); - return fd; -} - -class ExceptionHandlerTest : public ::testing::Test { - protected: - void SetUp() { - // We need to be able to wait for children, so SIGCHLD cannot be SIG_IGN. - struct sigaction sa; - memset(&sa, 0, sizeof(sa)); - sa.sa_handler = sigchld_handler; - ASSERT_NE(sigaction(SIGCHLD, &sa, &old_action), -1); - } - - void TearDown() { - sigaction(SIGCHLD, &old_action, NULL); - } - - struct sigaction old_action; -}; - - -void WaitForProcessToTerminate(pid_t process_id, int expected_status) { - int status; - ASSERT_NE(HANDLE_EINTR(waitpid(process_id, &status, 0)), -1); - ASSERT_TRUE(WIFSIGNALED(status)); - ASSERT_EQ(expected_status, WTERMSIG(status)); -} - -// Reads the minidump path sent over the pipe |fd| and sets it in |path|. -void ReadMinidumpPathFromPipe(int fd, string* path) { - struct pollfd pfd; - memset(&pfd, 0, sizeof(pfd)); - pfd.fd = fd; - pfd.events = POLLIN | POLLERR; - - const int r = HANDLE_EINTR(poll(&pfd, 1, 0)); - ASSERT_EQ(1, r); - ASSERT_TRUE(pfd.revents & POLLIN); - - int32_t len; - ASSERT_EQ(static_cast<ssize_t>(sizeof(len)), read(fd, &len, sizeof(len))); - ASSERT_LT(len, 2048); - char* filename = static_cast<char*>(malloc(len + 1)); - ASSERT_EQ(len, read(fd, filename, len)); - filename[len] = 0; - close(fd); - *path = filename; - free(filename); -} - -} // namespace - -TEST(ExceptionHandlerTest, SimpleWithPath) { - AutoTempDir temp_dir; - ExceptionHandler handler( - MinidumpDescriptor(temp_dir.path()), NULL, NULL, NULL, true, -1); - EXPECT_EQ(temp_dir.path(), handler.minidump_descriptor().directory()); - string temp_subdir = temp_dir.path() + "/subdir"; - handler.set_minidump_descriptor(MinidumpDescriptor(temp_subdir)); - EXPECT_EQ(temp_subdir, handler.minidump_descriptor().directory()); -} - -TEST(ExceptionHandlerTest, SimpleWithFD) { - AutoTempDir temp_dir; - string path; - const int fd = CreateTMPFile(temp_dir.path(), &path); - ExceptionHandler handler(MinidumpDescriptor(fd), NULL, NULL, NULL, true, -1); - close(fd); -} - -static bool DoneCallback(const MinidumpDescriptor& descriptor, - void* context, - bool succeeded) { - if (!succeeded) - return false; - - if (!descriptor.IsFD()) { - int fd = reinterpret_cast<intptr_t>(context); - uint32_t len = 0; - len = my_strlen(descriptor.path()); - IGNORE_RET(HANDLE_EINTR(sys_write(fd, &len, sizeof(len)))); - IGNORE_RET(HANDLE_EINTR(sys_write(fd, descriptor.path(), len))); - } - return true; -} - -#ifndef ADDRESS_SANITIZER - -// This is a replacement for "*reinterpret_cast<volatile int*>(NULL) = 0;" -// It is needed because GCC is allowed to assume that the program will -// not execute any undefined behavior (UB) operation. Further, when GCC -// observes that UB statement is reached, it can assume that all statements -// leading to the UB one are never executed either, and can completely -// optimize them out. In the case of ExceptionHandlerTest::ExternalDumper, -// GCC-4.9 optimized out the entire set up of ExceptionHandler, causing -// test failure. -volatile int *p_null; // external linkage, so GCC can't tell that it - // remains NULL. Volatile just for a good measure. -static void DoNullPointerDereference() { - *p_null = 1; -} - -void ChildCrash(bool use_fd) { - AutoTempDir temp_dir; - int fds[2] = {0}; - int minidump_fd = -1; - string minidump_path; - if (use_fd) { - minidump_fd = CreateTMPFile(temp_dir.path(), &minidump_path); - } else { - ASSERT_NE(pipe(fds), -1); - } - - const pid_t child = fork(); - if (child == 0) { - { - google_breakpad::scoped_ptr<ExceptionHandler> handler; - if (use_fd) { - handler.reset(new ExceptionHandler(MinidumpDescriptor(minidump_fd), - NULL, NULL, NULL, true, -1)); - } else { - close(fds[0]); // Close the reading end. - void* fd_param = reinterpret_cast<void*>(fds[1]); - handler.reset(new ExceptionHandler(MinidumpDescriptor(temp_dir.path()), - NULL, DoneCallback, fd_param, - true, -1)); - } - // Crash with the exception handler in scope. - DoNullPointerDereference(); - } - } - if (!use_fd) - close(fds[1]); // Close the writting end. - - ASSERT_NO_FATAL_FAILURE(WaitForProcessToTerminate(child, SIGSEGV)); - - if (!use_fd) - ASSERT_NO_FATAL_FAILURE(ReadMinidumpPathFromPipe(fds[0], &minidump_path)); - - struct stat st; - ASSERT_EQ(0, stat(minidump_path.c_str(), &st)); - ASSERT_GT(st.st_size, 0); - unlink(minidump_path.c_str()); -} - -TEST(ExceptionHandlerTest, ChildCrashWithPath) { - ASSERT_NO_FATAL_FAILURE(ChildCrash(false)); -} - -TEST(ExceptionHandlerTest, ChildCrashWithFD) { - ASSERT_NO_FATAL_FAILURE(ChildCrash(true)); -} - -static bool DoneCallbackReturnFalse(const MinidumpDescriptor& descriptor, - void* context, - bool succeeded) { - return false; -} - -static bool DoneCallbackReturnTrue(const MinidumpDescriptor& descriptor, - void* context, - bool succeeded) { - return true; -} - -static bool DoneCallbackRaiseSIGKILL(const MinidumpDescriptor& descriptor, - void* context, - bool succeeded) { - raise(SIGKILL); - return true; -} - -static bool FilterCallbackReturnFalse(void* context) { - return false; -} - -static bool FilterCallbackReturnTrue(void* context) { - return true; -} - -// SIGKILL cannot be blocked and a handler cannot be installed for it. In the -// following tests, if the child dies with signal SIGKILL, then the signal was -// redelivered to this handler. If the child dies with SIGSEGV then it wasn't. -static void RaiseSIGKILL(int sig) { - raise(SIGKILL); -} - -static bool InstallRaiseSIGKILL() { - struct sigaction sa; - memset(&sa, 0, sizeof(sa)); - sa.sa_handler = RaiseSIGKILL; - return sigaction(SIGSEGV, &sa, NULL) != -1; -} - -static void CrashWithCallbacks(ExceptionHandler::FilterCallback filter, - ExceptionHandler::MinidumpCallback done, - string path) { - ExceptionHandler handler( - MinidumpDescriptor(path), filter, done, NULL, true, -1); - // Crash with the exception handler in scope. - DoNullPointerDereference(); -} - -TEST(ExceptionHandlerTest, RedeliveryOnFilterCallbackFalse) { - AutoTempDir temp_dir; - - const pid_t child = fork(); - if (child == 0) { - ASSERT_TRUE(InstallRaiseSIGKILL()); - CrashWithCallbacks(FilterCallbackReturnFalse, NULL, temp_dir.path()); - } - - ASSERT_NO_FATAL_FAILURE(WaitForProcessToTerminate(child, SIGKILL)); -} - -TEST(ExceptionHandlerTest, RedeliveryOnDoneCallbackFalse) { - AutoTempDir temp_dir; - - const pid_t child = fork(); - if (child == 0) { - ASSERT_TRUE(InstallRaiseSIGKILL()); - CrashWithCallbacks(NULL, DoneCallbackReturnFalse, temp_dir.path()); - } - - ASSERT_NO_FATAL_FAILURE(WaitForProcessToTerminate(child, SIGKILL)); -} - -TEST(ExceptionHandlerTest, NoRedeliveryOnDoneCallbackTrue) { - AutoTempDir temp_dir; - - const pid_t child = fork(); - if (child == 0) { - ASSERT_TRUE(InstallRaiseSIGKILL()); - CrashWithCallbacks(NULL, DoneCallbackReturnTrue, temp_dir.path()); - } - - ASSERT_NO_FATAL_FAILURE(WaitForProcessToTerminate(child, SIGSEGV)); -} - -TEST(ExceptionHandlerTest, NoRedeliveryOnFilterCallbackTrue) { - AutoTempDir temp_dir; - - const pid_t child = fork(); - if (child == 0) { - ASSERT_TRUE(InstallRaiseSIGKILL()); - CrashWithCallbacks(FilterCallbackReturnTrue, NULL, temp_dir.path()); - } - - ASSERT_NO_FATAL_FAILURE(WaitForProcessToTerminate(child, SIGSEGV)); -} - -TEST(ExceptionHandlerTest, RedeliveryToDefaultHandler) { - AutoTempDir temp_dir; - - const pid_t child = fork(); - if (child == 0) { - CrashWithCallbacks(FilterCallbackReturnFalse, NULL, temp_dir.path()); - } - - // As RaiseSIGKILL wasn't installed, the redelivery should just kill the child - // with SIGSEGV. - ASSERT_NO_FATAL_FAILURE(WaitForProcessToTerminate(child, SIGSEGV)); -} - -// Check that saving and restoring the signal handler with 'signal' -// instead of 'sigaction' doesn't make the Breakpad signal handler -// crash. See comments in ExceptionHandler::SignalHandler for full -// details. -TEST(ExceptionHandlerTest, RedeliveryOnBadSignalHandlerFlag) { - AutoTempDir temp_dir; - const pid_t child = fork(); - if (child == 0) { - // Install the RaiseSIGKILL handler for SIGSEGV. - ASSERT_TRUE(InstallRaiseSIGKILL()); - - // Create a new exception handler, this installs a new SIGSEGV - // handler, after saving the old one. - ExceptionHandler handler( - MinidumpDescriptor(temp_dir.path()), NULL, - DoneCallbackReturnFalse, NULL, true, -1); - - // Install the default SIGSEGV handler, saving the current one. - // Then re-install the current one with 'signal', this loses the - // SA_SIGINFO flag associated with the Breakpad handler. - sighandler_t old_handler = signal(SIGSEGV, SIG_DFL); - ASSERT_NE(reinterpret_cast<void*>(old_handler), - reinterpret_cast<void*>(SIG_ERR)); - ASSERT_NE(reinterpret_cast<void*>(signal(SIGSEGV, old_handler)), - reinterpret_cast<void*>(SIG_ERR)); - - // Crash with the exception handler in scope. - DoNullPointerDereference(); - } - // SIGKILL means Breakpad's signal handler didn't crash. - ASSERT_NO_FATAL_FAILURE(WaitForProcessToTerminate(child, SIGKILL)); -} - -TEST(ExceptionHandlerTest, StackedHandlersDeliveredToTop) { - AutoTempDir temp_dir; - - const pid_t child = fork(); - if (child == 0) { - ExceptionHandler bottom(MinidumpDescriptor(temp_dir.path()), - NULL, - NULL, - NULL, - true, - -1); - CrashWithCallbacks(NULL, DoneCallbackRaiseSIGKILL, temp_dir.path()); - } - ASSERT_NO_FATAL_FAILURE(WaitForProcessToTerminate(child, SIGKILL)); -} - -TEST(ExceptionHandlerTest, StackedHandlersNotDeliveredToBottom) { - AutoTempDir temp_dir; - - const pid_t child = fork(); - if (child == 0) { - ExceptionHandler bottom(MinidumpDescriptor(temp_dir.path()), - NULL, - DoneCallbackRaiseSIGKILL, - NULL, - true, - -1); - CrashWithCallbacks(NULL, NULL, temp_dir.path()); - } - ASSERT_NO_FATAL_FAILURE(WaitForProcessToTerminate(child, SIGSEGV)); -} - -TEST(ExceptionHandlerTest, StackedHandlersFilteredToBottom) { - AutoTempDir temp_dir; - - const pid_t child = fork(); - if (child == 0) { - ExceptionHandler bottom(MinidumpDescriptor(temp_dir.path()), - NULL, - DoneCallbackRaiseSIGKILL, - NULL, - true, - -1); - CrashWithCallbacks(FilterCallbackReturnFalse, NULL, temp_dir.path()); - } - ASSERT_NO_FATAL_FAILURE(WaitForProcessToTerminate(child, SIGKILL)); -} - -TEST(ExceptionHandlerTest, StackedHandlersUnhandledToBottom) { - AutoTempDir temp_dir; - - const pid_t child = fork(); - if (child == 0) { - ExceptionHandler bottom(MinidumpDescriptor(temp_dir.path()), - NULL, - DoneCallbackRaiseSIGKILL, - NULL, - true, - -1); - CrashWithCallbacks(NULL, DoneCallbackReturnFalse, temp_dir.path()); - } - ASSERT_NO_FATAL_FAILURE(WaitForProcessToTerminate(child, SIGKILL)); -} - -#endif // !ADDRESS_SANITIZER - -const unsigned char kIllegalInstruction[] = { -#if defined(__mips__) - // mfc2 zero,Impl - usually illegal in userspace. - 0x48, 0x00, 0x00, 0x48 -#else - // This crashes with SIGILL on x86/x86-64/arm. - 0xff, 0xff, 0xff, 0xff -#endif -}; - -// Test that memory around the instruction pointer is written -// to the dump as a MinidumpMemoryRegion. -TEST(ExceptionHandlerTest, InstructionPointerMemory) { - AutoTempDir temp_dir; - int fds[2]; - ASSERT_NE(pipe(fds), -1); - - // These are defined here so the parent can use them to check the - // data from the minidump afterwards. - const uint32_t kMemorySize = 256; // bytes - const int kOffset = kMemorySize / 2; - - const pid_t child = fork(); - if (child == 0) { - close(fds[0]); - ExceptionHandler handler(MinidumpDescriptor(temp_dir.path()), NULL, - DoneCallback, reinterpret_cast<void*>(fds[1]), - true, -1); - // Get some executable memory. - char* memory = - reinterpret_cast<char*>(mmap(NULL, - kMemorySize, - PROT_READ | PROT_WRITE | PROT_EXEC, - MAP_PRIVATE | MAP_ANON, - -1, - 0)); - if (!memory) - exit(0); - - // Write some instructions that will crash. Put them in the middle - // of the block of memory, because the minidump should contain 128 - // bytes on either side of the instruction pointer. - memcpy(memory + kOffset, kIllegalInstruction, sizeof(kIllegalInstruction)); - FlushInstructionCache(memory, kMemorySize); - - // Now execute the instructions, which should crash. - typedef void (*void_function)(void); - void_function memory_function = - reinterpret_cast<void_function>(memory + kOffset); - memory_function(); - } - close(fds[1]); - - ASSERT_NO_FATAL_FAILURE(WaitForProcessToTerminate(child, SIGILL)); - - string minidump_path; - ASSERT_NO_FATAL_FAILURE(ReadMinidumpPathFromPipe(fds[0], &minidump_path)); - - struct stat st; - ASSERT_EQ(0, stat(minidump_path.c_str(), &st)); - ASSERT_GT(st.st_size, 0); - - // Read the minidump. Locate the exception record and the - // memory list, and then ensure that there is a memory region - // in the memory list that covers the instruction pointer from - // the exception record. - Minidump minidump(minidump_path); - ASSERT_TRUE(minidump.Read()); - - MinidumpException* exception = minidump.GetException(); - MinidumpMemoryList* memory_list = minidump.GetMemoryList(); - ASSERT_TRUE(exception); - ASSERT_TRUE(memory_list); - ASSERT_LT(0U, memory_list->region_count()); - - MinidumpContext* context = exception->GetContext(); - ASSERT_TRUE(context); - - uint64_t instruction_pointer; - ASSERT_TRUE(context->GetInstructionPointer(&instruction_pointer)); - - MinidumpMemoryRegion* region = - memory_list->GetMemoryRegionForAddress(instruction_pointer); - ASSERT_TRUE(region); - - EXPECT_EQ(kMemorySize, region->GetSize()); - const uint8_t* bytes = region->GetMemory(); - ASSERT_TRUE(bytes); - - uint8_t prefix_bytes[kOffset]; - uint8_t suffix_bytes[kMemorySize - kOffset - sizeof(kIllegalInstruction)]; - memset(prefix_bytes, 0, sizeof(prefix_bytes)); - memset(suffix_bytes, 0, sizeof(suffix_bytes)); - EXPECT_TRUE(memcmp(bytes, prefix_bytes, sizeof(prefix_bytes)) == 0); - EXPECT_TRUE(memcmp(bytes + kOffset, kIllegalInstruction, - sizeof(kIllegalInstruction)) == 0); - EXPECT_TRUE(memcmp(bytes + kOffset + sizeof(kIllegalInstruction), - suffix_bytes, sizeof(suffix_bytes)) == 0); - - unlink(minidump_path.c_str()); -} - -// Test that the memory region around the instruction pointer is -// bounded correctly on the low end. -TEST(ExceptionHandlerTest, InstructionPointerMemoryMinBound) { - AutoTempDir temp_dir; - int fds[2]; - ASSERT_NE(pipe(fds), -1); - - // These are defined here so the parent can use them to check the - // data from the minidump afterwards. - const uint32_t kMemorySize = 256; // bytes - const int kOffset = 0; - - const pid_t child = fork(); - if (child == 0) { - close(fds[0]); - ExceptionHandler handler(MinidumpDescriptor(temp_dir.path()), NULL, - DoneCallback, reinterpret_cast<void*>(fds[1]), - true, -1); - // Get some executable memory. - char* memory = - reinterpret_cast<char*>(mmap(NULL, - kMemorySize, - PROT_READ | PROT_WRITE | PROT_EXEC, - MAP_PRIVATE | MAP_ANON, - -1, - 0)); - if (!memory) - exit(0); - - // Write some instructions that will crash. Put them in the middle - // of the block of memory, because the minidump should contain 128 - // bytes on either side of the instruction pointer. - memcpy(memory + kOffset, kIllegalInstruction, sizeof(kIllegalInstruction)); - FlushInstructionCache(memory, kMemorySize); - - // Now execute the instructions, which should crash. - typedef void (*void_function)(void); - void_function memory_function = - reinterpret_cast<void_function>(memory + kOffset); - memory_function(); - } - close(fds[1]); - - ASSERT_NO_FATAL_FAILURE(WaitForProcessToTerminate(child, SIGILL)); - - string minidump_path; - ASSERT_NO_FATAL_FAILURE(ReadMinidumpPathFromPipe(fds[0], &minidump_path)); - - struct stat st; - ASSERT_EQ(0, stat(minidump_path.c_str(), &st)); - ASSERT_GT(st.st_size, 0); - - // Read the minidump. Locate the exception record and the - // memory list, and then ensure that there is a memory region - // in the memory list that covers the instruction pointer from - // the exception record. - Minidump minidump(minidump_path); - ASSERT_TRUE(minidump.Read()); - - MinidumpException* exception = minidump.GetException(); - MinidumpMemoryList* memory_list = minidump.GetMemoryList(); - ASSERT_TRUE(exception); - ASSERT_TRUE(memory_list); - ASSERT_LT(0U, memory_list->region_count()); - - MinidumpContext* context = exception->GetContext(); - ASSERT_TRUE(context); - - uint64_t instruction_pointer; - ASSERT_TRUE(context->GetInstructionPointer(&instruction_pointer)); - - MinidumpMemoryRegion* region = - memory_list->GetMemoryRegionForAddress(instruction_pointer); - ASSERT_TRUE(region); - - EXPECT_EQ(kMemorySize / 2, region->GetSize()); - const uint8_t* bytes = region->GetMemory(); - ASSERT_TRUE(bytes); - - uint8_t suffix_bytes[kMemorySize / 2 - sizeof(kIllegalInstruction)]; - memset(suffix_bytes, 0, sizeof(suffix_bytes)); - EXPECT_TRUE(memcmp(bytes + kOffset, kIllegalInstruction, - sizeof(kIllegalInstruction)) == 0); - EXPECT_TRUE(memcmp(bytes + kOffset + sizeof(kIllegalInstruction), - suffix_bytes, sizeof(suffix_bytes)) == 0); - unlink(minidump_path.c_str()); -} - -// Test that the memory region around the instruction pointer is -// bounded correctly on the high end. -TEST(ExceptionHandlerTest, InstructionPointerMemoryMaxBound) { - AutoTempDir temp_dir; - int fds[2]; - ASSERT_NE(pipe(fds), -1); - - // These are defined here so the parent can use them to check the - // data from the minidump afterwards. - // Use 4k here because the OS will hand out a single page even - // if a smaller size is requested, and this test wants to - // test the upper bound of the memory range. - const uint32_t kMemorySize = 4096; // bytes - const int kOffset = kMemorySize - sizeof(kIllegalInstruction); - - const pid_t child = fork(); - if (child == 0) { - close(fds[0]); - ExceptionHandler handler(MinidumpDescriptor(temp_dir.path()), NULL, - DoneCallback, reinterpret_cast<void*>(fds[1]), - true, -1); - // Get some executable memory. - char* memory = - reinterpret_cast<char*>(mmap(NULL, - kMemorySize, - PROT_READ | PROT_WRITE | PROT_EXEC, - MAP_PRIVATE | MAP_ANON, - -1, - 0)); - if (!memory) - exit(0); - - // Write some instructions that will crash. Put them in the middle - // of the block of memory, because the minidump should contain 128 - // bytes on either side of the instruction pointer. - memcpy(memory + kOffset, kIllegalInstruction, sizeof(kIllegalInstruction)); - FlushInstructionCache(memory, kMemorySize); - - // Now execute the instructions, which should crash. - typedef void (*void_function)(void); - void_function memory_function = - reinterpret_cast<void_function>(memory + kOffset); - memory_function(); - } - close(fds[1]); - - ASSERT_NO_FATAL_FAILURE(WaitForProcessToTerminate(child, SIGILL)); - - string minidump_path; - ASSERT_NO_FATAL_FAILURE(ReadMinidumpPathFromPipe(fds[0], &minidump_path)); - - struct stat st; - ASSERT_EQ(0, stat(minidump_path.c_str(), &st)); - ASSERT_GT(st.st_size, 0); - - // Read the minidump. Locate the exception record and the memory list, and - // then ensure that there is a memory region in the memory list that covers - // the instruction pointer from the exception record. - Minidump minidump(minidump_path); - ASSERT_TRUE(minidump.Read()); - - MinidumpException* exception = minidump.GetException(); - MinidumpMemoryList* memory_list = minidump.GetMemoryList(); - ASSERT_TRUE(exception); - ASSERT_TRUE(memory_list); - ASSERT_LT(0U, memory_list->region_count()); - - MinidumpContext* context = exception->GetContext(); - ASSERT_TRUE(context); - - uint64_t instruction_pointer; - ASSERT_TRUE(context->GetInstructionPointer(&instruction_pointer)); - - MinidumpMemoryRegion* region = - memory_list->GetMemoryRegionForAddress(instruction_pointer); - ASSERT_TRUE(region); - - const size_t kPrefixSize = 128; // bytes - EXPECT_EQ(kPrefixSize + sizeof(kIllegalInstruction), region->GetSize()); - const uint8_t* bytes = region->GetMemory(); - ASSERT_TRUE(bytes); - - uint8_t prefix_bytes[kPrefixSize]; - memset(prefix_bytes, 0, sizeof(prefix_bytes)); - EXPECT_TRUE(memcmp(bytes, prefix_bytes, sizeof(prefix_bytes)) == 0); - EXPECT_TRUE(memcmp(bytes + kPrefixSize, - kIllegalInstruction, sizeof(kIllegalInstruction)) == 0); - - unlink(minidump_path.c_str()); -} - -#ifndef ADDRESS_SANITIZER - -// Ensure that an extra memory block doesn't get added when the instruction -// pointer is not in mapped memory. -TEST(ExceptionHandlerTest, InstructionPointerMemoryNullPointer) { - AutoTempDir temp_dir; - int fds[2]; - ASSERT_NE(pipe(fds), -1); - - const pid_t child = fork(); - if (child == 0) { - close(fds[0]); - ExceptionHandler handler(MinidumpDescriptor(temp_dir.path()), NULL, - DoneCallback, reinterpret_cast<void*>(fds[1]), - true, -1); - // Try calling a NULL pointer. - typedef void (*void_function)(void); - // Volatile markings are needed to keep Clang from generating invalid - // opcodes. See http://crbug.com/498354 for details. - volatile void_function memory_function = - reinterpret_cast<void_function>(NULL); - memory_function(); - // not reached - exit(1); - } - close(fds[1]); - - ASSERT_NO_FATAL_FAILURE(WaitForProcessToTerminate(child, SIGSEGV)); - - string minidump_path; - ASSERT_NO_FATAL_FAILURE(ReadMinidumpPathFromPipe(fds[0], &minidump_path)); - - struct stat st; - ASSERT_EQ(0, stat(minidump_path.c_str(), &st)); - ASSERT_GT(st.st_size, 0); - - // Read the minidump. Locate the exception record and the - // memory list, and then ensure that there is a memory region - // in the memory list that covers the instruction pointer from - // the exception record. - Minidump minidump(minidump_path); - ASSERT_TRUE(minidump.Read()); - - MinidumpException* exception = minidump.GetException(); - MinidumpMemoryList* memory_list = minidump.GetMemoryList(); - ASSERT_TRUE(exception); - ASSERT_TRUE(memory_list); - ASSERT_EQ(static_cast<unsigned int>(1), memory_list->region_count()); - - unlink(minidump_path.c_str()); -} - -#endif // !ADDRESS_SANITIZER - -// Test that anonymous memory maps can be annotated with names and IDs. -TEST(ExceptionHandlerTest, ModuleInfo) { - // These are defined here so the parent can use them to check the - // data from the minidump afterwards. - const uint32_t kMemorySize = sysconf(_SC_PAGESIZE); - const char* kMemoryName = "a fake module"; - const uint8_t kModuleGUID[sizeof(MDGUID)] = { - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, - 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF - }; - const string module_identifier = "33221100554477668899AABBCCDDEEFF0"; - - // Get some memory. - char* memory = - reinterpret_cast<char*>(mmap(NULL, - kMemorySize, - PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANON, - -1, - 0)); - const uintptr_t kMemoryAddress = reinterpret_cast<uintptr_t>(memory); - ASSERT_TRUE(memory); - - AutoTempDir temp_dir; - ExceptionHandler handler( - MinidumpDescriptor(temp_dir.path()), NULL, NULL, NULL, true, -1); - - // Add info about the anonymous memory mapping. - handler.AddMappingInfo(kMemoryName, - kModuleGUID, - kMemoryAddress, - kMemorySize, - 0); - ASSERT_TRUE(handler.WriteMinidump()); - - const MinidumpDescriptor& minidump_desc = handler.minidump_descriptor(); - // Read the minidump. Load the module list, and ensure that the mmap'ed - // |memory| is listed with the given module name and debug ID. - Minidump minidump(minidump_desc.path()); - ASSERT_TRUE(minidump.Read()); - - MinidumpModuleList* module_list = minidump.GetModuleList(); - ASSERT_TRUE(module_list); - const MinidumpModule* module = - module_list->GetModuleForAddress(kMemoryAddress); - ASSERT_TRUE(module); - - EXPECT_EQ(kMemoryAddress, module->base_address()); - EXPECT_EQ(kMemorySize, module->size()); - EXPECT_EQ(kMemoryName, module->code_file()); - EXPECT_EQ(module_identifier, module->debug_identifier()); - - unlink(minidump_desc.path()); -} - -#ifndef ADDRESS_SANITIZER - -static const unsigned kControlMsgSize = - CMSG_SPACE(sizeof(int)) + CMSG_SPACE(sizeof(struct ucred)); - -static bool -CrashHandler(const void* crash_context, size_t crash_context_size, - void* context) { - const int fd = (intptr_t) context; - int fds[2]; - if (pipe(fds) == -1) { - // There doesn't seem to be any way to reliably handle - // this failure without the parent process hanging - // At least make sure that this process doesn't access - // unexpected file descriptors - fds[0] = -1; - fds[1] = -1; - } - struct kernel_msghdr msg = {0}; - struct kernel_iovec iov; - iov.iov_base = const_cast<void*>(crash_context); - iov.iov_len = crash_context_size; - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - char cmsg[kControlMsgSize]; - 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]; - hdr = CMSG_NXTHDR((struct msghdr*) &msg, hdr); - hdr->cmsg_level = SOL_SOCKET; - hdr->cmsg_type = SCM_CREDENTIALS; - hdr->cmsg_len = CMSG_LEN(sizeof(struct ucred)); - struct ucred *cred = reinterpret_cast<struct ucred*>(CMSG_DATA(hdr)); - cred->uid = getuid(); - cred->gid = getgid(); - cred->pid = getpid(); - - ssize_t ret = HANDLE_EINTR(sys_sendmsg(fd, &msg, 0)); - sys_close(fds[1]); - if (ret <= 0) - return false; - - char b; - IGNORE_RET(HANDLE_EINTR(sys_read(fds[0], &b, 1))); - - return true; -} - -TEST(ExceptionHandlerTest, ExternalDumper) { - int fds[2]; - ASSERT_NE(socketpair(AF_UNIX, SOCK_DGRAM, 0, fds), -1); - static const int on = 1; - setsockopt(fds[0], SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)); - setsockopt(fds[1], SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)); - - const pid_t child = fork(); - if (child == 0) { - close(fds[0]); - ExceptionHandler handler(MinidumpDescriptor("/tmp1"), NULL, NULL, - reinterpret_cast<void*>(fds[1]), true, -1); - handler.set_crash_handler(CrashHandler); - DoNullPointerDereference(); - } - close(fds[1]); - struct msghdr msg = {0}; - struct iovec iov; - static const unsigned kCrashContextSize = - sizeof(ExceptionHandler::CrashContext); - char context[kCrashContextSize]; - char control[kControlMsgSize]; - iov.iov_base = context; - iov.iov_len = kCrashContextSize; - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - msg.msg_control = control; - msg.msg_controllen = kControlMsgSize; - - const ssize_t n = HANDLE_EINTR(recvmsg(fds[0], &msg, 0)); - ASSERT_EQ(static_cast<ssize_t>(kCrashContextSize), n); - ASSERT_EQ(kControlMsgSize, msg.msg_controllen); - ASSERT_EQ(static_cast<__typeof__(msg.msg_flags)>(0), msg.msg_flags); - ASSERT_EQ(0, close(fds[0])); - - 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_EQ(sizeof(int), len); - signal_fd = *(reinterpret_cast<int*>(CMSG_DATA(hdr))); - } else if (hdr->cmsg_type == SCM_CREDENTIALS) { - const struct ucred *cred = - reinterpret_cast<struct ucred*>(CMSG_DATA(hdr)); - crashing_pid = cred->pid; - } - } - - ASSERT_NE(crashing_pid, -1); - ASSERT_NE(signal_fd, -1); - - AutoTempDir temp_dir; - string templ = temp_dir.path() + "/exception-handler-unittest"; - ASSERT_TRUE(WriteMinidump(templ.c_str(), crashing_pid, context, - kCrashContextSize)); - static const char b = 0; - ASSERT_EQ(1, (HANDLE_EINTR(write(signal_fd, &b, 1)))); - ASSERT_EQ(0, close(signal_fd)); - - ASSERT_NO_FATAL_FAILURE(WaitForProcessToTerminate(child, SIGSEGV)); - - struct stat st; - ASSERT_EQ(0, stat(templ.c_str(), &st)); - ASSERT_GT(st.st_size, 0); - unlink(templ.c_str()); -} - -#endif // !ADDRESS_SANITIZER - -TEST(ExceptionHandlerTest, WriteMinidumpExceptionStream) { - AutoTempDir temp_dir; - ExceptionHandler handler(MinidumpDescriptor(temp_dir.path()), NULL, NULL, - NULL, false, -1); - ASSERT_TRUE(handler.WriteMinidump()); - - string minidump_path = handler.minidump_descriptor().path(); - - // Read the minidump and check the exception stream. - Minidump minidump(minidump_path); - ASSERT_TRUE(minidump.Read()); - MinidumpException* exception = minidump.GetException(); - ASSERT_TRUE(exception); - const MDRawExceptionStream* raw = exception->exception(); - ASSERT_TRUE(raw); - EXPECT_EQ(MD_EXCEPTION_CODE_LIN_DUMP_REQUESTED, - raw->exception_record.exception_code); -} - -TEST(ExceptionHandlerTest, GenerateMultipleDumpsWithFD) { - AutoTempDir temp_dir; - string path; - const int fd = CreateTMPFile(temp_dir.path(), &path); - ExceptionHandler handler(MinidumpDescriptor(fd), NULL, NULL, NULL, false, -1); - ASSERT_TRUE(handler.WriteMinidump()); - // Check by the size of the data written to the FD that a minidump was - // generated. - off_t size = lseek(fd, 0, SEEK_CUR); - ASSERT_GT(size, 0); - - // Generate another minidump. - ASSERT_TRUE(handler.WriteMinidump()); - size = lseek(fd, 0, SEEK_CUR); - ASSERT_GT(size, 0); -} - -TEST(ExceptionHandlerTest, GenerateMultipleDumpsWithPath) { - AutoTempDir temp_dir; - ExceptionHandler handler(MinidumpDescriptor(temp_dir.path()), NULL, NULL, - NULL, false, -1); - ASSERT_TRUE(handler.WriteMinidump()); - - const MinidumpDescriptor& minidump_1 = handler.minidump_descriptor(); - struct stat st; - ASSERT_EQ(0, stat(minidump_1.path(), &st)); - ASSERT_GT(st.st_size, 0); - string minidump_1_path(minidump_1.path()); - // Check it is a valid minidump. - Minidump minidump1(minidump_1_path); - ASSERT_TRUE(minidump1.Read()); - unlink(minidump_1.path()); - - // Generate another minidump, it should go to a different file. - ASSERT_TRUE(handler.WriteMinidump()); - const MinidumpDescriptor& minidump_2 = handler.minidump_descriptor(); - ASSERT_EQ(0, stat(minidump_2.path(), &st)); - ASSERT_GT(st.st_size, 0); - string minidump_2_path(minidump_2.path()); - // Check it is a valid minidump. - Minidump minidump2(minidump_2_path); - ASSERT_TRUE(minidump2.Read()); - unlink(minidump_2.path()); - - // 2 distinct files should be produced. - ASSERT_STRNE(minidump_1_path.c_str(), minidump_2_path.c_str()); -} - -// Test that an additional memory region can be added to the minidump. -TEST(ExceptionHandlerTest, AdditionalMemory) { - const uint32_t kMemorySize = sysconf(_SC_PAGESIZE); - - // Get some heap memory. - uint8_t* memory = new uint8_t[kMemorySize]; - const uintptr_t kMemoryAddress = reinterpret_cast<uintptr_t>(memory); - ASSERT_TRUE(memory); - - // Stick some data into the memory so the contents can be verified. - for (uint32_t i = 0; i < kMemorySize; ++i) { - memory[i] = i % 255; - } - - AutoTempDir temp_dir; - ExceptionHandler handler( - MinidumpDescriptor(temp_dir.path()), NULL, NULL, NULL, true, -1); - - // Add the memory region to the list of memory to be included. - handler.RegisterAppMemory(memory, kMemorySize); - handler.WriteMinidump(); - - const MinidumpDescriptor& minidump_desc = handler.minidump_descriptor(); - - // Read the minidump. Ensure that the memory region is present - Minidump minidump(minidump_desc.path()); - ASSERT_TRUE(minidump.Read()); - - MinidumpMemoryList* dump_memory_list = minidump.GetMemoryList(); - ASSERT_TRUE(dump_memory_list); - const MinidumpMemoryRegion* region = - dump_memory_list->GetMemoryRegionForAddress(kMemoryAddress); - ASSERT_TRUE(region); - - EXPECT_EQ(kMemoryAddress, region->GetBase()); - EXPECT_EQ(kMemorySize, region->GetSize()); - - // Verify memory contents. - EXPECT_EQ(0, memcmp(region->GetMemory(), memory, kMemorySize)); - - delete[] memory; -} - -// Test that a memory region that was previously registered -// can be unregistered. -TEST(ExceptionHandlerTest, AdditionalMemoryRemove) { - const uint32_t kMemorySize = sysconf(_SC_PAGESIZE); - - // Get some heap memory. - uint8_t* memory = new uint8_t[kMemorySize]; - const uintptr_t kMemoryAddress = reinterpret_cast<uintptr_t>(memory); - ASSERT_TRUE(memory); - - AutoTempDir temp_dir; - ExceptionHandler handler( - MinidumpDescriptor(temp_dir.path()), NULL, NULL, NULL, true, -1); - - // Add the memory region to the list of memory to be included. - handler.RegisterAppMemory(memory, kMemorySize); - - // ...and then remove it - handler.UnregisterAppMemory(memory); - handler.WriteMinidump(); - - const MinidumpDescriptor& minidump_desc = handler.minidump_descriptor(); - - // Read the minidump. Ensure that the memory region is not present. - Minidump minidump(minidump_desc.path()); - ASSERT_TRUE(minidump.Read()); - - MinidumpMemoryList* dump_memory_list = minidump.GetMemoryList(); - ASSERT_TRUE(dump_memory_list); - const MinidumpMemoryRegion* region = - dump_memory_list->GetMemoryRegionForAddress(kMemoryAddress); - EXPECT_FALSE(region); - - delete[] memory; -} - -static bool SimpleCallback(const MinidumpDescriptor& descriptor, - void* context, - bool succeeded) { - string* filename = reinterpret_cast<string*>(context); - *filename = descriptor.path(); - return true; -} - -TEST(ExceptionHandlerTest, WriteMinidumpForChild) { - int fds[2]; - ASSERT_NE(-1, pipe(fds)); - - const pid_t child = fork(); - if (child == 0) { - close(fds[1]); - char b; - HANDLE_EINTR(read(fds[0], &b, sizeof(b))); - close(fds[0]); - syscall(__NR_exit); - } - close(fds[0]); - - AutoTempDir temp_dir; - string minidump_filename; - ASSERT_TRUE( - ExceptionHandler::WriteMinidumpForChild(child, child, - temp_dir.path(), SimpleCallback, - (void*)&minidump_filename)); - - Minidump minidump(minidump_filename); - ASSERT_TRUE(minidump.Read()); - // Check that the crashing thread is the main thread of |child| - MinidumpException* exception = minidump.GetException(); - ASSERT_TRUE(exception); - uint32_t thread_id; - ASSERT_TRUE(exception->GetThreadID(&thread_id)); - EXPECT_EQ(child, static_cast<int32_t>(thread_id)); - - const MDRawExceptionStream* raw = exception->exception(); - ASSERT_TRUE(raw); - EXPECT_EQ(MD_EXCEPTION_CODE_LIN_DUMP_REQUESTED, - raw->exception_record.exception_code); - - close(fds[1]); - unlink(minidump_filename.c_str()); -} diff --git a/toolkit/crashreporter/google-breakpad/src/client/linux/handler/microdump_extra_info.h b/toolkit/crashreporter/google-breakpad/src/client/linux/handler/microdump_extra_info.h deleted file mode 100644 index bf01f0c7b..000000000 --- a/toolkit/crashreporter/google-breakpad/src/client/linux/handler/microdump_extra_info.h +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2015 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_HANDLER_MICRODUMP_EXTRA_INFO_H_ -#define CLIENT_LINUX_HANDLER_MICRODUMP_EXTRA_INFO_H_ - -namespace google_breakpad { - -struct MicrodumpExtraInfo { - // Strings pointed to by this struct are not copied, and are - // expected to remain valid for the lifetime of the process. - const char* build_fingerprint; - const char* product_info; - const char* gpu_fingerprint; - const char* process_type; - - MicrodumpExtraInfo() - : build_fingerprint(NULL), - product_info(NULL), - gpu_fingerprint(NULL), - process_type(NULL) {} -}; - -} - -#endif // CLIENT_LINUX_HANDLER_MICRODUMP_EXTRA_INFO_H_ diff --git a/toolkit/crashreporter/google-breakpad/src/client/linux/handler/minidump_descriptor.cc b/toolkit/crashreporter/google-breakpad/src/client/linux/handler/minidump_descriptor.cc deleted file mode 100644 index ce09153dd..000000000 --- a/toolkit/crashreporter/google-breakpad/src/client/linux/handler/minidump_descriptor.cc +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright (c) 2012 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 <stdio.h> - -#include "client/linux/handler/minidump_descriptor.h" - -#include "common/linux/guid_creator.h" - -namespace google_breakpad { - -//static -const MinidumpDescriptor::MicrodumpOnConsole - MinidumpDescriptor::kMicrodumpOnConsole = {}; - -MinidumpDescriptor::MinidumpDescriptor(const MinidumpDescriptor& descriptor) - : mode_(descriptor.mode_), - fd_(descriptor.fd_), - directory_(descriptor.directory_), - c_path_(NULL), - size_limit_(descriptor.size_limit_), - microdump_extra_info_(descriptor.microdump_extra_info_) { - // The copy constructor is not allowed to be called on a MinidumpDescriptor - // with a valid path_, as getting its c_path_ would require the heap which - // can cause problems in compromised environments. - assert(descriptor.path_.empty()); -} - -MinidumpDescriptor& MinidumpDescriptor::operator=( - const MinidumpDescriptor& descriptor) { - assert(descriptor.path_.empty()); - - mode_ = descriptor.mode_; - fd_ = descriptor.fd_; - directory_ = descriptor.directory_; - path_.clear(); - if (c_path_) { - // This descriptor already had a path set, so generate a new one. - c_path_ = NULL; - UpdatePath(); - } - size_limit_ = descriptor.size_limit_; - microdump_extra_info_ = descriptor.microdump_extra_info_; - return *this; -} - -void MinidumpDescriptor::UpdatePath() { - assert(mode_ == kWriteMinidumpToFile && !directory_.empty()); - - GUID guid; - char guid_str[kGUIDStringLength + 1]; - if (!CreateGUID(&guid) || !GUIDToString(&guid, guid_str, sizeof(guid_str))) { - assert(false); - } - - path_.clear(); - path_ = directory_ + "/" + guid_str + ".dmp"; - c_path_ = path_.c_str(); -} - -} // namespace google_breakpad diff --git a/toolkit/crashreporter/google-breakpad/src/client/linux/handler/minidump_descriptor.h b/toolkit/crashreporter/google-breakpad/src/client/linux/handler/minidump_descriptor.h deleted file mode 100644 index 782a60a4e..000000000 --- a/toolkit/crashreporter/google-breakpad/src/client/linux/handler/minidump_descriptor.h +++ /dev/null @@ -1,149 +0,0 @@ -// Copyright (c) 2012 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_HANDLER_MINIDUMP_DESCRIPTOR_H_ -#define CLIENT_LINUX_HANDLER_MINIDUMP_DESCRIPTOR_H_ - -#include <assert.h> -#include <sys/types.h> - -#include <string> - -#include "client/linux/handler/microdump_extra_info.h" -#include "common/using_std_string.h" - -// This class describes how a crash dump should be generated, either: -// - Writing a full minidump to a file in a given directory (the actual path, -// inside the directory, is determined by this class). -// - Writing a full minidump to a given fd. -// - Writing a reduced microdump to the console (logcat on Android). -namespace google_breakpad { - -class MinidumpDescriptor { - public: - struct MicrodumpOnConsole {}; - static const MicrodumpOnConsole kMicrodumpOnConsole; - - MinidumpDescriptor() - : mode_(kUninitialized), - fd_(-1), - size_limit_(-1) {} - - explicit MinidumpDescriptor(const string& directory) - : mode_(kWriteMinidumpToFile), - fd_(-1), - directory_(directory), - c_path_(NULL), - size_limit_(-1) { - assert(!directory.empty()); - } - - explicit MinidumpDescriptor(int fd) - : mode_(kWriteMinidumpToFd), - fd_(fd), - c_path_(NULL), - size_limit_(-1) { - assert(fd != -1); - } - - explicit MinidumpDescriptor(const MicrodumpOnConsole&) - : mode_(kWriteMicrodumpToConsole), - fd_(-1), - size_limit_(-1) {} - - explicit MinidumpDescriptor(const MinidumpDescriptor& descriptor); - MinidumpDescriptor& operator=(const MinidumpDescriptor& descriptor); - - static MinidumpDescriptor getMicrodumpDescriptor(); - - bool IsFD() const { return mode_ == kWriteMinidumpToFd; } - - int fd() const { return fd_; } - - string directory() const { return directory_; } - - const char* path() const { return c_path_; } - - bool IsMicrodumpOnConsole() const { - return mode_ == kWriteMicrodumpToConsole; - } - - // Updates the path so it is unique. - // Should be called from a normal context: this methods uses the heap. - void UpdatePath(); - - off_t size_limit() const { return size_limit_; } - void set_size_limit(off_t limit) { size_limit_ = limit; } - - MicrodumpExtraInfo* microdump_extra_info() { - assert(IsMicrodumpOnConsole()); - return µdump_extra_info_; - }; - - private: - enum DumpMode { - kUninitialized = 0, - kWriteMinidumpToFile, - kWriteMinidumpToFd, - kWriteMicrodumpToConsole - }; - - // Specifies the dump mode (see DumpMode). - DumpMode mode_; - - // The file descriptor where the minidump is generated. - int fd_; - - // The directory where the minidump should be generated. - string directory_; - - // The full path to the generated minidump. - string path_; - - // The C string of |path_|. Precomputed so it can be access from a compromised - // context. - const char* c_path_; - - off_t size_limit_; - - // The extra microdump data (e.g. product name/version, build - // fingerprint, gpu fingerprint) that should be appended to the dump - // (microdump only). Microdumps don't have the ability of appending - // extra metadata after the dump is generated (as opposite to - // minidumps MIME fields), therefore the extra data must be provided - // upfront. Any memory pointed to by members of the - // MicrodumpExtraInfo struct must be valid for the lifetime of the - // process (read: the caller has to guarantee that it is stored in - // global static storage.) - MicrodumpExtraInfo microdump_extra_info_; -}; - -} // namespace google_breakpad - -#endif // CLIENT_LINUX_HANDLER_MINIDUMP_DESCRIPTOR_H_ diff --git a/toolkit/crashreporter/google-breakpad/src/client/linux/log/log.cc b/toolkit/crashreporter/google-breakpad/src/client/linux/log/log.cc deleted file mode 100644 index fc23aa6d5..000000000 --- a/toolkit/crashreporter/google-breakpad/src/client/linux/log/log.cc +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright (c) 2012 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 "client/linux/log/log.h" - -#if defined(__ANDROID__) -#include <android/log.h> -#include <dlfcn.h> -#else -#include "third_party/lss/linux_syscall_support.h" -#endif - -namespace logger { - -#if defined(__ANDROID__) -namespace { - -// __android_log_buf_write() is not exported in the NDK and is being used by -// dynamic runtime linking. Its declaration is taken from Android's -// system/core/include/log/log.h. -using AndroidLogBufferWriteFunc = int (*)(int bufID, int prio, const char *tag, - const char *text); -const int kAndroidCrashLogId = 4; // From LOG_ID_CRASH in log.h. -const char kAndroidLogTag[] = "google-breakpad"; - -bool g_crash_log_initialized = false; -AndroidLogBufferWriteFunc g_android_log_buf_write = nullptr; - -} // namespace - -void initializeCrashLogWriter() { - if (g_crash_log_initialized) - return; - g_android_log_buf_write = reinterpret_cast<AndroidLogBufferWriteFunc>( - dlsym(RTLD_DEFAULT, "__android_log_buf_write")); - g_crash_log_initialized = true; -} - -int writeToCrashLog(const char* buf) { - // Try writing to the crash log ring buffer. If not available, fall back to - // the standard log buffer. - if (g_android_log_buf_write) { - return g_android_log_buf_write(kAndroidCrashLogId, ANDROID_LOG_FATAL, - kAndroidLogTag, buf); - } - return __android_log_write(ANDROID_LOG_FATAL, kAndroidLogTag, buf); -} -#endif - -int write(const char* buf, size_t nbytes) { -#if defined(__ANDROID__) - return __android_log_write(ANDROID_LOG_WARN, kAndroidLogTag, buf); -#else - return sys_write(2, buf, nbytes); -#endif -} - -} // namespace logger diff --git a/toolkit/crashreporter/google-breakpad/src/client/linux/log/log.h b/toolkit/crashreporter/google-breakpad/src/client/linux/log/log.h deleted file mode 100644 index f94bbd5fb..000000000 --- a/toolkit/crashreporter/google-breakpad/src/client/linux/log/log.h +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright (c) 2012, 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_LOG_LOG_H_ -#define CLIENT_LINUX_LOG_LOG_H_ - -#include <stddef.h> - -namespace logger { - -int write(const char* buf, size_t nbytes); - -// In the case of Android the log can be written to the default system log -// (default behavior of write() above, or to the crash log (see -// writeToCrashLog() below). -#if defined(__ANDROID__) - -// The logger must be initialized in a non-compromised context. -void initializeCrashLogWriter(); - -// Once initialized, writeToCrashLog is safe to use in a compromised context, -// even if the initialization failed, in which case this will silently fall -// back on write(). -int writeToCrashLog(const char* buf); -#endif - -} // namespace logger - -#endif // CLIENT_LINUX_LOG_LOG_H_ diff --git a/toolkit/crashreporter/google-breakpad/src/client/linux/microdump_writer/microdump_writer.cc b/toolkit/crashreporter/google-breakpad/src/client/linux/microdump_writer/microdump_writer.cc deleted file mode 100644 index a508667a0..000000000 --- a/toolkit/crashreporter/google-breakpad/src/client/linux/microdump_writer/microdump_writer.cc +++ /dev/null @@ -1,609 +0,0 @@ -// Copyright (c) 2014, 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. - -// This translation unit generates microdumps into the console (logcat on -// Android). See crbug.com/410294 for more info and design docs. - -#include "client/linux/microdump_writer/microdump_writer.h" - -#include <limits> - -#include <sys/utsname.h> - -#include "client/linux/dump_writer_common/thread_info.h" -#include "client/linux/dump_writer_common/ucontext_reader.h" -#include "client/linux/handler/exception_handler.h" -#include "client/linux/handler/microdump_extra_info.h" -#include "client/linux/log/log.h" -#include "client/linux/minidump_writer/linux_ptrace_dumper.h" -#include "common/linux/file_id.h" -#include "common/linux/linux_libc_support.h" -#include "common/memory.h" - -namespace { - -using google_breakpad::auto_wasteful_vector; -using google_breakpad::ExceptionHandler; -using google_breakpad::kDefaultBuildIdSize; -using google_breakpad::LinuxDumper; -using google_breakpad::LinuxPtraceDumper; -using google_breakpad::MappingInfo; -using google_breakpad::MappingList; -using google_breakpad::MicrodumpExtraInfo; -using google_breakpad::RawContextCPU; -using google_breakpad::ThreadInfo; -using google_breakpad::UContextReader; - -const size_t kLineBufferSize = 2048; - -#if !defined(__LP64__) -// The following are only used by DumpFreeSpace, so need to be compiled -// in conditionally in the same way. - -template <typename Dst, typename Src> -Dst saturated_cast(Src src) { - if (src >= std::numeric_limits<Dst>::max()) - return std::numeric_limits<Dst>::max(); - if (src <= std::numeric_limits<Dst>::min()) - return std::numeric_limits<Dst>::min(); - return static_cast<Dst>(src); -} - -int Log2Floor(uint64_t n) { - // Copied from chromium src/base/bits.h - if (n == 0) - return -1; - int log = 0; - uint64_t value = n; - for (int i = 5; i >= 0; --i) { - int shift = (1 << i); - uint64_t x = value >> shift; - if (x != 0) { - value = x; - log += shift; - } - } - assert(value == 1u); - return log; -} - -bool MappingsAreAdjacent(const MappingInfo& a, const MappingInfo& b) { - // Because of load biasing, we can end up with a situation where two - // mappings actually overlap. So we will define adjacency to also include a - // b start address that lies within a's address range (including starting - // immediately after a). - // Because load biasing only ever moves the start address backwards, the end - // address should still increase. - return a.start_addr <= b.start_addr && a.start_addr + a.size >= b.start_addr; -} - -bool MappingLessThan(const MappingInfo* a, const MappingInfo* b) { - // Return true if mapping a is before mapping b. - // For the same reason (load biasing) we compare end addresses, which - unlike - // start addresses - will not have been modified. - return a->start_addr + a->size < b->start_addr + b->size; -} - -size_t NextOrderedMapping( - const google_breakpad::wasteful_vector<MappingInfo*>& mappings, - size_t curr) { - // Find the mapping that directly follows mappings[curr]. - // If no such mapping exists, return |invalid| to indicate this. - const size_t invalid = std::numeric_limits<size_t>::max(); - size_t best = invalid; - for (size_t next = 0; next < mappings.size(); ++next) { - if (MappingLessThan(mappings[curr], mappings[next]) && - (best == invalid || MappingLessThan(mappings[next], mappings[best]))) { - best = next; - } - } - return best; -} - -#endif // !__LP64__ - -class MicrodumpWriter { - public: - MicrodumpWriter(const ExceptionHandler::CrashContext* context, - const MappingList& mappings, - const MicrodumpExtraInfo& microdump_extra_info, - LinuxDumper* dumper) - : ucontext_(context ? &context->context : NULL), -#if !defined(__ARM_EABI__) && !defined(__mips__) - float_state_(context ? &context->float_state : NULL), -#endif - dumper_(dumper), - mapping_list_(mappings), - microdump_extra_info_(microdump_extra_info), - log_line_(NULL) { - log_line_ = reinterpret_cast<char*>(Alloc(kLineBufferSize)); - if (log_line_) - log_line_[0] = '\0'; // Clear out the log line buffer. - } - - ~MicrodumpWriter() { dumper_->ThreadsResume(); } - - bool Init() { - // In the exceptional case where the system was out of memory and there - // wasn't even room to allocate the line buffer, bail out. There is nothing - // useful we can possibly achieve without the ability to Log. At least let's - // try to not crash. - if (!dumper_->Init() || !log_line_) - return false; - return dumper_->ThreadsSuspend() && dumper_->LateInit(); - } - - bool Dump() { - bool success; - LogLine("-----BEGIN BREAKPAD MICRODUMP-----"); - DumpProductInformation(); - DumpOSInformation(); - DumpProcessType(); - DumpGPUInformation(); -#if !defined(__LP64__) - DumpFreeSpace(); -#endif - success = DumpCrashingThread(); - if (success) - success = DumpMappings(); - LogLine("-----END BREAKPAD MICRODUMP-----"); - dumper_->ThreadsResume(); - return success; - } - - private: - // Writes one line to the system log. - void LogLine(const char* msg) { -#if defined(__ANDROID__) - logger::writeToCrashLog(msg); -#else - logger::write(msg, my_strlen(msg)); - logger::write("\n", 1); -#endif - } - - // Stages the given string in the current line buffer. - void LogAppend(const char* str) { - my_strlcat(log_line_, str, kLineBufferSize); - } - - // As above (required to take precedence over template specialization below). - void LogAppend(char* str) { - LogAppend(const_cast<const char*>(str)); - } - - // Stages the hex repr. of the given int type in the current line buffer. - template<typename T> - void LogAppend(T value) { - // Make enough room to hex encode the largest int type + NUL. - static const char HEX[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', - 'A', 'B', 'C', 'D', 'E', 'F'}; - char hexstr[sizeof(T) * 2 + 1]; - for (int i = sizeof(T) * 2 - 1; i >= 0; --i, value >>= 4) - hexstr[i] = HEX[static_cast<uint8_t>(value) & 0x0F]; - hexstr[sizeof(T) * 2] = '\0'; - LogAppend(hexstr); - } - - // Stages the buffer content hex-encoded in the current line buffer. - void LogAppend(const void* buf, size_t length) { - const uint8_t* ptr = reinterpret_cast<const uint8_t*>(buf); - for (size_t i = 0; i < length; ++i, ++ptr) - LogAppend(*ptr); - } - - // Writes out the current line buffer on the system log. - void LogCommitLine() { - LogLine(log_line_); - my_strlcpy(log_line_, "", kLineBufferSize); - } - - void DumpProductInformation() { - LogAppend("V "); - if (microdump_extra_info_.product_info) { - LogAppend(microdump_extra_info_.product_info); - } else { - LogAppend("UNKNOWN:0.0.0.0"); - } - LogCommitLine(); - } - - void DumpProcessType() { - LogAppend("P "); - if (microdump_extra_info_.process_type) { - LogAppend(microdump_extra_info_.process_type); - } else { - LogAppend("UNKNOWN"); - } - LogCommitLine(); - } - - void DumpOSInformation() { - const uint8_t n_cpus = static_cast<uint8_t>(sysconf(_SC_NPROCESSORS_CONF)); - -#if defined(__ANDROID__) - const char kOSId[] = "A"; -#else - const char kOSId[] = "L"; -#endif - -// Dump the runtime architecture. On multiarch devices it might not match the -// hw architecture (the one returned by uname()), for instance in the case of -// a 32-bit app running on a aarch64 device. -#if defined(__aarch64__) - const char kArch[] = "arm64"; -#elif defined(__ARMEL__) - const char kArch[] = "arm"; -#elif defined(__x86_64__) - const char kArch[] = "x86_64"; -#elif defined(__i386__) - const char kArch[] = "x86"; -#elif defined(__mips__) -# if _MIPS_SIM == _ABIO32 - const char kArch[] = "mips"; -# elif _MIPS_SIM == _ABI64 - const char kArch[] = "mips64"; -# else -# error "This mips ABI is currently not supported (n32)" -#endif -#else -#error "This code has not been ported to your platform yet" -#endif - - LogAppend("O "); - LogAppend(kOSId); - LogAppend(" "); - LogAppend(kArch); - LogAppend(" "); - LogAppend(n_cpus); - LogAppend(" "); - - // Dump the HW architecture (e.g., armv7l, aarch64). - struct utsname uts; - const bool has_uts_info = (uname(&uts) == 0); - const char* hwArch = has_uts_info ? uts.machine : "unknown_hw_arch"; - LogAppend(hwArch); - LogAppend(" "); - - // If the client has attached a build fingerprint to the MinidumpDescriptor - // use that one. Otherwise try to get some basic info from uname(). - if (microdump_extra_info_.build_fingerprint) { - LogAppend(microdump_extra_info_.build_fingerprint); - } else if (has_uts_info) { - LogAppend(uts.release); - LogAppend(" "); - LogAppend(uts.version); - } else { - LogAppend("no build fingerprint available"); - } - LogCommitLine(); - } - - void DumpGPUInformation() { - LogAppend("G "); - if (microdump_extra_info_.gpu_fingerprint) { - LogAppend(microdump_extra_info_.gpu_fingerprint); - } else { - LogAppend("UNKNOWN"); - } - LogCommitLine(); - } - - bool DumpThreadStack(uint32_t thread_id, - uintptr_t stack_pointer, - int max_stack_len, - uint8_t** stack_copy) { - *stack_copy = NULL; - const void* stack; - size_t stack_len; - - if (!dumper_->GetStackInfo(&stack, &stack_len, stack_pointer)) { - // The stack pointer might not be available. In this case we don't hard - // fail, just produce a (almost useless) microdump w/o a stack section. - return true; - } - - LogAppend("S 0 "); - LogAppend(stack_pointer); - LogAppend(" "); - LogAppend(reinterpret_cast<uintptr_t>(stack)); - LogAppend(" "); - LogAppend(stack_len); - LogCommitLine(); - - if (max_stack_len >= 0 && - stack_len > static_cast<unsigned int>(max_stack_len)) { - stack_len = max_stack_len; - } - - *stack_copy = reinterpret_cast<uint8_t*>(Alloc(stack_len)); - dumper_->CopyFromProcess(*stack_copy, thread_id, stack, stack_len); - - // Dump the content of the stack, splicing it into chunks which size is - // compatible with the max logcat line size (see LOGGER_ENTRY_MAX_PAYLOAD). - const size_t STACK_DUMP_CHUNK_SIZE = 384; - for (size_t stack_off = 0; stack_off < stack_len; - stack_off += STACK_DUMP_CHUNK_SIZE) { - LogAppend("S "); - LogAppend(reinterpret_cast<uintptr_t>(stack) + stack_off); - LogAppend(" "); - LogAppend(*stack_copy + stack_off, - std::min(STACK_DUMP_CHUNK_SIZE, stack_len - stack_off)); - LogCommitLine(); - } - return true; - } - - // Write information about the crashing thread. - bool DumpCrashingThread() { - const unsigned num_threads = dumper_->threads().size(); - - for (unsigned i = 0; i < num_threads; ++i) { - MDRawThread thread; - my_memset(&thread, 0, sizeof(thread)); - thread.thread_id = dumper_->threads()[i]; - - // Dump only the crashing thread. - if (static_cast<pid_t>(thread.thread_id) != dumper_->crash_thread()) - continue; - - assert(ucontext_); - assert(!dumper_->IsPostMortem()); - - uint8_t* stack_copy; - const uintptr_t stack_ptr = UContextReader::GetStackPointer(ucontext_); - if (!DumpThreadStack(thread.thread_id, stack_ptr, -1, &stack_copy)) - return false; - - RawContextCPU cpu; - my_memset(&cpu, 0, sizeof(RawContextCPU)); -#if !defined(__ARM_EABI__) && !defined(__mips__) - UContextReader::FillCPUContext(&cpu, ucontext_, float_state_); -#else - UContextReader::FillCPUContext(&cpu, ucontext_); -#endif - DumpCPUState(&cpu); - } - return true; - } - - void DumpCPUState(RawContextCPU* cpu) { - LogAppend("C "); - LogAppend(cpu, sizeof(*cpu)); - LogCommitLine(); - } - - // If there is caller-provided information about this mapping - // in the mapping_list_ list, return true. Otherwise, return false. - bool HaveMappingInfo(const MappingInfo& mapping) { - for (MappingList::const_iterator iter = mapping_list_.begin(); - iter != mapping_list_.end(); - ++iter) { - // Ignore any mappings that are wholly contained within - // mappings in the mapping_info_ list. - if (mapping.start_addr >= iter->first.start_addr && - (mapping.start_addr + mapping.size) <= - (iter->first.start_addr + iter->first.size)) { - return true; - } - } - return false; - } - - // Dump information about the provided |mapping|. If |identifier| is non-NULL, - // use it instead of calculating a file ID from the mapping. - void DumpModule(const MappingInfo& mapping, - bool member, - unsigned int mapping_id, - const uint8_t* identifier) { - - auto_wasteful_vector<uint8_t, kDefaultBuildIdSize> identifier_bytes( - dumper_->allocator()); - - if (identifier) { - // GUID was provided by caller. - identifier_bytes.insert(identifier_bytes.end(), - identifier, - identifier + sizeof(MDGUID)); - } else { - dumper_->ElfFileIdentifierForMapping( - mapping, - member, - mapping_id, - identifier_bytes); - } - - // Copy as many bytes of |identifier| as will fit into a MDGUID - MDGUID module_identifier = {0}; - memcpy(&module_identifier, &identifier_bytes[0], - std::min(sizeof(MDGUID), identifier_bytes.size())); - - char file_name[NAME_MAX]; - char file_path[NAME_MAX]; - dumper_->GetMappingEffectiveNameAndPath( - mapping, file_path, sizeof(file_path), file_name, sizeof(file_name)); - - LogAppend("M "); - LogAppend(static_cast<uintptr_t>(mapping.start_addr)); - LogAppend(" "); - LogAppend(mapping.offset); - LogAppend(" "); - LogAppend(mapping.size); - LogAppend(" "); - LogAppend(module_identifier.data1); - LogAppend(module_identifier.data2); - LogAppend(module_identifier.data3); - LogAppend(module_identifier.data4[0]); - LogAppend(module_identifier.data4[1]); - LogAppend(module_identifier.data4[2]); - LogAppend(module_identifier.data4[3]); - LogAppend(module_identifier.data4[4]); - LogAppend(module_identifier.data4[5]); - LogAppend(module_identifier.data4[6]); - LogAppend(module_identifier.data4[7]); - LogAppend("0 "); // Age is always 0 on Linux. - LogAppend(file_name); - LogCommitLine(); - } - -#if !defined(__LP64__) - void DumpFreeSpace() { - const google_breakpad::wasteful_vector<MappingInfo*>& mappings = - dumper_->mappings(); - if (mappings.size() == 0) return; - - // This is complicated by the fact that mappings is not in order. It should - // be mostly in order, however the mapping that contains the entry point for - // the process is always at the front of the vector. - - static const int HBITS = sizeof(size_t) * 8; - size_t hole_histogram[HBITS]; - my_memset(hole_histogram, 0, sizeof(hole_histogram)); - - // Find the lowest address mapping. - size_t curr = 0; - for (size_t i = 1; i < mappings.size(); ++i) { - if (mappings[i]->start_addr < mappings[curr]->start_addr) curr = i; - } - - uintptr_t lo_addr = mappings[curr]->start_addr; - - size_t hole_cnt = 0; - size_t hole_max = 0; - size_t hole_sum = 0; - - while (true) { - // Skip to the end of an adjacent run of mappings. This is an optimization - // for the fact that mappings is mostly sorted. - while (curr != mappings.size() - 1 && - MappingsAreAdjacent(*mappings[curr], *mappings[curr + 1])) { - ++curr; - } - - size_t next = NextOrderedMapping(mappings, curr); - if (next == std::numeric_limits<size_t>::max()) - break; - - uintptr_t hole_lo = mappings[curr]->start_addr + mappings[curr]->size; - uintptr_t hole_hi = mappings[next]->start_addr; - - if (hole_hi > hole_lo) { - size_t hole_sz = hole_hi - hole_lo; - hole_sum += hole_sz; - hole_max = std::max(hole_sz, hole_max); - ++hole_cnt; - ++hole_histogram[Log2Floor(hole_sz)]; - } - curr = next; - } - - uintptr_t hi_addr = mappings[curr]->start_addr + mappings[curr]->size; - - LogAppend("H "); - LogAppend(lo_addr); - LogAppend(" "); - LogAppend(hi_addr); - LogAppend(" "); - LogAppend(saturated_cast<uint16_t>(hole_cnt)); - LogAppend(" "); - LogAppend(hole_max); - LogAppend(" "); - LogAppend(hole_sum); - for (unsigned int i = 0; i < HBITS; ++i) { - if (!hole_histogram[i]) continue; - LogAppend(" "); - LogAppend(saturated_cast<uint8_t>(i)); - LogAppend(":"); - LogAppend(saturated_cast<uint8_t>(hole_histogram[i])); - } - LogCommitLine(); - } -#endif - - // Write information about the mappings in effect. - bool DumpMappings() { - // First write all the mappings from the dumper - for (unsigned i = 0; i < dumper_->mappings().size(); ++i) { - const MappingInfo& mapping = *dumper_->mappings()[i]; - if (mapping.name[0] == 0 || // only want modules with filenames. - !mapping.exec || // only want executable mappings. - mapping.size < 4096 || // too small to get a signature for. - HaveMappingInfo(mapping)) { - continue; - } - - DumpModule(mapping, true, i, NULL); - } - // Next write all the mappings provided by the caller - for (MappingList::const_iterator iter = mapping_list_.begin(); - iter != mapping_list_.end(); - ++iter) { - DumpModule(iter->first, false, 0, iter->second); - } - return true; - } - - void* Alloc(unsigned bytes) { return dumper_->allocator()->Alloc(bytes); } - - const ucontext_t* const ucontext_; -#if !defined(__ARM_EABI__) && !defined(__mips__) - const google_breakpad::fpstate_t* const float_state_; -#endif - LinuxDumper* dumper_; - const MappingList& mapping_list_; - const MicrodumpExtraInfo microdump_extra_info_; - char* log_line_; -}; -} // namespace - -namespace google_breakpad { - -bool WriteMicrodump(pid_t crashing_process, - const void* blob, - size_t blob_size, - const MappingList& mappings, - const MicrodumpExtraInfo& microdump_extra_info) { - LinuxPtraceDumper dumper(crashing_process); - const ExceptionHandler::CrashContext* context = NULL; - if (blob) { - if (blob_size != sizeof(ExceptionHandler::CrashContext)) - return false; - context = reinterpret_cast<const ExceptionHandler::CrashContext*>(blob); - dumper.set_crash_address( - reinterpret_cast<uintptr_t>(context->siginfo.si_addr)); - dumper.set_crash_signal(context->siginfo.si_signo); - dumper.set_crash_thread(context->tid); - } - MicrodumpWriter writer(context, mappings, microdump_extra_info, &dumper); - if (!writer.Init()) - return false; - return writer.Dump(); -} - -} // namespace google_breakpad diff --git a/toolkit/crashreporter/google-breakpad/src/client/linux/microdump_writer/microdump_writer.h b/toolkit/crashreporter/google-breakpad/src/client/linux/microdump_writer/microdump_writer.h deleted file mode 100644 index 7c742761d..000000000 --- a/toolkit/crashreporter/google-breakpad/src/client/linux/microdump_writer/microdump_writer.h +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright (c) 2014, 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_MINIDUMP_WRITER_MICRODUMP_WRITER_H_ -#define CLIENT_LINUX_MINIDUMP_WRITER_MICRODUMP_WRITER_H_ - -#include <stdint.h> -#include <sys/types.h> - -#include "client/linux/dump_writer_common/mapping_info.h" - -namespace google_breakpad { - -struct MicrodumpExtraInfo; - -// Writes a microdump (a reduced dump containing only the state of the crashing -// thread) on the console (logcat on Android). These functions do not malloc nor -// use libc functions which may. Thus, it can be used in contexts where the -// state of the heap may be corrupt. -// Args: -// crashing_process: the pid of the crashing process. This must be trusted. -// blob: a blob of data from the crashing process. See exception_handler.h -// blob_size: the length of |blob| in bytes. -// mappings: a list of additional mappings provided by the application. -// build_fingerprint: a (optional) C string which determines the OS -// build fingerprint (e.g., aosp/occam/mako:5.1.1/LMY47W/1234:eng/dev-keys). -// product_info: a (optional) C string which determines the product name and -// version (e.g., WebView:42.0.2311.136). -// -// Returns true iff successful. -bool WriteMicrodump(pid_t crashing_process, - const void* blob, - size_t blob_size, - const MappingList& mappings, - const MicrodumpExtraInfo& microdump_extra_info); - -} // namespace google_breakpad - -#endif // CLIENT_LINUX_MINIDUMP_WRITER_MICRODUMP_WRITER_H_ diff --git a/toolkit/crashreporter/google-breakpad/src/client/linux/microdump_writer/microdump_writer_unittest.cc b/toolkit/crashreporter/google-breakpad/src/client/linux/microdump_writer/microdump_writer_unittest.cc deleted file mode 100644 index 58a731188..000000000 --- a/toolkit/crashreporter/google-breakpad/src/client/linux/microdump_writer/microdump_writer_unittest.cc +++ /dev/null @@ -1,257 +0,0 @@ -// Copyright (c) 2014 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 <ctype.h> -#include <sys/syscall.h> -#include <sys/types.h> -#include <unistd.h> - -#include <sstream> -#include <string> - -#include "breakpad_googletest_includes.h" -#include "client/linux/handler/exception_handler.h" -#include "client/linux/handler/microdump_extra_info.h" -#include "client/linux/microdump_writer/microdump_writer.h" -#include "common/linux/eintr_wrapper.h" -#include "common/linux/ignore_ret.h" -#include "common/scoped_ptr.h" -#include "common/tests/auto_tempdir.h" -#include "common/using_std_string.h" - -using namespace google_breakpad; - -namespace { - -typedef testing::Test MicrodumpWriterTest; - -MicrodumpExtraInfo MakeMicrodumpExtraInfo( - const char* build_fingerprint, - const char* product_info, - const char* gpu_fingerprint) { - MicrodumpExtraInfo info; - info.build_fingerprint = build_fingerprint; - info.product_info = product_info; - info.gpu_fingerprint = gpu_fingerprint; - return info; -} - -void CrashAndGetMicrodump( - const MappingList& mappings, - const MicrodumpExtraInfo& microdump_extra_info, - scoped_array<char>* buf) { - int fds[2]; - ASSERT_NE(-1, pipe(fds)); - - AutoTempDir temp_dir; - string stderr_file = temp_dir.path() + "/stderr.log"; - int err_fd = open(stderr_file.c_str(), O_CREAT | O_RDWR, S_IRUSR | S_IWUSR); - ASSERT_NE(-1, err_fd); - - const pid_t child = fork(); - if (child == 0) { - close(fds[1]); - char b; - IGNORE_RET(HANDLE_EINTR(read(fds[0], &b, sizeof(b)))); - close(fds[0]); - syscall(__NR_exit); - } - close(fds[0]); - - ExceptionHandler::CrashContext context; - memset(&context, 0, sizeof(context)); - - // Set a non-zero tid to avoid tripping asserts. - context.tid = child; - - // Redirect temporarily stderr to the stderr.log file. - int save_err = dup(STDERR_FILENO); - ASSERT_NE(-1, save_err); - ASSERT_NE(-1, dup2(err_fd, STDERR_FILENO)); - - ASSERT_TRUE(WriteMicrodump(child, &context, sizeof(context), mappings, - microdump_extra_info)); - - // Revert stderr back to the console. - dup2(save_err, STDERR_FILENO); - close(save_err); - - // Read back the stderr file and check for the microdump marker. - fsync(err_fd); - lseek(err_fd, 0, SEEK_SET); - const size_t kBufSize = 64 * 1024; - buf->reset(new char[kBufSize]); - ASSERT_GT(read(err_fd, buf->get(), kBufSize), 0); - - close(err_fd); - close(fds[1]); - - ASSERT_NE(static_cast<char*>(0), strstr( - buf->get(), "-----BEGIN BREAKPAD MICRODUMP-----")); - ASSERT_NE(static_cast<char*>(0), strstr( - buf->get(), "-----END BREAKPAD MICRODUMP-----")); -} - -void CheckMicrodumpContents(const string& microdump_content, - const MicrodumpExtraInfo& expected_info) { - std::istringstream iss(microdump_content); - bool did_find_os_info = false; - bool did_find_product_info = false; - bool did_find_gpu_info = false; - for (string line; std::getline(iss, line);) { - if (line.find("O ") == 0) { - std::istringstream os_info_tokens(line); - string token; - os_info_tokens.ignore(2); // Ignore the "O " preamble. - // Check the OS descriptor char (L=Linux, A=Android). - os_info_tokens >> token; - ASSERT_TRUE(token == "L" || token == "A"); - - os_info_tokens >> token; // HW architecture. - os_info_tokens >> token; // Number of cpus. - for (size_t i = 0; i < token.size(); ++i) - ASSERT_TRUE(isxdigit(token[i])); - os_info_tokens >> token; // SW architecture. - - // Check that the build fingerprint is in the right place. - os_info_tokens >> token; - if (expected_info.build_fingerprint) - ASSERT_EQ(expected_info.build_fingerprint, token); - did_find_os_info = true; - } else if (line.find("V ") == 0) { - if (expected_info.product_info) - ASSERT_EQ(string("V ") + expected_info.product_info, line); - did_find_product_info = true; - } else if (line.find("G ") == 0) { - if (expected_info.gpu_fingerprint) - ASSERT_EQ(string("G ") + expected_info.gpu_fingerprint, line); - did_find_gpu_info = true; - } - } - ASSERT_TRUE(did_find_os_info); - ASSERT_TRUE(did_find_product_info); - ASSERT_TRUE(did_find_gpu_info); -} - -void CheckMicrodumpContents(const string& microdump_content, - const string& expected_fingerprint, - const string& expected_product_info, - const string& expected_gpu_fingerprint) { - CheckMicrodumpContents( - microdump_content, - MakeMicrodumpExtraInfo(expected_fingerprint.c_str(), - expected_product_info.c_str(), - expected_gpu_fingerprint.c_str())); -} - -TEST(MicrodumpWriterTest, BasicWithMappings) { - // Push some extra mapping to check the MappingList logic. - const uint32_t memory_size = sysconf(_SC_PAGESIZE); - const char* kMemoryName = "libfoo.so"; - const uint8_t kModuleGUID[sizeof(MDGUID)] = { - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, - 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF - }; - - MappingInfo info; - info.start_addr = memory_size; - info.size = memory_size; - info.offset = 42; - strcpy(info.name, kMemoryName); - - MappingList mappings; - MappingEntry mapping; - mapping.first = info; - memcpy(mapping.second, kModuleGUID, sizeof(MDGUID)); - mappings.push_back(mapping); - - scoped_array<char> buf; - CrashAndGetMicrodump(mappings, MicrodumpExtraInfo(), &buf); - -#ifdef __LP64__ - ASSERT_NE(static_cast<char*>(0), strstr( - buf.get(), "M 0000000000001000 000000000000002A 0000000000001000 " - "33221100554477668899AABBCCDDEEFF0 libfoo.so")); -#else - ASSERT_NE(static_cast<char*>(0), strstr( - buf.get(), "M 00001000 0000002A 00001000 " - "33221100554477668899AABBCCDDEEFF0 libfoo.so")); -#endif - - // In absence of a product info in the minidump, the writer should just write - // an unknown marker. - ASSERT_NE(static_cast<char*>(0), strstr( - buf.get(), "V UNKNOWN:0.0.0.0")); -} - -// Ensure that the product info and build fingerprint metadata show up in the -// final microdump if present. -TEST(MicrodumpWriterTest, BuildFingerprintAndProductInfo) { - const char kProductInfo[] = "MockProduct:42.0.2311.99"; - const char kBuildFingerprint[] = - "aosp/occam/mako:5.1.1/LMY47W/12345678:userdegbug/dev-keys"; - const char kGPUFingerprint[] = - "Qualcomm;Adreno (TM) 330;OpenGL ES 3.0 V@104.0 AU@ (GIT@Id3510ff6dc)"; - const MicrodumpExtraInfo kMicrodumpExtraInfo( - MakeMicrodumpExtraInfo(kBuildFingerprint, kProductInfo, kGPUFingerprint)); - scoped_array<char> buf; - MappingList no_mappings; - - CrashAndGetMicrodump(no_mappings, kMicrodumpExtraInfo, &buf); - CheckMicrodumpContents(string(buf.get()), kMicrodumpExtraInfo); -} - -TEST(MicrodumpWriterTest, NoProductInfo) { - const char kBuildFingerprint[] = "foobar"; - const char kGPUFingerprint[] = "bazqux"; - scoped_array<char> buf; - MappingList no_mappings; - - const MicrodumpExtraInfo kMicrodumpExtraInfoNoProductInfo( - MakeMicrodumpExtraInfo(kBuildFingerprint, NULL, kGPUFingerprint)); - - CrashAndGetMicrodump(no_mappings, kMicrodumpExtraInfoNoProductInfo, &buf); - CheckMicrodumpContents(string(buf.get()), kBuildFingerprint, - "UNKNOWN:0.0.0.0", kGPUFingerprint); -} - -TEST(MicrodumpWriterTest, NoGPUInfo) { - const char kProductInfo[] = "bazqux"; - const char kBuildFingerprint[] = "foobar"; - scoped_array<char> buf; - MappingList no_mappings; - - const MicrodumpExtraInfo kMicrodumpExtraInfoNoGPUInfo( - MakeMicrodumpExtraInfo(kBuildFingerprint, kProductInfo, NULL)); - - CrashAndGetMicrodump(no_mappings, kMicrodumpExtraInfoNoGPUInfo, &buf); - CheckMicrodumpContents(string(buf.get()), kBuildFingerprint, - kProductInfo, "UNKNOWN"); -} -} // namespace diff --git a/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/cpu_set.h b/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/cpu_set.h deleted file mode 100644 index 1cca9aa5a..000000000 --- a/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/cpu_set.h +++ /dev/null @@ -1,144 +0,0 @@ -// Copyright (c) 2013, 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_MINIDUMP_WRITER_CPU_SET_H_ -#define CLIENT_LINUX_MINIDUMP_WRITER_CPU_SET_H_ - -#include <stdint.h> -#include <assert.h> -#include <string.h> - -#include "common/linux/linux_libc_support.h" -#include "third_party/lss/linux_syscall_support.h" - -namespace google_breakpad { - -// Helper class used to model a set of CPUs, as read from sysfs -// files like /sys/devices/system/cpu/present -// See See http://www.kernel.org/doc/Documentation/cputopology.txt -class CpuSet { -public: - // The maximum number of supported CPUs. - static const size_t kMaxCpus = 1024; - - CpuSet() { - my_memset(mask_, 0, sizeof(mask_)); - } - - // Parse a sysfs file to extract the corresponding CPU set. - bool ParseSysFile(int fd) { - char buffer[512]; - int ret = sys_read(fd, buffer, sizeof(buffer)-1); - if (ret < 0) - return false; - - buffer[ret] = '\0'; - - // Expected format: comma-separated list of items, where each - // item can be a decimal integer, or two decimal integers separated - // by a dash. - // E.g.: - // 0 - // 0,1,2,3 - // 0-3 - // 1,10-23 - const char* p = buffer; - const char* p_end = p + ret; - while (p < p_end) { - // Skip leading space, if any - while (p < p_end && my_isspace(*p)) - p++; - - // Find start and size of current item. - const char* item = p; - size_t item_len = static_cast<size_t>(p_end - p); - const char* item_next = - static_cast<const char*>(my_memchr(p, ',', item_len)); - if (item_next != NULL) { - p = item_next + 1; - item_len = static_cast<size_t>(item_next - item); - } else { - p = p_end; - item_next = p_end; - } - - // Ignore trailing spaces. - while (item_next > item && my_isspace(item_next[-1])) - item_next--; - - // skip empty items. - if (item_next == item) - continue; - - // read first decimal value. - uintptr_t start = 0; - const char* next = my_read_decimal_ptr(&start, item); - uintptr_t end = start; - if (*next == '-') - my_read_decimal_ptr(&end, next+1); - - while (start <= end) - SetBit(start++); - } - return true; - } - - // Intersect this CPU set with another one. - void IntersectWith(const CpuSet& other) { - for (size_t nn = 0; nn < kMaskWordCount; ++nn) - mask_[nn] &= other.mask_[nn]; - } - - // Return the number of CPUs in this set. - int GetCount() { - int result = 0; - for (size_t nn = 0; nn < kMaskWordCount; ++nn) { - result += __builtin_popcount(mask_[nn]); - } - return result; - } - -private: - void SetBit(uintptr_t index) { - size_t nn = static_cast<size_t>(index); - if (nn < kMaxCpus) - mask_[nn / kMaskWordBits] |= (1U << (nn % kMaskWordBits)); - } - - typedef uint32_t MaskWordType; - static const size_t kMaskWordBits = 8*sizeof(MaskWordType); - static const size_t kMaskWordCount = - (kMaxCpus + kMaskWordBits - 1) / kMaskWordBits; - - MaskWordType mask_[kMaskWordCount]; -}; - -} // namespace google_breakpad - -#endif // CLIENT_LINUX_MINIDUMP_WRITER_CPU_SET_H_ diff --git a/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/cpu_set_unittest.cc b/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/cpu_set_unittest.cc deleted file mode 100644 index e2274bd17..000000000 --- a/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/cpu_set_unittest.cc +++ /dev/null @@ -1,164 +0,0 @@ -// Copyright (c) 2013, 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 <stdlib.h> -#include <unistd.h> -#include <sys/types.h> -#include <stdio.h> -#include <errno.h> -#include <string.h> - -#include "breakpad_googletest_includes.h" -#include "client/linux/minidump_writer/cpu_set.h" -#include "common/linux/tests/auto_testfile.h" - -using namespace google_breakpad; - -namespace { - -typedef testing::Test CpuSetTest; - -// Helper class to write test text file to a temporary file and return -// its file descriptor. -class ScopedTestFile : public AutoTestFile { -public: - explicit ScopedTestFile(const char* text) - : AutoTestFile("cpu_set", text) { - } -}; - -} - -TEST(CpuSetTest, EmptyCount) { - CpuSet set; - ASSERT_EQ(0, set.GetCount()); -} - -TEST(CpuSetTest, OneCpu) { - ScopedTestFile file("10"); - ASSERT_TRUE(file.IsOk()); - - CpuSet set; - ASSERT_TRUE(set.ParseSysFile(file.GetFd())); - ASSERT_EQ(1, set.GetCount()); -} - -TEST(CpuSetTest, OneCpuTerminated) { - ScopedTestFile file("10\n"); - ASSERT_TRUE(file.IsOk()); - - CpuSet set; - ASSERT_TRUE(set.ParseSysFile(file.GetFd())); - ASSERT_EQ(1, set.GetCount()); -} - -TEST(CpuSetTest, TwoCpusWithComma) { - ScopedTestFile file("1,10"); - ASSERT_TRUE(file.IsOk()); - - CpuSet set; - ASSERT_TRUE(set.ParseSysFile(file.GetFd())); - ASSERT_EQ(2, set.GetCount()); -} - -TEST(CpuSetTest, TwoCpusWithRange) { - ScopedTestFile file("1-2"); - ASSERT_TRUE(file.IsOk()); - - CpuSet set; - ASSERT_TRUE(set.ParseSysFile(file.GetFd())); - ASSERT_EQ(2, set.GetCount()); -} - -TEST(CpuSetTest, TenCpusWithRange) { - ScopedTestFile file("9-18"); - ASSERT_TRUE(file.IsOk()); - - CpuSet set; - ASSERT_TRUE(set.ParseSysFile(file.GetFd())); - ASSERT_EQ(10, set.GetCount()); -} - -TEST(CpuSetTest, MultiItems) { - ScopedTestFile file("0, 2-4, 128"); - ASSERT_TRUE(file.IsOk()); - - CpuSet set; - ASSERT_TRUE(set.ParseSysFile(file.GetFd())); - ASSERT_EQ(5, set.GetCount()); -} - -TEST(CpuSetTest, IntersectWith) { - ScopedTestFile file1("9-19"); - ASSERT_TRUE(file1.IsOk()); - CpuSet set1; - ASSERT_TRUE(set1.ParseSysFile(file1.GetFd())); - ASSERT_EQ(11, set1.GetCount()); - - ScopedTestFile file2("16-24"); - ASSERT_TRUE(file2.IsOk()); - CpuSet set2; - ASSERT_TRUE(set2.ParseSysFile(file2.GetFd())); - ASSERT_EQ(9, set2.GetCount()); - - set1.IntersectWith(set2); - ASSERT_EQ(4, set1.GetCount()); - ASSERT_EQ(9, set2.GetCount()); -} - -TEST(CpuSetTest, SelfIntersection) { - ScopedTestFile file1("9-19"); - ASSERT_TRUE(file1.IsOk()); - CpuSet set1; - ASSERT_TRUE(set1.ParseSysFile(file1.GetFd())); - ASSERT_EQ(11, set1.GetCount()); - - set1.IntersectWith(set1); - ASSERT_EQ(11, set1.GetCount()); -} - -TEST(CpuSetTest, EmptyIntersection) { - ScopedTestFile file1("0-19"); - ASSERT_TRUE(file1.IsOk()); - CpuSet set1; - ASSERT_TRUE(set1.ParseSysFile(file1.GetFd())); - ASSERT_EQ(20, set1.GetCount()); - - ScopedTestFile file2("20-39"); - ASSERT_TRUE(file2.IsOk()); - CpuSet set2; - ASSERT_TRUE(set2.ParseSysFile(file2.GetFd())); - ASSERT_EQ(20, set2.GetCount()); - - set1.IntersectWith(set2); - ASSERT_EQ(0, set1.GetCount()); - - ASSERT_EQ(20, set2.GetCount()); -} - diff --git a/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/directory_reader.h b/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/directory_reader.h deleted file mode 100644 index a4bde1803..000000000 --- a/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/directory_reader.h +++ /dev/null @@ -1,106 +0,0 @@ -// 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_MINIDUMP_WRITER_DIRECTORY_READER_H_ -#define CLIENT_LINUX_MINIDUMP_WRITER_DIRECTORY_READER_H_ - -#include <stdint.h> -#include <unistd.h> -#include <limits.h> -#include <assert.h> -#include <errno.h> -#include <string.h> - -#include "common/linux/linux_libc_support.h" -#include "third_party/lss/linux_syscall_support.h" - -namespace google_breakpad { - -// A class for enumerating a directory without using diropen/readdir or other -// functions which may allocate memory. -class DirectoryReader { - public: - DirectoryReader(int fd) - : fd_(fd), - buf_used_(0) { - } - - // Return the next entry from the directory - // name: (output) the NUL terminated entry name - // - // Returns true iff successful (false on EOF). - // - // After calling this, one must call |PopEntry| otherwise you'll get the same - // entry over and over. - bool GetNextEntry(const char** name) { - struct kernel_dirent* const dent = - reinterpret_cast<kernel_dirent*>(buf_); - - if (buf_used_ == 0) { - // need to read more entries. - const int n = sys_getdents(fd_, dent, sizeof(buf_)); - if (n < 0) { - return false; - } else if (n == 0) { - hit_eof_ = true; - } else { - buf_used_ += n; - } - } - - if (buf_used_ == 0 && hit_eof_) - return false; - - assert(buf_used_ > 0); - - *name = dent->d_name; - return true; - } - - void PopEntry() { - if (!buf_used_) - return; - - const struct kernel_dirent* const dent = - reinterpret_cast<kernel_dirent*>(buf_); - - buf_used_ -= dent->d_reclen; - my_memmove(buf_, buf_ + dent->d_reclen, buf_used_); - } - - private: - const int fd_; - bool hit_eof_; - unsigned buf_used_; - uint8_t buf_[sizeof(struct kernel_dirent) + NAME_MAX + 1]; -}; - -} // namespace google_breakpad - -#endif // CLIENT_LINUX_MINIDUMP_WRITER_DIRECTORY_READER_H_ diff --git a/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/directory_reader_unittest.cc b/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/directory_reader_unittest.cc deleted file mode 100644 index 326f9e36b..000000000 --- a/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/directory_reader_unittest.cc +++ /dev/null @@ -1,78 +0,0 @@ -// 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 <set> -#include <string> - -#include <dirent.h> -#include <fcntl.h> -#include <sys/types.h> - -#include "client/linux/minidump_writer/directory_reader.h" -#include "common/using_std_string.h" -#include "breakpad_googletest_includes.h" - -using namespace google_breakpad; - -namespace { -typedef testing::Test DirectoryReaderTest; -} - -TEST(DirectoryReaderTest, CompareResults) { - std::set<string> dent_set; - - DIR *const dir = opendir("/proc/self"); - ASSERT_TRUE(dir != NULL); - - struct dirent* dent; - while ((dent = readdir(dir))) - dent_set.insert(dent->d_name); - - closedir(dir); - - const int fd = open("/proc/self", O_DIRECTORY | O_RDONLY); - ASSERT_GE(fd, 0); - - DirectoryReader dir_reader(fd); - unsigned seen = 0; - - const char* name; - while (dir_reader.GetNextEntry(&name)) { - ASSERT_TRUE(dent_set.find(name) != dent_set.end()); - seen++; - dir_reader.PopEntry(); - } - - ASSERT_TRUE(dent_set.find("status") != dent_set.end()); - ASSERT_TRUE(dent_set.find("stat") != dent_set.end()); - ASSERT_TRUE(dent_set.find("cmdline") != dent_set.end()); - - ASSERT_EQ(dent_set.size(), seen); - close(fd); -} diff --git a/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/line_reader.h b/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/line_reader.h deleted file mode 100644 index 779cfeb60..000000000 --- a/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/line_reader.h +++ /dev/null @@ -1,131 +0,0 @@ -// 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_MINIDUMP_WRITER_LINE_READER_H_ -#define CLIENT_LINUX_MINIDUMP_WRITER_LINE_READER_H_ - -#include <stdint.h> -#include <assert.h> -#include <string.h> - -#include "common/linux/linux_libc_support.h" -#include "third_party/lss/linux_syscall_support.h" - -namespace google_breakpad { - -// A class for reading a file, line by line, without using fopen/fgets or other -// functions which may allocate memory. -class LineReader { - public: - LineReader(int fd) - : fd_(fd), - hit_eof_(false), - buf_used_(0) { - } - - // The maximum length of a line. - static const size_t kMaxLineLen = 512; - - // Return the next line from the file. - // line: (output) a pointer to the start of the line. The line is NUL - // terminated. - // len: (output) the length of the line (not inc the NUL byte) - // - // Returns true iff successful (false on EOF). - // - // One must call |PopLine| after this function, otherwise you'll continue to - // get the same line over and over. - bool GetNextLine(const char **line, unsigned *len) { - for (;;) { - if (buf_used_ == 0 && hit_eof_) - return false; - - for (unsigned i = 0; i < buf_used_; ++i) { - if (buf_[i] == '\n' || buf_[i] == 0) { - buf_[i] = 0; - *len = i; - *line = buf_; - return true; - } - } - - if (buf_used_ == sizeof(buf_)) { - // we scanned the whole buffer and didn't find an end-of-line marker. - // This line is too long to process. - return false; - } - - // We didn't find any end-of-line terminators in the buffer. However, if - // this is the last line in the file it might not have one: - if (hit_eof_) { - assert(buf_used_); - // There's room for the NUL because of the buf_used_ == sizeof(buf_) - // check above. - buf_[buf_used_] = 0; - *len = buf_used_; - buf_used_ += 1; // since we appended the NUL. - *line = buf_; - return true; - } - - // Otherwise, we should pull in more data from the file - const ssize_t n = sys_read(fd_, buf_ + buf_used_, - sizeof(buf_) - buf_used_); - if (n < 0) { - return false; - } else if (n == 0) { - hit_eof_ = true; - } else { - buf_used_ += n; - } - - // At this point, we have either set the hit_eof_ flag, or we have more - // data to process... - } - } - - void PopLine(unsigned len) { - // len doesn't include the NUL byte at the end. - - assert(buf_used_ >= len + 1); - buf_used_ -= len + 1; - my_memmove(buf_, buf_ + len + 1, buf_used_); - } - - private: - const int fd_; - - bool hit_eof_; - unsigned buf_used_; - char buf_[kMaxLineLen]; -}; - -} // namespace google_breakpad - -#endif // CLIENT_LINUX_MINIDUMP_WRITER_LINE_READER_H_ diff --git a/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/line_reader_unittest.cc b/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/line_reader_unittest.cc deleted file mode 100644 index 29686f04a..000000000 --- a/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/line_reader_unittest.cc +++ /dev/null @@ -1,169 +0,0 @@ -// 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 <stdlib.h> -#include <unistd.h> -#include <sys/types.h> - -#include "client/linux/minidump_writer/line_reader.h" -#include "breakpad_googletest_includes.h" -#include "common/linux/tests/auto_testfile.h" - -using namespace google_breakpad; - -namespace { - -typedef testing::Test LineReaderTest; - -class ScopedTestFile : public AutoTestFile { -public: - explicit ScopedTestFile(const char* text) - : AutoTestFile("line_reader", text) { - } - - ScopedTestFile(const char* text, size_t text_len) - : AutoTestFile("line_reader", text, text_len) { - } -}; - -} - -TEST(LineReaderTest, EmptyFile) { - ScopedTestFile file(""); - ASSERT_TRUE(file.IsOk()); - LineReader reader(file.GetFd()); - - const char *line; - unsigned len; - ASSERT_FALSE(reader.GetNextLine(&line, &len)); -} - -TEST(LineReaderTest, OneLineTerminated) { - ScopedTestFile file("a\n"); - ASSERT_TRUE(file.IsOk()); - LineReader reader(file.GetFd()); - - const char *line; - unsigned int len; - ASSERT_TRUE(reader.GetNextLine(&line, &len)); - ASSERT_EQ((unsigned int)1, len); - ASSERT_EQ('a', line[0]); - ASSERT_EQ('\0', line[1]); - reader.PopLine(len); - - ASSERT_FALSE(reader.GetNextLine(&line, &len)); -} - -TEST(LineReaderTest, OneLine) { - ScopedTestFile file("a"); - ASSERT_TRUE(file.IsOk()); - LineReader reader(file.GetFd()); - - const char *line; - unsigned len; - ASSERT_TRUE(reader.GetNextLine(&line, &len)); - ASSERT_EQ((unsigned)1, len); - ASSERT_EQ('a', line[0]); - ASSERT_EQ('\0', line[1]); - reader.PopLine(len); - - ASSERT_FALSE(reader.GetNextLine(&line, &len)); -} - -TEST(LineReaderTest, TwoLinesTerminated) { - ScopedTestFile file("a\nb\n"); - ASSERT_TRUE(file.IsOk()); - LineReader reader(file.GetFd()); - - const char *line; - unsigned len; - ASSERT_TRUE(reader.GetNextLine(&line, &len)); - ASSERT_EQ((unsigned)1, len); - ASSERT_EQ('a', line[0]); - ASSERT_EQ('\0', line[1]); - reader.PopLine(len); - - ASSERT_TRUE(reader.GetNextLine(&line, &len)); - ASSERT_EQ((unsigned)1, len); - ASSERT_EQ('b', line[0]); - ASSERT_EQ('\0', line[1]); - reader.PopLine(len); - - ASSERT_FALSE(reader.GetNextLine(&line, &len)); -} - -TEST(LineReaderTest, TwoLines) { - ScopedTestFile file("a\nb"); - ASSERT_TRUE(file.IsOk()); - LineReader reader(file.GetFd()); - - const char *line; - unsigned len; - ASSERT_TRUE(reader.GetNextLine(&line, &len)); - ASSERT_EQ((unsigned)1, len); - ASSERT_EQ('a', line[0]); - ASSERT_EQ('\0', line[1]); - reader.PopLine(len); - - ASSERT_TRUE(reader.GetNextLine(&line, &len)); - ASSERT_EQ((unsigned)1, len); - ASSERT_EQ('b', line[0]); - ASSERT_EQ('\0', line[1]); - reader.PopLine(len); - - ASSERT_FALSE(reader.GetNextLine(&line, &len)); -} - -TEST(LineReaderTest, MaxLength) { - char l[LineReader::kMaxLineLen-1]; - memset(l, 'a', sizeof(l)); - ScopedTestFile file(l, sizeof(l)); - ASSERT_TRUE(file.IsOk()); - LineReader reader(file.GetFd()); - - const char *line; - unsigned len; - ASSERT_TRUE(reader.GetNextLine(&line, &len)); - ASSERT_EQ(sizeof(l), len); - ASSERT_TRUE(memcmp(l, line, sizeof(l)) == 0); - ASSERT_EQ('\0', line[len]); -} - -TEST(LineReaderTest, TooLong) { - // Note: this writes kMaxLineLen 'a' chars in the test file. - char l[LineReader::kMaxLineLen]; - memset(l, 'a', sizeof(l)); - ScopedTestFile file(l, sizeof(l)); - ASSERT_TRUE(file.IsOk()); - LineReader reader(file.GetFd()); - - const char *line; - unsigned len; - ASSERT_FALSE(reader.GetNextLine(&line, &len)); -} diff --git a/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/linux_core_dumper.cc b/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/linux_core_dumper.cc deleted file mode 100644 index 622f05069..000000000 --- a/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/linux_core_dumper.cc +++ /dev/null @@ -1,258 +0,0 @@ -// Copyright (c) 2012, 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. - -// linux_core_dumper.cc: Implement google_breakpad::LinuxCoreDumper. -// See linux_core_dumper.h for details. - -#include "client/linux/minidump_writer/linux_core_dumper.h" - -#include <asm/ptrace.h> -#include <assert.h> -#include <elf.h> -#include <stdio.h> -#include <string.h> -#include <sys/procfs.h> -#if defined(__mips__) && defined(__ANDROID__) -// To get register definitions. -#include <asm/reg.h> -#endif - -#include "common/linux/linux_libc_support.h" - -namespace google_breakpad { - -LinuxCoreDumper::LinuxCoreDumper(pid_t pid, - const char* core_path, - const char* procfs_path, - const char* root_prefix) - : LinuxDumper(pid, root_prefix), - core_path_(core_path), - procfs_path_(procfs_path), - thread_infos_(&allocator_, 8) { - assert(core_path_); -} - -bool LinuxCoreDumper::BuildProcPath(char* path, pid_t pid, - const char* node) const { - if (!path || !node) - return false; - - size_t node_len = my_strlen(node); - if (node_len == 0) - return false; - - size_t procfs_path_len = my_strlen(procfs_path_); - size_t total_length = procfs_path_len + 1 + node_len; - if (total_length >= NAME_MAX) - return false; - - memcpy(path, procfs_path_, procfs_path_len); - path[procfs_path_len] = '/'; - memcpy(path + procfs_path_len + 1, node, node_len); - path[total_length] = '\0'; - return true; -} - -bool LinuxCoreDumper::CopyFromProcess(void* dest, pid_t child, - const void* src, size_t length) { - ElfCoreDump::Addr virtual_address = reinterpret_cast<ElfCoreDump::Addr>(src); - // TODO(benchan): Investigate whether the data to be copied could span - // across multiple segments in the core dump file. ElfCoreDump::CopyData - // and this method do not handle that case yet. - if (!core_.CopyData(dest, virtual_address, length)) { - // If the data segment is not found in the core dump, fill the result - // with marker characters. - memset(dest, 0xab, length); - return false; - } - return true; -} - -bool LinuxCoreDumper::GetThreadInfoByIndex(size_t index, ThreadInfo* info) { - if (index >= thread_infos_.size()) - return false; - - *info = thread_infos_[index]; - const uint8_t* stack_pointer; -#if defined(__i386) - 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.ARM_sp, sizeof(info->regs.ARM_sp)); -#elif defined(__aarch64__) - memcpy(&stack_pointer, &info->regs.sp, sizeof(info->regs.sp)); -#elif defined(__mips__) - stack_pointer = - reinterpret_cast<uint8_t*>(info->mcontext.gregs[MD_CONTEXT_MIPS_REG_SP]); -#else -#error "This code hasn't been ported to your platform yet." -#endif - info->stack_pointer = reinterpret_cast<uintptr_t>(stack_pointer); - return true; -} - -bool LinuxCoreDumper::IsPostMortem() const { - return true; -} - -bool LinuxCoreDumper::ThreadsSuspend() { - return true; -} - -bool LinuxCoreDumper::ThreadsResume() { - return true; -} - -bool LinuxCoreDumper::EnumerateThreads() { - if (!mapped_core_file_.Map(core_path_, 0)) { - fprintf(stderr, "Could not map core dump file into memory\n"); - return false; - } - - core_.SetContent(mapped_core_file_.content()); - if (!core_.IsValid()) { - fprintf(stderr, "Invalid core dump file\n"); - return false; - } - - ElfCoreDump::Note note = core_.GetFirstNote(); - if (!note.IsValid()) { - fprintf(stderr, "PT_NOTE section not found\n"); - return false; - } - - bool first_thread = true; - do { - ElfCoreDump::Word type = note.GetType(); - MemoryRange name = note.GetName(); - MemoryRange description = note.GetDescription(); - - if (type == 0 || name.IsEmpty() || description.IsEmpty()) { - fprintf(stderr, "Could not found a valid PT_NOTE.\n"); - return false; - } - - // Based on write_note_info() in linux/kernel/fs/binfmt_elf.c, notes are - // ordered as follows (NT_PRXFPREG and NT_386_TLS are i386 specific): - // Thread Name Type - // ------------------------------------------------------------------- - // 1st thread CORE NT_PRSTATUS - // process-wide CORE NT_PRPSINFO - // process-wide CORE NT_AUXV - // 1st thread CORE NT_FPREGSET - // 1st thread LINUX NT_PRXFPREG - // 1st thread LINUX NT_386_TLS - // - // 2nd thread CORE NT_PRSTATUS - // 2nd thread CORE NT_FPREGSET - // 2nd thread LINUX NT_PRXFPREG - // 2nd thread LINUX NT_386_TLS - // - // 3rd thread CORE NT_PRSTATUS - // 3rd thread CORE NT_FPREGSET - // 3rd thread LINUX NT_PRXFPREG - // 3rd thread LINUX NT_386_TLS - // - // The following code only works if notes are ordered as expected. - switch (type) { - case NT_PRSTATUS: { - if (description.length() != sizeof(elf_prstatus)) { - fprintf(stderr, "Found NT_PRSTATUS descriptor of unexpected size\n"); - return false; - } - - const elf_prstatus* status = - reinterpret_cast<const elf_prstatus*>(description.data()); - pid_t pid = status->pr_pid; - ThreadInfo info; - memset(&info, 0, sizeof(ThreadInfo)); - info.tgid = status->pr_pgrp; - info.ppid = status->pr_ppid; -#if defined(__mips__) -#if defined(__ANDROID__) - for (int i = EF_R0; i <= EF_R31; i++) - info.mcontext.gregs[i - EF_R0] = status->pr_reg[i]; -#else // __ANDROID__ - for (int i = EF_REG0; i <= EF_REG31; i++) - info.mcontext.gregs[i - EF_REG0] = status->pr_reg[i]; -#endif // __ANDROID__ - info.mcontext.mdlo = status->pr_reg[EF_LO]; - info.mcontext.mdhi = status->pr_reg[EF_HI]; - info.mcontext.pc = status->pr_reg[EF_CP0_EPC]; -#else // __mips__ - memcpy(&info.regs, status->pr_reg, sizeof(info.regs)); -#endif // __mips__ - if (first_thread) { - crash_thread_ = pid; - crash_signal_ = status->pr_info.si_signo; - } - first_thread = false; - threads_.push_back(pid); - thread_infos_.push_back(info); - break; - } -#if defined(__i386) || defined(__x86_64) - case NT_FPREGSET: { - if (thread_infos_.empty()) - return false; - - ThreadInfo* info = &thread_infos_.back(); - if (description.length() != sizeof(info->fpregs)) { - fprintf(stderr, "Found NT_FPREGSET descriptor of unexpected size\n"); - return false; - } - - memcpy(&info->fpregs, description.data(), sizeof(info->fpregs)); - break; - } -#endif -#if defined(__i386) - case NT_PRXFPREG: { - if (thread_infos_.empty()) - return false; - - ThreadInfo* info = &thread_infos_.back(); - if (description.length() != sizeof(info->fpxregs)) { - fprintf(stderr, "Found NT_PRXFPREG descriptor of unexpected size\n"); - return false; - } - - memcpy(&info->fpxregs, description.data(), sizeof(info->fpxregs)); - break; - } -#endif - } - note = note.GetNextNote(); - } while (note.IsValid()); - - return true; -} - -} // namespace google_breakpad diff --git a/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/linux_core_dumper.h b/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/linux_core_dumper.h deleted file mode 100644 index 8a7c924b6..000000000 --- a/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/linux_core_dumper.h +++ /dev/null @@ -1,125 +0,0 @@ -// Copyright (c) 2012, 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. - -// linux_core_dumper.h: Define the google_breakpad::LinuxCoreDumper -// class, which is derived from google_breakpad::LinuxDumper to extract -// information from a crashed process via its core dump and proc files. - -#ifndef CLIENT_LINUX_MINIDUMP_WRITER_LINUX_CORE_DUMPER_H_ -#define CLIENT_LINUX_MINIDUMP_WRITER_LINUX_CORE_DUMPER_H_ - -#include "client/linux/minidump_writer/linux_dumper.h" -#include "common/linux/elf_core_dump.h" -#include "common/linux/memory_mapped_file.h" - -namespace google_breakpad { - -class LinuxCoreDumper : public LinuxDumper { - public: - // Constructs a dumper for extracting information of a given process - // with a process ID of |pid| via its core dump file at |core_path| and - // its proc files at |procfs_path|. If |procfs_path| is a copy of - // /proc/<pid>, it should contain the following files: - // auxv, cmdline, environ, exe, maps, status - // See LinuxDumper for the purpose of |root_prefix|. - LinuxCoreDumper(pid_t pid, const char* core_path, const char* procfs_path, - const char* root_prefix = ""); - - // Implements LinuxDumper::BuildProcPath(). - // Builds a proc path for a certain pid for a node (/proc/<pid>/<node>). - // |path| is a character array of at least NAME_MAX bytes to return the - // result.|node| is the final node without any slashes. Return true on - // success. - // - // As this dumper performs a post-mortem dump and makes use of a copy - // of the proc files of the crashed process, this derived method does - // not actually make use of |pid| and always returns a subpath of - // |procfs_path_| regardless of whether |pid| corresponds to the main - // process or a thread of the process, i.e. assuming both the main process - // and its threads have the following proc files with the same content: - // auxv, cmdline, environ, exe, maps, status - virtual bool BuildProcPath(char* path, pid_t pid, const char* node) const; - - // Implements LinuxDumper::CopyFromProcess(). - // Copies content of |length| bytes from a given process |child|, - // starting from |src|, into |dest|. This method extracts the content - // the core dump and fills |dest| with a sequence of marker bytes - // if the expected data is not found in the core dump. Returns true if - // the expected data is found in the core dump. - virtual bool CopyFromProcess(void* dest, pid_t child, const void* src, - size_t length); - - // Implements LinuxDumper::GetThreadInfoByIndex(). - // Reads information about the |index|-th thread of |threads_|. - // Returns true on success. One must have called |ThreadsSuspend| first. - virtual bool GetThreadInfoByIndex(size_t index, ThreadInfo* info); - - // Implements LinuxDumper::IsPostMortem(). - // Always returns true to indicate that this dumper performs a - // post-mortem dump of a crashed process via a core dump file. - virtual bool IsPostMortem() const; - - // Implements LinuxDumper::ThreadsSuspend(). - // As the dumper performs a post-mortem dump via a core dump file, - // there is no threads to suspend. This method does nothing and - // always returns true. - virtual bool ThreadsSuspend(); - - // Implements LinuxDumper::ThreadsResume(). - // As the dumper performs a post-mortem dump via a core dump file, - // there is no threads to resume. This method does nothing and - // always returns true. - virtual bool ThreadsResume(); - - protected: - // Implements LinuxDumper::EnumerateThreads(). - // Enumerates all threads of the given process into |threads_|. - virtual bool EnumerateThreads(); - - private: - // Path of the core dump file. - const char* core_path_; - - // Path of the directory containing the proc files of the given process, - // which is usually a copy of /proc/<pid>. - const char* procfs_path_; - - // Memory-mapped core dump file at |core_path_|. - MemoryMappedFile mapped_core_file_; - - // Content of the core dump file. - ElfCoreDump core_; - - // Thread info found in the core dump file. - wasteful_vector<ThreadInfo> thread_infos_; -}; - -} // namespace google_breakpad - -#endif // CLIENT_LINUX_HANDLER_LINUX_CORE_DUMPER_H_ diff --git a/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/linux_core_dumper_unittest.cc b/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/linux_core_dumper_unittest.cc deleted file mode 100644 index ae0c965b3..000000000 --- a/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/linux_core_dumper_unittest.cc +++ /dev/null @@ -1,128 +0,0 @@ -// Copyright (c) 2012, 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. - -// linux_core_dumper_unittest.cc: -// Unit tests for google_breakpad::LinuxCoreDumoer. - -#include <string> - -#include "breakpad_googletest_includes.h" -#include "client/linux/minidump_writer/linux_core_dumper.h" -#include "common/linux/tests/crash_generator.h" -#include "common/using_std_string.h" - -using namespace google_breakpad; - -TEST(LinuxCoreDumperTest, GetMappingAbsolutePath) { - const LinuxCoreDumper dumper(getpid(), "core", "/tmp", "/mnt/root"); - const MappingInfo mapping = { 0, 0, 0, false, "/usr/lib/libc.so" }; - - char path[PATH_MAX]; - dumper.GetMappingAbsolutePath(mapping, path); - - EXPECT_STREQ("/mnt/root/usr/lib/libc.so", path); -} - -TEST(LinuxCoreDumperTest, BuildProcPath) { - const pid_t pid = getpid(); - const char procfs_path[] = "/procfs_copy"; - LinuxCoreDumper dumper(getpid(), "core_file", procfs_path); - - char maps_path[NAME_MAX] = ""; - char maps_path_expected[NAME_MAX]; - snprintf(maps_path_expected, sizeof(maps_path_expected), - "%s/maps", procfs_path); - EXPECT_TRUE(dumper.BuildProcPath(maps_path, pid, "maps")); - EXPECT_STREQ(maps_path_expected, maps_path); - - EXPECT_FALSE(dumper.BuildProcPath(NULL, pid, "maps")); - EXPECT_FALSE(dumper.BuildProcPath(maps_path, pid, "")); - EXPECT_FALSE(dumper.BuildProcPath(maps_path, pid, NULL)); - - char long_node[NAME_MAX]; - size_t long_node_len = NAME_MAX - strlen(procfs_path) - 1; - memset(long_node, 'a', long_node_len); - long_node[long_node_len] = '\0'; - EXPECT_FALSE(dumper.BuildProcPath(maps_path, pid, long_node)); -} - -TEST(LinuxCoreDumperTest, VerifyDumpWithMultipleThreads) { - CrashGenerator crash_generator; - if (!crash_generator.HasDefaultCorePattern()) { - fprintf(stderr, "LinuxCoreDumperTest.VerifyDumpWithMultipleThreads test " - "is skipped due to non-default core pattern\n"); - return; - } - - const unsigned kNumOfThreads = 3; - const unsigned kCrashThread = 1; - const int kCrashSignal = SIGABRT; - pid_t child_pid; - ASSERT_TRUE(crash_generator.CreateChildCrash(kNumOfThreads, kCrashThread, - kCrashSignal, &child_pid)); - - const string core_file = crash_generator.GetCoreFilePath(); - const string procfs_path = crash_generator.GetDirectoryOfProcFilesCopy(); - -#if defined(__ANDROID__) - struct stat st; - if (stat(core_file.c_str(), &st) != 0) { - fprintf(stderr, "LinuxCoreDumperTest.VerifyDumpWithMultipleThreads test is " - "skipped due to no core file being generated"); - return; - } -#endif - - LinuxCoreDumper dumper(child_pid, core_file.c_str(), procfs_path.c_str()); - - EXPECT_TRUE(dumper.Init()); - - EXPECT_TRUE(dumper.IsPostMortem()); - - // These are no-ops and should always return true. - EXPECT_TRUE(dumper.ThreadsSuspend()); - EXPECT_TRUE(dumper.ThreadsResume()); - - // LinuxCoreDumper cannot determine the crash address and thus it always - // sets the crash address to 0. - EXPECT_EQ(0U, dumper.crash_address()); - EXPECT_EQ(kCrashSignal, dumper.crash_signal()); - EXPECT_EQ(crash_generator.GetThreadId(kCrashThread), - dumper.crash_thread()); - - EXPECT_EQ(kNumOfThreads, dumper.threads().size()); - for (unsigned i = 0; i < kNumOfThreads; ++i) { - ThreadInfo info; - EXPECT_TRUE(dumper.GetThreadInfoByIndex(i, &info)); - const void* stack; - size_t stack_len; - EXPECT_TRUE(dumper.GetStackInfo(&stack, &stack_len, info.stack_pointer)); - EXPECT_EQ(getpid(), info.ppid); - } -} diff --git a/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/linux_dumper.cc b/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/linux_dumper.cc deleted file mode 100644 index bdbdc6507..000000000 --- a/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/linux_dumper.cc +++ /dev/null @@ -1,776 +0,0 @@ -// 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. - -// linux_dumper.cc: Implement google_breakpad::LinuxDumper. -// See linux_dumper.h for details. - -// This code deals with the mechanics of getting information about a crashed -// process. Since this code may run in a compromised address space, the same -// rules apply as detailed at the top of minidump_writer.h: no libc calls and -// use the alternative allocator. - -#include "client/linux/minidump_writer/linux_dumper.h" - -#include <assert.h> -#include <elf.h> -#include <fcntl.h> -#include <limits.h> -#include <stddef.h> -#include <string.h> - -#include "client/linux/minidump_writer/line_reader.h" -#include "common/linux/elfutils.h" -#include "common/linux/file_id.h" -#include "common/linux/linux_libc_support.h" -#include "common/linux/memory_mapped_file.h" -#include "common/linux/safe_readlink.h" -#include "third_party/lss/linux_syscall_support.h" - -#if defined(__ANDROID__) - -// Android packed relocations definitions are not yet available from the -// NDK header files, so we have to provide them manually here. -#ifndef DT_LOOS -#define DT_LOOS 0x6000000d -#endif -#ifndef DT_ANDROID_REL -static const int DT_ANDROID_REL = DT_LOOS + 2; -#endif -#ifndef DT_ANDROID_RELA -static const int DT_ANDROID_RELA = DT_LOOS + 4; -#endif - -#endif // __ANDROID __ - -static const char kMappedFileUnsafePrefix[] = "/dev/"; -static const char kDeletedSuffix[] = " (deleted)"; -static const char kReservedFlags[] = " ---p"; - -inline static bool IsMappedFileOpenUnsafe( - const google_breakpad::MappingInfo& mapping) { - // It is unsafe to attempt to open a mapped file that lives under /dev, - // because the semantics of the open may be driver-specific so we'd risk - // hanging the crash dumper. And a file in /dev/ almost certainly has no - // ELF file identifier anyways. - return my_strncmp(mapping.name, - kMappedFileUnsafePrefix, - sizeof(kMappedFileUnsafePrefix) - 1) == 0; -} - -namespace google_breakpad { - -#if defined(__CHROMEOS__) - -namespace { - -// Recover memory mappings before writing dump on ChromeOS -// -// On Linux, breakpad relies on /proc/[pid]/maps to associate symbols from -// addresses. ChromeOS' hugepage implementation replaces some segments with -// anonymous private pages, which is a restriction of current implementation -// in Linux kernel at the time of writing. Thus, breakpad can no longer -// symbolize addresses from those text segments replaced with hugepages. -// -// This postprocess tries to recover the mappings. Because hugepages are always -// inserted in between some .text sections, it tries to infer the names and -// offsets of the segments, by looking at segments immediately precede and -// succeed them. -// -// For example, a text segment before hugepage optimization -// 02001000-03002000 r-xp /opt/google/chrome/chrome -// -// can be broken into -// 02001000-02200000 r-xp /opt/google/chrome/chrome -// 02200000-03000000 r-xp -// 03000000-03002000 r-xp /opt/google/chrome/chrome -// -// For more details, see: -// crbug.com/628040 ChromeOS' use of hugepages confuses crash symbolization - -// Copied from CrOS' hugepage implementation, which is unlikely to change. -// The hugepage size is 2M. -const unsigned int kHpageShift = 21; -const size_t kHpageSize = (1 << kHpageShift); -const size_t kHpageMask = (~(kHpageSize - 1)); - -// Find and merge anonymous r-xp segments with surrounding named segments. -// There are two cases: - -// Case 1: curr, next -// curr is anonymous -// curr is r-xp -// curr.size >= 2M -// curr.size is a multiple of 2M. -// next is backed by some file. -// curr and next are contiguous. -// offset(next) == sizeof(curr) -void TryRecoverMappings(MappingInfo *curr, MappingInfo *next) { - // Merged segments are marked with size = 0. - if (curr->size == 0 || next->size == 0) - return; - - if (curr->size >= kHpageSize && - curr->exec && - (curr->size & kHpageMask) == curr->size && - (curr->start_addr & kHpageMask) == curr->start_addr && - curr->name[0] == '\0' && - next->name[0] != '\0' && - curr->start_addr + curr->size == next->start_addr && - curr->size == next->offset) { - - // matched - my_strlcpy(curr->name, next->name, NAME_MAX); - if (next->exec) { - // (curr, next) - curr->size += next->size; - next->size = 0; - } - } -} - -// Case 2: prev, curr, next -// curr is anonymous -// curr is r-xp -// curr.size >= 2M -// curr.size is a multiple of 2M. -// next and prev are backed by the same file. -// prev, curr and next are contiguous. -// offset(next) == offset(prev) + sizeof(prev) + sizeof(curr) -void TryRecoverMappings(MappingInfo *prev, MappingInfo *curr, - MappingInfo *next) { - // Merged segments are marked with size = 0. - if (prev->size == 0 || curr->size == 0 || next->size == 0) - return; - - if (curr->size >= kHpageSize && - curr->exec && - (curr->size & kHpageMask) == curr->size && - (curr->start_addr & kHpageMask) == curr->start_addr && - curr->name[0] == '\0' && - next->name[0] != '\0' && - curr->start_addr + curr->size == next->start_addr && - prev->start_addr + prev->size == curr->start_addr && - my_strncmp(prev->name, next->name, NAME_MAX) == 0 && - next->offset == prev->offset + prev->size + curr->size) { - - // matched - my_strlcpy(curr->name, prev->name, NAME_MAX); - if (prev->exec) { - curr->offset = prev->offset; - curr->start_addr = prev->start_addr; - if (next->exec) { - // (prev, curr, next) - curr->size += prev->size + next->size; - prev->size = 0; - next->size = 0; - } else { - // (prev, curr), next - curr->size += prev->size; - prev->size = 0; - } - } else { - curr->offset = prev->offset + prev->size; - if (next->exec) { - // prev, (curr, next) - curr->size += next->size; - next->size = 0; - } else { - // prev, curr, next - } - } - } -} - -// mappings_ is sorted excepted for the first entry. -// This function tries to merge segemnts into the first entry, -// then check for other sorted entries. -// See LinuxDumper::EnumerateMappings(). -void CrOSPostProcessMappings(wasteful_vector<MappingInfo*>& mappings) { - // Find the candidate "next" to first segment, which is the only one that - // could be out-of-order. - size_t l = 1; - size_t r = mappings.size(); - size_t next = mappings.size(); - while (l < r) { - int m = (l + r) / 2; - if (mappings[m]->start_addr > mappings[0]->start_addr) - r = next = m; - else - l = m + 1; - } - - // Try to merge segments into the first. - if (next < mappings.size()) { - TryRecoverMappings(mappings[0], mappings[next]); - if (next - 1 > 0) - TryRecoverMappings(mappings[next - 1], mappings[0], mappings[next]); - } - - // Iterate through normal, sorted cases. - // Normal case 1. - for (size_t i = 1; i < mappings.size() - 1; i++) - TryRecoverMappings(mappings[i], mappings[i + 1]); - - // Normal case 2. - for (size_t i = 1; i < mappings.size() - 2; i++) - TryRecoverMappings(mappings[i], mappings[i + 1], mappings[i + 2]); - - // Collect merged (size == 0) segments. - size_t f, e; - for (f = e = 0; e < mappings.size(); e++) - if (mappings[e]->size > 0) - mappings[f++] = mappings[e]; - mappings.resize(f); -} - -} // namespace -#endif // __CHROMEOS__ - -// All interesting auvx entry types are below AT_SYSINFO_EHDR -#define AT_MAX AT_SYSINFO_EHDR - -LinuxDumper::LinuxDumper(pid_t pid, const char* root_prefix) - : pid_(pid), - root_prefix_(root_prefix), - crash_address_(0), - crash_signal_(0), - crash_thread_(pid), - threads_(&allocator_, 8), - mappings_(&allocator_), - auxv_(&allocator_, AT_MAX + 1) { - assert(root_prefix_ && my_strlen(root_prefix_) < PATH_MAX); - // The passed-in size to the constructor (above) is only a hint. - // Must call .resize() to do actual initialization of the elements. - auxv_.resize(AT_MAX + 1); -} - -LinuxDumper::~LinuxDumper() { -} - -bool LinuxDumper::Init() { - return ReadAuxv() && EnumerateThreads() && EnumerateMappings(); -} - -bool LinuxDumper::LateInit() { -#if defined(__ANDROID__) - LatePostprocessMappings(); -#endif - -#if defined(__CHROMEOS__) - CrOSPostProcessMappings(mappings_); -#endif - - return true; -} - -bool -LinuxDumper::ElfFileIdentifierForMapping(const MappingInfo& mapping, - bool member, - unsigned int mapping_id, - wasteful_vector<uint8_t>& identifier) { - assert(!member || mapping_id < mappings_.size()); - if (IsMappedFileOpenUnsafe(mapping)) - return false; - - // Special-case linux-gate because it's not a real file. - if (my_strcmp(mapping.name, kLinuxGateLibraryName) == 0) { - void* linux_gate = NULL; - if (pid_ == sys_getpid()) { - linux_gate = reinterpret_cast<void*>(mapping.start_addr); - } else { - linux_gate = allocator_.Alloc(mapping.size); - CopyFromProcess(linux_gate, pid_, - reinterpret_cast<const void*>(mapping.start_addr), - mapping.size); - } - return FileID::ElfFileIdentifierFromMappedFile(linux_gate, identifier); - } - - char filename[PATH_MAX]; - if (!GetMappingAbsolutePath(mapping, filename)) - return false; - bool filename_modified = HandleDeletedFileInMapping(filename); - - MemoryMappedFile mapped_file(filename, mapping.offset); - if (!mapped_file.data() || mapped_file.size() < SELFMAG) - return false; - - bool success = - FileID::ElfFileIdentifierFromMappedFile(mapped_file.data(), identifier); - if (success && member && filename_modified) { - mappings_[mapping_id]->name[my_strlen(mapping.name) - - sizeof(kDeletedSuffix) + 1] = '\0'; - } - - return success; -} - -bool LinuxDumper::GetMappingAbsolutePath(const MappingInfo& mapping, - char path[PATH_MAX]) const { - return my_strlcpy(path, root_prefix_, PATH_MAX) < PATH_MAX && - my_strlcat(path, mapping.name, PATH_MAX) < PATH_MAX; -} - -namespace { -bool ElfFileSoNameFromMappedFile( - const void* elf_base, char* soname, size_t soname_size) { - if (!IsValidElf(elf_base)) { - // Not ELF - return false; - } - - const void* segment_start; - size_t segment_size; - int elf_class; - if (!FindElfSection(elf_base, ".dynamic", SHT_DYNAMIC, - &segment_start, &segment_size, &elf_class)) { - // No dynamic section - return false; - } - - const void* dynstr_start; - size_t dynstr_size; - if (!FindElfSection(elf_base, ".dynstr", SHT_STRTAB, - &dynstr_start, &dynstr_size, &elf_class)) { - // No dynstr section - return false; - } - - const ElfW(Dyn)* dynamic = static_cast<const ElfW(Dyn)*>(segment_start); - size_t dcount = segment_size / sizeof(ElfW(Dyn)); - for (const ElfW(Dyn)* dyn = dynamic; dyn < dynamic + dcount; ++dyn) { - if (dyn->d_tag == DT_SONAME) { - const char* dynstr = static_cast<const char*>(dynstr_start); - if (dyn->d_un.d_val >= dynstr_size) { - // Beyond the end of the dynstr section - return false; - } - const char* str = dynstr + dyn->d_un.d_val; - const size_t maxsize = dynstr_size - dyn->d_un.d_val; - my_strlcpy(soname, str, maxsize < soname_size ? maxsize : soname_size); - return true; - } - } - - // Did not find SONAME - return false; -} - -// Find the shared object name (SONAME) by examining the ELF information -// for |mapping|. If the SONAME is found copy it into the passed buffer -// |soname| and return true. The size of the buffer is |soname_size|. -// The SONAME will be truncated if it is too long to fit in the buffer. -bool ElfFileSoName(const LinuxDumper& dumper, - const MappingInfo& mapping, char* soname, size_t soname_size) { - if (IsMappedFileOpenUnsafe(mapping)) { - // Not safe - return false; - } - - char filename[PATH_MAX]; - if (!dumper.GetMappingAbsolutePath(mapping, filename)) - return false; - - MemoryMappedFile mapped_file(filename, mapping.offset); - if (!mapped_file.data() || mapped_file.size() < SELFMAG) { - // mmap failed - return false; - } - - return ElfFileSoNameFromMappedFile(mapped_file.data(), soname, soname_size); -} - -} // namespace - - -void LinuxDumper::GetMappingEffectiveNameAndPath(const MappingInfo& mapping, - char* file_path, - size_t file_path_size, - char* file_name, - size_t file_name_size) { - my_strlcpy(file_path, mapping.name, file_path_size); - - // If an executable is mapped from a non-zero offset, this is likely because - // the executable was loaded directly from inside an archive file (e.g., an - // apk on Android). We try to find the name of the shared object (SONAME) by - // looking in the file for ELF sections. - bool mapped_from_archive = false; - if (mapping.exec && mapping.offset != 0) { - mapped_from_archive = - ElfFileSoName(*this, mapping, file_name, file_name_size); - } - - if (mapped_from_archive) { - // Some tools (e.g., stackwalk) extract the basename from the pathname. In - // this case, we append the file_name to the mapped archive path as follows: - // file_name := libname.so - // file_path := /path/to/ARCHIVE.APK/libname.so - if (my_strlen(file_path) + 1 + my_strlen(file_name) < file_path_size) { - my_strlcat(file_path, "/", file_path_size); - my_strlcat(file_path, file_name, file_path_size); - } - } else { - // Common case: - // file_path := /path/to/libname.so - // file_name := libname.so - const char* basename = my_strrchr(file_path, '/'); - basename = basename == NULL ? file_path : (basename + 1); - my_strlcpy(file_name, basename, file_name_size); - } -} - -bool LinuxDumper::ReadAuxv() { - char auxv_path[NAME_MAX]; - if (!BuildProcPath(auxv_path, pid_, "auxv")) { - return false; - } - - int fd = sys_open(auxv_path, O_RDONLY, 0); - if (fd < 0) { - return false; - } - - elf_aux_entry one_aux_entry; - bool res = false; - while (sys_read(fd, - &one_aux_entry, - sizeof(elf_aux_entry)) == sizeof(elf_aux_entry) && - one_aux_entry.a_type != AT_NULL) { - if (one_aux_entry.a_type <= AT_MAX) { - auxv_[one_aux_entry.a_type] = one_aux_entry.a_un.a_val; - res = true; - } - } - sys_close(fd); - return res; -} - -bool LinuxDumper::EnumerateMappings() { - char maps_path[NAME_MAX]; - if (!BuildProcPath(maps_path, pid_, "maps")) - return false; - - // linux_gate_loc is the beginning of the kernel's mapping of - // linux-gate.so in the process. It doesn't actually show up in the - // maps list as a filename, but it can be found using the AT_SYSINFO_EHDR - // aux vector entry, which gives the information necessary to special - // case its entry when creating the list of mappings. - // See http://www.trilithium.com/johan/2005/08/linux-gate/ for more - // information. - const void* linux_gate_loc = - reinterpret_cast<void *>(auxv_[AT_SYSINFO_EHDR]); - // Although the initial executable is usually the first mapping, it's not - // guaranteed (see http://crosbug.com/25355); therefore, try to use the - // actual entry point to find the mapping. - const void* entry_point_loc = reinterpret_cast<void *>(auxv_[AT_ENTRY]); - - const int fd = sys_open(maps_path, O_RDONLY, 0); - if (fd < 0) - return false; - LineReader* const line_reader = new(allocator_) LineReader(fd); - - const char* line; - unsigned line_len; - while (line_reader->GetNextLine(&line, &line_len)) { - uintptr_t start_addr, end_addr, offset; - - const char* i1 = my_read_hex_ptr(&start_addr, line); - if (*i1 == '-') { - const char* i2 = my_read_hex_ptr(&end_addr, i1 + 1); - if (*i2 == ' ') { - bool exec = (*(i2 + 3) == 'x'); - const char* i3 = my_read_hex_ptr(&offset, i2 + 6 /* skip ' rwxp ' */); - if (*i3 == ' ') { - const char* name = NULL; - // Only copy name if the name is a valid path name, or if - // it's the VDSO image. - if (((name = my_strchr(line, '/')) == NULL) && - linux_gate_loc && - reinterpret_cast<void*>(start_addr) == linux_gate_loc) { - name = kLinuxGateLibraryName; - offset = 0; - } - // Merge adjacent mappings with the same name into one module, - // assuming they're a single library mapped by the dynamic linker - if (name && !mappings_.empty()) { - MappingInfo* module = mappings_.back(); - if ((start_addr == module->start_addr + module->size) && - (my_strlen(name) == my_strlen(module->name)) && - (my_strncmp(name, module->name, my_strlen(name)) == 0)) { - module->size = end_addr - module->start_addr; - line_reader->PopLine(line_len); - continue; - } - } - // Also merge mappings that result from address ranges that the - // linker reserved but which a loaded library did not use. These - // appear as an anonymous private mapping with no access flags set - // and which directly follow an executable mapping. - if (!name && !mappings_.empty()) { - MappingInfo* module = mappings_.back(); - if ((start_addr == module->start_addr + module->size) && - module->exec && - module->name[0] == '/' && - offset == 0 && my_strncmp(i2, - kReservedFlags, - sizeof(kReservedFlags) - 1) == 0) { - module->size = end_addr - module->start_addr; - line_reader->PopLine(line_len); - continue; - } - } - MappingInfo* const module = new(allocator_) MappingInfo; - my_memset(module, 0, sizeof(MappingInfo)); - module->start_addr = start_addr; - module->size = end_addr - start_addr; - module->offset = offset; - module->exec = exec; - if (name != NULL) { - const unsigned l = my_strlen(name); - if (l < sizeof(module->name)) - my_memcpy(module->name, name, l); - } - // If this is the entry-point mapping, and it's not already the - // first one, then we need to make it be first. This is because - // the minidump format assumes the first module is the one that - // corresponds to the main executable (as codified in - // processor/minidump.cc:MinidumpModuleList::GetMainModule()). - if (entry_point_loc && - (entry_point_loc >= - reinterpret_cast<void*>(module->start_addr)) && - (entry_point_loc < - reinterpret_cast<void*>(module->start_addr+module->size)) && - !mappings_.empty()) { - // push the module onto the front of the list. - mappings_.resize(mappings_.size() + 1); - for (size_t idx = mappings_.size() - 1; idx > 0; idx--) - mappings_[idx] = mappings_[idx - 1]; - mappings_[0] = module; - } else { - mappings_.push_back(module); - } - } - } - } - line_reader->PopLine(line_len); - } - - sys_close(fd); - - return !mappings_.empty(); -} - -#if defined(__ANDROID__) - -bool LinuxDumper::GetLoadedElfHeader(uintptr_t start_addr, ElfW(Ehdr)* ehdr) { - CopyFromProcess(ehdr, pid_, - reinterpret_cast<const void*>(start_addr), - sizeof(*ehdr)); - return my_memcmp(&ehdr->e_ident, ELFMAG, SELFMAG) == 0; -} - -void LinuxDumper::ParseLoadedElfProgramHeaders(ElfW(Ehdr)* ehdr, - uintptr_t start_addr, - uintptr_t* min_vaddr_ptr, - uintptr_t* dyn_vaddr_ptr, - size_t* dyn_count_ptr) { - uintptr_t phdr_addr = start_addr + ehdr->e_phoff; - - const uintptr_t max_addr = UINTPTR_MAX; - uintptr_t min_vaddr = max_addr; - uintptr_t dyn_vaddr = 0; - size_t dyn_count = 0; - - for (size_t i = 0; i < ehdr->e_phnum; ++i) { - ElfW(Phdr) phdr; - CopyFromProcess(&phdr, pid_, - reinterpret_cast<const void*>(phdr_addr), - sizeof(phdr)); - if (phdr.p_type == PT_LOAD && phdr.p_vaddr < min_vaddr) { - min_vaddr = phdr.p_vaddr; - } - if (phdr.p_type == PT_DYNAMIC) { - dyn_vaddr = phdr.p_vaddr; - dyn_count = phdr.p_memsz / sizeof(ElfW(Dyn)); - } - phdr_addr += sizeof(phdr); - } - - *min_vaddr_ptr = min_vaddr; - *dyn_vaddr_ptr = dyn_vaddr; - *dyn_count_ptr = dyn_count; -} - -bool LinuxDumper::HasAndroidPackedRelocations(uintptr_t load_bias, - uintptr_t dyn_vaddr, - size_t dyn_count) { - uintptr_t dyn_addr = load_bias + dyn_vaddr; - for (size_t i = 0; i < dyn_count; ++i) { - ElfW(Dyn) dyn; - CopyFromProcess(&dyn, pid_, - reinterpret_cast<const void*>(dyn_addr), - sizeof(dyn)); - if (dyn.d_tag == DT_ANDROID_REL || dyn.d_tag == DT_ANDROID_RELA) { - return true; - } - dyn_addr += sizeof(dyn); - } - return false; -} - -uintptr_t LinuxDumper::GetEffectiveLoadBias(ElfW(Ehdr)* ehdr, - uintptr_t start_addr) { - uintptr_t min_vaddr = 0; - uintptr_t dyn_vaddr = 0; - size_t dyn_count = 0; - ParseLoadedElfProgramHeaders(ehdr, start_addr, - &min_vaddr, &dyn_vaddr, &dyn_count); - // If |min_vaddr| is non-zero and we find Android packed relocation tags, - // return the effective load bias. - if (min_vaddr != 0) { - const uintptr_t load_bias = start_addr - min_vaddr; - if (HasAndroidPackedRelocations(load_bias, dyn_vaddr, dyn_count)) { - return load_bias; - } - } - // Either |min_vaddr| is zero, or it is non-zero but we did not find the - // expected Android packed relocations tags. - return start_addr; -} - -void LinuxDumper::LatePostprocessMappings() { - for (size_t i = 0; i < mappings_.size(); ++i) { - // Only consider exec mappings that indicate a file path was mapped, and - // where the ELF header indicates a mapped shared library. - MappingInfo* mapping = mappings_[i]; - if (!(mapping->exec && mapping->name[0] == '/')) { - continue; - } - ElfW(Ehdr) ehdr; - if (!GetLoadedElfHeader(mapping->start_addr, &ehdr)) { - continue; - } - if (ehdr.e_type == ET_DYN) { - // Compute the effective load bias for this mapped library, and update - // the mapping to hold that rather than |start_addr|, at the same time - // adjusting |size| to account for the change in |start_addr|. Where - // the library does not contain Android packed relocations, - // GetEffectiveLoadBias() returns |start_addr| and the mapping entry - // is not changed. - const uintptr_t load_bias = GetEffectiveLoadBias(&ehdr, - mapping->start_addr); - mapping->size += mapping->start_addr - load_bias; - mapping->start_addr = load_bias; - } - } -} - -#endif // __ANDROID__ - -// Get information about the stack, given the stack pointer. We don't try to -// walk the stack since we might not have all the information needed to do -// 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) { - // Move the stack pointer to the bottom of the page that it's in. - const uintptr_t page_size = getpagesize(); - - uint8_t* const stack_pointer = - reinterpret_cast<uint8_t*>(int_stack_pointer & ~(page_size - 1)); - - // The number of bytes of stack which we try to capture. - static const ptrdiff_t kStackToCapture = 32 * 1024; - - const MappingInfo* mapping = FindMapping(stack_pointer); - if (!mapping) - return false; - const ptrdiff_t offset = stack_pointer - - reinterpret_cast<uint8_t*>(mapping->start_addr); - const ptrdiff_t distance_to_end = - static_cast<ptrdiff_t>(mapping->size) - offset; - *stack_len = distance_to_end > kStackToCapture ? - kStackToCapture : distance_to_end; - *stack = stack_pointer; - return true; -} - -// Find the mapping which the given memory address falls in. -const MappingInfo* LinuxDumper::FindMapping(const void* address) const { - const uintptr_t addr = (uintptr_t) address; - - for (size_t i = 0; i < mappings_.size(); ++i) { - const uintptr_t start = static_cast<uintptr_t>(mappings_[i]->start_addr); - if (addr >= start && addr - start < mappings_[i]->size) - return mappings_[i]; - } - - return NULL; -} - -bool LinuxDumper::HandleDeletedFileInMapping(char* path) const { - static const size_t kDeletedSuffixLen = sizeof(kDeletedSuffix) - 1; - - // Check for ' (deleted)' in |path|. - // |path| has to be at least as long as "/x (deleted)". - const size_t path_len = my_strlen(path); - if (path_len < kDeletedSuffixLen + 2) - return false; - if (my_strncmp(path + path_len - kDeletedSuffixLen, kDeletedSuffix, - kDeletedSuffixLen) != 0) { - return false; - } - - // Check |path| against the /proc/pid/exe 'symlink'. - char exe_link[NAME_MAX]; - if (!BuildProcPath(exe_link, pid_, "exe")) - return false; - MappingInfo new_mapping = {0}; - if (!SafeReadLink(exe_link, new_mapping.name)) - return false; - char new_path[PATH_MAX]; - if (!GetMappingAbsolutePath(new_mapping, new_path)) - return false; - if (my_strcmp(path, new_path) != 0) - return false; - - // Check to see if someone actually named their executable 'foo (deleted)'. - struct kernel_stat exe_stat; - struct kernel_stat new_path_stat; - if (sys_stat(exe_link, &exe_stat) == 0 && - sys_stat(new_path, &new_path_stat) == 0 && - exe_stat.st_dev == new_path_stat.st_dev && - exe_stat.st_ino == new_path_stat.st_ino) { - return false; - } - - my_memcpy(path, exe_link, NAME_MAX); - return true; -} - -} // namespace google_breakpad diff --git a/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/linux_dumper.h b/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/linux_dumper.h deleted file mode 100644 index c3c799267..000000000 --- a/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/linux_dumper.h +++ /dev/null @@ -1,265 +0,0 @@ -// 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. - -// linux_dumper.h: Define the google_breakpad::LinuxDumper class, which -// is a base class for extracting information of a crashed process. It -// was originally a complete implementation using the ptrace API, but -// has been refactored to allow derived implementations supporting both -// ptrace and core dump. A portion of the original implementation is now -// in google_breakpad::LinuxPtraceDumper (see linux_ptrace_dumper.h for -// details). - -#ifndef CLIENT_LINUX_MINIDUMP_WRITER_LINUX_DUMPER_H_ -#define CLIENT_LINUX_MINIDUMP_WRITER_LINUX_DUMPER_H_ - -#include <elf.h> -#if defined(__ANDROID__) -#include <link.h> -#endif -#include <linux/limits.h> -#include <stdint.h> -#include <sys/types.h> -#include <sys/user.h> - -#include "client/linux/dump_writer_common/mapping_info.h" -#include "client/linux/dump_writer_common/thread_info.h" -#include "common/linux/file_id.h" -#include "common/memory.h" -#include "google_breakpad/common/minidump_format.h" - -namespace google_breakpad { - -// Typedef for our parsing of the auxv variables in /proc/pid/auxv. -#if defined(__i386) || defined(__ARM_EABI__) || \ - (defined(__mips__) && _MIPS_SIM == _ABIO32) -typedef Elf32_auxv_t elf_aux_entry; -#elif defined(__x86_64) || defined(__aarch64__) || \ - (defined(__mips__) && _MIPS_SIM != _ABIO32) -typedef Elf64_auxv_t elf_aux_entry; -#endif - -typedef __typeof__(((elf_aux_entry*) 0)->a_un.a_val) elf_aux_val_t; - -// When we find the VDSO mapping in the process's address space, this -// is the name we use for it when writing it to the minidump. -// This should always be less than NAME_MAX! -const char kLinuxGateLibraryName[] = "linux-gate.so"; - -class LinuxDumper { - public: - // The |root_prefix| is prepended to mapping paths before opening them, which - // is useful if the crash originates from a chroot. - explicit LinuxDumper(pid_t pid, const char* root_prefix = ""); - - virtual ~LinuxDumper(); - - // Parse the data for |threads| and |mappings|. - virtual bool Init(); - - // Take any actions that could not be taken in Init(). LateInit() is - // called after all other caller's initialization is complete, and in - // particular after it has called ThreadsSuspend(), so that ptrace is - // available. - virtual bool LateInit(); - - // Return true if the dumper performs a post-mortem dump. - virtual bool IsPostMortem() const = 0; - - // Suspend/resume all threads in the given process. - virtual bool ThreadsSuspend() = 0; - virtual bool ThreadsResume() = 0; - - // Read information about the |index|-th thread of |threads_|. - // Returns true on success. One must have called |ThreadsSuspend| first. - virtual bool GetThreadInfoByIndex(size_t index, ThreadInfo* info) = 0; - - // These are only valid after a call to |Init|. - const wasteful_vector<pid_t> &threads() { return threads_; } - const wasteful_vector<MappingInfo*> &mappings() { return mappings_; } - const MappingInfo* FindMapping(const void* address) const; - const wasteful_vector<elf_aux_val_t>& auxv() { return auxv_; } - - // Find a block of memory to take as the stack given the top of stack pointer. - // stack: (output) the lowest address in the memory area - // stack_len: (output) the length of the memory area - // stack_top: the current top of the stack - bool GetStackInfo(const void** stack, size_t* stack_len, uintptr_t stack_top); - - PageAllocator* allocator() { return &allocator_; } - - // Copy content of |length| bytes from a given process |child|, - // starting from |src|, into |dest|. Returns true on success. - virtual bool CopyFromProcess(void* dest, pid_t child, const void* src, - size_t length) = 0; - - // Builds a proc path for a certain pid for a node (/proc/<pid>/<node>). - // |path| is a character array of at least NAME_MAX bytes to return the - // result.|node| is the final node without any slashes. Returns true on - // success. - virtual bool BuildProcPath(char* path, pid_t pid, const char* node) const = 0; - - // Generate a File ID from the .text section of a mapped entry. - // If not a member, mapping_id is ignored. This method can also manipulate the - // |mapping|.name to truncate "(deleted)" from the file name if necessary. - bool ElfFileIdentifierForMapping(const MappingInfo& mapping, - bool member, - unsigned int mapping_id, - wasteful_vector<uint8_t>& identifier); - - uintptr_t crash_address() const { return crash_address_; } - void set_crash_address(uintptr_t crash_address) { - crash_address_ = crash_address; - } - - int crash_signal() const { return crash_signal_; } - void set_crash_signal(int crash_signal) { crash_signal_ = crash_signal; } - - pid_t crash_thread() const { return crash_thread_; } - void set_crash_thread(pid_t crash_thread) { crash_thread_ = crash_thread; } - - // Concatenates the |root_prefix_| and |mapping| path. Writes into |path| and - // returns true unless the string is too long. - bool GetMappingAbsolutePath(const MappingInfo& mapping, - char path[PATH_MAX]) const; - - // Extracts the effective path and file name of from |mapping|. In most cases - // the effective name/path are just the mapping's path and basename. In some - // other cases, however, a library can be mapped from an archive (e.g., when - // loading .so libs from an apk on Android) and this method is able to - // reconstruct the original file name. - void GetMappingEffectiveNameAndPath(const MappingInfo& mapping, - char* file_path, - size_t file_path_size, - char* file_name, - size_t file_name_size); - - protected: - bool ReadAuxv(); - - virtual bool EnumerateMappings(); - - virtual bool EnumerateThreads() = 0; - - // For the case where a running program has been deleted, it'll show up in - // /proc/pid/maps as "/path/to/program (deleted)". If this is the case, then - // see if '/path/to/program (deleted)' matches /proc/pid/exe and return - // /proc/pid/exe in |path| so ELF identifier generation works correctly. This - // also checks to see if '/path/to/program (deleted)' exists, so it does not - // get fooled by a poorly named binary. - // For programs that don't end with ' (deleted)', this is a no-op. - // This assumes |path| is a buffer with length NAME_MAX. - // Returns true if |path| is modified. - bool HandleDeletedFileInMapping(char* path) const; - - // ID of the crashed process. - const pid_t pid_; - - // Path of the root directory to which mapping paths are relative. - const char* const root_prefix_; - - // Virtual address at which the process crashed. - uintptr_t crash_address_; - - // Signal that terminated the crashed process. - int crash_signal_; - - // ID of the crashed thread. - pid_t crash_thread_; - - mutable PageAllocator allocator_; - - // IDs of all the threads. - wasteful_vector<pid_t> threads_; - - // Info from /proc/<pid>/maps. - wasteful_vector<MappingInfo*> mappings_; - - // Info from /proc/<pid>/auxv - wasteful_vector<elf_aux_val_t> auxv_; - -#if defined(__ANDROID__) - private: - // Android M and later support packed ELF relocations in shared libraries. - // Packing relocations changes the vaddr of the LOAD segments, such that - // the effective load bias is no longer the same as the start address of - // the memory mapping containing the executable parts of the library. The - // packing is applied to the stripped library run on the target, but not to - // any other library, and in particular not to the library used to generate - // breakpad symbols. As a result, we need to adjust the |start_addr| for - // any mapping that results from a shared library that contains Android - // packed relocations, so that it properly represents the effective library - // load bias. The following functions support this adjustment. - - // Check that a given mapping at |start_addr| is for an ELF shared library. - // If it is, place the ELF header in |ehdr| and return true. - // The first LOAD segment in an ELF shared library has offset zero, so the - // ELF file header is at the start of this map entry, and in already mapped - // memory. - bool GetLoadedElfHeader(uintptr_t start_addr, ElfW(Ehdr)* ehdr); - - // For the ELF file mapped at |start_addr|, iterate ELF program headers to - // find the min vaddr of all program header LOAD segments, the vaddr for - // the DYNAMIC segment, and a count of DYNAMIC entries. Return values in - // |min_vaddr_ptr|, |dyn_vaddr_ptr|, and |dyn_count_ptr|. - // The program header table is also in already mapped memory. - void ParseLoadedElfProgramHeaders(ElfW(Ehdr)* ehdr, - uintptr_t start_addr, - uintptr_t* min_vaddr_ptr, - uintptr_t* dyn_vaddr_ptr, - size_t* dyn_count_ptr); - - // Search the DYNAMIC tags for the ELF file with the given |load_bias|, and - // return true if the tags indicate that the file contains Android packed - // relocations. Dynamic tags are found at |dyn_vaddr| past the |load_bias|. - bool HasAndroidPackedRelocations(uintptr_t load_bias, - uintptr_t dyn_vaddr, - size_t dyn_count); - - // If the ELF file mapped at |start_addr| contained Android packed - // relocations, return the load bias that the system linker (or Chromium - // crazy linker) will have used. If the file did not contain Android - // packed relocations, returns |start_addr|, indicating that no adjustment - // is necessary. - // The effective load bias is |start_addr| adjusted downwards by the - // min vaddr in the library LOAD segments. - uintptr_t GetEffectiveLoadBias(ElfW(Ehdr)* ehdr, uintptr_t start_addr); - - // Called from LateInit(). Iterates |mappings_| and rewrites the |start_addr| - // field of any that represent ELF shared libraries with Android packed - // relocations, so that |start_addr| is the load bias that the system linker - // (or Chromium crazy linker) used. This value matches the addresses produced - // when the non-relocation-packed library is used for breakpad symbol - // generation. - void LatePostprocessMappings(); -#endif // __ANDROID__ -}; - -} // namespace google_breakpad - -#endif // CLIENT_LINUX_HANDLER_LINUX_DUMPER_H_ diff --git a/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/linux_dumper_unittest_helper.cc b/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/linux_dumper_unittest_helper.cc deleted file mode 100644 index 4ccb7201f..000000000 --- a/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/linux_dumper_unittest_helper.cc +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright (c) 2010, Google Inc. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// Helper program for the linux_dumper class, which creates a bunch of -// threads. The first word of each thread's stack is set to the thread -// id. - -#include <pthread.h> -#include <stdint.h> -#include <stdio.h> -#include <stdlib.h> -#include <sys/syscall.h> -#include <unistd.h> - -#include "common/scoped_ptr.h" -#include "third_party/lss/linux_syscall_support.h" - -#if defined(__ARM_EABI__) -#define TID_PTR_REGISTER "r3" -#elif defined(__aarch64__) -#define TID_PTR_REGISTER "x3" -#elif defined(__i386) -#define TID_PTR_REGISTER "ecx" -#elif defined(__x86_64) -#define TID_PTR_REGISTER "rcx" -#elif defined(__mips__) -#define TID_PTR_REGISTER "$1" -#else -#error This test has not been ported to this platform. -#endif - -void *thread_function(void *data) { - int pipefd = *static_cast<int *>(data); - volatile pid_t thread_id = syscall(__NR_gettid); - // Signal parent that a thread has started. - uint8_t byte = 1; - if (write(pipefd, &byte, sizeof(byte)) != sizeof(byte)) { - perror("ERROR: parent notification failed"); - return NULL; - } - register volatile pid_t *thread_id_ptr asm(TID_PTR_REGISTER) = &thread_id; - while (true) - asm volatile ("" : : "r" (thread_id_ptr)); - return NULL; -} - -int main(int argc, char *argv[]) { - if (argc < 3) { - fprintf(stderr, - "usage: linux_dumper_unittest_helper <pipe fd> <# of threads>\n"); - return 1; - } - int pipefd = atoi(argv[1]); - int num_threads = atoi(argv[2]); - if (num_threads < 1) { - fprintf(stderr, "ERROR: number of threads is 0"); - return 1; - } - google_breakpad::scoped_array<pthread_t> threads(new pthread_t[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, &pipefd); - } - thread_function(&pipefd); - return 0; -} diff --git a/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/linux_ptrace_dumper.cc b/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/linux_ptrace_dumper.cc deleted file mode 100644 index c35e0e958..000000000 --- a/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/linux_ptrace_dumper.cc +++ /dev/null @@ -1,355 +0,0 @@ -// Copyright (c) 2012, 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. - -// linux_ptrace_dumper.cc: Implement google_breakpad::LinuxPtraceDumper. -// See linux_ptrace_dumper.h for detals. -// This class was originally splitted from google_breakpad::LinuxDumper. - -// This code deals with the mechanics of getting information about a crashed -// process. Since this code may run in a compromised address space, the same -// rules apply as detailed at the top of minidump_writer.h: no libc calls and -// use the alternative allocator. - -#include "client/linux/minidump_writer/linux_ptrace_dumper.h" - -#include <asm/ptrace.h> -#include <assert.h> -#include <errno.h> -#include <fcntl.h> -#include <limits.h> -#include <stddef.h> -#include <stdlib.h> -#include <string.h> -#include <sys/ptrace.h> -#include <sys/uio.h> -#include <sys/wait.h> - -#if defined(__i386) -#include <cpuid.h> -#endif - -#include "client/linux/minidump_writer/directory_reader.h" -#include "client/linux/minidump_writer/line_reader.h" -#include "common/linux/linux_libc_support.h" -#include "third_party/lss/linux_syscall_support.h" - -// Suspends a thread by attaching to it. -static bool SuspendThread(pid_t pid) { - // This may fail if the thread has just died or debugged. - errno = 0; - if (sys_ptrace(PTRACE_ATTACH, pid, NULL, NULL) != 0 && - errno != 0) { - return false; - } - while (sys_waitpid(pid, NULL, __WALL) < 0) { - if (errno != EINTR) { - sys_ptrace(PTRACE_DETACH, pid, NULL, NULL); - return false; - } - } -#if defined(__i386) || defined(__x86_64) - // On x86, the stack pointer is NULL or -1, when executing trusted code in - // the seccomp sandbox. Not only does this cause difficulties down the line - // when trying to dump the thread's stack, it also results in the minidumps - // containing information about the trusted threads. This information is - // generally completely meaningless and just pollutes the minidumps. - // We thus test the stack pointer and exclude any threads that are part of - // the seccomp sandbox's trusted code. - user_regs_struct regs; - if (sys_ptrace(PTRACE_GETREGS, pid, NULL, ®s) == -1 || -#if defined(__i386) - !regs.esp -#elif defined(__x86_64) - !regs.rsp -#endif - ) { - sys_ptrace(PTRACE_DETACH, pid, NULL, NULL); - return false; - } -#endif - return true; -} - -// Resumes a thread by detaching from it. -static bool ResumeThread(pid_t pid) { - return sys_ptrace(PTRACE_DETACH, pid, NULL, NULL) >= 0; -} - -namespace google_breakpad { - -LinuxPtraceDumper::LinuxPtraceDumper(pid_t pid) - : LinuxDumper(pid), - threads_suspended_(false) { -} - -bool LinuxPtraceDumper::BuildProcPath(char* path, pid_t pid, - const char* node) const { - if (!path || !node || pid <= 0) - return false; - - size_t node_len = my_strlen(node); - if (node_len == 0) - return false; - - const unsigned pid_len = my_uint_len(pid); - const size_t total_length = 6 + pid_len + 1 + node_len; - if (total_length >= NAME_MAX) - return false; - - my_memcpy(path, "/proc/", 6); - my_uitos(path + 6, pid, pid_len); - path[6 + pid_len] = '/'; - my_memcpy(path + 6 + pid_len + 1, node, node_len); - path[total_length] = '\0'; - return true; -} - -bool LinuxPtraceDumper::CopyFromProcess(void* dest, pid_t child, - const void* src, size_t length) { - unsigned long tmp = 55; - size_t done = 0; - static const size_t word_size = sizeof(tmp); - uint8_t* const local = (uint8_t*) dest; - uint8_t* const remote = (uint8_t*) 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) { - tmp = 0; - } - my_memcpy(local + done, &tmp, l); - done += l; - } - return true; -} - -// Read thread info from /proc/$pid/status. -// 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 -// available. -bool LinuxPtraceDumper::GetThreadInfoByIndex(size_t index, ThreadInfo* info) { - if (index >= threads_.size()) - return false; - - pid_t tid = threads_[index]; - - assert(info != NULL); - char status_path[NAME_MAX]; - if (!BuildProcPath(status_path, tid, "status")) - return false; - - const int fd = sys_open(status_path, O_RDONLY, 0); - if (fd < 0) - return false; - - LineReader* const line_reader = new(allocator_) LineReader(fd); - const char* line; - unsigned line_len; - - info->ppid = info->tgid = -1; - - while (line_reader->GetNextLine(&line, &line_len)) { - if (my_strncmp("Tgid:\t", line, 6) == 0) { - my_strtoui(&info->tgid, line + 6); - } else if (my_strncmp("PPid:\t", line, 6) == 0) { - my_strtoui(&info->ppid, line + 6); - } - - line_reader->PopLine(line_len); - } - sys_close(fd); - - if (info->ppid == -1 || info->tgid == -1) - return false; - -#ifdef PTRACE_GETREGSET - struct iovec io; - info->GetGeneralPurposeRegisters(&io.iov_base, &io.iov_len); - if (sys_ptrace(PTRACE_GETREGSET, tid, (void*)NT_PRSTATUS, (void*)&io) == -1) { - return false; - } - - info->GetFloatingPointRegisters(&io.iov_base, &io.iov_len); - if (sys_ptrace(PTRACE_GETREGSET, tid, (void*)NT_FPREGSET, (void*)&io) == -1) { - return false; - } -#else // PTRACE_GETREGSET - void* gp_addr; - info->GetGeneralPurposeRegisters(&gp_addr, NULL); - if (sys_ptrace(PTRACE_GETREGS, tid, NULL, gp_addr) == -1) { - return false; - } - -#if !(defined(__ANDROID__) && defined(__ARM_EABI__)) - // When running an arm build on an arm64 device, attempting to get the - // floating point registers fails. On Android, the floating point registers - // aren't written to the cpu context anyway, so just don't get them here. - // See http://crbug.com/508324 - void* fp_addr; - info->GetFloatingPointRegisters(&fp_addr, NULL); - if (sys_ptrace(PTRACE_GETFPREGS, tid, NULL, fp_addr) == -1) { - return false; - } -#endif -#endif // PTRACE_GETREGSET - -#if defined(__i386) -#if !defined(bit_FXSAVE) // e.g. Clang -#define bit_FXSAVE bit_FXSR -#endif - // Detect if the CPU supports the FXSAVE/FXRSTOR instructions - int eax, ebx, ecx, edx; - __cpuid(1, eax, ebx, ecx, edx); - if (edx & bit_FXSAVE) { - if (sys_ptrace(PTRACE_GETFPXREGS, tid, NULL, &info->fpxregs) == -1) { - return false; - } - } else { - memset(&info->fpxregs, 0, sizeof(info->fpxregs)); - } -#endif // defined(__i386) - -#if defined(__i386) || defined(__x86_64) - for (unsigned i = 0; i < ThreadInfo::kNumDebugRegisters; ++i) { - if (sys_ptrace( - PTRACE_PEEKUSER, tid, - reinterpret_cast<void*> (offsetof(struct user, - u_debugreg[0]) + i * - sizeof(debugreg_t)), - &info->dregs[i]) == -1) { - return false; - } - } -#endif - -#if defined(__mips__) - sys_ptrace(PTRACE_PEEKUSER, tid, - reinterpret_cast<void*>(DSP_BASE), &info->mcontext.hi1); - sys_ptrace(PTRACE_PEEKUSER, tid, - reinterpret_cast<void*>(DSP_BASE + 1), &info->mcontext.lo1); - sys_ptrace(PTRACE_PEEKUSER, tid, - reinterpret_cast<void*>(DSP_BASE + 2), &info->mcontext.hi2); - sys_ptrace(PTRACE_PEEKUSER, tid, - reinterpret_cast<void*>(DSP_BASE + 3), &info->mcontext.lo2); - sys_ptrace(PTRACE_PEEKUSER, tid, - reinterpret_cast<void*>(DSP_BASE + 4), &info->mcontext.hi3); - sys_ptrace(PTRACE_PEEKUSER, tid, - reinterpret_cast<void*>(DSP_BASE + 5), &info->mcontext.lo3); - sys_ptrace(PTRACE_PEEKUSER, tid, - reinterpret_cast<void*>(DSP_CONTROL), &info->mcontext.dsp); -#endif - - const uint8_t* stack_pointer; -#if defined(__i386) - my_memcpy(&stack_pointer, &info->regs.esp, sizeof(info->regs.esp)); -#elif defined(__x86_64) - my_memcpy(&stack_pointer, &info->regs.rsp, sizeof(info->regs.rsp)); -#elif defined(__ARM_EABI__) - my_memcpy(&stack_pointer, &info->regs.ARM_sp, sizeof(info->regs.ARM_sp)); -#elif defined(__aarch64__) - my_memcpy(&stack_pointer, &info->regs.sp, sizeof(info->regs.sp)); -#elif defined(__mips__) - stack_pointer = - reinterpret_cast<uint8_t*>(info->mcontext.gregs[MD_CONTEXT_MIPS_REG_SP]); -#else -#error "This code hasn't been ported to your platform yet." -#endif - info->stack_pointer = reinterpret_cast<uintptr_t>(stack_pointer); - - return true; -} - -bool LinuxPtraceDumper::IsPostMortem() const { - return false; -} - -bool LinuxPtraceDumper::ThreadsSuspend() { - if (threads_suspended_) - return true; - for (size_t i = 0; i < threads_.size(); ++i) { - if (!SuspendThread(threads_[i])) { - // If the thread either disappeared before we could attach to it, or if - // it was part of the seccomp sandbox's trusted code, it is OK to - // silently drop it from the minidump. - if (i < threads_.size() - 1) { - my_memmove(&threads_[i], &threads_[i + 1], - (threads_.size() - i - 1) * sizeof(threads_[i])); - } - threads_.resize(threads_.size() - 1); - --i; - } - } - threads_suspended_ = true; - return threads_.size() > 0; -} - -bool LinuxPtraceDumper::ThreadsResume() { - if (!threads_suspended_) - return false; - bool good = true; - for (size_t i = 0; i < threads_.size(); ++i) - good &= ResumeThread(threads_[i]); - threads_suspended_ = false; - return good; -} - -// Parse /proc/$pid/task to list all the threads of the process identified by -// pid. -bool LinuxPtraceDumper::EnumerateThreads() { - char task_path[NAME_MAX]; - if (!BuildProcPath(task_path, pid_, "task")) - return false; - - const int fd = sys_open(task_path, O_RDONLY | O_DIRECTORY, 0); - if (fd < 0) - return false; - DirectoryReader* dir_reader = new(allocator_) DirectoryReader(fd); - - // The directory may contain duplicate entries which we filter by assuming - // that they are consecutive. - int last_tid = -1; - const char* dent_name; - while (dir_reader->GetNextEntry(&dent_name)) { - if (my_strcmp(dent_name, ".") && - my_strcmp(dent_name, "..")) { - int tid = 0; - if (my_strtoui(&tid, dent_name) && - last_tid != tid) { - last_tid = tid; - threads_.push_back(tid); - } - } - dir_reader->PopEntry(); - } - - sys_close(fd); - return true; -} - -} // namespace google_breakpad diff --git a/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/linux_ptrace_dumper.h b/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/linux_ptrace_dumper.h deleted file mode 100644 index 2ce834b0f..000000000 --- a/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/linux_ptrace_dumper.h +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright (c) 2012, 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. - -// linux_ptrace_dumper.h: Define the google_breakpad::LinuxPtraceDumper -// class, which is derived from google_breakpad::LinuxDumper to extract -// information from a crashed process via ptrace. -// This class was originally splitted from google_breakpad::LinuxDumper. - -#ifndef CLIENT_LINUX_MINIDUMP_WRITER_LINUX_PTRACE_DUMPER_H_ -#define CLIENT_LINUX_MINIDUMP_WRITER_LINUX_PTRACE_DUMPER_H_ - -#include "client/linux/minidump_writer/linux_dumper.h" - -namespace google_breakpad { - -class LinuxPtraceDumper : public LinuxDumper { - public: - // Constructs a dumper for extracting information of a given process - // with a process ID of |pid|. - explicit LinuxPtraceDumper(pid_t pid); - - // Implements LinuxDumper::BuildProcPath(). - // Builds a proc path for a certain pid for a node (/proc/<pid>/<node>). - // |path| is a character array of at least NAME_MAX bytes to return the - // result. |node| is the final node without any slashes. Returns true on - // success. - virtual bool BuildProcPath(char* path, pid_t pid, const char* node) const; - - // Implements LinuxDumper::CopyFromProcess(). - // Copies content of |length| bytes from a given process |child|, - // starting from |src|, into |dest|. This method uses ptrace to extract - // the content from the target process. Always returns true. - virtual bool CopyFromProcess(void* dest, pid_t child, const void* src, - size_t length); - - // Implements LinuxDumper::GetThreadInfoByIndex(). - // Reads information about the |index|-th thread of |threads_|. - // Returns true on success. One must have called |ThreadsSuspend| first. - virtual bool GetThreadInfoByIndex(size_t index, ThreadInfo* info); - - // Implements LinuxDumper::IsPostMortem(). - // Always returns false to indicate this dumper performs a dump of - // a crashed process via ptrace. - virtual bool IsPostMortem() const; - - // Implements LinuxDumper::ThreadsSuspend(). - // Suspends all threads in the given process. Returns true on success. - virtual bool ThreadsSuspend(); - - // Implements LinuxDumper::ThreadsResume(). - // Resumes all threads in the given process. Returns true on success. - virtual bool ThreadsResume(); - - protected: - // Implements LinuxDumper::EnumerateThreads(). - // Enumerates all threads of the given process into |threads_|. - virtual bool EnumerateThreads(); - - private: - // Set to true if all threads of the crashed process are suspended. - bool threads_suspended_; -}; - -} // namespace google_breakpad - -#endif // CLIENT_LINUX_HANDLER_LINUX_PTRACE_DUMPER_H_ diff --git a/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/linux_ptrace_dumper_unittest.cc b/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/linux_ptrace_dumper_unittest.cc deleted file mode 100644 index be533e157..000000000 --- a/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/linux_ptrace_dumper_unittest.cc +++ /dev/null @@ -1,470 +0,0 @@ -// 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. - -// linux_ptrace_dumper_unittest.cc: -// Unit tests for google_breakpad::LinuxPtraceDumper. -// -// This file was renamed from linux_dumper_unittest.cc and modified due -// to LinuxDumper being splitted into two classes. - -#include <errno.h> -#include <fcntl.h> -#include <limits.h> -#include <unistd.h> -#include <signal.h> -#include <stdint.h> -#include <string.h> -#include <sys/mman.h> -#include <sys/prctl.h> -#include <sys/poll.h> -#include <sys/stat.h> -#include <sys/types.h> - -#include <string> - -#include "breakpad_googletest_includes.h" -#include "client/linux/minidump_writer/linux_ptrace_dumper.h" -#include "client/linux/minidump_writer/minidump_writer_unittest_utils.h" -#include "common/linux/eintr_wrapper.h" -#include "common/linux/file_id.h" -#include "common/linux/ignore_ret.h" -#include "common/linux/safe_readlink.h" -#include "common/memory.h" -#include "common/using_std_string.h" - -#ifndef PR_SET_PTRACER -#define PR_SET_PTRACER 0x59616d61 -#endif - -using namespace google_breakpad; - -namespace { - -typedef wasteful_vector<uint8_t> id_vector; -typedef testing::Test LinuxPtraceDumperTest; - -/* Fixture for running tests in a child process. */ -class LinuxPtraceDumperChildTest : public testing::Test { - protected: - virtual void SetUp() { - child_pid_ = fork(); -#ifndef __ANDROID__ - prctl(PR_SET_PTRACER, child_pid_); -#endif - } - - /* Gtest is calling TestBody from this class, which sets up a child - * process in which the RealTestBody virtual member is called. - * As such, TestBody is not supposed to be overridden in derived classes. - */ - virtual void TestBody() /* final */ { - if (child_pid_ == 0) { - // child process - RealTestBody(); - exit(HasFatalFailure() ? kFatalFailure : - (HasNonfatalFailure() ? kNonFatalFailure : 0)); - } - - ASSERT_TRUE(child_pid_ > 0); - int status; - waitpid(child_pid_, &status, 0); - if (WEXITSTATUS(status) == kFatalFailure) { - GTEST_FATAL_FAILURE_("Test failed in child process"); - } else if (WEXITSTATUS(status) == kNonFatalFailure) { - GTEST_NONFATAL_FAILURE_("Test failed in child process"); - } - } - - /* Gtest defines TestBody functions through its macros, but classes - * derived from this one need to define RealTestBody instead. - * This is achieved by defining a TestBody macro further below. - */ - virtual void RealTestBody() = 0; - - id_vector make_vector() { - return id_vector(&allocator, kDefaultBuildIdSize); - } - - private: - static const int kFatalFailure = 1; - static const int kNonFatalFailure = 2; - - pid_t child_pid_; - PageAllocator allocator; -}; - -} // namespace - -/* Replace TestBody declarations within TEST*() with RealTestBody - * declarations */ -#define TestBody RealTestBody - -TEST_F(LinuxPtraceDumperChildTest, Setup) { - LinuxPtraceDumper dumper(getppid()); -} - -TEST_F(LinuxPtraceDumperChildTest, FindMappings) { - LinuxPtraceDumper dumper(getppid()); - ASSERT_TRUE(dumper.Init()); - - ASSERT_TRUE(dumper.FindMapping(reinterpret_cast<void*>(getpid))); - ASSERT_TRUE(dumper.FindMapping(reinterpret_cast<void*>(printf))); - ASSERT_FALSE(dumper.FindMapping(NULL)); -} - -TEST_F(LinuxPtraceDumperChildTest, ThreadList) { - LinuxPtraceDumper dumper(getppid()); - ASSERT_TRUE(dumper.Init()); - - 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] == getppid()) { - ASSERT_FALSE(found); - found = true; - } - } - ASSERT_TRUE(found); -} - -// Helper stack class to close a file descriptor and unmap -// a mmap'ed mapping. -class StackHelper { - public: - StackHelper() - : fd_(-1), mapping_(NULL), size_(0) {} - ~StackHelper() { - if (size_) - munmap(mapping_, size_); - if (fd_ >= 0) - close(fd_); - } - void Init(int fd, char* mapping, size_t size) { - fd_ = fd; - mapping_ = mapping; - size_ = size; - } - - char* mapping() const { return mapping_; } - size_t size() const { return size_; } - - private: - int fd_; - char* mapping_; - size_t size_; -}; - -class LinuxPtraceDumperMappingsTest : public LinuxPtraceDumperChildTest { - protected: - virtual void SetUp(); - - string helper_path_; - size_t page_size_; - StackHelper helper_; -}; - -void LinuxPtraceDumperMappingsTest::SetUp() { - helper_path_ = GetHelperBinary(); - if (helper_path_.empty()) { - FAIL() << "Couldn't find helper binary"; - exit(1); - } - - // mmap two segments out of the helper binary, one - // enclosed in the other, but with different protections. - page_size_ = sysconf(_SC_PAGESIZE); - const size_t kMappingSize = 3 * page_size_; - int fd = open(helper_path_.c_str(), O_RDONLY); - ASSERT_NE(-1, fd) << "Failed to open file: " << helper_path_ - << ", Error: " << strerror(errno); - char* mapping = - reinterpret_cast<char*>(mmap(NULL, - kMappingSize, - PROT_READ, - MAP_SHARED, - fd, - 0)); - ASSERT_TRUE(mapping); - - // Ensure that things get cleaned up. - helper_.Init(fd, mapping, kMappingSize); - - // Carve a page out of the first mapping with different permissions. - char* inside_mapping = reinterpret_cast<char*>( - mmap(mapping + 2 * page_size_, - page_size_, - PROT_NONE, - MAP_SHARED | MAP_FIXED, - fd, - // Map a different offset just to - // better test real-world conditions. - page_size_)); - ASSERT_TRUE(inside_mapping); - - LinuxPtraceDumperChildTest::SetUp(); -} - -TEST_F(LinuxPtraceDumperMappingsTest, MergedMappings) { - // Now check that LinuxPtraceDumper interpreted the mappings properly. - LinuxPtraceDumper dumper(getppid()); - ASSERT_TRUE(dumper.Init()); - int mapping_count = 0; - for (unsigned i = 0; i < dumper.mappings().size(); ++i) { - const MappingInfo& mapping = *dumper.mappings()[i]; - if (strcmp(mapping.name, this->helper_path_.c_str()) == 0) { - // This mapping should encompass the entire original mapped - // range. - EXPECT_EQ(reinterpret_cast<uintptr_t>(this->helper_.mapping()), - mapping.start_addr); - EXPECT_EQ(this->helper_.size(), mapping.size); - EXPECT_EQ(0U, mapping.offset); - mapping_count++; - } - } - EXPECT_EQ(1, mapping_count); -} - -TEST_F(LinuxPtraceDumperChildTest, BuildProcPath) { - const pid_t pid = getppid(); - LinuxPtraceDumper dumper(pid); - - char maps_path[NAME_MAX] = ""; - char maps_path_expected[NAME_MAX]; - snprintf(maps_path_expected, sizeof(maps_path_expected), - "/proc/%d/maps", pid); - EXPECT_TRUE(dumper.BuildProcPath(maps_path, pid, "maps")); - EXPECT_STREQ(maps_path_expected, maps_path); - - EXPECT_FALSE(dumper.BuildProcPath(NULL, pid, "maps")); - EXPECT_FALSE(dumper.BuildProcPath(maps_path, 0, "maps")); - EXPECT_FALSE(dumper.BuildProcPath(maps_path, pid, "")); - EXPECT_FALSE(dumper.BuildProcPath(maps_path, pid, NULL)); - - char long_node[NAME_MAX]; - size_t long_node_len = NAME_MAX - strlen("/proc/123") - 1; - memset(long_node, 'a', long_node_len); - long_node[long_node_len] = '\0'; - EXPECT_FALSE(dumper.BuildProcPath(maps_path, 123, long_node)); -} - -#if !defined(__ARM_EABI__) && !defined(__mips__) -// Ensure that the linux-gate VDSO is included in the mapping list. -TEST_F(LinuxPtraceDumperChildTest, MappingsIncludeLinuxGate) { - LinuxPtraceDumper dumper(getppid()); - ASSERT_TRUE(dumper.Init()); - - void* linux_gate_loc = - reinterpret_cast<void *>(dumper.auxv()[AT_SYSINFO_EHDR]); - ASSERT_TRUE(linux_gate_loc); - bool found_linux_gate = false; - - const wasteful_vector<MappingInfo*> mappings = dumper.mappings(); - const MappingInfo* mapping; - for (unsigned i = 0; i < mappings.size(); ++i) { - mapping = mappings[i]; - if (!strcmp(mapping->name, kLinuxGateLibraryName)) { - found_linux_gate = true; - break; - } - } - EXPECT_TRUE(found_linux_gate); - EXPECT_EQ(linux_gate_loc, reinterpret_cast<void*>(mapping->start_addr)); - EXPECT_EQ(0, memcmp(linux_gate_loc, ELFMAG, SELFMAG)); -} - -// Ensure that the linux-gate VDSO can generate a non-zeroed File ID. -TEST_F(LinuxPtraceDumperChildTest, LinuxGateMappingID) { - LinuxPtraceDumper dumper(getppid()); - ASSERT_TRUE(dumper.Init()); - - bool found_linux_gate = false; - const wasteful_vector<MappingInfo*> mappings = dumper.mappings(); - unsigned index = 0; - for (unsigned i = 0; i < mappings.size(); ++i) { - if (!strcmp(mappings[i]->name, kLinuxGateLibraryName)) { - found_linux_gate = true; - index = i; - break; - } - } - ASSERT_TRUE(found_linux_gate); - - // Need to suspend the child so ptrace actually works. - ASSERT_TRUE(dumper.ThreadsSuspend()); - id_vector identifier(make_vector()); - ASSERT_TRUE(dumper.ElfFileIdentifierForMapping(*mappings[index], - true, - index, - identifier)); - - id_vector empty_identifier(make_vector()); - empty_identifier.resize(kDefaultBuildIdSize, 0); - EXPECT_NE(empty_identifier, identifier); - EXPECT_TRUE(dumper.ThreadsResume()); -} -#endif - -TEST_F(LinuxPtraceDumperChildTest, FileIDsMatch) { - // Calculate the File ID of our binary using both - // FileID::ElfFileIdentifier and LinuxDumper::ElfFileIdentifierForMapping - // and ensure that we get the same result from both. - char exe_name[PATH_MAX]; - ASSERT_TRUE(SafeReadLink("/proc/self/exe", exe_name)); - - LinuxPtraceDumper dumper(getppid()); - ASSERT_TRUE(dumper.Init()); - const wasteful_vector<MappingInfo*> mappings = dumper.mappings(); - bool found_exe = false; - unsigned i; - for (i = 0; i < mappings.size(); ++i) { - const MappingInfo* mapping = mappings[i]; - if (!strcmp(mapping->name, exe_name)) { - found_exe = true; - break; - } - } - ASSERT_TRUE(found_exe); - - id_vector identifier1(make_vector()); - id_vector identifier2(make_vector()); - EXPECT_TRUE(dumper.ElfFileIdentifierForMapping(*mappings[i], true, i, - identifier1)); - FileID fileid(exe_name); - EXPECT_TRUE(fileid.ElfFileIdentifier(identifier2)); - - string identifier_string1 = - FileID::ConvertIdentifierToUUIDString(identifier1); - string identifier_string2 = - FileID::ConvertIdentifierToUUIDString(identifier2); - EXPECT_EQ(identifier_string1, identifier_string2); -} - -/* Get back to normal behavior of TEST*() macros wrt TestBody. */ -#undef TestBody - -TEST(LinuxPtraceDumperTest, VerifyStackReadWithMultipleThreads) { - static const int kNumberOfThreadsInHelperProgram = 5; - char kNumberOfThreadsArgument[2]; - sprintf(kNumberOfThreadsArgument, "%d", kNumberOfThreadsInHelperProgram); - - int fds[2]; - ASSERT_NE(-1, pipe(fds)); - - pid_t child_pid = fork(); - if (child_pid == 0) { - // In child process. - close(fds[0]); - - string helper_path(GetHelperBinary()); - if (helper_path.empty()) { - FAIL() << "Couldn't find helper binary"; - exit(1); - } - - // Pass the pipe fd and the number of threads as arguments. - char pipe_fd_string[8]; - sprintf(pipe_fd_string, "%d", fds[1]); - execl(helper_path.c_str(), - "linux_dumper_unittest_helper", - pipe_fd_string, - kNumberOfThreadsArgument, - NULL); - // Kill if we get here. - printf("Errno from exec: %d", errno); - FAIL() << "Exec of " << helper_path << " failed: " << strerror(errno); - exit(0); - } - close(fds[1]); - - // Wait for all child threads to indicate that they have started - for (int threads = 0; threads < kNumberOfThreadsInHelperProgram; threads++) { - struct pollfd pfd; - memset(&pfd, 0, sizeof(pfd)); - pfd.fd = fds[0]; - pfd.events = POLLIN | POLLERR; - - const int r = HANDLE_EINTR(poll(&pfd, 1, 1000)); - ASSERT_EQ(1, r); - ASSERT_TRUE(pfd.revents & POLLIN); - uint8_t junk; - ASSERT_EQ(read(fds[0], &junk, sizeof(junk)), - static_cast<ssize_t>(sizeof(junk))); - } - close(fds[0]); - - // There is a race here because we may stop a child thread before - // it is actually running the busy loop. Empirically this sleep - // is sufficient to avoid the race. - usleep(100000); - - // Children are ready now. - LinuxPtraceDumper dumper(child_pid); - ASSERT_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.GetThreadInfoByIndex(i, &one_thread)); - const void* stack; - size_t stack_len; - EXPECT_TRUE(dumper.GetStackInfo(&stack, &stack_len, - one_thread.stack_pointer)); - // In the helper program, we stored a pointer to the thread id in a - // specific register. Check that we can recover its value. -#if defined(__ARM_EABI__) - pid_t* process_tid_location = (pid_t*)(one_thread.regs.uregs[3]); -#elif defined(__aarch64__) - pid_t* process_tid_location = (pid_t*)(one_thread.regs.regs[3]); -#elif defined(__i386) - pid_t* process_tid_location = (pid_t*)(one_thread.regs.ecx); -#elif defined(__x86_64) - pid_t* process_tid_location = (pid_t*)(one_thread.regs.rcx); -#elif defined(__mips__) - pid_t* process_tid_location = - reinterpret_cast<pid_t*>(one_thread.mcontext.gregs[1]); -#else -#error This test has not been ported to this platform. -#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); - } - EXPECT_TRUE(dumper.ThreadsResume()); - kill(child_pid, SIGKILL); - - // Reap child - int status; - ASSERT_NE(-1, HANDLE_EINTR(waitpid(child_pid, &status, 0))); - ASSERT_TRUE(WIFSIGNALED(status)); - ASSERT_EQ(SIGKILL, WTERMSIG(status)); -} diff --git a/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/minidump_writer.cc b/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/minidump_writer.cc deleted file mode 100644 index 4b1ae5ad3..000000000 --- a/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/minidump_writer.cc +++ /dev/null @@ -1,1376 +0,0 @@ -// 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. - -// This code writes out minidump files: -// http://msdn.microsoft.com/en-us/library/ms680378(VS.85,loband).aspx -// -// Minidumps are a Microsoft format which Breakpad uses for recording crash -// dumps. This code has to run in a compromised environment (the address space -// may have received SIGSEGV), thus the following rules apply: -// * You may not enter the dynamic linker. This means that we cannot call -// any symbols in a shared library (inc libc). Because of this we replace -// libc functions in linux_libc_support.h. -// * You may not call syscalls via the libc wrappers. This rule is a subset -// of the first rule but it bears repeating. We have direct wrappers -// around the system calls in linux_syscall_support.h. -// * You may not malloc. There's an alternative allocator in memory.h and -// a canonical instance in the LinuxDumper object. We use the placement -// new form to allocate objects and we don't delete them. - -#include "client/linux/handler/minidump_descriptor.h" -#include "client/linux/minidump_writer/minidump_writer.h" -#include "client/minidump_file_writer-inl.h" - -#include <ctype.h> -#include <errno.h> -#include <fcntl.h> -#include <link.h> -#include <stdio.h> -#if defined(__ANDROID__) -#include <sys/system_properties.h> -#endif -#include <sys/types.h> -#include <sys/ucontext.h> -#include <sys/user.h> -#include <sys/utsname.h> -#include <time.h> -#include <unistd.h> - -#include <algorithm> - -#include "client/linux/dump_writer_common/thread_info.h" -#include "client/linux/dump_writer_common/ucontext_reader.h" -#include "client/linux/handler/exception_handler.h" -#include "client/linux/minidump_writer/cpu_set.h" -#include "client/linux/minidump_writer/line_reader.h" -#include "client/linux/minidump_writer/linux_dumper.h" -#include "client/linux/minidump_writer/linux_ptrace_dumper.h" -#include "client/linux/minidump_writer/proc_cpuinfo_reader.h" -#include "client/minidump_file_writer.h" -#include "common/linux/file_id.h" -#include "common/linux/linux_libc_support.h" -#include "common/minidump_type_helper.h" -#include "google_breakpad/common/minidump_format.h" -#include "third_party/lss/linux_syscall_support.h" - -namespace { - -using google_breakpad::AppMemoryList; -using google_breakpad::auto_wasteful_vector; -using google_breakpad::ExceptionHandler; -using google_breakpad::CpuSet; -using google_breakpad::kDefaultBuildIdSize; -using google_breakpad::LineReader; -using google_breakpad::LinuxDumper; -using google_breakpad::LinuxPtraceDumper; -using google_breakpad::MDTypeHelper; -using google_breakpad::MappingEntry; -using google_breakpad::MappingInfo; -using google_breakpad::MappingList; -using google_breakpad::MinidumpFileWriter; -using google_breakpad::PageAllocator; -using google_breakpad::ProcCpuInfoReader; -using google_breakpad::RawContextCPU; -using google_breakpad::ThreadInfo; -using google_breakpad::TypedMDRVA; -using google_breakpad::UContextReader; -using google_breakpad::UntypedMDRVA; -using google_breakpad::wasteful_vector; - -typedef MDTypeHelper<sizeof(void*)>::MDRawDebug MDRawDebug; -typedef MDTypeHelper<sizeof(void*)>::MDRawLinkMap MDRawLinkMap; - -class MinidumpWriter { - public: - // The following kLimit* constants are for when minidump_size_limit_ is set - // and the minidump size might exceed it. - // - // Estimate for how big each thread's stack will be (in bytes). - static const unsigned kLimitAverageThreadStackLength = 8 * 1024; - // Number of threads whose stack size we don't want to limit. These base - // threads will simply be the first N threads returned by the dumper (although - // the crashing thread will never be limited). Threads beyond this count are - // the extra threads. - static const unsigned kLimitBaseThreadCount = 20; - // Maximum stack size to dump for any extra thread (in bytes). - static const unsigned kLimitMaxExtraThreadStackLen = 2 * 1024; - // Make sure this number of additional bytes can fit in the minidump - // (exclude the stack data). - static const unsigned kLimitMinidumpFudgeFactor = 64 * 1024; - - MinidumpWriter(const char* minidump_path, - int minidump_fd, - const ExceptionHandler::CrashContext* context, - const MappingList& mappings, - const AppMemoryList& appmem, - LinuxDumper* dumper) - : fd_(minidump_fd), - path_(minidump_path), - ucontext_(context ? &context->context : NULL), -#if !defined(__ARM_EABI__) && !defined(__mips__) - float_state_(context ? &context->float_state : NULL), -#endif - dumper_(dumper), - minidump_size_limit_(-1), - memory_blocks_(dumper_->allocator()), - mapping_list_(mappings), - app_memory_list_(appmem) { - // Assert there should be either a valid fd or a valid path, not both. - assert(fd_ != -1 || minidump_path); - assert(fd_ == -1 || !minidump_path); - } - - bool Init() { - if (!dumper_->Init()) - return false; - - if (fd_ != -1) - minidump_writer_.SetFile(fd_); - else if (!minidump_writer_.Open(path_)) - return false; - - return dumper_->ThreadsSuspend() && dumper_->LateInit(); - } - - ~MinidumpWriter() { - // Don't close the file descriptor when it's been provided explicitly. - // Callers might still need to use it. - if (fd_ == -1) - minidump_writer_.Close(); - dumper_->ThreadsResume(); - } - - bool Dump() { - // A minidump file contains a number of tagged streams. This is the number - // of stream which we write. - unsigned kNumWriters = 13; - - TypedMDRVA<MDRawDirectory> dir(&minidump_writer_); - { - // Ensure the header gets flushed, as that happens in the destructor. - // If we crash somewhere below, we should have a mostly-intact dump - TypedMDRVA<MDRawHeader> header(&minidump_writer_); - if (!header.Allocate()) - return false; - - if (!dir.AllocateArray(kNumWriters)) - return false; - - my_memset(header.get(), 0, sizeof(MDRawHeader)); - - header.get()->signature = MD_HEADER_SIGNATURE; - header.get()->version = MD_HEADER_VERSION; - header.get()->time_date_stamp = time(NULL); - header.get()->stream_count = kNumWriters; - header.get()->stream_directory_rva = dir.position(); - } - - unsigned dir_index = 0; - MDRawDirectory dirent; - - if (!WriteThreadListStream(&dirent)) - return false; - dir.CopyIndex(dir_index++, &dirent); - - if (!WriteMappings(&dirent)) - return false; - dir.CopyIndex(dir_index++, &dirent); - - if (!WriteAppMemory()) - return false; - - if (!WriteMemoryListStream(&dirent)) - return false; - dir.CopyIndex(dir_index++, &dirent); - - if (!WriteExceptionStream(&dirent)) - return false; - dir.CopyIndex(dir_index++, &dirent); - - if (!WriteSystemInfoStream(&dirent)) - return false; - dir.CopyIndex(dir_index++, &dirent); - - dirent.stream_type = MD_LINUX_CPU_INFO; - if (!WriteFile(&dirent.location, "/proc/cpuinfo")) - NullifyDirectoryEntry(&dirent); - dir.CopyIndex(dir_index++, &dirent); - - dirent.stream_type = MD_LINUX_PROC_STATUS; - if (!WriteProcFile(&dirent.location, GetCrashThread(), "status")) - NullifyDirectoryEntry(&dirent); - dir.CopyIndex(dir_index++, &dirent); - - dirent.stream_type = MD_LINUX_LSB_RELEASE; - if (!WriteFile(&dirent.location, "/etc/lsb-release")) - NullifyDirectoryEntry(&dirent); - dir.CopyIndex(dir_index++, &dirent); - - dirent.stream_type = MD_LINUX_CMD_LINE; - if (!WriteProcFile(&dirent.location, GetCrashThread(), "cmdline")) - NullifyDirectoryEntry(&dirent); - dir.CopyIndex(dir_index++, &dirent); - - dirent.stream_type = MD_LINUX_ENVIRON; - if (!WriteProcFile(&dirent.location, GetCrashThread(), "environ")) - NullifyDirectoryEntry(&dirent); - dir.CopyIndex(dir_index++, &dirent); - - dirent.stream_type = MD_LINUX_AUXV; - if (!WriteProcFile(&dirent.location, GetCrashThread(), "auxv")) - NullifyDirectoryEntry(&dirent); - dir.CopyIndex(dir_index++, &dirent); - - dirent.stream_type = MD_LINUX_MAPS; - if (!WriteProcFile(&dirent.location, GetCrashThread(), "maps")) - NullifyDirectoryEntry(&dirent); - dir.CopyIndex(dir_index++, &dirent); - - dirent.stream_type = MD_LINUX_DSO_DEBUG; - if (!WriteDSODebugStream(&dirent)) - NullifyDirectoryEntry(&dirent); - dir.CopyIndex(dir_index++, &dirent); - - // If you add more directory entries, don't forget to update kNumWriters, - // above. - - dumper_->ThreadsResume(); - return true; - } - - bool FillThreadStack(MDRawThread* thread, uintptr_t stack_pointer, - int max_stack_len, uint8_t** stack_copy) { - *stack_copy = NULL; - const void* stack; - size_t stack_len; - if (dumper_->GetStackInfo(&stack, &stack_len, stack_pointer)) { - UntypedMDRVA memory(&minidump_writer_); - if (max_stack_len >= 0 && - stack_len > static_cast<unsigned int>(max_stack_len)) { - stack_len = max_stack_len; - // Skip empty chunks of length max_stack_len. - uintptr_t int_stack = reinterpret_cast<uintptr_t>(stack); - if (max_stack_len > 0) { - while (int_stack + max_stack_len < stack_pointer) { - int_stack += max_stack_len; - } - } - stack = reinterpret_cast<const void*>(int_stack); - } - if (!memory.Allocate(stack_len)) - return false; - *stack_copy = reinterpret_cast<uint8_t*>(Alloc(stack_len)); - dumper_->CopyFromProcess(*stack_copy, thread->thread_id, stack, - stack_len); - memory.Copy(*stack_copy, stack_len); - thread->stack.start_of_memory_range = - reinterpret_cast<uintptr_t>(stack); - thread->stack.memory = memory.location(); - memory_blocks_.push_back(thread->stack); - } else { - thread->stack.start_of_memory_range = stack_pointer; - thread->stack.memory.data_size = 0; - thread->stack.memory.rva = minidump_writer_.position(); - } - return true; - } - - // Write information about the threads. - bool WriteThreadListStream(MDRawDirectory* dirent) { - const unsigned num_threads = dumper_->threads().size(); - - TypedMDRVA<uint32_t> list(&minidump_writer_); - if (!list.AllocateObjectAndArray(num_threads, sizeof(MDRawThread))) - return false; - - dirent->stream_type = MD_THREAD_LIST_STREAM; - dirent->location = list.location(); - - *list.get() = num_threads; - - // If there's a minidump size limit, check if it might be exceeded. Since - // most of the space is filled with stack data, just check against that. - // If this expects to exceed the limit, set extra_thread_stack_len such - // that any thread beyond the first kLimitBaseThreadCount threads will - // have only kLimitMaxExtraThreadStackLen bytes dumped. - int extra_thread_stack_len = -1; // default to no maximum - if (minidump_size_limit_ >= 0) { - const unsigned estimated_total_stack_size = num_threads * - kLimitAverageThreadStackLength; - const off_t estimated_minidump_size = minidump_writer_.position() + - estimated_total_stack_size + kLimitMinidumpFudgeFactor; - if (estimated_minidump_size > minidump_size_limit_) - extra_thread_stack_len = kLimitMaxExtraThreadStackLen; - } - - for (unsigned i = 0; i < num_threads; ++i) { - MDRawThread thread; - my_memset(&thread, 0, sizeof(thread)); - thread.thread_id = dumper_->threads()[i]; - - // We have a different source of information for the crashing thread. If - // we used the actual state of the thread we would find it running in the - // signal handler with the alternative stack, which would be deeply - // unhelpful. - if (static_cast<pid_t>(thread.thread_id) == GetCrashThread() && - ucontext_ && - !dumper_->IsPostMortem()) { - uint8_t* stack_copy; - const uintptr_t stack_ptr = UContextReader::GetStackPointer(ucontext_); - if (!FillThreadStack(&thread, stack_ptr, -1, &stack_copy)) - return false; - - // Copy 256 bytes around crashing instruction pointer to minidump. - const size_t kIPMemorySize = 256; - uint64_t ip = UContextReader::GetInstructionPointer(ucontext_); - // Bound it to the upper and lower bounds of the memory map - // it's contained within. If it's not in mapped memory, - // don't bother trying to write it. - bool ip_is_mapped = false; - MDMemoryDescriptor ip_memory_d; - for (unsigned j = 0; j < dumper_->mappings().size(); ++j) { - const MappingInfo& mapping = *dumper_->mappings()[j]; - if (ip >= mapping.start_addr && - ip < mapping.start_addr + mapping.size) { - ip_is_mapped = true; - // Try to get 128 bytes before and after the IP, but - // settle for whatever's available. - ip_memory_d.start_of_memory_range = - std::max(mapping.start_addr, - uintptr_t(ip - (kIPMemorySize / 2))); - uintptr_t end_of_range = - std::min(uintptr_t(ip + (kIPMemorySize / 2)), - uintptr_t(mapping.start_addr + mapping.size)); - ip_memory_d.memory.data_size = - end_of_range - ip_memory_d.start_of_memory_range; - break; - } - } - - if (ip_is_mapped) { - UntypedMDRVA ip_memory(&minidump_writer_); - if (!ip_memory.Allocate(ip_memory_d.memory.data_size)) - return false; - uint8_t* memory_copy = - reinterpret_cast<uint8_t*>(Alloc(ip_memory_d.memory.data_size)); - dumper_->CopyFromProcess( - memory_copy, - thread.thread_id, - reinterpret_cast<void*>(ip_memory_d.start_of_memory_range), - ip_memory_d.memory.data_size); - ip_memory.Copy(memory_copy, ip_memory_d.memory.data_size); - ip_memory_d.memory = ip_memory.location(); - memory_blocks_.push_back(ip_memory_d); - } - - TypedMDRVA<RawContextCPU> cpu(&minidump_writer_); - if (!cpu.Allocate()) - return false; - my_memset(cpu.get(), 0, sizeof(RawContextCPU)); -#if !defined(__ARM_EABI__) && !defined(__mips__) - UContextReader::FillCPUContext(cpu.get(), ucontext_, float_state_); -#else - UContextReader::FillCPUContext(cpu.get(), ucontext_); -#endif - thread.thread_context = cpu.location(); - crashing_thread_context_ = cpu.location(); - } else { - ThreadInfo info; - if (!dumper_->GetThreadInfoByIndex(i, &info)) - return false; - - uint8_t* stack_copy; - int max_stack_len = -1; // default to no maximum for this thread - if (minidump_size_limit_ >= 0 && i >= kLimitBaseThreadCount) - max_stack_len = extra_thread_stack_len; - if (!FillThreadStack(&thread, info.stack_pointer, max_stack_len, - &stack_copy)) - return false; - - TypedMDRVA<RawContextCPU> cpu(&minidump_writer_); - if (!cpu.Allocate()) - return false; - my_memset(cpu.get(), 0, sizeof(RawContextCPU)); - info.FillCPUContext(cpu.get()); - thread.thread_context = cpu.location(); - if (dumper_->threads()[i] == GetCrashThread()) { - crashing_thread_context_ = cpu.location(); - if (!dumper_->IsPostMortem()) { - // This is the crashing thread of a live process, but - // no context was provided, so set the crash address - // while the instruction pointer is already here. - dumper_->set_crash_address(info.GetInstructionPointer()); - } - } - } - - list.CopyIndexAfterObject(i, &thread, sizeof(thread)); - } - - return true; - } - - // Write application-provided memory regions. - bool WriteAppMemory() { - for (AppMemoryList::const_iterator iter = app_memory_list_.begin(); - iter != app_memory_list_.end(); - ++iter) { - uint8_t* data_copy = - reinterpret_cast<uint8_t*>(dumper_->allocator()->Alloc(iter->length)); - dumper_->CopyFromProcess(data_copy, GetCrashThread(), iter->ptr, - iter->length); - - UntypedMDRVA memory(&minidump_writer_); - if (!memory.Allocate(iter->length)) { - return false; - } - memory.Copy(data_copy, iter->length); - MDMemoryDescriptor desc; - desc.start_of_memory_range = reinterpret_cast<uintptr_t>(iter->ptr); - desc.memory = memory.location(); - memory_blocks_.push_back(desc); - } - - return true; - } - - static bool ShouldIncludeMapping(const MappingInfo& mapping) { - if (mapping.name[0] == 0 || // only want modules with filenames. - // Only want to include one mapping per shared lib. - // Avoid filtering executable mappings. - (mapping.offset != 0 && !mapping.exec) || - mapping.size < 4096) { // too small to get a signature for. - return false; - } - - return true; - } - - // If there is caller-provided information about this mapping - // in the mapping_list_ list, return true. Otherwise, return false. - bool HaveMappingInfo(const MappingInfo& mapping) { - for (MappingList::const_iterator iter = mapping_list_.begin(); - iter != mapping_list_.end(); - ++iter) { - // Ignore any mappings that are wholly contained within - // mappings in the mapping_info_ list. - if (mapping.start_addr >= iter->first.start_addr && - (mapping.start_addr + mapping.size) <= - (iter->first.start_addr + iter->first.size)) { - return true; - } - } - return false; - } - - // Write information about the mappings in effect. Because we are using the - // minidump format, the information about the mappings is pretty limited. - // Because of this, we also include the full, unparsed, /proc/$x/maps file in - // another stream in the file. - bool WriteMappings(MDRawDirectory* dirent) { - const unsigned num_mappings = dumper_->mappings().size(); - unsigned num_output_mappings = mapping_list_.size(); - - for (unsigned i = 0; i < dumper_->mappings().size(); ++i) { - const MappingInfo& mapping = *dumper_->mappings()[i]; - if (ShouldIncludeMapping(mapping) && !HaveMappingInfo(mapping)) - num_output_mappings++; - } - - TypedMDRVA<uint32_t> list(&minidump_writer_); - if (num_output_mappings) { - if (!list.AllocateObjectAndArray(num_output_mappings, MD_MODULE_SIZE)) - return false; - } else { - // Still create the module list stream, although it will have zero - // modules. - if (!list.Allocate()) - return false; - } - - dirent->stream_type = MD_MODULE_LIST_STREAM; - dirent->location = list.location(); - *list.get() = num_output_mappings; - - // First write all the mappings from the dumper - unsigned int j = 0; - for (unsigned i = 0; i < num_mappings; ++i) { - const MappingInfo& mapping = *dumper_->mappings()[i]; - if (!ShouldIncludeMapping(mapping) || HaveMappingInfo(mapping)) - continue; - - MDRawModule mod; - if (!FillRawModule(mapping, true, i, &mod, NULL)) - return false; - list.CopyIndexAfterObject(j++, &mod, MD_MODULE_SIZE); - } - // Next write all the mappings provided by the caller - for (MappingList::const_iterator iter = mapping_list_.begin(); - iter != mapping_list_.end(); - ++iter) { - MDRawModule mod; - if (!FillRawModule(iter->first, false, 0, &mod, iter->second)) - return false; - list.CopyIndexAfterObject(j++, &mod, MD_MODULE_SIZE); - } - - return true; - } - - // Fill the MDRawModule |mod| with information about the provided - // |mapping|. If |identifier| is non-NULL, use it instead of calculating - // a file ID from the mapping. - bool FillRawModule(const MappingInfo& mapping, - bool member, - unsigned int mapping_id, - MDRawModule* mod, - const uint8_t* identifier) { - my_memset(mod, 0, MD_MODULE_SIZE); - - mod->base_of_image = mapping.start_addr; - mod->size_of_image = mapping.size; - - auto_wasteful_vector<uint8_t, kDefaultBuildIdSize> identifier_bytes( - dumper_->allocator()); - - if (identifier) { - // GUID was provided by caller. - identifier_bytes.insert(identifier_bytes.end(), - identifier, - identifier + sizeof(MDGUID)); - } else { - // Note: ElfFileIdentifierForMapping() can manipulate the |mapping.name|. - dumper_->ElfFileIdentifierForMapping(mapping, - member, - mapping_id, - identifier_bytes); - } - - if (!identifier_bytes.empty()) { - UntypedMDRVA cv(&minidump_writer_); - if (!cv.Allocate(MDCVInfoELF_minsize + identifier_bytes.size())) - return false; - - const uint32_t cv_signature = MD_CVINFOELF_SIGNATURE; - cv.Copy(&cv_signature, sizeof(cv_signature)); - cv.Copy(cv.position() + sizeof(cv_signature), &identifier_bytes[0], - identifier_bytes.size()); - - mod->cv_record = cv.location(); - } - - char file_name[NAME_MAX]; - char file_path[NAME_MAX]; - dumper_->GetMappingEffectiveNameAndPath( - mapping, file_path, sizeof(file_path), file_name, sizeof(file_name)); - - MDLocationDescriptor ld; - if (!minidump_writer_.WriteString(file_path, my_strlen(file_path), &ld)) - return false; - mod->module_name_rva = ld.rva; - return true; - } - - bool WriteMemoryListStream(MDRawDirectory* dirent) { - TypedMDRVA<uint32_t> list(&minidump_writer_); - if (memory_blocks_.size()) { - if (!list.AllocateObjectAndArray(memory_blocks_.size(), - sizeof(MDMemoryDescriptor))) - return false; - } else { - // Still create the memory list stream, although it will have zero - // memory blocks. - if (!list.Allocate()) - return false; - } - - dirent->stream_type = MD_MEMORY_LIST_STREAM; - dirent->location = list.location(); - - *list.get() = memory_blocks_.size(); - - for (size_t i = 0; i < memory_blocks_.size(); ++i) { - list.CopyIndexAfterObject(i, &memory_blocks_[i], - sizeof(MDMemoryDescriptor)); - } - return true; - } - - bool WriteExceptionStream(MDRawDirectory* dirent) { - TypedMDRVA<MDRawExceptionStream> exc(&minidump_writer_); - if (!exc.Allocate()) - return false; - my_memset(exc.get(), 0, sizeof(MDRawExceptionStream)); - - dirent->stream_type = MD_EXCEPTION_STREAM; - dirent->location = exc.location(); - - exc.get()->thread_id = GetCrashThread(); - exc.get()->exception_record.exception_code = dumper_->crash_signal(); - exc.get()->exception_record.exception_address = dumper_->crash_address(); - exc.get()->thread_context = crashing_thread_context_; - - return true; - } - - bool WriteSystemInfoStream(MDRawDirectory* dirent) { - TypedMDRVA<MDRawSystemInfo> si(&minidump_writer_); - if (!si.Allocate()) - return false; - my_memset(si.get(), 0, sizeof(MDRawSystemInfo)); - - dirent->stream_type = MD_SYSTEM_INFO_STREAM; - dirent->location = si.location(); - - WriteCPUInformation(si.get()); - WriteOSInformation(si.get()); - - return true; - } - - bool WriteDSODebugStream(MDRawDirectory* dirent) { - ElfW(Phdr)* phdr = reinterpret_cast<ElfW(Phdr) *>(dumper_->auxv()[AT_PHDR]); - char* base; - int phnum = dumper_->auxv()[AT_PHNUM]; - if (!phnum || !phdr) - return false; - - // Assume the program base is at the beginning of the same page as the PHDR - base = reinterpret_cast<char *>(reinterpret_cast<uintptr_t>(phdr) & ~0xfff); - - // Search for the program PT_DYNAMIC segment - ElfW(Addr) dyn_addr = 0; - for (; phnum >= 0; phnum--, phdr++) { - ElfW(Phdr) ph; - if (!dumper_->CopyFromProcess(&ph, GetCrashThread(), phdr, sizeof(ph))) - return false; - - // Adjust base address with the virtual address of the PT_LOAD segment - // corresponding to offset 0 - if (ph.p_type == PT_LOAD && ph.p_offset == 0) { - base -= ph.p_vaddr; - } - if (ph.p_type == PT_DYNAMIC) { - dyn_addr = ph.p_vaddr; - } - } - if (!dyn_addr) - return false; - - ElfW(Dyn) *dynamic = reinterpret_cast<ElfW(Dyn) *>(dyn_addr + base); - - // The dynamic linker makes information available that helps gdb find all - // DSOs loaded into the program. If this information is indeed available, - // dump it to a MD_LINUX_DSO_DEBUG stream. - struct r_debug* r_debug = NULL; - uint32_t dynamic_length = 0; - - for (int i = 0; ; ++i) { - ElfW(Dyn) dyn; - dynamic_length += sizeof(dyn); - if (!dumper_->CopyFromProcess(&dyn, GetCrashThread(), dynamic + i, - sizeof(dyn))) { - return false; - } - -#ifdef __mips__ - const int32_t debug_tag = DT_MIPS_RLD_MAP; -#else - const int32_t debug_tag = DT_DEBUG; -#endif - if (dyn.d_tag == debug_tag) { - r_debug = reinterpret_cast<struct r_debug*>(dyn.d_un.d_ptr); - continue; - } else if (dyn.d_tag == DT_NULL) { - break; - } - } - - // The "r_map" field of that r_debug struct contains a linked list of all - // loaded DSOs. - // Our list of DSOs potentially is different from the ones in the crashing - // process. So, we have to be careful to never dereference pointers - // directly. Instead, we use CopyFromProcess() everywhere. - // See <link.h> for a more detailed discussion of the how the dynamic - // loader communicates with debuggers. - - // Count the number of loaded DSOs - int dso_count = 0; - struct r_debug debug_entry; - if (!dumper_->CopyFromProcess(&debug_entry, GetCrashThread(), r_debug, - sizeof(debug_entry))) { - return false; - } - for (struct link_map* ptr = debug_entry.r_map; ptr; ) { - struct link_map map; - if (!dumper_->CopyFromProcess(&map, GetCrashThread(), ptr, sizeof(map))) - return false; - - ptr = map.l_next; - dso_count++; - } - - MDRVA linkmap_rva = minidump_writer_.kInvalidMDRVA; - if (dso_count > 0) { - // If we have at least one DSO, create an array of MDRawLinkMap - // entries in the minidump file. - TypedMDRVA<MDRawLinkMap> linkmap(&minidump_writer_); - if (!linkmap.AllocateArray(dso_count)) - return false; - linkmap_rva = linkmap.location().rva; - int idx = 0; - - // Iterate over DSOs and write their information to mini dump - for (struct link_map* ptr = debug_entry.r_map; ptr; ) { - struct link_map map; - if (!dumper_->CopyFromProcess(&map, GetCrashThread(), ptr, sizeof(map))) - return false; - - ptr = map.l_next; - char filename[257] = { 0 }; - if (map.l_name) { - dumper_->CopyFromProcess(filename, GetCrashThread(), map.l_name, - sizeof(filename) - 1); - } - MDLocationDescriptor location; - if (!minidump_writer_.WriteString(filename, 0, &location)) - return false; - MDRawLinkMap entry; - entry.name = location.rva; - entry.addr = map.l_addr; - entry.ld = reinterpret_cast<uintptr_t>(map.l_ld); - linkmap.CopyIndex(idx++, &entry); - } - } - - // Write MD_LINUX_DSO_DEBUG record - TypedMDRVA<MDRawDebug> debug(&minidump_writer_); - if (!debug.AllocateObjectAndArray(1, dynamic_length)) - return false; - my_memset(debug.get(), 0, sizeof(MDRawDebug)); - dirent->stream_type = MD_LINUX_DSO_DEBUG; - dirent->location = debug.location(); - - debug.get()->version = debug_entry.r_version; - debug.get()->map = linkmap_rva; - debug.get()->dso_count = dso_count; - debug.get()->brk = debug_entry.r_brk; - debug.get()->ldbase = debug_entry.r_ldbase; - debug.get()->dynamic = reinterpret_cast<uintptr_t>(dynamic); - - wasteful_vector<char> dso_debug_data(dumper_->allocator(), dynamic_length); - // The passed-in size to the constructor (above) is only a hint. - // Must call .resize() to do actual initialization of the elements. - dso_debug_data.resize(dynamic_length); - dumper_->CopyFromProcess(&dso_debug_data[0], GetCrashThread(), dynamic, - dynamic_length); - debug.CopyIndexAfterObject(0, &dso_debug_data[0], dynamic_length); - - return true; - } - - void set_minidump_size_limit(off_t limit) { minidump_size_limit_ = limit; } - - private: - void* Alloc(unsigned bytes) { - return dumper_->allocator()->Alloc(bytes); - } - - pid_t GetCrashThread() const { - return dumper_->crash_thread(); - } - - void NullifyDirectoryEntry(MDRawDirectory* dirent) { - dirent->stream_type = 0; - dirent->location.data_size = 0; - dirent->location.rva = 0; - } - -#if defined(__i386__) || defined(__x86_64__) || defined(__mips__) - bool WriteCPUInformation(MDRawSystemInfo* sys_info) { - char vendor_id[sizeof(sys_info->cpu.x86_cpu_info.vendor_id) + 1] = {0}; - static const char vendor_id_name[] = "vendor_id"; - - struct CpuInfoEntry { - const char* info_name; - int value; - bool found; - } cpu_info_table[] = { - { "processor", -1, false }, -#if defined(__i386__) || defined(__x86_64__) - { "model", 0, false }, - { "stepping", 0, false }, - { "cpu family", 0, false }, -#endif - }; - - // processor_architecture should always be set, do this first - sys_info->processor_architecture = -#if defined(__mips__) -# if _MIPS_SIM == _ABIO32 - MD_CPU_ARCHITECTURE_MIPS; -# elif _MIPS_SIM == _ABI64 - MD_CPU_ARCHITECTURE_MIPS64; -# else -# error "This mips ABI is currently not supported (n32)" -#endif -#elif defined(__i386__) - MD_CPU_ARCHITECTURE_X86; -#else - MD_CPU_ARCHITECTURE_AMD64; -#endif - - const int fd = sys_open("/proc/cpuinfo", O_RDONLY, 0); - if (fd < 0) - return false; - - { - PageAllocator allocator; - ProcCpuInfoReader* const reader = new(allocator) ProcCpuInfoReader(fd); - const char* field; - while (reader->GetNextField(&field)) { - bool is_first_entry = true; - for (CpuInfoEntry& entry : cpu_info_table) { - if (!is_first_entry && entry.found) { - // except for the 'processor' field, ignore repeated values. - continue; - } - is_first_entry = false; - if (!my_strcmp(field, entry.info_name)) { - size_t value_len; - const char* value = reader->GetValueAndLen(&value_len); - if (value_len == 0) - continue; - - uintptr_t val; - if (my_read_decimal_ptr(&val, value) == value) - continue; - - entry.value = static_cast<int>(val); - entry.found = true; - } - } - - // special case for vendor_id - if (!my_strcmp(field, vendor_id_name)) { - size_t value_len; - const char* value = reader->GetValueAndLen(&value_len); - if (value_len > 0) - my_strlcpy(vendor_id, value, sizeof(vendor_id)); - } - } - sys_close(fd); - } - - // make sure we got everything we wanted - for (const CpuInfoEntry& entry : cpu_info_table) { - if (!entry.found) { - return false; - } - } - // cpu_info_table[0] holds the last cpu id listed in /proc/cpuinfo, - // assuming this is the highest id, change it to the number of CPUs - // by adding one. - cpu_info_table[0].value++; - - sys_info->number_of_processors = cpu_info_table[0].value; -#if defined(__i386__) || defined(__x86_64__) - sys_info->processor_level = cpu_info_table[3].value; - sys_info->processor_revision = cpu_info_table[1].value << 8 | - cpu_info_table[2].value; -#endif - - if (vendor_id[0] != '\0') { - my_memcpy(sys_info->cpu.x86_cpu_info.vendor_id, vendor_id, - sizeof(sys_info->cpu.x86_cpu_info.vendor_id)); - } - return true; - } -#elif defined(__arm__) || defined(__aarch64__) - bool WriteCPUInformation(MDRawSystemInfo* sys_info) { - // The CPUID value is broken up in several entries in /proc/cpuinfo. - // This table is used to rebuild it from the entries. - const struct CpuIdEntry { - const char* field; - char format; - char bit_lshift; - char bit_length; - } cpu_id_entries[] = { - { "CPU implementer", 'x', 24, 8 }, - { "CPU variant", 'x', 20, 4 }, - { "CPU part", 'x', 4, 12 }, - { "CPU revision", 'd', 0, 4 }, - }; - - // The ELF hwcaps are listed in the "Features" entry as textual tags. - // This table is used to rebuild them. - const struct CpuFeaturesEntry { - const char* tag; - uint32_t hwcaps; - } cpu_features_entries[] = { -#if defined(__arm__) - { "swp", MD_CPU_ARM_ELF_HWCAP_SWP }, - { "half", MD_CPU_ARM_ELF_HWCAP_HALF }, - { "thumb", MD_CPU_ARM_ELF_HWCAP_THUMB }, - { "26bit", MD_CPU_ARM_ELF_HWCAP_26BIT }, - { "fastmult", MD_CPU_ARM_ELF_HWCAP_FAST_MULT }, - { "fpa", MD_CPU_ARM_ELF_HWCAP_FPA }, - { "vfp", MD_CPU_ARM_ELF_HWCAP_VFP }, - { "edsp", MD_CPU_ARM_ELF_HWCAP_EDSP }, - { "java", MD_CPU_ARM_ELF_HWCAP_JAVA }, - { "iwmmxt", MD_CPU_ARM_ELF_HWCAP_IWMMXT }, - { "crunch", MD_CPU_ARM_ELF_HWCAP_CRUNCH }, - { "thumbee", MD_CPU_ARM_ELF_HWCAP_THUMBEE }, - { "neon", MD_CPU_ARM_ELF_HWCAP_NEON }, - { "vfpv3", MD_CPU_ARM_ELF_HWCAP_VFPv3 }, - { "vfpv3d16", MD_CPU_ARM_ELF_HWCAP_VFPv3D16 }, - { "tls", MD_CPU_ARM_ELF_HWCAP_TLS }, - { "vfpv4", MD_CPU_ARM_ELF_HWCAP_VFPv4 }, - { "idiva", MD_CPU_ARM_ELF_HWCAP_IDIVA }, - { "idivt", MD_CPU_ARM_ELF_HWCAP_IDIVT }, - { "idiv", MD_CPU_ARM_ELF_HWCAP_IDIVA | MD_CPU_ARM_ELF_HWCAP_IDIVT }, -#elif defined(__aarch64__) - // No hwcaps on aarch64. -#endif - }; - - // processor_architecture should always be set, do this first - sys_info->processor_architecture = -#if defined(__aarch64__) - MD_CPU_ARCHITECTURE_ARM64; -#else - MD_CPU_ARCHITECTURE_ARM; -#endif - - // /proc/cpuinfo is not readable under various sandboxed environments - // (e.g. Android services with the android:isolatedProcess attribute) - // prepare for this by setting default values now, which will be - // returned when this happens. - // - // Note: Bogus values are used to distinguish between failures (to - // read /sys and /proc files) and really badly configured kernels. - sys_info->number_of_processors = 0; - sys_info->processor_level = 1U; // There is no ARMv1 - sys_info->processor_revision = 42; - sys_info->cpu.arm_cpu_info.cpuid = 0; - sys_info->cpu.arm_cpu_info.elf_hwcaps = 0; - - // Counting the number of CPUs involves parsing two sysfs files, - // because the content of /proc/cpuinfo will only mirror the number - // of 'online' cores, and thus will vary with time. - // See http://www.kernel.org/doc/Documentation/cputopology.txt - { - CpuSet cpus_present; - CpuSet cpus_possible; - - int fd = sys_open("/sys/devices/system/cpu/present", O_RDONLY, 0); - if (fd >= 0) { - cpus_present.ParseSysFile(fd); - sys_close(fd); - - fd = sys_open("/sys/devices/system/cpu/possible", O_RDONLY, 0); - if (fd >= 0) { - cpus_possible.ParseSysFile(fd); - sys_close(fd); - - cpus_present.IntersectWith(cpus_possible); - int cpu_count = cpus_present.GetCount(); - if (cpu_count > 255) - cpu_count = 255; - sys_info->number_of_processors = static_cast<uint8_t>(cpu_count); - } - } - } - - // Parse /proc/cpuinfo to reconstruct the CPUID value, as well - // as the ELF hwcaps field. For the latter, it would be easier to - // read /proc/self/auxv but unfortunately, this file is not always - // readable from regular Android applications on later versions - // (>= 4.1) of the Android platform. - const int fd = sys_open("/proc/cpuinfo", O_RDONLY, 0); - if (fd < 0) { - // Do not return false here to allow the minidump generation - // to happen properly. - return true; - } - - { - PageAllocator allocator; - ProcCpuInfoReader* const reader = - new(allocator) ProcCpuInfoReader(fd); - const char* field; - while (reader->GetNextField(&field)) { - for (const CpuIdEntry& entry : cpu_id_entries) { - if (my_strcmp(entry.field, field) != 0) - continue; - uintptr_t result = 0; - const char* value = reader->GetValue(); - const char* p = value; - if (value[0] == '0' && value[1] == 'x') { - p = my_read_hex_ptr(&result, value+2); - } else if (entry.format == 'x') { - p = my_read_hex_ptr(&result, value); - } else { - p = my_read_decimal_ptr(&result, value); - } - if (p == value) - continue; - - result &= (1U << entry.bit_length)-1; - result <<= entry.bit_lshift; - sys_info->cpu.arm_cpu_info.cpuid |= - static_cast<uint32_t>(result); - } -#if defined(__arm__) - // Get the architecture version from the "Processor" field. - // Note that it is also available in the "CPU architecture" field, - // however, some existing kernels are misconfigured and will report - // invalid values here (e.g. 6, while the CPU is ARMv7-A based). - // The "Processor" field doesn't have this issue. - if (!my_strcmp(field, "Processor")) { - size_t value_len; - const char* value = reader->GetValueAndLen(&value_len); - // Expected format: <text> (v<level><endian>) - // Where <text> is some text like "ARMv7 Processor rev 2" - // and <level> is a decimal corresponding to the ARM - // architecture number. <endian> is either 'l' or 'b' - // and corresponds to the endianess, it is ignored here. - while (value_len > 0 && my_isspace(value[value_len-1])) - value_len--; - - size_t nn = value_len; - while (nn > 0 && value[nn-1] != '(') - nn--; - if (nn > 0 && value[nn] == 'v') { - uintptr_t arch_level = 5; - my_read_decimal_ptr(&arch_level, value + nn + 1); - sys_info->processor_level = static_cast<uint16_t>(arch_level); - } - } -#elif defined(__aarch64__) - // The aarch64 architecture does not provide the architecture level - // in the Processor field, so we instead check the "CPU architecture" - // field. - if (!my_strcmp(field, "CPU architecture")) { - uintptr_t arch_level = 0; - const char* value = reader->GetValue(); - const char* p = value; - p = my_read_decimal_ptr(&arch_level, value); - if (p == value) - continue; - sys_info->processor_level = static_cast<uint16_t>(arch_level); - } -#endif - // Rebuild the ELF hwcaps from the 'Features' field. - if (!my_strcmp(field, "Features")) { - size_t value_len; - const char* value = reader->GetValueAndLen(&value_len); - - // Parse each space-separated tag. - while (value_len > 0) { - const char* tag = value; - size_t tag_len = value_len; - const char* p = my_strchr(tag, ' '); - if (p) { - tag_len = static_cast<size_t>(p - tag); - value += tag_len + 1; - value_len -= tag_len + 1; - } else { - tag_len = strlen(tag); - value_len = 0; - } - for (const CpuFeaturesEntry& entry : cpu_features_entries) { - if (tag_len == strlen(entry.tag) && - !memcmp(tag, entry.tag, tag_len)) { - sys_info->cpu.arm_cpu_info.elf_hwcaps |= entry.hwcaps; - break; - } - } - } - } - } - sys_close(fd); - } - - return true; - } -#else -# error "Unsupported CPU" -#endif - - bool WriteFile(MDLocationDescriptor* result, const char* filename) { - const int fd = sys_open(filename, O_RDONLY, 0); - if (fd < 0) - return false; - - // We can't stat the files because several of the files that we want to - // read are kernel seqfiles, which always have a length of zero. So we have - // to read as much as we can into a buffer. - static const unsigned kBufSize = 1024 - 2*sizeof(void*); - struct Buffers { - Buffers* next; - size_t len; - uint8_t data[kBufSize]; - } *buffers = reinterpret_cast<Buffers*>(Alloc(sizeof(Buffers))); - buffers->next = NULL; - buffers->len = 0; - - size_t total = 0; - for (Buffers* bufptr = buffers;;) { - ssize_t r; - do { - r = sys_read(fd, &bufptr->data[bufptr->len], kBufSize - bufptr->len); - } while (r == -1 && errno == EINTR); - - if (r < 1) - break; - - total += r; - bufptr->len += r; - if (bufptr->len == kBufSize) { - bufptr->next = reinterpret_cast<Buffers*>(Alloc(sizeof(Buffers))); - bufptr = bufptr->next; - bufptr->next = NULL; - bufptr->len = 0; - } - } - sys_close(fd); - - if (!total) - return false; - - UntypedMDRVA memory(&minidump_writer_); - if (!memory.Allocate(total)) - return false; - for (MDRVA pos = memory.position(); buffers; buffers = buffers->next) { - // Check for special case of a zero-length buffer. This should only - // occur if a file's size happens to be a multiple of the buffer's - // size, in which case the final sys_read() will have resulted in - // zero bytes being read after the final buffer was just allocated. - if (buffers->len == 0) { - // This can only occur with final buffer. - assert(buffers->next == NULL); - continue; - } - memory.Copy(pos, &buffers->data, buffers->len); - pos += buffers->len; - } - *result = memory.location(); - return true; - } - - bool WriteOSInformation(MDRawSystemInfo* sys_info) { -#if defined(__ANDROID__) - sys_info->platform_id = MD_OS_ANDROID; -#else - sys_info->platform_id = MD_OS_LINUX; -#endif - - struct utsname uts; - if (uname(&uts)) - return false; - - static const size_t buf_len = 512; - char buf[buf_len] = {0}; - size_t space_left = buf_len - 1; - const char* info_table[] = { - uts.sysname, - uts.release, - uts.version, - uts.machine, - NULL - }; - bool first_item = true; - for (const char** cur_info = info_table; *cur_info; cur_info++) { - static const char separator[] = " "; - size_t separator_len = sizeof(separator) - 1; - size_t info_len = my_strlen(*cur_info); - if (info_len == 0) - continue; - - if (space_left < info_len + (first_item ? 0 : separator_len)) - break; - - if (!first_item) { - my_strlcat(buf, separator, sizeof(buf)); - space_left -= separator_len; - } - - first_item = false; - my_strlcat(buf, *cur_info, sizeof(buf)); - space_left -= info_len; - } - - MDLocationDescriptor location; - if (!minidump_writer_.WriteString(buf, 0, &location)) - return false; - sys_info->csd_version_rva = location.rva; - - return true; - } - - bool WriteProcFile(MDLocationDescriptor* result, pid_t pid, - const char* filename) { - char buf[NAME_MAX]; - if (!dumper_->BuildProcPath(buf, pid, filename)) - return false; - return WriteFile(result, buf); - } - - // Only one of the 2 member variables below should be set to a valid value. - const int fd_; // File descriptor where the minidum should be written. - const char* path_; // Path to the file where the minidum should be written. - - const ucontext_t* const ucontext_; // also from the signal handler -#if !defined(__ARM_EABI__) && !defined(__mips__) - const google_breakpad::fpstate_t* const float_state_; // ditto -#endif - LinuxDumper* dumper_; - MinidumpFileWriter minidump_writer_; - off_t minidump_size_limit_; - MDLocationDescriptor crashing_thread_context_; - // Blocks of memory written to the dump. These are all currently - // written while writing the thread list stream, but saved here - // so a memory list stream can be written afterwards. - wasteful_vector<MDMemoryDescriptor> memory_blocks_; - // Additional information about some mappings provided by the caller. - const MappingList& mapping_list_; - // Additional memory regions to be included in the dump, - // provided by the caller. - const AppMemoryList& app_memory_list_; -}; - - -bool WriteMinidumpImpl(const char* minidump_path, - int minidump_fd, - off_t minidump_size_limit, - pid_t crashing_process, - const void* blob, size_t blob_size, - const MappingList& mappings, - const AppMemoryList& appmem) { - LinuxPtraceDumper dumper(crashing_process); - const ExceptionHandler::CrashContext* context = NULL; - if (blob) { - if (blob_size != sizeof(ExceptionHandler::CrashContext)) - return false; - context = reinterpret_cast<const ExceptionHandler::CrashContext*>(blob); - dumper.set_crash_address( - reinterpret_cast<uintptr_t>(context->siginfo.si_addr)); - dumper.set_crash_signal(context->siginfo.si_signo); - dumper.set_crash_thread(context->tid); - } - MinidumpWriter writer(minidump_path, minidump_fd, context, mappings, - appmem, &dumper); - // Set desired limit for file size of minidump (-1 means no limit). - writer.set_minidump_size_limit(minidump_size_limit); - if (!writer.Init()) - return false; - return writer.Dump(); -} - -} // namespace - -namespace google_breakpad { - -bool WriteMinidump(const char* minidump_path, pid_t crashing_process, - const void* blob, size_t blob_size) { - return WriteMinidumpImpl(minidump_path, -1, -1, - crashing_process, blob, blob_size, - MappingList(), AppMemoryList()); -} - -bool WriteMinidump(int minidump_fd, pid_t crashing_process, - const void* blob, size_t blob_size) { - return WriteMinidumpImpl(NULL, minidump_fd, -1, - crashing_process, blob, blob_size, - MappingList(), AppMemoryList()); -} - -bool WriteMinidump(const char* minidump_path, pid_t process, - pid_t process_blamed_thread) { - LinuxPtraceDumper dumper(process); - // MinidumpWriter will set crash address - dumper.set_crash_signal(MD_EXCEPTION_CODE_LIN_DUMP_REQUESTED); - dumper.set_crash_thread(process_blamed_thread); - MinidumpWriter writer(minidump_path, -1, NULL, MappingList(), - AppMemoryList(), &dumper); - if (!writer.Init()) - return false; - return writer.Dump(); -} - -bool WriteMinidump(const char* minidump_path, pid_t crashing_process, - const void* blob, size_t blob_size, - const MappingList& mappings, - const AppMemoryList& appmem) { - return WriteMinidumpImpl(minidump_path, -1, -1, crashing_process, - blob, blob_size, - mappings, appmem); -} - -bool WriteMinidump(int minidump_fd, pid_t crashing_process, - const void* blob, size_t blob_size, - const MappingList& mappings, - const AppMemoryList& appmem) { - return WriteMinidumpImpl(NULL, minidump_fd, -1, crashing_process, - blob, blob_size, - mappings, appmem); -} - -bool WriteMinidump(const char* minidump_path, off_t minidump_size_limit, - pid_t crashing_process, - const void* blob, size_t blob_size, - const MappingList& mappings, - const AppMemoryList& appmem) { - return WriteMinidumpImpl(minidump_path, -1, minidump_size_limit, - crashing_process, blob, blob_size, - mappings, appmem); -} - -bool WriteMinidump(int minidump_fd, off_t minidump_size_limit, - pid_t crashing_process, - const void* blob, size_t blob_size, - const MappingList& mappings, - const AppMemoryList& appmem) { - return WriteMinidumpImpl(NULL, minidump_fd, minidump_size_limit, - crashing_process, blob, blob_size, - mappings, appmem); -} - -bool WriteMinidump(const char* filename, - const MappingList& mappings, - const AppMemoryList& appmem, - LinuxDumper* dumper) { - MinidumpWriter writer(filename, -1, NULL, mappings, appmem, dumper); - if (!writer.Init()) - return false; - return writer.Dump(); -} - -} // namespace google_breakpad diff --git a/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/minidump_writer.h b/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/minidump_writer.h deleted file mode 100644 index d13fb120b..000000000 --- a/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/minidump_writer.h +++ /dev/null @@ -1,124 +0,0 @@ -// 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_MINIDUMP_WRITER_MINIDUMP_WRITER_H_ -#define CLIENT_LINUX_MINIDUMP_WRITER_MINIDUMP_WRITER_H_ - -#include <stdint.h> -#include <sys/types.h> -#include <sys/ucontext.h> -#include <unistd.h> - -#include <list> -#include <utility> - -#include "client/linux/minidump_writer/linux_dumper.h" -#include "google_breakpad/common/minidump_format.h" - -namespace google_breakpad { - -class ExceptionHandler; - -#if defined(__aarch64__) -typedef struct fpsimd_context fpstate_t; -#elif !defined(__ARM_EABI__) && !defined(__mips__) -typedef struct _libc_fpstate fpstate_t; -#endif - -// These entries store a list of memory regions that the client wants included -// in the minidump. -struct AppMemory { - void* ptr; - size_t length; - - bool operator==(const struct AppMemory& other) const { - return ptr == other.ptr; - } - - bool operator==(const void* other) const { - return ptr == other; - } -}; -typedef std::list<AppMemory> AppMemoryList; - -// Writes a minidump to the filesystem. These functions do not malloc nor use -// libc functions which may. Thus, it can be used in contexts where the state -// of the heap may be corrupt. -// minidump_path: the path to the file to write to. This is opened O_EXCL and -// fails open fails. -// crashing_process: the pid of the crashing process. This must be trusted. -// blob: a blob of data from the crashing process. See exception_handler.h -// blob_size: the length of |blob|, in bytes -// -// Returns true iff successful. -bool WriteMinidump(const char* minidump_path, pid_t crashing_process, - const void* blob, size_t blob_size); -// Same as above but takes an open file descriptor instead of a path. -bool WriteMinidump(int minidump_fd, pid_t crashing_process, - const void* blob, size_t blob_size); - -// Alternate form of WriteMinidump() that works with processes that -// are not expected to have crashed. If |process_blamed_thread| is -// meaningful, it will be the one from which a crash signature is -// extracted. It is not expected that this function will be called -// from a compromised context, but it is safe to do so. -bool WriteMinidump(const char* minidump_path, pid_t process, - pid_t process_blamed_thread); - -// These overloads also allow passing a list of known mappings and -// a list of additional memory regions to be included in the minidump. -bool WriteMinidump(const char* minidump_path, pid_t crashing_process, - const void* blob, size_t blob_size, - const MappingList& mappings, - const AppMemoryList& appdata); -bool WriteMinidump(int minidump_fd, pid_t crashing_process, - const void* blob, size_t blob_size, - const MappingList& mappings, - const AppMemoryList& appdata); - -// These overloads also allow passing a file size limit for the minidump. -bool WriteMinidump(const char* minidump_path, off_t minidump_size_limit, - pid_t crashing_process, - const void* blob, size_t blob_size, - const MappingList& mappings, - const AppMemoryList& appdata); -bool WriteMinidump(int minidump_fd, off_t minidump_size_limit, - pid_t crashing_process, - const void* blob, size_t blob_size, - const MappingList& mappings, - const AppMemoryList& appdata); - -bool WriteMinidump(const char* filename, - const MappingList& mappings, - const AppMemoryList& appdata, - LinuxDumper* dumper); - -} // namespace google_breakpad - -#endif // CLIENT_LINUX_MINIDUMP_WRITER_MINIDUMP_WRITER_H_ diff --git a/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/minidump_writer_unittest.cc b/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/minidump_writer_unittest.cc deleted file mode 100644 index 2e4749e7e..000000000 --- a/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/minidump_writer_unittest.cc +++ /dev/null @@ -1,775 +0,0 @@ -// Copyright (c) 2011 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 <fcntl.h> -#include <sys/poll.h> -#include <sys/stat.h> -#include <sys/syscall.h> -#include <sys/types.h> -#include <ucontext.h> -#include <unistd.h> - -#include <string> - -#include "breakpad_googletest_includes.h" -#include "client/linux/handler/exception_handler.h" -#include "client/linux/minidump_writer/linux_dumper.h" -#include "client/linux/minidump_writer/minidump_writer.h" -#include "client/linux/minidump_writer/minidump_writer_unittest_utils.h" -#include "common/linux/eintr_wrapper.h" -#include "common/linux/file_id.h" -#include "common/linux/ignore_ret.h" -#include "common/linux/safe_readlink.h" -#include "common/scoped_ptr.h" -#include "common/tests/auto_tempdir.h" -#include "common/tests/file_utils.h" -#include "common/using_std_string.h" -#include "google_breakpad/processor/minidump.h" - -using namespace google_breakpad; - -namespace { - -typedef testing::Test MinidumpWriterTest; - -const char kMDWriterUnitTestFileName[] = "/minidump-writer-unittest"; - -TEST(MinidumpWriterTest, SetupWithPath) { - int fds[2]; - ASSERT_NE(-1, pipe(fds)); - - const pid_t child = fork(); - if (child == 0) { - close(fds[1]); - char b; - IGNORE_RET(HANDLE_EINTR(read(fds[0], &b, sizeof(b)))); - close(fds[0]); - syscall(__NR_exit); - } - close(fds[0]); - - ExceptionHandler::CrashContext context; - memset(&context, 0, sizeof(context)); - - AutoTempDir temp_dir; - string templ = temp_dir.path() + kMDWriterUnitTestFileName; - // Set a non-zero tid to avoid tripping asserts. - context.tid = child; - ASSERT_TRUE(WriteMinidump(templ.c_str(), child, &context, sizeof(context))); - struct stat st; - ASSERT_EQ(0, stat(templ.c_str(), &st)); - ASSERT_GT(st.st_size, 0); - - close(fds[1]); -} - -TEST(MinidumpWriterTest, SetupWithFD) { - int fds[2]; - ASSERT_NE(-1, pipe(fds)); - - const pid_t child = fork(); - if (child == 0) { - close(fds[1]); - char b; - HANDLE_EINTR(read(fds[0], &b, sizeof(b))); - close(fds[0]); - syscall(__NR_exit); - } - close(fds[0]); - - ExceptionHandler::CrashContext context; - memset(&context, 0, sizeof(context)); - - AutoTempDir temp_dir; - string templ = temp_dir.path() + kMDWriterUnitTestFileName; - int fd = open(templ.c_str(), O_CREAT | O_WRONLY, S_IRWXU); - // Set a non-zero tid to avoid tripping asserts. - context.tid = child; - ASSERT_TRUE(WriteMinidump(fd, child, &context, sizeof(context))); - struct stat st; - ASSERT_EQ(0, stat(templ.c_str(), &st)); - ASSERT_GT(st.st_size, 0); - - close(fds[1]); -} - -// Test that mapping info can be specified when writing a minidump, -// and that it ends up in the module list of the minidump. -TEST(MinidumpWriterTest, MappingInfo) { - int fds[2]; - ASSERT_NE(-1, pipe(fds)); - - // These are defined here so the parent can use them to check the - // data from the minidump afterwards. - const uint32_t memory_size = sysconf(_SC_PAGESIZE); - const char* kMemoryName = "a fake module"; - const uint8_t kModuleGUID[sizeof(MDGUID)] = { - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, - 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF - }; - const string module_identifier = "33221100554477668899AABBCCDDEEFF0"; - - // Get some memory. - char* memory = - reinterpret_cast<char*>(mmap(NULL, - memory_size, - PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANON, - -1, - 0)); - const uintptr_t kMemoryAddress = reinterpret_cast<uintptr_t>(memory); - ASSERT_TRUE(memory); - - const pid_t child = fork(); - if (child == 0) { - close(fds[1]); - char b; - IGNORE_RET(HANDLE_EINTR(read(fds[0], &b, sizeof(b)))); - close(fds[0]); - syscall(__NR_exit); - } - close(fds[0]); - - ExceptionHandler::CrashContext context; - memset(&context, 0, sizeof(context)); - ASSERT_EQ(0, getcontext(&context.context)); - context.tid = child; - - AutoTempDir temp_dir; - string templ = temp_dir.path() + kMDWriterUnitTestFileName; - - // Add information about the mapped memory. - MappingInfo info; - info.start_addr = kMemoryAddress; - info.size = memory_size; - info.offset = 0; - info.exec = false; - strcpy(info.name, kMemoryName); - - MappingList mappings; - AppMemoryList memory_list; - MappingEntry mapping; - mapping.first = info; - memcpy(mapping.second, kModuleGUID, sizeof(MDGUID)); - mappings.push_back(mapping); - ASSERT_TRUE(WriteMinidump(templ.c_str(), child, &context, sizeof(context), - mappings, memory_list)); - - // Read the minidump. Load the module list, and ensure that - // the mmap'ed |memory| is listed with the given module name - // and debug ID. - Minidump minidump(templ); - ASSERT_TRUE(minidump.Read()); - - MinidumpModuleList* module_list = minidump.GetModuleList(); - ASSERT_TRUE(module_list); - const MinidumpModule* module = - module_list->GetModuleForAddress(kMemoryAddress); - ASSERT_TRUE(module); - - EXPECT_EQ(kMemoryAddress, module->base_address()); - EXPECT_EQ(memory_size, module->size()); - EXPECT_EQ(kMemoryName, module->code_file()); - EXPECT_EQ(module_identifier, module->debug_identifier()); - - uint32_t len; - // These streams are expected to be there - EXPECT_TRUE(minidump.SeekToStreamType(MD_THREAD_LIST_STREAM, &len)); - EXPECT_TRUE(minidump.SeekToStreamType(MD_MEMORY_LIST_STREAM, &len)); - EXPECT_TRUE(minidump.SeekToStreamType(MD_EXCEPTION_STREAM, &len)); - EXPECT_TRUE(minidump.SeekToStreamType(MD_SYSTEM_INFO_STREAM, &len)); - EXPECT_TRUE(minidump.SeekToStreamType(MD_LINUX_CPU_INFO, &len)); - EXPECT_TRUE(minidump.SeekToStreamType(MD_LINUX_PROC_STATUS, &len)); - EXPECT_TRUE(minidump.SeekToStreamType(MD_LINUX_CMD_LINE, &len)); - EXPECT_TRUE(minidump.SeekToStreamType(MD_LINUX_ENVIRON, &len)); - EXPECT_TRUE(minidump.SeekToStreamType(MD_LINUX_AUXV, &len)); - EXPECT_TRUE(minidump.SeekToStreamType(MD_LINUX_MAPS, &len)); - EXPECT_TRUE(minidump.SeekToStreamType(MD_LINUX_DSO_DEBUG, &len)); - - close(fds[1]); -} - -// Test that a binary with a longer-than-usual build id note -// makes its way all the way through to the minidump unscathed. -// The linux_client_unittest is linked with an explicit --build-id -// in Makefile.am. -TEST(MinidumpWriterTest, BuildIDLong) { - int fds[2]; - ASSERT_NE(-1, pipe(fds)); - - const pid_t child = fork(); - if (child == 0) { - close(fds[1]); - char b; - IGNORE_RET(HANDLE_EINTR(read(fds[0], &b, sizeof(b)))); - close(fds[0]); - syscall(__NR_exit); - } - close(fds[0]); - - ExceptionHandler::CrashContext context; - memset(&context, 0, sizeof(context)); - ASSERT_EQ(0, getcontext(&context.context)); - context.tid = child; - - AutoTempDir temp_dir; - const string dump_path = temp_dir.path() + kMDWriterUnitTestFileName; - - EXPECT_TRUE(WriteMinidump(dump_path.c_str(), - child, &context, sizeof(context))); - close(fds[1]); - - // Read the minidump. Load the module list, and ensure that - // the main module has the correct debug id and code id. - Minidump minidump(dump_path); - ASSERT_TRUE(minidump.Read()); - - MinidumpModuleList* module_list = minidump.GetModuleList(); - ASSERT_TRUE(module_list); - const MinidumpModule* module = module_list->GetMainModule(); - ASSERT_TRUE(module); - const string module_identifier = "030201000504070608090A0B0C0D0E0F0"; - // This is passed explicitly to the linker in Makefile.am - const string build_id = - "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f"; - EXPECT_EQ(module_identifier, module->debug_identifier()); - EXPECT_EQ(build_id, module->code_identifier()); -} - -// Test that mapping info can be specified, and that it overrides -// existing mappings that are wholly contained within the specified -// range. -TEST(MinidumpWriterTest, MappingInfoContained) { - int fds[2]; - ASSERT_NE(-1, pipe(fds)); - - // These are defined here so the parent can use them to check the - // data from the minidump afterwards. - const int32_t memory_size = sysconf(_SC_PAGESIZE); - const char* kMemoryName = "a fake module"; - const uint8_t kModuleGUID[sizeof(MDGUID)] = { - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, - 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF - }; - const string module_identifier = "33221100554477668899AABBCCDDEEFF0"; - - // mmap a file - AutoTempDir temp_dir; - string tempfile = temp_dir.path() + "/minidump-writer-unittest-temp"; - int fd = open(tempfile.c_str(), O_RDWR | O_CREAT, 0); - ASSERT_NE(-1, fd); - unlink(tempfile.c_str()); - // fill with zeros - google_breakpad::scoped_array<char> buffer(new char[memory_size]); - memset(buffer.get(), 0, memory_size); - ASSERT_EQ(memory_size, write(fd, buffer.get(), memory_size)); - lseek(fd, 0, SEEK_SET); - - char* memory = - reinterpret_cast<char*>(mmap(NULL, - memory_size, - PROT_READ | PROT_WRITE, - MAP_PRIVATE, - fd, - 0)); - const uintptr_t kMemoryAddress = reinterpret_cast<uintptr_t>(memory); - ASSERT_TRUE(memory); - close(fd); - - const pid_t child = fork(); - if (child == 0) { - close(fds[1]); - char b; - IGNORE_RET(HANDLE_EINTR(read(fds[0], &b, sizeof(b)))); - close(fds[0]); - syscall(__NR_exit); - } - close(fds[0]); - - ExceptionHandler::CrashContext context; - memset(&context, 0, sizeof(context)); - context.tid = 1; - - string dumpfile = temp_dir.path() + kMDWriterUnitTestFileName; - - // Add information about the mapped memory. Report it as being larger than - // it actually is. - MappingInfo info; - info.start_addr = kMemoryAddress - memory_size; - info.size = memory_size * 3; - info.offset = 0; - info.exec = false; - strcpy(info.name, kMemoryName); - - MappingList mappings; - AppMemoryList memory_list; - MappingEntry mapping; - mapping.first = info; - memcpy(mapping.second, kModuleGUID, sizeof(MDGUID)); - mappings.push_back(mapping); - ASSERT_TRUE(WriteMinidump(dumpfile.c_str(), child, &context, sizeof(context), - mappings, memory_list)); - - // Read the minidump. Load the module list, and ensure that - // the mmap'ed |memory| is listed with the given module name - // and debug ID. - Minidump minidump(dumpfile); - ASSERT_TRUE(minidump.Read()); - - MinidumpModuleList* module_list = minidump.GetModuleList(); - ASSERT_TRUE(module_list); - const MinidumpModule* module = - module_list->GetModuleForAddress(kMemoryAddress); - ASSERT_TRUE(module); - - EXPECT_EQ(info.start_addr, module->base_address()); - EXPECT_EQ(info.size, module->size()); - EXPECT_EQ(kMemoryName, module->code_file()); - EXPECT_EQ(module_identifier, module->debug_identifier()); - - close(fds[1]); -} - -TEST(MinidumpWriterTest, DeletedBinary) { - const string kNumberOfThreadsArgument = "1"; - const string helper_path(GetHelperBinary()); - if (helper_path.empty()) { - FAIL() << "Couldn't find helper binary"; - exit(1); - } - - // Copy binary to a temp file. - AutoTempDir temp_dir; - string binpath = temp_dir.path() + "/linux-dumper-unittest-helper"; - ASSERT_TRUE(CopyFile(helper_path.c_str(), binpath.c_str())) - << "Failed to copy " << helper_path << " to " << binpath; - ASSERT_EQ(0, chmod(binpath.c_str(), 0755)); - - int fds[2]; - ASSERT_NE(-1, pipe(fds)); - - pid_t child_pid = fork(); - if (child_pid == 0) { - // In child process. - close(fds[0]); - - // Pass the pipe fd and the number of threads as arguments. - char pipe_fd_string[8]; - sprintf(pipe_fd_string, "%d", fds[1]); - execl(binpath.c_str(), - binpath.c_str(), - pipe_fd_string, - kNumberOfThreadsArgument.c_str(), - NULL); - } - close(fds[1]); - // Wait for the child process to signal that it's ready. - struct pollfd pfd; - memset(&pfd, 0, sizeof(pfd)); - pfd.fd = fds[0]; - pfd.events = POLLIN | POLLERR; - - const int r = HANDLE_EINTR(poll(&pfd, 1, 1000)); - ASSERT_EQ(1, r); - ASSERT_TRUE(pfd.revents & POLLIN); - uint8_t junk; - const int nr = HANDLE_EINTR(read(fds[0], &junk, sizeof(junk))); - ASSERT_EQ(static_cast<ssize_t>(sizeof(junk)), nr); - close(fds[0]); - - // Child is ready now. - // Unlink the test binary. - unlink(binpath.c_str()); - - ExceptionHandler::CrashContext context; - memset(&context, 0, sizeof(context)); - - string templ = temp_dir.path() + kMDWriterUnitTestFileName; - // Set a non-zero tid to avoid tripping asserts. - context.tid = child_pid; - ASSERT_TRUE(WriteMinidump(templ.c_str(), child_pid, &context, - sizeof(context))); - kill(child_pid, SIGKILL); - - struct stat st; - ASSERT_EQ(0, stat(templ.c_str(), &st)); - ASSERT_GT(st.st_size, 0); - - Minidump minidump(templ); - ASSERT_TRUE(minidump.Read()); - - // Check that the main module filename is correct. - MinidumpModuleList* module_list = minidump.GetModuleList(); - ASSERT_TRUE(module_list); - const MinidumpModule* module = module_list->GetMainModule(); - EXPECT_STREQ(binpath.c_str(), module->code_file().c_str()); - // Check that the file ID is correct. - FileID fileid(helper_path.c_str()); - PageAllocator allocator; - wasteful_vector<uint8_t> identifier(&allocator, kDefaultBuildIdSize); - EXPECT_TRUE(fileid.ElfFileIdentifier(identifier)); - string identifier_string = FileID::ConvertIdentifierToUUIDString(identifier); - string module_identifier(identifier_string); - // Strip out dashes - size_t pos; - while ((pos = module_identifier.find('-')) != string::npos) { - module_identifier.erase(pos, 1); - } - // And append a zero, because module IDs include an "age" field - // which is always zero on Linux. - module_identifier += "0"; - EXPECT_EQ(module_identifier, module->debug_identifier()); -} - -// Test that an additional memory region can be added to the minidump. -TEST(MinidumpWriterTest, AdditionalMemory) { - int fds[2]; - ASSERT_NE(-1, pipe(fds)); - - // These are defined here so the parent can use them to check the - // data from the minidump afterwards. - const uint32_t kMemorySize = sysconf(_SC_PAGESIZE); - - // Get some heap memory. - uint8_t* memory = new uint8_t[kMemorySize]; - const uintptr_t kMemoryAddress = reinterpret_cast<uintptr_t>(memory); - ASSERT_TRUE(memory); - - // Stick some data into the memory so the contents can be verified. - for (uint32_t i = 0; i < kMemorySize; ++i) { - memory[i] = i % 255; - } - - const pid_t child = fork(); - if (child == 0) { - close(fds[1]); - char b; - HANDLE_EINTR(read(fds[0], &b, sizeof(b))); - close(fds[0]); - syscall(__NR_exit); - } - close(fds[0]); - - ExceptionHandler::CrashContext context; - - // This needs a valid context for minidump writing to work, but getting - // a useful one from the child is too much work, so just use one from - // the parent since the child is just a forked copy anyway. - ASSERT_EQ(0, getcontext(&context.context)); - context.tid = child; - - AutoTempDir temp_dir; - string templ = temp_dir.path() + kMDWriterUnitTestFileName; - unlink(templ.c_str()); - - MappingList mappings; - AppMemoryList memory_list; - - // Add the memory region to the list of memory to be included. - AppMemory app_memory; - app_memory.ptr = memory; - app_memory.length = kMemorySize; - memory_list.push_back(app_memory); - ASSERT_TRUE(WriteMinidump(templ.c_str(), child, &context, sizeof(context), - mappings, memory_list)); - - // Read the minidump. Ensure that the memory region is present - Minidump minidump(templ); - ASSERT_TRUE(minidump.Read()); - - MinidumpMemoryList* dump_memory_list = minidump.GetMemoryList(); - ASSERT_TRUE(dump_memory_list); - const MinidumpMemoryRegion* region = - dump_memory_list->GetMemoryRegionForAddress(kMemoryAddress); - ASSERT_TRUE(region); - - EXPECT_EQ(kMemoryAddress, region->GetBase()); - EXPECT_EQ(kMemorySize, region->GetSize()); - - // Verify memory contents. - EXPECT_EQ(0, memcmp(region->GetMemory(), memory, kMemorySize)); - - delete[] memory; - close(fds[1]); -} - -// Test that an invalid thread stack pointer still results in a minidump. -TEST(MinidumpWriterTest, InvalidStackPointer) { - int fds[2]; - ASSERT_NE(-1, pipe(fds)); - - const pid_t child = fork(); - if (child == 0) { - close(fds[1]); - char b; - HANDLE_EINTR(read(fds[0], &b, sizeof(b))); - close(fds[0]); - syscall(__NR_exit); - } - close(fds[0]); - - ExceptionHandler::CrashContext context; - - // This needs a valid context for minidump writing to work, but getting - // a useful one from the child is too much work, so just use one from - // the parent since the child is just a forked copy anyway. - ASSERT_EQ(0, getcontext(&context.context)); - context.tid = child; - - // Fake the child's stack pointer for its crashing thread. NOTE: This must - // be an invalid memory address for the child process (stack or otherwise). - // Try 1MB below the current stack. - uintptr_t invalid_stack_pointer = - reinterpret_cast<uintptr_t>(&context) - 1024*1024; -#if defined(__i386) - context.context.uc_mcontext.gregs[REG_ESP] = invalid_stack_pointer; -#elif defined(__x86_64) - context.context.uc_mcontext.gregs[REG_RSP] = invalid_stack_pointer; -#elif defined(__ARM_EABI__) - context.context.uc_mcontext.arm_sp = invalid_stack_pointer; -#elif defined(__aarch64__) - context.context.uc_mcontext.sp = invalid_stack_pointer; -#elif defined(__mips__) - context.context.uc_mcontext.gregs[MD_CONTEXT_MIPS_REG_SP] = - invalid_stack_pointer; -#else -# error "This code has not been ported to your platform yet." -#endif - - AutoTempDir temp_dir; - string templ = temp_dir.path() + kMDWriterUnitTestFileName; - // NOTE: In previous versions of Breakpad, WriteMinidump() would fail if - // presented with an invalid stack pointer. - ASSERT_TRUE(WriteMinidump(templ.c_str(), child, &context, sizeof(context))); - - // Read the minidump. Ensure that the memory region is present - Minidump minidump(templ); - ASSERT_TRUE(minidump.Read()); - - // TODO(ted.mielczarek,mkrebs): Enable this part of the test once - // https://breakpad.appspot.com/413002/ is committed. -#if 0 - // Make sure there's a thread without a stack. NOTE: It's okay if - // GetThreadList() shows the error: "ERROR: MinidumpThread has a memory - // region problem". - MinidumpThreadList* dump_thread_list = minidump.GetThreadList(); - ASSERT_TRUE(dump_thread_list); - bool found_empty_stack = false; - for (int i = 0; i < dump_thread_list->thread_count(); i++) { - MinidumpThread* thread = dump_thread_list->GetThreadAtIndex(i); - ASSERT_TRUE(thread->thread() != NULL); - // When the stack size is zero bytes, GetMemory() returns NULL. - if (thread->GetMemory() == NULL) { - found_empty_stack = true; - break; - } - } - // NOTE: If you fail this, first make sure that "invalid_stack_pointer" - // above is indeed set to an invalid address. - ASSERT_TRUE(found_empty_stack); -#endif - - close(fds[1]); -} - -// Test that limiting the size of the minidump works. -TEST(MinidumpWriterTest, MinidumpSizeLimit) { - static const int kNumberOfThreadsInHelperProgram = 40; - - char number_of_threads_arg[3]; - sprintf(number_of_threads_arg, "%d", kNumberOfThreadsInHelperProgram); - - string helper_path(GetHelperBinary()); - if (helper_path.empty()) { - FAIL() << "Couldn't find helper binary"; - exit(1); - } - - int fds[2]; - ASSERT_NE(-1, pipe(fds)); - - pid_t child_pid = fork(); - if (child_pid == 0) { - // In child process. - close(fds[0]); - - // Pass the pipe fd and the number of threads as arguments. - char pipe_fd_string[8]; - sprintf(pipe_fd_string, "%d", fds[1]); - execl(helper_path.c_str(), - helper_path.c_str(), - pipe_fd_string, - number_of_threads_arg, - NULL); - } - close(fds[1]); - - // Wait for all child threads to indicate that they have started - for (int threads = 0; threads < kNumberOfThreadsInHelperProgram; threads++) { - struct pollfd pfd; - memset(&pfd, 0, sizeof(pfd)); - pfd.fd = fds[0]; - pfd.events = POLLIN | POLLERR; - - const int r = HANDLE_EINTR(poll(&pfd, 1, 1000)); - ASSERT_EQ(1, r); - ASSERT_TRUE(pfd.revents & POLLIN); - uint8_t junk; - ASSERT_EQ(read(fds[0], &junk, sizeof(junk)), - static_cast<ssize_t>(sizeof(junk))); - } - close(fds[0]); - - // There is a race here because we may stop a child thread before - // it is actually running the busy loop. Empirically this sleep - // is sufficient to avoid the race. - usleep(100000); - - // Child and its threads are ready now. - - - off_t normal_file_size; - int total_normal_stack_size = 0; - AutoTempDir temp_dir; - - // First, write a minidump with no size limit. - { - string normal_dump = temp_dir.path() + - "/minidump-writer-unittest.dmp"; - ASSERT_TRUE(WriteMinidump(normal_dump.c_str(), -1, - child_pid, NULL, 0, - MappingList(), AppMemoryList())); - struct stat st; - ASSERT_EQ(0, stat(normal_dump.c_str(), &st)); - ASSERT_GT(st.st_size, 0); - normal_file_size = st.st_size; - - Minidump minidump(normal_dump); - ASSERT_TRUE(minidump.Read()); - MinidumpThreadList* dump_thread_list = minidump.GetThreadList(); - ASSERT_TRUE(dump_thread_list); - for (unsigned int i = 0; i < dump_thread_list->thread_count(); i++) { - MinidumpThread* thread = dump_thread_list->GetThreadAtIndex(i); - ASSERT_TRUE(thread->thread() != NULL); - // When the stack size is zero bytes, GetMemory() returns NULL. - MinidumpMemoryRegion* memory = thread->GetMemory(); - ASSERT_TRUE(memory != NULL); - total_normal_stack_size += memory->GetSize(); - } - } - - // Second, write a minidump with a size limit big enough to not trigger - // anything. - { - // Set size limit arbitrarily 1MB larger than the normal file size -- such - // that the limiting code will not kick in. - const off_t minidump_size_limit = normal_file_size + 1024*1024; - - string same_dump = temp_dir.path() + - "/minidump-writer-unittest-same.dmp"; - ASSERT_TRUE(WriteMinidump(same_dump.c_str(), minidump_size_limit, - child_pid, NULL, 0, - MappingList(), AppMemoryList())); - struct stat st; - ASSERT_EQ(0, stat(same_dump.c_str(), &st)); - // Make sure limiting wasn't actually triggered. NOTE: If you fail this, - // first make sure that "minidump_size_limit" above is indeed set to a - // large enough value -- the limit-checking code in minidump_writer.cc - // does just a rough estimate. - ASSERT_EQ(normal_file_size, st.st_size); - } - - // Third, write a minidump with a size limit small enough to be triggered. - { - // Set size limit to some arbitrary amount, such that the limiting code - // will kick in. The equation used to set this value was determined by - // simply reversing the size-limit logic a little bit in order to pick a - // size we know will trigger it. The definition of - // kLimitAverageThreadStackLength here was copied from class - // MinidumpWriter in minidump_writer.cc. - static const unsigned kLimitAverageThreadStackLength = 8 * 1024; - off_t minidump_size_limit = kNumberOfThreadsInHelperProgram * - kLimitAverageThreadStackLength; - // If, in reality, each of the threads' stack is *smaller* than - // kLimitAverageThreadStackLength, the normal file size could very well be - // smaller than the arbitrary limit that was just set. In that case, - // either of these numbers should trigger the size-limiting code, but we - // might as well pick the smallest. - if (normal_file_size < minidump_size_limit) - minidump_size_limit = normal_file_size; - - string limit_dump = temp_dir.path() + - "/minidump-writer-unittest-limit.dmp"; - ASSERT_TRUE(WriteMinidump(limit_dump.c_str(), minidump_size_limit, - child_pid, NULL, 0, - MappingList(), AppMemoryList())); - struct stat st; - ASSERT_EQ(0, stat(limit_dump.c_str(), &st)); - ASSERT_GT(st.st_size, 0); - // Make sure the file size is at least smaller than the original. If this - // fails because it's the same size, then the size-limit logic didn't kick - // in like it was supposed to. - EXPECT_LT(st.st_size, normal_file_size); - - Minidump minidump(limit_dump); - ASSERT_TRUE(minidump.Read()); - MinidumpThreadList* dump_thread_list = minidump.GetThreadList(); - ASSERT_TRUE(dump_thread_list); - int total_limit_stack_size = 0; - for (unsigned int i = 0; i < dump_thread_list->thread_count(); i++) { - MinidumpThread* thread = dump_thread_list->GetThreadAtIndex(i); - ASSERT_TRUE(thread->thread() != NULL); - // When the stack size is zero bytes, GetMemory() returns NULL. - MinidumpMemoryRegion* memory = thread->GetMemory(); - ASSERT_TRUE(memory != NULL); - total_limit_stack_size += memory->GetSize(); - } - - // Make sure stack size shrunk by at least 1KB per extra thread. The - // definition of kLimitBaseThreadCount here was copied from class - // MinidumpWriter in minidump_writer.cc. - // Note: The 1KB is arbitrary, and assumes that the thread stacks are big - // enough to shrink by that much. For example, if each thread stack was - // originally only 2KB, the current size-limit logic wouldn't actually - // shrink them because that's the size to which it tries to shrink. If - // you fail this part of the test due to something like that, the test - // logic should probably be improved to account for your situation. - const unsigned kLimitBaseThreadCount = 20; - const unsigned kMinPerExtraThreadStackReduction = 1024; - const int min_expected_reduction = (kNumberOfThreadsInHelperProgram - - kLimitBaseThreadCount) * kMinPerExtraThreadStackReduction; - EXPECT_LT(total_limit_stack_size, - total_normal_stack_size - min_expected_reduction); - } - - // Kill the helper program. - kill(child_pid, SIGKILL); -} - -} // namespace diff --git a/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/minidump_writer_unittest_utils.cc b/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/minidump_writer_unittest_utils.cc deleted file mode 100644 index 9f46fa65c..000000000 --- a/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/minidump_writer_unittest_utils.cc +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright (c) 2011 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. - -// minidump_writer_unittest_utils.cc: -// Shared routines used by unittests under client/linux/minidump_writer. - -#include <limits.h> -#include <stdlib.h> - -#include "client/linux/minidump_writer/minidump_writer_unittest_utils.h" -#include "common/linux/safe_readlink.h" -#include "common/using_std_string.h" - -namespace google_breakpad { - -string GetHelperBinary() { - string helper_path; - char *bindir = getenv("bindir"); - if (bindir) { - helper_path = string(bindir) + "/"; - } else { - // Locate helper binary next to the current binary. - char self_path[PATH_MAX]; - if (!SafeReadLink("/proc/self/exe", self_path)) { - return ""; - } - helper_path = string(self_path); - size_t pos = helper_path.rfind('/'); - if (pos == string::npos) { - return ""; - } - helper_path.erase(pos + 1); - } - - helper_path += "linux_dumper_unittest_helper"; - - return helper_path; -} - -} // namespace google_breakpad diff --git a/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/minidump_writer_unittest_utils.h b/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/minidump_writer_unittest_utils.h deleted file mode 100644 index f16cc086b..000000000 --- a/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/minidump_writer_unittest_utils.h +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright (c) 2012, 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. - -// minidump_writer_unittest_utils.h: -// Shared routines used by unittests under client/linux/minidump_writer. - -#ifndef CLIENT_LINUX_MINIDUMP_WRITER_MINIDUMP_WRITER_UNITTEST_UTILS_H_ -#define CLIENT_LINUX_MINIDUMP_WRITER_MINIDUMP_WRITER_UNITTEST_UTILS_H_ - -#include <string> - -#include "common/using_std_string.h" - -namespace google_breakpad { - -// Returns the full path to linux_dumper_unittest_helper. The full path is -// discovered either by using the environment variable "bindir" or by using -// the location of the main module of the currently running process. -string GetHelperBinary(); - -} // namespace google_breakpad - -#endif // CLIENT_LINUX_MINIDUMP_WRITER_MINIDUMP_WRITER_UNITTEST_UTILS_H_ diff --git a/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/proc_cpuinfo_reader.h b/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/proc_cpuinfo_reader.h deleted file mode 100644 index d9461bf30..000000000 --- a/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/proc_cpuinfo_reader.h +++ /dev/null @@ -1,130 +0,0 @@ -// Copyright (c) 2013, 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_MINIDUMP_WRITER_PROC_CPUINFO_READER_H_ -#define CLIENT_LINUX_MINIDUMP_WRITER_PROC_CPUINFO_READER_H_ - -#include <stdint.h> -#include <assert.h> -#include <string.h> - -#include "client/linux/minidump_writer/line_reader.h" -#include "common/linux/linux_libc_support.h" -#include "third_party/lss/linux_syscall_support.h" - -namespace google_breakpad { - -// A class for reading /proc/cpuinfo without using fopen/fgets or other -// functions which may allocate memory. -class ProcCpuInfoReader { -public: - ProcCpuInfoReader(int fd) - : line_reader_(fd), pop_count_(-1) { - } - - // Return the next field name, or NULL in case of EOF. - // field: (output) Pointer to zero-terminated field name. - // Returns true on success, or false on EOF or error (line too long). - bool GetNextField(const char** field) { - for (;;) { - const char* line; - unsigned line_len; - - // Try to read next line. - if (pop_count_ >= 0) { - line_reader_.PopLine(pop_count_); - pop_count_ = -1; - } - - if (!line_reader_.GetNextLine(&line, &line_len)) - return false; - - pop_count_ = static_cast<int>(line_len); - - const char* line_end = line + line_len; - - // Expected format: <field-name> <space>+ ':' <space> <value> - // Note that: - // - empty lines happen. - // - <field-name> can contain spaces. - // - some fields have an empty <value> - char* sep = static_cast<char*>(my_memchr(line, ':', line_len)); - if (sep == NULL) - continue; - - // Record the value. Skip leading space after the column to get - // its start. - const char* val = sep+1; - while (val < line_end && my_isspace(*val)) - val++; - - value_ = val; - value_len_ = static_cast<size_t>(line_end - val); - - // Remove trailing spaces before the column to properly 0-terminate - // the field name. - while (sep > line && my_isspace(sep[-1])) - sep--; - - if (sep == line) - continue; - - // zero-terminate field name. - *sep = '\0'; - - *field = line; - return true; - } - } - - // Return the field value. This must be called after a succesful - // call to GetNextField(). - const char* GetValue() { - assert(value_); - return value_; - } - - // Same as GetValue(), but also returns the length in characters of - // the value. - const char* GetValueAndLen(size_t* length) { - assert(value_); - *length = value_len_; - return value_; - } - -private: - LineReader line_reader_; - int pop_count_; - const char* value_; - size_t value_len_; -}; - -} // namespace google_breakpad - -#endif // CLIENT_LINUX_MINIDUMP_WRITER_PROC_CPUINFO_READER_H_ diff --git a/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/proc_cpuinfo_reader_unittest.cc b/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/proc_cpuinfo_reader_unittest.cc deleted file mode 100644 index 6037c7e66..000000000 --- a/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/proc_cpuinfo_reader_unittest.cc +++ /dev/null @@ -1,199 +0,0 @@ -// Copyright (c) 2013, 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 <stdlib.h> -#include <unistd.h> -#include <sys/types.h> -#include <stdio.h> -#include <errno.h> -#include <string.h> - -#include "client/linux/minidump_writer/proc_cpuinfo_reader.h" -#include "breakpad_googletest_includes.h" -#include "common/linux/tests/auto_testfile.h" - -using namespace google_breakpad; - -#if !defined(__ANDROID__) -#define TEMPDIR "/tmp" -#else -#define TEMPDIR "/data/local/tmp" -#endif - - -namespace { - -typedef testing::Test ProcCpuInfoReaderTest; - -class ScopedTestFile : public AutoTestFile { -public: - explicit ScopedTestFile(const char* text) - : AutoTestFile("proc_cpuinfo_reader", text) { - } -}; - -} - -TEST(ProcCpuInfoReaderTest, EmptyFile) { - ScopedTestFile file(""); - ASSERT_TRUE(file.IsOk()); - ProcCpuInfoReader reader(file.GetFd()); - - const char *field; - ASSERT_FALSE(reader.GetNextField(&field)); -} - -TEST(ProcCpuInfoReaderTest, OneLineTerminated) { - ScopedTestFile file("foo : bar\n"); - ASSERT_TRUE(file.IsOk()); - ProcCpuInfoReader reader(file.GetFd()); - - const char *field; - ASSERT_TRUE(reader.GetNextField(&field)); - ASSERT_STREQ("foo", field); - ASSERT_STREQ("bar", reader.GetValue()); - - ASSERT_FALSE(reader.GetNextField(&field)); -} - -TEST(ProcCpuInfoReaderTest, OneLine) { - ScopedTestFile file("foo : bar"); - ASSERT_TRUE(file.IsOk()); - ProcCpuInfoReader reader(file.GetFd()); - - const char *field; - size_t value_len; - ASSERT_TRUE(reader.GetNextField(&field)); - ASSERT_STREQ("foo", field); - ASSERT_STREQ("bar", reader.GetValueAndLen(&value_len)); - ASSERT_EQ(3U, value_len); - - ASSERT_FALSE(reader.GetNextField(&field)); -} - -TEST(ProcCpuInfoReaderTest, TwoLinesTerminated) { - ScopedTestFile file("foo : bar\nzoo : tut\n"); - ASSERT_TRUE(file.IsOk()); - ProcCpuInfoReader reader(file.GetFd()); - - const char* field; - ASSERT_TRUE(reader.GetNextField(&field)); - ASSERT_STREQ("foo", field); - ASSERT_STREQ("bar", reader.GetValue()); - - ASSERT_TRUE(reader.GetNextField(&field)); - ASSERT_STREQ("zoo", field); - ASSERT_STREQ("tut", reader.GetValue()); - - ASSERT_FALSE(reader.GetNextField(&field)); -} - -TEST(ProcCpuInfoReaderTest, SkipMalformedLine) { - ScopedTestFile file("this line should have a column\nfoo : bar\n"); - ASSERT_TRUE(file.IsOk()); - ProcCpuInfoReader reader(file.GetFd()); - - const char* field; - ASSERT_TRUE(reader.GetNextField(&field)); - ASSERT_STREQ("foo", field); - ASSERT_STREQ("bar", reader.GetValue()); - - ASSERT_FALSE(reader.GetNextField(&field)); -} - -TEST(ProcCpuInfoReaderTest, SkipOneEmptyLine) { - ScopedTestFile file("\n\nfoo : bar\n"); - ASSERT_TRUE(file.IsOk()); - ProcCpuInfoReader reader(file.GetFd()); - - const char* field; - ASSERT_TRUE(reader.GetNextField(&field)); - ASSERT_STREQ("foo", field); - ASSERT_STREQ("bar", reader.GetValue()); - - ASSERT_FALSE(reader.GetNextField(&field)); -} - -TEST(ProcCpuInfoReaderTest, SkipEmptyField) { - ScopedTestFile file(" : bar\nzoo : tut\n"); - ASSERT_TRUE(file.IsOk()); - ProcCpuInfoReader reader(file.GetFd()); - - const char* field; - ASSERT_TRUE(reader.GetNextField(&field)); - ASSERT_STREQ("zoo", field); - ASSERT_STREQ("tut", reader.GetValue()); - - ASSERT_FALSE(reader.GetNextField(&field)); -} - -TEST(ProcCpuInfoReaderTest, SkipTwoEmptyLines) { - ScopedTestFile file("foo : bar\n\n\nfoo : bar\n"); - ASSERT_TRUE(file.IsOk()); - ProcCpuInfoReader reader(file.GetFd()); - - const char* field; - ASSERT_TRUE(reader.GetNextField(&field)); - ASSERT_STREQ("foo", field); - ASSERT_STREQ("bar", reader.GetValue()); - - ASSERT_TRUE(reader.GetNextField(&field)); - ASSERT_STREQ("foo", field); - ASSERT_STREQ("bar", reader.GetValue()); - - ASSERT_FALSE(reader.GetNextField(&field)); -} - -TEST(ProcCpuInfoReaderTest, FieldWithSpaces) { - ScopedTestFile file("foo bar : zoo\n"); - ASSERT_TRUE(file.IsOk()); - ProcCpuInfoReader reader(file.GetFd()); - - const char* field; - ASSERT_TRUE(reader.GetNextField(&field)); - ASSERT_STREQ("foo bar", field); - ASSERT_STREQ("zoo", reader.GetValue()); - - ASSERT_FALSE(reader.GetNextField(&field)); -} - -TEST(ProcCpuInfoReaderTest, EmptyValue) { - ScopedTestFile file("foo :\n"); - ASSERT_TRUE(file.IsOk()); - ProcCpuInfoReader reader(file.GetFd()); - - const char* field; - ASSERT_TRUE(reader.GetNextField(&field)); - ASSERT_STREQ("foo", field); - size_t value_len; - ASSERT_STREQ("", reader.GetValueAndLen(&value_len)); - ASSERT_EQ(0U, value_len); - - ASSERT_FALSE(reader.GetNextField(&field)); -} diff --git a/toolkit/crashreporter/google-breakpad/src/client/linux/moz.build b/toolkit/crashreporter/google-breakpad/src/client/linux/moz.build deleted file mode 100644 index 9bb8bace3..000000000 --- a/toolkit/crashreporter/google-breakpad/src/client/linux/moz.build +++ /dev/null @@ -1,35 +0,0 @@ -# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- -# vim: set filetype=python: -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -UNIFIED_SOURCES += [ - 'crash_generation/crash_generation_client.cc', - 'crash_generation/crash_generation_server.cc', - 'dump_writer_common/thread_info.cc', - 'dump_writer_common/ucontext_reader.cc', - 'handler/exception_handler.cc', - 'handler/minidump_descriptor.cc', - 'log/log.cc', - 'microdump_writer/microdump_writer.cc', - 'minidump_writer/linux_dumper.cc', - 'minidump_writer/linux_ptrace_dumper.cc', - 'minidump_writer/minidump_writer.cc', -] - -if CONFIG['OS_TARGET'] == 'Android': - LOCAL_INCLUDES += [ - '/toolkit/crashreporter/google-breakpad/src/common/android/include', - ] - -# We allow warnings for third-party code that can be updated from upstream. -ALLOW_COMPILER_WARNINGS = True - -FINAL_LIBRARY = 'xul' - -if CONFIG['OS_TARGET'] == 'Android' and CONFIG['CPU_ARCH'] == 'x86': - # The NDK's user.h defines this struct with a different name. - DEFINES['user_fpxregs_struct'] = 'user_fxsr_struct' - -include('/toolkit/crashreporter/crashreporter.mozbuild') diff --git a/toolkit/crashreporter/google-breakpad/src/client/linux/sender/google_crash_report_sender.cc b/toolkit/crashreporter/google-breakpad/src/client/linux/sender/google_crash_report_sender.cc deleted file mode 100644 index ec6c06e87..000000000 --- a/toolkit/crashreporter/google-breakpad/src/client/linux/sender/google_crash_report_sender.cc +++ /dev/null @@ -1,104 +0,0 @@ -// 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 "common/linux/google_crashdump_uploader.h" -#include "third_party/linux/include/gflags/gflags.h" -#include <string> -#include <iostream> - -#include "common/using_std_string.h" - -DEFINE_string(crash_server, "https://clients2.google.com/cr", - "The crash server to upload minidumps to."); -DEFINE_string(product_name, "", - "The product name that the minidump corresponds to."); -DEFINE_string(product_version, "", - "The version of the product that produced the minidump."); -DEFINE_string(client_id, "", - "The client GUID"); -DEFINE_string(minidump_path, "", - "The path of the minidump file."); -DEFINE_string(ptime, "", - "The process uptime in milliseconds."); -DEFINE_string(ctime, "", - "The cumulative process uptime in milliseconds."); -DEFINE_string(email, "", - "The user's email address."); -DEFINE_string(comments, "", - "Extra user comments"); -DEFINE_string(proxy_host, "", - "Proxy host"); -DEFINE_string(proxy_userpasswd, "", - "Proxy username/password in user:pass format."); - - -bool CheckForRequiredFlagsOrDie() { - string error_text = ""; - if (FLAGS_product_name.empty()) { - error_text.append("\nProduct name must be specified."); - } - - if (FLAGS_product_version.empty()) { - error_text.append("\nProduct version must be specified."); - } - - if (FLAGS_client_id.empty()) { - error_text.append("\nClient ID must be specified."); - } - - if (FLAGS_minidump_path.empty()) { - error_text.append("\nMinidump pathname must be specified."); - } - - if (!error_text.empty()) { - std::cout << error_text; - return false; - } - return true; -} - -int main(int argc, char *argv[]) { - google::InitGoogleLogging(argv[0]); - google::ParseCommandLineFlags(&argc, &argv, true); - if (!CheckForRequiredFlagsOrDie()) { - return 1; - } - google_breakpad::GoogleCrashdumpUploader g(FLAGS_product_name, - FLAGS_product_version, - FLAGS_client_id, - FLAGS_ptime, - FLAGS_ctime, - FLAGS_email, - FLAGS_comments, - FLAGS_minidump_path, - FLAGS_crash_server, - FLAGS_proxy_host, - FLAGS_proxy_userpasswd); - g.Upload(NULL, NULL, NULL); -} |