Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Throw an error when reference files are missing. #2926

Merged
merged 6 commits into from Oct 21, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
75 changes: 49 additions & 26 deletions backends/p4test/run-p4-sample.py
Expand Up @@ -16,7 +16,7 @@
# Runs the compiler on a sample P4 V1.2 program


from subprocess import Popen,PIPE
from subprocess import Popen, PIPE
from threading import Thread
import errno
import sys
Expand All @@ -32,6 +32,7 @@
SUCCESS = 0
FAILURE = 1


class Options(object):
def __init__(self):
self.binary = "" # this program's name
Expand All @@ -46,6 +47,7 @@ def __init__(self):
self.runDebugger_skip = 0
self.generateP4Runtime = False


def usage(options):
name = options.binary
print(name, "usage:")
Expand All @@ -59,27 +61,32 @@ def usage(options):
print(" -a \"args\": pass args to the compiler")
print(" --p4runtime: generate P4Info message in text format")


def isError(p4filename):
# True if the filename represents a p4 program that should fail
return "_errors" in p4filename


def ignoreStderr(options):
for line in open(options.p4filename):
if "P4TEST_IGNORE_STDERR" in line:
return True
return False


class Local(object):
# object to hold local vars accessable to nested functions
pass


def run_timeout(options, args, timeout, stderr):
if options.verbose:
print(args[0], args[len(args) - 1]) # handy for manual cut-and-paste
print(" ".join(args))
local = Local()
local.process = None
local.filter = None

def target():
procstderr = None
if stderr is not None:
Expand All @@ -93,7 +100,7 @@ def target():
# available don't seem to actually work.
local.filter = Popen(['sed', '-E',
r's|^[-[:alnum:][:punct:][:space:]_/]*/([-[:alnum:][:punct:][:space:]_]+\.[ph]4?[:(][[:digit:]]+)|\1|'],
stdin=PIPE, stdout=outfile)
stdin=PIPE, stdout=outfile)
procstderr = local.filter.stdin
local.process = Popen(args, stderr=procstderr)
local.process.wait()
Expand All @@ -116,8 +123,10 @@ def target():
print("Exit code ", local.process.returncode)
return local.process.returncode


timeout = 10 * 60


def compare_files(options, produced, expected, ignore_case):
# p4info files should not change
if options.replace and "p4info" not in produced:
Expand All @@ -129,23 +138,24 @@ def compare_files(options, produced, expected, ignore_case):
if options.verbose:
print("Comparing", expected, "and", produced)

args = "-B -u -w";
args = "-B -u -w"
if ignore_case:
args = args + " -i";
args = args + " -i"
cmd = ("diff " + args + " " + expected + " " + produced + " >&2")
if options.verbose:
print(cmd)
exitcode = subprocess.call(cmd, shell=True);
exitcode = subprocess.call(cmd, shell=True)
if exitcode == 0:
return SUCCESS
else:
return FAILURE


def recompile_file(options, produced, mustBeIdentical):
# Compile the generated file a second time
secondFile = produced + "-x";
secondFile = produced + "-x"
args = ["./p4test", "-I.", "--pp", secondFile, "--std", "p4-16", produced] + \
options.compilerOptions
options.compilerOptions
if options.runDebugger:
if options.runDebugger_skip > 0:
options.runDebugger_skip = options.runDebugger_skip - 1
Expand All @@ -156,29 +166,39 @@ def recompile_file(options, produced, mustBeIdentical):
if result != SUCCESS:
return result
if mustBeIdentical:
result = compare_files(options, produced, secondFile, false)
result = compare_files(options, produced, secondFile, False)
return result


def check_generated_files(options, tmpdir, expecteddir):
files = os.listdir(tmpdir)
for file in files:
if options.verbose:
print("Checking", file)
produced = tmpdir + "/" + file
expected = expecteddir + "/" + file
if not os.path.isfile(expected):

if options.replace:
# Only create files when explicitly asked to do so
if options.verbose:
print("Expected file does not exist; creating", expected)
shutil.copy2(produced, expected)
else:
result = compare_files(options, produced, expected, file[-7:] == "-stderr")
if result != SUCCESS and (file[-7:] != "-stderr" or not ignoreStderr(options)):
return result
elif not os.path.isfile(expected):
# The file is missing and we do not replace. This is an error.
print(
"Missing reference for file %s. Please rerun the test with the -f option turned on or rerun all tests using \"P4TEST_REPLACE=True make check\"." % expected)
return FAILURE
result = compare_files(
options, produced, expected, file[-7:] == "-stderr")
if result != SUCCESS and (file[-7:] != "-stderr" or not ignoreStderr(options)):
return result
return SUCCESS


def file_name(tmpfolder, base, suffix, ext):
return tmpfolder + "/" + base + "-" + suffix + ext


def process_file(options, argv):
assert isinstance(options, Options)

Expand All @@ -201,15 +221,15 @@ def process_file(options, argv):
expected_dirname = dirname.replace("p4_16/", "p4_16_outputs/", 1)
elif loops_unrolling:
expected_dirname = dirname + "_outputs/parser-unroll"
else:
else:
expected_dirname = dirname + "_outputs" # expected outputs are here
if not os.path.exists(expected_dirname):
os.makedirs(expected_dirname)

# We rely on the fact that these keys are in alphabetical order.
rename = { "FrontEndDump": "first",
"FrontEndLast": "frontend",
"MidEndLast": "midend" }
rename = {"FrontEndDump": "first",
"FrontEndLast": "frontend",
"MidEndLast": "midend"}

if options.verbose:
print("Writing temporary files into ", tmpdir)
Expand Down Expand Up @@ -260,7 +280,7 @@ def getArch(path):
args.extend(["--p4runtime-entries-files", p4runtimeEntriesFile])

if "p4_14" in options.p4filename or "v1_samples" in options.p4filename:
args.extend(["--std", "p4-14"]);
args.extend(["--std", "p4-14"])
args.extend(argv)
if options.runDebugger:
if options.runDebugger_skip > 0:
Expand Down Expand Up @@ -289,9 +309,9 @@ def getArch(path):
lastFile = None

for k in sorted(rename.keys()):
files = glob.glob(tmpdir + "/" + base + "*" + k + "*.p4");
files = glob.glob(tmpdir + "/" + base + "*" + k + "*.p4")
if len(files) > 1:
print("Multiple files matching", k);
print("Multiple files matching", k)
elif len(files) == 1:
file = files[0]
if os.path.isfile(file):
Expand All @@ -300,7 +320,7 @@ def getArch(path):
lastFile = newName

if (result == SUCCESS):
result = check_generated_files(options, tmpdir, expected_dirname);
result = check_generated_files(options, tmpdir, expected_dirname)
if (result == SUCCESS) and (not expected_error):
result = recompile_file(options, ppfile, False)
if (result == SUCCESS) and (not expected_error) and (lastFile is not None) and (arch is not "psa"):
Expand All @@ -315,13 +335,15 @@ def getArch(path):
shutil.rmtree(tmpdir)
return result


def isdir(path):
try:
return stat.S_ISDIR(os.stat(path).st_mode)
except OSError:
return False;
return False

# main

######################### main

def main(argv):
options = Options()
Expand Down Expand Up @@ -353,7 +375,7 @@ def main(argv):
usage(options)
sys.exit(FAILURE)
else:
options.compilerOptions += argv[1].split();
options.compilerOptions += argv[1].split()
argv = argv[1:]
elif argv[0][1] == 'D' or argv[0][1] == 'I' or argv[0][1] == 'T':
options.compilerOptions.append(argv[0])
Expand All @@ -372,10 +394,10 @@ def main(argv):
if 'P4TEST_REPLACE' in os.environ:
options.replace = True

options.p4filename=argv[-1]
options.p4filename = argv[-1]
options.testName = None
if options.p4filename.startswith(options.compilerSrcdir):
options.testName = options.p4filename[len(options.compilerSrcdir):];
options.testName = options.p4filename[len(options.compilerSrcdir):]
if options.testName.startswith('/'):
options.testName = options.testName[1:]
if options.testName.endswith('.p4'):
Expand All @@ -387,5 +409,6 @@ def main(argv):

sys.exit(result)


if __name__ == "__main__":
main(sys.argv)
57 changes: 57 additions & 0 deletions testdata/p4_16_samples/gauntlet_inout_slice_table_key-bmv2.p4
@@ -0,0 +1,57 @@
#include <core.p4>
#include <v1model.p4>

header ethernet_t {
bit<48> dst_addr;
bit<48> src_addr;
bit<16> eth_type;
}

struct Headers {
ethernet_t eth_hdr;
}

struct Meta {}

parser p(packet_in pkt, out Headers hdr, inout Meta m, inout standard_metadata_t sm) {
state start {
transition parse_hdrs;
}
state parse_hdrs {
pkt.extract(hdr.eth_hdr);
transition accept;
}
}

control ingress(inout Headers h, inout Meta m, inout standard_metadata_t sm) {
bit<8> tmp_val = 1;
action simple_action(inout bit<4> dummy) {
}
table simple_table {
key = {
tmp_val : exact @name("dummy") ;
}
actions = {
NoAction();
}
}
apply {
simple_action(tmp_val[7:4]);
if (simple_table.apply().hit) {
h.eth_hdr.eth_type = 1;
}
}
}

control vrfy(inout Headers h, inout Meta m) { apply {} }

control update(inout Headers h, inout Meta m) { apply {} }

control egress(inout Headers h, inout Meta m, inout standard_metadata_t sm) { apply {} }

control deparser(packet_out pkt, in Headers h) {
apply {
pkt.emit(h.eth_hdr);
}
}
V1Switch(p(), vrfy(), ingress(), egress(), update(), deparser()) main;
@@ -0,0 +1,71 @@
#include <core.p4>
#define V1MODEL_VERSION 20180101
#include <v1model.p4>

header ethernet_t {
bit<48> dst_addr;
bit<48> src_addr;
bit<16> eth_type;
}

struct Headers {
ethernet_t eth_hdr;
}

struct Meta {
}

parser p(packet_in pkt, out Headers hdr, inout Meta m, inout standard_metadata_t sm) {
state start {
transition parse_hdrs;
}
state parse_hdrs {
pkt.extract<ethernet_t>(hdr.eth_hdr);
transition accept;
}
}

control ingress(inout Headers h, inout Meta m, inout standard_metadata_t sm) {
bit<8> tmp_val = 8w1;
action simple_action(inout bit<4> dummy) {
}
table simple_table {
key = {
tmp_val: exact @name("dummy") ;
}
actions = {
NoAction();
}
default_action = NoAction();
}
apply {
simple_action(tmp_val[7:4]);
if (simple_table.apply().hit) {
h.eth_hdr.eth_type = 16w1;
}
}
}

control vrfy(inout Headers h, inout Meta m) {
apply {
}
}

control update(inout Headers h, inout Meta m) {
apply {
}
}

control egress(inout Headers h, inout Meta m, inout standard_metadata_t sm) {
apply {
}
}

control deparser(packet_out pkt, in Headers h) {
apply {
pkt.emit<ethernet_t>(h.eth_hdr);
}
}

V1Switch<Headers, Meta>(p(), vrfy(), ingress(), egress(), update(), deparser()) main;