docs: function(dump) {
        // This flag becomes true if the size attribute of the current dump
        // should be defined, i.e. if (1) the current dump's size attribute is
        // defined, (2) the size of at least one of its children is defined or
        // (3) the size of at least one of its owners is defined.
        var shouldDefineSize = false;
  
        // This helper function returns the numeric value of the size attribute
        // of the given dependent memory allocator dump. If the attribute is
        // defined, the shouldDefineSize flag above is also set to true (because
        // condition (2) or (3) is satisfied). Otherwise, zero is returned (and
        // the flag is left unchanged).
        function getDependencySize(dependencyDump) {
          var attr = dependencyDump.attributes[SIZE_ATTRIBUTE_NAME];
          if (attr === undefined)
            return 0;
          shouldDefineSize = true;
          return attr.value;
        }
  
        // 1. Get the size provided by the dump. If present, define a function
        // for checking dependent size consistency (a dump must always be bigger
        // than all its children aggregated together and/or its largest owner).
        var sizeAttribute = dump.getValidSizeAttributeOrUndefined(
            SIZE_ATTRIBUTE_NAME, this.model);
        var size = 0;
        var infos = [];
        var checkDependentSizeIsConsistent = function() { /* no-op */ };
        if (sizeAttribute !== undefined) {
          size = sizeAttribute.value;
          shouldDefineSize = true;
          checkDependentSizeIsConsistent = function(dependentSize,
              dependentName) {
            if (size >= dependentSize)
              return;
            var messageSuffix = ' (' + tr.b.u.Units.sizeInBytes.format(size) +
                ') is less than ' + dependentName + ' (' +
                  tr.b.u.Units.sizeInBytes.format(dependentSize) + ').';
            this.model.importWarning({
              type: 'memory_dump_parse_error',
              message: 'Size provided by memory allocator dump \'' +
                  dump.fullName + '\'' + messageSuffix
            });
            infos.push(new tr.model.AttributeInfo(
                tr.model.AttributeInfoType.WARNING,
                'Size provided by this memory allocator dump' + messageSuffix));
          }.bind(this);
        }
  
        // 2. Aggregate size of children. The recursive function traverses all
        // descendants and ensures that double-counting due to ownership within a
        // subsystem is avoided.
        var aggregatedChildrenSize = 0;
        // Owned child dump name -> (Owner child dump name -> overlapping size).
        var allOverlaps = {};
        dump.children.forEach(function(childDump) {
          function aggregateDescendantDump(descendantDump) {
            // Don't count this descendant dump if it owns another descendant of
            // the current dump (would cause double-counting).
            var ownedDumpLink = descendantDump.owns;
            if (ownedDumpLink !== undefined &&
                ownedDumpLink.target.isDescendantOf(dump)) {
              // If the target owned dump is a descendant of a *different* child
              // of the the current dump (i.e. not childDump), then we remember
              // the ownership so that we could explain why the size of the
              // current dump is not equal to the sum of its children.
              var ownedDescendantDump = ownedDumpLink.target;
              var ownedChildDump = ownedDescendantDump;
              while (ownedChildDump.parent !== dump)
                ownedChildDump = ownedChildDump.parent;
              if (childDump !== ownedChildDump) {
                var overlap = getDependencySize(descendantDump);
                if (overlap > 0) {
                  // Owner child dump -> total overlapping size.
                  var ownedChildOverlaps = allOverlaps[ownedChildDump.name];
                  if (ownedChildOverlaps === undefined)
                    allOverlaps[ownedChildDump.name] = ownedChildOverlaps = {};
                  var previousTotalOverlap =
                      ownedChildOverlaps[childDump.name] || 0;
                  var updatedTotalOverlap = previousTotalOverlap + overlap;
                  ownedChildOverlaps[childDump.name] = updatedTotalOverlap;
                }
              }
              return;
            }
  
            // If this descendant dump is a leaf node, add its size to the
            // aggregated size.
            if (descendantDump.children.length === 0) {
              aggregatedChildrenSize += getDependencySize(descendantDump);
              return;
            }
  
            // If this descendant dump is an intermediate node, recurse down into
            // its children. Note that the dump's size is NOT added because it is
            // an aggregate of its children (would cause double-counting).
            descendantDump.children.forEach(aggregateDescendantDump);
          }
          aggregateDescendantDump(childDump);
        });
        // If the size of the dump is not equal to the sum of its children, add
        // infos to its children explaining the difference.
        dump.children.forEach(function(childDump) {
          var childOverlaps = allOverlaps[childDump.name];
          if (childOverlaps === undefined)
            return;
  
          var message = tr.b.dictionaryValues(tr.b.mapItems(childOverlaps,
              function(ownerChildName, overlap) {
            return 'overlaps with its sibling \'' + ownerChildName + '\' (' +
                tr.b.u.Units.sizeInBytes.format(overlap) + ')';
          })).join(' ');
  
          childDump.attributes[SIZE_ATTRIBUTE_NAME].infos.push(
              new tr.model.AttributeInfo(
                  tr.model.AttributeInfoType.INFORMATION, message));
        });
        checkDependentSizeIsConsistent(
            aggregatedChildrenSize, 'the aggregated size of its children');
  
        // 3. Calculate the largest owner size.
        var largestOwnerSize = 0;
        dump.ownedBy.forEach(function(ownershipLink) {
          var owner = ownershipLink.source;
          var ownerSize = getDependencySize(owner);
          largestOwnerSize = Math.max(largestOwnerSize, ownerSize);
        });
        checkDependentSizeIsConsistent(
            largestOwnerSize, 'the size of its largest owner');
  
        // If neither the dump nor any of its dependencies (children and owners)
        // provide a size, do NOT add a zero size attribute.
        if (!shouldDefineSize) {
          // The rest of the pipeline relies on size being either a valid
          // ScalarAttribute, or undefined.
          dump.attributes[SIZE_ATTRIBUTE_NAME] = undefined;
          return;
        }
  
        // A dump must always be bigger than all its children aggregated
        // together and/or its largest owner.
        size = Math.max(size, aggregatedChildrenSize, largestOwnerSize);
  
        var sizeAttribute = new tr.model.ScalarAttribute('bytes', size);
        sizeAttribute.infos = infos;
        dump.attributes[SIZE_ATTRIBUTE_NAME] = sizeAttribute;
  
        // Add a virtual child to make up for extra size of the dump with
        // respect to its children (if applicable).
        if (aggregatedChildrenSize < size &&
            dump.children !== undefined && dump.children.length > 0) {
          var virtualChild = new tr.model.MemoryAllocatorDump(
              dump.containerMemoryDump, dump.fullName + '/<unspecified>');
          virtualChild.parent = dump;
          dump.children.unshift(virtualChild);
          virtualChild.attributes[SIZE_ATTRIBUTE_NAME] =
              new tr.model.ScalarAttribute(
                  'bytes', size - aggregatedChildrenSize);
        }
      }