Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 36 additions & 3 deletions .automation_scripts/pytorch-unit-test-scripts/download_testlogs
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,7 @@ def _shorten_unzipped_dirs():
failing job from the parity summary.
"""
from pathlib import Path
job_ids = []
for d in sorted(Path(".").glob("unzipped-*")):
if not d.is_dir():
continue
Expand All @@ -346,11 +347,39 @@ def _shorten_unzipped_dirs():
job_id_match = re.search(r'_(\d{6,})(?:\.zip)?$', d.name)
if job_id_match:
short_name += f"_{job_id_match.group(1)}"
job_ids.append(job_id_match.group(1))
if not Path(short_name).exists():
d.rename(short_name)
print(f" Renamed {d.name} -> {short_name}")
else:
print(f" WARNING: {short_name} already exists, keeping {d.name}")
return job_ids

def _record_shard_run_ids(job_ids, workflow_run_id):
"""Persist a job_id -> upstream run_id map ("_wf_run_ids.json") next to the
shard dirs so summarize_xml_testreports.py can build a correct job URL for
each shard.

The default/distributed/inductor configs resolve to DIFFERENT upstream runs
(e.g. mi350 default+inductor come from a trunk push while distributed comes
from periodic / a trunk fallback), yet they all unzip into the same rocm_xml
folder. The legacy single "_wf_run_id" (last write wins) therefore mislabels
shards from the other runs, producing "busted" job URLs that point at a run
the job never belonged to. Recording the run id per job id fixes that.
"""
if not job_ids:
return
mapping = {}
if os.path.isfile("_wf_run_ids.json"):
try:
with open("_wf_run_ids.json") as f:
mapping = json.load(f)
except Exception:
mapping = {}
for jid in job_ids:
mapping[str(jid)] = str(workflow_run_id)
with open("_wf_run_ids.json", "w") as f:
json.dump(mapping, f)

def download_xml_files(workflow_run_id, workflow_run_attempts, prefixes=[], allowed_substrings=None):
# Get from S3 artifacts.
Expand Down Expand Up @@ -410,8 +439,12 @@ def download_xml_files(workflow_run_id, workflow_run_attempts, prefixes=[], allo
for path in artifact_paths:
unzip(path)

_shorten_unzipped_dirs()
job_ids = _shorten_unzipped_dirs()
# Record which upstream run each shard's job id came from so job URLs are
# built per-shard (different configs come from different runs).
_record_shard_run_ids(job_ids, workflow_run_id)

# Kept for backward compatibility with older summarize layouts.
with open("_wf_run_id", "w") as f:
f.write(str(workflow_run_id))

Expand Down Expand Up @@ -526,7 +559,7 @@ def parse_args():
parser.add_argument('--no_rocm', action='store_true')
parser.add_argument('--no_cuda', action='store_true')
parser.add_argument('--pr_id', type=int, help='The pull request ID')
parser.add_argument('--arch', type=str, choices=['mi200', 'mi300', 'mi350', 'navi31', 'nightly'], default='mi350', help='ROCm GPU architecture (mi200, mi300, mi350, navi31, or nightly, default: mi350)')
parser.add_argument('--arch', type=str, choices=['mi200', 'mi300', 'mi350', 'navi31', 'preview'], default='mi350', help='ROCm GPU architecture (mi200, mi300, mi350, navi31, or preview, default: mi350)')
parser.add_argument('--include_inductor_periodic', action='store_true', help='Also download inductor-periodic benchmark artifacts (into a separate directory, not included in parity CSV)')
parser.add_argument('--baseline_sha', type=str, help='Baseline commit SHA to compare against. Downloads the same ROCm workflows for this commit into baseline_xml/.')
return parser.parse_args()
Expand Down Expand Up @@ -615,7 +648,7 @@ def main():
# in, job-name prefixes, shard counts and fallbacks) comes from
# parity_job_config.json - see PARITY_CONFIG at the top of this file.
global ROCmWorkflowNames
arch = args.arch # 'mi200', 'mi300', 'mi350', 'navi31', or 'nightly'
arch = args.arch # 'mi200', 'mi300', 'mi350', 'navi31', or 'preview'
arch_config = PARITY_CONFIG["rocm"][arch]

ROCmWorkflowNames, rocm_job_prefix, arch_fallbacks = parity_config_views(arch_config)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
"default": [{ "workflow": "rocm-mi300", "job_prefix": "linux-noble-rocm-py3.12-mi300" }],
"distributed": [{ "workflow": "periodic-rocm-mi300", "job_prefix": "linux-noble-rocm-py3.12-mi300" }],
"inductor": [{ "workflow": "inductor-rocm-mi300", "job_prefix": "linux-noble-rocm-py3.12-mi300" }],
"shard_counts": { "default": 6, "distributed": 3, "inductor": 2 },
"shard_counts": { "default": 8, "distributed": 3, "inductor": 2 },
"checkrun_regex": "rocm.*mi300.*/ test [(](default|distributed|inductor),"
},
"mi200": {
Expand All @@ -64,12 +64,12 @@
"shard_counts": { "default": 2, "distributed": 3, "inductor": 2 },
"checkrun_regex": "rocm.*navi31.*/ test [(]default,"
},
"nightly": {
"default": [{ "workflow": "rocm-nightly", "job_prefix": "linux-noble-rocm-nightly-py3.12-mi350" }],
"distributed": [{ "workflow": "rocm-nightly", "job_prefix": "linux-noble-rocm-nightly-py3.12-mi350" }],
"inductor": [{ "workflow": "rocm-nightly", "job_prefix": "linux-noble-rocm-nightly-py3.12-mi350" }],
"shard_counts": { "default": 6, "distributed": 3, "inductor": 2 },
"checkrun_regex": "rocm-nightly.*/ test [(](default|distributed|inductor),"
"preview": {
"default": [{ "workflow": "rocm-preview", "job_prefix": "linux-noble-rocm-preview-py3.12-mi350" }],
"distributed": [{ "workflow": "rocm-preview", "job_prefix": "linux-noble-rocm-preview-py3.12-mi350" }],
"inductor": [{ "workflow": "rocm-preview", "job_prefix": "linux-noble-rocm-preview-py3.12-mi350" }],
"shard_counts": { "default": 8, "distributed": 3, "inductor": 2 },
"checkrun_regex": "rocm-preview.*/ test [(](default|distributed|inductor),"
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import argparse
import csv
import json
import os
import re
import pandas as pd
Expand Down Expand Up @@ -73,11 +74,27 @@ def parse_xml_reports_as_dict(workflow_run_id, workflow_run_attempt, tag, path="
test_config = ""
test_cases = {}

# download_testlogs writes the upstream pytorch CI workflow run id
# into "_wf_run_id" alongside the shard dirs. We combine it with each
# shard dir's trailing "_<job_id>" to form the URL
# https://github.com/pytorch/pytorch/actions/runs/<wf>/job/<job_id>
# download_testlogs records the upstream pytorch CI run id for each shard's
# job id in "_wf_run_ids.json" (job_id -> run_id) alongside the shard dirs,
# so we can build the URL
# https://github.com/pytorch/pytorch/actions/runs/<run_id>/job/<job_id>
# surfaced as the "Job ID" column in the FAILED TESTS table.
#
# A single run id is NOT enough: the default/distributed/inductor configs
# resolve to DIFFERENT upstream runs (e.g. mi350 default+inductor come from
# a trunk push while distributed comes from periodic/a trunk fallback), so
# pairing every shard's job id with one run id produced links to a run the
# job never belonged to (a "busted" URL that 404s). We look each job id up
# in the per-job map and fall back to the legacy single "_wf_run_id" file
# only when the map has no entry (older downloads / backward compat).
run_id_by_job = {}
run_ids_file = os.path.join(path, "_wf_run_ids.json")
if os.path.isfile(run_ids_file):
try:
with open(run_ids_file) as f:
run_id_by_job = {str(k): str(v) for k, v in json.load(f).items()}
except Exception:
run_id_by_job = {}
wf_run_id = ""
wf_id_file = os.path.join(path, "_wf_run_id")
if os.path.isfile(wf_id_file):
Expand All @@ -96,9 +113,13 @@ def parse_xml_reports_as_dict(workflow_run_id, workflow_run_attempt, tag, path="
test_config = TestConfigName.inductor.name
shard = _extract_shard(dir)
jid = re.search(r'_(\d+)$', dir)
job_id = jid.group(1) if jid else ""
# Prefer the run id recorded for this specific job; fall back to the
# single legacy run id so we never regress older download layouts.
shard_run_id = run_id_by_job.get(job_id, wf_run_id)
job_url = (
f"https://github.com/pytorch/pytorch/actions/runs/{wf_run_id}/job/{jid.group(1)}"
if wf_run_id and jid else ""
f"https://github.com/pytorch/pytorch/actions/runs/{shard_run_id}/job/{job_id}"
if shard_run_id and job_id else ""
)
for xml_report in Path(new_dir).glob("**/*.xml"):
try:
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/parity.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ on:
required: false
type: string
arch:
description: 'ROCm architectures, comma or space separated. Options: mi350, mi300, mi200, nightly, navi31. Example: "nightly, mi350" or "mi300"'
description: 'ROCm architectures, comma or space separated. Options: mi350, mi300, mi200, navi31, preview. Example: "preview, mi350" or "mi300"'
required: false
default: 'mi350, mi300, mi200'
type: string
Expand Down Expand Up @@ -72,7 +72,7 @@ on:
type: boolean
# summarize_xml_testreports flags
set1_name:
description: 'Label for ROCm columns in output CSV. Examples: rocm, nightly, mi300. Default: rocm'
description: 'Label for ROCm columns in output CSV. Examples: rocm, preview, mi300. Default: rocm'
required: false
default: 'rocm'
type: string
Expand Down