pytorch/scripts/compile_tests/failures_histogram.py
James Wu 82099ab87b [easy] Reword unexpected success error messages and generated github issues now that we have sentinel files (#120766)
It's a bit annoying to have to read through the test name in verbose mode just to see what the test's sentinel file is actually called when encountering an unexpected success. Now that we have sentinel files, we can directly list the file path from root in the error message.

Pull Request resolved: https://github.com/pytorch/pytorch/pull/120766
Approved by: https://github.com/Skylion007
2024-02-28 11:15:29 +00:00

167 lines
4.9 KiB
Python

import argparse
import re
from common import download_reports, get_testcases, key, open_test_results, skipped_test
from passrate import compute_pass_rate
"""
python failures_histogram.py commit_sha
Analyzes skip reasons for Dynamo tests and prints a histogram with repro
commands. You'll need to provide the commit_sha for a commit on the main branch,
from which we will pull CI test results.
This script requires the `gh` cli. You'll need to install it and then
authenticate with it via `gh auth login` before using this script.
https://docs.github.com/en/github-cli/github-cli/quickstart
"""
def skip_reason(testcase):
for child in testcase.iter():
if child.tag != "skipped":
continue
return child.attrib["message"]
raise AssertionError("no message?")
def skip_reason_normalized(testcase):
for child in testcase.iter():
if child.tag != "skipped":
continue
result = child.attrib["message"].split("\n")[0]
result = result.split(">")[0]
result = re.sub(r"0x\w+", "0xDEADBEEF", result)
result = re.sub(r"MagicMock id='\d+'", "MagicMock id='0000000000'", result)
result = re.sub(r"issues/\d+", "issues/XXX", result)
result = re.sub(r"torch.Size\(\[.*\]\)", "torch.Size([...])", result)
result = re.sub(
r"Could not get qualified name for class '.*'",
"Could not get qualified name for class",
result,
)
return result
raise AssertionError("no message?")
def get_failures(testcases):
skipped = [t for t in testcases if skipped_test(t)]
skipped_dict = {}
for s in skipped:
reason = skip_reason_normalized(s)
if reason not in skipped_dict:
skipped_dict[reason] = []
skipped_dict[reason].append(s)
result = []
for s, v in skipped_dict.items():
result.append((len(v), s, v))
result.sort(reverse=True)
return result
def repro(testcase):
return f"PYTORCH_TEST_WITH_DYNAMO=1 pytest {testcase.attrib['file']} -v -k {testcase.attrib['name']}"
def all_tests(testcase):
return f"{testcase.attrib['file']}::{testcase.attrib['classname']}.{testcase.attrib['name']}"
# e.g. "17c5f69852/eager", "17c5f69852/dynamo"
def failures_histogram(eager_dir, dynamo_dir, verbose=False, format_issues=False):
fail_keys = compute_pass_rate(eager_dir, dynamo_dir)
xmls = open_test_results(dynamo_dir)
testcases = get_testcases(xmls)
testcases = [t for t in testcases if key(t) in fail_keys]
dct = get_failures(testcases)
result = []
for count, reason, testcases in dct:
if verbose:
row = (
count,
reason,
repro(testcases[0]),
[all_tests(t) for t in testcases],
)
else:
row = (count, reason, repro(testcases[0]))
result.append(row)
header = (
"(num_failed_tests, error_msg, sample_test, all_tests)"
if verbose
else "(num_failed_tests, error_msg, sample_test)"
)
print(header)
sum_counts = sum([r[0] for r in result])
for row in result:
if format_issues:
print(as_issue(*row))
else:
print(row)
print("[counts]", sum_counts)
def as_issue(count, msg, repro, tests):
tests = "\n".join(tests)
result = f"""
{'-' * 50}
{count} Dynamo test are failing with \"{msg}\".
## Repro
`{repro}`
You will need to remove the skip or expectedFailure before running the repro command.
This may be just removing a sentinel file from in
[dynamo_expected_failures](https://github.com/pytorch/pytorch/blob/main/test/dynamo_expected_failures)
or [dynamo_skips](https://github.com/pytorch/pytorch/blob/main/test/dynamo_skips).
## Failing tests
Here's a comprehensive list of tests that fail (as of this issue) with the above message:
<details>
<summary>Click me</summary>
```
{tests}
```
</details>
"""
return result
if __name__ == "__main__":
parser = argparse.ArgumentParser(
prog="failures_histogram",
description="See statistics about skipped Dynamo tests",
)
parser.add_argument(
"commit",
help=(
"The commit sha for the latest commit on a PR from which we will "
"pull CI test results, e.g. 7e5f597aeeba30c390c05f7d316829b3798064a5"
),
)
parser.add_argument(
"-v", "--verbose", help="Prints all failing test names", action="store_true"
)
parser.add_argument(
"--format-issues",
help="Prints histogram in a way that they can be copy-pasted as a github issues",
action="store_true",
)
args = parser.parse_args()
# args.format_issues implies verbose=True
verbose = args.verbose
if args.format_issues:
verbose = True
dynamo311, eager311 = download_reports(args.commit, ("dynamo311", "eager311"))
failures_histogram(eager311, dynamo311, verbose, args.format_issues)