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