]> git.proxmox.com Git - mirror_ovs.git/blame - build-aux/check-structs
Add types and accessors for working with half-aligned 64-bit values.
[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}
14types['uint16_t'] = {"size": 2, "alignment": 2}
15types['uint32_t'] = {"size": 4, "alignment": 4}
16types['uint64_t'] = {"size": 8, "alignment": 8}
fba47339
BP
17types['ovs_be16'] = {"size": 2, "alignment": 2}
18types['ovs_be32'] = {"size": 4, "alignment": 4}
19types['ovs_be64'] = {"size": 8, "alignment": 8}
05b3c97b
BP
20
21token = None
22line = ""
23idRe = "[a-zA-Z_][a-zA-Z_0-9]*"
24tokenRe = "#?" + idRe + "|[0-9]+|."
25inComment = False
26inDirective = False
27def 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
72def fatal(msg):
73 sys.stderr.write("%s:%d: error at \"%s\": %s\n" % (fileName, lineNumber, token, msg))
74 sys.exit(1)
75
76def warn(msg):
77 global anyWarnings
78 anyWarnings = True
79 sys.stderr.write("%s:%d: warning: %s\n" % (fileName, lineNumber, msg))
80
81def skipDirective():
82 getToken()
83 while token != '$':
84 getToken()
85
86def isId(s):
87 return re.match(idRe + "$", s) != None
88
89def forceId():
90 if not isId(token):
91 fatal("identifier expected")
92
93def forceInteger():
94 if not re.match('[0-9]+$', token):
95 fatal("integer expected")
96
97def match(t):
98 if token == t:
99 getToken()
100 return True
101 else:
102 return False
103
104def forceMatch(t):
105 if not match(t):
106 fatal("%s expected" % t)
107
108def 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
117def 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
131def 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
190def 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
200usage: %(argv0)s HEADER [HEADER]...
201
202This program reads the header files specified on the command line and
203verifies that all struct members are aligned on natural boundaries
204without any need for the compiler to add additional padding. It also
205verifies that each struct's size is a multiple of 32 bits (because
206some ABIs for ARM require all structs to be a multiple of 32 bits), or
20764 bits if the struct has any 64-bit members, again without the
208compiler adding additional padding. Finally, it checks struct size
209assertions using OFP_ASSERT.
210
211Header files are read in the order specified. #include directives are
212not processed, so specify them in dependency order.
213
214This program is specialized for reading include/openflow/openflow.h
215and include/openflow/nicira-ext.h. It will not work on arbitrary
216header 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
267if __name__ == '__main__':
268 checkStructs()