CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
227 lines
5.9 KiB
Python
Executable File
227 lines
5.9 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
|
|
# Copyright Advanced Micro Devices, Inc., or its affiliates.
|
|
# SPDX-License-Identifier: MIT
|
|
|
|
"""Load kernel graph from the meta data of an assembly file and render it.
|
|
|
|
Usage:
|
|
|
|
kgraph.py path/to/assembly.s
|
|
|
|
this creates a PDF file at path/to/assembly.pdf. To automatically
|
|
open it (via xdg-open), pass '-x'.
|
|
|
|
"""
|
|
|
|
import argparse
|
|
import re
|
|
import subprocess
|
|
from pathlib import Path
|
|
|
|
import yaml
|
|
from dot_diff import diff_dots
|
|
|
|
|
|
def extract_normalised_asm(path: Path, keep_trailing_comments: bool = False):
|
|
"""Extract normalized ASM from assembly file."""
|
|
source = path.read_text()
|
|
|
|
meta = False
|
|
|
|
asm = []
|
|
for line in source.splitlines():
|
|
if keep_trailing_comments:
|
|
line = line.strip()
|
|
if line.startswith("//"):
|
|
continue
|
|
else:
|
|
double_slash = line.find("//")
|
|
if double_slash != -1:
|
|
line = line[:double_slash]
|
|
line = line.rstrip()
|
|
if "kernel_graph_dot" in line:
|
|
continue
|
|
if ".amdgpu_metadata" in line:
|
|
meta = True
|
|
if line and not meta:
|
|
asm.append(line)
|
|
if ".end_amdgpu_metadata" in line:
|
|
meta = False
|
|
|
|
return "\n".join(asm)
|
|
|
|
|
|
def extract_asm_dot(path: Path):
|
|
"""Extract .kernel_graph meta data from assembly file."""
|
|
source = path.read_text()
|
|
beginMatch = re.search(r"^---$", source, re.MULTILINE)
|
|
endMatch = re.search(r"^\.\.\.$", source, re.MULTILINE)
|
|
|
|
assert beginMatch is not None
|
|
assert endMatch is not None
|
|
|
|
beginPos = beginMatch.span()[1]
|
|
endPos = endMatch.span()[0]
|
|
assert beginPos < endPos
|
|
|
|
meta = yaml.load(source[beginPos:endPos], Loader=yaml.CSafeLoader)
|
|
kernel = meta["amdhsa.kernels"][0]
|
|
|
|
def try_get(key):
|
|
if key in kernel:
|
|
return kernel[key]
|
|
return None
|
|
|
|
return try_get(".name"), try_get(".kernel_graph_dot"), try_get(".kernel_graph")
|
|
|
|
|
|
def extract_log_dots(path: Path):
|
|
source = path.read_text()
|
|
source_clean = ""
|
|
prev_end = 0
|
|
dots = []
|
|
for graph in re.finditer("digraph {", source):
|
|
this_graph = source[graph.start() :].partition("\n\n")[0]
|
|
before_graph = source[prev_end : graph.start()]
|
|
source_clean += before_graph
|
|
prev_end = graph.start() + len(this_graph)
|
|
|
|
dots.append(this_graph)
|
|
|
|
source_clean += source[prev_end:]
|
|
|
|
return dots, source_clean
|
|
|
|
|
|
def write_dot(dot: str, fname: Path):
|
|
out_fname = fname.with_suffix(".dot")
|
|
out_fname.write_text(dot)
|
|
print(f"Wrote {out_fname}")
|
|
return out_fname
|
|
|
|
|
|
def render_dot(fname: Path, dot: str):
|
|
"""Render graph."""
|
|
with fname.open("w") as out:
|
|
subprocess.run(["dot", "-Tpdf"], input=dot.encode(), stdout=out)
|
|
print(f"Wrote {fname}")
|
|
|
|
|
|
def open_dot(fname):
|
|
subprocess.call(["xdg-open", str(fname)])
|
|
|
|
|
|
def open_code(fname):
|
|
subprocess.call(["code", str(fname)])
|
|
|
|
|
|
def process_dot(
|
|
dot: str,
|
|
out_path: Path,
|
|
code_open: bool,
|
|
dot_only: bool,
|
|
xdg_open: bool,
|
|
):
|
|
dotfile = write_dot(dot, out_path)
|
|
if code_open:
|
|
open_code(dotfile)
|
|
if not dot_only:
|
|
rendered_fname = out_path.with_suffix(".pdf")
|
|
render_dot(rendered_fname, dot)
|
|
if xdg_open:
|
|
open_dot(rendered_fname)
|
|
|
|
|
|
def process_serialized_graph(graph: dict, out_path: Path):
|
|
with out_path.with_suffix(".yaml").open("w") as f:
|
|
yaml.dump(graph, f, default_flow_style=None, Dumper=yaml.CSafeDumper)
|
|
print(f"Wrote {f.name}")
|
|
|
|
|
|
def process_normalized_asm(path: Path, keep_trailing_comments: bool = True):
|
|
asm = Path(path.stem + "_normalized.s")
|
|
asm.write_text(extract_normalised_asm(path, keep_trailing_comments))
|
|
print(f"Wrote {asm}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
parser = argparse.ArgumentParser(description="Extract and render kernel graph.")
|
|
parser.add_argument("fname", type=str, help="Assembly or log file name")
|
|
parser.add_argument("-d", "--dot-only", default=False, action="store_true")
|
|
parser.add_argument(
|
|
"-x",
|
|
"--xdg-open",
|
|
default=False,
|
|
action="store_true",
|
|
help="Open with xdg-open after generating",
|
|
)
|
|
parser.add_argument(
|
|
"-c",
|
|
"--code-open",
|
|
default=False,
|
|
action="store_true",
|
|
help="Open .dot with VSCode after generating",
|
|
)
|
|
parser.add_argument(
|
|
"-o",
|
|
"--output",
|
|
type=str,
|
|
help="Output file base name",
|
|
)
|
|
parser.add_argument(
|
|
"-k",
|
|
"--keep-trailing",
|
|
default=False,
|
|
action="store_true",
|
|
help="Keep trailing comments in normalized assembly file.",
|
|
)
|
|
parser.add_argument(
|
|
"--omit_diff",
|
|
default=False,
|
|
action="store_true",
|
|
help="Omit dot diff coloring",
|
|
)
|
|
|
|
args = parser.parse_args()
|
|
path = Path(args.fname)
|
|
|
|
foutput = path
|
|
if args.output is not None:
|
|
foutput = Path(args.output)
|
|
|
|
if path.suffix == ".s":
|
|
_, dot, graph = extract_asm_dot(path)
|
|
process_dot(
|
|
dot,
|
|
foutput,
|
|
args.code_open,
|
|
args.dot_only,
|
|
args.xdg_open,
|
|
)
|
|
if graph is not None and not args.dot_only:
|
|
process_serialized_graph(graph, foutput)
|
|
|
|
process_normalized_asm(path, args.keep_trailing)
|
|
|
|
elif path.suffix == ".log":
|
|
dots, source_clean = extract_log_dots(path)
|
|
|
|
foutput_clean = Path(str(foutput) + "_clean.log")
|
|
foutput_clean.write_text(source_clean)
|
|
|
|
if not args.omit_diff:
|
|
dots = diff_dots(dots)
|
|
for i, dot in enumerate(dots):
|
|
serial_str = f"_{i:04d}"
|
|
foutput_serial = Path(str(foutput) + serial_str)
|
|
process_dot(
|
|
dot,
|
|
foutput_serial,
|
|
args.code_open,
|
|
args.dot_only,
|
|
args.xdg_open,
|
|
)
|
|
else:
|
|
print("Unknown file extension")
|