From 5a969aa1391203e09dee3146fc31b395de36344e Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Wed, 4 May 2016 17:18:19 -0700 Subject: [PATCH] Fix issue #28602068: Add count to job scheduler stats Also increase the event buffer size to 100, and implement it as a real ring buffer. And put that implementation in a generic class for use in other places. Change-Id: I06936984e2c253fb5f0eb5d15faf0019ec73d4e2 --- .../internal/util/RingBufferIndices.java | 82 +++++++++++ .../android/server/job/JobPackageTracker.java | 132 ++++++++++-------- 2 files changed, 157 insertions(+), 57 deletions(-) create mode 100644 core/java/com/android/internal/util/RingBufferIndices.java diff --git a/core/java/com/android/internal/util/RingBufferIndices.java b/core/java/com/android/internal/util/RingBufferIndices.java new file mode 100644 index 0000000000000..fe751f4efa091 --- /dev/null +++ b/core/java/com/android/internal/util/RingBufferIndices.java @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2016 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 com.android.internal.util; + +/** + * Helper class for implementing a ring buffer. This supplies the indices, you supply + * the array(s). + */ +public class RingBufferIndices { + private final int mCapacity; + + // The first valid element and the next open slot. + private int mStart; + private int mSize; + + /** + * Create ring buffer of the given capacity. + */ + public RingBufferIndices(int capacity) { + mCapacity = capacity; + } + + /** + * Add a new item to the ring buffer. If the ring buffer is full, this + * replaces the oldest item. + * @return Returns the index at which the new item appears, for placing in your array. + */ + public int add() { + if (mSize < mCapacity) { + final int pos = mSize; + mSize++; + return pos; + } + int pos = mStart; + mStart++; + if (mStart == mCapacity) { + mStart = 0; + } + return pos; + } + + /** + * Clear the ring buffer. + */ + public void clear() { + mStart = 0; + mSize = 0; + } + + /** + * Return the current size of the ring buffer. + */ + public int size() { + return mSize; + } + + /** + * Convert a position in the ring buffer that is [0..size()] to an offset + * in the array(s) containing the ring buffer items. + */ + public int indexOf(int pos) { + int index = mStart + pos; + if (index >= mCapacity) { + index -= mCapacity; + } + return index; + } +} diff --git a/services/core/java/com/android/server/job/JobPackageTracker.java b/services/core/java/com/android/server/job/JobPackageTracker.java index 4ba9dae87ca49..eb5edd38d8a91 100644 --- a/services/core/java/com/android/server/job/JobPackageTracker.java +++ b/services/core/java/com/android/server/job/JobPackageTracker.java @@ -23,6 +23,7 @@ import android.text.format.DateFormat; import android.util.ArrayMap; import android.util.SparseArray; import android.util.TimeUtils; +import com.android.internal.util.RingBufferIndices; import com.android.server.job.controllers.JobStatus; import java.io.PrintWriter; @@ -33,26 +34,24 @@ public final class JobPackageTracker { // Number of historical data sets we keep. static final int NUM_HISTORY = 5; - private static final int EVENT_BUFFER_SIZE = 50; + private static final int EVENT_BUFFER_SIZE = 100; public static final int EVENT_NULL = 0; public static final int EVENT_START_JOB = 1; public static final int EVENT_STOP_JOB = 2; - private int[] mEventCmds = new int[EVENT_BUFFER_SIZE]; - private long[] mEventTimes = new long[EVENT_BUFFER_SIZE]; - private int[] mEventUids = new int[EVENT_BUFFER_SIZE]; - private String[] mEventTags = new String[EVENT_BUFFER_SIZE]; + private final RingBufferIndices mEventIndices = new RingBufferIndices(EVENT_BUFFER_SIZE); + private final int[] mEventCmds = new int[EVENT_BUFFER_SIZE]; + private final long[] mEventTimes = new long[EVENT_BUFFER_SIZE]; + private final int[] mEventUids = new int[EVENT_BUFFER_SIZE]; + private final String[] mEventTags = new String[EVENT_BUFFER_SIZE]; public void addEvent(int cmd, int uid, String tag) { - System.arraycopy(mEventCmds, 0, mEventCmds, 1, EVENT_BUFFER_SIZE - 1); - System.arraycopy(mEventTimes, 0, mEventTimes, 1, EVENT_BUFFER_SIZE - 1); - System.arraycopy(mEventUids, 0, mEventUids, 1, EVENT_BUFFER_SIZE - 1); - System.arraycopy(mEventTags, 0, mEventTags, 1, EVENT_BUFFER_SIZE - 1); - mEventCmds[0] = cmd; - mEventTimes[0] = SystemClock.elapsedRealtime(); - mEventUids[0] = uid; - mEventTags[0] = tag; + int index = mEventIndices.add(); + mEventCmds[index] = cmd; + mEventTimes[index] = SystemClock.elapsedRealtime(); + mEventUids[index] = uid; + mEventTags[index] = tag; } DataSet mCurDataSet = new DataSet(); @@ -61,20 +60,23 @@ public final class JobPackageTracker { final static class PackageEntry { long pastActiveTime; long activeStartTime; + int activeNesting; int activeCount; boolean hadActive; long pastActiveTopTime; long activeTopStartTime; + int activeTopNesting; int activeTopCount; boolean hadActiveTop; long pastPendingTime; long pendingStartTime; + int pendingNesting; int pendingCount; boolean hadPending; public long getActiveTime(long now) { long time = pastActiveTime; - if (activeCount > 0) { + if (activeNesting > 0) { time += now - activeStartTime; } return time; @@ -82,7 +84,7 @@ public final class JobPackageTracker { public long getActiveTopTime(long now) { long time = pastActiveTopTime; - if (activeTopCount > 0) { + if (activeTopNesting > 0) { time += now - activeTopStartTime; } return time; @@ -90,7 +92,7 @@ public final class JobPackageTracker { public long getPendingTime(long now) { long time = pastPendingTime; - if (pendingCount > 0) { + if (pendingNesting > 0) { time += now - pendingStartTime; } return time; @@ -147,50 +149,53 @@ public final class JobPackageTracker { void incPending(int uid, String pkg, long now) { PackageEntry pe = getOrCreateEntry(uid, pkg); - if (pe.pendingCount == 0) { + if (pe.pendingNesting == 0) { pe.pendingStartTime = now; + pe.pendingCount++; } - pe.pendingCount++; + pe.pendingNesting++; } void decPending(int uid, String pkg, long now) { PackageEntry pe = getOrCreateEntry(uid, pkg); - if (pe.pendingCount == 1) { + if (pe.pendingNesting == 1) { pe.pastPendingTime += now - pe.pendingStartTime; } - pe.pendingCount--; + pe.pendingNesting--; } void incActive(int uid, String pkg, long now) { PackageEntry pe = getOrCreateEntry(uid, pkg); - if (pe.activeCount == 0) { + if (pe.activeNesting == 0) { pe.activeStartTime = now; + pe.activeCount++; } - pe.activeCount++; + pe.activeNesting++; } void decActive(int uid, String pkg, long now) { PackageEntry pe = getOrCreateEntry(uid, pkg); - if (pe.activeCount == 1) { + if (pe.activeNesting == 1) { pe.pastActiveTime += now - pe.activeStartTime; } - pe.activeCount--; + pe.activeNesting--; } void incActiveTop(int uid, String pkg, long now) { PackageEntry pe = getOrCreateEntry(uid, pkg); - if (pe.activeTopCount == 0) { + if (pe.activeTopNesting == 0) { pe.activeTopStartTime = now; + pe.activeTopCount++; } - pe.activeTopCount++; + pe.activeTopNesting++; } void decActiveTop(int uid, String pkg, long now) { PackageEntry pe = getOrCreateEntry(uid, pkg); - if (pe.activeTopCount == 1) { + if (pe.activeTopNesting == 1) { pe.pastActiveTopTime += now - pe.activeTopStartTime; } - pe.activeTopCount--; + pe.activeTopNesting--; } void finish(DataSet next, long now) { @@ -198,27 +203,27 @@ public final class JobPackageTracker { ArrayMap uidMap = mEntries.valueAt(i); for (int j = uidMap.size() - 1; j >= 0; j--) { PackageEntry pe = uidMap.valueAt(j); - if (pe.activeCount > 0 || pe.activeTopCount > 0 || pe.pendingCount > 0) { + if (pe.activeNesting > 0 || pe.activeTopNesting > 0 || pe.pendingNesting > 0) { // Propagate existing activity in to next data set. PackageEntry nextPe = next.getOrCreateEntry(mEntries.keyAt(i), uidMap.keyAt(j)); nextPe.activeStartTime = now; - nextPe.activeCount = pe.activeCount; + nextPe.activeNesting = pe.activeNesting; nextPe.activeTopStartTime = now; - nextPe.activeTopCount = pe.activeTopCount; + nextPe.activeTopNesting = pe.activeTopNesting; nextPe.pendingStartTime = now; - nextPe.pendingCount = pe.pendingCount; + nextPe.pendingNesting = pe.pendingNesting; // Finish it off. - if (pe.activeCount > 0) { + if (pe.activeNesting > 0) { pe.pastActiveTime += now - pe.activeStartTime; - pe.activeCount = 0; + pe.activeNesting = 0; } - if (pe.activeTopCount > 0) { + if (pe.activeTopNesting > 0) { pe.pastActiveTopTime += now - pe.activeTopStartTime; - pe.activeTopCount = 0; + pe.activeTopNesting = 0; } - if (pe.pendingCount > 0) { + if (pe.pendingNesting > 0) { pe.pastPendingTime += now - pe.pendingStartTime; - pe.pendingCount = 0; + pe.pendingNesting = 0; } } } @@ -233,17 +238,20 @@ public final class JobPackageTracker { PackageEntry pe = uidMap.valueAt(j); PackageEntry outPe = out.getOrCreateEntry(mEntries.keyAt(i), uidMap.keyAt(j)); outPe.pastActiveTime += pe.pastActiveTime; + outPe.activeCount += pe.activeCount; outPe.pastActiveTopTime += pe.pastActiveTopTime; + outPe.activeTopCount += pe.activeTopCount; outPe.pastPendingTime += pe.pastPendingTime; - if (pe.activeCount > 0) { + outPe.pendingCount += pe.pendingCount; + if (pe.activeNesting > 0) { outPe.pastActiveTime += now - pe.activeStartTime; outPe.hadActive = true; } - if (pe.activeTopCount > 0) { + if (pe.activeTopNesting > 0) { outPe.pastActiveTopTime += now - pe.activeTopStartTime; outPe.hadActiveTop = true; } - if (pe.pendingCount > 0) { + if (pe.pendingNesting > 0) { outPe.pastPendingTime += now - pe.pendingStartTime; outPe.hadPending = true; } @@ -251,13 +259,20 @@ public final class JobPackageTracker { } } - void printDuration(PrintWriter pw, long period, long duration, String suffix) { + void printDuration(PrintWriter pw, long period, long duration, int count, String suffix) { float fraction = duration / (float) period; int percent = (int) ((fraction * 100) + .5f); if (percent > 0) { pw.print(" "); pw.print(percent); pw.print("% "); + pw.print(count); + pw.print("x "); + pw.print(suffix); + } else if (count > 0) { + pw.print(" "); + pw.print(count); + pw.print("x "); pw.print(suffix); } } @@ -286,16 +301,17 @@ public final class JobPackageTracker { UserHandle.formatUid(pw, uid); pw.print(" / "); pw.print(uidMap.keyAt(j)); pw.print(":"); - printDuration(pw, period, pe.getPendingTime(now), "pending"); - printDuration(pw, period, pe.getActiveTime(now), "active"); - printDuration(pw, period, pe.getActiveTopTime(now), "active-top"); - if (pe.pendingCount > 0 || pe.hadPending) { + printDuration(pw, period, pe.getPendingTime(now), pe.pendingCount, "pending"); + printDuration(pw, period, pe.getActiveTime(now), pe.activeCount, "active"); + printDuration(pw, period, pe.getActiveTopTime(now), pe.activeTopCount, + "active-top"); + if (pe.pendingNesting > 0 || pe.hadPending) { pw.print(" (pending)"); } - if (pe.activeCount > 0 || pe.hadActive) { + if (pe.activeNesting > 0 || pe.hadActive) { pw.print(" (active)"); } - if (pe.activeTopCount > 0 || pe.hadActiveTop) { + if (pe.activeTopNesting > 0 || pe.hadActiveTop) { pw.print(" (active-top)"); } pw.println(); @@ -389,34 +405,36 @@ public final class JobPackageTracker { } public boolean dumpHistory(PrintWriter pw, String prefix, int filterUid) { - if (mEventCmds[0] == EVENT_NULL) { + final int size = mEventIndices.size(); + if (size <= 0) { return false; } pw.println(" Job history:"); - long now = SystemClock.elapsedRealtime(); - for (int i=EVENT_BUFFER_SIZE-1; i>=0; i--) { - int uid = mEventUids[i]; + final long now = SystemClock.elapsedRealtime(); + for (int i=0; i