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/ui/analysis/memory_dump_sub_view_util.html"> 10<link rel="import" href="/tracing/ui/analysis/stacked_pane.html"> 11<link rel="import" href="/tracing/ui/base/table.html"> 12<link rel="import" href="/tracing/value/numeric.html"> 13<link rel="import" href="/tracing/value/unit.html"> 14 15<polymer-element name="tr-ui-a-memory-dump-vm-regions-details-pane" 16 extends="tr-ui-a-stacked-pane"> 17 <template> 18 <style> 19 :host { 20 display: flex; 21 flex-direction: column; 22 } 23 24 #label { 25 flex: 0 0 auto; 26 padding: 8px; 27 28 background-color: #eee; 29 border-bottom: 1px solid #8e8e8e; 30 border-top: 1px solid white; 31 32 font-size: 15px; 33 font-weight: bold; 34 } 35 36 #contents { 37 flex: 1 0 auto; 38 align-self: stretch; 39 font-size: 12px; 40 } 41 42 #info_text { 43 padding: 8px; 44 color: #666; 45 font-style: italic; 46 text-align: center; 47 } 48 49 #table { 50 display: none; /* Hide until memory dumps are set. */ 51 flex: 1 0 auto; 52 align-self: stretch; 53 } 54 </style> 55 <div id="label">Memory maps</div> 56 <div id="contents"> 57 <div id="info_text">No memory maps selected</div> 58 <tr-ui-b-table id="table"></tr-ui-b-table> 59 </div> 60 </template> 61</polymer-element> 62<script> 63'use strict'; 64 65tr.exportTo('tr.ui.analysis', function() { 66 67 var ScalarNumeric = tr.v.ScalarNumeric; 68 var sizeInBytes_smallerIsBetter = 69 tr.v.Unit.byName.sizeInBytes_smallerIsBetter; 70 71 var CONSTANT_COLUMN_RULES = [ 72 { 73 condition: 'Start address', 74 importance: 0, 75 columnConstructor: tr.ui.analysis.StringMemoryColumn 76 } 77 ]; 78 79 var VARIABLE_COLUMN_RULES = [ 80 { 81 condition: 'Virtual size', 82 importance: 7, 83 columnConstructor: tr.ui.analysis.NumericMemoryColumn 84 }, 85 { 86 condition: 'Protection flags', 87 importance: 6, 88 columnConstructor: tr.ui.analysis.StringMemoryColumn 89 }, 90 { 91 condition: 'PSS', 92 importance: 5, 93 columnConstructor: tr.ui.analysis.NumericMemoryColumn 94 }, 95 { 96 condition: 'Private dirty', 97 importance: 4, 98 columnConstructor: tr.ui.analysis.NumericMemoryColumn 99 }, 100 { 101 condition: 'Private clean', 102 importance: 3, 103 columnConstructor: tr.ui.analysis.NumericMemoryColumn 104 }, 105 { 106 condition: 'Shared dirty', 107 importance: 2, 108 columnConstructor: tr.ui.analysis.NumericMemoryColumn 109 }, 110 { 111 condition: 'Shared clean', 112 importance: 1, 113 columnConstructor: tr.ui.analysis.NumericMemoryColumn 114 }, 115 { 116 condition: 'Swapped', 117 importance: 0, 118 columnConstructor: tr.ui.analysis.NumericMemoryColumn 119 } 120 ]; 121 122 var BYTE_STAT_COLUMN_MAP = { 123 'proportionalResident': 'PSS', 124 'privateDirtyResident': 'Private dirty', 125 'privateCleanResident': 'Private clean', 126 'sharedDirtyResident': 'Shared dirty', 127 'sharedCleanResident': 'Shared clean', 128 'swapped': 'Swapped' 129 }; 130 131 function hexString(address, is64BitAddress) { 132 if (address === undefined) 133 return undefined; 134 var hexPadding = is64BitAddress ? '0000000000000000' : '00000000'; 135 return (hexPadding + address.toString(16)).substr(-hexPadding.length); 136 } 137 138 function pruneEmptyRuleRows(row) { 139 if (row.subRows === undefined || row.subRows.length === 0) 140 return; 141 142 // Either all sub-rows are rule rows, or all sub-rows are VM region rows. 143 if (row.subRows[0].rule === undefined) { 144 // VM region rows: Early out to avoid filtering a large array for 145 // performance reasons (no sub-rows would be removed, but the whole array 146 // would be unnecessarily copied to a new array). 147 return; 148 } 149 150 row.subRows.forEach(pruneEmptyRuleRows); 151 row.subRows = row.subRows.filter(function(subRow) { 152 return subRow.subRows.length > 0; 153 }); 154 } 155 156 Polymer('tr-ui-a-memory-dump-vm-regions-details-pane', { 157 created: function() { 158 this.vmRegions_ = undefined; 159 this.aggregationMode_ = undefined; 160 }, 161 162 ready: function() { 163 this.$.table.selectionMode = tr.ui.b.TableFormat.SelectionMode.ROW; 164 }, 165 166 /** 167 * Sets the VM regions and schedules rebuilding the pane. 168 * 169 * The provided value should be a chronological list of lists of VM 170 * regions. All VM regions are assumed to belong to the same process. 171 * Example: 172 * 173 * [ 174 * [ 175 * // VM regions at timestamp 1. 176 * tr.model.VMRegion {}, 177 * tr.model.VMRegion {}, 178 * tr.model.VMRegion {} 179 * ], 180 * undefined, // No VM regions provided at timestamp 2. 181 * [ 182 * // VM regions at timestamp 3. 183 * tr.model.VMRegion {}, 184 * tr.model.VMRegion {} 185 * ] 186 * ] 187 */ 188 set vmRegions(vmRegions) { 189 this.vmRegions_ = vmRegions; 190 this.scheduleRebuildPane_(); 191 }, 192 193 get vmRegions() { 194 return this.vmRegions_; 195 }, 196 197 set aggregationMode(aggregationMode) { 198 this.aggregationMode_ = aggregationMode; 199 this.scheduleRebuildPane_(); 200 }, 201 202 get aggregationMode() { 203 return this.aggregationMode_; 204 }, 205 206 rebuildPane_: function() { 207 if (this.vmRegions_ === undefined || this.vmRegions_.length === 0) { 208 // Show the info text (hide the table). 209 this.$.info_text.style.display = 'block'; 210 this.$.table.style.display = 'none'; 211 212 this.$.table.clear(); 213 this.$.table.rebuild(); 214 return; 215 } 216 217 // Show the table (hide the info text). 218 this.$.info_text.style.display = 'none'; 219 this.$.table.style.display = 'block'; 220 221 var rows = this.createRows_(this.vmRegions_); 222 var columns = this.createColumns_(rows); 223 224 // Note: There is no need to aggregate fields of the VM regions because 225 // the classification tree already takes care of that. 226 227 this.$.table.tableRows = rows; 228 this.$.table.tableColumns = columns; 229 230 // TODO(petrcermak): This can be quite slow. Consider doing this somehow 231 // asynchronously. 232 this.$.table.rebuild(); 233 234 tr.ui.analysis.expandTableRowsRecursively(this.$.table); 235 }, 236 237 createRows_: function(timeToVmRegionTree) { 238 // Determine if any start address is outside the 32-bit range. 239 var is64BitAddress = timeToVmRegionTree.some(function(vmRegionTree) { 240 if (vmRegionTree === undefined) 241 return false; 242 return vmRegionTree.someRegion(function(region) { 243 if (region.startAddress === undefined) 244 return false; 245 return region.startAddress >= 4294967296 /* 2^32 */; 246 }); 247 }); 248 249 return [ 250 this.createClassificationNodeRow(timeToVmRegionTree, is64BitAddress) 251 ]; 252 }, 253 254 createClassificationNodeRow: function(timeToNode, is64BitAddress) { 255 // Get any defined classification node so that we can extract the 256 // properties which don't change over time. 257 var definedNode = tr.b.findFirstInArray(timeToNode); 258 259 // Child node ID (list index) -> Timestamp (list index) -> 260 // VM region classification node. 261 var childNodeIdToTimeToNode = tr.b.dictionaryValues( 262 tr.b.invertArrayOfDicts(timeToNode, function(node) { 263 var children = node.children; 264 if (children === undefined) 265 return undefined; 266 var childMap = {}; 267 children.forEach(function(childNode) { 268 if (!childNode.hasRegions) 269 return; 270 childMap[childNode.title] = childNode; 271 }); 272 return childMap; 273 })); 274 var childNodeSubRows = childNodeIdToTimeToNode.map( 275 function(timeToChildNode) { 276 return this.createClassificationNodeRow( 277 timeToChildNode, is64BitAddress); 278 }, this); 279 280 // Region ID (list index) -> Timestamp (list index) -> VM region. 281 var regionIdToTimeToRegion = tr.b.dictionaryValues( 282 tr.b.invertArrayOfDicts(timeToNode, function(node) { 283 var regions = node.regions; 284 if (regions === undefined) 285 return undefined; 286 return tr.b.arrayToDict(regions, function(region) { 287 return region.uniqueIdWithinProcess; 288 }); 289 })); 290 var regionSubRows = regionIdToTimeToRegion.map(function(timeToRegion) { 291 return this.createRegionRow_(timeToRegion, is64BitAddress); 292 }, this); 293 294 var subRows = childNodeSubRows.concat(regionSubRows); 295 296 return { 297 title: definedNode.title, 298 contexts: timeToNode, 299 variableCells: this.createVariableCells_(timeToNode), 300 subRows: subRows 301 }; 302 }, 303 304 createRegionRow_: function(timeToRegion, is64BitAddress) { 305 // Get any defined VM region so that we can extract the properties which 306 // don't change over time. 307 var definedRegion = tr.b.findFirstInArray(timeToRegion); 308 309 return { 310 title: definedRegion.mappedFile, 311 contexts: timeToRegion, 312 constantCells: this.createConstantCells_(definedRegion, is64BitAddress), 313 variableCells: this.createVariableCells_(timeToRegion) 314 }; 315 }, 316 317 /** 318 * Create cells for VM region properties which DON'T change over time. 319 * 320 * Note that there are currently no such properties of classification nodes. 321 */ 322 createConstantCells_: function(definedRegion, is64BitAddress) { 323 return tr.ui.analysis.createCells([definedRegion], function(region) { 324 var startAddress = region.startAddress; 325 if (startAddress === undefined) 326 return undefined; 327 return { 'Start address': hexString(startAddress, is64BitAddress) }; 328 }); 329 }, 330 331 /** 332 * Create cells for VM region (classification node) properties which DO 333 * change over time. 334 */ 335 createVariableCells_: function(timeToRegion) { 336 return tr.ui.analysis.createCells(timeToRegion, function(region) { 337 var fields = {}; 338 339 var sizeInBytes = region.sizeInBytes; 340 if (sizeInBytes !== undefined) { 341 fields['Virtual size'] = new ScalarNumeric( 342 sizeInBytes_smallerIsBetter, sizeInBytes); 343 } 344 var protectionFlags = region.protectionFlagsToString; 345 if (protectionFlags !== undefined) 346 fields['Protection flags'] = protectionFlags; 347 348 tr.b.iterItems(BYTE_STAT_COLUMN_MAP, 349 function(byteStatName, columnName) { 350 var byteStat = region.byteStats[byteStatName]; 351 if (byteStat === undefined) 352 return; 353 fields[columnName] = new ScalarNumeric( 354 sizeInBytes_smallerIsBetter, byteStat); 355 }); 356 357 return fields; 358 }); 359 }, 360 361 createColumns_: function(rows) { 362 var titleColumn = new tr.ui.analysis.TitleColumn('Mapped file'); 363 titleColumn.width = '200px'; 364 365 var constantColumns = tr.ui.analysis.MemoryColumn.fromRows( 366 rows, 'constantCells', undefined, CONSTANT_COLUMN_RULES); 367 var variableColumns = tr.ui.analysis.MemoryColumn.fromRows( 368 rows, 'variableCells', this.aggregationMode_, VARIABLE_COLUMN_RULES); 369 var fieldColumns = constantColumns.concat(variableColumns); 370 tr.ui.analysis.MemoryColumn.spaceEqually(fieldColumns); 371 372 var columns = [titleColumn].concat(fieldColumns); 373 return columns; 374 } 375 }); 376 377 return {}; 378}); 379</script> 380