1<!DOCTYPE html>
2<!--
3Copyright (c) 2015 The Chromium Authors. All rights reserved.
4Use of this source code is governed by a BSD-style license that can be
5found in the LICENSE file.
6-->
7
8<link rel="import" href="/tracing/base/iteration_helpers.html">
9<link rel="import" href="/tracing/value/numeric.html">
10<link rel="import" href="/tracing/value/unit.html">
11
12<script>
13'use strict';
14
15/**
16 * @fileoverview Provides the MemoryAllocatorDump class.
17 */
18tr.exportTo('tr.model', function() {
19  /**
20   * @constructor
21   */
22  function MemoryAllocatorDump(containerMemoryDump, fullName, opt_guid) {
23    this.fullName = fullName;
24    this.parent = undefined;
25    this.children = [];
26
27    // String -> ScalarNumeric.
28    this.numerics = {};
29
30    // String -> string.
31    this.diagnostics = {};
32
33    // The associated container memory dump.
34    this.containerMemoryDump = containerMemoryDump;
35
36    // Ownership relationship between memory allocator dumps.
37    this.owns = undefined;
38    this.ownedBy = [];
39
40    // Map from sibling dumps (other children of this dump's parent) to the
41    // proportion of this dump's size which they (or their descendants) own.
42    this.ownedBySiblingSizes = new Map();
43
44    // Retention relationship between memory allocator dumps.
45    this.retains = [];
46    this.retainedBy = [];
47
48    // Weak memory allocator dumps are removed from the model after import in
49    // tr.model.GlobalMemoryDump.removeWeakDumps(). See
50    // base::trace_event::MemoryAllocatorDump::Flags::WEAK in the Chromium
51    // codebase.
52    this.weak = false;
53
54    // A list of information about the memory allocator dump (e.g. about how
55    // its fields were calculated). Each item should be an object with
56    // a mandatory 'type' property and type-specific extra arguments (see
57    // MemoryAllocatorDumpInfoType).
58    this.infos = [];
59
60    // For debugging purposes.
61    this.guid = opt_guid;
62  };
63
64  /**
65   * Size numeric names. Please refer to the Memory Dump Graph Metric
66   * Calculation design document for more details (https://goo.gl/fKg0dt).
67   */
68  MemoryAllocatorDump.SIZE_NUMERIC_NAME = 'size';
69  MemoryAllocatorDump.EFFECTIVE_SIZE_NUMERIC_NAME = 'effective_size';
70  MemoryAllocatorDump.RESIDENT_SIZE_NUMERIC_NAME = 'resident_size';
71  MemoryAllocatorDump.DISPLAYED_SIZE_NUMERIC_NAME =
72      MemoryAllocatorDump.EFFECTIVE_SIZE_NUMERIC_NAME;
73
74  MemoryAllocatorDump.prototype = {
75    get name() {
76      return this.fullName.substring(this.fullName.lastIndexOf('/') + 1);
77    },
78
79    get quantifiedName() {
80      return '\'' + this.fullName + '\' in ' +
81          this.containerMemoryDump.containerName;
82    },
83
84    isDescendantOf: function(otherDump) {
85      var dump = this;
86      while (dump !== undefined) {
87        if (dump === otherDump)
88          return true;
89        dump = dump.parent;
90      }
91      return false;
92    },
93
94    addNumeric: function(name, numeric) {
95      if (!(numeric instanceof tr.v.ScalarNumeric))
96        throw new Error('Numeric value must be an instance of ScalarNumeric.');
97      if (name in this.numerics)
98        throw new Error('Duplicate numeric name: ' + name + '.');
99      this.numerics[name] = numeric;
100    },
101
102    addDiagnostic: function(name, text) {
103      if (typeof text !== 'string')
104        throw new Error('Diagnostic text must be a string.');
105      if (name in this.diagnostics)
106        throw new Error('Duplicate diagnostic name: ' + name + '.');
107      this.diagnostics[name] = text;
108    },
109
110    aggregateNumericsRecursively: function(opt_model) {
111      var numericNames = new Set();
112
113      // Aggregate descendants's numerics recursively and gather children's
114      // numeric names.
115      this.children.forEach(function(child) {
116        child.aggregateNumericsRecursively(opt_model);
117        tr.b.iterItems(child.numerics, numericNames.add, numericNames);
118      }, this);
119
120      // Aggregate children's numerics.
121      numericNames.forEach(function(numericName) {
122        if (numericName === MemoryAllocatorDump.SIZE_NUMERIC_NAME ||
123            numericName === MemoryAllocatorDump.EFFECTIVE_SIZE_NUMERIC_NAME ||
124            this.numerics[numericName] !== undefined) {
125            // Don't aggregate size and effective size numerics. These are
126            // calculated in GlobalMemoryDump.prototype.calculateSizes() and
127            // GlobalMemoryDump.prototype.calculateEffectiveSizes respectively.
128            // Also don't aggregate numerics that the parent already has.
129          return;
130        }
131
132        this.numerics[numericName] = MemoryAllocatorDump.aggregateNumerics(
133            this.children.map(function(child) {
134              return child.numerics[numericName];
135            }), opt_model);
136      }, this);
137    }
138  };
139
140  // TODO(petrcermak): Consider moving this to tr.v.Numeric.
141  MemoryAllocatorDump.aggregateNumerics = function(numerics, opt_model) {
142    var shouldLogWarning = !!opt_model;
143    var aggregatedUnit = undefined;
144    var aggregatedValue = 0;
145
146    // Aggregate the units and sum up the values of the numerics.
147    numerics.forEach(function(numeric) {
148      if (numeric === undefined)
149        return;
150
151      var unit = numeric.unit;
152      if (aggregatedUnit === undefined) {
153        aggregatedUnit = unit;
154      } else if (aggregatedUnit !== unit) {
155        if (shouldLogWarning) {
156          opt_model.importWarning({
157            type: 'numeric_parse_error',
158            message: 'Multiple units provided for numeric: \'' +
159                aggregatedUnit.unitName + '\' and \'' + unit.unitName + '\'.'
160          });
161          shouldLogWarning = false;  // Don't log multiple warnings.
162        }
163        // Use the most generic unit when the numerics don't agree (best
164        // effort).
165        aggregatedUnit = tr.v.Unit.byName.unitlessNumber_smallerIsBetter;
166      }
167
168      aggregatedValue += numeric.value;
169    }, this);
170
171    if (aggregatedUnit === undefined)
172      return undefined;
173
174    return new tr.v.ScalarNumeric(aggregatedUnit, aggregatedValue);
175  };
176
177  /**
178   * @constructor
179   */
180  function MemoryAllocatorDumpLink(source, target, opt_importance) {
181    this.source = source;
182    this.target = target;
183    this.importance = opt_importance;
184    this.size = undefined;
185  }
186
187  /**
188   * Types of size numeric information.
189   *
190   * @enum
191   */
192  var MemoryAllocatorDumpInfoType = {
193    // The provided size of a MemoryAllocatorDump was less than the aggregated
194    // size of its children.
195    //
196    // Mandatory extra args:
197    //   * providedSize: The inconsistent provided size.
198    //   * dependencySize: The aggregated size of the children.
199    PROVIDED_SIZE_LESS_THAN_AGGREGATED_CHILDREN: 0,
200
201    // The provided size of a MemoryAllocatorDump was less than the size of its
202    // largest owner.
203    //
204    // Mandatory extra args:
205    //   * providedSize: The inconsistent provided size.
206    //   * dependencySize: The size of the largest owner.
207    PROVIDED_SIZE_LESS_THAN_LARGEST_OWNER: 1
208  };
209
210  return {
211    MemoryAllocatorDump: MemoryAllocatorDump,
212    MemoryAllocatorDumpLink: MemoryAllocatorDumpLink,
213    MemoryAllocatorDumpInfoType: MemoryAllocatorDumpInfoType
214  };
215});
216</script>
217