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:
Jeff Sharkey
2012-01-11 18:38:16 -08:00
parent e32bcef8e1
commit bfe82685e7
6 changed files with 1217 additions and 1033 deletions

View File

@@ -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);
}
}

View File

@@ -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.

View File

@@ -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;
}