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 }
這裏就能看到程序中用到的變量。