ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

使用 SGLang Kernel API 日志调试 CUDA 崩溃:从环境变量到 compute-sanitizer 的完整实战指南

使用 SGLang Kernel API 日志调试 CUDA 崩溃:从环境变量到 compute-sanitizer 的完整实战指南 使用 SGLang Kernel API 日志调试 CUDA 崩溃从环境变量到 compute-sanitizer 的完整实战指南【免费下载链接】sglangSGLang is a high-performance serving framework for large language models and multimodal models.项目地址: https://gitcode.com/GitHub_Trending/sg/sglang导读CUDA 非法内存访问、device-side assert、NaN/Inf 扩散——这类崩溃的共性是进程在正常调试输出被刷出之前就中止了你永远看不到触发崩溃的那批张量长什么样。SGLang 为此内置了一套基于debug_kernel_api装饰器的内核 API 日志系统它在内核执行之前捕获输入张量的形状、dtype、设备、连续性乃至数值统计把崩溃发生时到底发生了什么记录下来。本文以该功能为主线完整覆盖从环境变量启用、四个日志级别、崩溃安全 dump、多进程调试到与 compute-sanitizer / cuda-gdb / printf 组合定位的全流程并结合 kernel_api_logging.py 等源码说明其底层实现原理。读完本文你将掌握一套对 LLM 与 Diffusion 模型通用的 CUDA 崩溃排查方法论。为什么 CUDA 崩溃需要内核 API 日志问题CUDA 错误illegal memory access、device-side assert、out-of-bounds、NaN/Inf往往直接中止进程。标准做法是在代码里手动打印张量但大多数情况下崩溃发生在你还没来得及加打印的位置而且普通 stdout 缓冲会在 abort 时丢失。解决方案SGLang 的debug_kernel_api装饰器在执行前记录输入因此即使程序中止你依然能看到导致崩溃的调用边界上发生了什么。核心实现位于 python/sglang/kernels/kernel_api_logging.py其设计参照了 FlashInfer 的 kernel API logging 工具。日志覆盖范围当前日志覆盖聚焦于 SGLang 中价值最高的内核边界通过register_custom_op(...)注册的自定义算子通过register_custom_op_from_extern(...)注册的外部自定义算子如 FlashInfer 等外部库的内核LLM 的 attention、linear、quantization 以及多平台 wrapper 入口Diffusion 的 attention 实现、linear、rotary 和 custom-op wrapper 入口部分直接torch.ops.sglang.*热点与模型级 bypass。这意味着该日志对 LLM 和 Diffusion 内核调试都有用但它不会自动覆盖仓库中每一个纯 PyTorch 调用。源码中的接线方式日志系统通过三条路径接入调用边界你可以从源码确认其覆盖面LLM 自定义算子register_custom_op最终通过debug_torch_op把日志装饰器挂到torch.ops.sglang.op上见 python/sglang/srt/utils/custom_op.py外部算子走register_custom_op_from_extern同样以debug_torch_op(fn, name)收尾同文件 L328-L337。Diffusion 自定义算子CustomOp基类的forward直接标注debug_kernel_api见 python/sglang/multimodal_gen/runtime/layers/custom_op.py对应的register_custom_op位于 python/sglang/multimodal_gen/runtime/layers/utils.py。AOT 编译内核maybe_wrap_debug_kernel提供条件包装仅在环境变量开启时生效被 python/sglang/kernels/aot/python/sgl_kernel/init.py 用于批量包装sgl_kernel中的导出函数。因此当你通过register_custom_op注册自己的算子时它天然处于日志覆盖范围内——这正是后文复现实验的基础。Step 1启用 Kernel API 日志日志由 4 个环境变量控制其中SGLANG_KERNEL_API_LOGLEVEL决定详细程度SGLANG_KERNEL_API_LOGDEST决定输出位置。在源码中这些变量在模块导入时一次性解析kernel_api_logging.py因此必须在启动 Python 进程前设置好。基础日志仅函数名Level 1export SGLANG_KERNEL_API_LOGLEVEL1 export SGLANG_KERNEL_API_LOGDESTstdout python my_script.py输出示例真实摘自Qwen/Qwen3-0.6B [2026-03-19 00:47:06] SGLang Kernel API Call: RMSNorm.forward [2026-03-19 00:47:06] SGLang Kernel API Call: sglang.quant_method.UnquantizedLinearMethod.apply [2026-03-19 00:47:06] SGLang Kernel API Call: sglang.custom_op.fused_inplace_qknorm注意函数名的三类形态普通模块方法RMSNorm.forward、quant 方法sglang.quant_method.UnquantizedLinearMethod.apply和自定义算子sglang.custom_op.fused_inplace_qknorm。详细日志输入输出带元数据Level 3export SGLANG_KERNEL_API_LOGLEVEL3 export SGLANG_KERNEL_API_LOGDESTdebug.log python my_script.pydebug.log中的输出示例真实摘自Qwen/Qwen3-0.6B [2026-03-19 00:47:30] SGLang Kernel API Call: sglang.quant_method.UnquantizedLinearMethod.apply Positional input arguments: arg[0]QKVParallelLinear( reprQKVParallelLinear(in_features1024, output_features4096, biasFalse, tp_size1, gather_outputFalse) ) arg[1]Tensor( shape(1, 1024) dtypetorch.bfloat16 devicecuda:0 requires_gradFalse is_contiguousTrue ) arg[2]None Output: returnTensor( shape(1, 4096) dtypetorch.bfloat16 devicecuda:0 requires_gradFalse is_contiguousTrue )Level 3 已经足够你判断绝大多数形状shape、数据类型dtype和设备device不匹配问题。注意arg[0]是非张量对象QKVParallelLinear模块实例序列化器会提取shape/dtype/device属性或截断的repris_contiguous一栏则直接反映 stride 连续性是排查 stride 相关内核 bug 的关键信号。完整日志带张量统计Level 5export SGLANG_KERNEL_API_LOGLEVEL5 export SGLANG_KERNEL_API_LOGDESTdebug.log python my_script.py额外输出真实摘自black-forest-labs/FLUX.1-dev [2026-03-19 01:00:42] SGLANG Kernel API Call: diffusion.quant_method.UnquantizedLinearMethod.apply Positional input arguments: arg[1]Tensor( shape(1, 77, 768) dtypetorch.bfloat16 devicecuda:0 requires_gradFalse is_contiguousTrue min-27.250000 max28.500000 mean0.011723 nan_count0 inf_count0 ) Output: returnTensor( shape(1, 77, 2304) dtypetorch.bfloat16 devicecuda:0 requires_gradFalse is_contiguousTrue min-8.937500 max9.375000 mean0.009460 nan_count0 inf_count0 )Level 5 在 Level 3 基础上追加min/max/mean/nan_count/inf_count。统计计算由_serialize_tensor完成kernel_api_logging.py复数张量先取绝对值整数张量不统计 NaN/Inf浮点张量则逐项统计。空张量会标记为statistics[empty tensor]。崩溃安全 DumpLevel 10export SGLANG_KERNEL_API_LOGLEVEL10 export SGLANG_KERNEL_API_LOGDESTdebug.log export SGLANG_KERNEL_API_DUMP_DIR/tmp/sglang_kernel_api_dumps python my_script.pyLevel 10 在执行前保存输入。若内核崩溃dump 目录中依然保留输入与异常元数据。真实Qwen/Qwen3-0.6B的 Level 10 dump 布局/tmp/sglang_kernel_api_validation/qwen_qwen3_0_6b_level10_dumps /tmp/sglang_kernel_api_validation/qwen_qwen3_0_6b_level10_dumps/20260319_004821_182_pid919286_RotaryEmbedding.forward_call0001 /tmp/sglang_kernel_api_validation/qwen_qwen3_0_6b_level10_dumps/20260319_004821_182_pid919286_RotaryEmbedding.forward_call0001/inputs.pt /tmp/sglang_kernel_api_validation/qwen_qwen3_0_6b_level10_dumps/20260319_004821_182_pid919286_RotaryEmbedding.forward_call0001/metadata.json /tmp/sglang_kernel_api_validation/qwen_qwen3_0_6b_level10_dumps/20260319_004821_182_pid919286_RotaryEmbedding.forward_call0001/outputs.pt每个调用对应一个以时间戳_pid_函数名_call序号命名的子目录内含inputs.pttorch.save保存的输入张量字典key 形如arg_0、arg_1、kwarg_x容器与嵌套元素会被展开为arg_0_0之类的前缀 key容器结构记录在metadata.json中outputs.pt输出张量仅当调用成功完成时存在metadata.json调用元数据。真实metadata.json片段{ function_name: RotaryEmbedding.forward, timestamp: 20260319_004821_182, process_id: 919286, execution_status: completed, input_tensor_keys: [arg_0, arg_1, arg_2], output_tensor_keys: [result_0, result_1] }源码层面的执行流是kernel_api_logging.py先_dump_function_inputs写入execution_status: inputs_saved的元数据与inputs.pt随后调用被包装函数成功则_dump_function_outputs把状态改为completed并写入outputs.pt抛异常则_mark_dump_exception把状态改为exception并记录异常类型与消息。因此你只要看到execution_status: exception且缺outputs.pt就说明崩溃发生在该调用边界内。使用 Level 10 的注意事项若 CUDA graph capture 处于激活状态张量 dump 会被自动跳过避免 capture 期间触发 CUDA 错误此时仍能得到调用日志但没有inputs.pt/outputs.ptLevel 10 dump 的本质是崩溃安全的调用快照它始终保留观察到的调用边界但并非每个方法都能一键回放因为部分方法依赖未序列化进 dump 的模块状态对真实模型的成功路径 Level 10 dump通常建议在调试运行中临时关闭 CUDA graph 与 piecewise CUDA graph。Step 2复现一个 LLM CUDA 崩溃先用一个最小复现脚本模拟embedding 索引越界导致 device-side assert的场景。脚本通过register_custom_op注册一个会崩溃的自定义算子从而让崩溃点正好落在日志覆盖边界上python3 - PY from pathlib import Path Path(/tmp/sglang_llm_crash.py).write_text( import torch\n import torch.nn.functional as F\n from sglang.srt.utils.custom_op import register_custom_op\n\n def _fake_embedding(indices, table):\n return torch.empty((*indices.shape, table.shape[-1]), devicetable.device, dtypetable.dtype)\n\n register_custom_op(op_namemock_llm_cuda_crash, fake_impl_fake_embedding)\n def mock_llm_cuda_crash(indices, table):\n out F.embedding(indices, table)\n torch.cuda.synchronize()\n return out\n\n table torch.randn(4, 8, devicecuda, dtypetorch.float16)\n indices torch.tensor([0, 7], devicecuda, dtypetorch.long)\n mock_llm_cuda_crash(indices, table)\n ) PY SGLANG_KERNEL_API_LOGLEVEL1 \ SGLANG_KERNEL_API_LOGDEST/tmp/sglang_llm_level1.log \ python3 /tmp/sglang_llm_crash.py预期结果脚本以 CUDAdevice-side assert退出日志中仍保留崩溃前的最后一个 API 边界记录即sglang.custom_op.mock_llm_cuda_crash。这个脚本刻意用indices[0, 7]配合仅 4 行的table制造越界F.embedding查表时索引 7 超出词表规模 4GPU 侧 assert 触发随后torch.cuda.synchronize()把异步错误同步回主机端。同一示例换 Level 3SGLANG_KERNEL_API_LOGLEVEL3 \ SGLANG_KERNEL_API_LOGDEST/tmp/sglang_llm_level3.log \ python3 /tmp/sglang_llm_crash.py现在日志中会出现崩溃前的张量元数据——你可以直接看到indices的形状与数值来源。再试 Level 10SGLANG_KERNEL_API_LOGLEVEL10 \ SGLANG_KERNEL_API_LOGDEST/tmp/sglang_llm_level10.log \ SGLANG_KERNEL_API_DUMP_DIR/tmp/sglang_llm_level10_dumps \ python3 /tmp/sglang_llm_crash.py此时你应该看到一条sglang.custom_op.mock_llm_cuda_crash的日志条目一个包含inputs.pt的 dump 目录metadata.json显示execution_status: exception没有outputs.pt因为内核在产生输出前就崩溃了。Step 3复现一个 Diffusion CUDA 崩溃Diffusion 侧的自定义算子注册入口不同位于sglang.multimodal_gen.runtime.layers.utils其余套路完全一致python3 - PY from pathlib import Path Path(/tmp/sglang_diffusion_crash.py).write_text( import torch\n import torch.nn.functional as F\n from sglang.multimodal_gen.runtime.layers.utils import register_custom_op\n\n def _fake_embedding(positions, cache):\n return torch.empty((*positions.shape, cache.shape[-1]), devicecache.device, dtypecache.dtype)\n\n register_custom_op(op_namemock_diffusion_cuda_crash, fake_impl_fake_embedding)\n def mock_diffusion_cuda_crash(positions, cache):\n out F.embedding(positions, cache)\n torch.cuda.synchronize()\n return out\n\n cache torch.randn(4, 64, devicecuda, dtypetorch.float16)\n positions torch.tensor([0, 9], devicecuda, dtypetorch.long)\n mock_diffusion_cuda_crash(positions, cache)\n ) PY SGLANG_KERNEL_API_LOGLEVEL1 \ SGLANG_KERNEL_API_LOGDEST/tmp/sglang_diffusion_level1.log \ python3 /tmp/sglang_diffusion_crash.py同样依次尝试 Level 3 与 Level 10SGLANG_KERNEL_API_LOGLEVEL3 \ SGLANG_KERNEL_API_LOGDEST/tmp/sglang_diffusion_level3.log \ python3 /tmp/sglang_diffusion_crash.py SGLANG_KERNEL_API_LOGLEVEL10 \ SGLANG_KERNEL_API_LOGDEST/tmp/sglang_diffusion_level10.log \ SGLANG_KERNEL_API_DUMP_DIR/tmp/sglang_diffusion_level10_dumps \ python3 /tmp/sglang_diffusion_crash.py如果你的本地环境存在无关的 FlashInfer 导入问题请先在上层 shell 中解决再运行示例示例本身不会设置任何FLASHINFER_*环境变量。Step 4多进程调试多 GPU 或 worker 进程场景下多个 rank 会把日志写到同一个文件互相覆盖。SGLANG_KERNEL_API_LOGDEST和SGLANG_KERNEL_API_DUMP_DIR都支持%i占位符它在进程内被替换为os.getpid()见 kernel_api_logging.py 的_str_with_pidexport SGLANG_KERNEL_API_LOGLEVEL3 export SGLANG_KERNEL_API_LOGDESTdebug_rank_%i.log torchrun --nproc_per_node4 my_script.py这会生成彼此独立的日志文件例如debug_rank_12345.log、debug_rank_12346.log、debug_rank_12347.log、debug_rank_12348.log。真实的多进程示例来自一次 2-GPUQwen/Qwen2.5-0.5B-Instruct运行/tmp/sglang_kernel_api_validation_multi/qwen_qwen2_5_0_5b_instruct_level3_950201.log /tmp/sglang_kernel_api_validation_multi/qwen_qwen2_5_0_5b_instruct_level3_950349.log /tmp/sglang_kernel_api_validation_multi/qwen_qwen2_5_0_5b_instruct_level3_950350.log /tmp/sglang_kernel_api_validation_multi/qwen_qwen2_5_0_5b_instruct_level3_950351.logLevel 10 的 dump 目录也应同样处理export SGLANG_KERNEL_API_LOGLEVEL10 export SGLANG_KERNEL_API_LOGDESTdebug_rank_%i.log export SGLANG_KERNEL_API_DUMP_DIR/tmp/sglang_kernel_api_dumps_%i这样可避免多个 rank 写入同一棵 dump 目录树。Step 5过滤 Level 10 DumpLevel 10 全量 dump 可能过于嘈杂。此时用通配符限制 dump 范围——SGLANG_KERNEL_API_DUMP_INCLUDE/SGLANG_KERNEL_API_DUMP_EXCLUDE采用 shell 风格通配匹配fnmatch且支持逗号分隔的多模式源码解析见 kernel_api_logging.py 与_should_dump_function同文件 L122-L131export SGLANG_KERNEL_API_LOGLEVEL10 export SGLANG_KERNEL_API_LOGDESTdebug.log export SGLANG_KERNEL_API_DUMP_DIR/tmp/sglang_kernel_api_dumps export SGLANG_KERNEL_API_DUMP_INCLUDEsglang.custom_op.* export SGLANG_KERNEL_API_DUMP_EXCLUDE*.fake_impl匹配规则若设置了 INCLUDE函数名必须命中至少一个模式才 dump若设置了 EXCLUDE命中任意一个模式即跳过。Step 6常见 CUDA 错误及排查要点非法内存访问或 Device-Side Assert典型报错RuntimeError: CUDA error: an illegal memory access was encountered torch.AcceleratorError: CUDA error: device-side assert triggered排查命令export SGLANG_KERNEL_API_LOGLEVEL3在日志中检查张量形状shape张量 dtypeCUDA 与 CPU 的设备放置stride / 连续性is_contiguous是否存在只记录到输入、没有输出的调用——这是崩溃点的直接标志典型的形状不匹配模式SGLANG Kernel API Call: ... arg[0]Tensor(shape(..., 128), ...) # 期望的维度 arg[1]Tensor(shape(..., 64), ...) # 不匹配这类现象通常指向 head-dim、hidden-dim 或 cache 布局不一致而不是随机的 CUDA 故障。NaN 或 Inf排查命令export SGLANG_KERNEL_API_LOGLEVEL5检查字段min、max、mean、nan_count、inf_count。典型坏数据模式Tensor( ... min-1234567.000000 # 数值异常偏大 max9876543.000000 # 数值异常偏大 meannan # 已出现 NaN nan_count128 # 找到 NaN inf_count0 # 此处尚无 Inf )这通常意味着坏值在进入崩溃内核之前就已经存在——你需要沿调用链向前追溯找到第一个产生坏值的位置而不是盯着崩溃点本身。显存不足Out of Memory排查命令export SGLANG_KERNEL_API_LOGLEVEL3检查异常大的张量形状batch size序列长度Diffusion 场景下的帧数或图像分辨率同时确认是否存在本应是 per-token / per-frame 的张量意外变成了 full-sequence / full-image 尺寸的情况。典型坏模式Tensor( shape(1024, 8192, 128, 128) # 尺寸过大 ... )示例从日志中定位形状 Bug假设失败调用日志如下[2026-03-19 00:47:30] SGLang Kernel API Call: RotaryEmbedding.forward Positional input arguments: arg[0]Tensor(shape(1, 8), dtypetorch.int64, ...) arg[1]Tensor(shape(1, 8, 8, 256), dtypetorch.bfloat16, ...) # query 正常 arg[2]Tensor(shape(1, 8, 4, 64), dtypetorch.bfloat16, ...) # key 的 head_dim 不匹配你能得出什么结论positions 看起来合理query 看起来合理key 的最后一维与预期的 rotary/head 维度不一致。这通常意味着 bug 出在 projection 布局、head 打包或 cache 格式上而不是 rotary 内核本身。Step 7与 compute-sanitizer 组合使用对于更隐蔽的越界写等问题把内核 API 日志与 CUDA 内存检查工具结合export SGLANG_KERNEL_API_LOGLEVEL3 export SGLANG_KERNEL_API_LOGDESTdebug.log compute-sanitizer --tool memcheck python3 /tmp/sglang_llm_crash.py用debug.log查看到达崩溃 API 边界的确切输入。典型compute-sanitizer输出 COMPUTE-SANITIZER Invalid __global__ write of size 4 bytes at 0x1234 in SomeKernel by thread (256,0,0) in block (10,0,0) Address 0x... is out of bounds工作流是用 sanitizer 输出定位崩溃的内核用debug.log定位到达该边界之前的张量两者交叉得到完整证据链。如果需要更同步的主机端错误上报可以单独把CUDA_LAUNCH_BLOCKING1作为后续对照实验。它不属于默认工作流的一部分因为改变执行时序可能掩盖与并发相关的问题。Step 8与 cuda-gdb 组合使用需要栈回溯而非仅内存诊断时export SGLANG_KERNEL_API_LOGLEVEL3 export SGLANG_KERNEL_API_LOGDESTdebug.log cuda-gdb --args python3 /tmp/sglang_llm_crash.py在cuda-gdb内部(cuda-gdb) run (cuda-gdb) where然后将回溯与debug.log相互对照gdb 告诉你崩溃发生在哪个内核、哪个指令日志告诉你到达该内核时输入张量是什么。Step 9内核级 printf 调试当你拥有 CUDA 内核源码时printf()仍是缩小坏索引、坏启动几何或状态传播问题范围的有效手段。基本模式__global__ void MyKernel(const float* input, float* output, int n) { int idx blockIdx.x * blockDim.x threadIdx.x; if (threadIdx.x 0 blockIdx.x 0) { printf(n%d input0%f\n, n, input[0]); } if (idx n) { output[idx] input[idx] * 2.0f; } }内核启动后强制刷新输出my_kernel(...) torch.cuda.synchronize()printf输出是异步的不 synchronize 就可能丢失。Warp 专用内核选择正确的打印线程问题threadIdx.x 0只打印 block 中第一个 warp 的信息对 warp-specialized 内核这往往错过真正出错的 warp 或 specialization 组。更好的模式每个 warp 的第一个 lane 打印__global__ void WarpSpecializedKernel(...) { // 示例每个 warp 的第一个 lane if ((threadIdx.x % 32) 0) { printf(warp%d\n, threadIdx.x / 32); } }或者如果内核按更大的 specialization 组组织改为每组打印一次而非每 block 一次。常见错误只有 warp 0 打印// 只有 warp 0 会打印 if (threadIdx.x 0) { printf(warp%d\n, threadIdx.x / 32); }快速参考内核类型打印条件说明简单内核threadIdx.x 0每 block 一个线程通常足够Warp 专用内核每个 warp 选一个代表 lane例如threadIdx.x % 32 0组专用内核每组选一个代表 lane依据内核的调度布局选择其他内核调试手段assert(value 0.0f value must be non-negative); static_assert(BLOCK_SIZE % 32 0, BLOCK_SIZE must be warp aligned);static_assert能在编译期捕获 warp 对齐等结构性问题与运行期断言互补。环境变量参考变量取值说明SGLANG_KERNEL_API_LOGLEVEL0关闭日志默认值1仅函数名3输入输出 元数据5Level 3 张量统计min/max/mean/nan/inf10Level 5 崩溃安全张量 dumpSGLANG_KERNEL_API_LOGDESTstdout输出到 stdoutstderr输出到 stderrpath输出到文件log_%i.txt%i展开为进程 IDSGLANG_KERNEL_API_DUMP_DIRpathLevel 10 dump 目录同样支持%iSGLANG_KERNEL_API_DUMP_INCLUDE通配符列表仅 dump 匹配的 APISGLANG_KERNEL_API_DUMP_EXCLUDE通配符列表跳过匹配的 API日志系统的几个实现细节零开销设计日志级别为 0 时debug_kernel_api直接返回原函数装饰器不会给热路径增加任何运行时开销kernel_api_logging.pymaybe_wrap_debug_kernel也在环境变量为 0 时直接透传原函数debug_utils.py。防重复包装包装函数带有_debug_kernel_wrapped标记重复装饰会被安全跳过kernel_api_logging.py。torch.compile 兼容torch.compiler.is_compiling()为真时 wrapper 直接透传原函数避免干扰图编译同文件 L418-L420。统计跳过CUDA graph capture 期间 Level 5 统计被有意跳过标记为statistics[skipped: CUDA graph capture in progress]Level 10 dump 同样跳过Tensor dump skipped: CUDA graph capture in progress因为统计与 dump 都需要同步/拷贝到 CPU在 capture 期间不允许。函数名推断默认函数名由__qualname__与模块名组合而成并剥离sglang./sgl_kernel.前缀得到sglang.quant_method.UnquantizedLinearMethod.apply这类紧凑名称也支持op_name显式覆盖同文件 L367-L384。最佳实践1. 从 Level 3 开始export SGLANG_KERNEL_API_LOGLEVEL3Level 3 通常足以捕获错误的形状、dtype 和设备放置。2. 数值问题用 Level 5export SGLANG_KERNEL_API_LOGLEVEL5怀疑 NaN/Inf 时使用。3. 崩溃复现用 Level 10export SGLANG_KERNEL_API_LOGLEVEL10进程在你能检查实时张量之前就崩溃时这是最有用的模式。配套建议需要真实模型运行的成功路径输入/输出 dump 时临时为该调试会话关闭 CUDA graphLevel 10 过于嘈杂时配合SGLANG_KERNEL_API_DUMP_INCLUDE/SGLANG_KERNEL_API_DUMP_EXCLUDE精确圈定 API而不是对所有覆盖的 API 全量 dump。4. 崩溃时写文件而非 stdoutexport SGLANG_KERNEL_API_LOGDESTcrash.log进程中止时文件日志比 stdout 更安全。5. 生产环境关闭日志unset SGLANG_KERNEL_API_LOGLEVEL关闭时装饰器返回原始可调用对象不增加运行时日志开销见上文零开销设计。故障排查没有日志输出依次检查echo $SGLANG_KERNEL_API_LOGLEVEL—— 确认级别不为 0echo $SGLANG_KERNEL_API_LOGDEST—— 确认输出目标失败路径是否经过受覆盖的 API 边界——纯 PyTorch 调用默认不在覆盖范围内。输出太多降低级别export SGLANG_KERNEL_API_LOGLEVEL3CUDA Graph Capture 期间统计被跳过如果看到statistics[skipped: CUDA graph capture in progress]这是预期行为Level 5 统计在 CUDA graph capture 期间被有意跳过以避免同步副作用。CUDA Graph Capture 期间张量 dump 被跳过如果看到Tensor dump skipped: CUDA graph capture in progress同样是预期行为Level 10 dump 需要把张量拷贝到 CPU这在 CUDA graph capture 期间不允许。总结一套完整的崩溃排查工作流把以上步骤串成一条可复用的排查链路首次崩溃SGLANG_KERNEL_API_LOGLEVEL3LOGDESTfile重跑从日志找有输入无输出的边界数值异常升级到LOGLEVEL5检查 min/max/mean/nan/inf判断坏值来源需要回放现场升级到LOGLEVEL10DUMP_DIR拿到inputs.pt/metadata.jsonexecution_status: exception即崩溃点证据多进程日志与 dump 路径加%i按 PID 隔离越界写等内存问题叠加compute-sanitizer --tool memcheck定位内核用日志定位输入需要栈回溯叠加cuda-gdbwhere后与日志交叉比对内核自持在自定义 CUDA 内核中按 warp/组选择代表线程printf配合torch.cuda.synchronize()与assert/static_assert。这套方法论的依据都落在仓库中日志核心实现见 python/sglang/kernels/kernel_api_logging.pyLLM 侧接入见 python/sglang/srt/utils/custom_op.pyDiffusion 侧接入见 python/sglang/multimodal_gen/runtime/layers/custom_op.py 与 python/sglang/multimodal_gen/runtime/layers/utils.pyAOT 内核条件包装见 python/sglang/kernels/aot/python/sgl_kernel/debug_utils.py。下次再遇到莫名其妙的 CUDA 崩溃先开日志再谈猜测。【免费下载链接】sglangSGLang is a high-performance serving framework for large language models and multimodal models.项目地址: https://gitcode.com/GitHub_Trending/sg/sglang创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表