go/java/C++覆盖率工具原理汇总学习记录

news2024/11/14 19:59:10

go–goc

goc采用的是插桩源码的形式,而不是待二进制执行时再去设置breakpoints。这就导致了当前go的测试覆盖率收集技术,一定是侵入式的,会修改目标程序源码。直接看案例

package main

import "fmt"

func main() {
  test2(3)
  fmt.Println("main")
  test2(-3)
}

func test1() {
  fmt.Println("hello")
  fmt.Println("test1")
}

func test2(a int) {
  if a > 0 {
    fmt.Println(a)
    test1()
  } else {
    fmt.Println("world")
  }
  fmt.Println("test2")
}

运行命令

 go tool cover -mode=count -var=CoverageVariableName learn/cover/exp1/main.go >> learn/cover/exp1/main_gen.go

生成的代码如下

//line learn/cover/exp2/main.go:1
package main

import "fmt"

  
func main() {CoverageVariableName.Count[0]++;
  test2(3)
  fmt.Println("main")
  test2(-3)
}

  
func test1() {CoverageVariableName.Count[1]++;
  fmt.Println("hello")
  fmt.Println("test1")
}

  
func test2(
    a int) {CoverageVariableName.Cont[2]++;
  if a > 0 {CoverageVariableName.Count[4]++;
    fmt.Println(a)
    test1 (
    CoverageVariableName.Count[5]++
    
    } else{ CoverageVariableName.Count[5]++;{
      
  fmt.Println("world")
  }}
  
  CoverageVariableName.Count[3]++;fmt.Println("test2")
}

var CorageVariableName = struct {
  Count     [6]uint32     //数组中每个元素代表相应基本块(basic block)被执行到的次数
  Pos     [3 * 6]uint32     // 代表的各个基本块在源码文件中的位置,三个为一组。比如这里的`21`代表该基本块的起始行数,`23`代表结束行数,`0x2000d`比较有趣,其前16位代表结束列数,后16位代表起始列数。通过行和列能唯一确定一个点,而通过起始点和结束点,就能精确表达某基本块在源码文件中的物理范围
  NumStmt   [6]uint16   // 代表相应基本块范围内有多少语句(statement)
} {
  Pos: [3 * 6]uint32{
    5, 9, 0x2000d, // [0]
    11, 14, 0x2000e, // [1]
    16, 17, 0xb0013, // [2]
    23, 23, 0x160002, // [3]
    17, 20, 0x3000b, // [4]
    20, 22, 0x30008, // [5]
  },
  NumStmt: [6]uint16{
    3, // 0
    2, // 1
    1, // 2
    1, // 3
    2, // 4
    1, // 5
  },
}

java-jacoco

jacoco是一个开源的代码覆盖率工具,针对java语言,其使用方法很灵活,可以嵌入到Ant、Maven中;可以作为Eclipse插件,可以使用其JavaAgent技术监控Java程序等等。

java 代码运行原理

java代码是运行在java虚拟机(JVM)上的,JVM相当于一个虚拟的计算机,符合约定的指令均可在上面执行。java编译后的class文件就是一种符合JVM的字节码指令集合,所以可以在JVM上执行。所以JVM其实指的不是运行java代码的虚拟机,它并不关心字节码是由哪种语言编译而来的,而是只要符合该虚拟机指令的文件均可在上面执行,如Kotlin、Groovy、JRuby、Jython、Scala等编译后也可以在JVM上执行。所以java代码在JVM里运行的时候,实际运行的是编译后的class文件字节码指令流,如果想要改变类的行为,分析类的信息等,只需要修改对应的字节码即可

jacoco原理

jacoco即是通过修改class文件的字节码来进行代码覆盖率统计的。即,在原有class字节码中的指定位置插入探针字节码,形成新的字节码指令流。jacoco使用的是ASM字节码框架对字节码进行修改的。jacoco的探针实际是一个布尔值,当代码执行到探针位置时,将其置为true,该探针前面的代码会被认为执行过,然后对该部分代码对应的html文件中的css样式进行染色(红色表示未覆盖,绿色表示已覆盖,黄色表示部分覆盖),形成最终的覆盖率报告

jacoco插庄模式(插入探针)

  • offline模式:编译时插桩,在测试前先对文件进行插桩,然后生成插过桩的class或jar包,测试插过桩 的class和jar包后,会生成动态覆盖信息到文件,最后统一对覆盖信息进行处理,并生成报告。

  • on-the-fly模式:运行时插桩,JVM中通过-javaagent参数指定特定的jar文件启动Instrumentation的代理程序,启动jvm实例会调用程序里面的premain方法,通过Class Loader装载一个class前判断是否转换修改class文件,将统计代码插入class,测试覆盖率分析可以在JVM执行测试代码的过程中完成。addTransformer 方法并没有指明要转换哪个类,转换发生在 premain 函数执行之后,main 函数执行之前(转换发生在JVM定义类之前),这时每装载一个类,transform 方法就会执行一次,看看是否需要转换。

插庄对比

在这里插入图片描述

C+±gcov

gcov是gcc内置的一个代码覆盖率工具,配合GCC共同实现对C/C++文件的语句覆盖、功能函数覆盖和分支覆盖测试,通常需要结合lcov生成可视化报告。
在这里插入图片描述

工作流

  1. 编译前,在编译器中加入编译器参数-fprofile-arcs -ftest-coverage;
  2. 源码经过编译预处理,然后编译成汇编文件,在生成汇编文件的同时完成插桩。插桩是在生成汇编文件的阶段完成的,因此插桩是汇编时候的插桩,每个桩点插入3~4条汇编语句,直接插入生成的*.s文件中,最后汇编文件汇编生成目标文件,生成可执行文件;并且生成关联BB和ARC的.gcno文件;
  3. 执行可执行文件,在运行过程中之前插入桩点负责收集程序的执行信息。所谓桩点,其实就是一个变量,内存中的一个格子,对应的代码执行一次,则其值增加一次;
  4. 生成.gcda文件,其中有BB和ARC的执行统计次数等,由此经过加工可得到覆盖率。

配置使用

gcc -fprofile-arcs -ftest-coverage -o test test.c

-ftest-coverage:在编译时产生.gcno文件,它包含了重建基本块图和相应的块的源码的行号信息*

-fprofile-arcs :在运行编译过的程序,会产生.gcda文件,包含基本块弧跳变的次数信息
Gcc在编译阶段指定 –ftest-coverage 等覆盖率测试选项后,GCC会:
1、 在输出目标文件中留出一段存储区保存统计数据;

2、 在源代码中每行可执行语句生成的代码之后附加一段更新覆盖率统计结果的代码,也就是插桩(后面详细介绍);

3、 Gcc编译,会生成*.gcno文件,它包含重建基本块图和相应块的源码的行号信息;

4、 在最终可执行文件中,进入main函数之前调用gcov_init内部函数初始化统计数据区,并将gcov_init内部函数注册为exit_handers,用户代码调用exit正常结束时,gcov_exit函数得到调用,并继续调用__gcov_flush输出统计数据到*.gcda文件

插庄

gcov是使用 基本块BB 和 跳转ARC 计数,结合程序流图来实现代码覆盖率统计的
基本块BB:如果一段程序的第一条语句被执行过一次,这段程序中的每一个都要执行一次,称为基本块。一个BB中的所有语句的执行次数一定是相同的。一般由多个顺序执行语句后边跟一个跳转语句组成。所以一般情况下BB的最后一条语句一定是一个跳转语句,跳转的目的地是另外一个BB的第一条语句,如果跳转时有条件的,就产生了分支,该BB就有两个BB作为目的地。

跳转ARC:从一个BB到另外一个BB的跳转叫做一个arc,要想知道程序中的每个语句和分支的执行次数,就必须知道每个BB和ARC的执行次数。

如果把BB作为一个节点,这样一个函数中的所有BB就构成了一个有向图。要想知道程序中的每个语句和分支的执行次数,就必须知道每个BB和ARC的执行次数。根据图论可以知道有向图中BB的入度和出度是相同的,所以只要知道了部分的BB或者ARC大小,就可以推断所有的大小。
在这里插入图片描述

由ARC的执行次数来推断BB的执行次数。所以对部分 ARC插桩,只要满足可以统计出来所有的BB和ARC的执行次数即可。

记录BB块和ARB的数据结构为:

struct bb

{

long zero_word; //是否被插入到链表中

const char *file_name; //当前被测试文件名

long *count;//指向bx2的指针

long ncounts;//桩点个数

struct bb *next;//下一个文件的BX2信息

};

1、GCC在插桩的过程中会向源文件的末尾插入一个静态数组,BX2.,数组的大小就是这个源文件中桩点的个数。BX2+0代表第0个桩点的位置,BX2+n代表第n个桩点的位置,数组的值就是桩点的执行次数。

2、每个桩点插入汇编语句:

*按照我的理解,汇编语句是inc$(BX2+n).

3、 BX2数组链表:

为了便于统计,gcc还将各个源文件中的BX2数组链接成一个链表,这个链表结构是在测试main函数之前就产生了,在调用main之前会有一个类似构造函数的函数,进行构建链表。这个函数会在退出时调用exit函数计算执行次数生成.gcda文件

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/452867.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Vue项目基于driverjs实现新用户导航

引导页就是当用户第一次或者手动进行触发的时候,提示给用户当前系统的模块介绍,比如哪里是退出,哪里是菜单等等相应的操作。 无论是开发 APP 还是 web 应用,新手引导都是一个很常见的需求,一般在这2个方面需要新手引导…

Java阶段二Day07

Java阶段二Day07 文章目录 Java阶段二Day07V17UserControllerDispatcherServletControllerRequestMapping V18DispatcherServletHandleMapping V19BirdBootApplication 线程池线程的执行过程线程池API 数据库数据库的基本概念数据库管理系统中常见的概念 SQL分类DDL语言-数据定…

浅析流媒体技术的发展趋势及EasyCVR视频技术的应用

随着科技的不断发展,流媒体已经成为人们日常生活中必不可少的一部分。为了进一步提高流媒体的质量,未来的技术革新方向将集中在以下几个方面: 1)提高视频编解码技术的质量和效率 随着高清视频的普及,人们对流媒体的质…

GD32F470 移植STM32F429工程 Keil调试笔记

keil版本:5.25 安装 GigaDevice.GD32F4xx_DFP.3.0.4.pack Keil.STM32F4xx_DFP.2.15.0.pack 一、GD32F470 与 STM32F429 切换编译 1、原项目为STM32F429 工程,切换到GD32F470 只需在 Options for Target"“对话框的Device菜单中选中“GD32F470II”…

Http协议—请求的构造

目录 一、通过 form表单 构造HTTP请求 1、form 发送 Get 请求 (1)form 的重要参数 (2)input 的重要参数 2、通过 form 构造 Post 请求 二、通过 ajax 构造 HTTP 请求 1、基于 jQuery 中的 ajax 构造 (1&#x…

动态网站开发讲课笔记07:EL和JSTL

文章目录 零、本节学习目标一、EL(一)EL基本语法1、EL的概念2、EL的语法3、案例演示(1)用EL读取保存的信息(2)使用Java代码与EL获取信息的对比 4、EL基本语法的特点 (二)EL中的标识符…

从语言模型到ChatGPT,大模型训练全攻略

文|python 前言 你是否想过,为什么ChatGPT能够如此火爆呢?我认为这主要是因为ChatGPT提供了好玩、有用的对话式交互功能,能够为用户提供帮助,而不仅仅是依靠“大”模型的魅力。毕竟,GPT-3在2020年就已经推出…

RedHat yum没有已启用源的解决方法

一般安装的红帽系统,自带的yum在没有付费的情况下是无使用的,所以我们要进行换源。 1、环境准备 先检查以下我们的linux系统环境,看看是不是Redhat7的版本 ,出现如下图所示的界面 cat /etc/redhat-release 检查系统中是否安…

科技云报道:重塑增长新动能,“数智融合”捷径该如何走?

科技云报道原创。 如果说,过去是数字化转型的试验阶段,实施的是开荒动土、选种育苗,那么当前要进行的是精耕细作、植树造林。 数字化转型已进入了由个别行业、个别场景的“点状应用”向各行各业全流程、全环节“整体渗透”的关键期。 云计算…

yolov5-7.0 训练自己的数据集之检测数据集

YOLOv5是一种单阶段目标检测算法,有很高的精度和速度,因为项目需求,需要利用yolov5-7.0训练自己的目标检测数据集。 假设,环境已经配置完成! 1)准备数据集 在D:\Graduation_Project_Coding\network_class…

QGIS中导入dwg文件并使用GetWKT插件获取绘制元素WKT字符串以及QuickWKT插件实现WKT显示在图层

场景 QGIS在Windows上下载安装与建立空间数据库连接: https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/124108040 在上面实现QGIS的安装之后,版本是3.26.3。 业务需求: 1、在dwg文件上绘制多边形区域,并获取绘制区域的wkt字…

如何在美国虚拟主机上安装WordPress?

如果你想在美国虚拟主机上搭建一个博客网站,那么WordPress是一个非常好的选择。本文将介绍如何在美国虚拟主机上安装WordPress,帮助你快速建立一个美观、高效的博客网站。 一、选择合适的虚拟主机服务商 在安装WordPress之前,你需要选择一个可…

[Netty] Netty自带的心跳机制 (十五)

文章目录 1.IdleStateHandler介绍2.IdleStateHandler源码解析3.IdleStateHandler总结 1.IdleStateHandler介绍 Netty服务端心跳机制: IdleStateHandler, 这个类可以对三种类型的心跳检测。 ChannelHandler.Sharable public class IdleHandler extends ChannelInboundHandlerA…

redis7详解

Redis基础 文章目录 一、Redis入门概述是什么?能干嘛?主流功能与应用优势 Redis7新特性 二、Redis安装配置Redis7安装步骤Redis7卸载步骤 三、Redis10大数据类型Redis 键(key)1、Redis 字符串(String)2、Reids列表(Lis…

java版本电子招标采购系统源码—企业战略布局下的采购

​ 智慧寻源 多策略、多场景寻源,多种看板让寻源过程全程可监控,根据不同采购场景,采取不同寻源策略, 实现采购寻源线上化管控;同时支持公域和私域寻源。 询价比价 全程线上询比价,信息公开透明&#xff0…

谷歌云 | 内部 HTTP(S) 负载均衡器现在可以在全球范围内访问

【本文由Cloud Ace整理发布,Cloud Ace 是谷歌云全球战略合作伙伴,拥有 300 多名工程师,也是谷歌最高级别合作伙伴,多次获得 Google Cloud 合作伙伴奖。 作为谷歌托管服务商,我们提供谷歌云、谷歌地图、谷歌办公套件、谷…

店铺销售管理系统有哪些用?该如何选?

现在市场竞争越来越激烈,实体店想要吸引更多意向客户,快速提高销量,使用店铺管理系统来智能化管理店铺,已经成为不可或缺的手段。 店铺管理系统能够帮助实体店老板实现店铺销售和库存管理的自动化和集中化,提高门店的销…

Ceph入门到精通-podman 入门实战

目录 podman安装podman制作本地镜像podman(docker)命令回顾podman快速入门 一入编程深似海,从此节操是路人。 最近使用podman,就想着写一篇总结性的笔记,以备后续参考。就如同写代码,不写注释,过了一段时间可能会想这…

【Leetcode】572.另一棵树的子树

另一棵树的子树 题目思路代码 题目 思路 这道题目主要用到判断两个二叉树是否相同 对root为根节点的树进行递归遍历判断 是否存在一颗子树和以subRoot为根节点的树 代码 class Solution {public boolean isSubtree(TreeNode root, TreeNode subRoot) {if(root null){return…

HJL-93/A数字式交流三相电流继电器 导轨安装 约瑟JOSEF

品牌:JOSEF约瑟名称:数字式交流三相电流继电器型号:HJL系列功率消耗:≤5W触点容量:250V/5A额定电压:58、100、110、220V HJL系列 数字式交流三相电流继电器型号: HJL-93/AY数字式交流三相电流继…