| // 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 <algorithm> |
| |
| #include <ddktl/protocol/block.h> |
| #include <storage-metrics/block-metrics.h> |
| #include <storage-metrics/fs-metrics.h> |
| #include <storage-metrics/storage-metrics.h> |
| |
| namespace storage_metrics { |
| |
| namespace { |
| constexpr uint32_t block_operation(uint32_t command) { return command & BLOCK_OP_MASK; } |
| } // namespace |
| |
| bool RawCallStatEqual(const CallStatRawFidl& lhs, const CallStatRawFidl& rhs) { |
| return (lhs.total_calls == rhs.total_calls) && (lhs.bytes_transferred == rhs.bytes_transferred); |
| } |
| |
| bool CallStatEqual(const CallStatFidl& lhs, const CallStatFidl& rhs) { |
| return RawCallStatEqual(lhs.success, rhs.success) && RawCallStatEqual(lhs.failure, rhs.failure); |
| } |
| |
| bool BlockStatEqual(const BlockStatFidl& lhs, const BlockStatFidl& rhs) { |
| return CallStatEqual(lhs.read, rhs.read) && CallStatEqual(lhs.write, rhs.write) && |
| CallStatEqual(lhs.trim, rhs.trim) && CallStatEqual(lhs.flush, rhs.flush) && |
| CallStatEqual(lhs.barrier_before, rhs.barrier_before) && |
| CallStatEqual(lhs.barrier_after, rhs.barrier_after); |
| } |
| |
| CallStat::CallStatRaw::CallStatRaw() { Reset(); } |
| |
| void CallStat::CallStatRaw::Reset() { |
| minimum_latency = 0; |
| maximum_latency = 0; |
| total_time_spent = 0; |
| total_calls = 0; |
| bytes_transferred = 0; |
| minimum_latency = kUninitializedMinimumLatency; |
| } |
| |
| void CallStat::CallStatRaw::CopyFromRawFidl(const CallStatRawFidl* istat) { |
| minimum_latency = istat->minimum_latency; |
| maximum_latency = istat->maximum_latency; |
| total_time_spent = istat->total_time_spent; |
| total_calls = istat->total_calls; |
| bytes_transferred = istat->bytes_transferred; |
| minimum_latency = istat->minimum_latency; |
| } |
| |
| void CallStat::CallStatRaw::CopyFromRawFidl(const fuchsia_storage_metrics_CallStatRaw* istat) { |
| minimum_latency = istat->minimum_latency; |
| maximum_latency = istat->maximum_latency; |
| total_time_spent = istat->total_time_spent; |
| total_calls = istat->total_calls; |
| bytes_transferred = istat->bytes_transferred; |
| minimum_latency = istat->minimum_latency; |
| } |
| |
| void CallStat::CallStatRaw::CopyToRawFidl(CallStatRawFidl* out) const { |
| out->minimum_latency = minimum_latency.load(); |
| out->maximum_latency = maximum_latency.load(); |
| out->total_time_spent = total_time_spent.load(); |
| out->total_calls = total_calls.load(); |
| out->bytes_transferred = bytes_transferred.load(); |
| out->minimum_latency = minimum_latency.load(); |
| } |
| |
| void CallStat::CallStatRaw::CopyToRawFidl(fuchsia_storage_metrics_CallStatRaw* out) const { |
| out->minimum_latency = minimum_latency.load(); |
| out->maximum_latency = maximum_latency.load(); |
| out->total_time_spent = total_time_spent.load(); |
| out->total_calls = total_calls.load(); |
| out->bytes_transferred = bytes_transferred.load(); |
| out->minimum_latency = minimum_latency.load(); |
| } |
| |
| void CallStat::CallStatRaw::UpdateRawCallStat(zx_ticks_t delta_time, uint64_t bytes) { |
| total_calls++; |
| total_time_spent += delta_time; |
| bytes_transferred += bytes; |
| |
| zx_ticks_t max = maximum_latency.load(); |
| while (delta_time > max) { |
| maximum_latency.compare_exchange_strong(max, delta_time); |
| max = maximum_latency.load(); |
| } |
| |
| zx_ticks_t min = minimum_latency.load(); |
| while (delta_time < min) { |
| minimum_latency.compare_exchange_strong(min, delta_time); |
| min = minimum_latency.load(); |
| } |
| } |
| |
| CallStat::CallStat() { Reset(); } |
| |
| void CallStat::Reset() { |
| success_stat_.Reset(); |
| failure_stat_.Reset(); |
| } |
| |
| void CallStat::CopyFromFidl(const CallStatFidl* stat) { |
| success_stat_.CopyFromRawFidl(&stat->success); |
| failure_stat_.CopyFromRawFidl(&stat->failure); |
| } |
| |
| void CallStat::CopyFromFidl(const fuchsia_storage_metrics_CallStat* stat) { |
| success_stat_.CopyFromRawFidl(&stat->success); |
| failure_stat_.CopyFromRawFidl(&stat->failure); |
| } |
| |
| void CallStat::CopyToFidl(CallStatFidl* out) const { |
| success_stat_.CopyToRawFidl(&out->success); |
| failure_stat_.CopyToRawFidl(&out->failure); |
| } |
| |
| void CallStat::CopyToFidl(fuchsia_storage_metrics_CallStat* out) const { |
| success_stat_.CopyToRawFidl(&out->success); |
| failure_stat_.CopyToRawFidl(&out->failure); |
| } |
| |
| zx_ticks_t CallStat::minimum_latency(std::optional<bool> success) const { |
| if (success) { |
| if (*success == true) { |
| return success_stat_.minimum_latency; |
| } else { |
| return failure_stat_.minimum_latency; |
| } |
| } |
| return std::min(success_stat_.minimum_latency, failure_stat_.minimum_latency); |
| } |
| zx_ticks_t CallStat::maximum_latency(std::optional<bool> success) const { |
| if (success) { |
| if (*success == true) { |
| return success_stat_.maximum_latency; |
| } else { |
| return failure_stat_.maximum_latency; |
| } |
| } |
| return std::max(success_stat_.maximum_latency, failure_stat_.maximum_latency); |
| } |
| zx_ticks_t CallStat::total_time_spent(std::optional<bool> success) const { |
| if (success) { |
| if (*success == true) { |
| return success_stat_.total_time_spent; |
| } else { |
| return failure_stat_.total_time_spent; |
| } |
| } |
| return success_stat_.total_time_spent + failure_stat_.total_time_spent; |
| } |
| uint64_t CallStat::total_calls(std::optional<bool> success) const { |
| if (success) { |
| if (*success == true) { |
| return success_stat_.total_calls; |
| } else { |
| return failure_stat_.total_calls; |
| } |
| } |
| return success_stat_.total_calls + failure_stat_.total_calls; |
| } |
| uint64_t CallStat::bytes_transferred(std::optional<bool> success) const { |
| if (success) { |
| if (*success == true) { |
| return success_stat_.bytes_transferred; |
| } else { |
| return failure_stat_.bytes_transferred; |
| } |
| } |
| return success_stat_.bytes_transferred + failure_stat_.bytes_transferred; |
| } |
| |
| void CallStat::Dump(FILE* stream, const char* stat_name, std::optional<bool> success) const { |
| const char* stat_success = "aggregate"; |
| if (success) { |
| if (*success) { |
| stat_success = "success"; |
| } else { |
| stat_success = "failure"; |
| } |
| } |
| fprintf(stream, "%s.%s.total_calls: %lu\n", stat_name, stat_success, |
| total_calls(success)); |
| fprintf(stream, "%s.%s.total_time_spent: %ld\n", stat_name, stat_success, |
| total_time_spent(success)); |
| fprintf(stream, "%s.%s.maximum_latency: %ld\n", stat_name, stat_success, |
| maximum_latency(success)); |
| fprintf(stream, "%s.%s.minimum_latency: %ld\n", stat_name, stat_success, |
| minimum_latency(success) == kUninitializedMinimumLatency ? 0 : minimum_latency(success)); |
| fprintf(stream, "%s.%s.bytes_transferred: %lu\n", stat_name, stat_success, |
| bytes_transferred(success)); |
| fprintf(stream, "\n"); |
| } |
| |
| void CallStat::UpdateCallStat(bool success, zx_ticks_t delta_time, uint64_t bytes_transferred) { |
| CallStatRaw* stat; |
| if (success) { |
| stat = &success_stat_; |
| } else { |
| stat = &failure_stat_; |
| } |
| |
| stat->UpdateRawCallStat(delta_time, bytes_transferred); |
| } |
| |
| void Metrics::SetEnable(bool enable) { enabled_ = enable; } |
| |
| bool Metrics::Enabled() const { return enabled_; } |
| |
| FsMetrics::FsMetrics(const ::llcpp::fuchsia::storage::metrics::FsMetrics* metrics) { |
| create_.CopyFromFidl(&metrics->create); |
| read_.CopyFromFidl(&metrics->read); |
| write_.CopyFromFidl(&metrics->write); |
| truncate_.CopyFromFidl(&metrics->truncate); |
| unlink_.CopyFromFidl(&metrics->unlink); |
| rename_.CopyFromFidl(&metrics->rename); |
| lookup_.CopyFromFidl(&metrics->lookup); |
| open_.CopyFromFidl(&metrics->open); |
| |
| SetEnable(true); |
| } |
| |
| FsMetrics::FsMetrics(const fuchsia_storage_metrics_FsMetrics* metrics) { |
| create_.CopyFromFidl(&metrics->create); |
| read_.CopyFromFidl(&metrics->read); |
| write_.CopyFromFidl(&metrics->write); |
| truncate_.CopyFromFidl(&metrics->truncate); |
| unlink_.CopyFromFidl(&metrics->unlink); |
| rename_.CopyFromFidl(&metrics->rename); |
| lookup_.CopyFromFidl(&metrics->lookup); |
| open_.CopyFromFidl(&metrics->open); |
| |
| SetEnable(true); |
| } |
| |
| void FsMetrics::CopyToFidl(::llcpp::fuchsia::storage::metrics::FsMetrics* metrics) const { |
| create_.CopyToFidl(&metrics->create); |
| read_.CopyToFidl(&metrics->read); |
| write_.CopyToFidl(&metrics->write); |
| truncate_.CopyToFidl(&metrics->truncate); |
| unlink_.CopyToFidl(&metrics->unlink); |
| rename_.CopyToFidl(&metrics->rename); |
| lookup_.CopyToFidl(&metrics->lookup); |
| open_.CopyToFidl(&metrics->open); |
| } |
| |
| void FsMetrics::CopyToFidl(fuchsia_storage_metrics_FsMetrics* metrics) const { |
| create_.CopyToFidl(&metrics->create); |
| read_.CopyToFidl(&metrics->read); |
| write_.CopyToFidl(&metrics->write); |
| truncate_.CopyToFidl(&metrics->truncate); |
| unlink_.CopyToFidl(&metrics->unlink); |
| rename_.CopyToFidl(&metrics->rename); |
| lookup_.CopyToFidl(&metrics->lookup); |
| open_.CopyToFidl(&metrics->open); |
| } |
| |
| void FsMetrics::Dump(FILE* stream, std::optional<bool> success) const { |
| create_.Dump(stream, "create", success); |
| read_.Dump(stream, "read", success); |
| write_.Dump(stream, "write", success); |
| truncate_.Dump(stream, "truncate", success); |
| unlink_.Dump(stream, "unlink", success); |
| rename_.Dump(stream, "rename", success); |
| lookup_.Dump(stream, "lookup", success); |
| open_.Dump(stream, "open", success); |
| } |
| |
| BlockDeviceMetrics::BlockDeviceMetrics(const BlockStatFidl* metrics) { |
| read_.CopyFromFidl(&metrics->read); |
| write_.CopyFromFidl(&metrics->write); |
| trim_.CopyFromFidl(&metrics->trim); |
| flush_.CopyFromFidl(&metrics->flush); |
| barrier_before_.CopyFromFidl(&metrics->barrier_before); |
| barrier_after_.CopyFromFidl(&metrics->barrier_after); |
| |
| SetEnable(true); |
| } |
| |
| BlockDeviceMetrics::BlockDeviceMetrics(const fuchsia_hardware_block_BlockStats* metrics) { |
| read_.CopyFromFidl(&metrics->read); |
| write_.CopyFromFidl(&metrics->write); |
| trim_.CopyFromFidl(&metrics->trim); |
| flush_.CopyFromFidl(&metrics->flush); |
| barrier_before_.CopyFromFidl(&metrics->barrier_before); |
| barrier_after_.CopyFromFidl(&metrics->barrier_after); |
| |
| SetEnable(true); |
| } |
| |
| void BlockDeviceMetrics::CopyToFidl(BlockStatFidl* metrics) const { |
| read_.CopyToFidl(&metrics->read); |
| write_.CopyToFidl(&metrics->write); |
| trim_.CopyToFidl(&metrics->trim); |
| flush_.CopyToFidl(&metrics->flush); |
| barrier_before_.CopyToFidl(&metrics->barrier_before); |
| barrier_after_.CopyToFidl(&metrics->barrier_after); |
| } |
| |
| void BlockDeviceMetrics::CopyToFidl(fuchsia_hardware_block_BlockStats* metrics) const { |
| read_.CopyToFidl(&metrics->read); |
| write_.CopyToFidl(&metrics->write); |
| trim_.CopyToFidl(&metrics->trim); |
| flush_.CopyToFidl(&metrics->flush); |
| barrier_before_.CopyToFidl(&metrics->barrier_before); |
| barrier_after_.CopyToFidl(&metrics->barrier_after); |
| } |
| |
| uint64_t BlockDeviceMetrics::TotalCalls(std::optional<bool> success) const { |
| // Barriers are modifiers of a command so we don't count them here |
| return read_.total_calls(success) + write_.total_calls(success) + trim_.total_calls(success) + |
| flush_.total_calls(success); |
| } |
| |
| uint64_t BlockDeviceMetrics::TotalBytesTransferred(std::optional<bool> success) const { |
| // Barriers are modifiers of a command so we don't count their bytes |
| return read_.bytes_transferred(success) + write_.bytes_transferred(success) + |
| trim_.bytes_transferred(success) + flush_.bytes_transferred(success); |
| } |
| |
| void BlockDeviceMetrics::Dump(FILE* stream, std::optional<bool> success) const { |
| read_.Dump(stream, "read", success); |
| write_.Dump(stream, "write", success); |
| trim_.Dump(stream, "trim", success); |
| flush_.Dump(stream, "flush", success); |
| barrier_before_.Dump(stream, "barrier_before", success); |
| barrier_after_.Dump(stream, "barrier_after", success); |
| } |
| |
| void BlockDeviceMetrics::UpdateStats(bool success, const zx::ticks start_tick, |
| const uint32_t command, const uint64_t bytes_transfered) { |
| zx::ticks duration = zx::ticks::now() - start_tick; |
| |
| if (block_operation(command) == BLOCK_OP_WRITE) { |
| UpdateWriteStat(success, duration.get(), bytes_transfered); |
| } else if (block_operation(command) == BLOCK_OP_READ) { |
| UpdateReadStat(success, duration.get(), bytes_transfered); |
| } else if (block_operation(command) == BLOCK_OP_FLUSH) { |
| UpdateFlushStat(success, duration.get(), bytes_transfered); |
| } else if (block_operation(command) == BLOCK_OP_TRIM) { |
| UpdateTrimStat(success, duration.get(), bytes_transfered); |
| } |
| |
| if ((command & BLOCK_FL_BARRIER_BEFORE) == BLOCK_FL_BARRIER_BEFORE) { |
| UpdateBarrierBeforeStat(success, duration.get(), bytes_transfered); |
| } |
| |
| if ((command & BLOCK_FL_BARRIER_AFTER) == BLOCK_FL_BARRIER_AFTER) { |
| UpdateBarrierAfterStat(success, duration.get(), bytes_transfered); |
| } |
| } |
| |
| } // namespace storage_metrics |