Correct proc file reader, optimizations.
Moved away from BufferedReader, which only reads the first 8KB of some proc files because it aggresively fills its buffer. Optimized proc parsing, now double the speed. Tests to cover. Log when NetworkStats counters roll backwards when subtracting, and optimizations around findIndex(). When system removes UID, also remove from last stats snapshot to avoid xt counters from rolling backwards. Bug: 5472949, 5458380 Change-Id: I07c08fe5233156fac2b84450f6291868bf9bfaf2
This commit is contained in:
@@ -16,10 +16,11 @@
|
||||
|
||||
package android.net;
|
||||
|
||||
import static com.android.internal.util.Preconditions.checkNotNull;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.os.SystemClock;
|
||||
import android.util.Log;
|
||||
import android.util.SparseBooleanArray;
|
||||
|
||||
import com.android.internal.util.Objects;
|
||||
@@ -54,6 +55,8 @@ public class NetworkStats implements Parcelable {
|
||||
/** {@link #tag} value for total data across all tags. */
|
||||
public static final int TAG_NONE = 0;
|
||||
|
||||
// TODO: move fields to "mVariable" notation
|
||||
|
||||
/**
|
||||
* {@link SystemClock#elapsedRealtime()} timestamp when this data was
|
||||
* generated.
|
||||
@@ -295,8 +298,33 @@ public class NetworkStats implements Parcelable {
|
||||
*/
|
||||
public int findIndex(String iface, int uid, int set, int tag) {
|
||||
for (int i = 0; i < size; i++) {
|
||||
if (Objects.equal(iface, this.iface[i]) && uid == this.uid[i] && set == this.set[i]
|
||||
&& tag == this.tag[i]) {
|
||||
if (uid == this.uid[i] && set == this.set[i] && tag == this.tag[i]
|
||||
&& Objects.equal(iface, this.iface[i])) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find first stats index that matches the requested parameters, starting
|
||||
* search around the hinted index as an optimization.
|
||||
*/
|
||||
// @VisibleForTesting
|
||||
public int findIndexHinted(String iface, int uid, int set, int tag, int hintIndex) {
|
||||
for (int offset = 0; offset < size; offset++) {
|
||||
final int halfOffset = offset / 2;
|
||||
|
||||
// search outwards from hint index, alternating forward and backward
|
||||
final int i;
|
||||
if (offset % 2 == 0) {
|
||||
i = (hintIndex + halfOffset) % size;
|
||||
} else {
|
||||
i = (size + hintIndex - halfOffset - 1) % size;
|
||||
}
|
||||
|
||||
if (uid == this.uid[i] && set == this.set[i] && tag == this.tag[i]
|
||||
&& Objects.equal(iface, this.iface[i])) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
@@ -423,40 +451,10 @@ public class NetworkStats implements Parcelable {
|
||||
* Subtract the given {@link NetworkStats}, effectively leaving the delta
|
||||
* between two snapshots in time. Assumes that statistics rows collect over
|
||||
* time, and that none of them have disappeared.
|
||||
*
|
||||
* @throws IllegalArgumentException when given {@link NetworkStats} is
|
||||
* non-monotonic.
|
||||
*/
|
||||
public NetworkStats subtract(NetworkStats value) {
|
||||
return subtract(value, true, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Subtract the given {@link NetworkStats}, effectively leaving the delta
|
||||
* between two snapshots in time. Assumes that statistics rows collect over
|
||||
* time, and that none of them have disappeared.
|
||||
* <p>
|
||||
* Instead of throwing when counters are non-monotonic, this variant clamps
|
||||
* results to never be negative.
|
||||
*/
|
||||
public NetworkStats subtractClamped(NetworkStats value) {
|
||||
return subtract(value, false, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Subtract the given {@link NetworkStats}, effectively leaving the delta
|
||||
* between two snapshots in time. Assumes that statistics rows collect over
|
||||
* time, and that none of them have disappeared.
|
||||
*
|
||||
* @param enforceMonotonic Validate that incoming value is strictly
|
||||
* monotonic compared to this object.
|
||||
* @param clampNegative Instead of throwing like {@code enforceMonotonic},
|
||||
* clamp resulting counters at 0 to prevent negative values.
|
||||
*/
|
||||
private NetworkStats subtract(
|
||||
NetworkStats value, boolean enforceMonotonic, boolean clampNegative) {
|
||||
public NetworkStats subtract(NetworkStats value) throws NonMonotonicException {
|
||||
final long deltaRealtime = this.elapsedRealtime - value.elapsedRealtime;
|
||||
if (enforceMonotonic && deltaRealtime < 0) {
|
||||
if (deltaRealtime < 0) {
|
||||
throw new IllegalArgumentException("found non-monotonic realtime");
|
||||
}
|
||||
|
||||
@@ -470,7 +468,7 @@ public class NetworkStats implements Parcelable {
|
||||
entry.tag = tag[i];
|
||||
|
||||
// find remote row that matches, and subtract
|
||||
final int j = value.findIndex(entry.iface, entry.uid, entry.set, entry.tag);
|
||||
final int j = value.findIndexHinted(entry.iface, entry.uid, entry.set, entry.tag, i);
|
||||
if (j == -1) {
|
||||
// newly appearing row, return entire value
|
||||
entry.rxBytes = rxBytes[i];
|
||||
@@ -485,20 +483,10 @@ public class NetworkStats implements Parcelable {
|
||||
entry.txBytes = txBytes[i] - value.txBytes[j];
|
||||
entry.txPackets = txPackets[i] - value.txPackets[j];
|
||||
entry.operations = operations[i] - value.operations[j];
|
||||
if (enforceMonotonic
|
||||
&& (entry.rxBytes < 0 || entry.rxPackets < 0 || entry.txBytes < 0
|
||||
|| entry.txPackets < 0 || entry.operations < 0)) {
|
||||
Log.v(TAG, "lhs=" + this);
|
||||
Log.v(TAG, "rhs=" + value);
|
||||
throw new IllegalArgumentException(
|
||||
"found non-monotonic values at lhs[" + i + "] - rhs[" + j + "]");
|
||||
}
|
||||
if (clampNegative) {
|
||||
entry.rxBytes = Math.max(0, entry.rxBytes);
|
||||
entry.rxPackets = Math.max(0, entry.rxPackets);
|
||||
entry.txBytes = Math.max(0, entry.txBytes);
|
||||
entry.txPackets = Math.max(0, entry.txPackets);
|
||||
entry.operations = Math.max(0, entry.operations);
|
||||
|
||||
if (entry.rxBytes < 0 || entry.rxPackets < 0 || entry.txBytes < 0
|
||||
|| entry.txPackets < 0 || entry.operations < 0) {
|
||||
throw new NonMonotonicException(this, i, value, j);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -564,6 +552,24 @@ public class NetworkStats implements Parcelable {
|
||||
return stats;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all rows except those attributed to the requested UID; doesn't
|
||||
* mutate the original structure.
|
||||
*/
|
||||
public NetworkStats withoutUid(int uid) {
|
||||
final NetworkStats stats = new NetworkStats(elapsedRealtime, 10);
|
||||
|
||||
Entry entry = new Entry();
|
||||
for (int i = 0; i < size; i++) {
|
||||
entry = getValues(i, entry);
|
||||
if (entry.uid != uid) {
|
||||
stats.addValues(entry);
|
||||
}
|
||||
}
|
||||
|
||||
return stats;
|
||||
}
|
||||
|
||||
public void dump(String prefix, PrintWriter pw) {
|
||||
pw.print(prefix);
|
||||
pw.print("NetworkStats: elapsedRealtime="); pw.println(elapsedRealtime);
|
||||
@@ -625,4 +631,19 @@ public class NetworkStats implements Parcelable {
|
||||
return new NetworkStats[size];
|
||||
}
|
||||
};
|
||||
|
||||
public static class NonMonotonicException extends Exception {
|
||||
public final NetworkStats left;
|
||||
public final NetworkStats right;
|
||||
public final int leftIndex;
|
||||
public final int rightIndex;
|
||||
|
||||
public NonMonotonicException(
|
||||
NetworkStats left, int leftIndex, NetworkStats right, int rightIndex) {
|
||||
this.left = checkNotNull(left, "missing left");
|
||||
this.right = checkNotNull(right, "missing right");
|
||||
this.leftIndex = leftIndex;
|
||||
this.rightIndex = rightIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import android.app.DownloadManager;
|
||||
import android.app.backup.BackupManager;
|
||||
import android.content.Context;
|
||||
import android.media.MediaPlayer;
|
||||
import android.net.NetworkStats.NonMonotonicException;
|
||||
import android.os.RemoteException;
|
||||
import android.os.ServiceManager;
|
||||
|
||||
@@ -192,12 +193,15 @@ public class TrafficStats {
|
||||
throw new IllegalStateException("not profiling data");
|
||||
}
|
||||
|
||||
// subtract starting values and return delta
|
||||
final NetworkStats profilingStop = getDataLayerSnapshotForUid(context);
|
||||
final NetworkStats profilingDelta = profilingStop.subtractClamped(
|
||||
sActiveProfilingStart);
|
||||
sActiveProfilingStart = null;
|
||||
return profilingDelta;
|
||||
try {
|
||||
// subtract starting values and return delta
|
||||
final NetworkStats profilingStop = getDataLayerSnapshotForUid(context);
|
||||
final NetworkStats profilingDelta = profilingStop.subtract(sActiveProfilingStart);
|
||||
sActiveProfilingStart = null;
|
||||
return profilingDelta;
|
||||
} catch (NonMonotonicException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user