C関数のバックトレースは、プログラムの一連の関数呼び出しを返すだけですが、gdbの情報ローカルと同じように、プログラム内のすべてのローカル変数をリストしたい.これができるかどうか何か考えはありますか? ありがとう
3 に答える
一般的に、いいえ。「スタック」を事実を与えられたある種の神と考えるのはやめるべきです。コール スタックは、C の一般的な実装手法にすぎません。固有の意味や必要なセマンティクスはありません。自動変数 (あなたが言うように、「ローカル変数」) は特定の方法で動作する必要があり、それはコール スタックに書き込まれることを意味する場合もあります。ただし、ローカル変数がメモリ内でまったく実現されないことは完全に考えられます。代わりに、ローカル変数はプロセッサレジスタに格納されるか、同等のプログラムをそれらなしで定式化できる場合は完全に削除される可能性があります。
いいえ、ローカル変数を列挙するための言語固有のメカニズムはありません。あなたが言うように、デバッガーはある程度それを行うことができます(デバッグシンボルが存在し、最適化の対象となることに応じて)。おそらく、実行中のプログラム内からデバッグ シンボルを処理できるライブラリを見つけることができます。
backtrace
まず、は標準の C ライブラリ関数ではなく、GNU 固有の拡張機能である ことに注意してください。
一般に、コンパイルされたコードからローカル変数情報を取得することは、特にデバッグなしでコンパイルされた場合や最適化を有効にしてコンパイルされた場合、不可能にすることは困難です。デバッグがオンになっていない場合、変数の名前と型は通常、結果のマシン コードに保持されません。
たとえば、次の途方もなく単純なコードを見てみましょう。
#include <stdio.h>
#include <math.h>
int main(void)
{
int x = 1, y = 2, z;
z = 2 * y - x;
printf("x = %d, y = %d, z = %d\n", x, y, z);
return 0;
}
マシンコードの結果は次のとおりです。デバッグや最適化は行われていません。
.file "varinfo.c"
.version "01.01"
gcc2_compiled.:
.section .rodata
.LC0:
.string "x = %d, y = %d, z = %d\n"
.text
.align 4
.globl main
.type main,@function
main:
pushl %ebp
movl %esp, %ebp
subl $24, %esp
movl $1, -4(%ebp)
movl $2, -8(%ebp)
movl -8(%ebp), %eax
movl %eax, %eax
sall $1, %eax
subl -4(%ebp), %eax
movl %eax, -12(%ebp)
pushl -12(%ebp)
pushl -8(%ebp)
pushl -4(%ebp)
pushl $.LC0
call printf
addl $16, %esp
movl $0, %eax
leave
ret
.Lfe1:
.size main,.Lfe1-main
.ident "GCC: (GNU) 2.96 20000731 (Red Hat Linux 7.2 2.96-112.7.2)"
x
、y
、およびは、それぞれ、、およびz
を通じて参照されます。算術を実行するために使用される命令以外に、それらが整数であることを示すものは何もありません。 -4(%ebp)
-8(%ebp)
-12(%ebp)
最適化 (-O1) をオンにするとさらに良くなります。
.file "varinfo.c"
.version "01.01"
gcc2_compiled.:
.section .rodata.str1.1,"ams",@progbits,1
.LC0:
.string "x = %d, y = %d, z = %d\n"
.text
.align 4
.globl main
.type main,@function
main:
pushl %ebp
movl %esp, %ebp
subl $8, %esp
pushl $3
pushl $2
pushl $1
pushl $.LC0
call printf
movl $0, %eax
leave
ret
.Lfe1:
.size main,.Lfe1-main
.ident "GCC: (GNU) 2.96 20000731 (Red Hat Linux 7.2 2.96-112.7.2)"
この場合、コンパイラは静的な分析を行いz
、コンパイル時に値を計算できました。コンパイラーはそれらの値がどうあるべきかを既に知っているので、変数のためにメモリーを確保する 必要はまったくありません。
これが一時的なデバッグのためだけの場合は、デバッガーを呼び出すことができます。ただし、デバッガー自体がプログラムをフリーズさせるため、出力をキャプチャするための仲介者が必要です。たとえば、 を使用system
して出力をファイルにリダイレクトし、後でそのファイルを読み取ることができます。以下の例では、ファイルに次gdbcmds.txt
の行が含まれていますinfo locals
。
char buf[512];
FILE *gdb;
snprintf(buf, sizeof(buf), "gdb -batch -x gdbcmds.txt -p %d > gdbout.txt",
(int)getpid());
system(buf);
gdb = fopen("gdbout.txt", "r");
while (fgets(buf, sizeof(buf), gdb) != 0) {
printf("%s", buf);
}
fclose(gdb);