Vimのカバレッジを見る
この記事はVim Advent Calendar 2017の2日目の記事です。
Vimのカバレッジはcoveralls.ioやcodecov.io上で見ることができますが、手元で見たくなることもあると思います。
そんな時はMakefileに書いてある通り、次のようにしてカバレッジを計測し、生成されたhtmlから見ることができます。
# Vimのリポジトリ直下に移動 # 各種フラグに--coverageをつけてビルド、他はお好みで CFLAGS=--coverage LDFLAGS=--coverage ./configure --with-features=huge && make cd ./src # まずはゼロカバレッジの初期データを作る lcov -c -i -b . -d objects -o objects/coverage_base.info # テストを走らせてカバレッジ情報を作る make test lcov -c -b . -d objects/ -o objects/coverage_test.info # 初期データとテストのカバレッジ情報を結合する lcov -a objects/coverage_base.info -a objects/coverage_test.info -o objects/coverage_total.info # 結果をobjects/index.htmlとして生成する genhtml objects/coverage_total.info -o objects
ビルド時に--coverage
をつけることでgcovが有効になり、実行された行が記録されるようになります。
lcov(と付属のgenhtml)はそのフロントエンドツールです。
さて、実行された行が記録されるということは、Vimで特定の操作をした際にどの関数が呼ばれたのか調べることができる、ということです。
しかし、gcovはプログラムの終了直前に保存処理を行うため、目的の操作だけの結果を知るには
の順で操作をしつつ、1と3は除外する必要があります。
幸いgenhtmlには-b(--baseline-file)
オプションがあるので次のようにすれば実現できます。
# 上と同じ lcov -c -i -b . -d objects -o objects/coverage_base.info # 起動と終了のカバレッジを取得 VIMRUNTIME=../runtime ./vim --clean -c 'q' lcov -c -b . -d objects/ -o objects/coverage_quit.info lcov -a objects/coverage_base.info -a objects/coverage_quit.info -o objects/coverage_baseline.info # カバレッジをリセット lcov -z -d objects/ # 起動と:smileと終了のカバレッジを取得 VIMRUNTIME=../runtime ./vim --clean -c 'smile | q' lcov -c -b . -d objects/ -o objects/coverage_smile.info lcov -a objects/coverage_base.info -a objects/coverage_smile.info -o objects/coverage_result.info # 生成されるのはcoverage_result.infoからcoverage_baseline.info分のカウントを減らした結果 genhtml -b objects/coverage_baseline.info objects/coverage_result.info -o objects
というわけで:smile
*1にはsyntax.c
以下のソースが使われていることがわかりました。
処理を正確に追うにはデバッガを使うのが確実だとは思いますが、ソースの構造がある程度わかっていないと難しかったりもします。
なので、さらっと概要と知りたい時やソースコードリーディングのお供にでも是非カバレッジを活用してみてください。
:smile oooo$$$$$$$$$$$$oooo oo$$$$$$$$$$$$$$$$$$$$$$$$o oo$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$o o$ $$ o$ o $ oo o$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$o $$ $$ $$o$ oo $ $ "$ o$$$$$$$$$ $$$$$$$$$$$$$ $$$$$$$$$o $$$o$$o$ "$$$$$$o$ o$$$$$$$$$ $$$$$$$$$$$ $$$$$$$$$$o $$$$$$$$ $$$$$$$ $$$$$$$$$$$ $$$$$$$$$$$ $$$$$$$$$$$$$$$$$$$$$$$ $$$$$$$$$$$$$$$$$$$$$$$ $$$$$$$$$$$$$ $$$$$$$$$$$$$$ """$$$ "$$$""""$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ "$$$ $$$ o$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ "$$$o o$$" $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ $$$o $$$ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$" "$$$$$$ooooo$$$$o o$$$oooo$$$$$ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ o$$$$$$$$$$$$$$$$$ $$$$$$$$"$$$$ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ $$$$"""""""" """" $$$$ "$$$$$$$$$$$$$$$$$$$$$$$$$$$$" o$$$ "$$$o """$$$$$$$$$$$$$$$$$$"$$" $$$ $$$o "$$""$$$$$$"""" o$$$ $$$$o o$$$" "$$$$o o$$$$$$o"$$$$o o$$$$ "$$$$$oo ""$$$$o$$$$$o o$$$$"" ""$$$$$oooo "$$$o$$$$$$$$$""" ""$$$$$$$oo $$$$$$$$$$ """"$$$$$$$$$$$ $$$$$$$$$$$$ $$$$$$$$$$" "$$$"""" Press ENTER or type command to continue
*1:v8.0.1362
VimConf 2017でコントリビューターになる話をしてきた
VimConf 2017 - An international Vim Conferenceで
という発表をしてきました。
最初は25分も話すことなんてあるかな?なんて思ってたら意外と良い感じの時間になりました。
少し足りなかったところを少し補足しておくと、
gitコマンド
- v8.0.0512の調査で使ったgit bisect
# <bad> <good>の順番で指定する git bisect start v8.0.0104 v8.0.0000
あとは都度makeし、手動で補完させてgoodかbadを判定していきました。
※コマンドからの補完だと再現しなかったため
活動時間帯について
- 不具合の発見〜1次調査・応急処置
- 職場
- 2次調査〜パッチ作成
- 自宅・Meguro.vim
patchを送った後について
- 質問とかツッコミは無かったの?
- 全く無かった
- パッチが小さかったからかも
- もしくはタイミングが良かったからとか
場合によってはなかなか取り込まれないこともあったりするようです。
MacVimの不具合って?
マルチバイト文字が潰れて表示されてしまうという問題を修正しました。 github.com
コントリビュートする内容について
- 新機能を追加したいんだけど
- 色々大変だと思いますが、新機能を作れるくらいのパッションがあれば大丈夫だと思います!
- ドキュメントや軽微な(typo・インデント)修正でも良いの?
- OKです!
ちなみにコントリビュートに対するスタンスは最近聴いた
にすごく共感するものがあります。
vim-jpについて
「vim-jpに気軽に相談してみましょう」なんて勝手に言ってしまって良いのかな?
とか思っていたんですが、なんと本日vim-jpのメンバー入りをしました。
もし何かあれば出来る範囲でサポートしていきたいです!
ということを本番でも話せれば良かったんですが、アドリブ力が無く。。。
という感じです。
スライドの英訳を助けてくれた ujihisa さん、
発表の翻訳をしてくださった sandkatt さん、
そしてスタッフのみなさま、
ありがとうございました!お疲れ様でした。
Meguro.vim #4でプラグインを作った
Meguro.vim #4で自分用の:VimFilerSimple
に代わるプラグインを作りました。
vimfilerは機能が豊富でそこまで不満があったわけではないのですが、
- 常に全ての機能を必要としているわけではない
- unite.vimに依存している
ので、一番使う頻度の高い ファイルをツリー形式で表示する だけのプラグインが欲しかったからです。
実装としてはtreeコマンドの結果を
- dirvish.vimのように
conceal
で表示し、 - ディレクトリの階層を
folding
で表す
というものです。
マッピングは一切用意していないため、ファイルを開く時には標準機能のgf
などを使うことになります。
また、プラグインの起動もTree
コマンドしか提供していないため、使い方に合わせたマッピングをvimrcに追加していくというデザインにしています。
" Example: " 垂直分割してツリーを表示し、ウィンドウの幅を32にする nnoremap <silent> <Leader>vt \ :<C-u>execute 'vertical '. v:count .'Tree' \ <Bar> vertical resize 32 \ <CR> " ツリーを閉じずにファイルを開く nnoremap <silent> <C-w>e \ :<C-u>let @a = fnameescape(expand('<cfile>')) \ <Bar> wincmd w \ <Bar> execute 'edit ' . @a \ <CR>
grpc-web-clientをjsで試してみた
gRPC-Web: Moving past REST+JSON towards type-safe Web APIs - Improbableを見て、grpcwebを使えばgoogle.golang.org/grpc製の既存gRPCサーバがブラウザからも叩けるようになるとのことなので試してみた。
サーバ側の変更点
DOC.mdにも書いてあるように
- grpc.Serverをgrpcweb.WrappedGrpcServerに変換して
- http.ServerからServeするようにする
の2点を行うだけ。
diff --git a/backend/main.go b/backend/main.go index 0f230c4..f261ab9 100644 --- a/backend/main.go +++ b/backend/main.go @@ -2,8 +2,9 @@ package main import ( "log" - "net" + "net/http" + "github.com/improbable-eng/grpc-web/go/grpcweb" "golang.org/x/net/context" "google.golang.org/grpc" "google.golang.org/grpc/credentials" @@ -30,11 +31,15 @@ func main() { gs := grpc.NewServer(opts...) pb.RegisterEchoServer(gs, &server{}) - l, err := net.Listen("tcp", addr) - if err != nil { - log.Fatal(err) + ws := grpcweb.WrapServer(gs) + + mux := http.NewServeMux() + mux.Handle("/", http.HandlerFunc(ws.ServeHttp)) + hs := &http.Server{ + Addr: addr, + Handler: mux, } - log.Println("Starting server on", l.Addr()) - log.Println(gs.Serve(l)) + log.Println("Starting server on", hs.Addr) + log.Println(hs.ListenAndServeTLS("./certs/cert.pem", "./certs/key.pem")) }
ブラウザ側の実装
サンプルはTypeScriptだけど、JavaScriptでも書けるみたいなのでES6でやってみた。
1. まずはprotocのプラグイン、ts-protoc-genをインストールする
npm install --save-dev ts-protoc-gen
これで以下のように.protoからJavaScriptの定義ファイルが生成できるようになる。
protoc --plugin=protoc-gen-js_service=./node_modules/.bin/protoc-gen-js_service \ --js_out=import_style=commonjs,binary:. \ --js_service_out=. \ pb/*.proto
js_outで生成されるのが.protoのmessage
、
js_service_outで生成されるのが.protoのservice
になっていた。
2. 次にクライアントライブラリのgrpc-web-clientをインストールする
npm install --save google-protobuf @types/google-protobuf grpc-web-client
使い方はgrpc-web-clientのinvoke()
に第1引数としてjs_service_outのrpc
、第2引数としてリクエスト、接続先、各種コールバック関数をオブジェクトで渡す形になる。
import {grpc} from "grpc-web-client"; import {Echo} from "../pb/echo_pb_service.js"; import {Request} from "../pb/echo_pb.js"; function EchoCall(value) { const req = new Request(); req.setValue(value); grpc.invoke(Echo.Call, { request: req, host: "https://localhost:9090", onMessage: (message) => { console.log("onMessage", message.toObject()); alert(message.getValue()); }, onEnd: (code, msg, trailers) => { console.log("onEnd", code, msg, trailers); } }); } // ちゃんと動けば引数の文字列がダイアログに出てくる EchoCall("Hello grpc-web-client");
リクエストやレスポンスなどの各フィールドには基本的にgetterとsetterを通してアクセスすることになるみたい。
既存クライアントへの影響
気になるのは既存クライアントがどうなるのかというところ。
grpc.DialOptionの認証情報の有無で見てみると次のような結果になった。
grpc.WithInsecure()
メソッド \ サーバオプション | 無し | grpc.Creds() |
---|---|---|
ListenAndServe() | X | X |
ListenAndServeTLS() | X | X |
grpc.WithTransportCredentials()
メソッド \ サーバオプション | 無し | grpc.Creds() |
---|---|---|
ListenAndServe() | X | X |
ListenAndServeTLS() | O | O |
つまりクライアントはgrpc.WithTransportCredentials()が必須になり、grpc.Serverのオプションに関わらずListenAndServeTLS()を使えば良いということになる。
(grpc.WithInsecure()を使っていたらクライアントを直すなりサーバを分けるなりしないといけない)
まとめ
サーバ側はけっこう簡単にブラウザ対応できるし、
grpc-gatewayと比べると
- proxyサーバ
- .protoのREST定義
が不要になるので管理するものが減って少し楽になりそう。
けど証明書が必要になるのはローカルでの動作確認とかがちょっと面倒になるかも…
まあフロントエンド的にはswaggerで生成するかprotocで生成するかの違いなので実際どっちでも良かったりするのかな?
今更だけどLGL22をIIJmioで使い始めた
LGL22はそこそこ古い機種で、もう3年くらい使っているけど
色々とあって未だにこれを使っている。
www.lg.com
理由の1つとしてIIJmioのタイプAを待っていたっていうのもあるんだけど、
タイプAはVoLTE対応機じゃないと使えないので非対応のLGL22はアウツ…
なので結局タイプDにすることにした。
流れとしては、
1. MNP予約番号の取得
0077-75470 に電話してMNP予約番号を発行してもらう。
電話で伝えてもらえるけど後からSMSも送られてくるのでメモとか無くても大丈夫。
2. SIMロックの解除
以下のサイトからアンロックコードを取得する。
https://sim-unlock.net/jp/simlock/LG/LGL22/
画面には*#06#
でIMEIを調べてくださいと書いてあったけど1桁足りないので、
設定 → 一般 → 端末情報 → ステータス
に表示されているIMEIを入力した。
たしか入力してから5分くらいでメールが送られてきたと思う。
送られてきたNCKは
2945#*22# -> ネットワークロック
から入力する。
3. IIJmioの申し込み
普通にWebから申し込む。
SIMカードのサイズはnanoSIMを選択。
4. 回線の切り替え
実際にSIMが届いたのは申し込みから3日後だった。
「IIJmioオンデマンド開通センター」に電話して回線を切り替える。
時間が19:00までなのに気づいたのが18:56だったけどなんとか間に合った。
その後は
3845*#22# -> KDDI Only -> Network Setting -> Network Mode Change
からLTE/CDMA/GSM/WCDMA
を選択して、
設定 -> テザリングとネットワーク -> モバイルネットワーク -> アクセスポイント名 -> 手動設定
からAPNを設定する。
いつ切り替わったか覚えていないけど10分後には使えるようになっていた。
電話の発着信も問題無し。
2週間ちょい使ってみて
Band1 Onlyなのが少し不安だったけど都内で生活する分には全然問題無かった。
クーポンOFFだと使い物にならない時もあるけど今の所そこまで困っていない。
エンジニアにジョブチェンジしてやってきたこと
「君のスキルはウチの新人と同レベルだけど、そんなんでやっていけるの?」と言われて今の会社に入社することを決めたのはもう4年半ほど前のこと。
前職はテスターでコードは全然書けなかったしデータベースとかも触ったことなかったけど好き勝手やらせてもらった結果、それなりのエンジニアに成長することができたと思う。
それも今月いっぱいで退職なのでちょうど良い機会だし自分じゃないとできなかった(やらなかった)ようなことを中心にざっくり振り返ってみる。
※イマイチだったことは全部書いていくとキリがないので省略
Perl(CGI + 生DBI)製Webシステムの改善
Template-toolkitをwrapするPerlモジュールを作った
- HTMLがscriptタグも含めて1行ずつ丁寧にprintされていたのでメンテナンスが辛かった
- SQLも文字列連結で組み立てられていたり、無理矢理1行に詰め込まれていたりしたのでメンテナンスが辛かった
- Perlのバージョンが古くて(たしか5.8)フレームワークの導入も難しい状況だったのでとりあえずTemplate Toolkitで凌ぐことにした
- Perlモジュールにしたのは各.cgiで統一的なコードが書けるようにしたかったから
DBアクセス用のPerlモジュールを作った
- DBIの初期化を各.cgiでやっていたのでPerlモジュールに切り出した
- よく使われていた処理もまとめてPerlモジュールに持っていった
- 引数でテストモードを指定すると
Test::mysqld
に繋がるようにした- テストコードが一切無かったのでテストを書く布石にしたかった
単体テストを書くようにした
- ↑の通り、テストが無かったので
Test::More
でテストを書くようにした- その前に.cgiは関数化されていなかったので関数化するところから始めた
strict
をつけると動かなくなるコードもたくさんあったので合わせて直していった
- DB関連のテストには↑のモジュールと
Test::Fixture::DBI
を使った
新しい言語・フレームワークの導入
Python(Flask + SQLAlchemy)
- Perlで各自が好き勝手にコードを書いているとメンテナンスが辛くなるというのがチームの共通認識だった
- 当時はコードレビューをするという文化も無かったし…
- チーム内でPythonに興味を持っている人が多かったので新規システムを作るタイミングで思い切って導入してみた
Django
やPyramid
を使わなかった理由としては、py.test
でテストし、pep8
とpyflakes
でコードのチェックをするようにした- 今まではテーブルの管理を一切していなかったので
alembic
やFlask-Migrate
で管理するようにした
Go
- ↑でPythonを導入したはいいけどほとんどがCentOS6上の2.6で動いているのでなんとかしたくなった
- ということで、ある時期から新規システムを作るときはGoを使うようにしてみた
- フレームワークは
- 標準のnet/http
- goji
- go-swagger
- grpc-go + grpc-gateway
- など
- 外部パッケージは
git subtree
でvendoringするようにした
ログ・監視
fluentdでログ収集とアラート検知をするようにした
- 今まではアプリケーションコード内でアラートの処理を行なっていた
- アラートの実装が漏れたり後回しにされることがあった
- アラート処理自体に問題があってシステムが落ちてしまうこともあった
- アプリケーション側は適切なログレベルでログを出力するだけにし、その後の処理は全てfluentdに任せるようにした
リポジトリ
Trac(svn)からGitHub Enterpriseに移行した
- 積極的に移行したいという人は自分しかいなかったけど反対意見はあまり気にせず移行することにした
- 移行後はPull Requestでコードレビューをする文化も少しずつだけど作っていった
drone.ioの導入
デプロイ
fabricの導入
- 当初は全てwikiなどに記載された手順書を頼りにコマンドを1つずつ打ち込んでいた
- 設定ファイルの変更は
vi /etc/my.cnf
のような手順になっていることもあった
- 設定ファイルの変更は
- 最初に担当したシステムで耐えられなくなったのでfabric + shell scriptでデプロイをするようにした
- ansibleを使わなかったのは間にPython2.5のホストがあって使えなかったから
- chefを使わなかったのは
- 対象ホストにインストールできないと思っていたから
- ローカルにあるファイルを対象ホストに転送する方法がわからなかったから
chef-soloの導入
- fabricだと共通処理も含めて毎回コピペして設定を作っていたので
Berkshelf
が使いたくなった - 実はホストの手配をした際にchefがインストールされているということを知った
- ファイル転送はfabricを使いつつ、ホストの設定にはchef-soloを使うようにした
自作デプロイツールの導入
- 最初の頃に比べたらデプロイがだいぶ楽にはなったけどいくつか問題が出てきた
- 踏み台ホストを経由するのでファイル転送に時間がかかる
- rootになれる人しか使えないので特定の人に負荷が集中してしまうことがあった
- 制限していたのはホストの状態を誰も把握できなくなってしまうと困るから
- そこで対象ホストで実行するタイプのツールを新たに作成し
- ファイル転送は対象ホストから直接gitで取得するようにした
- 合わせてビルド済みのバイナリやDockerのイメージもダウンロードする
- チームのメンバなら誰でも実行できるようにし、オペレーションを全自動で行うようにした
- 開発時はアプリケーションコードだけでなく、オペレーションも含めてレビューをする
- タグがついた最新のバージョンのみがデプロイされるようにしたので
- 同じバージョンは1回しかデプロイされないし巻き戻ることはない
- バージョンがわかればホストの状態も把握できる
- ファイル転送は対象ホストから直接gitで取得するようにした
こうしてみると改善系の活動を結構やってきたんだけど、それが偉い人にはあまり評価されなかったのは残念といえば残念。
というか2つ上の職位に要求されることが出来ていないからといって評価を下げられたのは正直根に持ってる。
あとは実際の新人に入社時の自分と同じレベルのことを求めたら何度か怒られてしまったことがあるのも今となっては良い思い出かな?