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