1 /*
2 * Copyright (C) 2021 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 "RecordFilter.h"
18
19 #include <gtest/gtest.h>
20
21 #if defined(__linux__)
22 #include <unistd.h>
23 #endif // defined(__linux__)
24
25 #include <memory>
26
27 #include "event_attr.h"
28 #include "event_type.h"
29 #include "record.h"
30
31 using namespace simpleperf;
32
33 // @CddTest = 6.1/C-0-2
34 class RecordFilterTest : public ::testing::Test {
35 public:
RecordFilterTest()36 RecordFilterTest() : filter(thread_tree) {}
37
38 protected:
SetUp()39 void SetUp() override {
40 const EventType* event_type = FindEventTypeByName("cpu-clock");
41 attr = CreateDefaultPerfEventAttr(*event_type);
42 record.reset(new SampleRecord(attr, 0, 0, 0, 0, 0, 0, 0, {}, {}, {}, 0));
43 }
44
GetRecord(uint32_t pid,uint32_t tid)45 SampleRecord& GetRecord(uint32_t pid, uint32_t tid) {
46 record->tid_data.pid = pid;
47 record->tid_data.tid = tid;
48 return *record;
49 }
50
SetFilterData(const std::string & data)51 bool SetFilterData(const std::string& data) {
52 TemporaryFile tmpfile;
53 return android::base::WriteStringToFd(data, tmpfile.fd) && filter.SetFilterFile(tmpfile.path);
54 }
55
56 ThreadTree thread_tree;
57 perf_event_attr attr;
58 RecordFilter filter;
59 std::unique_ptr<SampleRecord> record;
60 };
61
62 // @CddTest = 6.1/C-0-2
TEST_F(RecordFilterTest,no_filter)63 TEST_F(RecordFilterTest, no_filter) {
64 ASSERT_TRUE(filter.Check(GetRecord(0, 0)));
65 }
66
67 // @CddTest = 6.1/C-0-2
TEST_F(RecordFilterTest,cpu)68 TEST_F(RecordFilterTest, cpu) {
69 filter.AddCpus({1});
70 SampleRecord& r = GetRecord(0, 0);
71 r.cpu_data.cpu = 1;
72 ASSERT_TRUE(filter.Check(r));
73 r.cpu_data.cpu = 2;
74 ASSERT_FALSE(filter.Check(r));
75 }
76
77 // @CddTest = 6.1/C-0-2
TEST_F(RecordFilterTest,exclude_pid)78 TEST_F(RecordFilterTest, exclude_pid) {
79 filter.AddPids({1}, true);
80 ASSERT_FALSE(filter.Check(GetRecord(1, 1)));
81 ASSERT_TRUE(filter.Check(GetRecord(2, 2)));
82 }
83
84 // @CddTest = 6.1/C-0-2
TEST_F(RecordFilterTest,exclude_tid)85 TEST_F(RecordFilterTest, exclude_tid) {
86 filter.AddTids({1}, true);
87 ASSERT_FALSE(filter.Check(GetRecord(1, 1)));
88 ASSERT_TRUE(filter.Check(GetRecord(1, 2)));
89 }
90
91 // @CddTest = 6.1/C-0-2
TEST_F(RecordFilterTest,exclude_process_name_regex)92 TEST_F(RecordFilterTest, exclude_process_name_regex) {
93 ASSERT_TRUE(filter.AddProcessNameRegex("processA", true));
94 thread_tree.SetThreadName(1, 1, "processA1");
95 thread_tree.SetThreadName(2, 2, "processB1");
96 ASSERT_FALSE(filter.Check(GetRecord(1, 1)));
97 ASSERT_TRUE(filter.Check(GetRecord(2, 2)));
98 }
99
100 // @CddTest = 6.1/C-0-2
TEST_F(RecordFilterTest,exclude_thread_name_regex)101 TEST_F(RecordFilterTest, exclude_thread_name_regex) {
102 ASSERT_TRUE(filter.AddThreadNameRegex("threadA", true));
103 thread_tree.SetThreadName(1, 1, "processA_threadA");
104 thread_tree.SetThreadName(1, 2, "processA_threadB");
105 ASSERT_FALSE(filter.Check(GetRecord(1, 1)));
106 ASSERT_TRUE(filter.Check(GetRecord(1, 2)));
107 }
108
109 #if defined(__linux__)
110 // @CddTest = 6.1/C-0-2
TEST_F(RecordFilterTest,exclude_uid)111 TEST_F(RecordFilterTest, exclude_uid) {
112 pid_t pid = getpid();
113 std::optional<uint32_t> uid = GetProcessUid(pid);
114 ASSERT_TRUE(uid.has_value());
115 filter.AddUids({uid.value()}, true);
116 ASSERT_FALSE(filter.Check(GetRecord(pid, pid)));
117 // The check fails if a process can't find its corresponding uid.
118 uint32_t pid_not_exist = UINT32_MAX;
119 ASSERT_FALSE(filter.Check(GetRecord(pid_not_exist, pid_not_exist)));
120 }
121 #endif // defined(__linux__)
122
123 // @CddTest = 6.1/C-0-2
TEST_F(RecordFilterTest,include_pid)124 TEST_F(RecordFilterTest, include_pid) {
125 filter.AddPids({1}, false);
126 ASSERT_TRUE(filter.Check(GetRecord(1, 1)));
127 ASSERT_FALSE(filter.Check(GetRecord(2, 2)));
128 }
129
130 // @CddTest = 6.1/C-0-2
TEST_F(RecordFilterTest,include_tid)131 TEST_F(RecordFilterTest, include_tid) {
132 filter.AddTids({1}, false);
133 ASSERT_TRUE(filter.Check(GetRecord(1, 1)));
134 ASSERT_FALSE(filter.Check(GetRecord(1, 2)));
135 }
136
137 // @CddTest = 6.1/C-0-2
TEST_F(RecordFilterTest,include_process_name_regex)138 TEST_F(RecordFilterTest, include_process_name_regex) {
139 ASSERT_TRUE(filter.AddProcessNameRegex("processA", false));
140 thread_tree.SetThreadName(1, 1, "processA1");
141 thread_tree.SetThreadName(2, 2, "processB1");
142 ASSERT_TRUE(filter.Check(GetRecord(1, 1)));
143 ASSERT_FALSE(filter.Check(GetRecord(2, 2)));
144 }
145
146 // @CddTest = 6.1/C-0-2
TEST_F(RecordFilterTest,include_thread_name_regex)147 TEST_F(RecordFilterTest, include_thread_name_regex) {
148 ASSERT_TRUE(filter.AddThreadNameRegex("threadA", false));
149 thread_tree.SetThreadName(1, 1, "processA_threadA");
150 thread_tree.SetThreadName(1, 2, "processA_threadB");
151 ASSERT_TRUE(filter.Check(GetRecord(1, 1)));
152 ASSERT_FALSE(filter.Check(GetRecord(1, 2)));
153 }
154
155 #if defined(__linux__)
156 // @CddTest = 6.1/C-0-2
TEST_F(RecordFilterTest,include_uid)157 TEST_F(RecordFilterTest, include_uid) {
158 pid_t pid = getpid();
159 std::optional<uint32_t> uid = GetProcessUid(pid);
160 ASSERT_TRUE(uid.has_value());
161 filter.AddUids({uid.value()}, false);
162 ASSERT_TRUE(filter.Check(GetRecord(pid, pid)));
163 uint32_t pid_not_exist = UINT32_MAX;
164 ASSERT_FALSE(filter.Check(GetRecord(pid_not_exist, pid_not_exist)));
165 }
166 #endif // defined(__linux__)
167
168 // @CddTest = 6.1/C-0-2
TEST_F(RecordFilterTest,global_time_filter)169 TEST_F(RecordFilterTest, global_time_filter) {
170 ASSERT_TRUE(
171 SetFilterData("GLOBAL_BEGIN 1000\n"
172 "GLOBAL_END 2000\n"
173 "GLOBAL_BEGIN 3000\n"
174 "GLOBAL_END 4000"));
175 SampleRecord& r = GetRecord(1, 1);
176 r.time_data.time = 0;
177 ASSERT_FALSE(filter.Check(r));
178 r.time_data.time = 999;
179 ASSERT_FALSE(filter.Check(r));
180 r.time_data.time = 1000;
181 ASSERT_TRUE(filter.Check(r));
182 r.time_data.time = 1001;
183 ASSERT_TRUE(filter.Check(r));
184 r.time_data.time = 1999;
185 ASSERT_TRUE(filter.Check(r));
186 r.time_data.time = 2000;
187 ASSERT_FALSE(filter.Check(r));
188 r.time_data.time = 2001;
189 ASSERT_FALSE(filter.Check(r));
190 r.time_data.time = 3000;
191 ASSERT_TRUE(filter.Check(r));
192 r.time_data.time = 4000;
193 ASSERT_FALSE(filter.Check(r));
194 }
195
196 // @CddTest = 6.1/C-0-2
TEST_F(RecordFilterTest,process_time_filter)197 TEST_F(RecordFilterTest, process_time_filter) {
198 ASSERT_TRUE(
199 SetFilterData("PROCESS_BEGIN 1 1000\n"
200 "PROCESS_END 1 2000"));
201 SampleRecord& r = GetRecord(1, 1);
202 r.time_data.time = 0;
203 ASSERT_FALSE(filter.Check(r));
204 r.time_data.time = 999;
205 ASSERT_FALSE(filter.Check(r));
206 r.time_data.time = 1000;
207 ASSERT_TRUE(filter.Check(r));
208 r.time_data.time = 1001;
209 ASSERT_TRUE(filter.Check(r));
210 r.time_data.time = 1999;
211 ASSERT_TRUE(filter.Check(r));
212 r.time_data.time = 2000;
213 ASSERT_FALSE(filter.Check(r));
214 // When process time filters are used, not mentioned processes should be filtered.
215 r.tid_data.pid = 2;
216 r.time_data.time = 1000;
217 ASSERT_FALSE(filter.Check(r));
218 }
219
220 // @CddTest = 6.1/C-0-2
TEST_F(RecordFilterTest,thread_time_filter)221 TEST_F(RecordFilterTest, thread_time_filter) {
222 ASSERT_TRUE(
223 SetFilterData("THREAD_BEGIN 1 1000\n"
224 "THREAD_END 1 2000"));
225 SampleRecord& r = GetRecord(1, 1);
226 r.time_data.time = 0;
227 ASSERT_FALSE(filter.Check(r));
228 r.time_data.time = 999;
229 ASSERT_FALSE(filter.Check(r));
230 r.time_data.time = 1000;
231 ASSERT_TRUE(filter.Check(r));
232 r.time_data.time = 1001;
233 ASSERT_TRUE(filter.Check(r));
234 r.time_data.time = 1999;
235 ASSERT_TRUE(filter.Check(r));
236 r.time_data.time = 2000;
237 ASSERT_FALSE(filter.Check(r));
238 // When thread time filters are used, not mentioned threads should be filtered.
239 r.tid_data.tid = 2;
240 r.time_data.time = 1000;
241 ASSERT_FALSE(filter.Check(r));
242 }
243
244 // @CddTest = 6.1/C-0-2
TEST_F(RecordFilterTest,clock_in_time_filter)245 TEST_F(RecordFilterTest, clock_in_time_filter) {
246 // If there is no filter data, any clock is fine.
247 ASSERT_TRUE(filter.CheckClock("monotonic"));
248 ASSERT_TRUE(filter.CheckClock("perf"));
249 // If there is no clock command, monotonic clock is used.
250 ASSERT_TRUE(SetFilterData(""));
251 ASSERT_TRUE(filter.CheckClock("monotonic"));
252 ASSERT_FALSE(filter.CheckClock("perf"));
253 // If there is a clock command, use that clock.
254 ASSERT_TRUE(SetFilterData("CLOCK realtime"));
255 ASSERT_TRUE(filter.CheckClock("realtime"));
256 ASSERT_FALSE(filter.CheckClock("monotonic"));
257 }
258
259 // @CddTest = 6.1/C-0-2
TEST_F(RecordFilterTest,error_in_time_filter)260 TEST_F(RecordFilterTest, error_in_time_filter) {
261 // no timestamp error
262 ASSERT_FALSE(SetFilterData("GLOBAL_BEGIN"));
263 // time range error
264 ASSERT_FALSE(
265 SetFilterData("GLOBAL_BEGIN 1000\n"
266 "GLOBAL_END 999"));
267 // time range error
268 ASSERT_FALSE(
269 SetFilterData("GLOBAL_BEGIN 1000\n"
270 "GLOBAL_END 1000"));
271 // no timestamp error
272 ASSERT_FALSE(SetFilterData("PROCESS_BEGIN 1"));
273 // time range error
274 ASSERT_FALSE(
275 SetFilterData("PROCESS_BEGIN 1 1000\n"
276 "PROCESS_END 1 999"));
277 // no timestamp error
278 ASSERT_FALSE(SetFilterData("THREAD_BEGIN 1"));
279 // time range error
280 ASSERT_FALSE(
281 SetFilterData("THREAD_BEGIN 1 1000\n"
282 "THREAD_END 1 999"));
283 }
284
285 namespace {
286
287 class ParseRecordFilterCommand : public Command {
288 public:
ParseRecordFilterCommand(RecordFilter & filter)289 ParseRecordFilterCommand(RecordFilter& filter) : Command("", "", ""), filter_(filter) {}
290
Run(const std::vector<std::string> & args)291 bool Run(const std::vector<std::string>& args) override {
292 const auto option_formats = GetRecordFilterOptionFormats(for_recording);
293 OptionValueMap options;
294 std::vector<std::pair<OptionName, OptionValue>> ordered_options;
295
296 if (!PreprocessOptions(args, option_formats, &options, &ordered_options, nullptr)) {
297 return false;
298 }
299 filter_.Clear();
300 return filter_.ParseOptions(options);
301 }
302
303 bool for_recording = true;
304
305 private:
306 RecordFilter& filter_;
307 };
308
309 } // namespace
310
311 // @CddTest = 6.1/C-0-2
TEST_F(RecordFilterTest,parse_options)312 TEST_F(RecordFilterTest, parse_options) {
313 ParseRecordFilterCommand filter_cmd(filter);
314
315 for (bool exclude : {true, false}) {
316 std::string prefix = exclude ? "--exclude-" : "--include-";
317
318 ASSERT_TRUE(filter_cmd.Run({prefix + "pid", "1,2", prefix + "pid", "3"}));
319 ASSERT_EQ(filter.Check(GetRecord(1, 1)), !exclude);
320 ASSERT_EQ(filter.Check(GetRecord(2, 2)), !exclude);
321 ASSERT_EQ(filter.Check(GetRecord(3, 3)), !exclude);
322
323 ASSERT_TRUE(filter_cmd.Run({prefix + "tid", "1,2", prefix + "tid", "3"}));
324 ASSERT_EQ(filter.Check(GetRecord(1, 1)), !exclude);
325 ASSERT_EQ(filter.Check(GetRecord(1, 2)), !exclude);
326 ASSERT_EQ(filter.Check(GetRecord(1, 3)), !exclude);
327
328 ASSERT_TRUE(
329 filter_cmd.Run({prefix + "process-name", "processA", prefix + "process-name", "processB"}));
330 thread_tree.SetThreadName(1, 1, "processA");
331 thread_tree.SetThreadName(2, 2, "processB");
332 ASSERT_EQ(filter.Check(GetRecord(1, 1)), !exclude);
333 ASSERT_EQ(filter.Check(GetRecord(2, 2)), !exclude);
334
335 ASSERT_TRUE(
336 filter_cmd.Run({prefix + "thread-name", "threadA", prefix + "thread-name", "threadB"}));
337 thread_tree.SetThreadName(1, 11, "threadA");
338 thread_tree.SetThreadName(1, 12, "threadB");
339 ASSERT_EQ(filter.Check(GetRecord(1, 11)), !exclude);
340 ASSERT_EQ(filter.Check(GetRecord(2, 12)), !exclude);
341
342 ASSERT_TRUE(filter_cmd.Run({prefix + "uid", "1,2", prefix + "uid", "3"}));
343 #if defined(__linux__)
344 pid_t pid = getpid();
345 uid_t uid = getuid();
346 ASSERT_TRUE(filter_cmd.Run({prefix + "uid", std::to_string(uid)}));
347 ASSERT_EQ(filter.Check(GetRecord(pid, pid)), !exclude);
348 #endif // defined(__linux__)
349 }
350
351 filter_cmd.for_recording = false;
352 ASSERT_TRUE(filter_cmd.Run({"--cpu", "0", "--cpu", "1-3"}));
353 SampleRecord& r = GetRecord(0, 0);
354 r.cpu_data.cpu = 0;
355 ASSERT_TRUE(filter.Check(r));
356 r.cpu_data.cpu = 2;
357 ASSERT_TRUE(filter.Check(r));
358 r.cpu_data.cpu = 4;
359 ASSERT_FALSE(filter.Check(r));
360 }
361