コンピューティングにおいて、スタック トレース(スタック バック トレース[ 1 ]またはスタック トレースバック[ 2 ]とも呼ばれる) は、プログラムの実行中の特定の時点におけるアクティブなスタック フレームのレポートです。プログラムが実行されると、メモリはスタックとヒープの2 つの場所に動的に割り当てられることがよくあります。メモリはスタックには継続的に割り当てられますが、ヒープには継続的に割り当てられません。スタックはプログラミング構造も指すため、区別するために、このスタックはプログラムの関数呼び出しスタックと呼ばれます。技術的には、メモリ ブロックがスタックに割り当てられると、その後に割り当てられた他のメモリ ブロックが存在する可能性があるため、簡単に削除することはできません。プログラムで関数が呼び出されるたびに、呼び出しスタックの最上位にアクティベーション レコードと呼ばれるメモリ ブロックが割り当てられます。一般的に、アクティベーション レコードには関数の引数とローカル変数が格納されます。具体的に何が含まれているか、どのように配置されているかは、呼び出し規約によって決まります。
プログラマーは、対話型デバッグや事後デバッグの際にスタックトレースをよく利用します。エンドユーザーは、エラーメッセージの一部としてスタックトレースが表示されるのを目にすることがあり、その情報をプログラマーに報告することができます。
スタックトレースを使用すると、スタックトレースが生成される時点までの、ネストされた関数の呼び出し順序を追跡できます。事後分析のシナリオでは、これは障害が発生した関数(ただし、必ずしも障害の原因となった関数ではない)まで追跡できます。兄弟関数の呼び出しはスタックトレースには表示されません。
Java [ 3 ]やC# [ 4 ]など、多くのプログラミング言語には、システムコールを介して現在のスタックトレースを取得するための組み込みサポートがあります。std::stacktraceが のコンテナとして標準ライブラリに追加される前はstd::stacktrace_entry、C++23以前の にはこれを行うための組み込みサポートはありませんでしたが、C++ ユーザーは (たとえば) stacktraceライブラリを使用してスタックトレースを取得できます。JavaScript では、例外には、例外stackがスローされた場所からのスタックを含むプロパティがあります。
例えば、以下のPythonプログラムにはエラーが含まれています。
def a () -> int :i : int = 0j : int = b ( i )jを返すdef b ( z : int ) -> int :k : int = 5z == 0 の場合:c ()k + zを返すdef c () -> None :エラー()if __name__ == "__main__" :a ()標準のPythonインタープリタでプログラムを実行すると、次のエラーメッセージが表示されます。
トレースバック (最新の呼び出し): ファイル"file.py"、16行目、 <module> 内a () ファイル"file.py"、3行目、 a内j = b ( i ) ファイル"file.py"、9行目、b内c () ファイル"file.py"、13行目、c内error () NameError :名前 'error' が定義されていませんスタックトレースは、エラーが発生した場所、つまりc関数を示しています。また、c関数がによって呼び出されb、さらにがによって呼び出され、そしてプログラムの16行目(最後の行)のコードによって呼び出されたことも示しています。これら3つの関数のアクティベーションレコードは、関数がスタックの一番下に、関数がスタックの一番上に配置されるaようにスタックに配置されます。ac
Javaでは、スタックトレースはjava.lang.Thread::dumpStack()[ 5 ]を使用して手動でダンプできます。次の入力を取り上げます。
public class Main {static void demo () {demo1 ();}static void demo1 () {demo2 ();}static void demo2 () {demo3 ();}static void demo3 () {Thread.dumpStack ( ) ;}public static void main ( String args [] ) {デモ();}}例外リストでは関数が降順で表示されるため、最も内側の呼び出しが最初に表示されます。
java.lang.Exception :スタックトレースat java.lang.Thread.dumpStack ( Thread.java:1336 ) at Main.demo3 ( Main.java:15 ) at Main.demo2 ( Main.java:11 ) at Main.demo1 ( Main.java:7 ) at Main.demo ( Main.java:3 ) at Main.main ( Main.java:19 )Javaでは、を使用して現在のスタックトレースを取得したりjava.lang.Thread::getStackTrace()、を使用してエラー時のスタックトレースを表示したりすることもできますjava.lang.Throwable::printStackTrace()。Javaのスタックトレースはjava.lang.StackTraceElement[]、配列であるとして表されます。[ 6 ]
C言語にはスタックトレースを取得するためのネイティブサポートはありませんが、glibcなどのライブラリがこの機能を提供しています。[ 7 ]
C および C++ では、コンパイラの最適化によって、実行時に復元できるコールスタック情報に干渉する場合があります。たとえば、インライン化によってスタックフレームが欠落したり、末尾呼び出しの最適化によってあるスタックフレームが別のスタックフレームに置き換えられたり、フレームポインタの削除によってコールスタック解析ツールがコールスタックの内容を正しく解釈できなくなったりすることがあります。[ 7 ]
例えば、glibcのbacktrace()関数は、プログラム関数とメモリアドレスを含む出力を返します。
. / a . out () [ 0x40067f ] . / a . out () [ 0x4006fe ] . / a . out () [ 0x40070a ] / lib / x86_64 - linux - gnu / libc . so .6 ( __libc_start_main + 0xf5 ) [ 0x7f7e60738f45 ] . / a . out () [ 0x400599 ]C++23より前は、C++ にはスタック トレースを取得するメカニズムは存在しませんでしたが、 Boost.Stacktraceのようなサードパーティ ライブラリがスタック トレースを提供していました。[ 8 ]std::stacktrace C++23 では、およびを使用してスタック トレースが追加されました。std::stacktrace_entry[ 9 ]スタック トレースは、静的メンバ関数によって返される値を印刷することで手動でダンプできますstd::stacktrace::current()。[ 10 ]
import std ;std :: stacktraceを使用します。void bar () { std :: println ( "bar() からのスタックトレース: \n {}" , stacktrace :: current ()); }void foo () { bar (); }int main () { foo (); }Rust には 2 種類のエラーがあります。panicマクロを使用する関数は「回復不能」であり、現在のスレッドはスタック アンワインドが発生して汚染されます。a を返す関数はstd::result::Result「回復可能」であり、適切に処理できます。[ 12 ]ただし、回復可能なエラーは手動で追加されるため、実行時エラーの結果ではないため、スタック トレースを生成できません。
2021年6月現在、Rustは回復不能なエラーのスタックトレースを実験的にサポートしています。Rustはスレッドがパニックを起こしたときにstderrに出力する機能をサポートしていますが、RUST_BACKTRACE環境変数を設定することで有効にする必要があります。[ 13 ]
有効にすると、このようなバックトレースは以下のように表示され、最新の呼び出しが最初に表示されます。
スレッド' main ' が' execute_to_panic 'でパニックを起こしました。main . rs : 3スタックバックトレース: 0 : std :: sys :: imp :: backtrace :: tracing :: imp :: unwind_backtrace 1 : std :: panicking :: default_hook ::{{ closure }} 2 : std :: panicking :: default_hook 3 : std :: panicking :: rust_panic_with_hook 4 : std :: panicking :: begin_panic 5 : futures :: task_impl :: with 6 : futures :: task_impl :: park .. .