1/*
2 * Copyright 2014 The Chromium Authors. All rights reserved.
3 * Use of this source code is governed by a BSD-style license that can be
4 * found in the LICENSE file.
5 */
6
7/**
8 * @constructor
9 */
10WebInspector.TracingModel = function()
11{
12    this.reset();
13}
14
15/**
16 * @enum {string}
17 */
18WebInspector.TracingModel.Phase = {
19    Begin: "B",
20    End: "E",
21    Complete: "X",
22    Instant: "I",
23    AsyncBegin: "S",
24    AsyncStepInto: "T",
25    AsyncStepPast: "p",
26    AsyncEnd: "F",
27    FlowBegin: "s",
28    FlowStep: "t",
29    FlowEnd: "f",
30    Metadata: "M",
31    Counter: "C",
32    Sample: "P",
33    CreateObject: "N",
34    SnapshotObject: "O",
35    DeleteObject: "D"
36};
37
38WebInspector.TracingModel.MetadataEvent = {
39    ProcessSortIndex: "process_sort_index",
40    ProcessName: "process_name",
41    ThreadSortIndex: "thread_sort_index",
42    ThreadName: "thread_name"
43}
44
45WebInspector.TracingModel.DevToolsMetadataEventCategory = "disabled-by-default-devtools.timeline";
46
47WebInspector.TracingModel.ConsoleEventCategory = "blink.console";
48
49WebInspector.TracingModel.FrameLifecycleEventCategory = "cc,devtools";
50
51WebInspector.TracingModel.DevToolsMetadataEvent = {
52    TracingStartedInPage: "TracingStartedInPage",
53    TracingSessionIdForWorker: "TracingSessionIdForWorker",
54};
55
56/**
57 * @param {string} phase
58 * @return {boolean}
59 */
60WebInspector.TracingModel.isAsyncPhase = function(phase)
61{
62    return phase === WebInspector.TracingModel.Phase.AsyncBegin || phase === WebInspector.TracingModel.Phase.AsyncEnd ||
63        phase === WebInspector.TracingModel.Phase.AsyncStepInto || phase === WebInspector.TracingModel.Phase.AsyncStepPast;
64}
65
66WebInspector.TracingModel.prototype = {
67    /**
68     * @return {!Array.<!WebInspector.TracingModel.Event>}
69     */
70    devtoolsPageMetadataEvents: function()
71    {
72        return this._devtoolsPageMetadataEvents;
73    },
74
75    /**
76     * @return {!Array.<!WebInspector.TracingModel.Event>}
77     */
78    devtoolsWorkerMetadataEvents: function()
79    {
80        return this._devtoolsWorkerMetadataEvents;
81    },
82
83    /**
84     * @return {?string}
85     */
86    sessionId: function()
87    {
88        return this._sessionId;
89    },
90
91    /**
92     * @param {!Array.<!WebInspector.TracingManager.EventPayload>} events
93     */
94    setEventsForTest: function(events)
95    {
96        this.reset();
97        this.addEvents(events);
98        this.tracingComplete();
99    },
100
101    /**
102     * @param {!Array.<!WebInspector.TracingManager.EventPayload>} events
103     */
104    addEvents: function(events)
105    {
106        for (var i = 0; i < events.length; ++i)
107            this._addEvent(events[i]);
108    },
109
110    tracingComplete: function()
111    {
112        this._processMetadataEvents();
113        for (var process in this._processById)
114            this._processById[process]._tracingComplete(this._maximumRecordTime);
115        this._backingStorage.finishWriting(function() {});
116    },
117
118    reset: function()
119    {
120        this._processById = {};
121        this._minimumRecordTime = 0;
122        this._maximumRecordTime = 0;
123        this._sessionId = null;
124        this._devtoolsPageMetadataEvents = [];
125        this._devtoolsWorkerMetadataEvents = [];
126        if (this._backingStorage)
127            this._backingStorage.remove();
128        this._backingStorage = new WebInspector.DeferredTempFile("tracing", String(Date.now()));
129        this._storageOffset = 0;
130    },
131
132    /**
133     * @param {!WebInspector.OutputStream} outputStream
134     * @param {!WebInspector.OutputStreamDelegate} delegate
135     */
136    writeToStream: function(outputStream, delegate)
137    {
138        this._backingStorage.writeToOutputStream(outputStream, delegate);
139    },
140
141    /**
142      * @param {!WebInspector.TracingManager.EventPayload} payload
143      */
144    _addEvent: function(payload)
145    {
146        var process = this._processById[payload.pid];
147        if (!process) {
148            process = new WebInspector.TracingModel.Process(payload.pid);
149            this._processById[payload.pid] = process;
150        }
151
152        var stringPayload = JSON.stringify(payload);
153        var startOffset = this._storageOffset;
154        if (startOffset) {
155            var recordDelimiter = ",\n";
156            stringPayload = recordDelimiter + stringPayload;
157            startOffset += recordDelimiter.length;
158        }
159        this._storageOffset += stringPayload.length;
160        this._backingStorage.write([stringPayload]);
161
162        if (payload.ph !== WebInspector.TracingModel.Phase.Metadata) {
163            var timestamp = payload.ts / 1000;
164            // We do allow records for unrelated threads to arrive out-of-order,
165            // so there's a chance we're getting records from the past.
166            if (timestamp && (!this._minimumRecordTime || timestamp < this._minimumRecordTime))
167                this._minimumRecordTime = timestamp;
168            var endTimeStamp = (payload.ts + (payload.dur || 0)) / 1000;
169            this._maximumRecordTime = Math.max(this._maximumRecordTime, endTimeStamp);
170            var event = process._addEvent(payload);
171            if (!event)
172                return;
173            event._setBackingStorage(this._backingStorage, startOffset, this._storageOffset);
174            if (event.name === WebInspector.TracingModel.DevToolsMetadataEvent.TracingStartedInPage &&
175                event.category === WebInspector.TracingModel.DevToolsMetadataEventCategory) {
176                this._devtoolsPageMetadataEvents.push(event);
177            }
178            if (event.name === WebInspector.TracingModel.DevToolsMetadataEvent.TracingSessionIdForWorker &&
179                event.category === WebInspector.TracingModel.DevToolsMetadataEventCategory) {
180                this._devtoolsWorkerMetadataEvents.push(event);
181            }
182            return;
183        }
184        switch (payload.name) {
185        case WebInspector.TracingModel.MetadataEvent.ProcessSortIndex:
186            process._setSortIndex(payload.args["sort_index"]);
187            break;
188        case WebInspector.TracingModel.MetadataEvent.ProcessName:
189            process._setName(payload.args["name"]);
190            break;
191        case WebInspector.TracingModel.MetadataEvent.ThreadSortIndex:
192            process.threadById(payload.tid)._setSortIndex(payload.args["sort_index"]);
193            break;
194        case WebInspector.TracingModel.MetadataEvent.ThreadName:
195            process.threadById(payload.tid)._setName(payload.args["name"]);
196            break;
197        }
198    },
199
200    _processMetadataEvents: function()
201    {
202        this._devtoolsPageMetadataEvents.sort(WebInspector.TracingModel.Event.compareStartTime);
203        if (!this._devtoolsPageMetadataEvents.length) {
204            WebInspector.console.error(WebInspector.TracingModel.DevToolsMetadataEvent.TracingStartedInPage + " event not found.");
205            return;
206        }
207        var sessionId = this._devtoolsPageMetadataEvents[0].args["sessionId"];
208        this._sessionId = sessionId;
209
210        var mismatchingIds = {};
211        function checkSessionId(event)
212        {
213            var args = event.args;
214            // FIXME: put sessionId into args["data"] for TracingStartedInPage event.
215            if (args["data"])
216                args = args["data"];
217            var id = args["sessionId"];
218            if (id === sessionId)
219                return true;
220            mismatchingIds[id] = true;
221            return false;
222        }
223        this._devtoolsPageMetadataEvents = this._devtoolsPageMetadataEvents.filter(checkSessionId);
224        this._devtoolsWorkerMetadataEvents = this._devtoolsWorkerMetadataEvents.filter(checkSessionId);
225
226        var idList = Object.keys(mismatchingIds);
227        if (idList.length)
228            WebInspector.console.error("Timeline recording was started in more than one page simulaniously. Session id mismatch: " + this._sessionId + " and " + idList + ".");
229    },
230
231    /**
232     * @return {number}
233     */
234    minimumRecordTime: function()
235    {
236        return this._minimumRecordTime;
237    },
238
239    /**
240     * @return {number}
241     */
242    maximumRecordTime: function()
243    {
244        return this._maximumRecordTime;
245    },
246
247    /**
248     * @return {!Array.<!WebInspector.TracingModel.Process>}
249     */
250    sortedProcesses: function()
251    {
252        return WebInspector.TracingModel.NamedObject._sort(Object.values(this._processById));
253    }
254}
255
256
257/**
258 * @constructor
259 * @param {!WebInspector.TracingModel} tracingModel
260 */
261WebInspector.TracingModel.Loader = function(tracingModel)
262{
263    this._tracingModel = tracingModel;
264    this._firstChunkReceived = false;
265}
266
267WebInspector.TracingModel.Loader.prototype = {
268    /**
269     * @param {!Array.<!WebInspector.TracingManager.EventPayload>} events
270     */
271    loadNextChunk: function(events)
272    {
273        if (!this._firstChunkReceived) {
274            this._tracingModel.reset();
275            this._firstChunkReceived = true;
276        }
277        this._tracingModel.addEvents(events);
278    },
279
280    finish: function()
281    {
282        this._tracingModel.tracingComplete();
283    }
284}
285
286
287/**
288 * @constructor
289 * @param {string} category
290 * @param {string} name
291 * @param {string} phase
292 * @param {number} startTime
293 * @param {?WebInspector.TracingModel.Thread} thread
294 */
295WebInspector.TracingModel.Event = function(category, name, phase, startTime, thread)
296{
297    this.category = category;
298    this.name = name;
299    this.phase = phase;
300    this.startTime = startTime;
301    this.thread = thread;
302    this.args = {};
303
304    /** @type {?string} */
305    this.warning = null;
306    /** @type {?WebInspector.TracingModel.Event} */
307    this.initiator = null;
308    /** @type {?Array.<!ConsoleAgent.CallFrame>} */
309    this.stackTrace = null;
310    /** @type {?Element} */
311    this.previewElement = null;
312    /** @type {?string} */
313    this.imageURL = null;
314    /** @type {number} */
315    this.backendNodeId = 0;
316
317    /** @type {number} */
318    this.selfTime = 0;
319}
320
321/**
322 * @param {!WebInspector.TracingManager.EventPayload} payload
323 * @param {?WebInspector.TracingModel.Thread} thread
324 * @return {!WebInspector.TracingModel.Event}
325 */
326WebInspector.TracingModel.Event.fromPayload = function(payload, thread)
327{
328    var event = new WebInspector.TracingModel.Event(payload.cat, payload.name, payload.ph, payload.ts / 1000, thread);
329    if (payload.args)
330        event.addArgs(payload.args);
331    else
332        console.error("Missing mandatory event argument 'args' at " + payload.ts / 1000);
333    if (typeof payload.dur === "number")
334        event.setEndTime((payload.ts + payload.dur) / 1000);
335    if (payload.id)
336        event.id = payload.id;
337    return event;
338}
339
340WebInspector.TracingModel.Event.prototype = {
341    /**
342     * @param {number} endTime
343     */
344    setEndTime: function(endTime)
345    {
346        if (endTime < this.startTime) {
347            console.assert(false, "Event out of order: " + this.name);
348            return;
349        }
350        this.endTime = endTime;
351        this.duration = endTime - this.startTime;
352    },
353
354    /**
355     * @param {!Object} args
356     */
357    addArgs: function(args)
358    {
359        // Shallow copy args to avoid modifying original payload which may be saved to file.
360        for (var name in args) {
361            if (name in this.args)
362                console.error("Same argument name (" + name +  ") is used for begin and end phases of " + this.name);
363            this.args[name] = args[name];
364        }
365    },
366
367    /**
368     * @param {!WebInspector.TracingManager.EventPayload} payload
369     */
370    _complete: function(payload)
371    {
372        if (payload.args)
373            this.addArgs(payload.args);
374        else
375            console.error("Missing mandatory event argument 'args' at " + payload.ts / 1000);
376        this.setEndTime(payload.ts / 1000);
377    },
378
379    /**
380     * @param {!WebInspector.DeferredTempFile} backingFile
381     * @param {number} startOffset
382     * @param {number} endOffset
383     */
384    _setBackingStorage: function(backingFile, startOffset, endOffset)
385    {
386    }
387}
388
389/**
390 * @param {!WebInspector.TracingModel.Event} a
391 * @param {!WebInspector.TracingModel.Event} b
392 * @return {number}
393 */
394WebInspector.TracingModel.Event.compareStartTime = function (a, b)
395{
396    return a.startTime - b.startTime;
397}
398
399/**
400 * @param {!WebInspector.TracingModel.Event} a
401 * @param {!WebInspector.TracingModel.Event} b
402 * @return {number}
403 */
404WebInspector.TracingModel.Event.orderedCompareStartTime = function (a, b)
405{
406    // Array.mergeOrdered coalesces objects if comparator returns 0.
407    // To change this behavior this comparator return -1 in the case events
408    // startTime's are equal, so both events got placed into the result array.
409    return a.startTime - b.startTime || -1;
410}
411
412/**
413 * @constructor
414 * @extends {WebInspector.TracingModel.Event}
415 * @param {string} category
416 * @param {string} name
417 * @param {number} startTime
418 * @param {?WebInspector.TracingModel.Thread} thread
419 */
420WebInspector.TracingModel.ObjectSnapshot = function(category, name, startTime, thread)
421{
422    WebInspector.TracingModel.Event.call(this, category, name, WebInspector.TracingModel.Phase.SnapshotObject, startTime, thread);
423}
424
425/**
426 * @param {!WebInspector.TracingManager.EventPayload} payload
427 * @param {?WebInspector.TracingModel.Thread} thread
428 * @return {!WebInspector.TracingModel.ObjectSnapshot}
429 */
430WebInspector.TracingModel.ObjectSnapshot.fromPayload = function(payload, thread)
431{
432    var snapshot = new WebInspector.TracingModel.ObjectSnapshot(payload.cat, payload.name, payload.ts / 1000, thread);
433    if (payload.id)
434        snapshot.id = payload.id;
435    if (!payload.args || !payload.args["snapshot"]) {
436        console.error("Missing mandatory 'snapshot' argument at " + payload.ts / 1000);
437        return snapshot;
438    }
439    if (payload.args)
440        snapshot.addArgs(payload.args);
441    return snapshot;
442}
443
444WebInspector.TracingModel.ObjectSnapshot.prototype = {
445   /**
446    * @param {function(?Object)} callback
447    */
448   requestObject: function(callback)
449   {
450       var snapshot = this.args["snapshot"];
451       if (snapshot) {
452           callback(snapshot);
453           return;
454       }
455       this._file.readRange(this._startOffset, this._endOffset, onRead);
456       /**
457        * @param {?string} result
458        */
459       function onRead(result)
460       {
461           if (!result) {
462               callback(null);
463               return;
464           }
465           var snapshot;
466           try {
467               var payload = JSON.parse(result);
468               snapshot = payload["args"]["snapshot"];
469           } catch (e) {
470               WebInspector.console.error("Malformed event data in backing storage");
471           }
472           callback(snapshot);
473       }
474    },
475
476    /**
477     * @param {!WebInspector.DeferredTempFile} backingFile
478     * @param {number} startOffset
479     * @param {number} endOffset
480     * @override
481     */
482    _setBackingStorage: function(backingFile, startOffset, endOffset)
483    {
484        if (endOffset - startOffset < 10000)
485            return;
486        this._file = backingFile;
487        this._startOffset = startOffset;
488        this._endOffset = endOffset;
489        this.args = {};
490    },
491
492    __proto__: WebInspector.TracingModel.Event.prototype
493}
494
495
496/**
497 * @constructor
498 */
499WebInspector.TracingModel.NamedObject = function()
500{
501}
502
503WebInspector.TracingModel.NamedObject.prototype =
504{
505    /**
506     * @param {string} name
507     */
508    _setName: function(name)
509    {
510        this._name = name;
511    },
512
513    /**
514     * @return {string}
515     */
516    name: function()
517    {
518        return this._name;
519    },
520
521    /**
522     * @param {number} sortIndex
523     */
524    _setSortIndex: function(sortIndex)
525    {
526        this._sortIndex = sortIndex;
527    },
528}
529
530/**
531 * @param {!Array.<!WebInspector.TracingModel.NamedObject>} array
532 */
533WebInspector.TracingModel.NamedObject._sort = function(array)
534{
535    /**
536     * @param {!WebInspector.TracingModel.NamedObject} a
537     * @param {!WebInspector.TracingModel.NamedObject} b
538     */
539    function comparator(a, b)
540    {
541        return a._sortIndex !== b._sortIndex ? a._sortIndex - b._sortIndex : a.name().localeCompare(b.name());
542    }
543    return array.sort(comparator);
544}
545
546/**
547 * @constructor
548 * @extends {WebInspector.TracingModel.NamedObject}
549 * @param {number} id
550 */
551WebInspector.TracingModel.Process = function(id)
552{
553    WebInspector.TracingModel.NamedObject.call(this);
554    this._setName("Process " + id);
555    this._threads = {};
556    this._objects = {};
557    /** @type {!Array.<!WebInspector.TracingManager.EventPayload>} */
558    this._asyncEvents = [];
559    /** @type {!Object.<string, ?Array.<!WebInspector.TracingModel.Event>>} */
560    this._openAsyncEvents = [];
561}
562
563WebInspector.TracingModel.Process.prototype = {
564    /**
565     * @param {number} id
566     * @return {!WebInspector.TracingModel.Thread}
567     */
568    threadById: function(id)
569    {
570        var thread = this._threads[id];
571        if (!thread) {
572            thread = new WebInspector.TracingModel.Thread(this, id);
573            this._threads[id] = thread;
574        }
575        return thread;
576    },
577
578    /**
579     * @param {!WebInspector.TracingManager.EventPayload} payload
580     * @return {?WebInspector.TracingModel.Event} event
581     */
582    _addEvent: function(payload)
583    {
584        var phase = WebInspector.TracingModel.Phase;
585        // Build async event when we've got events from all threads, so we can sort them and process in the chronological order.
586        // However, also add individual async events to the thread flow, so we can easily display them on the same chart as
587        // other events, should we choose so.
588        if (WebInspector.TracingModel.isAsyncPhase(payload.ph))
589            this._asyncEvents.push(payload);
590
591        var event = this.threadById(payload.tid)._addEvent(payload);
592        if (event && payload.ph === phase.SnapshotObject)
593            this.objectsByName(event.name).push(event);
594        return event;
595    },
596
597    /**
598     * @param {!number} lastEventTime
599     */
600    _tracingComplete: function(lastEventTime)
601    {
602        /**
603         * @param {!WebInspector.TracingManager.EventPayload} a
604         * @param {!WebInspector.TracingManager.EventPayload} b
605         */
606        function comparePayloadTimestamp(a, b)
607        {
608            return a.ts - b.ts;
609        }
610        this._asyncEvents.sort(comparePayloadTimestamp).forEach(this._addAsyncEvent, this);
611        for (var key in this._openAsyncEvents) {
612            var steps = this._openAsyncEvents[key];
613            if (!steps)
614                continue;
615            var startEvent = steps[0];
616            var syntheticEndEvent = new WebInspector.TracingModel.Event(startEvent.category, startEvent.name, WebInspector.TracingModel.Phase.AsyncEnd, lastEventTime, startEvent.thread);
617            steps.push(syntheticEndEvent);
618        }
619        this._asyncEvents = [];
620        this._openAsyncEvents = [];
621    },
622
623    /**
624     * @param {!WebInspector.TracingManager.EventPayload} payload
625     */
626    _addAsyncEvent: function(payload)
627    {
628        var phase = WebInspector.TracingModel.Phase;
629        var timestamp = payload.ts / 1000;
630        var key = payload.name + "." + payload.id;
631        var steps = this._openAsyncEvents[key];
632
633        var thread = this.threadById(payload.tid);
634        if (payload.ph === phase.AsyncBegin) {
635            if (steps) {
636                console.error("Event " + payload.name + " has already been started");
637                return;
638            }
639            steps = [WebInspector.TracingModel.Event.fromPayload(payload, thread)];
640            this._openAsyncEvents[key] = steps;
641            thread._addAsyncEventSteps(steps);
642            return;
643        }
644        if (!steps) {
645            console.error("Unexpected async event " + payload.name + ", phase " + payload.ph);
646            return;
647        }
648        var newEvent = WebInspector.TracingModel.Event.fromPayload(payload, thread);
649        if (payload.ph === phase.AsyncEnd) {
650            steps.push(newEvent);
651            delete this._openAsyncEvents[key];
652        } else if (payload.ph === phase.AsyncStepInto || payload.ph === phase.AsyncStepPast) {
653            var lastPhase = steps.peekLast().phase;
654            if (lastPhase !== phase.AsyncBegin && lastPhase !== payload.ph) {
655                console.assert(false, "Async event step phase mismatch: " + lastPhase + " at " + steps.peekLast().startTime + " vs. " + payload.ph + " at " + timestamp);
656                return;
657            }
658            steps.push(newEvent);
659        } else {
660            console.assert(false, "Invalid async event phase");
661        }
662    },
663
664    /**
665     * @param {string} name
666     * @return {!Array.<!WebInspector.TracingModel.Event>}
667     */
668    objectsByName: function(name)
669    {
670        var objects = this._objects[name];
671        if (!objects) {
672            objects = [];
673            this._objects[name] = objects;
674        }
675        return objects;
676    },
677
678    /**
679     * @return {!Array.<string>}
680     */
681    sortedObjectNames: function()
682    {
683        return Object.keys(this._objects).sort();
684    },
685
686    /**
687     * @return {!Array.<!WebInspector.TracingModel.Thread>}
688     */
689    sortedThreads: function()
690    {
691        return WebInspector.TracingModel.NamedObject._sort(Object.values(this._threads));
692    },
693
694    __proto__: WebInspector.TracingModel.NamedObject.prototype
695}
696
697/**
698 * @constructor
699 * @extends {WebInspector.TracingModel.NamedObject}
700 * @param {!WebInspector.TracingModel.Process} process
701 * @param {number} id
702 */
703WebInspector.TracingModel.Thread = function(process, id)
704{
705    WebInspector.TracingModel.NamedObject.call(this);
706    this._process = process;
707    this._setName("Thread " + id);
708    this._events = [];
709    this._asyncEvents = [];
710    this._id = id;
711
712    this._stack = [];
713}
714
715WebInspector.TracingModel.Thread.prototype = {
716
717    /**
718     * @return {?WebInspector.Target}
719     */
720    target: function()
721    {
722        //FIXME: correctly specify target
723        return WebInspector.targetManager.targets()[0];
724    },
725
726    /**
727     * @param {!WebInspector.TracingManager.EventPayload} payload
728     * @return {?WebInspector.TracingModel.Event} event
729     */
730    _addEvent: function(payload)
731    {
732        var timestamp = payload.ts / 1000;
733        if (payload.ph === WebInspector.TracingModel.Phase.End) {
734            // Quietly ignore unbalanced close events, they're legit (we could have missed start one).
735            if (!this._stack.length)
736                return null;
737            var top = this._stack.pop();
738            if (top.name !== payload.name || top.category !== payload.cat)
739                console.error("B/E events mismatch at " + top.startTime + " (" + top.name + ") vs. " + timestamp + " (" + payload.name + ")");
740            else
741                top._complete(payload);
742            return null;
743        }
744        var event = payload.ph === WebInspector.TracingModel.Phase.SnapshotObject
745            ? WebInspector.TracingModel.ObjectSnapshot.fromPayload(payload, this)
746            : WebInspector.TracingModel.Event.fromPayload(payload, this);
747        if (payload.ph === WebInspector.TracingModel.Phase.Begin)
748            this._stack.push(event);
749        if (this._events.length && this._events.peekLast().startTime > event.startTime)
750            console.assert(false, "Event is out of order: " + event.name);
751        this._events.push(event);
752        return event;
753    },
754
755    /**
756     * @param {!Array.<!WebInspector.TracingModel.Event>} eventSteps
757     */
758    _addAsyncEventSteps: function(eventSteps)
759    {
760        this._asyncEvents.push(eventSteps);
761    },
762
763    /**
764     * @return {number}
765     */
766    id: function()
767    {
768        return this._id;
769    },
770
771    /**
772     * @return {!WebInspector.TracingModel.Process}
773     */
774    process: function()
775    {
776        return this._process;
777    },
778
779    /**
780     * @return {!Array.<!WebInspector.TracingModel.Event>}
781     */
782    events: function()
783    {
784        return this._events;
785    },
786
787    /**
788     * @return {!Array.<!WebInspector.TracingModel.Event>}
789     */
790    asyncEvents: function()
791    {
792        return this._asyncEvents;
793    },
794
795    __proto__: WebInspector.TracingModel.NamedObject.prototype
796}
797