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

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

このコミットは、Go言語のmisc/cgo/testディレクトリに新しいテストケースを追加するものです。具体的には、GoプログラムがCgoを介してC言語のコードと連携する際に、外部スレッド(Goランタイムが管理しないスレッド)でSIGPROFシグナルを受信した場合の挙動を検証するためのテストです。これは、Goのプロファイリングメカニズムが外部スレッドからの予期せぬシグナルに対して堅牢であることを保証するために重要です。

コミット

commit 512ec7036ec497969bb085a520544ad61a4a5cc0
Author: Shenghou Ma <minux.ma@gmail.com>
Date:   Wed May 15 12:33:29 2013 +0800

    misc/cgo/test: test for issue 5337.
    Test for CL 9226043.
    
    R=golang-dev, dave, iant, bradfitz
    CC=golang-dev
    https://golang.org/cl/9249043

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

https://github.com/golang/go/commit/512ec7036ec497969bb085a520544ad61a4a5cc0

元コミット内容

このコミットは、Goのmisc/cgo/testディレクトリに、issue 5337に関連するテストを追加します。これは、Goの変更リスト(Change List: CL)9226043のテストとして作成されました。

変更の背景

Go言語では、Cgoを通じてC言語のコードを呼び出すことができます。Cgoを使用すると、Goのランタイムが管理しないスレッド(外部スレッド)がCコード内で作成される可能性があります。Goのプロファイリングツールは、プログラムの実行中にSIGPROFシグナルを送信して、CPU使用率などの情報を収集します。

問題は、Goランタイムが管理するGoスレッドだけでなく、Cgoを介して作成された外部スレッドがSIGPROFシグナルを受信した場合に、Goプログラムがクラッシュしたり、予期せぬ動作をしたりする可能性があったことです。issue 5337は、この特定のシナリオ、つまり外部スレッドでSIGPROFシグナルが受信された場合に、Goプログラムが堅牢に動作することを保証するための修正または検証の必要性を示唆しています。

このコミットは、その問題が修正されたことを確認するため、またはそのような状況下でのGoプログラムの安定性を検証するためのテストケースを追加することで、この背景に対処しています。

前提知識の解説

Cgo

Cgoは、GoプログラムからC言語のコードを呼び出すためのGoの機能です。Goのソースファイル内にCコードを直接記述したり、既存のCライブラリをリンクしたりすることができます。Cgoを使用すると、Goのガベージコレクタやスケジューラとは異なるメモリ管理やスレッドモデルを持つCコードと連携することになります。

SIGPROFシグナル

SIGPROFは、Unix系システムでプロセスに送信されるシグナルの一つです。主にプロファイリングツールによって使用されます。プロファイリングツールは、一定の間隔でSIGPROFシグナルをプロセスに送信し、シグナルハンドラ内で現在のプログラムカウンタ(実行中の命令のアドレス)を記録することで、どの関数がCPU時間を多く消費しているかを特定します。

pthread_createと外部スレッド

pthread_createは、POSIXスレッド(pthreads)ライブラリで提供される関数で、新しいスレッドを作成するために使用されます。Cgoを介してCコードがpthread_createを呼び出すと、Goランタイムが直接管理しない「外部スレッド」が生成されます。Goのプロファイリングメカニズムは、Goランタイムが管理するGoルーチン(Goスレッド)に対して最適化されていますが、外部スレッドに対するSIGPROFの処理はより複雑になります。

Goのプロファイリング

Goには、CPUプロファイリング、メモリプロファイリング、ブロックプロファイリングなど、様々なプロファイリングツールが組み込まれています。これらのツールは、プログラムのパフォーマンスボトルネックを特定するのに役立ちます。CPUプロファイリングは、SIGPROFシグナルを利用して、プログラムの実行中にサンプリングを行います。

技術的詳細

このコミットで追加されたテストは、以下のシナリオをシミュレートします。

  1. Cgoを介したCコードの実行: GoのテストコードからCgoを介してC関数test5337()が呼び出されます。
  2. Cスレッドの作成: test5337()関数内で、pthread_createを使用して新しいCスレッドが作成されます。このスレッドはGoランタイムの管理外です。
  3. SIGPROFの送信: 作成されたCスレッドは、pthread_kill(pthread_self(), SIGPROF)を呼び出すことで、自分自身にSIGPROFシグナルを送信します。これは、プロファイリングツールが外部スレッドにシグナルを送信する状況を模倣しています。
  4. スレッドの終了待機: メインのC関数は、pthread_joinを使用して、作成したCスレッドが終了するのを待ちます。

このテストの目的は、外部スレッドがSIGPROFシグナルを受信しても、Goプログラム全体がクラッシュしたり、デッドロックに陥ったりすることなく、正常に動作し続けることを確認することです。特に、Goのプロファイリングメカニズムが、Goランタイムが認識しないスレッドからのSIGPROFシグナルを適切に処理できるかどうかが検証されます。

issue5337.goファイルには、Windows以外のシステム向けのテストコードが含まれています。WindowsではSIGPROFpthreadの概念が異なるため、issue5337w.goという別のファイルでWindows向けのダミーテストが提供されています。これは、クロスプラットフォームでの互換性を考慮した設計です。

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

このコミットでは、以下の3つのファイルが変更されています。

  1. misc/cgo/test/cgo_test.go:

    • Test5337という新しいテスト関数が追加され、既存のテストスイートに組み込まれています。
    --- a/misc/cgo/test/cgo_test.go
    +++ b/misc/cgo/test/cgo_test.go
    @@ -39,5 +39,6 @@ func TestCthread(t *testing.T)             { testCthread(t) }
     func TestCallbackCallers(t *testing.T)     { testCallbackCallers(t) }
     func Test5227(t *testing.T)                { test5227(t) }
     func TestCflags(t *testing.T)              { testCflags(t) }
    +func Test5337(t *testing.T)                { test5337(t) }
     
     func BenchmarkCgoCall(b *testing.B) { benchCgoCall(b) }
    
  2. misc/cgo/test/issue5337.go (新規ファイル):

    • Windows以外のシステム向けに、SIGPROFシグナルを外部スレッドで発生させるCgoテストコードが定義されています。
    • // +build !windowsというビルドタグにより、Windows以外のOSでのみコンパイルされます。
    // Copyright 2013 The Go Authors.  All rights reserved.
    // Use of this source code is governed by a BSD-style
    // license that can be found in the LICENSE file.
    
    // +build !windows
    
    package cgotest
    
    /*
    #include <signal.h>
    #include <pthread.h>
    
    static void *thread(void *p) {
    	(void)p;
    	pthread_kill(pthread_self(), SIGPROF);
    	return NULL;
    }
    void test5337() {
    	pthread_t tid;
    	pthread_create(&tid, 0, thread, NULL);
    	pthread_join(tid, 0);
    }
    */
    import "C"
    
    import "testing"
    
    // Verify that we can withstand SIGPROF received on foreign threads
    func test5337(t *testing.T) {
    	C.test5337()
    }
    
  3. misc/cgo/test/issue5337w.go (新規ファイル):

    • Windowsシステム向けに、test5337関数のダミー実装が提供されています。
    • // +build windowsというビルドタグにより、Windowsでのみコンパイルされます。
    // Copyright 2013 The Go Authors.  All rights reserved.
    // Use of this source code is governed by a BSD-style
    // license that can be found in the LICENSE file.
    
    // +build windows
    
    package cgotest
    
    import "testing"
    
    func test5337(t *testing.T) {}
    

コアとなるコードの解説

issue5337.goのCgoコードがこのコミットの核心です。

#include <signal.h>
#include <pthread.h>

static void *thread(void *p) {
	(void)p;
	pthread_kill(pthread_self(), SIGPROF);
	return NULL;
}
void test5337() {
	pthread_t tid;
	pthread_create(&tid, 0, thread, NULL);
	pthread_join(tid, 0);
}
  • #include <signal.h>: シグナル関連の関数(pthread_killなど)を使用するために必要です。
  • #include <pthread.h>: POSIXスレッド関連の関数(pthread_create, pthread_kill, pthread_joinなど)を使用するために必要です。
  • static void *thread(void *p):
    • これは新しいスレッドで実行される関数です。
    • pthread_kill(pthread_self(), SIGPROF);が最も重要な部分です。pthread_self()は現在のスレッドのIDを返し、そのスレッドIDに対してSIGPROFシグナルを送信します。これにより、Goランタイムが管理しないCスレッドがSIGPROFを受信する状況をシミュレートします。
  • void test5337():
    • GoコードからCgoを介して呼び出されるエントリポイントとなるC関数です。
    • pthread_t tid;: スレッドIDを格納するための変数です。
    • pthread_create(&tid, 0, thread, NULL);: 新しいスレッドを作成し、そのスレッドでthread関数を実行させます。第2引数の0はデフォルト属性を使用することを意味します。
    • pthread_join(tid, 0);: 作成したスレッドtidが終了するまで、呼び出し元のスレッド(この場合はtest5337を実行しているスレッド)の実行をブロックします。これにより、テストがCスレッドの実行完了を待つことができます。

Go側のtest5337(t *testing.T)関数は非常にシンプルで、単にCgoを介してC.test5337()を呼び出すだけです。このテストが成功するということは、Goランタイムが外部スレッドからのSIGPROFシグナルを適切に処理し、プログラムの安定性を損なわないことを意味します。

関連リンク

参考にした情報源リンク