]>
Commit | Line | Data |
---|---|---|
05b3c97b BP |
1 | #! /usr/bin/python |
2 | ||
a44d74d7 | 3 | import os.path |
05b3c97b BP |
4 | import sys |
5 | import re | |
6 | ||
7 | macros = {} | |
8 | ||
9 | anyWarnings = False | |
10 | ||
11 | types = {} | |
12 | types['char'] = {"size": 1, "alignment": 1} | |
13 | types['uint8_t'] = {"size": 1, "alignment": 1} | |
14 | types['uint16_t'] = {"size": 2, "alignment": 2} | |
15 | types['uint32_t'] = {"size": 4, "alignment": 4} | |
16 | types['uint64_t'] = {"size": 8, "alignment": 8} | |
fba47339 BP |
17 | types['ovs_be16'] = {"size": 2, "alignment": 2} |
18 | types['ovs_be32'] = {"size": 4, "alignment": 4} | |
19 | types['ovs_be64'] = {"size": 8, "alignment": 8} | |
05b3c97b BP |
20 | |
21 | token = None | |
22 | line = "" | |
23 | idRe = "[a-zA-Z_][a-zA-Z_0-9]*" | |
24 | tokenRe = "#?" + idRe + "|[0-9]+|." | |
25 | inComment = False | |
26 | inDirective = False | |
27 | def getToken(): | |
28 | global token | |
29 | global line | |
30 | global inComment | |
31 | global inDirective | |
32 | while True: | |
33 | line = line.lstrip() | |
34 | if line != "": | |
35 | if line.startswith("/*"): | |
36 | inComment = True | |
37 | line = line[2:] | |
38 | elif inComment: | |
39 | commentEnd = line.find("*/") | |
40 | if commentEnd < 0: | |
41 | line = "" | |
42 | else: | |
43 | inComment = False | |
44 | line = line[commentEnd + 2:] | |
45 | else: | |
46 | match = re.match(tokenRe, line) | |
47 | token = match.group(0) | |
48 | line = line[len(token):] | |
49 | if token.startswith('#'): | |
50 | inDirective = True | |
51 | elif token in macros and not inDirective: | |
52 | line = macros[token] + line | |
53 | continue | |
54 | return True | |
55 | elif inDirective: | |
56 | token = "$" | |
57 | inDirective = False | |
58 | return True | |
59 | else: | |
60 | global lineNumber | |
61 | line = inputFile.readline() | |
62 | lineNumber += 1 | |
63 | while line.endswith("\\\n"): | |
64 | line = line[:-2] + inputFile.readline() | |
65 | lineNumber += 1 | |
66 | if line == "": | |
67 | if token == None: | |
68 | fatal("unexpected end of input") | |
69 | token = None | |
70 | return False | |
71 | ||
72 | def fatal(msg): | |
73 | sys.stderr.write("%s:%d: error at \"%s\": %s\n" % (fileName, lineNumber, token, msg)) | |
74 | sys.exit(1) | |
75 | ||
76 | def warn(msg): | |
77 | global anyWarnings | |
78 | anyWarnings = True | |
79 | sys.stderr.write("%s:%d: warning: %s\n" % (fileName, lineNumber, msg)) | |
80 | ||
81 | def skipDirective(): | |
82 | getToken() | |
83 | while token != '$': | |
84 | getToken() | |
85 | ||
86 | def isId(s): | |
87 | return re.match(idRe + "$", s) != None | |
88 | ||
89 | def forceId(): | |
90 | if not isId(token): | |
91 | fatal("identifier expected") | |
92 | ||
93 | def forceInteger(): | |
94 | if not re.match('[0-9]+$', token): | |
95 | fatal("integer expected") | |
96 | ||
97 | def match(t): | |
98 | if token == t: | |
99 | getToken() | |
100 | return True | |
101 | else: | |
102 | return False | |
103 | ||
104 | def forceMatch(t): | |
105 | if not match(t): | |
106 | fatal("%s expected" % t) | |
107 | ||
108 | def parseTaggedName(): | |
109 | assert token in ('struct', 'union') | |
110 | name = token | |
111 | getToken() | |
112 | forceId() | |
113 | name = "%s %s" % (name, token) | |
114 | getToken() | |
115 | return name | |
116 | ||
117 | def parseTypeName(): | |
118 | if token in ('struct', 'union'): | |
119 | name = parseTaggedName() | |
120 | elif isId(token): | |
121 | name = token | |
122 | getToken() | |
123 | else: | |
124 | fatal("type name expected") | |
125 | ||
126 | if name in types: | |
127 | return name | |
128 | else: | |
129 | fatal("unknown type \"%s\"" % name) | |
130 | ||
131 | def parseStruct(): | |
132 | isStruct = token == 'struct' | |
133 | structName = parseTaggedName() | |
134 | if token == ";": | |
135 | return | |
136 | ||
137 | ofs = size = 0 | |
138 | alignment = 4 # ARM has minimum 32-bit alignment | |
139 | forceMatch('{') | |
140 | while not match('}'): | |
141 | typeName = parseTypeName() | |
142 | typeSize = types[typeName]['size'] | |
143 | typeAlignment = types[typeName]['alignment'] | |
144 | ||
145 | forceId() | |
146 | memberName = token | |
147 | getToken() | |
148 | ||
149 | if match('['): | |
150 | if token == ']': | |
151 | count = 0 | |
152 | else: | |
153 | forceInteger() | |
154 | count = int(token) | |
155 | getToken() | |
156 | forceMatch(']') | |
157 | else: | |
158 | count = 1 | |
159 | ||
160 | nBytes = typeSize * count | |
161 | if isStruct: | |
162 | if ofs % typeAlignment: | |
163 | shortage = typeAlignment - (ofs % typeAlignment) | |
164 | warn("%s member %s is %d bytes short of %d-byte alignment" | |
165 | % (structName, memberName, shortage, typeAlignment)) | |
166 | size += shortage | |
167 | ofs += shortage | |
168 | size += nBytes | |
169 | ofs += nBytes | |
170 | else: | |
171 | if nBytes > size: | |
172 | size = nBytes | |
173 | if typeAlignment > alignment: | |
174 | alignment = typeAlignment | |
175 | ||
176 | forceMatch(';') | |
177 | if size % alignment: | |
178 | shortage = alignment - (size % alignment) | |
179 | if (structName == "struct ofp_packet_in" and | |
180 | shortage == 2 and | |
181 | memberName == 'data' and | |
182 | count == 0): | |
183 | # This is intentional | |
184 | pass | |
185 | else: | |
186 | warn("%s needs %d bytes of tail padding" % (structName, shortage)) | |
187 | size += shortage | |
188 | types[structName] = {"size": size, "alignment": alignment} | |
189 | ||
190 | def checkStructs(): | |
191 | if len(sys.argv) < 2: | |
192 | sys.stderr.write("at least one non-option argument required; " | |
193 | "use --help for help") | |
194 | sys.exit(1) | |
195 | ||
196 | if '--help' in sys.argv: | |
a44d74d7 | 197 | argv0 = os.path.basename(sys.argv[0]) |
05b3c97b BP |
198 | print '''\ |
199 | %(argv0)s, for checking struct and struct member alignment | |
200 | usage: %(argv0)s HEADER [HEADER]... | |
201 | ||
202 | This program reads the header files specified on the command line and | |
203 | verifies that all struct members are aligned on natural boundaries | |
204 | without any need for the compiler to add additional padding. It also | |
205 | verifies that each struct's size is a multiple of 32 bits (because | |
206 | some ABIs for ARM require all structs to be a multiple of 32 bits), or | |
207 | 64 bits if the struct has any 64-bit members, again without the | |
208 | compiler adding additional padding. Finally, it checks struct size | |
209 | assertions using OFP_ASSERT. | |
210 | ||
211 | Header files are read in the order specified. #include directives are | |
212 | not processed, so specify them in dependency order. | |
213 | ||
214 | This program is specialized for reading include/openflow/openflow.h | |
215 | and include/openflow/nicira-ext.h. It will not work on arbitrary | |
216 | header files without extensions.''' % {"argv0": argv0} | |
217 | sys.exit(0) | |
218 | ||
219 | global fileName | |
220 | for fileName in sys.argv[1:]: | |
221 | global inputFile | |
222 | global lineNumber | |
223 | inputFile = open(fileName) | |
224 | lineNumber = 0 | |
225 | while getToken(): | |
226 | if token in ("#ifdef", "#ifndef", "#include", | |
227 | "#endif", "#elif", "#else"): | |
228 | skipDirective() | |
229 | elif token == "#define": | |
230 | getToken() | |
231 | name = token | |
232 | if line.startswith('('): | |
233 | skipDirective() | |
234 | else: | |
235 | definition = "" | |
236 | getToken() | |
237 | while token != '$': | |
238 | definition += token | |
239 | getToken() | |
240 | macros[name] = definition | |
241 | elif token == "enum": | |
242 | while token != ';': | |
243 | getToken() | |
244 | elif token in ('struct', 'union'): | |
245 | parseStruct() | |
246 | elif match('OFP_ASSERT') or match('BOOST_STATIC_ASSERT'): | |
247 | forceMatch('(') | |
248 | forceMatch('sizeof') | |
249 | forceMatch('(') | |
250 | typeName = parseTypeName() | |
251 | forceMatch(')') | |
252 | forceMatch('=') | |
253 | forceMatch('=') | |
254 | forceInteger() | |
255 | size = int(token) | |
256 | getToken() | |
257 | forceMatch(')') | |
258 | if types[typeName]['size'] != size: | |
259 | warn("%s is %d bytes long but declared as %d" % ( | |
260 | typeName, types[typeName]['size'], size)) | |
261 | else: | |
262 | fatal("parse error") | |
263 | inputFile.close() | |
264 | if anyWarnings: | |
265 | sys.exit(1) | |
266 | ||
267 | if __name__ == '__main__': | |
268 | checkStructs() |