process_memory_dump_test.html revision 972bd9a9d2c6597a0145a675cbfa527d0510b048
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/core/test_utils.html">
9<link rel="import" href="/tracing/model/attribute.html">
10<link rel="import" href="/tracing/model/model.html">
11<link rel="import" href="/tracing/model/global_memory_dump.html">
12<link rel="import" href="/tracing/model/memory_allocator_dump.html">
13<link rel="import" href="/tracing/model/process_memory_dump.html">
14
15<script>
16'use strict';
17
18tr.b.unittest.testSuite(function() {
19  var GlobalMemoryDump = tr.model.GlobalMemoryDump;
20  var ProcessMemoryDump = tr.model.ProcessMemoryDump;
21  var MemoryAllocatorDump = tr.model.MemoryAllocatorDump;
22  var MemoryAllocatorDumpLink = tr.model.MemoryAllocatorDumpLink;
23  var VMRegion = tr.model.VMRegion;
24  var ScalarAttribute = tr.model.ScalarAttribute;
25
26  var createProcessMemoryDump = function(timestamp, model) {
27    var gmd = new GlobalMemoryDump(model, timestamp);
28    model.globalMemoryDumps.push(gmd);
29    var p = model.getOrCreateProcess(123);
30    var pmd = new ProcessMemoryDump(gmd, p, timestamp + 1);
31    gmd.processMemoryDumps[123] = pmd;
32    p.memoryDumps.push(pmd);
33    return pmd;
34  }
35
36  var createFinalizedProcessMemoryDump = function(timestamp, createdCallback) {
37    return createFinalizedProcessMemoryDumps([timestamp], function(pmds) {
38      createdCallback(pmds[0]);
39    })[0];
40  }
41
42  function createFinalizedProcessMemoryDumps(timestamps, createdCallback) {
43    var model = tr.c.TestUtils.newModel(function(model) {
44      var pmds = timestamps.map(function(timestamp) {
45        return createProcessMemoryDump(timestamp, model);
46      });
47      createdCallback(pmds);
48    });
49    var pmds = model.getProcess(123).memoryDumps;
50    assert.lengthOf(pmds, timestamps.length);
51    return pmds;
52  }
53
54  function checkProtectionFlagsToString(protectionFlags, expectedString) {
55    var vmRegion = VMRegion.fromDict({
56      startAddress: 256,
57      sizeInBytes: 336,
58      protectionFlags: protectionFlags,
59      mappedFile: '[stack:20310]',
60      byteStats: {
61        privateDirtyResident: 96,
62        swapped: 144,
63        proportionalResident: 158
64      }
65    });
66    assert.strictEqual(vmRegion.protectionFlagsToString, expectedString);
67  }
68
69  test('totalResidentSizeInBytes_undefinedVmRegions', function() {
70    var pmd = createFinalizedProcessMemoryDump(42, function(pmd) {});
71    assert.isUndefined(pmd.mostRecentTotalProportionalResidentSizeInBytes);
72    assert.isUndefined(
73        pmd.getMostRecentTotalVmRegionStat('privateDirtyResident'));
74    assert.isUndefined(
75        pmd.getMostRecentTotalVmRegionStat('privateCleanResident'));
76  });
77
78  test('totalResidentSizeInBytes_zeroVmRegions', function() {
79    var pmd = createFinalizedProcessMemoryDump(42, function(pmd) {
80      pmd.vmRegions = [];
81    });
82    assert.equal(pmd.getMostRecentTotalVmRegionStat('proportionalResident'), 0);
83    assert.equal(pmd.getMostRecentTotalVmRegionStat('privateDirtyResident'), 0);
84    assert.equal(pmd.getMostRecentTotalVmRegionStat('privateCleanResident'), 0);
85  });
86
87  test('totalResidentSizeInBytes_oneVmRegion', function() {
88    var pmd = createFinalizedProcessMemoryDump(42, function(pmd) {
89      pmd.vmRegions = [
90        VMRegion.fromDict({
91          startAddress: 256,
92          sizeInBytes: 336,
93          protectionFlags: VMRegion.PROTECTION_FLAG_READ |
94              VMRegion.PROTECTION_FLAG_WRITE,
95          mappedFile: '[stack:20310]',
96          byteStats: {
97            privateDirtyResident: 96,
98            swapped: 144,
99            proportionalResident: 158
100          }
101        })
102      ];
103    });
104    assert.equal(
105        pmd.getMostRecentTotalVmRegionStat('proportionalResident'), 158);
106    assert.equal(
107        pmd.getMostRecentTotalVmRegionStat('privateDirtyResident'), 96);
108    assert.equal(pmd.getMostRecentTotalVmRegionStat('privateCleanResident'), 0);
109  });
110
111  test('totalResidentSizeInBytes_twoVmRegions', function() {
112    var pmd = createFinalizedProcessMemoryDump(42, function(pmd) {
113      pmd.vmRegions = [
114        VMRegion.fromDict({
115          startAddress: 256,
116          sizeInBytes: 336,
117          protectionFlags: VMRegion.PROTECTION_FLAG_READ |
118              VMRegion.PROTECTION_FLAG_WRITE,
119          mappedFile: '[stack:20310]',
120          byteStats: {
121            privateDirtyResident: 96,
122            swapped: 144,
123            proportionalResident: 158
124          }
125        }),
126        VMRegion.fromDict({
127          startAddress: 848,
128          sizeInBytes: 592,
129          protectionFlags: VMRegion.PROTECTION_FLAG_READ |
130              VMRegion.PROTECTION_FLAG_EXECUTE,
131          mappedFile: '/dev/ashmem/dalvik',
132          byteStats: {
133            privateDirtyResident: 205,
134            privateCleanResident: 0,
135            proportionalResident: 205
136          }
137        })
138      ];
139    });
140    assert.equal(
141        pmd.getMostRecentTotalVmRegionStat('proportionalResident'), 363);
142    assert.equal(
143        pmd.getMostRecentTotalVmRegionStat('privateDirtyResident'), 301);
144    assert.equal(pmd.getMostRecentTotalVmRegionStat('swapped'), 144);
145    assert.equal(pmd.getMostRecentTotalVmRegionStat('privateCleanResident'), 0);
146    assert.equal(pmd.getMostRecentTotalVmRegionStat('sharedCleanResident'), 0);
147  });
148
149  test('hookUpMostRecentVmRegionsLinks_emptyArray', function() {
150    var dumps = [];
151    ProcessMemoryDump.hookUpMostRecentVmRegionsLinks(dumps);
152    assert.lengthOf(dumps, 0);
153  });
154
155  test('hookUpMostRecentVmRegionsLinks_nonEmptyArray', function() {
156    var m = new tr.Model();
157
158    // A dump with no VM regions or allocator dumps.
159    var dump1 = createProcessMemoryDump(1, m);
160
161    // A dump with VM regions and malloc and Oilpan allocator dumps.
162    var dump2 = createProcessMemoryDump(2, m);
163    dump2.vmRegions = [];
164    dump2.memoryAllocatorDumps = (function() {
165      var oilpanDump = new MemoryAllocatorDump('oilpan');
166      oilpanDump.addAttribute('size', new ScalarAttribute('bytes', 1024));
167      oilpanDump.addAttribute('objects_count',
168          new ScalarAttribute('objects', 7));
169      oilpanDump.addAttribute('inner_size', new ScalarAttribute('bytes', 768));
170
171      var v8Dump = new MemoryAllocatorDump('v8');
172      v8Dump.addAttribute('size', new ScalarAttribute('bytes', 2048));
173      v8Dump.addAttribute('objects_count', new ScalarAttribute('objects', 15));
174      v8Dump.addAttribute('inner_size', new ScalarAttribute('bytes', 1999));
175
176      return [oilpanDump. v8Dump];
177    })();
178
179    // A dump with malloc and V8 allocator dumps.
180    var dump3 = createProcessMemoryDump(3, m);
181    dump3.memoryAllocatorDumps = (function() {
182      var mallocDump = new MemoryAllocatorDump('malloc');
183      mallocDump.addAttribute('size', new ScalarAttribute('bytes', 1024));
184      mallocDump.addAttribute('objects_count',
185          new ScalarAttribute('objects', 7));
186      mallocDump.addAttribute('inner_size', new ScalarAttribute('bytes', 768));
187
188      var v8Dump = new MemoryAllocatorDump('v8');
189      v8Dump.addAttribute('size', new ScalarAttribute('bytes', 2048));
190      v8Dump.addAttribute('objects_count', new ScalarAttribute('objects', 15));
191      v8Dump.addAttribute('inner_size', new ScalarAttribute('bytes', 1999));
192
193      return [mallocDump. v8Dump];
194    })();
195
196    // A dump with VM regions.
197    var dump4 = createProcessMemoryDump(4, m);
198    dump4.vmRegions = [
199      VMRegion.fromDict({
200        startAddress: 256,
201        sizeInBytes: 336,
202        protectionFlags: VMRegion.PROTECTION_FLAG_READ |
203            VMRegion.PROTECTION_FLAG_WRITE,
204        mappedFile: '[stack:20310]',
205        byteStats: {
206          privateResident: 96,
207          sharedResident: 144,
208          proportionalResident: 158
209        }
210      })
211    ];
212
213    var dumps = [dump1, dump2, dump3, dump4];
214    ProcessMemoryDump.hookUpMostRecentVmRegionsLinks(dumps);
215
216    assert.lengthOf(dumps, 4);
217
218    assert.equal(dumps[0], dump1);
219    assert.isUndefined(dump1.mostRecentVmRegions);
220
221    assert.equal(dumps[1], dump2);
222    assert.equal(dump2.mostRecentVmRegions, dump2.vmRegions_);
223
224    assert.equal(dumps[2], dump3);
225    assert.equal(dump3.mostRecentVmRegions, dump2.vmRegions_);
226
227    assert.equal(dumps[3], dump4);
228    assert.equal(dump4.mostRecentVmRegions, dump4.vmRegions_);
229  });
230
231  test('vmRegion_protectionFlagsToString', function() {
232    checkProtectionFlagsToString(undefined, undefined);
233    checkProtectionFlagsToString(0, '---');
234    checkProtectionFlagsToString(VMRegion.PROTECTION_FLAG_READ, 'r--');
235    checkProtectionFlagsToString(
236        VMRegion.PROTECTION_FLAG_READ | VMRegion.PROTECTION_FLAG_EXECUTE,
237        'r-x');
238    checkProtectionFlagsToString(
239        VMRegion.PROTECTION_FLAG_READ | VMRegion.PROTECTION_FLAG_WRITE,
240        'rw-');
241  });
242
243  test('checkDiscountTracingOverhead_undefinedFields', function() {
244    var pmd = createFinalizedProcessMemoryDump(42, function(pmd) {
245      var v8Dump = new MemoryAllocatorDump(pmd, 'v8');
246      v8Dump.addAttribute('size', new ScalarAttribute('bytes', 2048));
247
248      var tracingDump = new MemoryAllocatorDump(pmd, 'tracing');
249      tracingDump.addAttribute('size', new ScalarAttribute('bytes', 1024));
250
251      pmd.memoryAllocatorDumps = [v8Dump, tracingDump];
252    });
253
254    assert.isUndefined(pmd.totals);
255    assert.isUndefined(
256        pmd.getMostRecentTotalVmRegionStat('proportionalResident'));
257
258    var v8Dump = pmd.getMemoryAllocatorDumpByFullName('v8');
259    assert.equal(v8Dump.attributes['size'].value, 2048);
260
261    var tracingDump = pmd.getMemoryAllocatorDumpByFullName('tracing');
262    assert.equal(tracingDump.attributes['size'].value, 1024);
263  });
264
265  test('checkDiscountTracingOverhead_definedFields', function() {
266    var pmd = createFinalizedProcessMemoryDump(42, function(pmd) {
267      pmd.totals = { residentBytes: 10240 };
268
269      pmd.vmRegions = [
270        VMRegion.fromDict({
271          startAddress: 256,
272          sizeInBytes: 6000,
273          protectionFlags: VMRegion.PROTECTION_FLAG_READ |
274              VMRegion.PROTECTION_FLAG_WRITE,
275          mappedFile: '[stack:20310]',
276          byteStats: {
277            privateDirtyResident: 4096,
278            swapped: 1536,
279            proportionalResident: 5120
280          }
281        })
282      ];
283
284      var mallocDump = new MemoryAllocatorDump(pmd, 'malloc');
285      mallocDump.addAttribute('size', new ScalarAttribute('bytes', 3072));
286      var allocatedObjectsDump = new MemoryAllocatorDump(
287          pmd, 'malloc/allocated_objects');
288      allocatedObjectsDump.addAttribute(
289          'size', new ScalarAttribute('bytes', 2560));
290      allocatedObjectsDump.parent = mallocDump;
291      mallocDump.children.push(allocatedObjectsDump);
292
293      var tracingDump = new MemoryAllocatorDump(pmd, 'tracing');
294      tracingDump.addAttribute('size', new ScalarAttribute('bytes', 1024));
295      tracingDump.addAttribute(
296          'resident_size', new ScalarAttribute('bytes', 1000));
297
298      // We need to inject a fake owner of the 'tracing' dump so that we alter
299      // its effective size.
300      var ownerDump = new MemoryAllocatorDump(pmd, 'owner');
301      ownerDump.addAttribute('size', new ScalarAttribute('bytes', 256));
302      var ownershipLink = new MemoryAllocatorDumpLink(ownerDump, tracingDump);
303      ownerDump.owns = ownershipLink;
304      tracingDump.ownedBy.push(ownershipLink);
305
306      pmd.memoryAllocatorDumps = [mallocDump, tracingDump, ownerDump];
307    });
308
309    assert.equal(pmd.totals.residentBytes, 9240);
310    assert.isUndefined(pmd.totals.peakResidentBytes);
311
312    assert.equal(
313        pmd.getMostRecentTotalVmRegionStat('privateDirtyResident'), 3096);
314    assert.equal(pmd.getMostRecentTotalVmRegionStat('swapped'), 1536);
315    assert.equal(
316        pmd.getMostRecentTotalVmRegionStat('proportionalResident'), 4120);
317
318    var mallocDump = pmd.getMemoryAllocatorDumpByFullName('malloc');
319    assert.equal(mallocDump.attributes['size'].value, 2048);
320    assert.equal(mallocDump.attributes['effective_size'].value, 2304);
321    assert.lengthOf(
322        mallocDump.children, 2 /* 'allocated_objects' and '<unspecified>' */);
323
324    var allocatedObjectsDump = pmd.getMemoryAllocatorDumpByFullName(
325        'malloc/allocated_objects');
326    assert.equal(allocatedObjectsDump.attributes['size'].value, 1536);
327    assert.equal(allocatedObjectsDump.attributes['effective_size'].value, 1792);
328    assert.lengthOf(
329        allocatedObjectsDump.children, 1 /* 'discounted_tracing_overhead' */);
330
331    var discountDump = pmd.getMemoryAllocatorDumpByFullName(
332        'malloc/allocated_objects/discounted_tracing_overhead');
333    assert.strictEqual(discountDump.parent, allocatedObjectsDump);
334    assert.include(allocatedObjectsDump.children, discountDump);
335    assert.equal(discountDump.attributes['size'].value, -1024);
336    assert.equal(discountDump.attributes['effective_size'].value, -768);
337
338    var tracingDump = pmd.getMemoryAllocatorDumpByFullName('tracing');
339    assert.equal(tracingDump.attributes['size'].value, 1024);
340    assert.equal(tracingDump.attributes['effective_size'].value, 768);
341    assert.equal(tracingDump.attributes['resident_size'].value, 1000);
342  });
343
344  test('checkDiscountTracingOverhead_winheap', function() {
345    var pmd = createFinalizedProcessMemoryDump(42, function(pmd) {
346      var winheapDump = new MemoryAllocatorDump(pmd, 'winheap');
347      winheapDump.addAttribute('size', new ScalarAttribute('bytes', 5120));
348
349      var tracingDump = new MemoryAllocatorDump(pmd, 'tracing');
350      tracingDump.addAttribute('size', new ScalarAttribute('bytes', 2048));
351
352      pmd.memoryAllocatorDumps = [tracingDump, winheapDump];
353    });
354
355    assert.isUndefined(pmd.totals);
356
357    assert.isUndefined(
358        pmd.getMostRecentTotalVmRegionStat('privateDirtyResident'));
359    assert.isUndefined(pmd.getMostRecentTotalVmRegionStat('swapped'));
360    assert.isUndefined(
361        pmd.getMostRecentTotalVmRegionStat('proportionalResident'));
362
363    var winheapDump = pmd.getMemoryAllocatorDumpByFullName('winheap');
364    assert.equal(winheapDump.attributes['size'].value, 3072);
365    assert.equal(winheapDump.attributes['effective_size'].value, 3072);
366    assert.lengthOf(winheapDump.children, 0);
367
368    var tracingDump = pmd.getMemoryAllocatorDumpByFullName('tracing');
369    assert.equal(tracingDump.attributes['size'].value, 2048);
370    assert.equal(tracingDump.attributes['effective_size'].value, 2048);
371  });
372
373  test('checkDiscountTracingOverhead_withMostRecentVmRegionsLinks', function() {
374    var pmds = createFinalizedProcessMemoryDumps([42, 90], function(pmds) {
375      pmds[0].totals = { residentBytes: 1000, peakResidentBytes: 2000 };
376      pmds[0].vmRegions = [
377        VMRegion.fromDict({
378          startAddress: 256,
379          sizeInBytes: 6000,
380          protectionFlags: VMRegion.PROTECTION_FLAG_READ |
381              VMRegion.PROTECTION_FLAG_WRITE,
382          mappedFile: '[stack:20310]',
383          byteStats: {
384            privateDirtyResident: 4096
385          }
386        })
387      ];
388      pmds[0].memoryAllocatorDumps = (function() {
389        var tracingDump = new MemoryAllocatorDump(pmds[0], 'tracing');
390        tracingDump.addAttribute(
391            'resident_size', new ScalarAttribute('bytes', 100));
392        return [tracingDump];
393      })();
394
395      pmds[1].totals = { peakResidentBytes: 3000 };
396      pmds[1].memoryAllocatorDumps = (function() {
397        var tracingDump = new MemoryAllocatorDump(pmds[0], 'tracing');
398        tracingDump.addAttribute(
399            'resident_size', new ScalarAttribute('bytes', 200));
400        return [tracingDump];
401      })();
402    });
403
404    // First PMD: Both total resident and private dirty resident size should be
405    // reduced by 100.
406    assert.equal(pmds[0].totals.residentBytes, 900);
407    assert.equal(pmds[0].totals.peakResidentBytes, 1900);
408    assert.equal(
409        pmds[0].getMostRecentTotalVmRegionStat('privateDirtyResident'), 3996);
410
411    // Second PMD: Total resident size should be reduced by 200, whereas private
412    // dirty resident size should be reduced by 100 (because it comes from
413    // the VM regions in the first dump).
414    assert.isUndefined(pmds[1].totals.residentBytes);
415    assert.equal(pmds[1].totals.peakResidentBytes, 2800);
416    assert.equal(
417        pmds[1].getMostRecentTotalVmRegionStat('privateDirtyResident'), 3996);
418  });
419});
420</script>
421