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 "environment.h"
20 #include "utils.h"
21
22 using android::base::Split;
23 using android::base::Trim;
24
25 namespace simpleperf {
26
27 namespace {
28
29 class CpuFilter : public RecordFilterCondition {
30 public:
AddCpus(const std::set<int> & cpus)31 void AddCpus(const std::set<int>& cpus) { cpus_.insert(cpus.begin(), cpus.end()); }
32
Check(const SampleRecord & sample)33 bool Check(const SampleRecord& sample) override {
34 int cpu = static_cast<int>(sample.cpu_data.cpu);
35 return cpus_.empty() || cpus_.count(cpu) == 1;
36 }
37
38 private:
39 std::set<int> cpus_;
40 };
41
42 class PidFilter : public RecordFilterCondition {
43 public:
AddPids(const std::set<pid_t> & pids,bool exclude)44 void AddPids(const std::set<pid_t>& pids, bool exclude) {
45 auto& dest = exclude ? exclude_pids_ : include_pids_;
46 dest.insert(pids.begin(), pids.end());
47 }
48
Check(const SampleRecord & sample)49 bool Check(const SampleRecord& sample) override {
50 uint32_t pid = sample.tid_data.pid;
51 if (!include_pids_.empty() && include_pids_.count(pid) == 0) {
52 return false;
53 }
54 return exclude_pids_.count(pid) == 0;
55 }
56
57 private:
58 std::set<pid_t> include_pids_;
59 std::set<pid_t> exclude_pids_;
60 };
61
62 class TidFilter : public RecordFilterCondition {
63 public:
AddTids(const std::set<pid_t> & tids,bool exclude)64 void AddTids(const std::set<pid_t>& tids, bool exclude) {
65 auto& dest = exclude ? exclude_tids_ : include_tids_;
66 dest.insert(tids.begin(), tids.end());
67 }
68
Check(const SampleRecord & sample)69 bool Check(const SampleRecord& sample) override {
70 uint32_t tid = sample.tid_data.tid;
71 if (!include_tids_.empty() && include_tids_.count(tid) == 0) {
72 return false;
73 }
74 return exclude_tids_.count(tid) == 0;
75 }
76
77 private:
78 std::set<pid_t> include_tids_;
79 std::set<pid_t> exclude_tids_;
80 };
81
82 class ProcessNameFilter : public RecordFilterCondition {
83 public:
ProcessNameFilter(const ThreadTree & thread_tree)84 ProcessNameFilter(const ThreadTree& thread_tree) : thread_tree_(thread_tree) {}
85
AddProcessNameRegex(const std::string & process_name,bool exclude)86 bool AddProcessNameRegex(const std::string& process_name, bool exclude) {
87 if (auto regex = RegEx::Create(process_name); regex != nullptr) {
88 auto& dest = exclude ? exclude_names_ : include_names_;
89 dest.emplace_back(std::move(regex));
90 return true;
91 }
92 return false;
93 }
94
Check(const SampleRecord & sample)95 bool Check(const SampleRecord& sample) override {
96 ThreadEntry* process = thread_tree_.FindThread(sample.tid_data.pid);
97 if (process == nullptr) {
98 return false;
99 }
100 std::string_view process_name = process->comm;
101 if (!include_names_.empty() && !SearchInRegs(process_name, include_names_)) {
102 return false;
103 }
104 return !SearchInRegs(process_name, exclude_names_);
105 }
106
107 private:
108 const ThreadTree& thread_tree_;
109 std::vector<std::unique_ptr<RegEx>> include_names_;
110 std::vector<std::unique_ptr<RegEx>> exclude_names_;
111 };
112
113 class ThreadNameFilter : public RecordFilterCondition {
114 public:
ThreadNameFilter(const ThreadTree & thread_tree)115 ThreadNameFilter(const ThreadTree& thread_tree) : thread_tree_(thread_tree) {}
116
AddThreadNameRegex(const std::string & thread_name,bool exclude)117 bool AddThreadNameRegex(const std::string& thread_name, bool exclude) {
118 if (auto regex = RegEx::Create(thread_name); regex != nullptr) {
119 auto& dest = exclude ? exclude_names_ : include_names_;
120 dest.emplace_back(std::move(regex));
121 return true;
122 }
123 return false;
124 }
125
Check(const SampleRecord & sample)126 bool Check(const SampleRecord& sample) override {
127 ThreadEntry* thread = thread_tree_.FindThread(sample.tid_data.tid);
128 if (thread == nullptr) {
129 return false;
130 }
131 std::string_view thread_name = thread->comm;
132 if (!include_names_.empty() && !SearchInRegs(thread_name, include_names_)) {
133 return false;
134 }
135 return !SearchInRegs(thread_name, exclude_names_);
136 }
137
138 private:
139 const ThreadTree& thread_tree_;
140 std::vector<std::unique_ptr<RegEx>> include_names_;
141 std::vector<std::unique_ptr<RegEx>> exclude_names_;
142 };
143
144 class UidFilter : public RecordFilterCondition {
145 public:
AddUids(const std::set<uint32_t> & uids,bool exclude)146 void AddUids(const std::set<uint32_t>& uids, bool exclude) {
147 auto& dest = exclude ? exclude_uids_ : include_uids_;
148 dest.insert(uids.begin(), uids.end());
149 }
150
Check(const SampleRecord & sample)151 bool Check(const SampleRecord& sample) override {
152 uint32_t pid = sample.tid_data.pid;
153 std::optional<uint32_t> uid;
154 if (auto it = pid_to_uid_map_.find(pid); it != pid_to_uid_map_.end()) {
155 uid = it->second;
156 } else {
157 uid = GetProcessUid(pid);
158 pid_to_uid_map_[pid] = uid;
159 }
160 if (!uid) {
161 return false;
162 }
163 if (!include_uids_.empty() && include_uids_.count(uid.value()) == 0) {
164 return false;
165 }
166 return exclude_uids_.count(uid.value()) == 0;
167 }
168
169 private:
170 std::set<uint32_t> include_uids_;
171 std::set<uint32_t> exclude_uids_;
172 std::unordered_map<uint32_t, std::optional<uint32_t>> pid_to_uid_map_;
173 };
174
175 using TimeRange = std::pair<uint64_t, uint64_t>;
176
177 class TimeRanges {
178 public:
Begin(uint64_t timestamp)179 void Begin(uint64_t timestamp) {
180 if (!begin_time_.has_value()) {
181 begin_time_ = timestamp;
182 }
183 }
184
End(uint64_t timestamp)185 bool End(uint64_t timestamp) {
186 if (begin_time_.has_value()) {
187 if (begin_time_ >= timestamp) {
188 LOG(ERROR) << "Invalid time range in filter data: begin time " << begin_time_.value()
189 << " >= end time " << timestamp;
190 return false;
191 }
192 ranges_.emplace_back(begin_time_.value(), timestamp);
193 begin_time_.reset();
194 }
195 return true;
196 }
197
NoMoreTimestamp()198 void NoMoreTimestamp() {
199 if (begin_time_.has_value()) {
200 ranges_.emplace_back(begin_time_.value(), UINT64_MAX);
201 }
202 std::sort(ranges_.begin(), ranges_.end());
203 }
204
Empty() const205 bool Empty() const { return ranges_.empty(); }
206
InRange(uint64_t timestamp) const207 bool InRange(uint64_t timestamp) const {
208 auto it = std::upper_bound(ranges_.begin(), ranges_.end(),
209 std::pair<uint64_t, uint64_t>(timestamp, 0));
210 if (it != ranges_.end() && it->first == timestamp) {
211 return true;
212 }
213 if (it != ranges_.begin()) {
214 --it;
215 if (it->second > timestamp) {
216 return true;
217 }
218 }
219 return false;
220 }
221
222 private:
223 std::optional<uint64_t> begin_time_;
224 std::vector<TimeRange> ranges_;
225 };
226
227 } // namespace
228
229 class TimeFilter : public RecordFilterCondition {
230 public:
GetClock() const231 const std::string& GetClock() const { return clock_; }
SetClock(const std::string & clock)232 void SetClock(const std::string& clock) { clock_ = clock; }
233
GlobalBegin(uint64_t timestamp)234 void GlobalBegin(uint64_t timestamp) { global_ranges_.Begin(timestamp); }
235
GlobalEnd(uint64_t timestamp)236 bool GlobalEnd(uint64_t timestamp) { return global_ranges_.End(timestamp); }
237
ProcessBegin(pid_t pid,uint64_t timestamp)238 void ProcessBegin(pid_t pid, uint64_t timestamp) { process_ranges_[pid].Begin(timestamp); }
239
ProcessEnd(pid_t pid,uint64_t timestamp)240 bool ProcessEnd(pid_t pid, uint64_t timestamp) { return process_ranges_[pid].End(timestamp); }
241
ThreadBegin(pid_t tid,uint64_t timestamp)242 void ThreadBegin(pid_t tid, uint64_t timestamp) { thread_ranges_[tid].Begin(timestamp); }
243
ThreadEnd(pid_t tid,uint64_t timestamp)244 bool ThreadEnd(pid_t tid, uint64_t timestamp) { return thread_ranges_[tid].End(timestamp); }
245
NoMoreTimestamp()246 void NoMoreTimestamp() {
247 global_ranges_.NoMoreTimestamp();
248 for (auto& p : process_ranges_) {
249 p.second.NoMoreTimestamp();
250 }
251 for (auto& p : thread_ranges_) {
252 p.second.NoMoreTimestamp();
253 }
254 }
255
Empty() const256 bool Empty() const {
257 return global_ranges_.Empty() && process_ranges_.empty() && thread_ranges_.empty();
258 }
259
Check(const SampleRecord & sample)260 bool Check(const SampleRecord& sample) override {
261 uint64_t timestamp = sample.Timestamp();
262 if (!global_ranges_.Empty() && !global_ranges_.InRange(timestamp)) {
263 return false;
264 }
265 if (!process_ranges_.empty()) {
266 auto it = process_ranges_.find(sample.tid_data.pid);
267 if (it == process_ranges_.end() || !it->second.InRange(timestamp)) {
268 return false;
269 }
270 }
271 if (!thread_ranges_.empty()) {
272 auto it = thread_ranges_.find(sample.tid_data.tid);
273 if (it == thread_ranges_.end() || !it->second.InRange(timestamp)) {
274 return false;
275 }
276 }
277 return true;
278 }
279
280 private:
281 std::string clock_ = "monotonic";
282 TimeRanges global_ranges_;
283 std::unordered_map<pid_t, TimeRanges> process_ranges_;
284 std::unordered_map<pid_t, TimeRanges> thread_ranges_;
285 };
286
287 // Read filter file. The format is in doc/sample_filter.md.
288 class FilterFileReader {
289 public:
FilterFileReader(const std::string & filename)290 FilterFileReader(const std::string& filename) : filename_(filename) {}
291
Read()292 bool Read() {
293 std::string data;
294 if (!android::base::ReadFileToString(filename_, &data)) {
295 PLOG(ERROR) << "failed to read " << filename_;
296 return false;
297 }
298 line_number_ = 0;
299 time_filter_.reset(new TimeFilter);
300 std::string arg_str;
301 std::vector<std::string> args;
302 uint64_t timestamp;
303 pid_t pid;
304 for (const auto& line : Split(data, "\n")) {
305 line_number_++;
306 if (SearchCmd(line, "CLOCK", &arg_str)) {
307 if (!SplitArgs(arg_str, 1, &args)) {
308 return false;
309 }
310 time_filter_->SetClock(args[0]);
311 } else if (SearchCmd(line, "GLOBAL_BEGIN", &arg_str)) {
312 if (!SplitArgs(arg_str, 1, &args) || !ParseTimestamp(args[0], ×tamp)) {
313 return false;
314 }
315 time_filter_->GlobalBegin(timestamp);
316 } else if (SearchCmd(line, "GLOBAL_END", &arg_str)) {
317 if (!SplitArgs(arg_str, 1, &args) || !ParseTimestamp(args[0], ×tamp) ||
318 !time_filter_->GlobalEnd(timestamp)) {
319 return false;
320 }
321 } else if (SearchCmd(line, "PROCESS_BEGIN", &arg_str)) {
322 if (!SplitArgs(arg_str, 2, &args) || !ParsePid(args[0], &pid) ||
323 !ParseTimestamp(args[1], ×tamp)) {
324 return false;
325 }
326 time_filter_->ProcessBegin(pid, timestamp);
327 } else if (SearchCmd(line, "PROCESS_END", &arg_str)) {
328 if (!SplitArgs(arg_str, 2, &args) || !ParsePid(args[0], &pid) ||
329 !ParseTimestamp(args[1], ×tamp) || !time_filter_->ProcessEnd(pid, timestamp)) {
330 return false;
331 }
332 } else if (SearchCmd(line, "THREAD_BEGIN", &arg_str)) {
333 if (!SplitArgs(arg_str, 2, &args) || !ParsePid(args[0], &pid) ||
334 !ParseTimestamp(args[1], ×tamp)) {
335 return false;
336 }
337 time_filter_->ThreadBegin(pid, timestamp);
338 } else if (SearchCmd(line, "THREAD_END", &arg_str)) {
339 if (!SplitArgs(arg_str, 2, &args) || !ParsePid(args[0], &pid) ||
340 !ParseTimestamp(args[1], ×tamp) || !time_filter_->ThreadEnd(pid, timestamp)) {
341 return false;
342 }
343 }
344 }
345 return true;
346 }
347
GetTimeFilter()348 std::unique_ptr<TimeFilter>& GetTimeFilter() { return time_filter_; }
349
350 private:
SearchCmd(const std::string & s,const char * cmd,std::string * arg_str)351 bool SearchCmd(const std::string& s, const char* cmd, std::string* arg_str) {
352 auto pos = s.find(cmd);
353 if (pos == s.npos) {
354 return false;
355 }
356 *arg_str = s.substr(pos + strlen(cmd));
357 return true;
358 }
359
SplitArgs(const std::string & s,size_t nargs,std::vector<std::string> * args)360 bool SplitArgs(const std::string& s, size_t nargs, std::vector<std::string>* args) {
361 *args = Split(Trim(s), " ");
362 if (args->size() != nargs) {
363 LOG(ERROR) << "Invalid args in " << filename_ << ":" << line_number_ << ": " << s;
364 return false;
365 }
366 return true;
367 }
368
ParsePid(const std::string & s,pid_t * pid)369 bool ParsePid(const std::string& s, pid_t* pid) {
370 if (!android::base::ParseInt(s.c_str(), pid, static_cast<pid_t>(0))) {
371 LOG(ERROR) << "Invalid pid in " << filename_ << ":" << line_number_ << ": " << s;
372 return false;
373 }
374 return true;
375 }
376
ParseTimestamp(const std::string & s,uint64_t * timestamp)377 bool ParseTimestamp(const std::string& s, uint64_t* timestamp) {
378 if (!android::base::ParseUint(s.c_str(), timestamp)) {
379 LOG(ERROR) << "Invalid timestamp in " << filename_ << ":" << line_number_ << ": " << s;
380 return false;
381 }
382 return true;
383 }
384
385 const std::string filename_;
386 size_t line_number_ = 0;
387 std::unique_ptr<TimeFilter> time_filter_;
388 };
389
RecordFilter(const ThreadTree & thread_tree)390 RecordFilter::RecordFilter(const ThreadTree& thread_tree) : thread_tree_(thread_tree) {}
391
~RecordFilter()392 RecordFilter::~RecordFilter() {}
393
ParseOptions(OptionValueMap & options)394 bool RecordFilter::ParseOptions(OptionValueMap& options) {
395 for (bool exclude : {true, false}) {
396 std::string prefix = exclude ? "--exclude-" : "--include-";
397 if (auto strs = options.PullStringValues(prefix + "pid"); !strs.empty()) {
398 if (auto pids = GetPidsFromStrings(strs, false, false); pids) {
399 AddPids(pids.value(), exclude);
400 } else {
401 return false;
402 }
403 }
404 for (const OptionValue& value : options.PullValues(prefix + "tid")) {
405 if (auto tids = GetTidsFromString(value.str_value, false); tids) {
406 AddTids(tids.value(), exclude);
407 } else {
408 return false;
409 }
410 }
411 for (const OptionValue& value : options.PullValues(prefix + "process-name")) {
412 if (!AddProcessNameRegex(value.str_value, exclude)) {
413 return false;
414 }
415 }
416 for (const OptionValue& value : options.PullValues(prefix + "thread-name")) {
417 if (!AddThreadNameRegex(value.str_value, exclude)) {
418 return false;
419 }
420 }
421 for (const OptionValue& value : options.PullValues(prefix + "uid")) {
422 if (auto uids = ParseUintVector<uint32_t>(value.str_value); uids) {
423 AddUids(uids.value(), exclude);
424 } else {
425 return false;
426 }
427 }
428 }
429 for (const OptionValue& value : options.PullValues("--cpu")) {
430 if (auto cpus = GetCpusFromString(value.str_value); cpus) {
431 AddCpus(cpus.value());
432 } else {
433 return false;
434 }
435 }
436 if (auto value = options.PullValue("--filter-file"); value) {
437 if (!SetFilterFile(value->str_value)) {
438 return false;
439 }
440 }
441 return true;
442 }
443
AddCpus(const std::set<int> & cpus)444 void RecordFilter::AddCpus(const std::set<int>& cpus) {
445 std::unique_ptr<RecordFilterCondition>& cpu_filter = conditions_["cpu"];
446 if (!cpu_filter) {
447 cpu_filter.reset(new CpuFilter);
448 }
449 static_cast<CpuFilter&>(*cpu_filter).AddCpus(cpus);
450 }
451
AddPids(const std::set<pid_t> & pids,bool exclude)452 void RecordFilter::AddPids(const std::set<pid_t>& pids, bool exclude) {
453 std::unique_ptr<RecordFilterCondition>& pid_filter = conditions_["pid"];
454 if (!pid_filter) {
455 pid_filter.reset(new PidFilter);
456 }
457 static_cast<PidFilter&>(*pid_filter).AddPids(pids, exclude);
458 }
459
AddTids(const std::set<pid_t> & tids,bool exclude)460 void RecordFilter::AddTids(const std::set<pid_t>& tids, bool exclude) {
461 std::unique_ptr<RecordFilterCondition>& tid_filter = conditions_["tid"];
462 if (!tid_filter) {
463 tid_filter.reset(new TidFilter);
464 }
465 static_cast<TidFilter&>(*tid_filter).AddTids(tids, exclude);
466 }
467
AddProcessNameRegex(const std::string & process_name,bool exclude)468 bool RecordFilter::AddProcessNameRegex(const std::string& process_name, bool exclude) {
469 std::unique_ptr<RecordFilterCondition>& process_name_filter = conditions_["process_name"];
470 if (!process_name_filter) {
471 process_name_filter.reset(new ProcessNameFilter(thread_tree_));
472 }
473 return static_cast<ProcessNameFilter&>(*process_name_filter)
474 .AddProcessNameRegex(process_name, exclude);
475 }
476
AddThreadNameRegex(const std::string & thread_name,bool exclude)477 bool RecordFilter::AddThreadNameRegex(const std::string& thread_name, bool exclude) {
478 std::unique_ptr<RecordFilterCondition>& thread_name_filter = conditions_["thread_name"];
479 if (!thread_name_filter) {
480 thread_name_filter.reset(new ThreadNameFilter(thread_tree_));
481 }
482 return static_cast<ThreadNameFilter&>(*thread_name_filter)
483 .AddThreadNameRegex(thread_name, exclude);
484 }
485
AddUids(const std::set<uint32_t> & uids,bool exclude)486 void RecordFilter::AddUids(const std::set<uint32_t>& uids, bool exclude) {
487 std::unique_ptr<RecordFilterCondition>& uid_filter = conditions_["uid"];
488 if (!uid_filter) {
489 uid_filter.reset(new UidFilter);
490 }
491 return static_cast<UidFilter&>(*uid_filter).AddUids(uids, exclude);
492 }
493
SetFilterFile(const std::string & filename)494 bool RecordFilter::SetFilterFile(const std::string& filename) {
495 FilterFileReader reader(filename);
496 if (!reader.Read()) {
497 return false;
498 }
499 conditions_["time"] = std::move(reader.GetTimeFilter());
500 return true;
501 }
502
Check(const SampleRecord & r)503 bool RecordFilter::Check(const SampleRecord& r) {
504 for (auto& p : conditions_) {
505 if (!p.second->Check(r)) {
506 return false;
507 }
508 }
509 return true;
510 }
511
CheckClock(const std::string & clock)512 bool RecordFilter::CheckClock(const std::string& clock) {
513 if (auto it = conditions_.find("time"); it != conditions_.end()) {
514 TimeFilter& time_filter = static_cast<TimeFilter&>(*it->second);
515 if (time_filter.GetClock() != clock) {
516 LOG(ERROR) << "clock generating sample timestamps is " << clock
517 << ", which doesn't match clock used in time filter " << time_filter.GetClock();
518 return false;
519 }
520 }
521 return true;
522 }
523
Clear()524 void RecordFilter::Clear() {
525 conditions_.clear();
526 }
527
528 } // namespace simpleperf
529