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/SysfsCollector.h>
18
19 #define LOG_TAG "pixelstats-vendor"
20
21 #include <android-base/file.h>
22 #include <android-base/parseint.h>
23 #include <android-base/properties.h>
24 #include <android-base/strings.h>
25 #include <android/frameworks/stats/1.0/IStats.h>
26 #include <hardware/google/pixel/pixelstats/pixelatoms.pb.h>
27 #include <utils/Log.h>
28 #include <utils/StrongPointer.h>
29 #include <utils/Timers.h>
30
31 #include <sys/timerfd.h>
32 #include <mntent.h>
33 #include <string>
34
35 namespace android {
36 namespace hardware {
37 namespace google {
38 namespace pixel {
39
40 using android::sp;
41 using android::base::ReadFileToString;
42 using android::base::StartsWith;
43 using android::frameworks::stats::V1_0::ChargeCycles;
44 using android::frameworks::stats::V1_0::HardwareFailed;
45 using android::frameworks::stats::V1_0::IStats;
46 using android::frameworks::stats::V1_0::SlowIo;
47 using android::frameworks::stats::V1_0::SpeakerImpedance;
48 using android::frameworks::stats::V1_0::SpeechDspStat;
49 using android::frameworks::stats::V1_0::VendorAtom;
50 using android::hardware::google::pixel::PixelAtoms::BatteryCapacity;
51 using android::hardware::google::pixel::PixelAtoms::StorageUfsHealth;
52 using android::hardware::google::pixel::PixelAtoms::F2fsStatsInfo;
53 using android::hardware::google::pixel::PixelAtoms::ZramMmStat;
54 using android::hardware::google::pixel::PixelAtoms::ZramBdStat;
55 using android::hardware::google::pixel::PixelAtoms::BootStatsInfo;
56
SysfsCollector(const struct SysfsPaths & sysfs_paths)57 SysfsCollector::SysfsCollector(const struct SysfsPaths &sysfs_paths)
58 : kSlowioReadCntPath(sysfs_paths.SlowioReadCntPath),
59 kSlowioWriteCntPath(sysfs_paths.SlowioWriteCntPath),
60 kSlowioUnmapCntPath(sysfs_paths.SlowioUnmapCntPath),
61 kSlowioSyncCntPath(sysfs_paths.SlowioSyncCntPath),
62 kCycleCountBinsPath(sysfs_paths.CycleCountBinsPath),
63 kImpedancePath(sysfs_paths.ImpedancePath),
64 kCodecPath(sysfs_paths.CodecPath),
65 kCodec1Path(sysfs_paths.Codec1Path),
66 kSpeechDspPath(sysfs_paths.SpeechDspPath),
67 kBatteryCapacityCC(sysfs_paths.BatteryCapacityCC),
68 kBatteryCapacityVFSOC(sysfs_paths.BatteryCapacityVFSOC),
69 kUFSLifetimeA(sysfs_paths.UFSLifetimeA),
70 kUFSLifetimeB(sysfs_paths.UFSLifetimeB),
71 kUFSLifetimeC(sysfs_paths.UFSLifetimeC),
72 kF2fsStatsPath(sysfs_paths.F2fsStatsPath),
73 kUserdataBlockProp(sysfs_paths.UserdataBlockProp),
74 kZramMmStatPath("/sys/block/zram0/mm_stat"),
75 kZramBdStatPath("/sys/block/zram0/bd_stat"),
76 kEEPROMPath(sysfs_paths.EEPROMPath) {}
77
ReadFileToInt(const std::string & path,int * val)78 bool SysfsCollector::ReadFileToInt(const std::string &path, int *val) {
79 return ReadFileToInt(path.c_str(), val);
80 }
81
ReadFileToInt(const char * const path,int * val)82 bool SysfsCollector::ReadFileToInt(const char *const path, int *val) {
83 std::string file_contents;
84
85 if (!ReadFileToString(path, &file_contents)) {
86 ALOGE("Unable to read %s - %s", path, strerror(errno));
87 return false;
88 } else if (StartsWith(file_contents, "0x")) {
89 if (sscanf(file_contents.c_str(), "0x%x", val) != 1) {
90 ALOGE("Unable to convert %s to hex - %s", path, strerror(errno));
91 return false;
92 }
93 } else if (sscanf(file_contents.c_str(), "%d", val) != 1) {
94 ALOGE("Unable to convert %s to int - %s", path, strerror(errno));
95 return false;
96 }
97 return true;
98 }
99
100 /**
101 * Read the contents of kCycleCountBinsPath and report them via IStats HAL.
102 * The contents are expected to be N buckets total, the nth of which indicates the
103 * number of times battery %-full has been increased with the n/N% full bucket.
104 */
logBatteryChargeCycles()105 void SysfsCollector::logBatteryChargeCycles() {
106 std::string file_contents;
107 int val;
108 std::vector<int> charge_cycles;
109 if (kCycleCountBinsPath == nullptr || strlen(kCycleCountBinsPath) == 0) {
110 ALOGV("Battery charge cycle path not specified");
111 return;
112 }
113 if (!ReadFileToString(kCycleCountBinsPath, &file_contents)) {
114 ALOGE("Unable to read battery charge cycles %s - %s", kCycleCountBinsPath, strerror(errno));
115 return;
116 }
117
118 std::stringstream stream(file_contents);
119 while (stream >> val) {
120 charge_cycles.push_back(val);
121 }
122 ChargeCycles cycles;
123 cycles.cycleBucket = charge_cycles;
124
125 std::replace(file_contents.begin(), file_contents.end(), ' ', ',');
126 stats_->reportChargeCycles(cycles);
127 }
128
129 /**
130 * Read the contents of kEEPROMPath and report them.
131 */
logBatteryEEPROM()132 void SysfsCollector::logBatteryEEPROM() {
133 if (kEEPROMPath == nullptr || strlen(kEEPROMPath) == 0) {
134 ALOGV("Battery EEPROM path not specified");
135 return;
136 }
137
138 battery_EEPROM_reporter_.checkAndReport(kEEPROMPath);
139 }
140
141 /**
142 * Check the codec for failures over the past 24hr.
143 */
logCodecFailed()144 void SysfsCollector::logCodecFailed() {
145 std::string file_contents;
146 if (kCodecPath == nullptr || strlen(kCodecPath) == 0) {
147 ALOGV("Audio codec path not specified");
148 return;
149 }
150 if (!ReadFileToString(kCodecPath, &file_contents)) {
151 ALOGE("Unable to read codec state %s - %s", kCodecPath, strerror(errno));
152 return;
153 }
154 if (file_contents == "0") {
155 return;
156 } else {
157 HardwareFailed failed = {.hardwareType = HardwareFailed::HardwareType::CODEC,
158 .hardwareLocation = 0,
159 .errorCode = HardwareFailed::HardwareErrorCode::COMPLETE};
160 stats_->reportHardwareFailed(failed);
161 }
162 }
163
164 /**
165 * Check the codec1 for failures over the past 24hr.
166 */
logCodec1Failed()167 void SysfsCollector::logCodec1Failed() {
168 std::string file_contents;
169 if (kCodec1Path == nullptr || strlen(kCodec1Path) == 0) {
170 ALOGV("Audio codec1 path not specified");
171 return;
172 }
173 if (!ReadFileToString(kCodec1Path, &file_contents)) {
174 ALOGE("Unable to read codec1 state %s - %s", kCodec1Path, strerror(errno));
175 return;
176 }
177 if (file_contents == "0") {
178 return;
179 } else {
180 ALOGE("%s report hardware fail", kCodec1Path);
181 HardwareFailed failed = {.hardwareType = HardwareFailed::HardwareType::CODEC,
182 .hardwareLocation = 1,
183 .errorCode = HardwareFailed::HardwareErrorCode::COMPLETE};
184 stats_->reportHardwareFailed(failed);
185 }
186 }
187
reportSlowIoFromFile(const char * path,const SlowIo::IoOperation & operation_s)188 void SysfsCollector::reportSlowIoFromFile(const char *path,
189 const SlowIo::IoOperation &operation_s) {
190 std::string file_contents;
191 if (path == nullptr || strlen(path) == 0) {
192 ALOGV("slow_io path not specified");
193 return;
194 }
195 if (!ReadFileToString(path, &file_contents)) {
196 ALOGE("Unable to read slowio %s - %s", path, strerror(errno));
197 return;
198 } else {
199 int32_t slow_io_count = 0;
200 if (sscanf(file_contents.c_str(), "%d", &slow_io_count) != 1) {
201 ALOGE("Unable to parse %s from file %s to int.", file_contents.c_str(), path);
202 } else if (slow_io_count > 0) {
203 SlowIo slowio = {.operation = operation_s, .count = slow_io_count};
204 stats_->reportSlowIo(slowio);
205 }
206 // Clear the stats
207 if (!android::base::WriteStringToFile("0", path, true)) {
208 ALOGE("Unable to clear SlowIO entry %s - %s", path, strerror(errno));
209 }
210 }
211 }
212
213 /**
214 * Check for slow IO operations.
215 */
logSlowIO()216 void SysfsCollector::logSlowIO() {
217 reportSlowIoFromFile(kSlowioReadCntPath, SlowIo::IoOperation::READ);
218 reportSlowIoFromFile(kSlowioWriteCntPath, SlowIo::IoOperation::WRITE);
219 reportSlowIoFromFile(kSlowioUnmapCntPath, SlowIo::IoOperation::UNMAP);
220 reportSlowIoFromFile(kSlowioSyncCntPath, SlowIo::IoOperation::SYNC);
221 }
222
223 /**
224 * Report the last-detected impedance of left & right speakers.
225 */
logSpeakerImpedance()226 void SysfsCollector::logSpeakerImpedance() {
227 std::string file_contents;
228 if (kImpedancePath == nullptr || strlen(kImpedancePath) == 0) {
229 ALOGV("Audio impedance path not specified");
230 return;
231 }
232 if (!ReadFileToString(kImpedancePath, &file_contents)) {
233 ALOGE("Unable to read impedance path %s", kImpedancePath);
234 return;
235 }
236
237 float left, right;
238 if (sscanf(file_contents.c_str(), "%g,%g", &left, &right) != 2) {
239 ALOGE("Unable to parse speaker impedance %s", file_contents.c_str());
240 return;
241 }
242 SpeakerImpedance left_obj = {.speakerLocation = 0,
243 .milliOhms = static_cast<int32_t>(left * 1000)};
244 SpeakerImpedance right_obj = {.speakerLocation = 1,
245 .milliOhms = static_cast<int32_t>(right * 1000)};
246 stats_->reportSpeakerImpedance(left_obj);
247 stats_->reportSpeakerImpedance(right_obj);
248 }
249
250 /**
251 * Report the Speech DSP state.
252 */
logSpeechDspStat()253 void SysfsCollector::logSpeechDspStat() {
254 std::string file_contents;
255 if (kSpeechDspPath == nullptr || strlen(kSpeechDspPath) == 0) {
256 ALOGV("Speech DSP path not specified");
257 return;
258 }
259 if (!ReadFileToString(kSpeechDspPath, &file_contents)) {
260 ALOGE("Unable to read speech dsp path %s", kSpeechDspPath);
261 return;
262 }
263
264 int32_t uptime = 0, downtime = 0, crashcount = 0, recovercount = 0;
265 if (sscanf(file_contents.c_str(), "%d,%d,%d,%d", &uptime, &downtime, &crashcount,
266 &recovercount) != 4) {
267 ALOGE("Unable to parse speech dsp stat %s", file_contents.c_str());
268 return;
269 }
270
271 ALOGD("SpeechDSP uptime %d downtime %d crashcount %d recovercount %d", uptime, downtime,
272 crashcount, recovercount);
273 SpeechDspStat dspstat = {.totalUptimeMillis = uptime,
274 .totalDowntimeMillis = downtime,
275 .totalCrashCount = crashcount,
276 .totalRecoverCount = recovercount};
277
278 stats_->reportSpeechDspStat(dspstat);
279 }
280
logBatteryCapacity()281 void SysfsCollector::logBatteryCapacity() {
282 std::string file_contents;
283 if (kBatteryCapacityCC == nullptr || strlen(kBatteryCapacityCC) == 0) {
284 ALOGV("Battery Capacity CC path not specified");
285 return;
286 }
287 if (kBatteryCapacityVFSOC == nullptr || strlen(kBatteryCapacityVFSOC) == 0) {
288 ALOGV("Battery Capacity VFSOC path not specified");
289 return;
290 }
291 int delta_cc_sum, delta_vfsoc_sum;
292 if (!ReadFileToInt(kBatteryCapacityCC, &delta_cc_sum) ||
293 !ReadFileToInt(kBatteryCapacityVFSOC, &delta_vfsoc_sum))
294 return;
295
296 // Load values array
297 std::vector<VendorAtom::Value> values(2);
298 VendorAtom::Value tmp;
299 tmp.intValue(delta_cc_sum);
300 values[BatteryCapacity::kDeltaCcSumFieldNumber - kVendorAtomOffset] = tmp;
301 tmp.intValue(delta_vfsoc_sum);
302 values[BatteryCapacity::kDeltaVfsocSumFieldNumber - kVendorAtomOffset] = tmp;
303
304 // Send vendor atom to IStats HAL
305 VendorAtom event = {.reverseDomainName = PixelAtoms::ReverseDomainNames().pixel(),
306 .atomId = PixelAtoms::Ids::BATTERY_CAPACITY,
307 .values = values};
308 Return<void> ret = stats_->reportVendorAtom(event);
309 if (!ret.isOk())
310 ALOGE("Unable to report ChargeStats to Stats service");
311 }
312
logUFSLifetime()313 void SysfsCollector::logUFSLifetime() {
314 std::string file_contents;
315 if (kUFSLifetimeA == nullptr || strlen(kUFSLifetimeA) == 0) {
316 ALOGV("UFS lifetimeA path not specified");
317 return;
318 }
319 if (kUFSLifetimeB == nullptr || strlen(kUFSLifetimeB) == 0) {
320 ALOGV("UFS lifetimeB path not specified");
321 return;
322 }
323 if (kUFSLifetimeC == nullptr || strlen(kUFSLifetimeC) == 0) {
324 ALOGV("UFS lifetimeC path not specified");
325 return;
326 }
327
328 int lifetimeA = 0, lifetimeB = 0, lifetimeC = 0;
329 if (!ReadFileToInt(kUFSLifetimeA, &lifetimeA) ||
330 !ReadFileToInt(kUFSLifetimeB, &lifetimeB) ||
331 !ReadFileToInt(kUFSLifetimeC, &lifetimeC)) {
332 ALOGE("Unable to read UFS lifetime : %s", strerror(errno));
333 return;
334 }
335
336 // Load values array
337 std::vector<VendorAtom::Value> values(3);
338 VendorAtom::Value tmp;
339 tmp.intValue(lifetimeA);
340 values[StorageUfsHealth::kLifetimeAFieldNumber - kVendorAtomOffset] = tmp;
341 tmp.intValue(lifetimeB);
342 values[StorageUfsHealth::kLifetimeBFieldNumber - kVendorAtomOffset] = tmp;
343 tmp.intValue(lifetimeC);
344 values[StorageUfsHealth::kLifetimeCFieldNumber - kVendorAtomOffset] = tmp;
345
346
347 // Send vendor atom to IStats HAL
348 VendorAtom event = {.reverseDomainName = PixelAtoms::ReverseDomainNames().pixel(),
349 .atomId = PixelAtoms::Ids::STORAGE_UFS_HEALTH,
350 .values = values};
351 Return<void> ret = stats_->reportVendorAtom(event);
352 if (!ret.isOk()) {
353 ALOGE("Unable to report UfsHealthStat to Stats service");
354 }
355 }
356
getUserDataBlock()357 static std::string getUserDataBlock() {
358 std::unique_ptr<std::FILE, int (*)(std::FILE*)> fp(setmntent("/proc/mounts", "re"), endmntent);
359 if (fp == nullptr) {
360 ALOGE("Error opening /proc/mounts");
361 return "";
362 }
363
364 mntent* mentry;
365 while ((mentry = getmntent(fp.get())) != nullptr) {
366 if (strcmp(mentry->mnt_dir, "/data") == 0) {
367 return std::string(basename(mentry->mnt_fsname));
368 }
369 }
370 return "";
371 }
372
logF2fsStats()373 void SysfsCollector::logF2fsStats() {
374 int dirty, free, cp_calls_fg, gc_calls_fg, moved_block_fg, vblocks;
375 int cp_calls_bg, gc_calls_bg, moved_block_bg;
376
377 if (kF2fsStatsPath == nullptr) {
378 ALOGE("F2fs stats path not specified");
379 return;
380 }
381
382 std::string userdataBlock = getUserDataBlock();
383
384 if (!ReadFileToInt(kF2fsStatsPath + (userdataBlock + "/dirty_segments"), &dirty)) {
385 ALOGV("Unable to read dirty segments");
386 }
387
388 if (!ReadFileToInt(kF2fsStatsPath + (userdataBlock + "/free_segments"), &free)) {
389 ALOGV("Unable to read free segments");
390 }
391
392 if (!ReadFileToInt(kF2fsStatsPath + (userdataBlock + "/cp_foreground_calls"), &cp_calls_fg)) {
393 ALOGV("Unable to read cp_foreground_calls");
394 }
395
396 if (!ReadFileToInt(kF2fsStatsPath + (userdataBlock + "/cp_background_calls"), &cp_calls_bg)) {
397 ALOGV("Unable to read cp_background_calls");
398 }
399
400 if (!ReadFileToInt(kF2fsStatsPath + (userdataBlock + "/gc_foreground_calls"), &gc_calls_fg)) {
401 ALOGV("Unable to read gc_foreground_calls");
402 }
403
404 if (!ReadFileToInt(kF2fsStatsPath + (userdataBlock + "/gc_background_calls"), &gc_calls_bg)) {
405 ALOGV("Unable to read gc_background_calls");
406 }
407
408 if (!ReadFileToInt(kF2fsStatsPath + (userdataBlock + "/moved_blocks_foreground"), &moved_block_fg)) {
409 ALOGV("Unable to read moved_blocks_foreground");
410 }
411
412 if (!ReadFileToInt(kF2fsStatsPath + (userdataBlock + "/moved_blocks_background"), &moved_block_bg)) {
413 ALOGV("Unable to read moved_blocks_background");
414 }
415
416 if (!ReadFileToInt(kF2fsStatsPath + (userdataBlock + "/avg_vblocks"), &vblocks)) {
417 ALOGV("Unable to read avg_vblocks");
418 }
419
420 // Load values array
421 std::vector<VendorAtom::Value> values(9);
422 VendorAtom::Value tmp;
423 tmp.intValue(dirty);
424 values[F2fsStatsInfo::kDirtySegmentsFieldNumber - kVendorAtomOffset] = tmp;
425 tmp.intValue(free);
426 values[F2fsStatsInfo::kFreeSegmentsFieldNumber - kVendorAtomOffset] = tmp;
427 tmp.intValue(cp_calls_fg);
428 values[F2fsStatsInfo::kCpCallsFgFieldNumber - kVendorAtomOffset] = tmp;
429 tmp.intValue(cp_calls_bg);
430 values[F2fsStatsInfo::kCpCallsBgFieldNumber - kVendorAtomOffset] = tmp;
431 tmp.intValue(gc_calls_fg);
432 values[F2fsStatsInfo::kGcCallsFgFieldNumber - kVendorAtomOffset] = tmp;
433 tmp.intValue(gc_calls_bg);
434 values[F2fsStatsInfo::kGcCallsBgFieldNumber - kVendorAtomOffset] = tmp;
435 tmp.intValue(moved_block_fg);
436 values[F2fsStatsInfo::kMovedBlocksFgFieldNumber - kVendorAtomOffset] = tmp;
437 tmp.intValue(moved_block_bg);
438 values[F2fsStatsInfo::kMovedBlocksBgFieldNumber - kVendorAtomOffset] = tmp;
439 tmp.intValue(vblocks);
440 values[F2fsStatsInfo::kValidBlocksFieldNumber - kVendorAtomOffset] = tmp;
441
442 // Send vendor atom to IStats HAL
443 VendorAtom event = {.reverseDomainName = PixelAtoms::ReverseDomainNames().pixel(),
444 .atomId = PixelAtoms::Ids::F2FS_STATS,
445 .values = values};
446 Return<void> ret = stats_->reportVendorAtom(event);
447 if (!ret.isOk()) {
448 ALOGE("Unable to report F2fs stats to Stats service");
449 }
450 }
451
reportZramMmStat()452 void SysfsCollector::reportZramMmStat() {
453 std::string file_contents;
454 if (!kZramMmStatPath) {
455 ALOGV("ZramMmStat path not specified");
456 return;
457 }
458
459 if (!ReadFileToString(kZramMmStatPath, &file_contents)) {
460 ALOGE("Unable to ZramMmStat %s - %s", kZramMmStatPath, strerror(errno));
461 return;
462 } else {
463 int64_t orig_data_size = 0;
464 int64_t compr_data_size = 0;
465 int64_t mem_used_total = 0;
466 int64_t mem_limit = 0;
467 int64_t max_used_total = 0;
468 int64_t same_pages = 0;
469 int64_t pages_compacted = 0;
470 int64_t huge_pages = 0;
471
472 if (sscanf(file_contents.c_str(), "%lu %lu %lu %lu %lu %lu %lu %lu",
473 &orig_data_size, &compr_data_size, &mem_used_total, &mem_limit,
474 &max_used_total, &same_pages, &pages_compacted, &huge_pages) != 8) {
475 ALOGE("Unable to parse ZramMmStat %s from file %s to int.",
476 file_contents.c_str(), kZramMmStatPath);
477 }
478
479 // Load values array
480 std::vector<VendorAtom::Value> values(5);
481 VendorAtom::Value tmp;
482 tmp.intValue(orig_data_size);
483 values[ZramMmStat::kOrigDataSizeFieldNumber - kVendorAtomOffset] = tmp;
484 tmp.intValue(compr_data_size);
485 values[ZramMmStat::kComprDataSizeFieldNumber - kVendorAtomOffset] = tmp;
486 tmp.intValue(mem_used_total);
487 values[ZramMmStat::kMemUsedTotalFieldNumber - kVendorAtomOffset] = tmp;
488 tmp.intValue(same_pages);
489 values[ZramMmStat::kSamePagesFieldNumber - kVendorAtomOffset] = tmp;
490 tmp.intValue(huge_pages);
491 values[ZramMmStat::kHugePagesFieldNumber - kVendorAtomOffset] = tmp;
492
493 // Send vendor atom to IStats HAL
494 VendorAtom event = {.reverseDomainName = PixelAtoms::ReverseDomainNames().pixel(),
495 .atomId = PixelAtoms::Ids::ZRAM_MM_STAT,
496 .values = values};
497 Return<void> ret = stats_->reportVendorAtom(event);
498 if (!ret.isOk())
499 ALOGE("Zram Unable to report ZramMmStat to Stats service");
500 }
501 }
502
reportZramBdStat()503 void SysfsCollector::reportZramBdStat() {
504 std::string file_contents;
505 if (!kZramBdStatPath) {
506 ALOGV("ZramBdStat path not specified");
507 return;
508 }
509
510 if (!ReadFileToString(kZramBdStatPath, &file_contents)) {
511 ALOGE("Unable to ZramBdStat %s - %s", kZramBdStatPath, strerror(errno));
512 return;
513 } else {
514 int64_t bd_count = 0;
515 int64_t bd_reads = 0;
516 int64_t bd_writes = 0;
517
518 if (sscanf(file_contents.c_str(), "%lu %lu %lu",
519 &bd_count, &bd_reads, &bd_writes) != 3) {
520 ALOGE("Unable to parse ZramBdStat %s from file %s to int.",
521 file_contents.c_str(), kZramBdStatPath);
522 }
523
524 // Load values array
525 std::vector<VendorAtom::Value> values(3);
526 VendorAtom::Value tmp;
527 tmp.intValue(bd_count);
528 values[ZramBdStat::kBdCountFieldNumber - kVendorAtomOffset] = tmp;
529 tmp.intValue(bd_reads);
530 values[ZramBdStat::kBdReadsFieldNumber - kVendorAtomOffset] = tmp;
531 tmp.intValue(bd_writes);
532 values[ZramBdStat::kBdWritesFieldNumber - kVendorAtomOffset] = tmp;
533
534 // Send vendor atom to IStats HAL
535 VendorAtom event = {.reverseDomainName = PixelAtoms::ReverseDomainNames().pixel(),
536 .atomId = PixelAtoms::Ids::ZRAM_BD_STAT,
537 .values = values};
538 Return<void> ret = stats_->reportVendorAtom(event);
539 if (!ret.isOk())
540 ALOGE("Zram Unable to report ZramBdStat to Stats service");
541 }
542 }
543
logZramStats()544 void SysfsCollector::logZramStats() {
545 reportZramMmStat();
546 reportZramBdStat();
547 }
548
logBootStats()549 void SysfsCollector::logBootStats() {
550 int mounted_time_sec = 0;
551
552 if (kF2fsStatsPath == nullptr) {
553 ALOGE("F2fs stats path not specified");
554 return;
555 }
556
557 std::string userdataBlock = getUserDataBlock();
558
559 if (!ReadFileToInt(kF2fsStatsPath + (userdataBlock + "/mounted_time_sec"), &mounted_time_sec)) {
560 ALOGV("Unable to read mounted_time_sec");
561 return;
562 }
563
564 int fsck_time_ms = android::base::GetIntProperty("ro.boottime.init.fsck.data", 0);
565 int checkpoint_time_ms = android::base::GetIntProperty("ro.boottime.init.mount.data", 0);
566
567 if (fsck_time_ms == 0 && checkpoint_time_ms == 0) {
568 ALOGV("Not yet initialized");
569 return;
570 }
571
572 // Load values array
573 std::vector<VendorAtom::Value> values(3);
574 VendorAtom::Value tmp;
575 tmp.intValue(mounted_time_sec);
576 values[BootStatsInfo::kMountedTimeSecFieldNumber - kVendorAtomOffset] = tmp;
577 tmp.intValue(fsck_time_ms / 1000);
578 values[BootStatsInfo::kFsckTimeSecFieldNumber - kVendorAtomOffset] = tmp;
579 tmp.intValue(checkpoint_time_ms / 1000);
580 values[BootStatsInfo::kCheckpointTimeSecFieldNumber - kVendorAtomOffset] = tmp;
581
582 // Send vendor atom to IStats HAL
583 VendorAtom event = {.reverseDomainName = PixelAtoms::ReverseDomainNames().pixel(),
584 .atomId = PixelAtoms::Ids::BOOT_STATS,
585 .values = values};
586 Return<void> ret = stats_->reportVendorAtom(event);
587 if (!ret.isOk()) {
588 ALOGE("Unable to report Boot stats to Stats service");
589 } else {
590 log_once_reported = true;
591 }
592 }
593
logAll()594 void SysfsCollector::logAll() {
595 stats_ = IStats::tryGetService();
596 if (!stats_) {
597 ALOGE("Unable to connect to Stats service");
598 return;
599 }
600
601 // Collect once per service init; can be multiple due to service reinit
602 if (!log_once_reported) {
603 logBootStats();
604 }
605 logBatteryChargeCycles();
606 logCodecFailed();
607 logCodec1Failed();
608 logSlowIO();
609 logSpeakerImpedance();
610 logSpeechDspStat();
611 logBatteryCapacity();
612 logUFSLifetime();
613 logF2fsStats();
614 logZramStats();
615 logBatteryEEPROM();
616
617 stats_.clear();
618 }
619
620 /**
621 * Loop forever collecting stats from sysfs nodes and reporting them via
622 * IStats.
623 */
collect(void)624 void SysfsCollector::collect(void) {
625 int timerfd = timerfd_create(CLOCK_BOOTTIME, 0);
626 if (timerfd < 0) {
627 ALOGE("Unable to create timerfd - %s", strerror(errno));
628 return;
629 }
630
631 // Sleep for 30 seconds on launch to allow codec driver to load.
632 sleep(30);
633
634 // Collect first set of stats on boot.
635 logAll();
636
637 // Collect stats every 24hrs after.
638 struct itimerspec period;
639 const int kSecondsPerDay = 60 * 60 * 24;
640 period.it_interval.tv_sec = kSecondsPerDay;
641 period.it_interval.tv_nsec = 0;
642 period.it_value.tv_sec = kSecondsPerDay;
643 period.it_value.tv_nsec = 0;
644
645 if (timerfd_settime(timerfd, 0, &period, NULL)) {
646 ALOGE("Unable to set 24hr timer - %s", strerror(errno));
647 return;
648 }
649
650 while (1) {
651 int readval;
652 do {
653 char buf[8];
654 errno = 0;
655 readval = read(timerfd, buf, sizeof(buf));
656 } while (readval < 0 && errno == EINTR);
657 if (readval < 0) {
658 ALOGE("Timerfd error - %s\n", strerror(errno));
659 return;
660 }
661 logAll();
662 }
663 }
664
665 } // namespace pixel
666 } // namespace google
667 } // namespace hardware
668 } // namespace android
669