Skip to content

Commit 542f1a6

Browse files
committed
Add experimental support for reporting exclusive sizes into rocksdb
1 parent d9b13ff commit 542f1a6

File tree

2 files changed

+46
-11
lines changed

2 files changed

+46
-11
lines changed

src/TreeBuilder.cpp

Lines changed: 43 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -157,9 +157,15 @@ struct TreeBuilder::Node {
157157

158158
std::optional<bool> isset{std::nullopt};
159159

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+
160166
MSGPACK_DEFINE_ARRAY(id, name, typeName, typePath, isTypedef, staticSize,
161167
dynamicSize, paddingSavingsSize, containerStats, pointer,
162-
children, isset)
168+
children, isset, exclusiveSize)
163169
};
164170

165171
TreeBuilder::~TreeBuilder() {
@@ -377,6 +383,19 @@ static std::string_view drgnKindStr(struct drgn_type *type) {
377383
return kind;
378384
}
379385

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+
380399
TreeBuilder::Node TreeBuilder::process(NodeID id, Variable variable) {
381400
Node node{
382401
.id = id,
@@ -391,6 +410,8 @@ TreeBuilder::Node TreeBuilder::process(NodeID id, Variable variable) {
391410
<< "', kind: " << drgnKindStr(variable.type) << ")"
392411
<< (variable.isStubbed ? " STUBBED" : "")
393412
<< (th->knownDummyTypeList.contains(variable.type) ? " DUMMY" : "");
413+
// Default dynamic size to 0 and calculate fallback exclusive size
414+
setSize(node, 0, 0);
394415
if (!variable.isStubbed) {
395416
switch (drgn_type_kind(variable.type)) {
396417
case DRGN_TYPE_POINTER:
@@ -414,7 +435,8 @@ TreeBuilder::Node TreeBuilder::process(NodeID id, Variable variable) {
414435
auto childID = nextNodeID++;
415436
auto child = process(childID, Variable{entry->second, "", ""});
416437
node.children = {childID, childID + 1};
417-
node.dynamicSize = child.staticSize + child.dynamicSize;
438+
setSize(node, child.staticSize + child.dynamicSize,
439+
child.staticSize + child.dynamicSize);
418440
}
419441
}
420442
break;
@@ -435,7 +457,8 @@ TreeBuilder::Node TreeBuilder::process(NodeID id, Variable variable) {
435457
auto childID = nextNodeID++;
436458
auto child = process(childID, Variable{entry->second, "", ""});
437459
node.children = {childID, childID + 1};
438-
node.dynamicSize = child.dynamicSize;
460+
setSize(node, child.dynamicSize,
461+
child.dynamicSize + child.staticSize);
439462
}
440463
} break;
441464
case DRGN_TYPE_CLASS:
@@ -475,6 +498,7 @@ TreeBuilder::Node TreeBuilder::process(NodeID id, Variable variable) {
475498
bool captureThriftIsset =
476499
th->thriftIssetStructTypes.contains(objectType);
477500

501+
uint64_t memberSizes = 0;
478502
for (std::size_t i = 0; i < members.size(); i++) {
479503
std::optional<bool> isset;
480504
if (captureThriftIsset && i < members.size() - 1) {
@@ -493,7 +517,9 @@ TreeBuilder::Node TreeBuilder::process(NodeID id, Variable variable) {
493517
Variable{member.type, member.member_name,
494518
member.member_name, isset, member.isStubbed});
495519
node.dynamicSize += child.dynamicSize;
520+
memberSizes += child.dynamicSize + child.staticSize;
496521
}
522+
setSize(node, node.dynamicSize, memberSizes);
497523
}
498524
break;
499525
default:
@@ -626,7 +652,7 @@ void TreeBuilder::processContainer(const Variable &variable, Node &node) {
626652
.name = "",
627653
.typePath = drgnTypeToName(containerType.type) + "[]"});
628654

629-
node.dynamicSize = child.dynamicSize;
655+
setSize(node, child.dynamicSize, child.dynamicSize + child.staticSize);
630656
node.containerStats = child.containerStats;
631657
return;
632658
}
@@ -666,7 +692,7 @@ void TreeBuilder::processContainer(const Variable &variable, Node &node) {
666692
.name = "",
667693
.typePath = drgnTypeToName(elementType.type) + "[]"});
668694

669-
node.dynamicSize = child.dynamicSize;
695+
setSize(node, child.dynamicSize, child.dynamicSize + child.staticSize);
670696
}
671697
return;
672698
}
@@ -742,7 +768,7 @@ void TreeBuilder::processContainer(const Variable &variable, Node &node) {
742768
break;
743769
case CAFFE2_BLOB_TYPE:
744770
// This is a weird one, need to ask why we just overwite size like this
745-
node.dynamicSize = next();
771+
setSize(node, next(), 0);
746772
return;
747773
case ARRAY_TYPE:
748774
contentsStoredInline = true;
@@ -771,7 +797,7 @@ void TreeBuilder::processContainer(const Variable &variable, Node &node) {
771797
containerStats.elementStaticSize += next();
772798
size_t bucketCount = next();
773799
// 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);
775801
containerStats.length = containerStats.capacity = next();
776802
} break;
777803
case F14_MAP:
@@ -782,7 +808,7 @@ void TreeBuilder::processContainer(const Variable &variable, Node &node) {
782808
// conveniently provide a `getAllocatedMemorySize()` method which we can
783809
// use instead.
784810
contentsStoredInline = true;
785-
node.dynamicSize += next();
811+
setSize(node, node.dynamicSize + next(), 0);
786812
containerStats.capacity = next();
787813
containerStats.length = next();
788814
break;
@@ -802,8 +828,10 @@ void TreeBuilder::processContainer(const Variable &variable, Node &node) {
802828
}
803829

804830
if (!contentsStoredInline) {
805-
node.dynamicSize +=
806-
containerStats.elementStaticSize * containerStats.capacity;
831+
setSize(node,
832+
node.dynamicSize +
833+
containerStats.elementStaticSize * containerStats.capacity,
834+
0);
807835
}
808836

809837
// A cutoff value used to sanity-check our results. If a container
@@ -835,15 +863,18 @@ void TreeBuilder::processContainer(const Variable &variable, Node &node) {
835863
<< node.children->first << ", " << node.children->second << ")";
836864
nextNodeID += numChildren;
837865
auto childID = node.children->first;
866+
uint64_t memberSizes = 0;
838867
for (size_t i = 0; i < containerStats.length; i++) {
839868
for (auto &type : elementTypes) {
840869
auto child =
841870
process(childID++, {.type = type.type,
842871
.name = "",
843872
.typePath = drgnTypeToName(type.type) + "[]"});
844873
node.dynamicSize += child.dynamicSize;
874+
memberSizes += child.dynamicSize + child.staticSize;
845875
}
846876
}
877+
setSize(node, node.dynamicSize, memberSizes);
847878
}
848879

849880
template <class T>
@@ -874,7 +905,8 @@ void TreeBuilder::JSON(NodeID id, std::ofstream &output) {
874905
output << "\"typeName\":\"" << node.typeName << "\",";
875906
output << "\"isTypedef\":" << (node.isTypedef ? "true" : "false") << ",";
876907
output << "\"staticSize\":" << node.staticSize << ",";
877-
output << "\"dynamicSize\":" << node.dynamicSize;
908+
output << "\"dynamicSize\":" << node.dynamicSize << ",";
909+
output << "\"exclusiveSize\":" << node.exclusiveSize;
878910
if (node.paddingSavingsSize.has_value()) {
879911
output << ",";
880912
output << "\"paddingSavingsSize\":" << *node.paddingSavingsSize;

src/TreeBuilder.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,4 +111,7 @@ class TreeBuilder {
111111
template <class T>
112112
std::string_view serialize(const T &);
113113
void JSON(NodeID id, std::ofstream &output);
114+
115+
static void setSize(TreeBuilder::Node &node, uint64_t dynamicSize,
116+
uint64_t memberSizes);
114117
};

0 commit comments

Comments
 (0)