编码规范

本文档定义 Runtime 仓代码实现时应遵守的统一编码规范。

文档结构:

  • 一、通用 C/C++ 编码规范 适用于 Runtime 仓的所有 C/C++ 代码,包括源码和 UT 代码。
  • 二、Runtime 约束性规范 主要适用于 src/include/pkg_inc/cmake/ 及其他非测试代码;审查或编写 UT 代码时,只在确实涉及相关场景时参考。

一、通用 C/C++ 编码规范

规则 1:禁止硬编码敏感信息、使用禁用敏感词

  • 源码中禁止硬编码明文密码、密钥等高级别敏感数据。
  • 需要彻底清理历史遗留的废弃账号、密码和用途不明的敏感配置。
  • 用途不明、语义含糊的大数组或可读性差的隐藏数据定义,容易被质疑为后门实现,应避免。
  • 代码或者配置中硬编码公网 IP、域名、邮箱地址以及容易引起误解的敏感词时,必须能说明合理用途;尤其不能出现指向华为或中国的可疑公网地址。

规则 2:外部数据作为数组索引或者指针偏移时,必须确保访问范围合法

  • 数组索引、内存偏移、长度和容量若直接由外部控制,必须先做严格范围校验,防止越界访问。

规则 3:整数之间运算时必须严格检查,确保不会出现溢出、反转、除 0

  • 若整数运算结果会参与内存申请、偏移计算、循环边界或拷贝长度,必须评估溢出、反转、截断和除零风险。

规则 4:资源管理和生命周期必须清晰,获取与释放必须成对

  • 文件句柄、内存以及 device、driver、context、stream、event、notify 等句柄,都必须在生命周期结束后及时释放。
  • 异常分支和早返回路径不能遗漏清理动作。
  • 资源释放后应立即赋予明确新值,避免悬空指针和重复释放。
  • 内存、锁、句柄等资源的获取与释放必须成对出现;优先使用 RAII 管理资源生命周期。

规则 5:内存申请前必须判断大小,申请后必须校验是否成功

  • 申请大小可能来自外部输入,必须检查合法性,不能申请 0 长度内存。
  • 外置 allocator 需要同时校验返回的结构体指针以及结构体内部 data 指针是否有效。
  • 内存申请后应评估是否需要初始化,避免读取未初始化内存。

规则 6:外部输入、空指针、数组下标和循环边界必须先校验

  • 用户参数、driver 返回值、配置文件、环境变量等外部输入,进入关键逻辑前都必须完成合法性校验。
  • 解引用前必须确认指针有效。
  • 数组下标、长度和循环边界必须位于合法范围内。

规则 7:错误处理必须完整,禁止吞错

  • 调用链上的错误返回值必须被正确传播,不能静默吞掉错误。
  • 返回的错误码必须语义准确。
  • 错误路径上已经申请的资源必须同步释放。
  • 错误处理宏必须使用正确,避免出现“记录了日志但没有收口”或“直接返回但缺少上下文”的情况。

规则 8:缓冲区、内存操作和对象生命周期必须满足基础安全约束

  • 数组或缓冲区访问必须避免越界。
  • new/deletemalloc/freenew[]/delete[] 必须严格配对,禁止混用。
  • 使用 new(std::nothrow) 时必须判空;使用普通 new 时不要再做 nullptr 判空。
  • 禁止返回栈空间地址。
  • 释放对象前,应明确对象内部成员指针的处理策略,避免二次释放或悬空引用。

规则 9:并发场景必须显式保证线程安全

  • 多线程场景下的共享数据必须有明确保护策略,避免竞态和可见性问题。
  • 普通锁禁止随意嵌套,避免死锁风险。
  • 涉及线程上下文切换、异步任务或回调时,必须明确时序关系和资源有效期。

规则 10:不要编写依赖表达式求值顺序或隐式优先级的代码

  • 类似 func(a++, a) 的写法会依赖未指定的求值顺序,属于未定义行为,必须避免。
  • 复杂表达式应使用括号明确运算优先级,避免依赖默认优先顺序。

规则 11:代码风格和提交内容必须保持整洁一致

  • 变更代码必须符合仓库 .clang-format 配置。
  • 命名必须符合项目惯例:函数名使用大驼峰,私有方法允许尾部下划线;变量名使用小驼峰;常量和宏使用全大写下划线;类名使用大驼峰。
  • 新文件必须包含华为 CANN Open Software License 版权头。
  • include 顺序必须符合项目惯例。
  • 提交中不得残留调试代码、注释掉的代码、未使用的 include 或变量。
  • 新增文件权限必须合理,源码应为 644,可执行脚本应为 755。

二、Runtime 约束性规范

规则 12:对外开放接口的预留参数要有严格校验,防止后续版本启用时出现兼容性问题

  • 预留参数必须强制用户传入无效值,否则后续启用时容易引入兼容性问题。
  • 数值类预留参数的无效值一般为 0
  • 指针类预留参数的无效值一般为 nullptr

规则 13:对外接口中表达内存长度的返回值或参数,应优先使用 size_t

  • 涉及内存长度、缓冲区大小、字节数等语义时,应优先使用 size_t,避免 32 位长度表达不足的问题。

规则 14:修改对外开放接口时,必须严格确保 API 和 ABI 兼容

  • include/include/external/acl/ 和对外发布头文件边界上的修改,都必须严格评估 API/ABI 兼容性。
  • 对外头文件的公开接口中禁止使用 std::string 作为 ABI 边界类型,应使用仓内兼容替代方案。
  • 新增或修改结构体时要考虑保留字段和后向兼容。
  • 废弃接口应优先做兼容标记,而不是直接删除。

规则 15:禁止在公共路径中硬编码芯片、平台或驱动能力分支

  • 公共逻辑应优先通过能力查询、配置注册或平台隔离目录处理差异。
  • 确需区分平台的逻辑,应收敛在 config/、平台子目录或明确的能力适配层中。

规则 16:禁止在加载/执行热路径中随意增加 EVENT/TRACE 打印

  • 热路径海量日志会引入明显性能抖动和 IO 压力。
  • 加载/执行热路径中禁止新增默认开启的 EVENT/TRACE 级别日志。
  • 可以增加默认关闭的 DEBUG/INFO 日志,但需要评估开关策略和字符串开销。
  • ERROR 日志仅用于真实异常,不可用于正常分支。

规则 17:调用其他组件提供的、需要 Runtime 传入资源的接口时,必须提前确认资源生命周期约束

  • 设计和编码阶段都要明确跨组件接口是否持有传入指针、何时复用、何时释放。

规则 18:对执行顺序敏感的逻辑,禁止依赖无序容器遍历结果

  • 如果容器遍历顺序会影响任务、事件、通知、回调注册或资源初始化顺序,禁止直接依赖 std::unordered_map 等无序容器的遍历结果。
  • 需要稳定顺序时优先使用有序容器或显式排序。
  • 不要使用进程内地址值作为顺序依据。

规则 19:禁止单例模式在头文件中以内联方式实现

  • 头文件内联单例在多编译单元、动态库和 dlopen 场景下可能带来多实例、析构时序和崩溃问题。

规则 20:禁止新增未公开的对外接口边界

  • 新增对外接口时,只能放在明确的公开边界头文件中;不要通过内部头文件或内部符号对外泄露能力。

规则 21:禁止在静态对象或全局对象析构函数里做跨 SO 的函数调用

  • 跨动态库析构顺序不可预测,极易导致悬空引用、异常终止和崩溃。
  • 典型风险包括:依赖的 SO 已卸载;shared_ptr 析构触发虚析构函数,但实际类型所在 SO 已失效;程序退出阶段其他线程仍在访问静态对象。
  • 推荐做法是把清理工作放到显式的 Finalize() 或等价接口中,在运行期完成清理,析构阶段保持空操作或仅做本地对象回收。

规则 22:实现逻辑必须与设计和接口约束保持一致

  • 编码前应明确变更对应的设计意图、接口约束和场景预期。
  • 实现中不得遗漏分支、边界条件或异常路径。
  • 正常流程、异常流程和边界场景的处理方式必须保持一致。

规则 23:编写日志时必须保证级别、内容和热路径开销合理

  • RT_LOG_ERROR 仅用于不可恢复错误,RT_LOG_WARNING 用于功能降级或非致命异常,RT_LOG_INFO 用于关键操作节点,RT_LOG_DEBUG 用于调试信息。
  • 日志内容必须包含足够上下文,并避免打印敏感信息。
  • 格式化字符串必须与参数类型匹配。
  • 日志内容中不得硬编码函数名,不得出现英文拼写错误。
  • 热路径不得引入高频日志。
  • 项目内错误日志宏必须使用正确,例如 COND_RETURN_ERROR_MSG_INNERRecordErrorLog 等。

规则 24:修改公共接口时必须同步满足兼容性要求

  • 修改 include/ 目录下头文件时,必须按向后兼容原则设计变更。
  • 公共 API 函数签名不得发生 breaking change,除非该变更已被明确接受并完成迁移设计。
  • 接口废弃应通过标记完成,而不是直接删除。
  • 枚举定义不得随意引入 XX_MAX 或影响多版本匹配兼容性。
  • pkg_inc/runtime/runtime 不得误新增内容,确需新增时必须先确认是否应改为其它公开边界。

规则 25:修改公开接口和错误码时必须同步更新文档

  • 修改 include/external/acl/acl_rt.h 时,必须在同一次变更中同步更新对应 docs/ 文档。
  • 修改 include/external/acl/error_codes/rt_error_codes.h 时,必须在同一次变更中同步更新 docs/03_api_ref/25_数据类型及其操作接口.md

规则 26:实现时必须遵守既有软件架构和构建边界

  • src/runtime 中非 api 目录不应调用 api 目录的方法,例如 feature 目录不应访问 Api::Instance
  • 编写实现时必须保持既有目录边界、平台隔离边界和组件职责边界清晰。
  • src/runtime 目录仅允许使用 STATIC_RT_LIBRUNTIME_APICFG_DEV_PLATFORM_PC 及编译器内置宏;禁止新增其他编译宏。