本文由 @lonelyrains 出品,转载请注明出处。
文章链接: http://blog.csdn.net/lonelyrains/article/details/7719614
调试程序时,经常需要查看程序的函数调用的流动方向。在PC上很简单,可以F10、F11单步调试查看或者gdb命令行查看。不过实际调试发现,在处理多重继承和虚函数的调用时,vc-express-2008的调试器单步并不能很好的发挥效用【目前还未单独针对这个结论做样例,以后再补上】。而在嵌入式设备上却很难实现单步调试。打印日志就成了很有用的调试方式。然而,在比较庞大的程序里,所有的函数头手动添加调试语句不太现实【自己一字一字码起来的除外,原因大家都懂的^_^】。所以想到了写个小工具,专门在所有的函数头添加调试语句。
废话少说,上代码:
#include <iostream>
#include <vector>
using namespace std;
/*******************************************************
功能:对输入的.h、.cpp文件的所有函数头增加打印
条件:考察的代码文件能正常编译通过,本程序不进行文法、语义检查
步骤:
1、读入文件[简化成申请合适空间一次性读入]
2、找到函数头[简化成不考虑有额外的宏定义的函数头]
①找到所有的不在单引号或者双引号之内的'{'
②排除'{'前导[非空和换行的字符]为if()、for()、while()、switch()
③排除'{'前导不是')' [不考虑 static / const 修饰函数]
③其余均为函数头
3、在'{'后添加打印:[回车换行]Debug_Func("tagxxx---func:%s,line:%d,file:%s",__FUNCTION__,__LINE__,__FILE__);[回车换行]
*******************************************************/
#define SWITCH_LENGTH 6
#define FOR_LENGTH 3
#define WHILE_LENGTH 5
#define IF_LENGTH 2
#define BUF_SIZE 100000
#define PATH_SIZE 300
#define DBG_LENGTH 20
const char *debugSentence = "\nDebug_Func(\"tagxxx---func:%s,line:%d,file:%s\\n\",__FUNCTION__,__LINE__,__FILE__);" ;
vector<int> debugPlace;//记录每次有效{的位置
void readfile(const char *filename, char *buf)
{
FILE* fp = fopen(filename,"rb"); //改成rb之后正常了...奇怪,明明是txt文件,fread内容不对,多出一段。这个地方很典型。可能c++的文件读写有更好的方式,习惯了c的方式了
if(!fp)
{
printf("error:no such file");
exit(-1);
}
fflush(fp);
int n = fread(buf,sizeof(char),BUF_SIZE,fp);
fclose(fp);
}
void writefile(const char *filename,char *buf)
{
FILE* fp = fopen(filename,"wb"); //改成wb之后正常了...不然会在每个行尾多回车换行写入
if(!fp)
{
printf("error:no such file");
exit(-1);
}
fwrite(buf,sizeof(char),strlen(buf),fp);
fclose(fp);
}
bool printable(char ch)
{
if(ch >= 'a' && ch <= 'z')
return true;
if(ch >= 'A' && ch <= 'Z')
return true;
if(ch >= '0' && ch <= '9')
return true;
if(ch == ')' || ch == ';')
return true;
return false;
}
void analy(char *buf)
{
bool sinQuoteFlag = false, douQuoteFlag = false;
bool valid = false;
int line = 0;
char chtmp = 0x00;
int rightSide = 0;
int placeMark[3] = {-1, -1, -1};//标记位置:分别')'、'('、'('左边第一个可打印字符的位置
char dbgstring[DBG_LENGTH]="";
//外层循环,遍历所有字符
for(int i=2; i < BUF_SIZE; i++)
{
chtmp = buf[i];
if(buf[i] == '\n')
line++;
//排除单引号和双引号之内的'{',不考虑\'转义的情况和在注释中的情况。然而要能实际中使用,这点无法避免,需要借助编译器的源码。
if(buf[i] == '\'')
sinQuoteFlag = !sinQuoteFlag;
if(buf[i] == '\"')
douQuoteFlag = !douQuoteFlag;
if(sinQuoteFlag || douQuoteFlag)
continue;
if(buf[i] == '{')
{
//调试语句
for(int k=0;k < DBG_LENGTH-1; k++)
{
dbgstring[k]=buf[i+k];
}
printf("%d-----%s-----------^",i,dbgstring);
//调试语句
//debugPlace.push_back(i+1);
valid = true;
placeMark[0] = -1;
placeMark[1] = -1;
placeMark[2] = -1;
//回溯排除前导非')'
for(int j=i-1; j>=0; j--)
{
if( !printable(buf[j]) )
continue;
//')'的位置
placeMark[0] = j;
break;
}
//未找到,则跳过本次'{'的考察
if(placeMark[0] < 1 || buf[placeMark[0]] != ')')
continue;
else
{
//记载多余右括号的数目以方便找到最外层的左括号,回溯考察字符串,遇到一次左括号则减一
rightSide = 1;
//排除是for/while/do/switch/关键字
for(int k=placeMark[0]-1; k>=1; k--)
{
//')'前出现单双引号直接认为不是函数头,不考虑有注释的情况
if( buf[k] == '\'' || buf[k] == '\"')
{
valid = false;
break;
}
if( buf[k] == ')' )
rightSide++;
if( buf[k] == '(' )
rightSide--;
if( rightSide)
continue;
//'('的位置
placeMark[1] = k;
break;
}
}
if(placeMark[1]<=1)
continue;
//回溯排除前导非printable
for(int l=placeMark[1]-1; l>=0; l--)
{
if( !printable(buf[l]) )
continue;
//'('左边的第一个可打印字符的位置
placeMark[2] = l;
break;
}
if(placeMark[2] < 0)
continue;
if(placeMark[2] >= IF_LENGTH)
{
if(buf[placeMark[2]] == 'f' && buf[placeMark[2]-1] == 'i')
{
if(!printable(buf[placeMark[2]-2]))
{
continue;
}
}
}
if(placeMark[2] >= FOR_LENGTH)
{
if(buf[placeMark[2]] == 'r' && buf[placeMark[2]-1] == 'o' && buf[placeMark[2]-2] == 'f')
{
if(!printable(buf[placeMark[2]-3]))
{
continue;
}
}
}
if(placeMark[2] >= WHILE_LENGTH)
{
if( buf[placeMark[2]] == 'e' && buf[placeMark[2]-1] == 'l' &&
buf[placeMark[2]-2] == 'i' && buf[placeMark[2]-3] == 'h' &&
buf[placeMark[2]-4] == 'w' )
{
if( !printable(buf[placeMark[2]-5]) )
{
continue;
}
}
}
if(placeMark[2] >= SWITCH_LENGTH)
{
if( buf[placeMark[2]] == 'h' && buf[placeMark[2]-1] == 'c' &&
buf[placeMark[2]-2] == 't' && buf[placeMark[2]-3] == 'i' &&
buf[placeMark[2]-4] == 'w' && buf[placeMark[2]-5] == 's' )
{
if(!printable(buf[placeMark[2]-6]))
{
continue;
}
}
}
if(valid)
{
debugPlace.push_back(i+1);//换算成从1开始的索引值,也就是到'{'的字符串的长度,包括'{'
}
}//if(buf[i] == '{')
}//for(int i=0; i<sizeof(buf); i++)
}
void edit(char *buf)
{
if(!debugPlace.size())
return;
char tmp[BUF_SIZE];
memset(tmp,0,sizeof(tmp));
int segLength = 0;
memcpy( tmp, buf, debugPlace[0]);
memcpy( tmp + debugPlace[0], debugSentence, strlen(debugSentence));
for(unsigned int i=1;i<debugPlace.size();i++)
{
segLength = debugPlace[i] - debugPlace[i-1] ;
//拷贝片段
memcpy( tmp + i * strlen(debugSentence) + debugPlace[i-1] , buf + debugPlace[i-1] , segLength);
//拷贝调试语句
memcpy( tmp + i * strlen(debugSentence) + debugPlace[i-1] + segLength , debugSentence, strlen(debugSentence));
//最后一个调试语句插入之后的,拷贝之后的片段
if(i == debugPlace.size() - 1)
{
memcpy(tmp + (i + 1) * strlen(debugSentence) + debugPlace[i] , buf + debugPlace[i], strlen(buf) - debugPlace[i]);
}
}
memcpy(buf,tmp,strlen(tmp));
}
void main(int argc,char *argv[])
{
if(argc < 2)
{
printf("The cmd format is : \n\
dbghead filename1 filename2 filename3 ....");
return;
}
char buf[BUF_SIZE];
for(int i=1;i<argc;i++)
{
memset(buf,0,sizeof(buf));
readfile(argv[i],buf);
analy(buf);
edit(buf);
writefile(argv[i],buf);
}
}