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