gawk1.01源码分析——进行调试
因为疫情之故,天天在家。前段天天读小说,后来,觉得,还是读源码,至少让精力有地方发泄。于是,又重新读源码。
虽然很久没读,但发现,以前读过,现在再读,还是有种熟悉的感觉。但,发现光读代码,而代码跑不起来,没有感觉。还是要把代码编译,测试,再修改,再编译,执行后看修改是否产生的效果。因此,又捡起原来曾编译通过的代码。再开始。
./gawk --version
./gawk: Segmentation fault (core dumped)
我原来以为gawk1.01也能看版本号,报这个错误,因为我是在win10下,wsl弄个ubuntu18进行调试。不能生成那个dump文件,于是就让我放弃。我后来才发现,其实是因为gawk1.01没有实现--version参数处理,于是再改个最简单的程序。
./gawk 'BEGIN {print "haha"}'
haha
果然程序就能运行了。我进行修改awk1.c文件,在main中加一句,再
sudo make
./gawk 'BEGIN {print "haha"}'
就能看到我所修改的效果。
现在最困难的是不理解gawk生成的那个解析树
awk.h头文件中那个复杂的数据结构。于是我想,能否进行调试呢?于是再读代码。
在awk1.c中,有一段
for(;*argv && **argv=='-';argc--,argv++) {
576 switch(argv[0][1]) {
577 #ifndef FAST
578 case 'd':
579 debugging++;
580 dotree++;
581 break;
582
583 case 'D':
584 debugging++;
585 yydebug=2;
586 break;
587 #endif
我想,先测试一下-D先项,如下所示:
./gawk -D 'BEGIN {print "haha"}'
输入结果太长了。如下所示:
Starting parse
Entering state 0
Reducing stack 0 by rule 51 (line 264):
-> $$ = nterm optional_newlines ()
Entering state 2
看来,是把bison进行解析的过程打印出来了。
再测试-d先项。如下:
./gawk -d 'BEGIN {print "haha"}'
test001_yang
(0)0xfd34cc = left<--(0xfd34e4 Rule_list.35)
(1) 0xfd346c = left<--(0xfd34cc Rule_node.36)-->right = 0xfd34b4
(2) (0xfd346c BEGIN.40)
(2) 0xfd349c = left<--(0xfd34b4 State_list.37)
(3) 0xfd3484 = left<--(0xfd349c PRINT.48)
(4) 0xfd6440 = left<--(0xfd3484 Exp_list.39)
(5) [(0xfd6440 String "haha")]
Statements:16594076
PRINT:16594076
DATA:16606272
haha
这个结果较短了。
如果你仔细看,上面就是语法树。
34e4[Rule_list]
34cc[Rule_node]
346c 34b4[State_list]
BEGIN 349c
于是重点分析debug.c文件。
以前,没有仔细的读debug.c文件,现在为了理解,只能认真读。
这里重点介绍一个函数
print_parse_tree(ptr)
{
depth表深度。
根据ptr->type进行处理
如果是字串,直接打印
如果是数字,直接打印
如果是数组,直接打印
打印当前结点的左子树,当前结点,及右子树的地址
depth++
如果ptr->lnode
递归print_parse_tree(ptr->lnode)
根据ptr->type再进行处理
几种处理没看懂
默认情况,如果ptr->rnode不空,调print_parse_tree(ptr->rnode)
depth--
}
实际上就是先根遍历一棵二叉树的算法。
所以我接着想知道
'BEGIN {a=2+3; print
a}'的语法树,就又调试了一次。我想,如果能用图的方式显示就好了。
另外,告诉你一个秘密,我当时想,如果打印出某个结点,到处搜索,发现在awk.y中有一个函数,
#ifndef FAST
405 NODE *do_prvars(), *do_bp();
406 #endif
#ifndef FAST
434 {"prvars", Node_builtin, LEX_BUILTIN, do_prvars},
435 #endif
也就是说,在gawk外面,可以调用do_prvars函数,结果如下:
./gawk -d 'BEGIN {a=2+3;print a; prvars }' > test3_gawk.txt
结果很让人意外,如下:
Fields:30 fields
$0 is ''
$1 is ''
$2 is ''
$3 is ''
$4 is ''
$5 is ''
$6 is ''
$7 is ''
$8 is ''
$9 is ''
$10 is ''
$11 is ''
$12 is ''
$13 is ''
$14 is ''
$15 is ''
$16 is ''
$17 is ''
$18 is ''
$19 is ''
$20 is ''
$21 is ''
$22 is ''
$23 is ''
$24 is ''
$25 is ''
$26 is ''
$27 is ''
$28 is ''
$29 is ''
Vars:
'FS': :(0)[(0x12502c0 String " ")]
'NF': var_type ref:19199020
DATA:19202816
0:(0)(0x1250300 Number 0)
'OFS': :(0)[(0x1250380 String " ")]
'NR': var_type ref:19199020
DATA:19202912
0:(0)(0x1250360 Number 0)
'OFMT': %.6g:(0)[(0x1250400 String "%.6g")]
'RS':
还有很长,于是接着在awk3.c中找到函数
dump_vars() 148 { 149 register int n; 150 register HASHNODE *buc; 151 152 printf("Fields:"); 153 dump_fields(); 154 printf("Vars:\n"); 155 for(n=0;n<HASHSIZE;n++) { 156 for(buc=variables[n];buc;buc=buc->next) { 157 printf("'%.*s': ",buc->length,buc->name); 158 print_simple(buc->value->var_value,stdout);
159 printf(":"); 160 print_parse_tree(buc->value->lnode);
161 /* print_parse_tree(buc->value); */
162 } 163 } 164 printf("End\n");
165 }
这里就能看到程序中用到的变量。