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