1 /*
2 * Copyright (C) 2024 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 "src/trace_redaction/collect_timeline_events.h"
18
19 #include "src/trace_redaction/process_thread_timeline.h"
20 #include "src/trace_redaction/trace_redaction_framework.h"
21
22 #include "protos/perfetto/trace/ftrace/ftrace_event.pbzero.h"
23 #include "protos/perfetto/trace/ftrace/ftrace_event_bundle.pbzero.h"
24 #include "protos/perfetto/trace/ftrace/sched.pbzero.h"
25 #include "protos/perfetto/trace/ftrace/task.pbzero.h"
26 #include "protos/perfetto/trace/ps/process_tree.pbzero.h"
27 #include "protos/perfetto/trace/trace_packet.pbzero.h"
28
29 namespace perfetto::trace_redaction {
30 namespace {
31
32 using TracePacket = protos::pbzero::TracePacket;
33 using ProcessTree = protos::pbzero::ProcessTree;
34 using FtraceEvent = protos::pbzero::FtraceEvent;
35 using FtraceEventBundle = protos::pbzero::FtraceEventBundle;
36 using SchedProcessFreeFtraceEvent = protos::pbzero::SchedProcessFreeFtraceEvent;
37 using TaskNewtaskFtraceEvent = protos::pbzero::TaskNewtaskFtraceEvent;
38
MarkOpen(uint64_t ts,const ProcessTree::Process::Decoder & process,ProcessThreadTimeline * timeline)39 void MarkOpen(uint64_t ts,
40 const ProcessTree::Process::Decoder& process,
41 ProcessThreadTimeline* timeline) {
42 auto uid = static_cast<uint64_t>(process.uid());
43
44 // See "trace_redaction_framework.h" for why uid must be normalized.
45 auto e = ProcessThreadTimeline::Event::Open(ts, process.pid(), process.ppid(),
46 NormalizeUid(uid));
47 timeline->Append(e);
48 }
49
MarkOpen(uint64_t ts,const ProcessTree::Thread::Decoder & thread,ProcessThreadTimeline * timeline)50 void MarkOpen(uint64_t ts,
51 const ProcessTree::Thread::Decoder& thread,
52 ProcessThreadTimeline* timeline) {
53 auto e = ProcessThreadTimeline::Event::Open(ts, thread.tid(), thread.tgid());
54 timeline->Append(e);
55 }
56
MarkClose(const FtraceEvent::Decoder & event,const SchedProcessFreeFtraceEvent::Decoder process_free,ProcessThreadTimeline * timeline)57 void MarkClose(const FtraceEvent::Decoder& event,
58 const SchedProcessFreeFtraceEvent::Decoder process_free,
59 ProcessThreadTimeline* timeline) {
60 auto e = ProcessThreadTimeline::Event::Close(event.timestamp(),
61 process_free.pid());
62 timeline->Append(e);
63 }
64
MarkOpen(const FtraceEvent::Decoder & event,const TaskNewtaskFtraceEvent::Decoder new_task,ProcessThreadTimeline * timeline)65 void MarkOpen(const FtraceEvent::Decoder& event,
66 const TaskNewtaskFtraceEvent::Decoder new_task,
67 ProcessThreadTimeline* timeline) {
68 // Event though pid() is uint32_t. all other pid values use int32_t, so it's
69 // assumed to be safe to narrow-cast it.
70 auto ppid = static_cast<int32_t>(event.pid());
71 auto e = ProcessThreadTimeline::Event::Open(event.timestamp(), new_task.pid(),
72 ppid);
73 timeline->Append(e);
74 }
75
AppendEvents(uint64_t ts,const ProcessTree::Decoder & tree,ProcessThreadTimeline * timeline)76 void AppendEvents(uint64_t ts,
77 const ProcessTree::Decoder& tree,
78 ProcessThreadTimeline* timeline) {
79 for (auto it = tree.processes(); it; ++it) {
80 MarkOpen(ts, ProcessTree::Process::Decoder(*it), timeline);
81 }
82
83 for (auto it = tree.threads(); it; ++it) {
84 MarkOpen(ts, ProcessTree::Thread::Decoder(*it), timeline);
85 }
86 }
87
AppendEvents(const FtraceEventBundle::Decoder & ftrace_events,ProcessThreadTimeline * timeline)88 void AppendEvents(const FtraceEventBundle::Decoder& ftrace_events,
89 ProcessThreadTimeline* timeline) {
90 for (auto it = ftrace_events.event(); it; ++it) {
91 FtraceEvent::Decoder event(*it);
92
93 if (event.has_task_newtask()) {
94 MarkOpen(event, TaskNewtaskFtraceEvent::Decoder(event.task_newtask()),
95 timeline);
96 continue;
97 }
98
99 if (event.has_sched_process_free()) {
100 MarkClose(
101 event,
102 SchedProcessFreeFtraceEvent::Decoder(event.sched_process_free()),
103 timeline);
104 continue;
105 }
106 }
107 }
108
109 } // namespace
110
Begin(Context * context) const111 base::Status CollectTimelineEvents::Begin(Context* context) const {
112 // This primitive is artifically limited to owning the timeline. In practice
113 // there is no reason why multiple primitives could contribute to the
114 // timeline.
115 if (context->timeline) {
116 return base::ErrStatus(
117 "CollectTimelineEvents: timeline was already initialized");
118 }
119
120 context->timeline = std::make_unique<ProcessThreadTimeline>();
121 return base::OkStatus();
122 }
123
Collect(const TracePacket::Decoder & packet,Context * context) const124 base::Status CollectTimelineEvents::Collect(const TracePacket::Decoder& packet,
125 Context* context) const {
126 // Unlike ftrace events, process trees do not provide per-process or
127 // per-thread timing information. The packet has timestamp and the process
128 // tree has collection_end_timestamp (collection_end_timestamp > timestamp).
129 //
130 // The packet's timestamp based on the assumption that in order to be
131 // collected, the processes and threads had to exist before "now".
132 if (packet.has_process_tree()) {
133 AppendEvents(packet.timestamp(),
134 ProcessTree::Decoder(packet.process_tree()),
135 context->timeline.get());
136 }
137
138 if (packet.has_ftrace_events()) {
139 AppendEvents(FtraceEventBundle::Decoder(packet.ftrace_events()),
140 context->timeline.get());
141 }
142
143 return base::OkStatus();
144 }
145
End(Context * context) const146 base::Status CollectTimelineEvents::End(Context* context) const {
147 // Sort must be called in order to read from the timeline. If any more events
148 // are added after this, then sort will need to be called again.
149 context->timeline->Sort();
150 return base::OkStatus();
151 }
152
153 } // namespace perfetto::trace_redaction
154