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