crashpad/client/ring_buffer_annotation_test.cc
Ben Hamilton 212b8f6b8c [client] New RingBufferAnnotation
This CL integrates the new ScopedSpinGuard with the new
LengthDelimitedRingBuffer into a new class, RingBufferAnnotation.

RingBufferAnnotation is thread-safe both for reading and writing, and is
suitable for streaming logs, trace events, and other high-throughput
data streams.

I included a load test (ring_buffer_annotation_load_test) which launches
two threads which simultaneously write to and read from the
RingBufferAnnotation.

By default, reads and writes are serialized using ScopedSpinGuard, but
passing the flag "--disable_spin_guard" to the test disables the spin
guard on the reading side (which is expected to make the test fail).

Change-Id: Ic8e28866d085d57e778c4f86bcb7492ef0638ab9
Bug: crashpad:437
Reviewed-on: https://chromium-review.googlesource.com/c/crashpad/crashpad/+/4023619
Reviewed-by: Robert Sesek <rsesek@chromium.org>
Reviewed-by: Mark Mentovai <mark@chromium.org>
Commit-Queue: Ben Hamilton <benhamilton@google.com>
2023-02-01 19:23:51 +00:00

189 lines
6.0 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.
#include "client/ring_buffer_annotation.h"
#include "client/length_delimited_ring_buffer.h"
#include <array>
#include <string>
#include "client/annotation_list.h"
#include "client/crashpad_info.h"
#include "gtest/gtest.h"
#include "test/gtest_death.h"
namespace crashpad {
namespace test {
namespace {
constexpr uint32_t kRingBufferHeaderSize = 16;
constexpr uint32_t kLengthDelimiter1ByteSize = 1;
class RingBufferAnnotationTest : 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:
AnnotationList annotations_;
};
TEST_F(RingBufferAnnotationTest, Basics) {
constexpr Annotation::Type kType = Annotation::UserDefinedType(1);
constexpr char kName[] = "annotation 1";
RingBufferAnnotation annotation(kType, kName);
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_TRUE(
annotation.Push(reinterpret_cast<const uint8_t*>("0123456789"), 10));
EXPECT_TRUE(annotation.is_set());
EXPECT_EQ(1u, AnnotationsCount());
constexpr Annotation::ValueSizeType kExpectedSize =
kRingBufferHeaderSize + kLengthDelimiter1ByteSize + 10u;
EXPECT_EQ(kExpectedSize, annotation.size());
EXPECT_EQ(&annotation, *annotations_.begin());
RingBufferData data;
EXPECT_TRUE(
data.DeserializeFromBuffer(annotation.value(), annotation.size()));
EXPECT_EQ(kExpectedSize, data.GetRingBufferLength());
std::vector<uint8_t> popped_value;
LengthDelimitedRingBufferReader reader(data);
EXPECT_TRUE(reader.Pop(popped_value));
const std::vector<uint8_t> expected = {
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};
EXPECT_EQ(expected, popped_value);
annotation.Clear();
EXPECT_FALSE(annotation.is_set());
EXPECT_EQ(0u, AnnotationsCount());
EXPECT_EQ(0u, annotation.size());
}
TEST_F(RingBufferAnnotationTest, MultiplePushesWithoutWrapping) {
constexpr Annotation::Type kType = Annotation::UserDefinedType(1);
constexpr char kName[] = "annotation 1";
RingBufferAnnotation annotation(kType, kName);
EXPECT_TRUE(
annotation.Push(reinterpret_cast<const uint8_t*>("0123456789"), 10));
EXPECT_TRUE(annotation.Push(reinterpret_cast<const uint8_t*>("ABCDEF"), 6));
EXPECT_TRUE(annotation.is_set());
EXPECT_EQ(1u, AnnotationsCount());
constexpr Annotation::ValueSizeType kExpectedSize =
kRingBufferHeaderSize + kLengthDelimiter1ByteSize + 10u +
kLengthDelimiter1ByteSize + 6u;
EXPECT_EQ(kExpectedSize, annotation.size());
EXPECT_EQ(&annotation, *annotations_.begin());
RingBufferData data;
EXPECT_TRUE(
data.DeserializeFromBuffer(annotation.value(), annotation.size()));
EXPECT_EQ(kExpectedSize, data.GetRingBufferLength());
std::vector<uint8_t> popped_value;
LengthDelimitedRingBufferReader reader(data);
EXPECT_TRUE(reader.Pop(popped_value));
const std::vector<uint8_t> expected1 = {
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};
EXPECT_EQ(expected1, popped_value);
popped_value.clear();
EXPECT_TRUE(reader.Pop(popped_value));
const std::vector<uint8_t> expected2 = {'A', 'B', 'C', 'D', 'E', 'F'};
EXPECT_EQ(expected2, popped_value);
}
TEST_F(RingBufferAnnotationTest,
MultiplePushCallsWithWrappingShouldOverwriteInFIFOOrder) {
constexpr Annotation::Type kType = Annotation::UserDefinedType(1);
constexpr char kName[] = "annotation 1";
RingBufferAnnotation<10> annotation(kType, kName);
// Each Push() call will push 1 byte for the varint 128-encoded length,
// then the number of bytes specified.
constexpr char kFirst[] = "AAA";
constexpr char kSecond[] = "BBB";
constexpr char kThird[] = "CCC";
// This takes up bytes 0-3 of the 10-byte RingBufferAnnotation.
ASSERT_TRUE(annotation.Push(reinterpret_cast<const uint8_t*>(kFirst), 3));
// This takes up bytes 4-7 of the 10-byte RingBufferAnnotation.
ASSERT_TRUE(annotation.Push(reinterpret_cast<const uint8_t*>(kSecond), 3));
// This should wrap around the end of the array and overwrite kFirst since it
// needs 4 bytes but there are only 2 left.
ASSERT_TRUE(annotation.Push(reinterpret_cast<const uint8_t*>(kThird), 3));
// The size of the annotation should include the header and the full 10 bytes
// of the ring buffer, since the third write wrapped around the end.
ASSERT_EQ(kRingBufferHeaderSize + 10u, annotation.size());
// This data size needs to match the size in the RingBufferAnnotation above.
RingBufferData<10> data;
ASSERT_TRUE(
data.DeserializeFromBuffer(annotation.value(), annotation.size()));
std::vector<uint8_t> popped_value;
LengthDelimitedRingBufferReader reader(data);
ASSERT_TRUE(reader.Pop(popped_value));
// "AAA" has been overwritten, so the first thing popped should be "BBB".
const std::vector<uint8_t> expected_b = {'B', 'B', 'B'};
EXPECT_EQ(expected_b, popped_value);
popped_value.clear();
ASSERT_TRUE(reader.Pop(popped_value));
const std::vector<uint8_t> expected_c = {'C', 'C', 'C'};
EXPECT_EQ(expected_c, popped_value);
}
} // namespace
} // namespace test
} // namespace crashpad