2023-01-28 04:54:20 +00:00
|
|
|
|
// Copyright 2017 The Crashpad Authors
|
2022-04-02 01:21:55 +00:00
|
|
|
|
//
|
|
|
|
|
// 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 <algorithm>
|
|
|
|
|
#include <atomic>
|
2023-02-16 02:08:10 +00:00
|
|
|
|
#include <optional>
|
2022-04-02 01:21:55 +00:00
|
|
|
|
|
|
|
|
|
#include <stdint.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
#include <sys/types.h>
|
|
|
|
|
|
|
|
|
|
#include "base/check.h"
|
|
|
|
|
#include "base/numerics/safe_conversions.h"
|
|
|
|
|
#include "base/strings/string_piece.h"
|
|
|
|
|
#include "build/build_config.h"
|
2023-02-16 02:08:10 +00:00
|
|
|
|
#include "util/synchronization/scoped_spin_guard.h"
|
2022-04-02 01:21:55 +00:00
|
|
|
|
|
|
|
|
|
namespace crashpad {
|
2022-08-16 00:48:53 +00:00
|
|
|
|
#if BUILDFLAG(IS_IOS)
|
2022-04-02 01:21:55 +00:00
|
|
|
|
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.
|
2022-08-16 00:48:53 +00:00
|
|
|
|
//! Matches the behavior of Breakpad's SimpleStringDictionary.
|
|
|
|
|
static constexpr size_t kNameMaxLength = 256;
|
2022-04-02 01:21:55 +00:00
|
|
|
|
|
|
|
|
|
//! \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,
|
|
|
|
|
};
|
|
|
|
|
|
2023-02-16 02:08:10 +00:00
|
|
|
|
//! \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,
|
|
|
|
|
};
|
|
|
|
|
|
2022-04-02 01:21:55 +00:00
|
|
|
|
//! \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>::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.
|
2022-08-16 00:48:53 +00:00
|
|
|
|
#if !BUILDFLAG(IS_WIN) || (defined(_MSC_VER) && _MSC_VER >= 1910)
|
2022-04-02 01:21:55 +00:00
|
|
|
|
const UnderlyingType start =
|
|
|
|
|
static_cast<UnderlyingType>(Type::kUserDefinedStart);
|
|
|
|
|
const UnderlyingType user_type = start + value;
|
|
|
|
|
DCHECK(user_type > start) << "User-defined Type is 0 or overflows";
|
|
|
|
|
return static_cast<Type>(user_type);
|
|
|
|
|
#else
|
|
|
|
|
return static_cast<Type>(
|
|
|
|
|
static_cast<UnderlyingType>(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.
|
2023-02-16 02:08:10 +00:00
|
|
|
|
constexpr Annotation(Type type, const char name[], void* value_ptr)
|
|
|
|
|
: Annotation(type,
|
|
|
|
|
name,
|
|
|
|
|
value_ptr,
|
|
|
|
|
ConcurrentAccessGuardMode::kUnguarded) {}
|
2022-04-02 01:21:55 +00:00
|
|
|
|
|
|
|
|
|
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_; }
|
|
|
|
|
|
2023-02-16 02:08:10 +00:00
|
|
|
|
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<ScopedSpinGuard> 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_);
|
|
|
|
|
}
|
|
|
|
|
|
2022-04-02 01:21:55 +00:00
|
|
|
|
protected:
|
2023-02-16 02:08:10 +00:00
|
|
|
|
//! \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_() {}
|
|
|
|
|
|
2022-04-02 01:21:55 +00:00
|
|
|
|
friend class AnnotationList;
|
2022-08-16 00:48:53 +00:00
|
|
|
|
#if BUILDFLAG(IS_IOS)
|
2022-04-02 01:21:55 +00:00
|
|
|
|
friend class internal::InProcessIntermediateDumpHandler;
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
std::atomic<Annotation*>& 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<Annotation*> link_node_;
|
|
|
|
|
|
|
|
|
|
const char* const name_;
|
|
|
|
|
void* const value_ptr_;
|
|
|
|
|
ValueSizeType size_;
|
|
|
|
|
const Type type_;
|
2023-02-16 02:08:10 +00:00
|
|
|
|
|
|
|
|
|
//! \brief Mode used to guard concurrent reads from writes.
|
|
|
|
|
const ConcurrentAccessGuardMode concurrent_access_guard_mode_;
|
|
|
|
|
|
|
|
|
|
SpinGuardState spin_guard_state_;
|
2022-04-02 01:21:55 +00:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//! \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 <Annotation::ValueSizeType MaxSize>
|
|
|
|
|
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<ValueSizeType>(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<ValueSizeType>(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_
|