• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 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 
17 #define LOG_TAG "android.hardware.power.stats@1.0-service-mock"
18 
19 #include "PowerStats.h"
20 #include <android-base/file.h>
21 #include <android-base/logging.h>
22 #include <android-base/properties.h>
23 #include <android-base/stringprintf.h>
24 #include <android-base/strings.h>
25 #include <inttypes.h>
26 #include <stdlib.h>
27 #include <algorithm>
28 #include <exception>
29 #include <thread>
30 
31 namespace android {
32 namespace hardware {
33 namespace power {
34 namespace stats {
35 namespace V1_0 {
36 namespace implementation {
37 
38 #define MAX_FILE_PATH_LEN 128
39 #define MAX_DEVICE_NAME_LEN 64
40 #define MAX_QUEUE_SIZE 8192
41 
42 constexpr char kIioDirRoot[] = "/sys/bus/iio/devices/";
43 constexpr char kDeviceName[] = "pm_device_name";
44 constexpr char kDeviceType[] = "iio:device";
45 constexpr uint32_t MAX_SAMPLING_RATE = 10;
46 constexpr uint64_t WRITE_TIMEOUT_NS = 1000000000;
47 
findIioPowerMonitorNodes()48 void PowerStats::findIioPowerMonitorNodes() {
49     struct dirent* ent;
50     int fd;
51     char devName[MAX_DEVICE_NAME_LEN];
52     char filePath[MAX_FILE_PATH_LEN];
53     DIR* iioDir = opendir(kIioDirRoot);
54     if (!iioDir) {
55         ALOGE("Error opening directory: %s", kIioDirRoot);
56         return;
57     }
58     while (ent = readdir(iioDir), ent) {
59         if (strcmp(ent->d_name, ".") != 0 && strcmp(ent->d_name, "..") != 0 &&
60             strlen(ent->d_name) > strlen(kDeviceType) &&
61             strncmp(ent->d_name, kDeviceType, strlen(kDeviceType)) == 0) {
62             snprintf(filePath, MAX_FILE_PATH_LEN, "%s/%s", ent->d_name, "name");
63             fd = openat(dirfd(iioDir), filePath, O_RDONLY);
64             if (fd < 0) {
65                 ALOGW("Failed to open directory: %s", filePath);
66                 continue;
67             }
68             if (read(fd, devName, MAX_DEVICE_NAME_LEN) < 0) {
69                 ALOGW("Failed to read device name from file: %s(%d)", filePath, fd);
70                 close(fd);
71                 continue;
72             }
73 
74             if (strncmp(devName, kDeviceName, strlen(kDeviceName)) == 0) {
75                 snprintf(filePath, MAX_FILE_PATH_LEN, "%s/%s", kIioDirRoot, ent->d_name);
76                 mPm.devicePaths.push_back(filePath);
77             }
78             close(fd);
79         }
80     }
81     closedir(iioDir);
82     return;
83 }
84 
parsePowerRails()85 size_t PowerStats::parsePowerRails() {
86     std::string data;
87     std::string railFileName;
88     std::string spsFileName;
89     uint32_t index = 0;
90     uint32_t samplingRate;
91     for (const auto& path : mPm.devicePaths) {
92         railFileName = path + "/enabled_rails";
93         spsFileName = path + "/sampling_rate";
94         if (!android::base::ReadFileToString(spsFileName, &data)) {
95             ALOGW("Error reading file: %s", spsFileName.c_str());
96             continue;
97         }
98         samplingRate = strtoul(data.c_str(), NULL, 10);
99         if (!samplingRate || samplingRate == ULONG_MAX) {
100             ALOGE("Error parsing: %s", spsFileName.c_str());
101             break;
102         }
103         if (!android::base::ReadFileToString(railFileName, &data)) {
104             ALOGW("Error reading file: %s", railFileName.c_str());
105             continue;
106         }
107         std::istringstream railNames(data);
108         std::string line;
109         while (std::getline(railNames, line)) {
110             std::vector<std::string> words = android::base::Split(line, ":");
111             if (words.size() == 2) {
112                 mPm.railsInfo.emplace(words[0], RailData{.devicePath = path,
113                                                          .index = index,
114                                                          .subsysName = words[1],
115                                                          .samplingRate = samplingRate});
116                 index++;
117             } else {
118                 ALOGW("Unexpected format in file: %s", railFileName.c_str());
119             }
120         }
121     }
122     return index;
123 }
124 
parseIioEnergyNode(std::string devName)125 int PowerStats::parseIioEnergyNode(std::string devName) {
126     int ret = 0;
127     std::string data;
128     std::string fileName = devName + "/energy_value";
129     if (!android::base::ReadFileToString(fileName, &data)) {
130         ALOGE("Error reading file: %s", fileName.c_str());
131         return -1;
132     }
133 
134     std::istringstream energyData(data);
135     std::string line;
136     uint64_t timestamp = 0;
137     bool timestampRead = false;
138     while (std::getline(energyData, line)) {
139         std::vector<std::string> words = android::base::Split(line, ",");
140         if (timestampRead == false) {
141             if (words.size() == 1) {
142                 timestamp = strtoull(words[0].c_str(), NULL, 10);
143                 if (timestamp == 0 || timestamp == ULLONG_MAX) {
144                     ALOGW("Potentially wrong timestamp: %" PRIu64, timestamp);
145                 }
146                 timestampRead = true;
147             }
148         } else if (words.size() == 2) {
149             std::string railName = words[0];
150             if (mPm.railsInfo.count(railName) != 0) {
151                 size_t index = mPm.railsInfo[railName].index;
152                 mPm.reading[index].index = index;
153                 mPm.reading[index].timestamp = timestamp;
154                 mPm.reading[index].energy = strtoull(words[1].c_str(), NULL, 10);
155                 if (mPm.reading[index].energy == ULLONG_MAX) {
156                     ALOGW("Potentially wrong energy value: %" PRIu64, mPm.reading[index].energy);
157                 }
158             }
159         } else {
160             ALOGW("Unexpected format in file: %s", fileName.c_str());
161             ret = -1;
162             break;
163         }
164     }
165     return ret;
166 }
167 
parseIioEnergyNodes()168 Status PowerStats::parseIioEnergyNodes() {
169     Status ret = Status::SUCCESS;
170     if (mPm.hwEnabled == false) {
171         return Status::NOT_SUPPORTED;
172     }
173 
174     for (const auto& devicePath : mPm.devicePaths) {
175         if (parseIioEnergyNode(devicePath) < 0) {
176             ALOGE("Error in parsing power stats");
177             ret = Status::FILESYSTEM_ERROR;
178             break;
179         }
180     }
181     return ret;
182 }
183 
PowerStats()184 PowerStats::PowerStats() {
185     findIioPowerMonitorNodes();
186     size_t numRails = parsePowerRails();
187     if (mPm.devicePaths.empty() || numRails == 0) {
188         mPm.hwEnabled = false;
189     } else {
190         mPm.hwEnabled = true;
191         mPm.reading.resize(numRails);
192     }
193 }
194 
getRailInfo(getRailInfo_cb _hidl_cb)195 Return<void> PowerStats::getRailInfo(getRailInfo_cb _hidl_cb) {
196     hidl_vec<RailInfo> rInfo;
197     Status ret = Status::SUCCESS;
198     size_t index;
199     std::lock_guard<std::mutex> _lock(mPm.mLock);
200     if (mPm.hwEnabled == false) {
201         _hidl_cb(rInfo, Status::NOT_SUPPORTED);
202         return Void();
203     }
204     rInfo.resize(mPm.railsInfo.size());
205     for (const auto& railData : mPm.railsInfo) {
206         index = railData.second.index;
207         rInfo[index].railName = railData.first;
208         rInfo[index].subsysName = railData.second.subsysName;
209         rInfo[index].index = index;
210         rInfo[index].samplingRate = railData.second.samplingRate;
211     }
212     _hidl_cb(rInfo, ret);
213     return Void();
214 }
215 
getEnergyData(const hidl_vec<uint32_t> & railIndices,getEnergyData_cb _hidl_cb)216 Return<void> PowerStats::getEnergyData(const hidl_vec<uint32_t>& railIndices,
217                                        getEnergyData_cb _hidl_cb) {
218     hidl_vec<EnergyData> eVal;
219     std::lock_guard<std::mutex> _lock(mPm.mLock);
220     Status ret = parseIioEnergyNodes();
221 
222     if (ret != Status::SUCCESS) {
223         ALOGE("Failed to getEnergyData");
224         _hidl_cb(eVal, ret);
225         return Void();
226     }
227 
228     if (railIndices.size() == 0) {
229         eVal.resize(mPm.railsInfo.size());
230         memcpy(&eVal[0], &mPm.reading[0], mPm.reading.size() * sizeof(EnergyData));
231     } else {
232         eVal.resize(railIndices.size());
233         int i = 0;
234         for (const auto& railIndex : railIndices) {
235             if (railIndex >= mPm.reading.size()) {
236                 ret = Status::INVALID_INPUT;
237                 eVal.resize(0);
238                 break;
239             }
240             memcpy(&eVal[i], &mPm.reading[railIndex], sizeof(EnergyData));
241             i++;
242         }
243     }
244     _hidl_cb(eVal, ret);
245     return Void();
246 }
247 
streamEnergyData(uint32_t timeMs,uint32_t samplingRate,streamEnergyData_cb _hidl_cb)248 Return<void> PowerStats::streamEnergyData(uint32_t timeMs, uint32_t samplingRate,
249                                           streamEnergyData_cb _hidl_cb) {
250     std::lock_guard<std::mutex> _lock(mPm.mLock);
251     if (mPm.fmqSynchronized != nullptr) {
252         _hidl_cb(MessageQueueSync::Descriptor(), 0, 0, Status::INSUFFICIENT_RESOURCES);
253         return Void();
254     }
255     uint32_t sps = std::min(samplingRate, MAX_SAMPLING_RATE);
256     uint32_t numSamples = timeMs * sps / 1000;
257     mPm.fmqSynchronized.reset(new (std::nothrow) MessageQueueSync(MAX_QUEUE_SIZE, true));
258     if (mPm.fmqSynchronized == nullptr || mPm.fmqSynchronized->isValid() == false) {
259         mPm.fmqSynchronized = nullptr;
260         _hidl_cb(MessageQueueSync::Descriptor(), 0, 0, Status::INSUFFICIENT_RESOURCES);
261         return Void();
262     }
263     std::thread pollThread = std::thread([this, sps, numSamples]() {
264         uint64_t sleepTimeUs = 1000000 / sps;
265         uint32_t currSamples = 0;
266         while (currSamples < numSamples) {
267             mPm.mLock.lock();
268             if (parseIioEnergyNodes() == Status::SUCCESS) {
269                 mPm.fmqSynchronized->writeBlocking(&mPm.reading[0], mPm.reading.size(),
270                                                    WRITE_TIMEOUT_NS);
271                 mPm.mLock.unlock();
272                 currSamples++;
273                 if (usleep(sleepTimeUs) < 0) {
274                     ALOGW("Sleep interrupted");
275                     break;
276                 }
277             } else {
278                 mPm.mLock.unlock();
279                 break;
280             }
281         }
282         mPm.mLock.lock();
283         mPm.fmqSynchronized = nullptr;
284         mPm.mLock.unlock();
285         return;
286     });
287     pollThread.detach();
288     _hidl_cb(*(mPm.fmqSynchronized)->getDesc(), numSamples, mPm.reading.size(), Status::SUCCESS);
289     return Void();
290 }
291 
addPowerEntity(const std::string & name,PowerEntityType type)292 uint32_t PowerStats::addPowerEntity(const std::string& name, PowerEntityType type) {
293     uint32_t id = mPowerEntityInfos.size();
294     mPowerEntityInfos.push_back({id, name, type});
295     return id;
296 }
297 
addStateResidencyDataProvider(std::shared_ptr<IStateResidencyDataProvider> p)298 void PowerStats::addStateResidencyDataProvider(std::shared_ptr<IStateResidencyDataProvider> p) {
299     std::vector<PowerEntityStateSpace> stateSpaces = p->getStateSpaces();
300     for (auto stateSpace : stateSpaces) {
301         mPowerEntityStateSpaces.emplace(stateSpace.powerEntityId, stateSpace);
302         mStateResidencyDataProviders.emplace(stateSpace.powerEntityId, p);
303     }
304 }
305 
getPowerEntityInfo(getPowerEntityInfo_cb _hidl_cb)306 Return<void> PowerStats::getPowerEntityInfo(getPowerEntityInfo_cb _hidl_cb) {
307     // If not configured, return NOT_SUPPORTED
308     if (mPowerEntityInfos.empty()) {
309         _hidl_cb({}, Status::NOT_SUPPORTED);
310         return Void();
311     }
312 
313     _hidl_cb(mPowerEntityInfos, Status::SUCCESS);
314     return Void();
315 }
316 
getPowerEntityStateInfo(const hidl_vec<uint32_t> & powerEntityIds,getPowerEntityStateInfo_cb _hidl_cb)317 Return<void> PowerStats::getPowerEntityStateInfo(const hidl_vec<uint32_t>& powerEntityIds,
318                                                  getPowerEntityStateInfo_cb _hidl_cb) {
319     // If not configured, return NOT_SUPPORTED
320     if (mPowerEntityStateSpaces.empty()) {
321         _hidl_cb({}, Status::NOT_SUPPORTED);
322         return Void();
323     }
324 
325     std::vector<PowerEntityStateSpace> stateSpaces;
326 
327     // If powerEntityIds is empty then return state space info for all entities
328     if (powerEntityIds.size() == 0) {
329         stateSpaces.reserve(mPowerEntityStateSpaces.size());
330         for (auto i : mPowerEntityStateSpaces) {
331             stateSpaces.emplace_back(i.second);
332         }
333         _hidl_cb(stateSpaces, Status::SUCCESS);
334         return Void();
335     }
336 
337     // Return state space information only for valid ids
338     auto ret = Status::SUCCESS;
339     stateSpaces.reserve(powerEntityIds.size());
340     for (const uint32_t id : powerEntityIds) {
341         auto stateSpace = mPowerEntityStateSpaces.find(id);
342         if (stateSpace != mPowerEntityStateSpaces.end()) {
343             stateSpaces.emplace_back(stateSpace->second);
344         } else {
345             ret = Status::INVALID_INPUT;
346         }
347     }
348 
349     _hidl_cb(stateSpaces, ret);
350     return Void();
351 }
352 
getPowerEntityStateResidencyData(const hidl_vec<uint32_t> & powerEntityIds,getPowerEntityStateResidencyData_cb _hidl_cb)353 Return<void> PowerStats::getPowerEntityStateResidencyData(
354         const hidl_vec<uint32_t>& powerEntityIds, getPowerEntityStateResidencyData_cb _hidl_cb) {
355     // If not configured, return NOT_SUPPORTED
356     if (mStateResidencyDataProviders.empty() || mPowerEntityStateSpaces.empty()) {
357         _hidl_cb({}, Status::NOT_SUPPORTED);
358         return Void();
359     }
360 
361     // If powerEntityIds is empty then return data for all supported entities
362     if (powerEntityIds.size() == 0) {
363         std::vector<uint32_t> ids;
364         for (auto stateSpace : mPowerEntityStateSpaces) {
365             ids.emplace_back(stateSpace.first);
366         }
367         return getPowerEntityStateResidencyData(ids, _hidl_cb);
368     }
369 
370     std::unordered_map<uint32_t, PowerEntityStateResidencyResult> stateResidencies;
371     std::vector<PowerEntityStateResidencyResult> results;
372     results.reserve(powerEntityIds.size());
373 
374     // return results for only the given powerEntityIds
375     bool invalidInput = false;
376     bool filesystemError = false;
377     for (auto id : powerEntityIds) {
378         auto dataProvider = mStateResidencyDataProviders.find(id);
379         // skip if the given powerEntityId does not have an associated StateResidencyDataProvider
380         if (dataProvider == mStateResidencyDataProviders.end()) {
381             invalidInput = true;
382             continue;
383         }
384 
385         // get the results if we have not already done so.
386         if (stateResidencies.find(id) == stateResidencies.end()) {
387             if (!dataProvider->second->getResults(stateResidencies)) {
388                 filesystemError = true;
389             }
390         }
391 
392         // append results
393         auto stateResidency = stateResidencies.find(id);
394         if (stateResidency != stateResidencies.end()) {
395             results.emplace_back(stateResidency->second);
396         }
397     }
398 
399     auto ret = Status::SUCCESS;
400     if (filesystemError) {
401         ret = Status::FILESYSTEM_ERROR;
402     } else if (invalidInput) {
403         ret = Status::INVALID_INPUT;
404     }
405 
406     _hidl_cb(results, ret);
407     return Void();
408 }
409 
DumpResidencyDataToFd(const hidl_vec<PowerEntityInfo> & infos,const hidl_vec<PowerEntityStateSpace> & stateSpaces,const hidl_vec<PowerEntityStateResidencyResult> & results,int fd)410 bool DumpResidencyDataToFd(const hidl_vec<PowerEntityInfo>& infos,
411                            const hidl_vec<PowerEntityStateSpace>& stateSpaces,
412                            const hidl_vec<PowerEntityStateResidencyResult>& results, int fd) {
413     // construct lookup table of powerEntityId to name
414     std::unordered_map<uint32_t, std::string> entityNames;
415     for (auto info : infos) {
416         entityNames.emplace(info.powerEntityId, info.powerEntityName);
417     }
418 
419     // construct lookup table of powerEntityId, powerEntityStateId to state name
420     std::unordered_map<uint32_t, std::unordered_map<uint32_t, std::string>> stateNames;
421     for (auto stateSpace : stateSpaces) {
422         stateNames.emplace(stateSpace.powerEntityId, std::unordered_map<uint32_t, std::string>());
423         for (auto state : stateSpace.states) {
424             stateNames.at(stateSpace.powerEntityId)
425                     .emplace(state.powerEntityStateId, state.powerEntityStateName);
426         }
427     }
428 
429     std::ostringstream dumpStats;
430     dumpStats << "\n========== PowerStats HAL 1.0 state residencies ==========\n";
431 
432     const char* headerFormat = "  %14s   %14s   %16s   %15s   %16s\n";
433     const char* dataFormat =
434             "  %14s   %14s   %13" PRIu64 " ms   %15" PRIu64 "   %13" PRIu64 " ms\n";
435     dumpStats << android::base::StringPrintf(headerFormat, "Entity", "State", "Total time",
436                                              "Total entries", "Last entry timestamp");
437 
438     for (auto result : results) {
439         for (auto stateResidency : result.stateResidencyData) {
440             dumpStats << android::base::StringPrintf(
441                     dataFormat, entityNames.at(result.powerEntityId).c_str(),
442                     stateNames.at(result.powerEntityId)
443                             .at(stateResidency.powerEntityStateId)
444                             .c_str(),
445                     stateResidency.totalTimeInStateMs, stateResidency.totalStateEntryCount,
446                     stateResidency.lastEntryTimestampMs);
447         }
448     }
449 
450     dumpStats << "========== End of PowerStats HAL 1.0 state residencies ==========\n";
451 
452     return android::base::WriteStringToFd(dumpStats.str(), fd);
453 }
454 
debug(const hidl_handle & handle,const hidl_vec<hidl_string> &)455 Return<void> PowerStats::debug(const hidl_handle& handle, const hidl_vec<hidl_string>&) {
456     if (handle == nullptr || handle->numFds < 1) {
457         return Void();
458     }
459 
460     int fd = handle->data[0];
461     Status status;
462     hidl_vec<PowerEntityInfo> infos;
463 
464     // Get power entity information
465     getPowerEntityInfo([&status, &infos](auto rInfos, auto rStatus) {
466         status = rStatus;
467         infos = rInfos;
468     });
469     if (status != Status::SUCCESS) {
470         LOG(ERROR) << "Error getting power entity info";
471         return Void();
472     }
473 
474     // Get power entity state information
475     hidl_vec<PowerEntityStateSpace> stateSpaces;
476     getPowerEntityStateInfo({}, [&status, &stateSpaces](auto rStateSpaces, auto rStatus) {
477         status = rStatus;
478         stateSpaces = rStateSpaces;
479     });
480     if (status != Status::SUCCESS) {
481         LOG(ERROR) << "Error getting state info";
482         return Void();
483     }
484 
485     // Get power entity state residency data
486     hidl_vec<PowerEntityStateResidencyResult> results;
487     getPowerEntityStateResidencyData({}, [&status, &results](auto rResults, auto rStatus) {
488         status = rStatus;
489         results = rResults;
490     });
491 
492     // This implementation of getPowerEntityStateResidencyData supports the
493     // return of partial results if status == FILESYSTEM_ERROR.
494     if (status != Status::SUCCESS) {
495         LOG(ERROR) << "Error getting residency data -- Some results missing";
496     }
497 
498     if (!DumpResidencyDataToFd(infos, stateSpaces, results, fd)) {
499         PLOG(ERROR) << "Failed to dump residency data to fd";
500     }
501 
502     fsync(fd);
503 
504     return Void();
505 }
506 
507 }  // namespace implementation
508 }  // namespace V1_0
509 }  // namespace stats
510 }  // namespace power
511 }  // namespace hardware
512 }  // namespace android
513