Skip to content

Commit 0b6612c

Browse files
authored
tp: fix dropped traces when merging same-remote-machine files (#6687)
1 parent 43924e6 commit 0b6612c

3 files changed

Lines changed: 67 additions & 1 deletion

File tree

src/trace_processor/importers/common/clock_tracker.cc

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,31 @@ base::StatusOr<uint32_t> ClockTracker::AddSnapshot(
8888
}
8989
current_file_tag_ = own_file_id_;
9090
}
91-
for (auto& ct : clock_timestamps)
91+
// REALTIME is a single universal wall clock and the cross-machine rendezvous
92+
// domain used to place files that share no other clock (see
93+
// docs/concepts/merging-traces.md). A non-primary file on a *remote*
94+
// (non-trace-time) machine reaches trace time only through that rendezvous,
95+
// so relate each REALTIME clock it actually carries to the machine-canonical
96+
// REALTIME at zero offset (a twin added to this snapshot). The trace-time
97+
// machine is excluded: its events reach trace time directly through BOOTTIME.
98+
const bool bridge_realtime =
99+
!is_primary_ &&
100+
machine_id_ != context_->trace_time_state->clock_id.machine_id;
101+
std::vector<ClockTimestamp> canonical_realtime;
102+
for (auto& ct : clock_timestamps) {
103+
const uint32_t clock = ct.clock.id.clock_id;
92104
ct.clock.id = ClockId::Qualify(ct.clock.id, machine_id_, current_file_tag_);
105+
if (PERFETTO_UNLIKELY(
106+
bridge_realtime &&
107+
(clock == protos::pbzero::BUILTIN_CLOCK_REALTIME ||
108+
clock == protos::pbzero::BUILTIN_CLOCK_REALTIME_COARSE))) {
109+
ClockTimestamp twin = ct;
110+
twin.clock.id = ClockId::Qualify(ClockId::Machine(clock), machine_id_, 0);
111+
canonical_realtime.push_back(twin);
112+
}
113+
}
114+
clock_timestamps.insert(clock_timestamps.end(), canonical_realtime.begin(),
115+
canonical_realtime.end());
93116
return AddSnapshotInternal(clock_timestamps);
94117
}
95118

src/trace_processor/importers/common/clock_tracker_unittest.cc

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,43 @@ TEST_F(ClockTrackerTest, ClockDomainConversions) {
130130
static_cast<int64_t>(100000 - 1000 + 1e6));
131131
}
132132

133+
// Merging independent traces from a remote machine deduplicates them onto one
134+
// machine: the first is the primary, the rest are non-primary. A non-primary
135+
// trace isolates its builtin clocks onto its own file tag so its snapshots
136+
// cannot corrupt the others' conversions. But a remote machine's only route to
137+
// trace time is the cross-machine REALTIME rendezvous (its BOOTTIME belongs to
138+
// a different machine), so a remote non-primary trace's REALTIME must stay on
139+
// the machine-canonical tag to join that rendezvous. Otherwise every one of its
140+
// events is dropped (clock_sync_failure_no_path).
141+
TEST_F(ClockTrackerTest, RemoteNonPrimaryFileResolvesThroughSharedRealtime) {
142+
// Host machine (trace time == its BOOTTIME): relate REALTIME to BOOTTIME so
143+
// the cross-machine REALTIME rendezvous can reach trace time.
144+
ct_->AddSnapshot({{REALTIME, 1000}, {BOOTTIME, 1000}});
145+
146+
// Remote machine, primary trace: its own REALTIME<->BOOTTIME plus the
147+
// deferred BOOTTIME->trace-time sync the remote reader registers. This builds
148+
// the REALTIME(remote)<->REALTIME(host) rendezvous.
149+
auto remote_primary = MakeRemoteTracker(/*raw_machine_id=*/1);
150+
remote_primary->AddSnapshot({{REALTIME, 2000}, {BOOTTIME, 200000}});
151+
remote_primary->AddDeferredClockSync(BOOTTIME);
152+
ASSERT_TRUE(remote_primary->ToTraceTime(BOOTTIME, 200000).has_value());
153+
154+
// Remote machine, non-primary trace: a distinct file id but the SAME machine
155+
// as remote_primary (reuse its machine_tracker; a real merge dedups files
156+
// with the same raw machine id onto one machine row). Only its own
157+
// REALTIME<->BOOTTIME. Its REALTIME must join the shared machine-canonical
158+
// REALTIME to reach trace time via the rendezvous.
159+
context_.trace_state =
160+
TraceProcessorContextPtr<TraceProcessorContext::TraceState>::MakeRoot(
161+
TraceProcessorContext::TraceState{TraceId(9)});
162+
ClockTracker remote_np(&context_, primary_sync_.get(), /*is_primary=*/false);
163+
remote_np.AddSnapshot({{REALTIME, 3000}, {BOOTTIME, 300000}});
164+
165+
// A BOOTTIME event on the non-primary trace reaches trace time only because
166+
// its REALTIME is the shared machine-canonical node feeding the rendezvous.
167+
EXPECT_TRUE(remote_np.ToTraceTime(BOOTTIME, 300000).has_value());
168+
}
169+
133170
// When a clock moves backwards conversions *from* that clock are forbidden
134171
// but conversions *to* that clock should still work.
135172
// Think to the case of REALTIME going backwards from 3AM to 2AM during DST day.

src/trace_processor/importers/proto/proto_trace_reader.cc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1263,6 +1263,12 @@ base::Status ProtoTraceReader::OnPushDataToSorter() {
12631263
for (auto& packet : eof_deferred_packets_) {
12641264
RETURN_IF_ERROR(TimestampTokenizeAndPushToSorter(std::move(packet)));
12651265
}
1266+
// Remote-machine readers are only ever reached by the dispatcher, never by
1267+
// the ForwardingTraceParser, so their own clock-deferred packets would
1268+
// otherwise never be flushed. Propagate EOF to them too.
1269+
for (auto it = machine_to_proto_readers_.GetIterator(); it; ++it) {
1270+
RETURN_IF_ERROR(it.value()->OnPushDataToSorter());
1271+
}
12661272
return base::OkStatus();
12671273
}
12681274

0 commit comments

Comments
 (0)