Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 18344] ファイルの概要

このコミットは、Goランタイムにおけるスタックトレースの表示に関する改善です。具体的には、トレースバック時にフレームが省略された場合に、その旨を示すメッセージを出力するように変更されています。これにより、デバッグ時により正確な情報が提供されるようになります。

コミット

commit be5d2d443247e8ab447f962a6bb583e62c746f60
Author: Keith Randall <khr@golang.org>
Date:   Thu Jan 23 12:47:30 2014 -0800

    runtime: Print elision message if we skipped frames on traceback.
    
    Fixes bug 7180
    
    R=golang-codereviews, dvyukov
    CC=golang-codereviews, gri
    https://golang.org/cl/55810044

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/be5d2d443247e8ab447f962a6bb583e62c746f60

元コミット内容

このコミットは、Goランタイムのトレースバック機能において、表示されるフレーム数に上限がある場合に、その上限に達してフレームが省略されたことをユーザーに通知するメッセージを追加するものです。これにより、トレースバックが完全ではないことを明示し、デバッグ時の混乱を防ぎます。

変更の背景

この変更は、Goのバグトラッカーで報告された「bug 7180」を修正するために行われました。このバグは、スタックトレースが長すぎる場合に、一部のフレームが省略されて表示されるにもかかわらず、そのことがユーザーに通知されないという問題でした。これにより、ユーザーは完全なスタックトレースが表示されていると誤解し、デバッグが困難になる可能性がありました。

Goランタイムは、デバッグ情報としてスタックトレースを提供しますが、非常に深いコールスタックを持つプログラムの場合、すべてのフレームを表示すると出力が膨大になり、可読性が損なわれる可能性があります。そのため、Goランタイムはデフォルトで表示するフレーム数に上限を設けています(このコミットではTracebackMaxFrames = 100と定義されています)。しかし、この上限に達してフレームが省略された場合、以前のバージョンではその旨が明示されませんでした。

このコミットは、この情報不足を解消し、ユーザーがスタックトレースの完全性を正確に把握できるようにすることを目的としています。

前提知識の解説

  • スタックトレース (Stack Trace): プログラムが実行中にエラーやパニック(Goにおける実行時エラー)を発生させた際に、そのエラーが発生した時点での関数呼び出しの履歴(コールスタック)を順に表示したものです。これにより、どの関数がどの関数を呼び出し、最終的にどこで問題が発生したのかを追跡し、デバッグに役立てることができます。
  • Goランタイム (Go Runtime): Goプログラムの実行を管理する低レベルのシステムです。ガベージコレクション、スケジューリング、メモリ管理、スタック管理、パニック処理など、Goプログラムの動作に必要な多くの機能を提供します。スタックトレースの生成もランタイムの重要な機能の一つです。
  • フレーム (Frame): スタックトレースにおける個々のエントリを指します。各フレームは、特定の関数呼び出しに対応し、その関数の名前、ファイル名、行番号などの情報を含みます。
  • トレースバック (Traceback): スタックトレースを生成し、表示するプロセスを指します。Goランタイムのruntime·traceback関数がこの処理を担当します。
  • runtime·gentraceback: Goランタイム内部で使用される関数で、実際のスタックトレースのフレームを収集する役割を担います。この関数は、収集したフレームの数を返します。
  • TracebackMaxFrames: このコミットで導入された定数で、スタックトレースで表示されるフレームの最大数を定義します。これにより、出力の長さを制限し、可読性を維持します。
  • ARM/x86アーキテクチャ: traceback_arm.ctraceback_x86.cは、それぞれARMおよびx86アーキテクチャ向けのスタックトレース生成ロジックを実装したファイルです。アーキテクチャによってレジスタの扱いなどが異なるため、それぞれに特化した実装が必要になります。

技術的詳細

このコミットの主要な変更点は、runtime·traceback関数に、runtime·gentracebackが返したフレーム数とTracebackMaxFramesを比較するロジックを追加したことです。

  1. TracebackMaxFramesの導入: src/pkg/runtime/runtime.hTracebackMaxFramesという定数が追加されました。これは、トレースバックで表示するフレームの最大数を100に設定します。

    enum
    {
    	// The maximum number of frames we print for a traceback
    	TracebackMaxFrames = 100,
    };
    
  2. runtime·tracebackの変更: src/pkg/runtime/traceback_arm.csrc/pkg/runtime/traceback_x86.c内のruntime·traceback関数が変更されました。

    • runtime·gentracebackの戻り値をnという変数で受け取るようになりました。runtime·gentracebackは、実際に収集されたフレームの数を返します。
    • n == TracebackMaxFramesという条件が追加されました。これは、収集されたフレームの数が最大フレーム数と等しい場合、つまり、表示上限に達してフレームが省略された場合に真となります。
    • この条件が真の場合、runtime·printf("...additional frames elided...\\n");というメッセージが出力されるようになりました。このメッセージは、「追加のフレームが省略されました」という意味で、ユーザーにトレースバックが完全ではないことを通知します。

    変更前(例: traceback_arm.c):

    if(runtime·gentraceback(pc, sp, lr, gp, 0, nil, 100, nil, nil, false) == 0)
    	runtime·gentraceback(pc, sp, lr, gp, 0, nil, 100, nil, nil, true);
    

    変更後(例: traceback_arm.c):

    int32 n; // nを宣言
    n = runtime·gentraceback(pc, sp, lr, gp, 0, nil, TracebackMaxFrames, nil, nil, false);
    if(n == 0)
    	runtime·gentraceback(pc, sp, lr, gp, 0, nil, TracebackMaxFrames, nil, nil, true);
    if(n == TracebackMaxFrames) // 追加された条件
    	runtime·printf("...additional frames elided...\\n"); // 追加されたメッセージ出力
    

この変更により、スタックトレースの出力が最大フレーム数に達した場合に、その旨が明示的に表示されるようになり、デバッグ時の情報不足が解消されます。

コアとなるコードの変更箇所

このコミットにおけるコアとなるコードの変更箇所は以下の3ファイルです。

  1. src/pkg/runtime/runtime.h:

    • TracebackMaxFramesという定数が追加されました。
  2. src/pkg/runtime/traceback_arm.c:

    • runtime·traceback関数内で、runtime·gentracebackの戻り値を受け取るn変数が追加されました。
    • n == TracebackMaxFramesの場合に「...additional frames elided...」メッセージを出力するロジックが追加されました。
  3. src/pkg/runtime/traceback_x86.c:

    • runtime·traceback関数内で、runtime·gentracebackの戻り値を受け取るn変数が追加されました。
    • n == TracebackMaxFramesの場合に「...additional frames elided...」メッセージを出力するロジックが追加されました。

コアとなるコードの解説

src/pkg/runtime/runtime.h

@@ -716,6 +716,11 @@ void	runtime·traceback(uintptr pc, uintptr sp, uintptr lr, G* gp);
 void	runtime·tracebackothers(G*);
 bool	runtime·haszeroargs(uintptr pc);
 bool	runtime·topofstack(Func*);
+enum
+{
+	// The maximum number of frames we print for a traceback
+	TracebackMaxFrames = 100,
+};

この変更は、GoランタイムのヘッダーファイルにTracebackMaxFramesという新しい列挙型定数を追加しています。この定数は、スタックトレースで表示されるフレームの最大数を100に設定します。これにより、トレースバックの出力が過度に長くなるのを防ぎ、デバッグ時の可読性を維持します。コメントでその目的が明確に説明されています。

src/pkg/runtime/traceback_arm.c

@@ -231,6 +231,8 @@ runtime·printcreatedby(G *gp)
 void
 runtime·traceback(uintptr pc, uintptr sp, uintptr lr, G *gp)
 {
+	int32 n; // nを宣言
+
 	if(gp->status == Gsyscall) {
 		// Override signal registers if blocked in system call.
 		pc = gp->syscallpc;
@@ -240,8 +242,11 @@ runtime·traceback(uintptr pc, uintptr sp, uintptr lr, G *gp)
 
 	// Print traceback. By default, omits runtime frames.
 	// If that means we print nothing at all, repeat forcing all frames printed.
-	if(runtime·gentraceback(pc, sp, lr, gp, 0, nil, 100, nil, nil, false) == 0)
-		runtime·gentraceback(pc, sp, lr, gp, 0, nil, 100, nil, nil, true);
+	n = runtime·gentraceback(pc, sp, lr, gp, 0, nil, TracebackMaxFrames, nil, nil, false); // 戻り値をnに格納
+	if(n == 0)
+		runtime·gentraceback(pc, sp, lr, gp, 0, nil, TracebackMaxFrames, nil, nil, true);
+	if(n == TracebackMaxFrames) // nが最大フレーム数と等しい場合
+		runtime·printf("...additional frames elided...\\n"); // メッセージを出力
 	runtime·printcreatedby(gp);
 }

このコードは、ARMアーキテクチャ向けのruntime·traceback関数の変更を示しています。

  1. int32 n;が追加され、runtime·gentraceback関数が返すフレーム数を格納するための変数が宣言されています。
  2. runtime·gentracebackの第7引数がハードコードされた100から、新しく定義されたTracebackMaxFrames定数に変更されています。これにより、最大フレーム数の設定が一元化されます。
  3. 最も重要な変更は、if(n == TracebackMaxFrames)という条件文の追加です。これは、runtime·gentracebackTracebackMaxFramesで指定された数のフレームをすべて収集した場合(つまり、それ以上のフレームが存在する可能性があるが、上限に達したために省略された場合)に真となります。
  4. この条件が真の場合、runtime·printf("...additional frames elided...\\n");が実行され、「...additional frames elided...」というメッセージが標準出力に表示されます。これは、スタックトレースが完全ではなく、一部のフレームが省略されていることをユーザーに通知します。

src/pkg/runtime/traceback_x86.c

@@ -232,6 +232,8 @@ runtime·printcreatedby(G *gp)
 void
 runtime·traceback(uintptr pc, uintptr sp, uintptr lr, G *gp)
 {
+	int32 n; // nを宣言
+
 	USED(lr);
 
 	if(gp->status == Gsyscall) {
@@ -242,8 +244,11 @@ runtime·traceback(uintptr pc, uintptr sp, uintptr lr, G *gp)
 	
 	// Print traceback. By default, omits runtime frames.
 	// If that means we print nothing at all, repeat forcing all frames printed.
-	if(runtime·gentraceback(pc, sp, 0, gp, 0, nil, 100, nil, nil, false) == 0)
-		n = runtime·gentraceback(pc, sp, 0, gp, 0, nil, 100, nil, nil, true);
+	n = runtime·gentraceback(pc, sp, 0, gp, 0, nil, TracebackMaxFrames, nil, nil, false); // 戻り値をnに格納
+	if(n == 0)
+		n = runtime·gentraceback(pc, sp, 0, gp, 0, nil, TracebackMaxFrames, nil, nil, true);
+	if(n == TracebackMaxFrames) // nが最大フレーム数と等しい場合
+		runtime·printf("...additional frames elided...\\n"); // メッセージを出力
 	runtime·printcreatedby(gp);
 }

このコードは、x86アーキテクチャ向けのruntime·traceback関数の変更を示しており、traceback_arm.cと同様のロジックが適用されています。TracebackMaxFramesの利用と、フレームが省略された場合のメッセージ出力が追加されています。これにより、異なるアーキテクチャでも一貫したトレースバックの挙動が保証されます。

これらの変更により、Goランタイムのスタックトレース機能は、よりユーザーフレンドリーで情報量の多いものになりました。デバッグ時にスタックトレースが途中で切れている場合に、その事実が明示されることで、開発者はより正確な状況判断ができるようになります。

関連リンク

参考にした情報源リンク

  • Go Issue 7180: https://github.com/golang/go/issues/7180
  • Go CL 55810044: https://golang.org/cl/55810044
  • Go言語のソースコード (runtimeパッケージ): https://github.com/golang/go/tree/master/src/runtime
  • Go言語のスタックトレースに関するドキュメント (Goのバージョンやドキュメントの更新により、具体的なパスは異なる場合がありますが、一般的にGoの公式ドキュメントやブログでスタックトレースに関する情報が見つかります。)
  • スタックトレースの一般的な概念に関する情報源 (例: Wikipedia, プログラミング関連の技術ブログなど)
  • ARMアーキテクチャとx86アーキテクチャに関する一般的な情報源 (例: Wikipedia, CPUアーキテクチャに関する書籍など)