| /* |
| * Copyright (c) 2017 VMware, Inc. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include <memory> |
| #include <string> |
| |
| #include <clang/AST/ASTContext.h> |
| #include <clang/AST/RecordLayout.h> |
| #include <clang/AST/RecursiveASTVisitor.h> |
| #include <llvm/ADT/StringExtras.h> |
| #include <llvm/Config/llvm-config.h> |
| #include "common.h" |
| #include "table_desc.h" |
| |
| namespace ebpf { |
| |
| using std::string; |
| using std::to_string; |
| using std::unique_ptr; |
| using namespace clang; |
| |
| // Helper visitor for constructing a string representation of a key/leaf decl |
| class BMapDeclVisitor : public clang::RecursiveASTVisitor<BMapDeclVisitor> { |
| public: |
| explicit BMapDeclVisitor(clang::ASTContext &C, std::string &result); |
| bool TraverseRecordDecl(clang::RecordDecl *Decl); |
| bool VisitRecordDecl(clang::RecordDecl *Decl); |
| bool VisitFieldDecl(clang::FieldDecl *Decl); |
| bool VisitBuiltinType(const clang::BuiltinType *T); |
| bool VisitTypedefType(const clang::TypedefType *T); |
| bool VisitTagType(const clang::TagType *T); |
| bool VisitPointerType(const clang::PointerType *T); |
| bool VisitEnumDecl(clang::EnumDecl *D); |
| bool VisitAttr(clang::Attr *A); |
| |
| private: |
| bool shouldSkipPadding(const RecordDecl *D); |
| void genJSONForField(FieldDecl *F); |
| |
| private: |
| clang::ASTContext &C; |
| std::string &result_; |
| }; |
| |
| // Encode the struct layout as a json description |
| BMapDeclVisitor::BMapDeclVisitor(ASTContext &C, string &result) : C(C), result_(result) {} |
| |
| bool BMapDeclVisitor::shouldSkipPadding(const RecordDecl *D) { |
| if (D->isUnion() || D->field_empty()) |
| return true; |
| for (auto F : D->getDefinition()->fields()) { |
| if (F->isBitField()) |
| return true; |
| QualType Ty = F->getType(); |
| if (Ty->isIncompleteArrayType()) |
| return true; |
| } |
| return false; |
| } |
| |
| void BMapDeclVisitor::genJSONForField(FieldDecl *F) { |
| if (F->isAnonymousStructOrUnion()) { |
| if (const RecordType *R = dyn_cast<RecordType>(F->getType())) |
| TraverseDecl(R->getDecl()); |
| result_ += ", "; |
| return; |
| } |
| result_ += "["; |
| TraverseDecl(F); |
| if (const ConstantArrayType *T = dyn_cast<ConstantArrayType>(F->getType())) |
| #if LLVM_VERSION_MAJOR >= 13 |
| result_ += ", [" + toString(T->getSize(), 10, false) + "]"; |
| #else |
| result_ += ", [" + T->getSize().toString(10, false) + "]"; |
| #endif |
| if (F->isBitField()) |
| result_ += ", " + to_string(F->getBitWidthValue(C)); |
| result_ += "], "; |
| } |
| |
| bool BMapDeclVisitor::VisitFieldDecl(FieldDecl *D) { |
| result_ += "\""; |
| result_ += D->getName(); |
| result_ += "\","; |
| return true; |
| } |
| |
| bool BMapDeclVisitor::VisitEnumDecl(EnumDecl *D) { |
| TraverseType(D->getIntegerType()); |
| return false; |
| } |
| |
| bool BMapDeclVisitor::TraverseRecordDecl(RecordDecl *D) { |
| // skip children, handled in Visit... |
| if (!WalkUpFromRecordDecl(D)) |
| return false; |
| return true; |
| } |
| |
| bool BMapDeclVisitor::VisitRecordDecl(RecordDecl *D) { |
| result_ += "[\""; |
| result_ += D->getName(); |
| result_ += "\", ["; |
| |
| bool SkipPadding = shouldSkipPadding(D); |
| if (SkipPadding) { |
| for (auto F : D->getDefinition()->fields()) { |
| genJSONForField(F); |
| } |
| } else { |
| const ASTRecordLayout &Layout = C.getASTRecordLayout(D); |
| CharUnits Offset = C.toCharUnitsFromBits(Layout.getFieldOffset(0)); |
| for (auto F : D->getDefinition()->fields()) { |
| CharUnits FieldSize = C.getTypeSizeInChars(F->getType()); |
| auto FieldOffsetBits = Layout.getFieldOffset(F->getFieldIndex()); |
| CharUnits FieldOffset = C.toCharUnitsFromBits(FieldOffsetBits); |
| |
| uint64_t Padding = (FieldOffset - Offset).getQuantity(); |
| if (Padding) { |
| /* Padding before this field with "char __pad_<FieldIndex>[Padding]". */ |
| result_ += "[\"__pad_" + to_string(F->getFieldIndex()) + "\",\"char\",[" |
| + to_string(Padding) + "]], "; |
| } |
| Offset = FieldOffset + FieldSize; |
| genJSONForField(F); |
| } |
| |
| /* Additional Padding after the last field so that the Record Size matches */ |
| CharUnits RecordSize = Layout.getSize(); |
| if (RecordSize > Offset) { |
| result_ += "[\"__pad_end\",\"char\",[" |
| + to_string((RecordSize - Offset).getQuantity()) + "]], "; |
| } |
| } |
| |
| if (!D->getDefinition()->field_empty()) |
| result_.erase(result_.end() - 2); |
| result_ += "]"; |
| if (D->isUnion()) |
| result_ += ", \"union\""; |
| else if (D->isStruct()) { |
| if (SkipPadding) |
| result_ += ", \"struct\""; |
| else |
| result_ += ", \"struct_packed\""; |
| } |
| result_ += "]"; |
| return true; |
| } |
| // pointer to anything should be treated as terminal, don't recurse further |
| bool BMapDeclVisitor::VisitPointerType(const PointerType *T) { |
| result_ += "\"unsigned long long\""; |
| return false; |
| } |
| bool BMapDeclVisitor::VisitTagType(const TagType *T) { |
| return TraverseDecl(T->getDecl()->getDefinition()); |
| } |
| bool BMapDeclVisitor::VisitTypedefType(const TypedefType *T) { return TraverseDecl(T->getDecl()); } |
| bool BMapDeclVisitor::VisitBuiltinType(const BuiltinType *T) { |
| result_ += "\""; |
| result_ += T->getName(C.getPrintingPolicy()); |
| result_ += "\""; |
| return true; |
| } |
| bool BMapDeclVisitor::VisitAttr(clang::Attr *A) { return false; } |
| |
| class JsonMapTypesVisitor : public virtual MapTypesVisitor { |
| public: |
| virtual void Visit(TableDesc &desc, clang::ASTContext &C, clang::QualType key_type, |
| clang::QualType leaf_type) { |
| BMapDeclVisitor v1(C, desc.key_desc), v2(C, desc.leaf_desc); |
| v1.TraverseType(key_type); |
| v2.TraverseType(leaf_type); |
| } |
| }; |
| |
| unique_ptr<MapTypesVisitor> createJsonMapTypesVisitor() { |
| return make_unique<JsonMapTypesVisitor>(); |
| } |
| |
| } // namespace ebpf |