1 /* 2 * Copyright (C) 2023 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 #pragma once 18 19 #include <gtest/gtest_prod.h> 20 21 #include <atomic> 22 #include <mutex> 23 #include <unordered_map> 24 #include <unordered_set> 25 26 namespace android { 27 namespace os { 28 namespace statsd { 29 30 /** 31 * Templating is for benchmarks only 32 * 33 * Based on benchmarks the more fast container to be used for atom ids filtering 34 * is unordered_set<int> 35 * #BM_LogEventFilterUnorderedSet 391208 ns 390086 ns 1793 36 * #BM_LogEventFilterUnorderedSet2Consumers 1293527 ns 1289326 ns 543 37 * #BM_LogEventFilterSet 613362 ns 611259 ns 1146 38 * #BM_LogEventFilterSet2Consumers 1859397 ns 1854193 ns 378 39 * 40 * See @LogEventFilter definition below 41 */ 42 template <typename T> 43 class LogEventFilterGeneric { 44 public: 45 virtual ~LogEventFilterGeneric() = default; 46 setFilteringEnabled(bool isEnabled)47 virtual void setFilteringEnabled(bool isEnabled) { 48 mLogsFilteringEnabled = isEnabled; 49 } 50 getFilteringEnabled()51 bool getFilteringEnabled() const { 52 return mLogsFilteringEnabled; 53 } 54 55 /** 56 * @brief Tests atom id with list of interesting atoms 57 * If Logs filtering is disabled - assume all atoms in use 58 * Most of the time should be non-blocking call - only in case when setAtomIds() was 59 * called the call will be blocking due to atom list needs to be synced up 60 * @param atomId 61 * @return true if atom is used by any of consumer or filtering is disabled 62 */ isAtomInUse(int atomId)63 virtual bool isAtomInUse(int atomId) const { 64 if (!mLogsFilteringEnabled) { 65 return true; 66 } 67 68 // check if there is an updated set of interesting atom ids 69 if (mLocalSetUpdateCounter != mSetUpdateCounter.load(std::memory_order_relaxed)) { 70 std::lock_guard<std::mutex> guard(mTagIdsMutex); 71 mLocalSetUpdateCounter = mSetUpdateCounter.load(std::memory_order_relaxed); 72 mLocalTagIds.swap(mTagIds); 73 } 74 return mLocalTagIds.find(atomId) != mLocalTagIds.end(); 75 } 76 77 typedef const void* ConsumerId; 78 79 typedef T AtomIdSet; 80 /** 81 * @brief Set the Atom Ids object 82 * 83 * @param tagIds set of atoms ids 84 * @param consumer used to differentiate the consumers to form proper superset of ids 85 */ setAtomIds(AtomIdSet tagIds,ConsumerId consumer)86 virtual void setAtomIds(AtomIdSet tagIds, ConsumerId consumer) { 87 std::lock_guard lock(mTagIdsMutex); 88 // update ids list from consumer 89 if (tagIds.size() == 0) { 90 mTagIdsPerConsumer.erase(consumer); 91 } else { 92 mTagIdsPerConsumer[consumer].swap(tagIds); 93 } 94 // populate the superset incorporating list of distinct atom ids from all consumers 95 mTagIds.clear(); 96 for (const auto& [_, atomIds] : mTagIdsPerConsumer) { 97 mTagIds.insert(atomIds.begin(), atomIds.end()); 98 } 99 mSetUpdateCounter.fetch_add(1, std::memory_order_relaxed); 100 } 101 102 private: 103 std::atomic_bool mLogsFilteringEnabled = false; 104 std::atomic_int mSetUpdateCounter; 105 mutable int mLocalSetUpdateCounter; 106 107 mutable std::mutex mTagIdsMutex; 108 std::unordered_map<ConsumerId, AtomIdSet> mTagIdsPerConsumer; 109 mutable AtomIdSet mTagIds; 110 mutable AtomIdSet mLocalTagIds; 111 112 friend class LogEventFilterTest; 113 114 FRIEND_TEST(LogEventFilterTest, TestEmptyFilter); 115 FRIEND_TEST(LogEventFilterTest, TestRemoveNonExistingEmptyFilter); 116 FRIEND_TEST(LogEventFilterTest, TestEmptyFilterDisabled); 117 FRIEND_TEST(LogEventFilterTest, TestNonEmptyFilterFullOverlap); 118 FRIEND_TEST(LogEventFilterTest, TestNonEmptyFilterPartialOverlap); 119 FRIEND_TEST(LogEventFilterTest, TestNonEmptyFilterDisabled); 120 FRIEND_TEST(LogEventFilterTest, TestNonEmptyFilterDisabledPartialOverlap); 121 FRIEND_TEST(LogEventFilterTest, TestMultipleConsumerOverlapIds); 122 FRIEND_TEST(LogEventFilterTest, TestMultipleConsumerOverlapIdsRemoved); 123 FRIEND_TEST(LogEventFilterTest, TestMultipleConsumerEmptyFilter); 124 }; 125 126 typedef LogEventFilterGeneric<std::unordered_set<int>> LogEventFilter; 127 128 } // namespace statsd 129 } // namespace os 130 } // namespace android 131