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