ConnectStats.java revision a2decca4f25b6d3adfd396cdc475807985783bab
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 17package android.net.metrics; 18 19import android.system.OsConstants; 20import android.util.IntArray; 21import android.util.SparseIntArray; 22import com.android.internal.util.TokenBucket; 23import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.ConnectStatistics; 24import 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 */ 32public 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; 46 /** Total count of successful connects done in blocking mode. */ 47 private int mConnectBlockingCount = 0; 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; 59 stats.connectBlockingCount = mConnectBlockingCount; 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)) { 68 countConnect(errno, ipAddr); 69 countLatency(errno, latencyMs); 70 } else { 71 countError(errno); 72 } 73 } 74 75 private void countConnect(int errno, String ipAddr) { 76 mConnectCount++; 77 if (!isNonBlocking(errno)) { 78 mConnectBlockingCount++; 79 } 80 if (isIPv6(ipAddr)) { 81 mIpv6ConnectCount++; 82 } 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 } 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 } 145} 146