DO NOT MERGE: Sample atomic network stats buckets, full poll.

When sampling network stats, always use atomic buckets instead of
interpolating.  Always poll iface and UID together so we distribute
into buckets equally.  Move stale bucket trimming to just before
writing stats.

Bug: 5321340
Change-Id: I78a2226778a79c875f3668336e39ea24a7b4d5c4
This commit is contained in:
Jeff Sharkey
2011-09-18 13:30:23 -07:00
parent 987f5ff357
commit b10824574c
2 changed files with 79 additions and 98 deletions

View File

@@ -441,10 +441,10 @@ public class NetworkStatsHistory implements Parcelable {
final long curStart = bucketStart[i]; final long curStart = bucketStart[i];
final long curEnd = curStart + bucketDuration; final long curEnd = curStart + bucketDuration;
// bucket is older than record; we're finished // bucket is older than request; we're finished
if (curEnd < start) break; if (curEnd <= start) break;
// bucket is newer than record; keep looking // bucket is newer than request; keep looking
if (curStart > end) continue; if (curStart >= end) continue;
// include full value for active buckets, otherwise only fractional // include full value for active buckets, otherwise only fractional
final boolean activeBucket = curStart < now && curEnd > now; final boolean activeBucket = curStart < now && curEnd > now;
@@ -466,7 +466,6 @@ public class NetworkStatsHistory implements Parcelable {
if (txPackets != null) entry.txPackets += txPackets[i] * overlap / bucketDuration; if (txPackets != null) entry.txPackets += txPackets[i] * overlap / bucketDuration;
if (operations != null) entry.operations += operations[i] * overlap / bucketDuration; if (operations != null) entry.operations += operations[i] * overlap / bucketDuration;
} }
return entry; return entry;
} }

View File

@@ -36,7 +36,6 @@ import static android.net.NetworkTemplate.buildTemplateMobileAll;
import static android.net.NetworkTemplate.buildTemplateWifi; import static android.net.NetworkTemplate.buildTemplateWifi;
import static android.net.TrafficStats.UID_REMOVED; import static android.net.TrafficStats.UID_REMOVED;
import static android.net.TrafficStats.UID_TETHERING; import static android.net.TrafficStats.UID_TETHERING;
import static android.provider.Settings.Secure.NETSTATS_FORCE_COMPLETE_POLL;
import static android.provider.Settings.Secure.NETSTATS_NETWORK_BUCKET_DURATION; import static android.provider.Settings.Secure.NETSTATS_NETWORK_BUCKET_DURATION;
import static android.provider.Settings.Secure.NETSTATS_NETWORK_MAX_HISTORY; import static android.provider.Settings.Secure.NETSTATS_NETWORK_MAX_HISTORY;
import static android.provider.Settings.Secure.NETSTATS_PERSIST_THRESHOLD; import static android.provider.Settings.Secure.NETSTATS_PERSIST_THRESHOLD;
@@ -136,15 +135,10 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
private static final int MSG_PERFORM_POLL = 0x1; private static final int MSG_PERFORM_POLL = 0x1;
/** Flags to control detail level of poll event. */ /** Flags to control detail level of poll event. */
private static final int FLAG_POLL_NETWORK = 0x1;
private static final int FLAG_POLL_UID = 0x2;
private static final int FLAG_POLL_TETHER = 0x3;
private static final int FLAG_PERSIST_NETWORK = 0x10; private static final int FLAG_PERSIST_NETWORK = 0x10;
private static final int FLAG_PERSIST_UID = 0x20; private static final int FLAG_PERSIST_UID = 0x20;
private static final int FLAG_FORCE_PERSIST = 0x100;
private static final int FLAG_POLL_ALL = FLAG_POLL_NETWORK | FLAG_POLL_UID | FLAG_POLL_TETHER;
private static final int FLAG_PERSIST_ALL = FLAG_PERSIST_NETWORK | FLAG_PERSIST_UID; private static final int FLAG_PERSIST_ALL = FLAG_PERSIST_NETWORK | FLAG_PERSIST_UID;
private static final int FLAG_PERSIST_FORCE = 0x100;
private final Context mContext; private final Context mContext;
private final INetworkManagementService mNetworkManager; private final INetworkManagementService mNetworkManager;
@@ -182,7 +176,6 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
public long getUidMaxHistory(); public long getUidMaxHistory();
public long getTagMaxHistory(); public long getTagMaxHistory();
public long getTimeCacheMaxAge(); public long getTimeCacheMaxAge();
public boolean getForceCompletePoll();
} }
private final Object mStatsLock = new Object(); private final Object mStatsLock = new Object();
@@ -529,7 +522,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
@Override @Override
public void forceUpdate() { public void forceUpdate() {
mContext.enforceCallingOrSelfPermission(READ_NETWORK_USAGE_HISTORY, TAG); mContext.enforceCallingOrSelfPermission(READ_NETWORK_USAGE_HISTORY, TAG);
performPoll(FLAG_POLL_ALL | FLAG_PERSIST_ALL); performPoll(FLAG_PERSIST_ALL);
} }
/** /**
@@ -561,7 +554,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {
// on background handler thread, and verified CONNECTIVITY_INTERNAL // on background handler thread, and verified CONNECTIVITY_INTERNAL
// permission above. // permission above.
performPoll(FLAG_POLL_TETHER); performPoll(FLAG_PERSIST_NETWORK);
} }
}; };
@@ -570,7 +563,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {
// on background handler thread, and verified UPDATE_DEVICE_STATS // on background handler thread, and verified UPDATE_DEVICE_STATS
// permission above. // permission above.
performPoll(FLAG_POLL_ALL | FLAG_PERSIST_ALL); performPoll(FLAG_PERSIST_ALL);
// verify that we're watching global alert // verify that we're watching global alert
registerGlobalAlert(); registerGlobalAlert();
@@ -617,7 +610,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
if (LIMIT_GLOBAL_ALERT.equals(limitName)) { if (LIMIT_GLOBAL_ALERT.equals(limitName)) {
// kick off background poll to collect network stats; UID stats // kick off background poll to collect network stats; UID stats
// are handled during normal polling interval. // are handled during normal polling interval.
final int flags = FLAG_POLL_NETWORK | FLAG_PERSIST_NETWORK; final int flags = FLAG_PERSIST_NETWORK;
mHandler.obtainMessage(MSG_PERFORM_POLL, flags, 0).sendToTarget(); mHandler.obtainMessage(MSG_PERFORM_POLL, flags, 0).sendToTarget();
// re-arm global alert for next update // re-arm global alert for next update
@@ -639,10 +632,9 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
// isn't perfect, since the kernel may already be counting traffic from // isn't perfect, since the kernel may already be counting traffic from
// the updated network. // the updated network.
// poll both network and UID stats, but only persist network stats, // poll, but only persist network stats to keep codepath fast. UID stats
// since this codepath should stay fast. UID stats will be persisted // will be persisted during next alarm poll event.
// during next alarm poll event. performPollLocked(FLAG_PERSIST_NETWORK);
performPollLocked(FLAG_POLL_ALL | FLAG_PERSIST_NETWORK);
final NetworkState[] states; final NetworkState[] states;
try { try {
@@ -706,21 +698,9 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
if (LOGV) Slog.v(TAG, "performPollLocked(flags=0x" + Integer.toHexString(flags) + ")"); if (LOGV) Slog.v(TAG, "performPollLocked(flags=0x" + Integer.toHexString(flags) + ")");
final long startRealtime = SystemClock.elapsedRealtime(); final long startRealtime = SystemClock.elapsedRealtime();
boolean pollNetwork = (flags & FLAG_POLL_NETWORK) != 0;
boolean pollUid = (flags & FLAG_POLL_UID) != 0;
boolean pollTether = (flags & FLAG_POLL_TETHER) != 0;
// when complete poll requested, any partial poll enables everything
final boolean forceCompletePoll = mSettings.getForceCompletePoll();
if (forceCompletePoll && (pollNetwork || pollUid || pollTether)) {
pollNetwork = true;
pollUid = true;
pollTether = true;
}
final boolean persistNetwork = (flags & FLAG_PERSIST_NETWORK) != 0; final boolean persistNetwork = (flags & FLAG_PERSIST_NETWORK) != 0;
final boolean persistUid = (flags & FLAG_PERSIST_UID) != 0; final boolean persistUid = (flags & FLAG_PERSIST_UID) != 0;
final boolean forcePersist = (flags & FLAG_FORCE_PERSIST) != 0; final boolean persistForce = (flags & FLAG_PERSIST_FORCE) != 0;
// try refreshing time source when stale // try refreshing time source when stale
if (mTime.getCacheAge() > mSettings.getTimeCacheMaxAge()) { if (mTime.getCacheAge() > mSettings.getTimeCacheMaxAge()) {
@@ -733,41 +713,36 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
final long threshold = mSettings.getPersistThreshold(); final long threshold = mSettings.getPersistThreshold();
try { try {
if (pollNetwork) { // record network stats
final NetworkStats networkSnapshot = mNetworkManager.getNetworkStatsSummary(); final NetworkStats networkSnapshot = mNetworkManager.getNetworkStatsSummary();
performNetworkPollLocked(networkSnapshot, currentTime); performNetworkPollLocked(networkSnapshot, currentTime);
// persist when enough network data has occurred // persist when enough network data has occurred
final NetworkStats persistNetworkDelta = computeStatsDelta( final NetworkStats persistNetworkDelta = computeStatsDelta(
mLastPersistNetworkSnapshot, networkSnapshot, true); mLastPersistNetworkSnapshot, networkSnapshot, true);
final boolean pastThreshold = persistNetworkDelta.getTotalBytes() > threshold; final boolean networkPastThreshold = persistNetworkDelta.getTotalBytes() > threshold;
if (forcePersist || (persistNetwork && pastThreshold)) { if (persistForce || (persistNetwork && networkPastThreshold)) {
writeNetworkStatsLocked(); writeNetworkStatsLocked();
mLastPersistNetworkSnapshot = networkSnapshot; mLastPersistNetworkSnapshot = networkSnapshot;
}
} }
if (pollTether) { // record tethering stats; persisted during normal UID cycle below
final String[] ifacePairs = mConnManager.getTetheredIfacePairs(); final String[] ifacePairs = mConnManager.getTetheredIfacePairs();
final NetworkStats tetherSnapshot = mNetworkManager.getNetworkStatsTethering( final NetworkStats tetherSnapshot = mNetworkManager.getNetworkStatsTethering(
ifacePairs); ifacePairs);
performTetherPollLocked(tetherSnapshot, currentTime); performTetherPollLocked(tetherSnapshot, currentTime);
// persisted during normal UID cycle below // record uid stats
} final NetworkStats uidSnapshot = mNetworkManager.getNetworkStatsUidDetail(UID_ALL);
performUidPollLocked(uidSnapshot, currentTime);
if (pollUid) { // persist when enough network data has occurred
final NetworkStats uidSnapshot = mNetworkManager.getNetworkStatsUidDetail(UID_ALL); final NetworkStats persistUidDelta = computeStatsDelta(
performUidPollLocked(uidSnapshot, currentTime); mLastPersistUidSnapshot, uidSnapshot, true);
final boolean uidPastThreshold = persistUidDelta.getTotalBytes() > threshold;
// persist when enough network data has occurred if (persistForce || (persistUid && uidPastThreshold)) {
final NetworkStats persistUidDelta = computeStatsDelta( writeUidStatsLocked();
mLastPersistUidSnapshot, uidSnapshot, true); mLastPersistUidSnapshot = uidSnapshot;
final boolean pastThreshold = persistUidDelta.getTotalBytes() > threshold;
if (forcePersist || (persistUid && pastThreshold)) {
writeUidStatsLocked();
mLastPersistUidSnapshot = uidSnapshot;
}
} }
} catch (IllegalStateException e) { } catch (IllegalStateException e) {
Log.wtf(TAG, "problem reading network stats", e); Log.wtf(TAG, "problem reading network stats", e);
@@ -781,9 +756,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
} }
// sample stats after each full poll // sample stats after each full poll
if (pollNetwork && pollUid) { performSample();
performSample();
}
// finally, dispatch updated event to any listeners // finally, dispatch updated event to any listeners
final Intent updatedIntent = new Intent(ACTION_NETWORK_STATS_UPDATED); final Intent updatedIntent = new Intent(ACTION_NETWORK_STATS_UPDATED);
@@ -813,12 +786,6 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
history.recordData(timeStart, currentTime, entry); history.recordData(timeStart, currentTime, entry);
} }
// trim any history beyond max
final long maxHistory = mSettings.getNetworkMaxHistory();
for (NetworkStatsHistory history : mNetworkStats.values()) {
history.removeBucketsBefore(currentTime - maxHistory);
}
mLastPollNetworkSnapshot = networkSnapshot; mLastPollNetworkSnapshot = networkSnapshot;
if (LOGD && unknownIface.size() > 0) { if (LOGD && unknownIface.size() > 0) {
@@ -862,20 +829,6 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
history.recordData(timeStart, currentTime, entry); history.recordData(timeStart, currentTime, entry);
} }
// trim any history beyond max
final long maxUidHistory = mSettings.getUidMaxHistory();
final long maxTagHistory = mSettings.getTagMaxHistory();
for (UidStatsKey key : mUidStats.keySet()) {
final NetworkStatsHistory history = mUidStats.get(key);
// detailed tags are trimmed sooner than summary in TAG_NONE
if (key.tag == TAG_NONE) {
history.removeBucketsBefore(currentTime - maxUidHistory);
} else {
history.removeBucketsBefore(currentTime - maxTagHistory);
}
}
mLastPollUidSnapshot = uidSnapshot; mLastPollUidSnapshot = uidSnapshot;
mLastPollOperationsSnapshot = mOperations; mLastPollOperationsSnapshot = mOperations;
mOperations = new NetworkStats(0L, 10); mOperations = new NetworkStats(0L, 10);
@@ -917,9 +870,13 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
* Sample recent statistics summary into {@link EventLog}. * Sample recent statistics summary into {@link EventLog}.
*/ */
private void performSample() { private void performSample() {
// take sample as total over last 4 hours final long largestBucketSize = Math.max(
final long end = mTime.hasCache() ? mTime.currentTimeMillis() : System.currentTimeMillis(); mSettings.getNetworkBucketDuration(), mSettings.getUidBucketDuration());
final long start = end - (4 * HOUR_IN_MILLIS);
// take sample as atomic buckets
final long now = mTime.hasCache() ? mTime.currentTimeMillis() : System.currentTimeMillis();
final long end = now - (now % largestBucketSize) + largestBucketSize;
final long start = end - largestBucketSize;
NetworkTemplate template = null; NetworkTemplate template = null;
NetworkStats.Entry ifaceTotal = null; NetworkStats.Entry ifaceTotal = null;
@@ -929,15 +886,17 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
template = buildTemplateMobileAll(getActiveSubscriberId(mContext)); template = buildTemplateMobileAll(getActiveSubscriberId(mContext));
ifaceTotal = getSummaryForNetwork(template, start, end).getTotal(ifaceTotal); ifaceTotal = getSummaryForNetwork(template, start, end).getTotal(ifaceTotal);
uidTotal = getSummaryForAllUid(template, start, end, false).getTotal(uidTotal); uidTotal = getSummaryForAllUid(template, start, end, false).getTotal(uidTotal);
EventLogTags.writeNetstatsMobileSample( EventLogTags.writeNetstatsMobileSample(ifaceTotal.rxBytes, ifaceTotal.rxPackets,
ifaceTotal.rxBytes, ifaceTotal.txBytes, uidTotal.rxBytes, uidTotal.txBytes); ifaceTotal.txBytes, ifaceTotal.txPackets, uidTotal.rxBytes, uidTotal.rxPackets,
uidTotal.txBytes, uidTotal.rxPackets);
// collect wifi sample // collect wifi sample
template = buildTemplateWifi(); template = buildTemplateWifi();
ifaceTotal = getSummaryForNetwork(template, start, end).getTotal(ifaceTotal); ifaceTotal = getSummaryForNetwork(template, start, end).getTotal(ifaceTotal);
uidTotal = getSummaryForAllUid(template, start, end, false).getTotal(uidTotal); uidTotal = getSummaryForAllUid(template, start, end, false).getTotal(uidTotal);
EventLogTags.writeNetstatsWifiSample( EventLogTags.writeNetstatsWifiSample(ifaceTotal.rxBytes, ifaceTotal.rxPackets,
ifaceTotal.rxBytes, ifaceTotal.txBytes, uidTotal.rxBytes, uidTotal.txBytes); ifaceTotal.txBytes, ifaceTotal.txPackets, uidTotal.rxBytes, uidTotal.rxPackets,
uidTotal.txBytes, uidTotal.rxPackets);
} }
/** /**
@@ -1137,6 +1096,15 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
// TODO: consider duplicating stats and releasing lock while writing // TODO: consider duplicating stats and releasing lock while writing
// trim any history beyond max
if (mTime.hasCache()) {
final long currentTime = mTime.currentTimeMillis();
final long maxHistory = mSettings.getNetworkMaxHistory();
for (NetworkStatsHistory history : mNetworkStats.values()) {
history.removeBucketsBefore(currentTime - maxHistory);
}
}
FileOutputStream fos = null; FileOutputStream fos = null;
try { try {
fos = mNetworkFile.startWrite(); fos = mNetworkFile.startWrite();
@@ -1172,6 +1140,23 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
// TODO: consider duplicating stats and releasing lock while writing // TODO: consider duplicating stats and releasing lock while writing
// trim any history beyond max
if (mTime.hasCache()) {
final long currentTime = mTime.currentTimeMillis();
final long maxUidHistory = mSettings.getUidMaxHistory();
final long maxTagHistory = mSettings.getTagMaxHistory();
for (UidStatsKey key : mUidStats.keySet()) {
final NetworkStatsHistory history = mUidStats.get(key);
// detailed tags are trimmed sooner than summary in TAG_NONE
if (key.tag == TAG_NONE) {
history.removeBucketsBefore(currentTime - maxUidHistory);
} else {
history.removeBucketsBefore(currentTime - maxTagHistory);
}
}
}
// build UidStatsKey lists grouped by ident // build UidStatsKey lists grouped by ident
final HashMap<NetworkIdentitySet, ArrayList<UidStatsKey>> keysByIdent = Maps.newHashMap(); final HashMap<NetworkIdentitySet, ArrayList<UidStatsKey>> keysByIdent = Maps.newHashMap();
for (UidStatsKey key : mUidStats.keySet()) { for (UidStatsKey key : mUidStats.keySet()) {
@@ -1236,7 +1221,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
} }
if (argSet.contains("poll")) { if (argSet.contains("poll")) {
performPollLocked(FLAG_POLL_ALL | FLAG_PERSIST_ALL | FLAG_FORCE_PERSIST); performPollLocked(FLAG_PERSIST_ALL | FLAG_PERSIST_FORCE);
pw.println("Forced poll"); pw.println("Forced poll");
return; return;
} }
@@ -1464,8 +1449,5 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
public long getTimeCacheMaxAge() { public long getTimeCacheMaxAge() {
return DAY_IN_MILLIS; return DAY_IN_MILLIS;
} }
public boolean getForceCompletePoll() {
return getSecureBoolean(NETSTATS_FORCE_COMPLETE_POLL, false);
}
} }
} }