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

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

このコミットは、Go言語のランタイムにおけるデバッグ出力の修正に関するものです。具体的には、newproc(新しいゴルーチンを作成する内部関数)のデバッグプリント文が正しく機能するように修正されています。

コミット

commit b65271d008daae3c9b424a0519d6cf79f0583675
Author: Anthony Martin <ality@pbrane.org>
Date:   Sat May 18 15:47:15 2013 -0700

    runtime: fix newproc debugging print
    
    R=golang-dev, remyoudompheng, r
    CC=golang-dev
    https://golang.org/cl/9249044

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

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

元コミット内容

runtime: fix newproc debugging print

このコミットは、Goランタイム内のnewproc関数におけるデバッグ用のプリント文を修正することを目的としています。

変更の背景

Go言語のランタイムは、プログラムの実行を管理する低レベルな部分であり、ゴルーチンのスケジューリング、メモリ管理、ガベージコレクションなどを担当しています。ランタイムのデバッグは、これらの内部動作を理解し、問題を発見するために不可欠です。

newproc関数は、新しいゴルーチンが作成される際に呼び出されるランタイムの内部関数です。開発者がランタイムの動作をデバッグする際、この関数がいつ、どのような引数で呼び出されたかを知ることは非常に有用です。しかし、このコミット以前は、newproc内のデバッグプリント文がコメントアウトされているか、あるいは正しく機能していなかった可能性があります。

このコミットの背景には、newprocのデバッグプリントが意図した通りに動作せず、ランタイムのデバッグ作業を妨げていたという問題があったと考えられます。特に、printfのような標準Cライブラリの関数ではなく、Goランタイム内部のruntime·printfを使用する必要があったこと、そして関数ポインタの扱いに関する問題が考えられます。

前提知識の解説

このコミットを理解するためには、以下の概念について知っておく必要があります。

  • Goランタイム (Go Runtime): Goプログラムは、Goランタイムと呼ばれる軽量な実行環境上で動作します。Goランタイムは、ゴルーチンのスケジューリング、メモリ割り当て、ガベージコレクション、システムコールなど、プログラムの低レベルな側面を管理します。C言語で書かれた部分とGo言語で書かれた部分が混在しています。
  • ゴルーチン (Goroutine): Go言語における軽量な並行処理の単位です。OSのスレッドよりもはるかに軽量で、数百万のゴルーチンを同時に実行することも可能です。goキーワードを使って関数を呼び出すことで新しいゴルーチンが作成されます。
  • newproc / newproc1: Goランタイムの内部関数で、新しいゴルーチンを作成する役割を担います。goキーワードが使われると、最終的にこれらの関数が呼び出され、新しいゴルーチンが初期化され、スケジューラに登録されます。
  • printfruntime·printf:
    • printf: 一般的なC言語の標準ライブラリ関数で、フォーマットされた文字列を標準出力に出力します。
    • runtime·printf: Goランタイム内部で使用される、ランタイム専用のプリント関数です。Goランタイムは、標準Cライブラリに依存しないように設計されているため、独自のプリント関数を持っています。ランタイムの低レベルな部分でデバッグ情報を出力する際に使用されます。この関数は、Goのソースコード内ではruntime.printfとして参照されますが、コンパイルされたバイナリではシンボル名がruntime·printfのようになることがあります(特にC言語で書かれた部分との連携において)。
  • FuncVal 構造体: Go言語の関数は、内部的にはFuncValという構造体で表現されます。この構造体には、関数のコードへのポインタ(fnフィールド)や、関連する情報が含まれています。

技術的詳細

このコミットの技術的な詳細は、Goランタイムのデバッグプリントのメカニズムと、C言語とGo言語の間のインターフェースにあります。

元のコードでは、newproc1関数内に以下のようなコメントアウトされた行がありました。

//printf("newproc1 %p %p narg=%d nret=%d\n", fn, argp, narg, nret);

この行は、C標準ライブラリのprintf関数を使用しようとしています。しかし、GoランタイムのCコードは、通常、標準Cライブラリに直接リンクしていません。代わりに、ランタイム独自のユーティリティ関数を使用します。そのため、printfを直接呼び出すと、リンクエラーや未定義の動作を引き起こす可能性があります。

また、元のコメントアウトされた行では、fnFuncVal型のポインタ)を直接%pで出力しようとしています。FuncValは構造体であり、その中の実際の関数ポインタはfn->fnフィールドに格納されています。したがって、関数のアドレスを出力するにはfn->fnを使用する必要があります。

このコミットは、以下の2つの問題を修正しています。

  1. printfからruntime·printfへの変更: 標準Cライブラリのprintfではなく、Goランタイム内部のruntime·printfを使用するように変更されました。これにより、ランタイムの独立性が保たれ、デバッグプリントが正しく機能するようになります。
  2. fnからfn->fnへの変更: FuncVal構造体のポインタfnから、実際の関数ポインタが格納されているfn->fnフィールドを参照するように変更されました。これにより、newproc1が作成しようとしているゴルーチンの関数の正確なアドレスがデバッグ出力に表示されるようになります。

これらの変更により、newproc1が呼び出された際に、新しいゴルーチンが実行する関数のアドレス、引数、戻り値の数といった重要なデバッグ情報が、Goランタイムの内部デバッグメカニズムを通じて正確に出力されるようになります。これは、ランタイム開発者がゴルーチンの生成とスケジューリングの動作を追跡する上で非常に役立ちます。

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

変更はsrc/pkg/runtime/proc.cファイル内の1箇所です。

--- a/src/pkg/runtime/proc.c
+++ b/src/pkg/runtime/proc.c
@@ -1491,7 +1491,7 @@ runtime·newproc1(FuncVal *fn, byte *argp, int32 narg, int32 nret, void *callerp
 	G *newg;
 	int32 siz;
 
-//printf("newproc1 %p %p narg=%d nret=%d\n", fn, argp, narg, nret);\n
+//runtime·printf("newproc1 %p %p narg=%d nret=%d\n", fn->fn, argp, narg, nret);\n
 	siz = narg + nret;
 	siz = (siz+7) & ~7;
 

具体的には、1492行目のコメントアウトされた行が修正されました。

コアとなるコードの解説

変更された行は、runtime·newproc1関数の内部にあります。

  • runtime·newproc1(FuncVal *fn, byte *argp, int32 narg, int32 nret, void *callerp):
    • fn: 新しいゴルーチンが実行する関数のFuncVal構造体へのポインタ。
    • argp: 関数の引数へのポインタ。
    • narg: 引数のバイトサイズ。
    • nret: 戻り値のバイトサイズ。
    • callerp: 呼び出し元のPC (Program Counter)。

修正前のコメントアウトされた行: //printf("newproc1 %p %p narg=%d nret=%d\n", fn, argp, narg, nret); この行は、C標準ライブラリのprintfを使用しようとしており、fnを直接ポインタとして渡しています。

修正後の行: //runtime·printf("newproc1 %p %p narg=%d nret=%d\n", fn->fn, argp, narg, nret); この行では、以下の2点が変更されています。

  1. printfruntime·printfに変更されました。これにより、Goランタイム内部のデバッグ出力メカニズムが使用されます。
  2. fnfn->fnに変更されました。FuncVal構造体のfnフィールドは、実際の関数のコードへのポインタを保持しています。この変更により、デバッグ出力にはFuncVal構造体のアドレスではなく、ゴルーチンが実行する関数の実際のエントリポイントのアドレスが表示されるようになります。

この修正により、newproc1が呼び出された際に、新しいゴルーチンがどの関数を実行しようとしているのか、その関数の引数と戻り値のサイズはどのくらいか、といった情報が正確にデバッグログに出力されるようになります。これは、Goランタイムの動作を詳細に分析する上で非常に重要な情報です。

関連リンク

参考にした情報源リンク