ランタイム検証は、実行中のシステムから情報を抽出し、それを使用して特定の特性を満たす、または違反する観測された動作を検出し、場合によってはそれに対応することに基づく、コンピューティングシステムの分析および実行アプローチです。[ 1 ]データレースやデッドロックフリー などの非常に特殊な特性は、通常、すべてのシステムで満たされることが望まれ、アルゴリズム的に実装するのが最適である場合があります。その他の特性は、形式仕様としてより簡単に捉えることができます。ランタイム検証仕様は通常、有限状態機械、正規表現、文脈自由パターン、線形時相論理などのトレース述語形式、またはこれらの拡張で表現されます。これにより、通常のテストよりもアドホックではないアプローチが可能になります。ただし、テストオラクルや参照実装に対する検証を含め、実行中のシステムを監視するメカニズムはすべてランタイム検証とみなされます。形式要件仕様が提供されると、モニターはそれらから合成され、計測によってシステムに組み込まれます。ランタイム検証は、セキュリティや安全ポリシーの監視、デバッグ、テスト、検証、妥当性確認、プロファイリング、障害保護、動作変更(リカバリなど)といった様々な目的に使用できます。ランタイム検証は、 1つまたは少数の実行トレースのみを分析し、実際のシステムと直接やり取りすることで、モデル検査や定理証明といった従来の形式検証手法の複雑さを回避します。そのため、比較的高い拡張性を持ち、分析結果に対する信頼性も高まります(システムの形式的なモデリングという面倒でエラーが発生しやすい手順を回避できるため)。ただし、カバレッジは低くなります。さらに、ランタイム検証はリフレクティブ機能により、対象システムに統合することができ、展開中にその実行を監視およびガイドすることができます。
実行中のシステムやプログラムに対して、形式的または非形式的に指定されたプロパティをチェックすることは、古くからあるトピックです(ソフトウェアの動的型付けや、ハードウェアのフェイルセーフデバイスやウォッチドッグタイマーなどがその代表例です)。その正確な起源を特定するのは困難です。ランタイム検証という用語は、形式検証とテストの境界にある問題に対処することを目的とした2001年のワークショップ[ 2 ]の名前として正式に導入されました。大規模なコードベースの場合、テストケースを手動で記述することは非常に時間がかかることが判明しています。さらに、開発中にすべてのエラーを検出できるわけではありません。自動検証への初期の貢献は、NASAエイムズ研究センターのクラウス・ハヴェルンドとグリゴレ・ロスによって、宇宙船、探査車、航空電子機器技術の高い安全基準を達成するために行われました。[ 3 ]彼らは、単一の実行パスを分析することによって、時相論理の仕様を検証し、 Javaプログラムの競合状態とデッドロックを 検出するツールを提案しました。
現在、ランタイム検証技術は、ランタイム監視、ランタイムチェック、ランタイムリフレクション、ランタイム分析、動的分析、ランタイム/動的シンボリック分析、トレース分析、ログファイル分析など、さまざまな別名で呼ばれることが多い。これらはすべて、同じ高レベルの概念を異なる分野に適用したり、異なるコミュニティの研究者が適用したりしている事例を指している。ランタイム検証は、展開前に使用されるテスト(特にモデルベーステスト)や、展開中に使用されるフォールトトレラントシステムなど、他の確立された分野と密接に関連している。
ランタイム検証という広範な分野において、以下のようないくつかのカテゴリを区別することができる。

実行時検証方法の広範な分野は、次の3つの次元で分類できます。[ 9 ]
それにもかかわらず、実行時検証の基本的なプロセスは同様である。[ 9 ]
以下の例では、執筆時点(2011年4月)で複数のランタイム検証グループによって、多少の差異はあるものの検討されてきたいくつかの単純なプロパティについて説明します。より興味深いものにするために、以下の各プロパティは異なる仕様形式を使用しており、すべてパラメトリックです。パラメトリックプロパティとは、パラメトリックイベントで形成されるトレースに関するプロパティであり、パラメトリックイベントとは、データをパラメータにバインドするイベントです。ここで、パラメトリックプロパティは次の形式をとります。、 どここれは、一般的な(インスタンス化されていない)パラメトリックイベントを参照する、適切な形式体系における仕様です。このようなパラメトリック特性の直感は、によって表現される特性です。観測されたトレースで(パラメトリック イベントを通じて)遭遇したすべてのパラメータ インスタンスに対して、この条件が満たされている必要があります。以下の例はいずれも特定のランタイム検証システムに特化したものではありませんが、パラメータのサポートが必要であることは明らかです。以下の例では Java 構文が前提とされており、"==" は論理等価性、"=" は代入を表します。一部のメソッド(update()UnsafeEnumExample など)は、Java API の一部ではないダミー メソッドであり、説明を分かりやすくするために使用されています。

Java Iteratorインターフェイスでは、メソッドが呼び出されるhasNext()前に、メソッドが呼び出されて true を返す必要がありますnext()。これが起こらない場合、ユーザーがコレクションの「末尾を超えて」反復処理してしまう可能性が非常に高くなります。右の図は、実行時検証によってこのプロパティをチェックおよび強制するためのモニタを定義する有限状態機械を示しています。不明なnext()状態からメソッドを呼び出すことは、そのような操作が安全でない可能性があるため、常にエラーになります。hasNext()が呼び出されてtrue を返す場合、を呼び出すのは安全なnext()ので、モニタはmore状態に入ります。ただし、メソッドがfalse をhasNext()返す場合、要素はもうなく、モニタはnone状態に入ります。more 状態と none 状態では、メソッドを呼び出しても新しい情報は得られません。more 状態からメソッドを呼び出すのは安全ですが、要素がさらに存在するかどうかは不明になるため、モニタは最初のunknown状態に戻ります。最後に、 none状態からメソッドを呼び出すと、エラー状態に入ります。以下は、パラメトリック過去時間線形時相論理を使用したこのプロパティの表現です。hasNext()next()next()
この式は、next()メソッドの呼び出しの直前に、hasNext()true を返すメソッドの呼び出しがなければならないことを示しています。ここでのプロパティは、イテレータでパラメトリックですi。概念的には、これはテスト プログラム内の可能なイテレータごとにモニターのコピーが 1 つ存在することを意味しますが、ランタイム検証システムはパラメトリック モニターをこのように実装する必要はありません。このプロパティのモニターは、式に違反したとき (つまり、有限状態機械がエラーnext()状態に入ったとき) にハンドラをトリガーするように設定されます。これは、を最初に呼び出さずにが呼び出された場合hasNext()、またhasNext()はがの前に呼び出されたがfalse がnext()返された場合に発生します。

Java のVectorクラスには、要素を反復処理するための 2 つの方法があります。前の例で示したように Iterator インターフェイスを使用する方法と、Enumerationインターフェイスを使用する方法です。Iterator インターフェイスには remove メソッドが追加されていますが、主な違いは、Iterator は「fail fast」であるのに対し、Enumeration はそうではない点です。つまり、Iterator を使用して Vector を反復処理しているときに、(Iterator の remove メソッドを使用する以外の方法で) Vector を変更すると、ConcurrentModificationExceptionがスローされます。しかし、前述のように、Enumeration を使用する場合はそうではありません。Enumeration の観点から Vector が矛盾した状態になるため、プログラムから非決定的な結果が生じる可能性があります。Enumeration インターフェイスをまだ使用しているレガシー プログラムでは、基となる Vector が変更されたときに Enumeration が使用されないように強制したい場合があります。この動作を強制するには、次のパラメトリック正規パターンを使用できます。
このパターンは、列挙型とベクトルの両方でパラメトリックです。直感的には、そして上記のように、ランタイム検証システムはパラメトリックモニタをこのように実装する必要はありませんが、このプロパティのパラメトリックモニタは、ベクトルと列挙型の可能なペアごとに非パラメトリックモニタインスタンスを作成して追跡すると考えることができます。一部のイベントは、複数のモニタに同時に関係する場合があります。たとえばv.update()、ランタイム検証システムは、(概念的に)それらをすべての関心のあるモニタにディスパッチする必要があります。ここでは、プロパティが指定されているため、プログラムの悪い動作を示します。したがって、このプロパティは、パターンに一致するかどうかを監視する必要があります。右の図は、このパターンに一致し、プロパティに違反するJavaコードを示しています。ベクトルvは、列挙型eが作成された後で更新され、その後eが使用されます。
前述の2つの例は有限状態の特性を示していますが、実行時検証で使用される特性ははるかに複雑になる可能性があります。SafeLock特性は、特定のメソッド呼び出し内で、(再入可能な)Lockクラスの取得と解放の回数が一致するというポリシーを強制します。もちろん、これはロックを取得したメソッド以外でのロックの解放を禁止しますが、これはテスト対象システムが達成すべき望ましい目標である可能性が非常に高いです。以下に、パラメトリックなコンテキストフリーパターンを使用したこの特性の仕様を示します。

このパターンは、各スレッドとロックに対して、入れ子になった開始/終了と取得/解放のペアのバランスの取れたシーケンスを指定します(は空のシーケンスです。ここで、begin と end は、プログラム内のすべてのメソッドの開始と終了 (acquire と release の呼び出しを除く) を指します。メソッドの開始と終了を関連付ける必要があるのは、それらが同じスレッドに属している場合のみであるため、これらはスレッドでパラメトリックです。acquire と release イベントも、同じ理由でスレッドでパラメトリックです。さらに、ある Lock のリリースを別の Lock の取得に関連付けたくないため、Lock でもパラメトリックです。極端な場合、Thread と Lock の可能な組み合わせごとに、プロパティのインスタンス、つまりコンテキストフリー解析メカニズムのコピーが存在する可能性があります。これも直感的に理解できます。実行時検証システムが同じ機能を異なる方法で実装する可能性があるためです。たとえば、システムに Threads がある場合、、、 そしてロック付きそしてすると、ペアのプロパティ インスタンスを維持する必要が生じる可能性があります <、>, <、>, <、>, <、>, <、>、および <、このプロパティは、パターンが正しい動作を指定しているため、パターンに一致しない場合は監視する必要があります。右の図は、このプロパティに違反する2つのトレースを示しています。図の下方向のステップはメソッドの開始を表し、上方向のステップは終了を表します。図中の灰色の矢印は、同じロックの取得と解放の一致を示しています。簡略化のため、トレースには1つのスレッドと1つのロックのみが表示されています。
ランタイム検証に関する研究のほとんどは、以下に挙げるトピックの1つ以上を扱っています。
実行中のシステムを監視すると、通常は実行時にオーバーヘッドが発生します(ハードウェアモニタの場合は例外となる場合があります)。特に生成されたモニタがシステムとともに展開される場合は、実行時検証ツールのオーバーヘッドを可能な限り削減することが重要です。実行時オーバーヘッドを削減する手法には、以下のようなものがあります。
i.next()i.hasnext()形式的なアプローチ全般における主要な実用上の障害の一つは、ユーザーが仕様書の読み書き方法を知りたがらない、あるいは学びたくないという点である。デッドロックやデータ競合などの仕様書は暗黙的に存在する場合もあるが、ほとんどの場合は作成する必要がある。さらに、特に実行時検証の文脈では、既存の仕様記述言語の多くが、意図した特性を十分に表現できないという問題もある。
ランタイム検証ツールのエラー検出能力は、実行トレースの分析能力に大きく依存します。モニターがシステムとともに展開される場合、ランタイムオーバーヘッドを低く抑えるため、計測は通常最小限に抑えられ、実行トレースも可能な限り単純化されます。テストにランタイム検証を使用する場合は、より包括的な計測が可能となり、イベントに重要なシステム情報を付加することで、モニターが実行システムのより洗練されたモデルを構築し、分析できるようになります。例えば、イベントにベクトルクロック情報、データフロー情報、制御フロー情報を付加することで、モニターは実行システムの因果モデルを構築できます。このモデルでは、観測された実行はあくまでも可能なインスタンスの一つに過ぎません。モデルと整合性のあるイベントの他の組み合わせは、システムの実行可能なシナリオであり、異なるスレッドインターリーブで発生する可能性があります。このように推論された実行におけるプロパティ違反を(監視することで)検出することで、モニターは観測された実行では発生しなかったものの、同じシステムの別の実行で発生する可能性のあるエラーを予測できるようになります。重要な研究課題の一つは、可能な限り多くの他の実行トレースを含む実行トレースからモデルを抽出することである。
テストや徹底的な検証とは異なり、ランタイム検証は、再構成、マイクロリセット、あるいはチューニングやステアリングと呼ばれるよりきめ細かな介入メカニズムを通じて、検出された違反からシステムが回復することを可能にするという利点があります。これらの技術をランタイム検証の厳格なフレームワーク内で実装すると、新たな課題が生じます。
ランタイム検証の研究者たちは、プログラム計測をモジュール的に定義する手法として、アスペクト指向プログラミング(AOP )の可能性に着目しました。アスペクト指向プログラミング(AOP)は一般的に、横断的な関心のモジュール化を促進します。ランタイム検証はまさにそのような関心の一つであり、AOPの特定の特性から恩恵を受けることができます。アスペクト指向のモニタ定義は大部分が宣言的であるため、命令型プログラミング言語で記述されたプログラム変換によって表現される計測よりも、推論が容易になる傾向があります。さらに、すべての計測が単一のアスペクト内に含まれているため、静的解析では、他の形式のプログラム計測よりも、監視アスペクトについて推論しやすくなります。そのため、現在の多くのランタイム検証ツールは、表現力豊かな高レベル仕様を入力として受け取り、何らかのアスペクト指向プログラミング言語(AspectJなど)で記述されたコードを出力として生成する仕様コンパイラの形で構築されています。
実行時検証は、証明可能な正しいリカバリコードと組み合わせて使用すれば、プログラム検証のための非常に貴重なインフラストラクチャを提供し、後者の複雑さを大幅に軽減できます。たとえば、ヒープソートアルゴリズムを形式的に検証することは非常に困難です。これを検証するためのより簡単な手法は、ソート対象の出力を監視し(線形複雑度モニタ)、ソートされていない場合は、挿入ソートなどの簡単に検証できる手順を使用してソートすることです。結果として得られるソートプログラムは、より簡単に検証できます。ヒープソートに求められるのは、マルチセットと見なされる元の要素を破壊しないことだけであり、これははるかに簡単に証明できます。反対の方向から見ると、形式検証を使用して実行時検証のオーバーヘッドを削減できます。これは、形式検証の代わりに静的解析を使用する場合にすでに述べたとおりです。実際、完全に実行時検証された、おそらく低速なプログラムから始めることができます。次に、コンパイラが静的解析を使用して型の正しさやメモリの安全性の実行時チェックを解除するのと同様に、形式検証(または静的解析)を使用してモニタを解除できます。
従来型の検証手法と比較すると、ランタイム検証の大きな欠点は、カバレッジが低いことです。ランタイムモニタがシステムとともに(プロパティ違反時に実行される適切なリカバリコードとともに)展開されている場合は問題ありませんが、システムのエラー検出にランタイム検証を使用する場合は、その有効性が制限される可能性があります。エラー検出目的でランタイム検証のカバレッジを高めるための手法には、以下のようなものがあります。