mirror of
https://github.com/chromium/crashpad.git
synced 2024-12-27 15:32:10 +08:00
win: Implement exception snapshot
Refactor some of the NT internals helpers and cpu_context to share between the thread and exception snapshot code. Add test that runs crashing child and validates the exception in the snapshot. R=mark@chromium.org, cpu@chromium.org, rsesek@chromium.org BUG=crashpad:1 Review URL: https://codereview.chromium.org/1126413008 .
This commit is contained in:
parent
1a770c8237
commit
a691448ffb
@ -56,17 +56,19 @@ LONG WINAPI UnhandledExceptionHandler(EXCEPTION_POINTERS* exception_pointers) {
|
||||
// contention here is very unlikely, and we'll still have a stack that's
|
||||
// blocked at this location.
|
||||
if (base::subtle::Barrier_AtomicIncrement(&g_have_crashed, 1) > 1) {
|
||||
SleepEx(false, INFINITE);
|
||||
SleepEx(INFINITE, false);
|
||||
}
|
||||
|
||||
// Otherwise, we're the first thread, so record the exception pointer and
|
||||
// signal the crash handler.
|
||||
crashpad::CrashpadInfo::GetCrashpadInfo()->set_thread_id(
|
||||
GetCurrentThreadId());
|
||||
crashpad::CrashpadInfo::GetCrashpadInfo()->set_exception_pointers(
|
||||
exception_pointers);
|
||||
DWORD rv = SignalObjectAndWait(g_signal_exception,
|
||||
g_wait_termination,
|
||||
kMillisecondsUntilTerminate,
|
||||
FALSE);
|
||||
false);
|
||||
if (rv != WAIT_OBJECT_0) {
|
||||
// Something went wrong. It is likely that a dump has not been created.
|
||||
if (rv == WAIT_TIMEOUT) {
|
||||
|
@ -89,7 +89,13 @@ CrashpadInfo::CrashpadInfo()
|
||||
crashpad_handler_behavior_(TriState::kUnset),
|
||||
system_crash_reporter_forwarding_(TriState::kUnset),
|
||||
padding_0_(0),
|
||||
simple_annotations_(nullptr) {
|
||||
simple_annotations_(nullptr)
|
||||
#if defined(OS_WIN)
|
||||
,
|
||||
exception_pointers_(nullptr),
|
||||
thread_id_(0)
|
||||
#endif // OS_WIN
|
||||
{
|
||||
}
|
||||
|
||||
} // namespace crashpad
|
||||
|
@ -99,10 +99,15 @@ struct CrashpadInfo {
|
||||
}
|
||||
|
||||
#if defined(OS_WIN)
|
||||
//! \brief Save an EXCEPTION_POINTERS record for the crash handler.
|
||||
//! \brief Save the crashing thread ID for the crash handler.
|
||||
void set_thread_id(DWORD thread_id) { thread_id_ = thread_id; }
|
||||
DWORD thread_id() const { return thread_id_; }
|
||||
|
||||
//! \brief Save an `EXCEPTION_POINTERS` record for the crash handler.
|
||||
void set_exception_pointers(EXCEPTION_POINTERS* exception_pointers) {
|
||||
exception_pointers_ = exception_pointers;
|
||||
}
|
||||
EXCEPTION_POINTERS* exception_pointers() const { return exception_pointers_; }
|
||||
#endif // OS_WIN
|
||||
|
||||
enum : uint32_t {
|
||||
@ -130,6 +135,7 @@ struct CrashpadInfo {
|
||||
|
||||
#if defined(OS_WIN)
|
||||
EXCEPTION_POINTERS* exception_pointers_;
|
||||
DWORD thread_id_;
|
||||
#endif // OS_WIN
|
||||
|
||||
#if defined(__clang__)
|
||||
|
@ -410,6 +410,9 @@ struct __attribute__((packed, aligned(4))) MINIDUMP_EXCEPTION {
|
||||
//! the original exception code will appear instead. The exception type as it
|
||||
//! was received will appear at index 0 of #ExceptionInformation.
|
||||
//!
|
||||
//! For Windows minidumps, this will be an \ref EXCEPTION_x "EXCEPTION_*"
|
||||
//! exception type, such as `EXCEPTION_ACCESS_VIOLATION`.
|
||||
//!
|
||||
//! \note This field is named ExceptionCode, but what is known as the
|
||||
//! “exception code” on Mac OS X/Mach is actually stored in the
|
||||
//! #ExceptionFlags field of a minidump file.
|
||||
@ -432,6 +435,10 @@ struct __attribute__((packed, aligned(4))) MINIDUMP_EXCEPTION {
|
||||
//! In all cases for Mac OS X minidumps, the code as it was received by the
|
||||
//! Mach exception handler will appear at index 1 of #ExceptionInformation.
|
||||
//!
|
||||
//! For Windows minidumps, this will either be `0` if the exception is
|
||||
//! continuable, or `EXCEPTION_NONCONTINUABLE` to indicate a noncontinuable
|
||||
//! exception.
|
||||
//!
|
||||
//! \todo Document the possible values by OS. There may be OS-specific enums
|
||||
//! in minidump_extensions.h.
|
||||
uint32_t ExceptionFlags;
|
||||
@ -461,6 +468,9 @@ struct __attribute__((packed, aligned(4))) MINIDUMP_EXCEPTION {
|
||||
//! exception handler. Unlike #ExceptionCode and #ExceptionFlags, the values
|
||||
//! received by a Mach exception handler are used directly here even for the
|
||||
//! `EXC_CRASH`, `EXC_RESOURCE`, and `EXC_GUARD` exception types.
|
||||
|
||||
//! For Windows, these are additional arguments (if any) as provided to
|
||||
//! `RaiseException()`.
|
||||
uint64_t ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS];
|
||||
};
|
||||
|
||||
|
@ -53,6 +53,9 @@ class ExceptionSnapshot {
|
||||
//! processed as `EXC_CRASH` when generated from another preceding exception:
|
||||
//! the original exception code will appear instead. The exception type as it
|
||||
//! was received will appear at index 0 of Codes().
|
||||
//!
|
||||
//! For Windows, this will be an \ref EXCEPTION_x "EXCEPTION_*" exception type
|
||||
//! such as `EXCEPTION_ACCESS_VIOLATION`.
|
||||
virtual uint32_t Exception() const = 0;
|
||||
|
||||
//! \brief Returns the second-level exception code identifying the exception.
|
||||
@ -69,6 +72,9 @@ class ExceptionSnapshot {
|
||||
//!
|
||||
//! In all cases on Mac OS X, the full exception code at index 0 as it was
|
||||
//! received will appear at index 1 of Codes().
|
||||
//!
|
||||
//! On Windows, this will either be `0` if the exception is continuable, or
|
||||
//! `EXCEPTION_NONCONTINUABLE` to indicate a noncontinuable exception.
|
||||
virtual uint32_t ExceptionInfo() const = 0;
|
||||
|
||||
//! \brief Returns the address that triggered the exception.
|
||||
@ -92,6 +98,10 @@ class ExceptionSnapshot {
|
||||
//! For Mac OS X, this will be a vector containing the original exception type
|
||||
//! and the values of `code[0]` and `code[1]` as received by a Mach exception
|
||||
//! handler.
|
||||
//!
|
||||
//! For Windows, these are additional arguments (if any) as provided to
|
||||
//! `RaiseException()`. See the documentation for `ExceptionInformation` in
|
||||
//! `EXCEPTION_RECORD`.
|
||||
virtual const std::vector<uint64_t>& Codes() const = 0;
|
||||
};
|
||||
|
||||
|
@ -14,6 +14,7 @@
|
||||
|
||||
#include "snapshot/mac/process_snapshot_mac.h"
|
||||
|
||||
#include "base/logging.h"
|
||||
#include "util/misc/tri_state.h"
|
||||
|
||||
namespace crashpad {
|
||||
|
@ -72,6 +72,7 @@
|
||||
'mac/system_snapshot_mac.h',
|
||||
'mac/thread_snapshot_mac.cc',
|
||||
'mac/thread_snapshot_mac.h',
|
||||
'memory_snapshot.h',
|
||||
'minidump/minidump_simple_string_dictionary_reader.cc',
|
||||
'minidump/minidump_simple_string_dictionary_reader.h',
|
||||
'minidump/minidump_string_list_reader.cc',
|
||||
@ -82,11 +83,14 @@
|
||||
'minidump/module_snapshot_minidump.h',
|
||||
'minidump/process_snapshot_minidump.cc',
|
||||
'minidump/process_snapshot_minidump.h',
|
||||
'memory_snapshot.h',
|
||||
'module_snapshot.h',
|
||||
'process_snapshot.h',
|
||||
'system_snapshot.h',
|
||||
'thread_snapshot.h',
|
||||
'win/cpu_context_win.cc',
|
||||
'win/cpu_context_win.h',
|
||||
'win/exception_snapshot_win.cc',
|
||||
'win/exception_snapshot_win.h',
|
||||
'win/memory_snapshot_win.cc',
|
||||
'win/memory_snapshot_win.h',
|
||||
'win/module_snapshot_win.cc',
|
||||
|
@ -54,6 +54,7 @@
|
||||
'snapshot.gyp:crashpad_snapshot',
|
||||
'../client/client.gyp:crashpad_client',
|
||||
'../compat/compat.gyp:crashpad_compat',
|
||||
'../handler/handler.gyp:crashpad_handler',
|
||||
'../test/test.gyp:crashpad_test',
|
||||
'../third_party/gtest/gtest.gyp:gtest',
|
||||
'../third_party/gtest/gtest.gyp:gtest_main',
|
||||
@ -74,6 +75,8 @@
|
||||
'mac/process_types_test.cc',
|
||||
'mac/system_snapshot_mac_test.cc',
|
||||
'minidump/process_snapshot_minidump_test.cc',
|
||||
'win/cpu_context_win_test.cc',
|
||||
'win/exception_snapshot_win_test.cc',
|
||||
'win/pe_image_annotations_reader_test.cc',
|
||||
'win/process_reader_win_test.cc',
|
||||
'win/system_snapshot_win_test.cc',
|
||||
|
77
snapshot/win/cpu_context_win.cc
Normal file
77
snapshot/win/cpu_context_win.cc
Normal file
@ -0,0 +1,77 @@
|
||||
// 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/cpu_context_win.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "base/logging.h"
|
||||
#include "snapshot/cpu_context.h"
|
||||
|
||||
namespace crashpad {
|
||||
|
||||
#if defined(ARCH_CPU_64_BITS)
|
||||
|
||||
void InitializeX86Context(const WOW64_CONTEXT& context, CPUContextX86* out) {
|
||||
CHECK(false) << "TODO(scottmg) InitializeX86Context()";
|
||||
}
|
||||
|
||||
void InitializeX64Context(const CONTEXT& context, CPUContextX86_64* out) {
|
||||
out->rax = context.Rax;
|
||||
out->rbx = context.Rbx;
|
||||
out->rcx = context.Rcx;
|
||||
out->rdx = context.Rdx;
|
||||
out->rdi = context.Rdi;
|
||||
out->rsi = context.Rsi;
|
||||
out->rbp = context.Rbp;
|
||||
out->rsp = context.Rsp;
|
||||
out->r8 = context.R8;
|
||||
out->r9 = context.R9;
|
||||
out->r10 = context.R10;
|
||||
out->r11 = context.R11;
|
||||
out->r12 = context.R12;
|
||||
out->r13 = context.R13;
|
||||
out->r14 = context.R14;
|
||||
out->r15 = context.R15;
|
||||
out->rip = context.Rip;
|
||||
out->rflags = context.EFlags;
|
||||
out->cs = context.SegCs;
|
||||
out->fs = context.SegFs;
|
||||
out->gs = context.SegGs;
|
||||
|
||||
out->dr0 = context.Dr0;
|
||||
out->dr1 = context.Dr1;
|
||||
out->dr2 = context.Dr2;
|
||||
out->dr3 = context.Dr3;
|
||||
// DR4 and DR5 are obsolete synonyms for DR6 and DR7, see
|
||||
// http://en.wikipedia.org/wiki/X86_debug_register.
|
||||
out->dr4 = context.Dr6;
|
||||
out->dr5 = context.Dr7;
|
||||
out->dr6 = context.Dr6;
|
||||
out->dr7 = context.Dr7;
|
||||
|
||||
static_assert(sizeof(out->fxsave) == sizeof(context.FltSave),
|
||||
"types must be equivalent");
|
||||
memcpy(&out->fxsave, &context.FltSave.ControlWord, sizeof(out->fxsave));
|
||||
}
|
||||
|
||||
#else // ARCH_CPU_64_BITS
|
||||
|
||||
void InitializeX86Context(const CONTEXT& context, CPUContextX86* out) {
|
||||
CHECK(false) << "TODO(scottmg) InitializeX86Context()";
|
||||
}
|
||||
|
||||
#endif // ARCH_CPU_64_BITS
|
||||
|
||||
} // namespace crashpad
|
47
snapshot/win/cpu_context_win.h
Normal file
47
snapshot/win/cpu_context_win.h
Normal file
@ -0,0 +1,47 @@
|
||||
// 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_CPU_CONTEXT_WIN_H_
|
||||
#define CRASHPAD_SNAPSHOT_WIN_CPU_CONTEXT_WIN_H_
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "build/build_config.h"
|
||||
|
||||
namespace crashpad {
|
||||
|
||||
struct CPUContextX86;
|
||||
struct CPUContextX86_64;
|
||||
|
||||
#if defined(ARCH_CPU_64_BITS) || DOXYGEN
|
||||
|
||||
//! \brief Initializes a CPUContextX86 structure from a native context structure
|
||||
//! on Windows.
|
||||
void InitializeX86Context(const WOW64_CONTEXT& context, CPUContextX86* out);
|
||||
|
||||
//! \brief Initializes a CPUContextX86_64 structure from a native context
|
||||
//! structure on Windows.
|
||||
void InitializeX64Context(const CONTEXT& context, CPUContextX86_64* out);
|
||||
|
||||
#else // ARCH_CPU_64_BITS
|
||||
|
||||
//! \brief Initializes a CPUContextX86 structure from a native context structure
|
||||
//! on Windows.
|
||||
void InitializeX86Context(const CONTEXT& context, CPUContextX86* out);
|
||||
|
||||
#endif // ARCH_CPU_64_BITS
|
||||
|
||||
} // namespace crashpad
|
||||
|
||||
#endif // CRASHPAD_SNAPSHOT_WIN_CPU_CONTEXT_WIN_H_
|
54
snapshot/win/cpu_context_win_test.cc
Normal file
54
snapshot/win/cpu_context_win_test.cc
Normal file
@ -0,0 +1,54 @@
|
||||
// Copyright 2014 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/cpu_context_win.h"
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "build/build_config.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "snapshot/cpu_context.h"
|
||||
|
||||
namespace crashpad {
|
||||
namespace test {
|
||||
namespace {
|
||||
|
||||
#if defined(ARCH_CPU_X86_64)
|
||||
|
||||
TEST(CPUContextWin, InitializeX64Context) {
|
||||
CONTEXT context;
|
||||
context.Rax = 10;
|
||||
context.FltSave.TagWord = 11;
|
||||
context.Dr0 = 12;
|
||||
|
||||
// Test the simple case, where everything in the CPUContextX86_64 argument is
|
||||
// set directly from the supplied thread, float, and debug state parameters.
|
||||
{
|
||||
CPUContextX86_64 cpu_context_x86_64 = {};
|
||||
InitializeX64Context(context, &cpu_context_x86_64);
|
||||
EXPECT_EQ(10u, cpu_context_x86_64.rax);
|
||||
EXPECT_EQ(11u, cpu_context_x86_64.fxsave.ftw);
|
||||
EXPECT_EQ(12u, cpu_context_x86_64.dr0);
|
||||
}
|
||||
}
|
||||
|
||||
#else // ARCH_CPU_X86_64
|
||||
|
||||
#error ARCH_CPU_X86
|
||||
|
||||
#endif // ARCH_CPU_X86_64
|
||||
|
||||
} // namespace
|
||||
} // namespace test
|
||||
} // namespace crashpad
|
144
snapshot/win/exception_snapshot_win.cc
Normal file
144
snapshot/win/exception_snapshot_win.cc
Normal file
@ -0,0 +1,144 @@
|
||||
// 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/exception_snapshot_win.h"
|
||||
|
||||
#include "snapshot/win/cpu_context_win.h"
|
||||
#include "snapshot/win/process_reader_win.h"
|
||||
#include "util/win/nt_internals.h"
|
||||
#include "util/win/process_structs.h"
|
||||
|
||||
namespace crashpad {
|
||||
namespace internal {
|
||||
|
||||
ExceptionSnapshotWin::ExceptionSnapshotWin()
|
||||
: ExceptionSnapshot(),
|
||||
context_union_(),
|
||||
context_(),
|
||||
codes_(),
|
||||
thread_id_(0),
|
||||
exception_address_(0),
|
||||
exception_flags_(0),
|
||||
exception_code_(0),
|
||||
initialized_() {
|
||||
}
|
||||
|
||||
ExceptionSnapshotWin::~ExceptionSnapshotWin() {
|
||||
}
|
||||
|
||||
bool ExceptionSnapshotWin::Initialize(ProcessReaderWin* process_reader,
|
||||
DWORD thread_id,
|
||||
WinVMAddress exception_pointers_address) {
|
||||
INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
|
||||
|
||||
bool found_thread = false;
|
||||
for (const auto& loop_thread : process_reader->Threads()) {
|
||||
if (thread_id == loop_thread.id) {
|
||||
found_thread = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found_thread) {
|
||||
LOG(ERROR) << "thread ID " << thread_id << "not found in process";
|
||||
return false;
|
||||
} else {
|
||||
thread_id_ = thread_id;
|
||||
}
|
||||
|
||||
EXCEPTION_POINTERS exception_pointers;
|
||||
if (!process_reader->ReadMemory(exception_pointers_address,
|
||||
sizeof(EXCEPTION_POINTERS),
|
||||
&exception_pointers)) {
|
||||
LOG(ERROR) << "EXCEPTION_POINTERS read failed";
|
||||
return false;
|
||||
}
|
||||
if (!exception_pointers.ExceptionRecord) {
|
||||
LOG(ERROR) << "null ExceptionRecord";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (process_reader->Is64Bit()) {
|
||||
EXCEPTION_RECORD64 first_record;
|
||||
if (!process_reader->ReadMemory(
|
||||
reinterpret_cast<WinVMAddress>(exception_pointers.ExceptionRecord),
|
||||
sizeof(first_record),
|
||||
&first_record)) {
|
||||
LOG(ERROR) << "ExceptionRecord";
|
||||
return false;
|
||||
}
|
||||
exception_code_ = first_record.ExceptionCode;
|
||||
exception_flags_ = first_record.ExceptionFlags;
|
||||
exception_address_ = first_record.ExceptionAddress;
|
||||
for (DWORD i = 0; i < first_record.NumberParameters; ++i)
|
||||
codes_.push_back(first_record.ExceptionInformation[i]);
|
||||
if (first_record.ExceptionRecord) {
|
||||
// https://code.google.com/p/crashpad/issues/detail?id=43
|
||||
LOG(WARNING) << "dropping chained ExceptionRecord";
|
||||
}
|
||||
|
||||
context_.architecture = kCPUArchitectureX86_64;
|
||||
context_.x86_64 = &context_union_.x86_64;
|
||||
// We assume 64-on-64 here in that we're relying on the CONTEXT definition
|
||||
// to be the x64 one.
|
||||
CONTEXT context_record;
|
||||
if (!process_reader->ReadMemory(
|
||||
reinterpret_cast<WinVMAddress>(exception_pointers.ContextRecord),
|
||||
sizeof(context_record),
|
||||
&context_record)) {
|
||||
LOG(ERROR) << "ContextRecord";
|
||||
return false;
|
||||
}
|
||||
InitializeX64Context(context_record, context_.x86_64);
|
||||
} else {
|
||||
CHECK(false) << "TODO(scottmg) x86";
|
||||
return false;
|
||||
}
|
||||
|
||||
INITIALIZATION_STATE_SET_VALID(initialized_);
|
||||
return true;
|
||||
}
|
||||
|
||||
const CPUContext* ExceptionSnapshotWin::Context() const {
|
||||
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||
return &context_;
|
||||
}
|
||||
|
||||
uint64_t ExceptionSnapshotWin::ThreadID() const {
|
||||
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||
return thread_id_;
|
||||
}
|
||||
|
||||
uint32_t ExceptionSnapshotWin::Exception() const {
|
||||
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||
return exception_code_;
|
||||
}
|
||||
|
||||
uint32_t ExceptionSnapshotWin::ExceptionInfo() const {
|
||||
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||
return exception_flags_;
|
||||
}
|
||||
|
||||
uint64_t ExceptionSnapshotWin::ExceptionAddress() const {
|
||||
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||
return exception_address_;
|
||||
}
|
||||
|
||||
const std::vector<uint64_t>& ExceptionSnapshotWin::Codes() const {
|
||||
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||
return codes_;
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace crashpad
|
84
snapshot/win/exception_snapshot_win.h
Normal file
84
snapshot/win/exception_snapshot_win.h
Normal file
@ -0,0 +1,84 @@
|
||||
// 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_EXCEPTION_SNAPSHOT_WIN_H_
|
||||
#define CRASHPAD_SNAPSHOT_WIN_EXCEPTION_SNAPSHOT_WIN_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <windows.h>
|
||||
|
||||
#include "base/basictypes.h"
|
||||
#include "build/build_config.h"
|
||||
#include "snapshot/cpu_context.h"
|
||||
#include "snapshot/exception_snapshot.h"
|
||||
#include "util/misc/initialization_state_dcheck.h"
|
||||
#include "util/win/address_types.h"
|
||||
|
||||
namespace crashpad {
|
||||
|
||||
class ProcessReaderWin;
|
||||
|
||||
namespace internal {
|
||||
|
||||
class ExceptionSnapshotWin final : public ExceptionSnapshot {
|
||||
public:
|
||||
ExceptionSnapshotWin();
|
||||
~ExceptionSnapshotWin() override;
|
||||
|
||||
//! \brief Initializes the object.
|
||||
//!
|
||||
//! \param[in] process_reader A ProcessReader for the process that sustained
|
||||
//! the exception.
|
||||
//! \param[in] thread_id The thread ID in which the exception occurred.
|
||||
//! \param[in] exception_pointers_address The address of an
|
||||
//! `EXCEPTION_POINTERS` record in the target process, passed through from
|
||||
//! the exception handler.
|
||||
//!
|
||||
//! \return `true` if the snapshot could be created, `false` otherwise with
|
||||
//! an appropriate message logged.
|
||||
bool Initialize(ProcessReaderWin* process_reader,
|
||||
DWORD thread_id,
|
||||
WinVMAddress exception_pointers);
|
||||
|
||||
// ExceptionSnapshot:
|
||||
|
||||
const CPUContext* Context() const override;
|
||||
uint64_t ThreadID() const override;
|
||||
uint32_t Exception() const override;
|
||||
uint32_t ExceptionInfo() const override;
|
||||
uint64_t ExceptionAddress() const override;
|
||||
const std::vector<uint64_t>& Codes() const override;
|
||||
|
||||
private:
|
||||
#if defined(ARCH_CPU_X86_FAMILY)
|
||||
union {
|
||||
CPUContextX86 x86;
|
||||
CPUContextX86_64 x86_64;
|
||||
} context_union_;
|
||||
#endif
|
||||
CPUContext context_;
|
||||
std::vector<uint64_t> codes_;
|
||||
uint64_t thread_id_;
|
||||
uint64_t exception_address_;
|
||||
uint32_t exception_flags_;
|
||||
DWORD exception_code_;
|
||||
InitializationStateDcheck initialized_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(ExceptionSnapshotWin);
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
} // namespace crashpad
|
||||
|
||||
#endif // CRASHPAD_SNAPSHOT_WIN_EXCEPTION_SNAPSHOT_WIN_H_
|
243
snapshot/win/exception_snapshot_win_test.cc
Normal file
243
snapshot/win/exception_snapshot_win_test.cc
Normal file
@ -0,0 +1,243 @@
|
||||
// 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/exception_snapshot_win.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "base/strings/stringprintf.h"
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "client/crashpad_client.h"
|
||||
#include "client/crashpad_info.h"
|
||||
#include "handler/win/registration_server.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "snapshot/win/process_reader_win.h"
|
||||
#include "snapshot/win/process_snapshot_win.h"
|
||||
#include "test/win/win_child_process.h"
|
||||
#include "util/thread/thread.h"
|
||||
#include "util/win/scoped_handle.h"
|
||||
|
||||
namespace crashpad {
|
||||
namespace test {
|
||||
namespace {
|
||||
|
||||
HANDLE DuplicateEvent(HANDLE process, HANDLE event) {
|
||||
HANDLE handle;
|
||||
if (DuplicateHandle(GetCurrentProcess(),
|
||||
event,
|
||||
process,
|
||||
&handle,
|
||||
SYNCHRONIZE | EVENT_MODIFY_STATE,
|
||||
false,
|
||||
0)) {
|
||||
return handle;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
class ExceptionSnapshotWinTest : public testing::Test {
|
||||
public:
|
||||
class Delegate : public RegistrationServer::Delegate {
|
||||
public:
|
||||
Delegate()
|
||||
: crashpad_info_address_(0),
|
||||
client_process_(),
|
||||
started_event_(CreateEvent(nullptr, false, false, nullptr)),
|
||||
request_dump_event_(CreateEvent(nullptr, false, false, nullptr)),
|
||||
dump_complete_event_(CreateEvent(nullptr, true, false, nullptr)) {
|
||||
EXPECT_TRUE(started_event_.is_valid());
|
||||
EXPECT_TRUE(request_dump_event_.is_valid());
|
||||
EXPECT_TRUE(dump_complete_event_.is_valid());
|
||||
}
|
||||
|
||||
~Delegate() override {
|
||||
}
|
||||
|
||||
void OnStarted() override {
|
||||
EXPECT_EQ(WAIT_TIMEOUT, WaitForSingleObject(started_event_.get(), 0));
|
||||
SetEvent(started_event_.get());
|
||||
}
|
||||
|
||||
bool RegisterClient(ScopedKernelHANDLE client_process,
|
||||
WinVMAddress crashpad_info_address,
|
||||
HANDLE* request_dump_event,
|
||||
HANDLE* dump_complete_event) override {
|
||||
client_process_ = client_process.Pass();
|
||||
crashpad_info_address_ = crashpad_info_address;
|
||||
*request_dump_event =
|
||||
DuplicateEvent(client_process_.get(), request_dump_event_.get());
|
||||
*dump_complete_event =
|
||||
DuplicateEvent(client_process_.get(), dump_complete_event_.get());
|
||||
return true;
|
||||
}
|
||||
|
||||
void WaitForStart() {
|
||||
DWORD wait_result = WaitForSingleObject(started_event_.get(), INFINITE);
|
||||
if (wait_result == WAIT_FAILED)
|
||||
PLOG(ERROR);
|
||||
ASSERT_EQ(wait_result, WAIT_OBJECT_0);
|
||||
}
|
||||
|
||||
void WaitForDumpRequestAndValidateException(void* break_near) {
|
||||
// Wait until triggered, and then grab information from the child.
|
||||
WaitForSingleObject(request_dump_event_.get(), INFINITE);
|
||||
|
||||
// Snapshot the process and exception.
|
||||
ProcessReaderWin process_reader;
|
||||
ASSERT_TRUE(process_reader.Initialize(client_process_.get()));
|
||||
CrashpadInfo crashpad_info;
|
||||
ASSERT_TRUE(process_reader.ReadMemory(
|
||||
crashpad_info_address_, sizeof(crashpad_info), &crashpad_info));
|
||||
ProcessSnapshotWin snapshot;
|
||||
snapshot.Initialize(client_process_.get());
|
||||
snapshot.InitializeException(
|
||||
crashpad_info.thread_id(),
|
||||
reinterpret_cast<WinVMAddress>(crashpad_info.exception_pointers()));
|
||||
|
||||
// Confirm the exception record was read correctly.
|
||||
EXPECT_NE(snapshot.Exception()->ThreadID(), 0u);
|
||||
EXPECT_EQ(snapshot.Exception()->Exception(), EXCEPTION_BREAKPOINT);
|
||||
|
||||
// Verify the exception happened at the expected location with a bit of
|
||||
// slop space to allow for reading the current PC before the exception
|
||||
// happens. See CrashingChildProcess::Run().
|
||||
const uint64_t kAllowedOffset = 64;
|
||||
EXPECT_GT(snapshot.Exception()->ExceptionAddress(),
|
||||
reinterpret_cast<uint64_t>(break_near));
|
||||
EXPECT_LT(snapshot.Exception()->ExceptionAddress(),
|
||||
reinterpret_cast<uint64_t>(break_near) + kAllowedOffset);
|
||||
|
||||
// Notify the child that we're done.
|
||||
SetEvent(dump_complete_event_.get());
|
||||
}
|
||||
|
||||
ScopedKernelHANDLE* request_dump_event() { return &request_dump_event_; }
|
||||
ScopedKernelHANDLE* dump_complete_event() { return &dump_complete_event_; }
|
||||
|
||||
private:
|
||||
WinVMAddress crashpad_info_address_;
|
||||
ScopedKernelHANDLE client_process_;
|
||||
ScopedKernelHANDLE started_event_;
|
||||
ScopedKernelHANDLE request_dump_event_;
|
||||
ScopedKernelHANDLE dump_complete_event_;
|
||||
};
|
||||
};
|
||||
|
||||
// Runs the RegistrationServer on a background thread.
|
||||
class RunServerThread : public Thread {
|
||||
public:
|
||||
// Instantiates a thread which will invoke server->Run(pipe_name, delegate).
|
||||
RunServerThread(RegistrationServer* server,
|
||||
const base::string16& pipe_name,
|
||||
RegistrationServer::Delegate* delegate)
|
||||
: server_(server), pipe_name_(pipe_name), delegate_(delegate) {}
|
||||
~RunServerThread() override {}
|
||||
|
||||
private:
|
||||
// Thread:
|
||||
void ThreadMain() override { server_->Run(pipe_name_, delegate_); }
|
||||
|
||||
RegistrationServer* server_;
|
||||
base::string16 pipe_name_;
|
||||
RegistrationServer::Delegate* delegate_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(RunServerThread);
|
||||
};
|
||||
|
||||
// During destruction, ensures that the server is stopped and the background
|
||||
// thread joined.
|
||||
class ScopedStopServerAndJoinThread {
|
||||
public:
|
||||
explicit ScopedStopServerAndJoinThread(RegistrationServer* server,
|
||||
Thread* thread)
|
||||
: server_(server), thread_(thread) {}
|
||||
~ScopedStopServerAndJoinThread() {
|
||||
server_->Stop();
|
||||
thread_->Join();
|
||||
}
|
||||
|
||||
private:
|
||||
RegistrationServer* server_;
|
||||
Thread* thread_;
|
||||
DISALLOW_COPY_AND_ASSIGN(ScopedStopServerAndJoinThread);
|
||||
};
|
||||
|
||||
std::string ReadString(FileHandle handle) {
|
||||
size_t length = 0;
|
||||
EXPECT_TRUE(LoggingReadFile(handle, &length, sizeof(length)));
|
||||
scoped_ptr<char[]> buffer(new char[length]);
|
||||
EXPECT_TRUE(LoggingReadFile(handle, &buffer[0], length));
|
||||
return std::string(&buffer[0], length);
|
||||
}
|
||||
|
||||
void WriteString(FileHandle handle, const std::string& str) {
|
||||
size_t length = str.size();
|
||||
EXPECT_TRUE(LoggingWriteFile(handle, &length, sizeof(length)));
|
||||
EXPECT_TRUE(LoggingWriteFile(handle, &str[0], length));
|
||||
}
|
||||
|
||||
__declspec(noinline) void* CurrentAddress() {
|
||||
return _ReturnAddress();
|
||||
}
|
||||
|
||||
class CrashingChildProcess final : public WinChildProcess {
|
||||
public:
|
||||
CrashingChildProcess() : WinChildProcess() {}
|
||||
~CrashingChildProcess() {}
|
||||
|
||||
private:
|
||||
int Run() override {
|
||||
std::string pipe_name = ReadString(ReadPipeHandle());
|
||||
CrashpadClient client;
|
||||
EXPECT_TRUE(client.SetHandler(pipe_name));
|
||||
EXPECT_TRUE(client.UseHandler());
|
||||
// Save the address where we're about to crash so the exception handler can
|
||||
// verify it's in approximately the right location (with a bit of fudge for
|
||||
// the code between here and the __debugbreak()).
|
||||
void* break_address = CurrentAddress();
|
||||
LoggingWriteFile(WritePipeHandle(), &break_address, sizeof(break_address));
|
||||
__debugbreak();
|
||||
return 0;
|
||||
};
|
||||
};
|
||||
|
||||
TEST_F(ExceptionSnapshotWinTest, ChildCrash) {
|
||||
// Set up the registration server on a background thread.
|
||||
RegistrationServer server;
|
||||
std::string pipe_name = "\\\\.\\pipe\\handler_test_pipe_" +
|
||||
base::StringPrintf("%08x", GetCurrentProcessId());
|
||||
base::string16 pipe_name_16 = base::UTF8ToUTF16(pipe_name);
|
||||
Delegate delegate;
|
||||
RunServerThread server_thread(&server, pipe_name_16, &delegate);
|
||||
server_thread.Start();
|
||||
ScopedStopServerAndJoinThread scoped_stop_server_and_join_thread(
|
||||
&server, &server_thread);
|
||||
ASSERT_NO_FATAL_FAILURE(delegate.WaitForStart());
|
||||
|
||||
// Spawn a child process that immediately crashes.
|
||||
WinChildProcess::EntryPoint<CrashingChildProcess>();
|
||||
scoped_ptr<WinChildProcess::Handles> handle = WinChildProcess::Launch();
|
||||
WriteString(handle->write.get(), pipe_name);
|
||||
|
||||
void* break_near_address;
|
||||
LoggingReadFile(
|
||||
handle->read.get(), &break_near_address, sizeof(break_near_address));
|
||||
|
||||
// Verify the exception information is as expected.
|
||||
delegate.WaitForDumpRequestAndValidateException(break_near_address);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace test
|
||||
} // namespace crashpad
|
@ -18,6 +18,7 @@
|
||||
|
||||
#include "base/memory/scoped_ptr.h"
|
||||
#include "base/numerics/safe_conversions.h"
|
||||
#include "util/win/nt_internals.h"
|
||||
#include "util/win/process_structs.h"
|
||||
#include "util/win/scoped_handle.h"
|
||||
#include "util/win/time.h"
|
||||
@ -26,49 +27,6 @@ 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));
|
||||
}
|
||||
|
||||
// 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().
|
||||
|
@ -14,6 +14,7 @@
|
||||
|
||||
#include "snapshot/win/process_snapshot_win.h"
|
||||
|
||||
#include "base/logging.h"
|
||||
#include "snapshot/win/module_snapshot_win.h"
|
||||
#include "util/win/time.h"
|
||||
|
||||
@ -24,7 +25,7 @@ ProcessSnapshotWin::ProcessSnapshotWin()
|
||||
system_(),
|
||||
threads_(),
|
||||
modules_(),
|
||||
// TODO(scottmg): exception_(),
|
||||
exception_(),
|
||||
process_reader_(),
|
||||
report_id_(),
|
||||
client_id_(),
|
||||
@ -53,6 +54,22 @@ bool ProcessSnapshotWin::Initialize(HANDLE process) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ProcessSnapshotWin::InitializeException(
|
||||
DWORD thread_id,
|
||||
WinVMAddress exception_pointers) {
|
||||
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||
DCHECK(!exception_);
|
||||
|
||||
exception_.reset(new internal::ExceptionSnapshotWin());
|
||||
if (!exception_->Initialize(
|
||||
&process_reader_, thread_id, exception_pointers)) {
|
||||
exception_.reset();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ProcessSnapshotWin::GetCrashpadOptions(
|
||||
CrashpadInfoClientOptions* options) {
|
||||
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||
@ -149,8 +166,7 @@ std::vector<const ModuleSnapshot*> ProcessSnapshotWin::Modules() const {
|
||||
}
|
||||
|
||||
const ExceptionSnapshot* ProcessSnapshotWin::Exception() const {
|
||||
CHECK(false) << "TODO(scottmg): Exception()";
|
||||
return nullptr;
|
||||
return exception_.get();
|
||||
}
|
||||
|
||||
void ProcessSnapshotWin::InitializeThreads() {
|
||||
|
@ -31,11 +31,13 @@
|
||||
#include "snapshot/process_snapshot.h"
|
||||
#include "snapshot/system_snapshot.h"
|
||||
#include "snapshot/thread_snapshot.h"
|
||||
#include "snapshot/win/exception_snapshot_win.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/win/address_types.h"
|
||||
#include "util/stdlib/pointer_container.h"
|
||||
|
||||
namespace crashpad {
|
||||
@ -55,6 +57,20 @@ class ProcessSnapshotWin final : public ProcessSnapshot {
|
||||
//! an appropriate message logged.
|
||||
bool Initialize(HANDLE process);
|
||||
|
||||
//! \brief Initializes the object's exception.
|
||||
//!
|
||||
//! This populates the data to be returned by Exception(). The parameters may
|
||||
//! be passed directly through from a Windows exception handler.
|
||||
//!
|
||||
//! This method must not be called until after a successful call to
|
||||
//! Initialize().
|
||||
//!
|
||||
//! \return `true` if the exception information could be initialized, `false`
|
||||
//! otherwise with an appropriate message logged. When this method returns
|
||||
//! `false`, the ProcessSnapshotWin object's validity remains unchanged.
|
||||
bool InitializeException(DWORD thread_id,
|
||||
WinVMAddress exception_pointers);
|
||||
|
||||
//! \brief Sets the value to be returned by ReportID().
|
||||
//!
|
||||
//! The crash report ID is under the control of the snapshot producer, which
|
||||
@ -113,7 +129,7 @@ class ProcessSnapshotWin final : public ProcessSnapshot {
|
||||
internal::SystemSnapshotWin system_;
|
||||
PointerVector<internal::ThreadSnapshotWin> threads_;
|
||||
PointerVector<internal::ModuleSnapshotWin> modules_;
|
||||
// TODO(scottmg): scoped_ptr<internal::ExceptionSnapshotWin> exception_;
|
||||
scoped_ptr<internal::ExceptionSnapshotWin> exception_;
|
||||
ProcessReaderWin process_reader_;
|
||||
UUID report_id_;
|
||||
UUID client_id_;
|
||||
|
@ -15,60 +15,12 @@
|
||||
#include "snapshot/win/thread_snapshot_win.h"
|
||||
|
||||
#include "base/logging.h"
|
||||
#include "snapshot/win/cpu_context_win.h"
|
||||
#include "snapshot/win/process_reader_win.h"
|
||||
|
||||
namespace crashpad {
|
||||
namespace internal {
|
||||
|
||||
namespace {
|
||||
|
||||
void InitializeX64Context(const CONTEXT& context,
|
||||
CPUContextX86_64* out) {
|
||||
out->rax = context.Rax;
|
||||
out->rbx = context.Rbx;
|
||||
out->rcx = context.Rcx;
|
||||
out->rdx = context.Rdx;
|
||||
out->rdi = context.Rdi;
|
||||
out->rsi = context.Rsi;
|
||||
out->rbp = context.Rbp;
|
||||
out->rsp = context.Rsp;
|
||||
out->r8 = context.R8;
|
||||
out->r9 = context.R9;
|
||||
out->r10 = context.R10;
|
||||
out->r11 = context.R11;
|
||||
out->r12 = context.R12;
|
||||
out->r13 = context.R13;
|
||||
out->r14 = context.R14;
|
||||
out->r15 = context.R15;
|
||||
out->rip = context.Rip;
|
||||
out->rflags = context.EFlags;
|
||||
out->cs = context.SegCs;
|
||||
out->fs = context.SegFs;
|
||||
out->gs = context.SegGs;
|
||||
|
||||
out->dr0 = context.Dr0;
|
||||
out->dr1 = context.Dr1;
|
||||
out->dr2 = context.Dr2;
|
||||
out->dr3 = context.Dr3;
|
||||
// DR4 and DR5 are obsolete synonyms for DR6 and DR7, see
|
||||
// http://en.wikipedia.org/wiki/X86_debug_register.
|
||||
out->dr4 = context.Dr6;
|
||||
out->dr5 = context.Dr7;
|
||||
out->dr6 = context.Dr6;
|
||||
out->dr7 = context.Dr7;
|
||||
|
||||
static_assert(sizeof(out->fxsave) == sizeof(context.FltSave),
|
||||
"types must be equivalent");
|
||||
memcpy(&out->fxsave, &context.FltSave.ControlWord, sizeof(out->fxsave));
|
||||
}
|
||||
|
||||
void InitializeX86Context(const CONTEXT& context,
|
||||
CPUContextX86* out) {
|
||||
CHECK(false) << "TODO(scottmg) InitializeX86Context()";
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
ThreadSnapshotWin::ThreadSnapshotWin()
|
||||
: ThreadSnapshot(), context_(), stack_(), thread_(), initialized_() {
|
||||
}
|
||||
@ -85,7 +37,7 @@ bool ThreadSnapshotWin::Initialize(
|
||||
stack_.Initialize(
|
||||
process_reader, thread_.stack_region_address, thread_.stack_region_size);
|
||||
|
||||
#if defined(ARCH_CPU_X86_FAMILY)
|
||||
#if defined(ARCH_CPU_X86_64)
|
||||
if (process_reader->Is64Bit()) {
|
||||
context_.architecture = kCPUArchitectureX86_64;
|
||||
context_.x86_64 = &context_union_.x86_64;
|
||||
@ -93,9 +45,13 @@ bool ThreadSnapshotWin::Initialize(
|
||||
} else {
|
||||
context_.architecture = kCPUArchitectureX86;
|
||||
context_.x86 = &context_union_.x86;
|
||||
InitializeX86Context(process_reader_thread.context, context_.x86);
|
||||
InitializeX86Context(
|
||||
*reinterpret_cast<const WOW64_CONTEXT*>(&process_reader_thread.context),
|
||||
context_.x86);
|
||||
}
|
||||
#endif
|
||||
#else
|
||||
#error ARCH_CPU_X86
|
||||
#endif // ARCH_CPU_X86_64
|
||||
|
||||
INITIALIZATION_STATE_SET_VALID(initialized_);
|
||||
return true;
|
||||
|
@ -149,6 +149,8 @@
|
||||
'win/checked_win_address_range.h',
|
||||
'win/module_version.cc',
|
||||
'win/module_version.h',
|
||||
'win/nt_internals.cc',
|
||||
'win/nt_internals.h',
|
||||
'win/process_info.cc',
|
||||
'win/process_info.h',
|
||||
'win/process_structs.h',
|
||||
|
91
util/win/nt_internals.cc
Normal file
91
util/win/nt_internals.cc
Normal file
@ -0,0 +1,91 @@
|
||||
// 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 "util/win/nt_internals.h"
|
||||
|
||||
#include "base/logging.h"
|
||||
|
||||
namespace crashpad {
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
// 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));
|
||||
}
|
||||
|
||||
// Explicit instantiations with the only 2 valid template arguments to avoid
|
||||
// putting the body of the function in the header.
|
||||
template NTSTATUS NtOpenThread<process_types::internal::Traits32>(
|
||||
PHANDLE thread_handle,
|
||||
ACCESS_MASK desired_access,
|
||||
POBJECT_ATTRIBUTES object_attributes,
|
||||
const process_types::CLIENT_ID<process_types::internal::Traits32>*
|
||||
client_id);
|
||||
|
||||
template NTSTATUS NtOpenThread<process_types::internal::Traits64>(
|
||||
PHANDLE thread_handle,
|
||||
ACCESS_MASK desired_access,
|
||||
POBJECT_ATTRIBUTES object_attributes,
|
||||
const process_types::CLIENT_ID<process_types::internal::Traits64>*
|
||||
client_id);
|
||||
|
||||
} // namespace crashpad
|
48
util/win/nt_internals.h
Normal file
48
util/win/nt_internals.h
Normal file
@ -0,0 +1,48 @@
|
||||
// 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 <windows.h>
|
||||
#include <winternl.h>
|
||||
|
||||
#include "util/win/process_structs.h"
|
||||
|
||||
namespace crashpad {
|
||||
|
||||
// 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)
|
||||
|
||||
// winternal.h defines THREADINFOCLASS, but not all members.
|
||||
enum { ThreadBasicInformation = 0 };
|
||||
|
||||
NTSTATUS NtQuerySystemInformation(
|
||||
SYSTEM_INFORMATION_CLASS system_information_class,
|
||||
PVOID system_information,
|
||||
ULONG system_information_length,
|
||||
PULONG return_length);
|
||||
|
||||
NTSTATUS NtQueryInformationThread(HANDLE thread_handle,
|
||||
THREADINFOCLASS thread_information_class,
|
||||
PVOID thread_information,
|
||||
ULONG thread_information_length,
|
||||
PULONG return_length);
|
||||
|
||||
template <class Traits>
|
||||
NTSTATUS NtOpenThread(PHANDLE thread_handle,
|
||||
ACCESS_MASK desired_access,
|
||||
POBJECT_ATTRIBUTES object_attributes,
|
||||
const process_types::CLIENT_ID<Traits>* client_id);
|
||||
|
||||
} // namespace crashpad
|
Loading…
x
Reference in New Issue
Block a user