global_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 GlobalMemoryDump class. 18 */ 19tr.exportTo('tr.model', function() { 20 /** 21 * The GlobalMemoryDump represents a simultaneous memory dump of all 22 * processes. 23 * @constructor 24 */ 25 function GlobalMemoryDump(model, start) { 26 tr.model.ContainerMemoryDump.call(this, start); 27 this.model = model; 28 this.processMemoryDumps = {}; 29 } 30 31 var SIZE_ATTRIBUTE_NAME = 'size'; 32 var EFFECTIVE_SIZE_ATTRIBUTE_NAME = 'effective_size'; 33 34 function getSize(dump) { 35 var attr = dump.attributes[SIZE_ATTRIBUTE_NAME]; 36 if (attr === undefined) 37 return 0; 38 return attr.value; 39 } 40 41 function hasSize(dump) { 42 return dump.attributes[SIZE_ATTRIBUTE_NAME] !== undefined; 43 } 44 45 function optional(value, defaultValue) { 46 if (value === undefined) 47 return defaultValue; 48 return value; 49 } 50 51 function ownershipToUserFriendlyString(dump, importance) { 52 return dump.quantifiedName + ' (importance: ' + 53 optional(importance, 0) + ')'; 54 } 55 56 GlobalMemoryDump.prototype = { 57 __proto__: tr.model.ContainerMemoryDump.prototype, 58 59 get userFriendlyName() { 60 return 'Global memory dump at ' + tr.b.u.TimeStamp.format(this.start); 61 }, 62 63 get containerName() { 64 return 'global space'; 65 }, 66 67 calculateGraphAttributes: function() { 68 // 1. Calculate the sizes of all memory allocator dumps (MADs). 69 this.calculateSizes(); 70 71 // 2. Calculate the effective sizes of all MADs. This step requires that 72 // the sizes of all MADs have already been calculated (step 1). 73 this.calculateEffectiveSizes(); 74 75 // 3. Aggregate all other attributes of all MADs. This step must be 76 // carried out after the sizes of all MADs were calculated (step 1). 77 // Otherwise, the sizes of all MADs would be aggregated as a direct sums 78 // of their children, which would most likely lead to double-counting. 79 this.aggregateAttributes(); 80 81 // 4. Discount tracing from VM regions stats and malloc or winheap 82 // allocator stats. This steps requires that the sizes (step 1), 83 // effective sizes (step 2), and resident sizes (step 3) of the relevant 84 // MADs have already been calculated. 85 this.discountTracingOverhead(); 86 }, 87 88 /** 89 * Calculate the size of all memory allocator dumps in the dump graph. 90 * 91 * The size refers to the allocated size of a (sub)component. It is a 92 * natural extension of the optional size attribute provided by 93 * MemoryAllocatorDump(s): 94 * 95 * - If a MAD provides a size attribute, then its size is assumed to be 96 * equal to it. 97 * - If a MAD does not provide a size attribute, then its size is assumed 98 * to be the maximum of (1) the size of the largest owner of the MAD 99 * and (2) the aggregated size of the MAD's children. 100 * 101 * Metric motivation: "How big is a (sub)system?" 102 * 103 * Please refer to the Memory Dump Graph Metric Calculation design document 104 * for more details (https://goo.gl/fKg0dt). 105 */ 106 calculateSizes: function() { 107 this.traverseAllocatorDumpsInDepthFirstPostOrder( 108 this.calculateMemoryAllocatorDumpSize_.bind(this)); 109 }, 110 111 /** 112 * Calculate the size of the given MemoryAllocatorDump. This method assumes 113 * that the size of both the children and owners of the dump has already 114 * been calculated. 115 */ 116 calculateMemoryAllocatorDumpSize_: function(dump) { 117 // This flag becomes true if the size attribute of the current dump 118 // should be defined, i.e. if (1) the current dump's size attribute is 119 // defined, (2) the size of at least one of its children is defined or 120 // (3) the size of at least one of its owners is defined. 121 var shouldDefineSize = false; 122 123 // This helper function returns the numeric value of the size attribute 124 // of the given dependent memory allocator dump. If the attribute is 125 // defined, the shouldDefineSize flag above is also set to true (because 126 // condition (2) or (3) is satisfied). Otherwise, zero is returned (and 127 // the flag is left unchanged). 128 function getDependencySize(dependencyDump) { 129 var attr = dependencyDump.attributes[SIZE_ATTRIBUTE_NAME]; 130 if (attr === undefined) 131 return 0; 132 shouldDefineSize = true; 133 return attr.value; 134 } 135 136 // 1. Get the size provided by the dump. If present, define a function 137 // for checking dependent size consistency (a dump must always be bigger 138 // than all its children aggregated together and/or its largest owner). 139 var sizeAttribute = dump.getValidSizeAttributeOrUndefined( 140 SIZE_ATTRIBUTE_NAME, this.model); 141 var size = 0; 142 var infos = []; 143 var checkDependentSizeIsConsistent = function() { /* no-op */ }; 144 if (sizeAttribute !== undefined) { 145 size = sizeAttribute.value; 146 shouldDefineSize = true; 147 checkDependentSizeIsConsistent = function(dependentSize, 148 dependentName) { 149 if (size >= dependentSize) 150 return; 151 var messageSuffix = ' (' + tr.b.u.Units.sizeInBytes.format(size) + 152 ') is less than ' + dependentName + ' (' + 153 tr.b.u.Units.sizeInBytes.format(dependentSize) + ').'; 154 this.model.importWarning({ 155 type: 'memory_dump_parse_error', 156 message: 'Size provided by memory allocator dump \'' + 157 dump.fullName + '\'' + messageSuffix 158 }); 159 infos.push(new tr.model.AttributeInfo( 160 tr.model.AttributeInfoType.WARNING, 161 'Size provided by this memory allocator dump' + messageSuffix)); 162 }.bind(this); 163 } 164 165 // 2. Aggregate size of children. The recursive function traverses all 166 // descendants and ensures that double-counting due to ownership within a 167 // subsystem is avoided. 168 var aggregatedChildrenSize = 0; 169 // Owned child dump name -> (Owner child dump name -> overlapping size). 170 var allOverlaps = {}; 171 dump.children.forEach(function(childDump) { 172 function aggregateDescendantDump(descendantDump) { 173 // Don't count this descendant dump if it owns another descendant of 174 // the current dump (would cause double-counting). 175 var ownedDumpLink = descendantDump.owns; 176 if (ownedDumpLink !== undefined && 177 ownedDumpLink.target.isDescendantOf(dump)) { 178 // If the target owned dump is a descendant of a *different* child 179 // of the the current dump (i.e. not childDump), then we remember 180 // the ownership so that we could explain why the size of the 181 // current dump is not equal to the sum of its children. 182 var ownedDescendantDump = ownedDumpLink.target; 183 var ownedChildDump = ownedDescendantDump; 184 while (ownedChildDump.parent !== dump) 185 ownedChildDump = ownedChildDump.parent; 186 if (childDump !== ownedChildDump) { 187 var overlap = getDependencySize(descendantDump); 188 if (overlap > 0) { 189 // Owner child dump -> total overlapping size. 190 var ownedChildOverlaps = allOverlaps[ownedChildDump.name]; 191 if (ownedChildOverlaps === undefined) 192 allOverlaps[ownedChildDump.name] = ownedChildOverlaps = {}; 193 var previousTotalOverlap = 194 ownedChildOverlaps[childDump.name] || 0; 195 var updatedTotalOverlap = previousTotalOverlap + overlap; 196 ownedChildOverlaps[childDump.name] = updatedTotalOverlap; 197 } 198 } 199 return; 200 } 201 202 // If this descendant dump is a leaf node, add its size to the 203 // aggregated size. 204 if (descendantDump.children.length === 0) { 205 aggregatedChildrenSize += getDependencySize(descendantDump); 206 return; 207 } 208 209 // If this descendant dump is an intermediate node, recurse down into 210 // its children. Note that the dump's size is NOT added because it is 211 // an aggregate of its children (would cause double-counting). 212 descendantDump.children.forEach(aggregateDescendantDump); 213 } 214 aggregateDescendantDump(childDump); 215 }); 216 // If the size of the dump is not equal to the sum of its children, add 217 // infos to its children explaining the difference. 218 dump.children.forEach(function(childDump) { 219 var childOverlaps = allOverlaps[childDump.name]; 220 if (childOverlaps === undefined) 221 return; 222 223 var message = tr.b.dictionaryValues(tr.b.mapItems(childOverlaps, 224 function(ownerChildName, overlap) { 225 return 'overlaps with its sibling \'' + ownerChildName + '\' (' + 226 tr.b.u.Units.sizeInBytes.format(overlap) + ')'; 227 })).join(' '); 228 229 childDump.attributes[SIZE_ATTRIBUTE_NAME].infos.push( 230 new tr.model.AttributeInfo( 231 tr.model.AttributeInfoType.INFORMATION, message)); 232 }); 233 checkDependentSizeIsConsistent( 234 aggregatedChildrenSize, 'the aggregated size of its children'); 235 236 // 3. Calculate the largest owner size. 237 var largestOwnerSize = 0; 238 dump.ownedBy.forEach(function(ownershipLink) { 239 var owner = ownershipLink.source; 240 var ownerSize = getDependencySize(owner); 241 largestOwnerSize = Math.max(largestOwnerSize, ownerSize); 242 }); 243 checkDependentSizeIsConsistent( 244 largestOwnerSize, 'the size of its largest owner'); 245 246 // If neither the dump nor any of its dependencies (children and owners) 247 // provide a size, do NOT add a zero size attribute. 248 if (!shouldDefineSize) { 249 // The rest of the pipeline relies on size being either a valid 250 // ScalarAttribute, or undefined. 251 dump.attributes[SIZE_ATTRIBUTE_NAME] = undefined; 252 return; 253 } 254 255 // A dump must always be bigger than all its children aggregated 256 // together and/or its largest owner. 257 size = Math.max(size, aggregatedChildrenSize, largestOwnerSize); 258 259 var sizeAttribute = new tr.model.ScalarAttribute('bytes', size); 260 sizeAttribute.infos = infos; 261 dump.attributes[SIZE_ATTRIBUTE_NAME] = sizeAttribute; 262 263 // Add a virtual child to make up for extra size of the dump with 264 // respect to its children (if applicable). 265 if (aggregatedChildrenSize < size && 266 dump.children !== undefined && dump.children.length > 0) { 267 var virtualChild = new tr.model.MemoryAllocatorDump( 268 dump.containerMemoryDump, dump.fullName + '/<unspecified>'); 269 virtualChild.parent = dump; 270 dump.children.unshift(virtualChild); 271 virtualChild.attributes[SIZE_ATTRIBUTE_NAME] = 272 new tr.model.ScalarAttribute( 273 'bytes', size - aggregatedChildrenSize); 274 } 275 }, 276 277 /** 278 * Calculate the effective size of all memory allocator dumps in the dump 279 * graph. 280 * 281 * The effective size refers to the amount of memory a particular component 282 * is using/consuming. In other words, every (reported) byte of used memory 283 * is uniquely attributed to exactly one component. Consequently, unlike 284 * size, effective size is cumulative, i.e. the sum of the effective sizes 285 * of (top-level) components is equal to the total amount of (reported) 286 * used memory. 287 * 288 * Metric motivation: "How much memory does a (sub)system use?" or "For how 289 * much memory should a (sub)system be 'charged'?" 290 * 291 * Please refer to the Memory Dump Graph Metric Calculation design document 292 * for more details (https://goo.gl/fKg0dt). 293 * 294 * This method assumes that the size of all contained memory allocator 295 * dumps has already been calculated [see calculateSizes()]. 296 */ 297 calculateEffectiveSizes: function() { 298 // 1. Calculate not-owned and not-owning sub-sizes of all MADs 299 // (depth-first post-order traversal). 300 this.traverseAllocatorDumpsInDepthFirstPostOrder( 301 this.calculateDumpSubSizes_.bind(this)); 302 303 // 2. Calculate owned and owning coefficients of owned and owner MADs 304 // respectively (arbitrary traversal). 305 this.traverseAllocatorDumpsInDepthFirstPostOrder( 306 this.calculateDumpOwnershipCoefficient_.bind(this)); 307 308 // 3. Calculate cumulative owned and owning coefficients of all MADs 309 // (depth-first pre-order traversal). 310 this.traverseAllocatorDumpsInDepthFirstPreOrder( 311 this.calculateDumpCumulativeOwnershipCoefficient_.bind(this)); 312 313 // 4. Calculate the effective sizes of all MADs (depth-first post-order 314 // traversal). 315 this.traverseAllocatorDumpsInDepthFirstPostOrder( 316 this.calculateDumpEffectiveSize_.bind(this)); 317 }, 318 319 /** 320 * Calculate not-owned and not-owning sub-sizes of a memory allocator dump 321 * from its children's (sub-)sizes. 322 * 323 * Not-owned sub-size refers to the aggregated memory of all children which 324 * is not owned by other MADs. Conversely, not-owning sub-size is the 325 * aggregated memory of all children which do not own another MAD. The 326 * diagram below illustrates these two concepts: 327 * 328 * ROOT 1 ROOT 2 329 * size: 4 size: 5 330 * not-owned sub-size: 4 not-owned sub-size: 1 (!) 331 * not-owning sub-size: 0 (!) not-owning sub-size: 5 332 * 333 * ^ ^ 334 * | | 335 * 336 * PARENT 1 ===== owns =====> PARENT 2 337 * size: 4 size: 5 338 * not-owned sub-size: 4 not-owned sub-size: 5 339 * not-owning sub-size: 4 not-owning sub-size: 5 340 * 341 * ^ ^ 342 * | | 343 * 344 * CHILD 1 CHILD 2 345 * size [given]: 4 size [given]: 5 346 * not-owned sub-size: 4 not-owned sub-size: 5 347 * not-owning sub-size: 4 not-owning sub-size: 5 348 * 349 * This method assumes that (1) the size of the dump, its children, and its 350 * owners [see calculateSizes()] and (2) the not-owned and not-owning 351 * sub-sizes of both the children and owners of the dump have already been 352 * calculated [depth-first post-order traversal]. 353 */ 354 calculateDumpSubSizes_: function(dump) { 355 // Completely skip dumps with undefined size. 356 if (!hasSize(dump)) 357 return; 358 359 // If the dump is a leaf node, then both sub-sizes are equal to the size. 360 if (dump.children === undefined || dump.children.length === 0) { 361 var size = getSize(dump); 362 dump.notOwningSubSize_ = size; 363 dump.notOwnedSubSize_ = size; 364 return; 365 } 366 367 // Calculate this dump's not-owning sub-size by summing up the not-owning 368 // sub-sizes of children MADs which do not own another MAD. 369 var notOwningSubSize = 0; 370 dump.children.forEach(function(childDump) { 371 if (childDump.owns !== undefined) 372 return; 373 notOwningSubSize += optional(childDump.notOwningSubSize_, 0); 374 }); 375 dump.notOwningSubSize_ = notOwningSubSize; 376 377 // Calculate this dump's not-owned sub-size. 378 var notOwnedSubSize = 0; 379 dump.children.forEach(function(childDump) { 380 // If the child dump is not owned, then add its not-owned sub-size. 381 if (childDump.ownedBy.length === 0) { 382 notOwnedSubSize += optional(childDump.notOwnedSubSize_, 0); 383 return; 384 } 385 // If the child dump is owned, then add the difference between its size 386 // and the largest owner. 387 var largestChildOwnerSize = 0; 388 childDump.ownedBy.forEach(function(ownershipLink) { 389 largestChildOwnerSize = Math.max( 390 largestChildOwnerSize, getSize(ownershipLink.source)); 391 }); 392 notOwnedSubSize += getSize(childDump) - largestChildOwnerSize; 393 }); 394 dump.notOwnedSubSize_ = notOwnedSubSize; 395 }, 396 397 /** 398 * Calculate owned and owning coefficients of a memory allocator dump and 399 * its owners. 400 * 401 * The owning coefficient refers to the proportion of a dump's not-owning 402 * sub-size which is attributed to the dump (only relevant to owning MADs). 403 * Conversely, the owned coefficient is the proportion of a dump's 404 * not-owned sub-size, which is attributed to it (only relevant to owned 405 * MADs). 406 * 407 * The not-owned size of the owned dump is split among its owners in the 408 * order of the ownership importance as demonstrated by the following 409 * example: 410 * 411 * memory allocator dumps 412 * OWNED OWNER1 OWNER2 OWNER3 OWNER4 413 * not-owned sub-size [given] 10 - - - - 414 * not-owning sub-size [given] - 6 7 5 8 415 * importance [given] - 2 2 1 0 416 * attributed not-owned sub-size 2 - - - - 417 * attributed not-owning sub-size - 3 4 0 1 418 * owned coefficient 2/10 - - - - 419 * owning coefficient - 3/6 4/7 0/5 1/8 420 * 421 * Explanation: Firstly, 6 bytes are split equally among OWNER1 and OWNER2 422 * (highest importance). OWNER2 owns one more byte, so its attributed 423 * not-owning sub-size is 6/2 + 1 = 4 bytes. OWNER3 is attributed no size 424 * because it is smaller than the owners with higher priority. However, 425 * OWNER4 is larger, so it's attributed the difference 8 - 7 = 1 byte. 426 * Finally, 2 bytes remain unattributed and are hence kept in the OWNED 427 * dump as attributed not-owned sub-size. The coefficients are then 428 * directly calculated as fractions of the sub-sizes and corresponding 429 * attributed sub-sizes. 430 * 431 * Note that we always assume that all ownerships of a dump overlap (e.g. 432 * OWNER3 is subsumed by both OWNER1 and OWNER2). Hence, the table could 433 * be alternatively represented as follows: 434 * 435 * owned memory range 436 * 0 1 2 3 4 5 6 7 8 9 10 437 * Priority 2 | OWNER1 + OWNER2 (split) | OWNER2 | 438 * Priority 1 | (already attributed) | 439 * Priority 0 | - - - (already attributed) - - - | OWNER4 | 440 * Remainder | - - - - - (already attributed) - - - - - - | OWNED | 441 * 442 * This method assumes that (1) the size of the dump [see calculateSizes()] 443 * and (2) the not-owned size of the dump and not-owning sub-sizes of its 444 * owners [see the first step of calculateEffectiveSizes()] have already 445 * been calculated. Note that the method doesn't make any assumptions about 446 * the order in which dumps are visited. 447 */ 448 calculateDumpOwnershipCoefficient_: function(dump) { 449 // Completely skip dumps with undefined size. 450 if (!hasSize(dump)) 451 return; 452 453 // We only need to consider owned dumps. 454 if (dump.ownedBy.length === 0) 455 return; 456 457 // Sort the owners in decreasing order of ownership importance and 458 // increasing order of not-owning sub-size (in case of equal importance). 459 var owners = dump.ownedBy.map(function(ownershipLink) { 460 return { 461 dump: ownershipLink.source, 462 importance: optional(ownershipLink.importance, 0), 463 notOwningSubSize: optional(ownershipLink.source.notOwningSubSize_, 0) 464 }; 465 }); 466 owners.sort(function(a, b) { 467 if (a.importance === b.importance) 468 return a.notOwningSubSize - b.notOwningSubSize; 469 return b.importance - a.importance; 470 }); 471 472 // Loop over the list of owners and distribute the owned dump's not-owned 473 // sub-size among them according to their ownership importance and 474 // not-owning sub-size. 475 var currentImportanceStartPos = 0; 476 var alreadyAttributedSubSize = 0; 477 while (currentImportanceStartPos < owners.length) { 478 var currentImportance = owners[currentImportanceStartPos].importance; 479 480 // Find the position of the first owner with lower priority. 481 var nextImportanceStartPos = currentImportanceStartPos + 1; 482 while (nextImportanceStartPos < owners.length && 483 owners[nextImportanceStartPos].importance === 484 currentImportance) { 485 nextImportanceStartPos++; 486 } 487 488 // Visit the owners with the same importance in increasing order of 489 // not-owned sub-size, split the owned memory among them appropriately, 490 // and calculate their owning coefficients. 491 var attributedNotOwningSubSize = 0; 492 for (var pos = currentImportanceStartPos; pos < nextImportanceStartPos; 493 pos++) { 494 var owner = owners[pos]; 495 var notOwningSubSize = owner.notOwningSubSize; 496 if (notOwningSubSize > alreadyAttributedSubSize) { 497 attributedNotOwningSubSize += 498 (notOwningSubSize - alreadyAttributedSubSize) / 499 (nextImportanceStartPos - pos); 500 alreadyAttributedSubSize = notOwningSubSize; 501 } 502 503 var owningCoefficient = 0; 504 if (notOwningSubSize !== 0) 505 owningCoefficient = attributedNotOwningSubSize / notOwningSubSize; 506 owner.dump.owningCoefficient_ = owningCoefficient; 507 } 508 509 currentImportanceStartPos = nextImportanceStartPos; 510 } 511 512 // Attribute the remainder of the owned dump's not-owned sub-size to 513 // the dump itself and calculate its owned coefficient. 514 var notOwnedSubSize = optional(dump.notOwnedSubSize_, 0); 515 var remainderSubSize = notOwnedSubSize - alreadyAttributedSubSize; 516 var ownedCoefficient = 0; 517 if (notOwnedSubSize !== 0) 518 ownedCoefficient = remainderSubSize / notOwnedSubSize; 519 dump.ownedCoefficient_ = ownedCoefficient; 520 }, 521 522 /** 523 * Calculate cumulative owned and owning coefficients of a memory allocator 524 * dump from its (non-cumulative) owned and owning coefficients and the 525 * cumulative coefficients of its parent and/or owned dump. 526 * 527 * The cumulative coefficients represent the total effect of all 528 * (non-strict) ancestor ownerships on a memory allocator dump. The 529 * cumulative owned coefficient of a MAD can be calculated simply as: 530 * 531 * cumulativeOwnedC(M) = ownedC(M) * cumulativeOwnedC(parent(M)) 532 * 533 * This reflects the assumption that if a parent of a child MAD is 534 * (partially) owned, then the parent's owner also indirectly owns (a part 535 * of) the child MAD. 536 * 537 * The cumulative owning coefficient of a MAD depends on whether the MAD 538 * owns another dump: 539 * 540 * [if M doesn't own another MAD] 541 * / cumulativeOwningC(parent(M)) 542 * cumulativeOwningC(M) = 543 * \ [if M owns another MAD] 544 * owningC(M) * cumulativeOwningC(owned(M)) 545 * 546 * The reasoning behind the first case is similar to the one for cumulative 547 * owned coefficient above. The only difference is that we don't need to 548 * include the dump's (non-cumulative) owning coefficient because it is 549 * implicitly 1. 550 * 551 * The formula for the second case is derived as follows: Since the MAD 552 * owns another dump, its memory is not included in its parent's not-owning 553 * sub-size and hence shouldn't be affected by the parent's corresponding 554 * cumulative coefficient. Instead, the MAD indirectly owns everything 555 * owned by its owned dump (and so it should be affected by the 556 * corresponding coefficient). 557 * 558 * Note that undefined coefficients (and coefficients of non-existent 559 * dumps) are implicitly assumed to be 1. 560 * 561 * This method assumes that (1) the size of the dump [see calculateSizes()], 562 * (2) the (non-cumulative) owned and owning coefficients of the dump [see 563 * the second step of calculateEffectiveSizes()], and (3) the cumulative 564 * coefficients of the dump's parent and owned MADs (if present) 565 * [depth-first pre-order traversal] have already been calculated. 566 */ 567 calculateDumpCumulativeOwnershipCoefficient_: function(dump) { 568 // Completely skip dumps with undefined size. 569 if (!hasSize(dump)) 570 return; 571 572 var cumulativeOwnedCoefficient = optional(dump.ownedCoefficient_, 1); 573 var parent = dump.parent; 574 if (dump.parent !== undefined) 575 cumulativeOwnedCoefficient *= dump.parent.cumulativeOwnedCoefficient_; 576 dump.cumulativeOwnedCoefficient_ = cumulativeOwnedCoefficient; 577 578 var cumulativeOwningCoefficient; 579 if (dump.owns !== undefined) { 580 cumulativeOwningCoefficient = dump.owningCoefficient_ * 581 dump.owns.target.cumulativeOwningCoefficient_; 582 } else if (dump.parent !== undefined) { 583 cumulativeOwningCoefficient = dump.parent.cumulativeOwningCoefficient_; 584 } else { 585 cumulativeOwningCoefficient = 1; 586 } 587 dump.cumulativeOwningCoefficient_ = cumulativeOwningCoefficient; 588 }, 589 590 /** 591 * Calculate the effective size of a memory allocator dump. 592 * 593 * In order to simplify the (already complex) calculation, we use the fact 594 * that effective size is cumulative (unlike regular size), i.e. the 595 * effective size of a non-leaf node is equal to the sum of effective sizes 596 * of its children. The effective size of a leaf MAD is calculated as: 597 * 598 * effectiveSize(M) = size(M) * cumulativeOwningC(M) * cumulativeOwnedC(M) 599 * 600 * This method assumes that (1) the size of the dump and its children [see 601 * calculateSizes()] and (2) the cumulative owning and owned coefficients 602 * of the dump (if it's a leaf node) [see the third step of 603 * calculateEffectiveSizes()] or the effective sizes of its children (if 604 * it's a non-leaf node) [depth-first post-order traversal] have already 605 * been calculated. 606 */ 607 calculateDumpEffectiveSize_: function(dump) { 608 // Completely skip dumps with undefined size. As a result, each dump will 609 // have defined effective size if and only if it has defined size. 610 if (!hasSize(dump)) { 611 // The rest of the pipeline relies on effective size being either a 612 // valid ScalarAttribute, or undefined. 613 dump.attributes[EFFECTIVE_SIZE_ATTRIBUTE_NAME] = undefined; 614 return; 615 } 616 617 var effectiveSize; 618 if (dump.children === undefined || dump.children.length === 0) { 619 // Leaf dump. 620 effectiveSize = getSize(dump) * dump.cumulativeOwningCoefficient_ * 621 dump.cumulativeOwnedCoefficient_; 622 } else { 623 // Non-leaf dump. 624 effectiveSize = 0; 625 dump.children.forEach(function(childDump) { 626 if (!hasSize(childDump)) 627 return; 628 effectiveSize += 629 childDump.attributes[EFFECTIVE_SIZE_ATTRIBUTE_NAME].value; 630 }); 631 } 632 var attribute = new tr.model.ScalarAttribute('bytes', effectiveSize); 633 dump.attributes[EFFECTIVE_SIZE_ATTRIBUTE_NAME] = attribute; 634 635 // Add attribute infos regarding ownership (if applicable). 636 // TODO(petrcermak): This belongs to the corresponding analysis UI code. 637 if (dump.ownedBy.length > 0) { 638 var message = 'shared by:' + 639 dump.ownedBy.map(function(ownershipLink) { 640 return '\n - ' + ownershipToUserFriendlyString( 641 ownershipLink.source, ownershipLink.importance); 642 }).join(); 643 attribute.infos.push(new tr.model.AttributeInfo( 644 tr.model.AttributeInfoType.MEMORY_OWNED, message)); 645 } 646 if (dump.owns !== undefined) { 647 var target = dump.owns.target; 648 var message = 'shares ' + 649 ownershipToUserFriendlyString(target, dump.owns.importance) + 650 ' with'; 651 652 var otherOwnershipLinks = target.ownedBy.filter( 653 function(ownershipLink) { 654 return ownershipLink.source !== dump; 655 }); 656 if (otherOwnershipLinks.length > 0) { 657 message += ':'; 658 message += otherOwnershipLinks.map(function(ownershipLink) { 659 return '\n - ' + ownershipToUserFriendlyString( 660 ownershipLink.source, ownershipLink.importance); 661 }).join(); 662 } else { 663 message += ' no other dumps'; 664 } 665 666 attribute.infos.push(new tr.model.AttributeInfo( 667 tr.model.AttributeInfoType.MEMORY_OWNER, message)); 668 } 669 }, 670 671 aggregateAttributes: function() { 672 this.iterateRootAllocatorDumps(function(dump) { 673 dump.aggregateAttributes(this.model); 674 }); 675 }, 676 677 discountTracingOverhead: function() { 678 // TODO(petrcermak): Consider factoring out all the finalization code and 679 // constants to a single file. 680 tr.b.iterItems(this.processMemoryDumps, function(pid, dump) { 681 dump.discountTracingOverhead(this.model); 682 }, this); 683 }, 684 685 iterateContainerDumps: function(fn) { 686 fn.call(this, this); 687 tr.b.iterItems(this.processMemoryDumps, function(pid, processDump) { 688 fn.call(this, processDump); 689 }, this); 690 }, 691 692 iterateRootAllocatorDumps: function(fn) { 693 this.iterateContainerDumps(function(containerDump) { 694 var memoryAllocatorDumps = containerDump.memoryAllocatorDumps; 695 if (memoryAllocatorDumps === undefined) 696 return; 697 memoryAllocatorDumps.forEach(fn, this); 698 }); 699 }, 700 701 /** 702 * Traverse the memory dump graph in a depth first post-order, i.e. 703 * children and owners of a memory allocator dump are visited before the 704 * dump itself. This method will throw an exception if the graph contains 705 * a cycle. 706 */ 707 traverseAllocatorDumpsInDepthFirstPostOrder: function(fn) { 708 var visitedDumps = new WeakSet(); 709 var openDumps = new WeakSet(); 710 711 function visit(dump) { 712 if (visitedDumps.has(dump)) 713 return; 714 715 if (openDumps.has(dump)) 716 throw new Error(dump.userFriendlyName + ' contains a cycle'); 717 openDumps.add(dump); 718 719 // Visit owners before the dumps they own. 720 dump.ownedBy.forEach(function(ownershipLink) { 721 visit.call(this, ownershipLink.source); 722 }, this); 723 724 // Visit children before parents. 725 dump.children.forEach(visit, this); 726 727 // Actually visit the current memory allocator dump. 728 fn.call(this, dump); 729 visitedDumps.add(dump); 730 731 openDumps.delete(dump); 732 } 733 734 this.iterateRootAllocatorDumps(visit); 735 }, 736 737 /** 738 * Traverse the memory dump graph in a depth first pre-order, i.e. 739 * children and owners of a memory allocator dump are visited after the 740 * dump itself. This method will not visit some dumps if the graph contains 741 * a cycle. 742 */ 743 traverseAllocatorDumpsInDepthFirstPreOrder: function(fn) { 744 var visitedDumps = new WeakSet(); 745 746 function visit(dump) { 747 if (visitedDumps.has(dump)) 748 return; 749 750 // If this dumps owns another dump which hasn't been visited yet, then 751 // wait for this dump to be visited later. 752 if (dump.owns !== undefined && !visitedDumps.has(dump.owns.target)) 753 return; 754 755 // If this dump's parent hasn't been visited yet, then wait for this 756 // dump to be visited later. 757 if (dump.parent !== undefined && !visitedDumps.has(dump.parent)) 758 return; 759 760 // Actually visit the current memory allocator dump. 761 fn.call(this, dump); 762 visitedDumps.add(dump); 763 764 // Visit owners after the dumps they own. 765 dump.ownedBy.forEach(function(ownershipLink) { 766 visit.call(this, ownershipLink.source); 767 }, this); 768 769 // Visit children after parents. 770 dump.children.forEach(visit, this); 771 } 772 773 this.iterateRootAllocatorDumps(visit); 774 } 775 }; 776 777 tr.model.EventRegistry.register( 778 GlobalMemoryDump, 779 { 780 name: 'globalMemoryDump', 781 pluralName: 'globalMemoryDumps', 782 singleViewElementName: 'tr-ui-a-single-global-memory-dump-sub-view', 783 multiViewElementName: 'tr-ui-a-multi-global-memory-dump-sub-view' 784 }); 785 786 return { 787 GlobalMemoryDump: GlobalMemoryDump 788 }; 789}); 790</script> 791