OLD | NEW |
(Empty) | |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "components/tracing/core/trace_buffer_writer.h" |
| 6 |
| 7 #include "base/memory/ptr_util.h" |
| 8 #include "base/strings/string_number_conversions.h" |
| 9 #include "components/tracing/core/trace_ring_buffer.h" |
| 10 #include "components/tracing/test/golden_protos/events_chunk.pb.h" |
| 11 #include "testing/gtest/include/gtest/gtest.h" |
| 12 |
| 13 namespace tracing { |
| 14 namespace v2 { |
| 15 namespace { |
| 16 |
| 17 class MockEvent : public proto::Event { |
| 18 public: |
| 19 static TraceEventHandle Add(TraceBufferWriter* writer, size_t event_size) { |
| 20 TraceEventHandle handle = writer->AddEvent(); |
| 21 MockEvent* mock_event = static_cast<MockEvent*>(&*handle); |
| 22 |
| 23 size_t buffer_size = 0; |
| 24 DCHECK_GT(event_size, 2u); |
| 25 if (event_size < (1 << 7) + 2) |
| 26 buffer_size = event_size - 2; |
| 27 else if (event_size < (1 << 14) + 3) |
| 28 buffer_size = event_size - 3; |
| 29 else if (event_size < (1 << 21) + 4) |
| 30 buffer_size = event_size - 4; |
| 31 else |
| 32 NOTREACHED(); |
| 33 |
| 34 DCHECK(buffer_size); |
| 35 std::unique_ptr<uint8_t[]> buf(new uint8_t[buffer_size]); |
| 36 memset(buf.get(), 0, buffer_size); |
| 37 mock_event->AppendBytes(1, buf.get(), buffer_size); |
| 38 |
| 39 return handle; |
| 40 } |
| 41 |
| 42 private: |
| 43 DISALLOW_COPY_AND_ASSIGN(MockEvent); |
| 44 }; |
| 45 |
| 46 constexpr uint32_t kChunkSize = TraceRingBuffer::Chunk::kSize; |
| 47 constexpr uint32_t kNumChunks = 10; |
| 48 constexpr size_t kBufferSize = kNumChunks * kChunkSize; |
| 49 |
| 50 class TraceBufferWriterTest : public ::testing::Test { |
| 51 public: |
| 52 void SetUp() override { |
| 53 ring_buf_mem_.reset(new uint8_t[kBufferSize]); |
| 54 memset(ring_buf_mem_.get(), 0, kBufferSize); |
| 55 ring_buf_.reset(new TraceRingBuffer(ring_buf_mem_.get(), kBufferSize)); |
| 56 |
| 57 // Estimate the size required to fill one event. |
| 58 std::unique_ptr<TraceBufferWriter> writer = CreateWriter(1); |
| 59 MockEvent::Add(&*writer, 4); |
| 60 event_size_to_fill_chunk_ = writer->stream_writer().bytes_available() + 4; |
| 61 writer.reset(); |
| 62 ring_buf_.reset(new TraceRingBuffer(ring_buf_mem_.get(), kBufferSize)); |
| 63 } |
| 64 |
| 65 void TearDown() override { |
| 66 ring_buf_.reset(); |
| 67 ring_buf_mem_.reset(); |
| 68 } |
| 69 |
| 70 std::unique_ptr<TraceBufferWriter> CreateWriter(uint32_t writer_id) { |
| 71 return base::WrapUnique(new TraceBufferWriter(ring_buf_.get(), writer_id)); |
| 72 } |
| 73 |
| 74 const TraceRingBuffer::Chunk* GetChunk(uint32_t i) { |
| 75 DCHECK_LT(i, kNumChunks); |
| 76 return &ring_buf_->chunks_for_testing()[i]; |
| 77 } |
| 78 |
| 79 proto::EventsChunk ReadBackAndTestChunk(uint32_t chunk_id, |
| 80 uint32_t expected_writer_id, |
| 81 uint32_t expected_seq_id, |
| 82 int expected_num_events, |
| 83 bool expected_first_event_continues, |
| 84 bool expected_last_event_continues) { |
| 85 const TraceRingBuffer::Chunk* chunk = GetChunk(chunk_id); |
| 86 proto::EventsChunk parsed_chunk; |
| 87 EXPECT_TRUE( |
| 88 parsed_chunk.ParseFromArray(chunk->payload(), chunk->used_size())); |
| 89 |
| 90 EXPECT_TRUE(parsed_chunk.has_writer_id()); |
| 91 EXPECT_EQ(expected_writer_id, parsed_chunk.writer_id()); |
| 92 |
| 93 EXPECT_TRUE(parsed_chunk.has_seq_id()); |
| 94 EXPECT_EQ(expected_seq_id, parsed_chunk.seq_id()); |
| 95 |
| 96 EXPECT_EQ(expected_first_event_continues, |
| 97 parsed_chunk.first_event_continues_from_prev_chunk()); |
| 98 EXPECT_EQ(expected_last_event_continues, |
| 99 parsed_chunk.last_event_continues_on_next_chunk()); |
| 100 EXPECT_EQ(expected_num_events, parsed_chunk.events_size()); |
| 101 return parsed_chunk; |
| 102 } |
| 103 |
| 104 const TraceRingBuffer& ring_buffer() const { return *ring_buf_; } |
| 105 size_t event_size_to_fill_chunk() const { return event_size_to_fill_chunk_; } |
| 106 |
| 107 private: |
| 108 std::unique_ptr<uint8_t[]> ring_buf_mem_; |
| 109 std::unique_ptr<TraceRingBuffer> ring_buf_; |
| 110 size_t event_size_to_fill_chunk_; |
| 111 }; |
| 112 |
| 113 TEST_F(TraceBufferWriterTest, SingleEvent) { |
| 114 const uint32_t kWriterId = 0x42; |
| 115 std::unique_ptr<TraceBufferWriter> writer = CreateWriter(kWriterId); |
| 116 MockEvent::Add(writer.get(), 7); |
| 117 writer->Flush(); |
| 118 |
| 119 auto parsed_chunk = ReadBackAndTestChunk(0, kWriterId, 0, 1, false, false); |
| 120 EXPECT_EQ(7u, parsed_chunk.events(0).size()); |
| 121 } |
| 122 |
| 123 TEST_F(TraceBufferWriterTest, ManySmallEvents) { |
| 124 const uint32_t kWriterId = 0x42; |
| 125 std::unique_ptr<TraceBufferWriter> writer = CreateWriter(kWriterId); |
| 126 |
| 127 uint32_t last_owned_chunk_id = 1; |
| 128 uint32_t num_times_did_switch_to_chunk[kNumChunks] = {}; |
| 129 |
| 130 // kBufferSize here is just an upper bound to prevent the test to get stuck |
| 131 // undefinitely in the loop if it fails. |
| 132 for (size_t i = 0; i < kBufferSize; i++) { |
| 133 MockEvent::Add(&*writer, 5); |
| 134 |
| 135 // Small events shouldn't never cause more than a chunk to be owned. Check |
| 136 // that TraceBufferWriter doesn't accidentally retain chunks. |
| 137 uint32_t num_chunks_owned = 0; |
| 138 for (uint32_t chunk_id = 0; chunk_id < kNumChunks; chunk_id++) { |
| 139 const bool is_owned = GetChunk(chunk_id)->is_owned(); |
| 140 num_chunks_owned += is_owned ? 1 : 0; |
| 141 if (is_owned && chunk_id != last_owned_chunk_id) { |
| 142 last_owned_chunk_id = chunk_id; |
| 143 num_times_did_switch_to_chunk[chunk_id]++; |
| 144 } |
| 145 } |
| 146 ASSERT_EQ(1u, num_chunks_owned); |
| 147 |
| 148 // Stop once the last chunk has been filled twice. |
| 149 if (num_times_did_switch_to_chunk[kNumChunks - 1] == 2) |
| 150 break; |
| 151 } |
| 152 |
| 153 // Test the wrap-over logic: all chunks should have been filled twice. |
| 154 for (uint32_t chunk_id = 0; chunk_id < kNumChunks; chunk_id++) |
| 155 EXPECT_EQ(2u, num_times_did_switch_to_chunk[chunk_id]); |
| 156 |
| 157 // Test that Flush() releases all chunks. |
| 158 writer->Flush(); |
| 159 for (uint32_t chunk_id = 0; chunk_id < kNumChunks; chunk_id++) |
| 160 EXPECT_FALSE(GetChunk(chunk_id)->is_owned()); |
| 161 } |
| 162 |
| 163 TEST_F(TraceBufferWriterTest, OneWriterWithFragmentingEvents) { |
| 164 const uint32_t kWriterId = 0x42; |
| 165 std::unique_ptr<TraceBufferWriter> writer = CreateWriter(kWriterId); |
| 166 |
| 167 MockEvent::Add(&*writer, event_size_to_fill_chunk()); |
| 168 |
| 169 EXPECT_TRUE(GetChunk(0)->is_owned()); |
| 170 EXPECT_FALSE(GetChunk(1)->is_owned()); |
| 171 |
| 172 MockEvent::Add(&*writer, event_size_to_fill_chunk()); |
| 173 EXPECT_TRUE(GetChunk(1)->is_owned()); |
| 174 EXPECT_FALSE(GetChunk(2)->is_owned()); |
| 175 |
| 176 // Create one event which starts at the beginning of chunk 2 and overflows |
| 177 // into chunk 3. |
| 178 MockEvent::Add(&*writer, event_size_to_fill_chunk() + 1); |
| 179 EXPECT_TRUE(GetChunk(2)->is_owned()); |
| 180 EXPECT_TRUE(GetChunk(3)->is_owned()); |
| 181 |
| 182 // Adding a new event should cause the chunk 2 to be released, while chunk |
| 183 // 3 is still retained. |
| 184 MockEvent::Add(&*writer, 4); |
| 185 EXPECT_FALSE(GetChunk(2)->is_owned()); |
| 186 EXPECT_TRUE(GetChunk(3)->is_owned()); |
| 187 |
| 188 // Now add a very large event which spans across 3 chunks (chunks 3, 4 and 5). |
| 189 MockEvent::Add(&*writer, event_size_to_fill_chunk() * 2 + 1); |
| 190 EXPECT_TRUE(GetChunk(3)->is_owned()); |
| 191 EXPECT_TRUE(GetChunk(4)->is_owned()); |
| 192 EXPECT_TRUE(GetChunk(5)->is_owned()); |
| 193 |
| 194 // Add a final small event and check that chunks 3 and 4 are released. |
| 195 MockEvent::Add(&*writer, 4); |
| 196 EXPECT_FALSE(GetChunk(3)->is_owned()); |
| 197 EXPECT_FALSE(GetChunk(4)->is_owned()); |
| 198 EXPECT_TRUE(GetChunk(5)->is_owned()); |
| 199 |
| 200 // Flush and readback the chunks using the official protos. |
| 201 writer->Flush(); |
| 202 |
| 203 // The first two chunks should have one event each, neither of them wrapping. |
| 204 auto chunk = ReadBackAndTestChunk(0, kWriterId, 0, 1, false, false); |
| 205 EXPECT_EQ(event_size_to_fill_chunk(), chunk.events(0).size()); |
| 206 |
| 207 chunk = ReadBackAndTestChunk(1, kWriterId, 1, 1, false, false); |
| 208 EXPECT_EQ(event_size_to_fill_chunk(), chunk.events(0).size()); |
| 209 |
| 210 // Chunk 2 should have one partial event, which overflows into 3. |
| 211 chunk = ReadBackAndTestChunk(2, kWriterId, 2, 1, false, true); |
| 212 EXPECT_EQ(event_size_to_fill_chunk(), chunk.events(0).size()); |
| 213 |
| 214 // Chunk 3 should have the overflowing event from above, a small event, and |
| 215 // the beginning of the very large event. |
| 216 chunk = ReadBackAndTestChunk(3, kWriterId, 3, 3, true, true); |
| 217 EXPECT_EQ(4u, chunk.events(1).size()); |
| 218 |
| 219 // Chunk 4 should contain the partial continuation of the large event. |
| 220 chunk = ReadBackAndTestChunk(4, kWriterId, 4, 1, true, true); |
| 221 EXPECT_EQ(event_size_to_fill_chunk(), chunk.events(0).size()); |
| 222 |
| 223 // Chunk 5 should contain the end of the large event and the final small one. |
| 224 chunk = ReadBackAndTestChunk(5, kWriterId, 5, 2, true, false); |
| 225 EXPECT_EQ(4u, chunk.events(1).size()); |
| 226 } |
| 227 |
| 228 TEST_F(TraceBufferWriterTest, ManyWriters) { |
| 229 const uint32_t kNumWriters = kNumChunks / 2; |
| 230 std::unique_ptr<TraceBufferWriter> writer[kNumWriters]; |
| 231 |
| 232 for (uint32_t i = 0; i < kNumWriters; ++i) { |
| 233 writer[i] = CreateWriter(i + 1); |
| 234 MockEvent::Add(writer[i].get(), 4); |
| 235 EXPECT_EQ(writer[i]->writer_id(), GetChunk(i)->owner()); |
| 236 } |
| 237 |
| 238 // Write one large and one small event on each writer. |
| 239 for (uint32_t i = 0; i < kNumWriters; ++i) { |
| 240 MockEvent::Add(writer[i].get(), event_size_to_fill_chunk()); |
| 241 MockEvent::Add(writer[i].get(), 5 + i); |
| 242 } |
| 243 |
| 244 // At this point the first 5 chunks should be returned and the last 5 owned |
| 245 // by the respective 5 writers. |
| 246 for (uint32_t i = 0; i < kNumWriters; ++i) |
| 247 EXPECT_FALSE(GetChunk(i)->is_owned()); |
| 248 for (uint32_t i = kNumWriters; i < kNumWriters * 2; ++i) |
| 249 EXPECT_EQ(writer[i - kNumWriters]->writer_id(), GetChunk(i)->owner()); |
| 250 |
| 251 // Write one large event to writer 0 (currently owning chunk 5). That will |
| 252 // make it return the chunk 5 and take ownership of chunks [0, 4]. |
| 253 MockEvent::Add(writer[0].get(), event_size_to_fill_chunk()); |
| 254 auto retain_event = |
| 255 MockEvent::Add(writer[0].get(), event_size_to_fill_chunk() * 4 + 1); |
| 256 for (uint32_t i = 0; i < 5; ++i) |
| 257 EXPECT_EQ(writer[0]->writer_id(), GetChunk(i)->owner()); |
| 258 |
| 259 // At this point the only free chunk is chunk 5. Attempting a write from |
| 260 // another writer should fill that. |
| 261 EXPECT_FALSE(GetChunk(5)->is_owned()); |
| 262 auto retain_event_2 = |
| 263 MockEvent::Add(writer[3].get(), event_size_to_fill_chunk()); |
| 264 |
| 265 // Now all the chunks are taken. |
| 266 for (uint32_t i = 0; i < kNumChunks; ++i) |
| 267 EXPECT_TRUE(GetChunk(i)->is_owned()); |
| 268 |
| 269 // An attempt to write a larger event on another write should cause the ring |
| 270 // buffer to fall in bankrupcy mode. |
| 271 auto retain_event_3 = |
| 272 MockEvent::Add(writer[4].get(), event_size_to_fill_chunk()); |
| 273 EXPECT_EQ(ring_buffer().num_chunks(), ring_buffer().GetNumChunksTaken()); |
| 274 |
| 275 // A small writer to the writer 0 should cause it to return all chunks but the |
| 276 // last one and leave the bankrupcy chunk. |
| 277 retain_event = MockEvent::Add(writer[0].get(), 7); |
| 278 EXPECT_LT(ring_buffer().GetNumChunksTaken(), ring_buffer().num_chunks()); |
| 279 EXPECT_EQ(writer[0]->writer_id(), GetChunk(4)->owner()); |
| 280 for (uint32_t i = 0; i < 3; ++i) |
| 281 EXPECT_FALSE(GetChunk(i)->is_owned()); |
| 282 |
| 283 // Flush all the writers and test that all chunks are returned. |
| 284 for (uint32_t i = 0; i < kNumWriters; ++i) |
| 285 writer[i]->Flush(); |
| 286 for (uint32_t i = 0; i < kNumChunks; ++i) |
| 287 EXPECT_FALSE(GetChunk(i)->is_owned()); |
| 288 |
| 289 // Readback and test the content of the chunks. |
| 290 |
| 291 auto chunk = |
| 292 ReadBackAndTestChunk(4, writer[0]->writer_id(), 6, 2, true, false); |
| 293 EXPECT_EQ(7u, chunk.events(1).size()); |
| 294 |
| 295 // writer[1] and writer[2] just have a continuation and a small event each. |
| 296 chunk = ReadBackAndTestChunk(6, writer[1]->writer_id(), 1, 2, true, false); |
| 297 EXPECT_EQ(5u + 1, chunk.events(1).size()); |
| 298 |
| 299 chunk = ReadBackAndTestChunk(7, writer[2]->writer_id(), 1, 2, true, false); |
| 300 EXPECT_EQ(5u + 2, chunk.events(1).size()); |
| 301 |
| 302 // writer[3] did the last write before the bankrupcy and has one extra event. |
| 303 ReadBackAndTestChunk(8, writer[3]->writer_id(), 1, 3, true, true); |
| 304 |
| 305 // writer[4] overflew in the bankrupcy chunk, and has 3 events as well. |
| 306 ReadBackAndTestChunk(9, writer[4]->writer_id(), 1, 3, true, true); |
| 307 } |
| 308 |
| 309 } // namespace |
| 310 } // namespace v2 |
| 311 } // namespace tracing |
OLD | NEW |