Skip to content

Commit b60a5bf

Browse files
committed
tbV2: On rescraping don't remove kChunkIncomplete
Change-Id: Id51e6ebdbf06b6173e49d00a1d05b4cb3ce42c95
1 parent fdd88c0 commit b60a5bf

2 files changed

Lines changed: 76 additions & 1 deletion

File tree

src/tracing/service/trace_buffer_v2.cc

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -810,7 +810,11 @@ void TraceBufferV2::CopyChunkUntrusted(
810810
PERFETTO_DCHECK(suppress_client_dchecks_for_testing_);
811811
return;
812812
}
813-
recommit_chunk->flags &= ~kChunkIncomplete;
813+
// Only clear kChunkIncomplete on real IPC recommits. A scrape recommit
814+
// with the same payload would otherwise hit the early-return below before
815+
// chunk_flags (which has kChunkIncomplete) gets OR'd back in.
816+
if (chunk_complete)
817+
recommit_chunk->flags &= ~kChunkIncomplete;
814818
if (all_frags_size == recommit_chunk->payload_size) {
815819
TRACE_BUFFER_V2_DLOG(" skipping recommit of identical chunk");
816820
return;

src/tracing/service/trace_buffer_v2_unittest.cc

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2447,4 +2447,75 @@ TEST_F(TraceBufferV2Test, RescrapeAfterEviction_PartiallyRead) {
24472447
EXPECT_FALSE(found_a) << "Packet 'a' was re-read after eviction+rescrape";
24482448
}
24492449

2450+
// Regression test for the scrape-recommit-read interaction. When a chunk is
2451+
// scraped (chunk_complete=false), it gets kChunkIncomplete. If the same chunk
2452+
// is scraped again with the same payload, the recommit path must NOT clear
2453+
// kChunkIncomplete. Otherwise the reader would consume it prematurely, and when
2454+
// the real IPC commit arrives it's discarded as "late", orphaning the kFragEnd
2455+
// in the next chunk.
2456+
TEST_F(TraceBufferV2Test, ScrapeRecommitPreservesIncomplete) {
2457+
ResetBuffer(4096);
2458+
SuppressClientDchecksForTesting();
2459+
2460+
// Scrape chunk 0: [Whole 'a'] [kFragBegin 'b' kContOnNextChunk].
2461+
// Scraping drops the last fragment ('b'), clears kContOnNextChunk, sets
2462+
// kChunkIncomplete. Only fragment 'a' is visible.
2463+
CreateChunk(ProducerID(1), WriterID(1), ChunkID(0))
2464+
.AddPacket(20, 'a')
2465+
.AddPacket(10, 'b', kContOnNextChunk)
2466+
.PadTo(512)
2467+
.CopyIntoTraceBuffer(/*chunk_complete=*/false);
2468+
2469+
// Read cycle 1: gets 'a'. The chunk's payload is fully consumed but it is
2470+
// NOT erased because kChunkIncomplete prevents it.
2471+
trace_buffer()->BeginRead();
2472+
ASSERT_THAT(ReadPacket(), ElementsAre(FakePacketFragment(20, 'a')));
2473+
ASSERT_THAT(ReadPacket(), IsEmpty());
2474+
2475+
// Second scrape of the same chunk (same payload). This triggers the recommit
2476+
// path. The fix ensures kChunkIncomplete is NOT cleared here.
2477+
CreateChunk(ProducerID(1), WriterID(1), ChunkID(0))
2478+
.AddPacket(20, 'a')
2479+
.AddPacket(10, 'b', kContOnNextChunk)
2480+
.PadTo(512)
2481+
.CopyIntoTraceBuffer(/*chunk_complete=*/false);
2482+
2483+
// Read cycle 2: chunk still has kChunkIncomplete, so nothing new to read.
2484+
trace_buffer()->BeginRead();
2485+
ASSERT_THAT(ReadPacket(), IsEmpty());
2486+
2487+
// The chunk must NOT have been consumed (no chunks_discarded bump yet).
2488+
EXPECT_EQ(0u, trace_buffer()->stats().chunks_discarded());
2489+
2490+
// IPC recommit: producer finished writing, commits as complete. This clears
2491+
// kChunkIncomplete and restores the full payload including fragment 'b'.
2492+
CreateChunk(ProducerID(1), WriterID(1), ChunkID(0))
2493+
.AddPacket(20, 'a')
2494+
.AddPacket(10, 'b', kContOnNextChunk)
2495+
.PadTo(512)
2496+
.CopyIntoTraceBuffer(/*chunk_complete=*/true);
2497+
2498+
// Chunk 1: continuation of 'b' plus a whole packet 'd'.
2499+
CreateChunk(ProducerID(1), WriterID(1), ChunkID(1))
2500+
.AddPacket(10, 'c', kContFromPrevChunk)
2501+
.AddPacket(20, 'd')
2502+
.CopyIntoTraceBuffer();
2503+
2504+
// Read cycle 3: 'a' was already consumed. We should get the reassembled
2505+
// fragmented packet ['b','c'] and then 'd'. No data loss.
2506+
trace_buffer()->BeginRead();
2507+
bool previous_packet_dropped = false;
2508+
2509+
ASSERT_THAT(
2510+
ReadPacket(nullptr, &previous_packet_dropped),
2511+
ElementsAre(FakePacketFragment(10, 'b'), FakePacketFragment(10, 'c')));
2512+
EXPECT_FALSE(previous_packet_dropped);
2513+
2514+
ASSERT_THAT(ReadPacket(nullptr, &previous_packet_dropped),
2515+
ElementsAre(FakePacketFragment(20, 'd')));
2516+
EXPECT_FALSE(previous_packet_dropped);
2517+
2518+
ASSERT_THAT(ReadPacket(), IsEmpty());
2519+
}
2520+
24502521
} // namespace perfetto

0 commit comments

Comments
 (0)