• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "common.h"
24 #include "table_desc.h"
25 
26 namespace ebpf {
27 
28 using std::string;
29 using std::to_string;
30 using std::unique_ptr;
31 using namespace clang;
32 
33 // Helper visitor for constructing a string representation of a key/leaf decl
34 class BMapDeclVisitor : public clang::RecursiveASTVisitor<BMapDeclVisitor> {
35  public:
36   explicit BMapDeclVisitor(clang::ASTContext &C, std::string &result);
37   bool TraverseRecordDecl(clang::RecordDecl *Decl);
38   bool VisitRecordDecl(clang::RecordDecl *Decl);
39   bool VisitFieldDecl(clang::FieldDecl *Decl);
40   bool VisitBuiltinType(const clang::BuiltinType *T);
41   bool VisitTypedefType(const clang::TypedefType *T);
42   bool VisitTagType(const clang::TagType *T);
43   bool VisitPointerType(const clang::PointerType *T);
44   bool VisitEnumConstantDecl(clang::EnumConstantDecl *D);
45   bool VisitEnumDecl(clang::EnumDecl *D);
46 
47  private:
48   bool shouldSkipPadding(const RecordDecl *D);
49   void genJSONForField(FieldDecl *F);
50 
51  private:
52   clang::ASTContext &C;
53   std::string &result_;
54 };
55 
56 // Encode the struct layout as a json description
BMapDeclVisitor(ASTContext & C,string & result)57 BMapDeclVisitor::BMapDeclVisitor(ASTContext &C, string &result) : C(C), result_(result) {}
58 
shouldSkipPadding(const RecordDecl * D)59 bool BMapDeclVisitor::shouldSkipPadding(const RecordDecl *D) {
60   if (D->isUnion() || D->field_empty())
61     return true;
62   for (auto F : D->getDefinition()->fields()) {
63     if (F->isBitField())
64       return true;
65     QualType Ty = F->getType();
66     if (Ty->isIncompleteArrayType())
67       return true;
68   }
69   return false;
70 }
71 
genJSONForField(FieldDecl * F)72 void BMapDeclVisitor::genJSONForField(FieldDecl *F) {
73   if (F->isAnonymousStructOrUnion()) {
74     if (const RecordType *R = dyn_cast<RecordType>(F->getType()))
75       TraverseDecl(R->getDecl());
76     result_ += ", ";
77     return;
78   }
79   result_ += "[";
80   TraverseDecl(F);
81   if (const ConstantArrayType *T = dyn_cast<ConstantArrayType>(F->getType()))
82     result_ += ", [" + T->getSize().toString(10, false) + "]";
83   if (F->isBitField())
84     result_ += ", " + to_string(F->getBitWidthValue(C));
85   result_ += "], ";
86 }
87 
VisitFieldDecl(FieldDecl * D)88 bool BMapDeclVisitor::VisitFieldDecl(FieldDecl *D) {
89   result_ += "\"";
90   result_ += D->getName();
91   result_ += "\",";
92   return true;
93 }
94 
VisitEnumConstantDecl(EnumConstantDecl * D)95 bool BMapDeclVisitor::VisitEnumConstantDecl(EnumConstantDecl *D) {
96   result_ += "\"";
97   result_ += D->getName();
98   result_ += "\",";
99   return false;
100 }
101 
VisitEnumDecl(EnumDecl * D)102 bool BMapDeclVisitor::VisitEnumDecl(EnumDecl *D) {
103   result_ += "[\"";
104   result_ += D->getName();
105   result_ += "\", [";
106   for (auto it = D->enumerator_begin(); it != D->enumerator_end(); ++it) {
107     TraverseDecl(*it);
108   }
109   result_.erase(result_.end() - 1);
110   result_ += "], \"enum\"]";
111   return false;
112 }
113 
TraverseRecordDecl(RecordDecl * D)114 bool BMapDeclVisitor::TraverseRecordDecl(RecordDecl *D) {
115   // skip children, handled in Visit...
116   if (!WalkUpFromRecordDecl(D))
117     return false;
118   return true;
119 }
120 
VisitRecordDecl(RecordDecl * D)121 bool BMapDeclVisitor::VisitRecordDecl(RecordDecl *D) {
122   result_ += "[\"";
123   result_ += D->getName();
124   result_ += "\", [";
125 
126   bool SkipPadding = shouldSkipPadding(D);
127   if (SkipPadding) {
128     for (auto F : D->getDefinition()->fields()) {
129       genJSONForField(F);
130     }
131   } else {
132     const ASTRecordLayout &Layout = C.getASTRecordLayout(D);
133     CharUnits Offset = C.toCharUnitsFromBits(Layout.getFieldOffset(0));
134     for (auto F : D->getDefinition()->fields()) {
135       CharUnits FieldSize = C.getTypeSizeInChars(F->getType());
136       auto FieldOffsetBits = Layout.getFieldOffset(F->getFieldIndex());
137       CharUnits FieldOffset = C.toCharUnitsFromBits(FieldOffsetBits);
138 
139       uint64_t Padding = (FieldOffset - Offset).getQuantity();
140       if (Padding) {
141         /* Padding before this field with "char __pad_<FieldIndex>[Padding]". */
142         result_ += "[\"__pad_" + to_string(F->getFieldIndex()) + "\",\"char\",["
143                 + to_string(Padding) + "]], ";
144       }
145       Offset = FieldOffset + FieldSize;
146       genJSONForField(F);
147     }
148 
149     /* Additional Padding after the last field so that the Record Size matches */
150     CharUnits RecordSize = Layout.getSize();
151     if (RecordSize > Offset) {
152         result_ += "[\"__pad_end\",\"char\",["
153                 + to_string((RecordSize - Offset).getQuantity()) + "]], ";
154     }
155   }
156 
157   if (!D->getDefinition()->field_empty())
158     result_.erase(result_.end() - 2);
159   result_ += "]";
160   if (D->isUnion())
161     result_ += ", \"union\"";
162   else if (D->isStruct()) {
163     if (SkipPadding)
164       result_ += ", \"struct\"";
165     else
166       result_ += ", \"struct_packed\"";
167   }
168   result_ += "]";
169   return true;
170 }
171 // pointer to anything should be treated as terminal, don't recurse further
VisitPointerType(const PointerType * T)172 bool BMapDeclVisitor::VisitPointerType(const PointerType *T) {
173   result_ += "\"unsigned long long\"";
174   return false;
175 }
VisitTagType(const TagType * T)176 bool BMapDeclVisitor::VisitTagType(const TagType *T) {
177   return TraverseDecl(T->getDecl()->getDefinition());
178 }
VisitTypedefType(const TypedefType * T)179 bool BMapDeclVisitor::VisitTypedefType(const TypedefType *T) { return TraverseDecl(T->getDecl()); }
VisitBuiltinType(const BuiltinType * T)180 bool BMapDeclVisitor::VisitBuiltinType(const BuiltinType *T) {
181   result_ += "\"";
182   result_ += T->getName(C.getPrintingPolicy());
183   result_ += "\"";
184   return true;
185 }
186 
187 class JsonMapTypesVisitor : public virtual MapTypesVisitor {
188  public:
Visit(TableDesc & desc,clang::ASTContext & C,clang::QualType key_type,clang::QualType leaf_type)189   virtual void Visit(TableDesc &desc, clang::ASTContext &C, clang::QualType key_type,
190                      clang::QualType leaf_type) {
191     BMapDeclVisitor v1(C, desc.key_desc), v2(C, desc.leaf_desc);
192     v1.TraverseType(key_type);
193     v2.TraverseType(leaf_type);
194   }
195 };
196 
createJsonMapTypesVisitor()197 unique_ptr<MapTypesVisitor> createJsonMapTypesVisitor() {
198   return make_unique<JsonMapTypesVisitor>();
199 }
200 
201 }  // namespace ebpf
202