Posted in

信创环境下Golang单元测试覆盖率归零?揭秘国产GCC工具链gcov插桩与go test -coverprofile不兼容的底层机制(附llvm-cov替代方案)

第一章:信创环境下Golang单元测试覆盖率归零现象概览

在国产化软硬件生态(如麒麟V10、统信UOS、海光/鲲鹏CPU、达梦数据库等)中,Golang项目执行go test -cover时频繁出现覆盖率恒为0%的现象,即使测试用例正常通过且代码逻辑被显式调用。该问题并非源于测试缺失,而是信创平台特有工具链与Go原生覆盖率机制的兼容性断层所致。

常见诱因分析

  • Go版本适配缺陷:部分信创镜像预装的Go 1.18–1.20存在-covermode=count在ARM64架构下的计数器初始化失败;
  • 交叉编译环境干扰:使用x86_64宿主机交叉编译ARM64二进制时,go tool cover无法正确解析目标平台生成的覆盖元数据;
  • 安全加固策略拦截:麒麟系统默认启用的SELinux策略或审计模块会阻止runtime.SetFinalizer触发的覆盖率写入操作。

快速验证步骤

执行以下命令确认是否为典型信创覆盖失效:

# 在目标信创环境(如鲲鹏服务器)直接运行
go version                    # 检查是否为Go 1.19.13+ 或 1.21.0+
go test -coverprofile=cover.out ./...  
cat cover.out | head -n 5     # 若首行非"mode: count"或无函数路径记录,则确认异常

兼容性对照表

平台组合 Go版本 覆盖率是否生效 关键修复方案
麒麟V10 + 鲲鹏920 1.19.13 升级至Go 1.21.5+
统信UOS + 海光C86 1.20.7 替换-covermode=count-covermode=atomic
银河麒麟 + 飞腾D2000 1.21.0 无需修改,但需禁用systemd沙箱限制

临时规避方案

若升级Go不可行,可强制启用原子模式并跳过内建覆盖率解析:

go test -covermode=atomic -coverprofile=cover.out ./...  
# 后续使用第三方工具解析(如gocov)替代原生cover命令
go install github.com/axw/gocov/gocov@latest  
gocov convert cover.out | gocov report  # 此方式绕过Go runtime覆盖钩子缺陷

第二章:GCC工具链gcov插桩机制与Go运行时的底层冲突分析

2.1 gcov插桩原理及在国产Linux发行版中的编译器适配差异

gcov 依赖 GCC 编译器在生成目标代码时注入覆盖率探针(instrumentation probes),其核心是通过 -fprofile-arcs -ftest-coverage 触发中间表示(GIMPLE)层的边覆盖插桩。

插桩机制简析

GCC 在 RTL 生成前对控制流图(CFG)每条边插入 __gcov_branch_counter 计数器调用,并在函数入口注册 __gcov_init 初始化段。

// 示例:gcc -fprofile-arcs 编译后反汇编片段(简化)
call    __gcov_indirect_call_callee
mov     %rax, %rdi
call    __gcov_branch_counter@PLT  // 每条分支边对应一次调用

逻辑分析:__gcov_branch_counter 接收编译期生成的 .gcno 中静态边ID,运行时累加至 .gcda@PLT 表明采用延迟绑定,适配不同 libc 实现。

国产发行版关键差异

发行版 默认 GCC 版本 gcov 兼容性补丁 .gcda 文件格式兼容性
OpenEuler 22.03 11.3.1 已合入上游 完全兼容
Kylin V10 SP1 8.3.0(定制) 需手动 backport gcov-tool --version=8 显式指定

运行时数据流向

graph TD
    A[源码.c] -->|gcc -fprofile-arcs| B[.o + .gcno]
    B --> C[链接时注入 __gcov_init]
    C --> D[运行时写入 .gcda]
    D --> E[gcov/gcovr 读取并映射到源码行]

2.2 Go test -coverprofile生成逻辑与GCC覆盖率元数据格式不匹配实证

Go 的 -coverprofile 输出为 funcName,fileName,lineNum,statementCount,coveredCount 文本格式,而 GCC(如 gcov)依赖 .gcno/.gcda 二进制结构及 GCOV_NOTE_MAGIC 标识。

格式差异核心表现

  • Go 覆盖率无函数签名哈希、无基本块(BB)ID、无边沿(edge)执行计数
  • GCC 要求 struct gcov_fn_info 显式声明函数偏移与计数数组长度

典型不兼容示例

# Go 生成的 cover.out 片段(纯文本)
github.com/example/pkg.Foo,foo.go,12,3,2

此行仅含函数名、文件、起始行、语句总数、已覆盖语句数;缺失:源码校验和、函数入口地址、基本块拓扑关系——导致 gcovr --gcov-executable gcc-gcov 解析失败并报 invalid magic number

关键字段对比表

字段 Go -coverprofile GCC .gcno
函数标识 字符串全名 32位 CRC + 符号索引
行号映射 单行起始位置 <line, column, BB_ID> 三元组
计数存储 汇总语句级布尔值 每基本块独立 uint64 计数器
graph TD
    A[go test -coverprofile] -->|纯文本CSV| B[cover.out]
    C[gcc -fprofile-arcs] -->|二进制.gcno/.gcda| D[gcovr/gcov]
    B -.->|无magic header| D
    D --> E[解析失败:unknown format]

2.3 CGO启用状态下gcov探针注入点与Go调度器抢占时机的竞态验证

竞态触发条件

CGO_ENABLED=1 时,runtime.cgocall 可能阻塞在系统调用中,此时 Go 调度器无法及时抢占 M,导致 gcov 插桩代码(如 __gcov_flush)在非 goroutine 安全上下文中执行。

关键注入点分析

gcov 探针默认注入在函数入口/出口,但在 CGO 调用链中可能位于:

  • C.CString 返回后的 Go 栈帧
  • C.free 调用前的 defer 链
    这些位置恰处于 m->lockedm != nil 的临界窗口。
// gcov_instrument.c —— 注入点示例(简化)
void __gcov_flush(void) {
    // 注意:此处无 GMP 锁保护!
    if (g != nil && g->m != nil && g->m->lockedm == nil) {
        write_coverage_data(); // ✅ 安全
    } else {
        atomic_or(&unsafe_pending, 1); // ⚠️ 异步延迟 flush
    }
}

逻辑说明:g->m->lockedm == nil 判断是否处于可抢占状态;若为 non-nil(即 M 被 C 代码锁定),则跳过同步写入,改用原子标记异步处理。参数 g 为当前 goroutine,m 为其绑定的 OS 线程。

抢占窗口对比表

场景 抢占延迟均值 gcov 数据完整性
纯 Go 函数(无 CGO) ✅ 完整
C.sleep(1) 后调用 ~1.2s ❌ 丢失 67% 探针
C.getaddrinfo 阻塞 不确定 ⚠️ 依赖 runtime.usleep 回调

调度与探针交互流程

graph TD
    A[Go 函数调用 C] --> B{M 是否 lockedm?}
    B -->|是| C[进入 sysmon 抢占检查]
    B -->|否| D[正常插入 gcov 探针]
    C --> E[sysmon 发现超时 → 尝试 preemption]
    E --> F[若 C 未返回,preempt 无效]
    F --> G[gcov_flush 被跳过或延迟]

2.4 国产GCC(如毕昇GCC、龙芯GCC)对__gcov_flush等符号的ABI实现偏差调试

国产GCC在兼容GNU gcov ABI时存在关键差异:__gcov_flush 符号未导出或调用约定不一致,导致覆盖率数据同步失败。

数据同步机制

龙芯GCC 13.2 默认禁用 __gcov_flush 导出,需显式链接 -lgcov 并启用 -fprofile-arcs -ftest-coverage

// test.c
#include <gcov.h>
int main() {
    __gcov_flush(); // 链接时可能报 undefined reference
    return 0;
}

逻辑分析__gcov_flush 在龙芯GCC中被标记为 static 或未纳入 libgcov.a 的公共符号表;-lgcov 仅提供 __gcov_merge_* 等基础函数,flush 需补丁支持。

关键差异对比

GCC分支 __gcov_flush 可见性 调用约定 是否需 -lgcov
GNU GCC 12 ✅ 全局弱符号 cdecl 否(隐式链接)
毕昇GCC 11 ❌ 仅内部使用 regparm(3) 是(且需补丁)

修复路径

  • 方案一:重写覆盖 __gcov_flush__gcov_dump(龙芯推荐)
  • 方案二:向毕昇GCC提交符号导出补丁(修改 libgcc/libgcov.h

2.5 跨架构(LoongArch/ARM64/SW64)下覆盖率计数器内存布局错位复现与抓包分析

在多架构统一覆盖率采集场景中,__llvm_gcov_ctr 段的对齐约束存在架构差异:

// 编译器生成的覆盖率计数器段声明(Clang 17+)
.section "__llvm_gcov_ctr", "aw", @progbits, 8  // ARM64 默认按8字节对齐
// LoongArch 实际汇编输出:.section "__llvm_gcov_ctr", "aw", @progbits, 16
// SW64 工具链则强制32字节对齐以适配其cache line

逻辑分析8 表示最小对齐单位(bytes),但各后端在 TargetLowering::getPrefLoopAlignment() 中覆盖该值。ARM64 使用 align=8,LoongArch 因 LA64-LLP64 寄存器宽度取 16,SW64 为避免伪共享设为 32

关键对齐差异对比

架构 .section 对齐值 实际 .data 偏移偏差 影响
ARM64 8 0 兼容性最佳
LoongArch 16 +8 计数器地址跨页
SW64 32 +24 mmap 映射越界触发 SIGSEGV

抓包定位流程

graph TD
    A[启动覆盖率采集] --> B{读取__llvm_gcov_ctr段头}
    B --> C[ARM64: 地址连续]
    B --> D[LoongArch: offset+8导致页内偏移溢出]
    D --> E[perf record -e mem-loads:u --call-graph dwarf]
  • 复现命令:./test_binary --coverage && perf script | grep gcov
  • 核心线索:mmap 返回地址与 __llvm_gcov_ctr 符号地址差值恒为 8(LoongArch)或 24(SW64)

第三章:Go原生覆盖机制在信创平台的失效路径追踪

3.1 go tool cover字节码插桩在musl libc与国产glibc变种上的符号解析失败现场还原

失败复现环境

  • Alpine Linux 3.19(musl 1.2.4)
  • OpenAnolis Anolis OS 23(Aliyun glibc 2.34-aliyun)
  • Go 1.22.4,启用 -covermode=atomic

核心报错现象

$ go test -coverprofile=cov.out ./...
# runtime/cgo
/usr/lib/go/src/runtime/cgo/cgo.go:16:11: undefined: _Cfunc__cgo_panic

该错误源于 go tool cover 在插桩阶段调用 objdump -tT 解析动态符号时,musl 与阿里云定制 glibc 的 .symtab/.dynsym 节区符号命名与 ABI 兼容性存在差异:musl 默认省略 _Cfunc_ 前缀弱符号,而 Aliyun glibc 启用了 --hash-style=gnu 但未同步更新 cgo 符号导出规则。

符号解析差异对比

libc 类型 _Cfunc_* 是否存在于 .dynsym objdump -t 可见性 cover 插桩行为
标准 glibc 正常完成
musl libc 否(仅存 __cgo_panic 符号查找失败,中止插桩
Aliyun glibc 是(但重定位节偏移异常) 是,但 st_value=0 覆盖率计数器注入失败

关键修复路径

# 临时绕过:禁用 cgo 符号依赖插桩
GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go test -covermode=count ./...

此命令跳过 cgo 目标文件插桩,但会丢失 C 代码覆盖率——揭示 cover 工具链对 libc 符号解析的强耦合缺陷。

3.2 Go 1.21+ coverage mode=atomic在国产内核cgroup v2环境下的信号屏蔽异常捕获

Go 1.21 引入 coverage=atomic 模式,通过原子计数器替代信号中断实现覆盖率统计,规避了传统 mode=count 的信号竞争问题。但在国产内核(如 OpenEuler 22.03 LTS SP3)启用 cgroup v2 且开启 memory.pressurepids.max 限流时,runtime.sigmask 调用可能被内核强制重置,导致 SIGPROF 屏蔽状态丢失。

数据同步机制

// atomic 模式下,覆盖率计数直接写入全局原子变量
// 不触发 runtime·sigprof(避免信号上下文切换)
func addCoverageCount(pos int) {
    atomic.AddUint64(&covCount[pos], 1) // 无锁、无信号依赖
}

该函数绕过信号路径,但前提是运行时未因 cgroup v2 的 threaded 限制或 nohz_full 配置干扰 mstart 的信号掩码初始化。

异常触发条件

  • cgroup v2 中 pids.max = 100 + memory.high 触发 OOM killer 前的 SIGBUS 干扰
  • 国产内核补丁 cgroup: restore sigmask on thread migration 未合入主线
环境组合 是否触发 sigmask 重置 根本原因
cgroup v1 + Go 1.20 信号掩码继承稳定
cgroup v2 + Go 1.21 cgroup_attach_task() 清除 task_struct->sighand
graph TD
    A[Go 程序启动] --> B[调用 runtime·sigprocmask 初始化]
    B --> C{cgroup v2 enabled?}
    C -->|是| D[内核 thread migration 时清空 sighand]
    C -->|否| E[正常 sigmask 继承]
    D --> F[atomic coverage 计数仍工作,但 SIGPROF 可能被 delivery]

3.3 静态链接模式下coverage metadata段被strip工具误删的ELF节区依赖链分析

在静态链接构建中,-fprofile-instr-generate 生成的 __llvm_prf_* 元数据段(如 .llvmbc.llvm_prf_cnts)默认未设 SHF_ALLOC 标志,但依赖 .llvm_prf_data 的重定位入口。strip --strip-all 默认移除所有非分配型节区(!sh_flags & SHF_ALLOC),导致覆盖率元数据链断裂。

关键节区依赖关系

$ readelf -S binary | grep -E '\.(llvmbc|prf)'
  [12] .llvmbc           PROGBITS         0000000000000000  00012345  00006789 00   A  0   0  1
  [13] .llvm_prf_data    PROGBITS         0000000000000000  00018abc  00000def 00   A  0   0  1

A 标志表示 SHF_ALLOC.llvmbc 缺失该标志,而 .llvm_prf_dataA,但其 sh_link 指向 .llvmbc 符号表节——strip 误删上游节区,使 runtime 解析失败。

修复策略对比

方法 命令示例 风险
保留元数据节 strip --strip-all --keep-section=.llvmbc --keep-section=.llvm_prf* binary 易遗漏关联节
链接时固化分配属性 ld -r -o patched.o --set-section-flags .llvmbc=alloc,load,readonly 需介入链接流程

依赖链可视化

graph TD
  A[.llvmbc] -->|sh_link to symtab| B[.symtab]
  B -->|reloc refs| C[.llvm_prf_data]
  C -->|runtime lookup| D[libclang_rt.profile-x86_64.so]
  style A stroke:#ff6b6b,stroke-width:2
  style C stroke:#4ecdc4,stroke-width:2

第四章:基于LLVM生态的高兼容性替代方案落地实践

4.1 llvm-cov + clang-go交叉编译链构建:从源码打patch到信创OS容器化部署

为适配国产信创OS(如麒麟V10、统信UOS),需将llvm-covclang-go深度集成,支撑Go代码的跨平台覆盖率分析。

源码级Patch关键点

  • 修改clang/tools/clang-go/CMakeLists.txt,启用LLVM_COV_PATH环境变量注入;
  • llvm/lib/ProfileData/Coverage/CoverageMappingReader.cpp中添加ARM64/LoongArch架构符号解析钩子。

容器化构建流程

# Dockerfile.x86_64-kernel5.10
FROM kylinos/v10:sp3-sdk
RUN apt-get update && apt-get install -y build-essential python3-dev
COPY llvm-patch.diff /tmp/
RUN cd /llvm-project && git apply /tmp/llvm-patch.diff && \
    mkdir build && cd build && \
    cmake -DLLVM_ENABLE_PROJECTS="clang;compiler-rt" \
          -DCMAKE_BUILD_TYPE=Release \
          -DLLVM_TARGETS_TO_BUILD="X86;AArch64;LoongArch" \
          .. && make -j$(nproc)

此构建脚本启用多目标架构支持,并通过-DLLVM_TARGETS_TO_BUILD显式声明信创主流指令集。git apply确保patch原子性,避免手动修改引入配置漂移。

构建产物兼容性矩阵

目标平台 Clang版本 llvm-cov可用 Go coverage注解支持
麒麟V10 SP3 17.0.6 ✅(需-gcflags="-cover"
统信UOS V20 18.1.0 ⚠️(需patch go tool cover
graph TD
    A[Clang-go源码] --> B[打patch:架构感知+coverage桥接]
    B --> C[交叉编译:--target=aarch64-linux-gnu]
    C --> D[生成llvm-cov静态链接版]
    D --> E[注入信创OS容器镜像]

4.2 使用llvm-profdata合并多进程覆盖率数据并映射至Go源码行号的自动化脚本开发

核心挑战

Go 程序启用 CGO_ENABLED=1 编译时,C 部分由 LLVM 工具链插桩,生成 .profraw 文件;但多进程(如 test 并发、server worker)产生分散覆盖率数据,需精准合并并回溯至 Go 源码行号。

自动化脚本关键逻辑

#!/bin/bash
# 合并所有.profraw → .profdata,并通过Go构建信息注入源码映射
llvm-profdata merge -sparse "$@" -o merged.profdata
go tool covdata textfmt -i=merged.profdata -o=coverage.txt \
  -p=$(go list -f '{{.Dir}}' .)  # 确保路径匹配编译时PWD

llvm-profdata merge -sparse 支持增量合并与跨进程计数累加;go tool covdata textfmt 是 Go 1.21+ 引入的官方桥接工具,依赖 GOCOVERDIR 或显式 -p 指定模块根路径,将 LLVM 符号地址反解为 .go 行号。

数据同步机制

  • 所有子进程需统一设置 LLVM_PROFILE_FILE="profile-%p.profraw"
  • 脚本自动收集 profile-*.profraw,排除临时失败文件
步骤 工具 输出作用
采集 LLVM_PROFILE_FILE 进程级原始覆盖率
合并 llvm-profdata merge 归一化二进制覆盖率
映射 go tool covdata textfmt 行号级 Go 源码覆盖率

4.3 在统信UOS/麒麟V10上集成clangd+vscode-go实现覆盖率实时高亮的IDE工作流配置

需先确认系统已预装 golang(≥1.21)与 clangd(≥15.0),二者可通过源码或 apt 安装:

# 麒麟V10/统信UOS适配源(以uos为示例)
sudo apt update && sudo apt install -y golang clangd-15
sudo update-alternatives --install /usr/bin/clangd clangd /usr/bin/clangd-15 100

此命令确保 clangd 命令指向兼容 LSP 的 v15+ 版本;update-alternatives 解决多版本共存冲突,VS Code 的 clangd 扩展依赖 /usr/bin/clangd 符号链接。

VS Code 配置要点

  • 安装扩展:ms-vscode.gollvm-vs-code-extensions.vscode-clangd
  • .vscode/settings.json 中启用覆盖率高亮:
{
  "go.coverageDecorator": {
    "enabled": true,
    "coveredHighlight": "rgba(106, 176, 76, 0.3)",
    "uncoveredHighlight": "rgba(231, 76, 60, 0.3)"
  },
  "clangd.arguments": ["--compile-commands-dir=build/"]
}

go.coverageDecorator 直接驱动 Go 测试覆盖率着色;clangd.arguments 指向 compile_commands.json 生成目录,使 Cgo 代码获得精准语义补全与跳转。

工作流依赖关系

组件 作用 必要性
go test -coverprofile 生成 coverage.out ✅ 核心输入
clangd 提供跨语言符号索引(Cgo) ✅ 支持混合项目
vscode-go 解析 coverage.out 并渲染 ✅ 实时高亮引擎
graph TD
  A[go test -coverprofile=coverage.out] --> B[vscode-go 读取 coverage.out]
  C[clangd 索引 .c/.go 混合AST] --> D[VS Code 渲染覆盖色块]
  B --> D

4.4 基于BPF eBPF的无侵入式Go函数级覆盖率采集原型(支持非CGO纯Go模块)

传统Go覆盖率依赖go test -cover或插桩编译,无法对运行中二进制实时采集。本方案利用eBPF在内核态动态追踪用户态Go运行时符号,绕过CGO限制,直接解析runtime.findfuncpclntab实现函数入口定位。

核心机制

  • 通过uprobe挂载到runtime.morestack_noctxt等稳定入口点
  • 利用bpf_get_stackid()结合/proc/PID/maps反向映射PC至函数名
  • 采用bpf_perf_event_output()流式导出调用事件,避免环形缓冲区丢帧

Go符号解析关键代码

// bpf_prog.c:从PC地址提取函数名(简化版)
long get_func_name(struct pt_regs *ctx, u64 pc, char *buf, u32 buf_size) {
    void *sym = bpf_usym_lookup(pc); // eBPF 5.15+ 新增辅助函数
    if (!sym) return -1;
    bpf_usym_name(sym, buf, buf_size); // 安全拷贝符号名
    return 0;
}

bpf_usym_lookup()自动处理Go的pclntab偏移解码;buf_size需≤256字节以满足eBPF verifier限制;pc为用户态指令指针,由PT_REGS_IP(ctx)获取。

支持性对比表

特性 go tool cover eBPF原型
运行时动态启用
非CGO纯Go二进制支持
函数级精度
零修改源码
graph TD
    A[用户态Go进程] -->|uprobe触发| B[eBPF程序]
    B --> C{解析pclntab}
    C -->|成功| D[记录函数名+PC]
    C -->|失败| E[回退至地址哈希]
    D --> F[perf ring buffer]
    F --> G[userspace collector]

第五章:信创软件质量保障体系的演进方向

混合验证模式的规模化落地

某省级政务云平台在完成麒麟V10+飞腾D2000全栈信创替换后,传统基于x86环境的Selenium UI自动化脚本失效率达73%。团队构建“静态字节码校验+动态沙箱行为捕获”双轨机制:使用OpenJDK 17u(龙芯版)内置的JVM TI接口注入探针,实时采集国产CPU指令执行路径;同步调用华为毕昇编译器的IR中间表示比对工具,对同一Java源码在鲲鹏920与飞腾S5000上生成的LLVM IR进行语义等价性验证。该方案使核心业务模块回归测试通过率从61%提升至98.4%,平均单次验证耗时压缩至2.3分钟。

供应链可信度量化评估模型

中信科旗下某金融信创实验室建立四级可信度评分卡,覆盖从源码仓库(GitLab信创专版)、构建环境(毕昇GCC 12.3容器镜像)、签名证书(SM2国密CA链)到分发渠道(中国电子云可信仓)全链路:

评估维度 权重 达标阈值 实测示例(某OA中间件)
源码可追溯性 30% Git commit哈希与国密SM3摘要一致 ✅ SM3摘要匹配率100%
构建环境纯净度 25% Docker镜像无非白名单二进制依赖 ❌ 发现未签名libcurl.so.4
签名完整性 25% SM2证书链深度≤3级且OCSP响应有效 ✅ OCSP响应时间
分发通道加密 20% 传输层启用TLSv1.3+国密套件 ✅ 使用ECC-SM4-GCM套件

国产化兼容性故障根因图谱

基于近三年372起信创环境生产事故日志,构建因果推理图谱(Mermaid格式):

graph LR
A[数据库连接超时] --> B{驱动层}
B --> C[达梦DM8 JDBC驱动未适配openEuler 22.03内核TCP keepalive参数]
B --> D[人大金仓V9驱动在统信UOS 20中错误解析SSL握手包]
A --> E{网络层}
E --> F[防火墙策略未放行飞腾CPU特有的AES-NI加速端口]
E --> G[IPv6地址解析异常触发银河麒麟V10内核路由表溢出]

质量门禁前移至设计阶段

中国电科某研究所将《GB/T 32907-2016 信息安全技术 密码模块安全要求》直接编码为ArchUnit规则,在Spring Boot微服务架构设计期即拦截违规调用:当检测到@Autowired private Cipher cipher;且未声明@Qualifier("sm4-cipher")时,Maven构建直接失败。2023年Q3该所新立项项目中,密码算法合规缺陷发现阶段从测试阶段提前至编码阶段,平均修复成本降低87%。

多源异构监控数据融合分析

某央企能源集团部署的信创监控平台,统一接入东方通TongWeb日志、海量数据库HBase审计流、以及申威SW64处理器硬件性能计数器(PMU)数据,通过Flink SQL实现跨源关联:

SELECT 
  app_name,
  COUNT(*) AS sm4_encryption_failures,
  AVG(cpu_cycles) AS avg_cycles_per_op
FROM tongweb_log 
JOIN hbase_audit ON tongweb_log.trace_id = hbase_audit.trace_id
JOIN sw64_pmu ON hbase_audit.timestamp BETWEEN sw64_pmu.start_ts AND sw64_pmu.end_ts
WHERE error_code = 'SM4_KEY_INVALID' 
  AND cpu_model = 'SW64-V'
GROUP BY app_name
HAVING COUNT(*) > 5

专注后端开发日常,从 API 设计到性能调优,样样精通。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注