This repository has been archived by the owner on Nov 8, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
metricsd: Only collect metrics over a short period.
Instead of reporting the metrics over both a long and a short period, collect and report only over a short period. This makes the code simpler and the metrics easier to understand. Also move the collection out of metrics_daemon and into a separate collector to make it simpler to understand. BUG: 24464945 Change-Id: I17e52536aaa75321a5e34f42ed488545c2c3efde
- Loading branch information
Showing
7 changed files
with
412 additions
and
347 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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_ |
Oops, something went wrong.