1 /*
2 * Copyright (c) 2023-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 "sys_event_database.h"
16
17 #include <sstream>
18 #include <unordered_map>
19
20 #include "event_store_config.h"
21 #include "file_util.h"
22 #include "hisysevent.h"
23 #include "hiview_global.h"
24 #include "hiview_logger.h"
25 #include "hiview_zip_util.h"
26 #include "string_util.h"
27 #include "sys_event_dao.h"
28 #include "sys_event_repeat_guard.h"
29
30 namespace OHOS {
31 namespace HiviewDFX {
32 namespace EventStore {
33 DEFINE_LOG_TAG("HiView-SysEventDatabase");
34 namespace {
35 constexpr size_t DEFAULT_CAPACITY = 30;
36 const char FILE_DELIMIT_STR[] = "/";
37 const char FILE_NAME_DELIMIT_STR[] = "-";
38 constexpr size_t INDEX_FILE_SIZE = 0;
39 constexpr size_t INDEX_NORMAL_QUEUE = 1;
40 constexpr size_t INDEX_LIMIT_QUEUE = 2;
41
GetFileSeq(const std::string & file)42 int64_t GetFileSeq(const std::string& file)
43 {
44 std::stringstream ss;
45 ss << file.substr(file.rfind(FILE_NAME_DELIMIT_STR) + 1);
46 long long seq = 0;
47 ss >> seq;
48 return seq;
49 }
50
GetFileDomain(const std::string & file)51 std::string GetFileDomain(const std::string& file)
52 {
53 std::vector<std::string> dirNames;
54 StringUtil::SplitStr(file, FILE_DELIMIT_STR, dirNames);
55 constexpr size_t domainOffset = 2;
56 return dirNames.size() < domainOffset ? "" : dirNames[dirNames.size() - domainOffset];
57 }
58
CompareFileLessFunc(const std::string & fileA,const std::string & fileB)59 bool CompareFileLessFunc(const std::string& fileA, const std::string& fileB)
60 {
61 return GetFileSeq(fileA) < GetFileSeq(fileB);
62 }
63
CompareFileGreaterFunc(const std::string & fileA,const std::string & fileB)64 bool CompareFileGreaterFunc(const std::string& fileA, const std::string& fileB)
65 {
66 return GetFileSeq(fileA) > GetFileSeq(fileB);
67 }
68
GetEventTypeFromFileName(const std::string & fileName)69 std::string GetEventTypeFromFileName(const std::string& fileName)
70 {
71 std::vector<std::string> splitStrs;
72 StringUtil::SplitStr(fileName, FILE_NAME_DELIMIT_STR, splitStrs);
73 if (splitStrs.size() < FILE_NAME_SPLIT_SIZE) {
74 HIVIEW_LOGW("invalid file name, file=%{public}s", fileName.c_str());
75 return "";
76 }
77 return splitStrs[EVENT_TYPE_INDEX];
78 }
79 }
80
SysEventDatabase()81 SysEventDatabase::SysEventDatabase()
82 {
83 lruCache_ = std::make_unique<SysEventDocLruCache>(DEFAULT_CAPACITY);
84 SysEventRepeatGuard::RegisterListeningUeSwitch();
85 }
86
~SysEventDatabase()87 SysEventDatabase::~SysEventDatabase()
88 {
89 SysEventRepeatGuard::UnregisterListeningUeSwitch();
90 }
91
GetDatabaseDir()92 std::string SysEventDatabase::GetDatabaseDir()
93 {
94 static std::string dir;
95 if (!dir.empty()) {
96 return dir;
97 }
98 auto& context = HiviewGlobal::GetInstance();
99 if (context == nullptr) {
100 HIVIEW_LOGE("hiview context is null");
101 return dir;
102 }
103 std::string workPath = context->GetHiViewDirectory(HiviewContext::DirectoryType::WORK_DIRECTORY);
104 dir = FileUtil::IncludeTrailingPathDelimiter(workPath) + "sys_event_db" + FILE_DELIMIT_STR;
105 if (!FileUtil::FileExists(dir)) {
106 if (FileUtil::ForceCreateDirectory(dir, FileUtil::FILE_PERM_770)) {
107 HIVIEW_LOGI("succeeded in creating sys_event_db path=%{public}s", dir.c_str());
108 } else {
109 dir = workPath;
110 HIVIEW_LOGW("failed to create sys_event_db path, use default=%{public}s", dir.c_str());
111 }
112 }
113 return dir;
114 }
115
Insert(const std::shared_ptr<SysEvent> & event)116 int SysEventDatabase::Insert(const std::shared_ptr<SysEvent>& event)
117 {
118 std::unique_lock<std::shared_mutex> lock(mutex_);
119 std::shared_ptr<SysEventDoc> sysEventDoc = nullptr;
120 auto keyOfCache = std::pair<std::string, std::string>(event->domain_, event->eventName_);
121 if (lruCache_->Contain(keyOfCache)) {
122 sysEventDoc = lruCache_->Get(keyOfCache);
123 } else {
124 sysEventDoc = std::make_shared<SysEventDoc>(event->domain_, event->eventName_);
125 lruCache_->Add(keyOfCache, sysEventDoc);
126 }
127 return sysEventDoc->Insert(event);
128 }
129
CheckRepeat(SysEvent & event)130 void SysEventDatabase::CheckRepeat(SysEvent& event)
131 {
132 SysEventRepeatGuard::Check(event);
133 }
134
Backup(const std::string & zipFilePath)135 bool SysEventDatabase::Backup(const std::string& zipFilePath)
136 {
137 HIVIEW_LOGI("start backup.");
138 std::shared_lock<std::shared_mutex> lock(mutex_);
139 std::vector<std::string> domainDirs;
140 std::string dbDir(GetDatabaseDir());
141 FileUtil::GetDirDirs(dbDir, domainDirs);
142 if (domainDirs.empty()) {
143 HIVIEW_LOGI("no event files exist.");
144 return false;
145 }
146 std::string seqFilePath(dbDir + "event_sequence");
147 if (!FileUtil::FileExists(seqFilePath)) {
148 HIVIEW_LOGW("seq id file not exist.");
149 return false;
150 }
151
152 HiviewZipUnit zipUnit(zipFilePath);
153 if (int32_t ret = zipUnit.AddFileInZip(seqFilePath, ZipFileLevel::KEEP_NONE_PARENT_PATH); ret != 0) {
154 HIVIEW_LOGW("zip seq id file failed, ret: %{public}d.", ret);
155 return false;
156 }
157
158 const std::string faultType(std::to_string(HiSysEvent::EventType::FAULT));
159 for (const auto& domainDir : domainDirs) {
160 std::vector<std::string> eventFiles;
161 FileUtil::GetDirFiles(domainDir, eventFiles, false);
162 for (const auto& eventFile : eventFiles) {
163 std::string fileName(FileUtil::ExtractFileName(eventFile));
164 if (GetEventTypeFromFileName(fileName) != faultType) {
165 continue;
166 }
167 if (int32_t ret = zipUnit.AddFileInZip(eventFile, ZipFileLevel::KEEP_ONE_PARENT_PATH); ret != 0) {
168 HIVIEW_LOGW("zip file failed: %{public}s, ret: %{public}d", fileName.c_str(), ret);
169 return false;
170 }
171 }
172 }
173 HIVIEW_LOGI("finish backup.");
174 return true;
175 }
176
Restore(const std::string & zipFilePath,const std::string & restoreDir)177 bool SysEventDatabase::Restore(const std::string& zipFilePath, const std::string& restoreDir)
178 {
179 // no need to get lock, for restore only be called during plugin loaded time
180 HIVIEW_LOGI("start restore.");
181 HiviewUnzipUnit unzipUnit(zipFilePath, restoreDir);
182 if (!unzipUnit.UnzipFile()) {
183 HIVIEW_LOGW("unzip event backup file failed.");
184 return false;
185 }
186
187 std::string seqFilePath(restoreDir + "event_sequence");
188 if (!FileUtil::FileExists(seqFilePath)) {
189 HIVIEW_LOGW("seq id file not exist in zip file.");
190 return false;
191 }
192 HIVIEW_LOGI("finish restore.");
193 return true;
194 }
195
Clear()196 void SysEventDatabase::Clear()
197 {
198 if (quotaMap_.empty()) {
199 // init the quota for clearing each type of events
200 InitQuotaMap();
201 }
202
203 std::unique_lock<std::shared_mutex> lock(mutex_);
204 UpdateClearMap();
205 if (!clearMap_.empty()) {
206 ClearCache(); // need to close the open files before clear
207 }
208 for (auto it = clearMap_.begin(); it != clearMap_.end(); ++it) {
209 const double delPct = 0.1;
210 uint64_t maxSize = GetMaxSize(it->first);
211 uint64_t totalFileSize = std::get<INDEX_FILE_SIZE>(it->second);
212 if (totalFileSize < (maxSize + maxSize * delPct)) {
213 HIVIEW_LOGI("do not clear type=%{public}d, curSize=%{public}" PRIu64 ", maxSize=%{public}" PRIu64,
214 it->first, totalFileSize, maxSize);
215 continue;
216 }
217
218 auto& normalQueue = std::get<INDEX_NORMAL_QUEUE>(it->second);
219 auto& limitQueue = std::get<INDEX_LIMIT_QUEUE>(it->second);
220 while (totalFileSize >= maxSize) {
221 std::string delFile;
222 if (!limitQueue.empty()) {
223 delFile = limitQueue.top();
224 limitQueue.pop();
225 } else if (!normalQueue.empty()) {
226 delFile = normalQueue.top();
227 normalQueue.pop();
228 } else {
229 break;
230 }
231
232 auto fileSize = FileUtil::GetFileSize(delFile);
233 if (!FileUtil::RemoveFile(delFile)) {
234 HIVIEW_LOGI("failed to remove file=%{public}s", delFile.c_str());
235 continue;
236 }
237 HIVIEW_LOGI("success to remove file=%{public}s", delFile.c_str());
238 totalFileSize = totalFileSize >= fileSize ? (totalFileSize - fileSize) : 0;
239 }
240 HIVIEW_LOGI("end to clear type=%{public}d, curSize=%{public}" PRIu64 ", maxSize=%{public}" PRIu64,
241 it->first, totalFileSize, maxSize);
242 }
243 }
244
Query(SysEventQuery & sysEventQuery,EntryQueue & entries)245 int SysEventDatabase::Query(SysEventQuery& sysEventQuery, EntryQueue& entries)
246 {
247 std::shared_lock<std::shared_mutex> lock(mutex_);
248 FileQueue queryFiles(CompareFileLessFunc);
249 const auto& queryArg = sysEventQuery.queryArg_;
250 GetQueryFiles(queryArg, queryFiles);
251 HIVIEW_LOGD("get the file list, size=%{public}zu", queryFiles.size());
252
253 if (queryFiles.empty()) {
254 return DOC_STORE_SUCCESS;
255 }
256
257 return QueryByFiles(sysEventQuery, entries, queryFiles);
258 }
259
InitQuotaMap()260 void SysEventDatabase::InitQuotaMap()
261 {
262 const int eventTypes[] = { 1, 2, 3, 4 }; // for fault, statistic, security and behavior event
263 for (auto eventType : eventTypes) {
264 auto maxSize = EventStoreConfig::GetInstance().GetMaxSize(eventType) * NUM_OF_BYTES_IN_MB;
265 auto maxFileNum = EventStoreConfig::GetInstance().GetMaxFileNum(eventType);
266 quotaMap_.insert({eventType, {maxSize, maxFileNum}});
267 }
268 }
269
UpdateClearMap()270 void SysEventDatabase::UpdateClearMap()
271 {
272 // clear the map
273 clearMap_.clear();
274
275 // get all event files
276 FileQueue files(CompareFileLessFunc);
277 GetQueryFiles(SysEventQueryArg(), files);
278
279 // build clear map
280 std::unordered_map<std::string, uint32_t> nameLimitMap;
281 while (!files.empty()) {
282 std::string file = files.top();
283 files.pop();
284 std::string fileName = file.substr(file.rfind(FILE_DELIMIT_STR) + 1); // 1 for skipping '/'
285 std::vector<std::string> splitNames;
286 StringUtil::SplitStr(fileName, "-", splitNames);
287 if (splitNames.size() != FILE_NAME_SPLIT_SIZE) {
288 HIVIEW_LOGI("invalid clear file=%{public}s", file.c_str());
289 continue;
290 }
291
292 std::string name = splitNames[EVENT_NAME_INDEX];
293 std::string domainNameStr = GetFileDomain(file) + name;
294 nameLimitMap[domainNameStr]++;
295 int type = std::strtol(splitNames[EVENT_TYPE_INDEX].c_str(), nullptr, 0);
296 uint64_t fileSize = FileUtil::GetFileSize(file);
297 if (clearMap_.find(type) == clearMap_.end()) {
298 FileQueue fileQueue(CompareFileGreaterFunc);
299 fileQueue.emplace(file);
300 clearMap_.insert({type, std::make_tuple(fileSize, fileQueue, FileQueue(CompareFileGreaterFunc))});
301 continue;
302 }
303
304 auto& clearTuple = clearMap_.at(type);
305 std::get<INDEX_FILE_SIZE>(clearTuple) += fileSize;
306 if (nameLimitMap[domainNameStr] > GetMaxFileNum(type)) {
307 std::get<INDEX_LIMIT_QUEUE>(clearTuple).emplace(file);
308 } else {
309 std::get<INDEX_NORMAL_QUEUE>(clearTuple).emplace(file);
310 }
311 }
312 }
313
ClearCache()314 void SysEventDatabase::ClearCache()
315 {
316 HIVIEW_LOGI("start to clear lru cache");
317 lruCache_->Clear();
318 }
319
GetMaxFileNum(int type)320 uint32_t SysEventDatabase::GetMaxFileNum(int type)
321 {
322 if (quotaMap_.empty() || quotaMap_.find(type) == quotaMap_.end()) {
323 return 0;
324 }
325 return quotaMap_.at(type).second;
326 }
327
GetMaxSize(int type)328 uint64_t SysEventDatabase::GetMaxSize(int type)
329 {
330 if (quotaMap_.empty() || quotaMap_.find(type) == quotaMap_.end()) {
331 return 0;
332 }
333 return quotaMap_.at(type).first;
334 }
335
GetQueryFiles(const SysEventQueryArg & queryArg,FileQueue & queryFiles)336 void SysEventDatabase::GetQueryFiles(const SysEventQueryArg& queryArg, FileQueue& queryFiles)
337 {
338 std::vector<std::string> queryDirs;
339 GetQueryDirsByDomain(queryArg.domain, queryDirs);
340 if (queryDirs.empty()) {
341 return;
342 }
343
344 for (const auto& queryDir : queryDirs) {
345 std::vector<std::string> files;
346 FileUtil::GetDirFiles(queryDir, files);
347 sort(files.begin(), files.end(), CompareFileGreaterFunc);
348 std::unordered_map<std::string, long long> nameSeqMap;
349 for (const auto& file : files) {
350 if (IsContainQueryArg(file, queryArg, nameSeqMap)) {
351 queryFiles.emplace(file);
352 HIVIEW_LOGD("add query file=%{public}s", file.c_str());
353 }
354 }
355 }
356 }
357
GetQueryDirsByDomain(const std::string & domain,std::vector<std::string> & queryDirs)358 void SysEventDatabase::GetQueryDirsByDomain(const std::string& domain, std::vector<std::string>& queryDirs)
359 {
360 if (domain.empty()) {
361 FileUtil::GetDirDirs(GetDatabaseDir(), queryDirs);
362 } else {
363 std::string domainDir = GetDatabaseDir() + domain;
364 if (FileUtil::IsDirectory(domainDir)) {
365 queryDirs.push_back(domainDir + FILE_DELIMIT_STR);
366 }
367 }
368 }
369
IsContainQueryArg(const std::string & file,const SysEventQueryArg & queryArg,std::unordered_map<std::string,long long> & nameSeqMap)370 bool SysEventDatabase::IsContainQueryArg(const std::string& file, const SysEventQueryArg& queryArg,
371 std::unordered_map<std::string, long long>& nameSeqMap)
372 {
373 if (queryArg.names.empty() && queryArg.type == 0 && queryArg.toSeq == INVALID_VALUE_INT) {
374 return true;
375 }
376 std::string fileName = file.substr(file.rfind(FILE_DELIMIT_STR) + 1); // 1 for next char
377 std::vector<std::string> splitStrs;
378 StringUtil::SplitStr(fileName, FILE_NAME_DELIMIT_STR, splitStrs);
379 if (splitStrs.size() < FILE_NAME_SPLIT_SIZE) {
380 HIVIEW_LOGE("invalid file name, file=%{public}s", fileName.c_str());
381 return false;
382 }
383 std::string eventName = splitStrs[EVENT_NAME_INDEX];
384 std::string eventType = splitStrs[EVENT_TYPE_INDEX];
385 long long eventSeq = std::strtoll(splitStrs[EVENT_SEQ_INDEX].c_str(), nullptr, 0);
386 auto iter = nameSeqMap.find(eventName);
387 if (iter != nameSeqMap.end() && iter->second <= queryArg.fromSeq) {
388 return false;
389 }
390 nameSeqMap[eventName] = eventSeq;
391 if (!queryArg.names.empty() && !std::any_of(queryArg.names.begin(), queryArg.names.end(),
392 [&eventName] (auto& item) {
393 return item == eventName;
394 })) {
395 return false;
396 }
397 if (queryArg.type != 0 && eventType != StringUtil::ToString(queryArg.type)) {
398 return false;
399 }
400 if (queryArg.toSeq != INVALID_VALUE_INT && eventSeq >= queryArg.toSeq) {
401 return false;
402 }
403 return true;
404 }
405
QueryByFiles(SysEventQuery & sysEventQuery,EntryQueue & entries,FileQueue & queryFiles)406 int SysEventDatabase::QueryByFiles(SysEventQuery& sysEventQuery, EntryQueue& entries, FileQueue& queryFiles)
407 {
408 DocQuery docQuery;
409 sysEventQuery.BuildDocQuery(docQuery);
410 int totalNum = 0;
411 while (!queryFiles.empty()) {
412 std::string file = queryFiles.top();
413 queryFiles.pop();
414 auto sysEventDoc = std::make_shared<SysEventDoc>(file);
415 if (auto res = sysEventDoc->Query(docQuery, entries, totalNum); res != DOC_STORE_SUCCESS) {
416 HIVIEW_LOGE("failed to query event from doc, file=%{public}s, res=%{public}d", file.c_str(), res);
417 continue;
418 }
419 if (totalNum >= sysEventQuery.limit_) {
420 sysEventQuery.queryArg_.toSeq = GetFileSeq(file);
421 break;
422 }
423 }
424 HIVIEW_LOGD("query end, limit=%{public}d, totalNum=%{public}d", sysEventQuery.limit_, totalNum);
425 return DOC_STORE_SUCCESS;
426 }
427 } // EventStore
428 } // HiviewDFX
429 } // OHOS
430