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 "libpixelpowerstats"
18 
19 #include <memory>
20 #include <string>
21 #include <unordered_map>
22 #include <utility>
23 #include <vector>
24 
25 #include <pixelpowerstats/PowerStats.h>
26 
27 #include <android-base/file.h>
28 #include <android-base/logging.h>
29 #include <android-base/stringprintf.h>
30 
31 #include <inttypes.h>
32 #include <time.h>
33 
34 namespace android {
35 namespace hardware {
36 namespace power {
37 namespace stats {
38 namespace V1_0 {
39 namespace implementation {
40 
setRailDataProvider(std::unique_ptr<IRailDataProvider> dataProvider)41 void PowerStats::setRailDataProvider(std::unique_ptr<IRailDataProvider> dataProvider) {
42     mRailDataProvider = std::move(dataProvider);
43 }
44 
getRailInfo(getRailInfo_cb _hidl_cb)45 Return<void> PowerStats::getRailInfo(getRailInfo_cb _hidl_cb) {
46     if (!mRailDataProvider) {
47         _hidl_cb({}, Status::NOT_SUPPORTED);
48         return Void();
49     }
50 
51     return mRailDataProvider->getRailInfo(_hidl_cb);
52 }
53 
getEnergyData(const hidl_vec<uint32_t> & railIndices,getEnergyData_cb _hidl_cb)54 Return<void> PowerStats::getEnergyData(const hidl_vec<uint32_t> &railIndices,
55                                        getEnergyData_cb _hidl_cb) {
56     if (!mRailDataProvider) {
57         _hidl_cb({}, Status::NOT_SUPPORTED);
58         return Void();
59     }
60 
61     return mRailDataProvider->getEnergyData(railIndices, _hidl_cb);
62 }
63 
streamEnergyData(uint32_t timeMs,uint32_t samplingRate,streamEnergyData_cb _hidl_cb)64 Return<void> PowerStats::streamEnergyData(uint32_t timeMs, uint32_t samplingRate,
65                                           streamEnergyData_cb _hidl_cb) {
66     if (!mRailDataProvider) {
67         _hidl_cb({}, 0, 0, Status::NOT_SUPPORTED);
68         return Void();
69     }
70 
71     return mRailDataProvider->streamEnergyData(timeMs, samplingRate, _hidl_cb);
72 }
73 
addPowerEntity(const std::string & name,PowerEntityType type)74 uint32_t PowerStats::addPowerEntity(const std::string &name, PowerEntityType type) {
75     uint32_t id = mPowerEntityInfos.size();
76     mPowerEntityInfos.push_back({id, name, type});
77     return id;
78 }
79 
addStateResidencyDataProvider(sp<IStateResidencyDataProvider> p)80 void PowerStats::addStateResidencyDataProvider(sp<IStateResidencyDataProvider> p) {
81     std::vector<PowerEntityStateSpace> stateSpaces = p->getStateSpaces();
82     for (auto stateSpace : stateSpaces) {
83         mPowerEntityStateSpaces.emplace(stateSpace.powerEntityId, stateSpace);
84         mStateResidencyDataProviders.emplace(stateSpace.powerEntityId, p);
85     }
86 }
87 
getPowerEntityInfo(getPowerEntityInfo_cb _hidl_cb)88 Return<void> PowerStats::getPowerEntityInfo(getPowerEntityInfo_cb _hidl_cb) {
89     // If not configured, return NOT_SUPPORTED
90     if (mPowerEntityInfos.empty()) {
91         _hidl_cb({}, Status::NOT_SUPPORTED);
92         return Void();
93     }
94 
95     _hidl_cb(mPowerEntityInfos, Status::SUCCESS);
96     return Void();
97 }
98 
getPowerEntityStateInfo(const hidl_vec<uint32_t> & powerEntityIds,getPowerEntityStateInfo_cb _hidl_cb)99 Return<void> PowerStats::getPowerEntityStateInfo(const hidl_vec<uint32_t> &powerEntityIds,
100                                                  getPowerEntityStateInfo_cb _hidl_cb) {
101     // If not configured, return NOT_SUPPORTED
102     if (mPowerEntityStateSpaces.empty()) {
103         _hidl_cb({}, Status::NOT_SUPPORTED);
104         return Void();
105     }
106 
107     std::vector<PowerEntityStateSpace> stateSpaces;
108 
109     // If powerEntityIds is empty then return state space info for all entities
110     if (powerEntityIds.size() == 0) {
111         stateSpaces.reserve(mPowerEntityStateSpaces.size());
112         for (auto i : mPowerEntityStateSpaces) {
113             stateSpaces.emplace_back(i.second);
114         }
115         _hidl_cb(stateSpaces, Status::SUCCESS);
116         return Void();
117     }
118 
119     // Return state space information only for valid ids
120     auto ret = Status::SUCCESS;
121     stateSpaces.reserve(powerEntityIds.size());
122     for (const uint32_t id : powerEntityIds) {
123         auto stateSpace = mPowerEntityStateSpaces.find(id);
124         if (stateSpace != mPowerEntityStateSpaces.end()) {
125             stateSpaces.emplace_back(stateSpace->second);
126         } else {
127             ret = Status::INVALID_INPUT;
128         }
129     }
130 
131     _hidl_cb(stateSpaces, ret);
132     return Void();
133 }
134 
getPowerEntityStateResidencyData(const hidl_vec<uint32_t> & powerEntityIds,getPowerEntityStateResidencyData_cb _hidl_cb)135 Return<void> PowerStats::getPowerEntityStateResidencyData(
136     const hidl_vec<uint32_t> &powerEntityIds, getPowerEntityStateResidencyData_cb _hidl_cb) {
137     // If not configured, return NOT_SUPPORTED
138     if (mStateResidencyDataProviders.empty() || mPowerEntityStateSpaces.empty()) {
139         _hidl_cb({}, Status::NOT_SUPPORTED);
140         return Void();
141     }
142 
143     // If powerEntityIds is empty then return data for all supported entities
144     if (powerEntityIds.size() == 0) {
145         std::vector<uint32_t> ids;
146         for (auto stateSpace : mPowerEntityStateSpaces) {
147             ids.emplace_back(stateSpace.first);
148         }
149         return getPowerEntityStateResidencyData(ids, _hidl_cb);
150     }
151 
152     std::unordered_map<uint32_t, PowerEntityStateResidencyResult> stateResidencies;
153     std::vector<PowerEntityStateResidencyResult> results;
154     results.reserve(powerEntityIds.size());
155 
156     // return results for only the given powerEntityIds
157     bool invalidInput = false;
158     bool filesystemError = false;
159     for (auto id : powerEntityIds) {
160         auto dataProvider = mStateResidencyDataProviders.find(id);
161         // skip if the given powerEntityId does not have an associated StateResidencyDataProvider
162         if (dataProvider == mStateResidencyDataProviders.end()) {
163             invalidInput = true;
164             continue;
165         }
166 
167         // get the results if we have not already done so.
168         if (stateResidencies.find(id) == stateResidencies.end()) {
169             if (!dataProvider->second->getResults(stateResidencies)) {
170                 filesystemError = true;
171             }
172         }
173 
174         // append results
175         auto stateResidency = stateResidencies.find(id);
176         if (stateResidency != stateResidencies.end()) {
177             results.emplace_back(stateResidency->second);
178         }
179     }
180 
181     auto ret = Status::SUCCESS;
182     if (filesystemError) {
183         ret = Status::FILESYSTEM_ERROR;
184     } else if (invalidInput) {
185         ret = Status::INVALID_INPUT;
186     }
187 
188     _hidl_cb(results, ret);
189     return Void();
190 }
191 
192 //
193 // Debugging utilities to support printing data via debug()
194 //
195 
getTimeElapsedMs(const struct timespec & now,const struct timespec & then)196 static uint64_t getTimeElapsedMs(const struct timespec &now, const struct timespec &then) {
197     uint64_t thenMs = then.tv_sec * 1000 + (then.tv_nsec / 1000000);
198     uint64_t nowMs = now.tv_sec * 1000 + (now.tv_nsec / 1000000);
199     return (nowMs - thenMs);
200 }
201 
202 static const char RESIDENCY_HEADER[] =
203         "\n============= PowerStats HAL 1.0 state residencies ==============\n";
204 static const char RESIDENCY_FOOTER[] =
205         "========== End of PowerStats HAL 1.0 state residencies ==========\n";
206 
DumpResidencyDataToFd(const std::unordered_map<uint32_t,std::string> & entityNames,const std::unordered_map<uint32_t,std::unordered_map<uint32_t,std::string>> stateNames,const hidl_vec<PowerEntityStateResidencyResult> & results,int fd)207 static bool DumpResidencyDataToFd(
208         const std::unordered_map<uint32_t, std::string> &entityNames,
209         const std::unordered_map<uint32_t, std::unordered_map<uint32_t, std::string>> stateNames,
210         const hidl_vec<PowerEntityStateResidencyResult> &results, int fd) {
211     std::ostringstream dumpStats;
212     const char *headerFormat = "  %14s   %14s   %16s   %15s   %17s\n";
213     const char *dataFormat =
214             "  %14s   %14s   %13" PRIu64 " ms   %15" PRIu64 "   %14" PRIu64 " ms\n";
215 
216     dumpStats << RESIDENCY_HEADER;
217     dumpStats << android::base::StringPrintf(headerFormat, "Entity", "State", "Total time",
218                                              "Total entries", "Last entry tstamp");
219 
220     for (auto result : results) {
221         for (auto stateResidency : result.stateResidencyData) {
222             dumpStats << android::base::StringPrintf(
223                     dataFormat, entityNames.at(result.powerEntityId).c_str(),
224                     stateNames.at(result.powerEntityId)
225                               .at(stateResidency.powerEntityStateId).c_str(),
226                     stateResidency.totalTimeInStateMs, stateResidency.totalStateEntryCount,
227                     stateResidency.lastEntryTimestampMs);
228         }
229     }
230 
231     dumpStats << RESIDENCY_FOOTER;
232 
233     return android::base::WriteStringToFd(dumpStats.str(), fd);
234 }
235 
DumpResidencyDataDiffToFd(const std::unordered_map<uint32_t,std::string> & entityNames,const std::unordered_map<uint32_t,std::unordered_map<uint32_t,std::string>> stateNames,uint64_t elapsedTimeMs,const hidl_vec<PowerEntityStateResidencyResult> & prevResults,const hidl_vec<PowerEntityStateResidencyResult> & results,int fd)236 static bool DumpResidencyDataDiffToFd(
237         const std::unordered_map<uint32_t, std::string> &entityNames,
238         const std::unordered_map<uint32_t, std::unordered_map<uint32_t, std::string>> stateNames,
239         uint64_t elapsedTimeMs, const hidl_vec<PowerEntityStateResidencyResult> &prevResults,
240         const hidl_vec<PowerEntityStateResidencyResult> &results, int fd) {
241     std::ostringstream dumpStats;
242     const char *headerFormat = "  %14s   %14s   %16s (%14s)   %15s (%16s)   %17s (%14s)\n";
243     const char *dataFormatWithDelta =
244             "  %14s   %14s   %13" PRIu64 " ms (%14" PRId64 ")   %15" PRIu64 " (%16" PRId64 ")"
245             "   %14" PRIu64 " ms (%14" PRId64 ")\n";
246     const char *dataFormatWithoutDelta =
247             "  %14s   %14s   %13" PRIu64 " ms (          none)   %15" PRIu64 " (            none)"
248             "   %14" PRIu64 " ms (          none)\n";
249 
250     dumpStats << RESIDENCY_HEADER;
251     dumpStats << "Elapsed time: "
252               << (elapsedTimeMs == 0 ? "unknown" : std::to_string(elapsedTimeMs)) << " ms\n";
253 
254     dumpStats << android::base::StringPrintf(headerFormat, "Entity", "State", "Total time",
255                                              "Delta   ", "Total entries", "Delta   ",
256                                              "Last entry tstamp", "Delta ");
257 
258     // Process prevResults into a 2-tier lookup table for easy reference
259     std::unordered_map<uint32_t, std::unordered_map<uint32_t, PowerEntityStateResidencyData>>
260             prevResultsMap;
261     for (auto prevResult : prevResults) {
262         prevResultsMap.emplace(prevResult.powerEntityId,
263                                std::unordered_map<uint32_t, PowerEntityStateResidencyData>());
264         for (auto stateResidency : prevResult.stateResidencyData) {
265             prevResultsMap.at(prevResult.powerEntityId)
266                     .emplace(stateResidency.powerEntityStateId, stateResidency);
267         }
268     }
269 
270     // Iterate over the new result data (one "result" per entity)
271     for (auto result : results) {
272         uint32_t entityId = result.powerEntityId;
273         const char *entityName = entityNames.at(entityId).c_str();
274 
275         // Look up previous result data for the same entity
276         auto prevEntityResultIt = prevResultsMap.find(entityId);
277 
278         // Iterate over individual states within the current entity's new result
279         for (auto stateResidency : result.stateResidencyData) {
280             uint32_t stateId = stateResidency.powerEntityStateId;
281             const char *stateName = stateNames.at(entityId).at(stateId).c_str();
282 
283             // If a previous result was found for the same entity, see if that
284             // result also contains data for the current state
285             bool prevValueFound = false;
286             if (prevEntityResultIt != prevResultsMap.end()) {
287                 auto prevStateResidencyIt = prevEntityResultIt->second.find(stateId);
288                 // If a previous result was found for the current entity and state, calculate the
289                 // deltas and display them along with new result
290                 if (prevStateResidencyIt != prevEntityResultIt->second.end()) {
291                     int64_t deltaTotalTime = stateResidency.totalTimeInStateMs -
292                                              prevStateResidencyIt->second.totalTimeInStateMs;
293                     int64_t deltaTotalCount = stateResidency.totalStateEntryCount -
294                                               prevStateResidencyIt->second.totalStateEntryCount;
295                     int64_t deltaTimestamp = stateResidency.lastEntryTimestampMs -
296                                              prevStateResidencyIt->second.lastEntryTimestampMs;
297 
298                     dumpStats << android::base::StringPrintf(
299                             dataFormatWithDelta, entityName, stateName,
300                             stateResidency.totalTimeInStateMs, deltaTotalTime,
301                             stateResidency.totalStateEntryCount, deltaTotalCount,
302                             stateResidency.lastEntryTimestampMs, deltaTimestamp);
303                     prevValueFound = true;
304                 }
305             }
306 
307             // If no previous result was found for the current entity and state, display the new
308             // result without deltas
309             if (!prevValueFound) {
310                 dumpStats << android::base::StringPrintf(
311                         dataFormatWithoutDelta, entityName, stateName,
312                         stateResidency.totalTimeInStateMs, stateResidency.totalStateEntryCount,
313                         stateResidency.lastEntryTimestampMs);
314             }
315         }
316     }
317 
318     dumpStats << RESIDENCY_FOOTER;
319 
320     return android::base::WriteStringToFd(dumpStats.str(), fd);
321 }
322 
debugStateResidency(const std::unordered_map<uint32_t,std::string> & entityNames,int fd,bool delta)323 void PowerStats::debugStateResidency(const std::unordered_map<uint32_t, std::string> &entityNames,
324                                      int fd, bool delta) {
325     static struct timespec prevDataTime;
326     static bool prevDataTimeValid = false;
327     struct timespec dataTime;
328     bool dataTimeValid;
329 
330     // Get power entity state space information
331     Status status;
332     hidl_vec<PowerEntityStateSpace> stateSpaces;
333     getPowerEntityStateInfo({}, [&status, &stateSpaces](auto rStateSpaces, auto rStatus) {
334         status = rStatus;
335         stateSpaces = rStateSpaces;
336     });
337     if (status != Status::SUCCESS) {
338         LOG(ERROR) << "Error getting state info";
339         return;
340     }
341 
342     // Construct lookup table of powerEntityId, powerEntityStateId to state name
343     std::unordered_map<uint32_t, std::unordered_map<uint32_t, std::string>> stateNames;
344     for (auto stateSpace : stateSpaces) {
345         stateNames.emplace(stateSpace.powerEntityId, std::unordered_map<uint32_t, std::string>());
346         auto &entityStateNames = stateNames.at(stateSpace.powerEntityId);
347         for (auto state : stateSpace.states) {
348             entityStateNames.emplace(state.powerEntityStateId, state.powerEntityStateName);
349         }
350     }
351 
352     // Get power entity state residency data
353     hidl_vec<PowerEntityStateResidencyResult> results;
354     getPowerEntityStateResidencyData(
355             {}, [&status, &results, &dataTime, &dataTimeValid](auto rResults, auto rStatus) {
356                 status = rStatus;
357                 results = rResults;
358                 dataTimeValid = (clock_gettime(CLOCK_BOOTTIME, &dataTime) == 0);
359             });
360 
361     // This implementation of getPowerEntityStateResidencyData supports the
362     // return of partial results if status == FILESYSTEM_ERROR.
363     if (status != Status::SUCCESS) {
364         LOG(ERROR) << "Error getting residency data -- Some results missing";
365     }
366 
367     if (!delta) {
368         // If no delta argument was supplied, just dump the latest data
369         if (!DumpResidencyDataToFd(entityNames, stateNames, results, fd)) {
370             PLOG(ERROR) << "Failed to dump residency data to fd";
371         }
372     } else {
373         // If the delta argument was supplied, calculate the elapsed time since the previous
374         // result and then dump the latest data along with elapsed time and deltas
375         static hidl_vec<PowerEntityStateResidencyResult> prevResults;
376         uint64_t elapsedTimeMs = 0;
377         if (dataTimeValid && prevDataTimeValid) {
378             elapsedTimeMs = getTimeElapsedMs(dataTime, prevDataTime);
379         }
380 
381         if (!DumpResidencyDataDiffToFd(entityNames, stateNames, elapsedTimeMs, prevResults,
382                                        results, fd)) {
383             PLOG(ERROR) << "Failed to dump residency data delta to fd";
384         }
385 
386         prevResults = results;
387         prevDataTime = dataTime;
388         prevDataTimeValid = dataTimeValid;
389     }
390 }
391 
392 static const char ENERGYDATA_HEADER[] =
393         "\n============= PowerStats HAL 1.0 rail energy data ==============\n";
394 static const char ENERGYDATA_FOOTER[] =
395         "========== End of PowerStats HAL 1.0 rail energy data ==========\n";
396 
DumpEnergyDataToFd(const std::unordered_map<uint32_t,std::pair<std::string,std::string>> & railNames,const hidl_vec<EnergyData> & energyData,int fd)397 static bool DumpEnergyDataToFd(
398         const std::unordered_map<uint32_t, std::pair<std::string, std::string>> &railNames,
399         const hidl_vec<EnergyData> &energyData, int fd) {
400     std::ostringstream dumpStats;
401     const char *headerFormat = "  %14s   %18s   %18s\n";
402     const char *dataFormat = "  %14s   %18s   %14.2f mWs\n";
403 
404     dumpStats << ENERGYDATA_HEADER;
405     dumpStats << android::base::StringPrintf(headerFormat, "Subsys", "Rail", "Cumulative Energy");
406 
407     for (auto data : energyData) {
408         dumpStats << android::base::StringPrintf(dataFormat, railNames.at(data.index).first.c_str(),
409                                                  railNames.at(data.index).second.c_str(),
410                                                  static_cast<float>(data.energy) / 1000.0);
411     }
412 
413     dumpStats << ENERGYDATA_FOOTER;
414 
415     return android::base::WriteStringToFd(dumpStats.str(), fd);
416 }
417 
DumpEnergyDataDiffToFd(const std::unordered_map<uint32_t,std::pair<std::string,std::string>> & railNames,uint64_t elapsedTimeMs,const hidl_vec<EnergyData> & prevEnergyData,const hidl_vec<EnergyData> & energyData,int fd)418 static bool DumpEnergyDataDiffToFd(
419         const std::unordered_map<uint32_t, std::pair<std::string, std::string>> &railNames,
420         uint64_t elapsedTimeMs, const hidl_vec<EnergyData> &prevEnergyData,
421         const hidl_vec<EnergyData> &energyData, int fd) {
422     std::ostringstream dumpStats;
423     const char *headerFormat = "  %14s   %18s   %18s (%14s)\n";
424     const char *dataFormatWithDelta = "  %14s   %18s   %14.2f mWs (%14.2f)\n";
425     const char *dataFormatWithoutDelta = "  %14s   %18s   %14.2f mWs (          none)\n";
426 
427     dumpStats << ENERGYDATA_HEADER;
428     dumpStats << "Elapsed time: "
429               << (elapsedTimeMs == 0 ? "unknown" : std::to_string(elapsedTimeMs)) << " ms\n";
430 
431     dumpStats << android::base::StringPrintf(headerFormat, "Subsys", "Rail", "Cumulative Energy",
432                                              "Delta   ");
433 
434     std::unordered_map<uint32_t, uint64_t> prevEnergyDataMap;
435     for (auto data : prevEnergyData) {
436         prevEnergyDataMap.emplace(data.index, data.energy);
437     }
438 
439     for (auto data : energyData) {
440         const char *subsysName = railNames.at(data.index).first.c_str();
441         const char *railName = railNames.at(data.index).second.c_str();
442 
443         auto prevEnergyDataIt = prevEnergyDataMap.find(data.index);
444 
445         if (prevEnergyDataIt != prevEnergyDataMap.end()) {
446             int64_t deltaEnergy = data.energy - prevEnergyDataIt->second;
447 
448             dumpStats << android::base::StringPrintf(dataFormatWithDelta, subsysName, railName,
449                                                      static_cast<float>(data.energy) / 1000.0,
450                                                      static_cast<float>(deltaEnergy) / 1000.0);
451         } else {
452             dumpStats << android::base::StringPrintf(dataFormatWithoutDelta, subsysName, railName,
453                                                      static_cast<float>(data.energy) / 1000.0);
454         }
455     }
456 
457     dumpStats << ENERGYDATA_FOOTER;
458 
459     return android::base::WriteStringToFd(dumpStats.str(), fd);
460 }
461 
debugEnergyData(int fd,bool delta)462 void PowerStats::debugEnergyData(int fd, bool delta) {
463     static struct timespec prevDataTime;
464     static bool prevDataTimeValid = false;
465     struct timespec dataTime;
466     bool dataTimeValid = false;
467 
468     std::unordered_map<uint32_t, std::pair<std::string, std::string>> railNames;
469     getRailInfo([&railNames](auto infos, auto /* status */) {
470         // Don't care about the status. infos will be nonempty if rail energy is supported.
471         for (auto info : infos) {
472             railNames.emplace(info.index, std::make_pair(info.subsysName, info.railName));
473         }
474     });
475     if (railNames.empty()) {
476         return;
477     }
478 
479     Status status;
480     hidl_vec<EnergyData> energyData;
481     getEnergyData(
482             {}, [&status, &energyData, &dataTime, &dataTimeValid](auto rEnergyData, auto rStatus) {
483                 status = rStatus;
484                 energyData = rEnergyData;
485                 dataTimeValid = (clock_gettime(CLOCK_BOOTTIME, &dataTime) == 0);
486             });
487 
488     // getEnergyData returns no results if status != SUCCESS.
489     if (status != Status::SUCCESS) {
490         LOG(ERROR) << "Error getting rail data";
491         return;
492     }
493 
494     if (!delta) {
495         if (!DumpEnergyDataToFd(railNames, energyData, fd)) {
496             PLOG(ERROR) << "Failed to dump energy data to fd";
497         }
498     } else {
499         // If the delta argument was supplied, calculate the elapsed time since the previous
500         // result and then dump the latest data along with elapsed time and deltas
501         static hidl_vec<EnergyData> prevEnergyData;
502         uint64_t elapsedTimeMs = 0;
503         if (dataTimeValid && prevDataTimeValid) {
504             elapsedTimeMs = getTimeElapsedMs(dataTime, prevDataTime);
505         }
506 
507         if (!DumpEnergyDataDiffToFd(railNames, elapsedTimeMs, prevEnergyData, energyData, fd)) {
508             PLOG(ERROR) << "Failed to dump energy data delta to fd";
509         }
510 
511         prevEnergyData = energyData;
512         prevDataTime = dataTime;
513         prevDataTimeValid = dataTimeValid;
514     }
515 }
516 
debug(const hidl_handle & handle,const hidl_vec<hidl_string> & args)517 Return<void> PowerStats::debug(const hidl_handle &handle, const hidl_vec<hidl_string> &args) {
518     if (handle == nullptr || handle->numFds < 1) {
519         return Void();
520     }
521 
522     int fd = handle->data[0];
523     bool delta = (args.size() == 1) && (args[0] == "delta");
524 
525     // Get power entity information, which is common across all supported data categories
526     Status status;
527     hidl_vec<PowerEntityInfo> stateInfos;
528     getPowerEntityInfo([&status, &stateInfos](auto rInfos, auto rStatus) {
529         status = rStatus;
530         stateInfos = rInfos;
531     });
532     if (status != Status::SUCCESS) {
533         LOG(ERROR) << "Error getting power entity info";
534         return Void();
535     }
536 
537     // Construct lookup table of powerEntityId to name
538     std::unordered_map<uint32_t, std::string> entityNames;
539     for (auto info : stateInfos) {
540         entityNames.emplace(info.powerEntityId, info.powerEntityName);
541     }
542 
543     // Generate debug output for supported data categories
544     debugStateResidency(entityNames, fd, delta);
545 
546     // Generate debug output for energy data
547     debugEnergyData(fd, delta);
548 
549     fsync(fd);
550     return Void();
551 }
552 
553 }  // namespace implementation
554 }  // namespace V1_0
555 }  // namespace stats
556 }  // namespace power
557 }  // namespace hardware
558 }  // namespace android
559