1 /*
2  * Copyright (C) 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 #include "CpupmStateResidencyDataProvider.h"
17 
18 #include <android-base/logging.h>
19 #include <android-base/parseint.h>
20 #include <android-base/strings.h>
21 
22 #include <string>
23 #include <utility>
24 
25 using android::base::ParseUint;
26 using android::base::Split;
27 using android::base::StartsWith;
28 using android::base::Trim;
29 
30 namespace aidl {
31 namespace android {
32 namespace hardware {
33 namespace power {
34 namespace stats {
35 
CpupmStateResidencyDataProvider(const std::string & path,const Config & config,const std::string & sleepPath,const SleepConfig & sleepConfig)36 CpupmStateResidencyDataProvider::CpupmStateResidencyDataProvider(
37         const std::string &path,
38         const Config &config,
39         const std::string &sleepPath,
40         const SleepConfig &sleepConfig)
41     : mPath(std::move(path)),
42       mConfig(std::move(config)),
43       mSleepPath(std::move(sleepPath)),
44       mSleepConfig(std::move(sleepConfig)) {}
45 
matchState(char const * line)46 int32_t CpupmStateResidencyDataProvider::matchState(char const *line) {
47     for (int32_t i = 0; i < mConfig.states.size(); i++) {
48         if (mConfig.states[i].second == Trim(std::string(line))) {
49             return i;
50         }
51     }
52     return -1;
53 }
54 
matchEntity(char const * line)55 int32_t CpupmStateResidencyDataProvider::matchEntity(char const *line) {
56     for (int32_t i = 0; i < mConfig.entities.size(); i++) {
57         if (StartsWith(Trim(std::string(line)), mConfig.entities[i].second)) {
58             return i;
59         }
60     }
61     return -1;
62 }
63 
parseState(char const * line,uint64_t * duration,uint64_t * count)64 bool CpupmStateResidencyDataProvider::parseState(
65         char const *line, uint64_t *duration, uint64_t *count) {
66     std::vector<std::string> parts = Split(line, " ");
67     if (parts.size() != 5) {
68         return false;
69     }
70     if (!ParseUint(Trim(parts[1]), count)) {
71         return false;
72     }
73     if (!ParseUint(Trim(parts[3]), duration)) {
74         return false;
75     }
76     return true;
77 }
78 
getStateResidencies(std::unordered_map<std::string,std::vector<StateResidency>> * residencies)79 bool CpupmStateResidencyDataProvider::getStateResidencies(
80         std::unordered_map<std::string, std::vector<StateResidency>> *residencies) {
81     std::unique_ptr<FILE, decltype(&fclose)> fp(fopen(mPath.c_str(), "r"), fclose);
82     if (!fp) {
83         PLOG(ERROR) << __func__ << ":Failed to open file " << mPath;
84         return false;
85     }
86 
87     std::unique_ptr<FILE, decltype(&fclose)> sleepFp(fopen(mSleepPath.c_str(), "r"), fclose);
88     if (!sleepFp) {
89         PLOG(ERROR) << __func__ << ":Failed to open file " << mSleepPath;
90         return false;
91     }
92 
93     for (int32_t i = 0; i < mConfig.entities.size(); i++) {
94         std::vector<StateResidency> stateResidencies(mConfig.states.size());
95         for (int32_t j = 0; j < stateResidencies.size(); j++) {
96             stateResidencies[j].id = j;
97         }
98         residencies->emplace(mConfig.entities[i].first, stateResidencies);
99     }
100 
101     size_t len = 0;
102     char *line = nullptr;
103 
104     int32_t temp, entityIndex, stateId = -1;
105     uint64_t duration, count, sleepDurationMs = 0;
106     auto it = residencies->end();
107     int32_t sleepIndex = 0;
108 
109     // Parse state for sleep duration
110     while (getline(&line, &len, sleepFp.get()) != -1) {
111         std::string trimedLine = Trim(std::string(line));
112         if (StartsWith(trimedLine, mSleepConfig[sleepIndex])) {
113             if (sleepIndex < mSleepConfig.size() - 1) {
114                 sleepIndex++;
115                 continue;
116             } else {
117                 std::vector<std::string> parts = Split(trimedLine, " ");
118                 if (parts.size() == 2) {
119                     ParseUint(parts[1], &sleepDurationMs);
120                     sleepDurationMs /= NS_TO_MS;
121                 }
122                 break;
123             }
124         }
125     }
126 
127     // Parse state for CPUPM entities
128     while (getline(&line, &len, fp.get()) != -1) {
129         temp = matchState(line);
130         // Assign new id only when a new valid state is encountered.
131         if (temp >= 0) {
132             stateId = temp;
133         }
134 
135         if (stateId < 0) continue;
136 
137         entityIndex = matchEntity(line);
138 
139         if (entityIndex < 0) continue;
140 
141         it = residencies->find(mConfig.entities[entityIndex].first);
142         if (it != residencies->end()) {
143             if (parseState(line, &duration, &count)) {
144                 it->second[stateId].totalTimeInStateMs = duration / US_TO_MS + sleepDurationMs;
145                 it->second[stateId].totalStateEntryCount = count;
146             } else {
147                 LOG(ERROR) << "Failed to parse duration and count from [" << std::string(line)
148                            << "]";
149                 return false;
150             }
151         }
152     }
153 
154     free(line);
155 
156     return true;
157 }
158 
getInfo()159 std::unordered_map<std::string, std::vector<State>> CpupmStateResidencyDataProvider::getInfo() {
160     std::unordered_map<std::string, std::vector<State>> info;
161     for (auto const &entity : mConfig.entities) {
162         std::vector<State> stateInfo(mConfig.states.size());
163         int32_t stateId = 0;
164         for (auto const &state : mConfig.states) {
165             stateInfo[stateId] = State{
166                 .id = stateId,
167                 .name = state.first
168             };
169             stateId++;
170         }
171         info.emplace(entity.first, stateInfo);
172     }
173     return info;
174 }
175 
176 }  // namespace stats
177 }  // namespace power
178 }  // namespace hardware
179 }  // namespace android
180 }  // namespace aidl
181