[インデックス 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
シグナルを利用して、プログラムの実行中にサンプリングを行います。
技術的詳細
このコミットで追加されたテストは、以下のシナリオをシミュレートします。
- Cgoを介したCコードの実行: GoのテストコードからCgoを介してC関数
test5337()
が呼び出されます。 - Cスレッドの作成:
test5337()
関数内で、pthread_create
を使用して新しいCスレッドが作成されます。このスレッドはGoランタイムの管理外です。 - SIGPROFの送信: 作成されたCスレッドは、
pthread_kill(pthread_self(), SIGPROF)
を呼び出すことで、自分自身にSIGPROF
シグナルを送信します。これは、プロファイリングツールが外部スレッドにシグナルを送信する状況を模倣しています。 - スレッドの終了待機: メインのC関数は、
pthread_join
を使用して、作成したCスレッドが終了するのを待ちます。
このテストの目的は、外部スレッドがSIGPROF
シグナルを受信しても、Goプログラム全体がクラッシュしたり、デッドロックに陥ったりすることなく、正常に動作し続けることを確認することです。特に、Goのプロファイリングメカニズムが、Goランタイムが認識しないスレッドからのSIGPROF
シグナルを適切に処理できるかどうかが検証されます。
issue5337.go
ファイルには、Windows以外のシステム向けのテストコードが含まれています。WindowsではSIGPROF
やpthread
の概念が異なるため、issue5337w.go
という別のファイルでWindows向けのダミーテストが提供されています。これは、クロスプラットフォームでの互換性を考慮した設計です。
コアとなるコードの変更箇所
このコミットでは、以下の3つのファイルが変更されています。
-
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) }
-
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() }
- Windows以外のシステム向けに、
-
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) {}
- Windowsシステム向けに、
コアとなるコードの解説
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
シグナルを適切に処理し、プログラムの安定性を損なわないことを意味します。
関連リンク
- Go言語のCgoに関する公式ドキュメント: https://go.dev/blog/cgo
- Go言語のプロファイリングに関する公式ドキュメント: https://go.dev/doc/diagnostics#profiling
参考にした情報源リンク
- Goの変更リスト(CL)
9249043
(このコミットの元となったCL): https://go.dev/cl/9249043- このCLのレビューページには、
issue 5337
への言及があり、このテストが追加された背景がより詳細に説明されています。
- このCLのレビューページには、
- POSIX
pthread_create
manページ: https://man7.org/linux/man-pages/man3/pthread_create.3.html - POSIX
pthread_kill
manページ: https://man7.org/linux/man-pages/man3/pthread_kill.3.html - POSIX
signal
manページ: https://man7.org/linux/man-pages/man7/signal.7.html- 特に
SIGPROF
に関する説明。
- 特に