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/redact_ftrace_events.h"
18
19 #include <string>
20
21 #include "perfetto/protozero/scattered_heap_buffer.h"
22 #include "src/trace_processor/util/status_macros.h"
23 #include "src/trace_redaction/proto_util.h"
24
25 #include "protos/perfetto/trace/ftrace/ftrace_event.pbzero.h"
26 #include "protos/perfetto/trace/ftrace/ftrace_event_bundle.pbzero.h"
27 #include "protos/perfetto/trace/ftrace/power.pbzero.h"
28 #include "protos/perfetto/trace/trace.pbzero.h"
29
30 namespace perfetto::trace_redaction {
31
32 FtraceEventFilter::~FtraceEventFilter() = default;
33
Includes(const Context &,protozero::Field event) const34 bool FilterFtraceUsingSuspendResume::Includes(const Context&,
35 protozero::Field event) const {
36 // Values are taken from "suspend_period.textproto". These values would
37 // ideally be provided via the context, but until there are multiple sources,
38 // they can be here.
39 constexpr std::string_view kSyscoreSuspend = "syscore_suspend";
40 constexpr std::string_view kSyscoreResume = "syscore_resume";
41 constexpr std::string_view kTimekeepingFreeze = "timekeeping_freeze";
42
43 protozero::ProtoDecoder event_decoder(event.as_bytes());
44
45 // It's not a suspend-resume event, defer the decision to another filter.
46 auto suspend_resume = event_decoder.FindField(
47 protos::pbzero::FtraceEvent::kSuspendResumeFieldNumber);
48
49 if (!suspend_resume.valid()) {
50 return true;
51 }
52
53 protozero::ProtoDecoder suspend_resume_decoder(suspend_resume.as_bytes());
54
55 auto action = suspend_resume_decoder.FindField(
56 protos::pbzero::SuspendResumeFtraceEvent::kActionFieldNumber);
57
58 // If a suspend-resume has no action, there is nothing to redact, so it is
59 // safe to passthrough.
60 if (!action.valid()) {
61 return true;
62 }
63
64 std::string_view action_str(action.as_string().data, action.size());
65
66 return kSyscoreSuspend == action_str || kSyscoreResume == action_str ||
67 kTimekeepingFreeze == action_str;
68 }
69
Includes(const Context & context,protozero::Field event) const70 bool FilterRss::Includes(const Context& context, protozero::Field event) const {
71 protos::pbzero::FtraceEvent::Decoder event_decoder(event.as_bytes());
72
73 if (event_decoder.has_rss_stat_throttled() || event_decoder.has_rss_stat()) {
74 // The event's pid is unsigned, but tids are always signed.
75 auto pid = static_cast<int32_t>(event_decoder.pid());
76 return context.timeline->PidConnectsToUid(event_decoder.timestamp(), pid,
77 *context.package_uid);
78 }
79
80 return true;
81 }
82
Transform(const Context & context,std::string * packet) const83 base::Status RedactFtraceEvents::Transform(const Context& context,
84 std::string* packet) const {
85 if (packet == nullptr || packet->empty()) {
86 return base::ErrStatus("RedactFtraceEvents: null or empty packet.");
87 }
88
89 protozero::ProtoDecoder packet_decoder(*packet);
90 auto ftrace_events = packet_decoder.FindField(
91 protos::pbzero::TracePacket::kFtraceEventsFieldNumber);
92
93 if (!ftrace_events.valid()) {
94 return base::OkStatus();
95 }
96
97 protozero::ProtoDecoder decoder(*packet);
98
99 protozero::HeapBuffered<protos::pbzero::TracePacket> message;
100
101 for (auto it = decoder.ReadField(); it.valid(); it = decoder.ReadField()) {
102 if (it.id() == protos::pbzero::TracePacket::kFtraceEventsFieldNumber) {
103 RETURN_IF_ERROR(
104 OnFtraceEvents(context, it, message->set_ftrace_events()));
105 } else {
106 proto_util::AppendField(it, message.get());
107 }
108 }
109
110 packet->assign(message.SerializeAsString());
111
112 return base::OkStatus();
113 }
114
OnFtraceEvents(const Context & context,protozero::Field ftrace_events,protos::pbzero::FtraceEventBundle * message) const115 base::Status RedactFtraceEvents::OnFtraceEvents(
116 const Context& context,
117 protozero::Field ftrace_events,
118 protos::pbzero::FtraceEventBundle* message) const {
119 // If there are N ftrace events, and all N events are passed to the modifier,
120 // it is far better to have the bundle fully decoded ahead of time.
121 protos::pbzero::FtraceEventBundle::Decoder bundle(ftrace_events.as_bytes());
122
123 if (!bundle.has_cpu()) {
124 return base::ErrStatus(
125 "RedactFtraceEvents: missing field FtraceEventBundle::kCpu.");
126 }
127
128 protozero::ProtoDecoder decoder(ftrace_events.as_bytes());
129
130 for (auto it = decoder.ReadField(); it.valid(); it = decoder.ReadField()) {
131 if (it.id() == protos::pbzero::FtraceEventBundle::kEventFieldNumber) {
132 OnFtraceEvent(context, bundle, it, message);
133 } else {
134 proto_util::AppendField(it, message);
135 }
136 }
137
138 return base::OkStatus();
139 }
140
OnFtraceEvent(const Context & context,const protos::pbzero::FtraceEventBundle::Decoder & bundle,protozero::Field event,protos::pbzero::FtraceEventBundle * parent_message) const141 void RedactFtraceEvents::OnFtraceEvent(
142 const Context& context,
143 const protos::pbzero::FtraceEventBundle::Decoder& bundle,
144 protozero::Field event,
145 protos::pbzero::FtraceEventBundle* parent_message) const {
146 PERFETTO_DCHECK(filter_);
147 PERFETTO_DCHECK(modifier_);
148
149 if (event.id() != protos::pbzero::FtraceEventBundle::kEventFieldNumber) {
150 proto_util::AppendField(event, parent_message);
151 return;
152 }
153
154 protozero::ProtoDecoder decoder(event.as_bytes());
155
156 auto ts_field =
157 decoder.FindField(protos::pbzero::FtraceEvent::kTimestampFieldNumber);
158 PERFETTO_DCHECK(ts_field.valid());
159
160 auto pid_field =
161 decoder.FindField(protos::pbzero::FtraceEvent::kPidFieldNumber);
162 PERFETTO_DCHECK(pid_field.valid());
163
164 if (!filter_->Includes(context, event)) {
165 return;
166 }
167
168 auto cpu = static_cast<int32_t>(bundle.cpu());
169 auto pid = pid_field.as_int32();
170
171 modifier_->Modify(context, ts_field.as_uint64(), cpu, &pid, nullptr);
172
173 auto* message = parent_message->add_event();
174
175 for (auto it = decoder.ReadField(); it.valid(); it = decoder.ReadField()) {
176 if (it.id() == protos::pbzero::FtraceEvent::kPidFieldNumber) {
177 message->set_pid(static_cast<uint32_t>(pid));
178 } else {
179 proto_util::AppendField(it, message);
180 }
181 }
182 }
183
184 } // namespace perfetto::trace_redaction
185