1 // Copyright 2018 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #pragma once
6 
7 #include <stdint.h>
8 
9 #include <fbl/function.h>
10 #include <fbl/string.h>
11 
12 namespace cobalt_client {
13 
14 // Defines basic set of options for instantiating a metric.
15 struct MetricOptions {
16 
17     enum class Mode : uint8_t {
18         // This mode marks a set of options as a placeholder, allowing metric instantiations to
19         // defer initialization to a later stage.
20         kLazy,
21         // Metric is aggregated locally and published via collector interface.
22         kLocal,
23         // Metric deltas are aggregated locally, and sent for global aggregation to a remote
24         // service.
25         kRemote,
26         // Combination of kLocal and kRemote.
27         kRemoteAndLocal,
28     };
29 
SetModeMetricOptions30     void SetMode(Mode mode) { this->mode = mode; }
31 
32     // Returns true if the metrics supports remote collection.
33     // This is values collected by another service, such as Cobalt.
IsRemoteMetricOptions34     bool IsRemote() const { return mode == Mode::kRemote || mode == Mode::kRemoteAndLocal; }
35 
36     // Returns true if the metric supports in process collection.
37     // This is values tied to the process life-time.
IsLocalMetricOptions38     bool IsLocal() const { return mode == Mode::kLocal || mode == Mode::kRemoteAndLocal; }
39 
40     // Returns true if this does not represent a valid configuration, and is in |kLazy| mode.
IsLazyMetricOptions41     bool IsLazy() const { return mode == Mode::kLazy; }
42 
43     // Required for local metrics. If not set, and metric is both Local and Remote,
44     // this will be generated from the |metric_id|, |event_code|(if not 0) and |component|(if not
45     // empty).
46     fbl::String name;
47 
48     // Provides refined metric collection for remote and local metrics.
49     // Warning: |component| is not yet supported in the backend, so it will be ignored.
50     fbl::String component;
51 
52     // Function that translates |metric_id| to a human readable name.
53     // If returns |nullptr| or is unset, the stringified version of |uint32_t| will be used.
54     const char* (*get_metric_name)(uint32_t);
55 
56     // Function that translates |event_code| to a human readable name.
57     // If returns |nullptr| or is unset, the stringified version of |uint32_t| will be used.
58     const char* (*get_event_name)(uint32_t);
59 
60     // Used by remote metrics to match with the respective unique id for the projects defined
61     // metrics in the backend.
62     uint32_t metric_id;
63 
64     // Provides refined metric collection for |kRemote| and |kLocal| metrics.
65     // |event_code| 0 is reserved for Unknown events.
66     // Warning: |event_code| is not yet supported in the backend, so it will be set to 0.
67     uint32_t event_code;
68 
69     // Defines whether the metric is local or remote.
70     // Internal use, should not be set manually.
71     Mode mode = Mode::kLazy;
72 };
73 
74 // Describes an histogram, and provides data for mapping a value to a given bucket.
75 // Every histogram contains two additional buckets, one at index 0 and bucket_count + 1.
76 // These buckets are used to store underflow and overflow respectively.
77 //
78 // buckets = [-inf, min_value) ...... [max_value, +inf)
79 //
80 // Parameters are calculated by the factory methods based on the input parameters,
81 // so that expectations are met.
82 //
83 // If using cobalt to flush your observations to the backend, this options should match
84 // your metric definitions for correct behavior. Mismatch with the respective metric definition
85 // will not allow proper collection and aggregation of metrics in the backend.
86 struct HistogramOptions : public MetricOptions {
87     enum class Type {
88         // Each bucket is described in the following form:
89         // range(i) =  [ b * i + c, b * {i +1} + c)
90         // i = (val - c) / b
91         kLinear,
92         // Each bucket is described in the following form:
93         // range(i) =  [ b * a^i + c, b * a^{i+1} + c)
94         // The cost of this type is O(1), because:
95         // i = floor(log (val - c)  - log b)/log a
96         kExponential,
97     };
98 
99     // Returns HistogramOptions that:
100     //   * Exponential bucket range with base 2 => lower_bound[i] = a*2^i-1 - 1
101     //   * Has underflow bucket from (-inf, 0)
102     //   * The first bucket contains [0, 1)
103     //   * a is an integer greater or equal to 1.
104     //   * if |max| % (2^|bucket_count| - 1):
105     //      - Is not 0, then |max| is contained in the last bucket.
106     //      - Is 0, then |max| is the lower bound of the overflow bucket.
107     //
108     //   For example:
109     //       - With bucket_count 12 and max 40950, we get scalar 10, base 2,
110     //         and offset -10.
111     //       - With bucket_count 12 and max 40960, we get scalar 11, base 2,
112     //         and offset -11.
113     static HistogramOptions Exponential(uint32_t bucket_count, int64_t max);
114 
115     // Returns HistogramOptions that:
116     //   * Exponential bucket range with base 2 => lower_bound[i] = a*2^i-1 - 1
117     //   * Has underflow bucket from (-inf, min)
118     //   * The first bucket contains [min, min+1)
119     //   * a is an integer greater or equal to 1.
120     //   * if |max - min| % (2^|bucket_count| - 1):
121     //      - Is not, then |max| is contained in the last bucket.
122     //      - Is 0, then |max| is the lower bound of the overflow bucket.
123     static HistogramOptions Exponential(uint32_t bucket_count, int64_t min, int64_t max);
124 
125     // Returns HistogramOptions that:
126     //   * Has an extra underflow bucket.
127     //   * Has an extra overflow bucket.
128     //   * For every bucket from i = 1 to |bucket_count| has a lower bound defined by:
129     //       scalar * (base^(i-1) - 1) + min
130     static HistogramOptions CustomizedExponential(uint32_t bucket_count, uint32_t base,
131                                                   uint32_t scalar, int64_t min);
132 
133     // Returns HistogramOptions that:
134     //   * Linear bucket range with fixed step size ceil(|max|/|bucket_count|).
135     //   * Has underflow bucket from (-inf, 0)
136     //   * The first bucket contains [0, step_size)
137     //   * |max| is contained in the last bucket if its not a multiple of |bucket_count|.
138     static HistogramOptions Linear(uint32_t bucket_count, int64_t max);
139 
140     // Returns HistogramOptions that:
141     //   * Linear bucket range with fixed step size ceil(|max|/|bucket_count|).
142     //   * Has underflow bucket from (-inf, 0)
143     //   * The first bucket contains [0, step_size)
144     //   * |max| is contained in the last bucket if its not a multiple of |bucket_count|.
145     static HistogramOptions Linear(uint32_t bucket_count, int64_t min, int64_t max);
146 
147     // Returns HistogramOptions that:
148     //   * Has an extra underflow bucket.
149     //   * Has an extra overflow bucket.
150     //   * For every bucket from i = 1 to |bucket_count| has a lower bound defined by:
151     //       min + step_size * (i-1)
152     static HistogramOptions CustomizedLinear(uint32_t bucket_count, uint32_t step_size,
153                                              int64_t min);
154 
155     HistogramOptions() = default;
156     HistogramOptions(const HistogramOptions&);
157 
158     // Sanity check.
159     bool IsValid() const;
160 
161     // This parameters should not be set manually.
162 
163     // Function used for mapping a value to a given bucket.
164     uint32_t (*map_fn)(double, uint32_t, const HistogramOptions&) = nullptr;
165 
166     // Function used for mapping a bucket to its lowerbound.
167     double (*reverse_map_fn)(uint32_t, uint32_t, const HistogramOptions&) = nullptr;
168 
169     // Base to describe the width of each step, in |kExponentialWidth|.
170     double base = 1;
171 
172     // Scalar used by the type. This scales the width of each step.
173     double scalar = 1;
174 
175     // This matchest offset', which is calculated depending on the histogram type.
176     double offset = 0;
177 
178     // Bounds for the histogram.
179     double max_value = 0;
180 
181     // Type of the histogram to be constructed.
182     Type type;
183 };
184 
185 } // namespace cobalt_client
186