Split out jank data from policy

Move ProfileData out to its own file with helper
accessors. This keeps policy (what is/isn't jank)
outside of the data storage.

Also use lambdas to iterate over the histogram
to make it nicer for dumping & proto-ifying.

Test: hwui_unit_tests pass & jank data still dumps

Change-Id: I88488369ec77590a2867f51128e65bb786aa34e6
diff --git a/libs/hwui/JankTracker.cpp b/libs/hwui/JankTracker.cpp
index 028d9f7..f9671ed 100644
--- a/libs/hwui/JankTracker.cpp
+++ b/libs/hwui/JankTracker.cpp
@@ -34,14 +34,6 @@
 namespace android {
 namespace uirenderer {
 
-static const char* JANK_TYPE_NAMES[] = {
-        "Missed Vsync",
-        "High input latency",
-        "Slow UI thread",
-        "Slow bitmap uploads",
-        "Slow issue draw commands",
-};
-
 struct Comparison {
     FrameInfoIndex start;
     FrameInfoIndex end;
@@ -68,65 +60,11 @@
  */
 static const int64_t EXEMPT_FRAMES_FLAGS = FrameInfoFlags::SurfaceCanvas;
 
-// The bucketing algorithm controls so to speak
-// If a frame is <= to this it goes in bucket 0
-static const uint32_t kBucketMinThreshold = 5;
-// If a frame is > this, start counting in increments of 2ms
-static const uint32_t kBucket2msIntervals = 32;
-// If a frame is > this, start counting in increments of 4ms
-static const uint32_t kBucket4msIntervals = 48;
-
 // For testing purposes to try and eliminate test infra overhead we will
 // consider any unknown delay of frame start as part of the test infrastructure
 // and filter it out of the frame profile data
 static FrameInfoIndex sFrameStart = FrameInfoIndex::IntendedVsync;
 
-// The interval of the slow frame histogram
-static const uint32_t kSlowFrameBucketIntervalMs = 50;
-// The start point of the slow frame bucket in ms
-static const uint32_t kSlowFrameBucketStartMs = 150;
-
-// This will be called every frame, performance sensitive
-// Uses bit twiddling to avoid branching while achieving the packing desired
-static uint32_t frameCountIndexForFrameTime(nsecs_t frameTime) {
-    uint32_t index = static_cast<uint32_t>(ns2ms(frameTime));
-    // If index > kBucketMinThreshold mask will be 0xFFFFFFFF as a result
-    // of negating 1 (twos compliment, yaay) else mask will be 0
-    uint32_t mask = -(index > kBucketMinThreshold);
-    // If index > threshold, this will essentially perform:
-    // amountAboveThreshold = index - threshold;
-    // index = threshold + (amountAboveThreshold / 2)
-    // However if index is <= this will do nothing. It will underflow, do
-    // a right shift by 0 (no-op), then overflow back to the original value
-    index = ((index - kBucket4msIntervals) >> (index > kBucket4msIntervals))
-            + kBucket4msIntervals;
-    index = ((index - kBucket2msIntervals) >> (index > kBucket2msIntervals))
-            + kBucket2msIntervals;
-    // If index was < minThreshold at the start of all this it's going to
-    // be a pretty garbage value right now. However, mask is 0 so we'll end
-    // up with the desired result of 0.
-    index = (index - kBucketMinThreshold) & mask;
-    return index;
-}
-
-// Only called when dumping stats, less performance sensitive
-int32_t JankTracker::frameTimeForFrameCountIndex(uint32_t index) {
-    index = index + kBucketMinThreshold;
-    if (index > kBucket2msIntervals) {
-        index += (index - kBucket2msIntervals);
-    }
-    if (index > kBucket4msIntervals) {
-        // This works because it was already doubled by the above if
-        // 1 is added to shift slightly more towards the middle of the bucket
-        index += (index - kBucket4msIntervals) + 1;
-    }
-    return index;
-}
-
-int32_t JankTracker::frameTimeForSlowFrameCountIndex(uint32_t index) {
-    return (index * kSlowFrameBucketIntervalMs) + kSlowFrameBucketStartMs;
-}
-
 JankTracker::JankTracker(const DisplayInfo& displayInfo) {
     // By default this will use malloc memory. It may be moved later to ashmem
     // if there is shared space for it and a request comes in to do that.
@@ -199,29 +137,7 @@
         return;
     }
 
-    // The new buffer may have historical data that we want to build on top of
-    // But let's make sure we don't overflow Just In Case
-    uint32_t divider = 0;
-    if (newData->totalFrameCount > (1 << 24)) {
-        divider = 4;
-    }
-    for (size_t i = 0; i < mData->jankTypeCounts.size(); i++) {
-        newData->jankTypeCounts[i] >>= divider;
-        newData->jankTypeCounts[i] += mData->jankTypeCounts[i];
-    }
-    for (size_t i = 0; i < mData->frameCounts.size(); i++) {
-        newData->frameCounts[i] >>= divider;
-        newData->frameCounts[i] += mData->frameCounts[i];
-    }
-    newData->jankFrameCount >>= divider;
-    newData->jankFrameCount += mData->jankFrameCount;
-    newData->totalFrameCount >>= divider;
-    newData->totalFrameCount += mData->totalFrameCount;
-    if (newData->statStartTime > mData->statStartTime
-            || newData->statStartTime == 0) {
-        newData->statStartTime = mData->statStartTime;
-    }
-
+    newData->mergeWith(*mData);
     freeData();
     mData = newData;
     mIsMapped = true;
@@ -251,7 +167,6 @@
 }
 
 void JankTracker::addFrame(const FrameInfo& frame) {
-    mData->totalFrameCount++;
     // Fast-path for jank-free frames
     int64_t totalDuration = frame.duration(sFrameStart, FrameInfoIndex::FrameCompleted);
     if (mDequeueTimeForgiveness
@@ -271,11 +186,10 @@
         }
     }
     LOG_ALWAYS_FATAL_IF(totalDuration <= 0, "Impossible totalDuration %" PRId64, totalDuration);
-    uint32_t framebucket = frameCountIndexForFrameTime(totalDuration);
-    LOG_ALWAYS_FATAL_IF(framebucket < 0, "framebucket < 0 (%u)", framebucket);
+    mData->reportFrame(totalDuration);
+
     // Keep the fast path as fast as possible.
     if (CC_LIKELY(totalDuration < mFrameInterval)) {
-        mData->frameCounts[framebucket]++;
         return;
     }
 
@@ -284,22 +198,12 @@
         return;
     }
 
-    if (framebucket <= mData->frameCounts.size()) {
-        mData->frameCounts[framebucket]++;
-    } else {
-        framebucket = (ns2ms(totalDuration) - kSlowFrameBucketStartMs)
-                / kSlowFrameBucketIntervalMs;
-        framebucket = std::min(framebucket,
-                static_cast<uint32_t>(mData->slowFrameCounts.size() - 1));
-        mData->slowFrameCounts[framebucket]++;
-    }
-
-    mData->jankFrameCount++;
+    mData->reportJank();
 
     for (int i = 0; i < NUM_BUCKETS; i++) {
         int64_t delta = frame.duration(COMPARISONS[i].start, COMPARISONS[i].end);
         if (delta >= mThresholds[i] && delta < IGNORE_EXCEEDING) {
-            mData->jankTypeCounts[i]++;
+            mData->reportJankType((JankType) i);
         }
     }
 }
@@ -320,58 +224,16 @@
     if (sFrameStart != FrameInfoIndex::IntendedVsync) {
         dprintf(fd, "\nNote: Data has been filtered!");
     }
-    dprintf(fd, "\nStats since: %" PRIu64 "ns", data->statStartTime);
-    dprintf(fd, "\nTotal frames rendered: %u", data->totalFrameCount);
-    dprintf(fd, "\nJanky frames: %u (%.2f%%)", data->jankFrameCount,
-            (float) data->jankFrameCount / (float) data->totalFrameCount * 100.0f);
-    dprintf(fd, "\n50th percentile: %ums", findPercentile(data, 50));
-    dprintf(fd, "\n90th percentile: %ums", findPercentile(data, 90));
-    dprintf(fd, "\n95th percentile: %ums", findPercentile(data, 95));
-    dprintf(fd, "\n99th percentile: %ums", findPercentile(data, 99));
-    for (int i = 0; i < NUM_BUCKETS; i++) {
-        dprintf(fd, "\nNumber %s: %u", JANK_TYPE_NAMES[i], data->jankTypeCounts[i]);
-    }
-    dprintf(fd, "\nHISTOGRAM:");
-    for (size_t i = 0; i < data->frameCounts.size(); i++) {
-        dprintf(fd, " %ums=%u", frameTimeForFrameCountIndex(i),
-                data->frameCounts[i]);
-    }
-    for (size_t i = 0; i < data->slowFrameCounts.size(); i++) {
-        dprintf(fd, " %ums=%u", frameTimeForSlowFrameCountIndex(i),
-                data->slowFrameCounts[i]);
-    }
+    data->dump(fd);
     dprintf(fd, "\n");
 }
 
 void JankTracker::reset() {
-    mData->jankTypeCounts.fill(0);
-    mData->frameCounts.fill(0);
-    mData->slowFrameCounts.fill(0);
-    mData->totalFrameCount = 0;
-    mData->jankFrameCount = 0;
-    mData->statStartTime = systemTime(CLOCK_MONOTONIC);
+    mData->reset();
     sFrameStart = Properties::filterOutTestOverhead
             ? FrameInfoIndex::HandleInputStart
             : FrameInfoIndex::IntendedVsync;
 }
 
-uint32_t JankTracker::findPercentile(const ProfileData* data, int percentile) {
-    int pos = percentile * data->totalFrameCount / 100;
-    int remaining = data->totalFrameCount - pos;
-    for (int i = data->slowFrameCounts.size() - 1; i >= 0; i--) {
-        remaining -= data->slowFrameCounts[i];
-        if (remaining <= 0) {
-            return (i * kSlowFrameBucketIntervalMs) + kSlowFrameBucketStartMs;
-        }
-    }
-    for (int i = data->frameCounts.size() - 1; i >= 0; i--) {
-        remaining -= data->frameCounts[i];
-        if (remaining <= 0) {
-            return frameTimeForFrameCountIndex(i);
-        }
-    }
-    return 0;
-}
-
 } /* namespace uirenderer */
 } /* namespace android */