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