MUSA Compute Sanitizer 用户指南
什么是 MUSA Compute Sanitizer?
MUSA Compute Sanitizer(简称 mt-compute-sanitizer)是摩尔线程自主研发的 GPU 运行时调试工具,专为 MUSA GPU 应用程序设计。该工具可帮助开发者在程序运行时检测和定位内存错误,包括越界访问、内存泄漏和 API 调用错误等问题。MUSA Compute Sanitizer 通过详细的错误报告和调用栈信息,帮助开发者快速定位并修复问题,提高代码质量和开发效率。
主要功能
| 功能 | 默认状态 | 说明 |
|---|---|---|
| global memory 越界检查 | 默认启用 | 检测 kernel 访问不属于自己的 global memory 地址。 |
| MUSA API 错误检查 | 默认启用 | 报告 MUSA Runtime / Driver API 返回的错误。 |
| 显存泄漏检查 | 默认关闭 | 检测 musaMalloc() 后未正确 musaFree() 的显存,需要 --leak-check=full。 |
显存泄漏检查不是默认项。排查泄漏或长稳显存增长问题时,必须显式添加 --leak-check=full。
推荐运行方式
最常用的方式是在原始测试命令前加 mt-compute-sanitizer,并使用 --log-file 保存 MT Compute Sanitizer 日志:
/usr/local/musa/bin/mt-compute-sanitizer \
--log-file san.log \
./your_test_program --case-args
建议同时保存应用输出:
/usr/local/musa/bin/mt-compute-sanitizer \
--log-file san.log \
./your_test_program --case-args \
> app.out 2>&1
| 文件 | 内容 |
|---|---|
san.log | MT Compute Sanitizer 检查到的 OOB、leak、API error、错误汇总。 |
app.out | 被测程序自己的输出、报错和测试框架日志。 |
环境检查
排查问题前建议记录环境信息:
mthreads-gmi
/usr/local/musa/bin/mcc --version
/usr/local/musa/bin/mt-compute-sanitizer --version
/usr/local/musa/bin/mt-compute-sanitizer -h
提报问题时至少保留 Driver、Toolkit、mt-compute-sanitizer 版本和完整命令。
内存错误检测
越界检查
global memory 越界检查默认启用。典型命令如下:
/usr/local/musa/bin/mt-compute-sanitizer \
--log-file oob.log \
./your_test_program --case-args \
> oob.out 2>&1
查看结果:
grep "ERROR SUMMARY" oob.log
grep -n "Out of bounds memory access\|ERROR SUMMARY" oob.log
代码示例:越界访问
- 有 Bug 的代码
- 修复后的代码
#include <musa_runtime_api.h>
__global__ void oob_load_kernel(const int *input, int *output, int n) {
int idx = blockIdx.x * blockDim.x + threadIdx.x;
// BUG:只保护了写 output 的范围,但读 input 时使用 idx + 1。
// 当 idx == n - 1 时,会加载 input[n],发生 global memory 越界读。
if (idx < n) {
output[idx] = input[idx + 1];
}
}
int main() {
const int n = 64;
int h_input[n] = {0};
int h_output[n] = {0};
int *d_input = nullptr;
int *d_output = nullptr;
musaMalloc((void **)&d_input, n * sizeof(int));
musaMalloc((void **)&d_output, n * sizeof(int));
// 加载输入数据到设备侧。
musaMemcpy(d_input, h_input, n * sizeof(int), musaMemcpyHostToDevice);
oob_load_kernel<<<1, 64>>>(d_input, d_output, n);
musaDeviceSynchronize();
musaMemcpy(h_output, d_output, n * sizeof(int), musaMemcpyDeviceToHost);
musaFree(d_input);
musaFree(d_output);
return 0;
}
#include <musa_runtime_api.h>
__global__ void oob_load_kernel(const int *input, int *output, int n) {
int idx = blockIdx.x * blockDim.x + threadIdx.x;
// 修复:读 input[idx + 1] 时,idx 必须小于 n - 1。
if (idx + 1 < n) {
output[idx] = input[idx + 1];
}
}
int main() {
const int n = 64;
int h_input[n] = {0};
int h_output[n] = {0};
int *d_input = nullptr;
int *d_output = nullptr;
musaMalloc((void **)&d_input, n * sizeof(int));
musaMalloc((void **)&d_output, n * sizeof(int));
musaMemcpy(d_input, h_input, n * sizeof(int), musaMemcpyHostToDevice);
oob_load_kernel<<<1, 64>>>(d_input, d_output, n);
musaDeviceSynchronize();
musaMemcpy(h_output, d_output, n * sizeof(int), musaMemcpyDeviceToHost);
musaFree(d_input);
musaFree(d_output);
return 0;
}
典型 OOB 日志片段:
========= COMPUTE-SANITIZER
========= ERROR SUMMARY: 448 errors
========= ERROR SUMMARY: 448 errors were not printed. Use --print-limit option to adjust the number of printed errors
该示例说明:错误数量很多时,summary 仍会统计总错误数;如果详细错误被截断,可通过 --print-limit 调整打印数量。
常见修复方式:
| 常见原因 | 修复建议 |
|---|---|
| kernel 中没有边界检查 | 添加 if (idx < n) 等边界保护。 |
| launch 配置产生多余线程 | 确认多余线程不会访问非法地址。 |
| offset / stride / shape 计算错误 | 检查索引 和指针计算。 |
泄漏检查
显存泄漏检查默认关闭,必须显式开启:
/usr/local/musa/bin/mt-compute-sanitizer \
--leak-check=full \
--log-file leak.log \
./your_test_program --case-args \
> leak.out 2>&1
查看 结果:
grep "ERROR SUMMARY" leak.log
grep -n "Leaked\|musaMalloc\|ERROR SUMMARY" leak.log
代码示例:显存泄漏
- 有 Bug 的代码
- 修复后的代码
#include <musa_runtime_api.h>
int main() {
int *data = nullptr;
// BUG:申请了 MUSA device memory,但程序退出前没有释放。
musaMalloc((void **)&data, 4096);
musaDeviceSynchronize();
return 0;
}
#include <musa_runtime_api.h>
int main() {
int *data = nullptr;
musaMalloc((void **)&data, 4096);
musaDeviceSynchronize();
// 修复:每个 musaMalloc 都应有对应的 musaFree。
musaFree(data);
return 0;
}
典型 leak 日志片段:
========= COMPUTE-SANITIZER
========= Leaked 4096 bytes at 0x1006b600000
========= Saved host backtrace up to driver entry point at allocation time
========= Host Frame: main [0x1155] in your_program
========= ERROR SUMMARY: 1 error
适用场景:
- 长稳后显存持续增长。
- 循环跑 case 后显存没有回落。
- 怀疑异常路径没有释放 MUSA memory。
常见修复方式:
| 常见原因 | 修复建议 |
|---|---|
musaMalloc() 后没有 musaFree() | 为每个设备内存分配添加对应释放。 |
| 提前 return | 使用统一清理路径,确保所有分支释放资源。 |
| 异常路径未释放 | 使用 RAII 或封装资源生命周期。 |
MUSA API 错误检查
--report-api-errors 默认值为 explicit,即默认报告用户显式调用的顶层 MUSA Runtime / Driver API error。
代码示例:API 错误
- 有 Bug 的代码
- 修复后的代码
#include <musa_runtime_api.h>
int main() {
// BUG:选择不存在的 device id,会触发 musaErrorInvalidDevice。
musaSetDevice(9999);
return 0;
}
#include <musa_runtime_api.h>
int main() {
int count = 0;
musaGetDeviceCount(&count);
if (count <= 0) {
return 1;
}
// 修复:只选择合法 device id。
musaSetDevice(0);
return 0;
}
典型 API error 日志片段:
========= COMPUTE-SANITIZER
========= Program hit musaErrorInvalidDevice (error 101) due to "invalid device ordinal" on MUSA API call to musaSetDevice.
========= Saved host backtrace up to driver entry point at error
========= Host Frame: main [0x1149] in your_program
========= ERROR SUMMARY: 1 error
如果明确不关注 API error,可以关闭:
/usr/local/musa/bin/mt-compute-sanitizer \
--report-api-errors=no \
--log-file no_api_error.log \
./your_test_program --case-args
关闭后,被测程序自己的 API 返回值仍可能出现在应用输出中,但 MT Compute Sanitizer 不再把 API error 计入 ERROR SUMMARY。
--report-api-errors 支持三种模式:
| 模式 | 行为 |
|---|---|
explicit | 默认模式,仅报告用户显式调用的顶层 Runtime / Driver API error。 |
all | 报告所有非过滤项的 Runtime / Driver API error,包括内部隐式或嵌套 API 调用返回的错误。 |
no | 关闭 API error 上报。 |
以下 API 返回值通常用于轮询或状态检查,因此不会被 MT Compute Sanitizer 报告为 API error:
| API | 返回值 | 含义 |
|---|---|---|
musaStreamQuery / musaEventQuery | musaErrorNotReady | 查询的 stream 或 event 尚未完成。 |
musaDeviceEnablePeerAccess | musaErrorPeerAccessAlreadyEnabled | 当前 device 到目标 peer device 的 peer access 已经启用。 |
musaDeviceDisablePeerAccess | musaErrorPeerAccessNotEnabled | 当前 device 到目标 peer device 的 peer access 尚未启用。 |
muStreamQuery / muEventQuery | MUSA_ERROR_NOT_READY | 查询的 stream 或 event 尚未完成。 |
muCtxEnablePeerAccess | MUSA_ERROR_PEER_ACCESS_ALREADY_ENABLED | 当前 context 到目标 peer context 的 peer access 已经启用。 |
muCtxDisablePeerAccess | MUSA_ERROR_PEER_ACCESS_NOT_ENABLED | 当前 context 到目标 peer context 的 peer access 尚未启用。 |