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