2
0
mirror of https://github.com/openvswitch/ovs synced 2025-10-17 14:28:02 +00:00
Files
openvswitch/build-aux/extract-ofp-errors
Jarno Rajahalme df30f9b1cd ofp-errors: Remove OFPERR_* values for error categories.
The main use of OFPERR_* is to define specific errors, but OFPERR_* also
existed for each possible category of error, to enable partial decoding of
unknown specific errors within a known category.  However, in practice,
it was very easy to misuse the error categories as if they were particular
errors.  This commit removes the error category values, to make that error
impossible.

(If partial decoding of unknown specific errors turns out to have been a
valuable feature, then we can reintroduce it some other way.)

Signed-off-by: Jarno Rajahalme <jarno.rajahalme@nsn.com>
Signed-off-by: Ben Pfaff <blp@nicira.com>
2012-12-04 08:25:56 -08:00

369 lines
11 KiB
Python
Executable File

#! /usr/bin/python
import sys
import os.path
import re
macros = {}
token = None
line = ""
idRe = "[a-zA-Z_][a-zA-Z_0-9]*"
tokenRe = "#?" + idRe + "|[0-9]+|."
inComment = False
inDirective = False
def getLine():
global line
global lineNumber
line = inputFile.readline()
lineNumber += 1
if line == "":
fatal("unexpected end of input")
def getToken():
global token
global line
global inComment
global inDirective
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
line = inputFile.readline()
lineNumber += 1
while line.endswith("\\\n"):
line = line[:-2] + inputFile.readline()
lineNumber += 1
if line == "":
if token == None:
fatal("unexpected end of input")
token = None
return False
n_errors = 0
def error(msg):
global n_errors
sys.stderr.write("%s:%d: %s\n" % (fileName, lineNumber, msg))
n_errors += 1
def fatal(msg):
error(msg)
sys.exit(1)
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 print_enum(tag, constants, storage_class):
print """
%(storage_class)sconst char *
%(tag)s_to_string(uint16_t value)
{
switch (value) {\
""" % {"tag": tag,
"bufferlen": len(tag) + 32,
"storage_class": storage_class}
for constant in constants:
print " case %s: return \"%s\";" % (constant, constant)
print """\
}
return NULL;
}\
""" % {"tag": tag}
def usage():
argv0 = os.path.basename(sys.argv[0])
print '''\
%(argv0)s, for extracting OpenFlow error codes from header files
usage: %(argv0)s FILE [FILE...]
This program reads the header files specified on the command line and
outputs a C source file for translating OpenFlow error codes into
strings, for use as lib/ofp-errors.c in the Open vSwitch source tree.
This program is specialized for reading lib/ofp-errors.h. It will not
work on arbitrary header files without extensions.\
''' % {"argv0": argv0}
sys.exit(0)
def extract_ofp_errors(filenames):
error_types = {}
comments = []
names = []
domain = {}
reverse = {}
for domain_name in ("OF1.0", "OF1.1", "OF1.2", "OF1.3",
"NX1.0", "NX1.1", "NX1.2", "NX1.3"):
domain[domain_name] = {}
reverse[domain_name] = {}
n_errors = 0
expected_errors = {}
global fileName
for fileName in filenames:
global inputFile
global lineNumber
inputFile = open(fileName)
lineNumber = 0
while True:
getLine()
if re.match('enum ofperr', line):
break
while True:
getLine()
if line.startswith('/*') or not line or line.isspace():
continue
elif re.match('}', line):
break
if not line.lstrip().startswith('/*'):
fatal("unexpected syntax between errors")
comment = line.lstrip()[2:].strip()
while not comment.endswith('*/'):
getLine()
if line.startswith('/*') or not line or line.isspace():
fatal("unexpected syntax within error")
comment += ' %s' % line.lstrip('* \t').rstrip(' \t\r\n')
comment = comment[:-2].rstrip()
m = re.match('Expected: (.*)\.$', comment)
if m:
expected_errors[m.group(1)] = (fileName, lineNumber)
continue
m = re.match('((?:.(?!\. ))+.)\. (.*)$', comment)
if not m:
fatal("unexpected syntax between errors")
dsts, comment = m.groups()
getLine()
m = re.match('\s+(?:OFPERR_((?:OFP|NX)[A-Z0-9_]+))(\s*=\s*OFPERR_OFS)?,',
line)
if not m:
fatal("syntax error expecting enum value")
enum = m.group(1)
comments.append(re.sub('\[[^]]*\]', '', comment))
names.append(enum)
for dst in dsts.split(', '):
m = re.match(r'([A-Z0-9.+]+)\((\d+|(0x)[0-9a-fA-F]+)(?:,(\d+))?\)$', dst)
if not m:
fatal("%s: syntax error in destination" % dst)
targets = m.group(1)
if m.group(3):
base = 16
else:
base = 10
type_ = int(m.group(2), base)
if m.group(4):
code = int(m.group(4))
else:
code = None
target_map = {"OF1.0+": ("OF1.0", "OF1.1", "OF1.2", "OF1.3"),
"OF1.1+": ("OF1.1", "OF1.2", "OF1.3"),
"OF1.2+": ("OF1.2", "OF1.3"),
"OF1.3+": ("OF1.3",),
"OF1.0": ("OF1.0",),
"OF1.1": ("OF1.1",),
"OF1.2": ("OF1.2",),
"OF1.3": ("OF1.3",),
"NX1.0+": ("OF1.0", "OF1.1", "OF1.2", "OF1.3"),
"NX1.1+": ("OF1.1", "OF1.2", "OF1.3"),
"NX1.2+": ("OF1.2", "OF1.3"),
"NX1.3+": ("OF1.3",),
"NX1.0": ("OF1.0",),
"NX1.1": ("OF1.1",),
"NX1.2": ("OF1.2",),
"NX1.3": ("OF1.3",)}
if targets not in target_map:
fatal("%s: unknown error domain" % targets)
if targets.startswith('NX') and code < 0x100:
fatal("%s: NX domain code cannot be less than 0x100" % dst)
if targets.startswith('OF') and code >= 0x100:
fatal("%s: OF domain code cannot be greater than 0x100"
% dst)
for target in target_map[targets]:
domain[target].setdefault(type_, {})
if code in domain[target][type_]:
msg = "%d,%d in %s means both %s and %s" % (
type_, code, target,
domain[target][type_][code][0], enum)
if msg in expected_errors:
del expected_errors[msg]
else:
error("%s: %s." % (dst, msg))
sys.stderr.write("%s:%d: %s: Here is the location "
"of the previous definition.\n"
% (domain[target][type_][code][1],
domain[target][type_][code][2],
dst))
else:
domain[target][type_][code] = (enum, fileName,
lineNumber)
if enum in reverse[target]:
error("%s: %s in %s means both %d,%d and %d,%d." %
(dst, enum, target,
reverse[target][enum][0],
reverse[target][enum][1],
type_, code))
reverse[target][enum] = (type_, code)
inputFile.close()
for fn, ln in expected_errors.itervalues():
sys.stderr.write("%s:%d: expected duplicate not used.\n" % (fn, ln))
n_errors += 1
if n_errors:
sys.exit(1)
print """\
/* Generated automatically; do not modify! -*- buffer-read-only: t -*- */
#define OFPERR_N_ERRORS %d
struct ofperr_domain {
const char *name;
uint8_t version;
enum ofperr (*decode)(uint16_t type, uint16_t code);
struct pair errors[OFPERR_N_ERRORS];
};
static const char *error_names[OFPERR_N_ERRORS] = {
%s
};
static const char *error_comments[OFPERR_N_ERRORS] = {
%s
};\
""" % (len(names),
'\n'.join(' "%s",' % name for name in names),
'\n'.join(' "%s",' % re.sub(r'(["\\])', r'\\\1', comment)
for comment in comments))
def output_domain(map, name, description, version):
print """
static enum ofperr
%s_decode(uint16_t type, uint16_t code)
{
switch ((type << 16) | code) {""" % name
found = set()
for enum in names:
if enum not in map:
continue
type_, code = map[enum]
if code is None:
continue
value = (type_ << 16) | code
if value in found:
continue
found.add(value)
print " case (%d << 16) | %d:" % (type_, code)
print " return OFPERR_%s;" % enum
print """\
}
return 0;
}"""
print """
static const struct ofperr_domain %s = {
"%s",
%d,
%s_decode,
{""" % (name, description, version, name)
for enum in names:
if enum in map:
type_, code = map[enum]
if code == None:
code = -1
else:
type_ = code = -1
print " { %2d, %3d }, /* %s */" % (type_, code, enum)
print """\
},
};"""
output_domain(reverse["OF1.0"], "ofperr_of10", "OpenFlow 1.0", 0x01)
output_domain(reverse["OF1.1"], "ofperr_of11", "OpenFlow 1.1", 0x02)
output_domain(reverse["OF1.2"], "ofperr_of12", "OpenFlow 1.2", 0x03)
output_domain(reverse["OF1.3"], "ofperr_of13", "OpenFlow 1.3", 0x04)
if __name__ == '__main__':
if '--help' in sys.argv:
usage()
elif len(sys.argv) < 2:
sys.stderr.write("at least one non-option argument required; "
"use --help for help\n")
sys.exit(1)
else:
extract_ofp_errors(sys.argv[1:])