| <!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="/base/range.html"> |
| <link rel="import" href="/base/sorted_array_utils.html"> |
| |
| <script> |
| 'use strict'; |
| |
| /** |
| * @fileoverview Provides the TimeToObjectInstanceMap class. |
| */ |
| tv.exportTo('tv.c.trace_model', function() { |
| /** |
| * Tracks all the instances associated with a given ID over its lifetime. |
| * |
| * An id can be used multiple times throughout a trace, referring to different |
| * objects at different times. This data structure does the bookkeeping to |
| * figure out what ObjectInstance is referred to at a given timestamp. |
| * |
| * @constructor |
| */ |
| function TimeToObjectInstanceMap(createObjectInstanceFunction, parent, id) { |
| this.createObjectInstanceFunction_ = createObjectInstanceFunction; |
| this.parent = parent; |
| this.id = id; |
| this.instances = []; |
| } |
| |
| TimeToObjectInstanceMap.prototype = { |
| idWasCreated: function(category, name, ts) { |
| if (this.instances.length == 0) { |
| this.instances.push(this.createObjectInstanceFunction_( |
| this.parent, this.id, category, name, ts)); |
| this.instances[0].creationTsWasExplicit = true; |
| return this.instances[0]; |
| } |
| |
| var lastInstance = this.instances[this.instances.length - 1]; |
| if (ts < lastInstance.deletionTs) { |
| throw new Error('Mutation of the TimeToObjectInstanceMap must be ' + |
| 'done in ascending timestamp order.'); |
| } |
| lastInstance = this.createObjectInstanceFunction_( |
| this.parent, this.id, category, name, ts); |
| lastInstance.creationTsWasExplicit = true; |
| this.instances.push(lastInstance); |
| return lastInstance; |
| }, |
| |
| addSnapshot: function(category, name, ts, args, opt_baseTypeName) { |
| if (this.instances.length == 0) { |
| this.instances.push(this.createObjectInstanceFunction_( |
| this.parent, this.id, category, name, ts, opt_baseTypeName)); |
| } |
| |
| var i = tv.b.findIndexInSortedIntervals( |
| this.instances, |
| function(inst) { return inst.creationTs; }, |
| function(inst) { return inst.deletionTs - inst.creationTs; }, |
| ts); |
| |
| var instance; |
| if (i < 0) { |
| instance = this.instances[0]; |
| if (ts > instance.deletionTs || |
| instance.creationTsWasExplicit) { |
| throw new Error( |
| 'At the provided timestamp, no instance was still alive'); |
| } |
| |
| if (instance.snapshots.length != 0) { |
| throw new Error( |
| 'Cannot shift creationTs forward, ' + |
| 'snapshots have been added. First snap was at ts=' + |
| instance.snapshots[0].ts + ' and creationTs was ' + |
| instance.creationTs); |
| } |
| instance.creationTs = ts; |
| } else if (i >= this.instances.length) { |
| instance = this.instances[this.instances.length - 1]; |
| if (ts >= instance.deletionTs) { |
| // The snap is added after our oldest and deleted instance. This means |
| // that this is a new implicit instance. |
| instance = this.createObjectInstanceFunction_( |
| this.parent, this.id, category, name, ts, opt_baseTypeName); |
| this.instances.push(instance); |
| } else { |
| // If the ts is before the last objects deletion time, then the caller |
| // is trying to add a snapshot when there may have been an instance |
| // alive. In that case, try to move an instance's creationTs to |
| // include this ts, provided that it has an implicit creationTs. |
| |
| // Search backward from the right for an instance that was definitely |
| // deleted before this ts. Any time an instance is found that has a |
| // moveable creationTs |
| var lastValidIndex; |
| for (var i = this.instances.length - 1; i >= 0; i--) { |
| var tmp = this.instances[i]; |
| if (ts >= tmp.deletionTs) |
| break; |
| if (tmp.creationTsWasExplicit == false && tmp.snapshots.length == 0) |
| lastValidIndex = i; |
| } |
| if (lastValidIndex === undefined) { |
| throw new Error( |
| 'Cannot add snapshot. No instance was alive that was mutable.'); |
| } |
| instance = this.instances[lastValidIndex]; |
| instance.creationTs = ts; |
| } |
| } else { |
| instance = this.instances[i]; |
| } |
| |
| return instance.addSnapshot(ts, args, name, opt_baseTypeName); |
| }, |
| |
| get lastInstance() { |
| if (this.instances.length == 0) |
| return undefined; |
| return this.instances[this.instances.length - 1]; |
| }, |
| |
| idWasDeleted: function(category, name, ts) { |
| if (this.instances.length == 0) { |
| this.instances.push(this.createObjectInstanceFunction_( |
| this.parent, this.id, category, name, ts)); |
| } |
| var lastInstance = this.instances[this.instances.length - 1]; |
| if (ts < lastInstance.creationTs) |
| throw new Error('Cannot delete a id before it was crated'); |
| if (lastInstance.deletionTs == Number.MAX_VALUE) { |
| lastInstance.wasDeleted(ts); |
| return lastInstance; |
| } |
| |
| if (ts < lastInstance.deletionTs) |
| throw new Error('id was already deleted earlier.'); |
| |
| // A new instance was deleted with no snapshots in-between. |
| // Create an instance then kill it. |
| lastInstance = this.createObjectInstanceFunction_( |
| this.parent, this.id, category, name, ts); |
| this.instances.push(lastInstance); |
| lastInstance.wasDeleted(ts); |
| return lastInstance; |
| }, |
| |
| getInstanceAt: function(ts) { |
| var i = tv.b.findIndexInSortedIntervals( |
| this.instances, |
| function(inst) { return inst.creationTs; }, |
| function(inst) { return inst.deletionTs - inst.creationTs; }, |
| ts); |
| if (i < 0) { |
| if (this.instances[0].creationTsWasExplicit) |
| return undefined; |
| return this.instances[0]; |
| } else if (i >= this.instances.length) { |
| return undefined; |
| } |
| return this.instances[i]; |
| }, |
| |
| logToConsole: function() { |
| for (var i = 0; i < this.instances.length; i++) { |
| var instance = this.instances[i]; |
| var cEF = ''; |
| var dEF = ''; |
| if (instance.creationTsWasExplicit) |
| cEF = '(explicitC)'; |
| if (instance.deletionTsWasExplicit) |
| dEF = '(explicit)'; |
| console.log(instance.creationTs, cEF, |
| instance.deletionTs, dEF, |
| instance.category, |
| instance.name, |
| instance.snapshots.length + ' snapshots'); |
| } |
| } |
| }; |
| |
| return { |
| TimeToObjectInstanceMap: TimeToObjectInstanceMap |
| }; |
| }); |
| </script> |