diff --git a/snapshot/mac/memory_snapshot_mac.cc b/snapshot/mac/memory_snapshot_mac.cc index b0cce244..4ded2512 100644 --- a/snapshot/mac/memory_snapshot_mac.cc +++ b/snapshot/mac/memory_snapshot_mac.cc @@ -14,6 +14,7 @@ #include "snapshot/mac/memory_snapshot_mac.h" +#include "base/memory/scoped_ptr.h" #include "util/mach/task_memory.h" namespace crashpad { diff --git a/snapshot/snapshot.gyp b/snapshot/snapshot.gyp index dcb386f6..0eda1b6b 100644 --- a/snapshot/snapshot.gyp +++ b/snapshot/snapshot.gyp @@ -88,6 +88,8 @@ 'system_snapshot.h', 'thread_snapshot.h', 'win/module_snapshot_win.cc', + 'win/memory_snapshot_win.cc', + 'win/memory_snapshot_win.h', 'win/module_snapshot_win.h', 'win/pe_image_reader.cc', 'win/pe_image_reader.h', @@ -97,6 +99,8 @@ 'win/process_snapshot_win.h', 'win/system_snapshot_win.cc', 'win/system_snapshot_win.h', + 'win/thread_snapshot_win.cc', + 'win/thread_snapshot_win.h', ], 'conditions': [ ['OS=="win"', { diff --git a/snapshot/win/memory_snapshot_win.cc b/snapshot/win/memory_snapshot_win.cc new file mode 100644 index 00000000..1861a885 --- /dev/null +++ b/snapshot/win/memory_snapshot_win.cc @@ -0,0 +1,70 @@ +// Copyright 2015 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/win/memory_snapshot_win.h" + +#include "base/memory/scoped_ptr.h" + +namespace crashpad { +namespace internal { + +MemorySnapshotWin::MemorySnapshotWin() + : MemorySnapshot(), + process_reader_(nullptr), + address_(0), + size_(0), + initialized_() { +} + +MemorySnapshotWin::~MemorySnapshotWin() { +} + +void MemorySnapshotWin::Initialize(ProcessReaderWin* process_reader, + uint64_t address, + uint64_t size) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + process_reader_ = process_reader; + address_ = address; + DLOG_IF(WARNING, size >= std::numeric_limits::max()) + << "size overflow"; + size_ = static_cast(size); + INITIALIZATION_STATE_SET_VALID(initialized_); +} + +uint64_t MemorySnapshotWin::Address() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return address_; +} + +size_t MemorySnapshotWin::Size() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return size_; +} + +bool MemorySnapshotWin::Read(Delegate* delegate) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + if (size_ == 0) { + return delegate->MemorySnapshotDelegateRead(nullptr, size_); + } + + scoped_ptr buffer(new uint8_t[size_]); + if (!process_reader_->ReadMemory(address_, size_, buffer.get())) { + return false; + } + return delegate->MemorySnapshotDelegateRead(buffer.get(), size_); +} + +} // namespace internal +} // namespace crashpad diff --git a/snapshot/win/memory_snapshot_win.h b/snapshot/win/memory_snapshot_win.h new file mode 100644 index 00000000..b6d2074e --- /dev/null +++ b/snapshot/win/memory_snapshot_win.h @@ -0,0 +1,68 @@ +// Copyright 2015 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_WIN_MEMORY_SNAPSHOT_WIN_H_ +#define CRASHPAD_SNAPSHOT_WIN_MEMORY_SNAPSHOT_WIN_H_ + +#include +#include + +#include "base/basictypes.h" +#include "snapshot/memory_snapshot.h" +#include "snapshot/win/process_reader_win.h" +#include "util/misc/initialization_state_dcheck.h" + +namespace crashpad { +namespace internal { + +//! \brief A MemorySnapshot of a memory region in a process on the running +//! system, when the system runs Windows. +class MemorySnapshotWin final : public MemorySnapshot { + public: + MemorySnapshotWin(); + ~MemorySnapshotWin() override; + + //! \brief Initializes the object. + //! + //! Memory is read lazily. No attempt is made to read the memory snapshot data + //! until Read() is called, and the memory snapshot data is discared when + //! Read() returns. + //! + //! \param[in] process_reader A reader for the process being snapshotted. + //! \param[in] address The base address of the memory region to snapshot, in + //! the snapshot process' address space. + //! \param[in] size The size of the memory region to snapshot. + void Initialize(ProcessReaderWin* process_reader, + uint64_t address, + uint64_t size); + + // MemorySnapshot: + + uint64_t Address() const override; + size_t Size() const override; + bool Read(Delegate* delegate) const override; + + private: + ProcessReaderWin* process_reader_; // weak + uint64_t address_; + size_t size_; + InitializationStateDcheck initialized_; + + DISALLOW_COPY_AND_ASSIGN(MemorySnapshotWin); +}; + +} // namespace internal +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_WIN_MEMORY_SNAPSHOT_WIN_H_ diff --git a/snapshot/win/process_reader_win.cc b/snapshot/win/process_reader_win.cc index 84e176b8..a8774b01 100644 --- a/snapshot/win/process_reader_win.cc +++ b/snapshot/win/process_reader_win.cc @@ -14,15 +14,216 @@ #include "snapshot/win/process_reader_win.h" +#include + +#include "base/memory/scoped_ptr.h" #include "base/numerics/safe_conversions.h" +#include "util/win/process_structs.h" +#include "util/win/scoped_handle.h" #include "util/win/time.h" namespace crashpad { +namespace { + +NTSTATUS NtQuerySystemInformation( + SYSTEM_INFORMATION_CLASS system_information_class, + PVOID system_information, + ULONG system_information_length, + PULONG return_length) { + static decltype(::NtQuerySystemInformation)* nt_query_system_information = + reinterpret_cast(GetProcAddress( + LoadLibrary(L"ntdll.dll"), "NtQuerySystemInformation")); + DCHECK(nt_query_system_information); + return nt_query_system_information(system_information_class, + system_information, + system_information_length, + return_length); +} + +// The 4th argument is CLIENT_ID*, but as we can't typedef that, we simply cast +// to void* here. +typedef NTSTATUS(WINAPI* NtOpenThreadFunction)( + PHANDLE ThreadHandle, + ACCESS_MASK DesiredAccess, + POBJECT_ATTRIBUTES ObjectAttributes, + const void* ClientId); + +template +NTSTATUS NtOpenThread(PHANDLE thread_handle, + ACCESS_MASK desired_access, + POBJECT_ATTRIBUTES object_attributes, + const process_types::CLIENT_ID* client_id) { + static NtOpenThreadFunction nt_open_thread = + reinterpret_cast( + GetProcAddress(LoadLibrary(L"ntdll.dll"), "NtOpenThread")); + DCHECK(nt_open_thread); + return nt_open_thread(thread_handle, + desired_access, + object_attributes, + static_cast(client_id)); +} + +NTSTATUS NtQueryInformationThread(HANDLE thread_handle, + THREADINFOCLASS thread_information_class, + PVOID thread_information, + ULONG thread_information_length, + PULONG return_length) { + static decltype(::NtQueryInformationThread)* nt_query_information_thread = + reinterpret_cast(GetProcAddress( + LoadLibrary(L"ntdll.dll"), "NtQueryInformationThread")); + DCHECK(nt_query_information_thread); + return nt_query_information_thread(thread_handle, + thread_information_class, + thread_information, + thread_information_length, + return_length); +} + +// Copied from ntstatus.h because um/winnt.h conflicts with general inclusion of +// ntstatus.h. +#define STATUS_BUFFER_TOO_SMALL ((NTSTATUS)0xC0000023L) +#define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS)0xC0000004L) + +// Gets a pointer to the process information structure after a given one, or +// null when iteration is complete, assuming they've been retrieved in a block +// via NtQuerySystemInformation(). +template +process_types::SYSTEM_PROCESS_INFORMATION* NextProcess( + process_types::SYSTEM_PROCESS_INFORMATION* process) { + ULONG offset = process->NextEntryOffset; + if (offset == 0) + return nullptr; + return reinterpret_cast*>( + reinterpret_cast(process) + offset); +} + +//! \brief Retrieves the SYSTEM_PROCESS_INFORMATION for a given process. +//! +//! The returned pointer points into the memory block stored by \a buffer. +//! Ownership of \a buffer is transferred to the caller. +//! +//! \return Pointer to the process' data, or nullptr if it was not found or on +//! error. On error, a message will be logged. +template +process_types::SYSTEM_PROCESS_INFORMATION* GetProcessInformation( + HANDLE process_handle, + scoped_ptr* buffer) { + ULONG buffer_size = 16384; + buffer->reset(new uint8_t[buffer_size]); + NTSTATUS status; + // This must be in retry loop, as we're racing with process creation on the + // system to find a buffer large enough to hold all process information. + for (int tries = 0; tries < 20; ++tries) { + const int kSystemExtendedProcessInformation = 57; + status = crashpad::NtQuerySystemInformation( + static_cast( + kSystemExtendedProcessInformation), + reinterpret_cast(buffer->get()), + buffer_size, + &buffer_size); + if (status == STATUS_BUFFER_TOO_SMALL || + status == STATUS_INFO_LENGTH_MISMATCH) { + // Add a little extra to try to avoid an additional loop iteration. We're + // racing with system-wide process creation between here and the next call + // to NtQuerySystemInformation(). + buffer_size += 4096; + buffer->reset(new uint8_t[buffer_size]); + } else { + break; + } + } + + if (!NT_SUCCESS(status)) { + LOG(ERROR) << "NtQuerySystemInformation failed: " << std::hex << status; + return nullptr; + } + + process_types::SYSTEM_PROCESS_INFORMATION* process = + reinterpret_cast*>( + buffer->get()); + DWORD process_id = GetProcessId(process_handle); + do { + if (process->UniqueProcessId == process_id) + return process; + } while (process = NextProcess(process)); + + LOG(ERROR) << "process " << process_id << " not found"; + return nullptr; +} + +template +uint32_t GetThreadSuspendCount( + const process_types::SYSTEM_EXTENDED_THREAD_INFORMATION& + thread_info) { + // Wait reason values are from KWAIT_REASON in wdm.h. We don't need all of + // them, so just declare the one we need. + const ULONG kWaitReasonSuspended = 5; + + // Kernel mode enumerations for thread state come from + // http://www.nirsoft.net/kernel_struct/vista/KTHREAD_STATE.html and + // https://msdn.microsoft.com/en-us/library/system.diagnostics.threadstate(v=vs.110).aspx + const ULONG kThreadStateWaiting = 5; + const ULONG kThreadStateGateWait = 8; + + bool suspended = (thread_info.ThreadState == kThreadStateWaiting || + thread_info.ThreadState == kThreadStateGateWait) && + thread_info.WaitReason == kWaitReasonSuspended; + if (!suspended) + return 0; + + HANDLE thread_handle; + ACCESS_MASK query_access = THREAD_QUERY_LIMITED_INFORMATION; + OBJECT_ATTRIBUTES object_attributes; + InitializeObjectAttributes(&object_attributes, nullptr, 0, nullptr, nullptr); + NTSTATUS status = crashpad::NtOpenThread( + &thread_handle, query_access, &object_attributes, &thread_info.ClientId); + if (!NT_SUCCESS(status)) { + LOG(WARNING) << "couldn't open thread to retrieve suspend count"; + // Fall back to something semi-reasonable. We know we're suspended at this + // point, so just return 1. + return 1; + } + + // Take ownership of this handle so we close on exit. NtClose and CloseHandle + // are identical. + ScopedKernelHANDLE handle(thread_handle); + + // From ntddk.h. winternl.h defines THREADINFOCLASS, but only one value. + const int kThreadSuspendCount = 35; + ULONG suspend_count; + status = crashpad::NtQueryInformationThread( + handle.get(), + static_cast(kThreadSuspendCount), + &suspend_count, + sizeof(suspend_count), + nullptr); + if (!NT_SUCCESS(status)) { + LOG(WARNING) << "NtQueryInformationThread failed" << std::hex << status; + return 1; + } + + return suspend_count; +} + +} // namespace + +ProcessReaderWin::Thread::Thread() + : id(0), + teb(0), + stack_region_address(0), + stack_region_size(0), + suspend_count(0), + priority_class(0), + priority(0) { +} + ProcessReaderWin::ProcessReaderWin() : process_(INVALID_HANDLE_VALUE), process_info_(), + threads_(), modules_(), + initialized_threads_(false), initialized_() { } @@ -77,6 +278,61 @@ bool ProcessReaderWin::CPUTimes(timeval* user_time, return true; } +const std::vector& ProcessReaderWin::Threads() { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + if (initialized_threads_) + return threads_; + + initialized_threads_ = true; + + DCHECK(threads_.empty()); + +#if ARCH_CPU_32_BITS + using SizeTraits = process_types::internal::Traits32; +#else + using SizeTraits = process_types::internal::Traits64; +#endif + scoped_ptr buffer; + process_types::SYSTEM_PROCESS_INFORMATION* process_information = + GetProcessInformation(process_, &buffer); + if (!process_information) + return threads_; + + for (unsigned long i = 0; i < process_information->NumberOfThreads; ++i) { + const process_types::SYSTEM_EXTENDED_THREAD_INFORMATION& + thread_info = process_information->Threads[i]; + Thread thread; + thread.id = thread_info.ClientId.UniqueThread; + thread.suspend_count = GetThreadSuspendCount(thread_info); + + // TODO(scottmg): I believe we could reverse engineer the PriorityClass from + // the Priority, BasePriority, and + // https://msdn.microsoft.com/en-us/library/windows/desktop/ms685100 . + // MinidumpThreadWriter doesn't handle it yet in any case, so investigate + // both of those at the same time if it's useful. + thread.priority_class = NORMAL_PRIORITY_CLASS; + + thread.priority = thread_info.Priority; + thread.teb = thread_info.TebBase; + + // While there are semi-documented fields in the thread structure called + // StackBase and StackLimit, they don't appear to be correct in practice (or + // at least, I don't know how to interpret them). Instead, read the TIB + // (Thread Information Block) which is the first element of the TEB, and use + // its stack fields. + process_types::NT_TIB tib; + if (ReadMemory(thread_info.TebBase, sizeof(tib), &tib)) { + thread.stack_region_address = tib.StackBase; + // Note, "backwards" because of direction of stack growth. + thread.stack_region_size = tib.StackBase - tib.StackLimit; + } + threads_.push_back(thread); + } + + return threads_; +} + const std::vector& ProcessReaderWin::Modules() { INITIALIZATION_STATE_DCHECK_VALID(initialized_); diff --git a/snapshot/win/process_reader_win.h b/snapshot/win/process_reader_win.h index ff687885..0e9ada52 100644 --- a/snapshot/win/process_reader_win.h +++ b/snapshot/win/process_reader_win.h @@ -18,6 +18,8 @@ #include #include +#include + #include "util/misc/initialization_state_dcheck.h" #include "util/win/address_types.h" #include "util/win/process_info.h" @@ -27,6 +29,20 @@ namespace crashpad { //! \brief Accesses information about another process, identified by a HANDLE. class ProcessReaderWin { public: + //! \brief Contains information about a thread that belongs to a process. + struct Thread { + Thread(); + ~Thread() {} + + uint64_t id; + WinVMAddress teb; + WinVMAddress stack_region_address; + WinVMSize stack_region_size; + uint32_t suspend_count; + uint32_t priority_class; + uint32_t priority; + }; + ProcessReaderWin(); ~ProcessReaderWin(); @@ -66,6 +82,10 @@ class ProcessReaderWin { //! \return `true` on success, `false` on failure, with a warning logged. bool CPUTimes(timeval* user_time, timeval* system_time) const; + //! \return The threads that are in the process. The first element (at index + //! `0`) corresponds to the main thread. + const std::vector& Threads(); + //! \return The modules loaded in the process. The first element (at index //! `0`) corresponds to the main executable. const std::vector& Modules(); @@ -73,7 +93,9 @@ class ProcessReaderWin { private: HANDLE process_; ProcessInfo process_info_; + std::vector threads_; std::vector modules_; + bool initialized_threads_; InitializationStateDcheck initialized_; DISALLOW_COPY_AND_ASSIGN(ProcessReaderWin); diff --git a/snapshot/win/process_snapshot_win.cc b/snapshot/win/process_snapshot_win.cc index 16623069..b25e8caa 100644 --- a/snapshot/win/process_snapshot_win.cc +++ b/snapshot/win/process_snapshot_win.cc @@ -22,7 +22,7 @@ namespace crashpad { ProcessSnapshotWin::ProcessSnapshotWin() : ProcessSnapshot(), system_(), - // TODO(scottmg): threads_(), + threads_(), modules_(), // TODO(scottmg): exception_(), process_reader_(), @@ -46,7 +46,7 @@ bool ProcessSnapshotWin::Initialize(HANDLE process) { system_.Initialize(&process_reader_); - // TODO(scottmg): InitializeThreads(); + InitializeThreads(); InitializeModules(); INITIALIZATION_STATE_SET_VALID(initialized_); @@ -131,8 +131,12 @@ const SystemSnapshot* ProcessSnapshotWin::System() const { } std::vector ProcessSnapshotWin::Threads() const { - CHECK(false) << "TODO(scottmg)"; - return std::vector(); + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + std::vector threads; + for (internal::ThreadSnapshotWin* thread : threads_) { + threads.push_back(thread); + } + return threads; } std::vector ProcessSnapshotWin::Modules() const { @@ -145,10 +149,22 @@ std::vector ProcessSnapshotWin::Modules() const { } const ExceptionSnapshot* ProcessSnapshotWin::Exception() const { - CHECK(false) << "TODO(scottmg)"; + CHECK(false) << "TODO(scottmg): Exception()"; return nullptr; } +void ProcessSnapshotWin::InitializeThreads() { + const std::vector& process_reader_threads = + process_reader_.Threads(); + for (const ProcessReaderWin::Thread& process_reader_thread : + process_reader_threads) { + auto thread = make_scoped_ptr(new internal::ThreadSnapshotWin()); + if (thread->Initialize(&process_reader_, process_reader_thread)) { + threads_.push_back(thread.release()); + } + } +} + void ProcessSnapshotWin::InitializeModules() { const std::vector& process_reader_modules = process_reader_.Modules(); diff --git a/snapshot/win/process_snapshot_win.h b/snapshot/win/process_snapshot_win.h index 8bb0bf48..97bb1c69 100644 --- a/snapshot/win/process_snapshot_win.h +++ b/snapshot/win/process_snapshot_win.h @@ -33,6 +33,7 @@ #include "snapshot/thread_snapshot.h" #include "snapshot/win/module_snapshot_win.h" #include "snapshot/win/system_snapshot_win.h" +#include "snapshot/win/thread_snapshot_win.h" #include "util/misc/initialization_state_dcheck.h" #include "util/misc/uuid.h" #include "util/stdlib/pointer_container.h" @@ -104,13 +105,13 @@ class ProcessSnapshotWin final : public ProcessSnapshot { private: // Initializes threads_ on behalf of Initialize(). - // TODO(scottmg): void InitializeThreads(); + void InitializeThreads(); // Initializes modules_ on behalf of Initialize(). void InitializeModules(); internal::SystemSnapshotWin system_; - // TODO(scottmg): PointerVector threads_; + PointerVector threads_; PointerVector modules_; // TODO(scottmg): scoped_ptr exception_; ProcessReaderWin process_reader_; diff --git a/snapshot/win/thread_snapshot_win.cc b/snapshot/win/thread_snapshot_win.cc new file mode 100644 index 00000000..689e79ca --- /dev/null +++ b/snapshot/win/thread_snapshot_win.cc @@ -0,0 +1,75 @@ +// Copyright 2015 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/win/thread_snapshot_win.h" + +#include "base/logging.h" +#include "snapshot/win/process_reader_win.h" + +namespace crashpad { +namespace internal { + +ThreadSnapshotWin::ThreadSnapshotWin() + : ThreadSnapshot(), context_(), stack_(), thread_(), initialized_() { +} + +ThreadSnapshotWin::~ThreadSnapshotWin() { +} + +bool ThreadSnapshotWin::Initialize( + ProcessReaderWin* process_reader, + const ProcessReaderWin::Thread& process_reader_thread) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + + thread_ = process_reader_thread; + stack_.Initialize( + process_reader, thread_.stack_region_address, thread_.stack_region_size); + + INITIALIZATION_STATE_SET_VALID(initialized_); + return true; +} + +const CPUContext* ThreadSnapshotWin::Context() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + LOG(ERROR) << "TODO(scottmg): CPUContext"; + return &context_; +} + +const MemorySnapshot* ThreadSnapshotWin::Stack() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return &stack_; +} + +uint64_t ThreadSnapshotWin::ThreadID() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return thread_.id; +} + +int ThreadSnapshotWin::SuspendCount() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return thread_.suspend_count; +} + +int ThreadSnapshotWin::Priority() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return thread_.priority; +} + +uint64_t ThreadSnapshotWin::ThreadSpecificDataAddress() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return thread_.teb; +} + +} // namespace internal +} // namespace crashpad diff --git a/snapshot/win/thread_snapshot_win.h b/snapshot/win/thread_snapshot_win.h new file mode 100644 index 00000000..19e8066c --- /dev/null +++ b/snapshot/win/thread_snapshot_win.h @@ -0,0 +1,74 @@ +// Copyright 2015 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_WIN_THREAD_SNAPSHOT_WIN_H_ +#define CRASHPAD_SNAPSHOT_WIN_THREAD_SNAPSHOT_WIN_H_ + +#include + +#include "base/basictypes.h" +#include "snapshot/cpu_context.h" +#include "snapshot/memory_snapshot.h" +#include "snapshot/thread_snapshot.h" +#include "snapshot/win/memory_snapshot_win.h" +#include "snapshot/win/process_reader_win.h" +#include "util/misc/initialization_state_dcheck.h" + +namespace crashpad { + +class ProcessReaderWin; + +namespace internal { + +//! \brief A ThreadSnapshot of a thread in a running (or crashed) process on a +//! Windows system. +class ThreadSnapshotWin final : public ThreadSnapshot { + public: + ThreadSnapshotWin(); + ~ThreadSnapshotWin() override; + + //! \brief Initializes the object. + //! + //! \param[in] process_reader A ProcessReaderWin for the process containing + //! the thread. + //! \param[in] process_reader_thread The thread within the ProcessReaderWin + //! for which the snapshot should be created. + //! + //! \return `true` if the snapshot could be created, `false` otherwise with + //! an appropriate message logged. + bool Initialize(ProcessReaderWin* process_reader, + const ProcessReaderWin::Thread& process_reader_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; + + private: + CPUContext context_; + MemorySnapshotWin stack_; + ProcessReaderWin::Thread thread_; + InitializationStateDcheck initialized_; + + DISALLOW_COPY_AND_ASSIGN(ThreadSnapshotWin); +}; + +} // namespace internal +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_WIN_THREAD_SNAPSHOT_WIN_H_ diff --git a/util/win/process_structs.h b/util/win/process_structs.h index ece60896..d88b33d1 100644 --- a/util/win/process_structs.h +++ b/util/win/process_structs.h @@ -281,6 +281,107 @@ struct PEB { DWORD FlsHighIndex; }; +template +struct NT_TIB { + typename Traits::Pointer ExceptionList; + typename Traits::Pointer StackBase; + typename Traits::Pointer StackLimit; + typename Traits::Pointer SubSystemTib; + union { + typename Traits::Pointer FiberData; + BYTE Version[4]; + }; + typename Traits::Pointer ArbitraryUserPointer; + typename Traits::Pointer Self; +}; + +// See https://msdn.microsoft.com/en-us/library/gg750647.aspx. +template +struct CLIENT_ID { + typename Traits::Pointer UniqueProcess; + typename Traits::Pointer UniqueThread; +}; + +// See https://msdn.microsoft.com/en-us/library/gg750724.aspx for the base +// structure, and +// http://processhacker.sourceforge.net/doc/struct___s_y_s_t_e_m___e_x_t_e_n_d_e_d___t_h_r_e_a_d___i_n_f_o_r_m_a_t_i_o_n.html +// for the extension part. +template +struct SYSTEM_EXTENDED_THREAD_INFORMATION { + LARGE_INTEGER KernelTime; + LARGE_INTEGER UserTime; + LARGE_INTEGER CreateTime; + union { + ULONG WaitTime; + typename Traits::Pad padding_for_x64_0; + }; + typename Traits::Pointer StartAddress; + CLIENT_ID ClientId; + LONG Priority; + LONG BasePriority; + ULONG ContextSwitches; + ULONG ThreadState; + union { + ULONG WaitReason; + typename Traits::Pad padding_for_x64_1; + }; + typename Traits::Pointer StackBase; // These don't appear to be correct. + typename Traits::Pointer StackLimit; + typename Traits::Pointer Win32StartAddress; + typename Traits::Pointer TebBase; + typename Traits::Pointer Reserved; + typename Traits::Pointer Reserved2; + typename Traits::Pointer Reserved3; +}; + +// See http://undocumented.ntinternals.net/source/usermode/undocumented%20functions/system%20information/structures/system_process_information.html +template +struct SYSTEM_PROCESS_INFORMATION { + ULONG NextEntryOffset; + ULONG NumberOfThreads; + LARGE_INTEGER Reserved[3]; + LARGE_INTEGER CreateTime; + LARGE_INTEGER UserTime; + LARGE_INTEGER KernelTime; + UNICODE_STRING ImageName; + union { + LONG BasePriority; + typename Traits::Pad padding_for_x64_0; + }; + union { + DWORD UniqueProcessId; + typename Traits::Pad padding_for_x64_1; + }; + union { + DWORD InheritedFromUniqueProcessId; + typename Traits::Pad padding_for_x64_2; + }; + ULONG HandleCount; + ULONG Reserved2[3]; + SIZE_T PeakVirtualSize; + SIZE_T VirtualSize; + union { + ULONG PageFaultCount; + typename Traits::Pad padding_for_x64_3; + }; + SIZE_T PeakWorkingSetSize; + SIZE_T WorkingSetSize; + SIZE_T QuotaPeakPagedPoolUsage; + SIZE_T QuotaPagedPoolUsage; + SIZE_T QuotaPeakNonPagedPoolUsage; + SIZE_T QuotaNonPagedPoolUsage; + SIZE_T PagefileUsage; + SIZE_T PeakPagefileUsage; + SIZE_T PrivatePageCount; + LARGE_INTEGER ReadOperationCount; + LARGE_INTEGER WriteOperationCount; + LARGE_INTEGER OtherOperationCount; + LARGE_INTEGER ReadTransferCount; + LARGE_INTEGER WriteTransferCount; + LARGE_INTEGER OtherTransferCount; + SYSTEM_EXTENDED_THREAD_INFORMATION Threads[1]; +}; + #pragma pack(pop) //! \}