Skip to content
Merged
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
12 changes: 9 additions & 3 deletions build_tools/github_actions/amdgpu_family_matrix.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,15 @@ def load_external_runner_config() -> dict | None:


def is_asan():
"""Determines if this is an ASAN build using BUILD_VARIANT env var."""
BUILD_VARIANT = os.getenv("BUILD_VARIANT", "")
return BUILD_VARIANT == "asan"
"""Determines if this is an ASAN-family build using BUILD_VARIANT env var.

Matches "asan", "host-asan" and their "-debug" forms, like the check in
fetch_test_configurations.py. An exact match on "asan" leaves host-asan test
jobs without the ASAN handling their callers apply -- most visibly the
LD_PRELOAD in test_hiptests.py, without which Catch2 cannot load the
instrumented binaries to enumerate tests.
"""
return "asan" in os.getenv("BUILD_VARIANT", "")
Comment on lines 75 to +84

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Expanding this is also going to skip our test for rocminfo here:

class TestROCmSanity:
@pytest.mark.skipif(is_windows(), reason="rocminfo is not supported on Windows")
# TODO(#3312): Re-enable once rocminfo test is fixed for ASAN builds
@pytest.mark.skipif(
is_asan(), reason="rocminfo test fails with ASAN build, see TheRock#3312"
)

We may want to differentiate between host asan and device asan in test skipping.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree!
I'll get a new PR for it after merging, and update this comment as well.



def select_weighted_label(labels_config: list[dict], context_name: str) -> str:
Expand Down
41 changes: 26 additions & 15 deletions build_tools/github_actions/configure_asan_env.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@

import argparse
import os
import platform
import subprocess
import sys
from pathlib import Path
Expand Down Expand Up @@ -58,7 +59,14 @@
"HSA_XNACK": "1",
}

ASAN_RUNTIME_LIB = "libclang_rt.asan.so"
# Which of these exists depends on LLVM_ENABLE_PER_TARGET_RUNTIME_DIR: ON
# installs <resource-dir>/lib/<triple>/libclang_rt.asan.so, OFF installs
# <resource-dir>/lib/linux/libclang_rt.asan-<arch>.so. TheRock has flipped
# between the two (#8077), so try both rather than pinning one.
ASAN_RUNTIME_LIBS = (
"libclang_rt.asan.so",
f"libclang_rt.asan-{platform.machine()}.so",
)


def _resolve_asan_runtime(
Expand All @@ -72,20 +80,23 @@ def _resolve_asan_runtime(
if not os.access(clang, os.X_OK):
return None, f"clang not found at {clang}, ASAN runtime path not resolved"
Comment on lines 62 to 81

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have some other code across the project that is also checking for the asan runtime path, such as

def get_asan_lib_path():
arch = platform.machine()
CLANG_PATH = str(Path(THEROCK_BIN_DIR).parent / "lib" / "llvm" / "bin" / "clang++")
cmd = [f"{CLANG_PATH}", f"--print-file-name=libclang_rt.asan-{arch}.so"]
logging.info(f"++ Exec [{CLANG_PATH}]$ {shlex.join(cmd)}")
result = subprocess.run(
cmd,
check=True,
text=True,
capture_output=True,
)
return result.stdout.strip()

I also want our tests to match our documentation in more places, e.g. https://github.com/ROCm/TheRock/blob/main/docs/development/sanitizers.md#step-2-locate-the-asan-runtime-directory

I think this is fine for now though.


try:
result = subprocess.run(
[str(clang), f"-print-file-name={ASAN_RUNTIME_LIB}"],
capture_output=True,
text=True,
check=True,
)
except (subprocess.CalledProcessError, OSError) as e:
return None, f"could not query {clang} for the ASAN runtime: {e}"

runtime = Path(result.stdout.strip())
if not runtime.is_file():
return None, f"ASAN runtime not found at {runtime}"
return runtime.resolve(), None
for lib in ASAN_RUNTIME_LIBS:
try:
result = subprocess.run(
[str(clang), f"-print-file-name={lib}"],
capture_output=True,
text=True,
check=True,
)
except (subprocess.CalledProcessError, OSError) as e:
return None, f"could not query {clang} for the ASAN runtime: {e}"

# clang echoes the name back unchanged when it cannot locate the file.
runtime = Path(result.stdout.strip())
if runtime.is_file():
return runtime.resolve(), None

return None, f"ASAN runtime not found, tried {', '.join(ASAN_RUNTIME_LIBS)}"


def _resolve_symbolizer(artifacts_dir: Path) -> tuple[Optional[Path], Optional[str]]:
Expand Down
30 changes: 30 additions & 0 deletions build_tools/github_actions/tests/configure_asan_env_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"""Unit tests for configure_asan_env.py"""

import os
import platform
import stat
import sys
import tempfile
Expand Down Expand Up @@ -89,6 +90,35 @@ def test_missing_clang_warns_without_exporting_runtime(self):
self.assertNotIn("ASAN_RUNTIME_PATH", env)
self.assertTrue(any("clang not found" in w for w in warnings))

@requires_posix
def test_resolves_the_legacy_arch_suffixed_runtime(self):
"""The name depends on LLVM_ENABLE_PER_TARGET_RUNTIME_DIR; both must work."""
arch_lib = f"libclang_rt.asan-{platform.machine()}.so"
with tempfile.TemporaryDirectory() as tmp:
artifacts = Path(tmp) / "build"
runtime_path = (
artifacts / "lib" / "llvm" / "lib" / "clang" / "24" / arch_lib
)
runtime_path.parent.mkdir(parents=True, exist_ok=True)
runtime_path.write_text("")
# Real clang echoes the requested name back when it cannot find it.
_make_executable(
artifacts / "llvm" / "bin" / "clang",
"#!/bin/sh\n"
f'case "$1" in\n'
f' *={arch_lib}) echo "{runtime_path}" ;;\n'
f' *) echo "${{1#*=}}" ;;\n'
f"esac\n",
)
_make_executable(
artifacts / "llvm" / "bin" / "llvm-symbolizer", "#!/bin/sh\n"
)

env, warnings = resolve_asan_env(artifacts)

self.assertEqual(warnings, [])
self.assertEqual(env["ASAN_RUNTIME_PATH"], str(runtime_path.resolve()))

@requires_posix
def test_missing_runtime_file_warns(self):
with tempfile.TemporaryDirectory() as tmp:
Expand Down
Loading