標準ベースの開発手法を使用する必要がある理由(必要がない場合でも)
業界全体が、機能安全、セキュリティ、およびIEC 61508、ISO 26262、IEC 62304、MISRA C、CWEなどのコーディング標準によって支持されている検証および妥当性確認の実践を中心に発展してきました。もちろん、特にソフトウェアがこれらの標準の厳格さを満たす必要がない場合は、これらの標準が推進する正式なプロセスと方法論に従う義務があるわけではありません。ただし、標準はベストプラクティスを支持しています。これは、高品質で信頼性が高く、堅牢なソフトウェアを実現するための最も効果的な方法を経験が示しているためです。
これらの標準に準拠したベストプラクティスの開発手法は、エラーが最初からコードに導入されないようにするのに役立ち、市場投入までの時間を遅らせてコストを追加する可能性のある広範なデバッグアクティビティの必要性を減らします。もちろん、すべての開発者が、航空宇宙、自動車、または医療機器業界で見られるアプリケーションに提供される時間と予算の余裕を持っているわけではありません。ただし、それらが展開する手法は、重要度がそれらの使用を強制するかどうかに関係なく、開発チームにとって大きな潜在的利益をもたらすツールボックスを表しています。
エラーの種類とそれらに対処するためのツール
ソフトウェアには2つの主要なタイプのエラーがあり、エラーの発生を防ぐためのツールを使用して対処できます。
- コーディングエラー。例として、配列の境界外にアクセスしようとするコードがあります。この種の問題は、静的分析を実行することで検出できます。
- アプリケーションエラー。これらは、アプリケーションが何をすべきかを正確に知ることによってのみ検出できます。つまり、要件に照らしてテストする必要があります。
コーディングエラーとコードレビュー
静的分析は、特にプロジェクトの開始から展開される場合に、コーディングのバグを検出するための効果的な手法です。コードが分析されると、表示できるさまざまなタイプの結果があります。コードレビューでは、この記事で焦点を当てるMISRA C:2012などのコーディング標準に対してコードをチェックします。
理想的には、Adaなどの安全な言語がすべての組み込みプロジェクトに使用されます。 Adaには、エラーを自然に減らす思考プロセスを実施するための多数の特性が含まれています(たとえば、厳密な型付けなど)。残念ながら、Adaの知識と経験を持つプログラマーを見つけるのは難しいため、大多数の企業は代わりにCやC ++を使用しています。ただし、これらの言語は、経験豊富な開発者にとっても落とし穴があります。幸い、コードレビューを実行することで、これらの潜在的な落とし穴のほとんどを回避できます。
コードの欠陥を回避する最善の方法は、コードの欠陥を回避することです。これは当たり前のように聞こえますが、これはまさにコーディング標準が行うことです。 CおよびC ++の世界では、ソフトウェアの欠陥の約80%は、言語の約20%の誤った使用によって引き起こされます。問題があることがわかっている言語の部分を回避するために言語の使用が制限されている場合、欠陥が回避され、ソフトウェアの品質が大幅に向上します。
C / C ++プログラミング言語での失敗の基本的な言語関連の原因は、未定義の動作、実装定義の動作、および未指定の動作です。これらの動作は、ソフトウェアのバグやセキュリティの問題につながります。
実装定義の動作の例として、符号付き整数が右にシフトされたときの上位ビットの伝播を考えてみます。結果は0x40000000または0xC0000000ですか?
図1:一部のCおよびC ++構造の動作は、使用するコンパイラによって異なります。 (出典:LDRA)
答えは、使用しているコンパイラによって異なります(図1)。どちらかである可能性があります。関数の引数が評価される順序は、C言語では指定されていません。図2に示すコードでは、 rollDice() 関数は、値「1、2、3、および4」を保持する循環バッファーから次の値を読み取るだけです。期待される戻り値は1234です。ただし、その保証はなく、少なくとも1つのコンパイラーが次の値を返すコードを生成します。値3412。
図2:一部のCおよびC ++構造の動作は、言語によって指定されていません。 (出典:LDRA)
C / C ++言語には、このような多くの落とし穴がありますが、コーディング標準を使用することで、これらの未定義、未指定、および実装定義の動作を回避できます。同様に、 goto などの構成の使用 または malloc 欠陥につながる可能性があるため、コーディング標準を使用して、これらの構成が使用されないようにすることができます。符号付きの値と符号なしの値を混在させると多くの問題が発生します。ほとんどの場合問題は発生しませんが、符号付きの値がオーバーフローして負になる場合があります。
コーディング標準は、コードが特定のスタイルで記述されていることを確認することもできます。たとえば、タブ文字が使用されていないこと、インデントが特定のサイズであること、または括弧が特定の位置に配置されていることを確認します。手動によるコードレビューが必要になるため、これは重要です。タブ文字のサイズが異なる別のエディターでコードを表示すると、奇妙なレイアウトにより、レビュー担当者はコードのレビューに集中できなくなります。
一部の開発者は、非常に効率的でコンパクトであるが、不可解で複雑である可能性があり、他の人が理解するのを困難にする「賢い」コードを書くことに罪を犯しています。シンプルに保ち、コンパイラに効率的なバイナリの作成を任せる方がはるかに優れています。繰り返しになりますが、コーディング標準を使用すると、開発者が文書化されていない過度に複雑なコードを作成するのを防ぐことができます。
最もよく知られているプログラミング標準は、おそらくMISRA標準であり、1998年に自動車業界向けに最初に公開されました。これらの標準の人気は、ある程度のMISRAチェックを提供する組み込みコンパイラの数に反映されています。 MISRAの最新バージョンはMISRAC:2012で、前のバージョンのほぼ2倍のページ数になっています。この追加のドキュメントのほとんどは、各ルールが存在する理由に関する有用な説明と、そのルールのさまざまな例外の詳細で構成されています。 MISRAにはいくつかのガイドラインがあり、該当する場合は、標準または未定義、未指定、および実装定義の動作への参照が含まれています。この例を図3に示します。
図3:MISRA Cは、未定義、未指定、および実装定義の動作を参照しています。 (出典:LDRA)
MISRAガイドラインの大部分は「決定可能」です。これは、ツールが違反の有無を識別できる必要があることを意味します。ただし、いくつかのガイドラインは「決定不能」です。つまり、ツールが違反の有無を推測できるとは限りません。この例は、初期化されていない変数が、初期化する必要のあるシステム関数に出力パラメーターとして渡される場合です。ただし、静的分析がシステム関数のソースコードにアクセスできない限り、その関数が変数を初期化する前にその変数を使用しているかどうかを知ることはできません。単純なMISRAチェッカーを使用すると、この違反が報告されない可能性があり、偽陰性につながる可能性があります。または、MISRAチェッカーが不明な場合は、違反を報告し、誤検知につながる可能性があります。何が一番いいですか?問題があるかもしれないことを知りませんか?または、間違いなく問題がないことを確認するために時間を費やす場所を正確に知っていますか?確かに、偽陰性よりも偽陽性の方が望ましいです。
2016年4月、MISRA委員会はMISRA C:2012の修正を発行し、MISRAがセーフティクリティカルなソフトウェアだけでなくセキュリティクリティカルなソフトウェアにも適用できるようにするための14のガイドラインを追加しました。これらのガイドラインの1つは指令4.14でした。これは、図4に示すように、未定義の動作による落とし穴を防ぐのに役立ちます。
図4:MISRAとセキュリティに関する考慮事項。 (出典:LDRA)
アプリケーションエラーと要件テスト
アプリケーションのバグは、製品が想定どおりに機能することをテストすることによってのみ見つけることができます。つまり、要件があることを意味します。アプリケーションのバグを回避するには、適切な製品を設計することと、製品を適切に設計することの両方が必要です。
適切な製品を設計するということは、要件を事前に確立し、要件とソースコード間の双方向のトレーサビリティを確保して、すべての要件が実装され、すべてのソフトウェア機能が要件にまでさかのぼることを意味します。不足している機能や不要な機能(要件を満たしていない)も、アプリケーションのバグです。製品の権利を設計することは、開発されたシステムコードがプロジェクトの要件を満たしていることを確認するプロセスです。これは、要件ベースのテストを実行することで達成できます。
図5に双方向トレーサビリティの例を示します。この簡単な例では、単一の機能が選択されており、上流のトレーサビリティは、機能から低レベルの要件、高レベルの要件、最後にシステムレベルの要件へと強調表示されています。
図5:機能が選択された双方向のトレーサビリティ。 (出典:LDRA)
図6では、高レベルの要件が選択されており、強調表示により、システムレベルの要件に対するアップストリームのトレーサビリティと、低レベルの要件およびソースコード機能に対するダウンストリームのトレーサビリティの両方が示されています。
クリックすると拡大画像が表示されます
図6:要件が選択された双方向のトレーサビリティ。 (出典:LDRA)
トレーサビリティを視覚化するこの機能は、ライフサイクルの早い段階でトレーサビリティの問題(アプリケーションのバグ)を検出することにつながる可能性があります。
コード機能をテストするには、コードの機能を認識する必要があります。つまり、各機能の機能を説明するための低レベルの要件が必要です。図7は、低レベルの要件の例を示しています。この場合、単一の機能を完全に説明しています。
図7:低レベルの要件の例。 (出典:LDRA)
テストケースは、表1に示すように、低レベルの要件から導き出されます。
表1:低レベルの要件から派生したテストケース。 (出典:LDRA)
単体テストツールを使用して、これらのテストケースをホストまたはターゲットで実行し、コードが要件に従って動作することを確認できます。図8は、すべてのテストケースがリグレッションされて合格したことを示しています。
図8:単体テストの実行。 (出典:LDRA)
テストケースが実行されたら、すべてのコードが実行されたことを確認するために、構造カバレッジを測定する必要があります。カバレッジが100%でない場合は、より多くのテストケースが必要であるか、削除する必要のある余分なコードがある可能性があります。
結論
ソフトウェアの複雑さが増すにつれて、潜在的なソフトウェアエラーも増加します。ベストプラクティスの開発手法は、これらのエラーの発生を防ぐのに役立ちます。ベストプラクティスの開発は、MISRA C:2012などの最先端のコーディング標準の使用、コードのメトリックの測定、要件のトレース、および要件ベースのテストの実装で構成されます。基準を満たす義務がない場合にこれらの手法がどの程度適用されるかは、明らかに開発チームの裁量に委ねられています。ただし、これらの標準は、品質、信頼性、および堅牢なソフトウェアを実現するための最も効果的な方法であると経験から述べられているため、これらの方法を支持しています。そして、製品がセーフティクリティカルであるかどうかにかかわらず、それは確かにその開発チームにのみ有益な結果になります。
埋め込み