いろいろ備忘録日記

主に .NET とか Go とか Flutter とか Python絡みのメモを公開しています。

gccで各関数が使用しているスタックサイズを出力するオプション

概要

忘れないうちメモメモ。こういうのは知ってるのと知らないので手間が大きく変わりますね・・・。

プログラムがいきなり落ちた

自分が書いたプログラムではないんですが、動かしていると特定の処理パターンを通したときにいきなり死ぬという状況が発生。

C言語なので、本当にスンって死んでくれます・・w

いろんなところをコメントアウトしたり、printfデバッグ入れたりして箇所は特定。

最終的にいろいろ調べたところの結果が「デフォルトのスタックサイズを超える割当をしてるのが原因」でした。これはアカン・・。

で、修正するのは良いとして、もうちょいマシな調べ方ないのかしら?って思いました。

手作業すぎるし、一時的にせよコードいろいろいじらないといけないのが嫌・・・。

gcc の -fstack-usage オプション

情報調べてみると、いいのがありました。

gcc.gnu.org

コンパイル時に -fstack-usage ってオプションを付与すると、オブジェクトファイルと共に

拡張子が su というファイルが生成されるようになります。sustack 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上にサンプルをアップしておきました。ご参考まで。

こっちは、デフォルトのスタックサイズを超えない版と超える版の両方を試せるようになっています。

github.com

参照情報

今回の件を調べる際に以下のページの情報がとても役立ちました。記事書いてくださった方々に感謝です m( )m

qiita.com

www.uquest.co.jp

syohex.hatenablog.com

qiita.com


過去の記事については、以下のページからご参照下さい。

  • いろいろ備忘録日記まとめ

devlights.github.io

サンプルコードは、以下の場所で公開しています。

  • いろいろ備忘録日記サンプルソース置き場

github.com

github.com

github.com