| <!DOCTYPE html> |
| <!-- |
| Copyright (c) 2014 The Chromium Authors. All rights reserved. |
| Use of this source code is governed by a BSD-style license that can be |
| found in the LICENSE file. |
| --> |
| <link rel="import" href="/tracing/base/math/range.html"> |
| <link rel="import" href="/tracing/extras/chrome/chrome_user_friendly_category_driver.html"> |
| <link rel="import" href="/tracing/model/helpers/chrome_process_helper.html"> |
| |
| <script> |
| 'use strict'; |
| |
| tr.exportTo('tr.model.helpers', function() { |
| function ChromeRendererHelper(modelHelper, process) { |
| tr.model.helpers.ChromeProcessHelper.call(this, modelHelper, process); |
| this.mainThread_ = process.findAtMostOneThreadNamed('CrRendererMain') || |
| process.findAtMostOneThreadNamed('Chrome_InProcRendererThread'); |
| this.compositorThread_ = process.findAtMostOneThreadNamed('Compositor'); |
| this.rasterWorkerThreads_ = process.findAllThreadsMatching(function(t) { |
| if (t.name === undefined) return false; |
| if (t.name.indexOf('CompositorTileWorker') === 0) return true; |
| if (t.name.indexOf('CompositorRasterWorker') === 0) return true; |
| return false; |
| }); |
| |
| if (!process.name) |
| process.name = ChromeRendererHelper.PROCESS_NAME; |
| } |
| |
| |
| /** |
| * @callback getEventAttributeCallback |
| * @param {tr.b.Event} event The event to read an attribute from. |
| * @return {number} The value of the attribute. |
| */ |
| |
| /** |
| * Generate a breakdown tree from all slices of |mainThread| between |
| * |rangeStart| and |rangeEnd|. The callback functions |getEventStart|, |
| * |getEventDuration| and |getEventSelfTime| specify how to get start, |
| * duration and selftime from a given event. |
| * |
| * @param {tr.model.Thread} mainThread |
| * @param {number} rangeStart |
| * @param {number} rangeEnd |
| * @callback {getEventAttributeCallback} getEventStart |
| * @callback {getEventAttributeCallback} getEventDuration |
| * @callback {getEventAttributeCallback} getEventSelfTime |
| * @return {Map.<string, Object>} A time breakdown object whose keys are |
| * Chrome userfriendly title & values are an object that show the total spent |
| * between |rangeStart| & |rangeEnd|, and the list of event labels of the |
| * group and their total time between |rangeStart| and |rangeEnd|. |
| * |
| * Example: |
| * { |
| * layout: { |
| * total: 100, |
| * events: {'FrameView::performPreLayoutTasks': 20,..}}, |
| * v8_runtime: { |
| * total: 500, |
| * events: {'String::NewExternalTwoByte': 0.5,..}}, |
| * ... |
| * } |
| */ |
| function generateTimeBreakdownTree_(mainThread, rangeStart, rangeEnd, |
| getEventStart, getEventDuration, getEventSelfTime) { |
| if (mainThread === null) return; |
| var breakdownTree = {}; |
| var range = tr.b.math.Range.fromExplicitRange(rangeStart, rangeEnd); |
| for (var title of |
| tr.e.chrome.ChromeUserFriendlyCategoryDriver.ALL_TITLES) { |
| breakdownTree[title] = {total: 0, events: {}}; |
| } |
| for (var event of mainThread.getDescendantEvents()) { |
| var eventStart = getEventStart(event); |
| var eventDuration = getEventDuration(event); |
| var eventSelfTime = getEventSelfTime(event); |
| var eventEnd = eventStart + eventDuration; |
| if (!range.intersectsExplicitRangeExclusive(eventStart, eventEnd)) |
| continue; |
| if (eventSelfTime === undefined) continue; |
| var title = |
| tr.e.chrome.ChromeUserFriendlyCategoryDriver.fromEvent(event); |
| var timeIntersectionRatio = 0; |
| if (eventDuration > 0) { |
| timeIntersectionRatio = |
| range.findExplicitIntersectionDuration(eventStart, |
| eventEnd) / eventDuration; |
| } |
| |
| var v8Runtime = event.args['runtime-call-stat']; |
| if (v8Runtime !== undefined) { |
| var v8RuntimeObject = JSON.parse(v8Runtime); |
| for (var runtimeCall in v8RuntimeObject) { |
| // When the V8 Runtime Object contains 2 values, the 2nd value |
| // always represents the V8 Runtime duration. |
| if (v8RuntimeObject[runtimeCall].length === 2) { |
| if (breakdownTree['v8_runtime'].events[runtimeCall] === |
| undefined) { |
| breakdownTree['v8_runtime'].events[runtimeCall] = 0; |
| } |
| var runtimeTime = tr.b.Unit.timestampFromUs( |
| v8RuntimeObject[runtimeCall][1] * timeIntersectionRatio); |
| breakdownTree['v8_runtime'].total += runtimeTime; |
| breakdownTree['v8_runtime'].events[runtimeCall] += runtimeTime; |
| } |
| } |
| } |
| // [ Slice 1 ] [ Slice 2 ] [ Slice 3 ] |
| // [ Slice 4 ] [ Slice 5 ] |
| // [ Slice 6 ] | |
| // | | |
| // | | |
| // v v |
| // start end |
| // |
| // For the case where the |start| or |end| overlapped with some existing |
| // slice (see above diagram), we approximate the overlapped self-time |
| // by multiplying the ratio of overlapped wall time to the self-time. |
| // There should be way to compute the exact number, but in practice, |
| // this should rarely happen, and when it does, the overlapped range |
| // is relative small so that using approximation here should be good |
| // enough. |
| var approximatedSelfTimeContribution = |
| eventSelfTime * timeIntersectionRatio; |
| breakdownTree[title].total += approximatedSelfTimeContribution; |
| if (breakdownTree[title].events[event.title] === undefined) { |
| breakdownTree[title].events[event.title] = 0; |
| } |
| breakdownTree[title].events[event.title] += |
| approximatedSelfTimeContribution; |
| } |
| return breakdownTree; |
| } |
| |
| ChromeRendererHelper.PROCESS_NAME = 'Renderer'; |
| |
| // Returns true if there is either a main thread or a compositor thread. |
| ChromeRendererHelper.isRenderProcess = function(process) { |
| if (process.findAtMostOneThreadNamed('CrRendererMain')) return true; |
| if (process.findAtMostOneThreadNamed('Compositor')) return true; |
| return false; |
| }; |
| |
| ChromeRendererHelper.isTracingProcess = function(process) { |
| return process.labels !== undefined && |
| process.labels.length === 1 && |
| process.labels[0] === 'chrome://tracing'; |
| }; |
| |
| ChromeRendererHelper.prototype = { |
| __proto__: tr.model.helpers.ChromeProcessHelper.prototype, |
| |
| // May be undefined. |
| get mainThread() { |
| return this.mainThread_; |
| }, |
| |
| // May be undefined. |
| get compositorThread() { |
| return this.compositorThread_; |
| }, |
| |
| // May be empty. |
| get rasterWorkerThreads() { |
| return this.rasterWorkerThreads_; |
| }, |
| |
| get isChromeTracingUI() { |
| return ChromeRendererHelper.isTracingProcess(this.process); |
| }, |
| |
| /** |
| * Generate a breakdown that attributes where wall clock time goes between |
| * |start| & |end| on the renderer thread. |
| * |
| * @param {number} start |
| * @param {number} end |
| * @return {Map.<string, Object>} A time breakdown map whose keys are |
| * Chrome userfriendly titles & values are an object that shows the total |
| * wall clock time spent between |start| & |end|, and the list of event |
| * labels of the group and their total wall clock time between |start| & |
| * |end|. |
| * |
| * Example: |
| * { |
| * layout: { |
| * total: 100, |
| * events: {'FrameView::performPreLayoutTasks': 20,..}}, |
| * v8_runtime: { |
| * total: 500, |
| * events: {'String::NewExternalTwoByte': 0.5,..}}, |
| * ... |
| * } |
| */ |
| generateWallClockTimeBreakdownTree: function(start, end) { |
| function getEventStart(e) { return e.start; } |
| function getEventDuration(e) { return e.duration; } |
| function getEventSelfTime(e) { return e.selfTime; } |
| var breakdownTree = generateTimeBreakdownTree_(this.mainThread, |
| start, end, getEventStart, getEventDuration, getEventSelfTime); |
| var idleTotal = end - start; |
| for (let cat in breakdownTree) { |
| idleTotal -= breakdownTree[cat].total; |
| } |
| breakdownTree['idle'] = {total: idleTotal, events: {}}; |
| return breakdownTree; |
| }, |
| |
| /** |
| * Generate a breakdown that attributes where CPU time goes between |
| * |cpuStart| & |cpuEnd| on the renderer thread. |
| * |
| * @param {number} cpuStart |
| * @param {number} cpuEnd |
| * @return {Map.<string, Object>} A time breakdown map whose keys are |
| * Chrome userfriendly titles & values are an object that shows the total |
| * CPU time spent between |cpuStart| & |cpuEnd|, and the list of event labels |
| * of the group and their total CPU time between |cpuStart| & |cpuEnd|. |
| * |
| * Example: |
| * { |
| * layout: { |
| * total: 100, |
| * events: {'FrameView::performPreLayoutTasks': 20,..}}, |
| * v8_runtime: { |
| * total: 500, |
| * events: {'String::NewExternalTwoByte': 0.5,..}}, |
| * ... |
| * } |
| */ |
| generateCpuTimeBreakdownTree: function(cpuStart, cpuEnd) { |
| function getEventStart(e) { return e.cpuStart; } |
| function getEventDuration(e) { return e.cpuDuration; } |
| function getEventSelfTime(e) { return e.cpuSelfTime; } |
| return generateTimeBreakdownTree_(this.mainThread, cpuStart, cpuEnd, |
| getEventStart, getEventDuration, getEventSelfTime); |
| } |
| }; |
| |
| return { |
| ChromeRendererHelper, |
| }; |
| }); |
| </script> |