This commit is contained in:
Jake Hillion 2023-08-16 12:40:36 -07:00 committed by Jake Hillion
parent 57210cf06c
commit 5071519e45
103 changed files with 2566 additions and 481 deletions

View File

@ -334,9 +334,18 @@ target_link_libraries(treebuilder
## OI Outputs
### Object Introspection as a Library (OIL)
add_library(oil oi/OILibrary.cpp oi/OILibraryImpl.cpp)
add_library(oil
oi/IntrospectionResult.cpp
oi/exporters/ParsedData.cpp
)
target_link_libraries(oil folly_headers)
target_include_directories(oil PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)
target_link_libraries(oil oicore)
add_library(oil_jit
oi/OILibrary.cpp
oi/OILibraryImpl.cpp
)
target_link_libraries(oil_jit oicore oil)
### Object Introspection as a Library Generator (OILGen)
add_executable(oilgen

View File

@ -0,0 +1,78 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#if defined(INCLUDED_OI_INTROSPECTIONRESULT_INL_H) || \
!defined(INCLUDED_OI_INTROSPECTIONRESULT_H)
static_assert(false,
"IntrospectionResult-inl.h provides inline declarations for "
"IntrospectionResult.h and should only "
"be included by IntrospectionResult.h");
#endif
#define INCLUDED_OI_INTROSPECTIONRESULT_INL_H 1
#include <cassert>
#include "IntrospectionResult.h"
namespace oi {
inline IntrospectionResult::IntrospectionResult(std::vector<uint8_t> buf,
exporters::inst::Inst inst)
: buf_(std::move(buf)), inst_(inst) {
}
inline IntrospectionResult::const_iterator::const_iterator(
std::vector<uint8_t>::const_iterator data, exporters::inst::Inst type)
: data_(data), stack_({type}) {
}
inline IntrospectionResult::const_iterator::const_iterator(
std::vector<uint8_t>::const_iterator data)
: data_(data) {
}
inline IntrospectionResult::const_iterator IntrospectionResult::cbegin() const {
return ++const_iterator{buf_.cbegin(), inst_};
}
inline IntrospectionResult::const_iterator IntrospectionResult::cend() const {
return {buf_.cend()};
}
inline IntrospectionResult::const_iterator
IntrospectionResult::const_iterator::operator++(int) {
auto old = *this;
operator++();
return old;
}
inline const result::Element& IntrospectionResult::const_iterator::operator*()
const {
assert(next_);
return *next_;
}
inline const result::Element* IntrospectionResult::const_iterator::operator->()
const {
return &*next_;
}
inline bool IntrospectionResult::const_iterator::operator==(
const IntrospectionResult::const_iterator& that) const {
return this->data_ == that.data_ && !this->next_.has_value() &&
!that.next_.has_value(); // TODO: is this sufficient? kind of hacky as
// this only works for comparing to .end()
}
inline bool IntrospectionResult::const_iterator::operator!=(
const IntrospectionResult::const_iterator& that) const {
return !(*this == that);
}
} // namespace oi

View File

@ -0,0 +1,70 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef INCLUDED_OI_INTROSPECTIONRESULT_H
#define INCLUDED_OI_INTROSPECTIONRESULT_H 1
#include <oi/exporters/inst.h>
#include <oi/result/Element.h>
#include <oi/types/dy.h>
#include <cstdint>
#include <optional>
#include <span>
#include <stack>
#include <string_view>
#include <vector>
namespace oi {
class IntrospectionResult {
public:
class const_iterator {
friend class IntrospectionResult;
public:
bool operator==(const const_iterator& that) const;
bool operator!=(const const_iterator& that) const;
const result::Element& operator*() const;
const result::Element* operator->() const;
const_iterator& operator++();
const_iterator operator++(int);
private:
const_iterator(std::vector<uint8_t>::const_iterator data,
exporters::inst::Inst type);
const_iterator(std::vector<uint8_t>::const_iterator data);
std::vector<uint8_t>::const_iterator data_;
std::stack<exporters::inst::Inst> stack_;
std::optional<result::Element> next_;
std::vector<std::string_view> type_path_;
};
IntrospectionResult(std::vector<uint8_t> buf, exporters::inst::Inst inst);
const_iterator cbegin() const;
const_iterator cend() const;
private:
std::vector<uint8_t> buf_;
exporters::inst::Inst inst_;
};
} // namespace oi
#include <oi/IntrospectionResult-inl.h>
#endif

View File

@ -0,0 +1,44 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef INCLUDED_OI_EXPORTERS_JSON_H
#define INCLUDED_OI_EXPORTERS_JSON_H 1
#include <oi/IntrospectionResult.h>
#include <ostream>
namespace oi::exporters {
class Json {
public:
Json(std::ostream& out);
void print(const IntrospectionResult&);
void print(IntrospectionResult::const_iterator& it,
IntrospectionResult::const_iterator end);
void setPretty(bool pretty) {
pretty_ = pretty;
}
private:
bool pretty_ = false;
std::ostream& out_;
};
} // namespace oi::exporters
#endif

View File

@ -0,0 +1,80 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef INCLUDED_OI_EXPORTERS_PARSED_DATA_H
#define INCLUDED_OI_EXPORTERS_PARSED_DATA_H 1
#include <oi/types/dy.h>
#include <cassert>
#include <cstdint>
#include <variant>
#include <vector>
namespace oi::exporters {
struct ParsedData {
class Lazy {
public:
Lazy(std::vector<uint8_t>::const_iterator& it, types::dy::Dynamic ty)
: it_(it), ty_(ty) {
}
ParsedData operator()() {
return ParsedData::parse(it_, ty_);
}
private:
std::vector<uint8_t>::const_iterator& it_;
types::dy::Dynamic ty_;
};
struct Unit {};
struct VarInt {
uint64_t value;
};
struct Pair {
Lazy first;
Lazy second;
};
struct List {
uint64_t length;
Lazy values;
};
struct Sum {
uint64_t index;
Lazy value;
};
static ParsedData parse(std::vector<uint8_t>::const_iterator& it,
types::dy::Dynamic ty);
ParsedData(Unit&& val_) : val(val_) {
}
ParsedData(VarInt&& val_) : val(val_) {
}
ParsedData(Pair&& val_) : val(val_) {
}
ParsedData(List&& val_) : val(val_) {
}
ParsedData(Sum&& val_) : val(val_) {
}
std::variant<Unit, VarInt, Pair, List, Sum> val;
};
} // namespace oi::exporters
#endif

View File

@ -0,0 +1,90 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef INCLUDED_OI_EXPORTERS_INST_H
#define INCLUDED_OI_EXPORTERS_INST_H 1
#include <oi/exporters/ParsedData.h>
#include <oi/result/Element.h>
#include <oi/types/dy.h>
#include <array>
#include <cstdint>
#include <initializer_list>
#include <stack>
#include <utility>
#include <variant>
namespace oi::exporters::inst {
struct PopTypePath;
struct Field;
using Inst = std::variant<PopTypePath, std::reference_wrapper<const Field>>;
using Processor = void (*)(result::Element&, std::stack<Inst>&, ParsedData);
using ProcessorInst = std::pair<types::dy::Dynamic, Processor>;
struct PopTypePath {};
struct Field {
template <size_t N0, size_t N1, size_t N2>
constexpr Field(size_t static_size_,
size_t exclusive_size_,
std::string_view name_,
const std::array<std::string_view, N0>& type_names_,
const std::array<Field, N1>& fields_,
const std::array<ProcessorInst, N2>& processors_);
template <size_t N0, size_t N1, size_t N2>
constexpr Field(size_t static_size_,
std::string_view name_,
const std::array<std::string_view, N0>& type_names_,
const std::array<Field, N1>& fields_,
const std::array<ProcessorInst, N2>& processors_)
: Field(static_size_,
static_size_,
name_,
type_names_,
fields_,
processors_) {
}
constexpr Field(const Field&) = default; // no idea why this is needed
size_t static_size;
size_t exclusive_size;
std::string_view name;
std::span<const std::string_view> type_names;
std::span<const Field> fields;
std::span<const ProcessorInst> processors;
};
template <size_t N0, size_t N1, size_t N2>
constexpr Field::Field(size_t static_size_,
size_t exclusive_size_,
std::string_view name_,
const std::array<std::string_view, N0>& type_names_,
const std::array<Field, N1>& fields_,
const std::array<ProcessorInst, N2>& processors_)
: static_size(static_size_),
exclusive_size(exclusive_size_),
name(name_),
type_names(type_names_),
fields(fields_),
processors(processors_) {
}
} // namespace oi::exporters::inst
#endif

91
include/oi/oi-jit-inl.h Normal file
View File

@ -0,0 +1,91 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#if defined(INCLUDED_OI_OI_JIT_INL_H) || !defined(INCLUDED_OI_OI_JIT_H)
static_assert(
false,
"oi-jit-inl.h provides inline declarations for oi-jit.h and should only "
"be included by oi-jit.h");
#endif
#define INCLUDED_OI_OI_JIT_INL_H 1
#include <stdexcept>
#include "oi-jit.h"
namespace oi {
template <typename T, Feature... Fs>
inline std::optional<IntrospectionResult> setupAndIntrospect(
const T& objectAddr, const GeneratorOptions& opts) {
if (!CodegenHandler<T, Fs...>::init(opts))
return std::nullopt;
return CodegenHandler<T, Fs...>::introspect(objectAddr);
}
template <typename T, Feature... Fs>
inline std::atomic<bool>& CodegenHandler<T, Fs...>::getIsCritical() {
static std::atomic<bool> isCritical = false;
return isCritical;
}
template <typename T, Feature... Fs>
inline std::atomic<void (*)(const T&, std::vector<uint8_t>&)>&
CodegenHandler<T, Fs...>::getIntrospectionFunc() {
static std::atomic<void (*)(const T&, std::vector<uint8_t>&)> func = nullptr;
return func;
}
template <typename T, Feature... Fs>
inline std::atomic<const exporters::inst::Inst*>&
CodegenHandler<T, Fs...>::getTreeBuilderInstructions() {
static std::atomic<const exporters::inst::Inst*> ty = nullptr;
return ty;
}
template <typename T, Feature... Fs>
inline bool CodegenHandler<T, Fs...>::init(const GeneratorOptions& opts) {
if (getIntrospectionFunc().load() != nullptr &&
getTreeBuilderInstructions().load() != nullptr)
return true; // already initialised
if (getIsCritical().exchange(true))
return false; // other thread is initialising/has failed
auto lib =
OILibrary(reinterpret_cast<void*>(&getIntrospectionFunc), {Fs...}, opts);
auto [vfp, ty] = lib.init();
getIntrospectionFunc().store(reinterpret_cast<func_type>(vfp));
getTreeBuilderInstructions().store(&ty);
return true;
}
template <typename T, Feature... Fs>
inline IntrospectionResult CodegenHandler<T, Fs...>::introspect(
const T& objectAddr) {
func_type func = getIntrospectionFunc().load();
const exporters::inst::Inst* ty = getTreeBuilderInstructions().load();
if (func == nullptr || ty == nullptr)
throw std::logic_error("introspect(const T&) called when uninitialised");
std::vector<uint8_t> buf;
static_assert(sizeof(std::vector<uint8_t>) == 24);
func(objectAddr, buf);
return IntrospectionResult{std::move(buf), *ty};
}
} // namespace oi

82
include/oi/oi-jit.h Normal file
View File

@ -0,0 +1,82 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef INCLUDED_OI_OI_JIT_H
#define INCLUDED_OI_OI_JIT_H 1
#include <atomic>
#include <filesystem>
#include <memory>
#include <optional>
#include <unordered_set>
#include <utility>
#include <vector>
#include "exporters/inst.h"
#include "oi.h"
namespace oi::detail {
class OILibraryImpl;
}
namespace oi {
struct GeneratorOptions {
std::filesystem::path configFilePath;
std::filesystem::path sourceFileDumpPath;
int debugLevel = 0;
};
class OILibrary {
public:
OILibrary(void* atomicHome,
std::unordered_set<Feature>,
GeneratorOptions opts);
~OILibrary();
std::pair<void*, const exporters::inst::Inst&> init();
private:
std::unique_ptr<detail::OILibraryImpl> pimpl_;
};
/*
* setupAndIntrospect
*
* Execute JIT compilation then introspect the given object. Throws on error.
* Returns std::nullopt rather than duplicating initialisation if its ongoing.
*/
template <typename T, Feature... Fs>
std::optional<IntrospectionResult> setupAndIntrospect(
const T& objectAddr, const GeneratorOptions& opts);
template <typename T, Feature... Fs>
class CodegenHandler {
public:
static bool init(const GeneratorOptions& opts);
static IntrospectionResult introspect(const T& objectAddr);
private:
using func_type = void (*)(const T&, std::vector<uint8_t>&);
static std::atomic<bool>& getIsCritical();
static std::atomic<func_type>& getIntrospectionFunc();
static std::atomic<const exporters::inst::Inst*>&
getTreeBuilderInstructions();
};
} // namespace oi
#include "oi-jit-inl.h"
#endif

47
include/oi/oi.h Normal file
View File

@ -0,0 +1,47 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef INCLUDED_OI_OI_H
#define INCLUDED_OI_OI_H 1
#include <oi/IntrospectionResult.h>
#include <oi/types/dy.h>
#include <cstdint>
#include <vector>
namespace oi {
enum class Feature {
ChaseRawPointers,
CaptureThriftIsset,
GenJitDebug,
};
template <typename T, Feature... Fs>
IntrospectionResult __attribute__((noinline)) introspect(const T& objectAddr);
} // namespace oi
#ifndef OIL_AOT_COMPILATION
#include "oi-jit.h"
#else
template <typename T, Feature... Fs>
IntrospectionResult __attribute__((noinline)) introspect(const T& objectAddr);
{ static_assert(false, "OIL v2 does not yet support AoT compilation."); }
#endif
#endif

View File

@ -0,0 +1,49 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef INCLUDED_OI_RESULT_ELEMENT_H
#define INCLUDED_OI_RESULT_ELEMENT_H 1
#include <optional>
#include <span>
#include <string_view>
#include <vector>
namespace oi::result {
struct Element {
struct ContainerStats {
size_t capacity;
size_t length;
};
struct IsSetStats {
bool is_set;
};
std::string_view name;
std::vector<std::string_view>
type_path; // TODO: should be span<const std::string_view>
std::span<const std::string_view> type_names;
size_t static_size;
size_t exclusive_size;
std::optional<uintptr_t> pointer;
std::optional<ContainerStats> container_stats;
std::optional<IsSetStats> is_set_stats;
};
} // namespace oi::result
#endif

View File

@ -14,8 +14,8 @@
* limitations under the License.
*/
#ifndef OI_TYPES_DY_H
#define OI_TYPES_DY_H 1
#ifndef INCLUDED_OI_TYPES_DY_H
#define INCLUDED_OI_TYPES_DY_H 1
/*
* Dynamic Types
@ -45,11 +45,11 @@
namespace oi::types::dy {
class Unit;
class VarInt;
class Pair;
class Sum;
class List;
struct Unit;
struct VarInt;
struct Pair;
struct Sum;
struct List;
/*
* Dynamic
@ -63,11 +63,10 @@ using Dynamic = std::variant<std::reference_wrapper<const Unit>,
std::reference_wrapper<const Sum>,
std::reference_wrapper<const List> >;
class Unit {};
class VarInt {};
struct Unit {};
struct VarInt {};
class Pair {
public:
struct Pair {
constexpr Pair(Dynamic first_, Dynamic second_)
: first(first_), second(second_) {
}
@ -76,8 +75,7 @@ class Pair {
Dynamic second;
};
class Sum {
public:
struct Sum {
template <size_t N>
constexpr Sum(const std::array<Dynamic, N>& variants_) : variants(variants_) {
}
@ -85,8 +83,7 @@ class Sum {
std::span<const Dynamic> variants;
};
class List {
public:
struct List {
constexpr List(Dynamic element_) : element(element_) {
}

View File

@ -13,8 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef OI_TYPES_ST_H
#define OI_TYPES_ST_H 1
#ifndef INCLUDED_OI_TYPES_ST_H
#define INCLUDED_OI_TYPES_ST_H 1
/*
* Static Types
@ -123,7 +123,7 @@ class VarInt {
}
Unit<DataBuffer> write(uint64_t val) {
while (val >= 128) {
while (val >= 0x80) {
_buf.write_byte(0x80 | (val & 0x7f));
val >>= 7;
}

View File

@ -52,4 +52,10 @@ target_link_libraries(codegen
glog::glog
)
add_library(exporters_json
exporters/Json.cpp
)
target_include_directories(exporters_json PUBLIC ${CMAKE_SOURCE_DIR}/include)
target_link_libraries(exporters_json oil)
add_subdirectory(type_graph)

View File

@ -19,6 +19,7 @@
#include <boost/format.hpp>
#include <iostream>
#include <numeric>
#include <set>
#include <string_view>
@ -54,9 +55,11 @@ using type_graph::Enum;
using type_graph::Flattener;
using type_graph::Member;
using type_graph::NameGen;
using type_graph::Primitive;
using type_graph::Prune;
using type_graph::RemoveMembers;
using type_graph::RemoveTopLevelPointer;
using type_graph::TemplateParam;
using type_graph::TopoSorter;
using type_graph::Type;
using type_graph::Typedef;
@ -67,6 +70,18 @@ template <typename T>
using ref = std::reference_wrapper<T>;
namespace {
std::vector<std::string_view> enumerateTypeNames(Type& type) {
std::vector<std::string_view> names;
Type* t = &type;
while (const Typedef* td = dynamic_cast<Typedef*>(t)) {
names.emplace_back(t->inputName());
t = &td->underlyingType();
}
names.emplace_back(t->inputName());
return names;
}
void defineMacros(std::string& code) {
if (true /* TODO: config.useDataSegment*/) {
code += R"(
@ -93,6 +108,23 @@ struct OIArray {
void defineJitLog(FeatureSet features, std::string& code) {
if (features[Feature::JitLogging]) {
code += R"(
extern int logFile;
void __jlogptr(uintptr_t ptr) {
static constexpr char hexdigits[] = "0123456789abcdef";
static constexpr size_t ptrlen = 2 * sizeof(ptr);
static char hexstr[ptrlen + 1] = {};
size_t i = ptrlen;
while (i--) {
hexstr[i] = hexdigits[ptr & 0xf];
ptr = ptr >> 4;
}
hexstr[ptrlen] = '\n';
write(logFile, hexstr, sizeof(hexstr));
}
#define JLOG(str) \
do { \
if (__builtin_expect(logFile, 0)) { \
@ -128,6 +160,10 @@ void addIncludes(const TypeGraph& typeGraph,
code += "#define DEFINE_DESCRIBE 1\n"; // added before all includes
}
if (features[Feature::TreeBuilderV2])
includes.emplace("oi/exporters/inst.h");
if (features[Feature::Library])
includes.emplace("vector");
if (features[Feature::JitTiming]) {
includes.emplace("chrono");
}
@ -569,38 +605,6 @@ void CodeGen::addGetSizeFuncDefs(const TypeGraph& typeGraph,
namespace {
void addStandardTypeHandlers(std::string& code) {
// Provide a wrapper function, getSizeType, to infer T instead of having to
// explicitly specify it with TypeHandler<DB, T>::getSizeType every time.
code += R"(
template <typename DB, typename T>
types::st::Unit<DB>
getSizeType(const T &t, typename TypeHandler<DB, T>::type returnArg) {
JLOG("obj @");
JLOGPTR(&t);
return TypeHandler<DB, T>::getSizeType(t, returnArg);
}
)";
code += R"(
template<typename DB, typename T0, long unsigned int N>
struct TypeHandler<DB, OIArray<T0, N>> {
using type = types::st::List<DB, typename TypeHandler<DB, T0>::type>;
static types::st::Unit<DB> getSizeType(
const OIArray<T0, N> &container,
typename TypeHandler<DB, OIArray<T0,N>>::type returnArg) {
auto tail = returnArg.write(N);
for (size_t i=0; i<N; i++) {
tail = tail.delegate([&container, i](auto ret) {
return TypeHandler<DB, T0>::getSizeType(container.vals[i], ret);
});
}
return tail.finish();
}
};
)";
}
// Find the last member that isn't padding's index. Return -1 if no such member.
size_t getLastNonPaddingMemberIndex(const std::vector<Member>& members) {
for (size_t i = members.size() - 1; i != (size_t)-1; --i) {
@ -724,6 +728,70 @@ void CodeGen::genClassStaticType(const Class& c, std::string& code) {
}
}
namespace {
size_t calculateExclusiveSize(const Type& t) {
if (const auto* c = dynamic_cast<const Class*>(&t)) {
return std::accumulate(c->members.cbegin(), c->members.cend(), 0,
[](size_t a, const auto& m) {
if (m.name.starts_with(AddPadding::MemberPrefix))
return a + m.type().size();
return a;
});
}
return t.size();
}
} // namespace
void CodeGen::genClassTreeBuilderInstructions(const Class& c,
std::string& code) {
code += " private:\n";
size_t index = 0;
for (const auto& m : c.members) {
++index;
if (m.name.starts_with(AddPadding::MemberPrefix))
continue;
auto names = enumerateTypeNames(m.type());
code += " static constexpr std::array<std::string_view, " +
std::to_string(names.size()) + "> member_" + std::to_string(index) +
"_type_names = {";
for (const auto& name : names) {
code += "\"";
code += name;
code += "\",";
}
code += "};\n";
}
code += " public:\n";
size_t numFields =
std::count_if(c.members.cbegin(), c.members.cend(), [](const auto& m) {
return !m.name.starts_with(AddPadding::MemberPrefix);
});
code += " static constexpr std::array<inst::Field, ";
code += std::to_string(numFields);
code += "> fields{\n";
index = 0;
for (const auto& m : c.members) {
++index;
if (m.name.starts_with(AddPadding::MemberPrefix))
continue;
std::string fullName = c.name() + "::" + m.name;
code += " inst::Field{sizeof(" + fullName + "), " +
std::to_string(calculateExclusiveSize(m.type())) + ",\"" +
m.inputName + "\", member_" + std::to_string(index) +
"_type_names, TypeHandler<DB, decltype(" + fullName +
")>::fields, TypeHandler<DB, decltype(" + fullName +
")>::processors},\n";
}
code += " };\n";
code +=
"static constexpr std::array<exporters::inst::ProcessorInst, 0> "
"processors{};\n";
}
void CodeGen::genClassTypeHandler(const Class& c, std::string& code) {
std::string helpers;
@ -756,30 +824,159 @@ void CodeGen::genClassTypeHandler(const Class& c, std::string& code) {
code += " using type = ";
genClassStaticType(c, code);
code += ";\n";
if (config_.features[Feature::TreeBuilderV2])
genClassTreeBuilderInstructions(c, code);
genClassTraversalFunction(c, code);
code += "};\n";
}
namespace {
void getContainerTypeHandler(std::unordered_set<const ContainerInfo*>& used,
const Container& c,
void genContainerTypeHandler(FeatureSet features,
std::unordered_set<const ContainerInfo*>& used,
const ContainerInfo& c,
std::span<const TemplateParam> templateParams,
std::string& code) {
if (!used.insert(&c.containerInfo_).second) {
if (!used.insert(&c).second)
return;
}
const auto& handler = c.containerInfo_.codegen.handler;
// TODO: Move this check into the ContainerInfo parsing once always enabled.
if (!features[Feature::TreeBuilderV2]) {
const auto& handler = c.codegen.handler;
if (handler.empty()) {
LOG(ERROR) << "`codegen.handler` must be specified for all containers "
"under \"-ftyped-data-segment\", not specified for \"" +
c.containerInfo_.typeName + "\"";
c.typeName + "\"";
throw std::runtime_error("missing `codegen.handler`");
}
auto fmt = boost::format(c.containerInfo_.codegen.handler) %
c.containerInfo_.typeName;
auto fmt = boost::format(c.codegen.handler) % c.typeName;
code += fmt.str();
return;
}
// TODO: Move this check into the ContainerInfo parsing once always enabled.
const auto& func = c.codegen.traversalFunc;
const auto& processors = c.codegen.processors;
if (func.empty()) {
LOG(ERROR)
<< "`codegen.traversal_func` must be specified for all containers "
"under \"-ftree-builder-v2\", not specified for \"" +
c.typeName + "\"";
throw std::runtime_error("missing `codegen.traversal_func`");
}
std::string containerWithTypes = c.typeName;
if (!templateParams.empty())
containerWithTypes += '<';
size_t types = 0, values = 0;
for (const auto& p : templateParams) {
if (types > 0 || values > 0)
containerWithTypes += ", ";
if (p.value) {
containerWithTypes += "N" + std::to_string(values++);
} else {
containerWithTypes += "T" + std::to_string(types++);
}
}
if (!templateParams.empty())
containerWithTypes += '>';
code += "template <typename DB";
types = 0, values = 0;
for (const auto& p : templateParams) {
if (p.value) {
code += ", ";
code += p.type().name();
code += " N" + std::to_string(values++);
} else {
code += ", typename T" + std::to_string(types++);
}
}
code += ">\n";
code += "struct TypeHandler<DB, ";
code += containerWithTypes;
code += "> {\n";
code += " using type = ";
if (processors.empty()) {
code += "types::st::Unit<DB>";
} else {
for (auto it = processors.cbegin(); it != processors.cend(); ++it) {
if (it != processors.cend() - 1)
code += "types::st::Pair<DB, ";
code += it->type;
if (it != processors.cend() - 1)
code += ", ";
}
code += std::string(processors.size() - 1, '>');
}
code += ";\n";
code += " static types::st::Unit<DB> getSizeType(\n";
code += " const ";
code += containerWithTypes;
code += "& container,\n";
code += " typename TypeHandler<DB, ";
code += containerWithTypes;
code += ">::type returnArg) {\n";
code += func; // has rubbish indentation
code += " }\n";
code += " private:\n";
size_t count = 0;
for (const auto& pr : processors) {
code += " static void processor_";
code += std::to_string(count++);
code +=
"(result::Element& el, std::stack<inst::Inst>& ins, ParsedData d) {\n";
code += pr.func; // bad indentation
code += " }\n";
}
code += " public:\n";
code +=
" static constexpr std::array<exporters::inst::Field, 0> fields{};\n";
code += " static constexpr std::array<exporters::inst::ProcessorInst, ";
code += std::to_string(processors.size());
code += "> processors{\n";
count = 0;
for (const auto& pr : processors) {
code += " exporters::inst::ProcessorInst{";
code += pr.type;
code += "::describe, &processor_";
code += std::to_string(count++);
code += "},\n";
}
code += " };\n";
code += "};\n\n";
}
void addStandardTypeHandlers(TypeGraph& typeGraph,
FeatureSet features,
std::string& code) {
// Provide a wrapper function, getSizeType, to infer T instead of having to
// explicitly specify it with TypeHandler<DB, T>::getSizeType every time.
code += R"(
template <typename DB, typename T>
types::st::Unit<DB>
getSizeType(const T &t, typename TypeHandler<DB, T>::type returnArg) {
JLOG("obj @");
JLOGPTR(&t);
return TypeHandler<DB, T>::getSizeType(t, returnArg);
}
)";
// TODO: bit of a hack - making ContainerInfo a node in the type graph and
// traversing for it would remove the need for this set altogether.
std::unordered_set<const ContainerInfo*> used{};
std::vector<TemplateParam> arrayParams{
TemplateParam{typeGraph.makeType<Primitive>(Primitive::Kind::UInt64)},
TemplateParam{typeGraph.makeType<Primitive>(Primitive::Kind::UInt64),
"0"},
};
genContainerTypeHandler(features, used, FuncGen::GetOiArrayContainerInfo(),
arrayParams, code);
}
} // namespace
@ -789,7 +986,8 @@ void CodeGen::addTypeHandlers(const TypeGraph& typeGraph, std::string& code) {
if (const auto* c = dynamic_cast<const Class*>(&t)) {
genClassTypeHandler(*c, code);
} else if (const auto* con = dynamic_cast<const Container*>(&t)) {
getContainerTypeHandler(definedContainers_, *con, code);
genContainerTypeHandler(config_.features, definedContainers_,
con->containerInfo_, con->templateParams, code);
}
}
}
@ -886,6 +1084,9 @@ void CodeGen::generate(
struct drgn_type* drgnType /* TODO: this argument should not be required */
) {
code = headers::oi_OITraceCode_cpp;
if (!config_.features[Feature::Library]) {
FuncGen::DeclareExterns(code);
}
if (!config_.features[Feature::TypedDataSegment]) {
defineMacros(code);
}
@ -894,12 +1095,19 @@ void CodeGen::generate(
defineJitLog(config_.features, code);
if (config_.features[Feature::TypedDataSegment]) {
if (config_.features[Feature::Library]) {
FuncGen::DefineBackInserterDataBuffer(code);
} else {
FuncGen::DefineDataSegmentDataBuffer(code);
}
code += "using namespace oi;\n";
code += "using namespace oi::detail;\n";
if (config_.features[Feature::TreeBuilderV2]) {
code += "using oi::exporters::ParsedData;\n";
code += "using namespace oi::exporters;\n";
}
code += "namespace OIInternal {\nnamespace {\n";
FuncGen::DefineBasicTypeHandlers(code);
FuncGen::DefineBasicTypeHandlers(code, config_.features);
code += "} // namespace\n} // namespace OIInternal\n";
}
@ -930,7 +1138,7 @@ void CodeGen::generate(
genStaticAsserts(typeGraph, code);
if (config_.features[Feature::TypedDataSegment]) {
addStandardTypeHandlers(code);
addStandardTypeHandlers(typeGraph, config_.features, code);
addTypeHandlers(typeGraph, code);
} else {
addStandardGetSizeFuncDecls(code);
@ -946,13 +1154,19 @@ void CodeGen::generate(
code += "} // namespace\n} // namespace OIInternal\n";
const auto typeName = SymbolService::getTypeName(drgnType);
if (config_.features[Feature::TypedDataSegment]) {
if (config_.features[Feature::Library]) {
FuncGen::DefineTopLevelIntrospect(code, typeName);
} else if (config_.features[Feature::TypedDataSegment]) {
FuncGen::DefineTopLevelGetSizeRefTyped(code, typeName, config_.features);
} else {
FuncGen::DefineTopLevelGetSizeRef(code, typeName, config_.features);
}
if (config_.features[Feature::TreeBuilderTypeChecking]) {
if (config_.features[Feature::TreeBuilderV2]) {
FuncGen::DefineTreeBuilderInstructions(code, typeName,
calculateExclusiveSize(rootType),
enumerateTypeNames(rootType));
} else if (config_.features[Feature::TreeBuilderTypeChecking]) {
FuncGen::DefineOutputType(code, typeName);
}

View File

@ -80,6 +80,8 @@ class CodeGen {
void genClassTypeHandler(const type_graph::Class& c, std::string& code);
void genClassStaticType(const type_graph::Class& c, std::string& code);
void genClassTraversalFunction(const type_graph::Class& c, std::string& code);
void genClassTreeBuilderInstructions(const type_graph::Class& c,
std::string& code);
};
} // namespace oi::detail

View File

@ -265,6 +265,38 @@ ContainerInfo::ContainerInfo(const fs::path& path) {
codegenToml["handler"].value<std::string>()) {
codegen.handler = std::move(*str);
}
if (std::optional<std::string> str =
codegenToml["traversal_func"].value<std::string>()) {
codegen.traversalFunc = std::move(*str);
}
if (toml::array* arr = codegenToml["processor"].as_array()) {
codegen.processors.reserve(arr->size());
arr->for_each([&](auto&& el) {
if (toml::table* proc = el.as_table()) {
std::string type, func;
if (std::optional<std::string> str =
(*proc)["type"].value<std::string>()) {
type = std::move(*str);
} else {
throw ContainerInfoError(
path, "codegen.processor.type is a required field");
}
if (std::optional<std::string> str =
(*proc)["func"].value<std::string>()) {
func = std::move(*str);
} else {
throw ContainerInfoError(
path, "codegen.processor.func is a required field");
}
codegen.processors.emplace_back(
Processor{std::move(type), std::move(func)});
} else {
throw ContainerInfoError(
path, "codegen.processor should only contain tables");
}
});
}
}
ContainerInfo::ContainerInfo(std::string typeName_,
@ -275,5 +307,5 @@ ContainerInfo::ContainerInfo(std::string typeName_,
ctype(ctype_),
header(std::move(header_)),
codegen(Codegen{"// DummyDecl %1%\n", "// DummyFunc %1%\n",
"// DummyHandler %1%\n"}) {
"// DummyHandler %1%\n", "// DummyFunc\n"}) {
}

View File

@ -27,10 +27,17 @@ ContainerTypeEnum containerTypeEnumFromStr(std::string& str);
const char* containerTypeEnumToStr(ContainerTypeEnum ty);
struct ContainerInfo {
struct Processor {
std::string type;
std::string func;
};
struct Codegen {
std::string decl;
std::string func;
std::string handler = "";
std::string traversalFunc = "";
std::vector<Processor> processors{};
};
explicit ContainerInfo(const std::filesystem::path& path); // Throws

View File

@ -45,6 +45,8 @@ std::optional<std::string_view> featureHelp(Feature f) {
case Feature::TreeBuilderTypeChecking:
return "Use Typed Data Segment to perform runtime Type Checking in "
"TreeBuilder.";
case Feature::Library:
return std::nullopt; // Hide in OID help
case Feature::TreeBuilderV2:
return "Use Tree Builder v2 for reading the data segment";
case Feature::GenJitDebug:
@ -72,6 +74,20 @@ std::span<const Feature> requirements(Feature f) {
case Feature::TreeBuilderV2:
static constexpr std::array tb2 = {Feature::TreeBuilderTypeChecking};
return tb2;
case Feature::Library:
static constexpr std::array lib = {Feature::TreeBuilderV2};
return lib;
default:
return {};
}
}
std::span<const Feature> conflicts(Feature f) {
switch (f) {
case Feature::Library:
static constexpr std::array lib = {Feature::JitLogging,
Feature::JitTiming};
return lib;
default:
return {};
}
@ -147,6 +163,19 @@ std::optional<FeatureSet> handleFeatureConflicts(FeatureSet enabled,
}
}
for (const auto f : allFeatures) {
if (!enabled[f])
continue;
for (const auto c : conflicts(f)) {
if (enabled[c]) {
LOG(ERROR) << featureToStr(f) << " feature conflicts with "
<< featureToStr(c) << " but both are enabled!";
return std::nullopt;
}
}
}
return enabled;
}

View File

@ -31,6 +31,7 @@
X(PruneTypeGraph, "prune-type-graph") \
X(TypedDataSegment, "typed-data-segment") \
X(TreeBuilderTypeChecking, "tree-builder-type-checking") \
X(Library, "library") \
X(TreeBuilderV2, "tree-builder-v2") \
X(GenJitDebug, "gen-jit-debug") \
X(JitLogging, "jit-logging") \

View File

@ -147,6 +147,16 @@ void FuncGen::DeclareTopLevelGetSize(std::string& testCode,
boost::format fmt = boost::format("void getSizeType(const %1% &t);\n") % type;
testCode.append(fmt.str());
}
void FuncGen::DeclareExterns(std::string& code) {
constexpr std::string_view vars = R"(
extern uint8_t* dataBase;
extern size_t dataSize;
extern uintptr_t cookieValue;
)";
code.append(vars);
}
void FuncGen::DeclareStoreData(std::string& testCode) {
testCode.append("void StoreData(uintptr_t data, size_t& dataSegOffset);\n");
}
@ -235,6 +245,35 @@ void FuncGen::DefineTopLevelGetObjectSize(std::string& testCode,
testCode.append(fmt.str());
}
void FuncGen::DefineTopLevelIntrospect(std::string& code,
const std::string& type) {
std::string func = R"(
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunknown-attributes"
/* RawType: %1% */
void __attribute__((used, retain)) introspect_%2$016x(
const OIInternal::__ROOT_TYPE__& t,
std::vector<uint8_t>& v)
#pragma GCC diagnostic pop
{
pointers.initialize();
pointers.add((uintptr_t)&t);
v.clear();
v.reserve(4096);
using DataBufferType = DataBuffer::BackInserter<std::vector<uint8_t>>;
using ContentType = OIInternal::TypeHandler<DataBufferType, OIInternal::__ROOT_TYPE__>::type;
ContentType ret{DataBufferType{v}};
OIInternal::getSizeType<DataBufferType>(t, ret);
}
)";
code.append(
(boost::format(func) % type % std::hash<std::string>{}(type)).str());
}
void FuncGen::DefineTopLevelGetSizeRef(std::string& testCode,
const std::string& rawType,
FeatureSet features) {
@ -382,6 +421,49 @@ void FuncGen::DefineOutputType(std::string& code, const std::string& rawType) {
code.append(fmt.str());
}
void FuncGen::DefineTreeBuilderInstructions(
std::string& code,
const std::string& rawType,
size_t exclusiveSize,
std::span<const std::string_view> typeNames) {
std::string typeHash =
(boost::format("%1$016x") % std::hash<std::string>{}(rawType)).str();
code += R"(
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunknown-attributes"
namespace {
const std::array<std::string_view, )";
code += std::to_string(typeNames.size());
code += "> typeNames";
code += typeHash;
code += '{';
for (const auto& name : typeNames) {
code += '"';
code += name;
code += "\",";
}
code += "};\n";
code += "const exporters::inst::Field rootInstructions";
code += typeHash;
code += "{sizeof(OIInternal::__ROOT_TYPE__), ";
code += std::to_string(exclusiveSize);
code += ", \"a0\", typeNames";
code += typeHash;
code +=
", OIInternal::TypeHandler<int, OIInternal::__ROOT_TYPE__>::fields, "
"OIInternal::TypeHandler<int, OIInternal::__ROOT_TYPE__>::processors};\n";
code += "} // namespace\n";
code +=
"extern const exporters::inst::Inst __attribute__((used, retain)) "
"treeBuilderInstructions";
code += typeHash;
code += " = rootInstructions";
code += typeHash;
code += ";\n";
code += "#pragma GCC diagnostic pop\n";
}
void FuncGen::DefineTopLevelGetSizeRefRet(std::string& testCode,
const std::string& rawType) {
std::string func = R"(
@ -564,6 +646,34 @@ void FuncGen::DefineDataSegmentDataBuffer(std::string& testCode) {
testCode.append(func);
}
/*
* DefineBackInserterDataBuffer
*
* Provides a DataBuffer implementation that takes anything convertible with
* std::back_inserter.
*/
void FuncGen::DefineBackInserterDataBuffer(std::string& code) {
constexpr std::string_view buf = R"(
namespace oi::detail::DataBuffer {
template <class Container>
class BackInserter {
public:
BackInserter(Container& v) : buf(v) {}
void write_byte(uint8_t byte) {
*buf = byte;
}
private:
std::back_insert_iterator<Container> buf;
};
} // namespace oi::detail::DataBuffer
)";
code.append(buf);
}
/*
* DefineBasicTypeHandlers
*
@ -573,8 +683,8 @@ void FuncGen::DefineDataSegmentDataBuffer(std::string& testCode) {
* pointer's value always, then the value of the pointer if it is unique. void
* is of type Unit and always stores nothing.
*/
void FuncGen::DefineBasicTypeHandlers(std::string& testCode) {
constexpr std::string_view tHandler = R"(
void FuncGen::DefineBasicTypeHandlers(std::string& code, FeatureSet features) {
code += R"(
template <typename DB, typename T>
struct TypeHandler {
private:
@ -582,10 +692,8 @@ void FuncGen::DefineBasicTypeHandlers(std::string& testCode) {
if constexpr(std::is_pointer_v<T>) {
return std::type_identity<types::st::Pair<DB,
types::st::VarInt<DB>,
types::st::Sum<DB,
types::st::Unit<DB>,
typename TypeHandler<DB, std::remove_pointer_t<T>>::type
>>>();
types::st::Sum<DB, types::st::Unit<DB>, typename TypeHandler<DB, std::remove_pointer_t<T>>::type>
>>();
} else {
return std::type_identity<types::st::Unit<DB>>();
}
@ -593,7 +701,56 @@ void FuncGen::DefineBasicTypeHandlers(std::string& testCode) {
public:
using type = typename decltype(choose_type())::type;
)";
if (features[Feature::TreeBuilderV2]) {
code += R"(private:
static void process_pointer(result::Element& el, std::stack<inst::Inst>& ins, ParsedData d) {
el.pointer = std::get<ParsedData::VarInt>(d.val).value;
}
static void process_pointer_content(result::Element& el, std::stack<inst::Inst>& ins, ParsedData d) {
static constexpr std::array<std::string_view, 1> names{"TODO"};
static constexpr auto childField = inst::Field{
sizeof(T),
"*",
names,
TypeHandler<DB, T>::fields,
TypeHandler<DB, T>::processors,
};
const ParsedData::Sum& sum = std::get<ParsedData::Sum>(d.val);
el.container_stats.emplace(result::Element::ContainerStats{ .capacity = 1 });
if (sum.index == 0)
return;
el.container_stats->length = 1;
ins.emplace(childField);
}
static constexpr auto choose_fields() {
if constexpr(std::is_pointer_v<T>) {
return std::array<exporters::inst::Field, 0>{};
} else {
return std::array<exporters::inst::Field, 0>{};
}
}
static constexpr auto choose_processors() {
if constexpr(std::is_pointer_v<T>) {
return std::array<inst::ProcessorInst, 2>{
{types::st::VarInt<DB>::describe, &process_pointer},
{types::st::Sum<DB, types::st::Unit<DB>, typename TypeHandler<DB, std::remove_pointer_t<T>>::type>::describe, &process_pointer_content},
};
} else {
return std::array<inst::ProcessorInst, 0>{};
}
}
public:
static constexpr auto fields = choose_fields();
static constexpr auto processors = choose_processors();
)";
}
code += R"(
static types::st::Unit<DB> getSizeType(
const T& t,
typename TypeHandler<DB, T>::type returnArg) {
@ -619,16 +776,75 @@ void FuncGen::DefineBasicTypeHandlers(std::string& testCode) {
};
)";
constexpr std::string_view voidHandler = R"(
code += R"(
template <typename DB>
class TypeHandler<DB, void> {
public:
using type = types::st::Unit<DB>;
)";
if (features[Feature::TreeBuilderV2]) {
code +=
"static constexpr std::array<exporters::inst::Field, 0> fields{};\n";
code +=
"static constexpr std::array<exporters::inst::ProcessorInst, 0> "
"processors{};\n";
}
code += "};\n";
}
ContainerInfo FuncGen::GetOiArrayContainerInfo() {
ContainerInfo oiArray{"OIArray", UNKNOWN_TYPE,
"cstdint"}; // TODO: remove the need for a dummy header
oiArray.codegen.handler = R"(
template<typename DB, typename T0, long unsigned int N>
struct TypeHandler<DB, %1%<T0, N>> {
using type = types::st::List<DB, typename TypeHandler<DB, T0>::type>;
static types::st::Unit<DB> getSizeType(
const %1%<T0, N> &container,
typename TypeHandler<DB, %1%<T0,N>>::type returnArg) {
auto tail = returnArg.write(N);
for (size_t i=0; i<N; i++) {
tail = tail.delegate([&container, i](auto ret) {
return TypeHandler<DB, T0>::getSizeType(container.vals[i], ret);
});
}
return tail.finish();
}
};
)";
oiArray.codegen.traversalFunc = R"(
auto tail = returnArg.write(N0);
for (size_t i=0; i<N0; i++) {
tail = tail.delegate([&container, i](auto ret) {
return TypeHandler<DB, T0>::getSizeType(container.vals[i], ret);
});
}
return tail.finish();
)";
oiArray.codegen.processors.emplace_back(ContainerInfo::Processor{
.type = "types::st::List<DB, typename TypeHandler<DB, T0>::type>",
.func = R"(
static constexpr std::array<std::string_view, 1> names{"TODO"};
static constexpr auto childField = inst::Field{
sizeof(T0),
"[]",
names,
TypeHandler<DB, T0>::fields,
TypeHandler<DB, T0>::processors,
};
testCode.append(tHandler);
testCode.append(voidHandler);
el.exclusive_size = 0;
el.container_stats.emplace(result::Element::ContainerStats{ .capacity = N0, .length = N0 });
auto list = std::get<ParsedData::List>(d.val);
// assert(list.length == N0);
for (size_t i = 0; i < N0; i++)
ins.emplace(childField);
)",
});
return oiArray;
}
} // namespace oi::detail

View File

@ -17,6 +17,7 @@
#include <filesystem>
#include <map>
#include <set>
#include <span>
#include <string>
#include "oi/ContainerInfo.h"
@ -28,6 +29,8 @@ namespace oi::detail {
class FuncGen {
public:
static void DeclareExterns(std::string& code);
static void DeclareStoreData(std::string& testCode);
static void DefineStoreData(std::string& testCode);
@ -56,6 +59,8 @@ class FuncGen {
static void DefineTopLevelGetObjectSize(std::string& testCode,
const std::string& type,
const std::string& linkageName);
static void DefineTopLevelIntrospect(std::string& code,
const std::string& type);
static void DefineTopLevelGetSizeRef(std::string& testCode,
const std::string& rawType,
@ -65,6 +70,11 @@ class FuncGen {
FeatureSet features);
static void DefineOutputType(std::string& testCode,
const std::string& rawType);
static void DefineTreeBuilderInstructions(
std::string& testCode,
const std::string& rawType,
size_t exclusiveSize,
std::span<const std::string_view> typeNames);
static void DefineTopLevelGetSizeRefRet(std::string& testCode,
const std::string& type);
@ -77,7 +87,10 @@ class FuncGen {
const std::string& ctype);
static void DefineDataSegmentDataBuffer(std::string& testCode);
static void DefineBasicTypeHandlers(std::string& testCode);
static void DefineBackInserterDataBuffer(std::string& code);
static void DefineBasicTypeHandlers(std::string& code, FeatureSet features);
static ContainerInfo GetOiArrayContainerInfo();
};
} // namespace oi::detail

View File

@ -19,7 +19,10 @@ namespace oi::detail::headers {
// These externs are provided by our build system. See resources/CMakeLists.txt
extern const std::string_view oi_OITraceCode_cpp;
extern const std::string_view oi_types_st_h;
extern const std::string_view oi_exporters_ParsedData_h;
extern const std::string_view oi_exporters_inst_h;
extern const std::string_view oi_result_Element_h;
extern const std::string_view oi_types_dy_h;
extern const std::string_view oi_types_st_h;
} // namespace oi::detail::headers

View File

@ -0,0 +1,80 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <oi/IntrospectionResult.h>
#include <oi/exporters/ParsedData.h>
#include <oi/types/dy.h>
#include <cassert>
#include <iterator>
#include <stdexcept>
template <typename T>
inline constexpr bool always_false_v = false;
namespace oi {
IntrospectionResult::const_iterator&
IntrospectionResult::const_iterator::operator++() {
if (stack_.empty()) {
next_ = std::nullopt;
return *this;
}
auto el = stack_.top();
stack_.pop();
return std::visit(
[this](auto&& r) -> IntrospectionResult::const_iterator& {
using U = std::decay_t<decltype(r)>;
if constexpr (std::is_same_v<U, exporters::inst::PopTypePath>) {
type_path_.pop_back();
return operator++();
} else {
// reference wrapper
auto ty = r.get();
using T = std::decay_t<decltype(ty)>;
if constexpr (std::is_same_v<T, exporters::inst::Field>) {
type_path_.emplace_back(ty.name);
stack_.emplace(exporters::inst::PopTypePath{});
next_ = result::Element{
.name = ty.name,
.type_path = type_path_,
.type_names = ty.type_names,
.static_size = ty.static_size,
.exclusive_size = ty.exclusive_size,
.container_stats = std::nullopt,
.is_set_stats = std::nullopt,
};
for (const auto& [dy, handler] : ty.processors) {
auto parsed = exporters::ParsedData::parse(data_, dy);
handler(*next_, stack_, parsed);
}
for (auto it = ty.fields.rbegin(); it != ty.fields.rend(); ++it) {
stack_.emplace(*it);
}
return *this;
} else {
static_assert(always_false_v<T>, "non-exhaustive visitor!");
}
}
},
el);
}
} // namespace oi

View File

@ -2983,6 +2983,7 @@ void OICodeGen::declareThriftStruct(std::string& code, std::string_view name) {
}
bool OICodeGen::generateJitCode(std::string& code) {
FuncGen::DeclareExterns(code);
// Include relevant headers
code.append("// relevant header includes -----\n");

View File

@ -37,6 +37,7 @@
#include <llvm/Support/TargetSelect.h>
#include <llvm/Support/raw_os_ostream.h>
#include <array>
#include <boost/range/combine.hpp>
#include <boost/scope_exit.hpp>
@ -516,21 +517,32 @@ bool OICompiler::compile(const std::string& code,
path.c_str(), clang::frontend::IncludeDirGroup::System, false, false);
}
if (config.features[Feature::TypedDataSegment]) {
static const auto syntheticHeaders = std::array<
std::pair<Feature, std::pair<std::string_view, std::string>>, 5>{{
{Feature::TypedDataSegment, {headers::oi_types_st_h, "oi/types/st.h"}},
{Feature::TreeBuilderTypeChecking,
{headers::oi_types_dy_h, "oi/types/dy.h"}},
{Feature::TreeBuilderV2,
{headers::oi_exporters_inst_h, "oi/exporters/inst.h"}},
{Feature::TreeBuilderV2,
{headers::oi_exporters_ParsedData_h, "oi/exporters/ParsedData.h"}},
{Feature::TreeBuilderV2,
{headers::oi_result_Element_h, "oi/result/Element.h"}},
}};
for (const auto& [k, v] : syntheticHeaders) {
if (!config.features[k])
continue;
compInv->getPreprocessorOpts().addRemappedFile(
"/synthetic/headers/oi/types/st.h",
MemoryBuffer::getMemBuffer(headers::oi_types_st_h).release());
headerSearchOptions.AddPath(
"/synthetic/headers", clang::frontend::IncludeDirGroup::IndexHeaderMap,
false, false);
std::string{"/synthetic/headers/"} + v.second,
MemoryBuffer::getMemBuffer(v.first).release());
}
if (config.features[Feature::TreeBuilderTypeChecking]) {
compInv->getPreprocessorOpts().addRemappedFile(
"/synthetic/headers/oi/types/dy.h",
MemoryBuffer::getMemBuffer(headers::oi_types_dy_h).release());
for (const auto& [k, _] : syntheticHeaders) {
if (config.features[k]) {
headerSearchOptions.AddPath(
"/synthetic/headers", clang::frontend::IncludeDirGroup::IndexHeaderMap,
false, false);
"/synthetic/headers",
clang::frontend::IncludeDirGroup::IndexHeaderMap, false, false);
break;
}
}
compInv->getFrontendOpts().OutputFile = objectPath;

View File

@ -14,49 +14,21 @@
* limitations under the License.
*/
#include "oi/OILibraryImpl.h"
#include "oi/oi-jit.h"
bool debug = false;
namespace oi {
namespace ObjectIntrospection {
bool operator==(const options& lhs, const options& rhs) {
return lhs.configFilePath == rhs.configFilePath &&
lhs.debugFilePath == rhs.debugFilePath &&
lhs.debugLevel == rhs.debugLevel &&
lhs.chaseRawPointers == rhs.chaseRawPointers;
OILibrary::OILibrary(void* atomicHole,
std::unordered_set<Feature> fs,
GeneratorOptions opts)
: pimpl_{std::make_unique<detail::OILibraryImpl>(
atomicHole, std::move(fs), std::move(opts))} {
}
bool operator!=(const options& lhs, const options& rhs) {
return !(lhs == rhs);
}
OILibrary::OILibrary(void* TemplateFunc, options opt) : opts(opt) {
this->pimpl_ = new OILibraryImpl(this, TemplateFunc);
}
OILibrary::~OILibrary() {
delete pimpl_;
}
int OILibrary::init() {
if (!pimpl_->processConfigFile()) {
return Response::OIL_BAD_CONFIG_FILE;
std::pair<void*, const exporters::inst::Inst&> OILibrary::init() {
return pimpl_->init();
}
if (!pimpl_->mapSegment()) {
return Response::OIL_SEGMENT_INIT_FAIL;
}
pimpl_->initCompiler();
return pimpl_->compileCode();
}
int OILibrary::getObjectSize(void* ObjectAddr, size_t& size) {
if (fp == nullptr) {
return Response::OIL_UNINITIALISED;
}
size = (*fp)(ObjectAddr);
return Response::OIL_SUCCESS;
}
} // namespace ObjectIntrospection
} // namespace oi

View File

@ -13,208 +13,241 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "oi/OILibraryImpl.h"
#include "OILibraryImpl.h"
#include <fcntl.h>
#include <glog/logging.h>
#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
#include <unistd.h>
#include <boost/core/demangle.hpp>
#include <boost/format.hpp>
#include <cerrno>
#include <cstring>
#include <fstream>
#include <stdexcept>
#include "oi/DrgnUtils.h"
#include "oi/Headers.h"
#include "oi/OIParser.h"
#include "oi/OIUtils.h"
extern "C" {
#include <libelf.h>
}
namespace oi::detail {
namespace {
// Map between the high level feature requests in the OIL API and the underlying
// codegen features.
std::map<Feature, bool> convertFeatures(std::unordered_set<oi::Feature> fs);
namespace ObjectIntrospection {
// Extract the root type from an atomic function pointer
drgn_qualified_type getTypeFromAtomicHole(drgn_program* prog, void* hole);
} // namespace
using namespace oi::detail;
OILibraryImpl::OILibraryImpl(OILibrary* self, void* TemplateFunc)
: _self(self), _TemplateFunc(TemplateFunc) {
if (_self->opts.debugLevel != 0) {
google::LogToStderr();
google::SetStderrLogging(0);
google::SetVLOGLevel("*", _self->opts.debugLevel);
// Upstream glog defines `GLOG_INFO` as 0 https://fburl.com/ydjajhz0,
// but internally it's defined as 1 https://fburl.com/code/9fwams75
//
// We don't want to link gflags in OIL, so setting it via the flags rather
// than with gflags::SetCommandLineOption
FLAGS_minloglevel = 0;
}
}
OILibraryImpl::~OILibraryImpl() {
unmapSegment();
}
bool OILibraryImpl::mapSegment() {
void* textSeg =
mmap(NULL, segConfig.textSegSize, PROT_EXEC | PROT_READ | PROT_WRITE,
OILibraryImpl::LocalTextSegment::LocalTextSegment(size_t size) {
void* base = mmap(NULL, size, PROT_EXEC | PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (textSeg == MAP_FAILED) {
PLOG(ERROR) << "error mapping text segment";
return false;
}
segConfig.textSegBase = textSeg;
if (base == MAP_FAILED)
throw std::runtime_error(std::string("segment map failed: ") +
std::strerror(errno));
return true;
data_ = {static_cast<uint8_t*>(base), size};
}
bool OILibraryImpl::unmapSegment() {
if (segConfig.textSegBase != nullptr &&
munmap(segConfig.textSegBase, segConfig.textSegSize) != 0) {
PLOG(ERROR) << "error unmapping text segment";
return false;
OILibraryImpl::LocalTextSegment::~LocalTextSegment() {
if (data_.empty())
return;
PLOG_IF(ERROR, munmap(data_.data(), data_.size()) != 0)
<< "segment unmap failed";
}
return true;
OILibraryImpl::MemoryFile::MemoryFile(const char* name) {
fd_ = memfd_create(name, 0);
if (fd_ == -1)
throw std::runtime_error(std::string("memfd creation failed: ") +
std::strerror(errno));
}
void OILibraryImpl::initCompiler() {
symbols = std::make_shared<SymbolService>(getpid());
OILibraryImpl::MemoryFile::~MemoryFile() {
if (fd_ == -1)
return;
generatorConfig.useDataSegment = false;
PLOG_IF(ERROR, close(fd_) == -1) << "memfd close failed";
}
bool OILibraryImpl::processConfigFile() {
auto features = utils::processConfigFile(
_self->opts.configFilePath,
{
{Feature::ChaseRawPointers, _self->opts.chaseRawPointers},
{Feature::PackStructs, true},
{Feature::PruneTypeGraph, true},
{Feature::GenJitDebug, _self->opts.generateJitDebugInfo},
},
compilerConfig, generatorConfig);
if (!features) {
return false;
}
generatorConfig.features = *features;
compilerConfig.features = *features;
return true;
std::filesystem::path OILibraryImpl::MemoryFile::path() {
return {(boost::format("/dev/fd/%1%") % fd_).str()};
}
template <class T, class F>
class Cleanup {
T resource;
F cleanupFunc;
public:
Cleanup(T _resource, F _cleanupFunc)
: resource{_resource}, cleanupFunc{_cleanupFunc} {};
~Cleanup() {
cleanupFunc(resource);
}
};
void close_file(std::FILE* fp) {
std::fclose(fp);
OILibraryImpl::OILibraryImpl(void* atomicHole,
std::unordered_set<oi::Feature> fs,
GeneratorOptions opts)
: atomicHole_(atomicHole),
requestedFeatures_(convertFeatures(std::move(fs))),
opts_(std::move(opts)) {
}
int OILibraryImpl::compileCode() {
OICompiler compiler{symbols, compilerConfig};
std::pair<void*, const exporters::inst::Inst&> OILibraryImpl::init() {
processConfigFile();
int objectMemfd = memfd_create("oil_object_code", 0);
if (!objectMemfd) {
PLOG(ERROR) << "failed to create memfd for object code";
return Response::OIL_COMPILATION_FAILURE;
constexpr size_t TextSegSize = 1u << 22;
textSeg = {TextSegSize};
return compileCode();
}
using unique_file_t = std::unique_ptr<std::FILE, decltype(&close_file)>;
unique_file_t objectStream(fdopen(objectMemfd, "w+"), &close_file);
if (!objectStream) {
PLOG(ERROR) << "failed to convert memfd to stream";
// This only needs to be cleaned up in the error case, as the fclose
// on the unique_file_t will clean up the underlying fd if it was
// created successfully.
close(objectMemfd);
return Response::OIL_COMPILATION_FAILURE;
}
auto objectPath =
fs::path((boost::format("/dev/fd/%1%") % objectMemfd).str());
void OILibraryImpl::processConfigFile() {
auto features =
utils::processConfigFile(opts_.configFilePath, requestedFeatures_,
compilerConfig_, generatorConfig_);
if (!features)
throw std::runtime_error("failed to process configuration");
struct drgn_program* prog = symbols->getDrgnProgram();
if (!prog) {
return Response::OIL_COMPILATION_FAILURE;
}
struct drgn_symbol* sym;
if (auto err = drgn_program_find_symbol_by_address(
prog, (uintptr_t)_TemplateFunc, &sym)) {
LOG(ERROR) << "Error when finding symbol by address " << err->code << " "
<< err->message;
drgn_error_destroy(err);
return Response::OIL_COMPILATION_FAILURE;
}
const char* name = drgn_symbol_name(sym);
drgn_symbol_destroy(sym);
// TODO: change this to the new drgn interface from symbol -> type
auto rootType = symbols->getRootType(irequest{"entry", name, "arg0"});
if (!rootType.has_value()) {
LOG(ERROR) << "Failed to get type of probe argument";
return Response::OIL_COMPILATION_FAILURE;
generatorConfig_.features = *features;
compilerConfig_.features = *features;
}
std::string code(headers::oi_OITraceCode_cpp);
std::pair<void*, const exporters::inst::Inst&> OILibraryImpl::compileCode() {
auto symbols = std::make_shared<SymbolService>(getpid());
auto codegen = OICodeGen::buildFromConfig(generatorConfig, *symbols);
if (!codegen) {
return OIL_COMPILATION_FAILURE;
}
auto* prog = symbols->getDrgnProgram();
CHECK(prog != nullptr) << "does this check need to exist?";
codegen->setRootType(rootType->type);
if (!codegen->generate(code)) {
return Response::OIL_COMPILATION_FAILURE;
}
auto rootType = getTypeFromAtomicHole(symbols->getDrgnProgram(), atomicHole_);
std::string sourcePath = _self->opts.sourceFileDumpPath;
if (_self->opts.sourceFileDumpPath.empty()) {
// This is the path Clang acts as if it has compiled from e.g. for debug
// information. It does not need to exist.
sourcePath = "oil_jit.cpp";
CodeGen codegen{generatorConfig_, *symbols};
std::string code;
if (!codegen.codegenFromDrgn(rootType.type, code))
throw std::runtime_error("oil jit codegen failed!");
std::string sourcePath = opts_.sourceFileDumpPath;
if (sourcePath.empty()) {
sourcePath = "oil_jit.cpp"; // fake path for JIT debug info
} else {
std::ofstream outputFile(sourcePath);
outputFile << code;
}
if (!compiler.compile(code, sourcePath, objectPath)) {
return Response::OIL_COMPILATION_FAILURE;
}
auto object = MemoryFile("oil_object_code");
OICompiler compiler{symbols, compilerConfig_};
if (!compiler.compile(code, sourcePath, object.path()))
throw std::runtime_error("oil jit compilation failed!");
auto relocRes = compiler.applyRelocs(
reinterpret_cast<uint64_t>(segConfig.textSegBase), {objectPath}, {});
if (!relocRes.has_value()) {
return Response::OIL_RELOCATION_FAILURE;
}
reinterpret_cast<uint64_t>(textSeg.data().data()), {object.path()}, {});
if (!relocRes)
throw std::runtime_error("oil jit relocation failed!");
const auto& [_, segments, jitSymbols] = relocRes.value();
const auto& [_, segments, jitSymbols] = *relocRes;
// Locate the probe's entry point
_self->fp = nullptr;
std::string nameHash =
(boost::format("%1$016x") %
std::hash<std::string>{}(SymbolService::getTypeName(rootType.type)))
.str();
std::string functionSymbolPrefix = "_Z27introspect_" + nameHash;
std::string typeSymbolName = "treeBuilderInstructions" + nameHash;
void* fp = nullptr;
const exporters::inst::Inst* ty = nullptr;
for (const auto& [symName, symAddr] : jitSymbols) {
if (symName.starts_with("_Z7getSize")) {
_self->fp = (size_t(*)(const void*))symAddr;
if (fp == nullptr && symName.starts_with(functionSymbolPrefix)) {
fp = reinterpret_cast<void*>(symAddr);
if (ty != nullptr)
break;
} else if (ty == nullptr && symName == typeSymbolName) {
ty = reinterpret_cast<const exporters::inst::Inst*>(symAddr);
if (fp != nullptr)
break;
}
}
if (!_self->fp) {
return Response::OIL_RELOCATION_FAILURE;
CHECK(fp != nullptr && ty != nullptr)
<< "failed to find always present symbols!";
for (const auto& [baseAddr, relocAddr, size] : segments)
std::memcpy(reinterpret_cast<void*>(relocAddr),
reinterpret_cast<void*>(baseAddr), size);
textSeg.release(); // don't munmap() the region containing the code
return {fp, *ty};
}
// Copy relocated segments in their final destination
for (const auto& [BaseAddr, RelocAddr, Size] : segments)
memcpy((void*)RelocAddr, (void*)BaseAddr, Size);
namespace {
std::map<Feature, bool> convertFeatures(std::unordered_set<oi::Feature> fs) {
std::map<Feature, bool> out{
{Feature::TypeGraph, true},
{Feature::TypedDataSegment, true},
{Feature::TreeBuilderTypeChecking, true},
{Feature::TreeBuilderV2, true},
{Feature::Library, true},
{Feature::PackStructs, true},
{Feature::PruneTypeGraph, true},
};
return Response::OIL_SUCCESS;
for (const auto f : fs) {
switch (f) {
case oi::Feature::ChaseRawPointers:
out[Feature::ChaseRawPointers] = true;
break;
case oi::Feature::CaptureThriftIsset:
out[Feature::CaptureThriftIsset] = true;
break;
case oi::Feature::GenJitDebug:
out[Feature::GenJitDebug] = true;
break;
}
}
} // namespace ObjectIntrospection
return out;
}
drgn_qualified_type getTypeFromAtomicHole(drgn_program* prog, void* hole) {
// get the getter type:
// std::atomic<std::vector<uint8_t> (*)(const T&)>& getIntrospectionFunc();
auto atomicGetterType =
SymbolService::findTypeOfAddr(prog, reinterpret_cast<uintptr_t>(hole));
if (!atomicGetterType)
throw std::runtime_error("failed to lookup function");
// get the return type:
// std::atomic<std::vector<uint8_t> (*)(const T&)>&
CHECK(drgn_type_has_type(atomicGetterType->type))
<< "functions have a return type";
auto retType = drgn_type_type(atomicGetterType->type);
// get the atomic type:
// std::atomic<std::vector<uint8_t> (*)(const T&)>
CHECK(drgn_type_has_type(retType.type)) << "pointers have a value type";
auto atomicType = drgn_type_type(retType.type);
// get the function pointer type:
// std::vector<uint8_t> (*)(const T&)
CHECK(drgn_type_has_template_parameters(atomicType.type))
<< "atomic should have template parameters";
CHECK(drgn_type_num_template_parameters(atomicType.type) == 1)
<< "atomic should have 1 template parameter";
auto* templateParam = drgn_type_template_parameters(atomicType.type);
struct drgn_qualified_type funcPointerType;
if (auto err = drgn_template_parameter_type(templateParam, &funcPointerType))
throw drgnplusplus::error(err);
// get the function type:
// std::vector<uint8_t>(const T&)
CHECK(drgn_type_has_type(funcPointerType.type))
<< "function pointers have a value type";
auto funcType = drgn_type_type(funcPointerType.type);
// get the argument type:
// const T&
CHECK(drgn_type_has_parameters(funcType.type)) << "functions have parameters";
CHECK(drgn_type_num_parameters(funcType.type) == 2)
<< "function should have 2 parameters";
drgn_qualified_type argType;
if (auto err =
drgn_parameter_type(drgn_type_parameters(funcType.type), &argType))
throw drgnplusplus::error(err);
// get the type
CHECK(drgn_type_has_type(argType.type))
<< "reference types have a value type";
return drgn_type_type(argType.type);
}
} // namespace
} // namespace oi::detail

View File

@ -14,38 +14,76 @@
* limitations under the License.
*/
#pragma once
#include <oi/oi.h>
#include "ObjectIntrospection.h"
#include "oi/OICodeGen.h"
#include <filesystem>
#include <map>
#include <span>
#include <unordered_set>
#include <utility>
#include "oi/CodeGen.h"
#include "oi/Features.h"
#include "oi/OICompiler.h"
#include "oi/SymbolService.h"
namespace ObjectIntrospection {
namespace oi::detail {
class OILibraryImpl {
private:
class LocalTextSegment {
public:
OILibraryImpl(OILibrary*, void*);
~OILibraryImpl();
LocalTextSegment() = default;
LocalTextSegment(size_t size);
~LocalTextSegment();
LocalTextSegment(const LocalTextSegment&) = delete;
LocalTextSegment& operator=(const LocalTextSegment&) = delete;
LocalTextSegment(LocalTextSegment&& that) {
std::swap(this->data_, that.data_);
}
LocalTextSegment& operator=(LocalTextSegment&& that) {
std::swap(this->data_, that.data_);
return *this;
}
bool mapSegment();
bool unmapSegment();
void initCompiler();
int compileCode();
bool processConfigFile();
void enableLayoutAnalysis();
std::span<uint8_t> data() {
return data_;
}
void release() {
data_ = {};
}
private:
class OILibrary* _self;
void* _TemplateFunc;
oi::detail::OICompiler::Config compilerConfig{};
oi::detail::OICodeGen::Config generatorConfig{};
std::shared_ptr<oi::detail::SymbolService> symbols{};
struct c {
void* textSegBase = nullptr;
size_t textSegSize = 1u << 22;
} segConfig;
std::span<uint8_t> data_;
};
} // namespace ObjectIntrospection
class MemoryFile {
public:
MemoryFile(const char* name);
~MemoryFile();
std::filesystem::path path();
private:
int fd_ = -1;
};
public:
OILibraryImpl(void* atomicHole,
std::unordered_set<oi::Feature> fs,
GeneratorOptions opts);
std::pair<void*, const exporters::inst::Inst&> init();
private:
void* atomicHole_;
std::map<Feature, bool> requestedFeatures_;
GeneratorOptions opts_;
oi::detail::OICompiler::Config compilerConfig_{};
oi::detail::OICodeGen::Config generatorConfig_{};
LocalTextSegment textSeg;
void processConfigFile();
std::pair<void*, const exporters::inst::Inst&> compileCode();
};
} // namespace oi::detail

View File

@ -34,12 +34,6 @@
#define C10_USING_CUSTOM_GENERATED_MACROS
// These globals are set by oid, see end of OIDebugger::compileCode()
extern uint8_t* dataBase;
extern size_t dataSize;
extern uintptr_t cookieValue;
extern int logFile;
constexpr int oidMagicId = 0x01DE8;
#include <array>
@ -89,21 +83,6 @@ class {
}
} static pointers;
void __jlogptr(uintptr_t ptr) {
static constexpr char hexdigits[] = "0123456789abcdef";
static constexpr size_t ptrlen = 2 * sizeof(ptr);
static char hexstr[ptrlen + 1] = {};
size_t i = ptrlen;
while (i--) {
hexstr[i] = hexdigits[ptr & 0xf];
ptr = ptr >> 4;
}
hexstr[ptrlen] = '\n';
write(logFile, hexstr, sizeof(hexstr));
}
} // namespace
// alignas(0) is ignored according to docs so can be default

View File

@ -298,7 +298,7 @@ bool SymbolService::loadModules() {
return true;
}
static std::optional<drgn_qualified_type> findTypeOfSymbol(
std::optional<drgn_qualified_type> SymbolService::findTypeOfSymbol(
drgn_program* prog, const std::string& symbolName) {
drgn_symbol* sym;
if (auto* err =
@ -313,6 +313,16 @@ static std::optional<drgn_qualified_type> findTypeOfSymbol(
uint64_t addr = drgn_symbol_address(sym);
drgn_symbol_destroy(sym);
if (auto t = findTypeOfAddr(prog, addr)) {
return t;
} else {
LOG(ERROR) << "Failed to lookup symbol '" << symbolName;
return std::nullopt;
}
}
std::optional<drgn_qualified_type> SymbolService::findTypeOfAddr(
drgn_program* prog, uintptr_t addr) {
drgn_object obj;
drgn_object_init(&obj, prog);
@ -320,7 +330,7 @@ static std::optional<drgn_qualified_type> findTypeOfSymbol(
if (auto* err =
drgn_program_find_function_by_address(prog, addr, &name, &obj);
err != nullptr) {
LOG(ERROR) << "Failed to lookup function '" << symbolName
LOG(ERROR) << "Failed to lookup function '" << reinterpret_cast<void*>(addr)
<< "': " << err->code << " " << err->message;
drgn_error_destroy(err);
return std::nullopt;
@ -598,7 +608,7 @@ static std::optional<std::shared_ptr<FuncDesc>> createFuncDesc(
struct drgn_program* prog, const irequest& request) {
VLOG(1) << "Creating function description for: " << request.func;
auto ft = findTypeOfSymbol(prog, request.func);
auto ft = SymbolService::findTypeOfSymbol(prog, request.func);
if (!ft) {
return std::nullopt;
}

View File

@ -56,6 +56,11 @@ class SymbolService {
static std::string getTypeName(struct drgn_type*);
std::optional<RootInfo> getRootType(const irequest&);
static std::optional<drgn_qualified_type> findTypeOfSymbol(
drgn_program*, const std::string& symbolName);
static std::optional<drgn_qualified_type> findTypeOfAddr(drgn_program*,
uintptr_t addr);
std::unordered_map<std::string, std::shared_ptr<FuncDesc>> funcDescs;
std::unordered_map<std::string, std::shared_ptr<GlobalDesc>> globalDescs;

125
oi/exporters/Json.cpp Normal file
View File

@ -0,0 +1,125 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <oi/exporters/Json.h>
#include <stdexcept>
template <class>
inline constexpr bool always_false_v = false;
namespace oi::exporters {
namespace {
template <typename It>
void printStringList(std::ostream& out, It it, It end, bool pretty) {
out << '[';
for (; it != end; ++it) {
out << '"' << (*it) << '"';
if (it != end - 1)
out << (pretty ? ", " : ",");
}
out << ']';
}
std::string makeIndent(std::vector<std::string_view>& tp) {
return std::string((tp.size() - 1) * 4, ' ');
}
} // namespace
Json::Json(std::ostream& out) : out_(out) {
}
void Json::print(const IntrospectionResult& r) {
auto begin = r.cbegin();
return print(begin, r.cend());
}
void Json::print(IntrospectionResult::const_iterator& it,
IntrospectionResult::const_iterator end) {
std::vector<std::string_view> firstTypePath = it->type_path;
const auto indent = pretty_ ? makeIndent(firstTypePath) : "";
const auto* tab = pretty_ ? " " : "";
const auto* space = pretty_ ? " " : "";
const auto* endl = pretty_ ? "\n" : "";
out_ << '[';
out_ << endl << indent;
bool first = true;
while (it != end) {
if (it->type_path.size() < firstTypePath.size()) {
// no longer a sibling, must be a sibling of the type we're printing
break;
}
if (!first)
out_ << ',' << endl << indent;
first = false;
out_ << '{' << endl << indent;
out_ << tab << "\"name\"" << space << ':' << space << "\"" << it->name
<< "\"," << endl
<< indent;
out_ << tab << "\"typePath\"" << space << ':' << space << "";
printStringList(out_, it->type_path.begin(), it->type_path.end(), pretty_);
out_ << (pretty_ ? ",\n" : ",") << indent;
out_ << tab << "\"typeNames\"" << space << ':' << space;
printStringList(out_, it->type_names.begin(), it->type_names.end(),
pretty_);
out_ << ',' << endl << indent;
out_ << tab << "\"staticSize\":" << space << it->static_size << ',' << endl
<< indent;
out_ << tab << "\"exclusiveSize\":" << space << it->exclusive_size << ','
<< endl
<< indent;
if (it->pointer.has_value()) {
out_ << tab << "\"pointer\":" << space << *(it->pointer) << ',' << endl
<< indent;
}
if (it->container_stats.has_value()) {
out_ << tab << "\"length\":" << space << it->container_stats->length
<< ',' << endl
<< indent;
out_ << tab << "\"capacity\":" << space << it->container_stats->capacity
<< ',' << endl
<< indent;
}
if (it->is_set_stats.has_value()) {
out_ << tab << "\"is_set\":" << space << it->is_set_stats->is_set << ','
<< endl
<< indent;
}
out_ << tab << "\"members\":" << space;
if ((++it)->type_path.size() > firstTypePath.size()) {
print(it, end);
} else {
out_ << "[]" << endl << indent;
}
out_ << "}";
}
out_ << endl << indent << ']' << endl;
}
} // namespace oi::exporters

View File

@ -0,0 +1,76 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <oi/exporters/ParsedData.h>
#include <cassert>
#include <stdexcept>
#include <type_traits>
template <typename T>
constexpr bool always_false_v = false;
namespace oi::exporters {
namespace {
uint64_t parseVarint(std::vector<uint8_t>::const_iterator& it);
}
ParsedData ParsedData::parse(std::vector<uint8_t>::const_iterator& it,
types::dy::Dynamic dy) {
return std::visit(
[&it](const auto el) -> ParsedData {
auto ty = el.get();
using T = std::decay_t<decltype(ty)>;
if constexpr (std::is_same_v<T, types::dy::Unit>) {
return ParsedData::Unit{};
} else if constexpr (std::is_same_v<T, types::dy::VarInt>) {
return ParsedData::VarInt{.value = parseVarint(it)};
} else if constexpr (std::is_same_v<T, types::dy::Pair>) {
return ParsedData::Pair{
.first = Lazy{it, ty.first},
.second = Lazy{it, ty.second},
};
} else if constexpr (std::is_same_v<T, types::dy::List>) {
return ParsedData::List{
.length = parseVarint(it),
.values = {it, ty.element},
};
} else if constexpr (std::is_same_v<T, types::dy::Sum>) {
auto index = parseVarint(it);
assert(index < ty.variants.size());
return ParsedData::Sum{
.index = parseVarint(it),
.value = {it, ty.variants[index]},
};
} else {
static_assert(always_false_v<T>, "non-exhaustive visitor!");
}
},
dy);
}
namespace {
uint64_t parseVarint(std::vector<uint8_t>::const_iterator& it) {
uint64_t v = 0;
int shift = 0;
while (*it >= 0x80) {
v |= static_cast<uint64_t>(*it++ & 0x7f) << shift;
shift += 7;
}
v |= static_cast<uint64_t>(*it++ & 0x7f) << shift;
return v;
}
} // namespace
} // namespace oi::exporters

View File

@ -71,6 +71,8 @@ void NameGen::visit(Class& c) {
std::string name = c.name();
removeTemplateParams(name);
deduplicate(name);
if (c.name().empty())
c.setInputName(name);
c.setName(name);
// Deduplicate member names. Duplicates may be present after flattening.
@ -151,6 +153,8 @@ void NameGen::visit(Container& c) {
void NameGen::visit(Enum& e) {
std::string name = e.name();
deduplicate(name);
if (e.name().empty())
e.setInputName(name);
e.setName(name);
}

View File

@ -40,7 +40,6 @@ namespace {
void print(const TypeGraph& typeGraph) {
if (!VLOG_IS_ON(1))
return;
std::stringstream out;
Printer printer{out, typeGraph.resetTracker(), typeGraph.size()};
for (const auto& type : typeGraph.rootTypes()) {

View File

@ -231,6 +231,10 @@ class Class : public Type {
name_ = std::move(name);
}
void setInputName(std::string name) {
inputName_ = std::move(name);
}
virtual size_t size() const override {
return size_;
}
@ -376,6 +380,10 @@ class Enum : public Type {
return inputName_;
}
void setInputName(std::string name) {
inputName_ = std::move(name);
}
void setName(std::string name) {
name_ = std::move(name);
}

View File

@ -7,6 +7,9 @@ function(embed_headers output)
file(APPEND ${output} "namespace oi::detail::headers {\n")
set(HEADERS
../include/oi/exporters/ParsedData.h
../include/oi/exporters/inst.h
../include/oi/result/Element.h
../include/oi/types/dy.h
../include/oi/types/st.h
../oi/OITraceCode.cpp

View File

@ -50,7 +50,7 @@ add_executable(integration_test_target
${INTEGRATION_TEST_TARGET_SRC}
folly_shims.cpp)
target_compile_options(integration_test_target PRIVATE -O1)
target_link_libraries(integration_test_target PRIVATE oil Boost::headers ${Boost_LIBRARIES})
target_link_libraries(integration_test_target PRIVATE oil_jit exporters_json Boost::headers ${Boost_LIBRARIES})
add_executable(integration_test_runner ${INTEGRATION_TEST_RUNNER_SRC} runner_common.cpp)
target_include_directories(integration_test_runner PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})

View File

@ -56,6 +56,12 @@ definitions = '''
{"typeName": "Align16", "staticSize": 16, "exclusiveSize": 15, "members": [
{"typeName": "char", "staticSize": 1, "exclusiveSize": 1}
]}]}]'''
expect_json_v2 = '''[
{"staticSize": 32, "exclusiveSize": 15, "members": [
{"typeNames": ["int8_t", "__int8_t", "int8_t"], "staticSize": 1, "exclusiveSize": 1},
{"typeNames": ["Align16"], "staticSize": 16, "exclusiveSize": 15, "members": [
{"typeNames": ["int8_t"], "staticSize": 1, "exclusiveSize": 1}
]}]}]'''
[cases.container_struct]
skip = "container alignment is broken (#143)"
param_types = ["const std::optional<Align16>&"]
@ -78,6 +84,16 @@ definitions = '''
{"typeName": "Align16", "staticSize": 16, "exclusiveSize": 15, "members": [
{"typeName": "char", "staticSize": 1, "exclusiveSize": 1}]}
]}]}]'''
expect_json_v2 = '''[
{"staticSize": 64, "exclusiveSize": 15, "members": [
{"typeNames": ["int8_t", "__int8_t", "int8_t"], "staticSize": 1, "exclusiveSize": 1},
{"typeNames": ["TwoStruct"], "staticSize": 48, "exclusiveSize": 15, "members": [
{"typeNames": ["Align16"], "staticSize": 16, "exclusiveSize": 15, "members": [
{"typeNames": ["int8_t"], "staticSize": 1, "exclusiveSize": 1}]},
{"typeNames": ["int8_t"], "staticSize": 1, "exclusiveSize": 1},
{"typeNames": ["Align16"], "staticSize": 16, "exclusiveSize": 15, "members": [
{"typeNames": ["int8_t"], "staticSize": 1, "exclusiveSize": 1}]}
]}]}]'''
[cases.container_two_members]
skip = "container alignment is broken (#143)"
param_types = ["const std::optional<TwoStruct>&"]
@ -101,6 +117,13 @@ definitions = '''
{"typeName": "char", "staticSize": 1, "exclusiveSize": 1},
{"typeName": "char", "staticSize": 1, "exclusiveSize": 1}
]}]}]'''
expect_json_v2 = '''[
{"staticSize": 96, "exclusiveSize": 31, "members": [
{"typeNames": ["int8_t", "__int8_t", "int8_t"], "staticSize": 1, "exclusiveSize": 1},
{"typeNames": ["MemberAlignment"], "staticSize": 64, "exclusiveSize": 62, "members": [
{"typeNames": ["int8_t"], "staticSize": 1, "exclusiveSize": 1},
{"typeNames": ["int8_t"], "staticSize": 1, "exclusiveSize": 1}
]}]}]'''
[cases.container_member_alignment]
skip = "container alignment is broken (#143)"
param_types = ["const std::optional<MemberAlignment>&"]
@ -119,6 +142,11 @@ definitions = '''
{"typeName": "int8_t", "staticSize": 1, "exclusiveSize": 1},
{"typeName": "UnionMember", "staticSize": 32, "exclusiveSize": 32, "NOT":"members"}
]}]'''
expect_json_v2 = '''[
{"staticSize": 64, "exclusiveSize": 31, "members": [
{"typeNames": ["int8_t", "__int8_t", "int8_t"], "staticSize": 1, "exclusiveSize": 1},
{"typeNames": ["UnionMember"], "staticSize": 32, "exclusiveSize": 32, "members":[]}
]}]'''
[cases.container_union_member]
skip = "container alignment is broken (#143)"
param_types = ["const std::optional<UnionMember>&"]
@ -138,6 +166,14 @@ definitions = '''
{"typeName": "Align16", "staticSize": 16, "exclusiveSize": 15, "members": [
{"typeName": "char", "staticSize": 1, "exclusiveSize": 1}
]}]}]}]'''
expect_json_v2 = '''[
{"staticSize": 96, "exclusiveSize": 31, "members": [
{"typeNames": ["int8_t", "__int8_t", "int8_t"], "staticSize": 1, "exclusiveSize": 1},
{"typeNames": ["MemberAlignmentOverriden"], "staticSize": 64, "exclusiveSize": 47, "members": [
{"typeNames": ["int8_t"], "staticSize": 1, "exclusiveSize": 1},
{"typeNames": ["Align16"], "staticSize": 16, "exclusiveSize": 15, "members": [
{"typeNames": ["int8_t"], "staticSize": 1, "exclusiveSize": 1}
]}]}]}]'''
[cases.container_member_override]
skip = "container alignment is broken (#143)"
param_types = ["const std::optional<MemberAlignmentOverriden>&"]
@ -159,6 +195,13 @@ definitions = '''
{"typeName": "char", "staticSize": 1, "exclusiveSize": 1},
{"typeName": "char", "staticSize": 1, "exclusiveSize": 1}
]}]}]'''
expect_json_v2 = '''[
{"staticSize": 256, "exclusiveSize": 127, "members": [
{"typeNames": ["int8_t", "__int8_t", "int8_t"], "staticSize": 1, "exclusiveSize": 1},
{"typeNames": ["AlignedStructMemberAlignLower"], "staticSize": 128, "exclusiveSize": 126, "members": [
{"typeNames": ["int8_t"], "staticSize": 1, "exclusiveSize": 1},
{"typeNames": ["int8_t"], "staticSize": 1, "exclusiveSize": 1}
]}]}]'''
[cases.container_member_lower]
skip = "container alignment is broken (#143)"
param_types = ["const std::optional<AlignedStructMemberAlignLower>&"]

View File

@ -192,6 +192,7 @@ definitions = '''
}]'''
[cases.anon_union]
oil_skip = "anonymous unions are fully stubbed in the generated code" # https://github.com/facebookexperimental/object-introspection/issues/292
param_types = ["const AnonUnionContainer&"]
setup = 'return AnonUnionContainer{ .a = 3 };'
cli_options = ["-fchase-raw-pointers"]
@ -204,6 +205,15 @@ definitions = '''
{"name":"e", "staticSize":4, "dynamicSize":0, "typeName":"int"}
]
}]'''
expect_json_v2 = '''[{
"staticSize": 24,
"exclusiveSize": 10,
"members": [
{"name":"__anon_member_0", "staticSize":2, "exclusiveSize":2},
{"name":"__anon_member_1", "staticSize":8, "exclusiveSize":8},
{"name":"e", "staticSize":4, "exclusiveSize":4, "typeNames":["int32_t"]}
]
}]'''
[cases.nested_anon_struct]
oil_disable = "oil can't chase raw pointers safely"

View File

@ -30,7 +30,17 @@ definitions = '''
"capacity":10,
"elementStaticSize":4
}]}]'''
expect_json_v2 = '''[{
"staticSize":40,
"exclusiveSize":0,
"members":[{
"staticSize":40,
"exclusiveSize":0,
"length":10,
"capacity":10
}]}]'''
[cases.member_int0]
oil_skip = 'zero length arrays fail codegen v2' # https://github.com/facebookexperimental/object-introspection/issues/295
# WARNING: zero-length arrays are handled differently to non-empty arrays.
# They end up not being treated as containers. This should probably change
# in the future.
@ -43,8 +53,16 @@ definitions = '''
"staticSize":0,
"dynamicSize":0
}]}]'''
expect_json_v2 = '''[{
"staticSize":1,
"exclusiveSize":1,
"members":[{
"staticSize":0,
"exclusiveSize":0
}]}]'''
[cases.multidim_legacy] # Test for legacy behaviour. Remove with OICodeGen
cli_options = ["-Ftype-graph", "-Ftyped-data-segment", "-Ftree-builder-type-checking"]
oil_disable = 'oil only runs on codegen v2'
cli_options = ["-Ftype-graph", "-Ftyped-data-segment", "-Ftree-builder-type-checking", "-Ftree-builder-v2"]
param_types = ["const MultiDim&"]
setup = "return {};"
expect_json = '''[{
@ -67,6 +85,12 @@ definitions = '''
{"staticSize":12, "dynamicSize":0, "exclusiveSize":12, "length":3, "capacity":3, "elementStaticSize":4},
{"staticSize":12, "dynamicSize":0, "exclusiveSize":12, "length":3, "capacity":3, "elementStaticSize":4}]
}]}]'''
expect_json_v2 = '''[
{"staticSize":24, "exclusiveSize":0, "members":[
{"staticSize":24, "exclusiveSize":0, "length":2, "capacity":2, "members":[
{"staticSize":12, "exclusiveSize":0, "length":3, "capacity":3},
{"staticSize":12, "exclusiveSize":0, "length":3, "capacity":3}]
}]}]'''
[cases.direct_int10]
skip = "Direct array arguments don't work"
param_types = ["int[10]"]

View File

@ -154,6 +154,7 @@ definitions = '''
'''
[cases.unique_ptr]
oil_skip = "cycles are broken" # https://github.com/facebookexperimental/object-introspection/issues/293
param_types = ["std::reference_wrapper<UniqueNode>&"]
setup = '''
auto first = std::make_unique<UniqueNode>();
@ -231,6 +232,7 @@ definitions = '''
'''
[cases.shared_ptr]
oil_skip = "cycles are broken" # https://github.com/facebookexperimental/object-introspection/issues/293
param_types = ["std::reference_wrapper<SharedNode>&"]
setup = '''
auto first = std::make_shared<SharedNode>();

View File

@ -38,7 +38,8 @@ definitions = '''
[cases.scoped_enum_val_cast]
param_types = ["const std::array<int, static_cast<size_t>(MyNS::ScopedEnum::Two)>&"]
setup = "return {};"
expect_json = '[{"staticSize":8, "dynamicSize":0, "length":2, "capacity":2, "elementStaticSize":4}]'
expect_json = '[{"staticSize":8, "length":2, "capacity":2, "elementStaticSize":4}]'
expect_json_v2 = '[{"staticSize":8, "dynamicSize":0, "length":2, "capacity":2}]'
[cases.scoped_enum_val]
param_types = ["const MyClass<MyNS::ScopedEnum::One>&"]
@ -59,4 +60,5 @@ definitions = '''
[cases.unscoped_enum_val_cast]
param_types = ["const std::array<int, MyNS::ONE>&"]
setup = "return {};"
expect_json = '[{"staticSize":4, "dynamicSize":0, "length":1, "capacity":1, "elementStaticSize":4}]'
expect_json = '[{"staticSize":4, "length":1, "capacity":1, "elementStaticSize":4}]'
expect_json_v2 = '[{"staticSize":4, "length":1, "capacity":1}]'

View File

@ -1,6 +1,7 @@
includes = ["folly/FBString.h"]
[cases]
[cases.empty]
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/322
param_types = ["folly::fbstring&"]
setup = "return {};"
expect_json = '''
@ -26,6 +27,7 @@ includes = ["folly/FBString.h"]
'''
[cases.inline]
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/322
param_types = ["folly::fbstring&"]
setup = 'return {"012345"};'
expect_json = '''
@ -51,6 +53,7 @@ includes = ["folly/FBString.h"]
'''
[cases.heap_allocated]
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/322
param_types = ["folly::fbstring&"]
setup = 'return {"abcdefghijklmnopqrstuvwxzy"};'
expect_json = '''
@ -76,6 +79,7 @@ includes = ["folly/FBString.h"]
'''
[cases.string_pooled]
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/322
param_types = ["folly::fbstring&"]
setup = "return folly::fbstring(1024, 'c');"
expect_json = '''

View File

@ -1,23 +1,28 @@
includes = ["folly/small_vector.h", "vector"]
[cases]
[cases.int_default_empty]
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/321
param_types = ["const folly::small_vector<int>&"]
setup = "return {};"
expect_json = '[{"staticSize":16, "dynamicSize":0, "exclusiveSize":16, "length":0, "capacity":2, "elementStaticSize":4}]'
[cases.int_default_inlined]
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/321
param_types = ["const folly::small_vector<int>&"]
setup = "return {{1,2}};"
expect_json = '[{"staticSize":16, "dynamicSize":0, "exclusiveSize":16, "length":2, "capacity":2, "elementStaticSize":4}]'
[cases.int_default_overflow]
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/321
param_types = ["const folly::small_vector<int>&"]
setup = "return {{1,2,3,4}};"
expect_json = '[{"staticSize":16, "dynamicSize":24, "exclusiveSize":40, "length":4, "capacity":6, "elementStaticSize":4}]'
[cases.vector_3_empty]
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/321
param_types = ["const folly::small_vector<std::vector<int>, 3>&"]
setup = "return {};"
expect_json = '[{"staticSize":80, "dynamicSize":0, "exclusiveSize":80, "length":0, "capacity":3, "elementStaticSize":24}]'
[cases.vector_3_inlined]
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/321
param_types = ["const folly::small_vector<std::vector<int>, 3>&"]
setup = "return {{ {1,2,3}, {4}, {5,6} }};"
expect_json = '''[
@ -27,6 +32,7 @@ includes = ["folly/small_vector.h", "vector"]
{"staticSize":24, "dynamicSize":8, "exclusiveSize":32, "length":2, "capacity":2, "elementStaticSize":4}
]}]'''
[cases.vector_3_overflow]
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/321
param_types = ["const folly::small_vector<std::vector<int>, 3>&"]
setup = "return {{ {1,2,3}, {4}, {5,6}, {7} }};"
expect_json = '''[
@ -38,6 +44,7 @@ includes = ["folly/small_vector.h", "vector"]
]}]'''
[cases.int_always_heap]
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/321
param_types = ["const folly::small_vector<int, 0>&"]
setup = "return {{1}};"
expect_json = '[{"staticSize":16, "dynamicSize":4, "exclusiveSize":20, "length":1, "capacity":1, "elementStaticSize":4}]'

View File

@ -4,10 +4,39 @@ includes = ["folly/sorted_vector_types.h", "vector"]
param_types = ["const folly::sorted_vector_map<int, int>&"]
setup = "return {};"
expect_json = '[{"staticSize":24, "dynamicSize":0, "exclusiveSize":24, "length":0, "capacity":0, "elementStaticSize":8}]'
expect_json_v2 = '[{"staticSize":24, "exclusiveSize":24, "length":0, "capacity":0, "members":[]}]'
[cases.int_int_some]
param_types = ["const folly::sorted_vector_map<int, int>&"]
setup = "return {{ {1,2}, {3,4} }};"
expect_json = '[{"staticSize":24, "dynamicSize":16, "exclusiveSize":40, "length":2, "capacity":2, "elementStaticSize":8}]'
expect_json_v2 = '''[{"staticSize":24, "exclusiveSize":24, "length":2, "capacity":2, "members":[
{"staticSize":8, "exclusiveSize":0, "members": [
{"name":"key", "staticSize":4, "exclusiveSize":4},
{"name":"value", "staticSize":4, "exclusiveSize":4}
]},
{"staticSize":8, "exclusiveSize":0, "members": [
{"name":"key", "staticSize":4, "exclusiveSize":4},
{"name":"value", "staticSize":4, "exclusiveSize":4}
]}
]}]'''
[cases.int_int_reserve]
param_types = ["const folly::sorted_vector_map<int, int>&"]
setup = '''
folly::sorted_vector_map<int, int> m{{ {1,2}, {3,4} }};
m.reserve(10);
return m;
'''
expect_json = '[{"staticSize":24, "dynamicSize":80, "exclusiveSize":104, "length":2, "capacity":10, "elementStaticSize":8}]'
expect_json_v2 = '''[{"staticSize":24, "exclusiveSize":88, "length":2, "capacity":10, "members":[
{"staticSize":8, "exclusiveSize":0, "members": [
{"name":"key", "staticSize":4, "exclusiveSize":4},
{"name":"value", "staticSize":4, "exclusiveSize":4}
]},
{"staticSize":8, "exclusiveSize":0, "members": [
{"name":"key", "staticSize":4, "exclusiveSize":4},
{"name":"value", "staticSize":4, "exclusiveSize":4}
]}
]}]'''
[cases.int_vector_empty]
skip = "Wrong results" # https://github.com/facebookexperimental/object-introspection/issues/258

View File

@ -38,7 +38,8 @@ def add_headers(f, custom_headers, thrift_headers):
#include <thread>
#include <tuple>
#include <ObjectIntrospection.h>
#include <oi/oi.h>
#include <oi/exporters/Json.h>
"""
)
@ -83,11 +84,11 @@ def add_test_setup(f, config):
def define_traceable_func(name, params, body):
return (
f"\n"
f' extern "C" {{\n'
f" void __attribute__((noinline)) {name}({params}) {{\n"
# f' extern "C" {{\n'
f' extern "C" void __attribute__((noinline)) {name}({params}) {{\n'
f"{body}"
f" }}\n"
f" }}\n"
# f" }}\n"
)
cases = config["cases"]
@ -134,22 +135,18 @@ def add_test_setup(f, config):
)
oil_func_body = (
f"\n"
f"ObjectIntrospection::options opts{{\n"
f" oi::GeneratorOptions opts{{\n"
f" .configFilePath = configFile,\n"
f" .debugLevel = 3,\n"
f' .sourceFileDumpPath = "oil_jit_code.cpp",\n'
f"}};"
f" .debugLevel = 3,\n"
f" }};\n\n"
)
oil_func_body += ' std::cout << "{\\"results\\": [" << std::endl;\n'
oil_func_body += ' std::cout << "," << std::endl;\n'.join(
f" size_t size{i} = 0;\n"
f" auto ret{i} = ObjectIntrospection::getObjectSize(a{i}, size{i}, opts);\n"
f' std::cout << "{{\\"ret\\": " << ret{i} << ", \\"size\\": " << size{i} << "}}" << std::endl;\n'
for i in range(len(case["param_types"]))
)
oil_func_body += ' std::cout << "]}" << std::endl;\n'
oil_func_body += " auto pr = oi::exporters::Json(std::cout);\n"
oil_func_body += " pr.setPretty(true);\n"
for i in range(len(case["param_types"])):
oil_func_body += f" auto ret{i} = oi::setupAndIntrospect(a{i}, opts);\n"
oil_func_body += f" pr.print(*ret{i});\n"
f.write(
define_traceable_func(
@ -367,23 +364,14 @@ def add_oil_integration_test(f, config, case_name, case):
f' .targetArgs = "oil {case_str}",\n'
f" }}, std::move(configPrefix), std::move(configSuffix));\n\n"
f" ASSERT_EQ(exit_code(target), {exit_code});\n"
f"\n"
f" bpt::ptree result_json;\n"
f" auto json_ss = std::stringstream(stdout_);\n"
f" bpt::read_json(json_ss, result_json);\n"
f" std::vector<size_t> sizes;\n"
f' for (const auto& each : result_json.get_child("results")) {{\n'
f" const auto& result = each.second;\n"
f' int oilResult = result.get<int>("ret");\n'
f' size_t oilSize = result.get<size_t>("size");\n'
f" ASSERT_EQ(oilResult, 0);\n"
f" sizes.push_back(oilSize);\n"
f" }}"
)
if "expect_json" in case:
key = "expect_json"
if "expect_json_v2" in case:
key = "expect_json_v2"
if key in case:
try:
json.loads(case["expect_json"])
json.loads(case[key])
except json.decoder.JSONDecodeError as error:
print(
f"\x1b[31m`expect_json` value for test case {config['suite']}.{case_name} was invalid JSON: {error}\x1b[0m",
@ -394,17 +382,12 @@ def add_oil_integration_test(f, config, case_name, case):
f.write(
f"\n"
f" std::stringstream expected_json_ss;\n"
f' expected_json_ss << R"--({case["expect_json"]})--";\n'
f" bpt::ptree expected_json;\n"
f' expected_json_ss << R"--({case[key]})--";\n'
f" auto result_json_ss = std::stringstream(stdout_);\n"
f" bpt::ptree expected_json, actual_json;\n"
f" bpt::read_json(expected_json_ss, expected_json);\n"
f" auto sizes_it = sizes.begin();\n"
f" for (auto it = expected_json.begin(); it != expected_json.end(); ++it, ++sizes_it) {{\n"
f" auto node = it->second;\n"
f' size_t expected_size = node.get<size_t>("staticSize");\n'
f' if (node.find("dynamicSize") != node.not_found())\n' # Assume dynamicSize is 0 if not set
f' expected_size += node.get<size_t>("dynamicSize");\n'
f" EXPECT_EQ(*sizes_it, expected_size);\n"
f" }}\n"
f" bpt::read_json(result_json_ss, actual_json);\n"
f" compare_json(expected_json, actual_json);\n"
)
f.write(f"}}\n")

View File

@ -10,6 +10,7 @@ definitions = '''
[cases]
[cases.a]
oil_skip = 'v2 hides the member entirely when it should show it with static size' # todo: github issue
param_types = ["const Bar&"]
setup = """
return Bar{

View File

@ -1,18 +1,18 @@
definitions = '''
class Base {
int base_int;
int32_t base_int;
};
class Public : public Base {
int public_int;
int32_t public_int;
};
class Protected : protected Base {
int protected_int;
int32_t protected_int;
};
class Private : private Base {
int private_int;
int32_t private_int;
};
'''
[cases]
@ -21,30 +21,27 @@ definitions = '''
setup = "return {};"
expect_json = '''[{
"staticSize":8,
"dynamicSize":0,
"members":[
{"name":"base_int", "staticSize":4, "dynamicSize":0, "typeName": "int"},
{"name":"public_int", "staticSize":4, "dynamicSize":0, "typeName": "int"}
{"name":"base_int", "staticSize":4, "typeName": "int32_t"},
{"name":"public_int", "staticSize":4, "typeName": "int32_t"}
]}]'''
[cases.protected]
param_types = ["const Protected&"]
setup = "return {};"
expect_json = '''[{
"staticSize":8,
"dynamicSize":0,
"members":[
{"name":"base_int", "staticSize":4, "dynamicSize":0, "typeName": "int"},
{"name":"protected_int", "staticSize":4, "dynamicSize":0, "typeName": "int"}
{"name":"base_int", "staticSize":4, "typeName": "int32_t"},
{"name":"protected_int", "staticSize":4, "typeName": "int32_t"}
]}]'''
[cases.private]
param_types = ["const Private&"]
setup = "return {};"
expect_json = '''[{
"staticSize":8,
"dynamicSize":0,
"members":[
{"name":"base_int", "staticSize":4, "dynamicSize":0, "typeName": "int"},
{"name":"private_int", "staticSize":4, "dynamicSize":0, "typeName": "int"}
{"name":"base_int", "staticSize":4, "typeName": "int32_t"},
{"name":"private_int", "staticSize":4, "typeName": "int32_t"}
]}]'''
[cases.public_as_base]
param_types = ["const Base&"]
@ -52,7 +49,6 @@ definitions = '''
setup = "return {};"
expect_json = '''[{
"staticSize":4,
"dynamicSize":0,
"members":[
{"name":"base_int", "staticSize":4, "dynamicSize":0, "typeName": "int"}
{"name":"base_int", "staticSize":4, "typeName": "int32_t"}
]}]'''

View File

@ -34,3 +34,14 @@ definitions = '''
{"name":"e", "staticSize":4, "dynamicSize":0, "typeName": "int"},
{"name":"f", "staticSize":4, "dynamicSize":0, "typeName": "int"}
]}]'''
expect_json_v2 = '''[{
"staticSize":24,
"exclusiveSize":0,
"members":[
{"name":"a", "staticSize":4, "exclusiveSize":4, "typeNames": ["int32_t"]},
{"name":"b", "staticSize":4, "exclusiveSize":4, "typeNames": ["int32_t"]},
{"name":"c", "staticSize":4, "exclusiveSize":4, "typeNames": ["int32_t"]},
{"name":"d", "staticSize":4, "exclusiveSize":4, "typeNames": ["int32_t"]},
{"name":"e", "staticSize":4, "exclusiveSize":4, "typeNames": ["int32_t"]},
{"name":"f", "staticSize":4, "exclusiveSize":4, "typeNames": ["int32_t"]}
]}]'''

View File

@ -82,6 +82,7 @@ definitions = '''
{"name":"vec_b", "staticSize":24, "dynamicSize":12, "length":3, "capacity":3, "elementStaticSize":4}
]}]'''
[cases.b_no_polymorphic]
oil_skip = "vptr included in output when it should probably be hidden" # https://github.com/facebookexperimental/object-introspection/issues/291
param_types = ["const B&"]
arg_types = ["B"]
setup = '''
@ -157,6 +158,7 @@ definitions = '''
{"name":"int_c", "staticSize":4, "dynamicSize":0}
]}]'''
[cases.c_no_polymorphic]
oil_skip = 'vptrs are included int he json output' # https://github.com/facebookexperimental/object-introspection/issues/291
param_types = ["const C&"]
arg_types = ["C"]
setup = '''
@ -174,3 +176,14 @@ definitions = '''
{"name":"vec_b", "staticSize":24, "dynamicSize":12, "length":3, "capacity":3, "elementStaticSize":4},
{"name":"int_c", "staticSize":4, "dynamicSize":0}
]}]'''
expect_json_v2 = '''[{
"typeNames": ["C"],
"staticSize": 48,
"exclusiveSize": 8,
"members": [
{"name":"int_a", "staticSize":4, "exclusiveSize":4},
{"staticSize":8, "exclusiveSize":8},
{"name":"vec_b", "staticSize":24, "exclusiveSize":24, "length":3, "capacity":3},
{"name":"int_c", "staticSize":4, "exclusiveSize":4}
]
}]'''

View File

@ -12,6 +12,7 @@ definitions = '''
[cases]
[cases.a]
oil_disable = 'multi-argument probing has no meaning for oil'
param_types = ["int", "double"]
args = "arg0,arg1"
setup = "return {1,2.0};"

View File

@ -16,6 +16,7 @@ definitions = '''
'''
[cases]
[cases.queue]
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/308
param_types = ["const std::queue<std::pair<nsA::Foo, nsB::Foo>>&"]
setup = "return std::queue<std::pair<ns_namespaces::nsA::Foo, ns_namespaces::nsB::Foo>>({{ns_namespaces::nsA::Foo(), ns_namespaces::nsB::Foo()}});"
expect_json = '''[{
@ -23,6 +24,7 @@ definitions = '''
"staticSize": 80, "dynamicSize": 12, "length": 1, "capacity": 1, "elementStaticSize": 12
}]'''
[cases.stack]
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/305
param_types = ["const std::stack<std::pair<nsA::Foo, nsB::Foo>>&"]
setup = "return std::stack<std::pair<ns_namespaces::nsA::Foo, ns_namespaces::nsB::Foo>>({{ns_namespaces::nsA::Foo(), ns_namespaces::nsB::Foo()}});"
expect_json = '''[{

View File

@ -93,6 +93,7 @@ definitions = '''
]}]'''
[cases.parent_padding]
oid_skip = 'calculating incorrectly'
param_types = ["const PaddedChild&"]
setup = "return PaddedChild{};"
expect_json = '''[{
@ -100,3 +101,7 @@ definitions = '''
"dynamicSize": 0,
"paddingSavingsSize": 19
}]'''
expect_json_v2 = '''[{
"staticSize": 104,
"exclusiveSize": 32
}]'''

View File

@ -14,8 +14,7 @@ definitions = '''
[cases]
[cases.int]
oid_skip = "top-level pointers are skipped over" # https://github.com/facebookexperimental/object-introspection/issues/19
oil_disable = "oil can't chase raw pointers safely"
skip = "top-level pointers are skipped over" # https://github.com/facebookexperimental/object-introspection/issues/19
param_types = ["int*"]
setup = "return new int(1);"
cli_options = ["-fchase-raw-pointers"]
@ -33,7 +32,7 @@ definitions = '''
]
}]'''
[cases.int_no_follow]
oid_skip = "top-level pointers are skipped over" # https://github.com/facebookexperimental/object-introspection/issues/19
skip = "top-level pointers are skipped over" # https://github.com/facebookexperimental/object-introspection/issues/19
param_types = ["int*"]
setup = "return new int(1);"
expect_json = '''[{
@ -44,7 +43,7 @@ definitions = '''
"NOT": "members"
}]'''
[cases.int_null]
oid_skip = "top-level pointers are skipped over" # https://github.com/facebookexperimental/object-introspection/issues/19
skip = "top-level pointers are skipped over" # https://github.com/facebookexperimental/object-introspection/issues/19
param_types = ["int*"]
setup = "return nullptr;"
expect_json = '''[{
@ -57,8 +56,7 @@ definitions = '''
[cases.void]
oid_skip = "top-level pointers are skipped over" # https://github.com/facebookexperimental/object-introspection/issues/19
oil_disable = "oil can't chase raw pointers safely"
skip = "top-level pointers are skipped over" # https://github.com/facebookexperimental/object-introspection/issues/19
param_types = ["void*"]
setup = "return new int(1);"
cli_options = ["-fchase-raw-pointers"]
@ -70,7 +68,7 @@ definitions = '''
"NOT": "members"
}]'''
[cases.void_no_follow]
oid_skip = "top-level pointers are skipped over" # https://github.com/facebookexperimental/object-introspection/issues/19
skip = "top-level pointers are skipped over" # https://github.com/facebookexperimental/object-introspection/issues/19
param_types = ["void*"]
setup = "return new int(1);"
expect_json = '''[{
@ -81,7 +79,7 @@ definitions = '''
"NOT": "members"
}]'''
[cases.void_null]
oid_skip = "top-level pointers are skipped over" # https://github.com/facebookexperimental/object-introspection/issues/19
skip = "top-level pointers are skipped over" # https://github.com/facebookexperimental/object-introspection/issues/19
param_types = ["void*"]
setup = "return nullptr;"
expect_json = '''[{
@ -94,8 +92,7 @@ definitions = '''
[cases.vector]
oid_skip = "top-level pointers are skipped over" # https://github.com/facebookexperimental/object-introspection/issues/19
oil_disable = "oil can't chase raw pointers safely"
skip = "top-level pointers are skipped over" # https://github.com/facebookexperimental/object-introspection/issues/19
param_types = ["std::vector<int>*"]
setup = "return new std::vector<int>{1,2,3};"
cli_options = ["-fchase-raw-pointers"]
@ -113,7 +110,7 @@ definitions = '''
]
}]'''
[cases.vector_no_follow]
oid_skip = "top-level pointers are skipped over" # https://github.com/facebookexperimental/object-introspection/issues/19
skip = "top-level pointers are skipped over" # https://github.com/facebookexperimental/object-introspection/issues/19
param_types = ["std::vector<int>*"]
setup = "return new std::vector<int>{1,2,3};"
expect_json = '''[{
@ -124,7 +121,7 @@ definitions = '''
"NOT": "members"
}]'''
[cases.vector_null]
oid_skip = "BAD DATA SEGMENT!!! top-level pointers are skipped over" # https://github.com/facebookexperimental/object-introspection/issues/19
skip = "BAD DATA SEGMENT!!! top-level pointers are skipped over" # https://github.com/facebookexperimental/object-introspection/issues/19
param_types = ["std::vector<int>*"]
setup = "return nullptr;"
expect_json = '''[{
@ -224,7 +221,7 @@ definitions = '''
{"staticSize":8, "dynamicSize":4, "NOT": {"pointer":0}}
]}]'''
[cases.vector_of_pointers_no_follow]
oid_skip = "pointer field is missing from results" # https://github.com/facebookexperimental/object-introspection/issues/21
skip = "pointer field is missing from results" # https://github.com/facebookexperimental/object-introspection/issues/21
param_types = ["const std::vector<int*>&"]
setup = "return {{new int(1), nullptr, new int(3)}};"
expect_json = '''[{

View File

@ -14,7 +14,7 @@ definitions = '''
[cases]
[cases.raw]
oid_skip = "function pointers are not handled correctly" # https://github.com/facebookexperimental/object-introspection/issues/22
skip = "function pointers are not handled correctly" # https://github.com/facebookexperimental/object-introspection/issues/22
param_types = ["const FuncPtrStruct&"]
setup = "return {{myFunction}};"
expect_json = '''[{
@ -46,7 +46,7 @@ definitions = '''
}]
}]'''
[cases.raw_null]
oid_skip = "function pointers are not handled correctly" # https://github.com/facebookexperimental/object-introspection/issues/22
skip = "function pointers are not handled correctly" # https://github.com/facebookexperimental/object-introspection/issues/22
param_types = ["const FuncPtrStruct&"]
setup = "return {{nullptr}};"
expect_json = '''[{
@ -62,7 +62,7 @@ definitions = '''
}]'''
[cases.std_function]
oid_skip = "function pointers are not handled correctly" # https://github.com/facebookexperimental/object-introspection/issues/22
skip = "function pointers are not handled correctly" # https://github.com/facebookexperimental/object-introspection/issues/22
param_types = ["std::function<void(int)> &"]
setup = "return myFunction;"
expect_json = '''[{
@ -86,7 +86,7 @@ definitions = '''
"NOT": "members"
}]'''
[cases.std_function_null]
oid_skip = "function pointers are not handled correctly" # https://github.com/facebookexperimental/object-introspection/issues/22
skip = "function pointers are not handled correctly" # https://github.com/facebookexperimental/object-introspection/issues/22
param_types = ["std::function<void(int)> &"]
setup = "return nullptr;"
expect_json = '''[{

View File

@ -32,7 +32,7 @@ definitions = '''
"NOT": "members"
}]'''
[cases.raw_no_follow]
oid_skip = "oid codegen fails on this" # https://github.com/facebookexperimental/object-introspection/issues/17
skip = "oid codegen fails on this" # https://github.com/facebookexperimental/object-introspection/issues/17
param_types = ["IncompleteType*"]
setup = "return static_cast<IncompleteType*>(::operator new(5));"
expect_json = '''[{
@ -43,7 +43,7 @@ definitions = '''
"NOT": "members"
}]'''
[cases.raw_null]
oid_skip = "oid codegen fails on this" # https://github.com/facebookexperimental/object-introspection/issues/17
skip = "oid codegen fails on this" # https://github.com/facebookexperimental/object-introspection/issues/17
param_types = ["IncompleteType*"]
setup = "return nullptr;"
expect_json = '''[{
@ -55,6 +55,7 @@ definitions = '''
}]'''
[cases.unique_ptr]
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/299
param_types = ["const std::unique_ptr<IncompleteType, decltype(&incomplete_type_deleter)>&"]
setup = '''
auto raw_ptr = static_cast<IncompleteType*>(::operator new(5));
@ -63,6 +64,7 @@ definitions = '''
'''
expect_json = '[{"staticSize":16, "dynamicSize":0, "NOT":"members"}]'
[cases.unique_ptr_null]
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/299
param_types = ["const std::unique_ptr<IncompleteType, decltype(&incomplete_type_deleter)>&"]
setup = '''
return std::unique_ptr<IncompleteType, decltype(&incomplete_type_deleter)>(
@ -71,6 +73,7 @@ definitions = '''
expect_json = '[{"staticSize":16, "dynamicSize":0, "NOT":"members"}]'
[cases.shared_ptr]
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/300
param_types = ["const std::shared_ptr<IncompleteType>&"]
setup = '''
auto raw_ptr = static_cast<IncompleteType*>(::operator new(5));
@ -78,6 +81,7 @@ definitions = '''
'''
expect_json = '[{"staticSize":16, "dynamicSize":0, "NOT":"members"}]'
[cases.shared_ptr_null]
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/300
param_types = ["const std::shared_ptr<IncompleteType>"]
setup = "return nullptr;"
expect_json = '[{"staticSize":16, "dynamicSize":0, "NOT":"members"}]'

View File

@ -302,7 +302,7 @@ OidProc OidIntegration::runOidOnProcess(OidOpts opts,
};
}
void OidIntegration::compare_json(const bpt::ptree& expected_json,
void IntegrationBase::compare_json(const bpt::ptree& expected_json,
const bpt::ptree& actual_json,
const std::string& full_key,
bool expect_eq) {
@ -357,6 +357,21 @@ void OidIntegration::compare_json(const bpt::ptree& expected_json,
auto actual_it = actual_json.find(key);
auto curr_key = full_key + "." + key;
if (actual_it == actual_json.not_found()) {
// TODO: Remove these with the switch to treebuilderv2. This is a hack to
// make some old JSON output compatible with new JSON output.
if (key == "typeName") {
auto type_names = actual_json.find("typeNames");
if (type_names != actual_json.not_found()) {
if (auto name = type_names->second.rbegin();
name != type_names->second.rend()) {
compare_json(val, name->second, curr_key, expect_eq);
continue;
}
}
} else if (key == "dynamicSize" && val.get_value<size_t>() == 0) {
continue;
}
ADD_FAILURE() << "Expected key not found in output: " << curr_key;
continue;
}

View File

@ -49,16 +49,6 @@ class IntegrationBase : public ::testing::Test {
std::string stdout_;
std::string stderr_;
};
class OidIntegration : public IntegrationBase {
protected:
std::string TmpDirStr() override;
OidProc runOidOnProcess(OidOpts opts,
std::vector<std::string> extra_args,
std::string configPrefix,
std::string configSuffix);
/*
* compare_json
@ -72,6 +62,16 @@ class OidIntegration : public IntegrationBase {
bool expect_eq = true);
};
class OidIntegration : public IntegrationBase {
protected:
std::string TmpDirStr() override;
OidProc runOidOnProcess(OidOpts opts,
std::vector<std::string> extra_args,
std::string configPrefix,
std::string configSuffix);
};
class OilIntegration : public IntegrationBase {
protected:
std::string TmpDirStr() override;

View File

@ -44,6 +44,5 @@ definitions = '''
setup = "return {};"
expect_json = '''[{
"staticSize":8,
"dynamicSize":0,
"NOT":"members"
"dynamicSize":0
}]'''

View File

@ -5,6 +5,7 @@ definitions = '''
[cases]
[cases.no_ints]
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/320
param_types = ["const sorted_vector_set<int>&"]
setup = "return {};"
expect_json = '''[{
@ -22,6 +23,7 @@ definitions = '''
}]}]'''
[cases.some_ints]
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/320
param_types = ["const sorted_vector_set<int>&"]
setup = '''
sorted_vector_set<int> is;

View File

@ -1,6 +1,7 @@
includes = ["array", "cstdint", "vector"]
[cases]
[cases.uint64_length_0]
oil_skip = 'needs updating for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/318
param_types = ["std::array<std::uint64_t, 0>&"]
setup = "return {{}};"
expect_json = '''
@ -16,6 +17,7 @@ includes = ["array", "cstdint", "vector"]
]
'''
[cases.uint64_length_1]
oil_skip = 'needs updating for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/318
param_types = ["std::array<std::uint64_t, 1>&"]
setup = "return {{1}};"
expect_json = '''
@ -31,6 +33,7 @@ includes = ["array", "cstdint", "vector"]
]
'''
[cases.uint64_length_8]
oil_skip = 'needs updating for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/318
param_types = ["std::array<std::uint64_t, 8>&"]
setup = "return {{0,1,2,3,4,5,6,7}};"
expect_json = '''
@ -45,6 +48,7 @@ includes = ["array", "cstdint", "vector"]
]
'''
[cases.vector_length_1]
oil_skip = 'needs updating for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/318
param_types = ["std::array<std::vector<std::uint64_t>, 1>&"]
setup = "return {{std::initializer_list<std::uint64_t>({1,2,3,4,5})}};"
expect_json = '''
@ -65,6 +69,7 @@ includes = ["array", "cstdint", "vector"]
]
'''
[cases.vector_length_2]
oil_skip = 'needs updating for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/318
param_types = ["std::array<std::vector<std::uint64_t>, 2>&"]
setup = "return {{std::initializer_list<std::uint64_t>({1,2,3,4,5}), std::initializer_list<std::uint64_t>({6,7,8,9})}};"
expect_json = '''

View File

@ -16,6 +16,7 @@ public:
'''
[cases]
[cases.a]
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/317
param_types = ["const C&"]
setup = '''
C foo;

View File

@ -3,18 +3,22 @@
includes = ["deque"]
[cases]
[cases.int_empty]
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/316
param_types = ["const std::deque<int>&"]
setup = "return {};"
expect_json = '[{"staticSize":80, "dynamicSize":0, "length":0, "capacity":0, "elementStaticSize":4}]'
[cases.int_some]
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/316
param_types = ["const std::deque<int>&"]
setup = "return {{1,2,3}};"
expect_json = '[{"staticSize":80, "dynamicSize":12, "length":3, "capacity":3, "elementStaticSize":4}]'
[cases.deque_int_empty]
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/316
param_types = ["const std::deque<std::deque<int>>&"]
setup = "return {};"
expect_json = '[{"staticSize":80, "dynamicSize":0, "length":0, "capacity":0, "elementStaticSize":80}]'
[cases.deque_int_some]
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/316
param_types = ["const std::deque<std::deque<int>>&"]
setup = "return {{{1,2,3},{},{4,5}}};"
expect_json = '''[{

View File

@ -24,6 +24,7 @@ includes = ["deque"]
[cases]
[cases.a]
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/316
param_types = ["const Foo&"]
setup = '''
Foo foo;

View File

@ -27,6 +27,7 @@ includes = ["list"]
[cases]
[cases.a]
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/315
param_types = ["const Foo&"]
setup = '''
Foo foo;

View File

@ -36,6 +36,7 @@ includes = ["map", "functional"]
[cases]
[cases.a]
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/314
param_types = ["const Foo&"]
setup = '''
Foo foo;

View File

@ -14,6 +14,7 @@ includes = ["map"]
[cases]
[cases.a]
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/313
param_types = ["const Foo&"]
setup = '''
Foo foo;

View File

@ -1,6 +1,7 @@
includes = ["optional", "cstdint", "vector"]
[cases]
[cases.uint64_empty]
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/312
param_types = ["std::optional<std::uint64_t>&"]
setup = "return std::nullopt;"
expect_json = '''
@ -16,6 +17,7 @@ includes = ["optional", "cstdint", "vector"]
]
'''
[cases.uint64_present]
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/312
param_types = ["std::optional<std::uint64_t>&"]
setup = "return 64;"
expect_json = '''
@ -31,6 +33,7 @@ includes = ["optional", "cstdint", "vector"]
]
'''
[cases.vector_empty]
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/312
param_types = ["std::optional<std::vector<std::uint64_t>>&"]
setup = "return std::nullopt;"
expect_json = '''
@ -46,6 +49,7 @@ includes = ["optional", "cstdint", "vector"]
]
'''
[cases.vector_present]
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/312
param_types = ["std::optional<std::vector<std::uint64_t>>&"]
setup = "return {{{1,2,3,4,5}}};"
expect_json = '''

View File

@ -1,6 +1,7 @@
includes = ["vector", "utility", "cstdint"]
[cases]
[cases.uint64_uint64]
oil_skip = 'tests need updating for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/310
param_types = ["std::pair<std::uint64_t, std::uint64_t>&"]
setup = "return {{0, 1}};"
expect_json = '''
@ -14,6 +15,7 @@ includes = ["vector", "utility", "cstdint"]
]
'''
[cases.uint64_uint32]
oil_skip = 'tests need updating for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/310
param_types = ["std::pair<std::uint64_t, std::uint32_t>&"]
setup = "return {{0, 1}};"
# Should still have static size of 16 due to padding
@ -28,6 +30,7 @@ includes = ["vector", "utility", "cstdint"]
]
'''
[cases.vector_vector]
oil_skip = 'tests need updating for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/310
param_types = ["std::pair<std::vector<std::uint64_t>, std::vector<std::uint64_t>>&"]
setup = "return {{std::initializer_list<std::uint64_t>({0,1,2}), std::initializer_list<std::uint64_t>({3,4,5,6})}};"
expect_json = '''

View File

@ -1,6 +1,7 @@
includes = ["queue"]
[cases]
[cases.int_empty]
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/309
param_types = ["const std::priority_queue<int>&"]
setup = "return {};"
expect_json = '''[{
@ -13,6 +14,7 @@ includes = ["queue"]
}
]}]'''
[cases.int_some]
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/309
param_types = ["const std::priority_queue<int>&"]
setup = "return std::priority_queue<int>({}, {3,2,1});"
expect_json = '''[{
@ -25,6 +27,7 @@ includes = ["queue"]
}
]}]'''
[cases.adapter_deque_empty]
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/309
param_types = ["const std::priority_queue<int, std::deque<int>>&"]
setup = "return {};"
expect_json = '''[{
@ -37,6 +40,7 @@ includes = ["queue"]
}
]}]'''
[cases.adapter_deque_some]
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/309
param_types = ["const std::priority_queue<int, std::deque<int>>&"]
setup = "return std::priority_queue<int, std::deque<int>>({}, {3,2,1});"
expect_json = '''[{

View File

@ -1,6 +1,7 @@
includes = ["queue"]
[cases]
[cases.int_empty]
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/308
param_types = ["const std::queue<int>&"]
setup = "return {};"
expect_json = '''[{
@ -13,6 +14,7 @@ includes = ["queue"]
}
]}]'''
[cases.int_some]
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/308
param_types = ["const std::queue<int>&"]
setup = "return std::queue<int>({1,2,3});"
expect_json = '''[{
@ -25,6 +27,7 @@ includes = ["queue"]
}
]}]'''
[cases.queue_int_empty]
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/308
param_types = ["const std::queue<std::queue<int>>&"]
setup = "return {};"
expect_json = '''[{
@ -35,6 +38,7 @@ includes = ["queue"]
}
]}]'''
[cases.queue_int_some]
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/308
param_types = ["const std::queue<std::queue<int>>&"]
setup = '''
return std::queue<std::queue<int>>({
@ -83,6 +87,7 @@ includes = ["queue"]
}
]}]'''
[cases.adapter_vector_empty]
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/308
param_types = ["const std::queue<int, std::vector<int>>&"]
setup = "return {};"
expect_json = '''[{
@ -95,6 +100,7 @@ includes = ["queue"]
}
]}]'''
[cases.adapter_vector_some]
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/308
param_types = ["const std::queue<int, std::vector<int>>&"]
setup = "return std::queue<int, std::vector<int>>({1,2,3});"
expect_json = '''[{

View File

@ -1,10 +1,12 @@
includes = ["functional"]
[cases]
[cases.int]
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/307
param_types = ["const std::reference_wrapper<int>&"]
setup = "return std::ref(*new int(1));"
expect_json = '[{"staticSize":8, "dynamicSize":4, "length":1, "capacity":1, "elementStaticSize":4}]'
[cases.vector]
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/307
param_types = ["const std::vector<std::reference_wrapper<int>>&"]
setup = "return {{std::ref(*new int(1)), std::ref(*new int(2)), std::ref(*new int(3))}};"
expect_json = '''[{

View File

@ -31,6 +31,7 @@ includes = ["set", "functional"]
[cases]
[cases.a]
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/306
param_types = ["const Foo&"]
setup = '''
Foo foo;

View File

@ -7,6 +7,7 @@ definitions = '''
'''
[cases]
[cases.unique_ptr_uint64_empty]
oil_skip = "std::unique_ptr is not implemented for treebuilder v2" # https://github.com/facebookexperimental/object-introspection/issues/299
param_types = ["std::unique_ptr<std::uint64_t>&"]
setup = "return {nullptr};"
expect_json = '''
@ -22,6 +23,7 @@ definitions = '''
]
'''
[cases.unique_ptr_uint64_present]
oil_skip = "std::unique_ptr is not implemented for treebuilder v2" # https://github.com/facebookexperimental/object-introspection/issues/299
param_types = ["std::unique_ptr<std::uint64_t>&"]
setup = "return {std::make_unique<std::uint64_t>(64)};"
expect_json = '''
@ -36,6 +38,7 @@ definitions = '''
]
'''
[cases.unique_ptr_vector_empty]
oil_skip = "std::unique_ptr is not implemented for treebuilder v2" # https://github.com/facebookexperimental/object-introspection/issues/299
param_types = ["std::unique_ptr<std::vector<std::uint64_t>>&"]
setup = "return {nullptr};"
expect_json = '''
@ -50,6 +53,7 @@ definitions = '''
]
'''
[cases.unique_ptr_vector_present]
oil_skip = "std::unique_ptr is not implemented for treebuilder v2" # https://github.com/facebookexperimental/object-introspection/issues/299
param_types = ["std::unique_ptr<std::vector<std::uint64_t>>&"]
setup = "return {std::make_unique<std::vector<std::uint64_t>>(std::initializer_list<std::uint64_t>({1,2,3,4,5}))};"
expect_json = '''
@ -72,6 +76,7 @@ definitions = '''
]
'''
[cases.unique_ptr_void_empty]
oil_skip = "std::unique_ptr is not implemented for treebuilder v2" # https://github.com/facebookexperimental/object-introspection/issues/299
param_types = ["std::unique_ptr<void, decltype(&void_int_deleter)>&"]
setup = "return {std::unique_ptr<void, decltype(&void_int_deleter)>(nullptr, &void_int_deleter)};"
expect_json = '''
@ -83,6 +88,7 @@ definitions = '''
]
'''
[cases.unique_ptr_void_present]
oil_skip = "std::unique_ptr is not implemented for treebuilder v2" # https://github.com/facebookexperimental/object-introspection/issues/299
param_types = ["std::unique_ptr<void, decltype(&void_int_deleter)>&"]
setup = "return {std::unique_ptr<void, decltype(&void_int_deleter)>(new int, &void_int_deleter)};"
expect_json = '''
@ -94,6 +100,7 @@ definitions = '''
]
'''
[cases.shared_ptr_uint64_empty]
oil_skip = "std::shared_ptr is not implemented for treebuilder v2" # https://github.com/facebookexperimental/object-introspection/issues/300
param_types = ["std::shared_ptr<std::uint64_t>&"]
setup = "return {nullptr};"
expect_json = '''
@ -108,6 +115,7 @@ definitions = '''
]
'''
[cases.shared_ptr_uint64_present]
oil_skip = "std::shared_ptr is not implemented for treebuilder v2" # https://github.com/facebookexperimental/object-introspection/issues/300
param_types = ["std::shared_ptr<std::uint64_t>&"]
setup = "return std::make_shared<std::uint64_t>(64);"
expect_json = '''
@ -122,6 +130,7 @@ definitions = '''
]
'''
[cases.shared_ptr_vector_empty]
oil_skip = "std::shared_ptr is not implemented for treebuilder v2" # https://github.com/facebookexperimental/object-introspection/issues/300
param_types = ["std::shared_ptr<std::vector<std::uint64_t>>&"]
setup = "return {nullptr};"
expect_json = '''
@ -136,6 +145,7 @@ definitions = '''
]
'''
[cases.shared_ptr_vector_present]
oil_skip = "std::shared_ptr is not implemented for treebuilder v2" # https://github.com/facebookexperimental/object-introspection/issues/300
param_types = ["std::shared_ptr<std::vector<std::uint64_t>>&"]
setup = "return std::make_shared<std::vector<std::uint64_t>>(std::initializer_list<std::uint64_t>({1,2,3,4,5}));"
expect_json = '''
@ -156,6 +166,7 @@ definitions = '''
]
'''
[cases.shared_ptr_void_empty]
oil_skip = "std::shared_ptr is not implemented for treebuilder v2" # https://github.com/facebookexperimental/object-introspection/issues/300
param_types = ["std::shared_ptr<void>&"]
setup = "return {nullptr};"
expect_json = '''
@ -168,6 +179,7 @@ definitions = '''
]
'''
[cases.shared_ptr_void_present]
oil_skip = "std::shared_ptr is not implemented for treebuilder v2" # https://github.com/facebookexperimental/object-introspection/issues/300
param_types = ["std::shared_ptr<void>&"]
setup = "return {std::shared_ptr<void>(new int)};"
expect_json = '''
@ -192,6 +204,7 @@ definitions = '''
}
]
'''
expect_json_v2 = '''[{ "staticSize": 16, "exclusiveSize": 16, "members":[]}]'''
[cases.weak_ptr_int64_void_empty]
param_types = ["std::weak_ptr<void>&"]
setup = "return std::weak_ptr<void>();"
@ -205,6 +218,7 @@ definitions = '''
}
]
'''
expect_json_v2 = '''[{ "staticSize": 16, "exclusiveSize": 16, "members":[]}]'''
[cases.weak_ptr_int64_present]
param_types = ["std::weak_ptr<std::uint64_t>&"]
setup = '''
@ -222,6 +236,7 @@ definitions = '''
}
]
'''
expect_json_v2 = '''[{ "staticSize": 16, "exclusiveSize": 16, "members":[]}]'''
[cases.weak_ptr_int64_expired]
param_types = ["std::weak_ptr<std::uint64_t>&"]
setup = '''
@ -239,6 +254,7 @@ definitions = '''
}
]
'''
expect_json_v2 = '''[{ "staticSize": 16, "exclusiveSize": 16, "members":[]}]'''
[cases.weak_ptr_int64_present_chase]
param_types = ["std::weak_ptr<std::uint64_t>&"]
cli_options = ["-fchase-raw-pointers"]
@ -257,6 +273,7 @@ definitions = '''
}
]
'''
expect_json_v2 = '''[{ "staticSize": 16, "exclusiveSize": 16, "members":[]}]'''
[cases.weak_ptr_int64_expired_chase]
param_types = ["std::weak_ptr<std::uint64_t>&"]
cli_options = ["-fchase-raw-pointers"]
@ -273,3 +290,4 @@ definitions = '''
}
]
'''
expect_json_v2 = '''[{ "staticSize": 16, "exclusiveSize": 16, "members":[]}]'''

View File

@ -1,6 +1,7 @@
includes = ["stack", "vector"]
[cases]
[cases.int_empty]
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/305
param_types = ["const std::stack<int>&"]
setup = "return {};"
expect_json = '''[{
@ -13,6 +14,7 @@ includes = ["stack", "vector"]
}
]}]'''
[cases.int_some]
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/305
param_types = ["const std::stack<int>&"]
setup = "return std::stack<int>({1,2,3});"
expect_json = '''[{
@ -25,6 +27,7 @@ includes = ["stack", "vector"]
}
]}]'''
[cases.stack_int_empty]
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/305
param_types = ["const std::stack<std::stack<int>>&"]
setup = "return {};"
expect_json = '''[{
@ -35,6 +38,7 @@ includes = ["stack", "vector"]
}
]}]'''
[cases.stack_int_some]
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/305
param_types = ["const std::stack<std::stack<int>>&"]
setup = '''
return std::stack<std::stack<int>>({
@ -83,6 +87,7 @@ includes = ["stack", "vector"]
}
]}]'''
[cases.adapter_vector_empty]
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/305
param_types = ["const std::stack<int, std::vector<int>>&"]
setup = "return {};"
expect_json = '''[{
@ -95,6 +100,7 @@ includes = ["stack", "vector"]
}
]}]'''
[cases.adapter_vector_some]
oil_skip = 'not implemented for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/305
param_types = ["const std::stack<int, std::vector<int>>&"]
setup = "return std::stack<int, std::vector<int>>({1,2,3});"
expect_json = '''[{

View File

@ -1,6 +1,7 @@
includes = ["string"]
[cases]
[cases.empty]
oil_skip = 'needs updating for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/311
param_types = ["std::string&"]
setup = "return {};"
expect_json = '''
@ -25,6 +26,7 @@ includes = ["string"]
]
'''
[cases.sso]
oil_skip = 'needs updating for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/311
param_types = ["std::string&"]
setup = 'return {"012345"};'
expect_json = '''
@ -49,6 +51,7 @@ includes = ["string"]
]
'''
[cases.heap_allocated]
oil_skip = 'needs updating for treebuilder v2' # https://github.com/facebookexperimental/object-introspection/issues/311
param_types = ["std::string&"]
setup = 'return {"abcdefghijklmnopqrstuvwxzy"};'
expect_json = '''

View File

@ -9,6 +9,7 @@ struct Bar {
'''
[cases]
[cases.uint64_uint64]
oil_skip = "std::tuple is not implemented for treebuilder v2" # https://github.com/facebookexperimental/object-introspection/issues/304
param_types = ["Bar&"]
setup = '''
Foo f;

View File

@ -33,6 +33,7 @@ includes = ["unordered_map"]
[cases]
[cases.a]
oil_skip = "std::unordered_map is not implemented for treebuilder v2" # https://github.com/facebookexperimental/object-introspection/issues/303
param_types = ["const Foo&"]
setup = '''
Foo foo;

View File

@ -33,6 +33,7 @@ includes = ["unordered_set"]
[cases]
[cases.a]
oil_skip = "std::unordered_set is not implemented for treebuilder v2" # https://github.com/facebookexperimental/object-introspection/issues/302
param_types = ["const Foo&"]
setup = '''
Foo foo;

View File

@ -9,6 +9,7 @@ definitions = '''
[cases]
[cases.char_int64_1]
oil_skip = "std::variant is not implemented for treebuilder v2" # https://github.com/facebookexperimental/object-introspection/issues/298
param_types = ["const std::variant<char, int64_t>&"]
setup = "return 'a';"
expect_json = '''[{
@ -22,6 +23,7 @@ definitions = '''
{"typeName":"char", "staticSize":1, "exclusiveSize":1, "dynamicSize":0}
]}]'''
[cases.char_int64_2]
oil_skip = "std::variant is not implemented for treebuilder v2" # https://github.com/facebookexperimental/object-introspection/issues/298
param_types = ["const std::variant<char, int64_t>&"]
setup = "return 1234;"
expect_json = '''[{
@ -36,6 +38,7 @@ definitions = '''
]}]'''
[cases.vector_int_1]
oil_skip = "std::variant is not implemented for treebuilder v2" # https://github.com/facebookexperimental/object-introspection/issues/298
param_types = ["const std::variant<std::vector<int>, int>&"]
setup = "return std::vector<int>{1,2,3};"
expect_json = '''[{
@ -57,6 +60,7 @@ definitions = '''
}
]}]'''
[cases.vector_int_2]
oil_skip = "std::variant is not implemented for treebuilder v2" # https://github.com/facebookexperimental/object-introspection/issues/298
param_types = ["const std::variant<std::vector<int>, int>&"]
setup = "return 123;"
expect_json = '''[{
@ -74,6 +78,7 @@ definitions = '''
]}]'''
[cases.optional]
oil_skip = "std::variant is not implemented for treebuilder v2" # https://github.com/facebookexperimental/object-introspection/issues/298
# This test case ensures that the alignment of std::variant is set
# correctly, as otherwise the size of the std::optional would be wrong
param_types = ["const std::optional<std::variant<char, int64_t>>&"]
@ -100,6 +105,7 @@ definitions = '''
]}]'''
[cases.empty]
oil_skip = "std::variant is not implemented for treebuilder v2" # https://github.com/facebookexperimental/object-introspection/issues/298
# https://en.cppreference.com/w/cpp/utility/variant/valueless_by_exception
param_types = ["const std::variant<int, Thrower>&"]
setup = '''
@ -127,6 +133,7 @@ definitions = '''
# 0xff can be a valid index if there are at least 256 parameters, and that
# the invalid index value is raised to 0xffff.
[cases.256_params_256]
oil_skip = "std::variant is not implemented for treebuilder v2" # https://github.com/facebookexperimental/object-introspection/issues/298
param_types = ["const std::variant<Thrower,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,char>&"]
setup = "return 'a';"
expect_json = '''[{
@ -140,6 +147,7 @@ definitions = '''
{"typeName":"char", "staticSize":1, "exclusiveSize":1, "dynamicSize":0}
]}]'''
[cases.256_params_empty]
oil_skip = "std::variant is not implemented for treebuilder v2" # https://github.com/facebookexperimental/object-introspection/issues/298
param_types = ["const std::variant<Thrower,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,char>&"]
setup = '''
std::variant<Thrower,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,char> var{'a'};

View File

@ -4,10 +4,16 @@ includes = ["vector"]
param_types = ["const std::vector<int>&"]
setup = "return {};"
expect_json = '[{"staticSize":24, "dynamicSize":0, "length":0, "capacity":0, "elementStaticSize":4}]'
expect_json_v2 = '[{"staticSize":24, "exclusiveSize":24, "length":0, "capacity":0, "members":[]}]'
[cases.int_some]
param_types = ["const std::vector<int>&"]
setup = "return {{1,2,3}};"
expect_json = '[{"staticSize":24, "dynamicSize":12, "length":3, "capacity":3, "elementStaticSize":4}]'
expect_json_v2 = '''[{"staticSize":24, "exclusiveSize":24, "length":3, "capacity":3, "members":[
{"staticSize":4, "exclusiveSize":4},
{"staticSize":4, "exclusiveSize":4},
{"staticSize":4, "exclusiveSize":4}
]}]'''
[cases.bool_empty]
skip = true # https://github.com/facebookexperimental/object-introspection/issues/14
param_types = ["const std::vector<bool>&"]
@ -22,6 +28,7 @@ includes = ["vector"]
param_types = ["const std::vector<std::vector<int>>&"]
setup = "return {};"
expect_json = '[{"staticSize":24, "dynamicSize":0, "length":0, "capacity":0, "elementStaticSize":24}]'
expect_json_v2 = '[{"staticSize":24, "exclusiveSize":24, "length":0, "capacity":0, "members":[]}]'
[cases.vector_int_some]
param_types = ["const std::vector<std::vector<int>>&"]
setup = "return {{{1,2,3},{4},{5,6}}};"
@ -37,6 +44,11 @@ includes = ["vector"]
{"staticSize":24, "dynamicSize":4, "exclusiveSize":28, "length":1, "capacity":1, "elementStaticSize":4},
{"staticSize":24, "dynamicSize":8, "exclusiveSize":32, "length":2, "capacity":2, "elementStaticSize":4}
]}]'''
expect_json_v2 = '''[{"staticSize":24, "exclusiveSize":24, "length":3, "capacity": 3, "members":[
{"staticSize":24, "exclusiveSize":24, "length":3, "capacity": 3, "members":[]},
{"staticSize":24, "exclusiveSize":24, "length":1, "capacity": 1, "members":[]},
{"staticSize":24, "exclusiveSize":24, "length":2, "capacity": 2, "members":[]}
]}]'''
[cases.reserve]
param_types = ["const std::vector<int>&"]
setup = '''
@ -45,3 +57,8 @@ includes = ["vector"]
return ret;
'''
expect_json = '[{"staticSize":24, "dynamicSize":40, "exclusiveSize":64, "length":3, "capacity":10, "elementStaticSize":4}]'
expect_json_v2 = '''[{"staticSize":24, "exclusiveSize":52, "length":3, "capacity":10, "members":[
{"staticSize":4, "exclusiveSize":4},
{"staticSize":4, "exclusiveSize":4},
{"staticSize":4, "exclusiveSize":4}
]}]'''

View File

@ -27,6 +27,7 @@ includes = ["vector"]
[cases]
[cases.a]
oil_skip = "oil gets the exclusive size of vector subfields wrong" # https://github.com/facebookexperimental/object-introspection/issues/301
param_types = ["const Foo&"]
setup = '''
Foo foo;
@ -57,3 +58,22 @@ includes = ["vector"]
]}
]}
]}]'''
expect_json_v2 = '''[{
"staticSize":48,
"exclusiveSize":0,
"members":[
{"name":"v1", "staticSize":24, "exclusiveSize":24, "length":1, "capacity":1, "members":[
{"name":"[]", "staticSize":4, "exclusiveSize":0, "members":[
{"name":"a", "staticSize":4, "exclusiveSize":4, "members":[]}
]}
]},
{"name":"v2", "staticSize":24, "exclusiveSize":24, "length":2, "capacity":2, "members":[
{"name":"[]", "staticSize":4, "exclusiveSize":0, "members":[
{"name":"b", "staticSize":4, "exclusiveSize":4, "members":[]}
]},
{"name":"[]", "staticSize":4, "exclusiveSize":0, "members":[
{"name":"b", "staticSize":4, "exclusiveSize":4, "members":[]}
]}
]}
]
}]'''

View File

@ -24,6 +24,7 @@ definitions = '''
[cases]
[cases.int]
oil_skip = "primitives are named differently in treebuilderv2" # https://github.com/facebookexperimental/object-introspection/issues/286
param_types = ["const TemplatedClass1<int>&"]
setup = "return {};"
expect_json = '''[{
@ -48,7 +49,19 @@ definitions = '''
"capacity":0,
"elementStaticSize":4
}]}]'''
expect_json_v2 = '''[{
"typeName":"TemplatedClass1<std::vector<int, std::allocator<int> > >",
"staticSize":24,
"exclusiveSize":0,
"members":[{
"typeName":"std::vector<int32_t, std::allocator<int32_t>>",
"staticSize":24,
"exclusiveSize":24,
"length":0,
"capacity":0
}]}]'''
[cases.two]
oil_skip = "OIL returns better primitive names"
param_types = ["const TemplatedClass2<Foo, int>&"]
setup = "return {};"
expect_json = '''[{
@ -73,3 +86,13 @@ definitions = '''
"capacity":3,
"elementStaticSize":4
}]}]'''
expect_json_v2 = '''[{
"typeName":"TemplatedClassVal<3>",
"staticSize":12,
"exclusiveSize":0,
"members":[{
"staticSize":12,
"exclusiveSize":0,
"length":3,
"capacity":3
}]}]'''

View File

@ -77,6 +77,7 @@ namespace cpp2 {
[cases]
[cases.unpacked]
oil_skip = 'oil does not support thrift isset yet' # https://github.com/facebookexperimental/object-introspection/issues/296
param_types = ["const cpp2::MyThriftStructUnpacked&"]
setup = '''
cpp2::MyThriftStructUnpacked ret;
@ -96,6 +97,7 @@ namespace cpp2 {
]}]'''
[cases.packed]
oil_skip = 'oil does not support thrift isset yet' # https://github.com/facebookexperimental/object-introspection/issues/296
param_types = ["const cpp2::MyThriftStructPacked&"]
setup = '''
cpp2::MyThriftStructPacked ret;
@ -126,6 +128,7 @@ namespace cpp2 {
]}]'''
[cases.packed_non_atomic]
oil_skip = 'oil does not support thrift isset yet' # https://github.com/facebookexperimental/object-introspection/issues/296
param_types = ["const cpp2::MyThriftStructPackedNonAtomic&"]
setup = '''
cpp2::MyThriftStructPackedNonAtomic ret;
@ -146,6 +149,7 @@ namespace cpp2 {
]}]'''
[cases.out_of_order]
oil_skip = 'oil does not support thrift isset yet' # https://github.com/facebookexperimental/object-introspection/issues/296
param_types = ["const cpp2::MyThriftStructOutOfOrder&"]
setup = '''
cpp2::MyThriftStructOutOfOrder ret;
@ -164,6 +168,7 @@ namespace cpp2 {
]}]'''
[cases.required]
oil_skip = 'oil does not support thrift isset yet' # https://github.com/facebookexperimental/object-introspection/issues/296
param_types = ["const cpp2::MyThriftStructRequired&"]
setup = '''
cpp2::MyThriftStructRequired ret;
@ -186,6 +191,7 @@ namespace cpp2 {
]}]'''
[cases.box]
oil_skip = 'oil does not support thrift isset yet' # https://github.com/facebookexperimental/object-introspection/issues/296
param_types = ["const cpp2::MyThriftStructBoxed&"]
setup = '''
cpp2::MyThriftStructBoxed ret;
@ -207,6 +213,7 @@ namespace cpp2 {
]}]'''
[cases.no_capture]
oil_skip = 'oil does not support thrift isset yet' # https://github.com/facebookexperimental/object-introspection/issues/296
param_types = ["const cpp2::MyThriftStructBoxed&"]
setup = '''
cpp2::MyThriftStructBoxed ret;

View File

@ -58,6 +58,7 @@ raw_definitions = '''
'''
[cases]
[cases.present]
oil_skip = 'oil does not support thrift isset yet' # https://github.com/facebookexperimental/object-introspection/issues/296
param_types = ["const FakeThriftWithData&"]
setup = '''
FakeThriftWithData ret;
@ -78,6 +79,7 @@ raw_definitions = '''
{"name":"__isset", "staticSize":3}
]}]'''
[cases.missing]
oil_skip = 'oil does not support thrift isset yet' # https://github.com/facebookexperimental/object-introspection/issues/296
param_types = ["const FakeThriftWithoutData&"]
setup = '''
FakeThriftWithoutData ret;
@ -98,6 +100,7 @@ raw_definitions = '''
{"name":"__isset", "staticSize":3}
]}]'''
[cases.mixed]
oil_skip = 'oil does not support thrift isset yet' # https://github.com/facebookexperimental/object-introspection/issues/296
param_types = ["const Mixed&"]
setup = '''
Mixed ret;

View File

@ -10,6 +10,7 @@ thrift_definitions = '''
[cases]
[cases.a]
oil_skip = 'enum type template arguments do not match' # https://github.com/facebookexperimental/object-introspection/issues/297
param_types = ["const namespaceA::namespaceB::TTTTT&"]
setup = '''
namespaceA::namespaceB::TTTTT ret;

View File

@ -33,6 +33,13 @@ namespace cpp2 {
{"typeName":"storage_type", "name":"value_", "staticSize":4},
{"typeName":"underlying_type_t<cpp2::StaticUnion::Type>", "name":"type_", "staticSize":4}
]}]'''
expect_json_v2 = '''[{
"staticSize":8,
"exclusiveSize":0,
"members":[
{"typeNames":["storage_type"], "name":"value_", "staticSize":4, "exclusiveSize":4},
{"typeNames":["underlying_type_t<cpp2::StaticUnion::Type>", "type", "int32_t"], "name":"type_", "staticSize":4, "exclusiveSize":4}
]}]'''
[cases.dynamic_int]
param_types = ["const cpp2::DynamicUnion&"]
setup = '''
@ -47,6 +54,13 @@ namespace cpp2 {
{"typeName":"storage_type", "name":"value_", "staticSize":24},
{"typeName":"underlying_type_t<cpp2::DynamicUnion::Type>", "name":"type_", "staticSize":4}
]}]'''
expect_json_v2 = '''[{
"staticSize":32,
"exclusiveSize":4,
"members":[
{"typeNames":["storage_type"], "name":"value_", "staticSize":24, "exclusiveSize":24},
{"typeNames":["underlying_type_t<cpp2::DynamicUnion::Type>", "type", "int32_t"], "name":"type_", "staticSize":4, "exclusiveSize":4}
]}]'''
[cases.dynamic_vec]
param_types = ["const cpp2::DynamicUnion&"]
setup = '''
@ -61,3 +75,10 @@ namespace cpp2 {
{"typeName":"storage_type", "name":"value_", "staticSize":24},
{"typeName":"underlying_type_t<cpp2::DynamicUnion::Type>", "name":"type_", "staticSize":4}
]}]'''
expect_json_v2 = '''[{
"staticSize":32,
"exclusiveSize":4,
"members":[
{"typeNames":["storage_type"], "name":"value_", "staticSize":24, "exclusiveSize":24},
{"typeNames":["underlying_type_t<cpp2::DynamicUnion::Type>", "type", "int32_t"], "name":"type_", "staticSize":4, "exclusiveSize":4}
]}]'''

View File

@ -27,6 +27,13 @@ definitions = '''
{"name":"a", "staticSize":24, "dynamicSize":12, "length":3, "capacity":3},
{"name":"b", "staticSize":24, "dynamicSize":20, "length":5, "capacity":5}
]}]'''
expect_json_v2 = '''[{
"staticSize":48,
"exclusiveSize":0,
"members":[
{"name":"a", "staticSize":24, "exclusiveSize":24, "length":3, "capacity":3},
{"name":"b", "staticSize":24, "exclusiveSize":24, "length":5, "capacity":5}
]}]'''
[cases.multilevel_typedef_parent]
param_types = ["const Bar_2&"]
setup = '''
@ -40,3 +47,10 @@ definitions = '''
{"name":"a", "staticSize":24, "dynamicSize":12, "length":3, "capacity":3},
{"name":"c", "staticSize":24, "dynamicSize":20, "length":5, "capacity":5}
]}]'''
expect_json_v2 = '''[{
"staticSize":48,
"exclusiveSize":0,
"members":[
{"name":"a", "staticSize":24, "exclusiveSize":24, "length":3, "capacity":3},
{"name":"c", "staticSize":24, "exclusiveSize":24, "length":5, "capacity":5}
]}]'''

View File

@ -4,9 +4,15 @@ definitions = '''
using UsingUInt64 = uint64_t;
using IntVector = std::vector<int>;
using Anonymous = struct { int n; };
template <typename T>
struct Proxy {
T t;
};
'''
[cases]
[cases.c_style]
oil_disable = "oil can't pick up top level typedefs"
param_types = ["TdUInt64"]
setup = "return {};"
expect_json = '''[{
@ -24,6 +30,7 @@ definitions = '''
}
]}]'''
[cases.using]
oil_disable = "oil can't pick up top level typedefs"
param_types = ["UsingUInt64"]
setup = "return {};"
expect_json = '''[{
@ -41,6 +48,7 @@ definitions = '''
}
]}]'''
[cases.container]
oil_disable = "oil can't pick up top level typedefs"
param_types = ["const IntVector&"]
setup = "return {};"
expect_json = '''[{
@ -61,7 +69,7 @@ definitions = '''
}
]}]'''
[cases.anonymous]
oil_skip = "TreeBuilder crashes" # https://github.com/facebookexperimental/object-introspection/issues/232
oil_disable = "oil can't pick up top level typedefs"
param_types = ["const Anonymous&"]
setup = "return {};"
expect_json = '''[{
@ -86,3 +94,4 @@ definitions = '''
}
]}
]}]'''

View File

@ -45,14 +45,17 @@ definitions = '''
param_types = ["const MyUnion&"]
setup = "return 123;"
expect_json = '[{"staticSize":56, "dynamicSize":0, "exclusiveSize":56, "NOT":"members"}]'
expect_json_v2 = '[{"staticSize":56, "dynamicSize":0, "exclusiveSize":56, "members":[]}]'
[cases.vector]
param_types = ["const MyUnion&"]
setup = "return std::vector{1,2,3};"
expect_json = '[{"staticSize":56, "dynamicSize":0, "exclusiveSize":56, "NOT":"members"}]'
expect_json_v2 = '[{"staticSize":56, "dynamicSize":0, "exclusiveSize":56, "members":[]}]'
[cases.unordered_map]
param_types = ["const MyUnion&"]
setup = 'return std::unordered_map<std::string, std::string>{{"a", "b"}, {"c","d"}};'
expect_json = '[{"staticSize":56, "dynamicSize":0, "exclusiveSize":56, "NOT":"members"}]'
expect_json_v2 = '[{"staticSize":56, "dynamicSize":0, "exclusiveSize":56, "members":[]}]'
[cases.alignment]
# Wrap the union in a pair as a way of inferring its alignment
@ -63,6 +66,11 @@ definitions = '''
{"staticSize":1, "dynamicSize":0, "exclusiveSize":1},
{"staticSize":56, "dynamicSize":0, "exclusiveSize":56, "NOT":"members"}
]}]'''
expect_json_v2 = '''[
{"staticSize":64, "dynamicSize":0, "exclusiveSize":7, "members":[
{"staticSize":1, "dynamicSize":0, "exclusiveSize":1},
{"staticSize":56, "dynamicSize":0, "exclusiveSize":56, "members":[]}
]}]'''
[cases.tagged_int]
param_types = ["const TaggedUnion&"]
@ -72,6 +80,11 @@ definitions = '''
{"name":"storage", "staticSize":56, "dynamicSize":0, "exclusiveSize":56, "NOT":"members"},
{"name":"tag", "staticSize":1, "dynamicSize":0, "exclusiveSize":1, "NOT":"members"}
]}]'''
expect_json_v2 = '''[
{"staticSize":64, "dynamicSize":0, "exclusiveSize":7, "members":[
{"name":"storage", "staticSize":56, "dynamicSize":0, "exclusiveSize":56, "members":[]},
{"name":"tag", "staticSize":1, "dynamicSize":0, "exclusiveSize":1, "members":[]}
]}]'''
[cases.tagged_vector]
param_types = ["const TaggedUnion&"]
setup = "return TaggedUnion{MyUnion{std::vector{1,2,3}}, 1};"
@ -80,6 +93,11 @@ definitions = '''
{"name":"storage", "staticSize":56, "dynamicSize":0, "exclusiveSize":56, "NOT":"members"},
{"name":"tag", "staticSize":1, "dynamicSize":0, "exclusiveSize":1, "NOT":"members"}
]}]'''
expect_json_v2 = '''[
{"staticSize":64, "dynamicSize":0, "exclusiveSize":7, "members":[
{"name":"storage", "staticSize":56, "dynamicSize":0, "exclusiveSize":56, "members":[]},
{"name":"tag", "staticSize":1, "dynamicSize":0, "exclusiveSize":1, "members":[]}
]}]'''
[cases.tagged_unordered_map]
param_types = ["const TaggedUnion&"]
setup = 'return TaggedUnion{MyUnion{std::unordered_map<std::string, std::string>{{"a", "b"}, {"c","d"}}}, 2};'
@ -88,3 +106,8 @@ definitions = '''
{"name":"storage", "staticSize":56, "dynamicSize":0, "exclusiveSize":56, "NOT":"members"},
{"name":"tag", "staticSize":1, "dynamicSize":0, "exclusiveSize":1, "NOT":"members"}
]}]'''
expect_json_v2 = '''[
{"staticSize":64, "dynamicSize":0, "exclusiveSize":7, "members":[
{"name":"storage", "staticSize":56, "dynamicSize":0, "exclusiveSize":56, "members":[]},
{"name":"tag", "staticSize":1, "dynamicSize":0, "exclusiveSize":1, "members":[]}
]}]'''

View File

@ -50,14 +50,16 @@ TEST_F(AddChildrenTest, SimpleStruct) {
TEST_F(AddChildrenTest, InheritanceStatic) {
// Should do nothing
test("oid_test_case_inheritance_access_public", R"(
[2] Pointer
[4] Pointer
[0] Class: Public (size: 8)
Parent (offset: 0)
[1] Class: Base (size: 4)
Member: base_int (offset: 0)
[3] Typedef: int32_t
[2] Typedef: __int32_t
Primitive: int32_t
Member: public_int (offset: 4)
Primitive: int32_t
[3]
)");
}

View File

@ -144,14 +144,16 @@ TEST_F(DrgnParserTest, SimpleUnion) {
TEST_F(DrgnParserTest, Inheritance) {
test("oid_test_case_inheritance_access_public", R"(
[2] Pointer
[4] Pointer
[0] Class: Public (size: 8)
Parent (offset: 0)
[1] Class: Base (size: 4)
Member: base_int (offset: 0)
[3] Typedef: int32_t
[2] Typedef: __int32_t
Primitive: int32_t
Member: public_int (offset: 4)
Primitive: int32_t
[3]
)");
}

View File

@ -446,7 +446,7 @@ TEST(NameGenTest, AnonymousTypes) {
EXPECT_EQ(myenum.name(), "__oi_anon_1");
EXPECT_EQ(mytypedef.name(), "__oi_anon_2");
EXPECT_EQ(myclass.inputName(), "");
EXPECT_EQ(myenum.inputName(), "");
EXPECT_EQ(myclass.inputName(), "__oi_anon_0");
EXPECT_EQ(myenum.inputName(), "__oi_anon_1");
EXPECT_EQ(mytypedef.inputName(), "");
}

View File

@ -8,10 +8,6 @@ This document describes the format of the container definition files contained i
The fully-qualified name of the container type. This is used to match against
the names of types contained in an executable's debug information.
- `ctype`
A reference to the enum `ContainerTypeEnum` in OI's source code.
- `header`
The name of the C++ header file in which this container is defined.
@ -35,42 +31,32 @@ This document describes the format of the container definition files contained i
C++ code for the definition of a `getSizeType` function for this container.
- `handler`
- `processor.type`
C++ code for the definition of a `TypeHandler` class for this container. See
further down for a description.
The static type that will be filled in by this particular processor. Multiple
processors are allowed and recommended.
- `processor.func`
C++ code for the function body of a function with this signature:
`void(result::Element& el, std::stack<Inst>& ins, ParsedData d)` which is
supplied with the data of the paired static type.
- `traversal_func`
C++ code for the function body of a function with this signature:
`static types::st::Unit<DB> getSizeType(const T& container, ST returnArg)`
where `ST` defines the combined static type of each supplied processor.
## Changes introduced with Typed Data Segment
- `decl` and `func` fields are ignored when using `-ftyped-data-segment` and the
`handler` field is used instead.
### TypeHandler Classes
A `TypeHandler` class describes both what a type will write into the data segment
and how to write it. It consists of two major parts:
- `using type = ...;` - describe what it will write into the data segment.
- `static types::st::Unit<DB> getSizeType(...)` - a function which takes a
const reference to a container and a `::type` by value and fills in the type.
Example:
```cpp
template <typename DB, typename T0>
struct TypeHandler<DB, std::string<T0>> {
using type =
types::st::Pair<DB, types::st::VarInt<DB>, types::st::VarInt<DB>>;
static types::st::Unit<DB> getSizeType(
const std::string<T0> & container,
typename TypeHandler<DB, std::string<T0>>::type returnArg) {
bool sso = ((uintptr_t)container.data() <
(uintptr_t)(&container + sizeof(std::string<T0>))) &&
((uintptr_t)container.data() >= (uintptr_t)&container);
return returnArg.write(container.capacity()).write(container.size());
}
};
```
## Changes introduced with TreeBuilder V2
- `decl`, `func`, and `handler` fields are ignored when using `-ftree-builder-v2`.
The `TypeHandler` is instead constructed from `traversal_func` and `processor`
entries.
## Changes introduced with TypeGraph
- `typeName` and `matcher` fields have been merged into the single field `type_name`.
@ -81,6 +67,16 @@ struct TypeHandler<DB, std::string<T0>> {
- There is one option to specify template parameters which should not be measured: `stub_template_params`. The type graph code will automatically determine what type of stubbing to apply to each parameter in this list.
### Deprecated Options
- `ctype`
A reference to the enum `ContainerTypeEnum` in OI's source code. This is no
longer required with Tree Builder v2.
- `handler`
C++ code for the definition of a `TypeHandler` class for this container. Now
generated from `traversal_func` and `processor` entries.
- `numTemplateParams`
The first `numTemplateParams` template parameters for this container represent

View File

@ -49,3 +49,34 @@ struct TypeHandler<DB, %1%<T0, N>> {
}
};
"""
traversal_func = """
auto tail = returnArg.write(container.size());
for (auto & it: container) {
tail = tail.delegate([&it](auto ret) {
return TypeHandler<DB, T0>::getSizeType(it, ret);
});
}
return tail.finish();
"""
[[codegen.processor]]
type = "types::st::List<DB, typename TypeHandler<DB, T0>::type>"
func = """
static constexpr std::array<std::string_view, 1> names{"TODO"};
static constexpr inst::Field childField{
sizeof(T0),
"[]",
names,
TypeHandler<DB, T0>::fields,
TypeHandler<DB, T0>::processors,
};
size_t size = std::get<ParsedData::List>(d.val).length;
el.exclusive_size = N0 == 0 ? 1 : 0;
el.container_stats.emplace(result::Element::ContainerStats{ .capacity = size, .length = size });
for (size_t i = 0; i < size; i++)
ins.emplace(childField);
"""

View File

@ -53,3 +53,34 @@ struct TypeHandler<DB, %1% <T0>> {
}
};
"""
traversal_func = """
bool sso = ((uintptr_t)container.data() <
(uintptr_t)(&container + sizeof(std::__cxx11::basic_string<T0>))) &&
((uintptr_t)container.data() >= (uintptr_t)&container);
return returnArg.write(container.capacity())
.write(sso)
.write(container.size());
"""
[[codegen.processor]]
type = "types::st::VarInt<DB>"
func = """
uint64_t capacity = std::get<ParsedData::VarInt>(d.val).value;
el.container_stats.emplace(result::Element::ContainerStats { .capacity = capacity });
"""
[[codegen.processor]]
type = "types::st::VarInt<DB>"
func = """
bool sso = std::get<ParsedData::VarInt>(d.val).value;
if (!sso)
el.exclusive_size += el.container_stats->capacity * sizeof(T0);
"""
[[codegen.processor]]
type = "types::st::VarInt<DB>"
func = """
el.container_stats->length = std::get<ParsedData::VarInt>(d.val).value;
"""

View File

@ -66,3 +66,73 @@ struct TypeHandler<DB, %1%<T0, T1, T2, T3, T4, T5>> {
}
};
"""
traversal_func = '''
auto tail = returnArg.write((uintptr_t)&container)
.write(container.capacity())
.write(container.size());
for (const auto& kv : container) {
tail = tail.delegate([&kv](auto ret) {
auto next = ret.delegate([&kv](typename TypeHandler<DB, T0>::type ret) {
return OIInternal::getSizeType<DB>(kv.first, ret);
});
return OIInternal::getSizeType<DB>(kv.second, next);
});
}
return tail.finish();
'''
[[codegen.processor]]
type = "types::st::VarInt<DB>"
func = "el.pointer = std::get<ParsedData::VarInt>(d.val).value;"
[[codegen.processor]]
type = "types::st::VarInt<DB>"
func = '''
el.container_stats.emplace(result::Element::ContainerStats{ .capacity = std::get<ParsedData::VarInt>(d.val).value });
'''
[[codegen.processor]]
type = '''types::st::List<DB, types::st::Pair<DB,
typename TypeHandler<DB, T0>::type,
typename TypeHandler<DB, T1>::type
>>'''
func = '''
using element_type = std::pair<T0, T1>;
static constexpr std::array<std::string_view, 1> names{"TODO"};
static constexpr std::array<inst::Field, 2> entryFields{
inst::Field{
sizeof(T0),
"key",
names,
TypeHandler<DB, T0>::fields,
TypeHandler<DB, T0>::processors,
},
inst::Field{
sizeof(T1),
"value",
names,
TypeHandler<DB, T1>::fields,
TypeHandler<DB, T1>::processors,
},
};
static constexpr auto entryField = inst::Field {
sizeof(element_type),
sizeof(element_type) - sizeof(T0) - sizeof(T1),
"[]",
names,
entryFields,
std::array<inst::ProcessorInst, 0>{},
};
auto list = std::get<ParsedData::List>(d.val);
el.container_stats->length = list.length;
el.exclusive_size += (el.container_stats->capacity - el.container_stats->length) * sizeof(element_type);
for (size_t i = 0; i < list.length; i++)
ins.emplace(entryField);
'''

View File

@ -45,3 +45,36 @@ struct TypeHandler<DB, %1%<T0, T1>> {
}
};
"""
traversal_func = """
return OIInternal::getSizeType<DB>(
container.second,
returnArg.delegate([&container](auto ret) {
return OIInternal::getSizeType<DB>(container.first, ret);
})
);
"""
[[codegen.processor]]
type = "types::st::Pair<DB, typename TypeHandler<DB, T0>::type, typename TypeHandler<DB, T1>::type>"
func = """
static constexpr std::array<std::string_view, 1> names{"TODO"};
static constexpr auto firstField = inst::Field{
sizeof(T0),
"first",
names,
TypeHandler<DB, T0>::fields,
TypeHandler<DB, T0>::processors,
};
static constexpr auto secondField = inst::Field{
sizeof(T1),
"second",
names,
TypeHandler<DB, T1>::fields,
TypeHandler<DB, T1>::processors,
};
el.exclusive_size = sizeof(std::pair<T0, T1>) - sizeof(T0) - sizeof(T1);
ins.emplace(secondField);
ins.emplace(firstField);
"""

View File

@ -1,10 +1,10 @@
[info]
type_name = "std::vector"
stub_template_params = [1]
ctype = "SEQ_TYPE"
header = "vector"
# Old:
ctype = "SEQ_TYPE"
typeName = "std::vector<"
ns = ["namespace std"]
numTemplateParams = 1
@ -63,3 +63,51 @@ struct TypeHandler<DB, %1%<T0, T1>> {
}
};
"""
traversal_func = """
auto tail = returnArg.write((uintptr_t)&container)
.write(container.capacity())
.write(container.size());
// The double ampersand is needed otherwise this loop doesn't work with
// vector<bool>
for (auto&& it : container) {
tail = tail.delegate([&it](auto ret) {
return OIInternal::getSizeType<DB>(it, ret);
});
}
return tail.finish();
"""
[[codegen.processor]]
type = "types::st::VarInt<DB>"
func = """
el.pointer = std::get<ParsedData::VarInt>(d.val).value;
"""
[[codegen.processor]]
type = "types::st::VarInt<DB>"
func = """
el.container_stats.emplace(result::Element::ContainerStats{ .capacity = std::get<ParsedData::VarInt>(d.val).value });
"""
[[codegen.processor]]
type = "types::st::List<DB, typename TypeHandler<DB, T0>::type>"
func = """
static constexpr std::array<std::string_view, 1> names{"TODO"};
static constexpr auto childField = inst::Field{
sizeof(T0),
"[]",
names,
TypeHandler<DB, T0>::fields,
TypeHandler<DB, T0>::processors,
};
auto list = std::get<ParsedData::List>(d.val);
el.container_stats->length = list.length;
el.exclusive_size += (el.container_stats->capacity - el.container_stats->length) * sizeof(T0);
for (size_t i = 0; i < list.length; i++)
ins.emplace(childField);
"""

Some files were not shown because too many files have changed in this diff Show More