167 lines
6.2 KiB
C++
167 lines
6.2 KiB
C++
// 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 "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;
|
|
}
|
|
NOTREACHED();
|
|
}
|
|
|
|
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::SaveAddressQueryFailure() {
|
|
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(
|
|
base::StringPiece leaf_name) const {
|
|
base::StringPiece type_str = "RecordHttps";
|
|
base::StringPiece secure = secure_ ? "Secure" : "Insecure";
|
|
// This part is just a legacy from old experiments but now meaningless.
|
|
base::StringPiece expectation = "ExpectNoerror";
|
|
|
|
// Example metric name:
|
|
// Net.DNS.HTTPSSVC.RecordHttps.Secure.ExpectNoerror.DnsRcode
|
|
// TODO(crbug.com/1366422): 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());
|
|
DCHECK(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
|