diff options
author | Wei Wang <wvw@google.com> | 2018-03-23 11:28:59 -0700 |
---|---|---|
committer | Wei Wang <wvw@google.com> | 2018-03-23 11:28:59 -0700 |
commit | bbf73e5412c44e643a7b751f24099cd3891adfc3 (patch) | |
tree | 7e4d0fa8f0ec93e8fbfc31833d2bddb7eef93d48 /boottime_tools/bootio/bootio_collector.cpp | |
parent | 56f68caff62f404cff5491b3e84e3d031781db32 (diff) | |
download | extras-bbf73e5412c44e643a7b751f24099cd3891adfc3.tar.gz |
Move boottime tools to system folder
Bug: 65481007
Test: Build
Change-Id: I66b280ff4fe06b47084ce4279705582e3d4d0330
Diffstat (limited to 'boottime_tools/bootio/bootio_collector.cpp')
-rw-r--r-- | boottime_tools/bootio/bootio_collector.cpp | 381 |
1 files changed, 381 insertions, 0 deletions
diff --git a/boottime_tools/bootio/bootio_collector.cpp b/boottime_tools/bootio/bootio_collector.cpp new file mode 100644 index 00000000..495a9aa4 --- /dev/null +++ b/boottime_tools/bootio/bootio_collector.cpp @@ -0,0 +1,381 @@ +/* + * Copyright (C) 2016 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 "bootio_collector.h" +#include <android-base/logging.h> +#include <android-base/file.h> +#include <log/log.h> +#include "protos.pb.h" +#include "time.h" +#include <unordered_map> +#include <inttypes.h> +#include <dirent.h> + +namespace android { + +#define CPU_STAT_FILE "/proc/stat" +#define SAMPLES_FILE "/samples" +#define PID_STAT_FILE "/proc/%d/stat" +#define PID_CMDLINE_FILE "/proc/%d/cmdline" +#define PID_IO_FILE "/proc/%d/io" +#define PROC_DIR "/proc" + +static const int MAX_LINE = 256; + +#define die(...) { LOG(ERROR) << (__VA_ARGS__); exit(EXIT_FAILURE); } + +void PopulateCpu(CpuData& cpu) { + long unsigned utime, ntime, stime, itime; + long unsigned iowtime, irqtime, sirqtime; + FILE *file; + file = fopen(CPU_STAT_FILE, "r"); + if (!file) die("Could not open /proc/stat.\n"); + fscanf(file, "cpu %lu %lu %lu %lu %lu %lu %lu", &utime, &ntime, &stime, + &itime, &iowtime, &irqtime, &sirqtime); + fclose(file); + cpu.set_utime(utime); + cpu.set_ntime(ntime); + cpu.set_stime(stime); + cpu.set_itime(itime); + cpu.set_iowtime(iowtime); + cpu.set_irqtime(irqtime); + cpu.set_sirqtime(sirqtime); +} + +void ClearPreviousResults(std::string path) { + std::string err; + if (!android::base::RemoveFileIfExists(path, &err)) { + LOG(ERROR) << "failed to remove the file " << path << " " << err; + return; + } +} + +int ReadIo(char *filename, AppSample *sample) { + FILE *file; + char line[MAX_LINE]; + unsigned int rchar, wchar, syscr, syscw, readbytes, writebytes; + + file = fopen(filename, "r"); + if (!file) return 1; + while (fgets(line, MAX_LINE, file)) { + sscanf(line, "rchar: %u", &rchar); + sscanf(line, "wchar: %u", &wchar); + sscanf(line, "syscr: %u", &syscr); + sscanf(line, "syscw: %u", &syscw); + sscanf(line, "read_bytes: %u", &readbytes); + sscanf(line, "write_bytes: %u", &writebytes); + } + fclose(file); + sample->set_rchar(rchar); + sample->set_wchar(wchar); + sample->set_syscr(syscr); + sample->set_syscw(syscw); + sample->set_readbytes(readbytes); + sample->set_writebytes(writebytes); + return 0; +} + +int ReadStatForName(char *filename, AppData *app) { + FILE *file; + char buf[MAX_LINE], *open_paren, *close_paren; + + file = fopen(filename, "r"); + if (!file) return 1; + fgets(buf, MAX_LINE, file); + fclose(file); + + /* Split at first '(' and last ')' to get process name. */ + open_paren = strchr(buf, '('); + close_paren = strrchr(buf, ')'); + if (!open_paren || !close_paren) return 1; + + *open_paren = *close_paren = '\0'; + if (!app->has_tname()) { + app->set_tname(open_paren + 1, close_paren - open_paren - 1); + } + return 0; +} + +int ReadStat(char *filename, AppSample *sample) { + FILE *file; + char buf[MAX_LINE], *open_paren, *close_paren; + + file = fopen(filename, "r"); + if (!file) return 1; + fgets(buf, MAX_LINE, file); + fclose(file); + + /* Split at first '(' and last ')' to get process name. */ + open_paren = strchr(buf, '('); + close_paren = strrchr(buf, ')'); + if (!open_paren || !close_paren) return 1; + + uint64_t utime; + uint64_t stime; + uint64_t rss; + + /* Scan rest of string. */ + sscanf(close_paren + 1, + " %*c " "%*d %*d %*d %*d %*d %*d %*d %*d %*d %*d " + "%" PRIu64 /*SCNu64*/ + "%" PRIu64 /*SCNu64*/ "%*d %*d %*d %*d %*d %*d %*d %*d " + "%" PRIu64 /*SCNu64*/ "%*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d", + &utime, + &stime, + &rss); + sample->set_utime(utime); + sample->set_stime(stime); + sample->set_rss(rss); + + return 0; +} + +int ReadCmdline(char *filename, AppData *app) { + FILE *file; + char line[MAX_LINE]; + + line[0] = '\0'; + file = fopen(filename, "r"); + if (!file) return 1; + fgets(line, MAX_LINE, file); + fclose(file); + if (strlen(line) > 0) { + app->set_name(line, strlen(line)); + } else { + app->set_name("N/A"); + } + return 0; +}; + +void ReadProcData(std::unordered_map<int, AppData*>& pidDataMap, DataContainer& dataContainer, + time_t currentTimeUtc, time_t currentUptime) { + DIR *procDir; + struct dirent *pidDir; + pid_t pid; + char filename[64]; + procDir = opendir(PROC_DIR); + if (!procDir) die("Could not open /proc.\n"); + while ((pidDir = readdir(procDir))) { + if (!isdigit(pidDir->d_name[0])) { + continue; + } + pid = atoi(pidDir->d_name); + AppData *data; + + // TODO: in theory same pid can be shared for multiple processes, + // might need add extra check. + if (pidDataMap.count(pid) == 0) { + data = dataContainer.add_app(); + data->set_pid(pid); + sprintf(filename, PID_STAT_FILE, pid); + ReadStatForName(filename, data); + sprintf(filename, PID_CMDLINE_FILE, pid); + ReadCmdline(filename, data); + pidDataMap[pid] = data; + } else { + data = pidDataMap[pid]; + } + AppSample *sample = data->add_samples(); + sample->set_timestamp(currentTimeUtc); + sample->set_uptime(currentUptime); + + sprintf(filename, PID_STAT_FILE, pid); + ReadStat(filename, sample); + + sprintf(filename, PID_IO_FILE, pid); + ReadIo(filename, sample); + } +} + +uint64_t SumCpuValues(CpuData& cpu) { + return cpu.utime() + cpu.ntime() + cpu.stime() + cpu.itime() + cpu.iowtime() + + cpu.irqtime() + cpu.sirqtime(); +} + +time_t GetUptime() { + std::string uptime_str; + if (!android::base::ReadFileToString("/proc/uptime", &uptime_str)) { + LOG(ERROR) << "Failed to read /proc/uptime"; + return -1; + } + + // Cast intentionally rounds down. + return static_cast<time_t>(strtod(uptime_str.c_str(), NULL)); +} + +struct Stats { + int uptime; + float cpu; + uint64_t rbytes; + uint64_t wbytes; +}; + +void PrintPids(DataContainer& data, std::unordered_map<int, uint64_t>& cpuDataMap) { + printf("rchar: number of bytes the process read, using any read-like system call " + "(from files, pipes, tty...).\n"); + printf("wchar: number of bytes the process wrote using any write-like system call.\n"); + printf("wchar: number of bytes the process wrote using any write-like system call.\n"); + printf("syscr: number of write-like system call invocations that the process performed.\n"); + printf("rbytes: number of bytes the process directly read from disk.\n"); + printf("wbytes: number of bytes the process originally dirtied in the page-cache " + "(assuming they will go to disk later).\n\n"); + + std::unique_ptr<AppSample> bootZeroSample(new AppSample()); + std::map<int, Stats> statsMap; + // Init stats map + Stats emptyStat {0, 0., 0, 0}; + for (auto it = cpuDataMap.begin(); it != cpuDataMap.end(); it++) { + statsMap[it->first] = emptyStat; + } + for (int i = 0; i < data.app_size(); i++) { + const AppData appData = data.app(i); + printf("\n-----------------------------------------------------------------------------\n"); + printf("PID:\t%u\n", appData.pid()); + printf("Name:\t%s\n", appData.name().c_str()); + printf("ThName:\t%s\n", appData.tname().c_str()); + printf("%-15s%-13s%-13s%-13s%-13s%-13s%-13s%-13s\n", "Uptime inter.", "rchar", "wchar", + "syscr", "syscw", "rbytes", "wbytes", "cpu%"); + const AppSample *olderSample = NULL; + const AppSample *newerSample = NULL; + bool isFirstSample = true; + for (int j = 0; j < appData.samples_size(); j++) { + olderSample = newerSample; + newerSample = &(appData.samples(j)); + if (olderSample == NULL) { + olderSample = bootZeroSample.get(); + } + float cpuLoad = 0.; + uint64_t cpuDelta; + if (isFirstSample) { + cpuDelta = cpuDataMap[newerSample->timestamp()]; + } else { + cpuDelta = cpuDataMap[newerSample->timestamp()] - + cpuDataMap[olderSample->timestamp()]; + } + if (cpuDelta != 0) { + cpuLoad = (newerSample->utime() - olderSample->utime() + + newerSample->stime() - olderSample->stime()) * 100. / cpuDelta; + } + Stats& stats = statsMap[newerSample->timestamp()]; + stats.uptime = newerSample->uptime(); + stats.cpu += cpuLoad; + stats.rbytes += (newerSample->readbytes() - olderSample->readbytes()); + stats.wbytes += (newerSample->writebytes() - olderSample->writebytes()); + + // Note that all of these are explicitly `long long`s, not int64_t, + // so we can't use PRId64 here. +#define NUMBER "%-13lld" + printf("%5lld - %-5lld " NUMBER NUMBER NUMBER NUMBER NUMBER NUMBER "%-9.2f\n", +#undef NUMBER + olderSample->uptime(), + newerSample->uptime(), + newerSample->rchar() - olderSample->rchar(), + newerSample->wchar() - olderSample->wchar(), + newerSample->syscr() - olderSample->syscr(), + newerSample->syscw() - olderSample->syscw(), + newerSample->readbytes() - olderSample->readbytes(), + newerSample->writebytes() - olderSample->writebytes(), + cpuLoad); + isFirstSample = false; + } + printf("-----------------------------------------------------------------------------\n"); +#define NUMBER "%-13lld" + printf("%-15s" NUMBER NUMBER NUMBER NUMBER NUMBER NUMBER "\n", +#undef NUMBER + "Total", + newerSample->rchar(), + newerSample->wchar(), + newerSample->syscr(), + newerSample->syscw(), + newerSample->readbytes(), + newerSample->writebytes()); + } + printf("\nAggregations\n%-10s%-13s%-13s%-13s\n", + "Total", + "rbytes", + "wbytes", + "cpu%"); + + for (auto it = statsMap.begin(); it != statsMap.end(); it++) { + printf("%-10u%-13" PRIu64 "%-13" PRIu64 "%-9.2f\n", + it->second.uptime, + it->second.rbytes, + it->second.wbytes, + it->second.cpu); + } +} + +} + +BootioCollector::BootioCollector(std::string path) { + DCHECK_EQ('/', path.back()); + path_ = path; +} + +void BootioCollector::StartDataCollection(int timeout, int samples) { + android::ClearPreviousResults(getStoragePath()); + int remaining = samples + 1; + int delayS = timeout / samples; + + std::unordered_map < int, AppData * > pidDataMap; + std::unique_ptr <DataContainer> data(new DataContainer()); + while (remaining > 0) { + time_t currentTimeUtc = time(nullptr); + time_t currentUptime = android::GetUptime(); + CpuData *cpu = data->add_cpu(); + cpu->set_timestamp(currentTimeUtc); + cpu->set_uptime(currentUptime); + android::PopulateCpu(*cpu); + android::ReadProcData(pidDataMap, *data.get(), currentTimeUtc, currentUptime); + remaining--; + if (remaining == 0) { + continue; + } + sleep(delayS); + } + std::string file_data; + if (!data->SerializeToString(&file_data)) { + LOG(ERROR) << "Failed to serialize"; + return; + } + if (!android::base::WriteStringToFile(file_data, getStoragePath())) { + LOG(ERROR) << "Failed to write samples"; + } +} + +void BootioCollector::Print() { + std::string file_data; + if (!android::base::ReadFileToString(getStoragePath(), &file_data)) { + printf("Failed to read data from file.\n"); + return; + } + std::unique_ptr <DataContainer> data(new DataContainer()); + if (!data->ParsePartialFromString(file_data)) { + printf("Failed to parse data.\n"); + return; + } + std::unordered_map<int, uint64_t> cpuDataMap; + for (int i = 0; i < data->cpu_size(); i++) { + CpuData cpu_data = data->cpu(i); + cpuDataMap[cpu_data.timestamp()] = android::SumCpuValues(cpu_data); + } + android::PrintPids(*data.get(), cpuDataMap); +} + + +std::string BootioCollector::getStoragePath() { + return path_ + SAMPLES_FILE; +} |