LoginSignup
1
0

More than 1 year has passed since last update.

ふつうのLinuxプログラミングをやってみる その7-3 gdb

Last updated at Posted at 2022-01-22

有名な本らしいので買ってみました
ふつうの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の開発で使うデバッガ。

前準備

  1. 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を使うには

  1. install

結構大変だった

sudo yum install -y gdb
sudo debuginfo-install -y glibc-2.17-325.el7_9.x86_64
  1. compile時に特別なオプション -g が必要
$ gcc -Wall -g -o head stdlinux2-source/head3.c 
  1. 起動
gdb ./head

....

(gdb)   ←これが出れば成功

ここで、引数にファイル名を渡しているので、今後はファイル名を省略してgdb専用のコマンドを実行していく。
このフィアル名=コマンド名になる

実行。コマンド名部分は除き、optionだけ書いていく

run -n 5 /etc/os-release  
   runheadと読み替えれば 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);

print

ここからはプログラマの推測が入っていきます。経験が物を言う時間。

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プログラミングの王道

1
0
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0