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