Merge "metricsd: Only collect metrics over a short period."
This commit is contained in:
commit
66255db05d
|
@ -26,6 +26,7 @@ metrics_client_sources := \
|
|||
metrics_client.cc
|
||||
|
||||
metrics_daemon_sources := \
|
||||
collectors/averaged_statistics_collector.cc \
|
||||
collectors/disk_usage_collector.cc \
|
||||
metrics_daemon.cc \
|
||||
metrics_daemon_main.cc \
|
||||
|
@ -40,6 +41,8 @@ metrics_daemon_sources := \
|
|||
serialization/serialization_utils.cc
|
||||
|
||||
metrics_tests_sources := \
|
||||
collectors/averaged_statistics_collector.cc \
|
||||
collectors/averaged_statistics_collector_test.cc \
|
||||
collectors/disk_usage_collector.cc \
|
||||
metrics_daemon.cc \
|
||||
metrics_daemon_test.cc \
|
||||
|
@ -146,6 +149,7 @@ include $(BUILD_EXECUTABLE)
|
|||
# ========================================================
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := metrics_tests
|
||||
LOCAL_CLANG := true
|
||||
LOCAL_CFLAGS := $(metrics_CFLAGS)
|
||||
LOCAL_CPP_EXTENSION := $(metrics_cpp_extension)
|
||||
LOCAL_CPPFLAGS := $(metrics_CPPFLAGS) -Wno-sign-compare
|
||||
|
|
|
@ -0,0 +1,216 @@
|
|||
/*
|
||||
* Copyright (C) 2015 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 "averaged_statistics_collector.h"
|
||||
|
||||
#include <base/files/file_util.h>
|
||||
#include <base/files/file_path.h>
|
||||
#include <base/strings/string_number_conversions.h>
|
||||
#include <base/strings/string_split.h>
|
||||
|
||||
#include "metrics_daemon.h"
|
||||
|
||||
namespace {
|
||||
|
||||
// disk stats metrics
|
||||
|
||||
// The {Read,Write}Sectors numbers are in sectors/second.
|
||||
// A sector is usually 512 bytes.
|
||||
const char kReadSectorsHistogramName[] = "Platform.ReadSectors";
|
||||
const char kWriteSectorsHistogramName[] = "Platform.WriteSectors";
|
||||
const int kDiskMetricsStatItemCount = 11;
|
||||
|
||||
// Assume a max rate of 250Mb/s for reads (worse for writes) and 512 byte
|
||||
// sectors.
|
||||
const int kSectorsIOMax = 500000; // sectors/second
|
||||
const int kSectorsBuckets = 50; // buckets
|
||||
|
||||
// Page size is 4k, sector size is 0.5k. We're not interested in page fault
|
||||
// rates that the disk cannot sustain.
|
||||
const int kPageFaultsMax = kSectorsIOMax / 8; // Page faults/second
|
||||
const int kPageFaultsBuckets = 50;
|
||||
|
||||
// Major page faults, i.e. the ones that require data to be read from disk.
|
||||
const char kPageFaultsHistogramName[] = "Platform.PageFaults";
|
||||
|
||||
// Swap in and Swap out
|
||||
const char kSwapInHistogramName[] = "Platform.SwapIn";
|
||||
const char kSwapOutHistogramName[] = "Platform.SwapOut";
|
||||
|
||||
const int kIntervalBetweenCollection = 60; // seconds
|
||||
const int kCollectionDuration = 1; // seconds
|
||||
|
||||
} // namespace
|
||||
|
||||
AveragedStatisticsCollector::AveragedStatisticsCollector(
|
||||
MetricsLibraryInterface* metrics_library,
|
||||
const std::string& diskstats_path,
|
||||
const std::string& vmstats_path) :
|
||||
metrics_lib_(metrics_library),
|
||||
diskstats_path_(diskstats_path),
|
||||
vmstats_path_(vmstats_path) {
|
||||
}
|
||||
|
||||
void AveragedStatisticsCollector::ScheduleWait() {
|
||||
base::MessageLoop::current()->PostDelayedTask(FROM_HERE,
|
||||
base::Bind(&AveragedStatisticsCollector::WaitCallback,
|
||||
base::Unretained(this)),
|
||||
base::TimeDelta::FromSeconds(
|
||||
kIntervalBetweenCollection - kCollectionDuration));
|
||||
}
|
||||
|
||||
void AveragedStatisticsCollector::ScheduleCollect() {
|
||||
base::MessageLoop::current()->PostDelayedTask(FROM_HERE,
|
||||
base::Bind(&AveragedStatisticsCollector::CollectCallback,
|
||||
base::Unretained(this)),
|
||||
base::TimeDelta::FromSeconds(kCollectionDuration));
|
||||
}
|
||||
|
||||
void AveragedStatisticsCollector::WaitCallback() {
|
||||
ReadInitialValues();
|
||||
ScheduleCollect();
|
||||
}
|
||||
|
||||
void AveragedStatisticsCollector::CollectCallback() {
|
||||
Collect();
|
||||
ScheduleWait();
|
||||
}
|
||||
|
||||
void AveragedStatisticsCollector::ReadInitialValues() {
|
||||
stats_start_time_ = MetricsDaemon::GetActiveTime();
|
||||
DiskStatsReadStats(&read_sectors_, &write_sectors_);
|
||||
VmStatsReadStats(&vmstats_);
|
||||
}
|
||||
|
||||
bool AveragedStatisticsCollector::DiskStatsReadStats(
|
||||
uint64_t* read_sectors, uint64_t* write_sectors) {
|
||||
CHECK(read_sectors);
|
||||
CHECK(write_sectors);
|
||||
std::string line;
|
||||
if (diskstats_path_.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!base::ReadFileToString(base::FilePath(diskstats_path_), &line)) {
|
||||
PLOG(WARNING) << "Could not read disk stats from "
|
||||
<< diskstats_path_.value();
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<std::string> parts = base::SplitString(
|
||||
line, " ", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
|
||||
if (parts.size() != kDiskMetricsStatItemCount) {
|
||||
LOG(ERROR) << "Could not parse disk stat correctly. Expected "
|
||||
<< kDiskMetricsStatItemCount << " elements but got "
|
||||
<< parts.size();
|
||||
return false;
|
||||
}
|
||||
if (!base::StringToUint64(parts[2], read_sectors)) {
|
||||
LOG(ERROR) << "Couldn't convert read sectors " << parts[2] << " to uint64";
|
||||
return false;
|
||||
}
|
||||
if (!base::StringToUint64(parts[6], write_sectors)) {
|
||||
LOG(ERROR) << "Couldn't convert write sectors " << parts[6] << " to uint64";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AveragedStatisticsCollector::VmStatsParseStats(
|
||||
const char* stats, struct VmstatRecord* record) {
|
||||
CHECK(stats);
|
||||
CHECK(record);
|
||||
base::StringPairs pairs;
|
||||
base::SplitStringIntoKeyValuePairs(stats, ' ', '\n', &pairs);
|
||||
|
||||
for (base::StringPairs::iterator it = pairs.begin();
|
||||
it != pairs.end(); ++it) {
|
||||
if (it->first == "pgmajfault" &&
|
||||
!base::StringToUint64(it->second, &record->page_faults)) {
|
||||
return false;
|
||||
}
|
||||
if (it->first == "pswpin" &&
|
||||
!base::StringToUint64(it->second, &record->swap_in)) {
|
||||
return false;
|
||||
}
|
||||
if (it->first == "pswpout" &&
|
||||
!base::StringToUint64(it->second, &record->swap_out)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AveragedStatisticsCollector::VmStatsReadStats(struct VmstatRecord* stats) {
|
||||
CHECK(stats);
|
||||
std::string value_string;
|
||||
if (!base::ReadFileToString(vmstats_path_, &value_string)) {
|
||||
LOG(WARNING) << "cannot read " << vmstats_path_.value();
|
||||
return false;
|
||||
}
|
||||
return VmStatsParseStats(value_string.c_str(), stats);
|
||||
}
|
||||
|
||||
void AveragedStatisticsCollector::Collect() {
|
||||
uint64_t read_sectors_now, write_sectors_now;
|
||||
struct VmstatRecord vmstats_now;
|
||||
double time_now = MetricsDaemon::GetActiveTime();
|
||||
double delta_time = time_now - stats_start_time_;
|
||||
bool diskstats_success = DiskStatsReadStats(&read_sectors_now,
|
||||
&write_sectors_now);
|
||||
|
||||
int delta_read = read_sectors_now - read_sectors_;
|
||||
int delta_write = write_sectors_now - write_sectors_;
|
||||
int read_sectors_per_second = delta_read / delta_time;
|
||||
int write_sectors_per_second = delta_write / delta_time;
|
||||
bool vmstats_success = VmStatsReadStats(&vmstats_now);
|
||||
uint64_t delta_faults = vmstats_now.page_faults - vmstats_.page_faults;
|
||||
uint64_t delta_swap_in = vmstats_now.swap_in - vmstats_.swap_in;
|
||||
uint64_t delta_swap_out = vmstats_now.swap_out - vmstats_.swap_out;
|
||||
uint64_t page_faults_per_second = delta_faults / delta_time;
|
||||
uint64_t swap_in_per_second = delta_swap_in / delta_time;
|
||||
uint64_t swap_out_per_second = delta_swap_out / delta_time;
|
||||
if (diskstats_success) {
|
||||
metrics_lib_->SendToUMA(kReadSectorsHistogramName,
|
||||
read_sectors_per_second,
|
||||
1,
|
||||
kSectorsIOMax,
|
||||
kSectorsBuckets);
|
||||
metrics_lib_->SendToUMA(kWriteSectorsHistogramName,
|
||||
write_sectors_per_second,
|
||||
1,
|
||||
kSectorsIOMax,
|
||||
kSectorsBuckets);
|
||||
}
|
||||
if (vmstats_success) {
|
||||
metrics_lib_->SendToUMA(kPageFaultsHistogramName,
|
||||
page_faults_per_second,
|
||||
1,
|
||||
kPageFaultsMax,
|
||||
kPageFaultsBuckets);
|
||||
metrics_lib_->SendToUMA(kSwapInHistogramName,
|
||||
swap_in_per_second,
|
||||
1,
|
||||
kPageFaultsMax,
|
||||
kPageFaultsBuckets);
|
||||
metrics_lib_->SendToUMA(kSwapOutHistogramName,
|
||||
swap_out_per_second,
|
||||
1,
|
||||
kPageFaultsMax,
|
||||
kPageFaultsBuckets);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
* Copyright (C) 2015 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.
|
||||
*/
|
||||
|
||||
#ifndef METRICSD_COLLECTORS_AVERAGED_STATISTICS_COLLECTOR_H_
|
||||
#define METRICSD_COLLECTORS_AVERAGED_STATISTICS_COLLECTOR_H_
|
||||
|
||||
#include "metrics/metrics_library.h"
|
||||
|
||||
class AveragedStatisticsCollector {
|
||||
public:
|
||||
AveragedStatisticsCollector(MetricsLibraryInterface* metrics_library,
|
||||
const std::string& diskstats_path,
|
||||
const std::string& vmstat_path);
|
||||
|
||||
// Schedule a wait period.
|
||||
void ScheduleWait();
|
||||
|
||||
// Schedule a collection period.
|
||||
void ScheduleCollect();
|
||||
|
||||
// Callback used by the main loop.
|
||||
void CollectCallback();
|
||||
|
||||
// Callback used by the main loop.
|
||||
void WaitCallback();
|
||||
|
||||
// Read and store the initial values at the beginning of a collection cycle.
|
||||
void ReadInitialValues();
|
||||
|
||||
// Collect the disk usage statistics and report them.
|
||||
void Collect();
|
||||
|
||||
private:
|
||||
friend class AveragedStatisticsTest;
|
||||
FRIEND_TEST(AveragedStatisticsTest, ParseDiskStats);
|
||||
FRIEND_TEST(AveragedStatisticsTest, ParseVmStats);
|
||||
|
||||
// Record for retrieving and reporting values from /proc/vmstat
|
||||
struct VmstatRecord {
|
||||
uint64_t page_faults; // major faults
|
||||
uint64_t swap_in; // pages swapped in
|
||||
uint64_t swap_out; // pages swapped out
|
||||
};
|
||||
|
||||
// Read the disk read/write statistics for the main disk.
|
||||
bool DiskStatsReadStats(uint64_t* read_sectors, uint64_t* write_sectors);
|
||||
|
||||
// Parse the content of the vmstats file into |record|.
|
||||
bool VmStatsParseStats(const char* stats, struct VmstatRecord* record);
|
||||
|
||||
// Read the vmstats into |stats|.
|
||||
bool VmStatsReadStats(struct VmstatRecord* stats);
|
||||
|
||||
MetricsLibraryInterface* metrics_lib_;
|
||||
base::FilePath diskstats_path_;
|
||||
base::FilePath vmstats_path_;
|
||||
|
||||
// Values observed at the beginning of the collection period.
|
||||
uint64_t read_sectors_;
|
||||
uint64_t write_sectors_;
|
||||
struct VmstatRecord vmstats_;
|
||||
|
||||
double stats_start_time_;
|
||||
};
|
||||
|
||||
#endif // METRICSD_COLLECTORS_AVERAGED_STATISTICS_COLLECTOR_H_
|
|
@ -0,0 +1,99 @@
|
|||
/*
|
||||
* Copyright (C) 2015 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 "averaged_statistics_collector.h"
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
#include <base/files/file_util.h>
|
||||
#include <base/files/scoped_temp_dir.h>
|
||||
#include <base/memory/scoped_ptr.h>
|
||||
#include <base/strings/stringprintf.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
|
||||
static const char kFakeDiskStatsFormat[] =
|
||||
" 1793 1788 %" PRIu64 " 105580 "
|
||||
" 196 175 %" PRIu64 " 30290 "
|
||||
" 0 44060 135850\n";
|
||||
static const uint64_t kFakeReadSectors[] = {80000, 100000};
|
||||
static const uint64_t kFakeWriteSectors[] = {3000, 4000};
|
||||
|
||||
|
||||
class AveragedStatisticsTest : public testing::Test {
|
||||
protected:
|
||||
std::string kFakeDiskStats0;
|
||||
std::string kFakeDiskStats1;
|
||||
|
||||
virtual void SetUp() {
|
||||
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
|
||||
disk_stats_path_ = temp_dir_.path().Append("disk_stats");
|
||||
collector_.reset(new AveragedStatisticsCollector(
|
||||
&metrics_lib_, disk_stats_path_.value(), ""));
|
||||
|
||||
kFakeDiskStats0 = base::StringPrintf(kFakeDiskStatsFormat,
|
||||
kFakeReadSectors[0],
|
||||
kFakeWriteSectors[0]);
|
||||
kFakeDiskStats1 = base::StringPrintf(kFakeDiskStatsFormat,
|
||||
kFakeReadSectors[1],
|
||||
kFakeWriteSectors[1]);
|
||||
|
||||
CreateFakeDiskStatsFile(kFakeDiskStats0);
|
||||
}
|
||||
|
||||
// Creates or overwrites an input file containing fake disk stats.
|
||||
void CreateFakeDiskStatsFile(const std::string& fake_stats) {
|
||||
EXPECT_EQ(base::WriteFile(disk_stats_path_,
|
||||
fake_stats.data(), fake_stats.size()),
|
||||
fake_stats.size());
|
||||
}
|
||||
|
||||
// Collector used for tests.
|
||||
scoped_ptr<AveragedStatisticsCollector> collector_;
|
||||
|
||||
// Temporary directory used for tests.
|
||||
base::ScopedTempDir temp_dir_;
|
||||
|
||||
// Path for the fake files.
|
||||
base::FilePath disk_stats_path_;
|
||||
|
||||
MetricsLibrary metrics_lib_;
|
||||
};
|
||||
|
||||
TEST_F(AveragedStatisticsTest, ParseDiskStats) {
|
||||
uint64_t read_sectors_now, write_sectors_now;
|
||||
CreateFakeDiskStatsFile(kFakeDiskStats0);
|
||||
ASSERT_TRUE(collector_->DiskStatsReadStats(&read_sectors_now,
|
||||
&write_sectors_now));
|
||||
EXPECT_EQ(read_sectors_now, kFakeReadSectors[0]);
|
||||
EXPECT_EQ(write_sectors_now, kFakeWriteSectors[0]);
|
||||
|
||||
CreateFakeDiskStatsFile(kFakeDiskStats1);
|
||||
ASSERT_TRUE(collector_->DiskStatsReadStats(&read_sectors_now,
|
||||
&write_sectors_now));
|
||||
EXPECT_EQ(read_sectors_now, kFakeReadSectors[1]);
|
||||
EXPECT_EQ(write_sectors_now, kFakeWriteSectors[1]);
|
||||
}
|
||||
|
||||
TEST_F(AveragedStatisticsTest, ParseVmStats) {
|
||||
static char kVmStats[] = "pswpin 1345\npswpout 8896\n"
|
||||
"foo 100\nbar 200\npgmajfault 42\netcetc 300\n";
|
||||
struct AveragedStatisticsCollector::VmstatRecord stats;
|
||||
EXPECT_TRUE(collector_->VmStatsParseStats(kVmStats, &stats));
|
||||
EXPECT_EQ(stats.page_faults, 42);
|
||||
EXPECT_EQ(stats.swap_in, 1345);
|
||||
EXPECT_EQ(stats.swap_out, 8896);
|
||||
}
|
|
@ -16,10 +16,6 @@
|
|||
|
||||
#include "metrics_daemon.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <inttypes.h>
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
#include <sysexits.h>
|
||||
#include <time.h>
|
||||
|
||||
|
@ -71,48 +67,12 @@ const char kKernelCrashDetectedFile[] = "/var/run/kernel-crash-detected";
|
|||
const char kUncleanShutdownDetectedFile[] =
|
||||
"/var/run/unclean-shutdown-detected";
|
||||
|
||||
// disk stats metrics
|
||||
|
||||
// The {Read,Write}Sectors numbers are in sectors/second.
|
||||
// A sector is usually 512 bytes.
|
||||
|
||||
const char kMetricReadSectorsLongName[] = "Platform.ReadSectors.PerMinute";
|
||||
const char kMetricWriteSectorsLongName[] = "Platform.WriteSectors.PerMinute";
|
||||
const char kMetricReadSectorsShortName[] = "Platform.ReadSectors.PerSecond";
|
||||
const char kMetricWriteSectorsShortName[] = "Platform.WriteSectors.PerSecond";
|
||||
|
||||
const int kMetricStatsShortInterval = 1; // seconds
|
||||
const int kMetricStatsLongInterval = 60; // seconds
|
||||
|
||||
const int kMetricMeminfoInterval = 30; // seconds
|
||||
|
||||
// Assume a max rate of 250Mb/s for reads (worse for writes) and 512 byte
|
||||
// sectors.
|
||||
const int kMetricSectorsIOMax = 500000; // sectors/second
|
||||
const int kMetricSectorsBuckets = 50; // buckets
|
||||
// Page size is 4k, sector size is 0.5k. We're not interested in page fault
|
||||
// rates that the disk cannot sustain.
|
||||
const int kMetricPageFaultsMax = kMetricSectorsIOMax / 8;
|
||||
const int kMetricPageFaultsBuckets = 50;
|
||||
|
||||
// Major page faults, i.e. the ones that require data to be read from disk.
|
||||
|
||||
const char kMetricPageFaultsLongName[] = "Platform.PageFaults.PerMinute";
|
||||
const char kMetricPageFaultsShortName[] = "Platform.PageFaults.PerSecond";
|
||||
|
||||
// Swap in and Swap out
|
||||
|
||||
const char kMetricSwapInLongName[] = "Platform.SwapIn.PerMinute";
|
||||
const char kMetricSwapInShortName[] = "Platform.SwapIn.PerSecond";
|
||||
|
||||
const char kMetricSwapOutLongName[] = "Platform.SwapOut.PerMinute";
|
||||
const char kMetricSwapOutShortName[] = "Platform.SwapOut.PerSecond";
|
||||
|
||||
const char kMetricsProcStatFileName[] = "/proc/stat";
|
||||
const char kVmStatFileName[] = "/proc/vmstat";
|
||||
const char kMeminfoFileName[] = "/proc/meminfo";
|
||||
const char kVmStatFileName[] = "/proc/vmstat";
|
||||
const int kMetricsProcStatFirstLineItemsCount = 11;
|
||||
const int kDiskMetricsStatItemCount = 11;
|
||||
|
||||
// Thermal CPU throttling.
|
||||
|
||||
|
@ -142,17 +102,13 @@ static const int kMemuseIntervals[] = {
|
|||
MetricsDaemon::MetricsDaemon()
|
||||
: memuse_final_time_(0),
|
||||
memuse_interval_index_(0),
|
||||
read_sectors_(0),
|
||||
write_sectors_(0),
|
||||
vmstats_(),
|
||||
stats_state_(kStatsShort),
|
||||
stats_initial_time_(0),
|
||||
ticks_per_second_(0),
|
||||
latest_cpu_use_ticks_(0) {}
|
||||
|
||||
MetricsDaemon::~MetricsDaemon() {
|
||||
}
|
||||
|
||||
// static
|
||||
double MetricsDaemon::GetActiveTime() {
|
||||
struct timespec ts;
|
||||
int r = clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
|
@ -275,14 +231,12 @@ void MetricsDaemon::Init(bool testing,
|
|||
weekly_cycle_.reset(new PersistentInteger("weekly.cycle"));
|
||||
version_cycle_.reset(new PersistentInteger("version.cycle"));
|
||||
|
||||
diskstats_path_ = diskstats_path;
|
||||
scaling_max_freq_path_ = scaling_max_freq_path;
|
||||
cpuinfo_max_freq_path_ = cpuinfo_max_freq_path;
|
||||
disk_usage_collector_.reset(new DiskUsageCollector(metrics_lib_));
|
||||
|
||||
// If testing, initialize Stats Reporter without connecting DBus
|
||||
if (testing_)
|
||||
StatsReporterInit();
|
||||
averaged_stats_collector_.reset(
|
||||
new AveragedStatisticsCollector(metrics_lib_, diskstats_path,
|
||||
kVmStatFileName));
|
||||
}
|
||||
|
||||
int MetricsDaemon::OnInit() {
|
||||
|
@ -494,94 +448,12 @@ bool MetricsDaemon::CheckSystemCrash(const string& crash_file) {
|
|||
|
||||
void MetricsDaemon::StatsReporterInit() {
|
||||
disk_usage_collector_->Schedule();
|
||||
DiskStatsReadStats(&read_sectors_, &write_sectors_);
|
||||
VmStatsReadStats(&vmstats_);
|
||||
// The first time around just run the long stat, so we don't delay boot.
|
||||
stats_state_ = kStatsLong;
|
||||
stats_initial_time_ = GetActiveTime();
|
||||
if (stats_initial_time_ < 0) {
|
||||
LOG(WARNING) << "not collecting disk stats";
|
||||
} else {
|
||||
ScheduleStatsCallback(kMetricStatsLongInterval);
|
||||
}
|
||||
|
||||
// Don't start a collection cycle during the first run to avoid delaying the
|
||||
// boot.
|
||||
averaged_stats_collector_->ScheduleWait();
|
||||
}
|
||||
|
||||
void MetricsDaemon::ScheduleStatsCallback(int wait) {
|
||||
if (testing_) {
|
||||
return;
|
||||
}
|
||||
base::MessageLoop::current()->PostDelayedTask(FROM_HERE,
|
||||
base::Bind(&MetricsDaemon::StatsCallback, base::Unretained(this)),
|
||||
base::TimeDelta::FromSeconds(wait));
|
||||
}
|
||||
|
||||
bool MetricsDaemon::DiskStatsReadStats(uint64_t* read_sectors,
|
||||
uint64_t* write_sectors) {
|
||||
CHECK(read_sectors);
|
||||
CHECK(write_sectors);
|
||||
std::string line;
|
||||
if (diskstats_path_.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!base::ReadFileToString(base::FilePath(diskstats_path_), &line)) {
|
||||
PLOG(WARNING) << "Could not read disk stats from " << diskstats_path_;
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<std::string> parts = base::SplitString(
|
||||
line, " ", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
|
||||
if (parts.size() != kDiskMetricsStatItemCount) {
|
||||
LOG(ERROR) << "Could not parse disk stat correctly. Expected "
|
||||
<< kDiskMetricsStatItemCount << " elements but got "
|
||||
<< parts.size();
|
||||
return false;
|
||||
}
|
||||
if (!base::StringToUint64(parts[2], read_sectors)) {
|
||||
LOG(ERROR) << "Couldn't convert read sectors " << parts[2] << " to uint64";
|
||||
return false;
|
||||
}
|
||||
if (!base::StringToUint64(parts[6], write_sectors)) {
|
||||
LOG(ERROR) << "Couldn't convert write sectors " << parts[6] << " to uint64";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MetricsDaemon::VmStatsParseStats(const char* stats,
|
||||
struct VmstatRecord* record) {
|
||||
CHECK(stats);
|
||||
CHECK(record);
|
||||
base::StringPairs pairs;
|
||||
base::SplitStringIntoKeyValuePairs(stats, ' ', '\n', &pairs);
|
||||
|
||||
for (base::StringPairs::iterator it = pairs.begin(); it != pairs.end(); ++it) {
|
||||
if (it->first == "pgmajfault" &&
|
||||
!base::StringToUint64(it->second, &record->page_faults_)) {
|
||||
return false;
|
||||
}
|
||||
if (it->first == "pswpin" &&
|
||||
!base::StringToUint64(it->second, &record->swap_in_)) {
|
||||
return false;
|
||||
}
|
||||
if (it->first == "pswpout" &&
|
||||
!base::StringToUint64(it->second, &record->swap_out_)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MetricsDaemon::VmStatsReadStats(struct VmstatRecord* stats) {
|
||||
CHECK(stats);
|
||||
string value_string;
|
||||
if (!base::ReadFileToString(base::FilePath(kVmStatFileName), &value_string)) {
|
||||
LOG(WARNING) << "cannot read " << kVmStatFileName;
|
||||
return false;
|
||||
}
|
||||
return VmStatsParseStats(value_string.c_str(), stats);
|
||||
}
|
||||
|
||||
bool MetricsDaemon::ReadFreqToInt(const string& sysfs_file_name, int* value) {
|
||||
const FilePath sysfs_path(sysfs_file_name);
|
||||
|
@ -639,115 +511,6 @@ void MetricsDaemon::SendCpuThrottleMetrics() {
|
|||
SendLinearSample(kMetricScaledCpuFrequencyName, percent, 101, 102);
|
||||
}
|
||||
|
||||
// Collects disk and vm stats alternating over a short and a long interval.
|
||||
|
||||
void MetricsDaemon::StatsCallback() {
|
||||
uint64_t read_sectors_now, write_sectors_now;
|
||||
struct VmstatRecord vmstats_now;
|
||||
double time_now = GetActiveTime();
|
||||
double delta_time = time_now - stats_initial_time_;
|
||||
if (testing_) {
|
||||
// Fake the time when testing.
|
||||
delta_time = stats_state_ == kStatsShort ?
|
||||
kMetricStatsShortInterval : kMetricStatsLongInterval;
|
||||
}
|
||||
bool diskstats_success = DiskStatsReadStats(&read_sectors_now,
|
||||
&write_sectors_now);
|
||||
int delta_read = read_sectors_now - read_sectors_;
|
||||
int delta_write = write_sectors_now - write_sectors_;
|
||||
int read_sectors_per_second = delta_read / delta_time;
|
||||
int write_sectors_per_second = delta_write / delta_time;
|
||||
bool vmstats_success = VmStatsReadStats(&vmstats_now);
|
||||
uint64_t delta_faults = vmstats_now.page_faults_ - vmstats_.page_faults_;
|
||||
uint64_t delta_swap_in = vmstats_now.swap_in_ - vmstats_.swap_in_;
|
||||
uint64_t delta_swap_out = vmstats_now.swap_out_ - vmstats_.swap_out_;
|
||||
uint64_t page_faults_per_second = delta_faults / delta_time;
|
||||
uint64_t swap_in_per_second = delta_swap_in / delta_time;
|
||||
uint64_t swap_out_per_second = delta_swap_out / delta_time;
|
||||
|
||||
switch (stats_state_) {
|
||||
case kStatsShort:
|
||||
if (diskstats_success) {
|
||||
SendSample(kMetricReadSectorsShortName,
|
||||
read_sectors_per_second,
|
||||
1,
|
||||
kMetricSectorsIOMax,
|
||||
kMetricSectorsBuckets);
|
||||
SendSample(kMetricWriteSectorsShortName,
|
||||
write_sectors_per_second,
|
||||
1,
|
||||
kMetricSectorsIOMax,
|
||||
kMetricSectorsBuckets);
|
||||
}
|
||||
if (vmstats_success) {
|
||||
SendSample(kMetricPageFaultsShortName,
|
||||
page_faults_per_second,
|
||||
1,
|
||||
kMetricPageFaultsMax,
|
||||
kMetricPageFaultsBuckets);
|
||||
SendSample(kMetricSwapInShortName,
|
||||
swap_in_per_second,
|
||||
1,
|
||||
kMetricPageFaultsMax,
|
||||
kMetricPageFaultsBuckets);
|
||||
SendSample(kMetricSwapOutShortName,
|
||||
swap_out_per_second,
|
||||
1,
|
||||
kMetricPageFaultsMax,
|
||||
kMetricPageFaultsBuckets);
|
||||
}
|
||||
// Schedule long callback.
|
||||
stats_state_ = kStatsLong;
|
||||
ScheduleStatsCallback(kMetricStatsLongInterval -
|
||||
kMetricStatsShortInterval);
|
||||
break;
|
||||
case kStatsLong:
|
||||
if (diskstats_success) {
|
||||
SendSample(kMetricReadSectorsLongName,
|
||||
read_sectors_per_second,
|
||||
1,
|
||||
kMetricSectorsIOMax,
|
||||
kMetricSectorsBuckets);
|
||||
SendSample(kMetricWriteSectorsLongName,
|
||||
write_sectors_per_second,
|
||||
1,
|
||||
kMetricSectorsIOMax,
|
||||
kMetricSectorsBuckets);
|
||||
// Reset sector counters.
|
||||
read_sectors_ = read_sectors_now;
|
||||
write_sectors_ = write_sectors_now;
|
||||
}
|
||||
if (vmstats_success) {
|
||||
SendSample(kMetricPageFaultsLongName,
|
||||
page_faults_per_second,
|
||||
1,
|
||||
kMetricPageFaultsMax,
|
||||
kMetricPageFaultsBuckets);
|
||||
SendSample(kMetricSwapInLongName,
|
||||
swap_in_per_second,
|
||||
1,
|
||||
kMetricPageFaultsMax,
|
||||
kMetricPageFaultsBuckets);
|
||||
SendSample(kMetricSwapOutLongName,
|
||||
swap_out_per_second,
|
||||
1,
|
||||
kMetricPageFaultsMax,
|
||||
kMetricPageFaultsBuckets);
|
||||
|
||||
vmstats_ = vmstats_now;
|
||||
}
|
||||
SendCpuThrottleMetrics();
|
||||
// Set start time for new cycle.
|
||||
stats_initial_time_ = time_now;
|
||||
// Schedule short callback.
|
||||
stats_state_ = kStatsShort;
|
||||
ScheduleStatsCallback(kMetricStatsShortInterval);
|
||||
break;
|
||||
default:
|
||||
LOG(FATAL) << "Invalid stats state";
|
||||
}
|
||||
}
|
||||
|
||||
void MetricsDaemon::ScheduleMeminfoCallback(int wait) {
|
||||
if (testing_) {
|
||||
return;
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#include <chromeos/daemons/dbus_daemon.h>
|
||||
#include <gtest/gtest_prod.h> // for FRIEND_TEST
|
||||
|
||||
#include "collectors/averaged_statistics_collector.h"
|
||||
#include "collectors/disk_usage_collector.h"
|
||||
#include "metrics/metrics_library.h"
|
||||
#include "persistent_integer.h"
|
||||
|
@ -65,6 +66,9 @@ class MetricsDaemon : public chromeos::DBusDaemon {
|
|||
// Triggers an upload event and exit. (Used to test UploadService)
|
||||
void RunUploaderTest();
|
||||
|
||||
// Returns the active time since boot (uptime minus sleep time) in seconds.
|
||||
static double GetActiveTime();
|
||||
|
||||
protected:
|
||||
// Used also by the unit tests.
|
||||
static const char kComprDataSizeName[];
|
||||
|
@ -79,8 +83,6 @@ class MetricsDaemon : public chromeos::DBusDaemon {
|
|||
FRIEND_TEST(MetricsDaemonTest, GetHistogramPath);
|
||||
FRIEND_TEST(MetricsDaemonTest, IsNewEpoch);
|
||||
FRIEND_TEST(MetricsDaemonTest, MessageFilter);
|
||||
FRIEND_TEST(MetricsDaemonTest, ParseDiskStats);
|
||||
FRIEND_TEST(MetricsDaemonTest, ParseVmStats);
|
||||
FRIEND_TEST(MetricsDaemonTest, ProcessKernelCrash);
|
||||
FRIEND_TEST(MetricsDaemonTest, ProcessMeminfo);
|
||||
FRIEND_TEST(MetricsDaemonTest, ProcessMeminfo2);
|
||||
|
@ -95,12 +97,6 @@ class MetricsDaemon : public chromeos::DBusDaemon {
|
|||
FRIEND_TEST(MetricsDaemonTest, SendCpuThrottleMetrics);
|
||||
FRIEND_TEST(MetricsDaemonTest, SendZramMetrics);
|
||||
|
||||
// State for disk stats collector callback.
|
||||
enum StatsState {
|
||||
kStatsShort, // short wait before short interval collection
|
||||
kStatsLong, // final wait before new collection
|
||||
};
|
||||
|
||||
// Type of scale to use for meminfo histograms. For most of them we use
|
||||
// percent of total RAM, but for some we use absolute numbers, usually in
|
||||
// megabytes, on a log scale from 0 to 4000, and 0 to 8000 for compressed
|
||||
|
@ -120,16 +116,6 @@ class MetricsDaemon : public chromeos::DBusDaemon {
|
|||
int value; // value from /proc/meminfo
|
||||
};
|
||||
|
||||
// Record for retrieving and reporting values from /proc/vmstat
|
||||
struct VmstatRecord {
|
||||
uint64_t page_faults_; // major faults
|
||||
uint64_t swap_in_; // pages swapped in
|
||||
uint64_t swap_out_; // pages swapped out
|
||||
};
|
||||
|
||||
// Returns the active time since boot (uptime minus sleep time) in seconds.
|
||||
double GetActiveTime();
|
||||
|
||||
// D-Bus filter callback.
|
||||
static DBusHandlerResult MessageFilter(DBusConnection* connection,
|
||||
DBusMessage* message,
|
||||
|
@ -189,21 +175,6 @@ class MetricsDaemon : public chromeos::DBusDaemon {
|
|||
// Initializes vm and disk stats reporting.
|
||||
void StatsReporterInit();
|
||||
|
||||
// Schedules a callback for the next vm and disk stats collection.
|
||||
void ScheduleStatsCallback(int wait);
|
||||
|
||||
// Reads cumulative disk statistics from sysfs. Returns true for success.
|
||||
bool DiskStatsReadStats(uint64_t* read_sectors, uint64_t* write_sectors);
|
||||
|
||||
// Reads cumulative vm statistics from procfs. Returns true for success.
|
||||
bool VmStatsReadStats(struct VmstatRecord* stats);
|
||||
|
||||
// Parse cumulative vm statistics from a C string. Returns true for success.
|
||||
bool VmStatsParseStats(const char* stats, struct VmstatRecord* record);
|
||||
|
||||
// Reports disk and vm statistics.
|
||||
void StatsCallback();
|
||||
|
||||
// Schedules meminfo collection callback.
|
||||
void ScheduleMeminfoCallback(int wait);
|
||||
|
||||
|
@ -286,14 +257,6 @@ class MetricsDaemon : public chromeos::DBusDaemon {
|
|||
// Selects the wait time for the next memory use callback.
|
||||
unsigned int memuse_interval_index_;
|
||||
|
||||
// Contain the most recent disk and vm cumulative stats.
|
||||
uint64_t read_sectors_;
|
||||
uint64_t write_sectors_;
|
||||
struct VmstatRecord vmstats_;
|
||||
|
||||
StatsState stats_state_;
|
||||
double stats_initial_time_;
|
||||
|
||||
// The system "HZ", or frequency of ticks. Some system data uses ticks as a
|
||||
// unit, and this is used to convert to standard time units.
|
||||
uint32_t ticks_per_second_;
|
||||
|
@ -329,8 +292,8 @@ class MetricsDaemon : public chromeos::DBusDaemon {
|
|||
scoped_ptr<PersistentInteger> unclean_shutdowns_daily_count_;
|
||||
scoped_ptr<PersistentInteger> unclean_shutdowns_weekly_count_;
|
||||
scoped_ptr<DiskUsageCollector> disk_usage_collector_;
|
||||
scoped_ptr<AveragedStatisticsCollector> averaged_stats_collector_;
|
||||
|
||||
std::string diskstats_path_;
|
||||
std::string scaling_max_freq_path_;
|
||||
std::string cpuinfo_max_freq_path_;
|
||||
|
||||
|
|
|
@ -14,17 +14,12 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <utime.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <base/at_exit.h>
|
||||
#include <base/files/file_util.h>
|
||||
#include <base/files/scoped_temp_dir.h>
|
||||
#include <base/strings/string_number_conversions.h>
|
||||
#include <base/strings/stringprintf.h>
|
||||
#include <chromeos/flag_helper.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
|
@ -34,10 +29,7 @@
|
|||
#include "persistent_integer_mock.h"
|
||||
|
||||
using base::FilePath;
|
||||
using base::StringPrintf;
|
||||
using base::Time;
|
||||
using base::TimeDelta;
|
||||
using base::TimeTicks;
|
||||
using std::string;
|
||||
using std::vector;
|
||||
using ::testing::_;
|
||||
|
@ -47,34 +39,15 @@ using ::testing::Return;
|
|||
using ::testing::StrictMock;
|
||||
using chromeos_metrics::PersistentIntegerMock;
|
||||
|
||||
static const char kFakeDiskStatsFormat[] =
|
||||
" 1793 1788 %" PRIu64 " 105580 "
|
||||
" 196 175 %" PRIu64 " 30290 "
|
||||
" 0 44060 135850\n";
|
||||
static const uint64_t kFakeReadSectors[] = {80000, 100000};
|
||||
static const uint64_t kFakeWriteSectors[] = {3000, 4000};
|
||||
|
||||
|
||||
class MetricsDaemonTest : public testing::Test {
|
||||
protected:
|
||||
std::string kFakeDiskStats0;
|
||||
std::string kFakeDiskStats1;
|
||||
|
||||
virtual void SetUp() {
|
||||
chromeos::FlagHelper::Init(0, nullptr, "");
|
||||
EXPECT_TRUE(temp_dir_.CreateUniqueTempDir());
|
||||
scaling_max_freq_path_ = temp_dir_.path().Append("scaling_max");
|
||||
cpu_max_freq_path_ = temp_dir_.path().Append("cpu_freq_max");
|
||||
disk_stats_path_ = temp_dir_.path().Append("disk_stats");
|
||||
|
||||
kFakeDiskStats0 = base::StringPrintf(kFakeDiskStatsFormat,
|
||||
kFakeReadSectors[0],
|
||||
kFakeWriteSectors[0]);
|
||||
kFakeDiskStats1 = base::StringPrintf(kFakeDiskStatsFormat,
|
||||
kFakeReadSectors[1],
|
||||
kFakeWriteSectors[1]);
|
||||
|
||||
CreateFakeDiskStatsFile(kFakeDiskStats0);
|
||||
CreateUint64ValueFile(cpu_max_freq_path_, 10000000);
|
||||
CreateUint64ValueFile(scaling_max_freq_path_, 10000000);
|
||||
|
||||
|
@ -84,7 +57,7 @@ class MetricsDaemonTest : public testing::Test {
|
|||
false,
|
||||
true,
|
||||
&metrics_lib_,
|
||||
disk_stats_path_.value(),
|
||||
"",
|
||||
scaling_max_freq_path_.value(),
|
||||
cpu_max_freq_path_.value(),
|
||||
base::TimeDelta::FromMinutes(30),
|
||||
|
@ -131,12 +104,6 @@ class MetricsDaemonTest : public testing::Test {
|
|||
dbus_message_unref(msg);
|
||||
}
|
||||
|
||||
// Creates or overwrites an input file containing fake disk stats.
|
||||
void CreateFakeDiskStatsFile(const string& fake_stats) {
|
||||
EXPECT_EQ(base::WriteFile(disk_stats_path_,
|
||||
fake_stats.data(), fake_stats.size()),
|
||||
fake_stats.size());
|
||||
}
|
||||
|
||||
// Creates or overwrites the file in |path| so that it contains the printable
|
||||
// representation of |value|.
|
||||
|
@ -156,7 +123,6 @@ class MetricsDaemonTest : public testing::Test {
|
|||
// Path for the fake files.
|
||||
base::FilePath scaling_max_freq_path_;
|
||||
base::FilePath cpu_max_freq_path_;
|
||||
base::FilePath disk_stats_path_;
|
||||
|
||||
// Mocks. They are strict mock so that all unexpected
|
||||
// calls are marked as failures.
|
||||
|
@ -200,21 +166,6 @@ TEST_F(MetricsDaemonTest, SendSample) {
|
|||
/* min */ 1, /* max */ 100, /* buckets */ 50);
|
||||
}
|
||||
|
||||
TEST_F(MetricsDaemonTest, ParseDiskStats) {
|
||||
uint64_t read_sectors_now, write_sectors_now;
|
||||
CreateFakeDiskStatsFile(kFakeDiskStats0);
|
||||
ASSERT_TRUE(daemon_.DiskStatsReadStats(&read_sectors_now,
|
||||
&write_sectors_now));
|
||||
EXPECT_EQ(read_sectors_now, kFakeReadSectors[0]);
|
||||
EXPECT_EQ(write_sectors_now, kFakeWriteSectors[0]);
|
||||
|
||||
CreateFakeDiskStatsFile(kFakeDiskStats1);
|
||||
ASSERT_TRUE(daemon_.DiskStatsReadStats(&read_sectors_now,
|
||||
&write_sectors_now));
|
||||
EXPECT_EQ(read_sectors_now, kFakeReadSectors[1]);
|
||||
EXPECT_EQ(write_sectors_now, kFakeWriteSectors[1]);
|
||||
}
|
||||
|
||||
TEST_F(MetricsDaemonTest, ProcessMeminfo) {
|
||||
string meminfo =
|
||||
"MemTotal: 2000000 kB\nMemFree: 500000 kB\n"
|
||||
|
@ -258,16 +209,6 @@ TEST_F(MetricsDaemonTest, ProcessMeminfo2) {
|
|||
EXPECT_FALSE(daemon_.ProcessMeminfo(meminfo));
|
||||
}
|
||||
|
||||
TEST_F(MetricsDaemonTest, ParseVmStats) {
|
||||
static char kVmStats[] = "pswpin 1345\npswpout 8896\n"
|
||||
"foo 100\nbar 200\npgmajfault 42\netcetc 300\n";
|
||||
struct MetricsDaemon::VmstatRecord stats;
|
||||
EXPECT_TRUE(daemon_.VmStatsParseStats(kVmStats, &stats));
|
||||
EXPECT_EQ(stats.page_faults_, 42);
|
||||
EXPECT_EQ(stats.swap_in_, 1345);
|
||||
EXPECT_EQ(stats.swap_out_, 8896);
|
||||
}
|
||||
|
||||
TEST_F(MetricsDaemonTest, ReadFreqToInt) {
|
||||
const int fake_scaled_freq = 1666999;
|
||||
const int fake_max_freq = 2000000;
|
||||
|
|
Loading…
Reference in New Issue