1 /*
2  * Copyright (c) 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 #include "daily_controller.h"
16 
17 #include <cmath>
18 
19 #include "hiview_logger.h"
20 #include "time_util.h"
21 
22 namespace OHOS {
23 namespace HiviewDFX {
24 DEFINE_LOG_TAG("DailyController");
25 namespace {
26 constexpr int32_t COUNT_OF_INIT = 1;
27 }
28 
DailyController(const std::string & workPath,const std::string & configPath)29 DailyController::DailyController(const std::string& workPath, const std::string& configPath)
30 {
31     dbHelper_ = std::make_unique<DailyDbHelper>(workPath);
32     config_ = std::make_unique<DailyConfig>(configPath);
33 }
34 
CheckThreshold(std::shared_ptr<SysEvent> event)35 bool DailyController::CheckThreshold(std::shared_ptr<SysEvent> event)
36 {
37     if (event == nullptr) {
38         HIVIEW_LOGW("event is null");
39         return false;
40     }
41 
42     // try to update cache to db and report db before checking
43     int64_t nowTime = TimeUtil::GetSeconds();
44     TryToUpdateCacheToDb(nowTime);
45     TryToReportDb(nowTime);
46 
47     // check the threshold of event
48     auto cacheKey = std::make_pair(event->domain_, event->eventName_);
49     int32_t threshold = GetThreshold(cacheKey, event->eventType_);
50     int32_t count = GetCount(cacheKey) + 1;
51 
52     // update cache and db after checking
53     UpdateCacheAndDb(cacheKey, threshold, count);
54     return config_->IsValid() ? (count <= threshold) : true;
55 }
56 
TryToUpdateCacheToDb(int64_t nowTime)57 void DailyController::TryToUpdateCacheToDb(int64_t nowTime)
58 {
59     if (CheckTimeOfCache(nowTime) || CheckSizeOfCache()) {
60         UpdateCacheToDb();
61     }
62 }
63 
CheckTimeOfCache(int64_t nowTime)64 bool DailyController::CheckTimeOfCache(int64_t nowTime)
65 {
66     static int64_t lastUpdateTime = 0;
67     if (lastUpdateTime == 0) {
68         lastUpdateTime = TimeUtil::GetSeconds();
69         return false;
70     }
71 
72     constexpr int64_t updateInterval = 600; // 10min
73     if (std::abs(nowTime - lastUpdateTime) <= updateInterval) {
74         return false;
75     }
76     lastUpdateTime = nowTime;
77     return true;
78 }
79 
CheckSizeOfCache()80 bool DailyController::CheckSizeOfCache()
81 {
82     constexpr size_t maxSizeOfCache = 1000;
83     return cache_.size() > maxSizeOfCache;
84 }
85 
UpdateCacheToDb()86 void DailyController::UpdateCacheToDb()
87 {
88     HIVIEW_LOGI("start to update cache to db, size=%{public}zu", cache_.size());
89     for (const auto& [key, value] : cache_) {
90         // means that the record has been inserted and does not need to be updated
91         if (value.count == COUNT_OF_INIT) {
92             continue;
93         }
94 
95         DailyDbHelper::EventInfo eventInfo = {
96             .domain = key.first,
97             .name = key.second,
98             .count = value.count,
99             .exceedTime = value.exceedTime,
100         };
101         if (dbHelper_->UpdateEventInfo(eventInfo) < 0) {
102             HIVIEW_LOGW("failed to update, domain=%{public}s, name=%{public}s",
103                 key.first.c_str(), key.second.c_str());
104         }
105     }
106     cache_.clear();
107 }
108 
TryToReportDb(int64_t nowTime)109 void DailyController::TryToReportDb(int64_t nowTime)
110 {
111     if (dbHelper_->NeedReport(nowTime)) {
112         UpdateCacheToDb();
113         dbHelper_->Report();
114     }
115 }
116 
GetThreshold(const CacheKey & cachekey,int32_t type)117 int32_t DailyController::GetThreshold(const CacheKey& cachekey, int32_t type)
118 {
119     if (cache_.find(cachekey) != cache_.end()) {
120         return cache_[cachekey].threshold;
121     }
122     int32_t threshold = config_->GetThreshold(cachekey.first, cachekey.second, type);
123     if (threshold < 0) {
124         HIVIEW_LOGW("failed to get threshold from config, threshold=%{public}d", threshold);
125         return 0;
126     }
127     return threshold;
128 }
129 
GetCount(const CacheKey & cachekey)130 int32_t DailyController::GetCount(const CacheKey& cachekey)
131 {
132     if (cache_.find(cachekey) != cache_.end()) {
133         return cache_[cachekey].count;
134     }
135 
136     DailyDbHelper::EventInfo eventInfo = {
137         .domain = cachekey.first,
138         .name = cachekey.second,
139     };
140     if (dbHelper_->QueryEventInfo(eventInfo) < 0) {
141         HIVIEW_LOGW("failed to query event info from db, count=%{public}d", eventInfo.count);
142         return 0;
143     }
144     return eventInfo.count;
145 }
146 
UpdateCacheAndDb(const CacheKey & cachekey,int32_t threshold,int32_t count)147 void DailyController::UpdateCacheAndDb(const CacheKey& cachekey, int32_t threshold, int32_t count)
148 {
149     // check the first time the event crosses the threshold
150     int64_t exceedTime = 0;
151     if (count == (threshold + 1)) {
152         exceedTime = TimeUtil::GetSeconds();
153         HIVIEW_LOGI("event first exceeds threshold, domain=%{public}s, name=%{public}s",
154             cachekey.first.c_str(), cachekey.second.c_str());
155     }
156 
157     UpdateCache(cachekey, threshold, count, exceedTime);
158     UpdateDb(cachekey, count, exceedTime);
159 }
160 
UpdateCache(const CacheKey & cachekey,int32_t threshold,int32_t count,int64_t exceedTime)161 void DailyController::UpdateCache(const CacheKey& cachekey, int32_t threshold, int32_t count, int64_t exceedTime)
162 {
163     if (cache_.find(cachekey) == cache_.end()) {
164         cache_[cachekey] = {
165             .threshold = threshold,
166             .count = count,
167             .exceedTime = exceedTime,
168         };
169         return;
170     }
171 
172     cache_[cachekey].count = count;
173     if (exceedTime != 0) {
174         cache_[cachekey].exceedTime = exceedTime;
175     }
176 }
177 
UpdateDb(const CacheKey & cachekey,int32_t count,int64_t exceedTime)178 void DailyController::UpdateDb(const CacheKey& cachekey, int32_t count, int64_t exceedTime)
179 {
180     // the record does not exist in the db, need to init the record
181     if (count == COUNT_OF_INIT) {
182         DailyDbHelper::EventInfo eventInfo = {
183             .domain = cachekey.first,
184             .name = cachekey.second,
185             .count = count,
186             .exceedTime = exceedTime,
187         };
188         if (dbHelper_->InsertEventInfo(eventInfo) < 0) {
189             HIVIEW_LOGW("failed to insert, domain=%{public}s, name=%{public}s",
190                 eventInfo.domain.c_str(), eventInfo.name.c_str());
191         }
192     }
193 }
194 } // namespace HiviewDFX
195 } // namespace OHOS
196