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