作者:mannywang,腾讯安全平台后台开发

研发效能是一个涉及面很广的话题,它涵盖了软件交付的整个生命周期,涉及产品、架构、开发、测试、运维,每个环节都可能影响顺畅、高质量地持续有效交付。在腾讯安全平台部实际研发与测试工作中我们发现,代码插桩隔离是单元测试工作中的一个强需求,然而业界现有 C/C++插桩工具由于使用上的局限性,运行效率和体验仍有很大改善空间。本文介绍了团队基于研效优化实践而自研的动态插桩工具,旨在实现单元测试的轻量化运行,提高代码覆盖率,从而助力研发团队的效能提升。

问题&思路

目前存在的 C/C++插桩工具,基本上都有各种使用上的局限,比如流行的 gmock,只能对 C++的虚函数进行插桩替换,针对非虚函数,则需要先对被测代码进行改造;同时对于系统接口,C 风格的第三方库代码,也无能为力。

如果可以绕开编译器,直接从底层入手,比如做机器指令修改,则可以不受语法及编译器的束缚,直接达到目的,这样在使用中就 几乎不受限制

原理

C/C++语言编译后的可执行体,其实就是一个个的函数实现,每个函数的开头就是它的入口。一个函数 A 调用另一个函数 B,就是代码在执行过程中,控制流从函数 A 的某处跳到了函数 B 的开头,所以如果想用一个新的函数 C 取代函数 B,可以在函数 B 的开头用机器码的形式写入如下等价逻辑:

MOVQ ADDRESS_OF_C %RAX //将函数C的地址放到寄存器RAX
JMPQ *RAX           //无条件跳转到RAX所指向的位置

这样,当控制流从函数 A 进入函数 B 的开始位置的时候,即会执行上述代码,从而直接跳转到 C 的开头处。其最终效果,是所有对函数 B 的调用,都如同直接调用了函数 C。

基于上述原理,被插桩的代码包括第三方库,如 MySql、其他同事未完成的模块、甚至是操作系统的 API 接口,如 read、select 等

同时,桩函数不仅可以模拟原函数的返回值,实际上它作为一个普通的 C 函数,对原函数有完全的操作能力,比如可以访问传递给原函数调用真实的参数、C++成员变量(针对对成员函数的模拟),给定任意的返回值,访问全局变量、对调用进行计数等

实际实现中,考虑到不同测试用例间的互不干扰,除了能执行函数替换,还需要在执行完一个测试时还原现场。这些具体细节可以直接参考代码。

使用

对全局函数插桩

原始函数:

int global(int a, int b) {return a + b;
}

对应的桩函数:

int fake_global(int a, int b) {//校验参数正确性,确定被测代码传入了正确的值assert(a == 3);assert(b == 2);//给一个返回值,配合被测代码走特定分支return a - b;
}

插桩示例:


assert(global(3, 2) == 5);//通过mock调用,完成函数动态替换
assert(0 == mock(&global, &fake_global));//调用mock后的函数,可以看到返回值变了
assert(global(3, 2) == 1);//结束mock
reset();//函数行为恢复
assert(global(3, 2) == 5);

对普通成员函数插桩

被测代码:

class A {
public:int member(int a) {return ++a;}static int static_member(int a) {return 200;}virtual int virtual_member() {return 400;}
};

桩函数:

int fake_member(A *pTihs, int a) {//由于是对成员函数插桩,这里需要这个this指针参数return --a;
}

插桩示例:


A a;
assert(a.member(100) == 101);mock(&A::member, fake_member);
assert(a.member(100) == 99);reset();assert(a.member(100) == 101);

对静态成员函数插桩

桩函数:

int fake_static_member() {//静态函数不需要this指针return 300;
}

插桩示例:

assert(A::static_member(200) == 200);mock(&A::static_member, fake_static_member);
assert(A::static_member(100) == 300);reset();assert(A::static_member(200) == 200);

对虚函数插桩

桩函数:

int fake_virtual_member(A *pThis) {//虚函数同普通的成员函数由于,同样需要this指针return 500;
}

插桩示例:

A a;
assert(a.virtual_member() == 400);//虚函数mock需要多传一个相关类的对象,任意一个对象即可,跟实际代码中的对象没有关系
A a_obj;
mock(&A::virtual_member, fake_virtual_member, &a_obj);
assert(a.virtual_member() == 500);reset();
assert(a.virtual_member() == 400);

对系统及第三方库函数插桩

桩函数:

int fake_write(int, char*, int) {return 100;
}

插桩示例:

//直接写入一个无效的文件描述符,会失败
assert(write(5, "hello", 5) == -1);//来一个假的wirte
mock(write, fake_write);
//模拟调用成功
assert(write(5, "hello", 5) == 100);reset();assert(write(5, "hello", 5) == -1);

可以看到,对系统函数的 mock,其实跟普通的全局函数并无两样,第三方库函数也是同理。

使用限制&注意事项

  • 目前支持 X86_64 平台上的 Linux、MacOS 系统,如有需求,Windows 和其它硬件平台,如 X86_32、ARM,也可在短期内支持。

  • MacOS 下,需要在执行前对单测可执行文件做以下修改:

printf '\x07' | dd of=<ut_executable> bs=1 seek=160 count=1 conv=notrunc
  • 显然,这种方法对内联函数无效,不过对于单元测试来说,可以关闭内联,同时也建议关闭其它编译器优化。

  • 可以使用-fno-access-control 编译你的测试代码,可以使 g++关闭 c++成员的访问控制(即 protected 及 private 不再生效)。

项目地址

https://github.com/wangyongfeng5/lmock

结语

持续改进是研效工具平台发展的必经之路,欢迎感兴趣的同学与我们交流探讨,共同助力测试效能的优化。

效能优化实践:C/C++单元测试万能插桩工具相关推荐

  1. 工程师必备:C/C 单元测试万能插桩工具

    研发效能是一个涉及面很广的话题,它涵盖了软件交付的整个生命周期,涉及产品.架构.开发.测试.运维,每个环节都可能影响顺畅.高质量地持续有效交付.在腾讯安全平台部实际研发与测试工作中我们发现,代码插桩隔 ...

  2. 【字节码插桩】AOP 技术 ( “字节码插桩“ 技术简介 | AspectJ 插桩工具 | ASM 插桩工具 )

    文章目录 一." 字节码插桩 " 技术简介 二.AspectJ 插桩工具 三.ASM 插桩工具 一." 字节码插桩 " 技术简介 性能优化 , 插件化 , 热修 ...

  3. java 插桩 工具_一个基于Eclipse的通用Java程序插桩工具.pdf

    第38卷第7期 计算机科学 V01.38NO.7 Science 2011 2011年7月 Computer July 一个基于Eclipse的通用Java程序插桩工具 郑晓梅 (南京中医药大学信息技 ...

  4. linux静态插桩工具PEBIL

    文章目录 引言 论文学习 摘要及简介 设计与实现 插桩代码效率 实验结果及其他 具体使用 引言 PEBIL是San Diego Supercomputer Center某实验室研发的工具,用来对ELF ...

  5. 网页算法动态插桩工具(Js逆向)

    网页算法动态插桩工具(Js逆向) 一. 功能 通过 anyproxy工具拦截浏览器的请求 并通过AST 获取相关加密字段的函数 进行插桩 输出相关的内容 目前 测试 通过 的算法 有 AES DES ...

  6. android 插桩工具,Android Asm 插桩 教学项目

    AndroidAutoTrack 本项目主要就是给大家一个参考学习的demo而已,主要是打算简化学习gradle插件的成本,以及对于android transform的一次抽象,将增量更新等等进行一次 ...

  7. intel 插桩工具 pin 介绍

    目录 Pin 介绍

  8. 研效优化实践:Python单测——从入门到起飞

    作者:uniquewang,腾讯安全平台后台开发工程师 福生于微,积微成著,一行代码的精心调试,一条指令的细心验证,一个字节的研磨优化,都是影响企业研发效能工程的细节因素.而单元测试,是指针对软件中的 ...

  9. 吹爆系列:Android 插桩之美,全面掌握~

    作者:阿明的小蝴蝶 插桩 插桩是什么?你在开发中有用过插桩的技术吗? 所谓的插桩就是在代码编译期间修改已有的代码或者生成新代码. 插桩具体在编译的哪个流程介入呢? 插桩的作用与场景 代码生成 代码监控 ...

最新文章

  1. Windows RDP协议 Fuzzing 漏洞挖掘研究
  2. oracle中where中使用函数,Oracle 尽量避免在 SQL语句的WHERE子句中使用函数
  3. 红帽资深解决方案架构师魏新宇:云原生应用构建之路
  4. 【高危漏洞通告】Spring Framework 远程代码执行 (CVE-2022-22965)
  5. 让input支持 ctrl v上传粘贴图片? 让input支持QQ截图或剪切板中的图像数据(Java实现保存)...
  6. 【英语学习】【医学】Unit 03 Blood
  7. ssas计算度量_如何在Analysis Services(SSAS)中创建中间度量
  8. linux错误—2.man:command not found
  9. Awaken for Mac闹钟定时器
  10. 单片机c语言延时1ms函数,单片机c语言延时函数用int与char有延时差吗?
  11. 什么是登录Token
  12. 鸿蒙分布式内核,鸿蒙系统:微内核,分布式
  13. 直连路由和静态路由(实验)
  14. java 常量定义_java常量的定义
  15. 毕业一年小结——说好的战斗呢?
  16. php文章下一页,php获取文章上一页与下一页的方法,_PHP教程
  17. 针对顽固dll后缀文件删除
  18. Windows系统盘搬家方法及Junction使用
  19. HDU 4416 后缀自动机
  20. 设计模式(10)[JS版]-JavaScript如何实现组合模式???

热门文章

  1. Nginx配置之基于域名的虚拟主机
  2. 用BlazeMeter录制JMeter测试脚本
  3. bash shell基础之三字符串测试及for循环
  4. Linux学习笔记033_8文本处理
  5. 将jar文件转换成exe可执行文件[转]
  6. harris角点检测的学习
  7. router3 BGP1 基础部分
  8. CodeForces - 1557D Ezzat and Grid(线段树+dp)
  9. HDU - 3551 Hard Problem(一般图最大匹配)
  10. HDU - 3613 Best Reward(字符串哈希)