blob: b0ee2169bae69efbf576a5bbb46bc34924e34690 [file] [log] [blame]
<!DOCTYPE html>
<!--
Copyright (c) 2013 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="/core/test_utils.html">
<link rel="import" href="/extras/importer/trace_event_importer.html">
<script>
'use strict';
tv.b.unittest.testSuite(function() { // @suppress longLineCheck
var findSliceNamed = tv.c.test_utils.findSliceNamed;
test('canImportEmpty', function() {
self.assertFalse(tv.e.importer.TraceEventImporter.canImport([]));
self.assertFalse(tv.e.importer.TraceEventImporter.canImport(''));
});
test('basicSingleThreadNonnestedParsing', function() {
var events = [
{name: 'a', args: {}, pid: 52, ts: 520, cat: 'foo', tid: 53, ph: 'B'},
{name: 'a', args: {}, pid: 52, ts: 560, cat: 'foo', tid: 53, ph: 'E'},
{name: 'b', args: {}, pid: 52, ts: 629, cat: 'bar', tid: 53, ph: 'B'},
{name: 'b', args: {}, pid: 52, ts: 631, cat: 'bar', tid: 53, ph: 'E'}
];
var m = new tv.c.TraceModel(events);
assertEquals(1, m.numProcesses);
var p = m.processes[52];
assertNotUndefined(p);
assertEquals(1, p.numThreads);
var t = p.threads[53];
assertNotUndefined(t);
assertEquals(2, t.sliceGroup.length);
assertEquals(53, t.tid);
var slice = t.sliceGroup.slices[0];
assertEquals('a', slice.title);
assertEquals('foo', slice.category);
assertEquals(0, slice.start);
assertAlmostEquals((560 - 520) / 1000, slice.duration);
assertEquals(0, slice.subSlices.length);
slice = t.sliceGroup.slices[1];
assertEquals('b', slice.title);
assertEquals('bar', slice.category);
assertAlmostEquals((629 - 520) / 1000, slice.start);
assertAlmostEquals((631 - 629) / 1000, slice.duration);
assertEquals(0, slice.subSlices.length);
});
test('basicSingleThreadNonnestedParsingWithCpuDuration', function() {
var events = [
{name: 'a', args: {}, pid: 52, ts: 520, cat: 'foo', tid: 53, ph: 'B', tts: 221}, // @suppress longLineCheck
{name: 'a', args: {}, pid: 52, ts: 560, cat: 'foo', tid: 53, ph: 'E', tts: 259}, // @suppress longLineCheck
{name: 'b', args: {}, pid: 52, ts: 629, cat: 'bar', tid: 53, ph: 'B', tts: 329}, // @suppress longLineCheck
{name: 'b', args: {}, pid: 52, ts: 631, cat: 'bar', tid: 53, ph: 'E', tts: 331} // @suppress longLineCheck
];
var m = new tv.c.TraceModel(events);
assertEquals(1, m.numProcesses);
var p = m.processes[52];
assertNotUndefined(p);
assertEquals(1, p.numThreads);
var t = p.threads[53];
assertNotUndefined(t);
assertEquals(2, t.sliceGroup.length);
assertEquals(53, t.tid);
var slice = t.sliceGroup.slices[0];
assertEquals('a', slice.title);
assertEquals('foo', slice.category);
assertEquals(0, slice.start);
assertAlmostEquals((560 - 520) / 1000, slice.duration);
assertAlmostEquals((259 - 221) / 1000, slice.cpuDuration);
assertEquals(0, slice.subSlices.length);
slice = t.sliceGroup.slices[1];
assertEquals('b', slice.title);
assertEquals('bar', slice.category);
assertAlmostEquals((629 - 520) / 1000, slice.start);
assertAlmostEquals((631 - 629) / 1000, slice.duration);
assertAlmostEquals((331 - 329) / 1000, slice.cpuDuration);
assertEquals(0, slice.subSlices.length);
});
test('argumentDupeCreatesNonFailingImportError', function() {
var events = [
{name: 'a',
args: {'x': 1},
pid: 1,
ts: 520,
cat: 'foo',
tid: 1,
ph: 'B'},
{name: 'a',
args: {'x': 2},
pid: 1,
ts: 560,
cat: 'foo',
tid: 1,
ph: 'E'}
];
var m = new tv.c.TraceModel(events);
var t = m.processes[1].threads[1];
var sA = findSliceNamed(t.sliceGroup, 'a');
assertEquals(2, sA.args.x);
assertTrue(m.hasImportWarnings);
assertEquals(m.importWarnings.length, 1);
});
test('importMissingArgs', function() {
var events = [
{name: 'a', pid: 52, ts: 520, cat: 'foo', tid: 53, ph: 'B'},
{name: 'a', pid: 52, ts: 560, cat: 'foo', tid: 53, ph: 'E'},
{name: 'b', pid: 52, ts: 629, cat: 'bar', tid: 53, ph: 'I'}
];
// This should not throw an exception.
new tv.c.TraceModel(events);
});
test('importDoesNotChokeOnNulls', function() {
var events = [
{name: 'a', args: { foo: null }, pid: 52, ts: 520, cat: 'foo', tid: 53, ph: 'B'}, // @suppress longLineCheck
{name: 'a', pid: 52, ts: 560, cat: 'foo', tid: 53, ph: 'E'}
];
// This should not throw an exception.
new tv.c.TraceModel(events);
});
test('categoryBeginEndMismatchPrefersBegin', function() {
var events = [
{name: 'a', args: {}, pid: 52, ts: 520, cat: 'foo', tid: 53, ph: 'B'},
{name: 'a', args: {}, pid: 52, ts: 560, cat: 'bar', tid: 53, ph: 'E'}
];
var m = new tv.c.TraceModel(events);
assertEquals(1, m.numProcesses);
var p = m.processes[52];
assertNotUndefined(p);
assertEquals(1, p.numThreads);
var t = p.threads[53];
assertNotUndefined(t);
assertEquals(1, t.sliceGroup.length);
assertEquals(53, t.tid);
var slice = t.sliceGroup.slices[0];
assertEquals('a', slice.title);
assertEquals('foo', slice.category);
});
test('beginEndNameMismatch', function() {
var events = [
{name: 'a', args: {}, pid: 52, ts: 520, cat: 'foo', tid: 53, ph: 'B'},
{name: 'b', args: {}, pid: 52, ts: 560, cat: 'foo', tid: 53, ph: 'E'}
];
var m = new tv.c.TraceModel(events);
assertTrue(m.hasImportWarnings);
assertEquals(1, m.importWarnings.length);
});
test('nestedParsing', function() {
var events = [
{name: 'a', args: {}, pid: 1, ts: 1, tts: 1, cat: 'foo', tid: 1, ph: 'B'},
{name: 'b', args: {}, pid: 1, ts: 2, tts: 2, cat: 'bar', tid: 1, ph: 'B'},
{name: 'b', args: {}, pid: 1, ts: 3, tts: 3, cat: 'bar', tid: 1, ph: 'E'},
{name: 'a', args: {}, pid: 1, ts: 4, tts: 3, cat: 'foo', tid: 1, ph: 'E'}
];
var m = new tv.c.TraceModel(events, false);
var t = m.processes[1].threads[1];
var sA = findSliceNamed(t.sliceGroup, 'a');
var sB = findSliceNamed(t.sliceGroup, 'b');
assertEquals('a', sA.title);
assertEquals('foo', sA.category);
assertEquals(0.001, sA.start);
assertEquals(0.003, sA.duration);
assertEquals(0.002, sA.selfTime);
assertEquals(0.001, sA.cpuSelfTime);
assertEquals('b', sB.title);
assertEquals('bar', sB.category);
assertEquals(0.002, sB.start);
assertEquals(0.001, sB.duration);
assertTrue(sA.subSlices.length == 1);
assertTrue(sA.subSlices[0] == sB);
assertTrue(sB.parentSlice == sA);
});
test('nestedParsingWithTwoSubSlices', function() {
var events = [
{name: 'a', args: {}, pid: 1, ts: 1, tts: 1, cat: 'foo', tid: 1, ph: 'B'},
{name: 'b', args: {}, pid: 1, ts: 2, tts: 2, cat: 'bar', tid: 1, ph: 'B'},
{name: 'b', args: {}, pid: 1, ts: 3, tts: 3, cat: 'bar', tid: 1, ph: 'E'},
{name: 'c', args: {}, pid: 1, ts: 5, tts: 5, cat: 'baz', tid: 1, ph: 'B'},
{name: 'c', args: {}, pid: 1, ts: 7, tts: 6, cat: 'baz', tid: 1, ph: 'E'},
{name: 'a', args: {}, pid: 1, ts: 8, tts: 8, cat: 'foo', tid: 1, ph: 'E'}
];
var m = new tv.c.TraceModel(events, false);
var t = m.processes[1].threads[1];
var sA = findSliceNamed(t.sliceGroup, 'a');
var sB = findSliceNamed(t.sliceGroup, 'b');
var sC = findSliceNamed(t.sliceGroup, 'c');
assertEquals('a', sA.title);
assertEquals('foo', sA.category);
assertEquals(0.001, sA.start);
assertEquals(0.007, sA.duration);
assertEquals(0.004, sA.selfTime);
assertEquals(0.005, sA.cpuSelfTime);
assertEquals('b', sB.title);
assertEquals('bar', sB.category);
assertEquals(0.002, sB.start);
assertEquals(0.001, sB.duration);
assertEquals('c', sC.title);
assertEquals('baz', sC.category);
assertEquals(0.005, sC.start);
assertEquals(0.002, sC.duration);
assertTrue(sA.subSlices.length == 2);
assertTrue(sA.subSlices[0] == sB);
assertTrue(sA.subSlices[1] == sC);
assertTrue(sB.parentSlice == sA);
assertTrue(sC.parentSlice == sA);
});
test('nestedParsingWithDoubleNesting', function() {
var events = [
{name: 'a', args: {}, pid: 1, ts: 1, cat: 'foo', tid: 1, ph: 'B'},
{name: 'b', args: {}, pid: 1, ts: 2, cat: 'bar', tid: 1, ph: 'B'},
{name: 'c', args: {}, pid: 1, ts: 3, cat: 'baz', tid: 1, ph: 'B'},
{name: 'c', args: {}, pid: 1, ts: 5, cat: 'baz', tid: 1, ph: 'E'},
{name: 'b', args: {}, pid: 1, ts: 7, cat: 'bar', tid: 1, ph: 'E'},
{name: 'a', args: {}, pid: 1, ts: 8, cat: 'foo', tid: 1, ph: 'E'}
];
var m = new tv.c.TraceModel(events, false);
var t = m.processes[1].threads[1];
var sA = findSliceNamed(t.sliceGroup, 'a');
var sB = findSliceNamed(t.sliceGroup, 'b');
var sC = findSliceNamed(t.sliceGroup, 'c');
assertEquals('a', sA.title);
assertEquals('foo', sA.category);
assertEquals(0.001, sA.start);
assertEquals(0.007, sA.duration);
assertEquals(0.002, sA.selfTime);
assertEquals('b', sB.title);
assertEquals('bar', sB.category);
assertEquals(0.002, sB.start);
assertEquals(0.005, sB.duration);
assertEquals(0.002, sA.selfTime);
assertEquals('c', sC.title);
assertEquals('baz', sC.category);
assertEquals(0.003, sC.start);
assertEquals(0.002, sC.duration);
assertTrue(sA.subSlices.length == 1);
assertTrue(sA.subSlices[0] == sB);
assertTrue(sB.parentSlice == sA);
assertTrue(sB.subSlices.length == 1);
assertTrue(sB.subSlices[0] == sC);
assertTrue(sC.parentSlice == sB);
});
test('autoclosing', function() {
var events = [
// Slice that doesn't finish.
{name: 'a', args: {}, pid: 1, ts: 1, cat: 'foo', tid: 1, ph: 'B'},
// Slice that does finish to give an 'end time' to make autoclosing work.
{name: 'b', args: {}, pid: 1, ts: 1, cat: 'bar', tid: 2, ph: 'B'},
{name: 'b', args: {}, pid: 1, ts: 2, cat: 'bar', tid: 2, ph: 'E'}
];
var m = new tv.c.TraceModel(events);
var p = m.processes[1];
var t = p.threads[1];
var slice = t.sliceGroup.slices[0];
assertEquals('a', slice.title);
assertEquals('foo', slice.category);
assertTrue(slice.didNotFinish);
assertEquals(0, slice.start);
assertEquals((2 - 1) / 1000, slice.duration);
});
test('autoclosingLoneBegin', function() {
var events = [
// Slice that doesn't finish.
{name: 'a', args: {}, pid: 1, ts: 1, cat: 'foo', tid: 1, ph: 'B'}
];
var m = new tv.c.TraceModel(events);
var p = m.processes[1];
var t = p.threads[1];
var slice = t.sliceGroup.slices[0];
assertEquals('a', slice.title);
assertEquals('foo', slice.category);
assertTrue(slice.didNotFinish);
assertEquals(0, slice.start);
assertEquals(0, slice.duration);
});
test('autoclosingWithSubTasks', function() {
var events = [
{name: 'a', args: {}, pid: 1, ts: 1, cat: 'foo', tid: 1, ph: 'B'},
{name: 'b1', args: {}, pid: 1, ts: 2, cat: 'foo', tid: 1, ph: 'B'},
{name: 'b1', args: {}, pid: 1, ts: 3, cat: 'foo', tid: 1, ph: 'E'},
{name: 'b2', args: {}, pid: 1, ts: 3, cat: 'foo', tid: 1, ph: 'B'}
];
var m = new tv.c.TraceModel(events, false);
var t = m.processes[1].threads[1];
var sA = findSliceNamed(t.sliceGroup, 'a');
var sB1 = findSliceNamed(t.sliceGroup, 'b1');
var sB2 = findSliceNamed(t.sliceGroup, 'b2');
assertEquals(0.003, sA.end);
assertEquals(0.003, sB1.end);
assertEquals(0.003, sB2.end);
});
test('autoclosingWithEventsOutsideBounds', function() {
var events = [
// Slice that begins before min and ends after max of the other threads.
{name: 'a', args: {}, pid: 1, ts: 0, cat: 'foo', tid: 1, ph: 'B'},
{name: 'b', args: {}, pid: 1, ts: 3, cat: 'foo', tid: 1, ph: 'B'},
// Slice that does finish to give an 'end time' to establish a basis
{name: 'c', args: {}, pid: 1, ts: 1, cat: 'bar', tid: 2, ph: 'B'},
{name: 'c', args: {}, pid: 1, ts: 2, cat: 'bar', tid: 2, ph: 'E'}
];
var m = new tv.c.TraceModel(events, false);
var p = m.processes[1];
var t = p.threads[1];
assertEquals(2, t.sliceGroup.length);
var slice = findSliceNamed(t.sliceGroup, 'a');
assertEquals('a', slice.title);
assertEquals('foo', slice.category);
assertEquals(0, slice.start);
assertEquals(0.003, slice.duration);
var t2 = p.threads[2];
var slice2 = findSliceNamed(t2.sliceGroup, 'c');
assertEquals('c', slice2.title);
assertEquals('bar', slice2.category);
assertEquals(0.001, slice2.start);
assertEquals(0.001, slice2.duration);
assertEquals(0.000, m.bounds.min);
assertEquals(0.003, m.bounds.max);
});
test('nestedAutoclosing', function() {
var events = [
// Tasks that don't finish.
{name: 'a1', args: {}, pid: 1, ts: 1, cat: 'foo', tid: 1, ph: 'B'},
{name: 'a2', args: {}, pid: 1, ts: 1.5, cat: 'foo', tid: 1, ph: 'B'},
// Slice that does finish to give an 'end time' to make autoclosing work.
{name: 'b', args: {}, pid: 1, ts: 1, cat: 'foo', tid: 2, ph: 'B'},
{name: 'b', args: {}, pid: 1, ts: 2, cat: 'foo', tid: 2, ph: 'E'}
];
var m = new tv.c.TraceModel(events, false);
var t1 = m.processes[1].threads[1];
var t2 = m.processes[1].threads[2];
var sA1 = findSliceNamed(t1.sliceGroup, 'a1');
var sA2 = findSliceNamed(t1.sliceGroup, 'a2');
var sB = findSliceNamed(t2.sliceGroup, 'b');
assertEquals(0.002, sA1.end);
assertEquals(0.002, sA2.end);
});
test('taskColoring', function() {
// The test below depends on hashing of 'a' != 'b'. Fail early if that
// assumption is incorrect.
assertNotEquals(tv.b.ui.getStringHash('a'), tv.b.ui.getStringHash('b'));
var events = [
{name: 'a', args: {}, pid: 1, ts: 1, cat: 'foo', tid: 1, ph: 'B'},
{name: 'a', args: {}, pid: 1, ts: 2, cat: 'foo', tid: 1, ph: 'E'},
{name: 'b', args: {}, pid: 1, ts: 3, cat: 'bar', tid: 1, ph: 'B'},
{name: 'b', args: {}, pid: 1, ts: 4, cat: 'bar', tid: 1, ph: 'E'},
{name: 'a', args: {}, pid: 1, ts: 5, cat: 'baz', tid: 1, ph: 'B'},
{name: 'a', args: {}, pid: 1, ts: 6, cat: 'baz', tid: 1, ph: 'E'}
];
var m = new tv.c.TraceModel(events);
var p = m.processes[1];
var t = p.threads[1];
var a1 = t.sliceGroup.slices[0];
assertEquals('a', a1.title);
assertEquals('foo', a1.category);
var b = t.sliceGroup.slices[1];
assertEquals('b', b.title);
assertEquals('bar', b.category);
assertNotEquals(a1.colorId, b.colorId);
var a2 = t.sliceGroup.slices[2];
assertEquals('a', a2.title);
assertEquals('baz', a2.category);
assertEquals(a1.colorId, a2.colorId);
});
test('multipleThreadParsing', function() {
var events = [
{name: 'a', args: {}, pid: 1, ts: 1, cat: 'foo', tid: 1, ph: 'B'},
{name: 'a', args: {}, pid: 1, ts: 2, cat: 'foo', tid: 1, ph: 'E'},
{name: 'b', args: {}, pid: 1, ts: 3, cat: 'bar', tid: 2, ph: 'B'},
{name: 'b', args: {}, pid: 1, ts: 4, cat: 'bar', tid: 2, ph: 'E'}
];
var m = new tv.c.TraceModel(events);
assertEquals(1, m.numProcesses);
var p = m.processes[1];
assertNotUndefined(p);
assertEquals(2, p.numThreads);
// Check thread 1.
var t = p.threads[1];
assertNotUndefined(t);
assertEquals(1, t.sliceGroup.length);
assertEquals(1, t.tid);
var slice = t.sliceGroup.slices[0];
assertEquals('a', slice.title);
assertEquals('foo', slice.category);
assertEquals(0, slice.start);
assertEquals((2 - 1) / 1000, slice.duration);
assertEquals(0, slice.subSlices.length);
// Check thread 2.
var t = p.threads[2];
assertNotUndefined(t);
assertEquals(1, t.sliceGroup.length);
assertEquals(2, t.tid);
slice = t.sliceGroup.slices[0];
assertEquals('b', slice.title);
assertEquals('bar', slice.category);
assertEquals((3 - 1) / 1000, slice.start);
assertEquals((4 - 3) / 1000, slice.duration);
assertEquals(0, slice.subSlices.length);
});
test('multiplePidParsing', function() {
var events = [
{name: 'a', args: {}, pid: 1, ts: 1, cat: 'foo', tid: 1, ph: 'B'},
{name: 'a', args: {}, pid: 1, ts: 2, cat: 'foo', tid: 1, ph: 'E'},
{name: 'b', args: {}, pid: 2, ts: 3, cat: 'bar', tid: 2, ph: 'B'},
{name: 'b', args: {}, pid: 2, ts: 4, cat: 'bar', tid: 2, ph: 'E'}
];
var m = new tv.c.TraceModel(events);
assertEquals(2, m.numProcesses);
var p = m.processes[1];
assertNotUndefined(p);
assertEquals(1, p.numThreads);
// Check process 1 thread 1.
var t = p.threads[1];
assertNotUndefined(t);
assertEquals(1, t.sliceGroup.length);
assertEquals(1, t.tid);
var slice = t.sliceGroup.slices[0];
assertEquals('a', slice.title);
assertEquals('foo', slice.category);
assertEquals(0, slice.start);
assertEquals((2 - 1) / 1000, slice.duration);
assertEquals(0, slice.subSlices.length);
// Check process 2 thread 2.
var p = m.processes[2];
assertNotUndefined(p);
assertEquals(1, p.numThreads);
var t = p.threads[2];
assertNotUndefined(t);
assertEquals(1, t.sliceGroup.length);
assertEquals(2, t.tid);
slice = t.sliceGroup.slices[0];
assertEquals('b', slice.title);
assertEquals('bar', slice.category);
assertEquals((3 - 1) / 1000, slice.start);
assertEquals((4 - 3) / 1000, slice.duration);
assertEquals(0, slice.subSlices.length);
// Check getAllThreads.
assertArrayEquals([m.processes[1].threads[1], m.processes[2].threads[2]],
m.getAllThreads());
});
// Process names.
test('processNames', function() {
var events = [
{name: 'process_name', args: {name: 'SomeProcessName'},
pid: 1, ts: 0, tid: 1, ph: 'M'},
{name: 'process_name', args: {name: 'SomeProcessName'},
pid: 2, ts: 0, tid: 1, ph: 'M'}
];
var m = new tv.c.TraceModel();
m.importTraces([events], false, false);
assertEquals('SomeProcessName', m.processes[1].name);
});
// Process labels.
test('processLabels', function() {
var events = [
{name: 'process_labels', args: {labels: 'foo,bar,bar,foo,baz'},
pid: 1, ts: 0, tid: 1, ph: 'M'},
{name: 'process_labels', args: {labels: 'baz'},
pid: 2, ts: 0, tid: 1, ph: 'M'}
];
var m = new tv.c.TraceModel();
m.importTraces([events], false, false);
assertArrayEquals(['foo', 'bar', 'baz'], m.processes[1].labels);
assertArrayEquals(['baz'], m.processes[2].labels);
});
// Process sort index.
test('processSortIndex', function() {
var events = [
{name: 'process_name', args: {name: 'First'},
pid: 2, ts: 0, tid: 1, ph: 'M'},
{name: 'process_name', args: {name: 'Second'},
pid: 2, ts: 0, tid: 1, ph: 'M'},
{name: 'process_sort_index', args: {sort_index: 1},
pid: 1, ts: 0, tid: 1, ph: 'M'}
];
var m = new tv.c.TraceModel();
m.importTraces([events], false, false);
// By name, p1 is before p2. But, its sort index overrides that.
assertTrue(m.processes[1].compareTo(m.processes[2]) > 0);
});
// Thread names.
test('threadNames', function() {
var events = [
{name: 'thread_name', args: {name: 'Thread 1'},
pid: 1, ts: 0, tid: 1, ph: 'M'},
{name: 'thread_name', args: {name: 'Thread 2'},
pid: 2, ts: 0, tid: 2, ph: 'M'}
];
var m = new tv.c.TraceModel(events);
m.importTraces([events], false, false);
assertEquals('Thread 1', m.processes[1].threads[1].name);
assertEquals('Thread 2', m.processes[2].threads[2].name);
});
// Thread sort index.
test('threadSortIndex', function() {
var events = [
{name: 'thread_name', args: {name: 'Thread 1'},
pid: 1, ts: 0, tid: 1, ph: 'M'},
{name: 'thread_name', args: {name: 'Thread 2'},
pid: 1, ts: 0, tid: 2, ph: 'M'},
{name: 'thread_sort_index', args: {sort_index: 1},
pid: 1, ts: 0, tid: 1, ph: 'M'}
];
var m = new tv.c.TraceModel();
m.importTraces([events], false, false);
// By name, t1 is before t2. But, its sort index overrides that.
var t1 = m.processes[1].threads[1];
var t2 = m.processes[1].threads[2];
assertTrue(t1.compareTo(t2) > 0);
});
// CPU counts.
test('cpuCounts', function() {
var events = [
{name: 'num_cpus', args: {number: 4},
pid: 7, ts: 0, tid: 0, ph: 'M'},
{name: 'num_cpus', args: {number: 4},
pid: 14, ts: 0, tid: 0, ph: 'M'}
];
var m = new tv.c.TraceModel();
m.importTraces([events], false, false);
assertEquals(4, m.kernel.softwareMeasuredCpuCount);
assertEquals(4, m.kernel.bestGuessAtCpuCount);
});
test('cpuCountsWithSandboxBeingConfused', function() {
var events = [
{name: 'num_cpus', args: {number: 4},
pid: 7, ts: 0, tid: 0, ph: 'M'},
{name: 'num_cpus', args: {number: 1},
pid: 14, ts: 0, tid: 0, ph: 'M'}
];
var m = new tv.c.TraceModel();
m.importTraces([events], false, false);
assertEquals(4, m.kernel.softwareMeasuredCpuCount);
assertEquals(4, m.kernel.bestGuessAtCpuCount);
});
test('parsingWhenEndComesFirst', function() {
var events = [
{name: 'a', args: {}, pid: 1, ts: 1, cat: 'foo', tid: 1, ph: 'E'},
{name: 'a', args: {}, pid: 1, ts: 4, cat: 'foo', tid: 1, ph: 'B'},
{name: 'a', args: {}, pid: 1, ts: 5, cat: 'foo', tid: 1, ph: 'E'}
];
var m = new tv.c.TraceModel(events, false);
var p = m.processes[1];
var t = p.threads[1];
assertEquals(1, t.sliceGroup.length);
assertEquals('a', t.sliceGroup.slices[0].title);
assertEquals('foo', t.sliceGroup.slices[0].category);
assertEquals(0.004, t.sliceGroup.slices[0].start);
assertEquals(0.001, t.sliceGroup.slices[0].duration);
assertTrue(m.hasImportWarnings);
assertEquals(1, m.importWarnings.length);
});
test('immediateParsing', function() {
var events = [
// Need to include immediates inside a task so the timeline
// recentering/zeroing doesn't clobber their timestamp.
{name: 'a', args: {}, pid: 1, ts: 1, cat: 'foo', tid: 1, ph: 'B'},
{name: 'immediate', args: {}, pid: 1, ts: 2, cat: 'bar', tid: 1, ph: 'I'},
{name: 'slower', args: {}, pid: 1, ts: 4, cat: 'baz', tid: 1, ph: 'i'},
{name: 'a', args: {}, pid: 1, ts: 4, cat: 'foo', tid: 1, ph: 'E'}
];
var m = new tv.c.TraceModel(events, false);
var p = m.processes[1];
var t = p.threads[1];
assertEquals(3, t.sliceGroup.length);
assertEquals(0.001, t.sliceGroup.slices[0].start);
assertEquals(0.003, t.sliceGroup.slices[0].duration);
assertEquals(0.002, t.sliceGroup.slices[1].start);
assertEquals(0, t.sliceGroup.slices[1].duration);
assertEquals(0.004, t.sliceGroup.slices[2].start);
var slice = findSliceNamed(t.sliceGroup, 'a');
assertEquals('a', slice.title);
assertEquals('foo', slice.category);
assertEquals(0.003, slice.duration);
var immed = findSliceNamed(t.sliceGroup, 'immediate');
assertEquals('immediate', immed.title);
assertEquals('bar', immed.category);
assertEquals(0.002, immed.start);
assertEquals(0, immed.duration);
var slower = findSliceNamed(t.sliceGroup, 'slower');
assertEquals('slower', slower.title);
assertEquals('baz', slower.category);
assertEquals(0.004, slower.start);
assertEquals(0, slower.duration);
});
test('simpleCounter', function() {
var events = [
{name: 'ctr', args: {'value': 0}, pid: 1, ts: 0, cat: 'foo', tid: 1,
ph: 'C'},
{name: 'ctr', args: {'value': 10}, pid: 1, ts: 10, cat: 'foo', tid: 1,
ph: 'C'},
{name: 'ctr', args: {'value': 0}, pid: 1, ts: 20, cat: 'foo', tid: 1,
ph: 'C'}
];
var m = new tv.c.TraceModel(events);
var p = m.processes[1];
var ctr = m.processes[1].counters['foo.ctr'];
assertEquals('ctr', ctr.name);
assertEquals('foo', ctr.category);
assertEquals(3, ctr.numSamples);
assertEquals(1, ctr.numSeries);
assertEquals('value', ctr.series[0].name);
assertEquals(tv.b.ui.getColorIdForGeneralPurposeString('ctr.value'),
ctr.series[0].color);
assertArrayEquals([0, 0.01, 0.02], ctr.timestamps);
var samples = [];
ctr.series[0].samples.forEach(function(sample) {
samples.push(sample.value);
});
assertArrayEquals([0, 10, 0], samples);
assertArrayEquals([0, 10, 0], ctr.totals);
assertEquals(10, ctr.maxTotal);
});
test('instanceCounter', function() {
var events = [
{name: 'ctr', args: {'value': 0}, pid: 1, ts: 0, cat: 'foo', tid: 1,
ph: 'C', id: 0},
{name: 'ctr', args: {'value': 10}, pid: 1, ts: 10, cat: 'foo', tid: 1,
ph: 'C', id: 0},
{name: 'ctr', args: {'value': 10}, pid: 1, ts: 10, cat: 'foo', tid: 1,
ph: 'C', id: 1},
{name: 'ctr', args: {'value': 20}, pid: 1, ts: 15, cat: 'foo', tid: 1,
ph: 'C', id: 1},
{name: 'ctr', args: {'value': 30}, pid: 1, ts: 18, cat: 'foo', tid: 1,
ph: 'C', id: 1},
{name: 'ctr', args: {'value': 40}, pid: 1, ts: 20, cat: 'bar', tid: 1,
ph: 'C', id: 2}
];
var m = new tv.c.TraceModel(events);
var p = m.processes[1];
var ctr = m.processes[1].counters['foo.ctr[0]'];
assertEquals('ctr[0]', ctr.name);
assertEquals('foo', ctr.category);
assertEquals(2, ctr.numSamples);
assertEquals(1, ctr.numSeries);
assertArrayEquals([0, 0.01], ctr.timestamps);
var samples = [];
ctr.series[0].samples.forEach(function(sample) {
samples.push(sample.value);
});
assertArrayEquals([0, 10], samples);
ctr = m.processes[1].counters['foo.ctr[1]'];
assertEquals('ctr[1]', ctr.name);
assertEquals('foo', ctr.category);
assertEquals(3, ctr.numSamples);
assertEquals(1, ctr.numSeries);
assertArrayEquals([0.01, 0.015, 0.018], ctr.timestamps);
samples = [];
ctr.series[0].samples.forEach(function(sample) {
samples.push(sample.value);
});
assertArrayEquals([10, 20, 30], samples);
ctr = m.processes[1].counters['bar.ctr[2]'];
assertEquals('ctr[2]', ctr.name);
assertEquals('bar', ctr.category);
assertEquals(1, ctr.numSamples);
assertEquals(1, ctr.numSeries);
assertArrayEquals([0.02], ctr.timestamps);
var samples = [];
ctr.series[0].samples.forEach(function(sample) {
samples.push(sample.value);
});
assertArrayEquals([40], samples);
});
test('multiCounterUpdateBounds', function() {
var ctr = new tv.c.trace_model.Counter(undefined, 'testBasicCounter',
'', 'testBasicCounter');
var value1Series = new tv.c.trace_model.CounterSeries(
'value1', 'testBasicCounter.value1');
var value2Series = new tv.c.trace_model.CounterSeries(
'value2', 'testBasicCounter.value2');
ctr.addSeries(value1Series);
ctr.addSeries(value2Series);
value1Series.addCounterSample(0, 0);
value1Series.addCounterSample(1, 1);
value1Series.addCounterSample(2, 1);
value1Series.addCounterSample(3, 2);
value1Series.addCounterSample(4, 3);
value1Series.addCounterSample(5, 1);
value1Series.addCounterSample(6, 3);
value1Series.addCounterSample(7, 3.1);
value2Series.addCounterSample(0, 0);
value2Series.addCounterSample(1, 0);
value2Series.addCounterSample(2, 1);
value2Series.addCounterSample(3, 1.1);
value2Series.addCounterSample(4, 0);
value2Series.addCounterSample(5, 7);
value2Series.addCounterSample(6, 0);
value2Series.addCounterSample(7, 0.5);
ctr.updateBounds();
assertEquals(0, ctr.bounds.min);
assertEquals(7, ctr.bounds.max);
assertEquals(8, ctr.maxTotal);
assertArrayEquals([0, 0,
1, 1,
1, 2,
2, 3.1,
3, 3,
1, 8,
3, 3,
3.1, 3.6], ctr.totals);
});
test('multiCounter', function() {
var events = [
{name: 'ctr', args: {'value1': 0, 'value2': 7}, pid: 1, ts: 0, cat: 'foo', tid: 1, ph: 'C'}, // @suppress longLineCheck
{name: 'ctr', args: {'value1': 10, 'value2': 4}, pid: 1, ts: 10, cat: 'foo', tid: 1, ph: 'C'}, // @suppress longLineCheck
{name: 'ctr', args: {'value1': 0, 'value2': 1 }, pid: 1, ts: 20, cat: 'foo', tid: 1, ph: 'C'} // @suppress longLineCheck
];
var m = new tv.c.TraceModel(events);
var p = m.processes[1];
var ctr = m.processes[1].counters['foo.ctr'];
assertEquals('ctr', ctr.name);
assertEquals('ctr', ctr.name);
assertEquals('foo', ctr.category);
assertEquals(3, ctr.numSamples);
assertEquals(2, ctr.numSeries);
assertEquals('value1', ctr.series[0].name);
assertEquals('value2', ctr.series[1].name);
assertEquals(tv.b.ui.getColorIdForGeneralPurposeString('ctr.value1'),
ctr.series[0].color);
assertEquals(tv.b.ui.getColorIdForGeneralPurposeString('ctr.value2'),
ctr.series[1].color);
assertArrayEquals([0, 0.01, 0.02], ctr.timestamps);
var samples = [];
ctr.series[0].samples.forEach(function(sample) {
samples.push(sample.value);
});
assertArrayEquals([0, 10, 0], samples);
var samples1 = [];
ctr.series[1].samples.forEach(function(sample) {
samples1.push(sample.value);
});
assertArrayEquals([7, 4, 1], samples1);
assertArrayEquals([0, 7,
10, 14,
0, 1], ctr.totals);
assertEquals(14, ctr.maxTotal);
});
test('importObjectInsteadOfArray', function() {
var events = { traceEvents: [
{name: 'a', args: {}, pid: 52, ts: 524, cat: 'foo', tid: 53, ph: 'B'},
{name: 'a', args: {}, pid: 52, ts: 560, cat: 'foo', tid: 53, ph: 'E'}
] };
var m = new tv.c.TraceModel(events);
assertEquals(1, m.numProcesses);
});
test('importString', function() {
var events = [
{name: 'a', args: {}, pid: 52, ts: 524, cat: 'foo', tid: 53, ph: 'B'},
{name: 'a', args: {}, pid: 52, ts: 560, cat: 'foo', tid: 53, ph: 'E'}
];
var m = new tv.c.TraceModel(JSON.stringify(events));
assertEquals(1, m.numProcesses);
});
test('importStringWithLeadingSpaces', function() {
var events = [
{name: 'a', args: {}, pid: 52, ts: 524, cat: 'foo', tid: 53, ph: 'B'},
{name: 'a', args: {}, pid: 52, ts: 560, cat: 'foo', tid: 53, ph: 'E'}
];
var m = new tv.c.TraceModel(' ' + JSON.stringify(events));
assertEquals(1, m.numProcesses);
});
test('importStringWithTrailingNewLine', function() {
var events = [
{name: 'a', args: {}, pid: 52, ts: 524, cat: 'foo', tid: 53, ph: 'B'},
{name: 'a', args: {}, pid: 52, ts: 560, cat: 'foo', tid: 53, ph: 'E'}
];
var m = new tv.c.TraceModel(JSON.stringify(events) + '\n');
assertEquals(1, m.numProcesses);
});
test('importStringWithMissingCloseSquareBracket', function() {
var events = [
{name: 'a', args: {}, pid: 52, ts: 524, cat: 'foo', tid: 53, ph: 'B'},
{name: 'a', args: {}, pid: 52, ts: 560, cat: 'foo', tid: 53, ph: 'E'}
];
var tmp = JSON.stringify(events);
assertEquals(']', tmp[tmp.length - 1]);
// Drop off the trailing ]
var dropped = tmp.substring(0, tmp.length - 1);
var m = new tv.c.TraceModel(dropped);
assertEquals(1, m.numProcesses);
});
test('importStringWithEndingCommaButMissingCloseSquareBracket', function() {
var lines = [
'[',
'{"name": "a", "args": {}, "pid": 52, "ts": 524, "cat": "foo", "tid": 53, "ph": "B"},', // @suppress longLineCheck
'{"name": "a", "args": {}, "pid": 52, "ts": 560, "cat": "foo", "tid": 53, "ph": "E"},' // @suppress longLineCheck
];
var text = lines.join('\n');
var m = new tv.c.TraceModel(text);
assertEquals(1, m.numProcesses);
assertEquals(1, m.processes[52].threads[53].sliceGroup.length);
});
test('importStringWithMissingCloseSquareBracketAndNewline', function() {
var events = [
{name: 'a', args: {}, pid: 52, ts: 524, cat: 'foo', tid: 53, ph: 'B'},
{name: 'a', args: {}, pid: 52, ts: 560, cat: 'foo', tid: 53, ph: 'E'}
];
var tmp = JSON.stringify(events);
assertEquals(']', tmp[tmp.length - 1]);
// Drop off the trailing ] and add a newline
var dropped = tmp.substring(0, tmp.length - 1);
var m = new tv.c.TraceModel(dropped + '\n');
assertEquals(1, m.numProcesses);
});
test('ImportStringEndingCommaButMissingCloseSquareBracketCRLF', function() {
var lines = [
'[',
'{"name": "a", "args": {}, "pid": 52, "ts": 524, "cat": "foo", "tid": 53, "ph": "B"},', // @suppress longLineCheck
'{"name": "a", "args": {}, "pid": 52, "ts": 560, "cat": "foo", "tid": 53, "ph": "E"},' // @suppress longLineCheck
];
var text = lines.join('\r\n');
var m = new tv.c.TraceModel(text);
assertEquals(1, m.numProcesses);
assertEquals(1, m.processes[52].threads[53].sliceGroup.length);
});
test('importOldFormat', function() {
var lines = [
'[',
'{"cat":"a","pid":9,"tid":8,"ts":194,"ph":"E","name":"I","args":{}},',
'{"cat":"b","pid":9,"tid":8,"ts":194,"ph":"B","name":"I","args":{}}',
']'
];
var text = lines.join('\n');
var m = new tv.c.TraceModel(text);
assertEquals(1, m.numProcesses);
assertEquals(1, m.processes[9].threads[8].sliceGroup.length);
});
test('startFinishOneSliceOneThread', function() {
var events = [
// Time is intentionally out of order.
{name: 'a', args: {}, pid: 52, ts: 560, cat: 'cat', tid: 53,
ph: 'F', id: 72},
{name: 'a', pid: 52, ts: 524, cat: 'cat', tid: 53,
ph: 'S', id: 72, args: {'foo': 'bar'}}
];
var m = new tv.c.TraceModel(events);
var t = m.processes[52].threads[53];
assertNotUndefined(t);
assertEquals(1, t.asyncSliceGroup.slices.length);
assertEquals('a', t.asyncSliceGroup.slices[0].title);
assertEquals('cat', t.asyncSliceGroup.slices[0].category);
assertEquals(72, t.asyncSliceGroup.slices[0].id);
assertEquals('bar', t.asyncSliceGroup.slices[0].args.foo);
assertEquals(0, t.asyncSliceGroup.slices[0].start);
assertAlmostEquals((60 - 24) / 1000, t.asyncSliceGroup.slices[0].duration);
assertEquals(t, t.asyncSliceGroup.slices[0].startThread);
assertEquals(t, t.asyncSliceGroup.slices[0].endThread);
});
test('endArgsAddedToSlice', function() {
var events = [
{name: 'a', args: {x: 1}, pid: 52, ts: 520, cat: 'foo', tid: 53, ph: 'B'},
{name: 'a', args: {y: 2}, pid: 52, ts: 560, cat: 'foo', tid: 53, ph: 'E'}
];
var m = new tv.c.TraceModel(events);
assertEquals(1, m.numProcesses);
var p = m.processes[52];
assertNotUndefined(p);
assertEquals(1, p.numThreads);
var t = p.threads[53];
assertNotUndefined(t);
assertEquals(1, t.sliceGroup.length);
assertEquals(53, t.tid);
var slice = t.sliceGroup.slices[0];
assertEquals('a', slice.title);
assertEquals('foo', slice.category);
assertEquals(0, slice.start);
assertEquals(0, slice.subSlices.length);
assertEquals(1, slice.args['x']);
assertEquals(2, slice.args['y']);
});
test('endArgOverrwritesOriginalArgValueIfDuplicated', function() {
var events = [
{name: 'b', args: {z: 3}, pid: 52, ts: 629, cat: 'foo', tid: 53, ph: 'B'},
{name: 'b', args: {z: 4}, pid: 52, ts: 631, cat: 'foo', tid: 53, ph: 'E'}
];
var m = new tv.c.TraceModel(events);
assertEquals(1, m.numProcesses);
var p = m.processes[52];
assertNotUndefined(p);
assertEquals(1, p.numThreads);
var t = p.threads[53];
assertNotUndefined(t);
var slice = t.sliceGroup.slices[0];
assertEquals('b', slice.title);
assertEquals('foo', slice.category);
assertEquals(0, slice.start);
assertEquals(0, slice.subSlices.length);
assertEquals(4, slice.args['z']);
});
test('asyncEndArgsAddedToSlice', function() {
var events = [
// Time is intentionally out of order.
{name: 'c', args: {y: 2}, pid: 52, ts: 560, cat: 'foo', tid: 53,
ph: 'F', id: 72},
{name: 'c', args: {x: 1}, pid: 52, ts: 524, cat: 'foo', tid: 53,
ph: 'S', id: 72}
];
var m = new tv.c.TraceModel(events);
var t = m.processes[52].threads[53];
assertNotUndefined(t);
assertEquals(1, t.asyncSliceGroup.slices.length);
var parentSlice = t.asyncSliceGroup.slices[0];
assertEquals('c', parentSlice.title);
assertEquals('foo', parentSlice.category);
assertEquals(1, parentSlice.args['x']);
assertEquals(2, parentSlice.args['y']);
assertNotUndefined(parentSlice.subSlices);
assertEquals(0, parentSlice.subSlices.length);
});
test('asyncEndArgOverwritesOriginalArgValueIfDuplicated', function() {
var events = [
// Time is intentionally out of order.
{name: 'd', args: {z: 4}, pid: 52, ts: 560, cat: 'foo', tid: 53,
ph: 'F', id: 72},
{name: 'd', args: {z: 3}, pid: 52, ts: 524, cat: 'foo', tid: 53,
ph: 'S', id: 72}
];
var m = new tv.c.TraceModel(events);
var t = m.processes[52].threads[53];
assertNotUndefined(t);
assertEquals(1, t.asyncSliceGroup.slices.length);
var parentSlice = t.asyncSliceGroup.slices[0];
assertEquals('d', parentSlice.title);
assertEquals('foo', parentSlice.category);
assertEquals(4, parentSlice.args['z']);
assertNotUndefined(parentSlice.subSlices);
assertEquals(0, parentSlice.subSlices.length);
});
test('asyncStepsInOneThread', function() {
var events = [
// Time is intentionally out of order.
{name: 'a', args: {z: 3}, pid: 52, ts: 560, cat: 'foo', tid: 53, ph: 'F', id: 72}, // @suppress longLineCheck
{name: 'a', args: {step: 's1', y: 2}, pid: 52, ts: 548, cat: 'foo', tid: 53, ph: 'T', id: 72}, // @suppress longLineCheck
{name: 'a', args: {x: 1}, pid: 52, ts: 524, cat: 'foo', tid: 53, ph: 'S', id: 72} // @suppress longLineCheck
];
var m = new tv.c.TraceModel(events);
var t = m.processes[52].threads[53];
assertNotUndefined(t);
assertEquals(1, t.asyncSliceGroup.slices.length);
var parentSlice = t.asyncSliceGroup.slices[0];
assertEquals('a', parentSlice.title);
assertEquals('foo', parentSlice.category);
assertEquals(0, parentSlice.start);
assertEquals(1, parentSlice.args['x']);
assertUndefined(parentSlice.args['y']);
assertEquals(3, parentSlice.args['z']);
assertNotUndefined(parentSlice.subSlices);
assertEquals(1, parentSlice.subSlices.length);
var subSlice = parentSlice.subSlices[0];
assertEquals('a:s1', subSlice.title);
assertEquals('foo', subSlice.category);
assertAlmostEquals((548 - 524) / 1000, subSlice.start);
assertAlmostEquals((560 - 548) / 1000, subSlice.duration);
assertUndefined(subSlice.args['x']);
assertEquals(2, subSlice.args['y']);
assertUndefined(subSlice.args['z']);
});
test('asyncStepsMissingStart', function() {
var events = [
// Time is intentionally out of order.
{name: 'a', args: {z: 3}, pid: 52, ts: 560, cat: 'foo', tid: 53,
ph: 'F', id: 72},
{name: 'a', args: {step: 's1', y: 2}, pid: 52, ts: 548, cat: 'foo',
tid: 53, ph: 'T', id: 72}
];
var m = new tv.c.TraceModel(events);
var t = m.processes[52].threads[53];
assertUndefined(t);
});
test('asyncStepsMissingFinish', function() {
var events = [
// Time is intentionally out of order.
{name: 'a', args: {step: 's1', y: 2}, pid: 52, ts: 548, cat: 'foo',
tid: 53, ph: 'T', id: 72},
{name: 'a', args: {z: 3}, pid: 52, ts: 560, cat: 'foo', tid: 53,
ph: 'S', id: 72}
];
var m = new tv.c.TraceModel(events);
var t = m.processes[52].threads[53];
assertUndefined(t);
});
test('asyncStepEndEvent', function() {
var events = [
// Time is intentionally out of order.
{name: 'a', args: {z: 3}, pid: 52, ts: 560, cat: 'foo', tid: 53,
ph: 'F', id: 72},
{name: 'a', args: {step: 's1', y: 2}, pid: 52, ts: 548, cat: 'foo',
tid: 53, ph: 'p', id: 72},
{name: 'a', args: {x: 1}, pid: 52, ts: 524, cat: 'foo', tid: 53,
ph: 'S', id: 72}
];
var m = new tv.c.TraceModel(events);
var t = m.processes[52].threads[53];
assertNotUndefined(t);
assertEquals(1, t.asyncSliceGroup.slices.length);
var parentSlice = t.asyncSliceGroup.slices[0];
assertEquals('a', parentSlice.title);
assertEquals('foo', parentSlice.category);
assertEquals(0, parentSlice.start);
assertEquals(1, parentSlice.args['x']);
assertUndefined(parentSlice.args['y']);
assertEquals(3, parentSlice.args['z']);
assertNotUndefined(parentSlice.subSlices);
assertEquals(1, parentSlice.subSlices.length);
var subSlice = parentSlice.subSlices[0];
assertEquals('a:s1', subSlice.title);
assertEquals('foo', subSlice.category);
assertEquals(0, subSlice.start);
assertAlmostEquals((548 - 524) / 1000, subSlice.duration);
assertUndefined(subSlice.args['x']);
assertEquals(2, subSlice.args['y']);
assertUndefined(subSlice.args['z']);
});
test('asyncStepMismatch', function() {
var events = [
// Time is intentionally out of order.
{name: 'a', args: {z: 3}, pid: 52, ts: 560, cat: 'foo', tid: 53,
ph: 'F', id: 72},
{name: 'a', args: {step: 's2'}, pid: 52, ts: 548, cat: 'foo', tid: 53,
ph: 'T', id: 72},
{name: 'a', args: {step: 's1'}, pid: 52, ts: 548, cat: 'foo', tid: 53,
ph: 'p', id: 72},
{name: 'a', args: {x: 1}, pid: 52, ts: 524, cat: 'foo', tid: 53,
ph: 'S', id: 72}
];
var m = new tv.c.TraceModel(events);
var t = m.processes[52].threads[53];
assertUndefined(t);
assertTrue(m.hasImportWarnings);
});
test('nestableAsyncBasic', function() {
var events = [
{name: 'a', args: {}, pid: 52, ts: 524, cat: 'foo', tid: 53,
ph: 'b', id: 72},
{name: 'b', args: {x: 1}, pid: 52, ts: 525, cat: 'foo', tid: 53,
ph: 'b', id: 72},
{name: 'b', args: {y: 2}, pid: 52, ts: 560, cat: 'foo', tid: 53,
ph: 'e', id: 72},
{name: 'a', args: {}, pid: 52, ts: 565, cat: 'foo', tid: 53,
ph: 'e', id: 72}
];
var m = new tv.c.TraceModel(events);
var t = m.processes[52].threads[53];
assertNotUndefined(t);
assertEquals(1, t.asyncSliceGroup.slices.length);
var parentSlice = t.asyncSliceGroup.slices[0];
assertEquals('a', parentSlice.title);
assertEquals('foo', parentSlice.category);
assertNotUndefined(parentSlice.subSlices);
assertEquals(1, parentSlice.subSlices.length);
var subSlice = parentSlice.subSlices[0];
// Arguments should include both BEGIN and END event.
assertEquals(1, subSlice.args['x']);
assertEquals(2, subSlice.args['y']);
assertUndefined(subSlice.subSlices);
});
test('nestableAsyncCombinedParams', function() {
var events = [
{name: 'a', args: {x: 1, params: {p1: 'hello', p2: 123}},
pid: 52, ts: 525, cat: 'foo', tid: 53, ph: 'b', id: 72},
{name: 'a', args: {y: 2, params: {p3: 'hi'}}, pid: 52, ts: 560,
cat: 'foo', tid: 53, ph: 'e', id: 72},
{name: 'b', args: {params: {p4: 'foo'}},
pid: 52, ts: 525, cat: 'foo', tid: 53, ph: 'b', id: 73},
{name: 'b', args: {params: ''}, pid: 52, ts: 560,
cat: 'foo', tid: 53, ph: 'e', id: 73},
{name: 'c', args: {params: {p5: 'bar'}},
pid: 52, ts: 525, cat: 'foo', tid: 53, ph: 'b', id: 74},
{name: 'c', args: {}, pid: 52, ts: 560,
cat: 'foo', tid: 53, ph: 'e', id: 74}
];
var m = new tv.c.TraceModel(events);
var t = m.processes[52].threads[53];
assertNotUndefined(t);
assertEquals(3, t.asyncSliceGroup.slices.length);
var sliceA = t.asyncSliceGroup.slices[0];
// Arguments should include both BEGIN and END event.
assertEquals(1, sliceA.args['x']);
assertEquals(2, sliceA.args['y']);
var paramsA = sliceA.args['params'];
assertNotUndefined(paramsA);
assertEquals('hello', paramsA.p1);
assertEquals(123, paramsA.p2);
assertEquals('hi', paramsA.p3);
var sliceB = t.asyncSliceGroup.slices[1];
// Arguments should include both BEGIN and END event.
var paramsB = sliceB.args['params'];
assertNotUndefined(paramsB);
assertEquals('foo', paramsB.p4);
var sliceC = t.asyncSliceGroup.slices[2];
// Arguments should include both BEGIN and END event.
var paramsC = sliceC.args['params'];
assertNotUndefined(paramsC);
assertEquals('bar', paramsC.p5);
});
test('nestableAsyncManyLevels', function() {
// There are 5 nested levels.
var events = [
{name: 'l1', args: {}, pid: 52, ts: 524, cat: 'foo', tid: 53,
ph: 'b', id: 72},
{name: 'l2', args: {}, pid: 52, ts: 525, cat: 'foo', tid: 53,
ph: 'b', id: 72},
{name: 'l3', args: {}, pid: 52, ts: 526, cat: 'foo', tid: 53,
ph: 'b', id: 72},
{name: 'l4', args: {}, pid: 52, ts: 527, cat: 'foo', tid: 53,
ph: 'b', id: 72},
{name: 'l5', args: {}, pid: 52, ts: 528, cat: 'foo', tid: 53,
ph: 'b', id: 72},
{name: 'l5', args: {}, pid: 52, ts: 529, cat: 'foo', tid: 53,
ph: 'e', id: 72},
{name: 'l4', args: {}, pid: 52, ts: 530, cat: 'foo', tid: 53,
ph: 'e', id: 72},
{name: 'l3', args: {}, pid: 52, ts: 531, cat: 'foo', tid: 53,
ph: 'e', id: 72},
{name: 'l2', args: {}, pid: 52, ts: 532, cat: 'foo', tid: 53,
ph: 'e', id: 72},
{name: 'l1', args: {}, pid: 52, ts: 533, cat: 'foo', tid: 53,
ph: 'e', id: 72}
];
var m = new tv.c.TraceModel(events);
var t = m.processes[52].threads[53];
assertNotUndefined(t);
// Perfectly matched events should not produce a warning.
assertFalse(m.hasImportWarnings);
assertEquals(1, t.asyncSliceGroup.slices.length);
var l1Slice = t.asyncSliceGroup.slices[0];
assertEquals('l1', l1Slice.title);
assertAlmostEquals(0, l1Slice.start);
assertAlmostEquals(9 / 1000, l1Slice.duration);
assertNotUndefined(l1Slice.subSlices);
assertEquals(1, l1Slice.subSlices.length);
var l2Slice = l1Slice.subSlices[0];
assertEquals('l2', l2Slice.title);
assertAlmostEquals(1 / 1000, l2Slice.start);
assertAlmostEquals(7 / 1000, l2Slice.duration);
assertNotUndefined(l2Slice.subSlices);
assertEquals(1, l2Slice.subSlices.length);
var l3Slice = l2Slice.subSlices[0];
assertEquals('l3', l3Slice.title);
assertAlmostEquals(2 / 1000, l3Slice.start);
assertAlmostEquals(5 / 1000, l3Slice.duration);
assertNotUndefined(l3Slice.subSlices);
assertEquals(1, l3Slice.subSlices.length);
var l4Slice = l3Slice.subSlices[0];
assertEquals('l4', l4Slice.title);
assertAlmostEquals(3 / 1000, l4Slice.start);
assertAlmostEquals(3 / 1000, l4Slice.duration);
assertNotUndefined(l4Slice.subSlices);
assertEquals(1, l4Slice.subSlices.length);
var l5Slice = l4Slice.subSlices[0];
assertEquals('l5', l5Slice.title);
assertAlmostEquals(4 / 1000, l5Slice.start);
assertAlmostEquals(1 / 1000, l5Slice.duration);
});
test('nestableAsyncInstantEvent', function() {
var events = [
{name: 'c', args: {}, pid: 52, ts: 524, cat: 'foo', tid: 53,
ph: 'n', id: 71},
{name: 'a', args: {}, pid: 52, ts: 524, cat: 'foo', tid: 53,
ph: 'b', id: 72},
{name: 'd', args: {}, pid: 52, ts: 525, cat: 'foo', tid: 53,
ph: 'n', id: 72},
{name: 'a', args: {}, pid: 52, ts: 565, cat: 'foo', tid: 53,
ph: 'e', id: 72}
];
var m = new tv.c.TraceModel(events);
var t = m.processes[52].threads[53];
assertNotUndefined(t);
assertEquals(2, t.asyncSliceGroup.slices.length);
var instantSlice = t.asyncSliceGroup.slices[0];
assertEquals('c', instantSlice.title);
assertAlmostEquals(0, instantSlice.start);
assertAlmostEquals(0, instantSlice.duration);
assertUndefined(instantSlice.subSlices);
var nestedSlice = t.asyncSliceGroup.slices[1];
assertEquals('a', nestedSlice.title);
assertAlmostEquals(0, nestedSlice.start);
assertAlmostEquals((565 - 524) / 1000, nestedSlice.duration);
assertNotUndefined(nestedSlice.subSlices);
assertEquals(1, nestedSlice.subSlices.length);
var nestedInstantSlice = nestedSlice.subSlices[0];
assertUndefined(nestedInstantSlice.subSlices);
assertEquals('d', nestedInstantSlice.title);
});
test('nestableAsyncUnmatchedOuterBeginEvent', function() {
var events = [
{name: 'a', args: {x: 1}, pid: 52, ts: 524, cat: 'foo', tid: 53,
ph: 'b', id: 72},
{name: 'b', args: {}, pid: 52, ts: 525, cat: 'foo', tid: 53,
ph: 'b', id: 72},
{name: 'b', args: {y: 2}, pid: 52, ts: 560, cat: 'foo', tid: 53,
ph: 'e', id: 72}
];
var m = new tv.c.TraceModel(events);
var t = m.processes[52].threads[53];
assertNotUndefined(t);
// Unmatched BEGIN should produce a warning.
assertTrue(m.hasImportWarnings);
assertEquals(1, t.asyncSliceGroup.slices.length);
var parentSlice = t.asyncSliceGroup.slices[0];
assertEquals('a', parentSlice.title);
assertEquals('foo', parentSlice.category);
assertAlmostEquals(0, parentSlice.start);
// Unmatched BEGIN event ends at the last event of that ID.
assertAlmostEquals(36 / 1000, parentSlice.duration);
// Arguments should include only include its arguments.
assertUndefined(parentSlice.args['y']);
assertEquals(1, parentSlice.args['x']);
assertNotUndefined(parentSlice.error);
assertNotUndefined(parentSlice.subSlices);
assertEquals(1, parentSlice.subSlices.length);
var subSlice = parentSlice.subSlices[0];
assertAlmostEquals(1 / 1000, subSlice.start);
assertAlmostEquals(35 / 1000, subSlice.duration);
assertUndefined(subSlice.subSlices);
// Arguments should include those of the END event.
assertEquals(2, subSlice.args['y']);
assertUndefined(subSlice.subSlices);
});
test('nestableAsyncUnmatchedInnerBeginEvent', function() {
var events = [
{name: 'a', args: {z: 3}, pid: 52, ts: 524, cat: 'foo', tid: 53,
ph: 'b', id: 72},
{name: 'c', args: {}, pid: 52, ts: 525, cat: 'foo', tid: 53,
ph: 'n', id: 72},
{name: 'b', args: {x: 1}, pid: 52, ts: 525, cat: 'foo', tid: 53,
ph: 'b', id: 72},
{name: 'a', args: {y: 2}, pid: 52, ts: 565, cat: 'foo', tid: 53,
ph: 'e', id: 72}
];
var m = new tv.c.TraceModel(events);
var t = m.processes[52].threads[53];
assertNotUndefined(t);
// Unmatched BEGIN should produce a warning.
assertTrue(m.hasImportWarnings);
assertEquals(1, t.asyncSliceGroup.slices.length);
var parentSlice = t.asyncSliceGroup.slices[0];
assertEquals('a', parentSlice.title);
assertEquals('foo', parentSlice.category);
assertAlmostEquals(0, parentSlice.start);
assertAlmostEquals(41 / 1000, parentSlice.duration);
// Arguments should include both BEGIN and END event.
assertEquals(2, parentSlice.args['y']);
assertEquals(3, parentSlice.args['z']);
assertUndefined(parentSlice.args['x']);
assertNotUndefined(parentSlice.subSlices);
assertEquals(2, parentSlice.subSlices.length);
var subSliceInstant = parentSlice.subSlices[0];
var subSliceUnmatched = parentSlice.subSlices[1];
assertEquals('c', subSliceInstant.title);
assertEquals('b', subSliceUnmatched.title);
// Unmatched BEGIN ends at the last event of that ID.
assertAlmostEquals(1 / 1000, subSliceUnmatched.start);
assertAlmostEquals(40 / 1000, subSliceUnmatched.duration);
assertUndefined(subSliceUnmatched.subSlices);
assertEquals(1, subSliceUnmatched.args['x']);
assertUndefined(subSliceUnmatched['y']);
assertNotUndefined(subSliceUnmatched.error);
assertAlmostEquals(1 / 1000, subSliceInstant.start);
assertAlmostEquals(0, subSliceInstant.duration);
assertUndefined(subSliceInstant.subSlices);
});
test('nestableAsyncUnmatchedOuterEndEvent', function() {
// Events are intentionally out-of-order.
var events = [
{name: 'b', args: {x: 1}, pid: 52, ts: 525, cat: 'foo', tid: 53,
ph: 'b', id: 72},
{name: 'b', args: {y: 2}, pid: 52, ts: 560, cat: 'foo', tid: 53,
ph: 'e', id: 72},
{name: 'a', args: {z: 3}, pid: 52, ts: 524, cat: 'foo', tid: 53,
ph: 'e', id: 72}
];
var m = new tv.c.TraceModel(events);
var t = m.processes[52].threads[53];
assertNotUndefined(t);
// Unmatched END should produce a warning.
assertTrue(m.hasImportWarnings);
assertEquals(2, t.asyncSliceGroup.slices.length);
var unmatchedSlice = t.asyncSliceGroup.slices[0];
var slice = t.asyncSliceGroup.slices[1];
assertEquals('a', unmatchedSlice.title);
assertAlmostEquals(0, unmatchedSlice.start);
// Unmatched END event begins at the first event of that ID. In this
// case, the first event happens to be the same unmatched event.
assertAlmostEquals(0 / 1000, unmatchedSlice.duration);
assertUndefined(unmatchedSlice.args['x']);
assertUndefined(unmatchedSlice.args['y']);
assertEquals(3, unmatchedSlice.args['z']);
assertNotUndefined(unmatchedSlice.error);
assertUndefined(unmatchedSlice.subSlices);
assertEquals('b', slice.title);
assertAlmostEquals(1 / 1000, slice.start);
assertAlmostEquals(35 / 1000, slice.duration);
// Arguments should include both BEGIN and END event.
assertEquals(1, slice.args['x']);
assertEquals(2, slice.args['y']);
assertUndefined(slice.subSlices);
});
test('nestableAsyncUnmatchedInnerEndEvent', function() {
var events = [
{name: 'a', args: {x: 1}, pid: 52, ts: 524, cat: 'foo', tid: 53,
ph: 'b', id: 72},
{name: 'c', args: {}, pid: 52, ts: 525, cat: 'foo', tid: 53,
ph: 'n', id: 72},
{name: 'b', args: {z: 3}, pid: 52, ts: 525, cat: 'foo', tid: 53,
ph: 'e', id: 72},
{name: 'a', args: {y: 2}, pid: 52, ts: 565, cat: 'foo', tid: 53,
ph: 'e', id: 72}
];
var m = new tv.c.TraceModel(events);
var t = m.processes[52].threads[53];
assertNotUndefined(t);
// Unmatched END should produce a warning.
assertTrue(m.hasImportWarnings);
assertEquals(1, t.asyncSliceGroup.slices.length);
var parentSlice = t.asyncSliceGroup.slices[0];
assertEquals('a', parentSlice.title);
assertAlmostEquals(0, parentSlice.start);
assertAlmostEquals(41 / 1000, parentSlice.duration);
// Arguments should include both BEGIN and END event.
assertEquals(1, parentSlice.args['x']);
assertEquals(2, parentSlice.args['y']);
assertNotUndefined(parentSlice.subSlices);
assertEquals(2, parentSlice.subSlices.length);
var subSliceInstant = parentSlice.subSlices[0];
var subSliceUnmatched = parentSlice.subSlices[1];
assertEquals('c', subSliceInstant.title);
assertEquals('b', subSliceUnmatched.title);
// Unmatched END begins at the first event of that ID.
assertAlmostEquals(0 / 1000, subSliceUnmatched.start);
assertAlmostEquals(1 / 1000, subSliceUnmatched.duration);
// Arguments should include both BEGIN and END event.
assertUndefined(subSliceUnmatched.args['x']);
assertUndefined(subSliceUnmatched.args['y']);
assertEquals(3, subSliceUnmatched.args['z']);
assertNotUndefined(subSliceUnmatched.error);
assertUndefined(subSliceUnmatched.subSlices);
assertAlmostEquals(1 / 1000, subSliceInstant.start);
assertAlmostEquals(0, subSliceInstant.duration);
assertUndefined(subSliceInstant.subSlices);
});
test('nestableAsyncSameIDDifferentCategory', function() {
// Events with the same ID, but different categories should not be
// considered as nested.
var events = [
{name: 'EVENT_A', args: {}, pid: 52, ts: 500, cat: 'foo', tid: 53,
ph: 'b', id: 72},
{name: 'EVENT_B', args: {y: 2}, pid: 52, ts: 550, cat: 'bar', tid: 53,
ph: 'b', id: 72},
{name: 'EVENT_B', args: {}, pid: 52, ts: 600, cat: 'bar', tid: 53,
ph: 'e', id: 72},
{name: 'EVENT_A', args: {x: 1}, pid: 52, ts: 650, cat: 'foo', tid: 53,
ph: 'e', id: 72}
];
var m = new tv.c.TraceModel(events);
var t = m.processes[52].threads[53];
assertNotUndefined(t);
assertEquals(2, t.asyncSliceGroup.slices.length);
var eventASlice = t.asyncSliceGroup.slices[0];
assertEquals('EVENT_A', eventASlice.title);
assertEquals('foo', eventASlice.category);
assertEquals('foo:72', eventASlice.id);
assertEquals(1, eventASlice.args['x']);
assertUndefined(eventASlice.subSlices);
var eventBSlice = t.asyncSliceGroup.slices[1];
assertEquals('EVENT_B', eventBSlice.title);
assertEquals('bar', eventBSlice.category);
assertEquals('bar:72', eventBSlice.id);
assertEquals(2, eventBSlice.args['y']);
assertUndefined(eventBSlice.subSlices);
});
test('importSamples', function() {
var events = [
{name: 'a', args: {}, pid: 52, ts: 548, cat: 'test', tid: 53, ph: 'P'},
{name: 'b', args: {}, pid: 52, ts: 548, cat: 'test', tid: 53, ph: 'P'},
{name: 'c', args: {}, pid: 52, ts: 558, cat: 'test', tid: 53, ph: 'P'}
];
var m = new tv.c.TraceModel(events);
var p = m.processes[52];
assertNotUndefined(p);
var t = p.threads[53];
assertNotUndefined(t);
assertEquals(3, t.samples_.length);
assertEquals(0.0, t.samples_[0].start);
assertEquals(0.0, t.samples_[1].start);
assertApproxEquals(0.01, t.samples_[2].start);
assertEquals('a', t.samples_[0].leafStackFrame.title);
assertEquals('b', t.samples_[1].leafStackFrame.title);
assertEquals('c', t.samples_[2].leafStackFrame.title);
assertFalse(m.hasImportWarnings);
});
test('importSamplesWithStackFrames', function() {
var eventData = {
traceEvents: [
{ name: 'a', args: {}, pid: 1, ts: 0, cat: 'test', tid: 2, ph: 'P', sf: 7 } // @suppress longLineCheck
],
stackFrames: {
'1': {
category: 'm1',
name: 'main'
},
'7': {
category: 'm2',
name: 'frame7',
parent: '1'
}
}
};
var options = new tv.c.ImportOptions();
options.shiftWorldToZero = false;
var m = new tv.c.TraceModel(eventData, options);
var p = m.processes[1];
var t = p.threads[2];
assertEquals(1, t.samples.length);
assertEquals(0.0, t.samples_[0].start);
assertEquals('frame7', t.samples_[0].leafStackFrame.title);
assertFalse(m.hasImportWarnings);
});
test('importSamplesMissingArgs', function() {
var events = [
{name: 'a', pid: 52, ts: 548, cat: 'test', tid: 53, ph: 'P'},
{name: 'b', pid: 52, ts: 548, cat: 'test', tid: 53, ph: 'P'},
{name: 'c', pid: 52, ts: 549, cat: 'test', tid: 53, ph: 'P'}
];
var m = new tv.c.TraceModel(events);
var p = m.processes[52];
assertNotUndefined(p);
var t = p.threads[53];
assertNotUndefined(t);
assertNotUndefined(t);
assertEquals(3, t.samples_.length);
assertFalse(m.hasImportWarnings);
});
test('importSimpleObject', function() {
var events = [
{ts: 10000, pid: 1, tid: 1, ph: 'N', cat: 'c', id: '0x1000', name: 'a', args: {}}, // @suppress longLineCheck
{ts: 15000, pid: 1, tid: 1, ph: 'O', cat: 'c', id: '0x1000', name: 'a', args: {snapshot: 15}}, // @suppress longLineCheck
{ts: 20000, pid: 1, tid: 1, ph: 'O', cat: 'c', id: '0x1000', name: 'a', args: {snapshot: 20}}, // @suppress longLineCheck
{ts: 50000, pid: 1, tid: 1, ph: 'D', cat: 'c', id: '0x1000', name: 'a', args: {}} // @suppress longLineCheck
];
var m = new tv.c.TraceModel();
m.importTraces([events], false);
assertEquals(10, m.bounds.min);
assertEquals(50, m.bounds.max);
assertFalse(m.hasImportWarnings);
var p = m.processes[1];
assertNotUndefined(p);
var i10 = p.objects.getObjectInstanceAt('0x1000', 10);
assertEquals('c', i10.category);
assertEquals(10, i10.creationTs);
assertEquals(50, i10.deletionTs);
assertEquals(2, i10.snapshots.length);
var s15 = i10.snapshots[0];
assertEquals(15, s15.ts);
assertEquals(15, s15.args);
var s20 = i10.snapshots[1];
assertEquals(20, s20.ts);
assertEquals(20, s20.args);
});
test('importImplicitObjects', function() {
var events = [
{ts: 10000, pid: 1, tid: 1, ph: 'N', cat: 'c', id: '0x1000', name: 'a', args: {}}, // @suppress longLineCheck
{ts: 15000, pid: 1, tid: 1, ph: 'O', cat: 'c', id: '0x1000', name: 'a',
args: { snapshot: [
{ id: 'subObject/0x1',
foo: 1
}
]}},
{ts: 20000, pid: 1, tid: 1, ph: 'O', cat: 'c', id: '0x1000', name: 'a',
args: { snapshot: [
{ id: 'subObject/0x1',
foo: 2
},
{ id: 'subObject/0x2',
foo: 1
}
]}}
];
var m = new tv.c.TraceModel();
m.importTraces([events], false);
var p1 = m.processes[1];
var iA = p1.objects.getObjectInstanceAt('0x1000', 10);
var subObjectInstances = p1.objects.getAllInstancesByTypeName()[
'subObject'];
assertEquals(2, subObjectInstances.length);
var subObject1 = p1.objects.getObjectInstanceAt('0x1', 15);
assertEquals('subObject', subObject1.name);
assertEquals(15, subObject1.creationTs);
assertEquals(2, subObject1.snapshots.length);
assertEquals(15, subObject1.snapshots[0].ts);
assertEquals(1, subObject1.snapshots[0].args.foo);
assertEquals(20, subObject1.snapshots[1].ts);
assertEquals(2, subObject1.snapshots[1].args.foo);
var subObject2 = p1.objects.getObjectInstanceAt('0x2', 20);
assertEquals('subObject', subObject2.name);
assertEquals(20, subObject2.creationTs);
assertEquals(1, subObject2.snapshots.length);
assertEquals(20, subObject2.snapshots[0].ts);
});
test('importImplicitObjectWithCategoryOverride', function() {
var events = [
{ts: 10000, pid: 1, tid: 1, ph: 'N', cat: 'cat', id: '0x1000', name: 'a', args: {}}, // @suppress longLineCheck
{ts: 15000, pid: 1, tid: 1, ph: 'O', cat: 'otherCat', id: '0x1000', name: 'a', // @suppress longLineCheck
args: { snapshot: [
{ id: 'subObject/0x1',
cat: 'cat',
foo: 1
}
]}}
];
var m = new tv.c.TraceModel();
m.importTraces([events], false);
var p1 = m.processes[1];
var iA = p1.objects.getObjectInstanceAt('0x1000', 10);
var subObjectInstances = p1.objects.getAllInstancesByTypeName()[
'subObject'];
assertEquals(1, subObjectInstances.length);
});
test('importImplicitObjectWithBaseTypeOverride', function() {
var events = [
{ts: 10000, pid: 1, tid: 1, ph: 'O', cat: 'c', id: '0x1000', name: 'PictureLayerImpl', args: { // @suppress longLineCheck
snapshot: {
base_type: 'LayerImpl'
}
}},
{ts: 50000, pid: 1, tid: 1, ph: 'D', cat: 'c', id: '0x1000', name: 'LayerImpl', args: {}} // @suppress longLineCheck
];
var m = new tv.c.TraceModel();
m.importTraces([events], false);
var p1 = m.processes[1];
assertEquals(0, m.importWarnings.length);
var iA = p1.objects.getObjectInstanceAt('0x1000', 10);
assertEquals(1, iA.snapshots.length);
});
test('importIDRefs', function() {
var events = [
// An object with two snapshots.
{ts: 10000, pid: 1, tid: 1, ph: 'N', cat: 'c', id: '0x1000', name: 'a', args: {}}, // @suppress longLineCheck
{ts: 15000, pid: 1, tid: 1, ph: 'O', cat: 'c', id: '0x1000', name: 'a', args: {snapshot: 15}}, // @suppress longLineCheck
{ts: 20000, pid: 1, tid: 1, ph: 'O', cat: 'c', id: '0x1000', name: 'a', args: {snapshot: 20}}, // @suppress longLineCheck
{ts: 50000, pid: 1, tid: 1, ph: 'D', cat: 'c', id: '0x1000', name: 'a', args: {}}, // @suppress longLineCheck
// A slice that references the object.
{ts: 17000, pid: 1, tid: 1, ph: 'B', cat: 'c', name: 'taskSlice', args: {my_object: {id_ref: '0x1000'}}}, // @suppress longLineCheck
{ts: 17500, pid: 1, tid: 1, ph: 'E', cat: 'c', name: 'taskSlice', args: {}} // @suppress longLineCheck
];
var m = new tv.c.TraceModel();
m.importTraces([events], false);
var p1 = m.processes[1];
var iA = p1.objects.getObjectInstanceAt('0x1000', 10);
var s15 = iA.getSnapshotAt(15);
var taskSlice = p1.threads[1].sliceGroup.slices[0];
assertEquals(s15, taskSlice.args.my_object);
});
test('importIDRefsThatPointAtEachOther', function() {
var events = [
// An object.
{ts: 10000, pid: 1, tid: 1, ph: 'N', cat: 'c', id: '0x1000', name: 'a', args: {}}, // @suppress longLineCheck
{ts: 15000, pid: 1, tid: 1, ph: 'O', cat: 'c', id: '0x1000', name: 'a', args: { // @suppress longLineCheck
snapshot: { x: {
id: 'foo/0x1001',
value: 'bar'
}}}},
{ts: 50000, pid: 1, tid: 1, ph: 'D', cat: 'c', id: '0x1000', name: 'a', args: {}}, // @suppress longLineCheck
// A slice that references the object.
{ts: 17000, pid: 1, tid: 1, ph: 'B', cat: 'c', name: 'taskSlice', args: {my_object: {id_ref: '0x1001'}}}, // @suppress longLineCheck
{ts: 17500, pid: 1, tid: 1, ph: 'E', cat: 'c', name: 'taskSlice', args: {}} // @suppress longLineCheck
];
var m = new tv.c.TraceModel();
m.importTraces([events], false);
var p1 = m.processes[1];
var iA = p1.objects.getObjectInstanceAt('0x1000', 15);
var iFoo = p1.objects.getObjectInstanceAt('0x1001', 15);
assertNotUndefined(iA);
assertNotUndefined(iFoo);
var a15 = iA.getSnapshotAt(15);
var foo15 = iFoo.getSnapshotAt(15);
var taskSlice = p1.threads[1].sliceGroup.slices[0];
assertEquals(foo15, taskSlice.args.my_object);
});
test('importArrayWithIDs', function() {
var events = [
{ts: 15000, pid: 1, tid: 1, ph: 'O', cat: 'c', id: '0x1000', name: 'a', args: { // @suppress longLineCheck
snapshot: { x: [
{id: 'foo/0x1001', value: 'bar1'},
{id: 'foo/0x1002', value: 'bar2'},
{id: 'foo/0x1003', value: 'bar3'}
]}}}
];
var m = new tv.c.TraceModel();
m.importTraces([events], false);
var p1 = m.processes[1];
var sA = p1.objects.getSnapshotAt('0x1000', 15);
assertTrue(sA.args.x instanceof Array);
assertEquals(3, sA.args.x.length);
assertTrue(sA.args.x[0] instanceof tv.c.trace_model.ObjectSnapshot);
assertTrue(sA.args.x[1] instanceof tv.c.trace_model.ObjectSnapshot);
assertTrue(sA.args.x[2] instanceof tv.c.trace_model.ObjectSnapshot);
});
test('importDoesNotMutateEventList', function() {
var events = [
// An object.
{ts: 10000, pid: 1, tid: 1, ph: 'N', cat: 'c', id: '0x1000', name: 'a', args: {}}, // @suppress longLineCheck
{ts: 15000, pid: 1, tid: 1, ph: 'O', cat: 'c', id: '0x1000', name: 'a', args: { // @suppress longLineCheck
snapshot: {foo: 15}}},
{ts: 50000, pid: 1, tid: 1, ph: 'D', cat: 'c', id: '0x1000', name: 'a', args: {}}, // @suppress longLineCheck
// A slice that references the object.
{ts: 17000, pid: 1, tid: 1, ph: 'B', cat: 'c', name: 'taskSlice', args: {
my_object: {id_ref: '0x1000'}}
},
{ts: 17500, pid: 1, tid: 1, ph: 'E', cat: 'c', name: 'taskSlice', args: {}} // @suppress longLineCheck
];
// The A type family exists to mutate the args list provided to
// snapshots.
function ASnapshot() {
tv.c.trace_model.ObjectSnapshot.apply(this, arguments);
this.args.foo = 7;
}
ASnapshot.prototype = {
__proto__: tv.c.trace_model.ObjectSnapshot.prototype
};
// Import event while the A types are registered, causing the
// arguments of the snapshots to be mutated.
var m = new tv.c.TraceModel();
try {
tv.c.trace_model.ObjectSnapshot.register(ASnapshot, {typeName: 'a'});
m.importTraces([events], false);
} finally {
tv.c.trace_model.ObjectSnapshot.unregister(ASnapshot);
}
assertFalse(m.hasImportWarnings);
// Verify that the events array wasn't modified.
assertObjectEquals(
events[1].args,
{snapshot: {foo: 15}});
assertObjectEquals(
events[3].args,
{my_object: {id_ref: '0x1000'}});
});
test('importFlowEvent', function() {
var events = [
{ name: 'a', cat: 'foo', id: 72, pid: 52, tid: 53, ts: 548, ph: 's', args: {} }, // @suppress longLineCheck
{ name: 'a', cat: 'foo', id: 72, pid: 52, tid: 53, ts: 560, ph: 't', args: {} }, // @suppress longLineCheck
{ name: 'a', cat: 'foo', id: 72, pid: 52, tid: 53, ts: 580, ph: 'f', args: {} } // @suppress longLineCheck
];
var m = new tv.c.TraceModel(events);
var t = m.processes[52].threads[53];
assertNotUndefined(t);
assertEquals(3, t.sliceGroup.slices.length);
assertEquals(2, m.flowEvents.length);
var start = m.flowEvents[0][0];
var step = m.flowEvents[0][1];
var finish = m.flowEvents[1][1];
assertEquals('a', start.title);
assertEquals('foo', start.category);
assertEquals(72, start.id);
assertEquals(0, start.start);
assertEquals(0, start.duration);
assertEquals(start.title, step.title);
assertEquals(start.category, step.category);
assertEquals(start.id, step.id);
assertAlmostEquals(12 / 1000, step.start);
assertEquals(0, step.duration);
assertEquals(start.title, finish.title);
assertEquals(start.category, finish.category);
assertEquals(start.id, finish.id);
assertAlmostEquals((20 + 12) / 1000, finish.start);
assertEquals(0, finish.duration);
assertEquals(2, m.flowIntervalTree.size);
});
test('importOutOfOrderFlowEvent', function() {
var events = [
{ name: 'a', cat: 'foo', id: 72, pid: 52, tid: 53, ts: 548, ph: 's', args: {} }, // @suppress longLineCheck
{ name: 'b', cat: 'foo', id: 73, pid: 52, tid: 53, ts: 148, ph: 's', args: {} }, // @suppress longLineCheck
{ name: 'b', cat: 'foo', id: 73, pid: 52, tid: 53, ts: 570, ph: 'f', args: {} }, // @suppress longLineCheck
{ name: 'a', cat: 'foo', id: 72, pid: 52, tid: 53, ts: 560, ph: 't', args: {} }, // @suppress longLineCheck
{ name: 'a', cat: 'foo', id: 72, pid: 52, tid: 53, ts: 580, ph: 'f', args: {} } // @suppress longLineCheck
];
var expected = [0.4, 0.0, 0.412];
var m = new tv.c.TraceModel(events);
assertEquals(3, m.flowIntervalTree.size);
var order = m.flowEvents.map(function(x) { return x[0].start });
for (var i = 0; i < expected.length; ++i)
assertAlmostEquals(expected[i], order[i]);
});
test('importCompleteEvent', function() {
var events = [
{ name: 'a', args: {}, pid: 52, ts: 629, dur: 1, cat: 'baz', tid: 53, ph: 'X' }, // @suppress longLineCheck
{ name: 'b', args: {}, pid: 52, ts: 730, dur: 20, cat: 'foo', tid: 53, ph: 'X' }, // @suppress longLineCheck
{ name: 'c', args: {}, pid: 52, ts: 740, cat: 'baz', tid: 53, ph: 'X' }
];
var m = new tv.c.TraceModel(events);
assertEquals(1, m.numProcesses);
var p = m.processes[52];
assertNotUndefined(p);
assertEquals(1, p.numThreads);
var t = p.threads[53];
assertNotUndefined(t);
assertEquals(3, t.sliceGroup.slices.length);
assertEquals(53, t.tid);
var slice = t.sliceGroup.slices[0];
assertEquals('a', slice.title);
assertEquals('baz', slice.category);
assertAlmostEquals(0, slice.start);
assertAlmostEquals(1 / 1000, slice.duration);
assertEquals(0, slice.subSlices.length);
slice = t.sliceGroup.slices[1];
assertEquals('b', slice.title);
assertEquals('foo', slice.category);
assertAlmostEquals((730 - 629) / 1000, slice.start);
assertAlmostEquals(20 / 1000, slice.duration);
assertEquals(1, slice.subSlices.length);
slice = t.sliceGroup.slices[2];
assertEquals('c', slice.title);
assertTrue(slice.didNotFinish);
assertAlmostEquals(10 / 1000, slice.duration);
});
test('importCompleteEventWithCpuDuration', function() {
var events = [
{ name: 'a', args: {}, pid: 52, ts: 629, dur: 1, cat: 'baz', tid: 53, ph: 'X', tts: 12, tdur: 1 }, // @suppress longLineCheck
{ name: 'b', args: {}, pid: 52, ts: 730, dur: 20, cat: 'foo', tid: 53, ph: 'X', tts: 110, tdur: 16 }, // @suppress longLineCheck
{ name: 'c', args: {}, pid: 52, ts: 740, cat: 'baz', tid: 53, ph: 'X', tts: 115 } // @suppress longLineCheck
];
var m = new tv.c.TraceModel(events);
assertEquals(1, m.numProcesses);
var p = m.processes[52];
assertNotUndefined(p);
assertEquals(1, p.numThreads);
var t = p.threads[53];
assertNotUndefined(t);
assertEquals(3, t.sliceGroup.slices.length);
assertEquals(53, t.tid);
var slice = t.sliceGroup.slices[0];
assertEquals('a', slice.title);
assertEquals('baz', slice.category);
assertAlmostEquals(0, slice.start);
assertAlmostEquals(1 / 1000, slice.duration);
assertAlmostEquals(12 / 1000, slice.cpuStart);
assertAlmostEquals(1 / 1000, slice.cpuDuration);
assertEquals(0, slice.subSlices.length);
slice = t.sliceGroup.slices[1];
assertEquals('b', slice.title);
assertEquals('foo', slice.category);
assertAlmostEquals((730 - 629) / 1000, slice.start);
assertAlmostEquals(20 / 1000, slice.duration);
assertAlmostEquals(110 / 1000, slice.cpuStart);
assertAlmostEquals(16 / 1000, slice.cpuDuration);
assertEquals(1, slice.subSlices.length);
slice = t.sliceGroup.slices[2];
assertEquals('c', slice.title);
assertTrue(slice.didNotFinish);
assertAlmostEquals(10 / 1000, slice.duration);
});
test('importNestedCompleteEventWithTightBounds', function() {
var events = [
{ name: 'a', args: {}, pid: 52, ts: 244654227065, dur: 36075, cat: 'baz', tid: 53, ph: 'X' }, // @suppress longLineCheck
{ name: 'b', args: {}, pid: 52, ts: 244654227095, dur: 36045, cat: 'foo', tid: 53, ph: 'X' } // @suppress longLineCheck
];
var m = new tv.c.TraceModel(events, false);
var t = m.processes[52].threads[53];
var sA = findSliceNamed(t.sliceGroup, 'a');
var sB = findSliceNamed(t.sliceGroup, 'b');
assertEquals('a', sA.title);
assertEquals('baz', sA.category);
assertEquals(244654227.065, sA.start);
assertEquals(36.075, sA.duration);
assertAlmostEquals(0.03, sA.selfTime);
assertEquals('b', sB.title);
assertEquals('foo', sB.category);
assertEquals(244654227.095, sB.start);
assertEquals(36.045, sB.duration);
assertTrue(sA.subSlices.length == 1);
assertTrue(sA.subSlices[0] == sB);
assertTrue(sB.parentSlice == sA);
});
test('importCompleteEventWithStackFrame', function() {
var eventData = {
traceEvents: [
{ name: 'a', args: {}, pid: 1, ts: 0, dur: 1, cat: 'baz', tid: 2, ph: 'X', sf: 7 }, // @suppress longLineCheck
{ name: 'b', args: {}, pid: 1, ts: 5, dur: 1, cat: 'baz', tid: 2, ph: 'X', sf: 8, esf: 9 } // @suppress longLineCheck
],
stackFrames: {
'1': {
category: 'm1',
name: 'main'
},
'7': {
category: 'm2',
name: 'frame7',
parent: '1'
},
'8': {
category: 'm2',
name: 'frame8',
parent: '1'
},
'9': {
category: 'm2',
name: 'frame9',
parent: '1'
}
}
};
var options = new tv.c.ImportOptions();
options.shiftWorldToZero = false;
var m = new tv.c.TraceModel(eventData, options);
var p = m.processes[1];
var t = p.threads[2];
assertNotUndefined(t);
assertEquals(2, t.sliceGroup.slices.length);
var s0 = t.sliceGroup.slices[0];
assertEquals('frame7', s0.startStackFrame.title);
assertUndefined(s0.endStackFrame);
var s1 = t.sliceGroup.slices[1];
assertEquals('frame8', s1.startStackFrame.title);
assertEquals('frame9', s1.endStackFrame.title);
});
test('importAsyncEventWithSameTimestamp', function() {
var events = [];
// Events are added with ts 0, 1, 1, 2, 2, 3, 3 ...500, 500, 1000
// and use 'seq' to track the order of when the event is recorded.
events.push({name: 'a', cat: 'foo', id: 72, pid: 52, tid: 53, ts: 0, ph: 'S', args: {'seq': 0}}); // @suppress longLineCheck
for (var i = 1; i <= 1000; i++)
events.push({name: 'a', cat: 'foo', id: 72, pid: 52, tid: 53, ts: Math.round(i / 2) , ph: 'T', args: {'seq': i}}); // @suppress longLineCheck
events.push({name: 'a', cat: 'foo', id: 72, pid: 52, tid: 53, ts: 1000, ph: 'F', args: {'seq': 1001}}); // @suppress longLineCheck
var m = new tv.c.TraceModel(events);
var t = m.processes[52].threads[53];
assertEquals(1, t.asyncSliceGroup.slices.length);
var parentSlice = t.asyncSliceGroup.slices[0];
assertEquals('a', parentSlice.title);
assertEquals('foo', parentSlice.category);
assertNotUndefined(parentSlice.subSlices);
var subSlices = parentSlice.subSlices;
assertEquals(1000, subSlices.length);
// Slices should be sorted according to 'ts'. And if 'ts' is the same,
// slices should keep the order that they were recorded.
for (var i = 0; i < 1000; i++)
assertEquals(subSlices[i].args['seq'], i + 1);
});
test('sampleDataSimple', function() {
var events = {
'traceEvents': [],
'stackFrames': {
'1': {
'category': 'mod',
'name': 'main'
},
'2': {
'category': 'mod',
'name': 'a',
'parent': 1
},
'3': {
'category': 'mod',
'name': 'a_sub',
'parent': 2
},
'4': {
'category': 'mod',
'name': 'b',
'parent': 1
}
},
'samples': [
{
'cpu': 0, 'tid': 1, 'ts': 1000.0,
'name': 'cycles:HG', 'sf': 3, 'weight': 1
},
{
'cpu': 0, 'tid': 1, 'ts': 2000.0,
'name': 'cycles:HG', 'sf': 2, 'weight': 1
},
{
'cpu': 1, 'tid': 1, 'ts': 3000.0,
'name': 'cycles:HG', 'sf': 3, 'weight': 1
}
]
};
var m = new tv.c.TraceModel(events, false);
assertNotUndefined(m.kernel.cpus[0]);
assertEquals(1, m.getAllThreads().length);
assertEquals(4, tv.b.dictionaryKeys(m.stackFrames).length);
assertEquals(3, m.samples.length);
var t1 = m.processes[1].threads[1];
assertEquals(3, t1.samples.length);
var c0 = m.kernel.cpus[0];
var c1 = m.kernel.cpus[1];
assertEquals(2, c0.samples.length);
assertEquals(1, c1.samples.length);
assertEquals(c0, m.samples[0].cpu);
assertEquals(t1, m.samples[0].thread);
assertEquals('cycles:HG', m.samples[0].title);
assertEquals(1, m.samples[0].start);
assertArrayEquals(
['main', 'a', 'a_sub'],
m.samples[0].stackTrace.map(function(x) { return x.title; }));
assertEquals(1, m.samples[0].weight);
});
test('importMemoryDumps', function() {
var events = [
// 2 process memory dump events without a global memory dump event.
{name: 'a', pid: 42, ts: 10, cat: 'test', tid: 53, ph: 'v', 'id': 'dump1', args: {}}, // @suppress longLineCheck
{name: 'b', pid: 43, ts: 11, cat: 'test', tid: 54, ph: 'v', 'id': 'dump1', args: {}}, // @suppress longLineCheck
// 1 global memory dump event and 1 process memory dump event.
{name: 'c', pid: 44, ts: 12, cat: 'test', tid: 55, ph: 'V', 'id': 'dump2', args: {}}, // @suppress longLineCheck
{name: 'd', pid: 42, ts: 13, cat: 'test', tid: 56, ph: 'v', 'id': 'dump2', args: {}} // @suppress longLineCheck
];
var m = new tv.c.TraceModel(events, false);
var p1 = m.getProcess(42);
var p2 = m.getProcess(43);
assertNotUndefined(p1);
assertNotUndefined(p2);
// Check that TraceModel and Process objects contain the right dumps.
assertEquals(2, m.globalMemoryDumps.length);
assertEquals(2, p1.memoryDumps.length);
assertEquals(1, p2.memoryDumps.length);
assertEquals(10, m.globalMemoryDumps[0].start);
assertEquals(10, p1.memoryDumps[0].start);
assertEquals(11, p2.memoryDumps[0].start);
assertEquals(1, m.globalMemoryDumps[0].duration);
assertEquals(0, p1.memoryDumps[0].duration);
assertEquals(0, p2.memoryDumps[0].duration);
assertEquals(12, m.globalMemoryDumps[1].start);
assertEquals(13, p1.memoryDumps[1].start);
assertEquals(1, m.globalMemoryDumps[1].duration);
assertEquals(0, p1.memoryDumps[1].duration);
// Check that GlobalMemoryDump and ProcessMemoryDump objects are
// interconnected correctly.
assertEquals(m.globalMemoryDumps[0].processMemoryDumps[42],
p1.memoryDumps[0]);
assertEquals(m.globalMemoryDumps[0].processMemoryDumps[43],
p2.memoryDumps[0]);
assertEquals(m.globalMemoryDumps[0], p1.memoryDumps[0].globalMemoryDump);
assertEquals(m.globalMemoryDumps[0], p2.memoryDumps[0].globalMemoryDump);
assertEquals(m.globalMemoryDumps[1].processMemoryDumps[42],
p1.memoryDumps[1]);
assertEquals(m.globalMemoryDumps[1], p1.memoryDumps[1].globalMemoryDump);
});
test('importThreadInstantSliceWithStackFrame', function() {
var eventData = {
traceEvents: [
{ name: 'a', args: {}, pid: 1, ts: 0, cat: 'baz', tid: 2, ph: 'I', s: 't', sf: 7 } // @suppress longLineCheck
],
stackFrames: {
'1': {
category: 'm1',
name: 'main'
},
'7': {
category: 'm2',
name: 'frame7',
parent: '1'
}
}
};
var options = new tv.c.ImportOptions();
options.shiftWorldToZero = false;
var m = new tv.c.TraceModel(eventData, options);
var p = m.processes[1];
var t = p.threads[2];
assertNotUndefined(t);
assertEquals(1, t.sliceGroup.slices.length);
var s0 = t.sliceGroup.slices[0];
assertEquals('frame7', s0.startStackFrame.title);
assertUndefined(s0.endStackFrame);
});
test('importDurationEventsWithStackFrames', function() {
var eventData = {
traceEvents: [
{ name: 'a', args: {}, pid: 1, ts: 0, cat: 'baz', tid: 2, ph: 'B', sf: 7 }, // @suppress longLineCheck
{ name: 'b', args: {}, pid: 1, ts: 5, cat: 'baz', tid: 2, ph: 'E', sf: 8 } // @suppress longLineCheck
],
stackFrames: {
'1': {
category: 'm1',
name: 'main'
},
'7': {
category: 'm2',
name: 'frame7',
parent: '1'
},
'8': {
category: 'm2',
name: 'frame8',
parent: '1'
}
}
};
var options = new tv.c.ImportOptions();
options.shiftWorldToZero = false;
var m = new tv.c.TraceModel(eventData, options);
var p = m.processes[1];
var t = p.threads[2];
assertNotUndefined(t);
assertEquals(1, t.sliceGroup.slices.length);
var s0 = t.sliceGroup.slices[0];
assertEquals('frame7', s0.startStackFrame.title);
assertEquals('frame8', s0.endStackFrame.title);
});
// TODO(nduca): one slice, two threads
// TODO(nduca): one slice, two pids
});
</script>