[inductor] add _get_inductor_debug_symbol_cflags for debug symbol control. (#159938)

We need to add inductor debug symbol support for crash case debug. When we turn on generate debug symbol.
On Windows, it should create a [module_name].pdb file. It helps debug by WinDBG.
On Linux, it should create some debug sections in binary file.

I added UT for it also.

It works well on Windows inductor debug.
<img width="1648" height="833" alt="image" src="https://github.com/user-attachments/assets/5282a7de-cef3-4a38-9cd4-a0e63482c8b6" />

Pull Request resolved: https://github.com/pytorch/pytorch/pull/159938
Approved by: https://github.com/jansel, https://github.com/angelayi
This commit is contained in:
Xu Han
2025-08-06 19:31:42 +00:00
committed by PyTorch MergeBot
parent 6fa3592dc6
commit c71950907d
2 changed files with 99 additions and 3 deletions

View File

@ -1,6 +1,14 @@
# Owner(s): ["module: inductor"]
import os
import shlex
import subprocess
import sys
from unittest import mock
import torch
from torch import _dynamo as dynamo, _inductor as inductor
from torch._inductor.codecache import write
from torch._inductor.cpp_builder import CppBuilder, CppOptions
from torch._inductor.test_case import run_tests, TestCase
from torch._inductor.utils import gen_gm_and_inputs
from torch.fx import symbolic_trace
@ -8,6 +16,25 @@ from torch.fx.experimental.proxy_tensor import make_fx
from torch.testing._internal.inductor_utils import HAS_CPU
_IS_MACOS = sys.platform.startswith("darwin")
_IS_WINDOWS = sys.platform == "win32"
def safe_command_output(cmd, timeout=30):
try:
return subprocess.check_output(
cmd,
stderr=subprocess.STDOUT,
text=True,
timeout=timeout,
shell=isinstance(cmd, str),
).strip()
except subprocess.CalledProcessError as e:
return f"run failederror code {e.returncode}: {e.output.strip()}"
except subprocess.TimeoutExpired:
return "runt timeout"
class MyModule(torch.nn.Module):
def __init__(self) -> None:
super().__init__()
@ -109,6 +136,53 @@ class TestStandaloneInductor(TestCase):
mod_opt = inductor.compile(mod, inp)
self.assertEqual(mod(*inp), mod_opt(*inp))
@mock.patch.dict(os.environ, {"TORCHINDUCTOR_DEBUG_SYMBOL": "1"})
def test_inductor_generate_debug_symbol(self):
cpp_code = """
int main(){
return 0;
}
"""
_, source_path = write(
cpp_code,
"cpp",
)
build_option = CppOptions()
cpp_builder = CppBuilder(
name="test_symbol",
sources=source_path,
output_dir=os.path.dirname(source_path),
BuildOption=build_option,
)
cpp_builder.build()
binary_path = cpp_builder.get_target_file_path()
"""
When we turn on generate debug symbol.
On Windows, it should create a [module_name].pdb file. It helps debug by WinDBG.
On Linux, it should create some debug sections in binary file.
"""
def check_linux_debug_section(module_path: str):
check_cmd = shlex.split(f"readelf -S {module_path}")
output = safe_command_output(check_cmd)
has_debug_sym = ".debug_info" in output
self.assertEqual(has_debug_sym, True)
def check_windows_pdb_exist(module_path: str):
file_name_no_ext = os.path.splitext(module_path)[0]
file_name_pdb = f"{file_name_no_ext}.pdb"
has_pdb_file = os.path.exists(file_name_pdb)
self.assertEqual(has_pdb_file, True)
if _IS_WINDOWS:
check_windows_pdb_exist(binary_path)
elif _IS_MACOS:
pass # MacOS not sure that if it should be works.
else:
check_linux_debug_section(binary_path)
if __name__ == "__main__":
if HAS_CPU:

View File

@ -637,7 +637,7 @@ def _get_optimization_cflags(
return cflags
def _get_shared_cflag(do_link: bool) -> list[str]:
def _get_shared_cflags(do_link: bool) -> list[str]:
if _IS_WINDOWS:
"""
MSVC `/MD` using python `ucrtbase.dll` lib as runtime.
@ -652,6 +652,25 @@ def _get_shared_cflag(do_link: bool) -> list[str]:
return ["shared", "fPIC"]
def _get_inductor_debug_symbol_cflags() -> tuple[list[str], list[str]]:
"""
When we turn on generate debug symbol.
On Windows, it should create a [module_name].pdb file. It helps debug by WinDBG.
On Linux, it should create some debug sections in binary file.
"""
cflags: list[str] = []
ldflags: list[str] = []
b_enable_debug_symbol = os.environ.get("TORCHINDUCTOR_DEBUG_SYMBOL", "0") == "1"
if b_enable_debug_symbol:
if _IS_WINDOWS:
cflags = ["Z7", "_DEBUG", "OD"]
ldflags = ["DEBUG", "OPT:REF", "OPT:ICF"]
else:
cflags.append("g")
return cflags, ldflags
def get_cpp_options(
cpp_compiler: str,
do_link: bool,
@ -667,12 +686,15 @@ def get_cpp_options(
libraries: list[str] = []
passthrough_args: list[str] = []
dbg_cflags, dbg_ldflags = _get_inductor_debug_symbol_cflags()
cflags = (
_get_shared_cflag(do_link)
_get_shared_cflags(do_link)
+ _get_optimization_cflags(cpp_compiler, min_optimize)
+ _get_warning_all_cflag(warning_all)
+ _get_cpp_std_cflag()
+ _get_os_related_cpp_cflags(cpp_compiler)
+ dbg_cflags
)
if not _IS_WINDOWS and config.aot_inductor.enable_lto and _is_clang(cpp_compiler):
@ -685,7 +707,7 @@ def get_cpp_options(
definitions,
include_dirs,
cflags,
ldflags,
ldflags + dbg_ldflags,
libraries_dirs,
libraries,
passthrough_args,