Connectivity metrics: collect NFLOG wakeup events
This patch stores NFLOG packet wakeup events sent by Netd to the system
server into a ring buffer inside NetdEventListenerService. The content
of this buffer is accessible by $ dumpsys connmetrics or $ dumpsys
connmetrics list, and is added to bug reports.
The wakeup event buffer stores currently uid and timestamps.
Bug: 34901696
Bug: 62179647
Test: runtest frameworks-net, new unit tests
Merged-In: Ie8db6f8572b1a929a20398d8dc03e189bc488382
(cherry picked from commit f562ac34a5)
Change-Id: Ibe706872a80dfd06abd9779a2116ca7e4bc0fb77
This commit is contained in:
34
core/java/android/net/metrics/WakeupEvent.java
Normal file
34
core/java/android/net/metrics/WakeupEvent.java
Normal file
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* An event logged when NFLOG notifies userspace of a wakeup packet for
|
||||
* watched interfaces.
|
||||
* {@hide}
|
||||
*/
|
||||
public class WakeupEvent {
|
||||
public String iface;
|
||||
public long timestampMs;
|
||||
public int uid;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("WakeupEvent(%tT.%tL, %s, uid: %d)",
|
||||
timestampMs, timestampMs, iface, uid);
|
||||
}
|
||||
}
|
||||
@@ -25,6 +25,7 @@ import android.net.metrics.ConnectStats;
|
||||
import android.net.metrics.DnsEvent;
|
||||
import android.net.metrics.INetdEventListener;
|
||||
import android.net.metrics.IpConnectivityLog;
|
||||
import android.net.metrics.WakeupEvent;
|
||||
import android.os.RemoteException;
|
||||
import android.text.format.DateUtils;
|
||||
import android.util.Log;
|
||||
@@ -59,12 +60,23 @@ public class NetdEventListenerService extends INetdEventListener.Stub {
|
||||
private static final int CONNECT_LATENCY_FILL_RATE = 15 * (int) DateUtils.SECOND_IN_MILLIS;
|
||||
private static final int CONNECT_LATENCY_MAXIMUM_RECORDS = 20000;
|
||||
|
||||
@VisibleForTesting
|
||||
static final int WAKEUP_EVENT_BUFFER_LENGTH = 1024;
|
||||
|
||||
private static final String WAKEUP_EVENT_IFACE_PREFIX = "iface:";
|
||||
|
||||
// Sparse arrays of DNS and connect events, grouped by net id.
|
||||
@GuardedBy("this")
|
||||
private final SparseArray<DnsEvent> mDnsEvents = new SparseArray<>();
|
||||
@GuardedBy("this")
|
||||
private final SparseArray<ConnectStats> mConnectEvents = new SparseArray<>();
|
||||
|
||||
// Ring buffer array for storing packet wake up events sent by Netd.
|
||||
@GuardedBy("this")
|
||||
private final WakeupEvent[] mWakeupEvents = new WakeupEvent[WAKEUP_EVENT_BUFFER_LENGTH];
|
||||
@GuardedBy("this")
|
||||
private long mWakeupEventCursor = 0;
|
||||
|
||||
private final ConnectivityManager mCm;
|
||||
|
||||
@GuardedBy("this")
|
||||
@@ -137,6 +149,44 @@ public class NetdEventListenerService extends INetdEventListener.Stub {
|
||||
|
||||
@Override
|
||||
public synchronized void onWakeupEvent(String prefix, int uid, int gid, long timestampNs) {
|
||||
maybeVerboseLog("onWakeupEvent(%s, %d, %d, %sns)", prefix, uid, gid, timestampNs);
|
||||
|
||||
// TODO: add ip protocol and port
|
||||
|
||||
String iface = prefix.replaceFirst(WAKEUP_EVENT_IFACE_PREFIX, "");
|
||||
long timestampMs = timestampNs / 1000000;
|
||||
// FIXME: Netd timestampNs is always 0.
|
||||
timestampMs = System.currentTimeMillis();
|
||||
|
||||
addWakupEvent(iface, timestampMs, uid);
|
||||
}
|
||||
|
||||
@GuardedBy("this")
|
||||
private void addWakupEvent(String iface, long timestampMs, int uid) {
|
||||
int index = wakeupEventIndex(mWakeupEventCursor);
|
||||
mWakeupEventCursor++;
|
||||
WakeupEvent event = new WakeupEvent();
|
||||
event.iface = iface;
|
||||
event.timestampMs = timestampMs;
|
||||
event.uid = uid;
|
||||
mWakeupEvents[index] = event;
|
||||
}
|
||||
|
||||
@GuardedBy("this")
|
||||
private WakeupEvent[] getWakeupEvents() {
|
||||
int length = (int) Math.min(mWakeupEventCursor, (long) mWakeupEvents.length);
|
||||
WakeupEvent[] out = new WakeupEvent[length];
|
||||
// Reverse iteration from youngest event to oldest event.
|
||||
long inCursor = mWakeupEventCursor - 1;
|
||||
int outIdx = out.length - 1;
|
||||
while (outIdx >= 0) {
|
||||
out[outIdx--] = mWakeupEvents[wakeupEventIndex(inCursor--)];
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
private static int wakeupEventIndex(long cursor) {
|
||||
return (int) Math.abs(cursor % WAKEUP_EVENT_BUFFER_LENGTH);
|
||||
}
|
||||
|
||||
public synchronized void flushStatistics(List<IpConnectivityEvent> events) {
|
||||
@@ -155,6 +205,7 @@ public class NetdEventListenerService extends INetdEventListener.Stub {
|
||||
public synchronized void list(PrintWriter pw) {
|
||||
listEvents(pw, mConnectEvents, (x) -> x);
|
||||
listEvents(pw, mDnsEvents, (x) -> x);
|
||||
listWakeupEvents(pw, getWakeupEvents());
|
||||
}
|
||||
|
||||
public synchronized void listAsProtos(PrintWriter pw) {
|
||||
@@ -170,13 +221,19 @@ public class NetdEventListenerService extends INetdEventListener.Stub {
|
||||
in.clear();
|
||||
}
|
||||
|
||||
public static <T> void listEvents(
|
||||
private static <T> void listEvents(
|
||||
PrintWriter pw, SparseArray<T> events, Function<T, Object> mapper) {
|
||||
for (int i = 0; i < events.size(); i++) {
|
||||
pw.println(mapper.apply(events.valueAt(i)).toString());
|
||||
}
|
||||
}
|
||||
|
||||
private static void listWakeupEvents(PrintWriter pw, WakeupEvent[] events) {
|
||||
for (WakeupEvent wakeup : events) {
|
||||
pw.println(wakeup);
|
||||
}
|
||||
}
|
||||
|
||||
private ConnectStats makeConnectStats(int netId) {
|
||||
long transports = getTransports(netId);
|
||||
return new ConnectStats(netId, transports, mConnectTb, CONNECT_LATENCY_MAXIMUM_RECORDS);
|
||||
|
||||
@@ -19,6 +19,7 @@ package com.android.server.connectivity;
|
||||
import static android.net.metrics.INetdEventListener.EVENT_GETADDRINFO;
|
||||
import static android.net.metrics.INetdEventListener.EVENT_GETHOSTBYNAME;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.mockito.Mockito.any;
|
||||
import static org.mockito.Mockito.mock;
|
||||
@@ -74,6 +75,52 @@ public class NetdEventListenerServiceTest {
|
||||
mNetdEventListenerService = new NetdEventListenerService(mCm);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWakeupEventLogging() throws Exception {
|
||||
final int BUFFER_LENGTH = NetdEventListenerService.WAKEUP_EVENT_BUFFER_LENGTH;
|
||||
|
||||
// Assert no events
|
||||
String[] events1 = listNetdEvent();
|
||||
assertEquals(new String[]{""}, events1);
|
||||
|
||||
long now = System.currentTimeMillis();
|
||||
String prefix = "iface:wlan0";
|
||||
int[] uids = { 10001, 10002, 10004, 1000, 10052, 10023, 10002, 10123, 10004 };
|
||||
for (int uid : uids) {
|
||||
mNetdEventListenerService.onWakeupEvent(prefix, uid, uid, now);
|
||||
}
|
||||
|
||||
String[] events2 = listNetdEvent();
|
||||
assertEquals(uids.length, events2.length);
|
||||
for (int i = 0; i < uids.length; i++) {
|
||||
String got = events2[i];
|
||||
assertContains(got, "wlan0");
|
||||
assertContains(got, "uid: " + uids[i]);
|
||||
}
|
||||
|
||||
int uid = 20000;
|
||||
for (int i = 0; i < BUFFER_LENGTH * 2; i++) {
|
||||
long ts = now + 10;
|
||||
mNetdEventListenerService.onWakeupEvent(prefix, uid, uid, ts);
|
||||
}
|
||||
|
||||
// Assert there are BUFFER_LENGTH events all with uid 20000
|
||||
String[] events3 = listNetdEvent();
|
||||
assertEquals(BUFFER_LENGTH, events3.length);
|
||||
for (String got : events3) {
|
||||
assertContains(got, "wlan0");
|
||||
assertContains(got, "uid: " + uid);
|
||||
}
|
||||
|
||||
uid = 45678;
|
||||
mNetdEventListenerService.onWakeupEvent(prefix, uid, uid, now);
|
||||
|
||||
String[] events4 = listNetdEvent();
|
||||
String lastEvent = events4[events4.length - 1];
|
||||
assertContains(lastEvent, "wlan0");
|
||||
assertContains(lastEvent, "uid: " + uid);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDnsLogging() throws Exception {
|
||||
asyncDump(100);
|
||||
@@ -329,4 +376,15 @@ public class NetdEventListenerServiceTest {
|
||||
}
|
||||
return log.toString();
|
||||
}
|
||||
|
||||
String[] listNetdEvent() throws Exception {
|
||||
StringWriter buffer = new StringWriter();
|
||||
PrintWriter writer = new PrintWriter(buffer);
|
||||
mNetdEventListenerService.list(writer);
|
||||
return buffer.toString().split("\\n");
|
||||
}
|
||||
|
||||
static void assertContains(String got, String want) {
|
||||
assertTrue(got + " did not contain \"" + want + "\"", got.contains(want));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user