xref: /aosp_15_r20/external/perfetto/src/trace_redaction/redact_process_trees_integrationtest.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 <string>
18 #include <string_view>
19 #include <vector>
20 
21 #include "src/base/test/status_matchers.h"
22 #include "src/trace_redaction/collect_system_info.h"
23 #include "src/trace_redaction/collect_timeline_events.h"
24 #include "src/trace_redaction/find_package_uid.h"
25 #include "src/trace_redaction/redact_process_trees.h"
26 #include "src/trace_redaction/trace_redaction_framework.h"
27 #include "src/trace_redaction/trace_redaction_integration_fixture.h"
28 #include "src/trace_redaction/trace_redactor.h"
29 #include "test/gtest_and_gmock.h"
30 
31 #include "protos/perfetto/trace/ps/process_tree.pbzero.h"
32 #include "protos/perfetto/trace/trace.pbzero.h"
33 
34 namespace perfetto::trace_redaction {
35 
36 namespace {
37 
38 constexpr std::string_view kProcessName =
39     "com.Unity.com.unity.multiplayer.samples.coop";
40 
41 }  // namespace
42 
43 class RedactProcessTreesIntegrationTest
44     : public testing::Test,
45       protected TraceRedactionIntegrationFixure {
46  protected:
SetUp()47   void SetUp() override {
48     trace_redactor_.emplace_collect<CollectSystemInfo>();
49     trace_redactor_.emplace_build<BuildSyntheticThreads>();
50 
51     trace_redactor_.emplace_collect<FindPackageUid>();
52     trace_redactor_.emplace_collect<CollectTimelineEvents>();
53 
54     // Filter the process tree based on whether or not a process is part of the
55     // target package.
56     auto* process_tree =
57         trace_redactor_.emplace_transform<RedactProcessTrees>();
58     process_tree->emplace_modifier<ProcessTreeDoNothing>();
59     process_tree->emplace_filter<ConnectedToPackage>();
60 
61     // In this case, the process and package have the same name.
62     context_.package_name = kProcessName;
63   }
64 
GetPids(const std::string & bytes) const65   std::unordered_set<int32_t> GetPids(const std::string& bytes) const {
66     std::unordered_set<int32_t> pids;
67 
68     protos::pbzero::Trace::Decoder decoder(bytes);
69 
70     for (auto it = decoder.packet(); it; ++it) {
71       protos::pbzero::TracePacket::Decoder packet(*it);
72 
73       if (packet.has_process_tree()) {
74         GetPids(packet.process_tree(), &pids);
75       }
76     }
77 
78     return pids;
79   }
80 
GetTids(const std::string & bytes) const81   std::unordered_set<int32_t> GetTids(const std::string& bytes) const {
82     std::unordered_set<int32_t> tids;
83 
84     protos::pbzero::Trace::Decoder decoder(bytes);
85 
86     for (auto it = decoder.packet(); it; ++it) {
87       protos::pbzero::TracePacket::Decoder packet(*it);
88 
89       if (packet.has_process_tree()) {
90         GetTids(packet.process_tree(), &tids);
91       }
92     }
93 
94     return tids;
95   }
96 
97   Context context_;
98   TraceRedactor trace_redactor_;
99 
100  private:
GetPids(protozero::ConstBytes bytes,std::unordered_set<int32_t> * pids) const101   void GetPids(protozero::ConstBytes bytes,
102                std::unordered_set<int32_t>* pids) const {
103     protos::pbzero::ProcessTree::Decoder process_tree(bytes);
104 
105     for (auto it = process_tree.processes(); it; ++it) {
106       protos::pbzero::ProcessTree::Process::Decoder process(*it);
107       pids->insert(process.ppid());
108       pids->insert(process.pid());
109     }
110   }
111 
GetTids(protozero::ConstBytes bytes,std::unordered_set<int32_t> * tids) const112   void GetTids(protozero::ConstBytes bytes,
113                std::unordered_set<int32_t>* tids) const {
114     protos::pbzero::ProcessTree::Decoder process_tree(bytes);
115 
116     for (auto it = process_tree.threads(); it; ++it) {
117       protos::pbzero::ProcessTree::Thread::Decoder thread(*it);
118       tids->insert(thread.tgid());
119       tids->insert(thread.tid());
120     }
121   }
122 };
123 
TEST_F(RedactProcessTreesIntegrationTest,FilterProcesses)124 TEST_F(RedactProcessTreesIntegrationTest, FilterProcesses) {
125   ASSERT_OK(Redact(trace_redactor_, &context_));
126 
127   auto original_trace_str = LoadOriginal();
128   ASSERT_OK(original_trace_str);
129 
130   auto redacted_trace_str = LoadRedacted();
131   ASSERT_OK(redacted_trace_str);
132 
133   auto original_pids = GetPids(*original_trace_str);
134   auto redacted_pids = GetPids(*redacted_trace_str);
135 
136   // There are 902 unique pids across all process trees:
137   //    grep 'processes {' -A 1  src.pftrace.txt | grep 'pid: ' | grep -Po "\d+"
138   //    | sort | uniq | wc -l
139   //
140   // But if ppids are included, there are 903 pids in the process tree:
141   //    grep 'processes {' -A 2  src.pftrace.txt | grep 'pid: ' | grep -Po "\d+"
142   //    | sort | uniq | wc -l
143   //
144   // The above grep statements use a stringified version of the trace. Using "-A
145   // 1" will return the pid line. Using "-A 2" will include both pid and ppid.
146   //
147   // The original process count aligns with trace processor. However, the
148   // redacted count does not. The final tree has one process but trace processor
149   // reports 4 processes.
150   ASSERT_EQ(original_pids.size(), 903u);
151   ASSERT_EQ(redacted_pids.size(), 2u);
152 
153   ASSERT_TRUE(redacted_pids.count(7105));
154 }
155 
TEST_F(RedactProcessTreesIntegrationTest,FilterThreads)156 TEST_F(RedactProcessTreesIntegrationTest, FilterThreads) {
157   ASSERT_OK(Redact(trace_redactor_, &context_));
158 
159   auto original_trace_str = LoadOriginal();
160   ASSERT_OK(original_trace_str);
161 
162   auto redacted_trace_str = LoadRedacted();
163   ASSERT_OK(redacted_trace_str);
164 
165   auto original_tids = GetTids(*original_trace_str);
166   auto redacted_tids = GetTids(*redacted_trace_str);
167 
168   // There are 2761 unique tids across all process trees:
169   //    grep 'threads {' -A 1  src.pftrace.txt | grep 'tid: ' | grep -Po "\d+" |
170   //    sort | uniq | wc -l
171   //
172   // There are 2896 unique tids/tgis across all process trees:
173   //    grep 'threads {' -A 2  src.pftrace.txt | grep -P '(tid|tgid): ' | grep
174   //    -Po '\d+' | sort | uniq | wc -l
175   //
176   // The original tid count does NOT align with what trace processor returns.
177   // Trace processor reports 3666 threads. The assumption is trace processor is
178   // fulling thread information from additional.
179   //
180   // The redacted tid+tgid count does NOT align with what trace processor
181   // returns. Trace processor reports 199 tids where are there are only 63 tids
182   // found in process tree. This suggests that trace processor is pulling tid
183   // data from other locations.
184   ASSERT_EQ(original_tids.size(), 2896u);
185   ASSERT_EQ(redacted_tids.size(), 64u);
186 }
187 
TEST_F(RedactProcessTreesIntegrationTest,AddSynthProcess)188 TEST_F(RedactProcessTreesIntegrationTest, AddSynthProcess) {
189   // Append another primitive that won't filter, but will add new threads. This
190   // will be compatible with the other instanced in SetUp().
191   auto* process_tree = trace_redactor_.emplace_transform<RedactProcessTrees>();
192   process_tree->emplace_modifier<ProcessTreeCreateSynthThreads>();
193   process_tree->emplace_filter<AllowAll>();
194 
195   ASSERT_OK(Redact(trace_redactor_, &context_));
196 
197   auto redacted_trace_str = LoadRedacted();
198   ASSERT_OK(redacted_trace_str);
199 
200   auto redacted_pids = GetPids(*redacted_trace_str);
201 
202   const auto* synth_process = context_.synthetic_process.get();
203   ASSERT_TRUE(synth_process);
204 
205   ASSERT_NE(std::find(redacted_pids.begin(), redacted_pids.end(),
206                       synth_process->tgid()),
207             redacted_pids.end());
208 }
209 
TEST_F(RedactProcessTreesIntegrationTest,AddSynthThreads)210 TEST_F(RedactProcessTreesIntegrationTest, AddSynthThreads) {
211   // Append another primitive that won't filter, but will add new threads. This
212   // will be compatible with the other instanced in SetUp().
213   auto* process_tree = trace_redactor_.emplace_transform<RedactProcessTrees>();
214   process_tree->emplace_modifier<ProcessTreeCreateSynthThreads>();
215   process_tree->emplace_filter<AllowAll>();
216 
217   ASSERT_OK(Redact(trace_redactor_, &context_));
218 
219   const auto* synth_process = context_.synthetic_process.get();
220   ASSERT_TRUE(synth_process);
221 
222   ASSERT_FALSE(synth_process->tids().empty());
223 
224   auto original_trace_str = LoadOriginal();
225   ASSERT_OK(original_trace_str);
226 
227   auto original_tids = GetTids(*original_trace_str);
228 
229   // The synth threads should not be found in the original trace.
230   for (auto tid : synth_process->tids()) {
231     ASSERT_FALSE(original_tids.count(tid));
232   }
233 
234   auto redacted_trace_str = LoadRedacted();
235   ASSERT_OK(redacted_trace_str);
236 
237   auto redacted_tids = GetTids(*redacted_trace_str);
238 
239   // The synth threads should be found in the redacted trace.
240   for (auto tid : synth_process->tids()) {
241     ASSERT_TRUE(redacted_tids.count(tid));
242   }
243 }
244 
245 }  // namespace perfetto::trace_redaction
246