From 0429216f59b10f191eae77904ab1476b3b9bc273 Mon Sep 17 00:00:00 2001 From: Joshua Peraza Date: Fri, 16 Feb 2018 09:13:35 -0800 Subject: [PATCH] linux: Add CrashReportExceptionHandler Bug: crashpad:30 Change-Id: I2855b34abe34f6d665539de0e4a227c933bd2b8c Reviewed-on: https://chromium-review.googlesource.com/922923 Commit-Queue: Joshua Peraza Reviewed-by: Mark Mentovai --- handler/BUILD.gn | 2 + handler/handler.gyp | 2 + .../linux/crash_report_exception_handler.cc | 153 ++++++++++++++++++ .../linux/crash_report_exception_handler.h | 86 ++++++++++ handler/win/crash_report_exception_handler.cc | 1 - util/misc/metrics.h | 10 ++ 6 files changed, 253 insertions(+), 1 deletion(-) create mode 100644 handler/linux/crash_report_exception_handler.cc create mode 100644 handler/linux/crash_report_exception_handler.h diff --git a/handler/BUILD.gn b/handler/BUILD.gn index 728290a6..7cf4c3d9 100644 --- a/handler/BUILD.gn +++ b/handler/BUILD.gn @@ -39,6 +39,8 @@ static_library("handler") { if (crashpad_is_linux || crashpad_is_android) { sources += [ + "linux/crash_report_exception_handler.cc", + "linux/crash_report_exception_handler.h", "linux/exception_handler_server.cc", "linux/exception_handler_server.h", ] diff --git a/handler/handler.gyp b/handler/handler.gyp index 8263dfde..fb5de2a8 100644 --- a/handler/handler.gyp +++ b/handler/handler.gyp @@ -39,6 +39,8 @@ 'crash_report_upload_thread.h', 'handler_main.cc', 'handler_main.h', + 'linux/crash_report_exception_handler.cc', + 'linux/crash_report_exception_handler.h', 'linux/exception_handler_server.cc', 'linux/exception_handler_server.h', 'mac/crash_report_exception_handler.cc', diff --git a/handler/linux/crash_report_exception_handler.cc b/handler/linux/crash_report_exception_handler.cc new file mode 100644 index 00000000..3aa46981 --- /dev/null +++ b/handler/linux/crash_report_exception_handler.cc @@ -0,0 +1,153 @@ +// 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. + +#include "handler/linux/crash_report_exception_handler.h" + +#include + +#include "base/logging.h" +#include "client/settings.h" +#include "minidump/minidump_file_writer.h" +#include "snapshot/crashpad_info_client_options.h" +#include "snapshot/linux/process_snapshot_linux.h" +#include "util/linux/direct_ptrace_connection.h" +#include "util/linux/ptrace_client.h" +#include "util/misc/metrics.h" +#include "util/misc/tri_state.h" +#include "util/misc/uuid.h" + +namespace crashpad { + +CrashReportExceptionHandler::CrashReportExceptionHandler( + CrashReportDatabase* database, + CrashReportUploadThread* upload_thread, + const std::map* process_annotations, + const UserStreamDataSources* user_stream_data_sources) + : database_(database), + upload_thread_(upload_thread), + process_annotations_(process_annotations), + user_stream_data_sources_(user_stream_data_sources) {} + +CrashReportExceptionHandler::~CrashReportExceptionHandler() = default; + +bool CrashReportExceptionHandler::HandleException( + pid_t client_process_id, + VMAddress exception_info_address) { + Metrics::ExceptionEncountered(); + + DirectPtraceConnection connection; + if (!connection.Initialize(client_process_id)) { + Metrics::ExceptionCaptureResult( + Metrics::CaptureResult::kDirectPtraceFailed); + return false; + } + + return HandleExceptionWithConnection(&connection, exception_info_address); +} + +bool CrashReportExceptionHandler::HandleExceptionWithBroker( + pid_t client_process_id, + VMAddress exception_info_address, + int broker_sock) { + Metrics::ExceptionEncountered(); + + PtraceClient client; + if (client.Initialize(broker_sock, client_process_id)) { + Metrics::ExceptionCaptureResult( + Metrics::CaptureResult::kBrokeredPtraceFailed); + return false; + } + + return HandleExceptionWithConnection(&client, exception_info_address); +} + +bool CrashReportExceptionHandler::HandleExceptionWithConnection( + PtraceConnection* connection, + VMAddress exception_info_address) { + ProcessSnapshotLinux process_snapshot; + if (!process_snapshot.Initialize(connection)) { + Metrics::ExceptionCaptureResult(Metrics::CaptureResult::kSnapshotFailed); + return false; + } + + if (!process_snapshot.InitializeException(exception_info_address)) { + Metrics::ExceptionCaptureResult( + Metrics::CaptureResult::kExceptionInitializationFailed); + return false; + } + + Metrics::ExceptionCode(process_snapshot.Exception()->Exception()); + + CrashpadInfoClientOptions client_options; + process_snapshot.GetCrashpadOptions(&client_options); + if (client_options.crashpad_handler_behavior != TriState::kDisabled) { + UUID client_id; + Settings* const settings = database_->GetSettings(); + if (settings) { + // If GetSettings() or GetClientID() fails, something else will log a + // message and client_id will be left at its default value, all zeroes, + // which is appropriate. + settings->GetClientID(&client_id); + } + + process_snapshot.SetClientID(client_id); + process_snapshot.SetAnnotationsSimpleMap(*process_annotations_); + + std::unique_ptr new_report; + CrashReportDatabase::OperationStatus database_status = + database_->PrepareNewCrashReport(&new_report); + if (database_status != CrashReportDatabase::kNoError) { + LOG(ERROR) << "PrepareNewCrashReport failed"; + Metrics::ExceptionCaptureResult( + Metrics::CaptureResult::kPrepareNewCrashReportFailed); + return false; + } + + process_snapshot.SetReportID(new_report->ReportID()); + + MinidumpFileWriter minidump; + minidump.InitializeFromSnapshot(&process_snapshot); + AddUserExtensionStreams( + user_stream_data_sources_, &process_snapshot, &minidump); + + if (!minidump.WriteEverything(new_report->Writer())) { + LOG(ERROR) << "WriteEverything failed"; + Metrics::ExceptionCaptureResult( + Metrics::CaptureResult::kMinidumpWriteFailed); + return false; + } + + UUID uuid; + database_status = + database_->FinishedWritingCrashReport(std::move(new_report), &uuid); + if (database_status != CrashReportDatabase::kNoError) { + LOG(ERROR) << "FinishedWritingCrashReport failed"; + Metrics::ExceptionCaptureResult( + Metrics::CaptureResult::kFinishedWritingCrashReportFailed); + return false; + } + + if (upload_thread_) { + upload_thread_->ReportPending(uuid); + } else { + database_->SkipReportUpload( + uuid, Metrics::CrashSkippedReason::kUploadsDisabled); + } + } + + Metrics::ExceptionCaptureResult(Metrics::CaptureResult::kSuccess); + return true; +} + +} // namespace crashpad diff --git a/handler/linux/crash_report_exception_handler.h b/handler/linux/crash_report_exception_handler.h new file mode 100644 index 00000000..ca14f81a --- /dev/null +++ b/handler/linux/crash_report_exception_handler.h @@ -0,0 +1,86 @@ +// 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_HANDLER_LINUX_CRASH_REPORT_EXCEPTION_HANDLER_H_ +#define CRASHPAD_HANDLER_LINUX_CRASH_REPORT_EXCEPTION_HANDLER_H_ + +#include +#include + +#include "base/macros.h" +#include "client/crash_report_database.h" +#include "handler/crash_report_upload_thread.h" +#include "handler/linux/exception_handler_server.h" +#include "handler/user_stream_data_source.h" +#include "util/linux/ptrace_connection.h" +#include "util/misc/address_types.h" + +namespace crashpad { + +//! \brief An exception handler that writes crash reports for exceptions +//! to a CrashReportDatabase. +class CrashReportExceptionHandler : public ExceptionHandlerServer::Delegate { + public: + //! \brief Creates a new object that will store crash reports in \a database. + //! + //! \param[in] database The database to store crash reports in. Weak. + //! \param[in] upload_thread The upload thread to notify when a new crash + //! report is written into \a database. Report upload is skipped if this + //! value is `nullptr`. + //! \param[in] process_annotations A map of annotations to insert as + //! process-level annotations into each crash report that is written. Do + //! not confuse this with module-level annotations, which are under the + //! control of the crashing process, and are used to implement Chrome’s + //! “crash keys.” Process-level annotations are those that are beyond the + //! control of the crashing process, which must reliably be set even if + //! the process crashes before it’s able to establish its own annotations. + //! To interoperate with Breakpad servers, the recommended practice is to + //! specify values for the `"prod"` and `"ver"` keys as process + //! annotations. + //! \param[in] user_stream_data_sources Data sources to be used to extend + //! crash reports. For each crash report that is written, the data sources + //! are called in turn. These data sources may contribute additional + //! minidump streams. `nullptr` if not required. + CrashReportExceptionHandler( + CrashReportDatabase* database, + CrashReportUploadThread* upload_thread, + const std::map* process_annotations, + const UserStreamDataSources* user_stream_data_sources); + + ~CrashReportExceptionHandler(); + + // ExceptionHandlerServer::Delegate: + + bool HandleException(pid_t client_process_id, + VMAddress exception_info_address) override; + + bool HandleExceptionWithBroker(pid_t client_process_id, + VMAddress exception_info_address, + int broker_sock) override; + + private: + bool HandleExceptionWithConnection(PtraceConnection* connection, + VMAddress exception_info_address); + + CrashReportDatabase* database_; // weak + CrashReportUploadThread* upload_thread_; // weak + const std::map* process_annotations_; // weak + const UserStreamDataSources* user_stream_data_sources_; // weak + + DISALLOW_COPY_AND_ASSIGN(CrashReportExceptionHandler); +}; + +} // namespace crashpad + +#endif // CRASHPAD_HANDLER_LINUX_CRASH_REPORT_EXCEPTION_HANDLER_H_ diff --git a/handler/win/crash_report_exception_handler.cc b/handler/win/crash_report_exception_handler.cc index b1ea8446..726dcb39 100644 --- a/handler/win/crash_report_exception_handler.cc +++ b/handler/win/crash_report_exception_handler.cc @@ -60,7 +60,6 @@ unsigned int CrashReportExceptionHandler::ExceptionHandlerServerException( ProcessSuspensionState::kSuspended, exception_information_address, debug_critical_section_address)) { - LOG(WARNING) << "ProcessSnapshotWin::Initialize failed"; Metrics::ExceptionCaptureResult(Metrics::CaptureResult::kSnapshotFailed); return kTerminationCodeSnapshotFailed; } diff --git a/util/misc/metrics.h b/util/misc/metrics.h index fbbf3ecb..54ccb843 100644 --- a/util/misc/metrics.h +++ b/util/misc/metrics.h @@ -119,6 +119,16 @@ class Metrics { //! \brief There was a database error in attempt to complete the report. kFinishedWritingCrashReportFailed = 7, + //! \brief An attempt to directly `ptrace` the target failed. + //! + //! This value is only used on Linux/Android. + kDirectPtraceFailed = 8, + + //! \brief An attempt to `ptrace` via a PtraceBroker failed. + //! + //! This value is only used on Linux/Android. + kBrokeredPtraceFailed = 9, + //! \brief The number of values in this enumeration; not a valid value. kMaxValue };