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
46 ThreadTree thread_tree;
47 perf_event_attr attr;
48 RecordFilter filter;
49 std::unique_ptr<SampleRecord> record;
50 };
51
TEST_F(RecordFilterTest,no_filter)52 TEST_F(RecordFilterTest, no_filter) {
53 ASSERT_TRUE(filter.Check(GetRecord(0, 0)));
54 }
55
TEST_F(RecordFilterTest,exclude_pid)56 TEST_F(RecordFilterTest, exclude_pid) {
57 filter.AddPids({1}, true);
58 ASSERT_FALSE(filter.Check(GetRecord(1, 1)));
59 ASSERT_TRUE(filter.Check(GetRecord(2, 2)));
60 }
61
TEST_F(RecordFilterTest,exclude_tid)62 TEST_F(RecordFilterTest, exclude_tid) {
63 filter.AddTids({1}, true);
64 ASSERT_FALSE(filter.Check(GetRecord(1, 1)));
65 ASSERT_TRUE(filter.Check(GetRecord(1, 2)));
66 }
67
TEST_F(RecordFilterTest,exclude_process_name_regex)68 TEST_F(RecordFilterTest, exclude_process_name_regex) {
69 filter.AddProcessNameRegex("processA", true);
70 thread_tree.SetThreadName(1, 1, "processA1");
71 thread_tree.SetThreadName(2, 2, "processB1");
72 ASSERT_FALSE(filter.Check(GetRecord(1, 1)));
73 ASSERT_TRUE(filter.Check(GetRecord(2, 2)));
74 }
75
TEST_F(RecordFilterTest,exclude_thread_name_regex)76 TEST_F(RecordFilterTest, exclude_thread_name_regex) {
77 filter.AddThreadNameRegex("threadA", true);
78 thread_tree.SetThreadName(1, 1, "processA_threadA");
79 thread_tree.SetThreadName(1, 2, "processA_threadB");
80 ASSERT_FALSE(filter.Check(GetRecord(1, 1)));
81 ASSERT_TRUE(filter.Check(GetRecord(1, 2)));
82 }
83
TEST_F(RecordFilterTest,exclude_uid)84 TEST_F(RecordFilterTest, exclude_uid) {
85 pid_t pid = getpid();
86 std::optional<uid_t> uid = GetProcessUid(pid);
87 ASSERT_TRUE(uid.has_value());
88 filter.AddUids({uid.value()}, true);
89 ASSERT_FALSE(filter.Check(GetRecord(pid, pid)));
90 uint32_t pid_not_exist = UINT32_MAX;
91 ASSERT_TRUE(filter.Check(GetRecord(pid_not_exist, pid_not_exist)));
92 }
93
TEST_F(RecordFilterTest,include_pid)94 TEST_F(RecordFilterTest, include_pid) {
95 filter.AddPids({1}, false);
96 ASSERT_TRUE(filter.Check(GetRecord(1, 1)));
97 ASSERT_FALSE(filter.Check(GetRecord(2, 2)));
98 }
99
TEST_F(RecordFilterTest,include_tid)100 TEST_F(RecordFilterTest, include_tid) {
101 filter.AddTids({1}, false);
102 ASSERT_TRUE(filter.Check(GetRecord(1, 1)));
103 ASSERT_FALSE(filter.Check(GetRecord(1, 2)));
104 }
105
TEST_F(RecordFilterTest,include_process_name_regex)106 TEST_F(RecordFilterTest, include_process_name_regex) {
107 filter.AddProcessNameRegex("processA", false);
108 thread_tree.SetThreadName(1, 1, "processA1");
109 thread_tree.SetThreadName(2, 2, "processB1");
110 ASSERT_TRUE(filter.Check(GetRecord(1, 1)));
111 ASSERT_FALSE(filter.Check(GetRecord(2, 2)));
112 }
113
TEST_F(RecordFilterTest,include_thread_name_regex)114 TEST_F(RecordFilterTest, include_thread_name_regex) {
115 filter.AddThreadNameRegex("threadA", false);
116 thread_tree.SetThreadName(1, 1, "processA_threadA");
117 thread_tree.SetThreadName(1, 2, "processA_threadB");
118 ASSERT_TRUE(filter.Check(GetRecord(1, 1)));
119 ASSERT_FALSE(filter.Check(GetRecord(1, 2)));
120 }
121
TEST_F(RecordFilterTest,include_uid)122 TEST_F(RecordFilterTest, include_uid) {
123 pid_t pid = getpid();
124 std::optional<uid_t> uid = GetProcessUid(pid);
125 ASSERT_TRUE(uid.has_value());
126 filter.AddUids({uid.value()}, false);
127 ASSERT_TRUE(filter.Check(GetRecord(pid, pid)));
128 uint32_t pid_not_exist = UINT32_MAX;
129 ASSERT_FALSE(filter.Check(GetRecord(pid_not_exist, pid_not_exist)));
130 }
131
132 namespace {
133
134 class ParseRecordFilterCommand : public Command {
135 public:
ParseRecordFilterCommand(RecordFilter & filter)136 ParseRecordFilterCommand(RecordFilter& filter) : Command("", "", ""), filter_(filter) {}
137
Run(const std::vector<std::string> & args)138 bool Run(const std::vector<std::string>& args) override {
139 const auto& option_formats = GetRecordFilterOptionFormats();
140 OptionValueMap options;
141 std::vector<std::pair<OptionName, OptionValue>> ordered_options;
142
143 if (!PreprocessOptions(args, option_formats, &options, &ordered_options, nullptr)) {
144 return false;
145 }
146 filter_.Clear();
147 return filter_.ParseOptions(options);
148 }
149
150 private:
151 RecordFilter& filter_;
152 };
153
154 } // namespace
155
TEST_F(RecordFilterTest,parse_options)156 TEST_F(RecordFilterTest, parse_options) {
157 ParseRecordFilterCommand filter_cmd(filter);
158
159 for (bool exclude : {true, false}) {
160 std::string prefix = exclude ? "--exclude-" : "--include-";
161
162 ASSERT_TRUE(filter_cmd.Run({prefix + "pid", "1,2", prefix + "pid", "3"}));
163 ASSERT_EQ(filter.GetCondition(exclude).pids, std::set<pid_t>({1, 2, 3}));
164 ASSERT_TRUE(filter_cmd.Run({prefix + "tid", "1,2", prefix + "tid", "3"}));
165 ASSERT_EQ(filter.GetCondition(exclude).tids, std::set<pid_t>({1, 2, 3}));
166
167 ASSERT_TRUE(
168 filter_cmd.Run({prefix + "process-name", "processA", prefix + "process-name", "processB"}));
169 auto& process_regs = filter.GetCondition(exclude).process_name_regs;
170 ASSERT_EQ(process_regs.size(), 2);
171 ASSERT_TRUE(std::regex_match("processA", process_regs[0]));
172 ASSERT_TRUE(std::regex_match("processB", process_regs[1]));
173
174 ASSERT_TRUE(
175 filter_cmd.Run({prefix + "thread-name", "threadA", prefix + "thread-name", "threadB"}));
176 auto& thread_regs = filter.GetCondition(exclude).thread_name_regs;
177 ASSERT_EQ(thread_regs.size(), 2);
178 ASSERT_TRUE(std::regex_match("threadA", thread_regs[0]));
179 ASSERT_TRUE(std::regex_match("threadB", thread_regs[1]));
180
181 ASSERT_TRUE(filter_cmd.Run({prefix + "uid", "1,2", prefix + "uid", "3"}));
182 ASSERT_EQ(filter.GetCondition(exclude).uids, std::set<uid_t>({1, 2, 3}));
183 }
184 }
185