• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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