Have driver_ast_parser report the size of structs and types.

PiperOrigin-RevId: 660453033
This commit is contained in:
Anthony Cui
2024-08-07 10:55:09 -07:00
committed by gVisor bot
parent b1ade52f24
commit d7d18541d3
5 changed files with 65 additions and 29 deletions
+25 -13
View File
@@ -24,6 +24,7 @@
#include "absl/container/flat_hash_set.h"
#include "absl/strings/str_cat.h"
#include "nlohmann/json.hpp"
#include "clang/include/clang/AST/ASTContext.h"
#include "clang/include/clang/AST/Decl.h"
#include "clang/include/clang/AST/Type.h"
#include "clang/include/clang/ASTMatchers/ASTMatchFinder.h"
@@ -89,7 +90,7 @@ struct DriverStructReporter : public MatchFinder::MatchCallback {
}
void run(const MatchFinder::MatchResult &result) override {
const auto &sm = result.Context->getSourceManager();
const auto *ctx = result.Context;
const auto *typedef_decl =
result.Nodes.getNodeAs<clang::TypedefDecl>("typedef_decl");
@@ -104,22 +105,28 @@ struct DriverStructReporter : public MatchFinder::MatchCallback {
const auto *struct_decl =
result.Nodes.getNodeAs<clang::RecordDecl>("struct_decl");
if (struct_decl == nullptr) {
// Add the typedef to TypeAliases, and recurse on the underlying type.
// Generate the definition for the underlying type, then copy it for
// this struct.
const auto type = typedef_decl->getUnderlyingType();
const auto type_name = type.getAsString();
TypeAliases[name] = type_name;
add_type_definition(type, type_name, sm);
add_type_definition(type, type_name, ctx);
if (type->isRecordType()) {
RecordDefinitions[name] = RecordDefinitions[type_name];
} else {
TypeAliases[name] = TypeAliases[type_name];
}
return;
}
add_type_definition(result.Context->getTypeDeclType(struct_decl), name, sm);
add_type_definition(ctx->getTypeDeclType(struct_decl), name, ctx);
}
// Adds the type definition of `type` to either `RecordDefinitions` or
// `TypeAliases`, mapped to `name`. Recursively adds the type definitions
// of any nested types.
void add_type_definition(const clang::QualType &type, const std::string &name,
const clang::SourceManager &sm) {
const clang::ASTContext *ctx) {
// We've already handled this type.
if (ParsedTypes.contains(name)) {
return;
@@ -132,9 +139,12 @@ struct DriverStructReporter : public MatchFinder::MatchCallback {
if (canonical_type->isRecordType()) {
const auto record_decl = canonical_type->getAsRecordDecl();
add_record_definition(record_decl, name, sm);
add_record_definition(record_decl, name, ctx);
} else {
TypeAliases[name] = canonical_type.getAsString();
// getTypeSize returns the size in bits, so we divide by 8 to get bytes.
uint64_t size = ctx->getTypeSize(canonical_type) / 8;
TypeAliases[name] = json::object(
{{"size", size}, {"type", canonical_type.getAsString()}});
}
}
@@ -142,7 +152,7 @@ struct DriverStructReporter : public MatchFinder::MatchCallback {
// to `name`. Recursively adds the type definitions of any nested types.
void add_record_definition(const clang::RecordDecl *record_decl,
const std::string &name,
const clang::SourceManager &sm) {
const clang::ASTContext *ctx) {
json fields;
for (const auto *field : record_decl->fields()) {
auto field_type = field->getType();
@@ -182,13 +192,15 @@ struct DriverStructReporter : public MatchFinder::MatchCallback {
{{"name", field->getNameAsString()}, {"type", field_type_name}}));
// Recurse on the field type.
add_type_definition(field_type, base_type_name, sm);
add_type_definition(field_type, base_type_name, ctx);
}
std::string source = record_decl->getLocation().printToString(sm);
std::string source =
record_decl->getLocation().printToString(ctx->getSourceManager());
// getTypeSize returns the size in bits, so we divide by 8 to get bytes.
uint64_t size = ctx->getTypeSize(record_decl->getTypeForDecl()) / 8;
RecordDefinitions[name] =
json::object({{"source", source}, {"fields", fields}});
json::object({{"source", source}, {"fields", fields}, {"size", size}});
}
};
@@ -56,7 +56,7 @@ func TestParser(t *testing.T) {
}()
input := parser.InputJSON{
Structs: []string{"TestStruct"},
Structs: []string{"TestStruct", "TestStruct2"},
}
if err := json.NewEncoder(structsFile).Encode(&input); err != nil {
t.Fatalf("failed to write input structs file: %v", err)
@@ -84,6 +84,17 @@ func TestParser(t *testing.T) {
parser.RecordField{Name: "e", Type: "TestStruct::e_t[4]"},
parser.RecordField{Name: "f", Type: "TestUnion"},
},
Size: 44,
Source: "test_struct.cc:25:16",
},
"TestStruct2": parser.RecordDef{
Fields: []parser.RecordField{
parser.RecordField{Name: "a", Type: "int"},
parser.RecordField{Name: "b", Type: "int"},
parser.RecordField{Name: "e", Type: "TestStruct::e_t[4]"},
parser.RecordField{Name: "f", Type: "TestUnion"},
},
Size: 44,
Source: "test_struct.cc:25:16",
},
"TestStruct::e_t": parser.RecordDef{
@@ -91,6 +102,7 @@ func TestParser(t *testing.T) {
parser.RecordField{Name: "c", Type: "OtherInt"},
parser.RecordField{Name: "d", Type: "OtherInt"},
},
Size: 8,
Source: "test_struct.cc:28:3",
},
"TestUnion": parser.RecordDef{
@@ -98,12 +110,13 @@ func TestParser(t *testing.T) {
parser.RecordField{Name: "u_a", Type: "int"},
parser.RecordField{Name: "u_b", Type: "int"},
},
Size: 4,
Source: "test_struct.cc:20:9",
},
},
Aliases: parser.TypeAliases{
"OtherInt": "int",
"int": "int",
"OtherInt": parser.TypeDef{Type: "int", Size: 4},
"int": parser.TypeDef{Type: "int", Size: 4},
},
}
@@ -61,29 +61,38 @@ func (s RecordField) String() string {
// RecordDef represents the definition of a record (struct or union).
type RecordDef struct {
Fields []RecordField
Size uint64
Source string
}
// Equals returns true if the two record definitions are equal. We only
// compare the fields, not the source.
// Equals returns true if the two record definitions are equal. We ignore the source of the records.
func (s RecordDef) Equals(other RecordDef) bool {
return slices.Equal(s.Fields, other.Fields)
return s.Size == other.Size && slices.Equal(s.Fields, other.Fields)
}
// TypeDef represents the definition of a type.
type TypeDef struct {
Type string
Size uint64
}
// RecordDefs is a map of type names to definitions.
type RecordDefs map[string]RecordDef
// TypeAliases is a map of type aliases to their underlying type.
type TypeAliases map[string]string
type TypeAliases map[string]TypeDef
// GetRecordDiff prints a diff between two records.
func GetRecordDiff(name nvproxy.DriverStructName, s1, s2 RecordDef) string {
var b strings.Builder
fmt.Fprintf(&b, "--- A: %s\n", s1.Source)
fmt.Fprintf(&b, "+++ B: %s\n", s2.Source)
func GetRecordDiff(name nvproxy.DriverStructName, a, b RecordDef) string {
var sb strings.Builder
fmt.Fprintf(&sb, "--- A: %s\n", a.Source)
fmt.Fprintf(&sb, "+++ B: %s\n", b.Source)
fmt.Fprintf(&b, "struct %s\n", name)
fmt.Fprint(&b, cmp.Diff(s1.Fields, s2.Fields))
fmt.Fprintf(&sb, "struct %s\n", name)
if a.Size != b.Size {
fmt.Fprintf(&sb, " size: %d -> %d (bytes)\n", a.Size, b.Size)
}
fmt.Fprint(&sb, cmp.Diff(a.Fields, b.Fields))
return b.String()
return sb.String()
}
+2 -2
View File
@@ -170,8 +170,8 @@ func Main() error {
continue
}
if baseAlias != nextAlias {
log.Infof("alias %s changed from %s to %s", name, baseAlias, nextAlias)
if baseAlias.Type != nextAlias.Type {
log.Infof("alias %s changed from %s to %s", name, baseAlias.Type, nextAlias.Type)
}
}
@@ -31,3 +31,5 @@ typedef struct TestStruct {
} e[4];
TestUnion f;
} TestStruct;
typedef TestStruct TestStruct2;