xref: /aosp_15_r20/external/perfetto/src/trace_redaction/redact_process_trees.cc (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
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