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 (at) 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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