- 概要
- プログラムがいきなり落ちた
- gcc の -fstack-usage オプション
- 現在のスタック領域のサイズを調べる
- 試してみる
- makefile
- github にサンプルプロジェクトをアップ
- 参照情報
概要
忘れないうちメモメモ。こういうのは知ってるのと知らないので手間が大きく変わりますね・・・。
プログラムがいきなり落ちた
自分が書いたプログラムではないんですが、動かしていると特定の処理パターンを通したときにいきなり死ぬという状況が発生。
C言語なので、本当にスンって死んでくれます・・w
いろんなところをコメントアウトしたり、printfデバッグ入れたりして箇所は特定。
最終的にいろいろ調べたところの結果が「デフォルトのスタックサイズを超える割当をしてるのが原因」でした。これはアカン・・。
で、修正するのは良いとして、もうちょいマシな調べ方ないのかしら?って思いました。
手作業すぎるし、一時的にせよコードいろいろいじらないといけないのが嫌・・・。
gcc の -fstack-usage
オプション
情報調べてみると、いいのがありました。
コンパイル時に -fstack-usage
ってオプションを付与すると、オブジェクトファイルと共に
拡張子が su
というファイルが生成されるようになります。su
は stack usage
の略ですかね。
su ファイルは一行に以下の3セクションという形で出力されます。
- ファイルと関数名
- サイズ (バイト)
- 種別
現在のスタック領域のサイズを調べる
以下のようにすると調べられます。
$ ulimit -s 8192
上はGitpod上で実行した結果ですが、現在の大抵のLinuxだとデフォルトは8192だと思います。
単位はキロバイトです。なので、8MBですね。
ulimit -a
とする現在の設定値が全部みれます。
$ ulimit -a core file size (blocks, -c) unlimited data seg size (kbytes, -d) unlimited scheduling priority (-e) 0 file size (blocks, -f) unlimited pending signals (-i) 241427 max locked memory (kbytes, -l) 64 max memory size (kbytes, -m) unlimited open files (-n) 1048576 pipe size (512 bytes, -p) 8 POSIX message queues (bytes, -q) 819200 real-time priority (-r) 0 stack size (kbytes, -s) 8192 cpu time (seconds, -t) unlimited max user processes (-u) unlimited virtual memory (kbytes, -v) unlimited file locks (-x) unlimited
試してみる
以下のクソコードがあるとします。
#include <stdio.h> #include <stdlib.h> #include <ctype.h> int write_hello(char *buf, int start); int write_space(char *buf, int start); int write_world(char *buf, int start); void print_message(char *buf, int length); int main(void) { char buf[128] = {0}; int written = write_hello(buf, 0); written += write_space(buf, written); written += write_world(buf, written); print_message(buf, written); } int write_hello(char *buf, int start) { char *p = &buf[start]; const char *message = "hello"; return sprintf(p, "%s", message); } int write_space(char *buf, int start) { char *p = &buf[start]; const char *message = " "; return sprintf(p, "%s", message); } int write_world(char *buf, int start) { char *p = &buf[start]; const char *message = "world"; return sprintf(p, "%s", message); } void print_message(char *buf, int length) { for (int i = 0; i < length; i++) { int c = buf[i]; int u = toupper(c); printf("%c\n", u); } }
これをコンパイルする際に以下のように -fstack-usage
オプションを付与します。
$ gcc -g -Wall -Wextra -fstack-usage -o fstack-usage-example ./*.c
すると、実行ファイル(またはオブジェクトファイル)が生成される場所に 拡張子が su というファイルが生成されます。
$ ls -1 fstack-usage-example main.c main.su makefile
この中にスタックサイズが記録されています。
$ cat main.su main.c:10:5:main 176 static main.c:20:5:write_hello 48 static main.c:27:5:write_space 48 static main.c:34:5:write_world 48 static main.c:41:6:print_message 48 static
これぐらいの量だと、大したことないですが現実のプログラムだと関数がもっといっぱいあるのでガタガタになってて、ちょっと見づらいですね。
なので、以下のようにして少し整形すると見やすくなります。
$ cat main.su | awk -F" " '{printf("%-60s\t%d\t%s\n", $1, $2, $3)}' main.c:10:5:main 176 static main.c:20:5:write_hello 48 static main.c:27:5:write_space 48 static main.c:34:5:write_world 48 static main.c:41:6:print_message 48 static
makefile
上記のものを makefile にしておきました。ご参考まで。
CC = gcc CFLAGS = -g -Wall -Wextra -fstack-usage EXE = fstack-usage-example all: _compile _run _view-su _clean _compile: $(CC) $(CFLAGS) -o $(EXE) ./*.c _run: @./$(EXE) _view-su: @find . -name "*.su" -type f | xargs cat | awk -F" " '{printf("%-60s\t%d\t%s\n", $$1, $$2, $$3)}' _clean: @$(RM) ./$(EXE) @$(RM) ./*.su
github にサンプルプロジェクトをアップ
ついでなので、github上にサンプルをアップしておきました。ご参考まで。
こっちは、デフォルトのスタックサイズを超えない版と超える版の両方を試せるようになっています。
参照情報
今回の件を調べる際に以下のページの情報がとても役立ちました。記事書いてくださった方々に感謝です m( )m
過去の記事については、以下のページからご参照下さい。
- いろいろ備忘録日記まとめ
サンプルコードは、以下の場所で公開しています。
- いろいろ備忘録日記サンプルソース置き場