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