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

[インデックス 19317] ファイルの概要

このコミットは、Go言語のツールチェインに含まれる cmd/addr2line コマンドの挙動を改善するものです。具体的には、addr2line が標準入力から受け取るアドレス文字列に、オプションで 0x プレフィックスが付与されていても正しく処理できるように変更されました。これにより、ユーザーの利便性が向上し、より柔軟な入力形式に対応できるようになります。

コミット

  • コミットハッシュ: 8dfd5184abb7d363741cb1a71045a5bea3cd4271
  • 作者: Shenghou Ma minux.ma@gmail.com
  • コミット日時: 2014年5月10日 土曜日 13:35:40 -0400

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/8dfd5184abb7d363741cb1a71045a5bea3cd4271

元コミット内容

cmd/addr2line: accept optional "0x" prefix for addresses.

LGTM=rsc
R=rsc
CC=golang-codereviews
https://golang.org/cl/91250043

変更の背景

go tool addr2line は、Goプログラムのデバッグやプロファイリングにおいて非常に重要なツールです。これは、実行バイナリ内のメモリアドレスを、対応するソースコードのファイル名と行番号に変換する役割を担います。例えば、クラッシュレポートやプロファイリングツール(pprofなど)が出力するスタックトレースには、しばしばメモリアドレスが含まれています。これらのアドレスを人間が理解できるソースコード上の位置にマッピングすることで、問題の根本原因を特定したり、パフォーマンスのボトルネックを分析したりすることが可能になります。

このコミット以前の addr2line は、アドレスを16進数文字列として受け取る際に、0x プレフィックスが付いていないことを前提としていました。しかし、多くのデバッグツールやシステムが出力する16進数アドレスには、慣習的に 0x プレフィックスが付与されています。この不一致は、ユーザーが addr2line を利用する際に、手動で 0x プレフィックスを削除する必要があるという不便さを生んでいました。

この変更は、このような手動での前処理の必要性をなくし、addr2line がより多様な入力形式に対応できるようにすることで、ユーザーエクスペリエンスを向上させることを目的としています。これにより、他のツールからの出力を直接 addr2line にパイプするなど、ワークフローがよりスムーズになります。

前提知識の解説

このコミットの理解には、以下の知識が役立ちます。

  • go tool addr2line: Go言語のツールチェインに含まれるコマンドラインユーティリティです。コンパイルされたGoバイナリのデバッグ情報(シンボルテーブル)を利用して、特定のメモリアドレスがどのソースコードのファイル、行、関数に対応するかを調べます。主に、クラッシュ時のスタックトレース解析や、pprof などのプロファイリングツールと連携して使用されます。標準入力から16進数アドレスを受け取り、標準出力に結果を出力します。

  • 16進数アドレスと 0x プレフィックス: コンピュータのメモリアドレスは、通常16進数(hexadecimal)で表現されます。16進数は、0から9までの数字とAからFまでのアルファベット(またはaからf)を使って数を表現する方法です。プログラミングやデバッグの文脈では、16進数であることを明示するために、数値の前に 0x というプレフィックスを付けることが一般的です(例: 0x1a2b3c)。

  • Go言語の strconv パッケージ: strconv パッケージは、Go言語で文字列と基本的なデータ型(数値、真偽値など)の間で変換を行うための機能を提供します。

    • strconv.ParseUint(s string, base int, bitSize int) (uint64, error): この関数は、指定された基数(base)で表現された文字列 suint64 型の符号なし整数にパース(解析)します。base には2(2進数)、8(8進数)、10(10進数)、16(16進数)などを指定できます。bitSize は結果の整数が占めるビット数を指定します。
  • Go言語の strings パッケージ: strings パッケージは、Go言語で文字列操作を行うためのユーティリティ関数を提供します。

    • strings.TrimPrefix(s, prefix string) string: この関数は、文字列 sprefix で始まる場合、その prefix を取り除いた新しい文字列を返します。sprefix で始まらない場合は、元の文字列 s をそのまま返します。この関数は、文字列の先頭から特定のプレフィックスを安全に削除するために使用されます。

技術的詳細

このコミットの技術的な核心は、addr2line がアドレス文字列を数値に変換する前に、0x プレフィックスの有無を透過的に処理する点にあります。

変更前は、strconv.ParseUint(p, 16, 64) が直接呼び出されていました。strconv.ParseUint は、基数16を指定した場合、0x プレフィックスを自動的に解釈する機能を持っていません。そのため、入力文字列 p0x が含まれていると、パースエラーが発生するか、誤った値が返される可能性がありました。

変更後は、strconv.ParseUint(strings.TrimPrefix(p, "0x"), 16, 64) となっています。ここで strings.TrimPrefix(p, "0x") が重要な役割を果たします。この関数は、入力されたアドレス文字列 p の先頭に 0x が存在すればそれを取り除き、存在しなければ元の文字列をそのまま返します。これにより、strconv.ParseUint に渡される文字列は常に 0x プレフィックスを含まない純粋な16進数表現となり、strconv.ParseUint は常に正しくアドレスをパースできるようになります。

この変更は、addr2line の入力処理の堅牢性を高め、ユーザーが提供するアドレス文字列の形式に対する許容度を広げます。結果として、addr2line はより使いやすく、他のツールとの連携も容易になります。

コアとなるコードの変更箇所

変更は src/cmd/addr2line/main.go ファイルの1箇所のみです。

--- a/src/cmd/addr2line/main.go
+++ b/src/cmd/addr2line/main.go
@@ -8,7 +8,7 @@
 // Usage:
 //	go tool addr2line binary
 //
-// Addr2line reads hexadecimal addresses, one per line and without a 0x prefix,
+// Addr2line reads hexadecimal addresses, one per line and with optional 0x prefix,
 // from standard input. For each input address, addr2line prints two output lines,
 // first the name of the function containing the address and second the file:line
 // of the source code corresponding to that address.
@@ -88,7 +88,7 @@ func main() {
 			fmt.Fprintf(stdout, "!reverse translation not implemented\\n")
 			continue
 		}
-		pc, _ := strconv.ParseUint(p, 16, 64)
+		pc, _ := strconv.ParseUint(strings.TrimPrefix(p, "0x"), 16, 64)
 		file, line, fn := tab.PCToLine(pc)
 		name := "?"
 		if fn != nil {

コアとなるコードの解説

変更された行は以下の通りです。

変更前:

pc, _ := strconv.ParseUint(p, 16, 64)

この行では、標準入力から読み込んだアドレス文字列 p を、直接 strconv.ParseUint 関数を使って16進数(基数16)として uint64 型の数値 pc に変換しようとしていました。この方法では、p0x プレフィックスを含んでいると、パースに失敗するか、予期せぬ結果を招く可能性がありました。

変更後:

pc, _ := strconv.ParseUint(strings.TrimPrefix(p, "0x"), 16, 64)

この行では、strconv.ParseUint を呼び出す前に、strings.TrimPrefix(p, "0x") を介してアドレス文字列 p を前処理しています。

  • strings.TrimPrefix(p, "0x"): この関数は、文字列 p の先頭が "0x" であれば、その "0x" を取り除いた新しい文字列を返します。もし "0x" で始まらない場合は、p そのものを返します。
  • この前処理により、strconv.ParseUint に渡される文字列は、常に 0x プレフィックスを含まない純粋な16進数表現となります。これにより、strconv.ParseUint は常に意図通りにアドレスを数値に変換できるようになり、0x プレフィックスの有無に関わらず、addr2line が正しく動作するようになりました。

また、コードコメントも変更され、Addr2line reads hexadecimal addresses, one per line and without a 0x prefix, から Addr2line reads hexadecimal addresses, one per line and with optional 0x prefix, に修正され、新しい挙動が反映されています。

関連リンク

  • Go言語の strconv パッケージドキュメント: https://pkg.go.dev/strconv
  • Go言語の strings パッケージドキュメント: https://pkg.go.dev/strings
  • Go言語の go tool addr2line に関する公式ドキュメント(go help addr2line で確認可能)

参考にした情報源リンク