1 // Copyright 2023 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "components/metrics/debug/structured/structured_metrics_debug_provider.h"
6 
7 #include <optional>
8 #include <utility>
9 
10 #include "base/i18n/number_formatting.h"
11 #include "base/logging.h"
12 #include "base/memory/raw_ptr.h"
13 #include "base/strings/string_util.h"
14 #include "components/metrics/structured/event_validator.h"
15 #include "components/metrics/structured/project_validator.h"
16 #include "components/metrics/structured/proto/event_storage.pb.h"
17 #include "components/metrics/structured/recorder.h"
18 #include "components/metrics/structured/structured_metrics_service.h"
19 #include "components/metrics/structured/structured_metrics_validator.h"
20 #include "third_party/metrics_proto/structured_data.pb.h"
21 
22 namespace metrics::structured {
23 namespace {
24 
25 struct EventInfo {
26   base::StringPiece project_name;
27   base::StringPiece event_name;
28   raw_ptr<const EventValidator> event_validator;
29 
30   // Normalizes the name into an easier to read format as defined in the
31   // structured.xml.
32   std::string NormalizeProjectName() const;
33   std::string NormalizeEventName() const;
34 };
35 
Normalize(base::StringPiece value)36 std::string Normalize(base::StringPiece value) {
37   std::string result;
38   base::ReplaceChars(value, "_", ".", &result);
39   return result;
40 }
41 
NormalizeProjectName() const42 std::string EventInfo::NormalizeProjectName() const {
43   return Normalize(project_name);
44 }
45 
NormalizeEventName() const46 std::string EventInfo::NormalizeEventName() const {
47   return Normalize(event_name);
48 }
49 
50 // Retrieves information about an event that is needed for rendering.
GetEventInfo(const StructuredEventProto & proto)51 std::optional<EventInfo> GetEventInfo(const StructuredEventProto& proto) {
52   validator::Validators* validators = validator::Validators::Get();
53   auto project_name = validators->GetProjectName(proto.project_name_hash());
54   if (!project_name.has_value()) {
55     return std::nullopt;
56   }
57 
58   // This will not fail.
59   const auto* project_validator =
60       validators->GetProjectValidator(*project_name);
61   CHECK(project_validator);
62 
63   const auto event_name =
64       project_validator->GetEventName(proto.event_name_hash());
65   if (!event_name.has_value()) {
66     return std::nullopt;
67   }
68 
69   // This will not fail.
70   const auto* event_validator =
71       project_validator->GetEventValidator(*event_name);
72   CHECK(event_validator);
73 
74   return EventInfo{.project_name = *project_name,
75                    .event_name = *event_name,
76                    .event_validator = event_validator};
77 }
78 
79 // Creates a dictionary that represents a key-value pair.
CreateKeyValue(base::StringPiece key,base::Value value)80 base::Value::Dict CreateKeyValue(base::StringPiece key, base::Value value) {
81   base::Value::Dict result;
82   result.Set("key", key);
83   result.Set("value", std::move(value));
84   return result;
85 }
86 
MetricToValue(const StructuredEventProto::Metric & metric)87 std::optional<base::Value> MetricToValue(
88     const StructuredEventProto::Metric& metric) {
89   using Metric = StructuredEventProto::Metric;
90   switch (metric.value_case()) {
91     case Metric::kValueHmac:
92       return base::Value(base::NumberToString(metric.value_hmac()));
93     case Metric::kValueInt64:
94       return base::Value(base::NumberToString(metric.value_int64()));
95     case Metric::kValueString:
96       return base::Value(metric.value_string());
97     case Metric::kValueDouble:
98       return base::Value(metric.value_double());
99     case Metric::kValueRepeatedInt64: {
100       base::Value::List list;
101       for (int value : metric.value_repeated_int64().values()) {
102         list.Append(value);
103       }
104       return base::Value(std::move(list));
105     }
106     case Metric::VALUE_NOT_SET:
107       return std::nullopt;
108   }
109 }
110 
111 // Creates a list of metrics represented by a key-value pair from the metrics of
112 // an event.
CreateMetricsList(const google::protobuf::RepeatedPtrField<StructuredEventProto::Metric> & metrics,const EventValidator * event_validator)113 base::Value::List CreateMetricsList(const google::protobuf::RepeatedPtrField<
114                                         StructuredEventProto::Metric>& metrics,
115                                     const EventValidator* event_validator) {
116   base::Value::List result;
117   for (const auto& metric : metrics) {
118     std::string metric_name =
119         event_validator
120             ? std::string(event_validator->GetMetricName(metric.name_hash())
121                               .value_or("unknown"))
122             : base::NumberToString(metric.name_hash());
123     auto value = MetricToValue(metric);
124     if (!value.has_value()) {
125       continue;
126     }
127     result.Append(CreateKeyValue(metric_name, std::move(*value)));
128   }
129   return result;
130 }
131 
132 // Creates an event metadata dictionary from an event.
CreateEventMetadataDict(const StructuredEventProto::EventSequenceMetadata & sequence_metadata)133 base::Value::Dict CreateEventMetadataDict(
134     const StructuredEventProto::EventSequenceMetadata& sequence_metadata) {
135   base::Value::Dict metadata;
136   metadata.Set("systemUptimeMs",
137                base::FormatNumber(sequence_metadata.system_uptime()));
138   metadata.Set("id", base::NumberToString(sequence_metadata.event_unique_id()));
139   metadata.Set("resetCounter",
140                base::NumberToString(sequence_metadata.reset_counter()));
141   return metadata;
142 }
143 
144 // Creates a dictionary from an event.
CreateEventDict(const StructuredEventProto & proto)145 base::Value::Dict CreateEventDict(const StructuredEventProto& proto) {
146   base::Value::Dict result;
147 
148   auto event_info = GetEventInfo(proto);
149   const EventValidator* event_validator = nullptr;
150 
151   if (event_info.has_value()) {
152     event_validator = event_info->event_validator;
153     result.Set("project", event_info->NormalizeProjectName());
154     result.Set("event", event_info->NormalizeEventName());
155   } else {
156     result.Set("project", base::NumberToString(proto.project_name_hash()));
157     result.Set("event", base::NumberToString(proto.event_name_hash()));
158   }
159 
160   result.Set("metrics", CreateMetricsList(proto.metrics(), event_validator));
161 
162   if (proto.event_type() == StructuredEventProto::SEQUENCE) {
163     result.Set("type", "sequence");
164     result.Set("sequenceMetadata",
165                CreateEventMetadataDict(proto.event_sequence_metadata()));
166   } else {
167     result.Set("type", "metric");
168   }
169 
170   return result;
171 }
172 
173 }  // namespace
174 
StructuredMetricsDebugProvider(StructuredMetricsService * service)175 StructuredMetricsDebugProvider::StructuredMetricsDebugProvider(
176     StructuredMetricsService* service)
177     : service_(service) {
178   CHECK(service);
179   LoadRecordedEvents();
180   service_->recorder()->AddEventsObserver(this);
181 }
182 
~StructuredMetricsDebugProvider()183 StructuredMetricsDebugProvider::~StructuredMetricsDebugProvider() {
184   service_->recorder()->RemoveEventsObserver(this);
185 }
186 
OnEventRecorded(const StructuredEventProto & event)187 void StructuredMetricsDebugProvider::OnEventRecorded(
188     const StructuredEventProto& event) {
189   events_.Append(CreateEventDict(event));
190 }
191 
LoadRecordedEvents()192 void StructuredMetricsDebugProvider::LoadRecordedEvents() {
193   EventsProto proto;
194   service_->recorder()->event_storage()->CopyEvents(&proto);
195   for (const auto& event : proto.events()) {
196     events_.Append(CreateEventDict(event));
197   }
198 }
199 
200 }  // namespace metrics::structured
201