From f0f3b29142a4244a2edd9acd33328079c168e54b Mon Sep 17 00:00:00 2001 From: Viktar Donich Date: Wed, 25 Apr 2018 11:23:24 -0700 Subject: [PATCH] Report low FPS to analytics Change-Id: I43e90294b16b48f717cfa7eaca18fec7c0bb488d --- .../core/gr-reporting/gr-jank-detector.js | 61 +++++++++++++++ .../gr-reporting/gr-jank-detector_test.html | 78 +++++++++++++++++++ .../core/gr-reporting/gr-reporting.html | 1 + .../core/gr-reporting/gr-reporting.js | 15 ++++ .../core/gr-reporting/gr-reporting_test.html | 4 + polygerrit-ui/app/test/index.html | 1 + 6 files changed, 160 insertions(+) create mode 100644 polygerrit-ui/app/elements/core/gr-reporting/gr-jank-detector.js create mode 100644 polygerrit-ui/app/elements/core/gr-reporting/gr-jank-detector_test.html diff --git a/polygerrit-ui/app/elements/core/gr-reporting/gr-jank-detector.js b/polygerrit-ui/app/elements/core/gr-reporting/gr-jank-detector.js new file mode 100644 index 0000000000..28c46f4e9e --- /dev/null +++ b/polygerrit-ui/app/elements/core/gr-reporting/gr-jank-detector.js @@ -0,0 +1,61 @@ +/** + * @license + * 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. + */ +(function() { + 'use strict'; + + const JANK_SLEEP_TIME_MS = 1000; + + const GrJankDetector = { + // Slowdowns counter. + jank: 0, + fps: 0, + _lastFrameTime: 0, + + start() { + this._requestAnimationFrame(this._detect.bind(this)); + }, + + _requestAnimationFrame(callback) { + window.requestAnimationFrame(callback); + }, + + _detect(now) { + if (this._lastFrameTime === 0) { + this._lastFrameTime = now; + this.fps = 0; + this._requestAnimationFrame(this._detect.bind(this)); + return; + } + const fpsNow = 1000/(now - this._lastFrameTime); + this._lastFrameTime = now; + // Calculate moving average within last 3 measurements. + this.fps = this.fps === 0 ? fpsNow : ((this.fps * 2 + fpsNow) / 3); + if (this.fps > 10) { + this._requestAnimationFrame(this._detect.bind(this)); + } else { + this.jank++; + console.warn('JANK', this.jank); + this._lastFrameTime = 0; + window.setTimeout( + () => this._requestAnimationFrame(this._detect.bind(this)), + JANK_SLEEP_TIME_MS); + } + }, + }; + + window.GrJankDetector = GrJankDetector; +})(); diff --git a/polygerrit-ui/app/elements/core/gr-reporting/gr-jank-detector_test.html b/polygerrit-ui/app/elements/core/gr-reporting/gr-jank-detector_test.html new file mode 100644 index 0000000000..6faeec1d81 --- /dev/null +++ b/polygerrit-ui/app/elements/core/gr-reporting/gr-jank-detector_test.html @@ -0,0 +1,78 @@ + + + + +gr-jank-detector + + + + + + + + diff --git a/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting.html b/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting.html index 2970a267e2..cbb2c0977b 100644 --- a/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting.html +++ b/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting.html @@ -19,5 +19,6 @@ limitations under the License. + diff --git a/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting.js b/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting.js index 0db442f015..ae67dac024 100644 --- a/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting.js +++ b/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting.js @@ -48,6 +48,14 @@ STARTED_HIDDEN: 'hidden', }; + // Frame rate related constants. + const JANK = { + TYPE: 'lifecycle', + CATEGORY: 'UI Latency', + // Reported events - alphabetize below. + COUNT: 'Jank count', + }; + // Navigation reporting constants. const NAVIGATION = { TYPE: 'nav-report', @@ -118,6 +126,8 @@ }; catchErrors(); + GrJankDetector.start(); + const GrReporting = Polymer({ is: 'gr-reporting', @@ -206,6 +216,11 @@ }, beforeLocationChanged() { + if (GrJankDetector.jank > 0) { + this.reporter( + JANK.TYPE, JANK.CATEGORY, JANK.COUNT, GrJankDetector.jank); + GrJankDetector.jank = 0; + } for (const prop of Object.keys(this._baselines)) { delete this._baselines[prop]; } diff --git a/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting_test.html b/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting_test.html index bfb45f6943..e2bb83d900 100644 --- a/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting_test.html +++ b/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting_test.html @@ -93,7 +93,11 @@ limitations under the License. test('beforeLocationChanged', () => { element._baselines['garbage'] = 'monster'; sandbox.stub(element, 'time'); + GrJankDetector.jank = 42; element.beforeLocationChanged(); + assert.equal(GrJankDetector.jank, 0); + assert.isTrue(element.reporter.calledWithExactly( + 'lifecycle', 'UI Latency', 'Jank count', 42)); assert.isTrue(element.time.calledWithExactly('DashboardDisplayed')); assert.isTrue(element.time.calledWithExactly('ChangeDisplayed')); assert.isTrue(element.time.calledWithExactly('DiffViewDisplayed')); diff --git a/polygerrit-ui/app/test/index.html b/polygerrit-ui/app/test/index.html index 6cf674a2ef..6a562fcb86 100644 --- a/polygerrit-ui/app/test/index.html +++ b/polygerrit-ui/app/test/index.html @@ -88,6 +88,7 @@ limitations under the License. 'core/gr-error-manager/gr-error-manager_test.html', 'core/gr-main-header/gr-main-header_test.html', 'core/gr-navigation/gr-navigation_test.html', + 'core/gr-reporting/gr-jank-detector_test.html', 'core/gr-reporting/gr-reporting_test.html', 'core/gr-router/gr-router_test.html', 'core/gr-search-bar/gr-search-bar_test.html',