Skip to content

Commit 7009bed

Browse files
Medha KiniCQ Bot Account
Medha Kini
authored and
CQ Bot Account
committed
pw_metric: Share MetricWalker
Moves the MetricWalker to a private header and introduces a virtual MetricWriter interface to share code common between the pwpb and nanopb MetricService implementations. Change-Id: Ibcb173e7ce5cb178a0ec66d07de6c44d1101b3b4 Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/101380 Reviewed-by: Keir Mierle <keir@google.com> Commit-Queue: Auto-Submit <auto-submit@pigweed.google.com.iam.gserviceaccount.com> Pigweed-Auto-Submit: Armando Montanez <amontanez@google.com>
1 parent b5f2fd7 commit 7009bed

File tree

5 files changed

+128
-112
lines changed

5 files changed

+128
-112
lines changed

pw_metric/BUILD.bazel

+18-6
Original file line numberDiff line numberDiff line change
@@ -50,26 +50,38 @@ pw_cc_library(
5050
],
5151
)
5252

53+
# Common MetricWalker/MetricWriter used by RPC service.
54+
pw_cc_library(
55+
name = "metric_walker",
56+
hdrs = ["pw_metric_private/metric_walker.h"],
57+
visibility = ["//visibility:private"],
58+
deps = [
59+
":pw_metric",
60+
"//pw_assert:assert",
61+
"//pw_containers",
62+
"//pw_status",
63+
"//pw_tokenizer",
64+
],
65+
)
66+
5367
pw_cc_library(
5468
name = "metric_service_nanopb",
5569
srcs = ["metric_service_nanopb.cc"],
56-
hdrs = [
57-
"public/pw_metric/metric_service_nanopb.h",
58-
],
70+
hdrs = ["public/pw_metric/metric_service_nanopb.h"],
5971
deps = [
6072
":metric",
73+
":metric_walker",
6174
],
6275
)
6376

6477
pw_cc_library(
6578
name = "metric_service_pwpb",
6679
srcs = ["metric_service_pwpb.cc"],
67-
hdrs = [
68-
"public/pw_metric/metric_service_pwpb.h",
69-
],
80+
hdrs = ["public/pw_metric/metric_service_pwpb.h"],
7081
deps = [
7182
":metric",
7283
":metric_proto_cc",
84+
":metric_walker",
7385
"//pw_assert",
7486
"//pw_bytes",
7587
"//pw_containers",

pw_metric/BUILD.gn

+15
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,19 @@ pw_proto_library("metric_service_proto") {
6262
# TODO(keir): Consider moving the nanopb service into the nanopb/ directory
6363
# instead of having it directly inside pw_metric/.
6464

65+
# Common MetricWalker/MetricWriter used by RPC service.
66+
pw_source_set("metric_walker") {
67+
visibility = [ ":*" ]
68+
public = [ "pw_metric_private/metric_walker.h" ]
69+
deps = [
70+
":pw_metric",
71+
"$dir_pw_assert:assert",
72+
"$dir_pw_containers",
73+
"$dir_pw_status",
74+
"$dir_pw_tokenizer",
75+
]
76+
}
77+
6578
if (dir_pw_third_party_nanopb != "") {
6679
pw_source_set("metric_service_nanopb") {
6780
public_configs = [ ":default_config" ]
@@ -73,6 +86,7 @@ if (dir_pw_third_party_nanopb != "") {
7386
public = [ "public/pw_metric/metric_service_nanopb.h" ]
7487
deps = [
7588
":metric_service_proto.nanopb_rpc",
89+
":metric_walker",
7690
"$dir_pw_containers:vector",
7791
dir_pw_tokenizer,
7892
]
@@ -94,6 +108,7 @@ pw_source_set("metric_service_pwpb") {
94108
public_deps = [
95109
":metric_service_proto.pwpb_rpc",
96110
":metric_service_proto.raw_rpc",
111+
":metric_walker",
97112
":pw_metric",
98113
"$dir_pw_bytes",
99114
"$dir_pw_containers",

pw_metric/metric_service_nanopb.cc

+12-52
Original file line numberDiff line numberDiff line change
@@ -19,23 +19,25 @@
1919
#include "pw_assert/check.h"
2020
#include "pw_containers/vector.h"
2121
#include "pw_metric/metric.h"
22+
#include "pw_metric_private/metric_walker.h"
2223
#include "pw_preprocessor/util.h"
2324
#include "pw_span/span.h"
2425

2526
namespace pw::metric {
2627
namespace {
2728

28-
class MetricWriter {
29+
class NanopbMetricWriter : public virtual internal::MetricWriter {
2930
public:
30-
MetricWriter(MetricService::ServerWriter<pw_metric_proto_MetricResponse>&
31-
response_writer)
31+
NanopbMetricWriter(
32+
MetricService::ServerWriter<pw_metric_proto_MetricResponse>&
33+
response_writer)
3234
: response_(pw_metric_proto_MetricResponse_init_zero),
3335
response_writer_(response_writer) {}
3436

3537
// TODO(keir): Figure out a pw_rpc mechanism to fill a streaming packet based
3638
// on transport MTU, rather than having this as a static knob. For example,
3739
// some transports may be able to fit 30 metrics; others, only 5.
38-
void Write(const Metric& metric, const Vector<Token>& path) {
40+
Status Write(const Metric& metric, const Vector<Token>& path) override {
3941
// Nanopb doesn't offer an easy way to do bounds checking, so use span's
4042
// type deduction magic to figure out the max size.
4143
span<pw_metric_proto_Metric> metrics(response_.metrics);
@@ -68,6 +70,8 @@ class MetricWriter {
6870
if (response_.metrics_count == metrics.size()) {
6971
Flush();
7072
}
73+
74+
return OkStatus();
7175
}
7276

7377
void Flush() {
@@ -84,58 +88,14 @@ class MetricWriter {
8488
MetricService::ServerWriter<pw_metric_proto_MetricResponse>& response_writer_;
8589
};
8690

87-
// Walk a metric tree recursively; passing metrics with their path (names) to a
88-
// metric writer which can consume them.
89-
//
90-
// TODO(keir): Generalize this to support a generic visitor.
91-
class MetricWalker {
92-
public:
93-
MetricWalker(MetricWriter& writer) : writer_(writer) {}
94-
95-
void Walk(const IntrusiveList<Metric>& metrics) {
96-
for (const auto& m : metrics) {
97-
ScopedName scoped_name(m.name(), *this);
98-
writer_.Write(m, path_);
99-
}
100-
}
101-
102-
void Walk(const IntrusiveList<Group>& groups) {
103-
for (const auto& g : groups) {
104-
Walk(g);
105-
}
106-
}
107-
108-
void Walk(const Group& group) {
109-
ScopedName scoped_name(group.name(), *this);
110-
Walk(group.children());
111-
Walk(group.metrics());
112-
}
113-
114-
private:
115-
// Exists to safely push/pop parent groups from the explicit stack.
116-
struct ScopedName {
117-
ScopedName(Token name, MetricWalker& rhs) : walker(rhs) {
118-
PW_CHECK_INT_LT(walker.path_.size(),
119-
walker.path_.capacity(),
120-
"Metrics are too deep; bump path_ capacity");
121-
walker.path_.push_back(name);
122-
}
123-
~ScopedName() { walker.path_.pop_back(); }
124-
MetricWalker& walker;
125-
};
126-
127-
Vector<Token, 4 /* max depth */> path_;
128-
MetricWriter& writer_;
129-
};
130-
13191
} // namespace
13292

13393
void MetricService::Get(
13494
const pw_metric_proto_MetricRequest& /* request */,
13595
ServerWriter<pw_metric_proto_MetricResponse>& response) {
13696
// For now, ignore the request and just stream all the metrics back.
137-
MetricWriter writer(response);
138-
MetricWalker walker(writer);
97+
NanopbMetricWriter writer(response);
98+
internal::MetricWalker walker(writer);
13999

140100
// This will stream all the metrics in the span of this Get() method call.
141101
// This will have the effect of blocking the RPC thread until all the metrics
@@ -144,8 +104,8 @@ void MetricService::Get(
144104
//
145105
// In the future, this should be replaced with an optional async solution
146106
// that puts the application in control of when the response batches are sent.
147-
walker.Walk(metrics_);
148-
walker.Walk(groups_);
107+
walker.Walk(metrics_).IgnoreError();
108+
walker.Walk(groups_).IgnoreError();
149109
writer.Flush();
150110
}
151111

pw_metric/metric_service_pwpb.cc

+8-54
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "pw_assert/check.h"
2020
#include "pw_containers/vector.h"
2121
#include "pw_metric/metric.h"
22+
#include "pw_metric_private/metric_walker.h"
2223
#include "pw_metric_proto/metric_service.pwpb.h"
2324
#include "pw_preprocessor/util.h"
2425
#include "pw_rpc/raw/server_reader_writer.h"
@@ -33,17 +34,18 @@ constexpr size_t kMaxNumPackedEntries = 3;
3334

3435
namespace {
3536

36-
class MetricWriter {
37+
class PwpbMetricWriter : public virtual internal::MetricWriter {
3738
public:
38-
MetricWriter(span<std::byte> response, rpc::RawServerWriter& response_writer)
39+
PwpbMetricWriter(span<std::byte> response,
40+
rpc::RawServerWriter& response_writer)
3941
: response_(response),
4042
response_writer_(response_writer),
4143
encoder_(response) {}
4244

4345
// TODO(keir): Figure out a pw_rpc mechanism to fill a streaming packet based
4446
// on transport MTU, rather than having this as a static knob. For example,
4547
// some transports may be able to fit 30 metrics; others, only 5.
46-
Status Write(const Metric& metric, const Vector<Token>& path) {
48+
Status Write(const Metric& metric, const Vector<Token>& path) override {
4749
{ // Scope to control proto_encoder lifetime.
4850

4951
// Grab the next available Metric slot to write to in the response.
@@ -86,54 +88,6 @@ class MetricWriter {
8688
proto::MetricRequest::MemoryEncoder encoder_;
8789
size_t metrics_count = 0;
8890
};
89-
90-
// Walk a metric tree recursively; passing metrics with their path (names) to a
91-
// metric writer which can consume them.
92-
//
93-
// TODO(keir): Generalize this to support a generic visitor.
94-
class MetricWalker {
95-
public:
96-
MetricWalker(MetricWriter& writer) : writer_(writer) {}
97-
98-
Status Walk(const IntrusiveList<Metric>& metrics) {
99-
for (const auto& m : metrics) {
100-
ScopedName scoped_name(m.name(), *this);
101-
PW_TRY(writer_.Write(m, path_));
102-
}
103-
return OkStatus();
104-
}
105-
106-
Status Walk(const IntrusiveList<Group>& groups) {
107-
for (const auto& g : groups) {
108-
PW_TRY(Walk(g));
109-
}
110-
return OkStatus();
111-
}
112-
113-
Status Walk(const Group& group) {
114-
ScopedName scoped_name(group.name(), *this);
115-
PW_TRY(Walk(group.children()));
116-
PW_TRY(Walk(group.metrics()));
117-
return OkStatus();
118-
}
119-
120-
private:
121-
// Exists to safely push/pop parent groups from the explicit stack.
122-
struct ScopedName {
123-
ScopedName(Token name, MetricWalker& rhs) : walker(rhs) {
124-
PW_CHECK_INT_LT(walker.path_.size(),
125-
walker.path_.capacity(),
126-
"Metrics are too deep; bump path_ capacity");
127-
walker.path_.push_back(name);
128-
}
129-
~ScopedName() { walker.path_.pop_back(); }
130-
MetricWalker& walker;
131-
};
132-
133-
Vector<Token, 4 /* max depth */> path_;
134-
MetricWriter& writer_;
135-
};
136-
13791
} // namespace
13892

13993
void MetricService::Get(ConstByteSpan /*request*/,
@@ -147,8 +101,8 @@ void MetricService::Get(ConstByteSpan /*request*/,
147101

148102
std::array<std::byte, kEncodeBufferSize> encode_buffer;
149103

150-
MetricWriter writer(encode_buffer, raw_response);
151-
MetricWalker walker(writer);
104+
PwpbMetricWriter writer(encode_buffer, raw_response);
105+
internal::MetricWalker walker(writer);
152106

153107
// This will stream all the metrics in the span of this Get() method call.
154108
// This will have the effect of blocking the RPC thread until all the metrics
@@ -159,7 +113,7 @@ void MetricService::Get(ConstByteSpan /*request*/,
159113
// that puts the application in control of when the response batches are sent.
160114

161115
// Propagate status through walker.
162-
auto status = OkStatus();
116+
Status status;
163117
status.Update(walker.Walk(metrics_));
164118
status.Update(walker.Walk(groups_));
165119
status.Update(writer.Flush());
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
// Copyright 2022 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+
#pragma once
15+
16+
#include "pw_assert/check.h"
17+
#include "pw_containers/intrusive_list.h"
18+
#include "pw_containers/vector.h"
19+
#include "pw_metric/metric.h"
20+
#include "pw_status/status.h"
21+
#include "pw_tokenizer/tokenize.h"
22+
23+
namespace pw::metric::internal {
24+
25+
class MetricWriter {
26+
public:
27+
virtual ~MetricWriter() = default;
28+
virtual Status Write(const Metric& metric, const Vector<Token>& path) = 0;
29+
};
30+
31+
// Walk a metric tree recursively; passing metrics with their path (names) to a
32+
// MetricWriter that can consume them.
33+
class MetricWalker {
34+
public:
35+
MetricWalker(MetricWriter& writer) : writer_(writer) {}
36+
37+
Status Walk(const IntrusiveList<Metric>& metrics) {
38+
for (const auto& m : metrics) {
39+
ScopedName scoped_name(m.name(), *this);
40+
PW_TRY(writer_.Write(m, path_));
41+
}
42+
return OkStatus();
43+
}
44+
45+
Status Walk(const IntrusiveList<Group>& groups) {
46+
for (const auto& g : groups) {
47+
PW_TRY(Walk(g));
48+
}
49+
return OkStatus();
50+
}
51+
52+
Status Walk(const Group& group) {
53+
ScopedName scoped_name(group.name(), *this);
54+
PW_TRY(Walk(group.children()));
55+
PW_TRY(Walk(group.metrics()));
56+
return OkStatus();
57+
}
58+
59+
private:
60+
// Exists to safely push/pop parent groups from the explicit stack.
61+
struct ScopedName {
62+
ScopedName(Token name, MetricWalker& rhs) : walker(rhs) {
63+
// Metrics are too deep; bump path_ capacity.
64+
PW_ASSERT(walker.path_.size() < walker.path_.capacity());
65+
walker.path_.push_back(name);
66+
}
67+
~ScopedName() { walker.path_.pop_back(); }
68+
MetricWalker& walker;
69+
};
70+
71+
Vector<Token, /*capacity=*/4> path_;
72+
MetricWriter& writer_;
73+
};
74+
75+
} // namespace pw::metric::internal

0 commit comments

Comments
 (0)