mirror of
https://github.com/chromium/crashpad.git
synced 2025-03-20 18:53:47 +00:00
Merge master f64ef73d8bba into doc
This commit is contained in:
commit
5e9c0c67d7
2
DEPS
2
DEPS
@ -25,7 +25,7 @@ deps = {
|
||||
'01528c7244837168a1c80f06ff60fa5a9793c824',
|
||||
'crashpad/third_party/mini_chromium/mini_chromium':
|
||||
Var('chromium_git') + '/chromium/mini_chromium@' +
|
||||
'a43fee120b10ed71df4e55a370948ca461d78232',
|
||||
'7c5b0c1ab44a4264b02f4c825a5b73d9173253e4',
|
||||
'buildtools':
|
||||
Var('chromium_git') + '/chromium/buildtools.git@' +
|
||||
'c2f259809d5ede3275df5ea0842f0431990c4f98',
|
||||
|
42
build/crashpad_dependencies.gypi
Normal file
42
build/crashpad_dependencies.gypi
Normal file
@ -0,0 +1,42 @@
|
||||
# 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.
|
||||
|
||||
{
|
||||
# Crashpad can obtain dependencies in three different ways, directed by the
|
||||
# crashpad_standalone GYP variable. It may have these values:
|
||||
# standalone
|
||||
# A “standalone” Crashpad build, where the dependencies are in the
|
||||
# Crashpad tree. third_party/mini_chromium and third_party/gtest provide
|
||||
# the base and gtest libraries.
|
||||
# chromium
|
||||
# An in-Chromium build, where Crashpad is within the Chromium tree.
|
||||
# Chromium provides its own base library and its copy of the gtest
|
||||
# library.
|
||||
# external
|
||||
# A build with external dependencies. mini_chromium provides the base
|
||||
# library, but it’s located outside of the Crashpad tree, as is gtest.
|
||||
#
|
||||
# In order for Crashpad’s .gyp files to reference the correct versions
|
||||
# depending on how dependencies are being provided, include this .gypi file
|
||||
# and reference the crashpad_dependencies variable.
|
||||
|
||||
'variables': {
|
||||
# When building as a standalone project or with external dependencies,
|
||||
# build/gyp_crashpad.py sets crashpad_dependencies to "standalone" or
|
||||
# "external", and this % assignment will not override it. The variable will
|
||||
# not be set by anything else when building as part of Chromium, so in that
|
||||
# case, this will define it with value "chromium".
|
||||
'crashpad_dependencies%': 'chromium',
|
||||
},
|
||||
}
|
@ -1,43 +0,0 @@
|
||||
# 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.
|
||||
|
||||
{
|
||||
# Crashpad can build as a standalone project or as part of Chromium. When
|
||||
# building as a standalone project, it uses mini_chromium to provide the base
|
||||
# library, and uses its own copy of gtest in third_party. When building as
|
||||
# part of Chromium, it uses Chromium’s base library and copy of gtest. In
|
||||
# order for Crashpad’s .gyp files to reference the correct versions depending
|
||||
# on whether building standalone or as a part of Chromium, include this .gypi
|
||||
# file and reference the crashpad_in_chromium variable.
|
||||
|
||||
'variables': {
|
||||
'variables': {
|
||||
# When building as a standalone project, build/gyp_crashpad.py sets
|
||||
# crashpad_standalone to 1, and this % assignment will not override it.
|
||||
# The variable will not be set when building as part of Chromium, so in
|
||||
# that case, this will define it with value 0.
|
||||
'crashpad_standalone%': 0,
|
||||
},
|
||||
|
||||
'conditions': [
|
||||
['crashpad_standalone!=0', {
|
||||
'crashpad_in_chromium': 0,
|
||||
}, {
|
||||
'crashpad_in_chromium': 1,
|
||||
}],
|
||||
],
|
||||
|
||||
'crashpad_in_chromium': '<(crashpad_in_chromium)',
|
||||
},
|
||||
}
|
@ -17,10 +17,38 @@
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
||||
def ChooseDependencyPath(local_path, external_path):
|
||||
"""Chooses between a dependency located at local path and an external path.
|
||||
|
||||
The local path, used in standalone builds, is preferred. If it is not present
|
||||
but the external path is, the external path will be used. If neither path is
|
||||
present, the local path will be used, so that error messages uniformly refer
|
||||
to the local path.
|
||||
|
||||
Args:
|
||||
local_path: The preferred local path to use for a standalone build.
|
||||
external_path: The external path to fall back to.
|
||||
|
||||
Returns:
|
||||
A 2-tuple. The first element is 'standalone' or 'external', depending on
|
||||
whether local_path or external_path was chosen. The second element is the
|
||||
chosen path.
|
||||
"""
|
||||
if os.path.exists(local_path) or not os.path.exists(external_path):
|
||||
return ('standalone', local_path)
|
||||
return ('external', external_path)
|
||||
|
||||
|
||||
script_dir = os.path.dirname(__file__)
|
||||
crashpad_dir = os.path.dirname(script_dir) if script_dir is not '' else '..'
|
||||
sys.path.insert(
|
||||
0, os.path.join(crashpad_dir, 'third_party', 'gyp', 'gyp', 'pylib'))
|
||||
crashpad_dir = (os.path.dirname(script_dir) if script_dir not in ('', os.curdir)
|
||||
else os.pardir)
|
||||
|
||||
sys.path.insert(0,
|
||||
ChooseDependencyPath(os.path.join(crashpad_dir, 'third_party', 'gyp', 'gyp',
|
||||
'pylib'),
|
||||
os.path.join(crashpad_dir, os.pardir, os.pardir, 'gyp',
|
||||
'pylib'))[1])
|
||||
|
||||
import gyp
|
||||
|
||||
@ -29,12 +57,15 @@ def main(args):
|
||||
if 'GYP_GENERATORS' not in os.environ:
|
||||
os.environ['GYP_GENERATORS'] = 'ninja'
|
||||
|
||||
crashpad_dir_or_dot = crashpad_dir if crashpad_dir is not '' else '.'
|
||||
crashpad_dir_or_dot = crashpad_dir if crashpad_dir is not '' else os.curdir
|
||||
|
||||
args.extend(['-D', 'crashpad_standalone=1'])
|
||||
args.extend(['--include',
|
||||
os.path.join(crashpad_dir, 'third_party', 'mini_chromium',
|
||||
'mini_chromium', 'build', 'common.gypi')])
|
||||
(dependencies, mini_chromium_dir) = (ChooseDependencyPath(
|
||||
os.path.join(crashpad_dir, 'third_party', 'mini_chromium',
|
||||
'mini_chromium', 'build', 'common.gypi'),
|
||||
os.path.join(crashpad_dir, os.pardir, os.pardir, 'mini_chromium',
|
||||
'mini_chromium', 'build', 'common.gypi')))
|
||||
args.extend(['-D', 'crashpad_dependencies=%s' % dependencies])
|
||||
args.extend(['--include', mini_chromium_dir])
|
||||
args.extend(['--depth', crashpad_dir_or_dot])
|
||||
args.append(os.path.join(crashpad_dir, 'crashpad.gyp'))
|
||||
|
||||
|
@ -46,6 +46,8 @@
|
||||
'settings.h',
|
||||
'simple_string_dictionary.cc',
|
||||
'simple_string_dictionary.h',
|
||||
'simple_address_range_bag.cc',
|
||||
'simple_address_range_bag.h',
|
||||
'simulate_crash.h',
|
||||
'simulate_crash_mac.cc',
|
||||
'simulate_crash_mac.h',
|
||||
|
@ -39,6 +39,7 @@
|
||||
'crashpad_client_win_test.cc',
|
||||
'prune_crash_reports_test.cc',
|
||||
'settings_test.cc',
|
||||
'simple_address_range_bag_test.cc',
|
||||
'simple_string_dictionary_test.cc',
|
||||
'simulate_crash_mac_test.cc',
|
||||
],
|
||||
|
@ -98,14 +98,29 @@ CrashpadInfo::CrashpadInfo()
|
||||
version_(kCrashpadInfoVersion),
|
||||
crashpad_handler_behavior_(TriState::kUnset),
|
||||
system_crash_reporter_forwarding_(TriState::kUnset),
|
||||
gather_indirectly_referenced_memory_(TriState::kUnset),
|
||||
padding_0_(0),
|
||||
simple_annotations_(nullptr)
|
||||
extra_memory_ranges_(nullptr),
|
||||
simple_annotations_(nullptr),
|
||||
user_data_minidump_stream_head_(nullptr)
|
||||
#if !defined(NDEBUG) && defined(OS_WIN)
|
||||
,
|
||||
invalid_read_detection_(0xbadc0de)
|
||||
#endif
|
||||
{
|
||||
}
|
||||
|
||||
void CrashpadInfo::AddUserDataMinidumpStream(uint32_t stream_type,
|
||||
const void* data,
|
||||
size_t size) {
|
||||
auto to_be_added = new internal::UserDataMinidumpStreamListEntry();
|
||||
to_be_added->next = base::checked_cast<uint64_t>(
|
||||
reinterpret_cast<uintptr_t>(user_data_minidump_stream_head_));
|
||||
to_be_added->stream_type = stream_type;
|
||||
to_be_added->base_address =
|
||||
base::checked_cast<uint64_t>(reinterpret_cast<uintptr_t>(data));
|
||||
to_be_added->size = base::checked_cast<uint64_t>(size);
|
||||
user_data_minidump_stream_head_ = to_be_added;
|
||||
}
|
||||
|
||||
} // namespace crashpad
|
||||
|
@ -19,6 +19,7 @@
|
||||
|
||||
#include "base/macros.h"
|
||||
#include "build/build_config.h"
|
||||
#include "client/simple_address_range_bag.h"
|
||||
#include "client/simple_string_dictionary.h"
|
||||
#include "util/misc/tri_state.h"
|
||||
|
||||
@ -28,6 +29,29 @@
|
||||
|
||||
namespace crashpad {
|
||||
|
||||
namespace internal {
|
||||
|
||||
//! \brief A linked list of blocks representing custom streams in the minidump,
|
||||
//! with addresses (and size) stored as uint64_t to simplify reading from
|
||||
//! the handler process.
|
||||
struct UserDataMinidumpStreamListEntry {
|
||||
//! \brief The address of the next entry in the linked list.
|
||||
uint64_t next;
|
||||
|
||||
//! \brief The base address of the memory block in the target process' address
|
||||
//! space that represents the user data stream.
|
||||
uint64_t base_address;
|
||||
|
||||
//! \brief The size of memory block in the target process' address space that
|
||||
//! represents the user data stream.
|
||||
uint64_t size;
|
||||
|
||||
//! \brief The stream type identifier.
|
||||
uint32_t stream_type;
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
|
||||
//! \brief A structure that can be used by a Crashpad-enabled program to
|
||||
//! provide information to the Crashpad crash handler.
|
||||
//!
|
||||
@ -42,6 +66,22 @@ struct CrashpadInfo {
|
||||
|
||||
CrashpadInfo();
|
||||
|
||||
//! \brief Sets the bag of extra memory ranges to be included in the snapshot.
|
||||
//!
|
||||
//! Extra memory ranges may exist in \a address_range_bag at the time that
|
||||
//! this method is called, or they may be added, removed, or modified in \a
|
||||
//! address_range_bag after this method is called.
|
||||
//!
|
||||
//! TODO(scottmg) This is currently only supported on Windows.
|
||||
//!
|
||||
//! \param[in] address_range_bag A bag of address ranges. The CrashpadInfo
|
||||
//! object does not take ownership of the SimpleAddressRangeBag object.
|
||||
//! It is the caller’s responsibility to ensure that this pointer remains
|
||||
//! valid while it is in effect for a CrashpadInfo object.
|
||||
void set_extra_memory_ranges(SimpleAddressRangeBag* address_range_bag) {
|
||||
extra_memory_ranges_ = address_range_bag;
|
||||
}
|
||||
|
||||
//! \brief Sets the simple annotations dictionary.
|
||||
//!
|
||||
//! Simple annotations set on a CrashpadInfo structure are interpreted by
|
||||
@ -72,7 +112,7 @@ struct CrashpadInfo {
|
||||
//! that it has been disabled.
|
||||
//!
|
||||
//! The Crashpad handler should not normally be disabled. More commonly, it
|
||||
//! is appropraite to disable crash report upload by calling
|
||||
//! is appropriate to disable crash report upload by calling
|
||||
//! Settings::SetUploadsEnabled().
|
||||
void set_crashpad_handler_behavior(TriState crashpad_handler_behavior) {
|
||||
crashpad_handler_behavior_ = crashpad_handler_behavior;
|
||||
@ -97,6 +137,43 @@ struct CrashpadInfo {
|
||||
system_crash_reporter_forwarding_ = system_crash_reporter_forwarding;
|
||||
}
|
||||
|
||||
//! \brief Enables or disables Crashpad capturing indirectly referenced memory
|
||||
//! in the minidump.
|
||||
//!
|
||||
//! When handling an exception, the Crashpad handler will scan all modules in
|
||||
//! a process. The first one that has a CrashpadInfo structure populated with
|
||||
//! a value other than #kUnset for this field will dictate whether the extra
|
||||
//! memory is captured.
|
||||
//!
|
||||
//! This causes Crashpad to include pages of data referenced by locals or
|
||||
//! other stack memory. Turning this on can increase the size of the minidump
|
||||
//! significantly.
|
||||
void set_gather_indirectly_referenced_memory(
|
||||
TriState gather_indirectly_referenced_memory) {
|
||||
gather_indirectly_referenced_memory_ = gather_indirectly_referenced_memory;
|
||||
}
|
||||
|
||||
//! \brief Adds a custom stream to the minidump.
|
||||
//!
|
||||
//! The memory block referenced by \a data and \a size will added to the
|
||||
//! minidump as separate stream with type \stream_type. The memory referred to
|
||||
//! by \a data and \a size is owned by the caller and must remain valid while
|
||||
//! it is in effect for the CrashpadInfo object.
|
||||
//!
|
||||
//! Note that streams will appear in the minidump in the reverse order to
|
||||
//! which they are added.
|
||||
//!
|
||||
//! TODO(scottmg) This is currently only supported on Windows.
|
||||
//!
|
||||
//! \param[in] stream_type The stream type identifier to use. This should be
|
||||
//! normally be larger than `MINIDUMP_STREAM_TYPE::LastReservedStream`
|
||||
//! which is `0xffff`.
|
||||
//! \param[in] data The base pointer of the stream data.
|
||||
//! \param[in] size The size of the stream data.
|
||||
void AddUserDataMinidumpStream(uint32_t stream_type,
|
||||
const void* data,
|
||||
size_t size);
|
||||
|
||||
enum : uint32_t {
|
||||
kSignature = 'CPad',
|
||||
};
|
||||
@ -117,8 +194,11 @@ struct CrashpadInfo {
|
||||
uint32_t version_; // kVersion
|
||||
TriState crashpad_handler_behavior_;
|
||||
TriState system_crash_reporter_forwarding_;
|
||||
uint16_t padding_0_;
|
||||
TriState gather_indirectly_referenced_memory_;
|
||||
uint8_t padding_0_;
|
||||
SimpleAddressRangeBag* extra_memory_ranges_; // weak
|
||||
SimpleStringDictionary* simple_annotations_; // weak
|
||||
internal::UserDataMinidumpStreamListEntry* user_data_minidump_stream_head_;
|
||||
|
||||
#if !defined(NDEBUG) && defined(OS_WIN)
|
||||
uint32_t invalid_read_detection_;
|
||||
|
45
client/simple_address_range_bag.cc
Normal file
45
client/simple_address_range_bag.cc
Normal file
@ -0,0 +1,45 @@
|
||||
// Copyright 2016 The Crashpad Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "client/simple_address_range_bag.h"
|
||||
|
||||
#include "util/stdlib/cxx.h"
|
||||
|
||||
#if CXX_LIBRARY_VERSION >= 2011
|
||||
#include <type_traits>
|
||||
#endif
|
||||
|
||||
namespace crashpad {
|
||||
namespace {
|
||||
|
||||
using SimpleAddressRangeBagForAssertion = TSimpleAddressRangeBag<1>;
|
||||
|
||||
#if CXX_LIBRARY_VERSION >= 2011
|
||||
// In C++11, check that TSimpleAddressRangeBag has standard layout, which is
|
||||
// what is actually important.
|
||||
static_assert(
|
||||
std::is_standard_layout<SimpleAddressRangeBagForAssertion>::value,
|
||||
"SimpleAddressRangeBag must be standard layout");
|
||||
#else
|
||||
// In C++98 (ISO 14882), section 9.5.1 says that a union cannot have a member
|
||||
// with a non-trivial ctor, copy ctor, dtor, or assignment operator. Use this
|
||||
// property to ensure that Entry remains POD. This doesn’t work for C++11
|
||||
// because the requirements for unions have been relaxed.
|
||||
union Compile_Assert {
|
||||
SimpleAddressRangeBagForAssertion::Entry Compile_Assert__entry_must_be_pod;
|
||||
};
|
||||
#endif
|
||||
|
||||
} // namespace
|
||||
} // namespace crashpad
|
193
client/simple_address_range_bag.h
Normal file
193
client/simple_address_range_bag.h
Normal file
@ -0,0 +1,193 @@
|
||||
// Copyright 2016 The Crashpad Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef CRASHPAD_CLIENT_SIMPLE_ADDRESS_RANGE_BAG_H_
|
||||
#define CRASHPAD_CLIENT_SIMPLE_ADDRESS_RANGE_BAG_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "base/logging.h"
|
||||
#include "base/macros.h"
|
||||
#include "util/numeric/checked_range.h"
|
||||
|
||||
namespace crashpad {
|
||||
|
||||
//! \brief A bag implementation using a fixed amount of storage, so that it does
|
||||
//! not perform any dynamic allocations for its operations.
|
||||
//!
|
||||
//! The actual bag storage (TSimpleAddressRangeBag::Entry) is POD, so that it
|
||||
//! can be transmitted over various IPC mechanisms.
|
||||
template <size_t NumEntries = 64>
|
||||
class TSimpleAddressRangeBag {
|
||||
public:
|
||||
//! Constant and publicly accessible version of the template parameter.
|
||||
static const size_t num_entries = NumEntries;
|
||||
|
||||
//! \brief A single entry in the bag.
|
||||
struct Entry {
|
||||
//! \brief The base address of the range.
|
||||
uint64_t base;
|
||||
|
||||
//! \brief The size of the range in bytes.
|
||||
uint64_t size;
|
||||
|
||||
//! \brief Returns the validity of the entry.
|
||||
//!
|
||||
//! If #base and #size are both zero, the entry is considered inactive, and
|
||||
//! this method returns `false`. Otherwise, returns `true`.
|
||||
bool is_active() const {
|
||||
return base != 0 || size != 0;
|
||||
}
|
||||
};
|
||||
|
||||
//! \brief An iterator to traverse all of the active entries in a
|
||||
//! TSimpleAddressRangeBag.
|
||||
class Iterator {
|
||||
public:
|
||||
explicit Iterator(const TSimpleAddressRangeBag& bag)
|
||||
: bag_(bag),
|
||||
current_(0) {
|
||||
}
|
||||
|
||||
//! \brief Returns the next entry in the bag, or `nullptr` if at the end of
|
||||
//! the collection.
|
||||
const Entry* Next() {
|
||||
while (current_ < bag_.num_entries) {
|
||||
const Entry* entry = &bag_.entries_[current_++];
|
||||
if (entry->is_active()) {
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
const TSimpleAddressRangeBag& bag_;
|
||||
size_t current_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(Iterator);
|
||||
};
|
||||
|
||||
TSimpleAddressRangeBag()
|
||||
: entries_() {
|
||||
}
|
||||
|
||||
TSimpleAddressRangeBag(const TSimpleAddressRangeBag& other) {
|
||||
*this = other;
|
||||
}
|
||||
|
||||
TSimpleAddressRangeBag& operator=(const TSimpleAddressRangeBag& other) {
|
||||
memcpy(entries_, other.entries_, sizeof(entries_));
|
||||
return *this;
|
||||
}
|
||||
|
||||
//! \brief Returns the number of active entries. The upper limit for this is
|
||||
//! \a NumEntries.
|
||||
size_t GetCount() const {
|
||||
size_t count = 0;
|
||||
for (size_t i = 0; i < num_entries; ++i) {
|
||||
if (entries_[i].is_active()) {
|
||||
++count;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
//! \brief Inserts the given range into the bag. Duplicates and overlapping
|
||||
//! ranges are supported and allowed, but not coalesced.
|
||||
//!
|
||||
//! \param[in] range The range to be inserted. The range must have either a
|
||||
//! non-zero base address or size.
|
||||
//!
|
||||
//! \return `true` if there was space to insert the range into the bag,
|
||||
//! otherwise `false` with an error logged.
|
||||
bool Insert(CheckedRange<uint64_t> range) {
|
||||
DCHECK(range.base() != 0 || range.size() != 0);
|
||||
|
||||
for (size_t i = 0; i < num_entries; ++i) {
|
||||
if (!entries_[i].is_active()) {
|
||||
entries_[i].base = range.base();
|
||||
entries_[i].size = range.size();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
LOG(ERROR) << "no space available to insert range";
|
||||
return false;
|
||||
}
|
||||
|
||||
//! \brief Inserts the given range into the bag. Duplicates and overlapping
|
||||
//! ranges are supported and allowed, but not coalesced.
|
||||
//!
|
||||
//! \param[in] base The base of the range to be inserted. May not be null.
|
||||
//! \param[in] size The size of the range to be inserted. May not be zero.
|
||||
//!
|
||||
//! \return `true` if there was space to insert the range into the bag,
|
||||
//! otherwise `false` with an error logged.
|
||||
bool Insert(void* base, size_t size) {
|
||||
DCHECK(base != nullptr);
|
||||
DCHECK_NE(0u, size);
|
||||
return Insert(CheckedRange<uint64_t>(
|
||||
base::checked_cast<uint64_t>(reinterpret_cast<uintptr_t>(base)),
|
||||
base::checked_cast<uint64_t>(size)));
|
||||
}
|
||||
|
||||
//! \brief Removes the given range from the bag.
|
||||
//!
|
||||
//! \param[in] range The range to be removed. The range must have either a
|
||||
//! non-zero base address or size.
|
||||
//!
|
||||
//! \return `true` if the range was found and removed, otherwise `false` with
|
||||
//! an error logged.
|
||||
bool Remove(CheckedRange<uint64_t> range) {
|
||||
DCHECK(range.base() != 0 || range.size() != 0);
|
||||
|
||||
for (size_t i = 0; i < num_entries; ++i) {
|
||||
if (entries_[i].base == range.base() &&
|
||||
entries_[i].size == range.size()) {
|
||||
entries_[i].base = entries_[i].size = 0;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
LOG(ERROR) << "did not find range to remove";
|
||||
return false;
|
||||
}
|
||||
|
||||
//! \brief Removes the given range from the bag.
|
||||
//!
|
||||
//! \param[in] base The base of the range to be removed. May not be null.
|
||||
//! \param[in] size The size of the range to be removed. May not be zero.
|
||||
//!
|
||||
//! \return `true` if the range was found and removed, otherwise `false` with
|
||||
//! an error logged.
|
||||
bool Remove(void* base, size_t size) {
|
||||
DCHECK(base != nullptr);
|
||||
DCHECK_NE(0u, size);
|
||||
return Remove(CheckedRange<uint64_t>(
|
||||
base::checked_cast<uint64_t>(reinterpret_cast<uintptr_t>(base)),
|
||||
base::checked_cast<uint64_t>(size)));
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
Entry entries_[NumEntries];
|
||||
};
|
||||
|
||||
//! \brief A TSimpleAddressRangeBag with default template parameters.
|
||||
using SimpleAddressRangeBag = TSimpleAddressRangeBag<64>;
|
||||
|
||||
} // namespace crashpad
|
||||
|
||||
#endif // CRASHPAD_CLIENT_SIMPLE_ADDRESS_RANGE_BAG_H_
|
109
client/simple_address_range_bag_test.cc
Normal file
109
client/simple_address_range_bag_test.cc
Normal file
@ -0,0 +1,109 @@
|
||||
// Copyright 2016 The Crashpad Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "client/simple_address_range_bag.h"
|
||||
|
||||
#include "base/logging.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "test/gtest_death_check.h"
|
||||
|
||||
namespace crashpad {
|
||||
namespace test {
|
||||
namespace {
|
||||
|
||||
TEST(SimpleAddressRangeBag, Entry) {
|
||||
using TestBag = TSimpleAddressRangeBag<15>;
|
||||
TestBag bag;
|
||||
|
||||
const TestBag::Entry* entry = TestBag::Iterator(bag).Next();
|
||||
EXPECT_FALSE(entry);
|
||||
|
||||
bag.Insert(reinterpret_cast<void*>(0x1000), 200);
|
||||
entry = TestBag::Iterator(bag).Next();
|
||||
ASSERT_TRUE(entry);
|
||||
EXPECT_EQ(entry->base, 0x1000u);
|
||||
EXPECT_EQ(entry->size, 200u);
|
||||
|
||||
bag.Remove(reinterpret_cast<void*>(0x1000), 200);
|
||||
EXPECT_FALSE(entry->is_active());
|
||||
EXPECT_EQ(entry->base, 0u);
|
||||
EXPECT_EQ(entry->size, 0u);
|
||||
}
|
||||
|
||||
TEST(SimpleAddressRangeBag, SimpleAddressRangeBag) {
|
||||
SimpleAddressRangeBag bag;
|
||||
|
||||
EXPECT_TRUE(bag.Insert(reinterpret_cast<void*>(0x1000), 10));
|
||||
EXPECT_TRUE(bag.Insert(reinterpret_cast<void*>(0x2000), 20));
|
||||
EXPECT_TRUE(bag.Insert(CheckedRange<uint64_t>(0x3000, 30)));
|
||||
|
||||
EXPECT_EQ(bag.GetCount(), 3u);
|
||||
|
||||
// Duplicates added too.
|
||||
EXPECT_TRUE(bag.Insert(CheckedRange<uint64_t>(0x3000, 30)));
|
||||
EXPECT_TRUE(bag.Insert(CheckedRange<uint64_t>(0x3000, 30)));
|
||||
EXPECT_EQ(bag.GetCount(), 5u);
|
||||
|
||||
// Can be removed 3 times, but not the 4th time.
|
||||
EXPECT_TRUE(bag.Remove(CheckedRange<uint64_t>(0x3000, 30)));
|
||||
EXPECT_TRUE(bag.Remove(CheckedRange<uint64_t>(0x3000, 30)));
|
||||
EXPECT_TRUE(bag.Remove(CheckedRange<uint64_t>(0x3000, 30)));
|
||||
EXPECT_EQ(bag.GetCount(), 2u);
|
||||
EXPECT_FALSE(bag.Remove(CheckedRange<uint64_t>(0x3000, 30)));
|
||||
EXPECT_EQ(bag.GetCount(), 2u);
|
||||
|
||||
EXPECT_TRUE(bag.Remove(reinterpret_cast<void*>(0x1000), 10));
|
||||
EXPECT_TRUE(bag.Remove(reinterpret_cast<void*>(0x2000), 20));
|
||||
EXPECT_EQ(bag.GetCount(), 0u);
|
||||
}
|
||||
|
||||
TEST(SimpleAddressRangeBag, CopyAndAssign) {
|
||||
TSimpleAddressRangeBag<10> bag;
|
||||
EXPECT_TRUE(bag.Insert(CheckedRange<uint64_t>(1, 2)));
|
||||
EXPECT_TRUE(bag.Insert(CheckedRange<uint64_t>(3, 4)));
|
||||
EXPECT_TRUE(bag.Insert(CheckedRange<uint64_t>(5, 6)));
|
||||
EXPECT_TRUE(bag.Remove(CheckedRange<uint64_t>(3, 4)));
|
||||
EXPECT_EQ(2u, bag.GetCount());
|
||||
|
||||
// Test copy.
|
||||
TSimpleAddressRangeBag<10> bag_copy(bag);
|
||||
EXPECT_EQ(2u, bag_copy.GetCount());
|
||||
EXPECT_TRUE(bag_copy.Remove(CheckedRange<uint64_t>(1, 2)));
|
||||
EXPECT_TRUE(bag_copy.Remove(CheckedRange<uint64_t>(5, 6)));
|
||||
EXPECT_EQ(0u, bag_copy.GetCount());
|
||||
EXPECT_EQ(2u, bag.GetCount());
|
||||
|
||||
// Test assign.
|
||||
TSimpleAddressRangeBag<10> bag_assign;
|
||||
bag_assign = bag;
|
||||
EXPECT_EQ(2u, bag_assign.GetCount());
|
||||
EXPECT_TRUE(bag_assign.Remove(CheckedRange<uint64_t>(1, 2)));
|
||||
EXPECT_TRUE(bag_assign.Remove(CheckedRange<uint64_t>(5, 6)));
|
||||
EXPECT_EQ(0u, bag_assign.GetCount());
|
||||
EXPECT_EQ(2u, bag.GetCount());
|
||||
}
|
||||
|
||||
// Running out of space shouldn't crash.
|
||||
TEST(SimpleAddressRangeBag, OutOfSpace) {
|
||||
TSimpleAddressRangeBag<2> bag;
|
||||
EXPECT_TRUE(bag.Insert(CheckedRange<uint64_t>(1, 2)));
|
||||
EXPECT_TRUE(bag.Insert(CheckedRange<uint64_t>(3, 4)));
|
||||
EXPECT_FALSE(bag.Insert(CheckedRange<uint64_t>(5, 6)));
|
||||
EXPECT_EQ(2u, bag.GetCount());
|
||||
EXPECT_FALSE(bag.Remove(CheckedRange<uint64_t>(5, 6)));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace test
|
||||
} // namespace crashpad
|
@ -24,11 +24,6 @@
|
||||
|
||||
namespace crashpad {
|
||||
|
||||
// Opaque type for the serialized representation of a TSimpleStringDictionary.
|
||||
// One is created in TSimpleStringDictionary::Serialize and can be deserialized
|
||||
// using one of the constructors.
|
||||
struct SerializedSimpleStringDictionary;
|
||||
|
||||
//! \brief A map/dictionary collection implementation using a fixed amount of
|
||||
//! storage, so that it does not perform any dynamic allocations for its
|
||||
//! operations.
|
||||
@ -112,16 +107,6 @@ class TSimpleStringDictionary {
|
||||
return *this;
|
||||
}
|
||||
|
||||
//! \brief Constructs a map from its serialized form. \a map should be the out
|
||||
//! parameter from Serialize(), and \a size should be its return value.
|
||||
TSimpleStringDictionary(
|
||||
const SerializedSimpleStringDictionary* map, size_t size) {
|
||||
DCHECK_EQ(size, sizeof(entries_));
|
||||
if (size == sizeof(entries_)) {
|
||||
memcpy(entries_, map, size);
|
||||
}
|
||||
}
|
||||
|
||||
//! \brief Returns the number of active key/value pairs. The upper limit for
|
||||
//! this is \a NumEntries.
|
||||
size_t GetCount() const {
|
||||
@ -236,18 +221,6 @@ class TSimpleStringDictionary {
|
||||
DCHECK_EQ(GetEntryForKey(key), implicit_cast<Entry*>(nullptr));
|
||||
}
|
||||
|
||||
//! \brief Returns a serialized form of the map.
|
||||
//!
|
||||
//! Places a serialized version of the map into \a map and returns the size in
|
||||
//! bytes. Both \a map and the size should be passed to the deserializing
|
||||
//! constructor. Note that the serialized \a map is scoped to the lifetime of
|
||||
//! the non-serialized instance of this class. The \a map data can be copied
|
||||
//! across IPC boundaries.
|
||||
size_t Serialize(const SerializedSimpleStringDictionary** map) const {
|
||||
*map = reinterpret_cast<const SerializedSimpleStringDictionary*>(entries_);
|
||||
return sizeof(entries_);
|
||||
}
|
||||
|
||||
private:
|
||||
const Entry* GetConstEntryForKey(const char* key) const {
|
||||
for (size_t i = 0; i < num_entries; ++i) {
|
||||
|
@ -240,34 +240,6 @@ TEST(SimpleStringDictionary, AddRemove) {
|
||||
EXPECT_FALSE(map.GetValueForKey("mark"));
|
||||
}
|
||||
|
||||
TEST(SimpleStringDictionary, Serialize) {
|
||||
using TestMap = TSimpleStringDictionary<4, 5, 7>;
|
||||
TestMap map;
|
||||
map.SetKeyValue("one", "abc");
|
||||
map.SetKeyValue("two", "def");
|
||||
map.SetKeyValue("tre", "hig");
|
||||
|
||||
EXPECT_STREQ("abc", map.GetValueForKey("one"));
|
||||
EXPECT_STREQ("def", map.GetValueForKey("two"));
|
||||
EXPECT_STREQ("hig", map.GetValueForKey("tre"));
|
||||
|
||||
const SerializedSimpleStringDictionary* serialized;
|
||||
size_t size = map.Serialize(&serialized);
|
||||
|
||||
SerializedSimpleStringDictionary* serialized_copy =
|
||||
reinterpret_cast<SerializedSimpleStringDictionary*>(malloc(size));
|
||||
ASSERT_TRUE(serialized_copy);
|
||||
memcpy(serialized_copy, serialized, size);
|
||||
|
||||
TestMap deserialized(serialized_copy, size);
|
||||
free(serialized_copy);
|
||||
|
||||
EXPECT_EQ(3u, deserialized.GetCount());
|
||||
EXPECT_STREQ("abc", deserialized.GetValueForKey("one"));
|
||||
EXPECT_STREQ("def", deserialized.GetValueForKey("two"));
|
||||
EXPECT_STREQ("hig", deserialized.GetValueForKey("tre"));
|
||||
}
|
||||
|
||||
// Running out of space shouldn't crash.
|
||||
TEST(SimpleStringDictionary, OutOfSpace) {
|
||||
TSimpleStringDictionary<3, 2, 2> map;
|
||||
|
@ -12,7 +12,8 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
CODE_REVIEW_SERVER: codereview.chromium.org
|
||||
CC_LIST: crashpad-dev@chromium.org
|
||||
GERRIT_HOST: True
|
||||
GERRIT_SQUASH_UPLOADS: True
|
||||
CODE_REVIEW_SERVER: https://canary-chromium-review.googlesource.com/
|
||||
VIEW_VC: https://chromium.googlesource.com/crashpad/crashpad/+/
|
||||
PROJECT: crashpad
|
||||
|
@ -160,6 +160,9 @@ enum MINIDUMP_STREAM_TYPE {
|
||||
//! \brief The stream contains information about active `HANDLE`s.
|
||||
HandleDataStream = 12,
|
||||
|
||||
//! \brief The stream type for MINIDUMP_UNLOADED_MODULE_LIST.
|
||||
UnloadedModuleListStream = 14,
|
||||
|
||||
//! \brief The stream type for MINIDUMP_MISC_INFO, MINIDUMP_MISC_INFO_2,
|
||||
//! MINIDUMP_MISC_INFO_3, and MINIDUMP_MISC_INFO_4.
|
||||
//!
|
||||
@ -173,6 +176,10 @@ enum MINIDUMP_STREAM_TYPE {
|
||||
|
||||
//! \brief The stream type for MINIDUMP_MEMORY_INFO_LIST.
|
||||
MemoryInfoListStream = 16,
|
||||
|
||||
//! \brief Values greater than this value will not be used by the system
|
||||
//! and can be used for custom user data streams.
|
||||
LastReservedStream = 0xffff,
|
||||
};
|
||||
|
||||
//! \brief Information about the CPU (or CPUs) that ran the process that the
|
||||
@ -674,6 +681,9 @@ struct __attribute__((packed, aligned(4))) MINIDUMP_HANDLE_DESCRIPTOR_2
|
||||
};
|
||||
|
||||
//! \brief Represents the header for a handle data stream.
|
||||
//!
|
||||
//! A list of MINIDUMP_HANDLE_DESCRIPTOR or MINIDUMP_HANDLE_DESCRIPTOR_2
|
||||
//! structures will immediately follow in the 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)`.
|
||||
@ -691,6 +701,58 @@ struct __attribute((packed, aligned(4))) MINIDUMP_HANDLE_DATA_STREAM {
|
||||
uint32_t Reserved;
|
||||
};
|
||||
|
||||
//! \brief Information about a specific module that was recorded as being
|
||||
//! unloaded at the time the snapshot was taken.
|
||||
//!
|
||||
//! An unloaded module may be a shared library or a loadable module.
|
||||
//!
|
||||
//! \sa MINIDUMP_UNLOADED_MODULE_LIST
|
||||
struct __attribute__((packed, aligned(4))) MINIDUMP_UNLOADED_MODULE {
|
||||
//! \brief The base address where the module was loaded in the address space
|
||||
//! of the process that the minidump file contains a snapshot of.
|
||||
uint64_t BaseOfImage;
|
||||
|
||||
//! \brief The size of the unloaded module.
|
||||
uint32_t SizeOfImage;
|
||||
|
||||
//! \brief The module’s checksum, or `0` if unknown.
|
||||
//!
|
||||
//! On Windows, this field comes from the `CheckSum` field of the module’s
|
||||
//! `IMAGE_OPTIONAL_HEADER` structure, if present. It reflects the checksum at
|
||||
//! the time the module was linked.
|
||||
uint32_t CheckSum;
|
||||
|
||||
//! \brief The module’s timestamp, in `time_t` units, seconds since the POSIX
|
||||
//! epoch.
|
||||
//!
|
||||
//! On Windows, this field comes from the `TimeDateStamp` field of the
|
||||
//! module’s `IMAGE_HEADER` structure. It reflects the timestamp at the time
|
||||
//! the module was linked.
|
||||
uint32_t TimeDateStamp;
|
||||
|
||||
//! \brief ::RVA of a MINIDUMP_STRING containing the module’s path or file
|
||||
//! name.
|
||||
RVA ModuleNameRva;
|
||||
};
|
||||
|
||||
//! \brief Information about all modules recorded as unloaded when the snapshot
|
||||
//! was taken.
|
||||
//!
|
||||
//! A list of MINIDUMP_UNLOADED_MODULE structures will immediately follow in the
|
||||
//! stream.
|
||||
struct __attribute__((packed, aligned(4))) MINIDUMP_UNLOADED_MODULE_LIST {
|
||||
//! \brief The size of the header information for the stream, in bytes. This
|
||||
//! value is `sizeof(MINIDUMP_UNLOADED_MODULE_LIST)`.
|
||||
uint32_t SizeOfHeader;
|
||||
|
||||
//! \brief The size of a descriptor in the stream, in bytes. This value is
|
||||
//! `sizeof(MINIDUMP_UNLOADED_MODULE)`.
|
||||
uint32_t SizeOfEntry;
|
||||
|
||||
//! \brief The number of entries in the stream.
|
||||
uint32_t NumberOfEntries;
|
||||
};
|
||||
|
||||
//! \anchor MINIDUMP_MISCx
|
||||
//! \name MINIDUMP_MISC*
|
||||
//!
|
||||
|
@ -19,18 +19,16 @@
|
||||
== Completed
|
||||
|
||||
Crashpad currently consists of a crash-reporting client and some related tools
|
||||
for Mac OS X. The core client work for Mac OS X is substantially complete.
|
||||
Crashpad has become the crash reporter client for
|
||||
for Mac OS X and Windows. The core client work for both platforms is
|
||||
substantially complete. Crashpad became the crash reporter client for
|
||||
https://dev.chromium.org/Home[Chromium] on Mac OS X as of
|
||||
https://chromium.googlesource.com/chromium/src/+/d413b2dcb54d523811d386f1ff4084f677a6d089[March
|
||||
2015], and for https://dev.chromium.org/Home[Chromium] on Windows as of
|
||||
https://chromium.googlesource.com/chromium/src/+/cfa5b01bb1d06bf96967bd37e21a44752801948c[November
|
||||
2015].
|
||||
|
||||
== In Progress
|
||||
|
||||
Crashpad is actively being bootstrapped on
|
||||
https://crashpad.chromium.org/bug/1[Windows]. All major components are now
|
||||
working on Windows, and integration into Chromium is expected shortly.
|
||||
|
||||
Initial work on a Crashpad client for
|
||||
https://crashpad.chromium.org/bug/30[Android] has begun. This is currently in
|
||||
the design phase.
|
||||
|
@ -15,7 +15,7 @@
|
||||
{
|
||||
'includes': [
|
||||
'../build/crashpad.gypi',
|
||||
'../build/crashpad_in_chromium.gypi',
|
||||
'../build/crashpad_dependencies.gypi',
|
||||
],
|
||||
'targets': [
|
||||
{
|
||||
@ -78,7 +78,7 @@
|
||||
'component%': 'static_library',
|
||||
},
|
||||
'conditions': [
|
||||
['crashpad_in_chromium!=0 and component=="shared_library"', {
|
||||
['crashpad_dependencies=="chromium" and component=="shared_library"', {
|
||||
'xcode_settings': {
|
||||
'LD_RUNPATH_SEARCH_PATHS': [ # -Wl,-rpath
|
||||
# Get back from
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include "base/macros.h"
|
||||
#include "build/build_config.h"
|
||||
#include "client/crashpad_client.h"
|
||||
#include "client/crashpad_info.h"
|
||||
#include "util/win/critical_section_with_debug_info.h"
|
||||
#include "util/win/get_function.h"
|
||||
|
||||
@ -37,6 +38,10 @@
|
||||
#endif
|
||||
|
||||
namespace crashpad {
|
||||
|
||||
int* g_extra_memory_pointer;
|
||||
int* g_extra_memory_not_saved;
|
||||
|
||||
namespace {
|
||||
|
||||
CRITICAL_SECTION g_test_critical_section;
|
||||
@ -135,6 +140,33 @@ void SomeCrashyFunction() {
|
||||
*foo = 42;
|
||||
}
|
||||
|
||||
void AllocateExtraMemoryToBeSaved(
|
||||
crashpad::SimpleAddressRangeBag* extra_ranges) {
|
||||
const size_t kNumInts = 2000;
|
||||
int* extra_memory = new int[kNumInts];
|
||||
g_extra_memory_pointer = extra_memory;
|
||||
for (int i = 0; i < kNumInts; ++i)
|
||||
extra_memory[i] = i * 13 + 2;
|
||||
extra_ranges->Insert(extra_memory, sizeof(extra_memory[0]) * kNumInts);
|
||||
extra_ranges->Insert(&g_extra_memory_pointer, sizeof(g_extra_memory_pointer));
|
||||
}
|
||||
|
||||
void AllocateExtraUnsavedMemory(crashpad::SimpleAddressRangeBag* extra_ranges) {
|
||||
// Allocate some extra memory, and then Insert() but also Remove() it so we
|
||||
// can confirm it doesn't get saved.
|
||||
const size_t kNumInts = 2000;
|
||||
int* extra_memory = new int[kNumInts];
|
||||
g_extra_memory_not_saved = extra_memory;
|
||||
for (int i = 0; i < kNumInts; ++i)
|
||||
extra_memory[i] = i * 17 + 7;
|
||||
extra_ranges->Insert(extra_memory, sizeof(extra_memory[0]) * kNumInts);
|
||||
extra_ranges->Insert(&g_extra_memory_not_saved,
|
||||
sizeof(g_extra_memory_not_saved));
|
||||
|
||||
// We keep the pointer's memory, but remove the pointed-to memory.
|
||||
extra_ranges->Remove(extra_memory, sizeof(extra_memory[0]) * kNumInts);
|
||||
}
|
||||
|
||||
int CrashyMain(int argc, wchar_t* argv[]) {
|
||||
CrashpadClient client;
|
||||
|
||||
@ -164,6 +196,47 @@ int CrashyMain(int argc, wchar_t* argv[]) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
crashpad::SimpleAddressRangeBag* extra_ranges =
|
||||
new crashpad::SimpleAddressRangeBag();
|
||||
crashpad::CrashpadInfo::GetCrashpadInfo()->set_extra_memory_ranges(
|
||||
extra_ranges);
|
||||
AllocateExtraMemoryToBeSaved(extra_ranges);
|
||||
AllocateExtraUnsavedMemory(extra_ranges);
|
||||
|
||||
// Load and unload some uncommonly used modules so we can see them in the list
|
||||
// reported by `lm`. At least two so that we confirm we got the size of
|
||||
// RTL_UNLOAD_EVENT_TRACE right.
|
||||
CHECK(GetModuleHandle(L"lz32.dll") == nullptr);
|
||||
CHECK(GetModuleHandle(L"wmerror.dll") == nullptr);
|
||||
HMODULE lz32 = LoadLibrary(L"lz32.dll");
|
||||
HMODULE wmerror = LoadLibrary(L"wmerror.dll");
|
||||
FreeLibrary(lz32);
|
||||
FreeLibrary(wmerror);
|
||||
|
||||
// Make sure data pointed to by the stack is captured.
|
||||
const int kDataSize = 512;
|
||||
int* pointed_to_data = new int[kDataSize];
|
||||
for (int i = 0; i < kDataSize; ++i)
|
||||
pointed_to_data[i] = i | ((i % 2 == 0) ? 0x80000000 : 0);
|
||||
int* offset_pointer = &pointed_to_data[128];
|
||||
// Encourage the compiler to keep this variable around.
|
||||
printf("%p, %p\n", offset_pointer, &offset_pointer);
|
||||
|
||||
crashpad::CrashpadInfo::GetCrashpadInfo()
|
||||
->set_gather_indirectly_referenced_memory(TriState::kEnabled);
|
||||
|
||||
std::vector<uint8_t> data_stream1(128, 'x');
|
||||
crashpad::CrashpadInfo::GetCrashpadInfo()->AddUserDataMinidumpStream(
|
||||
222222,
|
||||
reinterpret_cast<const void*>(data_stream1.data()),
|
||||
data_stream1.size());
|
||||
|
||||
std::vector<uint8_t> data_stream2(4096, 'z');
|
||||
crashpad::CrashpadInfo::GetCrashpadInfo()->AddUserDataMinidumpStream(
|
||||
333333,
|
||||
reinterpret_cast<const void*>(data_stream2.data()),
|
||||
data_stream2.size());
|
||||
|
||||
AllocateMemoryOfVariousProtections();
|
||||
|
||||
if (InitializeCriticalSectionWithDebugInfoIfPossible(
|
||||
|
@ -70,6 +70,10 @@
|
||||
'minidump_thread_id_map.h',
|
||||
'minidump_thread_writer.cc',
|
||||
'minidump_thread_writer.h',
|
||||
'minidump_unloaded_module_writer.cc',
|
||||
'minidump_unloaded_module_writer.h',
|
||||
'minidump_user_stream_writer.cc',
|
||||
'minidump_user_stream_writer.h',
|
||||
'minidump_writable.cc',
|
||||
'minidump_writable.h',
|
||||
'minidump_writer_util.cc',
|
||||
|
@ -76,6 +76,11 @@ enum MinidumpStreamType : uint32_t {
|
||||
//! \sa HandleDataStream
|
||||
kMinidumpStreamTypeHandleData = HandleDataStream,
|
||||
|
||||
//! \brief The stream type for MINIDUMP_UNLOADED_MODULE_LIST.
|
||||
//!
|
||||
//! \sa UnloadedModuleListStream
|
||||
kMinidumpStreamTypeUnloadedModuleList = UnloadedModuleListStream,
|
||||
|
||||
//! \brief The stream type for MINIDUMP_MISC_INFO, MINIDUMP_MISC_INFO_2,
|
||||
//! MINIDUMP_MISC_INFO_3, and MINIDUMP_MISC_INFO_4.
|
||||
//!
|
||||
|
@ -27,8 +27,11 @@
|
||||
#include "minidump/minidump_system_info_writer.h"
|
||||
#include "minidump/minidump_thread_id_map.h"
|
||||
#include "minidump/minidump_thread_writer.h"
|
||||
#include "minidump/minidump_unloaded_module_writer.h"
|
||||
#include "minidump/minidump_user_stream_writer.h"
|
||||
#include "minidump/minidump_writer_util.h"
|
||||
#include "snapshot/exception_snapshot.h"
|
||||
#include "snapshot/module_snapshot.h"
|
||||
#include "snapshot/process_snapshot.h"
|
||||
#include "util/file/file_writer.h"
|
||||
#include "util/numeric/safe_assignment.h"
|
||||
@ -95,6 +98,22 @@ void MinidumpFileWriter::InitializeFromSnapshot(
|
||||
module_list->InitializeFromSnapshot(process_snapshot->Modules());
|
||||
AddStream(std::move(module_list));
|
||||
|
||||
for (const auto& module : process_snapshot->Modules()) {
|
||||
for (const UserMinidumpStream* stream : module->CustomMinidumpStreams()) {
|
||||
auto user_stream = make_scoped_ptr(new MinidumpUserStreamWriter());
|
||||
user_stream->InitializeFromSnapshot(stream);
|
||||
AddStream(std::move(user_stream));
|
||||
}
|
||||
}
|
||||
|
||||
auto unloaded_modules = process_snapshot->UnloadedModules();
|
||||
if (!unloaded_modules.empty()) {
|
||||
auto unloaded_module_list =
|
||||
make_scoped_ptr(new MinidumpUnloadedModuleListWriter());
|
||||
unloaded_module_list->InitializeFromSnapshot(unloaded_modules);
|
||||
AddStream(std::move(unloaded_module_list));
|
||||
}
|
||||
|
||||
auto crashpad_info = make_scoped_ptr(new MinidumpCrashpadInfoWriter());
|
||||
crashpad_info->InitializeFromSnapshot(process_snapshot);
|
||||
|
||||
|
@ -49,6 +49,8 @@
|
||||
'minidump_system_info_writer_test.cc',
|
||||
'minidump_thread_id_map_test.cc',
|
||||
'minidump_thread_writer_test.cc',
|
||||
'minidump_unloaded_module_writer_test.cc',
|
||||
'minidump_user_stream_writer_test.cc',
|
||||
'minidump_writable_test.cc',
|
||||
'test/minidump_context_test_util.cc',
|
||||
'test/minidump_context_test_util.h',
|
||||
|
203
minidump/minidump_unloaded_module_writer.cc
Normal file
203
minidump/minidump_unloaded_module_writer.cc
Normal file
@ -0,0 +1,203 @@
|
||||
// Copyright 2016 The Crashpad Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "minidump/minidump_unloaded_module_writer.h"
|
||||
|
||||
#include <limits>
|
||||
|
||||
#include "minidump/minidump_writer_util.h"
|
||||
#include "util/file/file_writer.h"
|
||||
#include "util/numeric/in_range_cast.h"
|
||||
#include "util/numeric/safe_assignment.h"
|
||||
|
||||
namespace crashpad {
|
||||
|
||||
MinidumpUnloadedModuleWriter::MinidumpUnloadedModuleWriter()
|
||||
: MinidumpWritable(), unloaded_module_(), name_() {}
|
||||
|
||||
MinidumpUnloadedModuleWriter::~MinidumpUnloadedModuleWriter() {
|
||||
}
|
||||
|
||||
void MinidumpUnloadedModuleWriter::InitializeFromSnapshot(
|
||||
const UnloadedModuleSnapshot& unloaded_module_snapshot) {
|
||||
DCHECK_EQ(state(), kStateMutable);
|
||||
DCHECK(!name_);
|
||||
|
||||
SetName(unloaded_module_snapshot.Name());
|
||||
|
||||
SetImageBaseAddress(unloaded_module_snapshot.Address());
|
||||
SetImageSize(InRangeCast<uint32_t>(unloaded_module_snapshot.Size(),
|
||||
std::numeric_limits<uint32_t>::max()));
|
||||
SetTimestamp(unloaded_module_snapshot.Timestamp());
|
||||
SetChecksum(unloaded_module_snapshot.Checksum());
|
||||
}
|
||||
|
||||
const MINIDUMP_UNLOADED_MODULE*
|
||||
MinidumpUnloadedModuleWriter::MinidumpUnloadedModule() const {
|
||||
DCHECK_EQ(state(), kStateWritable);
|
||||
|
||||
return &unloaded_module_;
|
||||
}
|
||||
|
||||
void MinidumpUnloadedModuleWriter::SetName(const std::string& name) {
|
||||
DCHECK_EQ(state(), kStateMutable);
|
||||
|
||||
if (!name_) {
|
||||
name_.reset(new internal::MinidumpUTF16StringWriter());
|
||||
}
|
||||
name_->SetUTF8(name);
|
||||
}
|
||||
|
||||
void MinidumpUnloadedModuleWriter::SetTimestamp(time_t timestamp) {
|
||||
DCHECK_EQ(state(), kStateMutable);
|
||||
|
||||
internal::MinidumpWriterUtil::AssignTimeT(&unloaded_module_.TimeDateStamp,
|
||||
timestamp);
|
||||
}
|
||||
|
||||
bool MinidumpUnloadedModuleWriter::Freeze() {
|
||||
DCHECK_EQ(state(), kStateMutable);
|
||||
CHECK(name_);
|
||||
|
||||
if (!MinidumpWritable::Freeze()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
name_->RegisterRVA(&unloaded_module_.ModuleNameRva);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t MinidumpUnloadedModuleWriter::SizeOfObject() {
|
||||
DCHECK_GE(state(), kStateFrozen);
|
||||
|
||||
// This object doesn’t directly write anything itself. Its
|
||||
// MINIDUMP_UNLOADED_MODULE is written by its parent as part of a
|
||||
// MINIDUMP_UNLOADED_MODULE_LIST, and its children are responsible for writing
|
||||
// themselves.
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::vector<internal::MinidumpWritable*>
|
||||
MinidumpUnloadedModuleWriter::Children() {
|
||||
DCHECK_GE(state(), kStateFrozen);
|
||||
DCHECK(name_);
|
||||
|
||||
std::vector<MinidumpWritable*> children(1, name_.get());
|
||||
return children;
|
||||
}
|
||||
|
||||
bool MinidumpUnloadedModuleWriter::WriteObject(
|
||||
FileWriterInterface* file_writer) {
|
||||
DCHECK_EQ(state(), kStateWritable);
|
||||
|
||||
// This object doesn’t directly write anything itself. Its
|
||||
// MINIDUMP_UNLOADED_MODULE is written by its parent as part of a
|
||||
// MINIDUMP_UNLOADED_MODULE_LIST, and its children are responsible for writing
|
||||
// themselves.
|
||||
return true;
|
||||
}
|
||||
|
||||
MinidumpUnloadedModuleListWriter::MinidumpUnloadedModuleListWriter()
|
||||
: MinidumpStreamWriter(),
|
||||
unloaded_modules_(),
|
||||
unloaded_module_list_base_() {}
|
||||
|
||||
MinidumpUnloadedModuleListWriter::~MinidumpUnloadedModuleListWriter() {
|
||||
}
|
||||
|
||||
void MinidumpUnloadedModuleListWriter::InitializeFromSnapshot(
|
||||
const std::vector<UnloadedModuleSnapshot>& unloaded_module_snapshots) {
|
||||
DCHECK_EQ(state(), kStateMutable);
|
||||
DCHECK(unloaded_modules_.empty());
|
||||
|
||||
for (auto unloaded_module_snapshot : unloaded_module_snapshots) {
|
||||
auto unloaded_module = make_scoped_ptr(new MinidumpUnloadedModuleWriter());
|
||||
unloaded_module->InitializeFromSnapshot(unloaded_module_snapshot);
|
||||
AddUnloadedModule(std::move(unloaded_module));
|
||||
}
|
||||
}
|
||||
|
||||
void MinidumpUnloadedModuleListWriter::AddUnloadedModule(
|
||||
scoped_ptr<MinidumpUnloadedModuleWriter> unloaded_module) {
|
||||
DCHECK_EQ(state(), kStateMutable);
|
||||
|
||||
unloaded_modules_.push_back(unloaded_module.release());
|
||||
}
|
||||
|
||||
bool MinidumpUnloadedModuleListWriter::Freeze() {
|
||||
DCHECK_EQ(state(), kStateMutable);
|
||||
|
||||
if (!MinidumpStreamWriter::Freeze()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
unloaded_module_list_base_.SizeOfHeader =
|
||||
sizeof(MINIDUMP_UNLOADED_MODULE_LIST);
|
||||
unloaded_module_list_base_.SizeOfEntry = sizeof(MINIDUMP_UNLOADED_MODULE);
|
||||
|
||||
size_t unloaded_module_count = unloaded_modules_.size();
|
||||
if (!AssignIfInRange(&unloaded_module_list_base_.NumberOfEntries,
|
||||
unloaded_module_count)) {
|
||||
LOG(ERROR) << "unloaded_module_count " << unloaded_module_count
|
||||
<< " out of range";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t MinidumpUnloadedModuleListWriter::SizeOfObject() {
|
||||
DCHECK_GE(state(), kStateFrozen);
|
||||
|
||||
return sizeof(unloaded_module_list_base_) +
|
||||
unloaded_modules_.size() * sizeof(MINIDUMP_UNLOADED_MODULE);
|
||||
}
|
||||
|
||||
std::vector<internal::MinidumpWritable*>
|
||||
MinidumpUnloadedModuleListWriter::Children() {
|
||||
DCHECK_GE(state(), kStateFrozen);
|
||||
|
||||
std::vector<MinidumpWritable*> children;
|
||||
for (MinidumpUnloadedModuleWriter* unloaded_module : unloaded_modules_) {
|
||||
children.push_back(unloaded_module);
|
||||
}
|
||||
|
||||
return children;
|
||||
}
|
||||
|
||||
bool MinidumpUnloadedModuleListWriter::WriteObject(
|
||||
FileWriterInterface* file_writer) {
|
||||
DCHECK_EQ(state(), kStateWritable);
|
||||
|
||||
WritableIoVec iov;
|
||||
iov.iov_base = &unloaded_module_list_base_;
|
||||
iov.iov_len = sizeof(unloaded_module_list_base_);
|
||||
std::vector<WritableIoVec> iovecs(1, iov);
|
||||
|
||||
for (const MinidumpUnloadedModuleWriter* unloaded_module :
|
||||
unloaded_modules_) {
|
||||
iov.iov_base = unloaded_module->MinidumpUnloadedModule();
|
||||
iov.iov_len = sizeof(MINIDUMP_UNLOADED_MODULE);
|
||||
iovecs.push_back(iov);
|
||||
}
|
||||
|
||||
return file_writer->WriteIoVec(&iovecs);
|
||||
}
|
||||
|
||||
MinidumpStreamType MinidumpUnloadedModuleListWriter::StreamType() const {
|
||||
return kMinidumpStreamTypeUnloadedModuleList;
|
||||
}
|
||||
|
||||
} // namespace crashpad
|
154
minidump/minidump_unloaded_module_writer.h
Normal file
154
minidump/minidump_unloaded_module_writer.h
Normal file
@ -0,0 +1,154 @@
|
||||
// Copyright 2016 The Crashpad Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef CRASHPAD_MINIDUMP_MINIDUMP_UNLOADED_MODULE_WRITER_H_
|
||||
#define CRASHPAD_MINIDUMP_MINIDUMP_UNLOADED_MODULE_WRITER_H_
|
||||
|
||||
#include <windows.h>
|
||||
#include <dbghelp.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "base/macros.h"
|
||||
#include "base/memory/scoped_ptr.h"
|
||||
#include "minidump/minidump_stream_writer.h"
|
||||
#include "minidump/minidump_string_writer.h"
|
||||
#include "minidump/minidump_writable.h"
|
||||
#include "snapshot/unloaded_module_snapshot.h"
|
||||
|
||||
namespace crashpad {
|
||||
|
||||
//! \brief The writer for a MINIDUMP_UNLOADED_MODULE object in a minidump file.
|
||||
//!
|
||||
//! Because MINIDUMP_UNLOADED_MODULE objects only appear as elements of
|
||||
//! MINIDUMP_UNLOADED_MODULE_LIST objects, this class does not write any data on
|
||||
//! its own. It makes its MINIDUMP_UNLOADED_MODULE data available to its
|
||||
//! MinidumpUnloadedModuleListWriter parent, which writes it as part of a
|
||||
//! MINIDUMP_UNLOADED_MODULE_LIST.
|
||||
class MinidumpUnloadedModuleWriter final : public internal::MinidumpWritable {
|
||||
public:
|
||||
MinidumpUnloadedModuleWriter();
|
||||
~MinidumpUnloadedModuleWriter() override;
|
||||
|
||||
//! \brief Initializes the MINIDUMP_UNLOADED_MODULE based on \a
|
||||
//! unloaded_module_snapshot.
|
||||
//!
|
||||
//! \param[in] unloaded_module_snapshot The unloaded module snapshot to use as
|
||||
//! source data.
|
||||
//!
|
||||
//! \note Valid in #kStateMutable. No mutator methods may be called before
|
||||
//! this method, and it is not normally necessary to call any mutator
|
||||
//! methods after this method.
|
||||
void InitializeFromSnapshot(
|
||||
const UnloadedModuleSnapshot& unloaded_module_snapshot);
|
||||
|
||||
//! \brief Returns a MINIDUMP_UNLOADED_MODULE referencing this object’s data.
|
||||
//!
|
||||
//! This method is expected to be called by a MinidumpUnloadedModuleListWriter
|
||||
//! in order to obtain a MINIDUMP_UNLOADED_MODULE to include in its list.
|
||||
//!
|
||||
//! \note Valid in #kStateWritable.
|
||||
const MINIDUMP_UNLOADED_MODULE* MinidumpUnloadedModule() const;
|
||||
|
||||
//! \brief Arranges for MINIDUMP_UNLOADED_MODULE::ModuleNameRva to point to a
|
||||
//! MINIDUMP_STRING containing \a name.
|
||||
//!
|
||||
//! \note Valid in #kStateMutable.
|
||||
void SetName(const std::string& name);
|
||||
|
||||
//! \brief Sets MINIDUMP_UNLOADED_MODULE::BaseOfImage.
|
||||
void SetImageBaseAddress(uint64_t image_base_address) {
|
||||
unloaded_module_.BaseOfImage = image_base_address;
|
||||
}
|
||||
|
||||
//! \brief Sets MINIDUMP_UNLOADED_MODULE::SizeOfImage.
|
||||
void SetImageSize(uint32_t image_size) {
|
||||
unloaded_module_.SizeOfImage = image_size;
|
||||
}
|
||||
|
||||
//! \brief Sets MINIDUMP_UNLOADED_MODULE::CheckSum.
|
||||
void SetChecksum(uint32_t checksum) { unloaded_module_.CheckSum = checksum; }
|
||||
|
||||
//! \brief Sets MINIDUMP_UNLOADED_MODULE::TimeDateStamp.
|
||||
//!
|
||||
//! \note Valid in #kStateMutable.
|
||||
void SetTimestamp(time_t timestamp);
|
||||
|
||||
protected:
|
||||
// MinidumpWritable:
|
||||
bool Freeze() override;
|
||||
size_t SizeOfObject() override;
|
||||
std::vector<MinidumpWritable*> Children() override;
|
||||
bool WriteObject(FileWriterInterface* file_writer) override;
|
||||
|
||||
private:
|
||||
MINIDUMP_UNLOADED_MODULE unloaded_module_;
|
||||
scoped_ptr<internal::MinidumpUTF16StringWriter> name_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(MinidumpUnloadedModuleWriter);
|
||||
};
|
||||
|
||||
//! \brief The writer for a MINIDUMP_UNLOADED_MODULE_LIST stream in a minidump
|
||||
//! file, containing a list of MINIDUMP_UNLOADED_MODULE objects.
|
||||
class MinidumpUnloadedModuleListWriter final
|
||||
: public internal::MinidumpStreamWriter {
|
||||
public:
|
||||
MinidumpUnloadedModuleListWriter();
|
||||
~MinidumpUnloadedModuleListWriter() override;
|
||||
|
||||
//! \brief Adds an initialized MINIDUMP_UNLOADED_MODULE for each unloaded
|
||||
//! module in \a unloaded_module_snapshots to the
|
||||
//! MINIDUMP_UNLOADED_MODULE_LIST.
|
||||
//!
|
||||
//! \param[in] unloaded_module_snapshots The unloaded module snapshots to use
|
||||
//! as source data.
|
||||
//!
|
||||
//! \note Valid in #kStateMutable. AddUnloadedModule() may not be called
|
||||
//! before this this method, and it is not normally necessary to call
|
||||
//! AddUnloadedModule() after this method.
|
||||
void InitializeFromSnapshot(
|
||||
const std::vector<UnloadedModuleSnapshot>& unloaded_module_snapshots);
|
||||
|
||||
//! \brief Adds a MinidumpUnloadedModuleWriter to the
|
||||
//! MINIDUMP_UNLOADED_MODULE_LIST.
|
||||
//!
|
||||
//! This object takes ownership of \a unloaded_module and becomes its parent
|
||||
//! in the overall tree of internal::MinidumpWritable objects.
|
||||
//!
|
||||
//! \note Valid in #kStateMutable.
|
||||
void AddUnloadedModule(
|
||||
scoped_ptr<MinidumpUnloadedModuleWriter> unloaded_module);
|
||||
|
||||
protected:
|
||||
// MinidumpWritable:
|
||||
bool Freeze() override;
|
||||
size_t SizeOfObject() override;
|
||||
std::vector<MinidumpWritable*> Children() override;
|
||||
bool WriteObject(FileWriterInterface* file_writer) override;
|
||||
|
||||
// MinidumpStreamWriter:
|
||||
MinidumpStreamType StreamType() const override;
|
||||
|
||||
private:
|
||||
PointerVector<MinidumpUnloadedModuleWriter> unloaded_modules_;
|
||||
MINIDUMP_UNLOADED_MODULE_LIST unloaded_module_list_base_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(MinidumpUnloadedModuleListWriter);
|
||||
};
|
||||
|
||||
} // namespace crashpad
|
||||
|
||||
#endif // CRASHPAD_MINIDUMP_MINIDUMP_UNLOADED_MODULE_WRITER_H_
|
162
minidump/minidump_unloaded_module_writer_test.cc
Normal file
162
minidump/minidump_unloaded_module_writer_test.cc
Normal file
@ -0,0 +1,162 @@
|
||||
// Copyright 2016 The Crashpad Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "minidump/minidump_unloaded_module_writer.h"
|
||||
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "minidump/minidump_file_writer.h"
|
||||
#include "minidump/test/minidump_file_writer_test_util.h"
|
||||
#include "minidump/test/minidump_string_writer_test_util.h"
|
||||
#include "minidump/test/minidump_writable_test_util.h"
|
||||
#include "util/file/string_file.h"
|
||||
|
||||
namespace crashpad {
|
||||
namespace test {
|
||||
namespace {
|
||||
|
||||
void ExpectUnloadedModule(const MINIDUMP_UNLOADED_MODULE* expected,
|
||||
const MINIDUMP_UNLOADED_MODULE* observed,
|
||||
const std::string& file_contents,
|
||||
const std::string& expected_module_name) {
|
||||
EXPECT_EQ(expected->BaseOfImage, observed->BaseOfImage);
|
||||
EXPECT_EQ(expected->SizeOfImage, observed->SizeOfImage);
|
||||
EXPECT_EQ(expected->CheckSum, observed->CheckSum);
|
||||
EXPECT_EQ(expected->TimeDateStamp, observed->TimeDateStamp);
|
||||
EXPECT_NE(0u, observed->ModuleNameRva);
|
||||
base::string16 observed_module_name_utf16 =
|
||||
MinidumpStringAtRVAAsString(file_contents, observed->ModuleNameRva);
|
||||
base::string16 expected_module_name_utf16 =
|
||||
base::UTF8ToUTF16(expected_module_name);
|
||||
EXPECT_EQ(expected_module_name_utf16, observed_module_name_utf16);
|
||||
}
|
||||
|
||||
void GetUnloadedModuleListStream(
|
||||
const std::string& file_contents,
|
||||
const MINIDUMP_UNLOADED_MODULE_LIST** unloaded_module_list) {
|
||||
const size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER);
|
||||
const size_t kUnloadedModuleListStreamOffset =
|
||||
kDirectoryOffset + sizeof(MINIDUMP_DIRECTORY);
|
||||
const size_t kUnloadedModulesOffset =
|
||||
kUnloadedModuleListStreamOffset + sizeof(MINIDUMP_UNLOADED_MODULE_LIST);
|
||||
|
||||
ASSERT_GE(file_contents.size(), kUnloadedModulesOffset);
|
||||
|
||||
const MINIDUMP_DIRECTORY* directory;
|
||||
const MINIDUMP_HEADER* header =
|
||||
MinidumpHeaderAtStart(file_contents, &directory);
|
||||
ASSERT_NO_FATAL_FAILURE(VerifyMinidumpHeader(header, 1, 0));
|
||||
ASSERT_TRUE(directory);
|
||||
|
||||
ASSERT_EQ(kMinidumpStreamTypeUnloadedModuleList, directory[0].StreamType);
|
||||
EXPECT_EQ(kUnloadedModuleListStreamOffset, directory[0].Location.Rva);
|
||||
|
||||
*unloaded_module_list =
|
||||
MinidumpWritableAtLocationDescriptor<MINIDUMP_UNLOADED_MODULE_LIST>(
|
||||
file_contents, directory[0].Location);
|
||||
ASSERT_TRUE(unloaded_module_list);
|
||||
}
|
||||
|
||||
TEST(MinidumpUnloadedModuleWriter, EmptyModule) {
|
||||
MinidumpFileWriter minidump_file_writer;
|
||||
auto unloaded_module_list_writer =
|
||||
make_scoped_ptr(new MinidumpUnloadedModuleListWriter());
|
||||
|
||||
const char kModuleName[] = "test_dll";
|
||||
|
||||
auto unloaded_module_writer =
|
||||
make_scoped_ptr(new MinidumpUnloadedModuleWriter());
|
||||
unloaded_module_writer->SetName(kModuleName);
|
||||
|
||||
unloaded_module_list_writer->AddUnloadedModule(
|
||||
std::move(unloaded_module_writer));
|
||||
minidump_file_writer.AddStream(std::move(unloaded_module_list_writer));
|
||||
|
||||
StringFile string_file;
|
||||
ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));
|
||||
|
||||
ASSERT_GT(string_file.string().size(),
|
||||
sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) +
|
||||
sizeof(MINIDUMP_UNLOADED_MODULE_LIST) +
|
||||
1 * sizeof(MINIDUMP_UNLOADED_MODULE));
|
||||
|
||||
const MINIDUMP_UNLOADED_MODULE_LIST* unloaded_module_list = nullptr;
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
GetUnloadedModuleListStream(string_file.string(), &unloaded_module_list));
|
||||
|
||||
EXPECT_EQ(1u, unloaded_module_list->NumberOfEntries);
|
||||
|
||||
MINIDUMP_UNLOADED_MODULE expected = {};
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
ExpectUnloadedModule(&expected,
|
||||
reinterpret_cast<const MINIDUMP_UNLOADED_MODULE*>(
|
||||
&unloaded_module_list[1]),
|
||||
string_file.string(),
|
||||
kModuleName));
|
||||
}
|
||||
|
||||
TEST(MinidumpUnloadedModuleWriter, OneModule) {
|
||||
MinidumpFileWriter minidump_file_writer;
|
||||
auto unloaded_module_list_writer =
|
||||
make_scoped_ptr(new MinidumpUnloadedModuleListWriter());
|
||||
|
||||
const char kModuleName[] = "statically_linked";
|
||||
const uint64_t kModuleBase = 0x10da69000;
|
||||
const uint32_t kModuleSize = 0x1000;
|
||||
const uint32_t kChecksum = 0x76543210;
|
||||
const time_t kTimestamp = 0x386d4380;
|
||||
|
||||
auto unloaded_module_writer =
|
||||
make_scoped_ptr(new MinidumpUnloadedModuleWriter());
|
||||
unloaded_module_writer->SetName(kModuleName);
|
||||
unloaded_module_writer->SetImageBaseAddress(kModuleBase);
|
||||
unloaded_module_writer->SetImageSize(kModuleSize);
|
||||
unloaded_module_writer->SetChecksum(kChecksum);
|
||||
unloaded_module_writer->SetTimestamp(kTimestamp);
|
||||
|
||||
unloaded_module_list_writer->AddUnloadedModule(
|
||||
std::move(unloaded_module_writer));
|
||||
minidump_file_writer.AddStream(std::move(unloaded_module_list_writer));
|
||||
|
||||
StringFile string_file;
|
||||
ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));
|
||||
|
||||
ASSERT_GT(string_file.string().size(),
|
||||
sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) +
|
||||
sizeof(MINIDUMP_UNLOADED_MODULE_LIST) +
|
||||
1 * sizeof(MINIDUMP_UNLOADED_MODULE));
|
||||
|
||||
const MINIDUMP_UNLOADED_MODULE_LIST* unloaded_module_list = nullptr;
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
GetUnloadedModuleListStream(string_file.string(), &unloaded_module_list));
|
||||
|
||||
EXPECT_EQ(1u, unloaded_module_list->NumberOfEntries);
|
||||
|
||||
MINIDUMP_UNLOADED_MODULE expected = {};
|
||||
expected.BaseOfImage = kModuleBase;
|
||||
expected.SizeOfImage = kModuleSize;
|
||||
expected.CheckSum = kChecksum;
|
||||
expected.TimeDateStamp = kTimestamp;
|
||||
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
ExpectUnloadedModule(&expected,
|
||||
reinterpret_cast<const MINIDUMP_UNLOADED_MODULE*>(
|
||||
&unloaded_module_list[1]),
|
||||
string_file.string(),
|
||||
kModuleName));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace test
|
||||
} // namespace crashpad
|
73
minidump/minidump_user_stream_writer.cc
Normal file
73
minidump/minidump_user_stream_writer.cc
Normal file
@ -0,0 +1,73 @@
|
||||
// Copyright 2016 The Crashpad Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "minidump/minidump_user_stream_writer.h"
|
||||
|
||||
#include "util/file/file_writer.h"
|
||||
|
||||
namespace crashpad {
|
||||
|
||||
MinidumpUserStreamWriter::MinidumpUserStreamWriter()
|
||||
: stream_type_(0), reader_() {
|
||||
}
|
||||
|
||||
MinidumpUserStreamWriter::~MinidumpUserStreamWriter() {
|
||||
}
|
||||
|
||||
void MinidumpUserStreamWriter::InitializeFromSnapshot(
|
||||
const UserMinidumpStream* stream) {
|
||||
DCHECK_EQ(state(), kStateMutable);
|
||||
|
||||
stream_type_ = stream->stream_type();
|
||||
if (stream->memory())
|
||||
stream->memory()->Read(&reader_);
|
||||
}
|
||||
|
||||
bool MinidumpUserStreamWriter::Freeze() {
|
||||
DCHECK_EQ(state(), kStateMutable);
|
||||
DCHECK_NE(stream_type_, 0u);
|
||||
return MinidumpStreamWriter::Freeze();
|
||||
}
|
||||
|
||||
size_t MinidumpUserStreamWriter::SizeOfObject() {
|
||||
DCHECK_GE(state(), kStateFrozen);
|
||||
return reader_.size();
|
||||
}
|
||||
|
||||
std::vector<internal::MinidumpWritable*>
|
||||
MinidumpUserStreamWriter::Children() {
|
||||
DCHECK_GE(state(), kStateFrozen);
|
||||
return std::vector<internal::MinidumpWritable*>();
|
||||
}
|
||||
|
||||
bool MinidumpUserStreamWriter::WriteObject(FileWriterInterface* file_writer) {
|
||||
DCHECK_EQ(state(), kStateWritable);
|
||||
return file_writer->Write(reader_.data(), reader_.size());
|
||||
}
|
||||
|
||||
MinidumpStreamType MinidumpUserStreamWriter::StreamType() const {
|
||||
return static_cast<MinidumpStreamType>(stream_type_);
|
||||
}
|
||||
|
||||
MinidumpUserStreamWriter::MemoryReader::~MemoryReader() {}
|
||||
|
||||
bool MinidumpUserStreamWriter::MemoryReader::MemorySnapshotDelegateRead(
|
||||
void* data,
|
||||
size_t size) {
|
||||
data_.resize(size);
|
||||
memcpy(&data_[0], data, size);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace crashpad
|
77
minidump/minidump_user_stream_writer.h
Normal file
77
minidump/minidump_user_stream_writer.h
Normal file
@ -0,0 +1,77 @@
|
||||
// Copyright 2016 The Crashpad Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef CRASHPAD_MINIDUMP_MINIDUMP_USER_STREAM_WRITER_H_
|
||||
#define CRASHPAD_MINIDUMP_MINIDUMP_USER_STREAM_WRITER_H_
|
||||
|
||||
#include <windows.h>
|
||||
#include <dbghelp.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "base/macros.h"
|
||||
#include "minidump/minidump_stream_writer.h"
|
||||
#include "minidump/minidump_writable.h"
|
||||
#include "snapshot/module_snapshot.h"
|
||||
|
||||
namespace crashpad {
|
||||
|
||||
//! \brief The writer for a MINIDUMP_USER_STREAM in a minidump file.
|
||||
class MinidumpUserStreamWriter final : public internal::MinidumpStreamWriter {
|
||||
public:
|
||||
MinidumpUserStreamWriter();
|
||||
~MinidumpUserStreamWriter() override;
|
||||
|
||||
//! \brief Initializes a MINIDUMP_USER_STREAM based on \a stream.
|
||||
//!
|
||||
//! \param[in] stream The memory and stream type to use as source data.
|
||||
//!
|
||||
//! \note Valid in #kStateMutable.
|
||||
void InitializeFromSnapshot(const UserMinidumpStream* stream);
|
||||
|
||||
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:
|
||||
class MemoryReader : public MemorySnapshot::Delegate {
|
||||
public:
|
||||
~MemoryReader() override;
|
||||
bool MemorySnapshotDelegateRead(void* data, size_t size) override;
|
||||
|
||||
const void* data() const {
|
||||
return reinterpret_cast<const void*>(data_.data());
|
||||
}
|
||||
size_t size() const { return data_.size(); }
|
||||
|
||||
private:
|
||||
std::vector<uint8_t> data_;
|
||||
};
|
||||
|
||||
uint32_t stream_type_;
|
||||
MemoryReader reader_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(MinidumpUserStreamWriter);
|
||||
};
|
||||
|
||||
} // namespace crashpad
|
||||
|
||||
#endif // CRASHPAD_MINIDUMP_MINIDUMP_USER_STREAM_WRITER_H_
|
104
minidump/minidump_user_stream_writer_test.cc
Normal file
104
minidump/minidump_user_stream_writer_test.cc
Normal file
@ -0,0 +1,104 @@
|
||||
// Copyright 2016 The Crashpad Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "minidump/minidump_user_stream_writer.h"
|
||||
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#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_snapshot.h"
|
||||
#include "util/file/string_file.h"
|
||||
|
||||
namespace crashpad {
|
||||
namespace test {
|
||||
namespace {
|
||||
|
||||
// The user stream is expected to be the only stream.
|
||||
void GetUserStream(const std::string& file_contents,
|
||||
MINIDUMP_LOCATION_DESCRIPTOR* user_stream_location,
|
||||
uint32_t stream_type) {
|
||||
const size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER);
|
||||
const size_t kUserStreamOffset =
|
||||
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(stream_type, directory[kDirectoryIndex].StreamType);
|
||||
EXPECT_EQ(kUserStreamOffset, directory[kDirectoryIndex].Location.Rva);
|
||||
*user_stream_location = directory[kDirectoryIndex].Location;
|
||||
}
|
||||
|
||||
TEST(MinidumpUserStreamWriter, NoData) {
|
||||
MinidumpFileWriter minidump_file_writer;
|
||||
auto user_stream_writer = make_scoped_ptr(new MinidumpUserStreamWriter());
|
||||
const uint32_t kTestStreamId = 0x123456;
|
||||
auto stream = make_scoped_ptr(new UserMinidumpStream(kTestStreamId, nullptr));
|
||||
user_stream_writer->InitializeFromSnapshot(stream.get());
|
||||
minidump_file_writer.AddStream(std::move(user_stream_writer));
|
||||
|
||||
StringFile string_file;
|
||||
ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));
|
||||
|
||||
ASSERT_EQ(sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY),
|
||||
string_file.string().size());
|
||||
|
||||
MINIDUMP_LOCATION_DESCRIPTOR user_stream_location;
|
||||
ASSERT_NO_FATAL_FAILURE(GetUserStream(
|
||||
string_file.string(), &user_stream_location, kTestStreamId));
|
||||
EXPECT_EQ(0u, user_stream_location.DataSize);
|
||||
}
|
||||
|
||||
TEST(MinidumpUserStreamWriter, OneStream) {
|
||||
MinidumpFileWriter minidump_file_writer;
|
||||
auto user_stream_writer = make_scoped_ptr(new MinidumpUserStreamWriter());
|
||||
const uint32_t kTestStreamId = 0x123456;
|
||||
|
||||
TestMemorySnapshot* test_data = new TestMemorySnapshot();
|
||||
test_data->SetAddress(97865);
|
||||
const size_t kStreamSize = 128;
|
||||
test_data->SetSize(kStreamSize);
|
||||
test_data->SetValue('c');
|
||||
auto stream =
|
||||
make_scoped_ptr(new UserMinidumpStream(kTestStreamId, test_data));
|
||||
user_stream_writer->InitializeFromSnapshot(stream.get());
|
||||
minidump_file_writer.AddStream(std::move(user_stream_writer));
|
||||
|
||||
StringFile string_file;
|
||||
ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));
|
||||
|
||||
ASSERT_EQ(sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) + kStreamSize,
|
||||
string_file.string().size());
|
||||
|
||||
MINIDUMP_LOCATION_DESCRIPTOR user_stream_location;
|
||||
ASSERT_NO_FATAL_FAILURE(GetUserStream(
|
||||
string_file.string(), &user_stream_location, kTestStreamId));
|
||||
EXPECT_EQ(kStreamSize, user_stream_location.DataSize);
|
||||
const std::string stream_data = string_file.string().substr(
|
||||
user_stream_location.Rva, user_stream_location.DataSize);
|
||||
EXPECT_EQ(std::string(kStreamSize, 'c'), stream_data);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace test
|
||||
} // namespace crashpad
|
@ -173,6 +173,14 @@ struct MinidumpModuleListTraits {
|
||||
}
|
||||
};
|
||||
|
||||
struct MinidumpUnloadedModuleListTraits {
|
||||
using ListType = MINIDUMP_UNLOADED_MODULE_LIST;
|
||||
enum : size_t { kElementSize = sizeof(MINIDUMP_UNLOADED_MODULE) };
|
||||
static size_t ElementCount(const ListType* list) {
|
||||
return list->NumberOfEntries;
|
||||
}
|
||||
};
|
||||
|
||||
struct MinidumpThreadListTraits {
|
||||
using ListType = MINIDUMP_THREAD_LIST;
|
||||
enum : size_t { kElementSize = sizeof(MINIDUMP_THREAD) };
|
||||
@ -252,6 +260,15 @@ const MINIDUMP_MODULE_LIST* MinidumpWritableAtLocationDescriptor<
|
||||
file_contents, location);
|
||||
}
|
||||
|
||||
template <>
|
||||
const MINIDUMP_UNLOADED_MODULE_LIST*
|
||||
MinidumpWritableAtLocationDescriptor<MINIDUMP_UNLOADED_MODULE_LIST>(
|
||||
const std::string& file_contents,
|
||||
const MINIDUMP_LOCATION_DESCRIPTOR& location) {
|
||||
return MinidumpListAtLocationDescriptor<MinidumpUnloadedModuleListTraits>(
|
||||
file_contents, location);
|
||||
}
|
||||
|
||||
template <>
|
||||
const MINIDUMP_THREAD_LIST* MinidumpWritableAtLocationDescriptor<
|
||||
MINIDUMP_THREAD_LIST>(const std::string& file_contents,
|
||||
|
@ -89,6 +89,7 @@ MINIDUMP_ALLOW_OVERSIZED_DATA(MINIDUMP_DIRECTORY);
|
||||
// variable-sized lists.
|
||||
MINIDUMP_ALLOW_OVERSIZED_DATA(MINIDUMP_MEMORY_LIST);
|
||||
MINIDUMP_ALLOW_OVERSIZED_DATA(MINIDUMP_MODULE_LIST);
|
||||
MINIDUMP_ALLOW_OVERSIZED_DATA(MINIDUMP_UNLOADED_MODULE_LIST);
|
||||
MINIDUMP_ALLOW_OVERSIZED_DATA(MINIDUMP_THREAD_LIST);
|
||||
MINIDUMP_ALLOW_OVERSIZED_DATA(MINIDUMP_HANDLE_DATA_STREAM);
|
||||
MINIDUMP_ALLOW_OVERSIZED_DATA(MINIDUMP_MEMORY_INFO_LIST);
|
||||
@ -186,6 +187,12 @@ const MINIDUMP_MODULE_LIST* MinidumpWritableAtLocationDescriptor<
|
||||
MINIDUMP_MODULE_LIST>(const std::string& file_contents,
|
||||
const MINIDUMP_LOCATION_DESCRIPTOR& location);
|
||||
|
||||
template <>
|
||||
const MINIDUMP_UNLOADED_MODULE_LIST*
|
||||
MinidumpWritableAtLocationDescriptor<MINIDUMP_UNLOADED_MODULE_LIST>(
|
||||
const std::string& file_contents,
|
||||
const MINIDUMP_LOCATION_DESCRIPTOR& location);
|
||||
|
||||
template <>
|
||||
const MINIDUMP_THREAD_LIST* MinidumpWritableAtLocationDescriptor<
|
||||
MINIDUMP_THREAD_LIST>(const std::string& file_contents,
|
||||
|
53
snapshot/api/module_annotations_win.cc
Normal file
53
snapshot/api/module_annotations_win.cc
Normal file
@ -0,0 +1,53 @@
|
||||
// Copyright 2016 The Crashpad Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "snapshot/api/module_annotations_win.h"
|
||||
|
||||
#include "snapshot/win/pe_image_annotations_reader.h"
|
||||
#include "snapshot/win/pe_image_reader.h"
|
||||
#include "snapshot/win/process_reader_win.h"
|
||||
#include "util/win/get_module_information.h"
|
||||
|
||||
namespace crashpad {
|
||||
|
||||
bool ReadModuleAnnotations(HANDLE process,
|
||||
HMODULE module,
|
||||
std::map<std::string, std::string>* annotations) {
|
||||
ProcessReaderWin process_reader;
|
||||
if (!process_reader.Initialize(process, ProcessSuspensionState::kRunning))
|
||||
return false;
|
||||
|
||||
MODULEINFO module_info;
|
||||
if (!CrashpadGetModuleInformation(
|
||||
process, module, &module_info, sizeof(module_info))) {
|
||||
PLOG(ERROR) << "CrashpadGetModuleInformation";
|
||||
return false;
|
||||
}
|
||||
|
||||
PEImageReader image_reader;
|
||||
if (!image_reader.Initialize(
|
||||
&process_reader,
|
||||
reinterpret_cast<crashpad::WinVMAddress>(module_info.lpBaseOfDll),
|
||||
module_info.SizeOfImage,
|
||||
""))
|
||||
return false;
|
||||
|
||||
PEImageAnnotationsReader annotations_reader(
|
||||
&process_reader, &image_reader, L"");
|
||||
|
||||
*annotations = annotations_reader.SimpleMap();
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace crashpad
|
42
snapshot/api/module_annotations_win.h
Normal file
42
snapshot/api/module_annotations_win.h
Normal file
@ -0,0 +1,42 @@
|
||||
// Copyright 2016 The Crashpad Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef CRASHPAD_SNAPSHOT_API_MODULE_ANNOTATIONS_WIN_H_
|
||||
#define CRASHPAD_SNAPSHOT_API_MODULE_ANNOTATIONS_WIN_H_
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
namespace crashpad {
|
||||
|
||||
//! \brief Reads the module annotations from another process.
|
||||
//!
|
||||
//! \param[in] process The handle to the process that hosts the \a module.
|
||||
//! Requires PROCESS_QUERY_INFORMATION and PROCESS_VM_READ accesses.
|
||||
//! \param[in] module The handle to the module from which the \a annotations
|
||||
//! will be read. This module should be loaded in the target process.
|
||||
//! \param[out] annotations The map that will be filled with the annotations.
|
||||
//! Remains unchanged if the function returns 'false'.
|
||||
//!
|
||||
//! \return `true` if the annotations could be read succesfully, even if the
|
||||
//! module doesn't contain any annotations.
|
||||
bool ReadModuleAnnotations(HANDLE process,
|
||||
HMODULE module,
|
||||
std::map<std::string, std::string>* annotations);
|
||||
|
||||
} // namespace crashpad
|
||||
|
||||
#endif // CRASHPAD_SNAPSHOT_API_MODULE_ANNOTATIONS_WIN_H_
|
83
snapshot/api/module_annotations_win_test.cc
Normal file
83
snapshot/api/module_annotations_win_test.cc
Normal file
@ -0,0 +1,83 @@
|
||||
// Copyright 2016 The Crashpad Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "snapshot/api/module_annotations_win.h"
|
||||
|
||||
#include "client/crashpad_info.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "test/win/win_multiprocess.h"
|
||||
#include "util/file/file_io.h"
|
||||
|
||||
namespace crashpad {
|
||||
namespace test {
|
||||
namespace {
|
||||
|
||||
class ModuleAnnotationsMultiprocessTest final : public WinMultiprocess {
|
||||
private:
|
||||
void WinMultiprocessParent() override {
|
||||
// Read the child executable module.
|
||||
HMODULE module = nullptr;
|
||||
CheckedReadFile(ReadPipeHandle(), &module, sizeof(module));
|
||||
|
||||
// Reopen the child process with necessary access.
|
||||
HANDLE process_handle =
|
||||
OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
|
||||
FALSE,
|
||||
GetProcessId(ChildProcess()));
|
||||
EXPECT_TRUE(process_handle);
|
||||
|
||||
// Read the module annotations in the child process and verify them.
|
||||
std::map<std::string, std::string> annotations;
|
||||
ASSERT_TRUE(ReadModuleAnnotations(process_handle, module, &annotations));
|
||||
|
||||
EXPECT_GE(annotations.size(), 3u);
|
||||
EXPECT_EQ("value", annotations["#APITEST# key"]);
|
||||
EXPECT_EQ("y", annotations["#APITEST# x"]);
|
||||
EXPECT_EQ("", annotations["#APITEST# empty_value"]);
|
||||
|
||||
// Signal the child process to terminate.
|
||||
char c = ' ';
|
||||
CheckedWriteFile(WritePipeHandle(), &c, sizeof(c));
|
||||
}
|
||||
|
||||
void WinMultiprocessChild() override {
|
||||
// Set some test annotations.
|
||||
crashpad::CrashpadInfo* crashpad_info =
|
||||
crashpad::CrashpadInfo::GetCrashpadInfo();
|
||||
|
||||
crashpad::SimpleStringDictionary* simple_annotations =
|
||||
new crashpad::SimpleStringDictionary();
|
||||
simple_annotations->SetKeyValue("#APITEST# key", "value");
|
||||
simple_annotations->SetKeyValue("#APITEST# x", "y");
|
||||
simple_annotations->SetKeyValue("#APITEST# empty_value", "");
|
||||
|
||||
crashpad_info->set_simple_annotations(simple_annotations);
|
||||
|
||||
// Send the executable module.
|
||||
HMODULE module = GetModuleHandle(nullptr);
|
||||
CheckedWriteFile(WritePipeHandle(), &module, sizeof(module));
|
||||
|
||||
// Wait until a signal from the parent process to terminate.
|
||||
char c;
|
||||
CheckedReadFile(ReadPipeHandle(), &c, sizeof(c));
|
||||
}
|
||||
};
|
||||
|
||||
TEST(ModuleAnnotationsWin, ReadAnnotations) {
|
||||
WinMultiprocess::Run<ModuleAnnotationsMultiprocessTest>();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace test
|
||||
} // namespace crashpad
|
128
snapshot/capture_memory.cc
Normal file
128
snapshot/capture_memory.cc
Normal file
@ -0,0 +1,128 @@
|
||||
// Copyright 2016 The Crashpad Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "snapshot/capture_memory.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <limits>
|
||||
|
||||
#include "base/memory/scoped_ptr.h"
|
||||
#include "snapshot/memory_snapshot.h"
|
||||
|
||||
namespace crashpad {
|
||||
namespace internal {
|
||||
|
||||
namespace {
|
||||
|
||||
void MaybeCaptureMemoryAround(CaptureMemory::Delegate* delegate,
|
||||
uint64_t address) {
|
||||
const uint64_t non_address_offset = 0x10000;
|
||||
if (address < non_address_offset)
|
||||
return;
|
||||
|
||||
const uint64_t max_address = delegate->Is64Bit() ?
|
||||
std::numeric_limits<uint64_t>::max() :
|
||||
std::numeric_limits<uint32_t>::max();
|
||||
if (address > max_address - non_address_offset)
|
||||
return;
|
||||
|
||||
const uint64_t kRegisterByteOffset = 256;
|
||||
const uint64_t target = address - kRegisterByteOffset;
|
||||
const uint64_t size = 1024;
|
||||
static_assert(kRegisterByteOffset <= size / 2,
|
||||
"negative offset too large");
|
||||
auto ranges =
|
||||
delegate->GetReadableRanges(CheckedRange<uint64_t>(target, size));
|
||||
for (const auto& range : ranges) {
|
||||
delegate->AddNewMemorySnapshot(range);
|
||||
}
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void CaptureAtPointersInRange(uint8_t* buffer,
|
||||
uint64_t buffer_size,
|
||||
CaptureMemory::Delegate* delegate) {
|
||||
for (uint64_t address_offset = 0; address_offset < buffer_size;
|
||||
address_offset += sizeof(T)) {
|
||||
uint64_t target_address = *reinterpret_cast<T*>(&buffer[address_offset]);
|
||||
MaybeCaptureMemoryAround(delegate, target_address);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// static
|
||||
void CaptureMemory::PointedToByContext(const CPUContext& context,
|
||||
Delegate* delegate) {
|
||||
#if defined(ARCH_CPU_X86_FAMILY)
|
||||
if (context.architecture == kCPUArchitectureX86_64) {
|
||||
MaybeCaptureMemoryAround(delegate, context.x86_64->rax);
|
||||
MaybeCaptureMemoryAround(delegate, context.x86_64->rbx);
|
||||
MaybeCaptureMemoryAround(delegate, context.x86_64->rcx);
|
||||
MaybeCaptureMemoryAround(delegate, context.x86_64->rdx);
|
||||
MaybeCaptureMemoryAround(delegate, context.x86_64->rdi);
|
||||
MaybeCaptureMemoryAround(delegate, context.x86_64->rsi);
|
||||
MaybeCaptureMemoryAround(delegate, context.x86_64->rbp);
|
||||
MaybeCaptureMemoryAround(delegate, context.x86_64->r8);
|
||||
MaybeCaptureMemoryAround(delegate, context.x86_64->r9);
|
||||
MaybeCaptureMemoryAround(delegate, context.x86_64->r10);
|
||||
MaybeCaptureMemoryAround(delegate, context.x86_64->r11);
|
||||
MaybeCaptureMemoryAround(delegate, context.x86_64->r12);
|
||||
MaybeCaptureMemoryAround(delegate, context.x86_64->r13);
|
||||
MaybeCaptureMemoryAround(delegate, context.x86_64->r14);
|
||||
MaybeCaptureMemoryAround(delegate, context.x86_64->r15);
|
||||
MaybeCaptureMemoryAround(delegate, context.x86_64->rip);
|
||||
} else {
|
||||
MaybeCaptureMemoryAround(delegate, context.x86->eax);
|
||||
MaybeCaptureMemoryAround(delegate, context.x86->ebx);
|
||||
MaybeCaptureMemoryAround(delegate, context.x86->ecx);
|
||||
MaybeCaptureMemoryAround(delegate, context.x86->edx);
|
||||
MaybeCaptureMemoryAround(delegate, context.x86->edi);
|
||||
MaybeCaptureMemoryAround(delegate, context.x86->esi);
|
||||
MaybeCaptureMemoryAround(delegate, context.x86->ebp);
|
||||
MaybeCaptureMemoryAround(delegate, context.x86->eip);
|
||||
}
|
||||
#else
|
||||
#error non-x86
|
||||
#endif
|
||||
}
|
||||
|
||||
// static
|
||||
void CaptureMemory::PointedToByMemoryRange(const MemorySnapshot& memory,
|
||||
Delegate* delegate) {
|
||||
if (memory.Size() == 0)
|
||||
return;
|
||||
|
||||
const size_t alignment =
|
||||
delegate->Is64Bit() ? sizeof(uint64_t) : sizeof(uint32_t);
|
||||
if (memory.Address() % alignment != 0 || memory.Size() % alignment != 0) {
|
||||
LOG(ERROR) << "unaligned range";
|
||||
return;
|
||||
}
|
||||
|
||||
scoped_ptr<uint8_t[]> buffer(new uint8_t[memory.Size()]);
|
||||
if (!delegate->ReadMemory(memory.Address(), memory.Size(), buffer.get())) {
|
||||
LOG(ERROR) << "ReadMemory";
|
||||
return;
|
||||
}
|
||||
|
||||
if (delegate->Is64Bit())
|
||||
CaptureAtPointersInRange<uint64_t>(buffer.get(), memory.Size(), delegate);
|
||||
else
|
||||
CaptureAtPointersInRange<uint32_t>(buffer.get(), memory.Size(), delegate);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace crashpad
|
98
snapshot/capture_memory.h
Normal file
98
snapshot/capture_memory.h
Normal file
@ -0,0 +1,98 @@
|
||||
// Copyright 2016 The Crashpad Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef CRASHPAD_SNAPSHOT_CAPTURE_MEMORY_H_
|
||||
#define CRASHPAD_SNAPSHOT_CAPTURE_MEMORY_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "snapshot/cpu_context.h"
|
||||
#include "util/numeric/checked_range.h"
|
||||
|
||||
namespace crashpad {
|
||||
|
||||
class MemorySnapshot;
|
||||
|
||||
namespace internal {
|
||||
|
||||
class CaptureMemory {
|
||||
public:
|
||||
//! \brief An interface to a platform-specific process reader.
|
||||
class Delegate {
|
||||
public:
|
||||
virtual ~Delegate() {}
|
||||
|
||||
//! \return `true` if the target process is a 64-bit process.
|
||||
virtual bool Is64Bit() const = 0;
|
||||
|
||||
//! \brief Attempts to read \a num_bytes bytes from the target process
|
||||
//! starting at address \a at into \a into.
|
||||
//!
|
||||
//! \return `true` if the entire region could be read, or `false` with an
|
||||
//! error logged.
|
||||
virtual bool ReadMemory(uint64_t at,
|
||||
uint64_t num_bytes,
|
||||
void* into) const = 0;
|
||||
|
||||
//! \brief Given a range to be read from the target process, returns a
|
||||
//! vector
|
||||
//! of ranges, representing the readable portions of the original range.
|
||||
//!
|
||||
//! \param[in] range The range being identified.
|
||||
//!
|
||||
//! \return A vector of ranges corresponding to the portion of \a range that
|
||||
//! is readable.
|
||||
virtual std::vector<CheckedRange<uint64_t>> GetReadableRanges(
|
||||
const CheckedRange<uint64_t, uint64_t>& range) const = 0;
|
||||
|
||||
//! \brief Adds the given range representing a memory snapshot in the target
|
||||
//! process to the result.
|
||||
virtual void AddNewMemorySnapshot(
|
||||
const CheckedRange<uint64_t, uint64_t>& range) = 0;
|
||||
};
|
||||
|
||||
//! \brief For all registers that appear to be pointer-like in \a context,
|
||||
//! captures a small amount of memory near their pointed to location.
|
||||
//!
|
||||
//! "Pointer-like" in this context means not too close to zero (signed or
|
||||
//! unsigned) so that there's a reasonable chance that the value is a pointer.
|
||||
//!
|
||||
//! \param[in] context The context to inspect.
|
||||
//! \param[in] process_reader A MemoryCaptureProcessReader to read from the
|
||||
//! target process, and that handles adding new ranges.
|
||||
static void PointedToByContext(const CPUContext& context, Delegate* delegate);
|
||||
|
||||
//! \brief For all pointer-like values in a memory range of the target
|
||||
//! process,
|
||||
//! captures a small amount of memory near the pointed to location.
|
||||
//!
|
||||
//! \param[in] memory An existing MemorySnapshot of the range to search. The
|
||||
//! base address and size must be pointer-aligned and an integral number
|
||||
//! of
|
||||
//! pointers long.
|
||||
//! \param[in] process_reader A MemoryCaptureProcessReader to read from the
|
||||
//! target process, and that handles adding new ranges.
|
||||
static void PointedToByMemoryRange(const MemorySnapshot& memory,
|
||||
Delegate* delegate);
|
||||
|
||||
private:
|
||||
DISALLOW_IMPLICIT_CONSTRUCTORS(CaptureMemory);
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
} // namespace crashpad
|
||||
|
||||
#endif // CRASHPAD_SNAPSHOT_CAPTURE_MEMORY_H_
|
@ -38,7 +38,8 @@ TriState CrashpadInfoClientOptions::TriStateFromCrashpadInfo(
|
||||
|
||||
CrashpadInfoClientOptions::CrashpadInfoClientOptions()
|
||||
: crashpad_handler_behavior(TriState::kUnset),
|
||||
system_crash_reporter_forwarding(TriState::kUnset) {
|
||||
system_crash_reporter_forwarding(TriState::kUnset),
|
||||
gather_indirectly_referenced_memory(TriState::kUnset) {
|
||||
}
|
||||
|
||||
} // namespace crashpad
|
||||
|
@ -59,6 +59,9 @@ struct CrashpadInfoClientOptions {
|
||||
|
||||
//! \sa CrashpadInfo::set_system_crash_reporter_forwarding()
|
||||
TriState system_crash_reporter_forwarding;
|
||||
|
||||
//! \sa CrashpadInfo::set_gather_indirectly_referenced_memory()
|
||||
TriState gather_indirectly_referenced_memory;
|
||||
};
|
||||
|
||||
} // namespace crashpad
|
||||
|
@ -61,6 +61,7 @@ class ScopedUnsetCrashpadInfoOptions {
|
||||
~ScopedUnsetCrashpadInfoOptions() {
|
||||
crashpad_info_->set_crashpad_handler_behavior(TriState::kUnset);
|
||||
crashpad_info_->set_system_crash_reporter_forwarding(TriState::kUnset);
|
||||
crashpad_info_->set_gather_indirectly_referenced_memory(TriState::kUnset);
|
||||
}
|
||||
|
||||
private:
|
||||
@ -69,14 +70,13 @@ class ScopedUnsetCrashpadInfoOptions {
|
||||
DISALLOW_COPY_AND_ASSIGN(ScopedUnsetCrashpadInfoOptions);
|
||||
};
|
||||
|
||||
TEST(CrashpadInfoClientOptions, OneModule) {
|
||||
// Make sure that the initial state has all values unset.
|
||||
CrashpadInfoClientOptions SelfProcessSnapshotAndGetCrashpadOptions() {
|
||||
#if defined(OS_MACOSX)
|
||||
ProcessSnapshotMac process_snapshot;
|
||||
ASSERT_TRUE(process_snapshot.Initialize(mach_task_self()));
|
||||
EXPECT_TRUE(process_snapshot.Initialize(mach_task_self()));
|
||||
#elif defined(OS_WIN)
|
||||
ProcessSnapshotWin process_snapshot;
|
||||
ASSERT_TRUE(process_snapshot.Initialize(
|
||||
EXPECT_TRUE(process_snapshot.Initialize(
|
||||
GetCurrentProcess(), ProcessSuspensionState::kRunning, 0));
|
||||
#else
|
||||
#error Port.
|
||||
@ -84,9 +84,16 @@ TEST(CrashpadInfoClientOptions, OneModule) {
|
||||
|
||||
CrashpadInfoClientOptions options;
|
||||
process_snapshot.GetCrashpadOptions(&options);
|
||||
return options;
|
||||
}
|
||||
|
||||
TEST(CrashpadInfoClientOptions, OneModule) {
|
||||
// Make sure that the initial state has all values unset.
|
||||
auto options = SelfProcessSnapshotAndGetCrashpadOptions();
|
||||
|
||||
EXPECT_EQ(TriState::kUnset, options.crashpad_handler_behavior);
|
||||
EXPECT_EQ(TriState::kUnset, options.system_crash_reporter_forwarding);
|
||||
EXPECT_EQ(TriState::kUnset, options.gather_indirectly_referenced_memory);
|
||||
|
||||
CrashpadInfo* crashpad_info = CrashpadInfo::GetCrashpadInfo();
|
||||
ASSERT_TRUE(crashpad_info);
|
||||
@ -96,9 +103,10 @@ TEST(CrashpadInfoClientOptions, OneModule) {
|
||||
|
||||
crashpad_info->set_crashpad_handler_behavior(TriState::kEnabled);
|
||||
|
||||
process_snapshot.GetCrashpadOptions(&options);
|
||||
options = SelfProcessSnapshotAndGetCrashpadOptions();
|
||||
EXPECT_EQ(TriState::kEnabled, options.crashpad_handler_behavior);
|
||||
EXPECT_EQ(TriState::kUnset, options.system_crash_reporter_forwarding);
|
||||
EXPECT_EQ(TriState::kUnset, options.gather_indirectly_referenced_memory);
|
||||
}
|
||||
|
||||
{
|
||||
@ -106,9 +114,21 @@ TEST(CrashpadInfoClientOptions, OneModule) {
|
||||
|
||||
crashpad_info->set_system_crash_reporter_forwarding(TriState::kDisabled);
|
||||
|
||||
process_snapshot.GetCrashpadOptions(&options);
|
||||
options = SelfProcessSnapshotAndGetCrashpadOptions();
|
||||
EXPECT_EQ(TriState::kUnset, options.crashpad_handler_behavior);
|
||||
EXPECT_EQ(TriState::kDisabled, options.system_crash_reporter_forwarding);
|
||||
EXPECT_EQ(TriState::kUnset, options.gather_indirectly_referenced_memory);
|
||||
}
|
||||
|
||||
{
|
||||
ScopedUnsetCrashpadInfoOptions unset(crashpad_info);
|
||||
|
||||
crashpad_info->set_gather_indirectly_referenced_memory(TriState::kEnabled);
|
||||
|
||||
options = SelfProcessSnapshotAndGetCrashpadOptions();
|
||||
EXPECT_EQ(TriState::kUnset, options.crashpad_handler_behavior);
|
||||
EXPECT_EQ(TriState::kUnset, options.system_crash_reporter_forwarding);
|
||||
EXPECT_EQ(TriState::kEnabled, options.gather_indirectly_referenced_memory);
|
||||
}
|
||||
}
|
||||
|
||||
@ -184,23 +204,12 @@ TEST(CrashpadInfoClientOptions, TwoModules) {
|
||||
dl_handle.LookUpSymbol<CrashpadInfo* (*)()>("TestModule_GetCrashpadInfo");
|
||||
ASSERT_TRUE(TestModule_GetCrashpadInfo);
|
||||
|
||||
auto options = SelfProcessSnapshotAndGetCrashpadOptions();
|
||||
|
||||
// Make sure that the initial state has all values unset.
|
||||
#if defined(OS_MACOSX)
|
||||
ProcessSnapshotMac process_snapshot;
|
||||
ASSERT_TRUE(process_snapshot.Initialize(mach_task_self()));
|
||||
#elif defined(OS_WIN)
|
||||
ProcessSnapshotWin process_snapshot;
|
||||
ASSERT_TRUE(process_snapshot.Initialize(
|
||||
GetCurrentProcess(), ProcessSuspensionState::kRunning, 0));
|
||||
#else
|
||||
#error Port.
|
||||
#endif // OS_MACOSX
|
||||
|
||||
CrashpadInfoClientOptions options;
|
||||
process_snapshot.GetCrashpadOptions(&options);
|
||||
|
||||
EXPECT_EQ(TriState::kUnset, options.crashpad_handler_behavior);
|
||||
EXPECT_EQ(TriState::kUnset, options.system_crash_reporter_forwarding);
|
||||
EXPECT_EQ(TriState::kUnset, options.gather_indirectly_referenced_memory);
|
||||
|
||||
// Get both CrashpadInfo structures.
|
||||
CrashpadInfo* local_crashpad_info = CrashpadInfo::GetCrashpadInfo();
|
||||
@ -216,18 +225,20 @@ TEST(CrashpadInfoClientOptions, TwoModules) {
|
||||
// When only one module sets a value, it applies to the entire process.
|
||||
remote_crashpad_info->set_crashpad_handler_behavior(TriState::kEnabled);
|
||||
|
||||
process_snapshot.GetCrashpadOptions(&options);
|
||||
options = SelfProcessSnapshotAndGetCrashpadOptions();
|
||||
EXPECT_EQ(TriState::kEnabled, options.crashpad_handler_behavior);
|
||||
EXPECT_EQ(TriState::kUnset, options.system_crash_reporter_forwarding);
|
||||
EXPECT_EQ(TriState::kUnset, options.gather_indirectly_referenced_memory);
|
||||
|
||||
// When more than one module sets a value, the first one in the module list
|
||||
// applies to the process. The local module should appear before the remote
|
||||
// module, because the local module loaded the remote module.
|
||||
local_crashpad_info->set_crashpad_handler_behavior(TriState::kDisabled);
|
||||
|
||||
process_snapshot.GetCrashpadOptions(&options);
|
||||
options = SelfProcessSnapshotAndGetCrashpadOptions();
|
||||
EXPECT_EQ(TriState::kDisabled, options.crashpad_handler_behavior);
|
||||
EXPECT_EQ(TriState::kUnset, options.system_crash_reporter_forwarding);
|
||||
EXPECT_EQ(TriState::kUnset, options.gather_indirectly_referenced_memory);
|
||||
}
|
||||
|
||||
{
|
||||
@ -238,9 +249,10 @@ TEST(CrashpadInfoClientOptions, TwoModules) {
|
||||
remote_crashpad_info->set_system_crash_reporter_forwarding(
|
||||
TriState::kDisabled);
|
||||
|
||||
process_snapshot.GetCrashpadOptions(&options);
|
||||
options = SelfProcessSnapshotAndGetCrashpadOptions();
|
||||
EXPECT_EQ(TriState::kUnset, options.crashpad_handler_behavior);
|
||||
EXPECT_EQ(TriState::kDisabled, options.system_crash_reporter_forwarding);
|
||||
EXPECT_EQ(TriState::kUnset, options.gather_indirectly_referenced_memory);
|
||||
|
||||
// When more than one module sets a value, the first one in the module list
|
||||
// applies to the process. The local module should appear before the remote
|
||||
@ -248,9 +260,10 @@ TEST(CrashpadInfoClientOptions, TwoModules) {
|
||||
local_crashpad_info->set_system_crash_reporter_forwarding(
|
||||
TriState::kEnabled);
|
||||
|
||||
process_snapshot.GetCrashpadOptions(&options);
|
||||
options = SelfProcessSnapshotAndGetCrashpadOptions();
|
||||
EXPECT_EQ(TriState::kUnset, options.crashpad_handler_behavior);
|
||||
EXPECT_EQ(TriState::kEnabled, options.system_crash_reporter_forwarding);
|
||||
EXPECT_EQ(TriState::kUnset, options.gather_indirectly_referenced_memory);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -64,6 +64,7 @@ void ModuleSnapshotMac::GetCrashpadOptions(CrashpadInfoClientOptions* options) {
|
||||
if (!mach_o_image_reader_->GetCrashpadInfo(&crashpad_info)) {
|
||||
options->crashpad_handler_behavior = TriState::kUnset;
|
||||
options->system_crash_reporter_forwarding = TriState::kUnset;
|
||||
options->gather_indirectly_referenced_memory = TriState::kUnset;
|
||||
return;
|
||||
}
|
||||
|
||||
@ -74,6 +75,10 @@ void ModuleSnapshotMac::GetCrashpadOptions(CrashpadInfoClientOptions* options) {
|
||||
options->system_crash_reporter_forwarding =
|
||||
CrashpadInfoClientOptions::TriStateFromCrashpadInfo(
|
||||
crashpad_info.system_crash_reporter_forwarding);
|
||||
|
||||
options->gather_indirectly_referenced_memory =
|
||||
CrashpadInfoClientOptions::TriStateFromCrashpadInfo(
|
||||
crashpad_info.gather_indirectly_referenced_memory);
|
||||
}
|
||||
|
||||
std::string ModuleSnapshotMac::Name() const {
|
||||
@ -177,5 +182,15 @@ std::map<std::string, std::string> ModuleSnapshotMac::AnnotationsSimpleMap()
|
||||
return annotations_reader.SimpleMap();
|
||||
}
|
||||
|
||||
std::set<CheckedRange<uint64_t>> ModuleSnapshotMac::ExtraMemoryRanges() const {
|
||||
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||
return std::set<CheckedRange<uint64_t>>();
|
||||
}
|
||||
|
||||
std::vector<const UserMinidumpStream*>
|
||||
ModuleSnapshotMac::CustomMinidumpStreams() const {
|
||||
return std::vector<const UserMinidumpStream*>();
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace crashpad
|
||||
|
@ -79,6 +79,8 @@ class ModuleSnapshotMac final : public ModuleSnapshot {
|
||||
std::string DebugFileName() const override;
|
||||
std::vector<std::string> AnnotationsVector() const override;
|
||||
std::map<std::string, std::string> AnnotationsSimpleMap() const override;
|
||||
std::set<CheckedRange<uint64_t>> ExtraMemoryRanges() const override;
|
||||
std::vector<const UserMinidumpStream*> CustomMinidumpStreams() const override;
|
||||
|
||||
private:
|
||||
std::string name_;
|
||||
|
@ -104,11 +104,16 @@ void ProcessSnapshotMac::GetCrashpadOptions(
|
||||
local_options.system_crash_reporter_forwarding =
|
||||
module_options.system_crash_reporter_forwarding;
|
||||
}
|
||||
if (local_options.gather_indirectly_referenced_memory == TriState::kUnset) {
|
||||
local_options.gather_indirectly_referenced_memory =
|
||||
module_options.gather_indirectly_referenced_memory;
|
||||
}
|
||||
|
||||
// If non-default values have been found for all options, the loop can end
|
||||
// early.
|
||||
if (local_options.crashpad_handler_behavior != TriState::kUnset &&
|
||||
local_options.system_crash_reporter_forwarding != TriState::kUnset) {
|
||||
local_options.system_crash_reporter_forwarding != TriState::kUnset &&
|
||||
local_options.gather_indirectly_referenced_memory != TriState::kUnset) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -181,6 +186,12 @@ std::vector<const ModuleSnapshot*> ProcessSnapshotMac::Modules() const {
|
||||
return modules;
|
||||
}
|
||||
|
||||
std::vector<UnloadedModuleSnapshot> ProcessSnapshotMac::UnloadedModules()
|
||||
const {
|
||||
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||
return std::vector<UnloadedModuleSnapshot>();
|
||||
}
|
||||
|
||||
const ExceptionSnapshot* ProcessSnapshotMac::Exception() const {
|
||||
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||
return exception_.get();
|
||||
|
@ -38,6 +38,7 @@
|
||||
#include "snapshot/process_snapshot.h"
|
||||
#include "snapshot/system_snapshot.h"
|
||||
#include "snapshot/thread_snapshot.h"
|
||||
#include "snapshot/unloaded_module_snapshot.h"
|
||||
#include "util/mach/mach_extensions.h"
|
||||
#include "util/misc/initialization_state_dcheck.h"
|
||||
#include "util/misc/uuid.h"
|
||||
@ -126,6 +127,7 @@ class ProcessSnapshotMac final : public ProcessSnapshot {
|
||||
const SystemSnapshot* System() const override;
|
||||
std::vector<const ThreadSnapshot*> Threads() const override;
|
||||
std::vector<const ModuleSnapshot*> Modules() const override;
|
||||
std::vector<UnloadedModuleSnapshot> UnloadedModules() const override;
|
||||
const ExceptionSnapshot* Exception() const override;
|
||||
std::vector<const MemoryMapRegionSnapshot*> MemoryMap() const override;
|
||||
std::vector<HandleSnapshot> Handles() const override;
|
||||
|
@ -32,8 +32,17 @@ PROCESS_TYPE_STRUCT_BEGIN(CrashpadInfo)
|
||||
// TriState
|
||||
PROCESS_TYPE_STRUCT_MEMBER(uint8_t, system_crash_reporter_forwarding)
|
||||
|
||||
PROCESS_TYPE_STRUCT_MEMBER(uint16_t, padding_0)
|
||||
// TriState
|
||||
PROCESS_TYPE_STRUCT_MEMBER(uint8_t, gather_indirectly_referenced_memory)
|
||||
|
||||
PROCESS_TYPE_STRUCT_MEMBER(uint8_t, padding_0)
|
||||
|
||||
// SimpleAddressRangeBag*
|
||||
PROCESS_TYPE_STRUCT_MEMBER(Pointer, extra_memory_ranges)
|
||||
|
||||
// SimpleStringDictionary*
|
||||
PROCESS_TYPE_STRUCT_MEMBER(Pointer, simple_annotations)
|
||||
|
||||
// UserDataStreamListEntry*
|
||||
PROCESS_TYPE_STRUCT_MEMBER(Pointer, user_data_minidump_stream_head)
|
||||
PROCESS_TYPE_STRUCT_END(CrashpadInfo)
|
||||
|
@ -135,6 +135,20 @@ ModuleSnapshotMinidump::AnnotationsSimpleMap() const {
|
||||
return annotations_simple_map_;
|
||||
}
|
||||
|
||||
std::set<CheckedRange<uint64_t>> ModuleSnapshotMinidump::ExtraMemoryRanges()
|
||||
const {
|
||||
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||
NOTREACHED(); // https://crashpad.chromium.org/bug/10
|
||||
return std::set<CheckedRange<uint64_t>>();
|
||||
}
|
||||
|
||||
std::vector<const UserMinidumpStream*>
|
||||
ModuleSnapshotMinidump::CustomMinidumpStreams() const {
|
||||
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||
NOTREACHED(); // https://crashpad.chromium.org/bug/10
|
||||
return std::vector<const UserMinidumpStream*>();
|
||||
}
|
||||
|
||||
bool ModuleSnapshotMinidump::InitializeModuleCrashpadInfo(
|
||||
FileReaderInterface* file_reader,
|
||||
const MINIDUMP_LOCATION_DESCRIPTOR*
|
||||
|
@ -76,6 +76,8 @@ class ModuleSnapshotMinidump final : public ModuleSnapshot {
|
||||
std::string DebugFileName() const override;
|
||||
std::vector<std::string> AnnotationsVector() const override;
|
||||
std::map<std::string, std::string> AnnotationsSimpleMap() const override;
|
||||
std::set<CheckedRange<uint64_t>> ExtraMemoryRanges() const override;
|
||||
std::vector<const UserMinidumpStream*> CustomMinidumpStreams() const override;
|
||||
|
||||
private:
|
||||
// Initializes data carried in a MinidumpModuleCrashpadInfo structure on
|
||||
|
@ -28,6 +28,7 @@ ProcessSnapshotMinidump::ProcessSnapshotMinidump()
|
||||
stream_directory_(),
|
||||
stream_map_(),
|
||||
modules_(),
|
||||
unloaded_modules_(),
|
||||
crashpad_info_(),
|
||||
annotations_simple_map_(),
|
||||
file_reader_(nullptr),
|
||||
@ -171,6 +172,13 @@ std::vector<const ModuleSnapshot*> ProcessSnapshotMinidump::Modules() const {
|
||||
return modules;
|
||||
}
|
||||
|
||||
std::vector<UnloadedModuleSnapshot> ProcessSnapshotMinidump::UnloadedModules()
|
||||
const {
|
||||
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||
NOTREACHED(); // https://crashpad.chromium.org/bug/10
|
||||
return unloaded_modules_;
|
||||
}
|
||||
|
||||
const ExceptionSnapshot* ProcessSnapshotMinidump::Exception() const {
|
||||
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||
NOTREACHED(); // https://crashpad.chromium.org/bug/10
|
||||
|
@ -33,6 +33,7 @@
|
||||
#include "snapshot/process_snapshot.h"
|
||||
#include "snapshot/system_snapshot.h"
|
||||
#include "snapshot/thread_snapshot.h"
|
||||
#include "snapshot/unloaded_module_snapshot.h"
|
||||
#include "util/file/file_reader.h"
|
||||
#include "util/misc/initialization_state_dcheck.h"
|
||||
#include "util/misc/uuid.h"
|
||||
@ -69,6 +70,7 @@ class ProcessSnapshotMinidump final : public ProcessSnapshot {
|
||||
const SystemSnapshot* System() const override;
|
||||
std::vector<const ThreadSnapshot*> Threads() const override;
|
||||
std::vector<const ModuleSnapshot*> Modules() const override;
|
||||
std::vector<UnloadedModuleSnapshot> UnloadedModules() const override;
|
||||
const ExceptionSnapshot* Exception() const override;
|
||||
std::vector<const MemoryMapRegionSnapshot*> MemoryMap() const override;
|
||||
std::vector<HandleSnapshot> Handles() const override;
|
||||
@ -94,6 +96,7 @@ class ProcessSnapshotMinidump final : public ProcessSnapshot {
|
||||
std::vector<MINIDUMP_DIRECTORY> stream_directory_;
|
||||
std::map<MinidumpStreamType, const MINIDUMP_LOCATION_DESCRIPTOR*> stream_map_;
|
||||
PointerVector<internal::ModuleSnapshotMinidump> modules_;
|
||||
std::vector<UnloadedModuleSnapshot> unloaded_modules_;
|
||||
MinidumpCrashpadInfo crashpad_info_;
|
||||
std::map<std::string, std::string> annotations_simple_map_;
|
||||
FileReaderInterface* file_reader_; // weak
|
||||
|
@ -19,13 +19,39 @@
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "base/memory/scoped_ptr.h"
|
||||
#include "util/misc/uuid.h"
|
||||
#include "util/numeric/checked_range.h"
|
||||
#include "snapshot/memory_snapshot.h"
|
||||
|
||||
namespace crashpad {
|
||||
|
||||
class MemorySnapshot;
|
||||
|
||||
//! \brief Information describing a custom user data stream in a minidump.
|
||||
class UserMinidumpStream {
|
||||
public:
|
||||
//! \brief Constructs a UserMinidumpStream, takes ownership of \a memory.
|
||||
UserMinidumpStream(uint32_t stream_type, MemorySnapshot* memory)
|
||||
: memory_(memory), stream_type_(stream_type) {}
|
||||
|
||||
const MemorySnapshot* memory() const { return memory_.get(); }
|
||||
uint32_t stream_type() const { return stream_type_; }
|
||||
|
||||
private:
|
||||
//! \brief The memory representing the custom minidump stream.
|
||||
scoped_ptr<MemorySnapshot> memory_;
|
||||
|
||||
//! \brief The stream type that the minidump stream will be tagged with.
|
||||
uint32_t stream_type_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(UserMinidumpStream);
|
||||
};
|
||||
|
||||
//! \brief An abstract interface to a snapshot representing a code module
|
||||
//! (binary image) loaded into a snapshot process.
|
||||
class ModuleSnapshot {
|
||||
@ -168,6 +194,19 @@ class ModuleSnapshot {
|
||||
//! system, or snapshot producer may be obtained by calling
|
||||
//! ProcessSnapshot::AnnotationsSimpleMap().
|
||||
virtual std::map<std::string, std::string> AnnotationsSimpleMap() const = 0;
|
||||
|
||||
//! \brief Returns a set of extra memory ranges specified in the module as
|
||||
//! being desirable to include in the crash dump.
|
||||
virtual std::set<CheckedRange<uint64_t>> ExtraMemoryRanges() const = 0;
|
||||
|
||||
//! \brief Returns a list of custom minidump stream specified in the module to
|
||||
//! be included in the crash dump.
|
||||
//!
|
||||
//! \return The caller does not take ownership of the returned objects, they
|
||||
//! are scoped to the lifetime of the ModuleSnapshot object that they were
|
||||
//! obtained from.
|
||||
virtual std::vector<const UserMinidumpStream*> CustomMinidumpStreams()
|
||||
const = 0;
|
||||
};
|
||||
|
||||
} // namespace crashpad
|
||||
|
@ -33,6 +33,7 @@ class MemorySnapshot;
|
||||
class ModuleSnapshot;
|
||||
class SystemSnapshot;
|
||||
class ThreadSnapshot;
|
||||
class UnloadedModuleSnapshot;
|
||||
|
||||
//! \brief An abstract interface to a snapshot representing the state of a
|
||||
//! process.
|
||||
@ -146,6 +147,12 @@ class ProcessSnapshot {
|
||||
//! ProcessSnapshot object that they were obtained from.
|
||||
virtual std::vector<const ModuleSnapshot*> Modules() const = 0;
|
||||
|
||||
//! \brief Returns UnloadedModuleSnapshot objects reflecting the code modules
|
||||
//! the were recorded as unloaded at the time of the snapshot.
|
||||
//!
|
||||
//! \return A vector of UnloadedModuleSnapshot objects.
|
||||
virtual std::vector<UnloadedModuleSnapshot> UnloadedModules() const = 0;
|
||||
|
||||
//! \brief Returns ThreadSnapshot objects reflecting the threads (lightweight
|
||||
//! processes) existing in the snapshot process at the time of the
|
||||
//! snapshot.
|
||||
|
@ -30,6 +30,8 @@
|
||||
'..',
|
||||
],
|
||||
'sources': [
|
||||
'capture_memory.cc',
|
||||
'capture_memory.h',
|
||||
'cpu_architecture.h',
|
||||
'cpu_context.cc',
|
||||
'cpu_context.h',
|
||||
@ -89,12 +91,14 @@
|
||||
'process_snapshot.h',
|
||||
'system_snapshot.h',
|
||||
'thread_snapshot.h',
|
||||
'win/capture_context_memory.cc',
|
||||
'win/capture_context_memory.h',
|
||||
'unloaded_module_snapshot.cc',
|
||||
'unloaded_module_snapshot.h',
|
||||
'win/cpu_context_win.cc',
|
||||
'win/cpu_context_win.h',
|
||||
'win/exception_snapshot_win.cc',
|
||||
'win/exception_snapshot_win.h',
|
||||
'win/capture_memory_delegate_win.cc',
|
||||
'win/capture_memory_delegate_win.h',
|
||||
'win/memory_map_region_snapshot_win.cc',
|
||||
'win/memory_map_region_snapshot_win.h',
|
||||
'win/memory_snapshot_win.cc',
|
||||
@ -129,5 +133,32 @@
|
||||
}],
|
||||
]
|
||||
},
|
||||
{
|
||||
'variables': {
|
||||
'conditions': [
|
||||
['OS == "win"', {
|
||||
'snapshot_api_target_type%': 'static_library',
|
||||
}, {
|
||||
# There are no source files except on Windows.
|
||||
'snapshot_api_target_type%': 'none',
|
||||
}],
|
||||
],
|
||||
},
|
||||
'target_name': 'crashpad_snapshot_api',
|
||||
'type': '<(snapshot_api_target_type)',
|
||||
'dependencies': [
|
||||
'crashpad_snapshot',
|
||||
'../compat/compat.gyp:crashpad_compat',
|
||||
'../third_party/mini_chromium/mini_chromium.gyp:base',
|
||||
'../util/util.gyp:crashpad_util',
|
||||
],
|
||||
'include_dirs': [
|
||||
'..',
|
||||
],
|
||||
'sources': [
|
||||
'api/module_annotations_win.cc',
|
||||
'api/module_annotations_win.h',
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
|
@ -54,6 +54,7 @@
|
||||
'dependencies': [
|
||||
'crashpad_snapshot_test_module',
|
||||
'snapshot.gyp:crashpad_snapshot',
|
||||
'snapshot.gyp:crashpad_snapshot_api',
|
||||
'../client/client.gyp:crashpad_client',
|
||||
'../compat/compat.gyp:crashpad_compat',
|
||||
'../test/test.gyp:crashpad_test',
|
||||
@ -68,6 +69,7 @@
|
||||
'sources': [
|
||||
'cpu_context_test.cc',
|
||||
'crashpad_info_client_options_test.cc',
|
||||
'api/module_annotations_win_test.cc',
|
||||
'mac/cpu_context_mac_test.cc',
|
||||
'mac/mach_o_image_annotations_reader_test.cc',
|
||||
'mac/mach_o_image_reader_test.cc',
|
||||
@ -78,6 +80,7 @@
|
||||
'minidump/process_snapshot_minidump_test.cc',
|
||||
'win/cpu_context_win_test.cc',
|
||||
'win/exception_snapshot_win_test.cc',
|
||||
'win/extra_memory_ranges_test.cc',
|
||||
'win/pe_image_annotations_reader_test.cc',
|
||||
'win/pe_image_reader_test.cc',
|
||||
'win/process_reader_win_test.cc',
|
||||
@ -168,6 +171,18 @@
|
||||
'win/crashpad_snapshot_test_dump_without_crashing.cc',
|
||||
],
|
||||
},
|
||||
{
|
||||
'target_name': 'crashpad_snapshot_test_extra_memory_ranges',
|
||||
'type': 'executable',
|
||||
'dependencies': [
|
||||
'../client/client.gyp:crashpad_client',
|
||||
'../compat/compat.gyp:crashpad_compat',
|
||||
'../third_party/mini_chromium/mini_chromium.gyp:base',
|
||||
],
|
||||
'sources': [
|
||||
'win/crashpad_snapshot_test_extra_memory_ranges.cc',
|
||||
],
|
||||
},
|
||||
{
|
||||
'target_name': 'crashpad_snapshot_test_image_reader',
|
||||
'type': 'executable',
|
||||
|
@ -29,7 +29,8 @@ TestModuleSnapshot::TestModuleSnapshot()
|
||||
uuid_(),
|
||||
debug_file_name_(),
|
||||
annotations_vector_(),
|
||||
annotations_simple_map_() {
|
||||
annotations_simple_map_(),
|
||||
extra_memory_ranges_() {
|
||||
}
|
||||
|
||||
TestModuleSnapshot::~TestModuleSnapshot() {
|
||||
@ -93,5 +94,14 @@ std::map<std::string, std::string> TestModuleSnapshot::AnnotationsSimpleMap()
|
||||
return annotations_simple_map_;
|
||||
}
|
||||
|
||||
std::set<CheckedRange<uint64_t>> TestModuleSnapshot::ExtraMemoryRanges() const {
|
||||
return extra_memory_ranges_;
|
||||
}
|
||||
|
||||
std::vector<const UserMinidumpStream*>
|
||||
TestModuleSnapshot::CustomMinidumpStreams() const {
|
||||
return custom_minidump_streams_;
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
} // namespace crashpad
|
||||
|
@ -24,6 +24,7 @@
|
||||
|
||||
#include "base/macros.h"
|
||||
#include "snapshot/module_snapshot.h"
|
||||
#include "util/stdlib/pointer_container.h"
|
||||
|
||||
namespace crashpad {
|
||||
namespace test {
|
||||
@ -75,6 +76,13 @@ class TestModuleSnapshot final : public ModuleSnapshot {
|
||||
const std::map<std::string, std::string>& annotations_simple_map) {
|
||||
annotations_simple_map_ = annotations_simple_map;
|
||||
}
|
||||
void SetExtraMemoryRanges(
|
||||
const std::set<CheckedRange<uint64_t>>& extra_memory_ranges) {
|
||||
extra_memory_ranges_ = extra_memory_ranges;
|
||||
}
|
||||
void AddCustomMinidumpStream(const UserMinidumpStream* stream) {
|
||||
custom_minidump_streams_.push_back(stream);
|
||||
}
|
||||
|
||||
// ModuleSnapshot:
|
||||
|
||||
@ -95,6 +103,8 @@ class TestModuleSnapshot final : public ModuleSnapshot {
|
||||
std::string DebugFileName() const override;
|
||||
std::vector<std::string> AnnotationsVector() const override;
|
||||
std::map<std::string, std::string> AnnotationsSimpleMap() const override;
|
||||
std::set<CheckedRange<uint64_t>> ExtraMemoryRanges() const override;
|
||||
std::vector<const UserMinidumpStream*> CustomMinidumpStreams() const override;
|
||||
|
||||
private:
|
||||
std::string name_;
|
||||
@ -109,6 +119,8 @@ class TestModuleSnapshot final : public ModuleSnapshot {
|
||||
std::string debug_file_name_;
|
||||
std::vector<std::string> annotations_vector_;
|
||||
std::map<std::string, std::string> annotations_simple_map_;
|
||||
std::set<CheckedRange<uint64_t>> extra_memory_ranges_;
|
||||
PointerVector<const UserMinidumpStream> custom_minidump_streams_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(TestModuleSnapshot);
|
||||
};
|
||||
|
@ -96,6 +96,11 @@ std::vector<const ModuleSnapshot*> TestProcessSnapshot::Modules() const {
|
||||
return modules;
|
||||
}
|
||||
|
||||
std::vector<UnloadedModuleSnapshot> TestProcessSnapshot::UnloadedModules()
|
||||
const {
|
||||
return unloaded_modules_;
|
||||
}
|
||||
|
||||
const ExceptionSnapshot* TestProcessSnapshot::Exception() const {
|
||||
return exception_.get();
|
||||
}
|
||||
|
@ -33,6 +33,7 @@
|
||||
#include "snapshot/process_snapshot.h"
|
||||
#include "snapshot/system_snapshot.h"
|
||||
#include "snapshot/thread_snapshot.h"
|
||||
#include "snapshot/unloaded_module_snapshot.h"
|
||||
#include "util/misc/uuid.h"
|
||||
#include "util/stdlib/pointer_container.h"
|
||||
|
||||
@ -92,6 +93,15 @@ class TestProcessSnapshot final : public ProcessSnapshot {
|
||||
modules_.push_back(module.release());
|
||||
}
|
||||
|
||||
//! \brief Adds an unloaded module snapshot to be returned by
|
||||
//! UnloadedModules().
|
||||
//!
|
||||
//! \param[in] unloaded_module The unloaded module snapshot that will be
|
||||
//! included in UnloadedModules().
|
||||
void AddModule(const UnloadedModuleSnapshot& unloaded_module) {
|
||||
unloaded_modules_.push_back(unloaded_module);
|
||||
}
|
||||
|
||||
//! \brief Sets the exception snapshot to be returned by Exception().
|
||||
//!
|
||||
//! \param[in] exception The exception snapshot that Exception() will return.
|
||||
@ -139,6 +149,7 @@ class TestProcessSnapshot final : public ProcessSnapshot {
|
||||
const SystemSnapshot* System() const override;
|
||||
std::vector<const ThreadSnapshot*> Threads() const override;
|
||||
std::vector<const ModuleSnapshot*> Modules() const override;
|
||||
std::vector<UnloadedModuleSnapshot> UnloadedModules() const override;
|
||||
const ExceptionSnapshot* Exception() const override;
|
||||
std::vector<const MemoryMapRegionSnapshot*> MemoryMap() const override;
|
||||
std::vector<HandleSnapshot> Handles() const override;
|
||||
@ -157,6 +168,7 @@ class TestProcessSnapshot final : public ProcessSnapshot {
|
||||
scoped_ptr<SystemSnapshot> system_;
|
||||
PointerVector<ThreadSnapshot> threads_;
|
||||
PointerVector<ModuleSnapshot> modules_;
|
||||
std::vector<UnloadedModuleSnapshot> unloaded_modules_;
|
||||
scoped_ptr<ExceptionSnapshot> exception_;
|
||||
PointerVector<MemoryMapRegionSnapshot> memory_map_;
|
||||
std::vector<HandleSnapshot> handles_;
|
||||
|
33
snapshot/unloaded_module_snapshot.cc
Normal file
33
snapshot/unloaded_module_snapshot.cc
Normal file
@ -0,0 +1,33 @@
|
||||
// Copyright 2016 The Crashpad Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "snapshot/unloaded_module_snapshot.h"
|
||||
|
||||
namespace crashpad {
|
||||
|
||||
UnloadedModuleSnapshot::UnloadedModuleSnapshot(uint64_t address,
|
||||
uint64_t size,
|
||||
uint32_t checksum,
|
||||
uint32_t timestamp,
|
||||
const std::string& name)
|
||||
: name_(name),
|
||||
address_(address),
|
||||
size_(size),
|
||||
checksum_(checksum),
|
||||
timestamp_(timestamp) {}
|
||||
|
||||
UnloadedModuleSnapshot::~UnloadedModuleSnapshot() {
|
||||
}
|
||||
|
||||
} // namespace crashpad
|
61
snapshot/unloaded_module_snapshot.h
Normal file
61
snapshot/unloaded_module_snapshot.h
Normal file
@ -0,0 +1,61 @@
|
||||
// Copyright 2016 The Crashpad Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef CRASHPAD_SNAPSHOT_UNLOADED_MODULE_SNAPSHOT_H_
|
||||
#define CRASHPAD_SNAPSHOT_UNLOADED_MODULE_SNAPSHOT_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace crashpad {
|
||||
|
||||
//! \brief Information about an unloaded module that was previously loaded into
|
||||
//! a snapshot process.
|
||||
class UnloadedModuleSnapshot {
|
||||
public:
|
||||
UnloadedModuleSnapshot(uint64_t address,
|
||||
uint64_t size,
|
||||
uint32_t checksum,
|
||||
uint32_t timestamp,
|
||||
const std::string& name);
|
||||
~UnloadedModuleSnapshot();
|
||||
|
||||
//! \brief The base address of the module in the target processes' address
|
||||
//! space.
|
||||
uint64_t Address() const { return address_; }
|
||||
|
||||
//! \brief The size of the module.
|
||||
uint64_t Size() const { return size_; }
|
||||
|
||||
//! \brief The checksum of the image.
|
||||
uint32_t Checksum() const { return checksum_; }
|
||||
|
||||
//! \brief The time and date stamp in `time_t` format.
|
||||
uint32_t Timestamp() const { return timestamp_; }
|
||||
|
||||
//! \brief The name of the module.
|
||||
std::string Name() const { return name_; }
|
||||
|
||||
private:
|
||||
std::string name_;
|
||||
uint64_t address_;
|
||||
uint64_t size_;
|
||||
uint32_t checksum_;
|
||||
uint32_t timestamp_;
|
||||
};
|
||||
|
||||
} // namespace crashpad
|
||||
|
||||
#endif // CRASHPAD_SNAPSHOT_UNLOADED_MODULE_SNAPSHOT_H_
|
@ -1,103 +0,0 @@
|
||||
// Copyright 2016 The Crashpad Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "snapshot/win/capture_context_memory.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <limits>
|
||||
|
||||
#include "snapshot/win/memory_snapshot_win.h"
|
||||
#include "snapshot/win/process_reader_win.h"
|
||||
|
||||
namespace crashpad {
|
||||
namespace internal {
|
||||
|
||||
namespace {
|
||||
|
||||
void MaybeCaptureMemoryAround(ProcessReaderWin* process_reader,
|
||||
WinVMAddress address,
|
||||
PointerVector<MemorySnapshotWin>* into) {
|
||||
const WinVMAddress non_address_offset = 0x10000;
|
||||
if (address < non_address_offset)
|
||||
return;
|
||||
if (process_reader->Is64Bit()) {
|
||||
if (address >= std::numeric_limits<uint64_t>::max() - non_address_offset)
|
||||
return;
|
||||
} else {
|
||||
if (address >= std::numeric_limits<uint32_t>::max() - non_address_offset)
|
||||
return;
|
||||
}
|
||||
|
||||
const WinVMSize kRegisterByteOffset = 32;
|
||||
const WinVMAddress target = address - kRegisterByteOffset;
|
||||
const WinVMSize size = 128;
|
||||
auto ranges = process_reader->GetProcessInfo().GetReadableRanges(
|
||||
CheckedRange<WinVMAddress, WinVMSize>(target, size));
|
||||
for (const auto& range : ranges) {
|
||||
internal::MemorySnapshotWin* snapshot = new internal::MemorySnapshotWin();
|
||||
snapshot->Initialize(process_reader, range.base(), range.size());
|
||||
into->push_back(snapshot);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void CaptureMemoryPointedToByContext(const CPUContext& context,
|
||||
ProcessReaderWin* process_reader,
|
||||
const ProcessReaderWin::Thread& thread,
|
||||
PointerVector<MemorySnapshotWin>* into) {
|
||||
#if defined(ARCH_CPU_X86_64)
|
||||
if (context.architecture == kCPUArchitectureX86_64) {
|
||||
MaybeCaptureMemoryAround(process_reader, context.x86_64->rax, into);
|
||||
MaybeCaptureMemoryAround(process_reader, context.x86_64->rbx, into);
|
||||
MaybeCaptureMemoryAround(process_reader, context.x86_64->rcx, into);
|
||||
MaybeCaptureMemoryAround(process_reader, context.x86_64->rdx, into);
|
||||
MaybeCaptureMemoryAround(process_reader, context.x86_64->rdi, into);
|
||||
MaybeCaptureMemoryAround(process_reader, context.x86_64->rsi, into);
|
||||
if (context.x86_64->rbp < thread.stack_region_address ||
|
||||
context.x86_64->rbp >=
|
||||
thread.stack_region_address + thread.stack_region_size) {
|
||||
MaybeCaptureMemoryAround(process_reader, context.x86_64->rbp, into);
|
||||
}
|
||||
MaybeCaptureMemoryAround(process_reader, context.x86_64->r8, into);
|
||||
MaybeCaptureMemoryAround(process_reader, context.x86_64->r9, into);
|
||||
MaybeCaptureMemoryAround(process_reader, context.x86_64->r10, into);
|
||||
MaybeCaptureMemoryAround(process_reader, context.x86_64->r11, into);
|
||||
MaybeCaptureMemoryAround(process_reader, context.x86_64->r12, into);
|
||||
MaybeCaptureMemoryAround(process_reader, context.x86_64->r13, into);
|
||||
MaybeCaptureMemoryAround(process_reader, context.x86_64->r14, into);
|
||||
MaybeCaptureMemoryAround(process_reader, context.x86_64->r15, into);
|
||||
MaybeCaptureMemoryAround(process_reader, context.x86_64->rip, into);
|
||||
} else {
|
||||
#endif
|
||||
MaybeCaptureMemoryAround(process_reader, context.x86->eax, into);
|
||||
MaybeCaptureMemoryAround(process_reader, context.x86->ebx, into);
|
||||
MaybeCaptureMemoryAround(process_reader, context.x86->ecx, into);
|
||||
MaybeCaptureMemoryAround(process_reader, context.x86->edx, into);
|
||||
MaybeCaptureMemoryAround(process_reader, context.x86->edi, into);
|
||||
MaybeCaptureMemoryAround(process_reader, context.x86->esi, into);
|
||||
if (context.x86->ebp < thread.stack_region_address ||
|
||||
context.x86->ebp >=
|
||||
thread.stack_region_address + thread.stack_region_size) {
|
||||
MaybeCaptureMemoryAround(process_reader, context.x86->ebp, into);
|
||||
}
|
||||
MaybeCaptureMemoryAround(process_reader, context.x86->eip, into);
|
||||
#if defined(ARCH_CPU_X86_64)
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace crashpad
|
@ -1,43 +0,0 @@
|
||||
// Copyright 2016 The Crashpad Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef CRASHPAD_SNAPSHOT_WIN_CAPTURE_CONTEXT_MEMORY_H_
|
||||
#define CRASHPAD_SNAPSHOT_WIN_CAPTURE_CONTEXT_MEMORY_H_
|
||||
|
||||
#include "snapshot/cpu_context.h"
|
||||
#include "snapshot/win/process_reader_win.h"
|
||||
#include "util/stdlib/pointer_container.h"
|
||||
|
||||
namespace crashpad {
|
||||
namespace internal {
|
||||
|
||||
class MemorySnapshotWin;
|
||||
|
||||
//! \brief For all registers that appear to be pointer-like in \a context,
|
||||
//! captures a small amount of memory near their pointed to location.
|
||||
//!
|
||||
//! \param[in] context The context to inspect.
|
||||
//! \param[in] process_reader A ProcessReaderWin to read from the target
|
||||
//! process.
|
||||
//! \param[in] thread The thread to which the context belongs.
|
||||
//! \param[out] into A vector of pointers to append new ranges to.
|
||||
void CaptureMemoryPointedToByContext(const CPUContext& context,
|
||||
ProcessReaderWin* process_reader,
|
||||
const ProcessReaderWin::Thread& thread,
|
||||
PointerVector<MemorySnapshotWin>* into);
|
||||
|
||||
} // namespace internal
|
||||
} // namespace crashpad
|
||||
|
||||
#endif // CRASHPAD_SNAPSHOT_WIN_CAPTURE_CONTEXT_MEMORY_H_
|
56
snapshot/win/capture_memory_delegate_win.cc
Normal file
56
snapshot/win/capture_memory_delegate_win.cc
Normal file
@ -0,0 +1,56 @@
|
||||
// Copyright 2016 The Crashpad Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "snapshot/win/capture_memory_delegate_win.h"
|
||||
|
||||
#include "snapshot/win/memory_snapshot_win.h"
|
||||
|
||||
namespace crashpad {
|
||||
namespace internal {
|
||||
|
||||
CaptureMemoryDelegateWin::CaptureMemoryDelegateWin(
|
||||
ProcessReaderWin* process_reader,
|
||||
const ProcessReaderWin::Thread& thread,
|
||||
PointerVector<MemorySnapshotWin>* snapshots)
|
||||
: stack_(thread.stack_region_address, thread.stack_region_size),
|
||||
process_reader_(process_reader),
|
||||
snapshots_(snapshots) {}
|
||||
|
||||
bool CaptureMemoryDelegateWin::Is64Bit() const {
|
||||
return process_reader_->Is64Bit();
|
||||
}
|
||||
|
||||
bool CaptureMemoryDelegateWin::ReadMemory(uint64_t at,
|
||||
uint64_t num_bytes,
|
||||
void* into) const {
|
||||
return process_reader_->ReadMemory(at, num_bytes, into);
|
||||
}
|
||||
|
||||
std::vector<CheckedRange<uint64_t>> CaptureMemoryDelegateWin::GetReadableRanges(
|
||||
const CheckedRange<uint64_t, uint64_t>& range) const {
|
||||
return process_reader_->GetProcessInfo().GetReadableRanges(range);
|
||||
}
|
||||
|
||||
void CaptureMemoryDelegateWin::AddNewMemorySnapshot(
|
||||
const CheckedRange<uint64_t, uint64_t>& range) {
|
||||
// Don't bother storing this memory if it points back into the stack.
|
||||
if (stack_.ContainsRange(range))
|
||||
return;
|
||||
internal::MemorySnapshotWin* snapshot = new internal::MemorySnapshotWin();
|
||||
snapshot->Initialize(process_reader_, range.base(), range.size());
|
||||
snapshots_->push_back(snapshot);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace crashpad
|
58
snapshot/win/capture_memory_delegate_win.h
Normal file
58
snapshot/win/capture_memory_delegate_win.h
Normal file
@ -0,0 +1,58 @@
|
||||
// Copyright 2016 The Crashpad Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef CRASHPAD_SNAPSHOT_WIN_CAPTURE_MEMORY_DELEGATE_WIN_H_
|
||||
#define CRASHPAD_SNAPSHOT_WIN_CAPTURE_MEMORY_DELEGATE_WIN_H_
|
||||
|
||||
#include "snapshot/capture_memory.h"
|
||||
|
||||
#include "snapshot/win/process_reader_win.h"
|
||||
#include "util/stdlib/pointer_container.h"
|
||||
|
||||
namespace crashpad {
|
||||
namespace internal {
|
||||
|
||||
class MemorySnapshotWin;
|
||||
|
||||
class CaptureMemoryDelegateWin : public CaptureMemory::Delegate {
|
||||
public:
|
||||
//! \brief A MemoryCaptureDelegate for Windows.
|
||||
//!
|
||||
//! \param[in] process_reader A ProcessReaderWin for the target process.
|
||||
//! \param[in] thread The thread being inspected. Memory ranges overlapping
|
||||
//! this thread's stack will be ignored on the assumption that they're
|
||||
//! already captured elsewhere.
|
||||
//! \param[in] snapshots A vector of MemorySnapshotWin to which the captured
|
||||
//! memory will be added.
|
||||
CaptureMemoryDelegateWin(ProcessReaderWin* process_reader,
|
||||
const ProcessReaderWin::Thread& thread,
|
||||
PointerVector<MemorySnapshotWin>* snapshots);
|
||||
|
||||
// MemoryCaptureDelegate:
|
||||
bool Is64Bit() const override;
|
||||
bool ReadMemory(uint64_t at, uint64_t num_bytes, void* into) const override;
|
||||
std::vector<CheckedRange<uint64_t>> GetReadableRanges(
|
||||
const CheckedRange<uint64_t, uint64_t>& range) const override;
|
||||
void AddNewMemorySnapshot(const CheckedRange<uint64_t, uint64_t>& range);
|
||||
|
||||
private:
|
||||
CheckedRange<uint64_t, uint64_t> stack_;
|
||||
ProcessReaderWin* process_reader_;
|
||||
PointerVector<MemorySnapshotWin>* snapshots_;
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
} // namespace crashpad
|
||||
|
||||
#endif // CRASHPAD_SNAPSHOT_WIN_CAPTURE_MEMORY_DELEGATE_WIN_H_
|
54
snapshot/win/crashpad_snapshot_test_extra_memory_ranges.cc
Normal file
54
snapshot/win/crashpad_snapshot_test_extra_memory_ranges.cc
Normal file
@ -0,0 +1,54 @@
|
||||
// Copyright 2016 The Crashpad Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "base/logging.h"
|
||||
#include "client/crashpad_info.h"
|
||||
#include "util/file/file_io.h"
|
||||
|
||||
|
||||
int wmain(int argc, wchar_t* argv[]) {
|
||||
using namespace crashpad;
|
||||
|
||||
CrashpadInfo* crashpad_info = CrashpadInfo::GetCrashpadInfo();
|
||||
|
||||
// This is "leaked" to crashpad_info.
|
||||
SimpleAddressRangeBag* extra_ranges = new SimpleAddressRangeBag();
|
||||
extra_ranges->Insert(CheckedRange<uint64_t>(0, 1));
|
||||
extra_ranges->Insert(CheckedRange<uint64_t>(1, 0));
|
||||
extra_ranges->Insert(CheckedRange<uint64_t>(0x1000000000ULL, 0x1000));
|
||||
extra_ranges->Insert(CheckedRange<uint64_t>(0x2000, 0x2000000000ULL));
|
||||
extra_ranges->Insert(CheckedRange<uint64_t>(1234, 5678));
|
||||
extra_ranges->Insert(CheckedRange<uint64_t>(1234, 5678));
|
||||
extra_ranges->Insert(CheckedRange<uint64_t>(1234, 5678));
|
||||
crashpad_info->set_extra_memory_ranges(extra_ranges);
|
||||
|
||||
// Tell the parent that the environment has been set up.
|
||||
HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
PCHECK(out != INVALID_HANDLE_VALUE) << "GetStdHandle";
|
||||
char c = ' ';
|
||||
CheckedWriteFile(out, &c, sizeof(c));
|
||||
|
||||
HANDLE in = GetStdHandle(STD_INPUT_HANDLE);
|
||||
PCHECK(in != INVALID_HANDLE_VALUE) << "GetStdHandle";
|
||||
CheckedReadFile(in, &c, sizeof(c));
|
||||
CHECK(c == 'd' || c == ' ');
|
||||
|
||||
// If 'd' we crash with a debug break, otherwise exit normally.
|
||||
if (c == 'd')
|
||||
__debugbreak();
|
||||
|
||||
return 0;
|
||||
}
|
@ -23,6 +23,7 @@ import sys
|
||||
import tempfile
|
||||
import time
|
||||
|
||||
|
||||
g_temp_dirs = []
|
||||
|
||||
|
||||
@ -235,6 +236,11 @@ def RunTests(cdb_path,
|
||||
out.Check(r'Event\s+\d+', 'capture some event handles')
|
||||
out.Check(r'File\s+\d+', 'capture some file handles')
|
||||
|
||||
out = CdbRun(cdb_path, dump_path, 'lm')
|
||||
out.Check(r'Unloaded modules:', 'captured some unloaded modules')
|
||||
out.Check(r'lz32\.dll', 'found expected unloaded module lz32')
|
||||
out.Check(r'wmerror\.dll', 'found expected unloaded module wmerror')
|
||||
|
||||
out = CdbRun(cdb_path, destroyed_dump_path, '.ecxr;!peb;k 2')
|
||||
out.Check(r'Ldr\.InMemoryOrderModuleList:.*\d+ \. \d+', 'PEB_LDR_DATA saved')
|
||||
out.Check(r'ntdll\.dll', 'ntdll present', re.IGNORECASE)
|
||||
@ -247,10 +253,49 @@ def RunTests(cdb_path,
|
||||
r'FreeOwnStackAndBreak.*\nquit:',
|
||||
'at correct location, no additional stack entries')
|
||||
|
||||
# Switch to the other thread after jumping to the exception, and examine
|
||||
# memory.
|
||||
out = CdbRun(cdb_path, dump_path, '.ecxr; ~1s; db /c14 edi')
|
||||
out.Check(r'63 62 61 60 5f 5e 5d 5c-5b 5a 59 58 57 56 55 54 53 52 51 50',
|
||||
'data pointed to by registers captured')
|
||||
|
||||
# Move up one stack frame after jumping to the exception, and examine memory.
|
||||
out = CdbRun(cdb_path, dump_path,
|
||||
'.ecxr; .f+; dd /c100 poi(offset_pointer)-20')
|
||||
out.Check(r'80000078 00000079 8000007a 0000007b 8000007c 0000007d 8000007e '
|
||||
r'0000007f 80000080 00000081 80000082 00000083 80000084 00000085 '
|
||||
r'80000086 00000087 80000088 00000089 8000008a 0000008b 8000008c '
|
||||
r'0000008d 8000008e 0000008f 80000090 00000091 80000092 00000093 '
|
||||
r'80000094 00000095 80000096 00000097',
|
||||
'data pointed to by stack captured')
|
||||
|
||||
# Attempt to retrieve the value of g_extra_memory_pointer (by name), and then
|
||||
# examine the memory at which it points. Both should have been saved.
|
||||
out = CdbRun(cdb_path, dump_path,
|
||||
'dd poi(crashy_program!crashpad::g_extra_memory_pointer)+0x1f30 '
|
||||
'L8')
|
||||
out.Check(r'0000655e 0000656b 00006578 00006585',
|
||||
'extra memory range captured')
|
||||
out.Check(r'\?\?\?\?\?\?\?\? \?\?\?\?\?\?\?\? '
|
||||
r'\?\?\?\?\?\?\?\? \?\?\?\?\?\?\?\?',
|
||||
' and not memory after range')
|
||||
|
||||
out = CdbRun(cdb_path, dump_path,
|
||||
'dd poi(crashy_program!crashpad::g_extra_memory_not_saved)'
|
||||
'+0x1f30 L4')
|
||||
# We save only the pointer, not the pointed-to data. If the pointer itself
|
||||
# wasn't saved, then we won't get any memory printed, so here we're confirming
|
||||
# the pointer was saved but the memory wasn't.
|
||||
out.Check(r'\?\?\?\?\?\?\?\? \?\?\?\?\?\?\?\? '
|
||||
r'\?\?\?\?\?\?\?\? \?\?\?\?\?\?\?\?',
|
||||
'extra memory removal')
|
||||
|
||||
out = CdbRun(cdb_path, dump_path, '.dumpdebug')
|
||||
out.Check(r'type \?\?\? \(333333\), size 00001000',
|
||||
'first user stream')
|
||||
out.Check(r'type \?\?\? \(222222\), size 00000080',
|
||||
'second user stream')
|
||||
|
||||
if z7_dump_path:
|
||||
out = CdbRun(cdb_path, z7_dump_path, '.ecxr;lm')
|
||||
out.Check('This dump file has an exception of interest stored in it',
|
||||
|
@ -14,9 +14,10 @@
|
||||
|
||||
#include "snapshot/win/exception_snapshot_win.h"
|
||||
|
||||
#include "snapshot/capture_memory.h"
|
||||
#include "snapshot/memory_snapshot.h"
|
||||
#include "snapshot/win/capture_context_memory.h"
|
||||
#include "snapshot/win/cpu_context_win.h"
|
||||
#include "snapshot/win/capture_memory_delegate_win.h"
|
||||
#include "snapshot/win/memory_snapshot_win.h"
|
||||
#include "snapshot/win/process_reader_win.h"
|
||||
#include "util/win/nt_internals.h"
|
||||
@ -89,8 +90,9 @@ bool ExceptionSnapshotWin::Initialize(ProcessReaderWin* process_reader,
|
||||
InitializeX86Context(context_record, context_.x86);
|
||||
}
|
||||
|
||||
CaptureMemoryPointedToByContext(
|
||||
context_, process_reader, *thread, &extra_memory_);
|
||||
CaptureMemoryDelegateWin capture_memory_delegate(
|
||||
process_reader, *thread, &extra_memory_);
|
||||
CaptureMemory::PointedToByContext(context_, &capture_memory_delegate);
|
||||
|
||||
INITIALIZATION_STATE_SET_VALID(initialized_);
|
||||
return true;
|
||||
|
133
snapshot/win/extra_memory_ranges_test.cc
Normal file
133
snapshot/win/extra_memory_ranges_test.cc
Normal file
@ -0,0 +1,133 @@
|
||||
// Copyright 2016 The Crashpad Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "snapshot/win/module_snapshot_win.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "base/files/file_path.h"
|
||||
#include "build/build_config.h"
|
||||
#include "client/crashpad_info.h"
|
||||
#include "client/simple_address_range_bag.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "snapshot/win/process_snapshot_win.h"
|
||||
#include "test/paths.h"
|
||||
#include "test/win/child_launcher.h"
|
||||
#include "util/file/file_io.h"
|
||||
#include "util/win/process_info.h"
|
||||
|
||||
namespace crashpad {
|
||||
namespace test {
|
||||
namespace {
|
||||
|
||||
enum TestType {
|
||||
// Don't crash, just test the CrashpadInfo interface.
|
||||
kDontCrash = 0,
|
||||
|
||||
// The child process should crash by __debugbreak().
|
||||
kCrashDebugBreak,
|
||||
};
|
||||
|
||||
void TestExtraMemoryRanges(TestType type,
|
||||
const base::string16& directory_modification) {
|
||||
// Spawn a child process, passing it the pipe name to connect to.
|
||||
base::FilePath test_executable = Paths::Executable();
|
||||
std::wstring child_test_executable =
|
||||
test_executable.DirName()
|
||||
.Append(directory_modification)
|
||||
.Append(test_executable.BaseName().RemoveFinalExtension().value() +
|
||||
L"_extra_memory_ranges.exe")
|
||||
.value();
|
||||
ChildLauncher child(child_test_executable, L"");
|
||||
child.Start();
|
||||
|
||||
// Wait for the child process to indicate that it's done setting up its
|
||||
// annotations via the CrashpadInfo interface.
|
||||
char c;
|
||||
CheckedReadFile(child.stdout_read_handle(), &c, sizeof(c));
|
||||
|
||||
ProcessSnapshotWin snapshot;
|
||||
ASSERT_TRUE(snapshot.Initialize(
|
||||
child.process_handle(), ProcessSuspensionState::kRunning, 0));
|
||||
|
||||
// Verify the extra memory ranges set via the CrashpadInfo interface.
|
||||
std::set<CheckedRange<uint64_t>> all_ranges;
|
||||
for (const auto* module : snapshot.Modules()) {
|
||||
for (const auto& range : module->ExtraMemoryRanges())
|
||||
all_ranges.insert(range);
|
||||
}
|
||||
|
||||
EXPECT_EQ(5u, all_ranges.size());
|
||||
EXPECT_NE(all_ranges.end(), all_ranges.find(CheckedRange<uint64_t>(0, 1)));
|
||||
EXPECT_NE(all_ranges.end(), all_ranges.find(CheckedRange<uint64_t>(1, 0)));
|
||||
EXPECT_NE(all_ranges.end(),
|
||||
all_ranges.find(CheckedRange<uint64_t>(1234, 5678)));
|
||||
EXPECT_NE(all_ranges.end(),
|
||||
all_ranges.find(CheckedRange<uint64_t>(0x1000000000ULL, 0x1000)));
|
||||
EXPECT_NE(all_ranges.end(),
|
||||
all_ranges.find(CheckedRange<uint64_t>(0x2000, 0x2000000000ULL)));
|
||||
|
||||
// Tell the child process to continue.
|
||||
DWORD expected_exit_code;
|
||||
switch (type) {
|
||||
case kDontCrash:
|
||||
c = ' ';
|
||||
expected_exit_code = 0;
|
||||
break;
|
||||
case kCrashDebugBreak:
|
||||
c = 'd';
|
||||
expected_exit_code = STATUS_BREAKPOINT;
|
||||
break;
|
||||
default:
|
||||
FAIL();
|
||||
}
|
||||
CheckedWriteFile(child.stdin_write_handle(), &c, sizeof(c));
|
||||
|
||||
EXPECT_EQ(expected_exit_code, child.WaitForExit());
|
||||
}
|
||||
|
||||
TEST(ExtraMemoryRanges, DontCrash) {
|
||||
TestExtraMemoryRanges(kDontCrash, FILE_PATH_LITERAL("."));
|
||||
}
|
||||
|
||||
TEST(ExtraMemoryRanges, CrashDebugBreak) {
|
||||
TestExtraMemoryRanges(kCrashDebugBreak, FILE_PATH_LITERAL("."));
|
||||
}
|
||||
|
||||
#if defined(ARCH_CPU_64_BITS)
|
||||
TEST(ExtraMemoryRanges, DontCrashWOW64) {
|
||||
#ifndef NDEBUG
|
||||
TestExtraMemoryRanges(kDontCrash, FILE_PATH_LITERAL("..\\..\\out\\Debug"));
|
||||
#else
|
||||
TestExtraMemoryRanges(kDontCrash, FILE_PATH_LITERAL("..\\..\\out\\Release"));
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST(ExtraMemoryRanges, CrashDebugBreakWOW64) {
|
||||
#ifndef NDEBUG
|
||||
TestExtraMemoryRanges(kCrashDebugBreak,
|
||||
FILE_PATH_LITERAL("..\\..\\out\\Debug"));
|
||||
#else
|
||||
TestExtraMemoryRanges(kCrashDebugBreak,
|
||||
FILE_PATH_LITERAL("..\\..\\out\\Release"));
|
||||
#endif
|
||||
}
|
||||
#endif // ARCH_CPU_64_BITS
|
||||
|
||||
} // namespace
|
||||
} // namespace test
|
||||
} // namespace crashpad
|
@ -15,6 +15,9 @@
|
||||
#include "snapshot/win/module_snapshot_win.h"
|
||||
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "client/crashpad_info.h"
|
||||
#include "client/simple_address_range_bag.h"
|
||||
#include "snapshot/win/memory_snapshot_win.h"
|
||||
#include "snapshot/win/pe_image_annotations_reader.h"
|
||||
#include "snapshot/win/pe_image_reader.h"
|
||||
#include "util/misc/tri_state.h"
|
||||
@ -185,6 +188,34 @@ std::map<std::string, std::string> ModuleSnapshotWin::AnnotationsSimpleMap()
|
||||
return annotations_reader.SimpleMap();
|
||||
}
|
||||
|
||||
std::set<CheckedRange<uint64_t>> ModuleSnapshotWin::ExtraMemoryRanges() const {
|
||||
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||
std::set<CheckedRange<uint64_t>> ranges;
|
||||
if (process_reader_->Is64Bit())
|
||||
GetCrashpadExtraMemoryRanges<process_types::internal::Traits64>(&ranges);
|
||||
else
|
||||
GetCrashpadExtraMemoryRanges<process_types::internal::Traits32>(&ranges);
|
||||
return ranges;
|
||||
}
|
||||
|
||||
std::vector<const UserMinidumpStream*>
|
||||
ModuleSnapshotWin::CustomMinidumpStreams() const {
|
||||
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||
streams_.clear();
|
||||
if (process_reader_->Is64Bit()) {
|
||||
GetCrashpadUserMinidumpStreams<process_types::internal::Traits64>(
|
||||
&streams_);
|
||||
} else {
|
||||
GetCrashpadUserMinidumpStreams<process_types::internal::Traits32>(
|
||||
&streams_);
|
||||
}
|
||||
|
||||
std::vector<const UserMinidumpStream*> result;
|
||||
for (const auto* stream : streams_)
|
||||
result.push_back(stream);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <class Traits>
|
||||
void ModuleSnapshotWin::GetCrashpadOptionsInternal(
|
||||
CrashpadInfoClientOptions* options) {
|
||||
@ -202,6 +233,10 @@ void ModuleSnapshotWin::GetCrashpadOptionsInternal(
|
||||
options->system_crash_reporter_forwarding =
|
||||
CrashpadInfoClientOptions::TriStateFromCrashpadInfo(
|
||||
crashpad_info.system_crash_reporter_forwarding);
|
||||
|
||||
options->gather_indirectly_referenced_memory =
|
||||
CrashpadInfoClientOptions::TriStateFromCrashpadInfo(
|
||||
crashpad_info.gather_indirectly_referenced_memory);
|
||||
}
|
||||
|
||||
const VS_FIXEDFILEINFO* ModuleSnapshotWin::VSFixedFileInfo() const {
|
||||
@ -218,5 +253,61 @@ const VS_FIXEDFILEINFO* ModuleSnapshotWin::VSFixedFileInfo() const {
|
||||
: nullptr;
|
||||
}
|
||||
|
||||
template <class Traits>
|
||||
void ModuleSnapshotWin::GetCrashpadExtraMemoryRanges(
|
||||
std::set<CheckedRange<uint64_t>>* ranges) const {
|
||||
process_types::CrashpadInfo<Traits> crashpad_info;
|
||||
if (!pe_image_reader_->GetCrashpadInfo(&crashpad_info))
|
||||
return;
|
||||
|
||||
if (!crashpad_info.extra_address_ranges)
|
||||
return;
|
||||
|
||||
std::vector<SimpleAddressRangeBag::Entry> simple_ranges(
|
||||
SimpleAddressRangeBag::num_entries);
|
||||
if (!process_reader_->ReadMemory(
|
||||
crashpad_info.extra_address_ranges,
|
||||
simple_ranges.size() * sizeof(simple_ranges[0]),
|
||||
&simple_ranges[0])) {
|
||||
LOG(WARNING) << "could not read simple address_ranges from "
|
||||
<< base::UTF16ToUTF8(name_);
|
||||
return;
|
||||
}
|
||||
|
||||
for (const auto& entry : simple_ranges) {
|
||||
if (entry.base != 0 || entry.size != 0) {
|
||||
// Deduplication here is fine.
|
||||
ranges->insert(CheckedRange<uint64_t>(entry.base, entry.size));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <class Traits>
|
||||
void ModuleSnapshotWin::GetCrashpadUserMinidumpStreams(
|
||||
PointerVector<const UserMinidumpStream>* streams) const {
|
||||
process_types::CrashpadInfo<Traits> crashpad_info;
|
||||
if (!pe_image_reader_->GetCrashpadInfo(&crashpad_info))
|
||||
return;
|
||||
|
||||
for (uint64_t cur = crashpad_info.user_data_minidump_stream_head; cur;) {
|
||||
internal::UserDataMinidumpStreamListEntry list_entry;
|
||||
if (!process_reader_->ReadMemory(
|
||||
cur, sizeof(list_entry), &list_entry)) {
|
||||
LOG(WARNING) << "could not read user data stream entry from "
|
||||
<< base::UTF16ToUTF8(name_);
|
||||
return;
|
||||
}
|
||||
|
||||
scoped_ptr<internal::MemorySnapshotWin> memory(
|
||||
new internal::MemorySnapshotWin());
|
||||
memory->Initialize(
|
||||
process_reader_, list_entry.base_address, list_entry.size);
|
||||
streams->push_back(
|
||||
new UserMinidumpStream(list_entry.stream_type, memory.release()));
|
||||
|
||||
cur = list_entry.next;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace crashpad
|
||||
|
@ -29,6 +29,7 @@
|
||||
#include "snapshot/win/process_reader_win.h"
|
||||
#include "util/misc/initialization_state.h"
|
||||
#include "util/misc/initialization_state_dcheck.h"
|
||||
#include "util/stdlib/pointer_container.h"
|
||||
#include "util/win/process_info.h"
|
||||
|
||||
namespace crashpad {
|
||||
@ -85,11 +86,21 @@ class ModuleSnapshotWin final : public ModuleSnapshot {
|
||||
std::string DebugFileName() const override;
|
||||
std::vector<std::string> AnnotationsVector() const override;
|
||||
std::map<std::string, std::string> AnnotationsSimpleMap() const override;
|
||||
std::set<CheckedRange<uint64_t>> ExtraMemoryRanges() const override;
|
||||
std::vector<const UserMinidumpStream*> CustomMinidumpStreams() const override;
|
||||
|
||||
private:
|
||||
template <class Traits>
|
||||
void GetCrashpadOptionsInternal(CrashpadInfoClientOptions* options);
|
||||
|
||||
template <class Traits>
|
||||
void GetCrashpadExtraMemoryRanges(
|
||||
std::set<CheckedRange<uint64_t>>* ranges) const;
|
||||
|
||||
template <class Traits>
|
||||
void GetCrashpadUserMinidumpStreams(
|
||||
PointerVector<const UserMinidumpStream>* streams) const;
|
||||
|
||||
// Initializes vs_fixed_file_info_ if it has not yet been initialized, and
|
||||
// returns a pointer to it. Returns nullptr on failure, with a message logged
|
||||
// on the first call.
|
||||
@ -102,6 +113,8 @@ class ModuleSnapshotWin final : public ModuleSnapshot {
|
||||
ProcessReaderWin* process_reader_; // weak
|
||||
time_t timestamp_;
|
||||
uint32_t age_;
|
||||
// Too const-y: https://crashpad.chromium.org/bug/9.
|
||||
mutable PointerVector<const UserMinidumpStream> streams_;
|
||||
InitializationStateDcheck initialized_;
|
||||
|
||||
// VSFixedFileInfo() is logically const, but updates these members on the
|
||||
|
@ -31,7 +31,6 @@
|
||||
#include "snapshot/win/process_reader_win.h"
|
||||
#include "test/paths.h"
|
||||
#include "test/win/child_launcher.h"
|
||||
#include "test/win/win_multiprocess.h"
|
||||
#include "util/file/file_io.h"
|
||||
#include "util/win/process_info.h"
|
||||
|
||||
|
@ -41,8 +41,11 @@ struct CrashpadInfo {
|
||||
uint32_t version;
|
||||
uint8_t crashpad_handler_behavior; // TriState.
|
||||
uint8_t system_crash_reporter_forwarding; // TriState.
|
||||
uint16_t padding_0;
|
||||
uint8_t gather_indirectly_referenced_memory; // TriState.
|
||||
uint8_t padding_0;
|
||||
typename Traits::Pointer extra_address_ranges;
|
||||
typename Traits::Pointer simple_annotations;
|
||||
typename Traits::Pointer user_data_minidump_stream_head;
|
||||
};
|
||||
|
||||
} // namespace process_types
|
||||
|
@ -22,7 +22,7 @@
|
||||
#include "gtest/gtest.h"
|
||||
#include "snapshot/win/process_reader_win.h"
|
||||
#include "test/errors.h"
|
||||
#include "util/win/get_function.h"
|
||||
#include "util/win/get_module_information.h"
|
||||
#include "util/win/module_version.h"
|
||||
#include "util/win/process_info.h"
|
||||
|
||||
@ -32,15 +32,6 @@ namespace crashpad {
|
||||
namespace test {
|
||||
namespace {
|
||||
|
||||
BOOL CrashpadGetModuleInformation(HANDLE process,
|
||||
HMODULE module,
|
||||
MODULEINFO* module_info,
|
||||
DWORD cb) {
|
||||
static const auto get_module_information =
|
||||
GET_FUNCTION_REQUIRED(L"psapi.dll", ::GetModuleInformation);
|
||||
return get_module_information(process, module, module_info, cb);
|
||||
}
|
||||
|
||||
TEST(PEImageReader, DebugDirectory) {
|
||||
PEImageReader pe_image_reader;
|
||||
ProcessReaderWin process_reader;
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "snapshot/win/memory_snapshot_win.h"
|
||||
#include "snapshot/win/module_snapshot_win.h"
|
||||
#include "util/win/nt_internals.h"
|
||||
#include "util/win/registration_protocol_win.h"
|
||||
#include "util/win/time.h"
|
||||
|
||||
@ -38,6 +39,7 @@ ProcessSnapshotWin::ProcessSnapshotWin()
|
||||
client_id_(),
|
||||
annotations_simple_map_(),
|
||||
snapshot_time_(),
|
||||
options_(),
|
||||
initialized_() {
|
||||
}
|
||||
|
||||
@ -65,14 +67,25 @@ bool ProcessSnapshotWin::Initialize(
|
||||
debug_critical_section_address);
|
||||
}
|
||||
|
||||
InitializeThreads();
|
||||
InitializeModules();
|
||||
InitializeUnloadedModules();
|
||||
|
||||
GetCrashpadOptionsInternal(&options_);
|
||||
|
||||
InitializeThreads(options_.gather_indirectly_referenced_memory ==
|
||||
TriState::kEnabled);
|
||||
|
||||
for (const MEMORY_BASIC_INFORMATION64& mbi :
|
||||
process_reader_.GetProcessInfo().MemoryInfo()) {
|
||||
memory_map_.push_back(new internal::MemoryMapRegionSnapshotWin(mbi));
|
||||
}
|
||||
|
||||
for (const auto& module : modules_) {
|
||||
for (const auto& range : module->ExtraMemoryRanges()) {
|
||||
AddMemorySnapshot(range.base(), range.size(), &extra_memory_);
|
||||
}
|
||||
}
|
||||
|
||||
INITIALIZATION_STATE_SET_VALID(initialized_);
|
||||
return true;
|
||||
}
|
||||
@ -104,31 +117,7 @@ bool ProcessSnapshotWin::InitializeException(
|
||||
void ProcessSnapshotWin::GetCrashpadOptions(
|
||||
CrashpadInfoClientOptions* options) {
|
||||
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||
|
||||
CrashpadInfoClientOptions local_options;
|
||||
|
||||
for (internal::ModuleSnapshotWin* module : modules_) {
|
||||
CrashpadInfoClientOptions module_options;
|
||||
module->GetCrashpadOptions(&module_options);
|
||||
|
||||
if (local_options.crashpad_handler_behavior == TriState::kUnset) {
|
||||
local_options.crashpad_handler_behavior =
|
||||
module_options.crashpad_handler_behavior;
|
||||
}
|
||||
if (local_options.system_crash_reporter_forwarding == TriState::kUnset) {
|
||||
local_options.system_crash_reporter_forwarding =
|
||||
module_options.system_crash_reporter_forwarding;
|
||||
}
|
||||
|
||||
// If non-default values have been found for all options, the loop can end
|
||||
// early.
|
||||
if (local_options.crashpad_handler_behavior != TriState::kUnset &&
|
||||
local_options.system_crash_reporter_forwarding != TriState::kUnset) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
*options = local_options;
|
||||
*options = options_;
|
||||
}
|
||||
|
||||
pid_t ProcessSnapshotWin::ProcessID() const {
|
||||
@ -196,6 +185,12 @@ std::vector<const ModuleSnapshot*> ProcessSnapshotWin::Modules() const {
|
||||
return modules;
|
||||
}
|
||||
|
||||
std::vector<UnloadedModuleSnapshot> ProcessSnapshotWin::UnloadedModules()
|
||||
const {
|
||||
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||
return unloaded_modules_;
|
||||
}
|
||||
|
||||
const ExceptionSnapshot* ProcessSnapshotWin::Exception() const {
|
||||
return exception_.get();
|
||||
}
|
||||
@ -234,13 +229,16 @@ std::vector<const MemorySnapshot*> ProcessSnapshotWin::ExtraMemory() const {
|
||||
return extra_memory;
|
||||
}
|
||||
|
||||
void ProcessSnapshotWin::InitializeThreads() {
|
||||
void ProcessSnapshotWin::InitializeThreads(
|
||||
bool gather_indirectly_referenced_memory) {
|
||||
const std::vector<ProcessReaderWin::Thread>& process_reader_threads =
|
||||
process_reader_.Threads();
|
||||
for (const ProcessReaderWin::Thread& process_reader_thread :
|
||||
process_reader_threads) {
|
||||
auto thread = make_scoped_ptr(new internal::ThreadSnapshotWin());
|
||||
if (thread->Initialize(&process_reader_, process_reader_thread)) {
|
||||
if (thread->Initialize(&process_reader_,
|
||||
process_reader_thread,
|
||||
gather_indirectly_referenced_memory)) {
|
||||
threads_.push_back(thread.release());
|
||||
}
|
||||
}
|
||||
@ -258,6 +256,86 @@ void ProcessSnapshotWin::InitializeModules() {
|
||||
}
|
||||
}
|
||||
|
||||
void ProcessSnapshotWin::InitializeUnloadedModules() {
|
||||
// As documented by https://msdn.microsoft.com/en-us/library/cc678403.aspx
|
||||
// we can retrieve the location for our unload events, and use that address in
|
||||
// the target process. Unfortunately, this of course only works for
|
||||
// 64-reading-64 and 32-reading-32, so at the moment, we simply do not
|
||||
// retrieve unloaded modules for 64-reading-32. See
|
||||
// https://crashpad.chromium.org/bug/89.
|
||||
|
||||
#if defined(ARCH_CPU_X86_64)
|
||||
if (!process_reader_.Is64Bit()) {
|
||||
LOG(ERROR)
|
||||
<< "reading unloaded modules across bitness not currently supported";
|
||||
return;
|
||||
}
|
||||
using Traits = process_types::internal::Traits64;
|
||||
#elif defined(ARCH_CPU_X86)
|
||||
using Traits = process_types::internal::Traits32;
|
||||
#else
|
||||
#error port
|
||||
#endif
|
||||
|
||||
RTL_UNLOAD_EVENT_TRACE<Traits>* unload_event_trace_address =
|
||||
RtlGetUnloadEventTrace<Traits>();
|
||||
WinVMAddress address_in_target_process =
|
||||
reinterpret_cast<WinVMAddress>(unload_event_trace_address);
|
||||
|
||||
std::vector<RTL_UNLOAD_EVENT_TRACE<Traits>> events(
|
||||
RTL_UNLOAD_EVENT_TRACE_NUMBER);
|
||||
if (!process_reader_.ReadMemory(address_in_target_process,
|
||||
events.size() * sizeof(events[0]),
|
||||
&events[0])) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const RTL_UNLOAD_EVENT_TRACE<Traits>& uet : events) {
|
||||
if (uet.ImageName[0]) {
|
||||
unloaded_modules_.push_back(UnloadedModuleSnapshot(
|
||||
uet.BaseAddress,
|
||||
uet.SizeOfImage,
|
||||
uet.CheckSum,
|
||||
uet.TimeDateStamp,
|
||||
base::UTF16ToUTF8(
|
||||
base::StringPiece16(uet.ImageName, arraysize(uet.ImageName)))));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ProcessSnapshotWin::GetCrashpadOptionsInternal(
|
||||
CrashpadInfoClientOptions* options) {
|
||||
CrashpadInfoClientOptions local_options;
|
||||
|
||||
for (internal::ModuleSnapshotWin* module : modules_) {
|
||||
CrashpadInfoClientOptions module_options;
|
||||
module->GetCrashpadOptions(&module_options);
|
||||
|
||||
if (local_options.crashpad_handler_behavior == TriState::kUnset) {
|
||||
local_options.crashpad_handler_behavior =
|
||||
module_options.crashpad_handler_behavior;
|
||||
}
|
||||
if (local_options.system_crash_reporter_forwarding == TriState::kUnset) {
|
||||
local_options.system_crash_reporter_forwarding =
|
||||
module_options.system_crash_reporter_forwarding;
|
||||
}
|
||||
if (local_options.gather_indirectly_referenced_memory == TriState::kUnset) {
|
||||
local_options.gather_indirectly_referenced_memory =
|
||||
module_options.gather_indirectly_referenced_memory;
|
||||
}
|
||||
|
||||
// If non-default values have been found for all options, the loop can end
|
||||
// early.
|
||||
if (local_options.crashpad_handler_behavior != TriState::kUnset &&
|
||||
local_options.system_crash_reporter_forwarding != TriState::kUnset &&
|
||||
local_options.gather_indirectly_referenced_memory != TriState::kUnset) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
*options = local_options;
|
||||
}
|
||||
|
||||
template <class Traits>
|
||||
void ProcessSnapshotWin::InitializePebData(
|
||||
WinVMAddress debug_critical_section_address) {
|
||||
|
@ -34,6 +34,7 @@
|
||||
#include "snapshot/process_snapshot.h"
|
||||
#include "snapshot/system_snapshot.h"
|
||||
#include "snapshot/thread_snapshot.h"
|
||||
#include "snapshot/unloaded_module_snapshot.h"
|
||||
#include "snapshot/win/exception_snapshot_win.h"
|
||||
#include "snapshot/win/memory_map_region_snapshot_win.h"
|
||||
#include "snapshot/win/memory_snapshot_win.h"
|
||||
@ -134,6 +135,7 @@ class ProcessSnapshotWin final : public ProcessSnapshot {
|
||||
const SystemSnapshot* System() const override;
|
||||
std::vector<const ThreadSnapshot*> Threads() const override;
|
||||
std::vector<const ModuleSnapshot*> Modules() const override;
|
||||
std::vector<UnloadedModuleSnapshot> UnloadedModules() const override;
|
||||
const ExceptionSnapshot* Exception() const override;
|
||||
std::vector<const MemoryMapRegionSnapshot*> MemoryMap() const override;
|
||||
std::vector<HandleSnapshot> Handles() const override;
|
||||
@ -141,11 +143,17 @@ class ProcessSnapshotWin final : public ProcessSnapshot {
|
||||
|
||||
private:
|
||||
// Initializes threads_ on behalf of Initialize().
|
||||
void InitializeThreads();
|
||||
void InitializeThreads(bool gather_indirectly_referenced_memory);
|
||||
|
||||
// Initializes modules_ on behalf of Initialize().
|
||||
void InitializeModules();
|
||||
|
||||
// Initializes unloaded_modules_ on behalf of Initialize().
|
||||
void InitializeUnloadedModules();
|
||||
|
||||
// Initializes options_ on behalf of Initialize().
|
||||
void GetCrashpadOptionsInternal(CrashpadInfoClientOptions* options);
|
||||
|
||||
// Initializes various memory blocks reachable from the PEB on behalf of
|
||||
// Initialize().
|
||||
template <class Traits>
|
||||
@ -179,6 +187,7 @@ class ProcessSnapshotWin final : public ProcessSnapshot {
|
||||
PointerVector<internal::MemorySnapshotWin> extra_memory_;
|
||||
PointerVector<internal::ThreadSnapshotWin> threads_;
|
||||
PointerVector<internal::ModuleSnapshotWin> modules_;
|
||||
std::vector<UnloadedModuleSnapshot> unloaded_modules_;
|
||||
scoped_ptr<internal::ExceptionSnapshotWin> exception_;
|
||||
PointerVector<internal::MemoryMapRegionSnapshotWin> memory_map_;
|
||||
ProcessReaderWin process_reader_;
|
||||
@ -186,6 +195,7 @@ class ProcessSnapshotWin final : public ProcessSnapshot {
|
||||
UUID client_id_;
|
||||
std::map<std::string, std::string> annotations_simple_map_;
|
||||
timeval snapshot_time_;
|
||||
CrashpadInfoClientOptions options_;
|
||||
InitializationStateDcheck initialized_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(ProcessSnapshotWin);
|
||||
|
@ -17,8 +17,9 @@
|
||||
#include <vector>
|
||||
|
||||
#include "base/logging.h"
|
||||
#include "snapshot/win/capture_context_memory.h"
|
||||
#include "snapshot/capture_memory.h"
|
||||
#include "snapshot/win/cpu_context_win.h"
|
||||
#include "snapshot/win/capture_memory_delegate_win.h"
|
||||
#include "snapshot/win/process_reader_win.h"
|
||||
|
||||
namespace crashpad {
|
||||
@ -38,7 +39,8 @@ ThreadSnapshotWin::~ThreadSnapshotWin() {
|
||||
|
||||
bool ThreadSnapshotWin::Initialize(
|
||||
ProcessReaderWin* process_reader,
|
||||
const ProcessReaderWin::Thread& process_reader_thread) {
|
||||
const ProcessReaderWin::Thread& process_reader_thread,
|
||||
bool gather_indirectly_referenced_memory) {
|
||||
INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
|
||||
|
||||
thread_ = process_reader_thread;
|
||||
@ -76,8 +78,11 @@ bool ThreadSnapshotWin::Initialize(
|
||||
InitializeX86Context(process_reader_thread.context.native, context_.x86);
|
||||
#endif // ARCH_CPU_X86_64
|
||||
|
||||
CaptureMemoryPointedToByContext(
|
||||
context_, process_reader, thread_, &pointed_to_memory_);
|
||||
CaptureMemoryDelegateWin capture_memory_delegate(
|
||||
process_reader, thread_, &pointed_to_memory_);
|
||||
CaptureMemory::PointedToByContext(context_, &capture_memory_delegate);
|
||||
if (gather_indirectly_referenced_memory)
|
||||
CaptureMemory::PointedToByMemoryRange(stack_, &capture_memory_delegate);
|
||||
|
||||
INITIALIZATION_STATE_SET_VALID(initialized_);
|
||||
return true;
|
||||
|
@ -48,11 +48,14 @@ class ThreadSnapshotWin final : public ThreadSnapshot {
|
||||
//! the thread.
|
||||
//! \param[in] process_reader_thread The thread within the ProcessReaderWin
|
||||
//! for which the snapshot should be created.
|
||||
//! \param[in] gather_indirectly_referenced_memory If `true`, adds extra
|
||||
//! memory regions to the snapshot pointed to by the thread's stack.
|
||||
//!
|
||||
//! \return `true` if the snapshot could be created, `false` otherwise with
|
||||
//! an appropriate message logged.
|
||||
bool Initialize(ProcessReaderWin* process_reader,
|
||||
const ProcessReaderWin::Thread& process_reader_thread);
|
||||
const ProcessReaderWin::Thread& process_reader_thread,
|
||||
bool gather_indirectly_referenced_memory);
|
||||
|
||||
// ThreadSnapshot:
|
||||
|
||||
|
107
third_party/gtest/gmock.gyp
vendored
107
third_party/gtest/gmock.gyp
vendored
@ -14,10 +14,19 @@
|
||||
|
||||
{
|
||||
'includes': [
|
||||
'../../build/crashpad_in_chromium.gypi',
|
||||
'../../build/crashpad_dependencies.gypi',
|
||||
],
|
||||
'conditions': [
|
||||
['crashpad_in_chromium==0', {
|
||||
['crashpad_dependencies!="chromium"', {
|
||||
'variables': {
|
||||
'conditions': [
|
||||
['crashpad_dependencies=="standalone"', {
|
||||
'gmock_dir': 'gtest/googlemock',
|
||||
}, {
|
||||
'gmock_dir': '../../../../gmock',
|
||||
}],
|
||||
],
|
||||
},
|
||||
'target_defaults': {
|
||||
# gmock relies heavily on objects with static storage duration.
|
||||
'xcode_settings': {
|
||||
@ -38,41 +47,41 @@
|
||||
'gtest.gyp:gtest',
|
||||
],
|
||||
'include_dirs': [
|
||||
'gtest/googlemock',
|
||||
'gtest/googlemock/include',
|
||||
'<(gmock_dir)',
|
||||
'<(gmock_dir)/include',
|
||||
],
|
||||
'sources': [
|
||||
'gtest/googlemock/include/gmock/gmock-actions.h',
|
||||
'gtest/googlemock/include/gmock/gmock-cardinalities.h',
|
||||
'gtest/googlemock/include/gmock/gmock-generated-actions.h',
|
||||
'gtest/googlemock/include/gmock/gmock-generated-function-mockers.h',
|
||||
'gtest/googlemock/include/gmock/gmock-generated-matchers.h',
|
||||
'gtest/googlemock/include/gmock/gmock-generated-nice-strict.h',
|
||||
'gtest/googlemock/include/gmock/gmock-matchers.h',
|
||||
'gtest/googlemock/include/gmock/gmock-more-actions.h',
|
||||
'gtest/googlemock/include/gmock/gmock-more-matchers.h',
|
||||
'gtest/googlemock/include/gmock/gmock-spec-builders.h',
|
||||
'gtest/googlemock/include/gmock/gmock.h',
|
||||
'gtest/googlemock/include/gmock/internal/custom/gmock-generated-actions.h',
|
||||
'gtest/googlemock/include/gmock/internal/custom/gmock-matchers.h',
|
||||
'gtest/googlemock/include/gmock/internal/custom/gmock-port.h',
|
||||
'gtest/googlemock/include/gmock/internal/gmock-generated-internal-utils.h',
|
||||
'gtest/googlemock/include/gmock/internal/gmock-internal-utils.h',
|
||||
'gtest/googlemock/include/gmock/internal/gmock-port.h',
|
||||
'gtest/googlemock/src/gmock-all.cc',
|
||||
'gtest/googlemock/src/gmock-cardinalities.cc',
|
||||
'gtest/googlemock/src/gmock-internal-utils.cc',
|
||||
'gtest/googlemock/src/gmock-matchers.cc',
|
||||
'gtest/googlemock/src/gmock-spec-builders.cc',
|
||||
'gtest/googlemock/src/gmock.cc',
|
||||
'<(gmock_dir)/include/gmock/gmock-actions.h',
|
||||
'<(gmock_dir)/include/gmock/gmock-cardinalities.h',
|
||||
'<(gmock_dir)/include/gmock/gmock-generated-actions.h',
|
||||
'<(gmock_dir)/include/gmock/gmock-generated-function-mockers.h',
|
||||
'<(gmock_dir)/include/gmock/gmock-generated-matchers.h',
|
||||
'<(gmock_dir)/include/gmock/gmock-generated-nice-strict.h',
|
||||
'<(gmock_dir)/include/gmock/gmock-matchers.h',
|
||||
'<(gmock_dir)/include/gmock/gmock-more-actions.h',
|
||||
'<(gmock_dir)/include/gmock/gmock-more-matchers.h',
|
||||
'<(gmock_dir)/include/gmock/gmock-spec-builders.h',
|
||||
'<(gmock_dir)/include/gmock/gmock.h',
|
||||
'<(gmock_dir)/include/gmock/internal/custom/gmock-generated-actions.h',
|
||||
'<(gmock_dir)/include/gmock/internal/custom/gmock-matchers.h',
|
||||
'<(gmock_dir)/include/gmock/internal/custom/gmock-port.h',
|
||||
'<(gmock_dir)/include/gmock/internal/gmock-generated-internal-utils.h',
|
||||
'<(gmock_dir)/include/gmock/internal/gmock-internal-utils.h',
|
||||
'<(gmock_dir)/include/gmock/internal/gmock-port.h',
|
||||
'<(gmock_dir)/src/gmock-all.cc',
|
||||
'<(gmock_dir)/src/gmock-cardinalities.cc',
|
||||
'<(gmock_dir)/src/gmock-internal-utils.cc',
|
||||
'<(gmock_dir)/src/gmock-matchers.cc',
|
||||
'<(gmock_dir)/src/gmock-spec-builders.cc',
|
||||
'<(gmock_dir)/src/gmock.cc',
|
||||
],
|
||||
'sources!': [
|
||||
'gtest/googlemock/src/gmock-all.cc',
|
||||
'<(gmock_dir)/src/gmock-all.cc',
|
||||
],
|
||||
|
||||
'direct_dependent_settings': {
|
||||
'include_dirs': [
|
||||
'gtest/googlemock/include',
|
||||
'<(gmock_dir)/include',
|
||||
],
|
||||
'conditions': [
|
||||
['clang!=0', {
|
||||
@ -114,7 +123,7 @@
|
||||
'gtest.gyp:gtest',
|
||||
],
|
||||
'sources': [
|
||||
'gtest/googlemock/src/gmock_main.cc',
|
||||
'<(gmock_dir)/src/gmock_main.cc',
|
||||
],
|
||||
},
|
||||
{
|
||||
@ -127,7 +136,7 @@
|
||||
'direct_dependent_settings': {
|
||||
'type': 'executable',
|
||||
'include_dirs': [
|
||||
'gtest/googlemock',
|
||||
'<(gmock_dir)',
|
||||
],
|
||||
},
|
||||
'export_dependent_settings': [
|
||||
@ -145,19 +154,19 @@
|
||||
'gtest/googletest',
|
||||
],
|
||||
'sources': [
|
||||
'gtest/googlemock/test/gmock-actions_test.cc',
|
||||
'gtest/googlemock/test/gmock-cardinalities_test.cc',
|
||||
'gtest/googlemock/test/gmock-generated-actions_test.cc',
|
||||
'gtest/googlemock/test/gmock-generated-function-mockers_test.cc',
|
||||
'gtest/googlemock/test/gmock-generated-internal-utils_test.cc',
|
||||
'gtest/googlemock/test/gmock-generated-matchers_test.cc',
|
||||
'gtest/googlemock/test/gmock-internal-utils_test.cc',
|
||||
'gtest/googlemock/test/gmock-matchers_test.cc',
|
||||
'gtest/googlemock/test/gmock-more-actions_test.cc',
|
||||
'gtest/googlemock/test/gmock-nice-strict_test.cc',
|
||||
'gtest/googlemock/test/gmock-port_test.cc',
|
||||
'gtest/googlemock/test/gmock-spec-builders_test.cc',
|
||||
'gtest/googlemock/test/gmock_test.cc',
|
||||
'<(gmock_dir)/test/gmock-actions_test.cc',
|
||||
'<(gmock_dir)/test/gmock-cardinalities_test.cc',
|
||||
'<(gmock_dir)/test/gmock-generated-actions_test.cc',
|
||||
'<(gmock_dir)/test/gmock-generated-function-mockers_test.cc',
|
||||
'<(gmock_dir)/test/gmock-generated-internal-utils_test.cc',
|
||||
'<(gmock_dir)/test/gmock-generated-matchers_test.cc',
|
||||
'<(gmock_dir)/test/gmock-internal-utils_test.cc',
|
||||
'<(gmock_dir)/test/gmock-matchers_test.cc',
|
||||
'<(gmock_dir)/test/gmock-more-actions_test.cc',
|
||||
'<(gmock_dir)/test/gmock-nice-strict_test.cc',
|
||||
'<(gmock_dir)/test/gmock-port_test.cc',
|
||||
'<(gmock_dir)/test/gmock-spec-builders_test.cc',
|
||||
'<(gmock_dir)/test/gmock_test.cc',
|
||||
],
|
||||
},
|
||||
{
|
||||
@ -167,9 +176,9 @@
|
||||
'gmock_main',
|
||||
],
|
||||
'sources': [
|
||||
'gtest/googlemock/test/gmock_link_test.cc',
|
||||
'gtest/googlemock/test/gmock_link_test.h',
|
||||
'gtest/googlemock/test/gmock_link2_test.cc',
|
||||
'<(gmock_dir)/test/gmock_link_test.cc',
|
||||
'<(gmock_dir)/test/gmock_link_test.h',
|
||||
'<(gmock_dir)/test/gmock_link2_test.cc',
|
||||
],
|
||||
},
|
||||
{
|
||||
@ -178,7 +187,7 @@
|
||||
'gmock_test_executable',
|
||||
],
|
||||
'sources': [
|
||||
'gtest/googlemock/test/gmock_stress_test.cc',
|
||||
'<(gmock_dir)/test/gmock_stress_test.cc',
|
||||
],
|
||||
},
|
||||
{
|
||||
@ -191,7 +200,7 @@
|
||||
],
|
||||
},
|
||||
],
|
||||
}, { # else: crashpad_in_chromium!=0
|
||||
}, { # else: crashpad_dependencies=="chromium"
|
||||
'targets': [
|
||||
{
|
||||
'target_name': 'gmock',
|
||||
|
168
third_party/gtest/gtest.gyp
vendored
168
third_party/gtest/gtest.gyp
vendored
@ -14,10 +14,19 @@
|
||||
|
||||
{
|
||||
'includes': [
|
||||
'../../build/crashpad_in_chromium.gypi',
|
||||
'../../build/crashpad_dependencies.gypi',
|
||||
],
|
||||
'conditions': [
|
||||
['crashpad_in_chromium==0', {
|
||||
['crashpad_dependencies!="chromium"', {
|
||||
'variables': {
|
||||
'conditions': [
|
||||
['crashpad_dependencies=="standalone"', {
|
||||
'gtest_dir': 'gtest/googletest',
|
||||
}, {
|
||||
'gtest_dir': '../../../../gtest',
|
||||
}],
|
||||
],
|
||||
},
|
||||
'target_defaults': {
|
||||
# gtest relies heavily on objects with static storage duration.
|
||||
'xcode_settings': {
|
||||
@ -35,53 +44,70 @@
|
||||
'target_name': 'gtest',
|
||||
'type': 'static_library',
|
||||
'include_dirs': [
|
||||
'gtest/googletest',
|
||||
'gtest/googletest/include',
|
||||
'<(gtest_dir)',
|
||||
'<(gtest_dir)/include',
|
||||
],
|
||||
'sources': [
|
||||
'gtest/googletest/include/gtest/gtest-death-test.h',
|
||||
'gtest/googletest/include/gtest/gtest-message.h',
|
||||
'gtest/googletest/include/gtest/gtest-param-test.h',
|
||||
'gtest/googletest/include/gtest/gtest-printers.h',
|
||||
'gtest/googletest/include/gtest/gtest-spi.h',
|
||||
'gtest/googletest/include/gtest/gtest-test-part.h',
|
||||
'gtest/googletest/include/gtest/gtest-typed-test.h',
|
||||
'gtest/googletest/include/gtest/gtest.h',
|
||||
'gtest/googletest/include/gtest/gtest_pred_impl.h',
|
||||
'gtest/googletest/include/gtest/gtest_prod.h',
|
||||
'gtest/googletest/include/gtest/internal/custom/gtest-port.h',
|
||||
'gtest/googletest/include/gtest/internal/custom/gtest-printers.h',
|
||||
'gtest/googletest/include/gtest/internal/custom/gtest.h',
|
||||
'gtest/googletest/include/gtest/internal/gtest-death-test-internal.h',
|
||||
'gtest/googletest/include/gtest/internal/gtest-filepath.h',
|
||||
'gtest/googletest/include/gtest/internal/gtest-internal.h',
|
||||
'gtest/googletest/include/gtest/internal/gtest-linked_ptr.h',
|
||||
'gtest/googletest/include/gtest/internal/gtest-param-util-generated.h',
|
||||
'gtest/googletest/include/gtest/internal/gtest-param-util.h',
|
||||
'gtest/googletest/include/gtest/internal/gtest-port-arch.h',
|
||||
'gtest/googletest/include/gtest/internal/gtest-port.h',
|
||||
'gtest/googletest/include/gtest/internal/gtest-string.h',
|
||||
'gtest/googletest/include/gtest/internal/gtest-tuple.h',
|
||||
'gtest/googletest/include/gtest/internal/gtest-type-util.h',
|
||||
'gtest/googletest/src/gtest-all.cc',
|
||||
'gtest/googletest/src/gtest-death-test.cc',
|
||||
'gtest/googletest/src/gtest-filepath.cc',
|
||||
'gtest/googletest/src/gtest-internal-inl.h',
|
||||
'gtest/googletest/src/gtest-port.cc',
|
||||
'gtest/googletest/src/gtest-printers.cc',
|
||||
'gtest/googletest/src/gtest-test-part.cc',
|
||||
'gtest/googletest/src/gtest-typed-test.cc',
|
||||
'gtest/googletest/src/gtest.cc',
|
||||
'<(gtest_dir)/include/gtest/gtest-death-test.h',
|
||||
'<(gtest_dir)/include/gtest/gtest-message.h',
|
||||
'<(gtest_dir)/include/gtest/gtest-param-test.h',
|
||||
'<(gtest_dir)/include/gtest/gtest-printers.h',
|
||||
'<(gtest_dir)/include/gtest/gtest-spi.h',
|
||||
'<(gtest_dir)/include/gtest/gtest-test-part.h',
|
||||
'<(gtest_dir)/include/gtest/gtest-typed-test.h',
|
||||
'<(gtest_dir)/include/gtest/gtest.h',
|
||||
'<(gtest_dir)/include/gtest/gtest_pred_impl.h',
|
||||
'<(gtest_dir)/include/gtest/gtest_prod.h',
|
||||
'<(gtest_dir)/include/gtest/internal/custom/gtest-port.h',
|
||||
'<(gtest_dir)/include/gtest/internal/custom/gtest-printers.h',
|
||||
'<(gtest_dir)/include/gtest/internal/custom/gtest.h',
|
||||
'<(gtest_dir)/include/gtest/internal/gtest-death-test-internal.h',
|
||||
'<(gtest_dir)/include/gtest/internal/gtest-filepath.h',
|
||||
'<(gtest_dir)/include/gtest/internal/gtest-internal.h',
|
||||
'<(gtest_dir)/include/gtest/internal/gtest-linked_ptr.h',
|
||||
'<(gtest_dir)/include/gtest/internal/gtest-param-util-generated.h',
|
||||
'<(gtest_dir)/include/gtest/internal/gtest-param-util.h',
|
||||
'<(gtest_dir)/include/gtest/internal/gtest-port-arch.h',
|
||||
'<(gtest_dir)/include/gtest/internal/gtest-port.h',
|
||||
'<(gtest_dir)/include/gtest/internal/gtest-string.h',
|
||||
'<(gtest_dir)/include/gtest/internal/gtest-tuple.h',
|
||||
'<(gtest_dir)/include/gtest/internal/gtest-type-util.h',
|
||||
'<(gtest_dir)/src/gtest-all.cc',
|
||||
'<(gtest_dir)/src/gtest-death-test.cc',
|
||||
'<(gtest_dir)/src/gtest-filepath.cc',
|
||||
'<(gtest_dir)/src/gtest-internal-inl.h',
|
||||
'<(gtest_dir)/src/gtest-port.cc',
|
||||
'<(gtest_dir)/src/gtest-printers.cc',
|
||||
'<(gtest_dir)/src/gtest-test-part.cc',
|
||||
'<(gtest_dir)/src/gtest-typed-test.cc',
|
||||
'<(gtest_dir)/src/gtest.cc',
|
||||
],
|
||||
'sources!': [
|
||||
'gtest/googletest/src/gtest-all.cc',
|
||||
'<(gtest_dir)/src/gtest-all.cc',
|
||||
],
|
||||
|
||||
'direct_dependent_settings': {
|
||||
'include_dirs': [
|
||||
'gtest/googletest/include',
|
||||
'<(gtest_dir)/include',
|
||||
],
|
||||
},
|
||||
'conditions': [
|
||||
['crashpad_dependencies=="external"', {
|
||||
'include_dirs': [
|
||||
'<(gtest_dir)/../..',
|
||||
],
|
||||
'defines': [
|
||||
'GUNIT_NO_GOOGLE3=1',
|
||||
],
|
||||
'direct_dependent_settings': {
|
||||
'include_dirs': [
|
||||
'<(gtest_dir)/../..',
|
||||
],
|
||||
'defines': [
|
||||
'GUNIT_NO_GOOGLE3=1',
|
||||
],
|
||||
},
|
||||
}],
|
||||
],
|
||||
},
|
||||
{
|
||||
'target_name': 'gtest_main',
|
||||
@ -90,7 +116,7 @@
|
||||
'gtest',
|
||||
],
|
||||
'sources': [
|
||||
'gtest/googletest/src/gtest_main.cc',
|
||||
'<(gtest_dir)/src/gtest_main.cc',
|
||||
],
|
||||
},
|
||||
{
|
||||
@ -102,7 +128,7 @@
|
||||
'direct_dependent_settings': {
|
||||
'type': 'executable',
|
||||
'include_dirs': [
|
||||
'gtest/googletest',
|
||||
'<(gtest_dir)',
|
||||
],
|
||||
},
|
||||
'export_dependent_settings': [
|
||||
@ -116,23 +142,23 @@
|
||||
'gtest_main',
|
||||
],
|
||||
'sources': [
|
||||
'gtest/googletest/test/gtest-death-test_test.cc',
|
||||
'gtest/googletest/test/gtest-filepath_test.cc',
|
||||
'gtest/googletest/test/gtest-linked_ptr_test.cc',
|
||||
'gtest/googletest/test/gtest-message_test.cc',
|
||||
'gtest/googletest/test/gtest-options_test.cc',
|
||||
'gtest/googletest/test/gtest-port_test.cc',
|
||||
'gtest/googletest/test/gtest-printers_test.cc',
|
||||
'gtest/googletest/test/gtest-test-part_test.cc',
|
||||
'gtest/googletest/test/gtest-typed-test2_test.cc',
|
||||
'gtest/googletest/test/gtest-typed-test_test.cc',
|
||||
'gtest/googletest/test/gtest-typed-test_test.h',
|
||||
'gtest/googletest/test/gtest_main_unittest.cc',
|
||||
'gtest/googletest/test/gtest_pred_impl_unittest.cc',
|
||||
'gtest/googletest/test/gtest_prod_test.cc',
|
||||
'gtest/googletest/test/gtest_unittest.cc',
|
||||
'gtest/googletest/test/production.cc',
|
||||
'gtest/googletest/test/production.h',
|
||||
'<(gtest_dir)/test/gtest-death-test_test.cc',
|
||||
'<(gtest_dir)/test/gtest-filepath_test.cc',
|
||||
'<(gtest_dir)/test/gtest-linked_ptr_test.cc',
|
||||
'<(gtest_dir)/test/gtest-message_test.cc',
|
||||
'<(gtest_dir)/test/gtest-options_test.cc',
|
||||
'<(gtest_dir)/test/gtest-port_test.cc',
|
||||
'<(gtest_dir)/test/gtest-printers_test.cc',
|
||||
'<(gtest_dir)/test/gtest-test-part_test.cc',
|
||||
'<(gtest_dir)/test/gtest-typed-test2_test.cc',
|
||||
'<(gtest_dir)/test/gtest-typed-test_test.cc',
|
||||
'<(gtest_dir)/test/gtest-typed-test_test.h',
|
||||
'<(gtest_dir)/test/gtest_main_unittest.cc',
|
||||
'<(gtest_dir)/test/gtest_pred_impl_unittest.cc',
|
||||
'<(gtest_dir)/test/gtest_prod_test.cc',
|
||||
'<(gtest_dir)/test/gtest_unittest.cc',
|
||||
'<(gtest_dir)/test/production.cc',
|
||||
'<(gtest_dir)/test/production.h',
|
||||
],
|
||||
},
|
||||
{
|
||||
@ -141,7 +167,7 @@
|
||||
'gtest_test_executable',
|
||||
],
|
||||
'sources': [
|
||||
'gtest/googletest/test/gtest_environment_test.cc',
|
||||
'<(gtest_dir)/test/gtest_environment_test.cc',
|
||||
],
|
||||
},
|
||||
{
|
||||
@ -150,7 +176,7 @@
|
||||
'gtest_test_executable',
|
||||
],
|
||||
'sources': [
|
||||
'gtest/googletest/test/gtest-listener_test.cc',
|
||||
'<(gtest_dir)/test/gtest-listener_test.cc',
|
||||
],
|
||||
},
|
||||
{
|
||||
@ -159,7 +185,7 @@
|
||||
'gtest_test_executable',
|
||||
],
|
||||
'sources': [
|
||||
'gtest/googletest/test/gtest_no_test_unittest.cc',
|
||||
'<(gtest_dir)/test/gtest_no_test_unittest.cc',
|
||||
],
|
||||
},
|
||||
{
|
||||
@ -168,9 +194,9 @@
|
||||
'gtest_test_executable',
|
||||
],
|
||||
'sources': [
|
||||
'gtest/googletest/test/gtest-param-test2_test.cc',
|
||||
'gtest/googletest/test/gtest-param-test_test.cc',
|
||||
'gtest/googletest/test/gtest-param-test_test.h',
|
||||
'<(gtest_dir)/test/gtest-param-test2_test.cc',
|
||||
'<(gtest_dir)/test/gtest-param-test_test.cc',
|
||||
'<(gtest_dir)/test/gtest-param-test_test.h',
|
||||
],
|
||||
},
|
||||
{
|
||||
@ -179,7 +205,7 @@
|
||||
'gtest_test_executable',
|
||||
],
|
||||
'sources': [
|
||||
'gtest/googletest/test/gtest_premature_exit_test.cc',
|
||||
'<(gtest_dir)/test/gtest_premature_exit_test.cc',
|
||||
],
|
||||
},
|
||||
{
|
||||
@ -188,7 +214,7 @@
|
||||
'gtest_test_executable',
|
||||
],
|
||||
'sources': [
|
||||
'gtest/googletest/test/gtest_repeat_test.cc',
|
||||
'<(gtest_dir)/test/gtest_repeat_test.cc',
|
||||
],
|
||||
},
|
||||
{
|
||||
@ -198,7 +224,7 @@
|
||||
'gtest_main',
|
||||
],
|
||||
'sources': [
|
||||
'gtest/googletest/test/gtest_sole_header_test.cc',
|
||||
'<(gtest_dir)/test/gtest_sole_header_test.cc',
|
||||
],
|
||||
},
|
||||
{
|
||||
@ -207,7 +233,7 @@
|
||||
'gtest_test_executable',
|
||||
],
|
||||
'sources': [
|
||||
'gtest/googletest/test/gtest_stress_test.cc',
|
||||
'<(gtest_dir)/test/gtest_stress_test.cc',
|
||||
],
|
||||
},
|
||||
{
|
||||
@ -216,7 +242,7 @@
|
||||
'gtest_test_executable',
|
||||
],
|
||||
'sources': [
|
||||
'gtest/googletest/test/gtest-unittest-api_test.cc',
|
||||
'<(gtest_dir)/test/gtest-unittest-api_test.cc',
|
||||
],
|
||||
},
|
||||
{
|
||||
@ -236,7 +262,7 @@
|
||||
],
|
||||
},
|
||||
],
|
||||
}, { # else: crashpad_in_chromium!=0
|
||||
}, { # else: crashpad_dependencies=="chromium"
|
||||
'targets': [
|
||||
{
|
||||
'target_name': 'gtest',
|
||||
|
24
third_party/mini_chromium/mini_chromium.gyp
vendored
24
third_party/mini_chromium/mini_chromium.gyp
vendored
@ -14,25 +14,27 @@
|
||||
|
||||
{
|
||||
'includes': [
|
||||
'../../build/crashpad_in_chromium.gypi',
|
||||
'../../build/crashpad_dependencies.gypi',
|
||||
],
|
||||
'targets': [
|
||||
{
|
||||
# To support both Crashpad’s standalone build and its in-Chromium build,
|
||||
# Crashpad code depending on base should do so through this shim, which
|
||||
# will either get base from mini_chromium or Chromium depending on the
|
||||
# build type.
|
||||
# To support Crashpad’s standalone build, its in-Chromium build, and its
|
||||
# build depending on external libraries, Crashpad code depending on base
|
||||
# should do so through this shim, which will either get base from
|
||||
# mini_chromium, Chromium, or an external library depending on the build
|
||||
# type.
|
||||
'target_name': 'base',
|
||||
'type': 'none',
|
||||
'conditions': [
|
||||
['crashpad_in_chromium==0', {
|
||||
['crashpad_dependencies=="standalone"', {
|
||||
'dependencies': [
|
||||
'mini_chromium/base/base.gyp:base',
|
||||
],
|
||||
'export_dependent_settings': [
|
||||
'mini_chromium/base/base.gyp:base',
|
||||
],
|
||||
}, { # else: crashpad_in_chromium!=0
|
||||
}],
|
||||
['crashpad_dependencies=="chromium"', {
|
||||
'dependencies': [
|
||||
'<(DEPTH)/base/base.gyp:base',
|
||||
],
|
||||
@ -40,6 +42,14 @@
|
||||
'<(DEPTH)/base/base.gyp:base',
|
||||
],
|
||||
}],
|
||||
['crashpad_dependencies=="external"', {
|
||||
'dependencies': [
|
||||
'../../../../mini_chromium/mini_chromium/base/base.gyp:base',
|
||||
],
|
||||
'export_dependent_settings': [
|
||||
'../../../../mini_chromium/mini_chromium/base/base.gyp:base',
|
||||
],
|
||||
}],
|
||||
],
|
||||
},
|
||||
],
|
||||
|
@ -16,6 +16,7 @@
|
||||
#define CRASHPAD_UTIL_NUMERIC_CHECKED_RANGE_H_
|
||||
|
||||
#include <limits>
|
||||
#include <tuple>
|
||||
|
||||
#include "base/logging.h"
|
||||
#include "base/numerics/safe_conversions.h"
|
||||
@ -125,6 +126,10 @@ class CheckedRange {
|
||||
return base() < that.end() && that.base() < end();
|
||||
}
|
||||
|
||||
bool operator<(const CheckedRange& other) const {
|
||||
return std::tie(base_, size_) < std::tie(other.base_, other.size_);
|
||||
}
|
||||
|
||||
private:
|
||||
ValueType base_;
|
||||
SizeType size_;
|
||||
|
@ -166,6 +166,8 @@
|
||||
'win/exception_handler_server.h',
|
||||
'win/get_function.cc',
|
||||
'win/get_function.h',
|
||||
'win/get_module_information.cc',
|
||||
'win/get_module_information.h',
|
||||
'win/handle.cc',
|
||||
'win/handle.h',
|
||||
'win/module_version.cc',
|
||||
|
30
util/win/get_module_information.cc
Normal file
30
util/win/get_module_information.cc
Normal file
@ -0,0 +1,30 @@
|
||||
// Copyright 2016 The Crashpad Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "util/win/get_module_information.h"
|
||||
|
||||
#include "util/win/get_function.h"
|
||||
|
||||
namespace crashpad {
|
||||
|
||||
BOOL CrashpadGetModuleInformation(HANDLE process,
|
||||
HMODULE module,
|
||||
MODULEINFO* module_info,
|
||||
DWORD cb) {
|
||||
static const auto get_module_information =
|
||||
GET_FUNCTION_REQUIRED(L"psapi.dll", GetModuleInformation);
|
||||
return get_module_information(process, module, module_info, cb);
|
||||
}
|
||||
|
||||
} // namespace crashpad
|
33
util/win/get_module_information.h
Normal file
33
util/win/get_module_information.h
Normal file
@ -0,0 +1,33 @@
|
||||
// Copyright 2016 The Crashpad Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef CRASHPAD_UTIL_WIN_GET_MODULE_INFORMATION_H_
|
||||
#define CRASHPAD_UTIL_WIN_GET_MODULE_INFORMATION_H_
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#define PSAPI_VERSION 1
|
||||
#include <psapi.h>
|
||||
|
||||
namespace crashpad {
|
||||
|
||||
//! \brief Proxy function for `GetModuleInformation()`.
|
||||
BOOL CrashpadGetModuleInformation(HANDLE process,
|
||||
HMODULE module,
|
||||
MODULEINFO* module_info,
|
||||
DWORD cb);
|
||||
|
||||
} // namespace crashpad
|
||||
|
||||
#endif // CRASHPAD_UTIL_WIN_GET_MODULE_INFORMATION_H_
|
@ -26,6 +26,8 @@ NTSTATUS NTAPI NtOpenThread(HANDLE* ThreadHandle,
|
||||
OBJECT_ATTRIBUTES* ObjectAttributes,
|
||||
CLIENT_ID* ClientId);
|
||||
|
||||
void* NTAPI RtlGetUnloadEventTrace();
|
||||
|
||||
namespace crashpad {
|
||||
|
||||
NTSTATUS NtQuerySystemInformation(
|
||||
@ -83,6 +85,14 @@ NTSTATUS NtQueryObject(HANDLE handle,
|
||||
return_length);
|
||||
}
|
||||
|
||||
template <class Traits>
|
||||
RTL_UNLOAD_EVENT_TRACE<Traits>* RtlGetUnloadEventTrace() {
|
||||
static const auto rtl_get_unload_event_trace =
|
||||
GET_FUNCTION_REQUIRED(L"ntdll.dll", ::RtlGetUnloadEventTrace);
|
||||
return reinterpret_cast<RTL_UNLOAD_EVENT_TRACE<Traits>*>(
|
||||
rtl_get_unload_event_trace());
|
||||
}
|
||||
|
||||
// Explicit instantiations with the only 2 valid template arguments to avoid
|
||||
// putting the body of the function in the header.
|
||||
template NTSTATUS NtOpenThread<process_types::internal::Traits32>(
|
||||
@ -99,4 +109,10 @@ template NTSTATUS NtOpenThread<process_types::internal::Traits64>(
|
||||
const process_types::CLIENT_ID<process_types::internal::Traits64>*
|
||||
client_id);
|
||||
|
||||
template RTL_UNLOAD_EVENT_TRACE<process_types::internal::Traits32>*
|
||||
RtlGetUnloadEventTrace<process_types::internal::Traits32>();
|
||||
|
||||
template RTL_UNLOAD_EVENT_TRACE<process_types::internal::Traits64>*
|
||||
RtlGetUnloadEventTrace<process_types::internal::Traits64>();
|
||||
|
||||
} // namespace crashpad
|
||||
|
@ -54,4 +54,26 @@ NTSTATUS NtQueryObject(HANDLE handle,
|
||||
ULONG object_information_length,
|
||||
ULONG* return_length);
|
||||
|
||||
// From https://msdn.microsoft.com/en-us/library/bb432428(VS.85).aspx and
|
||||
// http://processhacker.sourceforge.net/doc/struct___r_t_l___u_n_l_o_a_d___e_v_e_n_t___t_r_a_c_e.html
|
||||
#define RTL_UNLOAD_EVENT_TRACE_NUMBER 64
|
||||
|
||||
template <class Traits>
|
||||
struct RTL_UNLOAD_EVENT_TRACE {
|
||||
typename Traits::Pointer BaseAddress;
|
||||
typename Traits::UnsignedIntegral SizeOfImage;
|
||||
ULONG Sequence;
|
||||
ULONG TimeDateStamp;
|
||||
ULONG CheckSum;
|
||||
WCHAR ImageName[32];
|
||||
ULONG Version0;
|
||||
union {
|
||||
ULONG Version1;
|
||||
typename Traits::Pad alignment_for_x64;
|
||||
};
|
||||
};
|
||||
|
||||
template <class Traits>
|
||||
RTL_UNLOAD_EVENT_TRACE<Traits>* RtlGetUnloadEventTrace();
|
||||
|
||||
} // namespace crashpad
|
||||
|
Loading…
x
Reference in New Issue
Block a user