Merge master f64ef73d8bba into doc

This commit is contained in:
Mark Mentovai 2016-03-15 19:20:15 -04:00
commit 5e9c0c67d7
83 changed files with 3113 additions and 472 deletions

2
DEPS
View File

@ -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',

View 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 its located outside of the Crashpad tree, as is gtest.
#
# In order for Crashpads .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',
},
}

View File

@ -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 Chromiums base library and copy of gtest. In
# order for Crashpads .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)',
},
}

View File

@ -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'))

View File

@ -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',

View File

@ -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',
],

View File

@ -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

View File

@ -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 callers 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_;

View 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 doesnt 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

View 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_

View 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

View File

@ -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) {

View File

@ -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;

View File

@ -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

View File

@ -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 modules checksum, or `0` if unknown.
//!
//! On Windows, this field comes from the `CheckSum` field of the modules
//! `IMAGE_OPTIONAL_HEADER` structure, if present. It reflects the checksum at
//! the time the module was linked.
uint32_t CheckSum;
//! \brief The modules timestamp, in `time_t` units, seconds since the POSIX
//! epoch.
//!
//! On Windows, this field comes from the `TimeDateStamp` field of the
//! modules `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 modules 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*
//!

View File

@ -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.

View File

@ -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

View File

@ -28,6 +28,7 @@
#include "base/macros.h"
#include "build/build_config.h"
#include "client/crashpad_client.h"
#include "client/crashpad_info.h"
#include "util/win/critical_section_with_debug_info.h"
#include "util/win/get_function.h"
@ -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(

View File

@ -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',

View File

@ -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.
//!

View File

@ -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);

View File

@ -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',

View 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 doesnt 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 doesnt 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

View 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 objects 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_

View 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

View 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

View 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_

View 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

View File

@ -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,

View File

@ -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,

View 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

View 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_

View 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
View File

@ -0,0 +1,128 @@
// Copyright 2016 The Crashpad Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "snapshot/capture_memory.h"
#include <stdint.h>
#include <limits>
#include "base/memory/scoped_ptr.h"
#include "snapshot/memory_snapshot.h"
namespace crashpad {
namespace internal {
namespace {
void MaybeCaptureMemoryAround(CaptureMemory::Delegate* delegate,
uint64_t address) {
const uint64_t non_address_offset = 0x10000;
if (address < non_address_offset)
return;
const uint64_t max_address = delegate->Is64Bit() ?
std::numeric_limits<uint64_t>::max() :
std::numeric_limits<uint32_t>::max();
if (address > max_address - non_address_offset)
return;
const uint64_t kRegisterByteOffset = 256;
const uint64_t target = address - kRegisterByteOffset;
const uint64_t size = 1024;
static_assert(kRegisterByteOffset <= size / 2,
"negative offset too large");
auto ranges =
delegate->GetReadableRanges(CheckedRange<uint64_t>(target, size));
for (const auto& range : ranges) {
delegate->AddNewMemorySnapshot(range);
}
}
template <class T>
void CaptureAtPointersInRange(uint8_t* buffer,
uint64_t buffer_size,
CaptureMemory::Delegate* delegate) {
for (uint64_t address_offset = 0; address_offset < buffer_size;
address_offset += sizeof(T)) {
uint64_t target_address = *reinterpret_cast<T*>(&buffer[address_offset]);
MaybeCaptureMemoryAround(delegate, target_address);
}
}
} // namespace
// static
void CaptureMemory::PointedToByContext(const CPUContext& context,
Delegate* delegate) {
#if defined(ARCH_CPU_X86_FAMILY)
if (context.architecture == kCPUArchitectureX86_64) {
MaybeCaptureMemoryAround(delegate, context.x86_64->rax);
MaybeCaptureMemoryAround(delegate, context.x86_64->rbx);
MaybeCaptureMemoryAround(delegate, context.x86_64->rcx);
MaybeCaptureMemoryAround(delegate, context.x86_64->rdx);
MaybeCaptureMemoryAround(delegate, context.x86_64->rdi);
MaybeCaptureMemoryAround(delegate, context.x86_64->rsi);
MaybeCaptureMemoryAround(delegate, context.x86_64->rbp);
MaybeCaptureMemoryAround(delegate, context.x86_64->r8);
MaybeCaptureMemoryAround(delegate, context.x86_64->r9);
MaybeCaptureMemoryAround(delegate, context.x86_64->r10);
MaybeCaptureMemoryAround(delegate, context.x86_64->r11);
MaybeCaptureMemoryAround(delegate, context.x86_64->r12);
MaybeCaptureMemoryAround(delegate, context.x86_64->r13);
MaybeCaptureMemoryAround(delegate, context.x86_64->r14);
MaybeCaptureMemoryAround(delegate, context.x86_64->r15);
MaybeCaptureMemoryAround(delegate, context.x86_64->rip);
} else {
MaybeCaptureMemoryAround(delegate, context.x86->eax);
MaybeCaptureMemoryAround(delegate, context.x86->ebx);
MaybeCaptureMemoryAround(delegate, context.x86->ecx);
MaybeCaptureMemoryAround(delegate, context.x86->edx);
MaybeCaptureMemoryAround(delegate, context.x86->edi);
MaybeCaptureMemoryAround(delegate, context.x86->esi);
MaybeCaptureMemoryAround(delegate, context.x86->ebp);
MaybeCaptureMemoryAround(delegate, context.x86->eip);
}
#else
#error non-x86
#endif
}
// static
void CaptureMemory::PointedToByMemoryRange(const MemorySnapshot& memory,
Delegate* delegate) {
if (memory.Size() == 0)
return;
const size_t alignment =
delegate->Is64Bit() ? sizeof(uint64_t) : sizeof(uint32_t);
if (memory.Address() % alignment != 0 || memory.Size() % alignment != 0) {
LOG(ERROR) << "unaligned range";
return;
}
scoped_ptr<uint8_t[]> buffer(new uint8_t[memory.Size()]);
if (!delegate->ReadMemory(memory.Address(), memory.Size(), buffer.get())) {
LOG(ERROR) << "ReadMemory";
return;
}
if (delegate->Is64Bit())
CaptureAtPointersInRange<uint64_t>(buffer.get(), memory.Size(), delegate);
else
CaptureAtPointersInRange<uint32_t>(buffer.get(), memory.Size(), delegate);
}
} // namespace internal
} // namespace crashpad

98
snapshot/capture_memory.h Normal file
View File

@ -0,0 +1,98 @@
// Copyright 2016 The Crashpad Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef CRASHPAD_SNAPSHOT_CAPTURE_MEMORY_H_
#define CRASHPAD_SNAPSHOT_CAPTURE_MEMORY_H_
#include <stdint.h>
#include <vector>
#include "snapshot/cpu_context.h"
#include "util/numeric/checked_range.h"
namespace crashpad {
class MemorySnapshot;
namespace internal {
class CaptureMemory {
public:
//! \brief An interface to a platform-specific process reader.
class Delegate {
public:
virtual ~Delegate() {}
//! \return `true` if the target process is a 64-bit process.
virtual bool Is64Bit() const = 0;
//! \brief Attempts to read \a num_bytes bytes from the target process
//! starting at address \a at into \a into.
//!
//! \return `true` if the entire region could be read, or `false` with an
//! error logged.
virtual bool ReadMemory(uint64_t at,
uint64_t num_bytes,
void* into) const = 0;
//! \brief Given a range to be read from the target process, returns a
//! vector
//! of ranges, representing the readable portions of the original range.
//!
//! \param[in] range The range being identified.
//!
//! \return A vector of ranges corresponding to the portion of \a range that
//! is readable.
virtual std::vector<CheckedRange<uint64_t>> GetReadableRanges(
const CheckedRange<uint64_t, uint64_t>& range) const = 0;
//! \brief Adds the given range representing a memory snapshot in the target
//! process to the result.
virtual void AddNewMemorySnapshot(
const CheckedRange<uint64_t, uint64_t>& range) = 0;
};
//! \brief For all registers that appear to be pointer-like in \a context,
//! captures a small amount of memory near their pointed to location.
//!
//! "Pointer-like" in this context means not too close to zero (signed or
//! unsigned) so that there's a reasonable chance that the value is a pointer.
//!
//! \param[in] context The context to inspect.
//! \param[in] process_reader A MemoryCaptureProcessReader to read from the
//! target process, and that handles adding new ranges.
static void PointedToByContext(const CPUContext& context, Delegate* delegate);
//! \brief For all pointer-like values in a memory range of the target
//! process,
//! captures a small amount of memory near the pointed to location.
//!
//! \param[in] memory An existing MemorySnapshot of the range to search. The
//! base address and size must be pointer-aligned and an integral number
//! of
//! pointers long.
//! \param[in] process_reader A MemoryCaptureProcessReader to read from the
//! target process, and that handles adding new ranges.
static void PointedToByMemoryRange(const MemorySnapshot& memory,
Delegate* delegate);
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(CaptureMemory);
};
} // namespace internal
} // namespace crashpad
#endif // CRASHPAD_SNAPSHOT_CAPTURE_MEMORY_H_

View File

@ -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

View File

@ -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

View File

@ -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);
}
}

View File

@ -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

View File

@ -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_;

View File

@ -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();

View File

@ -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;

View File

@ -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)

View File

@ -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*

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -30,6 +30,8 @@
'..',
],
'sources': [
'capture_memory.cc',
'capture_memory.h',
'cpu_architecture.h',
'cpu_context.cc',
'cpu_context.h',
@ -89,12 +91,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',
],
},
],
}

View File

@ -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',

View File

@ -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

View File

@ -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);
};

View File

@ -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();
}

View File

@ -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_;

View 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

View 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_

View File

@ -1,103 +0,0 @@
// Copyright 2016 The Crashpad Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "snapshot/win/capture_context_memory.h"
#include <stdint.h>
#include <limits>
#include "snapshot/win/memory_snapshot_win.h"
#include "snapshot/win/process_reader_win.h"
namespace crashpad {
namespace internal {
namespace {
void MaybeCaptureMemoryAround(ProcessReaderWin* process_reader,
WinVMAddress address,
PointerVector<MemorySnapshotWin>* into) {
const WinVMAddress non_address_offset = 0x10000;
if (address < non_address_offset)
return;
if (process_reader->Is64Bit()) {
if (address >= std::numeric_limits<uint64_t>::max() - non_address_offset)
return;
} else {
if (address >= std::numeric_limits<uint32_t>::max() - non_address_offset)
return;
}
const WinVMSize kRegisterByteOffset = 32;
const WinVMAddress target = address - kRegisterByteOffset;
const WinVMSize size = 128;
auto ranges = process_reader->GetProcessInfo().GetReadableRanges(
CheckedRange<WinVMAddress, WinVMSize>(target, size));
for (const auto& range : ranges) {
internal::MemorySnapshotWin* snapshot = new internal::MemorySnapshotWin();
snapshot->Initialize(process_reader, range.base(), range.size());
into->push_back(snapshot);
}
}
} // namespace
void CaptureMemoryPointedToByContext(const CPUContext& context,
ProcessReaderWin* process_reader,
const ProcessReaderWin::Thread& thread,
PointerVector<MemorySnapshotWin>* into) {
#if defined(ARCH_CPU_X86_64)
if (context.architecture == kCPUArchitectureX86_64) {
MaybeCaptureMemoryAround(process_reader, context.x86_64->rax, into);
MaybeCaptureMemoryAround(process_reader, context.x86_64->rbx, into);
MaybeCaptureMemoryAround(process_reader, context.x86_64->rcx, into);
MaybeCaptureMemoryAround(process_reader, context.x86_64->rdx, into);
MaybeCaptureMemoryAround(process_reader, context.x86_64->rdi, into);
MaybeCaptureMemoryAround(process_reader, context.x86_64->rsi, into);
if (context.x86_64->rbp < thread.stack_region_address ||
context.x86_64->rbp >=
thread.stack_region_address + thread.stack_region_size) {
MaybeCaptureMemoryAround(process_reader, context.x86_64->rbp, into);
}
MaybeCaptureMemoryAround(process_reader, context.x86_64->r8, into);
MaybeCaptureMemoryAround(process_reader, context.x86_64->r9, into);
MaybeCaptureMemoryAround(process_reader, context.x86_64->r10, into);
MaybeCaptureMemoryAround(process_reader, context.x86_64->r11, into);
MaybeCaptureMemoryAround(process_reader, context.x86_64->r12, into);
MaybeCaptureMemoryAround(process_reader, context.x86_64->r13, into);
MaybeCaptureMemoryAround(process_reader, context.x86_64->r14, into);
MaybeCaptureMemoryAround(process_reader, context.x86_64->r15, into);
MaybeCaptureMemoryAround(process_reader, context.x86_64->rip, into);
} else {
#endif
MaybeCaptureMemoryAround(process_reader, context.x86->eax, into);
MaybeCaptureMemoryAround(process_reader, context.x86->ebx, into);
MaybeCaptureMemoryAround(process_reader, context.x86->ecx, into);
MaybeCaptureMemoryAround(process_reader, context.x86->edx, into);
MaybeCaptureMemoryAround(process_reader, context.x86->edi, into);
MaybeCaptureMemoryAround(process_reader, context.x86->esi, into);
if (context.x86->ebp < thread.stack_region_address ||
context.x86->ebp >=
thread.stack_region_address + thread.stack_region_size) {
MaybeCaptureMemoryAround(process_reader, context.x86->ebp, into);
}
MaybeCaptureMemoryAround(process_reader, context.x86->eip, into);
#if defined(ARCH_CPU_X86_64)
}
#endif
}
} // namespace internal
} // namespace crashpad

View File

@ -1,43 +0,0 @@
// Copyright 2016 The Crashpad Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef CRASHPAD_SNAPSHOT_WIN_CAPTURE_CONTEXT_MEMORY_H_
#define CRASHPAD_SNAPSHOT_WIN_CAPTURE_CONTEXT_MEMORY_H_
#include "snapshot/cpu_context.h"
#include "snapshot/win/process_reader_win.h"
#include "util/stdlib/pointer_container.h"
namespace crashpad {
namespace internal {
class MemorySnapshotWin;
//! \brief For all registers that appear to be pointer-like in \a context,
//! captures a small amount of memory near their pointed to location.
//!
//! \param[in] context The context to inspect.
//! \param[in] process_reader A ProcessReaderWin to read from the target
//! process.
//! \param[in] thread The thread to which the context belongs.
//! \param[out] into A vector of pointers to append new ranges to.
void CaptureMemoryPointedToByContext(const CPUContext& context,
ProcessReaderWin* process_reader,
const ProcessReaderWin::Thread& thread,
PointerVector<MemorySnapshotWin>* into);
} // namespace internal
} // namespace crashpad
#endif // CRASHPAD_SNAPSHOT_WIN_CAPTURE_CONTEXT_MEMORY_H_

View File

@ -0,0 +1,56 @@
// Copyright 2016 The Crashpad Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "snapshot/win/capture_memory_delegate_win.h"
#include "snapshot/win/memory_snapshot_win.h"
namespace crashpad {
namespace internal {
CaptureMemoryDelegateWin::CaptureMemoryDelegateWin(
ProcessReaderWin* process_reader,
const ProcessReaderWin::Thread& thread,
PointerVector<MemorySnapshotWin>* snapshots)
: stack_(thread.stack_region_address, thread.stack_region_size),
process_reader_(process_reader),
snapshots_(snapshots) {}
bool CaptureMemoryDelegateWin::Is64Bit() const {
return process_reader_->Is64Bit();
}
bool CaptureMemoryDelegateWin::ReadMemory(uint64_t at,
uint64_t num_bytes,
void* into) const {
return process_reader_->ReadMemory(at, num_bytes, into);
}
std::vector<CheckedRange<uint64_t>> CaptureMemoryDelegateWin::GetReadableRanges(
const CheckedRange<uint64_t, uint64_t>& range) const {
return process_reader_->GetProcessInfo().GetReadableRanges(range);
}
void CaptureMemoryDelegateWin::AddNewMemorySnapshot(
const CheckedRange<uint64_t, uint64_t>& range) {
// Don't bother storing this memory if it points back into the stack.
if (stack_.ContainsRange(range))
return;
internal::MemorySnapshotWin* snapshot = new internal::MemorySnapshotWin();
snapshot->Initialize(process_reader_, range.base(), range.size());
snapshots_->push_back(snapshot);
}
} // namespace internal
} // namespace crashpad

View File

@ -0,0 +1,58 @@
// Copyright 2016 The Crashpad Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef CRASHPAD_SNAPSHOT_WIN_CAPTURE_MEMORY_DELEGATE_WIN_H_
#define CRASHPAD_SNAPSHOT_WIN_CAPTURE_MEMORY_DELEGATE_WIN_H_
#include "snapshot/capture_memory.h"
#include "snapshot/win/process_reader_win.h"
#include "util/stdlib/pointer_container.h"
namespace crashpad {
namespace internal {
class MemorySnapshotWin;
class CaptureMemoryDelegateWin : public CaptureMemory::Delegate {
public:
//! \brief A MemoryCaptureDelegate for Windows.
//!
//! \param[in] process_reader A ProcessReaderWin for the target process.
//! \param[in] thread The thread being inspected. Memory ranges overlapping
//! this thread's stack will be ignored on the assumption that they're
//! already captured elsewhere.
//! \param[in] snapshots A vector of MemorySnapshotWin to which the captured
//! memory will be added.
CaptureMemoryDelegateWin(ProcessReaderWin* process_reader,
const ProcessReaderWin::Thread& thread,
PointerVector<MemorySnapshotWin>* snapshots);
// MemoryCaptureDelegate:
bool Is64Bit() const override;
bool ReadMemory(uint64_t at, uint64_t num_bytes, void* into) const override;
std::vector<CheckedRange<uint64_t>> GetReadableRanges(
const CheckedRange<uint64_t, uint64_t>& range) const override;
void AddNewMemorySnapshot(const CheckedRange<uint64_t, uint64_t>& range);
private:
CheckedRange<uint64_t, uint64_t> stack_;
ProcessReaderWin* process_reader_;
PointerVector<MemorySnapshotWin>* snapshots_;
};
} // namespace internal
} // namespace crashpad
#endif // CRASHPAD_SNAPSHOT_WIN_CAPTURE_MEMORY_DELEGATE_WIN_H_

View File

@ -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;
}

View File

@ -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',

View File

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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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"

View File

@ -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

View File

@ -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;

View File

@ -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) {

View File

@ -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);

View File

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

View File

@ -48,11 +48,14 @@ class ThreadSnapshotWin final : public ThreadSnapshot {
//! the thread.
//! \param[in] process_reader_thread The thread within the ProcessReaderWin
//! for which the snapshot should be created.
//! \param[in] gather_indirectly_referenced_memory If `true`, adds extra
//! memory regions to the snapshot pointed to by the thread's stack.
//!
//! \return `true` if the snapshot could be created, `false` otherwise with
//! an appropriate message logged.
bool Initialize(ProcessReaderWin* process_reader,
const ProcessReaderWin::Thread& process_reader_thread);
const ProcessReaderWin::Thread& process_reader_thread,
bool gather_indirectly_referenced_memory);
// ThreadSnapshot:

View File

@ -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',

View File

@ -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',

View File

@ -14,25 +14,27 @@
{
'includes': [
'../../build/crashpad_in_chromium.gypi',
'../../build/crashpad_dependencies.gypi',
],
'targets': [
{
# To support both Crashpads 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 Crashpads 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',
],
}],
],
},
],

View File

@ -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_;

View File

@ -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',

View 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

View 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_

View File

@ -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

View File

@ -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