CANN Runtime EH0009 Invalid_Argument 错误码全解析:格式、触发原理与排查实战
CANN Runtime EH0009 Invalid_Argument 错误码全解析:格式、触发原理与排查实战
本篇技术指南聚焦 CANN 运行时(runtime)中 ACL(ASCENDCL)侧的 EH0009 Invalid_Argument 错误码,系统讲解其错误信息的标准格式与占位符含义、真实的报错场景(以 acltdtGetDataItem 为例)、源码层的生成链路,以及"检查参数范围 + 检查调用关系"两步骤排查方法。读完本篇,你将能够快速识别 EH0009 报错中的关键信息,结合 host 日志定位到具体接口与非法参数,并在 TDT 通道、内存复制、流管理等典型场景中完成问题闭环。
EH0009 错误码概述
EH0009 属于 ACL Errors(错误分类 ACL Errors)下的 Invalid_Argument 错误子类,用于表示接口调用时传入的参数值本身不合法(而非参数为空指针、路径不存在等其他参数类错误)。它在 src/dfx/error_manager/error_code.json 中正式注册,其关键元数据如下:
| 元数据项 | 值 |
|---|---|
| errClass | ACL Errors |
| errTitle | Invalid_Argument |
| ErrCode | EH0009 |
| ErrMessage | %s failed. Value %s for parameter %s is invalid. Reason: %s. |
| Arglist | func, value, param, reason |
在 ACL 的日志管理头文件 src/acl/common/log_inner.h 中,EH0009 被定义为命名常量 INVALID_PARAM_REASON_MSG,与 EH0001(INVALID_PARAM_MSG)、EH0002(INVALID_NULL_POINTER_MSG)、EH0007(INVALID_VALUE_MSG)、EH0012(INVALID_PARAM_NO_VALUE_MSG)等同属一个参数校验错误码家族,但 EH0009 是其中唯一带详细 reason 说明的通用参数非法错误码,报错信息的信息量最丰富。
需要说明的是,该错误码在当前仓库中同时用于 ACL 侧(src/acl)和 ADUMP 维测组件(src/dfx/adump/adump/common/adump_error_manager.h),两者的报错格式一致,排查思路可以通用。
错误信息格式解读
EH0009 的报错格式固定为:
%s failed. Value %s for parameter %s is invalid. Reason: %s.
四个占位符 %s 的含义依次为:
| 占位符 | 含义 | 取值示例 |
|---|---|---|
| 第 1 个 %s | 报错阶段 / 报错的接口名(func) | acltdtGetDataItem |
| 第 2 个 %s | 实际传入的非法参数值(value) | 5 |
| 第 3 个 %s | 非法参数名(param) | index |
| 第 4 个 %s | 报错原因(reason) | index 5 is greater than or equal to dataset size 10 |
掌握这一格式是排障的第一步:看到 EH0009 报错时,优先从第 2、3 个占位符锁定"哪个接口的哪个参数传错了",再从第 4 个占位符确认违反的具体约束(越界、超出范围、取值不合法等),最后回到第 1 个占位符对应的接口文档核对合法取值范围。
真实报错示例剖析:acltdtGetDataItem
原文档给出的典型报错示例如下:
acltdtGetDataItem failed. Value 5 for parameter index is invalid. Reason: index 5 is greater than or equal to dataset size 10.
该报错来自 TDT(Tensor Data Transfer)通道场景。结合示例仓库中的用法(example/2_advanced_features/tdt_channel/0_simple_channel/main.cpp),acltdtGetDataItem 的调用形态通常为:
const size_t datasetSize = acltdtGetDatasetSize(dataset); // 获取 Dataset 中 DataItem 的数量
acltdtDataItem* item = acltdtGetDataItem(dataset, 0); // 按下标取出第 0 个 DataItem
其中 index 参数是"取第几个数据项"的下标,其合法范围是 [0, datasetSize)(0 ≤ index < datasetSize)。上例中 Dataset 里只有 10 个数据项,却传入了 index = 5 之外的越界值 5,即 index 5 大于等于 dataset size 10,因此触发 EH0009。
从源码层面看,该接口的定义位于 src/acl/acl_tdt_channel/tensor_data_transfer.cpp,校验逻辑非常直白:
acltdtDataItem* acltdtGetDataItem(const acltdtDataset* dataset, size_t index)
{
ACL_REQUIRES_NOT_NULL_RET_NULL_INPUT_REPORT(dataset);
if (index >= dataset->blobs.size()) {
std::string errMsg = acl::AclErrorLogManager::FormatStr(
"index %zu is greater than or equal to dataset size %zu", index, dataset->blobs.size());
const std::string indexVal = std::to_string(index);
acl::AclErrorLogManager::ReportInputError(
acl::INVALID_PARAM_REASON_MSG, std::vector<const char*>({"func", "value", "param", "reason"}),
std::vector<const char*>({__func__, indexVal.c_str(), "index", errMsg.c_str()}));
return nullptr;
}
return dataset->blobs[index];
}
这里可以提炼出两点关键事实:
- 报错原因(reason)由具体接口自己拼接。
index %zu is greater than or equal to dataset size %zu这条 reason 是acltdtGetDataItem内部生成的,说明 EH0009 的 reason 部分是"接口自定义、人读友好"的,通常已经直接告诉了你违反了什么约束。 - 越界访问不会导致崩溃,而是返回
nullptr。index越界时接口先报 EH0009,随后返回空指针。因此调用方若忽略返回值检查,后续对空指针的解引用会引发第二层问题,这也是"检查调用关系"的意义所在——参见 example/2_advanced_features/tdt_channel/0_simple_channel/main.cpp 中ERROR_LOG("acltdtGetDataItem returned nullptr")的防御式写法。
源码级原理:EH0009 是如何生成的
EH0009 的生成链路可以概括为"业务代码显式上报"模式,即不是系统全局兜底捕获的异常,而是每个接口在校验参数时主动调用的。核心链路如下:
- 接口内部完成参数合法性判断(如上述
index >= dataset->blobs.size()); - 调用
acl::AclErrorLogManager::ReportInputError(...)上报错误; ReportInputError的实现位于 src/acl/common/log_inner.cpp,其本质是对宏REPORT_PREDEFINED_ERR_MSG(errorCode, key, val)的封装——将错误码字符串("EH0009")与键值对(func / value / param / reason)组合,交给底层日志框架格式化输出;- 格式化模板
%s failed. Value %s for parameter %s is invalid. Reason: %s.与错误码元数据一同注册在 src/dfx/error_manager/error_code.json 中,Arglist字段func, value, param, reason与调用时传入的键一一对应。
因此,当你在代码中看到 ReportInputError 或 REPORT_PREDEFINED_ERR_MSG 配合 INVALID_PARAM_REASON_MSG(即 "EH0009")出现时,就意味着该处是一个 EH0009 的触发点。用同样的方式在仓库中搜索,可以找到 EH0009 在 Dataset 内存类型不一致(memType 不统一)、DataItem 校验等多个场景的触发位置。
值得一提的对照是:Runtime 侧(src/runtime)存在一条格式几乎相同但错误码不同的路径——src/runtime/core/inc/common/error_code_meta.h 中的 EE1011 Invalid_Argument,其消息模板同为 %s failed. Value %s for parameter %s is invalid. Reason: %s.。也就是说,ACL 侧看到的是 EH0009,而更底层的 Runtime(RTS)侧对应 EE1011,两者是同一类参数非法问题在不同软件层的表达,排障时可相互参考(参见 docs/zh/error_code_ref/RTS-Errors 目录下相关文档)。
解决方法一:检查接口的输入参数范围
EH0009 的第一类触发原因是参数值超出接口约定的合法范围,排查步骤如下:
- 锁定参数名:从报错的第 3 个占位符确定是哪个参数(如
index); - 核对合法范围:到该接口的 API 参考文档中确认参数取值范围,例如 TDT 通道相关接口见 docs/zh/api_ref/17-01_tensor_data_transfer.md,错误码与返回码定义见 docs/zh/api_ref/22_error_reporting_APIs.md;
- 结合 reason 反推约束:报错中的 reason 往往直接给出约束,如"index 必须小于 dataset size";
- 修正调用代码:对于越界类问题,典型修法是用
acltdtGetDatasetSize动态获取上限后再传参,或先对index做边界判断。
// 修正示例:先取 size,再做边界判断,避免越界
const size_t datasetSize = acltdtGetDatasetSize(dataset);
if (index >= datasetSize) {
// 处理非法下标,而不是直接传给 acltdtGetDataItem
return;
}
acltdtDataItem* item = acltdtGetDataItem(dataset, index);
解决方法二:检查接口的调用关系
EH0009 的第二类触发原因是调用关系不满足接口前置条件。许多 ACL 接口对参数对象有隐式的前置依赖,例如:
- 对象尚未初始化:DataItem/Dataset 未通过
acltdtCreateDataItem/acltdtCreateDataset创建即被使用; - 状态机不满足:在通道尚未创建、收发未配对时调用依赖通道状态的接口;
- 资源未就绪:在错误恢复、释放之后的句柄仍被引用。
以 TDT 通道的典型生命周期为例(详见 example/2_advanced_features/tdt_channel/0_simple_channel/README.md),正确调用顺序是:创建通道(acltdtCreateChannel)→ 构造 DataItem 与 Dataset(acltdtCreateDataItem、acltdtCreateDataset、acltdtAddDataItem)→ 发送/接收 → 读取(acltdtGetDatasetSize、acltdtGetDataItem)→ 销毁(acltdtDestroyDataItem、acltdtDestroyDataset)。仓库示例 example/2_advanced_features/tdt_common_utils.h 展示了遍历 Dataset 并逐个销毁 DataItem 的规范写法:先 acltdtGetDatasetSize 拿数量,再循环 acltdtGetDataItem(dataset, i) 取指,最后统一 acltdtDestroyDataItem,保证"创建—使用—销毁"生命周期完整。
排查调用关系时,应重点确认:当前报错接口所需的前置对象(Dataset、通道句柄等)是否已完成创建与初始化,其生命周期是否贯穿整个调用过程,以及是否在同一上下文中使用。
日志定位与配套排障手段
EH0009 通过 REPORT_PREDEFINED_ERR_MSG 输出,会同时进入 ASCENDCL 的 host 日志(plog)。拿到报错后建议配合以下手段缩小范围:
- 查看 host 侧运行日志:日志文件位置与查看方法参见 docs/zh/log_ref/README.md 及 docs/zh/log_ref/viewing_logs_ep.md,按模块(ASCENDCL)和时间过滤,可看到报错前后的接口调用上下文;
- 关注返回值联动:EH0009 往往伴随接口返回
ACL_ERROR_INVALID_PARAM(值为100000,见 include/external/acl/acl_base_rt.h)或返回空指针/无效句柄,排查时可同时校验返回值; - 对照示例工程复现:仓库 example 目录下提供了大量接口的标准用法示例,可在 example/2_advanced_features/tdt_channel 等目录中寻找对应场景,对比自己的调用方式找出差异。
小结
EH0009 是 ACL 层最典型的"参数值非法"错误码,其报错信息自带接口名、非法值、参数名与具体原因四个要素,信息完整、定位直接。结合本仓库的源码实现可以确认:EH0009 由各接口在校验失败时通过 AclErrorLogManager::ReportInputError 显式上报,模板注册于 src/dfx/error_manager/error_code.json,因此在任何接口文档中看到 EH0009,均可按照"先查参数范围、再查调用关系、必要时翻 host 日志"的路径完成排查。若排查后仍无法解决,可收集报错时的完整 host 日志,连同接口调用代码提交给技术支持进一步分析。