292 lines
8.7 KiB
Python
Executable File
292 lines
8.7 KiB
Python
Executable File
#! /usr/bin/python
|
|
|
|
import os.path
|
|
import sys
|
|
import re
|
|
|
|
macros = {}
|
|
|
|
anyWarnings = False
|
|
|
|
types = {}
|
|
types['char'] = {"size": 1, "alignment": 1}
|
|
types['uint8_t'] = {"size": 1, "alignment": 1}
|
|
types['ovs_be16'] = {"size": 2, "alignment": 2}
|
|
types['ovs_be32'] = {"size": 4, "alignment": 4}
|
|
types['ovs_be64'] = {"size": 8, "alignment": 8}
|
|
types['ovs_32aligned_be64'] = {"size": 8, "alignment": 4}
|
|
types['struct eth_addr'] = {"size": 6, "alignment": 1}
|
|
|
|
token = None
|
|
line = ""
|
|
idRe = "[a-zA-Z_][a-zA-Z_0-9]*"
|
|
tokenRe = "#?" + idRe + "|[0-9]+|."
|
|
includeRe = re.compile(r'\s*#include\s+<(openflow/[^#]+)>')
|
|
includePath = ''
|
|
inComment = False
|
|
inDirective = False
|
|
inputStack = []
|
|
def getToken():
|
|
global token
|
|
global line
|
|
global inComment
|
|
global inDirective
|
|
global inputFile
|
|
global fileName
|
|
while True:
|
|
line = line.lstrip()
|
|
if line != "":
|
|
if line.startswith("/*"):
|
|
inComment = True
|
|
line = line[2:]
|
|
elif inComment:
|
|
commentEnd = line.find("*/")
|
|
if commentEnd < 0:
|
|
line = ""
|
|
else:
|
|
inComment = False
|
|
line = line[commentEnd + 2:]
|
|
else:
|
|
match = re.match(tokenRe, line)
|
|
token = match.group(0)
|
|
line = line[len(token):]
|
|
if token.startswith('#'):
|
|
inDirective = True
|
|
elif token in macros and not inDirective:
|
|
line = macros[token] + line
|
|
continue
|
|
return True
|
|
elif inDirective:
|
|
token = "$"
|
|
inDirective = False
|
|
return True
|
|
else:
|
|
global lineNumber
|
|
while True:
|
|
line = inputFile.readline()
|
|
lineNumber += 1
|
|
while line.endswith("\\\n"):
|
|
line = line[:-2] + inputFile.readline()
|
|
lineNumber += 1
|
|
match = includeRe.match(line)
|
|
if match:
|
|
inputStack.append((fileName, inputFile, lineNumber))
|
|
inputFile = open(includePath + match.group(1))
|
|
lineNumber = 0
|
|
continue
|
|
if line == "":
|
|
if inputStack:
|
|
fileName, inputFile, lineNumber = inputStack.pop()
|
|
continue
|
|
if token == None:
|
|
fatal("unexpected end of input")
|
|
token = None
|
|
return False
|
|
break
|
|
|
|
def fatal(msg):
|
|
sys.stderr.write("%s:%d: error at \"%s\": %s\n" % (fileName, lineNumber, token, msg))
|
|
sys.exit(1)
|
|
|
|
def warn(msg):
|
|
global anyWarnings
|
|
anyWarnings = True
|
|
sys.stderr.write("%s:%d: warning: %s\n" % (fileName, lineNumber, msg))
|
|
|
|
def skipDirective():
|
|
getToken()
|
|
while token != '$':
|
|
getToken()
|
|
|
|
def isId(s):
|
|
return re.match(idRe + "$", s) != None
|
|
|
|
def forceId():
|
|
if not isId(token):
|
|
fatal("identifier expected")
|
|
|
|
def forceInteger():
|
|
if not re.match('[0-9]+$', token):
|
|
fatal("integer expected")
|
|
|
|
def match(t):
|
|
if token == t:
|
|
getToken()
|
|
return True
|
|
else:
|
|
return False
|
|
|
|
def forceMatch(t):
|
|
if not match(t):
|
|
fatal("%s expected" % t)
|
|
|
|
def parseTaggedName():
|
|
assert token in ('struct', 'union')
|
|
name = token
|
|
getToken()
|
|
forceId()
|
|
name = "%s %s" % (name, token)
|
|
getToken()
|
|
return name
|
|
|
|
def parseTypeName():
|
|
if token in ('struct', 'union'):
|
|
name = parseTaggedName()
|
|
elif isId(token):
|
|
name = token
|
|
getToken()
|
|
else:
|
|
fatal("type name expected")
|
|
|
|
if name in types:
|
|
return name
|
|
else:
|
|
fatal("unknown type \"%s\"" % name)
|
|
|
|
def parseStruct():
|
|
isStruct = token == 'struct'
|
|
structName = parseTaggedName()
|
|
if token == ";":
|
|
return
|
|
|
|
ofs = size = 0
|
|
alignment = 4 # ARM has minimum 32-bit alignment
|
|
forceMatch('{')
|
|
while not match('}'):
|
|
typeName = parseTypeName()
|
|
typeSize = types[typeName]['size']
|
|
typeAlignment = types[typeName]['alignment']
|
|
|
|
forceId()
|
|
memberName = token
|
|
getToken()
|
|
|
|
if match('['):
|
|
if token == ']':
|
|
count = 0
|
|
else:
|
|
forceInteger()
|
|
count = int(token)
|
|
getToken()
|
|
forceMatch(']')
|
|
else:
|
|
count = 1
|
|
|
|
nBytes = typeSize * count
|
|
if isStruct:
|
|
if ofs % typeAlignment:
|
|
shortage = typeAlignment - (ofs % typeAlignment)
|
|
warn("%s member %s is %d bytes short of %d-byte alignment"
|
|
% (structName, memberName, shortage, typeAlignment))
|
|
size += shortage
|
|
ofs += shortage
|
|
size += nBytes
|
|
ofs += nBytes
|
|
else:
|
|
if nBytes > size:
|
|
size = nBytes
|
|
if typeAlignment > alignment:
|
|
alignment = typeAlignment
|
|
|
|
forceMatch(';')
|
|
if size % alignment:
|
|
shortage = alignment - (size % alignment)
|
|
if (structName == "struct ofp10_packet_in" and
|
|
shortage == 2 and
|
|
memberName == 'data' and
|
|
count == 0):
|
|
# This is intentional
|
|
pass
|
|
else:
|
|
warn("%s needs %d bytes of tail padding" % (structName, shortage))
|
|
size += shortage
|
|
types[structName] = {"size": size, "alignment": alignment}
|
|
return structName
|
|
|
|
def checkStructs():
|
|
if len(sys.argv) < 2:
|
|
sys.stderr.write("at least one non-option argument required; "
|
|
"use --help for help")
|
|
sys.exit(1)
|
|
|
|
if '--help' in sys.argv:
|
|
argv0 = os.path.basename(sys.argv[0])
|
|
print '''\
|
|
%(argv0)s, for checking struct and struct member alignment
|
|
usage: %(argv0)s -Ipath HEADER [HEADER]...
|
|
|
|
This program reads the header files specified on the command line and
|
|
verifies that all struct members are aligned on natural boundaries
|
|
without any need for the compiler to add additional padding. It also
|
|
verifies that each struct's size is a multiple of 32 bits (because
|
|
some ABIs for ARM require all structs to be a multiple of 32 bits), or
|
|
64 bits if the struct has any 64-bit members, again without the
|
|
compiler adding additional padding. Finally, it checks struct size
|
|
assertions using OFP_ASSERT.
|
|
|
|
This program is specialized for reading Open vSwitch's OpenFlow header
|
|
files. It will not work on arbitrary header files without extensions.\
|
|
''' % {"argv0": argv0}
|
|
sys.exit(0)
|
|
|
|
global fileName
|
|
for fileName in sys.argv[1:]:
|
|
if fileName.startswith('-I'):
|
|
global includePath
|
|
includePath = fileName[2:]
|
|
if not includePath.endswith('/'):
|
|
includePath += '/'
|
|
continue
|
|
global inputFile
|
|
global lineNumber
|
|
inputFile = open(fileName)
|
|
lineNumber = 0
|
|
lastStruct = None
|
|
while getToken():
|
|
if token in ("#ifdef", "#ifndef", "#include",
|
|
"#endif", "#elif", "#else"):
|
|
skipDirective()
|
|
elif token == "#define":
|
|
getToken()
|
|
name = token
|
|
if line.startswith('('):
|
|
skipDirective()
|
|
else:
|
|
definition = ""
|
|
getToken()
|
|
while token != '$':
|
|
definition += token
|
|
getToken()
|
|
macros[name] = definition
|
|
elif token == "enum":
|
|
while token != ';':
|
|
getToken()
|
|
elif token in ('struct', 'union'):
|
|
lastStruct = parseStruct()
|
|
elif match('OFP_ASSERT') or match('BOOST_STATIC_ASSERT'):
|
|
forceMatch('(')
|
|
forceMatch('sizeof')
|
|
forceMatch('(')
|
|
typeName = parseTypeName()
|
|
if typeName != lastStruct:
|
|
warn("checking size of %s but %s was most recently defined"
|
|
% (typeName, lastStruct))
|
|
forceMatch(')')
|
|
forceMatch('=')
|
|
forceMatch('=')
|
|
forceInteger()
|
|
size = int(token)
|
|
getToken()
|
|
forceMatch(')')
|
|
if types[typeName]['size'] != size:
|
|
warn("%s is %d bytes long but declared as %d" % (
|
|
typeName, types[typeName]['size'], size))
|
|
else:
|
|
fatal("parse error")
|
|
inputFile.close()
|
|
if anyWarnings:
|
|
sys.exit(1)
|
|
|
|
if __name__ == '__main__':
|
|
checkStructs()
|