Commit 838f843b authored by Chung-Sheng Wu's avatar Chung-Sheng Wu Committed by Commit Bot

[CrOS]Fix GetSwapInfo to accommodate kernel zram api change.

ZRam has changed its sysfs interfaces since kernel version 3.18.
(https://github.com/torvalds/linux/commit/c87d1655c29500b459fb135258a93f8309ada9c7)
If file "/sys/block/zram0/mm_stat" exists, use the new interfaces,
otherwise, use the old one.

Bug=chromium:740438

Change-Id: Ief192baeb76824e06c78744a1a5010b3d8d682fa
Reviewed-on: https://chromium-review.googlesource.com/566796Reviewed-by: default avatarLei Zhang <thestig@chromium.org>
Commit-Queue: Zhong-sheng Wu <chungsheng@google.com>
Cr-Commit-Position: refs/heads/master@{#488578}
parent eef04de4
...@@ -480,9 +480,22 @@ struct BASE_EXPORT SwapInfo { ...@@ -480,9 +480,22 @@ struct BASE_EXPORT SwapInfo {
uint64_t mem_used_total; uint64_t mem_used_total;
}; };
// Parses a string containing the contents of /sys/block/zram0/mm_stat.
// This should be used for the new ZRAM sysfs interfaces.
// Returns true on success or false for a parsing error.
// Exposed for testing.
BASE_EXPORT bool ParseZramMmStat(StringPiece mm_stat_data, SwapInfo* swap_info);
// Parses a string containing the contents of /sys/block/zram0/stat
// This should be used for the new ZRAM sysfs interfaces.
// Returns true on success or false for a parsing error.
// Exposed for testing.
BASE_EXPORT bool ParseZramStat(StringPiece stat_data, SwapInfo* swap_info);
// In ChromeOS, reads files from /sys/block/zram0 that contain ZRAM usage data. // In ChromeOS, reads files from /sys/block/zram0 that contain ZRAM usage data.
// Fills in the provided |swap_data| structure. // Fills in the provided |swap_data| structure.
BASE_EXPORT void GetSwapInfo(SwapInfo* swap_info); // Returns true on success or false for a parsing error.
BASE_EXPORT bool GetSwapInfo(SwapInfo* swap_info);
#endif // defined(OS_CHROMEOS) #endif // defined(OS_CHROMEOS)
// Collects and holds performance metrics for system memory and disk. // Collects and holds performance metrics for system memory and disk.
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
#include "base/files/file_util.h" #include "base/files/file_util.h"
#include "base/logging.h" #include "base/logging.h"
#include "base/memory/ptr_util.h" #include "base/memory/ptr_util.h"
#include "base/optional.h"
#include "base/process/internal_linux.h" #include "base/process/internal_linux.h"
#include "base/strings/string_number_conversions.h" #include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h" #include "base/strings/string_split.h"
...@@ -940,13 +941,63 @@ std::unique_ptr<Value> SwapInfo::ToValue() const { ...@@ -940,13 +941,63 @@ std::unique_ptr<Value> SwapInfo::ToValue() const {
return std::move(res); return std::move(res);
} }
void GetSwapInfo(SwapInfo* swap_info) { bool ParseZramMmStat(StringPiece mm_stat_data, SwapInfo* swap_info) {
// Synchronously reading files in /sys/block/zram0 does not hit the disk. // There are 7 columns in /sys/block/zram0/mm_stat,
ThreadRestrictions::ScopedAllowIO allow_io; // split by several spaces. The first three columns
// are orig_data_size, compr_data_size and mem_used_total.
// Example:
// 17715200 5008166 566062 0 1225715712 127 183842
//
// For more details:
// https://www.kernel.org/doc/Documentation/blockdev/zram.txt
std::vector<StringPiece> tokens = SplitStringPiece(
mm_stat_data, kWhitespaceASCII, TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY);
if (tokens.size() < 7) {
DLOG(WARNING) << "zram mm_stat: tokens: " << tokens.size()
<< " malformed line: " << mm_stat_data.as_string();
return false;
}
FilePath zram_path("/sys/block/zram0"); if (!StringToUint64(tokens[0], &swap_info->orig_data_size))
uint64_t orig_data_size = return false;
ReadFileToUint64(zram_path.Append("orig_data_size")); if (!StringToUint64(tokens[1], &swap_info->compr_data_size))
return false;
if (!StringToUint64(tokens[2], &swap_info->mem_used_total))
return false;
return true;
}
bool ParseZramStat(StringPiece stat_data, SwapInfo* swap_info) {
// There are 11 columns in /sys/block/zram0/stat,
// split by several spaces. The first column is read I/Os
// and fifth column is write I/Os.
// Example:
// 299 0 2392 0 1 0 8 0 0 0 0
//
// For more details:
// https://www.kernel.org/doc/Documentation/blockdev/zram.txt
std::vector<StringPiece> tokens = SplitStringPiece(
stat_data, kWhitespaceASCII, TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY);
if (tokens.size() < 11) {
DLOG(WARNING) << "zram stat: tokens: " << tokens.size()
<< " malformed line: " << stat_data.as_string();
return false;
}
if (!StringToUint64(tokens[0], &swap_info->num_reads))
return false;
if (!StringToUint64(tokens[4], &swap_info->num_writes))
return false;
return true;
}
namespace {
bool IgnoreZramFirstPage(uint64_t orig_data_size, SwapInfo* swap_info) {
if (orig_data_size <= 4096) { if (orig_data_size <= 4096) {
// A single page is compressed at startup, and has a high compression // A single page is compressed at startup, and has a high compression
// ratio. Ignore this as it doesn't indicate any real swapping. // ratio. Ignore this as it doesn't indicate any real swapping.
...@@ -955,8 +1006,18 @@ void GetSwapInfo(SwapInfo* swap_info) { ...@@ -955,8 +1006,18 @@ void GetSwapInfo(SwapInfo* swap_info) {
swap_info->num_writes = 0; swap_info->num_writes = 0;
swap_info->compr_data_size = 0; swap_info->compr_data_size = 0;
swap_info->mem_used_total = 0; swap_info->mem_used_total = 0;
return; return true;
} }
return false;
}
void ParseZramPath(SwapInfo* swap_info) {
FilePath zram_path("/sys/block/zram0");
uint64_t orig_data_size =
ReadFileToUint64(zram_path.Append("orig_data_size"));
if (IgnoreZramFirstPage(orig_data_size, swap_info))
return;
swap_info->orig_data_size = orig_data_size; swap_info->orig_data_size = orig_data_size;
swap_info->num_reads = ReadFileToUint64(zram_path.Append("num_reads")); swap_info->num_reads = ReadFileToUint64(zram_path.Append("num_reads"));
swap_info->num_writes = ReadFileToUint64(zram_path.Append("num_writes")); swap_info->num_writes = ReadFileToUint64(zram_path.Append("num_writes"));
...@@ -965,6 +1026,60 @@ void GetSwapInfo(SwapInfo* swap_info) { ...@@ -965,6 +1026,60 @@ void GetSwapInfo(SwapInfo* swap_info) {
swap_info->mem_used_total = swap_info->mem_used_total =
ReadFileToUint64(zram_path.Append("mem_used_total")); ReadFileToUint64(zram_path.Append("mem_used_total"));
} }
bool GetSwapInfoImpl(SwapInfo* swap_info) {
// Synchronously reading files in /sys/block/zram0 does not hit the disk.
ThreadRestrictions::ScopedAllowIO allow_io;
// Since ZRAM update, it shows the usage data in different places.
// If file "/sys/block/zram0/mm_stat" exists, use the new way, otherwise,
// use the old way.
static Optional<bool> use_new_zram_interface;
FilePath zram_mm_stat_file("/sys/block/zram0/mm_stat");
if (!use_new_zram_interface.has_value()) {
use_new_zram_interface = PathExists(zram_mm_stat_file);
}
if (!use_new_zram_interface) {
ParseZramPath(swap_info);
return true;
}
std::string mm_stat_data;
if (!ReadFileToString(zram_mm_stat_file, &mm_stat_data)) {
DLOG(WARNING) << "Failed to open " << zram_mm_stat_file.value();
return false;
}
if (!ParseZramMmStat(mm_stat_data, swap_info)) {
DLOG(WARNING) << "Failed to parse " << zram_mm_stat_file.value();
return false;
}
if (IgnoreZramFirstPage(swap_info->orig_data_size, swap_info))
return true;
FilePath zram_stat_file("/sys/block/zram0/stat");
std::string stat_data;
if (!ReadFileToString(zram_stat_file, &stat_data)) {
DLOG(WARNING) << "Failed to open " << zram_stat_file.value();
return false;
}
if (!ParseZramStat(stat_data, swap_info)) {
DLOG(WARNING) << "Failed to parse " << zram_stat_file.value();
return false;
}
return true;
}
} // namespace
bool GetSwapInfo(SwapInfo* swap_info) {
if (!GetSwapInfoImpl(swap_info)) {
*swap_info = SwapInfo();
return false;
}
return true;
}
#endif // defined(OS_CHROMEOS) #endif // defined(OS_CHROMEOS)
#if defined(OS_LINUX) || defined(OS_AIX) #if defined(OS_LINUX) || defined(OS_AIX)
......
...@@ -402,6 +402,43 @@ TEST_F(SystemMetricsTest, TestNoNegativeCpuUsage) { ...@@ -402,6 +402,43 @@ TEST_F(SystemMetricsTest, TestNoNegativeCpuUsage) {
#endif // defined(OS_LINUX) || defined(OS_CHROMEOS) #endif // defined(OS_LINUX) || defined(OS_CHROMEOS)
#if defined(OS_CHROMEOS)
TEST_F(SystemMetricsTest, ParseZramMmStat) {
SwapInfo swapinfo;
const char invalid_input1[] = "aaa";
const char invalid_input2[] = "1 2 3 4 5 6";
const char invalid_input3[] = "a 2 3 4 5 6 7";
EXPECT_FALSE(ParseZramMmStat(invalid_input1, &swapinfo));
EXPECT_FALSE(ParseZramMmStat(invalid_input2, &swapinfo));
EXPECT_FALSE(ParseZramMmStat(invalid_input3, &swapinfo));
const char valid_input1[] =
"17715200 5008166 566062 0 1225715712 127 183842";
EXPECT_TRUE(ParseZramMmStat(valid_input1, &swapinfo));
EXPECT_EQ(17715200ULL, swapinfo.orig_data_size);
EXPECT_EQ(5008166ULL, swapinfo.compr_data_size);
EXPECT_EQ(566062ULL, swapinfo.mem_used_total);
}
TEST_F(SystemMetricsTest, ParseZramStat) {
SwapInfo swapinfo;
const char invalid_input1[] = "aaa";
const char invalid_input2[] = "1 2 3 4 5 6 7 8 9 10";
const char invalid_input3[] = "a 2 3 4 5 6 7 8 9 10 11";
EXPECT_FALSE(ParseZramStat(invalid_input1, &swapinfo));
EXPECT_FALSE(ParseZramStat(invalid_input2, &swapinfo));
EXPECT_FALSE(ParseZramStat(invalid_input3, &swapinfo));
const char valid_input1[] =
"299 0 2392 0 1 0 8 0 0 0 0";
EXPECT_TRUE(ParseZramStat(valid_input1, &swapinfo));
EXPECT_EQ(299ULL, swapinfo.num_reads);
EXPECT_EQ(1ULL, swapinfo.num_writes);
}
#endif // defined(OS_CHROMEOS)
#if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX) || \ #if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX) || \
defined(OS_ANDROID) defined(OS_ANDROID)
TEST(SystemMetrics2Test, GetSystemMemoryInfo) { TEST(SystemMetrics2Test, GetSystemMemoryInfo) {
......
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