[インデックス 16805] ファイルの概要
このコミットでは、Go 1.2の新しいpcln
(Program Counter to Line Number) テーブル形式に対応するために、debug/gosym
パッケージが更新されています。具体的には、以下のファイルが変更されました。
src/pkg/debug/gosym/pclinetest.asm
: テスト用のアセンブリファイル。新しいpcln
テーブル形式のテストのために調整されています。src/pkg/debug/gosym/pclntab.go
:LineTable
構造体と、プログラムカウンタと行番号のマッピングを扱うロジックが含まれる主要なファイルです。Go 1.2形式のpcln
テーブルの解析と利用に関する大幅な変更が加えられています。src/pkg/debug/gosym/pclntab_test.go
:pclntab.go
のテストファイル。Go 1.2形式のpcln
テーブルのテストケースが追加・修正されています。src/pkg/debug/gosym/symtab.go
: シンボルテーブルを扱うファイル。Table
構造体と、シンボルおよび関数情報の管理に関する変更が含まれています。特に、Go 1.2形式のpcln
テーブルとの連携が強化されています。
コミット
commit d7b4a09ca8037d91a5a89ea0e9ab36cef37cef74
Author: Russ Cox <rsc@golang.org>
Date: Thu Jul 18 10:12:14 2013 -0400
debug/gosym: update for Go 1.2 pcln table
R=golang-dev, r
CC=golang-dev
https://golang.org/cl/11495043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/d7b4a09ca8037d91a5a89ea0e9ab36cef37cef74
元コミット内容
debug/gosym: update for Go 1.2 pcln table
R=golang-dev, r
CC=golang-dev
https://golang.org/cl/11495043
変更の背景
このコミットの主な背景は、Go 1.2で導入された新しいシンボルテーブルおよびpcln
(Program Counter to Line Number) テーブルのフォーマットへの対応です。Goの以前のバージョン(Go 1.1以前)では、Plan 9ベースのシンボルテーブル形式が使用されていました。この古い形式にはいくつかの問題がありました。
- 不安定なコードの存在: シンボルデコードプロセスの一部に不安定な部分があり、信頼性に欠けることがありました。
- メモリフットプリントの増大: ランタイムのメモリ使用量が多くなる傾向がありました。
- 起動時間のオーバーヘッド: プログラム起動時にテーブルをデコードする必要があり、これが起動時間のボトルネックとなることがありました。
- ガベージコレクション (GC) 実装の複雑さ: 新しいGCメタデータを追加する際に、既存のシンボルテーブル形式が障壁となることがありました。
- メタデータフローの非効率性: コンパイラからランタイムへのメタデータの受け渡しが、リンカによる複雑な変換を必要とし、非効率でした。
これらの問題を解決するため、Go 1.2では新しいシンボルテーブル形式が導入されました。この新しい形式は、ランタイムが直接利用できるインメモリ構造として設計されており、起動時のデコードコストを排除し、ランタイムメモリを削減することを目的としています。このコミットは、debug/gosym
パッケージがこの新しいGo 1.2形式のpcln
テーブルを正しく解析し、利用できるようにするためのものです。
前提知識の解説
プログラムカウンタ (PC) と行番号 (Line Number)
- プログラムカウンタ (PC): CPUが次に実行する命令のアドレスを指すレジスタです。Goのバイナリでは、特定のPC値がソースコードの特定の命令に対応します。
- 行番号 (Line Number): ソースコード内の行の識別子です。デバッグやプロファイリングにおいて、実行中のPCがソースコードのどの行に対応するかを知ることは非常に重要です。
pcln
(Program Counter to Line Number) テーブル
pcln
テーブルは、Goの実行可能バイナリに含まれるメタデータの一つで、プログラムカウンタ (PC) とソースコードの行番号をマッピングするために使用されます。これにより、デバッガやプロファイラが実行中のコードの場所をソースコード上で特定できるようになります。
シンボルテーブル
シンボルテーブルは、プログラム内のシンボル(関数名、変数名など)とそれらがメモリ上で占めるアドレスとの対応関係を記録したデータ構造です。デバッガはシンボルテーブルを利用して、人間が読めるシンボル名と機械が理解できるメモリアドレスを相互に変換します。
Goのシンボルテーブルの進化 (Go 1.1以前 vs. Go 1.2)
-
Go 1.1以前:
- 各関数(
Func
構造体で表現される)が独自のLineTable
を持っていました。 - 行番号は、プログラム全体のすべてのソース行にわたる通し番号(絶対行番号)でした。
- この絶対行番号は、別途ファイル名とファイル内の行番号に変換する必要がありました。
- Plan 9のシンボルおよび
pc->line
テーブル形式が使用され、これらはメモリにマッピングされた後、ランタイムによって起動時にデコードされていました。このデコードプロセスが起動時間とメモリ割り当てのオーバーヘッドを引き起こしていました。 - 新しいメタデータを追加する際に、コンパイラ、リンカ、ランタイムの変更が必要となり、プロセスが複雑でした。
- 各関数(
-
Go 1.2:
- データ形式が変更され、プログラム全体で共有される単一の
LineTable
が存在するようになりました。 - 絶対行番号の概念がなくなり、特定のファイル内の行番号のみが扱われるようになりました。
- 新しい形式は、ランタイムが前処理なしで直接使用できるように設計されています。これにより、起動時のデコードコストが排除され、ランタイムメモリが削減されます。
FUNCDATA
とPCDATA
という擬似命令が導入され、コンパイラがカスタムメタデータをリンカの変更なしに直接ランタイムに渡せるようになりました。pc-value
テーブルという概念が導入され、PCからint32
値へのマッピングを一般化しました。これは行番号だけでなく、スタックフレームサイズ、GCのためのスタック変数生存情報、パニック処理のためのレジスタフラッシュ情報など、様々なメタデータに利用されます。
- データ形式が変更され、プログラム全体で共有される単一の
このコミットは、debug/gosym
パッケージがGo 1.2で導入されたこれらの変更に対応し、新しい形式のpcln
テーブルを正確に解析・利用できるようにするためのものです。
技術的詳細
このコミットの核となる変更は、src/pkg/debug/gosym/pclntab.go
とsrc/pkg/debug/gosym/symtab.go
におけるGo 1.2の新しいpcln
テーブル形式への対応です。
LineTable
構造体の変更 (src/pkg/debug/gosym/pclntab.go
)
LineTable
構造体は、Go 1.2形式のメタデータを保持するために大幅に拡張されました。
type LineTable struct {
Data []byte
PC uint64
Line int
// Go 1.2 state
mu sync.Mutex
go12 int // is this in Go 1.2 format? -1 no, 0 unknown, 1 yes
binary binary.ByteOrder
quantum uint32
ptrsize uint32
functab []byte
nfunctab uint32
filetab []byte
nfiletab uint32
fileMap map[string]uint32
}
mu
: ミューテックス。Go 1.2形式の初期化処理(go12Init
やinitFileMap
)がスレッドセーフに行われるようにします。go12
: このLineTable
がGo 1.2形式であるかどうかを示すフラグ(-1: 否、0: 未知、1: はい)。binary
: バイトオーダー(リトルエンディアンまたはビッグエンディアン)。quantum
: プログラムカウンタの量子(命令のアラインメント)。ptrsize
: ポインタのサイズ(4バイトまたは8バイト)。functab
: 関数テーブルの生データ。nfunctab
: 関数テーブル内のエントリ数。filetab
: ファイルテーブルの生データ。nfiletab
: ファイルテーブル内のエントリ数。fileMap
: ファイル名からファイル番号へのマッピングをキャッシュするためのマップ。
Go 1.2形式の初期化と検出 (go12Init
, isGo12
)
isGo12()
:LineTable
がGo 1.2形式であるかを報告します。内部でgo12Init()
を呼び出して初期化を試みます。go12Init()
:LineTable
のData
フィールドを解析し、Go 1.2形式のヘッダ(マジックナンバー0xfffffffb
、PC量子、ポインタサイズなど)をチェックします。解析に失敗した場合(例: パニックが発生した場合)、Go 1.2形式ではないと判断します。成功した場合、binary
、quantum
、ptrsize
、functab
、nfunctab
、filetab
、nfiletab
を初期化します。
pc-value
テーブルの解析
Go 1.2のpcln
テーブルは、pc-value
テーブルという汎用的な形式でエンコードされています。これは、PCとそれに対応する値(行番号、ファイル番号など)のペアを効率的に表現するためのものです。
uintptr(b []byte) uint64
: 指定されたバイトスライスから、テーブルのポインタサイズに応じたuintptr
値を読み取ります。readvarint(pp *[]byte) uint32
: 可変長整数(varint)を読み取ります。pc-value
テーブルのデルタ値のエンコーディングに使用されます。step(p *[]byte, pc *uint64, val *int32, first bool) bool
: エンコードされたpc-value
テーブルから次のPCと値のペアに進みます。PCデルタと値デルタを読み取り、現在のPCと値を更新します。pcvalue(off uint32, entry, targetpc uint64) int32
: 指定されたオフセットから始まるpc-value
テーブルを走査し、targetpc
に対応する値を返します。
関数情報の検索 (findFunc
)
findFunc(pc uint64) []byte
: 指定されたプログラムカウンタpc
に対応する関数情報(Func
構造体の生データ)を検索します。関数テーブル(functab
)はPC値でソートされており、二分探索によって効率的に検索されます。
PCと行番号/ファイル名のマッピング (Go 1.2形式)
go12PCToLine(pc uint64) (line int)
: Go 1.2形式のpcln
テーブルを使用して、プログラムカウンタpc
に対応する行番号を返します。findFunc
で関数情報を見つけ、その中のpcln
テーブルのオフセットを使ってpcvalue
を呼び出します。go12PCToFile(pc uint64) (file string)
: Go 1.2形式のpcln
テーブルを使用して、プログラムカウンタpc
に対応するファイル名を返します。findFunc
で関数情報を見つけ、その中のpcfile
テーブルのオフセットを使ってpcvalue
を呼び出し、ファイル番号からファイル名を解決します。go12LineToPC(file string, line int) (pc uint64)
: Go 1.2形式のpcln
テーブルを使用して、指定されたファイルと行番号に対応するプログラムカウンタを返します。すべての関数を走査し、findFileLine
を呼び出して一致するPCを探します。findFileLine(entry uint64, filetab, linetab uint32, filenum, line int32) uint64
: 特定の関数内で、指定されたファイル番号と行番号に対応するプログラムカウンタを検索します。ファイルテーブルと行テーブルを同時に走査し、両方が一致するPCを見つけます。
ファイル名マップの初期化 (initFileMap
, go12MapFiles
)
initFileMap()
: ファイルテーブル(filetab
)を解析し、ファイル名からファイル番号へのマッピング(fileMap
)を初期化します。go12MapFiles(m map[string]*Obj, obj *Obj)
: Go 1.2形式のLineTable
内のすべてのファイル名をマップm
に追加します。
Table
構造体の変更 (src/pkg/debug/gosym/symtab.go
)
Table
構造体は、Go 1.2形式のLineTable
への参照を持つようになりました。
type Table struct {
Syms []Sym
Funcs []Func
Files map[string]*Obj // nil for Go 1.2 and later binaries
Objs []Obj // nil for Go 1.2 and later binaries
go12line *LineTable // Go 1.2 line number table
}
go12line
: Go 1.2形式のLineTable
へのポインタ。Go 1.2以降のバイナリでは、このフィールドが使用され、Files
とObjs
はnil
になります。
NewTable
関数の変更 (src/pkg/debug/gosym/symtab.go
)
NewTable
関数は、渡されたpcln
がGo 1.2形式であるかをチェックし、もしそうであればTable.go12line
を設定します。
func NewTable(symtab []byte, pcln *LineTable) (*Table, error) {
// ...
var t Table
if pcln.isGo12() {
t.go12line = pcln
}
// ...
}
また、Go 1.2形式のバイナリでは、すべての関数が単一のObj
にまとめられ、ファイル情報はgo12line
を通じて管理されるため、従来のObj
のPaths
フィールドやTable.Files
、Table.Objs
の扱いが変更されています。
PCToLine
とLineToPC
の変更 (src/pkg/debug/gosym/symtab.go
)
Table
のPCToLine
とLineToPC
メソッドは、Go 1.2形式のpcln
テーブルが存在する場合はそちらを利用するように変更されました。
PCToLine(pc uint64) (file string, line int, fn *Func)
:t.go12line != nil
の場合、t.go12line.go12PCToFile(pc)
とt.go12line.go12PCToLine(pc)
を呼び出してファイル名と行番号を取得します。- それ以外の場合、従来の
fn.Obj.lineFromAline(fn.LineTable.PCToLine(pc))
を使用します。
LineToPC(file string, line int) (pc uint64, fn *Func, err error)
:t.go12line != nil
の場合、t.go12line.go12LineToPC(file, line)
を呼び出してPCを取得します。- それ以外の場合、従来の
obj.alineFromLine(file, line)
を使用します。
これらの変更により、debug/gosym
パッケージはGo 1.2以降のバイナリのデバッグ情報も正確に解析できるようになりました。
コアとなるコードの変更箇所
src/pkg/debug/gosym/pclntab.go
LineTable
構造体にGo 1.2形式のメタデータ(mu
,go12
,binary
,quantum
,ptrsize
,functab
,nfunctab
,filetab
,nfiletab
,fileMap
)が追加されました。isGo12()
、go12Init()
、uintptr()
、readvarint()
、step()
、pcvalue()
、findFunc()
、string()
などのGo 1.2形式のpcln
テーブルを解析するためのヘルパー関数が追加されました。go12PCToLine()
、go12PCToFile()
、go12LineToPC()
、findFileLine()
、initFileMap()
、go12MapFiles()
など、Go 1.2形式のPC-行番号/ファイル名マッピングを行う関数が追加されました。- 既存の
PCToLine()
とLineToPC()
メソッドが、Go 1.2形式のLineTable
が存在する場合は新しいロジックを呼び出すように変更されました。 quantum
定数がoldQuantum
にリネームされ、Go 1.2形式ではLineTable.quantum
が使用されるようになりました。
src/pkg/debug/gosym/symtab.go
Obj
構造体のコメントが更新され、Go 1.2以降では単一のObj
がプログラム全体を表すことが明記されました。Table
構造体にgo12line *LineTable
フィールドが追加されました。NewTable()
関数内で、pcln.isGo12()
が真の場合にt.go12line
が設定されるようになりました。また、Go 1.2形式の場合、t.Objs
は単一のObj
を保持し、t.Files
はgo12line.go12MapFiles
によって初期化されるようになりました。PCToLine()
とLineToPC()
メソッドが、t.go12line
が存在する場合はGo 1.2形式のロジックを呼び出すように変更されました。Obj.lineFromAline()
のコメントが更新され、Go 1.1以前のレガシーコードであることが明記されました。
src/pkg/debug/gosym/pclinetest.asm
- テスト用のPC-行番号データに
BYTE $255;
が追加されました。これは、pcfromline
関数とmain
関数の終端を示すマーカーとして使用され、テストロジックの調整に役立っています。
src/pkg/debug/gosym/pclntab_test.go
dotest()
関数がGo 1.2形式のテストに対応するために修正されました。特に、go tool 6l
コマンドに-H linux
オプションが追加され、Linux環境でのテストバイナリ生成が明示されました。TestLineFromAline()
とTestLineAline()
テストが、Go 1.2形式のテーブルではスキップされるようになりました。これは、aline
の概念がGo 1.2形式には存在しないためです。TestPCLine()
テストが、Go 1.2形式のpcln
テーブルの変更に対応するように調整されました。特に、textdat[off] == 255
によるブレーク条件が追加されています。
コアとなるコードの解説
LineTable
構造体 (src/pkg/debug/gosym/pclntab.go
)
Go 1.2のpcln
テーブルは、以前のバージョンとは異なり、プログラム全体で共有される単一のテーブルとして設計されています。このため、LineTable
構造体は、そのテーブルの全体的なメタデータと、テーブル内の各セクション(関数テーブル、ファイルテーブルなど)へのポインタを保持するように拡張されました。
Data
:pcln
テーブル全体の生バイトデータ。このデータから様々な情報を解析します。functab
,nfunctab
:pcln
テーブル内の関数エントリのリストとその数。各エントリはPCと、対応するFunc
構造体へのオフセットを含みます。filetab
,nfiletab
:pcln
テーブル内のファイル名エントリのリストとその数。各エントリはファイル名文字列へのオフセットを含みます。binary
,quantum
,ptrsize
: バイナリのバイトオーダー、PCの量子(命令のアラインメント)、ポインタサイズといった、テーブルの解析に必要なアーキテクチャ固有の情報を保持します。
go12Init()
(src/pkg/debug/gosym/pclntab.go
)
この関数は、LineTable
がGo 1.2形式であるかを判断し、そのメタデータを初期化する重要な役割を担います。マジックナンバーのチェックから始まり、関数テーブルやファイルテーブルのオフセットとサイズを特定します。この初期化は一度だけ行われ、その結果はgo12
フィールドにキャッシュされます。
findFunc(pc uint64) []byte
(src/pkg/debug/gosym/pclntab.go
)
Go 1.2のpcln
テーブルでは、すべての関数情報が単一の関数テーブルに格納されています。この関数は、与えられたプログラムカウンタpc
がどの関数に属するかを効率的に見つけ出すために、関数テーブルに対して二分探索を実行します。これにより、PCから対応するFunc
構造体の生データへの高速なマッピングが可能になります。
pcvalue(off uint32, entry, targetpc uint64) int32
(src/pkg/debug/gosym/pclntab.go
)
この関数は、Go 1.2で導入されたpc-value
テーブルの汎用的なデコーダです。pc-value
テーブルは、PCの範囲とそれに対応する値(行番号、ファイル番号など)を効率的にエンコードします。この関数は、指定されたオフセットからテーブルを読み込み、targetpc
に対応する値を返します。これは、行番号やファイル番号の取得の基盤となります。
go12PCToLine(pc uint64) (line int)
と go12PCToFile(pc uint64) (file string)
(src/pkg/debug/gosym/pclntab.go
)
これらの関数は、Go 1.2形式のpcln
テーブルからPCに対応する行番号とファイル名を取得するための主要なAPIです。内部的にはfindFunc
とpcvalue
を組み合わせて使用し、効率的に情報を抽出します。
go12LineToPC(file string, line int) (pc uint64)
(src/pkg/debug/gosym/pclntab.go
)
この関数は、Go 1.2形式のpcln
テーブルからファイル名と行番号に対応するプログラムカウンタを取得します。これは、デバッガがソースコードの特定の行にブレークポイントを設定する際などに利用されます。すべての関数を走査し、findFileLine
を呼び出して一致するPCを見つけます。
Table
構造体とgo12line
フィールド (src/pkg/debug/gosym/symtab.go
)
Table
構造体にgo12line *LineTable
フィールドが追加されたことで、debug/gosym
パッケージはGo 1.2以降のバイナリのデバッグ情報を透過的に扱えるようになりました。NewTable
関数でこのフィールドが適切に設定されることで、PCToLine
やLineToPC
のような高レベルのAPIが、内部的にGo 1.2形式のLineTable
のメソッドを呼び出すか、従来のロジックを呼び出すかを自動的に切り替えることができます。これにより、ユーザーはバイナリのバージョンを意識することなく、一貫したインターフェースでデバッグ情報にアクセスできるようになります。
関連リンク
- Go CL 11495043: https://golang.org/cl/11495043
- Go 1.2 Symbol Table Format: https://golang.org/s/go12symtab
参考にした情報源リンク
- Go 1.2 Symbol Table Format: https://golang.org/s/go12symtab