mirror of
https://github.com/chromium/crashpad.git
synced 2024-12-25 22:30:49 +08:00
212b8f6b8c
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>
189 lines
6.0 KiB
C++
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
|