11cb0ef41Sopenharmony_ci// Copyright 2020 the V8 project authors. All rights reserved. 21cb0ef41Sopenharmony_ci// Use of this source code is governed by a BSD-style license that can be 31cb0ef41Sopenharmony_ci// found in the LICENSE file. 41cb0ef41Sopenharmony_ci 51cb0ef41Sopenharmony_ciimport {groupBy} from './helper.mjs' 61cb0ef41Sopenharmony_ci 71cb0ef41Sopenharmony_ciclass Timeline { 81cb0ef41Sopenharmony_ci // Class: 91cb0ef41Sopenharmony_ci _model; 101cb0ef41Sopenharmony_ci // Array of #model instances: 111cb0ef41Sopenharmony_ci _values; 121cb0ef41Sopenharmony_ci // Current selection, subset of #values: 131cb0ef41Sopenharmony_ci _selection; 141cb0ef41Sopenharmony_ci _breakdown; 151cb0ef41Sopenharmony_ci 161cb0ef41Sopenharmony_ci constructor(model, values = [], startTime = null, endTime = null) { 171cb0ef41Sopenharmony_ci this._model = model; 181cb0ef41Sopenharmony_ci this._values = values; 191cb0ef41Sopenharmony_ci this.startTime = startTime; 201cb0ef41Sopenharmony_ci this.endTime = endTime; 211cb0ef41Sopenharmony_ci if (values.length > 0) { 221cb0ef41Sopenharmony_ci if (startTime === null) this.startTime = values[0].time; 231cb0ef41Sopenharmony_ci if (endTime === null) this.endTime = values[values.length - 1].time; 241cb0ef41Sopenharmony_ci } else { 251cb0ef41Sopenharmony_ci if (startTime === null) this.startTime = 0; 261cb0ef41Sopenharmony_ci if (endTime === null) this.endTime = 0; 271cb0ef41Sopenharmony_ci } 281cb0ef41Sopenharmony_ci } 291cb0ef41Sopenharmony_ci 301cb0ef41Sopenharmony_ci get model() { 311cb0ef41Sopenharmony_ci return this._model; 321cb0ef41Sopenharmony_ci } 331cb0ef41Sopenharmony_ci 341cb0ef41Sopenharmony_ci get all() { 351cb0ef41Sopenharmony_ci return this._values; 361cb0ef41Sopenharmony_ci } 371cb0ef41Sopenharmony_ci 381cb0ef41Sopenharmony_ci get selection() { 391cb0ef41Sopenharmony_ci return this._selection; 401cb0ef41Sopenharmony_ci } 411cb0ef41Sopenharmony_ci 421cb0ef41Sopenharmony_ci get selectionOrSelf() { 431cb0ef41Sopenharmony_ci return this._selection ?? this; 441cb0ef41Sopenharmony_ci } 451cb0ef41Sopenharmony_ci 461cb0ef41Sopenharmony_ci set selection(value) { 471cb0ef41Sopenharmony_ci this._selection = value; 481cb0ef41Sopenharmony_ci } 491cb0ef41Sopenharmony_ci 501cb0ef41Sopenharmony_ci selectTimeRange(startTime, endTime) { 511cb0ef41Sopenharmony_ci const items = this.range(startTime, endTime); 521cb0ef41Sopenharmony_ci this._selection = new Timeline(this._model, items, startTime, endTime); 531cb0ef41Sopenharmony_ci } 541cb0ef41Sopenharmony_ci 551cb0ef41Sopenharmony_ci clearSelection() { 561cb0ef41Sopenharmony_ci this._selection = undefined; 571cb0ef41Sopenharmony_ci } 581cb0ef41Sopenharmony_ci 591cb0ef41Sopenharmony_ci getChunks(windowSizeMs) { 601cb0ef41Sopenharmony_ci return this.chunkSizes(windowSizeMs); 611cb0ef41Sopenharmony_ci } 621cb0ef41Sopenharmony_ci 631cb0ef41Sopenharmony_ci get values() { 641cb0ef41Sopenharmony_ci return this._values; 651cb0ef41Sopenharmony_ci } 661cb0ef41Sopenharmony_ci 671cb0ef41Sopenharmony_ci count(filter) { 681cb0ef41Sopenharmony_ci return this.all.reduce((sum, each) => { 691cb0ef41Sopenharmony_ci return sum + (filter(each) === true ? 1 : 0); 701cb0ef41Sopenharmony_ci }, 0); 711cb0ef41Sopenharmony_ci } 721cb0ef41Sopenharmony_ci 731cb0ef41Sopenharmony_ci filter(predicate) { 741cb0ef41Sopenharmony_ci return this.all.filter(predicate); 751cb0ef41Sopenharmony_ci } 761cb0ef41Sopenharmony_ci 771cb0ef41Sopenharmony_ci push(event) { 781cb0ef41Sopenharmony_ci let time = event.time; 791cb0ef41Sopenharmony_ci if (!this.isEmpty() && this.last().time > time) { 801cb0ef41Sopenharmony_ci // Invalid insertion order, might happen without --single-process, 811cb0ef41Sopenharmony_ci // finding insertion point. 821cb0ef41Sopenharmony_ci let insertionPoint = this.find(time); 831cb0ef41Sopenharmony_ci this._values.splice(insertionPoint, event); 841cb0ef41Sopenharmony_ci } else { 851cb0ef41Sopenharmony_ci this._values.push(event); 861cb0ef41Sopenharmony_ci } 871cb0ef41Sopenharmony_ci if (time > 0) { 881cb0ef41Sopenharmony_ci this.endTime = Math.max(this.endTime, time); 891cb0ef41Sopenharmony_ci if (this.startTime === 0) { 901cb0ef41Sopenharmony_ci this.startTime = time; 911cb0ef41Sopenharmony_ci } else { 921cb0ef41Sopenharmony_ci this.startTime = Math.min(this.startTime, time); 931cb0ef41Sopenharmony_ci } 941cb0ef41Sopenharmony_ci } 951cb0ef41Sopenharmony_ci } 961cb0ef41Sopenharmony_ci 971cb0ef41Sopenharmony_ci at(index) { 981cb0ef41Sopenharmony_ci return this._values[index]; 991cb0ef41Sopenharmony_ci } 1001cb0ef41Sopenharmony_ci 1011cb0ef41Sopenharmony_ci isEmpty() { 1021cb0ef41Sopenharmony_ci return this.size() === 0; 1031cb0ef41Sopenharmony_ci } 1041cb0ef41Sopenharmony_ci 1051cb0ef41Sopenharmony_ci size() { 1061cb0ef41Sopenharmony_ci return this._values.length; 1071cb0ef41Sopenharmony_ci } 1081cb0ef41Sopenharmony_ci 1091cb0ef41Sopenharmony_ci get length() { 1101cb0ef41Sopenharmony_ci return this._values.length; 1111cb0ef41Sopenharmony_ci } 1121cb0ef41Sopenharmony_ci 1131cb0ef41Sopenharmony_ci slice(startIndex, endIndex) { 1141cb0ef41Sopenharmony_ci return this._values.slice(startIndex, endIndex); 1151cb0ef41Sopenharmony_ci } 1161cb0ef41Sopenharmony_ci 1171cb0ef41Sopenharmony_ci first() { 1181cb0ef41Sopenharmony_ci return this._values[0]; 1191cb0ef41Sopenharmony_ci } 1201cb0ef41Sopenharmony_ci 1211cb0ef41Sopenharmony_ci last() { 1221cb0ef41Sopenharmony_ci return this._values[this._values.length - 1]; 1231cb0ef41Sopenharmony_ci } 1241cb0ef41Sopenharmony_ci 1251cb0ef41Sopenharmony_ci * [Symbol.iterator]() { 1261cb0ef41Sopenharmony_ci yield* this._values; 1271cb0ef41Sopenharmony_ci } 1281cb0ef41Sopenharmony_ci 1291cb0ef41Sopenharmony_ci duration() { 1301cb0ef41Sopenharmony_ci return this.endTime - this.startTime; 1311cb0ef41Sopenharmony_ci } 1321cb0ef41Sopenharmony_ci 1331cb0ef41Sopenharmony_ci forEachChunkSize(count, fn) { 1341cb0ef41Sopenharmony_ci if (this.isEmpty()) return; 1351cb0ef41Sopenharmony_ci const increment = this.duration() / count; 1361cb0ef41Sopenharmony_ci let currentTime = this.startTime; 1371cb0ef41Sopenharmony_ci let index = 0; 1381cb0ef41Sopenharmony_ci for (let i = 0; i < count - 1; i++) { 1391cb0ef41Sopenharmony_ci const nextTime = currentTime + increment; 1401cb0ef41Sopenharmony_ci const nextIndex = this.findLast(nextTime, index); 1411cb0ef41Sopenharmony_ci fn(index, nextIndex, currentTime, nextTime); 1421cb0ef41Sopenharmony_ci index = nextIndex + 1; 1431cb0ef41Sopenharmony_ci currentTime = nextTime; 1441cb0ef41Sopenharmony_ci } 1451cb0ef41Sopenharmony_ci fn(index, this._values.length - 1, currentTime, this.endTime); 1461cb0ef41Sopenharmony_ci } 1471cb0ef41Sopenharmony_ci 1481cb0ef41Sopenharmony_ci chunkSizes(count) { 1491cb0ef41Sopenharmony_ci const chunks = []; 1501cb0ef41Sopenharmony_ci this.forEachChunkSize(count, (start, end) => chunks.push(end - start)); 1511cb0ef41Sopenharmony_ci return chunks; 1521cb0ef41Sopenharmony_ci } 1531cb0ef41Sopenharmony_ci 1541cb0ef41Sopenharmony_ci chunks(count, predicate = undefined) { 1551cb0ef41Sopenharmony_ci const chunks = []; 1561cb0ef41Sopenharmony_ci this.forEachChunkSize(count, (start, end, startTime, endTime) => { 1571cb0ef41Sopenharmony_ci let items = this._values.slice(start, end + 1); 1581cb0ef41Sopenharmony_ci if (predicate !== undefined) items = items.filter(predicate); 1591cb0ef41Sopenharmony_ci chunks.push(new Chunk(chunks.length, startTime, endTime, items)); 1601cb0ef41Sopenharmony_ci }); 1611cb0ef41Sopenharmony_ci return chunks; 1621cb0ef41Sopenharmony_ci } 1631cb0ef41Sopenharmony_ci 1641cb0ef41Sopenharmony_ci // Return all entries in ({startTime}, {endTime}] 1651cb0ef41Sopenharmony_ci range(startTime, endTime) { 1661cb0ef41Sopenharmony_ci const firstIndex = this.find(startTime); 1671cb0ef41Sopenharmony_ci if (firstIndex < 0) return []; 1681cb0ef41Sopenharmony_ci const lastIndex = this.find(endTime, firstIndex + 1); 1691cb0ef41Sopenharmony_ci return this._values.slice(firstIndex, lastIndex); 1701cb0ef41Sopenharmony_ci } 1711cb0ef41Sopenharmony_ci 1721cb0ef41Sopenharmony_ci // Return the first index with element.time >= time. 1731cb0ef41Sopenharmony_ci find(time, offset = 0) { 1741cb0ef41Sopenharmony_ci return this.findFirst(time, offset); 1751cb0ef41Sopenharmony_ci } 1761cb0ef41Sopenharmony_ci 1771cb0ef41Sopenharmony_ci findFirst(time, offset = 0) { 1781cb0ef41Sopenharmony_ci return this._find(this._values, each => each.time - time, offset); 1791cb0ef41Sopenharmony_ci } 1801cb0ef41Sopenharmony_ci 1811cb0ef41Sopenharmony_ci // Return the last index with element.time <= time. 1821cb0ef41Sopenharmony_ci findLast(time, offset = 0) { 1831cb0ef41Sopenharmony_ci const nextTime = time + 1; 1841cb0ef41Sopenharmony_ci let index = (this.last().time <= nextTime) ? 1851cb0ef41Sopenharmony_ci this.length : 1861cb0ef41Sopenharmony_ci this.findFirst(nextTime + 1, offset); 1871cb0ef41Sopenharmony_ci // Typically we just have to look at the previous element. 1881cb0ef41Sopenharmony_ci while (index > 0) { 1891cb0ef41Sopenharmony_ci index--; 1901cb0ef41Sopenharmony_ci if (this._values[index].time <= time) return index; 1911cb0ef41Sopenharmony_ci } 1921cb0ef41Sopenharmony_ci return -1; 1931cb0ef41Sopenharmony_ci } 1941cb0ef41Sopenharmony_ci 1951cb0ef41Sopenharmony_ci // Return the first index for which compareFn(item) is >= 0; 1961cb0ef41Sopenharmony_ci _find(array, compareFn, offset = 0) { 1971cb0ef41Sopenharmony_ci let minIndex = offset; 1981cb0ef41Sopenharmony_ci let maxIndex = array.length - 1; 1991cb0ef41Sopenharmony_ci while (minIndex < maxIndex) { 2001cb0ef41Sopenharmony_ci const midIndex = minIndex + (((maxIndex - minIndex) / 2) | 0); 2011cb0ef41Sopenharmony_ci if (compareFn(array[midIndex]) < 0) { 2021cb0ef41Sopenharmony_ci minIndex = midIndex + 1; 2031cb0ef41Sopenharmony_ci } else { 2041cb0ef41Sopenharmony_ci maxIndex = midIndex; 2051cb0ef41Sopenharmony_ci } 2061cb0ef41Sopenharmony_ci } 2071cb0ef41Sopenharmony_ci return minIndex; 2081cb0ef41Sopenharmony_ci } 2091cb0ef41Sopenharmony_ci 2101cb0ef41Sopenharmony_ci getBreakdown(keyFunction, collect = false) { 2111cb0ef41Sopenharmony_ci if (keyFunction || collect) { 2121cb0ef41Sopenharmony_ci if (!keyFunction) { 2131cb0ef41Sopenharmony_ci keyFunction = each => each.type; 2141cb0ef41Sopenharmony_ci } 2151cb0ef41Sopenharmony_ci return groupBy(this._values, keyFunction, collect); 2161cb0ef41Sopenharmony_ci } 2171cb0ef41Sopenharmony_ci if (this._breakdown === undefined) { 2181cb0ef41Sopenharmony_ci this._breakdown = groupBy(this._values, each => each.type); 2191cb0ef41Sopenharmony_ci } 2201cb0ef41Sopenharmony_ci return this._breakdown; 2211cb0ef41Sopenharmony_ci } 2221cb0ef41Sopenharmony_ci 2231cb0ef41Sopenharmony_ci depthHistogram() { 2241cb0ef41Sopenharmony_ci return this._values.histogram(each => each.depth); 2251cb0ef41Sopenharmony_ci } 2261cb0ef41Sopenharmony_ci 2271cb0ef41Sopenharmony_ci fanOutHistogram() { 2281cb0ef41Sopenharmony_ci return this._values.histogram(each => each.children.length); 2291cb0ef41Sopenharmony_ci } 2301cb0ef41Sopenharmony_ci 2311cb0ef41Sopenharmony_ci forEach(fn) { 2321cb0ef41Sopenharmony_ci return this._values.forEach(fn); 2331cb0ef41Sopenharmony_ci } 2341cb0ef41Sopenharmony_ci} 2351cb0ef41Sopenharmony_ci 2361cb0ef41Sopenharmony_ci// =========================================================================== 2371cb0ef41Sopenharmony_ciclass Chunk { 2381cb0ef41Sopenharmony_ci constructor(index, start, end, items) { 2391cb0ef41Sopenharmony_ci this.index = index; 2401cb0ef41Sopenharmony_ci this.start = start; 2411cb0ef41Sopenharmony_ci this.end = end; 2421cb0ef41Sopenharmony_ci this.items = items; 2431cb0ef41Sopenharmony_ci this.height = 0; 2441cb0ef41Sopenharmony_ci } 2451cb0ef41Sopenharmony_ci 2461cb0ef41Sopenharmony_ci isEmpty() { 2471cb0ef41Sopenharmony_ci return this.items.length === 0; 2481cb0ef41Sopenharmony_ci } 2491cb0ef41Sopenharmony_ci 2501cb0ef41Sopenharmony_ci last() { 2511cb0ef41Sopenharmony_ci return this.at(this.size() - 1); 2521cb0ef41Sopenharmony_ci } 2531cb0ef41Sopenharmony_ci 2541cb0ef41Sopenharmony_ci first() { 2551cb0ef41Sopenharmony_ci return this.at(0); 2561cb0ef41Sopenharmony_ci } 2571cb0ef41Sopenharmony_ci 2581cb0ef41Sopenharmony_ci at(index) { 2591cb0ef41Sopenharmony_ci return this.items[index]; 2601cb0ef41Sopenharmony_ci } 2611cb0ef41Sopenharmony_ci 2621cb0ef41Sopenharmony_ci size() { 2631cb0ef41Sopenharmony_ci return this.items.length; 2641cb0ef41Sopenharmony_ci } 2651cb0ef41Sopenharmony_ci 2661cb0ef41Sopenharmony_ci get length() { 2671cb0ef41Sopenharmony_ci return this.items.length; 2681cb0ef41Sopenharmony_ci } 2691cb0ef41Sopenharmony_ci 2701cb0ef41Sopenharmony_ci yOffset(event) { 2711cb0ef41Sopenharmony_ci // items[0] == oldest event, displayed at the top of the chunk 2721cb0ef41Sopenharmony_ci // items[n-1] == youngest event, displayed at the bottom of the chunk 2731cb0ef41Sopenharmony_ci return ((this.indexOf(event) + 0.5) / this.size()) * this.height; 2741cb0ef41Sopenharmony_ci } 2751cb0ef41Sopenharmony_ci 2761cb0ef41Sopenharmony_ci indexOf(event) { 2771cb0ef41Sopenharmony_ci return this.items.indexOf(event); 2781cb0ef41Sopenharmony_ci } 2791cb0ef41Sopenharmony_ci 2801cb0ef41Sopenharmony_ci has(event) { 2811cb0ef41Sopenharmony_ci if (this.isEmpty()) return false; 2821cb0ef41Sopenharmony_ci return this.first().time <= event.time && event.time <= this.last().time; 2831cb0ef41Sopenharmony_ci } 2841cb0ef41Sopenharmony_ci 2851cb0ef41Sopenharmony_ci next(chunks) { 2861cb0ef41Sopenharmony_ci return this.findChunk(chunks, 1); 2871cb0ef41Sopenharmony_ci } 2881cb0ef41Sopenharmony_ci 2891cb0ef41Sopenharmony_ci prev(chunks) { 2901cb0ef41Sopenharmony_ci return this.findChunk(chunks, -1); 2911cb0ef41Sopenharmony_ci } 2921cb0ef41Sopenharmony_ci 2931cb0ef41Sopenharmony_ci findChunk(chunks, delta) { 2941cb0ef41Sopenharmony_ci let i = this.index + delta; 2951cb0ef41Sopenharmony_ci let chunk = chunks[i]; 2961cb0ef41Sopenharmony_ci while (chunk && chunk.size() === 0) { 2971cb0ef41Sopenharmony_ci i += delta; 2981cb0ef41Sopenharmony_ci chunk = chunks[i]; 2991cb0ef41Sopenharmony_ci } 3001cb0ef41Sopenharmony_ci return chunk; 3011cb0ef41Sopenharmony_ci } 3021cb0ef41Sopenharmony_ci 3031cb0ef41Sopenharmony_ci getBreakdown(keyFunction) { 3041cb0ef41Sopenharmony_ci return groupBy(this.items, keyFunction); 3051cb0ef41Sopenharmony_ci } 3061cb0ef41Sopenharmony_ci 3071cb0ef41Sopenharmony_ci filter() { 3081cb0ef41Sopenharmony_ci return this.items.filter(map => !map.parent || !this.has(map.parent)); 3091cb0ef41Sopenharmony_ci } 3101cb0ef41Sopenharmony_ci} 3111cb0ef41Sopenharmony_ci 3121cb0ef41Sopenharmony_ciexport {Timeline, Chunk}; 313