goでも(2)

毒ガス サリン VXガス 混ぜるな危険の塩素系ガス 練炭による一酸化炭素ガス そして 二酸化炭素ガス。えっ、二酸化炭素ってコーラにもサイダーにもビールにも含まれている あの泡の事でしょ。毒なの?

40%濃度だと瞬殺だそうですよ。ええ、『強奪・箱根駅伝』(新潮社)って小説を読んだら、 そんな事が書いてありました。

日テレとか関東学生陸上協議会やらを地道に取材して、長年の構想の上小説化したものだそうです。 そんな小説に、二酸化炭素がどうからんでくるの? それに日テレって何だ? それは、化け物 番組を作ってる(中継してる)テレビ屋さんでしょ。

神奈川某大学の陸上部、この所優勝はおろか、シード権も得られずに低迷中。そんな中に、 きら星が現れた。そして年末、その大学の女子マネが突然姿を消す。

その頃、日テレの駅伝中継卓では、最後の調整、リハーサルに余念がない。突然、モニターに 苦しげに歪んだ若い女の顔が映し出される。中継回線が、何者かに電波ジャックされた模様。 7GHz帯をジャックするって、とてつもなく大変。受信パラボラ目掛けて、強い電波を送信 しなければならないからだ。

どうも電波ジャックされた所は、鶴見中継所から新宿ビル間らしい。その頃、女子マネの 失踪を掴んだ神奈川県警は、箱根駅伝に絡んでいる事に気付く。 とまあ、ストーリーが進んで行くんだけど、どこに二酸化炭素が出てくるかは、お楽しみ。

ビールの海で泳いで、二酸化炭素でハイになれるんだろうか? 誰かやってみれ。

読書メーター 6冊 / 1854頁 / 10600円

go run とは

碁の説明書によると、ご覧は、実行ファイルを作成せずに、プログラムを実行します。それに対して ごびるどは、コンパイルされた実行ファイルを作成します、ってなってる。

ご覧は、インタープリターが起動するんか。まるで、ハスクルのghc対ghciだな。碁もハスクル に習ったんか、感心々。

go runが実行ファイルを作らんってのは、どうも嘘っぽいと思うぞ。裏を暴け。それには、 go runがまさに実行してる現場に踏み込んで、現行犯逮捕しなきゃならないです。すくなくても、 今の日本の法律ぢゃ。

犯行を長引かせておいて、その間に裏を取ればOK。実行が終わらないようなソースを書く。

package main

import "fmt"

func raw_input(prompt string) string {
        var s string
        fmt.Print(prompt + " ")
        fmt.Scanln(&s)
        return s
}

func main() {
        fmt.Printf("hello, world\n")
        raw := raw_input("input:")
        fmt.Println("read number", raw, "from stdin")
}

何の事は無い、某所から頂いた、python2コンパチのキーボード入力ルーチンを 実行させるだけ。

[sakae@manjaro helo-go]$ go run hello.go
hello, world
input:

入力待ちになってる間に、内偵します。オイラー予想によると、隠し実行ファイルを 作って、それを実行。プログラムの終了と共に、証拠隠滅してるに違いない。

ワーキングdirにドットファイル作成か? そんなんでは無かった。じゃ、普段余り気に しない、共同便所はどうだ?

[sakae@manjaro helo-go]$ cd /tmp
[sakae@manjaro tmp]$ ls
go-build685131702/

怪しい包みがありましたぜぇー。デカ長さん。中身をそっと覗いてみろ。

[sakae@manjaro tmp]$ tree go-build685131702/
go-build685131702/
├── command-line-arguments
│       └── _obj
│               └── exe
│                       └── hello
└── command-line-arguments.a

犯行日時も一致しました。これから、物を押さえます。

[sakae@manjaro tmp]$ cp go-build685131702/command-line-arguments/_obj/exe/hello  ~/hello-run

よし、じゃ、実行してたのを終了。と同時に、/tmpの下の包みは消されました。実行のたびに、 入れ物の名前を変えるのは、常套手段だ。 そんじゃ、buildで作ったのと同じ物件になるか確認しとけ。

[sakae@manjaro helo-go]$ go build hello.go
[sakae@manjaro helo-go]$ ls
hello*  hello.go
[sakae@manjaro helo-go]$ ls -l hello ~/hello-run
-rwxr-xr-x 1 sakae users 1162028 10.11.2014 05:41 /home/sakae/hello-run*
-rwxr-xr-x 1 sakae users 1497752 10.11.2014 05:45 hello*

runで一時的に作られた方は、身軽になってますぜ。どうしてですか、でか超さん? そんなの自分で考えろ。えと、goの庇護の下で動いてるんで、余計なコードは付けて ないんでしょう。裏を取れ。

goの親分が居ない所へhello-runを移送して、動くか確認しれ。そんじゃ、ウブ14.04へ 連れて行って、吐かせてみます。。。。

結論だけ言うと、あっさり動いてしまいましたぜ。これ以上は、科捜研にでも送って、 objdumpとかの機械で差分解析してもらおう。その前に、現場で出来る簡易鑑定をやっておけ。

[sakae@manjaro helo-go]$ size hello ~/hello-run
   text    data     bss     dec     hex filename
1046446   24336   79312 1150094  118c8e hello
1046446   24336   79312 1150094  118c8e /home/sakae/hello-run

あれ、同一人物みたい。普段目にする、lsコマンドは詐欺に遭ってたんすかね? それとも、はりぼてで少し膨らませているんですかね?ピークホールとか言う技が 開発されてるみたいですよ。 どうも、そうらしいな。そんじゃ、そんな姿に騙されずに、もう少しgo族の属性を 探っておけ。

[sakae@manjaro ~]$ ps awx
   :
  488 pts/2    Sl+    0:00 ./hello
  492 pts/1    R+     0:00 ps awx
[sakae@manjaro ~]$ pstree -p 488
hello(488)─┬─{hello}(489)
            ├─{hello}(490)
            └─{hello}(491)

今度は、分身術を使ってるぞ。またps検査器を騙しているな。主犯は488で後は従犯ですかね? 従犯の役目は、見張り、運搬係、替え玉ぐらいかな。以前、任意で引っ張って、gdbにかけて 締め上げたんですが、ステップ実行しても、のらりくらりとしてて、さっぱり核心に迫れま せんでした。今回は、動いてる犯行グループを強襲して、裏を探ってみます。

まずは、主犯と思しきプロセスと言うかスレッド。

[sakae@manjaro ~]$ gdb -p 488
  :
(gdb) bt
#0  0xb772f416 in __kernel_vsyscall ()
#1  0x080ae98f in syscall.Syscall ()
    at /usr/lib/go/src/pkg/syscall/asm_linux_386.s:25
#2  0x080ad9c0 in syscall.read (fd=0, p=..., n=0, err=...)
    at /usr/lib/go/src/pkg/syscall/zsyscall_linux_386.go:838
#3  0x00000003 in ?? ()
#4  0x00000000 in ?? ()

続いて、従犯 489

(gdb) detach
Detaching from program: /home/sakae/godev/src/helo-go/hello, process 488
(gdb) attach 489
Attaching to program: /home/sakae/godev/src/helo-go/hello, process 489
warning: process 489 is a cloned process
0xb772f416 in __kernel_vsyscall ()
(gdb) bt
#0  0xb772f416 in __kernel_vsyscall ()
#1  0x0806d893 in runtime.futex ()
    at /usr/lib/go/src/pkg/runtime/sys_linux_386.s:259
#2  0x0805430b in runtime.futexsleep ()
    at /usr/lib/go/src/pkg/runtime/os_linux.c:49
#3  0x081503d0 in runtime.sched ()

次は、従犯 490

(gdb) detach
(gdb) attach 490
(gdb) bt
#0  0xb772f416 in __kernel_vsyscall ()
#1  0x0806d893 in runtime.futex ()
    at /usr/lib/go/src/pkg/runtime/sys_linux_386.s:259
#2  0x08054374 in runtime.futexsleep ()
    at /usr/lib/go/src/pkg/runtime/os_linux.c:55
#3  0xb762cf98 in ?? ()

最後は、491

(gdb) bt
#0  0xb772f416 in __kernel_vsyscall ()
#1  0x0806d893 in runtime.futex ()
    at /usr/lib/go/src/pkg/runtime/sys_linux_386.s:259
#2  0x0805430b in runtime.futexsleep ()
    at /usr/lib/go/src/pkg/runtime/os_linux.c:49
#3  0x18318618 in ?? ()

後は、示されたそれぞれのファイルの所を見て、犯人の心理分析をしておけ。良い刑事に なるには、必須の修行だぞ。と、近頃も警察物小説を耽読してる人の戯言でした。

go-mode.elの改善

素のgo-modeは、ソースファイルを書く事しか考慮されていない。repl族を備えたLL系言語を やってきた人には、ちょいと辛い。だって、emacsで編集してsave。別端末に切り替えて、 go run xx.goとかやってた。正直面倒。

emacs上から、C-c C-r とかやったら、今編集してるファイルをgo runして欲しいぞ。 そうなれば、便利この上無い。何とかならないかと、go-mode.elを眺めていたら、godocの 関数に遭遇。こやつ、M-x godoc すると、今カーソルのある関数を引数にして、説明書を 表示してくれるんだ。まあ、manみたいなものだな。

これを叩き台にして、望む機能を実現しちゃおう。で、go-mode.elの最後の所にコードを 追加したんだけど、反映されず。そんな関数無いぞエラーになる。何で? それ以前に、emas起動時に、elcファイルよりelファイルの方が新しいよ、警告が出てた。

そうか、emacsは、バイトコンパイルされたファイルが有ると、そちらを優先しちゃうんだな。 だから、警告が出てきてたんだ。おいらの希望としては、Makeみたいに振る舞って欲しい。 新しいソースが有るんで、新しいソースと使うかい? って聞いてくるとか、その場でコンパイル しますかって聞いてくるとか。まあ、自分で注意するしかないな。

elファイルを改修debugする時は、対応するelcファイルを消しておけ。多少ファイルのロードに 時間がかかるけど、我慢我慢。ソースファイルが安定したら、コンパイルすれば良い。

と言うことで、開陳。

(defun go-build (query)
  "go build"
  (interactive (list (buffer-file-name)))
  (unless (string= query "")
    (save-buffer)
    (set-process-sentinel
     (start-process-shell-command "go-build" "build-result"
                                  (concat "go build -gcflags '-N -l' " query))
     'godoc--buffer-sentinel)
    nil))

(defun go-run (query)
  "go run on buffer *go-run-output*"
  (interactive (list (buffer-file-name)))
  (unless (string= query "")
    (save-buffer)
    (start-process-shell-command "go-run" "*go-run-output*"
                                 (concat "go run " query " "
                                         (read-from-minibuffer "args: ")))
    (switch-to-buffer "*go-run-output*")
    nil))

(defun go-clear-screen ()
  (interactive)
  (erase-buffer))

(define-key go-mode-map (kbd "\C-c\C-k") 'go-build)
(define-key go-mode-map (kbd "\C-c\C-r") 'go-run)

何の事は無い。コマンドを組み立てて、shellに渡してるだけ。良く使うのは、go-runの方。 今いるソース画面で、C-c C-r すると、その画面に結び付いたgoファイルを保存し go runして くれる。mini-bufferにargs:って聞いてくるんで、引数が有れば、入力、無ければ、そのまま RETキーを叩く。

画面が反転して、出力画面になる。実行結果が表示される。元の画面に戻る場合は、C-x b すれば、元のバッファーの候補が出てくるんで、そのままRET。

これで、goもrepl系言語と同様に使える。コンパイル時間も意外に速かったりします。

[sakae@manjaro helo-go]$ time go build hello.go

real    0m0.650s
user    0m0.287s
sys     0m0.307s

これもそれも、昔コンパイラーを書いていたノウハウがある人達だから? それとも、 思いっきり、文法を単純化してるから?

[sakae@manjaro helo-go]$ go build -x hello.go
WORK=/tmp/go-build352607200
mkdir -p $WORK/command-line-arguments/_obj/
cd /home/sakae/godev/src/helo-go
/usr/lib/go/pkg/tool/linux_386/8g -o $WORK/command-line-arguments.a -trimpath $WORK -p command-line-arguments -complete -D _/home/sakae/godev/src/helo-go -I $WORK -pack ./hello.go
cd .
/usr/lib/go/pkg/tool/linux_386/8l -o hello -L $WORK -extld=gcc $WORK/command-line-arguments.a

コンパイラーの挙動を見ると、goのソースを8gで一気にアセンブルして、後は、8lでリンク。 ここの所で、gccとニアミスするけど、リンカーフェーズだろうし、goだけで組んでいるなら 余り関係なさそう。これが、LLぽく速く動いてくれる秘訣なんだろうな。

で、注意を一つ。先の刑事物でやった例のように、セッション中に 入力をするようなのは、入力がgoに届かないので駄目だ。こうなったら、C-x k で、プロセスと 画面を強制削除する事。

go-buildの方は、debugに便利なように最適化禁止フラグを付けて、コンパイルしてる。 コンパイルが終わった後、M-x gdbで、debuggerに突入出来るんで、少しはLightIDEに 似てきたかな。

コンパイルエラーが有ると、画面が割れて、 エラーを表示してくれる。何か入力すると、その画面は無常にも消えてしまうので、 短期記憶を鍛えるのに使えるかも知れません。無事コンパイル出来ると、無表示の画面が 出てくるという、変な挙動をしますが、許してね。

go入力

最初、誤入力って変換されたぞ。全く碁とか伍とか娯とか色々出てくるな。おいらに取って、goが 娯になるのは何時の事だ。プログラミングに王道無し。

今回はgoに入力を与えるにはどうするか、あたりを調べて地理感を掴んでおく。 先にやったインタラクティブ入力は言うに及ばす、ファイル入力、ネットワーク入力 ぐらいは、誰でも思いつく。その他は?

go-runの便利emacs関数を書いた時に出てきた、コマンドライン入力、それと、環境変数入力、環境全部、 パイプ入力ぐらいかな。ちょいとやってみる。

package main

import "fmt"
import "flag"
import "os"
import _ "log"

func main() {
    fmt.Printf("hello, world\n")
    fmt.Println("os.Args: ", os.Args)
    flag.Parse()
    fmt.Println("flag.Args: ", flag.Args())
    fmt.Println("GOPATH: ", os.Getenv("GOPATH"))
    //  log.Println("Env: %#vx", os.Environ())
}

使っていないパッケージをimportしておくと、コンパイルエラーになるので、冒頭にアンダーバーを 入れておく。任意の文字列を指定しておくと、パッケージを別名指定した事になる。ドットの 場合は、そのパッケージ名を省いて関数名を入力出来る。

hello, world
os.Args:  [/tmp/go-build386928821/command-line-arguments/_obj/exe/hello 123 min\i-buf]
flag.Args:  [123 mini-buf]
GOPATH:  /home/sakae/godev

Process go-run finished

これ、C-c C-rの出力画面。自動スクロールしないので、画面出力を見る時は、自分でチョメチョメ するか、画面を消してからやってくれ。

御出力には、fmtとかlogを使うのがポピュラー。誤変換もとえ文字列表現の数字を 数値に変換するにはstrconvとかいうパッケージを使うそうな。C語世代には懐かしい、atoi なんて関数は、Atoi って世代間ギャップを埋めるために用意されてるぞ。

そうそう、頭が大文字で始まってる関数とかは、何処からでもでも使える公共物。小文字で 始まってるのは、そのパッケージ内からしかアクセス出来ない。こういう単純仕様は、 コンパイラー作者にもユーザーにも優しい仕様だな。

パッケージをつらつらと眺めているんだけど、品揃えが何となくWeb系を作る材料沢山 集めましたって、なっていないかい。まあ、ぐぐる様謹製だから、作ってる人も自粛してる んだろうけど。彼らは、unixの生みの親達、その辺は十分に承知してるだろうけど。

unixが作られた目的は、スタートトレックとか言うゲームを動かしたかったから。いくら なんでも、ベル研でそんな事は公言出来ない。彼らは、文書フォーマッターを開発する って表の目標を掲げて、unixを開発してたってのは、有名な話。

今後、ぐぐる様に追従するなら、ロボット系かな。人口知能 AIの世界だな。それ用の パッケージがいろいろ出てくる事を大いに期待します。毎度々、Webじゃつまんないですからね。

goの上のLisp

時代を先取りします。goの上にLispを実装したものが公開されてました。 例によって、OKIの人達です。彼らも、XXでLispってことでいろいろやってて、Javaとか C#とかrubyとかでLispを実装してます。

おいらも、XXでも、血圧管理プログラムをってやってますんで、同じ穴のむじなですな。 一つの言語に執着するあの人達をみると、石の上にも3年はおろか、10年以上は頑張って ますんで、それはもう、個人の生き様としか言いようがありませんけどね。

で、 Go 言語による簡単な Lispです。 実装対象が分かっていると、コードを読むのにも、ちからが入ります。これの続編で、 無駄にインテルが売っている多数のCPUを動かしちゃおうって試みも、興味津々。 future/forceを使うらしいけど、これの元となったのが、delay/forceだなんて、ちっとも 知りませんでした。そう言えば、知らずにdelay/forceを前回、使ったな。

プロファイルのやりかたの実例とかもあって、なかなかためになりますよ。 この他、 Go 言語 記事一覧 には、面白そうなのが沢山出てました。楽しまなくちゃ、娯じゃないですからね。

golangのprof

プロファイルを取るには、ソースに改変が必要らしい。

[sakae@manjaro lisp]$ diff -u tiny-lisp.go tiny-lisp-prof.go
   :
@@ -6,6 +6,7 @@
        "fmt"
        "lisp"
        "os"
+       "runtime/pprof"
        "strings"
 )

@@ -55,6 +56,10 @@

 // Lisp スクリプトまたは Lisp 対話セッションを実行する。
 func main() {
+       pf, _ := os.Create("cpu-profile")
+       pprof.StartCPUProfile(pf)
+       defer pprof.StopCPUProfile()
+
        lisp.ReadAndEval(prelude)
        n := len(os.Args)
        if n >= 2 && os.Args[1] != "-" {

godoc runtimeすると、CPUProfileだけじゃなくて、色々な角度から分析できるのね。

[sakae@manjaro lisp]$ /usr/share/go/misc/pprof tiny-lisp-prof cpu-profile
Possible precedence issue with control flow operator at /usr/share/go/misc/pprof line 3008.
Welcome to pprof!  For help, type 'help'.
(pprof) top
Total: 198 samples
     106  53.5%  53.5%      106  53.5% ExternalCode
      12   6.1%  59.6%       15   7.6% runtime.mapaccess2_fast32
       9   4.5%  64.1%       12   6.1% copyout
       9   4.5%  68.7%       23  11.6% runtime.mallocgc
       8   4.0%  72.7%       14   7.1% hash_insert
       8   4.0%  76.8%       88  44.4% lisp.(*Env).Eval
       6   3.0%  79.8%        6   3.0% runtime.memclr
       4   2.0%  81.8%       19   9.6% lisp.(*Env).Get
       4   2.0%  83.8%        4   2.0% settype
       3   1.5%  85.4%        3   1.5% runtime.efacethash
(pprof)

helpを見たら

(pprof) disasm ExternalCode
Total: 198 samples
ROUTINE ====================== ExternalCode
   106    106 samples (flat, cumulative) 53.5% of total
-------------------- /usr/lib/go/src/pkg/runtime/proc.c
   106    106  2170: static void ExternalCode(void) {}
   106    106     805ba60: mov    %gs:0x0,%ecx
     .      .     805ba67: mov    -0x8(%ecx),%ecx
     .      .     805ba6d: cmp    (%ecx),%esp
     .      .     805ba6f: ja     805ba7c <ExternalCode+0x1c>
     .      .     805ba71: xor    %edi,%edi
     .      .     805ba73: xor    %eax,%eax
     .      .     805ba75: call   806da50 <runtime.morestack_noctxt>
     .      .     805ba7a: jmp    805ba60 <ExternalCode>
     .      .     805ba7c: ret
     .      .     805ba7d: add    %al,(%eax)

そして、グラフツール(graphviz)の中に収録されてるdotが入っていると、可視化も出来るのね。

先人が居ないかと調べてみたら Go言語でプロファイリング(CPU profiling検証編 とか golangで書かれたプログラムのメモリ使用状況を見る が、見つかりました。みんな、娯してるな。

引越しのご案内

今までググル様の軒先を借りていたgoのソース用倉庫が、独立するんだってさ。 だから、オメーラのソースに書いてある住所も変更しろとな。 背戸なんてコマンド、久しぶりにみたぞ。

日本みたいに、1年間は、旧住所へ転送してくれるサービスは、どうやら無いっぽい。 これがアメリカ流、合理主義?

多数有る郵便物から、引越しした事を察知して旧住所への転送。考えただけでも、ぞっと するような複雑な作業。これを粛々と実行してくれる郵政。これぞ日本流サービス。 日本に生まれてよかったな。ああ、外国の郵便システムの事はさっぱり知らないで、 こんな事を書いてるけどね。。。。

 Updating

 Any import path in any Go source file beginning with “code.google.com/p/go.”
 should change to begin with “golang.org/x/”.

 On Linux, OS X, and other Unix systems, this command updates all Go source
 files in the current directory and all subdirectories, so running it in your
 GOPATH workspace root should suffice to update all your source code:

     sed -i .orig 's|"code\.google\.com/p/go\.|"golang.org/x/|' \
         $(find . -name '*.go')