关于追踪qemu 源码函数路径的一个方法
这阵子一直在研究qemu 磁盘io路径的源码,发现直接看代码是意见非常低效率的事情,qemu是一个比较庞大的家伙(源码部分大概154MB,完全由C语言来完成),整个结构也都非常地复杂,所以从代码上研究qemu最好的办法只有debug之。不断地收集更多的debug信息去获取源码所蕴含的道理。
很多人第一反应可能就是使用一些类似与Eclipse, gdb 这一类强大的debugger,我当然也不例外,在经过一个上午究竟该使用Eclipse还是gdb的思想斗争的私人情绪之后,我才恍然明白,原来我两个工具都不会用啊!! (大雾大雾

经过老大的前车之鉴的提醒之后,他说他以前弄Xen的时候使用gdb调试Xen的效果也是不太理想,并且由于我们使用的实验环境一直都是没有Xwindows的Centos7-miminal,所以使用Eclipse更是一种煎熬,他们以前是使用输出调试信息产生函数调用的日志来进行函数追踪和debug的,这或许真的是一种非常原生态,思路很简洁的方法,有时候最有效的或许就是最简单粗暴的方法了吧?
具体的调试方法我没有再多过问了,我想自己去尝试一下,于是便开始了自己的胡思乱想的debug方法构思。
一开始,我很理所当然地想到了 printf 函数,可是这个函数在单个源码文件的程序里面是完全可行简单的,一运行程序,便能够在你所允许的shell里面打印出调试信息出来。然后才发现,使用prtintf真是naive 啊naive!!当我建这个思路用在一个需要读取文件的多源码文件里面的时候发现就不行了,C语言强大的地方便在与世界上最复杂的软件系统几乎只能用C语言来完成,可是当我们需要满足日常使用的时候使用C语言便觉得有点杀鸡用牛刀了,远远比不上shell script 以及Python一类的脚本语言了。
好吧,之前老大提到了说做一个类似与输出函数调用日志文件的东西,既然如此,为了方便我们观察输出的日志消息,我们需要将函数调用的日志消息输出到一个文件里面,ok,我们现在来分析一下这个日志文件究竟需要具备什么样子的功能?借鉴了MySQL的输出日志文件的特点,总结出了以下几条
1.每一条的日志输出都需要带有时间戳的信息,包含年份,日,月, 时,分,秒。
2.每一条的日志输出都一定要带有所嗲用函数的精良精确的信息 file_name-fun1-fun2-fun3,其信息代表了 fun3被fun2调用 fun3 被fun1调用 fun1包含在file_name文件 里面。
3.每一条的日志输出具备所追踪的函数所携带的数据信息,如 数据量,数据值等等..
这样子经过好几次的修改和调试之后我就写出了以下的printf_debug 函数了:
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <time.h>
#include <string.h>
/*----------------DEBUG_FUNCTION--------------*/
void printf_debug(char *Path, char* functionName,
int NeedData[])
{
struct tm* p;
time_t timep;
time(&timep);
p = gmtime(&timep);
char s[] = "";
int fd = open(Path,O_RDWR | O_CREAT | O_APPEND,
S_IRUSR | S_IWUSR );
sprintf(s+strlen(s), "%s %d/%d/%d/ %d:%d:%d \n",
functionName,(+p->tm_year),(+p->tm_mon),p->tm_mday,
p->tm_hour, p->tm_min, p->tm_sec);
sprintf(s+strlen(s), "CONFIG_LINUX_AIO is %d \n use_aio is %d \n",NeedData[],NeedData[]);
write(fd,s,sizeof(s));
close(fd);
}
写到这里我已经迫不及待地将这个函数扔到qemu的源码里面进行调试了,老大一开始叫我追踪好几个函数,我就迫不及待地将这个函数的定义放到了制定的源码文件里面,哗啦啦地将这几个函数放在了追踪函数的前面。。。。。随之而来的是。。。。
老大时不时叫我关掉这个函数,独立开启另一个个函数,时不时叫我还原源码重新调整。。。。
卧槽卧槽卧槽卧槽!!!!,

那个源码文件将近3000行的代码,并且每个需要追踪的函数之间的间隔也比较大,每次需要屏蔽或者删除修改的时候都极度蛋疼,为了解决这个比较蛋疼的问题,我便开始重新构思这个debug函数的结构,由于我只能在vim这一类的命令行式的文本编辑器下面进行Coding和Reading,不方便进行可视化的快速复制和黏贴

所以每次需要进行debug函数的大规模的修改和删除的时候,最好能将操作区域集中在一块相对较小的区域里面进行,再次深度构思了一下之后,遂决定使用C语言里面的宏定义来满足我的需求,又胡思乱想地修改了之后,得到了如下的思路:
在这里我为printf_debug函数引入多了一个DebugAllow参数,如果DebugAllow为0的话就代表这个printf_debug被禁止掉了,
void printf_debug(char *Path, char* functionName,
int DebugAllow, int NeedData[])
{
if (DebugAllow == )
return ;
struct tm* p;
time_t timep;
time(&timep);
p = gmtime(&timep);
char s[] = "";
int fd = open(Path,O_RDWR | O_CREAT | O_APPEND,
S_IRUSR | S_IWUSR );
sprintf(s+strlen(s), "%s %d/%d/%d/ %d:%d:%d \n",
functionName,(+p->tm_year),(+p->tm_mon),p->tm_mday,
p->tm_hour, p->tm_min, p->tm_sec);
sprintf(s+strlen(s), "CONFIG_LINUX_AIO is %d \n use_aio is %d \n",NeedData[],NeedData[]);
write(fd,s,sizeof(s));
close(fd);
}
然后我再为每个所需要追踪的函数单独定义了一个宏:
#define ALLOW_RAW_OPEN 1
#define ALLOW_RAW_REOPEN_PREPARE 1
#define ALLOW_HANDLE_AIOCB_RW_VECTOR 1
#define ALLOW_HANDLE_AIOCB_RW_LINEAR 1
#define ALLOW_LAIO_SUBMIT 1
#define ALLOW_PAIO_SUBMIT 1
我们只需要将这些宏插入到对应的追踪函数的printf_debug里的DebugAllow即可
比如我们需要追踪如下的函数
return paio_submit(bs, s->fd, sector_num, qiov, nb_sectors,
cb, opaque, type);
只需在前面添加对应的printf_debug函数:
printf_debug(PATH_PAIO_SUBMIT , "raw-posix.c-raw_aio_submit-paio_submit",
ALLOW_PAIO_SUBMIT, );
return paio_submit(bs, s->fd, sector_num, qiov, nb_sectors,
cb, opaque, type);
当我们需要屏蔽掉paio_submit的printf_debug函数的时候,只要在前面的宏定义里面的 ALLOW_PAIO_SUBMIT设置为0即可。当需要修改多个printf_debug函数的屏蔽与否时,只需要集中在前面所定义的宏的代码块里面操作就可以了。这样就可以将操作范围从3000行缩短到6行了。
当我们需要集中地清楚掉所有的debug函数的时候,我们不妨在定义多一个宏
#define DEBUG_QEMU_IO_MODE
我们可以利用这个宏来一次性地掌控所有的printf_debug函数的存在,比如
#ifdef DEBUG_QEMU_IO_MODE
printf_debug(PATH_PAIO_SUBMIT , "raw-posix.c-raw_aio_submit-paio_submit",
ALLOW_PAIO_SUBMIT, needdata);
#endif
return paio_submit(bs, s->fd, sector_num, qiov, nb_sectors,
cb, opaque, type);
当我们需要清除掉所有的printf_debug函数的时候,只需除掉一开始的对于DEBUG_QEMU_IO_MODE的定义即可。
以上便是今天所用到的所有用来调试追踪qemu磁盘io源码的方案了,下面便是所用的所有源码
/*---------------------------------*/
/*------DEBUG_QEMU_IO_MODE---------*/ #define DEBUG_QEMU_IO_MODE /*---open or close the debug mode*/ #ifdef DEBUG_QEMU_IO_MODE #define ALLOW_RAW_OPEN 1
#define ALLOW_RAW_REOPEN_PREPARE 1
#define ALLOW_HANDLE_AIOCB_RW_VECTOR 1
#define ALLOW_HANDLE_AIOCB_RW_LINEAR 1
#define ALLOW_LAIO_SUBMIT 1
#define ALLOW_PAIO_SUBMIT 1 char *PATH_RAW_REOPEN_PREPARE ="/tmp/raw_reopen_prepare.log";
#define PATH_HANDLE_AIOCB_RW_VECTOR
#define PATH_RAW_OPEN
#define PATH_HANDLE_ATIOCB_RW_RW_LINEAR
char *PATH_LAIO_SUBMIT = "/tmp/laio-submit.log";
char *PATH_PAIO_SUBMIT = "/tmp/paio-submit.log";
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <time.h>
#include <string.h>
/*----------------DEBUG_FUNCTION--------------*/
void printf_debug(char *Path, char* functionName,
int DebugAllow, int NeedData[])
{
if (DebugAllow == )
return ;
struct tm* p;
time_t timep;
time(&timep);
p = gmtime(&timep);
char s[] = "";
int fd = open(Path,O_RDWR | O_CREAT | O_APPEND,
S_IRUSR | S_IWUSR );
sprintf(s+strlen(s), "%s %d/%d/%d/ %d:%d:%d \n",
functionName,(+p->tm_year),(+p->tm_mon),p->tm_mday,
p->tm_hour, p->tm_min, p->tm_sec);
sprintf(s+strlen(s), "CONFIG_LINUX_AIO is %d \n use_aio is %d \n",NeedData[],NeedData[]);
write(fd,s,sizeof(s));
close(fd);
}
/*----------------------------------------*/
/*----------------------------------------*/
#endif
关于追踪qemu 源码函数路径的一个方法的更多相关文章
- GDB调试qemu源码纪录
今天介绍下如何利用gdb调试qemu 1.首先获取qemu源码 获取地址:https://www.qemu.org/ 2.编译安装qemu 进入qemu目录 ./configure --enable- ...
- qemu 源码调试
1:下载最新的QEMU源码 git clone https://github.com/qemu/qemu.git 2:对qemu进行编译 ./configure --prefix=/usr --lib ...
- 源码编译路径错误导致的Apache 无法重启问题解决方法
问题现象: 第一次源码编译安装Apache设置路径错误,安装到/usr/local/src/ 目录下了. 删掉该目录下的安装文件,重新编译安装到/usr/local/目录下 重启apache服务时报这 ...
- ubuntu下linux内核源码阅读工具和调试方法总结
http://blog.chinaunix.net/uid-20940095-id-66148.html 一 linux内核源码阅读工具 windows下当然首选source insight, 但是l ...
- 痞子衡嵌入式:MCUXpresso IDE下将源码制作成Lib库方法及其与IAR,MDK差异
大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家分享的是MCUXpresso IDE下将源码制作成Lib库方法及其与IAR,MDK差异. 程序函数库是一个包含已经编译好代码和数据的函数集合,这 ...
- 框架源码系列五:学习源码的方法(学习源码的目的、 学习源码的方法、Eclipse里面查看源码的常用快捷键和方法)
一. 学习源码的目的 1. 为了扩展和调优:掌握框架的工作流程和原理 2. 为了提升自己的编程技能:学习他人的设计思想.编程技巧 二. 学习源码的方法 方法一: 1)掌握研究的对象和研究对象的核心概念 ...
- 你与优秀源码之间只差一个 Star
fir.im Weekly - 你与优秀源码之间只差一个 Star 说起开源社区,Github 是一个不可缺少的存在.作为全球最大的同性交友网站,上面有太多优秀的开源代码库和编程大神,让无数开发者 ...
- Netty 源码剖析之 unSafe.write 方法
前言 在 Netty 源码剖析之 unSafe.read 方法 一文中,我们研究了 read 方法的实现,这是读取内容到容器,再看看 Netty 是如何将内容从容器输出 Channel 的吧. 1. ...
- 从源码看commit和commitAllowingStateLoss方法区别
Fragment介绍 在很久以前,也就是我刚开始写Android时(大约在2012年的冬天--),那时候如果要实现像下面微信一样的Tab切换页面,需要继承TabActivity,然后使用TabHost ...
随机推荐
- 75. Spring Boot 定制URL匹配规则【从零开始学Spring Boot】
在之前有一篇文章说了,博客名称从原来的<从零开始学Spring Boot>更改为<Spring Boot常见异常汇总>,后来写了几篇文章之后发展,有些文章还是一些知识点,所以后 ...
- 修改flex chart中Legend的字体样式
最近在弄FLEX的图表, 发现CHART 中的Legend 的字体通过直接设置Style 并没有办法改变字体大小. google 了下, 发现了这个方法: 通过派生LegendItem类,并设置Leg ...
- HDU 5521 Meeting【最短路】
今天旁观了Angry_Newbie的模拟区域赛(2015shenyang) 倒着看最先看的M题,很明显的最短路问题,在我看懂的时候他们已经开始敲B了. 后来听说D过了很多人.. D题一看是个博弈,给了 ...
- java基础语法4--封装,继承,多态
学习路线: 未封装==>封装==>继承==>多态==>抽象类 首先还是那句话,万物皆对象,对象有行为和属性. 一:封装 1.封装的概念: 信息隐蔽和对象的属性及操作结合成一个独 ...
- zookeeper客户端
查看具体结点信息 bash zkServer.sh status 查看哪个结点被选作leader或者followerecho stat|nc 127.0.0.1 2181 测试是否启动了该Server ...
- Meteor核心API
在本教程中,我们将介绍学习Meteor核心API. 如果你想限制代码只在服务器或客户端可以使用下面的代码运行 - meteorApp.js if (Meteor.isClient) { // Code ...
- 2003-07-16T01:24:32Z这是什么时间格式
这是标准的XML Schema的"日期型数据格式”. T是代表后面跟着“时间”.Z代表0时区,或者叫UTC统一时间. 世界的每个地区都有自己的本地时间,在Internet及无线电通信时,时间 ...
- Activity调用isDestroyed()方法报出,java.lang.NoSuchMethodError
在測试App的过程中,Activity调用了isDestroyed()方法,报出了java.lang.NoSuchMethodError错误. 自己手机MI 2S,版本号4.1.1. 事实上原因就是i ...
- MyBatis -- sql映射文件具体解释
MyBatis 真正的力量是在映射语句中. 和对等功能的jdbc来比价,映射文件节省非常多的代码量. MyBatis的构建就是聚焦于sql的. sql映射文件有例如以下几个顶级元素:(按顺序) cac ...
- API Copy Big FIles
public class ApiCopyFile { private const int FO_COPY = 0x0002; private const int FOF_ALLOWUNDO = 0x0 ...