1<!DOCTYPE html> 2<!-- 3Copyright 2016 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/base.html"> 9 10<script> 11'use strict'; 12 13/** 14 * @fileoverview Provides classes for representing and classifying VM regions. 15 * 16 * See https://goo.gl/5SSPv0 for more details. 17 */ 18tr.exportTo('tr.model', function() { 19 20 /** 21 * A single virtual memory region (also called a memory map). 22 * 23 * @constructor 24 */ 25 function VMRegion(startAddress, sizeInBytes, protectionFlags, 26 mappedFile, byteStats) { 27 this.startAddress = startAddress; 28 this.sizeInBytes = sizeInBytes; 29 this.protectionFlags = protectionFlags; 30 this.mappedFile = mappedFile || ''; 31 this.byteStats = byteStats || {}; 32 }; 33 34 VMRegion.PROTECTION_FLAG_READ = 4; 35 VMRegion.PROTECTION_FLAG_WRITE = 2; 36 VMRegion.PROTECTION_FLAG_EXECUTE = 1; 37 VMRegion.PROTECTION_FLAG_MAYSHARE = 128; 38 39 VMRegion.prototype = { 40 get uniqueIdWithinProcess() { 41 // This value is assumed to be unique within a process. 42 return this.mappedFile + '#' + this.startAddress; 43 }, 44 45 get protectionFlagsToString() { 46 if (this.protectionFlags === undefined) 47 return undefined; 48 return ( 49 (this.protectionFlags & VMRegion.PROTECTION_FLAG_READ ? 'r' : '-') + 50 (this.protectionFlags & VMRegion.PROTECTION_FLAG_WRITE ? 'w' : '-') + 51 (this.protectionFlags & VMRegion.PROTECTION_FLAG_EXECUTE ? 52 'x' : '-') + 53 (this.protectionFlags & VMRegion.PROTECTION_FLAG_MAYSHARE ? 's' : 'p') 54 ); 55 } 56 }; 57 58 VMRegion.fromDict = function(dict) { 59 return new VMRegion( 60 dict.startAddress, 61 dict.sizeInBytes, 62 dict.protectionFlags, 63 dict.mappedFile, 64 dict.byteStats); 65 }; 66 67 /** 68 * Node in a VM region classification tree. 69 * 70 * Note: Most users of this class should use the 71 * VMRegionClassificationNode.fromRegions static method instead of this 72 * constructor because it leads to better performance due to fewer memory 73 * allocations. 74 * 75 * @constructor 76 */ 77 function VMRegionClassificationNode(opt_rule) { 78 this.rule_ = opt_rule || VMRegionClassificationNode.CLASSIFICATION_RULES; 79 80 // True iff this node or any of its descendant classification nodes has at 81 // least one classified VM region. 82 this.hasRegions = false; 83 84 // Total virtual size and byte stats of all regions matching this node's 85 // rule (including its sub-rules). 86 this.sizeInBytes = undefined; 87 this.byteStats = {}; 88 89 // Array of child classification nodes if this is an intermediate node. 90 this.children_ = undefined; 91 92 // Array of VM regions. If this is an intermediate node, then the regions 93 // are cached for lazy tree construction (i.e. its child classification 94 // nodes yet have to be built). 95 this.regions_ = []; 96 } 97 98 /** 99 * Rules for classifying memory maps. 100 * 101 * These rules are derived from core/jni/android_os_Debug.cpp in Android. 102 */ 103 VMRegionClassificationNode.CLASSIFICATION_RULES = { 104 name: 'Total', 105 children: [ 106 { 107 name: 'Android', 108 file: /^\/dev\/ashmem(?!\/libc malloc)/, 109 children: [ 110 { 111 name: 'Java runtime', 112 file: /^\/dev\/ashmem\/dalvik-/, 113 children: [ 114 { 115 name: 'Spaces', 116 file: /\/dalvik-(alloc|main|large object|non moving|zygote) space/, // @suppress longLineCheck 117 children: [ 118 { 119 name: 'Normal', 120 file: /\/dalvik-(alloc|main)/ 121 }, 122 { 123 name: 'Large', 124 file: /\/dalvik-large object/ 125 }, 126 { 127 name: 'Zygote', 128 file: /\/dalvik-zygote/ 129 }, 130 { 131 name: 'Non-moving', 132 file: /\/dalvik-non moving/ 133 } 134 ] 135 }, 136 { 137 name: 'Linear Alloc', 138 file: /\/dalvik-LinearAlloc/ 139 }, 140 { 141 name: 'Indirect Reference Table', 142 file: /\/dalvik-indirect.ref/ 143 }, 144 { 145 name: 'Cache', 146 file: /\/dalvik-jit-code-cache/ 147 }, 148 { 149 name: 'Accounting' 150 } 151 ] 152 }, 153 { 154 name: 'Cursor', 155 file: /\/CursorWindow/ 156 }, 157 { 158 name: 'Ashmem' 159 } 160 ] 161 }, 162 { 163 name: 'Native heap', 164 file: /^((\[heap\])|(\[anon:)|(\/dev\/ashmem\/libc malloc)|(\[discounted tracing overhead\])|$)/ // @suppress longLineCheck 165 }, 166 { 167 name: 'Stack', 168 file: /^\[stack/ 169 }, 170 { 171 name: 'Files', 172 file: /\.((((jar)|(apk)|(ttf)|(odex)|(oat)|(art))$)|(dex)|(so))/, 173 children: [ 174 { 175 name: 'so', 176 file: /\.so/ 177 }, 178 { 179 name: 'jar', 180 file: /\.jar$/ 181 }, 182 { 183 name: 'apk', 184 file: /\.apk$/ 185 }, 186 { 187 name: 'ttf', 188 file: /\.ttf$/ 189 }, 190 { 191 name: 'dex', 192 file: /\.((dex)|(odex$))/ 193 }, 194 { 195 name: 'oat', 196 file: /\.oat$/ 197 }, 198 { 199 name: 'art', 200 file: /\.art$/ 201 } 202 ] 203 }, 204 { 205 name: 'Devices', 206 file: /(^\/dev\/)|(anon_inode:dmabuf)/, 207 children: [ 208 { 209 name: 'GPU', 210 file: /\/((nv)|(mali)|(kgsl))/ 211 }, 212 { 213 name: 'DMA', 214 file: /anon_inode:dmabuf/ 215 } 216 ] 217 } 218 ] 219 }; 220 VMRegionClassificationNode.OTHER_RULE = { name: 'Other' }; 221 222 VMRegionClassificationNode.fromRegions = function(regions, opt_rules) { 223 var tree = new VMRegionClassificationNode(opt_rules); 224 tree.regions_ = regions; 225 for (var i = 0; i < regions.length; i++) 226 tree.addStatsFromRegion_(regions[i]); 227 return tree; 228 }; 229 230 VMRegionClassificationNode.prototype = { 231 get title() { 232 return this.rule_.name; 233 }, 234 235 get children() { 236 if (this.isLeafNode) 237 return undefined; // Leaf nodes don't have children (by definition). 238 if (this.children_ === undefined) 239 this.buildTree_(); // Lazily classify VM regions. 240 return this.children_; 241 }, 242 243 get regions() { 244 if (!this.isLeafNode) { 245 // Intermediate nodes only temporarily cache VM regions for lazy tree 246 // construction. 247 return undefined; 248 } 249 return this.regions_; 250 }, 251 252 get allRegionsForTesting() { 253 if (this.regions_ !== undefined) { 254 if (this.children_ !== undefined) { 255 throw new Error('Internal error: a VM region classification node ' + 256 'cannot have both regions and children'); 257 } 258 // Leaf node (or caching internal node). 259 return this.regions_; 260 } 261 262 // Intermediate node. 263 var regions = []; 264 this.children_.forEach(function(childNode) { 265 regions = regions.concat(childNode.allRegionsForTesting); 266 }); 267 return regions; 268 }, 269 270 get isLeafNode() { 271 var children = this.rule_.children; 272 return children === undefined || children.length === 0; 273 }, 274 275 addRegion: function(region) { 276 this.addRegionRecursively_(region, true /* addStatsToThisNode */); 277 }, 278 279 someRegion: function(fn, opt_this) { 280 if (this.regions_ !== undefined) { 281 // Leaf node (or caching internal node). 282 return this.regions_.some(fn, opt_this); 283 } 284 285 // Intermediate node. 286 return this.children_.some(function(childNode) { 287 return childNode.someRegion(fn, opt_this); 288 }); 289 }, 290 291 addRegionRecursively_: function(region, addStatsToThisNode) { 292 if (addStatsToThisNode) 293 this.addStatsFromRegion_(region); 294 295 if (this.regions_ !== undefined) { 296 if (this.children_ !== undefined) { 297 throw new Error('Internal error: a VM region classification node ' + 298 'cannot have both regions and children'); 299 } 300 // Leaf node or an intermediate node caching VM regions (add the 301 // region to this node and don't classify further). 302 this.regions_.push(region); 303 return; 304 } 305 306 // Non-leaf rule (classify region row further down the tree). 307 function regionRowMatchesChildNide(child) { 308 var fileRegExp = child.rule_.file; 309 if (fileRegExp === undefined) 310 return true; 311 return fileRegExp.test(region.mappedFile); 312 } 313 314 var matchedChild = tr.b.findFirstInArray( 315 this.children_, regionRowMatchesChildNide); 316 if (matchedChild === undefined) { 317 // Region belongs to the 'Other' node (created lazily). 318 if (this.children_.length !== this.rule_.children.length) 319 throw new Error('Internal error'); 320 matchedChild = new VMRegionClassificationNode( 321 VMRegionClassificationNode.OTHER_RULE); 322 this.children_.push(matchedChild); 323 } 324 325 matchedChild.addRegionRecursively_(region, true); 326 }, 327 328 buildTree_: function() { 329 var cachedRegions = this.regions_; 330 this.regions_ = undefined; 331 332 this.buildChildNodesRecursively_(); 333 for (var i = 0; i < cachedRegions.length; i++) { 334 // Note that we don't add the VM region's stats to this node because 335 // they have already been added to it. 336 this.addRegionRecursively_( 337 cachedRegions[i], false /* addStatsToThisNode */); 338 } 339 }, 340 341 buildChildNodesRecursively_: function() { 342 if (this.children_ !== undefined) { 343 throw new Error( 344 'Internal error: Classification node already has children'); 345 } 346 if (this.regions_ !== undefined && this.regions_.length !== 0) { 347 throw new Error( 348 'Internal error: Classification node should have no regions'); 349 } 350 351 if (this.isLeafNode) 352 return; // Leaf node: Nothing to do. 353 354 // Intermediate node: Clear regions and build children recursively. 355 this.regions_ = undefined; 356 this.children_ = this.rule_.children.map(function(childRule) { 357 var child = new VMRegionClassificationNode(childRule); 358 child.buildChildNodesRecursively_(); 359 return child; 360 }); 361 }, 362 363 addStatsFromRegion_: function(region) { 364 this.hasRegions = true; 365 366 // Aggregate virtual size. 367 var regionSizeInBytes = region.sizeInBytes; 368 if (regionSizeInBytes !== undefined) 369 this.sizeInBytes = (this.sizeInBytes || 0) + regionSizeInBytes; 370 371 // Aggregate byte stats. 372 var thisByteStats = this.byteStats; 373 var regionByteStats = region.byteStats; 374 for (var byteStatName in regionByteStats) { 375 var regionByteStatValue = regionByteStats[byteStatName]; 376 if (regionByteStatValue === undefined) 377 continue; 378 thisByteStats[byteStatName] = 379 (thisByteStats[byteStatName] || 0) + regionByteStatValue; 380 } 381 } 382 }; 383 384 return { 385 VMRegion: VMRegion, 386 VMRegionClassificationNode: VMRegionClassificationNode 387 }; 388}); 389</script> 390