システムテストの必須項目(1)・メモリリークテスト

2021年9月1日テスト品質

組み込みソフトにメモリリークテストは必須です

組み込み系ソフトにとってメモリリークテストの実施は重要です。メモリリークとは、動的メモリの残量が徐々に減っていき最終的に残量がゼロになって、その後は動的メモリを必要とする機能が動かなくなるようなバグです。このメモリリークのバグを見付けるには、①動的メモリの残量を確認し、②動的メモリを使う処理を大量に実行し、③再度動的メモリの残量を確認する、という3つのステップが必要です。

メモリリークのバグは、一旦市場に漏れ出してしまうと多くの装置で不具合を引き起こして大きな問題となってしまう時限爆弾バグです。ところがメモリリークのバグは、長時間エージングや高負荷エージング等の安定性を確認する通常のテストでは見つける事が難しいので、メモリリークの検出を目的とした専用のメモリリークテストはリリース前の必須のテストなのです。

この記事では、グータラ親父がこれまで経験してきた事例を元に、メモリリークの仕組みとメモリリークのバグを見付ける具体的な方法について紹介します。

メモリリークテストで時限爆弾バグを見付け出す

このブログでもあちこちで紹介していますが、時限爆弾バグは 24時間 365日 安定に動作し続ける事が求められる組み込みソフトにとって大敵です。時限爆弾バグの原因として最も多いのがメモリリークなので、組み込みソフトの出荷前には漏れなくメモリリークテストを実施しておきましょう。

メモリリークはプログラムが使うメモリを、「必要になった時に OS から獲得し使い終わったら OS に返却する」という、動的メモリ (Dynamic Memory) を利用する時に注意が必要なバグです。 unix  系の OS  の場合に有名な動的メモリの獲得/返却の関数が malloc/free ですね。メモリリークについてはネット上にも色々と情報が在りますので詳しくはそちらを見て頂くのが判り易のですが、簡単に言うと獲得したメモリの返却を忘れてしまい、OS が提供できる動的メモリの残量が減っていく事をメモリリークと呼びます。

メモリリークの一例を簡単に紹介しておきましょう

OS は動的メモリの機能を提供するために起動時に予め一定量のメモリを確保しておきます、これをメモリプールと呼ぶ事にします。そして、アプリソフトから malloc 関数を使って動的メモリの獲得の要求があると、OSは メモリプールから必要な量のメモリを切り出してアプリソフトに貸し出します。貸し出したメモリの量だけメモリプールの残量が減りますが、アプリソフトが使い終わったメモリを free 関するを使って動的メモリを返却すればメモリプールの残量は回復します。

ところが、アプリソフトにバグがあって malloc 関数で獲得したメモリを使った後に free 関数で返却するのを忘れてしまうとどうなるでしょうか? OS から見ると貸し出したメモリが返却されないので、メモリプールの残量は減ったままです。 バグのあるアプリソフトが呼ばれるたびに free 関数での返却忘れが繰り返されると、OS のメモリプールの残量がどんどん減っていきます。 このメモリプールの残量が減っていく状態の事を、バケツの底に穴が開いて貯めた水が漏れだしている状態に例えて、メモリが漏れているつまりメモリリークが起きていると表現しています。(英語で漏れるはリーク(LEAK)です)メモリリークが続くとやがてはメモリプールの残量がゼロになってメモリ枯渇の状態になってしまい、mallco 関数でメモリを獲得しようとしていたアプリソフトが必要な動的メモリを獲得できなくなり、正常に動作できなくなってしまいます。

メモリリークはなぜ厄介な時限爆弾バグになるのか

メモリリークが厄介な時限爆弾バグになるのはリリース前の社内テストで見つけにくいからです。ソフトのバグは何等かの発生条件が揃う事で不具合を引き起こします。例えば入力画面で特定の値を入力するとか、通信で特定のデータを受信するとかの条件が揃った時にソフトの中のバグのある箇所が実行されて不具合を引き起こします。ですので、社内テストでは様々な条件を想定したテスト計画を立てて、ソフトに潜在するバグを洗いだす事を試みます。メモリリークバグはこの発生条件を作るのが難しいので、社内テストで見付け難いのです。

メモリプールの減少というメモリリークの症状そのものは、他のバグと同じように何等かの発生条件が揃えば発生します。しかしメモリリークが発生していても、メモリプールの残量がある限りは不具合は起きずにソフトは正常に動作します。メモリリークが何度も繰り返されて、メモリプールの残量がゼロになるメモリ枯渇が起きて初めてソフトの不具合が起きます。例えばメモリプールの量が 300MByte あったとして、1日の動作でメモリリークが 1Mbyte 起きるとすると、装置の電源が入ってソフトが動作し始めてから 300日経って馴染めてメモリ残量がゼロになって不具合が起きます。

メモリリークを見付けるには、①メモリリークを起こす条件に加えて、その条件を何回繰り返せばよいかという②反復回数も条件として満たす必要あります。限られたテスト時間のうちにメモリリークによる不具合を起こすには、①の条件が揃う状態を加速するためのテスト加速の方法と、②の反復回数を満たすためのテスト期間の設定の両方が必要になるので、テストが難しくなります。

メモリリークテストってどんな事をすればいい

テスト条件は難しいですがソフトをリリースする時にはなんとかしてメモリリークのバグが潜在していない事をテストで確認しなければなりません、どんなメモリリークテストとしてどんなテストをすれば良いのでしょうか。実はメモリリークテストは、テストの考え方を少し変えればなんとかなります。メモリプールの残量がゼロになってソフトの不具合が起きるのを待つのが難しいのならば、メモリプールの残量が減っていない事を確認してあげればいい、と少しテストの判定方法を変えるのです。

具体的には、以下のような手順でメモリリークテストを計画します

  1. テストの最初にメモリプールの残量を測定する
  2. いろいろな条件でソフトを動作させる
  3. テストの最後にメモリプールの残量を再測定する
  4. メモリプールの残量が最初と最後で変わっていなければメモリリークは無いと判断する

1つ目のメモリプールの残量の測定は、動的メモリの残量を測定する方法が OS のインターフェースで提供されていれば良いですが、無い場合には同等の機能を持ったライブラリを探すか自分で開発する必要があります。開発する場合には、単純に動的メモリをどんどん獲得していき獲得出来なくなった時点までに獲得したメモリ量をその時の残メモリ量と判断する、というような関数を作るのが良いですね。細かく言えば、メモリ領域の断片化(フラグメンテーション)やメモリ割り当て後の実データ書き込みの要否など、考えないといけない項目が幾つかあるのでもう少し処理は複雑になりますが、残メモリの大まかな目途はつきます。

2つ目のいろいろな条件でのソフトの動作は、メモリリークの起きる条件が判らないのでともかく様々な動作をさせてみるという力業です。色々な機能の自動テストシステムがあれば、それを使って長時間に渡って色々な機能を動作させるのが一番良いですね。 それに加えて、何等かのエラー処理の中でのメモリリーク(実はこれが一番見つけにくくて市場に流出し易い)を見付けるために、エラーが発生するテスト環境を作ってそこで過負荷を掛けた長時間エージングを実施するといういのも、良い手です。

3つ目のメモリプールの残量測定は、1つ目と同じ方法を2つ目のテストを終了した後に行うだけですね。そして、1つ目と2つ目のメモリプールの残量を比較して、変化が無ければメモリプールの残量は減っていないと判断するのが4つ目です。 メモリプールの残量の測定方法にもよりますが、メモリプールの残量は実際の稼働状況によってかなりダイナミックに変化します。 ですので、メモリリークテストは1回だけではなく複数回実施した結果を見て、毎回のテストでメモリの残量現象が確認できる様ならメモリリークが起きていると判断するのが安全です。

リークするのはOSの動的メモリだけじゃない

ここまでの説明では動的メモリとして OS の提供する動的メモリを想定してきましたが、リークをするのは OS の提供する動的メモリだけではありません。アプリケーションレベルで動的メモリを実装してこれを別のアプリケーションから使っている場合にも、当然動的メモリのリークは起こり得ます。ですのでメモリリークのテストを計画する場合には、ソフトウエアで使っている動的メモリを全て洗い出して、各々の動的メモリに対してメモリリークテストの計画を立てる事が大切です。

メモリリークを確認したらそれだけで大丈夫?

メモリリークは動的メモリの返却忘れで起きます。という事は、メモリ以外にも動的に獲得/返却をする資源が他にもあれば、そこで返却忘れがあればやはりリークが起きます。アプリケーションソフトの中にも注意の必要な動的資源がありますし、OS の提供する動的資源にはメモリ以外にも在ります。これらについては次の記事で紹介しましょう。

(次の記事)システムテストの必須項目(2)・メモリ以外の動的資源リークテスト に続く
(前の記事)テスト報告書・テスト計画の評価にはテスト効率についての評価も書 に戻る
 テストの記事の先頭 に戻る