关于追踪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 ...
随机推荐
- IDEA将Maven项目中src源代码下的xml配置文件编译进classes
遇到这样的情况,maven项目启动报错,src中某个包下面的xml文件找不到. eclipse编译项目会自动将xml配置文件编译进classes,IDEA却不行 在报错项目的pom.xml文件中添加: ...
- php面向对象(设计模式 工厂模式)
//设计模式//单例模式//类的计划生育//让该类在外界无法造成对象//让外界可以造一个对象,做一个静态方法返回对象//在累里面可以通过静态变量控制返回对象只能有一个 //class Cat//{// ...
- POJ-1797Heavy Transportation,最短路变形,用dijkstra稍加修改就可以了;
Heavy Transportation Time Limit: 3000MS Memory Limit: 30000K Description Background Hugo ...
- BGP表
BGP是一种基于策略的路由选择协议,让AS能够根据多种BGP属性来控制数据流的传输.运行BGP的路由器交换被称为路径矢量或者属性的NLRI.路径矢量信息中包含一个BGP-AS号列表称为AS-PATH属 ...
- 【BFS+优先级队列】Rescue
https://www.bnuoj.com/v3/contest_show.php?cid=9154#problem/I [题意] 给定一个n*m的迷宫,A的多个小伙伴R要去营救A,问需要时间最少的小 ...
- 外星联络(bzoj 2251)
Description 小 P 在看过电影<超时空接触>(Contact)之后被深深的打动,决心致力于寻找外星人的事业.于是,他每天晚上都爬在屋顶上试图用自己的收音机收听外星人发来的信息. ...
- 【HDOJ6318】Swaps and Inversions(树状数组)
题意: 给定一串数组,其中含有一个逆序对则需要花费x,交换相邻两个数需要花费y,输出最小花费. n<=1e5,-1e9<=a[i]<=1e9 思路: #include<cstd ...
- BZOJ1583: [Usaco2009 Mar]Moon Mooing 哞哞叫
给n<=4000000,c,a1,b1,c1,a2,b2,c2,以c为初始得到的数,每次可以把得到的某个数x进行操作f1(x)=a1*x/c1+b1,f2(x)=a2*x/c2+b2,求最后能得 ...
- 转 蓝桥杯 历届试题 波动数列 [ dp ]
传送门 历届试题 波动数列 时间限制:1.0s 内存限制:256.0MB 锦囊1 锦囊2 锦囊3 问题描述 观察这个数列: 1 3 0 2 -1 1 -2 ... 这个 ...
- ubuntu 配置 samba, win7 map network device from linux
一. samba的安装: # sudo apt-get insall samba # sudo apt-get install smbfs 二. 创建共享目录,或是找已经存在的文件夹,只要权限放开就行 ...