Merge master 2adcd13fd66b into doc

This commit is contained in:
Mark Mentovai 2015-10-19 13:53:42 -04:00
commit eef9aa3298
58 changed files with 1891 additions and 75 deletions

2
DEPS
View File

@ -28,7 +28,7 @@ deps = {
'01528c7244837168a1c80f06ff60fa5a9793c824', '01528c7244837168a1c80f06ff60fa5a9793c824',
'crashpad/third_party/mini_chromium/mini_chromium': 'crashpad/third_party/mini_chromium/mini_chromium':
Var('chromium_git') + '/chromium/mini_chromium@' + Var('chromium_git') + '/chromium/mini_chromium@' +
'133a8c61c2567da9e72d4551a7cd5d5d2836e25c', 'c9625ad5d23c25fbb477e7dbb4c1c8b9a9360f41',
} }
hooks = [ hooks = [

View File

@ -64,7 +64,7 @@ def main(args):
print name print name
print '-' * 80 print '-' * 80
subprocess.check_call( subprocess.check_call(
[sys.executable, os.path.join(crashpad_dir, name), args[0]]) [sys.executable, os.path.join(crashpad_dir, name), binary_dir])
return 0 return 0

View File

@ -23,6 +23,7 @@
#include "base/strings/utf_string_conversions.h" #include "base/strings/utf_string_conversions.h"
#include "base/synchronization/lock.h" #include "base/synchronization/lock.h"
#include "util/file/file_io.h" #include "util/file/file_io.h"
#include "util/win/critical_section_with_debug_info.h"
#include "util/win/registration_protocol_win.h" #include "util/win/registration_protocol_win.h"
#include "util/win/scoped_handle.h" #include "util/win/scoped_handle.h"
@ -48,6 +49,13 @@ base::Lock* g_non_crash_dump_lock;
// dump. // dump.
crashpad::ExceptionInformation g_non_crash_exception_information; crashpad::ExceptionInformation g_non_crash_exception_information;
// A CRITICAL_SECTION initialized with
// RTL_CRITICAL_SECTION_FLAG_FORCE_DEBUG_INFO to force it to be allocated with a
// valid .DebugInfo field. The address of this critical section is given to the
// handler. All critical sections with debug info are linked in a doubly-linked
// list, so this allows the handler to capture all of them.
CRITICAL_SECTION g_critical_section_with_debug_info;
LONG WINAPI UnhandledExceptionHandler(EXCEPTION_POINTERS* exception_pointers) { LONG WINAPI UnhandledExceptionHandler(EXCEPTION_POINTERS* exception_pointers) {
// Tracks whether a thread has already entered UnhandledExceptionHandler. // Tracks whether a thread has already entered UnhandledExceptionHandler.
static base::subtle::AtomicWord have_crashed; static base::subtle::AtomicWord have_crashed;
@ -118,6 +126,7 @@ bool CrashpadClient::SetHandler(const std::string& ipc_port) {
DCHECK_EQ(g_signal_exception, INVALID_HANDLE_VALUE); DCHECK_EQ(g_signal_exception, INVALID_HANDLE_VALUE);
DCHECK_EQ(g_signal_non_crash_dump, INVALID_HANDLE_VALUE); DCHECK_EQ(g_signal_non_crash_dump, INVALID_HANDLE_VALUE);
DCHECK_EQ(g_non_crash_dump_done, INVALID_HANDLE_VALUE); DCHECK_EQ(g_non_crash_dump_done, INVALID_HANDLE_VALUE);
DCHECK(!g_critical_section_with_debug_info.DebugInfo);
ClientToServerMessage message; ClientToServerMessage message;
memset(&message, 0, sizeof(message)); memset(&message, 0, sizeof(message));
@ -129,6 +138,19 @@ bool CrashpadClient::SetHandler(const std::string& ipc_port) {
message.registration.non_crash_exception_information = message.registration.non_crash_exception_information =
reinterpret_cast<WinVMAddress>(&g_non_crash_exception_information); reinterpret_cast<WinVMAddress>(&g_non_crash_exception_information);
// We create this dummy CRITICAL_SECTION with the
// RTL_CRITICAL_SECTION_FLAG_FORCE_DEBUG_INFO flag set to have an entry point
// into the doubly-linked list of RTL_CRITICAL_SECTION_DEBUG objects. This
// allows us to walk the list at crash time to gather data for !locks. A
// debugger would instead inspect ntdll!RtlCriticalSectionList to get the head
// of the list. But that is not an exported symbol, so on an arbitrary client
// machine, we don't have a way of getting that pointer.
if (InitializeCriticalSectionWithDebugInfoIfPossible(
&g_critical_section_with_debug_info)) {
message.registration.critical_section_address =
reinterpret_cast<WinVMAddress>(&g_critical_section_with_debug_info);
}
ServerToClientMessage response = {0}; ServerToClientMessage response = {0};
if (!SendToCrashHandlerServer( if (!SendToCrashHandlerServer(

View File

@ -207,9 +207,10 @@ TEST(PruneCrashReports, PruneOrder) {
reports.push_back(temp); reports.push_back(temp);
} }
// The randomness from std::rand() is not, so use a better rand() instead. // The randomness from std::rand() is not, so use a better rand() instead.
std::random_shuffle(reports.begin(), reports.end(), [](int rand_max) { const auto random_generator = [](int rand_max) {
return base::RandUint64() % rand_max; return base::RandInt(0, rand_max - 1);
}); };
std::random_shuffle(reports.begin(), reports.end(), random_generator);
std::vector<CrashReportDatabase::Report> pending_reports( std::vector<CrashReportDatabase::Report> pending_reports(
reports.begin(), reports.begin() + 5); reports.begin(), reports.begin() + 5);
std::vector<CrashReportDatabase::Report> completed_reports( std::vector<CrashReportDatabase::Report> completed_reports(

View File

@ -167,6 +167,9 @@ enum MINIDUMP_STREAM_TYPE {
//! MINIDUMP_MISC_INFO::Flags1, that indicates which data is present and //! MINIDUMP_MISC_INFO::Flags1, that indicates which data is present and
//! valid. //! valid.
MiscInfoStream = 15, MiscInfoStream = 15,
//! \brief The stream type for MINIDUMP_MEMORY_INFO_LIST.
MemoryInfoListStream = 16,
}; };
//! \brief Information about the CPU (or CPUs) that ran the process that the //! \brief Information about the CPU (or CPUs) that ran the process that the
@ -618,6 +621,71 @@ struct __attribute__((packed, aligned(4))) MINIDUMP_MEMORY_LIST {
MINIDUMP_MEMORY_DESCRIPTOR MemoryRanges[0]; MINIDUMP_MEMORY_DESCRIPTOR MemoryRanges[0];
}; };
//! \brief Contains the state of an individual system handle at the time the
//! snapshot was taken. This structure is Windows-specific.
//!
//! \sa MINIDUMP_HANDLE_DESCRIPTOR_2
struct __attribute__((packed, aligned(4))) MINIDUMP_HANDLE_DESCRIPTOR {
//! \brief The Windows `HANDLE` value.
uint64_t Handle;
//! \brief An RVA to a MINIDUMP_STRING structure that specifies the object
//! type of the handle. This member can be zero.
RVA TypeNameRva;
//! \brief An RVA to a MINIDUMP_STRING structure that specifies the object
//! name of the handle. This member can be zero.
RVA ObjectNameRva;
//! \brief The attributes for the handle, this corresponds to `OBJ_INHERIT`,
//! `OBJ_CASE_INSENSITIVE`, etc.
uint32_t Attributes;
//! \brief The `ACCESS_MASK` for the handle.
uint32_t GrantedAccess;
//! \brief This is the number of open handles to the object that this handle
//! refers to.
uint32_t HandleCount;
//! \brief This is the number kernel references to the object that this
//! handle refers to.
uint32_t PointerCount;
};
//! \brief Contains the state of an individual system handle at the time the
//! snapshot was taken. This structure is Windows-specific.
//!
//! \sa MINIDUMP_HANDLE_DESCRIPTOR
struct __attribute__((packed, aligned(4))) MINIDUMP_HANDLE_DESCRIPTOR_2
: public MINIDUMP_HANDLE_DESCRIPTOR {
//! \brief An RVA to a MINIDUMP_HANDLE_OBJECT_INFORMATION structure that
//! specifies object-specific information. This member can be zero if
//! there is no extra information.
RVA ObjectInfoRva;
//! \brief Must be zero.
uint32_t Reserved0;
};
//! \brief Represents the header for a handle data stream.
struct __attribute((packed, aligned(4))) MINIDUMP_HANDLE_DATA_STREAM {
//! \brief The size of the header information for the stream, in bytes. This
//! value is `sizeof(MINIDUMP_HANDLE_DATA_STREAM)`.
uint32_t SizeOfHeader;
//! \brief The size of a descriptor in the stream, in bytes. This value is
//! `sizeof(MINIDUMP_HANDLE_DESCRIPTOR)` or
//! `sizeof(MINIDUMP_HANDLE_DESCRIPTOR_2)`.
uint32_t SizeOfDescriptor;
//! \brief The number of descriptors in the stream.
uint32_t NumberOfDescriptors;
//! \brief Must be zero.
uint32_t Reserved;
};
//! \anchor MINIDUMP_MISCx //! \anchor MINIDUMP_MISCx
//! \name MINIDUMP_MISC* //! \name MINIDUMP_MISC*
//! //!
@ -836,6 +904,57 @@ struct __attribute__((packed, aligned(4))) MINIDUMP_MISC_INFO_4
//! \brief The latest known version of the MINIDUMP_MISC_INFO structure. //! \brief The latest known version of the MINIDUMP_MISC_INFO structure.
typedef MINIDUMP_MISC_INFO_4 MINIDUMP_MISC_INFO_N; typedef MINIDUMP_MISC_INFO_4 MINIDUMP_MISC_INFO_N;
//! \brief Describes a region of memory.
struct __attribute__((packed, aligned(4))) MINIDUMP_MEMORY_INFO {
//! \brief The base address of the region of pages.
uint64_t BaseAddress;
//! \brief The base address of a range of pages in this region. The page is
//! contained within this memory region.
uint64_t AllocationBase;
//! \brief The memory protection when the region was initially allocated. This
//! member can be one of the memory protection options (such as
//! \ref PAGE_x PAGE_EXECUTE, \ref PAGE_x PAGE_NOACCESS, etc.), along with
//! \ref PAGE_x PAGE_GUARD or \ref PAGE_x PAGE_NOCACHE, as needed.
uint32_t AllocationProtect;
uint32_t __alignment1;
//! \brief The size of the region beginning at the base address in which all
//! pages have identical attributes, in bytes.
uint64_t RegionSize;
//! \brief The state of the pages in the region. This can be one of
//! \ref MEM_x MEM_COMMIT, \ref MEM_x MEM_FREE, or \ref MEM_x MEM_RESERVE.
uint32_t State;
//! \brief The access protection of the pages in the region. This member is
//! one of the values listed for the #AllocationProtect member.
uint32_t Protect;
//! \brief The type of pages in the region. This can be one of \ref MEM_x
//! MEM_IMAGE, \ref MEM_x MEM_MAPPED, or \ref MEM_x MEM_PRIVATE.
uint32_t Type;
uint32_t __alignment2;
};
//! \brief Contains a list of memory regions.
struct __attribute__((packed, aligned(4))) MINIDUMP_MEMORY_INFO_LIST {
//! \brief The size of the header data for the stream, in bytes. This is
//! generally sizeof(MINIDUMP_MEMORY_INFO_LIST).
uint32_t SizeOfHeader;
//! \brief The size of each entry following the header, in bytes. This is
//! generally sizeof(MINIDUMP_MEMORY_INFO).
uint32_t SizeOfEntry;
//! \brief The number of entries in the stream. These are generally
//! MINIDUMP_MEMORY_INFO structures. The entries follow the header.
uint64_t NumberOfEntries;
};
//! \brief Minidump file type values for MINIDUMP_HEADER::Flags. These bits //! \brief Minidump file type values for MINIDUMP_HEADER::Flags. These bits
//! describe the types of data carried within a minidump file. //! describe the types of data carried within a minidump file.
enum MINIDUMP_TYPE { enum MINIDUMP_TYPE {

View File

@ -179,4 +179,39 @@ struct IMAGE_DEBUG_MISC {
#define VER_PLATFORM_WIN32_NT 2 #define VER_PLATFORM_WIN32_NT 2
//! \} //! \}
//! \anchor PAGE_x
//! \name PAGE_*
//!
//! \brief Memory protection constants for MINIDUMP_MEMORY_INFO::Protect and
//! MINIDUMP_MEMORY_INFO::AllocationProtect.
//! \{
#define PAGE_NOACCESS 0x1
#define PAGE_READONLY 0x2
#define PAGE_READWRITE 0x4
#define PAGE_WRITECOPY 0x8
#define PAGE_EXECUTE 0x10
#define PAGE_EXECUTE_READ 0x20
#define PAGE_EXECUTE_READWRITE 0x40
#define PAGE_EXECUTE_WRITECOPY 0x80
#define PAGE_GUARD 0x100
#define PAGE_NOCACHE 0x200
#define PAGE_WRITECOMBINE 0x400
//! \}
//! \anchor MEM_x
//! \name MEM_*
//!
//! \brief Memory state and type constants for MINIDUMP_MEMORY_INFO::State and
//! MINIDUMP_MEMORY_INFO::Type.
//! \{
#define MEM_COMMIT 0x1000
#define MEM_RESERVE 0x2000
#define MEM_DECOMMIT 0x4000
#define MEM_RELEASE 0x8000
#define MEM_FREE 0x10000
#define MEM_PRIVATE 0x20000
#define MEM_MAPPED 0x40000
#define MEM_RESET 0x80000
//! \}
#endif // CRASHPAD_COMPAT_NON_WIN_WINNT_H_ #endif // CRASHPAD_COMPAT_NON_WIN_WINNT_H_

View File

@ -53,23 +53,25 @@ https://chromium.googlesource.com/crashpad/crashpad. Although it is possible to
check out this repository directly with `git clone`, Crashpads dependencies are check out this repository directly with `git clone`, Crashpads dependencies are
managed by managed by
https://dev.chromium.org/developers/how-tos/depottools#TOC-gclient[`gclient`] https://dev.chromium.org/developers/how-tos/depottools#TOC-gclient[`gclient`]
instead of Git submodules, so to work on Crashpad, it is best to use `gclient` instead of Git submodules, so to work on Crashpad, it is best to use `fetch` to
to get the source code. get the source code.
`gclient` is part of the `fetch` and `gclient` are part of the
https://dev.chromium.org/developers/how-tos/depottools[depot_tools]. Theres no https://dev.chromium.org/developers/how-tos/depottools[depot_tools]. Theres no
need to install it separately. need to install them separately.
=== Initial Checkout === Initial Checkout
[subs="verbatim,quotes"] [subs="verbatim,quotes"]
---- ----
$ *mkdir \~/crashpad* $ *mkdir ~/crashpad*
$ *cd ~/crashpad* $ *cd ~/crashpad*
$ *gclient config --unmanaged https://chromium.googlesource.com/crashpad/crashpad* $ *fetch crashpad*
$ *gclient sync*
---- ----
`fetch crashpad` performs the initial `gclient sync`, establishing a
fully-functional local checkout.
=== Subsequent Checkouts === Subsequent Checkouts
[subs="verbatim,quotes"] [subs="verbatim,quotes"]
@ -85,7 +87,7 @@ Crashpad uses https://gyp.googlecode.com/[GYP] to generate
https://martine.github.io/ninja/[Ninja] build files. The build is described by https://martine.github.io/ninja/[Ninja] build files. The build is described by
`.gyp` files throughout the Crashpad source code tree. The `.gyp` files throughout the Crashpad source code tree. The
`build/gyp_crashpad.py` script runs GYP properly for Crashpad, and is also `build/gyp_crashpad.py` script runs GYP properly for Crashpad, and is also
called when you run `gclient sync` or `gclient runhooks`. called when you run `fetch crashpad`, `gclient sync`, or `gclient runhooks`.
The Ninja build files and build output are in the `out` directory. Both debug- The Ninja build files and build output are in the `out` directory. Both debug-
and release-mode configurations are available. The examples below show the debug and release-mode configurations are available. The examples below show the debug

View File

@ -82,6 +82,7 @@
'../client/client.gyp:crashpad_client', '../client/client.gyp:crashpad_client',
'../third_party/mini_chromium/mini_chromium.gyp:base', '../third_party/mini_chromium/mini_chromium.gyp:base',
'../tools/tools.gyp:crashpad_tool_support', '../tools/tools.gyp:crashpad_tool_support',
'../util/util.gyp:crashpad_util',
], ],
'include_dirs': [ 'include_dirs': [
'..', '..',

View File

@ -42,14 +42,16 @@ void CrashReportExceptionHandler::ExceptionHandlerServerStarted() {
unsigned int CrashReportExceptionHandler::ExceptionHandlerServerException( unsigned int CrashReportExceptionHandler::ExceptionHandlerServerException(
HANDLE process, HANDLE process,
WinVMAddress exception_information_address) { WinVMAddress exception_information_address,
WinVMAddress debug_critical_section_address) {
const unsigned int kFailedTerminationCode = 0xffff7002; const unsigned int kFailedTerminationCode = 0xffff7002;
ScopedProcessSuspend suspend(process); ScopedProcessSuspend suspend(process);
ProcessSnapshotWin process_snapshot; ProcessSnapshotWin process_snapshot;
if (!process_snapshot.Initialize(process, if (!process_snapshot.Initialize(process,
ProcessSuspensionState::kSuspended)) { ProcessSuspensionState::kSuspended,
debug_critical_section_address)) {
LOG(WARNING) << "ProcessSnapshotWin::Initialize failed"; LOG(WARNING) << "ProcessSnapshotWin::Initialize failed";
return kFailedTerminationCode; return kFailedTerminationCode;
} }

View File

@ -60,7 +60,8 @@ class CrashReportExceptionHandler : public ExceptionHandlerServer::Delegate {
void ExceptionHandlerServerStarted() override; void ExceptionHandlerServerStarted() override;
unsigned int ExceptionHandlerServerException( unsigned int ExceptionHandlerServerException(
HANDLE process, HANDLE process,
WinVMAddress exception_information_address) override; WinVMAddress exception_information_address,
WinVMAddress debug_critical_section_address) override;
private: private:
CrashReportDatabase* database_; // weak CrashReportDatabase* database_; // weak

View File

@ -20,13 +20,17 @@
#define STATUS_NO_SUCH_FILE static_cast<NTSTATUS>(0xC000000F) #define STATUS_NO_SUCH_FILE static_cast<NTSTATUS>(0xC000000F)
#endif #endif
#include "base/basictypes.h"
#include "base/logging.h" #include "base/logging.h"
#include "client/crashpad_client.h" #include "client/crashpad_client.h"
#include "tools/tool_support.h" #include "tools/tool_support.h"
#include "util/win/critical_section_with_debug_info.h"
namespace crashpad { namespace crashpad {
namespace { namespace {
CRITICAL_SECTION g_test_critical_section;
ULONG RtlNtStatusToDosError(NTSTATUS status) { ULONG RtlNtStatusToDosError(NTSTATUS status) {
static decltype(::RtlNtStatusToDosError)* rtl_nt_status_to_dos_error = static decltype(::RtlNtStatusToDosError)* rtl_nt_status_to_dos_error =
reinterpret_cast<decltype(::RtlNtStatusToDosError)*>( reinterpret_cast<decltype(::RtlNtStatusToDosError)*>(
@ -35,6 +39,45 @@ ULONG RtlNtStatusToDosError(NTSTATUS status) {
return rtl_nt_status_to_dos_error(status); return rtl_nt_status_to_dos_error(status);
} }
void AllocateMemoryOfVariousProtections() {
SYSTEM_INFO system_info;
GetSystemInfo(&system_info);
const size_t kPageSize = system_info.dwPageSize;
const uint32_t kPageTypes[] = {
PAGE_NOACCESS,
PAGE_READONLY,
PAGE_READWRITE,
PAGE_EXECUTE,
PAGE_EXECUTE_READ,
PAGE_EXECUTE_READWRITE,
// PAGE_NOACCESS is invalid with PAGE_GUARD.
PAGE_READONLY | PAGE_GUARD,
PAGE_READWRITE | PAGE_GUARD,
PAGE_EXECUTE | PAGE_GUARD,
PAGE_EXECUTE_READ | PAGE_GUARD,
PAGE_EXECUTE_READWRITE | PAGE_GUARD,
};
// All of these allocations are leaked, we want to view them in windbg via
// !vprot.
void* reserve = VirtualAlloc(
nullptr, arraysize(kPageTypes) * kPageSize, MEM_RESERVE, PAGE_READWRITE);
PCHECK(reserve) << "VirtualAlloc MEM_RESERVE";
uintptr_t reserve_as_int = reinterpret_cast<uintptr_t>(reserve);
for (size_t i = 0; i < arraysize(kPageTypes); ++i) {
void* result =
VirtualAlloc(reinterpret_cast<void*>(reserve_as_int + (kPageSize * i)),
kPageSize,
MEM_COMMIT,
kPageTypes[i]);
PCHECK(result) << "VirtualAlloc MEM_COMMIT " << i;
}
}
void SomeCrashyFunction() { void SomeCrashyFunction() {
// SetLastError and NTSTATUS so that we have something to view in !gle in // SetLastError and NTSTATUS so that we have something to view in !gle in
// windbg. RtlNtStatusToDosError() stores STATUS_NO_SUCH_FILE into the // windbg. RtlNtStatusToDosError() stores STATUS_NO_SUCH_FILE into the
@ -61,6 +104,13 @@ int CrashyMain(int argc, char* argv[]) {
return 1; return 1;
} }
AllocateMemoryOfVariousProtections();
if (InitializeCriticalSectionWithDebugInfoIfPossible(
&g_test_critical_section)) {
EnterCriticalSection(&g_test_critical_section);
}
SomeCrashyFunction(); SomeCrashyFunction();
return 0; return 0;

View File

@ -44,6 +44,8 @@
'minidump_extensions.h', 'minidump_extensions.h',
'minidump_file_writer.cc', 'minidump_file_writer.cc',
'minidump_file_writer.h', 'minidump_file_writer.h',
'minidump_memory_info_writer.cc',
'minidump_memory_info_writer.h',
'minidump_memory_writer.cc', 'minidump_memory_writer.cc',
'minidump_memory_writer.h', 'minidump_memory_writer.h',
'minidump_misc_info_writer.cc', 'minidump_misc_info_writer.cc',

View File

@ -77,6 +77,11 @@ enum MinidumpStreamType : uint32_t {
//! \sa MiscInfoStream //! \sa MiscInfoStream
kMinidumpStreamTypeMiscInfo = MiscInfoStream, kMinidumpStreamTypeMiscInfo = MiscInfoStream,
//! \brief The stream type for MINIDUMP_MEMORY_INFO_LIST.
//!
//! \sa MemoryInfoListStream
kMinidumpStreamTypeMemoryInfoList = MemoryInfoListStream,
// 0x4350 = "CP" // 0x4350 = "CP"
//! \brief The stream type for MinidumpCrashpadInfo. //! \brief The stream type for MinidumpCrashpadInfo.

View File

@ -17,6 +17,7 @@
#include "base/logging.h" #include "base/logging.h"
#include "minidump/minidump_crashpad_info_writer.h" #include "minidump/minidump_crashpad_info_writer.h"
#include "minidump/minidump_exception_writer.h" #include "minidump/minidump_exception_writer.h"
#include "minidump/minidump_memory_info_writer.h"
#include "minidump/minidump_memory_writer.h" #include "minidump/minidump_memory_writer.h"
#include "minidump/minidump_misc_info_writer.h" #include "minidump/minidump_misc_info_writer.h"
#include "minidump/minidump_module_writer.h" #include "minidump/minidump_module_writer.h"
@ -99,6 +100,14 @@ void MinidumpFileWriter::InitializeFromSnapshot(
AddStream(crashpad_info.Pass()); AddStream(crashpad_info.Pass());
} }
std::vector<const MemoryMapRegionSnapshot*> memory_map_snapshot =
process_snapshot->MemoryMap();
if (!memory_map_snapshot.empty()) {
auto memory_info_list = make_scoped_ptr(new MinidumpMemoryInfoListWriter());
memory_info_list->InitializeFromSnapshot(memory_map_snapshot);
AddStream(memory_info_list.Pass());
}
memory_list->AddFromSnapshot(process_snapshot->ExtraMemory()); memory_list->AddFromSnapshot(process_snapshot->ExtraMemory());
AddStream(memory_list.Pass()); AddStream(memory_list.Pass());

View File

@ -0,0 +1,85 @@
// 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 "minidump/minidump_memory_info_writer.h"
#include "base/logging.h"
#include "snapshot/memory_map_region_snapshot.h"
#include "util/file/file_writer.h"
namespace crashpad {
MinidumpMemoryInfoListWriter::MinidumpMemoryInfoListWriter()
: memory_info_list_base_(), items_() {
}
MinidumpMemoryInfoListWriter::~MinidumpMemoryInfoListWriter() {
}
void MinidumpMemoryInfoListWriter::InitializeFromSnapshot(
const std::vector<const MemoryMapRegionSnapshot*>& memory_map) {
DCHECK_EQ(state(), kStateMutable);
DCHECK(items_.empty());
for (const auto& region : memory_map)
items_.push_back(region->AsMinidumpMemoryInfo());
}
bool MinidumpMemoryInfoListWriter::Freeze() {
DCHECK_EQ(state(), kStateMutable);
if (!MinidumpStreamWriter::Freeze())
return false;
memory_info_list_base_.SizeOfHeader = sizeof(MINIDUMP_MEMORY_INFO_LIST);
memory_info_list_base_.SizeOfEntry = sizeof(MINIDUMP_MEMORY_INFO);
memory_info_list_base_.NumberOfEntries = items_.size();
return true;
}
size_t MinidumpMemoryInfoListWriter::SizeOfObject() {
DCHECK_GE(state(), kStateFrozen);
return sizeof(memory_info_list_base_) + sizeof(items_[0]) * items_.size();
}
std::vector<internal::MinidumpWritable*>
MinidumpMemoryInfoListWriter::Children() {
DCHECK_GE(state(), kStateFrozen);
return std::vector<internal::MinidumpWritable*>();
}
bool MinidumpMemoryInfoListWriter::WriteObject(
FileWriterInterface* file_writer) {
DCHECK_EQ(state(), kStateWritable);
WritableIoVec iov;
iov.iov_base = &memory_info_list_base_;
iov.iov_len = sizeof(memory_info_list_base_);
std::vector<WritableIoVec> iovecs(1, iov);
for (const auto& minidump_memory_info : items_) {
iov.iov_base = &minidump_memory_info;
iov.iov_len = sizeof(minidump_memory_info);
iovecs.push_back(iov);
}
return file_writer->WriteIoVec(&iovecs);
}
MinidumpStreamType MinidumpMemoryInfoListWriter::StreamType() const {
return kMinidumpStreamTypeMemoryInfoList;
}
} // namespace crashpad

View File

@ -0,0 +1,71 @@
// 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_MINIDUMP_MINIDUMP_MEMORY_INFO_WRITER_H_
#define CRASHPAD_MINIDUMP_MINIDUMP_MEMORY_INFO_WRITER_H_
#include <windows.h>
#include <dbghelp.h>
#include <stdint.h>
#include <vector>
#include "base/basictypes.h"
#include "minidump/minidump_stream_writer.h"
#include "minidump/minidump_writable.h"
namespace crashpad {
class MemoryMapRegionSnapshot;
class MinidumpContextWriter;
class MinidumpMemoryListWriter;
class MinidumpMemoryWriter;
//! \brief The writer for a MINIDUMP_MEMORY_INFO_LIST stream in a minidump file,
//! containing a list of MINIDUMP_MEMORY_INFO objects.
class MinidumpMemoryInfoListWriter final
: public internal::MinidumpStreamWriter {
public:
MinidumpMemoryInfoListWriter();
~MinidumpMemoryInfoListWriter() override;
//! \brief Initializes a MINIDUMP_MEMORY_INFO_LIST based on \a memory_map.
//!
//! \param[in] memory_map The vector of memory map region snapshots to use as
//! source data.
//!
//! \note Valid in #kStateMutable.
void InitializeFromSnapshot(
const std::vector<const MemoryMapRegionSnapshot*>& memory_map);
protected:
// MinidumpWritable:
bool Freeze() override;
size_t SizeOfObject() override;
std::vector<internal::MinidumpWritable*> Children() override;
bool WriteObject(FileWriterInterface* file_writer) override;
// MinidumpStreamWriter:
MinidumpStreamType StreamType() const override;
private:
MINIDUMP_MEMORY_INFO_LIST memory_info_list_base_;
std::vector<MINIDUMP_MEMORY_INFO> items_;
DISALLOW_COPY_AND_ASSIGN(MinidumpMemoryInfoListWriter);
};
} // namespace crashpad
#endif // CRASHPAD_MINIDUMP_MINIDUMP_MEMORY_INFO_WRITER_H_

View File

@ -0,0 +1,126 @@
// 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 "minidump/minidump_memory_info_writer.h"
#include <string>
#include "gtest/gtest.h"
#include "minidump/minidump_file_writer.h"
#include "minidump/test/minidump_file_writer_test_util.h"
#include "minidump/test/minidump_writable_test_util.h"
#include "snapshot/test/test_memory_map_region_snapshot.h"
#include "util/file/string_file.h"
namespace crashpad {
namespace test {
namespace {
// The memory info list is expected to be the only stream.
void GetMemoryInfoListStream(
const std::string& file_contents,
const MINIDUMP_MEMORY_INFO_LIST** memory_info_list) {
const size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER);
const size_t kMemoryInfoListStreamOffset =
kDirectoryOffset + sizeof(MINIDUMP_DIRECTORY);
const MINIDUMP_DIRECTORY* directory;
const MINIDUMP_HEADER* header =
MinidumpHeaderAtStart(file_contents, &directory);
ASSERT_NO_FATAL_FAILURE(VerifyMinidumpHeader(header, 1, 0));
ASSERT_TRUE(directory);
const size_t kDirectoryIndex = 0;
ASSERT_EQ(kMinidumpStreamTypeMemoryInfoList,
directory[kDirectoryIndex].StreamType);
EXPECT_EQ(kMemoryInfoListStreamOffset,
directory[kDirectoryIndex].Location.Rva);
*memory_info_list =
MinidumpWritableAtLocationDescriptor<MINIDUMP_MEMORY_INFO_LIST>(
file_contents, directory[kDirectoryIndex].Location);
ASSERT_TRUE(memory_info_list);
}
TEST(MinidumpMemoryInfoWriter, Empty) {
MinidumpFileWriter minidump_file_writer;
auto memory_info_list_writer =
make_scoped_ptr(new MinidumpMemoryInfoListWriter());
minidump_file_writer.AddStream(memory_info_list_writer.Pass());
StringFile string_file;
ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));
ASSERT_EQ(sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) +
sizeof(MINIDUMP_MEMORY_INFO_LIST),
string_file.string().size());
const MINIDUMP_MEMORY_INFO_LIST* memory_info_list = nullptr;
ASSERT_NO_FATAL_FAILURE(
GetMemoryInfoListStream(string_file.string(), &memory_info_list));
EXPECT_EQ(0u, memory_info_list->NumberOfEntries);
}
TEST(MinidumpMemoryInfoWriter, OneRegion) {
MinidumpFileWriter minidump_file_writer;
auto memory_info_list_writer =
make_scoped_ptr(new MinidumpMemoryInfoListWriter());
auto memory_map_region = make_scoped_ptr(new TestMemoryMapRegionSnapshot());
MINIDUMP_MEMORY_INFO mmi = {0};
mmi.BaseAddress = 0x12340000;
mmi.AllocationBase = 0x12000000;
mmi.AllocationProtect = PAGE_READWRITE;
mmi.RegionSize = 0x6000;
mmi.State = MEM_COMMIT;
mmi.Protect = PAGE_NOACCESS;
mmi.Type = MEM_PRIVATE;
memory_map_region->SetMindumpMemoryInfo(mmi);
std::vector<const MemoryMapRegionSnapshot*> memory_map;
memory_map.push_back(memory_map_region.get());
memory_info_list_writer->InitializeFromSnapshot(memory_map);
minidump_file_writer.AddStream(memory_info_list_writer.Pass());
StringFile string_file;
ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));
ASSERT_EQ(sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) +
sizeof(MINIDUMP_MEMORY_INFO_LIST) +
sizeof(MINIDUMP_MEMORY_INFO),
string_file.string().size());
const MINIDUMP_MEMORY_INFO_LIST* memory_info_list = nullptr;
ASSERT_NO_FATAL_FAILURE(
GetMemoryInfoListStream(string_file.string(), &memory_info_list));
EXPECT_EQ(1u, memory_info_list->NumberOfEntries);
const MINIDUMP_MEMORY_INFO* memory_info =
reinterpret_cast<const MINIDUMP_MEMORY_INFO*>(&memory_info_list[1]);
EXPECT_EQ(mmi.BaseAddress, memory_info->BaseAddress);
EXPECT_EQ(mmi.AllocationBase, memory_info->AllocationBase);
EXPECT_EQ(mmi.AllocationProtect, memory_info->AllocationProtect);
EXPECT_EQ(mmi.RegionSize, memory_info->RegionSize);
EXPECT_EQ(mmi.State, memory_info->State);
EXPECT_EQ(mmi.Protect, memory_info->Protect);
EXPECT_EQ(mmi.Type, memory_info->Type);
}
} // namespace
} // namespace test
} // namespace crashpad

View File

@ -37,6 +37,7 @@
'minidump_crashpad_info_writer_test.cc', 'minidump_crashpad_info_writer_test.cc',
'minidump_exception_writer_test.cc', 'minidump_exception_writer_test.cc',
'minidump_file_writer_test.cc', 'minidump_file_writer_test.cc',
'minidump_memory_info_writer_test.cc',
'minidump_memory_writer_test.cc', 'minidump_memory_writer_test.cc',
'minidump_misc_info_writer_test.cc', 'minidump_misc_info_writer_test.cc',
'minidump_module_crashpad_info_writer_test.cc', 'minidump_module_crashpad_info_writer_test.cc',

View File

@ -181,6 +181,14 @@ struct MinidumpThreadListTraits {
} }
}; };
struct MinidumpMemoryInfoListTraits {
using ListType = MINIDUMP_MEMORY_INFO_LIST;
enum : size_t { kElementSize = sizeof(MINIDUMP_MEMORY_INFO) };
static size_t ElementCount(const ListType* list) {
return static_cast<size_t>(list->NumberOfEntries);
}
};
struct MinidumpModuleCrashpadInfoListTraits { struct MinidumpModuleCrashpadInfoListTraits {
using ListType = MinidumpModuleCrashpadInfoList; using ListType = MinidumpModuleCrashpadInfoList;
enum : size_t { kElementSize = sizeof(MinidumpModuleCrashpadInfoLink) }; enum : size_t { kElementSize = sizeof(MinidumpModuleCrashpadInfoLink) };
@ -244,6 +252,14 @@ const MINIDUMP_THREAD_LIST* MinidumpWritableAtLocationDescriptor<
file_contents, location); file_contents, location);
} }
template <>
const MINIDUMP_MEMORY_INFO_LIST* MinidumpWritableAtLocationDescriptor<
MINIDUMP_MEMORY_INFO_LIST>(const std::string& file_contents,
const MINIDUMP_LOCATION_DESCRIPTOR& location) {
return MinidumpListAtLocationDescriptor<MinidumpMemoryInfoListTraits>(
file_contents, location);
}
template <> template <>
const MinidumpModuleCrashpadInfoList* const MinidumpModuleCrashpadInfoList*
MinidumpWritableAtLocationDescriptor<MinidumpModuleCrashpadInfoList>( MinidumpWritableAtLocationDescriptor<MinidumpModuleCrashpadInfoList>(

View File

@ -90,6 +90,7 @@ MINIDUMP_ALLOW_OVERSIZED_DATA(MINIDUMP_DIRECTORY);
MINIDUMP_ALLOW_OVERSIZED_DATA(MINIDUMP_MEMORY_LIST); MINIDUMP_ALLOW_OVERSIZED_DATA(MINIDUMP_MEMORY_LIST);
MINIDUMP_ALLOW_OVERSIZED_DATA(MINIDUMP_MODULE_LIST); MINIDUMP_ALLOW_OVERSIZED_DATA(MINIDUMP_MODULE_LIST);
MINIDUMP_ALLOW_OVERSIZED_DATA(MINIDUMP_THREAD_LIST); MINIDUMP_ALLOW_OVERSIZED_DATA(MINIDUMP_THREAD_LIST);
MINIDUMP_ALLOW_OVERSIZED_DATA(MINIDUMP_MEMORY_INFO_LIST);
MINIDUMP_ALLOW_OVERSIZED_DATA(MinidumpModuleCrashpadInfoList); MINIDUMP_ALLOW_OVERSIZED_DATA(MinidumpModuleCrashpadInfoList);
MINIDUMP_ALLOW_OVERSIZED_DATA(MinidumpRVAList); MINIDUMP_ALLOW_OVERSIZED_DATA(MinidumpRVAList);
MINIDUMP_ALLOW_OVERSIZED_DATA(MinidumpSimpleStringDictionary); MINIDUMP_ALLOW_OVERSIZED_DATA(MinidumpSimpleStringDictionary);
@ -138,10 +139,10 @@ const T* TMinidumpWritableAtLocationDescriptor(
//! - With a MINIDUMP_HEADER template parameter, a template specialization //! - With a MINIDUMP_HEADER template parameter, a template specialization
//! ensures that the structures magic number and version fields are correct. //! ensures that the structures magic number and version fields are correct.
//! - With a MINIDUMP_MEMORY_LIST, MINIDUMP_THREAD_LIST, MINIDUMP_MODULE_LIST, //! - With a MINIDUMP_MEMORY_LIST, MINIDUMP_THREAD_LIST, MINIDUMP_MODULE_LIST,
//! or MinidumpSimpleStringDictionary template parameter, template //! MINIDUMP_MEMORY_INFO_LIST, or MinidumpSimpleStringDictionary template
//! specializations ensure that the size given by \a location matches the //! parameter, template specializations ensure that the size given by \a
//! size expected of a stream containing the number of elements it claims to //! location matches the size expected of a stream containing the number of
//! have. //! elements it claims to have.
//! - With an IMAGE_DEBUG_MISC, CodeViewRecordPDB20, or CodeViewRecordPDB70 //! - With an IMAGE_DEBUG_MISC, CodeViewRecordPDB20, or CodeViewRecordPDB70
//! template parameter, template specializations ensure that the structure //! template parameter, template specializations ensure that the structure
//! has the expected format including any magic number and the `NUL`- //! has the expected format including any magic number and the `NUL`-
@ -189,6 +190,11 @@ const MINIDUMP_THREAD_LIST* MinidumpWritableAtLocationDescriptor<
MINIDUMP_THREAD_LIST>(const std::string& file_contents, MINIDUMP_THREAD_LIST>(const std::string& file_contents,
const MINIDUMP_LOCATION_DESCRIPTOR& location); const MINIDUMP_LOCATION_DESCRIPTOR& location);
template <>
const MINIDUMP_MEMORY_INFO_LIST* MinidumpWritableAtLocationDescriptor<
MINIDUMP_MEMORY_INFO_LIST>(const std::string& file_contents,
const MINIDUMP_LOCATION_DESCRIPTOR& location);
template <> template <>
const CodeViewRecordPDB20* MinidumpWritableAtLocationDescriptor< const CodeViewRecordPDB20* MinidumpWritableAtLocationDescriptor<
CodeViewRecordPDB20>(const std::string& file_contents, CodeViewRecordPDB20>(const std::string& file_contents,

View File

@ -75,8 +75,8 @@ TEST(CrashpadInfoClientOptions, OneModule) {
ASSERT_TRUE(process_snapshot.Initialize(mach_task_self())); ASSERT_TRUE(process_snapshot.Initialize(mach_task_self()));
#elif defined(OS_WIN) #elif defined(OS_WIN)
ProcessSnapshotWin process_snapshot; ProcessSnapshotWin process_snapshot;
ASSERT_TRUE(process_snapshot.Initialize(GetCurrentProcess(), ASSERT_TRUE(process_snapshot.Initialize(
ProcessSuspensionState::kRunning)); GetCurrentProcess(), ProcessSuspensionState::kRunning, 0));
#else #else
#error Port. #error Port.
#endif // OS_MACOSX #endif // OS_MACOSX
@ -189,8 +189,8 @@ TEST(CrashpadInfoClientOptions, TwoModules) {
ASSERT_TRUE(process_snapshot.Initialize(mach_task_self())); ASSERT_TRUE(process_snapshot.Initialize(mach_task_self()));
#elif defined(OS_WIN) #elif defined(OS_WIN)
ProcessSnapshotWin process_snapshot; ProcessSnapshotWin process_snapshot;
ASSERT_TRUE(process_snapshot.Initialize(GetCurrentProcess(), ASSERT_TRUE(process_snapshot.Initialize(
ProcessSuspensionState::kRunning)); GetCurrentProcess(), ProcessSuspensionState::kRunning, 0));
#else #else
#error Port. #error Port.
#endif // OS_MACOSX #endif // OS_MACOSX

View File

@ -0,0 +1,31 @@
// 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/handle_snapshot.h"
namespace crashpad {
HandleSnapshot::HandleSnapshot()
: type_name(),
handle(0),
attributes(0),
granted_access(0),
pointer_count(0),
handle_count(0) {
}
HandleSnapshot::~HandleSnapshot() {
}
} // namespace crashpad

View File

@ -0,0 +1,56 @@
// 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_HANDLE_SNAPSHOT_H_
#define CRASHPAD_SNAPSHOT_HANDLE_SNAPSHOT_H_
#include <stdint.h>
#include <string>
namespace crashpad {
struct HandleSnapshot {
HandleSnapshot();
~HandleSnapshot();
//! \brief A string representation of the handle's type.
std::wstring type_name;
//! \brief The handle's value.
uint32_t handle;
//! \brief The attributes for the handle, e.g. `OBJ_INHERIT`,
//! `OBJ_CASE_INSENSITIVE`, etc.
uint32_t attributes;
//! \brief The ACCESS_MASK for the handle in this process.
//!
//! See
//! http://blogs.msdn.com/b/openspecification/archive/2010/04/01/about-the-access-mask-structure.aspx
//! for more information.
uint32_t granted_access;
//! \brief The number of kernel references to the object that this handle
//! refers to.
uint32_t pointer_count;
//! \brief The number of open handles to the object that this handle refers
//! to.
uint32_t handle_count;
};
} // namespace crashpad
#endif // CRASHPAD_SNAPSHOT_HANDLE_SNAPSHOT_H_

View File

@ -186,6 +186,17 @@ const ExceptionSnapshot* ProcessSnapshotMac::Exception() const {
return exception_.get(); return exception_.get();
} }
std::vector<const MemoryMapRegionSnapshot*> ProcessSnapshotMac::MemoryMap()
const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
return std::vector<const MemoryMapRegionSnapshot*>();
}
std::vector<HandleSnapshot> ProcessSnapshotMac::Handles() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
return std::vector<HandleSnapshot>();
}
std::vector<const MemorySnapshot*> ProcessSnapshotMac::ExtraMemory() const { std::vector<const MemorySnapshot*> ProcessSnapshotMac::ExtraMemory() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_); INITIALIZATION_STATE_DCHECK_VALID(initialized_);
return std::vector<const MemorySnapshot*>(); return std::vector<const MemorySnapshot*>();

View File

@ -33,6 +33,7 @@
#include "snapshot/mac/process_reader.h" #include "snapshot/mac/process_reader.h"
#include "snapshot/mac/system_snapshot_mac.h" #include "snapshot/mac/system_snapshot_mac.h"
#include "snapshot/mac/thread_snapshot_mac.h" #include "snapshot/mac/thread_snapshot_mac.h"
#include "snapshot/memory_map_region_snapshot.h"
#include "snapshot/module_snapshot.h" #include "snapshot/module_snapshot.h"
#include "snapshot/process_snapshot.h" #include "snapshot/process_snapshot.h"
#include "snapshot/system_snapshot.h" #include "snapshot/system_snapshot.h"
@ -126,6 +127,8 @@ class ProcessSnapshotMac final : public ProcessSnapshot {
std::vector<const ThreadSnapshot*> Threads() const override; std::vector<const ThreadSnapshot*> Threads() const override;
std::vector<const ModuleSnapshot*> Modules() const override; std::vector<const ModuleSnapshot*> Modules() const override;
const ExceptionSnapshot* Exception() const override; const ExceptionSnapshot* Exception() const override;
std::vector<const MemoryMapRegionSnapshot*> MemoryMap() const override;
std::vector<HandleSnapshot> Handles() const override;
std::vector<const MemorySnapshot*> ExtraMemory() const override; std::vector<const MemorySnapshot*> ExtraMemory() const override;
private: private:

View File

@ -0,0 +1,35 @@
// 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_MEMORY_MAP_REGION_SNAPSHOT_H_
#define CRASHPAD_SNAPSHOT_MEMORY_MAP_REGION_SNAPSHOT_H_
#include <windows.h>
#include <dbghelp.h>
namespace crashpad {
//! \brief An abstract interface to a snapshot representing a region of the
//! memory map present in the snapshot process.
class MemoryMapRegionSnapshot {
public:
virtual ~MemoryMapRegionSnapshot() {}
//! \brief Gets a MINIDUMP_MEMORY_INFO representing the region.
virtual const MINIDUMP_MEMORY_INFO& AsMinidumpMemoryInfo() const = 0;
};
} // namespace crashpad
#endif // CRASHPAD_SNAPSHOT_MEMORY_MAP_REGION_SNAPSHOT_H_

View File

@ -177,6 +177,19 @@ const ExceptionSnapshot* ProcessSnapshotMinidump::Exception() const {
return nullptr; return nullptr;
} }
std::vector<const MemoryMapRegionSnapshot*> ProcessSnapshotMinidump::MemoryMap()
const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
NOTREACHED(); // https://code.google.com/p/crashpad/issues/detail?id=10
return std::vector<const MemoryMapRegionSnapshot*>();
}
std::vector<HandleSnapshot> ProcessSnapshotMinidump::Handles() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
NOTREACHED(); // https://code.google.com/p/crashpad/issues/detail?id=10
return std::vector<HandleSnapshot>();
}
std::vector<const MemorySnapshot*> ProcessSnapshotMinidump::ExtraMemory() std::vector<const MemorySnapshot*> ProcessSnapshotMinidump::ExtraMemory()
const { const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_); INITIALIZATION_STATE_DCHECK_VALID(initialized_);

View File

@ -69,6 +69,8 @@ class ProcessSnapshotMinidump final : public ProcessSnapshot {
std::vector<const ThreadSnapshot*> Threads() const override; std::vector<const ThreadSnapshot*> Threads() const override;
std::vector<const ModuleSnapshot*> Modules() const override; std::vector<const ModuleSnapshot*> Modules() const override;
const ExceptionSnapshot* Exception() const override; const ExceptionSnapshot* Exception() const override;
std::vector<const MemoryMapRegionSnapshot*> MemoryMap() const override;
std::vector<HandleSnapshot> Handles() const override;
std::vector<const MemorySnapshot*> ExtraMemory() const override; std::vector<const MemorySnapshot*> ExtraMemory() const override;
private: private:

View File

@ -22,11 +22,13 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include "snapshot/handle_snapshot.h"
#include "util/misc/uuid.h" #include "util/misc/uuid.h"
namespace crashpad { namespace crashpad {
class ExceptionSnapshot; class ExceptionSnapshot;
class MemoryMapRegionSnapshot;
class MemorySnapshot; class MemorySnapshot;
class ModuleSnapshot; class ModuleSnapshot;
class SystemSnapshot; class SystemSnapshot;
@ -162,6 +164,20 @@ class ProcessSnapshot {
//! an exception, returns `nullptr`. //! an exception, returns `nullptr`.
virtual const ExceptionSnapshot* Exception() const = 0; virtual const ExceptionSnapshot* Exception() const = 0;
//! \brief Returns MemoryMapRegionSnapshot objects reflecting the regions
//! of the memory map in the snapshot process at the time of the snapshot.
//!
//! \return A vector of MemoryMapRegionSnapshot objects. The caller does not
//! take ownership of these objects, they are scoped to the lifetime of
//! the ProcessSnapshot object that they were obtained from.
virtual std::vector<const MemoryMapRegionSnapshot*> MemoryMap() const = 0;
//! \brief Returns HandleSnapshot objects reflecting the open handles in the
//! snapshot process at the time of the snapshot.
//!
//! \return A vector of HandleSnapshot objects.
virtual std::vector<HandleSnapshot> Handles() const = 0;
//! \brief Returns a vector of additional memory blocks that should be //! \brief Returns a vector of additional memory blocks that should be
//! included in a minidump. //! included in a minidump.
//! //!

View File

@ -36,6 +36,8 @@
'crashpad_info_client_options.cc', 'crashpad_info_client_options.cc',
'crashpad_info_client_options.h', 'crashpad_info_client_options.h',
'exception_snapshot.h', 'exception_snapshot.h',
'handle_snapshot.cc',
'handle_snapshot.h',
'mac/cpu_context_mac.cc', 'mac/cpu_context_mac.cc',
'mac/cpu_context_mac.h', 'mac/cpu_context_mac.h',
'mac/exception_snapshot_mac.cc', 'mac/exception_snapshot_mac.cc',
@ -91,6 +93,8 @@
'win/cpu_context_win.h', 'win/cpu_context_win.h',
'win/exception_snapshot_win.cc', 'win/exception_snapshot_win.cc',
'win/exception_snapshot_win.h', 'win/exception_snapshot_win.h',
'win/memory_map_region_snapshot_win.cc',
'win/memory_map_region_snapshot_win.h',
'win/memory_snapshot_win.cc', 'win/memory_snapshot_win.cc',
'win/memory_snapshot_win.h', 'win/memory_snapshot_win.h',
'win/module_snapshot_win.cc', 'win/module_snapshot_win.cc',

View File

@ -34,6 +34,8 @@
'test/test_cpu_context.h', 'test/test_cpu_context.h',
'test/test_exception_snapshot.cc', 'test/test_exception_snapshot.cc',
'test/test_exception_snapshot.h', 'test/test_exception_snapshot.h',
'test/test_memory_map_region_snapshot.cc',
'test/test_memory_map_region_snapshot.h',
'test/test_memory_snapshot.cc', 'test/test_memory_snapshot.cc',
'test/test_memory_snapshot.h', 'test/test_memory_snapshot.h',
'test/test_module_snapshot.cc', 'test/test_module_snapshot.cc',

View File

@ -0,0 +1,37 @@
// 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/test/test_memory_map_region_snapshot.h"
namespace crashpad {
namespace test {
TestMemoryMapRegionSnapshot::TestMemoryMapRegionSnapshot() : memory_info_() {
}
TestMemoryMapRegionSnapshot::~TestMemoryMapRegionSnapshot() {
}
void TestMemoryMapRegionSnapshot::SetMindumpMemoryInfo(
const MINIDUMP_MEMORY_INFO& mmi) {
memory_info_ = mmi;
}
const MINIDUMP_MEMORY_INFO& TestMemoryMapRegionSnapshot::AsMinidumpMemoryInfo()
const {
return memory_info_;
}
} // namespace test
} // namespace crashpad

View 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_TEST_TEST_MEMORY_MAP_REGION_SNAPSHOT_H_
#define CRASHPAD_SNAPSHOT_TEST_TEST_MEMORY_MAP_REGION_SNAPSHOT_H_
#include <vector>
#include "base/basictypes.h"
#include "snapshot/memory_map_region_snapshot.h"
namespace crashpad {
namespace test {
//! \brief A test MemoryMapRegionSnapshot that can carry arbitrary data for
//! testing purposes.
class TestMemoryMapRegionSnapshot final : public MemoryMapRegionSnapshot {
public:
TestMemoryMapRegionSnapshot();
~TestMemoryMapRegionSnapshot() override;
void SetMindumpMemoryInfo(const MINIDUMP_MEMORY_INFO& mmi);
// MemoryMapRegionSnapshot:
const MINIDUMP_MEMORY_INFO& AsMinidumpMemoryInfo() const override;
private:
MINIDUMP_MEMORY_INFO memory_info_;
DISALLOW_COPY_AND_ASSIGN(TestMemoryMapRegionSnapshot);
};
} // namespace test
} // namespace crashpad
#endif // CRASHPAD_SNAPSHOT_TEST_TEST_MEMORY_MAP_REGION_SNAPSHOT_H_

View File

@ -33,6 +33,8 @@ TestProcessSnapshot::TestProcessSnapshot()
threads_(), threads_(),
modules_(), modules_(),
exception_(), exception_(),
memory_map_(),
handles_(),
extra_memory_() { extra_memory_() {
} }
@ -98,6 +100,18 @@ const ExceptionSnapshot* TestProcessSnapshot::Exception() const {
return exception_.get(); return exception_.get();
} }
std::vector<const MemoryMapRegionSnapshot*> TestProcessSnapshot::MemoryMap()
const {
std::vector<const MemoryMapRegionSnapshot*> memory_map;
for (const auto& item : memory_map_)
memory_map.push_back(item);
return memory_map;
}
std::vector<HandleSnapshot> TestProcessSnapshot::Handles() const {
return handles_;
}
std::vector<const MemorySnapshot*> TestProcessSnapshot::ExtraMemory() const { std::vector<const MemorySnapshot*> TestProcessSnapshot::ExtraMemory() const {
std::vector<const MemorySnapshot*> extra_memory; std::vector<const MemorySnapshot*> extra_memory;
for (const auto& em : extra_memory_) for (const auto& em : extra_memory_)

View File

@ -26,6 +26,7 @@
#include "base/basictypes.h" #include "base/basictypes.h"
#include "base/memory/scoped_ptr.h" #include "base/memory/scoped_ptr.h"
#include "snapshot/exception_snapshot.h" #include "snapshot/exception_snapshot.h"
#include "snapshot/memory_map_region_snapshot.h"
#include "snapshot/memory_snapshot.h" #include "snapshot/memory_snapshot.h"
#include "snapshot/module_snapshot.h" #include "snapshot/module_snapshot.h"
#include "snapshot/process_snapshot.h" #include "snapshot/process_snapshot.h"
@ -96,6 +97,22 @@ class TestProcessSnapshot final : public ProcessSnapshot {
exception_ = exception.Pass(); exception_ = exception.Pass();
} }
//! \brief Adds a memory map region snapshot to be returned by MemoryMap().
//!
//! \param[in] region The memory map region snapshot that will be included in
//! MemoryMap(). The TestProcessSnapshot object takes ownership of \a
//! region.
void AddMemoryMapRegion(scoped_ptr<MemoryMapRegionSnapshot> region) {
memory_map_.push_back(region.release());
}
//! \brief Adds a handle snapshot to be returned by Handles().
//!
//! \param[in] region The handle snapshot that will be included in Handles().
void AddHandle(const HandleSnapshot& handle) {
handles_.push_back(handle);
}
//! \brief Add a memory snapshot to be returned by ExtraMemory(). //! \brief Add a memory snapshot to be returned by ExtraMemory().
//! //!
//! \param[in] extra_memory The memory snapshot that will be included in //! \param[in] extra_memory The memory snapshot that will be included in
@ -120,6 +137,8 @@ class TestProcessSnapshot final : public ProcessSnapshot {
std::vector<const ThreadSnapshot*> Threads() const override; std::vector<const ThreadSnapshot*> Threads() const override;
std::vector<const ModuleSnapshot*> Modules() const override; std::vector<const ModuleSnapshot*> Modules() const override;
const ExceptionSnapshot* Exception() const override; const ExceptionSnapshot* Exception() const override;
std::vector<const MemoryMapRegionSnapshot*> MemoryMap() const override;
std::vector<HandleSnapshot> Handles() const override;
std::vector<const MemorySnapshot*> ExtraMemory() const override; std::vector<const MemorySnapshot*> ExtraMemory() const override;
private: private:
@ -136,6 +155,8 @@ class TestProcessSnapshot final : public ProcessSnapshot {
PointerVector<ThreadSnapshot> threads_; PointerVector<ThreadSnapshot> threads_;
PointerVector<ModuleSnapshot> modules_; PointerVector<ModuleSnapshot> modules_;
scoped_ptr<ExceptionSnapshot> exception_; scoped_ptr<ExceptionSnapshot> exception_;
PointerVector<MemoryMapRegionSnapshot> memory_map_;
std::vector<HandleSnapshot> handles_;
PointerVector<MemorySnapshot> extra_memory_; PointerVector<MemorySnapshot> extra_memory_;
DISALLOW_COPY_AND_ASSIGN(TestProcessSnapshot); DISALLOW_COPY_AND_ASSIGN(TestProcessSnapshot);

View File

@ -15,7 +15,27 @@
# limitations under the License. # limitations under the License.
import os import os
import platform
import random
import re
import subprocess
import sys import sys
import tempfile
g_temp_dirs = []
def MakeTempDir():
global g_temp_dirs
new_dir = tempfile.mkdtemp()
g_temp_dirs.append(new_dir)
return new_dir
def CleanUpTempDirs():
global g_temp_dirs
for d in g_temp_dirs:
subprocess.call(['rmdir', '/s', '/q', d], shell=True)
def FindInstalledWindowsApplication(app_path): def FindInstalledWindowsApplication(app_path):
@ -35,6 +55,9 @@ def FindInstalledWindowsApplication(app_path):
def GetCdbPath(): def GetCdbPath():
"""Search in some reasonable places to find cdb.exe. Searches x64 before x86
and newer versions before older versions.
"""
possible_paths = ( possible_paths = (
os.path.join('Windows Kits', '10', 'Debuggers', 'x64'), os.path.join('Windows Kits', '10', 'Debuggers', 'x64'),
os.path.join('Windows Kits', '10', 'Debuggers', 'x86'), os.path.join('Windows Kits', '10', 'Debuggers', 'x86'),
@ -44,12 +67,7 @@ def GetCdbPath():
os.path.join('Windows Kits', '8.0', 'Debuggers', 'x86'), os.path.join('Windows Kits', '8.0', 'Debuggers', 'x86'),
'Debugging Tools For Windows (x64)', 'Debugging Tools For Windows (x64)',
'Debugging Tools For Windows (x86)', 'Debugging Tools For Windows (x86)',
'Debugging Tools For Windows', 'Debugging Tools For Windows',)
os.path.join('win_toolchain', 'vs2013_files', 'win8sdk', 'Debuggers',
'x64'),
os.path.join('win_toolchain', 'vs2013_files', 'win8sdk', 'Debuggers',
'x86'),
)
for possible_path in possible_paths: for possible_path in possible_paths:
app_path = os.path.join(possible_path, 'cdb.exe') app_path = os.path.join(possible_path, 'cdb.exe')
app_path = FindInstalledWindowsApplication(app_path) app_path = FindInstalledWindowsApplication(app_path)
@ -58,10 +76,147 @@ def GetCdbPath():
return None return None
def GetDumpFromCrashyProgram(out_dir, pipe_name):
"""Initialize a crash database, run crashpad_handler, run crashy_program
connecting to the crash_handler. Returns the minidump generated by
crash_handler for further testing.
"""
test_database = MakeTempDir()
handler = None
try:
if subprocess.call(
[os.path.join(out_dir, 'crashpad_database_util.exe'), '--create',
'--database=' + test_database]) != 0:
print 'could not initialize report database'
return None
handler = subprocess.Popen([
os.path.join(out_dir, 'crashpad_handler.exe'),
'--pipe-name=' + pipe_name,
'--database=' + test_database
])
subprocess.call([os.path.join(out_dir, 'crashy_program.exe'), pipe_name])
out = subprocess.check_output([
os.path.join(out_dir, 'crashpad_database_util.exe'),
'--database=' + test_database,
'--show-completed-reports',
'--show-all-report-info',
])
for line in out.splitlines():
if line.strip().startswith('Path:'):
return line.partition(':')[2].strip()
finally:
if handler:
handler.kill()
class CdbRun(object):
"""Run cdb.exe passing it a cdb command and capturing the output.
`Check()` searches for regex patterns in sequence allowing verification of
expected output.
"""
def __init__(self, cdb_path, dump_path, command):
# Run a command line that loads the dump, runs the specified cdb command,
# and then quits, and capturing stdout.
self.out = subprocess.check_output([
cdb_path,
'-z', dump_path,
'-c', command + ';q'
])
def Check(self, pattern, message, re_flags=0):
match_obj = re.search(pattern, self.out, re_flags)
if match_obj:
# Matched. Consume up to end of match.
self.out = self.out[match_obj.end(0):]
print 'ok - %s' % message
sys.stdout.flush()
else:
print >>sys.stderr, '-' * 80
print >>sys.stderr, 'FAILED - %s' % message
print >>sys.stderr, '-' * 80
print >>sys.stderr, 'did not match:\n %s' % pattern
print >>sys.stderr, '-' * 80
print >>sys.stderr, 'remaining output was:\n %s' % self.out
print >>sys.stderr, '-' * 80
sys.stderr.flush()
sys.exit(1)
def RunTests(cdb_path, dump_path, pipe_name):
"""Runs various tests in sequence. Runs a new cdb instance on the dump for
each block of tests to reduce the chances that output from one command is
confused for output from another.
"""
out = CdbRun(cdb_path, dump_path, '.ecxr')
out.Check('This dump file has an exception of interest stored in it',
'captured exception')
out.Check(
'crashy_program!crashpad::`anonymous namespace\'::SomeCrashyFunction',
'exception at correct location')
out = CdbRun(cdb_path, dump_path, '!peb')
out.Check(r'PEB at', 'found the PEB')
out.Check(r'Ldr\.InMemoryOrderModuleList:.*\d+ \. \d+', 'PEB_LDR_DATA saved')
out.Check(r'Base TimeStamp Module', 'module list present')
pipe_name_escaped = pipe_name.replace('\\', '\\\\')
out.Check(r'CommandLine: *\'.*crashy_program.exe *' + pipe_name_escaped,
'some PEB data is correct')
out.Check(r'SystemRoot=C:\\Windows', 'some of environment captured',
re.IGNORECASE)
out = CdbRun(cdb_path, dump_path, '!teb')
out.Check(r'TEB at', 'found the TEB')
out.Check(r'ExceptionList:\s+[0-9a-fA-F]+', 'some valid teb data')
out.Check(r'LastErrorValue:\s+2', 'correct LastErrorValue')
out = CdbRun(cdb_path, dump_path, '!gle')
out.Check('LastErrorValue: \(Win32\) 0x2 \(2\) - The system cannot find the '
'file specified.', '!gle gets last error')
out.Check('LastStatusValue: \(NTSTATUS\) 0xc000000f - {File Not Found} The '
'file %hs does not exist.', '!gle gets last ntstatus')
out = CdbRun(cdb_path, dump_path, '!locks')
out.Check(r'CritSec crashy_program!crashpad::`anonymous namespace\'::'
r'g_test_critical_section', 'lock was captured')
if float(platform.win32_ver()[0]) != 7:
# We can't allocate CRITICAL_SECTIONs with .DebugInfo on Win 7.
out.Check(r'\*\*\* Locked', 'lock debug info was captured, and is locked')
def main(args): def main(args):
cdb_path = GetCdbPath() try:
print 'cdb_path:', cdb_path if len(args) != 1:
return 0 print >>sys.stderr, 'must supply binary dir'
return 1
cdb_path = GetCdbPath()
if not cdb_path:
print >>sys.stderr, 'could not find cdb'
return 1
# Make sure we can download Windows symbols.
if not os.environ.get('_NT_SYMBOL_PATH'):
symbol_dir = MakeTempDir()
os.environ['_NT_SYMBOL_PATH'] = (
'SRV*' + symbol_dir + '*https://msdl.microsoft.com/download/symbols')
pipe_name = r'\\.\pipe\end-to-end_%s_%s' % (
os.getpid(), str(random.getrandbits(64)))
dump_path = GetDumpFromCrashyProgram(args[0], pipe_name)
if not dump_path:
return 1
RunTests(cdb_path, dump_path, pipe_name)
return 0
finally:
CleanUpTempDirs()
if __name__ == '__main__': if __name__ == '__main__':

View File

@ -88,10 +88,13 @@ class CrashingDelegate : public ExceptionHandlerServer::Delegate {
unsigned int ExceptionHandlerServerException( unsigned int ExceptionHandlerServerException(
HANDLE process, HANDLE process,
WinVMAddress exception_information_address) override { WinVMAddress exception_information_address,
WinVMAddress debug_critical_section_address) override {
ScopedProcessSuspend suspend(process); ScopedProcessSuspend suspend(process);
ProcessSnapshotWin snapshot; ProcessSnapshotWin snapshot;
snapshot.Initialize(process, ProcessSuspensionState::kSuspended); snapshot.Initialize(process,
ProcessSuspensionState::kSuspended,
debug_critical_section_address);
snapshot.InitializeException(exception_information_address); snapshot.InitializeException(exception_information_address);
// Confirm the exception record was read correctly. // Confirm the exception record was read correctly.
@ -186,10 +189,13 @@ class SimulateDelegate : public ExceptionHandlerServer::Delegate {
unsigned int ExceptionHandlerServerException( unsigned int ExceptionHandlerServerException(
HANDLE process, HANDLE process,
WinVMAddress exception_information_address) override { WinVMAddress exception_information_address,
WinVMAddress debug_critical_section_address) override {
ScopedProcessSuspend suspend(process); ScopedProcessSuspend suspend(process);
ProcessSnapshotWin snapshot; ProcessSnapshotWin snapshot;
snapshot.Initialize(process, ProcessSuspensionState::kSuspended); snapshot.Initialize(process,
ProcessSuspensionState::kSuspended,
debug_critical_section_address);
snapshot.InitializeException(exception_information_address); snapshot.InitializeException(exception_information_address);
EXPECT_TRUE(snapshot.Exception()); EXPECT_TRUE(snapshot.Exception());
EXPECT_EQ(0x517a7ed, snapshot.Exception()->Exception()); EXPECT_EQ(0x517a7ed, snapshot.Exception()->Exception());

View File

@ -0,0 +1,41 @@
// 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_map_region_snapshot_win.h"
namespace crashpad {
namespace internal {
MemoryMapRegionSnapshotWin::MemoryMapRegionSnapshotWin(
const MEMORY_BASIC_INFORMATION64& mbi)
: memory_info_() {
memory_info_.BaseAddress = mbi.BaseAddress;
memory_info_.AllocationBase = mbi.AllocationBase;
memory_info_.AllocationProtect = mbi.AllocationProtect;
memory_info_.RegionSize = mbi.RegionSize;
memory_info_.State = mbi.State;
memory_info_.Protect = mbi.Protect;
memory_info_.Type = mbi.Type;
}
MemoryMapRegionSnapshotWin::~MemoryMapRegionSnapshotWin() {
}
const MINIDUMP_MEMORY_INFO& MemoryMapRegionSnapshotWin::AsMinidumpMemoryInfo()
const {
return memory_info_;
}
} // namespace internal
} // namespace crashpad

View File

@ -0,0 +1,37 @@
// 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_MAP_REGION_SNAPSHOT_WIN_H_
#define CRASHPAD_SNAPSHOT_WIN_MEMORY_MAP_REGION_SNAPSHOT_WIN_H_
#include "snapshot/memory_map_region_snapshot.h"
namespace crashpad {
namespace internal {
class MemoryMapRegionSnapshotWin : public MemoryMapRegionSnapshot {
public:
explicit MemoryMapRegionSnapshotWin(const MEMORY_BASIC_INFORMATION64& mbi);
~MemoryMapRegionSnapshotWin() override;
virtual const MINIDUMP_MEMORY_INFO& AsMinidumpMemoryInfo() const override;
private:
MINIDUMP_MEMORY_INFO memory_info_;
};
} // namespace internal
} // namespace crashpad
#endif // CRASHPAD_SNAPSHOT_WIN_MEMORY_MAP_REGION_SNAPSHOT_WIN_H_

View File

@ -18,6 +18,7 @@
#include "base/logging.h" #include "base/logging.h"
#include "base/strings/stringprintf.h" #include "base/strings/stringprintf.h"
#include "snapshot/win/memory_snapshot_win.h"
#include "snapshot/win/module_snapshot_win.h" #include "snapshot/win/module_snapshot_win.h"
#include "util/win/registration_protocol_win.h" #include "util/win/registration_protocol_win.h"
#include "util/win/time.h" #include "util/win/time.h"
@ -30,6 +31,7 @@ ProcessSnapshotWin::ProcessSnapshotWin()
threads_(), threads_(),
modules_(), modules_(),
exception_(), exception_(),
memory_map_(),
process_reader_(), process_reader_(),
report_id_(), report_id_(),
client_id_(), client_id_(),
@ -41,8 +43,10 @@ ProcessSnapshotWin::ProcessSnapshotWin()
ProcessSnapshotWin::~ProcessSnapshotWin() { ProcessSnapshotWin::~ProcessSnapshotWin() {
} }
bool ProcessSnapshotWin::Initialize(HANDLE process, bool ProcessSnapshotWin::Initialize(
ProcessSuspensionState suspension_state) { HANDLE process,
ProcessSuspensionState suspension_state,
WinVMAddress debug_critical_section_address) {
INITIALIZATION_STATE_SET_INITIALIZING(initialized_); INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
GetTimeOfDay(&snapshot_time_); GetTimeOfDay(&snapshot_time_);
@ -52,14 +56,22 @@ bool ProcessSnapshotWin::Initialize(HANDLE process,
system_.Initialize(&process_reader_); system_.Initialize(&process_reader_);
if (process_reader_.Is64Bit()) if (process_reader_.Is64Bit()) {
InitializePebData<process_types::internal::Traits64>(); InitializePebData<process_types::internal::Traits64>(
else debug_critical_section_address);
InitializePebData<process_types::internal::Traits32>(); } else {
InitializePebData<process_types::internal::Traits32>(
debug_critical_section_address);
}
InitializeThreads(); InitializeThreads();
InitializeModules(); InitializeModules();
for (const MEMORY_BASIC_INFORMATION64& mbi :
process_reader_.GetProcessInfo().MemoryInfo()) {
memory_map_.push_back(new internal::MemoryMapRegionSnapshotWin(mbi));
}
INITIALIZATION_STATE_SET_VALID(initialized_); INITIALIZATION_STATE_SET_VALID(initialized_);
return true; return true;
} }
@ -187,11 +199,34 @@ const ExceptionSnapshot* ProcessSnapshotWin::Exception() const {
return exception_.get(); return exception_.get();
} }
std::vector<const MemoryMapRegionSnapshot*> ProcessSnapshotWin::MemoryMap()
const {
std::vector<const MemoryMapRegionSnapshot*> memory_map;
for (const auto& item : memory_map_)
memory_map.push_back(item);
return memory_map;
}
std::vector<HandleSnapshot> ProcessSnapshotWin::Handles() const {
std::vector<HandleSnapshot> result;
for (const auto& handle : process_reader_.GetProcessInfo().Handles()) {
HandleSnapshot snapshot;
snapshot.type_name = handle.type_name;
snapshot.handle = handle.handle;
snapshot.attributes = handle.attributes;
snapshot.granted_access = handle.granted_access;
snapshot.pointer_count = handle.pointer_count;
snapshot.handle_count = handle.handle_count;
result.push_back(snapshot);
}
return result;
}
std::vector<const MemorySnapshot*> ProcessSnapshotWin::ExtraMemory() const { std::vector<const MemorySnapshot*> ProcessSnapshotWin::ExtraMemory() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_); INITIALIZATION_STATE_DCHECK_VALID(initialized_);
std::vector<const MemorySnapshot*> extra_memory; std::vector<const MemorySnapshot*> extra_memory;
for (const auto& peb_memory : peb_memory_) for (const auto& em : extra_memory_)
extra_memory.push_back(peb_memory); extra_memory.push_back(em);
return extra_memory; return extra_memory;
} }
@ -220,11 +255,12 @@ void ProcessSnapshotWin::InitializeModules() {
} }
template <class Traits> template <class Traits>
void ProcessSnapshotWin::InitializePebData() { void ProcessSnapshotWin::InitializePebData(
WinVMAddress debug_critical_section_address) {
WinVMAddress peb_address; WinVMAddress peb_address;
WinVMSize peb_size; WinVMSize peb_size;
process_reader_.GetProcessInfo().Peb(&peb_address, &peb_size); process_reader_.GetProcessInfo().Peb(&peb_address, &peb_size);
AddMemorySnapshot(peb_address, peb_size, &peb_memory_); AddMemorySnapshot(peb_address, peb_size, &extra_memory_);
process_types::PEB<Traits> peb_data; process_types::PEB<Traits> peb_data;
if (!process_reader_.ReadMemory(peb_address, peb_size, &peb_data)) { if (!process_reader_.ReadMemory(peb_address, peb_size, &peb_data)) {
@ -233,7 +269,7 @@ void ProcessSnapshotWin::InitializePebData() {
} }
process_types::PEB_LDR_DATA<Traits> peb_ldr_data; process_types::PEB_LDR_DATA<Traits> peb_ldr_data;
AddMemorySnapshot(peb_data.Ldr, sizeof(peb_ldr_data), &peb_memory_); AddMemorySnapshot(peb_data.Ldr, sizeof(peb_ldr_data), &extra_memory_);
if (!process_reader_.ReadMemory( if (!process_reader_.ReadMemory(
peb_data.Ldr, sizeof(peb_ldr_data), &peb_ldr_data)) { peb_data.Ldr, sizeof(peb_ldr_data), &peb_ldr_data)) {
LOG(ERROR) << "ReadMemory PEB_LDR_DATA"; LOG(ERROR) << "ReadMemory PEB_LDR_DATA";
@ -242,17 +278,17 @@ void ProcessSnapshotWin::InitializePebData() {
AddMemorySnapshotForLdrLIST_ENTRY( AddMemorySnapshotForLdrLIST_ENTRY(
peb_ldr_data.InLoadOrderModuleList, peb_ldr_data.InLoadOrderModuleList,
offsetof(process_types::LDR_DATA_TABLE_ENTRY<Traits>, InLoadOrderLinks), offsetof(process_types::LDR_DATA_TABLE_ENTRY<Traits>, InLoadOrderLinks),
&peb_memory_); &extra_memory_);
AddMemorySnapshotForLdrLIST_ENTRY( AddMemorySnapshotForLdrLIST_ENTRY(
peb_ldr_data.InMemoryOrderModuleList, peb_ldr_data.InMemoryOrderModuleList,
offsetof(process_types::LDR_DATA_TABLE_ENTRY<Traits>, offsetof(process_types::LDR_DATA_TABLE_ENTRY<Traits>,
InMemoryOrderLinks), InMemoryOrderLinks),
&peb_memory_); &extra_memory_);
AddMemorySnapshotForLdrLIST_ENTRY( AddMemorySnapshotForLdrLIST_ENTRY(
peb_ldr_data.InInitializationOrderModuleList, peb_ldr_data.InInitializationOrderModuleList,
offsetof(process_types::LDR_DATA_TABLE_ENTRY<Traits>, offsetof(process_types::LDR_DATA_TABLE_ENTRY<Traits>,
InInitializationOrderLinks), InInitializationOrderLinks),
&peb_memory_); &extra_memory_);
} }
process_types::RTL_USER_PROCESS_PARAMETERS<Traits> process_parameters; process_types::RTL_USER_PROCESS_PARAMETERS<Traits> process_parameters;
@ -263,27 +299,38 @@ void ProcessSnapshotWin::InitializePebData() {
return; return;
} }
AddMemorySnapshot( AddMemorySnapshot(
peb_data.ProcessParameters, sizeof(process_parameters), &peb_memory_); peb_data.ProcessParameters, sizeof(process_parameters), &extra_memory_);
AddMemorySnapshotForUNICODE_STRING( AddMemorySnapshotForUNICODE_STRING(
process_parameters.CurrentDirectory.DosPath, &peb_memory_); process_parameters.CurrentDirectory.DosPath, &extra_memory_);
AddMemorySnapshotForUNICODE_STRING(process_parameters.DllPath, &peb_memory_); AddMemorySnapshotForUNICODE_STRING(process_parameters.DllPath,
&extra_memory_);
AddMemorySnapshotForUNICODE_STRING(process_parameters.ImagePathName, AddMemorySnapshotForUNICODE_STRING(process_parameters.ImagePathName,
&peb_memory_); &extra_memory_);
AddMemorySnapshotForUNICODE_STRING(process_parameters.CommandLine, AddMemorySnapshotForUNICODE_STRING(process_parameters.CommandLine,
&peb_memory_); &extra_memory_);
AddMemorySnapshotForUNICODE_STRING(process_parameters.WindowTitle, AddMemorySnapshotForUNICODE_STRING(process_parameters.WindowTitle,
&peb_memory_); &extra_memory_);
AddMemorySnapshotForUNICODE_STRING(process_parameters.DesktopInfo, AddMemorySnapshotForUNICODE_STRING(process_parameters.DesktopInfo,
&peb_memory_); &extra_memory_);
AddMemorySnapshotForUNICODE_STRING(process_parameters.ShellInfo, AddMemorySnapshotForUNICODE_STRING(process_parameters.ShellInfo,
&peb_memory_); &extra_memory_);
AddMemorySnapshotForUNICODE_STRING(process_parameters.RuntimeData, AddMemorySnapshotForUNICODE_STRING(process_parameters.RuntimeData,
&peb_memory_); &extra_memory_);
AddMemorySnapshot( AddMemorySnapshot(
process_parameters.Environment, process_parameters.Environment,
DetermineSizeOfEnvironmentBlock(process_parameters.Environment), DetermineSizeOfEnvironmentBlock(process_parameters.Environment),
&peb_memory_); &extra_memory_);
// Walk the loader lock which is directly referenced by the PEB. It may or may
// not have a .DebugInfo list, but doesn't on more recent OSs (it does on
// Vista). If it does, then we may walk the lock list more than once, but
// AddMemorySnapshot() will take care of deduplicating the added regions.
ReadLocks<Traits>(peb_data.LoaderLock, &extra_memory_);
// Traverse the locks with valid .DebugInfo if a starting point was supplied.
if (debug_critical_section_address)
ReadLocks<Traits>(debug_critical_section_address, &extra_memory_);
} }
void ProcessSnapshotWin::AddMemorySnapshot( void ProcessSnapshotWin::AddMemorySnapshot(
@ -384,4 +431,97 @@ WinVMSize ProcessSnapshotWin::DetermineSizeOfEnvironmentBlock(
return env_block.size() * sizeof(env_block[0]); return env_block.size() * sizeof(env_block[0]);
} }
template <class Traits>
void ProcessSnapshotWin::ReadLocks(
WinVMAddress start,
PointerVector<internal::MemorySnapshotWin>* into) {
// We're walking the RTL_CRITICAL_SECTION_DEBUG ProcessLocksList, but starting
// from an actual RTL_CRITICAL_SECTION, so start by getting to the first
// RTL_CRITICAL_SECTION_DEBUG.
process_types::RTL_CRITICAL_SECTION<Traits> critical_section;
if (!process_reader_.ReadMemory(
start, sizeof(critical_section), &critical_section)) {
LOG(ERROR) << "failed to read RTL_CRITICAL_SECTION";
return;
}
const decltype(critical_section.DebugInfo) kInvalid =
static_cast<decltype(critical_section.DebugInfo)>(-1);
if (critical_section.DebugInfo == kInvalid)
return;
const WinVMAddress start_address_backward = critical_section.DebugInfo;
WinVMAddress current_address = start_address_backward;
WinVMAddress last_good_address;
// Typically, this seems to be a circular list, but it's not clear that it
// always is, so follow Blink fields back to the head (or where we started)
// before following Flink to capture memory.
do {
last_good_address = current_address;
// Read the RTL_CRITICAL_SECTION_DEBUG structure to get ProcessLocksList.
process_types::RTL_CRITICAL_SECTION_DEBUG<Traits> critical_section_debug;
if (!process_reader_.ReadMemory(current_address,
sizeof(critical_section_debug),
&critical_section_debug)) {
LOG(ERROR) << "failed to read RTL_CRITICAL_SECTION_DEBUG";
return;
}
if (critical_section_debug.ProcessLocksList.Blink == 0) {
// At the head of the list.
break;
}
// Move to the previous RTL_CRITICAL_SECTION_DEBUG by walking
// ProcessLocksList.Blink.
current_address =
critical_section_debug.ProcessLocksList.Blink -
offsetof(process_types::RTL_CRITICAL_SECTION_DEBUG<Traits>,
ProcessLocksList);
} while (current_address != start_address_backward &&
current_address != kInvalid);
if (current_address == kInvalid) {
// Unexpectedly encountered a bad record, so step back one.
current_address = last_good_address;
}
const WinVMAddress start_address_forward = current_address;
// current_address is now the head of the list, walk Flink to add the whole
// list.
do {
// Read the RTL_CRITICAL_SECTION_DEBUG structure to get ProcessLocksList.
process_types::RTL_CRITICAL_SECTION_DEBUG<Traits> critical_section_debug;
if (!process_reader_.ReadMemory(current_address,
sizeof(critical_section_debug),
&critical_section_debug)) {
LOG(ERROR) << "failed to read RTL_CRITICAL_SECTION_DEBUG";
return;
}
// Add both RTL_CRITICAL_SECTION_DEBUG and RTL_CRITICAL_SECTION to the extra
// memory to be saved.
AddMemorySnapshot(current_address,
sizeof(process_types::RTL_CRITICAL_SECTION_DEBUG<Traits>),
into);
AddMemorySnapshot(critical_section_debug.CriticalSection,
sizeof(process_types::RTL_CRITICAL_SECTION<Traits>),
into);
if (critical_section_debug.ProcessLocksList.Flink == 0)
break;
// Move to the next RTL_CRITICAL_SECTION_DEBUG by walking
// ProcessLocksList.Flink.
current_address =
critical_section_debug.ProcessLocksList.Flink -
offsetof(process_types::RTL_CRITICAL_SECTION_DEBUG<Traits>,
ProcessLocksList);
} while (current_address != start_address_forward &&
current_address != kInvalid);
}
} // namespace crashpad } // namespace crashpad

View File

@ -27,12 +27,14 @@
#include "client/crashpad_info.h" #include "client/crashpad_info.h"
#include "snapshot/crashpad_info_client_options.h" #include "snapshot/crashpad_info_client_options.h"
#include "snapshot/exception_snapshot.h" #include "snapshot/exception_snapshot.h"
#include "snapshot/memory_map_region_snapshot.h"
#include "snapshot/memory_snapshot.h" #include "snapshot/memory_snapshot.h"
#include "snapshot/module_snapshot.h" #include "snapshot/module_snapshot.h"
#include "snapshot/process_snapshot.h" #include "snapshot/process_snapshot.h"
#include "snapshot/system_snapshot.h" #include "snapshot/system_snapshot.h"
#include "snapshot/thread_snapshot.h" #include "snapshot/thread_snapshot.h"
#include "snapshot/win/exception_snapshot_win.h" #include "snapshot/win/exception_snapshot_win.h"
#include "snapshot/win/memory_map_region_snapshot_win.h"
#include "snapshot/win/memory_snapshot_win.h" #include "snapshot/win/memory_snapshot_win.h"
#include "snapshot/win/module_snapshot_win.h" #include "snapshot/win/module_snapshot_win.h"
#include "snapshot/win/system_snapshot_win.h" #include "snapshot/win/system_snapshot_win.h"
@ -57,12 +59,18 @@ class ProcessSnapshotWin final : public ProcessSnapshot {
//! \param[in] process The handle to create a snapshot from. //! \param[in] process The handle to create a snapshot from.
//! \param[in] suspension_state Whether \a process has been suspended by the //! \param[in] suspension_state Whether \a process has been suspended by the
//! caller. //! caller.
//! \param[in] debug_critical_section_address The address in the target
//! process's address space of a `CRITICAL_SECTION` allocated with valid
//! `.DebugInfo`. Used as a starting point to walk the process's locks.
//! May be `0`.
//! //!
//! \return `true` if the snapshot could be created, `false` otherwise with //! \return `true` if the snapshot could be created, `false` otherwise with
//! an appropriate message logged. //! an appropriate message logged.
//! //!
//! \sa ScopedProcessSuspend //! \sa ScopedProcessSuspend
bool Initialize(HANDLE process, ProcessSuspensionState suspension_state); bool Initialize(HANDLE process,
ProcessSuspensionState suspension_state,
WinVMAddress debug_critical_section_address);
//! \brief Initializes the object's exception. //! \brief Initializes the object's exception.
//! //!
@ -126,6 +134,8 @@ class ProcessSnapshotWin final : public ProcessSnapshot {
std::vector<const ThreadSnapshot*> Threads() const override; std::vector<const ThreadSnapshot*> Threads() const override;
std::vector<const ModuleSnapshot*> Modules() const override; std::vector<const ModuleSnapshot*> Modules() const override;
const ExceptionSnapshot* Exception() const override; const ExceptionSnapshot* Exception() const override;
std::vector<const MemoryMapRegionSnapshot*> MemoryMap() const override;
std::vector<HandleSnapshot> Handles() const override;
std::vector<const MemorySnapshot*> ExtraMemory() const override; std::vector<const MemorySnapshot*> ExtraMemory() const override;
private: private:
@ -135,9 +145,10 @@ class ProcessSnapshotWin final : public ProcessSnapshot {
// Initializes modules_ on behalf of Initialize(). // Initializes modules_ on behalf of Initialize().
void InitializeModules(); void InitializeModules();
// Initializes peb_memory_ on behalf of Initialize(). // Initializes various memory blocks reachable from the PEB on behalf of
// Initialize().
template <class Traits> template <class Traits>
void InitializePebData(); void InitializePebData(WinVMAddress debug_critical_section_address);
void AddMemorySnapshot(WinVMAddress address, void AddMemorySnapshot(WinVMAddress address,
WinVMSize size, WinVMSize size,
@ -157,11 +168,20 @@ class ProcessSnapshotWin final : public ProcessSnapshot {
WinVMSize DetermineSizeOfEnvironmentBlock( WinVMSize DetermineSizeOfEnvironmentBlock(
WinVMAddress start_of_environment_block); WinVMAddress start_of_environment_block);
// Starting from the address of a CRITICAL_SECTION, walks the doubly-linked
// list stored in RTL_CRITICAL_SECTION.DebugInfo.ProcessLocksList adding both
// the RTL_CRITICAL_SECTION and the RTL_CRITICAL_SECTION_DEBUG memory blocks
// to the snapshot.
template <class Traits>
void ReadLocks(WinVMAddress start,
PointerVector<internal::MemorySnapshotWin>* into);
internal::SystemSnapshotWin system_; internal::SystemSnapshotWin system_;
PointerVector<internal::MemorySnapshotWin> peb_memory_; PointerVector<internal::MemorySnapshotWin> extra_memory_;
PointerVector<internal::ThreadSnapshotWin> threads_; PointerVector<internal::ThreadSnapshotWin> threads_;
PointerVector<internal::ModuleSnapshotWin> modules_; PointerVector<internal::ModuleSnapshotWin> modules_;
scoped_ptr<internal::ExceptionSnapshotWin> exception_; scoped_ptr<internal::ExceptionSnapshotWin> exception_;
PointerVector<internal::MemoryMapRegionSnapshotWin> memory_map_;
ProcessReaderWin process_reader_; ProcessReaderWin process_reader_;
UUID report_id_; UUID report_id_;
UUID client_id_; UUID client_id_;

View File

@ -52,8 +52,8 @@ void TestImageReaderChild(const base::string16& directory_modification) {
ScopedProcessSuspend suspend(child.process_handle()); ScopedProcessSuspend suspend(child.process_handle());
ProcessSnapshotWin process_snapshot; ProcessSnapshotWin process_snapshot;
ASSERT_TRUE(process_snapshot.Initialize(child.process_handle(), ASSERT_TRUE(process_snapshot.Initialize(
ProcessSuspensionState::kSuspended)); child.process_handle(), ProcessSuspensionState::kSuspended, 0));
ASSERT_GE(process_snapshot.Modules().size(), 2u); ASSERT_GE(process_snapshot.Modules().size(), 2u);

View File

@ -186,7 +186,8 @@ int GenerateDumpMain(int argc, char* argv[]) {
if (!process_snapshot.Initialize(process.get(), if (!process_snapshot.Initialize(process.get(),
options.suspend options.suspend
? ProcessSuspensionState::kSuspended ? ProcessSuspensionState::kSuspended
: ProcessSuspensionState::kRunning)) { : ProcessSuspensionState::kRunning,
0)) {
return EXIT_FAILURE; return EXIT_FAILURE;
} }
#endif // OS_MACOSX #endif // OS_MACOSX

View File

@ -152,6 +152,8 @@
'win/capture_context.asm', 'win/capture_context.asm',
'win/capture_context.h', 'win/capture_context.h',
'win/checked_win_address_range.h', 'win/checked_win_address_range.h',
'win/critical_section_with_debug_info.cc',
'win/critical_section_with_debug_info.h',
'win/exception_handler_server.cc', 'win/exception_handler_server.cc',
'win/exception_handler_server.h', 'win/exception_handler_server.h',
'win/module_version.cc', 'win/module_version.cc',

View File

@ -80,6 +80,7 @@
'thread/thread_log_messages_test.cc', 'thread/thread_log_messages_test.cc',
'thread/thread_test.cc', 'thread/thread_test.cc',
'win/capture_context_test.cc', 'win/capture_context_test.cc',
'win/critical_section_with_debug_info_test.cc',
'win/exception_handler_server_test.cc', 'win/exception_handler_server_test.cc',
'win/process_info_test.cc', 'win/process_info_test.cc',
'win/scoped_process_suspend_test.cc', 'win/scoped_process_suspend_test.cc',

View File

@ -0,0 +1,76 @@
// 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/critical_section_with_debug_info.h"
#include "base/logging.h"
namespace crashpad {
namespace {
BOOL CrashpadInitializeCriticalSectionEx(
CRITICAL_SECTION* critical_section,
DWORD spin_count,
DWORD flags) {
static decltype(InitializeCriticalSectionEx)* initialize_critical_section_ex =
reinterpret_cast<decltype(InitializeCriticalSectionEx)*>(GetProcAddress(
LoadLibrary(L"kernel32.dll"), "InitializeCriticalSectionEx"));
if (!initialize_critical_section_ex) {
PLOG(ERROR) << "GetProcAddress";
return false;
}
bool ret =
initialize_critical_section_ex(critical_section, spin_count, flags);
if (!ret) {
PLOG(ERROR) << "InitializeCriticalSectionEx";
return false;
}
return true;
}
} // namespace
bool InitializeCriticalSectionWithDebugInfoIfPossible(
CRITICAL_SECTION* critical_section) {
// On XP and Vista, a plain initialization causes the CRITICAL_SECTION to be
// allocated with .DebugInfo. On 8 and above, we can pass an additional flag
// to InitializeCriticalSectionEx() to force the .DebugInfo on. Before Win 8,
// that flag causes InitializeCriticalSectionEx() to fail. So, for XP, Vista,
// and 7 we use InitializeCriticalSection(), and for 8 and above,
// InitializeCriticalSectionEx() with the additional flag.
//
// TODO(scottmg): Try to find a solution for Win 7. It's unclear how to force
// it on for Win 7, however the Loader Lock does have .DebugInfo so there may
// be a way to do it. The comments in winnt.h imply that perhaps it's passed
// to InitializeCriticalSectionAndSpinCount() as the top bits of the spin
// count, but that doesn't appear to work. For now, we initialize a valid
// CRITICAL_SECTION, but without .DebugInfo.
const DWORD version = GetVersion();
const DWORD major_version = LOBYTE(LOWORD(version));
const DWORD minor_version = HIBYTE(LOWORD(version));
const bool win7_or_lower =
major_version < 6 || (major_version == 6 && minor_version <= 1);
if (win7_or_lower) {
InitializeCriticalSection(critical_section);
return true;
}
return CrashpadInitializeCriticalSectionEx(
critical_section, 0, RTL_CRITICAL_SECTION_FLAG_FORCE_DEBUG_INFO);
}
} // namespace crashpad

View File

@ -0,0 +1,34 @@
// 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_UTIL_WIN_CRITICAL_SECTION_WITH_DEBUG_INFO_H_
#define CRASHPAD_UTIL_WIN_CRITICAL_SECTION_WITH_DEBUG_INFO_H_
#include <windows.h>
namespace crashpad {
//! \brief Equivalent to `InitializeCritialSection()`, but attempts to allocate
//! with a valid `.DebugInfo` field on versions of Windows where it's
//! possible to do so.
//!
//! \return `true` on success, or `false` on failure with a message logged.
//! Success means that the critical section was successfully initialized,
//! but it does not necessarily have a valid `.DebugInfo` field.
bool InitializeCriticalSectionWithDebugInfoIfPossible(
CRITICAL_SECTION* critical_section);
} // namespace crashpad
#endif // CRASHPAD_UTIL_WIN_CRITICAL_SECTION_WITH_DEBUG_INFO_H_

View File

@ -0,0 +1,34 @@
// 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/critical_section_with_debug_info.h"
#include "gtest/gtest.h"
namespace crashpad {
namespace test {
namespace {
TEST(CriticalSectionWithDebugInfo, CriticalSectionWithDebugInfo) {
CRITICAL_SECTION critical_section;
ASSERT_TRUE(
InitializeCriticalSectionWithDebugInfoIfPossible(&critical_section));
EnterCriticalSection(&critical_section);
LeaveCriticalSection(&critical_section);
DeleteCriticalSection(&critical_section);
}
} // namespace
} // namespace test
} // namespace crashpad

View File

@ -105,6 +105,7 @@ class ClientData {
ScopedKernelHANDLE process, ScopedKernelHANDLE process,
WinVMAddress crash_exception_information_address, WinVMAddress crash_exception_information_address,
WinVMAddress non_crash_exception_information_address, WinVMAddress non_crash_exception_information_address,
WinVMAddress debug_critical_section_address,
WAITORTIMERCALLBACK crash_dump_request_callback, WAITORTIMERCALLBACK crash_dump_request_callback,
WAITORTIMERCALLBACK non_crash_dump_request_callback, WAITORTIMERCALLBACK non_crash_dump_request_callback,
WAITORTIMERCALLBACK process_end_callback) WAITORTIMERCALLBACK process_end_callback)
@ -124,7 +125,8 @@ class ClientData {
crash_exception_information_address_( crash_exception_information_address_(
crash_exception_information_address), crash_exception_information_address),
non_crash_exception_information_address_( non_crash_exception_information_address_(
non_crash_exception_information_address) { non_crash_exception_information_address),
debug_critical_section_address_(debug_critical_section_address) {
RegisterThreadPoolWaits(crash_dump_request_callback, RegisterThreadPoolWaits(crash_dump_request_callback,
non_crash_dump_request_callback, non_crash_dump_request_callback,
process_end_callback); process_end_callback);
@ -155,6 +157,9 @@ class ClientData {
WinVMAddress non_crash_exception_information_address() const { WinVMAddress non_crash_exception_information_address() const {
return non_crash_exception_information_address_; return non_crash_exception_information_address_;
} }
WinVMAddress debug_critical_section_address() const {
return debug_critical_section_address_;
}
HANDLE process() const { return process_.get(); } HANDLE process() const { return process_.get(); }
private: private:
@ -219,6 +224,7 @@ class ClientData {
ScopedKernelHANDLE process_; ScopedKernelHANDLE process_;
WinVMAddress crash_exception_information_address_; WinVMAddress crash_exception_information_address_;
WinVMAddress non_crash_exception_information_address_; WinVMAddress non_crash_exception_information_address_;
WinVMAddress debug_critical_section_address_;
DISALLOW_COPY_AND_ASSIGN(ClientData); DISALLOW_COPY_AND_ASSIGN(ClientData);
}; };
@ -412,6 +418,7 @@ bool ExceptionHandlerServer::ServiceClientConnection(
ScopedKernelHANDLE(client_process), ScopedKernelHANDLE(client_process),
message.registration.crash_exception_information, message.registration.crash_exception_information,
message.registration.non_crash_exception_information, message.registration.non_crash_exception_information,
message.registration.critical_section_address,
&OnCrashDumpEvent, &OnCrashDumpEvent,
&OnNonCrashDumpEvent, &OnNonCrashDumpEvent,
&OnProcessEnd); &OnProcessEnd);
@ -465,7 +472,9 @@ void __stdcall ExceptionHandlerServer::OnCrashDumpEvent(void* ctx, BOOLEAN) {
// Capture the exception. // Capture the exception.
unsigned int exit_code = client->delegate()->ExceptionHandlerServerException( unsigned int exit_code = client->delegate()->ExceptionHandlerServerException(
client->process(), client->crash_exception_information_address()); client->process(),
client->crash_exception_information_address(),
client->debug_critical_section_address());
TerminateProcess(client->process(), exit_code); TerminateProcess(client->process(), exit_code);
} }
@ -478,7 +487,9 @@ void __stdcall ExceptionHandlerServer::OnNonCrashDumpEvent(void* ctx, BOOLEAN) {
// Capture the exception. // Capture the exception.
client->delegate()->ExceptionHandlerServerException( client->delegate()->ExceptionHandlerServerException(
client->process(), client->non_crash_exception_information_address()); client->process(),
client->non_crash_exception_information_address(),
client->debug_critical_section_address());
bool result = SetEvent(client->non_crash_dump_completed_event()); bool result = SetEvent(client->non_crash_dump_completed_event());
PLOG_IF(ERROR, !result) << "SetEvent"; PLOG_IF(ERROR, !result) << "SetEvent";

View File

@ -49,11 +49,15 @@ class ExceptionHandlerServer {
//! lifetime of this handle is not passed to the delegate. //! lifetime of this handle is not passed to the delegate.
//! \param[in] exception_information_address The address in the client's //! \param[in] exception_information_address The address in the client's
//! address space of an ExceptionInformation structure. //! address space of an ExceptionInformation structure.
//! \param[in] debug_critical_section_address The address in the client's
//! address space of a `CRITICAL_SECTION` allocated with a valid
//! `.DebugInfo` field, or `0` if unavailable.
//! \return The exit code that should be used when terminating the client //! \return The exit code that should be used when terminating the client
//! process. //! process.
virtual unsigned int ExceptionHandlerServerException( virtual unsigned int ExceptionHandlerServerException(
HANDLE process, HANDLE process,
WinVMAddress exception_information_address) = 0; WinVMAddress exception_information_address,
WinVMAddress debug_critical_section_address) = 0;
}; };
//! \brief Constructs the exception handling server. //! \brief Constructs the exception handling server.

View File

@ -64,7 +64,8 @@ class TestDelegate : public ExceptionHandlerServer::Delegate {
} }
unsigned int ExceptionHandlerServerException( unsigned int ExceptionHandlerServerException(
HANDLE process, HANDLE process,
WinVMAddress exception_information_address) override { WinVMAddress exception_information_address,
WinVMAddress debug_critical_section_address) override {
return 0; return 0;
} }

View File

@ -72,6 +72,22 @@ NTSTATUS NtOpenThread(PHANDLE thread_handle,
static_cast<const void*>(client_id)); static_cast<const void*>(client_id));
} }
NTSTATUS NtQueryObject(HANDLE handle,
OBJECT_INFORMATION_CLASS object_information_class,
void* object_information,
ULONG object_information_length,
ULONG* return_length) {
static decltype(::NtQueryObject)* nt_query_object =
reinterpret_cast<decltype(::NtQueryObject)*>(
GetProcAddress(LoadLibrary(L"ntdll.dll"), "NtQueryObject"));
DCHECK(nt_query_object);
return nt_query_object(handle,
object_information_class,
object_information,
object_information_length,
return_length);
}
// Explicit instantiations with the only 2 valid template arguments to avoid // Explicit instantiations with the only 2 valid template arguments to avoid
// putting the body of the function in the header. // putting the body of the function in the header.
template NTSTATUS NtOpenThread<process_types::internal::Traits32>( template NTSTATUS NtOpenThread<process_types::internal::Traits32>(

View File

@ -27,6 +27,9 @@ namespace crashpad {
// winternal.h defines THREADINFOCLASS, but not all members. // winternal.h defines THREADINFOCLASS, but not all members.
enum { ThreadBasicInformation = 0 }; enum { ThreadBasicInformation = 0 };
// winternal.h defines SYSTEM_INFORMATION_CLASS, but not all members.
enum { SystemExtendedHandleInformation = 64 };
NTSTATUS NtQuerySystemInformation( NTSTATUS NtQuerySystemInformation(
SYSTEM_INFORMATION_CLASS system_information_class, SYSTEM_INFORMATION_CLASS system_information_class,
PVOID system_information, PVOID system_information,
@ -45,4 +48,10 @@ NTSTATUS NtOpenThread(PHANDLE thread_handle,
POBJECT_ATTRIBUTES object_attributes, POBJECT_ATTRIBUTES object_attributes,
const process_types::CLIENT_ID<Traits>* client_id); const process_types::CLIENT_ID<Traits>* client_id);
NTSTATUS NtQueryObject(HANDLE handle,
OBJECT_INFORMATION_CLASS object_information_class,
void* object_information,
ULONG object_information_length,
ULONG* return_length);
} // namespace crashpad } // namespace crashpad

View File

@ -20,12 +20,15 @@
#include <limits> #include <limits>
#include "base/logging.h" #include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "base/strings/stringprintf.h" #include "base/strings/stringprintf.h"
#include "base/template_util.h" #include "base/template_util.h"
#include "build/build_config.h" #include "build/build_config.h"
#include "util/numeric/safe_assignment.h" #include "util/numeric/safe_assignment.h"
#include "util/win/nt_internals.h"
#include "util/win/ntstatus_logging.h" #include "util/win/ntstatus_logging.h"
#include "util/win/process_structs.h" #include "util/win/process_structs.h"
#include "util/win/scoped_handle.h"
namespace crashpad { namespace crashpad {
@ -127,6 +130,34 @@ MEMORY_BASIC_INFORMATION64 MemoryBasicInformationToMemoryBasicInformation64(
return mbi64; return mbi64;
} }
// NtQueryObject with a retry for size mismatch as well as a minimum size to
// retrieve (and expect).
scoped_ptr<uint8_t[]> QueryObject(
HANDLE handle,
OBJECT_INFORMATION_CLASS object_information_class,
ULONG minimum_size) {
ULONG size = minimum_size;
ULONG return_length;
scoped_ptr<uint8_t[]> buffer(new uint8_t[size]);
NTSTATUS status = crashpad::NtQueryObject(
handle, object_information_class, buffer.get(), size, &return_length);
if (status == STATUS_INFO_LENGTH_MISMATCH) {
DCHECK_GT(return_length, size);
size = return_length;
buffer.reset(new uint8_t[size]);
status = crashpad::NtQueryObject(
handle, object_information_class, buffer.get(), size, &return_length);
}
if (!NT_SUCCESS(status)) {
NTSTATUS_LOG(ERROR, status) << "NtQueryObject";
return scoped_ptr<uint8_t[]>();
}
DCHECK_GE(return_length, minimum_size);
return buffer.Pass();
}
} // namespace } // namespace
template <class Traits> template <class Traits>
@ -314,20 +345,150 @@ bool ReadMemoryInfo(HANDLE process, bool is_64_bit, ProcessInfo* process_info) {
return true; return true;
} }
std::vector<ProcessInfo::Handle> ProcessInfo::BuildHandleVector(
HANDLE process) const {
ULONG buffer_size = 2 * 1024 * 1024;
scoped_ptr<uint8_t[]> buffer(new uint8_t[buffer_size]);
// Typically if the buffer were too small, STATUS_INFO_LENGTH_MISMATCH would
// return the correct size in the final argument, but it does not for
// SystemExtendedHandleInformation, so we loop and attempt larger sizes.
NTSTATUS status;
ULONG returned_length;
for (int tries = 0; tries < 5; ++tries) {
status = crashpad::NtQuerySystemInformation(
static_cast<SYSTEM_INFORMATION_CLASS>(SystemExtendedHandleInformation),
buffer.get(),
buffer_size,
&returned_length);
if (NT_SUCCESS(status) || status != STATUS_INFO_LENGTH_MISMATCH)
break;
buffer_size *= 2;
buffer.reset();
buffer.reset(new uint8_t[buffer_size]);
}
if (!NT_SUCCESS(status)) {
NTSTATUS_LOG(ERROR, status)
<< "NtQuerySystemInformation SystemExtendedHandleInformation";
return std::vector<Handle>();
}
const auto& system_handle_information_ex =
*reinterpret_cast<process_types::SYSTEM_HANDLE_INFORMATION_EX*>(
buffer.get());
DCHECK_LE(offsetof(process_types::SYSTEM_HANDLE_INFORMATION_EX, Handles) +
system_handle_information_ex.NumberOfHandles *
sizeof(system_handle_information_ex.Handles[0]),
returned_length);
std::vector<Handle> handles;
for (size_t i = 0; i < system_handle_information_ex.NumberOfHandles; ++i) {
const auto& handle = system_handle_information_ex.Handles[i];
if (handle.UniqueProcessId != process_id_)
continue;
Handle result_handle;
result_handle.handle =
static_cast<uint32_t>(reinterpret_cast<uintptr_t>(handle.HandleValue));
result_handle.attributes = handle.HandleAttributes;
result_handle.granted_access = handle.GrantedAccess;
// TODO(scottmg): Could special case for self.
HANDLE dup_handle;
if (DuplicateHandle(process,
reinterpret_cast<HANDLE>(handle.HandleValue),
GetCurrentProcess(),
&dup_handle,
0,
false,
DUPLICATE_SAME_ACCESS)) {
// Some handles cannot be duplicated, for example, handles of type
// EtwRegistration. If we fail to duplicate, then we can't gather any more
// information, but include the information that we do have already.
ScopedKernelHANDLE scoped_dup_handle(dup_handle);
scoped_ptr<uint8_t[]> object_basic_information_buffer =
QueryObject(dup_handle,
ObjectBasicInformation,
sizeof(PUBLIC_OBJECT_BASIC_INFORMATION));
if (object_basic_information_buffer) {
PUBLIC_OBJECT_BASIC_INFORMATION* object_basic_information =
reinterpret_cast<PUBLIC_OBJECT_BASIC_INFORMATION*>(
object_basic_information_buffer.get());
// The Attributes and GrantedAccess sometimes differ slightly between
// the data retrieved in SYSTEM_HANDLE_INFORMATION_EX and
// PUBLIC_OBJECT_TYPE_INFORMATION. We prefer the values in
// SYSTEM_HANDLE_INFORMATION_EX because they were retrieved from the
// target process, rather than on the duplicated handle, so don't use
// them here.
// Subtract one to account for our DuplicateHandle() and another for
// NtQueryObject() while the query was being executed.
DCHECK_GT(object_basic_information->PointerCount, 2u);
result_handle.pointer_count =
object_basic_information->PointerCount - 2;
// Subtract one to account for our DuplicateHandle().
DCHECK_GT(object_basic_information->HandleCount, 1u);
result_handle.handle_count = object_basic_information->HandleCount - 1;
}
scoped_ptr<uint8_t[]> object_type_information_buffer =
QueryObject(dup_handle,
ObjectTypeInformation,
sizeof(PUBLIC_OBJECT_TYPE_INFORMATION));
if (object_type_information_buffer) {
PUBLIC_OBJECT_TYPE_INFORMATION* object_type_information =
reinterpret_cast<PUBLIC_OBJECT_TYPE_INFORMATION*>(
object_type_information_buffer.get());
DCHECK_EQ(object_type_information->TypeName.Length %
sizeof(result_handle.type_name[0]),
0u);
result_handle.type_name =
std::wstring(object_type_information->TypeName.Buffer,
object_type_information->TypeName.Length /
sizeof(result_handle.type_name[0]));
}
}
handles.push_back(result_handle);
}
return handles;
}
ProcessInfo::Module::Module() : name(), dll_base(0), size(0), timestamp() { ProcessInfo::Module::Module() : name(), dll_base(0), size(0), timestamp() {
} }
ProcessInfo::Module::~Module() { ProcessInfo::Module::~Module() {
} }
ProcessInfo::Handle::Handle()
: type_name(),
handle(0),
attributes(0),
granted_access(0),
pointer_count(0),
handle_count(0) {
}
ProcessInfo::Handle::~Handle() {
}
ProcessInfo::ProcessInfo() ProcessInfo::ProcessInfo()
: process_id_(), : process_id_(),
inherited_from_process_id_(), inherited_from_process_id_(),
process_(),
command_line_(), command_line_(),
peb_address_(0), peb_address_(0),
peb_size_(0), peb_size_(0),
modules_(), modules_(),
memory_info_(), memory_info_(),
handles_(),
is_64_bit_(false), is_64_bit_(false),
is_wow64_(false), is_wow64_(false),
initialized_() { initialized_() {
@ -339,6 +500,8 @@ ProcessInfo::~ProcessInfo() {
bool ProcessInfo::Initialize(HANDLE process) { bool ProcessInfo::Initialize(HANDLE process) {
INITIALIZATION_STATE_SET_INITIALIZING(initialized_); INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
process_ = process;
is_wow64_ = IsProcessWow64(process); is_wow64_ = IsProcessWow64(process);
if (is_wow64_) { if (is_wow64_) {
@ -439,6 +602,13 @@ ProcessInfo::GetReadableRanges(
return GetReadableRangesOfMemoryMap(range, MemoryInfo()); return GetReadableRangesOfMemoryMap(range, MemoryInfo());
} }
const std::vector<ProcessInfo::Handle>& ProcessInfo::Handles() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
if (handles_.empty())
handles_ = BuildHandleVector(process_);
return handles_;
}
std::vector<CheckedRange<WinVMAddress, WinVMSize>> GetReadableRangesOfMemoryMap( std::vector<CheckedRange<WinVMAddress, WinVMSize>> GetReadableRangesOfMemoryMap(
const CheckedRange<WinVMAddress, WinVMSize>& range, const CheckedRange<WinVMAddress, WinVMSize>& range,
const std::vector<MEMORY_BASIC_INFORMATION64>& memory_info) { const std::vector<MEMORY_BASIC_INFORMATION64>& memory_info) {

View File

@ -50,6 +50,39 @@ class ProcessInfo {
time_t timestamp; time_t timestamp;
}; };
struct Handle {
Handle();
~Handle();
//! \brief A string representation of the handle's type.
std::wstring type_name;
//! \brief The handle's value.
//!
//! See https://msdn.microsoft.com/en-us/library/windows/desktop/aa384203 on
//! 32 bits being the correct size for `HANDLE`s, even on Windows x64.
uint32_t handle;
//! \brief The attributes for the handle, e.g. `OBJ_INHERIT`,
//! `OBJ_CASE_INSENSITIVE`, etc.
uint32_t attributes;
//! \brief The `ACCESS_MASK` for the handle in this process.
//!
//! See
//! http://blogs.msdn.com/b/openspecification/archive/2010/04/01/about-the-access-mask-structure.aspx
//! for more information.
uint32_t granted_access;
//! \brief The number of kernel references to the object that this handle
//! refers to.
uint32_t pointer_count;
//! \brief The number of open handles to the object that this handle refers
//! to.
uint32_t handle_count;
};
ProcessInfo(); ProcessInfo();
~ProcessInfo(); ~ProcessInfo();
@ -106,6 +139,9 @@ class ProcessInfo {
std::vector<CheckedRange<WinVMAddress, WinVMSize>> GetReadableRanges( std::vector<CheckedRange<WinVMAddress, WinVMSize>> GetReadableRanges(
const CheckedRange<WinVMAddress, WinVMSize>& range) const; const CheckedRange<WinVMAddress, WinVMSize>& range) const;
//! \brief Retrieves information about open handles in the target process.
const std::vector<Handle>& Handles() const;
private: private:
template <class Traits> template <class Traits>
friend bool GetProcessBasicInformation(HANDLE process, friend bool GetProcessBasicInformation(HANDLE process,
@ -122,13 +158,21 @@ class ProcessInfo {
bool is_64_bit, bool is_64_bit,
ProcessInfo* process_info); ProcessInfo* process_info);
std::vector<Handle> BuildHandleVector(HANDLE process) const;
pid_t process_id_; pid_t process_id_;
pid_t inherited_from_process_id_; pid_t inherited_from_process_id_;
HANDLE process_;
std::wstring command_line_; std::wstring command_line_;
WinVMAddress peb_address_; WinVMAddress peb_address_;
WinVMSize peb_size_; WinVMSize peb_size_;
std::vector<Module> modules_; std::vector<Module> modules_;
std::vector<MEMORY_BASIC_INFORMATION64> memory_info_; std::vector<MEMORY_BASIC_INFORMATION64> memory_info_;
// Handles() is logically const, but updates this member on first retrieval.
// See https://code.google.com/p/crashpad/issues/detail?id=9.
mutable std::vector<Handle> handles_;
bool is_64_bit_; bool is_64_bit_;
bool is_wow64_; bool is_wow64_;
InitializationStateDcheck initialized_; InitializationStateDcheck initialized_;

View File

@ -20,8 +20,13 @@
#include "base/files/file_path.h" #include "base/files/file_path.h"
#include "base/memory/scoped_ptr.h" #include "base/memory/scoped_ptr.h"
#include "base/rand_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h" #include "build/build_config.h"
#include "gtest/gtest.h" #include "gtest/gtest.h"
#include "test/errors.h"
#include "test/scoped_temp_dir.h"
#include "test/paths.h" #include "test/paths.h"
#include "test/win/child_launcher.h" #include "test/win/child_launcher.h"
#include "util/file/file_io.h" #include "util/file/file_io.h"
@ -510,6 +515,114 @@ TEST(ProcessInfo, ReadableRanges) {
&bytes_read)); &bytes_read));
} }
struct ScopedRegistryKeyCloseTraits {
static HKEY InvalidValue() {
return nullptr;
}
static void Free(HKEY key) {
RegCloseKey(key);
}
};
using ScopedRegistryKey =
base::ScopedGeneric<HKEY, ScopedRegistryKeyCloseTraits>;
TEST(ProcessInfo, Handles) {
ScopedTempDir temp_dir;
ScopedFileHandle file(LoggingOpenFileForWrite(
temp_dir.path().Append(FILE_PATH_LITERAL("test_file")),
FileWriteMode::kTruncateOrCreate,
FilePermissions::kWorldReadable));
ASSERT_TRUE(file.is_valid());
SECURITY_ATTRIBUTES security_attributes = {0};
security_attributes.bInheritHandle = true;
ScopedFileHandle inherited_file(CreateFile(L"CONOUT$",
GENERIC_WRITE,
0,
&security_attributes,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
nullptr));
ASSERT_TRUE(inherited_file.is_valid());
HKEY key;
ASSERT_EQ(ERROR_SUCCESS,
RegOpenKeyEx(
HKEY_CURRENT_USER, L"SOFTWARE\\Microsoft", 0, KEY_READ, &key));
ScopedRegistryKey scoped_key(key);
ASSERT_TRUE(scoped_key.is_valid());
std::wstring mapping_name =
base::UTF8ToUTF16(base::StringPrintf("Local\\test_mapping_%d_%I64x",
GetCurrentProcessId(),
base::RandUint64()));
ScopedKernelHANDLE mapping(CreateFileMapping(INVALID_HANDLE_VALUE,
nullptr,
PAGE_READWRITE,
0,
1024,
mapping_name.c_str()));
ASSERT_TRUE(mapping.is_valid()) << ErrorMessage("CreateFileMapping");
ProcessInfo info;
info.Initialize(GetCurrentProcess());
bool found_file_handle = false;
bool found_inherited_file_handle = false;
bool found_key_handle = false;
bool found_mapping_handle = false;
for (auto handle : info.Handles()) {
if (reinterpret_cast<uint32_t>(file.get()) == handle.handle) {
EXPECT_FALSE(found_file_handle);
found_file_handle = true;
EXPECT_EQ(L"File", handle.type_name);
EXPECT_EQ(1, handle.handle_count);
EXPECT_NE(0u, handle.pointer_count);
EXPECT_EQ(STANDARD_RIGHTS_READ | STANDARD_RIGHTS_WRITE | SYNCHRONIZE,
handle.granted_access & STANDARD_RIGHTS_ALL);
EXPECT_EQ(0, handle.attributes);
}
if (reinterpret_cast<uint32_t>(inherited_file.get()) == handle.handle) {
EXPECT_FALSE(found_inherited_file_handle);
found_inherited_file_handle = true;
EXPECT_EQ(L"File", handle.type_name);
EXPECT_EQ(1, handle.handle_count);
EXPECT_NE(0u, handle.pointer_count);
EXPECT_EQ(STANDARD_RIGHTS_READ | STANDARD_RIGHTS_WRITE | SYNCHRONIZE,
handle.granted_access & STANDARD_RIGHTS_ALL);
// OBJ_INHERIT from ntdef.h, but including that conflicts with other
// headers.
const int kObjInherit = 0x2;
EXPECT_EQ(kObjInherit, handle.attributes);
}
if (reinterpret_cast<uint32_t>(scoped_key.get()) == handle.handle) {
EXPECT_FALSE(found_key_handle);
found_key_handle = true;
EXPECT_EQ(L"Key", handle.type_name);
EXPECT_EQ(1, handle.handle_count);
EXPECT_NE(0u, handle.pointer_count);
EXPECT_EQ(STANDARD_RIGHTS_READ,
handle.granted_access & STANDARD_RIGHTS_ALL);
EXPECT_EQ(0, handle.attributes);
}
if (reinterpret_cast<uint32_t>(mapping.get()) == handle.handle) {
EXPECT_FALSE(found_mapping_handle);
found_mapping_handle = true;
EXPECT_EQ(L"Section", handle.type_name);
EXPECT_EQ(1, handle.handle_count);
EXPECT_NE(0u, handle.pointer_count);
EXPECT_EQ(DELETE | READ_CONTROL | WRITE_DAC | WRITE_OWNER |
STANDARD_RIGHTS_READ | STANDARD_RIGHTS_WRITE,
handle.granted_access & STANDARD_RIGHTS_ALL);
EXPECT_EQ(0, handle.attributes);
}
}
EXPECT_TRUE(found_file_handle);
EXPECT_TRUE(found_key_handle);
EXPECT_TRUE(found_mapping_handle);
}
} // namespace } // namespace
} // namespace test } // namespace test
} // namespace crashpad } // namespace crashpad

View File

@ -232,6 +232,7 @@ struct PEB {
typename Traits::Pointer UnicodeCaseTableData; typename Traits::Pointer UnicodeCaseTableData;
DWORD NumberOfProcessors; DWORD NumberOfProcessors;
DWORD NtGlobalFlag; DWORD NtGlobalFlag;
DWORD alignment_for_x86;
LARGE_INTEGER CriticalSectionTimeout; LARGE_INTEGER CriticalSectionTimeout;
typename Traits::UnsignedIntegral HeapSegmentReserve; typename Traits::UnsignedIntegral HeapSegmentReserve;
typename Traits::UnsignedIntegral HeapSegmentCommit; typename Traits::UnsignedIntegral HeapSegmentCommit;
@ -454,6 +455,52 @@ struct EXCEPTION_POINTERS {
using EXCEPTION_POINTERS32 = EXCEPTION_POINTERS<internal::Traits32>; using EXCEPTION_POINTERS32 = EXCEPTION_POINTERS<internal::Traits32>;
using EXCEPTION_POINTERS64 = EXCEPTION_POINTERS<internal::Traits64>; using EXCEPTION_POINTERS64 = EXCEPTION_POINTERS<internal::Traits64>;
// This is defined in winnt.h, but not for cross-bitness.
template <class Traits>
struct RTL_CRITICAL_SECTION {
typename Traits::Pointer DebugInfo;
LONG LockCount;
LONG RecursionCount;
typename Traits::Pointer OwningThread;
typename Traits::Pointer LockSemaphore;
typename Traits::UnsignedIntegral SpinCount;
};
template <class Traits>
struct RTL_CRITICAL_SECTION_DEBUG {
union {
struct {
WORD Type;
WORD CreatorBackTraceIndex;
};
typename Traits::Pad alignment_for_x64;
};
typename Traits::Pointer CriticalSection;
LIST_ENTRY<Traits> ProcessLocksList;
DWORD EntryCount;
DWORD ContentionCount;
DWORD Flags;
WORD CreatorBackTraceIndexHigh;
WORD SpareWORD;
};
struct SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX {
void* Object;
ULONG_PTR UniqueProcessId;
HANDLE HandleValue;
ULONG GrantedAccess;
USHORT CreatorBackTraceIndex;
USHORT ObjectTypeIndex;
ULONG HandleAttributes;
ULONG Reserved;
};
struct SYSTEM_HANDLE_INFORMATION_EX {
ULONG_PTR NumberOfHandles;
ULONG_PTR Reserved;
SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX Handles[1];
};
#pragma pack(pop) #pragma pack(pop)
//! \} //! \}

View File

@ -49,15 +49,23 @@ struct RegistrationRequest {
//! \brief The PID of the client process. //! \brief The PID of the client process.
DWORD client_process_id; DWORD client_process_id;
//! \brief The address, in the client process address space, of an //! \brief The address, in the client process's address space, of an
//! ExceptionInformation structure, used when handling a crash dump //! ExceptionInformation structure, used when handling a crash dump
//! request. //! request.
WinVMAddress crash_exception_information; WinVMAddress crash_exception_information;
//! \brief The address, in the client process address space, of an //! \brief The address, in the client process's address space, of an
//! ExceptionInformation structure, used when handling a non-crashing dump //! ExceptionInformation structure, used when handling a non-crashing dump
//! request. //! request.
WinVMAddress non_crash_exception_information; WinVMAddress non_crash_exception_information;
//! \brief The address, in the client process's address space, of a
//! `CRITICAL_SECTION` allocated with a valid .DebugInfo field. This can
//! be accomplished by using
//! InitializeCriticalSectionWithDebugInfoIfPossible() or equivalent. This
//! value can be `0`, however then limited lock data will be available in
//! minidumps.
WinVMAddress critical_section_address;
}; };
//! \brief A message only sent to the server by itself to trigger shutdown. //! \brief A message only sent to the server by itself to trigger shutdown.