Skip to content

编译过程调试

本文是扩展内容,介绍算子工程编译过程中的调试方法。算子工程编译涉及代码生成、Host侧编译、核函数(Kernel)侧编译、打包部署四个阶段,默认输出通常不足以定位根因,下文按阶段介绍调试手段,并附常见错误的诊断方法。

生成代码调试

npu_op_code_gen在cmake configure阶段自动生成aclnn调用代码和算子原型定义代码。生成产物位于ASCEND_AUTOGEN_PATH目录,默认是<CMAKE_BINARY_DIR>/autogen;按本文示例使用build_out时,对应目录为build_out/autogen。当原型定义有误或生成结果与预期不符时,需要检查这些文件。

生成产物目录

以AddCustom工程为例,配置完成后build_out/autogen/下会生成aclnn调用代码、算子原型定义代码和若干编译配置文件。下方仅列出排查代码生成问题时常用的关键产物;多算子工程会为每个算子生成对应的aclnn_<op>.cppaclnn_<op>.h

Text
build_out/autogen/
├── aclnn_add_custom.cpp      // aclnn调用代码实现
├── aclnn_add_custom.h        // aclnn调用代码头文件
├── op_proto.cc               // 算子原型定义代码(入图用)
├── op_proto.h                // 算子原型定义头文件
├── aic-<soc>-ops-info.ini    // 指定芯片型号的算子信息
├── custom_compile_options.ini
├── custom_opc_options.ini
└── ...

常见问题排查场景

问题查看什么
aclnn接口参数与原型定义不匹配检查aclnn_<op>.h中的函数签名,确认输入输出和属性参数是否正确映射。
入图场景算子注册信息异常检查op_proto.cc中的注册宏,确认OpType、dtype/format是否与Host侧定义一致。
某个算子的调用代码未生成检查npu_op_code_genSRC参数是否包含了该算子的Host侧源文件。
JOIN_OP_DEF相关芯片型号异常JOIN_OP_DEF=True时算子构建阶段不再显式传入ASCEND_SOC_SERIESASCEND_COMPUTE_UNIT,需结合configure日志、op_proto.cc中的AddConfig和CMakePresets.json中的芯片型号确认目标型号是否被覆盖。

强制重新生成

修改文件后,如果怀疑历史产物残留导致问题,可以手动清理后重新configure:

Bash
rm -rf build_out/autogen
cmake -S . -B build_out --preset=default

Host侧编译调试

Host侧构建时,默认只输出编译目标的摘要信息,不显示实际执行的编译和链接命令。通过VERBOSE机制可以打印完整的命令行,用于排查头文件路径、链接库、编译选项等问题。

方式一:构建时开启verbose

在构建命令中指定verbose,打印每条编译和链接命令:

Bash
cmake --build build_out --target binary package --verbose -j$(nproc)

输出示例:

Text
[ 30%] Building CXX object op_host/CMakeFiles/cust_opapi.dir/__/autogen/aclnn_add_custom.cpp.o
cd /data/asc-devkit/AddCustom/build_out/op_host && /usr/bin/c++ -D_FORTIFY_SOURCE=2 -D_GLIBCXX_USE_CXX11_ABI=0 -I/usr/local/Ascend/cann-9.1.0/include -fvisibility=hidden -fPIC -fvisibility-inlines-hidden -O2 -fstack-protector-strong -MD -MT op_host/CMakeFiles/cust_opapi.dir/__/autogen/aclnn_add_custom.cpp.o -MF CMakeFiles/cust_opapi.dir/__/autogen/aclnn_add_custom.cpp.o.d -o CMakeFiles/cust_opapi.dir/__/autogen/aclnn_add_custom.cpp.o -c /data/asc-devkit/AddCustom/build_out/autogen/aclnn_add_custom.cpp

每行输出包含完整的编译器调用,可以看到:

  • 实际的编译器路径(如/usr/bin/c++)。
  • 宏定义(-D)和头文件搜索路径(-I)。
  • 编译选项(如-fvisibility=hidden-g-O2)。
  • 链接库路径(-L)和链接库(-l)。

方式二:cmake配置CMAKE_VERBOSE_MAKEFILE

在cmake configure阶段开启verbose,后续所有make调用都会自动打印完整命令:

Bash
cmake -S . -B build_out --preset=default -DCMAKE_VERBOSE_MAKEFILE=ON
cmake --build build_out --target binary package -j$(nproc)

与方式一效果相同,区别在于设置持久化在CMakeCache.txt中,无需每次构建时重复指定。

常见问题排查场景

问题查看什么
头文件找不到-I路径是否包含目标头文件所在目录。
链接undefined reference-L-l是否包含缺失符号所在的库。

核函数(Kernel)侧编译调试

核函数(Kernel)侧由编译框架为每个算子调用毕昇编译器,默认不保留中间产物。通过--save-temp-files选项可以保留每次编译的中间文件,用于排查预处理、编译、链接各阶段的问题。

开启方法

在核函数(Kernel)侧CMakeLists.txt中,通过npu_op_kernel_options添加--save-temp-files选项:

CMake
# 为所有算子开启
npu_op_kernel_options(ascendc_kernels ALL OPTIONS --save-temp-files)

# 仅对特定算子开启
npu_op_kernel_options(ascendc_kernels AddCustom OPTIONS --save-temp-files)

也可以同时添加-g生成调试信息,配合使用:

CMake
npu_op_kernel_options(ascendc_kernels ALL OPTIONS --save-temp-files -g)

保留的中间产物

开启后,编译框架会在构建目录下保留每个核函数(Kernel)编译过程的中间文件。例如本文在ascendxxyy上验证的AddCustom工程中,临时文件位于:

Text
build_out/op_kernel/AddCustom_ascendxxyy/kernel_*/kernel_meta_<kernel_name>/kernel_meta/

实际目录名会随OpType、芯片型号和核函数(Kernel)名称变化。常见文件包括:

  • 预处理文件.i):预处理后的源码,可用于确认宏展开是否正确、头文件是否被正确包含。
  • 目标文件.o):链接前的目标文件,可用于确认符号是否正确导出。
  • 编译日志.log):单个核函数(Kernel)的编译日志,可用于查看编译器调用和诊断信息。
  • 生成源码*_kernel.cpp*_tiling_data.h等):编译框架生成的核函数(Kernel)源码和Tiling数据结构文件。

常见问题排查场景

问题查看什么
宏定义未生效检查.i文件,确认宏是否被正确展开。
头文件包含缺失检查.i文件,确认目标头文件的内容是否被引入。
链接符号缺失检查.o文件的符号表(nm命令),确认目标符号是否存在。

部分芯片编译失败处理

ASCEND_SOC_SERIESASCEND_COMPUTE_UNIT配置了多个芯片型号时,核函数(Kernel)编译会对每个芯片独立执行一轮。如果某个芯片的核函数(Kernel)编译失败,默认行为是终止整个构建流程。

ASCEND_SKIP_FAILED_COMPUTE_UNIT选项允许跳过单个芯片的编译失败,继续编译其余芯片。适用场景:

  • 算子对某些芯片型号尚不完全适配,但其他型号已可正常使用。
  • npu_op_code_genJOIN_OP_DEF参数设为False时,用户通过compute_unit自行设置芯片型号,算子对其中部分型号缺少适配。

配置方法:在CMakeLists.txt中设置:

CMake
set(ASCEND_SKIP_FAILED_COMPUTE_UNIT TRUE)

或在cmake命令行中传入:

Bash
cmake -S . -B build_out --preset=default -DASCEND_SKIP_FAILED_COMPUTE_UNIT=TRUE

开启该选项后,部分AI处理器型号对应的算子编译失败时,编译流程可跳过失败项并继续编译其他型号;关闭时,遇到失败会影响其他型号的编译流程。若需要定位被跳过的失败项,建议同时配置ASCENDC_BUILD_LOG_DIR保存编译日志。

打包与部署调试

打包阶段将编译产物组织为.run安装包,部署阶段将算子包安装到目标环境。打包或安装出问题时,检查中间产物和打包命令即可定位。

查看打包命令

与Host侧调试一样,使用VERBOSE机制可以看到实际的打包命令:

Bash
cmake --build build_out --target package --verbose -j$(nproc)

输出示例:

Text
Run CPack packaging tool...
/opt/buildtools/cmake-3.20.5-linux-aarch64/bin/cpack --config ./CPackConfig.cmake

可以看到cpack调用过程,用于排查打包配置问题。

检查编译产物目录

在打包或安装前,先检查编译产物目录,确认单算子API头文件、动态库、核函数(Kernel)二进制和Tiling库等是否已生成。不同编译方式下目录结构会有差异,下方仅列出排查时常用的关键路径。

TYPE RUN模式下,原始构建目录中主要检查中间产物:

Text
build_out/
├── autogen/
│   ├── aclnn_add_custom.h
│   └── ...
├── op_host/
│   ├── libcust_opapi.so
│   ├── libcust_opmaster_rt2.0.so
│   ├── libcust_opsproto_rt2.0.so
│   └── ...
├── op_kernel/ascendc_kernels/binary/
│   ├── ascendxxyy/
│   │   └── add_custom/
│   ├── config/ascendxxyy/
│   ├── dynamic/
│   └── ...
└── ...

执行package目标后,CPack staging目录中会出现接近安装后形态的vendors/<vendor_name>/目录,可重点检查op_api/op_impl/op_proto/

Text
build_out/_CPack_Packages/Linux/External/custom_opp_*.run/packages/vendors/<vendor_name>/
├── op_api/
│   ├── include/aclnn_<op>.h
│   └── lib/libcust_opapi.so
├── op_impl/
│   ├── ai_core/tbe/
│   └── ...
├── op_proto/
│   ├── inc/op_proto.h
│   └── lib/linux/aarch64/libcust_opsproto_rt2.0.so
└── ...

TYPE SHAREDTYPE STATIC安装模式下,产物通常位于${CMAKE_INSTALL_PREFIX}/include${CMAKE_INSTALL_PREFIX}/lib

检查要点

  • build_out/autogen/或staging目录的op_api/include/下是否生成aclnn_<op>.h
  • build_out/op_host/或staging目录的op_api/lib/下是否生成libcust_opapi.so
  • staging目录或安装目录的op_impl下是否包含目标芯片型号的核函数(Kernel)二进制和Tiling动态库。
  • 如果使用SHARED/STATIC模式,ENABLE_BINARY_PACKAGE必须为True。

安装后验证内容

安装.run包后,可在安装目录下检查部署结果。默认安装场景检查:

Bash
ls ${INSTALL_DIR}/opp/vendors/<vendor_name>/op_api
ls ${INSTALL_DIR}/opp/vendors/<vendor_name>/op_impl
ls ${INSTALL_DIR}/opp/vendors/<vendor_name>/op_proto # 可选

指定目录安装场景检查:

Bash
ls <path>/vendors/<vendor_name>/op_api
ls <path>/vendors/<vendor_name>/op_impl
ls <path>/vendors/<vendor_name>/op_proto # 可选

常见问题排查场景

问题查看什么
package目标失败cmake --build build_out --target package --verbose输出中cpack的命令和报错信息。
SHARED/STATIC模式下构建或安装报错npu_op_package CONFIG中ENABLE_BINARY_PACKAGE是否为True。
INSTALL_PATH生成产物位置不对npu_op_package CONFIG中INSTALL_PATH配置是否指向预期目录。
安装后算子未被识别指定目录安装时是否执行了source <path>/vendors/<vendor_name>/bin/set_env.bash,默认安装时opp/vendors/config.ini中优先级配置是否正确。

编译日志存盘

默认情况下,核函数(Kernel)侧编译过程中的中间日志不会保留到磁盘,编译失败时终端仅打印异常抛出信息。通过设置ASCENDC_BUILD_LOG_DIR环境变量,可将每个编译阶段的日志写入指定目录,便于事后回溯。

开启方法:在执行cmake或build.sh之前设置环境变量:

Bash
export ASCENDC_BUILD_LOG_DIR=/home/build_log/

编译框架会在其下按芯片型号创建子目录。例如编译ascendxxyyascendxxyy_2两个芯片时,日志目录结构为:

Text
/home/build_log/
├── ascendxxyy/
└── ascendxxyy_2/

日志命名规则:开启日志存盘后,编译框架会根据编译结果调整日志文件后缀:

  • 编译成功:对应日志文件后缀会添加_success
  • 编译失败:终端会打印错误信息和对应日志文件路径,日志文件后缀会添加_error

从日志定位错误

  1. 在终端报错中查看失败日志的具体路径,或在ASCENDC_BUILD_LOG_DIR对应芯片子目录下搜索带_error后缀的日志文件。
  2. 打开失败日志,查看毕昇编译器输出的错误信息。
  3. 根据错误中的文件名、行号或API提示回到源码修正。

说明

开启ASCEND_SKIP_FAILED_COMPUTE_UNIT时,建议同时开启ASCENDC_BUILD_LOG_DIR,两者配合使用可以完整保留每个失败核函数(Kernel)的详细编译日志,便于逐个修复。

免责声明:本站内容由 asc-devkit 仓 master 分支自动编译生成,属于持续开发版本,可能存在缺陷,仅供预览与参考。如需稳定及商用资料,请查阅官方 昇腾社区