Skip to content

Commit 3105073

Browse files
committed
CodeGen: Add support for capturing Thrift isset data
1 parent 01c9573 commit 3105073

File tree

6 files changed

+213
-82
lines changed

6 files changed

+213
-82
lines changed

oi/CodeGen.cpp

Lines changed: 195 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
using type_graph::Class;
4242
using type_graph::Container;
4343
using type_graph::Enum;
44+
using type_graph::Member;
4445
using type_graph::Type;
4546
using type_graph::Typedef;
4647
using type_graph::TypeGraph;
@@ -165,6 +166,81 @@ void genDecls(const TypeGraph& typeGraph, std::string& code) {
165166
}
166167
}
167168

169+
/*
170+
* Generates a declaration for a given fully-qualified type.
171+
*
172+
* e.g. Given "nsA::nsB::Foo"
173+
*
174+
* The folowing is generated:
175+
* namespace nsA::nsB {
176+
* struct Foo;
177+
* } // namespace nsA::nsB
178+
*/
179+
void declareFullyQualifiedStruct(const std::string& name, std::string& code) {
180+
if (auto pos = name.rfind("::"); pos != name.npos) {
181+
auto ns = name.substr(0, pos);
182+
auto structName = name.substr(pos + 2);
183+
code += "namespace ";
184+
code += ns;
185+
code += " {\n";
186+
code += "struct " + structName + ";\n";
187+
code += "} // namespace ";
188+
code += ns;
189+
code += "\n";
190+
} else {
191+
code += "struct ";
192+
code += name;
193+
code += ";\n";
194+
}
195+
}
196+
197+
void genDefsThriftClass(const Class& c, std::string& code) {
198+
declareFullyQualifiedStruct(c.fqName(), code);
199+
code += "namespace apache { namespace thrift {\n";
200+
code += "template <> struct TStructDataStorage<" + c.fqName() + "> {\n";
201+
code +=
202+
" static constexpr const std::size_t fields_size = 1; // Invalid, do "
203+
"not use\n";
204+
code +=
205+
" static const std::array<folly::StringPiece, fields_size> "
206+
"fields_names;\n";
207+
code += " static const std::array<int16_t, fields_size> fields_ids;\n";
208+
code +=
209+
" static const std::array<protocol::TType, fields_size> fields_types;\n";
210+
code += "\n";
211+
code +=
212+
" static const std::array<folly::StringPiece, fields_size> "
213+
"storage_names;\n";
214+
code +=
215+
" static const std::array<int, fields_size> __attribute__((weak)) "
216+
"isset_indexes;\n";
217+
code += "};\n";
218+
code += "}} // namespace thrift, namespace apache\n";
219+
}
220+
221+
} // namespace
222+
223+
void CodeGen::genDefsThrift(const TypeGraph& typeGraph, std::string& code) {
224+
for (const Type& t : typeGraph.finalTypes) {
225+
if (const auto* c = dynamic_cast<const Class*>(&t)) {
226+
const Member* issetMember = nullptr;
227+
for (const auto& member : c->members) {
228+
if (const auto* container = dynamic_cast<const Container*>(member.type);
229+
container && container->containerInfo_.ctype == THRIFT_ISSET_TYPE) {
230+
issetMember = &member;
231+
break;
232+
}
233+
}
234+
if (issetMember) {
235+
genDefsThriftClass(*c, code);
236+
thriftIssetMembers_[c] = issetMember;
237+
}
238+
}
239+
}
240+
}
241+
242+
namespace {
243+
168244
void genDefsClass(const Class& c, std::string& code) {
169245
if (c.kind() == Class::Kind::Union)
170246
code += "union ";
@@ -293,95 +369,129 @@ void addStandardGetSizeFuncDefs(std::string& code) {
293369
void getClassSizeFuncDecl(const Class& c, std::string& code) {
294370
code += "void getSizeType(const " + c.name() + " &t, size_t &returnArg);\n";
295371
}
372+
} // namespace
296373

297-
void getClassSizeFuncDef(const Class& c,
298-
SymbolService& symbols,
299-
bool polymorphicInheritance,
300-
std::string& code) {
301-
bool enablePolymorphicInheritance = polymorphicInheritance && c.isDynamic();
374+
/*
375+
* Generates a getSizeType function for the given concrete class.
376+
*
377+
* Does not worry about polymorphism.
378+
*/
379+
void CodeGen::getClassSizeFuncConcrete(std::string_view funcName,
380+
const Class& c,
381+
std::string& code) const {
382+
code += "void " + std::string{funcName} + "(const " + c.name() +
383+
" &t, size_t &returnArg) {\n";
302384

303-
std::string funcName = "getSizeType";
304-
if (enablePolymorphicInheritance) {
305-
funcName = "getSizeTypeConcrete";
385+
const Member* thriftIssetMember = nullptr;
386+
if (const auto it = thriftIssetMembers_.find(&c);
387+
it != thriftIssetMembers_.end()) {
388+
thriftIssetMember = it->second;
306389
}
307390

308-
code +=
309-
"void " + funcName + "(const " + c.name() + " &t, size_t &returnArg) {\n";
310-
for (const auto& member : c.members) {
391+
if (thriftIssetMember) {
392+
code += " using thrift_data = apache::thrift::TStructDataStorage<" +
393+
c.fqName() + ">;\n";
394+
}
395+
396+
for (size_t i = 0; i < c.members.size(); i++) {
397+
const auto& member = c.members[i];
311398
if (member.name.starts_with(type_graph::AddPadding::MemberPrefix))
312399
continue;
400+
401+
if (thriftIssetMember && thriftIssetMember != &member) {
402+
// Capture Thrift's isset value for each field, except for __isset
403+
// itself
404+
std::string issetIdxStr =
405+
"thrift_data::isset_indexes[" + std::to_string(i) + "]";
406+
code += " if (&thrift_data::isset_indexes != nullptr && " + issetIdxStr +
407+
" != -1) {\n";
408+
code += " SAVE_DATA(t." + thriftIssetMember->name + ".get(" +
409+
issetIdxStr + "));\n";
410+
code += " } else {\n";
411+
code += " SAVE_DATA(-1);\n";
412+
code += " }\n";
413+
}
414+
313415
code += " JLOG(\"" + member.name + " @\");\n";
314416
code += " JLOGPTR(&t." + member.name + ");\n";
315417
code += " getSizeType(t." + member.name + ", returnArg);\n";
316418
}
317419
code += "}\n";
420+
}
318421

319-
if (enablePolymorphicInheritance) {
320-
std::vector<SymbolInfo> childVtableAddrs;
321-
childVtableAddrs.reserve(c.children.size());
422+
void CodeGen::getClassSizeFuncDef(const Class& c, std::string& code) {
423+
if (!config_.features[Feature::PolymorphicInheritance] || !c.isDynamic()) {
424+
// Just directly use the concrete size function as this class' getSizeType()
425+
getClassSizeFuncConcrete("getSizeType", c, code);
426+
return;
427+
}
322428

323-
for (const Type& childType : c.children) {
324-
auto* childClass = dynamic_cast<const Class*>(&childType);
325-
if (childClass == nullptr) {
326-
abort(); // TODO
327-
}
328-
// TODO:
329-
// auto fqChildName = *fullyQualifiedName(child);
330-
auto fqChildName = "TODO - implement me";
331-
332-
// We must split this assignment and append because the C++ standard lacks
333-
// an operator for concatenating std::string and std::string_view...
334-
std::string childVtableName = "vtable for ";
335-
childVtableName += fqChildName;
336-
337-
auto optVtableSym = symbols.locateSymbol(childVtableName, true);
338-
if (!optVtableSym) {
339-
// LOG(ERROR) << "Failed to find vtable address for '" <<
340-
// childVtableName; LOG(ERROR) << "Falling back to non dynamic
341-
// mode";
342-
childVtableAddrs.clear(); // TODO why??
343-
break;
344-
}
345-
childVtableAddrs.push_back(*optVtableSym);
346-
}
429+
getClassSizeFuncConcrete("getSizeTypeConcrete", c, code);
347430

348-
code +=
349-
"void getSizeType(const " + c.name() + " &t, size_t &returnArg) {\n";
350-
code += " auto *vptr = *reinterpret_cast<uintptr_t * const *>(&t);\n";
351-
code += " uintptr_t topOffset = *(vptr - 2);\n";
352-
code += " uintptr_t vptrVal = reinterpret_cast<uintptr_t>(vptr);\n";
353-
354-
for (size_t i = 0; i < c.children.size(); i++) {
355-
// The vptr will point to *somewhere* in the vtable of this object's
356-
// concrete class. The exact offset into the vtable can vary based on a
357-
// number of factors, so we compare the vptr against the vtable range for
358-
// each possible class to determine the concrete type.
359-
//
360-
// This works for C++ compilers which follow the GNU v3 ABI, i.e. GCC and
361-
// Clang. Other compilers may differ.
362-
const Type& child = c.children[i];
363-
auto& vtableSym = childVtableAddrs[i];
364-
uintptr_t vtableMinAddr = vtableSym.addr;
365-
uintptr_t vtableMaxAddr = vtableSym.addr + vtableSym.size;
366-
code += " if (vptrVal >= 0x" +
367-
(boost::format("%x") % vtableMinAddr).str() + " && vptrVal < 0x" +
368-
(boost::format("%x") % vtableMaxAddr).str() + ") {\n";
369-
code += " SAVE_DATA(" + std::to_string(i) + ");\n";
370-
code +=
371-
" uintptr_t baseAddress = reinterpret_cast<uintptr_t>(&t) + "
372-
"topOffset;\n";
373-
code += " getSizeTypeConcrete(*reinterpret_cast<const " +
374-
child.name() + "*>(baseAddress), returnArg);\n";
375-
code += " return;\n";
376-
code += " }\n";
377-
}
431+
std::vector<SymbolInfo> childVtableAddrs;
432+
childVtableAddrs.reserve(c.children.size());
378433

379-
code += " SAVE_DATA(-1);\n";
380-
code += " getSizeTypeConcrete(t, returnArg);\n";
381-
code += "}\n";
434+
for (const Type& childType : c.children) {
435+
auto* childClass = dynamic_cast<const Class*>(&childType);
436+
if (childClass == nullptr) {
437+
abort(); // TODO
438+
}
439+
// TODO:
440+
// auto fqChildName = *fullyQualifiedName(child);
441+
auto fqChildName = "TODO - implement me";
442+
443+
// We must split this assignment and append because the C++ standard lacks
444+
// an operator for concatenating std::string and std::string_view...
445+
std::string childVtableName = "vtable for ";
446+
childVtableName += fqChildName;
447+
448+
auto optVtableSym = symbols_.locateSymbol(childVtableName, true);
449+
if (!optVtableSym) {
450+
// LOG(ERROR) << "Failed to find vtable address for '" <<
451+
// childVtableName; LOG(ERROR) << "Falling back to non dynamic
452+
// mode";
453+
childVtableAddrs.clear(); // TODO why??
454+
break;
455+
}
456+
childVtableAddrs.push_back(*optVtableSym);
457+
}
458+
459+
code += "void getSizeType(const " + c.name() + " &t, size_t &returnArg) {\n";
460+
code += " auto *vptr = *reinterpret_cast<uintptr_t * const *>(&t);\n";
461+
code += " uintptr_t topOffset = *(vptr - 2);\n";
462+
code += " uintptr_t vptrVal = reinterpret_cast<uintptr_t>(vptr);\n";
463+
464+
for (size_t i = 0; i < c.children.size(); i++) {
465+
// The vptr will point to *somewhere* in the vtable of this object's
466+
// concrete class. The exact offset into the vtable can vary based on a
467+
// number of factors, so we compare the vptr against the vtable range for
468+
// each possible class to determine the concrete type.
469+
//
470+
// This works for C++ compilers which follow the GNU v3 ABI, i.e. GCC and
471+
// Clang. Other compilers may differ.
472+
const Type& child = c.children[i];
473+
auto& vtableSym = childVtableAddrs[i];
474+
uintptr_t vtableMinAddr = vtableSym.addr;
475+
uintptr_t vtableMaxAddr = vtableSym.addr + vtableSym.size;
476+
code += " if (vptrVal >= 0x" +
477+
(boost::format("%x") % vtableMinAddr).str() + " && vptrVal < 0x" +
478+
(boost::format("%x") % vtableMaxAddr).str() + ") {\n";
479+
code += " SAVE_DATA(" + std::to_string(i) + ");\n";
480+
code +=
481+
" uintptr_t baseAddress = reinterpret_cast<uintptr_t>(&t) + "
482+
"topOffset;\n";
483+
code += " getSizeTypeConcrete(*reinterpret_cast<const " + child.name() +
484+
"*>(baseAddress), returnArg);\n";
485+
code += " return;\n";
486+
code += " }\n";
382487
}
488+
489+
code += " SAVE_DATA(-1);\n";
490+
code += " getSizeTypeConcrete(t, returnArg);\n";
491+
code += "}\n";
383492
}
384493

494+
namespace {
385495
void getContainerSizeFuncDecl(const Container& c, std::string& code) {
386496
auto fmt =
387497
boost::format(c.containerInfo_.codegen.decl) % c.containerInfo_.typeName;
@@ -410,21 +520,21 @@ void addGetSizeFuncDecls(const TypeGraph& typeGraph, std::string& code) {
410520
}
411521
}
412522

413-
void addGetSizeFuncDefs(
414-
const TypeGraph& typeGraph,
415-
SymbolService& symbols,
416-
std::unordered_set<const ContainerInfo*>& definedContainers,
417-
bool polymorphicInheritance,
418-
std::string& code) {
523+
} // namespace
524+
525+
void CodeGen::addGetSizeFuncDefs(const TypeGraph& typeGraph,
526+
std::string& code) {
419527
for (const Type& t : typeGraph.finalTypes) {
420528
if (const auto* c = dynamic_cast<const Class*>(&t)) {
421-
getClassSizeFuncDef(*c, symbols, polymorphicInheritance, code);
529+
getClassSizeFuncDef(*c, code);
422530
} else if (const auto* con = dynamic_cast<const Container*>(&t)) {
423-
getContainerSizeFuncDef(definedContainers, *con, code);
531+
getContainerSizeFuncDef(definedContainers_, *con, code);
424532
}
425533
}
426534
}
427535

536+
namespace {
537+
428538
void addStandardTypeHandlers(std::string& code) {
429539
code += R"(
430540
template <typename DB, typename T>
@@ -455,6 +565,7 @@ void addStandardTypeHandlers(std::string& code) {
455565
)";
456566
}
457567

568+
// TODO support thrift isset
458569
void getClassTypeHandler(const Class& c, std::string& code) {
459570
std::string funcName = "getSizeType";
460571

@@ -646,6 +757,10 @@ void CodeGen::generate(
646757
code += "} // namespace\n} // namespace OIInternal\n";
647758
}
648759

760+
if (config_.features[Feature::CaptureThriftIsset]) {
761+
genDefsThrift(typeGraph, code);
762+
}
763+
649764
/*
650765
* The purpose of the anonymous namespace within `OIInternal` is that
651766
* anything defined within an anonymous namespace has internal-linkage,
@@ -676,8 +791,7 @@ void CodeGen::generate(
676791
addGetSizeFuncDecls(typeGraph, code);
677792

678793
addStandardGetSizeFuncDefs(code);
679-
addGetSizeFuncDefs(typeGraph, symbols_, definedContainers_,
680-
config_.features[Feature::PolymorphicInheritance], code);
794+
addGetSizeFuncDefs(typeGraph, code);
681795
}
682796

683797
assert(typeGraph.rootTypes().size() == 1);

oi/CodeGen.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@
1818
#include <filesystem>
1919
#include <functional>
2020
#include <string>
21+
#include <unordered_map>
2122
#include <unordered_set>
23+
#include <vector>
2224

2325
#include "ContainerInfo.h"
2426
#include "OICodeGen.h"
@@ -28,6 +30,8 @@ struct drgn_type;
2830
class SymbolService;
2931

3032
namespace type_graph {
33+
class Class;
34+
struct Member;
3135
class TypeGraph;
3236
} // namespace type_graph
3337

@@ -58,4 +62,14 @@ class CodeGen {
5862
SymbolService& symbols_;
5963
std::vector<ContainerInfo> containerInfos_;
6064
std::unordered_set<const ContainerInfo*> definedContainers_;
65+
std::unordered_map<const type_graph::Class*, const type_graph::Member*>
66+
thriftIssetMembers_;
67+
68+
void genDefsThrift(const type_graph::TypeGraph& typeGraph, std::string& code);
69+
void addGetSizeFuncDefs(const type_graph::TypeGraph& typeGraph,
70+
std::string& code);
71+
void getClassSizeFuncDef(const type_graph::Class& c, std::string& code);
72+
void getClassSizeFuncConcrete(std::string_view funcName,
73+
const type_graph::Class& c,
74+
std::string& code) const;
6175
};

oi/ContainerTypeEnum.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
X(ENUM_MAP_TYPE) \
5656
X(BOOST_BIMAP_TYPE) \
5757
X(STD_VARIANT_TYPE) \
58+
X(THRIFT_ISSET_TYPE) \
5859
X(DUMMY_TYPE) \
5960
X(WEAK_PTR_TYPE)
6061

oi/TreeBuilder.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -835,6 +835,7 @@ void TreeBuilder::processContainer(const Variable& variable, Node& node) {
835835
case BY_MULTI_QRT_TYPE:
836836
containerStats.length = containerStats.capacity = next();
837837
break;
838+
case THRIFT_ISSET_TYPE:
838839
case DUMMY_TYPE:
839840
// Dummy container
840841
containerStats.elementStaticSize = 0;

0 commit comments

Comments
 (0)