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 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
184 trace_processor->NotifyEndOfFile();
185 return trace_processor;
186 }
187
188 std::unique_ptr<trace_processor::TraceProcessor> trace_processor_before_;
189 std::unique_ptr<trace_processor::TraceProcessor> trace_processor_after_;
190 };
191
TEST_F(BeforeAndAfterAfterIntegrationTest,KeepsAllTargetPackageThreads)192 TEST_F(BeforeAndAfterAfterIntegrationTest, KeepsAllTargetPackageThreads) {
193 std::string package_name = kPackageName;
194
195 // This should yield a collection of one.
196 std::string packages =
197 "SELECT uid FROM package_list WHERE package_name='" + package_name + "'";
198
199 // This should yield a collection of one.
200 std::string processes =
201 "SELECT upid FROM process WHERE uid IN (" + packages + ")";
202
203 // This should yield a collect of N where N is some non-zero integer.
204 const std::string tid_query =
205 "SELECT tid FROM thread WHERE upid IN (" + processes + ") ORDER BY tid";
206
207 auto it_before = trace_processor_before_->ExecuteQuery(tid_query);
208 auto it_after = trace_processor_after_->ExecuteQuery(tid_query);
209
210 ASSERT_TRUE(it_before.Next());
211
212 do {
213 ASSERT_TRUE(it_after.Next());
214 ASSERT_EQ(it_before.Get(0).AsLong(), it_after.Get(0).AsLong());
215 } while (it_before.Next());
216
217 ASSERT_FALSE(it_after.Next());
218
219 ASSERT_OK(it_before.Status());
220 ASSERT_OK(it_after.Status());
221 }
222
223 // There are two Zygotes on Android ('zygote', 'zygote64'). Modern device should
224 // have both, so we assume both are present in the unredacted trace. During
225 // redaction, all zygote information will be lost during the merge stage.
226 // However, since the target process references the zygote (ppid) a "ghost"
227 // process will appear in the process table.
228 class RedactedZygoteIntegrationTest
229 : public BeforeAndAfterAfterIntegrationTest {
230 protected:
SetUp()231 void SetUp() {
232 BeforeAndAfterAfterIntegrationTest::SetUp();
233
234 auto it_before = trace_processor_before_->ExecuteQuery(
235 "SELECT pid FROM process WHERE name IN ('zygote', 'zygote64')");
236
237 ASSERT_TRUE(it_before.Next());
238 zygotes_[0] = it_before.Get(0).AsLong();
239
240 ASSERT_TRUE(it_before.Next());
241 zygotes_[1] = it_before.Get(0).AsLong();
242
243 ASSERT_FALSE(it_before.Next());
244 ASSERT_OK(it_before.Status());
245 }
246
247 // Creates a SQL statement that can be used AFTER a "WHERE" clause to test if
248 // the process is a zygote processes. The caller is responsible for the
249 // prefix (e.g. WHERE, AND, OR, etc.).
IsZygote() const250 std::string IsZygote() const {
251 auto p = std::to_string(zygotes_.at(0));
252 auto q = std::to_string(zygotes_.at(1));
253
254 return "pid=" + p + " OR pid=" + q;
255 }
256
257 std::array<int64_t, 2> zygotes_;
258 };
259
TEST_F(RedactedZygoteIntegrationTest,KeepsOneZygote)260 TEST_F(RedactedZygoteIntegrationTest, KeepsOneZygote) {
261 auto count = trace_processor_after_->ExecuteQuery(
262 "SELECT COUNT(pid) FROM process WHERE " + IsZygote());
263
264 ASSERT_TRUE(count.Next());
265 ASSERT_EQ(count.Get(0).AsLong(), 1);
266 ASSERT_FALSE(count.Next());
267 ASSERT_OK(count.Status());
268 }
269
TEST_F(RedactedZygoteIntegrationTest,RemovesName)270 TEST_F(RedactedZygoteIntegrationTest, RemovesName) {
271 auto names = trace_processor_after_->ExecuteQuery(
272 "SELECT name FROM process WHERE " + IsZygote());
273
274 ASSERT_TRUE(names.Next());
275 ASSERT_TRUE(names.Get(0).is_null());
276 ASSERT_FALSE(names.Next());
277 ASSERT_OK(names.Status());
278 }
279
280 // After redaction, the only application left should be the target package.
281 // While an application can have multiple processes, there should one top level
282 // process that was forked by the zygote.
283 //
284 // WARNING: This test relies on an assumption: there is only be one instance of
285 // the application running. We know this assumption to be faulty as multiple
286 // profiles allow for multiple instances of the same package to be running.
287 // In redaction, we treat them all as a single instance. The test trace does not
288 // use multiple profiles, so this assumption hold for this trace.
TEST_F(RedactedZygoteIntegrationTest,OnlyReferencedByTargetPackage)289 TEST_F(RedactedZygoteIntegrationTest, OnlyReferencedByTargetPackage) {
290 // To avoid collisions, trace processor quickly moves away from volatile
291 // values like tid and pid to use globally stable values like upid and utid.
292 // Because of this, we can't check if a process's parent is the zygote, we
293 // need to convert the pid to a upid first.
294 auto upids = "SELECT upid FROM process WHERE " + IsZygote();
295
296 auto ppids = trace_processor_after_->ExecuteQuery(
297 "SELECT COUNT(pid) FROM process WHERE parent_upid IN (" + upids + ")");
298
299 ASSERT_TRUE(ppids.Next());
300 ASSERT_EQ(ppids.Get(0).AsLong(), 1);
301 ASSERT_FALSE(ppids.Next());
302 ASSERT_OK(ppids.Status());
303 }
304
305 } // namespace perfetto::trace_redaction
306