Move network stats to FileRotator pattern.
Split existing network stats into two separate classes: a recorder which generates historical data based on periodic counter snapshots, and a collection of historical data with persistance logic. Recorder keeps a pending history in memory until outstanding data crosses a specific threshold. Persisting is handled through a given FileRotator. This pattern significantly reduces disk churn and memory overhead. Separate UID data from UID tag data, enabling a shorter rotation cycle. Migrate existing stats into new structure. Remove "xt" stats until iptables hooks are ready. Avoid consuming Entry values when recording into NetworkStatsHistory. Assign operation counts to default route interface. Introduce "Rewriter" interface in FileRotator with methods to enable rewriteAll(). Introduce IndentingPrintWriter to handle indenting in dump() methods. Bug: 5386531 Change-Id: Ibe086230a17999a197206ca62d45f266225fdff1
This commit is contained in:
@@ -102,6 +102,15 @@ public class NetworkStats implements Parcelable {
|
||||
this.operations = operations;
|
||||
}
|
||||
|
||||
public boolean isNegative() {
|
||||
return rxBytes < 0 || rxPackets < 0 || txBytes < 0 || txPackets < 0 || operations < 0;
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return rxBytes == 0 && rxPackets == 0 && txBytes == 0 && txPackets == 0
|
||||
&& operations == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
@@ -343,6 +352,7 @@ public class NetworkStats implements Parcelable {
|
||||
* on matching {@link #uid} and {@link #tag} rows. Ignores {@link #iface},
|
||||
* since operation counts are at data layer.
|
||||
*/
|
||||
@Deprecated
|
||||
public void spliceOperationsFrom(NetworkStats stats) {
|
||||
for (int i = 0; i < size; i++) {
|
||||
final int j = stats.findIndex(IFACE_ALL, uid[i], set[i], tag[i]);
|
||||
@@ -397,7 +407,7 @@ public class NetworkStats implements Parcelable {
|
||||
* Return total of all fields represented by this snapshot object.
|
||||
*/
|
||||
public Entry getTotal(Entry recycle) {
|
||||
return getTotal(recycle, null, UID_ALL);
|
||||
return getTotal(recycle, null, UID_ALL, false);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -405,7 +415,7 @@ public class NetworkStats implements Parcelable {
|
||||
* the requested {@link #uid}.
|
||||
*/
|
||||
public Entry getTotal(Entry recycle, int limitUid) {
|
||||
return getTotal(recycle, null, limitUid);
|
||||
return getTotal(recycle, null, limitUid, false);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -413,7 +423,11 @@ public class NetworkStats implements Parcelable {
|
||||
* the requested {@link #iface}.
|
||||
*/
|
||||
public Entry getTotal(Entry recycle, HashSet<String> limitIface) {
|
||||
return getTotal(recycle, limitIface, UID_ALL);
|
||||
return getTotal(recycle, limitIface, UID_ALL, false);
|
||||
}
|
||||
|
||||
public Entry getTotalIncludingTags(Entry recycle) {
|
||||
return getTotal(recycle, null, UID_ALL, true);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -423,7 +437,8 @@ public class NetworkStats implements Parcelable {
|
||||
* @param limitIface Set of {@link #iface} to include in total; or {@code
|
||||
* null} to include all ifaces.
|
||||
*/
|
||||
private Entry getTotal(Entry recycle, HashSet<String> limitIface, int limitUid) {
|
||||
private Entry getTotal(
|
||||
Entry recycle, HashSet<String> limitIface, int limitUid, boolean includeTags) {
|
||||
final Entry entry = recycle != null ? recycle : new Entry();
|
||||
|
||||
entry.iface = IFACE_ALL;
|
||||
@@ -442,7 +457,7 @@ public class NetworkStats implements Parcelable {
|
||||
|
||||
if (matchesUid && matchesIface) {
|
||||
// skip specific tags, since already counted in TAG_NONE
|
||||
if (tag[i] != TAG_NONE) continue;
|
||||
if (tag[i] != TAG_NONE && !includeTags) continue;
|
||||
|
||||
entry.rxBytes += rxBytes[i];
|
||||
entry.rxPackets += rxPackets[i];
|
||||
@@ -460,7 +475,7 @@ public class NetworkStats implements Parcelable {
|
||||
* time, and that none of them have disappeared.
|
||||
*/
|
||||
public NetworkStats subtract(NetworkStats right) {
|
||||
return subtract(this, right, null);
|
||||
return subtract(this, right, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -471,12 +486,12 @@ public class NetworkStats implements Parcelable {
|
||||
* If counters have rolled backwards, they are clamped to {@code 0} and
|
||||
* reported to the given {@link NonMonotonicObserver}.
|
||||
*/
|
||||
public static NetworkStats subtract(
|
||||
NetworkStats left, NetworkStats right, NonMonotonicObserver observer) {
|
||||
public static <C> NetworkStats subtract(
|
||||
NetworkStats left, NetworkStats right, NonMonotonicObserver<C> observer, C cookie) {
|
||||
long deltaRealtime = left.elapsedRealtime - right.elapsedRealtime;
|
||||
if (deltaRealtime < 0) {
|
||||
if (observer != null) {
|
||||
observer.foundNonMonotonic(left, -1, right, -1);
|
||||
observer.foundNonMonotonic(left, -1, right, -1, cookie);
|
||||
}
|
||||
deltaRealtime = 0;
|
||||
}
|
||||
@@ -510,7 +525,7 @@ public class NetworkStats implements Parcelable {
|
||||
if (entry.rxBytes < 0 || entry.rxPackets < 0 || entry.txBytes < 0
|
||||
|| entry.txPackets < 0 || entry.operations < 0) {
|
||||
if (observer != null) {
|
||||
observer.foundNonMonotonic(left, i, right, j);
|
||||
observer.foundNonMonotonic(left, i, right, j, cookie);
|
||||
}
|
||||
entry.rxBytes = Math.max(entry.rxBytes, 0);
|
||||
entry.rxPackets = Math.max(entry.rxPackets, 0);
|
||||
@@ -663,8 +678,8 @@ public class NetworkStats implements Parcelable {
|
||||
}
|
||||
};
|
||||
|
||||
public interface NonMonotonicObserver {
|
||||
public interface NonMonotonicObserver<C> {
|
||||
public void foundNonMonotonic(
|
||||
NetworkStats left, int leftIndex, NetworkStats right, int rightIndex);
|
||||
NetworkStats left, int leftIndex, NetworkStats right, int rightIndex, C cookie);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,16 +26,18 @@ import static android.net.NetworkStatsHistory.DataStreamUtils.writeVarLongArray;
|
||||
import static android.net.NetworkStatsHistory.Entry.UNKNOWN;
|
||||
import static android.net.NetworkStatsHistory.ParcelUtils.readLongArray;
|
||||
import static android.net.NetworkStatsHistory.ParcelUtils.writeLongArray;
|
||||
import static com.android.internal.util.ArrayUtils.total;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.util.MathUtils;
|
||||
|
||||
import com.android.internal.util.IndentingPrintWriter;
|
||||
|
||||
import java.io.CharArrayWriter;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.net.ProtocolException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Random;
|
||||
@@ -74,6 +76,7 @@ public class NetworkStatsHistory implements Parcelable {
|
||||
private long[] txBytes;
|
||||
private long[] txPackets;
|
||||
private long[] operations;
|
||||
private long totalBytes;
|
||||
|
||||
public static class Entry {
|
||||
public static final long UNKNOWN = -1;
|
||||
@@ -106,6 +109,12 @@ public class NetworkStatsHistory implements Parcelable {
|
||||
if ((fields & FIELD_TX_PACKETS) != 0) txPackets = new long[initialSize];
|
||||
if ((fields & FIELD_OPERATIONS) != 0) operations = new long[initialSize];
|
||||
bucketCount = 0;
|
||||
totalBytes = 0;
|
||||
}
|
||||
|
||||
public NetworkStatsHistory(NetworkStatsHistory existing, long bucketDuration) {
|
||||
this(bucketDuration, existing.estimateResizeBuckets(bucketDuration));
|
||||
recordEntireHistory(existing);
|
||||
}
|
||||
|
||||
public NetworkStatsHistory(Parcel in) {
|
||||
@@ -118,6 +127,7 @@ public class NetworkStatsHistory implements Parcelable {
|
||||
txPackets = readLongArray(in);
|
||||
operations = readLongArray(in);
|
||||
bucketCount = bucketStart.length;
|
||||
totalBytes = in.readLong();
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@@ -130,6 +140,7 @@ public class NetworkStatsHistory implements Parcelable {
|
||||
writeLongArray(out, txBytes, bucketCount);
|
||||
writeLongArray(out, txPackets, bucketCount);
|
||||
writeLongArray(out, operations, bucketCount);
|
||||
out.writeLong(totalBytes);
|
||||
}
|
||||
|
||||
public NetworkStatsHistory(DataInputStream in) throws IOException {
|
||||
@@ -144,6 +155,7 @@ public class NetworkStatsHistory implements Parcelable {
|
||||
txPackets = new long[bucketStart.length];
|
||||
operations = new long[bucketStart.length];
|
||||
bucketCount = bucketStart.length;
|
||||
totalBytes = total(rxBytes) + total(txBytes);
|
||||
break;
|
||||
}
|
||||
case VERSION_ADD_PACKETS:
|
||||
@@ -158,6 +170,7 @@ public class NetworkStatsHistory implements Parcelable {
|
||||
txPackets = readVarLongArray(in);
|
||||
operations = readVarLongArray(in);
|
||||
bucketCount = bucketStart.length;
|
||||
totalBytes = total(rxBytes) + total(txBytes);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
@@ -207,6 +220,13 @@ public class NetworkStatsHistory implements Parcelable {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return total bytes represented by this history.
|
||||
*/
|
||||
public long getTotalBytes() {
|
||||
return totalBytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return index of bucket that contains or is immediately before the
|
||||
* requested time.
|
||||
@@ -266,13 +286,16 @@ public class NetworkStatsHistory implements Parcelable {
|
||||
* distribute across internal buckets, creating new buckets as needed.
|
||||
*/
|
||||
public void recordData(long start, long end, NetworkStats.Entry entry) {
|
||||
if (entry.rxBytes < 0 || entry.rxPackets < 0 || entry.txBytes < 0 || entry.txPackets < 0
|
||||
|| entry.operations < 0) {
|
||||
long rxBytes = entry.rxBytes;
|
||||
long rxPackets = entry.rxPackets;
|
||||
long txBytes = entry.txBytes;
|
||||
long txPackets = entry.txPackets;
|
||||
long operations = entry.operations;
|
||||
|
||||
if (entry.isNegative()) {
|
||||
throw new IllegalArgumentException("tried recording negative data");
|
||||
}
|
||||
if (entry.rxBytes == 0 && entry.rxPackets == 0 && entry.txBytes == 0 && entry.txPackets == 0
|
||||
&& entry.operations == 0) {
|
||||
// nothing to record; skip
|
||||
if (entry.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -295,21 +318,23 @@ public class NetworkStatsHistory implements Parcelable {
|
||||
if (overlap <= 0) continue;
|
||||
|
||||
// integer math each time is faster than floating point
|
||||
final long fracRxBytes = entry.rxBytes * overlap / duration;
|
||||
final long fracRxPackets = entry.rxPackets * overlap / duration;
|
||||
final long fracTxBytes = entry.txBytes * overlap / duration;
|
||||
final long fracTxPackets = entry.txPackets * overlap / duration;
|
||||
final long fracOperations = entry.operations * overlap / duration;
|
||||
final long fracRxBytes = rxBytes * overlap / duration;
|
||||
final long fracRxPackets = rxPackets * overlap / duration;
|
||||
final long fracTxBytes = txBytes * overlap / duration;
|
||||
final long fracTxPackets = txPackets * overlap / duration;
|
||||
final long fracOperations = operations * overlap / duration;
|
||||
|
||||
addLong(activeTime, i, overlap);
|
||||
addLong(rxBytes, i, fracRxBytes); entry.rxBytes -= fracRxBytes;
|
||||
addLong(rxPackets, i, fracRxPackets); entry.rxPackets -= fracRxPackets;
|
||||
addLong(txBytes, i, fracTxBytes); entry.txBytes -= fracTxBytes;
|
||||
addLong(txPackets, i, fracTxPackets); entry.txPackets -= fracTxPackets;
|
||||
addLong(operations, i, fracOperations); entry.operations -= fracOperations;
|
||||
addLong(this.rxBytes, i, fracRxBytes); rxBytes -= fracRxBytes;
|
||||
addLong(this.rxPackets, i, fracRxPackets); rxPackets -= fracRxPackets;
|
||||
addLong(this.txBytes, i, fracTxBytes); txBytes -= fracTxBytes;
|
||||
addLong(this.txPackets, i, fracTxPackets); txPackets -= fracTxPackets;
|
||||
addLong(this.operations, i, fracOperations); operations -= fracOperations;
|
||||
|
||||
duration -= overlap;
|
||||
}
|
||||
|
||||
totalBytes += entry.rxBytes + entry.txBytes;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -394,6 +419,7 @@ public class NetworkStatsHistory implements Parcelable {
|
||||
/**
|
||||
* Remove buckets older than requested cutoff.
|
||||
*/
|
||||
@Deprecated
|
||||
public void removeBucketsBefore(long cutoff) {
|
||||
int i;
|
||||
for (i = 0; i < bucketCount; i++) {
|
||||
@@ -415,6 +441,8 @@ public class NetworkStatsHistory implements Parcelable {
|
||||
if (txPackets != null) txPackets = Arrays.copyOfRange(txPackets, i, length);
|
||||
if (operations != null) operations = Arrays.copyOfRange(operations, i, length);
|
||||
bucketCount -= i;
|
||||
|
||||
// TODO: subtract removed values from totalBytes
|
||||
}
|
||||
}
|
||||
|
||||
@@ -527,19 +555,17 @@ public class NetworkStatsHistory implements Parcelable {
|
||||
return (long) (start + (r.nextFloat() * (end - start)));
|
||||
}
|
||||
|
||||
public void dump(String prefix, PrintWriter pw, boolean fullHistory) {
|
||||
pw.print(prefix);
|
||||
public void dump(IndentingPrintWriter pw, boolean fullHistory) {
|
||||
pw.print("NetworkStatsHistory: bucketDuration="); pw.println(bucketDuration);
|
||||
pw.increaseIndent();
|
||||
|
||||
final int start = fullHistory ? 0 : Math.max(0, bucketCount - 32);
|
||||
if (start > 0) {
|
||||
pw.print(prefix);
|
||||
pw.print(" (omitting "); pw.print(start); pw.println(" buckets)");
|
||||
pw.print("(omitting "); pw.print(start); pw.println(" buckets)");
|
||||
}
|
||||
|
||||
for (int i = start; i < bucketCount; i++) {
|
||||
pw.print(prefix);
|
||||
pw.print(" bucketStart="); pw.print(bucketStart[i]);
|
||||
pw.print("bucketStart="); pw.print(bucketStart[i]);
|
||||
if (activeTime != null) { pw.print(" activeTime="); pw.print(activeTime[i]); }
|
||||
if (rxBytes != null) { pw.print(" rxBytes="); pw.print(rxBytes[i]); }
|
||||
if (rxPackets != null) { pw.print(" rxPackets="); pw.print(rxPackets[i]); }
|
||||
@@ -548,12 +574,14 @@ public class NetworkStatsHistory implements Parcelable {
|
||||
if (operations != null) { pw.print(" operations="); pw.print(operations[i]); }
|
||||
pw.println();
|
||||
}
|
||||
|
||||
pw.decreaseIndent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final CharArrayWriter writer = new CharArrayWriter();
|
||||
dump("", new PrintWriter(writer), false);
|
||||
dump(new IndentingPrintWriter(writer, " "), false);
|
||||
return writer.toString();
|
||||
}
|
||||
|
||||
@@ -579,6 +607,10 @@ public class NetworkStatsHistory implements Parcelable {
|
||||
if (array != null) array[i] += value;
|
||||
}
|
||||
|
||||
public int estimateResizeBuckets(long newBucketDuration) {
|
||||
return (int) (size() * getBucketDuration() / newBucketDuration);
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility methods for interacting with {@link DataInputStream} and
|
||||
* {@link DataOutputStream}, mostly dealing with writing partial arrays.
|
||||
|
||||
@@ -195,7 +195,7 @@ public class TrafficStats {
|
||||
// subtract starting values and return delta
|
||||
final NetworkStats profilingStop = getDataLayerSnapshotForUid(context);
|
||||
final NetworkStats profilingDelta = NetworkStats.subtract(
|
||||
profilingStop, sActiveProfilingStart, null);
|
||||
profilingStop, sActiveProfilingStart, null, null);
|
||||
sActiveProfilingStart = null;
|
||||
return profilingDelta;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user