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