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

[インデックス 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の内容をチェックする条件分岐が追加されています。

  1. メモリ不正アクセス: 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」に置き換えられます。

  2. ゼロ除算: 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) (コミットメッセージに記載されているリンク)