xref: /aosp_15_r20/external/bcc/src/cc/json_map_decl_visitor.cc (revision 387f9dfdfa2baef462e92476d413c7bc2470293e)
1 /*
2  * Copyright (c) 2017 VMware, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <memory>
18 #include <string>
19 
20 #include <clang/AST/ASTContext.h>
21 #include <clang/AST/RecordLayout.h>
22 #include <clang/AST/RecursiveASTVisitor.h>
23 #include <llvm/ADT/StringExtras.h>
24 #include <llvm/Config/llvm-config.h>
25 #include "common.h"
26 #include "table_desc.h"
27 
28 namespace ebpf {
29 
30 using std::string;
31 using std::to_string;
32 using std::unique_ptr;
33 using namespace clang;
34 
35 // Helper visitor for constructing a string representation of a key/leaf decl
36 class BMapDeclVisitor : public clang::RecursiveASTVisitor<BMapDeclVisitor> {
37  public:
38   explicit BMapDeclVisitor(clang::ASTContext &C, std::string &result);
39   bool TraverseRecordDecl(clang::RecordDecl *Decl);
40   bool VisitRecordDecl(clang::RecordDecl *Decl);
41   bool VisitFieldDecl(clang::FieldDecl *Decl);
42   bool VisitBuiltinType(const clang::BuiltinType *T);
43   bool VisitTypedefType(const clang::TypedefType *T);
44   bool VisitTagType(const clang::TagType *T);
45   bool VisitPointerType(const clang::PointerType *T);
46   bool VisitEnumDecl(clang::EnumDecl *D);
47   bool VisitAttr(clang::Attr *A);
48 
49  private:
50   bool shouldSkipPadding(const RecordDecl *D);
51   void genJSONForField(FieldDecl *F);
52 
53  private:
54   clang::ASTContext &C;
55   std::string &result_;
56 };
57 
58 // Encode the struct layout as a json description
BMapDeclVisitor(ASTContext & C,string & result)59 BMapDeclVisitor::BMapDeclVisitor(ASTContext &C, string &result) : C(C), result_(result) {}
60 
shouldSkipPadding(const RecordDecl * D)61 bool BMapDeclVisitor::shouldSkipPadding(const RecordDecl *D) {
62   if (D->isUnion() || D->field_empty())
63     return true;
64   for (auto F : D->getDefinition()->fields()) {
65     if (F->isBitField())
66       return true;
67     QualType Ty = F->getType();
68     if (Ty->isIncompleteArrayType())
69       return true;
70   }
71   return false;
72 }
73 
genJSONForField(FieldDecl * F)74 void BMapDeclVisitor::genJSONForField(FieldDecl *F) {
75   if (F->isAnonymousStructOrUnion()) {
76     if (const RecordType *R = dyn_cast<RecordType>(F->getType()))
77       TraverseDecl(R->getDecl());
78     result_ += ", ";
79     return;
80   }
81   result_ += "[";
82   TraverseDecl(F);
83   if (const ConstantArrayType *T = dyn_cast<ConstantArrayType>(F->getType()))
84 #if LLVM_VERSION_MAJOR >= 13
85     result_ += ", [" + toString(T->getSize(), 10, false) + "]";
86 #else
87     result_ += ", [" + T->getSize().toString(10, false) + "]";
88 #endif
89   if (F->isBitField())
90     result_ += ", " + to_string(F->getBitWidthValue(C));
91   result_ += "], ";
92 }
93 
VisitFieldDecl(FieldDecl * D)94 bool BMapDeclVisitor::VisitFieldDecl(FieldDecl *D) {
95   result_ += "\"";
96   result_ += D->getName();
97   result_ += "\",";
98   return true;
99 }
100 
VisitEnumDecl(EnumDecl * D)101 bool BMapDeclVisitor::VisitEnumDecl(EnumDecl *D) {
102   TraverseType(D->getIntegerType());
103   return false;
104 }
105 
TraverseRecordDecl(RecordDecl * D)106 bool BMapDeclVisitor::TraverseRecordDecl(RecordDecl *D) {
107   // skip children, handled in Visit...
108   if (!WalkUpFromRecordDecl(D))
109     return false;
110   return true;
111 }
112 
VisitRecordDecl(RecordDecl * D)113 bool BMapDeclVisitor::VisitRecordDecl(RecordDecl *D) {
114   result_ += "[\"";
115   result_ += D->getName();
116   result_ += "\", [";
117 
118   bool SkipPadding = shouldSkipPadding(D);
119   if (SkipPadding) {
120     for (auto F : D->getDefinition()->fields()) {
121       genJSONForField(F);
122     }
123   } else {
124     const ASTRecordLayout &Layout = C.getASTRecordLayout(D);
125     CharUnits Offset = C.toCharUnitsFromBits(Layout.getFieldOffset(0));
126     for (auto F : D->getDefinition()->fields()) {
127       CharUnits FieldSize = C.getTypeSizeInChars(F->getType());
128       auto FieldOffsetBits = Layout.getFieldOffset(F->getFieldIndex());
129       CharUnits FieldOffset = C.toCharUnitsFromBits(FieldOffsetBits);
130 
131       uint64_t Padding = (FieldOffset - Offset).getQuantity();
132       if (Padding) {
133         /* Padding before this field with "char __pad_<FieldIndex>[Padding]". */
134         result_ += "[\"__pad_" + to_string(F->getFieldIndex()) + "\",\"char\",["
135                 + to_string(Padding) + "]], ";
136       }
137       Offset = FieldOffset + FieldSize;
138       genJSONForField(F);
139     }
140 
141     /* Additional Padding after the last field so that the Record Size matches */
142     CharUnits RecordSize = Layout.getSize();
143     if (RecordSize > Offset) {
144         result_ += "[\"__pad_end\",\"char\",["
145                 + to_string((RecordSize - Offset).getQuantity()) + "]], ";
146     }
147   }
148 
149   if (!D->getDefinition()->field_empty())
150     result_.erase(result_.end() - 2);
151   result_ += "]";
152   if (D->isUnion())
153     result_ += ", \"union\"";
154   else if (D->isStruct()) {
155     if (SkipPadding)
156       result_ += ", \"struct\"";
157     else
158       result_ += ", \"struct_packed\"";
159   }
160   result_ += "]";
161   return true;
162 }
163 // pointer to anything should be treated as terminal, don't recurse further
VisitPointerType(const PointerType * T)164 bool BMapDeclVisitor::VisitPointerType(const PointerType *T) {
165   result_ += "\"unsigned long long\"";
166   return false;
167 }
VisitTagType(const TagType * T)168 bool BMapDeclVisitor::VisitTagType(const TagType *T) {
169   return TraverseDecl(T->getDecl()->getDefinition());
170 }
VisitTypedefType(const TypedefType * T)171 bool BMapDeclVisitor::VisitTypedefType(const TypedefType *T) { return TraverseDecl(T->getDecl()); }
VisitBuiltinType(const BuiltinType * T)172 bool BMapDeclVisitor::VisitBuiltinType(const BuiltinType *T) {
173   result_ += "\"";
174   result_ += T->getName(C.getPrintingPolicy());
175   result_ += "\"";
176   return true;
177 }
VisitAttr(clang::Attr * A)178 bool BMapDeclVisitor::VisitAttr(clang::Attr *A) { return false; }
179 
180 class JsonMapTypesVisitor : public virtual MapTypesVisitor {
181  public:
Visit(TableDesc & desc,clang::ASTContext & C,clang::QualType key_type,clang::QualType leaf_type)182   virtual void Visit(TableDesc &desc, clang::ASTContext &C, clang::QualType key_type,
183                      clang::QualType leaf_type) {
184     BMapDeclVisitor v1(C, desc.key_desc), v2(C, desc.leaf_desc);
185     v1.TraverseType(key_type);
186     v2.TraverseType(leaf_type);
187   }
188 };
189 
createJsonMapTypesVisitor()190 unique_ptr<MapTypesVisitor> createJsonMapTypesVisitor() {
191   return make_unique<JsonMapTypesVisitor>();
192 }
193 
194 }  // namespace ebpf
195