From a92af82aa31fa44d3b3fde7a449076cbe1894ad5 Mon Sep 17 00:00:00 2001 From: Hugo Benichi Date: Thu, 23 Mar 2017 18:38:22 +0900 Subject: [PATCH] Connectivity metrics: add transports to connect stats This patch groups connect() events per netId. It adds netid and transport information to serialized ConnectStatistics events. Test: updated NetdEventListenerServiceTest updated IpConnectivityMetricsTest $ runtest frameworks-net passes Bug: 34901696 Change-Id: Id0d536ff723ded5c26eafe0bb138ba75ba2856c5 Merged-In: I4769496383943e714a1d350c298e093c2ed57477 (cherry picked from commit dfc2cc5857199345e08f07977b79b20292f964a2) --- .../IpConnectivityEventBuilderTest.java | 7 +- .../IpConnectivityMetricsTest.java | 138 ++++++++++- .../NetdEventListenerServiceTest.java | 220 ++++++++++-------- 3 files changed, 251 insertions(+), 114 deletions(-) diff --git a/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java b/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java index 66ca4b6b7e..d11565abb9 100644 --- a/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java +++ b/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java @@ -45,7 +45,9 @@ import android.net.metrics.NetworkEvent; import android.net.metrics.RaEvent; import android.net.metrics.ValidationProbeEvent; import android.test.suitebuilder.annotation.SmallTest; +import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpConnectivityEvent; import java.util.Arrays; +import java.util.List; import junit.framework.TestCase; // TODO: instead of comparing textpb to textpb, parse textpb and compare proto to proto. @@ -483,8 +485,9 @@ public class IpConnectivityEventBuilderTest extends TestCase { static void verifySerialization(String want, ConnectivityMetricsEvent... input) { try { - byte[] got = IpConnectivityEventBuilder.serialize(0, - IpConnectivityEventBuilder.toProto(Arrays.asList(input))); + List proto = + IpConnectivityEventBuilder.toProto(Arrays.asList(input)); + byte[] got = IpConnectivityEventBuilder.serialize(0, proto); IpConnectivityLog log = IpConnectivityLog.parseFrom(got); assertEquals(want, log.toString()); } catch (Exception e) { diff --git a/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java b/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java index fd81c90a05..e01469b182 100644 --- a/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java +++ b/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java @@ -16,12 +16,22 @@ package com.android.server.connectivity; +import static android.net.metrics.INetdEventListener.EVENT_GETADDRINFO; +import static android.net.metrics.INetdEventListener.EVENT_GETHOSTBYNAME; import static org.mockito.Mockito.timeout; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import android.content.Context; +import android.net.ConnectivityManager; import android.net.ConnectivityMetricsEvent; import android.net.IIpConnectivityMetrics; +import android.net.Network; +import android.net.NetworkCapabilities; import android.net.metrics.ApfProgramEvent; import android.net.metrics.ApfStats; import android.net.metrics.DefaultNetworkEvent; @@ -31,7 +41,9 @@ import android.net.metrics.IpManagerEvent; import android.net.metrics.IpReachabilityEvent; import android.net.metrics.RaEvent; import android.net.metrics.ValidationProbeEvent; +import android.system.OsConstants; import android.os.Parcelable; +import android.support.test.runner.AndroidJUnit4; import android.test.suitebuilder.annotation.SmallTest; import android.util.Base64; import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass; @@ -41,26 +53,38 @@ import java.util.Collections; import java.util.Comparator; import java.util.Iterator; import java.util.List; -import junit.framework.TestCase; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; -public class IpConnectivityMetricsTest extends TestCase { +@RunWith(AndroidJUnit4.class) +@SmallTest +public class IpConnectivityMetricsTest { static final IpReachabilityEvent FAKE_EV = new IpReachabilityEvent(IpReachabilityEvent.NUD_FAILED); + private static final String EXAMPLE_IPV4 = "192.0.2.1"; + private static final String EXAMPLE_IPV6 = "2001:db8:1200::2:1"; + @Mock Context mCtx; @Mock IIpConnectivityMetrics mMockService; + @Mock ConnectivityManager mCm; IpConnectivityMetrics mService; + NetdEventListenerService mNetdListener; + @Before public void setUp() { MockitoAnnotations.initMocks(this); mService = new IpConnectivityMetrics(mCtx, (ctx) -> 2000); + mNetdListener = new NetdEventListenerService(mCm); + mService.mNetdListener = mNetdListener; } - @SmallTest + @Test public void testLoggingEvents() throws Exception { IpConnectivityLog logger = new IpConnectivityLog(mMockService); @@ -74,7 +98,7 @@ public class IpConnectivityMetricsTest extends TestCase { assertEventsEqual(expectedEvent(3), got.get(2)); } - @SmallTest + @Test public void testLoggingEventsWithMultipleCallers() throws Exception { IpConnectivityLog logger = new IpConnectivityLog(mMockService); @@ -91,7 +115,7 @@ public class IpConnectivityMetricsTest extends TestCase { }.start(); } - List got = verifyEvents(nCallers * nEvents, 100); + List got = verifyEvents(nCallers * nEvents, 200); Collections.sort(got, EVENT_COMPARATOR); Iterator iter = got.iterator(); for (int i = 0; i < nCallers; i++) { @@ -102,7 +126,7 @@ public class IpConnectivityMetricsTest extends TestCase { } } - @SmallTest + @Test public void testBufferFlushing() { String output1 = getdump("flush"); assertEquals("", output1); @@ -115,7 +139,7 @@ public class IpConnectivityMetricsTest extends TestCase { assertEquals("", output3); } - @SmallTest + @Test public void testRateLimiting() { final IpConnectivityLog logger = new IpConnectivityLog(mService.impl); final ApfProgramEvent ev = new ApfProgramEvent(); @@ -137,12 +161,19 @@ public class IpConnectivityMetricsTest extends TestCase { assertEquals("", output2); } - @SmallTest - // TODO: add NetdEventListenerService coverage too - public void testEndToEndLogging() { + @Test + public void testEndToEndLogging() throws Exception { // TODO: instead of comparing textpb to textpb, parse textpb and compare proto to proto. IpConnectivityLog logger = new IpConnectivityLog(mService.impl); + NetworkCapabilities ncWifi = new NetworkCapabilities(); + NetworkCapabilities ncCell = new NetworkCapabilities(); + ncWifi.addTransportType(NetworkCapabilities.TRANSPORT_WIFI); + ncCell.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR); + + when(mCm.getNetworkCapabilities(new Network(100))).thenReturn(ncWifi); + when(mCm.getNetworkCapabilities(new Network(101))).thenReturn(ncCell); + ApfStats apfStats = new ApfStats(); apfStats.durationMs = 45000; apfStats.receivedRas = 10; @@ -178,6 +209,21 @@ public class IpConnectivityMetricsTest extends TestCase { logger.log(ev); } + // netId, errno, latency, destination + connectEvent(100, OsConstants.EALREADY, 0, EXAMPLE_IPV4); + connectEvent(100, OsConstants.EINPROGRESS, 0, EXAMPLE_IPV6); + connectEvent(100, 0, 110, EXAMPLE_IPV4); + connectEvent(101, 0, 23, EXAMPLE_IPV4); + connectEvent(101, 0, 45, EXAMPLE_IPV6); + connectEvent(100, OsConstants.EAGAIN, 0, EXAMPLE_IPV4); + + // netId, type, return code, latency + dnsEvent(100, EVENT_GETADDRINFO, 0, 3456); + dnsEvent(100, EVENT_GETADDRINFO, 3, 45); + dnsEvent(100, EVENT_GETHOSTBYNAME, 0, 638); + dnsEvent(101, EVENT_GETADDRINFO, 0, 56); + dnsEvent(101, EVENT_GETHOSTBYNAME, 0, 34); + String want = String.join("\n", "dropped_events: 0", "events <", @@ -280,6 +326,70 @@ public class IpConnectivityMetricsTest extends TestCase { " router_lifetime: 2000", " >", ">", + "events <", + " if_name: \"\"", + " link_layer: 4", + " network_id: 100", + " time_ms: 0", + " transports: 2", + " connect_statistics <", + " connect_blocking_count: 1", + " connect_count: 3", + " errnos_counters <", + " key: 11", + " value: 1", + " >", + " ipv6_addr_count: 1", + " latencies_ms: 110", + " >", + ">", + "events <", + " if_name: \"\"", + " link_layer: 2", + " network_id: 101", + " time_ms: 0", + " transports: 1", + " connect_statistics <", + " connect_blocking_count: 2", + " connect_count: 2", + " ipv6_addr_count: 1", + " latencies_ms: 23", + " latencies_ms: 45", + " >", + ">", + "events <", + " if_name: \"\"", + " link_layer: 4", + " network_id: 100", + " time_ms: 0", + " transports: 2", + " dns_lookup_batch <", + " event_types: 1", + " event_types: 1", + " event_types: 2", + " latencies_ms: 3456", + " latencies_ms: 45", + " latencies_ms: 638", + " return_codes: 0", + " return_codes: 3", + " return_codes: 0", + " >", + ">", + "events <", + " if_name: \"\"", + " link_layer: 2", + " network_id: 101", + " time_ms: 0", + " transports: 1", + " dns_lookup_batch <", + " event_types: 1", + " event_types: 2", + " latencies_ms: 56", + " latencies_ms: 34", + " return_codes: 0", + " return_codes: 0", + " >", + ">", "version: 2\n"); verifySerialization(want, getdump("flush")); @@ -292,6 +402,14 @@ public class IpConnectivityMetricsTest extends TestCase { return buffer.toString(); } + void connectEvent(int netid, int error, int latencyMs, String ipAddr) throws Exception { + mNetdListener.onConnectEvent(netid, error, latencyMs, ipAddr, 80, 1); + } + + void dnsEvent(int netId, int type, int result, int latency) throws Exception { + mNetdListener.onDnsEvent(netId, type, result, latency, "", null, 0, 0); + } + List verifyEvents(int n, int timeoutMs) throws Exception { ArgumentCaptor captor = ArgumentCaptor.forClass(ConnectivityMetricsEvent.class); diff --git a/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java b/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java index 05c9767325..f98ab3d069 100644 --- a/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java +++ b/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java @@ -62,20 +62,22 @@ public class NetdEventListenerServiceTest { @Before public void setUp() { - mCm = mock(ConnectivityManager.class); - mNetdEventListenerService = new NetdEventListenerService(mCm); - } - - @Test - public void testDnsLogging() throws Exception { NetworkCapabilities ncWifi = new NetworkCapabilities(); NetworkCapabilities ncCell = new NetworkCapabilities(); ncWifi.addTransportType(NetworkCapabilities.TRANSPORT_WIFI); ncCell.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR); + mCm = mock(ConnectivityManager.class); when(mCm.getNetworkCapabilities(new Network(100))).thenReturn(ncWifi); when(mCm.getNetworkCapabilities(new Network(101))).thenReturn(ncCell); + mNetdEventListenerService = new NetdEventListenerService(mCm); + } + + @Test + public void testDnsLogging() throws Exception { + asyncDump(100); + dnsEvent(100, EVENT_GETADDRINFO, 0, 3456); dnsEvent(100, EVENT_GETADDRINFO, 0, 267); dnsEvent(100, EVENT_GETHOSTBYNAME, 22, 1230); @@ -96,18 +98,6 @@ public class NetdEventListenerServiceTest { "dropped_events: 0", "events <", " if_name: \"\"", - " link_layer: 0", - " network_id: 0", - " time_ms: 0", - " transports: 0", - " connect_statistics <", - " connect_blocking_count: 0", - " connect_count: 0", - " ipv6_addr_count: 0", - " >", - ">", - "events <", - " if_name: \"\"", " link_layer: 4", " network_id: 100", " time_ms: 0", @@ -172,34 +162,36 @@ public class NetdEventListenerServiceTest { @Test public void testConnectLogging() throws Exception { + asyncDump(100); + final int OK = 0; Thread[] logActions = { // ignored - connectEventAction(OsConstants.EALREADY, 0, EXAMPLE_IPV4), - connectEventAction(OsConstants.EALREADY, 0, EXAMPLE_IPV6), - connectEventAction(OsConstants.EINPROGRESS, 0, EXAMPLE_IPV4), - connectEventAction(OsConstants.EINPROGRESS, 0, EXAMPLE_IPV6), - connectEventAction(OsConstants.EINPROGRESS, 0, EXAMPLE_IPV6), + connectEventAction(100, OsConstants.EALREADY, 0, EXAMPLE_IPV4), + connectEventAction(100, OsConstants.EALREADY, 0, EXAMPLE_IPV6), + connectEventAction(100, OsConstants.EINPROGRESS, 0, EXAMPLE_IPV4), + connectEventAction(101, OsConstants.EINPROGRESS, 0, EXAMPLE_IPV6), + connectEventAction(101, OsConstants.EINPROGRESS, 0, EXAMPLE_IPV6), // valid latencies - connectEventAction(OK, 110, EXAMPLE_IPV4), - connectEventAction(OK, 23, EXAMPLE_IPV4), - connectEventAction(OK, 45, EXAMPLE_IPV4), - connectEventAction(OK, 56, EXAMPLE_IPV4), - connectEventAction(OK, 523, EXAMPLE_IPV6), - connectEventAction(OK, 214, EXAMPLE_IPV6), - connectEventAction(OK, 67, EXAMPLE_IPV6), + connectEventAction(100, OK, 110, EXAMPLE_IPV4), + connectEventAction(100, OK, 23, EXAMPLE_IPV4), + connectEventAction(100, OK, 45, EXAMPLE_IPV4), + connectEventAction(101, OK, 56, EXAMPLE_IPV4), + connectEventAction(101, OK, 523, EXAMPLE_IPV6), + connectEventAction(101, OK, 214, EXAMPLE_IPV6), + connectEventAction(101, OK, 67, EXAMPLE_IPV6), // errors - connectEventAction(OsConstants.EPERM, 0, EXAMPLE_IPV4), - connectEventAction(OsConstants.EPERM, 0, EXAMPLE_IPV4), - connectEventAction(OsConstants.EAGAIN, 0, EXAMPLE_IPV4), - connectEventAction(OsConstants.EACCES, 0, EXAMPLE_IPV4), - connectEventAction(OsConstants.EACCES, 0, EXAMPLE_IPV4), - connectEventAction(OsConstants.EACCES, 0, EXAMPLE_IPV6), - connectEventAction(OsConstants.EADDRINUSE, 0, EXAMPLE_IPV4), - connectEventAction(OsConstants.ETIMEDOUT, 0, EXAMPLE_IPV4), - connectEventAction(OsConstants.ETIMEDOUT, 0, EXAMPLE_IPV6), - connectEventAction(OsConstants.ETIMEDOUT, 0, EXAMPLE_IPV6), - connectEventAction(OsConstants.ECONNREFUSED, 0, EXAMPLE_IPV4), + connectEventAction(100, OsConstants.EPERM, 0, EXAMPLE_IPV4), + connectEventAction(101, OsConstants.EPERM, 0, EXAMPLE_IPV4), + connectEventAction(100, OsConstants.EAGAIN, 0, EXAMPLE_IPV4), + connectEventAction(100, OsConstants.EACCES, 0, EXAMPLE_IPV4), + connectEventAction(101, OsConstants.EACCES, 0, EXAMPLE_IPV4), + connectEventAction(101, OsConstants.EACCES, 0, EXAMPLE_IPV6), + connectEventAction(100, OsConstants.EADDRINUSE, 0, EXAMPLE_IPV4), + connectEventAction(101, OsConstants.ETIMEDOUT, 0, EXAMPLE_IPV4), + connectEventAction(100, OsConstants.ETIMEDOUT, 0, EXAMPLE_IPV6), + connectEventAction(100, OsConstants.ETIMEDOUT, 0, EXAMPLE_IPV6), + connectEventAction(101, OsConstants.ECONNREFUSED, 0, EXAMPLE_IPV4), }; for (Thread t : logActions) { @@ -209,59 +201,84 @@ public class NetdEventListenerServiceTest { t.join(); } - List events = new ArrayList<>(); - mNetdEventListenerService.flushStatistics(events); - - IpConnectivityEvent got = events.get(0); + String got = flushStatistics(); String want = String.join("\n", - "if_name: \"\"", - "link_layer: 0", - "network_id: 0", - "time_ms: 0", - "transports: 0", - "connect_statistics <", - " connect_blocking_count: 7", - " connect_count: 12", - " errnos_counters <", - " key: 1", - " value: 2", + "dropped_events: 0", + "events <", + " if_name: \"\"", + " link_layer: 4", + " network_id: 100", + " time_ms: 0", + " transports: 2", + " connect_statistics <", + " connect_blocking_count: 3", + " connect_count: 6", + " errnos_counters <", + " key: 1", + " value: 1", + " >", + " errnos_counters <", + " key: 11", + " value: 1", + " >", + " errnos_counters <", + " key: 13", + " value: 1", + " >", + " errnos_counters <", + " key: 98", + " value: 1", + " >", + " errnos_counters <", + " key: 110", + " value: 2", + " >", + " ipv6_addr_count: 1", + " latencies_ms: 23", + " latencies_ms: 45", + " latencies_ms: 110", " >", - " errnos_counters <", - " key: 11", - " value: 1", + ">", + "events <", + " if_name: \"\"", + " link_layer: 2", + " network_id: 101", + " time_ms: 0", + " transports: 1", + " connect_statistics <", + " connect_blocking_count: 4", + " connect_count: 6", + " errnos_counters <", + " key: 1", + " value: 1", + " >", + " errnos_counters <", + " key: 13", + " value: 2", + " >", + " errnos_counters <", + " key: 110", + " value: 1", + " >", + " errnos_counters <", + " key: 111", + " value: 1", + " >", + " ipv6_addr_count: 5", + " latencies_ms: 56", + " latencies_ms: 67", + " latencies_ms: 214", + " latencies_ms: 523", " >", - " errnos_counters <", - " key: 13", - " value: 3", - " >", - " errnos_counters <", - " key: 98", - " value: 1", - " >", - " errnos_counters <", - " key: 110", - " value: 3", - " >", - " errnos_counters <", - " key: 111", - " value: 1", - " >", - " ipv6_addr_count: 6", - " latencies_ms: 23", - " latencies_ms: 45", - " latencies_ms: 56", - " latencies_ms: 67", - " latencies_ms: 110", - " latencies_ms: 214", - " latencies_ms: 523", - ">\n"); - verifyConnectEvent(want, got); + ">", + "version: 2\n"); + assertEquals(want, got); } - Thread connectEventAction(int error, int latencyMs, String ipAddr) { + Thread connectEventAction(int netId, int error, int latencyMs, String ipAddr) { return new Thread(() -> { try { - mNetdEventListenerService.onConnectEvent(100, error, latencyMs, ipAddr, 80, 1); + mNetdEventListenerService.onConnectEvent(netId, error, latencyMs, ipAddr, 80, 1); } catch (Exception e) { fail(e.toString()); } @@ -272,25 +289,14 @@ public class NetdEventListenerServiceTest { mNetdEventListenerService.onDnsEvent(netId, type, result, latency, "", null, 0, 0); } - Thread dumpAction(long durationMs) throws Exception { + void asyncDump(long durationMs) throws Exception { final long stop = System.currentTimeMillis() + durationMs; final PrintWriter pw = new PrintWriter(new FileOutputStream("/dev/null")); - return new Thread(() -> { + new Thread(() -> { while (System.currentTimeMillis() < stop) { mNetdEventListenerService.dump(pw); } - }); - } - - static void verifyConnectEvent(String expected, IpConnectivityEvent got) { - try { - Arrays.sort(got.getConnectStatistics().latenciesMs); - Arrays.sort(got.getConnectStatistics().errnosCounters, - Comparator.comparingInt((p) -> p.key)); - assertEquals(expected, got.toString()); - } catch (Exception e) { - fail(e.toString()); - } + }).start(); } // TODO: instead of comparing textpb to textpb, parse textpb and compare proto to proto. @@ -303,6 +309,16 @@ public class NetdEventListenerServiceTest { PrintWriter writer = new PrintWriter(buffer); metricsService.impl.dump(null, writer, new String[]{"flush"}); byte[] bytes = Base64.decode(buffer.toString(), Base64.DEFAULT); - return IpConnectivityLog.parseFrom(bytes).toString(); + IpConnectivityLog log = IpConnectivityLog.parseFrom(bytes); + for (IpConnectivityEvent ev : log.events) { + if (ev.getConnectStatistics() == null) { + continue; + } + // Sort repeated fields of connect() events arriving in non-deterministic order. + Arrays.sort(ev.getConnectStatistics().latenciesMs); + Arrays.sort(ev.getConnectStatistics().errnosCounters, + Comparator.comparingInt((p) -> p.key)); + } + return log.toString(); } }