ページ

2021年2月4日木曜日

マルチスレッドなプログラムをgdbでデバッグする

たまにはエンジニアらしくソフトウェアのお話を.

マルチスレッドなプログラムを書くとき, 常に念頭に置きながら設計しなければならないのが排他制御だが,そんなとき,どの処理がどのスレッドで実施されているか表示できることは非常に有効なテクニックだ.

そこで次のようなコードで実験する.

<サンプルコード:test.cpp>
test.cpp
--------------------------------------------------------------------------------
#include <iostream>
#include <thread>
#include <unistd.h>

void a( void )
{
  for( int i = 0; i < 30; i++ )
  {
    std::cout << "i : " << i << std::endl;
    sleep( 1 );
  }
}

void b( void )
{
  for( int j = 0; j < 10; j++ )
  {
    std::cout << "j : " << j << std::endl;
    sleep( 3 );
  }
}

int main( void )
{
  std::thread * _a = new std::thread( a );
  std::thread * _b = new std::thread( b );
  _a->join();
  _b->join();
  return 0;
}

※本来は,coutする前後でmutexを貼る必要があるが,今回は可読性重視で割愛する.

これをコンパイルして,gdbで実行する
 $ g++ -g -pthread test.cpp -o test
 $ gdb ./test

gdbに制御が移るので,マルチスレッドで処理している関数a及び関数bにそれぞれブレイクポイントを設定する.
(それぞれstd::cout の行にブレイクポイントを設定したので,文字列が出力される直前で動作が停止する)
 (gdb) break test.cpp:9
 Breakpoint 1 at 0x11fa: file test.cpp, line 9.
 (gdb) break test.cpp:18
 Breakpoint 2 at 0x125a: file test.cpp, line 18.

ここで,runすると,9行目のブレイクポイントで停止した.
 (gdb) run
 Starting program: /home/mirasuni/test 
 [Thread debugging using libthread_db enabled]
 Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
 [New Thread 0x7ffff7ab2700 (LWP 5638)]
 [New Thread 0x7ffff72b1700 (LWP 5639)]
 [Switching to Thread 0x7ffff7ab2700 (LWP 5638)]

 Thread 2 "test" hit Breakpoint 1, a () at test.cpp:9
 9           std::cout << "i : " << i << std::endl;

info threads でスレッド情報を取得すると,2つめのスレッドで停止していることがわかる.
 (gdb) info threads
   Id   Target Id                               Frame 
   1    Thread 0x7ffff7ab3740 (LWP 5634) "test" clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:78
 * 2    Thread 0x7ffff7ab2700 (LWP 5638) "test" a () at test.cpp:9
   3    Thread 0x7ffff72b1700 (LWP 5639) "test" clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:78

continueして次のブレイクポイントまで移動.
 (gdb) continue
 Continuing.
 [Switching to Thread 0x7ffff72b1700 (LWP 5639)]

 Thread 3 "test" hit Breakpoint 2, b () at test.cpp:18
 18          std::cout << "j : " << j << std::endl;

再びinfo threads でスレッド情報を取得すると,今度は3つめのスレッドで停止していることがわかる.
 (gdb) info threads
   Id   Target Id                               Frame 
   1    Thread 0x7ffff7ab3740 (LWP 5634) "test" clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:78
   2    Thread 0x7ffff7ab2700 (LWP 5638) "test" 0x0000555555555201 in a () at test.cpp:9
 * 3    Thread 0x7ffff72b1700 (LWP 5639) "test" b () at test.cpp:18


単純なプログラムであれば,深く考えるまでも無いが,複数の処理が複雑に絡み合っている場合,色々と考えを巡らせるより,さっさとデバッグ情報を調べたほうが単純かつ明確だったりする.

それよりも重要なことは,安易にマルチスレッド化に走らず,まずはシングルスレッドで効率よく処理できるよう設計することだけれども.

0 件のコメント:

コメントを投稿