blob: 50b0c971571461a4112de6275a05dc23ad5bdbc9 [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you 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 "common/atomic.h"
#include "testutil/gtest-util.h"
#include "util/disk-info.h"
#include "util/system-state-info.h"
#include "util/time.h"
#include <thread>
namespace impala {
class SystemStateInfoTest : public testing::Test {
protected:
SystemStateInfo info;
};
TEST_F(SystemStateInfoTest, FirstCallReturnsZero) {
const SystemStateInfo::CpuUsageRatios& r = info.GetCpuUsageRatios();
EXPECT_EQ(0, r.user + r.system + r.iowait);
const SystemStateInfo::NetworkUsage& n = info.GetNetworkUsage();
EXPECT_EQ(0, n.rx_rate + n.tx_rate);
}
// Smoke test to make sure that we read non-zero values from /proc/stat.
TEST_F(SystemStateInfoTest, ReadProcStat) {
info.ReadCurrentProcStat();
const SystemStateInfo::CpuValues& state = info.cpu_values_[info.cpu_val_idx_];
EXPECT_GT(state[SystemStateInfo::CPU_USER], 0);
EXPECT_GT(state[SystemStateInfo::CPU_SYSTEM], 0);
EXPECT_GT(state[SystemStateInfo::CPU_IDLE], 0);
EXPECT_GT(state[SystemStateInfo::CPU_IOWAIT], 0);
}
// Smoke test to make sure that we read non-zero values from /proc/net/dev.
TEST_F(SystemStateInfoTest, ReadProcNetDev) {
info.ReadCurrentProcNetDev();
const SystemStateInfo::NetworkValues& state = info.network_values_[info.net_val_idx_];
// IMPALA-8413: Don't assume that counters are non-zero as that is sometimes not the
// case, e.g. in virtualized environments.
EXPECT_GE(state[SystemStateInfo::NET_RX_BYTES], 0);
EXPECT_GE(state[SystemStateInfo::NET_RX_PACKETS], 0);
EXPECT_GE(state[SystemStateInfo::NET_TX_BYTES], 0);
EXPECT_GE(state[SystemStateInfo::NET_TX_PACKETS], 0);
}
// Smoke test to make sure that we read non-zero values from /proc/diskstats.
TEST_F(SystemStateInfoTest, ReadProcDiskStats) {
info.ReadCurrentProcDiskStats();
const SystemStateInfo::DiskValues& state = info.disk_values_[info.disk_val_idx_];
EXPECT_GT(state[SystemStateInfo::DISK_READS_COMPLETED], 0);
EXPECT_GT(state[SystemStateInfo::DISK_SECTORS_READ], 0);
EXPECT_GT(state[SystemStateInfo::DISK_WRITES_COMPLETED], 0);
EXPECT_GT(state[SystemStateInfo::DISK_SECTORS_WRITTEN], 0);
}
// Tests parsing a line similar to the first line of /proc/stat.
TEST_F(SystemStateInfoTest, ParseProcStat) {
// Fields are: user nice system idle iowait irq softirq steal guest guest_nice
info.ReadProcStatString("cpu 20 30 10 70 100 0 0 0 0 0");
const SystemStateInfo::CpuValues& state = info.cpu_values_[info.cpu_val_idx_];
EXPECT_EQ(state[SystemStateInfo::CPU_USER], 20);
EXPECT_EQ(state[SystemStateInfo::CPU_SYSTEM], 10);
EXPECT_EQ(state[SystemStateInfo::CPU_IDLE], 70);
EXPECT_EQ(state[SystemStateInfo::CPU_IOWAIT], 100);
// Test that values larger than INT_MAX parse without error.
info.ReadProcStatString("cpu 3000000000 30 10 70 100 0 0 0 0 0");
const SystemStateInfo::CpuValues& changed_state = info.cpu_values_[info.cpu_val_idx_];
EXPECT_EQ(changed_state[SystemStateInfo::CPU_USER], 3000000000);
}
// Tests parsing a string similar to the contents of /proc/net/dev.
TEST_F(SystemStateInfoTest, ParseProcNetDevString) {
// Fields are: user nice system idle iowait irq softirq steal guest guest_nice
string dev_net = R"(Inter-| Receive | Transmit
face |bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop fifo colls carrier compressed
lo: 12178099975318 24167151765 0 0 0 0 0 0 12178099975318 24167151765 0 0 0 0 0 0
br-c4d9b4cafca2: 1025814 409 0 0 0 0 0 0 70616330 1638104 0 0 0 0 0 0
eth0: 366039609986 388580561 0 2 0 0 0 62231368 95492744135 174535524 0 0 0 0 0 0)";
info.ReadProcNetDevString(dev_net);
const SystemStateInfo::NetworkValues& state = info.network_values_[info.net_val_idx_];
EXPECT_EQ(state[SystemStateInfo::NET_RX_BYTES], 366040635800);
EXPECT_EQ(state[SystemStateInfo::NET_RX_PACKETS], 388580970);
EXPECT_EQ(state[SystemStateInfo::NET_TX_BYTES], 95563360465);
EXPECT_EQ(state[SystemStateInfo::NET_TX_PACKETS], 176173628);
}
// Tests parsing a string similar to the contents of /proc/net/dev on older kernels, such
// as the one on Centos6.
TEST_F(SystemStateInfoTest, ParseProcNetDevStringCentos6) {
// Fields are: user nice system idle iowait irq softirq steal guest guest_nice
string dev_net = R"(Inter-| Receive | Transmit
face |bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop fifo colls carrier compressed
lo: 5829 53 0 0 0 0 0 0 5829 53 0 0 0 0 0 0
eth0:285025090 212208 0 0 0 0 0 0 9137793 84770 0 0 0 0 0 0)";
info.ReadProcNetDevString(dev_net);
const SystemStateInfo::NetworkValues& state = info.network_values_[info.net_val_idx_];
EXPECT_EQ(state[SystemStateInfo::NET_RX_BYTES], 285025090);
EXPECT_EQ(state[SystemStateInfo::NET_RX_PACKETS], 212208);
EXPECT_EQ(state[SystemStateInfo::NET_TX_BYTES], 9137793);
EXPECT_EQ(state[SystemStateInfo::NET_TX_PACKETS], 84770);
}
// Tests parsing a string similar to the contents of /proc/diskstats. This test also makes
// sure that values of a partition are not accounted towards the disk it is on.
TEST_F(SystemStateInfoTest, ParseProcDiskStatsString) {
// We cannot hard-code the device name since our Jenkins workers might see different
// device names in their virtual environments.
string disk_stats =
R"( 8 0 $0 17124835 222797 716029974 8414920 43758807 38432095 7867287712 630179264 0 32547524 638999340
8 1 $01 17124482 222797 716027002 8414892 43546943 38432095 7867287712 629089180 0 31590972 637917344)";
string device = DiskInfo::device_name(0);
cout << Substitute(disk_stats, device);
info.ReadProcDiskStatsString(Substitute(disk_stats, device));
const SystemStateInfo::DiskValues& state = info.disk_values_[info.disk_val_idx_];
EXPECT_EQ(state[SystemStateInfo::DISK_SECTORS_READ], 716029974);
EXPECT_EQ(state[SystemStateInfo::DISK_READS_COMPLETED], 17124835);
EXPECT_EQ(state[SystemStateInfo::DISK_SECTORS_WRITTEN], 7867287712);
EXPECT_EQ(state[SystemStateInfo::DISK_WRITES_COMPLETED], 43758807);
}
// Tests that computing CPU ratios doesn't overflow
TEST_F(SystemStateInfoTest, ComputeCpuRatiosIntOverflow) {
// Load old and new values for CPU counters. These values are from a system where we
// have seen this code overflow before.
info.ReadProcStatString("cpu 100952877 534 18552749 6318822633 4119619 0 0 0 0 0");
info.ReadProcStatString("cpu 100953598 534 18552882 6318826150 4119619 0 0 0 0 0");
info.ComputeCpuRatios();
const SystemStateInfo::CpuUsageRatios& r = info.GetCpuUsageRatios();
EXPECT_EQ(r.user, 1649);
EXPECT_EQ(r.system, 304);
EXPECT_EQ(r.iowait, 0);
}
// Smoke test for the public interface.
TEST_F(SystemStateInfoTest, GetCpuUsageRatios) {
AtomicBool running(true);
// Generate some load to observe counters > 0.
std::thread t([&running]() { while (running.Load()); });
for (int i = 0; i < 3; ++i) {
SleepForMs(200);
info.CaptureSystemStateSnapshot();
const SystemStateInfo::CpuUsageRatios& r = info.GetCpuUsageRatios();
EXPECT_GT(r.user + r.system + r.iowait, 0);
}
running.Store(false);
t.join();
}
// Tests the computation logic for CPU ratios.
TEST_F(SystemStateInfoTest, ComputeCpuRatios) {
info.ReadProcStatString("cpu 20 30 10 70 100 0 0 0 0 0");
info.ReadProcStatString("cpu 30 30 20 70 100 0 0 0 0 0");
info.ComputeCpuRatios();
const SystemStateInfo::CpuUsageRatios& r = info.GetCpuUsageRatios();
EXPECT_EQ(r.user, 5000);
EXPECT_EQ(r.system, 5000);
EXPECT_EQ(r.iowait, 0);
}
// Tests the computation logic for network usage.
TEST_F(SystemStateInfoTest, ComputeNetworkUsage) {
// Two sets of values in the format of /proc/net/dev
string dev_net_1 = R"(Inter-| Receive | Transmit
face |bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop fifo colls carrier compressed
lo: 1000 2000 0 0 0 0 0 0 3000 4000 0 0 0 0 0 0
br-c4d9b4cafca2: 5000 409 0 0 0 0 0 0 6000 7000 0 0 0 0 0 0
eth0: 8000 9000 0 2 0 0 0 10000 11000 12000 0 0 0 0 0 0)";
string dev_net_2 = R"(Inter-| Receive | Transmit
face |bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop fifo colls carrier compressed
lo: 2000 4000 0 0 0 0 0 0 6000 8000 0 0 0 0 0 0
br-c4d9b4cafca2: 7000 609 0 0 0 0 0 0 12000 14000 0 0 0 0 0 0
eth0: 10000 11000 0 2 0 0 0 10000 11000 12000 0 0 0 0 0 0)";
info.ReadProcNetDevString(dev_net_1);
info.ReadProcNetDevString(dev_net_2);
int period_ms = 500;
info.ComputeNetworkUsage(period_ms);
const SystemStateInfo::NetworkUsage& n = info.GetNetworkUsage();
EXPECT_EQ(n.rx_rate, 8000);
EXPECT_EQ(n.tx_rate, 12000);
}
// Tests the computation logic for disk statistics.
TEST_F(SystemStateInfoTest, ComputeDiskStats) {
// Two sets of values in the format of /proc/diskstats. We cannot hard-code the device
// name since our Jenkins workers might see different device names in their virtual
// environments.
string disk_stats_1 = R"( 1 15 ram15 0 0 0 0 0 0 0 0 0 0 0
7 0 loop0 0 0 0 0 0 0 0 0 0 0 0
8 0 $0 0 0 1000 0 0 0 2000 0 0 0 0
8 1 $01 0 0 1000 0 0 0 2000 0 0 0 0)";
string disk_stats_2 = R"( 8 0 $0 0 0 2000 0 0 0 4000 0 0 0 0
8 1 $01 0 0 2000 0 0 0 4000 0 0 0 0)";
string device = DiskInfo::device_name(0);
info.ReadProcDiskStatsString(Substitute(disk_stats_1, device));
info.ReadProcDiskStatsString(Substitute(disk_stats_2, device));
int period_ms = 500;
info.ComputeDiskStats(period_ms);
const SystemStateInfo::DiskStats& ds = info.GetDiskStats();
EXPECT_EQ(ds.read_rate, 2 * 1000 * SystemStateInfo::BYTES_PER_SECTOR);
EXPECT_EQ(ds.write_rate, 2 * 2000 * SystemStateInfo::BYTES_PER_SECTOR);
}
} // namespace impala