GDBを使用してプログラムをデバッグする方法は一般に3つあります。
- gdbファイル名
- gdb attach pid
- gdbファイル名コア名
これは、このレッスンのコアコンテンツにも対応しています。
- ターゲットプログラムを直接デバッグします
- 追加のプロセス
- コアファイルをデバッグする
次に、1つずつ説明します。
ターゲットプログラムを直接デバッグします
開発段階で、または他の人のプロジェクトを研究する際、対象のバイナリファイルが正常にコンパイルによって生成されたときには、使用することができます し、GDBファイル名を 直接このプログラムのデバッグを開始する ファイル名は 、デバッグプログラムファイルの名前があるのニーズへのこの方法では、GDBを直接使用して開始します。プログラムがデバッグされます。実際には実行可能ファイルを添付するだけで、プログラムを起動しないため、デバッグ用のプログラムを起動することは厳密ではないことに注意してください 。プログラムを実際に実行するには、runコマンドを入力する必要があります。 実行 コマンドは、コース以下に詳細に紹介します。前のレッスンのGDBデバッグhello_serverシリーズはこのメソッドを使用します。
fileserverというプログラムがあり、 gdb fileserverを使用し てプログラムをアタッチしてから、run コマンドを使用し てプログラムを開始するとします。以下に示すように:
追加のプロセス
場合によっては、プログラムがすでに開始されており、このプログラムをデバッグしたいのですが、再起動したくない場合があります。チャットテストサーバープログラムが実行されているシナリオがあるとします。一定期間実行した後、チャットサーバーが新しいクライアント接続を受け入れることができないことがわかりました。この時点で、プログラムを再起動しないでください。再起動した場合、現在のプログラムステータス情報は失われます。どうやるか?gdbアタッチプロセスID を使用して 、GDBデバッガーをチャットテストサーバープログラムにアタッチできます。たとえば、チャットプログラムがchatserverと呼ばれる場合、psコマンドを使用してプロセスのPIDを取得し、gdbattachを使用してそれをデバッグできます。操作は次のとおりです。
[zhangyl@iZ238vnojlyZ flamingoserver]$ ps -ef | grep chatserver
zhangyl 21462 21414 0 18:00 pts/2 00:00:00 grep --color=auto chatserver
zhangyl 26621 1 5 Oct10 ? 2-17:54:42 ./chatserver -d
コピー
実際の実行を次の図に示します。
上記のコードにより、チャットサーバーのPIDは26621になり、gdb attach 26621 を使用して GDBをチャットサーバープロセスにアタッチします。操作と出力は次のとおりです。
[zhangyl@localhost flamingoserver]$ gdb attach 26621
Attaching to process 26661
Reading symbols from /home/zhangyl/flamingoserver/chatserver...done.
Reading symbols from /usr/lib64/mysql/libmysqlclient.so.18...Reading symbols from /usr/lib64/mysql/libmysqlclient.so.18...(no debugging symbols found)...done.
Reading symbols from /lib64/libpthread.so.0...(no debugging symbols found)...done.
[New LWP 42931]
[New LWP 42930]
[New LWP 42929]
[New LWP 42928]
[New LWP 42927]
[New LWP 42926]
[New LWP 42925]
[New LWP 42924]
[New LWP 42922]
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
Loaded symbols for /lib64/libpthread.so.0
Reading symbols from /lib64/libc.so.6...(no debugging symbols found)...done.
コピー
スペースを節約するために、上記のコードのいくつかの無関係な情報を削除しました。「プロセス26621にアタッチしています」というプロンプトが表示されたら、 GDBがターゲットプロセスに正常にアタッチされたことを意味します。プログラムは一部のシステムライブラリ(libc.soなど)を使用することに注意してください。これはLinuxシステムのリリースバージョンであるため、これらのライブラリにはデバッグシンボルがないため、GDBはのデバッグシンボルが見つからないことを確認します。これらのライブラリ。目的はチャットサーバーのデバッグであるため、システムAPI呼び出しの内部実装には注意を払わないため、チャットサーバーファイルにデバッグ情報が含まれている限り、これらのヒントは無視できます。
ターゲットプロセスがgdbに接続されている場合、デバッガーは一時停止します。このとき、continueコマンドを使用してプログラムを続行するか、対応するブレークポイントを追加してプログラムの実行を続行できます(ここに記載されているcontinueコマンドかどうかは関係ありません)。これらのコマンドの使用については、後で詳しく説明します)。
プログラムのデバッグを終了してデバッグを終了し、現在のプロセスチャットサーバーに影響を与えない場合、つまり、プログラムを実行し続けたい場合は、GDBコマンドでdetachコマンドを入力できます。プログラムをGDBデバッガーから分離して、チャットサーバーを実行し続けることができるようにするためのラインインターフェイス:
(gdb) detach
Detaching from program: /home/zhangyl/flamingoserver/chatserver, process 42921
コピー
次に、GDBを再度終了します。
(gdb) quit
[zhangyl@localhost flamingoserver]$
コピー
コアファイルをデバッグする
サーバープログラムは、一定期間実行した後に突然クラッシュすることがあります。これは私たちが望んでいることではなく、この問題を解決する必要があります。プログラムがクラッシュしたときに生成されたコアファイルを持っている限り、このコアファイルを使用してクラッシュの原因を特定できます。もちろん、Linuxシステムはデフォルトでプログラムクラッシュのコアファイルメカニズムを有効にしません。ulimit-cコマンドを使用して、システムがこのメカニズムを有効にしているかどうかを確認できます。
ちなみに、ulimitコマンドは、コアファイルの生成が有効になっているかどうかだけでなく、システムで許可されているファイル記述子の最大数など、他の機能も確認できます。ulimit-aコマンドを使用して確認できます。具体的には、この内容のため、このレッスンのトピックとは関係がないので、ここでは繰り返しません。
[zhangyl@localhost flamingoserver]$ ulimit -a
core file size (blocks, -c) 0
data seg size (kbytes, -d) unlimited
scheduling priority (-e) 0
file size (blocks, -f) unlimited
pending signals (-i) 15045
max locked memory (kbytes, -l) 64
max memory size (kbytes, -m) unlimited
open files (-n) 1024
pipe size (512 bytes, -p) 8
POSIX message queues (bytes, -q) 819200
real-time priority (-r) 0
stack size (kbytes, -s) 8192
cpu time (seconds, -t) unlimited
max user processes (-u) 4096
virtual memory (kbytes, -v) unlimited
file locks (-x) unlimited
コピー
コアファイルサイズの行はデフォルトで0であることがわかります。これは、コアファイルの生成が閉じられていることを意味し、「ulimitオプション名設定値」を使用して変更できます。たとえば、コアファイルの生成を特定の値(最大許容バイト数)に変更できます。ここでは、 ulimit -c無制限(unlimited は -c オプションの値)を使用して、無制限のサイズに直接変更します。
[zhangyl@localhost flamingoserver]$ ulimit -c unlimited
[zhangyl@localhost flamingoserver]$ ulimit -a
core file size (blocks, -c) unlimited
data seg size (kbytes, -d) unlimited
scheduling priority (-e) 0
file size (blocks, -f) unlimited
pending signals (-i) 15045
max locked memory (kbytes, -l) 64
max memory size (kbytes, -m) unlimited
open files (-n) 1024
pipe size (512 bytes, -p) 8
POSIX message queues (bytes, -q) 819200
real-time priority (-r) 0
stack size (kbytes, -s) 8192
cpu time (seconds, -t) unlimited
max user processes (-u) 4096
virtual memory (kbytes, -v) unlimited
file locks (-x) unlimited
コピー
このコマンドは覚えやすいことに注意してください。最初のulimitは Linuxコマンドです。-cオプションの後の無制限はオプションの値です。つまり、サイズは制限されていません。もちろん、特定の値に変更できます。 。多くの初心者は、このコマンドを学習するときに、ulimitコマンドをunlimitedの値と 常に混同し ます。読者がその意味を理解できれば、通常は混同されません。
もう1つの問題は、この変更後、Linuxセッションを閉じると、設定項目の値が0に復元され、サーバープログラムが通常、バックグラウンドプログラム(デーモン)として長期間実行されることです。現在のセッションが閉じられている場合でも、サーバープログラムはバックグラウンドで実行され続けるため、プログラムは特定の瞬間にクラッシュした後、コアファイルを生成できません。この状況はトラブルシューティングに役立ちません。したがって、このオプションを永続的に有効にする必要があります。永続的に有効にする方法は、「ulimit-cunlimited」行を/ etc / profileファイルに追加し、ファイルの最後の行に配置することです。
具体例
生成されたコアファイルのデフォルトの命名方法はcore.pidです。たとえば、プログラムが実行されていて、そのプロセスIDが16663の場合、クラッシュによって生成されたコアファイルの名前はcore.16663です。特定の例を見てみましょう。サーバー上のmsg_serverがクラッシュし、次のようなコアファイルが生成されたことがあります。
-rw------- 1 root root 10092544 Sep 9 15:14 core.21985
コピー
このcore.21985ファイルを使用して、クラッシュの原因のトラブルシューティングを行うことができます。コアファイルをデバッグするコマンドは次のとおりです。
gdb filename corename
コピー
その中で、filenameはプログラム名、ここではmsg_server、corenameはcore.21985、gdb msg_servercore.21985と入力してデバッグを開始します。
[root@myaliyun msg_server]# gdb msg_server core.21985
Reading symbols from /root/teamtalkserver/src/msg_server/msg_server...done.
[New LWP 21985]
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
Core was generated by `./msg_server -d'.
Program terminated with signal 11, Segmentation fault.
#0 0x00000000004ceb1f in std::less<CMsgConn*>::operator() (this=0x2283878, __x=@0x7ffca83563a0: 0x2284430, __y=@0x51: <error reading variable>)
at /usr/include/c++/4.8.2/bits/stl_function.h:235
235 { return __x < __y; }
コピー
stl_function.hの235行目でプログラムがクラッシュしたことを確認してから、bt コマンド(このコマンドについては後で詳しく説明します)を使用してクラッシュ時のコールスタックを表示し、さらに分析して原因を特定 できます。クラッシュの。
(gdb) bt
#0 0x00000000004ceb1f in std::less<CMsgConn*>::operator() (this=0x2283878, __x=@0x7ffca83563a0: 0x2284430, __y=@0x51: <error reading variable>)
at /usr/include/c++/4.8.2/bits/stl_function.h:235
#1 0x00000000004cdd70 in std::_Rb_tree<CMsgConn*, CMsgConn*, std::_Identity<CMsgConn*>, std::less<CMsgConn*>, std::allocator<CMsgConn*> >::_M_get_insert_unique_pos
(this=0x2283878, __k=@0x7ffca83563a0: 0x2284430) at /usr/include/c++/4.8.2/bits/stl_tree.h:1324
#2 0x00000000004cd18a in std::_Rb_tree<CMsgConn*, CMsgConn*, std::_Identity<CMsgConn*>, std::less<CMsgConn*>, std::allocator<CMsgConn*> >::_M_insert_unique<CMsgConn* const&> (this=0x2283878, __v=@0x7ffca83563a0: 0x2284430) at /usr/include/c++/4.8.2/bits/stl_tree.h:1377
#3 0x00000000004cc8bd in std::set<CMsgConn*, std::less<CMsgConn*>, std::allocator<CMsgConn*> >::insert (this=0x2283878, __x=@0x7ffca83563a0: 0x2284430)
at /usr/include/c++/4.8.2/bits/stl_set.h:463
#4 0x00000000004cb011 in CImUser::AddUnValidateMsgConn (this=0x2283820, pMsgConn=0x2284430) at /root/teamtalkserver/src/msg_server/ImUser.h:42
#5 0x00000000004c64ae in CDBServConn::_HandleValidateResponse (this=0x227f6a0, pPdu=0x22860d0) at /root/teamtalkserver/src/msg_server/DBServConn.cpp:319
#6 0x00000000004c5e3d in CDBServConn::HandlePdu (this=0x227f6a0, pPdu=0x22860d0) at /root/teamtalkserver/src/msg_server/DBServConn.cpp:203
#7 0x00000000005022b3 in CImConn::OnRead (this=0x227f6a0) at /root/teamtalkserver/src/base/imconn.cpp:148
#8 0x0000000000501db3 in imconn_callback (callback_data=0x7f4b20 <g_db_server_conn_map>, msg=3 '\003', handle=8, pParam=0x0)
at /root/teamtalkserver/src/base/imconn.cpp:47
#9 0x0000000000504025 in CBaseSocket::OnRead (this=0x227f820) at /root/teamtalkserver/src/base/BaseSocket.cpp:178
#10 0x0000000000502f8a in CEventDispatch::StartDispatch (this=0x2279990, wait_timeout=100) at /root/teamtalkserver/src/base/EventDispatch.cpp:386
#11 0x00000000004fddbe in netlib_eventloop (wait_timeout=100) at /root/teamtalkserver/src/base/netlib.cpp:160
#12 0x00000000004d18c2 in main (argc=2, argv=0x7ffca8359978) at /root/teamtalkserver/src/msg_server/msg_server.cpp:213
(gdb)
コピー
スタック#4はライブラリコードではありません。ここでコードを確認して、問題の原因を見つけることができます。
カスタムコアファイル名
ただし、注意深い読者は問題を見つけるでしょう:プログラムが実行されているとき、そのPIDは取得できますが、プログラムがクラッシュすると、コアファイルが生成されます。特に、複数のプログラムが同時にクラッシュした場合、コアファイル名を渡すことはできません。 PIDでこの問題を解決するために使用されるサービスを区別する方法は2つあります。
- プログラムが開始したら、PIDを記録します
void writePid()
{
uint32_t curPid = (uint32_t) getpid();
FILE* f = fopen("xxserver.pid", "w");
assert(f);
char szPid[32];
snprintf(szPid, sizeof(szPid), "%d", curPid);
fwrite(szPid, strlen(szPid), 1, f);
fclose(f);
}
コピー
プログラムの起動時に上記のwritePID 関数を呼び出し、その時点でのプログラム のPIDを xxserver.pid ファイルに記録します。これにより、プログラムがクラッシュしたときに、その時点で実行されているプロセスのPIDを取得できます。ファイル。デフォルトのコアファイルと比較できます。名前が一致した後のPID。
- コアファイルの名前とディレクトリをカスタマイズします
/proc/sys/kernel/core_uses_pid
生成されたコアファイルのファイル名に拡張子としてPIDを追加するかどうかを制御できます。追加する場合、ファイルの内容は1、それ以外の場合は0です。/proc/sys/kernel/core_pattern はフォーマットされたコアファイルを設定できます。場所またはファイル名を保存します。変更方法は次のとおりです。
echo "/corefile/core-%e-%p-%t" > /proc/sys/kernel/core_pattern
コピー
各パラメーターの説明は次のとおりです。
パラメータ名 | パラメータの意味(英語) | パラメータの意味(中国語) |
---|---|---|
%p | ファイル名にpidを挿入します | コアファイル名にpidを追加します |
%u | 現在のuidをファイル名に挿入します | 現在のuidをコアファイル名に追加します |
%g | 現在のgidをファイル名に挿入します | 現在のgidをコアファイル名に追加します |
%s | コアダンプの原因となった信号をファイル名に挿入します | コアの原因となった信号をコアファイル名に追加します |
%t | コアダンプが発生したUNIX時間をファイル名に挿入します | コアファイル名にコアファイル生成時間(UNIX)を追加 |
%h | コアダンプが発生したホスト名をファイル名に挿入します | コアファイル名にホスト名を追加します |
%e | coredumping実行可能ファイル名をファイル名に挿入します | プログラム名をコアファイル名に追加します |
現在のプログラムがtestと呼ばれていると仮定すると、プログラムが クラッシュしたときのコアファイル名を次のように設定します。
echo "/root/testcore/core-%e-%p-%t" > /proc/sys/kernel/core_pattern
コピー
次に 、/ root / testcore /ディレクトリで生成される テストのコアファイル名の形式は次のとおりです。
-rw-------. 1 root root 409600 Jan 14 13:54 core-test-13154-1547445291
コピー
使用しているユーザーは、指定されたコアファイルディレクトリへの書き込み権限を持っている必要があることに注意してください。そうでない場合、生成中に権限が不十分なため、コアファイルを生成できません。
概要
このレッスンでは、GDBを使用してプログラムをデバッグする3つの方法を紹介します。これらの3つの方法を理解して上手に使用すると、読者が問題が発生したときにデバッグ方法を正確に選択するのに役立ちます。