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