137 lines
5.4 KiB
C++
137 lines
5.4 KiB
C++
// Copyright 2023 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_RING_BUFFER_ANNOTATION_H_
|
|
#define CRASHPAD_CLIENT_RING_BUFFER_ANNOTATION_H_
|
|
|
|
#include <stdio.h>
|
|
|
|
#include "client/annotation.h"
|
|
#include "client/length_delimited_ring_buffer.h"
|
|
|
|
namespace crashpad {
|
|
|
|
//! \brief Capacity of `RingBufferAnnotation`, in bytes.
|
|
using RingBufferAnnotationCapacity = RingBufferCapacity;
|
|
|
|
namespace internal {
|
|
|
|
//! \brief Default capacity of `RingBufferAnnotation`, in bytes.
|
|
inline constexpr RingBufferAnnotationCapacity
|
|
kDefaultRingBufferAnnotationCapacity = 8192;
|
|
|
|
} // namespace internal
|
|
|
|
//! \brief An `Annotation` which wraps a `LengthDelimitedRingBuffer`
|
|
//! of up to `Capacity` bytes in length.
|
|
//!
|
|
//! Supports writing variable-length data via `Push()`. When the ring buffer is
|
|
//! full, it will drop old data items in FIFO order until enough space is
|
|
//! available for the write.
|
|
//!
|
|
//! Supports guarding concurrent reads from writes via `ScopedSpinGuard`, so
|
|
//! writing to this object is thread-safe.
|
|
//!
|
|
//! Clients which read this `Annotation`'s memory can optionally invoke
|
|
//! `TryCreateScopedSpinGuard()` on this object to ensure any pending write
|
|
//! finishes before the memory is read.
|
|
//!
|
|
//! Each item in this ring buffer is delimited by its length encoded in
|
|
//! little-endian Base 128 varint encoding.
|
|
//!
|
|
//! `RingBufferAnnotation` uses varint-encoded delimiters to enable
|
|
//! zero-copy deserialization of the ringbuffer's contents when storing
|
|
//! protobufs inside the ringbuffer, e.g. via
|
|
//! `google::protobuf::util::ParseDelimitedFromZeroCopyStream()` or similar.
|
|
//!
|
|
//! \sa
|
|
//! https://github.com/protocolbuffers/protobuf/blob/3202b9da88ceb75b65bbabaf4033c95e872f828d/src/google/protobuf/util/delimited_message_util.h#L85
|
|
//! \sa
|
|
//! https://github.com/protocolbuffers/protobuf/blob/8bd49dea5e167a389d94b71d24c981d8f9fa0c99/src/google/protobuf/io/zero_copy_stream_impl_lite.h#L68
|
|
//! \sa
|
|
//! https://github.com/protocolbuffers/protobuf/blob/8bd49dea5e167a389d94b71d24c981d8f9fa0c99/src/google/protobuf/io/coded_stream.h#L171
|
|
//!
|
|
//! To deserialize the items stored in this annotation, use
|
|
//! `LengthDelimitedRingBufferReader`.
|
|
template <RingBufferAnnotationCapacity Capacity =
|
|
internal::kDefaultRingBufferAnnotationCapacity>
|
|
class RingBufferAnnotation final : public Annotation {
|
|
public:
|
|
//! \brief Constructs a `RingBufferAnnotation`.
|
|
//! \param[in] type A unique identifier for the type of data in the ring
|
|
//! buffer.
|
|
//! \param[in] name The name of the annotation.
|
|
constexpr RingBufferAnnotation(Annotation::Type type, const char name[])
|
|
: Annotation(type,
|
|
name,
|
|
reinterpret_cast<void* const>(&ring_buffer_data_),
|
|
ConcurrentAccessGuardMode::kScopedSpinGuard),
|
|
ring_buffer_data_(),
|
|
ring_buffer_writer_(ring_buffer_data_) {}
|
|
RingBufferAnnotation(const RingBufferAnnotation&) = delete;
|
|
RingBufferAnnotation& operator=(const RingBufferAnnotation&) = delete;
|
|
RingBufferAnnotation(RingBufferAnnotation&&) = default;
|
|
RingBufferAnnotation& operator=(RingBufferAnnotation&&) = default;
|
|
|
|
//! \brief Pushes data onto this annotation's ring buffer.
|
|
//!
|
|
//! If the ring buffer does not have enough space to store `buffer_length`
|
|
//! bytes of data, old data items are dropped in FIFO order until
|
|
//! enough space is available to store the new data.
|
|
bool Push(const void* const buffer,
|
|
RingBufferAnnotationCapacity buffer_length) {
|
|
// Use a zero timeout so the operation immediately fails if another thread
|
|
// or process is currently reading this Annotation.
|
|
constexpr uint64_t kSpinGuardTimeoutNanoseconds = 0;
|
|
|
|
auto spin_guard = TryCreateScopedSpinGuard(kSpinGuardTimeoutNanoseconds);
|
|
if (!spin_guard) {
|
|
return false;
|
|
}
|
|
bool success = ring_buffer_writer_.Push(buffer, buffer_length);
|
|
if (success) {
|
|
SetSize(ring_buffer_data_.GetRingBufferLength());
|
|
}
|
|
return success;
|
|
}
|
|
|
|
//! \brief Reset the annotation (e.g., for testing).
|
|
//! This method is not thread-safe.
|
|
void ResetForTesting() {
|
|
ring_buffer_data_.ResetForTesting();
|
|
ring_buffer_writer_.ResetForTesting();
|
|
}
|
|
|
|
private:
|
|
using RingBufferWriter =
|
|
LengthDelimitedRingBufferWriter<RingBufferData<Capacity>>;
|
|
|
|
//! \brief The ring buffer data stored in this Anotation.
|
|
RingBufferData<Capacity> ring_buffer_data_;
|
|
|
|
//! \brief The writer which wraps `ring_buffer_data_`.
|
|
RingBufferWriter ring_buffer_writer_;
|
|
};
|
|
|
|
// Allow just `RingBufferAnnotation foo;` to be declared without template
|
|
// arguments using C++17 class template argument deduction.
|
|
template <RingBufferAnnotationCapacity Capacity =
|
|
internal::kDefaultRingBufferAnnotationCapacity>
|
|
RingBufferAnnotation(Annotation::Type type, const char name[])
|
|
-> RingBufferAnnotation<Capacity>;
|
|
|
|
} // namespace crashpad
|
|
|
|
#endif // CRASHPAD_CLIENT_RING_BUFFER_ANNOTATION_H_
|