From d768538e39f3e53dbc1f22ba018737b80641b049 Mon Sep 17 00:00:00 2001 From: Joshua Peraza Date: Fri, 3 Nov 2017 08:54:54 -0700 Subject: [PATCH] Add ProcessSnapshotLinux Bug: crashpad:30 Change-Id: Ie03592aeb91741d957b98716e4d4bb19695a42cf Reviewed-on: https://chromium-review.googlesource.com/604627 Reviewed-by: Mark Mentovai Commit-Queue: Joshua Peraza --- snapshot/linux/process_snapshot_linux.cc | 185 +++++++++++++++++++++++ snapshot/linux/process_snapshot_linux.h | 127 ++++++++++++++++ snapshot/snapshot.gyp | 2 + util/linux/exception_information.h | 45 ++++++ util/util.gyp | 1 + 5 files changed, 360 insertions(+) create mode 100644 snapshot/linux/process_snapshot_linux.cc create mode 100644 snapshot/linux/process_snapshot_linux.h create mode 100644 util/linux/exception_information.h diff --git a/snapshot/linux/process_snapshot_linux.cc b/snapshot/linux/process_snapshot_linux.cc new file mode 100644 index 00000000..346da085 --- /dev/null +++ b/snapshot/linux/process_snapshot_linux.cc @@ -0,0 +1,185 @@ +// Copyright 2017 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 "snapshot/linux/process_snapshot_linux.h" + +#include + +#include "base/logging.h" +#include "util/linux/exception_information.h" + +namespace crashpad { + +ProcessSnapshotLinux::ProcessSnapshotLinux() + : ProcessSnapshot(), + annotations_simple_map_(), + snapshot_time_(), + report_id_(), + client_id_(), + threads_(), + exception_(), + system_(), + process_reader_(), + initialized_() {} + +ProcessSnapshotLinux::~ProcessSnapshotLinux() {} + +bool ProcessSnapshotLinux::Initialize(PtraceConnection* connection) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + + if (gettimeofday(&snapshot_time_, nullptr) != 0) { + PLOG(ERROR) << "gettimeofday"; + return false; + } + + if (!process_reader_.Initialize(connection)) { + return false; + } + + system_.Initialize(&process_reader_, &snapshot_time_); + InitializeThreads(); + + INITIALIZATION_STATE_SET_VALID(initialized_); + return true; +} + +bool ProcessSnapshotLinux::InitializeException( + LinuxVMAddress exception_info_address) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + DCHECK(!exception_); + + ExceptionInformation info; + if (!process_reader_.Memory()->Read( + exception_info_address, sizeof(info), &info)) { + LOG(ERROR) << "Couldn't read exception info"; + return false; + } + + exception_.reset(new internal::ExceptionSnapshotLinux()); + if (!exception_->Initialize(&process_reader_, + info.siginfo_address, + info.context_address, + info.thread_id)) { + exception_.reset(); + return false; + } + + return true; +} + +pid_t ProcessSnapshotLinux::ProcessID() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return process_reader_.ProcessID(); +} + +pid_t ProcessSnapshotLinux::ParentProcessID() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return process_reader_.ParentProcessID(); +} + +void ProcessSnapshotLinux::SnapshotTime(timeval* snapshot_time) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + *snapshot_time = snapshot_time_; +} + +void ProcessSnapshotLinux::ProcessStartTime(timeval* start_time) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + process_reader_.StartTime(start_time); +} + +void ProcessSnapshotLinux::ProcessCPUTimes(timeval* user_time, + timeval* system_time) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + process_reader_.CPUTimes(user_time, system_time); +} + +void ProcessSnapshotLinux::ReportID(UUID* report_id) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + *report_id = report_id_; +} + +void ProcessSnapshotLinux::ClientID(UUID* client_id) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + *client_id = client_id_; +} + +const std::map& +ProcessSnapshotLinux::AnnotationsSimpleMap() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return annotations_simple_map_; +} + +const SystemSnapshot* ProcessSnapshotLinux::System() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return &system_; +} + +std::vector ProcessSnapshotLinux::Threads() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + std::vector threads; + for (const auto& thread : threads_) { + threads.push_back(thread.get()); + } + return threads; +} + +std::vector ProcessSnapshotLinux::Modules() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + // TODO(jperaza): do this. + LOG(ERROR) << "Not implemented"; + return std::vector(); +} + +std::vector ProcessSnapshotLinux::UnloadedModules() + const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + // TODO(jperaza): Can this be implemented on Linux? + return std::vector(); +} + +const ExceptionSnapshot* ProcessSnapshotLinux::Exception() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return exception_.get(); +} + +std::vector ProcessSnapshotLinux::MemoryMap() + const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + // TODO(jperaza): do this. + return std::vector(); +} + +std::vector ProcessSnapshotLinux::Handles() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return std::vector(); +} + +std::vector ProcessSnapshotLinux::ExtraMemory() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return std::vector(); +} + +void ProcessSnapshotLinux::InitializeThreads() { + const std::vector& process_reader_threads = + process_reader_.Threads(); + for (const ProcessReader::Thread& process_reader_thread : + process_reader_threads) { + auto thread = std::make_unique(); + if (thread->Initialize(&process_reader_, process_reader_thread)) { + threads_.push_back(std::move(thread)); + } + } +} + +} // namespace crashpad diff --git a/snapshot/linux/process_snapshot_linux.h b/snapshot/linux/process_snapshot_linux.h new file mode 100644 index 00000000..446ddb54 --- /dev/null +++ b/snapshot/linux/process_snapshot_linux.h @@ -0,0 +1,127 @@ +// Copyright 2017 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_SNAPSHOT_LINUX_PROCESS_SNAPSHOT_LINUX_H_ +#define CRASHPAD_SNAPSHOT_LINUX_PROCESS_SNAPSHOT_LINUX_H_ + +#include +#include + +#include +#include +#include +#include + +#include "base/macros.h" +#include "snapshot/linux/exception_snapshot_linux.h" +#include "snapshot/linux/process_reader.h" +#include "snapshot/linux/system_snapshot_linux.h" +#include "snapshot/linux/thread_snapshot_linux.h" +#include "snapshot/memory_map_region_snapshot.h" +#include "snapshot/module_snapshot.h" +#include "snapshot/process_snapshot.h" +#include "snapshot/system_snapshot.h" +#include "snapshot/thread_snapshot.h" +#include "snapshot/unloaded_module_snapshot.h" +#include "util/linux/ptrace_connection.h" +#include "util/misc/initialization_state_dcheck.h" +#include "util/misc/uuid.h" + +namespace crashpad { + +//! \brief A ProcessSnapshot of a running (or crashed) process running on a +//! Linux system. +class ProcessSnapshotLinux final : public ProcessSnapshot { + public: + ProcessSnapshotLinux(); + ~ProcessSnapshotLinux() override; + + //! \brief Initializes the object. + //! + //! \param[in] connection A connection to the process to snapshot. + //! + //! \return `true` if the snapshot could be created, `false` otherwise with + //! an appropriate message logged. + bool Initialize(PtraceConnection* connection); + + //! \brief Initializes the object's exception. + //! + //! \param[in] exception_info The address of an ExceptionInformation in the + //! target process' address space. + bool InitializeException(LinuxVMAddress exception_info); + + //! \brief Sets the value to be returned by ReportID(). + //! + //! The crash report ID is under the control of the snapshot + //! producer, which may call this method to set the report ID. If this is not + //! done, ReportID() will return an identifier consisting entirely of zeroes. + void SetReportID(const UUID& report_id) { report_id_ = report_id; } + + //! \brief Sets the value to be returned by ClientID(). + //! + //! The client ID is under the control of the snapshot producer, + //! which may call this method to set the client ID. If this is not done, + //! ClientID() will return an identifier consisting entirely of zeroes. + void SetClientID(const UUID& client_id) { client_id_ = client_id; } + + //! \brief Sets the value to be returned by AnnotationsSimpleMap(). + //! + //! All process annotations are under the control of the snapshot + //! producer, which may call this method to establish these annotations. + //! Contrast this with module annotations, which are under the control of the + //! process being snapshotted. + void SetAnnotationsSimpleMap( + const std::map& annotations_simple_map) { + annotations_simple_map_ = annotations_simple_map; + } + + // ProcessSnapshot: + + pid_t ProcessID() const override; + pid_t ParentProcessID() const override; + void SnapshotTime(timeval* snapshot_time) const override; + void ProcessStartTime(timeval* start_time) const override; + void ProcessCPUTimes(timeval* user_time, timeval* system_time) const override; + void ReportID(UUID* report_id) const override; + void ClientID(UUID* client_id) const override; + const std::map& AnnotationsSimpleMap() + const override; + const SystemSnapshot* System() const override; + std::vector Threads() const override; + std::vector Modules() const override; + std::vector UnloadedModules() const override; + const ExceptionSnapshot* Exception() const override; + std::vector MemoryMap() const override; + std::vector Handles() const override; + std::vector ExtraMemory() const override; + + private: + void InitializeThreads(); + + std::map annotations_simple_map_; + timeval snapshot_time_; + UUID report_id_; + UUID client_id_; + std::vector> threads_; + std::unique_ptr exception_; + internal::SystemSnapshotLinux system_; + ProcessReader process_reader_; + InitializationStateDcheck initialized_; + + DISALLOW_COPY_AND_ASSIGN(ProcessSnapshotLinux); +}; + +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_LINUX_PROCESS_SNAPSHOT_LINUX_H_ diff --git a/snapshot/snapshot.gyp b/snapshot/snapshot.gyp index d50d1085..d2fc91f5 100644 --- a/snapshot/snapshot.gyp +++ b/snapshot/snapshot.gyp @@ -58,6 +58,8 @@ 'linux/memory_snapshot_linux.h', 'linux/process_reader.cc', 'linux/process_reader.h', + 'linux/process_snapshot_linux.cc', + 'linux/process_snapshot_linux.h', 'linux/signal_context.h', 'linux/system_snapshot_linux.cc', 'linux/system_snapshot_linux.h', diff --git a/util/linux/exception_information.h b/util/linux/exception_information.h new file mode 100644 index 00000000..d7dbe529 --- /dev/null +++ b/util/linux/exception_information.h @@ -0,0 +1,45 @@ +// Copyright 2017 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_UTIL_LINUX_EXCEPTION_INFORMATION_H_ +#define CRASHPAD_UTIL_LINUX_EXCEPTION_INFORMATION_H_ + +#include + +#include "util/linux/address_types.h" + +namespace crashpad { + +#pragma pack(push, 1) + +//! \brief Structure read out of the client process by the crash handler when an +//! exception occurs. +struct ExceptionInformation { + //! \brief The address of the `siginfo_t` passed to the signal handler in the + //! crashed process. + LinuxVMAddress siginfo_address; + + //! \brief The address of the `ucontext_t` passed to the signal handler in the + //! crashed process. + LinuxVMAddress context_address; + + //! \brief The thread ID of the thread which received the signal. + pid_t thread_id; +}; + +#pragma pack(pop) + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_LINUX_EXCEPTION_INFORMATION_H_ diff --git a/util/util.gyp b/util/util.gyp index 2b463c62..eb0794ea 100644 --- a/util/util.gyp +++ b/util/util.gyp @@ -65,6 +65,7 @@ 'linux/ptrace_connection.h', 'linux/ptracer.cc', 'linux/ptracer.h', + 'linux/exception_information.h', 'linux/scoped_ptrace_attach.cc', 'linux/scoped_ptrace_attach.h', 'linux/thread_info.cc',