1 // Copyright 2018 The Chromium Authors. All rights reserved.
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/ukm/debug/ukm_debug_data_extractor.h"
6 
7 #include <inttypes.h>
8 
9 #include <map>
10 #include <utility>
11 #include <vector>
12 
13 #include "base/format_macros.h"
14 #include "base/strings/stringprintf.h"
15 #include "services/metrics/public/cpp/ukm_decode.h"
16 #include "services/metrics/public/cpp/ukm_source.h"
17 #include "services/metrics/public/mojom/ukm_interface.mojom.h"
18 #include "url/gurl.h"
19 
20 namespace ukm {
21 namespace debug {
22 
23 namespace {
24 
25 static const uint64_t BIT_FILTER_LAST32 = 0xffffffffULL;
26 
27 struct SourceData {
28   UkmSource* source;
29   std::vector<mojom::UkmEntry*> entries;
30 };
31 
GetName(const ukm::builders::EntryDecoder & decoder,uint64_t hash)32 std::string GetName(const ukm::builders::EntryDecoder& decoder, uint64_t hash) {
33   const auto it = decoder.metric_map.find(hash);
34   if (it == decoder.metric_map.end())
35     return base::StringPrintf("Unknown %" PRIu64, hash);
36   return it->second;
37 }
38 
ConvertEntryToValue(const ukm::builders::DecodeMap & decode_map,const mojom::UkmEntry & entry)39 base::Value ConvertEntryToValue(const ukm::builders::DecodeMap& decode_map,
40                                 const mojom::UkmEntry& entry) {
41   base::DictionaryValue entry_value;
42 
43   const auto it = decode_map.find(entry.event_hash);
44   if (it == decode_map.end()) {
45     entry_value.SetKey(
46         "name", UkmDebugDataExtractor::UInt64AsPairOfInt(entry.event_hash));
47   } else {
48     entry_value.SetKey("name", base::Value(it->second.name));
49 
50     base::ListValue metrics_list;
51     for (const auto& metric : entry.metrics) {
52       base::DictionaryValue metric_value;
53       metric_value.SetKey("name",
54                           base::Value(GetName(it->second, metric.first)));
55       metric_value.SetKey(
56           "value", UkmDebugDataExtractor::UInt64AsPairOfInt(metric.second));
57       metrics_list.Append(std::move(metric_value));
58     }
59     entry_value.SetKey("metrics", std::move(metrics_list));
60   }
61   return std::move(entry_value);
62 }
63 
64 }  // namespace
65 
66 UkmDebugDataExtractor::UkmDebugDataExtractor() = default;
67 
68 UkmDebugDataExtractor::~UkmDebugDataExtractor() = default;
69 
70 // static
UInt64AsPairOfInt(uint64_t v)71 base::Value UkmDebugDataExtractor::UInt64AsPairOfInt(uint64_t v) {
72   // Convert int64_t to pair of int. Passing int64_t in base::Value is not
73   // supported. The pair of int will be passed as a ListValue.
74   base::Value::ListStorage int_pair;
75   int_pair.push_back(
76       base::Value(static_cast<int>((v >> 32) & BIT_FILTER_LAST32)));
77   int_pair.push_back(base::Value(static_cast<int>(v & BIT_FILTER_LAST32)));
78   return base::Value(int_pair);
79 }
80 
81 // static
GetStructuredData(const UkmService * ukm_service)82 base::Value UkmDebugDataExtractor::GetStructuredData(
83     const UkmService* ukm_service) {
84   if (!ukm_service)
85     return {};
86 
87   base::DictionaryValue ukm_data;
88 
89   ukm_data.SetKey("state", base::Value(ukm_service->recording_enabled_));
90   ukm_data.SetKey("client_id", base::Value(base::StringPrintf(
91                                    "%016" PRIx64, ukm_service->client_id_)));
92   ukm_data.SetKey("session_id",
93                   base::Value(static_cast<int>(ukm_service->session_id_)));
94 
95   ukm_data.SetKey(
96       "is_sampling_enabled",
97       base::Value(static_cast<bool>(ukm_service->IsSamplingEnabled())));
98 
99   std::map<SourceId, SourceData> source_data;
100   for (const auto& kv : ukm_service->recordings_.sources) {
101     source_data[kv.first].source = kv.second.get();
102   }
103 
104   for (const auto& v : ukm_service->recordings_.entries) {
105     source_data[v->source_id].entries.push_back(v.get());
106   }
107 
108   base::ListValue sources_list;
109   for (const auto& kv : source_data) {
110     const auto* src = kv.second.source;
111 
112     base::DictionaryValue source_value;
113     if (src) {
114       source_value.SetKey("id",
115                           UkmDebugDataExtractor::UInt64AsPairOfInt(src->id()));
116       source_value.SetKey("url", base::Value(src->url().spec()));
117     } else {
118       source_value.SetKey("id",
119                           UkmDebugDataExtractor::UInt64AsPairOfInt(kv.first));
120     }
121 
122     base::ListValue entries_list;
123     for (auto* entry : kv.second.entries) {
124       entries_list.Append(
125           ConvertEntryToValue(ukm_service->decode_map_, *entry));
126     }
127 
128     source_value.SetKey("entries", std::move(entries_list));
129 
130     sources_list.Append(std::move(source_value));
131   }
132   ukm_data.SetKey("sources", std::move(sources_list));
133   return std::move(ukm_data);
134 }
135 
136 }  // namespace debug
137 }  // namespace ukm
138