[インデックス 18453] ファイルの概要
このコミットは、GoランタイムがPlan 9オペレーティングシステム上で生成するパニック文字列を、他のシステムと一貫性のあるものにするための変更です。具体的には、Plan 9固有のシステムトラップメッセージを、Goの標準的なパニックメッセージにマッピングすることで、エラー報告の均一化を図っています。
コミット
commit 120218afeb74e717adf1397f04ef4ca6a10add03
Author: David du Colombier <0intro@gmail.com>
Date: Tue Feb 11 09:34:43 2014 +0100
runtime: homogenize panic strings on Plan 9
LGTM=rsc
R=rsc
CC=golang-codereviews
https://golang.org/cl/61410046
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/120218afeb74e717adf1397f04ef4ca6a10add03
元コミット内容
runtime: homogenize panic strings on Plan 9
変更の背景
Go言語はクロスプラットフォーム対応を重視しており、異なるオペレーティングシステム上でも一貫した挙動とエラー報告を提供することを目指しています。パニック(panic)はGoプログラムにおける回復不可能なエラーを示すメカニズムであり、その際に表示されるメッセージは、開発者が問題の原因を特定する上で非常に重要です。
Plan 9は、ベル研究所で開発された分散オペレーティングシステムであり、Go言語の設計思想やアセンブリ構文に大きな影響を与えました。しかし、Plan 9のシステムコールやエラー報告メカニズムは、他の一般的なOS(Linux, Windows, macOSなど)とは異なる場合があります。
このコミット以前は、GoランタイムがPlan 9上で特定のシステムトラップ(例: メモリ不正アクセス、ゼロ除算)を検出した際に、Plan 9固有のトラップメッセージ(sys: trap: fault read addr
など)をそのままパニック文字列として出力していた可能性があります。これは、Goの他のプラットフォームでのパニックメッセージ(例: invalid memory address or nil pointer dereference
)と異なり、開発者にとって混乱を招く可能性がありました。
この変更の背景には、Goランタイムが生成するパニックメッセージの「均一化(homogenize)」という明確な目的があります。これにより、どのプラットフォームでGoプログラムが実行されても、開発者は予測可能で理解しやすいパニックメッセージを受け取ることができ、デバッグ作業の効率化に貢献します。
前提知識の解説
Goにおけるパニック (Panic)
Go言語におけるパニックは、プログラムの通常の実行フローを中断させるランタイムエラーの一種です。これは通常、プログラムが回復できない状態に陥ったことを示し、スタックトレースを出力してプログラムを終了させます。パニックは、以下のような状況で発生します。
- ランタイムエラー: ゼロ除算、nilポインタのデリファレンス、配列の範囲外アクセスなど。
- 明示的なパニック:
panic
組み込み関数を呼び出すことによって、開発者が意図的にパニックを発生させる場合。
パニックは、recover
組み込み関数とdefer
ステートメントを組み合わせることで捕捉し、処理を継続することも可能ですが、これは通常、エラーハンドリングの最終手段として用いられます。
Plan 9オペレーティングシステム
Plan 9 from Bell Labsは、1980年代後半から1990年代初頭にかけてベル研究所で開発された分散オペレーティングシステムです。その設計思想は、すべてをファイルとして扱う「everything is a file」原則、名前空間の概念、そして強力なネットワーク透過性に基づいています。
Go言語の設計者の一部はPlan 9の開発にも携わっており、Go言語のツールチェインやアセンブリ言語の構文(Plan 9アセンブラのスタイル)にその影響が見られます。Goランタイムは、Goプログラムが動作する基盤となる部分であり、OSとのインタラクションを抽象化する役割を担っています。
システムトラップ (System Trap)
システムトラップは、CPUが特定のイベント(例: ゼロ除算、不正なメモリアクセス、特権命令の実行など)を検出した際に発生する、ソフトウェア割り込みの一種です。OSはこれらのトラップを捕捉し、適切なハンドラを実行してエラーを処理したり、プロセスを終了させたりします。Plan 9のようなOSでは、これらのトラップが特定の「ノート(note)」またはメッセージとしてアプリケーションに通知されることがあります。
m->notesig
Goランタイムの内部構造において、m
はM(Machine)構造体を指し、OSスレッドを表します。notesig
は、このM構造体の一部であり、OSから受け取ったシグナルやトラップに関する情報を保持するフィールドであると推測されます。Plan 9のコンテキストでは、これがシステムトラップによって生成されたメッセージ文字列を格納するために使用されていると考えられます。
技術的詳細
このコミットの核心は、src/pkg/runtime/os_plan9.c
ファイル内のruntime·sigpanic
関数に対する変更です。runtime·sigpanic
は、Goランタイムがシグナル(またはPlan 9のトラップ)によってパニック状態に陥った際に呼び出される関数です。
変更前は、m->notesig
に格納されたPlan 9固有のトラップメッセージが、そのままruntime·panicstring(m->notesig)
によってパニック文字列として出力されていました。
変更後は、runtime·panicstring
を呼び出す前に、m->notesig
の内容をチェックする条件分岐が追加されています。
-
メモリ不正アクセス:
runtime·strcmp((byte*)m->notesig, (byte*)"sys: trap: fault read addr") >= 0 || runtime·strcmp((byte*)m->notesig, (byte*)"sys: trap: fault write addr") >= 0
この条件は、m->notesig
が「sys: trap: fault read addr
」(読み取り時の不正メモリアクセス)または「sys: trap: fault write addr
」(書き込み時の不正メモリアクセス)であるかをチェックしています。これらのPlan 9固有のメッセージが検出された場合、Goの標準的なパニックメッセージである「invalid memory address or nil pointer dereference
」に置き換えられます。 -
ゼロ除算:
runtime·strcmp((byte*)m->notesig, (byte*)"sys: trap: divide error") >= 0
この条件は、m->notesig
が「sys: trap: divide error
」(ゼロ除算エラー)であるかをチェックしています。この場合、Goの標準的なパニックメッセージである「integer divide by zero
」に置き換えられます。
これらの変更により、Plan 9上で発生した特定のシステムトラップが、Goの他のプラットフォームで発生する同種のエラーと同じ、より汎用的で理解しやすいパニックメッセージとして報告されるようになります。これは、Goのクロスプラットフォームでの一貫性を高めるための重要なステップです。
runtime·strcmp
は、Goランタイム内部で使用される文字列比較関数であり、C言語のstrcmp
と同様に動作します。byte*
へのキャストは、GoランタイムがC言語で記述された部分とGo言語で記述された部分が混在しているため、型安全性を確保しつつ文字列を比較するための慣習です。
コアとなるコードの変更箇所
src/pkg/runtime/os_plan9.c
ファイルのruntime·sigpanic
関数に以下の4行が追加されました。
--- a/src/pkg/runtime/os_plan9.c
+++ b/src/pkg/runtime/os_plan9.c
@@ -310,6 +310,10 @@ runtime·sigpanic(void)
{\n \tif(g->sigpc == 0)\n \t\truntime·panicstring(\"call of nil func value\");\n+\tif(runtime·strcmp((byte*)m->notesig, (byte*)\"sys: trap: fault read addr\") >= 0 || runtime·strcmp((byte*)m->notesig, (byte*)\"sys: trap: fault write addr\") >= 0)\n+\t\truntime·panicstring(\"invalid memory address or nil pointer dereference\");\n+\tif(runtime·strcmp((byte*)m->notesig, (byte*)\"sys: trap: divide error\") >= 0)\n+\t\truntime·panicstring(\"integer divide by zero\");\n \truntime·panicstring(m->notesig);\n \n \tif(g->sig == 1 || g->sig == 2)\n```
## コアとなるコードの解説
追加されたコードは、`runtime·sigpanic`関数内で、現在のOSスレッド(`m`)の`notesig`フィールドに格納されている文字列をチェックし、特定のPlan 9固有のトラップメッセージと一致するかどうかを判断します。
1. `if(runtime·strcmp((byte*)m->notesig, (byte*)"sys: trap: fault read addr") >= 0 || runtime·strcmp((byte*)m->notesig, (byte*)"sys: trap: fault write addr") >= 0)`
* `runtime·strcmp`は、2つのバイト文字列を比較します。
* `m->notesig`が`"sys: trap: fault read addr"`または`"sys: trap: fault write addr"`と一致する場合(`strcmp`は一致すると0を返すため、`>=0`は文字列が辞書順で等しいかそれ以降であることを意味しますが、ここでは実質的に一致を意図していると考えられます)、次の行が実行されます。
* `runtime·panicstring("invalid memory address or nil pointer dereference");`
* Goの標準的なメモリ不正アクセスまたはnilポインタデリファレンスのパニックメッセージを出力します。
2. `if(runtime·strcmp((byte*)m->notesig, (byte*)"sys: trap: divide error") >= 0)`
* `m->notesig`が`"sys: trap: divide error"`と一致する場合、次の行が実行されます。
* `runtime·panicstring("integer divide by zero");`
* Goの標準的なゼロ除算のパニックメッセージを出力します。
これらの条件に合致しない場合、元のコードパスが実行され、`runtime·panicstring(m->notesig)`によってPlan 9から受け取った生のトラップメッセージがパニック文字列として使用されます。これにより、既知の一般的なエラーについてはGoの標準メッセージに変換し、それ以外のPlan 9固有のトラップについては元のメッセージを保持するという、バランスの取れたアプローチが実現されています。
## 関連リンク
* Go言語の公式ドキュメント: [https://go.dev/](https://go.dev/)
* Goのパニックとリカバリに関する公式ブログ記事 (英語): [https://go.dev/blog/defer-panic-and-recover](https://go.dev/blog/defer-panic-and-recover)
* Plan 9 from Bell Labs: [https://9p.io/plan9/](https://9p.io/plan9/)
## 参考にした情報源リンク
* Go runtime panic strings: [https://digitalocean.com](https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQFXwgLs1IXW_mdgivswi4LulViLufMM_el6Zf_27P-tCRG8npvpaVZY-_0hxwOc7fOqMUicfpgbQMOYiW3SFi1yx6x897Qa_UXraewqPgt6CEaPLxqxt9q7Om9e4saZvsqF1o2RQ64AfRsQoba3oP-cmdMA9ZKC0lQB3FcQgFW6WLE=)
* Go and Plan 9 connection: [https://ycombinator.com](https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQGFXdwyFQEX6KE6fL9x69HlAryKpwSCc9m0GUnk-NYDFwwFS_gOsYpxhtzXJsFsjVe0X_EC--_aa-6NQ6etc6nKmia3J--3XIGlgKr99JTJtkQQ0Jw4R6f5Sl6Wnvl8WJuSTrRwyF21aw=)
* Go Gerrit Change 61410046: [https://golang.org/cl/61410046](https://golang.org/cl/61410046) (コミットメッセージに記載されているリンク)