-
Notifications
You must be signed in to change notification settings - Fork 444
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
[P4testgen] Coverage script #3853
Merged
Merged
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
18c42f6
Benchmarking exploration strategies
hannelita a86d317
generating extra file with 1000 tests and inspecting when cov 1 is ac…
hannelita 019ac58
Initial data collection and --exhaust option
hannelita 9429e87
Refactoring and test case generation fixes.
fruffy e69821e
Minor fix.
fruffy File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,218 @@ | ||
#!/usr/bin/env python3 | ||
|
||
import argparse | ||
import subprocess | ||
import random | ||
import csv | ||
import sys | ||
import re | ||
import tempfile | ||
from pathlib import Path | ||
import datetime | ||
|
||
FILE_DIR = Path(__file__).parent.resolve() | ||
TOOLS_PATH = FILE_DIR.joinpath("../../../tools") | ||
sys.path.append(str(TOOLS_PATH)) | ||
import testutils | ||
|
||
TESTGEN_BIN = FILE_DIR.joinpath("../../../build/p4testgen") | ||
OUTPUT_DIR = FILE_DIR.joinpath("../../../build/results") | ||
# generate random seeds, increase the number for extra sampling | ||
ITERATIONS = 1 | ||
MAX_TESTS = 0 | ||
|
||
PARSER = argparse.ArgumentParser() | ||
PARSER.add_argument( | ||
"-p", | ||
"--p4-program", | ||
dest="p4_program", | ||
required=True, | ||
help="The P4 file to measure coverage on.", | ||
) | ||
PARSER.add_argument( | ||
"-o", | ||
"--out-dir", | ||
dest="out_dir", | ||
default=OUTPUT_DIR, | ||
help="The output folder where all tests are dumped.", | ||
) | ||
PARSER.add_argument( | ||
"-i", | ||
"--iterations", | ||
dest="iterations", | ||
default=ITERATIONS, | ||
type=int, | ||
help="How many iterations to run.", | ||
) | ||
PARSER.add_argument( | ||
"-m", | ||
"--max-tests", | ||
dest="max_tests", | ||
default=MAX_TESTS, | ||
type=int, | ||
help="How many tests to run for each test.", | ||
) | ||
PARSER.add_argument( | ||
"-s", | ||
"--seed", | ||
dest="seed", | ||
default=0, | ||
type=int, | ||
help="The random seed for the tests.", | ||
) | ||
PARSER.add_argument( | ||
"-b", | ||
"--testgen-bin", | ||
dest="p4testgen_bin", | ||
default=TESTGEN_BIN, | ||
help="Specifies the testgen binary.", | ||
) | ||
|
||
|
||
class Options: | ||
def __init__(self): | ||
self.p4testgen_bin = None # The P4Testgen binary. | ||
self.p4_program = None # P4 Program that is being measured. | ||
self.out_dir = None # The output directory. | ||
self.seed = None # Program seed. | ||
self.max_tests = None # The max tests parameter. | ||
|
||
|
||
class TestArgs: | ||
def __init__(self): | ||
self.seed = None # The seed for this particular run. | ||
self.extra_args = None # Extra arguments for P4Testgen execution. | ||
self.test_dir = None # The testing directory associated with this test run. | ||
self.strategy = None # The exploration strategy to execute. | ||
|
||
|
||
def get_test_files(input_dir, extension): | ||
test_files = input_dir.glob(extension) | ||
|
||
def natsort(s): | ||
return [int(t) if t.isdigit() else t.lower() for t in re.split("(\d+)", str(s))] | ||
|
||
test_files = sorted(test_files, key=natsort) | ||
return test_files | ||
|
||
|
||
def parse_stmt_cov(test_file): | ||
with test_file.open("r") as file_handle: | ||
for line in file_handle.readlines(): | ||
if "Current statement coverage:" in line: | ||
covstr = line.replace('metadata: "Current statement coverage: ', "") | ||
covstr = covstr.replace('"\n', "") | ||
return covstr | ||
return None | ||
|
||
|
||
def parse_timestamp(test_file): | ||
with test_file.open("r") as file_handle: | ||
for line in file_handle.readlines(): | ||
if "Date generated" in line: | ||
datestr = line.replace('metadata: "Date generated: ', "") | ||
datestr = datestr.replace('"\n', "") | ||
datestr = datestr.strip() | ||
return datestr | ||
return None | ||
|
||
|
||
def run_strategies_for_max_tests(data_row, options, test_args): | ||
|
||
cmd = ( | ||
f"{options.p4testgen_bin} --target bmv2 --arch v1model --std p4-16" | ||
f" -I/p4/p4c/build/p4include --test-backend PROTOBUF --seed {test_args.seed} " | ||
f"--max-tests {options.max_tests} --out-dir {test_args.test_dir}" | ||
f" --exploration-strategy {test_args.strategy} --stop-metric MAX_STATEMENT_COVERAGE " | ||
f" {test_args.extra_args} {options.p4_program}" | ||
) | ||
start_timestamp = datetime.datetime.now() | ||
try: | ||
# TODO: Use result | ||
_ = subprocess.run( | ||
cmd, | ||
shell=True, | ||
universal_newlines=True, | ||
capture_output=True, | ||
check=True, | ||
) | ||
except subprocess.CalledProcessError as e: | ||
returncode = e.returncode | ||
print(f"Error executing {e.cmd} {returncode}:\n{e.stdout}\n{e.stderr}") | ||
sys.exit(1) | ||
end_timestamp = datetime.datetime.now() | ||
|
||
test_files = get_test_files(test_args.test_dir, "*.proto") | ||
# Get the statement coverage of the last test generated. | ||
last_test = test_files[-1] | ||
statements_cov = parse_stmt_cov(last_test) | ||
|
||
time_needed = (end_timestamp - start_timestamp).total_seconds() | ||
num_tests = len(test_files) | ||
data_row.append(statements_cov) | ||
data_row.append(str(num_tests)) | ||
data_row.append(time_needed) | ||
print( | ||
f"Pct Statements Covered: {statements_cov} Number of tests: {num_tests} Time needed: {time_needed}" | ||
) | ||
return data_row | ||
|
||
|
||
def main(args): | ||
|
||
options = Options() | ||
options.p4_program = Path(testutils.check_if_file(args.p4_program)) | ||
options.max_tests = args.max_tests | ||
options.out_dir = Path(args.out_dir).absolute() | ||
options.seed = args.seed | ||
options.p4testgen_bin = Path(testutils.check_if_file(args.p4testgen_bin)) | ||
|
||
# 7189 is an example of a good seed, which gets cov 1 with less than 100 tests | ||
# in random access stack. | ||
random.seed(options.seed) | ||
seeds = [] | ||
for _ in range(args.iterations): | ||
seed = random.randint(0, sys.maxsize) | ||
seeds.append(seed) | ||
print(f"Chosen seeds: {seeds}") | ||
p4_program_name = options.p4_program.stem | ||
|
||
header = [ | ||
"seed", | ||
"coverage", | ||
"num_tests", | ||
"time (s)", | ||
] | ||
|
||
strategies = ["RANDOM_ACCESS_STACK", "RANDOM_ACCESS_MAX_COVERAGE", "INCREMENTAL_STACK"] | ||
config = { | ||
"INCREMENTAL_STACK": "", | ||
"RANDOM_ACCESS_STACK": "--pop-level 3", | ||
"RANDOM_ACCESS_MAX_COVERAGE": "--saddle-point 5", | ||
} | ||
# csv results file path | ||
for strategy in strategies: | ||
test_dir = options.out_dir.joinpath(f"{strategy}") | ||
testutils.check_and_create_dir(test_dir) | ||
results_path_max = test_dir.joinpath(f"coverage_results_{p4_program_name}_{strategy}.csv") | ||
with open(results_path_max, "w", encoding="utf-8") as f: | ||
writer = csv.writer(f) | ||
writer.writerow(header) | ||
for seed in seeds: | ||
data_row = [seed] | ||
print( | ||
f"Seed {seed} Generating metrics for {strategy} up to {options.max_tests} tests" | ||
) | ||
test_args = TestArgs() | ||
test_args.seed = seed | ||
test_args.test_dir = Path(tempfile.mkdtemp(dir=test_args.test_dir)) | ||
test_args.strategy = strategy | ||
test_args.extra_args = config[strategy] | ||
data_row = run_strategies_for_max_tests(data_row, options, test_args) | ||
writer.writerow(data_row) | ||
|
||
|
||
if __name__ == "__main__": | ||
# Parse options and process argv | ||
arguments, argv = PARSER.parse_known_args() | ||
main(arguments) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@fruffy if we want results for 10 seeds, we can just change this constant to 10