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