• 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 "cpu_storage.h"
16 
17 #include <functional>
18 #include <map>
19 
20 #include "file_util.h"
21 #include "hisysevent.h"
22 #include "hiview_db_util.h"
23 #include "hiview_logger.h"
24 #include "parameter_ex.h"
25 #ifdef POWER_MANAGER_ENABLE
26 #include "power_status_manager.h"
27 #endif
28 #include "process_collector.h"
29 #include "process_status.h"
30 #include "rdb_predicates.h"
31 #include "sql_util.h"
32 #include "string_util.h"
33 
34 namespace OHOS {
35 namespace HiviewDFX {
36 DEFINE_LOG_TAG("HiView-CpuStorage");
37 using namespace OHOS::HiviewDFX::UCollectUtil;
38 namespace {
39 constexpr int32_t DB_VERSION = 2;
40 constexpr char CPU_COLLECTION_TABLE_NAME[] = "unified_collection_cpu";
41 constexpr char THREAD_CPU_COLLECTION_TABLE_NAME[] = "unified_collection_hiview_cpu";
42 constexpr char SYS_VERSION_TABLE_NAME[] = "version";
43 constexpr char COLUMN_START_TIME[] = "start_time";
44 constexpr char COLUMN_END_TIME[] = "end_time";
45 constexpr char COLUMN_PID[] = "pid";
46 constexpr char COLUMN_TID[] = "tid";
47 constexpr char COLUMN_PROC_NAME[] = "proc_name";
48 constexpr char COLUMN_THREAD_NAME[] = "thread_name";
49 constexpr char COLUMN_PROC_STATE[] = "proc_state";
50 constexpr char COLUMN_CPU_LOAD[] = "cpu_load";
51 constexpr char COLUMN_CPU_USAGE[] = "cpu_usage";
52 constexpr char COLUMN_THREAD_CNT[] = "thread_cnt";
53 constexpr char COLUMN_VERSION_NAME[] = "name";
54 constexpr uint32_t DEFAULT_PRECISION_OF_DECIMAL = 6; // 0.123456
55 constexpr int32_t MEM_CG_PROCESS_FLAG = 100;
56 
CreateDbFileName()57 std::string CreateDbFileName()
58 {
59     return HiviewDbUtil::CreateFileNameByDate("cpu_stat_");
60 }
61 
IsValidProcess(const ProcessCpuStatInfo & cpuCollectionInfo)62 bool IsValidProcess(const ProcessCpuStatInfo& cpuCollectionInfo)
63 {
64     return (cpuCollectionInfo.pid > 0) && (!cpuCollectionInfo.procName.empty());
65 }
66 
IsValidCpuLoad(const ProcessCpuStatInfo & cpuCollectionInfo)67 bool IsValidCpuLoad(const ProcessCpuStatInfo& cpuCollectionInfo)
68 {
69     constexpr double storeFilteringThresholdOfCpuLoad = 0.0005; // 0.05%
70     return cpuCollectionInfo.cpuLoad >= storeFilteringThresholdOfCpuLoad;
71 }
72 
IsInvalidCpuLoad(const ProcessCpuStatInfo & cpuCollectionInfo)73 bool IsInvalidCpuLoad(const ProcessCpuStatInfo& cpuCollectionInfo)
74 {
75     return cpuCollectionInfo.cpuLoad == 0;
76 }
77 
IsValidCpuUsage(const ProcessCpuStatInfo & cpuCollectionInfo)78 bool IsValidCpuUsage(const ProcessCpuStatInfo& cpuCollectionInfo)
79 {
80     constexpr double storeFilteringThresholdOfCpuUsage = 0.0005; // 0.05%
81     return cpuCollectionInfo.cpuUsage >= storeFilteringThresholdOfCpuUsage;
82 }
83 
NeedStoreInDb(const ProcessCpuStatInfo & cpuCollectionInfo)84 bool NeedStoreInDb(const ProcessCpuStatInfo& cpuCollectionInfo)
85 {
86     if (!IsValidProcess(cpuCollectionInfo)) {
87         static uint32_t invalidProcNum = 0;
88         invalidProcNum++;
89         constexpr uint32_t logLimitNum = 1000;
90         if (invalidProcNum % logLimitNum == 0) {
91             HIVIEW_LOGW("invalid process num=%{public}u, pid=%{public}d, name=%{public}s",
92                 invalidProcNum, cpuCollectionInfo.pid, cpuCollectionInfo.procName.c_str());
93         }
94         return false;
95     }
96     return IsValidCpuLoad(cpuCollectionInfo)
97         || (IsInvalidCpuLoad(cpuCollectionInfo) && IsValidCpuUsage(cpuCollectionInfo));
98 }
99 
TruncateDecimalWithNBitPrecision(double decimal,uint32_t precision=DEFAULT_PRECISION_OF_DECIMAL)100 double TruncateDecimalWithNBitPrecision(double decimal, uint32_t precision = DEFAULT_PRECISION_OF_DECIMAL)
101 {
102     auto truncateCoefficient = std::pow(10, precision);
103     return std::floor(decimal * truncateCoefficient) / truncateCoefficient;
104 }
105 
IsForegroundStateInCollectionPeriod(const ProcessCpuStatInfo & cpuCollectionInfo)106 bool IsForegroundStateInCollectionPeriod(const ProcessCpuStatInfo& cpuCollectionInfo)
107 {
108     int32_t pid = cpuCollectionInfo.pid;
109     ProcessState procState = ProcessStatus::GetInstance().GetProcessState(pid);
110     if (procState == FOREGROUND) {
111         return true;
112     }
113     uint64_t procForegroundTime = ProcessStatus::GetInstance().GetProcessLastForegroundTime(pid);
114     return procForegroundTime >= cpuCollectionInfo.startTime;
115 }
116 
GetPowerProcessStateInCollectionPeriod(const ProcessCpuStatInfo & cpuCollectionInfo,const std::unordered_set<int32_t> & memCgProcs)117 int32_t GetPowerProcessStateInCollectionPeriod(const ProcessCpuStatInfo& cpuCollectionInfo,
118     const std::unordered_set<int32_t>& memCgProcs)
119 {
120     int32_t processState = IsForegroundStateInCollectionPeriod(cpuCollectionInfo) ? static_cast<int32_t>(FOREGROUND) :
121         static_cast<int32_t>(ProcessStatus::GetInstance().GetProcessState(cpuCollectionInfo.pid));
122 #ifdef POWER_MANAGER_ENABLE
123     int32_t powerState = PowerStatusManager::GetInstance().GetPowerState();
124     processState += powerState;
125 #endif
126     processState += (memCgProcs.find(cpuCollectionInfo.pid) != memCgProcs.end() ? MEM_CG_PROCESS_FLAG : 0);
127     return processState;
128 }
129 
CreateTable(NativeRdb::RdbStore & dbStore,const std::string & tableName,const std::vector<std::pair<std::string,std::string>> & fields)130 int32_t CreateTable(NativeRdb::RdbStore& dbStore, const std::string& tableName,
131     const std::vector<std::pair<std::string, std::string>>& fields)
132 {
133     std::string sql = SqlUtil::GenerateCreateSql(tableName, fields);
134     HIVIEW_LOGI("try to create %{public}s table, sql=%{public}s", tableName.c_str(), sql.c_str());
135     return dbStore.ExecuteSql(sql);
136 }
137 
StoreSysVersion(NativeRdb::RdbStore & dbStore,const std::string & version)138 int32_t StoreSysVersion(NativeRdb::RdbStore& dbStore, const std::string& version)
139 {
140     NativeRdb::ValuesBucket bucket;
141     bucket.PutString(COLUMN_VERSION_NAME, version);
142     int64_t seq = 0;
143     if (auto ret = dbStore.Insert(seq, SYS_VERSION_TABLE_NAME, bucket); ret != NativeRdb::E_OK) {
144         HIVIEW_LOGE("failed to insert %{public}s to version table", version.c_str());
145         return ret;
146     }
147     return NativeRdb::E_OK;
148 }
149 
CreateCpuCollectionTable(NativeRdb::RdbStore & dbStore)150 int32_t CreateCpuCollectionTable(NativeRdb::RdbStore& dbStore)
151 {
152     /**
153      * table: unified_collection_cpu
154      *
155      * |-----|------------|----------|-----|------------|-----------|----------|-----------|------------|
156      * |  id | start_time | end_time | pid | proc_state | proc_name | cpu_load | cpu_usage | thread_cnt |
157      * |-----|------------|----------|-----|------------|-----------|----------|-----------|------------|
158      * | INT |    INT64   |   INT64  | INT |    INT     |  VARCHAR  |  DOUBLE  |   DOUBLE  |    INT     |
159      * |-----|------------|----------|-----|------------|-----------|----------|-----------|------------|
160      */
161     const std::vector<std::pair<std::string, std::string>> fields = {
162         {COLUMN_START_TIME, SqlUtil::COLUMN_TYPE_INT},
163         {COLUMN_END_TIME, SqlUtil::COLUMN_TYPE_INT},
164         {COLUMN_PID, SqlUtil::COLUMN_TYPE_INT},
165         {COLUMN_PROC_STATE, SqlUtil::COLUMN_TYPE_INT},
166         {COLUMN_PROC_NAME, SqlUtil::COLUMN_TYPE_STR},
167         {COLUMN_CPU_LOAD, SqlUtil::COLUMN_TYPE_DOU},
168         {COLUMN_CPU_USAGE, SqlUtil::COLUMN_TYPE_DOU},
169         {COLUMN_THREAD_CNT, SqlUtil::COLUMN_TYPE_INT},
170     };
171     if (auto ret = CreateTable(dbStore, CPU_COLLECTION_TABLE_NAME, fields); ret != NativeRdb::E_OK) {
172         HIVIEW_LOGE("failed to create %{public}s table", CPU_COLLECTION_TABLE_NAME);
173         return ret;
174     }
175     return NativeRdb::E_OK;
176 }
177 
CreateThreadCpuCollectionTable(NativeRdb::RdbStore & dbStore)178 int32_t CreateThreadCpuCollectionTable(NativeRdb::RdbStore& dbStore)
179 {
180     /**
181      * table: unified_collection_hiview_cpu
182      *
183      * |-----|------------|----------|-----|-----------  |----------|-----------|
184      * |  id | start_time | end_time | tid | thread_name | cpu_load | cpu_usage |
185      * |-----|------------|----------|-----|-------------|----------|-----------|
186      * | INT |    INT64   |   INT64  | INT |  VARCHAR    |  DOUBLE  |   DOUBLE  |
187      * |-----|------------|----------|-----|-------------|----------|-----------|
188      */
189     const std::vector<std::pair<std::string, std::string>> fields = {
190         {COLUMN_START_TIME, SqlUtil::COLUMN_TYPE_INT},
191         {COLUMN_END_TIME, SqlUtil::COLUMN_TYPE_INT},
192         {COLUMN_TID, SqlUtil::COLUMN_TYPE_INT},
193         {COLUMN_THREAD_NAME, SqlUtil::COLUMN_TYPE_STR},
194         {COLUMN_CPU_LOAD, SqlUtil::COLUMN_TYPE_DOU},
195         {COLUMN_CPU_USAGE, SqlUtil::COLUMN_TYPE_DOU},
196     };
197     if (auto ret = CreateTable(dbStore, THREAD_CPU_COLLECTION_TABLE_NAME, fields); ret != NativeRdb::E_OK) {
198         HIVIEW_LOGE("failed to create %{public}s table", THREAD_CPU_COLLECTION_TABLE_NAME);
199         return ret;
200     }
201     return NativeRdb::E_OK;
202 }
203 
CreateVersionTable(NativeRdb::RdbStore & dbStore)204 int32_t CreateVersionTable(NativeRdb::RdbStore& dbStore)
205 {
206     /**
207      * table: version
208      *
209      * |-----|-----------|
210      * |  id |    name   |
211      * |-----|-----------|
212      * | INT |  VARCHAR  |
213      * |-----|-----------|
214      */
215     const std::vector<std::pair<std::string, std::string>> fields = {
216         {COLUMN_VERSION_NAME, SqlUtil::COLUMN_TYPE_STR},
217     };
218     if (auto ret = CreateTable(dbStore, SYS_VERSION_TABLE_NAME, fields); ret != NativeRdb::E_OK) {
219         HIVIEW_LOGE("failed to create %{public}s table", SYS_VERSION_TABLE_NAME);
220         return ret;
221     }
222     return NativeRdb::E_OK;
223 }
224 }
225 
OnCreate(NativeRdb::RdbStore & rdbStore)226 int CpuStorageDbCallback::OnCreate(NativeRdb::RdbStore& rdbStore)
227 {
228     HIVIEW_LOGD("create dbStore");
229     if (auto ret = CreateVersionTable(rdbStore); ret != NativeRdb::E_OK) {
230         HIVIEW_LOGE("failed to create version table in db creation");
231         return ret;
232     }
233     if (auto ret = StoreSysVersion(rdbStore, Parameter::GetDisplayVersionStr()); ret != NativeRdb::E_OK) {
234         HIVIEW_LOGE("failed to insert system version into version table in db creation");
235         return ret;
236     }
237     if (auto ret = CreateCpuCollectionTable(rdbStore); ret != NativeRdb::E_OK) {
238         HIVIEW_LOGE("failed to create cpu collection table in db creation");
239         return ret;
240     }
241     if (auto ret = CreateThreadCpuCollectionTable(rdbStore); ret != NativeRdb::E_OK) {
242         HIVIEW_LOGE("failed to create cpu collection table in db creation");
243         return ret;
244     }
245     return NativeRdb::E_OK;
246 }
247 
OnUpgrade(NativeRdb::RdbStore & rdbStore,int oldVersion,int newVersion)248 int CpuStorageDbCallback::OnUpgrade(NativeRdb::RdbStore& rdbStore, int oldVersion, int newVersion)
249 {
250     HIVIEW_LOGD("oldVersion=%{public}d, newVersion=%{public}d", oldVersion, newVersion);
251     return NativeRdb::E_OK;
252 }
253 
CpuStorage(const std::string & workPath)254 CpuStorage::CpuStorage(const std::string& workPath) : workPath_(workPath)
255 {
256     InitDbStorePath();
257     InitDbStore();
258     if (dbStore_!= nullptr && GetStoredSysVersion() != Parameter::GetDisplayVersionStr()) {
259         HIVIEW_LOGI("system has been upgaded, report directly");
260         ReportDbRecords();
261     }
262 }
263 
InitDbStorePath()264 void CpuStorage::InitDbStorePath()
265 {
266     std::string tempDbStorePath = FileUtil::IncludeTrailingPathDelimiter(workPath_);
267     const std::string cpuDirName = "cpu";
268     tempDbStorePath = FileUtil::IncludeTrailingPathDelimiter(tempDbStorePath.append(cpuDirName));
269     if (!FileUtil::IsDirectory(tempDbStorePath) && !FileUtil::ForceCreateDirectory(tempDbStorePath)) {
270         HIVIEW_LOGE("failed to create dir=%{public}s", tempDbStorePath.c_str());
271         return;
272     }
273     tempDbStorePath.append(CreateDbFileName());
274     dbStorePath_ = tempDbStorePath;
275     HIVIEW_LOGI("succ to init db store path=%{public}s", dbStorePath_.c_str());
276 }
277 
InitDbStore()278 void CpuStorage::InitDbStore()
279 {
280     NativeRdb::RdbStoreConfig config(dbStorePath_);
281     config.SetSecurityLevel(NativeRdb::SecurityLevel::S1);
282     CpuStorageDbCallback callback;
283     auto ret = NativeRdb::E_OK;
284     dbStore_ = NativeRdb::RdbHelper::GetRdbStore(config, DB_VERSION, callback, ret);
285     if (ret != NativeRdb::E_OK) {
286         HIVIEW_LOGE("failed to init db store, db store path=%{public}s", dbStorePath_.c_str());
287         dbStore_ = nullptr;
288         return;
289     }
290 }
291 
StoreProcessDatas(const std::vector<ProcessCpuStatInfo> & cpuCollectionInfos)292 void CpuStorage::StoreProcessDatas(const std::vector<ProcessCpuStatInfo>& cpuCollectionInfos)
293 {
294     if (dbStore_ == nullptr) {
295         HIVIEW_LOGW("db store is null, path=%{public}s", dbStorePath_.c_str());
296         return;
297     }
298     auto processCollector = UCollectUtil::ProcessCollector::Create();
299     auto result = processCollector->GetMemCgProcesses();
300     std::vector<NativeRdb::ValuesBucket> valuesBuckets;
301     for (auto& cpuCollectionInfo : cpuCollectionInfos) {
302         if (!NeedStoreInDb(cpuCollectionInfo)) {
303             continue;
304         }
305         NativeRdb::ValuesBucket bucket;
306         bucket.PutLong(COLUMN_START_TIME, static_cast<int64_t>(cpuCollectionInfo.startTime));
307         bucket.PutLong(COLUMN_END_TIME, static_cast<int64_t>(cpuCollectionInfo.endTime));
308         bucket.PutInt(COLUMN_PID, cpuCollectionInfo.pid);
309         bucket.PutInt(COLUMN_PROC_STATE, GetPowerProcessStateInCollectionPeriod(cpuCollectionInfo, result.data));
310         bucket.PutString(COLUMN_PROC_NAME, cpuCollectionInfo.procName);
311         bucket.PutDouble(COLUMN_CPU_LOAD, TruncateDecimalWithNBitPrecision(cpuCollectionInfo.cpuLoad));
312         bucket.PutDouble(COLUMN_CPU_USAGE, TruncateDecimalWithNBitPrecision(cpuCollectionInfo.cpuUsage));
313         bucket.PutInt(COLUMN_THREAD_CNT, cpuCollectionInfo.threadCount);
314         valuesBuckets.push_back(bucket);
315     }
316     int64_t outInsertNum = 0;
317     if (dbStore_->BatchInsert(outInsertNum, CPU_COLLECTION_TABLE_NAME, valuesBuckets) != NativeRdb::E_OK) {
318         HIVIEW_LOGE("Insert process data to unified_collection_cpu failed");
319     }
320 }
321 
StoreThreadDatas(const std::vector<ThreadCpuStatInfo> & cpuCollections)322 void CpuStorage::StoreThreadDatas(const std::vector<ThreadCpuStatInfo>& cpuCollections)
323 {
324     if (dbStore_ == nullptr) {
325         HIVIEW_LOGW("db store is null, path=%{public}s", dbStorePath_.c_str());
326         return;
327     }
328     std::vector<NativeRdb::ValuesBucket> valuesBuckets;
329     for (auto& cpuCollection : cpuCollections) {
330         NativeRdb::ValuesBucket bucket;
331         bucket.PutLong(COLUMN_START_TIME, static_cast<int64_t>(cpuCollection.startTime));
332         bucket.PutLong(COLUMN_END_TIME, static_cast<int64_t>(cpuCollection.endTime));
333         bucket.PutInt(COLUMN_TID, cpuCollection.tid);
334         bucket.PutString(COLUMN_THREAD_NAME, "");
335         bucket.PutDouble(COLUMN_CPU_LOAD, TruncateDecimalWithNBitPrecision(cpuCollection.cpuLoad));
336         bucket.PutDouble(COLUMN_CPU_USAGE, TruncateDecimalWithNBitPrecision(cpuCollection.cpuUsage));
337         valuesBuckets.push_back(bucket);
338     }
339     int64_t outInsertNum = 0;
340     if (dbStore_->BatchInsert(outInsertNum, THREAD_CPU_COLLECTION_TABLE_NAME, valuesBuckets) != NativeRdb::E_OK) {
341         HIVIEW_LOGE("Insert thread data to unified_collection_cpu failed");
342     }
343 }
344 
Report()345 void CpuStorage::Report()
346 {
347     if (!NeedReport()) {
348         return;
349     }
350     ReportDbRecords();
351 }
352 
ReportDbRecords()353 void CpuStorage::ReportDbRecords()
354 {
355     HIVIEW_LOGI("start to report cpu collection event");
356     PrepareOldDbFilesBeforeReport();
357     ReportCpuCollectionEvent();
358     PrepareNewDbFilesAfterReport();
359 }
360 
GetStoredSysVersion()361 std::string CpuStorage::GetStoredSysVersion()
362 {
363     NativeRdb::RdbPredicates predicates(SYS_VERSION_TABLE_NAME);
364     std::vector<std::string> columns;
365     columns.emplace_back(COLUMN_VERSION_NAME);
366     std::string version;
367     std::shared_ptr<NativeRdb::ResultSet> allVersions = dbStore_->Query(predicates, columns);
368     if (allVersions == nullptr || allVersions->GoToFirstRow() != NativeRdb::E_OK) {
369         HIVIEW_LOGE("failed to get result set from db query");
370         return version;
371     }
372     NativeRdb::RowEntity entity;
373     if (allVersions->GetRow(entity) != NativeRdb::E_OK) {
374         HIVIEW_LOGE("failed to read row entity from result set");
375         return version;
376     }
377     if (entity.Get(COLUMN_VERSION_NAME).GetString(version) != NativeRdb::E_OK) {
378         HIVIEW_LOGE("failed to get version value");
379     }
380     HIVIEW_LOGI("stored version in db is %{public}s", version.c_str());
381     return version;
382 }
383 
NeedReport()384 bool CpuStorage::NeedReport()
385 {
386     if (dbStorePath_.empty()) {
387         HIVIEW_LOGI("the db file stored directory is empty");
388         return false;
389     }
390     std::string nowDbFileName = FileUtil::ExtractFileName(dbStorePath_);
391     std::string newDbFileName = CreateDbFileName();
392     return newDbFileName != nowDbFileName;
393 }
394 
PrepareOldDbFilesBeforeReport()395 void CpuStorage::PrepareOldDbFilesBeforeReport()
396 {
397     // 1. Close the current db file
398     ResetDbStore();
399     // 2. Init upload directory
400     if (!HiviewDbUtil::InitDbUploadPath(dbStorePath_, dbStoreUploadPath_)) {
401         return;
402     }
403     // 3. Move the db file to the upload directory
404     HiviewDbUtil::MoveDbFilesToUploadDir(dbStorePath_, dbStoreUploadPath_);
405     // 4. Aging upload db files, only the latest 7 db files are retained
406     HiviewDbUtil::TryToAgeUploadDbFiles(dbStoreUploadPath_);
407 }
408 
ResetDbStore()409 void CpuStorage::ResetDbStore()
410 {
411     dbStore_ = nullptr;
412 }
413 
ReportCpuCollectionEvent()414 void CpuStorage::ReportCpuCollectionEvent()
415 {
416     int32_t ret = HiSysEventWrite(HiSysEvent::Domain::HIVIEWDFX, "CPU_COLLECTION", HiSysEvent::EventType::FAULT);
417     if (ret != 0) {
418         HIVIEW_LOGW("failed to report cpu collection event, ret=%{public}d", ret);
419     }
420 }
421 
PrepareNewDbFilesAfterReport()422 void CpuStorage::PrepareNewDbFilesAfterReport()
423 {
424     InitDbStorePath();
425     InitDbStore();
426 }
427 }  // namespace HiviewDFX
428 }  // namespace OHOS
429