GDBには、リバースデバッグをサポートする新しいバージョンがあります(http://www.gnu.org/software/gdb/news/reversible.htmlを参照)。私はそれがどのように機能するのか疑問に思いました。
リバースデバッグを機能させるには、各ステップのメモリを含むマシン状態全体を保存する必要があるように思われます。これにより、大量のメモリを使用することは言うまでもなく、パフォーマンスが非常に遅くなります。これらの問題はどのように解決されますか?
GDBには、リバースデバッグをサポートする新しいバージョンがあります(http://www.gnu.org/software/gdb/news/reversible.htmlを参照)。私はそれがどのように機能するのか疑問に思いました。
リバースデバッグを機能させるには、各ステップのメモリを含むマシン状態全体を保存する必要があるように思われます。これにより、大量のメモリを使用することは言うまでもなく、パフォーマンスが非常に遅くなります。これらの問題はどのように解決されますか?
回答:
私はgdbのメンテナであり、新しいリバースデバッグの作成者の1人です。それがどのように機能するかについてお話しさせていただきます。何人かの人々が推測しているように、後で復元できるように十分なマシン状態を保存する必要があります。いくつかのスキームがあり、そのうちの1つは、各マシン命令によって変更されたレジスタまたはメモリ位置を単に保存することです。次に、その命令を「元に戻す」には、それらのレジスタまたはメモリ位置のデータを元に戻すだけです。
はい、それは高価ですが、現代のCPUは非常に高速であるため、とにかく対話型(ステッピングまたはブレークポイントを実行)すると、それほど気付くことはありません。
next
、step
入力したコマンドをロールバックすることしかできませんか、それとも任意の数の命令を元に戻すことができますか?たとえば、命令にブレークポイントを設定してそれまで実行させた場合、スキップした場合でも前の命令にロールバックできますか?
逆実行を実装するには、シミュレーター、仮想マシン、およびハードウェアレコーダーの使用を忘れてはならないことに注意してください。
これを実装する別のソリューションは、ハードウェアベースのデバッガーでGreenHillsとLauterbachによって実行されるような、物理ハードウェアでの実行をトレースすることです。各命令のアクションのこの固定トレースに基づいて、各命令の効果を順番に削除することにより、トレース内の任意のポイントに移動できます。これは、デバッガーに表示される状態に影響を与えるすべてのものをトレースできることを前提としていることに注意してください。
もう1つの方法は、VmWare Workstation6.5およびVirtutechSimics 3.0(以降)で使用され、Visual Studio 2010に付属しているように見えるチェックポイント+再実行方法を使用することです。ここでは、仮想マシンまたはシミュレーターを使用します。システムの実行に関する間接的なレベルを取得します。状態全体を定期的にディスクまたはメモリにダンプしてから、シミュレータがまったく同じプログラムパスを決定論的に再実行できることに依存します。
簡略化すると、次のように機能します。システムの実行が時間Tであるとします。時間T-1に移動するには、ポイントt <Tからチェックポイントを取得し、(Tt-1)サイクルを実行して、現在の場所の1サイクル前に終了します。これは非常にうまく機能するようにすることができ、ディスクIOを実行し、カーネルレベルのコードで構成され、デバイスドライバーの作業を実行するワークロードにも適用できます。重要なのは、すべてのプロセッサ、デバイス、メモリ、およびIOを含むターゲットシステム全体を含むシミュレータを用意することです。詳細については、gdbメーリングリストとそれに続くgdbメーリングリストの説明を参照してください。私はこのアプローチを定期的に使用して、特にデバイスドライバーや初期のOSブートでトリッキーなコードをデバッグしています。
もう1つの情報源は、チェックポインティングに関するVirtutechホワイトペーパーです(完全な開示で私が書いたものです)。
EclipseConセッション中に、Chronon Debugger forJavaでこれをどのように行うかについても質問しました。その一つは、あなたがすることはできません実際にバックステップが、それがあるように記録されたプログラムの実行を再生することができます感じている逆のデバッグのように。(主な違いは、Chrononデバッガーでは実行中のプログラムを変更できないことですが、他のほとんどのJavaデバッガーでは変更できます。)
私が正しく理解していれば、実行中のプログラムのバイトコードを操作して、プログラムの内部状態のすべての変更が記録されるようにします。外部状態を追加で記録する必要はありません。それらが何らかの形でプログラムに影響を与える場合は、その外部状態に一致する内部変数が必要です(したがって、その内部変数で十分です)。
再生時間中に、基本的に、記録された状態変化から実行中のプログラムのすべての状態を再作成できます。
興味深いことに、状態の変化は、一見したときに予想されるよりもはるかに小さいです。したがって、条件付きの「if」ステートメントがある場合、プログラムがthenステートメントまたはelseステートメントのどちらを使用したかを記録するために少なくとも1ビットが必要であると考えるでしょう。多くの場合、それらの異なるブランチに戻り値が含まれている場合のように、それを回避することができます。次に、戻り値(とにかく必要になる)のみを記録し、実行されたブランチに関する決定を戻り値自体から再計算するだけで十分です。
この質問は古いですが、ほとんどの答えも古いです。 逆デバッグ興味深いトピックのままです、私は2015年の回答を投稿しています。私の修士論文の第1章と第2章では、コンピュータープログラミングの視覚的思考に向けたリバースデバッグとライブプログラミングの組み合わせで、リバースデバッグの歴史的なアプローチのいくつかを取り上げています(特にスナップショット(またはチェックポイント)とリプレイのアプローチに焦点を当てています)。それと全知デバッグの違いを説明します:
ある時点までプログラムを順方向に実行したコンピュータは、実際にそれに関する情報を私たちに提供できるはずです。このような改善は可能であり、いわゆる全知デバッガーに見られます。これらは通常、リバースデバッガーとして分類されますが、プログラマーが実行中のプログラムで実際に時間を遡ることができるのではなく、実行中に情報を記録して後で表示またはクエリするだけなので、「履歴ロギング」デバッガーとしてより正確に説明できます。 。「全知」は、記録されたプログラムの状態履歴全体が、実行後にデバッガーで利用できるという事実に由来します。その場合、プログラムを再実行する必要はなく、手動でコードを計測する必要もありません。
ソフトウェアベースの全知デバッグは、「デバッグ時間履歴再生」と呼ばれる1969年のEXDAMSシステムから始まりました。GNUデバッガーであるGDBは、2009年以来、「プロセスの記録と再生」機能を備えた全知デバッグをサポートしています。TotalView、UndoDB、およびChrononは、現在利用可能な最高の全知デバッガであるように見えますが、商用システムです。TODは、Javaの場合、部分的な決定論的再生、部分的なトレースキャプチャ、および関連する大量の情報の記録を可能にする分散データベースを利用する、最良のオープンソースの代替手段のようです。
記録のナビゲーションを許可するだけでなく、実際に実行時間を後退させることができるデバッガーも存在します。それらは、バックインタイム、タイムトラベル、双方向、またはリバースデバッガーとしてより正確に説明できます。
最初のそのようなシステムは1981年のCOPEプロトタイプでした...
mozillarr
は、GDBリバースデバッグのより堅牢な代替手段です。
GDBの組み込みの記録と再生には厳しい制限があります。たとえば、AVX命令がサポートされていません。gdbの逆デバッグが「プロセスレコードはアドレスで命令0xf0dをサポートしていません」で失敗します。
rrの利点:
rrは、スレッドスイッチなどのすべての非決定論的イベントで何が起こったかを記録する方法で最初にプログラムを実行することによってこれを実現します。
次に、2回目の再生実行中に、驚くほど小さいトレースファイルを使用して、元の非決定論的実行で発生したことを正確に再構築しますが、決定論的な方法で順方向または逆方向に再構築します。
rrはもともとMozillaによって開発され、翌日の夜間テストで発生したタイミングのバグを再現できるようにしています。ただし、実行中に数時間しか発生しないバグがある場合は、逆デバッグの側面も基本です。これは、前の状態が後の障害につながった原因を調べるために戻ることがよくあるためです。
次の例では、その機能の一部、特に展示reverse-next
、reverse-step
およびreverse-continue
コマンドを。
Ubuntu 18.04にインストール:
sudo apt-get install rr linux-tools-common linux-tools-generic linux-cloud-tools-generic
sudo cpupower frequency-set -g performance
# Overcome "rr needs /proc/sys/kernel/perf_event_paranoid <= 1, but it is 3."
echo 'kernel.perf_event_paranoid=1' | sudo tee -a /etc/sysctl.conf
sudo sysctl -p
テストプログラム:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int f() {
int i;
i = 0;
i = 1;
i = 2;
return i;
}
int main(void) {
int i;
i = 0;
i = 1;
i = 2;
/* Local call. */
f();
printf("i = %d\n", i);
/* Is randomness completely removed?
* Recently fixed: https://github.com/mozilla/rr/issues/2088 */
i = time(NULL);
printf("time(NULL) = %d\n", i);
return EXIT_SUCCESS;
}
コンパイルして実行します。
gcc -O0 -ggdb3 -o reverse.out -std=c89 -Wextra reverse.c
rr record ./reverse.out
rr replay
これで、GDBセッション内にとどまり、デバッグを適切に逆にすることができます。
(rr) break main
Breakpoint 1 at 0x55da250e96b0: file a.c, line 16.
(rr) continue
Continuing.
Breakpoint 1, main () at a.c:16
16 i = 0;
(rr) next
17 i = 1;
(rr) print i
$1 = 0
(rr) next
18 i = 2;
(rr) print i
$2 = 1
(rr) reverse-next
17 i = 1;
(rr) print i
$3 = 0
(rr) next
18 i = 2;
(rr) print i
$4 = 1
(rr) next
21 f();
(rr) step
f () at a.c:7
7 i = 0;
(rr) reverse-step
main () at a.c:21
21 f();
(rr) next
23 printf("i = %d\n", i);
(rr) next
i = 2
27 i = time(NULL);
(rr) reverse-next
23 printf("i = %d\n", i);
(rr) next
i = 2
27 i = time(NULL);
(rr) next
28 printf("time(NULL) = %d\n", i);
(rr) print i
$5 = 1509245372
(rr) reverse-next
27 i = time(NULL);
(rr) next
28 printf("time(NULL) = %d\n", i);
(rr) print i
$6 = 1509245372
(rr) reverse-continue
Continuing.
Breakpoint 1, main () at a.c:16
16 i = 0;
複雑なソフトウェアをデバッグする場合、クラッシュポイントに到達してから、深いフレームに陥る可能性があります。その場合、reverse-next
より高いフレームでは、最初に次のことを行う必要があることを忘れないでください。
reverse-finish
そのフレームまでup
は、通常のことをするだけでは十分ではありません。
私の意見では、rrの最も深刻な制限は次のとおりです。
UndoDBはrrの商用代替手段です:https://undo.ioどちらもトレース/リプレイベースですが、機能とパフォーマンスの点でどのように比較されるかはわかりません。
ネイサンフェルマンは書いた:
しかし、リバースデバッグでは、入力した次のコマンドとステップコマンドのみをロールバックできますか、それとも任意の数の命令を元に戻すことができますか?
命令はいくつでも元に戻すことができます。たとえば、前進するときに停止した地点でのみ停止することに制限されません。新しいブレークポイントを設定して、そのブレークポイントまで逆方向に実行できます。
たとえば、命令にブレークポイントを設定してそれまで実行させた場合、スキップした場合でも前の命令にロールバックできますか?
はい。ブレークポイントに到達する前に記録モードをオンにしている限り。
これは、ODBと呼ばれる別のリバースデバッガーがどのように機能するかを示しています。エキス:
Omniscient Debuggingは、プログラム内の各「関心のあるポイント」(値の設定、メソッド呼び出しの作成、例外のスロー/キャッチ)で「タイムスタンプ」を収集し、プログラマーがそれらのタイムスタンプを使用してそのプログラム実行の履歴。
ODB ...は、プログラムがロードされるときにコードをプログラムのクラスに挿入し、プログラムの実行時にイベントが記録されます。
gdbも同じように機能すると思います。
逆デバッグとは、プログラムを逆方向に実行できることを意味します。これは、問題の原因を突き止めるのに非常に役立ちます。
各ステップの完全なマシン状態を保存する必要はなく、変更のみを保存します。それはおそらくまだかなり高価です。