06
C言語で使用メモリをカウントする。
Filed Under (article, level☆☆☆) by on 06-02-2008
どうもひろきのだいちです。
最近、初心者にとっていちばんよい言語は・・みたいな話が出てますが、
いちばんよい言語というのはたぶん「近くに一番優しくわかりやすく丁寧に教えてくれる人のいる言語」だと思います。
別に言語なんて関係ないです。
でも、いろいろとできることが違うという意味ではC言語はすごいです。
今日はC言語を使ってプログラムの利用メモリを出力してくれるようなソフトを書いてみました。
はじめに
こんな風にランダムに生成した数字をquick sortして順番に出力するプログラムがあったとします。
$ ./qsort 10
133557644 467172391
587048537 700406542
924651875 942877877
1231884300 1236726036
1563773570 1858205801
こんな風にランダムに生成した数字をquick sortして順番に出力するプログラムがあったとします。
これにfleafeedとつけるだけで・・・
$fleafeed ./qsort 10
133557644 467172391
587048537 700406542
924651875 942877877
1231884300 1236726036
1563773570 1858205801
-----------fleafeed results------------
--USING STACK : 1160 bytes
--USING HEAP : 0 bytes
--ALLOC times : 0
--FREE times : 0
こんな付加情報を追加してくれるやつを作ってみました。
さらにただ、mallocして放置するというドSなプログラムを
試してみると・・・
-----------fleafeed results------------ --USING STACK : 1076 bytes --USING HEAP : 10000 bytes --ALLOC times : 100 --FREE times : 0
こんなかんじで、計算どおりヒープの数とmallocの数がカウントされてます。
これの作り方の前にちょっと基礎知識を。
わかる人は読み飛ばしてください。まちがった説明するかもなので><
スタックとかヒープとかってなに?
スタックとかヒープとかってのは、プログラムの管理領域であるメモリの役割につけられた名前です。
スタックは関数呼び出しや引数、関数内自動変数などを管理するメモリの領域です。
面倒なので、wikipedia先生に頼ると
・スタック
・コールスタック
こんなかんじのものです。
ヒープとは固定サイズじゃなくて自由に別途に確保できるところにあるメモリーです。
通常mallocでメモリーをもらってfreeで返します。
もらったものは必ず返さないといけないんですが、最近の言語ではGC(ガーベジコレクション)というステキ機能がついていて
変わりに返してくれます。連帯保証人みたいなひとです。
これだけだとあれなのでまたまた、wikipedia先生に・・
・ヒープ領域
・ヒープ構造
スタック編
理屈は簡単です。
一番最初に一度使われる前のスタック領域を俺色に染め上げて、
プログラムを実行し、なんらかの形で利用されると別の値になってしまうので
一番最後に俺色になっていないところの数を数え上げるという方法です。
そのために
-
#define FF_FIRST __attribute__ ((constructor))
-
#define FF_LAST __attribute__ ((destructor))
こんな宣言をしてmain関数より前に関数を実行させます。
-
#define FF_MAGIC 0xf1eafeed
-
#define FF_MAX 1024*1024
-
FF_FIRST int fleafeed_fillstack(){
-
int p[FF_MAX];
-
int i=0;
-
for(;i<FF_MAX;i++){
-
p[i]=FF_MAGIC;
-
}
-
}
-
FF_LAST void fleafeed_checkstack(){
-
int p[FF_MAX];
-
int i=0;
-
for(;i<FF_MAX;i++){
-
FF_USESTACK+=(p[i]==FF_MAGIC)?0:4;
-
}
-
}
このときに埋め立てる値を0xf1eafeedとう値にしたので
このプログラムの名前はfleafeedになりました。
適当に16進数で表現できる8文字の英語をいれただけです。
意味はわかりません><
これで、スタックの仕様数はわかりました。
これを
$gcc fleafeed.c -share -fPIC -o libfleafeed.so
のようにしてコンパイルします。これでできたライブラリをリンクしてやれば、スタックの使用量がわかります。
次はヒープです。
ヒープ領域編
ヒープは、よほど無理をしないかぎり、mallocなどから呼ばれるのでこの関数をwrapperしてやればヒープの使用量はわかります。
というわけで、
-
void *malloc(size_t size){
-
FF_ALLOCCOUNT++;
-
FF_USEHEAP+=size;
-
return malloc(size);//本物のmalloc?
-
}
というような感じでラッパしたいのですが、これだとmallocという名前が衝突してしまいます。
そんなわけで
-
#define GNU_SOURCE
-
#include <dlfcn.h>
-
static void*(*libc_malloc)(size_t);
-
static void*(*libc_calloc)(size_t,size_t);
-
static void (*libc_free )(void *);
-
-
void *malloc(size_t size){
-
FF_ALLOCCOUNT++;
-
FF_USEHEAP+=size;
-
return (*libc_malloc)(size);
-
}
-
FF_FIRST int fleafeed_fillstack(){
-
libc_malloc=dlsym((void *)-1L,"malloc");
-
}
こんな感じにdlsymのRTLD_NEXTをつかって、mallocのポインタをlibc_mallocという代理人にあずけておきます。
なぜかRTLD_NEXTが#defineされてなかったので即値で対応しました。
共有ライブラリを事前に読み込む
ソースコードがあればそこからビルドするときになんとかすればいいんですが、そうではないので事前にライブラリを読み込むようにしてコマンドを呼び出すという方法をとります。
alias fleafeed=LD_PRELOAD=./libfleafeed.so
こんなかんじに。LD_PRELOADはこのライブラリは最初に読み込んじゃって!という環境変数です。
これで準備は万端。
ためしにgccでコンパイルをするときの利用状況を見てみましょう。
$ fleafeed gcc test.c -----------fleafeed results------------ --USING STACK : 6072 bytes --USING HEAP : 832510 bytes --ALLOC times : 1776 --FREE times : 833 -----------fleafeed results------------ --USING STACK : 2272 bytes --USING HEAP : 1810165 bytes --ALLOC times : 863 --FREE times : 48 -----------fleafeed results------------ --USING STACK : 5980 bytes --USING HEAP : 1715525 bytes --ALLOC times : 1185 --FREE times : 355 -----------fleafeed results------------ --USING STACK : 2012 bytes --USING HEAP : 25998 bytes --ALLOC times : 128 --FREE times : 35 -----------fleafeed results------------ --USING STACK : 5840 bytes --USING HEAP : 33885 bytes --ALLOC times : 336 --FREE times : 181
うわなんかいっぱい出たw
プロセス分だけ終わりがあるので、プロセスごとに出力されるようです。
最後に全体のソースコード。
-
#define GNU_SOURCE
-
-
-
#include <dlfcn.h>
-
#include <stdio.h>
-
#define FF_FIRST __attribute__ ((constructor))
-
#define FF_LAST __attribute__ ((destructor))
-
#define FF_MAGIC 0xf1eafeed
-
#define FF_MAX 1024*1024
-
-
int FF_ALLOCCOUNT=0;
-
int FF_FREECOUNT=0;
-
int FF_USEHEAP=0;
-
int FF_USESTACK=0;
-
-
-
static void*(*libc_malloc)(size_t);
-
static void*(*libc_calloc)(size_t,size_t);
-
static void (*libc_free )(void *);
-
-
void *malloc(size_t size){
-
FF_ALLOCCOUNT++;
-
FF_USEHEAP+=size;
-
return (*libc_malloc)(size);
-
}
-
void *calloc(size_t nmemb,size_t size){
-
FF_ALLOCCOUNT++;
-
FF_USEHEAP+=size*nmemb;
-
return (*libc_calloc)(nmemb,size);
-
}
-
void free(void *ptr){
-
FF_FREECOUNT++;
-
(*libc_free)(ptr);
-
}
-
/*void *realloc(void *ptr,size_t size){
-
}*/
-
-
FF_FIRST int fleafeed_fillstack(){
-
int p[FF_MAX];
-
int i=0;
-
-
libc_malloc=dlsym((void *)-1L,"malloc");
-
libc_free=dlsym((void *)-1L,"free");
-
libc_calloc=dlsym((void *)-1L,"calloc");
-
-
for(;i<FF_MAX;i++){
-
p[i]=FF_MAGIC;
-
}
-
}
-
FF_LAST void fleafeed_checkstack(){
-
int p[FF_MAX];
-
int i=0;
-
-
for(;i<FF_MAX;i++){
-
FF_USESTACK+=(p[i]==FF_MAGIC)?0:4;
-
}
-
fflush(stdout);
-
fprintf(stderr,"\n-----------fleafeed results------------");
-
fprintf(stderr,"\n--USING STACK\t:%8d bytes",FF_USESTACK);
-
fprintf(stderr,"\n--USING HEAP\t:%8d bytes",FF_USEHEAP);
-
fprintf(stderr,"\n--ALLOC times\t:%8d",FF_ALLOCCOUNT);
-
fprintf(stderr,"\n--FREE times\t:%8d\n",FF_FREECOUNT);
-
}

[...] 次はメモリの消費量です。 これは昨日作ったfleafeedを使ってみます。 C言語でメモリ使用量をカウントする! [...]