xref: /aosp_15_r20/external/pigweed/pw_metric/metric_service_pwpb.cc (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1*61c4878aSAndroid Build Coastguard Worker // Copyright 2022 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_service_pwpb.h"
16*61c4878aSAndroid Build Coastguard Worker 
17*61c4878aSAndroid Build Coastguard Worker #include <cstring>
18*61c4878aSAndroid Build Coastguard Worker 
19*61c4878aSAndroid Build Coastguard Worker #include "pw_assert/check.h"
20*61c4878aSAndroid Build Coastguard Worker #include "pw_containers/vector.h"
21*61c4878aSAndroid Build Coastguard Worker #include "pw_metric/metric.h"
22*61c4878aSAndroid Build Coastguard Worker #include "pw_metric_private/metric_walker.h"
23*61c4878aSAndroid Build Coastguard Worker #include "pw_metric_proto/metric_service.pwpb.h"
24*61c4878aSAndroid Build Coastguard Worker #include "pw_preprocessor/util.h"
25*61c4878aSAndroid Build Coastguard Worker #include "pw_rpc/raw/server_reader_writer.h"
26*61c4878aSAndroid Build Coastguard Worker #include "pw_span/span.h"
27*61c4878aSAndroid Build Coastguard Worker #include "pw_status/status.h"
28*61c4878aSAndroid Build Coastguard Worker #include "pw_status/try.h"
29*61c4878aSAndroid Build Coastguard Worker 
30*61c4878aSAndroid Build Coastguard Worker namespace pw::metric {
31*61c4878aSAndroid Build Coastguard Worker 
32*61c4878aSAndroid Build Coastguard Worker // TODO(amontanez): Make this follow the metric_service.options configuration.
33*61c4878aSAndroid Build Coastguard Worker constexpr size_t kMaxNumPackedEntries = 3;
34*61c4878aSAndroid Build Coastguard Worker 
35*61c4878aSAndroid Build Coastguard Worker namespace {
36*61c4878aSAndroid Build Coastguard Worker 
37*61c4878aSAndroid Build Coastguard Worker class PwpbMetricWriter : public virtual internal::MetricWriter {
38*61c4878aSAndroid Build Coastguard Worker  public:
PwpbMetricWriter(span<std::byte> response,rpc::RawServerWriter & response_writer)39*61c4878aSAndroid Build Coastguard Worker   PwpbMetricWriter(span<std::byte> response,
40*61c4878aSAndroid Build Coastguard Worker                    rpc::RawServerWriter& response_writer)
41*61c4878aSAndroid Build Coastguard Worker       : response_(response),
42*61c4878aSAndroid Build Coastguard Worker         response_writer_(response_writer),
43*61c4878aSAndroid Build Coastguard Worker         encoder_(response) {}
44*61c4878aSAndroid Build Coastguard Worker 
45*61c4878aSAndroid Build Coastguard Worker   // TODO(keir): Figure out a pw_rpc mechanism to fill a streaming packet based
46*61c4878aSAndroid Build Coastguard Worker   // on transport MTU, rather than having this as a static knob. For example,
47*61c4878aSAndroid Build Coastguard Worker   // some transports may be able to fit 30 metrics; others, only 5.
Write(const Metric & metric,const Vector<Token> & path)48*61c4878aSAndroid Build Coastguard Worker   Status Write(const Metric& metric, const Vector<Token>& path) override {
49*61c4878aSAndroid Build Coastguard Worker     {  // Scope to control proto_encoder lifetime.
50*61c4878aSAndroid Build Coastguard Worker 
51*61c4878aSAndroid Build Coastguard Worker       // Grab the next available Metric slot to write to in the response.
52*61c4878aSAndroid Build Coastguard Worker       proto::pwpb::Metric::StreamEncoder proto_encoder =
53*61c4878aSAndroid Build Coastguard Worker           encoder_.GetMetricsEncoder();
54*61c4878aSAndroid Build Coastguard Worker       PW_TRY(proto_encoder.WriteTokenPath(path));
55*61c4878aSAndroid Build Coastguard Worker       // Encode the metric value.
56*61c4878aSAndroid Build Coastguard Worker       if (metric.is_float()) {
57*61c4878aSAndroid Build Coastguard Worker         PW_TRY(proto_encoder.WriteAsFloat(metric.as_float()));
58*61c4878aSAndroid Build Coastguard Worker       } else {
59*61c4878aSAndroid Build Coastguard Worker         PW_TRY(proto_encoder.WriteAsInt(metric.as_int()));
60*61c4878aSAndroid Build Coastguard Worker       }
61*61c4878aSAndroid Build Coastguard Worker 
62*61c4878aSAndroid Build Coastguard Worker       metrics_count++;
63*61c4878aSAndroid Build Coastguard Worker     }
64*61c4878aSAndroid Build Coastguard Worker 
65*61c4878aSAndroid Build Coastguard Worker     if (metrics_count == kMaxNumPackedEntries) {
66*61c4878aSAndroid Build Coastguard Worker       return Flush();
67*61c4878aSAndroid Build Coastguard Worker     }
68*61c4878aSAndroid Build Coastguard Worker     return OkStatus();
69*61c4878aSAndroid Build Coastguard Worker   }
70*61c4878aSAndroid Build Coastguard Worker 
Flush()71*61c4878aSAndroid Build Coastguard Worker   Status Flush() {
72*61c4878aSAndroid Build Coastguard Worker     Status status;
73*61c4878aSAndroid Build Coastguard Worker     if (metrics_count) {
74*61c4878aSAndroid Build Coastguard Worker       status = response_writer_.Write(encoder_);
75*61c4878aSAndroid Build Coastguard Worker       // Different way to clear MemoryEncoder. Copy constructor is disabled
76*61c4878aSAndroid Build Coastguard Worker       // for memory encoder, and there is no "clear()" method.
77*61c4878aSAndroid Build Coastguard Worker       encoder_.~MemoryEncoder();
78*61c4878aSAndroid Build Coastguard Worker       new (&encoder_) proto::pwpb::MetricRequest::MemoryEncoder(response_);
79*61c4878aSAndroid Build Coastguard Worker       metrics_count = 0;
80*61c4878aSAndroid Build Coastguard Worker     }
81*61c4878aSAndroid Build Coastguard Worker     return status;
82*61c4878aSAndroid Build Coastguard Worker   }
83*61c4878aSAndroid Build Coastguard Worker 
84*61c4878aSAndroid Build Coastguard Worker  private:
85*61c4878aSAndroid Build Coastguard Worker   span<std::byte> response_;
86*61c4878aSAndroid Build Coastguard Worker   // This RPC stream writer handle must be valid for the metric writer
87*61c4878aSAndroid Build Coastguard Worker   // lifetime.
88*61c4878aSAndroid Build Coastguard Worker   rpc::RawServerWriter& response_writer_;
89*61c4878aSAndroid Build Coastguard Worker   proto::pwpb::MetricRequest::MemoryEncoder encoder_;
90*61c4878aSAndroid Build Coastguard Worker   size_t metrics_count = 0;
91*61c4878aSAndroid Build Coastguard Worker };
92*61c4878aSAndroid Build Coastguard Worker }  // namespace
93*61c4878aSAndroid Build Coastguard Worker 
Get(ConstByteSpan,rpc::RawServerWriter & raw_response)94*61c4878aSAndroid Build Coastguard Worker void MetricService::Get(ConstByteSpan /*request*/,
95*61c4878aSAndroid Build Coastguard Worker                         rpc::RawServerWriter& raw_response) {
96*61c4878aSAndroid Build Coastguard Worker   // For now, ignore the request and just stream all the metrics back.
97*61c4878aSAndroid Build Coastguard Worker   // TODO(amontanez): Make this follow the metric_service.options configuration.
98*61c4878aSAndroid Build Coastguard Worker   constexpr size_t kSizeOfOneMetric =
99*61c4878aSAndroid Build Coastguard Worker       pw::metric::proto::pwpb::MetricResponse::kMaxEncodedSizeBytes +
100*61c4878aSAndroid Build Coastguard Worker       pw::metric::proto::pwpb::Metric::kMaxEncodedSizeBytes;
101*61c4878aSAndroid Build Coastguard Worker   constexpr size_t kEncodeBufferSize = kMaxNumPackedEntries * kSizeOfOneMetric;
102*61c4878aSAndroid Build Coastguard Worker 
103*61c4878aSAndroid Build Coastguard Worker   std::array<std::byte, kEncodeBufferSize> encode_buffer;
104*61c4878aSAndroid Build Coastguard Worker 
105*61c4878aSAndroid Build Coastguard Worker   PwpbMetricWriter writer(encode_buffer, raw_response);
106*61c4878aSAndroid Build Coastguard Worker   internal::MetricWalker walker(writer);
107*61c4878aSAndroid Build Coastguard Worker 
108*61c4878aSAndroid Build Coastguard Worker   // This will stream all the metrics in the span of this Get() method call.
109*61c4878aSAndroid Build Coastguard Worker   // This will have the effect of blocking the RPC thread until all the metrics
110*61c4878aSAndroid Build Coastguard Worker   // are sent. That is likely to cause problems if there are many metrics, or
111*61c4878aSAndroid Build Coastguard Worker   // if other RPCs are higher priority and should complete first.
112*61c4878aSAndroid Build Coastguard Worker   //
113*61c4878aSAndroid Build Coastguard Worker   // In the future, this should be replaced with an optional async solution
114*61c4878aSAndroid Build Coastguard Worker   // that puts the application in control of when the response batches are sent.
115*61c4878aSAndroid Build Coastguard Worker 
116*61c4878aSAndroid Build Coastguard Worker   // Propagate status through walker.
117*61c4878aSAndroid Build Coastguard Worker   Status status;
118*61c4878aSAndroid Build Coastguard Worker   status.Update(walker.Walk(metrics_));
119*61c4878aSAndroid Build Coastguard Worker   status.Update(walker.Walk(groups_));
120*61c4878aSAndroid Build Coastguard Worker   status.Update(writer.Flush());
121*61c4878aSAndroid Build Coastguard Worker   raw_response.Finish(status).IgnoreError();
122*61c4878aSAndroid Build Coastguard Worker }
123*61c4878aSAndroid Build Coastguard Worker }  // namespace pw::metric
124