Skip to content

Commit 8bad704

Browse files
committed
Implement Container V2 for fbstring
1 parent 7a7a9b3 commit 8bad704

File tree

4 files changed

+141
-7
lines changed

4 files changed

+141
-7
lines changed

.circleci/config.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ workflows:
6161
name: test-clang
6262
requires:
6363
- build-clang
64+
# Tests disabled due to bad DWARF generated by the old clang compiler in CI
65+
exclude_regex: "OilIntegration.fbstring_.*"
6466
- test:
6567
name: test-type-graph-clang
6668
requires:

oi/OITraceCode.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,9 @@ class {
8181
index = (index + 1) % data.size();
8282
}
8383
}
84+
bool add(const auto* p) {
85+
return add((uintptr_t)p);
86+
}
8487
} static pointers;
8588

8689
} // namespace
@@ -145,3 +148,8 @@ struct validate_offset {
145148
};
146149

147150
enum class StubbedPointer : uintptr_t {};
151+
152+
bool isStorageInline(const auto& c) {
153+
return (uintptr_t)std::data(c) < (uintptr_t)(&c + sizeof(c)) &&
154+
(uintptr_t)std::data(c) >= (uintptr_t)&c;
155+
}

test/integration/fbstring.toml

Lines changed: 57 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1-
includes = ["folly/FBString.h"]
1+
includes = ["folly/FBString.h", "utility"]
22
[cases]
33
[cases.empty]
4-
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/322
54
param_types = ["folly::fbstring&"]
65
setup = "return {};"
76
expect_json = '''
@@ -18,16 +17,22 @@ includes = ["folly/FBString.h"]
1817
"dynamicSize": 0,
1918
"exclusiveSize": 24,
2019
"length": 0,
21-
"capacity": 23,
20+
"capacity":23,
2221
"elementStaticSize": 1
2322
}
2423
]
2524
}
2625
]
2726
'''
27+
expect_json_v2 = '''[{
28+
"typeNames": ["folly::basic_fbstring<int8_t, std::char_traits<int8_t>, std::allocator<int8_t>, folly::fbstring_core<int8_t>>"],
29+
"staticSize": 24,
30+
"exclusiveSize": 24,
31+
"length": 0,
32+
"capacity": 23
33+
}]'''
2834

2935
[cases.inline]
30-
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/322
3136
param_types = ["folly::fbstring&"]
3237
setup = 'return {"012345"};'
3338
expect_json = '''
@@ -51,9 +56,15 @@ includes = ["folly/FBString.h"]
5156
}
5257
]
5358
'''
59+
expect_json_v2 = '''[{
60+
"typeNames": ["folly::basic_fbstring<int8_t, std::char_traits<int8_t>, std::allocator<int8_t>, folly::fbstring_core<int8_t>>"],
61+
"staticSize": 24,
62+
"exclusiveSize": 24,
63+
"length": 6,
64+
"capacity": 23
65+
}]'''
5466

5567
[cases.heap_allocated]
56-
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/322
5768
param_types = ["folly::fbstring&"]
5869
setup = 'return {"abcdefghijklmnopqrstuvwxzy"};'
5970
expect_json = '''
@@ -77,9 +88,15 @@ includes = ["folly/FBString.h"]
7788
}
7889
]
7990
'''
91+
expect_json_v2 = '''[{
92+
"typeNames": ["folly::basic_fbstring<int8_t, std::char_traits<int8_t>, std::allocator<int8_t>, folly::fbstring_core<int8_t>>"],
93+
"staticSize": 24,
94+
"exclusiveSize": 50,
95+
"length": 26,
96+
"capacity": 26
97+
}]'''
8098

81-
[cases.string_pooled]
82-
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/322
99+
[cases.string_pooled_unique]
83100
param_types = ["folly::fbstring&"]
84101
setup = "return folly::fbstring(1024, 'c');"
85102
expect_json = '''
@@ -103,3 +120,36 @@ includes = ["folly/FBString.h"]
103120
}
104121
]
105122
'''
123+
expect_json_v2 = '''[{
124+
"typeNames": ["folly::basic_fbstring<int8_t, std::char_traits<int8_t>, std::allocator<int8_t>, folly::fbstring_core<int8_t>>"],
125+
"staticSize": 24,
126+
"exclusiveSize": 1056,
127+
"length": 1024,
128+
"capacity": 1024
129+
}]'''
130+
131+
[cases.string_pooled_shared]
132+
param_types = ["std::pair<folly::fbstring, folly::fbstring>&"]
133+
setup = """
134+
folly::fbstring s(1024, 'c');
135+
return {{s, s}};
136+
"""
137+
expect_json_v2 = '''[{
138+
"staticSize": 48,
139+
"exclusiveSize": 0,
140+
"members": [
141+
{
142+
"typeNames": ["folly::basic_fbstring<int8_t, std::char_traits<int8_t>, std::allocator<int8_t>, folly::fbstring_core<int8_t>>"],
143+
"staticSize": 24,
144+
"exclusiveSize": 1056,
145+
"length": 1024,
146+
"capacity": 1024
147+
}, {
148+
"typeNames": ["folly::basic_fbstring<int8_t, std::char_traits<int8_t>, std::allocator<int8_t>, folly::fbstring_core<int8_t>>"],
149+
"staticSize": 24,
150+
"exclusiveSize": 24,
151+
"length": 1024,
152+
"capacity": 1024
153+
}
154+
]
155+
}]'''

types/fb_string_type.toml

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,3 +69,77 @@ struct TypeHandler<DB, %1%<T0, T1, T2, T3>> {
6969
}
7070
};
7171
"""
72+
73+
traversal_func = """
74+
// fbstring has inlining (SSO) and allocates large strings as
75+
// reference counted strings. Reference counted strings have an
76+
// overhead of a single std::atomic<size_t> at the beginning. To
77+
// correctly attribute the size in the processor we need the
78+
// following 4 categories as well as the usual metadata.
79+
80+
enum class Category : uint8_t {
81+
InlinedStorage,
82+
OwnedHeapStorage,
83+
ReferenceCountedStorage,
84+
AlreadyAttributed,
85+
};
86+
87+
constexpr static size_t minLargeSize = 255;
88+
size_t capacity = container.capacity();
89+
90+
Category category;
91+
if (isStorageInline(container)) {
92+
category = Category::InlinedStorage;
93+
} else if (capacity < minLargeSize) {
94+
category = Category::OwnedHeapStorage;
95+
} else if (pointers.add(container.data())) {
96+
category = Category::ReferenceCountedStorage;
97+
} else {
98+
category = Category::AlreadyAttributed;
99+
}
100+
101+
return returnArg.write((uintptr_t)container.data())
102+
.write(capacity)
103+
.write(container.size())
104+
.write(static_cast<std::underlying_type_t<Category>>(category));
105+
"""
106+
107+
[[codegen.processor]]
108+
type = "types::st::VarInt<DB>"
109+
func = """
110+
el.pointer = std::get<ParsedData::VarInt>(d.val).value;
111+
"""
112+
113+
[[codegen.processor]]
114+
type = "types::st::VarInt<DB>"
115+
func = """
116+
uint64_t capacity = std::get<ParsedData::VarInt>(d.val).value;
117+
el.container_stats.emplace(result::Element::ContainerStats { .capacity = capacity });
118+
"""
119+
120+
[[codegen.processor]]
121+
type = "types::st::VarInt<DB>"
122+
func = """
123+
el.container_stats->length = std::get<ParsedData::VarInt>(d.val).value;
124+
"""
125+
126+
[[codegen.processor]]
127+
type = "types::st::VarInt<DB>"
128+
func = """
129+
using CharType = T0;
130+
131+
enum class Category : uint8_t {
132+
InlinedStorage,
133+
OwnedHeapStorage,
134+
ReferenceCountedStorage,
135+
AlreadyAttributed,
136+
};
137+
138+
auto category = static_cast<Category>(std::get<ParsedData::VarInt>(d.val).value);
139+
if (category == Category::InlinedStorage || category == Category::AlreadyAttributed)
140+
return;
141+
142+
el.exclusive_size += el.container_stats->capacity * sizeof(CharType);
143+
if (category == Category::ReferenceCountedStorage)
144+
el.exclusive_size += 8; // 8 bytes for std::atomic<size_t>
145+
"""

0 commit comments

Comments
 (0)