自从写第一个guru模式的stap脚本,我就再也不写内核模块了。stap简直太方便了,竟然可以将C语言当脚本语言来玩。

随着我写的stap脚本越来越复杂,我需要使用一些半吊子设计模式来让代码更好看些,比如我不会再将所有东西写在一个stap脚本里,我会将一些公共的结构体定义,宏定义写在一个头文件里:

// common.h
#define VAR 100

然后在我的stap脚本里引用它们就是了:

#!/usr/local/bin/stap -g
// aa.stp
#!/usr/local/bin/stap -g%{#include "common.h"
%}function func(who:long)
%{STAP_PRINTF("%d   %lu\n", VAR, STAP_ARG_who);
%}probe begin
{func($1);exit();
}

然而不行。会报错:

[root@localhost test]# ./aa.stp 1
/tmp/stapxVC8YT/stap_c5a511d01be2d078445a0de2c20c8a7f_1308_src.c:29:20: 致命错误:common.h:没有那个文件或目录#include "common.h"^
编译中断。
make[1]: *** [/tmp/stapxVC8YT/stap_c5a511d01be2d078445a0de2c20c8a7f_1308_src.o] 错误 1
make: *** [_module_/tmp/stapxVC8YT] 错误 2
WARNING: kbuild exited with status: 2
Pass 4: compilation failed.  [man error::pass4]

明明common.h就在当前目录,却没有找到。遇到这种问题,应该如何解决呢?

其实,了解stap原理的都会明白怎么回事,只需要用-vvv导出详细日志,看一下make参数即可:

[root@localhost test]# ./aa.stp 1 -vvv >./log 2>&1
[root@localhost test]# grep -rn 'make -C' ./log
521:Running env -uARCH -uKBUILD_EXTMOD -uCROSS_COMPILE -uKBUILD_IMAGE -uKCONFIG_CONFIG -uINSTALL_PATH -uLD_LIBRARY_PATH PATH=/usr/bin:/bin:/root/.cargo/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin:/bin:/sbin make -C /lib/modules/3.10.0-327.x86_64/build M=/tmp/stapmjgFbY modules CONFIG_DEBUG_INFO= CONFIG_STACK_VALIDATION= CONFIG_MODVERSIONS= ARCH=x86_64 V=1 -j2

你看,你看,include目录根本没有包含当前目录嘛。

如果想包含当前目录,需要在Makefile中增加以下选项:

EXTRA_CFLAGS += -I$(pwd)

然而如何去做到呢?

嗯,修改stap源码是最直接的,直接找到生成Makefile的逻辑的地方,照猫画虎将以上字符串padding到Makefile末尾即可。在完成手艺之后,我特意尝试了这种重新编译的方法,确实简单。

grep EXTRA_CFLAGS找到buildrun.cxx文件的compile_pass函数,增加:

string module_cflags = "EXTRA_CFLAGS";
o << module_cflags << " :=" << endl;
// 以下为新增行
o << module_cflags << "EXTRA_CFLAGS += -I$(pwd)" << endl;

然后重新编译。

但这不是手艺人的做法!手艺人需要不修改代码,不重新编译完成此事。

咬尾蛇玩多了,思路就是这么简单, 让stap自己tap自己呗!

使用stap将被运行的stap的Makefile命令生成函数hook住,然后在命令生成之前往Makefile中padding上面的字符串。

为了不依赖源代码找出需要hook的函数,只能靠猜测:

stap -L 'process("/usr/local/bin/stap").function("*make*")'

以上命令会导出一个列表,依赖作者函数命名的清晰程度,我一个一个连猜带试折腾了一个小时,终于确定了下面的函数:

process("/usr/local/bin/stap").function("make_any_make_cmd@/usr/test/systemtap/buildrun.cxx:96") $s:struct systemtap_session& $dir:string const& $target:string const& $newpath:string $make_cmd:class vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > >

我一开始用的是strings /usr/local/bin/stap|grep make,而不是用的stap -L。

找到了目标函数,我通过printf还知道其dir参数就是Makefile的目录,接下来让我们开始吧:

#!/usr/local/bin/stap -gfunction padding(dir:string)
%{char mf[64];// 增加一个新的环境变量,以引用任意地方的头文件char *ext = "EXTRA_CFLAGS += -I$(STAP_INCLUDE)\n";struct file *fp;mm_segment_t old_fs;loff_t pos;snprintf(mf, 64, "%s/Makefile", (char *)STAP_ARG_dir);// 在内核空间将EXTRA_CFLAGS追加到Makefile的最后fp = filp_open(mf, O_RDWR | O_APPEND, 0644);old_fs = get_fs();set_fs(KERNEL_DS);pos = fp->f_pos;vfs_write(fp, ext, strlen(ext), &pos);fp->f_pos = pos;set_fs(old_fs);filp_close(fp, NULL);
%}probe process("/usr/local/bin/stap").function("make_any_make_cmd")
{// 不懂C++,我是通过printf("%s\n", $dir$);找到_M_local_buf成员变量的。padding(user_string($dir->_M_local_buf));
}

来来来,看效果:

[root@localhost test]# export STAP_INCLUDE=`pwd`
[root@localhost test]# echo $STAP_INCLUDE
/usr/test
[root@localhost test]# ./selftap -c  '/usr/local/bin/stap -g ./aa.stp 3'
100   3

事情就是这样。

值得一提的是,只要你的aa.stp脚本不发生变化,且其运行参数不发生变化,当你后续再执行aa.stp的时候,就可以直接执行了,无需再selftap:

[root@localhost test]# ./aa.stp 3
100   3

然而一旦脚本本身或者参数发生了变化,就必须再重新selftap一次:

[root@localhost test]# ./aa.stp 3
100   3
[root@localhost test]# ./aa.stp 5
/tmp/stapLTrZIV/stap_4e39e7642313b2bf2a6d9120e03487ab_1308_src.c:29:20: 致命错误:common.h:没有那个文件或目录#include "common.h"^
编译中断。
make[1]: *** [/tmp/stapLTrZIV/stap_4e39e7642313b2bf2a6d9120e03487ab_1308_src.o] 错误 1
make: *** [_module_/tmp/stapLTrZIV] 错误 2
WARNING: kbuild exited with status: 2
Pass 4: compilation failed.  [man error::pass4]
[root@localhost test]# ./selftap -c  '/usr/local/bin/stap -g ./aa.stp 5'
100   5
[root@localhost test]# ./aa.stp 5
100   5

这是stap的cache机制在起作用,上一次操作的一切都cache在~/.systemtap/cache目录下。

其实,这篇文章展示的手艺旨在表达一种感情,只要理解了一个软件的运行原理,你可以肆意折腾它,在不改变源码的情况下改变其行为。

理解了stap其实是通过编译成内核模块完成工作的大前提之后,就可以hook住其生成编译命令的过程,在Makefile里任意存在命令,达到一些特殊的目的。


浙江温州皮鞋湿,下雨进水不会胖。

systemtap引用自定义头文件的手艺相关推荐

  1. gcc编译自定义头文件

    2019独角兽企业重金招聘Python工程师标准>>> C中外部函数实例 内部函数:static声明,只对本文件域生效,外部不可引用 外部函数,使用extern声明,默认可以去掉,在 ...

  2. C语言中include““与include<>的区别(自定义头文件、预设头文件)

    文章目录 新建控制台应用程序 自定义头文件 编辑头文件 工程内引用头文件,调用函数 标准库头文件,调用函数 新建控制台应用程序 Win32 Application和Win32 Console Appl ...

  3. c++语言程序设计——头文件和引用系统头文件、用户头文件的定义及使用方法

    文章目录 头文件 引用头文件 1.引用系统头文件 2.引用用户头文件 实例 注意 结语 头文件 头文件是拓展名为.h的文件,其包含了函数的声明和宏定义,它可以被多个源文件引用共享.头文件分为两种类型: ...

  4. vue引用自定义.js文件 (常量抛出 + 地址三级联动为例)- 语法篇

    文章目录 vue如何引用`自定义封装的` `.js`文件? `如何操作,详细如下.`(注意代码注释部分:注意1/2/3/4/5) 一.效果图预览: 二.以自定义`area.min.js`文件为例: 三 ...

  5. vue引用自定义.css文件 - 语法篇

    vue如何引用外部自定义的.css文件 ? 一般都会因为存在权重或优先级设置的问题才会侧意引用: [详情进入查看:如何在scoped不污染组件样式的前提下,实现el-input组件样式覆盖?] 再者, ...

  6. VS 2022 C++ 自定义头文件示例

    前言: 博主最近刚从VS Code转到VS 2022,但发现自定义的方法和VS Code有些不同,故出一期VS 2022自定义头文件的博客,时间仓促,请不吝赐教 如何联系我?wei.haoran@ou ...

  7. Visual Studio 2022 自定义头文件源文件切换快捷键

    Visual Studio 2022 自定义头文件源文件切换快捷键 修改步骤 注意事项 修改步骤 废话不多说,直接上流程. (1)工具 -> 选项 -> 环境 -> 键盘 -> ...

  8. C语言如何自定义头文件——一看就废!!!

    为什么要自定义头文件? 如果在一个文件中,写上成百上千行的代码,那么这些代码让人阅读起来是真的烦.因此,我们可以引入头文件,把自己写的函数放入头文件中,然后直接调用到主程序中,这样在主程序中看起来就比 ...

  9. 在QT中自定义头文件和源文件的使用方法

    在QT中自定义头文件和源文件的使用方法 最近想用QT来实现一个简单的功能,为了便于函数的集成需要将功能函数进行封装,自己补了些c++的函数封装方法,发现在QT中还不太一样.接来下简单介绍一下具体怎么实 ...

最新文章

  1. c语言子程序return,c语言return返回到哪
  2. 试试这个文字冒险游戏,故事是AI写的:情节丰满逻辑不乱,进去就出不来了,在线可玩...
  3. 一颗椰子糖机器人_孩子编程启蒙机器人玩了不下10个,最推荐哪个呢?
  4. Batch Normalization的作用及原理
  5. MyEclipse JAVA提示信息配置
  6. 协程(Coroutine)与多线程,多进程
  7. 基于FPGA实现压缩算法
  8. html怎么隐藏y方向内容,如何隐藏scroll-Y纵向滚动条,并不影响内容滚动的方法...
  9. 我从别人那里偷学的前端调试小技巧(浏览器篇)
  10. 编译并刷入nexus 6p手机
  11. 计算机绘图第二章,机械制图电子教桉-02第二章+计算机绘图..ppt
  12. “FCoE全解系列”之网络融合交换机类型
  13. win10u盘被写保护怎么解除_磁盘被写保护怎么解除,小编告诉你如何解决U盘磁盘被写保护...
  14. 【Python神器】推荐这款傻瓜式GIF制作工具,以后别再说不会了(好用到爆~)
  15. python—武汉市2021年新房数据分析
  16. uboot中展示gpio接口的驱动
  17. Leetcode-93. 复原 IP 地址
  18. QUIC 技术创新 让视频和图片分发再提速
  19. VUE3 使用 Ant Design Vue的icon图标
  20. Linux驱动——mmc概念与框架(一)

热门文章

  1. 13.在一个数组中,每一个数左边比当前数小的数累加起来,叫做这个数组的小和。求一个数组的小和。(左神算法基础班源码)
  2. SWA(随机权重平均)——一种全新的模型优化方法
  3. 手把手教你搭建视频去重系统
  4. el-upload 多文件 上传 只执行一次成功回调on-success的问题
  5. 从Spy Mouse看App Store的推广方法
  6. vb发出声音音乐代码
  7. C++实现生产者和消费者模型
  8. RabbitMQ图文详解 | MQ_SpringAMQP | 系统性学习 | 无知的我费曼笔记
  9. 三角函数反三角函数乘 [cos(arcsinx)]^2=1-x^2 [sin(arccosx)]^2=1-x^2 sinarctanx=sint=x/√1+x² cosarctanx=1/√1
  10. android的环境搭建