xref: /aosp_15_r20/external/pigweed/pw_metric/metric.cc (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1*61c4878aSAndroid Build Coastguard Worker // Copyright 2020 The Pigweed Authors
2*61c4878aSAndroid Build Coastguard Worker //
3*61c4878aSAndroid Build Coastguard Worker // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4*61c4878aSAndroid Build Coastguard Worker // use this file except in compliance with the License. You may obtain a copy of
5*61c4878aSAndroid Build Coastguard Worker // the License at
6*61c4878aSAndroid Build Coastguard Worker //
7*61c4878aSAndroid Build Coastguard Worker //     https://www.apache.org/licenses/LICENSE-2.0
8*61c4878aSAndroid Build Coastguard Worker //
9*61c4878aSAndroid Build Coastguard Worker // Unless required by applicable law or agreed to in writing, software
10*61c4878aSAndroid Build Coastguard Worker // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11*61c4878aSAndroid Build Coastguard Worker // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12*61c4878aSAndroid Build Coastguard Worker // License for the specific language governing permissions and limitations under
13*61c4878aSAndroid Build Coastguard Worker // the License.
14*61c4878aSAndroid Build Coastguard Worker 
15*61c4878aSAndroid Build Coastguard Worker #include "pw_metric/metric.h"
16*61c4878aSAndroid Build Coastguard Worker 
17*61c4878aSAndroid Build Coastguard Worker #include <array>
18*61c4878aSAndroid Build Coastguard Worker 
19*61c4878aSAndroid Build Coastguard Worker #include "pw_assert/check.h"
20*61c4878aSAndroid Build Coastguard Worker #include "pw_log/log.h"
21*61c4878aSAndroid Build Coastguard Worker #include "pw_preprocessor/compiler.h"
22*61c4878aSAndroid Build Coastguard Worker #include "pw_span/span.h"
23*61c4878aSAndroid Build Coastguard Worker #include "pw_tokenizer/base64.h"
24*61c4878aSAndroid Build Coastguard Worker 
25*61c4878aSAndroid Build Coastguard Worker namespace pw::metric {
26*61c4878aSAndroid Build Coastguard Worker namespace {
27*61c4878aSAndroid Build Coastguard Worker 
28*61c4878aSAndroid Build Coastguard Worker template <typename T>
AsSpan(const T & t)29*61c4878aSAndroid Build Coastguard Worker span<const std::byte> AsSpan(const T& t) {
30*61c4878aSAndroid Build Coastguard Worker   return span<const std::byte>(reinterpret_cast<const std::byte*>(&t),
31*61c4878aSAndroid Build Coastguard Worker                                sizeof(t));
32*61c4878aSAndroid Build Coastguard Worker }
33*61c4878aSAndroid Build Coastguard Worker 
34*61c4878aSAndroid Build Coastguard Worker // A convenience class to encode a token as base64 while managing the storage.
35*61c4878aSAndroid Build Coastguard Worker // TODO(keir): Consider putting this into upstream pw_tokenizer.
36*61c4878aSAndroid Build Coastguard Worker struct Base64EncodedToken {
Base64EncodedTokenpw::metric::__anonc6a00c4c0111::Base64EncodedToken37*61c4878aSAndroid Build Coastguard Worker   Base64EncodedToken(Token token) {
38*61c4878aSAndroid Build Coastguard Worker     size_t encoded_size = tokenizer::PrefixedBase64Encode(AsSpan(token), data);
39*61c4878aSAndroid Build Coastguard Worker     data[encoded_size] = 0;
40*61c4878aSAndroid Build Coastguard Worker   }
41*61c4878aSAndroid Build Coastguard Worker 
valuepw::metric::__anonc6a00c4c0111::Base64EncodedToken42*61c4878aSAndroid Build Coastguard Worker   const char* value() { return data.data(); }
43*61c4878aSAndroid Build Coastguard Worker   std::array<char, 16> data;
44*61c4878aSAndroid Build Coastguard Worker };
45*61c4878aSAndroid Build Coastguard Worker 
Indent(int level)46*61c4878aSAndroid Build Coastguard Worker const char* Indent(int level) {
47*61c4878aSAndroid Build Coastguard Worker   static const char* kWhitespace10 = "          ";
48*61c4878aSAndroid Build Coastguard Worker   level = std::min(level, 4);
49*61c4878aSAndroid Build Coastguard Worker   return kWhitespace10 + 8 - 2 * level;
50*61c4878aSAndroid Build Coastguard Worker }
51*61c4878aSAndroid Build Coastguard Worker 
52*61c4878aSAndroid Build Coastguard Worker }  // namespace
53*61c4878aSAndroid Build Coastguard Worker 
54*61c4878aSAndroid Build Coastguard Worker // Enable easier registration when used as a member.
Metric(Token name,float value,IntrusiveList<Metric> & metrics)55*61c4878aSAndroid Build Coastguard Worker Metric::Metric(Token name, float value, IntrusiveList<Metric>& metrics)
56*61c4878aSAndroid Build Coastguard Worker     : Metric(name, value) {
57*61c4878aSAndroid Build Coastguard Worker   metrics.push_front(*this);
58*61c4878aSAndroid Build Coastguard Worker }
Metric(Token name,uint32_t value,IntrusiveList<Metric> & metrics)59*61c4878aSAndroid Build Coastguard Worker Metric::Metric(Token name, uint32_t value, IntrusiveList<Metric>& metrics)
60*61c4878aSAndroid Build Coastguard Worker     : Metric(name, value) {
61*61c4878aSAndroid Build Coastguard Worker   metrics.push_front(*this);
62*61c4878aSAndroid Build Coastguard Worker }
63*61c4878aSAndroid Build Coastguard Worker 
as_float() const64*61c4878aSAndroid Build Coastguard Worker float Metric::as_float() const {
65*61c4878aSAndroid Build Coastguard Worker   PW_DCHECK(is_float());
66*61c4878aSAndroid Build Coastguard Worker   return float_;
67*61c4878aSAndroid Build Coastguard Worker }
68*61c4878aSAndroid Build Coastguard Worker 
as_int() const69*61c4878aSAndroid Build Coastguard Worker uint32_t Metric::as_int() const {
70*61c4878aSAndroid Build Coastguard Worker   PW_DCHECK(is_int());
71*61c4878aSAndroid Build Coastguard Worker   return uint_;
72*61c4878aSAndroid Build Coastguard Worker }
73*61c4878aSAndroid Build Coastguard Worker 
Increment(uint32_t amount)74*61c4878aSAndroid Build Coastguard Worker void Metric::Increment(uint32_t amount) {
75*61c4878aSAndroid Build Coastguard Worker   PW_DCHECK(is_int());
76*61c4878aSAndroid Build Coastguard Worker   if (PW_ADD_OVERFLOW(uint_, amount, &uint_)) {
77*61c4878aSAndroid Build Coastguard Worker     uint_ = std::numeric_limits<uint32_t>::max();
78*61c4878aSAndroid Build Coastguard Worker   }
79*61c4878aSAndroid Build Coastguard Worker }
80*61c4878aSAndroid Build Coastguard Worker 
Decrement(uint32_t amount)81*61c4878aSAndroid Build Coastguard Worker void Metric::Decrement(uint32_t amount) {
82*61c4878aSAndroid Build Coastguard Worker   PW_DCHECK(is_int());
83*61c4878aSAndroid Build Coastguard Worker   if (PW_SUB_OVERFLOW(uint_, amount, &uint_)) {
84*61c4878aSAndroid Build Coastguard Worker     uint_ = 0;
85*61c4878aSAndroid Build Coastguard Worker   }
86*61c4878aSAndroid Build Coastguard Worker }
87*61c4878aSAndroid Build Coastguard Worker 
SetInt(uint32_t value)88*61c4878aSAndroid Build Coastguard Worker void Metric::SetInt(uint32_t value) {
89*61c4878aSAndroid Build Coastguard Worker   PW_DCHECK(is_int());
90*61c4878aSAndroid Build Coastguard Worker   uint_ = value;
91*61c4878aSAndroid Build Coastguard Worker }
92*61c4878aSAndroid Build Coastguard Worker 
SetFloat(float value)93*61c4878aSAndroid Build Coastguard Worker void Metric::SetFloat(float value) {
94*61c4878aSAndroid Build Coastguard Worker   PW_DCHECK(is_float());
95*61c4878aSAndroid Build Coastguard Worker   float_ = value;
96*61c4878aSAndroid Build Coastguard Worker }
97*61c4878aSAndroid Build Coastguard Worker 
Dump(int level,bool last) const98*61c4878aSAndroid Build Coastguard Worker void Metric::Dump(int level, bool last) const {
99*61c4878aSAndroid Build Coastguard Worker   Base64EncodedToken encoded_name(name());
100*61c4878aSAndroid Build Coastguard Worker   const char* indent = Indent(level);
101*61c4878aSAndroid Build Coastguard Worker   const char* comma = last ? "" : ",";
102*61c4878aSAndroid Build Coastguard Worker   if (is_float()) {
103*61c4878aSAndroid Build Coastguard Worker     // Variadic macros promote float to double. Explicitly cast here to
104*61c4878aSAndroid Build Coastguard Worker     // acknowledge this and allow projects to use -Wdouble-promotion.
105*61c4878aSAndroid Build Coastguard Worker     PW_LOG_INFO("%s \"%s\": %f%s",
106*61c4878aSAndroid Build Coastguard Worker                 indent,
107*61c4878aSAndroid Build Coastguard Worker                 encoded_name.value(),
108*61c4878aSAndroid Build Coastguard Worker                 static_cast<double>(as_float()),
109*61c4878aSAndroid Build Coastguard Worker                 comma);
110*61c4878aSAndroid Build Coastguard Worker   } else {
111*61c4878aSAndroid Build Coastguard Worker     PW_LOG_INFO("%s \"%s\": %u%s",
112*61c4878aSAndroid Build Coastguard Worker                 indent,
113*61c4878aSAndroid Build Coastguard Worker                 encoded_name.value(),
114*61c4878aSAndroid Build Coastguard Worker                 static_cast<unsigned int>(as_int()),
115*61c4878aSAndroid Build Coastguard Worker                 comma);
116*61c4878aSAndroid Build Coastguard Worker   }
117*61c4878aSAndroid Build Coastguard Worker }
118*61c4878aSAndroid Build Coastguard Worker 
Dump(const IntrusiveList<Metric> & metrics,int level)119*61c4878aSAndroid Build Coastguard Worker void Metric::Dump(const IntrusiveList<Metric>& metrics, int level) {
120*61c4878aSAndroid Build Coastguard Worker   auto iter = metrics.begin();
121*61c4878aSAndroid Build Coastguard Worker   while (iter != metrics.end()) {
122*61c4878aSAndroid Build Coastguard Worker     const Metric& m = *iter++;
123*61c4878aSAndroid Build Coastguard Worker     m.Dump(level, iter == metrics.end());
124*61c4878aSAndroid Build Coastguard Worker   }
125*61c4878aSAndroid Build Coastguard Worker }
126*61c4878aSAndroid Build Coastguard Worker 
Group(Token name,IntrusiveList<Group> & groups)127*61c4878aSAndroid Build Coastguard Worker Group::Group(Token name, IntrusiveList<Group>& groups) : name_(name) {
128*61c4878aSAndroid Build Coastguard Worker   groups.push_front(*this);
129*61c4878aSAndroid Build Coastguard Worker }
130*61c4878aSAndroid Build Coastguard Worker 
Dump() const131*61c4878aSAndroid Build Coastguard Worker void Group::Dump() const {
132*61c4878aSAndroid Build Coastguard Worker   PW_LOG_INFO("{");
133*61c4878aSAndroid Build Coastguard Worker   Dump(0, true);
134*61c4878aSAndroid Build Coastguard Worker   PW_LOG_INFO("}");
135*61c4878aSAndroid Build Coastguard Worker }
136*61c4878aSAndroid Build Coastguard Worker 
Dump(int level,bool last) const137*61c4878aSAndroid Build Coastguard Worker void Group::Dump(int level, bool last) const {
138*61c4878aSAndroid Build Coastguard Worker   Base64EncodedToken encoded_name(name());
139*61c4878aSAndroid Build Coastguard Worker   const char* indent = Indent(level);
140*61c4878aSAndroid Build Coastguard Worker   const char* comma = last ? "" : ",";
141*61c4878aSAndroid Build Coastguard Worker   PW_LOG_INFO("%s\"%s\": {", indent, encoded_name.value());
142*61c4878aSAndroid Build Coastguard Worker   Group::Dump(children(), level + 1);
143*61c4878aSAndroid Build Coastguard Worker   Metric::Dump(metrics(), level + 1);
144*61c4878aSAndroid Build Coastguard Worker   PW_LOG_INFO("%s}%s", indent, comma);
145*61c4878aSAndroid Build Coastguard Worker }
146*61c4878aSAndroid Build Coastguard Worker 
Dump(const IntrusiveList<Group> & groups,int level)147*61c4878aSAndroid Build Coastguard Worker void Group::Dump(const IntrusiveList<Group>& groups, int level) {
148*61c4878aSAndroid Build Coastguard Worker   auto iter = groups.begin();
149*61c4878aSAndroid Build Coastguard Worker   while (iter != groups.end()) {
150*61c4878aSAndroid Build Coastguard Worker     const Group& g = *iter++;
151*61c4878aSAndroid Build Coastguard Worker     g.Dump(level, iter == groups.end());
152*61c4878aSAndroid Build Coastguard Worker   }
153*61c4878aSAndroid Build Coastguard Worker }
154*61c4878aSAndroid Build Coastguard Worker 
155*61c4878aSAndroid Build Coastguard Worker }  // namespace pw::metric
156