Support dynamic polymorphic inheritance

i.e. classes which contain virtual functions and use a virtual pointer
This commit is contained in:
Alastair Robertson 2023-01-25 06:37:16 -08:00 committed by Alastair Robertson
parent aef8826a3a
commit 9e72ada131
17 changed files with 451 additions and 80 deletions

View File

@ -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;
};

View File

@ -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() {

View File

@ -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> &paramIdxs,
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);

View File

@ -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{

View File

@ -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;
}

View File

@ -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++;

View File

@ -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

View File

@ -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;
}

View File

@ -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)

View File

@ -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

View File

@ -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) {

View File

@ -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 &);

View File

@ -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;

View File

@ -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 = '''

View File

@ -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 = '''

View File

@ -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 = '''

View File

@ -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);
}