├── CHANGELOG.md ├── website ├── static │ ├── .nojekyll │ ├── CNAME │ ├── .DS_Store │ ├── img │ │ ├── favicon.ico │ │ ├── ._OISymbol.svg │ │ ├── docusaurus.png │ │ ├── mstile-70x70.png │ │ ├── ._OIBrandmark.svg │ │ ├── favicon-16x16.png │ │ ├── favicon-32x32.png │ │ ├── mstile-144x144.png │ │ ├── mstile-150x150.png │ │ ├── mstile-310x150.png │ │ ├── mstile-310x310.png │ │ ├── apple-touch-icon.png │ │ ├── android-chrome-192x192.png │ │ ├── android-chrome-512x512.png │ │ ├── ._ObjectIntrospection-SecondarySymbol.svg │ │ ├── browserconfig.xml │ │ ├── ObjectIntrospection-SecondarySymbol.svg │ │ └── OISymbol.svg │ ├── ._.DS_Store │ └── site.webmanifest ├── .gitignore ├── src │ ├── pages │ │ ├── markdown-page.md │ │ └── styles.module.css │ └── css │ │ └── custom.css ├── babel.config.js ├── docs │ ├── contributing.md │ ├── constraints.md │ └── addrbook-intro.md ├── sidebars.js ├── package.json └── README.md ├── test ├── integration_entry_inc_arg0.oid ├── integration_return_incN_arg0.oid ├── integration_entry_doStuff_arg0.oid ├── integration_entry_doStuff_this.oid ├── mocks.h ├── integration │ ├── std_unordered_map.toml │ ├── packed.toml │ ├── thrift_namespaces.toml │ ├── std_tuple.toml │ ├── folly_shims.cpp │ ├── inheritance_multiple.toml │ ├── references.toml │ ├── std_multimap_custom_comparator.toml │ ├── sorted_vector_set.toml │ ├── namespaces.toml │ ├── std_reference_wrapper.toml │ ├── typedefed_parent.toml │ ├── multi_arg.toml │ ├── inheritance_access.toml │ ├── std_list_del_allocator.toml │ ├── std_conditional.toml │ ├── enums.toml │ ├── enums_params.toml │ ├── std_string.toml │ ├── runner_common.h │ ├── std_deque.toml │ ├── std_unordered_set_custom_operator.toml │ ├── std_unordered_multiset_custom_operator.toml │ ├── std_set_custom_comparator.toml │ └── std_unordered_map_custom_operator.toml ├── mttest.h ├── main.cpp ├── test_enforce_compatibility.cpp ├── test_container_info.cpp ├── type_graph_utils.h ├── test_remove_top_level_pointer.cpp ├── TypeGraphParser.h ├── test_drgn_parser.h └── test_prune.cpp ├── .git-blame-ignore-revs ├── .gitmodules ├── examples ├── compile-time-oil │ ├── .gitignore │ ├── Makefile │ └── OilVectorOfStrings.cpp └── web │ └── AddrBook │ └── Makefile ├── oss.oid.toml ├── .editorconfig ├── .github ├── pull_request_template.md └── workflows │ ├── test-report.yml │ ├── test-deploy.yml │ ├── deploy.yml │ └── tests_failing_under_nix.txt ├── .gitignore ├── .clang-format ├── types ├── thrift_isset_type.toml ├── weak_ptr_type.toml ├── folly_iobuf_queue_type.toml ├── try_type.toml ├── repeated_field_type.toml ├── repeated_ptr_field_type.toml ├── boost_bimap_type.toml ├── stack_container_adapter_type.toml ├── queue_container_adapter_type.toml ├── priority_queue_container_adapter_type.toml ├── folly_small_heap_vector_map.toml ├── pair_type.toml ├── optional_type.toml ├── array_type.toml ├── folly_optional_type.toml ├── string_type.toml ├── ref_wrapper_type.toml ├── list_type.toml ├── cxx11_list_type.toml ├── seq_type.toml ├── uniq_ptr_type.toml ├── folly_iobuf_type.toml ├── sorted_vec_set_type.toml ├── f14_fast_set.toml └── f14_node_set.toml ├── oi ├── type_graph │ ├── CMakeLists.txt │ ├── RemoveTopLevelPointer.h │ ├── EnforceCompatibility.h │ ├── Prune.h │ ├── AlignmentCalc.h │ ├── Flattener.h │ ├── RemoveTopLevelPointer.cpp │ ├── PassManager.h │ ├── Prune.cpp │ ├── IdentifyContainers.h │ ├── RemoveMembers.h │ ├── TypeIdentifier.h │ ├── AddPadding.h │ ├── NameGen.h │ ├── TopoSorter.h │ ├── KeyCapture.h │ ├── Printer.h │ ├── PassManager.cpp │ └── DrgnExporter.h ├── support │ ├── Toml.h │ └── Toml.cpp ├── TimeUtils.h ├── Portability.h ├── arch │ ├── Arch.h │ └── x86_64.cpp ├── Config.h ├── OILibrary.cpp ├── X86InstDefs.h ├── CMakeLists.txt ├── Headers.h ├── OILexer.h ├── TypeHierarchy.h ├── OIGenerator.h ├── EnumBitset.h ├── Features.h ├── exporters │ └── TypeCheckingWalker.h └── OICache.h ├── cmake ├── FindThrift.cmake ├── PreventInSourceBuilds.cmake ├── Finduring.cmake ├── Findzstd.cmake └── StandardProjectSettings.cmake ├── CONTRIBUTING.md ├── resources └── CMakeLists.txt ├── include └── oi │ ├── result │ ├── Element.h │ └── SizedResult.h │ ├── oi.h │ └── exporters │ └── ParsedData.h ├── .clang-tidy ├── tools └── combine_configs.py ├── dev.oid.toml └── flake.lock /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /website/static/.nojekyll: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /website/.gitignore: -------------------------------------------------------------------------------- 1 | .docusaurus 2 | -------------------------------------------------------------------------------- /website/static/CNAME: -------------------------------------------------------------------------------- 1 | objectintrospection.org 2 | -------------------------------------------------------------------------------- /test/integration_entry_inc_arg0.oid: -------------------------------------------------------------------------------- 1 | entry:_ZN3Foo3incEv:arg0 2 | -------------------------------------------------------------------------------- /test/integration_return_incN_arg0.oid: -------------------------------------------------------------------------------- 1 | return:_ZN3Foo4incNEi:arg0 2 | -------------------------------------------------------------------------------- /.git-blame-ignore-revs: -------------------------------------------------------------------------------- 1 | a52e90c0419d7da0fe65cb758d2ed10024ec7b2e 2 | 06604fd12398b7d1b0563350ea48382555a51840 3 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "extern/drgn"] 2 | path = extern/drgn 3 | url = https://github.com/JakeHillion/drgn.git 4 | -------------------------------------------------------------------------------- /website/static/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/facebookexperimental/object-introspection/HEAD/website/static/.DS_Store -------------------------------------------------------------------------------- /examples/compile-time-oil/.gitignore: -------------------------------------------------------------------------------- 1 | oilgen 2 | OilVectorOfStrings 3 | OilVectorOfStrings.o 4 | jit.cpp 5 | JitCompiled.*.o 6 | 7 | -------------------------------------------------------------------------------- /website/static/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/facebookexperimental/object-introspection/HEAD/website/static/img/favicon.ico -------------------------------------------------------------------------------- /website/static/img/._OISymbol.svg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/facebookexperimental/object-introspection/HEAD/website/static/img/._OISymbol.svg -------------------------------------------------------------------------------- /website/static/img/docusaurus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/facebookexperimental/object-introspection/HEAD/website/static/img/docusaurus.png -------------------------------------------------------------------------------- /website/static/._.DS_Store: -------------------------------------------------------------------------------- 1 | Mac OS X  2Fx @ATTRxx -------------------------------------------------------------------------------- /website/static/img/mstile-70x70.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/facebookexperimental/object-introspection/HEAD/website/static/img/mstile-70x70.png -------------------------------------------------------------------------------- /website/static/img/._OIBrandmark.svg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/facebookexperimental/object-introspection/HEAD/website/static/img/._OIBrandmark.svg -------------------------------------------------------------------------------- /website/static/img/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/facebookexperimental/object-introspection/HEAD/website/static/img/favicon-16x16.png -------------------------------------------------------------------------------- /website/static/img/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/facebookexperimental/object-introspection/HEAD/website/static/img/favicon-32x32.png -------------------------------------------------------------------------------- /website/static/img/mstile-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/facebookexperimental/object-introspection/HEAD/website/static/img/mstile-144x144.png -------------------------------------------------------------------------------- /website/static/img/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/facebookexperimental/object-introspection/HEAD/website/static/img/mstile-150x150.png -------------------------------------------------------------------------------- /website/static/img/mstile-310x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/facebookexperimental/object-introspection/HEAD/website/static/img/mstile-310x150.png -------------------------------------------------------------------------------- /website/static/img/mstile-310x310.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/facebookexperimental/object-introspection/HEAD/website/static/img/mstile-310x310.png -------------------------------------------------------------------------------- /website/static/img/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/facebookexperimental/object-introspection/HEAD/website/static/img/apple-touch-icon.png -------------------------------------------------------------------------------- /website/static/img/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/facebookexperimental/object-introspection/HEAD/website/static/img/android-chrome-192x192.png -------------------------------------------------------------------------------- /website/static/img/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/facebookexperimental/object-introspection/HEAD/website/static/img/android-chrome-512x512.png -------------------------------------------------------------------------------- /website/src/pages/markdown-page.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Markdown page example 3 | --- 4 | 5 | # Markdown page example 6 | 7 | You don't need React to write simple standalone pages. 8 | -------------------------------------------------------------------------------- /website/static/img/._ObjectIntrospection-SecondarySymbol.svg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/facebookexperimental/object-introspection/HEAD/website/static/img/._ObjectIntrospection-SecondarySymbol.svg -------------------------------------------------------------------------------- /test/integration_entry_doStuff_arg0.oid: -------------------------------------------------------------------------------- 1 | entry:_Z7doStuffR3FooRSt6vectorISt3mapINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEES8_St4lessIS8_ESaISt4pairIKS8_S8_EEESaISF_EERS1_IS8_SaIS8_EERS1_ISB_IS8_dESaISM_EE:arg0 2 | -------------------------------------------------------------------------------- /test/integration_entry_doStuff_this.oid: -------------------------------------------------------------------------------- 1 | entry:_Z7doStuffR3FooRSt6vectorISt3mapINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEES8_St4lessIS8_ESaISt4pairIKS8_S8_EEESaISF_EERS1_IS8_SaIS8_EERS1_ISB_IS8_dESaISM_EE:this 2 | -------------------------------------------------------------------------------- /oss.oid.toml: -------------------------------------------------------------------------------- 1 | [headers] 2 | user_paths = [ 3 | "/usr/lib64/llvm12/lib/clang/12.0.1/include/", 4 | ] 5 | system_paths = [ 6 | "/usr/include/c++/11", 7 | "/usr/include/c++/11/x86_64-redhat-linux/", 8 | "/usr/include", 9 | ] 10 | -------------------------------------------------------------------------------- /test/mocks.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "gmock/gmock.h" 4 | #include "oi/SymbolService.h" 5 | 6 | namespace oi::detail { 7 | 8 | class MockSymbolService : public SymbolService { 9 | public: 10 | MockSymbolService() { 11 | } 12 | }; 13 | 14 | } // namespace oi::detail 15 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | [*] 2 | end_of_line = lf 3 | insert_final_newline = true 4 | trim_trailing_whitespace = true 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | 9 | [*.py] 10 | indent_size = 4 11 | indent_style = space 12 | 13 | [{makefile, Makefile}*] 14 | indent_style = tab 15 | -------------------------------------------------------------------------------- /examples/web/AddrBook/Makefile: -------------------------------------------------------------------------------- 1 | .DEFAULT_GOAL := addrbook 2 | CC = clang++ 3 | CFLAGS = -std=c++20 -g -O3 4 | SRC = AddrBook.cpp 5 | 6 | default: addrbook 7 | 8 | 9 | .PHONY: addrbook 10 | addrbook: $(SRC) 11 | $(CC) -o $@ $^ $(CFLAGS) 12 | 13 | .PHONY: clean 14 | 15 | clean: 16 | rm addrbook 17 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ## Summary 2 | Describe what your change accomplishes 3 | 4 | ## Test plan 5 | Check that OI is working correctly, you can add some unit or integration test, 6 | paste the output of some manual test, and / or paste the output of running 7 | the test locally with `make test-static` 8 | -------------------------------------------------------------------------------- /website/static/img/browserconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | #da532c 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## OI specific 2 | build/ 3 | test/integration_mttest 4 | test/integration_sleepy 5 | test/.autogen-* 6 | oi_preprocessed 7 | *_test.oid 8 | oid_out.json 9 | compile_commands.json 10 | oid_metrics.json 11 | Testing 12 | *.o 13 | PADDING 14 | failed 15 | website/node_modules 16 | 17 | ## Vim 18 | *.swp 19 | *.swo 20 | -------------------------------------------------------------------------------- /website/babel.config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | * 7 | * @format 8 | */ 9 | 10 | module.exports = { 11 | presets: [require.resolve('@docusaurus/core/lib/babel/preset')], 12 | }; 13 | -------------------------------------------------------------------------------- /website/docs/contributing.md: -------------------------------------------------------------------------------- 1 | --- 2 | --- 3 | 4 | # Contributing 5 | 6 | We welcome contributions from the community! If you're looking for an idea then feel free to pick one of the open issues on GitHub. Feel free to drop by the object-introspection chat rooms ([Matrix](https://matrix.to/#/#object-introspection:matrix.org)/[IRC](irc://irc.oftc.net/#object-introspection)). 7 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | BasedOnStyle: Google 4 | AllowShortBlocksOnASingleLine: false 5 | AllowShortCaseLabelsOnASingleLine: false 6 | AllowShortFunctionsOnASingleLine: false 7 | AllowShortIfStatementsOnASingleLine: false 8 | AllowShortLoopsOnASingleLine: false 9 | DerivePointerAlignment: false 10 | PointerAlignment: Left 11 | BinPackParameters: false 12 | BinPackArguments: false 13 | -------------------------------------------------------------------------------- /test/integration/std_unordered_map.toml: -------------------------------------------------------------------------------- 1 | includes = ["unordered_map"] 2 | [cases] 3 | [cases.int_int] 4 | skip = true # https://github.com/facebookexperimental/object-introspection/issues/15 5 | param_types = ["const std::unordered_map&"] 6 | setup = "return {{{1,2},{3,4}}};" 7 | # TODO confirm this JSON is correct 8 | expect_json = '[{"staticSize":56, "dynamicSize":0, "length":2, "capacity":2, "elementStaticSize":0}]' 9 | -------------------------------------------------------------------------------- /.github/workflows/test-report.yml: -------------------------------------------------------------------------------- 1 | name: 'Test Report' 2 | on: 3 | workflow_run: 4 | workflows: ['CI'] 5 | types: 6 | - completed 7 | permissions: 8 | contents: read 9 | actions: read 10 | checks: write 11 | jobs: 12 | report: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: dorny/test-reporter@v1 16 | with: 17 | artifact: test-results-16 18 | name: CTest Tests 19 | path: results.xml 20 | reporter: jest-junit 21 | -------------------------------------------------------------------------------- /website/static/site.webmanifest: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Object Introspection", 3 | "short_name": "Object Introspection", 4 | "icons": [ 5 | { 6 | "src": "/android-chrome-192x192.png", 7 | "sizes": "192x192", 8 | "type": "image/png" 9 | }, 10 | { 11 | "src": "/android-chrome-512x512.png", 12 | "sizes": "512x512", 13 | "type": "image/png" 14 | } 15 | ], 16 | "theme_color": "#ffffff", 17 | "background_color": "#ffffff", 18 | "display": "standalone" 19 | } 20 | -------------------------------------------------------------------------------- /types/thrift_isset_type.toml: -------------------------------------------------------------------------------- 1 | [info] 2 | type_name = "apache::thrift::detail::isset_bitset" 3 | ctype = "THRIFT_ISSET_TYPE" 4 | header = "thrift/lib/cpp2/gen/module_types_h.h" 5 | required_features = ["capture-thrift-isset"] 6 | 7 | # Old: 8 | typeName = "apache::thrift::detail::isset_bitset<" 9 | ns = ["apache::thrift::detail::isset_bitset"] 10 | numTemplateParams = 2 11 | replaceTemplateParamIndex = [] 12 | 13 | [codegen] 14 | decl = """ 15 | // DummyDecl %1% 16 | """ 17 | func = """ 18 | // DummyFunc %1% 19 | """ 20 | 21 | traversal_func = """ 22 | return returnArg; 23 | """ 24 | -------------------------------------------------------------------------------- /test/mttest.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | struct custom_cmp { 4 | bool operator()(int a, int b) const { 5 | return a < b; 6 | } 7 | }; 8 | 9 | struct OIDTestingTwoString { 10 | std::string first; 11 | std::string second; 12 | }; 13 | 14 | struct customTwoStringEq { 15 | bool operator()(const OIDTestingTwoString& a, const OIDTestingTwoString& b) { 16 | return (a.first == a.first && a.second == b.second); 17 | } 18 | }; 19 | 20 | struct customHash { 21 | std::size_t operator()(const OIDTestingTwoString& two) const { 22 | return ((std::hash()(two.first) ^ 23 | (std::hash()(two.second)))); 24 | } 25 | }; 26 | -------------------------------------------------------------------------------- /types/weak_ptr_type.toml: -------------------------------------------------------------------------------- 1 | [info] 2 | type_name = "std::weak_ptr" 3 | ctype = "WEAK_PTR_TYPE" 4 | header = "memory" 5 | 6 | # Old: 7 | typeName = "std::weak_ptr" 8 | ns = ["namespace std"] 9 | numTemplateParams = 1 10 | replaceTemplateParamIndex = [] 11 | 12 | [codegen] 13 | decl = """ 14 | template 15 | void getSizeType(const %1% &s_ptr, size_t& returnArg); 16 | """ 17 | 18 | # Weak pointers do not have ownership, so let's not follow them (for now) 19 | func = """ 20 | template 21 | void getSizeType(const %1% &s_ptr, size_t& returnArg) 22 | { 23 | SAVE_SIZE(sizeof(%1%)); 24 | } 25 | """ 26 | 27 | traversal_func = "return returnArg;" 28 | 29 | -------------------------------------------------------------------------------- /test/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | /* 4 | * This listener ensures that test failures (ASSERTs) in helper functions 5 | * propagate all the way up and stop the test from continuing. 6 | */ 7 | class ThrowListener : public testing::EmptyTestEventListener { 8 | void OnTestPartResult(const testing::TestPartResult& result) override { 9 | if (result.type() == testing::TestPartResult::kFatalFailure) { 10 | throw testing::AssertionException(result); 11 | } 12 | } 13 | }; 14 | 15 | int main(int argc, char* argv[]) { 16 | ::testing::InitGoogleTest(&argc, argv); 17 | ::testing::UnitTest::GetInstance()->listeners().Append(new ThrowListener); 18 | return RUN_ALL_TESTS(); 19 | } 20 | -------------------------------------------------------------------------------- /oi/type_graph/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(type_graph 2 | AddChildren.cpp 3 | AddPadding.cpp 4 | AlignmentCalc.cpp 5 | ClangTypeParser.cpp 6 | DrgnExporter.cpp 7 | DrgnParser.cpp 8 | EnforceCompatibility.cpp 9 | Flattener.cpp 10 | IdentifyContainers.cpp 11 | KeyCapture.cpp 12 | NameGen.cpp 13 | PassManager.cpp 14 | Printer.cpp 15 | Prune.cpp 16 | RemoveMembers.cpp 17 | RemoveTopLevelPointer.cpp 18 | TopoSorter.cpp 19 | TypeGraph.cpp 20 | TypeIdentifier.cpp 21 | Types.cpp 22 | ) 23 | target_link_libraries(type_graph 24 | container_info 25 | symbol_service 26 | drgn 27 | ) 28 | target_include_directories(type_graph SYSTEM PUBLIC ${LLVM_INCLUDE_DIRS} ${CLANG_INCLUDE_DIRS}) 29 | -------------------------------------------------------------------------------- /test/integration/packed.toml: -------------------------------------------------------------------------------- 1 | definitions = ''' 2 | struct __attribute__((__packed__)) Foo { 3 | char *p; /* 8 bytes */ 4 | char c; /* 1 byte */ 5 | long x; /* 8 bytes */ 6 | }; 7 | ''' 8 | [cases] 9 | [cases.a] 10 | param_types = ["const Foo&"] 11 | setup = "return {};" 12 | expect_json = '''[{ 13 | "staticSize":17, 14 | "dynamicSize":0, 15 | "exclusiveSize":0, 16 | "size":17, 17 | "members":[ 18 | {"name":"p", "staticSize":8, "dynamicSize":0, "exclusiveSize":8, "size":8}, 19 | {"name":"c", "staticSize":1, "dynamicSize":0, "exclusiveSize":1, "size":1}, 20 | {"name":"x", "staticSize":8, "dynamicSize":0, "exclusiveSize":8, "size":8} 21 | ]}]''' 22 | -------------------------------------------------------------------------------- /oi/support/Toml.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #define TOML_HEADER_ONLY 0 17 | #include 18 | -------------------------------------------------------------------------------- /oi/support/Toml.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #define TOML_IMPLEMENTATION 17 | #include "oi/support/Toml.h" 18 | -------------------------------------------------------------------------------- /website/static/img/ObjectIntrospection-SecondarySymbol.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /cmake/FindThrift.cmake: -------------------------------------------------------------------------------- 1 | # - Try to find Thrift 2 | # 3 | # Defines the following variables: 4 | # 5 | # THRIFT_FOUND - system has Thrift 6 | # THRIFT_COMPILER - The thrift compiler executable 7 | # THRIFT_INCLUDE_DIRS - The Thrift include directory 8 | 9 | find_program(THRIFT_COMPILER thrift) 10 | 11 | find_path(THRIFT_INCLUDE_DIRS 12 | NAMES 13 | thrift/annotation/cpp.thrift 14 | HINTS 15 | ${THRIFT_ROOT}) 16 | 17 | include (FindPackageHandleStandardArgs) 18 | 19 | # handle the QUIETLY and REQUIRED arguments and set THRIFT_FOUND to TRUE if all listed variables are TRUE 20 | FIND_PACKAGE_HANDLE_STANDARD_ARGS(Thrift "Thrift not found" 21 | THRIFT_COMPILER 22 | THRIFT_INCLUDE_DIRS) 23 | 24 | mark_as_advanced(THRIFT_COMPILER THRIFT_INCLUDE_DIRS) 25 | -------------------------------------------------------------------------------- /examples/compile-time-oil/Makefile: -------------------------------------------------------------------------------- 1 | .DEFAULT_GOAL := all 2 | 3 | CXX=clang++ 4 | CXXFLAGS=-g -std=c++17 -Wall -pedantic -DOIL_AOT_COMPILATION=1 5 | 6 | .PHONY: oilgen 7 | 8 | INC=-I../../include 9 | 10 | OilVectorOfStrings.o: OilVectorOfStrings.cpp 11 | ${CXX} ${CXXFLAGS} ${INC} OilVectorOfStrings.cpp -c -o OilVectorOfStrings.o 12 | 13 | oilgen: 14 | rm -f oilgen 15 | (cd ../../ && make oid) 16 | ln -s ../../build/oilgen oilgen 17 | 18 | OilVectorOfStrings: oilgen OilVectorOfStrings.o 19 | ${CXX} ${CXXFLAGS} OilVectorOfStrings.o $$(./oilgen --exit-code -o JitCompiled.o -c ../../build/sample.oid.toml -d OilVectorOfStrings.o) -o OilVectorOfStrings 20 | 21 | all: OilVectorOfStrings 22 | 23 | clean: 24 | rm -f oilgen OilVectorOfStrings{,.o,.o.dwarf} JitCompiled.*.o jit.cpp 25 | 26 | -------------------------------------------------------------------------------- /cmake/PreventInSourceBuilds.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # This function will prevent in-source builds 3 | function(AssureOutOfSourceBuilds) 4 | # make sure the user doesn't play dirty with symlinks 5 | get_filename_component(srcdir "${CMAKE_SOURCE_DIR}" REALPATH) 6 | get_filename_component(bindir "${CMAKE_BINARY_DIR}" REALPATH) 7 | 8 | # disallow in-source builds 9 | if("${srcdir}" STREQUAL "${bindir}") 10 | message("######################################################") 11 | message("Warning: in-source builds are disabled") 12 | message("Please create a separate build directory and run cmake from there") 13 | message("######################################################") 14 | message(FATAL_ERROR "Quitting configuration") 15 | endif() 16 | endfunction() 17 | 18 | assureoutofsourcebuilds() 19 | -------------------------------------------------------------------------------- /types/folly_iobuf_queue_type.toml: -------------------------------------------------------------------------------- 1 | [info] 2 | type_name = "folly::IOBufQueue" 3 | ctype = "FOLLY_IOBUFQUEUE_TYPE" 4 | header = "folly/io/IOBufQueue.h" 5 | 6 | # Old: 7 | typeName = "folly::IOBufQueue" 8 | matcher = "^folly::IOBufQueue$" 9 | ns = ["folly::IOBufQueue"] 10 | numTemplateParams = 0 11 | replaceTemplateParamIndex = [] 12 | 13 | [codegen] 14 | decl = """ 15 | void getSizeType(const %1% &container, size_t& returnArg); 16 | """ 17 | 18 | func = """ 19 | void getSizeType(const %1% &container, size_t& returnArg) 20 | { 21 | SAVE_SIZE(sizeof(%1%)); 22 | 23 | const folly::IOBuf *head = container.front(); 24 | SAVE_DATA((uintptr_t)head); 25 | if (head && ctx.pointers.add((uintptr_t)head)) { 26 | SAVE_DATA(1); 27 | getSizeType(*head, returnArg); 28 | } else { 29 | SAVE_DATA(0); 30 | } 31 | } 32 | """ 33 | -------------------------------------------------------------------------------- /.github/workflows/test-deploy.yml: -------------------------------------------------------------------------------- 1 | name: Test deployment 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - main 7 | # Review gh actions docs if you want to further define triggers, paths, etc 8 | # https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#on 9 | 10 | jobs: 11 | test-deploy: 12 | defaults: 13 | run: 14 | working-directory: website 15 | name: Test deployment 16 | runs-on: ubuntu-latest 17 | steps: 18 | - uses: actions/checkout@v3 19 | - uses: actions/setup-node@v3 20 | with: 21 | node-version: 18 22 | cache: yarn 23 | cache-dependency-path: ./website/yarn.lock 24 | 25 | - name: Install dependencies 26 | run: yarn install --frozen-lockfile 27 | - name: Test build website 28 | run: yarn build 29 | -------------------------------------------------------------------------------- /cmake/Finduring.cmake: -------------------------------------------------------------------------------- 1 | # - Find liburing 2 | # 3 | # uring_INCLUDE_DIR - Where to find liburing.h 4 | # uring_LIBRARIES - List of libraries when using uring. 5 | # uring_FOUND - True if uring found. 6 | 7 | find_path(uring_INCLUDE_DIR 8 | NAMES liburing.h) 9 | find_library(uring_LIBRARIES 10 | NAMES liburing.a liburing.so) 11 | 12 | include(FindPackageHandleStandardArgs) 13 | find_package_handle_standard_args(uring 14 | DEFAULT_MSG uring_LIBRARIES uring_INCLUDE_DIR) 15 | 16 | mark_as_advanced( 17 | uring_INCLUDE_DIR 18 | uring_LIBRARIES) 19 | 20 | if(uring_FOUND AND NOT TARGET uring::uring) 21 | add_library(uring::uring UNKNOWN IMPORTED) 22 | set_target_properties(uring::uring PROPERTIES 23 | INTERFACE_INCLUDE_DIRECTORIES "${uring_INCLUDE_DIR}" 24 | IMPORTED_LINK_INTERFACE_LANGUAGES "C" 25 | IMPORTED_LOCATION "${uring_LIBRARIES}") 26 | endif() 27 | 28 | -------------------------------------------------------------------------------- /website/sidebars.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | * 7 | * @format 8 | */ 9 | 10 | /** 11 | * Creating a sidebar enables you to: 12 | - create an ordered group of docs 13 | - render a sidebar for each doc of that group 14 | - provide next/previous navigation 15 | 16 | The sidebars can be generated from the filesystem, or explicitly defined here. 17 | 18 | Create as many sidebars as you want. 19 | */ 20 | 21 | module.exports = { 22 | mysidebar: [ 23 | 'intro', 24 | 'getting-started', 25 | { 26 | type: 'category', 27 | label: 'A Brief Practical Introduction', 28 | items: ['addrbook-intro', 'addrbook-this', 'addrbook-funcargs'], 29 | }, 30 | 'constraints', 31 | 'contributing', 32 | ], 33 | }; 34 | -------------------------------------------------------------------------------- /test/test_enforce_compatibility.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "oi/type_graph/EnforceCompatibility.h" 4 | #include "test/type_graph_utils.h" 5 | 6 | using namespace type_graph; 7 | 8 | TEST(EnforceCompatibilityTest, ParentContainers) { 9 | test(EnforceCompatibility::createPass(), 10 | R"( 11 | [0] Class: MyClass (size: 24) 12 | Member: __oi_parent (offset: 0) 13 | [1] Container: std::vector (size: 24) 14 | Param 15 | Primitive: int32_t 16 | )", 17 | R"( 18 | [0] Class: MyClass (size: 24) 19 | )"); 20 | } 21 | 22 | TEST(EnforceCompatibilityTest, TypesToStub) { 23 | test(EnforceCompatibility::createPass(), 24 | R"( 25 | [0] Class: EnumMap (size: 8) 26 | Member: a (offset: 0) 27 | Primitive: int32_t 28 | Member: b (offset: 4) 29 | Primitive: int32_t 30 | )", 31 | R"( 32 | [0] Class: EnumMap (size: 8) 33 | )"); 34 | } 35 | -------------------------------------------------------------------------------- /test/integration/thrift_namespaces.toml: -------------------------------------------------------------------------------- 1 | thrift_definitions = ''' 2 | namespace cpp2 namespaceA.namespaceB 3 | 4 | struct TTTTT { 5 | 1: i32 a; 6 | 2: i32 b; 7 | 3: i32 c; 8 | } 9 | ''' 10 | 11 | [cases] 12 | [cases.a] 13 | oil_skip = 'enum type template arguments do not match' # https://github.com/facebookexperimental/object-introspection/issues/297 14 | param_types = ["const namespaceA::namespaceB::TTTTT&"] 15 | setup = ''' 16 | namespaceA::namespaceB::TTTTT ret; 17 | return ret; 18 | ''' 19 | features = ["capture-thrift-isset"] 20 | expect_json = '''[{ 21 | "staticSize":16, 22 | "dynamicSize":0, 23 | "members":[ 24 | {"name":"__fbthrift_field_a", "staticSize":4}, 25 | {"name":"__fbthrift_field_b", "staticSize":4}, 26 | {"name":"__fbthrift_field_c", "staticSize":4}, 27 | {"name":"__isset", "staticSize":3} 28 | ]}]''' 29 | -------------------------------------------------------------------------------- /website/src/pages/styles.module.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | * 7 | * @format 8 | */ 9 | 10 | /** 11 | * CSS files with the .module.css suffix will be treated as CSS modules 12 | * and scoped locally. 13 | */ 14 | 15 | .heroBanner { 16 | padding: 4rem 0; 17 | text-align: center; 18 | position: relative; 19 | overflow: hidden; 20 | } 21 | 22 | @media screen and (max-width: 996px) { 23 | .heroBanner { 24 | padding: 2rem; 25 | } 26 | } 27 | 28 | .buttons { 29 | display: flex; 30 | align-items: center; 31 | justify-content: center; 32 | } 33 | 34 | .features { 35 | display: flex; 36 | align-items: center; 37 | padding: 2rem 0; 38 | width: 100%; 39 | } 40 | 41 | .featureImage { 42 | height: 200px; 43 | width: 200px; 44 | } 45 | -------------------------------------------------------------------------------- /oi/TimeUtils.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #pragma once 17 | #include 18 | 19 | using time_hr = std::chrono::high_resolution_clock; 20 | 21 | template 22 | auto time_ns(Duration const& d) { 23 | return std::chrono::duration_cast(d).count(); 24 | } 25 | -------------------------------------------------------------------------------- /types/try_type.toml: -------------------------------------------------------------------------------- 1 | [info] 2 | type_name = "folly::Try" 3 | ctype = "TRY_TYPE" 4 | header = "folly/Try.h" 5 | 6 | # Old: 7 | typeName = "folly::Try<" 8 | ns = ["folly::Try"] 9 | numTemplateParams = 1 10 | replaceTemplateParamIndex = [] 11 | 12 | [codegen] 13 | decl = """ 14 | template 15 | void getSizeType(const %1% &container, size_t& returnArg); 16 | """ 17 | 18 | func = """ 19 | template 20 | void getSizeType(const %1% &container, size_t& returnArg) 21 | { 22 | SAVE_SIZE(sizeof(%1%)); 23 | SAVE_DATA((uintptr_t)&container); 24 | if (container.hasValue()) { 25 | SAVE_DATA((uintptr_t)(&(container.value()))); 26 | getSizeType(container.value(), returnArg); 27 | } else { 28 | SAVE_DATA(0); 29 | } 30 | } 31 | 32 | /* Workaround for issue https://github.com/facebookexperimental/object-introspection/issues/289 */ 33 | extern "C" void _ZNSt11logic_errorC2EOS_(void) {} 34 | """ 35 | -------------------------------------------------------------------------------- /types/repeated_field_type.toml: -------------------------------------------------------------------------------- 1 | [info] 2 | type_name = "google::protobuf::RepeatedField" 3 | ctype = "REPEATED_FIELD_TYPE" 4 | header = "google/protobuf/repeated_field.h" 5 | 6 | # Old: 7 | typeName = "google::protobuf::RepeatedField<" 8 | ns = ["google::protobuf::RepeatedField"] 9 | numTemplateParams = 1 10 | replaceTemplateParamIndex = [] 11 | 12 | [codegen] 13 | decl = """ 14 | template 15 | void getSizeType(const %1% &container, size_t& returnArg); 16 | """ 17 | 18 | func = """ 19 | template 20 | void getSizeType(const %1% &container, size_t& returnArg) 21 | { 22 | SAVE_DATA((uintptr_t)&container); 23 | SAVE_DATA((uintptr_t)container.Capacity()); 24 | SAVE_DATA((uintptr_t)container.size()); 25 | // The double ampersand is needed otherwise this loop doesn't work with vector 26 | for (const auto& it: container) { 27 | getSizeType(it, returnArg); 28 | } 29 | } 30 | """ 31 | -------------------------------------------------------------------------------- /test/test_container_info.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "oi/ContainerInfo.h" 4 | 5 | TEST(ContainerInfoTest, matcher) { 6 | ContainerInfo info{"std::vector", SEQ_TYPE, "vector"}; 7 | 8 | EXPECT_TRUE(info.matches("std::vector")); 9 | EXPECT_TRUE(info.matches("std::vector>")); 10 | EXPECT_TRUE(info.matches("std::vector")); 11 | 12 | EXPECT_FALSE(info.matches("vector")); 13 | EXPECT_FALSE(info.matches("non_std::vector")); 14 | EXPECT_FALSE(info.matches("std::vector_other")); 15 | EXPECT_FALSE(info.matches("std::list>")); 16 | EXPECT_FALSE(info.matches("std::vector::value_type")); 17 | EXPECT_FALSE(info.matches("std::vector::value_type")); 18 | EXPECT_FALSE(info.matches("std::vector>::value_type")); 19 | // Uh-oh, here's a case that I don't think regexes are powerful enough to 20 | // match: EXPECT_FALSE(info.matches("std::vector::subtype")); 21 | } 22 | -------------------------------------------------------------------------------- /types/repeated_ptr_field_type.toml: -------------------------------------------------------------------------------- 1 | [info] 2 | type_name = "google::protobuf::RepeatedPtrField" 3 | ctype = "REPEATED_FIELD_TYPE" 4 | header = "google/protobuf/repeated_field.h" 5 | 6 | # Old: 7 | typeName = "google::protobuf::RepeatedPtrField<" 8 | ns = ["google::protobuf::RepeatedPtrField"] 9 | numTemplateParams = 1 10 | replaceTemplateParamIndex = [] 11 | 12 | [codegen] 13 | decl = """ 14 | template 15 | void getSizeType(const %1% &container, size_t& returnArg); 16 | """ 17 | 18 | func = """ 19 | template 20 | void getSizeType(const %1% &container, size_t& returnArg) 21 | { 22 | SAVE_DATA((uintptr_t)&container); 23 | SAVE_DATA((uintptr_t)container.Capacity()); 24 | SAVE_DATA((uintptr_t)container.size()); 25 | // The double ampersand is needed otherwise this loop doesn't work with vector 26 | for (const auto& it: container) { 27 | getSizeType(it, returnArg); 28 | } 29 | } 30 | """ 31 | -------------------------------------------------------------------------------- /types/boost_bimap_type.toml: -------------------------------------------------------------------------------- 1 | [info] 2 | type_name = "boost::bimap" 3 | ctype = "BOOST_BIMAP_TYPE" 4 | header = "boost/bimap.hpp" 5 | 6 | # Old: 7 | numTemplateParams = 2 8 | typeName = "boost::bimap" 9 | ns = ["boost::bimap"] 10 | 11 | [codegen] 12 | decl = """ 13 | template 14 | void getSizeType(const %1% &container, 15 | size_t& returnArg); 16 | """ 17 | 18 | func = """ 19 | template 20 | void getSizeType(const %1% &container, 21 | size_t& returnArg) 22 | { 23 | 24 | SAVE_SIZE(sizeof(%1%)); 25 | 26 | SAVE_DATA((uintptr_t)container.size()); 27 | 28 | for (auto const& it: container) { 29 | getSizeType(it.left, returnArg); 30 | getSizeType(it.right, returnArg); 31 | } 32 | } 33 | """ 34 | -------------------------------------------------------------------------------- /test/type_graph_utils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "oi/type_graph/Types.h" 8 | 9 | namespace oi::detail::type_graph { 10 | class Pass; 11 | class TypeGraph; 12 | } // namespace oi::detail::type_graph 13 | 14 | using namespace oi::detail; 15 | 16 | void check(const type_graph::TypeGraph& typeGraph, 17 | std::string_view expected, 18 | std::string_view comment); 19 | 20 | void test(type_graph::Pass pass, 21 | std::string_view input, 22 | std::string_view expectedAfter); 23 | 24 | void testNoChange(type_graph::Pass pass, std::string_view input); 25 | 26 | std::vector> getContainerInfos(); 27 | type_graph::Container getVector(type_graph::NodeId id = 0); 28 | type_graph::Container getMap(type_graph::NodeId id = 0); 29 | type_graph::Container getList(type_graph::NodeId id = 0); 30 | type_graph::Container getPair(type_graph::NodeId id = 0); 31 | -------------------------------------------------------------------------------- /oi/Portability.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #pragma once 17 | 18 | namespace oi::detail { 19 | 20 | #ifdef OI_META_INTERNAL 21 | 22 | #define OI_PORTABILITY_META_INTERNAL() 1 23 | static constexpr bool kIsMetaInternal = true; 24 | 25 | #else 26 | 27 | #define OI_PORTABILITY_META_INTERNAL() 0 28 | static constexpr bool kIsMetaInternal = false; 29 | 30 | #endif 31 | 32 | } // namespace oi::detail 33 | -------------------------------------------------------------------------------- /types/stack_container_adapter_type.toml: -------------------------------------------------------------------------------- 1 | [info] 2 | type_name = "std::stack" 3 | ctype = "CONTAINER_ADAPTER_TYPE" 4 | header = "stack" 5 | underlying_container_index = 1 6 | 7 | # Old: 8 | typeName = "std::stack<" 9 | ns = ["namespace std"] 10 | replaceTemplateParamIndex = [] 11 | underlyingContainerIndex = 1 12 | 13 | [codegen] 14 | decl = """ 15 | template 16 | void getSizeType(const %1% &container, size_t& returnArg); 17 | """ 18 | 19 | func = """ 20 | template 21 | void getSizeType(const %1% &containerAdapter, size_t& returnArg) 22 | { 23 | SAVE_DATA((uintptr_t)&containerAdapter); 24 | 25 | // Only record the overhead of this container adapter - don't count the 26 | // underlying container as that will be taken care of in its own 27 | // getSizeType() function 28 | SAVE_SIZE(sizeof(%1%) - sizeof(Container)); 29 | 30 | const Container &container = get_container(containerAdapter); 31 | getSizeType(container, returnArg); 32 | } 33 | """ 34 | -------------------------------------------------------------------------------- /types/queue_container_adapter_type.toml: -------------------------------------------------------------------------------- 1 | [info] 2 | type_name = "std::queue" 3 | ctype = "CONTAINER_ADAPTER_TYPE" 4 | header = "queue" 5 | underlying_container_index = 1 6 | 7 | # Old: 8 | typeName = "std::queue<" 9 | ns = ["namespace std"] 10 | replaceTemplateParamIndex = [] 11 | underlyingContainerIndex = 1 12 | 13 | [codegen] 14 | decl = """ 15 | template 16 | void getSizeType(const %1% &container, size_t& returnArg); 17 | """ 18 | 19 | func = """ 20 | template 21 | void getSizeType(const %1% &containerAdapter, size_t& returnArg) 22 | { 23 | SAVE_DATA((uintptr_t)&containerAdapter); 24 | 25 | // Only record the overhead of this container adapter - don't count the 26 | // underlying container as that will be taken care of in its own 27 | // getSizeType() function 28 | SAVE_SIZE(sizeof(%1%) - sizeof(Container)); 29 | 30 | const Container &container = get_container(containerAdapter); 31 | getSizeType(container, returnArg); 32 | } 33 | """ 34 | 35 | -------------------------------------------------------------------------------- /website/docs/constraints.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 3 3 | --- 4 | 5 | # Limitations and Constraints 6 | 7 | OI has been initially designed for use within Meta and therefore some of its current implementation may present challenges for you in your environment. We'd love to hear from you about what you need supporting and how any limitations effect you so please feel free to create issues on [GitHub](https://github.com/facebookexperimental/object-introspection) or tell us directly on [Matrix](https://matrix.to/#/#object-introspection:matrix.org) or [IRC](irc://irc.oftc.net/#object-introspection). 8 | 9 | Current known limitations and constraints: 10 | 11 |
    12 |
  • Statically linked binaries only.
  • 13 |
  • Split Debug DWARF not currently supported (planned).
  • 14 |
  • C style unions not supported.
  • 15 |
  • Only supported architecture is x86-64.
  • 16 |
  • Only single probe specifications allowed in an oid invocation
  • 17 |
  • Virtual inheritance support
  • 18 |
  • Template specialization support
  • 19 |
  • Pluggable container support
  • 20 |
21 | -------------------------------------------------------------------------------- /cmake/Findzstd.cmake: -------------------------------------------------------------------------------- 1 | # - Find zstd 2 | # Find the zstd compression library and includes 3 | # 4 | # zstd_INCLUDE_DIRS - where to find zstd.h, etc. 5 | # zstd_LIBRARIES - List of libraries when using zstd. 6 | # zstd_FOUND - True if zstd found. 7 | 8 | find_path(zstd_INCLUDE_DIRS zstd.h HINTS ${zstd_ROOT_DIR}/include) 9 | 10 | # Don't merge these two `find_library`! This is to give priority to the static library. 11 | # See "Professional CMake", page 300, for more info. 12 | find_library(zstd_LIBRARIES libzstd.a HINTS ${zstd_ROOT_DIR}/lib) 13 | find_library(zstd_LIBRARIES zstd HINTS ${zstd_ROOT_DIR}/lib) 14 | 15 | include(FindPackageHandleStandardArgs) 16 | find_package_handle_standard_args(zstd DEFAULT_MSG zstd_LIBRARIES zstd_INCLUDE_DIRS) 17 | 18 | mark_as_advanced( 19 | zstd_LIBRARIES 20 | zstd_INCLUDE_DIRS) 21 | 22 | if(zstd_FOUND AND NOT (TARGET zstd::zstd)) 23 | add_library (zstd::zstd UNKNOWN IMPORTED) 24 | set_target_properties(zstd::zstd 25 | PROPERTIES 26 | IMPORTED_LOCATION ${zstd_LIBRARIES} 27 | INTERFACE_INCLUDE_DIRECTORIES ${zstd_INCLUDE_DIRS} 28 | COMPILE_DEFINITIONS -DZSTD) 29 | endif() 30 | -------------------------------------------------------------------------------- /test/test_remove_top_level_pointer.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "oi/type_graph/RemoveTopLevelPointer.h" 4 | #include "oi/type_graph/Types.h" 5 | #include "test/type_graph_utils.h" 6 | 7 | using namespace type_graph; 8 | 9 | TEST(RemoveTopLevelPointerTest, TopLevelPointerRemoved) { 10 | test(RemoveTopLevelPointer::createPass(), 11 | R"( 12 | [0] Pointer 13 | [1] Class: MyClass (size: 4) 14 | Member: n (offset: 0) 15 | Primitive: int32_t 16 | )", 17 | R"( 18 | [1] Class: MyClass (size: 4) 19 | Member: n (offset: 0) 20 | Primitive: int32_t 21 | )"); 22 | } 23 | 24 | TEST(RemoveTopLevelPointerTest, TopLevelClassUntouched) { 25 | testNoChange(RemoveTopLevelPointer::createPass(), R"( 26 | [0] Class: MyClass (size: 4) 27 | Member: n (offset: 0) 28 | Primitive: int32_t 29 | )"); 30 | } 31 | 32 | TEST(RemoveTopLevelPointerTest, IntermediatePointerUntouched) { 33 | testNoChange(RemoveTopLevelPointer::createPass(), R"( 34 | [0] Class: MyClass (size: 4) 35 | Member: n (offset: 0) 36 | [1] Pointer 37 | Primitive: int32_t 38 | )"); 39 | } 40 | -------------------------------------------------------------------------------- /types/priority_queue_container_adapter_type.toml: -------------------------------------------------------------------------------- 1 | [info] 2 | type_name = "std::priority_queue" 3 | ctype = "CONTAINER_ADAPTER_TYPE" 4 | header = "queue" 5 | underlying_container_index = 1 6 | stub_template_params = [2] 7 | 8 | # Old: 9 | typeName = "std::priority_queue<" 10 | ns = ["namespace std"] 11 | replaceTemplateParamIndex = [] 12 | underlyingContainerIndex = 1 13 | 14 | [codegen] 15 | decl = """ 16 | template 17 | void getSizeType(const %1% &container, size_t& returnArg); 18 | """ 19 | 20 | func = """ 21 | template 22 | void getSizeType(const %1% &containerAdapter, size_t& returnArg) 23 | { 24 | SAVE_DATA((uintptr_t)&containerAdapter); 25 | 26 | // Only record the overhead of this container adapter - don't count the 27 | // underlying container as that will be taken care of in its own 28 | // getSizeType() function 29 | SAVE_SIZE(sizeof(%1%) - sizeof(Container)); 30 | 31 | const Container &container = get_container(containerAdapter); 32 | getSizeType(container, returnArg); 33 | } 34 | """ 35 | -------------------------------------------------------------------------------- /oi/arch/Arch.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #pragma once 17 | 18 | #include 19 | #include 20 | 21 | struct user_regs_struct; 22 | 23 | namespace oi::detail::arch { 24 | 25 | void setProgramCounter(user_regs_struct& regs, uintptr_t pc); 26 | 27 | std::optional getReturnValueAddress(const user_regs_struct&); 28 | 29 | std::optional naiveReadArgument(const user_regs_struct&, 30 | uint8_t idx); 31 | 32 | } // namespace oi::detail::arch 33 | -------------------------------------------------------------------------------- /oi/Config.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #pragma once 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #include "oi/Features.h" 24 | #include "oi/OICodeGen.h" 25 | #include "oi/OICompiler.h" 26 | 27 | namespace oi::detail::config { 28 | 29 | std::optional processConfigFiles( 30 | std::span configFilePaths, 31 | std::map featureMap, 32 | OICompiler::Config& compilerConfig, 33 | OICodeGen::Config& generatorConfig); 34 | 35 | } // namespace oi::detail::config 36 | -------------------------------------------------------------------------------- /oi/OILibrary.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #include "oi/OILibraryImpl.h" 17 | #include "oi/oi-jit.h" 18 | 19 | namespace oi { 20 | 21 | OILibrary::OILibrary(void* atomicHole, 22 | std::unordered_set fs, 23 | GeneratorOptions opts) 24 | : pimpl_{std::make_unique( 25 | atomicHole, std::move(fs), std::move(opts))} { 26 | } 27 | OILibrary::~OILibrary() { 28 | } 29 | 30 | std::pair OILibrary::init() { 31 | return pimpl_->init(); 32 | } 33 | 34 | } // namespace oi 35 | -------------------------------------------------------------------------------- /website/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "oi-web", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "docusaurus": "docusaurus", 7 | "start": "docusaurus start", 8 | "build": "docusaurus build", 9 | "swizzle": "docusaurus swizzle", 10 | "deploy": "docusaurus deploy", 11 | "clear": "docusaurus clear", 12 | "serve": "docusaurus serve", 13 | "write-translations": "docusaurus write-translations", 14 | "write-heading-ids": "docusaurus write-heading-ids" 15 | }, 16 | "dependencies": { 17 | "@docusaurus/core": "3.5.2", 18 | "@docusaurus/preset-classic": "3.5.2", 19 | "@mdx-js/react": "^3.0.1", 20 | "clsx": "^2.1.1", 21 | "prism-react-renderer": "^2.1.0", 22 | "react": "^18.3.1", 23 | "react-dom": "^18.3.1" 24 | }, 25 | "devDependencies": { 26 | "@docusaurus/module-type-aliases": "3.5.2", 27 | "@docusaurus/types": "3.5.2" 28 | }, 29 | "browserslist": { 30 | "production": [ 31 | ">0.5%", 32 | "not dead", 33 | "not op_mini all" 34 | ], 35 | "development": [ 36 | "last 1 chrome version", 37 | "last 1 firefox version", 38 | "last 1 safari version" 39 | ] 40 | }, 41 | "engines": { 42 | "node": ">=18.0" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /types/folly_small_heap_vector_map.toml: -------------------------------------------------------------------------------- 1 | [info] 2 | type_name = "folly::small_heap_vector_map" 3 | ctype = "FOLLY_SMALL_HEAP_VECTOR_MAP" 4 | header = "folly/container/heap_vector_types.h" 5 | 6 | # Old: 7 | typeName = "folly::small_heap_vector_map" 8 | ns = ["folly::small_heap_vector_map"] 9 | numTemplateParams = 2 10 | replaceTemplateParamIndex = [] 11 | 12 | [codegen] 13 | decl = """ 14 | template 15 | void getSizeType(const %1% &container, size_t& returnArg); 16 | """ 17 | 18 | func = """ 19 | template 20 | void getSizeType(const %1% &container, size_t& returnArg) 21 | { 22 | SAVE_SIZE(sizeof(%1%)); 23 | 24 | SAVE_DATA((uintptr_t)&container); 25 | SAVE_DATA((uintptr_t)container.capacity()); 26 | SAVE_DATA((uintptr_t)container.size()); 27 | 28 | SAVE_SIZE((container.capacity() - container.size()) * (sizeof(Key) + sizeof(Value))); 29 | 30 | for (auto const& it : container) 31 | { 32 | getSizeType(it.first, returnArg); 33 | getSizeType(it.second, returnArg); 34 | } 35 | } 36 | """ 37 | -------------------------------------------------------------------------------- /website/README.md: -------------------------------------------------------------------------------- 1 | # Website 2 | 3 | This website is built using [Docusaurus 2](https://docusaurus.io/), a modern static website generator. 4 | 5 | ### Installation 6 | 7 | ``` 8 | $ yarn 9 | ``` 10 | 11 | ### Local Development 12 | 13 | ``` 14 | $ yarn start 15 | ``` 16 | 17 | This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server. 18 | 19 | ### Build 20 | 21 | ``` 22 | $ yarn build 23 | ``` 24 | 25 | This command generates static content into the `build` directory and can be served using any static contents hosting service. 26 | 27 | ### Deployment 28 | 29 | Using SSH: 30 | 31 | ``` 32 | $ USE_SSH=true yarn deploy 33 | ``` 34 | 35 | Not using SSH: 36 | 37 | ``` 38 | $ GIT_USER= yarn deploy 39 | ``` 40 | 41 | If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch. 42 | 43 | ### Continuous Integration 44 | 45 | Some common defaults for linting/formatting have been set for you. If you integrate your project with an open source Continuous Integration system (e.g. Travis CI, CircleCI), you may check for issues using the following command. 46 | 47 | ``` 48 | $ yarn ci 49 | ``` 50 | -------------------------------------------------------------------------------- /examples/compile-time-oil/OilVectorOfStrings.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include 18 | 19 | #include 20 | #include 21 | #include 22 | 23 | struct Foo { 24 | std::vector strings; 25 | }; 26 | 27 | int main() { 28 | Foo foo; 29 | 30 | foo.strings.push_back("Lorem ipsum dolor"); 31 | foo.strings.push_back("sit amet,"); 32 | foo.strings.push_back("consectetur adipiscing elit,"); 33 | 34 | size_t size = -1; 35 | int ret = ObjectIntrospection::getObjectSize(foo, size); 36 | 37 | std::cout << "oil returned: " << ret << "; with size: " << size << std::endl; 38 | } 39 | -------------------------------------------------------------------------------- /website/src/css/custom.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | * 7 | * @format 8 | */ 9 | 10 | /** 11 | * Any CSS included here will be global. The classic template 12 | * bundles Infima by default. Infima is a CSS framework designed to 13 | * work well for content-centric websites. 14 | */ 15 | 16 | /* You can override the default Infima variables here. */ 17 | :root { 18 | --ifm-color-primary: #2e8555; 19 | --ifm-color-primary-dark: #29784c; 20 | --ifm-color-primary-darker: #277148; 21 | --ifm-color-primary-darkest: #205d3b; 22 | --ifm-color-primary-light: #33925d; 23 | --ifm-color-primary-lighter: #359962; 24 | --ifm-color-primary-lightest: #3cad6e; 25 | --ifm-code-font-size: 95%; 26 | } 27 | 28 | /* For readability concerns, you should choose a lighter palette in dark mode. */ 29 | [data-theme='dark'] { 30 | --ifm-color-primary: #25c2a0; 31 | --ifm-color-primary-dark: #21af90; 32 | --ifm-color-primary-darker: #1fa588; 33 | --ifm-color-primary-darkest: #1a8870; 34 | --ifm-color-primary-light: #29d5b0; 35 | --ifm-color-primary-lighter: #32d8b4; 36 | --ifm-color-primary-lightest: #4fddbf; 37 | } 38 | -------------------------------------------------------------------------------- /oi/X86InstDefs.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #pragma once 17 | 18 | static constexpr uint8_t int3Inst = 0xCC; 19 | static constexpr uint8_t nopInst = 0x90; 20 | static constexpr uint8_t movabsrdi0Inst = 0x48; /* movabs %rdi */ 21 | static constexpr uint8_t movabsrdi1Inst = 0xbf; 22 | static constexpr uint8_t movabsrax0Inst = 0x48; /* movabs %rax */ 23 | static constexpr uint8_t movabsrax1Inst = 0xb8; 24 | static constexpr uint8_t callRaxInst0Inst = 0xff; 25 | static constexpr uint8_t callRaxInst1Inst = 0xd0; 26 | static constexpr long syscallInsts = 0x9090909090050fcc; 27 | static constexpr uint8_t ud2Inst0 = 0x0f; 28 | static constexpr uint8_t ud2Inst1 = 0x0b; 29 | -------------------------------------------------------------------------------- /oi/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(toml 2 | support/Toml.cpp 3 | ) 4 | target_link_libraries(toml PUBLIC tomlplusplus::tomlplusplus) 5 | 6 | add_library(drgn_utils DrgnUtils.cpp) 7 | target_link_libraries(drgn_utils 8 | glog::glog 9 | drgn 10 | ) 11 | 12 | add_library(symbol_service 13 | Descs.cpp 14 | SymbolService.cpp 15 | arch/aarch64.cpp 16 | arch/x86_64.cpp 17 | ) 18 | target_link_libraries(symbol_service 19 | drgn_utils 20 | 21 | Boost::headers 22 | ${Boost_LIBRARIES} 23 | glog::glog 24 | 25 | dw 26 | ) 27 | 28 | add_library(features Features.cpp) 29 | target_link_libraries(features glog::glog) 30 | 31 | add_library(container_info 32 | ContainerInfo.cpp 33 | ) 34 | target_link_libraries(container_info 35 | features 36 | 37 | Boost::regex 38 | glog::glog 39 | toml 40 | ) 41 | 42 | add_library(codegen 43 | CodeGen.cpp 44 | FuncGen.cpp 45 | OICodeGen.cpp 46 | ) 47 | target_link_libraries(codegen 48 | container_info 49 | resources 50 | symbol_service 51 | type_graph 52 | 53 | Boost::headers 54 | ${Boost_LIBRARIES} 55 | folly_headers 56 | glog::glog 57 | ) 58 | 59 | add_library(exporters_csv exporters/CSV.cpp) 60 | target_include_directories(exporters_csv PUBLIC ${CMAKE_SOURCE_DIR}/include) 61 | target_link_libraries(exporters_csv oil) 62 | 63 | add_subdirectory(type_graph) 64 | -------------------------------------------------------------------------------- /oi/Headers.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #include 17 | 18 | namespace oi::detail::headers { 19 | 20 | // These externs are provided by our build system. See resources/CMakeLists.txt 21 | extern const std::string_view oi_IntrospectionResult_h; 22 | extern const std::string_view oi_IntrospectionResult_inl_h; 23 | extern const std::string_view oi_OITraceCode_cpp; 24 | extern const std::string_view oi_exporters_ParsedData_h; 25 | extern const std::string_view oi_exporters_inst_h; 26 | extern const std::string_view oi_result_Element_h; 27 | extern const std::string_view oi_types_dy_h; 28 | extern const std::string_view oi_types_st_h; 29 | 30 | } // namespace oi::detail::headers 31 | -------------------------------------------------------------------------------- /test/integration/std_tuple.toml: -------------------------------------------------------------------------------- 1 | includes = ["tuple"] 2 | definitions = ''' 3 | struct Foo { 4 | std::tuple t; 5 | }; 6 | struct Bar { 7 | std::optional f; 8 | }; 9 | ''' 10 | [cases] 11 | [cases.uint64_uint64] 12 | param_types = ["Bar&"] 13 | setup = ''' 14 | Foo f; 15 | f.t = std::make_tuple(1,2); 16 | Bar b; 17 | b.f = f; 18 | return b; 19 | ''' 20 | expect_json = ''' 21 | [ 22 | { 23 | "staticSize": 24, 24 | "dynamicSize": 0, 25 | "members": [ 26 | { 27 | "staticSize": 24, 28 | "dynamicSize": 0, 29 | "length": 1, 30 | "capacity": 1, 31 | "elementStaticSize": 16 32 | } 33 | ] 34 | } 35 | ] 36 | ''' 37 | expect_json_v2 = '''[ 38 | { "size":24, "staticSize":24, "exclusiveSize":0, "members":[ 39 | {"size":24, "staticSize":24, "exclusiveSize":8, "members":[ 40 | {"size":16, "staticSize":16, "exclusiveSize":0, "members": [ 41 | {"size":16, "staticSize":16, "exclusiveSize":0, "members": [ 42 | {"size":8, "staticSize":8, "exclusiveSize":8, "members":[]}, 43 | {"size":8, "staticSize":8, "exclusiveSize":8, "members":[]} 44 | ]} 45 | ]} 46 | ]} 47 | ]} 48 | ]''' 49 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to object-introspection 2 | We want to make contributing to this project as easy and transparent as 3 | possible. 4 | 5 | ## Pull Requests 6 | We actively welcome your pull requests. 7 | 8 | 1. Fork the repo and create your branch from `master`. 9 | 2. If you've added code that should be tested, add tests. 10 | 3. If you've changed APIs, update the documentation. 11 | 4. Ensure the test suite passes. 12 | 5. Make sure your code lints. 13 | 6. If you haven't already, complete the Contributor License Agreement ("CLA"). 14 | 15 | ## Contributor License Agreement ("CLA") 16 | In order to accept your pull request, we need you to submit a CLA. You only need 17 | to do this once to work on any of Facebook's open source projects. 18 | 19 | Complete your CLA here: 20 | 21 | ## Issues 22 | We use GitHub issues to track public bugs. Please ensure your description is 23 | clear and has sufficient instructions to be able to reproduce the issue. 24 | 25 | Facebook has a [bounty program](https://www.facebook.com/whitehat/) for the safe 26 | disclosure of security bugs. In those cases, please go through the process 27 | outlined on that page and do not file a public issue. 28 | 29 | ## License 30 | By contributing to object-introspection, you agree that your contributions will be licensed 31 | under the LICENSE file in the root directory of this source tree. -------------------------------------------------------------------------------- /types/pair_type.toml: -------------------------------------------------------------------------------- 1 | [info] 2 | type_name = "std::pair" 3 | ctype = "PAIR_TYPE" 4 | header = "utility" 5 | 6 | # Old: 7 | typeName = "std::pair<" 8 | ns = ["namespace std"] 9 | numTemplateParams = 2 10 | replaceTemplateParamIndex = [] 11 | 12 | [codegen] 13 | decl = """ 14 | template 15 | void getSizeType(const %1% &container, size_t& returnArg); 16 | """ 17 | 18 | func = """ 19 | template 20 | void getSizeType(const %1% &container, size_t& returnArg) 21 | { 22 | SAVE_SIZE(sizeof(%1%) - sizeof(P) - sizeof(Q)); 23 | 24 | getSizeType(container.first, returnArg); 25 | getSizeType(container.second, returnArg); 26 | } 27 | """ 28 | 29 | traversal_func = """ 30 | return OIInternal::getSizeType(ctx, 31 | container.second, 32 | returnArg.delegate([&ctx, &container](auto ret) { 33 | return OIInternal::getSizeType(ctx, container.first, ret); 34 | }) 35 | ); 36 | """ 37 | 38 | [[codegen.processor]] 39 | type = "types::st::Pair::type, typename TypeHandler::type>" 40 | func = """ 41 | static constexpr auto firstField = make_field("first"); 42 | static constexpr auto secondField = make_field("second"); 43 | 44 | el.exclusive_size = sizeof(std::pair) - sizeof(T0) - sizeof(T1); 45 | stack_ins(secondField); 46 | stack_ins(firstField); 47 | """ 48 | -------------------------------------------------------------------------------- /website/static/img/OISymbol.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /oi/type_graph/RemoveTopLevelPointer.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #pragma once 17 | 18 | #include 19 | #include 20 | 21 | #include "PassManager.h" 22 | #include "Types.h" 23 | #include "Visitor.h" 24 | 25 | namespace oi::detail::type_graph { 26 | 27 | /* 28 | * RemoveTopLevelPointer 29 | * 30 | * If the top type node is a pointer, remove it from the graph and instead have 31 | * the pointee type as the top-level node. 32 | */ 33 | class RemoveTopLevelPointer : public LazyVisitor { 34 | public: 35 | static Pass createPass(); 36 | 37 | void removeTopLevelPointers(std::vector>& types); 38 | void visit(Pointer& p) override; 39 | void visit(Reference& r) override; 40 | 41 | private: 42 | Type* topLevelType_ = nullptr; 43 | }; 44 | 45 | } // namespace oi::detail::type_graph 46 | -------------------------------------------------------------------------------- /test/integration/folly_shims.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | namespace folly { 6 | namespace detail { 7 | 8 | // The FOLLY_SAFE_DCHECK macro is peppered throughout the folly headers. When 9 | // building in release mode this macro does nothing, but in debug builds it 10 | // requires safe_assert_terminate() to be defined. To avoid building and 11 | // linking against folly, we define our own no-op version of this function here. 12 | template <> 13 | void safe_assert_terminate(safe_assert_arg const* /*arg*/, 14 | ...) noexcept { 15 | abort(); 16 | } 17 | 18 | template <> 19 | void safe_assert_terminate(safe_assert_arg const* /*arg*/, ...) noexcept { 20 | abort(); 21 | } 22 | 23 | void ScopeGuardImplBase::terminate() noexcept { 24 | abort(); 25 | } 26 | 27 | } // namespace detail 28 | 29 | namespace f14 { 30 | namespace detail { 31 | 32 | void F14LinkCheck::check() noexcept { 33 | } 34 | 35 | bool tlsPendingSafeInserts(std::ptrdiff_t /*delta*/) { 36 | /* Disable extra debugging re-hash by classifying all inserts as safe (return 37 | * true) */ 38 | return true; 39 | } 40 | 41 | std::size_t tlsMinstdRand(std::size_t /*n*/) { 42 | /* Disable insert order perturbation by always returning 0 */ 43 | return 0; 44 | } 45 | 46 | } // namespace detail 47 | } // namespace f14 48 | 49 | } // namespace folly 50 | -------------------------------------------------------------------------------- /oi/type_graph/EnforceCompatibility.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #pragma once 17 | 18 | #include 19 | 20 | #include "NodeTracker.h" 21 | #include "PassManager.h" 22 | #include "Types.h" 23 | #include "Visitor.h" 24 | 25 | namespace oi::detail::type_graph { 26 | 27 | /* 28 | * EnforceCompatibility 29 | * 30 | * Transforms the type graph so that CodeGen produces code which is compatible 31 | * with OICodeGen. 32 | */ 33 | class EnforceCompatibility : public RecursiveVisitor { 34 | public: 35 | static Pass createPass(); 36 | 37 | EnforceCompatibility(NodeTracker& tracker) : tracker_(tracker) { 38 | } 39 | 40 | using RecursiveVisitor::accept; 41 | 42 | void accept(Type& type) override; 43 | void visit(Class& c) override; 44 | 45 | private: 46 | NodeTracker& tracker_; 47 | }; 48 | 49 | } // namespace oi::detail::type_graph 50 | -------------------------------------------------------------------------------- /test/TypeGraphParser.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "oi/type_graph/Types.h" 7 | 8 | namespace oi::detail::type_graph { 9 | class TypeGraph; 10 | } // namespace oi::detail::type_graph 11 | 12 | using namespace oi::detail::type_graph; 13 | 14 | /* 15 | * TypeGraphParser 16 | * 17 | * Parses a textual type graph, as emitted by Printer. 18 | */ 19 | class TypeGraphParser { 20 | public: 21 | TypeGraphParser(TypeGraph& typeGraph) : typeGraph_(typeGraph) { 22 | } 23 | 24 | void parse(std::string_view input); 25 | 26 | private: 27 | TypeGraph& typeGraph_; 28 | std::unordered_map> nodesById_; 29 | 30 | Type& parseType(std::string_view& input, size_t rootIndent); 31 | template 32 | void parseParams(T& c, std::string_view& input, size_t rootIndent); 33 | void parseParents(Class& c, std::string_view& input, size_t rootIndent); 34 | void parseMembers(Class& c, std::string_view& input, size_t rootIndent); 35 | void parseFunctions(Class& c, std::string_view& input, size_t rootIndent); 36 | void parseChildren(Class& c, std::string_view& input, size_t rootIndent); 37 | void parseUnderlying(Container& c, 38 | std::string_view& input, 39 | size_t rootIndent); 40 | }; 41 | 42 | class TypeGraphParserError : public std::runtime_error { 43 | public: 44 | TypeGraphParserError(const std::string& msg) : std::runtime_error{msg} { 45 | } 46 | }; 47 | -------------------------------------------------------------------------------- /types/optional_type.toml: -------------------------------------------------------------------------------- 1 | [info] 2 | type_name = "std::optional" 3 | ctype = "OPTIONAL_TYPE" 4 | header = "optional" 5 | 6 | # Old: 7 | typeName = "std::optional<" 8 | ns = ["namespace std"] 9 | numTemplateParams = 1 10 | 11 | [codegen] 12 | decl = """ 13 | template 14 | void getSizeType(const %1% &container, size_t& returnArg); 15 | """ 16 | 17 | func = """ 18 | template 19 | void getSizeType(const %1%& container, size_t& returnArg) { 20 | if (container) { 21 | SAVE_SIZE(sizeof(%1%) - sizeof(T)); 22 | SAVE_DATA(true); 23 | getSizeType(*container, returnArg); 24 | } else { 25 | SAVE_SIZE(sizeof(%1%)); 26 | SAVE_DATA(false); 27 | } 28 | } 29 | """ 30 | 31 | traversal_func = """ 32 | if (container.has_value()) { 33 | return returnArg.template delegate<1>([&ctx, &container](auto ret) { 34 | return OIInternal::getSizeType(ctx, *container, ret); 35 | }); 36 | } else { 37 | return returnArg.template delegate<0>(std::identity()); 38 | } 39 | """ 40 | 41 | [[codegen.processor]] 42 | type = "types::st::Sum, typename TypeHandler::type>" 43 | func = """ 44 | static constexpr auto elementField = make_field("el"); 45 | 46 | auto sum = std::get(d.val); 47 | 48 | el.container_stats = result::Element::ContainerStats { 49 | .capacity = 1, 50 | .length = sum.index, 51 | }; 52 | 53 | if (sum.index == 1) { 54 | el.exclusive_size -= sizeof(T0); 55 | stack_ins(elementField); 56 | } 57 | """ 58 | -------------------------------------------------------------------------------- /oi/type_graph/Prune.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #pragma once 17 | 18 | #include 19 | #include 20 | 21 | #include "NodeTracker.h" 22 | #include "PassManager.h" 23 | #include "Types.h" 24 | #include "Visitor.h" 25 | 26 | namespace oi::detail::type_graph { 27 | 28 | /* 29 | * Prune 30 | * 31 | * Removes unnecessary information from a type graph and releases memory where 32 | * possible. 33 | */ 34 | class Prune : public RecursiveVisitor { 35 | public: 36 | static Pass createPass(); 37 | 38 | Prune(NodeTracker& tracker) : tracker_(tracker) { 39 | } 40 | 41 | using RecursiveVisitor::accept; 42 | using RecursiveVisitor::visit; 43 | 44 | void accept(Type& type) override; 45 | void visit(Class& c) override; 46 | void visit(Container& c) override; 47 | 48 | private: 49 | NodeTracker& tracker_; 50 | }; 51 | 52 | } // namespace oi::detail::type_graph 53 | -------------------------------------------------------------------------------- /oi/type_graph/AlignmentCalc.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #pragma once 17 | 18 | #include 19 | #include 20 | #include 21 | 22 | #include "PassManager.h" 23 | #include "Types.h" 24 | #include "Visitor.h" 25 | 26 | namespace oi::detail::type_graph { 27 | 28 | /* 29 | * AlignmentCalc 30 | * 31 | * Calculates alignment information for types TODO finish comment 32 | */ 33 | class AlignmentCalc final : public RecursiveVisitor { 34 | public: 35 | static Pass createPass(); 36 | 37 | void calculateAlignments( 38 | const std::vector>& types); 39 | 40 | using RecursiveVisitor::accept; 41 | 42 | void accept(Type& type) override; 43 | void visit(Class& c) override; 44 | void visit(Container& c) override; 45 | 46 | private: 47 | std::unordered_set visited_; 48 | }; 49 | 50 | } // namespace oi::detail::type_graph 51 | -------------------------------------------------------------------------------- /test/integration/inheritance_multiple.toml: -------------------------------------------------------------------------------- 1 | definitions = ''' 2 | struct Base_1 { 3 | int a; 4 | }; 5 | struct Base_2 { 6 | int b; 7 | }; 8 | struct Base_3 { 9 | int c; 10 | }; 11 | struct Derived_1: Base_2, Base_3 { 12 | int d; 13 | }; 14 | struct Base_4 { 15 | int e; 16 | }; 17 | struct Derived_2: Base_1, Derived_1, Base_4 { 18 | int f; 19 | }; 20 | ''' 21 | 22 | [cases] 23 | [cases.a] 24 | param_types = ["const Derived_2&"] 25 | setup = 'return {};' 26 | expect_json = '''[{ 27 | "staticSize":24, 28 | "dynamicSize":0, 29 | "members":[ 30 | {"name":"a", "staticSize":4, "dynamicSize":0}, 31 | {"name":"b", "staticSize":4, "dynamicSize":0}, 32 | {"name":"c", "staticSize":4, "dynamicSize":0}, 33 | {"name":"d", "staticSize":4, "dynamicSize":0}, 34 | {"name":"e", "staticSize":4, "dynamicSize":0}, 35 | {"name":"f", "staticSize":4, "dynamicSize":0} 36 | ]}]''' 37 | expect_json_v2 = '''[{ 38 | "staticSize":24, 39 | "exclusiveSize":0, 40 | "size":24, 41 | "members":[ 42 | {"name":"a", "staticSize":4, "exclusiveSize":4, "size":4}, 43 | {"name":"b", "staticSize":4, "exclusiveSize":4, "size":4}, 44 | {"name":"c", "staticSize":4, "exclusiveSize":4, "size":4}, 45 | {"name":"d", "staticSize":4, "exclusiveSize":4, "size":4}, 46 | {"name":"e", "staticSize":4, "exclusiveSize":4, "size":4}, 47 | {"name":"f", "staticSize":4, "exclusiveSize":4, "size":4} 48 | ]}]''' 49 | -------------------------------------------------------------------------------- /test/integration/references.toml: -------------------------------------------------------------------------------- 1 | includes = ["vector"] 2 | definitions = ''' 3 | struct IntRef { 4 | int &n; 5 | }; 6 | struct VectorRef { 7 | std::vector &vec; 8 | }; 9 | ''' 10 | [cases] 11 | [cases.int_ref] 12 | skip = "references are being treated as raw pointers" # https://github.com/facebookexperimental/object-introspection/issues/16 13 | param_types = ["const IntRef&"] 14 | setup = "return {{*new int(1)}};" 15 | expect_json = '''[{ 16 | "staticSize":8, 17 | "dynamicSize":4, 18 | "members":[ 19 | { 20 | "typeName": "int &", 21 | "name": "n", 22 | "staticSize": 8, 23 | "dynamicSize": 4 24 | } 25 | ]}]''' 26 | [cases.vector_ref] 27 | skip = "references are being treated as raw pointers" # https://github.com/facebookexperimental/object-introspection/issues/16 28 | param_types = ["const VectorRef&"] 29 | setup = "return {{*new std::vector{1,2,3}}};" 30 | expect_json = '''[{ 31 | "staticSize":8, 32 | "dynamicSize":36, 33 | "members":[ 34 | { 35 | "typeName": "std::vector &", 36 | "name": "vec", 37 | "staticSize": 8, 38 | "dynamicSize": 36, 39 | "members": [ 40 | { 41 | "typeName": "std::vector", 42 | "staticSize":24, 43 | "dynamicSize":12, 44 | "length":3, 45 | "capacity":3, 46 | "elementStaticSize":4 47 | } 48 | ] 49 | } 50 | ]}]''' 51 | -------------------------------------------------------------------------------- /types/array_type.toml: -------------------------------------------------------------------------------- 1 | [info] 2 | type_name = "std::array" 3 | ctype = "ARRAY_TYPE" 4 | header = "array" 5 | 6 | # Old: 7 | numTemplateParams = 1 8 | ns = ["namespace std"] 9 | typeName = "std::array<" 10 | 11 | [codegen] 12 | decl = """ 13 | template 14 | void getSizeType(const %1% &container, size_t& returnArg); 15 | """ 16 | 17 | func = """ 18 | template 19 | void getSizeType(const %1% &container, size_t& returnArg) 20 | { 21 | SAVE_DATA((uintptr_t)container.size()); 22 | SAVE_SIZE(sizeof(container)); 23 | 24 | for (auto & it: container) { 25 | // undo the static size that has already been added per-element 26 | SAVE_SIZE(-sizeof(it)); 27 | getSizeType(it, returnArg); 28 | } 29 | } 30 | """ 31 | 32 | traversal_func = """ 33 | auto tail = returnArg.write(container.size()); 34 | 35 | for (auto & it: container) { 36 | tail = tail.delegate([&ctx, &it](auto ret) { 37 | return TypeHandler::getSizeType(ctx, it, ret); 38 | }); 39 | } 40 | 41 | return tail.finish(); 42 | """ 43 | 44 | [[codegen.processor]] 45 | type = "types::st::List::type>" 46 | func = """ 47 | static constexpr auto childField = make_field("[]"); 48 | 49 | size_t size = std::get(d.val).length; 50 | el.exclusive_size = N0 == 0 ? 1 : 0; 51 | el.container_stats.emplace(result::Element::ContainerStats{ .capacity = size, .length = size }); 52 | for (size_t i = 0; i < size; i++) 53 | stack_ins(childField); 54 | """ 55 | -------------------------------------------------------------------------------- /types/folly_optional_type.toml: -------------------------------------------------------------------------------- 1 | [info] 2 | type_name = "folly::Optional" 3 | ctype = "FOLLY_OPTIONAL_TYPE" 4 | header = "folly/Optional.h" 5 | 6 | # Old: 7 | typeName = "folly::Optional<" 8 | ns = ["folly::Optional"] 9 | numTemplateParams = 1 10 | replaceTemplateParamIndex = [] 11 | 12 | [codegen] 13 | decl = """ 14 | template 15 | void getSizeType(const %1% &container, size_t& returnArg); 16 | """ 17 | 18 | func = """ 19 | template 20 | void getSizeType(const %1%& container, size_t& returnArg) { 21 | if (container) { 22 | SAVE_SIZE(sizeof(%1%) - sizeof(T)); 23 | SAVE_DATA((uintptr_t)(container.get_pointer())); 24 | 25 | getSizeType(*(container.get_pointer()), returnArg); 26 | } else { 27 | SAVE_SIZE(sizeof(%1%)); 28 | SAVE_DATA(0); 29 | } 30 | } 31 | """ 32 | 33 | traversal_func = """ 34 | if (container.has_value()) { 35 | return returnArg.template delegate<1>([&ctx, &container](auto ret) { 36 | return OIInternal::getSizeType(ctx, *container, ret); 37 | }); 38 | } else { 39 | return returnArg.template delegate<0>(std::identity()); 40 | } 41 | """ 42 | 43 | [[codegen.processor]] 44 | type = "types::st::Sum, typename TypeHandler::type>" 45 | func = """ 46 | static constexpr auto elementField = make_field("el"); 47 | 48 | auto sum = std::get(d.val); 49 | 50 | el.container_stats = result::Element::ContainerStats { 51 | .capacity = 1, 52 | .length = sum.index, 53 | }; 54 | 55 | if (sum.index == 1) { 56 | el.exclusive_size -= sizeof(T0); 57 | stack_ins(elementField); 58 | } 59 | """ 60 | -------------------------------------------------------------------------------- /oi/type_graph/Flattener.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #pragma once 17 | 18 | #include 19 | #include 20 | 21 | #include "NodeTracker.h" 22 | #include "PassManager.h" 23 | #include "Types.h" 24 | #include "Visitor.h" 25 | 26 | namespace oi::detail::type_graph { 27 | 28 | /* 29 | * Flattener 30 | * 31 | * Flattens classes by removing parents and adding their attributes directly 32 | * into derived classes. 33 | */ 34 | class Flattener : public RecursiveVisitor { 35 | public: 36 | static Pass createPass(); 37 | 38 | Flattener(NodeTracker& tracker) : tracker_(tracker) { 39 | } 40 | 41 | using RecursiveVisitor::accept; 42 | 43 | void accept(Type& type) override; 44 | void visit(Class& c) override; 45 | 46 | static const inline std::string ParentPrefix = "__oi_parent"; 47 | 48 | private: 49 | NodeTracker& tracker_; 50 | std::vector flattened_members_; 51 | std::vector offset_stack_; 52 | }; 53 | 54 | } // namespace oi::detail::type_graph 55 | -------------------------------------------------------------------------------- /cmake/StandardProjectSettings.cmake: -------------------------------------------------------------------------------- 1 | # Set a default build type if none was specified 2 | if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) 3 | message(STATUS "Setting build type to 'RelWithDebInfo' as none was specified.") 4 | set(CMAKE_BUILD_TYPE 5 | RelWithDebInfo 6 | CACHE STRING "Choose the type of build." FORCE) 7 | # Set the possible values of build type for cmake-gui, ccmake 8 | set_property( 9 | CACHE CMAKE_BUILD_TYPE 10 | PROPERTY STRINGS 11 | "Debug" 12 | "Release" 13 | "MinSizeRel" 14 | "RelWithDebInfo") 15 | endif() 16 | 17 | # Generate compile_commands.json to make it easier to work with clang based tools 18 | set(CMAKE_EXPORT_COMPILE_COMMANDS ON) 19 | # Include implicit directories in the compile commands file 20 | set(CMAKE_CXX_STANDARD_INCLUDE_DIRECTORIES ${CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES}) 21 | 22 | option(ENABLE_IPO "Enable Interprocedural Optimization, aka Link Time Optimization (LTO)" OFF) 23 | 24 | if(ENABLE_IPO) 25 | include(CheckIPOSupported) 26 | check_ipo_supported( 27 | RESULT 28 | result 29 | OUTPUT 30 | output) 31 | if(result) 32 | set(CMAKE_INTERPROCEDURAL_OPTIMIZATION ON) 33 | else() 34 | message(SEND_ERROR "IPO is not supported: ${output}") 35 | endif() 36 | endif() 37 | if(CMAKE_CXX_COMPILER_ID MATCHES ".*Clang") 38 | add_compile_options(-fcolor-diagnostics) 39 | elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") 40 | add_compile_options(-fdiagnostics-color=auto) 41 | else() 42 | message(STATUS "No colored compiler diagnostic set for '${CMAKE_CXX_COMPILER_ID}' compiler.") 43 | endif() 44 | 45 | -------------------------------------------------------------------------------- /resources/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(resources headers.cpp) 2 | 3 | target_include_directories(resources PRIVATE ../) 4 | 5 | function(embed_headers output) 6 | file(WRITE ${output} "#include \"oi/Headers.h\"\n\n") 7 | file(APPEND ${output} "namespace oi::detail::headers {\n") 8 | 9 | set(HEADERS 10 | ../include/oi/IntrospectionResult-inl.h 11 | ../include/oi/IntrospectionResult.h 12 | ../include/oi/exporters/ParsedData.h 13 | ../include/oi/exporters/inst.h 14 | ../include/oi/result/Element.h 15 | ../include/oi/types/dy.h 16 | ../include/oi/types/st.h 17 | ../oi/OITraceCode.cpp 18 | ) 19 | foreach(header ${HEADERS}) 20 | set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${header}) 21 | 22 | file(REAL_PATH ${header} header_path) 23 | file(REAL_PATH ${CMAKE_SOURCE_DIR}/include include_path) 24 | file(REAL_PATH ${CMAKE_SOURCE_DIR} base_path) 25 | 26 | cmake_path(IS_PREFIX include_path ${header_path} NORMALIZE in_include) 27 | if (${in_include}) 28 | file(RELATIVE_PATH header_rel_path ${include_path} ${header_path}) 29 | else() 30 | file(RELATIVE_PATH header_rel_path ${base_path} ${header_path}) 31 | endif() 32 | 33 | string(MAKE_C_IDENTIFIER ${header_rel_path} varname) 34 | get_filename_component(filename ${header} NAME) 35 | 36 | file(READ ${header} contents) 37 | file(APPEND ${output} "const std::string_view ${varname} = R\"CONTENTS(${contents})CONTENTS\";\n\n") 38 | endforeach() 39 | 40 | file(APPEND ${output} "} // namespace oi::detail::headers\n") 41 | endfunction() 42 | 43 | embed_headers(${CMAKE_BINARY_DIR}/resources/headers.cpp) 44 | -------------------------------------------------------------------------------- /oi/OILexer.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #pragma once 17 | 18 | /* It looks like the yyFlexLexerOnce method is not the correct way of doing 19 | this but it works for now. Fix it in the future. */ 20 | #if !defined(yyFlexLexerOnce) 21 | #include 22 | #endif 23 | 24 | /* #pragma once 25 | #include */ 26 | 27 | #include "OIParser.tab.hh" 28 | #include "location.hh" 29 | 30 | namespace oi::detail { 31 | 32 | class OIScanner : public yyFlexLexer { 33 | public: 34 | OIScanner(std::istream* in) : yyFlexLexer(in) {}; 35 | 36 | virtual ~OIScanner() {}; 37 | 38 | // get rid of override virtual function warning 39 | using FlexLexer::yylex; 40 | 41 | virtual int yylex(OIParser::semantic_type* const lval, 42 | OIParser::location_type* location); 43 | // YY_DECL defined in OILexer.l 44 | // Method body created by flex in OILexer.yy.cc 45 | 46 | private: 47 | /* yyval ptr */ 48 | OIParser::semantic_type* yylval = nullptr; 49 | }; 50 | 51 | } // namespace oi::detail 52 | -------------------------------------------------------------------------------- /oi/type_graph/RemoveTopLevelPointer.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #include "RemoveTopLevelPointer.h" 17 | 18 | #include "TypeGraph.h" 19 | 20 | namespace oi::detail::type_graph { 21 | 22 | Pass RemoveTopLevelPointer::createPass() { 23 | auto fn = [](TypeGraph& typeGraph, NodeTracker&) { 24 | RemoveTopLevelPointer pass; 25 | pass.removeTopLevelPointers(typeGraph.rootTypes()); 26 | }; 27 | 28 | return Pass("RemoveTopLevelPointer", fn); 29 | } 30 | 31 | void RemoveTopLevelPointer::removeTopLevelPointers( 32 | std::vector>& types) { 33 | for (size_t i = 0; i < types.size(); i++) { 34 | Type& type = types[i]; 35 | topLevelType_ = &type; 36 | type.accept(*this); 37 | types[i] = *topLevelType_; 38 | } 39 | } 40 | 41 | void RemoveTopLevelPointer::visit(Pointer& p) { 42 | topLevelType_ = &p.pointeeType(); 43 | } 44 | 45 | void RemoveTopLevelPointer::visit(Reference& r) { 46 | topLevelType_ = &r.pointeeType(); 47 | } 48 | 49 | } // namespace oi::detail::type_graph 50 | -------------------------------------------------------------------------------- /test/integration/std_multimap_custom_comparator.toml: -------------------------------------------------------------------------------- 1 | definitions = ''' 2 | struct CustomComparator { 3 | bool operator()(const int& left, const int& right) const { 4 | return left < right; 5 | } 6 | }; 7 | 8 | struct Foo { 9 | std::multimap m1; 10 | std::multimap m2; 11 | }; 12 | ''' 13 | includes = ["map"] 14 | 15 | [cases] 16 | [cases.a] 17 | param_types = ["const Foo&"] 18 | setup = ''' 19 | Foo foo; 20 | 21 | for (int i = 0; i < 3; i++) { 22 | foo.m1.insert(std::pair(i,i)); 23 | } 24 | 25 | for (int i = 0; i < 5; i++) { 26 | foo.m2.insert(std::pair(i,i)); 27 | } 28 | 29 | return {foo}; 30 | ''' 31 | expect_json = '''[{ 32 | "staticSize":96, 33 | "dynamicSize":76, 34 | "members":[ 35 | {"name":"m1", "staticSize":48, "dynamicSize":36, "length":3, "capacity":3, "elementStaticSize":12}, 36 | {"name":"m2", "staticSize":48, "dynamicSize":40, "length":5, "capacity":5, "elementStaticSize":8} 37 | ]}]''' 38 | expect_json_v2 = '''[{ 39 | "staticSize":96, 40 | "exclusiveSize":0, 41 | "size":440, 42 | "members":[ 43 | {"name":"m1", 44 | "staticSize":48, 45 | "exclusiveSize":48, 46 | "size":192, 47 | "length":3, 48 | "capacity":3, 49 | "members": [ 50 | {"name":"[]", "staticSize":48, "exclusiveSize":36, "size":48}, 51 | {}, 52 | {} 53 | ]}, 54 | {"name":"m2", "staticSize":48, "exclusiveSize":48, "size":248, "length":5, "capacity":5} 55 | ]}]''' 56 | -------------------------------------------------------------------------------- /oi/type_graph/PassManager.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #pragma once 17 | 18 | #include 19 | #include 20 | #include 21 | 22 | namespace oi::detail::type_graph { 23 | 24 | class NodeTrackerHolder; 25 | class NodeTracker; 26 | class TypeGraph; 27 | class Type; 28 | 29 | /* 30 | * Pass 31 | * 32 | * TODO 33 | */ 34 | class Pass { 35 | using PassFn = 36 | std::function; 37 | 38 | public: 39 | Pass(std::string name, PassFn fn) : name_(std::move(name)), fn_(fn) { 40 | } 41 | void run(TypeGraph& typeGraph, NodeTrackerHolder tracker); 42 | std::string& name() { 43 | return name_; 44 | }; 45 | 46 | private: 47 | std::string name_; 48 | PassFn fn_; 49 | }; 50 | 51 | /* 52 | * PassManager 53 | * 54 | * TODO 55 | */ 56 | class PassManager { 57 | public: 58 | void addPass(Pass p); 59 | void run(TypeGraph& typeGraph); 60 | 61 | private: 62 | std::vector passes_; 63 | }; 64 | 65 | } // namespace oi::detail::type_graph 66 | -------------------------------------------------------------------------------- /test/integration/sorted_vector_set.toml: -------------------------------------------------------------------------------- 1 | includes = ["folly/sorted_vector_types.h"] 2 | definitions = ''' 3 | using folly::sorted_vector_set; 4 | ''' 5 | 6 | [cases] 7 | [cases.no_ints] 8 | param_types = ["const sorted_vector_set&"] 9 | setup = "return {};" 10 | expect_json = '''[{ 11 | "staticSize":24, 12 | "dynamicSize":0, 13 | "length":0, 14 | "capacity":0, 15 | "elementStaticSize":4, 16 | "members":[{ 17 | "staticSize": 24, 18 | "dynamicSize": 0, 19 | "length": 0, 20 | "capacity": 0, 21 | "elementStaticSize": 4 22 | }]}]''' 23 | expect_json_v2 = '''[{ 24 | "staticSize": 24, 25 | "exclusiveSize": 24, 26 | "size": 24, 27 | "length": 0, 28 | "capacity": 0 29 | }]''' 30 | 31 | [cases.some_ints] 32 | param_types = ["const sorted_vector_set&"] 33 | setup = ''' 34 | sorted_vector_set is; 35 | is.insert(1); 36 | is.insert(3); 37 | is.insert(2); 38 | is.insert(3); 39 | return is; 40 | ''' 41 | expect_json = '''[{ 42 | "staticSize": 24, 43 | "dynamicSize": 16, 44 | "exclusiveSize": 0, 45 | "length":3, 46 | "capacity":4, 47 | "elementStaticSize":4, 48 | "members": [{ 49 | "staticSize": 24, 50 | "dynamicSize": 16, 51 | "exclusiveSize": 40, 52 | "length": 3, 53 | "capacity": 4, 54 | "elementStaticSize": 4 55 | }]}] 56 | ''' 57 | expect_json_v2 = '''[{ 58 | "staticSize": 24, 59 | "exclusiveSize": 28, 60 | "size": 40, 61 | "length": 3, 62 | "capacity": 4 63 | }]''' 64 | -------------------------------------------------------------------------------- /test/test_drgn_parser.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "oi/type_graph/DrgnParser.h" 6 | 7 | namespace oi::detail { 8 | class SymbolService; 9 | } 10 | namespace oi::detail::type_graph { 11 | class TypeGraph; 12 | } 13 | struct drgn_type; 14 | 15 | using namespace oi::detail; 16 | 17 | class DrgnParserTest : public ::testing::Test { 18 | protected: 19 | static void SetUpTestSuite(); 20 | static void TearDownTestSuite(); 21 | 22 | static type_graph::DrgnParser getDrgnParser( 23 | type_graph::TypeGraph& typeGraph, type_graph::DrgnParserOptions options); 24 | drgn_type* getDrgnRoot(std::string_view function); 25 | 26 | virtual std::string run(std::string_view function, 27 | type_graph::DrgnParserOptions options); 28 | void test(std::string_view function, 29 | std::string_view expected, 30 | type_graph::DrgnParserOptions options); 31 | void test(std::string_view function, std::string_view expected); 32 | void testGlob(std::string_view function, 33 | std::string_view expected, 34 | type_graph::DrgnParserOptions options = {}); 35 | void testMultiCompiler(std::string_view function, 36 | std::string_view expectedClang, 37 | std::string_view expectedGcc, 38 | type_graph::DrgnParserOptions options = {}); 39 | void testMultiCompilerGlob(std::string_view function, 40 | std::string_view expectedClang, 41 | std::string_view expectedGcc, 42 | type_graph::DrgnParserOptions options = {}); 43 | 44 | static SymbolService* symbols_; 45 | }; 46 | -------------------------------------------------------------------------------- /include/oi/result/Element.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #ifndef INCLUDED_OI_RESULT_ELEMENT_H 17 | #define INCLUDED_OI_RESULT_ELEMENT_H 1 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | namespace oi::result { 27 | 28 | struct Element { 29 | struct ContainerStats { 30 | size_t capacity; 31 | size_t length; 32 | }; 33 | struct IsSetStats { 34 | bool is_set; 35 | }; 36 | struct Pointer { 37 | uintptr_t p; 38 | }; 39 | struct Scalar { 40 | uint64_t n; 41 | }; 42 | 43 | std::string_view name; 44 | std::span type_path; 45 | std::span type_names; 46 | size_t static_size; 47 | size_t exclusive_size; 48 | 49 | std::optional pointer; 50 | std::variant data = { 51 | std::nullopt}; 52 | std::optional container_stats; 53 | std::optional is_set_stats; 54 | bool is_primitive; 55 | }; 56 | 57 | } // namespace oi::result 58 | 59 | #endif 60 | -------------------------------------------------------------------------------- /oi/type_graph/Prune.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #include "Prune.h" 17 | 18 | #include "TypeGraph.h" 19 | #include "TypeIdentifier.h" 20 | 21 | namespace oi::detail::type_graph { 22 | 23 | Pass Prune::createPass() { 24 | auto fn = [](TypeGraph& typeGraph, NodeTracker& tracker) { 25 | Prune pass{tracker}; 26 | for (auto& type : typeGraph.rootTypes()) { 27 | pass.accept(type); 28 | } 29 | }; 30 | 31 | return Pass("Prune", fn); 32 | } 33 | 34 | void Prune::accept(Type& type) { 35 | if (tracker_.visit(type)) 36 | return; 37 | 38 | type.accept(*this); 39 | } 40 | 41 | void Prune::visit(Class& c) { 42 | RecursiveVisitor::visit(c); 43 | 44 | c.templateParams.clear(); 45 | c.parents.clear(); 46 | c.functions.clear(); 47 | 48 | // Should we bother with this shrinking? It saves memory but costs CPU 49 | c.templateParams.shrink_to_fit(); 50 | c.parents.shrink_to_fit(); 51 | c.functions.shrink_to_fit(); 52 | } 53 | 54 | void Prune::visit(Container& c) { 55 | RecursiveVisitor::visit(c); 56 | 57 | c.setUnderlying(nullptr); 58 | } 59 | 60 | } // namespace oi::detail::type_graph 61 | -------------------------------------------------------------------------------- /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | name: Deploy to GitHub Pages 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | # Review gh actions docs if you want to further define triggers, paths, etc 8 | # https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#on 9 | 10 | jobs: 11 | deploy: 12 | defaults: 13 | run: 14 | working-directory: website 15 | name: Deploy to GitHub Pages 16 | runs-on: ubuntu-latest 17 | steps: 18 | - uses: actions/checkout@v3 19 | - uses: actions/setup-node@v3 20 | with: 21 | node-version: 18 22 | cache: yarn 23 | cache-dependency-path: ./website/yarn.lock 24 | 25 | - name: Install dependencies 26 | run: yarn install --frozen-lockfile 27 | - name: Build website 28 | run: yarn build 29 | 30 | # Popular action to deploy to GitHub Pages: 31 | # Docs: https://github.com/peaceiris/actions-gh-pages#%EF%B8%8F-docusaurus 32 | - name: Deploy to GitHub Pages 33 | uses: peaceiris/actions-gh-pages@v3 34 | with: 35 | github_token: ${{ secrets.GITHUB_TOKEN }} 36 | # Build output to publish to the `gh-pages` branch: 37 | publish_dir: ./website/build 38 | # The following lines assign commit authorship to the official 39 | # GH-Actions bot for deploys to `gh-pages` branch: 40 | # https://github.com/actions/checkout/issues/13#issuecomment-724415212 41 | # The GH actions bot is used by default if you didn't specify the two fields. 42 | # You can swap them out with your own user credentials. 43 | user_name: github-actions[bot] 44 | user_email: 41898282+github-actions[bot]@users.noreply.github.com 45 | -------------------------------------------------------------------------------- /.clang-tidy: -------------------------------------------------------------------------------- 1 | Checks: > 2 | -*, 3 | bugprone*, 4 | readability*, 5 | cppcoreguidelines*, 6 | clang-analyzer*, 7 | performance*, 8 | -cppcoreguidelines-pro-type-union-access, 9 | -cppcoreguidelines-pro-type-vararg, 10 | -cppcoreguidelines-avoid-non-const-global-variables, 11 | -cppcoreguidelines-pro-bounds-pointer-arithmetic, 12 | -readability-function-cognitive-complexity 13 | 14 | CheckOptions: 15 | - { key: readability-identifier-naming.NamespaceCase, value: lower_case } 16 | - { key: readability-identifier-naming.ClassCase, value: CamelCase } 17 | - { key: readability-identifier-naming.StructCase, value: CamelCase } 18 | - { key: readability-identifier-naming.TemplateParameterCase, value: CamelCase } 19 | - { key: readability-identifier-naming.FunctionCase, value: camelBack } 20 | - { key: readability-identifier-naming.VariableCase, value: camelBack } 21 | - { key: readability-identifier-naming.ClassMemberCase, value: camelBack } 22 | - { key: readability-identifier-naming.ClassMemberCase, value: camelBack } 23 | - { key: readability-identifier-naming.PrivateMemberCase, value: camelBack } 24 | - { key: readability-identifier-naming.ProtectedMemberCase, value: camelBack } 25 | - { key: readability-identifier-naming.EnumConstantCase, value: camelBack } 26 | - { key: readability-identifier-naming.ConstexprVariableCase, value: camelBack } 27 | - { key: readability-identifier-naming.GlobalConstantCase, value: camelBack } 28 | - { key: readability-identifier-naming.MemberConstantCase, value: CamelCase } 29 | - { key: readability-identifier-naming.StaticConstantCase, value: camelBack } 30 | - { key: readability-implicit-bool-conversion.AllowIntegerConditions, value: 1 } 31 | - { key: readability-implicit-bool-conversion.AllowPointerConditions, value: 1 } 32 | -------------------------------------------------------------------------------- /oi/type_graph/IdentifyContainers.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #pragma once 17 | 18 | #include 19 | #include 20 | #include 21 | 22 | #include "NodeTracker.h" 23 | #include "PassManager.h" 24 | #include "Types.h" 25 | #include "Visitor.h" 26 | #include "oi/ContainerInfo.h" 27 | 28 | namespace oi::detail::type_graph { 29 | 30 | class TypeGraph; 31 | 32 | /* 33 | * IdentifyContainers 34 | * 35 | * Walks a flattened type graph and replaces type nodes based on container 36 | * definition TOML files. 37 | */ 38 | class IdentifyContainers : public RecursiveMutator { 39 | public: 40 | static Pass createPass( 41 | const std::vector>& containers); 42 | 43 | IdentifyContainers( 44 | TypeGraph& typeGraph, 45 | const std::vector>& containers); 46 | 47 | using RecursiveMutator::mutate; 48 | 49 | Type& mutate(Type& type) override; 50 | Type& visit(Class& c) override; 51 | Type& visit(Container& c) override; 52 | 53 | private: 54 | ResultTracker tracker_; 55 | TypeGraph& typeGraph_; 56 | const std::vector>& containers_; 57 | }; 58 | 59 | } // namespace oi::detail::type_graph 60 | -------------------------------------------------------------------------------- /types/string_type.toml: -------------------------------------------------------------------------------- 1 | [info] 2 | type_name = "std::basic_string" 3 | stub_template_params = [2] 4 | ctype = "STRING_TYPE" 5 | header = "string" 6 | 7 | # Old: 8 | typeName = "std::basic_string<" 9 | ns = ["namespace std"] 10 | numTemplateParams = 1 11 | replaceTemplateParamIndex = [] 12 | 13 | [codegen] 14 | decl = """ 15 | template 16 | void getSizeType(const %1% &container, size_t& returnArg); 17 | """ 18 | 19 | func = """ 20 | template 21 | void getSizeType(const %1% &container, size_t& returnArg) 22 | { 23 | SAVE_SIZE(sizeof(%1%)); 24 | 25 | SAVE_DATA((uintptr_t)container.capacity()); 26 | SAVE_DATA((uintptr_t)container.size()); 27 | 28 | // Test for small string optimisation - whether the underlying string is 29 | // contained within the string object. 30 | SAVE_SIZE( 31 | isStorageInline(container) ? 0 : (container.capacity() * sizeof(T)) 32 | ); 33 | } 34 | """ 35 | 36 | traversal_func = """ 37 | bool sso = isStorageInline(container); 38 | 39 | return returnArg.write(container.capacity()) 40 | .write(sso) 41 | .write(container.size()); 42 | """ 43 | 44 | [[codegen.processor]] 45 | type = "types::st::VarInt" 46 | func = """ 47 | uint64_t capacity = std::get(d.val).value; 48 | el.container_stats.emplace(result::Element::ContainerStats { .capacity = capacity }); 49 | """ 50 | 51 | [[codegen.processor]] 52 | type = "types::st::VarInt" 53 | func = """ 54 | bool sso = std::get(d.val).value; 55 | if (!sso) 56 | el.exclusive_size += el.container_stats->capacity * sizeof(T0); 57 | """ 58 | 59 | [[codegen.processor]] 60 | type = "types::st::VarInt" 61 | func = """ 62 | el.container_stats->length = std::get(d.val).value; 63 | """ 64 | -------------------------------------------------------------------------------- /oi/type_graph/RemoveMembers.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #pragma once 17 | 18 | #include 19 | 20 | #include "PassManager.h" 21 | #include "Types.h" 22 | #include "Visitor.h" 23 | 24 | namespace oi::detail::type_graph { 25 | 26 | /* 27 | * RemoveMembers 28 | * 29 | * Removes members as requested by the [[codegen.ignore]] section in the OI 30 | * config. 31 | * 32 | * Removes members from unions as we have no way of telling which one is active. 33 | */ 34 | class RemoveMembers : public RecursiveVisitor { 35 | public: 36 | static Pass createPass( 37 | const std::vector>& membersToIgnore); 38 | 39 | RemoveMembers( 40 | const std::vector>& membersToIgnore) 41 | : membersToIgnore_(membersToIgnore) { 42 | } 43 | 44 | using RecursiveVisitor::accept; 45 | 46 | void accept(Type& type) override; 47 | void visit(Class& c) override; 48 | 49 | private: 50 | bool ignoreMember(std::string_view typeName, 51 | const std::string& memberName) const; 52 | 53 | std::unordered_set visited_; 54 | const std::vector>& membersToIgnore_; 55 | }; 56 | 57 | } // namespace oi::detail::type_graph 58 | -------------------------------------------------------------------------------- /types/ref_wrapper_type.toml: -------------------------------------------------------------------------------- 1 | [info] 2 | type_name = "std::reference_wrapper" 3 | ctype = "REF_WRAPPER_TYPE" 4 | header = "functional" 5 | 6 | # Old: 7 | typeName = "std::reference_wrapper<" 8 | ns = ["namespace std"] 9 | numTemplateParams = 1 10 | replaceTemplateParamIndex = [] 11 | 12 | [codegen] 13 | decl = """ 14 | template 15 | void getSizeType(const %1% &ref, size_t& returnArg); 16 | """ 17 | 18 | func = """ 19 | template 20 | void getSizeType(const %1% &ref, size_t& returnArg) 21 | { 22 | SAVE_SIZE(sizeof(%1%)); 23 | SAVE_DATA((uintptr_t)&(ref.get())); 24 | if (ctx.pointers.add((uintptr_t)&ref.get())) { 25 | SAVE_DATA(1); 26 | getSizeType(ref.get(), returnArg); 27 | } else { 28 | SAVE_DATA(0); 29 | } 30 | } 31 | """ 32 | 33 | traversal_func = """ 34 | auto tail = returnArg.write((uintptr_t)&(container.get())); 35 | 36 | if (ctx.pointers.add((uintptr_t)&container.get())) { 37 | return tail.template delegate<1>([&ctx, &container](auto ret) { 38 | return OIInternal::getSizeType(ctx, container.get(), ret); 39 | }); 40 | } else { 41 | return tail.template delegate<0>(std::identity()); 42 | } 43 | """ 44 | 45 | [[codegen.processor]] 46 | type = "types::st::VarInt" 47 | func = """ 48 | el.pointer = std::get(d.val).value; 49 | """ 50 | 51 | [[codegen.processor]] 52 | type = """ 53 | types::st::Sum, 55 | typename TypeHandler::type> 56 | """ 57 | func = """ 58 | auto sum = std::get(d.val); 59 | el.container_stats.emplace(result::Element::ContainerStats { 60 | .capacity = 1, 61 | .length = sum.index, // 0 if in a cycle, 1 otherwise 62 | }); 63 | 64 | if constexpr (oi_is_complete) { 65 | if (sum.index == 1) { 66 | static constexpr auto element = make_field("ref_val"); 67 | stack_ins(element); 68 | } 69 | } 70 | """ 71 | -------------------------------------------------------------------------------- /oi/TypeHierarchy.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #pragma once 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #include "oi/ContainerTypeEnum.h" 24 | 25 | extern "C" { 26 | #include 27 | } 28 | 29 | struct RootInfo { 30 | std::string varName; 31 | struct drgn_qualified_type type; 32 | }; 33 | 34 | struct ClassMember { 35 | std::string typeName; 36 | std::string varName; 37 | }; 38 | 39 | struct DrgnClassMemberInfo { 40 | struct drgn_type* type; 41 | std::string member_name; 42 | uint64_t bit_offset; 43 | uint64_t bit_field_size; 44 | bool isStubbed; 45 | }; 46 | 47 | struct TypeHierarchy { 48 | std::map> classMembersMap; 49 | std::map< 50 | struct drgn_type*, 51 | std::pair>> 52 | containerTypeMap; 53 | std::map typedefMap; 54 | std::map sizeMap; 55 | std::set knownDummyTypeList; 56 | std::map pointerToTypeMap; 57 | std::set thriftIssetStructTypes; 58 | std::map> descendantClasses; 59 | }; 60 | -------------------------------------------------------------------------------- /test/integration/namespaces.toml: -------------------------------------------------------------------------------- 1 | # This test checks that we can correctly distinguish between types with the same 2 | # name in different namespaces. 3 | includes = ["queue", "stack"] 4 | definitions = ''' 5 | namespace nsA { 6 | struct Foo { 7 | int x; 8 | }; 9 | } // namespace nsA 10 | namespace nsB { 11 | struct Foo { 12 | int y; 13 | int z; 14 | }; 15 | } // namespace nsB 16 | ''' 17 | [cases] 18 | [cases.queue] 19 | oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/308 20 | param_types = ["const std::queue>&"] 21 | setup = "return std::queue>({{ns_namespaces::nsA::Foo(), ns_namespaces::nsB::Foo()}});" 22 | expect_json = '''[{ 23 | "typeName": "std::queue, std::deque, std::allocator>>>", 24 | "staticSize": 80, "dynamicSize": 12, "length": 1, "capacity": 1, "elementStaticSize": 12 25 | }]''' 26 | [cases.stack] 27 | oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/305 28 | param_types = ["const std::stack>&"] 29 | setup = "return std::stack>({{ns_namespaces::nsA::Foo(), ns_namespaces::nsB::Foo()}});" 30 | expect_json = '''[{ 31 | "typeName": "std::stack, std::deque, std::allocator>>>", 32 | "staticSize": 80, "dynamicSize": 12, "length": 1, "capacity": 1, "elementStaticSize": 12 33 | }]''' 34 | -------------------------------------------------------------------------------- /test/integration/std_reference_wrapper.toml: -------------------------------------------------------------------------------- 1 | includes = ["functional"] 2 | [cases] 3 | [cases.int] 4 | param_types = ["const std::reference_wrapper&"] 5 | setup = "return std::ref(*new int(1));" 6 | expect_json = '[{"staticSize":8, "dynamicSize":4, "length":1, "capacity":1, "elementStaticSize":4}]' 7 | expect_json_v2 = '''[{ 8 | "size":12, "staticSize":8, "exclusiveSize":8, "length":1, "capacity":1, "members":[ 9 | {"size":4, "staticSize":4, "exclusiveSize":4, "members":[]} 10 | ]}]''' 11 | [cases.vector] 12 | param_types = ["const std::vector>&"] 13 | setup = "return {{std::ref(*new int(1)), std::ref(*new int(2)), std::ref(*new int(3))}};" 14 | expect_json = '''[{ 15 | "staticSize":24, 16 | "dynamicSize":36, 17 | "length":3, 18 | "capacity":3, 19 | "elementStaticSize":8, 20 | "members":[ 21 | {"staticSize":8, "dynamicSize":4, "length":1, "capacity":1, "elementStaticSize":4}, 22 | {"staticSize":8, "dynamicSize":4, "length":1, "capacity":1, "elementStaticSize":4}, 23 | {"staticSize":8, "dynamicSize":4, "length":1, "capacity":1, "elementStaticSize":4} 24 | ]}]''' 25 | expect_json_v2 = '''[{ 26 | "size":60, 27 | "staticSize":24, 28 | "exclusiveSize":24, 29 | "length":3, 30 | "capacity":3, 31 | "members":[ 32 | {"size":12, "staticSize":8, "exclusiveSize":8, "length":1, "capacity":1, "members":[ 33 | {"size":4, "staticSize":4, "exclusiveSize":4, "members":[]} 34 | ]}, 35 | {"size":12, "staticSize":8, "exclusiveSize":8, "length":1, "capacity":1, "members":[ 36 | {"size":4, "staticSize":4, "exclusiveSize":4, "members":[]} 37 | ]}, 38 | {"size":12, "staticSize":8, "exclusiveSize":8, "length":1, "capacity":1, "members":[ 39 | {"size":4, "staticSize":4, "exclusiveSize":4, "members":[]} 40 | ]} 41 | ] 42 | }]''' 43 | -------------------------------------------------------------------------------- /test/integration/typedefed_parent.toml: -------------------------------------------------------------------------------- 1 | definitions = ''' 2 | struct Foo { 3 | std::vector a = {10, 20, 30}; 4 | }; 5 | typedef Foo Parent; 6 | struct Bar: Parent { 7 | std::vector b = {1, 2, 3, 4, 5}; 8 | }; 9 | 10 | typedef Parent Parent_2; 11 | struct Bar_2: Parent_2 { 12 | std::vector c = {1, 2, 3, 4, 5}; 13 | }; 14 | ''' 15 | 16 | [cases] 17 | [cases.simple_typedef_parent] 18 | param_types = ["const Bar&"] 19 | setup = ''' 20 | Bar bar; 21 | return {bar}; 22 | ''' 23 | expect_json = '''[{ 24 | "staticSize":48, 25 | "dynamicSize":32, 26 | "members":[ 27 | {"name":"a", "staticSize":24, "dynamicSize":12, "length":3, "capacity":3}, 28 | {"name":"b", "staticSize":24, "dynamicSize":20, "length":5, "capacity":5} 29 | ]}]''' 30 | expect_json_v2 = '''[{ 31 | "staticSize":48, 32 | "exclusiveSize":0, 33 | "size":80, 34 | "members":[ 35 | {"name":"a", "staticSize":24, "exclusiveSize":24, "length":3, "capacity":3, "size":36}, 36 | {"name":"b", "staticSize":24, "exclusiveSize":24, "length":5, "capacity":5, "size":44} 37 | ]}]''' 38 | [cases.multilevel_typedef_parent] 39 | param_types = ["const Bar_2&"] 40 | setup = ''' 41 | Bar_2 bar; 42 | return {bar}; 43 | ''' 44 | expect_json = '''[{ 45 | "staticSize":48, 46 | "dynamicSize":32, 47 | "members":[ 48 | {"name":"a", "staticSize":24, "dynamicSize":12, "length":3, "capacity":3}, 49 | {"name":"c", "staticSize":24, "dynamicSize":20, "length":5, "capacity":5} 50 | ]}]''' 51 | expect_json_v2 = '''[{ 52 | "staticSize":48, 53 | "exclusiveSize":0, 54 | "size":80, 55 | "members":[ 56 | {"name":"a", "staticSize":24, "exclusiveSize":24, "length":3, "capacity":3, "size":36}, 57 | {"name":"c", "staticSize":24, "exclusiveSize":24, "length":5, "capacity":5, "size":44} 58 | ]}]''' 59 | -------------------------------------------------------------------------------- /oi/arch/x86_64.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #ifdef __x86_64__ 17 | 18 | extern "C" { 19 | #include 20 | } 21 | 22 | #include "Arch.h" 23 | 24 | namespace oi::detail::arch { 25 | 26 | std::optional getReturnValueAddress(const user_regs_struct& regs) { 27 | return regs.rax; 28 | } 29 | 30 | void setProgramCounter(user_regs_struct& regs, uintptr_t pc) { 31 | regs.rip = pc; 32 | } 33 | 34 | std::optional naiveReadArgument(const user_regs_struct& regs, 35 | uint8_t idx) { 36 | /* 37 | * This function is based on the information available at 38 | * http://6.s081.scripts.mit.edu/sp18/x86-64-architecture-guide.html. I have 39 | * no idea under which conditions these registers are selected. We rely on the 40 | * fact that OID will safely exit if incorrect, potentially producing some 41 | * incorrect data but otherwise leaving the process unharmed. 42 | */ 43 | switch (idx) { 44 | case 0: 45 | return regs.rdi; 46 | case 1: 47 | return regs.rsi; 48 | case 2: 49 | return regs.rdx; 50 | case 3: 51 | return regs.rcx; 52 | case 4: 53 | return regs.r8; 54 | case 5: 55 | return regs.r9; 56 | default: 57 | return std::nullopt; 58 | } 59 | } 60 | 61 | } // namespace oi::detail::arch 62 | 63 | #endif 64 | -------------------------------------------------------------------------------- /oi/type_graph/TypeIdentifier.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #pragma once 17 | 18 | #include 19 | #include 20 | #include 21 | 22 | #include "NodeTracker.h" 23 | #include "PassManager.h" 24 | #include "Types.h" 25 | #include "Visitor.h" 26 | #include "oi/ContainerInfo.h" 27 | 28 | namespace oi::detail::type_graph { 29 | 30 | class TypeGraph; 31 | 32 | /* 33 | * TODO Pass Name 34 | * 35 | * TODO description 36 | */ 37 | class TypeIdentifier : public RecursiveVisitor { 38 | public: 39 | static Pass createPass(const std::vector& passThroughTypes); 40 | static bool isAllocator(Type& t); 41 | 42 | TypeIdentifier(NodeTracker& tracker, 43 | TypeGraph& typeGraph, 44 | const std::vector& passThroughTypes) 45 | : tracker_(tracker), 46 | typeGraph_(typeGraph), 47 | passThroughTypes_(passThroughTypes) { 48 | } 49 | 50 | using RecursiveVisitor::accept; 51 | 52 | void accept(Type& type) override; 53 | void visit(Container& c) override; 54 | 55 | private: 56 | NodeTracker& tracker_; 57 | TypeGraph& typeGraph_; 58 | const std::vector& passThroughTypes_; 59 | 60 | std::unordered_map> 61 | passThroughTypeDummys_; 62 | }; 63 | 64 | } // namespace oi::detail::type_graph 65 | -------------------------------------------------------------------------------- /oi/type_graph/AddPadding.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #pragma once 17 | 18 | #include 19 | #include 20 | 21 | #include "PassManager.h" 22 | #include "Types.h" 23 | #include "Visitor.h" 24 | 25 | namespace oi::detail::type_graph { 26 | 27 | class TypeGraph; 28 | 29 | /* 30 | * AddPadding 31 | * 32 | * Adds members to classes to represent padding. This is necessary until we have 33 | * complete alignment information from DWARF, otherwise our classes may be 34 | * undersized. 35 | */ 36 | class AddPadding final : public RecursiveVisitor { 37 | public: 38 | static Pass createPass(); 39 | 40 | explicit AddPadding(TypeGraph& typeGraph) : typeGraph_(typeGraph) { 41 | } 42 | 43 | using RecursiveVisitor::accept; 44 | 45 | void accept(Type& type) override; 46 | void visit(Class& c) override; 47 | 48 | static const inline std::string MemberPrefix = "__oi_padding"; 49 | 50 | private: 51 | std::unordered_set visited_; 52 | TypeGraph& typeGraph_; 53 | 54 | void addPadding(const Member& prevMember, 55 | uint64_t paddingEndBits, 56 | std::vector& paddedMembers); 57 | void addPadding(uint64_t paddingStartBits, 58 | uint64_t paddingEndBits, 59 | std::vector& paddedMembers); 60 | }; 61 | 62 | } // namespace oi::detail::type_graph 63 | -------------------------------------------------------------------------------- /oi/type_graph/NameGen.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #pragma once 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #include "PassManager.h" 24 | #include "Types.h" 25 | #include "Visitor.h" 26 | 27 | namespace oi::detail::type_graph { 28 | 29 | // TODO make all final 30 | /* 31 | * NameGen 32 | * 33 | * Generates unique names for all types in a type graph. 34 | */ 35 | class NameGen final : public RecursiveVisitor { 36 | public: 37 | static Pass createPass(); 38 | 39 | void generateNames(const std::vector>& types); 40 | 41 | using RecursiveVisitor::accept; 42 | 43 | void visit(Class& c) override; 44 | void visit(Container& c) override; 45 | void visit(Enum& e) override; 46 | void visit(Array& a) override; 47 | void visit(Typedef& td) override; 48 | void visit(Pointer& p) override; 49 | void visit(Reference& r) override; 50 | void visit(DummyAllocator& d) override; 51 | void visit(CaptureKeys& d) override; 52 | void visit(Incomplete& i) override; 53 | 54 | static const inline std::string AnonPrefix = "__oi_anon"; 55 | 56 | private: 57 | void accept(Type& type) override; 58 | void deduplicate(std::string& name); 59 | 60 | std::unordered_set visited_; 61 | int n = 0; 62 | }; 63 | 64 | } // namespace oi::detail::type_graph 65 | -------------------------------------------------------------------------------- /.github/workflows/tests_failing_under_nix.txt: -------------------------------------------------------------------------------- 1 | AddChildrenTest.InheritancePolymorphic 2 | DrgnParserTest.ClassTemplateInt 3 | DrgnParserTest.ClassTemplateTwo 4 | DrgnParserTest.ClassTemplateValue 5 | DrgnParserTest.Container 6 | DrgnParserTest.TemplateEnumValue 7 | DrgnParserTest.TemplateEnumValueGaps 8 | DrgnParserTest.TemplateEnumValueNegative 9 | DrgnParserTest.Typedef 10 | DrgnParserTest.Using 11 | OidIntegration.inheritance_polymorphic_b_as_a 12 | OidIntegration.inheritance_polymorphic_c_as_a 13 | OidIntegration.inheritance_polymorphic_c_as_b 14 | OidIntegration.inheritance_polymorphic_diamond_child_as_middle1 15 | OidIntegration.inheritance_polymorphic_diamond_child_as_middle1_root 16 | OidIntegration.inheritance_polymorphic_diamond_child_as_middle2 17 | OidIntegration.inheritance_polymorphic_diamond_child_as_middle2_root 18 | OidIntegration.inheritance_polymorphic_diamond_middle1_as_root 19 | OidIntegration.inheritance_polymorphic_diamond_middle2_as_root 20 | OidIntegration.inheritance_polymorphic_non_dynamic_base_b_as_b 21 | OidIntegration.inheritance_polymorphic_non_dynamic_base_b_no_polymorphic 22 | OidIntegration.inheritance_polymorphic_non_dynamic_base_c_as_b 23 | OidIntegration.inheritance_polymorphic_non_dynamic_base_c_as_c 24 | OidIntegration.inheritance_polymorphic_non_dynamic_base_c_no_polymorphic 25 | OidIntegration.std_vector_del_allocator_a 26 | OidIntegration.templates_int 27 | OidIntegration.templates_two 28 | OidIntegration.templates_value 29 | OidIntegration.templates_vector 30 | OidIntegration.typedefed_parent_multilevel_typedef_parent 31 | OidIntegration.typedefed_parent_simple_typedef_parent 32 | OidIntegration.typedefs_anonymous 33 | OidIntegration.typedefs_c_style 34 | OidIntegration.typedefs_container 35 | OidIntegration.typedefs_using 36 | OidIntegration.unions_alignment 37 | OidIntegration.unions_int 38 | OidIntegration.unions_tagged_int 39 | OidIntegration.unions_tagged_unordered_map 40 | OidIntegration.unions_tagged_vector 41 | OidIntegration.unions_unordered_map 42 | OidIntegration.unions_vector 43 | -------------------------------------------------------------------------------- /test/integration/multi_arg.toml: -------------------------------------------------------------------------------- 1 | definitions = ''' 2 | struct NodeA { 3 | int x, y, z; 4 | }; 5 | 6 | // Structure that mimic the inside of a std::string 7 | // So we can create bogus std::string and test TreeBuilder failures 8 | struct StringInner { 9 | uintptr_t buffer, size, capacity, extra; 10 | }; 11 | ''' 12 | 13 | [cases] 14 | [cases.a] 15 | oil_disable = 'multi-argument probing has no meaning for oil' 16 | param_types = ["int", "double"] 17 | args = "arg0,arg1" 18 | setup = "return {1,2.0};" 19 | expect_json = '[{"staticSize":4, "dynamicSize":0},{"staticSize":8, "dynamicSize":0}]' 20 | # TODO separate sizes for each argument? 21 | 22 | # Test that TreeBuilder failing to run on the first arg doesn't impact the second arg 23 | [cases.tb_fail_first_arg] 24 | oil_disable = "treebuilder tests are oid specific" 25 | param_types = ["const std::string&", "const NodeA&"] 26 | args = "arg0,arg1" 27 | setup = """ 28 | // Create a string with an invalid size/capacity to trip TreeBuilder 29 | StringInner strIn{0, (uintptr_t)-1, (uintptr_t)-1, 0}; 30 | std::string *str = (std::string*)&strIn; 31 | return { std::move(*str), NodeA{4, 5, 6} }; 32 | """ 33 | expect_json = '[{},{"staticSize":12, "dynamicSize":0}]' 34 | 35 | [cases.tb_all_fail_crashes] 36 | oil_disable = "treebuilder tests are oid specific" 37 | param_types = ["const std::string&", "const std::string&"] 38 | args = "arg0,arg1" 39 | setup = """ 40 | // Create a string with an invalid size/capacity to trip TreeBuilder 41 | StringInner strIn1{0, (uintptr_t)-1, (uintptr_t)-1, 0}; 42 | std::string *str1 = (std::string*)&strIn1; 43 | 44 | StringInner strIn2{0, (uintptr_t)-1, (uintptr_t)-1, 0}; 45 | std::string *str2 = (std::string*)&strIn2; 46 | 47 | return { std::move(*str1), std::move(*str2) }; 48 | """ 49 | expect_oid_exit_code = 6 50 | expect_stderr = ".*Nothing to output: failed to run TreeBuilder on any argument.*" 51 | -------------------------------------------------------------------------------- /oi/OIGenerator.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #pragma once 18 | 19 | #include 20 | #include 21 | 22 | #include "oi/DrgnUtils.h" 23 | #include "oi/OICodeGen.h" 24 | #include "oi/OICompiler.h" 25 | 26 | namespace clang::tooling { 27 | class CompilationDatabase; 28 | } 29 | 30 | namespace oi::detail { 31 | namespace type_graph { 32 | class Type; 33 | } 34 | 35 | class OIGenerator { 36 | public: 37 | int generate(clang::tooling::CompilationDatabase&, 38 | const std::vector&); 39 | 40 | void setOutputPath(fs::path _outputPath) { 41 | outputPath = std::move(_outputPath); 42 | } 43 | void setConfigFilePaths(std::vector _configFilePaths) { 44 | configFilePaths = std::move(_configFilePaths); 45 | } 46 | void setSourceFileDumpPath(fs::path _sourceFileDumpPath) { 47 | sourceFileDumpPath = std::move(_sourceFileDumpPath); 48 | } 49 | void setFailIfNothingGenerated(bool fail) { 50 | failIfNothingGenerated = fail; 51 | } 52 | void setClangArgs(std::vector args_) { 53 | clangArgs = std::move(args_); 54 | } 55 | 56 | private: 57 | std::filesystem::path outputPath; 58 | std::vector configFilePaths; 59 | std::filesystem::path sourceFileDumpPath; 60 | bool failIfNothingGenerated = false; 61 | std::vector clangArgs; 62 | }; 63 | 64 | } // namespace oi::detail 65 | -------------------------------------------------------------------------------- /test/integration/inheritance_access.toml: -------------------------------------------------------------------------------- 1 | definitions = ''' 2 | class Base { 3 | int32_t base_int; 4 | }; 5 | 6 | class Public : public Base { 7 | int32_t public_int; 8 | }; 9 | 10 | class Protected : protected Base { 11 | int32_t protected_int; 12 | }; 13 | 14 | class Private : private Base { 15 | int32_t private_int; 16 | }; 17 | ''' 18 | [cases] 19 | [cases.public] 20 | param_types = ["const Public&"] 21 | setup = "return {};" 22 | expect_json = '''[{ 23 | "staticSize":8, 24 | "exclusiveSize":0, 25 | "size":8, 26 | "members":[ 27 | {"name":"base_int", "staticSize":4, "exclusiveSize":4, "size":4, "typeName": "int32_t"}, 28 | {"name":"public_int", "staticSize":4, "exclusiveSize":4, "size":4, "typeName": "int32_t"} 29 | ]}]''' 30 | [cases.protected] 31 | param_types = ["const Protected&"] 32 | setup = "return {};" 33 | expect_json = '''[{ 34 | "staticSize":8, 35 | "exclusiveSize":0, 36 | "size":8, 37 | "members":[ 38 | {"name":"base_int", "staticSize":4, "exclusiveSize":4, "size":4, "typeName": "int32_t"}, 39 | {"name":"protected_int", "staticSize":4, "exclusiveSize":4, "size":4, "typeName": "int32_t"} 40 | ]}]''' 41 | [cases.private] 42 | param_types = ["const Private&"] 43 | setup = "return {};" 44 | expect_json = '''[{ 45 | "staticSize":8, 46 | "exclusiveSize":0, 47 | "size":8, 48 | "members":[ 49 | {"name":"base_int", "staticSize":4, "exclusiveSize":4, "size":4, "typeName": "int32_t"}, 50 | {"name":"private_int", "staticSize":4, "exclusiveSize":4, "size":4, "typeName": "int32_t"} 51 | ]}]''' 52 | [cases.public_as_base] 53 | param_types = ["const Base&"] 54 | arg_types = ["Public"] 55 | setup = "return {};" 56 | expect_json = '''[{ 57 | "staticSize":4, 58 | "exclusiveSize":0, 59 | "size":4, 60 | "members":[ 61 | {"name":"base_int", "staticSize":4, "exclusiveSize":4, "size":4, "typeName": "int32_t"} 62 | ]}]''' 63 | -------------------------------------------------------------------------------- /include/oi/oi.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #ifndef INCLUDED_OI_OI_H 17 | #define INCLUDED_OI_OI_H 1 18 | 19 | #include 20 | #include 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | namespace oi { 28 | 29 | enum class Feature { 30 | ChaseRawPointers, 31 | CaptureThriftIsset, 32 | GenJitDebug, 33 | }; 34 | 35 | #ifdef OIL_AOT_COMPILATION 36 | 37 | template 38 | IntrospectionResult __attribute__((weak)) introspectImpl(const T& objectAddr); 39 | 40 | template 41 | __attribute__((noinline)) IntrospectionResult introspect(const T& objectAddr) { 42 | if (!introspectImpl) 43 | throw std::logic_error( 44 | "OIL is expecting AoT compilation but it doesn't appear to have run."); 45 | 46 | return introspectImpl(objectAddr); 47 | } 48 | 49 | template 50 | std::optional tryIntrospect(const T& objectAddr) { 51 | if (!introspectImpl) 52 | return std::nullopt; 53 | 54 | // This checks twice but is necessary for compile time as it currently 55 | // depends on the presence of the strong symbol. 56 | return introspect(objectAddr); 57 | } 58 | 59 | #endif 60 | 61 | } // namespace oi 62 | 63 | #ifndef OIL_AOT_COMPILATION 64 | #include "oi/oi-jit.h" 65 | #endif 66 | 67 | #endif 68 | -------------------------------------------------------------------------------- /test/integration/std_list_del_allocator.toml: -------------------------------------------------------------------------------- 1 | definitions = ''' 2 | namespace nsA { 3 | struct C { 4 | int a; 5 | }; 6 | } 7 | namespace nsB { 8 | struct C { 9 | int b; 10 | }; 11 | } 12 | 13 | template 14 | class CustomAllocator: public std::allocator 15 | { 16 | 17 | }; 18 | 19 | // Naming conflict will only work if OI deletes the allocator field 20 | struct Foo { 21 | std::list> v1; 22 | std::list> v2; 23 | }; 24 | 25 | ''' 26 | includes = ["list"] 27 | 28 | [cases] 29 | [cases.a] 30 | param_types = ["const Foo&"] 31 | setup = ''' 32 | Foo foo; 33 | foo.v1.resize(1); 34 | foo.v2.resize(2); 35 | return {foo}; 36 | ''' 37 | expect_json = '''[{ 38 | "staticSize":48, 39 | "dynamicSize":12, 40 | "members":[ 41 | {"name":"v1", "staticSize":24, "dynamicSize":4, "length":1, "capacity":1, "elementStaticSize":4, 42 | "members":[ 43 | {"name":"", "staticSize":4, "dynamicSize":0, 44 | "members":[ 45 | {"name":"a", "staticSize":4, "dynamicSize":0} 46 | ]} 47 | ]}, 48 | {"name":"v2", "staticSize":24, "dynamicSize":8, "length":2, "capacity":2, "elementStaticSize":4, 49 | "members":[ 50 | {"name":"", "staticSize":4, "dynamicSize":0, 51 | "members":[ 52 | {"name":"b", "staticSize":4, "dynamicSize":0} 53 | ]}, 54 | {"name":"", "staticSize":4, "dynamicSize":0, 55 | "members":[ 56 | {"name":"b", "staticSize":4, "dynamicSize":0} 57 | ]} 58 | ]} 59 | ]}]''' 60 | expect_json_v2 = '''[{ 61 | "staticSize": 48, 62 | "exclusiveSize": 0, 63 | "size": 120, 64 | "members": [ 65 | {"name": "v1", "staticSize": 24, "exclusiveSize": 44, "size": 48, "length": 1, "capacity": 1}, 66 | {"name": "v2", "staticSize": 24, "exclusiveSize": 64, "size": 72, "length": 2, "capacity": 2} 67 | ]}]''' 68 | -------------------------------------------------------------------------------- /oi/EnumBitset.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #pragma once 17 | 18 | #include 19 | 20 | template 21 | class EnumBitset { 22 | private: 23 | using BitsetType = std::bitset; 24 | 25 | public: 26 | EnumBitset() = default; 27 | EnumBitset(std::initializer_list values) { 28 | for (auto v : values) { 29 | (*this)[v] = true; 30 | } 31 | } 32 | 33 | constexpr bool operator[](T v) const { 34 | return bitset[static_cast(v)]; 35 | } 36 | typename BitsetType::reference operator[](T v) { 37 | return bitset[static_cast(v)]; 38 | } 39 | 40 | bool all() const noexcept { 41 | return bitset.all(); 42 | } 43 | bool any() const noexcept { 44 | return bitset.any(); 45 | } 46 | bool none() const noexcept { 47 | return bitset.none(); 48 | } 49 | 50 | bool operator==(const EnumBitset& that) const { 51 | return bitset == that.bitset; 52 | } 53 | EnumBitset& operator|=(const EnumBitset& that) { 54 | bitset |= that.bitset; 55 | return *this; 56 | } 57 | EnumBitset& operator&=(const EnumBitset& that) { 58 | bitset &= that.bitset; 59 | return *this; 60 | } 61 | 62 | private: 63 | BitsetType bitset; 64 | }; 65 | 66 | template 67 | EnumBitset operator&(const EnumBitset& lhs, 68 | const EnumBitset& rhs) { 69 | auto out = lhs; 70 | out &= rhs; 71 | return out; 72 | } 73 | -------------------------------------------------------------------------------- /oi/type_graph/TopoSorter.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #pragma once 17 | 18 | #include 19 | #include 20 | #include 21 | 22 | #include "PassManager.h" 23 | #include "Types.h" 24 | #include "Visitor.h" 25 | 26 | namespace oi::detail::type_graph { 27 | 28 | /* 29 | * TopoSorter 30 | * 31 | * Topologically sorts a list of types so that dependencies appear before 32 | * dependent types. 33 | */ 34 | class TopoSorter : public RecursiveVisitor { 35 | public: 36 | static Pass createPass(); 37 | 38 | void sort(const std::vector>& types); 39 | const std::vector>& sortedTypes() const; 40 | 41 | using RecursiveVisitor::accept; 42 | 43 | void accept(Type& type) override; 44 | void visit(Class& c) override; 45 | void visit(Container& c) override; 46 | void visit(Enum& e) override; 47 | void visit(Typedef& td) override; 48 | void visit(Pointer& p) override; 49 | void visit(Reference& r) override; 50 | void visit(Primitive& p) override; 51 | void visit(CaptureKeys& p) override; 52 | void visit(Incomplete& i) override; 53 | void visit(Array& i) override; 54 | 55 | private: 56 | std::unordered_set visited_; 57 | std::vector> sortedTypes_; 58 | std::queue> typesToSort_; 59 | 60 | void acceptAfter(Type& type); 61 | void acceptAfter(Type* type); 62 | }; 63 | 64 | } // namespace oi::detail::type_graph 65 | -------------------------------------------------------------------------------- /types/list_type.toml: -------------------------------------------------------------------------------- 1 | [info] 2 | type_name = "std::list" 3 | stub_template_params = [1] 4 | ctype = "LIST_TYPE" 5 | header = "list" 6 | 7 | # Old: 8 | typeName = "std::list<" 9 | ns = ["namespace std"] 10 | numTemplateParams = 1 11 | replaceTemplateParamIndex = [] 12 | allocatorIndex = 1 13 | 14 | [codegen] 15 | decl = """ 16 | template 17 | void getSizeType(const %1% &container, size_t& returnArg); 18 | """ 19 | 20 | func = """ 21 | template 22 | void getSizeType(const %1% &container, size_t& returnArg) 23 | { 24 | SAVE_SIZE(sizeof(%1%)); 25 | 26 | SAVE_DATA((uintptr_t)&container); 27 | SAVE_DATA((uintptr_t)container.size()); 28 | 29 | // The double ampersand is needed otherwise this loop doesn't work with vector 30 | for (auto&& it: container) { 31 | getSizeType(it, returnArg); 32 | } 33 | } 34 | """ 35 | 36 | traversal_func = """ 37 | auto tail = returnArg.write((uintptr_t)&container) 38 | .write(container.size()); 39 | 40 | for (auto&& it : container) { 41 | tail = tail.delegate([&ctx, &it](auto ret) { 42 | return OIInternal::getSizeType(ctx, it, ret); 43 | }); 44 | } 45 | 46 | return tail.finish(); 47 | """ 48 | 49 | [[codegen.processor]] 50 | type = "types::st::VarInt" 51 | func = """ 52 | el.pointer = std::get(d.val).value; 53 | """ 54 | 55 | [[codegen.processor]] 56 | type = "types::st::List::type>" 57 | func = """ 58 | #ifdef __GLIBCXX__ 59 | static constexpr size_t element_size = sizeof(std::_List_node); 60 | #else 61 | static_assert(false && "No known element_size for list. See types/cxx11_list_type.toml"); 62 | #endif 63 | 64 | static constexpr auto childField = make_field("[]"); 65 | 66 | auto list = std::get(d.val); 67 | el.container_stats.emplace(result::Element::ContainerStats{ 68 | .capacity = list.length, 69 | .length = list.length, 70 | }); 71 | el.exclusive_size += el.container_stats->length * (element_size - sizeof(T0)); 72 | 73 | stack_ins(inst::Repeat{ list.length, childField }); 74 | """ 75 | -------------------------------------------------------------------------------- /test/integration/std_conditional.toml: -------------------------------------------------------------------------------- 1 | definitions = ''' 2 | class A { 3 | int f; 4 | int g; 5 | }; 6 | 7 | class B { 8 | int h; 9 | }; 10 | 11 | class C { 12 | public: 13 | // using T = std::conditional_t= sizeof(char), A, B>; 14 | std::conditional_t= sizeof(char), A, B> foo; 15 | }; 16 | ''' 17 | [cases] 18 | [cases.a] 19 | oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/317 20 | param_types = ["const C&"] 21 | setup = ''' 22 | C foo; 23 | return {foo}; 24 | ''' 25 | expect_json = ''' 26 | [ 27 | { 28 | "typeName": "ns_std_conditional::C", 29 | "isTypedef": false, 30 | "staticSize": 8, 31 | "dynamicSize": 0, 32 | "members": [ 33 | { 34 | "name": "foo", 35 | "typePath": "foo", 36 | "isTypedef": true, 37 | "staticSize": 8, 38 | "dynamicSize": 0, 39 | "members": [ 40 | { 41 | "name": "", 42 | "typePath": "", 43 | "typeName": "type", 44 | "isTypedef": true, 45 | "staticSize": 8, 46 | "dynamicSize": 0, 47 | "members": [ 48 | { 49 | "name": "", 50 | "typePath": "", 51 | "typeName": "ns_std_conditional::A", 52 | "isTypedef": false, 53 | "staticSize": 8, 54 | "dynamicSize": 0, 55 | "members": [ 56 | { 57 | "name": "f", 58 | "typePath": "f", 59 | "isTypedef": false, 60 | "staticSize": 4, 61 | "dynamicSize": 0 62 | }, 63 | { 64 | "name": "g", 65 | "typePath": "g", 66 | "isTypedef": false, 67 | "staticSize": 4, 68 | "dynamicSize": 0 69 | } 70 | ] 71 | } 72 | ] 73 | } 74 | ] 75 | } 76 | ] 77 | } 78 | ] 79 | ''' 80 | -------------------------------------------------------------------------------- /types/cxx11_list_type.toml: -------------------------------------------------------------------------------- 1 | [info] 2 | type_name = "std::__cxx11::list" 3 | stub_template_params = [1] 4 | header = "list" 5 | 6 | # Old: 7 | ctype = "LIST_TYPE" 8 | typeName = "std::__cxx11::list" 9 | ns = ["namespace std"] 10 | numTemplateParams = 1 11 | replaceTemplateParamIndex = [] 12 | allocatorIndex = 1 13 | 14 | [codegen] 15 | decl = """ 16 | template 17 | void getSizeType(const %1% &container, size_t& returnArg); 18 | """ 19 | 20 | func = """ 21 | template 22 | void getSizeType(const %1% &container, size_t& returnArg) 23 | { 24 | SAVE_SIZE(sizeof(%1%)); 25 | 26 | SAVE_DATA((uintptr_t)&container); 27 | SAVE_DATA((uintptr_t)container.size()); 28 | 29 | // The double ampersand is needed otherwise this loop doesn't work with vector 30 | for (auto&& it: container) { 31 | getSizeType(it, returnArg); 32 | } 33 | } 34 | """ 35 | 36 | traversal_func = """ 37 | auto tail = returnArg.write((uintptr_t)&container) 38 | .write(container.size()); 39 | 40 | for (auto&& it : container) { 41 | tail = tail.delegate([&ctx, &it](auto ret) { 42 | return OIInternal::getSizeType(ctx, it, ret); 43 | }); 44 | } 45 | 46 | return tail.finish(); 47 | """ 48 | 49 | [[codegen.processor]] 50 | type = "types::st::VarInt" 51 | func = """ 52 | el.pointer = std::get(d.val).value; 53 | """ 54 | 55 | [[codegen.processor]] 56 | type = "types::st::List::type>" 57 | func = """ 58 | #ifdef __GLIBCXX__ 59 | static constexpr size_t element_size = sizeof(std::_List_node); 60 | #else 61 | static_assert(false && "No known element_size for list. See types/cxx11_list_type.toml"); 62 | #endif 63 | 64 | static constexpr auto childField = make_field("[]"); 65 | 66 | auto list = std::get(d.val); 67 | el.container_stats.emplace(result::Element::ContainerStats{ 68 | .capacity = list.length, 69 | .length = list.length, 70 | }); 71 | el.exclusive_size += el.container_stats->length * (element_size - sizeof(T0)); 72 | 73 | stack_ins(inst::Repeat{ list.length, childField }); 74 | """ 75 | -------------------------------------------------------------------------------- /include/oi/exporters/ParsedData.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #ifndef INCLUDED_OI_EXPORTERS_PARSED_DATA_H 17 | #define INCLUDED_OI_EXPORTERS_PARSED_DATA_H 1 18 | 19 | #include 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | namespace oi::exporters { 27 | 28 | struct ParsedData { 29 | class Lazy { 30 | public: 31 | Lazy(std::vector::const_iterator& it, types::dy::Dynamic ty) 32 | : it_(it), ty_(ty) { 33 | } 34 | 35 | ParsedData operator()() { 36 | return ParsedData::parse(it_, ty_); 37 | } 38 | 39 | private: 40 | std::vector::const_iterator& it_; 41 | types::dy::Dynamic ty_; 42 | }; 43 | 44 | struct Unit {}; 45 | struct VarInt { 46 | uint64_t value; 47 | }; 48 | struct Pair { 49 | Lazy first; 50 | Lazy second; 51 | }; 52 | struct List { 53 | uint64_t length; 54 | Lazy values; 55 | }; 56 | struct Sum { 57 | uint64_t index; 58 | Lazy value; 59 | }; 60 | 61 | static ParsedData parse(std::vector::const_iterator& it, 62 | types::dy::Dynamic ty); 63 | 64 | ParsedData(Unit&& val_) : val(val_) { 65 | } 66 | ParsedData(VarInt&& val_) : val(val_) { 67 | } 68 | ParsedData(Pair&& val_) : val(val_) { 69 | } 70 | ParsedData(List&& val_) : val(val_) { 71 | } 72 | ParsedData(Sum&& val_) : val(val_) { 73 | } 74 | 75 | std::variant val; 76 | }; 77 | 78 | } // namespace oi::exporters 79 | 80 | #endif 81 | -------------------------------------------------------------------------------- /test/integration/enums.toml: -------------------------------------------------------------------------------- 1 | definitions = ''' 2 | enum class ScopedEnum { 3 | CaseA, 4 | CaseB, 5 | CaseC, 6 | }; 7 | 8 | enum class ScopedEnumUint8 : uint8_t { 9 | CaseA = 2, 10 | CaseB = 3, 11 | CaseC = 4, 12 | }; 13 | 14 | 15 | enum UNSCOPED_ENUM { 16 | CASE_A = 5, 17 | CASE_B = -2, 18 | CASE_C = 20, 19 | }; 20 | 21 | struct Holder { 22 | enum { 23 | One, 24 | Two, 25 | } e; 26 | }; 27 | 28 | template 29 | struct Pair { 30 | T1 first; 31 | T2 second; 32 | }; 33 | ''' 34 | [cases] 35 | [cases.scoped] 36 | param_types = ["ScopedEnum"] 37 | setup = "return {};" 38 | expect_json = '[{"staticSize":4, "dynamicSize":0}]' 39 | expect_json_v2 = '[{"typeNames": ["ns_enums::ScopedEnum"], "staticSize":4, "exclusiveSize":4, "size":4}]' 40 | [cases.scoped_uint8] 41 | param_types = ["ScopedEnumUint8"] 42 | setup = "return {};" 43 | expect_json = '[{"staticSize":1, "dynamicSize":0}]' 44 | expect_json_v2 = '[{"typeNames": ["ns_enums::ScopedEnumUint8"], "staticSize":1, "exclusiveSize":1, "size":1}]' 45 | [cases.unscoped] 46 | param_types = ["UNSCOPED_ENUM"] 47 | setup = "return {};" 48 | expect_json = '[{"staticSize":4, "dynamicSize":0, "exclusiveSize":4, "size":4}]' 49 | [cases.anonymous] 50 | skip = "TreeBuilder crashes" # https://github.com/facebookexperimental/object-introspection/issues/232 51 | param_types = ["Holder&"] 52 | setup = "return {};" 53 | expect_json = '''[ 54 | {"staticSize":4, "dynamicSize":0, "exclusiveSize":0, "size":4, "members":[ 55 | {"name":"e", "staticSize":4, "dynamicSize":0, "exclusiveSize":4, "size":4} 56 | ]}]''' 57 | [cases.paired_with_element] 58 | param_types = ["Pair"] 59 | setup = "return {};" 60 | expect_json = '[{"staticSize":2, "dynamicSize":0}]' 61 | expect_json_v2 = '''[ 62 | {"staticSize": 2, "exclusiveSize": 0, "size":2, "members": [ 63 | {"typeNames": ["ns_enums::ScopedEnumUint8"], "staticSize":1, "exclusiveSize":1, "size":1}, 64 | {"typeNames": ["uint8_t"], "staticSize":1, "exclusiveSize":1, "size":1} 65 | ]} 66 | ]''' 67 | -------------------------------------------------------------------------------- /test/integration/enums_params.toml: -------------------------------------------------------------------------------- 1 | includes = ["vector"] 2 | definitions = ''' 3 | namespace MyNS { 4 | enum class ScopedEnum { 5 | Zero = 0, 6 | One = 1, 7 | Two = 2, 8 | }; 9 | 10 | enum UNSCOPED_ENUM { 11 | ZERO = 0, 12 | ONE = 1, 13 | TWO = 2, 14 | }; 15 | 16 | enum class EnumWithGaps { 17 | Five = 5, 18 | MinusTwo = -2, 19 | Twenty = 20, 20 | }; 21 | } // MyNS 22 | 23 | template 24 | class MyClass { 25 | int n; 26 | }; 27 | 28 | template 29 | class ClassGaps { 30 | int n; 31 | }; 32 | ''' 33 | 34 | [cases] 35 | [cases.scoped_enum_type] 36 | param_types = ["const std::vector&"] 37 | setup = "return {};" 38 | [cases.scoped_enum_val_cast] 39 | param_types = ["const std::array(MyNS::ScopedEnum::Two)>&"] 40 | setup = "return {};" 41 | expect_json = '[{"staticSize":8, "length":2, "capacity":2, "elementStaticSize":4}]' 42 | expect_json_v2 = '[{"staticSize":8, "exclusiveSize":0, "size":8, "length":2, "capacity":2}]' 43 | 44 | [cases.scoped_enum_val] 45 | param_types = ["const MyClass&"] 46 | setup = "return {};" 47 | expect_json = '[{"staticSize":4, "dynamicSize":0, "exclusiveSize":0, "size":4}]' 48 | [cases.scoped_enum_val_gaps] 49 | param_types = ["const ClassGaps&"] 50 | setup = "return {};" 51 | expect_json = '[{"staticSize":4, "dynamicSize":0, "exclusiveSize":0, "size":4}]' 52 | [cases.scoped_enum_val_negative] 53 | param_types = ["const ClassGaps&"] 54 | setup = "return {};" 55 | expect_json = '[{"staticSize":4, "dynamicSize":0, "exclusiveSize":0, "size":4}]' 56 | 57 | [cases.unscoped_enum_type] 58 | param_types = ["const std::vector&"] 59 | setup = "return {};" 60 | [cases.unscoped_enum_val_cast] 61 | param_types = ["const std::array&"] 62 | setup = "return {};" 63 | expect_json = '[{"staticSize":4, "length":1, "capacity":1, "elementStaticSize":4}]' 64 | expect_json_v2 = '[{"staticSize":4, "exclusiveSize":0, "size":4, "length":1, "capacity":1}]' 65 | -------------------------------------------------------------------------------- /oi/Features.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #pragma once 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #include "oi/EnumBitset.h" 24 | 25 | #define OI_FEATURE_LIST \ 26 | X(ChaseRawPointers, "chase-raw-pointers") \ 27 | X(PackStructs, "pack-structs") \ 28 | X(GenPaddingStats, "gen-padding-stats") \ 29 | X(CaptureThriftIsset, "capture-thrift-isset") \ 30 | X(TypeGraph, "type-graph") \ 31 | X(PruneTypeGraph, "prune-type-graph") \ 32 | X(Library, "library") \ 33 | X(TreeBuilderV2, "tree-builder-v2") \ 34 | X(GenJitDebug, "gen-jit-debug") \ 35 | X(JitLogging, "jit-logging") \ 36 | X(JitTiming, "jit-timing") \ 37 | X(PolymorphicInheritance, "polymorphic-inheritance") 38 | 39 | namespace oi::detail { 40 | 41 | enum class Feature { 42 | UnknownFeature, 43 | #define X(name, _) name, 44 | OI_FEATURE_LIST 45 | #undef X 46 | }; 47 | 48 | constexpr std::array allFeatures = { 49 | #define X(name, _) Feature::name, 50 | OI_FEATURE_LIST 51 | #undef X 52 | }; 53 | 54 | // Use "size+1" to account for UnknownFeature" 55 | using FeatureSet = EnumBitset; 56 | 57 | Feature featureFromStr(std::string_view); 58 | const char* featureToStr(Feature); 59 | void featuresHelp(std::ostream& out); 60 | std::optional handleFeatureConflicts(FeatureSet enabled, 61 | const FeatureSet& disabled); 62 | 63 | } // namespace oi::detail 64 | -------------------------------------------------------------------------------- /tools/combine_configs.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) Meta Platforms, Inc. and affiliates. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | # 16 | import sys 17 | 18 | import toml 19 | 20 | 21 | def main(): 22 | if len(sys.argv) < 2: 23 | print("usage: combine_configs.py OUTPUT_PATH INPUT_PATHS...") 24 | return 25 | 26 | out = { 27 | "types": { 28 | "containers": [], 29 | "pass_through": [], 30 | }, 31 | "headers": { 32 | "user_paths": [], 33 | "system_paths": [], 34 | }, 35 | "codegen": { 36 | "default_headers": set(), 37 | "default_namespaces": set(), 38 | }, 39 | } 40 | for cfg_path in sys.argv[2:]: 41 | cfg = toml.load(cfg_path) 42 | 43 | types = cfg.get("types", None) 44 | if types is not None: 45 | out["types"]["containers"] += types.get("containers", []) 46 | out["types"]["pass_through"] += types.get("pass_through", []) 47 | 48 | headers = cfg.get("headers", None) 49 | if headers is not None: 50 | out["headers"]["user_paths"] += headers.get("user_paths", []) 51 | out["headers"]["system_paths"] += headers.get("system_paths", []) 52 | 53 | codegen = cfg.get("codegen", None) 54 | if codegen is not None: 55 | out["codegen"]["default_headers"].update( 56 | codegen.get("default_headers", set()) 57 | ) 58 | out["codegen"]["default_namespaces"].update( 59 | codegen.get("default_namespaces", set()) 60 | ) 61 | 62 | with open(sys.argv[1], "w") as f: 63 | toml.dump(out, f) 64 | 65 | 66 | if __name__ == "__main__": 67 | main() 68 | -------------------------------------------------------------------------------- /website/docs/addrbook-intro.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: A Simple Address Book Example 3 | --- 4 | 5 | # A simple address book example 6 | 7 | Let's start with a very simple C++ application: an address book. This contrived simple piece of code contains everything we need to take you through the basics of using OI. The code itself can be found in the `examples/web/AddrBook` directory in the OI [GitHub repo](https://github.com/facebookexperimental/object-introspection/tree/main/examples/web/AddrBook). 8 | 9 | First, build the test application: 10 | ``` 11 | $ ~/object-introspection/examples/web/AddrBook: make CC=clang++-12 12 | clang++-12 -o addrbook AddrBook.cpp -std=c++20 -g -O3 13 | ``` 14 | 15 | (No need to override the 'CC' make variable if you have `clang++` in your path). 16 | 17 | You can see the DWARF data is present in the generated executable: 18 | 19 | ``` 20 | $ ~/object-introspection/examples/web/AddrBook# size -At addrbook | grep "\.debug" 21 | .debug_info 71316 0 22 | .debug_abbrev 2446 0 23 | .debug_line 8971 0 24 | .debug_str 44990 0 25 | .debug_loc 27968 0 26 | .debug_ranges 10240 0 27 | ``` 28 | 29 | Each address book is composed of a single `AddressBook` object which contains zero or more `Contact` objects. Here's how the data and interface definitions look for the two objects: 30 | 31 | ```C++ 32 | class Contact { 33 | public: 34 | Contact(std::string& f, std::string& l, std::string& n); 35 | private: 36 | std::string firstName, lastName; 37 | std::string number; 38 | }; 39 | 40 | class AddressBook { 41 | public: 42 | void AddContact(std::string& f, std::string& l, std::string& n); 43 | void DumpContacts(void); 44 | private: 45 | int rev; 46 | std::string Owner; 47 | std::vector Entries; 48 | }; 49 | ``` 50 | 51 | OI can introspect objects at specific points in an application: 52 | 53 |
    54 |
  • Function arguments upon entry to a function.
  • 55 |
  • Function arguments upon return to a function.
  • 56 |
  • The return value from a function.
  • 57 |
  • This `this` pointer at entry or return from an object method.
  • 58 |
  • Global objects.
  • 59 |
60 | 61 | Let's get started by introspecting an object using its `this` pointer. 62 | -------------------------------------------------------------------------------- /oi/type_graph/KeyCapture.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #pragma once 17 | 18 | #include 19 | #include 20 | #include 21 | 22 | #include "NodeTracker.h" 23 | #include "PassManager.h" 24 | #include "Types.h" 25 | #include "Visitor.h" 26 | #include "oi/OICodeGen.h" 27 | 28 | namespace oi::detail::type_graph { 29 | 30 | /* 31 | * KeyCapture 32 | * 33 | * Marks containers for which the user has requested key-capture. 34 | */ 35 | class KeyCapture : public RecursiveVisitor { 36 | public: 37 | static Pass createPass( 38 | const std::vector& keysToCapture, 39 | std::vector>& containerInfos); 40 | 41 | KeyCapture(NodeTracker& tracker, 42 | TypeGraph& typeGraph, 43 | const std::vector& keysToCapture, 44 | std::vector>& containerInfos) 45 | : tracker_(tracker), 46 | typeGraph_(typeGraph), 47 | keysToCapture_(keysToCapture), 48 | containerInfos_(containerInfos) { 49 | } 50 | 51 | using RecursiveVisitor::accept; 52 | using RecursiveVisitor::visit; 53 | 54 | void insertCaptureDataNodes(std::vector>& types); 55 | void visit(Class& c) override; 56 | 57 | private: 58 | NodeTracker& tracker_; 59 | TypeGraph& typeGraph_; 60 | const std::vector& keysToCapture_; 61 | std::vector>& containerInfos_; 62 | 63 | void accept(Type& type) override; 64 | Type& captureKey(Type& type); 65 | }; 66 | 67 | } // namespace oi::detail::type_graph 68 | -------------------------------------------------------------------------------- /test/integration/std_string.toml: -------------------------------------------------------------------------------- 1 | includes = ["string"] 2 | [cases] 3 | [cases.empty] 4 | oil_skip = 'needs updating for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/311 5 | param_types = ["std::string&"] 6 | setup = "return {};" 7 | expect_json = ''' 8 | [ 9 | { 10 | "typeName": "string", 11 | "isTypedef": true, 12 | "staticSize": 32, 13 | "dynamicSize": 0, 14 | "exclusiveSize": 0, 15 | "members": [ 16 | { 17 | "staticSize": 32, 18 | "dynamicSize": 0, 19 | "exclusiveSize": 32, 20 | "length": 0, 21 | "capacity": 15, 22 | "elementStaticSize": 1 23 | } 24 | ] 25 | } 26 | ] 27 | ''' 28 | [cases.sso] 29 | oil_skip = 'needs updating for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/311 30 | param_types = ["std::string&"] 31 | setup = 'return {"012345"};' 32 | expect_json = ''' 33 | [ 34 | { 35 | "typeName": "string", 36 | "isTypedef": true, 37 | "staticSize": 32, 38 | "dynamicSize": 0, 39 | "exclusiveSize": 0, 40 | "members": [ 41 | { 42 | "staticSize": 32, 43 | "dynamicSize": 0, 44 | "exclusiveSize": 32, 45 | "length": 6, 46 | "capacity": 15, 47 | "elementStaticSize": 1 48 | } 49 | ] 50 | } 51 | ] 52 | ''' 53 | [cases.heap_allocated] 54 | oil_skip = 'needs updating for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/311 55 | param_types = ["std::string&"] 56 | setup = 'return {"abcdefghijklmnopqrstuvwxzy"};' 57 | expect_json = ''' 58 | [ 59 | { 60 | "typeName": "string", 61 | "isTypedef": true, 62 | "staticSize": 32, 63 | "dynamicSize": 26, 64 | "exclusiveSize": 0, 65 | "members": [ 66 | { 67 | "staticSize": 32, 68 | "dynamicSize": 26, 69 | "exclusiveSize": 58, 70 | "length": 26, 71 | "capacity": 26, 72 | "elementStaticSize": 1 73 | } 74 | ] 75 | } 76 | ] 77 | ''' 78 | -------------------------------------------------------------------------------- /types/seq_type.toml: -------------------------------------------------------------------------------- 1 | [info] 2 | type_name = "std::vector" 3 | stub_template_params = [1] 4 | header = "vector" 5 | 6 | # Old: 7 | ctype = "SEQ_TYPE" 8 | typeName = "std::vector<" 9 | ns = ["namespace std"] 10 | numTemplateParams = 1 11 | allocatorIndex = 1 12 | 13 | [codegen] 14 | decl = """ 15 | template 16 | void getSizeType(const %1% &container, size_t& returnArg); 17 | """ 18 | 19 | func = """ 20 | template 21 | void getSizeType(const %1% &container, size_t& returnArg) 22 | { 23 | SAVE_SIZE(sizeof(%1%)); 24 | 25 | SAVE_DATA((uintptr_t)&container); 26 | SAVE_DATA((uintptr_t)container.capacity()); 27 | SAVE_DATA((uintptr_t)container.size()); 28 | 29 | SAVE_SIZE((container.capacity() - container.size()) * sizeof(T)); 30 | 31 | // The double ampersand is needed otherwise this loop doesn't work with vector 32 | for (auto&& it: container) { 33 | getSizeType(it, returnArg); 34 | } 35 | } 36 | """ 37 | 38 | traversal_func = """ 39 | auto tail = returnArg.write((uintptr_t)&container) 40 | .write(container.capacity()) 41 | .write(container.size()); 42 | 43 | // The double ampersand is needed otherwise this loop doesn't work with 44 | // vector 45 | for (auto&& it : container) { 46 | tail = tail.delegate([&ctx, &it](auto ret) { 47 | return OIInternal::getSizeType(ctx, it, ret); 48 | }); 49 | } 50 | 51 | return tail.finish(); 52 | """ 53 | 54 | [[codegen.processor]] 55 | type = "types::st::VarInt" 56 | func = """ 57 | el.pointer = std::get(d.val).value; 58 | """ 59 | 60 | [[codegen.processor]] 61 | type = "types::st::VarInt" 62 | func = """ 63 | el.container_stats.emplace(result::Element::ContainerStats{ .capacity = std::get(d.val).value }); 64 | """ 65 | 66 | [[codegen.processor]] 67 | type = "types::st::List::type>" 68 | func = """ 69 | static constexpr auto childField = make_field("[]"); 70 | 71 | auto list = std::get(d.val); 72 | el.container_stats->length = list.length; 73 | el.exclusive_size += (el.container_stats->capacity - el.container_stats->length) * sizeof(T0); 74 | 75 | stack_ins(inst::Repeat{ list.length, childField }); 76 | """ 77 | -------------------------------------------------------------------------------- /dev.oid.toml: -------------------------------------------------------------------------------- 1 | # DON'T use this file directly. 2 | # 3 | # It is used to extend sample.oid.toml for development purposes. 4 | 5 | [types] 6 | containers = [ 7 | "PWD/types/array_type.toml", 8 | "PWD/types/string_type.toml", 9 | "PWD/types/cxx11_string_type.toml", 10 | "PWD/types/folly_iobuf_type.toml", 11 | "PWD/types/folly_iobuf_queue_type.toml", 12 | "PWD/types/set_type.toml", 13 | "PWD/types/multi_set_type.toml", 14 | "PWD/types/unordered_set_type.toml", 15 | "PWD/types/unordered_multiset_type.toml", 16 | "PWD/types/seq_type.toml", 17 | "PWD/types/list_type.toml", 18 | "PWD/types/cxx11_list_type.toml", 19 | "PWD/types/deque_list_type.toml", 20 | "PWD/types/shrd_ptr_type.toml", 21 | "PWD/types/uniq_ptr_type.toml", 22 | "PWD/types/std_map_type.toml", 23 | "PWD/types/std_unordered_map_type.toml", 24 | "PWD/types/std_unordered_multimap_type.toml", 25 | "PWD/types/pair_type.toml", 26 | "PWD/types/stack_container_adapter_type.toml", 27 | "PWD/types/queue_container_adapter_type.toml", 28 | "PWD/types/priority_queue_container_adapter_type.toml", 29 | "PWD/types/ref_wrapper_type.toml", 30 | "PWD/types/multi_map_type.toml", 31 | "PWD/types/folly_small_heap_vector_map.toml", 32 | "PWD/types/folly_optional_type.toml", 33 | "PWD/types/optional_type.toml", 34 | "PWD/types/try_type.toml", 35 | "PWD/types/fb_string_type.toml", 36 | "PWD/types/small_vec_type.toml", 37 | "PWD/types/f14_fast_map.toml", 38 | "PWD/types/f14_node_map.toml", 39 | "PWD/types/f14_value_map.toml", 40 | "PWD/types/f14_vector_map.toml", 41 | "PWD/types/f14_fast_set.toml", 42 | "PWD/types/f14_node_set.toml", 43 | "PWD/types/f14_value_set.toml", 44 | "PWD/types/f14_vector_set.toml", 45 | "PWD/types/sorted_vec_set_type.toml", 46 | "PWD/types/map_seq_type.toml", 47 | "PWD/types/boost_bimap_type.toml", 48 | "PWD/types/repeated_field_type.toml", 49 | "PWD/types/repeated_ptr_field_type.toml", 50 | "PWD/types/caffe2_blob_type.toml", 51 | "PWD/types/std_variant.toml", 52 | "PWD/types/thrift_isset_type.toml", 53 | "PWD/types/weak_ptr_type.toml", 54 | ] 55 | pass_through = [ 56 | ["std::allocator", "memory"], 57 | ["std::char_traits", "string"], 58 | ["folly::fbstring_core", "folly/FBString.h"], 59 | ] 60 | -------------------------------------------------------------------------------- /types/uniq_ptr_type.toml: -------------------------------------------------------------------------------- 1 | [info] 2 | type_name = "std::unique_ptr" 3 | ctype = "UNIQ_PTR_TYPE" 4 | header = "memory" 5 | stub_template_params = [1] 6 | 7 | # Old: 8 | typeName = "std::unique_ptr" 9 | ns = ["namespace std"] 10 | numTemplateParams = 1 11 | replaceTemplateParamIndex = [] 12 | 13 | [codegen] 14 | decl = """ 15 | template 16 | void getSizeType(const %1% &container, size_t& returnArg); 17 | """ 18 | 19 | func = """ 20 | template 21 | void getSizeType(const %1% &u_ptr, size_t& returnArg) 22 | { 23 | SAVE_SIZE(sizeof(%1%)); 24 | 25 | if constexpr (oi_is_complete) { 26 | SAVE_DATA((uintptr_t)(u_ptr.get())); 27 | 28 | if (u_ptr && ctx.pointers.add((uintptr_t)(u_ptr.get()))) { 29 | SAVE_DATA(1); 30 | getSizeType(*(u_ptr.get()), returnArg); 31 | } else { 32 | SAVE_DATA(0); 33 | } 34 | } 35 | } 36 | """ 37 | 38 | traversal_func = """ 39 | auto tail = returnArg.write((uintptr_t)container.get()); 40 | 41 | if constexpr (!oi_is_complete) { 42 | return tail.template delegate<0>(std::identity()); 43 | } else { 44 | bool do_visit = container && ctx.pointers.add((uintptr_t)container.get()); 45 | if (!do_visit) 46 | return tail.template delegate<0>(std::identity()); 47 | 48 | return tail.template delegate<1>([&ctx, &container](auto ret) { 49 | return OIInternal::getSizeType(ctx, *container, ret); 50 | }); 51 | } 52 | """ 53 | 54 | [[codegen.processor]] 55 | type = "types::st::VarInt" 56 | func = """ 57 | el.pointer = std::get(d.val).value; 58 | """ 59 | 60 | [[codegen.processor]] 61 | type = """ 62 | types::st::Sum, 64 | typename TypeHandler>::type> 65 | """ 66 | func = """ 67 | auto sum = std::get(d.val); 68 | el.container_stats.emplace(result::Element::ContainerStats { 69 | .capacity = 1, 70 | .length = sum.index, // 0 for empty containers/void, 1 otherwise 71 | }); 72 | 73 | // Must be in a `if constexpr` or the compiler will complain about make_field 74 | if constexpr (oi_is_complete) { 75 | if (sum.index == 1) { 76 | static constexpr auto element = make_field("ptr_val"); 77 | stack_ins(element); 78 | } 79 | } 80 | """ 81 | -------------------------------------------------------------------------------- /oi/exporters/TypeCheckingWalker.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #pragma once 17 | 18 | /* 19 | * TypeCheckingWalker 20 | * 21 | * Walks a dynamic data segment type simultaneously with the contents of the 22 | * data segment, providing context to each extracted element. 23 | * 24 | * The dynamic types are implemented as a stack machine. Handling a type 25 | * pops the top element, handles it which may involve pushing more types onto 26 | * the stack, and returns an element from the data segment if appropriate or 27 | * recurses. See the implementation for details. 28 | */ 29 | 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | #include "oi/types/dy.h" 38 | 39 | namespace oi::detail::exporters { 40 | 41 | class TypeCheckingWalker { 42 | public: 43 | struct VarInt { 44 | uint64_t value; 45 | }; 46 | struct SumIndex { 47 | uint64_t index; 48 | }; 49 | struct ListLength { 50 | uint64_t length; 51 | }; 52 | using Element = std::variant; 53 | 54 | TypeCheckingWalker(types::dy::Dynamic rootType, 55 | std::span buffer) 56 | : stack({rootType}), buf(buffer) { 57 | } 58 | 59 | std::optional advance(); 60 | 61 | private: 62 | std::stack stack; 63 | std::span buf; 64 | 65 | private: 66 | uint64_t popFront() { 67 | if (buf.empty()) { 68 | throw std::runtime_error("unexpected end of data segment"); 69 | } 70 | auto el = buf.front(); 71 | buf = buf.last(buf.size() - 1); 72 | return el; 73 | } 74 | }; 75 | 76 | } // namespace oi::detail::exporters 77 | -------------------------------------------------------------------------------- /test/integration/runner_common.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | struct OidOpts { 15 | boost::asio::io_context& ctx; 16 | std::string targetArgs; 17 | std::string scriptSource; 18 | }; 19 | 20 | struct OilOpts { 21 | boost::asio::io_context& ctx; 22 | std::string targetArgs; 23 | }; 24 | 25 | struct Proc { 26 | boost::asio::io_context& ctx; 27 | boost::process::child proc; 28 | boost::process::child tee_stdout; 29 | boost::process::child tee_stderr; 30 | }; 31 | 32 | struct OidProc { 33 | Proc target; 34 | Proc oid; 35 | }; 36 | 37 | class IntegrationBase : public ::testing::Test { 38 | protected: 39 | virtual ~IntegrationBase() = default; 40 | 41 | virtual std::string TmpDirStr() = 0; 42 | 43 | void TearDown() override; 44 | void SetUp() override; 45 | int exit_code(Proc& proc); 46 | std::optional writeCustomConfig( 47 | std::string_view filePrefix, std::string_view content); 48 | 49 | std::filesystem::path workingDir; 50 | 51 | std::string stdout_; 52 | std::string stderr_; 53 | 54 | /* 55 | * compare_json 56 | * 57 | * Compares two JSON objects for equality if "expect_eq" is true, or for 58 | * inequality if "expect_eq" is false. 59 | */ 60 | void compare_json(const boost::property_tree::ptree& expected_json, 61 | const boost::property_tree::ptree& actual_json, 62 | const std::string& full_key = "root", 63 | bool expect_eq = true); 64 | }; 65 | 66 | class OidIntegration : public IntegrationBase { 67 | protected: 68 | std::string TmpDirStr() override; 69 | 70 | OidProc runOidOnProcess(OidOpts opts, 71 | std::vector extra_args, 72 | std::string configPrefix, 73 | std::string configSuffix); 74 | }; 75 | 76 | class OilIntegration : public IntegrationBase { 77 | protected: 78 | std::string TmpDirStr() override; 79 | 80 | Proc runOilTarget(OilOpts opts, 81 | std::string configPrefix, 82 | std::string configSuffix); 83 | }; 84 | -------------------------------------------------------------------------------- /oi/type_graph/Printer.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #pragma once 17 | 18 | #include 19 | 20 | #include "NodeTracker.h" 21 | #include "Types.h" 22 | #include "Visitor.h" 23 | 24 | namespace oi::detail::type_graph { 25 | 26 | /* 27 | * Printer 28 | */ 29 | class Printer : public ConstVisitor { 30 | public: 31 | Printer(std::ostream& out, NodeTracker& tracker, size_t numTypes); 32 | 33 | void print(const Type& type); 34 | 35 | void visit(const Incomplete& i) override; 36 | void visit(const Class& c) override; 37 | void visit(const Container& c) override; 38 | void visit(const Primitive& p) override; 39 | void visit(const Enum& e) override; 40 | void visit(const Array& a) override; 41 | void visit(const Typedef& td) override; 42 | void visit(const Pointer& p) override; 43 | void visit(const Reference& r) override; 44 | void visit(const Dummy& d) override; 45 | void visit(const DummyAllocator& d) override; 46 | void visit(const CaptureKeys& d) override; 47 | 48 | private: 49 | void prefix(); 50 | [[nodiscard]] bool prefix(const Type& type); 51 | void print_param(const TemplateParam& param); 52 | void print_parent(const Parent& parent); 53 | void print_member(const Member& member); 54 | void print_function(const Function& function); 55 | void print_type(std::string_view header, const Type& type); 56 | void print_value(const std::string& value); 57 | void print_qualifiers(const QualifierSet& qualifiers); 58 | void print_enumerator(int64_t val, const std::string& name); 59 | static std::string align_str(uint64_t align); 60 | 61 | NodeTracker& tracker_; 62 | std::ostream& out_; 63 | int baseIndent_; 64 | int depth_ = -1; 65 | }; 66 | 67 | } // namespace oi::detail::type_graph 68 | -------------------------------------------------------------------------------- /flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "flake-utils": { 4 | "inputs": { 5 | "systems": "systems" 6 | }, 7 | "locked": { 8 | "lastModified": 1731533236, 9 | "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", 10 | "owner": "numtide", 11 | "repo": "flake-utils", 12 | "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", 13 | "type": "github" 14 | }, 15 | "original": { 16 | "owner": "numtide", 17 | "repo": "flake-utils", 18 | "type": "github" 19 | } 20 | }, 21 | "nixpkgs": { 22 | "locked": { 23 | "lastModified": 1752950548, 24 | "narHash": "sha256-NS6BLD0lxOrnCiEOcvQCDVPXafX1/ek1dfJHX1nUIzc=", 25 | "owner": "nixos", 26 | "repo": "nixpkgs", 27 | "rev": "c87b95e25065c028d31a94f06a62927d18763fdf", 28 | "type": "github" 29 | }, 30 | "original": { 31 | "owner": "nixos", 32 | "ref": "nixos-unstable", 33 | "repo": "nixpkgs", 34 | "type": "github" 35 | } 36 | }, 37 | "root": { 38 | "inputs": { 39 | "flake-utils": "flake-utils", 40 | "nixpkgs": "nixpkgs", 41 | "treefmt-nix": "treefmt-nix" 42 | } 43 | }, 44 | "systems": { 45 | "locked": { 46 | "lastModified": 1681028828, 47 | "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", 48 | "owner": "nix-systems", 49 | "repo": "default", 50 | "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", 51 | "type": "github" 52 | }, 53 | "original": { 54 | "owner": "nix-systems", 55 | "repo": "default", 56 | "type": "github" 57 | } 58 | }, 59 | "treefmt-nix": { 60 | "inputs": { 61 | "nixpkgs": [ 62 | "nixpkgs" 63 | ] 64 | }, 65 | "locked": { 66 | "lastModified": 1753006367, 67 | "narHash": "sha256-tzbhc4XttkyEhswByk5R38l+ztN9UDbnj0cTcP6Hp9A=", 68 | "owner": "numtide", 69 | "repo": "treefmt-nix", 70 | "rev": "421b56313c65a0815a52b424777f55acf0b56ddf", 71 | "type": "github" 72 | }, 73 | "original": { 74 | "owner": "numtide", 75 | "repo": "treefmt-nix", 76 | "type": "github" 77 | } 78 | } 79 | }, 80 | "root": "root", 81 | "version": 7 82 | } 83 | -------------------------------------------------------------------------------- /include/oi/result/SizedResult.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #ifndef INCLUDED_OI_RESULT_SIZED_ELEMENT_H 17 | #define INCLUDED_OI_RESULT_SIZED_ELEMENT_H 1 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | namespace oi::result { 25 | 26 | template 27 | struct SizedElement : public El { 28 | SizedElement(const El& el, size_t size); 29 | const El& inner() const; 30 | 31 | size_t size; 32 | }; 33 | 34 | template 35 | class SizedResult { 36 | private: 37 | using It = std::decay_t().begin())>; 38 | using ParentEl = std::decay_t().operator*())>; 39 | 40 | public: 41 | using Element = SizedElement; 42 | 43 | private: 44 | struct SizeHelper { 45 | size_t size = -1; 46 | size_t last_child = -1; 47 | }; 48 | 49 | public: 50 | class const_iterator { 51 | friend SizedResult; 52 | 53 | public: 54 | bool operator==(const const_iterator& that) const; 55 | bool operator!=(const const_iterator& that) const; 56 | const Element& operator*() const; 57 | const Element* operator->() const; 58 | const_iterator& operator++(); 59 | const_iterator operator++(int); 60 | 61 | private: 62 | const_iterator(It start, const It& end); 63 | const_iterator(It end); 64 | 65 | It data_; 66 | 67 | std::vector helpers_; 68 | size_t count_ = 0; 69 | std::optional next_; 70 | }; 71 | 72 | SizedResult(Res res); 73 | 74 | const_iterator begin() const; 75 | const_iterator end() const; 76 | 77 | private: 78 | Res res_; 79 | }; 80 | 81 | } // namespace oi::result 82 | 83 | #include "SizedResult-inl.h" 84 | #endif 85 | -------------------------------------------------------------------------------- /test/integration/std_deque.toml: -------------------------------------------------------------------------------- 1 | # TODO deque capacity: https://github.com/facebookexperimental/object-introspection/issues/637 2 | # 8 times the object size on 64-bit libstdc++; 16 times the object size or 4096 bytes, whichever is larger, on 64-bit libc++ 3 | includes = ["deque"] 4 | [cases] 5 | [cases.int_empty] 6 | param_types = ["const std::deque&"] 7 | setup = "return {};" 8 | expect_json = '[{"staticSize":80, "dynamicSize":0, "length":0, "capacity":0, "elementStaticSize":4}]' 9 | expect_json_v2 = '[{"size":656, "staticSize":80, "exclusiveSize":656, "length":0, "capacity":128}]' 10 | [cases.int_some] 11 | param_types = ["const std::deque&"] 12 | setup = "return {{1,2,3}};" 13 | expect_json = '[{"staticSize":80, "dynamicSize":12, "length":3, "capacity":3, "elementStaticSize":4}]' 14 | expect_json_v2 = '[{"size":656, "staticSize":80, "exclusiveSize":644, "length":3, "capacity":128}]' 15 | [cases.deque_int_empty] 16 | param_types = ["const std::deque>&"] 17 | setup = "return {};" 18 | expect_json = '[{"staticSize":80, "dynamicSize":0, "length":0, "capacity":0, "elementStaticSize":80}]' 19 | expect_json_v2 = '[{"size":624, "staticSize":80, "exclusiveSize":624, "length":0, "capacity":6}]' 20 | [cases.deque_int_some] 21 | param_types = ["const std::deque>&"] 22 | setup = "return {{{1,2,3},{},{4,5}}};" 23 | expect_json = '''[{ 24 | "staticSize":80, 25 | "dynamicSize":260, 26 | "length":3, 27 | "capacity":3, 28 | "elementStaticSize":80, 29 | "members":[ 30 | {"size":0, "staticSize":80, "dynamicSize":12, "length":3, "capacity":3, "elementStaticSize":4}, 31 | {"size":0, "staticSize":80, "dynamicSize":0, "length":0, "capacity":0, "elementStaticSize":4}, 32 | {"size":0, "staticSize":80, "dynamicSize":8, "length":2, "capacity":2, "elementStaticSize":4} 33 | ]}]''' 34 | expect_json_v2 = '''[{ 35 | "size":2352, 36 | "staticSize":80, 37 | "exclusiveSize":384, 38 | "length":3, 39 | "capacity":6, 40 | "members":[ 41 | {"size":656, "staticSize":80, "exclusiveSize":644, "length":3, "capacity":128}, 42 | {"size":656, "staticSize":80, "exclusiveSize":656, "length":0, "capacity":128}, 43 | {"size":656, "staticSize":80, "exclusiveSize":648, "length":2, "capacity":128} 44 | ]}]''' 45 | -------------------------------------------------------------------------------- /oi/type_graph/PassManager.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #include "PassManager.h" 17 | 18 | #include 19 | 20 | #include 21 | #include 22 | 23 | #include "NodeTracker.h" 24 | #include "Printer.h" 25 | #include "TypeGraph.h" 26 | 27 | template 28 | using ref = std::reference_wrapper; 29 | 30 | namespace oi::detail::type_graph { 31 | 32 | void Pass::run(TypeGraph& typeGraph, NodeTrackerHolder tracker) { 33 | fn_(typeGraph, tracker.get(typeGraph.size())); 34 | } 35 | 36 | void PassManager::addPass(Pass p) { 37 | passes_.push_back(std::move(p)); 38 | } 39 | 40 | namespace { 41 | void print(const TypeGraph& typeGraph, NodeTrackerHolder tracker) { 42 | if (!VLOG_IS_ON(1)) 43 | return; 44 | std::stringstream out; 45 | Printer printer{out, tracker.get(typeGraph.size()), typeGraph.size()}; 46 | for (const auto& type : typeGraph.rootTypes()) { 47 | printer.print(type); 48 | } 49 | 50 | // Long strings will be truncated by glog, use std::cerr instead 51 | std::cerr << "\n" << out.str(); 52 | } 53 | } // namespace 54 | 55 | const std::string separator = "----------------"; 56 | 57 | void PassManager::run(TypeGraph& typeGraph) { 58 | NodeTracker tracker; 59 | 60 | VLOG(1) << separator; 61 | VLOG(1) << "Parsed Type Graph:"; 62 | VLOG(1) << separator; 63 | print(typeGraph, tracker); 64 | VLOG(1) << separator; 65 | 66 | for (size_t i = 0; i < passes_.size(); i++) { 67 | auto& pass = passes_[i]; 68 | LOG(INFO) << "Running pass (" << i + 1 << "/" << passes_.size() 69 | << "): " << pass.name(); 70 | pass.run(typeGraph, tracker); 71 | VLOG(1) << separator; 72 | print(typeGraph, tracker); 73 | VLOG(1) << separator; 74 | } 75 | } 76 | 77 | } // namespace oi::detail::type_graph 78 | -------------------------------------------------------------------------------- /types/folly_iobuf_type.toml: -------------------------------------------------------------------------------- 1 | [info] 2 | type_name = "folly::IOBuf" 3 | ctype = "FOLLY_IOBUF_TYPE" 4 | header = "folly/io/IOBuf.h" 5 | 6 | # Old: 7 | typeName = "folly::IOBuf" 8 | matcher = "^folly::IOBuf$" 9 | ns = ["folly::IOBuf"] 10 | numTemplateParams = 0 11 | replaceTemplateParamIndex = [] 12 | 13 | [codegen] 14 | decl = """ 15 | void getSizeType(const %1% &container, size_t& returnArg); 16 | """ 17 | 18 | func = """ 19 | void getSizeType(const %1% &container, size_t& returnArg) 20 | { 21 | SAVE_SIZE(sizeof(%1%)); 22 | 23 | // We calculate the length of all IOBufs in the chain manually. 24 | // IOBuf has built-in computeChainCapacity()/computeChainLength() 25 | // functions which do this for us. But dead code optimization in TAO 26 | // caused these functions to be removed which causes relocation 27 | // errors. 28 | 29 | std::size_t fullLength = container.length(); 30 | std::size_t fullCapacity = container.capacity(); 31 | for (const folly::IOBuf* current = container.next(); current != &container; 32 | current = current->next()) { 33 | fullLength += current->length(); 34 | fullCapacity += current->capacity(); 35 | } 36 | SAVE_DATA(fullCapacity); 37 | SAVE_DATA(fullLength); 38 | 39 | SAVE_SIZE(fullCapacity); 40 | } 41 | """ 42 | 43 | traversal_func = """ 44 | // We calculate the length of all IOBufs in the chain manually. 45 | // IOBuf has built-in computeChainCapacity()/computeChainLength() 46 | // functions which do this for us. But dead code optimization in TAO 47 | // caused these functions to be removed which causes relocation 48 | // errors. 49 | 50 | std::size_t fullLength = container.length(); 51 | std::size_t fullCapacity = container.capacity(); 52 | for (const folly::IOBuf* current = container.next(); current != &container; 53 | current = current->next()) { 54 | fullLength += current->length(); 55 | fullCapacity += current->capacity(); 56 | } 57 | 58 | return returnArg.write(fullCapacity).write(fullLength); 59 | """ 60 | 61 | [[codegen.processor]] 62 | type = "types::st::VarInt" 63 | func = """ 64 | el.container_stats.emplace(result::Element::ContainerStats{ .capacity = std::get(d.val).value }); 65 | el.exclusive_size += el.container_stats->capacity; 66 | """ 67 | 68 | [[codegen.processor]] 69 | type = "types::st::VarInt" 70 | func = """ 71 | el.container_stats->length = std::get(d.val).value; 72 | """ 73 | -------------------------------------------------------------------------------- /oi/OICache.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #pragma once 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #include "oi/OICodeGen.h" 24 | #include "oi/OIParser.h" 25 | #include "oi/SymbolService.h" 26 | 27 | namespace oi::detail { 28 | 29 | class OICache { 30 | public: 31 | OICache(const OICodeGen::Config& generatorConfig) 32 | : generatorConfig(generatorConfig) { 33 | } 34 | 35 | std::filesystem::path basePath{}; 36 | std::shared_ptr symbols{}; 37 | bool downloadedRemote = false; 38 | bool enableUpload = false; 39 | bool enableDownload = false; 40 | bool abortOnLoadFail = false; 41 | 42 | // We need the generator config to download the cache 43 | // with the matching configuration. 44 | const OICodeGen::Config& generatorConfig; 45 | 46 | // Entity is used to index the `extensions` array 47 | // So we must keep the Entity enum and `extensions` array in sync! 48 | enum class Entity { 49 | Source, 50 | Object, 51 | FuncDescs, 52 | GlobalDescs, 53 | TypeHierarchy, 54 | PaddingInfo, 55 | MAX 56 | }; 57 | static constexpr std::array(Entity::MAX)> 58 | extensions{".cc", ".o", ".fd", ".gd", ".th", ".pd"}; 59 | 60 | bool isEnabled() const { 61 | return !basePath.empty(); 62 | } 63 | std::optional getPath(const irequest&, Entity) const; 64 | template 65 | bool store(const irequest&, Entity, const T&); 66 | template 67 | bool load(const irequest&, Entity, T&); 68 | 69 | bool upload(const irequest& req); 70 | bool download(const irequest& req); 71 | 72 | private: 73 | std::string generateRemoteHash(const irequest&); 74 | }; 75 | 76 | } // namespace oi::detail 77 | -------------------------------------------------------------------------------- /oi/type_graph/DrgnExporter.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | #pragma once 17 | 18 | #include 19 | #include 20 | 21 | #include "NodeTracker.h" 22 | #include "PassManager.h" 23 | #include "Types.h" 24 | #include "Visitor.h" 25 | 26 | extern "C" { 27 | #include 28 | } 29 | 30 | struct TypeHierarchy; 31 | 32 | namespace oi::detail::type_graph { 33 | 34 | /* 35 | * DrgnExporter 36 | * 37 | * Converts Type Graph nodes into minimal drgn_type structs and populates a 38 | * TypeHierarchy with them, for use by TreeBuilder v1. 39 | */ 40 | class DrgnExporter : public Visitor { 41 | public: 42 | static Pass createPass(TypeHierarchy& th, std::list& drgnTypes); 43 | 44 | DrgnExporter(TypeHierarchy& th, std::list& drgnTypes) 45 | : th_(th), drgnTypes_(drgnTypes) { 46 | } 47 | 48 | drgn_type* accept(Type&); 49 | drgn_type* visit(Incomplete&) override; 50 | drgn_type* visit(Class&) override; 51 | drgn_type* visit(Container&) override; 52 | drgn_type* visit(Primitive&) override; 53 | drgn_type* visit(Enum&) override; 54 | drgn_type* visit(Array&) override; 55 | drgn_type* visit(Typedef&) override; 56 | drgn_type* visit(Pointer&) override; 57 | drgn_type* visit(Reference&) override; 58 | drgn_type* visit(Dummy&) override; 59 | drgn_type* visit(DummyAllocator&) override; 60 | drgn_type* visit(CaptureKeys&) override; 61 | 62 | private: 63 | drgn_type* makeDrgnType(enum drgn_type_kind kind, 64 | bool is_complete, 65 | enum drgn_primitive_type primitive, 66 | const Type& type); 67 | 68 | ResultTracker tracker_; 69 | TypeHierarchy& th_; 70 | std::list& drgnTypes_; 71 | }; 72 | 73 | } // namespace oi::detail::type_graph 74 | -------------------------------------------------------------------------------- /test/test_prune.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "oi/type_graph/Prune.h" 4 | #include "test/type_graph_utils.h" 5 | 6 | using type_graph::Prune; 7 | 8 | TEST(PruneTest, PruneClass) { 9 | test(Prune::createPass(), 10 | R"( 11 | [0] Class: MyClass (size: 8) 12 | Param 13 | Primitive: int32_t 14 | Param 15 | Value: "123" 16 | Primitive: int32_t 17 | Parent (offset: 0) 18 | [1] Class: MyParent (size: 4) 19 | Member: a (offset: 0) 20 | Primitive: int32_t 21 | Member: a (offset: 0) 22 | Primitive: int32_t 23 | Member: b (offset: 4) 24 | Primitive: int32_t 25 | Function: foo 26 | Function: bar 27 | )", 28 | R"( 29 | [0] Class: MyClass (size: 8) 30 | Member: a (offset: 0) 31 | Primitive: int32_t 32 | Member: b (offset: 4) 33 | Primitive: int32_t 34 | )"); 35 | } 36 | 37 | TEST(PruneTest, RecurseClassMember) { 38 | test(Prune::createPass(), 39 | R"( 40 | [0] Class: MyClass (size: 0) 41 | Member: xxx (offset: 0) 42 | [1] Class: ClassA (size: 12) 43 | Function: foo 44 | )", 45 | R"( 46 | [0] Class: MyClass (size: 0) 47 | Member: xxx (offset: 0) 48 | [1] Class: ClassA (size: 12) 49 | )"); 50 | } 51 | 52 | TEST(PruneTest, RecurseClassChild) { 53 | test(Prune::createPass(), 54 | R"( 55 | [0] Class: MyClass (size: 0) 56 | Child 57 | [1] Class: ClassA (size: 12) 58 | Function: foo 59 | )", 60 | R"( 61 | [0] Class: MyClass (size: 0) 62 | Child 63 | [1] Class: ClassA (size: 12) 64 | )"); 65 | } 66 | 67 | TEST(PruneTest, PruneContainer) { 68 | test(Prune::createPass(), 69 | R"( 70 | [0] Container: std::vector (size: 24) 71 | Param 72 | Primitive: int32_t 73 | Param 74 | Value: "123" 75 | Primitive: int32_t 76 | Underlying 77 | [1] Class: vector (size: 24) 78 | Parent (offset: 0) 79 | [2] Class: MyParent (size: 4) 80 | Member: a (offset: 0) 81 | Primitive: int32_t 82 | Member: a (offset: 0) 83 | Primitive: int32_t 84 | Member: b (offset: 4) 85 | Primitive: int32_t 86 | )", 87 | R"( 88 | [0] Container: std::vector (size: 24) 89 | Param 90 | Primitive: int32_t 91 | Param 92 | Value: "123" 93 | Primitive: int32_t 94 | )"); 95 | } 96 | -------------------------------------------------------------------------------- /test/integration/std_unordered_set_custom_operator.toml: -------------------------------------------------------------------------------- 1 | definitions = ''' 2 | 3 | template 4 | class CustomIntHasher 5 | { 6 | double d[N]; 7 | public: 8 | size_t operator() (int const& key) const 9 | { 10 | return std::hash{}(key); 11 | } 12 | }; 13 | 14 | template 15 | class CustomEqualFnInt 16 | { 17 | double d[N]; 18 | public: 19 | bool operator() (int const& t1, int const& t2) const 20 | { 21 | return t1 == t2; 22 | } 23 | }; 24 | 25 | struct Foo { 26 | std::unordered_set m1; 27 | std::unordered_set> m2; 28 | std::unordered_set, CustomEqualFnInt<8>> m3; 29 | std::unordered_set, CustomEqualFnInt<8>> m4; 30 | }; 31 | ''' 32 | includes = ["unordered_set"] 33 | 34 | [cases] 35 | [cases.a] 36 | param_types = ["const Foo&"] 37 | setup = ''' 38 | Foo foo; 39 | 40 | for (int i = 0; i < 3; i++) { 41 | foo.m1.insert(i); 42 | } 43 | 44 | for (int i = 0; i < 5; i++) { 45 | foo.m2.insert(i); 46 | } 47 | 48 | for (int i = 0; i < 7; i++) { 49 | foo.m3.insert(i); 50 | } 51 | 52 | for (int i = 0; i < 9; i++) { 53 | foo.m4.insert(i); 54 | } 55 | 56 | return {foo}; 57 | ''' 58 | expect_json = '''[{ 59 | "staticSize":480, 60 | "dynamicSize":704, 61 | "members":[ 62 | {"name":"m1", "staticSize":56, "dynamicSize":140, "length":3, "capacity":3, "elementStaticSize":12}, 63 | {"name":"m2", "staticSize":120, "dynamicSize":164, "length":5, "capacity":5, "elementStaticSize":12}, 64 | {"name":"m3", "staticSize":120, "dynamicSize":188, "length":7, "capacity":7, "elementStaticSize":12}, 65 | {"name":"m4", "staticSize":184, "dynamicSize":212, "length":9, "capacity":9, "elementStaticSize":12} 66 | ]}]''' 67 | expect_json_v2 = '''[{ 68 | "staticSize":480, 69 | "exclusiveSize":0, 70 | "size":1472, 71 | "members":[ 72 | {"name":"m1", "staticSize":56, "exclusiveSize":220, "size":232,"length":3, "capacity":3}, 73 | {"name":"m2", "staticSize":120, "exclusiveSize":324, "size":344, "length":5, "capacity":5}, 74 | {"name":"m3", "staticSize":120, "exclusiveSize":364, "size":392, "length":7, "capacity":7}, 75 | {"name":"m4", "staticSize":184, "exclusiveSize":468, "size":504, "length":9, "capacity":9} 76 | ]}]''' 77 | -------------------------------------------------------------------------------- /types/sorted_vec_set_type.toml: -------------------------------------------------------------------------------- 1 | [info] 2 | type_name = "folly::sorted_vector_set" 3 | header = "folly/sorted_vector_types.h" 4 | ctype = "SORTED_VEC_SET_TYPE" 5 | stub_template_params = [1,2] 6 | underlying_container_index = 4 7 | 8 | # Old 9 | typeName = "folly::sorted_vector_set<" 10 | ns = ["namespace std", "folly::sorted_vector_set"] 11 | replaceTemplateParamIndex = [1,2] 12 | underlyingContainerIndex = 4 13 | 14 | [codegen] 15 | decl = """ 16 | template 17 | void getSizeType(const %1% &container, size_t& returnArg); 18 | """ 19 | 20 | func = """ 21 | template 22 | void getSizeType(const %1% &containerAdapter, size_t& returnArg) 23 | { 24 | SAVE_DATA((uintptr_t)&containerAdapter); 25 | 26 | // Underlying container is grabbed by recursion, store only the exclusive size. 27 | SAVE_SIZE(sizeof(%1%) - sizeof(Container)); 28 | 29 | const Container &container = containerAdapter.get_container(); 30 | getSizeType(container, returnArg); 31 | } 32 | """ 33 | 34 | traversal_func = ''' 35 | auto tail = returnArg.write((uintptr_t)&container) 36 | .write(container.capacity()) 37 | .write(container.size()); 38 | 39 | for (const auto& el : container) { 40 | tail = tail.delegate([&ctx, &el](auto ret) { 41 | return OIInternal::getSizeType(ctx, el, ret); 42 | }); 43 | } 44 | 45 | return tail.finish(); 46 | ''' 47 | 48 | [[codegen.processor]] 49 | type = "types::st::VarInt" 50 | func = "el.pointer = std::get(d.val).value;" 51 | 52 | [[codegen.processor]] 53 | type = "types::st::VarInt" 54 | func = ''' 55 | el.container_stats.emplace(result::Element::ContainerStats{ .capacity = std::get(d.val).value }); 56 | ''' 57 | 58 | [[codegen.processor]] 59 | type = "types::st::List::type>" 60 | func = """ 61 | static constexpr auto childField = make_field("[]"); 62 | 63 | auto list = std::get(d.val); 64 | el.container_stats->length = list.length; 65 | el.exclusive_size += (el.container_stats->capacity - el.container_stats->length) * sizeof(T0); 66 | 67 | for (size_t i = 0; i < list.length; i++) 68 | stack_ins(childField); 69 | """ 70 | 71 | -------------------------------------------------------------------------------- /test/integration/std_unordered_multiset_custom_operator.toml: -------------------------------------------------------------------------------- 1 | definitions = ''' 2 | 3 | template 4 | class CustomIntHasher 5 | { 6 | double d[N]; 7 | public: 8 | size_t operator() (int const& key) const 9 | { 10 | return std::hash{}(key); 11 | } 12 | }; 13 | 14 | template 15 | class CustomEqualFnInt 16 | { 17 | double d[N]; 18 | public: 19 | bool operator() (int const& t1, int const& t2) const 20 | { 21 | return t1 == t2; 22 | } 23 | }; 24 | 25 | struct Foo { 26 | std::unordered_multiset m1; 27 | std::unordered_multiset> m2; 28 | std::unordered_multiset, CustomEqualFnInt<8>> m3; 29 | std::unordered_multiset, CustomEqualFnInt<8>> m4; 30 | }; 31 | ''' 32 | includes = ["unordered_set"] 33 | 34 | [cases] 35 | [cases.a] 36 | param_types = ["const Foo&"] 37 | setup = ''' 38 | Foo foo; 39 | 40 | for (int i = 0; i < 3; i++) { 41 | foo.m1.insert(i); 42 | } 43 | 44 | for (int i = 0; i < 5; i++) { 45 | foo.m2.insert(i); 46 | } 47 | 48 | for (int i = 0; i < 7; i++) { 49 | foo.m3.insert(i); 50 | } 51 | 52 | for (int i = 0; i < 9; i++) { 53 | foo.m4.insert(i); 54 | } 55 | 56 | return {foo}; 57 | ''' 58 | expect_json = '''[{ 59 | "staticSize":480, 60 | "dynamicSize":704, 61 | "members":[ 62 | {"name":"m1", "staticSize":56, "dynamicSize":140, "length":3, "capacity":3, "elementStaticSize":12}, 63 | {"name":"m2", "staticSize":120, "dynamicSize":164, "length":5, "capacity":5, "elementStaticSize":12}, 64 | {"name":"m3", "staticSize":120, "dynamicSize":188, "length":7, "capacity":7, "elementStaticSize":12}, 65 | {"name":"m4", "staticSize":184, "dynamicSize":212, "length":9, "capacity":9, "elementStaticSize":12} 66 | ]}]''' 67 | expect_json_v2 = '''[{ 68 | "staticSize":480, 69 | "exclusiveSize":0, 70 | "size":1472, 71 | "members":[ 72 | {"name":"m1", "staticSize":56, "exclusiveSize":220, "size":232, "length":3, "capacity":3}, 73 | {"name":"m2", "staticSize":120, "exclusiveSize":324, "size":344, "length":5, "capacity":5}, 74 | {"name":"m3", "staticSize":120, "exclusiveSize":364, "size":392, "length":7, "capacity":7}, 75 | {"name":"m4", "staticSize":184, "exclusiveSize":468, "size":504, "length":9, "capacity":9} 76 | ]}]''' 77 | -------------------------------------------------------------------------------- /test/integration/std_set_custom_comparator.toml: -------------------------------------------------------------------------------- 1 | definitions = ''' 2 | struct CustomComparator { 3 | bool operator()(const int& left, const int& right) const { 4 | return left < right; 5 | } 6 | }; 7 | 8 | struct SmallSizedCustomComparator { 9 | double a; 10 | bool operator()(const int& left, const int& right) const { 11 | return left < right; 12 | } 13 | }; 14 | 15 | struct BigSizedCustomComparator { 16 | double d[1000]; 17 | bool operator()(const int& left, const int& right) const { 18 | return left < right; 19 | } 20 | }; 21 | 22 | struct Foo { 23 | std::set m1; 24 | std::set m2; 25 | std::set m3; 26 | std::set m4; 27 | }; 28 | 29 | ''' 30 | includes = ["set", "functional"] 31 | 32 | [cases] 33 | [cases.a] 34 | param_types = ["const Foo&"] 35 | setup = ''' 36 | Foo foo; 37 | 38 | for (int i = 0; i < 3; i++) { 39 | foo.m1.insert(i); 40 | } 41 | 42 | for (int i = 0; i < 5; i++) { 43 | foo.m2.insert(i); 44 | } 45 | 46 | for (int i = 0; i < 7; i++) { 47 | foo.m3.insert(i); 48 | } 49 | 50 | for (int i = 0; i < 9; i++) { 51 | foo.m4.insert(i); 52 | } 53 | 54 | return {foo}; 55 | ''' 56 | expect_json = '''[{ 57 | "staticSize":8184, 58 | "dynamicSize":288, 59 | "members":[ 60 | {"name":"m1", "staticSize":48, "dynamicSize":36, "length":3, "capacity":3, "elementStaticSize":12}, 61 | {"name":"m2", "staticSize":48, "dynamicSize":60, "length":5, "capacity":5, "elementStaticSize":12}, 62 | {"name":"m3", "staticSize":48, "dynamicSize":84, "length":7, "capacity":7, "elementStaticSize":12}, 63 | {"name":"m4", "staticSize":8040, "dynamicSize":108, "length":9, "capacity":9, "elementStaticSize":12} 64 | ]}]''' 65 | expect_json_v2 = '''[{ 66 | "staticSize":8184, 67 | "exclusiveSize": 0, 68 | "size": 9144, 69 | "members":[ 70 | {"name":"m1", "staticSize":48, "exclusiveSize": 156, "size": 168, "length": 3, "capacity": 3}, 71 | {"name":"m2", "staticSize":48, "exclusiveSize": 228, "size": 248, "length": 5, "capacity": 5}, 72 | {"name":"m3", "staticSize":48, "exclusiveSize": 300, "size": 328, "length": 7, "capacity": 7}, 73 | {"name":"m4", "staticSize":8040, "exclusiveSize": 8364, "size": 8400, "length": 9, "capacity": 9} 74 | ]}]''' 75 | -------------------------------------------------------------------------------- /test/integration/std_unordered_map_custom_operator.toml: -------------------------------------------------------------------------------- 1 | definitions = ''' 2 | 3 | template 4 | class CustomIntHasher 5 | { 6 | double d[N]; 7 | public: 8 | size_t operator() (int const& key) const 9 | { 10 | return std::hash{}(key); 11 | } 12 | }; 13 | 14 | template 15 | class CustomEqualFnInt 16 | { 17 | double d[N]; 18 | public: 19 | bool operator() (int const& t1, int const& t2) const 20 | { 21 | return t1 == t2; 22 | } 23 | }; 24 | 25 | struct Foo { 26 | std::unordered_map m1; 27 | std::unordered_map> m2; 28 | std::unordered_map, CustomEqualFnInt<8>> m3; 29 | std::unordered_map, CustomEqualFnInt<8>> m4; 30 | }; 31 | ''' 32 | includes = ["unordered_map"] 33 | 34 | [cases] 35 | [cases.a] 36 | param_types = ["const Foo&"] 37 | setup = ''' 38 | Foo foo; 39 | 40 | for (int i = 0; i < 3; i++) { 41 | foo.m1[i] = (i * 10); 42 | } 43 | 44 | for (int i = 0; i < 5; i++) { 45 | foo.m2[i] = (i * 10); 46 | } 47 | 48 | for (int i = 0; i < 7; i++) { 49 | foo.m3[i] = (i * 10); 50 | } 51 | 52 | for (int i = 0; i < 9; i++) { 53 | foo.m4[i] = (i * 10); 54 | } 55 | 56 | return {foo}; 57 | ''' 58 | expect_json = '''[{ 59 | "staticSize":480, 60 | "dynamicSize":1184, 61 | "members":[ 62 | {"name":"m1", "staticSize":56, "dynamicSize":200, "length":3, "capacity":3, "elementStaticSize":32}, 63 | {"name":"m2", "staticSize":120, "dynamicSize":264, "length":5, "capacity":5, "elementStaticSize":32}, 64 | {"name":"m3", "staticSize":120, "dynamicSize":328, "length":7, "capacity":7, "elementStaticSize":32}, 65 | {"name":"m4", "staticSize":184, "dynamicSize":392, "length":9, "capacity":9, "elementStaticSize":32} 66 | ]}]''' 67 | expect_json_v2 = '''[{ 68 | "staticSize":480, 69 | "exclusiveSize": 0, 70 | "size": 1952, 71 | "members":[ 72 | {"name":"m1", "staticSize":56, "exclusiveSize":220, "size":292, "length":3, "capacity":3}, 73 | {"name":"m2", "staticSize":120, "exclusiveSize":324, "size":444, "length":5, "capacity":5}, 74 | {"name":"m3", "staticSize":120, "exclusiveSize":364, "size":532, "length":7, "capacity":7}, 75 | {"name":"m4", "staticSize":184, "exclusiveSize":468, "size":684, "length":9, "capacity":9} 76 | ]}]''' 77 | -------------------------------------------------------------------------------- /types/f14_fast_set.toml: -------------------------------------------------------------------------------- 1 | [info] 2 | type_name = "folly::F14FastSet" 3 | stub_template_params = [1,2,3] 4 | ctype = "F14_SET" 5 | header = "folly/container/F14Set.h" 6 | 7 | # Old: 8 | typeName = "folly::F14FastSet<" 9 | ns = ["folly::F14FastSet"] 10 | numTemplateParams = 1 11 | replaceTemplateParamIndex = [1, 2] 12 | allocatorIndex = 3 13 | 14 | [codegen] 15 | decl = """ 16 | template 17 | void getSizeType(const %1% &container, size_t& returnArg); 18 | """ 19 | 20 | func = """ 21 | template 22 | void getSizeType(const %1% &container, size_t& returnArg) 23 | { 24 | size_t memorySize = container.getAllocatedMemorySize(); 25 | SAVE_SIZE(sizeof(%1%) + memorySize); 26 | 27 | SAVE_DATA(memorySize); 28 | SAVE_DATA(container.bucket_count()); 29 | SAVE_DATA(container.size()); 30 | 31 | // The double ampersand is needed otherwise this loop doesn't work with vector 32 | for (auto&& it: container) { 33 | getSizeType(it, returnArg); 34 | } 35 | } 36 | """ 37 | 38 | traversal_func = """ 39 | auto tail = returnArg 40 | .write((uintptr_t)container.getAllocatedMemorySize()) 41 | .write((uintptr_t)container.bucket_count()) 42 | .write(container.size()); 43 | 44 | for (auto &&entry: container) { 45 | tail = tail.delegate([&ctx, &entry](auto ret) { 46 | return OIInternal::getSizeType(ctx, entry, ret); 47 | }); 48 | } 49 | 50 | return tail.finish(); 51 | """ 52 | 53 | [[codegen.processor]] 54 | type = "types::st::VarInt" 55 | func = "el.pointer = std::get(d.val).value;" 56 | 57 | [[codegen.processor]] 58 | type = "types::st::VarInt" 59 | func = """ 60 | el.container_stats.emplace(result::Element::ContainerStats { 61 | .capacity = std::get(d.val).value, 62 | }); 63 | """ 64 | 65 | [[codegen.processor]] 66 | type = """ 67 | types::st::List::type> 68 | """ 69 | func = """ 70 | auto allocationSize = el.pointer.value(); 71 | el.pointer.reset(); 72 | 73 | auto list = std::get(d.val); 74 | el.container_stats->length = list.length; 75 | 76 | el.exclusive_size += allocationSize - list.length * sizeof(T0); 77 | 78 | static constexpr auto childField = make_field("[]"); 79 | for (size_t i = 0; i < list.length; i++) 80 | stack_ins(childField); 81 | """ 82 | -------------------------------------------------------------------------------- /types/f14_node_set.toml: -------------------------------------------------------------------------------- 1 | [info] 2 | type_name = "folly::F14NodeSet" 3 | stub_template_params = [1,2,3] 4 | ctype = "F14_SET" 5 | header = "folly/container/F14Set.h" 6 | 7 | # Old: 8 | typeName = "folly::F14NodeSet<" 9 | ns = ["folly::F14NodeSet"] 10 | numTemplateParams = 1 11 | replaceTemplateParamIndex = [1, 2] 12 | allocatorIndex = 3 13 | 14 | [codegen] 15 | decl = """ 16 | template 17 | void getSizeType(const %1% &container, size_t& returnArg); 18 | """ 19 | 20 | func = """ 21 | template 22 | void getSizeType(const %1% &container, size_t& returnArg) 23 | { 24 | size_t memorySize = container.getAllocatedMemorySize(); 25 | SAVE_SIZE(sizeof(%1%) + memorySize); 26 | 27 | SAVE_DATA(memorySize); 28 | SAVE_DATA(container.bucket_count()); 29 | SAVE_DATA(container.size()); 30 | 31 | // The double ampersand is needed otherwise this loop doesn't work with vector 32 | for (auto&& it: container) { 33 | getSizeType(it, returnArg); 34 | } 35 | } 36 | """ 37 | 38 | traversal_func = """ 39 | auto tail = returnArg 40 | .write((uintptr_t)container.getAllocatedMemorySize()) 41 | .write((uintptr_t)container.bucket_count()) 42 | .write(container.size()); 43 | 44 | for (auto &&entry: container) { 45 | tail = tail.delegate([&ctx, &entry](auto ret) { 46 | return OIInternal::getSizeType(ctx, entry, ret); 47 | }); 48 | } 49 | 50 | return tail.finish(); 51 | """ 52 | 53 | [[codegen.processor]] 54 | type = "types::st::VarInt" 55 | func = "el.pointer = std::get(d.val).value;" 56 | 57 | [[codegen.processor]] 58 | type = "types::st::VarInt" 59 | func = """ 60 | el.container_stats.emplace(result::Element::ContainerStats { 61 | .capacity = std::get(d.val).value, 62 | }); 63 | """ 64 | 65 | [[codegen.processor]] 66 | type = """ 67 | types::st::List::type> 68 | """ 69 | func = """ 70 | auto allocationSize = el.pointer.value(); 71 | el.pointer.reset(); 72 | 73 | auto list = std::get(d.val); 74 | el.container_stats->length = list.length; 75 | 76 | el.exclusive_size += allocationSize - list.length * sizeof(T0); 77 | 78 | static constexpr auto childField = make_field("[]"); 79 | for (size_t i = 0; i < list.length; i++) 80 | stack_ins(childField); 81 | """ 82 | --------------------------------------------------------------------------------