File: httpssvc_metrics.cc

package info (click to toggle)
chromium 139.0.7258.127-1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 6,122,068 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (162 lines) | stat: -rw-r--r-- 6,237 bytes parent folder | download | duplicates (5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "net/dns/httpssvc_metrics.h"

#include <string_view>

#include "base/containers/contains.h"
#include "base/feature_list.h"
#include "base/metrics/histogram.h"
#include "base/metrics/histogram_base.h"
#include "base/metrics/histogram_functions.h"
#include "base/notreached.h"
#include "base/numerics/clamped_math.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "net/base/features.h"
#include "net/dns/dns_util.h"
#include "net/dns/public/dns_protocol.h"

namespace net {

enum HttpssvcDnsRcode TranslateDnsRcodeForHttpssvcExperiment(uint8_t rcode) {
  switch (rcode) {
    case dns_protocol::kRcodeNOERROR:
      return HttpssvcDnsRcode::kNoError;
    case dns_protocol::kRcodeFORMERR:
      return HttpssvcDnsRcode::kFormErr;
    case dns_protocol::kRcodeSERVFAIL:
      return HttpssvcDnsRcode::kServFail;
    case dns_protocol::kRcodeNXDOMAIN:
      return HttpssvcDnsRcode::kNxDomain;
    case dns_protocol::kRcodeNOTIMP:
      return HttpssvcDnsRcode::kNotImp;
    case dns_protocol::kRcodeREFUSED:
      return HttpssvcDnsRcode::kRefused;
    default:
      return HttpssvcDnsRcode::kUnrecognizedRcode;
  }
}

HttpssvcMetrics::HttpssvcMetrics(bool secure) : secure_(secure) {}

HttpssvcMetrics::~HttpssvcMetrics() {
  RecordMetrics();
}

void HttpssvcMetrics::SaveForAddressQuery(base::TimeDelta resolve_time,
                                          enum HttpssvcDnsRcode rcode) {
  address_resolve_times_.push_back(resolve_time);

  if (rcode != HttpssvcDnsRcode::kNoError)
    disqualified_ = true;
}

void HttpssvcMetrics::SaveForHttps(enum HttpssvcDnsRcode rcode,
                                   const std::vector<bool>& condensed_records,
                                   base::TimeDelta https_resolve_time) {
  DCHECK(!rcode_https_.has_value());
  rcode_https_ = rcode;

  num_https_records_ = condensed_records.size();

  // We only record one "parsable" sample per HTTPS query. In case multiple
  // matching records are present in the response, we combine their parsable
  // values with logical AND.
  const bool parsable = !base::Contains(condensed_records, false);

  DCHECK(!is_https_parsable_.has_value());
  is_https_parsable_ = parsable;

  DCHECK(!https_resolve_time_.has_value());
  https_resolve_time_ = https_resolve_time;
}

std::string HttpssvcMetrics::BuildMetricName(std::string_view leaf_name) const {
  std::string_view type_str = "RecordHttps";
  std::string_view secure = secure_ ? "Secure" : "Insecure";
  // This part is just a legacy from old experiments but now meaningless.
  std::string_view expectation = "ExpectNoerror";

  // Example metric name:
  // Net.DNS.HTTPSSVC.RecordHttps.Secure.ExpectNoerror.DnsRcode
  // TODO(crbug.com/40239736): Simplify the metric names.
  return base::JoinString(
      {"Net.DNS.HTTPSSVC", type_str, secure, expectation, leaf_name}, ".");
}

void HttpssvcMetrics::RecordMetrics() {
  DCHECK(!already_recorded_);
  already_recorded_ = true;

  // We really have no metrics to record without an HTTPS query resolve time and
  // `address_resolve_times_`. If this HttpssvcMetrics is in an inconsistent
  // state, disqualify any metrics from being recorded.
  if (!https_resolve_time_.has_value() || address_resolve_times_.empty()) {
    disqualified_ = true;
  }
  if (disqualified_)
    return;

  base::UmaHistogramMediumTimes(BuildMetricName("ResolveTimeExperimental"),
                                *https_resolve_time_);

  // Record the address resolve times.
  const std::string kMetricResolveTimeAddressRecord =
      BuildMetricName("ResolveTimeAddress");
  for (base::TimeDelta resolve_time_other : address_resolve_times_) {
    base::UmaHistogramMediumTimes(kMetricResolveTimeAddressRecord,
                                  resolve_time_other);
  }

  // ResolveTimeRatio is the HTTPS query resolve time divided by the slower of
  // the A or AAAA resolve times. Arbitrarily choosing precision at two decimal
  // places.
  std::vector<base::TimeDelta>::iterator slowest_address_resolve =
      std::max_element(address_resolve_times_.begin(),
                       address_resolve_times_.end());
  CHECK(slowest_address_resolve != address_resolve_times_.end());

  // It's possible to get here with a zero resolve time in tests.  Avoid
  // divide-by-zero below by returning early; this data point is invalid anyway.
  if (slowest_address_resolve->is_zero())
    return;

  // Compute a percentage showing how much larger the HTTPS query resolve time
  // was compared to the slowest A or AAAA query.
  //
  // Computation happens on TimeDelta objects, which use CheckedNumeric. This
  // will crash if the system clock leaps forward several hundred millennia
  // (numeric_limits<int64_t>::max() microseconds ~= 292,000 years).
  //
  // Then scale the value of the percent by dividing by `kPercentScale`. Sample
  // values are bounded between 1 and 20. A recorded sample of 10 means that the
  // HTTPS query resolve time took 100% of the slower A/AAAA resolve time. A
  // sample of 20 means that the HTTPS query resolve time was 200% relative to
  // the A/AAAA resolve time, twice as long.
  constexpr int64_t kMaxRatio = 20;
  constexpr int64_t kPercentScale = 10;
  const int64_t resolve_time_percent = base::ClampFloor<int64_t>(
      *https_resolve_time_ / *slowest_address_resolve * 100);
  base::UmaHistogramExactLinear(BuildMetricName("ResolveTimeRatio"),
                                resolve_time_percent / kPercentScale,
                                kMaxRatio);

  if (num_https_records_ > 0) {
    DCHECK(rcode_https_.has_value());
    if (*rcode_https_ == HttpssvcDnsRcode::kNoError) {
      base::UmaHistogramBoolean(BuildMetricName("Parsable"),
                                is_https_parsable_.value_or(false));
    } else {
      // Record boolean indicating whether we received an HTTPS record and
      // an error simultaneously.
      base::UmaHistogramBoolean(BuildMetricName("RecordWithError"), true);
    }
  }

  base::UmaHistogramEnumeration(BuildMetricName("DnsRcode"), *rcode_https_);
}

}  // namespace net