亀鶴茄子
亀鶴と茄子のblogです
土曜日, 12月 31, 2016
2016年最初で最期の投稿
この一年も人に道をよく聞かれたけど、一時期ほどの頻度ではなくなった気がする。
twitterを見返すとアウトプットを増やしたいのと、電子工作なことをやりたいと書いていて、見事にその通りに活動してきたように思う。
正月あたりはRaspberry Piに接続された白黒液晶をGoで動かすために、i2cの簡単な使い方や、Goでのクロスコンパイルの仕方について調べていた。このあたりの成果は6月に開催されたPayPal Tech MeetupのDemo Nightの際に存分に生かされている。
また直接的ではないものの、Raspberry PiのGPIO制御に慣れたことは、11月のArt Hack Dayにおいて非常に役に立った。
Demo NightにしろArt Hack Dayにしろ、会社の外で、特に評価がつくイベントに参加することは、緊張感を強いられるけど、地力をブーストしていくにはうってつけと思う。適度な危険に身を晒さないと成長実感を得るのは難しい。
夏には小規模ながらGoの勉強会を主催したりして、今更感はあるもののGoの布教活動もした。とはいうものの、Goを教えるという点では社外より社内の方が頻繁にあり、先生役が求められている気がしないでもない。
一方で、今年は応用というか実装面での学習、成長にパワーが割かれすぎて、もう少し基礎的な知識や、新しい言語を覚える時間が削られてしまった気もする。
夏ぐらいにCommon Lispに挑戦しようとしていたのだけど、Common Lisp自体はともかく、Emacsの半強制(一応elvisは入れたのだけど)に閉口してしまい、結局起動自体が億劫になってしまった。
他にもOCamlやRustを時々触ってはいたのだけど、イベント発表用の制作に追われて、結局まともには出来なかった。
プログラミング言語に限らず、自然言語についても、英語というか英会話を要求されるケースが仕事だけでなくプライベートでも何故か増えてきていて、そろそろ本格的に開始しないと日常生活でストレスがたまるようになってきている(弊社は英語公用語とかではない)。
一応、英会話学習サービスをやってはいたのだけど、やはり制作が佳境に入るとそれどころではなくなってしまった。
ということで、来年はもう少し基礎的な学習と英会話に力を入れていきたいなと思っている。
このブログももう少し活用するか。もしくはmediumあたりに引っ越すか。
bloggerはいつになったら公式でMarkdownをサポートするのだろう。
日曜日, 12月 27, 2015
LLDBでGoのデバッグをする その2 test編
その1ではgo build
したLLDBでデバッグしてみましたが、その2ではgo test
でのdebugの仕方です。
今回のデバッグ対象として以下のようなプログラム(main.go)とそのテスト(main_test.go)を書きました。
package main
import "fmt"
func RectArea(w, h int) int {
return w * h
}
package main
import "testing"
func TestRectArea(t *testing.T) {
s := RectArea(6, 8)
if s != 48 {
t.Error("error")
}
}
testをビルドする
go test
には -c
というオプションがあり、これを使うとtestのバイナリをビルドできます。これと -gcflags
を組み合わせてデバッグ可能なtestバイナリを作ります。
# -o は出力結果のファイル名を指定するオプション
$ go test -c -gcflags '-N -l' -o test
# 実行するとgo testと同じ結果が得られる
$ ./test
PASS
デバッグする
後は、前回同様lldbに、このtestバイナリを渡すだけです。
$ lldb test
(lldb) target create "test"
Current executable set to 'test' (x86_64).
(lldb)
せっかくなのでプログラム本体とテスト両方にブレイクポイントを置いてみます。
(lldb) target create "test"
Current executable set to 'test' (x86_64).
(lldb) b main.go:4
Breakpoint 1: where = test`_/Users/kmtr/misc/c.RectArea + 9 at main.go:6, address = 0x000000000007dc09
(lldb) b main_test.go:7
Breakpoint 2: where = test`_/Users/kmtr/misc/c.TestRectArea + 58 at main_test.go:7, address = 0x000000000007dc5a
(lldb)
後は run
するだけです。
main.go側で止まったところで、変数を確認。
3 func RectArea(w, h int) int {
4 return w * h
5 }
(lldb) fr v
(long) w = 6
(long) h = 8
(long) ~r2 = 0
(lldb)
c
で継続して、次はmain_test.goで止まるので、こちらでも変数を確認。
そして c
で終了させます。
4
5 func TestRectArea(t *testing.T) {
6 s := RectArea(6, 8)
-> 7 if s != 48 {
8 t.Error("error")
9 }
10 }
(lldb) fr v
(long) s = 48
(testing.T *) t = 0x000000c82009c000
(lldb) c
Process 25022 resuming
PASS
Process 25022 exited with status = 0 (0x00000000)
(lldb)
まとめ
-c
オプションでtestバイナリを作るのがポイントで、後は前回と同じですね。
また続くかもしれない。
土曜日, 12月 26, 2015
LLDBでGoのデバッグをする その1
環境
環境は以下の通りです。
- OS X El Capitan 10.11.2
- lldb-340.4.119
- go version go1.5 darwin/amd64
LLDBはxcodeについてきたものです。
やってみる
デバッグ対象のコードは、やっつけですがこんな感じです。ファイル名はmain.goとしました。
package main
import "fmt"
func main() {
a := 10
fmt.Printf("Add1(10) == %d\n", Add1(a))
}
// Add1 ...
func Add1(i int) int {
return i + 1
}
LLDBでデバッグするために、-gcflags '-N -l'
というオプションをつけてビルドします。
$ go build -gcflags '-N -l' main.go
$ ls
main main.go
lldbコマンドに作成されたファイルを渡してみます。
$ lldb main
(lldb) target create "main"
Current executable set to 'main' (x86_64).
(lldb)
ここから先はlldbの操作になります。とりあえず q
と打つとlldbから抜けられることだけは覚えてください。
デバッグを開始するには run
または r
と打ちます。
(lldb) run
Process 22044 launched: '/Users/kmtr/misc/c/main' (x86_64)
Add1(10) == 11
Process 22044 exited with status = 0 (0x00000000)
(lldb)
無事実行されました。
これではデバッグにならないので、ブレイクポイントを打ちます。
試しに6行目と12行目に打ってみます。ブレイクポイントを打つ方法はいくつかありますが、行番号指定の場合 b ソースファイル名:行番号
が楽です。
(lldb) b main.go:6
Breakpoint 1: where = main`main.main + 31 at main.go:6, address = 0x000000000000205f
(lldb) b main.go:12
Breakpoint 2: where = main`main.Add1 + 9 at main.go:12, address = 0x0000000000002199
(lldb)
br list
もしくは b
と打つと、現在のブレイクポイントの一覧が表示されます。
br delete ブレイクポイント番号
で対象のブレイクポイントを削除できます。
2番のブレイクポイントを消した後、再度ブレイクポイントを一覧表示して削除されることを確認してみてください。
ブレイクポイントを打った状態で再び run
すると、こんな感じに止まります。
3 import "fmt"
4
5 func main() {
-> 6 a := 10
7 fmt.Printf("Add1(10) == %d\n", Add1(a))
8 }
9
(lldb)
ステップオーバーは n
です。打ってみるとカーソルが7行目に移ると思います。fr v 変数名
で変数の中身を表示できるので打ってみます。
(lldb) fr v a
(long) a = 10
(lldb)
なぜかlongですが、とりあえずの確認には十分です。
現在のブレイクポイント周辺のソースを再表示する場合は f
と打ちます。
ソースレベルでのステップインは s
です。7行目で実行すると次のようにAdd1関数の中に入ると思います。
9
10 // Add1 ...
11 func Add1(i int) int {
-> 12 return i + 1
13 }
(lldb)
fr v
と変数名を指定せずに打つと、現在のフレームの変数が全て表示されます。
(lldb) fr v
(long) i = 10
(long) ~r1 = 0
(lldb)
(~r1
てなんだろう、、、)
c
と打つと、次のブレイクポイント、もしくは終了するまで停止せずに実行されます。
(lldb) c
Process 22103 resuming
Add1(10) == 11
Process 22103 exited with status = 0 (0x00000000)
(lldb)
LLDBの使い方は、The LLDB Debugger のgdbとの比較がわかりやすいです。
watchが効かないという話も見かけましたが、少なくとも私の環境では問題ありませんでした。
感想
少ししか使っていないので、どこまでちゃんと動くのか、GDBとの差がどれだけあるかわからないけど、LLDBでもデバッグはできそうです。
続くかもしれない。
水曜日, 5月 13, 2015
Goで書かれたHTTP2対応サーバーCADDYを試す
CADDYというHTTP2対応サーバーを見つけたので簡単に使ってみる。
Goで書かれていて、インストールは各プラットホーム毎のバイナリを適当に配置するだけなので、インストールが楽。
起動する
とりあえずPATHが通っているところに置いて叩いてみる。
$ caddy
0.0.0.0:2015
デフォルトポートが2015なのだけど、来年になったら2016になるのだろうか。
curlで叩いてヘッダを見てみる。
$ curl -I localhost:2015
HTTP/1.1 404 Not Found
Server: Caddy
Date: Tue, 12 May 2015 13:52:04 GMT
Content-Length: 13
Content-Type: text/plain; charset=utf-8
404だけど、とりあえず動いている。HTTP2はHTTPSじゃないと動かないので、HTTP/1.1なのが残念。
(CADDYのサイトはCADDYでホストされていて、HTTP2が使われていることをブラウザで確認しています)
設定を変える
CADDYの設定ファイルは-conf
オプションで任意のファイルを指定出来るけど、デフォルトではコマンドを叩いたディレクトリにあるCaddyfileを見るらしい。
1行目には必ずlistenするアドレスを書けということなので、ポートをデフォルトから変更したものを記述してみる。
$ echo "localhost:2016" > Caddyfile
$ caddy
localhost:2016
無事ポート2016で起動した。
ちなみに1行目と言うのは、設定値としての1行目で、コメントは普通にかける。コメントはapacheと同様、#
から始まる文字列。
静的ファイル配信
このままでは404を返すだけのサーバーなので、とりあえず静的ファイルを返すようにしてみる。
まずはドキュメントルートとなるディレクトリを作り、適当なHTMLファイルを置く。
$ mkdir public_html
$ echo "Hello, world" > public_html/index.html
次にCaddyfileをこんな感じに編集する。
root
はドキュメントルートを指定するディレクティブ。カッコでくくらずに、そのまま次の行に書いてもよいのだけど、書いた方が分かりやすいと思う。
# example
localhost:2016 {
root ./public_html/
}
再起動して、curlしてみる。
$ curl localhost:2016
Hello, world
無事、静的ファイルを配信出来た。
markdown配信
リダイレクトやプロキシといった機能の他に、CADDY特有の機能がいくつかある。その一つのmarkdown配信機能を使ってみる。
設定はmarkdownディレクティブの後ろに、適用するPATHを記述する。このPATHはリクエストのパスであると同時にmarkdownファイルを置くディレクトリの事も示すらしい。
つまりmarkdownファイルは、public_html/blogではなくblogというディレクトリに置く必要がある。
# example
localhost:2016 {
root ./public_html/
markdown /blog
}
次に記事を作る。
$ mkdir blog
$ echo "# Hello, markdown" > blog/hello.md
curlで確認。
$ curl localhost:2016/blog/hello.md
<!DOCTYPE html>
<html>
<head>
<title># Hello, markdown</title>
<meta charset="utf-8">
</head>
<body>
<h1>Hello, markdown</h1>
</body>
</html>
ソースを確認したところ、markdownの処理には https://github.com/russross/blackfriday を使っていた。
性能
性能について、FAQには正確に答えるのは難しいとしながら、概ね速いと書いてある。
Apacheよりは速くて、nginxよりちょっと遅いぐらいという事なので、実用上十分な速さと思っておけば良さそう。
CADDYのコンセプトは設定が簡単なWebサーバなので、それでよいのだと思う。
感想
全部の機能を試したわけではないけど、1バイナリで完結して、設定が簡単に出来て、それなりに速いという使い勝手の良いサーバーという印象。
ちょっとしたサーバーが欲しいのだけど、Apacheやnginxだと大げさ過ぎるとか、設定が面倒という時に重宝しそう。
あとはfcgiを使ったプロジェクトのルートディレクトリにCaddyfileを置いといて、git cloneしてcaddyと叩いたら開発環境として立ち上がるみたいな使い方とか。
小回りが効くので、応用範囲が広そう。
水曜日, 4月 29, 2015
Goのtesting/quickを簡単に触ってみる
Goのpackageを眺めていたら、いまさらtesting/quickというものを見つけたので試してみる。
quickの説明をみると、
help with black box testing
と書いてあるし、Checkという関数があったりするので、QuickCheckのためのパッケージのはず。
普通のtest
まずはquickを使わないテスト。
2つのintで割り算をする関数のテスト。
package main
import "testing"
func div(n, m int) int {
return n / m
}
func TestDiv(t *testing.T) {
if div(4, 2) != 2 {
t.Error("error!")
}
if div(3, 2) != 1 {
t.Error("error!")
}
}
明らかにテストケースが不足してる。
とりあえず第二引数に0を入れたら、確実にエラーになるはずだけど、このケースでは考慮されていない。
quick導入
というわけでquickを導入。
package main
import (
"fmt"
"testing"
"testing/quick"
)
// テスト回数計測のためのカウンター
var count int = 0
func div(n, m int) int {
count++
return n / m
}
func TestDiv(t *testing.T) {
f := func(x, y int) bool {
return div(x, y) == (x / y)
}
if err := quick.Check(f, nil); err != nil {
t.Error(err.Error())
}
fmt.Printf("テスト回数:%d\n", count)
}
これでgo test
してみる。
$ go test
テスト回数:100
PASS
ok quick_example 0.005s
通ってしまった。
quickというかQuickTestは要はとにかくいろいろ試してみるテストなので、100回程度では通ってしまうのも仕方ない。
今回の場合、第二引数に0が入るのは 「1/intの取りうる値」 なので、試行回数が全然足りない。
試行回数を変える
ということで試行回数を増やしてみる。
試行回数の設定をするには、quick.Configのインスタンスを作って、
MaxCountに試行回数を設定する。
package main
import (
"fmt"
"testing"
"testing/quick"
)
var count int = 0
func div(n, m int) int {
count++
return n / m
}
func TestDiv(t *testing.T) {
f := func(x, y int) bool {
return div(x, y) == (x / y)
}
config := &quick.Config{
MaxCount: 10000,
}
if err := quick.Check(f, config); err != nil {
t.Error(err.Error())
}
fmt.Printf("テスト回数:%d\n", count)
}
そしてgo test
する。
$ go test
テスト回数:10000
PASS
ok quick_example 0.015s
通ってしまった。
math.MaxInt64
を突っ込めば確実に失敗するのだろうけど、
それは最早ブラックボックステストではない。
(ちなみにMaxInt64でやってみたら、手元のマシンでは3分経っても終わらなかった)
テスト値を制御する
とはいえ、0ぐらい入れて欲しい。
ブラックボックステストと言っても、本当に無作為に値を突っ込むのではなく、とりあえず0から100と、0から-100で動いてもらえば最低限の仕様は満たすという事はままある。
そのように、自分でランダム性を制御するにはconfig.Valuesに値を生成する関数を設定する。
値を生成する関数はfunc(args []reflect.Value, rand *rand.Rand)
というシグネチャを持つ。
args
には、quick.Check関数の第一引数に渡す関数の引数の数のサイズを持つスライスが渡されてくる。
今回のケースで、0から100の範囲の値を無作為に選ばせるのであれば、こんな感じの関数をconfig.Valuesに渡せば良い。
func(args []reflect.Value, rand *rand.Rand) {
args[0] = reflect.ValueOf(rand.Intn(100))
args[1] = reflect.ValueOf(rand.Intn(100))
}
これで10000回くらい回せば、流石にargs[1]に0が入るはず。
第二引数のrandは見ての通りRandインスタンスが渡ってくる。このRandインスタンスはconfig.Randで設定しないとデフォルトのものが使われるのだけど、go 1.4.2ではデフォルトのrandはseedが0固定されているので、変えないと毎回同じ結果になる(デフォルトのseed=0は意図したものなのか?)。
上記を踏まえるとこんな感じのソースになる。
package main
import (
"math/rand"
"reflect"
"testing"
"testing/quick"
"time"
)
var count int = 0
func div(n, m int) int {
count++
return n / m
}
func TestDiv(t *testing.T) {
f := func(x, y int) bool {
return div(x, y) == (x / y)
}
config := &quick.Config{
MaxCount: 10000,
Rand: rand.New(rand.NewSource(time.Now().UTC().UnixNano())),
Values: func(args []reflect.Value, rand *rand.Rand) {
args[0] = reflect.ValueOf(rand.Intn(100))
args[1] = reflect.ValueOf(rand.Intn(100))
},
}
if err := quick.Check(f, config); err != nil {
t.Error(err.Error())
}
fmt.Printf("テスト回数:%d\n", count)
}
これでgo test
すると、まず確実にこんなかんじで0除算エラーが発生する。めでたしめでたし。
— FAIL: TestDiv (0.00s)
panic: runtime error: integer divide by zero [recovered]
panic: runtime error: integer divide by zero
まとめ
今回はquickを使うというのが目的なので、こんな感じのテストコードになったけど、実際には普通のテスト(=ホワイトボックステスト)と組み合わせて使うはず。
ブラックボックステストはホワイトボックスを置き換えるものではないし。
といっても、特に難しく考えず、とりあえず値を大量に作ってくれる装置として使うだけでも恩恵があるかと。今回のように完全な無作為ではなく、そこそこの無作為にしてグレーボックスとして使ったりとか。
quick固有の問題として、デフォルトのRandがseed=0で初期化されているという点だけは要注意ですね。
火曜日, 2月 17, 2015
Firefox OS WoTハッカソンに参加してきた
2/14-15に行われたFirefox OS WoTハッカソンに参加してきました。
申し込んだ時点で70名の募集のところ既にいっぱいで、補欠でしたが数日でキャンセルが何人か出て、繰り上がりで参加出来ることになりました。
終わった今、改めて見てみたら50位ぐらいまで繰り上がっていたので、こういうのは諦めたら駄目ですね。
アイディア出し
初日は企画コンセプトやWoTの概要説明から始まり、アイディア出し&チーム決め。
最終的には、以下にまとめられているものを作ったわけですが、
http://fabble.cc/dobogo/pitagoraxsugoroku
いきなり「要件定義」に書いているような立派な案が出たわけではなく、お菓子を食べつつ、光らせたいとか、NFCを使いたいという、使いたいものベースで、だらだらとホワイトボードに書き付けていました。
途中でゲーム的なものや動きのあるものが作りたいという流れになり、すごろくというアイディアと、ピラゴラ装置というアイディアが発生。それらを組み合わせた「いろんな仕掛けのあるすごろく」になっていきました。
当初、そのすごろくはFx0を持った人間が歩き回るという話になっていましたが、どこかのタイミングでロボット(この時点ではふぉくすけの存在は無く、車に乗せたFx0)に移動させるというアイディアが出てきて「すごろく」と「ロボット」の二本立てとなりました。
最終的に、今回はインパクト重視で「ロボット」に注力し、すごろくは余力があったらという方針になりました。
ふぉくすけが採用された経緯が記憶からすっぽり抜けていますが、他のチームの方から、ふぉくすけのヌイグルミをお借りして、ふぉくすけロボという具体的なゴールが昼食前には完成しました。
設計と実装
ロボットの方針が出たあたりで、ざっくりとどういうモジュールが必要かと、それぞれの間のやり取りの図を書き、それを元に役割分担をしました。私はhttpd.jsを使って、Fx0内で動くサーバーの実装を担当しました。
ふぉくすけロボは、足と尻尾はmbedで制御されているのですが、その指示はFx0からHTTPで、mbed側のサーバに飛ばしています。私が作ったのは、リモコン側からのリクエストをそのままmbedに飛ばす、一種のリバースプロキシというわけです。
とりあえずリモコン担当の方、mbed担当の方と、ざっくり必要そうなAPIを相談し実装。
mbedが制御しない、表情制御については、並行で作られている表情プログラムを呼び出す事になるので、一旦呼び出し口だけを用意しておきました。
実装そのものは大したことが無いのですが、テストが大変でした。ひとまずFx0にインストールしてみて動かしてみたのですが、デバッガにソースが出てこない。console.logも出てこない(確か)。
仕方がないので、FirefoxのWebIDEからシミュレータを起動して、そこでテストしようとするも、バージョン2.1以降(unstable)でないとやはりソースが出てこない。
やっとデバッガにソースが出たと喜んだものの、コールバック内のブレイクポイントは機能しないという状況でした。Flame(fxos ver3.0?)をお借りして試してみたところ、シミュレータと同様の状態だったため、console.logでプリントデバッグで進めました。
また、初日はネットワーク環境が不安定で、テスト用の無線LANになかなか繋げられず、mbed側サーバーとの疎通が安定して出来るようになったのは2日目になってからでした。
今回のハッカソンはWoTがテーマになっていますが、実際のWoTの現場でも、安定したネットワークの確保は課題になると思います。
もう一つ大変だったのは、現状のhttpd.jsのパスHandler機能、要するに静的ファイル配信の機能がFx0でしか動作しないことでした。リモコンは静的なHTML+JSで書かれているので、これをロボット側のFx0から配信しようとしたのですが、シミュレータやFlameでは動作せず、しばらくたってFx0で試して、初めて機能する事を確認しました。
そんな新しい端末やOSらしいトラブルに見舞われながらも、何とかかんとか、ふぉくすけロボを完成させることができました。
デモ
完成したふぉくすけロボですが、mbed側は、mbed + 無線LANモジュール + モーター*3という構成のため、非常に電気を使うようです。さらに、それを乾電池だけで動かしているため、下手に動かすとデモが出来ない恐れがありました。
それでも、デモの数分間、ふぉくすけロボは頑張ってくれて、おかげさまでARM賞もいただけました。
Firefox OSに対する感想
Firefox OSは使う分には、特に問題無いレベルにあると思います。少なくともFx0やFlameは、スマートフォンとして普段使うのに必要十分な動作をしていました。
開発環境も、若干使いづらいところはあるものの、今後改善していくだろうし、何よりWeb技術でアプリが作れるという特性は、潜在的な開発者数が、AndroidやiOS以上に存在しているという事を示しています。httpd.jsは普通のアプリにはまず使われないと思われるので、今回ほどの苦労はしないはずです。
また今回はFirefoxOSの端末としてFx0を使うことが、ある種の縛りとしてあったのですが、他のチームの発表を見ると、Fx0ではなくOpen Web Boardを主にしたものもいくつかあり、スマートフォン以外の応用可能性が、Firefoxには十分あるように思います。
ふぉくすけロボの発展
ふぉくすけロボはその動きをリモコンに頼っていたわけですが、リモコンからは単にHTTPでリクエストを飛ばしているだけです。つまりロボのFx0にHTTPに投げられる存在であれば、何であれふぉくすけロボを操作出来るわけです。
今回作れなかったすごろく側ですが、センサーとHTTPクライアントを埋めた壁、床を作ることで、すごろくがふぉくすけを動かす仕組みなんかが作れると、わけがわからなくて良いと思います。
それと懇親会の最後で、ふぉくすけが出た状態のFx0をお面のようにしたのですが、カメラAPIと組み合わせて、任意の顔をお面にするアプリが作れるなあと考えていました。
問題はFx0もFlameも持っていないことなわけですが。
今後機会があれば作りたいです。