| 文件 | 最后提交记录 | 最后更新时间 |
|---|---|---|
feat: log支持将日志级别下发给drv以采集URMA日志 (#849) Co-authored-by: GuoWenbo<guowenbo13@h-partners.com> # message auto-generated for no-merge-commit merge: !4515 merge feature/issue849-log-collect-urma-log into master feat: log支持将日志级别下发给drv以采集URMA日志 (#849) Created-by: GuoWenbo Commit-by: GuoWenbo Merged-by: cann-robot Description: ## 描述 实现 issue #849 的功能要求,经两轮检视修复后完成并发与时序加固: 1. **日志级别下发**:级别变化时 log 将级别传给 drv( drv_log_set_module_log_level),由 drv 转换后设置给 UNIFIEDBUS。env(应用进程)与 shmem(系统进程)两条级别来源路径均同步下发。 2. **drv 日志回调注册**:进程将 DlogDriverLog 注册给驱动(halCtl),驱动/UNIFIEDBUS 日志经此回流到本进程写路径落盘。host / device / MDC 三种形态统一支持。 ### 架构:全部收敛到 dlog_drv.c(统一实现,无 host/device 双份) 新增 dlog_drv.c/h 作为与 drv 交互的唯一适配层(原方案的 plog_driver_log.c 双实现与弱符号挂接已废弃删除): | 能力 | 接口 | 说明 | |---|---|---| | 级别下发 | DlogSetDriverLogLevel | ALL_MODULE 扇出为 [DRV, UNIFIEDBUS] 单次调用;仅 DRV/UNIFIEDBUS 管辖模块下发;DLOG_NULL(4) 为合法级别正常下发,由 drv 内部转换为 ERROR | | 回调注册 | DlogInitDriverLog | mutex 闩锁保证每个初始化周期至多一次;halCtl 优先注册 RUN_LOG handle、失败回退通用 handle | | 回调接收 | DlogDriverLog | 驱动注册时的同步试打直接进入本进程写路径;带 inited 守卫防止注册期间重入死锁 | | 解注册/复位 | DlogUnregisterDriverLog / DlogResetDriverLog | 仅作用于本库注册过的 handle(委托形态不误删他库注册);复位时卸载驱动库句柄,杜绝泄漏 | **注册时机按构建形态区分**,保证 plog 写回调就位后再注册(驱动同步试打才不会落错路径): - host alog/slog(LOG_CPP):首写的 DlogInit 末尾 - unified_dlog(非 LOG_CPP):PlogInitHostLog 内、写回调注册之后(其 DllMain 早于 plog 构造器执行,不能在那里注册) - device:DllMain(slog)或首写(alog);MDC:iam 形态的 DlogInit ### 级别同步的三条路径 1. **env 路径**(应用进程):dlog_level_env.c 初始化与 dlog_setlevel 接口 2. **shmem 路径**(系统进程):dlog_level_shm.c 的 watcher 在级别刷新后 SyncLevelToDriver 3. **注册后补发**:handle.logLevel 只携带全局级别,与全局不一致的模块级配置在注册后单独重发(DlogResyncDrvModuleLevels) ### 并发与时序加固(检视修复轮) - g_drvLibMutex 保护驱动库句柄生命周期(懒加载/符号解析/卸载统一走 DlogDrvLibFunc):消除 dlog_setlevel 线程 × shmem watcher 线程并发时的重复 dlopen、句柄覆盖与泄漏 - transfer 与 drv 注册的闩锁采用 **mutex + 原子快路径**(非 pthread_once):复位与尝试同锁串行,无"复位使用中的 once 控制"UB,且保留 DlogFree 后 re-init 重试语义;稳态开销为一次 acquire 原子读 - **API 入口先尝试 transfer 再查转发句柄**:委托形态(存在 libunified_dlog.so,本地无 plog 写回调)首写不再穿透到本地 socket/shm 路径;transfer 成功后不再注册 drv 回调(由 unified_dlog 自己注册),避免覆盖其 handle 导致 driver 日志误路由 - DlogFree / iam 析构先解注册再释放写路径资源;AlogCloseSlogLib/AlogCloseDrvLib 卸载后清空句柄表(原为悬空指针) - TLS 同线程重入防护:闩锁回调内 dlopen 的库若在构造期经本库打日志,不再自死锁 ### UNIFIEDBUS = 15 master 已将 id 14 分配给 ASCTOOL,本 PR 的 UNIFIEDBUS 使用空闲 id 15。模块级别表原地替换占位符,表条目数与 INVALID_MODULE_ID(76)均不变,无 ABI 影响。**该 id 值需 drv/UNIFIEDBUS 侧同步对齐**。 ### 变更范围 include/dfx/base/log_types.h | 1 + src/dfx/log/liblog/slog/dlog_drv.c | 300 ++++++++ (新增) src/dfx/log/liblog/slog/dlog_drv.h | 44 ++ (新增) src/dfx/log/liblog/slog/dlog_core.c | 194 ++++-- src/dfx/log/liblog/slog/dlog_core.h | 12 + src/dfx/log/liblog/slog/dlog_core_iam.c | 90 ++- src/dfx/log/liblog/slog/slog_api.cpp | 73 ++- src/dfx/log/liblog/slog/dlog_level_env.c | 13 +- src/dfx/log/liblog/slog/dlog_level_shm.c | 27 + src/dfx/log/liblog/slog/plog/plog_core.c | 45 ++- src/dfx/log/liblog/slog/plog/plog_driver_log.c | 92 ----- (删除,并入 dlog_drv.c) src/dfx/log/liblog/slog/plog/plog_driver_log.h | 23 -- (删除) src/dfx/log/liblog/slog/{plog,plog_device,plog_device_mdc,plog_host}/CMakeLists.txt src/dfx/log/utils/{log_level.h,log_platform.h} tests/ut/slog/**(ep/rc/iam 用例与桩) 31 files changed, 1301 insertions(+), 186 deletions(-) ## 变更类型 <!-- [x] 表示选中 --> - [x] 🐛 Bug 修复(transfer 时序、并发竞态、句柄泄漏、析构顺序) - [x] ✨ 新功能 - [ ] 💄 代码风格更新(格式化,局部变量) - [ ] ♻️ 重构(既不修复错误也不增加功能的代码变动) - [ ] 📦 构建过程或辅助工具的变动 - [ ] 📝 文档内容更新 ## 关联的Issue https://gitcode.com/cann/runtime/issues/849 ## 如何测试 ### 前提条件 无特殊硬件依赖,UT 可在开发环境直接执行。 ### 测试步骤 1. 编译并运行 host 侧 UT: bash cd build && make ep_alog_host_utest -j8 ./tests/ut/slog/ut/ep/alog_host/toolchain_ep_alog_host_utest 2. 编译并运行 device/MDC 侧及其余 slog 相关 UT,确认无回归: bash make rc_alog_utest log_common_utest log_libalog_utest \ iam_slog_utest mergeslog_coverage_utest -j8 ### 测试结果 | UT 目标 | 用例数 | 结论 | |---|---|---| | ep_alog_host_utest | 128 | 全部通过 | | rc_alog_utest | 12 | 全部通过 | | log_common_utest | 105 | 全部通过 | | log_libalog_utest | 20 | 全部通过 | | iam_slog_utest | 4 | 全部通过 | | mergeslog_coverage_utest | 3 | 全部通过 | | **合计** | **272** | **0 失败,0 回归**(ep 套件连跑 3 次稳定) | ### 本 PR 新增的 UT 用例 | 用例 | 验证点 | |---|---| | DlogSetLevelForUbModule | UNIFIEDBUS 级别 set/get round-trip | | DlogSetLevelModuleIdBoundary | INVALID_MODULE_ID 边界仍被正确拒绝 | | DlogModuleLevelTableStaysAligned | 复用槽位 15 未导致既有模块级别错位(含 DQS/ASCTOOL 邻居位) | | DlogSetLevelReachesDriverAdapter | 下发到达 drv 且参数正确;非管辖模块与非法级别不下发;level 4 正常下发 | | DlogSetLevelDispatchNoDriverSymbol | drv 符号缺失时降级不崩溃不报错 | | DlogRegisterToDriver / DlogUnregisterFromDriverIsSafe | 注册/解注册幂等、无注册时解注册安全 | | UbLogNotWrittenWhenLevelIsNull | 级别为 4 时本地不落盘 | | DlogPrint_FirstWriteForwardedAfterTransfer | **委托形态首写必须转发**,不触碰本地 socket/shm 路径(修复前验证该用例失败) | | DlogConcurrentWriteAndSetLevel | 4 写线程 + 级别翻转线程并发,无崩溃、级别系统一致 | | DlogInitRegistersDriverLog (rc) | **device 侧**注册经 halCtl 到达 drv,注册级别取当前生效级别,同步试打落写路径 | | PerModuleLevelResyncAfterRegister (rc) | 模块级配置与全局不一致时,注册后单独重发该模块级别 | | DlogSetDriverLogLevelReachesDriver (rc) | device 侧下发:单模块、ALL_MODULE 扇出 [DRV, UNIFIEDBUS]、level 4、非管辖与越界拒绝 | ### 实测验证发现(issue #849 评论,2026-09-12 补充) **1. shmem 级别刷新触发三次冗余分发(已修复,31c478e5e)** 实测日志显示设置级别时 dlog_drv.c:230 打印三次(ALL_MODULE 一次 + DRV 一次 + UNIFIEDBUS 一次,level 相同)。 根因:ParseGlobalLevel 的 ALL_MODULE 扇出已覆盖 DRV/UNIFIEDBUS,ParseModuleLevel 的逐模块分发未检查级别是否等于全局。 修复:SyncLevelToDriver 对 DRV/UNIFIEDBUS 增加"级别等于全局则跳过"检查(与 DlogResyncDrvModuleLevels 逻辑一致)。 同时改进维测打印:moduleId=65535 改为语义名称 ALL_MODULE(DRV+UNIFIEDBUS)。 **2. SlogLock 死锁(已由 29f690022 解决)** 2026-09-07 实测发现两个线程(TraceServerSendThread / RunServerProcess)阻塞在 g_slogMutex。 已由 PR 内提交 29f690022("修复首写日志触发的注册死锁与LogLevelWatcher线程重复")解决。 后续审计补充修复(d1cb36373):P1 DlogLevelInit 自死锁 / P2 TOCTOU UAF / P4 读竞态 / P7 fork 协议。 ### 关键风险的独立验证 **1. 模块表下标对齐** 模块级别表按 module id 直接索引,错位会导致所有模块级别混乱。独立程序断言 table[i].moduleId == i: UNIFIEDBUS=15 INVALID_MODULE_ID=76 table_entries=77 index[13]=DQS index[14]=ASCTOOL index[15]=UNIFIEDBUS index[16]=16 RESULT: PASS **2. drv 符号缺失降级** libascend_hal.so 尚未导出 drv_log_set_module_log_level 时:驱动库加载仍成功(LoadDllFunc 对缺失符号仅记录 INFO 跳过),级别下发走静默跳过分支,不报错不崩溃——**drv 尚未发布接口时本 PR 可安全合入**。 **3. 各构建形态编译验证** - alog / slog / unified_dlog 产品目标构建链接通过 - device 宏组合(LOG_CPP × PROCESS_LOG 四种配置)gcc -Werror=implicit-function-declaration 语法扫描通过 - unified_dlog 的 .init_array 顺序经 readelf 复核:DllMain 先于 ProcessLogInit(注册时序前提) ## 核对清单 <!-- [x] 表示选中 --> - [x] 我的代码遵循了项目的代码风格(pre-commit:clang-format + OAT 全过) - [x] 我已对代码进行了自测 - [ ] 我已更新了相关的文档(版本说明书/发布文档待确认) - [x] 我在标题中使用了合适的类型标签(如:feat:, fix:) - [x] 我已经详细阅读了贡献指南(CONTRIBUTING.md),并遵守了其中的所有规定 ## 其他信息 ### 检视意见处理情况 两轮检视共 11 条意见已全部处理完毕(详见各评论回复),覆盖:dlopen 句柄泄漏、级别下发懒加载竞态、iam 闩锁一致性、once 复位 UB、析构顺序、分支冗余、并发用例补充、文件缺失(已过时)。 ### 遗留问题 #### 1. drv 接口尚未发布 drv_log_set_module_log_level 与 halCtl 注册命令当前在 libascend_hal.so 中不存在,运行时走降级分支。本 PR 可先合入,待 drv 发布后自动生效,log 侧无需再改。**UNIFIEDBUS=15 的 id 值需 drv/UNIFIEDBUS 侧对齐。** #### 2. unified_dlog 形态无 UT 目标覆盖 unified_dlog(非 LOG_CPP + plog)没有独立 UT 构建目标(PR 前即如此),其"plog 构造器内注册、写回调之后"路径已通过产品目标编译 + .init_array 顺序复核验证,建议在带 libunified_dlog.so 的环境实测:plog_host 进程首条日志与 UNIFIEDBUS 日志均应落 plog 文件而非 slogd。 #### 3. 并发用例的 mockcpp 限制 DlogConcurrentWriteAndSetLevel 的并发线程刻意避开驱动分发路径(dlopen/dlsym 为 mockcpp 桩、非线程安全);dlog_drv.c 的 mutex 正确性由评审与单线程分发用例覆盖。 ### 不在本 issue 范围内的项 按 issue 原文「本issue只关注log跟drv的交互,drv和UNIFIEDBUS的交互由其他issue跟踪」: - UNIFIEDBUS 8 级 → alog 4 级映射(ub2alog_level):由 drv 侧 func2 实现 - urma_register_log_func / urma_unregister_log_func 对接:由 drv 完成 ### 无法在本仓验证的项 | 项 | 原因 | |---|---| | UNIFIEDBUS 日志端到端采集 | 依赖 drv 与 UNIFIEDBUS 实现 | | drv_log_set_module_log_level 真实调用 | drv 尚未发布该符号 | | 级别 4 → ERROR 的 drv 侧转换 | 在 drv 内部实现,log 侧不可见 | | 多芯片平台遍历 | 需硬件环境 | | 性能影响 | 需 UNIFIEDBUS 压测环境(API 入口新增开销为一次原子读,远低于写路径 mutex+vsnprintf 开销) | ### 既有问题(本 PR 未修改,仅记录) - DlogWithKVInner(DlgWithKVInnerForC) 在 master 上即无转发检查,委托形态下走本地路径——pre-existing,未扩大 - 两份 log_types.h 的 DQS = 13 不同步问题仍保持原状,未顺带修复 See merge request: cann/runtime!4515 | 9 天前 | |
Initial commit | 8 个月前 |
| 文件 | 最后提交记录 | 最后更新时间 |
|---|---|---|
| 9 天前 | ||
| 8 个月前 |