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 の作成
/*
* 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) を利用して実装しました。
#!/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 リークの発見も簡単に可能になります。
これらは自分が必要とする機能のみを簡単に実装したもので、汎用的に使える事を目指したツールではありません。ただ、日頃発生しうる面倒な作業がちょっとの工夫で多少なりとも楽になる一つの例として公開してみました。