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