Add additional stats for swapping from /proc/vmstat
This adds swap-in and swap-out rate calculation on the same intervals as major page fault collection. BUG=chromium:261965 TEST=observe Platform.SwapIn{Long,Short} and Platform.SwapOut{Long,Short} appear in histograms Change-Id: Ifcdba1088cdff355b8e132145ac79635b185663b Reviewed-on: https://gerrit.chromium.org/gerrit/64162 Reviewed-by: Luigi Semenzato <semenzato@chromium.org> Commit-Queue: Sonny Rao <sonnyrao@chromium.org> Tested-by: Sonny Rao <sonnyrao@chromium.org>
This commit is contained in:
parent
6e55c11537
commit
4b8aebb0f4
|
@ -13,6 +13,7 @@
|
|||
#include <base/logging.h>
|
||||
#include <base/string_number_conversions.h>
|
||||
#include <base/string_util.h>
|
||||
#include <base/string_split.h>
|
||||
#include <base/stringprintf.h>
|
||||
#include <chromeos/dbus/service_constants.h>
|
||||
#include <dbus/dbus-glib-lowlevel.h>
|
||||
|
@ -129,6 +130,18 @@ const char MetricsDaemon::kMetricPageFaultsLongName[] =
|
|||
const char MetricsDaemon::kMetricPageFaultsShortName[] =
|
||||
"Platform.PageFaultsShort";
|
||||
|
||||
// Swap in and Swap out
|
||||
|
||||
const char MetricsDaemon::kMetricSwapInLongName[] =
|
||||
"Platform.SwapInLong";
|
||||
const char MetricsDaemon::kMetricSwapInShortName[] =
|
||||
"Platform.SwapInShort";
|
||||
|
||||
const char MetricsDaemon::kMetricSwapOutLongName[] =
|
||||
"Platform.SwapOutLong";
|
||||
const char MetricsDaemon::kMetricSwapOutShortName[] =
|
||||
"Platform.SwapOutShort";
|
||||
|
||||
// Thermal CPU throttling.
|
||||
|
||||
const char MetricsDaemon::kMetricScaledCpuFrequencyName[] =
|
||||
|
@ -171,7 +184,7 @@ MetricsDaemon::MetricsDaemon()
|
|||
memuse_interval_index_(0),
|
||||
read_sectors_(0),
|
||||
write_sectors_(0),
|
||||
page_faults_(0),
|
||||
vmstats_(),
|
||||
stats_state_(kStatsShort),
|
||||
stats_initial_time_(0) {}
|
||||
|
||||
|
@ -586,7 +599,7 @@ void MetricsDaemon::UnscheduleUseMonitor() {
|
|||
|
||||
void MetricsDaemon::StatsReporterInit() {
|
||||
DiskStatsReadStats(&read_sectors_, &write_sectors_);
|
||||
VmStatsReadStats(&page_faults_);
|
||||
VmStatsReadStats(&vmstats_);
|
||||
// The first time around just run the long stat, so we don't delay boot.
|
||||
stats_state_ = kStatsLong;
|
||||
stats_initial_time_ = GetActiveTime();
|
||||
|
@ -639,55 +652,66 @@ bool MetricsDaemon::DiskStatsReadStats(long int* read_sectors,
|
|||
return success;
|
||||
}
|
||||
|
||||
bool MetricsDaemon::VmStatsParseStats(char* stats, long int* page_faults) {
|
||||
static const char kPageFaultSearchString[] = "\npgmajfault ";
|
||||
bool success = false;
|
||||
bool MetricsDaemon::VmStatsParseStats(const char* stats,
|
||||
struct VmstatRecord* record) {
|
||||
// a mapping of string name to field in VmstatRecord and whether we found it
|
||||
struct mapping {
|
||||
const string name;
|
||||
uint64_t* value_p;
|
||||
bool found;
|
||||
} map[] =
|
||||
{ { .name = "pgmajfault",
|
||||
.value_p = &record->page_faults_,
|
||||
.found = false },
|
||||
{ .name = "pswpin",
|
||||
.value_p = &record->swap_in_,
|
||||
.found = false },
|
||||
{ .name = "pswpout",
|
||||
.value_p = &record->swap_out_,
|
||||
.found = false }, };
|
||||
|
||||
// Each line in the file has the form
|
||||
// <ID> <VALUE>
|
||||
// for instance:
|
||||
// nr_free_pages 213427
|
||||
char* s = strstr(stats, kPageFaultSearchString);
|
||||
if (s == NULL) {
|
||||
LOG(WARNING) << "cannot find page fault entry in vmstats";
|
||||
} else {
|
||||
char* endp;
|
||||
// Skip <ID> and space. Don't count the terminating null.
|
||||
s += sizeof(kPageFaultSearchString) - 1;
|
||||
*page_faults = strtol(s, &endp, 10);
|
||||
if (*endp == '\n') {
|
||||
success = true;
|
||||
vector<string> lines;
|
||||
Tokenize(stats, "\n", &lines);
|
||||
for (vector<string>::iterator it = lines.begin();
|
||||
it != lines.end(); ++it) {
|
||||
vector<string> tokens;
|
||||
base::SplitString(*it, ' ', &tokens);
|
||||
if (tokens.size() == 2) {
|
||||
for (unsigned int i = 0; i < sizeof(map)/sizeof(struct mapping); i++) {
|
||||
if (!tokens[0].compare(map[i].name)) {
|
||||
if (!base::StringToUint64(tokens[1], map[i].value_p))
|
||||
return false;
|
||||
map[i].found = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
LOG(WARNING) << "error parsing vmstats";
|
||||
LOG(WARNING) << "unexpected vmstat format";
|
||||
}
|
||||
}
|
||||
return success;
|
||||
// make sure we got all the stats
|
||||
for (unsigned i = 0; i < sizeof(map)/sizeof(struct mapping); i++) {
|
||||
if (map[i].found == false) {
|
||||
LOG(WARNING) << "vmstat missing " << map[i].name;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MetricsDaemon::VmStatsReadStats(long int* page_faults) {
|
||||
char buffer[4000];
|
||||
int nchars;
|
||||
int success = false;
|
||||
if (testing_) {
|
||||
bool MetricsDaemon::VmStatsReadStats(struct VmstatRecord* stats) {
|
||||
string value_string;
|
||||
FilePath* path = new FilePath(vmstats_path_);
|
||||
if (!file_util::ReadFileToString(*path, &value_string)) {
|
||||
delete path;
|
||||
LOG(WARNING) << "cannot read " << vmstats_path_;
|
||||
return false;
|
||||
}
|
||||
int file = HANDLE_EINTR(open(vmstats_path_.c_str(), O_RDONLY));
|
||||
if (file < 0) {
|
||||
PLOG(WARNING) << "cannot open " << vmstats_path_;
|
||||
return false;
|
||||
}
|
||||
nchars = HANDLE_EINTR(read(file, buffer, sizeof(buffer) - 1));
|
||||
LOG_IF(WARNING, nchars == sizeof(buffer) - 1)
|
||||
<< "file too large in " << vmstats_path_;
|
||||
if (nchars < 0) {
|
||||
PLOG(WARNING) << "cannot read from " << vmstats_path_;
|
||||
} else if (nchars == 0) {
|
||||
LOG(WARNING) << vmstats_path_ << " is empty";
|
||||
} else {
|
||||
buffer[nchars] = '\0';
|
||||
success = VmStatsParseStats(buffer, page_faults);
|
||||
}
|
||||
HANDLE_EINTR(close(file));
|
||||
return success;
|
||||
delete path;
|
||||
return VmStatsParseStats(value_string.c_str(), stats);
|
||||
}
|
||||
|
||||
bool MetricsDaemon::ReadFreqToInt(const string& sysfs_file_name, int* value) {
|
||||
|
@ -755,7 +779,8 @@ gboolean MetricsDaemon::StatsCallbackStatic(void* handle) {
|
|||
// Collects disk and vm stats alternating over a short and a long interval.
|
||||
|
||||
void MetricsDaemon::StatsCallback() {
|
||||
long int read_sectors_now, write_sectors_now, page_faults_now;
|
||||
long int read_sectors_now, write_sectors_now;
|
||||
struct VmstatRecord vmstats_now;
|
||||
double time_now = GetActiveTime();
|
||||
double delta_time = time_now - stats_initial_time_;
|
||||
if (testing_) {
|
||||
|
@ -769,9 +794,13 @@ void MetricsDaemon::StatsCallback() {
|
|||
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(&page_faults_now);
|
||||
int delta_faults = page_faults_now - page_faults_;
|
||||
int page_faults_per_second = delta_faults / 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:
|
||||
|
@ -793,6 +822,16 @@ void MetricsDaemon::StatsCallback() {
|
|||
1,
|
||||
kMetricPageFaultsMax,
|
||||
kMetricPageFaultsBuckets);
|
||||
SendMetric(kMetricSwapInShortName,
|
||||
swap_in_per_second,
|
||||
1,
|
||||
kMetricPageFaultsMax,
|
||||
kMetricPageFaultsBuckets);
|
||||
SendMetric(kMetricSwapOutShortName,
|
||||
swap_out_per_second,
|
||||
1,
|
||||
kMetricPageFaultsMax,
|
||||
kMetricPageFaultsBuckets);
|
||||
}
|
||||
// Schedule long callback.
|
||||
stats_state_ = kStatsLong;
|
||||
|
@ -821,7 +860,18 @@ void MetricsDaemon::StatsCallback() {
|
|||
1,
|
||||
kMetricPageFaultsMax,
|
||||
kMetricPageFaultsBuckets);
|
||||
page_faults_ = page_faults_now;
|
||||
SendMetric(kMetricSwapInLongName,
|
||||
swap_in_per_second,
|
||||
1,
|
||||
kMetricPageFaultsMax,
|
||||
kMetricPageFaultsBuckets);
|
||||
SendMetric(kMetricSwapOutLongName,
|
||||
swap_out_per_second,
|
||||
1,
|
||||
kMetricPageFaultsMax,
|
||||
kMetricPageFaultsBuckets);
|
||||
|
||||
vmstats_ = vmstats_now;
|
||||
}
|
||||
SendCpuThrottleMetrics();
|
||||
// Set start time for new cycle.
|
||||
|
|
|
@ -120,6 +120,13 @@ class MetricsDaemon {
|
|||
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
|
||||
};
|
||||
|
||||
typedef std::map<std::string, chromeos_metrics::FrequencyCounter*>
|
||||
FrequencyCounters;
|
||||
|
||||
|
@ -152,6 +159,10 @@ class MetricsDaemon {
|
|||
static const char kMetricWriteSectorsShortName[];
|
||||
static const char kMetricPageFaultsShortName[];
|
||||
static const char kMetricPageFaultsLongName[];
|
||||
static const char kMetricSwapInLongName[];
|
||||
static const char kMetricSwapInShortName[];
|
||||
static const char kMetricSwapOutLongName[];
|
||||
static const char kMetricSwapOutShortName[];
|
||||
static const char kMetricScaledCpuFrequencyName[];
|
||||
static const int kMetricStatsShortInterval;
|
||||
static const int kMetricStatsLongInterval;
|
||||
|
@ -284,10 +295,10 @@ class MetricsDaemon {
|
|||
bool DiskStatsReadStats(long int* read_sectors, long int* write_sectors);
|
||||
|
||||
// Reads cumulative vm statistics from procfs. Returns true for success.
|
||||
bool VmStatsReadStats(long int* page_faults);
|
||||
bool VmStatsReadStats(struct VmstatRecord* stats);
|
||||
|
||||
// Parse cumulative vm statistics from a C string. Returns true for success.
|
||||
bool VmStatsParseStats(char* stats, long int* page_faults);
|
||||
bool VmStatsParseStats(const char* stats, struct VmstatRecord* record);
|
||||
|
||||
// Reports disk and vm statistics (static version for glib). Arguments are a
|
||||
// glib artifact.
|
||||
|
@ -403,7 +414,7 @@ class MetricsDaemon {
|
|||
// Contain the most recent disk and vm cumulative stats.
|
||||
long int read_sectors_;
|
||||
long int write_sectors_;
|
||||
long int page_faults_;
|
||||
struct VmstatRecord vmstats_;
|
||||
|
||||
StatsState stats_state_;
|
||||
double stats_initial_time_;
|
||||
|
|
|
@ -661,10 +661,13 @@ MemFree: 1000000 kB\n\
|
|||
}
|
||||
|
||||
TEST_F(MetricsDaemonTest, ParseVmStats) {
|
||||
static char kVmStats[] = "foo 100\nbar 200\npgmajfault 42\netcetc 300\n";
|
||||
long int page_faults = 0;
|
||||
EXPECT_TRUE(daemon_.VmStatsParseStats(kVmStats, &page_faults));
|
||||
EXPECT_EQ(page_faults, 42);
|
||||
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) {
|
||||
|
|
Loading…
Reference in New Issue