Initial attempt at jank-tracking stat collection

Is a bit naive, perhaps overly aggressive, but sorta works

Change-Id: I01a774e00dbe681439c02557d9728ae43c45ce50
diff --git a/libs/hwui/JankTracker.cpp b/libs/hwui/JankTracker.cpp
new file mode 100644
index 0000000..62cb97c
--- /dev/null
+++ b/libs/hwui/JankTracker.cpp
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+#include "JankTracker.h"
+
+#include <cstdio>
+#include <inttypes.h>
+
+namespace android {
+namespace uirenderer {
+
+static const char* JANK_TYPE_NAMES[] = {
+        "Missed Vsync",
+        "High input latency",
+        "Slow UI thread",
+        "Slow bitmap uploads",
+        "Slow draw",
+};
+
+struct Comparison {
+    FrameInfoIndexEnum start;
+    FrameInfoIndexEnum end;
+};
+
+static const Comparison COMPARISONS[] = {
+        {FrameInfoIndex::kIntendedVsync, FrameInfoIndex::kVsync},
+        {FrameInfoIndex::kOldestInputEvent, FrameInfoIndex::kVsync},
+        {FrameInfoIndex::kVsync, FrameInfoIndex::kSyncStart},
+        {FrameInfoIndex::kSyncStart, FrameInfoIndex::kIssueDrawCommandsStart},
+        {FrameInfoIndex::kIssueDrawCommandsStart, FrameInfoIndex::kFrameCompleted},
+};
+
+// If the event exceeds 10 seconds throw it away, this isn't a jank event
+// it's an ANR and will be handled as such
+static const int64_t IGNORE_EXCEEDING = seconds_to_nanoseconds(10);
+
+/*
+ * Frames that are exempt from jank metrics.
+ * First-draw frames, for example, are expected to
+ * be slow, this is hidden from the user with window animations and
+ * other tricks
+ *
+ * Similarly, we don't track direct-drawing via Surface:lockHardwareCanvas()
+ * for now
+ *
+ * TODO: kSurfaceCanvas can negatively impact other drawing by using up
+ * time on the RenderThread, figure out how to attribute that as a jank-causer
+ */
+static const int64_t EXEMPT_FRAMES_FLAGS
+        = FrameInfoFlags::kWindowLayoutChanged
+        | FrameInfoFlags::kSurfaceCanvas;
+
+JankTracker::JankTracker(nsecs_t frameIntervalNanos) {
+    reset();
+    setFrameInterval(frameIntervalNanos);
+}
+
+void JankTracker::setFrameInterval(nsecs_t frameInterval) {
+    mFrameInterval = frameInterval;
+    mThresholds[kMissedVsync] = 1;
+    /*
+     * Due to interpolation and sample rate differences between the touch
+     * panel and the display (example, 85hz touch panel driving a 60hz display)
+     * we call high latency 1.5 * frameinterval
+     *
+     * NOTE: Be careful when tuning this! A theoretical 1,000hz touch panel
+     * on a 60hz display will show kOldestInputEvent - kIntendedVsync of being 15ms
+     * Thus this must always be larger than frameInterval, or it will fail
+     */
+    mThresholds[kHighInputLatency] = static_cast<int64_t>(1.5 * frameInterval);
+
+    // Note that these do not add up to 1. This is intentional. It's to deal
+    // with variance in values, and should be sort of an upper-bound on what
+    // is reasonable to expect.
+    mThresholds[kSlowUI] = static_cast<int64_t>(.5 * frameInterval);
+    mThresholds[kSlowSync] = static_cast<int64_t>(.2 * frameInterval);
+    mThresholds[kSlowRT] = static_cast<int64_t>(.75 * frameInterval);
+
+}
+
+void JankTracker::addFrame(const FrameInfo& frame) {
+    using namespace FrameInfoIndex;
+    mTotalFrameCount++;
+    // Fast-path for jank-free frames
+    int64_t totalDuration = frame[kFrameCompleted] - frame[kIntendedVsync];
+    if (CC_LIKELY(totalDuration < mFrameInterval)) {
+        return;
+    }
+
+    if (frame[kFlags] & EXEMPT_FRAMES_FLAGS) {
+        return;
+    }
+
+    mJankFrameCount++;
+
+    for (int i = 0; i < NUM_BUCKETS; i++) {
+        int64_t delta = frame[COMPARISONS[i].end] - frame[COMPARISONS[i].start];
+        if (delta >= mThresholds[i] && delta < IGNORE_EXCEEDING) {
+            mBuckets[i].count++;
+        }
+    }
+}
+
+void JankTracker::dump(int fd) {
+    FILE* file = fdopen(fd, "a");
+    fprintf(file, "\nFrame stats:");
+    fprintf(file, "\n  Total frames rendered: %u", mTotalFrameCount);
+    fprintf(file, "\n  Janky frames: %u (%.2f%%)", mJankFrameCount,
+            (float) mJankFrameCount / (float) mTotalFrameCount * 100.0f);
+    for (int i = 0; i < NUM_BUCKETS; i++) {
+        fprintf(file, "\n   Number %s: %u", JANK_TYPE_NAMES[i], mBuckets[i].count);
+    }
+    fprintf(file, "\n");
+    fflush(file);
+}
+
+void JankTracker::reset() {
+    memset(mBuckets, 0, sizeof(JankBucket) * NUM_BUCKETS);
+    mTotalFrameCount = 0;
+    mJankFrameCount = 0;
+}
+
+} /* namespace uirenderer */
+} /* namespace android */