netdev-dpdk: fix mbuf leaks
[cascardo/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()