blob: cfa46891f150f781d47b6944a3ae10ce78887814 [file] [log] [blame]
# Copyright 2016 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.
import collections
import copy
import unittest
from activity_lens import (ActivityLens, _EventsTree)
import clovis_constants
import test_utils
import tracing_track
class ActivityLensTestCase(unittest.TestCase):
@classmethod
def _EventsFromRawEvents(cls, raw_events):
track = tracing_track.TracingTrack(None,
clovis_constants.DEFAULT_CATEGORIES)
track.Handle(
'Tracing.dataCollected', {'params': {'value': raw_events}})
return track.GetEvents()
def setUp(self):
self.track = tracing_track.TracingTrack(None,
clovis_constants.DEFAULT_CATEGORIES)
def testGetRendererMainThread(self):
first_renderer_tid = 12345
second_renderer_tid = 123456
raw_events = [
{u'args': {u'name': u'CrBrowserMain'},
u'cat': u'__metadata',
u'name': u'thread_name',
u'ph': u'M',
u'pid': 1,
u'tid': 123,
u'ts': 0},
{u'args': {u'name': u'CrRendererMain'},
u'cat': u'__metadata',
u'name': u'thread_name',
u'ph': u'M',
u'pid': 1,
u'tid': first_renderer_tid,
u'ts': 0},
{u'args': {u'name': u'CrRendererMain'},
u'cat': u'__metadata',
u'name': u'thread_name',
u'ph': u'M',
u'pid': 1,
u'tid': second_renderer_tid,
u'ts': 0}]
raw_events += [
{u'args': {u'data': {}},
u'cat': u'devtools.timeline,v8',
u'name': u'FunctionCall',
u'ph': u'X',
u'pid': 1,
u'tdur': 0,
u'tid': first_renderer_tid,
u'ts': 251427174674,
u'tts': 5107725}] * 100
raw_events += [
{u'args': {u'data': {}},
u'cat': u'devtools.timeline,v8',
u'name': u'FunctionCall',
u'ph': u'X',
u'pid': 1,
u'tdur': 0,
u'tid': second_renderer_tid,
u'ts': 251427174674,
u'tts': 5107725}] * 150
# There are more events from first_renderer_tid when (incorrectly) ignoring
# the PID.
raw_events += [
{u'args': {u'data': {}},
u'cat': u'devtools.timeline,v8',
u'name': u'FunctionCall',
u'ph': u'X',
u'pid': 12,
u'tdur': 0,
u'tid': first_renderer_tid,
u'ts': 251427174674,
u'tts': 5107725}] * 100
events = self._EventsFromRawEvents(raw_events)
self.assertEquals((1, second_renderer_tid),
ActivityLens._GetRendererMainThreadId(events))
def testThreadBusyness(self):
raw_events = [
{u'args': {},
u'cat': u'toplevel',
u'dur': 200 * 1000,
u'name': u'MessageLoop::RunTask',
u'ph': u'X',
u'pid': 123,
u'tid': 123,
u'ts': 0,
u'tts': 56485},
{u'args': {},
u'cat': u'toplevel',
u'dur': 8 * 200,
u'name': u'MessageLoop::NestedSomething',
u'ph': u'X',
u'pid': 123,
u'tid': 123,
u'ts': 0,
u'tts': 0}]
events = self._EventsFromRawEvents(raw_events)
self.assertEquals(200, ActivityLens._ThreadBusyness(events, 0, 1000))
# Clamping duration.
self.assertEquals(100, ActivityLens._ThreadBusyness(events, 0, 100))
self.assertEquals(50, ActivityLens._ThreadBusyness(events, 25, 75))
def testScriptExecuting(self):
url = u'http://example.com/script.js'
raw_events = [
{u'args': {u'data': {u'scriptName': url}},
u'cat': u'devtools.timeline,v8',
u'dur': 250 * 1000,
u'name': u'FunctionCall',
u'ph': u'X',
u'pid': 123,
u'tdur': 247,
u'tid': 123,
u'ts': 0,
u'tts': 0},
{u'args': {u'data': {}},
u'cat': u'devtools.timeline,v8',
u'dur': 350 * 1000,
u'name': u'EvaluateScript',
u'ph': u'X',
u'pid': 123,
u'tdur': 247,
u'tid': 123,
u'ts': 0,
u'tts': 0}]
events = self._EventsFromRawEvents(raw_events)
self.assertEquals(2, len(ActivityLens._ScriptsExecuting(events, 0, 1000)))
self.assertTrue(None in ActivityLens._ScriptsExecuting(events, 0, 1000))
self.assertEquals(
350, ActivityLens._ScriptsExecuting(events, 0, 1000)[None])
self.assertTrue(url in ActivityLens._ScriptsExecuting(events, 0, 1000))
self.assertEquals(250, ActivityLens._ScriptsExecuting(events, 0, 1000)[url])
# Aggreagates events.
raw_events.append({u'args': {u'data': {}},
u'cat': u'devtools.timeline,v8',
u'dur': 50 * 1000,
u'name': u'EvaluateScript',
u'ph': u'X',
u'pid': 123,
u'tdur': 247,
u'tid': 123,
u'ts': 0,
u'tts': 0})
events = self._EventsFromRawEvents(raw_events)
self.assertEquals(
350 + 50, ActivityLens._ScriptsExecuting(events, 0, 1000)[None])
def testParsing(self):
css_url = u'http://example.com/style.css'
html_url = u'http://example.com/yeah.htnl'
raw_events = [
{u'args': {u'data': {u'styleSheetUrl': css_url}},
u'cat': u'blink,devtools.timeline',
u'dur': 400 * 1000,
u'name': u'ParseAuthorStyleSheet',
u'ph': u'X',
u'pid': 32723,
u'tdur': 49721,
u'tid': 32738,
u'ts': 0,
u'tts': 216148},
{u'args': {u'beginData': {u'url': html_url}},
u'cat': u'devtools.timeline',
u'dur': 42 * 1000,
u'name': u'ParseHTML',
u'ph': u'X',
u'pid': 32723,
u'tdur': 49721,
u'tid': 32738,
u'ts': 0,
u'tts': 5032310},]
events = self._EventsFromRawEvents(raw_events)
self.assertEquals(2, len(ActivityLens._Parsing(events, 0, 1000)))
self.assertTrue(css_url in ActivityLens._Parsing(events, 0, 1000))
self.assertEquals(400, ActivityLens._Parsing(events, 0, 1000)[css_url])
self.assertTrue(html_url in ActivityLens._Parsing(events, 0, 1000))
self.assertEquals(42, ActivityLens._Parsing(events, 0, 1000)[html_url])
def testBreakdownEdgeActivityByInitiator(self):
requests = [test_utils.MakeRequest(0, 1, 10, 20, 30),
test_utils.MakeRequest(0, 1, 50, 60, 70)]
raw_events = [
{u'args': {u'beginData': {u'url': requests[0].url}},
u'cat': u'devtools.timeline',
u'dur': 12 * 1000,
u'name': u'ParseHTML',
u'ph': u'X',
u'pid': 1,
u'tid': 1,
u'ts': 25 * 1000},
{u'args': {u'data': {'scriptName': requests[0].url}},
u'cat': u'devtools.timeline,v8',
u'dur': 0,
u'name': u'EvaluateScript',
u'ph': u'X',
u'pid': 1,
u'tid': 1,
u'ts': 0},
{u'cat': u'toplevel',
u'dur': 100 * 1000,
u'name': u'MessageLoop::RunTask',
u'ph': u'X',
u'pid': 1,
u'tid': 1,
u'ts': 0},
{u'args': {u'name': u'CrRendererMain'},
u'cat': u'__metadata',
u'name': u'thread_name',
u'ph': u'M',
u'pid': 1,
u'tid': 1,
u'ts': 0}]
activity = self._ActivityLens(requests, raw_events)
dep = (requests[0], requests[1], 'parser')
self.assertEquals(
{'unrelated_work': 18, 'idle': 0, 'script': 0, 'parsing': 12,
'other_url': 0, 'unknown_url': 0},
activity.BreakdownEdgeActivityByInitiator(dep))
dep = (requests[0], requests[1], 'other')
# Truncating the event from the parent request end.
self.assertEquals(
{'unrelated_work': 13, 'idle': 0, 'script': 0, 'parsing': 7,
'other_url': 0, 'unknown_url': 0},
activity.BreakdownEdgeActivityByInitiator(dep))
# Unknown URL
raw_events[0]['args']['beginData']['url'] = None
activity = self._ActivityLens(requests, raw_events)
dep = (requests[0], requests[1], 'parser')
self.assertEquals(
{'unrelated_work': 18, 'idle': 0, 'script': 0, 'parsing': 0,
'other_url': 0, 'unknown_url': 12},
activity.BreakdownEdgeActivityByInitiator(dep))
# Script
raw_events[1]['ts'] = 40 * 1000
raw_events[1]['dur'] = 6 * 1000
activity = self._ActivityLens(requests, raw_events)
dep = (requests[0], requests[1], 'script')
self.assertEquals(
{'unrelated_work': 7, 'idle': 0, 'script': 6, 'parsing': 0,
'other_url': 0, 'unknown_url': 7},
activity.BreakdownEdgeActivityByInitiator(dep))
# Other URL
raw_events[1]['args']['data']['scriptName'] = 'http://other.com/url'
activity = self._ActivityLens(requests, raw_events)
self.assertEquals(
{'unrelated_work': 7, 'idle': 0, 'script': 0., 'parsing': 0.,
'other_url': 6., 'unknown_url': 7.},
activity.BreakdownEdgeActivityByInitiator(dep))
def testMainRendererThreadBusyness(self):
raw_events = [
{u'args': {u'name': u'CrRendererMain'},
u'cat': u'__metadata',
u'name': u'thread_name',
u'ph': u'M',
u'pid': 1,
u'tid': 12,
u'ts': 0},
{u'args': {},
u'cat': u'toplevel',
u'dur': 200 * 1000,
u'name': u'MessageLoop::RunTask',
u'ph': u'X',
u'pid': 1,
u'tid': 12,
u'ts': 0,
u'tts': 56485},
{u'args': {},
u'cat': u'toplevel',
u'dur': 8 * 200,
u'name': u'MessageLoop::NestedSomething',
u'ph': u'X',
u'pid': 1,
u'tid': 12,
u'ts': 0,
u'tts': 0},
{u'args': {},
u'cat': u'toplevel',
u'dur': 500 * 1000,
u'name': u'MessageLoop::RunTask',
u'ph': u'X',
u'pid': 12,
u'tid': 12,
u'ts': 0,
u'tts': 56485}]
lens = self._ActivityLens([], raw_events)
# Ignore events from another PID.
self.assertEquals(200, lens.MainRendererThreadBusyness(0, 1000))
# Clamping duration.
self.assertEquals(100, lens.MainRendererThreadBusyness(0, 100))
self.assertEquals(50, lens.MainRendererThreadBusyness(25, 75))
# Other PID.
raw_events[0]['pid'] = 12
lens = self._ActivityLens([], raw_events)
self.assertEquals(500, lens.MainRendererThreadBusyness(0, 1000))
def _ActivityLens(self, requests, raw_events):
loading_trace = test_utils.LoadingTraceFromEvents(
requests, None, raw_events)
return ActivityLens(loading_trace)
class EventsTreeTestCase(unittest.TestCase):
FakeEvent = collections.namedtuple(
'FakeEvent', ('name', 'start_msec', 'end_msec'))
_ROOT_EVENT = FakeEvent('-1', 0, 20)
_EVENTS = [
FakeEvent('0', 2, 4), FakeEvent('1', 1, 5),
FakeEvent('2', 6, 9),
FakeEvent('3', 13, 14), FakeEvent('4', 14, 17), FakeEvent('5', 12, 18)]
def setUp(self):
self.tree = _EventsTree(self._ROOT_EVENT, copy.deepcopy(self._EVENTS))
def testEventsTreeConstruction(self):
self.assertEquals(self._ROOT_EVENT, self.tree.event)
self.assertEquals(3, len(self.tree.children))
self.assertEquals(self._EVENTS[1], self.tree.children[0].event)
self.assertEquals(self._EVENTS[0], self.tree.children[0].children[0].event)
self.assertEquals(self._EVENTS[2], self.tree.children[1].event)
self.assertEquals([], self.tree.children[1].children)
self.assertEquals(self._EVENTS[5], self.tree.children[2].event)
self.assertEquals(2, len(self.tree.children[2].children))
def testDominatingEventsWithNames(self):
self.assertListEqual(
[self._ROOT_EVENT], self.tree.DominatingEventsWithNames(('-1')))
self.assertListEqual(
[self._ROOT_EVENT], self.tree.DominatingEventsWithNames(('-1', '0')))
self.assertListEqual(
[self._EVENTS[1], self._EVENTS[5]],
self.tree.DominatingEventsWithNames(('1', '5')))
if __name__ == '__main__':
unittest.main()