xref: /aosp_15_r20/external/pigweed/pw_log_rpc/log_filter.cc (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1 // Copyright 2020 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14 
15 #include "pw_log_rpc/log_filter.h"
16 
17 #include "pw_log/levels.h"
18 #include "pw_protobuf/decoder.h"
19 #include "pw_status/try.h"
20 
21 namespace pw::log_rpc {
22 namespace {
23 
24 namespace FilterRule = ::pw::log::pwpb::FilterRule;
25 namespace LogEntry = ::pw::log::pwpb::LogEntry;
26 
27 // Returns true if the provided log parameters match the given filter rule.
IsRuleMet(const Filter::Rule & rule,uint32_t level,ConstByteSpan module,uint32_t flags,ConstByteSpan thread)28 bool IsRuleMet(const Filter::Rule& rule,
29                uint32_t level,
30                ConstByteSpan module,
31                uint32_t flags,
32                ConstByteSpan thread) {
33   if (level < static_cast<uint32_t>(rule.level_greater_than_or_equal)) {
34     return false;
35   }
36   if ((rule.any_flags_set != 0) && ((flags & rule.any_flags_set) == 0)) {
37     return false;
38   }
39   if (!rule.module_equals.empty() && !std::equal(module.begin(),
40                                                  module.end(),
41                                                  rule.module_equals.begin(),
42                                                  rule.module_equals.end())) {
43     return false;
44   }
45   if (!rule.thread_equals.empty() && !std::equal(thread.begin(),
46                                                  thread.end(),
47                                                  rule.thread_equals.begin(),
48                                                  rule.thread_equals.end())) {
49     return false;
50   }
51   return true;
52 }
53 
54 }  // namespace
55 
UpdateRulesFromProto(ConstByteSpan buffer)56 Status Filter::UpdateRulesFromProto(ConstByteSpan buffer) {
57   if (rules_.empty()) {
58     return Status::FailedPrecondition();
59   }
60 
61   // Reset rules.
62   for (auto& rule : rules_) {
63     rule = {};
64   }
65 
66   protobuf::Decoder decoder(buffer);
67   Status status;
68   for (size_t i = 0; (i < rules_.size()) && (status = decoder.Next()).ok();
69        ++i) {
70     ConstByteSpan rule_buffer;
71     PW_TRY(decoder.ReadBytes(&rule_buffer));
72     protobuf::Decoder rule_decoder(rule_buffer);
73     while ((status = rule_decoder.Next()).ok()) {
74       switch (static_cast<FilterRule::Fields>(rule_decoder.FieldNumber())) {
75         case FilterRule::Fields::kLevelGreaterThanOrEqual:
76           PW_TRY(rule_decoder.ReadUint32(reinterpret_cast<uint32_t*>(
77               &rules_[i].level_greater_than_or_equal)));
78           break;
79         case FilterRule::Fields::kModuleEquals: {
80           ConstByteSpan module;
81           PW_TRY(rule_decoder.ReadBytes(&module));
82           if (module.size() > rules_[i].module_equals.max_size()) {
83             return Status::InvalidArgument();
84           }
85           rules_[i].module_equals.assign(module.begin(), module.end());
86         } break;
87         case FilterRule::Fields::kAnyFlagsSet:
88           PW_TRY(rule_decoder.ReadUint32(&rules_[i].any_flags_set));
89           break;
90         case FilterRule::Fields::kAction:
91           PW_TRY(rule_decoder.ReadUint32(
92               reinterpret_cast<uint32_t*>(&rules_[i].action)));
93           break;
94         case FilterRule::Fields::kThreadEquals: {
95           ConstByteSpan thread;
96           PW_TRY(rule_decoder.ReadBytes(&thread));
97           if (thread.size() > rules_[i].thread_equals.max_size()) {
98             return Status::InvalidArgument();
99           }
100           rules_[i].thread_equals.assign(thread.begin(), thread.end());
101         } break;
102       }
103     }
104   }
105   return status.IsOutOfRange() ? OkStatus() : status;
106 }
107 
ShouldDropLog(ConstByteSpan entry) const108 bool Filter::ShouldDropLog(ConstByteSpan entry) const {
109   if (rules_.empty()) {
110     return false;
111   }
112 
113   uint32_t log_level = 0;
114   ConstByteSpan log_module;
115   ConstByteSpan log_thread;
116   uint32_t log_flags = 0;
117   protobuf::Decoder decoder(entry);
118   while (decoder.Next().ok()) {
119     const auto field_num = static_cast<LogEntry::Fields>(decoder.FieldNumber());
120 
121     if (field_num == LogEntry::Fields::kLineLevel) {
122       if (decoder.ReadUint32(&log_level).ok()) {
123         log_level &= PW_LOG_LEVEL_BITMASK;
124       }
125 
126     } else if (field_num == LogEntry::Fields::kModule) {
127       decoder.ReadBytes(&log_module).IgnoreError();
128 
129     } else if (field_num == LogEntry::Fields::kFlags) {
130       decoder.ReadUint32(&log_flags).IgnoreError();
131 
132     } else if (field_num == LogEntry::Fields::kThread) {
133       decoder.ReadBytes(&log_thread).IgnoreError();
134     }
135   }
136 
137   // Follow the action of the first rule whose condition is met.
138   for (const auto& rule : rules_) {
139     if (rule.action == Filter::Rule::Action::kInactive) {
140       continue;
141     }
142     if (IsRuleMet(rule, log_level, log_module, log_flags, log_thread)) {
143       return rule.action == Filter::Rule::Action::kDrop;
144     }
145   }
146 
147   return false;
148 }
149 
150 }  // namespace pw::log_rpc
151