diff --git a/client/annotation.cc b/client/annotation.cc new file mode 100644 index 00000000..b2f23d09 --- /dev/null +++ b/client/annotation.cc @@ -0,0 +1,41 @@ +// Copyright 2017 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/annotation.h" + +#include + +#include "base/logging.h" +#include "client/annotation_list.h" + +namespace crashpad { + +static_assert(std::is_standard_layout::value, + "Annotation must be POD"); + +// static +constexpr size_t Annotation::kNameMaxLength; +constexpr size_t Annotation::kValueMaxSize; + +void Annotation::SetSize(ValueSizeType size) { + DCHECK_LT(size, kValueMaxSize); + size_ = size; + AnnotationList::Get()->Add(this); +} + +void Annotation::Clear() { + size_ = 0; +} + +} // namespace crashpad diff --git a/client/annotation.h b/client/annotation.h new file mode 100644 index 00000000..a1dd37e9 --- /dev/null +++ b/client/annotation.h @@ -0,0 +1,221 @@ +// Copyright 2017 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_ANNOTATION_H_ +#define CRASHPAD_CLIENT_ANNOTATION_H_ + +#include +#include + +#include +#include +#include + +#include "base/macros.h" +#include "base/numerics/safe_conversions.h" + +namespace crashpad { + +class AnnotationList; + +//! \brief Base class for an annotation, which records a name-value pair of +//! arbitrary data when set. +//! +//! After an annotation is declared, its `value_ptr_` will not be captured in a +//! crash report until a call to \a SetSize() specifies how much data from the +//! value should be recorded. +//! +//! Annotations should be declared with static storage duration. +//! +//! An example declaration and usage: +//! +//! \code +//! // foo.cc: +//! +//! namespace { +//! char g_buffer[1024]; +//! crashpad::Annotation g_buffer_annotation( +//! crashpad::Annotation::Type::kString, "buffer_head", g_buffer); +//! } // namespace +//! +//! void OnBufferProduced(size_t n) { +//! // Capture the head of the buffer, in case we crash when parsing it. +//! g_buffer_annotation.SetSize(std::min(64, n)); +//! +//! // Start parsing the header. +//! Frobinate(g_buffer, n); +//! } +//! \endcode +//! +//! Annotation objects are not inherently thread-safe. To manipulate them +//! from multiple threads, external synchronization must be used. +//! +//! Annotation objects should never be destroyed. Once they are Set(), they +//! are permanently referenced by a global object. +class Annotation { + public: + //! \brief The maximum length of the #name field in bytes. + static constexpr size_t kNameMaxLength = 64; + + //! \brief The maximum size of the #value field in bytes. + static constexpr size_t kValueMaxSize = 2048; + + //! \brief The type used for \a SetSize(). + using ValueSizeType = uint32_t; + + //! \brief The type of data stored in the annotation. + enum class Type : uint16_t { + //! \brief An invalid annotation. Reserved for internal use. + kInvalid = 0, + + //! \brief A `NUL`-terminated C-string. + kString = 1, + + //! \brief Clients may declare their own custom types by using values + //! greater than this. + kUserDefinedStart = 0x8000, + }; + + //! \brief Creates a user-defined Annotation::Type. + //! + //! This exists to remove the casting overhead of `enum class`. + //! + //! \param[in] value A value used to create a user-defined type. + //! + //! \returns The value added to Type::kUserDefinedStart and casted. + constexpr static Type UserDefinedType(uint16_t value) { + using UnderlyingType = std::underlying_type::type; + // MSVS 2015 doesn't have full C++14 support and complains about local + // variables defined in a constexpr function, which is valid. Avoid them + // and the also-problematic DCHECK until all the infrastructure is updated: + // https://crbug.com/crashpad/201. +#if !defined(OS_WIN) || (defined(_MSC_VER) && _MSC_VER >= 1910) + const UnderlyingType start = + static_cast(Type::kUserDefinedStart); + const UnderlyingType user_type = start + value; + DCHECK(user_type > start) << "User-defined Type is 0 or overflows"; + return static_cast(user_type); +#else + return static_cast( + static_cast(Type::kUserDefinedStart) + value); +#endif + } + + //! \brief Constructs a new annotation. + //! + //! Upon construction, the annotation will not be included in any crash + //! reports until \sa SetSize() is called with a value greater than `0`. + //! + //! \param[in] type The data type of the value of the annotation. + //! \param[in] name A `NUL`-terminated C-string name for the annotation. Names + //! do not have to be unique, though not all crash processors may handle + //! Annotations with the same name. Names should be constexpr data with + //! static storage duration. + //! \param[in] value_ptr A pointer to the value for the annotation. The + //! pointer may not be changed once associated with an annotation, but + //! the data may be mutated. + constexpr Annotation(Type type, const char name[], void* const value_ptr) + : link_node_(nullptr), + name_(name), + value_ptr_(value_ptr), + size_(0), + type_(type) {} + + //! \brief Specifies the number of bytes in \a value_ptr_ to include when + //! generating a crash report. + //! + //! A size of `0` indicates that no value should be recorded and is the + //! equivalent of calling \sa Clear(). + //! + //! This method does not mutate the data referenced by the annotation, it + //! merely updates the annotation system's bookkeeping. + //! + //! Subclasses of this base class that provide additional Set methods to + //! mutate the value of the annotation must call always call this method. + //! + //! \param[in] size The number of bytes. + void SetSize(ValueSizeType size); + + //! \brief Marks the annotation as cleared, indicating the \a value_ptr_ + //! should not be included in a crash report. + //! + //! This method does not mutate the data referenced by the annotation, it + //! merely updates the annotation system's bookkeeping. + void Clear(); + + //! \brief Tests whether the annotation has been set. + bool is_set() const { return size_ > 0; } + + Type type() const { return type_; } + ValueSizeType size() const { return size_; } + const char* name() const { return name_; } + const void* value() const { return value_ptr_; } + + protected: + friend class AnnotationList; + + std::atomic& link_node() { return link_node_; } + + private: + //! \brief Linked list next-node pointer. Accessed only by \sa AnnotationList. + //! + //! This will be null until the first call to \sa SetSize(), after which the + //! presence of the pointer will prevent the node from being added to the + //! list again. + std::atomic link_node_; + + const char* const name_; + void* const value_ptr_; + ValueSizeType size_; + const Type type_; + + DISALLOW_COPY_AND_ASSIGN(Annotation); +}; + +//! \brief An \sa Annotation that stores a `NUL`-terminated C-string value. +//! +//! The storage for the value is allocated by the annotation and the template +//! parameter \a MaxSize controls the maxmium length for the value. +//! +//! It is expected that the string value be valid UTF-8, although this is not +//! validated. +template +class StringAnnotation : public Annotation { + public: + //! \brief Constructs a new StringAnnotation with the given \a name. + //! + //! \param[in] name The Annotation name. + constexpr explicit StringAnnotation(const char name[]) + : Annotation(Type::kString, name, value_), value_() {} + + //! \brief Sets the Annotation's string value. + //! + //! \param[in] value The `NUL`-terminated C-string value. + void Set(const char* value) { + strncpy(value_, value, MaxSize); + SetSize( + std::min(MaxSize, base::saturated_cast(strlen(value)))); + } + + private: + // This value is not `NUL`-terminated, since the size is stored by the base + // annotation. + char value_[MaxSize]; + + DISALLOW_COPY_AND_ASSIGN(StringAnnotation); +}; + +} // namespace crashpad + +#endif // CRASHPAD_CLIENT_ANNOTATION_H_ diff --git a/client/annotation_list.cc b/client/annotation_list.cc new file mode 100644 index 00000000..10f5ea49 --- /dev/null +++ b/client/annotation_list.cc @@ -0,0 +1,97 @@ +// Copyright 2017 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/annotation_list.h" + +#include "base/logging.h" +#include "client/crashpad_info.h" + +namespace crashpad { + +AnnotationList::AnnotationList() + : tail_pointer_(&tail_), + head_(Annotation::Type::kInvalid, nullptr, nullptr), + tail_(Annotation::Type::kInvalid, nullptr, nullptr) { + head_.link_node().store(&tail_); +} + +AnnotationList::~AnnotationList() {} + +// static +AnnotationList* AnnotationList::Get() { + return CrashpadInfo::GetCrashpadInfo()->annotations_list(); +} + +// static +AnnotationList* AnnotationList::Register() { + AnnotationList* list = Get(); + if (!list) { + list = new AnnotationList(); + CrashpadInfo::GetCrashpadInfo()->set_annotations_list(list); + } + return list; +} + +void AnnotationList::Add(Annotation* annotation) { + Annotation* null = nullptr; + Annotation* head_next = head_.link_node().load(std::memory_order_relaxed); + if (!annotation->link_node().compare_exchange_strong(null, head_next)) { + // If |annotation|'s link node is not null, then it has been added to the + // list already and no work needs to be done. + return; + } + + // Check that the annotation's name is less than the maximum size. This is + // done here, since the Annotation constructor must be constexpr and this + // path is taken once per annotation. + DCHECK_LT(strlen(annotation->name_), Annotation::kNameMaxLength); + + // Update the head link to point to the new |annotation|. + while (!head_.link_node().compare_exchange_weak(head_next, annotation)) { + // Another thread has updated the head-next pointer, so try again with the + // re-loaded |head_next|. + annotation->link_node().store(head_next, std::memory_order_relaxed); + } +} + +AnnotationList::Iterator::Iterator(Annotation* head, const Annotation* tail) + : curr_(head), tail_(tail) {} + +AnnotationList::Iterator::~Iterator() = default; + +Annotation* AnnotationList::Iterator::operator*() const { + CHECK_NE(curr_, tail_); + return curr_; +} + +AnnotationList::Iterator& AnnotationList::Iterator::operator++() { + CHECK_NE(curr_, tail_); + curr_ = curr_->link_node(); + return *this; +} + +bool AnnotationList::Iterator::operator==( + const AnnotationList::Iterator& other) const { + return curr_ == other.curr_; +} + +AnnotationList::Iterator AnnotationList::begin() { + return Iterator(head_.link_node(), tail_pointer_); +} + +AnnotationList::Iterator AnnotationList::end() { + return Iterator(&tail_, tail_pointer_); +} + +} // namespace crashpad diff --git a/client/annotation_list.h b/client/annotation_list.h new file mode 100644 index 00000000..9485c46c --- /dev/null +++ b/client/annotation_list.h @@ -0,0 +1,94 @@ +// Copyright 2017 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_ANNOTATION_LIST_H_ +#define CRASHPAD_CLIENT_ANNOTATION_LIST_H_ + +#include "base/macros.h" +#include "client/annotation.h" + +namespace crashpad { + +//! \brief A list that contains all the currently set annotations. +//! +//! An instance of this class must be registered on the \a CrashpadInfo +//! structure in order to use the annotations system. Once a list object has +//! been registered on the CrashpadInfo, a different instance should not +//! be used instead. +class AnnotationList { + public: + AnnotationList(); + ~AnnotationList(); + + //! \brief Returns the instance of the list that has been registered on the + //! CrashapdInfo structure. + static AnnotationList* Get(); + + //! \brief Returns the instace of the list, creating and registering + //! it if one is not already set on the CrashapdInfo structure. + static AnnotationList* Register(); + + //! \brief Adds \a annotation to the global list. This method does not need + //! to be called by clients directly. The Annotation object will do so + //! automatically. + //! + //! Once an annotation is added to the list, it is not removed. This is + //! because the AnnotationList avoids the use of locks/mutexes, in case it is + //! being manipulated in a compromised context. Instead, an Annotation keeps + //! track of when it has been cleared, which excludes it from a crash report. + //! This design also avoids linear scans of the list when repeatedly setting + //! and/or clearing the value. + void Add(Annotation* annotation); + + //! \brief An InputIterator for the AnnotationList. + class Iterator { + public: + ~Iterator(); + + Annotation* operator*() const; + Iterator& operator++(); + bool operator==(const Iterator& other) const; + bool operator!=(const Iterator& other) const { return !(*this == other); } + + private: + friend class AnnotationList; + Iterator(Annotation* head, const Annotation* tail); + + Annotation* curr_; + const Annotation* const tail_; + + // Copy and assign are required. + }; + + //! \brief Returns an iterator to the first element of the annotation list. + Iterator begin(); + + //! \brief Returns an iterator past the last element of the annotation list. + Iterator end(); + + private: + // To make it easier for the handler to locate the dummy tail node, store the + // pointer. Placed first for packing. + const Annotation* const tail_pointer_; + + // Dummy linked-list head and tail elements of \a Annotation::Type::kInvalid. + Annotation head_; + Annotation tail_; + + DISALLOW_COPY_AND_ASSIGN(AnnotationList); +}; + +} // namespace crashpad + +#endif // CRASHPAD_CLIENT_ANNOTATION_LIST_H_ diff --git a/client/annotation_list_test.cc b/client/annotation_list_test.cc new file mode 100644 index 00000000..ef3039c5 --- /dev/null +++ b/client/annotation_list_test.cc @@ -0,0 +1,183 @@ +// Copyright 2017 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/annotation.h" + +#include +#include + +#include "base/rand_util.h" +#include "client/crashpad_info.h" +#include "gtest/gtest.h" +#include "util/misc/clock.h" +#include "util/thread/thread.h" + +namespace crashpad { +namespace test { +namespace { + +TEST(AnnotationListStatic, Register) { + ASSERT_FALSE(AnnotationList::Get()); + EXPECT_TRUE(AnnotationList::Register()); + EXPECT_TRUE(AnnotationList::Get()); + EXPECT_EQ(AnnotationList::Get(), AnnotationList::Register()); + + // This isn't expected usage of the AnnotationList API, but it is necessary + // for testing. + AnnotationList* list = AnnotationList::Get(); + CrashpadInfo::GetCrashpadInfo()->set_annotations_list(nullptr); + delete list; + + EXPECT_FALSE(AnnotationList::Get()); +} + +class AnnotationList : public testing::Test { + public: + void SetUp() override { + CrashpadInfo::GetCrashpadInfo()->set_annotations_list(&annotations_); + } + + void TearDown() override { + CrashpadInfo::GetCrashpadInfo()->set_annotations_list(nullptr); + } + + // NOTE: Annotations should be declared at file-scope, but in order to test + // them, they are declared as part of the test. These members are public so + // they are accessible from global helpers. + crashpad::StringAnnotation<8> one_{"First"}; + crashpad::StringAnnotation<256> two_{"Second"}; + crashpad::StringAnnotation<101> three_{"First"}; + + protected: + using AllAnnotations = std::vector>; + + AllAnnotations CollectAnnotations() { + AllAnnotations annotations; + + for (Annotation* curr : annotations_) { + if (!curr->is_set()) + continue; + std::string value(static_cast(curr->value()), curr->size()); + annotations.push_back(std::make_pair(curr->name(), value)); + } + + return annotations; + } + + bool ContainsNameValue(const AllAnnotations& annotations, + const std::string& name, + const std::string& value) { + return std::find(annotations.begin(), + annotations.end(), + std::make_pair(name, value)) != annotations.end(); + } + + crashpad::AnnotationList annotations_; +}; + +TEST_F(AnnotationList, SetAndClear) { + one_.Set("this is a value longer than 8 bytes"); + AllAnnotations annotations = CollectAnnotations(); + + EXPECT_EQ(1u, annotations.size()); + EXPECT_TRUE(ContainsNameValue(annotations, "First", "this is ")); + + one_.Clear(); + + EXPECT_EQ(0u, CollectAnnotations().size()); + + one_.Set("short"); + two_.Set(std::string(500, 'A').data()); + + annotations = CollectAnnotations(); + EXPECT_EQ(2u, annotations.size()); + + EXPECT_EQ(5u, one_.size()); + EXPECT_EQ(256u, two_.size()); + + EXPECT_TRUE(ContainsNameValue(annotations, "First", "short")); + EXPECT_TRUE(ContainsNameValue(annotations, "Second", std::string(256, 'A'))); +} + +TEST_F(AnnotationList, DuplicateKeys) { + ASSERT_EQ(0u, CollectAnnotations().size()); + + one_.Set("1"); + three_.Set("2"); + + AllAnnotations annotations = CollectAnnotations(); + EXPECT_EQ(2u, annotations.size()); + + EXPECT_TRUE(ContainsNameValue(annotations, "First", "1")); + EXPECT_TRUE(ContainsNameValue(annotations, "First", "2")); + + one_.Clear(); + + annotations = CollectAnnotations(); + EXPECT_EQ(1u, annotations.size()); +} + +class RaceThread : public Thread { + public: + explicit RaceThread(test::AnnotationList* test) : Thread(), test_(test) {} + + private: + void ThreadMain() override { + for (int i = 0; i <= 50; ++i) { + if (i % 2 == 0) { + test_->three_.Set("three"); + test_->two_.Clear(); + } else { + test_->three_.Clear(); + } + SleepNanoseconds(base::RandInt(1, 1000)); + } + } + + test::AnnotationList* test_; +}; + +TEST_F(AnnotationList, MultipleThreads) { + ASSERT_EQ(0u, CollectAnnotations().size()); + + RaceThread other_thread(this); + other_thread.Start(); + + for (int i = 0; i <= 50; ++i) { + if (i % 2 == 0) { + one_.Set("one"); + two_.Set("two"); + } else { + one_.Clear(); + } + SleepNanoseconds(base::RandInt(1, 1000)); + } + + other_thread.Join(); + + AllAnnotations annotations = CollectAnnotations(); + EXPECT_GE(annotations.size(), 2u); + EXPECT_LE(annotations.size(), 3u); + + EXPECT_TRUE(ContainsNameValue(annotations, "First", "one")); + EXPECT_TRUE(ContainsNameValue(annotations, "First", "three")); + + if (annotations.size() == 3) { + EXPECT_TRUE(ContainsNameValue(annotations, "Second", "two")); + } +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/client/annotation_test.cc b/client/annotation_test.cc new file mode 100644 index 00000000..25530715 --- /dev/null +++ b/client/annotation_test.cc @@ -0,0 +1,112 @@ +// Copyright 2017 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/annotation.h" + +#include + +#include "client/annotation_list.h" +#include "client/crashpad_info.h" +#include "gtest/gtest.h" + +namespace crashpad { +namespace test { +namespace { + +class Annotation : public testing::Test { + public: + void SetUp() override { + CrashpadInfo::GetCrashpadInfo()->set_annotations_list(&annotations_); + } + + void TearDown() override { + CrashpadInfo::GetCrashpadInfo()->set_annotations_list(nullptr); + } + + size_t AnnotationsCount() { + size_t result = 0; + for (auto* annotation : annotations_) { + if (annotation->is_set()) + ++result; + } + return result; + } + + protected: + crashpad::AnnotationList annotations_; +}; + +TEST_F(Annotation, Basics) { + constexpr crashpad::Annotation::Type kType = + crashpad::Annotation::UserDefinedType(1); + + const char kName[] = "annotation 1"; + char buffer[1024]; + crashpad::Annotation annotation(kType, kName, buffer); + + EXPECT_FALSE(annotation.is_set()); + EXPECT_EQ(0u, AnnotationsCount()); + + EXPECT_EQ(kType, annotation.type()); + EXPECT_EQ(0u, annotation.size()); + EXPECT_EQ(std::string(kName), annotation.name()); + EXPECT_EQ(buffer, annotation.value()); + + annotation.SetSize(10); + + EXPECT_TRUE(annotation.is_set()); + EXPECT_EQ(1u, AnnotationsCount()); + + EXPECT_EQ(10u, annotation.size()); + EXPECT_EQ(&annotation, *annotations_.begin()); + + annotation.Clear(); + + EXPECT_FALSE(annotation.is_set()); + EXPECT_EQ(0u, AnnotationsCount()); + + EXPECT_EQ(0u, annotation.size()); +} + +TEST_F(Annotation, StringType) { + crashpad::StringAnnotation<5> annotation("name"); + const char* value_ptr = static_cast(annotation.value()); + + EXPECT_FALSE(annotation.is_set()); + + EXPECT_EQ(crashpad::Annotation::Type::kString, annotation.type()); + EXPECT_EQ(0u, annotation.size()); + EXPECT_EQ(std::string("name"), annotation.name()); + EXPECT_EQ(0u, strlen(value_ptr)); + + annotation.Set("test"); + + EXPECT_TRUE(annotation.is_set()); + EXPECT_EQ(1u, AnnotationsCount()); + + EXPECT_EQ(4u, annotation.size()); + EXPECT_EQ(std::string("test"), value_ptr); + + annotation.Set("loooooooooooong"); + + EXPECT_TRUE(annotation.is_set()); + EXPECT_EQ(1u, AnnotationsCount()); + + EXPECT_EQ(5u, annotation.size()); + EXPECT_EQ(std::string("loooo"), std::string(value_ptr, annotation.size())); +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/client/client.gyp b/client/client.gyp index 0e4a89d8..4b14c4bf 100644 --- a/client/client.gyp +++ b/client/client.gyp @@ -29,6 +29,10 @@ '..', ], 'sources': [ + 'annotation.cc', + 'annotation.h', + 'annotation_list.cc', + 'annotation_list.h', 'capture_context_mac.S', 'capture_context_mac.h', 'crash_report_database.cc', diff --git a/client/client_test.gyp b/client/client_test.gyp index 03cde2cb..4ea4ef2b 100644 --- a/client/client_test.gyp +++ b/client/client_test.gyp @@ -35,6 +35,8 @@ '..', ], 'sources': [ + 'annotation_test.cc', + 'annotation_list_test.cc', 'capture_context_mac_test.cc', 'crash_report_database_test.cc', 'crashpad_client_win_test.cc', diff --git a/client/crashpad_info.cc b/client/crashpad_info.cc index 18866ba3..9452382b 100644 --- a/client/crashpad_info.cc +++ b/client/crashpad_info.cc @@ -105,7 +105,8 @@ CrashpadInfo::CrashpadInfo() padding_1_(0), extra_memory_ranges_(nullptr), simple_annotations_(nullptr), - user_data_minidump_stream_head_(nullptr) + user_data_minidump_stream_head_(nullptr), + annotations_list_(nullptr) #if !defined(NDEBUG) && defined(OS_WIN) , invalid_read_detection_(0xbadc0de) diff --git a/client/crashpad_info.h b/client/crashpad_info.h index 77c9097a..7ce55edc 100644 --- a/client/crashpad_info.h +++ b/client/crashpad_info.h @@ -19,6 +19,7 @@ #include "base/macros.h" #include "build/build_config.h" +#include "client/annotation_list.h" #include "client/simple_address_range_bag.h" #include "client/simple_string_dictionary.h" #include "util/misc/tri_state.h" @@ -109,6 +110,33 @@ struct CrashpadInfo { return simple_annotations_; } + //! \brief Sets the annotations list. + //! + //! Unlike the \a simple_annotations structure, the \a annotations can + //! typed data and it is not limited to a dictionary form. Annotations are + //! interpreted by Crashpad as module-level annotations. + //! + //! Annotations may exist in \a annotations_list at the time that this + //! method is called, or they may be added, removed, or modified in \a + //! annotations_list after this method is called. + //! + //! \param[in] annotations_list A list of set Annotation objects that maintain + //! arbitrary, typed key-value state. The CrashpadInfo object does not + //! take ownership of the AnnotationsList object. It is the caller’s + //! responsibility to ensure that this pointer remains valid while it is + //! in effect for a CrashpadInfo object. + //! + //! \sa annotations_list() + //! \sa AnnotationList::Register() + void set_annotations_list(AnnotationList* list) { annotations_list_ = list; } + + //! \return The annotations list. + //! + //! \sa set_annotations_list() + //! \sa AnnotationList::Get() + //! \sa AnnotationList::Register() + AnnotationList* annotations_list() const { return annotations_list_; } + //! \brief Enables or disables Crashpad handler processing. //! //! When handling an exception, the Crashpad handler will scan all modules in @@ -218,6 +246,7 @@ struct CrashpadInfo { SimpleAddressRangeBag* extra_memory_ranges_; // weak SimpleStringDictionary* simple_annotations_; // weak internal::UserDataMinidumpStreamListEntry* user_data_minidump_stream_head_; + AnnotationList* annotations_list_; // weak #if !defined(NDEBUG) && defined(OS_WIN) uint32_t invalid_read_detection_; diff --git a/snapshot/mac/process_types/crashpad_info.proctype b/snapshot/mac/process_types/crashpad_info.proctype index 713bb09d..41af5fc9 100644 --- a/snapshot/mac/process_types/crashpad_info.proctype +++ b/snapshot/mac/process_types/crashpad_info.proctype @@ -47,4 +47,7 @@ PROCESS_TYPE_STRUCT_BEGIN(CrashpadInfo) // UserDataStreamListEntry* PROCESS_TYPE_STRUCT_MEMBER(Pointer, user_data_minidump_stream_head) + + // AnnotationList* + PROCESS_TYPE_STRUCT_MEMBER(Pointer, annotations_list) PROCESS_TYPE_STRUCT_END(CrashpadInfo) diff --git a/snapshot/win/pe_image_reader.h b/snapshot/win/pe_image_reader.h index be185419..56a991b8 100644 --- a/snapshot/win/pe_image_reader.h +++ b/snapshot/win/pe_image_reader.h @@ -48,6 +48,7 @@ struct CrashpadInfo { typename Traits::Pointer extra_address_ranges; typename Traits::Pointer simple_annotations; typename Traits::Pointer user_data_minidump_stream_head; + typename Traits::Pointer annotations_list; }; } // namespace process_types