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