blob: 68d307c5383a542a6a705dd3184c296e1bee836a [file] [edit]
############################################################################
# Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
############################################################################
"""Profile GPU columns stay zero when only gres/gpumem is tracked.
With AccountingStorageTRES=gres/gpumem and no gres/gpu entry the
gres/gpuutil auto-append stays off, so slurmstepd resolves the gpumem
usage TRES position while the gpuutil position stays unresolved. The
profile dataset still defines both GPU columns, and since slurmd 26.11
each is filled behind its own TRES position guard, with
JobAcctGatherParams=DisableGPUAcct and the cgroup device memory accounting
off leaving the gpumem slot at the INFINITE64 no-sample sentinel whether
or not the node has GPUs. Before the guard split the GPUUtilization column
was read through the gpumem position guard alone, an out-of-bounds read of
the step usage array here, showing as nonzero GPUUtilization samples. The
gres TRES require slurmdbd, so the module needs accounting and no GPU
hardware.
"""
import pytest
import atf
import hdf5_profile
gpu_cols = ["GPUMemMB", "GPUUtilization"]
@pytest.fixture(scope="module", autouse=True)
def setup(hdf5_dir):
atf.require_version(
(26, 11),
"sbin/slurmd",
reason="MR 4338: the profile records unsampled GPU TRES as zero since 26.11",
)
atf.require_config_parameter("AcctGatherProfileType", "acct_gather_profile/hdf5")
atf.require_config_parameter(
"ProfileHDF5Dir",
hdf5_dir,
source="acct_gather",
)
atf.require_config_parameter(
"JobAcctGatherType",
"jobacct_gather/cgroup",
)
# Track only the gpumem usage TRES, with no gres/gpu entry so the
# gres/gpuutil auto-append stays off and the gpuutil position stays
# unresolved. The gres TRES require slurmdbd, so bring accounting up
# first.
atf.require_accounting(modify=True)
atf.require_config_parameter("AccountingStorageTRES", "gres/gpumem")
# Keep GPU usage samples out of the gpumem usage slot so it holds the
# no-sample sentinel deterministically, with or without GPUs on the node.
atf.require_config_parameter_includes("JobAcctGatherParams", "DisableGPUAcct")
# The cgroup plugin fills the gpumem slot from the dmem controller when
# ConstrainDeviceMemory is on, DisableGPUAcct notwithstanding.
atf.require_config_parameter("ConstrainDeviceMemory", [None, "no"], source="cgroup")
atf.require_slurm_running()
@pytest.fixture(scope="module")
def hdf5_dir():
return hdf5_profile.profile_hdf5_dir()
@pytest.fixture(scope="module")
def user_profile_dir(hdf5_dir):
return hdf5_profile.profile_user_dir(hdf5_dir)
@pytest.fixture(scope="module")
def job_id(user_profile_dir):
yield from hdf5_profile.run_profile_job(user_profile_dir, "test_149_5")
@pytest.fixture(scope="module")
def node_profiles(user_profile_dir, job_id):
return hdf5_profile.wait_for_node_profiles(user_profile_dir, job_id, 1)
@pytest.fixture(scope="module")
def hdf5_file(job_id, node_profiles):
return hdf5_profile.merge_job_profiles(job_id)
def test_gpu_columns_zero_with_gpumem_alone(job_id, hdf5_file):
"""GPUMemMB and GPUUtilization are exactly 0 in every profile sample.
The dataset defines both columns unconditionally, so every sample
carries both even though only gres/gpumem is tracked. DisableGPUAcct,
with ConstrainDeviceMemory off, leaves the gpumem step usage slot at
the INFINITE64 no-sample sentinel, which
hdf5_profile.assert_gpu_columns_zero requires the profile to record as
0, and the per-TRES guard must leave the untracked GPUUtilization
column at exactly 0.0 instead of reading an out-of-bounds usage slot.
"""
rows = hdf5_profile.read_sh5util_csv(
job_id, hdf5_file, "-E --level=Node:TimeSeries", gpu_cols
)
hdf5_profile.assert_gpu_columns_zero(rows)