• 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 "cpu_storage.h"
16 
17 #include <algorithm>
18 #include <cmath>
19 
20 #include "file_util.h"
21 #include "hisysevent.h"
22 #include "logger.h"
23 #include "process_status.h"
24 #include "rdb_helper.h"
25 #include "sql_util.h"
26 #include "string_util.h"
27 #include "time_util.h"
28 
29 namespace OHOS {
30 namespace HiviewDFX {
31 DEFINE_LOG_TAG("HiView-CpuStorage");
32 using namespace OHOS::HiviewDFX::UCollectUtil;
33 namespace {
34 constexpr int32_t DB_VERSION = 1;
35 const std::string TABLE_NAME = "unified_collection_cpu";
36 const std::string COLUMN_START_TIME = "start_time";
37 const std::string COLUMN_END_TIME = "end_time";
38 const std::string COLUMN_PID = "pid";
39 const std::string COLUMN_PROC_NAME = "proc_name";
40 const std::string COLUMN_PROC_STATE = "proc_state";
41 const std::string COLUMN_CPU_LOAD = "cpu_load";
42 const std::string COLUMN_CPU_USAGE = "cpu_usage";
43 constexpr uint32_t MAX_NUM_OF_DB_FILES = 7; // save files for one week
44 constexpr uint32_t DEFAULT_PRECISION_OF_DECIMAL = 6; // 0.123456
45 
CreateDbFileName()46 std::string CreateDbFileName()
47 {
48     std::string dbFileName;
49     std::string dateStr = TimeUtil::TimestampFormatToDate(std::time(nullptr), "%Y%m%d");
50     dbFileName.append("cpu_stat_").append(dateStr).append(".db"); // cpu_stat_yyyymmdd.db
51     return dbFileName;
52 }
53 
NeedCleanDbFiles(const std::vector<std::string> & dbFiles)54 bool NeedCleanDbFiles(const std::vector<std::string>& dbFiles)
55 {
56     return dbFiles.size() > MAX_NUM_OF_DB_FILES;
57 }
58 
ClearDbFilesByTimestampOrder(const std::vector<std::string> & dbFiles)59 void ClearDbFilesByTimestampOrder(const std::vector<std::string>& dbFiles)
60 {
61     uint32_t numOfCleanFiles = dbFiles.size() - MAX_NUM_OF_DB_FILES;
62     for (size_t i = 0; i < numOfCleanFiles; i++) {
63         HIVIEW_LOGI("start to clear db file=%{public}s", dbFiles[i].c_str());
64         if (!FileUtil::RemoveFile(dbFiles[i])) {
65             HIVIEW_LOGW("failed to delete db file=%{public}s", dbFiles[i].c_str());
66         }
67     }
68 }
69 
IsDbFile(const std::string & dbFilePath)70 bool IsDbFile(const std::string& dbFilePath)
71 {
72     std::string dbFileName = FileUtil::ExtractFileName(dbFilePath);
73     std::string dbFileExt = FileUtil::ExtractFileExt(dbFileName);
74     return dbFileExt == "db";
75 }
76 
IsValidProcess(const ProcessCpuStatInfo & cpuCollectionInfo)77 bool IsValidProcess(const ProcessCpuStatInfo& cpuCollectionInfo)
78 {
79     return (cpuCollectionInfo.pid > 0) && (!cpuCollectionInfo.procName.empty());
80 }
81 
IsValidCpuLoad(const ProcessCpuStatInfo & cpuCollectionInfo)82 bool IsValidCpuLoad(const ProcessCpuStatInfo& cpuCollectionInfo)
83 {
84     constexpr double storeFilteringThresholdOfCpuLoad = 0.0005; // 0.05%
85     return cpuCollectionInfo.cpuLoad >= storeFilteringThresholdOfCpuLoad;
86 }
87 
IsInvalidCpuLoad(const ProcessCpuStatInfo & cpuCollectionInfo)88 bool IsInvalidCpuLoad(const ProcessCpuStatInfo& cpuCollectionInfo)
89 {
90     return cpuCollectionInfo.cpuLoad == 0;
91 }
92 
IsValidCpuUsage(const ProcessCpuStatInfo & cpuCollectionInfo)93 bool IsValidCpuUsage(const ProcessCpuStatInfo& cpuCollectionInfo)
94 {
95     constexpr double storeFilteringThresholdOfCpuUsage = 0.0005; // 0.05%
96     return cpuCollectionInfo.cpuUsage >= storeFilteringThresholdOfCpuUsage;
97 }
98 
NeedStoreInDb(const ProcessCpuStatInfo & cpuCollectionInfo)99 bool NeedStoreInDb(const ProcessCpuStatInfo& cpuCollectionInfo)
100 {
101     if (!IsValidProcess(cpuCollectionInfo)) {
102         static uint32_t invalidProcNum = 0;
103         invalidProcNum++;
104         constexpr uint32_t logLimitNum = 1000;
105         if (invalidProcNum % logLimitNum == 0) {
106             HIVIEW_LOGW("invalid process num=%{public}u, pid=%{public}d, name=%{public}s",
107                 invalidProcNum, cpuCollectionInfo.pid, cpuCollectionInfo.procName.c_str());
108         }
109         return false;
110     }
111     return IsValidCpuLoad(cpuCollectionInfo)
112         || (IsInvalidCpuLoad(cpuCollectionInfo) && IsValidCpuUsage(cpuCollectionInfo));
113 }
114 
TruncateDecimalWithNBitPrecision(double decimal,uint32_t precision=DEFAULT_PRECISION_OF_DECIMAL)115 double TruncateDecimalWithNBitPrecision(double decimal, uint32_t precision = DEFAULT_PRECISION_OF_DECIMAL)
116 {
117     auto truncateCoefficient = std::pow(10, precision);
118     return std::floor(decimal * truncateCoefficient) / truncateCoefficient;
119 }
120 
IsForegroundStateInCollectionPeriod(const ProcessCpuStatInfo & cpuCollectionInfo)121 bool IsForegroundStateInCollectionPeriod(const ProcessCpuStatInfo& cpuCollectionInfo)
122 {
123     int32_t pid = cpuCollectionInfo.pid;
124     ProcessState procState = ProcessStatus::GetInstance().GetProcessState(pid);
125     if (procState == FOREGROUND) {
126         return true;
127     }
128     uint64_t procForegroundTime = ProcessStatus::GetInstance().GetProcessLastForegroundTime(pid);
129     return (procForegroundTime >= cpuCollectionInfo.startTime && procForegroundTime < cpuCollectionInfo.endTime);
130 }
131 
GetProcessStateInCollectionPeriod(const ProcessCpuStatInfo & cpuCollectionInfo)132 int32_t GetProcessStateInCollectionPeriod(const ProcessCpuStatInfo& cpuCollectionInfo)
133 {
134     return IsForegroundStateInCollectionPeriod(cpuCollectionInfo)
135         ? static_cast<int32_t>(FOREGROUND)
136         : static_cast<int32_t>(ProcessStatus::GetInstance().GetProcessState(cpuCollectionInfo.pid));
137 }
138 }
139 
140 class CpuDbStoreCallback : public NativeRdb::RdbOpenCallback {
141 public:
142     int OnCreate(NativeRdb::RdbStore &rdbStore) override;
143     int OnUpgrade(NativeRdb::RdbStore &rdbStore, int oldVersion, int newVersion) override;
144 };
145 
OnCreate(NativeRdb::RdbStore & rdbStore)146 int CpuDbStoreCallback::OnCreate(NativeRdb::RdbStore& rdbStore)
147 {
148     HIVIEW_LOGD("create dbStore");
149     return NativeRdb::E_OK;
150 }
151 
OnUpgrade(NativeRdb::RdbStore & rdbStore,int oldVersion,int newVersion)152 int CpuDbStoreCallback::OnUpgrade(NativeRdb::RdbStore& rdbStore, int oldVersion, int newVersion)
153 {
154     HIVIEW_LOGD("oldVersion=%{public}d, newVersion=%{public}d", oldVersion, newVersion);
155     return NativeRdb::E_OK;
156 }
157 
CpuStorage(const std::string & workPath)158 CpuStorage::CpuStorage(const std::string& workPath) : workPath_(workPath)
159 {
160     InitDbStorePath();
161     InitDbStore();
162 }
163 
InitDbStorePath()164 void CpuStorage::InitDbStorePath()
165 {
166     std::string tempDbStorePath = FileUtil::IncludeTrailingPathDelimiter(workPath_);
167     const std::string cpuDirName = "cpu";
168     tempDbStorePath = FileUtil::IncludeTrailingPathDelimiter(tempDbStorePath.append(cpuDirName));
169     if (!FileUtil::IsDirectory(tempDbStorePath) && !FileUtil::ForceCreateDirectory(tempDbStorePath)) {
170         HIVIEW_LOGE("failed to create dir=%{public}s", tempDbStorePath.c_str());
171         return;
172     }
173     tempDbStorePath.append(CreateDbFileName());
174     dbStorePath_ = tempDbStorePath;
175     HIVIEW_LOGI("succ to init db store path=%{public}s", dbStorePath_.c_str());
176 }
177 
InitDbStore()178 void CpuStorage::InitDbStore()
179 {
180     NativeRdb::RdbStoreConfig config(dbStorePath_);
181     config.SetSecurityLevel(NativeRdb::SecurityLevel::S1);
182     CpuDbStoreCallback callback;
183     auto ret = NativeRdb::E_OK;
184     dbStore_ = NativeRdb::RdbHelper::GetRdbStore(config, DB_VERSION, callback, ret);
185     if (ret != NativeRdb::E_OK) {
186         HIVIEW_LOGE("failed to init db store, db store path=%{public}s", dbStorePath_.c_str());
187         dbStore_ = nullptr;
188         return;
189     }
190 }
191 
Store(const std::vector<ProcessCpuStatInfo> & cpuCollectionInfos)192 void CpuStorage::Store(const std::vector<ProcessCpuStatInfo>& cpuCollectionInfos)
193 {
194     if (dbStore_ == nullptr) {
195         HIVIEW_LOGW("db store is null, path=%{public}s", dbStorePath_.c_str());
196         return;
197     }
198 
199     for (auto& cpuCollectionInfo : cpuCollectionInfos) {
200         if (NeedStoreInDb(cpuCollectionInfo)) {
201             Store(cpuCollectionInfo);
202         }
203     }
204 }
205 
Store(const ProcessCpuStatInfo & cpuCollectionInfo)206 void CpuStorage::Store(const ProcessCpuStatInfo& cpuCollectionInfo)
207 {
208     InsertTable(cpuCollectionInfo);
209 }
210 
InsertTable(const ProcessCpuStatInfo & cpuCollectionInfo)211 void CpuStorage::InsertTable(const ProcessCpuStatInfo& cpuCollectionInfo)
212 {
213     if (CreateTable() != 0) {
214         return;
215     }
216     NativeRdb::ValuesBucket bucket;
217     bucket.PutLong(COLUMN_START_TIME, static_cast<int64_t>(cpuCollectionInfo.startTime));
218     bucket.PutLong(COLUMN_END_TIME, static_cast<int64_t>(cpuCollectionInfo.endTime));
219     bucket.PutInt(COLUMN_PID, cpuCollectionInfo.pid);
220     bucket.PutInt(COLUMN_PROC_STATE, GetProcessStateInCollectionPeriod(cpuCollectionInfo));
221     bucket.PutString(COLUMN_PROC_NAME, cpuCollectionInfo.procName);
222     bucket.PutDouble(COLUMN_CPU_LOAD, TruncateDecimalWithNBitPrecision(cpuCollectionInfo.cpuLoad));
223     bucket.PutDouble(COLUMN_CPU_USAGE, TruncateDecimalWithNBitPrecision(cpuCollectionInfo.cpuUsage));
224     int64_t seq = 0;
225     if (dbStore_->Insert(seq, TABLE_NAME, bucket) != NativeRdb::E_OK) {
226         HIVIEW_LOGE("failed to insert cpu data to db store, pid=%{public}d, proc_name=%{public}s", 0, "");
227     }
228 }
229 
CreateTable()230 int32_t CpuStorage::CreateTable()
231 {
232     /**
233      * table: unified_collection_cpu
234      *
235      * |-----|------------|----------|-----|------------|-----------|----------|-----------|
236      * |  id | start_time | end_time | pid | proc_state | proc_name | cpu_load | cpu_usage |
237      * |-----|------------|----------|-----|------------|-----------|----------|-----------|
238      * | INT |    INT64   |   INT64  | INT |    INT     |  VARCHAR  |  DOUBLE  |   DOUBLE  |
239      * |-----|------------|----------|-----|------------|-----------|----------|-----------|
240      */
241     const std::vector<std::pair<std::string, std::string>> fields = {
242         {COLUMN_START_TIME, SqlUtil::COLUMN_TYPE_INT},
243         {COLUMN_END_TIME, SqlUtil::COLUMN_TYPE_INT},
244         {COLUMN_PID, SqlUtil::COLUMN_TYPE_INT},
245         {COLUMN_PROC_STATE, SqlUtil::COLUMN_TYPE_INT},
246         {COLUMN_PROC_NAME, SqlUtil::COLUMN_TYPE_STR},
247         {COLUMN_CPU_LOAD, SqlUtil::COLUMN_TYPE_DOU},
248         {COLUMN_CPU_USAGE, SqlUtil::COLUMN_TYPE_DOU},
249     };
250     std::string sql = SqlUtil::GenerateCreateSql(TABLE_NAME, fields);
251     if (dbStore_->ExecuteSql(sql) != NativeRdb::E_OK) {
252         HIVIEW_LOGE("failed to create table, sql=%{public}s", sql.c_str());
253         return -1;
254     }
255     return 0;
256 }
257 
Report()258 void CpuStorage::Report()
259 {
260     if (!NeedReport()) {
261         return;
262     }
263     HIVIEW_LOGI("start to report cpu collection event");
264     PrepareOldDbFilesBeforeReport();
265     ReportCpuCollectionEvent();
266     PrepareNewDbFilesAfterReport();
267 }
268 
NeedReport()269 bool CpuStorage::NeedReport()
270 {
271     if (dbStorePath_.empty()) {
272         return false;
273     }
274     std::string nowDbFileName = FileUtil::ExtractFileName(dbStorePath_);
275     std::string newDbFileName = CreateDbFileName();
276     return newDbFileName != nowDbFileName;
277 }
278 
PrepareOldDbFilesBeforeReport()279 void CpuStorage::PrepareOldDbFilesBeforeReport()
280 {
281     // 1. Close the current db file
282     ResetDbStore();
283     // 2. Init upload directory
284     if (!InitDbStoreUploadPath()) {
285         return;
286     }
287     // 3. Move the db file to the upload directory
288     MoveDbFilesToUploadDir();
289     // 4. Aging upload db files, only the latest N db files are retained
290     TryToAgeUploadDbFiles();
291 }
292 
ResetDbStore()293 void CpuStorage::ResetDbStore()
294 {
295     dbStore_ = nullptr;
296 }
297 
InitDbStoreUploadPath()298 bool CpuStorage::InitDbStoreUploadPath()
299 {
300     if (!dbStoreUploadPath_.empty()) {
301         return true;
302     }
303     const std::string uploadDirName = "upload";
304     std::string tmpUploadPath = FileUtil::IncludeTrailingPathDelimiter(
305         FileUtil::ExtractFilePath(dbStorePath_)).append(uploadDirName);
306     if (!FileUtil::IsDirectory(tmpUploadPath) && !FileUtil::ForceCreateDirectory(tmpUploadPath)) {
307         HIVIEW_LOGE("failed to create upload dir=%{public}s", tmpUploadPath.c_str());
308         return false;
309     }
310     dbStoreUploadPath_ = tmpUploadPath;
311     HIVIEW_LOGI("init db upload path=%{public}s", dbStoreUploadPath_.c_str());
312     return true;
313 }
314 
MoveDbFilesToUploadDir()315 void CpuStorage::MoveDbFilesToUploadDir()
316 {
317     std::vector<std::string> dbFiles;
318     FileUtil::GetDirFiles(FileUtil::ExtractFilePath(dbStorePath_), dbFiles, false);
319     for (auto& dbFile : dbFiles) {
320         // upload only xxx.db, and delete xxx.db-shm/xxx.db-wal
321         if (IsDbFile(dbFile)) {
322             MoveDbFileToUploadDir(dbFile);
323             continue;
324         }
325         HIVIEW_LOGI("start to remove db file=%{public}s", dbFile.c_str());
326         if (!FileUtil::RemoveFile(dbFile)) {
327             HIVIEW_LOGW("failed to remove db file=%{public}s", dbFile.c_str());
328         }
329     }
330 }
331 
MoveDbFileToUploadDir(const std::string dbFilePath)332 void CpuStorage::MoveDbFileToUploadDir(const std::string dbFilePath)
333 {
334     std::string uploadFilePath = FileUtil::IncludeTrailingPathDelimiter(dbStoreUploadPath_)
335         .append(FileUtil::ExtractFileName(dbFilePath));
336     HIVIEW_LOGI("start to move db file, src=%{public}s, dst=%{public}s", dbFilePath.c_str(), uploadFilePath.c_str());
337     if (FileUtil::CopyFile(dbFilePath, uploadFilePath) != 0) {
338         HIVIEW_LOGW("failed to copy db file");
339         return;
340     }
341     if (!FileUtil::RemoveFile(dbFilePath)) {
342         HIVIEW_LOGW("failed to delete db file=%{public}s", dbFilePath.c_str());
343     }
344 }
345 
TryToAgeUploadDbFiles()346 void CpuStorage::TryToAgeUploadDbFiles()
347 {
348     std::vector<std::string> dbFiles;
349     FileUtil::GetDirFiles(dbStoreUploadPath_, dbFiles);
350     if (!NeedCleanDbFiles(dbFiles)) {
351         return;
352     }
353     HIVIEW_LOGI("start to clean db files, size=%{public}zu", dbFiles.size());
354     std::sort(dbFiles.begin(), dbFiles.end());
355     ClearDbFilesByTimestampOrder(dbFiles);
356 }
357 
ReportCpuCollectionEvent()358 void CpuStorage::ReportCpuCollectionEvent()
359 {
360     int32_t ret = HiSysEventWrite(HiSysEvent::Domain::HIVIEWDFX, "CPU_COLLECTION", HiSysEvent::EventType::FAULT);
361     if (ret != 0) {
362         HIVIEW_LOGW("failed to report cpu collection event, ret=%{public}d", ret);
363     }
364 }
365 
PrepareNewDbFilesAfterReport()366 void CpuStorage::PrepareNewDbFilesAfterReport()
367 {
368     InitDbStorePath();
369     InitDbStore();
370 }
371 }  // namespace HiviewDFX
372 }  // namespace OHOS
373