[インデックス 18507] ファイルの概要
このコミットは、Go言語の標準ライブラリである net/http
パッケージにおいて、パニック発生時のスタックトレースバッファサイズを4KBから64KBに拡張する変更です。これにより、特にテンプレート実行時など、スタックトレースが長くなる状況での情報欠落を防ぎ、デバッグの助けとなる完全なトレースが出力されるようになります。
コミット
commit 645a341b7d4210eece285c8dbe6e3e6cdbfbe35e
Author: David Symonds <dsymonds@golang.org>
Date: Fri Feb 14 10:15:38 2014 +1100
net/http: increase panic stack trace buffer size from 4 KB to 64 KB.
4 KB is a bit too small in some situations (e.g. panic during a
template execution), and ends up with an unhelpfully-truncated trace.
64 KB should be much more likely to capture the useful information.
There's not a garbage generation issue, since this code should only
be triggered when there's something seriously wrong with the program.
LGTM=bradfitz
R=bradfitz
CC=golang-codereviews
https://golang.org/cl/63520043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/645a341b7d4210eece285c8dbe6e3e6cdbfbe35e
元コミット内容
net/http
パッケージにおけるパニック時のスタックトレースバッファサイズを、従来の4KBから64KBへと増大させる変更。これにより、特にテンプレートの実行中など、スタックトレースが長大になるケースで、トレースが途中で切れてしまう問題を解消し、より有用なデバッグ情報を提供できるようになる。この変更は、プログラムに深刻な問題が発生した場合にのみトリガーされるため、ガベージ生成に関する懸念はないとされている。
変更の背景
Go言語の net/http
パッケージは、HTTPサーバーの実装を提供します。このサーバーがリクエストを処理している最中にパニック(Goにおけるランタイムエラーの一種)が発生した場合、通常は recover
機構によって捕捉され、エラー情報と共にスタックトレースがログに出力されます。
しかし、従来の net/http
パッケージでは、このスタックトレースを格納するためのバッファサイズが4KBに固定されていました。コミットメッセージに記載されているように、特にWebアプリケーションでテンプレートのレンダリング中にパニックが発生するような複雑なシナリオでは、コールスタックが深くなり、生成されるスタックトレースのテキストが4KBを容易に超えてしまうことがありました。
スタックトレースがバッファサイズを超過すると、トレースの末尾が切り捨てられてしまい、問題の根本原因を特定するために必要な情報が欠落する可能性がありました。これは、開発者にとってデバッグ作業を著しく困難にする問題でした。このコミットは、この「不親切に切り詰められたトレース」という問題を解決し、より完全なデバッグ情報を提供することを目的としています。
前提知識の解説
このコミットを理解するためには、以下のGo言語の概念と net/http
パッケージの動作に関する知識が必要です。
-
PanicとRecover:
- Panic: Go言語におけるパニックは、プログラムの通常の実行フローを中断させるランタイムエラーです。これは、配列の範囲外アクセス、nilポインタのデリファレンス、または明示的な
panic
関数の呼び出しなどによって発生します。パニックが発生すると、現在のゴルーチンは実行を停止し、遅延関数(defer
)が実行されながらコールスタックを巻き戻していきます。 - Recover:
recover
は、defer
関数内で呼び出された場合にのみ機能する組み込み関数です。recover
がパニック中のゴルーチンで呼び出されると、パニックの値を捕捉し、ゴルーチンの実行を再開させることができます。これにより、プログラムがクラッシュするのを防ぎ、エラーを適切に処理する機会が与えられます。net/http
サーバーでは、リクエスト処理中に発生したパニックを捕捉し、サーバー全体が停止するのを防ぐためにrecover
が利用されています。
- Panic: Go言語におけるパニックは、プログラムの通常の実行フローを中断させるランタイムエラーです。これは、配列の範囲外アクセス、nilポインタのデリファレンス、または明示的な
-
スタックトレース (Stack Trace):
- スタックトレースは、プログラムが特定の時点(通常はエラーやパニックが発生した時点)で実行していた関数の呼び出し履歴を示すリストです。各エントリは、関数名、ファイル名、行番号を含み、問題が発生した場所とその呼び出し元を特定するのに役立ちます。Goでは、
runtime.Stack
関数を使用して現在のゴルーチンのスタックトレースを取得できます。
- スタックトレースは、プログラムが特定の時点(通常はエラーやパニックが発生した時点)で実行していた関数の呼び出し履歴を示すリストです。各エントリは、関数名、ファイル名、行番号を含み、問題が発生した場所とその呼び出し元を特定するのに役立ちます。Goでは、
-
net/http
パッケージ:- Goの標準ライブラリの一部であり、HTTPクライアントとサーバーの実装を提供します。HTTPサーバーは、受信したリクエストを処理するためにゴルーチンを起動します。このコミットで変更される
(*conn).serve()
メソッドは、個々のHTTP接続を処理するゴルーチン内で実行されます。
- Goの標準ライブラリの一部であり、HTTPクライアントとサーバーの実装を提供します。HTTPサーバーは、受信したリクエストを処理するためにゴルーチンを起動します。このコミットで変更される
-
runtime.Stack
関数:runtime.Stack(buf []byte, all bool) int
は、現在のゴルーチンのスタックトレースをbuf
に書き込み、書き込まれたバイト数を返します。all
がtrue
の場合、すべてのゴルーチンのスタックトレースが書き込まれますが、このコミットではfalse
が使用されており、現在のゴルーチンのトレースのみが対象です。buf
が小さすぎる場合、トレースは切り詰められます。
技術的詳細
このコミットの技術的な核心は、net/http
パッケージの (*conn).serve()
メソッド内の defer
関数ブロックにあります。この defer
関数は、HTTPリクエストの処理中に発生したパニックを捕捉し、そのスタックトレースをログに出力する役割を担っています。
変更前は、スタックトレースを格納するためのバッファ buf
のサイズが const size = 4096
(4KB) とハードコードされていました。
// 変更前
func (c *conn) serve() {
defer func() {
if err := recover(); err != nil {
const size = 4096 // 4 KB
buf := make([]byte, size)
buf = buf[:runtime.Stack(buf, false)]
log.Printf("http: panic serving %v: %v\n%s", c.remoteAddr, err, buf)
}
}()
// ... リクエスト処理ロジック ...
}
この4KBというサイズは、多くの一般的なパニックケースでは十分でしたが、コミットメッセージが指摘するように、特にWebアプリケーションで複雑なテンプレートエンジンを使用している場合など、コールスタックが非常に深くなる状況では、生成されるスタックトレースのテキストが4KBを容易に超えてしまうことがありました。runtime.Stack
関数は、提供されたバッファに収まらない場合、トレースを切り詰めてしまうため、デバッグに必要な情報が失われる結果となっていました。
このコミットでは、size
定数が 64 << 10
に変更されました。
// 変更後
func (c *conn) serve() {
defer func() {
if err := recover(); err != nil {
const size = 64 << 10 // 64 KB
buf := make([]byte, size)
buf = buf[:runtime.Stack(buf, false)]
log.Printf("http: panic serving %v: %v\n%s", c.remoteAddr, err, buf)
}
}()
// ... リクエスト処理ロジック ...
}
64 << 10
はビットシフト演算子で、64 * 2^10
、つまり 64 * 1024
を意味し、結果として65536バイト、すなわち64KBになります。この変更により、スタックトレースバッファのサイズが16倍に拡張されました。
コミットメッセージでは、「There's not a garbage generation issue, since this code should only be triggered when there's something seriously wrong with the program.」と述べられています。これは、このバッファの割り当てと使用は、プログラムが正常に動作している間は発生せず、深刻なエラー(パニック)が発生した場合にのみ行われるため、通常時のメモリ使用量やガベージコレクションのパフォーマンスには影響を与えないということを意味しています。つまり、この64KBのバッファは、デバッグ情報収集という特定の目的のために、必要な時にのみ一時的に確保されるものであり、通常のアプリケーション実行における「ガベージ生成」の問題とはならない、という設計判断が示されています。
この変更は、Goの net/http
サーバーの堅牢性を高め、開発者がより効率的に問題を診断できるようにするための、実用的な改善と言えます。
コアとなるコードの変更箇所
--- a/src/pkg/net/http/server.go
+++ b/src/pkg/net/http/server.go
@@ -1083,7 +1083,7 @@ func validNPN(proto string) bool {
func (c *conn) serve() {
defer func() {
if err := recover(); err != nil {
- const size = 4096
+ const size = 64 << 10
buf := make([]byte, size)
buf = buf[:runtime.Stack(buf, false)]
log.Printf("http: panic serving %v: %v\n%s", c.remoteAddr, err, buf)
コアとなるコードの解説
変更は src/pkg/net/http/server.go
ファイルの (*conn).serve()
メソッド内にあります。
このメソッドは、HTTP接続を処理するゴルーチン内で実行されます。その内部には defer
関数が定義されており、この defer
関数は recover()
を呼び出すことで、serve()
メソッドの実行中に発生したパニックを捕捉します。
捕捉されたパニックがある場合 (if err := recover(); err != nil
)、以下の処理が行われます。
-
const size = 64 << 10
:- これがこのコミットの主要な変更点です。以前は
4096
(4KB) だった定数size
が、64 << 10
(64KB) に変更されました。このsize
は、スタックトレースを格納するためのバイトスライスbuf
の容量を決定します。
- これがこのコミットの主要な変更点です。以前は
-
buf := make([]byte, size)
:- 新しい
size
(64KB) の容量を持つバイトスライスbuf
が作成されます。このスライスが、runtime.Stack
関数によって書き込まれるスタックトレースのテキストを保持します。
- 新しい
-
buf = buf[:runtime.Stack(buf, false)]
:runtime.Stack(buf, false)
が呼び出され、現在のゴルーチンのスタックトレースがbuf
に書き込まれます。false
は、現在のゴルーチンのみのトレースを取得することを意味します。runtime.Stack
は実際に書き込まれたバイト数を返すため、その戻り値を使ってbuf
スライスの長さを調整しています。これにより、buf
は実際にスタックトレースが占める正確なサイズにトリムされます。
-
log.Printf("http: panic serving %v: %v\n%s", c.remoteAddr, err, buf)
:- 最後に、
log.Printf
を使用して、パニック情報がログに出力されます。出力される情報には、接続元のアドレス (c.remoteAddr
)、パニックの値 (err
)、そして取得されたスタックトレース (buf
) が含まれます。
- 最後に、
この変更により、パニック発生時に出力されるスタックトレースがより長くなり、デバッグに必要な情報が切り捨てられる可能性が大幅に減少しました。
関連リンク
- Go CL 63520043: https://golang.org/cl/63520043
参考にした情報源リンク
- Go言語の
panic
とrecover
について: - Go言語の
runtime.Stack
関数について: - Go言語の
net/http
パッケージについて: - ビットシフト演算子
<<
について: