Commit 669a0933 authored by nduca@chromium.org's avatar nduca@chromium.org

Add the DiskInfo struct and associated functions to collect and hold system disk metrics.

BUG=236763
TEST=base_unitttests SystemMetricsTest.*
R=darin@chromium.org, nduca@chromium.org

Review URL: https://codereview.chromium.org/22824008

Patch from Json Mak <jwmak@chromium.org>.

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@220684 0039d316-1c4b-4281-b951-d872f2087c98
parent 8b549e11
......@@ -565,6 +565,7 @@
'process/memory_unittest.cc',
'process/memory_unittest_mac.h',
'process/memory_unittest_mac.mm',
'process/process_metrics_unittests.cc',
'process/process_util_unittest.cc',
'process/process_util_unittest_ios.cc',
'profiler/tracked_time_unittest.cc',
......
......@@ -12,6 +12,7 @@
#include "base/base_export.h"
#include "base/basictypes.h"
#include "base/gtest_prod_util.h"
#include "base/process/process_handle.h"
#include "base/time/time.h"
......@@ -261,6 +262,32 @@ struct BASE_EXPORT SystemMemoryInfoKB {
// Fills in the provided |meminfo| structure. Returns true on success.
// Exposed for memory debugging widget.
BASE_EXPORT bool GetSystemMemoryInfo(SystemMemoryInfoKB* meminfo);
// Data from /proc/diskstats about system-wide disk I/O.
struct BASE_EXPORT SystemDiskInfo {
SystemDiskInfo();
uint64 reads;
uint64 reads_merged;
uint64 sectors_read;
uint64 read_time;
uint64 writes;
uint64 writes_merged;
uint64 sectors_written;
uint64 write_time;
uint64 io;
uint64 io_time;
uint64 weighted_io_time;
};
// Checks whether the candidate string is a valid disk name, [sh]d[a-z]+
// for a generic disk or mmcblk[0-9]+ for the MMC case.
// Names of disk partitions (e.g. sda1) are not valid.
BASE_EXPORT bool IsValidDiskName(const std::string& candidate);
// Retrieves data from /proc/diskstats about system-wide disk I/O.
// Fills in the provided |diskinfo| structure. Returns true on success.
BASE_EXPORT bool GetSystemDiskInfo(SystemDiskInfo* diskinfo);
#endif // defined(OS_LINUX) || defined(OS_ANDROID)
#if defined(OS_CHROMEOS)
......@@ -296,6 +323,8 @@ class SystemMetrics {
static SystemMetrics Sample();
private:
FRIEND_TEST_ALL_PREFIXES(SystemMetricsTest, SystemMetrics);
size_t committed_memory_;
#if defined(OS_LINUX) || defined(OS_ANDROID)
SystemMemoryInfoKB memory_info_;
......
......@@ -421,6 +421,57 @@ const size_t kMemInactiveAnonIndex = 25;
const size_t kMemActiveFileIndex = 28;
const size_t kMemInactiveFileIndex = 31;
// The format of /proc/diskstats is:
// Device major number
// Device minor number
// Device name
// Field 1 -- # of reads completed
// This is the total number of reads completed successfully.
// Field 2 -- # of reads merged, field 6 -- # of writes merged
// Reads and writes which are adjacent to each other may be merged for
// efficiency. Thus two 4K reads may become one 8K read before it is
// ultimately handed to the disk, and so it will be counted (and queued)
// as only one I/O. This field lets you know how often this was done.
// Field 3 -- # of sectors read
// This is the total number of sectors read successfully.
// Field 4 -- # of milliseconds spent reading
// This is the total number of milliseconds spent by all reads (as
// measured from __make_request() to end_that_request_last()).
// Field 5 -- # of writes completed
// This is the total number of writes completed successfully.
// Field 6 -- # of writes merged
// See the description of field 2.
// Field 7 -- # of sectors written
// This is the total number of sectors written successfully.
// Field 8 -- # of milliseconds spent writing
// This is the total number of milliseconds spent by all writes (as
// measured from __make_request() to end_that_request_last()).
// Field 9 -- # of I/Os currently in progress
// The only field that should go to zero. Incremented as requests are
// given to appropriate struct request_queue and decremented as they
// finish.
// Field 10 -- # of milliseconds spent doing I/Os
// This field increases so long as field 9 is nonzero.
// Field 11 -- weighted # of milliseconds spent doing I/Os
// This field is incremented at each I/O start, I/O completion, I/O
// merge, or read of these stats by the number of I/Os in progress
// (field 9) times the number of milliseconds spent doing I/O since the
// last update of this field. This can provide an easy measure of both
// I/O completion time and the backlog that may be accumulating.
const size_t kDiskDriveName = 2;
const size_t kDiskReads = 3;
const size_t kDiskReadsMerged = 4;
const size_t kDiskSectorsRead = 5;
const size_t kDiskReadTime = 6;
const size_t kDiskWrites = 7;
const size_t kDiskWritesMerged = 8;
const size_t kDiskSectorsWritten = 9;
const size_t kDiskWriteTime = 10;
const size_t kDiskIO = 11;
const size_t kDiskIOTime = 12;
const size_t kDiskWeightedIOTime = 13;
} // namespace
SystemMemoryInfoKB::SystemMemoryInfoKB()
......@@ -527,6 +578,124 @@ bool GetSystemMemoryInfo(SystemMemoryInfoKB* meminfo) {
return true;
}
SystemDiskInfo::SystemDiskInfo() {
reads = 0;
reads_merged = 0;
sectors_read = 0;
read_time = 0;
writes = 0;
writes_merged = 0;
sectors_written = 0;
write_time = 0;
io = 0;
io_time = 0;
weighted_io_time = 0;
}
bool IsValidDiskName(const std::string& candidate) {
if (candidate.length() < 3)
return false;
if (candidate.substr(0,2) == "sd" || candidate.substr(0,2) == "hd") {
// [sh]d[a-z]+ case
for (size_t i = 2; i < candidate.length(); i++) {
if (!islower(candidate[i]))
return false;
}
} else {
if (candidate.length() < 7) {
return false;
}
if (candidate.substr(0,6) == "mmcblk") {
// mmcblk[0-9]+ case
for (size_t i = 6; i < candidate.length(); i++) {
if (!isdigit(candidate[i]))
return false;
}
} else {
return false;
}
}
return true;
}
bool GetSystemDiskInfo(SystemDiskInfo* diskinfo) {
// Synchronously reading files in /proc is safe.
ThreadRestrictions::ScopedAllowIO allow_io;
FilePath diskinfo_file("/proc/diskstats");
std::string diskinfo_data;
if (!ReadFileToString(diskinfo_file, &diskinfo_data)) {
DLOG(WARNING) << "Failed to open " << diskinfo_file.value();
return false;
}
std::vector<std::string> diskinfo_lines;
size_t line_count = Tokenize(diskinfo_data, "\n", &diskinfo_lines);
if (line_count == 0) {
DLOG(WARNING) << "No lines found";
return false;
}
diskinfo->reads = 0;
diskinfo->reads_merged = 0;
diskinfo->sectors_read = 0;
diskinfo->read_time = 0;
diskinfo->writes = 0;
diskinfo->writes_merged = 0;
diskinfo->sectors_written = 0;
diskinfo->write_time = 0;
diskinfo->io = 0;
diskinfo->io_time = 0;
diskinfo->weighted_io_time = 0;
uint64 reads = 0;
uint64 reads_merged = 0;
uint64 sectors_read = 0;
uint64 read_time = 0;
uint64 writes = 0;
uint64 writes_merged = 0;
uint64 sectors_written = 0;
uint64 write_time = 0;
uint64 io = 0;
uint64 io_time = 0;
uint64 weighted_io_time = 0;
for (size_t i = 0; i < line_count; i++) {
std::vector<std::string> disk_fields;
SplitStringAlongWhitespace(diskinfo_lines[i], &disk_fields);
// Fields may have overflowed and reset to zero.
if (IsValidDiskName(disk_fields[kDiskDriveName])) {
StringToUint64(disk_fields[kDiskReads], &reads);
StringToUint64(disk_fields[kDiskReadsMerged], &reads_merged);
StringToUint64(disk_fields[kDiskSectorsRead], &sectors_read);
StringToUint64(disk_fields[kDiskReadTime], &read_time);
StringToUint64(disk_fields[kDiskWrites], &writes);
StringToUint64(disk_fields[kDiskWritesMerged], &writes_merged);
StringToUint64(disk_fields[kDiskSectorsWritten], &sectors_written);
StringToUint64(disk_fields[kDiskWriteTime], &write_time);
StringToUint64(disk_fields[kDiskIO], &io);
StringToUint64(disk_fields[kDiskIOTime], &io_time);
StringToUint64(disk_fields[kDiskWeightedIOTime], &weighted_io_time);
diskinfo->reads += reads;
diskinfo->reads_merged += reads_merged;
diskinfo->sectors_read += sectors_read;
diskinfo->read_time += read_time;
diskinfo->writes += writes;
diskinfo->writes_merged += writes_merged;
diskinfo->sectors_written += sectors_written;
diskinfo->write_time += write_time;
diskinfo->io += io;
diskinfo->io_time += io_time;
diskinfo->weighted_io_time += weighted_io_time;
}
}
return true;
}
#if defined(OS_CHROMEOS)
void GetSwapInfo(SwapInfo* swap_info) {
// Synchronously reading files in /sys/block/zram0 is safe.
......
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/process/process_metrics.h"
#include <sstream>
#include <string>
#include "testing/gtest/include/gtest/gtest.h"
namespace base {
namespace debug {
// Tests for SystemMetrics.
// Exists as a class so it can be a friend of SystemMetrics.
class SystemMetricsTest : public testing::Test {
public:
SystemMetricsTest() {}
private:
DISALLOW_COPY_AND_ASSIGN(SystemMetricsTest);
};
/////////////////////////////////////////////////////////////////////////////
#if defined(OS_LINUX) || defined(OS_ANDROID)
TEST_F(SystemMetricsTest, IsValidDiskName) {
std::string invalid_input1 = "";
std::string invalid_input2 = "s";
std::string invalid_input3 = "sdz+";
std::string invalid_input4 = "hda0";
std::string invalid_input5 = "mmcbl";
std::string invalid_input6 = "mmcblka";
std::string invalid_input7 = "mmcblkb";
std::string invalid_input8 = "mmmblk0";
EXPECT_FALSE(IsValidDiskName(invalid_input1));
EXPECT_FALSE(IsValidDiskName(invalid_input2));
EXPECT_FALSE(IsValidDiskName(invalid_input3));
EXPECT_FALSE(IsValidDiskName(invalid_input4));
EXPECT_FALSE(IsValidDiskName(invalid_input5));
EXPECT_FALSE(IsValidDiskName(invalid_input6));
EXPECT_FALSE(IsValidDiskName(invalid_input7));
EXPECT_FALSE(IsValidDiskName(invalid_input8));
std::string valid_input1 = "sda";
std::string valid_input2 = "sdaaaa";
std::string valid_input3 = "hdz";
std::string valid_input4 = "mmcblk0";
std::string valid_input5 = "mmcblk999";
EXPECT_TRUE(IsValidDiskName(valid_input1));
EXPECT_TRUE(IsValidDiskName(valid_input2));
EXPECT_TRUE(IsValidDiskName(valid_input3));
EXPECT_TRUE(IsValidDiskName(valid_input4));
EXPECT_TRUE(IsValidDiskName(valid_input5));
}
#endif // defined(OS_LINUX) || defined(OS_ANDROID)
} // namespace debug
} // namespace base
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment