diff --git a/client/BUILD.gn b/client/BUILD.gn index a9262b96..9a2f66ea 100644 --- a/client/BUILD.gn +++ b/client/BUILD.gn @@ -44,7 +44,10 @@ static_library("client") { } if (crashpad_is_linux || crashpad_is_android) { - sources += [ "crashpad_client_linux.cc" ] + sources += [ + "crashpad_client_linux.cc", + "simulate_crash_linux.h", + ] } if (crashpad_is_linux || crashpad_is_android || crashpad_is_fuchsia) { diff --git a/client/client.gyp b/client/client.gyp index ecea124b..e149a2c1 100644 --- a/client/client.gyp +++ b/client/client.gyp @@ -50,6 +50,7 @@ 'simple_string_dictionary.h', 'simple_address_range_bag.h', 'simulate_crash.h', + 'simulate_crash_linux.h', 'simulate_crash_mac.cc', 'simulate_crash_mac.h', 'simulate_crash_win.h', @@ -73,6 +74,7 @@ ['OS=="android"', { 'sources/': [ ['include', '^crashpad_client_linux\\.cc$'], + ['include', '^simulate_crash_linux\\.h$'], ], }], ], diff --git a/client/crashpad_client.h b/client/crashpad_client.h index 7cf2eb8a..1a48c181 100644 --- a/client/crashpad_client.h +++ b/client/crashpad_client.h @@ -24,6 +24,7 @@ #include "base/files/file_path.h" #include "base/macros.h" #include "build/build_config.h" +#include "util/misc/capture_context.h" #if defined(OS_MACOSX) #include "base/mac/scoped_mach_port.h" @@ -168,6 +169,17 @@ class CrashpadClient { const std::map& annotations, const std::vector& arguments, int socket); + + //! \brief Requests that the handler capture a dump even though there hasn't + //! been a crash. + //! + //! TODO(jperaza): Floating point information in the context is zeroed out + //! until CaptureContext() supports collecting that information. + //! + //! \param[in] context A NativeCPUContext, generally captured by + //! CaptureContext() or similar. + static void DumpWithoutCrash(NativeCPUContext* context); + #endif // OS_LINUX || OS_ANDROID || DOXYGEN #if defined(OS_MACOSX) || DOXYGEN diff --git a/client/crashpad_client_linux.cc b/client/crashpad_client_linux.cc index a36d9229..12558965 100644 --- a/client/crashpad_client_linux.cc +++ b/client/crashpad_client_linux.cc @@ -27,6 +27,7 @@ #include "util/file/file_io.h" #include "util/linux/exception_handler_client.h" #include "util/linux/exception_information.h" +#include "util/linux/scoped_pr_set_ptracer.h" #include "util/misc/from_pointer_cast.h" #include "util/posix/double_fork_and_exec.h" #include "util/posix/signals.h" @@ -82,7 +83,7 @@ void BuildHandlerArgvStrings( } } -void ConvertArgvStrings(const std::vector argv_strings, +void ConvertArgvStrings(const std::vector& argv_strings, std::vector* argv) { argv->clear(); argv->reserve(argv_strings.size() + 1); @@ -92,8 +93,22 @@ void ConvertArgvStrings(const std::vector argv_strings, argv->push_back(nullptr); } +class SignalHandler { + public: + virtual void HandleCrashFatal(int signo, + siginfo_t* siginfo, + void* context) = 0; + virtual void HandleCrashNonFatal(int signo, + siginfo_t* siginfo, + void* context) = 0; + + protected: + SignalHandler() = default; + ~SignalHandler() = default; +}; + // Launches a single use handler to snapshot this process. -class LaunchAtCrashHandler { +class LaunchAtCrashHandler : public SignalHandler { public: static LaunchAtCrashHandler* Get() { static LaunchAtCrashHandler* instance = new LaunchAtCrashHandler(); @@ -110,6 +125,37 @@ class LaunchAtCrashHandler { return Signals::InstallCrashHandlers(HandleCrash, 0, nullptr); } + void HandleCrashNonFatal(int signo, + siginfo_t* siginfo, + void* context) override { + exception_information_.siginfo_address = + FromPointerCast( + siginfo); + exception_information_.context_address = + FromPointerCast( + context); + exception_information_.thread_id = syscall(SYS_gettid); + + ScopedPrSetPtracer set_ptracer(getpid(), /* may_log= */ false); + + pid_t pid = fork(); + if (pid < 0) { + return; + } + if (pid == 0) { + execv(argv_[0], const_cast(argv_.data())); + _exit(EXIT_FAILURE); + } + + int status; + waitpid(pid, &status, 0); + } + + void HandleCrashFatal(int signo, siginfo_t* siginfo, void* context) override { + HandleCrashNonFatal(signo, siginfo, context); + Signals::RestoreHandlerAndReraiseSignalOnReturn(siginfo, nullptr); + } + private: LaunchAtCrashHandler() = default; @@ -117,27 +163,7 @@ class LaunchAtCrashHandler { static void HandleCrash(int signo, siginfo_t* siginfo, void* context) { auto state = Get(); - auto exception_information = &state->exception_information_; - - exception_information->siginfo_address = - FromPointerCastsiginfo_address)>( - siginfo); - exception_information->context_address = - FromPointerCastcontext_address)>( - context); - exception_information->thread_id = syscall(SYS_gettid); - - pid_t pid = fork(); - if (pid < 0) { - return; - } - if (pid == 0) { - execv(state->argv_[0], const_cast(state->argv_.data())); - return; - } - - int status; - waitpid(pid, &status, 0); + state->HandleCrashFatal(signo, siginfo, context); } std::vector argv_strings_; @@ -147,6 +173,11 @@ class LaunchAtCrashHandler { DISALLOW_COPY_AND_ASSIGN(LaunchAtCrashHandler); }; +// A pointer to the currently installed crash signal handler. This allows +// the static method CrashpadClient::DumpWithoutCrashing to simulate a crash +// using the currently configured crash handling strategy. +static SignalHandler* g_crash_handler; + } // namespace CrashpadClient::CrashpadClient() {} @@ -181,7 +212,12 @@ bool CrashpadClient::StartHandlerAtCrash( handler, database, metrics_dir, url, annotations, arguments, &argv); auto signal_handler = LaunchAtCrashHandler::Get(); - return signal_handler->Initialize(&argv); + if (signal_handler->Initialize(&argv)) { + DCHECK(!g_crash_handler); + g_crash_handler = signal_handler; + return true; + } + return false; } bool CrashpadClient::StartHandlerForClient( @@ -201,4 +237,34 @@ bool CrashpadClient::StartHandlerForClient( return DoubleForkAndExec(argv, socket, true, nullptr); } +// static +void CrashpadClient::DumpWithoutCrash(NativeCPUContext* context) { + if (!g_crash_handler) { + LOG(WARNING) << "No crash handler installed"; + return; + } + +#if defined(ARCH_CPU_X86) + memset(&context->__fpregs_mem, 0, sizeof(context->__fpregs_mem)); + context->__fpregs_mem.status = 0xffff0000; +#elif defined(ARCH_CPU_X86_64) + memset(&context->__fpregs_mem, 0, sizeof(context->__fpregs_mem)); +#elif defined(ARCH_CPU_ARMEL) + memset(context->uc_regspace, 0, sizeof(context->uc_regspace)); +#elif defined(ARCH_CPU_ARM64) + memset(context->uc_mcontext.__reserved, + 0, + sizeof(context->uc_mcontext.__reserved)); +#else +#error Port. +#endif + + siginfo_t siginfo; + siginfo.si_signo = Signals::kSimulatedSigno; + siginfo.si_errno = 0; + siginfo.si_code = 0; + g_crash_handler->HandleCrashNonFatal( + siginfo.si_signo, &siginfo, reinterpret_cast(context)); +} + } // namespace crashpad diff --git a/client/simulate_crash.h b/client/simulate_crash.h index 299fe97c..63e09a17 100644 --- a/client/simulate_crash.h +++ b/client/simulate_crash.h @@ -21,6 +21,8 @@ #include "client/simulate_crash_mac.h" #elif defined(OS_WIN) #include "client/simulate_crash_win.h" +#elif defined(OS_LINUX) || defined(OS_ANDROID) +#include "client/simulate_crash_linux.h" #endif #endif // CRASHPAD_CLIENT_SIMULATE_CRASH_H_ diff --git a/client/simulate_crash_linux.h b/client/simulate_crash_linux.h new file mode 100644 index 00000000..e6c3e487 --- /dev/null +++ b/client/simulate_crash_linux.h @@ -0,0 +1,31 @@ +// Copyright 2018 The Crashpad Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef CRASHPAD_CLIENT_SIMULATE_CRASH_LINUX_H_ +#define CRASHPAD_CLIENT_SIMULATE_CRASH_LINUX_H_ + +#include "client/crashpad_client.h" +#include "util/misc/capture_context.h" + +//! \file + +//! \brief Captures the CPU context and simulates an exception without crashing. +#define CRASHPAD_SIMULATE_CRASH() \ + do { \ + crashpad::NativeCPUContext simulate_crash_cpu_context; \ + crashpad::CaptureContext(&simulate_crash_cpu_context); \ + crashpad::CrashpadClient::DumpWithoutCrash(&simulate_crash_cpu_context); \ + } while (false) + +#endif // CRASHPAD_CLIENT_SIMULATE_CRASH_LINUX_H_ diff --git a/util/misc/capture_context.h b/util/misc/capture_context.h index d12fad6a..5c1838a1 100644 --- a/util/misc/capture_context.h +++ b/util/misc/capture_context.h @@ -37,6 +37,10 @@ using NativeCPUContext = CONTEXT; using NativeCPUContext = ucontext_t; #endif // OS_MACOSX +// No NativeCPUContext defined for Fuchsia yet. +// https://crashpad.chromium.org/bug/196. +#if !defined(OS_FUCHSIA) + //! \brief Saves the CPU context. //! //! The CPU context will be captured as accurately and completely as possible, @@ -76,6 +80,8 @@ using NativeCPUContext = ucontext_t; //! \endcode void CaptureContext(NativeCPUContext* cpu_context); +#endif // !OS_FUCHSIA + } // namespace crashpad #endif // CRASHPAD_UTIL_MISC_CAPTURE_CONTEXT_H_ diff --git a/util/posix/signals.h b/util/posix/signals.h index ade093bf..dc550594 100644 --- a/util/posix/signals.h +++ b/util/posix/signals.h @@ -24,6 +24,9 @@ namespace crashpad { //! \brief Utilities for handling POSIX signals. class Signals { public: + //! \brief A signal number used by Crashpad to simulate signals. + static constexpr int kSimulatedSigno = -1; + //! \brief The type used for `struct sigaction::sa_sigaction`. using Handler = void (*)(int, siginfo_t*, void*);