Add symmetric debug mode to print stack trace of comm ops with unregistered tensors (#18569)

This commit is contained in:
Nicolas Castet
2026-04-08 22:34:58 -07:00
committed by GitHub
parent 6b96f8341d
commit e379befbac
4 changed files with 89 additions and 0 deletions
+1
View File
@@ -160,6 +160,7 @@ SGLang supports various environment variables that can be used to configure its
| `SGLANG_TEST_RETRACT_NO_PREFILL_BS` | When SGLANG_TEST_RETRACT is enabled, no prefill is performed if the batch size exceeds SGLANG_TEST_RETRACT_NO_PREFILL_BS. | `2 ** 31` | | `SGLANG_TEST_RETRACT_NO_PREFILL_BS` | When SGLANG_TEST_RETRACT is enabled, no prefill is performed if the batch size exceeds SGLANG_TEST_RETRACT_NO_PREFILL_BS. | `2 ** 31` |
| `SGLANG_RECORD_STEP_TIME` | Record step time for profiling | `false` | | `SGLANG_RECORD_STEP_TIME` | Record step time for profiling | `false` |
| `SGLANG_TEST_REQUEST_TIME_STATS` | Test request time statistics | `false` | | `SGLANG_TEST_REQUEST_TIME_STATS` | Test request time statistics | `false` |
| `SGLANG_DEBUG_SYMM_MEM` | Enable debug checks that verify tensors passed to NCCL communication ops are allocated in the symmetric memory pool. Logs warnings (rank 0 only) with stack traces for any tensor not in the pool. | `false` |
| `SGLANG_KERNEL_API_LOGLEVEL` | Controls crash-debug kernel API logging. `0` disables logging, `1` logs API names, `3` logs tensor metadata, `5` adds tensor statistics, and `10` also writes pre-call dump snapshots. | `0` | | `SGLANG_KERNEL_API_LOGLEVEL` | Controls crash-debug kernel API logging. `0` disables logging, `1` logs API names, `3` logs tensor metadata, `5` adds tensor statistics, and `10` also writes pre-call dump snapshots. | `0` |
| `SGLANG_KERNEL_API_LOGDEST` | Destination for crash-debug kernel API logs. Use `stdout`, `stderr`, or a file path. `%i` is replaced with the process PID. | `stdout` | | `SGLANG_KERNEL_API_LOGDEST` | Destination for crash-debug kernel API logs. Use `stdout`, `stderr`, or a file path. `%i` is replaced with the process PID. | `stdout` |
| `SGLANG_KERNEL_API_DUMP_DIR` | Output directory for level-10 kernel API input/output dumps. `%i` is replaced with the process PID. | `sglang_kernel_api_dumps` | | `SGLANG_KERNEL_API_DUMP_DIR` | Output directory for level-10 kernel API input/output dumps. `%i` is replaced with the process PID. | `sglang_kernel_api_dumps` |
@@ -1,11 +1,14 @@
import logging
import os import os
import tempfile import tempfile
import traceback
from contextlib import nullcontext from contextlib import nullcontext
import torch import torch
from torch.cuda.memory import CUDAPluggableAllocator from torch.cuda.memory import CUDAPluggableAllocator
from sglang.srt.distributed.parallel_state import GroupCoordinator from sglang.srt.distributed.parallel_state import GroupCoordinator
from sglang.srt.environ import envs
from sglang.srt.server_args import get_global_server_args from sglang.srt.server_args import get_global_server_args
from sglang.srt.utils.common import torch_release from sglang.srt.utils.common import torch_release
@@ -211,3 +214,78 @@ def use_symmetric_memory(group_coordinator: GroupCoordinator, disabled: bool = F
or group_coordinator.world_size == 1 or group_coordinator.world_size == 1
) )
return SymmetricMemoryContext(group_coordinator) if not disabled else nullcontext() return SymmetricMemoryContext(group_coordinator) if not disabled else nullcontext()
# --- Debug mode for symmetric memory validation ---
_symm_mem_logger = logging.getLogger(__name__)
_debug_seen_traces: set = set()
def is_tensor_in_symmetric_mempool(tensor: torch.Tensor) -> bool:
"""Check if a tensor's storage is allocated in the NCCL symmetric memory pool."""
if _mem_pool is None:
return False # Pool not initialized
data_ptr = tensor.untyped_storage().data_ptr()
for segment in _mem_pool.snapshot():
for block in segment["blocks"]:
if block["address"] == data_ptr:
return True
return False
def debug_check_symmetric_mempool(
group_coordinator: GroupCoordinator,
tensors: dict,
op_name: str,
) -> None:
"""
Debug check: verify that tensors passed to communication ops are allocated
in the NCCL symmetric memory pool.
Enabled by setting SGLANG_DEBUG_SYMM_MEM=1.
Only prints warnings on rank 0 and deduplicates identical stack traces.
Args:
tensors: dict mapping argument name to tensor
(e.g. {"input": t1, "output": t2})
op_name: name of the communication operation being checked
"""
if not envs.SGLANG_DEBUG_SYMM_MEM.get() or not is_symmetric_memory_enabled():
return
# Only print on rank 0
if not group_coordinator.is_first_rank:
return
bad_names = []
bad_details = []
for name, tensor in tensors.items():
if not is_tensor_in_symmetric_mempool(tensor):
bad_names.append(name)
bad_details.append(
f" - '{name}' (data_ptr=0x{tensor.storage().data_ptr():x}, "
f"shape={list(tensor.shape)}, dtype={tensor.dtype})"
)
if bad_names:
traces = traceback.format_stack()
# Skip autotune stack traces
if any("_flashinfer_autotune" in trace for trace in traces):
return
stack = "".join(traces[:-1])
trace_key = f"{op_name}:{','.join(bad_names)}:{stack}"
if trace_key not in _debug_seen_traces:
_debug_seen_traces.add(trace_key)
_symm_mem_logger.warning(
"[SymmMem Debug] %s: %d tensor(s) are NOT in the "
"NCCL symmetric memory pool:\n%s\n"
"Stack trace:\n%s",
op_name,
len(bad_names),
"\n".join(bad_details),
stack,
)
@@ -334,6 +334,7 @@ class GroupCoordinator:
PyNcclCommunicator, PyNcclCommunicator,
) )
from sglang.srt.distributed.device_communicators.pynccl_allocator import ( from sglang.srt.distributed.device_communicators.pynccl_allocator import (
debug_check_symmetric_mempool,
is_symmetric_memory_enabled, is_symmetric_memory_enabled,
use_symmetric_memory, use_symmetric_memory,
) )
@@ -345,6 +346,7 @@ class GroupCoordinator:
self.is_symmetric_memory_enabled = is_symmetric_memory_enabled self.is_symmetric_memory_enabled = is_symmetric_memory_enabled
self.use_symmetric_memory = use_symmetric_memory self.use_symmetric_memory = use_symmetric_memory
self.is_allocation_symmetric = is_allocation_symmetric self.is_allocation_symmetric = is_allocation_symmetric
self.debug_check_symmetric_mempool = debug_check_symmetric_mempool
if is_hip(): if is_hip():
from sglang.srt.distributed.device_communicators.quick_all_reduce import ( from sglang.srt.distributed.device_communicators.quick_all_reduce import (
QuickAllReduce, QuickAllReduce,
@@ -577,6 +579,7 @@ class GroupCoordinator:
return self.npu_communicator.all_reduce(input_) return self.npu_communicator.all_reduce(input_)
if self.pynccl_comm is not None and self.is_symmetric_memory_enabled(): if self.pynccl_comm is not None and self.is_symmetric_memory_enabled():
self.debug_check_symmetric_mempool(self, {"input": input_}, "all_reduce")
with self.pynccl_comm.change_state(enable=True): with self.pynccl_comm.change_state(enable=True):
self.pynccl_comm.all_reduce(input_) self.pynccl_comm.all_reduce(input_)
return input_ return input_
@@ -718,6 +721,9 @@ class GroupCoordinator:
if pynccl_comm is not None and ( if pynccl_comm is not None and (
not pynccl_comm.disabled or self.is_symmetric_memory_enabled() not pynccl_comm.disabled or self.is_symmetric_memory_enabled()
): ):
self.debug_check_symmetric_mempool(
self, {"output": output, "input": input}, "reduce_scatter_tensor"
)
with pynccl_comm.change_state(enable=True): with pynccl_comm.change_state(enable=True):
pynccl_comm.reduce_scatter(output, input) pynccl_comm.reduce_scatter(output, input)
else: else:
@@ -779,6 +785,9 @@ class GroupCoordinator:
if pynccl_comm is not None and ( if pynccl_comm is not None and (
not pynccl_comm.disabled or self.is_symmetric_memory_enabled() not pynccl_comm.disabled or self.is_symmetric_memory_enabled()
): ):
self.debug_check_symmetric_mempool(
self, {"output": output}, "all_gather_into_tensor"
)
with pynccl_comm.change_state(enable=True): with pynccl_comm.change_state(enable=True):
pynccl_comm.all_gather(output, input) pynccl_comm.all_gather(output, input)
else: else:
+1
View File
@@ -524,6 +524,7 @@ class Envs:
# Symmetric Memory # Symmetric Memory
SGLANG_SYMM_MEM_PREALLOC_GB_SIZE = EnvInt(-1) SGLANG_SYMM_MEM_PREALLOC_GB_SIZE = EnvInt(-1)
SGLANG_DEBUG_SYMM_MEM = EnvBool(False)
# Aiter # Aiter
SGLANG_USE_AITER_FP8_PER_TOKEN = EnvBool(False) SGLANG_USE_AITER_FP8_PER_TOKEN = EnvBool(False)