Skip to content
This repository has been archived by the owner on Nov 8, 2023. It is now read-only.

Commit

Permalink
Merge "metricsd: Only collect metrics over a short period."
Browse files Browse the repository at this point in the history
  • Loading branch information
bsimonnet authored and Gerrit Code Review committed Oct 2, 2015
2 parents c6c6ab5 + 7a96405 commit 66255db
Show file tree
Hide file tree
Showing 7 changed files with 412 additions and 347 deletions.
4 changes: 4 additions & 0 deletions metricsd/Android.mk
Original file line number Diff line number Diff line change
Expand Up @@ -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 \
Expand All @@ -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 \
Expand Down Expand Up @@ -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
Expand Down
216 changes: 216 additions & 0 deletions metricsd/collectors/averaged_statistics_collector.cc
Original file line number Diff line number Diff line change
@@ -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);
}
}
79 changes: 79 additions & 0 deletions metricsd/collectors/averaged_statistics_collector.h
Original file line number Diff line number Diff line change
@@ -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_
Loading

0 comments on commit 66255db

Please sign in to comment.