diff --git a/.circleci/config.yml b/.circleci/config.yml index a6991e1..470c052 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -61,6 +61,8 @@ workflows: name: test-clang requires: - build-clang + # Tests disabled due to bad DWARF generated by the old clang compiler in CI + exclude_regex: "OilIntegration.fbstring_.*" - test: name: test-type-graph-clang requires: diff --git a/oi/OITraceCode.cpp b/oi/OITraceCode.cpp index e70fb9b..47e98d7 100644 --- a/oi/OITraceCode.cpp +++ b/oi/OITraceCode.cpp @@ -81,6 +81,9 @@ class { index = (index + 1) % data.size(); } } + bool add(const auto* p) { + return add((uintptr_t)p); + } } static pointers; } // namespace @@ -145,3 +148,8 @@ struct validate_offset { }; enum class StubbedPointer : uintptr_t {}; + +bool isStorageInline(const auto& c) { + return (uintptr_t)std::data(c) < (uintptr_t)(&c + sizeof(c)) && + (uintptr_t)std::data(c) >= (uintptr_t)&c; +} diff --git a/test/integration/fbstring.toml b/test/integration/fbstring.toml index 9a3a255..6e68d99 100644 --- a/test/integration/fbstring.toml +++ b/test/integration/fbstring.toml @@ -1,7 +1,6 @@ -includes = ["folly/FBString.h"] +includes = ["folly/FBString.h", "utility"] [cases] [cases.empty] - oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/322 param_types = ["folly::fbstring&"] setup = "return {};" expect_json = ''' @@ -18,16 +17,22 @@ includes = ["folly/FBString.h"] "dynamicSize": 0, "exclusiveSize": 24, "length": 0, - "capacity": 23, + "capacity":23, "elementStaticSize": 1 } ] } ] ''' + expect_json_v2 = '''[{ + "typeNames": ["folly::basic_fbstring, std::allocator, folly::fbstring_core>"], + "staticSize": 24, + "exclusiveSize": 24, + "length": 0, + "capacity": 23 + }]''' [cases.inline] - oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/322 param_types = ["folly::fbstring&"] setup = 'return {"012345"};' expect_json = ''' @@ -51,9 +56,15 @@ includes = ["folly/FBString.h"] } ] ''' + expect_json_v2 = '''[{ + "typeNames": ["folly::basic_fbstring, std::allocator, folly::fbstring_core>"], + "staticSize": 24, + "exclusiveSize": 24, + "length": 6, + "capacity": 23 + }]''' [cases.heap_allocated] - oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/322 param_types = ["folly::fbstring&"] setup = 'return {"abcdefghijklmnopqrstuvwxzy"};' expect_json = ''' @@ -77,9 +88,15 @@ includes = ["folly/FBString.h"] } ] ''' + expect_json_v2 = '''[{ + "typeNames": ["folly::basic_fbstring, std::allocator, folly::fbstring_core>"], + "staticSize": 24, + "exclusiveSize": 50, + "length": 26, + "capacity": 26 + }]''' - [cases.string_pooled] - oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/322 + [cases.string_pooled_unique] param_types = ["folly::fbstring&"] setup = "return folly::fbstring(1024, 'c');" expect_json = ''' @@ -103,3 +120,36 @@ includes = ["folly/FBString.h"] } ] ''' + expect_json_v2 = '''[{ + "typeNames": ["folly::basic_fbstring, std::allocator, folly::fbstring_core>"], + "staticSize": 24, + "exclusiveSize": 1056, + "length": 1024, + "capacity": 1024 + }]''' + + [cases.string_pooled_shared] + param_types = ["std::pair&"] + setup = """ + folly::fbstring s(1024, 'c'); + return {{s, s}}; + """ + expect_json_v2 = '''[{ + "staticSize": 48, + "exclusiveSize": 0, + "members": [ + { + "typeNames": ["folly::basic_fbstring, std::allocator, folly::fbstring_core>"], + "staticSize": 24, + "exclusiveSize": 1056, + "length": 1024, + "capacity": 1024 + }, { + "typeNames": ["folly::basic_fbstring, std::allocator, folly::fbstring_core>"], + "staticSize": 24, + "exclusiveSize": 24, + "length": 1024, + "capacity": 1024 + } + ] + }]''' diff --git a/types/fb_string_type.toml b/types/fb_string_type.toml index fba5d81..19b9f34 100644 --- a/types/fb_string_type.toml +++ b/types/fb_string_type.toml @@ -69,3 +69,77 @@ struct TypeHandler> { } }; """ + +traversal_func = """ +// fbstring has inlining (SSO) and allocates large strings as +// reference counted strings. Reference counted strings have an +// overhead of a single std::atomic at the beginning. To +// correctly attribute the size in the processor we need the +// following 4 categories as well as the usual metadata. + +enum class Category : uint8_t { + InlinedStorage, + OwnedHeapStorage, + ReferenceCountedStorage, + AlreadyAttributed, +}; + +constexpr static size_t minLargeSize = 255; +size_t capacity = container.capacity(); + +Category category; +if (isStorageInline(container)) { + category = Category::InlinedStorage; +} else if (capacity < minLargeSize) { + category = Category::OwnedHeapStorage; +} else if (pointers.add(container.data())) { + category = Category::ReferenceCountedStorage; +} else { + category = Category::AlreadyAttributed; +} + +return returnArg.write((uintptr_t)container.data()) + .write(capacity) + .write(container.size()) + .write(static_cast>(category)); +""" + +[[codegen.processor]] +type = "types::st::VarInt" +func = """ +el.pointer = std::get(d.val).value; +""" + +[[codegen.processor]] +type = "types::st::VarInt" +func = """ +uint64_t capacity = std::get(d.val).value; +el.container_stats.emplace(result::Element::ContainerStats { .capacity = capacity }); +""" + +[[codegen.processor]] +type = "types::st::VarInt" +func = """ +el.container_stats->length = std::get(d.val).value; +""" + +[[codegen.processor]] +type = "types::st::VarInt" +func = """ +using CharType = T0; + +enum class Category : uint8_t { + InlinedStorage, + OwnedHeapStorage, + ReferenceCountedStorage, + AlreadyAttributed, +}; + +auto category = static_cast(std::get(d.val).value); +if (category == Category::InlinedStorage || category == Category::AlreadyAttributed) + return; + +el.exclusive_size += el.container_stats->capacity * sizeof(CharType); +if (category == Category::ReferenceCountedStorage) + el.exclusive_size += 8; // 8 bytes for std::atomic +"""