| // Copyright 2020 The Pigweed Authors |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| // use this file except in compliance with the License. You may obtain a copy of |
| // the License at |
| // |
| // https://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| // License for the specific language governing permissions and limitations under |
| // the License. |
| |
| #include "pw_log_rpc/log_filter.h" |
| |
| #include "pw_log/levels.h" |
| #include "pw_protobuf/decoder.h" |
| #include "pw_status/try.h" |
| |
| namespace pw::log_rpc { |
| namespace { |
| |
| // Returns true if the provided log parameters match the given filter rule. |
| bool IsRuleMet(const Filter::Rule& rule, |
| uint32_t level, |
| ConstByteSpan module, |
| uint32_t flags, |
| ConstByteSpan thread) { |
| if (level < static_cast<uint32_t>(rule.level_greater_than_or_equal)) { |
| return false; |
| } |
| if ((rule.any_flags_set != 0) && ((flags & rule.any_flags_set) == 0)) { |
| return false; |
| } |
| if (!rule.module_equals.empty() && !std::equal(module.begin(), |
| module.end(), |
| rule.module_equals.begin(), |
| rule.module_equals.end())) { |
| return false; |
| } |
| if (!rule.thread_equals.empty() && !std::equal(thread.begin(), |
| thread.end(), |
| rule.thread_equals.begin(), |
| rule.thread_equals.end())) { |
| return false; |
| } |
| return true; |
| } |
| |
| } // namespace |
| |
| Status Filter::UpdateRulesFromProto(ConstByteSpan buffer) { |
| if (rules_.empty()) { |
| return Status::FailedPrecondition(); |
| } |
| |
| // Reset rules. |
| for (auto& rule : rules_) { |
| rule = {}; |
| } |
| |
| protobuf::Decoder decoder(buffer); |
| Status status; |
| for (size_t i = 0; (i < rules_.size()) && (status = decoder.Next()).ok(); |
| ++i) { |
| ConstByteSpan rule_buffer; |
| PW_TRY(decoder.ReadBytes(&rule_buffer)); |
| protobuf::Decoder rule_decoder(rule_buffer); |
| while ((status = rule_decoder.Next()).ok()) { |
| switch ( |
| static_cast<log::FilterRule::Fields>(rule_decoder.FieldNumber())) { |
| case log::FilterRule::Fields::LEVEL_GREATER_THAN_OR_EQUAL: |
| PW_TRY(rule_decoder.ReadUint32(reinterpret_cast<uint32_t*>( |
| &rules_[i].level_greater_than_or_equal))); |
| break; |
| case log::FilterRule::Fields::MODULE_EQUALS: { |
| ConstByteSpan module; |
| PW_TRY(rule_decoder.ReadBytes(&module)); |
| if (module.size() > rules_[i].module_equals.max_size()) { |
| return Status::InvalidArgument(); |
| } |
| rules_[i].module_equals.assign(module.begin(), module.end()); |
| } break; |
| case log::FilterRule::Fields::ANY_FLAGS_SET: |
| PW_TRY(rule_decoder.ReadUint32(&rules_[i].any_flags_set)); |
| break; |
| case log::FilterRule::Fields::ACTION: |
| PW_TRY(rule_decoder.ReadUint32( |
| reinterpret_cast<uint32_t*>(&rules_[i].action))); |
| break; |
| case log::FilterRule::Fields::THREAD_EQUALS: { |
| ConstByteSpan thread; |
| PW_TRY(rule_decoder.ReadBytes(&thread)); |
| if (thread.size() > rules_[i].thread_equals.max_size()) { |
| return Status::InvalidArgument(); |
| } |
| rules_[i].thread_equals.assign(thread.begin(), thread.end()); |
| } break; |
| } |
| } |
| } |
| return status.IsOutOfRange() ? OkStatus() : status; |
| } |
| |
| bool Filter::ShouldDropLog(ConstByteSpan entry) const { |
| if (rules_.empty()) { |
| return false; |
| } |
| |
| uint32_t log_level = 0; |
| ConstByteSpan log_module; |
| ConstByteSpan log_thread; |
| uint32_t log_flags = 0; |
| protobuf::Decoder decoder(entry); |
| while (decoder.Next().ok()) { |
| const auto field_num = |
| static_cast<log::LogEntry::Fields>(decoder.FieldNumber()); |
| |
| if (field_num == log::LogEntry::Fields::LINE_LEVEL) { |
| if (decoder.ReadUint32(&log_level).ok()) { |
| log_level &= PW_LOG_LEVEL_BITMASK; |
| } |
| |
| } else if (field_num == log::LogEntry::Fields::MODULE) { |
| decoder.ReadBytes(&log_module).IgnoreError(); |
| |
| } else if (field_num == log::LogEntry::Fields::FLAGS) { |
| decoder.ReadUint32(&log_flags).IgnoreError(); |
| |
| } else if (field_num == log::LogEntry::Fields::THREAD) { |
| decoder.ReadBytes(&log_thread).IgnoreError(); |
| } |
| } |
| |
| // Follow the action of the first rule whose condition is met. |
| for (const auto& rule : rules_) { |
| if (rule.action == Filter::Rule::Action::kInactive) { |
| continue; |
| } |
| if (IsRuleMet(rule, log_level, log_module, log_flags, log_thread)) { |
| return rule.action == Filter::Rule::Action::kDrop; |
| } |
| } |
| |
| return false; |
| } |
| |
| } // namespace pw::log_rpc |