1 /*
2 * Copyright (c) 2023 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 "sys_event_database.h"
16
17 #include <unordered_map>
18
19 #include "event_store_config.h"
20 #include "file_util.h"
21 #include "hiview_global.h"
22 #include "logger.h"
23 #include "string_util.h"
24 #include "sys_event_dao.h"
25
26 namespace OHOS {
27 namespace HiviewDFX {
28 namespace EventStore {
29 DEFINE_LOG_TAG("HiView-SysEventDatabase");
30 namespace {
31 constexpr size_t DEFAULT_CAPACITY = 30;
32 const char FILE_DELIMIT_STR[] = "/";
33 const char FILE_NAME_DELIMIT_STR[] = "-";
34 constexpr size_t INDEX_FILE_SIZE = 0;
35 constexpr size_t INDEX_NORMAL_QUEUE = 1;
36 constexpr size_t INDEX_LIMIT_QUEUE = 2;
37
GetFileSeq(const std::string & file)38 int64_t GetFileSeq(const std::string& file)
39 {
40 return std::stoll(file.substr(file.rfind(FILE_NAME_DELIMIT_STR) + 1)); // 1 next char
41 }
42
GetFileDomain(const std::string & file)43 std::string GetFileDomain(const std::string& file)
44 {
45 std::vector<std::string> dirNames;
46 StringUtil::SplitStr(file, FILE_DELIMIT_STR, dirNames);
47 constexpr size_t domainOffset = 2;
48 return dirNames.size() < domainOffset ? "" : dirNames[dirNames.size() - domainOffset];
49 }
50
CompareFileLessFunc(const std::string & fileA,const std::string & fileB)51 bool CompareFileLessFunc(const std::string& fileA, const std::string& fileB)
52 {
53 return GetFileSeq(fileA) < GetFileSeq(fileB);
54 }
55
CompareFileGreaterFunc(const std::string & fileA,const std::string & fileB)56 bool CompareFileGreaterFunc(const std::string& fileA, const std::string& fileB)
57 {
58 return GetFileSeq(fileA) > GetFileSeq(fileB);
59 }
60 }
61
SysEventDatabase()62 SysEventDatabase::SysEventDatabase()
63 {
64 lruCache_ = std::make_unique<SysEventDocLruCache>(DEFAULT_CAPACITY);
65 }
66
GetDatabaseDir()67 std::string SysEventDatabase::GetDatabaseDir()
68 {
69 static std::string dir;
70 if (!dir.empty()) {
71 return dir;
72 }
73 auto& context = HiviewGlobal::GetInstance();
74 if (context == nullptr) {
75 HIVIEW_LOGE("hiview context is null");
76 return dir;
77 }
78 std::string workPath = context->GetHiViewDirectory(HiviewContext::DirectoryType::WORK_DIRECTORY);
79 dir = FileUtil::IncludeTrailingPathDelimiter(workPath) + "sys_event_db" + FILE_DELIMIT_STR;
80 if (!FileUtil::FileExists(dir)) {
81 if (FileUtil::ForceCreateDirectory(dir, FileUtil::FILE_PERM_770)) {
82 HIVIEW_LOGI("succeeded in creating sys_event_db path=%{public}s", dir.c_str());
83 } else {
84 dir = workPath;
85 HIVIEW_LOGW("failed to create sys_event_db path, use default=%{public}s", dir.c_str());
86 }
87 }
88 return dir;
89 }
90
Insert(const std::shared_ptr<SysEvent> & event)91 int SysEventDatabase::Insert(const std::shared_ptr<SysEvent>& event)
92 {
93 std::unique_lock<std::shared_mutex> lock(mutex_);
94 std::shared_ptr<SysEventDoc> sysEventDoc = nullptr;
95 auto keyOfCache = std::pair<std::string, std::string>(event->domain_, event->eventName_);
96 if (lruCache_->Contain(keyOfCache)) {
97 sysEventDoc = lruCache_->Get(keyOfCache);
98 } else {
99 sysEventDoc = std::make_shared<SysEventDoc>(event->domain_, event->eventName_);
100 lruCache_->Add(keyOfCache, sysEventDoc);
101 }
102 return sysEventDoc->Insert(event);
103 }
104
Clear()105 void SysEventDatabase::Clear()
106 {
107 if (quotaMap_.empty()) {
108 // init the quota for clearing each type of events
109 InitQuotaMap();
110 }
111
112 std::unique_lock<std::shared_mutex> lock(mutex_);
113 UpdateClearMap();
114 if (!clearMap_.empty()) {
115 ClearCache(); // need to close the open files before clear
116 }
117 for (auto it = clearMap_.begin(); it != clearMap_.end(); ++it) {
118 const double delPct = 0.1;
119 uint64_t maxSize = GetMaxSize(it->first);
120 uint64_t totalFileSize = std::get<INDEX_FILE_SIZE>(it->second);
121 if (totalFileSize < (maxSize + maxSize * delPct)) {
122 HIVIEW_LOGI("do not clear type=%{public}d, curSize=%{public}" PRIu64 ", maxSize=%{public}" PRIu64,
123 it->first, totalFileSize, maxSize);
124 continue;
125 }
126
127 auto& normalQueue = std::get<INDEX_NORMAL_QUEUE>(it->second);
128 auto& limitQueue = std::get<INDEX_LIMIT_QUEUE>(it->second);
129 while (totalFileSize >= maxSize) {
130 std::string delFile;
131 if (!limitQueue.empty()) {
132 delFile = limitQueue.top();
133 limitQueue.pop();
134 } else if (!normalQueue.empty()) {
135 delFile = normalQueue.top();
136 normalQueue.pop();
137 } else {
138 break;
139 }
140
141 auto fileSize = FileUtil::GetFileSize(delFile);
142 if (!FileUtil::RemoveFile(delFile)) {
143 HIVIEW_LOGI("failed to remove file=%{public}s", delFile.c_str());
144 continue;
145 }
146 HIVIEW_LOGI("success to remove file=%{public}s", delFile.c_str());
147 totalFileSize = totalFileSize >= fileSize ? (totalFileSize - fileSize) : 0;
148 }
149 HIVIEW_LOGI("end to clear type=%{public}d, curSize=%{public}" PRIu64 ", maxSize=%{public}" PRIu64,
150 it->first, totalFileSize, maxSize);
151 }
152 }
153
Query(SysEventQuery & sysEventQuery,EntryQueue & entries)154 int SysEventDatabase::Query(SysEventQuery& sysEventQuery, EntryQueue& entries)
155 {
156 std::shared_lock<std::shared_mutex> lock(mutex_);
157 FileQueue queryFiles(CompareFileLessFunc);
158 const auto& queryArg = sysEventQuery.queryArg_;
159 GetQueryFiles(queryArg, queryFiles);
160 HIVIEW_LOGD("get the file list, size=%{public}zu", queryFiles.size());
161
162 if (queryFiles.empty()) {
163 return DOC_STORE_SUCCESS;
164 }
165
166 return QueryByFiles(sysEventQuery, entries, queryFiles);
167 }
168
InitQuotaMap()169 void SysEventDatabase::InitQuotaMap()
170 {
171 const int eventTypes[] = { 1, 2, 3, 4 }; // for fault, statistic, security and behavior event
172 for (auto eventType : eventTypes) {
173 auto maxSize = EventStoreConfig::GetInstance().GetMaxSize(eventType) * NUM_OF_BYTES_IN_MB;
174 auto maxFileNum = EventStoreConfig::GetInstance().GetMaxFileNum(eventType);
175 quotaMap_.insert({eventType, {maxSize, maxFileNum}});
176 }
177 }
178
UpdateClearMap()179 void SysEventDatabase::UpdateClearMap()
180 {
181 // clear the map
182 clearMap_.clear();
183
184 // get all event files
185 FileQueue files(CompareFileLessFunc);
186 GetQueryFiles(SysEventQueryArg(), files);
187
188 // build clear map
189 std::unordered_map<std::string, uint32_t> nameLimitMap;
190 while (!files.empty()) {
191 std::string file = files.top();
192 files.pop();
193 std::string fileName = file.substr(file.rfind(FILE_DELIMIT_STR) + 1); // 1 for skipping '/'
194 std::vector<std::string> splitNames;
195 StringUtil::SplitStr(fileName, "-", splitNames);
196 if (splitNames.size() != FILE_NAME_SPLIT_SIZE) {
197 HIVIEW_LOGI("invalid clear file=%{public}s", file.c_str());
198 continue;
199 }
200
201 std::string name = splitNames[EVENT_NAME_INDEX];
202 std::string domainNameStr = GetFileDomain(file) + name;
203 nameLimitMap[domainNameStr]++;
204 int type = std::stoi(splitNames[EVENT_TYPE_INDEX]);
205 uint64_t fileSize = FileUtil::GetFileSize(file);
206 if (clearMap_.find(type) == clearMap_.end()) {
207 FileQueue fileQueue(CompareFileGreaterFunc);
208 fileQueue.emplace(file);
209 clearMap_.insert({type, std::make_tuple(fileSize, fileQueue, FileQueue(CompareFileGreaterFunc))});
210 continue;
211 }
212
213 auto& clearTuple = clearMap_.at(type);
214 std::get<INDEX_FILE_SIZE>(clearTuple) += fileSize;
215 if (nameLimitMap[domainNameStr] > GetMaxFileNum(type)) {
216 std::get<INDEX_LIMIT_QUEUE>(clearTuple).emplace(file);
217 } else {
218 std::get<INDEX_NORMAL_QUEUE>(clearTuple).emplace(file);
219 }
220 }
221 }
222
ClearCache()223 void SysEventDatabase::ClearCache()
224 {
225 HIVIEW_LOGI("start to clear lru cache");
226 lruCache_->Clear();
227 }
228
GetMaxFileNum(int type)229 uint32_t SysEventDatabase::GetMaxFileNum(int type)
230 {
231 if (quotaMap_.empty() || quotaMap_.find(type) == quotaMap_.end()) {
232 return 0;
233 }
234 return quotaMap_.at(type).second;
235 }
236
GetMaxSize(int type)237 uint64_t SysEventDatabase::GetMaxSize(int type)
238 {
239 if (quotaMap_.empty() || quotaMap_.find(type) == quotaMap_.end()) {
240 return 0;
241 }
242 return quotaMap_.at(type).first;
243 }
244
GetQueryFiles(const SysEventQueryArg & queryArg,FileQueue & queryFiles)245 void SysEventDatabase::GetQueryFiles(const SysEventQueryArg& queryArg, FileQueue& queryFiles)
246 {
247 std::vector<std::string> queryDirs;
248 GetQueryDirsByDomain(queryArg.domain, queryDirs);
249 if (queryDirs.empty()) {
250 return;
251 }
252
253 for (auto& queryDir : queryDirs) {
254 std::vector<std::string> files;
255 FileUtil::GetDirFiles(queryDir, files);
256 for (auto& file : files) {
257 if (IsContainQueryArg(file, queryArg)) {
258 queryFiles.emplace(file);
259 HIVIEW_LOGD("add query file=%{public}s", file.c_str());
260 }
261 }
262 }
263 }
264
GetQueryDirsByDomain(const std::string & domain,std::vector<std::string> & queryDirs)265 void SysEventDatabase::GetQueryDirsByDomain(const std::string& domain, std::vector<std::string>& queryDirs)
266 {
267 if (domain.empty()) {
268 FileUtil::GetDirDirs(GetDatabaseDir(), queryDirs);
269 } else {
270 std::string domainDir = GetDatabaseDir() + domain;
271 if (FileUtil::IsDirectory(domainDir)) {
272 queryDirs.push_back(domainDir + FILE_DELIMIT_STR);
273 }
274 }
275 }
276
IsContainQueryArg(const std::string file,const SysEventQueryArg & queryArg)277 bool SysEventDatabase::IsContainQueryArg(const std::string file, const SysEventQueryArg& queryArg)
278 {
279 if (queryArg.names.empty() && queryArg.type == 0 && queryArg.toSeq == INVALID_VALUE_INT) {
280 return true;
281 }
282 std::string fileName = file.substr(file.rfind(FILE_DELIMIT_STR) + 1); // 1 for next char
283 std::vector<std::string> splitStrs;
284 StringUtil::SplitStr(fileName, FILE_NAME_DELIMIT_STR, splitStrs);
285 if (splitStrs.size() < FILE_NAME_SPLIT_SIZE) {
286 HIVIEW_LOGE("invalid file name, file=%{public}s", fileName.c_str());
287 return false;
288 }
289 if (!queryArg.names.empty() && !std::any_of(queryArg.names.begin(), queryArg.names.end(),
290 [&splitStrs] (auto& item) {
291 return item == splitStrs[EVENT_NAME_INDEX];
292 })) {
293 return false;
294 }
295 if (queryArg.type != 0 && splitStrs[EVENT_TYPE_INDEX] != StringUtil::ToString(queryArg.type)) {
296 return false;
297 }
298 if (queryArg.toSeq != INVALID_VALUE_INT && std::stoll(splitStrs[EVENT_SEQ_INDEX]) >= queryArg.toSeq) {
299 return false;
300 }
301 return true;
302 }
303
QueryByFiles(SysEventQuery & sysEventQuery,EntryQueue & entries,FileQueue & queryFiles)304 int SysEventDatabase::QueryByFiles(SysEventQuery& sysEventQuery, EntryQueue& entries, FileQueue& queryFiles)
305 {
306 DocQuery docQuery;
307 sysEventQuery.BuildDocQuery(docQuery);
308 int totalNum = 0;
309 while (!queryFiles.empty()) {
310 std::string file = queryFiles.top();
311 queryFiles.pop();
312 auto sysEventDoc = std::make_shared<SysEventDoc>(file);
313 if (auto res = sysEventDoc->Query(docQuery, entries, totalNum); res != DOC_STORE_SUCCESS) {
314 HIVIEW_LOGE("failed to query event from doc, file=%{public}s, res=%{public}d", file.c_str(), res);
315 continue;
316 }
317 if (totalNum >= sysEventQuery.limit_) {
318 sysEventQuery.queryArg_.toSeq = GetFileSeq(file);
319 break;
320 }
321 }
322 HIVIEW_LOGI("query end, limit=%{public}d, totalNum=%{public}d", sysEventQuery.limit_, totalNum);
323 return DOC_STORE_SUCCESS;
324 }
325 } // EventStore
326 } // HiviewDFX
327 } // OHOS
328