mirror of
https://github.com/chromium/crashpad.git
synced 2025-03-09 14:06:33 +00:00
win: Add thread snapshot and memory snapshot for stacks
The next big piece of functionality in snapshot. There's a bit more grubbing around in the NT internals than would be nice, and it has made me start to question the value avoiding MinidumpWriteDump. But this seems to extract most of the data we need (I haven't pulled the cpu context yet, but I hope that won't be too hard.) R=mark@chromium.org BUG=crashpad:1 Review URL: https://codereview.chromium.org/1131473005
This commit is contained in:
parent
00c42ae7bd
commit
658cd3e1a7
@ -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 {
|
||||
|
@ -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"', {
|
||||
|
70
snapshot/win/memory_snapshot_win.cc
Normal file
70
snapshot/win/memory_snapshot_win.cc
Normal file
@ -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<size_t>::max())
|
||||
<< "size overflow";
|
||||
size_ = static_cast<size_t>(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<uint8_t[]> 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
|
68
snapshot/win/memory_snapshot_win.h
Normal file
68
snapshot/win/memory_snapshot_win.h
Normal file
@ -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 <stdint.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#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_
|
@ -14,15 +14,216 @@
|
||||
|
||||
#include "snapshot/win/process_reader_win.h"
|
||||
|
||||
#include <winternl.h>
|
||||
|
||||
#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<decltype(::NtQuerySystemInformation)*>(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 <class Traits>
|
||||
NTSTATUS NtOpenThread(PHANDLE thread_handle,
|
||||
ACCESS_MASK desired_access,
|
||||
POBJECT_ATTRIBUTES object_attributes,
|
||||
const process_types::CLIENT_ID<Traits>* client_id) {
|
||||
static NtOpenThreadFunction nt_open_thread =
|
||||
reinterpret_cast<NtOpenThreadFunction>(
|
||||
GetProcAddress(LoadLibrary(L"ntdll.dll"), "NtOpenThread"));
|
||||
DCHECK(nt_open_thread);
|
||||
return nt_open_thread(thread_handle,
|
||||
desired_access,
|
||||
object_attributes,
|
||||
static_cast<const void*>(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<decltype(::NtQueryInformationThread)*>(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 <class Traits>
|
||||
process_types::SYSTEM_PROCESS_INFORMATION<Traits>* NextProcess(
|
||||
process_types::SYSTEM_PROCESS_INFORMATION<Traits>* process) {
|
||||
ULONG offset = process->NextEntryOffset;
|
||||
if (offset == 0)
|
||||
return nullptr;
|
||||
return reinterpret_cast<process_types::SYSTEM_PROCESS_INFORMATION<Traits>*>(
|
||||
reinterpret_cast<uint8_t*>(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 <class Traits>
|
||||
process_types::SYSTEM_PROCESS_INFORMATION<Traits>* GetProcessInformation(
|
||||
HANDLE process_handle,
|
||||
scoped_ptr<uint8_t[]>* 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<SYSTEM_INFORMATION_CLASS>(
|
||||
kSystemExtendedProcessInformation),
|
||||
reinterpret_cast<void*>(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<Traits>* process =
|
||||
reinterpret_cast<process_types::SYSTEM_PROCESS_INFORMATION<Traits>*>(
|
||||
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 <class Traits>
|
||||
uint32_t GetThreadSuspendCount(
|
||||
const process_types::SYSTEM_EXTENDED_THREAD_INFORMATION<Traits>&
|
||||
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<THREADINFOCLASS>(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::Thread>& 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<uint8_t[]> buffer;
|
||||
process_types::SYSTEM_PROCESS_INFORMATION<SizeTraits>* process_information =
|
||||
GetProcessInformation<SizeTraits>(process_, &buffer);
|
||||
if (!process_information)
|
||||
return threads_;
|
||||
|
||||
for (unsigned long i = 0; i < process_information->NumberOfThreads; ++i) {
|
||||
const process_types::SYSTEM_EXTENDED_THREAD_INFORMATION<SizeTraits>&
|
||||
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<SizeTraits> 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<ProcessInfo::Module>& ProcessReaderWin::Modules() {
|
||||
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||
|
||||
|
@ -18,6 +18,8 @@
|
||||
#include <sys/time.h>
|
||||
#include <windows.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#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<Thread>& Threads();
|
||||
|
||||
//! \return The modules loaded in the process. The first element (at index
|
||||
//! `0`) corresponds to the main executable.
|
||||
const std::vector<ProcessInfo::Module>& Modules();
|
||||
@ -73,7 +93,9 @@ class ProcessReaderWin {
|
||||
private:
|
||||
HANDLE process_;
|
||||
ProcessInfo process_info_;
|
||||
std::vector<Thread> threads_;
|
||||
std::vector<ProcessInfo::Module> modules_;
|
||||
bool initialized_threads_;
|
||||
InitializationStateDcheck initialized_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(ProcessReaderWin);
|
||||
|
@ -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<const ThreadSnapshot*> ProcessSnapshotWin::Threads() const {
|
||||
CHECK(false) << "TODO(scottmg)";
|
||||
return std::vector<const ThreadSnapshot*>();
|
||||
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||
std::vector<const ThreadSnapshot*> threads;
|
||||
for (internal::ThreadSnapshotWin* thread : threads_) {
|
||||
threads.push_back(thread);
|
||||
}
|
||||
return threads;
|
||||
}
|
||||
|
||||
std::vector<const ModuleSnapshot*> ProcessSnapshotWin::Modules() const {
|
||||
@ -145,10 +149,22 @@ std::vector<const ModuleSnapshot*> 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<ProcessReaderWin::Thread>& 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<ProcessInfo::Module>& process_reader_modules =
|
||||
process_reader_.Modules();
|
||||
|
@ -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<internal::ThreadSnapshotWin> threads_;
|
||||
PointerVector<internal::ThreadSnapshotWin> threads_;
|
||||
PointerVector<internal::ModuleSnapshotWin> modules_;
|
||||
// TODO(scottmg): scoped_ptr<internal::ExceptionSnapshotWin> exception_;
|
||||
ProcessReaderWin process_reader_;
|
||||
|
75
snapshot/win/thread_snapshot_win.cc
Normal file
75
snapshot/win/thread_snapshot_win.cc
Normal file
@ -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
|
74
snapshot/win/thread_snapshot_win.h
Normal file
74
snapshot/win/thread_snapshot_win.h
Normal file
@ -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 <stdint.h>
|
||||
|
||||
#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_
|
@ -281,6 +281,107 @@ struct PEB {
|
||||
DWORD FlsHighIndex;
|
||||
};
|
||||
|
||||
template <class Traits>
|
||||
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 <class Traits>
|
||||
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 <class Traits>
|
||||
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<Traits> 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 <class Traits>
|
||||
struct SYSTEM_PROCESS_INFORMATION {
|
||||
ULONG NextEntryOffset;
|
||||
ULONG NumberOfThreads;
|
||||
LARGE_INTEGER Reserved[3];
|
||||
LARGE_INTEGER CreateTime;
|
||||
LARGE_INTEGER UserTime;
|
||||
LARGE_INTEGER KernelTime;
|
||||
UNICODE_STRING<Traits> 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<Traits> Threads[1];
|
||||
};
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
//! \}
|
||||
|
Loading…
x
Reference in New Issue
Block a user