| // Copyright 2019 The Fuchsia 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 "src/storage/lib/storage-metrics/storage-metrics.h" |
| |
| #include <fuchsia/hardware/block/driver/cpp/banjo.h> |
| |
| #include <algorithm> |
| |
| #include <zxtest/zxtest.h> |
| |
| #include "src/storage/lib/storage-metrics/block-metrics.h" |
| |
| namespace storage_metrics { |
| namespace { |
| |
| // Compare CallStat fields with the corresponding fields in |
| // CallStatFidl structure |
| void ExpectCallStatMatchFidlStat(CallStat& cs, CallStatFidl& cs_fidl) { |
| EXPECT_EQ(cs.minimum_latency(true), cs_fidl.success.minimum_latency); |
| EXPECT_EQ(cs.maximum_latency(true), cs_fidl.success.maximum_latency); |
| EXPECT_EQ(cs.total_time_spent(true), cs_fidl.success.total_time_spent); |
| EXPECT_EQ(cs.total_calls(true), cs_fidl.success.total_calls); |
| EXPECT_EQ(cs.bytes_transferred(true), cs_fidl.success.bytes_transferred); |
| |
| EXPECT_EQ(cs.minimum_latency(false), cs_fidl.failure.minimum_latency); |
| EXPECT_EQ(cs.maximum_latency(false), cs_fidl.failure.maximum_latency); |
| EXPECT_EQ(cs.total_time_spent(false), cs_fidl.failure.total_time_spent); |
| EXPECT_EQ(cs.total_calls(false), cs_fidl.failure.total_calls); |
| EXPECT_EQ(cs.bytes_transferred(false), cs_fidl.failure.bytes_transferred); |
| |
| EXPECT_EQ(cs.minimum_latency(), |
| std::min(cs_fidl.success.minimum_latency, cs_fidl.failure.minimum_latency)); |
| EXPECT_EQ(cs.maximum_latency(), |
| std::max(cs_fidl.success.maximum_latency, cs_fidl.failure.maximum_latency)); |
| EXPECT_EQ(cs.total_time_spent(), |
| (cs_fidl.success.total_time_spent + cs_fidl.failure.total_time_spent)); |
| EXPECT_EQ(cs.total_calls(), (cs_fidl.success.total_calls + cs_fidl.failure.total_calls)); |
| EXPECT_EQ(cs.bytes_transferred(), |
| (cs_fidl.success.bytes_transferred + cs_fidl.failure.bytes_transferred)); |
| |
| CallStatFidl tmp; |
| cs.CopyToFidl(&tmp); |
| } |
| |
| // Deep campares two CallStatRawFidl structures. Can be |
| void ExpectFidlCallStatRawMatch(const CallStatRawFidl& lhs, const CallStatRawFidl& rhs) { |
| EXPECT_EQ(lhs.total_calls, rhs.total_calls); |
| EXPECT_EQ(lhs.total_time_spent, rhs.total_time_spent); |
| EXPECT_EQ(lhs.minimum_latency, rhs.minimum_latency); |
| EXPECT_EQ(lhs.maximum_latency, rhs.maximum_latency); |
| EXPECT_EQ(lhs.bytes_transferred, rhs.bytes_transferred); |
| } |
| |
| // Compares two CallStatFidl structures |
| void ExpectMetricsMatchCallStat(const CallStatFidl& lhs, const CallStatFidl& rhs) { |
| ExpectFidlCallStatRawMatch(lhs.success, rhs.success); |
| ExpectFidlCallStatRawMatch(lhs.failure, rhs.failure); |
| } |
| |
| // Updates all private CallStat fields of |metrics| |
| void UpdateAllBlockDeviceMetricsRaw(storage_metrics::BlockDeviceMetrics& metrics, bool success, |
| zx_ticks_t delta, uint64_t bytes_transferred) { |
| metrics.UpdateReadStat(success, delta, bytes_transferred); |
| metrics.UpdateWriteStat(success, delta, bytes_transferred); |
| metrics.UpdateTrimStat(success, delta, bytes_transferred); |
| metrics.UpdateFlushStat(success, delta, bytes_transferred); |
| } |
| |
| // Updates both success and failure stats with (|minimum_latency|, |bytes_transferred1|) |
| // (|maximum_latency|, |bytes_transferred2|) respectively. |
| void BlockDeviceMetricsUpdate(storage_metrics::BlockDeviceMetrics& metrics, |
| zx_ticks_t minimum_latency, zx_ticks_t maximum_latency, |
| uint64_t bytes_transferred1, uint64_t bytes_transferred2) { |
| // Update successful minimum and maximum latencies |
| UpdateAllBlockDeviceMetricsRaw(metrics, true, minimum_latency, bytes_transferred1); |
| UpdateAllBlockDeviceMetricsRaw(metrics, true, maximum_latency, bytes_transferred2); |
| UpdateAllBlockDeviceMetricsRaw(metrics, false, minimum_latency, bytes_transferred1); |
| UpdateAllBlockDeviceMetricsRaw(metrics, false, maximum_latency, bytes_transferred2); |
| } |
| |
| // Compares all CallStatFidl fields within |
| // |fidl_block_device_metrics|, with |fidl_call_stat| |
| void CompareFidlBlockDeviceStatAll( |
| const fuchsia_hardware_block::wire::BlockStats& fidl_block_device_metrics, |
| const CallStatFidl& fidl_call_stat) { |
| ExpectMetricsMatchCallStat(fidl_block_device_metrics.read, fidl_call_stat); |
| ExpectMetricsMatchCallStat(fidl_block_device_metrics.write, fidl_call_stat); |
| ExpectMetricsMatchCallStat(fidl_block_device_metrics.flush, fidl_call_stat); |
| ExpectMetricsMatchCallStat(fidl_block_device_metrics.trim, fidl_call_stat); |
| } |
| |
| // Expects if |fidl_fs_metrics| is properly initialized. |
| void ExpectBlockDeviceMetricsInitialState( |
| const fuchsia_hardware_block::wire::BlockStats& fidl_block_device_metrics) { |
| CallStatFidl fidl_call_stat = {}; |
| fidl_call_stat.success.minimum_latency = storage_metrics::kUninitializedMinimumLatency; |
| fidl_call_stat.failure.minimum_latency = storage_metrics::kUninitializedMinimumLatency; |
| CompareFidlBlockDeviceStatAll(fidl_block_device_metrics, fidl_call_stat); |
| } |
| |
| TEST(RawCallStatEqual, Same) { |
| CallStatRawFidl a = {}, b = {}; |
| ASSERT_TRUE(RawCallStatEqual(a, b)); |
| } |
| |
| TEST(RawCallStatEqual, LargerTotalCalls) { |
| CallStatRawFidl a = {}, b = {}; |
| a.total_calls++; |
| ASSERT_FALSE(RawCallStatEqual(a, b)); |
| } |
| |
| TEST(RawCallStatEqual, LargerBytesTransferred) { |
| CallStatRawFidl a = {}, b = {}; |
| a.bytes_transferred++; |
| ASSERT_FALSE(RawCallStatEqual(a, b)); |
| } |
| |
| TEST(CallStatEqual, Same) { |
| CallStatFidl a = {}, b = {}; |
| ASSERT_TRUE(CallStatEqual(a, b)); |
| } |
| |
| TEST(CallStatEqual, LargerTotalCalls) { |
| CallStatFidl a = {}, b = {}; |
| a.success.total_calls++; |
| ASSERT_FALSE(CallStatEqual(a, b)); |
| } |
| |
| TEST(CallStatEqual, LargerBytesTransferred) { |
| CallStatFidl a = {}, b = {}; |
| a.failure.bytes_transferred++; |
| ASSERT_FALSE(CallStatEqual(a, b)); |
| } |
| |
| TEST(BlockStatEqual, Same) { |
| BlockStatFidl a = {}, b = {}; |
| ASSERT_TRUE(BlockStatEqual(a, b)); |
| } |
| |
| TEST(BlockStatEqual, LargerReadTotalCalls) { |
| BlockStatFidl a = {}, b = {}; |
| a.read.success.total_calls++; |
| ASSERT_FALSE(BlockStatEqual(a, b)); |
| } |
| |
| TEST(BlockStatEqual, LargerWriteBytesTransferred) { |
| BlockStatFidl a = {}, b = {}; |
| a.write.failure.bytes_transferred++; |
| ASSERT_FALSE(BlockStatEqual(a, b)); |
| } |
| |
| TEST(CallStatTest, UpdateSuccess) { |
| storage_metrics::CallStat cs = {}; |
| CallStatFidl fidl_stat; |
| |
| // Copy initial state |
| cs.CopyToFidl(&fidl_stat); |
| |
| cs.UpdateCallStat(true, 10, 100); |
| fidl_stat.success.total_calls++; |
| fidl_stat.success.total_time_spent += 10; |
| fidl_stat.success.minimum_latency = 10; |
| fidl_stat.success.maximum_latency = 10; |
| fidl_stat.success.bytes_transferred += 100; |
| ExpectCallStatMatchFidlStat(cs, fidl_stat); |
| } |
| |
| TEST(CallStatTest, UpdateFailure) { |
| storage_metrics::CallStat cs = {}; |
| CallStatFidl fidl_stat; |
| |
| // Copy initial state |
| cs.CopyToFidl(&fidl_stat); |
| |
| // No change in success stats but everything else changes |
| cs.UpdateCallStat(false, 10, 100); |
| fidl_stat.failure.total_calls++; |
| fidl_stat.failure.total_time_spent += 10; |
| fidl_stat.failure.minimum_latency = 10; |
| fidl_stat.failure.maximum_latency = 10; |
| fidl_stat.failure.bytes_transferred += 100; |
| ExpectCallStatMatchFidlStat(cs, fidl_stat); |
| } |
| |
| TEST(CallStatTest, UpdateBytesTransferred) { |
| storage_metrics::CallStat cs = {}; |
| CallStatFidl fidl_stat; |
| |
| // Copy initial state |
| cs.CopyToFidl(&fidl_stat); |
| |
| // No change in min/max latencies or failure but everything else changes |
| cs.UpdateCallStat(true, 10, 100); |
| fidl_stat.success.total_calls++; |
| fidl_stat.success.total_time_spent += 10; |
| fidl_stat.success.minimum_latency = 10; |
| fidl_stat.success.maximum_latency = 10; |
| fidl_stat.success.bytes_transferred += 100; |
| ExpectCallStatMatchFidlStat(cs, fidl_stat); |
| } |
| |
| TEST(CallStatTest, UpdateMinimumLatency) { |
| storage_metrics::CallStat cs = {}; |
| CallStatFidl fidl_stat; |
| |
| // Copy initial state |
| cs.CopyToFidl(&fidl_stat); |
| |
| // Expect min latency to change and bytes transferred unchanged |
| cs.UpdateCallStat(true, 9, 0); |
| cs.UpdateCallStat(true, 7, 0); |
| fidl_stat.success.total_calls += 2; |
| fidl_stat.success.total_time_spent += (9 + 7); |
| fidl_stat.success.minimum_latency = 7; |
| fidl_stat.success.maximum_latency = 9; |
| fidl_stat.success.bytes_transferred += 0; |
| ExpectCallStatMatchFidlStat(cs, fidl_stat); |
| } |
| |
| TEST(CallStatTest, UpdateFailedMaximumLatency) { |
| storage_metrics::CallStat cs = {}; |
| CallStatFidl fidl_stat; |
| |
| // Copy initial state |
| cs.CopyToFidl(&fidl_stat); |
| |
| // Expect max latency and failed count to change |
| cs.UpdateCallStat(false, 20, 100); |
| cs.UpdateCallStat(false, 30, 100); |
| fidl_stat.failure.total_calls += 2; |
| fidl_stat.failure.total_time_spent += (20 + 30); |
| fidl_stat.failure.minimum_latency = 20; |
| fidl_stat.failure.maximum_latency = 30; |
| fidl_stat.failure.bytes_transferred += (100 + 100); |
| ExpectCallStatMatchFidlStat(cs, fidl_stat); |
| } |
| |
| TEST(CallStatTest, UpdateTimeSpent) { |
| storage_metrics::CallStat cs = {}; |
| CallStatFidl fidl_stat; |
| |
| // Copy initial state |
| cs.CopyToFidl(&fidl_stat); |
| |
| // Expect only time spent and total calls to change |
| cs.UpdateCallStat(true, 20, 0); |
| cs.UpdateCallStat(true, 20, 0); |
| fidl_stat.success.total_calls += 2; |
| fidl_stat.success.minimum_latency = 20; |
| fidl_stat.success.maximum_latency = 20; |
| fidl_stat.success.total_time_spent += (20 + 20); |
| ExpectCallStatMatchFidlStat(cs, fidl_stat); |
| } |
| |
| TEST(CallStatTest, Reset) { |
| storage_metrics::CallStat cs = {}; |
| CallStatFidl fidl_stat; |
| |
| cs.UpdateCallStat(true, 20, 100); |
| cs.UpdateCallStat(false, 20, 100); |
| |
| // Everything should be cleared |
| cs.Reset(); |
| fidl_stat = {}; |
| fidl_stat.success.minimum_latency = storage_metrics::kUninitializedMinimumLatency; |
| fidl_stat.failure.minimum_latency = storage_metrics::kUninitializedMinimumLatency; |
| ExpectCallStatMatchFidlStat(cs, fidl_stat); |
| } |
| |
| TEST(CallStatTest, TestCopyToFidl) { |
| CallStatFidl f = {}; |
| storage_metrics::CallStat cs; |
| |
| // Set max latency |
| cs.UpdateCallStat(true, 20, 100); |
| |
| // Set min latency and fail count |
| cs.UpdateCallStat(true, 10, 20); |
| cs.CopyToFidl(&f); |
| |
| ExpectCallStatMatchFidlStat(cs, f); |
| } |
| |
| TEST(CallStatTest, TestCopyFromFidl) { |
| CallStatFidl f; |
| storage_metrics::CallStat cs; |
| |
| f.success.total_calls = 3; |
| f.success.minimum_latency = 4; |
| f.success.maximum_latency = 15; |
| f.success.total_time_spent = 19; |
| f.success.bytes_transferred = 92; |
| f.failure.total_calls = 3; |
| f.failure.minimum_latency = 4; |
| f.failure.maximum_latency = 15; |
| f.failure.total_time_spent = 19; |
| f.failure.bytes_transferred = 92; |
| cs.CopyFromFidl(&f); |
| |
| ExpectCallStatMatchFidlStat(cs, f); |
| } |
| |
| // Tests enable/disable functionality |
| TEST(MetricsTest, SetEnable) { |
| storage_metrics::Metrics metrics; |
| |
| EXPECT_TRUE(metrics.Enabled()); |
| metrics.SetEnable(false); |
| EXPECT_FALSE(metrics.Enabled()); |
| metrics.SetEnable(true); |
| EXPECT_TRUE(metrics.Enabled()); |
| } |
| |
| // Test initial state of BlockDeviceMetrics |
| TEST(BlockDeviceMetricsTest, DefaultValues) { |
| storage_metrics::BlockDeviceMetrics metrics; |
| fuchsia_hardware_block::wire::BlockStats fidl_block_metrics; |
| |
| EXPECT_TRUE(metrics.Enabled()); |
| |
| metrics.CopyToFidl(&fidl_block_metrics); |
| ExpectBlockDeviceMetricsInitialState(fidl_block_metrics); |
| } |
| |
| // Tests no-updates are made when metrics is disabled |
| TEST(BlockDeviceMetricsTest, DisabledMetricsIgnoreUpdates) { |
| storage_metrics::BlockDeviceMetrics metrics; |
| fuchsia_hardware_block::wire::BlockStats fidl_block_metrics; |
| |
| ASSERT_TRUE(metrics.Enabled()); |
| metrics.CopyToFidl(&fidl_block_metrics); |
| ExpectBlockDeviceMetricsInitialState(fidl_block_metrics); |
| |
| metrics.SetEnable(false); |
| EXPECT_FALSE(metrics.Enabled()); |
| |
| // When not enabled, this should not update anything |
| BlockDeviceMetricsUpdate(metrics, 10, 100, 100, 800); |
| |
| metrics.CopyToFidl(&fidl_block_metrics); |
| ExpectBlockDeviceMetricsInitialState(fidl_block_metrics); |
| } |
| |
| // Tests updates to block device metrics |
| TEST(BlockDeviceMetricsTest, EnabledMetricsCollectOnUpdate) { |
| storage_metrics::BlockDeviceMetrics metrics; |
| fuchsia_hardware_block::wire::BlockStats fidl_block_metrics; |
| CallStatRawFidl fidl_call_stat_raw = {}; |
| CallStatFidl fidl_call_stat; |
| ASSERT_TRUE(metrics.Enabled()); |
| |
| zx_ticks_t minimum_latency = 10; |
| zx_ticks_t maximum_latency = 100; |
| uint64_t bytes_transferred1 = 330; |
| uint64_t bytes_transferred2 = 440; |
| |
| BlockDeviceMetricsUpdate(metrics, minimum_latency, maximum_latency, bytes_transferred1, |
| bytes_transferred2); |
| |
| metrics.CopyToFidl(&fidl_block_metrics); |
| fidl_call_stat_raw.minimum_latency = minimum_latency; |
| fidl_call_stat_raw.maximum_latency = maximum_latency; |
| fidl_call_stat_raw.total_time_spent = minimum_latency + maximum_latency; |
| fidl_call_stat_raw.total_calls = 2; |
| fidl_call_stat_raw.bytes_transferred = bytes_transferred1 + bytes_transferred2; |
| fidl_call_stat.success = fidl_call_stat_raw; |
| fidl_call_stat.failure = fidl_call_stat_raw; |
| |
| CompareFidlBlockDeviceStatAll(fidl_block_metrics, fidl_call_stat); |
| |
| // Disable enable should not change the metrics |
| metrics.SetEnable(false); |
| metrics.CopyToFidl(&fidl_block_metrics); |
| CompareFidlBlockDeviceStatAll(fidl_block_metrics, fidl_call_stat); |
| metrics.SetEnable(true); |
| metrics.CopyToFidl(&fidl_block_metrics); |
| CompareFidlBlockDeviceStatAll(fidl_block_metrics, fidl_call_stat); |
| } |
| |
| TEST(BlockDeviceMetricsTest, UpdateWriteStats) { |
| storage_metrics::BlockDeviceMetrics metrics; |
| fuchsia_hardware_block::wire::BlockStats fidl_block_metrics; |
| |
| metrics.UpdateStats(true, zx::ticks(0), BLOCK_OPCODE_WRITE, 100); |
| metrics.UpdateStats(false, zx::ticks(0), BLOCK_OPCODE_WRITE, 10); |
| metrics.CopyToFidl(&fidl_block_metrics); |
| |
| ASSERT_EQ(1, fidl_block_metrics.write.success.total_calls); |
| ASSERT_EQ(100, fidl_block_metrics.write.success.bytes_transferred); |
| ASSERT_LT(0, fidl_block_metrics.write.success.total_time_spent); |
| ASSERT_EQ(1, fidl_block_metrics.write.failure.total_calls); |
| ASSERT_EQ(10, fidl_block_metrics.write.failure.bytes_transferred); |
| ASSERT_LT(0, fidl_block_metrics.write.failure.total_time_spent); |
| } |
| |
| TEST(BlockDeviceMetricsTest, UpdateReadStats) { |
| storage_metrics::BlockDeviceMetrics metrics; |
| fuchsia_hardware_block::wire::BlockStats fidl_block_metrics; |
| |
| metrics.UpdateStats(true, zx::ticks(0), BLOCK_OPCODE_READ, 100); |
| metrics.UpdateStats(false, zx::ticks(0), BLOCK_OPCODE_READ, 10); |
| metrics.CopyToFidl(&fidl_block_metrics); |
| |
| ASSERT_EQ(1, fidl_block_metrics.read.success.total_calls); |
| ASSERT_EQ(100, fidl_block_metrics.read.success.bytes_transferred); |
| ASSERT_LT(0, fidl_block_metrics.read.success.total_time_spent); |
| ASSERT_EQ(1, fidl_block_metrics.read.failure.total_calls); |
| ASSERT_EQ(10, fidl_block_metrics.read.failure.bytes_transferred); |
| ASSERT_LT(0, fidl_block_metrics.read.failure.total_time_spent); |
| } |
| |
| TEST(BlockDeviceMetricsTest, UpdateFlushStats) { |
| storage_metrics::BlockDeviceMetrics metrics; |
| fuchsia_hardware_block::wire::BlockStats fidl_block_metrics; |
| |
| metrics.UpdateStats(true, zx::ticks(0), BLOCK_OPCODE_FLUSH, 100); |
| metrics.UpdateStats(false, zx::ticks(0), BLOCK_OPCODE_FLUSH, 10); |
| metrics.CopyToFidl(&fidl_block_metrics); |
| |
| ASSERT_EQ(1, fidl_block_metrics.flush.success.total_calls); |
| ASSERT_EQ(100, fidl_block_metrics.flush.success.bytes_transferred); |
| ASSERT_LT(0, fidl_block_metrics.flush.success.total_time_spent); |
| ASSERT_EQ(1, fidl_block_metrics.flush.failure.total_calls); |
| ASSERT_EQ(10, fidl_block_metrics.flush.failure.bytes_transferred); |
| ASSERT_LT(0, fidl_block_metrics.flush.failure.total_time_spent); |
| } |
| |
| TEST(BlockDeviceMetricsTest, UpdateTrimStats) { |
| storage_metrics::BlockDeviceMetrics metrics; |
| fuchsia_hardware_block::wire::BlockStats fidl_block_metrics; |
| |
| metrics.UpdateStats(true, zx::ticks(0), BLOCK_OPCODE_TRIM, 100); |
| metrics.UpdateStats(false, zx::ticks(0), BLOCK_OPCODE_TRIM, 10); |
| metrics.CopyToFidl(&fidl_block_metrics); |
| |
| ASSERT_EQ(1, fidl_block_metrics.trim.success.total_calls); |
| ASSERT_EQ(100, fidl_block_metrics.trim.success.bytes_transferred); |
| ASSERT_LT(0, fidl_block_metrics.trim.success.total_time_spent); |
| ASSERT_EQ(1, fidl_block_metrics.trim.failure.total_calls); |
| ASSERT_EQ(10, fidl_block_metrics.trim.failure.bytes_transferred); |
| ASSERT_LT(0, fidl_block_metrics.trim.failure.total_time_spent); |
| } |
| |
| } // namespace |
| } // namespace storage_metrics |