xref: /aosp_15_r20/external/perfetto/src/trace_redaction/trace_redactor.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/trace_redactor.h"
18 
19 #include <cstddef>
20 #include <string>
21 #include <string_view>
22 
23 #include "perfetto/base/status.h"
24 #include "perfetto/ext/base/file_utils.h"
25 #include "perfetto/ext/base/scoped_file.h"
26 #include "perfetto/ext/base/scoped_mmap.h"
27 #include "perfetto/protozero/scattered_heap_buffer.h"
28 #include "perfetto/trace_processor/trace_blob.h"
29 #include "perfetto/trace_processor/trace_blob_view.h"
30 #include "src/trace_processor/util/status_macros.h"
31 #include "src/trace_redaction/broadphase_packet_filter.h"
32 #include "src/trace_redaction/collect_frame_cookies.h"
33 #include "src/trace_redaction/collect_system_info.h"
34 #include "src/trace_redaction/collect_timeline_events.h"
35 #include "src/trace_redaction/find_package_uid.h"
36 #include "src/trace_redaction/merge_threads.h"
37 #include "src/trace_redaction/populate_allow_lists.h"
38 #include "src/trace_redaction/prune_package_list.h"
39 #include "src/trace_redaction/redact_ftrace_events.h"
40 #include "src/trace_redaction/redact_process_events.h"
41 #include "src/trace_redaction/redact_process_trees.h"
42 #include "src/trace_redaction/scrub_process_stats.h"
43 #include "src/trace_redaction/trace_redaction_framework.h"
44 #include "src/trace_redaction/verify_integrity.h"
45 
46 #include "protos/perfetto/trace/trace.pbzero.h"
47 
48 namespace perfetto::trace_redaction {
49 
50 using Trace = protos::pbzero::Trace;
51 using TracePacket = protos::pbzero::TracePacket;
52 
53 TraceRedactor::TraceRedactor() = default;
54 
55 TraceRedactor::~TraceRedactor() = default;
56 
Redact(std::string_view source_filename,std::string_view dest_filename,Context * context) const57 base::Status TraceRedactor::Redact(std::string_view source_filename,
58                                    std::string_view dest_filename,
59                                    Context* context) const {
60   const std::string source_filename_str(source_filename);
61   base::ScopedMmap mapped =
62       base::ReadMmapWholeFile(source_filename_str.c_str());
63   if (!mapped.IsValid()) {
64     return base::ErrStatus("TraceRedactor: failed to map pages for trace (%s)",
65                            source_filename_str.c_str());
66   }
67 
68   trace_processor::TraceBlobView whole_view(
69       trace_processor::TraceBlob::FromMmap(std::move(mapped)));
70 
71   RETURN_IF_ERROR(Collect(context, whole_view));
72 
73   for (const auto& builder : builders_) {
74     RETURN_IF_ERROR(builder->Build(context));
75   }
76 
77   return Transform(*context, whole_view, std::string(dest_filename));
78 }
79 
Collect(Context * context,const trace_processor::TraceBlobView & view) const80 base::Status TraceRedactor::Collect(
81     Context* context,
82     const trace_processor::TraceBlobView& view) const {
83   for (const auto& collector : collectors_) {
84     RETURN_IF_ERROR(collector->Begin(context));
85   }
86 
87   const Trace::Decoder trace_decoder(view.data(), view.length());
88 
89   for (auto packet_it = trace_decoder.packet(); packet_it; ++packet_it) {
90     const TracePacket::Decoder packet(packet_it->as_bytes());
91 
92     for (auto& collector : collectors_) {
93       RETURN_IF_ERROR(collector->Collect(packet, context));
94     }
95   }
96 
97   for (const auto& collector : collectors_) {
98     RETURN_IF_ERROR(collector->End(context));
99   }
100 
101   return base::OkStatus();
102 }
103 
Transform(const Context & context,const trace_processor::TraceBlobView & view,const std::string & dest_file) const104 base::Status TraceRedactor::Transform(
105     const Context& context,
106     const trace_processor::TraceBlobView& view,
107     const std::string& dest_file) const {
108   std::ignore = context;
109   const auto dest_fd = base::OpenFile(dest_file, O_RDWR | O_CREAT, 0666);
110 
111   if (dest_fd.get() == -1) {
112     return base::ErrStatus(
113         "Failed to open destination file; can't write redacted trace.");
114   }
115 
116   const Trace::Decoder trace_decoder(view.data(), view.length());
117   for (auto packet_it = trace_decoder.packet(); packet_it; ++packet_it) {
118     auto packet = packet_it->as_std_string();
119 
120     for (const auto& transformer : transformers_) {
121       // If the packet has been cleared, it means a tranformation has removed it
122       // from the trace. Stop processing it. This saves transforms from having
123       // to check and handle empty packets.
124       if (packet.empty()) {
125         break;
126       }
127 
128       RETURN_IF_ERROR(transformer->Transform(context, &packet));
129     }
130 
131     // The packet has been removed from the trace. Don't write an empty packet
132     // to disk.
133     if (packet.empty()) {
134       continue;
135     }
136 
137     protozero::HeapBuffered<protos::pbzero::Trace> serializer;
138     serializer->add_packet()->AppendRawProtoBytes(packet.data(), packet.size());
139     packet.assign(serializer.SerializeAsString());
140 
141     if (const auto exported_data =
142             base::WriteAll(dest_fd.get(), packet.data(), packet.size());
143         exported_data <= 0) {
144       return base::ErrStatus(
145           "TraceRedactor: failed to write redacted trace to disk");
146     }
147   }
148 
149   return base::OkStatus();
150 }
151 
CreateInstance(const Config & config)152 std::unique_ptr<TraceRedactor> TraceRedactor::CreateInstance(
153     const Config& config) {
154   auto redactor = std::make_unique<TraceRedactor>();
155 
156   // VerifyIntegrity breaks the CollectPrimitive pattern. Instead of writing to
157   // the context, its job is to read trace packets and return errors if any
158   // packet does not look "correct". This primitive is added first in an effort
159   // to detect and react to bad input before other collectors run.
160   if (config.verify) {
161     redactor->emplace_collect<VerifyIntegrity>();
162   }
163 
164   // Add all collectors.
165   redactor->emplace_collect<FindPackageUid>();
166   redactor->emplace_collect<CollectTimelineEvents>();
167   redactor->emplace_collect<CollectFrameCookies>();
168   redactor->emplace_collect<CollectSystemInfo>();
169 
170   // Add all builders.
171   redactor->emplace_build<ReduceFrameCookies>();
172   redactor->emplace_build<BuildSyntheticThreads>();
173 
174   {
175     // In order for BroadphasePacketFilter to work, something needs to populate
176     // the masks (i.e. PopulateAllowlists).
177     redactor->emplace_build<PopulateAllowlists>();
178     redactor->emplace_transform<BroadphasePacketFilter>();
179   }
180 
181   {
182     auto* primitive = redactor->emplace_transform<RedactFtraceEvents>();
183     primitive->emplace_ftrace_filter<FilterRss>();
184     primitive->emplace_post_filter_modifier<DoNothing>();
185   }
186 
187   {
188     auto* primitive = redactor->emplace_transform<RedactFtraceEvents>();
189     primitive->emplace_ftrace_filter<FilterFtraceUsingSuspendResume>();
190     primitive->emplace_post_filter_modifier<DoNothing>();
191   }
192 
193   {
194     // Remove all frame timeline events that don't belong to the target package.
195     redactor->emplace_transform<FilterFrameEvents>();
196   }
197 
198   redactor->emplace_transform<PrunePackageList>();
199 
200   // Process stats includes per-process information, such as:
201   //
202   //   processes {
203   //   pid: 1
204   //   vm_size_kb: 11716992
205   //   vm_rss_kb: 5396
206   //   rss_anon_kb: 2896
207   //   rss_file_kb: 1728
208   //   rss_shmem_kb: 772
209   //   vm_swap_kb: 4236
210   //   vm_locked_kb: 0
211   //   vm_hwm_kb: 6720
212   //   oom_score_adj: -1000
213   // }
214   //
215   // Use the ConnectedToPackage primitive to ensure only the target package has
216   // stats in the trace.
217   {
218     auto* primitive = redactor->emplace_transform<ScrubProcessStats>();
219     primitive->emplace_filter<ConnectedToPackage>();
220   }
221 
222   // Redacts all switch and waking events. This should use the same modifier and
223   // filter as the process events (see below).
224   {
225     auto* primitive = redactor->emplace_transform<RedactSchedEvents>();
226     primitive->emplace_modifier<ClearComms>();
227     primitive->emplace_waking_filter<ConnectedToPackage>();
228   }
229 
230   // Redacts all new task, rename task, process free events. This should use the
231   // same modifier and filter as the schedule events (see above).
232   {
233     auto* primitive = redactor->emplace_transform<RedactProcessEvents>();
234     primitive->emplace_modifier<ClearComms>();
235     primitive->emplace_filter<ConnectedToPackage>();
236   }
237 
238   // Merge Threads (part 1): Remove all waking events that connected to the
239   // target package. Change the pids not connected to the target package.
240   {
241     auto* primitive = redactor->emplace_transform<RedactSchedEvents>();
242     primitive->emplace_modifier<MergeThreadsPids>();
243     primitive->emplace_waking_filter<ConnectedToPackage>();
244   }
245 
246   // Merge Threads (part 2): Drop all process events not belonging to the
247   // target package. No modification is needed.
248   {
249     auto* primitive = redactor->emplace_transform<RedactProcessEvents>();
250     primitive->emplace_modifier<DoNothing>();
251     primitive->emplace_filter<ConnectedToPackage>();
252   }
253 
254   // Merge Threads (part 3): Replace ftrace event's pid (not the task's pid)
255   // for all pids not connected to the target package.
256   {
257     auto* primitive = redactor->emplace_transform<RedactFtraceEvents>();
258     primitive->emplace_post_filter_modifier<MergeThreadsPids>();
259     primitive->emplace_ftrace_filter<AllowAll>();
260   }
261 
262   // Configure the primitive to remove processes and threads that don't belong
263   // to the target package and adds a process and threads for the synth thread
264   // group and threads.
265   {
266     auto* primitive = redactor->emplace_transform<RedactProcessTrees>();
267     primitive->emplace_modifier<ProcessTreeCreateSynthThreads>();
268     primitive->emplace_filter<ConnectedToPackage>();
269   }
270 
271   return redactor;
272 }
273 
274 }  // namespace perfetto::trace_redaction
275