Support client-specified extra memory ranges

Change-Id: I378e2513a4894fb1548445b660bb3db86e281572
Reviewed-on: https://chromium-review.googlesource.com/329564
Reviewed-by: Robert Sesek <rsesek@chromium.org>
Reviewed-by: Mark Mentovai <mark@chromium.org>
This commit is contained in:
Scott Graham 2016-02-26 14:42:47 -08:00
Родитель badfacccee
Коммит 7217cc0a8f
26 изменённых файлов: 719 добавлений и 58 удалений

Просмотреть файл

@ -46,6 +46,8 @@
'settings.h',
'simple_string_dictionary.cc',
'simple_string_dictionary.h',
'simple_address_range_bag.cc',
'simple_address_range_bag.h',
'simulate_crash.h',
'simulate_crash_mac.cc',
'simulate_crash_mac.h',

Просмотреть файл

@ -39,6 +39,7 @@
'crashpad_client_win_test.cc',
'prune_crash_reports_test.cc',
'settings_test.cc',
'simple_address_range_bag_test.cc',
'simple_string_dictionary_test.cc',
'simulate_crash_mac_test.cc',
],

Просмотреть файл

@ -100,13 +100,13 @@ CrashpadInfo::CrashpadInfo()
system_crash_reporter_forwarding_(TriState::kUnset),
gather_indirectly_referenced_memory_(TriState::kUnset),
padding_0_(0),
extra_memory_ranges_(nullptr),
simple_annotations_(nullptr)
#if !defined(NDEBUG) && defined(OS_WIN)
,
invalid_read_detection_(0xbadc0de)
#endif
{
}
} // namespace crashpad

Просмотреть файл

@ -19,6 +19,7 @@
#include "base/macros.h"
#include "build/build_config.h"
#include "client/simple_address_range_bag.h"
#include "client/simple_string_dictionary.h"
#include "util/misc/tri_state.h"
@ -42,6 +43,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
@ -135,6 +152,7 @@ struct CrashpadInfo {
TriState system_crash_reporter_forwarding_;
TriState gather_indirectly_referenced_memory_;
uint8_t padding_0_;
SimpleAddressRangeBag* extra_memory_ranges_; // weak
SimpleStringDictionary* simple_annotations_; // weak
#if !defined(NDEBUG) && defined(OS_WIN)

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

@ -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, 0x1000);
EXPECT_EQ(entry->size, 200);
bag.Remove(reinterpret_cast<void*>(0x1000), 200);
EXPECT_FALSE(entry->is_active());
EXPECT_EQ(entry->base, 0);
EXPECT_EQ(entry->size, 0);
}
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(), 2);
EXPECT_FALSE(bag.Remove(CheckedRange<uint64_t>(0x3000, 30)));
EXPECT_EQ(bag.GetCount(), 2);
EXPECT_TRUE(bag.Remove(reinterpret_cast<void*>(0x1000), 10));
EXPECT_TRUE(bag.Remove(reinterpret_cast<void*>(0x2000), 20));
EXPECT_EQ(bag.GetCount(), 0);
}
TEST(SimpleAddressRangeBag, CopyAndAssign) {
TSimpleAddressRangeBag<10> bag;
EXPECT_TRUE(bag.Insert(CheckedRange<uint64_t>(1, 2)));
EXPECT_TRUE(bag.Insert(CheckedRange<uint64_t>(3, 4)));
EXPECT_TRUE(bag.Insert(CheckedRange<uint64_t>(5, 6)));
EXPECT_TRUE(bag.Remove(CheckedRange<uint64_t>(3, 4)));
EXPECT_EQ(2u, bag.GetCount());
// Test copy.
TSimpleAddressRangeBag<10> bag_copy(bag);
EXPECT_EQ(2u, bag_copy.GetCount());
EXPECT_TRUE(bag_copy.Remove(CheckedRange<uint64_t>(1, 2)));
EXPECT_TRUE(bag_copy.Remove(CheckedRange<uint64_t>(5, 6)));
EXPECT_EQ(0u, bag_copy.GetCount());
EXPECT_EQ(2u, bag.GetCount());
// Test assign.
TSimpleAddressRangeBag<10> bag_assign;
bag_assign = bag;
EXPECT_EQ(2u, bag_assign.GetCount());
EXPECT_TRUE(bag_assign.Remove(CheckedRange<uint64_t>(1, 2)));
EXPECT_TRUE(bag_assign.Remove(CheckedRange<uint64_t>(5, 6)));
EXPECT_EQ(0u, bag_assign.GetCount());
EXPECT_EQ(2u, bag.GetCount());
}
// Running out of space shouldn't crash.
TEST(SimpleAddressRangeBag, OutOfSpace) {
TSimpleAddressRangeBag<2> bag;
EXPECT_TRUE(bag.Insert(CheckedRange<uint64_t>(1, 2)));
EXPECT_TRUE(bag.Insert(CheckedRange<uint64_t>(3, 4)));
EXPECT_FALSE(bag.Insert(CheckedRange<uint64_t>(5, 6)));
EXPECT_EQ(2u, bag.GetCount());
EXPECT_FALSE(bag.Remove(CheckedRange<uint64_t>(5, 6)));
}
} // namespace
} // namespace test
} // namespace crashpad

Просмотреть файл

@ -24,11 +24,6 @@
namespace crashpad {
// Opaque type for the serialized representation of a TSimpleStringDictionary.
// One is created in TSimpleStringDictionary::Serialize and can be deserialized
// using one of the constructors.
struct SerializedSimpleStringDictionary;
//! \brief A map/dictionary collection implementation using a fixed amount of
//! storage, so that it does not perform any dynamic allocations for its
//! operations.
@ -112,16 +107,6 @@ class TSimpleStringDictionary {
return *this;
}
//! \brief Constructs a map from its serialized form. \a map should be the out
//! parameter from Serialize(), and \a size should be its return value.
TSimpleStringDictionary(
const SerializedSimpleStringDictionary* map, size_t size) {
DCHECK_EQ(size, sizeof(entries_));
if (size == sizeof(entries_)) {
memcpy(entries_, map, size);
}
}
//! \brief Returns the number of active key/value pairs. The upper limit for
//! this is \a NumEntries.
size_t GetCount() const {
@ -236,18 +221,6 @@ class TSimpleStringDictionary {
DCHECK_EQ(GetEntryForKey(key), implicit_cast<Entry*>(nullptr));
}
//! \brief Returns a serialized form of the map.
//!
//! Places a serialized version of the map into \a map and returns the size in
//! bytes. Both \a map and the size should be passed to the deserializing
//! constructor. Note that the serialized \a map is scoped to the lifetime of
//! the non-serialized instance of this class. The \a map data can be copied
//! across IPC boundaries.
size_t Serialize(const SerializedSimpleStringDictionary** map) const {
*map = reinterpret_cast<const SerializedSimpleStringDictionary*>(entries_);
return sizeof(entries_);
}
private:
const Entry* GetConstEntryForKey(const char* key) const {
for (size_t i = 0; i < num_entries; ++i) {

Просмотреть файл

@ -240,34 +240,6 @@ TEST(SimpleStringDictionary, AddRemove) {
EXPECT_FALSE(map.GetValueForKey("mark"));
}
TEST(SimpleStringDictionary, Serialize) {
using TestMap = TSimpleStringDictionary<4, 5, 7>;
TestMap map;
map.SetKeyValue("one", "abc");
map.SetKeyValue("two", "def");
map.SetKeyValue("tre", "hig");
EXPECT_STREQ("abc", map.GetValueForKey("one"));
EXPECT_STREQ("def", map.GetValueForKey("two"));
EXPECT_STREQ("hig", map.GetValueForKey("tre"));
const SerializedSimpleStringDictionary* serialized;
size_t size = map.Serialize(&serialized);
SerializedSimpleStringDictionary* serialized_copy =
reinterpret_cast<SerializedSimpleStringDictionary*>(malloc(size));
ASSERT_TRUE(serialized_copy);
memcpy(serialized_copy, serialized, size);
TestMap deserialized(serialized_copy, size);
free(serialized_copy);
EXPECT_EQ(3u, deserialized.GetCount());
EXPECT_STREQ("abc", deserialized.GetValueForKey("one"));
EXPECT_STREQ("def", deserialized.GetValueForKey("two"));
EXPECT_STREQ("hig", deserialized.GetValueForKey("tre"));
}
// Running out of space shouldn't crash.
TEST(SimpleStringDictionary, OutOfSpace) {
TSimpleStringDictionary<3, 2, 2> map;

Просмотреть файл

@ -38,6 +38,10 @@
#endif
namespace crashpad {
int* g_extra_memory_pointer;
int* g_extra_memory_not_saved;
namespace {
CRITICAL_SECTION g_test_critical_section;
@ -136,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;
@ -165,6 +196,13 @@ 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.

Просмотреть файл

@ -182,5 +182,10 @@ 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>>();
}
} // namespace internal
} // namespace crashpad

Просмотреть файл

@ -79,6 +79,7 @@ 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;
private:
std::string name_;

Просмотреть файл

@ -135,6 +135,12 @@ ModuleSnapshotMinidump::AnnotationsSimpleMap() const {
return annotations_simple_map_;
}
std::set<CheckedRange<uint64_t>> ModuleSnapshotMinidump::ExtraMemoryRanges()
const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
return std::set<CheckedRange<uint64_t>>();
}
bool ModuleSnapshotMinidump::InitializeModuleCrashpadInfo(
FileReaderInterface* file_reader,
const MINIDUMP_LOCATION_DESCRIPTOR*

Просмотреть файл

@ -76,6 +76,7 @@ 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;
private:
// Initializes data carried in a MinidumpModuleCrashpadInfo structure on

Просмотреть файл

@ -19,10 +19,12 @@
#include <sys/types.h>
#include <map>
#include <set>
#include <string>
#include <vector>
#include "util/misc/uuid.h"
#include "util/numeric/checked_range.h"
namespace crashpad {
@ -168,6 +170,10 @@ 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;
};
} // namespace crashpad

Просмотреть файл

@ -80,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',
@ -170,6 +171,18 @@
'win/crashpad_snapshot_test_dump_without_crashing.cc',
],
},
{
'target_name': 'crashpad_snapshot_test_extra_memory_ranges',
'type': 'executable',
'dependencies': [
'../client/client.gyp:crashpad_client',
'../compat/compat.gyp:crashpad_compat',
'../third_party/mini_chromium/mini_chromium.gyp:base',
],
'sources': [
'win/crashpad_snapshot_test_extra_memory_ranges.cc',
],
},
{
'target_name': 'crashpad_snapshot_test_image_reader',
'type': 'executable',

Просмотреть файл

@ -29,7 +29,8 @@ TestModuleSnapshot::TestModuleSnapshot()
uuid_(),
debug_file_name_(),
annotations_vector_(),
annotations_simple_map_() {
annotations_simple_map_(),
extra_memory_ranges_() {
}
TestModuleSnapshot::~TestModuleSnapshot() {
@ -93,5 +94,9 @@ std::map<std::string, std::string> TestModuleSnapshot::AnnotationsSimpleMap()
return annotations_simple_map_;
}
std::set<CheckedRange<uint64_t>> TestModuleSnapshot::ExtraMemoryRanges() const {
return extra_memory_ranges_;
}
} // namespace test
} // namespace crashpad

Просмотреть файл

@ -75,6 +75,10 @@ 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;
}
// ModuleSnapshot:
@ -95,6 +99,7 @@ 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;
private:
std::string name_;
@ -109,6 +114,7 @@ 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_;
DISALLOW_COPY_AND_ASSIGN(TestModuleSnapshot);
};

Просмотреть файл

@ -0,0 +1,54 @@
// Copyright 2016 The Crashpad Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <windows.h>
#include "base/logging.h"
#include "client/crashpad_info.h"
#include "util/file/file_io.h"
int wmain(int argc, wchar_t* argv[]) {
using namespace crashpad;
CrashpadInfo* crashpad_info = CrashpadInfo::GetCrashpadInfo();
// This is "leaked" to crashpad_info.
SimpleAddressRangeBag* extra_ranges = new SimpleAddressRangeBag();
extra_ranges->Insert(CheckedRange<uint64_t>(0, 1));
extra_ranges->Insert(CheckedRange<uint64_t>(1, 0));
extra_ranges->Insert(CheckedRange<uint64_t>(0x1000000000ULL, 0x1000));
extra_ranges->Insert(CheckedRange<uint64_t>(0x2000, 0x2000000000ULL));
extra_ranges->Insert(CheckedRange<uint64_t>(1234, 5678));
extra_ranges->Insert(CheckedRange<uint64_t>(1234, 5678));
extra_ranges->Insert(CheckedRange<uint64_t>(1234, 5678));
crashpad_info->set_extra_memory_ranges(extra_ranges);
// Tell the parent that the environment has been set up.
HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE);
PCHECK(out != INVALID_HANDLE_VALUE) << "GetStdHandle";
char c = ' ';
CheckedWriteFile(out, &c, sizeof(c));
HANDLE in = GetStdHandle(STD_INPUT_HANDLE);
PCHECK(in != INVALID_HANDLE_VALUE) << "GetStdHandle";
CheckedReadFile(in, &c, sizeof(c));
CHECK(c == 'd' || c == ' ');
// If 'd' we crash with a debug break, otherwise exit normally.
if (c == 'd')
__debugbreak();
return 0;
}

Просмотреть файл

@ -23,6 +23,7 @@ import sys
import tempfile
import time
g_temp_dirs = []
@ -268,6 +269,27 @@ def RunTests(cdb_path,
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')
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',

Просмотреть файл

@ -0,0 +1,133 @@
// Copyright 2016 The Crashpad Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "snapshot/win/module_snapshot_win.h"
#include <stdlib.h>
#include <string.h>
#include <string>
#include "base/files/file_path.h"
#include "build/build_config.h"
#include "client/crashpad_info.h"
#include "client/simple_address_range_bag.h"
#include "gtest/gtest.h"
#include "snapshot/win/process_snapshot_win.h"
#include "test/paths.h"
#include "test/win/child_launcher.h"
#include "util/file/file_io.h"
#include "util/win/process_info.h"
namespace crashpad {
namespace test {
namespace {
enum TestType {
// Don't crash, just test the CrashpadInfo interface.
kDontCrash = 0,
// The child process should crash by __debugbreak().
kCrashDebugBreak,
};
void TestExtraMemoryRanges(TestType type,
const base::string16& directory_modification) {
// Spawn a child process, passing it the pipe name to connect to.
base::FilePath test_executable = Paths::Executable();
std::wstring child_test_executable =
test_executable.DirName()
.Append(directory_modification)
.Append(test_executable.BaseName().RemoveFinalExtension().value() +
L"_extra_memory_ranges.exe")
.value();
ChildLauncher child(child_test_executable, L"");
child.Start();
// Wait for the child process to indicate that it's done setting up its
// annotations via the CrashpadInfo interface.
char c;
CheckedReadFile(child.stdout_read_handle(), &c, sizeof(c));
ProcessSnapshotWin snapshot;
ASSERT_TRUE(snapshot.Initialize(
child.process_handle(), ProcessSuspensionState::kRunning, 0));
// Verify the extra memory ranges set via the CrashpadInfo interface.
std::set<CheckedRange<uint64_t>> all_ranges;
for (const auto* module : snapshot.Modules()) {
for (const auto& range : module->ExtraMemoryRanges())
all_ranges.insert(range);
}
EXPECT_EQ(5u, all_ranges.size());
EXPECT_NE(all_ranges.end(), all_ranges.find(CheckedRange<uint64_t>(0, 1)));
EXPECT_NE(all_ranges.end(), all_ranges.find(CheckedRange<uint64_t>(1, 0)));
EXPECT_NE(all_ranges.end(),
all_ranges.find(CheckedRange<uint64_t>(1234, 5678)));
EXPECT_NE(all_ranges.end(),
all_ranges.find(CheckedRange<uint64_t>(0x1000000000ULL, 0x1000)));
EXPECT_NE(all_ranges.end(),
all_ranges.find(CheckedRange<uint64_t>(0x2000, 0x2000000000ULL)));
// Tell the child process to continue.
DWORD expected_exit_code;
switch (type) {
case kDontCrash:
c = ' ';
expected_exit_code = 0;
break;
case kCrashDebugBreak:
c = 'd';
expected_exit_code = STATUS_BREAKPOINT;
break;
default:
FAIL();
}
CheckedWriteFile(child.stdin_write_handle(), &c, sizeof(c));
EXPECT_EQ(expected_exit_code, child.WaitForExit());
}
TEST(ExtraMemoryRanges, DontCrash) {
TestExtraMemoryRanges(kDontCrash, FILE_PATH_LITERAL("."));
}
TEST(ExtraMemoryRanges, CrashDebugBreak) {
TestExtraMemoryRanges(kCrashDebugBreak, FILE_PATH_LITERAL("."));
}
#if defined(ARCH_CPU_64_BITS)
TEST(ExtraMemoryRanges, DontCrashWOW64) {
#ifndef NDEBUG
TestExtraMemoryRanges(kDontCrash, FILE_PATH_LITERAL("..\\..\\out\\Debug"));
#else
TestExtraMemoryRanges(kDontCrash, FILE_PATH_LITERAL("..\\..\\out\\Release"));
#endif
}
TEST(ExtraMemoryRanges, CrashDebugBreakWOW64) {
#ifndef NDEBUG
TestExtraMemoryRanges(kCrashDebugBreak,
FILE_PATH_LITERAL("..\\..\\out\\Debug"));
#else
TestExtraMemoryRanges(kCrashDebugBreak,
FILE_PATH_LITERAL("..\\..\\out\\Release"));
#endif
}
#endif // ARCH_CPU_64_BITS
} // namespace
} // namespace test
} // namespace crashpad

Просмотреть файл

@ -15,6 +15,7 @@
#include "snapshot/win/module_snapshot_win.h"
#include "base/strings/utf_string_conversions.h"
#include "client/simple_address_range_bag.h"
#include "snapshot/win/pe_image_annotations_reader.h"
#include "snapshot/win/pe_image_reader.h"
#include "util/misc/tri_state.h"
@ -185,6 +186,16 @@ 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;
}
template <class Traits>
void ModuleSnapshotWin::GetCrashpadOptionsInternal(
CrashpadInfoClientOptions* options) {
@ -222,5 +233,34 @@ 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));
}
}
}
} // namespace internal
} // namespace crashpad

Просмотреть файл

@ -85,11 +85,16 @@ 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;
private:
template <class Traits>
void GetCrashpadOptionsInternal(CrashpadInfoClientOptions* options);
template <class Traits>
void GetCrashpadExtraMemoryRanges(
std::set<CheckedRange<uint64_t>>* ranges) 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.

Просмотреть файл

@ -43,6 +43,7 @@ struct CrashpadInfo {
uint8_t system_crash_reporter_forwarding; // TriState.
uint8_t gather_indirectly_referenced_memory; // TriState.
uint8_t padding_0;
typename Traits::Pointer extra_address_ranges;
typename Traits::Pointer simple_annotations;
};

Просмотреть файл

@ -80,6 +80,12 @@ bool ProcessSnapshotWin::Initialize(
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;
}
@ -321,7 +327,8 @@ void ProcessSnapshotWin::GetCrashpadOptionsInternal(
// 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;
}
}

Просмотреть файл

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