已关闭
[RFC]: Error日志统一增加reason #3951
wanglijun55创建于  8月12日关闭于  20 天前
wanglijun55成员
8月12日 创建

状态(Status): Draft
作者(Authors): @wanglijun55
创建日期(Created): 2026-08-12
更新日期(Updated): 2026-08-12
相关 Issue/PR: https://gitcode.com/Ascend/pytorch/pull/44441

1. 概述

1.1 简介

用户在排查分布式训练异常时,遇到如下报错:

RuntimeError: createHCCLCommOrigin:torch_npu/csrc/distributed/ProcessGroupHCCL.cpp:2587 NPU function error: device error type O, error code is 19
[ERROR] 2026-05-25-18:58:11 (PID:5907, Device:0, RankID:0) ERR00100 PTA call acl api failed

日志仅打印了 error code 19和 错误码ERR00100,没有说明错误原因和可行的解决,需要额外查阅PTA/CANN的资料来确认,因此提出了日志管理易用性提升的JDC。

本 RFC 提出统一的解决方案:为所有errorcode的已有日志,通过检索 error_code_map 获取 reason,并将其拼接到已有日志之后。

1.2 动机

  • 用户痛点: 易用性差,部分error的日志仅打印错误码,不利于排查和自动化异常扫描。
  • 优化价值: 泛化已有error_code_map的机制,提升易用性。
  • 范围: 覆盖 ACL 标准错误码、HCCL 通信错误码、LCCL 本地通信错误码、aclnn StressDetect 专用错误码四个体系。覆盖 v2.7.1+, v2.10.0-26.0.0(客户发现问题的版本), v2.10.0-26.1.0(客户使用版本)的分支。

1.3 目标

  • 目标:
    1. 对搜到的全部 18 处 "error code is" 日志位置完成 reason 补全(排除注释和非日志的 2 处,实际修改 16 处日志语句)。
    2. 在 NPUErrorCodes.h 中新增 HcclErrorCode 类(24 个码)和 StressDetectErrorCode 类(4 个码),与已有的 AclErrorCode 并列。
    3. 对每个日志位置:声明对应 ErrorCode 实例 → 调用 error_code_map.find() 检索 → 将命中的 reason 拼接到日志中。
    4. 提供可执行的验证方案(包括可复用已有测试用例和需新增的测试用例)。
  • 非目标:
    1. 不改动日志格式的整体结构(仅追加 description 字段)。
    2. 不涉及 Python 层日志整改(_error_code.py 中的正则匹配模式不在范围内)。
    3. 不引入新的错误处理框架或抽象层。

2. 用例分析

2.1 搜索范围与结果

grep -rn "error code is" --include="*.cpp" --include="*.h" --include="*.py" torch_npu/

共命中 18 处。按是否需要修改分类如下:

分类 计数 说明
🔧 需要新增 ErrorCode 类 + 修改日志 4 处 HCCL / LCCL / StressDetect 错误码,当前无对应的 ErrorCode 查找类
🔧 已有 ErrorCode 类,仅需修改日志 12 处 ACL 标准错误码,已有 AclErrorCode 类,但日志中未调用 lookup
⬜ 不需要修改 2 处 注释文字(NPUErrorCodes.h:160)和 Python 正则匹配模式(_error_code.py:104),非日志输出

2.2 逐文件详情

文件 行号 数量 错误码类型 已有 ErrorCode 类? 需新增类?
NPUErrorCodes.h 160 1 — — ⬜ 注释,不适用
NPUException.h 35 1 (宏 NPU_CHECK_WARN) ACL ✅ AclErrorCode ❌
NPUException.h 140 1 (宏 CHECK_AND_THROW_ERROR_WITH_SPECIFIC_MESSAGE) ACL + HCCL ❌(HCCL 部分无) ✅ 需为本宏新增 HcclErrorCode 查找
NPUException.h 173, 201 2 (宏 NPU_CHECK_ERROR_CHECK_UCE) ACL ✅ AclErrorCode ❌(HCCL 路径已覆盖)
NPUException.h 195 1 (同宏内 DEVICE_TASK_ABORT 分支) ACL ✅(同宏外层已声明) ❌
NPUException.h 230, 249 2 (宏 OPS_CHECK_ERROR) ACL ✅ AclErrorCode ❌
NPUException.cpp 249 1 ACL ✅ AclErrorCode ❌
NPUException.cpp 273 1 ACL ✅ AclErrorCode ❌
NPUException.cpp 334 1 ACL ✅ AclErrorCode ❌
CalcuOpUtil.h 45, 53 1 (宏 ACL_REQUIRE_OK_OP,含 2 路径) ACL ✅ AclErrorCode ❌
HCCLUtils.hpp 26, 39 1 (宏 HCCL_CHECK_ERROR,含 2 路径) HCCL ❌ ✅ 需新增 HcclErrorCode
ProcessGroupLCCL.cpp 254 1 LCCL (复用 HCCL 码) ❌ ✅ 需新增 HcclErrorCode
Stress_detect.cpp 103, 104, 112, 113 4 aclnn StressDetect ❌ ✅ 需新增 StressDetectErrorCode
_error_code.py 104 1 — — ⬜ 正则模式,不适用

需要修改的日志语句共 16 处(排除 2 处不适用),涉及 7 个文件。其中 4 处需要新增 ErrorCode 类,12 处可直接复用已有 AclErrorCode。额外发现同类问题:ProcessGroupLCCL.cpp:191(使用 "error code:" 而非 "error code is",需一并修复)。

2.3 场景用例

场景 触发路径 受影响用户
任意 NPU 算子执行失败 ACL_REQUIRE_OK_OP 宏(所有 op-plugin 算子调用)、NPU_CHECK_ERROR_CHECK_UCE 宏(所有 aclrt 调用) 所有 NPU 用户
HCCL 集合通信失败 HCCL_CHECK_ERROR 宏(hcclBroadcast / hcclAllReduce 等)、CHECK_AND_THROW_ERROR_WITH_SPECIFIC_MESSAGE 分布式训练用户
LCCL 本地通信失败 ProcessGroupLCCL::run_collective 多卡分布式用户
Device task abort NPU_CHECK_ERROR_CHECK_UCE 的 DEVICE_TASK_ABORT 分支 AI 训练/推理用户
硬件压力检测失败 StressDetector::transfer_result 硬件诊断/运维
ACL 诊断 API 内部失败 getDeviceErrorMessage / repair_device_error 内部诊断路径
OPS 侧错误检查 OPS_CHECK_ERROR 宏(op-plugin 侧) 算子开发者

2.4 功能点与质量要求

  • 功能: 全部 16 处日志打印位置均追加对应的 description。
  • 兼容性: 不改动日志整体格式,仅在 error code 后追加 [Error]: <description>(ACL 风格)或 (<description>)(HCCL 风格)。
  • 可测试性: 已有测试用例可覆盖主要路径;对无法自动化的路径通过代码审查验证。
  • 可维护性: 新增的 HcclErrorCode / StressDetectErrorCode 与已有 AclErrorCode 保持一致的命名和结构风格。

3. 方案设计

3.1 总体方案

核心思路: 为每一处 "error code is" 日志引入对应 ErrorCode 类的 lookup 逻辑,实现 error code → reason 的自动翻译。

修改前:

RuntimeError: createHCCLCommOrigin:torch_npu/csrc/distributed/ProcessGroupHCCL.cpp:2587 NPU function error: device error type O, error code is 19
[ERROR] 2026-05-25-18:58:11 (PID:5907, Device:0, RankID:0) ERR00100 PTA call acl api failed
或者
RuntimeError: createHCCLCommOrigin:torch_npu/csrc/distributed/ProcessGroupHCCL.cpp:2587 NPU function error: device error type O, error code is 19
[ERROR] 2026-05-25-18:58:11 (PID:5907, Device:0, RankID:0) ERR00100 PTA call acl api failed
[PID: 247947] 2026-08-12-16:26:39.217.599 Invalid_Argument(EE1003): SetDevice failed because value 9 for parameter drv devId is invalid. Expected value: [0, 8).
        Solution: 1.Check the input parameter range of the function. 2.Check the function invocation relationship.
        TraceBack (most recent call last):
        rtSetDevice execution failed, reason=device id error[FUNC:FuncErrorReason][FILE:error_message_manage.cc][LINE:65]
        open device 9 failed, runtime result = 10700

修改后:

RuntimeError: createHCCLCommOrigin:torch_npu/csrc/distributed/ProcessGroupHCCL.cpp:2587 NPU function error: device error type O, error code is 19
[ERROR] 2026-05-25-18:58:11 (PID:5907, Device:0, RankID:0) ERR00100 PTA call acl api failed
[Error]: Invalid device ID.
        Check whether the device ID is valid.
[PID: 247947] 2026-08-12-16:26:39.217.599 Invalid_Argument(EE1003): SetDevice failed because value 9 for parameter drv devId is invalid. Expected value: [0, 8).
        Solution: 1.Check the input parameter range of the function. 2.Check the function invocation relationship.
        TraceBack (most recent call last):
        rtSetDevice execution failed, reason=device id error[FUNC:FuncErrorReason][FILE:error_message_manage.cc][LINE:65]
        open device 9 failed, runtime result = 10700
3.1.1 关键数据结构:ErrorCode 类

所有错误码映射使用统一的 std::unordered_map<int, std::string> 结构,定义在 NPUErrorCodes.h 中。

已有的 AclErrorCode(以 code 100001 为例):

torch_npu/csrc/core/npu/NPUErrorCodes.h:

namespace c10_npu::acl {

class AclErrorCode {
public:
    std::unordered_map<int, std::string> error_code_map = {
        // ... ~120 entries ...
        {100001, "ACL uninitialized.\n\
         (1)Check whether the acl.init interface has been invoked...\n\
         (2)Check whether the initialization interface of the corresponding function..."},
        // ...
    };
};

本 RFC 新增的 HcclErrorCode(以 code 19 为例):

class HcclErrorCode {
public:
    std::unordered_map<int, std::string> error_code_map = {
        {1, "parameter error"},
        // ...
        {19, "call network api fail"},   // ← 客户遇到的报错码
        // ...
        {24, "out of memory"},
    };
};

本 RFC 新增的 StressDetectErrorCode(bitmask 编码):

class StressDetectErrorCode {
public:
    std::unordered_map<int, std::string> error_code_map = {
        {0x1, "bit fail (hardware malfunction)"},
        {0x2, "low bit fail (hardware malfunction)"},
        {0x4, "high bit fail (hardware malfunction)"},
        {0x8, "clear device state fail (voltage recovery failed)"},
    };
};
3.1.2 关键查找模式:如何在日志位置命中 error_code_map

在每个日志位置,通过以下三步完成 lookup:

// Step 1: 声明 static 局部 ErrorCode 实例(仅首次调用时构造,线程安全)
static c10_npu::acl::AclErrorCode err_map;    // ACL 错误码
// static c10_npu::acl::HcclErrorCode hccl_err_map;  // HCCL 错误码
// static c10_npu::acl::StressDetectErrorCode stress_err_map;  // Stress 错误码

// Step 2: 在 error_code_map 中检索
auto it = err_map.error_code_map.find(code);

// Step 3: 根据检索结果拼接到日志
//   命中 → 输出 description
//   未命中 → 输出 "." 兜底,不破坏原日志格式
const char* reason = (it != err_map.error_code_map.end()) ? it->second.c_str() : ".";

在 TORCH_CHECK 宏中的实际写法(ACL 风格,\n[Error]: 前缀):

// 修改前(只有数字,没有 reason):
TORCH_CHECK((expr) == 0, __func__, ":", __FILE__, ":", __LINE__,
    " NPU error,NPU error code is:", expr, "\n",
    c10_npu::acl::AclGetErrMsg(), OPS_ERROR(ErrCode::INTERNAL));

// 修改后(增加 err_map 声明 + lookup + reason 拼接):
static c10_npu::acl::AclErrorCode err_map;     // ← Step 1: 声明
TORCH_CHECK((expr) == 0, __func__, ":", __FILE__, ":", __LINE__,
    " NPU error,NPU error code is:", expr,      //   原日志不变
    (err_map.error_code_map.find(static_cast<int>(expr)) !=    // ← Step 2: find() 检索
     err_map.error_code_map.end() ?
     "\n[Error]: " + err_map.error_code_map[static_cast<int>(expr)] : "."), // ← Step 3: 命中→reason / 未命中→"."
    "\n",
    c10_npu::acl::AclGetErrMsg(), OPS_ERROR(ErrCode::INTERNAL));

在 HCCL_CHECK_ERROR 宏中的实际写法(HCCL 风格, (<desc>) 后缀):

// 修改前:
oss << " HCCL function error: " << ... << ", error code is " << Error << ...

// 修改后:
static c10_npu::acl::HcclErrorCode hccl_err_map;    // ← Step 1: 声明
oss << " HCCL function error: " << ...
    << ", error code is " << Error
    << (hccl_err_map.error_code_map.find(static_cast<int>(Error)) !=   // ← Step 2: find() 检索
        hccl_err_map.error_code_map.end() ?
        " " + hccl_err_map.error_code_map[static_cast<int>(Error)] : "")  // ← Step 3: 命中→" reason" / 未命中→""
    << " " << DIST_ERROR(ErrCode::HCCL) + ".\n";

StressDetect 场景(bitmask 需逐位检查):

// Stress 错误码为 bitmask 组合,需要逐位查找
static std::string getStressDetectErrorDesc(int errorCode) {
    static c10_npu::acl::StressDetectErrorCode err_map;    // ← Step 1: 声明
    std::string desc;
    for (const auto& [code, msg] : err_map.error_code_map) {
        if (errorCode & code) {                              // ← Step 2: bitmask 匹配
            if (!desc.empty()) desc += "; ";
            desc += msg;                                     // ← Step 3: 拼接所有命中项
        }
    }
    return desc;  // 返回 "" 表示未命中(调用侧兜底为 ".")
}

// 调用侧:
auto desc = getStressDetectErrorDesc(detectResult);
ASCEND_LOGW("..., error code is %d.%s", device_id, detectResult,
            desc.empty() ? "." : ("\n[Error]: " + desc).c_str());
3.1.3 实现流程
1. 分析每处日志打印的 error code 属于哪种错误码体系(ACL / HCCL / StressDetect)
   ├── ACL 体系  → 复用已有 AclErrorCode,在日志位置新增 lookup
   ├── HCCL 体系 → 在 NPUErrorCodes.h 新增 HcclErrorCode 类(24 个码),再在日志位置新增 lookup
   └── Stress 体系 → 在 NPUErrorCodes.h 新增 StressDetectErrorCode 类(4 个码),
                     再在日志文件新增辅助函数 + lookup

2. lookup 三步走:
   Step 1: static XxxErrorCode err_map;          // 仅构造一次,线程安全
   Step 2: err_map.error_code_map.find(code)     // O(1) 哈希查找
   Step 3: 命中 → 拼接 description / 未命中 → 拼接 "." 或 ""

3. 检索结果写入日志:
   - 命中 → 追加 "\n[Error]: <description>"(ACL 风格)或 " (<description>)"(HCCL 风格)
   - 未命中 → 追加 "."(句号),不影响下游日志解析
3.1.4 设计原则
  • 已有 map 直接复用,不重复定义。
  • 使用 static 局部变量,map 仅构造一次,线程安全(只读)。
  • 各体系保持各自的描述风格(ACL 用 \n[Error]: 前缀,HCCL 用 (<desc>) 后缀),不强行统一。
  • lookup 结果始终有兜底值(. 或 ""),不会因缺失映射导致崩溃或空输出。

3.2 技术选型

方案 描述 评估 决策
A. 在每个日志位置硬编码 if (code==19) desc="call network api fail" 零抽象 简单但不可维护,无法复用 ❌
B. 定义 ErrorCode 类(std::unordered_map<int, std::string>),在各日志位置声明 static 实例并 lookup 轻量级,与已有 AclErrorCode 模式一致 复用性好,新增错误码只需追加 map 条目 ✅ 采用
C. 抽象为全局通用函数 getErrorDesc(int code) 单一入口 不同错误码体系的码值可能重叠(如 ACL 的 1 ≠ HCCL 的 1),无法在同一个 map 中区分 ❌

3.3 功能与性能设计

3.3.1 新增 ErrorCode 类

需要在 NPUErrorCodes.h 中 namespace c10_npu::acl 内新增两个 ErrorCode 类:

HcclErrorCode(HCCL 通信错误码,24 个):

class HcclErrorCode {
public:
    std::unordered_map<int, std::string> error_code_map = {
        {1, "parameter error"},
        {2, "empty pointer"},
        {3, "memory error"},
        {4, "internal error"},
        {5, "not support feature"},
        {6, "not found specific resource"},
        {7, "resource unavailable"},
        {8, "call system interface error"},
        {9, "timeout"},
        {10, "open file fail"},
        {11, "tcp connect fail"},
        {12, "roce connect fail"},
        {13, "tcp transfer fail"},
        {14, "roce transfer fail"},
        {15, "call runtime api fail"},
        {16, "call driver api fail"},
        {17, "call profiling api fail"},
        {18, "call cce api fail"},
        {19, "call network api fail"},
        {20, "try again"},
        {21, "error cqe"},
        {22, "error communicator suspending"},
        {23, "retry constraint"},
        {24, "out of memory"},
    };
}; /* hcclError code */

StressDetectErrorCode(aclnn 压力检测错误码,4 个):

class StressDetectErrorCode {
public:
    std::unordered_map<int, std::string> error_code_map = {
        {0x1, "bit fail (hardware malfunction)"},
        {0x2, "low bit fail (hardware malfunction)"},
        {0x4, "high bit fail (hardware malfunction)"},
        {0x8, "clear device state fail (voltage recovery failed)"},
    };
}; /* aclnn stress detect error codes */
3.3.2 逐位置改动方案

以下按错误码体系分组,说明每处日志的修改方式。

组 A:ACL 错误码(已有 AclErrorCode,新增 lookup)—— 共 12 处

这 12 处日志打印的是标准 ACL 错误码,AclErrorCode 类已存在于 NPUErrorCodes.h:7-398(含 ~120 个码),只需在日志位置声明 static 实例并追加 lookup 结果。

A1. NPUException.h:35 — NPU_CHECK_WARN 宏

当前代码:

#define NPU_CHECK_WARN(err_code)
    do {
        auto Error = err_code;
        if ((Error) != ACL_ERROR_NONE) {
            TORCH_NPU_WARN("NPU warning, error code is ", Error,
                "[Error]: ",
                (err_map.error_code_map.find(Error) !=
                err_map.error_code_map.end() ?
                "\n[Error]: " + err_map.error_code_map[Error] : "."),
                "\n", c10_npu::c10_npu_get_error_message());
        }
    } while (0)

→ 已补全,无需修改。

A2. NPUException.h:173,201 — NPU_CHECK_ERROR_CHECK_UCE 宏(compact + 非 compact)

compact 路径(line 173)和非 compact 主路径(line 201)均已通过 err_map.error_code_map.find() 进行 lookup。

→ 已补全,无需修改。
⚠️ 但非 compact 的 DEVICE_TASK_ABORT 分支(line 195)漏掉了 lookup,见下方 A3。

A3. NPUException.h:195 — NPU_CHECK_ERROR_CHECK_UCE 的 DEVICE_TASK_ABORT 分支 🔧

调用链路: NPU kernel abort → NPU_CHECK_ERROR_CHECK_UCE → DEVICE_TASK_ABORT 分支

当前代码:

} else if (error_code == ACL_ERROR_RT_DEVICE_TASK_ABORT) {
    TORCH_CHECK(false,
        __func__, ":", __FILE__, ":", __LINE__,
        " NPU function error: ", (device_error_msg.empty() ?
        " FORCE STOP" : device_error_msg),
        ", error code is ", error_code,
        PTA_ERROR(ErrCode::ACL));

修改方案: 宏外层(line 151)已声明 static c10_npu::acl::AclErrorCode err_map;,直接复用。在 PTA_ERROR 后追加 lookup:

} else if (error_code == ACL_ERROR_RT_DEVICE_TASK_ABORT) {
    TORCH_CHECK(false,
        __func__, ":", __FILE__, ":", __LINE__,
        " NPU function error: ", (device_error_msg.empty() ?
        " FORCE STOP" : device_error_msg),
        ", error code is ", error_code,
        PTA_ERROR(ErrCode::ACL),
        (err_map.error_code_map.find(error_code) !=
         err_map.error_code_map.end() ?
         "\n[Error]: " + err_map.error_code_map[error_code] : "."));

A4. NPUException.h:230,249 — OPS_CHECK_ERROR 宏(compact + 非 compact)

均已通过 err_map.error_code_map.find() 进行 lookup。

→ 已补全,无需修改。

A5. NPUException.cpp:334 — checkUceErrAndRepair()

当前代码:

static c10_npu::acl::AclErrorCode err_map;
err_msg = ... + " NPU error, error code is " + std::to_string(err) + PTA_ERROR(ErrCode::ACL) +
    (err_map.error_code_map.find(err) != err_map.error_code_map.end() ?
    "\n[Error]: " + err_map.error_code_map[err] : ".") + ...

→ 已补全,无需修改。

A6. NPUException.cpp:249 — getDeviceErrorMessage() 🔧

调用链路: 异常处理 → getDeviceErrorMessage() → AclrtGetErrorVerbose 自身失败

当前代码:

if (ret != ACL_ERROR_NONE) {
    ASCEND_LOGE("AclrtGetErrorVerbose failed, device is %d, error code is %d.", device, ret);
    return "";
}

修改方案:

if (ret != ACL_ERROR_NONE) {
    static c10_npu::acl::AclErrorCode err_map;
    std::string err_info = (err_map.error_code_map.find(ret) != err_map.error_code_map.end() ?
                            "\n[Error]: " + err_map.error_code_map[ret] : ".");
    ASCEND_LOGE("AclrtGetErrorVerbose failed, device is %d, error code is %d.%s",
                device, ret, err_info.c_str());
    return "";
}

A7. NPUException.cpp:273 — repair_device_error() 🔧

调用链路: UCE 修复 → repair_device_error() → AclrtRepairError 自身失败

当前代码:

if (ret != ACL_ERROR_NONE) {
    ASCEND_LOGE("AclrtRepairError failed, device is %d, error code is %d.", error_info.device, ret);
    return false;
}

修改方案: 与 A6 同模式。

if (ret != ACL_ERROR_NONE) {
    static c10_npu::acl::AclErrorCode err_map;
    std::string err_info = (err_map.error_code_map.find(ret) != err_map.error_code_map.end() ?
                            "\n[Error]: " + err_map.error_code_map[ret] : ".");
    ASCEND_LOGE("AclrtRepairError failed, device is %d, error code is %d.%s",
                error_info.device, ret, err_info.c_str());
    return false;
}

A8. CalcuOpUtil.h:45,53 — ACL_REQUIRE_OK_OP 宏 🔧

调用链路: 任意 NPU 算子 → OpParamMaker::InnerRun() / InnerRunOpApi() → ACL_REQUIRE_OK_OP

改动要点: 宏内新增 static c10_npu::acl::AclErrorCode err_map;,compact 和非 compact 两路径均追加 lookup 结果。

#define ACL_REQUIRE_OK_OP(expr, opstr)
    do {
        if (ASCEND_UNLIKELY((expr) != 0)) {
            std::cout << (opstr) << std::endl;
            static c10_npu::acl::AclErrorCode err_map;    // ← 新增
            if (c10_npu::option::OptionsManager::IsCompactErrorOutput()) {
                std::ostringstream oss;
                oss << " NPU error,NPU error code is:" << (expr)
                  << (err_map.error_code_map.find(static_cast<int>(expr)) !=
                     err_map.error_code_map.end() ?
                     "\n[Error]: " + err_map.error_code_map[static_cast<int>(expr)] : ".")  // ← 新增
                  << "\n"
                  << OPS_ERROR(ErrCode::INTERNAL);
                ...
            } else {
                TORCH_CHECK((expr) == 0, __func__, ":", __FILE__, ":", __LINE__,
                    " NPU error,NPU error code is:", expr,
                    (err_map.error_code_map.find(static_cast<int>(expr)) !=
                     err_map.error_code_map.end() ?
                     "\n[Error]: " + err_map.error_code_map[static_cast<int>(expr)] : "."),  // ← 新增
                    "\n",
                    c10_npu::acl::AclGetErrMsg(), OPS_ERROR(ErrCode::INTERNAL));
            }
        }
    } while (0)

组 B:HCCL 错误码(需新增 HcclErrorCode + 修改日志)—— 共 4 处

HCCL 错误码(1-24)尚无对应的 ErrorCode 类。需先在 NPUErrorCodes.h 中新增 HcclErrorCode 类(见 3.3.1),再在以下位置添加 lookup。

B1. HCCLUtils.hpp:26,39 — HCCL_CHECK_ERROR 宏 🔧

调用链路: 分布式通信 → hcclBroadcast / hcclAllReduce 等 → HCCL_CHECK_ERROR

修改方案(compact 路径 + 非 compact 路径):

#define HCCL_CHECK_ERROR(err_code, ...)
    do {
        auto Error = err_code;
        if ((Error) != HCCL_SUCCESS) {
            CHECK_AND_THROW_ERROR_WITH_SPECIFIC_MESSAGE(Error);
            static c10_npu::acl::HcclErrorCode hccl_err_map;  // ← 新增
            if (c10_npu::option::OptionsManager::IsCompactErrorOutput()) {
                std::ostringstream oss;
                oss << " HCCL function error: " << getErrorFunction(#err_code, ##__VA_ARGS__)
                   << ", error code is " << Error
                   << (hccl_err_map.error_code_map.find(static_cast<int>(Error)) !=
                       hccl_err_map.error_code_map.end() ?
                        " " + hccl_err_map.error_code_map[static_cast<int>(Error)] : "")  // ← 新增
                   << " " << DIST_ERROR(ErrCode::HCCL) + ".\n";
                ...
            } else {
                auto retmsg = std::string(__func__) + ":" + __FILE__ + ":" + std::to_string(__LINE__) +
                    " HCCL function error: " + getErrorFunction(#err_code, ##__VA_ARGS__) +
                    ", error code is " + std::to_string(Error) +
                    (hccl_err_map.error_code_map.find(static_cast<int>(Error)) !=
                        hccl_err_map.error_code_map.end() ?
                        " " + hccl_err_map.error_code_map[static_cast<int>(Error)] : "") +  // ← 新增
                    " " + DIST_ERROR(ErrCode::HCCL) + ".\n" +
                    c10_npu::c10_npu_get_error_message();
                ...
            }
        }
    } while (0)

B2. NPUException.h:140 — CHECK_AND_THROW_ERROR_WITH_SPECIFIC_MESSAGE 宏 🔧

调用链路: HCCL_CHECK_ERROR → CHECK_AND_THROW_ERROR_WITH_SPECIFIC_MESSAGE(HCCL 错误码在此宏中需要 HcclErrorCode 查找)

当前此宏已有 AclErrorCode lookup,但缺少 HcclErrorCode lookup。需增加:

#define CHECK_AND_THROW_ERROR_WITH_SPECIFIC_MESSAGE(err_code)
    ...
    static c10_npu::acl::AclErrorCode err_map;
    static c10_npu::acl::HcclErrorCode hccl_err_map;  // ← 新增
    TORCH_CHECK(false,
        __func__, ":", __FILE__, ":", __LINE__,
        " NPU function error: ", device_error_msg,
        ", error code is ", error_code,
        (err_map.error_code_map.find(error_code) != err_map.error_code_map.end() ?
            "\n[Error]: " + err_map.error_code_map[error_code] :
            (hccl_err_map.error_code_map.find(error_code) != hccl_err_map.error_code_map.end() ?
                "\n[Error]: " + hccl_err_map.error_code_map[error_code] : ".")),  // ← 新增 HCCL fallback
        PTA_ERROR(ErrCode::ACL));

B3. ProcessGroupLCCL.cpp:254 — LCCL 操作错误 🔧

调用链路: LCCL 通信 → ProcessGroupLCCL::run_collective → fn() 返回非零

LCCL 底层复用 HCCL 错误码体系,使用同一个 HcclErrorCode 类。需新增 include。

修改方案:

#include "torch_npu/csrc/core/npu/NPUErrorCodes.h"  // ← 新增

// line 254:
auto ret = fn(inputs[i], outputs[i], lcclComms[i], lcclStream);
static c10_npu::acl::HcclErrorCode hccl_err_map;  // ← 新增
TORCH_CHECK(ret == 0, "LCCL function error:", opTypeToString(opType).c_str(),
    ", error code is ", ret,
    (hccl_err_map.error_code_map.find(ret) != hccl_err_map.error_code_map.end() ?
     " (" + hccl_err_map.error_code_map[ret] + ")" : ""),  // ← 新增
    "\n");

// line 191 同步修复("error code:" 而非 "error code is",同类问题):
TORCH_CHECK(ret == 0, "init lccl comm failed, error code: ", ret,
    (hccl_err_map.error_code_map.find(ret) != hccl_err_map.error_code_map.end() ?
     " (" + hccl_err_map.error_code_map[ret] + ")" : ""),
    PTA_ERROR(ErrCode::INTERNAL));

组 C:StressDetect 错误码(需新增 StressDetectErrorCode + 修改日志)—— 共 4 处

C1-C4. Stress_detect.cpp:103,104,112,113 — transfer_result() 函数 🔧

调用链路: torch_npu.npu.stress_detect(detect_type='aic') → _npu_stress_detect() → StressDetector::perform_stress_detect() → transfer_result()

修改方案: StressDetect 错误码为 bitmask,需逐位检查。先在 NPUErrorCodes.h 新增 StressDetectErrorCode 类(见 3.3.1),再在 Stress_detect.cpp 中新增辅助函数并修改 4 处日志 + 1 处硬编码。

#include "torch_npu/csrc/core/npu/NPUErrorCodes.h"  // ← 确认 include

// 新增辅助函数
static std::string getStressDetectErrorDesc(int errorCode) {
    static c10_npu::acl::StressDetectErrorCode err_map;
    std::string desc;
    for (const auto& [code, msg] : err_map.error_code_map) {
        if (errorCode & code) {
            if (!desc.empty()) desc += "; ";
            desc += msg;
        }
    }
    return desc;
}

// transfer_result() 修改后:
int StressDetector::transfer_result(int detectResult)
{
    int ret = kDetectFailed;
    switch (detectResult) {
        case 0:
            ret = kDetectSucceeded;
            ASCEND_LOGI("..., device id is %d.", device_id);
            break;
        case ACLNN_STRESS_BIT_FAIL:
        case ACLNN_STRESS_LOW_BIT_FAIL:
        case ACLNN_STRESS_HIGH_BIT_FAIL:
            ret = kDetectFailedWithHardwareFailure;
            {
                auto hw_desc = getStressDetectErrorDesc(detectResult);                // ← 新增
                ASCEND_LOGW("..., device id is %d, error code is %d.%s",
                    device_id, detectResult,                                          // ← 修改
                    hw_desc.empty() ? "." : ("\n[Error]: " + hw_desc).c_str());       // ← 新增
                TORCH_NPU_WARN("..., device id is ", device_id, ", error code is ",
                    detectResult,                                                     // ← 修改
                    hw_desc.empty() ? "." : "\n[Error]: " + hw_desc);                 // ← 新增
            }
            break;
        case ACLNN_CLEAR_DEVICE_STATE_FAIL:
            {
                auto fail_desc = getStressDetectErrorDesc(detectResult);              // ← 新增(替代原硬编码)
                ASCEND_LOGW("..., device id is %d, error code is %d.%s",
                    device_id, detectResult,                                          // ← 修改
                    fail_desc.empty() ? "." : ("\n[Error]: " + fail_desc).c_str());   // ← 新增
                TORCH_CHECK(false, "..., error code is ", detectResult,               // ← 修改
                    fail_desc.empty() ? "." : "\n[Error]: " + fail_desc,              // ← 新增
                    PTA_ERROR(ErrCode::ACL));
            }
            break;
        default:
            ret = kDetectFailed;
            {
                auto fail_desc = getStressDetectErrorDesc(detectResult);              // ← 新增
                ASCEND_LOGW("..., device id is %d, error code is %d.%s",
                    device_id, detectResult,                                          // ← 修改
                    fail_desc.empty() ? "." : ("\n[Error]: " + fail_desc).c_str());   // ← 新增
                TORCH_NPU_WARN("..., device id is ", device_id, ", error code is ",
                    detectResult,                                                     // ← 修改
                    fail_desc.empty() ? "." : "\n[Error]: " + fail_desc);             // ← 新增
            }
            break;
    }
    return ret;
}

3.4 安全隐私与DFX设计

  • 兼容性: 不改动日志整体格式,仅追加 description 字段。下游日志解析如仅依赖 error code is <数字> 模式,不受影响(数字仍在原位置)。对于依赖整行正则匹配的工具,需确认追加的 [Error]: <描述> 或 (<描述>) 不会导致误匹配。
  • 可维护性: HcclErrorCode / StressDetectErrorCode 与已有 AclErrorCode 同文件(NPUErrorCodes.h)、同命名空间(c10_npu::acl),新成员加入时只需在对应 map 中追加条目。
  • 可靠性: lookup 使用 static 局部变量,仅构造一次,线程安全(只读)。map 未命中时输出 . 或空字符串,不会因缺失映射导致崩溃。
  • 可测试性: 见第 4 节测试设计。

3.5 编程与调用设计

本提案不涉及对外 API 变更,全部改动在已有宏/函数内部实现,对外接口透明。

3.5.1 受影响模块
模块 文件 改动量 改动类型
错误码定义 NPUErrorCodes.h +38 行 新增 HcclErrorCode(24 码)+ StressDetectErrorCode(4 码)
DIST 通信(HCCL) HCCLUtils.hpp +5 行 HCCL_CHECK_ERROR 宏内新增 HcclErrorCode lookup
PTA 错误报告 NPUException.h +5 行 CHECK_AND_THROW_ERROR_WITH_SPECIFIC_MESSAGE 新增 HcclErrorCode fallback;DEVICE_TASK_ABORT 分支补 lookup
PTA 错误诊断 NPUException.cpp +6 行 2 处 ASCEND_LOGE 新增 AclErrorCode lookup
DIST 通信(LCCL) ProcessGroupLCCL.cpp +8 行 新增 include + 2 处 TORCH_CHECK 新增 HcclErrorCode lookup
OPS 错误报告 CalcuOpUtil.h +4 行 ACL_REQUIRE_OK_OP 宏内新增 AclErrorCode lookup
NPU 诊断 Stress_detect.cpp +30 行 新增辅助函数 + 4 处日志改造 + 1 处硬编码替代
合计 7 文件 ~+96 行 —
3.5.2 ErrorCode 类总览(变更后)
类名 文件位置 覆盖范围 码数量 状态
AclErrorCode NPUErrorCodes.h:7-398 标准 ACL 错误码 ~120+ 已有
HcclErrorCode NPUErrorCodes.h HCCL 通信错误码 24 新增
StressDetectErrorCode NPUErrorCodes.h aclnn 压力检测错误码 4 新增

4. 测试设计

4.1 可复用的已有测试用例

被测路径 已有测试 验证方式
ACL_REQUIRE_OK_OP (CalcuOpUtil.h) third_party/op-plugin/test/test_base_ops/test_npu_scaled_mm.py — test_npu_scaled_mm_invalid_* 系列 传入非法参数触发宏,assertRaisesRegex 检查 [Error]
NPU_CHECK_ERROR_CHECK_UCE (NPUException.h) torch_npu.npu.set_device(-1) 触发异常 捕获异常,检查 [Error]: Invalid device.
HCCL_CHECK_ERROR (HCCLUtils.hpp) 分布式训练已有用例(test/npu/test_c10d.py)覆盖正常通信路径;异常路径需构造 代码审查 + 多卡环境手动验证
DEVICE_TASK_ABORT (NPUException.h) test/npu/test_uce.py — monitor() 捕获 "FORCE STOP" 扩展断言,检查 [Error]: The aicpu execution is abnormal.
ACL 诊断内部错误 (NPUException.cpp) 无直接覆盖 代码审查

4.2 建议新增的测试用例

# 新建文件 测试内容 优先级
1 test/npu/test_stress_detect.py 调用 torch_npu.npu.stress_detect('aic'),验证返回码及日志 🔴 高
2 test/npu/test_error_message.py 通用回归:各异常路径检查 [Error] description 或 (<description>) 🟡 中
3 test/npu/test_lccl.py LCCL 通信失败场景的 error description 🟢 低(需多卡)

4.3 批量验证脚本

#!/bin/bash
set -e

echo "=== 1. ACL_REQUIRE_OK_OP (CalcuOpUtil.h) ==="
# compact 模式
TORCH_NPU_COMPACT_ERROR_OUTPUT=1 python -c "
import torch, torch_npu
try:
    torch_npu.npu.scaled_mm(torch.randn(2,3).npu(), torch.randn(3,2).npu(),
                            scale_a=torch.randn(1).npu().to(torch.int32))
except RuntimeError as e:
    assert '[Error]' in str(e), 'FAIL: compact mode missing [Error]'
    print('PASS: compact mode')
"
# 非 compact 模式
python -c "
import torch, torch_npu
try:
    torch_npu.npu.scaled_mm(torch.randn(2,3).npu(), torch.randn(3,2).npu(),
                            scale_a=torch.randn(1).npu().to(torch.int32))
except RuntimeError as e:
    assert '[Error]' in str(e), 'FAIL: non-compact mode missing [Error]'
    print('PASS: non-compact mode')
"

echo "=== 2. NPU_CHECK_ERROR (NPUException.h) ==="
python -c "
import torch, torch_npu
try:
    torch_npu.npu.set_device(-1)
except RuntimeError as e:
    assert '[Error]' in str(e), 'FAIL: NPU_CHECK_ERROR path missing [Error]'
    print('PASS: NPU_CHECK_ERROR path')
"

echo "=== 3. HCCL_CHECK_ERROR (HCCLUtils.hpp) ==="
# 需要多卡环境
# python -c "
# import os; os.environ['MASTER_ADDR']='127.0.0.1'; os.environ['MASTER_PORT']='29500'
# import torch; import torch_npu
# torch.distributed.init_process_group(backend='hccl', rank=0, world_size=1)
# torch.distributed.barrier()
# "

echo "=== 4. ASCEND 日志检查 ==="
grep -r "\[Error\]:" /var/log/npu/ascend_log/device-*/device-*.log 2>/dev/null | tail -5 || \
  echo "WARN: No device log found."

4.4 验证检查清单


5. 缺点和风险

风险 影响 应对
日志体积增大 每条异常日志增加约 20-200 字符(description 文本) 可接受(诊断价值 >> 存储成本);compact 模式可用 TORCH_NPU_COMPACT_ERROR_OUTPUT=1 控制
下游日志解析 依赖 error code is <数字> 正则的脚本可能受追加文本干扰 数字仍在原位置;如有冲突可通过 compact 模式恢复
HcclErrorCode 码值准确性 24 个 HCCL 码的描述需与 CANN 文档对齐 review 时逐条核对 HCCL 官方文档
StressDetectErrorCode 描述来源 当前描述来自代码注释和推断 与 CANN/HW 团队确认后定稿
代码量增加 ~96 行新增代码 模式统一、可复制,维护成本低

Breaking Change: 无。所有改动在已有日志输出基础上追加字段,不影响 API 签名或行为。


6. 现有技术

本方案参考了 NPUErrorCodes.h 中已有的 AclErrorCode 类(std::unordered_map<int, std::string> 存储 ~120 个错误码与描述映射)的设计模式。AclErrorCode 已在 NPU_CHECK_WARN(NPUException.h:33)、NPU_CHECK_ERROR_CHECK_UCE(NPUException.h:151)、OPS_CHECK_ERROR(NPUException.h:223)、checkUceErrAndRepair(NPUException.cpp:332)等位置被正确使用并验证有效。

本 RFC 将该模式系统化推广到所有 "error code is" 日志位置,并扩展覆盖 HCCL 和 StressDetect 两个新的错误码体系。


7. 未解决问题

  1. StressDetectErrorCode 错误描述精确性: 当前描述基于代码注释和 ACLNN_CLEAR_DEVICE_STATE_FAIL 分支的硬编码推断("Voltage recovery failed" 对应 0x8)。需与 CANN 文档/HW 团队确认 ACLNN_STRESS_BIT_FAIL (0x1) / ACLNN_STRESS_LOW_BIT_FAIL (0x2) / ACLNN_STRESS_HIGH_BIT_FAIL (0x4) 的官方描述是否准确。
  2. LCCL 与 HCCL 错误码的对应关系: 当前假设 LCCL 完全复用 HCCL 错误码体系(HcclErrorCode,码值 1-24)。需确认 LCCL 是否有独立的错误码空间(超出 1-24 范围的码)。
  3. ACL 与 HCCL 的 lookup 格式不统一: 当前 AclErrorCode 路径使用 \n[Error]: <description> 格式,HcclErrorCode 路径使用 (<description>) 后缀格式。RFC 通过后是否需统一为一种格式?建议保持现有差异(各体系历史原因),或在后续 RFC 中统一。

附录

  • 参考资料: CANN 错误码参考文档、HCCL API 参考文档、NPUErrorCodes.h 中现有 AclErrorCode 实现
  • 术语表:
    • ACL: Ascend Computing Language,昇腾计算语言
    • HCCL: Huawei Collective Communication Library,华为集合通信库
    • LCCL: Local Collective Communication Library,本地集合通信库
    • DFX: Design for X(兼容性、可维护性、可测试性、可靠性)
  • 文档更新计划: 无需更新对外文档,改动对用户透明

欢迎加入社区,感谢您对社区的贡献 🎉!

likedislike
Wwanglijun55成员
8月12日 添加了label:rfc
Wwanglijun55成员
8月12日 关联了看板:FrameworkPTAdapter 版本issue看板
TorchNPU-BotTorchNPU-Bot成员
8月12日 添加了label:triage-review
Wwanglijun55成员
8月12日 修改了issue 的描述
Wwanglijun55成员
8月12日 将 wanglijun55 设为负责人
Wwanglijun55成员
8月12日 修改了issue 的描述
Wwanglijun55成员
8月12日 关联了pull request:[WIP] modify error log
TorchNPU-BotTorchNPU-Bot成员
8月12日 添加了label:bot-triaged;删除了label:triage-review
TorchNPU-Bot
TorchNPU-Bot成员
8月12日 评论:

检测到当前 issue 已关联 PR,自动添加标签:bot-triaged

likedislike
Cchenqiming10成员
9月10日 关联了pull request:fix: append human-readable reason to HCCL/ACL error messages
Cchenqiming10成员
24 天前 关联了pull request:fix: append human-readable reason to HCCL/ACL error messages
Cchenqiming10成员
24 天前 关联了pull request:fix: append human-readable reason to HCCL/ACL error messages
Cchenqiming10成员
24 天前 关联了pull request:fix: append human-readable reason to HCCL/ACL error messages
Cchenqiming10成员
24 天前 关联了pull request:fix: append human-readable reason to HCCL/ACL error messages
Cchenqiming10成员
24 天前 关联了pull request:fix: append human-readable reason to HCCL/ACL error messages
Wwanglijun55成员
20 天前 issue状态由 TODO 改变为 DONE
Wwanglijun55成员
20 天前 关闭了 issue
ascend-robotascend-robot成员
20 天前 添加了label:resolved