xref: /aosp_15_r20/external/perfetto/src/trace_redaction/trace_processor_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 "perfetto/trace_processor/trace_processor.h"
18 #include "src/base/test/status_matchers.h"
19 #include "src/trace_redaction/trace_redaction_integration_fixture.h"
20 #include "test/gtest_and_gmock.h"
21 
22 namespace perfetto::trace_redaction {
23 namespace {
24 constexpr auto kTrace = "test/data/trace-redaction-api-capture.pftrace";
25 
26 constexpr auto kPackageName = "com.prefabulated.touchlatency";
27 constexpr auto kPackageUid = 10020;
28 }  // namespace
29 
30 class AfterRedactionIntegrationTest
31     : public testing::Test,
32       protected TraceRedactionIntegrationFixure {
33  protected:
SetUp()34   void SetUp() override {
35     SetSourceTrace(kTrace);
36 
37     trace_processor::Config tp_config;
38     trace_processor_ =
39         trace_processor::TraceProcessor::CreateInstance(tp_config);
40 
41     TraceRedactor::Config tr_config;
42     auto trace_redactor = TraceRedactor::CreateInstance(tr_config);
43 
44     Context context;
45     context.package_name = kPackageName;
46 
47     ASSERT_OK(Redact(*trace_redactor, &context));
48 
49     auto raw = LoadRedacted();
50     ASSERT_OK(raw);
51 
52     auto read_buffer = std::make_unique<uint8_t[]>(raw->size());
53     memcpy(read_buffer.get(), raw->data(), raw->size());
54 
55     ASSERT_OK(trace_processor_->Parse(std::move(read_buffer), raw->size()));
56     ASSERT_OK(trace_processor_->NotifyEndOfFile());
57   }
58 
59   std::unique_ptr<trace_processor::TraceProcessor> trace_processor_;
60 };
61 
62 // After redaction, the only package remaining in the package list should be the
63 // target package.
TEST_F(AfterRedactionIntegrationTest,FindsCorrectUid)64 TEST_F(AfterRedactionIntegrationTest, FindsCorrectUid) {
65   auto rows = trace_processor_->ExecuteQuery(
66       "SELECT uid FROM package_list ORDER BY uid");
67 
68   ASSERT_TRUE(rows.Next());
69   ASSERT_EQ(rows.Get(0).AsLong(), kPackageUid);
70 
71   ASSERT_FALSE(rows.Next());
72   ASSERT_OK(rows.Status());
73 }
74 
TEST_F(AfterRedactionIntegrationTest,CreatesThreadForEachCPU)75 TEST_F(AfterRedactionIntegrationTest, CreatesThreadForEachCPU) {
76   // There's a main thread, but it is not used (it's just there to create a
77   // thread group). Exclude it so we get N threads instead of N+1.
78 
79   // This should yield a collection of size 1.
80   std::string synth_process =
81       "SELECT upid FROM process WHERE name='Other-Processes'";
82 
83   auto threads = trace_processor_->ExecuteQuery(
84       "SELECT COUNT(tid) FROM thread WHERE upid IN (" + synth_process +
85       ") AND NOT is_main_thread");
86 
87   auto cpus = trace_processor_->ExecuteQuery(
88       "SELECT COUNT(DISTINCT cpu) FROM cpu_counter_track");
89 
90   ASSERT_TRUE(threads.Next());
91   ASSERT_TRUE(cpus.Next());
92 
93   auto thread_count = threads.Get(0).AsLong();
94   ASSERT_NE(thread_count, 0);
95 
96   auto cpu_count = threads.Get(0).AsLong();
97   ASSERT_NE(cpu_count, 0);
98 
99   ASSERT_EQ(thread_count, cpu_count);
100 
101   ASSERT_FALSE(threads.Next());
102   ASSERT_FALSE(cpus.Next());
103 
104   ASSERT_OK(threads.Status());
105   ASSERT_OK(cpus.Status());
106 }
107 
TEST_F(AfterRedactionIntegrationTest,ReducesProcesses)108 TEST_F(AfterRedactionIntegrationTest, ReducesProcesses) {
109   auto processes = trace_processor_->ExecuteQuery(
110       "SELECT pid, name FROM process ORDER BY pid");
111 
112   // PID      NAME
113   // ======================================================
114   // 0        NULL
115   // 1        NULL
116   // 863      NULL  <--- Zygote
117   // 4524     com.prefabulated.touchlatency
118   // 4194305  Other-Processes
119 
120   ASSERT_TRUE(processes.Next());
121   ASSERT_EQ(processes.Get(0).AsLong(), 0);
122   ASSERT_TRUE(processes.Get(1).is_null());
123 
124   ASSERT_TRUE(processes.Next());
125   ASSERT_EQ(processes.Get(0).AsLong(), 1);
126   ASSERT_TRUE(processes.Get(1).is_null());
127 
128   // Zygote
129   ASSERT_TRUE(processes.Next());
130   ASSERT_EQ(processes.Get(0).AsLong(), 863);
131   ASSERT_TRUE(processes.Get(1).is_null());
132 
133   ASSERT_TRUE(processes.Next());
134   ASSERT_EQ(processes.Get(0).AsLong(), 4524);
135   ASSERT_STREQ(processes.Get(1).AsString(), kPackageName);
136 
137   ASSERT_TRUE(processes.Next());
138   ASSERT_EQ(processes.Get(0).AsLong(), 4194305);
139   ASSERT_STREQ(processes.Get(1).AsString(), "Other-Processes");
140 }
141 
142 // Tests comparing the trace before and after redaction.
143 class BeforeAndAfterAfterIntegrationTest
144     : public testing::Test,
145       protected TraceRedactionIntegrationFixure {
146  protected:
SetUp()147   void SetUp() override {
148     SetSourceTrace(kTrace);
149 
150     trace_processor::Config config;
151 
152     auto raw_before = LoadOriginal();
153     ASSERT_OK(raw_before);
154     trace_processor_before_ = CreateTraceProcessor(raw_before.value());
155 
156     TraceRedactor::Config tr_config;
157     auto trace_redactor = TraceRedactor::CreateInstance(tr_config);
158 
159     Context context;
160     context.package_name = kPackageName;
161 
162     Redact(*trace_redactor, &context);
163 
164     auto raw_after = LoadRedacted();
165     ASSERT_OK(raw_after);
166     trace_processor_after_ = CreateTraceProcessor(raw_after.value());
167   }
168 
CreateTraceProcessor(std::string_view raw)169   static std::unique_ptr<trace_processor::TraceProcessor> CreateTraceProcessor(
170       std::string_view raw) {
171     auto read_buffer = std::make_unique<uint8_t[]>(raw.size());
172     memcpy(read_buffer.get(), raw.data(), raw.size());
173 
174     trace_processor::Config config;
175     auto trace_processor =
176         trace_processor::TraceProcessor::CreateInstance(config);
177 
178     auto parsed = trace_processor->Parse(std::move(read_buffer), raw.size());
179 
180     if (!parsed.ok()) {
181       return nullptr;
182     }
183     if (auto status = trace_processor->NotifyEndOfFile(); !status.ok()) {
184       return nullptr;
185     }
186     return trace_processor;
187   }
188 
189   std::unique_ptr<trace_processor::TraceProcessor> trace_processor_before_;
190   std::unique_ptr<trace_processor::TraceProcessor> trace_processor_after_;
191 };
192 
TEST_F(BeforeAndAfterAfterIntegrationTest,KeepsAllTargetPackageThreads)193 TEST_F(BeforeAndAfterAfterIntegrationTest, KeepsAllTargetPackageThreads) {
194   std::string package_name = kPackageName;
195 
196   // This should yield a collection of one.
197   std::string packages =
198       "SELECT uid FROM package_list WHERE package_name='" + package_name + "'";
199 
200   // This should yield a collection of one.
201   std::string processes =
202       "SELECT upid FROM process WHERE uid IN (" + packages + ")";
203 
204   // This should yield a collect of N where N is some non-zero integer.
205   const std::string tid_query =
206       "SELECT tid FROM thread WHERE upid IN (" + processes + ") ORDER BY tid";
207 
208   auto it_before = trace_processor_before_->ExecuteQuery(tid_query);
209   auto it_after = trace_processor_after_->ExecuteQuery(tid_query);
210 
211   ASSERT_TRUE(it_before.Next());
212 
213   do {
214     ASSERT_TRUE(it_after.Next());
215     ASSERT_EQ(it_before.Get(0).AsLong(), it_after.Get(0).AsLong());
216   } while (it_before.Next());
217 
218   ASSERT_FALSE(it_after.Next());
219 
220   ASSERT_OK(it_before.Status());
221   ASSERT_OK(it_after.Status());
222 }
223 
224 // There are two Zygotes on Android ('zygote', 'zygote64'). Modern device should
225 // have both, so we assume both are present in the unredacted trace. During
226 // redaction, all zygote information will be lost during the merge stage.
227 // However, since the target process references the zygote (ppid) a "ghost"
228 // process will appear in the process table.
229 class RedactedZygoteIntegrationTest
230     : public BeforeAndAfterAfterIntegrationTest {
231  protected:
SetUp()232   void SetUp() {
233     BeforeAndAfterAfterIntegrationTest::SetUp();
234 
235     auto it_before = trace_processor_before_->ExecuteQuery(
236         "SELECT pid FROM process WHERE name IN ('zygote', 'zygote64')");
237 
238     ASSERT_TRUE(it_before.Next());
239     zygotes_[0] = it_before.Get(0).AsLong();
240 
241     ASSERT_TRUE(it_before.Next());
242     zygotes_[1] = it_before.Get(0).AsLong();
243 
244     ASSERT_FALSE(it_before.Next());
245     ASSERT_OK(it_before.Status());
246   }
247 
248   // Creates a SQL statement that can be used AFTER a "WHERE" clause to test if
249   // the process is a zygote processes. The caller is responsible for the
250   // prefix (e.g. WHERE, AND, OR, etc.).
IsZygote() const251   std::string IsZygote() const {
252     auto p = std::to_string(zygotes_.at(0));
253     auto q = std::to_string(zygotes_.at(1));
254 
255     return "pid=" + p + " OR pid=" + q;
256   }
257 
258   std::array<int64_t, 2> zygotes_;
259 };
260 
TEST_F(RedactedZygoteIntegrationTest,KeepsOneZygote)261 TEST_F(RedactedZygoteIntegrationTest, KeepsOneZygote) {
262   auto count = trace_processor_after_->ExecuteQuery(
263       "SELECT COUNT(pid) FROM process WHERE " + IsZygote());
264 
265   ASSERT_TRUE(count.Next());
266   ASSERT_EQ(count.Get(0).AsLong(), 1);
267   ASSERT_FALSE(count.Next());
268   ASSERT_OK(count.Status());
269 }
270 
TEST_F(RedactedZygoteIntegrationTest,RemovesName)271 TEST_F(RedactedZygoteIntegrationTest, RemovesName) {
272   auto names = trace_processor_after_->ExecuteQuery(
273       "SELECT name FROM process WHERE " + IsZygote());
274 
275   ASSERT_TRUE(names.Next());
276   ASSERT_TRUE(names.Get(0).is_null());
277   ASSERT_FALSE(names.Next());
278   ASSERT_OK(names.Status());
279 }
280 
281 // After redaction, the only application left should be the target package.
282 // While an application can have multiple processes, there should one top level
283 // process that was forked by the zygote.
284 //
285 // WARNING: This test relies on an assumption: there is only be one instance of
286 // the application running. We know this assumption to be faulty as multiple
287 // profiles allow for multiple instances of the same package to be running.
288 // In redaction, we treat them all as a single instance. The test trace does not
289 // use multiple profiles, so this assumption hold for this trace.
TEST_F(RedactedZygoteIntegrationTest,OnlyReferencedByTargetPackage)290 TEST_F(RedactedZygoteIntegrationTest, OnlyReferencedByTargetPackage) {
291   // To avoid collisions, trace processor quickly moves away from volatile
292   // values like tid and pid to use globally stable values like upid and utid.
293   // Because of this, we can't check if a process's parent is the zygote, we
294   // need to convert the pid to a upid first.
295   auto upids = "SELECT upid FROM process WHERE " + IsZygote();
296 
297   auto ppids = trace_processor_after_->ExecuteQuery(
298       "SELECT COUNT(pid) FROM process WHERE parent_upid IN (" + upids + ")");
299 
300   ASSERT_TRUE(ppids.Next());
301   ASSERT_EQ(ppids.Get(0).AsLong(), 1);
302   ASSERT_FALSE(ppids.Next());
303   ASSERT_OK(ppids.Status());
304 }
305 
306 }  // namespace perfetto::trace_redaction
307