Last updated:
2002.03.19. coreを吐かせる
普段使っているソフトが突然落ちて、 そこらじゅうにcoreを吐くのはこまりものですが、 デバッグをしたい時には、coreほど大事なものはありません。
bashでは、ulimit
というコマンドでcoreの最大の大きさを制御します。
制限には、二種類あり、ulimit -H
でハード・リミットを、
ulimit -S
でソフト・リミットを操作します。
-H
も-S
も付けない場合は、
両方の制限が同時に操作されてしまいます。
coreの大きさそれ自身は、ソフト・リミットに制限されます。
ハード・リミットは 一度制限してしまうと、それ以上にはできないが、
ソフト・リミットは、ハード・リミットの範囲内で自由に設定できます。
ulimit -H
でハード・リミットが、
ulimit -S
でソフト・リミットが、
確認できます。
普段は.bashrc
でulimit -S 0
してcoreの生成を抑制し、
必要になったら、
ulimit -S unlimited
してcoreを採取するのがいいかも。
ハード・リミットが制限されてしまっている場合は、bashが起動時に読む、
/etc/profile
なども注意してみるといいかも。
ソフト中のどの関数がどのくらい時間を消費するかわかる。 うーむ。やっぱり微分は大変なのだ、とわかったり。
gcc -c -Wall -g -pg hoge.c
とか。
gcc -o hoge hoge.o -lm
とか。
hoge
とか。
gmon.out
というファイルができる。
gprof hoge gmon.out | less
Labviewなんてものを使いはじめたら、自分で共有ライブラリを作るハメに。 …あー。ライブラリ中でexit()しちゃうと悲惨なことになりますな。
共有ライブラリについては、 Program Library HOWTO (川崎貴彦さんによる和訳)が詳しいです。
共有ライブラリは以下の方法で生成します。 so.の後は、メジャーバージョン番号.マイナーバージョン番号.…で、 メインプログラムを再コンパイルしなきゃいけないような変更のときは、 メジャーバージョン番号を大きくしてライブラリを作りなおし、 リンクを張りなおすことになります。 あ、ln -sは本当はライブラリのインストール先でやらなくちゃね。
gcc -fPIC -g -c -Wall hoge.c gcc -shared -Wl,-soname,libhoge.so.1 -o libhoge.so.1.0.1 hoge.o -lc ln -s libhoge.so.1.0.1 libhoge.so.1 ln -s libhoge.so.1 libhoge.so
標準のパスに無い共有ライブラリにリンクする。 -Lの後に、ライブラリを探すパスを指定します。
gcc -Wall -c fuga.c gcc -Wall -o fuga -L`pwd` -lhoge fuga.o
普段は、共有ライブラリのパスは、 ldconfigが/etc/ld.so.confを参照しながら管理してくれます。 が、ちょっとだけ共有ライブラリを試したいときは、
export LD_LIBRARY_PATH=`pwd`:$LD_LIBRARY_PATHでちょっと作った共有ライブラリの場所を、 ローダに教えてあげることもできるようです。
共有ライブラリへの依存関係を表示する。 例えば、
ldd -r /usr/bin/ssh libdl.so.2 => /lib/libdl.so.2 (0x40019000) libnsl.so.1 => /lib/libnsl.so.1 (0x4001d000) libz.so.1 => /usr/lib/libz.so.1 (0x40034000) libutil.so.1 => /lib/libutil.so.1 (0x40043000) libpam.so.0 => /lib/libpam.so.0 (0x40047000) libcrypto.so.0 => /usr/lib/libcrypto.so.0 (0x4004f000) libc.so.6 => /lib/libc.so.6 (0x4010a000) /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)で、sshコマンドが必要とする共有ライブラリがわかります。 -rオプションは、 足りないオブジェクト(ってなんだ?)を表示してくれるオプション。 ちゃんと動いているプログラムでは何も表示されませんが、
ldd -r /usr/lib/libgsl.so libm.so.6 => /lib/libm.so.6 (0x40134000) libc.so.6 => /lib/libc.so.6 (0x40152000) /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x80000000) undefined symbol: gsl_blas_daxpy (/usr/lib/libgsl.so) undefined symbol: gsl_blas_dnrm2 (/usr/lib/libgsl.so) undefined symbol: gsl_blas_dgemv (/usr/lib/libgsl.so) undefined symbol: gsl_blas_dscal (/usr/lib/libgsl.so) undefined symbol: gsl_blas_dtrsv (/usr/lib/libgsl.so) undefined symbol: gsl_blas_idamax (/usr/lib/libgsl.so) undefined symbol: gsl_blas_ddot (/usr/lib/libgsl.so)とすると、gsl_blas_daxpyなどの関数がlibgsl.soでは定義されていない (使うときは他のライブラリもリンクしなくちゃいけない) ことがわかります。
nm -o /usr/lib/libgsl*.so | grep gsl_blas_daxpy$ /usr/lib/libgsl.so: U gsl_blas_daxpy /usr/lib/libgslblas.so:000046b0 T gsl_blas_daxpy /usr/lib/libgslblascblas.so:00007b80 T gsl_blas_daxpyとすると、libgslblas.soをリンクしとけばいいこともわかります。 おぉ、これは便利だ。 :p> gslは、GNUの科学技術計算ライブラリです。 詳しくは、gslのサイト を、Kondara 1.2のRPMは、 RPMを作ってみる からどうぞ。
共有ライブラリに定義されている(されていない)シンボルを表示する。 例えば、
nm -D /usr/lib/libm.so | less : U sprintf 0000b6b0 W sqrt :とすると、libm.soに定義されている関数が何かわかります。 -Dは動的なシンボル(ってなんだ?)だけを表示する。 表示された情報のうち、2番目のアルファベット一文字はシンボルタイプで、 大文字はグローバル、小文字はローカル、 T:コードセクション内の普通の定義、 W:weak (もしも他のライブラリもこのシンボルを定義していた場合、 その定義がオーバーライドする) U:未定義 (シンボルはライブラリによって使われているが、 ライブラリ内では定義されていない) D:初期化されたデータセクション、 B:初期化されていないデータセクション、 だそうです。
gcc/Linuxで普通にソフトを走らせると、 浮動小数点演算でおかしな計算をさせても、 実行が続いてしまいます。 デバッグの時には止ってほしいものだ。 SIGFPEのハンドラを作っても、SIGFPEは来ないみたい。
zero.c
#include <stdio.h> #include <stdlib.h> #include <math.h> int main(void) { double a, b, c; a = 1; b = 0; c = a / b; printf("%g / %g = %g\n", a, b, c); return 0; }
gcc -o zero zero.c
$ ./zero 1 / 0 = inf
浮動小数点プロセッサのオプションを変えているのかな? http://cthulhu.ale.org/ale-archive/ale-2000-08/msg00433.html からの引用です。 Thank you very much, Mr. Justin Russell
#include <fpu_control.h> static void __attribute__ ((constructor)) trapfpe (void) { fpu_control_t cw = _FPU_DEFAULT & ~(_FPU_MASK_IM | _FPU_MASK_ZM | _FPU_MASK_OM); _FPU_SETCW(cw); }
やってみました。まずは gdb なし。
gcc -o zero zero.c trapfpe.o
$ ./zero Floating point exception
ね?なかなかのものだ。
gdb zero
からrun
すると、
main()
の中で例外が起きたことがわかる。
次はどこで例外が起きたのかわかるようにしよう。
以下でも触れた、gdbを利用する。
gcc -o zero zero.c trapfpe.o -g
$ gdb zero GNU gdb 5.0 Copyright 2000 Free Software Foundation, Inc. GDB is free software, covered by the GNU General Public License, and you are welcome to change it and/or distribute copies of it under certain conditions. Type "show copying" to see the conditions. There is absolutely no warranty for GDB. Type "show warranty" for details. This GDB was configured as "i586-redhat-linux"... (gdb) run Starting program: /path/to/zero Program received signal SIGFPE, Arithmetic exception. 0x8048408 in main () at zero.c:10 10 c = a / b; (gdb) quit The program is running. Exit anyway? (y or n) y
うむ、しあわせじゃ。
というわけで、以上の例も、
Linux ホスト名 2.2.16-1k7 #1 Sat Jun 24 00:35:43 JST 2000 i686 unknown
での実行結果でした。
double型が4バイトと思いこんでたんですな。
以下のソースの実行結果。uname -a
と一緒に。
Linux ホスト名 2.2.16-1k7 #1 Sat Jun 24 00:35:43 JST 2000 i686 unknown char :1 bytes short :2 bytes int :4 bytes long :4 bytes float :4 bytes double:8 bytes
#include <stdio.h> #include <stdlib.h> int main(void) { fprintf(stdout, "char :%ld bytes\n", sizeof(char)); fprintf(stdout, "short :%ld bytes\n", sizeof(short)); fprintf(stdout, "int :%ld bytes\n", sizeof(int)); fprintf(stdout, "long :%ld bytes\n", sizeof(long)); fprintf(stdout, "float :%ld bytes\n", sizeof(float)); fprintf(stdout, "double:%ld bytes\n", sizeof(double)); return 0; }
SEGVる、というのは、Segmentation fault になるということです。 man 7 signal などで意味がわかります。 結局は、同じポインタに2回freeをしていたことが原因でした。
% gdb 調査対象 (gdb) run 引数 …
/* malloc監視 */ void *MyMalloc_Rep( size_t sz, const char *pcFileName, int nLine ) { void *ptr; ptr = malloc(sz); fprintf(stderr, "malloc at %s %-4d : %p - %p, %ld byte\n", pcFileName, nLine, ptr, ptr + sz, sz); return ptr; } /* free監視 */ void MyFree_Rep( void *ptr, const char *pcFileName, int nLine ) { fprintf(stderr, "freeing at %s %-4d : %p\n", pcFileName, nLine, ptr); free(ptr); }
#define MyMalloc(S) MyMalloc_Rep( (S), __FILE__, __LINE__ ) #define MyFree(P) MyFree_Rep( (P), __FILE__, __LINE__ )
Back to zunda.
zunda <zunda at freeshell.org>