blob: 3d4429af32972f6581901e285549af1b954fb43f [file] [log] [blame]
/*
* Copyright 2020 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 "IoPerfCollection.h"
#include <android-base/file.h>
#include <cutils/android_filesystem_config.h>
#include <algorithm>
#include "ProcPidDir.h"
#include "UidIoStats.h"
#include "gmock/gmock.h"
namespace android {
namespace automotive {
namespace watchdog {
using android::base::WriteStringToFile;
using testing::populateProcPidDir;
namespace {
bool isEqual(const UidIoPerfData& lhs, const UidIoPerfData& rhs) {
if (lhs.topNReads.size() != rhs.topNReads.size() ||
lhs.topNWrites.size() != rhs.topNWrites.size()) {
return false;
}
for (int i = 0; i < METRIC_TYPES; ++i) {
for (int j = 0; j < UID_STATES; ++j) {
if (lhs.total[i][j] != rhs.total[i][j]) {
return false;
}
}
}
auto comp = [&](const UidIoPerfData::Stats& l, const UidIoPerfData::Stats& r) -> bool {
bool isEqual = l.userId == r.userId && l.packageName == r.packageName;
for (int i = 0; i < UID_STATES; ++i) {
isEqual &= l.bytes[i] == r.bytes[i] && l.fsync[i] == r.fsync[i];
}
return isEqual;
};
return std::equal(lhs.topNReads.begin(), lhs.topNReads.end(), rhs.topNReads.begin(), comp) &&
std::equal(lhs.topNWrites.begin(), lhs.topNWrites.end(), rhs.topNWrites.begin(), comp);
}
bool isEqual(const SystemIoPerfData& lhs, const SystemIoPerfData& rhs) {
return lhs.cpuIoWaitTime == rhs.cpuIoWaitTime && lhs.totalCpuTime == rhs.totalCpuTime &&
lhs.ioBlockedProcessesCnt == rhs.ioBlockedProcessesCnt &&
lhs.totalProcessesCnt == rhs.totalProcessesCnt;
}
bool isEqual(const ProcessIoPerfData& lhs, const ProcessIoPerfData& rhs) {
if (lhs.topNIoBlockedUids.size() != rhs.topNIoBlockedUids.size() ||
lhs.topNMajorFaults.size() != rhs.topNMajorFaults.size() ||
lhs.totalMajorFaults != rhs.totalMajorFaults ||
lhs.majorFaultsPercentChange != rhs.majorFaultsPercentChange) {
return false;
}
auto comp = [&](const ProcessIoPerfData::Stats& l, const ProcessIoPerfData::Stats& r) -> bool {
return l.userId == r.userId && l.packageName == r.packageName && l.count == r.count;
};
return std::equal(lhs.topNIoBlockedUids.begin(), lhs.topNIoBlockedUids.end(),
rhs.topNIoBlockedUids.begin(), comp) &&
std::equal(lhs.topNIoBlockedUidsTotalTaskCnt.begin(),
lhs.topNIoBlockedUidsTotalTaskCnt.end(),
rhs.topNIoBlockedUidsTotalTaskCnt.begin()) &&
std::equal(lhs.topNMajorFaults.begin(), lhs.topNMajorFaults.end(),
rhs.topNMajorFaults.begin(), comp);
}
} // namespace
TEST(IoPerfCollectionTest, TestValidUidIoStatFile) {
// Format: uid fgRdChar fgWrChar fgRdBytes fgWrBytes bgRdChar bgWrChar bgRdBytes bgWrBytes
// fgFsync bgFsync
constexpr char firstSnapshot[] =
"1001234 5000 1000 3000 500 0 0 0 0 20 0\n"
"1005678 500 100 30 50 300 400 100 200 45 60\n"
"1009 0 0 0 0 40000 50000 20000 30000 0 300\n"
"1001000 4000 3000 2000 1000 400 300 200 100 50 10\n";
struct UidIoPerfData expectedUidIoPerfData = {};
expectedUidIoPerfData.total[READ_BYTES][FOREGROUND] = 5030;
expectedUidIoPerfData.total[READ_BYTES][BACKGROUND] = 20300;
expectedUidIoPerfData.total[WRITE_BYTES][FOREGROUND] = 1550;
expectedUidIoPerfData.total[WRITE_BYTES][BACKGROUND] = 30300;
expectedUidIoPerfData.total[FSYNC_COUNT][FOREGROUND] = 115;
expectedUidIoPerfData.total[FSYNC_COUNT][BACKGROUND] = 370;
expectedUidIoPerfData.topNReads.push_back({
// uid: 1009
.userId = 0,
.packageName = "mount",
.bytes = {0, 20000},
.fsync = {0, 300},
});
expectedUidIoPerfData.topNReads.push_back({
// uid: 1001234
.userId = 10,
.packageName = "1001234",
.bytes = {3000, 0},
.fsync = {20, 0},
});
expectedUidIoPerfData.topNWrites.push_back({
// uid: 1009
.userId = 0,
.packageName = "mount",
.bytes = {0, 30000},
.fsync = {0, 300},
});
expectedUidIoPerfData.topNWrites.push_back({
// uid: 1001000
.userId = 10,
.packageName = "shared:android.uid.system",
.bytes = {1000, 100},
.fsync = {50, 10},
});
TemporaryFile tf;
ASSERT_NE(tf.fd, -1);
ASSERT_TRUE(WriteStringToFile(firstSnapshot, tf.path));
IoPerfCollection collector(tf.path);
collector.mTopNStatsPerCategory = 2;
ASSERT_TRUE(collector.mUidIoStats.enabled()) << "Temporary file is inaccessible";
struct UidIoPerfData actualUidIoPerfData = {};
auto ret = collector.collectUidIoPerfDataLocked(&actualUidIoPerfData);
ASSERT_RESULT_OK(ret);
EXPECT_TRUE(isEqual(expectedUidIoPerfData, actualUidIoPerfData))
<< "First snapshot doesn't match.\nExpected:\n"
<< toString(expectedUidIoPerfData) << "\nActual:\n"
<< toString(actualUidIoPerfData);
constexpr char secondSnapshot[] =
"1001234 10000 2000 7000 950 0 0 0 0 45 0\n"
"1005678 600 100 40 50 1000 1000 1000 600 50 70\n"
"1003456 300 500 200 300 0 0 0 0 50 0\n"
"1001000 400 300 200 100 40 30 20 10 5 1\n";
expectedUidIoPerfData = {};
expectedUidIoPerfData.total[READ_BYTES][FOREGROUND] = 4210;
expectedUidIoPerfData.total[READ_BYTES][BACKGROUND] = 900;
expectedUidIoPerfData.total[WRITE_BYTES][FOREGROUND] = 750;
expectedUidIoPerfData.total[WRITE_BYTES][BACKGROUND] = 400;
expectedUidIoPerfData.total[FSYNC_COUNT][FOREGROUND] = 80;
expectedUidIoPerfData.total[FSYNC_COUNT][BACKGROUND] = 10;
expectedUidIoPerfData.topNReads.push_back({
// uid: 1001234
.userId = 10,
.packageName = "1001234",
.bytes = {4000, 0},
.fsync = {25, 0},
});
expectedUidIoPerfData.topNReads.push_back({
// uid: 1005678
.userId = 10,
.packageName = "1005678",
.bytes = {10, 900},
.fsync = {5, 10},
});
expectedUidIoPerfData.topNWrites.push_back({
// uid: 1001234
.userId = 10,
.packageName = "1001234",
.bytes = {450, 0},
.fsync = {25, 0},
});
expectedUidIoPerfData.topNWrites.push_back({
// uid: 1005678
.userId = 10,
.packageName = "1005678",
.bytes = {0, 400},
.fsync = {5, 10},
});
ASSERT_TRUE(WriteStringToFile(secondSnapshot, tf.path));
actualUidIoPerfData = {};
ret = collector.collectUidIoPerfDataLocked(&actualUidIoPerfData);
ASSERT_RESULT_OK(ret);
EXPECT_TRUE(isEqual(expectedUidIoPerfData, actualUidIoPerfData))
<< "Second snapshot doesn't match.\nExpected:\n"
<< toString(expectedUidIoPerfData) << "\nActual:\n"
<< toString(actualUidIoPerfData);
}
TEST(IoPerfCollectionTest, TestUidIOStatsLessThanTopNStatsLimit) {
// Format: uid fgRdChar fgWrChar fgRdBytes fgWrBytes bgRdChar bgWrChar bgRdBytes bgWrBytes
// fgFsync bgFsync
constexpr char contents[] = "1001234 5000 1000 3000 500 0 0 0 0 20 0\n";
struct UidIoPerfData expectedUidIoPerfData = {};
expectedUidIoPerfData.total[READ_BYTES][FOREGROUND] = 3000;
expectedUidIoPerfData.total[READ_BYTES][BACKGROUND] = 0;
expectedUidIoPerfData.total[WRITE_BYTES][FOREGROUND] = 500;
expectedUidIoPerfData.total[WRITE_BYTES][BACKGROUND] = 0;
expectedUidIoPerfData.total[FSYNC_COUNT][FOREGROUND] = 20;
expectedUidIoPerfData.total[FSYNC_COUNT][BACKGROUND] = 0;
expectedUidIoPerfData.topNReads.push_back({
// uid: 1001234
.userId = 10,
.packageName = "1001234",
.bytes = {3000, 0},
.fsync = {20, 0},
});
expectedUidIoPerfData.topNWrites.push_back({
// uid: 1001234
.userId = 10,
.packageName = "1001234",
.bytes = {500, 0},
.fsync = {20, 0},
});
TemporaryFile tf;
ASSERT_NE(tf.fd, -1);
ASSERT_TRUE(WriteStringToFile(contents, tf.path));
IoPerfCollection collector(tf.path);
collector.mTopNStatsPerCategory = 10;
ASSERT_TRUE(collector.mUidIoStats.enabled()) << "Temporary file is inaccessible";
struct UidIoPerfData actualUidIoPerfData = {};
const auto& ret = collector.collectUidIoPerfDataLocked(&actualUidIoPerfData);
ASSERT_RESULT_OK(ret);
EXPECT_TRUE(isEqual(expectedUidIoPerfData, actualUidIoPerfData))
<< "Collected data doesn't match.\nExpected:\n"
<< toString(expectedUidIoPerfData) << "\nActual:\n"
<< toString(actualUidIoPerfData);
}
TEST(IoPerfCollectionTest, TestProcUidIoStatsContentsFromDevice) {
// TODO(b/148486340): Enable the test after appropriate SELinux privileges are available to
// read the proc file.
/*IoPerfCollection collector;
ASSERT_TRUE(collector.mUidIoStats.enabled()) << "/proc/uid_io/stats file is inaccessible";
struct UidIoPerfData perfData = {};
const auto& ret = collector.collectUidIoPerfDataLocked(&perfData);
ASSERT_RESULT_OK(ret);
// The below check should pass because the /proc/uid_io/stats file should have at least
// |mTopNStatsPerCategory| entries since bootup.
EXPECT_EQ(perfData.topNReads.size(), collector.mTopNStatsPerCategory);
EXPECT_EQ(perfData.topNWrites.size(), collector.mTopNStatsPerCategory);
int numMappedAppUid = 0;
int numMappedSysUid = 0;
for (const auto& it : collector.mUidToPackageNameMapping) {
if (it.first >= AID_APP_START) {
++numMappedAppUid;
} else {
++numMappedSysUid;
}
}
EXPECT_GT(numMappedAppUid, 0);
EXPECT_GT(numMappedSysUid, 0);*/
}
TEST(IoPerfCollectionTest, TestValidProcStatFile) {
constexpr char firstSnapshot[] =
"cpu 6200 5700 1700 3100 1100 5200 3900 0 0 0\n"
"cpu0 2400 2900 600 690 340 4300 2100 0 0 0\n"
"cpu1 1900 2380 510 760 51 370 1500 0 0 0\n"
"cpu2 900 400 400 1000 600 400 160 0 0 0\n"
"cpu3 1000 20 190 650 109 130 140 0 0 0\n"
"intr 694351583 0 0 0 297062868 0 5922464 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 "
"0 0\n"
// Skipped most of the intr line as it is not important for testing the ProcStat parsing
// logic.
"ctxt 579020168\n"
"btime 1579718450\n"
"processes 113804\n"
"procs_running 17\n"
"procs_blocked 5\n"
"softirq 33275060 934664 11958403 5111 516325 200333 0 341482 10651335 0 8667407\n";
struct SystemIoPerfData expectedSystemIoPerfData = {
.cpuIoWaitTime = 1100,
.totalCpuTime = 26900,
.ioBlockedProcessesCnt = 5,
.totalProcessesCnt = 22,
};
TemporaryFile tf;
ASSERT_NE(tf.fd, -1);
ASSERT_TRUE(WriteStringToFile(firstSnapshot, tf.path));
IoPerfCollection collector("", tf.path);
ASSERT_TRUE(collector.mProcStat.enabled()) << "Temporary file is inaccessible";
struct SystemIoPerfData actualSystemIoPerfData = {};
auto ret = collector.collectSystemIoPerfDataLocked(&actualSystemIoPerfData);
ASSERT_RESULT_OK(ret);
EXPECT_TRUE(isEqual(expectedSystemIoPerfData, actualSystemIoPerfData))
<< "First snapshot doesn't match.\nExpected:\n"
<< toString(expectedSystemIoPerfData) << "\nActual:\n"
<< toString(actualSystemIoPerfData);
constexpr char secondSnapshot[] =
"cpu 16200 8700 2000 4100 2200 6200 5900 0 0 0\n"
"cpu0 4400 3400 700 890 800 4500 3100 0 0 0\n"
"cpu1 5900 3380 610 960 100 670 2000 0 0 0\n"
"cpu2 2900 1000 450 1400 800 600 460 0 0 0\n"
"cpu3 3000 920 240 850 500 430 340 0 0 0\n"
"intr 694351583 0 0 0 297062868 0 5922464 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 "
"0 0\n"
"ctxt 579020168\n"
"btime 1579718450\n"
"processes 113804\n"
"procs_running 10\n"
"procs_blocked 2\n"
"softirq 33275060 934664 11958403 5111 516325 200333 0 341482 10651335 0 8667407\n";
expectedSystemIoPerfData = {
.cpuIoWaitTime = 1100,
.totalCpuTime = 18400,
.ioBlockedProcessesCnt = 2,
.totalProcessesCnt = 12,
};
ASSERT_TRUE(WriteStringToFile(secondSnapshot, tf.path));
actualSystemIoPerfData = {};
ret = collector.collectSystemIoPerfDataLocked(&actualSystemIoPerfData);
ASSERT_RESULT_OK(ret);
EXPECT_TRUE(isEqual(expectedSystemIoPerfData, actualSystemIoPerfData))
<< "Second snapshot doesn't match.\nExpected:\n"
<< toString(expectedSystemIoPerfData) << "\nActual:\n"
<< toString(actualSystemIoPerfData);
}
TEST(IoPerfCollectionTest, TestValidProcPidContents) {
std::unordered_map<uint32_t, std::vector<uint32_t>> pidToTids = {
{1, {1, 453}},
{2546, {2546, 3456, 4789}},
{7890, {7890, 8978, 12890}},
{18902, {18902, 21345, 32452}},
{28900, {28900}},
};
std::unordered_map<uint32_t, std::string> perProcessStat = {
{1, "1 (init) S 0 0 0 0 0 0 0 0 220 0 0 0 0 0 0 0 2 0 0\n"},
{2546, "2546 (system_server) R 1 0 0 0 0 0 0 0 6000 0 0 0 0 0 0 0 3 0 1000\n"},
{7890, "7890 (logd) D 1 0 0 0 0 0 0 0 15000 0 0 0 0 0 0 0 3 0 2345\n"},
{18902, "18902 (disk I/O) D 1 0 0 0 0 0 0 0 45678 0 0 0 0 0 0 0 3 0 897654\n"},
{28900, "28900 (tombstoned) D 1 0 0 0 0 0 0 0 89765 0 0 0 0 0 0 0 3 0 2345671\n"},
};
std::unordered_map<uint32_t, std::string> perProcessStatus = {
{1, "Pid:\t1\nTgid:\t1\nUid:\t0\t0\t0\t0\n"},
{2546, "Pid:\t2546\nTgid:\t2546\nUid:\t1001000\t1001000\t1001000\t1001000\n"},
{7890, "Pid:\t7890\nTgid:\t7890\nUid:\t1001000\t1001000\t1001000\t1001000\n"},
{18902, "Pid:\t18902\nTgid:\t18902\nUid:\t1009\t1009\t1009\t1009\n"},
{28900, "Pid:\t28900\nTgid:\t28900\nUid:\t1001234\t1001234\t1001234\t1001234\n"},
};
std::unordered_map<uint32_t, std::string> perThreadStat = {
{1, "1 (init) S 0 0 0 0 0 0 0 0 200 0 0 0 0 0 0 0 2 0 0\n"},
{453, "453 (init) S 0 0 0 0 0 0 0 0 20 0 0 0 0 0 0 0 2 0 275\n"},
{2546, "2546 (system_server) R 1 0 0 0 0 0 0 0 1000 0 0 0 0 0 0 0 3 0 1000\n"},
{3456, "3456 (system_server) S 1 0 0 0 0 0 0 0 3000 0 0 0 0 0 0 0 3 0 2300\n"},
{4789, "4789 (system_server) D 1 0 0 0 0 0 0 0 2000 0 0 0 0 0 0 0 3 0 4500\n"},
{7890, "7890 (logd) D 1 0 0 0 0 0 0 0 10000 0 0 0 0 0 0 0 3 0 2345\n"},
{8978, "8978 (logd) D 1 0 0 0 0 0 0 0 1000 0 0 0 0 0 0 0 3 0 2500\n"},
{12890, "12890 (logd) D 1 0 0 0 0 0 0 0 500 0 0 0 0 0 0 0 3 0 2900\n"},
{18902, "18902 (disk I/O) D 1 0 0 0 0 0 0 0 30000 0 0 0 0 0 0 0 3 0 897654\n"},
{21345, "21345 (disk I/O) D 1 0 0 0 0 0 0 0 15000 0 0 0 0 0 0 0 3 0 904000\n"},
{32452, "32452 (disk I/O) D 1 0 0 0 0 0 0 0 678 0 0 0 0 0 0 0 3 0 1007000\n"},
{28900, "28900 (tombstoned) D 1 0 0 0 0 0 0 0 89765 0 0 0 0 0 0 0 3 0 2345671\n"},
};
struct ProcessIoPerfData expectedProcessIoPerfData = {};
expectedProcessIoPerfData.topNIoBlockedUids.push_back({
// uid: 1001000
.userId = 10,
.packageName = "shared:android.uid.system",
.count = 4,
});
expectedProcessIoPerfData.topNIoBlockedUidsTotalTaskCnt.push_back(6);
expectedProcessIoPerfData.topNIoBlockedUids.push_back({
// uid: 1009
.userId = 0,
.packageName = "mount",
.count = 3,
});
expectedProcessIoPerfData.topNIoBlockedUidsTotalTaskCnt.push_back(3);
expectedProcessIoPerfData.topNMajorFaults.push_back({
// uid: 1001234
.userId = 10,
.packageName = "1001234",
.count = 89765,
});
expectedProcessIoPerfData.topNMajorFaults.push_back({
// uid: 1009
.userId = 0,
.packageName = "mount",
.count = 45678,
});
expectedProcessIoPerfData.totalMajorFaults = 156663;
expectedProcessIoPerfData.majorFaultsPercentChange = 0;
TemporaryDir firstSnapshot;
auto ret = populateProcPidDir(firstSnapshot.path, pidToTids, perProcessStat, perProcessStatus,
perThreadStat);
ASSERT_TRUE(ret) << "Failed to populate proc pid dir: " << ret.error();
IoPerfCollection collector("", "", firstSnapshot.path);
collector.mTopNStatsPerCategory = 2;
ASSERT_TRUE(collector.mProcPidStat.enabled())
<< "Files under the temporary proc directory are inaccessible";
struct ProcessIoPerfData actualProcessIoPerfData = {};
ret = collector.collectProcessIoPerfDataLocked(&actualProcessIoPerfData);
ASSERT_TRUE(ret) << "Failed to collect first snapshot: " << ret.error();
EXPECT_TRUE(isEqual(expectedProcessIoPerfData, actualProcessIoPerfData))
<< "First snapshot doesn't match.\nExpected:\n"
<< toString(expectedProcessIoPerfData) << "\nActual:\n"
<< toString(actualProcessIoPerfData);
pidToTids = {
{1, {1, 453}},
{2546, {2546, 3456, 4789}},
};
perProcessStat = {
{1, "1 (init) S 0 0 0 0 0 0 0 0 880 0 0 0 0 0 0 0 2 0 0\n"},
{2546, "2546 (system_server) R 1 0 0 0 0 0 0 0 18000 0 0 0 0 0 0 0 3 0 1000\n"},
};
perProcessStatus = {
{1, "Pid:\t1\nTgid:\t1\nUid:\t0\t0\t0\t0\n"},
{2546, "Pid:\t2546\nTgid:\t2546\nUid:\t1001000\t1001000\t1001000\t1001000\n"},
};
perThreadStat = {
{1, "1 (init) S 0 0 0 0 0 0 0 0 800 0 0 0 0 0 0 0 2 0 0\n"},
{453, "453 (init) S 0 0 0 0 0 0 0 0 80 0 0 0 0 0 0 0 2 0 275\n"},
{2546, "2546 (system_server) R 1 0 0 0 0 0 0 0 3000 0 0 0 0 0 0 0 3 0 1000\n"},
{3456, "3456 (system_server) S 1 0 0 0 0 0 0 0 9000 0 0 0 0 0 0 0 3 0 2300\n"},
{4789, "4789 (system_server) D 1 0 0 0 0 0 0 0 6000 0 0 0 0 0 0 0 3 0 4500\n"},
};
expectedProcessIoPerfData = {};
expectedProcessIoPerfData.topNIoBlockedUids.push_back({
// uid: 1001000
.userId = 10,
.packageName = "shared:android.uid.system",
.count = 1,
});
expectedProcessIoPerfData.topNIoBlockedUidsTotalTaskCnt.push_back(3);
expectedProcessIoPerfData.topNMajorFaults.push_back({
// uid: 1001000
.userId = 10,
.packageName = "shared:android.uid.system",
.count = 12000,
});
expectedProcessIoPerfData.topNMajorFaults.push_back({
// uid: 0
.userId = 0,
.packageName = "root",
.count = 660,
});
expectedProcessIoPerfData.totalMajorFaults = 12660;
expectedProcessIoPerfData.majorFaultsPercentChange = ((12660.0 - 156663.0) / 156663.0) * 100;
TemporaryDir secondSnapshot;
ret = populateProcPidDir(secondSnapshot.path, pidToTids, perProcessStat, perProcessStatus,
perThreadStat);
ASSERT_TRUE(ret) << "Failed to populate proc pid dir: " << ret.error();
collector.mProcPidStat.mPath = secondSnapshot.path;
actualProcessIoPerfData = {};
ret = collector.collectProcessIoPerfDataLocked(&actualProcessIoPerfData);
ASSERT_TRUE(ret) << "Failed to collect second snapshot: " << ret.error();
EXPECT_TRUE(isEqual(expectedProcessIoPerfData, actualProcessIoPerfData))
<< "Second snapshot doesn't match.\nExpected:\n"
<< toString(expectedProcessIoPerfData) << "\nActual:\n"
<< toString(actualProcessIoPerfData);
}
TEST(IoPerfCollectionTest, TestProcPidContentsLessThanTopNStatsLimit) {
std::unordered_map<uint32_t, std::vector<uint32_t>> pidToTids = {
{1, {1, 453}},
};
std::unordered_map<uint32_t, std::string> perProcessStat = {
{1, "1 (init) S 0 0 0 0 0 0 0 0 880 0 0 0 0 0 0 0 2 0 0\n"},
};
std::unordered_map<uint32_t, std::string> perProcessStatus = {
{1, "Pid:\t1\nTgid:\t1\nUid:\t0\t0\t0\t0\n"},
};
std::unordered_map<uint32_t, std::string> perThreadStat = {
{1, "1 (init) S 0 0 0 0 0 0 0 0 800 0 0 0 0 0 0 0 2 0 0\n"},
{453, "453 (init) S 0 0 0 0 0 0 0 0 80 0 0 0 0 0 0 0 2 0 275\n"},
};
struct ProcessIoPerfData expectedProcessIoPerfData = {};
expectedProcessIoPerfData.topNMajorFaults.push_back({
// uid: 0
.userId = 0,
.packageName = "root",
.count = 880,
});
expectedProcessIoPerfData.totalMajorFaults = 880;
expectedProcessIoPerfData.majorFaultsPercentChange = 0.0;
TemporaryDir prodDir;
auto ret = populateProcPidDir(prodDir.path, pidToTids, perProcessStat, perProcessStatus,
perThreadStat);
ASSERT_TRUE(ret) << "Failed to populate proc pid dir: " << ret.error();
IoPerfCollection collector("", "", prodDir.path);
struct ProcessIoPerfData actualProcessIoPerfData = {};
ret = collector.collectProcessIoPerfDataLocked(&actualProcessIoPerfData);
ASSERT_TRUE(ret) << "Failed to collect proc pid contents: " << ret.error();
EXPECT_TRUE(isEqual(expectedProcessIoPerfData, actualProcessIoPerfData))
<< "proc pid contents don't match.\nExpected:\n"
<< toString(expectedProcessIoPerfData) << "\nActual:\n"
<< toString(actualProcessIoPerfData);
}
} // namespace watchdog
} // namespace automotive
} // namespace android