シミュレートされた時間を回す



私たちの多くは、アプリケーションをデバッグするときに、現在の行から1つ(または2、10 ...)戻って定期的に戻り、その行で発生する不正な動作の原因を確認したいと思っています。 ほとんどの場合、最初からデバッグを再開し、前回の試行よりも少し早くプログラムの実行を停止する必要があります。 次に、問題のある提案された場所に一歩ずつアプローチする必要があります...おっと、私たちは再びステップオーバーしました! デバッガーでは1ステップでも戻ることができないため、最初からやり直します。 それとも可能ですか?



以前の投稿の 1つで、デジタルシステムのソフトウェアシミュレーションを使用して、現実には不可能なこと、つまりデバッグの時間の流れを逆にする方法を説明する予定でした。 これについては、この記事で説明します。





写真:Ivan Andreev





まず、いくつかの一般的な考慮事項。 なぜあなたは直接のもの、すなわち 「通常の」プログラム実行? 手動で実行するのが望ましくない/難しい/不可能な計算結果を取得する。 ロケットの飛行の計算は計算です。 サイト上のシールの表示は計算です。 計算プロセスは、アルゴリズムの形式で提示することで形式化できます。



なぜ逆パフォーマンス



なぜ逆実行が必要なのでしょうか? プログラム内の計算がどのように特定の結果につながったかを理解するために、正しいか間違っているか。



プログラマが使用するさまざまなツールにプログラムの流れを逆にする機能のサポートを含めることができます。 さらに、主にシミュレーターに焦点を当てます。 これに関連して、このアイデアには2つの開発オプションがあります。 1つ目は、シミュレートされたBIOS /ファームウェア、OS、アプリケーション、モデル内で実行されているコードのデバッグです。これは、シミュレータのエンドユーザーであるソフトウェア開発者にとって興味深いものです。 2番目のアプリケーションは、シミュレーター自体をデバッグしています(これもプログラムです)。 これは、デバイスモデルの開発者にとって重要です。



デバッグ



何らかの形式で記述されたアルゴリズム(従来の用語では)の実行は、高水準言語演算子、機械命令、チューリング機械のキャリッジ移動など、別々のステップに分割できます。 デバッグプロセスは、アルゴリズムの実装でステップを見つけることから始まります。このステップでは、プログラマが予期したとおりに何かがうまくいきませんでした。 原則として、既知の2つの実行ポイントがあります。1つ目では、状態はまだ「良好」で、期待されています。2つ目では、不正な動作または状態がすでに観察されています。









既知の「良い」状態と「悪い」状態のポイント間の距離を1ステップに最小化する必要があります。 このプロセスはさまざまな方法で実行できます。 たとえば、アルゴリズムの最初から開始するたびに、線形方式ですべてのステップを線形に繰り返します。 これは、セグメント自体が非常に短い場合にのみ正当化されます。









各アプローチでセグメントの長さを半分に減らして、二分法に頼ることができます。







この場合の反復回数は、線形列挙の場合よりも大幅に少なくなります。 ただし、毎回プログラムを再起動する必要があります。 まあ、もしこのバグが開始後すぐに現れたら。 しかし、それが発生する前に数時間経過するとどうなりますか? 生産性のデバッグには、まだ多くのことが望まれています。



シミュレーション内でのデバッグ



デジタルシステムのシミュレーションはデバッグにどのように役立ちますか? 以下に示すように、互いに密接に関連する2つの方法を選びます。



セーブポイントを作成します。 シミュレータを使用すると、すべてのデバイスモデルの状態(メモリ、CPUレジスタ、デバイスの内容)をファイルとしてディスクに保存できます。 次に、そのようなセーブポイント (英語のチェックポイントまたはセーブポイント)から、実行スクリプトを繰り返し復元して、プログラムの開始から対象領域までの「実行」時間を節約できます。 これを読んでいるコンピューターゲームプレーヤーは、難しいレベルを最初からやり直さないために、保存メカニズムを繰り返し使用していると確信しています。

郵便2
男:「祖母があなたと同じくらい生き残っていたら、このゲームにも合格できたでしょう!」





時間反転。 検索中に問題が発生し、すでに問題が発生しているポイントを越えたことが判明した場合、保存ポイントからシミュレーションを再開する必要があります。 数ステップ前に戻ることができれば、それは素晴らしいことです! これにより、プロセスがさらに加速されます。 この場合、エラーのあるステップの検索は次のようになります。









シミュレーション要件



現実世界のすべてのプロセスを元に戻すことはできません。 歯磨き粉をチューブに押し戻そうとした人は誰でもこれを確認します。 したがって、これはすべてのシミュレーションで可能というわけではありません。多くの保証を提供することを目的として、参加ソフトウェアモデルとその著者の努力からいくつかのプロパティが必要です。



1.モデルの状態を検査する機能


後続のリカバリに適したセーブポイントを作成するには、シミュレーションに含まれる各デバイスモデルが、その内部状態を何らかの形式で十分詳細に提示できる必要があります。 復元中のアーキテクチャの状態(レジスタ、メモリ)に加えて、個々のデバイスがどのように互いに接続されているかを知る必要があります。



モデルの状態の一部の要素は、それが表すデバイスのアーキテクチャを反映していない場合は保存できず、将来明確に復元できる可能性があります。 たとえば、シミュレータキャッシュの内容(シミュレートされたデバイスのキャッシュではありません!)は、モデルの動作を高速化するためにのみ使用されるため、保存時にほとんどの場合破棄されます。 さらに、シミュレーションを復元する場合、復元する前に、そのようなキャッシュにモデルのアーキテクチャ状態に関連する古い情報が含まれていないことを確認する必要があります。



2.確定的


この奇妙な言葉は、同じ初期状態での2つの実行に対する特定のプロセスの再現性を意味します。 シングルスレッドプログラムの場合、外部のランダムシーケンスジェネレーターを使用せずに記述され、「初期化されていないデータへのアクセス」などのエラーがない場合、再現性は期待されるプロパティと見なされます。 並行アプリケーションの場合、再現性も可能ですが、特に注意深いプログラミングが必要です。 ソフトウェアパラレルシミュレータについては、 ここで条件の詳細を説明しました



確定性とセーブポイントの組み合わせにより、逆実行が可能になります。



仕組み



アイデアはとてもシンプルです。 直接実行のプロセスでは、シミュレーターは定期的にセーブポイントを作成します(必ずしもディスク上ではなく、メモリーに保存することもできます。つまり、スナップショット、英語のスナップショットです)。 数ステップをロールバックする必要がある場合は、最初に目的の位置に最も左のポイントが復元され、そこから必要な場所への直接実行が行われます。 その後、モデルの決定論により、その状態はシミュレートされた時間が実際にロールバックしたように見えます。



シミュレーション状態がTsim = 100のポイントにあると仮定します(時間はアルゴリズムのステップで測定されます。測定、命令、またはその他の個別の単位である可能性があります)。 この時点から、20ステップ前の時間をシミュレートする必要があります。 同時に、最も近い保存はTs = 50の瞬間に行われました。

  1. セーブポイントが復元されます。 その後、Tsim = Ts = 50。
  2. 30ステップの直接シミュレーションがあります。 その後、Tsim = 80。










機能とトレードオフ



シミュレーション中のデバッグを高速化するツールとしての逆実行の実際的な価値は、スナップショットの頻度に依存します。 頻繁に実行されると、逆実行はより速く動作します。 この場合、直接シミュレーションは、その状態を維持するために頻繁に中断する必要があり、速度に悪影響を及ぼします。 さらに、ポイント自体はメモリまたはディスクのどこかに保存する必要があり、リソースは限られています。

ここで、逆実行を作成するプログラマは、あらゆる種類のヒューリスティックを実装する多くの機会を作成します。増分保存ポイント、「古い」ポイントの削除、画像間の間隔の変更、スペースを節約するための透過的なデータ圧縮など。



再現性との戦い



シミュレーションに必要なすべてのデータが既にその中に含まれている場合、つまり、上記の2つの要件で十分です。 彼女は自給自足です。 しかし、作業の過程で、シミュレーションが外界と相互作用することがしばしば判明しますが、これはまったく再現性の傾向がありません。

そのような「ユニークな」外部ソースの例は、人です。 つまり、オペレーターはコンピューターと対話し、必要に応じてキーボードのボタンを押します。 入力データの再現性について話すことはできません。 個々のクリック間の時間間隔の長さが一定であることを期待する理由はありません。 ただし、多くのシミュレーションシナリオでは入力の再現性が重要です。割り込みの順序が異なると、OSコードとユーザーアプリケーションの実行がまったく異なります。 別のソースは、実際のネットワークとシミュレートされたネットワークに共通の基盤がある状況でのネットワークパケットです。

解決策は、最初の直接シミュレーション中にすべての外部ソースからのデータフローパスを記録することです。 後続の複製では、外部イベントは現実からではなく、このトラックから取得されます。 ユーザー/ネットワークは入力システムから切断されています。



実用的な実装



シミュレーター


もう一度強調します。シミュレーターが1)セーブポイントを作成でき、2)決定論的であれば、逆実行が可能です。 モデルが外界と相互作用する必要がある場合、トレースを記録することにより、その固有の不確定性を「補償」できます。



有名なシミュレータの多くは、セーブポイント/スナップショットを作成できます。 ほんの数例:Oracle VirtualBox、Qemu、Bochs、FCEUX(NES / Dendy emulator)。 仮想マシンの場合、ホストシステム間の移行を容易にするためにスナップショットがよく使用されます。

シミュレーションの確定性は、非常に少数のソフトウェアシミュレータによって保証されます。 オープンソースプロジェクトから、FCEUXだけが思い浮かびます。 コメントで他の例を指摘していただければ幸いです。



この記事で説明されている逆実行メカニズムは、フルプラットフォームの機能シミュレータWind River Simicsに実装されています。 次のビデオでは、LinuxカーネルをSimics内にロードする方法を示します。次に、逆実行を使用して、シミュレーション状態を過去の時点に戻します[ビデオの文字を解析するには、720p設定で視聴することをお勧めします ]。





クラシックデバッガー


時間反転は、デバッグのニーズに適用できるかなり一般的なアイデアであるため、疑いなく、古典的なデバッガーで実装できます。 デバッグ時反転オプションは、RogueWave TotalViewデバッガー( 記事 )で使用できますが、個人的に使用したことはありません。

読者が同様の何かをサポートする他のツールについての情報を持っている場合、コメントでこれについて書いてください。



PS私自身は、GDBにこのような機能が登場するのを楽しみにしています。 そして、 希望もあるようです。



All Articles