go go golang
とある日、何時もの日課で散歩してたら、ガソリンスタンドからカートを引いたおじいさんが出て来た。そのカートには、石油のポリタンクが2個搭載された。その日は雪が積もっていなかったので、さほど難儀そうには見えなかったけど、積雪が有って凍結してたら、えらい苦行をするだろう事は容易に想像出来る。
オイラーがこの地に帰郷する時、冬の暖房をどうしようか大変悩んだ事を思い出した。普通に考えれば、石油ストーブかランクアップして石油ファンヒーターが妥当な所だろう。ただこいつを 選んでしまうと、石油供給問題が発生する。車を運転出来る時は、面倒だけど3本とか4本を まとめて仕入れてくればよい。でもよれよれになって車を放棄したら、上のおじいさん状態になる事は必至。ラーメンの出前のごとく、配達してもらえばどうよ。配達料は馬鹿にならないだろうね。
実家のように石油をバレル(樽)買い、もとえドラム缶買いって手もあるだろうけど、借家住まいでは、そんなものを設置出来ない。仮に出来たとしても、ストーブのタンクへ小まめに給油する(させられる)必要がある。よって、石油暖房は放棄。
そうなると、白熊君か霧ヶ峰(避暑地の代名詞、軽井沢ブランドが無いのが不思議)を検討する 事になる。少々暖房能力とランニングコストに不安を抱いたけど、清水の舞台をダイブする事にした。この選択が大正解であった。
大蔵省の発表によると、関東エリアで石油ストーブを使ってた頃よりも、むしろ暖房代は安いとの事。暖かさにも不満は無し。そして手間要らず。こんなクーラー兼暖房機はどういう原理で 動いているの? 暇に任せて調べてみた。
とことんやさしい『ヒートポンプの本』なんてのを読んだ。
原理は、気体は圧力がかかると温度が上がり、圧力をゆるめると温度が下がるというボイル・シャルルの法則。熱は温度の高い方から低い方に流れるという熱力学第二法則。この2法則を利用したものだ。
チャリンコの空気入れを一生懸命に動かしていると、空気入れの所が熱くなる。あるいは、スプレーを連続放射すると、缶が冷えるでおなじみ。お茶が冷めるのも当たり前で、経験済よー。
冷媒と呼ばれる熱を伝える気体(昔はフロンだったけど、エコキュートとかだと二酸化炭素等) を圧縮(これで熱が発生するんで、暖房に利用したり水を温めたり)、圧縮された冷媒の圧力を 下げる(冷却されるんで、空冷に利用)の繰り返し。ここにモーターが使われる。
モーターを動かす電力(を熱換算)と、得られる暖(冷)熱との割合が、今では6(COPと言う専門用語が使われる)ぐらいになるという。じゃ、その熱は何処から来る?
空気中に存在する熱を使うんだ。えっ? 0度の寒い空気にそんなエネルギーなんてあるの? 勿論、273度の熱源ですよ。(絶対温度表現ね)
だから、上のようにして、空気中の熱を集めるポンプが出来るのさ。
暖房と冷房の切り替えは、室外機と室内機を四方弁というのを使って、論理的に入れ替えるそうだ。そんな事知らんかったわい。件のおじいさんの姿を見て、こんな事を知るとは、なかなか 面白いな。
go go golang
暇に任せて、 acmeでGoプログラミング なんてのを読んでいたんだ。p9p環境から使うのがお勧めらしい。goのお父さんはgoogleに行ってから、Plan9を使うのを止め、もっぱらp9pを使っているそうな。
ならば、オイラーもp9pからgoを操るというお父さんの真似をしてみるかな。ええ、勿論、他の 開発環境を使ってもいいですけどね。
ああ、去年のアベンドカレンダーでも、haskellのエントリーに続いて2位をキープしてますんで、 そこそこの人気が伺えますです。
goで昔、実用になるアプリを作ったけど、すっかり忘れてしまっているので、ちょいと THE GO TOOL とか go book なんてのを見て復習。
go tool
今回は、
$ pwd /usr/local/go/pkg/tool/openbsd_amd64 $ ls addr2line* cgo* dist* link* pack* vet* api* compile* doc* nm* pprof* asm* cover* fix* objdump* trace*
こんな所に隠れているツールに焦点をあてて見ていくかな。正式には、
$ go tool addr2line api asm cgo compile cover dist doc fix link nm objdump pack pprof trace vet
こんな風に、接頭語をかませて、各道具を呼び出すみたい。cgoは、goのC語拡張らしいので、 引数が山ほどあるgccとおさらば出来るのかな。(取り合えず置いておこう) 例題として、例のやつを取り上げて、コンパイル、実行、plan9には無かったobjdumpも父が 反省して付けてくれてるようなので、試してみる。
$ cat main.go package main import "fmt" func main() { fmt.Println("hello world") }
compileしてアセンブラを出し、それをアセンブルしてからlinkかな。
$ go tool compile main.go $ ls main.go main.o $ go tool link main.o $ ls a.out* main.go main.o $ ./a.out hello world
コンパイルした結果がオブジェクトファイルになるんか。そうすると、asmの出番は通常は無いな。特殊な事をやりたい場合、アセンブラ語を書いて使うとな。
きっとコンパイルする時にアセンブラー出力も得られるのかな?こういう時はヘルプだな。
$ go tool compile -h usage: compile [options] file.go... -S print assembly listing
大サービスで、見せてあげるってフラグが用意されてるな。試してみる。
$ go tool compile -S main.go "".main STEXT size=120 args=0x0 locals=0x48 0x0000 00000 (main.go:5) TEXT "".main(SB), $72-0 0x0000 00000 (main.go:5) MOVQ (TLS), CX 0x0009 00009 (main.go:5) CMPQ SP, 16(CX) : go.string."hello world" SRODATA dupok size=11 0x0000 68 65 6c 6c 6f 20 77 6f 72 6c 64 hello world :
これ、Plan9風のアセンブラー形式だね。父はATTからgoogleに移る時、ごっそりと前職の 遺産を持ってきたんだな。で、mainの前に空の文字列が有るけど、きっとパッケージ名が 入るんだろうね。
そんな時は、
$ go tool nm a.out : 559563 D io.initdone. 450df0 T main 492200 T main.init 559564 D main.initdone. 492180 T main.main 4d4510 R main.statictmp_0 :
この他に、fmt関連やOS呼び出しとか、色々なランタイムコードが合体されてたぞ。だから コードが肥大化するんだ。(いちいち、こまけいなあ。)次は、オブジェクトダンプだな。
$ go tool objdump a.out TEXT sync/atomic.(*Value).Store(SB) /usr/local/go/src/sync/atomic/value.go value.go:47 0x401000 64488b0c25f8ffffff MOVQ FS:0xfffffff8, CX value.go:47 0x401009 483b6110 CMPQ 0x10(CX), SP value.go:47 0x40100d 0f861a010000 JBE 0x40112d :
ほー、リンクしてる物を余す所なく表示してくれるとな。
$ go tool objdump -S -s main.main a.out TEXT main.main(SB) /tmp/t/main.go func main() { 0x492180 64488b0c25f8ffffff MOVQ FS:0xfffffff8, CX 0x492189 483b6110 CMPQ 0x10(CX), SP 0x49218d 7662 JBE 0x4921f1 0x49218f 4883ec48 SUBQ $0x48, SP 0x492193 48896c2440 MOVQ BP, 0x40(SP) 0x492198 488d6c2440 LEAQ 0x40(SP), BP fmt.Println("hello world") 0x49219d 48c744243000000000 MOVQ $0x0, 0x30(SP) 0x4921a6 48c744243800000000 MOVQ $0x0, 0x38(SP) 0x4921af 488d052a0c0100 LEAQ 0x10c2a(IP), AX 0x4921b6 4889442430 MOVQ AX, 0x30(SP) 0x4921bb 488d054e230400 LEAQ 0x4234e(IP), AX 0x4921c2 4889442438 MOVQ AX, 0x38(SP) 0x4921c7 488d442430 LEAQ 0x30(SP), AX 0x4921cc 48890424 MOVQ AX, 0(SP) 0x4921d0 48c744240801000000 MOVQ $0x1, 0x8(SP) 0x4921d9 48c744241001000000 MOVQ $0x1, 0x10(SP) 0x4921e2 e8898fffff CALL fmt.Println(SB) } 0x4921e7 488b6c2440 MOVQ 0x40(SP), BP 0x4921ec 4883c448 ADDQ $0x48, SP 0x4921f0 c3 RET func main() { 0x4921f1 e87ab8fbff CALL runtime.morestack_noctxt(SB) 0x4921f6 eb88 JMP main.main(SB)
そして、Sオプションを付けると、ソースと共に。sオプションで見たい所を絞って表示してくれてるよ。アセンブラーの出力形式は、命令 src、dest って、ATT式表示なのね。
golang系の融和政策
ふと、上のobjdumpを実行してて、これって既存OSにも有るじゃんと思いついた。nmもそうだね。OpenBSDに付属のobjdumpにかけてみる。
$ objdump -d a.out : 0000000000492180 <main.main>: 492180: 64 48 8b 0c 25 f8 ff mov %fs:0xfffffffffffffff8,%rcx 492187: ff ff 492189: 48 3b 61 10 cmp 0x10(%rcx),%rsp 49218d: 76 62 jbe 4921f1 <main.main+0x71> 49218f: 48 83 ec 48 sub $0x48,%rsp 492193: 48 89 6c 24 40 mov %rbp,0x40(%rsp) 492198: 48 8d 6c 24 40 lea 0x40(%rsp),%rbp 49219d: 48 c7 44 24 30 00 00 movq $0x0,0x30(%rsp) 4921a4: 00 00 4921a6: 48 c7 44 24 38 00 00 movq $0x0,0x38(%rsp) 4921ad: 00 00 4921af: 48 8d 05 2a 0c 01 00 lea 68650(%rip),%rax # 4a2de0<type.*+0xfde0> 4921b6: 48 89 44 24 30 mov %rax,0x30(%rsp) 4921bb: 48 8d 05 4e 23 04 00 lea 271182(%rip),%rax # 4d4510 <main.statictmp_0> 4921c2: 48 89 44 24 38 mov %rax,0x38(%rsp) 4921c7: 48 8d 44 24 30 lea 0x30(%rsp),%rax 4921cc: 48 89 04 24 mov %rax,(%rsp) 4921d0: 48 c7 44 24 08 01 00 movq $0x1,0x8(%rsp) 4921d7: 00 00 4921d9: 48 c7 44 24 10 01 00 movq $0x1,0x10(%rsp) 4921e0: 00 00 4921e2: e8 89 8f ff ff callq 48b170 <fmt.Println> 4921e7: 48 8b 6c 24 40 mov 0x40(%rsp),%rbp 4921ec: 48 83 c4 48 add $0x48,%rsp 4921f0: c3 retq 4921f1: e8 7a b8 fb ff callq 44da70 <runtime.morestack_noctxt> 4921f6: eb 88 jmp 492180 <main.main> 4921f8: cc int3 4921f9: cc int3 4921fa: cc int3
普通のOSの道具でも動いた。golangも既存OSにすりよっているな。
それはそうと、int3って、近頃お目にかかった記憶があるぞ。えーとね、 ブレークポイントの実装にあったな。
golangの父にしてPlan9の生みの親なら、acidを実装するのも容易だろうに。来るgolang2.0のtoolには、プログラマブルdebugger、acidが実装されるに違いない。(今までのリリースサイクルだと、来月の2月にお目見えの予定)
で、ふと、現状でも既存OS上のgdbが使えると予測。だって、a.outをreadelfしたら、debug情報がごっそり埋め込まれていたし、上記のように、道を踏み外しても、debuggerに落ちるように、int3が埋め込まれているからね。これって、伊達や酔狂で入れてるわけじゃないでしょ。どう解釈しても、防衛的プログラミングだな。
昔々、本当のコアメモリーが搭載されているミニコンに触った事が有る。そいつのdisk-operating-systemを停止させると、全メモリー領域がHALT命令で埋め尽くされていたな。悪戯しても、暴走しないような知恵と感心したよ。
(gdb) b main Breakpoint 1 at 0x450df0: file /usr/local/go/src/runtime/rt0_openbsd_amd64.s, line 14. (gdb) r Starting program: /tmp/t/a.out Breakpoint 1, main () at /usr/local/go/src/runtime/rt0_openbsd_amd64.s:14 14 MOVQ $runtime\302\267rt0_go(SB), AX
いきなりアセンブラーのコードが出てきた。そして、そのコードの中に、\302\267 なんていう不穏な文字が入ってる。中点を表すユニコードだな。Plan9でユニコードが発明されたらしいけど、さりげなく使われているな。
ステップ実行してもさっぱり抜けないので、
(gdb) b main.main Breakpoint 2 at 0x492180: file /tmp/t/main.go, line 5. (gdb) c Continuing. [New thread 478749] [New thread 186903] [New thread 501403] Thread 1 hit Breakpoint 2, main.main () at /tmp/t/main.go:5 warning: Source file is more recent than executable. 5 func main() {
スレッドが動くってGCのゴミ掃除人かしらね?
(gdb) n 6 fmt.Println("hello world") (gdb) s fmt.Println (a= []interface {} = {...}, n=29, err=...) at /usr/local/go/src/fmt/print.go:256 256 func Println(a ...interface{}) (n int, err error) { (gdb) n 257 return Fprintln(os.Stdout, a...) (gdb) n 256 func Println(a ...interface{}) (n int, err error) {
普通に動くじゃん。取り合えずこれを使っていてねって事だな。
vet
vetって、検査って意味なんだ。そうすると、C語のツールであるlintみたいなものかな。 ちゃんとコンパイルして実行出来るけど、重箱の隅をつつくような文句を垂れてくれる、小うるさい姑みたいなものか。違うって、先祖代々のしきたりをしっかり躾してくれるやつなんだろうね。物は試し、2014年の暮れごろに書いたコードを俎上に載せてみるか。
$ go tool fix bld.go $ go tool vet bld.go
古いソースなので、今の書式に合うようにfix(修復)をかます。何も文句を垂れないので、 書式は現代風になってると思われ。次に検査だな。何も文句を言われなかった。成績優秀な コードを書いていたって事だろうね。
ぐぐって、どんなやつがネチネチされるか見ておく。
Goのソースコード解析に標準ツールのgo vetを使ってみましょう
初心者が本山からコピペしてくるような段階だと、ひっかからないのか。守破離の守の段階だものね。破まで行くと、役に立つのかな。
それはそうと、go 1.9.0でコンパイルしてみるとplotのパッケージが見つからないと言われる。場所が移ったのかな?
どうも、gonum/plotこれっぽい。
$ go get gonum.org/v1/plot/... go: missing Mercurial command. See https://golang.org/s/gogetcmd package gonum.org/v1/plot imports bitbucket.org/zombiezen/gopdf/pdf: exec: "hg": executable file not found in $PATH $ go get gonum.org/v1/plot/...
hgって、Plan9様の御用達だったな。mercurialを入れておけばいいのか。
$ go build # _/tmp/u ./bld.go:8:2: imported and not used: "gonum.org/v1/plot/vg" ./bld.go:320:8: cannot use width (type float64) as type vg.Length in argument to p.Save ./bld.go:320:8: cannot use height (type float64) as type vg.Length in argument to p.Save
さあ、plan9port上のacmeの出番だぞ。
width := 6*vg.Inch height := 4*vg.Inch p.Save(width, height, "zzz.png")
こんな風に単位を付けたらOKになった。やれやれ。
trace
traceなんてのが有るなあ。lisp/schemeのあれかな? どうやって使うの? 探りを入れてみる。
これがサンプルプログラム。コードを埋め込むとな。
$ cat main.go package main import ( "fmt" // test "os" "runtime/trace" ) func main() { f, err := os.Create("trace.out") if err != nil { panic(err) } defer f.Close() err = trace.Start(f) if err != nil { panic(err) } // test start g, err := os.Create("test.txt") if err != nil { panic(err) } defer g.Close() for i := 0; i < 100; i++ { fmt.Fprintf(g, "line number: %d\n", i) } // test end defer trace.Stop() }
どうやって使うか検索。
$ go tool trace -h Open a web browser displaying trace: go tool trace [flags] [pkg.test] trace.out : Supported profile types are: - net: network blocking profile - sync: synchronization blocking profile - syscall: syscall blocking profile - sched: scheduler latency profile Flags: -http=addr: HTTP service address (e.g., ':6060') -pprof=type: print a pprof-like profile instead
使い方が分かったので、早速実践。
$ go build $ ./u $ go tool trace -http=:8080 syscall trace.out
/tmp/u/main.go をコンパイルすると、dir名のファイルが出来るんか。知らんかったわい。 次に、出来上がったトレースファイルを指定するとな。結果はWebでってのは、なかなか現代風だな。 トレースって、lispのやつみたいに実行順に命令が出てくるかと思ったら、違う角度からの トレースなのね。
pprof
traceだけでは解決出来ないような場合、プロファイリングせいとな。そこまでオイラーはシビアなプログラムを組まないので、参考までに見ておく。
Go で利用できるプロファイリングツール pprof の読み方
Go言語のプロファイリングツール、pprofのWeb UIがめちゃくちゃ便利
Go pprof 入門編 (CPU Profile とコマンドラインツール)
実用風には?
goのCUIコマンドを作ろうとすると、引数の処理が面倒。先輩のPlan9はどうしてるかmkdir.cを参考に見ると、
ARGBEGIN{ default: usage(); case 'm': m = ARGF(); if(m == nil) usage(); mode = strtoul(m, &m, 8); if(mode > 0777) usage(); break; case 'p': pflag = 1; break; }ARGEND
こんな具合に、ARGBEGINからARGENDまでが、マクロになってて、多少の手助けがされている。けど、必要最小限と言った感じ。きっとこの不便さは皆が感じていると思うぞ。
flag 並にシンプルでより強力な CLI パーサ kingpin の紹介
こんなのがあった。試しに使ってみるかな。更に面白そうなものとして、
go用のgemか。きっと出て来ると思ったよ。段々ぐちゃぐちゃになる予感。
今時のgoでネット関連のパッケージを使うと、シングルバイナリーじゃ無くなるとか。その回避策が解説されてた。
こちらは、積極的にsoファイルを作って、幸福を分け合いましょって策。段々現実的になって行くな。
GolangでwebサービスのAPIを叩くCLIツールを作ろう
go-toolのpresentで作ったスライドショーを公開する
go例としてWebじゃつまんないので、こういうのを見てみる。
go言語のデバッガ(delveとgdb)とcore dump
$ go get github.com/derekparker/delve/cmd/dlv # github.com/derekparker/delve/pkg/proc/native /home/sakae/godev/src/github.com/derekparker/delve/pkg/proc/native/threads.go:16:21: undefined: WaitStatus /home/sakae/godev/src/github.com/derekparker/delve/pkg/proc/native/threads.go:22:18: undefined: OSSpecificDetails # github.com/derekparker/delve/pkg/proc/gdbserial /home/sakae/godev/src/github.com/derekparker/delve/pkg/proc/gdbserial/gdbserver.go:412:21: undefined: backgroundSysProcAttr /home/sakae/godev/src/github.com/derekparker/delve/pkg/proc/gdbserial/gdbserver.go:462:21: undefined: backgroundSysProcAttr /home/sakae/godev/src/github.com/derekparker/delve/pkg/proc/gdbserial/rr.go:58:22: undefined: backgroundSysProcAttr
ちぇ、OpenBSDは蚊帳の外か。リナが繁殖しててつまらん脳。
goがPlan9の後継者って事がよく分かるぞ。そして、いつの間にかgoのsrcの中に、Plan9対応が入っているぞ。お父さんが昔を懐かしんで、20%ルールを使って夜なべしたんだな。
Goアセンブラ入門 より詳細な事は、Plan9のそれを見ろと、明言してますなあ。やってて良かったPlan9。