- CANN
- Ascend
- 人工智能
- 任务调度
【免费下载链接】runtime
本项目提供CANN运行时组件和维测功能组件。
本篇文章以 CANN/runtime 仓库中 example/5_performance/log 目录下的 ACL 日志样例为骨架,完整讲解对外 ACL 日志接口(acllogRecord、acllogVaList、acllogCheckDebugLevel、acllogRegisterCallback、acllogUnregisterCallback)的声明含义、日志级别与掩码机制、回调注册流程,并结合 main.cpp 源码与 run.sh 构建脚本给出可直接复现的运行方案。读完本文,你将掌握如何在自有模块中接入 CANN 的 ACL 日志通道,将调试日志、运行日志定向输出,并注册自定义回调对设备侧日志进行实时旁路。
一、样例概览:log 目录的定位与组成
example/5_performance/log是 CANN Runtime 对外 ACL 日志接口的可运行样例目录。目录级 README.md 说明其职责是“提供对外 ACL 日志接口的可运行样例,并通过目录级脚本输出统一的成功或失败结果”。整个目录包含以下内容:
- 0_acl_log:覆盖
acllogRecord、acllogVaList、acllogCheckDebugLevel、acllogRegisterCallback和acllogUnregisterCallback五个接口的核心用法; - CMakeLists.txt:目录级 CMake 工程,负责收集 CANN 安装路径并下发子目录构建;
- run.sh:目录级统一脚本,完成环境解析、构建、运行与结果校验,返回值为 0 表示全部步骤成功,非 0 表示构建或运行失败;
- 0_acl_log/main.cpp:样例主体程序,演示调试日志、运行日志、
va_list日志记录以及设备日志回调的注册与注销。
该样例属于性能与可观测性(performance/log)示例家族,核心价值在于:用户模块无需关心日志平台内部实现,仅通过公开头文件 acl_log.h 声明的 5 个接口即可把日志写入 CANN 日志体系,同时可获取调试级别开关状态以做日志开销裁剪。
二、对外 ACL 日志接口全景:头文件声明逐项解读
所有对外 ACL 日志接口均声明在 include/dfx/base/acl_log.h 中,头文件通过LOG_FUNC_VISIBILITY宏(定义于 include/dfx/base/log_types.h,非 Windows 平台展开为__attribute__((visibility("default"))))将符号导出为默认可见,调用方仅需#include "acl_log.h"并链接libascendalog.so即可。
2.1 回调句柄与输出类型
typedef uintptr_t acllogCallbackHandle; typedef enum { OUTPUT_TYPE_DEBUG = 0, OUTPUT_TYPE_RUN = 1, OUTPUT_TYPE_BOTH = 2, OUTPUT_TYPE_MAX } acllogOutputLogType;acllogCallbackHandle以uintptr_t表示回调句柄,由注册接口回填,注销时原样传回;acllogOutputLogType决定回调接收哪类日志:OUTPUT_TYPE_DEBUG(仅调试日志)、OUTPUT_TYPE_RUN(仅运行日志)、OUTPUT_TYPE_BOTH(两类都接收)。样例中注册回调时传入OUTPUT_TYPE_BOTH,与日志接口按掩码区分调试/运行日志的机制对应。
2.2 五个核心接口签名
| 接口 | 签名 | 用途 |
|---|---|---|
acllogRegisterCallback | int32_t (acllogRecordCallback callbackFunc, void* userData, uint32_t outputLogType, acllogCallbackHandle* callbackHandle) | 注册设备日志记录回调,回调被触发时把日志内容转发给调用方 |
acllogUnregisterCallback | int32_t (acllogCallbackHandle callback) | 按句柄注销已注册的回调 |
acllogRecord | void (int32_t moduleId, int32_t level, const char* fmt, ...) | 以格式化字符串方式记录日志,声明带format(printf, 3, 4)属性,编译期可校验格式串 |
acllogVaList | void (int32_t moduleId, int32_t level, const char* fmt, va_list list) | 以va_list方式记录日志,适用于把日志接口二次封装进可变参数函数 |
acllogCheckDebugLevel | int32_t (int32_t moduleId, int32_t logLevel) | 查询指定模块在指定日志级别下调试日志是否开启,供上层做日志开销裁剪 |
回调函数类型定义如下:
typedef int32_t (*acllogRecordCallback)(void* userData, uint32_t outputLogType, const char* logContent, size_t length);回调返回int32_t状态码,入参包括:注册时传入的userData、日志类型outputLogType、日志内容指针logContent与内容长度length(注意length为字节数,且内容不保证以\0结尾,样例中使用%.*s限定长度打印,这是正确的处理姿势)。
从源码结构看,acllogRecord、acllogVaList、acllogCheckDebugLevel三个记录/查询接口均带__attribute((weak))弱符号声明(见 acl_log.h),意味着它们属于“可选能力”:即便链接的运行时版本未导出这些符号,程序仍可正常链接,仅需在运行期判空。样例 main.cpp 正是据此在调用前做了空指针检查。
三、日志级别、掩码与模块 ID:log_types.h 中的基础概念
五个接口的level、moduleId参数以及日志归类,都建立在 include/dfx/base/log_types.h 定义的公共常量之上。理解这些常量是正确使用 ACL 日志接口的前提。
3.1 日志级别
#define DLOG_DEBUG 0x0 // debug level id #define DLOG_INFO 0x1 // info level id #define DLOG_WARN 0x2 // warning level id #define DLOG_ERROR 0x3 // error level id #define DLOG_NULL 0x4 // don't print log级别取值DLOG_DEBUG(0) ~DLOG_ERROR(3),DLOG_NULL(4) 表示不打印。样例中分别以DLOG_INFO记录调试日志与运行日志、以DLOG_WARN记录va_list日志,并用acllogCheckDebugLevel(kUserModuleId, DLOG_INFO)查询该级别是否使能。
3.2 日志掩码:调试日志与运行日志的分类机制
#define DEBUG_LOG_MASK (0x00010000U) // print log to directory debug #define SECURITY_LOG_MASK (0x00100000U) // print log to directory security #define RUN_LOG_MASK (0x01000000U) // print log to directory run #define STDOUT_LOG_MASK (0x10000000U) // print log to stdout调试日志(写入 debug 目录)与运行日志(写入 run 目录)并不是通过level区分的,而是通过模块 ID 上叠加掩码。样例中:
acllogRecord(kUserModuleId, DLOG_INFO, "user debug log: %d\n", 1); acllogRecord(kUserModuleId | RUN_LOG_MASK, DLOG_INFO, "user run log: %d\n", 2);第一条使用裸kUserModuleId记录为调试日志,第二条使用kUserModuleId | RUN_LOG_MASK(叠加0x01000000U)记录为运行日志。两条日志级别同为DLOG_INFO,却因掩码不同而进入不同的日志归类通道。相应地,注册回调时的OUTPUT_TYPE_DEBUG/OUTPUT_TYPE_RUN/OUTPUT_TYPE_BOTH正是对这一分类的消费端筛选。
3.3 模块 ID:从系统模块到用户应用
log_types.h定义了完整的模块 ID 枚举(SLOG=0、RUNTIME=7、HCCL=3、GE=45、ASCENDCL=48 等,直至INVALID_MODULE_ID=77),其中APP = 33预留给用户应用。样例并未使用 33,而是自定kUserModuleId = 0xff00(见 main.cpp),以规避与任何系统模块冲突,体现了“用户模块 ID 自行约定、避开系统枚举”的实践方式。此外还有MSG_LENGTH 1024表示单条日志最大长度,DLOG_TYPE_DEBUG = 0、DLOG_TYPE_RUN = 1定义日志类型枚举,供日志落盘目录与回调类型换算使用。
四、样例源码逐段解析:一条完整日志链路的实现
0_acl_log/main.cpp 规模很小但流程完整,包含回调定义、va_list封装、注册、记录、注销五个环节。
4.1 自定义日志回调
int32_t LogCallback(void*, uint32_t outputLogType, const char* logContent, size_t length) { std::printf("[CALLBACK type=%u] %.*s", outputLogType, static_cast<int>(length), logContent); return 0; }回调把日志类型与内容(按length精确截取)打印到标准输出。这里outputLogType的取值即前文acllogOutputLogType枚举值,可据此在回调内继续分流处理。
4.2 va_list 二次封装
void RecordWithVaList(int32_t moduleId, int32_t level, const char* format, ...) { va_list args; va_start(args, format); acllogVaList(moduleId, level, format, args); va_end(args); }这是acllogVaList的典型使用场景:当你的业务代码需要对日志接口再做一层可变参数封装时,用va_start/va_end捕获参数列表后直接转交acllogVaList,避免重新解析格式化字符串。注意必须先va_start再调用,且成对使用va_end。
4.3 主流程:注册 → 记录 → 注销
int main() { acllogCallbackHandle callbackHandle = 0; const int32_t registerResult = acllogRegisterCallback(LogCallback, nullptr, OUTPUT_TYPE_BOTH, &callbackHandle); if (registerResult != 0) { std::printf("[FAILURE] acllogRegisterCallback returned %d.\n", registerResult); return 1; } ... }主流程先以OUTPUT_TYPE_BOTH注册回调(userData传nullptr,回调中未使用),注册失败立即以非 0 退出。随后在空指针保护下依次执行三条日志记录与一次级别查询;最后注销回调:
const int32_t unregisterResult = acllogUnregisterCallback(callbackHandle); if (unregisterResult != 0) { ... return 1; } std::printf("[SUCCESS] ACL log sample completed successfully.\n");注销使用注册时回填的callbackHandle。整个样例的运行期行为可归纳为:
- 注册回调(输出类型为 BOTH)→ 打印注册结果;
- 记录一条调试日志(
DLOG_INFO+ 裸模块 ID)→ 触发回调,类型为 DEBUG; - 记录一条运行日志(
DLOG_INFO+RUN_LOG_MASK)→ 触发回调,类型为 RUN; - 经
RecordWithVaList以va_list记录一条DLOG_WARN日志; - 查询
acllogCheckDebugLevel返回的使能状态并打印; - 注销回调 → 打印成功标记。
需要留意的是,第 2~5 步整体处于acllogRecord != nullptr && acllogVaList != nullptr && acllogCheckDebugLevel != nullptr的保护之下。若链接的运行时未导出这三个弱符号,程序会打印警告并仅验证回调注册/注销能力后正常结束,这正是弱符号设计带来的向前兼容性。
五、编译与运行:从环境解析到结果校验
5.1 运行前置条件
样例面向已安装 CANN 软件包的环境,0_acl_log/README.md 给出的运行方式为在 CANN 环境中执行上级目录的run.sh:
source ${install_root}/cann/set_env.sh cd ${git_clone_path}/example/5_performance/log bash run.sh其中${install_root}为 CANN 安装根目录,${git_clone_path}为本仓库克隆路径。脚本会检查构建和样例输出,失败时返回非 0 退出码。
5.2 run.sh 的执行链路
run.sh 采用set -euo pipefail严格模式,执行链路如下:
- 定位脚本自身目录并
source ../../common/resolve_cann_env.sh; - 调用
resolve_cann_env解析 CANN 安装路径(详见 5.3); - 清理旧
build目录后执行cmake -B build -DASCEND_CANN_PACKAGE_PATH="${ASCEND_INSTALL_PATH}"配置工程; - 执行
cmake --build build -j"$(nproc)"并行构建; - 运行
./build/0_acl_log/acl_log_sample并将输出同时落盘到output_msg.txt; - 用
grep在输出中查找成功标记[SUCCESS] ACL log sample completed successfully.,命中则打印成功并返回 0,否则打印失败并返回 1。
即目录级 README 中“脚本返回值为 0 表示全部步骤成功,非 0 表示构建或运行失败;详细输出保存在output_msg.txt”的约定,其实现就是对样例输出文本的成功标记匹配。读者可自行把grep的成功标记替换为自定义标记以复用该骨架。
5.3 CANN 环境自动解析
example/common/resolve_cann_env.sh 是样例家族共享的环境解析脚本,其候选路径按优先级依次为:环境变量ASCEND_INSTALL_PATH、ASCEND_HOME_PATH、${HOME}/Ascend/cann、${HOME}/Ascend/ascend-toolkit/latest、/usr/local/Ascend/cann、/usr/local/Ascend/ascend-toolkit/latest、/opt/Ascend/cann。对每个候选路径,脚本要求同时存在set_env.sh(或bin/setenv.bash)且具备 ACL 标准布局(include/acl/acl.h与对应lib64/libacl_rt.so同时存在),满足条件后导出ASCEND_INSTALL_PATH与ASCEND_HOME_PATH并source环境脚本。若全部路径均不可用,脚本打印已检查路径列表并提示手动source <cann_path>/set_env.sh或export ASCEND_INSTALL_PATH=<cann_path>。
5.4 CMake 构建细节
目录级 CMakeLists.txt 强制要求传入ASCEND_CANN_PACKAGE_PATH,未设置时FATAL_ERROR终止。子工程 0_acl_log/CMakeLists.txt 揭示了本样例的依赖关系:
add_executable(acl_log_sample main.cpp) target_include_directories(acl_log_sample PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/../../../../include/dfx/base" "${ASCEND_CANN_PACKAGE_PATH}/include/base") target_link_libraries(acl_log_sample PRIVATE "${ASCEND_CANN_PACKAGE_PATH}/lib64/libascendalog.so")- 头文件搜索路径覆盖仓库内 include/dfx/base(提供
acl_log.h、log_types.h)与 CANN 安装包的include/base; - 链接的库为 CANN 安装包
lib64目录下的libascendalog.so,即 ACL 日志功能库; - 最终生成可执行文件
acl_log_sample。
这一依赖关系印证了接口分层:对外头文件随仓库开源,实现库由 CANN 软件包提供,用户程序通过标准链接方式接入。
六、使用建议与注意事项
- 日志级别与掩码配合使用:调试/运行日志的分类由模块 ID 叠加
DEBUG_LOG_MASK/RUN_LOG_MASK决定,与DLOG_*级别正交。想让同一条日志进 run 目录,务必按样例写法叠加RUN_LOG_MASK(0x01000000U)。 - 回调按长度消费日志内容:
logContent不保证\0结尾,务必按回调传入的length处理,样例的%.*s是安全范式。 - 弱符号判空:
acllogRecord/acllogVaList/acllogCheckDebugLevel为弱符号,调用前判空可保证旧版本运行时上的兼容降级。 - 用户模块 ID 自定并避让系统枚举:系统模块 ID 从
SLOG(0) 枚举到INVALID_MODULE_ID(77),用户模块建议像样例一样选用高位自定义值(如0xff00),或使用保留给用户应用的APP(33)。 - 日志级别查询用于开销裁剪:在日志高频路径上先
acllogCheckDebugLevel再决定是否拼装格式化参数,可避免无谓的字符串格式化开销。 - 运行失败排查:
run.sh全部输出保存在output_msg.txt,若返回非 0,先查看该文件确认是构建失败、样例运行失败还是成功标记未命中;环境相关报错可参考resolve_cann_env.sh打印的“已检查路径列表”核对 CANN 安装位置与ASCEND_INSTALL_PATH。
七、延伸阅读
- 接口头文件:include/dfx/base/acl_log.h、include/dfx/base/log_types.h
- 样例源码:example/5_performance/log/0_acl_log/main.cpp
- 构建与运行脚本:example/5_performance/log/run.sh、example/common/resolve_cann_env.sh
- 日志相关更多文档:docs/zh/log_ref(含日志总览、级别设置、日志查看等),以及环境变量 ASCEND_GLOBAL_LOG_LEVEL 与 ASCEND_MODULE_LOG_LEVEL 对日志级别控制的说明
- CANN
- Ascend
- 人工智能
- 任务调度
【免费下载链接】runtime
本项目提供CANN运行时组件和维测功能组件。
相关推荐
CANN Runtime ACL 日志接口实战:使用 acl_log.h 记录日志与注册设备日志回调
CANN Runtime ACL 日志接口实战:使用 acl_log.h 记录日志与注册设备日志回调 CANN(Compute Architecture for
CANNAscend人工智能任务调度9大网盘直链下载助手:告别龟速下载的智能解决方案
9大网盘直链下载助手:告别龟速下载的智能解决方案 还在为网盘下载速度慢、操作繁琐而烦恼吗?LinkSwift网盘直链下载助手为您提供了一站式解决方案。这款基于J
CANNAscend人工智能任务调度CANN Runtime ACL 日志接口实战:在应用程序中使用 acl_log.h 记录与回调设备日志
CANN Runtime ACL 日志接口实战:在应用程序中使用 acl_log.h 记录与回调设备日志 本技术指南围绕 CANN/runtime 开源仓库中的
CANNAscend人工智能任务调度
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考