Previously we maintained three types of builds: a fully internal BUCK build, a
CMake build with modifications to use things from an internal toolchain, and an
open source CMake build.
As far as I'm concerned the intermediate build is not useful because our source
is readily available in both an internal and external form. Use cases as
follows:
1. BUCK build for distributing widely.
2. BUCK build for getting a static binary that can be run on any machine.
3. CMake build for primary development.
4. CMake build for external CI.
With the internal update to CentOS Stream 9 an unmodified CMake build now works
readily. This change patches up some things that were relying on system headers
that should have been vendored and cleans up drgn dependencies.
Test plan:
- It builds.
- TODO: Document CentOS 9 installation.
Start the migration from CircleCI to GitHub Actions with migrating the lint
job. Used the structure from @robandpdx to setup Nix and use a GitHub key.
Restructured the check from `nix flake check` to
`nix fmt; git diff --exit-code` so we get a full patch again.
Test plan:
- Submitted this PR with a formatting error. CI failed. Submitted without and
it passed.
Co-authored-by: Rob Anderson <robandpdx@github.com>
Summary:
Correct identification of packing.
Tests were modified to reflect the new behaviour. One test was removed as it was bogus - the flattener pass runs before the alignmentcalc pass and therefore the layout in the test could never happen (i.e. it has a hierarchy).
Reviewed By: JakeHillion
Differential Revision: D53815661
Summary:
tbv2: remove unnecessary copy in Element
`IntrospectionResult::const_iterator` iterates through the `Element`s in an
`IntrospectionResult`. `Element` currently copies the `type_path` which is a
`std::vector<string_view>` every time the iterator is incremented. This is
unnecessary as the data in the vector only changes slightly between iterations.
This change changes the `type_path` field in `Element` to a
`std::span<const std::string_view>`. Doing this previously caused SEGVs because
of the iterator's potential to be copied. To make it possible we do two things:
1. Make all copies explicit using a clone interface as in `ContainerInfo`. This
prevents accidental copies of an expensive structure.
2. After calling the copy constructor in `clone()` update the `span` in `next_`
to point at the newly copied structure.
Moves are fine because the `span` points at the allocation of the `vector`, not
the vector itself.
Test Plan:
- CI
- `FILTER='OilIntegration.*' make test`
- Ran `OilgenIntegration.std_vector_vector_int_some` which SEGVd with the
`span` change before and now doesn't. This now passes cleanly with ASAN
enabled on the target, though isn't available in `main` (only works on my
machine).
Differential Revision: D53472595
Pulled By: JakeHillion
Summary: Adding support for the nullptr clang type (clang::BuiltinType::NullPtr). While there I augmented the exception message to include the type name that is missing.
Differential Revision: D53272742
CodeGen v2 permits template parameters to be qualified. This means that if we
call `make_field` with a template parameter it will be qualified. However, we
don't qualify the types when generating meta functions such as `NameProvider`
and `TypeHandler`. This means these qualified types don't match up with the
expected type.
Use `std::decay_t` when forwarding the type to `NameProvider` and `TypeHandler`
so they're always the base type that they were generated with. Most of this is
covered by `make_field`, but there are direct references to `TypeHandler<Ctx,
T>` in a lot of `TypeHandler::type` fields. Fix the problematic types manually
for now, there may need to be a better solution with meta functions for this in
the future.
Test Plan:
- CI
- Added a test for `std::unique_ptr<const uint64_t>` to exercise this. Failed
before, passes after.
- Added a test for `std::unique_ptr<const std::vector<uint64_t>>` to test a
non-primitive type. Failed before, passes after.
* fix string type sso computation
* fix rest of sbo/sso calculation
* make placement of uintptr_t cast consistent
* separate check-inline into function
Summary:
OI was previously using `std::regex_match` to match container names. This was bad because `libstdc++`'s implementation of regex is awful. In the case of limited inlining it was causing a stack overflow when running CodeGen for large types (I think types with large names but I never got to the bottom of it).
Replace this with the competent `boost::regex_match` that we already have a dependency on.
Reviewed By: ajor
Differential Revision: D53002752
Thrift isset was failing with a SEGV if the struct contained padding. This is
because we indexed the `isset_indexes` data structure using our field index
rather than the index of the field in Thrift. This then gave a rubbish index
for any exceeding which happens if we have added padding in the middle of the
struct, and this index was looked up in the bitset which can cause a SEGV.
Track a new index `thriftFieldIdx` which is only incremented if we've looked up
a Thrift index.
Namespaced the generated Thrift structs while I was there. This isn't necessary
anymore but cleans things up.
Test plan:
- Added a test case with lots of padding. These don't run in the CI but it
passes locally.
- `FILTER='OilIntegration.*' make test` - no failures
- `FILTER='OidIntegration.*' make test` - no new failures
Previously AlignmentCalc calculates the alignment and sets packing for every
type except a member with explicit alignment. Change this to check whether an
alignment has been previously set for a type before calculating it. Use this in
ClangTypeParser where the full alignment of the type is available.
Remove explicitly aligning members by the type because that was previously
reserved for members with explicit alignment. AlignmentCalc will correctly
align a member to the underlying type without this. Explicit member alignment
is still missing, as before this change.
Test plan:
- CI
- Too little. Gets further into a production type than without this change.
A previous change enabled running OIL tests with specific features enabled.
This highlighted that pointer code generation under TreeBuilder-v2 was very
broken. This change updates pointer code generation to work and enables the
skipped tests. All enabled tests need `expected_json_v2` added to them due to
formatting differences.
Reformatted and rewrote the basic type handler that handles primitives and
pointers. Removed the reliance on `features` to decide whether to generate for
TreeBuilder-v2 as the intermediate features have been removed. Pointers are
treated as containers with a capacity of 1 and a length of 0 if null/a cycle
and 1 if followed. This holds for void pointers where, although they aren't
followed, the length is still set.
There were a couple of other changes needed to enable these tests on TBv2 that
aren't worth their own issues and PRs, I sneaked them in here.
Extra changes:
- Added `Pointer` and `Reference` to TopoSorter so they generate
`NameProvider` instances. It might be worth visiting the graph differently
for `NameProvider` as it requires so many instances that others generators do
not. Will consider that in the future.
- Follow typedefs when calculating exclusive size for a type.
Closes#458.
Test plan:
- CI
- Enabled previously disabled tests.
Some of the logic that makes Thrift isset work for TreeBuilder-v2 in DrgnParser
(JIT OIL) wasn't ported to ClangTypeParser meaning it doesn't work in
Ahead-of-Time (AoT) OIL.
Add the template parameter name reconstruction for enum values to
ClangTypeParser.
Test plan:
- Tested with Thrift isset enabled on an internal type. Doesn't build before,
does build after.
Support the capture-thrift-isset feature with TreeBuilder-v2. Fairly minor
changes here except the type of the Enum in a template parameter now matters.
We follow the previous behaviour of capturing a value for each field in a
struct that has an `isset_bitset`. This value is a VarInt captured before the
C++ contents of the member. It has 3 values: 0 (not set), 1 (set), and 2
(unavailable). These are handled by the processor and represented in the output
as `false`, `true`, and `std::nullopt_t` respectively.
Changes:
- Add a simple Thrift isset processor before any fields that have Thrift isset.
- Store the fully qualified names of enum types in DrgnParser - it already
worked out this information anyway for naming the values and this is
consistent with classes.
- Forward all enum template parameters under their input name under the
assumption that they will all be policy type things like `IssetBitsetOption`.
This could turn out to be wrong.
Test plan:
- CI (doesn't test thrift changes but covers other regressions)
- Updated Thrift enum tests for new format.
- `FILTER='OilIntegration.*' make test` - Thrift tests failed before, succeed
after.
Previously we tested different feature flags by using the `cli_options` field
in the test `.toml`. This works for OID but doesn't work for JIT OIL and won't
work for AoT OIL when those tests get added.
This change adds a new higher level `features` field to the test `.toml` which
adds the features to the config file as a prefix. This works with all methods
of generation.
Change the existing `cli_options` features to `features` except for where
they're testing something specific. Enable tests that were previously disabled
for OIL but only didn't work because of not being able to enable features.
Change pointer tests that are currently broken for OIL from `oil_disable` to
`oil_skip` - they can work, but codegen is broken for them at the minute.
Test plan:
- CI
- `make test` is no worse
C++ has a concept of Primitive which holds in the type graph. However we don't
currently expose this information to the end user. Expose this from the OIL
iterator to allow future features like primitive rollups.
This affects containers like maps which have a fake `[]` element with no type.
They use this to group together the key/value in a map and to account for any
per element storage overhead. Currently the decision is to make the fake `[]`
element a primitive if all of its children are primitives. This allows for more
effective primitive rollups if that is implemented. This implementation detail
may be changed in future.
Test Plan:
- CI
- Updated simple tests.
Array members are currently being named "TODO" (whoops). Include arrays in
TopoSorter so each one can have a `NameProvider` generated in CodeGen. Then
pass array elements through `make_field`.
Test plan:
- CI
- Add array member names to an array test.
`std::list` has per element overhead for the individual heap allocations. This
was already calculated in the container implementation but not used. Allocate
the overhead of each element in the `std::list` to the `std::list` itself as
with other sequential containers.
Test Plan:
- CI
- Updated test cases
This test is a bit odd, but this change adds the full set of size/member checks
for the hierarchy for TreeBuilder v2 and enables it.
Closes#304
Test Plan:
- CI
TreeBuilder did not consider a zero-length array like a container and
never read the array's sizeof stored in the data buffer, leading to a
mismatch between bytes written vs read out of the buffer.
Now, `TreeBuilder::isContainer` does consider zero-length array like
a container and properly consume all the object sizes in the buffer.
The recursive template implemented for `validate_size` does not support
incomplete types, like zero-length array.
By splitting the `validate_size` struct in two parts:
1. `validate_size_eq` that does the actual size check, and
2. `validate_size` that preserves the previous interface and inherit
from `validate_size_eq`,
we get the same interface and feature than previously, but without the
recursive template that doesn't support incomplete types.
Type names of optional elements were accidentally left as todo. Update
`std::optional` to use `make_field` and correctly name its elements.
Test Plan:
- CI
- Updated the integration tests to test the names.
Summary:
We have a good type representation in the Type Graph of an incomplete type and
the underlying type that represents. However, this incomplete type still ends
up in the generated code as `void` which loses information. For example, a
container that can't contain void may fail to compile because it was
initialised with `void` but really its because the type it was supposed to be
initialised with (say, `Foo`) had incomplete debug information.
This change identifies that a type is incomplete in the output by generating it
as an incomplete type `struct Incomplete<struct Foo>`. This allows us to name
the type correctly in the TreeBuilder output and filter for incomplete types,
as well as getting appropriate compiler errors if it mustn't be incomplete.
Test Plan:
- CI
- Added a unit test to namegen.
- Enabled and added an extra pointers_incomplete test.
This change is tricky to test because it isn't really user visible. The types
still use their `inputName` which is unchanged in any successful output - this
change is used so the compiler fails with a more detailed error.
Add the option to calculate total size (inclusive size) by wrapping the
existing iterator. This change provides a new iterator, `SizedIterator`, which
wraps an existing iterator and adds a new field `size` to the output element.
This is achieved with a two pass algorithm on the existing iterator:
1. Gather metadata for each element. This includes the total size up until that
element and the range of elements that should be included in the size.
2. Return the result from the underlying iterator with the additional
field.
This algorithm is `O(N)` time on the number of elements in the iterator and
`O(N)` time, storing 16 bytes per element. This isn't super expensive but is a
lot more than the current algorithm which requires close to constant space.
Because of this I've implemented it as a wrapper on the iterator rather than on
by default, though it is now on in every one of our integration test cases.
Test plan:
- Added to the integration tests for full coverage.
Remove the CodeGen v1 sections of the CI config because both OID and OIL use
CodeGen v2.
We were missing running any test that wasn't `Oi{d,l}Integration.*` before.
This now runs the unit tests again and requires a minor fix to one unit test.
Test plan:
- CI
Attempting to complete a type which can't be completed currently fails oilgen.
For incomplete arrays, which we know are not possible to complete, return false
deliberately.
`requireCompleteType` likely needs to not fail in all cases in the future. For
now this works.
Test plan:
- `std::unique_ptr<long[]>` used to fail the generation. Now it can
successfully codegen.
Unlike DWARF, the Clang AST is capable of correctly calculating the alignment
for each member. If we do this then AlignmentCalc doesn't traverse into the
member to attempt to calculate the alignment.
This check might be wrong if the field has explicit alignment. That case can be
covered when we have proper integration testing and a repro.
Test plan:
- Without this lots of static asserts occur. With this it's okay.
Previously ClangTypeParser assumed all RecordTypes were structs. This is fine
for structs and classes but completely incorrect for unions. Check which type
it is and give type graph the correct one.
Test plan:
- Unions static assert without this change because their size/alignment is
wrong.
ClangTypeParser has emitted a duplicate type for `std::allocatr<int8_t>`.
Rather than fixing this, add the same check the compiler will do for the
duplicate templates that `addNames` emits. That is, `template<>
NameProvider<Foo>` will collide if `Foo` is used twice. We can do this by
adding a set of these strings for now. If this shows up regularly it will
likely make sense to deduplicate the type graph with a deduplication pass.
Test plan:
- Fixes the issue in prod. This change is quite logical.
folly::IOBuf does not have TreeBuilder v2 container support. Add it.
The implementation is a direct clone of v1. It still lacks tests.
Test Plan:
- It codegens on a prod type.
- No runtime testing... Bad form, I know.
- Issue created to add integration tests: https://github.com/facebookexperimental/object-introspection/issues/436
Summary:
oilgen: migrate to source parsing
Using debug information generated from partial source (that is, not the final
binary) has been insufficient to generally generate OIL code.
A particular example is pointers to templates:
```cpp
#include <oi/oi.h>
template <typename T>
struct Foo {
T t;
};
template <typename T>
struct Bar {
Foo<T>& f;
};
void foo(const Bar<int>& b) {
oi::introspect(b);
}
```
The pointer/reference to `Foo<int>` appears in DWARF with
`DW_AT_declaration(true)` because it could be specialised before its usage.
However, with OIL, we are creating an implicit usage site in the
`oi::introspect` call that the compiler is unable to see.
This change reworks OILGen to work from a Clang command line rather than debug
information. We setup and run a compiler on the source, giving us access to an
AST and Semantic Analyser. We then:
- Find the `oi::introspect` template.
- Iterate through each of its callsites for their type.
- Run `ClangTypeParser::parse` on each type.
- Run codegen.
- Compile into an object file.
Having access to the semantic analyser allows us to forcefully complete a type,
as it would be if it was used in the initial code.
Test Plan:
hope
`buck2 run fbcode//mode/opt fbcode//object-introspection/oil/examples/compile-time:compile-time`
Reviewed By: tyroguru
Differential Revision: D51854477
Pulled By: JakeHillion
Currently we rely on `SymbolService::getTypeName` for getting the hash that's
included in the generated function's name. The value of this must stay the same
to match with the value expected by OIDebugger - changing it causes failure to
relocate when attaching with OID and JIT OIL.
Calculate this name in the `codegenFromDrgn` method and pass it through where
appropriate rather than passing the `drgn_type` itself through.
We don't need to name the type like that when using AoT OIL. Let's
hash the linkage name instead as that is more unique.
Test Plan:
- CI
We previously moved container identification later in CodeGen in order
to preserve information for AlignmentCalc.
However, Flattener needs to know if a class is a container in order to
apply its special handling for this case.
This new approach moves container identification in front of Flattener,
but has Container own a type node, representing its layout. This
underlying type node can be used for calculating a container's
alignment in a later pass.