Hugo Benichi | 0d4a398 | 2016-11-25 11:24:22 +0900 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2016 The Android Open Source Project |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
| 16 | |
| 17 | package android.net.metrics; |
| 18 | |
| 19 | import android.system.OsConstants; |
| 20 | import android.util.IntArray; |
| 21 | import android.util.SparseIntArray; |
| 22 | import com.android.internal.util.TokenBucket; |
| 23 | import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.ConnectStatistics; |
| 24 | import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.Pair; |
| 25 | |
| 26 | /** |
| 27 | * A class that aggregates connect() statistics and helps build |
| 28 | * IpConnectivityLogClass.ConnectStatistics instances. |
| 29 | * |
| 30 | * {@hide} |
| 31 | */ |
| 32 | public class ConnectStats { |
| 33 | private final static int EALREADY = OsConstants.EALREADY; |
| 34 | private final static int EINPROGRESS = OsConstants.EINPROGRESS; |
| 35 | |
| 36 | /** How many events resulted in a given errno. */ |
| 37 | private final SparseIntArray mErrnos = new SparseIntArray(); |
| 38 | /** Latencies of blocking connects. TODO: add non-blocking connects latencies. */ |
| 39 | private final IntArray mLatencies = new IntArray(); |
| 40 | /** TokenBucket for rate limiting latency recording. */ |
| 41 | private final TokenBucket mLatencyTb; |
| 42 | /** Maximum number of latency values recorded. */ |
| 43 | private final int mMaxLatencyRecords; |
| 44 | /** Total count of successful connects. */ |
| 45 | private int mConnectCount = 0; |
Hugo Benichi | a2decca | 2017-02-22 14:32:27 +0900 | [diff] [blame^] | 46 | /** Total count of successful connects done in blocking mode. */ |
| 47 | private int mConnectBlockingCount = 0; |
Hugo Benichi | 0d4a398 | 2016-11-25 11:24:22 +0900 | [diff] [blame] | 48 | /** Total count of successful connects with IPv6 socket address. */ |
| 49 | private int mIpv6ConnectCount = 0; |
| 50 | |
| 51 | public ConnectStats(TokenBucket tb, int maxLatencyRecords) { |
| 52 | mLatencyTb = tb; |
| 53 | mMaxLatencyRecords = maxLatencyRecords; |
| 54 | } |
| 55 | |
| 56 | public ConnectStatistics toProto() { |
| 57 | ConnectStatistics stats = new ConnectStatistics(); |
| 58 | stats.connectCount = mConnectCount; |
Hugo Benichi | a2decca | 2017-02-22 14:32:27 +0900 | [diff] [blame^] | 59 | stats.connectBlockingCount = mConnectBlockingCount; |
Hugo Benichi | 0d4a398 | 2016-11-25 11:24:22 +0900 | [diff] [blame] | 60 | stats.ipv6AddrCount = mIpv6ConnectCount; |
| 61 | stats.latenciesMs = mLatencies.toArray(); |
| 62 | stats.errnosCounters = toPairArrays(mErrnos); |
| 63 | return stats; |
| 64 | } |
| 65 | |
| 66 | public void addEvent(int errno, int latencyMs, String ipAddr) { |
| 67 | if (isSuccess(errno)) { |
Hugo Benichi | a2decca | 2017-02-22 14:32:27 +0900 | [diff] [blame^] | 68 | countConnect(errno, ipAddr); |
Hugo Benichi | 0d4a398 | 2016-11-25 11:24:22 +0900 | [diff] [blame] | 69 | countLatency(errno, latencyMs); |
| 70 | } else { |
| 71 | countError(errno); |
| 72 | } |
| 73 | } |
| 74 | |
Hugo Benichi | a2decca | 2017-02-22 14:32:27 +0900 | [diff] [blame^] | 75 | private void countConnect(int errno, String ipAddr) { |
Hugo Benichi | 0d4a398 | 2016-11-25 11:24:22 +0900 | [diff] [blame] | 76 | mConnectCount++; |
Hugo Benichi | a2decca | 2017-02-22 14:32:27 +0900 | [diff] [blame^] | 77 | if (!isNonBlocking(errno)) { |
| 78 | mConnectBlockingCount++; |
| 79 | } |
| 80 | if (isIPv6(ipAddr)) { |
| 81 | mIpv6ConnectCount++; |
| 82 | } |
Hugo Benichi | 0d4a398 | 2016-11-25 11:24:22 +0900 | [diff] [blame] | 83 | } |
| 84 | |
| 85 | private void countLatency(int errno, int ms) { |
| 86 | if (isNonBlocking(errno)) { |
| 87 | // Ignore connect() on non-blocking sockets |
| 88 | return; |
| 89 | } |
| 90 | if (!mLatencyTb.get()) { |
| 91 | // Rate limited |
| 92 | return; |
| 93 | } |
| 94 | if (mLatencies.size() >= mMaxLatencyRecords) { |
| 95 | // Hard limit the total number of latency measurements. |
| 96 | return; |
| 97 | } |
| 98 | mLatencies.add(ms); |
| 99 | } |
| 100 | |
| 101 | private void countError(int errno) { |
| 102 | final int newcount = mErrnos.get(errno, 0) + 1; |
| 103 | mErrnos.put(errno, newcount); |
| 104 | } |
| 105 | |
| 106 | private static boolean isSuccess(int errno) { |
| 107 | return (errno == 0) || isNonBlocking(errno); |
| 108 | } |
| 109 | |
| 110 | private static boolean isNonBlocking(int errno) { |
| 111 | // On non-blocking TCP sockets, connect() immediately returns EINPROGRESS. |
| 112 | // On non-blocking TCP sockets that are connecting, connect() immediately returns EALREADY. |
| 113 | return (errno == EINPROGRESS) || (errno == EALREADY); |
| 114 | } |
| 115 | |
| 116 | private static boolean isIPv6(String ipAddr) { |
| 117 | return ipAddr.contains(":"); |
| 118 | } |
| 119 | |
| 120 | private static Pair[] toPairArrays(SparseIntArray counts) { |
| 121 | final int s = counts.size(); |
| 122 | Pair[] pairs = new Pair[s]; |
| 123 | for (int i = 0; i < s; i++) { |
| 124 | Pair p = new Pair(); |
| 125 | p.key = counts.keyAt(i); |
| 126 | p.value = counts.valueAt(i); |
| 127 | pairs[i] = p; |
| 128 | } |
| 129 | return pairs; |
| 130 | } |
Hugo Benichi | a2decca | 2017-02-22 14:32:27 +0900 | [diff] [blame^] | 131 | |
| 132 | @Override |
| 133 | public String toString() { |
| 134 | StringBuilder builder = new StringBuilder("ConnectStats(") |
| 135 | .append(String.format("%d success, ", mConnectCount)) |
| 136 | .append(String.format("%d blocking, ", mConnectBlockingCount)) |
| 137 | .append(String.format("%d IPv6 dst", mIpv6ConnectCount)); |
| 138 | for (int i = 0; i < mErrnos.size(); i++) { |
| 139 | String errno = OsConstants.errnoName(mErrnos.keyAt(i)); |
| 140 | int count = mErrnos.valueAt(i); |
| 141 | builder.append(String.format(", %s: %d", errno, count)); |
| 142 | } |
| 143 | return builder.append(")").toString(); |
| 144 | } |
Hugo Benichi | 0d4a398 | 2016-11-25 11:24:22 +0900 | [diff] [blame] | 145 | } |