Golangのpanicのお話

2018/11/21

はじめに

Golangというと、クロスコンパイルが特徴の1つとして挙げられます。
クロスコンパイルにより、Macで書いたコードからLinuxやWindows向けの実行ファイルを簡単に生成することができます。そのため、業務で使う便利ツールを作ってチーム内に配る、といったことも容易に行えます。
今回は僕がGolangを書き始めたばかりのころ、他の環境で動かすツールを作ったときに驚いた思い出ばなしです。

サンプルコード

実際のコードとは違いますが、当時は以下のようにエラーが発生したらpanic()で処理を中断させるコードを書いていました。

で、このコードを以下のようなディレクトリ構成の中に配置していました。log_analysisが1つのプロジェクトというイメージです。

tmp
└── tanaka
    └── azunyan_prpr
        └── tools
            └── log_analysis
                └── main.go

このコードをmitchellh/goxを使ってクロスコンパイルすると、以下のように各プラットフォームに対応した実行ファイルが生成されます。

$ gox
Number of parallel builds: 3

-->   freebsd/amd64: _/tmp/tanaka/azunyan_prpr/tools/log_analysis
-->      netbsd/arm: _/tmp/tanaka/azunyan_prpr/tools/log_analysis
-->       linux/386: _/tmp/tanaka/azunyan_prpr/tools/log_analysis
-->      darwin/386: _/tmp/tanaka/azunyan_prpr/tools/log_analysis
-->   windows/amd64: _/tmp/tanaka/azunyan_prpr/tools/log_analysis
-->     openbsd/386: _/tmp/tanaka/azunyan_prpr/tools/log_analysis
-->   openbsd/amd64: _/tmp/tanaka/azunyan_prpr/tools/log_analysis
-->     windows/386: _/tmp/tanaka/azunyan_prpr/tools/log_analysis
-->      netbsd/386: _/tmp/tanaka/azunyan_prpr/tools/log_analysis
-->     freebsd/arm: _/tmp/tanaka/azunyan_prpr/tools/log_analysis
-->    darwin/amd64: _/tmp/tanaka/azunyan_prpr/tools/log_analysis
-->       linux/arm: _/tmp/tanaka/azunyan_prpr/tools/log_analysis
-->     linux/amd64: _/tmp/tanaka/azunyan_prpr/tools/log_analysis
-->     freebsd/386: _/tmp/tanaka/azunyan_prpr/tools/log_analysis
-->    netbsd/amd64: _/tmp/tanaka/azunyan_prpr/tools/log_analysis

$ ls -1
log_analysis_darwin_386
log_analysis_darwin_amd64
log_analysis_freebsd_386
log_analysis_freebsd_amd64
log_analysis_freebsd_arm
log_analysis_linux_386
log_analysis_linux_amd64
log_analysis_linux_arm
log_analysis_netbsd_386
log_analysis_netbsd_amd64
log_analysis_netbsd_arm
log_analysis_openbsd_386
log_analysis_openbsd_amd64
log_analysis_windows_386.exe
log_analysis_windows_amd64.exe
main.go

log_analysis_windows_amd64.exeがWindows(64bit)向けの実行ファイルになるので、Windows10のD:\tools配下に置いておきます。
log_analysis_windows_amd64.exeを実行すると、読み込むべきsample.logが存在しないのでpanic("ファイルないよ")が実行されます。

D:\tools>dir
 ドライブ D のボリューム ラベルがありません。
 ボリューム シリアル番号は XXXX-XXXX です

 D:\tools のディレクトリ

2018/11/22  00:04    <DIR>          .
2018/11/22  00:04    <DIR>          ..
2018/11/22  00:01         1,273,344 log_analysis_windows_amd64.exe
               1 個のファイル           1,273,344 バイト
               2 個のディレクトリ  159,870,349,312 バイトの空き領域

D:\tools>log_analysis_windows_amd64.exe
panic: ファイルないよ

goroutine 1 [running]:
main.main()
        /tmp/tanaka/azunyan_prpr/tools/log_analysis/main.go:9 +0xa1

想定通りpanicにはなりましたが、ソースファイルをコンパイルした際のディレクトリが見えるようになってしまいました…

まとめ

ツールの使用者からすれば、コンパイルした際のディレクトリ情報は不要な情報なので隠しておきたいところです。 他のプラットフォームでテストしているときに気が付いたからよかったですが、危うく適当なディレクトリ名の情報をツール使用者に晒すところでした。

当時はおそらく、「panic()使えば、エラー時のメッセージ出しつつ、プログラムも終了させれるじゃん」ぐらいの軽い気持ちで書いてたんだと思います。

panic()はメモリ不足などの「これ以上は処理が継続できない」ときに使用します。 今回のような場合はos.Exit(1)log.Fatal(err)でプログラムを終了させるべきでした。

2018/11/23 追記

のぼのぼ☂さん(@nobonobo)からご指摘をいただきました。

見慣れないオプションをお教えいただいたので、それぞれ調べてみました。
ビルド時のオプションでgcflagsを指定するとgo tool compileのオプションを指定でき、asmflagsを指定するとgo tool asmのオプションを指定できるようです。
go tool compileコマンドはソースコードを実行可能なオブジェクトコードに変換します。変換されたファイルは.o拡張子のファイルになり、go runコマンドで実行することができます。

直接使ったことがないコマンドでしたが、go runコマンドでgoファイルを実行したときに、裏では go tool compileが実行されていたようです。 「go buildでは実行ファイルを生成し、 go runでは実行ファイルを一時的に作ってとりあえず実行する」程度のふわっとした認識でした…

go tool asmコマンドについては…アセンブリ関係らしいのですがドキュメント読んでもよく理解ができなかったので引き続き確認中です。
gcflagsだけでもファイルのパスを隠すことができたので、一旦進めていきます。

trimpathで指定したパスを実行ファイルから取り除くことができます。
指定するパスによって、goファイル名まで取り除いたり、

$ go build -o log_analysis -gcflags="-trimpath=`pwd`/main.go" main.go
$ ./log_analysis
panic: ファイルないよ

goroutine 1 [running]:
main.main()
	??:9 +0x9a

goファイル名だけを残したり、

$ go build -o log_analysis -gcflags="-trimpath=`pwd`" main.go
$ ./log_analysis
panic: ファイルないよ

goroutine 1 [running]:
main.main()
	main.go:9 +0x9a

ディレクトリ名を途中まで取り除くことができます。

$ go build -o log_analysis -gcflags="-trimpath=/tmp/tanaka/azunyan_prpr" main.go
$ ./log_analysis
panic: ファイルないよ

goroutine 1 [running]:
main.main()
	tools/log_analysis/main.go:9 +0x9a

雰囲気で書いてるから、知らない便利な機能がたくさんありますね。 今後仕事で使うことになるから使えるようになっておかないとです…

※追記ここまで

その他

少年チャンピオンで連載されている、ダーウィンズゲームがアニメ化するようです。
主人公がアンリミテッドブレードワークするのがとてもカッコイイので楽しみです。まだ放送時期などは公開されていませんが、ぜひ2クールでがっつりやってほしいものです。
あと、士明さんもカッコイイ。将来はあんな感じの老人になりたい。

もう1つのオススメ作品として、週刊少年マガジンで連載中の将来的に死んでくれをご紹介します。
作品名は過激ですが、主人公(女子高生)がヒロイン(女子高生)に諭吉を渡したり、ホテルに誘ったりする百合ラブコメです。
ギャグのセンスや絵柄が好みなので、長門知大先生(@ngt9chr)の作品は読み続けていきます。 サブタイトルが「交わりを水魚のように」や「臍を噛んでる暇は無い」のようになっているのもお気にポイントの1つです。

golang

Pocketからお祝いされました -2018-

Tweetボタンでつぶやく内容を動的に変更する