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_process_trees.h"
18
19 #include <string>
20
21 #include "perfetto/base/status.h"
22 #include "perfetto/protozero/field.h"
23 #include "perfetto/protozero/scattered_heap_buffer.h"
24 #include "src/trace_processor/util/status_macros.h"
25 #include "src/trace_redaction/proto_util.h"
26 #include "src/trace_redaction/trace_redaction_framework.h"
27
28 #include "protos/perfetto/trace/ps/process_tree.pbzero.h"
29 #include "protos/perfetto/trace/trace_packet.pbzero.h"
30
31 namespace perfetto::trace_redaction {
32
33 ProcessTreeModifier::~ProcessTreeModifier() = default;
34
Modify(const Context &,protos::pbzero::ProcessTree *) const35 base::Status ProcessTreeDoNothing::Modify(const Context&,
36 protos::pbzero::ProcessTree*) const {
37 return base::OkStatus();
38 }
39
Modify(const Context & context,protos::pbzero::ProcessTree * message) const40 base::Status ProcessTreeCreateSynthThreads::Modify(
41 const Context& context,
42 protos::pbzero::ProcessTree* message) const {
43 PERFETTO_DCHECK(message);
44
45 if (!context.synthetic_process) {
46 return base::ErrStatus(
47 "ProcessTreeCreateSynthThreads: missing synthetic thread group");
48 }
49
50 const auto& tids = context.synthetic_process->tids();
51
52 // At the very least there needs to be a main thread and one CPU thread. If
53 // not, something is wrong.
54 if (tids.size() < 2) {
55 return base::ErrStatus(
56 "ProcessTreeCreateSynthThreads: missing synthetic threads");
57 }
58
59 auto it = tids.begin();
60
61 auto* process = message->add_processes();
62 process->set_uid(context.synthetic_process->uid());
63 process->set_ppid(context.synthetic_process->ppid());
64 process->set_pid(*it);
65 process->add_cmdline("Other-Processes");
66
67 ++it;
68
69 for (; it != tids.end(); ++it) {
70 auto name = std::to_string(*it);
71 name.insert(0, "cpu-");
72
73 auto* thread = message->add_threads();
74 thread->set_tgid(context.synthetic_process->tgid());
75 thread->set_tid(*it);
76 thread->set_name(name);
77 }
78
79 return base::OkStatus();
80 }
81
Transform(const Context & context,std::string * packet) const82 base::Status RedactProcessTrees::Transform(const Context& context,
83 std::string* packet) const {
84 PERFETTO_DCHECK(packet);
85
86 if (!context.package_uid.has_value()) {
87 return base::ErrStatus("RedactProcessTrees: missing package uid.");
88 }
89
90 if (!context.timeline) {
91 return base::ErrStatus("RedactProcessTrees: missing timeline.");
92 }
93
94 if (!context.synthetic_process) {
95 return base::ErrStatus("RedactProcessTrees: missing synthentic threads.");
96 }
97
98 protozero::ProtoDecoder decoder(*packet);
99
100 auto tree =
101 decoder.FindField(protos::pbzero::TracePacket::kProcessTreeFieldNumber);
102
103 if (!tree.valid()) {
104 return base::OkStatus();
105 }
106
107 // This has been verified by the verify primitive.
108 auto timestamp =
109 decoder.FindField(protos::pbzero::TracePacket::kTimestampFieldNumber);
110
111 protozero::HeapBuffered<protos::pbzero::TracePacket> message;
112
113 for (auto it = decoder.ReadField(); it.valid(); it = decoder.ReadField()) {
114 if (it.id() == tree.id()) {
115 RETURN_IF_ERROR(OnProcessTree(context, timestamp.as_uint64(),
116 it.as_bytes(),
117 message->set_process_tree()));
118 } else {
119 proto_util::AppendField(it, message.get());
120 }
121 }
122
123 packet->assign(message.SerializeAsString());
124
125 return base::OkStatus();
126 }
127
OnProcessTree(const Context & context,uint64_t ts,protozero::ConstBytes bytes,protos::pbzero::ProcessTree * message) const128 base::Status RedactProcessTrees::OnProcessTree(
129 const Context& context,
130 uint64_t ts,
131 protozero::ConstBytes bytes,
132 protos::pbzero::ProcessTree* message) const {
133 protozero::ProtoDecoder decoder(bytes);
134
135 for (auto it = decoder.ReadField(); it.valid(); it = decoder.ReadField()) {
136 switch (it.id()) {
137 case protos::pbzero::ProcessTree::kProcessesFieldNumber:
138 RETURN_IF_ERROR(OnProcess(context, ts, it, message));
139 break;
140 case protos::pbzero::ProcessTree::kThreadsFieldNumber:
141 RETURN_IF_ERROR(OnThread(context, ts, it, message));
142 break;
143 default:
144 proto_util::AppendField(it, message);
145 break;
146 }
147 }
148
149 PERFETTO_DCHECK(modifier_);
150 return modifier_->Modify(context, message);
151 }
152
OnProcess(const Context & context,uint64_t ts,protozero::Field field,protos::pbzero::ProcessTree * message) const153 base::Status RedactProcessTrees::OnProcess(
154 const Context& context,
155 uint64_t ts,
156 protozero::Field field,
157 protos::pbzero::ProcessTree* message) const {
158 protozero::ProtoDecoder decoder(field.as_bytes());
159
160 auto pid =
161 decoder.FindField(protos::pbzero::ProcessTree::Process::kPidFieldNumber);
162 if (!pid.valid()) {
163 return base::ErrStatus("RedactProcessTrees: process with no pid");
164 }
165
166 PERFETTO_DCHECK(filter_);
167
168 if (filter_->Includes(context, ts, pid.as_int32())) {
169 proto_util::AppendField(field, message);
170 }
171
172 return base::OkStatus();
173 }
174
OnThread(const Context & context,uint64_t ts,protozero::Field field,protos::pbzero::ProcessTree * message) const175 base::Status RedactProcessTrees::OnThread(
176 const Context& context,
177 uint64_t ts,
178 protozero::Field field,
179 protos::pbzero::ProcessTree* message) const {
180 protozero::ProtoDecoder decoder(field.as_bytes());
181
182 auto tid =
183 decoder.FindField(protos::pbzero::ProcessTree::Thread::kTidFieldNumber);
184 if (!tid.valid()) {
185 return base::ErrStatus("RedactProcessTrees: thread with no tid");
186 }
187
188 PERFETTO_DCHECK(filter_);
189
190 if (filter_->Includes(context, ts, tid.as_int32())) {
191 proto_util::AppendField(field, message);
192 }
193
194 return base::OkStatus();
195 }
196
197 } // namespace perfetto::trace_redaction
198