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