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