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