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