9
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

メモリリークチェッカー

Posted at

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 リークの発見も簡単に可能になります。

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

9
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?