From 37f20f7b14003bc905e33664d7a4ba99f3e3a58d Mon Sep 17 00:00:00 2001 From: Joshua Peraza Date: Thu, 20 Jul 2017 12:08:56 -0700 Subject: [PATCH] Add ThreadSnapshotLinux Bug: crashpad:30 Change-Id: Iee8eaecadc4b8d61d3975a79fbc7f80dbb39a134 Reviewed-on: https://chromium-review.googlesource.com/580207 Reviewed-by: Mark Mentovai --- compat/android/sched.h | 30 ++++ snapshot/linux/thread_snapshot_linux.cc | 204 ++++++++++++++++++++++++ snapshot/linux/thread_snapshot_linux.h | 82 ++++++++++ snapshot/snapshot.gyp | 2 + 4 files changed, 318 insertions(+) create mode 100644 compat/android/sched.h create mode 100644 snapshot/linux/thread_snapshot_linux.cc create mode 100644 snapshot/linux/thread_snapshot_linux.h diff --git a/compat/android/sched.h b/compat/android/sched.h new file mode 100644 index 00000000..c4a027ae --- /dev/null +++ b/compat/android/sched.h @@ -0,0 +1,30 @@ +// 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_COMPAT_ANDROID_SCHED_H_ +#define CRASHPAD_COMPAT_ANDROID_SCHED_H_ + +#include_next + +// Android 5.0.0 (API 21) NDK + +#if !defined(SCHED_BATCH) +#define SCHED_BATCH 3 +#endif + +#if !defined(SCHED_IDLE) +#define SCHED_IDLE 5 +#endif + +#endif // CRASHPAD_COMPAT_ANDROID_SCHED_H_ diff --git a/snapshot/linux/thread_snapshot_linux.cc b/snapshot/linux/thread_snapshot_linux.cc new file mode 100644 index 00000000..dd902905 --- /dev/null +++ b/snapshot/linux/thread_snapshot_linux.cc @@ -0,0 +1,204 @@ +// 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/thread_snapshot_linux.h" + +#include + +#include "base/logging.h" +#include "snapshot/linux/cpu_context_linux.h" +#include "util/misc/reinterpret_bytes.h" + +namespace crashpad { +namespace internal { + +ThreadSnapshotLinux::ThreadSnapshotLinux() + : ThreadSnapshot(), + context_union_(), + context_(), + stack_(), + thread_specific_data_address_(0), + thread_id_(-1), + priority_(-1), + initialized_() { +} + +ThreadSnapshotLinux::~ThreadSnapshotLinux() { +} + +bool ThreadSnapshotLinux::Initialize( + ProcessReader* process_reader, + const ProcessReader::Thread& thread) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + +#if defined(ARCH_CPU_X86_FAMILY) + if (process_reader->Is64Bit()) { + context_.architecture = kCPUArchitectureX86_64; + context_.x86_64 = &context_union_.x86_64; + InitializeCPUContextX86_64(thread.thread_context.t64, + thread.float_context.f64, + context_.x86_64); + } else { + context_.architecture = kCPUArchitectureX86; + context_.x86 = &context_union_.x86; + InitializeCPUContextX86(thread.thread_context.t32, + thread.float_context.f32, + context_.x86); + } +#else +#error Port. +#endif + + stack_.Initialize(process_reader, + thread.stack_region_address, + thread.stack_region_size); + + thread_specific_data_address_ = + thread.thread_specific_data_address; + + thread_id_ = thread.tid; + + // Map Linux scheduling policy, static priority, and nice value into a single + // int value. + // + // The possible policies in order of approximate priority (low to high) are + // SCHED_IDLE + // SCHED_BATCH + // SCHED_OTHER + // SCHED_RR + // SCHED_FIFO + // + // static_priority is not used for OTHER, BATCH, or IDLE and should be 0. + // For FIFO and RR, static_priority should range from 1 to 99 with 99 being + // the highest priority. + // + // nice value ranges from -20 to 19, with -20 being highest priority + + enum class Policy : uint8_t { + kUnknown = 0, + kIdle, + kBatch, + kOther, + kRR, + kFIFO + }; + + struct LinuxPriority { +#if defined(ARCH_CPU_LITTLE_ENDIAN) + // nice values affect how dynamic priorities are updated, which only matters + // for threads with the same static priority. + uint8_t nice_value = 0; + + // The scheduling policy also affects how threads with the same static + // priority are ordered, but has greater impact than nice value. + Policy policy = Policy::kUnknown; + + // The static priority is the most significant in determining overall + // priority. + uint8_t static_priority = 0; + + // Put this in the most significant byte position to prevent negative + // priorities. + uint8_t unused = 0; +#elif defined(ARCH_CPU_BIG_ENDIAN) + uint8_t unused = 0; + uint8_t static_priority = 0; + Policy policy = Policy::kUnknown; + uint8_t nice_value = 0; +#endif // ARCH_CPU_LITTLE_ENDIAN + }; + static_assert(sizeof(LinuxPriority) <= sizeof(int), "priority is too large"); + + LinuxPriority prio; + + // Lower nice values have higher priority, so negate them and add 20 to put + // them in the range 1-40 with 40 being highest priority. + if (thread.nice_value < -20 || thread.nice_value > 19) { + LOG(WARNING) << "invalid nice value " << thread.nice_value; + prio.nice_value = 0; + } else { + prio.nice_value = -1 * thread.nice_value + 20; + } + + switch (thread.sched_policy) { + case SCHED_IDLE: + prio.policy = Policy::kIdle; + break; + case SCHED_BATCH: + prio.policy = Policy::kBatch; + break; + case SCHED_OTHER: + prio.policy = Policy::kOther; + break; + case SCHED_RR: + prio.policy = Policy::kRR; + break; + case SCHED_FIFO: + prio.policy = Policy::kFIFO; + break; + default: + prio.policy = Policy::kUnknown; + LOG(WARNING) << "Unknown scheduling policy " << thread.sched_policy; + } + + if (thread.static_priority < 0 || thread.static_priority > 99) { + LOG(WARNING) << "invalid static priority " << thread.static_priority; + } + prio.static_priority = thread.static_priority; + + if (!ReinterpretBytes(prio, &priority_)) { + LOG(ERROR) << "Couldn't set priority"; + return false; + } + + INITIALIZATION_STATE_SET_VALID(initialized_); + return true; +} + +const CPUContext* ThreadSnapshotLinux::Context() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return &context_; +} + +const MemorySnapshot* ThreadSnapshotLinux::Stack() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return &stack_; +} + +uint64_t ThreadSnapshotLinux::ThreadID() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return thread_id_; +} + +int ThreadSnapshotLinux::SuspendCount() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return 0; +} + +int ThreadSnapshotLinux::Priority() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return priority_; +} + +uint64_t ThreadSnapshotLinux::ThreadSpecificDataAddress() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return thread_specific_data_address_; +} + +std::vector ThreadSnapshotLinux::ExtraMemory() const { + return std::vector(); +} + +} // namespace internal +} // namespace crashpad diff --git a/snapshot/linux/thread_snapshot_linux.h b/snapshot/linux/thread_snapshot_linux.h new file mode 100644 index 00000000..e0ce7adb --- /dev/null +++ b/snapshot/linux/thread_snapshot_linux.h @@ -0,0 +1,82 @@ +// 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_THREAD_SNAPSHOT_LINUX_H_ +#define CRASHPAD_SNAPSHOT_LINUX_THREAD_SNAPSHOT_LINUX_H_ + +#include + +#include "base/macros.h" +#include "build/build_config.h" +#include "snapshot/cpu_context.h" +#include "snapshot/linux/memory_snapshot_linux.h" +#include "snapshot/linux/process_reader.h" +#include "snapshot/memory_snapshot.h" +#include "snapshot/thread_snapshot.h" +#include "util/misc/initialization_state_dcheck.h" + +namespace crashpad { +namespace internal { + +//! \brief A ThreadSnapshot of a thread on a Linux system. +class ThreadSnapshotLinux final : public ThreadSnapshot { + public: + ThreadSnapshotLinux(); + ~ThreadSnapshotLinux() override; + + //! \brief Initializes the object. + //! + //! \param[in] process_reader A ProcessReader for the process containing the + //! thread. + //! \param[in] thread The thread within the ProcessReader for + //! which the snapshot should be created. + //! + //! \return `true` if the snapshot could be created, `false` otherwise with + //! a message logged. + bool Initialize(ProcessReader* process_reader, + const ProcessReader::Thread& thread); + + // ThreadSnapshot: + + const CPUContext* Context() const override; + const MemorySnapshot* Stack() const override; + uint64_t ThreadID() const override; + int SuspendCount() const override; + int Priority() const override; + uint64_t ThreadSpecificDataAddress() const override; + std::vector ExtraMemory() const override; + + private: +#if defined(ARCH_CPU_X86_FAMILY) + union { + CPUContextX86 x86; + CPUContextX86_64 x86_64; + } context_union_; +#else +#error Port. +#endif // ARCH_CPU_X86_FAMILY + CPUContext context_; + MemorySnapshotLinux stack_; + LinuxVMAddress thread_specific_data_address_; + pid_t thread_id_; + int priority_; + InitializationStateDcheck initialized_; + + DISALLOW_COPY_AND_ASSIGN(ThreadSnapshotLinux); +}; + +} // namespace internal +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_LINUX_THREAD_SNAPSHOT_LINUX_H_ diff --git a/snapshot/snapshot.gyp b/snapshot/snapshot.gyp index 3d872d93..bd6c3d57 100644 --- a/snapshot/snapshot.gyp +++ b/snapshot/snapshot.gyp @@ -54,6 +54,8 @@ 'linux/memory_snapshot_linux.h', 'linux/process_reader.cc', 'linux/process_reader.h', + 'linux/thread_snapshot_linux.cc', + 'linux/thread_snapshot_linux.h', 'mac/cpu_context_mac.cc', 'mac/cpu_context_mac.h', 'mac/exception_snapshot_mac.cc',