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