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 "dump_manager_cpu_service.h"
16#include <file_ex.h>
17#include <if_system_ability_manager.h>
18#include <ipc_skeleton.h>
19#include <iservice_registry.h>
20#include <string_ex.h>
21#include <system_ability_definition.h>
22#include <thread>
23#include <unistd.h>
24#include "securec.h"
25
26#include "accesstoken_kit.h"
27#include "common/dumper_constant.h"
28#include "cpu_collector.h"
29#include "datetime_ex.h"
30#include "dump_log_manager.h"
31#include "dump_utils.h"
32#include "hilog_wrapper.h"
33#include "inner/dump_service_id.h"
34#include "manager/dump_implement.h"
35#include "token_setproc.h"
36#include "util/string_utils.h"
37#include "util/file_utils.h"
38
39using namespace std;
40namespace OHOS {
41namespace HiviewDFX {
42namespace {
43const std::string DUMPMGR_CPU_SERVICE_NAME = "HiDumperCpuService";
44static constexpr size_t LOAD_AVG_INFO_COUNT = 3;
45static constexpr int PROC_CPU_LENGTH = 256;
46static constexpr double HUNDRED_PERCENT_VALUE = 100.00;
47static constexpr long unsigned THOUSAND_PERCENT_VALUE = 1000;
48static constexpr int INVALID_PID = -1;
49static const int TM_START_YEAR = 1900;
50static const int DEC_SYSTEM_VALUE = 10;
51static const int AVG_INFO_SUBSTR_LENGTH = 4;
52static std::shared_ptr<OHOS::HiviewDFX::UCollectUtil::CpuCollector> g_collector;
53}
54DumpManagerCpuService::DumpManagerCpuService() : SystemAbility(DFX_SYS_HIDUMPER_CPU_ABILITY_ID, true)
55{
56}
57
58DumpManagerCpuService::~DumpManagerCpuService()
59{
60}
61
62void DumpManagerCpuService::OnStart()
63{
64    DUMPER_HILOGI(MODULE_CPU_SERVICE, "on start");
65    if (started_) {
66        DUMPER_HILOGE(MODULE_CPU_SERVICE, "it's ready, nothing to do.");
67        return;
68    }
69    g_collector = OHOS::HiviewDFX::UCollectUtil::CpuCollector::Create();
70    started_ = true;
71}
72
73int32_t DumpManagerCpuService::Request(DumpCpuData &dumpCpuData)
74{
75    DUMPER_HILOGI(MODULE_CPU_SERVICE, "enter");
76    static std::mutex mutex_;
77    unique_lock<mutex> lock(mutex_);
78    InitParam(dumpCpuData);
79    if (!HasDumpPermission()) {
80        DUMPER_HILOGE(MODULE_SERVICE,
81                      "No ohos.permission.DUMP permission to acccess hidumper cpuservice, please check!");
82        return DumpStatus::DUMP_NOPERMISSION;
83    }
84    int32_t ret = DumpCpuUsageData();
85    dumpCpuData.dumpCPUDatas_ = *dumpCPUDatas_;
86    ResetParam();
87    return ret;
88}
89
90void DumpManagerCpuService::InitParam(DumpCpuData &dumpCpuData)
91{
92    cpuUsagePid_ = dumpCpuData.cpuUsagePid_;
93    if (cpuUsagePid_ != INVALID_PID) {
94        curSpecProc_ = std::make_shared<ProcInfo>();
95    }
96    curCPUInfo_ = std::make_shared<CPUInfo>();
97    dumpCPUDatas_ = std::make_shared<std::vector<std::vector<std::string>>>(dumpCpuData.dumpCPUDatas_);
98}
99
100void DumpManagerCpuService::ResetParam()
101{
102    curCPUInfo_.reset();
103    curProcs_.clear();
104    if (cpuUsagePid_ != INVALID_PID) {
105        curSpecProc_.reset();
106    }
107}
108
109int DumpManagerCpuService::DumpCpuUsageData()
110{
111    if (!GetSysCPUInfo(curCPUInfo_)) {
112        return DumpStatus::DUMP_FAIL;
113    }
114
115    if (cpuUsagePid_ != INVALID_PID) {
116        if (!GetSingleProcInfo(cpuUsagePid_, curSpecProc_)) {
117            return DumpStatus::DUMP_FAIL;
118        }
119    } else {
120        if (!GetAllProcInfo(curProcs_)) {
121            return DumpStatus::DUMP_FAIL;
122        }
123    }
124    std::string avgInfo;
125    if (ReadLoadAvgInfo(avgInfo) != DumpStatus::DUMP_OK) {
126        return DumpStatus::DUMP_FAIL;
127    }
128    AddStrLineToDumpInfo(avgInfo);
129
130    std::string startTime;
131    std::string endTime;
132    GetDateAndTime(startTime_ / THOUSAND_PERCENT_VALUE, startTime);
133    GetDateAndTime(endTime_ / THOUSAND_PERCENT_VALUE, endTime);
134    std::string dumpTimeStr;
135    CreateDumpTimeString(startTime, endTime, dumpTimeStr);
136    AddStrLineToDumpInfo(dumpTimeStr);
137
138    std::string cpuStatStr;
139    CreateCPUStatString(cpuStatStr);
140    AddStrLineToDumpInfo(cpuStatStr);
141
142    DumpProcInfo();
143    return DumpStatus::DUMP_OK;
144}
145
146int DumpManagerCpuService::GetCpuUsageByPid(int32_t pid, double &cpuUsage)
147{
148    static std::mutex mutex_;
149    unique_lock<mutex> lock(mutex_);
150    if (g_collector == nullptr) {
151        g_collector = OHOS::HiviewDFX::UCollectUtil::CpuCollector::Create();
152    }
153    if (pid != INVALID_PID) {
154        std::shared_ptr<ProcInfo> singleProcInfo = std::make_shared<ProcInfo>();
155        if (!GetSingleProcInfo(pid, singleProcInfo)) {
156            return DumpStatus::DUMP_FAIL;
157        }
158        cpuUsage = singleProcInfo->totalUsage / HUNDRED_PERCENT_VALUE;
159    }
160    DUMPER_HILOGD(MODULE_CPU_SERVICE, "GetCpuUsageByPid end, pid = %{public}d, cpuUsage = %{public}f", pid, cpuUsage);
161    return DumpStatus::DUMP_OK;
162}
163
164DumpStatus DumpManagerCpuService::ReadLoadAvgInfo(std::string &info)
165{
166    CollectResult<OHOS::HiviewDFX::SysCpuLoad> collectResult = g_collector->CollectSysCpuLoad();
167    if (collectResult.retCode != OHOS::HiviewDFX::UCollect::UcError::SUCCESS) {
168        DUMPER_HILOGE(MODULE_CPU_SERVICE, "collect system cpu load error, ret:%{public}d", collectResult.retCode);
169        return DumpStatus::DUMP_FAIL;
170    }
171    std::vector<std::string> avgLoadInfo;
172    avgLoadInfo.push_back(std::string(std::to_string(collectResult.data.avgLoad1)).substr(0, AVG_INFO_SUBSTR_LENGTH));
173    avgLoadInfo.push_back(std::string(std::to_string(collectResult.data.avgLoad5)).substr(0, AVG_INFO_SUBSTR_LENGTH));
174    avgLoadInfo.push_back(std::string(std::to_string(collectResult.data.avgLoad15)).substr(0, AVG_INFO_SUBSTR_LENGTH));
175
176    info = "Load average:";
177    for (size_t i = 0; i < LOAD_AVG_INFO_COUNT; i++) {
178        info.append(" ");
179        info.append(avgLoadInfo[i]);
180        if (i == LOAD_AVG_INFO_COUNT - 1) {
181            info.append(";");
182        } else {
183            info.append(" /");
184        }
185    }
186    info.append(" the cpu load average in 1 min, 5 min and 15 min");
187    DUMPER_HILOGD(MODULE_CPU_SERVICE, "info is %{public}s", info.c_str());
188    return DumpStatus::DUMP_OK;
189}
190
191bool DumpManagerCpuService::GetSysCPUInfo(std::shared_ptr<CPUInfo> &cpuInfo)
192{
193    if (cpuInfo == nullptr) {
194        return false;
195    }
196    auto collectResult = g_collector->CollectProcessCpuStatInfos(false);
197    if (collectResult.retCode != OHOS::HiviewDFX::UCollect::UcError::SUCCESS || collectResult.data.empty()) {
198        DUMPER_HILOGE(MODULE_CPU_SERVICE, "collect process cpu stat info error");
199        return false;
200    }
201
202    CollectResult<OHOS::HiviewDFX::SysCpuUsage> result = g_collector->CollectSysCpuUsage(false);
203    if (result.retCode != OHOS::HiviewDFX::UCollect::UcError::SUCCESS) {
204        DUMPER_HILOGE(MODULE_CPU_SERVICE, "collect system cpu usage error,retCode is %{public}d", result.retCode);
205        return false;
206    }
207    const OHOS::HiviewDFX::SysCpuUsage& sysCpuUsage = result.data;
208    startTime_ = sysCpuUsage.startTime;
209    endTime_ = sysCpuUsage.endTime;
210
211    for (const auto& oneCpuInfo : sysCpuUsage.cpuInfos) {
212        cpuInfo->userUsage = oneCpuInfo.userUsage;
213        cpuInfo->niceUsage = oneCpuInfo.niceUsage;
214        cpuInfo->systemUsage = oneCpuInfo.systemUsage;
215        cpuInfo->idleUsage = oneCpuInfo.idleUsage;
216        cpuInfo->ioWaitUsage = oneCpuInfo.ioWaitUsage;
217        cpuInfo->irqUsage = oneCpuInfo.irqUsage;
218        cpuInfo->softIrqUsage = oneCpuInfo.softIrqUsage;
219        break;
220    }
221
222    return true;
223}
224
225bool DumpManagerCpuService::GetAllProcInfo(std::vector<std::shared_ptr<ProcInfo>> &procInfos)
226{
227    auto collectResult = g_collector->CollectProcessCpuStatInfos(false);
228    if (collectResult.retCode != OHOS::HiviewDFX::UCollect::UcError::SUCCESS || collectResult.data.empty()) {
229        DUMPER_HILOGE(MODULE_CPU_SERVICE, "collect process cpu stat info error");
230        return false;
231    }
232    for (const auto& cpuInfo : collectResult.data) {
233        std::shared_ptr<ProcInfo> ptrProcInfo = std::make_shared<ProcInfo>();;
234        ptrProcInfo->pid = std::to_string(cpuInfo.pid);
235        ptrProcInfo->comm = cpuInfo.procName;
236        ptrProcInfo->minflt = std::to_string(cpuInfo.minFlt);
237        ptrProcInfo->majflt = std::to_string(cpuInfo.majFlt);
238        ptrProcInfo->userSpaceUsage = cpuInfo.uCpuUsage * HUNDRED_PERCENT_VALUE;
239        ptrProcInfo->sysSpaceUsage = cpuInfo.sCpuUsage * HUNDRED_PERCENT_VALUE;
240        ptrProcInfo->totalUsage = cpuInfo.cpuUsage * HUNDRED_PERCENT_VALUE;
241        procInfos.push_back(ptrProcInfo);
242    }
243    return true;
244}
245
246bool DumpManagerCpuService::GetSingleProcInfo(int pid, std::shared_ptr<ProcInfo> &specProc)
247{
248    if (specProc == nullptr) {
249        return false;
250    }
251
252    CollectResult<OHOS::HiviewDFX::ProcessCpuStatInfo> collectResult = g_collector->CollectProcessCpuStatInfo(pid);
253    if (collectResult.retCode != OHOS::HiviewDFX::UCollect::UcError::SUCCESS) {
254        DUMPER_HILOGE(MODULE_CPU_SERVICE, "collect system cpu usage error,ret:%{public}d", collectResult.retCode);
255        return false;
256    }
257    specProc->comm = collectResult.data.procName;
258    specProc->pid = std::to_string(collectResult.data.pid);
259    specProc->minflt = std::to_string(collectResult.data.minFlt);
260    specProc->majflt = std::to_string(collectResult.data.majFlt);
261    specProc->userSpaceUsage = collectResult.data.uCpuUsage * HUNDRED_PERCENT_VALUE;
262    specProc->sysSpaceUsage = collectResult.data.sCpuUsage * HUNDRED_PERCENT_VALUE;
263    specProc->totalUsage = collectResult.data.cpuUsage * HUNDRED_PERCENT_VALUE;
264    return true;
265}
266
267bool DumpManagerCpuService::GetDateAndTime(uint64_t timeStamp, std::string &dateTime)
268{
269    time_t time = static_cast<time_t>(timeStamp);
270    struct tm timeData = {0};
271    localtime_r(&time, &timeData);
272
273    dateTime = " ";
274    dateTime.append(std::to_string(TM_START_YEAR + timeData.tm_year));
275    dateTime.append("-");
276    if (1 + timeData.tm_mon < DEC_SYSTEM_VALUE) {
277        dateTime.append(std::to_string(0));
278    }
279    dateTime.append(std::to_string(1 + timeData.tm_mon));
280    dateTime.append("-");
281    if (timeData.tm_mday < DEC_SYSTEM_VALUE) {
282        dateTime.append(std::to_string(0));
283    }
284    dateTime.append(std::to_string(timeData.tm_mday));
285    dateTime.append(" ");
286    if (timeData.tm_hour < DEC_SYSTEM_VALUE) {
287        dateTime.append(std::to_string(0));
288    }
289    dateTime.append(std::to_string(timeData.tm_hour));
290    dateTime.append(":");
291    if (timeData.tm_min < DEC_SYSTEM_VALUE) {
292        dateTime.append(std::to_string(0));
293    }
294    dateTime.append(std::to_string(timeData.tm_min));
295    dateTime.append(":");
296    if (timeData.tm_sec < DEC_SYSTEM_VALUE) {
297        dateTime.append(std::to_string(0));
298    }
299    dateTime.append(std::to_string(timeData.tm_sec));
300    return true;
301}
302
303
304void DumpManagerCpuService::CreateDumpTimeString(const std::string &startTime,
305    const std::string &endTime, std::string &timeStr)
306{
307    DUMPER_HILOGD(MODULE_CPU_SERVICE, "start:%{public}s, end:%{public}s", startTime.c_str(), endTime.c_str());
308    timeStr = "CPU usage from";
309    timeStr.append(startTime);
310    timeStr.append(" to");
311    timeStr.append(endTime);
312}
313
314void DumpManagerCpuService::AddStrLineToDumpInfo(const std::string &strLine)
315{
316    std::vector<std::string> vec;
317    vec.push_back(strLine);
318    dumpCPUDatas_->push_back(vec);
319}
320
321void DumpManagerCpuService::CreateCPUStatString(std::string &str)
322{
323    double userSpaceUsage = (curCPUInfo_->userUsage + curCPUInfo_->niceUsage) * HUNDRED_PERCENT_VALUE;
324    double sysSpaceUsage = curCPUInfo_->systemUsage * HUNDRED_PERCENT_VALUE;
325    double iowUsage = curCPUInfo_->ioWaitUsage * HUNDRED_PERCENT_VALUE;
326    double irqUsage = (curCPUInfo_->irqUsage + curCPUInfo_->softIrqUsage) * HUNDRED_PERCENT_VALUE;
327    double idleUsage = curCPUInfo_->idleUsage * HUNDRED_PERCENT_VALUE;
328    double totalUsage = userSpaceUsage + sysSpaceUsage;
329
330    char format[PROC_CPU_LENGTH] = {0};
331    int ret = sprintf_s(format, PROC_CPU_LENGTH,
332                        "Total: %.2f%%; User Space: %.2f%%; Kernel Space: %.2f%%; "
333                        "iowait: %.2f%%; irq: %.2f%%; idle: %.2f%%",
334                        totalUsage, userSpaceUsage, sysSpaceUsage, iowUsage, irqUsage, idleUsage);
335    if (ret < 0) {
336        DUMPER_HILOGE(MODULE_CPU_SERVICE, "create process cpu info failed!.");
337        return;
338    }
339    str = std::string(format);
340}
341
342void DumpManagerCpuService::DumpProcInfo()
343{
344    std::vector<std::shared_ptr<ProcInfo>> sortedInfos;
345    sortedInfos.assign(curProcs_.begin(), curProcs_.end());
346    std::sort(sortedInfos.begin(), sortedInfos.end(), SortProcInfo);
347
348    AddStrLineToDumpInfo("Details of Processes:");
349    AddStrLineToDumpInfo("    PID   Total Usage	   User Space    Kernel Space    Page Fault Minor"
350                         "    Page Fault Major    Name");
351    if (cpuUsagePid_ != INVALID_PID) {
352        char format[PROC_CPU_LENGTH] = {0};
353        int ret = sprintf_s(format, PROC_CPU_LENGTH,
354                            "    %-5s    %6.2f%%         %6.2f%%"
355                            "        %6.2f%%        %8s            %8s            %-15s",
356                            (curSpecProc_->pid).c_str(), curSpecProc_->totalUsage,
357                            curSpecProc_->userSpaceUsage, curSpecProc_->sysSpaceUsage,
358                            (curSpecProc_->minflt).c_str(), (curSpecProc_->majflt).c_str(),
359                            (curSpecProc_->comm).c_str());
360        AddStrLineToDumpInfo(std::string(format));
361        if (ret < 0) {
362            DUMPER_HILOGE(MODULE_CPU_SERVICE, "Dump process %{public}d cpu info failed!.", cpuUsagePid_);
363        }
364        return;
365    }
366    for (size_t i = 0; i < sortedInfos.size(); i++) {
367        char format[PROC_CPU_LENGTH] = {0};
368        int ret = sprintf_s(format, PROC_CPU_LENGTH,
369                            "    %-5s    %6.2f%%         %6.2f%%"
370                            "        %6.2f%%        %8s            %8s            %-15s",
371                            (sortedInfos[i]->pid).c_str(), sortedInfos[i]->totalUsage,
372                            sortedInfos[i]->userSpaceUsage, sortedInfos[i]->sysSpaceUsage,
373                            (sortedInfos[i]->minflt).c_str(), (sortedInfos[i]->majflt).c_str(),
374                            (sortedInfos[i]->comm).c_str());
375        if (ret < 0) {
376            continue;
377        }
378        AddStrLineToDumpInfo(std::string(format));
379    }
380}
381
382bool DumpManagerCpuService::SortProcInfo(std::shared_ptr<ProcInfo> &left, std::shared_ptr<ProcInfo> &right)
383{
384    if (right->totalUsage != left->totalUsage) {
385        return right->totalUsage < left->totalUsage;
386    }
387    if (right->userSpaceUsage != left->userSpaceUsage) {
388        return right->userSpaceUsage < left->userSpaceUsage;
389    }
390    if (right->sysSpaceUsage != left->sysSpaceUsage) {
391        return right->sysSpaceUsage < left->sysSpaceUsage;
392    }
393    if (right->pid.length() != left->pid.length()) {
394        return right->pid.length() < left->pid.length();
395    }
396    return (right->pid.compare(left->pid) < 0);
397}
398
399void DumpManagerCpuService::StartService()
400{
401    sptr<ISystemAbilityManager> samgr = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager();
402    if (samgr == nullptr) {
403        DUMPER_HILOGE(MODULE_CPU_SERVICE, "failed to find SystemAbilityManager.");
404        return;
405    }
406    auto dumpManagerCpuService = DumpDelayedSpSingleton<DumpManagerCpuService>::GetInstance();
407    int ret = samgr->AddSystemAbility(DFX_SYS_HIDUMPER_CPU_ABILITY_ID, dumpManagerCpuService);
408    if (ret != 0) {
409        DUMPER_HILOGE(MODULE_CPU_SERVICE, "failed to add sys dump cpu service ability.");
410        return;
411    }
412    OnStart();
413}
414
415// Authenticate dump permissions
416bool DumpManagerCpuService::HasDumpPermission() const
417{
418    uint32_t callingTokenID = IPCSkeleton::GetCallingTokenID();
419    int res = Security::AccessToken::AccessTokenKit::VerifyAccessToken(callingTokenID, "ohos.permission.DUMP");
420    if (res != Security::AccessToken::PermissionState::PERMISSION_GRANTED) {
421        DUMPER_HILOGI(MODULE_SERVICE, "No dump permission, please check!");
422        return false;
423    }
424    return true;
425}
426} // namespace HiviewDFX
427} // namespace OHOS
428