mirror of
https://github.com/JakeHillion/object-introspection.git
synced 2024-09-19 19:19:05 +01:00
Support dynamic polymorphic inheritance
i.e. classes which contain virtual functions and use a virtual pointer
This commit is contained in:
parent
aef8826a3a
commit
9e72ada131
@ -56,4 +56,6 @@ struct TypeHierarchy {
|
|||||||
std::set<struct drgn_type *> knownDummyTypeList;
|
std::set<struct drgn_type *> knownDummyTypeList;
|
||||||
std::map<struct drgn_type *, struct drgn_type *> pointerToTypeMap;
|
std::map<struct drgn_type *, struct drgn_type *> pointerToTypeMap;
|
||||||
std::set<struct drgn_type *> thriftIssetStructTypes;
|
std::set<struct drgn_type *> thriftIssetStructTypes;
|
||||||
|
std::map<struct drgn_type *, std::vector<struct drgn_type *>>
|
||||||
|
descendantClasses;
|
||||||
};
|
};
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
#include <boost/algorithm/string.hpp>
|
#include <boost/algorithm/string.hpp>
|
||||||
#include <boost/algorithm/string/regex.hpp>
|
#include <boost/algorithm/string/regex.hpp>
|
||||||
#include <boost/algorithm/string/replace.hpp>
|
#include <boost/algorithm/string/replace.hpp>
|
||||||
|
#include <boost/format.hpp>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
@ -43,8 +44,9 @@ static size_t g_level = 0;
|
|||||||
#define VLOG(verboselevel) \
|
#define VLOG(verboselevel) \
|
||||||
LOG_IF(INFO, VLOG_IS_ON(verboselevel)) << std::string(2 * g_level, ' ')
|
LOG_IF(INFO, VLOG_IS_ON(verboselevel)) << std::string(2 * g_level, ' ')
|
||||||
|
|
||||||
std::unique_ptr<OICodeGen> OICodeGen::buildFromConfig(const Config &c) {
|
std::unique_ptr<OICodeGen> OICodeGen::buildFromConfig(const Config &c,
|
||||||
auto cg = std::unique_ptr<OICodeGen>(new OICodeGen(c));
|
SymbolService &s) {
|
||||||
|
auto cg = std::unique_ptr<OICodeGen>(new OICodeGen(c, s));
|
||||||
|
|
||||||
for (const auto &path : c.containerConfigPaths) {
|
for (const auto &path : c.containerConfigPaths) {
|
||||||
if (!cg->registerContainer(path)) {
|
if (!cg->registerContainer(path)) {
|
||||||
@ -56,7 +58,8 @@ std::unique_ptr<OICodeGen> OICodeGen::buildFromConfig(const Config &c) {
|
|||||||
return cg;
|
return cg;
|
||||||
}
|
}
|
||||||
|
|
||||||
OICodeGen::OICodeGen(const Config &c) : config{c} {
|
OICodeGen::OICodeGen(const Config &c, SymbolService &s)
|
||||||
|
: config{c}, symbols{s} {
|
||||||
// TODO: Should folly::Range just be added as a container?
|
// TODO: Should folly::Range just be added as a container?
|
||||||
auto typesToStub = std::array{
|
auto typesToStub = std::array{
|
||||||
"SharedMutex",
|
"SharedMutex",
|
||||||
@ -986,6 +989,106 @@ std::string OICodeGen::typeToName(drgn_type *type) {
|
|||||||
return typeName;
|
return typeName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool OICodeGen::recordChildren(drgn_type *type) {
|
||||||
|
drgn_type_template_parameter *parents = drgn_type_parents(type);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < drgn_type_num_parents(type); i++) {
|
||||||
|
drgn_qualified_type t{};
|
||||||
|
|
||||||
|
if (auto *err = drgn_template_parameter_type(&parents[i], &t);
|
||||||
|
err != nullptr) {
|
||||||
|
LOG(ERROR) << "Error when looking up parent class for type " << type
|
||||||
|
<< " err " << err->code << " " << err->message;
|
||||||
|
drgn_error_destroy(err);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
drgn_type *parent = drgnUnderlyingType(t.type);
|
||||||
|
if (!isDrgnSizeComplete(parent)) {
|
||||||
|
VLOG(1) << "Incomplete size for parent class (" << drgn_type_tag(parent)
|
||||||
|
<< ") of " << drgn_type_tag(type);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *parentName = drgn_type_tag(parent);
|
||||||
|
if (parentName == nullptr) {
|
||||||
|
VLOG(1) << "No name for parent class (" << parent << ") of "
|
||||||
|
<< drgn_type_tag(type);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* drgn pointers are not stable, so use string representation for reverse
|
||||||
|
* mapping for now. We need to find a better way of creating this
|
||||||
|
* childClasses map - ideally drgn would do this for us.
|
||||||
|
*/
|
||||||
|
childClasses[parentName].push_back(type);
|
||||||
|
VLOG(1) << drgn_type_tag(type) << "(" << type << ") is a child of "
|
||||||
|
<< drgn_type_tag(parent) << "(" << parent << ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Build a mapping of Class -> Children
|
||||||
|
*
|
||||||
|
* drgn only gives us the mapping Class -> Parents, so we must iterate over all
|
||||||
|
* types in the program to build the reverse mapping.
|
||||||
|
*/
|
||||||
|
bool OICodeGen::enumerateChildClasses() {
|
||||||
|
if (!config.polymorphicInheritance) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((setenv("DRGN_ENABLE_TYPE_ITERATOR", "1", 1)) < 0) {
|
||||||
|
LOG(ERROR)
|
||||||
|
<< "Could not set DRGN_ENABLE_TYPE_ITERATOR environment variable";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
drgn_type_iterator *typesIterator;
|
||||||
|
auto *prog = symbols.getDrgnProgram();
|
||||||
|
drgn_error *err = drgn_type_iterator_create(prog, &typesIterator);
|
||||||
|
if (err) {
|
||||||
|
LOG(ERROR) << "Error initialising drgn_type_iterator: " << err->code << ", "
|
||||||
|
<< err->message;
|
||||||
|
drgn_error_destroy(err);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
int j = 0;
|
||||||
|
while (true) {
|
||||||
|
i++;
|
||||||
|
drgn_qualified_type *t;
|
||||||
|
err = drgn_type_iterator_next(typesIterator, &t);
|
||||||
|
if (err) {
|
||||||
|
LOG(ERROR) << "Error from drgn_type_iterator_next: " << err->code << ", "
|
||||||
|
<< err->message;
|
||||||
|
drgn_error_destroy(err);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!t) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
j++;
|
||||||
|
|
||||||
|
auto kind = drgn_type_kind(t->type);
|
||||||
|
if (kind != DRGN_TYPE_CLASS && kind != DRGN_TYPE_STRUCT) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!recordChildren(t->type)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
drgn_type_iterator_destroy(typesIterator);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// The top level function which enumerates the rootType object. This function
|
// The top level function which enumerates the rootType object. This function
|
||||||
// fills out : -
|
// fills out : -
|
||||||
// 1. struct/class definitions
|
// 1. struct/class definitions
|
||||||
@ -994,6 +1097,10 @@ std::string OICodeGen::typeToName(drgn_type *type) {
|
|||||||
//
|
//
|
||||||
// They are appended to the jit code in that order (1), (2), (3)
|
// They are appended to the jit code in that order (1), (2), (3)
|
||||||
bool OICodeGen::populateDefsAndDecls() {
|
bool OICodeGen::populateDefsAndDecls() {
|
||||||
|
if (!enumerateChildClasses()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (drgn_type_has_type(rootType.type) &&
|
if (drgn_type_has_type(rootType.type) &&
|
||||||
drgn_type_kind(rootType.type) == DRGN_TYPE_FUNCTION) {
|
drgn_type_kind(rootType.type) == DRGN_TYPE_FUNCTION) {
|
||||||
rootType = drgn_type_type(rootType.type);
|
rootType = drgn_type_type(rootType.type);
|
||||||
@ -1085,6 +1192,12 @@ drgn_type *OICodeGen::drgnUnderlyingType(drgn_type *type) {
|
|||||||
|
|
||||||
bool OICodeGen::enumerateClassParents(drgn_type *type,
|
bool OICodeGen::enumerateClassParents(drgn_type *type,
|
||||||
const std::string &typeName) {
|
const std::string &typeName) {
|
||||||
|
if (drgn_type_num_parents(type) == 0) {
|
||||||
|
// Be careful to early exit here to avoid accidentally initialising
|
||||||
|
// parentClasses when there are no parents.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
drgn_type_template_parameter *parents = drgn_type_parents(type);
|
drgn_type_template_parameter *parents = drgn_type_parents(type);
|
||||||
|
|
||||||
for (size_t i = 0; i < drgn_type_num_parents(type); ++i) {
|
for (size_t i = 0; i < drgn_type_num_parents(type); ++i) {
|
||||||
@ -1247,6 +1360,41 @@ bool OICodeGen::isEmptyClassOrFunctionType(drgn_type *type,
|
|||||||
drgn_type_num_members(type) == 0);
|
drgn_type_num_members(type) == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns true if the provided type represents a dynamic class.
|
||||||
|
*
|
||||||
|
* From the Itanium C++ ABI, a dynamic class is defined as:
|
||||||
|
* A class requiring a virtual table pointer (because it or its bases have
|
||||||
|
* one or more virtual member functions or virtual base classes).
|
||||||
|
*/
|
||||||
|
bool OICodeGen::isDynamic(drgn_type *type) const {
|
||||||
|
if (!config.polymorphicInheritance || !drgn_type_has_virtuality(type)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (drgn_type_virtuality(type) != 0 /*DW_VIRTUALITY_none*/) {
|
||||||
|
// Virtual class - not fully supported by OI yet
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
drgn_type_member_function *functions = drgn_type_functions(type);
|
||||||
|
for (size_t i = 0; i < drgn_type_num_functions(type); i++) {
|
||||||
|
drgn_qualified_type t{};
|
||||||
|
if (auto *err = drgn_member_function_type(&functions[i], &t)) {
|
||||||
|
LOG(ERROR) << "Error when looking up member function for type " << type
|
||||||
|
<< " err " << err->code << " " << err->message;
|
||||||
|
drgn_error_destroy(err);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (drgn_type_virtuality(t.type) != 0 /*DW_VIRTUALITY_none*/) {
|
||||||
|
// Virtual function
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool OICodeGen::enumerateClassType(drgn_type *type) {
|
bool OICodeGen::enumerateClassType(drgn_type *type) {
|
||||||
std::string typeName = getStructName(type);
|
std::string typeName = getStructName(type);
|
||||||
VLOG(2) << "Transformed typename: " << typeName << " " << type;
|
VLOG(2) << "Transformed typename: " << typeName << " " << type;
|
||||||
@ -1289,6 +1437,12 @@ bool OICodeGen::enumerateClassType(drgn_type *type) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
} else if (ifGenerateMemberDefinition(typeName)) {
|
} else if (ifGenerateMemberDefinition(typeName)) {
|
||||||
|
if (isDynamic(type)) {
|
||||||
|
const auto &children = childClasses[drgn_type_tag(type)];
|
||||||
|
for (const auto &child : children) {
|
||||||
|
enumerateTypesRecurse(child);
|
||||||
|
}
|
||||||
|
}
|
||||||
if (!generateMemberDefinition(type, typeName)) {
|
if (!generateMemberDefinition(type, typeName)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -1645,19 +1799,115 @@ void OICodeGen::getFuncDefClassMembers(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void OICodeGen::enumerateDescendants(drgn_type *type, drgn_type *baseType) {
|
||||||
|
auto it = childClasses.find(drgn_type_tag(type));
|
||||||
|
if (it == childClasses.end()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO this list may end up containing duplicates
|
||||||
|
const auto &children = it->second;
|
||||||
|
descendantClasses[baseType].insert(descendantClasses[baseType].end(),
|
||||||
|
children.begin(), children.end());
|
||||||
|
|
||||||
|
for (const auto &child : children) {
|
||||||
|
enumerateDescendants(child, baseType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void OICodeGen::getFuncDefinitionStr(std::string &code, drgn_type *type,
|
void OICodeGen::getFuncDefinitionStr(std::string &code, drgn_type *type,
|
||||||
const std::string &typeName) {
|
const std::string &typeName) {
|
||||||
if (classMembersMap.find(type) == classMembersMap.end()) {
|
if (classMembersMap.find(type) == classMembersMap.end()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
code += std::string("void getSizeType(const ") + typeName +
|
std::string funcName = "getSizeType";
|
||||||
std::string("& t, size_t& returnArg) {\n");
|
if (isDynamic(type)) {
|
||||||
|
funcName = "getSizeTypeConcrete";
|
||||||
|
}
|
||||||
|
|
||||||
|
code +=
|
||||||
|
"void " + funcName + "(const " + typeName + "& t, size_t& returnArg) {\n";
|
||||||
|
|
||||||
std::unordered_map<std::string, int> memberNames;
|
std::unordered_map<std::string, int> memberNames;
|
||||||
getFuncDefClassMembers(code, type, memberNames);
|
getFuncDefClassMembers(code, type, memberNames);
|
||||||
|
|
||||||
code += "}\n";
|
code += "}\n";
|
||||||
|
|
||||||
|
if (isDynamic(type)) {
|
||||||
|
enumerateDescendants(type, type);
|
||||||
|
auto it = descendantClasses.find(type);
|
||||||
|
if (it == descendantClasses.end()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto &descendants = it->second;
|
||||||
|
|
||||||
|
std::vector<std::pair<std::string, SymbolInfo>> concreteClasses;
|
||||||
|
concreteClasses.reserve(descendants.size());
|
||||||
|
|
||||||
|
for (const auto &child : descendants) {
|
||||||
|
auto fqChildName = *fullyQualifiedName(child);
|
||||||
|
|
||||||
|
// We must split this assignment and append because the C++ standard lacks
|
||||||
|
// an operator for concatenating std::string and std::string_view...
|
||||||
|
std::string childVtableName = "vtable for ";
|
||||||
|
childVtableName += fqChildName;
|
||||||
|
|
||||||
|
auto optVtableSym = symbols.locateSymbol(childVtableName, true);
|
||||||
|
if (!optVtableSym) {
|
||||||
|
LOG(ERROR) << "Failed to find vtable address for '" << childVtableName;
|
||||||
|
LOG(ERROR) << "Falling back to non dynamic mode";
|
||||||
|
concreteClasses.clear();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
auto vtableSym = *optVtableSym;
|
||||||
|
|
||||||
|
auto oiChildName = *getNameForType(child);
|
||||||
|
concreteClasses.push_back({oiChildName, vtableSym});
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto &child : concreteClasses) {
|
||||||
|
const auto &className = child.first;
|
||||||
|
code += "void getSizeTypeConcrete(const " + className +
|
||||||
|
"& t, size_t& returnArg);\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
code += std::string("void getSizeType(const ") + typeName +
|
||||||
|
std::string("& t, size_t& returnArg) {\n");
|
||||||
|
// The Itanium C++ ABI defines that the vptr must appear at the zero-offset
|
||||||
|
// position in any class in which they are present.
|
||||||
|
code += " auto *vptr = *reinterpret_cast<uintptr_t * const *>(&t);\n";
|
||||||
|
code += " uintptr_t topOffset = *(vptr - 2);\n";
|
||||||
|
code += " uintptr_t vptrVal = reinterpret_cast<uintptr_t>(vptr);\n";
|
||||||
|
|
||||||
|
for (size_t i = 0; i < concreteClasses.size(); i++) {
|
||||||
|
// The vptr will point to *somewhere* in the vtable of this object's
|
||||||
|
// concrete class. The exact offset into the vtable can vary based on a
|
||||||
|
// number of factors, so we compare the vptr against the vtable range for
|
||||||
|
// each possible class to determine the concrete type.
|
||||||
|
//
|
||||||
|
// This works for C++ compilers which follow the GNU v3 ABI, i.e. GCC and
|
||||||
|
// Clang. Other compilers may differ.
|
||||||
|
const auto &[className, vtableSym] = concreteClasses[i];
|
||||||
|
uintptr_t vtableMinAddr = vtableSym.addr;
|
||||||
|
uintptr_t vtableMaxAddr = vtableSym.addr + vtableSym.size;
|
||||||
|
code += " if (vptrVal >= 0x" +
|
||||||
|
(boost::format("%x") % vtableMinAddr).str() + " && vptrVal < 0x" +
|
||||||
|
(boost::format("%x") % vtableMaxAddr).str() + ") {\n";
|
||||||
|
code += " SAVE_DATA(" + std::to_string(i) + ");\n";
|
||||||
|
code +=
|
||||||
|
" uintptr_t baseAddress = reinterpret_cast<uintptr_t>(&t) + "
|
||||||
|
"topOffset;\n";
|
||||||
|
code += " getSizeTypeConcrete(*reinterpret_cast<const " + className +
|
||||||
|
"*>(baseAddress), returnArg);\n";
|
||||||
|
code += " return;\n";
|
||||||
|
code += " }\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
code += " SAVE_DATA(-1);\n";
|
||||||
|
code += " getSizeTypeConcrete(t, returnArg);\n";
|
||||||
|
code += "}\n";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string OICodeGen::templateTransformType(const std::string &typeName) {
|
std::string OICodeGen::templateTransformType(const std::string &typeName) {
|
||||||
@ -3666,6 +3916,7 @@ TypeHierarchy OICodeGen::getTypeHierarchy() {
|
|||||||
.knownDummyTypeList = knownDummyTypeList,
|
.knownDummyTypeList = knownDummyTypeList,
|
||||||
.pointerToTypeMap = pointerToTypeMap,
|
.pointerToTypeMap = pointerToTypeMap,
|
||||||
.thriftIssetStructTypes = thriftIssetStructTypes,
|
.thriftIssetStructTypes = thriftIssetStructTypes,
|
||||||
|
.descendantClasses = descendantClasses,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3686,13 +3937,17 @@ std::string OICodeGen::Config::toString() const {
|
|||||||
(packStructs ? "" : "Dont") + "PackStructs,"s +
|
(packStructs ? "" : "Dont") + "PackStructs,"s +
|
||||||
(genPaddingStats ? "" : "Dont") + "GenPaddingStats,"s +
|
(genPaddingStats ? "" : "Dont") + "GenPaddingStats,"s +
|
||||||
(captureThriftIsset ? "" : "Dont") + "CaptureThriftIsset,"s +
|
(captureThriftIsset ? "" : "Dont") + "CaptureThriftIsset,"s +
|
||||||
|
(polymorphicInheritance ? "" : "Dont") + "PolymorphicInheritance,"s +
|
||||||
ignoreMembers;
|
ignoreMembers;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string> OICodeGen::Config::toOptions() const {
|
std::vector<std::string> OICodeGen::Config::toOptions() const {
|
||||||
return {"", // useDataSegment is always true?
|
return {"", // useDataSegment is always true?
|
||||||
(chaseRawPointers ? "-n" : ""), (packStructs ? "" : "-z"),
|
(chaseRawPointers ? "-n" : ""),
|
||||||
(genPaddingStats ? "" : "-w"), (captureThriftIsset ? "-T" : "")};
|
(packStructs ? "" : "-z"),
|
||||||
|
(genPaddingStats ? "" : "-w"),
|
||||||
|
(captureThriftIsset ? "-T" : ""),
|
||||||
|
(polymorphicInheritance ? "-P" : "")};
|
||||||
}
|
}
|
||||||
|
|
||||||
void OICodeGen::initializeCodeGen() {
|
void OICodeGen::initializeCodeGen() {
|
||||||
|
@ -58,6 +58,7 @@ class OICodeGen {
|
|||||||
bool packStructs;
|
bool packStructs;
|
||||||
bool genPaddingStats;
|
bool genPaddingStats;
|
||||||
bool captureThriftIsset;
|
bool captureThriftIsset;
|
||||||
|
bool polymorphicInheritance;
|
||||||
|
|
||||||
std::set<fs::path> containerConfigPaths{};
|
std::set<fs::path> containerConfigPaths{};
|
||||||
std::set<std::string> defaultHeaders{};
|
std::set<std::string> defaultHeaders{};
|
||||||
@ -71,10 +72,11 @@ class OICodeGen {
|
|||||||
private:
|
private:
|
||||||
// Private constructor. Please use the fallible `OICodeGen::buildFromConfig`
|
// Private constructor. Please use the fallible `OICodeGen::buildFromConfig`
|
||||||
// for the expected behaviour.
|
// for the expected behaviour.
|
||||||
OICodeGen(const Config &);
|
OICodeGen(const Config &, SymbolService &);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static std::unique_ptr<OICodeGen> buildFromConfig(const Config &);
|
static std::unique_ptr<OICodeGen> buildFromConfig(const Config &,
|
||||||
|
SymbolService &);
|
||||||
bool generate(std::string &code);
|
bool generate(std::string &code);
|
||||||
|
|
||||||
[[deprecated("Use generate(std::string&) instead.")]] bool
|
[[deprecated("Use generate(std::string&) instead.")]] bool
|
||||||
@ -112,6 +114,7 @@ class OICodeGen {
|
|||||||
bool enumerateTypesRecurse(drgn_type *type);
|
bool enumerateTypesRecurse(drgn_type *type);
|
||||||
static std::string_view drgnKindStr(drgn_type *type);
|
static std::string_view drgnKindStr(drgn_type *type);
|
||||||
std::set<drgn_type *> processedTypes;
|
std::set<drgn_type *> processedTypes;
|
||||||
|
bool isDynamic(drgn_type *type) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Config config{};
|
Config config{};
|
||||||
@ -139,6 +142,10 @@ class OICodeGen {
|
|||||||
|
|
||||||
std::map<std::string, std::string> typedefMap;
|
std::map<std::string, std::string> typedefMap;
|
||||||
std::map<drgn_type *, std::vector<ParentMember>> parentClasses;
|
std::map<drgn_type *, std::vector<ParentMember>> parentClasses;
|
||||||
|
std::map<std::string, std::vector<drgn_type *>> childClasses;
|
||||||
|
std::map<drgn_type *, std::vector<drgn_type *>> descendantClasses;
|
||||||
|
|
||||||
|
SymbolService &symbols;
|
||||||
|
|
||||||
size_t pad_index = 0;
|
size_t pad_index = 0;
|
||||||
std::unordered_map<drgn_type *, std::pair<size_t, size_t>> paddingIndexMap;
|
std::unordered_map<drgn_type *, std::pair<size_t, size_t>> paddingIndexMap;
|
||||||
@ -206,6 +213,8 @@ class OICodeGen {
|
|||||||
bool getDrgnTypeName(drgn_type *type, std::string &outName);
|
bool getDrgnTypeName(drgn_type *type, std::string &outName);
|
||||||
|
|
||||||
bool getDrgnTypeNameInt(drgn_type *type, std::string &outName);
|
bool getDrgnTypeNameInt(drgn_type *type, std::string &outName);
|
||||||
|
bool recordChildren(drgn_type *type);
|
||||||
|
bool enumerateChildClasses();
|
||||||
bool populateDefsAndDecls();
|
bool populateDefsAndDecls();
|
||||||
static void memberTransformName(
|
static void memberTransformName(
|
||||||
std::map<std::string, std::string> &templateTransformMap,
|
std::map<std::string, std::string> &templateTransformMap,
|
||||||
@ -224,6 +233,7 @@ class OICodeGen {
|
|||||||
const std::vector<size_t> ¶mIdxs,
|
const std::vector<size_t> ¶mIdxs,
|
||||||
bool &ifStub);
|
bool &ifStub);
|
||||||
bool getContainerTemplateParams(drgn_type *type, bool &ifStub);
|
bool getContainerTemplateParams(drgn_type *type, bool &ifStub);
|
||||||
|
void enumerateDescendants(drgn_type *type, drgn_type *baseType);
|
||||||
void getFuncDefinitionStr(std::string &code, drgn_type *type,
|
void getFuncDefinitionStr(std::string &code, drgn_type *type,
|
||||||
const std::string &typeName);
|
const std::string &typeName);
|
||||||
std::optional<uint64_t> getDrgnTypeSize(drgn_type *type);
|
std::optional<uint64_t> getDrgnTypeSize(drgn_type *type);
|
||||||
|
@ -152,6 +152,8 @@ constexpr static OIOpts opts{
|
|||||||
"Padded structs will be written to file called PADDING"},
|
"Padded structs will be written to file called PADDING"},
|
||||||
OIOpt{'T', "capture-thrift-isset", no_argument, nullptr,
|
OIOpt{'T', "capture-thrift-isset", no_argument, nullptr,
|
||||||
"Capture the isset value for Thrift fields"},
|
"Capture the isset value for Thrift fields"},
|
||||||
|
OIOpt{'P', "polymorphic-inheritance", no_argument, nullptr,
|
||||||
|
"Follow runtime polymorphic inheritance hierarchies"},
|
||||||
OIOpt{'m', "mode", required_argument, "[prod]",
|
OIOpt{'m', "mode", required_argument, "[prod]",
|
||||||
"Allows to specify a mode of operation/group of settings"},
|
"Allows to specify a mode of operation/group of settings"},
|
||||||
};
|
};
|
||||||
@ -459,6 +461,7 @@ int main(int argc, char *argv[]) {
|
|||||||
bool packStructs = true;
|
bool packStructs = true;
|
||||||
bool dumpDataSegment = false;
|
bool dumpDataSegment = false;
|
||||||
bool captureThriftIsset = false;
|
bool captureThriftIsset = false;
|
||||||
|
bool polymorphicInheritance = false;
|
||||||
|
|
||||||
Metrics::Tracing _("main");
|
Metrics::Tracing _("main");
|
||||||
#ifndef OSS_ENABLE
|
#ifndef OSS_ENABLE
|
||||||
@ -635,6 +638,9 @@ int main(int argc, char *argv[]) {
|
|||||||
case 'T':
|
case 'T':
|
||||||
captureThriftIsset = true;
|
captureThriftIsset = true;
|
||||||
break;
|
break;
|
||||||
|
case 'P':
|
||||||
|
polymorphicInheritance = true;
|
||||||
|
break;
|
||||||
case 'h':
|
case 'h':
|
||||||
default:
|
default:
|
||||||
usage();
|
usage();
|
||||||
@ -679,6 +685,7 @@ int main(int argc, char *argv[]) {
|
|||||||
.packStructs = packStructs,
|
.packStructs = packStructs,
|
||||||
.genPaddingStats = oidConfig.genPaddingStats,
|
.genPaddingStats = oidConfig.genPaddingStats,
|
||||||
.captureThriftIsset = captureThriftIsset,
|
.captureThriftIsset = captureThriftIsset,
|
||||||
|
.polymorphicInheritance = polymorphicInheritance,
|
||||||
};
|
};
|
||||||
|
|
||||||
TreeBuilder::Config tbConfig{
|
TreeBuilder::Config tbConfig{
|
||||||
|
@ -2942,7 +2942,7 @@ std::optional<std::string> OIDebugger::generateCode(const irequest &req) {
|
|||||||
#include "OITraceCode.cpp"
|
#include "OITraceCode.cpp"
|
||||||
;
|
;
|
||||||
|
|
||||||
auto codegen = OICodeGen::buildFromConfig(generatorConfig);
|
auto codegen = OICodeGen::buildFromConfig(generatorConfig, *symbols);
|
||||||
if (!codegen) {
|
if (!codegen) {
|
||||||
return nullopt;
|
return nullopt;
|
||||||
}
|
}
|
||||||
|
@ -86,8 +86,9 @@ OIGenerator::findOilTypesAndNames(drgnplusplus::program& prog) {
|
|||||||
bool OIGenerator::generateForType(const OICodeGen::Config& generatorConfig,
|
bool OIGenerator::generateForType(const OICodeGen::Config& generatorConfig,
|
||||||
const OICompiler::Config& compilerConfig,
|
const OICompiler::Config& compilerConfig,
|
||||||
const drgn_qualified_type& type,
|
const drgn_qualified_type& type,
|
||||||
const std::string& linkageName) {
|
const std::string& linkageName,
|
||||||
auto codegen = OICodeGen::buildFromConfig(generatorConfig);
|
SymbolService& symbols) {
|
||||||
|
auto codegen = OICodeGen::buildFromConfig(generatorConfig, symbols);
|
||||||
if (!codegen) {
|
if (!codegen) {
|
||||||
LOG(ERROR) << "failed to initialise codegen";
|
LOG(ERROR) << "failed to initialise codegen";
|
||||||
return false;
|
return false;
|
||||||
@ -119,7 +120,7 @@ bool OIGenerator::generateForType(const OICodeGen::Config& generatorConfig,
|
|||||||
return compiler.compile(code, sourcePath, outputPath);
|
return compiler.compile(code, sourcePath, outputPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
int OIGenerator::generate(fs::path& primaryObject) {
|
int OIGenerator::generate(fs::path& primaryObject, SymbolService& symbols) {
|
||||||
drgnplusplus::program prog;
|
drgnplusplus::program prog;
|
||||||
|
|
||||||
{
|
{
|
||||||
@ -152,7 +153,8 @@ int OIGenerator::generate(fs::path& primaryObject) {
|
|||||||
|
|
||||||
size_t failures = 0;
|
size_t failures = 0;
|
||||||
for (const auto& [type, linkageName] : oilTypes) {
|
for (const auto& [type, linkageName] : oilTypes) {
|
||||||
if (!generateForType(generatorConfig, compilerConfig, type, linkageName)) {
|
if (!generateForType(generatorConfig, compilerConfig, type, linkageName,
|
||||||
|
symbols)) {
|
||||||
LOG(WARNING) << "failed to generate for symbol `" << linkageName
|
LOG(WARNING) << "failed to generate for symbol `" << linkageName
|
||||||
<< "`. this is non-fatal but the call will not work.";
|
<< "`. this is non-fatal but the call will not work.";
|
||||||
failures++;
|
failures++;
|
||||||
|
@ -28,7 +28,7 @@ namespace ObjectIntrospection {
|
|||||||
|
|
||||||
class OIGenerator {
|
class OIGenerator {
|
||||||
public:
|
public:
|
||||||
int generate(fs::path& primaryObject);
|
int generate(fs::path& primaryObject, SymbolService& symbols);
|
||||||
|
|
||||||
void setOutputPath(fs::path _outputPath) {
|
void setOutputPath(fs::path _outputPath) {
|
||||||
outputPath = std::move(_outputPath);
|
outputPath = std::move(_outputPath);
|
||||||
@ -50,7 +50,7 @@ class OIGenerator {
|
|||||||
bool generateForType(const OICodeGen::Config& generatorConfig,
|
bool generateForType(const OICodeGen::Config& generatorConfig,
|
||||||
const OICompiler::Config& compilerConfig,
|
const OICompiler::Config& compilerConfig,
|
||||||
const drgn_qualified_type& type,
|
const drgn_qualified_type& type,
|
||||||
const std::string& linkageName);
|
const std::string& linkageName, SymbolService& symbols);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace ObjectIntrospection
|
} // namespace ObjectIntrospection
|
||||||
|
@ -185,7 +185,7 @@ int OILibraryImpl::compileCode() {
|
|||||||
#include "OITraceCode.cpp"
|
#include "OITraceCode.cpp"
|
||||||
;
|
;
|
||||||
|
|
||||||
auto codegen = OICodeGen::buildFromConfig(generatorConfig);
|
auto codegen = OICodeGen::buildFromConfig(generatorConfig, *symbols);
|
||||||
if (!codegen) {
|
if (!codegen) {
|
||||||
return OIL_COMPILATION_FAILURE;
|
return OIL_COMPILATION_FAILURE;
|
||||||
}
|
}
|
||||||
|
@ -374,6 +374,7 @@ void serialize(Archive &ar, struct TypeHierarchy &th,
|
|||||||
ar &th.knownDummyTypeList;
|
ar &th.knownDummyTypeList;
|
||||||
ar &th.pointerToTypeMap;
|
ar &th.pointerToTypeMap;
|
||||||
ar &th.thriftIssetStructTypes;
|
ar &th.thriftIssetStructTypes;
|
||||||
|
ar &th.descendantClasses;
|
||||||
}
|
}
|
||||||
|
|
||||||
INSTANCIATE_SERIALIZE(struct TypeHierarchy)
|
INSTANCIATE_SERIALIZE(struct TypeHierarchy)
|
||||||
|
@ -51,7 +51,7 @@ DEFINE_TYPE_VERSION(struct drgn_type, 152, 4)
|
|||||||
DEFINE_TYPE_VERSION(DrgnClassMemberInfo, 64, 3)
|
DEFINE_TYPE_VERSION(DrgnClassMemberInfo, 64, 3)
|
||||||
DEFINE_TYPE_VERSION(struct drgn_qualified_type, 16, 2)
|
DEFINE_TYPE_VERSION(struct drgn_qualified_type, 16, 2)
|
||||||
DEFINE_TYPE_VERSION(RootInfo, 48, 2)
|
DEFINE_TYPE_VERSION(RootInfo, 48, 2)
|
||||||
DEFINE_TYPE_VERSION(TypeHierarchy, 336, 5)
|
DEFINE_TYPE_VERSION(TypeHierarchy, 384, 6)
|
||||||
|
|
||||||
#undef DEFINE_TYPE_VERSION
|
#undef DEFINE_TYPE_VERSION
|
||||||
|
|
||||||
|
@ -101,10 +101,11 @@ SymbolService::~SymbolService() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct ModParams {
|
struct ModParams {
|
||||||
const char *st;
|
std::string_view symName;
|
||||||
GElf_Sym s;
|
GElf_Sym sym;
|
||||||
GElf_Addr value;
|
GElf_Addr value;
|
||||||
std::vector<std::pair<uint64_t, uint64_t>> &exeAddrs;
|
std::vector<std::pair<uint64_t, uint64_t>> &exeAddrs;
|
||||||
|
bool demangle;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -146,15 +147,20 @@ static int moduleCallback(Dwfl_Module *mod, void ** /* userData */,
|
|||||||
Elf *elf = nullptr;
|
Elf *elf = nullptr;
|
||||||
GElf_Word shndxp = 0;
|
GElf_Word shndxp = 0;
|
||||||
|
|
||||||
const char *sname = dwfl_module_getsym_info(mod, i, &m->s, &m->value,
|
const char *lookupResult = dwfl_module_getsym_info(
|
||||||
&shndxp, &elf, nullptr);
|
mod, i, &m->sym, &m->value, &shndxp, &elf, nullptr);
|
||||||
|
if (lookupResult == nullptr || lookupResult[0] == '\0') {
|
||||||
if (sname == nullptr || sname[0] == '\0') {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string symName = lookupResult;
|
||||||
|
|
||||||
|
if (m->demangle) {
|
||||||
|
symName = boost::core::demangle(symName.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
switch
|
switch
|
||||||
GELF_ST_TYPE(m->s.st_info) {
|
GELF_ST_TYPE(m->sym.st_info) {
|
||||||
case STT_SECTION:
|
case STT_SECTION:
|
||||||
case STT_FILE:
|
case STT_FILE:
|
||||||
case STT_TLS:
|
case STT_TLS:
|
||||||
@ -162,10 +168,9 @@ static int moduleCallback(Dwfl_Module *mod, void ** /* userData */,
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case STT_OBJECT:
|
case STT_OBJECT:
|
||||||
if (shndxp != SHN_UNDEF && m->st && !strcmp(sname, m->st)) {
|
if (shndxp != SHN_UNDEF && symName == m->symName) {
|
||||||
VLOG(1) << "Symbol lookup successful for " << sname << " in module "
|
VLOG(1) << "Symbol lookup successful for " << symName
|
||||||
<< name;
|
<< " in module " << name;
|
||||||
m->st = nullptr;
|
|
||||||
return DWARF_CB_ABORT;
|
return DWARF_CB_ABORT;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -176,12 +181,10 @@ static int moduleCallback(Dwfl_Module *mod, void ** /* userData */,
|
|||||||
* to us here has NOTYPE yet readelf shows me it is defined
|
* to us here has NOTYPE yet readelf shows me it is defined
|
||||||
* as an STT_FUNC. Confused...
|
* as an STT_FUNC. Confused...
|
||||||
*/
|
*/
|
||||||
if (shndxp != SHN_UNDEF && m->st && !strcmp(sname, m->st) &&
|
if (shndxp != SHN_UNDEF && symName == m->symName &&
|
||||||
isExecutableAddr(m->value, m->exeAddrs)) {
|
isExecutableAddr(m->value, m->exeAddrs)) {
|
||||||
m->st = nullptr;
|
VLOG(1) << "Symbol lookup successful for " << symName
|
||||||
|
<< " in module " << name;
|
||||||
VLOG(1) << "Symbol lookup successful for " << sname << " in module "
|
|
||||||
<< name;
|
|
||||||
|
|
||||||
return DWARF_CB_ABORT;
|
return DWARF_CB_ABORT;
|
||||||
}
|
}
|
||||||
@ -201,7 +204,7 @@ static int moduleCallback(Dwfl_Module *mod, void ** /* userData */,
|
|||||||
* @return - A std::optional with the symbol's information
|
* @return - A std::optional with the symbol's information
|
||||||
*/
|
*/
|
||||||
std::optional<SymbolInfo> SymbolService::locateSymbol(
|
std::optional<SymbolInfo> SymbolService::locateSymbol(
|
||||||
const std::string &symName) {
|
const std::string &symName, bool demangle) {
|
||||||
static char *debuginfo_path;
|
static char *debuginfo_path;
|
||||||
static const Dwfl_Callbacks proc_callbacks{
|
static const Dwfl_Callbacks proc_callbacks{
|
||||||
.find_elf = dwfl_linux_proc_find_elf,
|
.find_elf = dwfl_linux_proc_find_elf,
|
||||||
@ -259,15 +262,18 @@ std::optional<SymbolInfo> SymbolService::locateSymbol(
|
|||||||
LOG(ERROR) << "dwfl_report_end: " << dwfl_errmsg(-1);
|
LOG(ERROR) << "dwfl_report_end: " << dwfl_errmsg(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
ModParams m = {
|
ModParams m = {.symName = symName,
|
||||||
.st = symName.c_str(), .s = {}, .value = 0, .exeAddrs = executableAddrs};
|
.sym = {},
|
||||||
|
.value = 0,
|
||||||
|
.exeAddrs = executableAddrs,
|
||||||
|
.demangle = demangle};
|
||||||
|
|
||||||
dwfl_getmodules(dwfl, moduleCallback, (void *)&m, 0);
|
dwfl_getmodules(dwfl, moduleCallback, (void *)&m, 0);
|
||||||
|
|
||||||
if (m.value == 0) {
|
if (m.value == 0) {
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
return SymbolInfo{m.value, m.s.st_size};
|
return SymbolInfo{m.value, m.sym.st_size};
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::string bytesToHexString(const unsigned char *bytes, int nbbytes) {
|
static std::string bytesToHexString(const unsigned char *bytes, int nbbytes) {
|
||||||
|
@ -43,7 +43,8 @@ class SymbolService {
|
|||||||
struct drgn_program *getDrgnProgram();
|
struct drgn_program *getDrgnProgram();
|
||||||
|
|
||||||
std::optional<std::string> locateBuildID();
|
std::optional<std::string> locateBuildID();
|
||||||
std::optional<SymbolInfo> locateSymbol(const std::string &);
|
std::optional<SymbolInfo> locateSymbol(const std::string &,
|
||||||
|
bool demangle = false);
|
||||||
|
|
||||||
std::shared_ptr<FuncDesc> findFuncDesc(const irequest &);
|
std::shared_ptr<FuncDesc> findFuncDesc(const irequest &);
|
||||||
std::shared_ptr<GlobalDesc> findGlobalDesc(const std::string &);
|
std::shared_ptr<GlobalDesc> findGlobalDesc(const std::string &);
|
||||||
|
@ -444,7 +444,23 @@ TreeBuilder::Node TreeBuilder::process(NodeID id, Variable variable) {
|
|||||||
} else if (isContainer(variable)) {
|
} else if (isContainer(variable)) {
|
||||||
processContainer(variable, node);
|
processContainer(variable, node);
|
||||||
} else {
|
} else {
|
||||||
auto entry = th->classMembersMap.find(variable.type);
|
drgn_type *objectType = variable.type;
|
||||||
|
if (auto it = th->descendantClasses.find(objectType);
|
||||||
|
it != th->descendantClasses.end()) {
|
||||||
|
// The first item of data in dynamic classes identifies which
|
||||||
|
// concrete type we should process it as, represented as an index
|
||||||
|
// into the vector of child classes, or -1 to processes this type
|
||||||
|
// as itself.
|
||||||
|
const auto &descendants = it->second;
|
||||||
|
auto val = next();
|
||||||
|
if (val != (uint64_t)-1) {
|
||||||
|
objectType = descendants[val];
|
||||||
|
node.typeName = drgnTypeToName(objectType);
|
||||||
|
node.staticSize = getDrgnTypeSize(objectType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto entry = th->classMembersMap.find(objectType);
|
||||||
if (entry == th->classMembersMap.end() || entry->second.empty()) {
|
if (entry == th->classMembersMap.end() || entry->second.empty()) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -455,7 +471,7 @@ TreeBuilder::Node TreeBuilder::process(NodeID id, Variable variable) {
|
|||||||
auto childID = node.children->first;
|
auto childID = node.children->first;
|
||||||
|
|
||||||
bool captureThriftIsset =
|
bool captureThriftIsset =
|
||||||
th->thriftIssetStructTypes.contains(variable.type);
|
th->thriftIssetStructTypes.contains(objectType);
|
||||||
|
|
||||||
for (std::size_t i = 0; i < members.size(); i++) {
|
for (std::size_t i = 0; i < members.size(); i++) {
|
||||||
std::optional<bool> isset;
|
std::optional<bool> isset;
|
||||||
|
@ -20,6 +20,8 @@ definitions = '''
|
|||||||
'''
|
'''
|
||||||
[cases]
|
[cases]
|
||||||
[cases.a_as_a]
|
[cases.a_as_a]
|
||||||
|
oil_skip = "Polymorphic inheritance disabled in OIL"
|
||||||
|
cli_options = ["--polymorphic-inheritance"]
|
||||||
param_types = ["const A&"]
|
param_types = ["const A&"]
|
||||||
arg_types = ["A"]
|
arg_types = ["A"]
|
||||||
setup = "return {};"
|
setup = "return {};"
|
||||||
@ -33,6 +35,8 @@ definitions = '''
|
|||||||
]}]'''
|
]}]'''
|
||||||
|
|
||||||
[cases.b_as_a]
|
[cases.b_as_a]
|
||||||
|
oil_skip = "Polymorphic inheritance disabled in OIL"
|
||||||
|
cli_options = ["--polymorphic-inheritance"]
|
||||||
param_types = ["const A&"]
|
param_types = ["const A&"]
|
||||||
arg_types = ["B"]
|
arg_types = ["B"]
|
||||||
setup = '''
|
setup = '''
|
||||||
@ -41,14 +45,17 @@ definitions = '''
|
|||||||
return b;
|
return b;
|
||||||
'''
|
'''
|
||||||
expect_json = '''[{
|
expect_json = '''[{
|
||||||
"typeName":"A",
|
"typeName":"B",
|
||||||
"staticSize":16,
|
"staticSize":40,
|
||||||
"dynamicSize":0,
|
"dynamicSize":12,
|
||||||
"members":[
|
"members":[
|
||||||
{"staticSize":8, "dynamicSize":0},
|
{"staticSize":8, "dynamicSize":0},
|
||||||
{"name":"int_a", "staticSize":4, "dynamicSize":0}
|
{"name":"int_a", "staticSize":4, "dynamicSize":0},
|
||||||
|
{"name":"vec_b", "staticSize":24, "dynamicSize":12, "length":3, "capacity":3, "elementStaticSize":4}
|
||||||
]}]'''
|
]}]'''
|
||||||
[cases.b_as_b]
|
[cases.b_as_b]
|
||||||
|
oil_skip = "Polymorphic inheritance disabled in OIL"
|
||||||
|
cli_options = ["--polymorphic-inheritance"]
|
||||||
param_types = ["const B&"]
|
param_types = ["const B&"]
|
||||||
arg_types = ["B"]
|
arg_types = ["B"]
|
||||||
setup = '''
|
setup = '''
|
||||||
@ -67,6 +74,8 @@ definitions = '''
|
|||||||
]}]'''
|
]}]'''
|
||||||
|
|
||||||
[cases.c_as_a]
|
[cases.c_as_a]
|
||||||
|
oil_skip = "Polymorphic inheritance disabled in OIL"
|
||||||
|
cli_options = ["--polymorphic-inheritance"]
|
||||||
param_types = ["const A&"]
|
param_types = ["const A&"]
|
||||||
arg_types = ["C"]
|
arg_types = ["C"]
|
||||||
setup = '''
|
setup = '''
|
||||||
@ -75,14 +84,18 @@ definitions = '''
|
|||||||
return c;
|
return c;
|
||||||
'''
|
'''
|
||||||
expect_json = '''[{
|
expect_json = '''[{
|
||||||
"typeName":"A",
|
"typeName":"C",
|
||||||
"staticSize":16,
|
"staticSize":48,
|
||||||
"dynamicSize":0,
|
"dynamicSize":12,
|
||||||
"members":[
|
"members":[
|
||||||
{"staticSize":8, "dynamicSize":0},
|
{"staticSize":8, "dynamicSize":0},
|
||||||
{"name":"int_a", "staticSize":4, "dynamicSize":0}
|
{"name":"int_a", "staticSize":4, "dynamicSize":0},
|
||||||
|
{"name":"vec_b", "staticSize":24, "dynamicSize":12, "length":3, "capacity":3, "elementStaticSize":4},
|
||||||
|
{"name":"int_c", "staticSize":4, "dynamicSize":0}
|
||||||
]}]'''
|
]}]'''
|
||||||
[cases.c_as_b]
|
[cases.c_as_b]
|
||||||
|
oil_skip = "Polymorphic inheritance disabled in OIL"
|
||||||
|
cli_options = ["--polymorphic-inheritance"]
|
||||||
param_types = ["const B&"]
|
param_types = ["const B&"]
|
||||||
arg_types = ["C"]
|
arg_types = ["C"]
|
||||||
setup = '''
|
setup = '''
|
||||||
@ -91,15 +104,18 @@ definitions = '''
|
|||||||
return c;
|
return c;
|
||||||
'''
|
'''
|
||||||
expect_json = '''[{
|
expect_json = '''[{
|
||||||
"typeName":"B",
|
"typeName":"C",
|
||||||
"staticSize":40,
|
"staticSize":48,
|
||||||
"dynamicSize":12,
|
"dynamicSize":12,
|
||||||
"members":[
|
"members":[
|
||||||
{"staticSize":8, "dynamicSize":0},
|
{"staticSize":8, "dynamicSize":0},
|
||||||
{"name":"int_a", "staticSize":4, "dynamicSize":0},
|
{"name":"int_a", "staticSize":4, "dynamicSize":0},
|
||||||
{"name":"vec_b", "staticSize":24, "dynamicSize":12, "length":3, "capacity":3, "elementStaticSize":4}
|
{"name":"vec_b", "staticSize":24, "dynamicSize":12, "length":3, "capacity":3, "elementStaticSize":4},
|
||||||
|
{"name":"int_c", "staticSize":4, "dynamicSize":0}
|
||||||
]}]'''
|
]}]'''
|
||||||
[cases.c_as_c]
|
[cases.c_as_c]
|
||||||
|
oil_skip = "Polymorphic inheritance disabled in OIL"
|
||||||
|
cli_options = ["--polymorphic-inheritance"]
|
||||||
param_types = ["const C&"]
|
param_types = ["const C&"]
|
||||||
arg_types = ["C"]
|
arg_types = ["C"]
|
||||||
setup = '''
|
setup = '''
|
||||||
|
@ -26,6 +26,8 @@ definitions = '''
|
|||||||
'''
|
'''
|
||||||
[cases]
|
[cases]
|
||||||
[cases.root_as_root]
|
[cases.root_as_root]
|
||||||
|
oil_skip = "Polymorphic inheritance disabled in OIL"
|
||||||
|
cli_options = ["--polymorphic-inheritance"]
|
||||||
param_types = ["const Root&"]
|
param_types = ["const Root&"]
|
||||||
arg_types = ["Root"]
|
arg_types = ["Root"]
|
||||||
setup = "return {};"
|
setup = "return {};"
|
||||||
@ -39,6 +41,8 @@ definitions = '''
|
|||||||
]}]'''
|
]}]'''
|
||||||
|
|
||||||
[cases.middle1_as_root]
|
[cases.middle1_as_root]
|
||||||
|
oil_skip = "Polymorphic inheritance disabled in OIL"
|
||||||
|
cli_options = ["--polymorphic-inheritance"]
|
||||||
param_types = ["const Root&"]
|
param_types = ["const Root&"]
|
||||||
arg_types = ["Middle1"]
|
arg_types = ["Middle1"]
|
||||||
setup = '''
|
setup = '''
|
||||||
@ -47,14 +51,17 @@ definitions = '''
|
|||||||
return m;
|
return m;
|
||||||
'''
|
'''
|
||||||
expect_json = '''[{
|
expect_json = '''[{
|
||||||
"typeName":"Root",
|
"typeName":"Middle1",
|
||||||
"staticSize":16,
|
"staticSize":40,
|
||||||
"dynamicSize":0,
|
"dynamicSize":12,
|
||||||
"members":[
|
"members":[
|
||||||
{"staticSize":8, "dynamicSize":0},
|
{"staticSize":8, "dynamicSize":0},
|
||||||
{"name":"int_root", "staticSize":4, "dynamicSize":0}
|
{"name":"int_root", "staticSize":4, "dynamicSize":0},
|
||||||
|
{"name":"vec_middle1", "staticSize":24, "dynamicSize":12, "length":3, "capacity":3, "elementStaticSize":4}
|
||||||
]}]'''
|
]}]'''
|
||||||
[cases.middle1_as_middle1]
|
[cases.middle1_as_middle1]
|
||||||
|
oil_skip = "Polymorphic inheritance disabled in OIL"
|
||||||
|
cli_options = ["--polymorphic-inheritance"]
|
||||||
param_types = ["const Middle1&"]
|
param_types = ["const Middle1&"]
|
||||||
arg_types = ["Middle1"]
|
arg_types = ["Middle1"]
|
||||||
setup = '''
|
setup = '''
|
||||||
@ -73,6 +80,8 @@ definitions = '''
|
|||||||
]}]'''
|
]}]'''
|
||||||
|
|
||||||
[cases.middle2_as_root]
|
[cases.middle2_as_root]
|
||||||
|
oil_skip = "Polymorphic inheritance disabled in OIL"
|
||||||
|
cli_options = ["--polymorphic-inheritance"]
|
||||||
param_types = ["const Root&"]
|
param_types = ["const Root&"]
|
||||||
arg_types = ["Middle2"]
|
arg_types = ["Middle2"]
|
||||||
setup = '''
|
setup = '''
|
||||||
@ -81,14 +90,17 @@ definitions = '''
|
|||||||
return m;
|
return m;
|
||||||
'''
|
'''
|
||||||
expect_json = '''[{
|
expect_json = '''[{
|
||||||
"typeName":"Root",
|
"typeName":"Middle2",
|
||||||
"staticSize":16,
|
"staticSize":40,
|
||||||
"dynamicSize":0,
|
"dynamicSize":8,
|
||||||
"members":[
|
"members":[
|
||||||
{"staticSize":8, "dynamicSize":0},
|
{"staticSize":8, "dynamicSize":0},
|
||||||
{"name":"int_root", "staticSize":4, "dynamicSize":0}
|
{"name":"int_root", "staticSize":4, "dynamicSize":0},
|
||||||
|
{"name":"vec_middle2", "staticSize":24, "dynamicSize":8, "length":2, "capacity":2, "elementStaticSize":4}
|
||||||
]}]'''
|
]}]'''
|
||||||
[cases.middle2_as_middle2]
|
[cases.middle2_as_middle2]
|
||||||
|
oil_skip = "Polymorphic inheritance disabled in OIL"
|
||||||
|
cli_options = ["--polymorphic-inheritance"]
|
||||||
param_types = ["const Middle2&"]
|
param_types = ["const Middle2&"]
|
||||||
arg_types = ["Middle2"]
|
arg_types = ["Middle2"]
|
||||||
setup = '''
|
setup = '''
|
||||||
@ -107,6 +119,8 @@ definitions = '''
|
|||||||
]}]'''
|
]}]'''
|
||||||
|
|
||||||
[cases.child_as_middle1_root]
|
[cases.child_as_middle1_root]
|
||||||
|
oil_skip = "Polymorphic inheritance disabled in OIL"
|
||||||
|
cli_options = ["--polymorphic-inheritance"]
|
||||||
# We need to explicitly cast from Child to Middle1 before going to root to
|
# We need to explicitly cast from Child to Middle1 before going to root to
|
||||||
# resolve the diamond problem
|
# resolve the diamond problem
|
||||||
param_types = ["const Root&"]
|
param_types = ["const Root&"]
|
||||||
@ -118,14 +132,21 @@ definitions = '''
|
|||||||
return static_cast<Middle1&>(*c);
|
return static_cast<Middle1&>(*c);
|
||||||
'''
|
'''
|
||||||
expect_json = '''[{
|
expect_json = '''[{
|
||||||
"typeName":"Root",
|
"typeName":"Child",
|
||||||
"staticSize":16,
|
"staticSize":88,
|
||||||
"dynamicSize":0,
|
"dynamicSize":20,
|
||||||
"members":[
|
"members":[
|
||||||
{"staticSize":8, "dynamicSize":0},
|
{"staticSize":8, "dynamicSize":0},
|
||||||
{"name":"int_root", "staticSize":4, "dynamicSize":0}
|
{"name":"int_root", "staticSize":4, "dynamicSize":0},
|
||||||
|
{"name":"vec_middle1", "staticSize":24, "dynamicSize":12, "length":3, "capacity":3, "elementStaticSize":4},
|
||||||
|
{"staticSize":8, "dynamicSize":0},
|
||||||
|
{"name":"int_root", "staticSize":4, "dynamicSize":0},
|
||||||
|
{"name":"vec_middle2", "staticSize":24, "dynamicSize":8, "length":2, "capacity":2, "elementStaticSize":4},
|
||||||
|
{"name":"int_child", "staticSize":4, "dynamicSize":0}
|
||||||
]}]'''
|
]}]'''
|
||||||
[cases.child_as_middle2_root]
|
[cases.child_as_middle2_root]
|
||||||
|
oil_skip = "Polymorphic inheritance disabled in OIL"
|
||||||
|
cli_options = ["--polymorphic-inheritance"]
|
||||||
# We need to explicitly cast from Child to Middle2 before going to root to
|
# We need to explicitly cast from Child to Middle2 before going to root to
|
||||||
# resolve the diamond problem
|
# resolve the diamond problem
|
||||||
param_types = ["const Root&"]
|
param_types = ["const Root&"]
|
||||||
@ -137,14 +158,21 @@ definitions = '''
|
|||||||
return static_cast<Middle2&>(*c);
|
return static_cast<Middle2&>(*c);
|
||||||
'''
|
'''
|
||||||
expect_json = '''[{
|
expect_json = '''[{
|
||||||
"typeName":"Root",
|
"typeName":"Child",
|
||||||
"staticSize":16,
|
"staticSize":88,
|
||||||
"dynamicSize":0,
|
"dynamicSize":20,
|
||||||
"members":[
|
"members":[
|
||||||
{"staticSize":8, "dynamicSize":0},
|
{"staticSize":8, "dynamicSize":0},
|
||||||
{"name":"int_root", "staticSize":4, "dynamicSize":0}
|
{"name":"int_root", "staticSize":4, "dynamicSize":0},
|
||||||
|
{"name":"vec_middle1", "staticSize":24, "dynamicSize":12, "length":3, "capacity":3, "elementStaticSize":4},
|
||||||
|
{"staticSize":8, "dynamicSize":0},
|
||||||
|
{"name":"int_root", "staticSize":4, "dynamicSize":0},
|
||||||
|
{"name":"vec_middle2", "staticSize":24, "dynamicSize":8, "length":2, "capacity":2, "elementStaticSize":4},
|
||||||
|
{"name":"int_child", "staticSize":4, "dynamicSize":0}
|
||||||
]}]'''
|
]}]'''
|
||||||
[cases.child_as_middle1]
|
[cases.child_as_middle1]
|
||||||
|
oil_skip = "Polymorphic inheritance disabled in OIL"
|
||||||
|
cli_options = ["--polymorphic-inheritance"]
|
||||||
param_types = ["const Middle1&"]
|
param_types = ["const Middle1&"]
|
||||||
arg_types = ["Child"]
|
arg_types = ["Child"]
|
||||||
setup = '''
|
setup = '''
|
||||||
@ -154,15 +182,21 @@ definitions = '''
|
|||||||
return c;
|
return c;
|
||||||
'''
|
'''
|
||||||
expect_json = '''[{
|
expect_json = '''[{
|
||||||
"typeName":"Middle1",
|
"typeName":"Child",
|
||||||
"staticSize":40,
|
"staticSize":88,
|
||||||
"dynamicSize":12,
|
"dynamicSize":20,
|
||||||
"members":[
|
"members":[
|
||||||
{"staticSize":8, "dynamicSize":0},
|
{"staticSize":8, "dynamicSize":0},
|
||||||
{"name":"int_root", "staticSize":4, "dynamicSize":0},
|
{"name":"int_root", "staticSize":4, "dynamicSize":0},
|
||||||
{"name":"vec_middle1", "staticSize":24, "dynamicSize":12, "length":3, "capacity":3, "elementStaticSize":4}
|
{"name":"vec_middle1", "staticSize":24, "dynamicSize":12, "length":3, "capacity":3, "elementStaticSize":4},
|
||||||
|
{"staticSize":8, "dynamicSize":0},
|
||||||
|
{"name":"int_root", "staticSize":4, "dynamicSize":0},
|
||||||
|
{"name":"vec_middle2", "staticSize":24, "dynamicSize":8, "length":2, "capacity":2, "elementStaticSize":4},
|
||||||
|
{"name":"int_child", "staticSize":4, "dynamicSize":0}
|
||||||
]}]'''
|
]}]'''
|
||||||
[cases.child_as_middle2]
|
[cases.child_as_middle2]
|
||||||
|
oil_skip = "Polymorphic inheritance disabled in OIL"
|
||||||
|
cli_options = ["--polymorphic-inheritance"]
|
||||||
param_types = ["const Middle2&"]
|
param_types = ["const Middle2&"]
|
||||||
arg_types = ["Child"]
|
arg_types = ["Child"]
|
||||||
setup = '''
|
setup = '''
|
||||||
@ -172,15 +206,21 @@ definitions = '''
|
|||||||
return c;
|
return c;
|
||||||
'''
|
'''
|
||||||
expect_json = '''[{
|
expect_json = '''[{
|
||||||
"typeName":"Middle2",
|
"typeName":"Child",
|
||||||
"staticSize":40,
|
"staticSize":88,
|
||||||
"dynamicSize":8,
|
"dynamicSize":20,
|
||||||
"members":[
|
"members":[
|
||||||
{"staticSize":8, "dynamicSize":0},
|
{"staticSize":8, "dynamicSize":0},
|
||||||
{"name":"int_root", "staticSize":4, "dynamicSize":0},
|
{"name":"int_root", "staticSize":4, "dynamicSize":0},
|
||||||
{"name":"vec_middle2", "staticSize":24, "dynamicSize":8, "length":2, "capacity":2, "elementStaticSize":4}
|
{"name":"vec_middle1", "staticSize":24, "dynamicSize":12, "length":3, "capacity":3, "elementStaticSize":4},
|
||||||
|
{"staticSize":8, "dynamicSize":0},
|
||||||
|
{"name":"int_root", "staticSize":4, "dynamicSize":0},
|
||||||
|
{"name":"vec_middle2", "staticSize":24, "dynamicSize":8, "length":2, "capacity":2, "elementStaticSize":4},
|
||||||
|
{"name":"int_child", "staticSize":4, "dynamicSize":0}
|
||||||
]}]'''
|
]}]'''
|
||||||
[cases.child_as_child]
|
[cases.child_as_child]
|
||||||
|
oil_skip = "Polymorphic inheritance disabled in OIL"
|
||||||
|
cli_options = ["--polymorphic-inheritance"]
|
||||||
param_types = ["const Child&"]
|
param_types = ["const Child&"]
|
||||||
arg_types = ["Child"]
|
arg_types = ["Child"]
|
||||||
setup = '''
|
setup = '''
|
||||||
|
@ -19,6 +19,8 @@ definitions = '''
|
|||||||
'''
|
'''
|
||||||
[cases]
|
[cases]
|
||||||
[cases.a_as_a]
|
[cases.a_as_a]
|
||||||
|
oil_skip = "Polymorphic inheritance disabled in OIL"
|
||||||
|
cli_options = ["--polymorphic-inheritance"]
|
||||||
param_types = ["const A&"]
|
param_types = ["const A&"]
|
||||||
arg_types = ["A"]
|
arg_types = ["A"]
|
||||||
setup = "return {};"
|
setup = "return {};"
|
||||||
@ -31,6 +33,8 @@ definitions = '''
|
|||||||
]}]'''
|
]}]'''
|
||||||
|
|
||||||
[cases.b_as_a]
|
[cases.b_as_a]
|
||||||
|
oil_skip = "Polymorphic inheritance disabled in OIL"
|
||||||
|
cli_options = ["--polymorphic-inheritance"]
|
||||||
param_types = ["const A&"]
|
param_types = ["const A&"]
|
||||||
arg_types = ["B"]
|
arg_types = ["B"]
|
||||||
setup = '''
|
setup = '''
|
||||||
@ -46,6 +50,8 @@ definitions = '''
|
|||||||
{"name":"int_a", "staticSize":4, "dynamicSize":0}
|
{"name":"int_a", "staticSize":4, "dynamicSize":0}
|
||||||
]}]'''
|
]}]'''
|
||||||
[cases.b_as_b]
|
[cases.b_as_b]
|
||||||
|
oil_skip = "Polymorphic inheritance disabled in OIL"
|
||||||
|
cli_options = ["--polymorphic-inheritance"]
|
||||||
param_types = ["const B&"]
|
param_types = ["const B&"]
|
||||||
arg_types = ["B"]
|
arg_types = ["B"]
|
||||||
setup = '''
|
setup = '''
|
||||||
@ -64,6 +70,8 @@ definitions = '''
|
|||||||
]}]'''
|
]}]'''
|
||||||
|
|
||||||
[cases.c_as_a]
|
[cases.c_as_a]
|
||||||
|
oil_skip = "Polymorphic inheritance disabled in OIL"
|
||||||
|
cli_options = ["--polymorphic-inheritance"]
|
||||||
param_types = ["const A&"]
|
param_types = ["const A&"]
|
||||||
arg_types = ["C"]
|
arg_types = ["C"]
|
||||||
setup = '''
|
setup = '''
|
||||||
@ -79,6 +87,8 @@ definitions = '''
|
|||||||
{"name":"int_a", "staticSize":4, "dynamicSize":0}
|
{"name":"int_a", "staticSize":4, "dynamicSize":0}
|
||||||
]}]'''
|
]}]'''
|
||||||
[cases.c_as_b]
|
[cases.c_as_b]
|
||||||
|
oil_skip = "Polymorphic inheritance disabled in OIL"
|
||||||
|
cli_options = ["--polymorphic-inheritance"]
|
||||||
param_types = ["const B&"]
|
param_types = ["const B&"]
|
||||||
arg_types = ["C"]
|
arg_types = ["C"]
|
||||||
setup = '''
|
setup = '''
|
||||||
@ -87,15 +97,18 @@ definitions = '''
|
|||||||
return c;
|
return c;
|
||||||
'''
|
'''
|
||||||
expect_json = '''[{
|
expect_json = '''[{
|
||||||
"typeName":"B",
|
"typeName":"C",
|
||||||
"staticSize":40,
|
"staticSize":48,
|
||||||
"dynamicSize":12,
|
"dynamicSize":12,
|
||||||
"members":[
|
"members":[
|
||||||
{"name":"int_a", "staticSize":4, "dynamicSize":0},
|
{"name":"int_a", "staticSize":4, "dynamicSize":0},
|
||||||
{"staticSize":8, "dynamicSize":0},
|
{"staticSize":8, "dynamicSize":0},
|
||||||
{"name":"vec_b", "staticSize":24, "dynamicSize":12, "length":3, "capacity":3, "elementStaticSize":4}
|
{"name":"vec_b", "staticSize":24, "dynamicSize":12, "length":3, "capacity":3, "elementStaticSize":4},
|
||||||
|
{"name":"int_c", "staticSize":4, "dynamicSize":0}
|
||||||
]}]'''
|
]}]'''
|
||||||
[cases.c_as_c]
|
[cases.c_as_c]
|
||||||
|
oil_skip = "Polymorphic inheritance disabled in OIL"
|
||||||
|
cli_options = ["--polymorphic-inheritance"]
|
||||||
param_types = ["const C&"]
|
param_types = ["const C&"]
|
||||||
arg_types = ["C"]
|
arg_types = ["C"]
|
||||||
setup = '''
|
setup = '''
|
||||||
|
@ -90,5 +90,7 @@ int main(int argc, char* argv[]) {
|
|||||||
oigen.setConfigFilePath(std::move(configFilePath));
|
oigen.setConfigFilePath(std::move(configFilePath));
|
||||||
oigen.setSourceFileDumpPath(sourceFileDumpPath);
|
oigen.setSourceFileDumpPath(sourceFileDumpPath);
|
||||||
|
|
||||||
return oigen.generate(primaryObject);
|
SymbolService symbols(primaryObject);
|
||||||
|
|
||||||
|
return oigen.generate(primaryObject, symbols);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user