blob: 33aea726a830ff3d0e121d3c4f80cef3cbd353cb [file] [log] [blame]
<!DOCTYPE html>
<!--
Copyright (c) 2015 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="/tracing/base/extension_registry.html">
<script>
'use strict';
/**
* @fileoverview Provides the Attribute class.
*/
tr.exportTo('tr.model', function() {
/**
* @constructor
*/
function Attribute(units) {
this.units = units;
// AttributeInfo(s) about the attribute (e.g. information about how it was
// calculated).
this.infos = [];
}
Attribute.fromDictIfPossible = function(dict, opt_model) {
var typeInfo = Attribute.findTypeInfoMatching(function(typeInfo) {
return typeInfo.metadata.type === dict.type;
});
if (typeInfo === undefined) {
if (opt_model) {
opt_model.importWarning({
type: 'attribute_parse_error',
message: 'Unknown attribute type \'' + dict.type + '\'.'
});
}
return UnknownAttribute.fromDict(dict, opt_model);
}
return typeInfo.constructor.fromDict(dict, opt_model);
};
/**
* Find the common constructor and units of a list of attribute values. If
* they have different types (e.g. ScalarAttribute and UnknownAttribute) or
* units (e.g. 'ms' and 'Hz'), the common constructor will be
* UnknownAttribute and the common units will be undefined.
*
* Undefined attribute values are skipped. This function will return undefined
* if the list of attribute values contains no defined attribute values.
*/
Attribute.findCommonTraits = function(attributes, opt_model) {
var commonTraits;
for (var i = 0; i < attributes.length; i++) {
var attribute = attributes[i];
if (attribute === undefined)
continue;
var attributeConstructor = attribute.constructor;
var attributeUnits = attribute.units;
if (commonTraits === undefined) {
commonTraits = {
constructor: attributeConstructor,
units: attributeUnits
};
} else if (attributeConstructor !== commonTraits.constructor) {
if (opt_model) {
opt_model.importWarning({
type: 'attribute_parse_error',
message: 'Attribute with different types: ' +
commonTraits.constructor + ' and ' + attributeConstructor + '.'
});
}
commonTraits = {
constructor: UnknownAttribute,
units: undefined
};
break;
} else if (attributeUnits !== commonTraits.units) {
if (opt_model) {
opt_model.importWarning({
type: 'attribute_parse_error',
message: 'Attribute with different units: ' + commonTraits.units +
' and ' + attributeUnits + '.'
});
}
commonTraits = {
constructor: UnknownAttribute,
units: undefined
};
break;
}
}
return commonTraits;
};
/**
* Aggregate a list of child attribute values with an existing attribute
* value. The individual values can be undefined, in which case they are
* ignored.
*/
Attribute.aggregate = function(childAttributes, existingParentAttribute,
opt_model) {
var definedChildAttributes = childAttributes.filter(
function(childAttribute) {
return childAttribute !== undefined;
});
// If all child attribute values were undefined, return the existing parent
// attribute value (possibly undefined).
var traits = Attribute.findCommonTraits(definedChildAttributes, opt_model);
if (traits === undefined)
return existingParentAttribute;
var constructor = traits.constructor;
// If the common type does not support merging child attribute values,
// return the existing parent attribute value (possibly undefined).
if (constructor.merge === undefined)
return existingParentAttribute;
var mergedAttribute = constructor.merge(
definedChildAttributes, traits.units, opt_model);
// If there is no existing parent attribute value, use the merged value
// (possibly undefined).
if (existingParentAttribute === undefined)
return mergedAttribute;
// Leave it up to the existing parent attribute value to decide if/how it
// will use the merged value (e.g. generate an import warning if the
// existing and merged attribute value types differ).
existingParentAttribute.useMergedAttribute(mergedAttribute, opt_model);
return existingParentAttribute;
}
Attribute.fromTraceValue = function(dict, opt_model) {
throw new Error('Not implemented');
};
Attribute.prototype.useMergedAttribute = function(mergedAttribute,
opt_model) {
if (mergedAttribute.constructor !== this.constructor) {
if (opt_model) {
opt_model.importWarning({
type: 'attribute_parse_error',
message: 'Attribute with different types: ' + this.constructor +
' and ' + mergedAttribute.constructor + '.'
});
}
} else if (mergedAttribute.units !== this.units) {
if (opt_model) {
opt_model.importWarning({
type: 'attribute_parse_error',
message: 'Attribute with different units: ' + this.units +
' and ' + mergedAttribute.units + '.'
});
}
}
};
var options = new tr.b.ExtensionRegistryOptions(tr.b.BASIC_REGISTRY_MODE);
tr.b.decorateExtensionRegistry(Attribute, options);
Attribute.addEventListener('will-register', function(e) {
if (!e.typeInfo.constructor.hasOwnProperty('fromDict'))
throw new Error('Attributes must have fromDict method');
if (!e.typeInfo.metadata.type)
throw new Error('Attributes must provide type');
if (e.typeInfo.constructor.prototype.constructor !== e.typeInfo.constructor)
throw new Error('Attribute prototypes must provide constructor.');
});
/**
* @constructor
*/
function ScalarAttribute(units, value) {
Attribute.call(this, units);
this.value = value;
}
ScalarAttribute.fromDict = function(dict) {
return new ScalarAttribute(dict.units, parseInt(dict.value, 16));
};
ScalarAttribute.merge = function(childAttributes, units) {
var sum = 0;
childAttributes.forEach(function(childAttribute) {
sum += childAttribute.value;
});
return new ScalarAttribute(units, sum);
}
ScalarAttribute.prototype.__proto__ = Attribute.prototype;
Attribute.register(ScalarAttribute, {type: 'scalar'});
/**
* @constructor
*/
function StringAttribute(units, value) {
Attribute.call(this, units);
this.value = value;
}
StringAttribute.fromDict = function(dict) {
return new StringAttribute(dict.units, dict.value);
};
Attribute.register(StringAttribute, {type: 'string'});
/**
* @constructor
*/
function UnknownAttribute(units, opt_value) {
Attribute.call(this, units, opt_value);
this.value = opt_value;
}
UnknownAttribute.fromDict = function(dict) {
return new UnknownAttribute(dict.units);
};
UnknownAttribute.prototype.__proto__ = Attribute.prototype;
/**
* @constructor
*/
function AttributeInfo(type, message) {
this.type = type;
this.message = message;
}
/**
* The type of AttributeInfo.
* @enum
*/
var AttributeInfoType = {
// Generic information (e.g. how the attribute was calculated).
INFORMATION: 0,
// Warning (e.g. inconsistent attribute values provided).
WARNING: 1,
// Attribute source (e.g. attribute refers to an older dump's attribute).
LINK: 2,
// Corresponding memory allocator dump owns another MAD.
// TODO(petrcermak): Figure out if there's a better place to store this.
MEMORY_OWNER: 3,
// Corresponding memory allocator dump is owned by another MAD.
// TODO(petrcermak): Figure out if there's a better place to store this.
MEMORY_OWNED: 4,
// Overall value (e.g. peak value since start process).
OVERALL_VALUE: 5,
// Recent value (e.g. peak value since the previous memory dump).
RECENT_VALUE: 6,
// The allocator has an associated memory heap dump.
// TODO(petrcermak): Move this into the UI.
HAS_HEAP_DUMP: 7
};
return {
Attribute: Attribute,
ScalarAttribute: ScalarAttribute,
StringAttribute: StringAttribute,
UnknownAttribute: UnknownAttribute,
AttributeInfo: AttributeInfo,
AttributeInfoType: AttributeInfoType
};
});
</script>