diff options
| author | Hugo Benichi <hugobenichi@google.com> | 2017-10-23 23:54:53 +0000 |
|---|---|---|
| committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2017-10-23 23:54:53 +0000 |
| commit | 64ef2a4d28d5ebf439c3a947102ef37d2c2f7a25 (patch) | |
| tree | ce2b8b7420b0befec7b15124d4b06e74dd561892 /core/java/android | |
| parent | 8218b219f3738d40731ecf9e70fd92dcdc2847a0 (diff) | |
| parent | ab20975c7485a0451d77e19d48ab8fe09e483927 (diff) | |
Merge "NetdEventListener: add rolling log of connect and dns stats"
Diffstat (limited to 'core/java/android')
| -rw-r--r-- | core/java/android/net/metrics/ConnectStats.java | 11 | ||||
| -rw-r--r-- | core/java/android/net/metrics/DnsEvent.java | 21 | ||||
| -rw-r--r-- | core/java/android/net/metrics/NetworkMetrics.java | 168 |
3 files changed, 193 insertions, 7 deletions
diff --git a/core/java/android/net/metrics/ConnectStats.java b/core/java/android/net/metrics/ConnectStats.java index 30b2656227d0..2495cab1adf9 100644 --- a/core/java/android/net/metrics/ConnectStats.java +++ b/core/java/android/net/metrics/ConnectStats.java @@ -20,6 +20,7 @@ import android.net.NetworkCapabilities; import android.system.OsConstants; import android.util.IntArray; import android.util.SparseIntArray; + import com.android.internal.util.BitUtils; import com.android.internal.util.TokenBucket; @@ -43,6 +44,8 @@ public class ConnectStats { public final TokenBucket mLatencyTb; /** Maximum number of latency values recorded. */ public final int mMaxLatencyRecords; + /** Total count of events */ + public int eventCount = 0; /** Total count of successful connects. */ public int connectCount = 0; /** Total count of successful connects done in blocking mode. */ @@ -57,12 +60,15 @@ public class ConnectStats { mMaxLatencyRecords = maxLatencyRecords; } - public void addEvent(int errno, int latencyMs, String ipAddr) { + boolean addEvent(int errno, int latencyMs, String ipAddr) { + eventCount++; if (isSuccess(errno)) { countConnect(errno, ipAddr); countLatency(errno, latencyMs); + return true; } else { countError(errno); + return false; } } @@ -101,7 +107,7 @@ public class ConnectStats { return (errno == 0) || isNonBlocking(errno); } - private static boolean isNonBlocking(int errno) { + static boolean isNonBlocking(int errno) { // On non-blocking TCP sockets, connect() immediately returns EINPROGRESS. // On non-blocking TCP sockets that are connecting, connect() immediately returns EALREADY. return (errno == EINPROGRESS) || (errno == EALREADY); @@ -117,6 +123,7 @@ public class ConnectStats { for (int t : BitUtils.unpackBits(transports)) { builder.append(NetworkCapabilities.transportNameOf(t)).append(", "); } + builder.append(String.format("%d events, ", eventCount)); builder.append(String.format("%d success, ", connectCount)); builder.append(String.format("%d blocking, ", connectBlockingCount)); builder.append(String.format("%d IPv6 dst", ipv6ConnectCount)); diff --git a/core/java/android/net/metrics/DnsEvent.java b/core/java/android/net/metrics/DnsEvent.java index a4970e4d0d28..81b098bbb38e 100644 --- a/core/java/android/net/metrics/DnsEvent.java +++ b/core/java/android/net/metrics/DnsEvent.java @@ -17,11 +17,13 @@ package android.net.metrics; import android.net.NetworkCapabilities; -import java.util.Arrays; + import com.android.internal.util.BitUtils; +import java.util.Arrays; + /** - * A DNS event recorded by NetdEventListenerService. + * A batch of DNS events recorded by NetdEventListenerService for a specific network. * {@hide} */ final public class DnsEvent { @@ -38,6 +40,8 @@ final public class DnsEvent { // the eventTypes, returnCodes, and latenciesMs arrays have the same length and the i-th event // is spread across the three array at position i. public int eventCount; + // The number of successful DNS queries recorded. + public int successCount; // The types of DNS queries as defined in INetdEventListener. public byte[] eventTypes; // Current getaddrinfo codes go from 1 to EAI_MAX = 15. gethostbyname returns errno, but there @@ -54,10 +58,11 @@ final public class DnsEvent { latenciesMs = new int[initialCapacity]; } - public void addResult(byte eventType, byte returnCode, int latencyMs) { + boolean addResult(byte eventType, byte returnCode, int latencyMs) { + boolean isSuccess = (returnCode == 0); if (eventCount >= SIZE_LIMIT) { // TODO: implement better rate limiting that does not biases metrics. - return; + return isSuccess; } if (eventCount == eventTypes.length) { resize((int) (1.4 * eventCount)); @@ -66,6 +71,10 @@ final public class DnsEvent { returnCodes[eventCount] = returnCode; latenciesMs[eventCount] = latencyMs; eventCount++; + if (isSuccess) { + successCount++; + } + return isSuccess; } public void resize(int newLength) { @@ -80,6 +89,8 @@ final public class DnsEvent { for (int t : BitUtils.unpackBits(transports)) { builder.append(NetworkCapabilities.transportNameOf(t)).append(", "); } - return builder.append(eventCount).append(" events)").toString(); + builder.append(String.format("%d events, ", eventCount)); + builder.append(String.format("%d success)", successCount)); + return builder.toString(); } } diff --git a/core/java/android/net/metrics/NetworkMetrics.java b/core/java/android/net/metrics/NetworkMetrics.java new file mode 100644 index 000000000000..2b662a0c28e2 --- /dev/null +++ b/core/java/android/net/metrics/NetworkMetrics.java @@ -0,0 +1,168 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.metrics; + +import android.net.NetworkCapabilities; + +import com.android.internal.util.BitUtils; +import com.android.internal.util.TokenBucket; + +import java.util.StringJoiner; + +/** + * A class accumulating network metrics received from Netd regarding dns queries and + * connect() calls on a given network. + * + * This class also accumulates running sums of dns and connect latency stats and + * error counts for bug report logging. + * + * @hide + */ +public class NetworkMetrics { + + private static final int INITIAL_DNS_BATCH_SIZE = 100; + private static final int CONNECT_LATENCY_MAXIMUM_RECORDS = 20000; + + // The network id of the Android Network. + public final int netId; + // The transport types bitmap of the Android Network, as defined in NetworkCapabilities.java. + public final long transports; + // Accumulated metrics for connect events. + public final ConnectStats connectMetrics; + // Accumulated metrics for dns events. + public final DnsEvent dnsMetrics; + // Running sums of latencies and error counts for connect and dns events. + public final Summary summary; + // Running sums of the most recent latencies and error counts for connect and dns events. + // Starts null until some events are accumulated. + // Allows to collect periodic snapshot of the running summaries for a given network. + public Summary pendingSummary; + + public NetworkMetrics(int netId, long transports, TokenBucket tb) { + this.netId = netId; + this.transports = transports; + this.connectMetrics = + new ConnectStats(netId, transports, tb, CONNECT_LATENCY_MAXIMUM_RECORDS); + this.dnsMetrics = new DnsEvent(netId, transports, INITIAL_DNS_BATCH_SIZE); + this.summary = new Summary(netId, transports); + } + + /** + * Get currently pending Summary statistics, if any, for this NetworkMetrics, merge them + * into the long running Summary statistics of this NetworkMetrics, and also clear them. + */ + public Summary getPendingStats() { + Summary s = pendingSummary; + pendingSummary = null; + if (s != null) { + summary.merge(s); + } + return s; + } + + /** Accumulate a dns query result reported by netd. */ + public void addDnsResult(int eventType, int returnCode, int latencyMs) { + if (pendingSummary == null) { + pendingSummary = new Summary(netId, transports); + } + boolean isSuccess = dnsMetrics.addResult((byte) eventType, (byte) returnCode, latencyMs); + pendingSummary.dnsLatencies.count(latencyMs); + pendingSummary.dnsErrorRate.count(isSuccess ? 0 : 1); + } + + /** Accumulate a connect query result reported by netd. */ + public void addConnectResult(int error, int latencyMs, String ipAddr) { + if (pendingSummary == null) { + pendingSummary = new Summary(netId, transports); + } + boolean isSuccess = connectMetrics.addEvent(error, latencyMs, ipAddr); + pendingSummary.connectErrorRate.count(isSuccess ? 0 : 1); + if (ConnectStats.isNonBlocking(error)) { + pendingSummary.connectLatencies.count(latencyMs); + } + } + + /** Represents running sums for dns and connect average error counts and average latencies. */ + public static class Summary { + + public final int netId; + public final long transports; + // DNS latencies measured in milliseconds. + public final Metrics dnsLatencies = new Metrics(); + // DNS error rate measured in percentage points. + public final Metrics dnsErrorRate = new Metrics(); + // Blocking connect latencies measured in milliseconds. + public final Metrics connectLatencies = new Metrics(); + // Blocking and non blocking connect error rate measured in percentage points. + public final Metrics connectErrorRate = new Metrics(); + + public Summary(int netId, long transports) { + this.netId = netId; + this.transports = transports; + } + + void merge(Summary that) { + dnsLatencies.merge(that.dnsLatencies); + dnsErrorRate.merge(that.dnsErrorRate); + connectLatencies.merge(that.connectLatencies); + connectErrorRate.merge(that.connectErrorRate); + } + + @Override + public String toString() { + StringJoiner j = new StringJoiner(", ", "{", "}"); + j.add("netId=" + netId); + for (int t : BitUtils.unpackBits(transports)) { + j.add(NetworkCapabilities.transportNameOf(t)); + } + j.add(String.format("dns avg=%dms max=%dms err=%.1f%% tot=%d", + (int) dnsLatencies.average(), (int) dnsLatencies.max, + 100 * dnsErrorRate.average(), dnsErrorRate.count)); + j.add(String.format("connect avg=%dms max=%dms err=%.1f%% tot=%d", + (int) connectLatencies.average(), (int) connectLatencies.max, + 100 * connectErrorRate.average(), connectErrorRate.count)); + return j.toString(); + } + } + + /** Tracks a running sum and returns the average of a metric. */ + static class Metrics { + public double sum; + public double max = Double.MIN_VALUE; + public int count; + + void merge(Metrics that) { + this.count += that.count; + this.sum += that.sum; + this.max = Math.max(this.max, that.max); + } + + void count(double value) { + count++; + sum += value; + max = Math.max(max, value); + } + + double average() { + double a = sum / (double) count; + if (Double.isNaN(a)) { + a = 0; + } + return a; + } + } +} |
