1/*
2 * Copyright (c) 2021-2022 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
16#include "cpu_time_reader.h"
17
18#include <fstream>
19#include "string_ex.h"
20
21#include "battery_stats_service.h"
22#include "stats_helper.h"
23#include "stats_log.h"
24#include "stats_utils.h"
25
26namespace OHOS {
27namespace PowerMgr {
28namespace {
29static const std::string UID_CPU_ACTIVE_TIME_FILE = "/proc/uid_concurrent_active_time";
30static const std::string UID_CPU_CLUSTER_TIME_FILE = "/proc/uid_concurrent_policy_time";
31static const std::string UID_CPU_FREQ_TIME_FILE = "/proc/uid_time_in_state";
32static const std::string UID_CPU_TIME_FILE = "/proc/uid_cputime/show_uid_stat";
33} // namespace
34bool CpuTimeReader::Init()
35{
36    if (!UpdateCpuTime()) {
37        STATS_HILOGW(COMP_SVC, "Update cpu time failed");
38    }
39    return true;
40}
41
42int64_t CpuTimeReader::GetUidCpuActiveTimeMs(int32_t uid)
43{
44    int64_t cpuActiveTime = 0;
45    auto iter = activeTimeMap_.find(uid);
46    if (iter != activeTimeMap_.end()) {
47        cpuActiveTime = iter->second;
48        STATS_HILOGD(COMP_SVC, "Get cpu active time: %{public}s for uid: %{public}d",
49            std::to_string(cpuActiveTime).c_str(), uid);
50    } else {
51        STATS_HILOGD(COMP_SVC, "No cpu active time found for uid: %{public}d, return 0", uid);
52    }
53    return cpuActiveTime;
54}
55
56void CpuTimeReader::DumpInfo(std::string& result, int32_t uid)
57{
58    auto uidIter = lastUidTimeMap_.find(uid);
59    if (uidIter == lastUidTimeMap_.end()) {
60        STATS_HILOGE(COMP_SVC, "No related CPU info for uid: %{public}d", uid);
61        return;
62    }
63    std::string freqTime = "";
64    auto freqIter = lastFreqTimeMap_.find(uid);
65    if (freqIter != lastFreqTimeMap_.end()) {
66        for (auto timeIter = freqIter->second.begin(); timeIter != freqIter->second.end(); timeIter++) {
67            for (uint32_t i = 0; i < timeIter->second.size(); i++) {
68                freqTime.append(ToString(timeIter->second[i]))
69                    .append(" ");
70            }
71        }
72    }
73    result.append("Total cpu time: userSpaceTime=")
74        .append(ToString(uidIter->second[0]))
75        .append("ms, systemSpaceTime=")
76        .append(ToString(uidIter->second[1]))
77        .append("ms\n")
78        .append("Total cpu time per freq: ")
79        .append(freqTime)
80        .append("\n");
81}
82
83int64_t CpuTimeReader::GetUidCpuClusterTimeMs(int32_t uid, uint32_t cluster)
84{
85    int64_t cpuClusterTime = 0;
86    auto iter = clusterTimeMap_.find(uid);
87    if (iter != clusterTimeMap_.end()) {
88        auto cpuClusterTimeVector = iter->second;
89        if (cluster < cpuClusterTimeVector.size()) {
90            cpuClusterTime = cpuClusterTimeVector[cluster];
91            STATS_HILOGD(COMP_SVC, "Get cpu cluster time: %{public}s of cluster: %{public}d",
92                std::to_string(cpuClusterTime).c_str(), cluster);
93        } else {
94            STATS_HILOGD(COMP_SVC, "No cpu cluster time of cluster: %{public}d found, return 0", cluster);
95        }
96    } else {
97        STATS_HILOGD(COMP_SVC, "No cpu cluster time vector found for uid: %{public}d, return 0", uid);
98    }
99    return cpuClusterTime;
100}
101
102int64_t CpuTimeReader::GetUidCpuFreqTimeMs(int32_t uid, uint32_t cluster, uint32_t speed)
103{
104    int64_t cpuFreqTime = 0;
105    auto uidIter = freqTimeMap_.find(uid);
106    if (uidIter != freqTimeMap_.end()) {
107        auto cpuFreqTimeMap = uidIter->second;
108        auto clusterIter = cpuFreqTimeMap.find(cluster);
109        if (clusterIter != cpuFreqTimeMap.end()) {
110            auto cpuFreqTimeVector = clusterIter->second;
111            if (speed < cpuFreqTimeVector.size()) {
112                cpuFreqTime = cpuFreqTimeVector[speed];
113                STATS_HILOGD(COMP_SVC, "Get cpu freq time: %{public}s of speed: %{public}d",
114                    std::to_string(cpuFreqTime).c_str(), speed);
115            } else {
116                STATS_HILOGD(COMP_SVC, "No cpu freq time of speed: %{public}d found, return 0", speed);
117            }
118        } else {
119            STATS_HILOGD(COMP_SVC, "No cluster cpu freq time vector of cluster: %{public}d found, return 0",
120                cluster);
121        }
122    } else {
123        STATS_HILOGD(COMP_SVC, "No uid cpu freq time map found for uid: %{public}d, return 0", uid);
124    }
125    return cpuFreqTime;
126}
127
128std::vector<int64_t> CpuTimeReader::GetUidCpuTimeMs(int32_t uid)
129{
130    std::vector<int64_t> cpuTimeVec;
131    auto iter = uidTimeMap_.find(uid);
132    if (iter != uidTimeMap_.end()) {
133        cpuTimeVec = iter->second;
134        STATS_HILOGD(COMP_SVC, "Get uid cpu time vector for uid: %{public}d, size: %{public}d", uid,
135            static_cast<int32_t>(cpuTimeVec.size()));
136    } else {
137        STATS_HILOGD(COMP_SVC, "No uid cpu time vector found for uid: %{public}d, return null", uid);
138    }
139    return cpuTimeVec;
140}
141
142bool CpuTimeReader::UpdateCpuTime()
143{
144    bool result = true;
145    if (!ReadUidCpuClusterTime()) {
146        STATS_HILOGW(COMP_SVC, "Read uid cpu cluster time failed");
147        result = false;
148    }
149
150    if (!ReadUidCpuTime()) {
151        STATS_HILOGW(COMP_SVC, "Read uid cpu time failed");
152        result = false;
153    }
154
155    if (!ReadUidCpuActiveTime()) {
156        STATS_HILOGW(COMP_SVC, "Read uid cpu active time failed");
157        result = false;
158    }
159
160    if (!ReadUidCpuFreqTime()) {
161        STATS_HILOGW(COMP_SVC, "Read uid cpu freq time failed");
162        result = false;
163    }
164    return result;
165}
166
167bool CpuTimeReader::ReadUidCpuActiveTimeImpl(std::string& line, int32_t uid)
168{
169    int64_t timeMs = 0;
170    std::vector<std::string> splitedTime;
171    Split(line, ' ', splitedTime);
172    for (uint16_t i = 0; i < splitedTime.size(); i++) {
173        timeMs += stoll(splitedTime[i]) * 10; // Unit is 10ms
174    }
175
176    int64_t increment = 0;
177    if (timeMs > 0) {
178        auto iterLast = lastActiveTimeMap_.find(uid);
179        if (iterLast != lastActiveTimeMap_.end()) {
180            increment = timeMs - iterLast->second;
181            if (increment >= 0) {
182                iterLast->second = timeMs;
183            } else {
184                STATS_HILOGI(COMP_SVC, "Negative cpu active time increment");
185                return false;
186            }
187        } else {
188            lastActiveTimeMap_.insert(std::pair<int32_t, int64_t>(uid, timeMs));
189            increment = timeMs;
190        }
191    }
192
193    if (StatsHelper::IsOnBattery()) {
194        STATS_HILOGD(COMP_SVC, "Power supply is not connected. Add the increment");
195        auto iter = activeTimeMap_.find(uid);
196        if (iter != activeTimeMap_.end()) {
197            iter->second += increment;
198        } else {
199            activeTimeMap_.insert(std::pair<int32_t, int64_t>(uid, increment));
200            STATS_HILOGI(COMP_SVC, "Add active time: %{public}sms, uid: %{public}d",
201                std::to_string(increment).c_str(), uid);
202        }
203    }
204    return true;
205}
206
207bool CpuTimeReader::ReadUidCpuActiveTime()
208{
209    std::ifstream input(UID_CPU_ACTIVE_TIME_FILE);
210    if (!input) {
211        STATS_HILOGW(COMP_SVC, "Open file failed");
212        return false;
213    }
214
215    std::string line;
216    const int32_t INDEX_0 = 0;
217    const int32_t INDEX_1 = 1;
218    while (getline(input, line)) {
219        int32_t uid = StatsUtils::INVALID_VALUE;
220        std::vector<std::string> splitedLine;
221        Split(line, ':', splitedLine);
222        if (splitedLine[INDEX_0] == "cpus") {
223            continue;
224        } else {
225            uid = stoi(splitedLine[INDEX_0]);
226        }
227
228        if (uid > StatsUtils::INVALID_VALUE) {
229            auto bss = BatteryStatsService::GetInstance();
230            auto uidEntity =
231                bss->GetBatteryStatsCore()->GetEntity(BatteryStatsInfo::CONSUMPTION_TYPE_APP);
232            if (uidEntity) {
233                uidEntity->UpdateUidMap(uid);
234            }
235        }
236
237        if (ReadUidCpuActiveTimeImpl(splitedLine[INDEX_1], uid)) {
238            continue;
239        } else {
240            return false;
241        }
242    }
243    return true;
244}
245
246void CpuTimeReader::ReadPolicy(std::vector<uint16_t>& clusters, std::string& line)
247{
248    std::vector<std::string> splitedPolicy;
249    Split(line, ' ', splitedPolicy);
250    uint32_t step = 2;
251    for (uint32_t i = 0; i < splitedPolicy.size(); i += step) {
252        uint16_t coreNum = static_cast<uint16_t>(stoi(splitedPolicy[i + 1]));
253        clusters.push_back(coreNum);
254        clustersMap_.insert(std::pair<uint16_t, uint16_t>(i, coreNum));
255    }
256}
257
258bool CpuTimeReader::ReadClusterTimeIncrement(std::vector<int64_t>& clusterTime, std::vector<int64_t>& increments,
259    int32_t uid, std::vector<uint16_t>& clusters, std::string& timeLine)
260{
261    std::vector<std::string> splitedTime;
262    Split(timeLine, ' ', splitedTime);
263    uint16_t count = 0;
264    for (uint16_t i = 0; i < clusters.size(); i++) {
265        int64_t tempTimeMs = 0;
266        for (uint16_t j = 0; j < clusters[i]; j++) {
267            tempTimeMs += stoll(splitedTime[count++]) * 10; // Unit is 10ms
268        }
269        clusterTime.push_back(tempTimeMs);
270    }
271
272    auto iterLast = lastClusterTimeMap_.find(uid);
273    if (iterLast != lastClusterTimeMap_.end()) {
274        for (uint16_t i = 0; i < clusters.size(); i++) {
275            int64_t increment = clusterTime[i] - iterLast->second[i];
276            if (increment >= 0) {
277                iterLast->second[i] = clusterTime[i];
278                increments.push_back(increment);
279            } else {
280                STATS_HILOGD(COMP_SVC, "Negative cpu cluster time increment");
281                return false;
282            }
283        }
284    } else {
285        lastClusterTimeMap_.insert(std::pair<int32_t, std::vector<int64_t>>(uid, clusterTime));
286        increments = clusterTime;
287        STATS_HILOGI(COMP_SVC, "Add last cpu cluster time for uid: %{public}d", uid);
288    }
289    return true;
290}
291
292bool CpuTimeReader::ReadUidCpuClusterTime()
293{
294    std::ifstream input(UID_CPU_CLUSTER_TIME_FILE);
295    if (!input) {
296        STATS_HILOGW(COMP_SVC, "Open file failed");
297        return false;
298    }
299    std::string line;
300    int32_t uid = -1;
301    std::vector<uint16_t> clusters;
302    std::vector<int64_t> clusterTime;
303    while (getline(input, line)) {
304        clusterTime.clear();
305        if (line.find("policy") != line.npos) {
306            ReadPolicy(clusters, line);
307            continue;
308        }
309
310        std::vector<std::string> splitedLine;
311        Split(line, ':', splitedLine);
312        uid = stoi(splitedLine[0]);
313        if (uid > StatsUtils::INVALID_VALUE) {
314            auto bss = BatteryStatsService::GetInstance();
315            auto uidEntity = bss->GetBatteryStatsCore()->GetEntity(BatteryStatsInfo::CONSUMPTION_TYPE_APP);
316            if (uidEntity) {
317                uidEntity->UpdateUidMap(uid);
318            }
319        }
320
321        std::vector<int64_t> increments;
322        if (!ReadClusterTimeIncrement(clusterTime, increments, uid, clusters, splitedLine[1])) {
323            return false;
324        }
325
326        if (StatsHelper::IsOnBattery()) {
327            STATS_HILOGD(COMP_SVC, "Power supply is not connected. Add the increment");
328            auto iter = clusterTimeMap_.find(uid);
329            if (iter != clusterTimeMap_.end()) {
330                AddIncrementsToClusterTime(iter->second, increments, clusters);
331            } else {
332                clusterTimeMap_.insert(std::pair<int32_t, std::vector<int64_t>>(uid, increments));
333                STATS_HILOGI(COMP_SVC, "Add cpu cluster time for uid: %{public}d", uid);
334            }
335        }
336    }
337    return true;
338}
339
340void CpuTimeReader::AddIncrementsToClusterTime(std::vector<int64_t>& clusterTime,
341    const std::vector<int64_t>& increments, const std::vector<uint16_t>& clusters)
342{
343    for (uint16_t i = 0; i < clusters.size(); i++) {
344        clusterTime[i] += increments[i];
345    }
346}
347
348bool CpuTimeReader::ProcessFreqTime(std::map<uint32_t, std::vector<int64_t>>& map, std::map<uint32_t,
349    std::vector<int64_t>>& increments, std::map<uint32_t, std::vector<int64_t>>& speedTime, int32_t index, int32_t uid)
350{
351    auto iterLastTemp = map.find(index);
352    if (iterLastTemp != map.end()) {
353        std::vector<int64_t> lastSpeedTimes = iterLastTemp->second;
354        std::vector<int64_t> newIncrementTimes;
355        newIncrementTimes.clear();
356        for (uint16_t j = 0; j < lastSpeedTimes.size(); j++) {
357            int64_t increment = speedTime.at(index)[j] - lastSpeedTimes[j];
358            if (increment >= 0) {
359                newIncrementTimes.push_back(increment);
360                increments.insert(std::pair<uint32_t, std::vector<int64_t>>(index, newIncrementTimes));
361            } else {
362                STATS_HILOGI(COMP_SVC, "Negative cpu freq time increment");
363                return false;
364            }
365        }
366        iterLastTemp->second = speedTime.at(index);
367    }
368    return true;
369}
370
371bool CpuTimeReader::ReadFreqTimeIncrement(std::map<uint32_t, std::vector<int64_t>>& speedTime,
372    std::map<uint32_t, std::vector<int64_t>>& increments, int32_t uid, std::vector<std::string>& splitedTime)
373{
374    auto bss = BatteryStatsService::GetInstance();
375    auto parser = bss->GetBatteryStatsParser();
376    uint16_t clusterNum = parser->GetClusterNum();
377    uint16_t count = 0;
378    for (uint16_t i = 0; i < clusterNum; i++) {
379        std::vector<int64_t> tempSpeedTimes;
380        tempSpeedTimes.clear();
381        for (uint16_t j = 0; j < parser->GetSpeedNum(i); j++) {
382            int64_t tempTimeMs = stoll(splitedTime[count++]) * 10; // Unit is 10ms
383            tempSpeedTimes.push_back(tempTimeMs);
384        }
385        speedTime.insert(std::pair<uint32_t, std::vector<int64_t>>(i, tempSpeedTimes));
386    }
387
388    auto iterLast = lastFreqTimeMap_.find(uid);
389    if (iterLast == lastFreqTimeMap_.end()) {
390        lastFreqTimeMap_.insert(std::pair<int32_t, std::map<uint32_t, std::vector<int64_t>>>(uid, speedTime));
391        increments = speedTime;
392        STATS_HILOGI(COMP_SVC, "Add last cpu freq time for uid: %{public}d", uid);
393        return true;
394    }
395    for (uint16_t i = 0; i < lastFreqTimeMap_.size(); i++) {
396        if (!ProcessFreqTime(iterLast->second, increments, speedTime, i, uid)) {
397            return false;
398        }
399    }
400    return true;
401}
402
403void CpuTimeReader::DistributeFreqTime(std::map<uint32_t, std::vector<int64_t>>& uidIncrements,
404    std::map<uint32_t, std::vector<int64_t>>& increments)
405{
406    auto bss = BatteryStatsService::GetInstance();
407    auto parser = bss->GetBatteryStatsParser();
408    uint16_t clusterNum = parser->GetClusterNum();
409    if (wakelockCounts_ > 0) {
410        for (uint16_t i = 0; i < clusterNum; i++) {
411            uint16_t speedNum = parser->GetSpeedNum(i);
412            for (uint16_t j = 0; j < speedNum; j++) {
413                int32_t step = 2;
414                uidIncrements.at(i)[j] = increments.at(i)[j] / step;
415            }
416        }
417        // TO-DO, distribute half of cpu freq time to wakelock holders
418    }
419}
420
421void CpuTimeReader::AddFreqTimeToUid(std::map<uint32_t, std::vector<int64_t>>& uidIncrements, int32_t uid)
422{
423    auto bss = BatteryStatsService::GetInstance();
424    auto parser = bss->GetBatteryStatsParser();
425    uint16_t clusterNum = parser->GetClusterNum();
426    auto iter = freqTimeMap_.find(uid);
427    if (iter != freqTimeMap_.end()) {
428        for (uint16_t i = 0; i < clusterNum; i++) {
429            uint16_t speedNum = parser->GetSpeedNum(i);
430            for (uint16_t j = 0; j < speedNum; j++) {
431                iter->second.at(i)[j] += uidIncrements.at(i)[j];
432            }
433        }
434    } else {
435        freqTimeMap_.insert(std::pair<int32_t, std::map<uint32_t, std::vector<int64_t>>>(uid, uidIncrements));
436        STATS_HILOGI(COMP_SVC, "Add cpu freq time for uid: %{public}d", uid);
437    }
438}
439
440bool CpuTimeReader::ReadUidCpuFreqTime()
441{
442    std::ifstream input(UID_CPU_FREQ_TIME_FILE);
443    if (!input) {
444        STATS_HILOGW(COMP_SVC, "Open file failed");
445        return false;
446    }
447    std::string line;
448    int32_t uid = -1;
449    std::map<uint32_t, std::vector<int64_t>> speedTime;
450    while (getline(input, line)) {
451        speedTime.clear();
452        std::vector<std::string> splitedLine;
453        Split(line, ':', splitedLine);
454        if (splitedLine[0] == "uid") {
455            continue;
456        } else {
457            uid = stoi(splitedLine[0]);
458        }
459
460        if (uid > StatsUtils::INVALID_VALUE) {
461            auto bss = BatteryStatsService::GetInstance();
462            auto uidEntity =
463                bss->GetBatteryStatsCore()->GetEntity(BatteryStatsInfo::CONSUMPTION_TYPE_APP);
464            if (uidEntity) {
465                uidEntity->UpdateUidMap(uid);
466            }
467        }
468
469        std::vector<std::string> splitedTime;
470        Split(splitedLine[1], ' ', splitedTime);
471
472        std::map<uint32_t, std::vector<int64_t>> increments;
473        if (!ReadFreqTimeIncrement(speedTime, increments, uid, splitedTime)) {
474            return false;
475        }
476
477        std::map<uint32_t, std::vector<int64_t>> uidIncrements = increments;
478        DistributeFreqTime(uidIncrements, increments);
479
480        if (!StatsHelper::IsOnBattery()) {
481            STATS_HILOGD(COMP_SVC, "Power supply is connected, don't add the increment");
482            continue;
483        }
484        AddFreqTimeToUid(uidIncrements, uid);
485    }
486    return true;
487}
488
489bool CpuTimeReader::ReadUidTimeIncrement(std::vector<int64_t>& cpuTime, std::vector<int64_t>& uidIncrements,
490    int32_t uid, std::string& timeLine)
491{
492    std::vector<std::string> splitedTime;
493    Split(timeLine, ' ', splitedTime);
494    for (uint16_t i = 0; i < splitedTime.size(); i++) {
495        int64_t tempTime = 0;
496        tempTime = stoll(splitedTime[i]);
497        cpuTime.push_back(tempTime);
498    }
499
500    std::vector<int64_t> increments;
501    auto iterLast = lastUidTimeMap_.find(uid);
502    if (iterLast != lastUidTimeMap_.end()) {
503        for (uint16_t i = 0; i < splitedTime.size(); i++) {
504            int64_t increment = 0;
505            increment = cpuTime[i] - iterLast->second[i];
506            if (increment >= 0) {
507                iterLast->second[i] = cpuTime[i];
508                increments.push_back(increment);
509            } else {
510                STATS_HILOGI(COMP_SVC, "Negative cpu time increment");
511                return false;
512            }
513        }
514    } else {
515        lastUidTimeMap_.insert(std::pair<int32_t, std::vector<int64_t>>(uid, cpuTime));
516        increments = cpuTime;
517        STATS_HILOGI(COMP_SVC, "Add last cpu time for uid: %{public}d", uid);
518    }
519
520    uidIncrements = increments;
521
522    if (wakelockCounts_ > 0) {
523        double weight = 0.5;
524        uidIncrements[0] = increments[0] / (StatsUtils::US_IN_MS * 1.0) * weight;
525        uidIncrements[1] = increments[1] / (StatsUtils::US_IN_MS * 1.0) * weight;
526        // TO-DO, distribute half of cpu time to wakelock holders
527    }
528    return true;
529}
530
531bool CpuTimeReader::ReadUidCpuTime()
532{
533    std::ifstream input(UID_CPU_TIME_FILE);
534    if (!input) {
535        STATS_HILOGW(COMP_SVC, "Open file failed");
536        return false;
537    }
538    std::string line;
539    std::vector<int64_t> cpuTime;
540    while (getline(input, line)) {
541        cpuTime.clear();
542        std::vector<std::string> splitedLine;
543        Split(line, ':', splitedLine);
544        int32_t uid = stoi(splitedLine[0]);
545        if (uid > StatsUtils::INVALID_VALUE) {
546            auto bss = BatteryStatsService::GetInstance();
547            auto uidEntity =
548                bss->GetBatteryStatsCore()->GetEntity(BatteryStatsInfo::CONSUMPTION_TYPE_APP);
549            if (uidEntity) {
550                uidEntity->UpdateUidMap(uid);
551            }
552        }
553
554        std::vector<int64_t> uidIncrements;
555        if (!ReadUidTimeIncrement(cpuTime, uidIncrements, uid, splitedLine[1])) {
556            return false;
557        }
558
559        if (StatsHelper::IsOnBattery()) {
560            STATS_HILOGD(COMP_SVC, "Power supply is not connected. Add the increment");
561            UpdateUidTimeMap(uid, uidIncrements);
562        }
563    }
564    return true;
565}
566
567void CpuTimeReader::UpdateUidTimeMap(int32_t uid, const std::vector<int64_t>& uidIncrements)
568{
569    auto iter = uidTimeMap_.find(uid);
570    if (iter != uidTimeMap_.end()) {
571        for (uint16_t i = 0; i < uidIncrements.size(); i++) {
572            iter->second[i] = uidIncrements[i];
573        }
574    } else {
575        uidTimeMap_.insert(std::pair<int32_t, std::vector<int64_t>>(uid, uidIncrements));
576        STATS_HILOGI(COMP_SVC, "Add cpu time for uid: %{public}d", uid);
577    }
578}
579
580void CpuTimeReader::Split(std::string &origin, char delimiter, std::vector<std::string> &splited)
581{
582    size_t start;
583    size_t end = 0;
584
585    while ((start = origin.find_first_not_of(delimiter, end)) != std::string::npos) {
586        end = origin.find(delimiter, start);
587        splited.push_back(origin.substr(start, end - start));
588    }
589}
590} // namespace PowerMgr
591} // namespace OHOS