CANN Runtime RTS 错误码 EE1020(Invalid Argument)深度解析与排查指南
【免费下载链接】runtime本项目提供CANN运行时组件和维测功能组件。项目地址: https://gitcode.com/cann/runtime
EE1020 是 CANN Runtime 的 RTS(Runtime Service)错误码之一,专门用于报告 Runtime 内部调用标准库安全函数(如memcpy_s、memset_s)失败时产生的参数/执行异常。本文以 docs/en/error_code_ref/RTS-Errors/EE1020-Invalid_Argument.md 为主线,结合仓库源码中的错误码元数据、日志输出链路、各 API 触发点与单元测试,完整拆解 EE1020 的消息格式、触发场景与排查方法,帮助你遇到该错误时能快速定位到具体的函数调用与参数问题。
一、错误码概览
- 错误码:EE1020
- 错误类型:Invalid Argument(参数无效)
- 错误分类:RTS Errors(Runtime 服务错误)
- 错误级别:Error(日志级别
DLOG_ERROR,见 error_code_meta.h) - 适用模块:CANN Runtime 的 Host 侧 API 层与核心任务构建层
- 本质:Runtime 内部调用 C 标准库安全函数(
memcpy_s、memset_s等)时,函数返回值不等于EOK(成功),说明发生了缓冲区参数不合法或内存操作失败。这类失败通常意味着传入 API 的缓冲区地址、长度或指针参数存在问题。
EE1020 与常见的参数校验错误码(如 EE1003、EE1004)不同:EE1003/EE1004 是 Runtime 对用户传入 API 的参数直接做合法性检查时抛出的;而 EE1020 是参数已通过外层校验、但在进入标准安全函数后被底层拒绝时抛出的,因此其定位线索藏在消息的“标准函数名 + errno + 扩展信息”三要素中。
二、错误消息格式与占位符语义
EE1020 的标准报错格式如下:
%s failed. Reason: Standard function %s failed. [Errno %s] %s. %s依次出现的占位符%s含义如下:
| 占位符 | 参数名 | 含义 |
|---|---|---|
第 1 个%s | func1 | 失败的 Runtime 接口名称(外层 API,如rtGetSocVersion) |
第 2 个%s | func2 | 失败的标准函数名称(内层,如memcpy_s) |
第 3 个%s | ret_code | 标准函数返回的错误码(errno 数值) |
第 4 个%s | reason | 错误原因(通常由strerror输出的错误码文本描述) |
第 5 个%s | extend_info | 扩展信息(打印涉及的内存地址、缓冲区长度等关键现场数据) |
需要说明的是,该格式在源码元数据中注册的完整模板为:
%s failed. Reason: Standard function %s failed. [Errno %s] %s. %s ErrorCode=EE1020.即实际打印的日志末尾会追加ErrorCode=EE1020.字样,便于在日志中直接检索定位(参见 error_code_meta.h 与 error_code.json 中的ErrMessage字段)。
三、典型报错示例逐字段解读
原文档给出了一个典型报错:
rtGetStreamId failed. Reason: Standard function memcpy_s failed. [Errno 1] Operation not permitted. src=socName, dest=3257281401236631602, dest_max=1223, count=1222.逐字段拆解:
| 片段 | 对应参数 | 解读 |
|---|---|---|
rtGetStreamId failed | func1 | 外层失败接口为rtGetStreamId(获取 Stream ID) |
Standard function memcpy_s failed | func2 | 内层失败函数为内存拷贝安全函数memcpy_s |
[Errno 1] | ret_code | memcpy_s返回错误码 1 |
Operation not permitted | reason | 错误码 1 对应的文本描述(errno 1 =EPERM) |
src=socName, dest=... | extend_info | 扩展现场信息:源、目标地址、目标缓冲区最大长度dest_max、拷贝字节数count |
结合源码可以还原这一报错的完整现场:
rtGetStreamId是获取 Stream 唯一 ID 的接口,位于 api_c.cc;- 接口内部通过
memcpy_s(dest, destMax, src, count)将字符串(如 SOC 版本名socName)拷入用户缓冲区; - 当
memcpy_s返回值不为EOK时,代码构造std::stringstream扩展信息,记录dest、src、destMax、count等十六进制/十进制现场值,并通过日志宏打印 EE1020 消息后返回失败错误码。
扩展信息中的字段是定位的核心:
dest/dest=0x...:目标缓冲区地址;src/src=0x...:源数据地址;dest_max/destMax/maxLen:目标缓冲区最大容量;count/length/paraSize:实际拷贝字节数。
当count > dest_max或目标缓冲区为无效地址时,memcpy_s将返回非零错误码并触发 EE1020。上例中dest_max=1223, count=1222恰好说明目标缓冲区容量仅比拷贝长度大 1 字节——这正是典型的目标缓冲区空间不足(或长度边界计算错误)导致memcpy_s失败的情形。
四、源码级实现原理:EE1020 从定义到打印的完整链路
4.1 错误码元数据定义
EE1020 在 error_code_meta.h 中通过 X-Macro 表注册,一行即可完成参数名列表、消息模板与日志级别的声明:
X(EE1020, "EE1020", ("func1", "func2", "ret_code", "reason", "extend_info"), "%s failed. Reason: Standard function %s failed. " "[Errno %s] %s. %s ErrorCode=EE1020.\n", DLOG_ERROR)- 参数名列表
("func1", "func2", "ret_code", "reason", "extend_info")与文档中的占位符一一对应; - 消息模板带
\n结尾,保证每条错误日志独立成行; - 日志级别为
DLOG_ERROR,属于错误级日志。
同一表中还并列定义了 EE1001~EE1024、EE2002、EE4002、EZ2001、WE0001 等错误码,EE1020 专门预留给了“标准函数调用失败”这一场景。此外,error_code.json 中同步维护了对外发布版本的错误码描述(errTitle为Invalid_Argument,Arglist为func1, func2, ret_code, reason, extend_info),供错误码查询工具与文档生成使用。
4.2 枚举与打印函数
EE1020 同时被注册进ErrorCode枚举(rt_log.h),并配套两个核心函数:
std::vector<std::string> GetParamNames(ErrorCode code); void PrintErrMsgToLog(ErrorCode errCode, const char* file, const int32_t line, const char* func, const std::vector<std::string>& values);GetParamNames根据错误码返回参数名列表,用于格式化时的参数校验(参数数量必须与Arglist一致);PrintErrMsgToLog将values按模板顺序填充后打印到日志;ProcessErrorCodeImpl负责错误码处理与错误码的最终落盘。
各 API 中实际使用的是封装宏RT_LOG_OUTER_MSG_IMPL(ErrorCode::EE1020, func1, func2, retCode, reason, extendInfo),它把调用点函数名、标准函数名、错误码数值、strerror文本与扩展信息统一组装后走PrintErrMsgToLog输出,并通过GetRtExtErrCodeAndSetGlobalErr将错误映射为对外返回码(如RT_ERROR_SEC_HANDLE、RT_ERROR_INVALID_VALUE)。
五、EE1020 的主要触发场景(源码实测)
通过检索源码中所有ErrorCode::EE1020的使用点,可以归纳出以下触发场景:
5.1 查询类接口中的字符串拷贝失败
api_c_soc.cc 的rtGetSocVersion:将 SOC 版本名(或UNKNOWN_SOC_TYPE)通过memcpy_s拷入用户缓冲区ver。当用户传入的maxLen不足以容纳版本字符串 + 终止符(socName.length() + 1U)时,memcpy_s失败并打印 EE1020,返回RT_ERROR_INSTANCE_VERSION或RT_ERROR_SEC_HANDLE。文档示例中rtGetStreamId场景与此同属“字符串/结构体拷入用户缓冲区”类。
5.2 Kernel 启动参数配置类接口
- api_c.cc 的
rtConfigureCall:当smDesc非空时,将调度描述符拷入线程局部的LaunchArgment,失败即报 EE1020; - api_c_kernel.cc 的
rtsKernelArgsParaUpdate:把内核参数para拷入参数句柄缓冲区(memcpy_s(offset, paraSize, para, paraSize)),失败时扩展信息打印dest=0x..., para=0x..., maxLen=..., paraSize=...; - api_impl_kernel_args.cc:向内核参数句柄追加占位符参数时,
memcpy_s失败会以"Adding placeholder parameters to the kernel parameter handle"作为func1报 EE1020; - api_impl.cc:
rtSetupArgument设置内核启动参数时,对偏移与大小校验后执行memcpy_s,失败报 EE1020。
5.3 Profiling 与维测接口
- api_c.cc 的
rtsProfTrace:将rtProfTraceUserData从用户内存拷入栈上局部变量(memcpy_s(&data, sizeof(data), userdata, length)),若传入的length超过结构体大小或为非法地址即触发; - api_impl_david.cc:获取内存 UCE(Uncorrectable Error)信息时,
memcpy_s失败以"Getting memory uce info"为func1报 EE1020。
5.4 核心任务构建层
除 API 层外,任务/命令构建路径中也有 EE1020 的使用点,例如 cmo_task.cc、memory_memcpy_async_task.cc、task_to_sqe.cc、arg_manage_ub.cc。从这些文件的使用位置可以推断:在向任务队列(SQE)或 UB 参数区填充数据时,若缓冲区长度校验失败,同样通过 EE1020 上报——这印证了该错误码“贯穿 Runtime 所有内存拷贝关键路径”的定位。
5.5 单元测试验证
tests/ut/runtime/runtime/test/rt_error_code_test.cc 对 EE1020 做了系统验证:
- 使用 5 个参数调用
PrintErrMsgToLog(ErrorCode::EE1020, ...),验证参数数量与格式模板匹配; - 构造
rtGetSocVersion场景:ErrorCode::EE1020, "rtGetSocVersion", "memcpy_s", "1", "count is greater than dest_max",验证memcpy_s返回错误码 1(count超过dest_max)时消息拼接正确; - 通过
GetParamNames(ErrorCode::EE1020)校验参数名列表为 5 项; - 校验参数数量表:
{ErrorCode::EE1020, 5},确保错误码与期望参数个数绑定,参数个数不匹配时会触发断言。
这说明 EE1020 的消息格式、参数数量与打印行为均有自动化测试兜底,任何一处改动都会在 UT 阶段暴露。
六、排查与解决方法
EE1020 的官方解决建议是“按照报错提示定位问题”。结合源码,建议按以下步骤排查:
- 记录完整报错:务必保留日志中
[Errno N]、错误原因文本与扩展信息(src、dest、dest_max、count)三个部分,缺一不可; - 核对 errno 与原因文本:
[Errno 1] Operation not permitted:安全函数返回EPERM,多为内存区域访问权限或缓冲区归属异常;[Errno 22] Invalid argument等:检查传入的目标缓冲区是否为合法地址、dest_max是否为真实可用容量;- 若原因文本提示
count is greater than dest_max,说明拷贝长度超过目标缓冲区容量,优先检查长度参数计算是否包含字符串终止符\0(源码中字符串场景统一为length + 1U)。
- 对照
func1定位接口:func1是外层 API 名,结合上文的触发场景表,可迅速判断是查询类(rtGetSocVersion)、内核参数类(rtConfigureCall、rtsKernelArgsParaUpdate)还是 Profiling 类(rtsProfTrace)接口; - 检查调用方缓冲区管理:多数 EE1020 源于调用方提供的缓冲区过小或指针失效。对照
dest_max与count,调整缓冲区分配策略或修正长度参数后重试; - 检索官方文档:可参考本仓库 RTS-Errors.md 错误码目录及中文版 EE1020-Invalid_Argument.md 交叉核对;
- 保留现场日志:若问题无法通过参数修正解决,保留带
ErrorCode=EE1020的完整日志(含时间戳、上下文与扩展信息)提交给 Runtime 团队分析——扩展信息中的十六进制地址与长度是定位内存问题的关键证据。
七、与其他 RTS 参数类错误码的区分
EE1020 属于“参数无效”大类,但触发时机特殊。使用时应与同类错误码区分:
| 错误码 | 触发时机 | 典型场景 |
|---|---|---|
| EE1003 | 参数取值不合法,消息中带“期望值” | 值域越界(如长度不在[0, 255]) |
| EE1004 | 参数为空指针 | 指针类参数为 NULL |
| EE1017 | 参数不合法,带原因描述 | 参数关系不满足(如paraSize不相等) |
| EE1020 | 外层校验通过后,标准安全函数(memcpy_s等)执行失败 | 缓冲区容量不足、内存拷贝失败 |
| EE1022 | 多个参数取值不合法 | 多参数同时越界 |
一句话记忆:EE1003/EE1004 是“还没开始拷就发现参数不对”,EE1020 是“参数看起来没问题、但拷的时候被安全函数拒绝”。因此 EE1020 的报错信息里永远会带标准函数名与 errno,这是它最鲜明的特征。
八、总结
EE1020 是 CANN Runtime 报告“内部标准安全函数调用失败”的统一错误码,其消息中完整保留了外层 API、内层标准函数、errno、原因文本与内存现场五要素,排查价值极高。通过 error_code_meta.h 的元数据定义、rt_log.h 的打印链路、api_c_soc.cc 等触发点以及 rt_error_code_test.cc 的测试覆盖,开发者可以快速建立“报错 → 函数 → 缓冲区 → 长度”的定位路径。遇到该错误时,优先核对dest_max与count的关系以及缓冲区归属,绝大多数场景均可通过修正缓冲区容量或长度参数解决。
【免费下载链接】runtime本项目提供CANN运行时组件和维测功能组件。项目地址: https://gitcode.com/cann/runtime
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考