1 /*
2  * Copyright (c) 2022-2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://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,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "write_controller.h"
17 
18 #include <chrono>
19 #include <cstddef>
20 #include <cstdint>
21 #include <iosfwd>
22 #include <mutex>
23 #include <ostream>
24 #include <sys/time.h>
25 #include <sstream>
26 #include <string>
27 
28 #include "hilog/log.h"
29 
30 #undef LOG_DOMAIN
31 #define LOG_DOMAIN 0xD002D08
32 
33 #undef LOG_TAG
34 #define LOG_TAG "WRITE_CONTROLLER"
35 
36 namespace OHOS {
37 namespace HiviewDFX {
38 namespace {
GenerateHash(const std::string & info)39 uint64_t GenerateHash(const std::string& info)
40 {
41     const char* p = info.c_str();
42     size_t infoLen = info.size();
43     size_t infoLenLimit = 256;
44     size_t hashLen = (infoLen < infoLenLimit) ? infoLen : infoLenLimit;
45     size_t i = 0;
46     uint64_t ret { 0xCBF29CE484222325ULL }; // hash basis value
47     while (i < hashLen) {
48         ret ^= *(p + i);
49         ret *= 0x100000001B3ULL; // hash prime value
50         i++;
51     }
52     return ret;
53 }
54 
ConcatenateInfoAsKey(const char * eventName,const char * func,int64_t line)55 uint64_t ConcatenateInfoAsKey(const char* eventName, const char* func, int64_t line)
56 {
57     std::string key;
58     key.append(eventName).append("_").append(func).append("_").append(std::to_string(line));
59     return GenerateHash(key);
60 }
61 
62 struct EventWroteRecord {
63     size_t count = 0;
64     uint64_t timestamp = INVALID_TIME_STAMP;
65 };
66 
67 struct EventWroteCacheNode {
68     std::list<uint64_t>::const_iterator iter;
69     struct EventWroteRecord record;
70 };
71 }
72 
73 class EventWroteLruCache {
74 public:
EventWroteLruCache(size_t capacity)75     explicit EventWroteLruCache(size_t capacity): capacity_(capacity) {}
76 
~EventWroteLruCache()77     ~EventWroteLruCache()
78     {
79         key2Index_.clear();
80     }
81 
82 public:
Get(uint64_t key)83     struct EventWroteRecord Get(uint64_t key)
84     {
85         std::lock_guard<std::mutex> lock(mutex_);
86         EventWroteRecord record;
87         if (key2Index_.count(key) == 0) {
88             return record;
89         }
90         Modify(key);
91         return key2Index_[key].record;
92     }
93 
Put(uint64_t key,struct EventWroteRecord record)94     void Put(uint64_t key, struct EventWroteRecord record)
95     {
96         std::lock_guard<std::mutex> lock(mutex_);
97         if (key2Index_.count(key) > 0) {
98             key2Index_[key].record = record;
99             Modify(key);
100             return;
101         }
102         if (keyCache_.size() == capacity_) {
103             key2Index_.erase(keyCache_.back());
104             keyCache_.pop_back();
105         }
106         keyCache_.push_front(key);
107         key2Index_[key] = {
108             .iter = keyCache_.cbegin(),
109             .record = record
110         };
111     }
112 
113 private:
Modify(uint64_t key)114     void Modify(uint64_t key)
115     {
116         keyCache_.splice(keyCache_.begin(), keyCache_, key2Index_[key].iter);
117         key2Index_[key].iter = keyCache_.cbegin();
118     }
119 
120 private:
121     std::mutex mutex_;
122     std::unordered_map<uint64_t, EventWroteCacheNode> key2Index_;
123     std::list<uint64_t> keyCache_;
124     size_t capacity_ = 0;
125 };
126 
127 std::shared_ptr<EventWroteLruCache> WriteController::eventWroteLruCache_ =
128     std::make_shared<EventWroteLruCache>(30); // 30 is the capacity of the lrucache
129 
GetCurrentTimeMills()130 uint64_t WriteController::GetCurrentTimeMills()
131 {
132     auto now = std::chrono::system_clock::now();
133     auto millisecs = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch());
134     return millisecs.count();
135 }
136 
CheckLimitWritingEvent(const ControlParam & param,const char * domain,const char * eventName,const CallerInfo & callerInfo)137 uint64_t WriteController::CheckLimitWritingEvent(const ControlParam& param, const char* domain, const char* eventName,
138     const CallerInfo& callerInfo)
139 {
140     uint64_t key = ConcatenateInfoAsKey(eventName, callerInfo.func, callerInfo.line);
141     auto record = eventWroteLruCache_->Get(key);
142     uint64_t cur = callerInfo.timeStamp;
143     const uint64_t secToMillis = 1000; // second to millisecond
144     if ((record.count == 0) || ((record.timestamp / secToMillis) + param.period < (cur / secToMillis)) ||
145         ((record.timestamp / secToMillis) > (cur / secToMillis))) {
146         record.count = 1; // record the first event writing during one cycle
147         record.timestamp = cur;
148         eventWroteLruCache_->Put(key, record);
149         return cur;
150     }
151     record.count++;
152     if (record.count <= param.threshold) {
153         eventWroteLruCache_->Put(key, record);
154         return cur;
155     }
156     eventWroteLruCache_->Put(key, record);
157     HILOG_DEBUG(LOG_CORE, "{.period = %{public}zu, .threshold = %{public}zu} "
158         "[%{public}lld, %{public}lld] discard %{public}zu event(s) "
159         "with domain %{public}s and name %{public}s which wrote in function %{public}s.",
160         param.period, param.threshold, static_cast<long long>(record.timestamp / secToMillis),
161         static_cast<long long>(cur / secToMillis), record.count - param.threshold,
162         domain, eventName, callerInfo.func);
163     return INVALID_TIME_STAMP;
164 }
165 
CheckLimitWritingEvent(const ControlParam & param,const char * domain,const char * eventName,const char * func,int64_t line)166 uint64_t WriteController::CheckLimitWritingEvent(const ControlParam& param, const char* domain,
167     const char* eventName, const char* func, int64_t line)
168 {
169     CallerInfo info = {
170         .func = func,
171         .line = line,
172         .timeStamp = GetCurrentTimeMills(),
173     };
174     return CheckLimitWritingEvent(param, domain, eventName, info);
175 }
176 } // HiviewDFX
177 } // OHOS