CUDA 编程指南 13.4.2 · 非官方中文译本
连续阅读
原著发布于 2026 年 9 月 10 日对照 NVIDIA 英文原文对应英文网页

4.9. 错误日志管理

错误日志管理允许用通俗英语报告 CUDA API 错误,与仅使用 API 返回值相比,能够提供有关错误原因的更多信息。

4.9.1. 背景

传统上,CUDA API 调用失败的唯一指示是非零返回码。许多 CUDA 错误码只描述一类故障,而不指出是哪个参数或哪项要求导致了故障。错误日志管理在这些返回码之外补充了描述性消息,旨在帮助开发者诊断 API 使用错误。

4.9.2. 启用

将环境变量CUDA_LOG_FILE设置为stdout、stderr或可写文件的路径。CUDA 会将生成的每条日志消息写入所选目标。

CUDA 还会将最近的消息存储在内部缓冲区中。即使在应用程序启动前未设置CUDA_LOG_FILE,应用程序也可以使用错误日志管理 API 注册回调或转储此缓冲区。无错误的执行可能不会生成任何消息。

4.9.3. 输出

写入CUDA_LOG_FILE或由转储 API 返回的消息使用以下格式:

[Time][TID][Source][Severity] Message

例如,调用cuLogsDumpToMemory时将缓冲区参数设为空指针,可能会生成类似以下内容的消息:

[22:21:32.099][25642][CUDA][E] buffer cannot be NULL
[22:21:32.099][25642][CUDA][E] Returning 1 (CUDA_ERROR_INVALID_VALUE) from cuLogsDumpToMemory

如果不使用错误日志管理,该调用会返回CUDA_ERROR_INVALID_VALUE。调用cuGetErrorString会提供通用描述invalid argument,但不会指出哪个参数无效。

4.9.4. API 说明

CUDA Driver API 和 Runtime API 均提供错误日志管理功能。Runtime 接口可处理 Runtime API 和 Driver API 生成的消息;这两个接口使用相同的底层日志缓冲区。

4.9.4.1. 回调注册

Driver API 的回调类型和注册函数如下:

typedef void (CUDA_CB *CUlogsCallback)(
    void *data, CUlogLevel logLevel, char *message, size_t length);

CUresult CUDAAPI cuLogsRegisterCallback(
    CUlogsCallback callbackFunc,
    void *userData,
    CUlogsCallbackHandle *callback_out);

CUresult CUDAAPI cuLogsUnregisterCallback(
    CUlogsCallbackHandle callback);

对应的 Runtime API 回调类型和函数如下:

typedef void (CUDART_CB *cudaLogsCallback_t)(
    void *data, cudaLogLevel logLevel, char *message, size_t length);

cudaError_t CUDARTAPI cudaLogsRegisterCallback(
    cudaLogsCallback_t callbackFunc,
    void *userData,
    cudaLogsCallbackHandle *callback_out);

cudaError_t CUDARTAPI cudaLogsUnregisterCallback(
    cudaLogsCallbackHandle callback);

Driver 回调将错误报告为CU_LOG_LEVEL_ERROR,将警告报告为CU_LOG_LEVEL_WARNING。对应的 Runtime 值为cudaLogLevelError和cudaLogLevelWarning。格式化输出分别将这些级别表示为E和W。

CUDA 将userData原样传递给回调。callback_out参数是可选的,但如果应用程序打算注销该回调,就必须保留返回的句柄。

回调接收消息文本及其明确的字节长度。消息不包含添加到格式化文件输出和转储输出中的时间戳、线程 ID、来源或严重性前缀。除非 API 参考明确保证该行为,否则应用程序不应依赖特定的回调线程或调用时机。

4.9.4.2. 迭代器与日志转储

日志迭代器标识内部缓冲区中的一个位置。调用cuLogsCurrent或cudaLogsCurrent会将迭代器设置为缓冲区的当前末尾,因此,之后使用该迭代器进行转储时,只会返回随后生成的消息。

Driver API 函数如下:

CUresult CUDAAPI cuLogsCurrent(
    CUlogIterator *iterator_out, unsigned int flags);

CUresult CUDAAPI cuLogsDumpToFile(
    CUlogIterator *iterator,
    const char *pathToFile,
    unsigned int flags);

CUresult CUDAAPI cuLogsDumpToMemory(
    CUlogIterator *iterator,
    char *buffer,
    size_t *size,
    unsigned int flags);

对应的 Runtime API 函数如下:

cudaError_t CUDARTAPI cudaLogsCurrent(
    cudaLogIterator *iterator_out, unsigned int flags);

cudaError_t CUDARTAPI cudaLogsDumpToFile(
    cudaLogIterator *iterator,
    const char *pathToFile,
    unsigned int flags);

cudaError_t CUDARTAPI cudaLogsDumpToMemory(
    cudaLogIterator *iterator,
    char *buffer,
    size_t *size,
    unsigned int flags);

所有获取当前位置的函数和转储函数中的flags参数均保留供将来使用,且必须为零。

以下 Runtime API 示例标记日志的当前末尾,并在之后仅转储该位置之后生成的消息:

#include <cstdio>
#include <cuda_runtime_api.h>

cudaError_t printNewCudaLogs()
{
    cudaLogIterator iterator;
    cudaError_t status = cudaLogsCurrent(&iterator, 0);
    if (status != cudaSuccess) {
        return status;
    }

    int deviceCount;
    status = cudaGetDeviceCount(&deviceCount);
    cudaError_t deviceCountStatus = status;

    char buffer[25600];
    size_t size = sizeof(buffer);
    status = cudaLogsDumpToMemory(&iterator, buffer, &size, 0);
    if (status != cudaSuccess) {
        return status;
    }
    if (size != 0) {
        std::fwrite(buffer, 1, size, stderr);
    }
    return deviceCountStatus;
}

在两者之间执行的 CUDA 调用仍然需要正常处理返回码。此示例演示了迭代器如何选择一段日志消息;它不要求故意执行无效的 CUDA 调用。该固定大小的数组使用文档规定的最大转储容量,即 25,600 字节。

4.9.4.3. 转储行为

如果iterator为空,转储会请求内部缓冲区当前保留的所有条目。如果提供了迭代器,转储就从该位置开始。成功转储后,CUDA 会将所提供的迭代器推进到日志的当前末尾。

内部缓冲区最多保留 100 个条目。如果请求的消息已被覆盖而不再保留,CUDA 会添加一条声明,指出输出已被截断,并返回仍然可用的条目。

cuLogsDumpToMemory和cudaLogsDumpToMemory还有以下要求:

  1. size最初指定buffer的容量。

  2. CUDA 会以空字符终止该缓冲区。各条目之间以换行符而非空字符分隔。

  3. 25,600 字节的输出缓冲区足以容纳所有保留的日志数据。

  4. 成功时,size包含已写入的字节数,不包括终止空字符。

  5. 如果没有消息可供转储,函数会成功并将size设置为零。

  6. 如果缓冲区只能容纳部分而非全部请求的消息,CUDA 会在输出前添加一条截断声明,省略无法容纳的最旧消息,并在输出中保留最新的消息。

  7. 如果缓冲区无法容纳任何消息,函数会返回CUDA_ERROR_INVALID_VALUE或cudaErrorInvalidValue,并将size设置为零。

4.9.5. 限制与已知问题

  1. 达到 100 个条目的上限时,CUDA 会替换最旧的条目。

  2. 目前并非所有 CUDA API 都会生成描述性消息。覆盖范围仍在持续扩大。

  3. CUDA 直到生成消息时才会验证CUDA_LOG_FILE目标。

  4. 日志消息未进行本地化;CUDA 以美式英语提供这些消息。

原著 © 2007 至 2026 NVIDIA Corporation 及其关联公司。保留所有权利。
中文译本仅作学习参考,非 NVIDIA 官方出版物。技术含义有疑问时请核对NVIDIA 英文原文。
关于本译本

输入关键词搜索全书。