TypeGraph: Stop identifying containers in DrgnParser

Leave it to the new mutator pass IdentifyContainers to replace Class
nodes with Container nodes where appropriate.

This will allow us to run passes over the type graph before identifying
containers, and therefore before we have lost information about the
internal details of the container (e.g. alignment of member variables).
This commit is contained in:
Alastair Robertson 2023-11-02 06:18:32 -07:00 committed by Alastair Robertson
parent 98008f9054
commit 2c5fb5d845
9 changed files with 131 additions and 204 deletions

View File

@ -32,6 +32,7 @@
#include "type_graph/DrgnParser.h" #include "type_graph/DrgnParser.h"
#include "type_graph/EnforceCompatibility.h" #include "type_graph/EnforceCompatibility.h"
#include "type_graph/Flattener.h" #include "type_graph/Flattener.h"
#include "type_graph/IdentifyContainers.h"
#include "type_graph/KeyCapture.h" #include "type_graph/KeyCapture.h"
#include "type_graph/NameGen.h" #include "type_graph/NameGen.h"
#include "type_graph/Prune.h" #include "type_graph/Prune.h"
@ -55,6 +56,7 @@ using type_graph::DrgnParserOptions;
using type_graph::EnforceCompatibility; using type_graph::EnforceCompatibility;
using type_graph::Enum; using type_graph::Enum;
using type_graph::Flattener; using type_graph::Flattener;
using type_graph::IdentifyContainers;
using type_graph::KeyCapture; using type_graph::KeyCapture;
using type_graph::Member; using type_graph::Member;
using type_graph::NameGen; using type_graph::NameGen;
@ -1137,21 +1139,25 @@ bool CodeGen::codegenFromDrgn(struct drgn_type* drgnType, std::string& code) {
return true; return true;
} }
void CodeGen::registerContainer(std::unique_ptr<ContainerInfo> info) {
VLOG(1) << "Registered container: " << info->typeName;
containerInfos_.emplace_back(std::move(info));
}
void CodeGen::registerContainer(const fs::path& path) { void CodeGen::registerContainer(const fs::path& path) {
auto info = std::make_unique<ContainerInfo>(path); auto info = std::make_unique<ContainerInfo>(path);
if (info->requiredFeatures != (config_.features & info->requiredFeatures)) { if (info->requiredFeatures != (config_.features & info->requiredFeatures)) {
VLOG(1) << "Skipping container (feature conflict): " << info->typeName; VLOG(1) << "Skipping container (feature conflict): " << info->typeName;
return; return;
} }
VLOG(1) << "Registered container: " << info->typeName; registerContainer(std::move(info));
containerInfos_.emplace_back(std::move(info));
} }
void CodeGen::addDrgnRoot(struct drgn_type* drgnType, TypeGraph& typeGraph) { void CodeGen::addDrgnRoot(struct drgn_type* drgnType, TypeGraph& typeGraph) {
DrgnParserOptions options{ DrgnParserOptions options{
.chaseRawPointers = config_.features[Feature::ChaseRawPointers], .chaseRawPointers = config_.features[Feature::ChaseRawPointers],
}; };
DrgnParser drgnParser{typeGraph, containerInfos_, options}; DrgnParser drgnParser{typeGraph, options};
Type& parsedRoot = drgnParser.parse(drgnType); Type& parsedRoot = drgnParser.parse(drgnType);
typeGraph.addRoot(parsedRoot); typeGraph.addRoot(parsedRoot);
} }
@ -1162,6 +1168,7 @@ void CodeGen::transform(TypeGraph& typeGraph) {
// Simplify the type graph first so there is less work for later passes // Simplify the type graph first so there is less work for later passes
pm.addPass(RemoveTopLevelPointer::createPass()); pm.addPass(RemoveTopLevelPointer::createPass());
pm.addPass(Flattener::createPass()); pm.addPass(Flattener::createPass());
pm.addPass(IdentifyContainers::createPass(containerInfos_));
pm.addPass(TypeIdentifier::createPass(config_.passThroughTypes)); pm.addPass(TypeIdentifier::createPass(config_.passThroughTypes));
if (config_.features[Feature::PruneTypeGraph]) if (config_.features[Feature::PruneTypeGraph])
pm.addPass(Prune::createPass()); pm.addPass(Prune::createPass());
@ -1171,11 +1178,12 @@ void CodeGen::transform(TypeGraph& typeGraph) {
DrgnParserOptions options{ DrgnParserOptions options{
.chaseRawPointers = config_.features[Feature::ChaseRawPointers], .chaseRawPointers = config_.features[Feature::ChaseRawPointers],
}; };
DrgnParser drgnParser{typeGraph, containerInfos_, options}; DrgnParser drgnParser{typeGraph, options};
pm.addPass(AddChildren::createPass(drgnParser, symbols_)); pm.addPass(AddChildren::createPass(drgnParser, symbols_));
// Re-run passes over newly added children // Re-run passes over newly added children
pm.addPass(Flattener::createPass()); pm.addPass(Flattener::createPass());
pm.addPass(IdentifyContainers::createPass(containerInfos_));
pm.addPass(TypeIdentifier::createPass(config_.passThroughTypes)); pm.addPass(TypeIdentifier::createPass(config_.passThroughTypes));
if (config_.features[Feature::PruneTypeGraph]) if (config_.features[Feature::PruneTypeGraph])
pm.addPass(Prune::createPass()); pm.addPass(Prune::createPass());

View File

@ -53,6 +53,7 @@ class CodeGen {
std::string linkageName, std::string linkageName,
std::string& code); std::string& code);
void registerContainer(std::unique_ptr<ContainerInfo> containerInfo);
void registerContainer(const std::filesystem::path& path); void registerContainer(const std::filesystem::path& path);
void addDrgnRoot(struct drgn_type* drgnType, void addDrgnRoot(struct drgn_type* drgnType,
type_graph::TypeGraph& typeGraph); type_graph::TypeGraph& typeGraph);

View File

@ -25,8 +25,6 @@ extern "C" {
#include <drgn.h> #include <drgn.h>
} }
#include <regex>
namespace oi::detail::type_graph { namespace oi::detail::type_graph {
namespace { namespace {
@ -148,31 +146,7 @@ Type& DrgnParser::enumerateType(struct drgn_type* type) {
return *t; return *t;
} }
/* Class& DrgnParser::enumerateClass(struct drgn_type* type) {
* enumerateContainer
*
* Attempts to parse a drgn_type as a Container. Returns nullptr if not
* sucessful.
*/
Container* DrgnParser::enumerateContainer(struct drgn_type* type,
const std::string& fqName) {
auto size = get_drgn_type_size(type);
for (const auto& containerInfo : containers_) {
if (!std::regex_search(fqName, containerInfo->matcher)) {
continue;
}
VLOG(2) << "Matching container `" << containerInfo->typeName << "` from `"
<< fqName << "`" << std::endl;
auto& c = makeType<Container>(type, *containerInfo, size);
enumerateClassTemplateParams(type, c.templateParams);
return &c;
}
return nullptr;
}
Type& DrgnParser::enumerateClass(struct drgn_type* type) {
std::string fqName; std::string fqName;
char* nameStr = nullptr; char* nameStr = nullptr;
size_t length = 0; size_t length = 0;
@ -181,10 +155,6 @@ Type& DrgnParser::enumerateClass(struct drgn_type* type) {
fqName = nameStr; fqName = nameStr;
} }
auto* container = enumerateContainer(type, fqName);
if (container)
return *container;
const char* typeTag = drgn_type_tag(type); const char* typeTag = drgn_type_tag(type);
std::string name = typeTag ? typeTag : ""; std::string name = typeTag ? typeTag : "";

View File

@ -46,25 +46,19 @@ struct DrgnParserOptions {
* - Performance: reading no more info from drgn than necessary * - Performance: reading no more info from drgn than necessary
* *
* For the most part we try to move logic out of DrgnParser and have later * For the most part we try to move logic out of DrgnParser and have later
* passes clean up the type graph, e.g. flattening parents. However, we do * passes clean up the type graph, e.g. flattening parents and identifying
* incorporate some extra logic here when it would allow us to read less DWARF * containers.
* information, e.g. matching containers in DrngParser means we don't read
* details about container internals.
*/ */
class DrgnParser { class DrgnParser {
public: public:
DrgnParser(TypeGraph& typeGraph, DrgnParser(TypeGraph& typeGraph, DrgnParserOptions options)
const std::vector<std::unique_ptr<ContainerInfo>>& containers, : typeGraph_(typeGraph), options_(options) {
DrgnParserOptions options)
: typeGraph_(typeGraph), containers_(containers), options_(options) {
} }
Type& parse(struct drgn_type* root); Type& parse(struct drgn_type* root);
private: private:
Type& enumerateType(struct drgn_type* type); Type& enumerateType(struct drgn_type* type);
Container* enumerateContainer(struct drgn_type* type, Class& enumerateClass(struct drgn_type* type);
const std::string& fqName);
Type& enumerateClass(struct drgn_type* type);
Enum& enumerateEnum(struct drgn_type* type); Enum& enumerateEnum(struct drgn_type* type);
Typedef& enumerateTypedef(struct drgn_type* type); Typedef& enumerateTypedef(struct drgn_type* type);
Type& enumeratePointer(struct drgn_type* type); Type& enumeratePointer(struct drgn_type* type);
@ -98,7 +92,6 @@ class DrgnParser {
drgn_types_; drgn_types_;
TypeGraph& typeGraph_; TypeGraph& typeGraph_;
const std::vector<std::unique_ptr<ContainerInfo>>& containers_;
int depth_; int depth_;
DrgnParserOptions options_; DrgnParserOptions options_;
}; };

View File

@ -86,6 +86,7 @@ void TypeIdentifier::visit(Container& c) {
} }
c.templateParams[i] = *dummy; c.templateParams[i] = *dummy;
replaced = true; replaced = true;
break;
} }
} }

View File

@ -66,7 +66,7 @@ TEST_F(AddChildrenTest, InheritanceStatic) {
} }
TEST_F(AddChildrenTest, InheritancePolymorphic) { TEST_F(AddChildrenTest, InheritancePolymorphic) {
testMultiCompiler("oid_test_case_inheritance_polymorphic_a_as_a", R"( testMultiCompilerGlob("oid_test_case_inheritance_polymorphic_a_as_a", R"(
[1] Pointer [1] Pointer
[0] Class: A (size: 16) [0] Class: A (size: 16)
Member: _vptr$A (offset: 0) Member: _vptr$A (offset: 0)
@ -78,11 +78,11 @@ TEST_F(AddChildrenTest, InheritancePolymorphic) {
Function: A Function: A
Function: A Function: A
Child Child
[8] Class: B (size: 40) [17] Class: B (size: 40)
Parent (offset: 0) Parent (offset: 0)
[0] [0]
Member: vec_b (offset: 16) Member: vec_b (offset: 16)
[4] Container: std::vector (size: 24) [4] Class: vector<int, std::allocator<int> > (size: 24)
Param Param
Primitive: int32_t Primitive: int32_t
Param Param
@ -105,14 +105,15 @@ TEST_F(AddChildrenTest, InheritancePolymorphic) {
Function: ~allocator Function: ~allocator
Function: allocate Function: allocate
Function: deallocate Function: deallocate
*
Function: ~B (virtual) Function: ~B (virtual)
Function: myfunc (virtual) Function: myfunc (virtual)
Function: B Function: B
Function: B Function: B
Child Child
[10] Class: C (size: 48) [19] Class: C (size: 48)
Parent (offset: 0) Parent (offset: 0)
[8] [17]
Member: int_c (offset: 40) Member: int_c (offset: 40)
Primitive: int32_t Primitive: int32_t
Function: ~C (virtual) Function: ~C (virtual)
@ -133,11 +134,11 @@ TEST_F(AddChildrenTest, InheritancePolymorphic) {
Function: ~A (virtual) Function: ~A (virtual)
Function: myfunc (virtual) Function: myfunc (virtual)
Child Child
[7] Class: B (size: 40) [13] Class: B (size: 40)
Parent (offset: 0) Parent (offset: 0)
[0] [0]
Member: vec_b (offset: 16) Member: vec_b (offset: 16)
[4] Container: std::vector (size: 24) [4] Class: vector<int, std::allocator<int> > (size: 24)
Param Param
Primitive: int32_t Primitive: int32_t
Param Param
@ -157,15 +158,16 @@ TEST_F(AddChildrenTest, InheritancePolymorphic) {
Function: ~allocator Function: ~allocator
Function: allocate Function: allocate
Function: deallocate Function: deallocate
*
Function: operator= Function: operator=
Function: B Function: B
Function: B Function: B
Function: ~B (virtual) Function: ~B (virtual)
Function: myfunc (virtual) Function: myfunc (virtual)
Child Child
[9] Class: C (size: 48) [15] Class: C (size: 48)
Parent (offset: 0) Parent (offset: 0)
[7] [13]
Member: int_c (offset: 40) Member: int_c (offset: 40)
Primitive: int32_t Primitive: int32_t
Function: operator= Function: operator=

View File

@ -35,6 +35,9 @@ void testTransform(OICodeGen::Config& config,
MockSymbolService symbols; MockSymbolService symbols;
CodeGen codegen{config, symbols}; CodeGen codegen{config, symbols};
for (auto& info : getContainerInfos()) {
codegen.registerContainer(std::move(info));
}
codegen.transform(typeGraph); codegen.transform(typeGraph);
check(typeGraph, expectedAfter, "after transform"); check(typeGraph, expectedAfter, "after transform");
@ -48,7 +51,7 @@ void testTransform(std::string_view input, std::string_view expectedAfter) {
TEST(CodeGenTest, TransformContainerAllocator) { TEST(CodeGenTest, TransformContainerAllocator) {
testTransform(R"( testTransform(R"(
[0] Container: std::vector (size: 24) [0] Class: std::vector (size: 24)
Param Param
Primitive: int32_t Primitive: int32_t
Param Param
@ -59,18 +62,18 @@ TEST(CodeGenTest, TransformContainerAllocator) {
Function: deallocate Function: deallocate
)", )",
R"( R"(
[0] Container: std::vector<int32_t, DummyAllocator<int32_t, 8, 0, 2>> (size: 24) [2] Container: std::vector<int32_t, DummyAllocator<int32_t, 8, 0, 3>> (size: 24)
Param Param
Primitive: int32_t Primitive: int32_t
Param Param
[2] DummyAllocator [MyAlloc] (size: 8) [3] DummyAllocator [MyAlloc] (size: 8)
Primitive: int32_t Primitive: int32_t
)"); )");
} }
TEST(CodeGenTest, TransformContainerAllocatorParamInParent) { TEST(CodeGenTest, TransformContainerAllocatorParamInParent) {
testTransform(R"( testTransform(R"(
[0] Container: std::map (size: 24) [0] Class: std::map (size: 24)
Param Param
Primitive: int32_t Primitive: int32_t
Param Param
@ -80,7 +83,7 @@ TEST(CodeGenTest, TransformContainerAllocatorParamInParent) {
Parent (offset: 0) Parent (offset: 0)
[2] Struct: MyAllocBase<std::pair<const int, int>> (size: 1) [2] Struct: MyAllocBase<std::pair<const int, int>> (size: 1)
Param Param
[3] Container: std::pair (size: 8) [3] Class: std::pair (size: 8)
Param Param
Primitive: int32_t Primitive: int32_t
Qualifiers: const Qualifiers: const
@ -92,14 +95,14 @@ TEST(CodeGenTest, TransformContainerAllocatorParamInParent) {
Function: deallocate Function: deallocate
)", )",
R"( R"(
[0] Container: std::map<int32_t, int32_t, DummyAllocator<std::pair<int32_t const, int32_t>, 0, 0, 4>> (size: 24) [4] Container: std::map<int32_t, int32_t, DummyAllocator<std::pair<int32_t const, int32_t>, 0, 0, 6>> (size: 24)
Param Param
Primitive: int32_t Primitive: int32_t
Param Param
Primitive: int32_t Primitive: int32_t
Param Param
[4] DummyAllocator [MyAlloc<std::pair<const int, int>>] (size: 0) [6] DummyAllocator [MyAlloc<std::pair<const int, int>>] (size: 0)
[3] Container: std::pair<int32_t const, int32_t> (size: 8) [5] Container: std::pair<int32_t const, int32_t> (size: 8)
Param Param
Primitive: int32_t Primitive: int32_t
Qualifiers: const Qualifiers: const
@ -150,3 +153,25 @@ TEST(CodeGenTest, UnionMembersAlignment) {
Primitive: int8_t Primitive: int8_t
)"); )");
} }
TEST(CodeGenTest, ReplaceContainersAndDummies) {
testTransform(R"(
[0] Class: std::vector (size: 24)
Param
Primitive: uint32_t
Param
[1] Class: allocator<int> (size: 1)
Param
Primitive: uint32_t
Function: allocate
Function: deallocate
)",
R"(
[2] Container: std::vector<uint32_t, DummyAllocator<uint32_t, 0, 0, 3>> (size: 24)
Param
Primitive: uint32_t
Param
[3] DummyAllocator [allocator<int>] (size: 0)
Primitive: uint32_t
)");
}

View File

@ -1,16 +1,15 @@
#include "test_drgn_parser.h"
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include <regex> #include <regex>
#include "oi/SymbolService.h"
// TODO needed?:
#include "oi/ContainerInfo.h"
#include "oi/OIParser.h" #include "oi/OIParser.h"
#include "oi/SymbolService.h"
#include "oi/type_graph/NodeTracker.h" #include "oi/type_graph/NodeTracker.h"
#include "oi/type_graph/Printer.h" #include "oi/type_graph/Printer.h"
#include "oi/type_graph/TypeGraph.h" #include "oi/type_graph/TypeGraph.h"
#include "oi/type_graph/Types.h" #include "oi/type_graph/Types.h"
#include "test_drgn_parser.h"
using namespace type_graph; using namespace type_graph;
@ -19,25 +18,17 @@ using namespace type_graph;
SymbolService* DrgnParserTest::symbols_ = nullptr; SymbolService* DrgnParserTest::symbols_ = nullptr;
namespace { void DrgnParserTest::SetUpTestSuite() {
const std::vector<std::unique_ptr<ContainerInfo>>& getContainerInfos() { symbols_ = new SymbolService{TARGET_EXE_PATH};
static auto res = []() { }
// TODO more container types, with various template parameter options
auto std_vector = void DrgnParserTest::TearDownTestSuite() {
std::make_unique<ContainerInfo>("std::vector", SEQ_TYPE, "vector"); delete symbols_;
std_vector->stubTemplateParams = {1};
std::vector<std::unique_ptr<ContainerInfo>> containers;
containers.emplace_back(std::move(std_vector));
return containers;
}();
return res;
} }
} // namespace
DrgnParser DrgnParserTest::getDrgnParser(TypeGraph& typeGraph, DrgnParser DrgnParserTest::getDrgnParser(TypeGraph& typeGraph,
DrgnParserOptions options) { DrgnParserOptions options) {
DrgnParser drgnParser{typeGraph, getContainerInfos(), options}; DrgnParser drgnParser{typeGraph, options};
return drgnParser; return drgnParser;
} }
@ -148,8 +139,9 @@ void DrgnParserTest::testGlob(std::string_view function,
expected.remove_prefix(1); // Remove initial '\n' expected.remove_prefix(1); // Remove initial '\n'
auto [expectedIdx, actualIdx] = globMatch(expected, actual); auto [expectedIdx, actualIdx] = globMatch(expected, actual);
if (expected.size() != expectedIdx) { if (expected.size() != expectedIdx) {
ADD_FAILURE() << prefixLines(expected.substr(expectedIdx), "-", 50) << "\n" ADD_FAILURE() << prefixLines(expected.substr(expectedIdx), "-", 10000)
<< prefixLines(actual.substr(actualIdx), "+", 50); << "\n"
<< prefixLines(actual.substr(actualIdx), "+", 10000);
} }
} }
@ -261,9 +253,9 @@ TEST_F(DrgnParserTest, InheritanceMultiple) {
} }
TEST_F(DrgnParserTest, Container) { TEST_F(DrgnParserTest, Container) {
testMultiCompiler("oid_test_case_std_vector_int_empty", R"( testMultiCompilerGlob("oid_test_case_std_vector_int_empty", R"(
[4] Pointer [13] Pointer
[0] Container: std::vector (size: 24) [0] Class: vector<int, std::allocator<int> > (size: 24)
Param Param
Primitive: int32_t Primitive: int32_t
Param Param
@ -286,10 +278,12 @@ TEST_F(DrgnParserTest, Container) {
Function: ~allocator Function: ~allocator
Function: allocate Function: allocate
Function: deallocate Function: deallocate
Parent (offset: 0)
*
)", )",
R"( R"(
[3] Pointer [9] Pointer
[0] Container: std::vector (size: 24) [0] Class: vector<int, std::allocator<int> > (size: 24)
Param Param
Primitive: int32_t Primitive: int32_t
Param Param
@ -309,6 +303,8 @@ TEST_F(DrgnParserTest, Container) {
Function: ~allocator Function: ~allocator
Function: allocate Function: allocate
Function: deallocate Function: deallocate
Parent (offset: 0)
*
)"); )");
} }
// TODO test vector with custom allocator // TODO test vector with custom allocator
@ -455,70 +451,6 @@ TEST_F(DrgnParserTest, ClassTemplateInt) {
)"); )");
} }
TEST_F(DrgnParserTest, ClassTemplateVector) {
testMultiCompiler("oid_test_case_templates_vector", R"(
[5] Pointer
[0] Class: TemplatedClass1<std::vector<int, std::allocator<int> > > (size: 24)
Param
[1] Container: std::vector (size: 24)
Param
Primitive: int32_t
Param
[2] Class: allocator<int> (size: 1)
Param
Primitive: int32_t
Parent (offset: 0)
[4] Typedef: __allocator_base<int>
[3] Class: new_allocator<int> (size: 1)
Param
Primitive: int32_t
Function: new_allocator
Function: new_allocator
Function: allocate
Function: deallocate
Function: _M_max_size
Function: allocator
Function: allocator
Function: operator=
Function: ~allocator
Function: allocate
Function: deallocate
Member: val (offset: 0)
[1]
Function: ~TemplatedClass1
Function: TemplatedClass1
)",
R"(
[4] Pointer
[0] Class: TemplatedClass1<std::vector<int, std::allocator<int> > > (size: 24)
Param
[1] Container: std::vector (size: 24)
Param
Primitive: int32_t
Param
[2] Class: allocator<int> (size: 1)
Parent (offset: 0)
[3] Class: new_allocator<int> (size: 1)
Param
Primitive: int32_t
Function: new_allocator
Function: new_allocator
Function: allocate
Function: deallocate
Function: _M_max_size
Function: allocator
Function: allocator
Function: operator=
Function: ~allocator
Function: allocate
Function: deallocate
Member: val (offset: 0)
[1]
Function: TemplatedClass1
Function: ~TemplatedClass1
)");
}
TEST_F(DrgnParserTest, ClassTemplateTwo) { TEST_F(DrgnParserTest, ClassTemplateTwo) {
test("oid_test_case_templates_two", R"( test("oid_test_case_templates_two", R"(
[3] Pointer [3] Pointer

View File

@ -16,13 +16,8 @@ using namespace oi::detail;
class DrgnParserTest : public ::testing::Test { class DrgnParserTest : public ::testing::Test {
protected: protected:
static void SetUpTestSuite() { static void SetUpTestSuite();
symbols_ = new SymbolService{TARGET_EXE_PATH}; static void TearDownTestSuite();
}
static void TearDownTestSuite() {
delete symbols_;
}
static type_graph::DrgnParser getDrgnParser( static type_graph::DrgnParser getDrgnParser(
type_graph::TypeGraph& typeGraph, type_graph::DrgnParserOptions options); type_graph::TypeGraph& typeGraph, type_graph::DrgnParserOptions options);