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のあれかな? どうやって使うの? 探りを入れてみる。

go tool trace 使ってみよう

go tool trace

これがサンプルプログラム。コードを埋め込むとな。

$ 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言語でサクッとコマンドラインツールをつくる

こんなのがあった。試しに使ってみるかな。更に面白そうなものとして、

glide - パッケージ管理のお困りの方へ

go用のgemか。きっと出て来ると思ったよ。段々ぐちゃぐちゃになる予感。

Go でツール書くときの Makefile 晒す

今時のgoでネット関連のパッケージを使うと、シングルバイナリーじゃ無くなるとか。その回避策が解説されてた。

Go 1.8のpluginパッケージを試してみる

こちらは、積極的にsoファイルを作って、幸福を分け合いましょって策。段々現実的になって行くな。

GolangでwebサービスのAPIを叩くCLIツールを作ろう

go-toolのpresentで作ったスライドショーを公開する

Goならわかるシステムプログラミング

go many projects in github

Go by Example

Goで小さなScheme、Gigueを実装しました

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とAlefとLimbo

goがPlan9の後継者って事がよく分かるぞ。そして、いつの間にかgoのsrcの中に、Plan9対応が入っているぞ。お父さんが昔を懐かしんで、20%ルールを使って夜なべしたんだな。

Goアセンブラ入門 より詳細な事は、Plan9のそれを見ろと、明言してますなあ。やってて良かったPlan9。