GNU Debugger (GDB) の簡単な使い方を記載します。
main.cpp
#include <iostream>
#include <unistd.h>
using namespace std;
int main() {
int i = 0;
while(true) {
sleep(1);
cout << ++i << endl;
}
return 0;
}
-g -O0
オプションを指定してデバッグモードでコンパイルします。
g++ -Wall -g -O0 main.cpp
gdb ./a.out
(gdb) run
(gdb) run 引数
gdb ./a.out -p 1234
gcore 1528
gdb ./a.out -c core.1528
実行プログラムの確認
file core.1528
core.1528: ELF 64-bit LSB core file x86-64, version 1 (SYSV), SVR4-style, from './a.out', real uid: 1000, effective uid: 1000, real gid: 1000, effective gid: 1000, execfn: './a.out', platform: 'x86_64'
共有ライブラリの確認 (ldd でも確認できる情報)
(gdb) info share
別環境でコアファイルを解析する場合は、必要に応じて共有ライブラリのパスを追加する必要があります。
(gdb) set solib-absolute-prefix /tmp/lib
(gdb) set solib-search-path /tmp/lib
(gdb) file ./myhello
(gdb) core-file ./core.1234
別環境でコアファイルを解析する場合は、ソースコードのパスを追加すると便利です。
(gdb) directory /tmp/src
c
や s
等のコマンドは、エンターを入力するだけで繰り返し実行できます。
現在位置の確認 (list)
bt
l 123
一行進める (next)
n
ステップを進める (step; 関数の中にも入っていきます)
s
変数の値を確認、設定 (print)
p myvar
p myvar = myvar + 1
次のブレークポイントまで進める (continue)
c
バックトレースを表示
bt
関数内で実行すると、関数を抜けるまで進める。
(gdb) finish
for/while で実行すると、for/while を抜けるまで進める。
(gdb) until
(gdb) shell pwd
(gdb) shell g++ -g -O0 main.cpp
(gdb) shell
$ pwd
./gdb.txt
にログ出力されます。
(gdb) set loggin on
(gdb) set loggin overwrite on
(gdb) b ファイル名:行数
(gdb) break ファイル名:行数 if i == j
(gdb) break main
(gdb) info breakpoints
(gdb) info breakpoints ブレークポイント番号
(gdb) delete
(gdb) delete ブレークポイント番号
(gdb) disable ブレークポイント番号
(gdb) enable ブレークポイント番号
値が変更される度にデバッガが停止します。
(gdb) watch i
読み込み時の監視
(gdb) rwatch i
書き込みと読み込みの監視
(gdb) awatch i
(gdb) info args
(gdb) info locals
(gdb) info catch
up
down
frame フレーム番号
(f フレーム番号)
info signals
info signals シグナル名
シグナルの送信
signal シグナル名
(gdb) generate-core-file
コアファイルの最大サイズの確認および最大サイズの解除
ulimit -c
sudo su -l
ulimit -c unlimited
(gdb) info threads
gdb -p 1234
(gdb) ...何らかの制御
(gdb) detach
(gdb) quit
(gdb) whatis 変数名
gdb python -c core.xxxx
参考: Python の実装を確認
$ python
>>> import platform
>>> print platform.python_implementation()
CPython
CPython の NSMALLPOSINTS が 257 となっている場合、0-256 の整数は同じメモリのデータが使い回される。
In : id(256)
Out: 94652018165808
In : id(256)
Out: 94652018165808 ←同じ
In : id(257)
Out: 94652020901008
In : id(257)
Out: 94652020900864 ←異なる
(gdb) p obj
$9 = {px = 0x557bb8d30c60, pn = {pi_ = 0x557bb8cf28d0}}
(gdb) p (obj.px)->MyIsOk()
$8 = false
bt
を適用thread apply all bt
catch throw
run
例外とは関係ありませんが、Segmentation fault が発生した場合、自動で gdb に処理が戻ります。
CPython 実装の Python は gdb でデバッグできます。必要なパッケージをインストールします。
sudo apt install python-dbg
from threading import Thread
from time import sleep
threads = []
def mysleep(x):
print x
sleep(999)
def target():
mysleep('hi')
print 'done'
for i in range(2):
thread = Thread(target=target)
threads.append(thread)
thread.start()
mysleep('hi main')
for thread in threads:
thread.join()
ソースコードの存在するディレクトリでアタッチ
gdb -p `pgrep -f threads.py`
スレッド一覧の確認
(gdb) thread apply all py-list
Thread 3 (Thread 0x7f4a9f5f8700 (LWP 21559)):
3
4 threads = []
5
6 def mysleep(x):
7 print x
>8 sleep(999)
9
10 def target():
11 mysleep('hi')
12 print 'done'
13
Thread 2 (Thread 0x7f4a9fdf9700 (LWP 21558)):
3
4 threads = []
5
6 def mysleep(x):
7 print x
>8 sleep(999)
9
10 def target():
11 mysleep('hi')
12 print 'done'
13
Thread 1 (Thread 0x7f4aa0290740 (LWP 21557)):
3
4 threads = []
5
6 def mysleep(x):
7 print x
>8 sleep(999)
9
10 def target():
11 mysleep('hi')
12 print 'done'
13
(gdb) thread apply all py-locals
Thread 3 (Thread 0x7fd4ccbb4700 (LWP 21985)):
x = 'hi'
Thread 2 (Thread 0x7fd4cd3b5700 (LWP 21984)):
x = 'hi'
Thread 1 (Thread 0x7fd4cd84c740 (LWP 21983)):
x = 'hi main'
スレッドを選択する場合
info threads
thread 2
バックトレースの確認およびフレームの移動
py-bt
py-up
py-down
変数の値の確認
py-local
py-print x
main.cpp
#include <iostream>
int main() {
std::cout << "hi" << std::endl;
return 0;
}
デバッグシンボルを含めるようにコンパイル
g++ -g -O0 main.cpp
du -h a.out
40K a.out
デバッグシンボルを外部ファイルに切り出す
objcopy --only-keep-debug a.out a.debug
objcopy --remove-section .gnu_debuglink a.out
objcopy --add-gnu-debuglink a.debug a.out
objcopy --strip-all --discard-all --preserve-dates a.out
du -hs *
28K a.debug
16K a.out
4.0K main.cpp
a.out
と同じディレクトリにデバッグシンボル a.debug
が存在すれば gdb が利用できます。更にソースコード main.cpp
が存在すればファイル内容や行番号の確認も行えます。
$ ls
a.debug a.out main.cpp
$ gdb a.out
(gdb) b main.cpp:4
Breakpoint 1 at 0x1169: file main.cpp, line 4.
(gdb) run
Starting program: /home/path/to/a.out
Breakpoint 1, main () at main.cpp:4
4 std::cout << "hi" << std::endl;
(gdb) l
1 #include <iostream>
2
3 int main() {
4 std::cout << "hi" << std::endl;
5 return 0;
6 }