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