• 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 #include <pixelstats/StatsHelper.h>
18 #include <pixelstats/SysfsCollector.h>
19 
20 #define LOG_TAG "pixelstats-vendor"
21 
22 #include <android-base/file.h>
23 #include <android-base/parseint.h>
24 #include <android-base/properties.h>
25 #include <android-base/strings.h>
26 #include <android/binder_manager.h>
27 #include <utils/Log.h>
28 #include <utils/Timers.h>
29 
30 #include <mntent.h>
31 #include <sys/timerfd.h>
32 #include <cinttypes>
33 #include <string>
34 
35 namespace android {
36 namespace hardware {
37 namespace google {
38 namespace pixel {
39 
40 using aidl::android::frameworks::stats::VendorAtom;
41 using aidl::android::frameworks::stats::VendorAtomValue;
42 using android::base::ReadFileToString;
43 using android::base::StartsWith;
44 using android::base::WriteStringToFile;
45 using android::hardware::google::pixel::PixelAtoms::BatteryCapacity;
46 using android::hardware::google::pixel::PixelAtoms::BootStatsInfo;
47 using android::hardware::google::pixel::PixelAtoms::F2fsCompressionInfo;
48 using android::hardware::google::pixel::PixelAtoms::F2fsStatsInfo;
49 using android::hardware::google::pixel::PixelAtoms::F2fsGcSegmentInfo;
50 using android::hardware::google::pixel::PixelAtoms::StorageUfsHealth;
51 using android::hardware::google::pixel::PixelAtoms::StorageUfsResetCount;
52 using android::hardware::google::pixel::PixelAtoms::VendorChargeCycles;
53 using android::hardware::google::pixel::PixelAtoms::VendorHardwareFailed;
54 using android::hardware::google::pixel::PixelAtoms::VendorSlowIo;
55 using android::hardware::google::pixel::PixelAtoms::VendorSpeakerImpedance;
56 using android::hardware::google::pixel::PixelAtoms::VendorSpeechDspStat;
57 using android::hardware::google::pixel::PixelAtoms::ZramBdStat;
58 using android::hardware::google::pixel::PixelAtoms::ZramMmStat;
59 
SysfsCollector(const struct SysfsPaths & sysfs_paths)60 SysfsCollector::SysfsCollector(const struct SysfsPaths &sysfs_paths)
61     : kSlowioReadCntPath(sysfs_paths.SlowioReadCntPath),
62       kSlowioWriteCntPath(sysfs_paths.SlowioWriteCntPath),
63       kSlowioUnmapCntPath(sysfs_paths.SlowioUnmapCntPath),
64       kSlowioSyncCntPath(sysfs_paths.SlowioSyncCntPath),
65       kCycleCountBinsPath(sysfs_paths.CycleCountBinsPath),
66       kImpedancePath(sysfs_paths.ImpedancePath),
67       kCodecPath(sysfs_paths.CodecPath),
68       kCodec1Path(sysfs_paths.Codec1Path),
69       kSpeechDspPath(sysfs_paths.SpeechDspPath),
70       kBatteryCapacityCC(sysfs_paths.BatteryCapacityCC),
71       kBatteryCapacityVFSOC(sysfs_paths.BatteryCapacityVFSOC),
72       kUFSLifetimeA(sysfs_paths.UFSLifetimeA),
73       kUFSLifetimeB(sysfs_paths.UFSLifetimeB),
74       kUFSLifetimeC(sysfs_paths.UFSLifetimeC),
75       kUFSHostResetPath(sysfs_paths.UFSHostResetPath),
76       kF2fsStatsPath(sysfs_paths.F2fsStatsPath),
77       kZramMmStatPath("/sys/block/zram0/mm_stat"),
78       kZramBdStatPath("/sys/block/zram0/bd_stat"),
79       kEEPROMPath(sysfs_paths.EEPROMPath),
80       kPowerMitigationStatsPath(sysfs_paths.MitigationPath) {}
81 
ReadFileToInt(const std::string & path,int * val)82 bool SysfsCollector::ReadFileToInt(const std::string &path, int *val) {
83     return ReadFileToInt(path.c_str(), val);
84 }
85 
ReadFileToInt(const char * const path,int * val)86 bool SysfsCollector::ReadFileToInt(const char *const path, int *val) {
87     std::string file_contents;
88 
89     if (!ReadFileToString(path, &file_contents)) {
90         ALOGE("Unable to read %s - %s", path, strerror(errno));
91         return false;
92     } else if (StartsWith(file_contents, "0x")) {
93         if (sscanf(file_contents.c_str(), "0x%x", val) != 1) {
94             ALOGE("Unable to convert %s to hex - %s", path, strerror(errno));
95             return false;
96         }
97     } else if (sscanf(file_contents.c_str(), "%d", val) != 1) {
98         ALOGE("Unable to convert %s to int - %s", path, strerror(errno));
99         return false;
100     }
101     return true;
102 }
103 
104 /**
105  * Read the contents of kCycleCountBinsPath and report them via IStats HAL.
106  * The contents are expected to be N buckets total, the nth of which indicates the
107  * number of times battery %-full has been increased with the n/N% full bucket.
108  */
logBatteryChargeCycles(const std::shared_ptr<IStats> & stats_client)109 void SysfsCollector::logBatteryChargeCycles(const std::shared_ptr<IStats> &stats_client) {
110     std::string file_contents;
111     int val;
112     if (kCycleCountBinsPath == nullptr || strlen(kCycleCountBinsPath) == 0) {
113         ALOGV("Battery charge cycle path not specified");
114         return;
115     }
116     if (!ReadFileToString(kCycleCountBinsPath, &file_contents)) {
117         ALOGE("Unable to read battery charge cycles %s - %s", kCycleCountBinsPath, strerror(errno));
118         return;
119     }
120 
121     const int32_t kChargeCyclesBucketsCount =
122             VendorChargeCycles::kCycleBucket10FieldNumber - kVendorAtomOffset + 1;
123     std::vector<int32_t> charge_cycles;
124     std::stringstream stream(file_contents);
125     while (stream >> val) {
126         charge_cycles.push_back(val);
127     }
128     if (charge_cycles.size() > kChargeCyclesBucketsCount) {
129         ALOGW("Got excessive battery charge cycles count %" PRIu64,
130               static_cast<uint64_t>(charge_cycles.size()));
131     } else {
132         // Push 0 for buckets that do not exist.
133         for (int bucketIdx = charge_cycles.size(); bucketIdx < kChargeCyclesBucketsCount;
134              ++bucketIdx) {
135             charge_cycles.push_back(0);
136         }
137     }
138 
139     std::replace(file_contents.begin(), file_contents.end(), ' ', ',');
140     reportChargeCycles(stats_client, charge_cycles);
141 }
142 
143 /**
144  * Read the contents of kEEPROMPath and report them.
145  */
logBatteryEEPROM(const std::shared_ptr<IStats> & stats_client)146 void SysfsCollector::logBatteryEEPROM(const std::shared_ptr<IStats> &stats_client) {
147     if (kEEPROMPath == nullptr || strlen(kEEPROMPath) == 0) {
148         ALOGV("Battery EEPROM path not specified");
149         return;
150     }
151 
152     battery_EEPROM_reporter_.checkAndReport(stats_client, kEEPROMPath);
153 }
154 
155 /**
156  * Check the codec for failures over the past 24hr.
157  */
logCodecFailed(const std::shared_ptr<IStats> & stats_client)158 void SysfsCollector::logCodecFailed(const std::shared_ptr<IStats> &stats_client) {
159     std::string file_contents;
160     if (kCodecPath == nullptr || strlen(kCodecPath) == 0) {
161         ALOGV("Audio codec path not specified");
162         return;
163     }
164     if (!ReadFileToString(kCodecPath, &file_contents)) {
165         ALOGE("Unable to read codec state %s - %s", kCodecPath, strerror(errno));
166         return;
167     }
168     if (file_contents == "0") {
169         return;
170     } else {
171         VendorHardwareFailed failure;
172         failure.set_hardware_type(VendorHardwareFailed::HARDWARE_FAILED_CODEC);
173         failure.set_hardware_location(0);
174         failure.set_failure_code(VendorHardwareFailed::COMPLETE);
175         reportHardwareFailed(stats_client, failure);
176     }
177 }
178 
179 /**
180  * Check the codec1 for failures over the past 24hr.
181  */
logCodec1Failed(const std::shared_ptr<IStats> & stats_client)182 void SysfsCollector::logCodec1Failed(const std::shared_ptr<IStats> &stats_client) {
183     std::string file_contents;
184     if (kCodec1Path == nullptr || strlen(kCodec1Path) == 0) {
185         ALOGV("Audio codec1 path not specified");
186         return;
187     }
188     if (!ReadFileToString(kCodec1Path, &file_contents)) {
189         ALOGE("Unable to read codec1 state %s - %s", kCodec1Path, strerror(errno));
190         return;
191     }
192     if (file_contents == "0") {
193         return;
194     } else {
195         ALOGE("%s report hardware fail", kCodec1Path);
196         VendorHardwareFailed failure;
197         failure.set_hardware_type(VendorHardwareFailed::HARDWARE_FAILED_CODEC);
198         failure.set_hardware_location(1);
199         failure.set_failure_code(VendorHardwareFailed::COMPLETE);
200         reportHardwareFailed(stats_client, failure);
201     }
202 }
203 
reportSlowIoFromFile(const std::shared_ptr<IStats> & stats_client,const char * path,const VendorSlowIo::IoOperation & operation_s)204 void SysfsCollector::reportSlowIoFromFile(const std::shared_ptr<IStats> &stats_client,
205                                           const char *path,
206                                           const VendorSlowIo::IoOperation &operation_s) {
207     std::string file_contents;
208     if (path == nullptr || strlen(path) == 0) {
209         ALOGV("slow_io path not specified");
210         return;
211     }
212     if (!ReadFileToString(path, &file_contents)) {
213         ALOGE("Unable to read slowio %s - %s", path, strerror(errno));
214         return;
215     } else {
216         int32_t slow_io_count = 0;
217         if (sscanf(file_contents.c_str(), "%d", &slow_io_count) != 1) {
218             ALOGE("Unable to parse %s from file %s to int.", file_contents.c_str(), path);
219         } else if (slow_io_count > 0) {
220             VendorSlowIo slow_io;
221             slow_io.set_operation(operation_s);
222             slow_io.set_count(slow_io_count);
223             reportSlowIo(stats_client, slow_io);
224         }
225         // Clear the stats
226         if (!android::base::WriteStringToFile("0", path, true)) {
227             ALOGE("Unable to clear SlowIO entry %s - %s", path, strerror(errno));
228         }
229     }
230 }
231 
232 /**
233  * Check for slow IO operations.
234  */
logSlowIO(const std::shared_ptr<IStats> & stats_client)235 void SysfsCollector::logSlowIO(const std::shared_ptr<IStats> &stats_client) {
236     reportSlowIoFromFile(stats_client, kSlowioReadCntPath, VendorSlowIo::READ);
237     reportSlowIoFromFile(stats_client, kSlowioWriteCntPath, VendorSlowIo::WRITE);
238     reportSlowIoFromFile(stats_client, kSlowioUnmapCntPath, VendorSlowIo::UNMAP);
239     reportSlowIoFromFile(stats_client, kSlowioSyncCntPath, VendorSlowIo::SYNC);
240 }
241 
242 /**
243  * Report the last-detected impedance of left & right speakers.
244  */
logSpeakerImpedance(const std::shared_ptr<IStats> & stats_client)245 void SysfsCollector::logSpeakerImpedance(const std::shared_ptr<IStats> &stats_client) {
246     std::string file_contents;
247     if (kImpedancePath == nullptr || strlen(kImpedancePath) == 0) {
248         ALOGV("Audio impedance path not specified");
249         return;
250     }
251     if (!ReadFileToString(kImpedancePath, &file_contents)) {
252         ALOGE("Unable to read impedance path %s", kImpedancePath);
253         return;
254     }
255 
256     float left, right;
257     if (sscanf(file_contents.c_str(), "%g,%g", &left, &right) != 2) {
258         ALOGE("Unable to parse speaker impedance %s", file_contents.c_str());
259         return;
260     }
261     VendorSpeakerImpedance left_obj;
262     left_obj.set_speaker_location(0);
263     left_obj.set_impedance(static_cast<int32_t>(left * 1000));
264 
265     VendorSpeakerImpedance right_obj;
266     right_obj.set_speaker_location(1);
267     right_obj.set_impedance(static_cast<int32_t>(right * 1000));
268 
269     reportSpeakerImpedance(stats_client, left_obj);
270     reportSpeakerImpedance(stats_client, right_obj);
271 }
272 
273 /**
274  * Report the Speech DSP state.
275  */
logSpeechDspStat(const std::shared_ptr<IStats> & stats_client)276 void SysfsCollector::logSpeechDspStat(const std::shared_ptr<IStats> &stats_client) {
277     std::string file_contents;
278     if (kSpeechDspPath == nullptr || strlen(kSpeechDspPath) == 0) {
279         ALOGV("Speech DSP path not specified");
280         return;
281     }
282     if (!ReadFileToString(kSpeechDspPath, &file_contents)) {
283         ALOGE("Unable to read speech dsp path %s", kSpeechDspPath);
284         return;
285     }
286 
287     int32_t up_time = 0, down_time = 0, crash_count = 0, recover_count = 0;
288     if (sscanf(file_contents.c_str(), "%d,%d,%d,%d", &up_time, &down_time, &crash_count,
289                &recover_count) != 4) {
290         ALOGE("Unable to parse speech dsp stat %s", file_contents.c_str());
291         return;
292     }
293 
294     ALOGD("SpeechDSP uptime %d downtime %d crashcount %d recovercount %d", up_time, down_time,
295           crash_count, recover_count);
296     VendorSpeechDspStat dsp_stat;
297     dsp_stat.set_total_uptime_millis(up_time);
298     dsp_stat.set_total_downtime_millis(down_time);
299     dsp_stat.set_total_crash_count(crash_count);
300     dsp_stat.set_total_recover_count(recover_count);
301 
302     reportSpeechDspStat(stats_client, dsp_stat);
303 }
304 
logBatteryCapacity(const std::shared_ptr<IStats> & stats_client)305 void SysfsCollector::logBatteryCapacity(const std::shared_ptr<IStats> &stats_client) {
306     std::string file_contents;
307     if (kBatteryCapacityCC == nullptr || strlen(kBatteryCapacityCC) == 0) {
308         ALOGV("Battery Capacity CC path not specified");
309         return;
310     }
311     if (kBatteryCapacityVFSOC == nullptr || strlen(kBatteryCapacityVFSOC) == 0) {
312         ALOGV("Battery Capacity VFSOC path not specified");
313         return;
314     }
315     int delta_cc_sum, delta_vfsoc_sum;
316     if (!ReadFileToInt(kBatteryCapacityCC, &delta_cc_sum) ||
317             !ReadFileToInt(kBatteryCapacityVFSOC, &delta_vfsoc_sum))
318         return;
319 
320     // Load values array
321     std::vector<VendorAtomValue> values(2);
322     VendorAtomValue tmp;
323     tmp.set<VendorAtomValue::intValue>(delta_cc_sum);
324     values[BatteryCapacity::kDeltaCcSumFieldNumber - kVendorAtomOffset] = tmp;
325     tmp.set<VendorAtomValue::intValue>(delta_vfsoc_sum);
326     values[BatteryCapacity::kDeltaVfsocSumFieldNumber - kVendorAtomOffset] = tmp;
327 
328     // Send vendor atom to IStats HAL
329     VendorAtom event = {.reverseDomainName = PixelAtoms::ReverseDomainNames().pixel(),
330                         .atomId = PixelAtoms::Atom::kBatteryCapacity,
331                         .values = std::move(values)};
332     const ndk::ScopedAStatus ret = stats_client->reportVendorAtom(event);
333     if (!ret.isOk())
334         ALOGE("Unable to report ChargeStats to Stats service");
335 }
336 
logUFSLifetime(const std::shared_ptr<IStats> & stats_client)337 void SysfsCollector::logUFSLifetime(const std::shared_ptr<IStats> &stats_client) {
338     std::string file_contents;
339     if (kUFSLifetimeA == nullptr || strlen(kUFSLifetimeA) == 0) {
340         ALOGV("UFS lifetimeA path not specified");
341         return;
342     }
343     if (kUFSLifetimeB == nullptr || strlen(kUFSLifetimeB) == 0) {
344         ALOGV("UFS lifetimeB path not specified");
345         return;
346     }
347     if (kUFSLifetimeC == nullptr || strlen(kUFSLifetimeC) == 0) {
348         ALOGV("UFS lifetimeC path not specified");
349         return;
350     }
351 
352     int lifetimeA = 0, lifetimeB = 0, lifetimeC = 0;
353     if (!ReadFileToInt(kUFSLifetimeA, &lifetimeA) ||
354         !ReadFileToInt(kUFSLifetimeB, &lifetimeB) ||
355         !ReadFileToInt(kUFSLifetimeC, &lifetimeC)) {
356         ALOGE("Unable to read UFS lifetime : %s", strerror(errno));
357         return;
358     }
359 
360     // Load values array
361     std::vector<VendorAtomValue> values(3);
362     VendorAtomValue tmp;
363     tmp.set<VendorAtomValue::intValue>(lifetimeA);
364     values[StorageUfsHealth::kLifetimeAFieldNumber - kVendorAtomOffset] = tmp;
365     tmp.set<VendorAtomValue::intValue>(lifetimeB);
366     values[StorageUfsHealth::kLifetimeBFieldNumber - kVendorAtomOffset] = tmp;
367     tmp.set<VendorAtomValue::intValue>(lifetimeC);
368     values[StorageUfsHealth::kLifetimeCFieldNumber - kVendorAtomOffset] = tmp;
369 
370     // Send vendor atom to IStats HAL
371     VendorAtom event = {.reverseDomainName = PixelAtoms::ReverseDomainNames().pixel(),
372                         .atomId = PixelAtoms::Atom::kStorageUfsHealth,
373                         .values = std::move(values)};
374     const ndk::ScopedAStatus ret = stats_client->reportVendorAtom(event);
375     if (!ret.isOk()) {
376         ALOGE("Unable to report UfsHealthStat to Stats service");
377     }
378 }
379 
logUFSErrorStats(const std::shared_ptr<IStats> & stats_client)380 void SysfsCollector::logUFSErrorStats(const std::shared_ptr<IStats> &stats_client) {
381     int host_reset_count;
382 
383     if (kUFSHostResetPath == nullptr || strlen(kUFSHostResetPath) == 0) {
384         ALOGV("UFS host reset count specified");
385         return;
386     }
387 
388     if (!ReadFileToInt(kUFSHostResetPath, &host_reset_count)) {
389         ALOGE("Unable to read host reset count");
390         return;
391     }
392 
393     // Load values array
394     std::vector<VendorAtomValue> values(1);
395     VendorAtomValue tmp;
396     tmp.set<VendorAtomValue::intValue>(host_reset_count);
397     values[StorageUfsResetCount::kHostResetCountFieldNumber - kVendorAtomOffset] = tmp;
398 
399     // Send vendor atom to IStats HAL
400     VendorAtom event = {.reverseDomainName = PixelAtoms::ReverseDomainNames().pixel(),
401                         .atomId = PixelAtoms::Atom::kUfsResetCount,
402                         .values = std::move(values)};
403     const ndk::ScopedAStatus ret = stats_client->reportVendorAtom(event);
404     if (!ret.isOk()) {
405         ALOGE("Unable to report UFS host reset count to Stats service");
406     }
407 }
408 
getUserDataBlock()409 static std::string getUserDataBlock() {
410     std::unique_ptr<std::FILE, int (*)(std::FILE*)> fp(setmntent("/proc/mounts", "re"), endmntent);
411     if (fp == nullptr) {
412         ALOGE("Error opening /proc/mounts");
413         return "";
414     }
415 
416     mntent* mentry;
417     while ((mentry = getmntent(fp.get())) != nullptr) {
418         if (strcmp(mentry->mnt_dir, "/data") == 0) {
419             return std::string(basename(mentry->mnt_fsname));
420         }
421     }
422     return "";
423 }
424 
logF2fsStats(const std::shared_ptr<IStats> & stats_client)425 void SysfsCollector::logF2fsStats(const std::shared_ptr<IStats> &stats_client) {
426     int dirty, free, cp_calls_fg, gc_calls_fg, moved_block_fg, vblocks;
427     int cp_calls_bg, gc_calls_bg, moved_block_bg;
428 
429     if (kF2fsStatsPath == nullptr) {
430         ALOGE("F2fs stats path not specified");
431         return;
432     }
433 
434     const std::string userdataBlock = getUserDataBlock();
435     const std::string kF2fsStatsDir = kF2fsStatsPath + userdataBlock;
436 
437     if (!ReadFileToInt(kF2fsStatsDir + "/dirty_segments", &dirty)) {
438         ALOGV("Unable to read dirty segments");
439     }
440 
441     if (!ReadFileToInt(kF2fsStatsDir + "/free_segments", &free)) {
442         ALOGV("Unable to read free segments");
443     }
444 
445     if (!ReadFileToInt(kF2fsStatsDir + "/cp_foreground_calls", &cp_calls_fg)) {
446         ALOGV("Unable to read cp_foreground_calls");
447     }
448 
449     if (!ReadFileToInt(kF2fsStatsDir + "/cp_background_calls", &cp_calls_bg)) {
450         ALOGV("Unable to read cp_background_calls");
451     }
452 
453     if (!ReadFileToInt(kF2fsStatsDir + "/gc_foreground_calls", &gc_calls_fg)) {
454         ALOGV("Unable to read gc_foreground_calls");
455     }
456 
457     if (!ReadFileToInt(kF2fsStatsDir + "/gc_background_calls", &gc_calls_bg)) {
458         ALOGV("Unable to read gc_background_calls");
459     }
460 
461     if (!ReadFileToInt(kF2fsStatsDir + "/moved_blocks_foreground", &moved_block_fg)) {
462         ALOGV("Unable to read moved_blocks_foreground");
463     }
464 
465     if (!ReadFileToInt(kF2fsStatsDir + "/moved_blocks_background", &moved_block_bg)) {
466         ALOGV("Unable to read moved_blocks_background");
467     }
468 
469     if (!ReadFileToInt(kF2fsStatsDir + "/avg_vblocks", &vblocks)) {
470         ALOGV("Unable to read avg_vblocks");
471     }
472 
473     // Load values array
474     std::vector<VendorAtomValue> values(9);
475     VendorAtomValue tmp;
476     tmp.set<VendorAtomValue::intValue>(dirty);
477     values[F2fsStatsInfo::kDirtySegmentsFieldNumber - kVendorAtomOffset] = tmp;
478     tmp.set<VendorAtomValue::intValue>(free);
479     values[F2fsStatsInfo::kFreeSegmentsFieldNumber - kVendorAtomOffset] = tmp;
480     tmp.set<VendorAtomValue::intValue>(cp_calls_fg);
481     values[F2fsStatsInfo::kCpCallsFgFieldNumber - kVendorAtomOffset] = tmp;
482     tmp.set<VendorAtomValue::intValue>(cp_calls_bg);
483     values[F2fsStatsInfo::kCpCallsBgFieldNumber - kVendorAtomOffset] = tmp;
484     tmp.set<VendorAtomValue::intValue>(gc_calls_fg);
485     values[F2fsStatsInfo::kGcCallsFgFieldNumber - kVendorAtomOffset] = tmp;
486     tmp.set<VendorAtomValue::intValue>(gc_calls_bg);
487     values[F2fsStatsInfo::kGcCallsBgFieldNumber - kVendorAtomOffset] = tmp;
488     tmp.set<VendorAtomValue::intValue>(moved_block_fg);
489     values[F2fsStatsInfo::kMovedBlocksFgFieldNumber - kVendorAtomOffset] = tmp;
490     tmp.set<VendorAtomValue::intValue>(moved_block_bg);
491     values[F2fsStatsInfo::kMovedBlocksBgFieldNumber - kVendorAtomOffset] = tmp;
492     tmp.set<VendorAtomValue::intValue>(vblocks);
493     values[F2fsStatsInfo::kValidBlocksFieldNumber - kVendorAtomOffset] = tmp;
494 
495     // Send vendor atom to IStats HAL
496     VendorAtom event = {.reverseDomainName = PixelAtoms::ReverseDomainNames().pixel(),
497                         .atomId = PixelAtoms::Atom::kF2FsStats,
498                         .values = std::move(values)};
499     const ndk::ScopedAStatus ret = stats_client->reportVendorAtom(event);
500     if (!ret.isOk()) {
501         ALOGE("Unable to report F2fs stats to Stats service");
502     }
503 }
504 
logF2fsCompressionInfo(const std::shared_ptr<IStats> & stats_client)505 void SysfsCollector::logF2fsCompressionInfo(const std::shared_ptr<IStats> &stats_client) {
506     int compr_written_blocks, compr_saved_blocks, compr_new_inodes;
507 
508     if (kF2fsStatsPath == nullptr) {
509         ALOGV("F2fs stats path not specified");
510         return;
511     }
512 
513     std::string userdataBlock = getUserDataBlock();
514 
515     std::string path = kF2fsStatsPath + (userdataBlock + "/compr_written_block");
516     if (!ReadFileToInt(path, &compr_written_blocks)) {
517         ALOGE("Unable to read compression written blocks");
518         return;
519     }
520 
521     path = kF2fsStatsPath + (userdataBlock + "/compr_saved_block");
522     if (!ReadFileToInt(path, &compr_saved_blocks)) {
523         ALOGE("Unable to read compression saved blocks");
524         return;
525     } else {
526         if (!WriteStringToFile(std::to_string(0), path)) {
527             ALOGE("Failed to write to file %s", path.c_str());
528             return;
529         }
530     }
531 
532     path = kF2fsStatsPath + (userdataBlock + "/compr_new_inode");
533     if (!ReadFileToInt(path, &compr_new_inodes)) {
534         ALOGE("Unable to read compression new inodes");
535         return;
536     } else {
537         if (!WriteStringToFile(std::to_string(0), path)) {
538             ALOGE("Failed to write to file %s", path.c_str());
539             return;
540         }
541     }
542 
543     // Load values array
544     std::vector<VendorAtomValue> values(3);
545     VendorAtomValue tmp;
546     tmp.set<VendorAtomValue::intValue>(compr_written_blocks);
547     values[F2fsCompressionInfo::kComprWrittenBlocksFieldNumber - kVendorAtomOffset] = tmp;
548     tmp.set<VendorAtomValue::intValue>(compr_saved_blocks);
549     values[F2fsCompressionInfo::kComprSavedBlocksFieldNumber - kVendorAtomOffset] = tmp;
550     tmp.set<VendorAtomValue::intValue>(compr_new_inodes);
551     values[F2fsCompressionInfo::kComprNewInodesFieldNumber - kVendorAtomOffset] = tmp;
552 
553     // Send vendor atom to IStats HAL
554     VendorAtom event = {.reverseDomainName = PixelAtoms::ReverseDomainNames().pixel(),
555                         .atomId = PixelAtoms::Atom::kF2FsCompressionInfo,
556                         .values = values};
557     const ndk::ScopedAStatus ret = stats_client->reportVendorAtom(event);
558     if (!ret.isOk()) {
559         ALOGE("Unable to report F2fs compression info to Stats service");
560     }
561 }
562 
getReclaimedSegments(const std::string & mode)563 int SysfsCollector::getReclaimedSegments(const std::string &mode) {
564     std::string userDataStatsPath = kF2fsStatsPath + getUserDataBlock();
565     std::string gcSegmentModePath = userDataStatsPath + "/gc_segment_mode";
566     std::string gcReclaimedSegmentsPath = userDataStatsPath + "/gc_reclaimed_segments";
567     int reclaimed_segments;
568 
569     if (!WriteStringToFile(mode, gcSegmentModePath)) {
570         ALOGE("Failed to change gc_segment_mode to %s", mode.c_str());
571         return -1;
572     }
573 
574     if (!ReadFileToInt(gcReclaimedSegmentsPath, &reclaimed_segments)) {
575         ALOGE("GC mode(%s): Unable to read gc_reclaimed_segments", mode.c_str());
576         return -1;
577     }
578 
579     if (!WriteStringToFile(std::to_string(0), gcReclaimedSegmentsPath)) {
580         ALOGE("GC mode(%s): Failed to reset gc_reclaimed_segments", mode.c_str());
581         return -1;
582     }
583 
584     return reclaimed_segments;
585 }
586 
logF2fsGcSegmentInfo(const std::shared_ptr<IStats> & stats_client)587 void SysfsCollector::logF2fsGcSegmentInfo(const std::shared_ptr<IStats> &stats_client) {
588     int reclaimed_segments_normal, reclaimed_segments_urgent_high, reclaimed_segments_urgent_low;
589     std::string gc_normal_mode = std::to_string(0);         // GC normal mode
590     std::string gc_urgent_high_mode = std::to_string(4);    // GC urgent high mode
591     std::string gc_urgent_low_mode = std::to_string(5);     // GC urgent low mode
592 
593     if (kF2fsStatsPath == nullptr) {
594         ALOGV("F2fs stats path not specified");
595         return;
596     }
597 
598     reclaimed_segments_normal = getReclaimedSegments(gc_normal_mode);
599     if (reclaimed_segments_normal == -1) return;
600     reclaimed_segments_urgent_high = getReclaimedSegments(gc_urgent_high_mode);
601     if (reclaimed_segments_urgent_high == -1) return;
602     reclaimed_segments_urgent_low = getReclaimedSegments(gc_urgent_low_mode);
603     if (reclaimed_segments_urgent_low == -1) return;
604 
605     // Load values array
606     std::vector<VendorAtomValue> values(3);
607     VendorAtomValue tmp;
608     tmp.set<VendorAtomValue::intValue>(reclaimed_segments_normal);
609     values[F2fsGcSegmentInfo::kReclaimedSegmentsNormalFieldNumber - kVendorAtomOffset] = tmp;
610     tmp.set<VendorAtomValue::intValue>(reclaimed_segments_urgent_high);
611     values[F2fsGcSegmentInfo::kReclaimedSegmentsUrgentHighFieldNumber - kVendorAtomOffset] = tmp;
612     tmp.set<VendorAtomValue::intValue>(reclaimed_segments_urgent_low);
613     values[F2fsGcSegmentInfo::kReclaimedSegmentsUrgentLowFieldNumber - kVendorAtomOffset] = tmp;
614 
615     // Send vendor atom to IStats HAL
616     VendorAtom event = {.reverseDomainName = PixelAtoms::ReverseDomainNames().pixel(),
617                         .atomId = PixelAtoms::Atom::kF2FsGcSegmentInfo,
618                         .values = values};
619     const ndk::ScopedAStatus ret = stats_client->reportVendorAtom(event);
620     if (!ret.isOk()) {
621         ALOGE("Unable to report F2fs GC Segment info to Stats service");
622     }
623 }
624 
reportZramMmStat(const std::shared_ptr<IStats> & stats_client)625 void SysfsCollector::reportZramMmStat(const std::shared_ptr<IStats> &stats_client) {
626     std::string file_contents;
627     if (!kZramMmStatPath) {
628         ALOGV("ZramMmStat path not specified");
629         return;
630     }
631 
632     if (!ReadFileToString(kZramMmStatPath, &file_contents)) {
633         ALOGE("Unable to ZramMmStat %s - %s", kZramMmStatPath, strerror(errno));
634         return;
635     } else {
636         int64_t orig_data_size = 0;
637         int64_t compr_data_size = 0;
638         int64_t mem_used_total = 0;
639         int64_t mem_limit = 0;
640         int64_t max_used_total = 0;
641         int64_t same_pages = 0;
642         int64_t pages_compacted = 0;
643         int64_t huge_pages = 0;
644         int64_t huge_pages_since_boot = 0;
645 
646         // huge_pages_since_boot may not exist according to kernel version.
647         // only check if the number of collected data is equal or larger then 8
648         if (sscanf(file_contents.c_str(),
649                    "%" SCNd64 " %" SCNd64 " %" SCNd64 " %" SCNd64 " %" SCNd64 " %" SCNd64
650                    " %" SCNd64 " %" SCNd64 " %" SCNd64,
651                    &orig_data_size, &compr_data_size, &mem_used_total, &mem_limit, &max_used_total,
652                    &same_pages, &pages_compacted, &huge_pages, &huge_pages_since_boot) < 8) {
653             ALOGE("Unable to parse ZramMmStat %s from file %s to int.",
654                     file_contents.c_str(), kZramMmStatPath);
655         }
656 
657         // Load values array.
658         // The size should be the same as the number of fields in ZramMmStat
659         std::vector<VendorAtomValue> values(6);
660         VendorAtomValue tmp;
661         tmp.set<VendorAtomValue::intValue>(orig_data_size);
662         values[ZramMmStat::kOrigDataSizeFieldNumber - kVendorAtomOffset] = tmp;
663         tmp.set<VendorAtomValue::intValue>(compr_data_size);
664         values[ZramMmStat::kComprDataSizeFieldNumber - kVendorAtomOffset] = tmp;
665         tmp.set<VendorAtomValue::intValue>(mem_used_total);
666         values[ZramMmStat::kMemUsedTotalFieldNumber - kVendorAtomOffset] = tmp;
667         tmp.set<VendorAtomValue::intValue>(same_pages);
668         values[ZramMmStat::kSamePagesFieldNumber - kVendorAtomOffset] = tmp;
669         tmp.set<VendorAtomValue::intValue>(huge_pages);
670         values[ZramMmStat::kHugePagesFieldNumber - kVendorAtomOffset] = tmp;
671 
672         // Skip the first data to avoid a big spike in this accumulated value.
673         if (prev_huge_pages_since_boot_ == -1)
674             tmp.set<VendorAtomValue::intValue>(0);
675         else
676             tmp.set<VendorAtomValue::intValue>(huge_pages_since_boot - prev_huge_pages_since_boot_);
677 
678         values[ZramMmStat::kHugePagesSinceBootFieldNumber - kVendorAtomOffset] = tmp;
679         prev_huge_pages_since_boot_ = huge_pages_since_boot;
680 
681         // Send vendor atom to IStats HAL
682         VendorAtom event = {.reverseDomainName = PixelAtoms::ReverseDomainNames().pixel(),
683                             .atomId = PixelAtoms::Atom::kZramMmStat,
684                             .values = std::move(values)};
685         const ndk::ScopedAStatus ret = stats_client->reportVendorAtom(event);
686         if (!ret.isOk())
687             ALOGE("Zram Unable to report ZramMmStat to Stats service");
688     }
689 }
690 
reportZramBdStat(const std::shared_ptr<IStats> & stats_client)691 void SysfsCollector::reportZramBdStat(const std::shared_ptr<IStats> &stats_client) {
692     std::string file_contents;
693     if (!kZramBdStatPath) {
694         ALOGV("ZramBdStat path not specified");
695         return;
696     }
697 
698     if (!ReadFileToString(kZramBdStatPath, &file_contents)) {
699         ALOGE("Unable to ZramBdStat %s - %s", kZramBdStatPath, strerror(errno));
700         return;
701     } else {
702         int64_t bd_count = 0;
703         int64_t bd_reads = 0;
704         int64_t bd_writes = 0;
705 
706         if (sscanf(file_contents.c_str(), "%" SCNd64 " %" SCNd64 " %" SCNd64,
707                                 &bd_count, &bd_reads, &bd_writes) != 3) {
708             ALOGE("Unable to parse ZramBdStat %s from file %s to int.",
709                     file_contents.c_str(), kZramBdStatPath);
710         }
711 
712         // Load values array
713         std::vector<VendorAtomValue> values(3);
714         VendorAtomValue tmp;
715         tmp.set<VendorAtomValue::intValue>(bd_count);
716         values[ZramBdStat::kBdCountFieldNumber - kVendorAtomOffset] = tmp;
717         tmp.set<VendorAtomValue::intValue>(bd_reads);
718         values[ZramBdStat::kBdReadsFieldNumber - kVendorAtomOffset] = tmp;
719         tmp.set<VendorAtomValue::intValue>(bd_writes);
720         values[ZramBdStat::kBdWritesFieldNumber - kVendorAtomOffset] = tmp;
721 
722         // Send vendor atom to IStats HAL
723         VendorAtom event = {.reverseDomainName = PixelAtoms::ReverseDomainNames().pixel(),
724                             .atomId = PixelAtoms::Atom::kZramBdStat,
725                             .values = std::move(values)};
726         const ndk::ScopedAStatus ret = stats_client->reportVendorAtom(event);
727         if (!ret.isOk())
728             ALOGE("Zram Unable to report ZramBdStat to Stats service");
729     }
730 }
731 
logZramStats(const std::shared_ptr<IStats> & stats_client)732 void SysfsCollector::logZramStats(const std::shared_ptr<IStats> &stats_client) {
733     reportZramMmStat(stats_client);
734     reportZramBdStat(stats_client);
735 }
736 
logBootStats(const std::shared_ptr<IStats> & stats_client)737 void SysfsCollector::logBootStats(const std::shared_ptr<IStats> &stats_client) {
738     int mounted_time_sec = 0;
739 
740     if (kF2fsStatsPath == nullptr) {
741         ALOGE("F2fs stats path not specified");
742         return;
743     }
744 
745     std::string userdataBlock = getUserDataBlock();
746 
747     if (!ReadFileToInt(kF2fsStatsPath + (userdataBlock + "/mounted_time_sec"), &mounted_time_sec)) {
748         ALOGV("Unable to read mounted_time_sec");
749         return;
750     }
751 
752     int fsck_time_ms = android::base::GetIntProperty("ro.boottime.init.fsck.data", 0);
753     int checkpoint_time_ms = android::base::GetIntProperty("ro.boottime.init.mount.data", 0);
754 
755     if (fsck_time_ms == 0 && checkpoint_time_ms == 0) {
756         ALOGV("Not yet initialized");
757         return;
758     }
759 
760     // Load values array
761     std::vector<VendorAtomValue> values(3);
762     VendorAtomValue tmp;
763     tmp.set<VendorAtomValue::intValue>(mounted_time_sec);
764     values[BootStatsInfo::kMountedTimeSecFieldNumber - kVendorAtomOffset] = tmp;
765     tmp.set<VendorAtomValue::intValue>(fsck_time_ms / 1000);
766     values[BootStatsInfo::kFsckTimeSecFieldNumber - kVendorAtomOffset] = tmp;
767     tmp.set<VendorAtomValue::intValue>(checkpoint_time_ms / 1000);
768     values[BootStatsInfo::kCheckpointTimeSecFieldNumber - kVendorAtomOffset] = tmp;
769 
770     // Send vendor atom to IStats HAL
771     VendorAtom event = {.reverseDomainName = PixelAtoms::ReverseDomainNames().pixel(),
772                         .atomId = PixelAtoms::Atom::kBootStats,
773                         .values = std::move(values)};
774     const ndk::ScopedAStatus ret = stats_client->reportVendorAtom(event);
775     if (!ret.isOk()) {
776         ALOGE("Unable to report Boot stats to Stats service");
777     } else {
778         log_once_reported = true;
779     }
780 }
781 
logPerDay()782 void SysfsCollector::logPerDay() {
783     const std::shared_ptr<IStats> stats_client = getStatsService();
784     if (!stats_client) {
785         ALOGE("Unable to get AIDL Stats service");
786         return;
787     }
788     // Collect once per service init; can be multiple due to service reinit
789     if (!log_once_reported) {
790         logBootStats(stats_client);
791     }
792     logBatteryCapacity(stats_client);
793     logBatteryChargeCycles(stats_client);
794     logBatteryEEPROM(stats_client);
795     logCodec1Failed(stats_client);
796     logCodecFailed(stats_client);
797     logF2fsStats(stats_client);
798     logF2fsCompressionInfo(stats_client);
799     logF2fsGcSegmentInfo(stats_client);
800     logSlowIO(stats_client);
801     logSpeakerImpedance(stats_client);
802     logSpeechDspStat(stats_client);
803     logUFSLifetime(stats_client);
804     logUFSErrorStats(stats_client);
805     logZramStats(stats_client);
806     mm_metrics_reporter_.logCmaStatus(stats_client);
807     mm_metrics_reporter_.logPixelMmMetricsPerDay(stats_client);
808 }
809 
logPerHour()810 void SysfsCollector::logPerHour() {
811     const std::shared_ptr<IStats> stats_client = getStatsService();
812     if (!stats_client) {
813         ALOGE("Unable to get AIDL Stats service");
814         return;
815     }
816     mm_metrics_reporter_.logPixelMmMetricsPerHour(stats_client);
817     if (kPowerMitigationStatsPath != nullptr && strlen(kPowerMitigationStatsPath) > 0)
818         mitigation_stats_reporter_.logMitigationStatsPerHour(stats_client,
819                                                              kPowerMitigationStatsPath);
820 }
821 
822 /**
823  * Loop forever collecting stats from sysfs nodes and reporting them via
824  * IStats.
825  */
collect(void)826 void SysfsCollector::collect(void) {
827     int timerfd = timerfd_create(CLOCK_BOOTTIME, 0);
828     if (timerfd < 0) {
829         ALOGE("Unable to create timerfd - %s", strerror(errno));
830         return;
831     }
832 
833     // Sleep for 30 seconds on launch to allow codec driver to load.
834     sleep(30);
835 
836     // Collect first set of stats on boot.
837     logPerHour();
838     logPerDay();
839 
840     // Set an one-hour timer.
841     struct itimerspec period;
842     const int kSecondsPerHour = 60 * 60;
843     int hours = 0;
844     period.it_interval.tv_sec = kSecondsPerHour;
845     period.it_interval.tv_nsec = 0;
846     period.it_value.tv_sec = kSecondsPerHour;
847     period.it_value.tv_nsec = 0;
848 
849     if (timerfd_settime(timerfd, 0, &period, NULL)) {
850         ALOGE("Unable to set one hour timer - %s", strerror(errno));
851         return;
852     }
853 
854     while (1) {
855         int readval;
856         do {
857             char buf[8];
858             errno = 0;
859             readval = read(timerfd, buf, sizeof(buf));
860         } while (readval < 0 && errno == EINTR);
861         if (readval < 0) {
862             ALOGE("Timerfd error - %s\n", strerror(errno));
863             return;
864         }
865 
866         hours++;
867         logPerHour();
868         if (hours == 24) {
869             // Collect stats every 24hrs after.
870             logPerDay();
871             hours = 0;
872         }
873     }
874 }
875 
876 }  // namespace pixel
877 }  // namespace google
878 }  // namespace hardware
879 }  // namespace android
880