process_memory_dump.html revision 46b43bff003ceda46cf9a5d40a47f7674996d2e0
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/units/time_stamp.html">
9<link rel="import" href="/tracing/model/attribute.html">
10<link rel="import" href="/tracing/model/container_memory_dump.html">
11<link rel="import" href="/tracing/model/memory_allocator_dump.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  /**
26   * The ProcessMemoryDump represents a memory dump of a single process.
27   * @constructor
28   */
29  function ProcessMemoryDump(globalMemoryDump, process, start) {
30    tr.model.ContainerMemoryDump.call(this, start);
31    this.process = process;
32    this.globalMemoryDump = globalMemoryDump;
33
34    this.totalResidentBytes = undefined;
35    this.vmRegions_ = undefined;
36
37    this.tracingMemoryDiscounted_ = false;
38  };
39
40  ProcessMemoryDump.prototype = {
41    __proto__: tr.model.ContainerMemoryDump.prototype,
42
43    get userFriendlyName() {
44      return 'Process memory dump at ' +
45          tr.b.u.TimeStamp.format(this.start);
46    },
47
48    get containerName() {
49      return this.process.userFriendlyName;
50    },
51
52    get vmRegions() {
53      throw new Error(
54          'VM regions must be accessed through the mostRecentVmRegions field');
55    },
56
57    set vmRegions(vmRegions) {
58      this.vmRegions_ = vmRegions;
59    },
60
61    get hasOwnVmRegions() {
62      return this.vmRegions_ !== undefined;
63    },
64
65    getMostRecentTotalVmRegionStat: function(statName) {
66      if (this.mostRecentVmRegions === undefined)
67        return undefined;
68
69      var total = 0;
70      this.mostRecentVmRegions.forEach(function(vmRegion) {
71        var statValue = vmRegion.byteStats[statName];
72        if (statValue === undefined)
73          return;
74        total += statValue;
75      });
76      return total;
77    },
78
79    discountTracingOverhead: function(opt_model) {
80      // Make sure that calling this method twice won't lead to
81      // 'double-discounting'.
82      if (this.tracingMemoryDiscounted_)
83        return;
84      this.tracingMemoryDiscounted_ = true;
85
86      var tracingDump = this.getMemoryAllocatorDumpByFullName('tracing');
87      if (tracingDump === undefined)
88        return;
89
90      function getDiscountedSize(sizeAttrName) {
91        var sizeAttr = tracingDump.getValidSizeAttributeOrUndefined(
92            sizeAttrName, opt_model);
93        if (sizeAttr === undefined)
94          return 0;
95        return sizeAttr.value;
96      }
97
98      var discountedSize = getDiscountedSize('size');
99      var discountedEffectiveSize = getDiscountedSize('effective_size');
100      var discountedResidentSize = getDiscountedSize('resident_size');
101
102      // Subtract 'resident_size' from totals and VM regions stats.
103      if (discountedResidentSize > 0) {
104        // Subtract the tracing size from the total.
105        if (this.totalResidentBytes !== undefined)
106          this.totalResidentBytes -= discountedResidentSize;
107
108        // Subtract the tracing size from VM regions.
109        if (this.vmRegions_ !== undefined) {
110          this.vmRegions_.push(VMRegion.fromDict({
111            mappedFile: '[discounted tracing overhead]',
112            byteStats: {
113              privateDirtyResident: -discountedResidentSize,
114              proportionalResident: -discountedResidentSize
115            }
116          }));
117        }
118      }
119
120      // Subtract 'size' and 'effective_size' from the 'winheap' or 'malloc'
121      // MemoryAllocatorDump.
122      if (discountedSize > 0 || discountedEffectiveSize > 0) {
123        function discountSizeAndEffectiveSize(dump) {
124          var dumpSizeAttr = dump.getValidSizeAttributeOrUndefined(
125              'size', opt_model);
126          if (dumpSizeAttr !== undefined)
127            dumpSizeAttr.value -= discountedSize;
128
129          var dumpEffectiveSizeAttr = dump.getValidSizeAttributeOrUndefined(
130              'effective_size', opt_model);
131          if (dumpEffectiveSizeAttr !== undefined)
132            dumpEffectiveSizeAttr.value -= discountedEffectiveSize;
133        }
134
135        var hasDiscountedFromAllocatorDumps = DISCOUNTED_ALLOCATOR_NAMES.some(
136            function(allocatorName) {
137          // Discount 'size' and 'effective_size' from the allocator root.
138          var allocatorDump = this.getMemoryAllocatorDumpByFullName(
139              allocatorName);
140          if (allocatorDump === undefined)
141            return false;  // Allocator doesn't exist, try another one.
142          discountSizeAndEffectiveSize(allocatorDump);
143
144          // Discount 'size' and 'effective_size' from allocated objects of the
145          // allocator ('<ALLOCATOR>/allocated_objects').
146          var allocatedObjectsDumpName = allocatorName + '/allocated_objects';
147          var allocatedObjectsDump = this.getMemoryAllocatorDumpByFullName(
148              allocatedObjectsDumpName);
149          if (allocatedObjectsDump === undefined)
150            return true;  // Allocator has unexpected structure, good enough.
151          discountSizeAndEffectiveSize(allocatedObjectsDump);
152
153          // Add a child MAD representing the discounted tracing overhead
154          // ('<ALLOCATOR>/allocated_objects/discounted_tracing_overhead').
155          var discountDumpName =
156              allocatedObjectsDumpName + '/discounted_tracing_overhead';
157          var discountDump = new tr.model.MemoryAllocatorDump(
158              this, discountDumpName);
159          discountDump.parent = allocatedObjectsDump;
160          discountDump.addAttribute('size',
161              new tr.model.ScalarAttribute('bytes', -discountedSize));
162          discountDump.addAttribute('effective_size',
163              new tr.model.ScalarAttribute('bytes', -discountedEffectiveSize));
164          allocatedObjectsDump.children.push(discountDump);
165
166          return true;
167        }, this);
168
169        // Force rebuilding the memory allocator dump index (if we've just added
170        // a new memory allocator dump).
171        if (hasDiscountedFromAllocatorDumps)
172          this.memoryAllocatorDumps = this.memoryAllocatorDumps;
173      }
174    }
175  };
176
177  ProcessMemoryDump.hookUpMostRecentVmRegionsLinks = function(processDumps) {
178    var mostRecentVmRegions = undefined;
179
180    processDumps.forEach(function(processDump) {
181      // Update the most recent VM regions from the current dump.
182      if (processDump.vmRegions_ !== undefined)
183        mostRecentVmRegions = processDump.vmRegions_;
184
185      // Set the most recent VM regions of the current dump.
186      processDump.mostRecentVmRegions = mostRecentVmRegions;
187    });
188  };
189
190  /**
191   * @constructor
192   */
193  function VMRegion(startAddress, sizeInBytes, protectionFlags,
194      mappedFile, byteStats) {
195    this.startAddress = startAddress;
196    this.sizeInBytes = sizeInBytes;
197    this.protectionFlags = protectionFlags;
198    this.mappedFile = mappedFile;
199    this.byteStats = byteStats;
200  };
201
202  VMRegion.PROTECTION_FLAG_READ = 4;
203  VMRegion.PROTECTION_FLAG_WRITE = 2;
204  VMRegion.PROTECTION_FLAG_EXECUTE = 1;
205
206  VMRegion.prototype = {
207    get protectionFlagsToString() {
208      if (this.protectionFlags === undefined)
209        return undefined;
210      return (
211          (this.protectionFlags & VMRegion.PROTECTION_FLAG_READ ? 'r' : '-') +
212          (this.protectionFlags & VMRegion.PROTECTION_FLAG_WRITE ? 'w' : '-') +
213          (this.protectionFlags & VMRegion.PROTECTION_FLAG_EXECUTE ? 'x' : '-')
214      );
215    }
216  };
217
218  VMRegion.fromDict = function(dict) {
219    return new VMRegion(
220        dict.startAddress,
221        dict.sizeInBytes,
222        dict.protectionFlags,
223        dict.mappedFile,
224        VMRegionByteStats.fromDict(dict.byteStats));
225  };
226
227  /**
228   * @constructor
229   */
230  function VMRegionByteStats(privateCleanResident, privateDirtyResident,
231                             sharedCleanResident, sharedDirtyResident,
232                             proportionalResident, swapped) {
233    this.privateCleanResident = privateCleanResident;
234    this.privateDirtyResident = privateDirtyResident;
235    this.sharedCleanResident = sharedCleanResident;
236    this.sharedDirtyResident = sharedDirtyResident;
237    this.proportionalResident = proportionalResident;
238    this.swapped = swapped;
239  }
240
241  VMRegionByteStats.fromDict = function(dict) {
242    return new VMRegionByteStats(
243        dict.privateCleanResident,
244        dict.privateDirtyResident,
245        dict.sharedCleanResident,
246        dict.sharedDirtyResident,
247        dict.proportionalResident,
248        dict.swapped);
249  }
250
251  tr.model.EventRegistry.register(
252      ProcessMemoryDump,
253      {
254        name: 'processMemoryDump',
255        pluralName: 'processMemoryDumps',
256        singleViewElementName: 'tr-ui-a-single-process-memory-dump-sub-view',
257        multiViewElementName: 'tr-ui-a-multi-process-memory-dump-sub-view'
258      });
259
260  return {
261    ProcessMemoryDump: ProcessMemoryDump,
262    VMRegion: VMRegion,
263    VMRegionByteStats: VMRegionByteStats
264  };
265});
266</script>
267