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/memory_allocator_dump.html"> 9<link rel="import" href="/tracing/ui/tracks/chart_axis.html"> 10<link rel="import" href="/tracing/ui/tracks/chart_point.html"> 11<link rel="import" href="/tracing/ui/tracks/chart_series.html"> 12<link rel="import" href="/tracing/ui/tracks/chart_track.html"> 13<link rel="import" href="/tracing/ui/tracks/container_track.html"> 14<link rel="import" href="/tracing/ui/tracks/letter_dot_track.html"> 15 16<script> 17'use strict'; 18 19tr.exportTo('tr.ui.tracks', function() { 20 var ColorScheme = tr.b.ColorScheme; 21 22 var DISPLAYED_SIZE_NUMERIC_NAME = 23 tr.model.MemoryAllocatorDump.DISPLAYED_SIZE_NUMERIC_NAME; 24 25 /** 26 * Add numeric values from a source dictionary to the numeric values in 27 * a destination dictionary. Undefined values are treated as zeros. Note that 28 * this method modifies the destination dictionary in place. 29 * 30 * Example: addDictionary({a: 1, b: 2}, {b: 3, c: 4}) will update the 31 * destination dictionary (first argument) to {a: 1, b: 5, c: 4}. 32 */ 33 function addDictionary(dstDict, srcDict) { 34 tr.b.iterItems(srcDict, function(key, value) { 35 var existingValue = dstDict[key]; 36 if (existingValue === undefined) 37 existingValue = 0; 38 dstDict[key] = existingValue + value; 39 }); 40 } 41 42 /** 43 * Get a dictionary mapping root allocator names (e.g. 'v8') to the 44 * corresponding sizes (e.g. 1024) in a process memory dump. 45 */ 46 function getProcessMemoryDumpAllocatorSizes(processMemoryDump) { 47 var allocatorDumps = processMemoryDump.memoryAllocatorDumps; 48 if (allocatorDumps === undefined) 49 return {}; 50 var allocatorSizes = {}; 51 allocatorDumps.forEach(function(allocatorDump) { 52 // Don't show tracing overhead in the charts. 53 // TODO(petrcermak): Find a less hacky way to do this. 54 if (allocatorDump.fullName === 'tracing') 55 return; 56 var allocatorSize = allocatorDump.numerics[DISPLAYED_SIZE_NUMERIC_NAME]; 57 if (allocatorSize === undefined) 58 return; 59 var allocatorSizeValue = allocatorSize.value; 60 if (allocatorSizeValue === undefined) 61 return; 62 allocatorSizes[allocatorDump.fullName] = allocatorSizeValue; 63 }); 64 return allocatorSizes; 65 }; 66 67 /** 68 * Get a dictionary mapping root allocator names (e.g. 'v8') to the 69 * corresponding sizes (e.g. 1024) in a global memory dump (i.e. summed over 70 * all simultaneous process memory dumps). 71 */ 72 function getGlobalMemoryDumpAllocatorSizes(globalMemoryDump) { 73 var globalAllocatorSizes = {}; 74 tr.b.iterItems(globalMemoryDump.processMemoryDumps, 75 function(pid, processMemoryDump) { 76 addDictionary(globalAllocatorSizes, 77 getProcessMemoryDumpAllocatorSizes(processMemoryDump)); 78 }); 79 return globalAllocatorSizes; 80 } 81 82 /** 83 * A generic function which converts a list of memory dumps to a list of chart 84 * series (one per root allocator). Each series represents the evolution of 85 * the size of a the corresponding root allocator (e.g. 'v8') over time. 86 */ 87 function buildAllocatedMemoryChartSeries(memoryDumps, 88 memoryDumpToAllocatorSizesFn) { 89 var allocatorNameToPoints = {}; 90 var dumpsData = memoryDumps.map(function(memoryDump) { 91 var allocatorSizes = memoryDumpToAllocatorSizesFn(memoryDump); 92 tr.b.iterItems(allocatorSizes, function(allocatorName) { 93 allocatorNameToPoints[allocatorName] = []; 94 }); 95 return {dump: memoryDump, sizes: allocatorSizes}; 96 }); 97 98 // Do not generate any chart series if no process memory dump contains any 99 // allocator dumps. 100 if (Object.keys(allocatorNameToPoints).length === 0) 101 return undefined; 102 103 dumpsData.forEach(function(dumpData) { 104 var memoryDump = dumpData.dump; 105 var allocatorSizes = dumpData.sizes; 106 tr.b.iterItems(allocatorNameToPoints, function(allocatorName, points) { 107 var allocatorSize = allocatorSizes[allocatorName] || 0; 108 points.push(new tr.ui.tracks.ChartPoint( 109 memoryDump, memoryDump.start, allocatorSize)); 110 }); 111 }); 112 113 // Create one common axis for all allocated memory chart series. 114 var axis = new tr.ui.tracks.ChartAxis(0); 115 116 // Build a chart series for each allocator. 117 var series = []; 118 tr.b.iterItems(allocatorNameToPoints, function(allocatorName, points) { 119 var colorId = ColorScheme.getColorIdForGeneralPurposeString( 120 allocatorName); 121 var renderingConfig = { 122 chartType: tr.ui.tracks.ChartSeriesType.LINE, 123 colorId: colorId 124 }; 125 series.push(new tr.ui.tracks.ChartSeries(points, axis, renderingConfig)); 126 }); 127 128 return series; 129 } 130 131 /** 132 * Transform a list of memory dumps to a list of letter dots (with letter 'M' 133 * inside). 134 */ 135 function buildMemoryLetterDots(memoryDumps) { 136 var lightMemoryColorId = 137 ColorScheme.getColorIdForReservedName('light_memory_dump'); 138 var detailedMemoryColorId = 139 ColorScheme.getColorIdForReservedName('detailed_memory_dump'); 140 return memoryDumps.map(function(memoryDump) { 141 var memoryColorId; 142 switch (memoryDump.levelOfDetail) { 143 case 'detailed': 144 memoryColorId = detailedMemoryColorId; 145 break; 146 case 'light': 147 default: 148 memoryColorId = lightMemoryColorId; 149 } 150 return new tr.ui.tracks.LetterDot( 151 memoryDump, 'M', memoryColorId, memoryDump.start); 152 }); 153 } 154 155 /** 156 * Convert a list of global memory dumps to a list of chart series (one per 157 * process). Each series represents the evolution of the memory used by the 158 * process over time. 159 */ 160 function buildGlobalUsedMemoryChartSeries(globalMemoryDumps) { 161 // Do not generate the chart if no process memory dump contains VM regions. 162 var containsVmRegions = globalMemoryDumps.some(function(globalDump) { 163 for (var pid in globalDump.processMemoryDumps) 164 if (globalDump.processMemoryDumps[pid].mostRecentVmRegions) 165 return true; 166 return false; 167 }); 168 if (!containsVmRegions) 169 return undefined; 170 171 // Find all processes that dump memory at least once. 172 var pidToProcess = {}; 173 globalMemoryDumps.forEach(function(globalDump) { 174 tr.b.iterItems(globalDump.processMemoryDumps, function(pid, processDump) { 175 pidToProcess[pid] = processDump.process; 176 }); 177 }); 178 179 // Build one list of points for each instrumented process. 180 var pidToPoints = {}; 181 tr.b.iterItems(pidToProcess, function(pid, process) { 182 pidToPoints[pid] = []; 183 }); 184 185 // For every timestamp, calculate the total PSS (proportional set size) of 186 // each process and append it to the corresponding list of points. 187 globalMemoryDumps.forEach(function(globalDump) { 188 var pssBase = 0; 189 tr.b.iterItems(pidToPoints, function(pid, points) { 190 var processMemoryDump = globalDump.processMemoryDumps[pid]; 191 var cumulativePss = pssBase; 192 // If no dump was found (probably dead) or it does not provide the 193 // necessary information (namely most recent VM regions), assume zero. 194 if (processMemoryDump !== undefined) { 195 var vmRegions = processMemoryDump.mostRecentVmRegions; 196 if (vmRegions !== undefined) 197 cumulativePss += vmRegions.byteStats.proportionalResident || 0; 198 } 199 points.push(new tr.ui.tracks.ChartPoint( 200 globalDump, globalDump.start, cumulativePss, pssBase)); 201 pssBase = cumulativePss; 202 }); 203 }); 204 205 // Create one common axis for all used memory chart series. 206 var axis = new tr.ui.tracks.ChartAxis(0); 207 208 // Build a chart series for each instrumented process. 209 var series = []; 210 tr.b.iterItems(pidToPoints, function(pid, points) { 211 var process = pidToProcess[pid]; 212 var colorId = ColorScheme.getColorIdForGeneralPurposeString( 213 process.userFriendlyName); 214 var renderingConfig = { 215 chartType: tr.ui.tracks.ChartSeriesType.AREA, 216 colorId: colorId, 217 backgroundOpacity: 0.8 218 }; 219 series.push(new tr.ui.tracks.ChartSeries(points, axis, renderingConfig)); 220 }); 221 222 // Show the first series (with the smallest cumulative value) at the top. 223 series.reverse(); 224 225 return series; 226 } 227 228 /** 229 * Convert a list of process memory dumps to a list of chart series (one per 230 * root allocator). Each series represents the evolution of the size of a the 231 * corresponding root allocator (e.g. 'v8') over time. 232 */ 233 function buildProcessAllocatedMemoryChartSeries(processMemoryDumps) { 234 return buildAllocatedMemoryChartSeries(processMemoryDumps, 235 getProcessMemoryDumpAllocatorSizes); 236 } 237 238 /** 239 * Convert a list of global memory dumps to a list of chart series (one per 240 * root allocator). Each series represents the evolution of the size of a the 241 * corresponding root allocator (e.g. 'v8') over time. 242 */ 243 function buildGlobalAllocatedMemoryChartSeries(globalMemoryDumps) { 244 return buildAllocatedMemoryChartSeries(globalMemoryDumps, 245 getGlobalMemoryDumpAllocatorSizes); 246 } 247 248 return { 249 buildMemoryLetterDots: 250 buildMemoryLetterDots, 251 buildGlobalUsedMemoryChartSeries: 252 buildGlobalUsedMemoryChartSeries, 253 buildProcessAllocatedMemoryChartSeries: 254 buildProcessAllocatedMemoryChartSeries, 255 buildGlobalAllocatedMemoryChartSeries: 256 buildGlobalAllocatedMemoryChartSeries 257 }; 258}); 259</script> 260