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/model/container_memory_dump.html">
9<link rel="import" href="/tracing/model/memory_allocator_dump.html">
10<link rel="import" href="/tracing/model/vm_region.html">
11<link rel="import" href="/tracing/value/unit.html">
12
13<script>
14'use strict';
15
16/**
17 * @fileoverview Provides the ProcessMemoryDump class.
18 */
19tr.exportTo('tr.model', function() {
20
21  // Names of MemoryAllocatorDump(s) from which tracing overhead should be
22  // discounted.
23  var DISCOUNTED_ALLOCATOR_NAMES = ['winheap', 'malloc'];
24
25  // The path to where the tracing overhead dump should be added to the
26  // winheap/malloc allocator dump tree.
27  var TRACING_OVERHEAD_PATH = ['allocated_objects', 'tracing_overhead'];
28
29  var SIZE_NUMERIC_NAME = tr.model.MemoryAllocatorDump.SIZE_NUMERIC_NAME;
30  var RESIDENT_SIZE_NUMERIC_NAME =
31      tr.model.MemoryAllocatorDump.RESIDENT_SIZE_NUMERIC_NAME;
32
33  function getSizeNumericValue(dump, sizeNumericName) {
34    var sizeNumeric = dump.numerics[sizeNumericName];
35    if (sizeNumeric === undefined)
36      return 0;
37    return sizeNumeric.value;
38  }
39
40  /**
41   * The ProcessMemoryDump represents a memory dump of a single process.
42   * @constructor
43   */
44  function ProcessMemoryDump(globalMemoryDump, process, start) {
45    tr.model.ContainerMemoryDump.call(this, start);
46    this.process = process;
47    this.globalMemoryDump = globalMemoryDump;
48
49    // Process memory totals (optional object) with the following fields (also
50    // optional):
51    //   - residentBytes: Total resident bytes (number)
52    //   - peakResidentBytes: Peak resident bytes (number)
53    //   - arePeakResidentBytesResettable: Flag whether peak resident bytes are
54    //     resettable (boolean)
55    //   - platformSpecific: Map from OS-specific total names (string) to sizes
56    //     (number)
57    this.totals = undefined;
58
59    this.vmRegions = undefined;
60
61    // Map from allocator names to heap dumps.
62    this.heapDumps = undefined;
63
64    this.tracingOverheadOwnershipSetUp_ = false;
65    this.tracingOverheadDiscountedFromVmRegions_ = false;
66  };
67
68  ProcessMemoryDump.prototype = {
69    __proto__: tr.model.ContainerMemoryDump.prototype,
70
71    get userFriendlyName() {
72      return 'Process memory dump at ' +
73          tr.v.Unit.byName.timeStampInMs.format(this.start);
74    },
75
76    get containerName() {
77      return this.process.userFriendlyName;
78    },
79
80    get processMemoryDumps() {
81      var dumps = {};
82      dumps[this.process.pid] = this;
83      return dumps;
84    },
85
86    get hasOwnVmRegions() {
87      return this.vmRegions !== undefined;
88    },
89
90    setUpTracingOverheadOwnership: function(opt_model) {
91      // Make sure that calling this method twice won't lead to
92      // 'double-discounting'.
93      if (this.tracingOverheadOwnershipSetUp_)
94        return;
95      this.tracingOverheadOwnershipSetUp_ = true;
96
97      var tracingDump = this.getMemoryAllocatorDumpByFullName('tracing');
98      if (tracingDump === undefined || tracingDump.owns !== undefined) {
99        // The tracing dump either doesn't exist, or it already owns another
100        // dump.
101        return;
102      }
103
104      if (tracingDump.owns !== undefined)
105        return;
106
107      // Add an ownership link from tracing to
108      // malloc/allocated_objects/tracing_overhead or
109      // winheap/allocated_objects/tracing_overhead.
110      var hasDiscountedFromAllocatorDumps = DISCOUNTED_ALLOCATOR_NAMES.some(
111          function(allocatorName) {
112        // First check if the allocator root exists.
113        var allocatorDump = this.getMemoryAllocatorDumpByFullName(
114            allocatorName);
115        if (allocatorDump === undefined)
116          return false;  // Allocator doesn't exist, try another one.
117
118        var nextPathIndex = 0;
119        var currentDump = allocatorDump;
120        var currentFullName = allocatorName;
121
122        // Descend from the root towards tracing_overhead as long as the dumps
123        // on the path exist.
124        for (; nextPathIndex < TRACING_OVERHEAD_PATH.length; nextPathIndex++) {
125          var childFullName = currentFullName + '/' +
126              TRACING_OVERHEAD_PATH[nextPathIndex];
127          var childDump = this.getMemoryAllocatorDumpByFullName(
128              childFullName);
129          if (childDump === undefined)
130            break;
131
132          currentDump = childDump;
133          currentFullName = childFullName;
134        }
135
136        // Create the missing descendant dumps on the path from the root
137        // towards tracing_overhead.
138        for (; nextPathIndex < TRACING_OVERHEAD_PATH.length; nextPathIndex++) {
139          var childFullName = currentFullName + '/' +
140              TRACING_OVERHEAD_PATH[nextPathIndex];
141          var childDump = new tr.model.MemoryAllocatorDump(
142              currentDump.containerMemoryDump, childFullName);
143          childDump.parent = currentDump;
144          currentDump.children.push(childDump);
145
146          currentFullName = childFullName;
147          currentDump = childDump;
148        }
149
150        // Add the ownership link.
151        var ownershipLink =
152            new tr.model.MemoryAllocatorDumpLink(tracingDump, currentDump);
153        tracingDump.owns = ownershipLink;
154        currentDump.ownedBy.push(ownershipLink);
155        return true;
156      }, this);
157
158      // Force rebuilding the memory allocator dump index (if we've just added
159      // a new memory allocator dump).
160      if (hasDiscountedFromAllocatorDumps)
161        this.forceRebuildingMemoryAllocatorDumpByFullNameIndex();
162    },
163
164    discountTracingOverheadFromVmRegions: function(opt_model) {
165      // Make sure that calling this method twice won't lead to
166      // 'double-discounting'.
167      if (this.tracingOverheadDiscountedFromVmRegions_)
168        return;
169      this.tracingOverheadDiscountedFromVmRegions_ = true;
170
171      var tracingDump = this.getMemoryAllocatorDumpByFullName('tracing');
172      if (tracingDump === undefined)
173        return;
174
175      var discountedSize =
176          getSizeNumericValue(tracingDump, SIZE_NUMERIC_NAME);
177      var discountedResidentSize =
178          getSizeNumericValue(tracingDump, RESIDENT_SIZE_NUMERIC_NAME);
179
180      if (discountedSize <= 0 && discountedResidentSize <= 0)
181        return;
182
183      // Subtract the tracing size from the totals.
184      if (this.totals !== undefined) {
185        if (this.totals.residentBytes !== undefined)
186          this.totals.residentBytes -= discountedResidentSize;
187        if (this.totals.peakResidentBytes !== undefined)
188          this.totals.peakResidentBytes -= discountedResidentSize;
189      }
190
191      // Subtract the tracing size from VM regions. More precisely, subtract
192      // tracing resident_size from byte stats (private dirty and PSS) and
193      // tracing size from virtual size by injecting a fake VM region with
194      // negative values.
195      if (this.vmRegions !== undefined) {
196        var hasSizeInBytes = this.vmRegions.sizeInBytes !== undefined;
197        var hasPrivateDirtyResident =
198            this.vmRegions.byteStats.privateDirtyResident !== undefined;
199        var hasProportionalResident =
200            this.vmRegions.byteStats.proportionalResident !== undefined;
201
202        if ((hasSizeInBytes && discountedSize > 0) ||
203            ((hasPrivateDirtyResident || hasProportionalResident) &&
204                discountedResidentSize > 0)) {
205          var byteStats = {};
206          if (hasPrivateDirtyResident)
207            byteStats.privateDirtyResident = -discountedResidentSize;
208          if (hasProportionalResident)
209            byteStats.proportionalResident = -discountedResidentSize;
210          this.vmRegions.addRegion(tr.model.VMRegion.fromDict({
211            mappedFile: '[discounted tracing overhead]',
212            sizeInBytes: hasSizeInBytes ? -discountedSize : undefined,
213            byteStats: byteStats
214          }));
215        }
216      }
217    }
218  };
219
220  ProcessMemoryDump.hookUpMostRecentVmRegionsLinks = function(processDumps) {
221    var mostRecentVmRegions = undefined;
222
223    processDumps.forEach(function(processDump) {
224      // Update the most recent VM regions from the current dump.
225      if (processDump.vmRegions !== undefined)
226        mostRecentVmRegions = processDump.vmRegions;
227
228      // Set the most recent VM regions of the current dump.
229      processDump.mostRecentVmRegions = mostRecentVmRegions;
230    });
231  };
232
233  tr.model.EventRegistry.register(
234      ProcessMemoryDump,
235      {
236        name: 'processMemoryDump',
237        pluralName: 'processMemoryDumps',
238        singleViewElementName: 'tr-ui-a-container-memory-dump-sub-view',
239        multiViewElementName: 'tr-ui-a-container-memory-dump-sub-view'
240      });
241
242  return {
243    ProcessMemoryDump: ProcessMemoryDump
244  };
245});
246</script>
247