调试程序时,经常需要查看程序的函数调用的流动方向。在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); } }