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

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

このコミットは、Go言語のランタイムプロファイリングツールであるpprofパッケージ内のテストファイルsrc/pkg/runtime/pprof/pprof_test.goに対する修正です。具体的には、TestGoroutineSwitchというテスト関数における、スタックトレースのチェックロジックが更新されています。

コミット

commit 39067c79f3812bd51a5184f54cf2779b37c6cb36
Author: Russ Cox <rsc@golang.org>
Date:   Thu Feb 13 01:16:20 2014 -0500

    runtime/pprof: fix arm build after CL 61270043
    
    TBR=dvyukov
    CC=golang-codereviews
    https://golang.org/cl/62960043

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

https://github.com/golang/go/commit/39067c79f3812bd51a5184f54cf2779b37c6cb36

元コミット内容

このコミットは、runtime/pprofパッケージにおいて、以前の変更リスト(CL 61270043)が適用された後のARMアーキテクチャでのビルド問題を修正するものです。

変更の背景

このコミットの背景には、Goランタイムのプロファイリング機能、特にpprofツールがどのようにスタックトレースを収集し、解釈するかの進化があります。

元のコミットメッセージにある「CL 61270043」は、Goの変更リスト(Change List)を指します。Goプロジェクトでは、コードの変更はGerritというコードレビューシステムを通じて提案され、各変更には一意のCL番号が割り当てられます。このCL 61270043が、ARMアーキテクチャにおける特定の挙動を変更したか、あるいは新たなコードパスを導入した結果、runtime/pprofのテストが期待通りに動作しなくなったと考えられます。

具体的には、プロファイリング時に取得されるスタックトレースにおいて、特定のフレームが「System」または「ExternalCode」として認識されるようになりました。これは、Goランタイムが外部のコード(C/C++など)やシステムコールを呼び出す際のスタックフレームの表現方法が変更されたことを示唆しています。pprofのテスト、特にTestGoroutineSwitchは、ゴルーチンの切り替え時のスタックトレースの正確性を検証するものであり、この変更によってテストが失敗するようになったため、テストロジックを更新する必要が生じました。

前提知識の解説

Go言語のプロファイリング (pprof)

Go言語には、プログラムのパフォーマンスを分析するための強力なプロファイリングツールが標準で組み込まれています。これがpprofです。pprofは、CPU使用率、メモリ割り当て、ゴルーチン、ミューテックス競合、ブロック操作など、様々な側面からプログラムの動作を可視化し、ボトルネックを特定するのに役立ちます。

  • CPUプロファイリング: プログラムがCPU時間をどこで消費しているかを分析します。
  • メモリプロファイリング: メモリの割り当てパターンを分析し、メモリリークや非効率なメモリ使用を特定します。
  • ゴルーチンプロファイリング: 実行中のゴルーチンの数やスタックトレースを分析します。

pprofは、プロファイルデータを収集し、それをグラフやテキスト形式で表示するためのツール群を提供します。特に、スタックトレースは、関数呼び出しの連鎖を追跡し、どの関数がどの関数を呼び出したかを示す重要な情報です。

スタックトレースとPC (Program Counter)

プログラムが実行される際、関数呼び出しはコールスタックに積まれていきます。スタックトレースは、このコールスタックの現在の状態を、呼び出し元の関数から現在の関数までの順序で表示したものです。各フレームは、関数名、ファイル名、行番号、そしてその関数が呼び出された時点のプログラムカウンタ(PC)を含みます。

  • PC (Program Counter): CPUが次に実行する命令のアドレスを指すレジスタです。スタックトレースでは、特定の関数呼び出しが行われた時点のPCが記録されます。

runtime.FuncForPC

Goのruntimeパッケージには、プログラムの実行時情報にアクセスするための関数が多数含まれています。runtime.FuncForPC(pc uintptr)は、与えられたプログラムカウンタ(PC)に対応する*runtime.Funcオブジェクトを返します。このruntime.Funcオブジェクトは、そのPCが属する関数の名前(Name()メソッド)、ファイル名、行番号などの情報を提供します。

ARMアーキテクチャ

ARM(Advanced RISC Machine)は、モバイルデバイスや組み込みシステムで広く使用されているCPUアーキテクチャです。Go言語はクロスプラットフォーム対応であり、ARMを含む様々なアーキテクチャで動作します。アーキテクチャ固有の最適化やシステムコールインターフェースの違いにより、特定のアーキテクチャで異なる挙動を示すことがあります。

ゴルーチン (Goroutine)

Go言語の並行処理の基本単位です。軽量なスレッドのようなもので、数千、数万のゴルーチンを同時に実行してもオーバーヘッドが少ないのが特徴です。ゴルーチンはGoランタイムによってスケジューリングされ、必要に応じてCPUコア間で切り替えられます。

TestGoroutineSwitch

このテスト関数は、pprofがゴルーチンの切り替えを正しくプロファイリングできることを確認するために存在します。ゴルーチンが切り替わる際に、pprofが取得するスタックトレースが期待通りのものであるかを検証します。特に、システムコールや外部コードへの呼び出しがスタックトレースにどのように現れるかは、プロファイリングの正確性に影響します。

技術的詳細

このコミットは、src/pkg/runtime/pprof/pprof_test.go内のTestGoroutineSwitch関数におけるスタックトレースの検証ロジックを修正しています。

元のコードでは、スタックトレースの特定のフレームがlen(stk) == 2(スタックトレースの深さが2)の場合に、そのフレームの関数名が「System」であれば、そのスタックトレースを無視していました。これは、Goランタイムが内部的に行うシステムコールなど、ユーザーコードとは直接関係のないスタックフレームをプロファイリング結果から除外するためと考えられます。

// Old code
if f != nil && f.Name() == "System" {
    return
}

このコミットでは、この条件にf.Name() == "ExternalCode"が追加されました。

// New code
if f != nil && (f.Name() == "System" || f.Name() == "ExternalCode") {
    return
}

この変更は、CL 61270043によって、ARMアーキテクチャでのスタックトレースの挙動が変わったことを示唆しています。具体的には、Goランタイムが外部のコード(例えば、C言語で書かれたライブラリやOSのAPIなど)を呼び出す際に、そのスタックフレームが以前は「System」として認識されていたか、あるいは全く認識されていなかったものが、新たに「ExternalCode」という名前で識別されるようになった可能性があります。

TestGoroutineSwitchは、ゴルーチンの切り替え時に発生するスタックトレースを検証するテストです。ゴルーチンが切り替わる際、Goランタイムは内部的な処理を行います。この内部処理や、場合によっては外部コードへの呼び出しがスタックトレースに現れることがあります。テストの目的は、ユーザーコードのスタックトレースが正しく取得されていることを確認することであり、ランタイム内部や外部コードの呼び出しによるノイズは除外したい場合があります。

したがって、この修正は、ARMアーキテクチャにおいて、プロファイリング時に「ExternalCode」という名前で現れるようになった特定のスタックフレームも「System」フレームと同様に無視することで、テストが正しくパスするように調整されたものです。これにより、pprofのテストが、Goランタイムの変更に適応し、引き続きプロファイリングの正確性を保証できるようになります。

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

変更はsrc/pkg/runtime/pprof/pprof_test.goファイル内のTestGoroutineSwitch関数にあります。

--- a/src/pkg/runtime/pprof/pprof_test.go
+++ b/src/pkg/runtime/pprof/pprof_test.go
@@ -218,7 +218,7 @@ func TestGoroutineSwitch(t *testing.T) {
 			// exists to record a PC without a traceback. Those are okay.
 			if len(stk) == 2 {
 				f := runtime.FuncForPC(stk[1])
-				if f != nil && f.Name() == "System" {
+				if f != nil && (f.Name() == "System" || f.Name() == "ExternalCode") {
 					return
 				}
 			}

コアとなるコードの解説

TestGoroutineSwitch関数は、ゴルーチンの切り替えがプロファイリングにどのように影響するかをテストします。このテストでは、プロファイルデータからスタックトレースを抽出し、その内容を検証しています。

変更された行は以下の部分です。

if f != nil && (f.Name() == "System" || f.Name() == "ExternalCode") {
    return
}
  • f := runtime.FuncForPC(stk[1]): stkはスタックトレースのPC(プログラムカウンタ)のリストです。stk[1]は、現在の関数を呼び出した一つ前のスタックフレームのPCを指します。runtime.FuncForPCを使って、このPCに対応する関数情報を取得しています。
  • f != nil: 関数情報が正常に取得できたことを確認します。
  • f.Name() == "System" || f.Name() == "ExternalCode": ここが変更の核心です。取得した関数の名前が「System」または「ExternalCode」であるかをチェックしています。
    • "System": Goランタイムの内部的な処理やシステムコールなど、Go言語のユーザーコードではない部分のスタックフレームを指すことが多いです。
    • "ExternalCode": このコミットで追加された条件です。CL 61270043以降、ARMアーキテクチャにおいて、Goランタイムが外部の(Go言語ではない)コードを呼び出した際に、そのスタックフレームが「ExternalCode」という名前で識別されるようになったことを示しています。
  • return: もしスタックフレームが「System」または「ExternalCode」であれば、このスタックトレースはテストの検証対象外として、それ以上の処理を行わずにテスト関数から抜けます。これは、これらのフレームがプロファイリングのノイズとなる可能性があり、テストの目的である「ユーザーコードの正確なプロファイリング」とは直接関係ないためです。

この修正により、ARMアーキテクチャで「ExternalCode」として識別されるようになったスタックフレームも適切に無視されるようになり、TestGoroutineSwitchが意図通りに動作し、プロファイリングの正確性を保証できるようになりました。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント
  • Go言語のソースコード(特にruntimeおよびruntime/pprofパッケージ)
  • Gerrit Code Review (Goプロジェクトの変更リストを検索するために使用)
    • CL 61270043に関する具体的な情報は、GoのGerritアーカイブを検索することで見つかる可能性がありますが、一般公開されている情報源からは直接的な詳細が見つかりにくい場合があります。しかし、コミットメッセージからそのCLがARMビルドに影響を与えたことは明らかです。
  • Goコミュニティの議論やメーリングリスト(過去の議論から背景情報を得るため)
  • Goのpprofツールの使用方法に関する一般的な記事やチュートリアル。
  • Goのテストフレームワークに関する知識。