1 /*
2  * Copyright (C) 2020 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 "DvfsStateResidencyDataProvider.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 static const std::string nameSuffix = "-DVFS";
31 
32 namespace aidl {
33 namespace android {
34 namespace hardware {
35 namespace power {
36 namespace stats {
37 
DvfsStateResidencyDataProvider(std::string path,uint64_t clockRate,std::vector<Config> cfgs)38 DvfsStateResidencyDataProvider::DvfsStateResidencyDataProvider(std::string path, uint64_t clockRate,
39         std::vector<Config> cfgs)
40     : mPowerEntities(std::move(cfgs)), mPath(std::move(path)), mClockRate(clockRate) {}
41 
matchEntity(char const * line)42 int32_t DvfsStateResidencyDataProvider::matchEntity(char const *line) {
43     for (int32_t i = 0; i < mPowerEntities.size(); i++) {
44         if (mPowerEntities[i].powerEntityName == Trim(std::string(line))) {
45             return i;
46         }
47     }
48     return -1;
49 }
50 
matchState(char const * line,const Config & powerEntity)51 int32_t DvfsStateResidencyDataProvider::matchState(char const *line, const Config& powerEntity) {
52     for (int32_t i = 0; i < powerEntity.states.size(); i++) {
53         if (StartsWith(Trim(std::string(line)), powerEntity.states[i].second)) {
54             return i;
55         }
56     }
57     return -1;
58 }
59 
parseState(char const * line,uint64_t * duration,uint64_t * count)60 bool DvfsStateResidencyDataProvider::parseState(char const *line, uint64_t *duration,
61         uint64_t *count) {
62     std::vector<std::string> parts = Split(line, " ");
63     if (parts.size() != 7) {
64         return false;
65     }
66     if (!ParseUint(Trim(parts[3]), count)) {
67         return false;
68     }
69     if (!ParseUint(Trim(parts[6]), duration)) {
70         return false;
71     }
72     return true;
73 }
74 
getStateResidencies(std::unordered_map<std::string,std::vector<StateResidency>> * residencies)75 bool DvfsStateResidencyDataProvider::getStateResidencies(
76         std::unordered_map<std::string, std::vector<StateResidency>> *residencies) {
77     std::unique_ptr<FILE, decltype(&fclose)> fp(fopen(mPath.c_str(), "r"), fclose);
78     if (!fp) {
79         PLOG(ERROR) << __func__ << ":Failed to open file " << mPath;
80         return false;
81     }
82 
83     for (const Config &powerEntity : mPowerEntities) {
84         std::vector<StateResidency> stateResidency(powerEntity.states.size());
85         for (int32_t i = 0; i < stateResidency.size(); i++) {
86             stateResidency[i].id = i;
87         }
88         residencies->emplace(powerEntity.powerEntityName + nameSuffix, stateResidency);
89     }
90 
91     size_t len = 0;
92     char *line = nullptr;
93 
94     int32_t temp, powerEntityIndex, stateId = -1;
95     uint64_t duration, count;
96     auto it = residencies->end();
97 
98     while (getline(&line, &len, fp.get()) != -1) {
99         temp = matchEntity(line);
100         // Assign new index only when a new valid entity is encountered.
101         if (temp >= 0) {
102             powerEntityIndex = temp;
103             it = residencies->find(mPowerEntities[powerEntityIndex].powerEntityName + nameSuffix);
104         }
105 
106         // The given string is last state for each entity.
107         if (StartsWith(Trim(std::string(line)), "last_freq_change_time_ns:"))
108             it = residencies->end();
109 
110         if (it != residencies->end()) {
111             stateId = matchState(line, mPowerEntities[powerEntityIndex]);
112 
113             if (stateId >= 0) {
114                 if (parseState(line, &duration, &count)) {
115                     it->second[stateId].totalTimeInStateMs =
116                             duration / mClockRate;
117                     it->second[stateId].totalStateEntryCount = count;
118                 } else {
119                     LOG(ERROR) << "Failed to parse duration and count from [" << std::string(line)
120                                << "]";
121                     return false;
122                 }
123             }
124         }
125     }
126 
127     free(line);
128 
129     return true;
130 }
131 
getInfo()132 std::unordered_map<std::string, std::vector<State>> DvfsStateResidencyDataProvider::getInfo() {
133     std::unordered_map<std::string, std::vector<State>> info;
134     for (auto const &entity : mPowerEntities) {
135         std::vector<State> stateInfo(entity.states.size());
136         int32_t stateId = 0;
137         for (auto const &state : entity.states) {
138             stateInfo[stateId] = State{
139                 .id = stateId,
140                 .name = state.first
141             };
142             stateId++;
143         }
144         info.emplace(entity.powerEntityName + nameSuffix, stateInfo);
145     }
146     return info;
147 }
148 
149 }  // namespace stats
150 }  // namespace power
151 }  // namespace hardware
152 }  // namespace android
153 }  // namespace aidl
154