blob: 6a6a81117f440e5ef3bc473f45ffdf0549ef38cf [file] [log] [blame]
// 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 <limits>
#include <string>
#include <type_traits>
#include <fbl/algorithm.h>
#include <fbl/string_buffer.h>
#include <fbl/string_printf.h>
#include <fs/metrics/histograms.h>
#include <fs/metrics/internal/attributes.h>
#include <fs/metrics/internal/object_offsets.h>
#include <zircon/assert.h>
namespace fs_metrics {
namespace {
// Number of buckets used for histograms. Must keep in syn with cobalt configuration if
// meant to be exported.
constexpr uint64_t kHistogramBuckets = 10;
// Attributes we are currently tracking.
// An attribute which indicates the number of blocks that were affected by a given operation.
//
// Inheriting from this attribute within an operation indicates that such operation is affected by
// the number of blocks.
struct BlockCount : NumericAttribute<BlockCount, int64_t> {
static constexpr int64_t kBuckets[] = {
// Bucket 0: [0, 5) for really small operations.
5,
// Bucket 1: [5, 32)
32,
};
static constexpr int64_t EventOptions::*kAttributeValue = &EventOptions::block_count;
};
// An attribute which indicates whether the operation may be cached in memory or not.
//
// Inheriting from this attribute within an operation indicates that such operation may have
// variable modes of operations, where it either acts on in-memory structures or sends requests to
// the underlying storage.
struct Bufferable : public BinaryAttribute {
static constexpr bool EventOptions::*kAttributeValue = &EventOptions::buffered;
static std::string ToString(size_t index) { return index == 0 ? "unbuffered" : "buffered"; }
};
// An attribute which indicates whether the operation successful completion should be treated
// differently than when it completes with failure.
//
// Inheriting from this attribute within an operation indicates that such operation may fail
// at any point, and that the recorded data should be splitted.
struct Success : public BinaryAttribute {
static constexpr bool EventOptions::*kAttributeValue = &EventOptions::success;
static std::string ToString(size_t index) { return index == 0 ? "ok" : "fail"; }
};
// An attribute which indicates the number of childs a given node in the file system has.
//
// Inheriting from this attribute within an operation indicates that such operation is affected
// by the number of children the node has. An example is a lookup operation.
struct NodeDegree : NumericAttribute<NodeDegree, int64_t> {
static constexpr int64_t kBuckets[] = {
// Bucket 0: [0, 10)
10,
// Bucket 1: [10, 100)
100,
// Bucket 2: [100, 1000)
1000,
};
static constexpr int64_t EventOptions::*kAttributeValue = &EventOptions::node_degree;
};
// Create a histogram with the default number of buckets and properties matching cobalt
// configuration so we can eventually replace cobalt-client.
void CreateNanosecHistogramId(const char* name, inspect::vmo::Object* root,
std::vector<inspect::vmo::ExponentialUintHistogram>* hist_list) {
constexpr uint64_t kBase = 2;
constexpr uint64_t kInitialStep = 10;
constexpr uint64_t kFloor = 0;
hist_list->push_back(
root->CreateExponentialUintHistogram(name, kFloor, kInitialStep, kBase, kHistogramBuckets));
}
void CreateMicrosecHistogramId(const char* name, inspect::vmo::Object* root,
std::vector<inspect::vmo::ExponentialUintHistogram>* hist_list) {
constexpr uint64_t kBase = 2;
constexpr uint64_t kInitialStep = 10000;
constexpr uint64_t kFloor = 0;
hist_list->push_back(
root->CreateExponentialUintHistogram(name, kFloor, kInitialStep, kBase, kHistogramBuckets));
}
// Provides a specialized type that keep track of created attributes. In order to add new
// attributes, the Attribute class needs to be listed here.
// Note: New attributes need to be added to |MakeOptionsSet| in HistogramsTest.
using HistogramOffsets = ObjectOffsets<NodeDegree, BlockCount, Bufferable, Success>;
// In order to add a new operations a couple of things needs to be added:
//
// 1. Add the operation to |OperationType| Enum.
// 2. Add a specialization to the |OperationInfo| template for the added operation.
// 3. Update switch tables in |Histograms::GetHistogramCount| and
// |Histograms::GetHistgramCount(OperationType).
// 4. Add a call to |AddOpHistogram<OperationType>| in the constructor.
// 5. Add the new operation to the operation list in histograms-test.
// Base template specialization for Operations.
template <OperationType operation>
struct OperationInfo {};
// Each operation or metric we want to track needs to provide its own specialization with the
// relavant data and the attributes that it wishes to track.
template <>
struct OperationInfo<OperationType::kRead> : public BlockCount, Bufferable, Success {
using AttributeData = EventOptions;
static constexpr char kPrefix[] = "read";
static constexpr auto CreateTracker = CreateMicrosecHistogramId;
static constexpr uint64_t kStart = 0;
};
template <>
struct OperationInfo<OperationType::kWrite> : public BlockCount, Bufferable, Success {
using AttributeData = EventOptions;
static constexpr char kPrefix[] = "write";
static constexpr auto CreateTracker = CreateMicrosecHistogramId;
static constexpr uint64_t kStart = HistogramOffsets::End<OperationInfo<OperationType::kRead>>();
};
template <>
struct OperationInfo<OperationType::kAppend> : public BlockCount, Bufferable, Success {
using AttributeData = EventOptions;
static constexpr char kPrefix[] = "append";
static constexpr auto CreateTracker = CreateMicrosecHistogramId;
static constexpr uint64_t kStart =
HistogramOffsets::End<OperationInfo<OperationType::kWrite>>();
};
template <>
struct OperationInfo<OperationType::kTruncate> : public BlockCount, Success {
using AttributeData = EventOptions;
static constexpr char kPrefix[] = "truncate";
static constexpr auto CreateTracker = CreateMicrosecHistogramId;
static constexpr uint64_t kStart =
HistogramOffsets::End<OperationInfo<OperationType::kAppend>>();
};
template <>
struct OperationInfo<OperationType::kSetAttr> : public Success {
using AttributeData = EventOptions;
static constexpr char kPrefix[] = "setattr";
static constexpr auto CreateTracker = CreateMicrosecHistogramId;
static constexpr uint64_t kStart =
HistogramOffsets::End<OperationInfo<OperationType::kTruncate>>();
};
template <>
struct OperationInfo<OperationType::kGetAttr> : public Success {
using AttributeData = EventOptions;
static constexpr char kPrefix[] = "getattr";
static constexpr auto CreateTracker = CreateMicrosecHistogramId;
static constexpr uint64_t kStart =
HistogramOffsets::End<OperationInfo<OperationType::kSetAttr>>();
};
template <>
struct OperationInfo<OperationType::kReadDir> : public NodeDegree, Success {
using AttributeData = EventOptions;
static constexpr char kPrefix[] = "readdir";
static constexpr auto CreateTracker = CreateMicrosecHistogramId;
static constexpr uint64_t kStart =
HistogramOffsets::End<OperationInfo<OperationType::kGetAttr>>();
};
template <>
struct OperationInfo<OperationType::kSync> : public BlockCount, Success {
using AttributeData = EventOptions;
static constexpr char kPrefix[] = "sync";
static constexpr auto CreateTracker = CreateMicrosecHistogramId;
static constexpr uint64_t kStart =
HistogramOffsets::End<OperationInfo<OperationType::kReadDir>>();
};
template <>
struct OperationInfo<OperationType::kLookUp> : public NodeDegree, Success {
using AttributeData = EventOptions;
static constexpr char kPrefix[] = "lookup";
static constexpr auto CreateTracker = CreateMicrosecHistogramId;
static constexpr uint64_t kStart = HistogramOffsets::End<OperationInfo<OperationType::kSync>>();
};
template <>
struct OperationInfo<OperationType::kCreate> : public NodeDegree, Success {
using AttributeData = EventOptions;
static constexpr char kPrefix[] = "create";
static constexpr auto CreateTracker = CreateMicrosecHistogramId;
static constexpr uint64_t kStart =
HistogramOffsets::End<OperationInfo<OperationType::kLookUp>>();
};
template <>
struct OperationInfo<OperationType::kClose> : public Success {
using AttributeData = EventOptions;
static constexpr char kPrefix[] = "close";
static constexpr auto CreateTracker = CreateMicrosecHistogramId;
static constexpr uint64_t kStart =
HistogramOffsets::End<OperationInfo<OperationType::kCreate>>();
};
template <>
struct OperationInfo<OperationType::kLink> : public NodeDegree, Success {
using AttributeData = EventOptions;
static constexpr char kPrefix[] = "link";
static constexpr auto CreateTracker = CreateMicrosecHistogramId;
static constexpr uint64_t kStart =
HistogramOffsets::End<OperationInfo<OperationType::kClose>>();
};
template <>
struct OperationInfo<OperationType::kUnlink> : public NodeDegree, Success {
using AttributeData = EventOptions;
static constexpr char kPrefix[] = "unlink";
static constexpr auto CreateTracker = CreateMicrosecHistogramId;
static constexpr uint64_t kStart = HistogramOffsets::End<OperationInfo<OperationType::kLink>>();
};
template <OperationType operation>
void AddOpHistograms(inspect::vmo::Object* root,
std::vector<inspect::vmo::ExponentialUintHistogram>* histograms) {
HistogramOffsets::AddObjects<OperationInfo<operation>>(root, histograms);
}
} // namespace
Histograms::Histograms(inspect::vmo::Object* root) {
nodes_.push_back(root->CreateChild(kHistComponent));
auto& hist_node = nodes_[nodes_.size() - 1];
// Histogram names are defined based on operation_name(_DimensionValue){0,5}, where
// dimension value is determined at runtime based on the EventOptions.
ZX_DEBUG_ASSERT(histograms_.size() == OperationInfo<OperationType::kRead>::kStart);
AddOpHistograms<OperationType::kRead>(&hist_node, &histograms_);
ZX_DEBUG_ASSERT(histograms_.size() == OperationInfo<OperationType::kWrite>::kStart);
AddOpHistograms<OperationType::kWrite>(&hist_node, &histograms_);
ZX_DEBUG_ASSERT(histograms_.size() == OperationInfo<OperationType::kAppend>::kStart);
AddOpHistograms<OperationType::kAppend>(&hist_node, &histograms_);
ZX_DEBUG_ASSERT(histograms_.size() == OperationInfo<OperationType::kTruncate>::kStart);
AddOpHistograms<OperationType::kTruncate>(&hist_node, &histograms_);
ZX_DEBUG_ASSERT(histograms_.size() == OperationInfo<OperationType::kSetAttr>::kStart);
AddOpHistograms<OperationType::kSetAttr>(&hist_node, &histograms_);
ZX_DEBUG_ASSERT(histograms_.size() == OperationInfo<OperationType::kGetAttr>::kStart);
AddOpHistograms<OperationType::kGetAttr>(&hist_node, &histograms_);
ZX_DEBUG_ASSERT(histograms_.size() == OperationInfo<OperationType::kReadDir>::kStart);
AddOpHistograms<OperationType::kReadDir>(&hist_node, &histograms_);
ZX_DEBUG_ASSERT(histograms_.size() == OperationInfo<OperationType::kSync>::kStart);
AddOpHistograms<OperationType::kSync>(&hist_node, &histograms_);
ZX_DEBUG_ASSERT(histograms_.size() == OperationInfo<OperationType::kLookUp>::kStart);
AddOpHistograms<OperationType::kLookUp>(&hist_node, &histograms_);
ZX_DEBUG_ASSERT(histograms_.size() == OperationInfo<OperationType::kCreate>::kStart);
AddOpHistograms<OperationType::kCreate>(&hist_node, &histograms_);
ZX_DEBUG_ASSERT(histograms_.size() == OperationInfo<OperationType::kClose>::kStart);
AddOpHistograms<OperationType::kClose>(&hist_node, &histograms_);
ZX_DEBUG_ASSERT(histograms_.size() == OperationInfo<OperationType::kLink>::kStart);
AddOpHistograms<OperationType::kLink>(&hist_node, &histograms_);
ZX_DEBUG_ASSERT(histograms_.size() == OperationInfo<OperationType::kUnlink>::kStart);
AddOpHistograms<OperationType::kUnlink>(&hist_node, &histograms_);
ZX_DEBUG_ASSERT(histograms_.size() ==
HistogramOffsets::End<OperationInfo<OperationType::kUnlink>>());
}
LatencyEvent Histograms::NewLatencyEvent(OperationType operation) {
return LatencyEvent(this, operation);
}
uint64_t Histograms::GetHistogramId(OperationType operation, const EventOptions& options) const {
switch (operation) {
case OperationType::kClose:
return HistogramOffsets::AbsoluteOffset<OperationInfo<OperationType::kClose>>(options);
case OperationType::kRead:
return HistogramOffsets::AbsoluteOffset<OperationInfo<OperationType::kRead>>(options);
case OperationType::kWrite:
return HistogramOffsets::AbsoluteOffset<OperationInfo<OperationType::kWrite>>(options);
case OperationType::kAppend:
return HistogramOffsets::AbsoluteOffset<OperationInfo<OperationType::kAppend>>(options);
case OperationType::kTruncate:
return HistogramOffsets::AbsoluteOffset<OperationInfo<OperationType::kTruncate>>(options);
case OperationType::kSetAttr:
return HistogramOffsets::AbsoluteOffset<OperationInfo<OperationType::kSetAttr>>(options);
case OperationType::kGetAttr:
return HistogramOffsets::AbsoluteOffset<OperationInfo<OperationType::kGetAttr>>(options);
case OperationType::kReadDir:
return HistogramOffsets::AbsoluteOffset<OperationInfo<OperationType::kReadDir>>(options);
case OperationType::kSync:
return HistogramOffsets::AbsoluteOffset<OperationInfo<OperationType::kSync>>(options);
case OperationType::kLookUp:
return HistogramOffsets::AbsoluteOffset<OperationInfo<OperationType::kLookUp>>(options);
case OperationType::kCreate:
return HistogramOffsets::AbsoluteOffset<OperationInfo<OperationType::kCreate>>(options);
case OperationType::kLink:
return HistogramOffsets::AbsoluteOffset<OperationInfo<OperationType::kLink>>(options);
case OperationType::kUnlink:
return HistogramOffsets::AbsoluteOffset<OperationInfo<OperationType::kUnlink>>(options);
default:
return GetHistogramCount();
};
}
uint64_t Histograms::GetHistogramCount(OperationType operation) {
switch (operation) {
case OperationType::kClose:
return HistogramOffsets::Count<OperationInfo<OperationType::kClose>>();
case OperationType::kRead:
return HistogramOffsets::Count<OperationInfo<OperationType::kRead>>();
case OperationType::kWrite:
return HistogramOffsets::Count<OperationInfo<OperationType::kWrite>>();
case OperationType::kAppend:
return HistogramOffsets::Count<OperationInfo<OperationType::kAppend>>();
case OperationType::kTruncate:
return HistogramOffsets::Count<OperationInfo<OperationType::kTruncate>>();
case OperationType::kSetAttr:
return HistogramOffsets::Count<OperationInfo<OperationType::kSetAttr>>();
case OperationType::kGetAttr:
return HistogramOffsets::Count<OperationInfo<OperationType::kGetAttr>>();
case OperationType::kReadDir:
return HistogramOffsets::Count<OperationInfo<OperationType::kReadDir>>();
case OperationType::kSync:
return HistogramOffsets::Count<OperationInfo<OperationType::kSync>>();
case OperationType::kLookUp:
return HistogramOffsets::Count<OperationInfo<OperationType::kLookUp>>();
case OperationType::kCreate:
return HistogramOffsets::Count<OperationInfo<OperationType::kCreate>>();
case OperationType::kLink:
return HistogramOffsets::Count<OperationInfo<OperationType::kLink>>();
case OperationType::kUnlink:
return HistogramOffsets::Count<OperationInfo<OperationType::kUnlink>>();
default:
return 0;
};
}
void Histograms::Record(uint64_t histogram_id, zx::duration duration) {
ZX_ASSERT(histogram_id < GetHistogramCount());
histograms_[histogram_id].Insert(duration.to_nsecs());
}
} // namespace fs_metrics