// Copyright 2017 The Crashpad Authors // // 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 #include "base/check.h" #include "base/numerics/safe_conversions.h" #include "base/strings/string_piece.h" #include "build/build_config.h" #include "util/synchronization/scoped_spin_guard.h" namespace crashpad { #if BUILDFLAG(IS_IOS) namespace internal { class InProcessIntermediateDumpHandler; } // namespace internal #endif 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 an annotation’s name, in bytes. //! Matches the behavior of Breakpad's SimpleStringDictionary. static constexpr size_t kNameMaxLength = 256; //! \brief The maximum size of an annotation’s value, in bytes. static constexpr size_t kValueMaxSize = 5 * 4096; //! \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 Mode used to guard concurrent reads from writes. enum class ConcurrentAccessGuardMode : bool { //! \!brief Annotation does not guard reads from concurrent //! writes. Annotation values can be corrupted if the process crashes //! mid-write and the handler tries to read from the Annotation while //! being written to. kUnguarded = false, //! \!brief Annotation guards reads from concurrent writes using //! ScopedSpinGuard. Clients must use TryCreateScopedSpinGuard() //! before reading or writing the data in this Annotation. kScopedSpinGuard = true, }; //! \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 !BUILDFLAG(IS_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* value_ptr) : Annotation(type, name, value_ptr, ConcurrentAccessGuardMode::kUnguarded) {} Annotation(const Annotation&) = delete; Annotation& operator=(const Annotation&) = delete; //! \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_; } ConcurrentAccessGuardMode concurrent_access_guard_mode() const { return concurrent_access_guard_mode_; } //! \brief If this Annotation guards concurrent access using ScopedSpinGuard, //! tries to obtain the spin guard and returns the result. //! //! \param[in] timeout_ns The timeout in nanoseconds after which to give up //! trying to obtain the spin guard. //! \return std::nullopt if the spin guard could not be obtained within //! timeout_ns, or the obtained spin guard otherwise. std::optional TryCreateScopedSpinGuard(uint64_t timeout_ns) { // This can't use DCHECK_EQ() because ostream doesn't support // operator<<(bool). DCHECK(concurrent_access_guard_mode_ == ConcurrentAccessGuardMode::kScopedSpinGuard); if (concurrent_access_guard_mode_ == ConcurrentAccessGuardMode::kUnguarded) { return std::nullopt; } return ScopedSpinGuard::TryCreateScopedSpinGuard(timeout_ns, spin_guard_state_); } protected: //! \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. //! \param[in] concurrent_access_guard_mode Mode used to guard concurrent //! reads from writes. constexpr Annotation(Type type, const char name[], void* value_ptr, ConcurrentAccessGuardMode concurrent_access_guard_mode) : link_node_(nullptr), name_(name), value_ptr_(value_ptr), size_(0), type_(type), concurrent_access_guard_mode_(concurrent_access_guard_mode), spin_guard_state_() {} friend class AnnotationList; #if BUILDFLAG(IS_IOS) friend class internal::InProcessIntermediateDumpHandler; #endif 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_; //! \brief Mode used to guard concurrent reads from writes. const ConcurrentAccessGuardMode concurrent_access_guard_mode_; SpinGuardState spin_guard_state_; }; //! \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 A constructor tag that enables braced initialization in C arrays. //! //! \sa StringAnnotation() enum class Tag { kArray }; //! \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_() {} StringAnnotation(const StringAnnotation&) = delete; StringAnnotation& operator=(const StringAnnotation&) = delete; //! \brief Constructs a new StringAnnotation with the given \a name. //! //! This constructor takes the ArrayInitializerTag for use when //! initializing a C array of annotations. The main constructor is //! explicit and cannot be brace-initialized. As an example: //! //! \code //! static crashpad::StringAnnotation<32> annotations[] = { //! {"name-1", crashpad::StringAnnotation<32>::Tag::kArray}, //! {"name-2", crashpad::StringAnnotation<32>::Tag::kArray}, //! {"name-3", crashpad::StringAnnotation<32>::Tag::kArray}, //! }; //! \endcode //! //! \param[in] name The Annotation name. //! \param[in] tag A constructor tag. constexpr StringAnnotation(const char name[], Tag tag) : StringAnnotation(name) {} //! \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)))); } //! \brief Sets the Annotation's string value. //! //! \param[in] string The string value. void Set(base::StringPiece string) { Annotation::ValueSizeType size = std::min(MaxSize, base::saturated_cast(string.size())); memcpy(value_, string.data(), size); // Check for no embedded `NUL` characters. DCHECK(!memchr(value_, '\0', size)) << "embedded NUL"; SetSize(size); } const base::StringPiece value() const { return base::StringPiece(value_, size()); } private: // This value is not `NUL`-terminated, since the size is stored by the base // annotation. char value_[MaxSize]; }; } // namespace crashpad #endif // CRASHPAD_CLIENT_ANNOTATION_H_