1*6777b538SAndroid Build Coastguard Worker // Copyright 2020 The Chromium Authors
2*6777b538SAndroid Build Coastguard Worker // Use of this source code is governed by a BSD-style license that can be
3*6777b538SAndroid Build Coastguard Worker // found in the LICENSE file.
4*6777b538SAndroid Build Coastguard Worker
5*6777b538SAndroid Build Coastguard Worker #include "net/dns/httpssvc_metrics.h"
6*6777b538SAndroid Build Coastguard Worker
7*6777b538SAndroid Build Coastguard Worker #include <string_view>
8*6777b538SAndroid Build Coastguard Worker
9*6777b538SAndroid Build Coastguard Worker #include "base/containers/contains.h"
10*6777b538SAndroid Build Coastguard Worker #include "base/feature_list.h"
11*6777b538SAndroid Build Coastguard Worker #include "base/metrics/histogram.h"
12*6777b538SAndroid Build Coastguard Worker #include "base/metrics/histogram_base.h"
13*6777b538SAndroid Build Coastguard Worker #include "base/metrics/histogram_functions.h"
14*6777b538SAndroid Build Coastguard Worker #include "base/notreached.h"
15*6777b538SAndroid Build Coastguard Worker #include "base/numerics/clamped_math.h"
16*6777b538SAndroid Build Coastguard Worker #include "base/strings/string_split.h"
17*6777b538SAndroid Build Coastguard Worker #include "base/strings/string_util.h"
18*6777b538SAndroid Build Coastguard Worker #include "net/base/features.h"
19*6777b538SAndroid Build Coastguard Worker #include "net/dns/dns_util.h"
20*6777b538SAndroid Build Coastguard Worker #include "net/dns/public/dns_protocol.h"
21*6777b538SAndroid Build Coastguard Worker
22*6777b538SAndroid Build Coastguard Worker namespace net {
23*6777b538SAndroid Build Coastguard Worker
TranslateDnsRcodeForHttpssvcExperiment(uint8_t rcode)24*6777b538SAndroid Build Coastguard Worker enum HttpssvcDnsRcode TranslateDnsRcodeForHttpssvcExperiment(uint8_t rcode) {
25*6777b538SAndroid Build Coastguard Worker switch (rcode) {
26*6777b538SAndroid Build Coastguard Worker case dns_protocol::kRcodeNOERROR:
27*6777b538SAndroid Build Coastguard Worker return HttpssvcDnsRcode::kNoError;
28*6777b538SAndroid Build Coastguard Worker case dns_protocol::kRcodeFORMERR:
29*6777b538SAndroid Build Coastguard Worker return HttpssvcDnsRcode::kFormErr;
30*6777b538SAndroid Build Coastguard Worker case dns_protocol::kRcodeSERVFAIL:
31*6777b538SAndroid Build Coastguard Worker return HttpssvcDnsRcode::kServFail;
32*6777b538SAndroid Build Coastguard Worker case dns_protocol::kRcodeNXDOMAIN:
33*6777b538SAndroid Build Coastguard Worker return HttpssvcDnsRcode::kNxDomain;
34*6777b538SAndroid Build Coastguard Worker case dns_protocol::kRcodeNOTIMP:
35*6777b538SAndroid Build Coastguard Worker return HttpssvcDnsRcode::kNotImp;
36*6777b538SAndroid Build Coastguard Worker case dns_protocol::kRcodeREFUSED:
37*6777b538SAndroid Build Coastguard Worker return HttpssvcDnsRcode::kRefused;
38*6777b538SAndroid Build Coastguard Worker default:
39*6777b538SAndroid Build Coastguard Worker return HttpssvcDnsRcode::kUnrecognizedRcode;
40*6777b538SAndroid Build Coastguard Worker }
41*6777b538SAndroid Build Coastguard Worker NOTREACHED();
42*6777b538SAndroid Build Coastguard Worker }
43*6777b538SAndroid Build Coastguard Worker
HttpssvcMetrics(bool secure)44*6777b538SAndroid Build Coastguard Worker HttpssvcMetrics::HttpssvcMetrics(bool secure) : secure_(secure) {}
45*6777b538SAndroid Build Coastguard Worker
~HttpssvcMetrics()46*6777b538SAndroid Build Coastguard Worker HttpssvcMetrics::~HttpssvcMetrics() {
47*6777b538SAndroid Build Coastguard Worker RecordMetrics();
48*6777b538SAndroid Build Coastguard Worker }
49*6777b538SAndroid Build Coastguard Worker
SaveForAddressQuery(base::TimeDelta resolve_time,enum HttpssvcDnsRcode rcode)50*6777b538SAndroid Build Coastguard Worker void HttpssvcMetrics::SaveForAddressQuery(base::TimeDelta resolve_time,
51*6777b538SAndroid Build Coastguard Worker enum HttpssvcDnsRcode rcode) {
52*6777b538SAndroid Build Coastguard Worker address_resolve_times_.push_back(resolve_time);
53*6777b538SAndroid Build Coastguard Worker
54*6777b538SAndroid Build Coastguard Worker if (rcode != HttpssvcDnsRcode::kNoError)
55*6777b538SAndroid Build Coastguard Worker disqualified_ = true;
56*6777b538SAndroid Build Coastguard Worker }
57*6777b538SAndroid Build Coastguard Worker
SaveAddressQueryFailure()58*6777b538SAndroid Build Coastguard Worker void HttpssvcMetrics::SaveAddressQueryFailure() {
59*6777b538SAndroid Build Coastguard Worker disqualified_ = true;
60*6777b538SAndroid Build Coastguard Worker }
61*6777b538SAndroid Build Coastguard Worker
SaveForHttps(enum HttpssvcDnsRcode rcode,const std::vector<bool> & condensed_records,base::TimeDelta https_resolve_time)62*6777b538SAndroid Build Coastguard Worker void HttpssvcMetrics::SaveForHttps(enum HttpssvcDnsRcode rcode,
63*6777b538SAndroid Build Coastguard Worker const std::vector<bool>& condensed_records,
64*6777b538SAndroid Build Coastguard Worker base::TimeDelta https_resolve_time) {
65*6777b538SAndroid Build Coastguard Worker DCHECK(!rcode_https_.has_value());
66*6777b538SAndroid Build Coastguard Worker rcode_https_ = rcode;
67*6777b538SAndroid Build Coastguard Worker
68*6777b538SAndroid Build Coastguard Worker num_https_records_ = condensed_records.size();
69*6777b538SAndroid Build Coastguard Worker
70*6777b538SAndroid Build Coastguard Worker // We only record one "parsable" sample per HTTPS query. In case multiple
71*6777b538SAndroid Build Coastguard Worker // matching records are present in the response, we combine their parsable
72*6777b538SAndroid Build Coastguard Worker // values with logical AND.
73*6777b538SAndroid Build Coastguard Worker const bool parsable = !base::Contains(condensed_records, false);
74*6777b538SAndroid Build Coastguard Worker
75*6777b538SAndroid Build Coastguard Worker DCHECK(!is_https_parsable_.has_value());
76*6777b538SAndroid Build Coastguard Worker is_https_parsable_ = parsable;
77*6777b538SAndroid Build Coastguard Worker
78*6777b538SAndroid Build Coastguard Worker DCHECK(!https_resolve_time_.has_value());
79*6777b538SAndroid Build Coastguard Worker https_resolve_time_ = https_resolve_time;
80*6777b538SAndroid Build Coastguard Worker }
81*6777b538SAndroid Build Coastguard Worker
BuildMetricName(std::string_view leaf_name) const82*6777b538SAndroid Build Coastguard Worker std::string HttpssvcMetrics::BuildMetricName(std::string_view leaf_name) const {
83*6777b538SAndroid Build Coastguard Worker std::string_view type_str = "RecordHttps";
84*6777b538SAndroid Build Coastguard Worker std::string_view secure = secure_ ? "Secure" : "Insecure";
85*6777b538SAndroid Build Coastguard Worker // This part is just a legacy from old experiments but now meaningless.
86*6777b538SAndroid Build Coastguard Worker std::string_view expectation = "ExpectNoerror";
87*6777b538SAndroid Build Coastguard Worker
88*6777b538SAndroid Build Coastguard Worker // Example metric name:
89*6777b538SAndroid Build Coastguard Worker // Net.DNS.HTTPSSVC.RecordHttps.Secure.ExpectNoerror.DnsRcode
90*6777b538SAndroid Build Coastguard Worker // TODO(crbug.com/1366422): Simplify the metric names.
91*6777b538SAndroid Build Coastguard Worker return base::JoinString(
92*6777b538SAndroid Build Coastguard Worker {"Net.DNS.HTTPSSVC", type_str, secure, expectation, leaf_name}, ".");
93*6777b538SAndroid Build Coastguard Worker }
94*6777b538SAndroid Build Coastguard Worker
RecordMetrics()95*6777b538SAndroid Build Coastguard Worker void HttpssvcMetrics::RecordMetrics() {
96*6777b538SAndroid Build Coastguard Worker DCHECK(!already_recorded_);
97*6777b538SAndroid Build Coastguard Worker already_recorded_ = true;
98*6777b538SAndroid Build Coastguard Worker
99*6777b538SAndroid Build Coastguard Worker // We really have no metrics to record without an HTTPS query resolve time and
100*6777b538SAndroid Build Coastguard Worker // `address_resolve_times_`. If this HttpssvcMetrics is in an inconsistent
101*6777b538SAndroid Build Coastguard Worker // state, disqualify any metrics from being recorded.
102*6777b538SAndroid Build Coastguard Worker if (!https_resolve_time_.has_value() || address_resolve_times_.empty()) {
103*6777b538SAndroid Build Coastguard Worker disqualified_ = true;
104*6777b538SAndroid Build Coastguard Worker }
105*6777b538SAndroid Build Coastguard Worker if (disqualified_)
106*6777b538SAndroid Build Coastguard Worker return;
107*6777b538SAndroid Build Coastguard Worker
108*6777b538SAndroid Build Coastguard Worker base::UmaHistogramMediumTimes(BuildMetricName("ResolveTimeExperimental"),
109*6777b538SAndroid Build Coastguard Worker *https_resolve_time_);
110*6777b538SAndroid Build Coastguard Worker
111*6777b538SAndroid Build Coastguard Worker // Record the address resolve times.
112*6777b538SAndroid Build Coastguard Worker const std::string kMetricResolveTimeAddressRecord =
113*6777b538SAndroid Build Coastguard Worker BuildMetricName("ResolveTimeAddress");
114*6777b538SAndroid Build Coastguard Worker for (base::TimeDelta resolve_time_other : address_resolve_times_) {
115*6777b538SAndroid Build Coastguard Worker base::UmaHistogramMediumTimes(kMetricResolveTimeAddressRecord,
116*6777b538SAndroid Build Coastguard Worker resolve_time_other);
117*6777b538SAndroid Build Coastguard Worker }
118*6777b538SAndroid Build Coastguard Worker
119*6777b538SAndroid Build Coastguard Worker // ResolveTimeRatio is the HTTPS query resolve time divided by the slower of
120*6777b538SAndroid Build Coastguard Worker // the A or AAAA resolve times. Arbitrarily choosing precision at two decimal
121*6777b538SAndroid Build Coastguard Worker // places.
122*6777b538SAndroid Build Coastguard Worker std::vector<base::TimeDelta>::iterator slowest_address_resolve =
123*6777b538SAndroid Build Coastguard Worker std::max_element(address_resolve_times_.begin(),
124*6777b538SAndroid Build Coastguard Worker address_resolve_times_.end());
125*6777b538SAndroid Build Coastguard Worker DCHECK(slowest_address_resolve != address_resolve_times_.end());
126*6777b538SAndroid Build Coastguard Worker
127*6777b538SAndroid Build Coastguard Worker // It's possible to get here with a zero resolve time in tests. Avoid
128*6777b538SAndroid Build Coastguard Worker // divide-by-zero below by returning early; this data point is invalid anyway.
129*6777b538SAndroid Build Coastguard Worker if (slowest_address_resolve->is_zero())
130*6777b538SAndroid Build Coastguard Worker return;
131*6777b538SAndroid Build Coastguard Worker
132*6777b538SAndroid Build Coastguard Worker // Compute a percentage showing how much larger the HTTPS query resolve time
133*6777b538SAndroid Build Coastguard Worker // was compared to the slowest A or AAAA query.
134*6777b538SAndroid Build Coastguard Worker //
135*6777b538SAndroid Build Coastguard Worker // Computation happens on TimeDelta objects, which use CheckedNumeric. This
136*6777b538SAndroid Build Coastguard Worker // will crash if the system clock leaps forward several hundred millennia
137*6777b538SAndroid Build Coastguard Worker // (numeric_limits<int64_t>::max() microseconds ~= 292,000 years).
138*6777b538SAndroid Build Coastguard Worker //
139*6777b538SAndroid Build Coastguard Worker // Then scale the value of the percent by dividing by `kPercentScale`. Sample
140*6777b538SAndroid Build Coastguard Worker // values are bounded between 1 and 20. A recorded sample of 10 means that the
141*6777b538SAndroid Build Coastguard Worker // HTTPS query resolve time took 100% of the slower A/AAAA resolve time. A
142*6777b538SAndroid Build Coastguard Worker // sample of 20 means that the HTTPS query resolve time was 200% relative to
143*6777b538SAndroid Build Coastguard Worker // the A/AAAA resolve time, twice as long.
144*6777b538SAndroid Build Coastguard Worker constexpr int64_t kMaxRatio = 20;
145*6777b538SAndroid Build Coastguard Worker constexpr int64_t kPercentScale = 10;
146*6777b538SAndroid Build Coastguard Worker const int64_t resolve_time_percent = base::ClampFloor<int64_t>(
147*6777b538SAndroid Build Coastguard Worker *https_resolve_time_ / *slowest_address_resolve * 100);
148*6777b538SAndroid Build Coastguard Worker base::UmaHistogramExactLinear(BuildMetricName("ResolveTimeRatio"),
149*6777b538SAndroid Build Coastguard Worker resolve_time_percent / kPercentScale,
150*6777b538SAndroid Build Coastguard Worker kMaxRatio);
151*6777b538SAndroid Build Coastguard Worker
152*6777b538SAndroid Build Coastguard Worker if (num_https_records_ > 0) {
153*6777b538SAndroid Build Coastguard Worker DCHECK(rcode_https_.has_value());
154*6777b538SAndroid Build Coastguard Worker if (*rcode_https_ == HttpssvcDnsRcode::kNoError) {
155*6777b538SAndroid Build Coastguard Worker base::UmaHistogramBoolean(BuildMetricName("Parsable"),
156*6777b538SAndroid Build Coastguard Worker is_https_parsable_.value_or(false));
157*6777b538SAndroid Build Coastguard Worker } else {
158*6777b538SAndroid Build Coastguard Worker // Record boolean indicating whether we received an HTTPS record and
159*6777b538SAndroid Build Coastguard Worker // an error simultaneously.
160*6777b538SAndroid Build Coastguard Worker base::UmaHistogramBoolean(BuildMetricName("RecordWithError"), true);
161*6777b538SAndroid Build Coastguard Worker }
162*6777b538SAndroid Build Coastguard Worker }
163*6777b538SAndroid Build Coastguard Worker
164*6777b538SAndroid Build Coastguard Worker base::UmaHistogramEnumeration(BuildMetricName("DnsRcode"), *rcode_https_);
165*6777b538SAndroid Build Coastguard Worker }
166*6777b538SAndroid Build Coastguard Worker
167*6777b538SAndroid Build Coastguard Worker } // namespace net
168