Merge "Trace Redaction - Make Timeline Range Inclusive" into main
diff --git a/src/trace_redaction/collect_timeline_events_unittest.cc b/src/trace_redaction/collect_timeline_events_unittest.cc
index fab14fb..9b8790b 100644
--- a/src/trace_redaction/collect_timeline_events_unittest.cc
+++ b/src/trace_redaction/collect_timeline_events_unittest.cc
@@ -16,12 +16,13 @@
*/
#include "src/trace_redaction/collect_timeline_events.h"
+#include "protos/perfetto/trace/ftrace/sched.gen.h"
#include "src/base/test/status_matchers.h"
#include "src/trace_redaction/trace_redaction_framework.h"
#include "protos/perfetto/trace/ftrace/ftrace_event.gen.h"
#include "protos/perfetto/trace/ftrace/ftrace_event_bundle.gen.h"
-#include "protos/perfetto/trace/ftrace/sched.gen.h"
+#include "protos/perfetto/trace/ftrace/task.gen.h"
#include "protos/perfetto/trace/ps/process_tree.gen.h"
#include "protos/perfetto/trace/trace_packet.gen.h"
@@ -29,23 +30,13 @@
namespace {
-constexpr uint64_t kSystemPackage = 0;
-constexpr uint64_t kUnityUid = 10252;
+constexpr uint64_t kPackage = 0;
+constexpr int32_t kPid = 1093;
-constexpr int32_t kZygotePid = 1093;
-constexpr int32_t kUnityPid = 7105;
-constexpr int32_t kUnityTid = 7127;
-
-// TODO(vaage): Need a better name and documentation.
-constexpr int32_t kPidWithNoOpen = 32;
-
-constexpr uint64_t kProcessTreeTimestamp = 6702093635419927;
-
-// These two timestamps are used to separate the packet and event times. A
-// packet can have time X, but the time can have time Y. Time Y should be used
-// for the events.
-constexpr uint64_t kThreadFreePacketTimestamp = 6702094703928940;
-constexpr uint64_t kThreadFreeOffset = 100;
+constexpr uint64_t kFullStep = 1000;
+constexpr uint64_t kTimeA = 0;
+constexpr uint64_t kTimeB = kFullStep;
+constexpr uint64_t kTimeC = kFullStep * 2;
} // namespace
@@ -53,167 +44,138 @@
// contains trace elements that should create timeline events.
class CollectTimelineEventsTest : public testing::Test {
protected:
- void SetUp() {
- CollectTimelineEvents collector;
-
- ASSERT_OK(collector.Begin(&context_));
-
- // Minimum ProcessTree information.
- {
- auto timestamp = kProcessTreeTimestamp;
-
- protos::gen::TracePacket packet;
- packet.set_timestamp(timestamp);
-
- auto* process_tree = packet.mutable_process_tree();
-
- auto* zygote = process_tree->add_processes();
- zygote->set_pid(kZygotePid);
- zygote->set_ppid(1);
- zygote->set_uid(kSystemPackage);
-
- auto* unity = process_tree->add_processes();
- unity->set_pid(kUnityPid);
- unity->set_ppid(1093);
- unity->set_uid(kUnityUid);
-
- auto* thread = process_tree->add_threads();
- thread->set_tid(kUnityTid);
- thread->set_tgid(kUnityPid);
-
- process_tree->set_collection_end_timestamp(timestamp);
-
- auto buffer = packet.SerializeAsString();
-
- protos::pbzero::TracePacket::Decoder decoder(buffer);
- ASSERT_OK(collector.Collect(decoder, &context_));
- }
-
- // Minimum proc free informations.
- {
- auto timestamp = kThreadFreePacketTimestamp;
-
- protos::gen::TracePacket packet;
- packet.set_timestamp(timestamp);
-
- auto* ftrace_event = packet.mutable_ftrace_events()->add_event();
- ftrace_event->set_timestamp(timestamp + kThreadFreeOffset);
- ftrace_event->set_pid(10); // kernel thread - e.g. "rcuop/0"
-
- auto* process_free = ftrace_event->mutable_sched_process_free();
- process_free->set_pid(kUnityTid);
-
- auto buffer = packet.SerializeAsString();
-
- protos::pbzero::TracePacket::Decoder decoder(buffer);
- ASSERT_OK(collector.Collect(decoder, &context_));
- }
-
- // Free a pid that neve started.
- {
- auto timestamp = kThreadFreePacketTimestamp;
-
- protos::gen::TracePacket packet;
- packet.set_timestamp(timestamp);
-
- auto* ftrace_event = packet.mutable_ftrace_events()->add_event();
- ftrace_event->set_timestamp(timestamp + kThreadFreeOffset);
- ftrace_event->set_pid(10); // kernel thread - e.g. "rcuop/0"
-
- auto* process_free = ftrace_event->mutable_sched_process_free();
- process_free->set_pid(kPidWithNoOpen);
-
- auto buffer = packet.SerializeAsString();
-
- protos::pbzero::TracePacket::Decoder decoder(buffer);
- ASSERT_OK(collector.Collect(decoder, &context_));
- }
-
- ASSERT_OK(collector.End(&context_));
- }
+ void SetUp() { ASSERT_OK(collector_.Begin(&context_)); }
Context context_;
+ CollectTimelineEvents collector_;
};
-class CollectTimelineFindsOpenEventTest
- : public CollectTimelineEventsTest,
- public testing::WithParamInterface<int32_t> {};
+TEST_F(CollectTimelineEventsTest, OpenEventForProcessTreeProcess) {
+ {
+ protos::gen::TracePacket packet;
+ packet.set_timestamp(kTimeA);
-TEST_P(CollectTimelineFindsOpenEventTest, NoOpenEventBeforeProcessTree) {
- auto pid = GetParam();
+ auto* process_tree = packet.mutable_process_tree();
- auto event =
- context_.timeline->FindPreviousEvent(kProcessTreeTimestamp - 1, pid);
- ASSERT_EQ(event.type, ProcessThreadTimeline::Event::Type::kInvalid);
+ auto* process = process_tree->add_processes();
+ process->set_pid(kPid);
+ process->set_ppid(1);
+ process->set_uid(kPackage);
+
+ auto buffer = packet.SerializeAsString();
+
+ protos::pbzero::TracePacket::Decoder decoder(buffer);
+
+ ASSERT_OK(collector_.Collect(decoder, &context_));
+ }
+
+ ASSERT_OK(collector_.End(&context_));
+
+ const auto* event = context_.timeline->GetOpeningEvent(kTimeA, kPid);
+ ASSERT_TRUE(event);
+ ASSERT_TRUE(event->valid());
}
-TEST_P(CollectTimelineFindsOpenEventTest, OpenEventOnProcessTree) {
- auto pid = GetParam();
+TEST_F(CollectTimelineEventsTest, OpenEventForProcessTreeThread) {
+ {
+ protos::gen::TracePacket packet;
+ packet.set_timestamp(kTimeA);
- auto event = context_.timeline->FindPreviousEvent(kProcessTreeTimestamp, pid);
- ASSERT_EQ(event.type, ProcessThreadTimeline::Event::Type::kOpen);
- ASSERT_EQ(event.pid, pid);
+ auto* process_tree = packet.mutable_process_tree();
+
+ auto* process = process_tree->add_threads();
+ process->set_tid(kPid);
+ process->set_tgid(1);
+
+ auto buffer = packet.SerializeAsString();
+
+ protos::pbzero::TracePacket::Decoder decoder(buffer);
+
+ ASSERT_OK(collector_.Collect(decoder, &context_));
+ }
+
+ ASSERT_OK(collector_.End(&context_));
+
+ const auto* event = context_.timeline->GetOpeningEvent(kTimeA, kPid);
+ ASSERT_TRUE(event);
+ ASSERT_TRUE(event->valid());
}
-TEST_P(CollectTimelineFindsOpenEventTest, OpenEventAfterProcessTree) {
- auto pid = GetParam();
+TEST_F(CollectTimelineEventsTest, OpenEventForNewTask) {
+ {
+ protos::gen::TracePacket packet;
+ auto* event = packet.mutable_ftrace_events()->add_event();
+ event->set_timestamp(kTimeA);
- auto event = context_.timeline->FindPreviousEvent(kProcessTreeTimestamp, pid);
- ASSERT_EQ(event.type, ProcessThreadTimeline::Event::Type::kOpen);
- ASSERT_EQ(event.pid, pid);
+ auto* new_task = event->mutable_task_newtask();
+ new_task->set_clone_flags(0);
+ new_task->set_comm("");
+ new_task->set_oom_score_adj(0);
+ new_task->set_pid(kPid);
+
+ auto buffer = packet.SerializeAsString();
+
+ protos::pbzero::TracePacket::Decoder decoder(buffer);
+
+ ASSERT_OK(collector_.Collect(decoder, &context_));
+ }
+
+ ASSERT_OK(collector_.End(&context_));
+
+ const auto* open_event = context_.timeline->GetOpeningEvent(kTimeA, kPid);
+ ASSERT_TRUE(open_event);
+ ASSERT_TRUE(open_event->valid());
}
-INSTANTIATE_TEST_SUITE_P(
- SystemProcess,
- CollectTimelineFindsOpenEventTest,
- testing::Values(kZygotePid, // System-level process/thread
- kUnityPid, // Process
- kUnityTid // Child thread. kUnityPid is the parent.
- ));
+TEST_F(CollectTimelineEventsTest, ProcFreeEndsThread) {
+ {
+ protos::gen::TracePacket packet;
-class CollectTimelineFindsFreeEventTest : public CollectTimelineEventsTest {};
+ auto* event = packet.mutable_ftrace_events()->add_event();
+ event->set_timestamp(kTimeA);
-TEST_F(CollectTimelineFindsFreeEventTest, UsesFtraceEventTime) {
- auto pid = kUnityTid;
+ auto* new_task = event->mutable_task_newtask();
+ new_task->set_clone_flags(0);
+ new_task->set_comm("");
+ new_task->set_oom_score_adj(0);
+ new_task->set_pid(kPid);
- // While this will be a valid event (type != invalid), it won't be the close
- // event.
- auto incorrect =
- context_.timeline->FindPreviousEvent(kThreadFreePacketTimestamp, pid);
- ASSERT_EQ(incorrect.type, ProcessThreadTimeline::Event::Type::kOpen);
+ auto buffer = packet.SerializeAsString();
- auto correct = context_.timeline->FindPreviousEvent(
- kThreadFreePacketTimestamp + kThreadFreeOffset, pid);
- ASSERT_EQ(correct.type, ProcessThreadTimeline::Event::Type::kClose);
-}
+ protos::pbzero::TracePacket::Decoder decoder(buffer);
+ ASSERT_OK(collector_.Collect(decoder, &context_));
+ }
-TEST_F(CollectTimelineFindsFreeEventTest, NoCloseEventBeforeFree) {
- auto pid = kUnityTid;
+ {
+ protos::gen::TracePacket packet;
- auto event =
- context_.timeline->FindPreviousEvent(kThreadFreePacketTimestamp - 1, pid);
- ASSERT_EQ(event.type, ProcessThreadTimeline::Event::Type::kOpen);
- ASSERT_EQ(event.pid, pid);
-}
+ auto* event = packet.mutable_ftrace_events()->add_event();
+ event->set_timestamp(kTimeB);
-// Whether or not AddsCloseOnFree and AddsCloseAfterFree are the same close
-// event is an implementation detail.
-TEST_F(CollectTimelineFindsFreeEventTest, AddsCloseOnFree) {
- auto pid = kUnityTid;
+ auto* process_free = event->mutable_sched_process_free();
+ process_free->set_comm("");
+ process_free->set_pid(kPid);
+ process_free->set_prio(0);
- auto event = context_.timeline->FindPreviousEvent(
- kThreadFreePacketTimestamp + kThreadFreeOffset, pid);
- ASSERT_EQ(event.type, ProcessThreadTimeline::Event::Type::kClose);
- ASSERT_EQ(event.pid, pid);
-}
+ auto buffer = packet.SerializeAsString();
-TEST_F(CollectTimelineFindsFreeEventTest, AddsCloseAfterFree) {
- auto pid = kUnityTid;
+ protos::pbzero::TracePacket::Decoder decoder(buffer);
+ ASSERT_OK(collector_.Collect(decoder, &context_));
+ }
- auto event = context_.timeline->FindPreviousEvent(
- kThreadFreePacketTimestamp + kThreadFreeOffset + 1, pid);
- ASSERT_EQ(event.type, ProcessThreadTimeline::Event::Type::kClose);
- ASSERT_EQ(event.pid, pid);
+ ASSERT_OK(collector_.End(&context_));
+
+ const auto* start = context_.timeline->GetOpeningEvent(kTimeA, kPid);
+ ASSERT_TRUE(start);
+ ASSERT_TRUE(start->valid());
+
+ // The end event was correctly set so that the free event is inclusive.
+ const auto* end = context_.timeline->GetOpeningEvent(kTimeB, kPid);
+ ASSERT_TRUE(end);
+ ASSERT_TRUE(end->valid());
+
+ const auto* after = context_.timeline->GetOpeningEvent(kTimeC, kPid);
+ ASSERT_FALSE(after);
}
} // namespace perfetto::trace_redaction
diff --git a/src/trace_redaction/process_thread_timeline.cc b/src/trace_redaction/process_thread_timeline.cc
index 43d3637..a428073 100644
--- a/src/trace_redaction/process_thread_timeline.cc
+++ b/src/trace_redaction/process_thread_timeline.cc
@@ -45,95 +45,116 @@
mode_ = Mode::kRead;
}
+const ProcessThreadTimeline::Event* ProcessThreadTimeline::GetOpeningEvent(
+ uint64_t ts,
+ int32_t pid) const {
+ PERFETTO_DCHECK(mode_ == Mode::kRead);
+
+ auto prev_open = QueryLeftMax(ts, pid, Event::Type::kOpen);
+ auto prev_close = QueryLeftMax(ts, pid, Event::Type::kClose);
+
+ // If there is no open event before ts, it means pid never started.
+ if (!prev_open) {
+ return nullptr;
+ }
+
+ // There is a close event that is strictly between the open event and ts, then
+ // the pid is considered free.
+ //
+ // |---------| ^ : pid is free
+ // ^ |---------| ^ : pid is free
+ // ^---------| : pid is active
+ // |---------^ : pid is active
+ // |----^----| : pid is active
+
+ // Both open and close are less than or equal to ts (QueryLeftMax).
+ uint64_t close = prev_close ? prev_close->ts : 0;
+ uint64_t open = prev_open->ts;
+
+ return close > open && close < ts ? nullptr : prev_open;
+}
+
bool ProcessThreadTimeline::PidConnectsToUid(uint64_t ts,
int32_t pid,
uint64_t uid) const {
PERFETTO_DCHECK(mode_ == Mode::kRead);
- auto event = FindPreviousEvent(ts, pid);
+ const auto* prev_open = QueryLeftMax(ts, pid, Event::Type::kOpen);
+ const auto* prev_close = QueryLeftMax(ts, pid, Event::Type::kClose);
for (size_t d = 0; d < kMaxSearchDepth; ++d) {
- // The thread/process was freed. It won't exist until a new open event.
- if (event.type != Event::Type::kOpen) {
+ // If there's no previous open event, it means this pid was never created.
+ // This should not happen.
+ if (!prev_open) {
return false;
}
+ // This get tricky here. If done wrong, proc_free events will fail because
+ // they'll report as disconnected when they could be connected to the
+ // package. Inclusive bounds are used here. In context, if a task_newtask
+ // event happens at time T, the pid exists at time T. If a proc_free event
+ // happens at time T, the pid is "shutting down" at time T but still exists.
+ //
+ // B E : B = begin
+ // . . E = end
+ // . .
+ // |---------| ^ : pid is free
+ // ^ |---------| : pid is free
+ // ^---------| : pid is active
+ // |---------^ : pid is active
+ // |----^----| : pid is active
+
+ // By definition, both open and close are less than or equal to ts
+ // (QueryLeftMax), so problem space is reduces.
+ auto close = prev_close ? prev_close->ts : 0;
+ auto open = prev_open->ts;
+
+ if (close > open && close < ts) {
+ return false; // Close is sitting between open and ts.
+ }
+
// TODO(vaage): Normalize the uid values.
- if (event.uid == uid) {
+ if (prev_open->uid == uid) {
return true;
}
- // If there is no parent, there is no way to keep searching.
- if (event.ppid == Event::kUnknownPid) {
- return false;
+ if (prev_open->ppid == Event::kUnknownPid) {
+ return false; // If there is no parent, there is no way to keep
+ // searching.
}
- event = FindPreviousEvent(ts, event.ppid);
+ auto ppid = prev_open->ppid;
+
+ prev_open = QueryLeftMax(ts, ppid, Event::Type::kOpen);
+ prev_close = QueryLeftMax(ts, ppid, Event::Type::kClose);
}
return false;
}
-ProcessThreadTimeline::Event ProcessThreadTimeline::FindPreviousEvent(
+const ProcessThreadTimeline::Event* ProcessThreadTimeline::QueryLeftMax(
uint64_t ts,
- int32_t pid) const {
- PERFETTO_DCHECK(mode_ == Mode::kRead);
-
- Event fake = Event::Close(ts, pid);
+ int32_t pid,
+ Event::Type type) const {
+ auto fake = Event::Close(0, pid);
// Events are sorted by pid, creating islands of data. This search is to put
// the cursor at the start of pid's island. Each island will be small (a
// couple of items), so searching within the islands should be cheap.
- auto at = std::lower_bound(events_.begin(), events_.end(), fake, OrderByPid);
+ auto it = std::lower_bound(events_.begin(), events_.end(), fake, OrderByPid);
+ auto end = std::upper_bound(events_.begin(), events_.end(), fake, OrderByPid);
- // `pid` was not found in `events_`.
- if (at == events_.end()) {
- return {};
- }
+ const Event* best = nullptr;
- // "no best option".
- Event best = {};
+ for (; it != end; ++it) {
+ bool replace = false;
- // Run through all events (related to this pid) and find the last event that
- // comes before ts. If the events were in order by time, the search could be
- // more efficient, but the gains are margin because:
- //
- // 1. The number of edge cases go up.
- //
- // 2. The code is harder to read.
- //
- // 3. The performance gains are minimal or non-existant because of the small
- // number of events.
- for (; at != events_.end() && at->pid == pid; ++at) {
- // This event is after "now" and can safely be ignored.
- if (at->ts > ts) {
- continue;
+ if (it->type == type && it->ts <= ts) {
+ replace = !best || it->ts > best->ts;
}
- // `at` is know to be before now. So it is always safe to accept an event.
- //
- // All ts values are positive. However, ts_at and ts_best are both less than
- // ts (see early condition), meaning they can be considered negative values.
- //
- // at best ts
- // <---+-----------+-------------+---->
- // 31 64 93
- //
- // at best ts
- // <---+-----------+-------------+---->
- // -62 -29 0
- //
- // This means that the latest ts value under ts is the closest to ts.
-
- if (best.type == Event::Type::kInvalid || at->ts > best.ts) {
- best = *at;
- }
-
- // This handles the rare edge case where an open and close event occur at
- // the same time. The close event must get priority. This is done by
- // allowing close events to use ">=" where as other events can only use ">".
- if (at->type == Event::Type::kClose && at->ts == best.ts) {
- best = *at;
+ if (replace) {
+ best = &(*it);
}
}
diff --git a/src/trace_redaction/process_thread_timeline.h b/src/trace_redaction/process_thread_timeline.h
index fdb70ba..6352ebe 100644
--- a/src/trace_redaction/process_thread_timeline.h
+++ b/src/trace_redaction/process_thread_timeline.h
@@ -99,8 +99,14 @@
// Returns true if a process/thread is connected to a package.
bool PidConnectsToUid(uint64_t ts, int32_t pid, uint64_t uid) const;
- // Finds the pid's last event before ts.
- Event FindPreviousEvent(uint64_t ts, int32_t pid) const;
+ // Get the opening event for a pid. If pid ends at ts, an opening event will
+ // still be returned.
+ const Event* GetOpeningEvent(uint64_t ts, int32_t pid) const;
+
+ // SELECT MAX(ts), * FROM events WHERE pid=@pid AND type=@type AND ts<=@ts
+ const Event* QueryLeftMax(uint64_t ts,
+ int32_t pid,
+ ProcessThreadTimeline::Event::Type type) const;
private:
enum class Mode {
diff --git a/src/trace_redaction/process_thread_timeline_unittest.cc b/src/trace_redaction/process_thread_timeline_unittest.cc
index 4e86a8a..a563b58 100644
--- a/src/trace_redaction/process_thread_timeline_unittest.cc
+++ b/src/trace_redaction/process_thread_timeline_unittest.cc
@@ -23,21 +23,6 @@
namespace {
-class SliceTestParams {
- public:
- SliceTestParams(uint64_t ts, int32_t pid, uint64_t uid)
- : ts_(ts), pid_(pid), uid_(uid) {}
-
- uint64_t ts() const { return ts_; }
- int32_t pid() const { return pid_; }
- uint64_t uid() const { return uid_; }
-
- private:
- uint64_t ts_;
- int32_t pid_;
- uint64_t uid_;
-};
-
constexpr uint64_t kTimeA = 0;
constexpr uint64_t kTimeB = 10;
constexpr uint64_t kTimeC = 20;
@@ -103,72 +88,122 @@
ProcessThreadTimeline timeline_;
};
-TEST_F(ProcessThreadTimelineTest, NoEventBeforeFirstSpan) {
- auto event = timeline_.FindPreviousEvent(kTimeA, kPidB);
- ASSERT_EQ(event, invalid_);
+TEST_F(ProcessThreadTimelineTest, BeforeSpan) {
+ auto prev_open = timeline_.QueryLeftMax(
+ kTimeA, kPidB, ProcessThreadTimeline::Event::Type::kOpen);
+ ASSERT_FALSE(prev_open);
+
+ auto prev_close = timeline_.QueryLeftMax(
+ kTimeA, kPidB, ProcessThreadTimeline::Event::Type::kClose);
+ ASSERT_FALSE(prev_close);
}
-TEST_F(ProcessThreadTimelineTest, OpenEventAtStartOfFirstSpan) {
- auto event = timeline_.FindPreviousEvent(kTimeB, kPidB);
- ASSERT_EQ(event, pid_b_events_[0]);
+TEST_F(ProcessThreadTimelineTest, StartOfSpan) {
+ auto prev_open = timeline_.QueryLeftMax(
+ kTimeB, kPidB, ProcessThreadTimeline::Event::Type::kOpen);
+ ASSERT_TRUE(prev_open);
+ ASSERT_EQ(*prev_open, pid_b_events_[0]);
+
+ auto prev_close = timeline_.QueryLeftMax(
+ kTimeB, kPidB, ProcessThreadTimeline::Event::Type::kClose);
+ ASSERT_FALSE(prev_close);
}
-TEST_F(ProcessThreadTimelineTest, OpenEventWithinFirstSpan) {
- auto event = timeline_.FindPreviousEvent(kTimeC, kPidB);
- ASSERT_EQ(event, pid_b_events_[0]);
+TEST_F(ProcessThreadTimelineTest, DuringSpan) {
+ auto prev_open = timeline_.QueryLeftMax(
+ kTimeC, kPidB, ProcessThreadTimeline::Event::Type::kOpen);
+ ASSERT_TRUE(prev_open);
+ ASSERT_EQ(*prev_open, pid_b_events_[0]);
+
+ auto prev_close = timeline_.QueryLeftMax(
+ kTimeC, kPidB, ProcessThreadTimeline::Event::Type::kClose);
+ ASSERT_FALSE(prev_close);
}
-TEST_F(ProcessThreadTimelineTest, CloseEventAtEndOfFirstSpan) {
- auto event = timeline_.FindPreviousEvent(kTimeD, kPidB);
- ASSERT_EQ(event, pid_b_events_[1]);
+TEST_F(ProcessThreadTimelineTest, EndOfSpan) {
+ auto prev_open = timeline_.QueryLeftMax(
+ kTimeD, kPidB, ProcessThreadTimeline::Event::Type::kOpen);
+ ASSERT_TRUE(prev_open);
+ ASSERT_EQ(*prev_open, pid_b_events_[0]);
+
+ auto prev_close = timeline_.QueryLeftMax(
+ kTimeD, kPidB, ProcessThreadTimeline::Event::Type::kClose);
+ ASSERT_TRUE(prev_close);
+ ASSERT_EQ(*prev_close, pid_b_events_[1]);
}
-TEST_F(ProcessThreadTimelineTest, CloseEventBetweenSpans) {
- auto event = timeline_.FindPreviousEvent(kTimeE, kPidB);
- ASSERT_EQ(event, pid_b_events_[1]);
+// Even through its after a span, the previous open and close events should be
+// openned.
+TEST_F(ProcessThreadTimelineTest, AfterSpan) {
+ auto prev_open = timeline_.QueryLeftMax(
+ kTimeE, kPidB, ProcessThreadTimeline::Event::Type::kOpen);
+ ASSERT_TRUE(prev_open);
+ ASSERT_EQ(*prev_open, pid_b_events_[0]);
+
+ auto prev_close = timeline_.QueryLeftMax(
+ kTimeE, kPidB, ProcessThreadTimeline::Event::Type::kClose);
+ ASSERT_TRUE(prev_close);
+ ASSERT_EQ(*prev_close, pid_b_events_[1]);
}
-TEST_F(ProcessThreadTimelineTest, OpenEventAtStartOfSecondSpan) {
- auto event = timeline_.FindPreviousEvent(kTimeF, kPidB);
- ASSERT_EQ(event, pid_b_events_[2]);
+// When a pid is reused, the new open event (for the reused pid) should be
+// returned, but the close from the previous span should be returned.
+TEST_F(ProcessThreadTimelineTest, StartOfSecondSpan) {
+ auto prev_open = timeline_.QueryLeftMax(
+ kTimeF, kPidB, ProcessThreadTimeline::Event::Type::kOpen);
+ ASSERT_TRUE(prev_open);
+ ASSERT_EQ(*prev_open, pid_b_events_[2]);
+
+ auto prev_close = timeline_.QueryLeftMax(
+ kTimeF, kPidB, ProcessThreadTimeline::Event::Type::kClose);
+ ASSERT_TRUE(prev_close);
+ ASSERT_EQ(*prev_close, pid_b_events_[1]);
}
-TEST_F(ProcessThreadTimelineTest, OpenEventWithinSecondSpan) {
- auto event = timeline_.FindPreviousEvent(kTimeG, kPidB);
- ASSERT_EQ(event, pid_b_events_[2]);
+// Now that there is a second close event, both open and close events should
+// come from the same span.
+TEST_F(ProcessThreadTimelineTest, CloseOfSecondSpan) {
+ auto prev_open = timeline_.QueryLeftMax(
+ kTimeH, kPidB, ProcessThreadTimeline::Event::Type::kOpen);
+ ASSERT_TRUE(prev_open);
+ ASSERT_EQ(*prev_open, pid_b_events_[2]);
+
+ auto prev_close = timeline_.QueryLeftMax(
+ kTimeH, kPidB, ProcessThreadTimeline::Event::Type::kClose);
+ ASSERT_TRUE(prev_close);
+ ASSERT_EQ(*prev_close, pid_b_events_[3]);
}
-TEST_F(ProcessThreadTimelineTest, CloseEventAtEndOfSecondSpan) {
- auto event = timeline_.FindPreviousEvent(kTimeH, kPidB);
- ASSERT_EQ(event, pid_b_events_[3]);
+TEST_F(ProcessThreadTimelineTest, BeforeSpanWithZeroDuration) {
+ auto prev_open = timeline_.QueryLeftMax(
+ kTimeA, kPidD, ProcessThreadTimeline::Event::Type::kOpen);
+ ASSERT_FALSE(prev_open);
+
+ auto prev_close = timeline_.QueryLeftMax(
+ kTimeA, kPidD, ProcessThreadTimeline::Event::Type::kClose);
+ ASSERT_FALSE(prev_close);
}
-// Pid B is active. But Pid C is not active. At this point, Pid C should report
-// as invalid event though another pid is active.
-TEST_F(ProcessThreadTimelineTest, InvalidEventWhenAnotherSpanIsActive) {
- ASSERT_EQ(timeline_.FindPreviousEvent(kTimeB, kPidB), pid_b_events_[0]);
- ASSERT_EQ(timeline_.FindPreviousEvent(kTimeB, kPidC), invalid_);
+TEST_F(ProcessThreadTimelineTest, SpanWithZeroDuration) {
+ auto prev_open = timeline_.QueryLeftMax(
+ kTimeC, kPidD, ProcessThreadTimeline::Event::Type::kOpen);
+ ASSERT_TRUE(prev_open);
+ ASSERT_EQ(*prev_open, pid_d_events_[0]);
+
+ auto prev_close = timeline_.QueryLeftMax(
+ kTimeC, kPidD, ProcessThreadTimeline::Event::Type::kClose);
+ ASSERT_TRUE(prev_close);
+ ASSERT_EQ(*prev_close, pid_d_events_[1]);
}
-// When both pids are active, they should both report as active (using their
-// open events).
-TEST_F(ProcessThreadTimelineTest, ConcurrentSpansBothReportAsActive) {
- ASSERT_EQ(timeline_.FindPreviousEvent(kTimeC, kPidB), pid_b_events_[0]);
- ASSERT_EQ(timeline_.FindPreviousEvent(kTimeC, kPidC), pid_c_events_[0]);
-}
+TEST_F(ProcessThreadTimelineTest, AfterSpanWithZeroDuration) {
+ auto prev_open = timeline_.QueryLeftMax(
+ kTimeE, kPidD, ProcessThreadTimeline::Event::Type::kOpen);
+ ASSERT_TRUE(prev_open);
-// There are three test cases here:
-//
-// 1. Before open/close
-// 2. At open/close
-// 3. After open/close
-//
-// Normally these would be tree different test cases, but the naming gets
-// complicated, so it is easier to do it in one case.
-TEST_F(ProcessThreadTimelineTest, ZeroDuration) {
- ASSERT_EQ(timeline_.FindPreviousEvent(kTimeB, kPidD), invalid_);
- ASSERT_EQ(timeline_.FindPreviousEvent(kTimeC, kPidD), pid_d_events_[1]);
- ASSERT_EQ(timeline_.FindPreviousEvent(kTimeD, kPidD), pid_d_events_[1]);
+ auto prev_close = timeline_.QueryLeftMax(
+ kTimeE, kPidD, ProcessThreadTimeline::Event::Type::kClose);
+ ASSERT_TRUE(prev_close);
}
// |----- UID A -----| |----- UID C -----|
diff --git a/src/trace_redaction/redact_process_events_unittest.cc b/src/trace_redaction/redact_process_events_unittest.cc
index 7961017..ac7c624 100644
--- a/src/trace_redaction/redact_process_events_unittest.cc
+++ b/src/trace_redaction/redact_process_events_unittest.cc
@@ -331,11 +331,12 @@
ASSERT_TRUE(process_free.comm().empty());
}
-TEST_F(RedactProcessFree, DropsCommAtProcessFree) {
+TEST_F(RedactProcessFree, KeepsCommAtProcessFree) {
redact_.emplace_modifier<ClearComms>();
- // The new task is for Pid A. Pid A is part of Uid A. Keep Uid A; But, Pid A
- // ends at kTimeB (that's when the free event occurs). Drop comm.
+ // The new task is for Pid A. Pid A is part of Uid A. Keep Uid A; Process free
+ // marks the end of Pid A, but process free is inclusive and Pid A is only
+ // free after the event.
context_.package_uid = kUidA;
context_.timeline->Append(ProcessThreadTimeline::Event::Close(kTimeB, kPidA));
@@ -359,7 +360,7 @@
ASSERT_EQ(process_free.pid(), kPidA);
ASSERT_TRUE(process_free.has_comm());
- ASSERT_TRUE(process_free.comm().empty());
+ ASSERT_EQ(process_free.comm(), kCommA);
}
TEST_F(RedactProcessFree, KeepTaskInPackage) {