win: Capture memory pointed to by the stack

Change-Id: Ide75475aa9c42edf36c3a709bfc7dfbfed68b0d3
Reviewed-on: https://chromium-review.googlesource.com/322261
Reviewed-by: Mark Mentovai <mark@chromium.org>
This commit is contained in:
Scott Graham 2016-01-14 12:50:22 -08:00
parent 83247fda60
commit feb3aa3923
15 changed files with 454 additions and 214 deletions

View File

@ -28,6 +28,7 @@
#include "base/macros.h"
#include "build/build_config.h"
#include "client/crashpad_client.h"
#include "client/crashpad_info.h"
#include "util/win/critical_section_with_debug_info.h"
#include "util/win/get_function.h"
@ -164,6 +165,18 @@ int CrashyMain(int argc, wchar_t* argv[]) {
return EXIT_FAILURE;
}
// Make sure data pointed to by the stack is captured.
const int kDataSize = 512;
int* pointed_to_data = new int[kDataSize];
for (int i = 0; i < kDataSize; ++i)
pointed_to_data[i] = i | ((i % 2 == 0) ? 0x80000000 : 0);
int* offset_pointer = &pointed_to_data[128];
// Encourage the compiler to keep this variable around.
printf("%p, %p\n", offset_pointer, &offset_pointer);
crashpad::CrashpadInfo::GetCrashpadInfo()
->set_gather_indirectly_referenced_memory(TriState::kEnabled);
AllocateMemoryOfVariousProtections();
if (InitializeCriticalSectionWithDebugInfoIfPossible(

128
snapshot/capture_memory.cc Normal file
View File

@ -0,0 +1,128 @@
// Copyright 2016 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/capture_memory.h"
#include <stdint.h>
#include <limits>
#include "base/memory/scoped_ptr.h"
#include "snapshot/memory_snapshot.h"
namespace crashpad {
namespace internal {
namespace {
void MaybeCaptureMemoryAround(CaptureMemory::Delegate* delegate,
uint64_t address) {
const uint64_t non_address_offset = 0x10000;
if (address < non_address_offset)
return;
const uint64_t max_address = delegate->Is64Bit() ?
std::numeric_limits<uint64_t>::max() :
std::numeric_limits<uint32_t>::max();
if (address > max_address - non_address_offset)
return;
const uint64_t kRegisterByteOffset = 256;
const uint64_t target = address - kRegisterByteOffset;
const uint64_t size = 1024;
static_assert(kRegisterByteOffset <= size / 2,
"negative offset too large");
auto ranges =
delegate->GetReadableRanges(CheckedRange<uint64_t>(target, size));
for (const auto& range : ranges) {
delegate->AddNewMemorySnapshot(range);
}
}
template <class T>
void CaptureAtPointersInRange(uint8_t* buffer,
uint64_t buffer_size,
CaptureMemory::Delegate* delegate) {
for (uint64_t address_offset = 0; address_offset < buffer_size;
address_offset += sizeof(T)) {
uint64_t target_address = *reinterpret_cast<T*>(&buffer[address_offset]);
MaybeCaptureMemoryAround(delegate, target_address);
}
}
} // namespace
// static
void CaptureMemory::PointedToByContext(const CPUContext& context,
Delegate* delegate) {
#if defined(ARCH_CPU_X86_FAMILY)
if (context.architecture == kCPUArchitectureX86_64) {
MaybeCaptureMemoryAround(delegate, context.x86_64->rax);
MaybeCaptureMemoryAround(delegate, context.x86_64->rbx);
MaybeCaptureMemoryAround(delegate, context.x86_64->rcx);
MaybeCaptureMemoryAround(delegate, context.x86_64->rdx);
MaybeCaptureMemoryAround(delegate, context.x86_64->rdi);
MaybeCaptureMemoryAround(delegate, context.x86_64->rsi);
MaybeCaptureMemoryAround(delegate, context.x86_64->rbp);
MaybeCaptureMemoryAround(delegate, context.x86_64->r8);
MaybeCaptureMemoryAround(delegate, context.x86_64->r9);
MaybeCaptureMemoryAround(delegate, context.x86_64->r10);
MaybeCaptureMemoryAround(delegate, context.x86_64->r11);
MaybeCaptureMemoryAround(delegate, context.x86_64->r12);
MaybeCaptureMemoryAround(delegate, context.x86_64->r13);
MaybeCaptureMemoryAround(delegate, context.x86_64->r14);
MaybeCaptureMemoryAround(delegate, context.x86_64->r15);
MaybeCaptureMemoryAround(delegate, context.x86_64->rip);
} else {
MaybeCaptureMemoryAround(delegate, context.x86->eax);
MaybeCaptureMemoryAround(delegate, context.x86->ebx);
MaybeCaptureMemoryAround(delegate, context.x86->ecx);
MaybeCaptureMemoryAround(delegate, context.x86->edx);
MaybeCaptureMemoryAround(delegate, context.x86->edi);
MaybeCaptureMemoryAround(delegate, context.x86->esi);
MaybeCaptureMemoryAround(delegate, context.x86->ebp);
MaybeCaptureMemoryAround(delegate, context.x86->eip);
}
#else
#error non-x86
#endif
}
// static
void CaptureMemory::PointedToByMemoryRange(const MemorySnapshot& memory,
Delegate* delegate) {
if (memory.Size() == 0)
return;
const size_t alignment =
delegate->Is64Bit() ? sizeof(uint64_t) : sizeof(uint32_t);
if (memory.Address() % alignment != 0 || memory.Size() % alignment != 0) {
LOG(ERROR) << "unaligned range";
return;
}
scoped_ptr<uint8_t[]> buffer(new uint8_t[memory.Size()]);
if (!delegate->ReadMemory(memory.Address(), memory.Size(), buffer.get())) {
LOG(ERROR) << "ReadMemory";
return;
}
if (delegate->Is64Bit())
CaptureAtPointersInRange<uint64_t>(buffer.get(), memory.Size(), delegate);
else
CaptureAtPointersInRange<uint32_t>(buffer.get(), memory.Size(), delegate);
}
} // namespace internal
} // namespace crashpad

98
snapshot/capture_memory.h Normal file
View File

@ -0,0 +1,98 @@
// Copyright 2016 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_CAPTURE_MEMORY_H_
#define CRASHPAD_SNAPSHOT_CAPTURE_MEMORY_H_
#include <stdint.h>
#include <vector>
#include "snapshot/cpu_context.h"
#include "util/numeric/checked_range.h"
namespace crashpad {
class MemorySnapshot;
namespace internal {
class CaptureMemory {
public:
//! \brief An interface to a platform-specific process reader.
class Delegate {
public:
virtual ~Delegate() {}
//! \return `true` if the target process is a 64-bit process.
virtual bool Is64Bit() const = 0;
//! \brief Attempts to read \a num_bytes bytes from the target process
//! starting at address \a at into \a into.
//!
//! \return `true` if the entire region could be read, or `false` with an
//! error logged.
virtual bool ReadMemory(uint64_t at,
uint64_t num_bytes,
void* into) const = 0;
//! \brief Given a range to be read from the target process, returns a
//! vector
//! of ranges, representing the readable portions of the original range.
//!
//! \param[in] range The range being identified.
//!
//! \return A vector of ranges corresponding to the portion of \a range that
//! is readable.
virtual std::vector<CheckedRange<uint64_t>> GetReadableRanges(
const CheckedRange<uint64_t, uint64_t>& range) const = 0;
//! \brief Adds the given range representing a memory snapshot in the target
//! process to the result.
virtual void AddNewMemorySnapshot(
const CheckedRange<uint64_t, uint64_t>& range) = 0;
};
//! \brief For all registers that appear to be pointer-like in \a context,
//! captures a small amount of memory near their pointed to location.
//!
//! "Pointer-like" in this context means not too close to zero (signed or
//! unsigned) so that there's a reasonable chance that the value is a pointer.
//!
//! \param[in] context The context to inspect.
//! \param[in] process_reader A MemoryCaptureProcessReader to read from the
//! target process, and that handles adding new ranges.
static void PointedToByContext(const CPUContext& context, Delegate* delegate);
//! \brief For all pointer-like values in a memory range of the target
//! process,
//! captures a small amount of memory near the pointed to location.
//!
//! \param[in] memory An existing MemorySnapshot of the range to search. The
//! base address and size must be pointer-aligned and an integral number
//! of
//! pointers long.
//! \param[in] process_reader A MemoryCaptureProcessReader to read from the
//! target process, and that handles adding new ranges.
static void PointedToByMemoryRange(const MemorySnapshot& memory,
Delegate* delegate);
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(CaptureMemory);
};
} // namespace internal
} // namespace crashpad
#endif // CRASHPAD_SNAPSHOT_CAPTURE_MEMORY_H_

View File

@ -70,14 +70,13 @@ class ScopedUnsetCrashpadInfoOptions {
DISALLOW_COPY_AND_ASSIGN(ScopedUnsetCrashpadInfoOptions);
};
TEST(CrashpadInfoClientOptions, OneModule) {
// Make sure that the initial state has all values unset.
CrashpadInfoClientOptions SelfProcessSnapshotAndGetCrashpadOptions() {
#if defined(OS_MACOSX)
ProcessSnapshotMac process_snapshot;
ASSERT_TRUE(process_snapshot.Initialize(mach_task_self()));
EXPECT_TRUE(process_snapshot.Initialize(mach_task_self()));
#elif defined(OS_WIN)
ProcessSnapshotWin process_snapshot;
ASSERT_TRUE(process_snapshot.Initialize(
EXPECT_TRUE(process_snapshot.Initialize(
GetCurrentProcess(), ProcessSuspensionState::kRunning, 0));
#else
#error Port.
@ -85,6 +84,12 @@ TEST(CrashpadInfoClientOptions, OneModule) {
CrashpadInfoClientOptions options;
process_snapshot.GetCrashpadOptions(&options);
return options;
}
TEST(CrashpadInfoClientOptions, OneModule) {
// Make sure that the initial state has all values unset.
auto options = SelfProcessSnapshotAndGetCrashpadOptions();
EXPECT_EQ(TriState::kUnset, options.crashpad_handler_behavior);
EXPECT_EQ(TriState::kUnset, options.system_crash_reporter_forwarding);
@ -98,7 +103,7 @@ TEST(CrashpadInfoClientOptions, OneModule) {
crashpad_info->set_crashpad_handler_behavior(TriState::kEnabled);
process_snapshot.GetCrashpadOptions(&options);
options = SelfProcessSnapshotAndGetCrashpadOptions();
EXPECT_EQ(TriState::kEnabled, options.crashpad_handler_behavior);
EXPECT_EQ(TriState::kUnset, options.system_crash_reporter_forwarding);
EXPECT_EQ(TriState::kUnset, options.gather_indirectly_referenced_memory);
@ -109,7 +114,7 @@ TEST(CrashpadInfoClientOptions, OneModule) {
crashpad_info->set_system_crash_reporter_forwarding(TriState::kDisabled);
process_snapshot.GetCrashpadOptions(&options);
options = SelfProcessSnapshotAndGetCrashpadOptions();
EXPECT_EQ(TriState::kUnset, options.crashpad_handler_behavior);
EXPECT_EQ(TriState::kDisabled, options.system_crash_reporter_forwarding);
EXPECT_EQ(TriState::kUnset, options.gather_indirectly_referenced_memory);
@ -120,7 +125,7 @@ TEST(CrashpadInfoClientOptions, OneModule) {
crashpad_info->set_gather_indirectly_referenced_memory(TriState::kEnabled);
process_snapshot.GetCrashpadOptions(&options);
options = SelfProcessSnapshotAndGetCrashpadOptions();
EXPECT_EQ(TriState::kUnset, options.crashpad_handler_behavior);
EXPECT_EQ(TriState::kUnset, options.system_crash_reporter_forwarding);
EXPECT_EQ(TriState::kEnabled, options.gather_indirectly_referenced_memory);
@ -199,21 +204,9 @@ TEST(CrashpadInfoClientOptions, TwoModules) {
dl_handle.LookUpSymbol<CrashpadInfo* (*)()>("TestModule_GetCrashpadInfo");
ASSERT_TRUE(TestModule_GetCrashpadInfo);
auto options = SelfProcessSnapshotAndGetCrashpadOptions();
// Make sure that the initial state has all values unset.
#if defined(OS_MACOSX)
ProcessSnapshotMac process_snapshot;
ASSERT_TRUE(process_snapshot.Initialize(mach_task_self()));
#elif defined(OS_WIN)
ProcessSnapshotWin process_snapshot;
ASSERT_TRUE(process_snapshot.Initialize(
GetCurrentProcess(), ProcessSuspensionState::kRunning, 0));
#else
#error Port.
#endif // OS_MACOSX
CrashpadInfoClientOptions options;
process_snapshot.GetCrashpadOptions(&options);
EXPECT_EQ(TriState::kUnset, options.crashpad_handler_behavior);
EXPECT_EQ(TriState::kUnset, options.system_crash_reporter_forwarding);
EXPECT_EQ(TriState::kUnset, options.gather_indirectly_referenced_memory);
@ -232,7 +225,7 @@ TEST(CrashpadInfoClientOptions, TwoModules) {
// When only one module sets a value, it applies to the entire process.
remote_crashpad_info->set_crashpad_handler_behavior(TriState::kEnabled);
process_snapshot.GetCrashpadOptions(&options);
options = SelfProcessSnapshotAndGetCrashpadOptions();
EXPECT_EQ(TriState::kEnabled, options.crashpad_handler_behavior);
EXPECT_EQ(TriState::kUnset, options.system_crash_reporter_forwarding);
EXPECT_EQ(TriState::kUnset, options.gather_indirectly_referenced_memory);
@ -242,7 +235,7 @@ TEST(CrashpadInfoClientOptions, TwoModules) {
// module, because the local module loaded the remote module.
local_crashpad_info->set_crashpad_handler_behavior(TriState::kDisabled);
process_snapshot.GetCrashpadOptions(&options);
options = SelfProcessSnapshotAndGetCrashpadOptions();
EXPECT_EQ(TriState::kDisabled, options.crashpad_handler_behavior);
EXPECT_EQ(TriState::kUnset, options.system_crash_reporter_forwarding);
EXPECT_EQ(TriState::kUnset, options.gather_indirectly_referenced_memory);
@ -256,7 +249,7 @@ TEST(CrashpadInfoClientOptions, TwoModules) {
remote_crashpad_info->set_system_crash_reporter_forwarding(
TriState::kDisabled);
process_snapshot.GetCrashpadOptions(&options);
options = SelfProcessSnapshotAndGetCrashpadOptions();
EXPECT_EQ(TriState::kUnset, options.crashpad_handler_behavior);
EXPECT_EQ(TriState::kDisabled, options.system_crash_reporter_forwarding);
EXPECT_EQ(TriState::kUnset, options.gather_indirectly_referenced_memory);
@ -267,7 +260,7 @@ TEST(CrashpadInfoClientOptions, TwoModules) {
local_crashpad_info->set_system_crash_reporter_forwarding(
TriState::kEnabled);
process_snapshot.GetCrashpadOptions(&options);
options = SelfProcessSnapshotAndGetCrashpadOptions();
EXPECT_EQ(TriState::kUnset, options.crashpad_handler_behavior);
EXPECT_EQ(TriState::kEnabled, options.system_crash_reporter_forwarding);
EXPECT_EQ(TriState::kUnset, options.gather_indirectly_referenced_memory);

View File

@ -30,6 +30,8 @@
'..',
],
'sources': [
'capture_memory.cc',
'capture_memory.h',
'cpu_architecture.h',
'cpu_context.cc',
'cpu_context.h',
@ -89,12 +91,12 @@
'process_snapshot.h',
'system_snapshot.h',
'thread_snapshot.h',
'win/capture_context_memory.cc',
'win/capture_context_memory.h',
'win/cpu_context_win.cc',
'win/cpu_context_win.h',
'win/exception_snapshot_win.cc',
'win/exception_snapshot_win.h',
'win/capture_memory_delegate_win.cc',
'win/capture_memory_delegate_win.h',
'win/memory_map_region_snapshot_win.cc',
'win/memory_map_region_snapshot_win.h',
'win/memory_snapshot_win.cc',

View File

@ -1,103 +0,0 @@
// Copyright 2016 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/capture_context_memory.h"
#include <stdint.h>
#include <limits>
#include "snapshot/win/memory_snapshot_win.h"
#include "snapshot/win/process_reader_win.h"
namespace crashpad {
namespace internal {
namespace {
void MaybeCaptureMemoryAround(ProcessReaderWin* process_reader,
WinVMAddress address,
PointerVector<MemorySnapshotWin>* into) {
const WinVMAddress non_address_offset = 0x10000;
if (address < non_address_offset)
return;
if (process_reader->Is64Bit()) {
if (address >= std::numeric_limits<uint64_t>::max() - non_address_offset)
return;
} else {
if (address >= std::numeric_limits<uint32_t>::max() - non_address_offset)
return;
}
const WinVMSize kRegisterByteOffset = 32;
const WinVMAddress target = address - kRegisterByteOffset;
const WinVMSize size = 128;
auto ranges = process_reader->GetProcessInfo().GetReadableRanges(
CheckedRange<WinVMAddress, WinVMSize>(target, size));
for (const auto& range : ranges) {
internal::MemorySnapshotWin* snapshot = new internal::MemorySnapshotWin();
snapshot->Initialize(process_reader, range.base(), range.size());
into->push_back(snapshot);
}
}
} // namespace
void CaptureMemoryPointedToByContext(const CPUContext& context,
ProcessReaderWin* process_reader,
const ProcessReaderWin::Thread& thread,
PointerVector<MemorySnapshotWin>* into) {
#if defined(ARCH_CPU_X86_64)
if (context.architecture == kCPUArchitectureX86_64) {
MaybeCaptureMemoryAround(process_reader, context.x86_64->rax, into);
MaybeCaptureMemoryAround(process_reader, context.x86_64->rbx, into);
MaybeCaptureMemoryAround(process_reader, context.x86_64->rcx, into);
MaybeCaptureMemoryAround(process_reader, context.x86_64->rdx, into);
MaybeCaptureMemoryAround(process_reader, context.x86_64->rdi, into);
MaybeCaptureMemoryAround(process_reader, context.x86_64->rsi, into);
if (context.x86_64->rbp < thread.stack_region_address ||
context.x86_64->rbp >=
thread.stack_region_address + thread.stack_region_size) {
MaybeCaptureMemoryAround(process_reader, context.x86_64->rbp, into);
}
MaybeCaptureMemoryAround(process_reader, context.x86_64->r8, into);
MaybeCaptureMemoryAround(process_reader, context.x86_64->r9, into);
MaybeCaptureMemoryAround(process_reader, context.x86_64->r10, into);
MaybeCaptureMemoryAround(process_reader, context.x86_64->r11, into);
MaybeCaptureMemoryAround(process_reader, context.x86_64->r12, into);
MaybeCaptureMemoryAround(process_reader, context.x86_64->r13, into);
MaybeCaptureMemoryAround(process_reader, context.x86_64->r14, into);
MaybeCaptureMemoryAround(process_reader, context.x86_64->r15, into);
MaybeCaptureMemoryAround(process_reader, context.x86_64->rip, into);
} else {
#endif
MaybeCaptureMemoryAround(process_reader, context.x86->eax, into);
MaybeCaptureMemoryAround(process_reader, context.x86->ebx, into);
MaybeCaptureMemoryAround(process_reader, context.x86->ecx, into);
MaybeCaptureMemoryAround(process_reader, context.x86->edx, into);
MaybeCaptureMemoryAround(process_reader, context.x86->edi, into);
MaybeCaptureMemoryAround(process_reader, context.x86->esi, into);
if (context.x86->ebp < thread.stack_region_address ||
context.x86->ebp >=
thread.stack_region_address + thread.stack_region_size) {
MaybeCaptureMemoryAround(process_reader, context.x86->ebp, into);
}
MaybeCaptureMemoryAround(process_reader, context.x86->eip, into);
#if defined(ARCH_CPU_X86_64)
}
#endif
}
} // namespace internal
} // namespace crashpad

View File

@ -1,43 +0,0 @@
// Copyright 2016 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_CAPTURE_CONTEXT_MEMORY_H_
#define CRASHPAD_SNAPSHOT_WIN_CAPTURE_CONTEXT_MEMORY_H_
#include "snapshot/cpu_context.h"
#include "snapshot/win/process_reader_win.h"
#include "util/stdlib/pointer_container.h"
namespace crashpad {
namespace internal {
class MemorySnapshotWin;
//! \brief For all registers that appear to be pointer-like in \a context,
//! captures a small amount of memory near their pointed to location.
//!
//! \param[in] context The context to inspect.
//! \param[in] process_reader A ProcessReaderWin to read from the target
//! process.
//! \param[in] thread The thread to which the context belongs.
//! \param[out] into A vector of pointers to append new ranges to.
void CaptureMemoryPointedToByContext(const CPUContext& context,
ProcessReaderWin* process_reader,
const ProcessReaderWin::Thread& thread,
PointerVector<MemorySnapshotWin>* into);
} // namespace internal
} // namespace crashpad
#endif // CRASHPAD_SNAPSHOT_WIN_CAPTURE_CONTEXT_MEMORY_H_

View File

@ -0,0 +1,56 @@
// Copyright 2016 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/capture_memory_delegate_win.h"
#include "snapshot/win/memory_snapshot_win.h"
namespace crashpad {
namespace internal {
CaptureMemoryDelegateWin::CaptureMemoryDelegateWin(
ProcessReaderWin* process_reader,
const ProcessReaderWin::Thread& thread,
PointerVector<MemorySnapshotWin>* snapshots)
: stack_(thread.stack_region_address, thread.stack_region_size),
process_reader_(process_reader),
snapshots_(snapshots) {}
bool CaptureMemoryDelegateWin::Is64Bit() const {
return process_reader_->Is64Bit();
}
bool CaptureMemoryDelegateWin::ReadMemory(uint64_t at,
uint64_t num_bytes,
void* into) const {
return process_reader_->ReadMemory(at, num_bytes, into);
}
std::vector<CheckedRange<uint64_t>> CaptureMemoryDelegateWin::GetReadableRanges(
const CheckedRange<uint64_t, uint64_t>& range) const {
return process_reader_->GetProcessInfo().GetReadableRanges(range);
}
void CaptureMemoryDelegateWin::AddNewMemorySnapshot(
const CheckedRange<uint64_t, uint64_t>& range) {
// Don't bother storing this memory if it points back into the stack.
if (stack_.ContainsRange(range))
return;
internal::MemorySnapshotWin* snapshot = new internal::MemorySnapshotWin();
snapshot->Initialize(process_reader_, range.base(), range.size());
snapshots_->push_back(snapshot);
}
} // namespace internal
} // namespace crashpad

View File

@ -0,0 +1,58 @@
// Copyright 2016 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_CAPTURE_MEMORY_DELEGATE_WIN_H_
#define CRASHPAD_SNAPSHOT_WIN_CAPTURE_MEMORY_DELEGATE_WIN_H_
#include "snapshot/capture_memory.h"
#include "snapshot/win/process_reader_win.h"
#include "util/stdlib/pointer_container.h"
namespace crashpad {
namespace internal {
class MemorySnapshotWin;
class CaptureMemoryDelegateWin : public CaptureMemory::Delegate {
public:
//! \brief A MemoryCaptureDelegate for Windows.
//!
//! \param[in] process_reader A ProcessReaderWin for the target process.
//! \param[in] thread The thread being inspected. Memory ranges overlapping
//! this thread's stack will be ignored on the assumption that they're
//! already captured elsewhere.
//! \param[in] snapshots A vector of MemorySnapshotWin to which the captured
//! memory will be added.
CaptureMemoryDelegateWin(ProcessReaderWin* process_reader,
const ProcessReaderWin::Thread& thread,
PointerVector<MemorySnapshotWin>* snapshots);
// MemoryCaptureDelegate:
bool Is64Bit() const override;
bool ReadMemory(uint64_t at, uint64_t num_bytes, void* into) const override;
std::vector<CheckedRange<uint64_t>> GetReadableRanges(
const CheckedRange<uint64_t, uint64_t>& range) const override;
void AddNewMemorySnapshot(const CheckedRange<uint64_t, uint64_t>& range);
private:
CheckedRange<uint64_t, uint64_t> stack_;
ProcessReaderWin* process_reader_;
PointerVector<MemorySnapshotWin>* snapshots_;
};
} // namespace internal
} // namespace crashpad
#endif // CRASHPAD_SNAPSHOT_WIN_CAPTURE_MEMORY_DELEGATE_WIN_H_

View File

@ -247,10 +247,22 @@ def RunTests(cdb_path,
r'FreeOwnStackAndBreak.*\nquit:',
'at correct location, no additional stack entries')
# Switch to the other thread after jumping to the exception, and examine
# memory.
out = CdbRun(cdb_path, dump_path, '.ecxr; ~1s; db /c14 edi')
out.Check(r'63 62 61 60 5f 5e 5d 5c-5b 5a 59 58 57 56 55 54 53 52 51 50',
'data pointed to by registers captured')
# Move up one stack frame after jumping to the exception, and examine memory.
out = CdbRun(cdb_path, dump_path,
'.ecxr; .f+; dd /c100 poi(offset_pointer)-20')
out.Check(r'80000078 00000079 8000007a 0000007b 8000007c 0000007d 8000007e '
r'0000007f 80000080 00000081 80000082 00000083 80000084 00000085 '
r'80000086 00000087 80000088 00000089 8000008a 0000008b 8000008c '
r'0000008d 8000008e 0000008f 80000090 00000091 80000092 00000093 '
r'80000094 00000095 80000096 00000097',
'data pointed to by stack captured')
if z7_dump_path:
out = CdbRun(cdb_path, z7_dump_path, '.ecxr;lm')
out.Check('This dump file has an exception of interest stored in it',

View File

@ -14,9 +14,10 @@
#include "snapshot/win/exception_snapshot_win.h"
#include "snapshot/capture_memory.h"
#include "snapshot/memory_snapshot.h"
#include "snapshot/win/capture_context_memory.h"
#include "snapshot/win/cpu_context_win.h"
#include "snapshot/win/capture_memory_delegate_win.h"
#include "snapshot/win/memory_snapshot_win.h"
#include "snapshot/win/process_reader_win.h"
#include "util/win/nt_internals.h"
@ -89,8 +90,9 @@ bool ExceptionSnapshotWin::Initialize(ProcessReaderWin* process_reader,
InitializeX86Context(context_record, context_.x86);
}
CaptureMemoryPointedToByContext(
context_, process_reader, *thread, &extra_memory_);
CaptureMemoryDelegateWin capture_memory_delegate(
process_reader, *thread, &extra_memory_);
CaptureMemory::PointedToByContext(context_, &capture_memory_delegate);
INITIALIZATION_STATE_SET_VALID(initialized_);
return true;

View File

@ -38,6 +38,7 @@ ProcessSnapshotWin::ProcessSnapshotWin()
client_id_(),
annotations_simple_map_(),
snapshot_time_(),
options_(),
initialized_() {
}
@ -65,9 +66,13 @@ bool ProcessSnapshotWin::Initialize(
debug_critical_section_address);
}
InitializeThreads();
InitializeModules();
GetCrashpadOptionsInternal(&options_);
InitializeThreads(options_.gather_indirectly_referenced_memory ==
TriState::kEnabled);
for (const MEMORY_BASIC_INFORMATION64& mbi :
process_reader_.GetProcessInfo().MemoryInfo()) {
memory_map_.push_back(new internal::MemoryMapRegionSnapshotWin(mbi));
@ -104,35 +109,7 @@ bool ProcessSnapshotWin::InitializeException(
void ProcessSnapshotWin::GetCrashpadOptions(
CrashpadInfoClientOptions* options) {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
CrashpadInfoClientOptions local_options;
for (internal::ModuleSnapshotWin* module : modules_) {
CrashpadInfoClientOptions module_options;
module->GetCrashpadOptions(&module_options);
if (local_options.crashpad_handler_behavior == TriState::kUnset) {
local_options.crashpad_handler_behavior =
module_options.crashpad_handler_behavior;
}
if (local_options.system_crash_reporter_forwarding == TriState::kUnset) {
local_options.system_crash_reporter_forwarding =
module_options.system_crash_reporter_forwarding;
}
if (local_options.gather_indirectly_referenced_memory == TriState::kUnset) {
local_options.gather_indirectly_referenced_memory =
module_options.gather_indirectly_referenced_memory;
}
// If non-default values have been found for all options, the loop can end
// early.
if (local_options.crashpad_handler_behavior != TriState::kUnset &&
local_options.system_crash_reporter_forwarding != TriState::kUnset) {
break;
}
}
*options = local_options;
*options = options_;
}
pid_t ProcessSnapshotWin::ProcessID() const {
@ -238,13 +215,16 @@ std::vector<const MemorySnapshot*> ProcessSnapshotWin::ExtraMemory() const {
return extra_memory;
}
void ProcessSnapshotWin::InitializeThreads() {
void ProcessSnapshotWin::InitializeThreads(
bool gather_indirectly_referenced_memory) {
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)) {
if (thread->Initialize(&process_reader_,
process_reader_thread,
gather_indirectly_referenced_memory)) {
threads_.push_back(thread.release());
}
}
@ -262,6 +242,38 @@ void ProcessSnapshotWin::InitializeModules() {
}
}
void ProcessSnapshotWin::GetCrashpadOptionsInternal(
CrashpadInfoClientOptions* options) {
CrashpadInfoClientOptions local_options;
for (internal::ModuleSnapshotWin* module : modules_) {
CrashpadInfoClientOptions module_options;
module->GetCrashpadOptions(&module_options);
if (local_options.crashpad_handler_behavior == TriState::kUnset) {
local_options.crashpad_handler_behavior =
module_options.crashpad_handler_behavior;
}
if (local_options.system_crash_reporter_forwarding == TriState::kUnset) {
local_options.system_crash_reporter_forwarding =
module_options.system_crash_reporter_forwarding;
}
if (local_options.gather_indirectly_referenced_memory == TriState::kUnset) {
local_options.gather_indirectly_referenced_memory =
module_options.gather_indirectly_referenced_memory;
}
// If non-default values have been found for all options, the loop can end
// early.
if (local_options.crashpad_handler_behavior != TriState::kUnset &&
local_options.system_crash_reporter_forwarding != TriState::kUnset) {
break;
}
}
*options = local_options;
}
template <class Traits>
void ProcessSnapshotWin::InitializePebData(
WinVMAddress debug_critical_section_address) {

View File

@ -141,11 +141,14 @@ class ProcessSnapshotWin final : public ProcessSnapshot {
private:
// Initializes threads_ on behalf of Initialize().
void InitializeThreads();
void InitializeThreads(bool gather_indirectly_referenced_memory);
// Initializes modules_ on behalf of Initialize().
void InitializeModules();
// Initializes options_ on behalf of Initialize().
void GetCrashpadOptionsInternal(CrashpadInfoClientOptions* options);
// Initializes various memory blocks reachable from the PEB on behalf of
// Initialize().
template <class Traits>
@ -186,6 +189,7 @@ class ProcessSnapshotWin final : public ProcessSnapshot {
UUID client_id_;
std::map<std::string, std::string> annotations_simple_map_;
timeval snapshot_time_;
CrashpadInfoClientOptions options_;
InitializationStateDcheck initialized_;
DISALLOW_COPY_AND_ASSIGN(ProcessSnapshotWin);

View File

@ -17,8 +17,9 @@
#include <vector>
#include "base/logging.h"
#include "snapshot/win/capture_context_memory.h"
#include "snapshot/capture_memory.h"
#include "snapshot/win/cpu_context_win.h"
#include "snapshot/win/capture_memory_delegate_win.h"
#include "snapshot/win/process_reader_win.h"
namespace crashpad {
@ -38,7 +39,8 @@ ThreadSnapshotWin::~ThreadSnapshotWin() {
bool ThreadSnapshotWin::Initialize(
ProcessReaderWin* process_reader,
const ProcessReaderWin::Thread& process_reader_thread) {
const ProcessReaderWin::Thread& process_reader_thread,
bool gather_indirectly_referenced_memory) {
INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
thread_ = process_reader_thread;
@ -76,8 +78,11 @@ bool ThreadSnapshotWin::Initialize(
InitializeX86Context(process_reader_thread.context.native, context_.x86);
#endif // ARCH_CPU_X86_64
CaptureMemoryPointedToByContext(
context_, process_reader, thread_, &pointed_to_memory_);
CaptureMemoryDelegateWin capture_memory_delegate(
process_reader, thread_, &pointed_to_memory_);
CaptureMemory::PointedToByContext(context_, &capture_memory_delegate);
if (gather_indirectly_referenced_memory)
CaptureMemory::PointedToByMemoryRange(stack_, &capture_memory_delegate);
INITIALIZATION_STATE_SET_VALID(initialized_);
return true;

View File

@ -48,11 +48,14 @@ class ThreadSnapshotWin final : public ThreadSnapshot {
//! the thread.
//! \param[in] process_reader_thread The thread within the ProcessReaderWin
//! for which the snapshot should be created.
//! \param[in] gather_indirectly_referenced_memory If `true`, adds extra
//! memory regions to the snapshot pointed to by the thread's stack.
//!
//! \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);
const ProcessReaderWin::Thread& process_reader_thread,
bool gather_indirectly_referenced_memory);
// ThreadSnapshot: