mirror of
https://github.com/JakeHillion/object-introspection.git
synced 2024-11-09 21:24:14 +00: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::map<struct drgn_type *, struct drgn_type *> pointerToTypeMap;
|
||||
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/regex.hpp>
|
||||
#include <boost/algorithm/string/replace.hpp>
|
||||
#include <boost/format.hpp>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
@ -43,8 +44,9 @@ static size_t g_level = 0;
|
||||
#define VLOG(verboselevel) \
|
||||
LOG_IF(INFO, VLOG_IS_ON(verboselevel)) << std::string(2 * g_level, ' ')
|
||||
|
||||
std::unique_ptr<OICodeGen> OICodeGen::buildFromConfig(const Config &c) {
|
||||
auto cg = std::unique_ptr<OICodeGen>(new OICodeGen(c));
|
||||
std::unique_ptr<OICodeGen> OICodeGen::buildFromConfig(const Config &c,
|
||||
SymbolService &s) {
|
||||
auto cg = std::unique_ptr<OICodeGen>(new OICodeGen(c, s));
|
||||
|
||||
for (const auto &path : c.containerConfigPaths) {
|
||||
if (!cg->registerContainer(path)) {
|
||||
@ -56,7 +58,8 @@ std::unique_ptr<OICodeGen> OICodeGen::buildFromConfig(const Config &c) {
|
||||
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?
|
||||
auto typesToStub = std::array{
|
||||
"SharedMutex",
|
||||
@ -986,6 +989,106 @@ std::string OICodeGen::typeToName(drgn_type *type) {
|
||||
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
|
||||
// fills out : -
|
||||
// 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)
|
||||
bool OICodeGen::populateDefsAndDecls() {
|
||||
if (!enumerateChildClasses()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (drgn_type_has_type(rootType.type) &&
|
||||
drgn_type_kind(rootType.type) == DRGN_TYPE_FUNCTION) {
|
||||
rootType = drgn_type_type(rootType.type);
|
||||
@ -1085,6 +1192,12 @@ drgn_type *OICodeGen::drgnUnderlyingType(drgn_type *type) {
|
||||
|
||||
bool OICodeGen::enumerateClassParents(drgn_type *type,
|
||||
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);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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) {
|
||||
std::string typeName = getStructName(type);
|
||||
VLOG(2) << "Transformed typename: " << typeName << " " << type;
|
||||
@ -1289,6 +1437,12 @@ bool OICodeGen::enumerateClassType(drgn_type *type) {
|
||||
return true;
|
||||
}
|
||||
} 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)) {
|
||||
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,
|
||||
const std::string &typeName) {
|
||||
if (classMembersMap.find(type) == classMembersMap.end()) {
|
||||
return;
|
||||
}
|
||||
|
||||
code += std::string("void getSizeType(const ") + typeName +
|
||||
std::string("& t, size_t& returnArg) {\n");
|
||||
std::string funcName = "getSizeType";
|
||||
if (isDynamic(type)) {
|
||||
funcName = "getSizeTypeConcrete";
|
||||
}
|
||||
|
||||
code +=
|
||||
"void " + funcName + "(const " + typeName + "& t, size_t& returnArg) {\n";
|
||||
|
||||
std::unordered_map<std::string, int> memberNames;
|
||||
getFuncDefClassMembers(code, type, memberNames);
|
||||
|
||||
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) {
|
||||
@ -3666,6 +3916,7 @@ TypeHierarchy OICodeGen::getTypeHierarchy() {
|
||||
.knownDummyTypeList = knownDummyTypeList,
|
||||
.pointerToTypeMap = pointerToTypeMap,
|
||||
.thriftIssetStructTypes = thriftIssetStructTypes,
|
||||
.descendantClasses = descendantClasses,
|
||||
};
|
||||
}
|
||||
|
||||
@ -3686,13 +3937,17 @@ std::string OICodeGen::Config::toString() const {
|
||||
(packStructs ? "" : "Dont") + "PackStructs,"s +
|
||||
(genPaddingStats ? "" : "Dont") + "GenPaddingStats,"s +
|
||||
(captureThriftIsset ? "" : "Dont") + "CaptureThriftIsset,"s +
|
||||
(polymorphicInheritance ? "" : "Dont") + "PolymorphicInheritance,"s +
|
||||
ignoreMembers;
|
||||
}
|
||||
|
||||
std::vector<std::string> OICodeGen::Config::toOptions() const {
|
||||
return {"", // useDataSegment is always true?
|
||||
(chaseRawPointers ? "-n" : ""), (packStructs ? "" : "-z"),
|
||||
(genPaddingStats ? "" : "-w"), (captureThriftIsset ? "-T" : "")};
|
||||
(chaseRawPointers ? "-n" : ""),
|
||||
(packStructs ? "" : "-z"),
|
||||
(genPaddingStats ? "" : "-w"),
|
||||
(captureThriftIsset ? "-T" : ""),
|
||||
(polymorphicInheritance ? "-P" : "")};
|
||||
}
|
||||
|
||||
void OICodeGen::initializeCodeGen() {
|
||||
|
@ -58,6 +58,7 @@ class OICodeGen {
|
||||
bool packStructs;
|
||||
bool genPaddingStats;
|
||||
bool captureThriftIsset;
|
||||
bool polymorphicInheritance;
|
||||
|
||||
std::set<fs::path> containerConfigPaths{};
|
||||
std::set<std::string> defaultHeaders{};
|
||||
@ -71,10 +72,11 @@ class OICodeGen {
|
||||
private:
|
||||
// Private constructor. Please use the fallible `OICodeGen::buildFromConfig`
|
||||
// for the expected behaviour.
|
||||
OICodeGen(const Config &);
|
||||
OICodeGen(const Config &, SymbolService &);
|
||||
|
||||
public:
|
||||
static std::unique_ptr<OICodeGen> buildFromConfig(const Config &);
|
||||
static std::unique_ptr<OICodeGen> buildFromConfig(const Config &,
|
||||
SymbolService &);
|
||||
bool generate(std::string &code);
|
||||
|
||||
[[deprecated("Use generate(std::string&) instead.")]] bool
|
||||
@ -112,6 +114,7 @@ class OICodeGen {
|
||||
bool enumerateTypesRecurse(drgn_type *type);
|
||||
static std::string_view drgnKindStr(drgn_type *type);
|
||||
std::set<drgn_type *> processedTypes;
|
||||
bool isDynamic(drgn_type *type) const;
|
||||
|
||||
private:
|
||||
Config config{};
|
||||
@ -139,6 +142,10 @@ class OICodeGen {
|
||||
|
||||
std::map<std::string, std::string> typedefMap;
|
||||
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;
|
||||
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 getDrgnTypeNameInt(drgn_type *type, std::string &outName);
|
||||
bool recordChildren(drgn_type *type);
|
||||
bool enumerateChildClasses();
|
||||
bool populateDefsAndDecls();
|
||||
static void memberTransformName(
|
||||
std::map<std::string, std::string> &templateTransformMap,
|
||||
@ -224,6 +233,7 @@ class OICodeGen {
|
||||
const std::vector<size_t> ¶mIdxs,
|
||||
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,
|
||||
const std::string &typeName);
|
||||
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"},
|
||||
OIOpt{'T', "capture-thrift-isset", no_argument, nullptr,
|
||||
"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]",
|
||||
"Allows to specify a mode of operation/group of settings"},
|
||||
};
|
||||
@ -459,6 +461,7 @@ int main(int argc, char *argv[]) {
|
||||
bool packStructs = true;
|
||||
bool dumpDataSegment = false;
|
||||
bool captureThriftIsset = false;
|
||||
bool polymorphicInheritance = false;
|
||||
|
||||
Metrics::Tracing _("main");
|
||||
#ifndef OSS_ENABLE
|
||||
@ -635,6 +638,9 @@ int main(int argc, char *argv[]) {
|
||||
case 'T':
|
||||
captureThriftIsset = true;
|
||||
break;
|
||||
case 'P':
|
||||
polymorphicInheritance = true;
|
||||
break;
|
||||
case 'h':
|
||||
default:
|
||||
usage();
|
||||
@ -679,6 +685,7 @@ int main(int argc, char *argv[]) {
|
||||
.packStructs = packStructs,
|
||||
.genPaddingStats = oidConfig.genPaddingStats,
|
||||
.captureThriftIsset = captureThriftIsset,
|
||||
.polymorphicInheritance = polymorphicInheritance,
|
||||
};
|
||||
|
||||
TreeBuilder::Config tbConfig{
|
||||
|
@ -2942,7 +2942,7 @@ std::optional<std::string> OIDebugger::generateCode(const irequest &req) {
|
||||
#include "OITraceCode.cpp"
|
||||
;
|
||||
|
||||
auto codegen = OICodeGen::buildFromConfig(generatorConfig);
|
||||
auto codegen = OICodeGen::buildFromConfig(generatorConfig, *symbols);
|
||||
if (!codegen) {
|
||||
return nullopt;
|
||||
}
|
||||
|
@ -86,8 +86,9 @@ OIGenerator::findOilTypesAndNames(drgnplusplus::program& prog) {
|
||||
bool OIGenerator::generateForType(const OICodeGen::Config& generatorConfig,
|
||||
const OICompiler::Config& compilerConfig,
|
||||
const drgn_qualified_type& type,
|
||||
const std::string& linkageName) {
|
||||
auto codegen = OICodeGen::buildFromConfig(generatorConfig);
|
||||
const std::string& linkageName,
|
||||
SymbolService& symbols) {
|
||||
auto codegen = OICodeGen::buildFromConfig(generatorConfig, symbols);
|
||||
if (!codegen) {
|
||||
LOG(ERROR) << "failed to initialise codegen";
|
||||
return false;
|
||||
@ -119,7 +120,7 @@ bool OIGenerator::generateForType(const OICodeGen::Config& generatorConfig,
|
||||
return compiler.compile(code, sourcePath, outputPath);
|
||||
}
|
||||
|
||||
int OIGenerator::generate(fs::path& primaryObject) {
|
||||
int OIGenerator::generate(fs::path& primaryObject, SymbolService& symbols) {
|
||||
drgnplusplus::program prog;
|
||||
|
||||
{
|
||||
@ -152,7 +153,8 @@ int OIGenerator::generate(fs::path& primaryObject) {
|
||||
|
||||
size_t failures = 0;
|
||||
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
|
||||
<< "`. this is non-fatal but the call will not work.";
|
||||
failures++;
|
||||
|
@ -28,7 +28,7 @@ namespace ObjectIntrospection {
|
||||
|
||||
class OIGenerator {
|
||||
public:
|
||||
int generate(fs::path& primaryObject);
|
||||
int generate(fs::path& primaryObject, SymbolService& symbols);
|
||||
|
||||
void setOutputPath(fs::path _outputPath) {
|
||||
outputPath = std::move(_outputPath);
|
||||
@ -50,7 +50,7 @@ class OIGenerator {
|
||||
bool generateForType(const OICodeGen::Config& generatorConfig,
|
||||
const OICompiler::Config& compilerConfig,
|
||||
const drgn_qualified_type& type,
|
||||
const std::string& linkageName);
|
||||
const std::string& linkageName, SymbolService& symbols);
|
||||
};
|
||||
|
||||
} // namespace ObjectIntrospection
|
||||
|
@ -185,7 +185,7 @@ int OILibraryImpl::compileCode() {
|
||||
#include "OITraceCode.cpp"
|
||||
;
|
||||
|
||||
auto codegen = OICodeGen::buildFromConfig(generatorConfig);
|
||||
auto codegen = OICodeGen::buildFromConfig(generatorConfig, *symbols);
|
||||
if (!codegen) {
|
||||
return OIL_COMPILATION_FAILURE;
|
||||
}
|
||||
|
@ -374,6 +374,7 @@ void serialize(Archive &ar, struct TypeHierarchy &th,
|
||||
ar &th.knownDummyTypeList;
|
||||
ar &th.pointerToTypeMap;
|
||||
ar &th.thriftIssetStructTypes;
|
||||
ar &th.descendantClasses;
|
||||
}
|
||||
|
||||
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(struct drgn_qualified_type, 16, 2)
|
||||
DEFINE_TYPE_VERSION(RootInfo, 48, 2)
|
||||
DEFINE_TYPE_VERSION(TypeHierarchy, 336, 5)
|
||||
DEFINE_TYPE_VERSION(TypeHierarchy, 384, 6)
|
||||
|
||||
#undef DEFINE_TYPE_VERSION
|
||||
|
||||
|
@ -101,10 +101,11 @@ SymbolService::~SymbolService() {
|
||||
}
|
||||
|
||||
struct ModParams {
|
||||
const char *st;
|
||||
GElf_Sym s;
|
||||
std::string_view symName;
|
||||
GElf_Sym sym;
|
||||
GElf_Addr value;
|
||||
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;
|
||||
GElf_Word shndxp = 0;
|
||||
|
||||
const char *sname = dwfl_module_getsym_info(mod, i, &m->s, &m->value,
|
||||
&shndxp, &elf, nullptr);
|
||||
|
||||
if (sname == nullptr || sname[0] == '\0') {
|
||||
const char *lookupResult = dwfl_module_getsym_info(
|
||||
mod, i, &m->sym, &m->value, &shndxp, &elf, nullptr);
|
||||
if (lookupResult == nullptr || lookupResult[0] == '\0') {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string symName = lookupResult;
|
||||
|
||||
if (m->demangle) {
|
||||
symName = boost::core::demangle(symName.c_str());
|
||||
}
|
||||
|
||||
switch
|
||||
GELF_ST_TYPE(m->s.st_info) {
|
||||
GELF_ST_TYPE(m->sym.st_info) {
|
||||
case STT_SECTION:
|
||||
case STT_FILE:
|
||||
case STT_TLS:
|
||||
@ -162,10 +168,9 @@ static int moduleCallback(Dwfl_Module *mod, void ** /* userData */,
|
||||
break;
|
||||
|
||||
case STT_OBJECT:
|
||||
if (shndxp != SHN_UNDEF && m->st && !strcmp(sname, m->st)) {
|
||||
VLOG(1) << "Symbol lookup successful for " << sname << " in module "
|
||||
<< name;
|
||||
m->st = nullptr;
|
||||
if (shndxp != SHN_UNDEF && symName == m->symName) {
|
||||
VLOG(1) << "Symbol lookup successful for " << symName
|
||||
<< " in module " << name;
|
||||
return DWARF_CB_ABORT;
|
||||
}
|
||||
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
|
||||
* 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)) {
|
||||
m->st = nullptr;
|
||||
|
||||
VLOG(1) << "Symbol lookup successful for " << sname << " in module "
|
||||
<< name;
|
||||
VLOG(1) << "Symbol lookup successful for " << symName
|
||||
<< " in module " << name;
|
||||
|
||||
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
|
||||
*/
|
||||
std::optional<SymbolInfo> SymbolService::locateSymbol(
|
||||
const std::string &symName) {
|
||||
const std::string &symName, bool demangle) {
|
||||
static char *debuginfo_path;
|
||||
static const Dwfl_Callbacks proc_callbacks{
|
||||
.find_elf = dwfl_linux_proc_find_elf,
|
||||
@ -259,15 +262,18 @@ std::optional<SymbolInfo> SymbolService::locateSymbol(
|
||||
LOG(ERROR) << "dwfl_report_end: " << dwfl_errmsg(-1);
|
||||
}
|
||||
|
||||
ModParams m = {
|
||||
.st = symName.c_str(), .s = {}, .value = 0, .exeAddrs = executableAddrs};
|
||||
ModParams m = {.symName = symName,
|
||||
.sym = {},
|
||||
.value = 0,
|
||||
.exeAddrs = executableAddrs,
|
||||
.demangle = demangle};
|
||||
|
||||
dwfl_getmodules(dwfl, moduleCallback, (void *)&m, 0);
|
||||
|
||||
if (m.value == 0) {
|
||||
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) {
|
||||
|
@ -43,7 +43,8 @@ class SymbolService {
|
||||
struct drgn_program *getDrgnProgram();
|
||||
|
||||
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<GlobalDesc> findGlobalDesc(const std::string &);
|
||||
|
@ -444,7 +444,23 @@ TreeBuilder::Node TreeBuilder::process(NodeID id, Variable variable) {
|
||||
} else if (isContainer(variable)) {
|
||||
processContainer(variable, node);
|
||||
} 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()) {
|
||||
break;
|
||||
}
|
||||
@ -455,7 +471,7 @@ TreeBuilder::Node TreeBuilder::process(NodeID id, Variable variable) {
|
||||
auto childID = node.children->first;
|
||||
|
||||
bool captureThriftIsset =
|
||||
th->thriftIssetStructTypes.contains(variable.type);
|
||||
th->thriftIssetStructTypes.contains(objectType);
|
||||
|
||||
for (std::size_t i = 0; i < members.size(); i++) {
|
||||
std::optional<bool> isset;
|
||||
|
@ -20,6 +20,8 @@ definitions = '''
|
||||
'''
|
||||
[cases]
|
||||
[cases.a_as_a]
|
||||
oil_skip = "Polymorphic inheritance disabled in OIL"
|
||||
cli_options = ["--polymorphic-inheritance"]
|
||||
param_types = ["const A&"]
|
||||
arg_types = ["A"]
|
||||
setup = "return {};"
|
||||
@ -33,6 +35,8 @@ definitions = '''
|
||||
]}]'''
|
||||
|
||||
[cases.b_as_a]
|
||||
oil_skip = "Polymorphic inheritance disabled in OIL"
|
||||
cli_options = ["--polymorphic-inheritance"]
|
||||
param_types = ["const A&"]
|
||||
arg_types = ["B"]
|
||||
setup = '''
|
||||
@ -41,14 +45,17 @@ definitions = '''
|
||||
return b;
|
||||
'''
|
||||
expect_json = '''[{
|
||||
"typeName":"A",
|
||||
"staticSize":16,
|
||||
"dynamicSize":0,
|
||||
"typeName":"B",
|
||||
"staticSize":40,
|
||||
"dynamicSize":12,
|
||||
"members":[
|
||||
{"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]
|
||||
oil_skip = "Polymorphic inheritance disabled in OIL"
|
||||
cli_options = ["--polymorphic-inheritance"]
|
||||
param_types = ["const B&"]
|
||||
arg_types = ["B"]
|
||||
setup = '''
|
||||
@ -67,6 +74,8 @@ definitions = '''
|
||||
]}]'''
|
||||
|
||||
[cases.c_as_a]
|
||||
oil_skip = "Polymorphic inheritance disabled in OIL"
|
||||
cli_options = ["--polymorphic-inheritance"]
|
||||
param_types = ["const A&"]
|
||||
arg_types = ["C"]
|
||||
setup = '''
|
||||
@ -75,14 +84,18 @@ definitions = '''
|
||||
return c;
|
||||
'''
|
||||
expect_json = '''[{
|
||||
"typeName":"A",
|
||||
"staticSize":16,
|
||||
"dynamicSize":0,
|
||||
"typeName":"C",
|
||||
"staticSize":48,
|
||||
"dynamicSize":12,
|
||||
"members":[
|
||||
{"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]
|
||||
oil_skip = "Polymorphic inheritance disabled in OIL"
|
||||
cli_options = ["--polymorphic-inheritance"]
|
||||
param_types = ["const B&"]
|
||||
arg_types = ["C"]
|
||||
setup = '''
|
||||
@ -91,15 +104,18 @@ definitions = '''
|
||||
return c;
|
||||
'''
|
||||
expect_json = '''[{
|
||||
"typeName":"B",
|
||||
"staticSize":40,
|
||||
"typeName":"C",
|
||||
"staticSize":48,
|
||||
"dynamicSize":12,
|
||||
"members":[
|
||||
{"staticSize":8, "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]
|
||||
oil_skip = "Polymorphic inheritance disabled in OIL"
|
||||
cli_options = ["--polymorphic-inheritance"]
|
||||
param_types = ["const C&"]
|
||||
arg_types = ["C"]
|
||||
setup = '''
|
||||
|
@ -26,6 +26,8 @@ definitions = '''
|
||||
'''
|
||||
[cases]
|
||||
[cases.root_as_root]
|
||||
oil_skip = "Polymorphic inheritance disabled in OIL"
|
||||
cli_options = ["--polymorphic-inheritance"]
|
||||
param_types = ["const Root&"]
|
||||
arg_types = ["Root"]
|
||||
setup = "return {};"
|
||||
@ -39,6 +41,8 @@ definitions = '''
|
||||
]}]'''
|
||||
|
||||
[cases.middle1_as_root]
|
||||
oil_skip = "Polymorphic inheritance disabled in OIL"
|
||||
cli_options = ["--polymorphic-inheritance"]
|
||||
param_types = ["const Root&"]
|
||||
arg_types = ["Middle1"]
|
||||
setup = '''
|
||||
@ -47,14 +51,17 @@ definitions = '''
|
||||
return m;
|
||||
'''
|
||||
expect_json = '''[{
|
||||
"typeName":"Root",
|
||||
"staticSize":16,
|
||||
"dynamicSize":0,
|
||||
"typeName":"Middle1",
|
||||
"staticSize":40,
|
||||
"dynamicSize":12,
|
||||
"members":[
|
||||
{"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]
|
||||
oil_skip = "Polymorphic inheritance disabled in OIL"
|
||||
cli_options = ["--polymorphic-inheritance"]
|
||||
param_types = ["const Middle1&"]
|
||||
arg_types = ["Middle1"]
|
||||
setup = '''
|
||||
@ -73,6 +80,8 @@ definitions = '''
|
||||
]}]'''
|
||||
|
||||
[cases.middle2_as_root]
|
||||
oil_skip = "Polymorphic inheritance disabled in OIL"
|
||||
cli_options = ["--polymorphic-inheritance"]
|
||||
param_types = ["const Root&"]
|
||||
arg_types = ["Middle2"]
|
||||
setup = '''
|
||||
@ -81,14 +90,17 @@ definitions = '''
|
||||
return m;
|
||||
'''
|
||||
expect_json = '''[{
|
||||
"typeName":"Root",
|
||||
"staticSize":16,
|
||||
"dynamicSize":0,
|
||||
"typeName":"Middle2",
|
||||
"staticSize":40,
|
||||
"dynamicSize":8,
|
||||
"members":[
|
||||
{"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]
|
||||
oil_skip = "Polymorphic inheritance disabled in OIL"
|
||||
cli_options = ["--polymorphic-inheritance"]
|
||||
param_types = ["const Middle2&"]
|
||||
arg_types = ["Middle2"]
|
||||
setup = '''
|
||||
@ -107,6 +119,8 @@ definitions = '''
|
||||
]}]'''
|
||||
|
||||
[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
|
||||
# resolve the diamond problem
|
||||
param_types = ["const Root&"]
|
||||
@ -118,14 +132,21 @@ definitions = '''
|
||||
return static_cast<Middle1&>(*c);
|
||||
'''
|
||||
expect_json = '''[{
|
||||
"typeName":"Root",
|
||||
"staticSize":16,
|
||||
"dynamicSize":0,
|
||||
"typeName":"Child",
|
||||
"staticSize":88,
|
||||
"dynamicSize":20,
|
||||
"members":[
|
||||
{"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]
|
||||
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
|
||||
# resolve the diamond problem
|
||||
param_types = ["const Root&"]
|
||||
@ -137,14 +158,21 @@ definitions = '''
|
||||
return static_cast<Middle2&>(*c);
|
||||
'''
|
||||
expect_json = '''[{
|
||||
"typeName":"Root",
|
||||
"staticSize":16,
|
||||
"dynamicSize":0,
|
||||
"typeName":"Child",
|
||||
"staticSize":88,
|
||||
"dynamicSize":20,
|
||||
"members":[
|
||||
{"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]
|
||||
oil_skip = "Polymorphic inheritance disabled in OIL"
|
||||
cli_options = ["--polymorphic-inheritance"]
|
||||
param_types = ["const Middle1&"]
|
||||
arg_types = ["Child"]
|
||||
setup = '''
|
||||
@ -154,15 +182,21 @@ definitions = '''
|
||||
return c;
|
||||
'''
|
||||
expect_json = '''[{
|
||||
"typeName":"Middle1",
|
||||
"staticSize":40,
|
||||
"dynamicSize":12,
|
||||
"typeName":"Child",
|
||||
"staticSize":88,
|
||||
"dynamicSize":20,
|
||||
"members":[
|
||||
{"staticSize":8, "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]
|
||||
oil_skip = "Polymorphic inheritance disabled in OIL"
|
||||
cli_options = ["--polymorphic-inheritance"]
|
||||
param_types = ["const Middle2&"]
|
||||
arg_types = ["Child"]
|
||||
setup = '''
|
||||
@ -172,15 +206,21 @@ definitions = '''
|
||||
return c;
|
||||
'''
|
||||
expect_json = '''[{
|
||||
"typeName":"Middle2",
|
||||
"staticSize":40,
|
||||
"dynamicSize":8,
|
||||
"typeName":"Child",
|
||||
"staticSize":88,
|
||||
"dynamicSize":20,
|
||||
"members":[
|
||||
{"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":"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]
|
||||
oil_skip = "Polymorphic inheritance disabled in OIL"
|
||||
cli_options = ["--polymorphic-inheritance"]
|
||||
param_types = ["const Child&"]
|
||||
arg_types = ["Child"]
|
||||
setup = '''
|
||||
|
@ -19,6 +19,8 @@ definitions = '''
|
||||
'''
|
||||
[cases]
|
||||
[cases.a_as_a]
|
||||
oil_skip = "Polymorphic inheritance disabled in OIL"
|
||||
cli_options = ["--polymorphic-inheritance"]
|
||||
param_types = ["const A&"]
|
||||
arg_types = ["A"]
|
||||
setup = "return {};"
|
||||
@ -31,6 +33,8 @@ definitions = '''
|
||||
]}]'''
|
||||
|
||||
[cases.b_as_a]
|
||||
oil_skip = "Polymorphic inheritance disabled in OIL"
|
||||
cli_options = ["--polymorphic-inheritance"]
|
||||
param_types = ["const A&"]
|
||||
arg_types = ["B"]
|
||||
setup = '''
|
||||
@ -46,6 +50,8 @@ definitions = '''
|
||||
{"name":"int_a", "staticSize":4, "dynamicSize":0}
|
||||
]}]'''
|
||||
[cases.b_as_b]
|
||||
oil_skip = "Polymorphic inheritance disabled in OIL"
|
||||
cli_options = ["--polymorphic-inheritance"]
|
||||
param_types = ["const B&"]
|
||||
arg_types = ["B"]
|
||||
setup = '''
|
||||
@ -64,6 +70,8 @@ definitions = '''
|
||||
]}]'''
|
||||
|
||||
[cases.c_as_a]
|
||||
oil_skip = "Polymorphic inheritance disabled in OIL"
|
||||
cli_options = ["--polymorphic-inheritance"]
|
||||
param_types = ["const A&"]
|
||||
arg_types = ["C"]
|
||||
setup = '''
|
||||
@ -79,6 +87,8 @@ definitions = '''
|
||||
{"name":"int_a", "staticSize":4, "dynamicSize":0}
|
||||
]}]'''
|
||||
[cases.c_as_b]
|
||||
oil_skip = "Polymorphic inheritance disabled in OIL"
|
||||
cli_options = ["--polymorphic-inheritance"]
|
||||
param_types = ["const B&"]
|
||||
arg_types = ["C"]
|
||||
setup = '''
|
||||
@ -87,15 +97,18 @@ definitions = '''
|
||||
return c;
|
||||
'''
|
||||
expect_json = '''[{
|
||||
"typeName":"B",
|
||||
"staticSize":40,
|
||||
"typeName":"C",
|
||||
"staticSize":48,
|
||||
"dynamicSize":12,
|
||||
"members":[
|
||||
{"name":"int_a", "staticSize":4, "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]
|
||||
oil_skip = "Polymorphic inheritance disabled in OIL"
|
||||
cli_options = ["--polymorphic-inheritance"]
|
||||
param_types = ["const C&"]
|
||||
arg_types = ["C"]
|
||||
setup = '''
|
||||
|
@ -90,5 +90,7 @@ int main(int argc, char* argv[]) {
|
||||
oigen.setConfigFilePath(std::move(configFilePath));
|
||||
oigen.setSourceFileDumpPath(sourceFileDumpPath);
|
||||
|
||||
return oigen.generate(primaryObject);
|
||||
SymbolService symbols(primaryObject);
|
||||
|
||||
return oigen.generate(primaryObject, symbols);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user