blob: 287ae2c8a0fab364ec000475a92dbdcec79665f2 [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 event.
//
// Inheriting from this attribute within an event indicates that such event 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 events.
5,
// Bucket 1: [5, 32)
32,
};
static constexpr int64_t EventOptions::*kAttributeValue = &EventOptions::block_count;
};
// An attribute which indicates whether the event may be cached in memory or not.
//
// Inheriting from this attribute within an event indicates that such event may have
// variable modes of events, 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 event successful completion should be treated
// differently than when it completes with failure.
//
// Inheriting from this attribute within an event indicates that such event 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 event indicates that such event is affected
// by the number of children the node has. An example is a lookup event.
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::Node* root,
std::vector<inspect::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::Node* root,
std::vector<inspect::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 events a couple of things needs to be added:
//
// 1. Add the event to |Event| Enum.
// 2. Add a specialization to the |EventInfo| template for the added event.
// 3. Update switch tables in |Histograms::GetHistogramCount| and
// |Histograms::GetHistgramCount(Event).
// 4. Add a call to |AddOpHistogram<Event>| in the constructor.
// 5. Add the new event to the event list in histograms-test.
// Base template specialization for Events.
template <Event event>
struct EventInfo {};
// Each event 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 EventInfo<Event::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 EventInfo<Event::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<EventInfo<Event::kRead>>();
};
template <>
struct EventInfo<Event::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<EventInfo<Event::kWrite>>();
};
template <>
struct EventInfo<Event::kTruncate> : public BlockCount, Success {
using AttributeData = EventOptions;
static constexpr char kPrefix[] = "truncate";
static constexpr auto CreateTracker = CreateMicrosecHistogramId;
static constexpr uint64_t kStart = HistogramOffsets::End<EventInfo<Event::kAppend>>();
};
template <>
struct EventInfo<Event::kSetAttr> : public Success {
using AttributeData = EventOptions;
static constexpr char kPrefix[] = "setattr";
static constexpr auto CreateTracker = CreateMicrosecHistogramId;
static constexpr uint64_t kStart = HistogramOffsets::End<EventInfo<Event::kTruncate>>();
};
template <>
struct EventInfo<Event::kGetAttr> : public Success {
using AttributeData = EventOptions;
static constexpr char kPrefix[] = "getattr";
static constexpr auto CreateTracker = CreateMicrosecHistogramId;
static constexpr uint64_t kStart = HistogramOffsets::End<EventInfo<Event::kSetAttr>>();
};
template <>
struct EventInfo<Event::kReadDir> : public NodeDegree, Success {
using AttributeData = EventOptions;
static constexpr char kPrefix[] = "readdir";
static constexpr auto CreateTracker = CreateMicrosecHistogramId;
static constexpr uint64_t kStart = HistogramOffsets::End<EventInfo<Event::kGetAttr>>();
};
template <>
struct EventInfo<Event::kSync> : public BlockCount, Success {
using AttributeData = EventOptions;
static constexpr char kPrefix[] = "sync";
static constexpr auto CreateTracker = CreateMicrosecHistogramId;
static constexpr uint64_t kStart = HistogramOffsets::End<EventInfo<Event::kReadDir>>();
};
template <>
struct EventInfo<Event::kLookUp> : public NodeDegree, Success {
using AttributeData = EventOptions;
static constexpr char kPrefix[] = "lookup";
static constexpr auto CreateTracker = CreateMicrosecHistogramId;
static constexpr uint64_t kStart = HistogramOffsets::End<EventInfo<Event::kSync>>();
};
template <>
struct EventInfo<Event::kCreate> : public NodeDegree, Success {
using AttributeData = EventOptions;
static constexpr char kPrefix[] = "create";
static constexpr auto CreateTracker = CreateMicrosecHistogramId;
static constexpr uint64_t kStart = HistogramOffsets::End<EventInfo<Event::kLookUp>>();
};
template <>
struct EventInfo<Event::kClose> : public Success {
using AttributeData = EventOptions;
static constexpr char kPrefix[] = "close";
static constexpr auto CreateTracker = CreateMicrosecHistogramId;
static constexpr uint64_t kStart = HistogramOffsets::End<EventInfo<Event::kCreate>>();
};
template <>
struct EventInfo<Event::kLink> : public NodeDegree, Success {
using AttributeData = EventOptions;
static constexpr char kPrefix[] = "link";
static constexpr auto CreateTracker = CreateMicrosecHistogramId;
static constexpr uint64_t kStart = HistogramOffsets::End<EventInfo<Event::kClose>>();
};
template <>
struct EventInfo<Event::kUnlink> : public NodeDegree, Success {
using AttributeData = EventOptions;
static constexpr char kPrefix[] = "unlink";
static constexpr auto CreateTracker = CreateMicrosecHistogramId;
static constexpr uint64_t kStart = HistogramOffsets::End<EventInfo<Event::kLink>>();
};
template <Event event>
void AddOpHistograms(inspect::Node* root,
std::vector<inspect::ExponentialUintHistogram>* histograms) {
HistogramOffsets::AddObjects<EventInfo<event>>(root, histograms);
}
} // namespace
Histograms::Histograms(inspect::Node* root) {
nodes_.push_back(root->CreateChild(kHistComponent));
auto& hist_node = nodes_[nodes_.size() - 1];
// Histogram names are defined based on event_name(_DimensionValue){0,5}, where
// dimension value is determined at runtime based on the EventOptions.
ZX_DEBUG_ASSERT(histograms_.size() == EventInfo<Event::kRead>::kStart);
AddOpHistograms<Event::kRead>(&hist_node, &histograms_);
ZX_DEBUG_ASSERT(histograms_.size() == EventInfo<Event::kWrite>::kStart);
AddOpHistograms<Event::kWrite>(&hist_node, &histograms_);
ZX_DEBUG_ASSERT(histograms_.size() == EventInfo<Event::kAppend>::kStart);
AddOpHistograms<Event::kAppend>(&hist_node, &histograms_);
ZX_DEBUG_ASSERT(histograms_.size() == EventInfo<Event::kTruncate>::kStart);
AddOpHistograms<Event::kTruncate>(&hist_node, &histograms_);
ZX_DEBUG_ASSERT(histograms_.size() == EventInfo<Event::kSetAttr>::kStart);
AddOpHistograms<Event::kSetAttr>(&hist_node, &histograms_);
ZX_DEBUG_ASSERT(histograms_.size() == EventInfo<Event::kGetAttr>::kStart);
AddOpHistograms<Event::kGetAttr>(&hist_node, &histograms_);
ZX_DEBUG_ASSERT(histograms_.size() == EventInfo<Event::kReadDir>::kStart);
AddOpHistograms<Event::kReadDir>(&hist_node, &histograms_);
ZX_DEBUG_ASSERT(histograms_.size() == EventInfo<Event::kSync>::kStart);
AddOpHistograms<Event::kSync>(&hist_node, &histograms_);
ZX_DEBUG_ASSERT(histograms_.size() == EventInfo<Event::kLookUp>::kStart);
AddOpHistograms<Event::kLookUp>(&hist_node, &histograms_);
ZX_DEBUG_ASSERT(histograms_.size() == EventInfo<Event::kCreate>::kStart);
AddOpHistograms<Event::kCreate>(&hist_node, &histograms_);
ZX_DEBUG_ASSERT(histograms_.size() == EventInfo<Event::kClose>::kStart);
AddOpHistograms<Event::kClose>(&hist_node, &histograms_);
ZX_DEBUG_ASSERT(histograms_.size() == EventInfo<Event::kLink>::kStart);
AddOpHistograms<Event::kLink>(&hist_node, &histograms_);
ZX_DEBUG_ASSERT(histograms_.size() == EventInfo<Event::kUnlink>::kStart);
AddOpHistograms<Event::kUnlink>(&hist_node, &histograms_);
ZX_DEBUG_ASSERT(histograms_.size() == HistogramOffsets::End<EventInfo<Event::kUnlink>>());
}
LatencyEvent Histograms::NewLatencyEvent(Event event) { return LatencyEvent(this, event); }
uint64_t Histograms::GetHistogramId(Event event, const EventOptions& options) const {
switch (event) {
case Event::kClose:
return HistogramOffsets::AbsoluteOffset<EventInfo<Event::kClose>>(options);
case Event::kRead:
return HistogramOffsets::AbsoluteOffset<EventInfo<Event::kRead>>(options);
case Event::kWrite:
return HistogramOffsets::AbsoluteOffset<EventInfo<Event::kWrite>>(options);
case Event::kAppend:
return HistogramOffsets::AbsoluteOffset<EventInfo<Event::kAppend>>(options);
case Event::kTruncate:
return HistogramOffsets::AbsoluteOffset<EventInfo<Event::kTruncate>>(options);
case Event::kSetAttr:
return HistogramOffsets::AbsoluteOffset<EventInfo<Event::kSetAttr>>(options);
case Event::kGetAttr:
return HistogramOffsets::AbsoluteOffset<EventInfo<Event::kGetAttr>>(options);
case Event::kReadDir:
return HistogramOffsets::AbsoluteOffset<EventInfo<Event::kReadDir>>(options);
case Event::kSync:
return HistogramOffsets::AbsoluteOffset<EventInfo<Event::kSync>>(options);
case Event::kLookUp:
return HistogramOffsets::AbsoluteOffset<EventInfo<Event::kLookUp>>(options);
case Event::kCreate:
return HistogramOffsets::AbsoluteOffset<EventInfo<Event::kCreate>>(options);
case Event::kLink:
return HistogramOffsets::AbsoluteOffset<EventInfo<Event::kLink>>(options);
case Event::kUnlink:
return HistogramOffsets::AbsoluteOffset<EventInfo<Event::kUnlink>>(options);
default:
return GetHistogramCount();
};
}
uint64_t Histograms::GetHistogramCount(Event event) {
switch (event) {
case Event::kClose:
return HistogramOffsets::Count<EventInfo<Event::kClose>>();
case Event::kRead:
return HistogramOffsets::Count<EventInfo<Event::kRead>>();
case Event::kWrite:
return HistogramOffsets::Count<EventInfo<Event::kWrite>>();
case Event::kAppend:
return HistogramOffsets::Count<EventInfo<Event::kAppend>>();
case Event::kTruncate:
return HistogramOffsets::Count<EventInfo<Event::kTruncate>>();
case Event::kSetAttr:
return HistogramOffsets::Count<EventInfo<Event::kSetAttr>>();
case Event::kGetAttr:
return HistogramOffsets::Count<EventInfo<Event::kGetAttr>>();
case Event::kReadDir:
return HistogramOffsets::Count<EventInfo<Event::kReadDir>>();
case Event::kSync:
return HistogramOffsets::Count<EventInfo<Event::kSync>>();
case Event::kLookUp:
return HistogramOffsets::Count<EventInfo<Event::kLookUp>>();
case Event::kCreate:
return HistogramOffsets::Count<EventInfo<Event::kCreate>>();
case Event::kLink:
return HistogramOffsets::Count<EventInfo<Event::kLink>>();
case Event::kUnlink:
return HistogramOffsets::Count<EventInfo<Event::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());
}
uint64_t Histograms::Size() {
// An integer for each bucket + metadata
constexpr uint32_t kApproximateNameLength = 30;
return fbl::round_up(HistogramOffsets::End<EventInfo<Event::kUnlink>>() *
((kHistogramBuckets * sizeof(uint64_t) + kApproximateNameLength) +
strlen(Histograms::kHistComponent)),
static_cast<uint64_t>(PAGE_SIZE));
}
} // namespace fs_metrics