リバースデバッグはどのように機能しますか?


82

GDBには、リバースデバッグをサポートする新しいバージョンがあります(http://www.gnu.org/software/gdb/news/reversible.htmlを参照)。私はそれがどのように機能するのか疑問に思いました。

リバースデバッグを機能させるには、各ステップのメモリを含むマシン状態全体を保存する必要があるように思われます。これにより、大量のメモリを使用することは言うまでもなく、パフォーマンスが非常に遅くなります。これらの問題はどのように解決されますか?


4
状態全体ではなく状態デルタを保存することでうまくいくと思いますが、それでもコストがかかるようです。
支出者2009


デルタの保存は確かに非常にうまく機能する可能性があり、効率的なフルシステムのリバーシブルソリューションには本当に必要です。
jakobengblom2 2009年

回答:


131

私はgdbのメンテナであり、新しいリバースデバッグの作成者の1人です。それがどのように機能するかについてお話しさせていただきます。何人かの人々が推測しているように、後で復元できるように十分なマシン状態を保存する必要があります。いくつかのスキームがあり、そのうちの1つは、各マシン命令によって変更されたレジスタまたはメモリ位置を単に保存することです。次に、その命令を「元に戻す」には、それらのレジスタまたはメモリ位置のデータを元に戻すだけです。

はい、それは高価ですが、現代のCPUは非常に高速であるため、とにかく対話型(ステッピングまたはブレークポイントを実行)すると、それほど気付くことはありません。


4
しかし、リバースデバッグではnextstep入力したコマンドをロールバックすることしかできませんか、それとも任意の数の命令を元に戻すことができますか?たとえば、命令にブレークポイントを設定してそれまで実行させた場合、スキップした場合でも前の命令にロールバックできますか?
ネイサンフェルマン

10
>ただし、リバースデバッグでは、入力した次のコマンドとステップコマンドのみをロールバックできますか、それとも任意の数の命令を元に戻すことができますか?任意の数の命令を元に戻すことができます。たとえば、前進するときに停止した地点でのみ停止することに制限されません。新しいブレークポイントを設定して逆方向に実行できます>たとえば、命令にブレークポイントを設定してそれまで実行させた場合、スキップした場合でも前の命令にロールバックできますかはいブレークポイントに到達する前に記録モードをオンにしました
Michael Snyder

3
書式なしテキストで申し訳ありませんが、どうしたのかわかりません。
Michael Snyder

10
逆デバッグによって時間が元に戻され、60年代または70年代に戻る可能性があるのではないかと心配しています。ベルボトムを着て、また髪を長く伸ばしたくありません。
ブリキの

3
そして、OSの状態を変更するシステムコール?それは正しく機能しないだけですか?不透明なハンドルを変更する場合はどうですか?
エイドリアン

12

逆実行を実装するには、シミュレーター、仮想マシン、およびハードウェアレコーダーの使用を忘れてはならないことに注意してください。

これを実装する別のソリューションは、ハードウェアベースのデバッガーで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ホワイトペーパーです(完全な開示で私が書いたものです)。


逆デバッグ手法のより完全なウォークスルーについては、jakob.engbloms.se / archives / 1547とそれに続く2つのブログ投稿も参照してください。
jakobengblom2 2012

リバースステッピングを実装する代わりに「セーブポイントを設定する」機能はどうですか。したがって、デバッグすると、ある時点で現在のステップを「保存ポイント」として選択できます。後で、その保存ポイントに戻って再度ステップし、必要に応じて変数を編集することができます。VMの場合は「スナップショット」、OSの場合は「復元ポイント」のようなものです。
ロルフ

9

EclipseConセッション中に、Chronon Debugger forJavaでこれをどのように行うかについても質問しました。その一つは、あなたがすることはできません実際にバックステップが、それがあるように記録されたプログラムの実行を再生することができます感じている逆のデバッグのように。(主な違いは、Chrononデバッガーでは実行中のプログラムを変更できないことですが、他のほとんどのJavaデバッガーでは変更できます。)

私が正しく理解していれば、実行中のプログラムのバイトコードを操作して、プログラムの内部状態のすべての変更が記録されるようにします。外部状態を追加で記録する必要はありません。それらが何らかの形でプログラムに影響を与える場合は、その外部状態に一致する内部変数が必要です(したがって、その内部変数で十分です)。

再生時間中に、基本的に、記録された状態変化から実行中のプログラムのすべての状態を再作成できます。

興味深いことに、状態の変化は、一見したときに予想されるよりもはるかに小さいです。したがって、条件付きの「if」ステートメントがある場合、プログラムがthenステートメントまたはelseステートメントのどちらを使用したかを記録するために少なくとも1ビットが必要であると考えるでしょう。多くの場合、それらの異なるブランチに戻り値が含まれている場合のように、それを回避することができます。次に、戻り値(とにかく必要になる)のみを記録し、実行されたブランチに関する決定を戻り値自体から再計算するだけで十分です。


8

この質問は古いですが、ほとんどの答えも古いです。 興味深いトピックのままです、私は2015年の回答を投稿しています。私の修士論文の第1章と第2章では、コンピュータープログラミングの視覚的思考に向けたリバースデバッグとライブプログラミングの組み合わせで、リバースデバッグの歴史的なアプローチのいくつかを取り上げています(特にスナップショット(またはチェックポイント)とリプレイのアプローチに焦点を当てています)。それと全知デバッグの違いを説明します:

ある時点までプログラムを順方向に実行したコンピュータは、実際にそれに関する情報を私たちに提供できるはずです。このような改善は可能であり、いわゆる全知デバッガーに見られます。これらは通常、リバースデバッガーとして分類されますが、プログラマーが実行中のプログラムで実際に時間を遡ることができるのではなく、実行中に情報を記録して後で表示またはクエリするだけなので、「履歴ロギング」デバッガーとしてより正確に説明できます。 。「全知」は、記録されたプログラムの状態履歴全体が、実行後にデバッガーで利用できるという事実に由来します。その場合、プログラムを再実行する必要はなく、手動でコードを計測する必要もありません。

ソフトウェアベースの全知デバッグは、「デバッグ時間履歴再生」と呼ばれる1969年のEXDAMSシステムから始まりました。GNUデバッガーであるGDBは、2009年以来、「プロセスの記録と再生」機能を備えた全知デバッグをサポートしています。TotalView、UndoDB、およびChrononは、現在利用可能な最高の全知デバッガであるように見えますが、商用システムです。TODは、Javaの場合、部分的な決定論的再生、部分的なトレースキャプチャ、および関連する大量の情報の記録を可能にする分散データベースを利用する、最良のオープンソースの代替手段のようです。

記録のナビゲーションを許可するだけでなく、実際に実行時間を後退させることができるデバッガーも存在します。それらは、バックインタイム、タイムトラベル、双方向、またはリバースデバッガーとしてより正確に説明できます。

最初のそのようなシステムは1981年のCOPEプロトタイプでした...


4

mozillarrは、GDBリバースデバッグのより堅牢な代替手段です。

https://github.com/mozilla/rr

GDBの組み込みの記録と再生には厳しい制限があります。たとえば、AVX命令がサポートされていません。gdbの逆デバッグが「プロセスレコードはアドレスで命令0xf0dをサポートしていません」で失敗します。

rrの利点:

  • 現在、はるかに信頼性があります。いくつかの複雑なソフトウェアを比較的長時間実行してテストしました。
  • また、gdbserverプロトコルを備えたGDBインターフェースを提供し、優れた代替品になります
  • ほとんどのプログラムでパフォーマンスがわずかに低下しますが、測定を行わないと自分で気づきませんでした
  • 生成されたトレースは、非決定論的なイベントがほとんど記録されていないため、ディスク上で小さいです。これまでのところ、それらのサイズについて心配する必要はありませんでした。

rrは、スレッドスイッチなどのすべての非決定論的イベントで何が起こったかを記録する方法で最初にプログラムを実行することによってこれを実現します。

次に、2回目の再生実行中に、驚くほど小さいトレースファイルを使用して、元の非決定論的実行で発生したことを正確に再構築しますが、決定論的な方法で順方向または逆方向に再構築します。

rrはもともとMozillaによって開発され、翌日の夜間テストで発生したタイミングのバグを再現できるようにしています。ただし、実行中に数時間しか発生しないバグがある場合は、逆デバッグの側面も基本です。これは、前の状態が後の障害につながった原因を調べるために戻ることがよくあるためです。

次の例では、その機能の一部、特に展示reverse-nextreverse-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どちらもトレース/リプレイベースですが、機能とパフォーマンスの点でどのように比較されるかはわかりません。


dddでこれを行う方法を知っていますか?おかげで
spraff

@spraffよくわかりませんが、可能性があります。まず、dddをgdbserverに接続してみます。それが機能する場合は、rrでも機能するはずです。
CiroSantilli郝海东冠事病六四事件法轮功

1
ただし、@ spraffはdddを使用せず、gdbダッシュボードを使用します;-) stackoverflow.com/questions/10115540/gdb-split-view-with-code/…これは通常のGDBであるため、間違いなく機能します。
CiroSantilli郝海东冠事病六四事件法轮功

3

ネイサンフェルマンは書いた:

しかし、リバースデバッグでは、入力した次のコマンドとステップコマンドのみをロールバックできますか、それとも任意の数の命令を元に戻すことができますか?

命令はいくつでも元に戻すことができます。たとえば、前進するときに停止した地点でのみ停止することに制限されません。新しいブレークポイントを設定して、そのブレークポイントまで逆方向に実行できます。

たとえば、命令にブレークポイントを設定してそれまで実行させた場合、スキップした場合でも前の命令にロールバックできますか?

はい。ブレークポイントに到達する前に記録モードをオンにしている限り。


2
リバースソリューションの重要な部分は、ある時点でオンにし、そのポイントまでしかリバースできないことです。マシンを真逆に実行し、何が起こったかの何らかの記録なしに以前に何が起こったかを知ることができる魔法はありません。
jakobengblom2 2009年

2

これは、ODBと呼ばれる別のリバースデバッガーがどのように機能するかを示しています。エキス:

Omniscient Debuggingは、プログラム内の各「関心のあるポイント」(値の設定、メソッド呼び出しの作成、例外のスロー/キャッチ)で「タイムスタンプ」を収集し、プログラマーがそれらのタイムスタンプを使用してそのプログラム実行の履歴。

ODB ...は、プログラムがロードされるときにコードをプログラムのクラスに挿入し、プログラムの実行時にイベントが記録されます。

gdbも同じように機能すると思います。


では、これらの興味深い点がどこにあるかをコンパイラーとデバッガーに伝えるために、コード内にディレクティブが必要でしょうか?
ネイサンフェルマン

いいえ。www.LambdaCS.com/ debugger /debugger.htmlにJavaWeb Startデモがあり、その動作を示しています。通常のプログラムのようです。とにかくそれはODBです、gdbについては知りません。でもとてもかっこいいです:)
demoncodemonkey 2009

gdbソリューションはターゲットプログラムを変更しないことに注意してください。プログラムをデバッグするためにインストルメント化する必要がある場合は、タイミングの違いやその他の障害が原因で問題が解消される可能性がかなりあります。すべての商用revexecツールは、プログラム自体のコードを変更しない何らかの形式の外部レコードに基づいています。
jakobengblom2 2009年

@ jakobengblom2:ターゲットをメモリに書き込むことによる変更、実行のエミュレート、または単にハードウェアブレークポイントの追加の違いを強調しすぎていると思います。それらはすべてタイミングを変更します。実際、ターゲットインストルメンテーションはおそらくタイミングの変化が最も少ないでしょう。
Ben Voigt 2013

2

逆デバッグとは、プログラムを逆方向に実行できることを意味します。これは、問題の原因を突き止めるのに非常に役立ちます。

各ステップの完全なマシン状態を保存する必要はなく、変更のみを保存します。それはおそらくまだかなり高価です。


わかりましたが、変更を保存するには、変更のたびに実行を中断する必要があります。
ネイサンフェルマン

はい、それは正しいですが、マシンは現在非常に高速であり、人間の観点からは、減速が耐えられないと思います。valgrindに匹敵しますが、valgrindほど遅くはないかもしれません。
Michael Snyder
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.