@@ -157,9 +157,15 @@ struct TreeBuilder::Node {
157
157
158
158
std::optional<bool > isset{std::nullopt };
159
159
160
+ /* *
161
+ * An estimation of the "exclusive size" of a type, trying to
162
+ * attribute every byte once and only once to types in the tree.
163
+ */
164
+ size_t exclusiveSize{};
165
+
160
166
MSGPACK_DEFINE_ARRAY (id, name, typeName, typePath, isTypedef, staticSize,
161
167
dynamicSize, paddingSavingsSize, containerStats, pointer,
162
- children, isset)
168
+ children, isset, exclusiveSize )
163
169
};
164
170
165
171
TreeBuilder::~TreeBuilder () {
@@ -377,6 +383,19 @@ static std::string_view drgnKindStr(struct drgn_type *type) {
377
383
return kind;
378
384
}
379
385
386
+ void TreeBuilder::setSize (TreeBuilder::Node &node, uint64_t dynamicSize,
387
+ uint64_t memberSizes) {
388
+ node.dynamicSize = dynamicSize;
389
+ if (memberSizes > node.staticSize + node.dynamicSize ) {
390
+ // TODO handle this edge case in a more elegant way.
391
+ // This can occur when handling bitfields or vector<bool>
392
+ // (as we cannot accurately track sub-byte sizes currently)
393
+ node.exclusiveSize = 0 ;
394
+ } else {
395
+ node.exclusiveSize = node.staticSize + node.dynamicSize - memberSizes;
396
+ }
397
+ }
398
+
380
399
TreeBuilder::Node TreeBuilder::process (NodeID id, Variable variable) {
381
400
Node node{
382
401
.id = id,
@@ -391,6 +410,8 @@ TreeBuilder::Node TreeBuilder::process(NodeID id, Variable variable) {
391
410
<< " ', kind: " << drgnKindStr (variable.type ) << " )"
392
411
<< (variable.isStubbed ? " STUBBED" : " " )
393
412
<< (th->knownDummyTypeList .contains (variable.type ) ? " DUMMY" : " " );
413
+ // Default dynamic size to 0 and calculate fallback exclusive size
414
+ setSize (node, 0 , 0 );
394
415
if (!variable.isStubbed ) {
395
416
switch (drgn_type_kind (variable.type )) {
396
417
case DRGN_TYPE_POINTER:
@@ -414,7 +435,8 @@ TreeBuilder::Node TreeBuilder::process(NodeID id, Variable variable) {
414
435
auto childID = nextNodeID++;
415
436
auto child = process (childID, Variable{entry->second , " " , " " });
416
437
node.children = {childID, childID + 1 };
417
- node.dynamicSize = child.staticSize + child.dynamicSize ;
438
+ setSize (node, child.staticSize + child.dynamicSize ,
439
+ child.staticSize + child.dynamicSize );
418
440
}
419
441
}
420
442
break ;
@@ -435,7 +457,8 @@ TreeBuilder::Node TreeBuilder::process(NodeID id, Variable variable) {
435
457
auto childID = nextNodeID++;
436
458
auto child = process (childID, Variable{entry->second , " " , " " });
437
459
node.children = {childID, childID + 1 };
438
- node.dynamicSize = child.dynamicSize ;
460
+ setSize (node, child.dynamicSize ,
461
+ child.dynamicSize + child.staticSize );
439
462
}
440
463
} break ;
441
464
case DRGN_TYPE_CLASS:
@@ -475,6 +498,7 @@ TreeBuilder::Node TreeBuilder::process(NodeID id, Variable variable) {
475
498
bool captureThriftIsset =
476
499
th->thriftIssetStructTypes .contains (objectType);
477
500
501
+ uint64_t memberSizes = 0 ;
478
502
for (std::size_t i = 0 ; i < members.size (); i++) {
479
503
std::optional<bool > isset;
480
504
if (captureThriftIsset && i < members.size () - 1 ) {
@@ -493,7 +517,9 @@ TreeBuilder::Node TreeBuilder::process(NodeID id, Variable variable) {
493
517
Variable{member.type , member.member_name ,
494
518
member.member_name , isset, member.isStubbed });
495
519
node.dynamicSize += child.dynamicSize ;
520
+ memberSizes += child.dynamicSize + child.staticSize ;
496
521
}
522
+ setSize (node, node.dynamicSize , memberSizes);
497
523
}
498
524
break ;
499
525
default :
@@ -626,7 +652,7 @@ void TreeBuilder::processContainer(const Variable &variable, Node &node) {
626
652
.name = " " ,
627
653
.typePath = drgnTypeToName (containerType.type ) + " []" });
628
654
629
- node.dynamicSize = child.dynamicSize ;
655
+ setSize ( node, child .dynamicSize , child.dynamicSize + child. staticSize ) ;
630
656
node.containerStats = child.containerStats ;
631
657
return ;
632
658
}
@@ -666,7 +692,7 @@ void TreeBuilder::processContainer(const Variable &variable, Node &node) {
666
692
.name = " " ,
667
693
.typePath = drgnTypeToName (elementType.type ) + " []" });
668
694
669
- node.dynamicSize = child.dynamicSize ;
695
+ setSize ( node, child .dynamicSize , child.dynamicSize + child. staticSize ) ;
670
696
}
671
697
return ;
672
698
}
@@ -742,7 +768,7 @@ void TreeBuilder::processContainer(const Variable &variable, Node &node) {
742
768
break ;
743
769
case CAFFE2_BLOB_TYPE:
744
770
// This is a weird one, need to ask why we just overwite size like this
745
- node. dynamicSize = next ();
771
+ setSize ( node, next (), 0 );
746
772
return ;
747
773
case ARRAY_TYPE:
748
774
contentsStoredInline = true ;
@@ -771,7 +797,7 @@ void TreeBuilder::processContainer(const Variable &variable, Node &node) {
771
797
containerStats.elementStaticSize += next ();
772
798
size_t bucketCount = next ();
773
799
// Both libc++ and libstdc++ define buckets as an array of raw pointers
774
- node.dynamicSize += bucketCount * sizeof (void *);
800
+ setSize ( node, node .dynamicSize + bucketCount * sizeof (void *), 0 );
775
801
containerStats.length = containerStats.capacity = next ();
776
802
} break ;
777
803
case F14_MAP:
@@ -782,7 +808,7 @@ void TreeBuilder::processContainer(const Variable &variable, Node &node) {
782
808
// conveniently provide a `getAllocatedMemorySize()` method which we can
783
809
// use instead.
784
810
contentsStoredInline = true ;
785
- node.dynamicSize += next ();
811
+ setSize ( node, node .dynamicSize + next (), 0 );
786
812
containerStats.capacity = next ();
787
813
containerStats.length = next ();
788
814
break ;
@@ -802,8 +828,10 @@ void TreeBuilder::processContainer(const Variable &variable, Node &node) {
802
828
}
803
829
804
830
if (!contentsStoredInline) {
805
- node.dynamicSize +=
806
- containerStats.elementStaticSize * containerStats.capacity ;
831
+ setSize (node,
832
+ node.dynamicSize +
833
+ containerStats.elementStaticSize * containerStats.capacity ,
834
+ 0 );
807
835
}
808
836
809
837
// A cutoff value used to sanity-check our results. If a container
@@ -835,15 +863,18 @@ void TreeBuilder::processContainer(const Variable &variable, Node &node) {
835
863
<< node.children ->first << " , " << node.children ->second << " )" ;
836
864
nextNodeID += numChildren;
837
865
auto childID = node.children ->first ;
866
+ uint64_t memberSizes = 0 ;
838
867
for (size_t i = 0 ; i < containerStats.length ; i++) {
839
868
for (auto &type : elementTypes) {
840
869
auto child =
841
870
process (childID++, {.type = type.type ,
842
871
.name = " " ,
843
872
.typePath = drgnTypeToName (type.type ) + " []" });
844
873
node.dynamicSize += child.dynamicSize ;
874
+ memberSizes += child.dynamicSize + child.staticSize ;
845
875
}
846
876
}
877
+ setSize (node, node.dynamicSize , memberSizes);
847
878
}
848
879
849
880
template <class T >
@@ -874,7 +905,8 @@ void TreeBuilder::JSON(NodeID id, std::ofstream &output) {
874
905
output << " \" typeName\" :\" " << node.typeName << " \" ," ;
875
906
output << " \" isTypedef\" :" << (node.isTypedef ? " true" : " false" ) << " ," ;
876
907
output << " \" staticSize\" :" << node.staticSize << " ," ;
877
- output << " \" dynamicSize\" :" << node.dynamicSize ;
908
+ output << " \" dynamicSize\" :" << node.dynamicSize << " ," ;
909
+ output << " \" exclusiveSize\" :" << node.exclusiveSize ;
878
910
if (node.paddingSavingsSize .has_value ()) {
879
911
output << " ," ;
880
912
output << " \" paddingSavingsSize\" :" << *node.paddingSavingsSize ;
0 commit comments