blob: 4994d068ae44bc5a6b96928f9e90a6a19adc46e1 [file] [log] [blame]
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "SysfsCollector.h"
#define LOG_TAG "pixelstats-vendor"
#include <android-base/file.h>
#include <android-base/parseint.h>
#include <android-base/strings.h>
#include <hardware/google/pixelstats/1.0/IPixelStats.h>
#include <utils/Log.h>
#include <utils/StrongPointer.h>
#include <utils/Timers.h>
#include <sys/timerfd.h>
#include <string>
namespace device {
namespace google {
namespace crosshatch {
using android::sp;
using android::base::ReadFileToString;
using ::hardware::google::pixelstats::V1_0::IPixelStats;
const char kSlowioReadCntPath[] = "/sys/devices/platform/soc/1d84000.ufshc/slowio_read_cnt";
const char kSlowioWriteCntPath[] = "/sys/devices/platform/soc/1d84000.ufshc/slowio_write_cnt";
const char kSlowioUnmapCntPath[] = "/sys/devices/platform/soc/1d84000.ufshc/slowio_unmap_cnt";
const char kSlowioSyncCntPath[] = "/sys/devices/platform/soc/1d84000.ufshc/slowio_sync_cnt";
const char kCycleCountBinsPath[] = "/sys/class/power_supply/maxfg/cycle_counts_bins";
const char kImpedancePath[] = "/sys/class/misc/msm_cirrus_playback/resistance_left_right";
const char kCodecPath[] =
"/sys/devices/platform/soc/171c0000.slim/tavil-slim-pgd/tavil_codec/codec_state";
/**
* Read the contents of kCycleCountBinsPath and report them via IPixelStats HAL.
* The contents are expected to be N buckets total, the nth of which indicates the
* number of times battery %-full has been increased with the n/N% full bucket.
*/
void SysfsCollector::logBatteryChargeCycles() {
std::string file_contents;
if (!ReadFileToString(kCycleCountBinsPath, &file_contents)) {
ALOGE("Unable to read battery charge cycles - %s", strerror(errno));
return;
}
std::replace(file_contents.begin(), file_contents.end(), ' ', ',');
pixelstats_->reportChargeCycles(android::base::Trim(file_contents));
}
/**
* Check the codec for failures over the past 24hr.
*/
void SysfsCollector::logCodecFailed() {
std::string file_contents;
if (!ReadFileToString(kCodecPath, &file_contents)) {
ALOGE("Unable to read codec state - %s", strerror(errno));
return;
}
if (file_contents == "0") {
return;
} else {
pixelstats_->reportHardwareFailed(IPixelStats::HardwareType::CODEC, 0,
IPixelStats::HardwareErrorCode::COMPLETE);
}
}
void SysfsCollector::reportSlowIoFromFile(const char *path,
const IPixelStats::IoOperation &operation) {
std::string file_contents;
if (!ReadFileToString(path, &file_contents)) {
ALOGE("Unable to read %s - %s", path, strerror(errno));
return;
} else {
int32_t slow_io_count = 0;
if (sscanf(file_contents.c_str(), "%d", &slow_io_count) != 1) {
ALOGE("Unable to parse %s from file %s to int.", file_contents.c_str(), path);
} else if (slow_io_count > 0) {
pixelstats_->reportSlowIo(operation, slow_io_count);
}
// Clear the stats
if (!android::base::WriteStringToFile("0", path, true)) {
ALOGE("Unable to clear SlowIO entry - %s", strerror(errno));
}
}
}
/**
* Check for slow IO operations.
*/
void SysfsCollector::logSlowIO() {
reportSlowIoFromFile(kSlowioReadCntPath, IPixelStats::IoOperation::READ);
reportSlowIoFromFile(kSlowioWriteCntPath, IPixelStats::IoOperation::WRITE);
reportSlowIoFromFile(kSlowioUnmapCntPath, IPixelStats::IoOperation::UNMAP);
reportSlowIoFromFile(kSlowioSyncCntPath, IPixelStats::IoOperation::SYNC);
}
/**
* Report the last-detected impedance of left & right speakers.
*/
void SysfsCollector::logSpeakerImpedance() {
std::string file_contents;
if (!ReadFileToString(kImpedancePath, &file_contents)) {
ALOGE("Unable to read impedance path %s", kImpedancePath);
return;
}
float left, right;
if (sscanf(file_contents.c_str(), "%g,%g", &left, &right) != 2) {
ALOGE("Unable to parse speaker impedance %s", file_contents.c_str());
return;
}
pixelstats_->reportSpeakerImpedance(0, left * 1000);
pixelstats_->reportSpeakerImpedance(1, right * 1000);
}
void SysfsCollector::logAll() {
pixelstats_ = IPixelStats::tryGetService();
if (!pixelstats_) {
ALOGE("Unable to connect to PixelStats service");
return;
}
logBatteryChargeCycles();
logCodecFailed();
logSlowIO();
logSpeakerImpedance();
pixelstats_.clear();
}
/**
* Loop forever collecting stats from sysfs nodes and reporting them via
* IPixelStats.
*/
void SysfsCollector::collect(void) {
int timerfd = timerfd_create(CLOCK_BOOTTIME, 0);
if (timerfd < 0) {
ALOGE("Unable to create timerfd - %s", strerror(errno));
return;
}
// Sleep for 30 seconds on launch to allow codec driver to load.
sleep(30);
// Collect first set of stats on boot.
logAll();
// Collect stats every 24hrs after.
struct itimerspec period;
const int kSecondsPerDay = 60 * 60 * 24;
period.it_interval.tv_sec = kSecondsPerDay;
period.it_interval.tv_nsec = 0;
period.it_value.tv_sec = kSecondsPerDay;
period.it_value.tv_nsec = 0;
if (timerfd_settime(timerfd, 0, &period, NULL)) {
ALOGE("Unable to set 24hr timer - %s", strerror(errno));
return;
}
while (1) {
int readval;
do {
char buf[8];
errno = 0;
readval = read(timerfd, buf, sizeof(buf));
} while (readval < 0 && errno == EINTR);
if (readval < 0) {
ALOGE("Timerfd error - %s\n", strerror(errno));
return;
}
logAll();
}
}
} // namespace crosshatch
} // namespace google
} // namespace device