1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #include <string.h> 18 19 #include <initializer_list> 20 #include <random> 21 #include <sstream> 22 #include <vector> 23 24 #include "perfetto/protozero/proto_utils.h" 25 #include "perfetto/tracing/core/basic_types.h" 26 #include "perfetto/tracing/core/shared_memory_abi.h" 27 #include "perfetto/tracing/core/trace_packet.h" 28 #include "src/tracing/core/trace_buffer.h" 29 #include "src/tracing/test/fake_packet.h" 30 31 #include "gmock/gmock.h" 32 #include "gtest/gtest.h" 33 34 namespace perfetto { 35 36 using ::testing::ContainerEq; 37 using ::testing::ElementsAre; 38 using ::testing::IsEmpty; 39 40 class TraceBufferTest : public testing::Test { 41 public: 42 using SequenceIterator = TraceBuffer::SequenceIterator; 43 using ChunkMetaKey = TraceBuffer::ChunkMeta::Key; 44 using ChunkRecord = TraceBuffer::ChunkRecord; 45 46 static constexpr uint8_t kContFromPrevChunk = 47 SharedMemoryABI::ChunkHeader::kFirstPacketContinuesFromPrevChunk; 48 static constexpr uint8_t kContOnNextChunk = 49 SharedMemoryABI::ChunkHeader::kLastPacketContinuesOnNextChunk; 50 static constexpr uint8_t kChunkNeedsPatching = 51 SharedMemoryABI::ChunkHeader::kChunkNeedsPatching; 52 53 FakeChunk CreateChunk(ProducerID p, WriterID w, ChunkID c) { 54 return FakeChunk(trace_buffer_.get(), p, w, c); 55 } 56 57 void ResetBuffer( 58 size_t size_, 59 TraceBuffer::OverwritePolicy policy = TraceBuffer::kOverwrite) { 60 trace_buffer_ = TraceBuffer::Create(size_, policy); 61 ASSERT_TRUE(trace_buffer_); 62 } 63 64 bool TryPatchChunkContents(ProducerID p, 65 WriterID w, 66 ChunkID c, 67 std::vector<TraceBuffer::Patch> patches, 68 bool other_patches_pending = false) { 69 return trace_buffer_->TryPatchChunkContents( 70 p, w, c, patches.data(), patches.size(), other_patches_pending); 71 } 72 73 std::vector<FakePacketFragment> ReadPacket( 74 TraceBuffer::PacketSequenceProperties* sequence_properties = nullptr, 75 bool* previous_packet_dropped = nullptr) { 76 std::vector<FakePacketFragment> fragments; 77 TracePacket packet; 78 TraceBuffer::PacketSequenceProperties ignored_sequence_properties{}; 79 bool ignored_previous_packet_dropped; 80 if (!trace_buffer_->ReadNextTracePacket( 81 &packet, 82 sequence_properties ? sequence_properties 83 : &ignored_sequence_properties, 84 previous_packet_dropped ? previous_packet_dropped 85 : &ignored_previous_packet_dropped)) { 86 return fragments; 87 } 88 for (const Slice& slice : packet.slices()) 89 fragments.emplace_back(slice.start, slice.size); 90 return fragments; 91 } 92 93 void AppendChunks( 94 std::initializer_list<std::tuple<ProducerID, WriterID, ChunkID>> chunks) { 95 for (const auto& c : chunks) { 96 char seed = 97 static_cast<char>(std::get<0>(c) + std::get<1>(c) + std::get<2>(c)); 98 CreateChunk(std::get<0>(c), std::get<1>(c), std::get<2>(c)) 99 .AddPacket(4, seed) 100 .CopyIntoTraceBuffer(); 101 } 102 } 103 104 bool IteratorSeqEq(ProducerID p, 105 WriterID w, 106 std::initializer_list<ChunkID> chunk_ids) { 107 std::stringstream expected_seq; 108 for (const auto& c : chunk_ids) 109 expected_seq << "{" << p << "," << w << "," << c << "},"; 110 111 std::stringstream actual_seq; 112 for (auto it = GetReadIterForSequence(p, w); it.is_valid(); it.MoveNext()) { 113 actual_seq << "{" << it.producer_id() << "," << it.writer_id() << "," 114 << it.chunk_id() << "},"; 115 } 116 std::string expected_seq_str = expected_seq.str(); 117 std::string actual_seq_str = actual_seq.str(); 118 EXPECT_EQ(expected_seq_str, actual_seq_str); 119 return expected_seq_str == actual_seq_str; 120 } 121 122 SequenceIterator GetReadIterForSequence(ProducerID p, WriterID w) { 123 TraceBuffer::ChunkMeta::Key key(p, w, 0); 124 return trace_buffer_->GetReadIterForSequence( 125 trace_buffer_->index_.lower_bound(key)); 126 } 127 128 void SuppressSanityDchecksForTesting() { 129 trace_buffer_->suppress_sanity_dchecks_for_testing_ = true; 130 } 131 132 std::vector<ChunkMetaKey> GetIndex() { 133 std::vector<ChunkMetaKey> keys; 134 keys.reserve(trace_buffer_->index_.size()); 135 for (const auto& it : trace_buffer_->index_) 136 keys.push_back(it.first); 137 return keys; 138 } 139 140 TraceBuffer* trace_buffer() { return trace_buffer_.get(); } 141 size_t size_to_end() { return trace_buffer_->size_to_end(); } 142 143 private: 144 std::unique_ptr<TraceBuffer> trace_buffer_; 145 }; 146 147 // ---------------------- 148 // Main TraceBuffer tests 149 // ---------------------- 150 151 // Note for the test code: remember that the resulting size of a chunk is: 152 // SUM(packets) + 16 (that is sizeof(ChunkRecord)). 153 // Also remember that chunks are rounded up to 16. So, unless we are testing the 154 // rounding logic, might be a good idea to create chunks of that size. 155 156 TEST_F(TraceBufferTest, ReadWrite_EmptyBuffer) { 157 ResetBuffer(4096); 158 trace_buffer()->BeginRead(); 159 ASSERT_THAT(ReadPacket(), IsEmpty()); 160 } 161 162 // On each iteration writes a fixed-size chunk and reads it back. 163 TEST_F(TraceBufferTest, ReadWrite_Simple) { 164 ResetBuffer(64 * 1024); 165 for (ChunkID chunk_id = 0; chunk_id < 1000; chunk_id++) { 166 char seed = static_cast<char>(chunk_id); 167 CreateChunk(ProducerID(1), WriterID(1), chunk_id) 168 .AddPacket(42, seed) 169 .CopyIntoTraceBuffer(); 170 trace_buffer()->BeginRead(); 171 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(42, seed))); 172 ASSERT_THAT(ReadPacket(), IsEmpty()); 173 EXPECT_EQ(chunk_id + 1u, trace_buffer()->stats().chunks_written()); 174 EXPECT_EQ(trace_buffer()->stats().chunks_written(), 175 trace_buffer()->stats().chunks_read()); 176 EXPECT_LT(0u, trace_buffer()->stats().bytes_written()); 177 EXPECT_EQ(trace_buffer()->stats().bytes_written(), 178 trace_buffer()->stats().bytes_read()); 179 EXPECT_EQ(0u, trace_buffer()->stats().padding_bytes_written()); 180 EXPECT_EQ(0u, trace_buffer()->stats().padding_bytes_cleared()); 181 } 182 } 183 184 TEST_F(TraceBufferTest, ReadWrite_OneChunkPerWriter) { 185 for (int8_t num_writers = 1; num_writers <= 10; num_writers++) { 186 ResetBuffer(4096); 187 for (char i = 1; i <= num_writers; i++) { 188 ASSERT_EQ(32u, CreateChunk(ProducerID(i), WriterID(i), ChunkID(i)) 189 .AddPacket(32 - 16, i) 190 .CopyIntoTraceBuffer()); 191 } 192 193 // The expected read sequence now is: c3, c4, c5. 194 trace_buffer()->BeginRead(); 195 for (char i = 1; i <= num_writers; i++) 196 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(32 - 16, i))); 197 ASSERT_THAT(ReadPacket(), IsEmpty()); 198 } // for(num_writers) 199 } 200 201 // Writes chunk that up filling the buffer precisely until the end, like this: 202 // [ c0: 512 ][ c1: 512 ][ c2: 1024 ][ c3: 2048 ] 203 // | ---------------- 4k buffer --------------- | 204 TEST_F(TraceBufferTest, ReadWrite_FillTillEnd) { 205 ResetBuffer(4096); 206 for (int i = 0; i < 3; i++) { 207 ASSERT_EQ(512u, CreateChunk(ProducerID(1), WriterID(1), ChunkID(i * 4)) 208 .AddPacket(512 - 16, 'a') 209 .CopyIntoTraceBuffer()); 210 ASSERT_EQ(512u, CreateChunk(ProducerID(1), WriterID(1), ChunkID(i * 4 + 1)) 211 .AddPacket(512 - 16, 'b') 212 .CopyIntoTraceBuffer()); 213 ASSERT_EQ(1024u, CreateChunk(ProducerID(1), WriterID(1), ChunkID(i * 4 + 2)) 214 .AddPacket(1024 - 16, 'c') 215 .CopyIntoTraceBuffer()); 216 ASSERT_EQ(2048u, CreateChunk(ProducerID(1), WriterID(1), ChunkID(i * 4 + 3)) 217 .AddPacket(2048 - 16, 'd') 218 .CopyIntoTraceBuffer()); 219 220 // At this point the write pointer should have been reset at the beginning. 221 ASSERT_EQ(4096u, size_to_end()); 222 223 trace_buffer()->BeginRead(); 224 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(512 - 16, 'a'))); 225 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(512 - 16, 'b'))); 226 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(1024 - 16, 'c'))); 227 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(2048 - 16, 'd'))); 228 ASSERT_THAT(ReadPacket(), IsEmpty()); 229 } 230 } 231 232 // Similar to the above, but this time leaves some gap at the end and then 233 // tries to add a chunk that doesn't fit to exercise the padding-at-end logic. 234 // Initial condition: 235 // [ c0: 128 ][ c1: 256 ][ c2: 512 ][ c3: 1024 ][ c4: 2048 ]{ 128 padding } 236 // | ------------------------------- 4k buffer ------------------------------ | 237 // 238 // At this point we try to insert a 512 Bytes chunk (c5). The result should be: 239 // [ c5: 512 ]{ padding }[c3: 1024 ][ c4: 2048 ]{ 128 padding } 240 // | ------------------------------- 4k buffer ------------------------------ | 241 TEST_F(TraceBufferTest, ReadWrite_Padding) { 242 ResetBuffer(4096); 243 ASSERT_EQ(128u, CreateChunk(ProducerID(1), WriterID(1), ChunkID(0)) 244 .AddPacket(128 - 16, 'a') 245 .CopyIntoTraceBuffer()); 246 ASSERT_EQ(256u, CreateChunk(ProducerID(1), WriterID(1), ChunkID(1)) 247 .AddPacket(256 - 16, 'b') 248 .CopyIntoTraceBuffer()); 249 ASSERT_EQ(512u, CreateChunk(ProducerID(1), WriterID(1), ChunkID(2)) 250 .AddPacket(512 - 16, 'c') 251 .CopyIntoTraceBuffer()); 252 ASSERT_EQ(1024u, CreateChunk(ProducerID(1), WriterID(1), ChunkID(3)) 253 .AddPacket(1024 - 16, 'd') 254 .CopyIntoTraceBuffer()); 255 ASSERT_EQ(2048u, CreateChunk(ProducerID(1), WriterID(1), ChunkID(4)) 256 .AddPacket(2048 - 16, 'e') 257 .CopyIntoTraceBuffer()); 258 259 // Now write c5 that will cause wrapping + padding. 260 ASSERT_EQ(128u, size_to_end()); 261 ASSERT_EQ(512u, CreateChunk(ProducerID(1), WriterID(1), ChunkID(5)) 262 .AddPacket(512 - 16, 'f') 263 .CopyIntoTraceBuffer()); 264 ASSERT_EQ(4096u - 512, size_to_end()); 265 266 // The expected read sequence now is: c3, c4, c5. 267 trace_buffer()->BeginRead(); 268 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(1024 - 16, 'd'))); 269 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(2048 - 16, 'e'))); 270 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(512 - 16, 'f'))); 271 ASSERT_THAT(ReadPacket(), IsEmpty()); 272 273 EXPECT_EQ(6u, trace_buffer()->stats().chunks_written()); 274 EXPECT_EQ(3u, trace_buffer()->stats().chunks_overwritten()); 275 EXPECT_EQ(3u, trace_buffer()->stats().chunks_read()); 276 EXPECT_EQ(4480u, trace_buffer()->stats().bytes_written()); 277 EXPECT_EQ(896u, trace_buffer()->stats().bytes_overwritten()); 278 EXPECT_EQ(3584u, trace_buffer()->stats().bytes_read()); 279 EXPECT_EQ(512u, trace_buffer()->stats().padding_bytes_written()); 280 EXPECT_EQ(0u, trace_buffer()->stats().padding_bytes_cleared()); 281 282 // Adding another chunk should clear some of the padding. 283 ASSERT_EQ(128u, CreateChunk(ProducerID(1), WriterID(1), ChunkID(6)) 284 .AddPacket(128 - 16, 'g') 285 .CopyIntoTraceBuffer()); 286 EXPECT_EQ(384u, trace_buffer()->stats().padding_bytes_cleared()); 287 } 288 289 // Like ReadWrite_Padding, but this time the padding introduced is the minimum 290 // allowed (16 bytes). This is to exercise edge cases in the padding logic. 291 // [c0: 2048 ][c1: 1024 ][c2: 1008 ][c3: 16] 292 // [c4: 2032 ][c5: 1040 ][c6 :16][c7: 1080 ] 293 TEST_F(TraceBufferTest, ReadWrite_MinimalPadding) { 294 ResetBuffer(4096); 295 296 ASSERT_EQ(2048u, CreateChunk(ProducerID(1), WriterID(1), ChunkID(0)) 297 .AddPacket(2048 - 16, 'a') 298 .CopyIntoTraceBuffer()); 299 ASSERT_EQ(1024u, CreateChunk(ProducerID(1), WriterID(1), ChunkID(1)) 300 .AddPacket(1024 - 16, 'b') 301 .CopyIntoTraceBuffer()); 302 ASSERT_EQ(1008u, CreateChunk(ProducerID(1), WriterID(1), ChunkID(2)) 303 .AddPacket(1008 - 16, 'c') 304 .CopyIntoTraceBuffer()); 305 ASSERT_EQ(16u, CreateChunk(ProducerID(1), WriterID(1), ChunkID(3)) 306 .CopyIntoTraceBuffer()); 307 308 ASSERT_EQ(4096u, size_to_end()); 309 310 ASSERT_EQ(2032u, CreateChunk(ProducerID(1), WriterID(1), ChunkID(4)) 311 .AddPacket(2032 - 16, 'd') 312 .CopyIntoTraceBuffer()); 313 ASSERT_EQ(1040u, CreateChunk(ProducerID(1), WriterID(1), ChunkID(5)) 314 .AddPacket(1040 - 16, 'e') 315 .CopyIntoTraceBuffer()); 316 ASSERT_EQ(16u, CreateChunk(ProducerID(1), WriterID(1), ChunkID(6)) 317 .CopyIntoTraceBuffer()); 318 ASSERT_EQ(1008u, CreateChunk(ProducerID(1), WriterID(1), ChunkID(7)) 319 .AddPacket(1008 - 16, 'f') 320 .CopyIntoTraceBuffer()); 321 322 ASSERT_EQ(4096u, size_to_end()); 323 324 // The expected read sequence now is: c3, c4, c5. 325 trace_buffer()->BeginRead(); 326 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(2032 - 16, 'd'))); 327 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(1040 - 16, 'e'))); 328 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(1008 - 16, 'f'))); 329 for (int i = 0; i < 3; i++) 330 ASSERT_THAT(ReadPacket(), IsEmpty()); 331 } 332 333 TEST_F(TraceBufferTest, ReadWrite_RandomChunksNoWrapping) { 334 for (unsigned int seed = 1; seed <= 32; seed++) { 335 std::minstd_rand0 rnd_engine(seed); 336 ResetBuffer(4096 * (1 + rnd_engine() % 32)); 337 std::uniform_int_distribution<size_t> size_dist(18, 4096); 338 std::uniform_int_distribution<ProducerID> prod_dist(1, kMaxProducerID); 339 std::uniform_int_distribution<WriterID> wri_dist(1, kMaxWriterID); 340 ChunkID chunk_id = 0; 341 std::map<std::tuple<ProducerID, WriterID, ChunkID>, size_t> expected_chunks; 342 for (;;) { 343 const size_t chunk_size = size_dist(rnd_engine); 344 if (base::AlignUp<16>(chunk_size) >= size_to_end()) 345 break; 346 ProducerID p = prod_dist(rnd_engine); 347 WriterID w = wri_dist(rnd_engine); 348 ChunkID c = chunk_id++; 349 expected_chunks.emplace(std::make_tuple(p, w, c), chunk_size); 350 ASSERT_EQ(chunk_size, 351 CreateChunk(p, w, c) 352 .AddPacket(chunk_size - 16, static_cast<char>(chunk_size)) 353 .CopyIntoTraceBuffer()); 354 } // for(;;) 355 trace_buffer()->BeginRead(); 356 for (const auto& it : expected_chunks) { 357 const size_t chunk_size = it.second; 358 ASSERT_THAT(ReadPacket(), 359 ElementsAre(FakePacketFragment( 360 chunk_size - 16, static_cast<char>(chunk_size)))); 361 } 362 ASSERT_THAT(ReadPacket(), IsEmpty()); 363 } 364 } 365 366 // Tests the case of writing a chunk that leaves just sizeof(ChunkRecord) at 367 // the end of the buffer. 368 TEST_F(TraceBufferTest, ReadWrite_WrappingCases) { 369 ResetBuffer(4096); 370 ASSERT_EQ(4080u, CreateChunk(ProducerID(1), WriterID(1), ChunkID(0)) 371 .AddPacket(4080 - 16, 'a') 372 .CopyIntoTraceBuffer()); 373 trace_buffer()->BeginRead(); 374 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(4080 - 16, 'a'))); 375 ASSERT_THAT(ReadPacket(), IsEmpty()); 376 377 ASSERT_EQ(16u, CreateChunk(ProducerID(1), WriterID(1), ChunkID(1)) 378 .CopyIntoTraceBuffer()); 379 ASSERT_EQ(2048u, CreateChunk(ProducerID(1), WriterID(1), ChunkID(2)) 380 .AddPacket(2048 - 16, 'b') 381 .CopyIntoTraceBuffer()); 382 ASSERT_EQ(2048u, CreateChunk(ProducerID(1), WriterID(1), ChunkID(3)) 383 .AddPacket(2048 - 16, 'c') 384 .CopyIntoTraceBuffer()); 385 trace_buffer()->BeginRead(); 386 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(2048 - 16, 'b'))); 387 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(2048 - 16, 'c'))); 388 ASSERT_THAT(ReadPacket(), IsEmpty()); 389 } 390 391 // Tests that when records are removed when adding padding at the end because 392 // there is no space left. The scenario is the following: 393 // Initial condition: [ c0: 2048 ][ c1: 2048 ] 394 // 2nd iteration: [ c2: 2048] <-- write pointer is here 395 // At this point we try to add a 3072 bytes chunk. It won't fit because the 396 // space left till the end is just 2048 bytes. At this point we expect that a 397 // padding record is added in place of c1, and c1 is removed from the index. 398 // Final situation: [ c3: 3072 ][ PAD ] 399 TEST_F(TraceBufferTest, ReadWrite_PaddingAtEndUpdatesIndex) { 400 ResetBuffer(4096); 401 // Setup initial condition: [ c0: 2048 ][ c1: 2048 ] 402 ASSERT_EQ(2048u, CreateChunk(ProducerID(1), WriterID(1), ChunkID(0)) 403 .AddPacket(2048 - 16, 'a') 404 .CopyIntoTraceBuffer()); 405 ASSERT_EQ(2048u, CreateChunk(ProducerID(1), WriterID(1), ChunkID(1)) 406 .AddPacket(2048 - 16, 'b') 407 .CopyIntoTraceBuffer()); 408 ASSERT_THAT(GetIndex(), 409 ElementsAre(ChunkMetaKey(1, 1, 0), ChunkMetaKey(1, 1, 1))); 410 411 // Wrap and get to this: [ c2: 2048] <-- write pointer is here 412 ASSERT_EQ(2048u, CreateChunk(ProducerID(1), WriterID(1), ChunkID(2)) 413 .AddPacket(2048 - 16, 'c') 414 .CopyIntoTraceBuffer()); 415 ASSERT_EQ(2048u, size_to_end()); 416 ASSERT_THAT(GetIndex(), 417 ElementsAre(ChunkMetaKey(1, 1, 1), ChunkMetaKey(1, 1, 2))); 418 419 // Force wrap because of lack of space and get: [ c3: 3072 ][ PAD ]. 420 ASSERT_EQ(3072u, CreateChunk(ProducerID(1), WriterID(1), ChunkID(3)) 421 .AddPacket(3072 - 16, 'd') 422 .CopyIntoTraceBuffer()); 423 ASSERT_THAT(GetIndex(), ElementsAre(ChunkMetaKey(1, 1, 3))); 424 425 trace_buffer()->BeginRead(); 426 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(3072 - 16, 'd'))); 427 ASSERT_THAT(ReadPacket(), IsEmpty()); 428 } 429 430 // Similar to ReadWrite_PaddingAtEndUpdatesIndex but makes it so that the 431 // various chunks don't perfectly align when wrapping. 432 TEST_F(TraceBufferTest, ReadWrite_PaddingAtEndUpdatesIndexMisaligned) { 433 ResetBuffer(4096); 434 435 // [c0: 512][c1: 512][c2: 512][c3: 512][c4: 512][c5: 512][c6: 512][c7: 512] 436 for (char i = 0; i < 8; i++) { 437 ASSERT_EQ(512u, CreateChunk(ProducerID(1), WriterID(1), ChunkID(i)) 438 .AddPacket(512 - 16, 'a' + i) 439 .CopyIntoTraceBuffer()); 440 } 441 ASSERT_EQ(8u, GetIndex().size()); 442 443 // [c8: 2080..........................][PAD][c5: 512][c6: 512][c7: 512] 444 // ^ write pointer is here. 445 ASSERT_EQ(2080u, CreateChunk(ProducerID(1), WriterID(1), ChunkID(8)) 446 .AddPacket(2080 - 16, 'i') 447 .CopyIntoTraceBuffer()); 448 ASSERT_EQ(2016u, size_to_end()); 449 ASSERT_THAT(GetIndex(), 450 ElementsAre(ChunkMetaKey(1, 1, 5), ChunkMetaKey(1, 1, 6), 451 ChunkMetaKey(1, 1, 7), ChunkMetaKey(1, 1, 8))); 452 453 // [ c9: 3104....................................][ PAD...............]. 454 ASSERT_EQ(3104u, CreateChunk(ProducerID(1), WriterID(1), ChunkID(9)) 455 .AddPacket(3104 - 16, 'j') 456 .CopyIntoTraceBuffer()); 457 ASSERT_THAT(GetIndex(), ElementsAre(ChunkMetaKey(1, 1, 9))); 458 459 trace_buffer()->BeginRead(); 460 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(3104u - 16, 'j'))); 461 ASSERT_THAT(ReadPacket(), IsEmpty()); 462 } 463 464 // Verify that empty packets are skipped. 465 TEST_F(TraceBufferTest, ReadWrite_EmptyPacket) { 466 ResetBuffer(4096); 467 CreateChunk(ProducerID(1), WriterID(1), 0) 468 .AddPacket(42, 1) 469 .AddPacket(1, 2) 470 .AddPacket(42, 3) 471 .CopyIntoTraceBuffer(); 472 473 trace_buffer()->BeginRead(); 474 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(42, 1))); 475 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(42, 3))); 476 ASSERT_THAT(ReadPacket(), IsEmpty()); 477 478 EXPECT_EQ(0u, trace_buffer()->stats().abi_violations()); 479 } 480 481 // -------------------------------------- 482 // Fragments stitching and skipping logic 483 // -------------------------------------- 484 485 TEST_F(TraceBufferTest, Fragments_Simple) { 486 ResetBuffer(4096); 487 CreateChunk(ProducerID(1), WriterID(1), ChunkID(0)) 488 .AddPacket(10, 'a', kContFromPrevChunk) 489 .AddPacket(20, 'b') 490 .AddPacket(30, 'c') 491 .AddPacket(10, 'd', kContOnNextChunk) 492 .CopyIntoTraceBuffer(); 493 CreateChunk(ProducerID(1), WriterID(1), ChunkID(1)) 494 .AddPacket(20, 'e', kContFromPrevChunk) 495 .AddPacket(30, 'f') 496 .CopyIntoTraceBuffer(); 497 498 trace_buffer()->BeginRead(); 499 // The (10, 'a') entry should be skipped because we don't have provided the 500 // previous chunk, hence should be treated as a data loss. 501 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(20, 'b'))); 502 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(30, 'c'))); 503 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(10, 'd'), 504 FakePacketFragment(20, 'e'))); 505 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(30, 'f'))); 506 ASSERT_THAT(ReadPacket(), IsEmpty()); 507 } 508 509 TEST_F(TraceBufferTest, Fragments_EdgeCases) { 510 ResetBuffer(4096); 511 CreateChunk(ProducerID(1), WriterID(1), ChunkID(0)) 512 .AddPacket(2, 'a', kContFromPrevChunk) 513 .CopyIntoTraceBuffer(); 514 CreateChunk(ProducerID(1), WriterID(1), ChunkID(1)) 515 .AddPacket(2, 'b', kContOnNextChunk) 516 .CopyIntoTraceBuffer(); 517 trace_buffer()->BeginRead(); 518 ASSERT_THAT(ReadPacket(), IsEmpty()); 519 520 // Now add the missing fragment. 521 CreateChunk(ProducerID(1), WriterID(1), ChunkID(2)) 522 .AddPacket(2, 'c', kContFromPrevChunk) 523 .CopyIntoTraceBuffer(); 524 trace_buffer()->BeginRead(); 525 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(2, 'b'), 526 FakePacketFragment(2, 'c'))); 527 ASSERT_THAT(ReadPacket(), IsEmpty()); 528 } 529 530 // The following tests verify that chunks received out-of-order are read in the 531 // correct order. 532 // 533 // Fragment order {0,2,1} for sequence {1,1}, without fragmenting packets. 534 TEST_F(TraceBufferTest, Fragments_OutOfOrderLastChunkIsMiddle) { 535 ResetBuffer(4096); 536 CreateChunk(ProducerID(1), WriterID(1), ChunkID(0)) 537 .AddPacket(10, 'a') 538 .CopyIntoTraceBuffer(); 539 CreateChunk(ProducerID(1), WriterID(1), ChunkID(2)) 540 .AddPacket(30, 'c') 541 .CopyIntoTraceBuffer(); 542 EXPECT_EQ(0u, trace_buffer()->stats().chunks_committed_out_of_order()); 543 trace_buffer()->BeginRead(); 544 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(10, 'a'))); 545 ASSERT_THAT(ReadPacket(), IsEmpty()); 546 547 CreateChunk(ProducerID(1), WriterID(1), ChunkID(1)) 548 .AddPacket(20, 'b') 549 .CopyIntoTraceBuffer(); 550 EXPECT_EQ(1u, trace_buffer()->stats().chunks_committed_out_of_order()); 551 trace_buffer()->BeginRead(); 552 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(20, 'b'))); 553 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(30, 'c'))); 554 ASSERT_THAT(ReadPacket(), IsEmpty()); 555 } 556 557 // Fragment order {0,2,1} for sequence {1,1}, with fragmenting packets. 558 TEST_F(TraceBufferTest, Fragments_OutOfOrderLastChunkIsMiddleFragmentation) { 559 ResetBuffer(4096); 560 CreateChunk(ProducerID(1), WriterID(1), ChunkID(0)) 561 .AddPacket(10, 'a', kContOnNextChunk) 562 .CopyIntoTraceBuffer(); 563 CreateChunk(ProducerID(1), WriterID(1), ChunkID(2)) 564 .AddPacket(30, 'c', kContFromPrevChunk) 565 .CopyIntoTraceBuffer(); 566 trace_buffer()->BeginRead(); 567 ASSERT_THAT(ReadPacket(), IsEmpty()); 568 569 CreateChunk(ProducerID(1), WriterID(1), ChunkID(1)) 570 .AddPacket(20, 'b', kContFromPrevChunk | kContOnNextChunk) 571 .CopyIntoTraceBuffer(); 572 trace_buffer()->BeginRead(); 573 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(10, 'a'), 574 FakePacketFragment(20, 'b'), 575 FakePacketFragment(30, 'c'))); 576 ASSERT_THAT(ReadPacket(), IsEmpty()); 577 } 578 579 // Fragment order {0,2,1,3} for sequence {1,1}, with fragmenting packets. Also 580 // verifies that another sequence isn't broken. 581 TEST_F(TraceBufferTest, Fragments_OutOfOrderLastChunkIsMaxFragmentation) { 582 ResetBuffer(4096); 583 CreateChunk(ProducerID(1), WriterID(1), ChunkID(0)) 584 .AddPacket(10, 'a', kContOnNextChunk) 585 .CopyIntoTraceBuffer(); 586 CreateChunk(ProducerID(1), WriterID(1), ChunkID(2)) 587 .AddPacket(30, 'c', kContFromPrevChunk) 588 .CopyIntoTraceBuffer(); 589 CreateChunk(ProducerID(1), WriterID(2), ChunkID(0)) 590 .AddPacket(10, 'd') 591 .CopyIntoTraceBuffer(); 592 trace_buffer()->BeginRead(); 593 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(10, 'd'))); 594 ASSERT_THAT(ReadPacket(), IsEmpty()); 595 596 CreateChunk(ProducerID(1), WriterID(1), ChunkID(1)) 597 .AddPacket(20, 'b', kContFromPrevChunk | kContOnNextChunk) 598 .CopyIntoTraceBuffer(); 599 CreateChunk(ProducerID(1), WriterID(1), ChunkID(3)) 600 .AddPacket(40, 'd') 601 .CopyIntoTraceBuffer(); 602 trace_buffer()->BeginRead(); 603 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(10, 'a'), 604 FakePacketFragment(20, 'b'), 605 FakePacketFragment(30, 'c'))); 606 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(40, 'd'))); 607 ASSERT_THAT(ReadPacket(), IsEmpty()); 608 } 609 610 // Fragment order {-2,1,-1,0} for sequence {1,1}, without fragmenting packets. 611 TEST_F(TraceBufferTest, Fragments_OutOfOrderWithIdOverflowADCB) { 612 ResetBuffer(4096); 613 CreateChunk(ProducerID(1), WriterID(1), ChunkID(kMaxChunkID - 1)) 614 .AddPacket(10, 'a') 615 .CopyIntoTraceBuffer(); 616 CreateChunk(ProducerID(1), WriterID(1), ChunkID(1)) 617 .AddPacket(40, 'd') 618 .CopyIntoTraceBuffer(); 619 trace_buffer()->BeginRead(); 620 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(10, 'a'))); 621 ASSERT_THAT(ReadPacket(), IsEmpty()); 622 623 CreateChunk(ProducerID(1), WriterID(1), ChunkID(0)) 624 .AddPacket(30, 'c') 625 .CopyIntoTraceBuffer(); 626 trace_buffer()->BeginRead(); 627 ASSERT_THAT(ReadPacket(), IsEmpty()); 628 629 CreateChunk(ProducerID(1), WriterID(1), ChunkID(kMaxChunkID)) 630 .AddPacket(20, 'b') 631 .CopyIntoTraceBuffer(); 632 trace_buffer()->BeginRead(); 633 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(20, 'b'))); 634 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(30, 'c'))); 635 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(40, 'd'))); 636 ASSERT_THAT(ReadPacket(), IsEmpty()); 637 } 638 639 // Fragment order {-2,0,-1,1} for sequence {1,1}, without fragmenting packets. 640 TEST_F(TraceBufferTest, Fragments_OutOfOrderWithIdOverflowACBD) { 641 ResetBuffer(4096); 642 CreateChunk(ProducerID(1), WriterID(1), ChunkID(kMaxChunkID - 1)) 643 .AddPacket(10, 'a') 644 .CopyIntoTraceBuffer(); 645 CreateChunk(ProducerID(1), WriterID(1), ChunkID(0)) 646 .AddPacket(30, 'c') 647 .CopyIntoTraceBuffer(); 648 trace_buffer()->BeginRead(); 649 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(10, 'a'))); 650 ASSERT_THAT(ReadPacket(), IsEmpty()); 651 652 CreateChunk(ProducerID(1), WriterID(1), ChunkID(kMaxChunkID)) 653 .AddPacket(20, 'b') 654 .CopyIntoTraceBuffer(); 655 trace_buffer()->BeginRead(); 656 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(20, 'b'))); 657 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(30, 'c'))); 658 ASSERT_THAT(ReadPacket(), IsEmpty()); 659 660 CreateChunk(ProducerID(1), WriterID(1), ChunkID(1)) 661 .AddPacket(40, 'd') 662 .CopyIntoTraceBuffer(); 663 trace_buffer()->BeginRead(); 664 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(40, 'd'))); 665 ASSERT_THAT(ReadPacket(), IsEmpty()); 666 } 667 668 TEST_F(TraceBufferTest, Fragments_EmptyChunkBefore) { 669 ResetBuffer(4096); 670 CreateChunk(ProducerID(1), WriterID(1), ChunkID(0)).CopyIntoTraceBuffer(); 671 CreateChunk(ProducerID(1), WriterID(1), ChunkID(1)) 672 .AddPacket(10, 'a') 673 .AddPacket(20, 'b', kContOnNextChunk) 674 .CopyIntoTraceBuffer(); 675 CreateChunk(ProducerID(1), WriterID(1), ChunkID(2)) 676 .AddPacket(30, 'c', kContFromPrevChunk) 677 .AddPacket(40, 'd', kContOnNextChunk) 678 .CopyIntoTraceBuffer(); 679 trace_buffer()->BeginRead(); 680 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(10, 'a'))); 681 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(20, 'b'), 682 FakePacketFragment(30, 'c'))); 683 ASSERT_THAT(ReadPacket(), IsEmpty()); 684 } 685 686 TEST_F(TraceBufferTest, Fragments_EmptyChunkAfter) { 687 ResetBuffer(4096); 688 CreateChunk(ProducerID(1), WriterID(1), ChunkID(0)) 689 .AddPacket(10, 'a') 690 .AddPacket(10, 'b', kContOnNextChunk) 691 .CopyIntoTraceBuffer(); 692 CreateChunk(ProducerID(1), WriterID(1), ChunkID(1)).CopyIntoTraceBuffer(); 693 trace_buffer()->BeginRead(); 694 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(10, 'a'))); 695 ASSERT_THAT(ReadPacket(), IsEmpty()); 696 } 697 698 // Set up a fragmented packet that happens to also have an empty chunk in the 699 // middle of the sequence. Test that it just gets skipped. 700 TEST_F(TraceBufferTest, Fragments_EmptyChunkInTheMiddle) { 701 ResetBuffer(4096); 702 CreateChunk(ProducerID(1), WriterID(1), ChunkID(0)) 703 .AddPacket(10, 'a', kContOnNextChunk) 704 .CopyIntoTraceBuffer(); 705 CreateChunk(ProducerID(1), WriterID(1), ChunkID(1)).CopyIntoTraceBuffer(); 706 CreateChunk(ProducerID(1), WriterID(1), ChunkID(2)) 707 .AddPacket(10, 'b', kContFromPrevChunk) 708 .AddPacket(20, 'c') 709 .CopyIntoTraceBuffer(); 710 trace_buffer()->BeginRead(); 711 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(10, 'a'), 712 FakePacketFragment(10, 'b'))); 713 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(20, 'c'))); 714 ASSERT_THAT(ReadPacket(), IsEmpty()); 715 } 716 717 // Generates sequences of fragmented packets of increasing length (|seq_len|), 718 // from [P0, P1a][P1y] to [P0, P1a][P1b][P1c]...[P1y]. Test that they are always 719 // read as one packet. 720 TEST_F(TraceBufferTest, Fragments_LongPackets) { 721 for (unsigned seq_len = 1; seq_len <= 10; seq_len++) { 722 ResetBuffer(4096); 723 std::vector<FakePacketFragment> expected_fragments; 724 expected_fragments.emplace_back(20, 'b'); 725 CreateChunk(ProducerID(1), WriterID(1), ChunkID(0)) 726 .AddPacket(10, 'a') 727 .AddPacket(20, 'b', kContOnNextChunk) 728 .CopyIntoTraceBuffer(); 729 for (unsigned i = 1; i <= seq_len; i++) { 730 char prefix = 'b' + static_cast<char>(i); 731 expected_fragments.emplace_back(20 + i, prefix); 732 CreateChunk(ProducerID(1), WriterID(1), ChunkID(i)) 733 .AddPacket(20 + i, prefix, kContFromPrevChunk | kContOnNextChunk) 734 .CopyIntoTraceBuffer(); 735 } 736 expected_fragments.emplace_back(30, 'y'); 737 CreateChunk(ProducerID(1), WriterID(1), ChunkID(seq_len + 1)) 738 .AddPacket(30, 'y', kContFromPrevChunk) 739 .AddPacket(50, 'z') 740 .CopyIntoTraceBuffer(); 741 742 trace_buffer()->BeginRead(); 743 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(10, 'a'))); 744 ASSERT_THAT(ReadPacket(), ContainerEq(expected_fragments)); 745 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(50, 'z'))); 746 ASSERT_THAT(ReadPacket(), IsEmpty()); 747 } 748 } 749 750 // Similar to Fragments_LongPacket, but covers also the case of ChunkID wrapping 751 // over its max value. 752 TEST_F(TraceBufferTest, Fragments_LongPacketWithWrappingID) { 753 ResetBuffer(4096); 754 std::vector<FakePacketFragment> expected_fragments; 755 756 for (ChunkID chunk_id = static_cast<ChunkID>(-2); chunk_id <= 2; chunk_id++) { 757 char prefix = static_cast<char>('c' + chunk_id); 758 expected_fragments.emplace_back(10 + chunk_id, prefix); 759 CreateChunk(ProducerID(1), WriterID(1), chunk_id) 760 .AddPacket(10 + chunk_id, prefix, kContOnNextChunk) 761 .CopyIntoTraceBuffer(); 762 } 763 trace_buffer()->BeginRead(); 764 ASSERT_THAT(ReadPacket(), ContainerEq(expected_fragments)); 765 ASSERT_THAT(ReadPacket(), IsEmpty()); 766 } 767 768 TEST_F(TraceBufferTest, Fragments_PreserveUID) { 769 ResetBuffer(4096); 770 CreateChunk(ProducerID(1), WriterID(1), ChunkID(0)) 771 .AddPacket(10, 'a') 772 .AddPacket(10, 'b', kContOnNextChunk) 773 .SetUID(11) 774 .CopyIntoTraceBuffer(); 775 CreateChunk(ProducerID(2), WriterID(1), ChunkID(0)) 776 .AddPacket(10, 'c') 777 .AddPacket(10, 'd') 778 .SetUID(22) 779 .CopyIntoTraceBuffer(); 780 CreateChunk(ProducerID(1), WriterID(1), ChunkID(1)) 781 .AddPacket(10, 'e', kContFromPrevChunk) 782 .AddPacket(10, 'f') 783 .SetUID(11) 784 .CopyIntoTraceBuffer(); 785 trace_buffer()->BeginRead(); 786 TraceBuffer::PacketSequenceProperties sequence_properties; 787 ASSERT_THAT(ReadPacket(&sequence_properties), 788 ElementsAre(FakePacketFragment(10, 'a'))); 789 ASSERT_EQ(11u, sequence_properties.producer_uid_trusted); 790 791 ASSERT_THAT( 792 ReadPacket(&sequence_properties), 793 ElementsAre(FakePacketFragment(10, 'b'), FakePacketFragment(10, 'e'))); 794 ASSERT_EQ(11u, sequence_properties.producer_uid_trusted); 795 796 ASSERT_THAT(ReadPacket(&sequence_properties), 797 ElementsAre(FakePacketFragment(10, 'f'))); 798 ASSERT_EQ(11u, sequence_properties.producer_uid_trusted); 799 800 ASSERT_THAT(ReadPacket(&sequence_properties), 801 ElementsAre(FakePacketFragment(10, 'c'))); 802 ASSERT_EQ(22u, sequence_properties.producer_uid_trusted); 803 804 ASSERT_THAT(ReadPacket(&sequence_properties), 805 ElementsAre(FakePacketFragment(10, 'd'))); 806 ASSERT_EQ(22u, sequence_properties.producer_uid_trusted); 807 808 ASSERT_THAT(ReadPacket(), IsEmpty()); 809 } 810 811 // -------------------------- 812 // Out of band patching tests 813 // -------------------------- 814 815 TEST_F(TraceBufferTest, Patching_Simple) { 816 ResetBuffer(4096); 817 CreateChunk(ProducerID(1), WriterID(1), ChunkID(0)) 818 .AddPacket(100, 'a') 819 .CopyIntoTraceBuffer(); 820 CreateChunk(ProducerID(2), WriterID(1), ChunkID(0)) 821 .AddPacket(9, 'b') 822 .ClearBytes(5, 4) // 5 := 4th payload byte. Byte 0 is the varint header. 823 .CopyIntoTraceBuffer(); 824 CreateChunk(ProducerID(3), WriterID(1), ChunkID(0)) 825 .AddPacket(100, 'c') 826 .CopyIntoTraceBuffer(); 827 ASSERT_TRUE(TryPatchChunkContents(ProducerID(2), WriterID(1), ChunkID(0), 828 {{5, {{'Y', 'M', 'C', 'A'}}}})); 829 trace_buffer()->BeginRead(); 830 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(100, 'a'))); 831 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment("b00-YMCA", 8))); 832 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(100, 'c'))); 833 ASSERT_THAT(ReadPacket(), IsEmpty()); 834 } 835 836 TEST_F(TraceBufferTest, Patching_SkipIfChunkDoesntExist) { 837 ResetBuffer(4096); 838 CreateChunk(ProducerID(1), WriterID(1), ChunkID(0)) 839 .AddPacket(100, 'a') 840 .CopyIntoTraceBuffer(); 841 ASSERT_FALSE(TryPatchChunkContents(ProducerID(1), WriterID(2), ChunkID(0), 842 {{0, {{'X', 'X', 'X', 'X'}}}})); 843 ASSERT_FALSE(TryPatchChunkContents(ProducerID(1), WriterID(1), ChunkID(1), 844 {{0, {{'X', 'X', 'X', 'X'}}}})); 845 ASSERT_FALSE(TryPatchChunkContents(ProducerID(1), WriterID(1), ChunkID(-1), 846 {{0, {{'X', 'X', 'X', 'X'}}}})); 847 trace_buffer()->BeginRead(); 848 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(100, 'a'))); 849 ASSERT_THAT(ReadPacket(), IsEmpty()); 850 } 851 852 TEST_F(TraceBufferTest, Patching_AtBoundariesOfChunk) { 853 ResetBuffer(4096); 854 CreateChunk(ProducerID(1), WriterID(1), ChunkID(0)) 855 .AddPacket(100, 'a', kContOnNextChunk) 856 .CopyIntoTraceBuffer(); 857 CreateChunk(ProducerID(1), WriterID(1), ChunkID(1)) 858 .AddPacket(16, 'b', kContFromPrevChunk | kContOnNextChunk) 859 .ClearBytes(1, 4) 860 .ClearBytes(16 - 4, 4) 861 .CopyIntoTraceBuffer(); 862 CreateChunk(ProducerID(1), WriterID(1), ChunkID(2)) 863 .AddPacket(100, 'c', kContFromPrevChunk) 864 .CopyIntoTraceBuffer(); 865 ASSERT_TRUE(TryPatchChunkContents( 866 ProducerID(1), WriterID(1), ChunkID(1), 867 {{1, {{'P', 'E', 'R', 'F'}}}, {16 - 4, {{'E', 'T', 'T', 'O'}}}})); 868 trace_buffer()->BeginRead(); 869 ASSERT_THAT(ReadPacket(), 870 ElementsAre(FakePacketFragment(100, 'a'), 871 FakePacketFragment("PERFb01-b02ETTO", 15), 872 FakePacketFragment(100, 'c'))); 873 ASSERT_THAT(ReadPacket(), IsEmpty()); 874 } 875 876 // Tests kChunkNeedsPatching logic: chunks that are marked as "pending patch" 877 // should not be read until the patch has happened. 878 TEST_F(TraceBufferTest, Patching_ReadWaitsForPatchComplete) { 879 ResetBuffer(4096); 880 881 CreateChunk(ProducerID(1), WriterID(1), ChunkID(0)) 882 .AddPacket(16, 'a', kChunkNeedsPatching) 883 .ClearBytes(1, 4) // 1 := 0th payload byte. Byte 0 is the varint header. 884 .CopyIntoTraceBuffer(); 885 CreateChunk(ProducerID(1), WriterID(1), ChunkID(1)) 886 .AddPacket(16, 'b') 887 .CopyIntoTraceBuffer(); 888 889 CreateChunk(ProducerID(2), WriterID(1), ChunkID(0)) 890 .AddPacket(16, 'c') 891 .CopyIntoTraceBuffer(); 892 CreateChunk(ProducerID(2), WriterID(1), ChunkID(1)) 893 .AddPacket(16, 'd', kChunkNeedsPatching) 894 .ClearBytes(1, 4) // 1 := 0th payload byte. Byte 0 is the varint header. 895 .CopyIntoTraceBuffer(); 896 CreateChunk(ProducerID(2), WriterID(1), ChunkID(2)) 897 .AddPacket(16, 'e') 898 .CopyIntoTraceBuffer(); 899 900 CreateChunk(ProducerID(3), WriterID(1), ChunkID(0)) 901 .AddPacket(16, 'f', kChunkNeedsPatching) 902 .ClearBytes(1, 8) // 1 := 0th payload byte. Byte 0 is the varint header. 903 .CopyIntoTraceBuffer(); 904 905 // The only thing that can be read right now is the 1st packet of the 2nd 906 // sequence. All the rest is blocked waiting for patching. 907 trace_buffer()->BeginRead(); 908 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(16, 'c'))); 909 ASSERT_THAT(ReadPacket(), IsEmpty()); 910 911 // Now patch the 2nd sequence and check that the sequence is unblocked. 912 ASSERT_TRUE(TryPatchChunkContents(ProducerID(2), WriterID(1), ChunkID(1), 913 {{1, {{'P', 'A', 'T', 'C'}}}})); 914 trace_buffer()->BeginRead(); 915 ASSERT_THAT(ReadPacket(), 916 ElementsAre(FakePacketFragment("PATCd01-d02-d03", 15))); 917 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(16, 'e'))); 918 ASSERT_THAT(ReadPacket(), IsEmpty()); 919 920 // Now patch the 3rd sequence, but in the first patch set 921 // |other_patches_pending| to true, so that the sequence is unblocked only 922 // after the 2nd patch. 923 ASSERT_TRUE(TryPatchChunkContents(ProducerID(3), WriterID(1), ChunkID(0), 924 {{1, {{'P', 'E', 'R', 'F'}}}}, 925 /*other_patches_pending=*/true)); 926 trace_buffer()->BeginRead(); 927 ASSERT_THAT(ReadPacket(), IsEmpty()); 928 929 ASSERT_TRUE(TryPatchChunkContents(ProducerID(3), WriterID(1), ChunkID(0), 930 {{5, {{'E', 'T', 'T', 'O'}}}}, 931 /*other_patches_pending=*/false)); 932 trace_buffer()->BeginRead(); 933 ASSERT_THAT(ReadPacket(), 934 ElementsAre(FakePacketFragment("PERFETTOf02-f03", 15))); 935 ASSERT_THAT(ReadPacket(), IsEmpty()); 936 937 } // namespace perfetto 938 939 // --------------------- 940 // Malicious input tests 941 // --------------------- 942 943 TEST_F(TraceBufferTest, Malicious_ZeroSizedChunk) { 944 ResetBuffer(4096); 945 SuppressSanityDchecksForTesting(); 946 CreateChunk(ProducerID(1), WriterID(1), ChunkID(0)) 947 .AddPacket(32, 'a') 948 .CopyIntoTraceBuffer(); 949 950 uint8_t valid_ptr = 0; 951 trace_buffer()->CopyChunkUntrusted( 952 ProducerID(1), uid_t(0), WriterID(1), ChunkID(1), 1 /* num packets */, 953 0 /* flags */, true /* chunk_complete */, &valid_ptr, sizeof(valid_ptr)); 954 955 CreateChunk(ProducerID(1), WriterID(1), ChunkID(2)) 956 .AddPacket(32, 'b') 957 .CopyIntoTraceBuffer(); 958 959 trace_buffer()->BeginRead(); 960 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(32, 'a'))); 961 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(32, 'b'))); 962 ASSERT_THAT(ReadPacket(), IsEmpty()); 963 } 964 965 // Attempting to write a chunk bigger than ChunkRecord::kMaxSize should end up 966 // in a no-op. 967 TEST_F(TraceBufferTest, Malicious_ChunkTooBig) { 968 ResetBuffer(4096); 969 SuppressSanityDchecksForTesting(); 970 CreateChunk(ProducerID(1), WriterID(1), ChunkID(0)) 971 .AddPacket(4096, 'a') 972 .AddPacket(2048, 'b') 973 .CopyIntoTraceBuffer(); 974 trace_buffer()->BeginRead(); 975 ASSERT_THAT(ReadPacket(), IsEmpty()); 976 } 977 978 TEST_F(TraceBufferTest, Malicious_DeclareMorePacketsBeyondBoundaries) { 979 ResetBuffer(4096); 980 SuppressSanityDchecksForTesting(); 981 CreateChunk(ProducerID(1), WriterID(1), ChunkID(0)) 982 .AddPacket(64, 'a') 983 .IncrementNumPackets() 984 .IncrementNumPackets() 985 .CopyIntoTraceBuffer(); 986 CreateChunk(ProducerID(1), WriterID(2), ChunkID(0)) 987 .IncrementNumPackets() 988 .CopyIntoTraceBuffer(); 989 CreateChunk(ProducerID(1), WriterID(3), ChunkID(0)) 990 .AddPacket(32, 'b') 991 .CopyIntoTraceBuffer(); 992 trace_buffer()->BeginRead(); 993 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(64, 'a'))); 994 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(32, 'b'))); 995 ASSERT_THAT(ReadPacket(), IsEmpty()); 996 ASSERT_THAT(ReadPacket(), IsEmpty()); 997 } 998 999 TEST_F(TraceBufferTest, Malicious_ZeroVarintHeader) { 1000 ResetBuffer(4096); 1001 SuppressSanityDchecksForTesting(); 1002 // Create a standalone chunk where the varint header is == 0. 1003 CreateChunk(ProducerID(1), WriterID(1), ChunkID(0)) 1004 .AddPacket(4, 'a') 1005 .ClearBytes(0, 1) 1006 .AddPacket(4, 'b') 1007 .CopyIntoTraceBuffer(); 1008 CreateChunk(ProducerID(2), WriterID(1), ChunkID(0)) 1009 .AddPacket(4, 'c') 1010 .CopyIntoTraceBuffer(); 1011 trace_buffer()->BeginRead(); 1012 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(4, 'c'))); 1013 ASSERT_THAT(ReadPacket(), IsEmpty()); 1014 } 1015 1016 // Forge a chunk where the first packet is valid but the second packet has a 1017 // varint header that continues beyond the end of the chunk (and also beyond the 1018 // end of the buffer). 1019 TEST_F(TraceBufferTest, Malicious_OverflowingVarintHeader) { 1020 ResetBuffer(4096); 1021 SuppressSanityDchecksForTesting(); 1022 CreateChunk(ProducerID(1), WriterID(1), ChunkID(0)) 1023 .AddPacket(4079, 'a') // 4079 := 4096 - sizeof(ChunkRecord) - 1 1024 .AddPacket({0x82}) // 0x8*: that the varint continues on the next byte. 1025 .CopyIntoTraceBuffer(); 1026 trace_buffer()->BeginRead(); 1027 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(4079, 'a'))); 1028 ASSERT_THAT(ReadPacket(), IsEmpty()); 1029 ASSERT_THAT(ReadPacket(), IsEmpty()); 1030 } 1031 1032 TEST_F(TraceBufferTest, Malicious_VarintHeaderTooBig) { 1033 ResetBuffer(4096); 1034 SuppressSanityDchecksForTesting(); 1035 1036 // Add a valid chunk. 1037 CreateChunk(ProducerID(1), WriterID(1), ChunkID(0)) 1038 .AddPacket(32, 'a') 1039 .CopyIntoTraceBuffer(); 1040 1041 // Forge a packet which has a varint header that is just off by one. 1042 CreateChunk(ProducerID(2), WriterID(1), ChunkID(0)) 1043 .AddPacket({0x16, '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 1044 'c', 'd', 'e', 'f'}) 1045 .CopyIntoTraceBuffer(); 1046 1047 // Forge a packet which has a varint header that tries to hit an overflow. 1048 CreateChunk(ProducerID(3), WriterID(1), ChunkID(0)) 1049 .AddPacket({0xff, 0xff, 0xff, 0x7f}) 1050 .CopyIntoTraceBuffer(); 1051 1052 // Forge a packet which has a jumbo varint header: 0xff, 0xff .. 0x7f. 1053 std::vector<uint8_t> chunk; 1054 chunk.insert(chunk.end(), 128 - sizeof(ChunkRecord), 0xff); 1055 chunk.back() = 0x7f; 1056 trace_buffer()->CopyChunkUntrusted( 1057 ProducerID(4), uid_t(0), WriterID(1), ChunkID(1), 1 /* num packets */, 1058 0 /* flags*/, true /* chunk_complete */, chunk.data(), chunk.size()); 1059 1060 // Add a valid chunk. 1061 CreateChunk(ProducerID(1), WriterID(1), ChunkID(1)) 1062 .AddPacket(32, 'b') 1063 .CopyIntoTraceBuffer(); 1064 1065 trace_buffer()->BeginRead(); 1066 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(32, 'a'))); 1067 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(32, 'b'))); 1068 ASSERT_THAT(ReadPacket(), IsEmpty()); 1069 } 1070 1071 // Similar to Malicious_VarintHeaderTooBig, but this time the full chunk 1072 // contains an enormous varint number that tries to overflow. 1073 TEST_F(TraceBufferTest, Malicious_JumboVarint) { 1074 ResetBuffer(64 * 1024); 1075 SuppressSanityDchecksForTesting(); 1076 1077 std::vector<uint8_t> chunk; 1078 chunk.insert(chunk.end(), 64 * 1024 - sizeof(ChunkRecord) * 2, 0xff); 1079 chunk.back() = 0x7f; 1080 for (int i = 0; i < 3; i++) { 1081 trace_buffer()->CopyChunkUntrusted( 1082 ProducerID(1), uid_t(0), WriterID(1), ChunkID(1), 1 /* num packets */, 1083 0 /* flags */, true /* chunk_complete */, chunk.data(), chunk.size()); 1084 } 1085 1086 trace_buffer()->BeginRead(); 1087 ASSERT_THAT(ReadPacket(), IsEmpty()); 1088 } 1089 1090 // Like the Malicious_ZeroVarintHeader, but put the chunk in the middle of a 1091 // sequence that would be otherwise valid. The zero-sized fragment should be 1092 // skipped. 1093 TEST_F(TraceBufferTest, Malicious_ZeroVarintHeaderInSequence) { 1094 ResetBuffer(4096); 1095 SuppressSanityDchecksForTesting(); 1096 CreateChunk(ProducerID(1), WriterID(1), ChunkID(0)) 1097 .AddPacket(4, 'a', kContOnNextChunk) 1098 .CopyIntoTraceBuffer(); 1099 CreateChunk(ProducerID(1), WriterID(1), ChunkID(1)) 1100 .AddPacket(4, 'b', kContFromPrevChunk | kContOnNextChunk) 1101 .ClearBytes(0, 1) 1102 .CopyIntoTraceBuffer(); 1103 CreateChunk(ProducerID(1), WriterID(1), ChunkID(2)) 1104 .AddPacket(4, 'c', kContFromPrevChunk) 1105 .AddPacket(4, 'd') 1106 .CopyIntoTraceBuffer(); 1107 CreateChunk(ProducerID(1), WriterID(1), ChunkID(3)) 1108 .AddPacket(4, 'e') 1109 .CopyIntoTraceBuffer(); 1110 CreateChunk(ProducerID(2), WriterID(1), ChunkID(3)) 1111 .AddPacket(5, 'f') 1112 .CopyIntoTraceBuffer(); 1113 1114 trace_buffer()->BeginRead(); 1115 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(4, 'a'), 1116 FakePacketFragment(4, 'c'))); 1117 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(4, 'd'))); 1118 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(4, 'e'))); 1119 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(5, 'f'))); 1120 ASSERT_THAT(ReadPacket(), IsEmpty()); 1121 } 1122 1123 // Similar to Malicious_ZeroVarintHeaderInSequence, but this time the zero-sized 1124 // fragment is the last fragment for a chunk and is marked for continuation. The 1125 // zero-sized fragment should be skipped. 1126 TEST_F(TraceBufferTest, Malicious_ZeroVarintHeaderAtEndOfChunk) { 1127 ResetBuffer(4096); 1128 SuppressSanityDchecksForTesting(); 1129 CreateChunk(ProducerID(1), WriterID(1), ChunkID(0)) 1130 .AddPacket(4, 'a') 1131 .AddPacket(4, 'b', kContOnNextChunk) 1132 .ClearBytes(4, 4) 1133 .CopyIntoTraceBuffer(); 1134 CreateChunk(ProducerID(1), WriterID(1), ChunkID(1)) 1135 .AddPacket(4, 'c', kContFromPrevChunk) 1136 .AddPacket(4, 'd') 1137 .CopyIntoTraceBuffer(); 1138 CreateChunk(ProducerID(1), WriterID(1), ChunkID(2)) 1139 .AddPacket(4, 'e') 1140 .CopyIntoTraceBuffer(); 1141 CreateChunk(ProducerID(2), WriterID(1), ChunkID(3)) 1142 .AddPacket(4, 'f') 1143 .CopyIntoTraceBuffer(); 1144 1145 trace_buffer()->BeginRead(); 1146 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(4, 'a'))); 1147 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(4, 'c'))); 1148 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(4, 'd'))); 1149 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(4, 'e'))); 1150 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(4, 'f'))); 1151 ASSERT_THAT(ReadPacket(), IsEmpty()); 1152 } 1153 1154 TEST_F(TraceBufferTest, Malicious_PatchOutOfBounds) { 1155 ResetBuffer(4096); 1156 CreateChunk(ProducerID(1), WriterID(1), ChunkID(0)) 1157 .AddPacket(2048, 'a') 1158 .CopyIntoTraceBuffer(); 1159 CreateChunk(ProducerID(1), WriterID(1), ChunkID(1)) 1160 .AddPacket(16, 'b') 1161 .CopyIntoTraceBuffer(); 1162 size_t offsets[] = {13, 16, size_t(-4), 1163 size_t(-8), size_t(-12), size_t(-16), 1164 size_t(-20), size_t(-32), size_t(-1024)}; 1165 for (size_t offset : offsets) { 1166 ASSERT_FALSE(TryPatchChunkContents(ProducerID(1), WriterID(1), ChunkID(1), 1167 {{offset, {{'0', 'd', 'a', 'y'}}}})); 1168 } 1169 } 1170 1171 TEST_F(TraceBufferTest, Malicious_OverrideWithShorterChunkSize) { 1172 ResetBuffer(4096); 1173 SuppressSanityDchecksForTesting(); 1174 CreateChunk(ProducerID(1), WriterID(1), ChunkID(0)) 1175 .AddPacket(2048, 'a') 1176 .CopyIntoTraceBuffer(); 1177 // The service should ignore this override of the chunk since the chunk size 1178 // is different. 1179 CreateChunk(ProducerID(1), WriterID(1), ChunkID(0)) 1180 .AddPacket(1024, 'b') 1181 .CopyIntoTraceBuffer(); 1182 trace_buffer()->BeginRead(); 1183 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(2048, 'a'))); 1184 ASSERT_THAT(ReadPacket(), IsEmpty()); 1185 } 1186 1187 TEST_F(TraceBufferTest, Malicious_OverrideWithShorterChunkSizeAfterRead) { 1188 ResetBuffer(4096); 1189 SuppressSanityDchecksForTesting(); 1190 1191 CreateChunk(ProducerID(1), WriterID(1), ChunkID(0)) 1192 .AddPacket(30, 'a') 1193 .AddPacket(40, 'b') 1194 .CopyIntoTraceBuffer(); 1195 trace_buffer()->BeginRead(); 1196 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(30, 'a'))); 1197 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(40, 'b'))); 1198 1199 // The service should ignore this override of the chunk since the chunk size 1200 // is different. 1201 CreateChunk(ProducerID(1), WriterID(1), ChunkID(0)) 1202 .AddPacket(10, 'a') 1203 .AddPacket(10, 'b') 1204 .AddPacket(10, 'c') 1205 .CopyIntoTraceBuffer(); 1206 trace_buffer()->BeginRead(); 1207 ASSERT_THAT(ReadPacket(), IsEmpty()); 1208 1209 // Test that the service didn't get stuck in some indeterminate state. 1210 // Writing a valid chunk with a larger ID should make things work again. 1211 CreateChunk(ProducerID(1), WriterID(1), ChunkID(1)) 1212 .AddPacket(10, 'd') 1213 .AddPacket(10, 'e') 1214 .CopyIntoTraceBuffer(); 1215 trace_buffer()->BeginRead(); 1216 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(10, 'd'))); 1217 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(10, 'e'))); 1218 ASSERT_THAT(ReadPacket(), IsEmpty()); 1219 } 1220 1221 TEST_F(TraceBufferTest, Malicious_OverrideWithDifferentOffsetAfterRead) { 1222 ResetBuffer(4096); 1223 SuppressSanityDchecksForTesting(); 1224 1225 CreateChunk(ProducerID(1), WriterID(1), ChunkID(0)) 1226 .AddPacket(30, 'a') 1227 .AddPacket(40, 'b') 1228 .PadTo(512) 1229 .CopyIntoTraceBuffer(); 1230 trace_buffer()->BeginRead(); 1231 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(30, 'a'))); 1232 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(40, 'b'))); 1233 1234 // The attacker in this case speculates on the fact that the read pointer is 1235 // @ 70 which is >> the size of the new chunk we overwrite. 1236 // The service will not discard this override since the chunk size is correct. 1237 // However, it should detect that the packet headers at the current read 1238 // offset are invalid and skip the read of this chunk. 1239 CreateChunk(ProducerID(1), WriterID(1), ChunkID(0)) 1240 .AddPacket(10, 'a') 1241 .AddPacket(10, 'b') 1242 .AddPacket(10, 'c') 1243 .PadTo(512) 1244 .CopyIntoTraceBuffer(); 1245 trace_buffer()->BeginRead(); 1246 ASSERT_THAT(ReadPacket(), IsEmpty()); 1247 1248 // Test that the service didn't get stuck in some indeterminate state. 1249 // Writing a valid chunk with a larger ID should make things work again. 1250 CreateChunk(ProducerID(1), WriterID(1), ChunkID(1)) 1251 .AddPacket(10, 'd') 1252 .AddPacket(10, 'e') 1253 .PadTo(512) 1254 .CopyIntoTraceBuffer(); 1255 trace_buffer()->BeginRead(); 1256 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(10, 'd'))); 1257 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(10, 'e'))); 1258 ASSERT_THAT(ReadPacket(), IsEmpty()); 1259 } 1260 1261 // ------------------- 1262 // SequenceIterator tests 1263 // ------------------- 1264 TEST_F(TraceBufferTest, Iterator_OneStreamOrdered) { 1265 ResetBuffer(64 * 1024); 1266 AppendChunks({ 1267 {ProducerID(1), WriterID(1), ChunkID(0)}, 1268 {ProducerID(1), WriterID(1), ChunkID(1)}, 1269 {ProducerID(1), WriterID(1), ChunkID(2)}, 1270 {ProducerID(1), WriterID(1), ChunkID(5)}, 1271 {ProducerID(1), WriterID(1), ChunkID(6)}, 1272 {ProducerID(1), WriterID(1), ChunkID(7)}, 1273 }); 1274 ASSERT_TRUE(IteratorSeqEq(ProducerID(1), WriterID(2), {})); 1275 ASSERT_TRUE(IteratorSeqEq(ProducerID(-1), WriterID(-1), {})); 1276 ASSERT_TRUE(IteratorSeqEq(ProducerID(1), WriterID(1), {0, 1, 2})); 1277 } 1278 1279 TEST_F(TraceBufferTest, Iterator_OneStreamWrapping) { 1280 ResetBuffer(64 * 1024); 1281 AppendChunks({ 1282 {ProducerID(1), WriterID(1), ChunkID(kMaxChunkID - 2)}, 1283 {ProducerID(1), WriterID(1), ChunkID(kMaxChunkID - 1)}, 1284 {ProducerID(1), WriterID(1), ChunkID(kMaxChunkID)}, 1285 {ProducerID(1), WriterID(1), ChunkID(0)}, 1286 {ProducerID(1), WriterID(1), ChunkID(1)}, 1287 {ProducerID(1), WriterID(1), ChunkID(2)}, 1288 }); 1289 ASSERT_TRUE(IteratorSeqEq(ProducerID(1), WriterID(2), {})); 1290 ASSERT_TRUE(IteratorSeqEq(ProducerID(-1), WriterID(-1), {})); 1291 ASSERT_TRUE( 1292 IteratorSeqEq(ProducerID(1), WriterID(1), 1293 {kMaxChunkID - 2, kMaxChunkID - 1, kMaxChunkID, 0, 1, 2})); 1294 } 1295 1296 TEST_F(TraceBufferTest, Iterator_ManyStreamsOrdered) { 1297 ResetBuffer(64 * 1024); 1298 AppendChunks({ 1299 {ProducerID(1), WriterID(1), ChunkID(0)}, 1300 {ProducerID(1), WriterID(1), ChunkID(1)}, 1301 {ProducerID(1), WriterID(2), ChunkID(0)}, 1302 {ProducerID(3), WriterID(1), ChunkID(0)}, 1303 {ProducerID(1), WriterID(2), ChunkID(1)}, 1304 {ProducerID(1), WriterID(2), ChunkID(2)}, 1305 {ProducerID(3), WriterID(1), ChunkID(1)}, 1306 {ProducerID(1), WriterID(1), ChunkID(2)}, 1307 {ProducerID(3), WriterID(1), ChunkID(2)}, 1308 }); 1309 ASSERT_TRUE(IteratorSeqEq(ProducerID(1), WriterID(1), {0, 1, 2})); 1310 ASSERT_TRUE(IteratorSeqEq(ProducerID(1), WriterID(2), {0, 1, 2})); 1311 ASSERT_TRUE(IteratorSeqEq(ProducerID(3), WriterID(1), {0, 1, 2})); 1312 } 1313 1314 TEST_F(TraceBufferTest, Iterator_ManyStreamsWrapping) { 1315 ResetBuffer(64 * 1024); 1316 auto Neg = [](int x) -> ChunkID { 1317 return kMaxChunkID + static_cast<ChunkID>(x) + 1; 1318 }; 1319 AppendChunks({ 1320 {ProducerID(1), WriterID(1), ChunkID(Neg(-2))}, 1321 {ProducerID(1), WriterID(1), ChunkID(Neg(-1))}, 1322 {ProducerID(1), WriterID(2), ChunkID(Neg(-1))}, 1323 {ProducerID(3), WriterID(1), ChunkID(Neg(-1))}, 1324 {ProducerID(1), WriterID(2), ChunkID(0)}, 1325 {ProducerID(1), WriterID(2), ChunkID(1)}, 1326 {ProducerID(3), WriterID(1), ChunkID(0)}, 1327 {ProducerID(1), WriterID(1), ChunkID(0)}, 1328 {ProducerID(3), WriterID(1), ChunkID(1)}, 1329 }); 1330 ASSERT_TRUE(IteratorSeqEq(ProducerID(1), WriterID(1), {Neg(-2), Neg(-1), 0})); 1331 ASSERT_TRUE(IteratorSeqEq(ProducerID(1), WriterID(2), {Neg(-1), 0, 1})); 1332 ASSERT_TRUE(IteratorSeqEq(ProducerID(3), WriterID(1), {Neg(-1), 0, 1})); 1333 } 1334 1335 // ------------------- 1336 // Re-writing same chunk id 1337 // ------------------- 1338 1339 TEST_F(TraceBufferTest, Override_ReCommitBeforeRead) { 1340 ResetBuffer(4096); 1341 CreateChunk(ProducerID(1), WriterID(1), ChunkID(0)) 1342 .AddPacket(100, 'a') 1343 .AddPacket(100, 'b') 1344 .PadTo(512) 1345 .CopyIntoTraceBuffer(/*chunk_complete=*/false); 1346 EXPECT_EQ(0u, trace_buffer()->stats().chunks_rewritten()); 1347 CreateChunk(ProducerID(1), WriterID(1), ChunkID(0)) 1348 .AddPacket(100, 'a') 1349 .AddPacket(100, 'b') 1350 .AddPacket(100, 'c') 1351 .AddPacket(100, 'd') 1352 .PadTo(512) 1353 .CopyIntoTraceBuffer(); 1354 trace_buffer()->BeginRead(); 1355 EXPECT_EQ(1u, trace_buffer()->stats().chunks_rewritten()); 1356 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(100, 'a'))); 1357 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(100, 'b'))); 1358 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(100, 'c'))); 1359 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(100, 'd'))); 1360 ASSERT_THAT(ReadPacket(), IsEmpty()); 1361 } 1362 1363 TEST_F(TraceBufferTest, Override_ReCommitAfterPartialRead) { 1364 ResetBuffer(4096); 1365 CreateChunk(ProducerID(1), WriterID(1), ChunkID(0)) 1366 .AddPacket(20, 'a') 1367 .AddPacket(30, 'b') 1368 .PadTo(512) 1369 .CopyIntoTraceBuffer(/*chunk_complete=*/false); 1370 trace_buffer()->BeginRead(); 1371 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(20, 'a'))); 1372 1373 CreateChunk(ProducerID(1), WriterID(1), ChunkID(0)) 1374 .AddPacket(20, 'a') 1375 .AddPacket(30, 'b') 1376 .AddPacket(40, 'c') 1377 .AddPacket(50, 'd') 1378 .PadTo(512) 1379 .CopyIntoTraceBuffer(); 1380 trace_buffer()->BeginRead(); 1381 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(30, 'b'))); 1382 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(40, 'c'))); 1383 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(50, 'd'))); 1384 ASSERT_THAT(ReadPacket(), IsEmpty()); 1385 } 1386 1387 TEST_F(TraceBufferTest, Override_ReCommitAfterFullRead) { 1388 ResetBuffer(4096); 1389 CreateChunk(ProducerID(1), WriterID(1), ChunkID(0)) 1390 .AddPacket(20, 'a') 1391 .AddPacket(30, 'b') 1392 .PadTo(512) 1393 .CopyIntoTraceBuffer(); 1394 trace_buffer()->BeginRead(); 1395 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(20, 'a'))); 1396 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(30, 'b'))); 1397 1398 // Overriding a complete packet here would trigger a DCHECK because the packet 1399 // was already marked as complete. 1400 SuppressSanityDchecksForTesting(); 1401 CreateChunk(ProducerID(1), WriterID(1), ChunkID(0)) 1402 .AddPacket(20, 'a') 1403 .AddPacket(30, 'b') 1404 .AddPacket(40, 'c') 1405 .AddPacket(50, 'd') 1406 .PadTo(512) 1407 .CopyIntoTraceBuffer(); 1408 trace_buffer()->BeginRead(); 1409 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(40, 'c'))); 1410 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(50, 'd'))); 1411 ASSERT_THAT(ReadPacket(), IsEmpty()); 1412 } 1413 1414 // See also the Malicious_Override* tests above. 1415 TEST_F(TraceBufferTest, Override_ReCommitInvalid) { 1416 ResetBuffer(4096); 1417 SuppressSanityDchecksForTesting(); 1418 CreateChunk(ProducerID(1), WriterID(1), ChunkID(0)) 1419 .AddPacket(20, 'a') 1420 .AddPacket(30, 'b') 1421 .PadTo(512) 1422 .CopyIntoTraceBuffer(); 1423 CreateChunk(ProducerID(1), WriterID(1), ChunkID(1)) 1424 .AddPacket(40, 'c') 1425 .AddPacket(50, 'd') 1426 .PadTo(512) 1427 .CopyIntoTraceBuffer(); 1428 trace_buffer()->BeginRead(); 1429 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(20, 'a'))); 1430 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(30, 'b'))); 1431 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(40, 'c'))); 1432 1433 // This should not happen when the producer behaves correctly, since it 1434 // shouldn't change the contents of chunk 0 after having allocated chunk 1. 1435 // 1436 // Since we've already started reading from chunk 1, TraceBuffer will 1437 // recognize this and discard the override. 1438 CreateChunk(ProducerID(1), WriterID(1), ChunkID(0)) 1439 .AddPacket(20, 'e') 1440 .AddPacket(60, 'f') 1441 .AddPacket(70, 'g') 1442 .PadTo(512) 1443 .CopyIntoTraceBuffer(); 1444 trace_buffer()->BeginRead(); 1445 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(50, 'd'))); 1446 ASSERT_THAT(ReadPacket(), IsEmpty()); 1447 } 1448 1449 TEST_F(TraceBufferTest, Override_ReCommitReordered) { 1450 ResetBuffer(4096); 1451 CreateChunk(ProducerID(1), WriterID(1), ChunkID(0)) 1452 .AddPacket(20, 'a') 1453 .AddPacket(30, 'b') 1454 .PadTo(512) 1455 .CopyIntoTraceBuffer(/*chunk_complete=*/false); 1456 1457 trace_buffer()->BeginRead(); 1458 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(20, 'a'))); 1459 1460 // Recommit chunk 0 and add chunk 1, but do this out of order. 1461 CreateChunk(ProducerID(1), WriterID(1), ChunkID(1)) 1462 .AddPacket(50, 'd') 1463 .AddPacket(60, 'e') 1464 .PadTo(512) 1465 .CopyIntoTraceBuffer(); 1466 CreateChunk(ProducerID(1), WriterID(1), ChunkID(0)) 1467 .AddPacket(20, 'a') 1468 .AddPacket(30, 'b') 1469 .AddPacket(40, 'c') 1470 .PadTo(512) 1471 .CopyIntoTraceBuffer(); 1472 1473 trace_buffer()->BeginRead(); 1474 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(30, 'b'))); 1475 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(40, 'c'))); 1476 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(50, 'd'))); 1477 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(60, 'e'))); 1478 } 1479 1480 TEST_F(TraceBufferTest, Override_ReCommitReorderedFragmenting) { 1481 ResetBuffer(4096); 1482 CreateChunk(ProducerID(1), WriterID(1), ChunkID(0)) 1483 .AddPacket(20, 'a') 1484 .AddPacket(30, 'b') 1485 .PadTo(512) 1486 .CopyIntoTraceBuffer(/*chunk_complete=*/false); 1487 1488 trace_buffer()->BeginRead(); 1489 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(20, 'a'))); 1490 1491 // Recommit chunk 0 and add chunk 1, but do this out of order. 1492 CreateChunk(ProducerID(1), WriterID(1), ChunkID(1)) 1493 .AddPacket(50, 'd', kContFromPrevChunk) 1494 .AddPacket(60, 'e') 1495 .PadTo(512) 1496 .CopyIntoTraceBuffer(); 1497 CreateChunk(ProducerID(1), WriterID(1), ChunkID(0)) 1498 .AddPacket(20, 'a') 1499 .AddPacket(30, 'b') 1500 .AddPacket(40, 'c', kContOnNextChunk) 1501 .PadTo(512) 1502 .CopyIntoTraceBuffer(); 1503 1504 trace_buffer()->BeginRead(); 1505 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(30, 'b'))); 1506 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(40, 'c'), 1507 FakePacketFragment(50, 'd'))); 1508 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(60, 'e'))); 1509 } 1510 1511 TEST_F(TraceBufferTest, Override_ReCommitSameBeforeRead) { 1512 ResetBuffer(4096); 1513 CreateChunk(ProducerID(1), WriterID(1), ChunkID(0)) 1514 .AddPacket(20, 'a') 1515 .AddPacket(30, 'b') 1516 .PadTo(512) 1517 .CopyIntoTraceBuffer(); 1518 1519 // Commit again the same chunk. 1520 CreateChunk(ProducerID(1), WriterID(1), ChunkID(0)) 1521 .AddPacket(20, 'a') 1522 .AddPacket(30, 'b') 1523 .PadTo(512) 1524 .CopyIntoTraceBuffer(); 1525 1526 // Then write some new content in a new chunk. 1527 CreateChunk(ProducerID(1), WriterID(1), ChunkID(1)) 1528 .AddPacket(40, 'c') 1529 .AddPacket(50, 'd') 1530 .PadTo(512) 1531 .CopyIntoTraceBuffer(); 1532 1533 // The reader should keep reading from the new chunk. 1534 trace_buffer()->BeginRead(); 1535 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(20, 'a'))); 1536 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(30, 'b'))); 1537 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(40, 'c'))); 1538 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(50, 'd'))); 1539 ASSERT_THAT(ReadPacket(), IsEmpty()); 1540 } 1541 1542 TEST_F(TraceBufferTest, Override_ReCommitSameAfterRead) { 1543 ResetBuffer(4096); 1544 CreateChunk(ProducerID(1), WriterID(1), ChunkID(0)) 1545 .AddPacket(20, 'a') 1546 .AddPacket(30, 'b') 1547 .PadTo(512) 1548 .CopyIntoTraceBuffer(); 1549 trace_buffer()->BeginRead(); 1550 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(20, 'a'))); 1551 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(30, 'b'))); 1552 1553 // This re-commit should be ignored. We just re-committed an identical chunk. 1554 CreateChunk(ProducerID(1), WriterID(1), ChunkID(0)) 1555 .AddPacket(20, 'a') 1556 .AddPacket(30, 'b') 1557 .PadTo(512) 1558 .CopyIntoTraceBuffer(); 1559 1560 // Then write some new content in a new chunk. 1561 CreateChunk(ProducerID(1), WriterID(1), ChunkID(1)) 1562 .AddPacket(40, 'c') 1563 .AddPacket(50, 'd') 1564 .PadTo(512) 1565 .CopyIntoTraceBuffer(); 1566 1567 // The reader should keep reading from the new chunk. 1568 trace_buffer()->BeginRead(); 1569 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(40, 'c'))); 1570 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(50, 'd'))); 1571 ASSERT_THAT(ReadPacket(), IsEmpty()); 1572 } 1573 1574 TEST_F(TraceBufferTest, Override_ReCommitIncompleteAfterReadOutOfOrder) { 1575 ResetBuffer(4096); 1576 CreateChunk(ProducerID(1), WriterID(1), ChunkID(0)) 1577 .AddPacket(20, 'a') 1578 .AddPacket(30, 'b') 1579 .PadTo(512) 1580 .CopyIntoTraceBuffer(/*chunk_complete=*/false); 1581 trace_buffer()->BeginRead(); 1582 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(20, 'a'))); 1583 // The last packet in an incomplete chunk should be ignored as the producer 1584 // may not have completed writing it. 1585 ASSERT_THAT(ReadPacket(), IsEmpty()); 1586 1587 // Then write some new content in a new chunk. 1588 CreateChunk(ProducerID(1), WriterID(1), ChunkID(1)) 1589 .AddPacket(40, 'c') 1590 .AddPacket(50, 'd') 1591 .PadTo(512) 1592 .CopyIntoTraceBuffer(); 1593 // The read still shouldn't be advancing past the incomplete chunk. 1594 trace_buffer()->BeginRead(); 1595 ASSERT_THAT(ReadPacket(), IsEmpty()); 1596 1597 // Recommit the original chunk with no changes but mark as complete. 1598 CreateChunk(ProducerID(1), WriterID(1), ChunkID(0)) 1599 .AddPacket(20, 'a') 1600 .AddPacket(30, 'b') 1601 .PadTo(512) 1602 .CopyIntoTraceBuffer(/*chunk_complete=*/true); 1603 1604 // Reading should resume from the now completed chunk. 1605 trace_buffer()->BeginRead(); 1606 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(30, 'b'))); 1607 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(40, 'c'))); 1608 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(50, 'd'))); 1609 ASSERT_THAT(ReadPacket(), IsEmpty()); 1610 } 1611 1612 TEST_F(TraceBufferTest, Override_ReCommitIncompleteFragmenting) { 1613 ResetBuffer(4096); 1614 CreateChunk(ProducerID(1), WriterID(1), ChunkID(0)) 1615 .AddPacket(20, 'a') 1616 .AddPacket(30, 'b', kContOnNextChunk) 1617 .PadTo(512) 1618 .CopyIntoTraceBuffer(/*chunk_complete=*/false); 1619 trace_buffer()->BeginRead(); 1620 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(20, 'a'))); 1621 // The last packet in an incomplete chunk should be ignored as the producer 1622 // may not have completed writing it. 1623 ASSERT_THAT(ReadPacket(), IsEmpty()); 1624 1625 // Then write some new content in a new chunk. 1626 CreateChunk(ProducerID(1), WriterID(1), ChunkID(1)) 1627 .AddPacket(40, 'c', kContFromPrevChunk) 1628 .AddPacket(50, 'd') 1629 .PadTo(512) 1630 .CopyIntoTraceBuffer(); 1631 // The read still shouldn't be advancing past the incomplete chunk. 1632 trace_buffer()->BeginRead(); 1633 ASSERT_THAT(ReadPacket(), IsEmpty()); 1634 1635 // Recommit the original chunk with no changes but mark as complete. 1636 CreateChunk(ProducerID(1), WriterID(1), ChunkID(0)) 1637 .AddPacket(20, 'a') 1638 .AddPacket(30, 'b', kContOnNextChunk) 1639 .PadTo(512) 1640 .CopyIntoTraceBuffer(/*chunk_complete=*/true); 1641 1642 // Reading should resume from the now completed chunk. 1643 trace_buffer()->BeginRead(); 1644 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(30, 'b'), 1645 FakePacketFragment(40, 'c'))); 1646 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(50, 'd'))); 1647 ASSERT_THAT(ReadPacket(), IsEmpty()); 1648 } 1649 1650 TEST_F(TraceBufferTest, DiscardPolicy) { 1651 ResetBuffer(4096, TraceBuffer::kDiscard); 1652 1653 CreateChunk(ProducerID(1), WriterID(1), ChunkID(0)) 1654 .AddPacket(96 - 16, 'a') 1655 .CopyIntoTraceBuffer(); 1656 CreateChunk(ProducerID(1), WriterID(1), ChunkID(1)) 1657 .AddPacket(4000 - 16, 'b') 1658 .CopyIntoTraceBuffer(); 1659 1660 trace_buffer()->BeginRead(); 1661 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(96 - 16, 'a'))); 1662 1663 // As long as the reader catches up, writes should succeed. 1664 CreateChunk(ProducerID(1), WriterID(1), ChunkID(2)) 1665 .AddPacket(48 - 16, 'c') 1666 .CopyIntoTraceBuffer(); 1667 CreateChunk(ProducerID(1), WriterID(1), ChunkID(3)) 1668 .AddPacket(48 - 16, 'd') 1669 .CopyIntoTraceBuffer(); 1670 1671 trace_buffer()->BeginRead(); 1672 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(4000 - 16, 'b'))); 1673 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(48 - 16, 'c'))); 1674 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(48 - 16, 'd'))); 1675 ASSERT_THAT(ReadPacket(), IsEmpty()); 1676 1677 // This will succeed. 1678 CreateChunk(ProducerID(1), WriterID(1), ChunkID(4)) 1679 .AddPacket(4000 - 16, 'e') 1680 .CopyIntoTraceBuffer(); 1681 1682 // But this will fail, preventing any further write. 1683 for (int i = 0; i < 3; i++) { 1684 CreateChunk(ProducerID(1), WriterID(i + 2), ChunkID(0)) 1685 .AddPacket(120 - 16, 'X') 1686 .CopyIntoTraceBuffer(); 1687 } 1688 1689 trace_buffer()->BeginRead(); 1690 ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(4000 - 16, 'e'))); 1691 ASSERT_THAT(ReadPacket(), IsEmpty()); 1692 1693 // Even after the reader catches up, writes should still be discarded. 1694 for (int i = 0; i < 3; i++) { 1695 CreateChunk(ProducerID(1), WriterID(i + 10), ChunkID(0)) 1696 .AddPacket(64 - 16, 'X') 1697 .CopyIntoTraceBuffer(); 1698 } 1699 trace_buffer()->BeginRead(); 1700 ASSERT_THAT(ReadPacket(), IsEmpty()); 1701 } 1702 1703 TEST_F(TraceBufferTest, MissingPacketsOnSequence) { 1704 ResetBuffer(4096); 1705 SuppressSanityDchecksForTesting(); 1706 CreateChunk(ProducerID(1), WriterID(1), ChunkID(0)) 1707 .AddPacket(10, 'a') 1708 .AddPacket(10, 'b') 1709 .AddPacket(10, 'c', kContOnNextChunk) 1710 .CopyIntoTraceBuffer(); 1711 CreateChunk(ProducerID(2), WriterID(1), ChunkID(0)) 1712 .AddPacket(10, 'u') 1713 .AddPacket(10, 'v') 1714 .AddPacket(10, 'w') 1715 .ClearBytes(10, 1) // Clears the varint header of packet "v". 1716 .CopyIntoTraceBuffer(); 1717 1718 bool previous_packet_dropped = false; 1719 1720 trace_buffer()->BeginRead(); 1721 ASSERT_THAT(ReadPacket(nullptr, &previous_packet_dropped), 1722 ElementsAre(FakePacketFragment(10, 'a'))); 1723 // First packet in first sequence, so previous one didn't exist. 1724 ASSERT_TRUE(previous_packet_dropped); 1725 1726 ASSERT_THAT(ReadPacket(nullptr, &previous_packet_dropped), 1727 ElementsAre(FakePacketFragment(10, 'b'))); 1728 // We read packet "a" before. 1729 ASSERT_FALSE(previous_packet_dropped); 1730 1731 ASSERT_THAT(ReadPacket(nullptr, &previous_packet_dropped), 1732 ElementsAre(FakePacketFragment(10, 'u'))); 1733 // First packet in second sequence, so previous one didn't exist. 1734 ASSERT_TRUE(previous_packet_dropped); 1735 1736 // Packet "v" in second sequence is corrupted, so chunk will be skipped. 1737 ASSERT_THAT(ReadPacket(), IsEmpty()); 1738 1739 CreateChunk(ProducerID(2), WriterID(1), ChunkID(1)) 1740 .AddPacket(10, 'x') 1741 .AddPacket(10, 'y') 1742 .CopyIntoTraceBuffer(); 1743 CreateChunk(ProducerID(1), WriterID(1), ChunkID(1)) 1744 .AddPacket(10, 'd', kContFromPrevChunk) 1745 .AddPacket(10, 'e') 1746 .CopyIntoTraceBuffer(); 1747 1748 trace_buffer()->BeginRead(); 1749 ASSERT_THAT( 1750 ReadPacket(nullptr, &previous_packet_dropped), 1751 ElementsAre(FakePacketFragment(10, 'c'), FakePacketFragment(10, 'd'))); 1752 // We read packet "b" before. 1753 ASSERT_FALSE(previous_packet_dropped); 1754 1755 ASSERT_THAT(ReadPacket(nullptr, &previous_packet_dropped), 1756 ElementsAre(FakePacketFragment(10, 'e'))); 1757 // We read packet "d" before. 1758 ASSERT_FALSE(previous_packet_dropped); 1759 1760 ASSERT_THAT(ReadPacket(nullptr, &previous_packet_dropped), 1761 ElementsAre(FakePacketFragment(10, 'x'))); 1762 // We didn't read packets "v" and "w". 1763 ASSERT_TRUE(previous_packet_dropped); 1764 1765 ASSERT_THAT(ReadPacket(nullptr, &previous_packet_dropped), 1766 ElementsAre(FakePacketFragment(10, 'y'))); 1767 // We read packet "x". 1768 ASSERT_FALSE(previous_packet_dropped); 1769 1770 ASSERT_THAT(ReadPacket(), IsEmpty()); 1771 1772 // Write two large chunks that don't fit into the buffer at the same time. We 1773 // will drop the former one before we can read it. 1774 CreateChunk(ProducerID(1), WriterID(1), ChunkID(2)) 1775 .AddPacket(2000, 'f') 1776 .CopyIntoTraceBuffer(); 1777 CreateChunk(ProducerID(1), WriterID(1), ChunkID(3)) 1778 .AddPacket(3000, 'g') 1779 .CopyIntoTraceBuffer(); 1780 1781 trace_buffer()->BeginRead(); 1782 ASSERT_THAT(ReadPacket(nullptr, &previous_packet_dropped), 1783 ElementsAre(FakePacketFragment(3000, 'g'))); 1784 // We didn't read packet "f". 1785 ASSERT_TRUE(previous_packet_dropped); 1786 1787 CreateChunk(ProducerID(2), WriterID(1), ChunkID(2)) 1788 .AddPacket(10, 'z') 1789 .CopyIntoTraceBuffer(); 1790 1791 trace_buffer()->BeginRead(); 1792 ASSERT_THAT(ReadPacket(nullptr, &previous_packet_dropped), 1793 ElementsAre(FakePacketFragment(10, 'z'))); 1794 // We've lost any state from the second producer's sequence because all its 1795 // previous chunks were removed from the buffer due to the two large chunks. 1796 // So the buffer can't be sure that no packets were dropped. 1797 ASSERT_TRUE(previous_packet_dropped); 1798 } 1799 1800 // TODO(primiano): test stats(). 1801 // TODO(primiano): test multiple streams interleaved. 1802 // TODO(primiano): more testing on packet merging. 1803 1804 } // namespace perfetto 1805