有名な本らしいので買ってみました
ふつうのLinuxプログラミング Linuxの仕組みから学べるgccプログラミングの王道
ふつうのLinuxプログラミング 第2版 Linuxの仕組みから学べるgccプログラミングの王道
ふつうのLinuxプログラミングをやってみる その1
https://qiita.com/uturned0/items/b9ae846f2aff5865c074
ふつうのLinuxプログラミングをやってみる その2
https://qiita.com/uturned0/items/56beac990cdd6f1059ed
ふつうのLinuxプログラミングをやってみる その3
https://qiita.com/uturned0/items/675092da8aa89c4b1ff0
その4
https://qiita.com/uturned0/items/8f5765cfc0f0be8a1981
その5
https://qiita.com/uturned0/items/ab97deb489c994a836da
その6
https://qiita.com/uturned0/items/b7df48e87ae9170f3698
その7
https://qiita.com/uturned0/items/263151cf7c83bff7fac1
その7−3
https://qiita.com/uturned0/items/3fbf3ed6bb2a47325f59
その8,9
https://qiita.com/uturned0/items/3d8314095c45aa0bf920
うわっ。前回の記事が 2020/10 で、いま 2022/01 。1年以上放置してたのか自分・・・・びっくりたまげたわ・・・・。
公式sourceはこちら
https://github.com/aamine/stdlinux2-source
gdb とは
C-langの開発で使うデバッガ。
前準備
- macの場合、できない場合がある
素直にvagrantでやりました。cent7.古い。
https://gist.github.com/kujiy/fdd73d6f200cfff8e067682f6b6a7848
wget https://gist.githubusercontent.com/kujiy/fdd73d6f200cfff8e067682f6b6a7848/raw/7fcadaf0bc43b9ace23ef620c2049962a77a97bc/Vagrantfile
# 共有フォルダ(synced_folder) 書き換えて
vim Vagrantfile
vagrant up
vagrant ssh node1
(ひとりごと)vmに入れるとIDEからremote debugになるから面倒なのよね・・
2.bugがあるコードを作る
まずbugありの head3.c を流す。
sourceはここから
https://github.com/aamine/stdlinux2-source
この 15行目の n:
を n
にしてbugを埋め込む
while ((opt = getopt(argc, argv, "n")) != -1) {
gdbを使うには
- install
結構大変だった
sudo yum install -y gdb
sudo debuginfo-install -y glibc-2.17-325.el7_9.x86_64
- compile時に特別なオプション
-g
が必要
$ gcc -Wall -g -o head stdlinux2-source/head3.c
- 起動
gdb ./head
....
(gdb) ←これが出れば成功
ここで、引数にファイル名を渡しているので、今後はファイル名を省略してgdb専用のコマンドを実行していく。
このフィアル名=コマンド名になる
実行。コマンド名部分は除き、optionだけ書いていく
run -n 5 /etc/os-release
↑ runをheadと読み替えれば head -n 5 /etc/os-release になる
エラーが出る
(gdb) run -n 5 /etc/os-release
Starting program: /repos/futu/head -n 5 /etc/os-release
Program received signal SIGSEGV, Segmentation fault.
__GI_____strtol_l_internal (nptr=0x0, endptr=0x0, base=10, group=<optimized out>, loc=0x7ffff7dd5060 <_nl_global_locale>) at ../stdlib/strtol_l.c:293
293 while (ISSPACE (*s))
エラーをおこした関数 __GI_____strtol_l_internal
がわかるがそんなコード書いてない。 strtol_l.c の中のエラー。誰がこれを呼んだか探す
backtrace
backtraceを使うとエラーになるまでの流れが見える
(gdb) backtrace
# 0 __GI_____strtol_l_internal (nptr=0x0, endptr=0x0, base=10, group=<optimized out>, loc=0x7ffff7dd5060 <_nl_global_locale>) at ../stdlib/strtol_l.c:293
# 1 0x00000000004008b2 in main (argc=4, argv=0x7fffffffe428) at stdlinux2-source/head3.c:18
最後の行を読み解くと head3.cの 18行目
ってことがわかる
Debugしてみよう!!!
frame
みたいエラーの行頭は #1
なので 1 を指定
(gdb) frame 1
# 1 0x00000000004008b2 in main (argc=4, argv=0x7fffffffe428) at stdlinux2-source/head3.c:18
18 nlines = atol(optarg);
この 2行目が、プログラムが止まった場所(本では "実行中の場所" とよんでいる)。たしかに nlines = ...
はソース中に存在します。18行目だね。
「18行目を実行したら、エラーになった。その場所で止まった。19行目には行けなかった。だから18行目にstayしてます。」この状態。
list
list を叩くともっと広くコードを見れる。ただ現在行にマークとかないので、行数は脳内に記憶しておく(今回は18行目)
(gdb) list
13 long nlines = DEFAULT_N_LINES;
14
15 while ((opt = getopt(argc, argv, "n")) != -1) {
16 switch (opt) {
17 case 'n':
18 nlines = atol(optarg);
19 break;
20 case '?':
21 fprintf(stderr, "Usage: %s [-n LINES] [file...]\n", argv[0]);
22 exit(1);
ここからはプログラマの推測が入っていきます。経験が物を言う時間。
nlines = atol(optarg);
を実行してエラーになるということは、きっと atol(optarg);
に問題がある。
optarg
に何が渡されてきたか調べるのが print
(gdb) print optarg
$1 = 0x0
0x0 や 0 は、つまり NULLらしい。
次の推測をします。
optargがNULLってことは、それを作る getopt()
が適切な値を作ってないってことです。ってわけで、あとは getopt()
を疑って、原因(今回は "n"ではなく "n:" にする)を探しましょう、というのが、本の内容でした。
うーん。なるほど。まあ、そうか。getopt()は現状エラー出してないし、そうなるか。なるほど。
名言: debuggerができることは、エラーの行数と、変数の中身を見せるところまで。
文字数まで教えてくれるdebuggerけっこう当たり前だと思ってたけど、gdbはそうじゃないっぽいかな。
continue
いま、debuggerはエラーが起こった行で一時停止してます。ステップ実行中みたいな。これを強制的に最後まで進ませると、当然エラーなので、プログラムが終了します。最後まで実行させるのが continue(cont, c でも動く)。IDEの F9 ですね
(gdb) continue
Continuing.
Program terminated with signal SIGSEGV, Segmentation fault.
The program no longer exists.
そしたら ctrl + D とかで quit して、gdb はおわり。
breakpoint
ここは本に書いてなかったけどざっくり試した
# 12行目にbreakpoint
(gdb) break 12
# main()に
(gdb) break main
b は b
で省略可能
breakpointおいたら、実行
(gdb) run -n 5 /etc/os-release
ちゃんと止まりました
Starting program: /repos/futu/head -n 5 /etc/os-release
Breakpoint 1, main (argc=4, argv=0x7fffffffe428) at stdlinux2-source/head3.c:13
13 long nlines = DEFAULT_N_LINES;
こっからは s, n, c でステップ実行していく
-
s
: 1行進む(IDEのF7) (step in) -
n
: 関数実行(IDEのF8) (step over) -
c
: 最後まで実行(IDEのF9 (cont, continueと同義)
詳細ref. https://condor.depaul.edu/glancast/373class/docs/gdb.html
ソースが広く見たくなったら list する。変数みたくなったら print する。
なるほど!!!
ついに gdb をマスターした!!
この本を読みながらやってます
ふつうのLinuxプログラミング Linuxの仕組みから学べるgccプログラミングの王道
ふつうのLinuxプログラミング 第2版 Linuxの仕組みから学べるgccプログラミングの王道