Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(525)

Unified Diff: tracing/tracing/metrics/system_health/memory_metric.html

Issue 2018503002: [system-health] Normalize the structure of memory metric values (Closed) Base URL: git@github.com:catapult-project/catapult.git@master
Patch Set: Exclude tracing from total reported_by_chrome values Created 4 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | tracing/tracing/metrics/system_health/memory_metric_test.html » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: tracing/tracing/metrics/system_health/memory_metric.html
diff --git a/tracing/tracing/metrics/system_health/memory_metric.html b/tracing/tracing/metrics/system_health/memory_metric.html
index 4a42bb8a61f3f9754187e0fd87992f994b0fa133..ca6a8a4811cf670fd412bc62e4f76d73a94feae5 100644
--- a/tracing/tracing/metrics/system_health/memory_metric.html
+++ b/tracing/tracing/metrics/system_health/memory_metric.html
@@ -6,6 +6,7 @@ found in the LICENSE file.
-->
<link rel="import" href="/tracing/base/iteration_helpers.html">
+<link rel="import" href="/tracing/base/multi_dimensional_view.html">
<link rel="import" href="/tracing/base/range.html">
<link rel="import" href="/tracing/metrics/metric_registry.html">
<link rel="import" href="/tracing/model/container_memory_dump.html">
@@ -19,7 +20,6 @@ found in the LICENSE file.
'use strict';
tr.exportTo('tr.metrics.sh', function() {
-
var LIGHT = tr.model.ContainerMemoryDump.LevelOfDetail.LIGHT;
var DETAILED = tr.model.ContainerMemoryDump.LevelOfDetail.DETAILED;
var ScalarNumeric = tr.v.ScalarNumeric;
@@ -27,37 +27,8 @@ tr.exportTo('tr.metrics.sh', function() {
tr.v.Unit.byName.sizeInBytes_smallerIsBetter;
var unitlessNumber_smallerIsBetter =
tr.v.Unit.byName.unitlessNumber_smallerIsBetter;
-
- var MMAPS_VALUES = {
- 'overall:pss': {
- path: [],
- byteStat: 'proportionalResident',
- descriptionPrefix: 'total proportional resident size (PSS) of'
- },
- 'overall:private_dirty': {
- path: [],
- byteStat: 'privateDirtyResident',
- descriptionPrefix: 'total private dirty size of'
- },
- 'java_heap:private_dirty': {
- path: ['Android', 'Java runtime', 'Spaces'],
- byteStat: 'privateDirtyResident',
- descriptionPrefix: 'private dirty size of the Java heap in'
- },
- 'ashmem:pss': {
- path: ['Android', 'Ashmem'],
- byteStat: 'proportionalResident',
- descriptionPrefix: 'proportional resident size (PSS) of ashmem in'
- },
- 'native_heap:pss': {
- path: ['Native heap'],
- byteStat: 'proportionalResident',
- descriptionPrefix:
- 'proportional resident size (PSS) of the native heap in'
- }
- };
-
- var ALL_PROCESS_NAMES = 'all';
+ var DISPLAYED_SIZE_NUMERIC_NAME =
+ tr.model.MemoryAllocatorDump.DISPLAYED_SIZE_NUMERIC_NAME;
var LEVEL_OF_DETAIL_NAMES = new Map();
LEVEL_OF_DETAIL_NAMES.set(LIGHT, 'light');
@@ -79,9 +50,9 @@ tr.exportTo('tr.metrics.sh', function() {
function memoryMetric(values, model) {
var browserNameToGlobalDumps = splitGlobalDumpsByBrowserName(model);
- addGeneralMemoryDumpValues(browserNameToGlobalDumps, values, model);
- addDetailedMemoryDumpValues(browserNameToGlobalDumps, values, model);
- addMemoryDumpCountValues(browserNameToGlobalDumps, values, model);
+ addGeneralMemoryDumpValues(browserNameToGlobalDumps, values);
+ addDetailedMemoryDumpValues(browserNameToGlobalDumps, values);
+ addMemoryDumpCountValues(browserNameToGlobalDumps, values);
}
/**
@@ -171,27 +142,35 @@ tr.exportTo('tr.metrics.sh', function() {
return '\'' + browserName + '\' browser';
}
+ function canonicalizeProcessName(rawProcessName) {
+ var baseCanonicalName = canonicalizeName(rawProcessName);
+ switch (baseCanonicalName) {
+ case 'renderer':
+ return 'renderer_processes'; // Intentionally plural.
+ case 'browser':
+ return 'browser_process';
+ default:
+ return baseCanonicalName;
+ }
+ }
+
/**
- * Convert a canonical process name used in value names to a plural
- * user-friendly name used in value descriptions.
- *
- * Examples:
- *
- * CANONICAL PROCESS NAME -> PLURAL USER-FRIENDLY NAME
- * browser -> browser processes
- * renderer -> renderer processes
- * all -> all processes
- * gpu_process -> GPU processes
- * other -> 'other' processes
+ * Convert a canonical process name used in value names to a user-friendly
+ * name used in value descriptions.
*/
- function convertProcessNameToPluralUserFriendlyName(processName) {
+ function convertProcessNameToUserFriendlyName(processName,
+ opt_requirePlural) {
switch (processName) {
- case 'browser':
- case 'renderer':
- case ALL_PROCESS_NAMES:
- return processName + ' processes';
+ case 'browser_process':
+ return opt_requirePlural ? 'browser processes' : 'the browser process';
+ case 'renderer_processes':
+ return 'renderer processes';
case 'gpu_process':
- return 'GPU processes';
+ return opt_requirePlural ? 'GPU processes' : 'the GPU process';
+ case 'ppapi_process':
+ return opt_requirePlural ? 'PPAPI processes' : 'the PPAPI process';
+ case 'all_processes':
+ return 'all processes';
default:
return '\'' + processName + '\' processes';
}
@@ -228,135 +207,377 @@ tr.exportTo('tr.metrics.sh', function() {
}
/**
- * Add general memory dump values calculated from all global memory dumps in
- * |model| to |values|. In particular, this function adds the following
- * values:
+ * Add general memory dump values calculated from all global memory dumps to
+ * |values|. In particular, this function adds the following values:
*
* * PROCESS COUNTS
- * memory:{chrome, webview}:{browser, renderer, ..., all}:process_count
+ * memory:{chrome, webview}:
+ * {browser_process, renderer_processes, ..., all_processes}:
+ * process_count
* type: tr.v.Numeric (histogram over all matching global memory dumps)
* unit: unitlessNumber_smallerIsBetter
*
- * * SUBSYSTEM STATISTICS
- * memory:{chrome, webview}:{browser, renderer, ..., all}:subsystem:
- * {v8, malloc, ...}:{effective_size, allocated_objects_size}
- * memory:{chrome, webview}:{browser, renderer, ..., all}:subsystem:
- * gpu:android_memtrack:{gl, ...}:memtrack_pss
- * memory:{chrome, webview}:{browser, renderer, ..., all}:subsystem:
- * discardable:locked_size
+ * * MEMORY USAGE REPORTED BY CHROME
+ * memory:{chrome, webview}:
+ * {browser_process, renderer_processes, ..., all_processes}:
+ * reported_by_chrome[:{v8, malloc, ...}]:
+ * {effective_size, allocated_objects_size, locked_size}
* type: tr.v.Numeric (histogram over all matching global memory dumps)
* unit: sizeInBytes_smallerIsBetter
*/
- function addGeneralMemoryDumpValues(
- browserNameToGlobalDumps, values, model) {
- addPerProcessNameMemoryDumpValues(browserNameToGlobalDumps,
+ function addGeneralMemoryDumpValues(browserNameToGlobalDumps, values) {
+ addMemoryDumpValues(browserNameToGlobalDumps,
gmd => true /* process all global memory dumps */,
function(processDump, addProcessScalar) {
- // Increment process_count value.
- addProcessScalar(
- 'process_count',
- new ScalarNumeric(unitlessNumber_smallerIsBetter, 1),
- 'total number of');
+ // Increment memory:<browser-name>:<process-name>:process_count value.
+ addProcessScalar({
+ source: 'process_count',
+ value: 1,
+ unit: unitlessNumber_smallerIsBetter,
+ descriptionPrefixBuilder: buildProcessCountDescriptionPrefix
+ });
+ // Add memory:<browser-name>:<process-name>:reported_by_chrome:...
+ // values.
if (processDump.memoryAllocatorDumps === undefined)
return;
-
processDump.memoryAllocatorDumps.forEach(function(rootAllocatorDump) {
- var name = rootAllocatorDump.name;
- var valueNamePrefix = 'subsystem:' + name;
-
- // Add generic values for each root memory allocator dump
- // (memory:<browser-name>:<process-name>:subsystem:<name>:
- // {effective_size, allocated_objects_size}).
- addProcessScalar(
- valueNamePrefix + ':effective_size',
- rootAllocatorDump.numerics['effective_size'],
- 'total effective size of ' + name + ' in');
- addProcessScalar(
- valueNamePrefix + ':allocated_objects_size',
- rootAllocatorDump.numerics['allocated_objects_size'],
- 'total size of all objects allocated by ' + name + ' in');
-
- // Add subsystem-specific values.
- switch (rootAllocatorDump.name) {
- // memory:<browser-name>:<process-name>:subsystem:gpu:
- // android_memtrack:<component-name>:memtrack_pss.
- case 'gpu':
- var memtrackDump =
- rootAllocatorDump.getDescendantDumpByFullName(
- 'android_memtrack');
- if (memtrackDump !== undefined) {
- memtrackDump.children.forEach(function(memtrackChildDump) {
- var childName = memtrackChildDump.name;
- addProcessScalar(
- valueNamePrefix + ':android_memtrack:' + childName +
- ':memtrack_pss',
- memtrackChildDump.numerics['memtrack_pss'],
- 'total proportional resident size (PSS) of the ' +
- childName + ' component of Android memtrack in');
- });
- }
- break;
- // memory:<browser-name>:<process-name>:subsystem:discardable:
- // locked_size.
- case 'discardable':
- addProcessScalar(
- valueNamePrefix + ':locked_size',
- rootAllocatorDump.numerics['locked_size'],
- 'total locked (pinned) size of ' + name + ' in');
- break;
- }
+ CHROME_VALUE_PROPERTIES.forEach(function(spec) {
+ addProcessScalar({
+ source: 'reported_by_chrome',
+ component: [rootAllocatorDump.name],
+ property: spec.propertyName,
+ value: rootAllocatorDump.numerics[spec.propertyName],
+ descriptionPrefixBuilder: spec.descriptionPrefixBuilder
+ });
+ });
});
- }, values, model);
+ },
+ function(componentTree) {
+ // Subtract memory:<browser-name>:<process-name>:reported_by_chrome:
+ // tracing:<size-property> from memory:<browser-name>:<process-name>:
+ // reported_by_chrome:<size-property> if applicable.
+ var tracingNode = componentTree.children[1].get('tracing');
+ if (tracingNode === undefined)
+ return;
+ for (var i = 0; i < componentTree.values.length; i++)
+ componentTree.values[i].total -= tracingNode.values[i].total;
+ }, values);
}
/**
- * Add heavy memory dump values calculated from heavy global memory dumps in
- * |model| to |values|. In particular, this function adds the following
- * values:
- *
- * * VIRTUAL MEMORY STATISTICS
- * memory:{chrome, webview}:{browser, renderer, ..., all}:vmstats:
- * {overall, ashmem, native_heap}:pss
- * memory:{chrome, webview}:{browser, renderer, ..., all}:vmstats:
- * {overall, java_heap}:private_dirty
+ * Build a description prefix for a memory:<browser-name>:<process-name>:
+ * process_count value.
+ *
+ * @param {!Array<string>} componentPath The underlying component path (must
+ * be empty).
+ * @param {string} processName The canonical name of the process.
+ * @return {string} Prefix for the value's description (always
+ * 'total number of renderer processes').
+ */
+ function buildProcessCountDescriptionPrefix(componentPath, processName) {
+ if (componentPath.length > 0) {
+ throw new Error('Unexpected process count non-empty component path: ' +
+ componentPath.join(':'));
+ }
+ return 'total number of ' + convertProcessNameToUserFriendlyName(
+ processName, true /* opt_requirePlural */);
+ }
+
+ /**
+ * Build a description prefix for a memory:<browser-name>:<process-name>:
+ * reported_by_chrome:... value.
+ *
+ * @param {{
+ * userFriendlyPropertyName: string,
+ * userFriendlyPropertyNamePrefix: (string|undefined),
+ * totalUserFriendlyPropertyName: (string|undefined),
+ * componentPreposition: (string|undefined) }}
+ * formatSpec Specification of how the property should be formatted.
+ * @param {!Array<string>} componentPath The underlying component path (e.g.
+ * ['malloc']).
+ * @param {string} processName The canonical name of the process.
+ * @return {string} Prefix for the value's description (e.g.
+ * 'effective size of malloc in the browser process').
+ */
+ function buildChromeValueDescriptionPrefix(
+ formatSpec, componentPath, processName) {
+ var nameParts = [];
+ if (componentPath.length === 0) {
+ nameParts.push('total');
+ if (formatSpec.totalUserFriendlyPropertyName) {
+ nameParts.push(formatSpec.totalUserFriendlyPropertyName);
+ } else {
+ if (formatSpec.userFriendlyPropertyNamePrefix)
+ nameParts.push(formatSpec.userFriendlyPropertyNamePrefix);
+ nameParts.push(formatSpec.userFriendlyPropertyName);
+ }
+ nameParts.push('reported by Chrome for');
+ } else {
+ if (formatSpec.componentPreposition === undefined) {
+ // Use component name as an adjective
+ // (e.g. 'size of V8 code and metadata').
+ if (formatSpec.userFriendlyPropertyNamePrefix)
+ nameParts.push(formatSpec.userFriendlyPropertyNamePrefix);
+ nameParts.push(componentPath.join(':'));
+ nameParts.push(formatSpec.userFriendlyPropertyName);
+ } else {
+ // Use component name as a noun with a preposition
+ // (e.g. 'size of all objects allocated BY MALLOC').
+ if (formatSpec.userFriendlyPropertyNamePrefix)
+ nameParts.push(formatSpec.userFriendlyPropertyNamePrefix);
+ nameParts.push(formatSpec.userFriendlyPropertyName);
+ nameParts.push(formatSpec.componentPreposition);
+ nameParts.push(componentPath.join(':'));
+ }
+ nameParts.push('in');
+ }
+ nameParts.push(convertProcessNameToUserFriendlyName(processName));
+ return nameParts.join(' ');
+ }
+
+ // Specifications of properties reported by Chrome.
+ var CHROME_VALUE_PROPERTIES = [
+ {
+ propertyName: 'effective_size',
+ descriptionPrefixBuilder: buildChromeValueDescriptionPrefix.bind(
+ undefined, {
+ userFriendlyPropertyName: 'effective size',
+ componentPreposition: 'of'
+ })
+ },
+ {
+ propertyName: 'allocated_objects_size',
+ descriptionPrefixBuilder: buildChromeValueDescriptionPrefix.bind(
+ undefined, {
+ userFriendlyPropertyName: 'size of all objects allocated',
+ totalUserFriendlyPropertyName: 'size of all allocated objects',
+ componentPreposition: 'by'
+ })
+ },
+ {
+ propertyName: 'locked_size',
+ descriptionPrefixBuilder: buildChromeValueDescriptionPrefix.bind(
+ undefined, {
+ userFriendlyPropertyName: 'locked (pinned) size',
+ componentPreposition: 'of'
+ })
+ }
+ ];
+
+ /**
+ * Add heavy memory dump values calculated from heavy global memory dumps to
+ * |values|. In particular, this function adds the following values:
+ *
+ * * MEMORY USAGE REPORTED BY THE OS
+ * memory:{chrome, webview}:
+ * {browser_process, renderer_processes, ..., all_processes}:
+ * reported_by_os:system_memory:[{ashmem, native_heap, java_heap}:]
+ * {proportional_resident_size, private_dirty_size}
+ * memory:{chrome, webview}:
+ * {browser_process, renderer_processes, ..., all_processes}:
+ * reported_by_os:gpu_memory:[{gl, graphics, ...}:]
+ * proportional_resident_size
+ * type: tr.v.Numeric (histogram over matching heavy global memory dumps)
+ * unit: sizeInBytes_smallerIsBetter
+ *
+ * * MEMORY USAGE REPORTED BY CHROME
+ * memory:{chrome, webview}:
+ * {browser_process, renderer_processes, ..., all_processes}:
+ * reported_by_chrome:v8:code_and_metadata_size
* type: tr.v.Numeric (histogram over matching heavy global memory dumps)
* unit: sizeInBytes_smallerIsBetter
*/
- function addDetailedMemoryDumpValues(
- browserNameToGlobalDumps, values, model) {
- addPerProcessNameMemoryDumpValues(browserNameToGlobalDumps,
+ function addDetailedMemoryDumpValues(browserNameToGlobalDumps, values) {
+ addMemoryDumpValues(browserNameToGlobalDumps,
g => g.levelOfDetail === DETAILED,
function(processDump, addProcessScalar) {
- // Add memory:<browser-name>:<process-name>:vmstats:<name> value for
- // each mmap metric.
- tr.b.iterItems(MMAPS_VALUES, function(valueName, valueSpec) {
- var node = getDescendantVmRegionClassificationNode(
- processDump.vmRegions, valueSpec.path);
- var value = node ? (node.byteStats[valueSpec.byteStat] || 0) : 0;
- addProcessScalar(
- 'vmstats:' + valueName,
- new ScalarNumeric(sizeInBytes_smallerIsBetter, value),
- valueSpec.descriptionPrefix);
- });
+ // Add memory:<browser-name>:<process-name>:reported_by_os:
+ // system_memory:... values.
+ tr.b.iterItems(
+ SYSTEM_VALUE_COMPONENTS,
+ function(componentName, componentSpec) {
+ tr.b.iterItems(
+ SYSTEM_VALUE_PROPERTIES,
+ function(propertyName, propertySpec) {
+ var node = getDescendantVmRegionClassificationNode(
+ processDump.vmRegions,
+ componentSpec.classificationPath);
+ var componentPath = ['system_memory'];
+ if (componentName)
+ componentPath.push(componentName);
+ addProcessScalar({
+ source: 'reported_by_os',
+ component: componentPath,
+ property: propertyName,
+ value: node === undefined ?
+ 0 : (node.byteStats[propertySpec.byteStat] || 0),
+ unit: sizeInBytes_smallerIsBetter,
+ descriptionPrefixBuilder:
+ propertySpec.descriptionPrefixBuilder
+ });
+ });
+ });
- // Add memory:<browser-name>:<process-name>:subsystem:v8:
+ // Add memory:<browser-name>:<process-name>:reported_by_os:
+ // gpu_memory:... values.
+ var memtrackDump = processDump.getMemoryAllocatorDumpByFullName(
+ 'gpu/android_memtrack');
+ if (memtrackDump !== undefined) {
+ var descriptionPrefixBuilder = SYSTEM_VALUE_PROPERTIES[
+ 'proportional_resident_size'].descriptionPrefixBuilder;
+ memtrackDump.children.forEach(function(memtrackChildDump) {
+ var childName = memtrackChildDump.name;
+ addProcessScalar({
+ source: 'reported_by_os',
+ component: ['gpu_memory', childName],
+ property: 'proportional_resident_size',
+ value: memtrackChildDump.numerics['memtrack_pss'],
+ descriptionPrefixBuilder: descriptionPrefixBuilder
+ });
+ });
+ }
+
+ // Add memory:<browser-name>:<process-name>:reported_by_chrome:v8:
// code_and_metadata_size when available.
var v8Dump = processDump.getMemoryAllocatorDumpByFullName('v8');
if (v8Dump !== undefined) {
- var CODE_SIZE_METRIC = 'subsystem:v8:code_and_metadata_size';
// V8 generates bytecode when interpreting and code objects when
// compiling the javascript. Total code size includes the size
// of code and bytecode objects.
- addProcessScalar(
- CODE_SIZE_METRIC,
- v8Dump.numerics['code_and_metadata_size']);
- addProcessScalar(
- CODE_SIZE_METRIC,
- v8Dump.numerics['bytecode_and_metadata_size']);
+ addProcessScalar({
+ source: 'reported_by_chrome',
+ component: ['v8'],
+ property: 'code_and_metadata_size',
+ value: v8Dump.numerics['code_and_metadata_size'],
+ descriptionPrefixBuilder:
+ buildCodeAndMetadataSizeValueDescriptionPrefix
+ });
+ addProcessScalar({
+ source: 'reported_by_chrome',
+ component: ['v8'],
+ property: 'code_and_metadata_size',
+ value: v8Dump.numerics['bytecode_and_metadata_size'],
+ descriptionPrefixBuilder:
+ buildCodeAndMetadataSizeValueDescriptionPrefix
+ });
+ }
+ }, function(componentTree) {}, values);
+ }
+
+ // Specifications of components reported by the system.
+ var SYSTEM_VALUE_COMPONENTS = {
+ '': {
+ classificationPath: [],
+ },
+ 'java_heap': {
+ classificationPath: ['Android', 'Java runtime', 'Spaces'],
+ userFriendlyName: 'the Java heap'
+ },
+ 'ashmem': {
+ classificationPath: ['Android', 'Ashmem'],
+ userFriendlyName: 'ashmem'
+ },
+ 'native_heap': {
+ classificationPath: ['Native heap'],
+ userFriendlyName: 'the native heap'
+ }
+ };
+
+ // Specifications of properties reported by the system.
+ var SYSTEM_VALUE_PROPERTIES = {
+ 'proportional_resident_size': {
+ byteStat: 'proportionalResident',
+ descriptionPrefixBuilder: buildOsValueDescriptionPrefix.bind(
+ undefined, 'proportional resident size (PSS)')
+ },
+ 'private_dirty_size': {
+ byteStat: 'privateDirtyResident',
+ descriptionPrefixBuilder: buildOsValueDescriptionPrefix.bind(
+ undefined, 'private dirty size')
+ }
+ };
+
+ /**
+ * Build a description prefix for a memory:<browser-name>:<process-name>:
+ * reported_by_os:... value.
+ *
+ * @param {string} userFriendlyPropertyName User-friendly name of the
+ * underlying property (e.g. 'private dirty size').
+ * @param {!Array<string>} componentPath The underlying component path (e.g.
+ * ['system', 'java_heap']).
+ * @param {string} processName The canonical name of the process.
+ * @return {string} Prefix for the value's description (e.g.
+ * 'total private dirty size of the Java heal in the GPU process').
+ */
+ function buildOsValueDescriptionPrefix(
+ userFriendlyPropertyName, componentPath, processName) {
+ if (componentPath.length > 2) {
+ throw new Error('OS value component path for \'' +
+ userFriendlyPropertyName + '\' too long: ' + componentPath.join(':'));
+ }
+
+ var nameParts = [];
+ if (componentPath.length < 2)
+ nameParts.push('total');
+
+ nameParts.push(userFriendlyPropertyName);
+
+ if (componentPath.length > 0) {
+ switch (componentPath[0]) {
+ case 'system_memory':
+ if (componentPath.length > 1) {
+ var userFriendlyComponentName =
+ SYSTEM_VALUE_COMPONENTS[componentPath[1]].userFriendlyName;
+ if (userFriendlyComponentName === undefined) {
+ throw new Error('System value sub-component for \'' +
+ userFriendlyPropertyName + '\' unknown: ' +
+ componentPath.join(':'));
+ }
+ nameParts.push('of', userFriendlyComponentName, 'in');
+ } else {
+ nameParts.push('of system memory (RAM) used by');
+ }
+ break;
+
+ case 'gpu_memory':
+ if (componentPath.length > 1) {
+ nameParts.push('of the', componentPath[1]);
+ nameParts.push('Android memtrack component in');
+ } else {
+ nameParts.push('of GPU memory (Android memtrack) used by');
}
+ break;
+
+ default:
+ throw new Error('OS value component for \'' +
+ userFriendlyPropertyName + '\' unknown: ' +
+ componentPath.join(':'));
+ }
+ } else {
+ nameParts.push('reported by the OS for');
+ }
- }, values, model);
+ nameParts.push(convertProcessNameToUserFriendlyName(processName));
+ return nameParts.join(' ');
+ }
+
+ /**
+ * Build a description prefix for a memory:<browser-name>:<process-name>:
+ * reported_by_chrome:...:code_and_metadata_size value.
+ *
+ * @param {!Array<string>} componentPath The underlying component path (e.g.
+ * ['v8']).
+ * @param {string} processName The canonical name of the process.
+ * @return {string} Prefix for the value's description (e.g.
+ * 'size of v8 code and metadata in').
+ */
+ function buildCodeAndMetadataSizeValueDescriptionPrefix(
+ componentPath, processName) {
+ return buildChromeValueDescriptionPrefix({
+ userFriendlyPropertyNamePrefix: 'size of',
+ userFriendlyPropertyName: 'code and metadata'
+ }, componentPath, processName);
}
/**
@@ -374,11 +595,11 @@ tr.exportTo('tr.metrics.sh', function() {
}
/**
- * Add global memory dump counts in |model| to |values|. In particular,
- * this function adds the following values:
+ * Add global memory dump counts to |values|. In particular, this function
+ * adds the following values:
*
* * DUMP COUNTS
- * memory:{chrome, webview}:all:dump_count:{light, detailed, total}
+ * memory:{chrome, webview}:all_processes:dump_count[:{light, detailed}]
* type: tr.v.ScalarNumeric (scalar over the whole trace)
* unit: unitlessNumber_smallerIsBetter
*
@@ -387,17 +608,16 @@ tr.exportTo('tr.metrics.sh', function() {
* because it doesn't make sense to aggregate them (they are already counts
* over all global dumps associated with the relevant browser).
*/
- function addMemoryDumpCountValues(
- browserNameToGlobalDumps, values, model) {
+ function addMemoryDumpCountValues(browserNameToGlobalDumps, values) {
browserNameToGlobalDumps.forEach(function(globalDumps, browserName) {
- var levelOfDetailNameToDumpCount = { 'total': 0 };
+ var totalDumpCount = 0;
+ var levelOfDetailNameToDumpCount = {};
LEVEL_OF_DETAIL_NAMES.forEach(function(levelOfDetailName) {
levelOfDetailNameToDumpCount[levelOfDetailName] = 0;
});
globalDumps.forEach(function(globalDump) {
- // Increment the total dump count.
- levelOfDetailNameToDumpCount.total++;
+ totalDumpCount++;
// Increment the level-of-detail-specific dump count (if possible).
var levelOfDetailName =
@@ -407,32 +627,51 @@ tr.exportTo('tr.metrics.sh', function() {
levelOfDetailNameToDumpCount[levelOfDetailName]++;
});
- // Add memory:<browser-name>:dump_count:<level> value for each level of
- // detail (and total).
- var browserUserFriendlyName =
- convertBrowserNameToUserFriendlyName(browserName);
+ // Add memory:<browser-name>:all_processes:dump_count[:<level>] values.
+ reportMemoryDumpCountAsValue(browserName, undefined /* total */,
+ totalDumpCount, values);
tr.b.iterItems(levelOfDetailNameToDumpCount,
function(levelOfDetailName, levelOfDetailDumpCount) {
- var description = [
- 'total number of',
- levelOfDetailName === 'total' ? 'all' : levelOfDetailName,
- 'memory dumps added by',
- browserUserFriendlyName,
- 'to the trace'
- ].join(' ');
- values.addValue(new tr.v.NumericValue(
- ['memory', browserName, ALL_PROCESS_NAMES, 'dump_count',
- levelOfDetailName].join(':'),
- new ScalarNumeric(
- unitlessNumber_smallerIsBetter, levelOfDetailDumpCount),
- { description: description }));
+ reportMemoryDumpCountAsValue(browserName, levelOfDetailName,
+ levelOfDetailDumpCount, values);
});
});
}
/**
+ * Add a tr.v.ScalarNumeric value to |values| reporting that the number of
+ * |levelOfDetailName| memory dumps added by |browserName| was
+ * |levelOfDetailCount|.
+ */
+ function reportMemoryDumpCountAsValue(
+ browserName, levelOfDetailName, levelOfDetailDumpCount, values) {
+ // Construct the name of the memory value.
+ var nameParts = ['memory', browserName, 'all_processes', 'dump_count'];
+ if (levelOfDetailName !== undefined)
+ nameParts.push(levelOfDetailName);
+ var name = nameParts.join(':');
+
+ // Build the underlying numeric for the memory value.
+ var numeric = new ScalarNumeric(
+ unitlessNumber_smallerIsBetter, levelOfDetailDumpCount);
+
+ // Build the options for the memory value.
+ var description = [
+ 'total number of',
+ levelOfDetailName || 'all',
+ 'memory dumps added by',
+ convertBrowserNameToUserFriendlyName(browserName),
+ 'to the trace'
+ ].join(' ');
+ var options = { description: description };
+
+ // Report the memory value.
+ values.addValue(new tr.v.NumericValue(name, numeric, options));
+ }
+
+ /**
* Add generic values extracted from process memory dumps and aggregated by
- * browser and process name into |values|.
+ * process name and component path into |values|.
*
* For each browser and set of global dumps in |browserNameToGlobalDumps|,
* |customProcessDumpValueExtractor| is applied to every process memory dump
@@ -441,41 +680,99 @@ tr.exportTo('tr.metrics.sh', function() {
*
* function sampleProcessDumpCallback(processDump, addProcessValue) {
* ...
- * addProcessValue('value_name_1', valueExtractedFromProcessDump1);
- * ...
- * addProcessValue('value_name_2', valueExtractedFromProcessDump2);
+ * addProcessScalar({
+ * source: 'reported_by_chrome',
+ * component: ['system', 'native_heap'],
+ * property: 'proportional_resident_size',
+ * value: pssExtractedFromProcessDump2,
+ * descriptionPrefixBuilder: function(componentPath) {
+ * return 'PSS of ' + componentPath.join('/') + ' in';
+ * }
+ * });
* ...
* }
*
* For each global memory dump, the extracted values are summed by process
- * name (browser, renderer, ..., all). The sums are then aggregated over all
- * global memory dumps associated with the given browser. For example,
- * assuming that |customProcessDumpValueExtractor| extracts a value called
- * 'x' from each process memory dump, the following values will be reported:
- *
- * memory:<browser-name>:browser:x : tr.v.Numeric aggregated over [
- * sum of 'x' in all 'browser' process dumps in global dump 1,
- * sum of 'x' in all 'browser' process dumps in global dump 2,
- * ...
- * sum of 'x' in all 'browser' process dumps in global dump N
+ * name (browser_process, renderer_processes, ..., all_processes) and
+ * component path (e.g. gpu is a sum of gpu:gl, gpu:graphics, ...). The sums
+ * are then aggregated over all global memory dumps associated with the given
+ * browser. For example, assuming that |customProcessDumpValueExtractor|
+ * extracts 'proportional_resident_size' values for component paths
+ * ['X', 'A'], ['X', 'B'] and ['Y'] under the same 'source' from each process
+ * memory dump, the following values will be reported (for Chrome):
+ *
+ * memory:chrome:browser_process:source:X:A:proportional_resident_size :
+ * Numeric aggregated over [
+ * sum of X:A in all 'browser' process dumps in global dump 1,
+ * ...
+ * sum of X:A in all 'browser' process dumps in global dump N
+ * ]
+ *
+ * memory:chrome:browser_process:source:X:B:proportional_resident_size :
+ * Numeric aggregated over [
+ * sum of X:B in all 'browser' process dumps in global dump 1,
+ * ...
+ * sum of X:B in all 'browser' process dumps in global dump N
+ * ]
+ *
+ * memory:chrome:browser_process:source:X:proportional_resident_size :
+ * Numeric aggregated over [
+ * sum of X:A+X:B in all 'browser' process dumps in global dump 1,
+ * ...
+ * sum of X:A+X:B in all 'browser' process dumps in global dump N
+ * ]
+ *
+ * memory:chrome:browser_process:source:Y:proportional_resident_size :
+ * Numeric aggregated over [
+ * sum of Y in all 'browser' process dumps in global dump 1,
+ * ...
+ * sum of Y in all 'browser' process dumps in global dump N
+ * ]
+ *
+ * memory:chrome:browser_process:source:proportional_resident_size :
+ * Numeric aggregated over [
+ * sum of X:A+X:B+Y in all 'browser' process dumps in global dump 1,
+ * ...
+ * sum of X:A+X:B+Y in all 'browser' process dumps in global dump N
+ * ]
+ *
+ * ...
+ *
+ * memory:chrome:all_processes:source:X:A:proportional_resident_size :
+ * Numeric aggregated over [
+ * sum of X:A in all process dumps in global dump 1,
+ * ...
+ * sum of X:A in all process dumps in global dump N,
* ]
*
- * memory:<browser-name>:renderer:x : tr.v.Numeric aggregated over [
- * sum of 'x' in all 'renderer' process dumps in global dump 1,
- * sum of 'x' in all 'renderer' process dumps in global dump 2,
- * ...
- * sum of 'x' in all 'renderer' process dumps in global dump N
+ * memory:chrome:all_processes:source:X:B:proportional_resident_size :
+ * Numeric aggregated over [
+ * sum of X:B in all process dumps in global dump 1,
+ * ...
+ * sum of X:B in all process dumps in global dump N,
* ]
*
- * ...
+ * memory:chrome:all_processes:source:X:proportional_resident_size :
+ * Numeric aggregated over [
+ * sum of X:A+X:B in all process dumps in global dump 1,
+ * ...
+ * sum of X:A+X:B in all process dumps in global dump N,
+ * ]
*
- * memory:<browser-name>:all:x : tr.v.Numeric aggregated over [
- * sum of 'x' in all process dumps in global dump 1,
- * sum of 'x' in all process dumps in global dump 2,
- * ...
- * sum of 'x' in all process dumps in global dump N,
+ * memory:chrome:all_processes:source:Y:proportional_resident_size :
+ * Numeric aggregated over [
+ * sum of Y in all process dumps in global dump 1,
+ * ...
+ * sum of Y in all process dumps in global dump N
* ]
*
+ * memory:chrome:all_processes:source:proportional_resident_size :
+ * Numeric aggregated over [
+ * sum of X:A+X:B+Y in all process dumps in global dump 1,
+ * ...
+ * sum of X:A+X:B+Y in all process dumps in global dump N
+ * ]
+ *
* where global dumps 1 to N are the global dumps associated with the given
* browser.
*
@@ -487,32 +784,31 @@ tr.exportTo('tr.metrics.sh', function() {
* customGlobalDumpFilter Predicate for filtering global memory dumps.
* @param {!function(
* !tr.model.ProcessMemoryDump,
- * !function(string, !tr.v.ScalarNumeric))}
+ * !function(!{
+ * source: string,
+ * componentPath: (!Array<string>|undefined),
+ * propertyName: (string|undefined),
+ * value: (!tr.v.ScalarNumeric|number|undefined),
+ * unit: (!tr.v.Unit|undefined),
+ * descriptionPrefixBuilder: (!function(!Array<string>): string)
+ * }))}
* customProcessDumpValueExtractor Callback for extracting values from a
* process memory dump.
+ * @param {!function(!tr.b.MultiDimensionalViewNode)}
+ * customComponentTreeModifier Callback applied to every component tree
+ * wrt each process name.
* @param {!tr.metrics.ValueSet} values List of values to which the
* resulting aggregated values are added.
- * @param {!tr.Model} model The underlying trace model.
*/
- function addPerProcessNameMemoryDumpValues(
- browserNameToGlobalDumps, customGlobalDumpFilter,
- customProcessDumpValueExtractor, values, model) {
+ function addMemoryDumpValues(browserNameToGlobalDumps, customGlobalDumpFilter,
+ customProcessDumpValueExtractor, customComponentTreeModifier,
+ values) {
browserNameToGlobalDumps.forEach(function(globalDumps, browserName) {
var filteredGlobalDumps = globalDumps.filter(customGlobalDumpFilter);
-
- // Value name -> {unit: tr.v.Unit, descriptionPrefix: string}.
- var valueNameToSpec = {};
-
- // Global memory dump timestamp (list index) -> Process name ->
- // Value name -> number.
- var timeToProcessNameToValueNameToSum =
- calculatePerProcessNameMemoryDumpValues(filteredGlobalDumps,
- valueNameToSpec, customProcessDumpValueExtractor);
-
- injectTotalsIntoPerProcessNameMemoryDumpValues(
- timeToProcessNameToValueNameToSum);
- reportPerProcessNameMemoryDumpValues(timeToProcessNameToValueNameToSum,
- valueNameToSpec, browserName, values, model);
+ var sourceToPropertyToData = extractDataFromGlobalDumps(
+ filteredGlobalDumps, customProcessDumpValueExtractor);
+ reportDataAsValues(sourceToPropertyToData, browserName,
+ customComponentTreeModifier, values);
});
}
@@ -521,141 +817,196 @@ tr.exportTo('tr.metrics.sh', function() {
* sums of values extracted by |customProcessDumpValueExtractor| from the
* associated process memory dumps.
*
- * This function returns the following list of nested maps:
+ * This function returns the following nested map structure:
*
- * Global memory dump timestamp (list index)
- * -> Process name (dict with keys 'browser', 'renderer', ...)
- * -> Value name (dict with keys 'subsystem:v8', ...)
- * -> Sum of value over the processes (number).
+ * Source name (Map key, e.g. 'reported_by_os')
+ * -> Property name (Map key, e.g. 'proportional_resident_size')
+ * -> {unit, descriptionPrefixBuilder, processAndComponentTreeBuilder}
*
- * and updates the |valueNameToSpec| argument to be a map from the names of
- * the extracted values to specifications:
+ * where |processAndComponentTreeBuilder| is a
+ * tr.b.MultiDimensionalViewBuilder:
*
- * Value name (dict with keys 'subsystem:v8', ...)
- * -> { unit: tr.v.Unit, descriptionPrefix: string }.
+ * Browser name (0th dimension key, e.g. 'webview') x
+ * -> Component path (1st dimension keys, e.g. ['system', 'native_heap'])
+ * -> Sum of value over the processes (number).
*
- * See addPerProcessNameMemoryDumpValues for more details.
+ * See addMemoryDumpValues for more details.
*/
- function calculatePerProcessNameMemoryDumpValues(
- globalDumps, valueNameToSpec, customProcessDumpValueExtractor) {
- return globalDumps.map(function(globalDump) {
- // Process name -> Value name -> Sum over processes.
- var processNameToValueNameToSum = {};
-
+ function extractDataFromGlobalDumps(
+ globalDumps, customProcessDumpValueExtractor) {
+ var sourceToPropertyToData = new Map();
+ var dumpCount = globalDumps.length;
+ globalDumps.forEach(function(globalDump, dumpIndex) {
tr.b.iterItems(globalDump.processMemoryDumps, function(_, processDump) {
- // Process name is typically 'browser', 'renderer', etc.
- var rawProcessName = processDump.process.name || 'unknown';
- var processName = canonicalizeName(rawProcessName);
-
- // Value name -> Sum over processes.
- var valueNameToSum = processNameToValueNameToSum[processName];
- if (valueNameToSum === undefined)
- processNameToValueNameToSum[processName] = valueNameToSum = {};
-
- customProcessDumpValueExtractor(
- processDump,
- function addProcessScalar(
- name, processDumpScalar, descriptionPrefix) {
- if (processDumpScalar === undefined)
- return;
- var valueSpec = valueNameToSpec[name];
- if (valueSpec === undefined) {
- valueNameToSpec[name] = valueSpec = {
- unit: processDumpScalar.unit,
- descriptionPrefix: descriptionPrefix
- };
- } else {
- if (processDumpScalar.unit !== valueSpec.unit) {
- throw new Error('Multiple units provided for value \'' +
- name + '\': ' + valueSpec.unit.unitName + ' and ' +
- processDumpScalar.unit.unitName);
- }
- if (descriptionPrefix !== valueSpec.descriptionPrefix) {
- throw new Error('Multiple description prefixes provided ' +
- 'for value \'' + name + '\': \'' +
- valueSpec.descriptionPrefix + '\' and \'' +
- descriptionPrefix + '\'');
- }
- }
- valueNameToSum[name] = (valueNameToSum[name] || 0) +
- processDumpScalar.value;
- });
+ extractDataFromProcessDump(
+ processDump, sourceToPropertyToData, dumpIndex, dumpCount,
+ customProcessDumpValueExtractor);
});
- return processNameToValueNameToSum;
});
+ return sourceToPropertyToData;
}
- /**
- * For each timestamp (corresponding to a global memory dump) in
- * |timeToProcessNameToValueNameToSum|, sum per-process-name sums into total
- * sums over all process names.
- *
- * See addPerProcessNameMemoryDumpValues for more details.
- */
- function injectTotalsIntoPerProcessNameMemoryDumpValues(
- timeToProcessNameToValueNameToSum) {
- timeToProcessNameToValueNameToSum.forEach(
- function(processNameToValueNameToSum) {
- var valueNameToProcessNameToSum = tr.b.invertArrayOfDicts(
- tr.b.dictionaryValues(processNameToValueNameToSum));
- processNameToValueNameToSum[ALL_PROCESS_NAMES] = tr.b.mapItems(
- valueNameToProcessNameToSum,
- function(valueName, perProcessSums) {
- return perProcessSums.reduce((acc, sum) => acc + sum, 0);
- });
+ function extractDataFromProcessDump(processDump, sourceToPropertyToData,
+ dumpIndex, dumpCount, customProcessDumpValueExtractor) {
+ // Process name is typically 'browser', 'renderer', etc.
+ var rawProcessName = processDump.process.name || 'unknown';
+ var processNamePath = [canonicalizeProcessName(rawProcessName)];
+
+ customProcessDumpValueExtractor(
+ processDump,
+ function addProcessScalar(spec) {
+ if (spec.value === undefined)
+ return;
+
+ var component = spec.component || [];
+ function createDetailsForErrorMessage() {
+ var propertyUserFriendlyName =
+ spec.property === undefined ? '(undefined)' : spec.property;
+ var componentUserFriendlyName =
+ component.length === 0 ? '(empty)' : component.join(':');
+ return ['source=', spec.source, ', property=',
+ propertyUserFriendlyName, ', component=',
+ componentUserFriendlyName, ' in ',
+ processDump.process.userFriendlyName].join('');
+ }
+
+ var value, unit;
+ if (spec.value instanceof ScalarNumeric) {
+ value = spec.value.value;
+ unit = spec.value.unit;
+ if (spec.unit !== undefined) {
+ throw new Error('ScalarNumeric value for ' +
+ createDetailsForErrorMessage() + ' already specifies a unit');
+ }
+ } else {
+ value = spec.value;
+ unit = spec.unit;
+ }
+
+ var propertyToData = sourceToPropertyToData.get(spec.source);
+ if (propertyToData === undefined) {
+ propertyToData = new Map();
+ sourceToPropertyToData.set(spec.source, propertyToData);
+ }
+
+ var data = propertyToData.get(spec.property);
+ if (data === undefined) {
+ data = {
+ processAndComponentTreeBuilder:
+ new tr.b.MultiDimensionalViewBuilder(
+ 2 /* dimensions (process name and component path) */,
+ dumpCount /* valueCount */),
+ unit: unit,
+ descriptionPrefixBuilder: spec.descriptionPrefixBuilder
+ };
+ propertyToData.set(spec.property, data);
+ } else if (data.unit !== unit) {
+ throw new Error('Multiple units provided for ' +
+ createDetailsForErrorMessage() + ':' +
+ data.unit.unitName + ' and ' + unit.unitName);
+ } else if (data.descriptionPrefixBuilder !==
+ spec.descriptionPrefixBuilder) {
+ throw new Error(
+ 'Multiple description prefix builders provided for' +
+ createDetailsForErrorMessage());
+ }
+
+ var values = new Array(dumpCount);
+ values[dumpIndex] = value;
+
+ data.processAndComponentTreeBuilder.addPath(
+ [processNamePath, component] /* path */, values,
+ tr.b.MultiDimensionalViewBuilder.ValueKind.TOTAL /* valueKind */);
});
}
+ function reportDataAsValues(sourceToPropertyToData, browserName,
+ customComponentTreeModifier, values) {
+ // For each source name (e.g. 'reported_by_os')...
+ sourceToPropertyToData.forEach(function(propertyToData, sourceName) {
+ // For each property name (e.g. 'effective_size')...
+ propertyToData.forEach(function(data, propertyName) {
+ var tree = data.processAndComponentTreeBuilder.buildTopDownTreeView();
+ var unit = data.unit;
+ var descriptionPrefixBuilder = data.descriptionPrefixBuilder;
+
+ // Total over 'all' processes...
+ customComponentTreeModifier(tree);
+ reportComponentDataAsValues(browserName, sourceName,
+ propertyName, 'all_processes', [] /* componentPath */, tree,
+ unit, descriptionPrefixBuilder, values);
+
+ // For each process name (e.g. 'renderer')...
+ tree.children[0].forEach(function(processTree, processName) {
+ if (processTree.children[0].size > 0) {
+ throw new Error('Multi-dimensional view node for source=' +
+ sourceName + ', property=' +
+ (propertyName === undefined ? '(undefined)' : propertyName) +
+ ', process=' + processName +
+ ' has children wrt the process name dimension');
+ }
+ customComponentTreeModifier(processTree);
+ reportComponentDataAsValues(browserName, sourceName,
+ propertyName, processName, [] /* componentPath */, processTree,
+ unit, descriptionPrefixBuilder, values);
+ });
+ });
+ });
+ }
+
/**
- * For each process name (plus total over 'all' process names) and value
- * name, add a tr.v.Numeric aggregating the associated values across all
- * timestamps (corresponding to global memory dumps associated with the given
- * browser) in |timeToProcessNameToValueNameToSum| to |values|.
+ * For the given |browserName| (e.g. 'chrome'), |processName|
+ * (e.g. 'gpu_process'), |propertyName| (e.g. 'effective_size'),
+ * |componentPath| (e.g. ['v8']), add a tr.v.Numeric with |unit| aggregating
+ * the total values of the associated |componentNode| across all timestamps
+ * (corresponding to global memory dumps associated with the given browser)
+ * to |values|.
*
- * See addPerProcessNameMemoryDumpValues for more details.
+ * See addMemoryDumpValues for more details.
*/
- function reportPerProcessNameMemoryDumpValues(
- timeToProcessNameToValueNameToSum, valueNameToSpec, browserName, values,
- model) {
- var browserUserFriendlyName =
- convertBrowserNameToUserFriendlyName(browserName);
- var processNameToTimeToValueNameToSum =
- tr.b.invertArrayOfDicts(timeToProcessNameToValueNameToSum);
- tr.b.iterItems(
- processNameToTimeToValueNameToSum,
- function(processName, timeToValueNameToSum) {
- var processPluralUserFriendlyName =
- convertProcessNameToPluralUserFriendlyName(processName);
- var valueNameToTimeToSum =
- tr.b.invertArrayOfDicts(timeToValueNameToSum);
- tr.b.iterItems(
- valueNameToTimeToSum,
- function(valueName, timeToSum) {
- var valueSpec = valueNameToSpec[valueName];
- var description = [
- valueSpec.descriptionPrefix,
- processPluralUserFriendlyName,
- 'in',
- browserUserFriendlyName
- ].join(' ');
- values.addValue(new tr.v.NumericValue(
- ['memory', browserName, processName, valueName].join(':'),
- buildMemoryNumeric(timeToSum, valueSpec.unit),
- { description: description }));
- });
- });
+ function reportComponentDataAsValues(
+ browserName, sourceName, propertyName, processName, componentPath,
+ componentNode, unit, descriptionPrefixBuilder, values) {
+ // Construct the name of the memory value.
+ var nameParts = ['memory', browserName, processName, sourceName].concat(
+ componentPath);
+ if (propertyName !== undefined)
+ nameParts.push(propertyName);
+ var name = nameParts.join(':');
+
+ // Build the underlying numeric for the memory value.
+ var numeric = buildMemoryNumericFromNode(componentNode, unit);
+
+ // Build the options for the memory value.
+ var description = [
+ descriptionPrefixBuilder(componentPath, processName),
+ 'in',
+ convertBrowserNameToUserFriendlyName(browserName)
+ ].join(' ');
+ var options = { description: description };
+
+ // Report the memory value.
+ values.addValue(new tr.v.NumericValue(name, numeric, options));
+
+ // Recursively report memory values for sub-components.
+ var depth = componentPath.length;
+ componentPath.push(undefined);
+ componentNode.children[1].forEach(function(childNode, childName) {
+ componentPath[depth] = childName;
+ reportComponentDataAsValues(
+ browserName, sourceName, propertyName, processName, componentPath,
+ childNode, unit, descriptionPrefixBuilder, values);
+ });
+ componentPath.pop();
}
/**
- * Create a memory tr.v.Numeric (histogram) for |unit| and add all |sums| to
- * it.
- *
- * Undefined items in |sums| are treated as zeros.
+ * Create a memory tr.v.Numeric (histogram) with |unit| and add all total
+ * values in |node| to it.
*/
- function buildMemoryNumeric(sums, unit) {
+ function buildMemoryNumericFromNode(node, unit) {
var numeric = MEMORY_NUMERIC_BUILDER_MAP.get(unit).build();
- for (var i = 0; i < sums.length; i++)
- numeric.add(sums[i] || 0);
+ node.values.forEach(v => numeric.add(v.total));
return numeric;
}
« no previous file with comments | « no previous file | tracing/tracing/metrics/system_health/memory_metric_test.html » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698