プログラマー探偵の事件簿:type 'invalid' is not valid
偶然とはいえプログラムのエラーメッセージが哲学的なものになることがある。そしてそれは問題解決のための何のヒントにもなっていない。エラーメッセージ(ログ)からその謎を解くシリーズの第二話は、
の二つ目。助手の猫が横から「この前の続き書くの忘れてるぞ!」とアシストしてくれたことで思い出した。
事件の始まり
今回もLinuxというOSで動作するGO言語のプログラムを開発していた時
SendGuestInfo 0: xdr:encode: type 'invalid' is not valid
というエラーログが30秒毎に出力されるようになった。Googleに翻訳してもらうまでもなく、
'無効’型は、無効です。
あるいは、
’無効’型は、有効ではありません。
バカにされているのか?それとも、もっと深い意味があるのか?神(ネコ+1)のお告げか?
また、犯人は私か?
さて、何を私がやったか考えてみる。仮想マシン(パソコンの中にある仮想のパソコン)の中で動作させた時に再起動するための機能を組み込んだ後から出力されるようになった気がする。
この機能は、
のサンプルプログラム
func main() {
flag.Parse()
in := toolbox.NewBackdoorChannelIn()
out := toolbox.NewBackdoorChannelOut()
service := toolbox.NewService(in, out)
if os.Getuid() == 0 {
service.Power.Halt.Handler = toolbox.Halt
service.Power.Reboot.Handler = toolbox.Reboot
}
err := service.Start()
|
if err != nil {
log.Fatal(err)
}
service.Wait()
}
を参考にして組み込んだものだ。それ程ソースコードが長いわけではないので、どう見比べても私が間違っているようには見えない。そして、例のエラーを出力する部分も見当たらない。
Googleに聞いてみる
何と!
3件しかしない。すばらしい。世界中で3人しか遭遇していないエラーログに出会ったということだ。日本では最初の事件かもしれない。この記事を公開したら4件目になるはずだ。しかし、Googleからは何のヒントも得られないかった。困った。
教訓をいかす
前回の教訓をいかして、闇雲に自分の作っているプログラムを書き変えるのではなく、まずは、エラーを出している場所を調べることにした。幸いにも、今回のエラーはオープンソースの(公開された)GO言語パッケージ(ライブラリ)の中で発生しているようなので、どこまでもソースコードを調べることができる。ただ、そのためにはGO言語の読解力とかなりの根気が必要だ。
何とか発見した!
の
func (enc *Encoder) encode(v reflect.Value) (int, error) {
if !v.IsValid() {
msg := fmt.Sprintf("type '%s' is not valid", v.Kind().String())
err := marshalError("encode", ErrUnsupportedType, msg, nil, nil)
return 0, err
}
という部分だった。偶然、v.Kind()というものが、invalidになったので何とも意味深なエラーメッセージになったのだ。おもしろい。
真相にせまる
エラーを出している場所はわかったが、
err := service.Start()
から問題の場所まではかなり遠い。またまたプログラムの読解力と根気が必要だった。調査を続けてついに見つけた!
func GuestInfoNicInfoRequest() ([]byte, error) {
r, err := EncodeXDR(DefaultGuestNicInfo())
if err != nil {
return nil, err
}
という処理の中のEncodeXDRに渡すデータに問題があったのだ。
確証を得るため問題を単純化したプログラムを作った。
type testStruct struct {
TestIntPtr *int32
}
func main() {
var a testStruct
r, err := toolbox.EncodeXDR(a)
if err != nil {
log.Printf("%#v", err)
} else {
log.Printf("%#v", r)
}
}
同じエラーが出た。簡単に解説するとtestStructというデータをEncodeXDRという関数で処理する時、このデータの中に含まれるTestIntPtrが無効なので
type 'invalid' is not valid
ということになっていたということである。
その後、無効になっているデータを有効に変えることでこの問題を解決した。
ソフトウェアはミルフィーユ
今回の事件のエラーメッセージは、私が直接使ったパッケージではなく、そのパッケージの中で使っているパッケージと更にその中で使っているパッケージのコラボレーションで表示されたものであった。お菓子のミルフィーユのようにソフトウェアはいろんなところで何層にもなっている。自分の使っているパッケージの中から更に深い階層で使われているパッケージが繋がっている。そして、その先には、
コンピュータのOS=>ハードウェア=>電子回路=>物理現象=>神(ネコ+1)
まで繋がっている。
これを書いている時、雷鳴が轟いているのは偶然だろうか?
でも、助手の猫は私が真面目に書いているのを見て横でぐっすり寝ている。