Help us understand the problem. What is going on with this article?

メモリリークチェッカー

More than 5 years have passed since last update.

c で daemon 動作するサーバを作っていると malloc(3) などで動的に確保したメモリの free(3) 忘れによる、いわゆるメモリリークを防ぐ事が非常に重要になってきます。
その様なメモリリークを防止するために役立つ様々なツールが世の中には沢山存在していて、それぞれ非常に有用ではあるのですが、いざ利用しようと思うと割と面倒な作業が発生したり、自分の求めている機能に対して明らかにオーバースペックだったりするので、自分の用途に合わせて簡単に使える『俺様ツール』を作ってみました。

動作原理は簡単で malloc(3) や free(3) したアドレスをログに出力して、後からログを解析してメモリの確保と解放が対になっていない部分を抽出するだけです。

そのためにまずは元となるソースコードを修正して、malloc(3) や free(3) を独自に作成した wrapper 関数に置換えて確保、解放したメモリのアドレスをログに出力する機能を組込む必要があるのですが、malloc(3) や free(3) などはエラー処理を共通化するため等の理由であらかじめ wrapper 関数化されている場合が多いと思うので、この部分は出力するログのフォーマットを決めるだけで割りと簡単に追加できると思います。

今回は syslog(3) を利用して malloc(3)、strdup(3)、 free(3) で確保/解放するメモリのアドレスを出力しました。

wrapper の作成

wrapper.c
/*
 * malloc() の wrapper
 */
void    *mymalloc(size_t size)
{

    void    *p;

    if(!(p = malloc(size)))
        /* エラー処理 */
        exit(255);
    syslog(LOG_DEBUG, "malloc %p\n", p);

    return(p);

}

/*
* strdup() の wrapper
*/
char    *mystrdup(const char *s)
{

    char    *p = NULL;

    if(s){
        if((p = strdup(s)))
            syslog(LOG_DEBUG, "strdup %p\n", p);
        else
            /* エラー処理 */
            exit(255);
    }

    return(p);

}

/*
* free() の wrapper
*/
void    myfree(void *p)
{

    if(p){
        free(p);
        syslog(LOG_DEBUG, "free: %p\n", p);
    }

}

検出ツールの作成

次に出力されたログの解析処理ですが、入力行の解析やハッシュテーブルが簡単に利用できる言語として今回は awk(1) を利用して実装しました。

checker.sh
#!/bin/sh
#
# -v: 冗長モード
#

if [ "${1}" = "-v" ]
then
    verbose=true
    shift
fi

awk '
    # syslog の出力は Month Day hh:mm:dd server process[PID]: … 型式と仮定
    # ユーザデータは $6 から始まっている筈なので $6 が関数で $7 がアドレス

    # malloc/strdup されたアドレスをキーとしたハッシュに行番号を格納
    /(malloc|strdup)/{
        alloc[$7] = NR;
    }

    # free されたアドレスをキーとしてハッシュを検索
    # 見付かれば alloc された領域の free なのでハッシュをクリア
    /^free/{
        if(alloc[$7]){
            if("'${verbose:-false}'" == "true")
                printf("% 5d %s: % 5d\n", NR, $7, $alloc[$7]);
                alloc[$7] = 0;
        }
        else
            printf("% 5d %s: unknown free.\n", NR, $7);
    }
    # クリアされていないハッシュ (=開放されていないメモリ) を表示
    END{
        for(i in alloc)
            if(alloc[i])
                printf("% 5d %s: NOT free.\n", alloc[i], i);
    }
' ${1}

このスクリプトを実行する事により malloc(3) もしくは strdup(3) で確保したメモリが解放されていない場合、もしくは確保していないメモリを解放した場合が簡単に発見可能です。
同様に open(2) / fopen(3) や、close(2) / fclose(3) などの wrapper 関数を作成すれば FD リークの発見も簡単に可能になります。

これらは自分が必要とする機能のみを簡単に実装したもので、汎用的に使える事を目指したツールではありません。ただ、日頃発生しうる面倒な作業がちょっとの工夫で多少なりとも楽になる一つの例として公開してみました。

bsdhack
親バカがviでシェルスクリプトを書いてる様なヤツです。 古き良きUNIXが好きですが最近は Mac ばかり使ってます。 Unix/Linux 技術者をお捜しの方は是非声をかけて下さい
http://blog.bsdhack.org
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした