MPIプログラムをデバッグするにはどうすればよいですか?


129

コンパイルして実行するMPIプログラムがありますが、奇妙なことが何も起こらないようにするために、ステップスルーしたいと思います。理想的には、GDBを特定のプロセスに接続する簡単な方法が欲しいのですが、それが可能かどうか、またはそれを行う方法は本当にわかりません。別の方法としては、各プロセスに個別のログファイルにデバッグ出力を書き込ませる方法がありますが、これは実際にはデバッガと同じ自由度を与えません。

より良いアプローチはありますか?MPIプログラムをどのようにデバッグしますか?

回答:


62

他の誰かが言ったように、TotalViewはこれの標準です。しかし、それはあなたに腕と脚を要します。

OpenMPIサイトには、MPIデバッグに関するすばらしいFAQがあります。FAQの項目#6は、GDBをMPIプロセスにアタッチする方法を説明しています。すべてを読んでください。いくつかの素晴らしいヒントがあります。

ただし、追跡するプロセスが多すぎる場合は、スタックトレース分析ツール(STAT)を確認してください。Livermoreでこれを使用して、実行可能な数十万の実行中のプロセスからスタックトレースを収集し、ユーザーにインテリジェントに表現します。これは完全な機能を備えたデバッガーではありません(完全な機能を備えたデバッガーは、208kコアまで拡張されません)が、どのプロセスのグループが同じことを実行しているかがわかります。次に、標準デバッガーで各グループの代表をステップ実行できます。


14
2010年現在、Allinea DDTはフル機能のデバッガーであり、208kコア以上に拡張できます
マーク

1
したがって、私は先に進んで、ここで@Markの回答に賛成票を投じます。DDTは素晴らしいです。ぜひお試しください。TotalViewは現在STATとも統合されているため、サイトにTotalViewがインストールされている場合は、それも試すことができます。LLNLはTotalViewとDDTを維持し、TotalViewが最終的に厳しい競争を繰り広げているのは素晴らしいことです。
トッドギャンブリン

MPIデバッグに関するFAQ(open-mpi.org/faq/?category=debugging#serial-debuggers)へのリンクを紹介したいと思います。具体的には、箇条書き6は、少なくとも個々のプロセスをデバッグする方法を理解するのに適しており、すばやく、簡単です(私でも十分です)。
ジェフ

FAQページの#6の手順は私にとって非常にうまくいき、私の問題を理解するのに役立ちました。本当にありがとうございました。
Jon Deaton 2017年

86

gdbは非常に便利です。私はそれを

mpirun -np <NP> xterm -e gdb ./program 

これは私ができるxtermウィンドウを起動します

run <arg1> <arg2> ... <argN>

通常は問題なく動作します

これらのコマンドを一緒にパッケージ化することもできます。

mpirun -n <NP> xterm -hold -e gdb -ex run --args ./program [arg1] [arg2] [...]

同じ入力をすべてのNP gdb xtermに送信するにはどうすればよいですか?たとえば、すべてのプロセスに2つのブレークポイントを追加したいのですが、16のプロセスがあります。これを行うためのxtermの代替手段はありますか?セッションを画面の単一インスタンス、tmux、またはChris Jonesのターミネーターに接続できますか?
osgx 2015

@osgxこれを行うには、コマンド( "break xxx"、 "break yyy"、 "run")を保存してgdbに<file>渡し-x <file>ます。
eush77 2016年

エラーメッセージは「ファイルxtermでのexecvpエラー(そのようなファイルまたはディレクトリはありません)」です
hitwlh

jdbとOpenMPIでこれを試しても機能しません。つまり、各jdbインスタンスは-np引数に指定されたものではなく、num_ranksとして1を参照します。なぜか?
ミシェル・ミューラー

26

ここの投稿の多くはGDBに関するものですが、起動からプロセスにアタッチする方法については触れません。もちろん、すべてのプロセスにアタッチできます。

mpiexec -n X gdb ./a.out

しかし、すべてのプロセスを起動するためにバウンスする必要があるため、これは非常に効果がありません。1つ(または少数)のMPIプロセスをデバッグするだけの場合は、:演算子を使用して、コマンドラインで別の実行可能ファイルとしてそれを追加できます。

mpiexec -n 1 gdb ./a.out : -n X-1 ./a.out

これで、プロセスの1つだけがGDBを取得します。


「mpiexec -n X gdb ./a.out」を使用できますが、gdb -tuiモードを使用する方法はありますか?
hitwlh

16

他の人が述べたように、ごく少数のMPIプロセスでのみ作業している場合は、複数のgdbセッション、redoubtable valgrind、または独自のprintf /ロギングソリューションを使用してみることができます。

それよりも多くのプロセスを使用している場合は、適切なデバッガーが必要になります。OpenMPIのよくある質問は、両方をお勧めしますAllinea DDTTotalViewのを

私はAllinea DDT取り組んでいます。これはフル機能のグラフィカルなソースコードデバッガなので、次のことができます。

  • (200k以上の)MPIプロセスのデバッグまたはアタッチ
  • グループまたは個別にステップして一時停止する
  • ブレークポイント、監視、トレースポイントを追加する
  • メモリエラーとリークをキャッチ

...等々。EclipseまたはVisual Studioを使用したことがあれば、ご自宅にいるようになります。

特にMPI、マルチスレッド、CUDAなどの並列コードをデバッグするための興味深い機能をいくつか追加しました。

  • スカラー変数はすべてのプロセスにわたって自動的に比較されます:( ソース:allinea.comプロセス全体の値を示すスパークライン

  • 変数と式の値をプロセスと時間にわたって追跡およびフィルタリングすることもできます。 時間の経過に伴うトレースポイントログ値

ORNLNCSALLNLJülichなど、上位500の HPCサイトで広く使用されています。al。

インターフェースはかなり素早いです。オークリッジのジャガークラスターでの受け入れテストの一環として、220,000プロセスのスタックと変数を0.1秒でステップおよびマージする時間を計測しました。

@tgamblinは、他のいくつかの一般的なオープンソースプロジェクトと同様に、Allinea DDTと統合する優れたSTATについて言及しました。



7

あなたがtmuxユーザーであれば、ベネディクト・モルバッハのスクリプトを使用して非常に快適に感じるでしょう:tmpi

元のソース: https://github.com/moben/scripts/blob/master/tmpi

フォーク:https : //github.com/Azrael3000/tmpi

これを使用すると、複数のパネル(プロセス数)がすべて同期されます(すべてのコマンドがすべてのパネルまたはプロセスに同時にコピーされるため、xterm -eアプローチと比較して多くの時間を節約できます)。さらにprint、別のパネルに移動することなく、実行したいプロセスの変数の値を知ることができます。これにより、各パネルに各プロセスの変数の値が出力されます。

あなたがtmuxユーザーでないなら、私はそれを試して見てみることを強く勧めます。


2
tmpiは本当に素晴らしく、まさに私が探していたものなので、元の作者が削除したgithub.com/Azrael3000/tmpiで githubアカウントにフォークしました
Azrael3000

6

http://github.com/jimktrains/pgdb/tree/masterは、これを行うために私が書いたユーティリティです。いくつかのドキュメントがあり、質問があれば遠慮なく私に連絡してください。

基本的には、GDBをラップし、そのIOを中央サーバーに集めるperlプログラムを呼び出します。これにより、GDBが各ホストで実行され、ターミナルの各ホストでGDBにアクセスできるようになります。


ありがとう!次回MPIで作業するときに、必ずこれを確認します。
ジェイコンロッド

5

screen一緒に使用してgdbMPIアプリケーションをデバッグすることxtermは、特にが利用できない場合、またはいくつかのプロセッサを処理している場合にうまく機能します。途中でスタックオーバーフロー検索を行うと、多くの落とし穴があったので、私のソリューションを完全に再現します。

まず、MPI_Initの後にコードを追加してPIDを出力し、プログラムが接続を待機するのを停止します。標準的な解決策は無限ループのようです。最終的にに落ち着きました。gdb内でエスケープraise(SIGSTOP);するには、の追加の呼び出しが必要continueです。

}
    int i, id, nid;
    MPI_Comm_rank(MPI_COMM_WORLD,&id);
    MPI_Comm_size(MPI_COMM_WORLD,&nid);
    for (i=0; i<nid; i++) {
        MPI_Barrier(MPI_COMM_WORLD);
        if (i==id) {
            fprintf(stderr,"PID %d rank %d\n",getpid(),id);
        }
        MPI_Barrier(MPI_COMM_WORLD);
    }
    raise(SIGSTOP);
}

コンパイル後、実行可能ファイルをバックグラウンドで実行し、stderrをキャッチします。次にgrep、いくつかのキーワード(ここではリテラルPID)のstderrファイルを使用して、各プロセスのPIDとランクを取得できます。

MDRUN_EXE=../../Your/Path/To/bin/executable
MDRUN_ARG="-a arg1 -f file1 -e etc"

mpiexec -n 1 $MDRUN_EXE $MDRUN_ARG >> output 2>> error &

sleep 2

PIDFILE=pid.dat
grep PID error > $PIDFILE
PIDs=(`awk '{print $2}' $PIDFILE`)
RANKs=(`awk '{print $4}' $PIDFILE`)

gdbセッションは、を使用して各プロセスにアタッチできますgdb $MDRUN_EXE $PID。スクリーンセッション内でこれを行うと、どのgdbセッションにも簡単にアクセスできます。-d -mデタッチモードで画面を起動し、-S "P$RANK"後で簡単にアクセスできるように画面に名前を付けることができます-l。bash のオプションはインタラクティブモードで画面を起動し、gdbがすぐに終了しないようにします。

for i in `awk 'BEGIN {for (i=0;i<'${#PIDs[@]}';i++) {print i}}'`
do
    PID=${PIDs[$i]}
    RANK=${RANKs[$i]}
    screen -d -m -S "P$RANK" bash -l -c "gdb $MDRUN_EXE $PID"
done

画面でgdbが起動したら、screenの-X stuffコマンドを使用して、画面への入力をスクリプト化できます(すべての画面に入力して同じものを入力する必要がないようにするため)。コマンドの最後に改行が必要です。ここで、画面には-S "P$i"以前に指定した名前を使用してアクセスします。この-p 0オプションは重要です。それ以外の場合、コマンドは断続的に失敗します(以前に画面に接続したことがあるかどうかに基づいて)。

for i in `awk 'BEGIN {for (i=0;i<'${#PIDs[@]}';i++) {print i}}'`
do
    screen -S "P$i" -p 0 -X stuff "set logging file debug.$i.log
"
    screen -S "P$i" -p 0 -X stuff "set logging overwrite on
"
    screen -S "P$i" -p 0 -X stuff "set logging on
"
    screen -S "P$i" -p 0 -X stuff "source debug.init
"
done

この時点で、を使用して任意の画面にアタッチしたり、を使用screen -rS "P$i"してデタッチしたりできますCtrl+A+D。コマンドは、前のコードセクションと同様に、すべてのgdbセッションに送信できます。


3

また、並列プログラミングの支援を目的とした私のオープンソースツールpadbもあります。デバッガとしてだけでなく、たとえば並列トップのようなプログラムとしても機能するため、これを「ジョブ検査ツール」と呼びます。「完全レポート」モードで実行すると、アプリケーション内のすべてのプロセスのスタックトレースと、すべてのランクのすべての関数のローカル変数が表示されます(-gでコンパイルした場合)。また、ジョブ内の各ランクの未処理の送受信のリストである「MPIメッセージキュー」も表示されます。

完全なレポートを表示するだけでなく、ジョブ内の個々の情報を拡大するようにpadbに指示することもできます。表示する情報を制御する無数のオプションと構成項目があります。詳細については、Webページを参照してください。

Padb


3

MPIプログラムをデバッグする「標準」の方法は、その実行モデルをサポートするデバッガーを使用することです。

UNIXの場合、TotalViewはMPIをサポートしていると言われています。


2

この小さなhomebrewnメソッドを使用して、MPIプロセスにデバッガーをアタッチします。コード内のMPI_Init()の直後に、次の関数DebugWait()を呼び出します。これで、プロセスがキーボード入力を待機している間、いつでもデバッガをプロセスに接続してブレークポイントを追加できます。完了したら、単一の文字入力を提供し、準備が整います。

static void DebugWait(int rank) {
    char    a;

    if(rank == 0) {
        scanf("%c", &a);
        printf("%d: Starting now\n", rank);
    } 

    MPI_Bcast(&a, 1, MPI_BYTE, 0, MPI_COMM_WORLD);
    printf("%d: Starting now\n", rank);
}

もちろん、この関数はデバッグビルド専用にコンパイルする必要があります。


MPIでは、単純なコードでも、これまでに記述したデバッグステートメントの中で最も多くのものが必要です。(笑)これはとても役に立ちます。
Troggy 2009

3
この解決策は、ここの箇条書き6に似ています(open-mpi.org/faq/?category=debugging#serial-debuggers)。を追加することで、コードを少し改善できますgethostname(hostname, sizeof(hostname)); printf("PID %d on host %s ready for attach\n", getpid(), hostname);。次に、と入力してプロセスにアタッチし、rsh <hostname_from_print_statement>最後にをアタッチしますgdb --pid=<PID_from_print_statement>
ジェフ


2

MPIプログラムをデバッグする非常に簡単な方法。

main()関数にスリープ(some_seconds)を追加します

通常どおりプログラムを実行します

$ mpirun -np <num_of_proc> <prog> <prog_args>

プログラムが開始され、スリープ状態になります。

したがって、psでプロセスを見つけ、gdbを実行してそれらに接続するまでに数秒かかります。

QtCreatorなどのエディターを使用している場合は、

デバッグ->デバッグの開始->実行中のアプリケーションにアタッチ

そしてそこであなたがプロセスを見つける。


1

ログトレースを使用してMPI関連のデバッグを行いますが、mpich2を使用している場合はgdbを実行することもできます:MPICH2およびgdb。この手法は、デバッガーから起動するのが難しいプロセスを処理している場合、一般的には良い方法です。


壊れていない別のリンクに変更し、コメントを追加しました。
Jim Hunziker、2015年


0

もう1つのソリューションは、シミュレートされたMPIであるSMPIでコードを実行することです。それは私が関わっているオープンソースプロジェクトです。すべてのMPIランクは、同じUNIXプロセスのスレッドに変換されます。その後、gdbを使用してMPIランクを段階的に変更できます。

SMPIはMPIアプリケーションの研究に他の利点を提案します:クレアボイアンス(システムのすべての部分を観察できます)、再現性(特に指定しない限り、数回実行するとまったく同じ動作になります)、ハイゼンバグの不在(シミュレーションプラットフォームが異なるため)ホストから)など

詳細については、このプレゼンテーションまたは関連する回答を参照してください。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.