Skip to content

Commit 2323987

Browse files
FindHaometa-codesync[bot]
authored andcommitted
Add query/sass module for SASS extraction from cubin files (#195)
Summary: Pull Request resolved: #195 New module: query/sass.py for extracting SASS assembly from cubin files using nvdisasm. Provides both programmatic API and CLI command. - query/sass.py: SassOutput dataclass, dump_sass(), dump_sass_to_file(). Runs `nvdisasm -g -c` for source-level debug info and line comments. Can be used standalone or by other modules (e.g., AI deadlock analysis). - query/cli.py: Added sass_command CLI (cutracer sass <cubin>). Options: --output, --stdout, --no-source-info, --no-line-info, --timeout. - query/__init__.py: Exported SassOutput, dump_sass, dump_sass_to_file. - cli.py: Registered sass as top-level command. Reviewed By: xuzhao9 Differential Revision: D97521606 fbshipit-source-id: 556ca976a5c11ef1f1e173a624502ecf0066d2a8
1 parent 8829439 commit 2323987

5 files changed

Lines changed: 503 additions & 2 deletions

File tree

python/cutracer/cli.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
import click
1313
from cutracer.analyze.cli import analyze_command
14-
from cutracer.query.cli import query_command
14+
from cutracer.query.cli import query_command, sass_command
1515
from cutracer.reduce.cli import reduce_command
1616
from cutracer.runner import trace_command
1717
from cutracer.validation.cli import compare_command, validate_command
@@ -54,6 +54,7 @@ def main() -> None:
5454
main.add_command(compare_command)
5555
main.add_command(query_command)
5656
main.add_command(reduce_command)
57+
main.add_command(sass_command)
5758
main.add_command(trace_command)
5859
main.add_command(validate_command)
5960

python/cutracer/query/__init__.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
- StreamingGrouper: Memory-efficient grouped queries
1111
- Formatters: Output formatting for table/json/csv
1212
- WarpSummary: Warp execution status summary
13+
- SASS: Extract SASS assembly from cubin files
1314
"""
1415

1516
from .formatters import (
@@ -27,6 +28,7 @@
2728
select_records,
2829
TraceReader,
2930
)
31+
from .sass import dump_sass, dump_sass_to_file, SassOutput
3032
from .warp_summary import (
3133
compute_warp_summary,
3234
format_ranges,
@@ -59,4 +61,8 @@
5961
"compute_warp_summary",
6062
"format_warp_summary_text",
6163
"warp_summary_to_dict",
64+
# SASS
65+
"SassOutput",
66+
"dump_sass",
67+
"dump_sass_to_file",
6268
]

python/cutracer/query/cli.py

Lines changed: 85 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
"""
44
CLI implementation for the query subcommand.
55
6-
Provides command-line interface for querying and viewing CUTracer trace files.
6+
Provides command-line interface for querying and viewing CUTracer trace files,
7+
and extracting SASS assembly from cubin files.
78
"""
89

910
import csv
@@ -406,3 +407,86 @@ def query_command(
406407
)
407408

408409
_write_output(output, output_file, compress, record_count=len(selected))
410+
411+
412+
@click.command(name="sass")
413+
@click.argument("cubin", type=click.Path(exists=True, path_type=Path))
414+
@click.option(
415+
"--output",
416+
"-o",
417+
type=click.Path(path_type=Path),
418+
default=None,
419+
help="Output .sass file path (default: <cubin>.sass).",
420+
)
421+
@click.option(
422+
"--no-source-info",
423+
"-G",
424+
is_flag=True,
425+
help="Omit source-level debug info (-g flag to nvdisasm).",
426+
)
427+
@click.option(
428+
"--no-line-info",
429+
"-C",
430+
is_flag=True,
431+
help="Omit //## source line comments (-c flag to nvdisasm).",
432+
)
433+
@click.option(
434+
"--timeout",
435+
type=int,
436+
default=60,
437+
show_default=True,
438+
help="nvdisasm timeout in seconds.",
439+
)
440+
@click.option(
441+
"--stdout",
442+
is_flag=True,
443+
help="Print SASS to stdout instead of writing to file.",
444+
)
445+
def sass_command(
446+
cubin: Path,
447+
output: Optional[Path],
448+
no_source_info: bool,
449+
no_line_info: bool,
450+
timeout: int,
451+
stdout: bool,
452+
) -> None:
453+
"""
454+
Dump SASS assembly from a cubin file.
455+
456+
Disassembles the given cubin file using nvdisasm and saves the SASS
457+
assembly output. By default, includes source-level debug info (-g)
458+
and source line comments (-c) for mapping back to source code.
459+
460+
\b
461+
Examples:
462+
cutracer query sass kernel.cubin
463+
cutracer query sass kernel.cubin -o kernel.sass
464+
cutracer query sass kernel.cubin --stdout
465+
cutracer query sass kernel.cubin --no-source-info
466+
cutracer query sass kernel.cubin -G -C # minimal output
467+
"""
468+
from cutracer.query.sass import dump_sass, dump_sass_to_file
469+
470+
if stdout:
471+
# Print to stdout, don't write file
472+
sass_output = dump_sass(
473+
cubin,
474+
include_source_info=not no_source_info,
475+
include_line_info=not no_line_info,
476+
timeout=timeout,
477+
)
478+
if sass_output is None:
479+
raise click.ClickException("Failed to dump SASS (see warnings above)")
480+
click.echo(sass_output.raw_text)
481+
else:
482+
# Write to file
483+
output_path = dump_sass_to_file(
484+
cubin,
485+
output_path=output,
486+
include_source_info=not no_source_info,
487+
include_line_info=not no_line_info,
488+
timeout=timeout,
489+
)
490+
if output_path is None:
491+
raise click.ClickException("Failed to dump SASS (see warnings above)")
492+
click.echo(f"SASS written to {output_path}", err=True)

python/cutracer/query/sass.py

Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
# Copyright (c) Meta Platforms, Inc. and affiliates.
2+
3+
"""
4+
SASS code extraction from cubin files.
5+
6+
Provides utilities to disassemble NVIDIA cubin files and extract
7+
SASS assembly code using nvdisasm. This module can be used standalone
8+
via CLI or programmatically by other modules (e.g., deadlock analysis).
9+
10+
Usage (CLI):
11+
cutracer query sass kernel.cubin
12+
cutracer query sass kernel.cubin -o kernel.sass
13+
14+
Usage (programmatic):
15+
from cutracer.query.sass import dump_sass, dump_sass_to_file
16+
17+
# Get SASS text in memory
18+
output = dump_sass(Path("kernel.cubin"))
19+
if output:
20+
print(output.raw_text)
21+
22+
# Save SASS to file
23+
sass_path = dump_sass_to_file(Path("kernel.cubin"))
24+
"""
25+
26+
from __future__ import annotations
27+
28+
import logging
29+
import subprocess
30+
from dataclasses import dataclass, field
31+
from pathlib import Path
32+
33+
logger: logging.Logger = logging.getLogger(__name__)
34+
35+
36+
@dataclass
37+
class SassOutput:
38+
"""Container for nvdisasm output.
39+
40+
Attributes:
41+
raw_text: The disassembled SASS text.
42+
cubin_path: Path to the source cubin file.
43+
flags_used: List of nvdisasm flags that were used.
44+
"""
45+
46+
raw_text: str
47+
cubin_path: Path
48+
flags_used: list[str] = field(default_factory=list)
49+
50+
def save(self, output_path: Path) -> None:
51+
"""Save SASS text to file.
52+
53+
Args:
54+
output_path: Path where SASS text will be written.
55+
"""
56+
output_path.parent.mkdir(parents=True, exist_ok=True)
57+
output_path.write_text(self.raw_text)
58+
59+
def __len__(self) -> int:
60+
"""Return length of SASS text."""
61+
return len(self.raw_text)
62+
63+
@property
64+
def line_count(self) -> int:
65+
"""Return number of lines in SASS text."""
66+
if not self.raw_text:
67+
return 0
68+
return len(self.raw_text.rstrip("\n").split("\n"))
69+
70+
71+
def dump_sass(
72+
cubin_path: Path,
73+
*,
74+
include_source_info: bool = True,
75+
include_line_info: bool = True,
76+
timeout: int = 60,
77+
) -> SassOutput | None:
78+
"""
79+
Dump SASS assembly from cubin file using nvdisasm.
80+
81+
This is the core function for extracting SASS code. It runs:
82+
nvdisasm -g -c <cubin_path>
83+
84+
Flags:
85+
-g: Include source-level debug info (file/line annotations)
86+
-c: Output assembly with //## File comments for source mapping
87+
88+
The combination of -g and -c provides:
89+
- Full instruction listing with PC offsets
90+
- Source file/line annotations via //## comments
91+
- Function boundaries and labels
92+
93+
Args:
94+
cubin_path: Absolute path to cubin file.
95+
include_source_info: If True, add -g flag for debug info.
96+
include_line_info: If True, add -c flag for //## comments.
97+
timeout: Timeout in seconds (default: 60).
98+
99+
Returns:
100+
SassOutput containing raw text and metadata, or None if failed.
101+
102+
Example:
103+
>>> output = dump_sass(Path("/path/to/kernel.cubin"))
104+
>>> if output:
105+
... print(f"Got {output.line_count} lines of SASS")
106+
... output.save(Path("kernel.sass"))
107+
"""
108+
if not cubin_path.exists():
109+
logger.warning("cubin file not found: %s", cubin_path)
110+
return None
111+
112+
flags: list[str] = []
113+
if include_source_info:
114+
flags.append("-g")
115+
if include_line_info:
116+
flags.append("-c")
117+
118+
cmd = ["nvdisasm", *flags, str(cubin_path)]
119+
120+
try:
121+
result = subprocess.run(
122+
cmd,
123+
capture_output=True,
124+
text=True,
125+
timeout=timeout,
126+
)
127+
except FileNotFoundError:
128+
logger.warning("nvdisasm not found in PATH")
129+
return None
130+
except subprocess.TimeoutExpired:
131+
logger.warning("nvdisasm timed out after %ds", timeout)
132+
return None
133+
134+
if result.returncode != 0:
135+
logger.warning(
136+
"nvdisasm failed (rc=%d): %s",
137+
result.returncode,
138+
result.stderr.strip(),
139+
)
140+
return None
141+
142+
return SassOutput(
143+
raw_text=result.stdout,
144+
cubin_path=cubin_path,
145+
flags_used=flags,
146+
)
147+
148+
149+
def dump_sass_to_file(
150+
cubin_path: Path,
151+
output_path: Path | None = None,
152+
*,
153+
include_source_info: bool = True,
154+
include_line_info: bool = True,
155+
timeout: int = 60,
156+
) -> Path | None:
157+
"""
158+
Dump SASS to file. Convenience wrapper around dump_sass().
159+
160+
Args:
161+
cubin_path: Path to cubin file.
162+
output_path: Output .sass file path. If None, derives from cubin path
163+
by replacing extension with .sass.
164+
include_source_info: If True, add -g flag for debug info.
165+
include_line_info: If True, add -c flag for //## comments.
166+
timeout: Timeout in seconds (default: 60).
167+
168+
Returns:
169+
Path to output file, or None if dump failed.
170+
171+
Example:
172+
>>> # Creates kernel.sass next to kernel.cubin
173+
>>> sass_path = dump_sass_to_file(Path("kernel.cubin"))
174+
>>> # Or specify explicit output path
175+
>>> sass_path = dump_sass_to_file(Path("kernel.cubin"), Path("output/kernel.sass"))
176+
"""
177+
sass_output = dump_sass(
178+
cubin_path,
179+
include_source_info=include_source_info,
180+
include_line_info=include_line_info,
181+
timeout=timeout,
182+
)
183+
if sass_output is None:
184+
return None
185+
186+
if output_path is None:
187+
output_path = cubin_path.with_suffix(".sass")
188+
189+
sass_output.save(output_path)
190+
return output_path

0 commit comments

Comments
 (0)