Pebble Timeを買って最初にやったこと
この記事はピョッコリンアドベントカレンダーのために書かれたものです。
はじめに
Pebble Timeを買ったので、最初にやった設定などについてまとめます。
Pebble Timeとは
最近はやりのスマートウォッチというやつです。 Apple Watchのようにスペックは高くないけれど、 電池は長持ちするし、カラー表示もできるし、 何よりお財布にやさしいという特徴を持っています。
レビューはいろんな人が書いているので今更書きません。
【Pebble Time 】買ってよかった!と感じたシーン3つtobalog.com
どうして買ったか?
値段そこそこで電池長持ち、かつかわいらしいデザインだったからです。 あとはシンプルなところかな。 普段心置きなく使えるようなものが欲しかったんです。
どこで買えるか?
日本のAmazonでももちろん買えるけど、公式サイトだと割引していたので 公式サイトで買いました。12/20現在でも$150で買えるようでした。 日本への送料は無料でしたよ、確か。
買って何をやったか
PebbleのアップデートとWatchfaceの入れ替え
何はなくとも、まずは手持ちのAndroidとつなげてアップデートします。 Play StoreにPebble Time用アプリがあるので、それを入れましょう。 最初に接続したときにアップデートは勝手に始まります、確か。 その他、Pebble Timeアプリの指示に従って必要なものを入れればOKです。 僕のときは何でかわからないけどもAndroid Wearアプリを入れろと指示がありました。
Pebble Time - Google Play の Android アプリ
アップデートが終わったらまずは時計としての体裁を整えるため、 Watchfaceを入れ替えます。壁紙を設定するようなもんですね。
ぐぐったらこんなのが出てきたので、載せておきます。 shikarunochi.matrix.jp
日本語化
AndroidはPebbleに通知画面(例えばメールが来たとかLINEが来たとか)を飛ばして くれるわけですが、Pebbleはデフォルトで日本語表示できないので、 有志のみなさまが作ってくださっている日本語化パックを入れます。
いろいろあるみたいですが、僕はこれを入れました。 Pebble language pack: 日本語言語パック
以上でやったことはおしまいです。
アプリを入れる
最近のアップデートで睡眠時間と歩数をデフォルトでトラックしてくれるようになったので、 他にアプリをいれなくても充分かな、という感じです。
おわりに
まだ使って4日くらいですが、結構楽しく使っています。 通知が飛んで来るだけで結構充分な感。
秒速でRAM Diskを作成する
この記事はピョッコリンアドベントカレンダーのために書かれたものです。
サマリ
LIOでループバックのRAM Diskを作成する。
はじめに
書くネタがだいぶ苦しくなってきたので、特に意味もなくRAM Diskの作り方について書きます。
そもそもRAM Diskとは何か
DRAM(要はメモリ)を一部確保して、HDDやSSDのごとく使えるようにしたものです。 SSDがいくら速いと言っても、メモリにはもちろん勝てないです。 最近のマシンはメモリを山ほど積んでるので、一部切り取って、 何かのキャッシュ置き場として使うといいのでは感があります。
Windowsだとなんかわけわからんドライバやら入れないといけないのでは、というところですが、 Linuxだとまあ文字通りあっという間にできます。
想定環境
- Linux Mint 17.1 (Ubuntu 14.04 LTSベース)
- Kernel 3.13.0-37-generic
必要なもの
LIOがあれば万事OK。 ビルドは結構大変なので以下のコマンドで入れます。
$ sudo apt install targetd --install-recommends
作り方
targetdをインストールすると、targetcliというある種のシェル的なものが使えるようになります。 targetcliを使うとRAM Diskを作ることができます。
targetcliを起動してみます。何かまあプロンプトが出ますね。
$ sudo targetcli targetcli GIT_VERSION (rtslib GIT_VERSION) Copyright (c) 2011-2013 by Datera, Inc. All rights reserved. />
lsをたたいてみましょう。いろいろ出てきましたね。 LIOはフロントエンド(ユーザがI/OするためのI/F。/dev/sdXとかそういうの)と、 backstores以下のバックエンド(ユーザのI/Oを実際に受けるところ)に分かれています。 今回は、フロントエンドにloopback(自分のサーバ内でのみ有効な/dev/sd何とかに見せる)、 バックエンドにbackstores/rd_mcp(要はRAM Disk)を選びます。
/> ls o- / ................................................................. [...] o- backstores ...................................................... [...] | o- fileio ........................................... [0 Storage Object] | o- iblock ........................................... [0 Storage Object] | o- pscsi ............................................ [0 Storage Object] | o- rd_dr ............................................ [0 Storage Object] | o- rd_mcp ........................................... [0 Storage Object] o- ib_srpt ................................................... [0 Targets] o- iscsi ..................................................... [0 Targets] o- loopback .................................................. [0 Targets] o- qla2xxx ................................................... [0 Targets] o- tcm_fc .................................................... [0 Targets] />
まずはバックエンド部分を作ります。ってもコマンド1発です。 今回は、ramdiskという名前の1GBのRAM Diskを作りました。
/> /backstores/rd_mcp create name=ramdisk size=1G Generating a wwn serial. Created rd_mcp ramdisk ramdisk with size 1G. /> ls o- / ................................................................. [...] o- backstores ...................................................... [...] | o- fileio ........................................... [0 Storage Object] | o- iblock ........................................... [0 Storage Object] | o- pscsi ............................................ [0 Storage Object] | o- rd_dr ............................................ [0 Storage Object] | o- rd_mcp ........................................... [1 Storage Object] | o- ramdisk ..................................... [ramdisk deactivated] o- ib_srpt ................................................... [0 Targets] o- iscsi ..................................................... [0 Targets] o- loopback .................................................. [0 Targets] o- qla2xxx ................................................... [0 Targets] o- tcm_fc .................................................... [0 Targets] />
バックエンドを作っただけではアクセスできないので、ramdiskのステータスは dactivatedになっています。では、フロントエンドを作りましょう。 こちらも同じくコマンド1発・・・とはいかなくて、2発叩き込む必要があります。 タブキーで補完が効くので、あまり面倒ではないです。作業自体はこれで完了です。
/> /loopback create Created target naa.60014053e8e7b899. /> /loopback/naa.60014053e8e7b899/luns create /backstores/rd_mcp/ramdisk Selected LUN 0. Successfully created LUN 0. /> ls o- / ................................................................. [...] o- backstores ...................................................... [...] | o- fileio ........................................... [0 Storage Object] | o- iblock ........................................... [0 Storage Object] | o- pscsi ............................................ [0 Storage Object] | o- rd_dr ............................................ [0 Storage Object] | o- rd_mcp ........................................... [1 Storage Object] | o- ramdisk ....................................... [ramdisk activated] o- ib_srpt ................................................... [0 Targets] o- iscsi ..................................................... [0 Targets] o- loopback ................................................... [1 Target] | o- naa.60014053e8e7b899 ......................... [naa.6001405ec6fd7aef] | o- luns ...................................................... [1 LUN] | o- lun0 ................................. [rd_mcp/ramdisk (ramdisk)] o- qla2xxx ................................................... [0 Targets] o- tcm_fc .................................................... [0 Targets] />
さて、lsscsiでチェックしてみましょう。僕の環境では/dev/sdcに見えていますね。 lsscsiがない人はaptで入れましょう。
$ lsscsi (略) [10:0:1:0] disk LIO-ORG RAMDISK-MCP 4.0 /dev/sdc
見えない場合は、以下コマンドでディスクをリスキャンしてみましょう。
#!/bin/bash for i in /sys/class/scsi_host/* do echo "- - -" > $i/scan done
さて、試しにinquiryを投げつけてみましょう。 inquiryとは、SCSIというDiskとやりとりするためのプロトコルで定義されているコマンドで、 Diskのベンダやら種類やらを知るためのものです。
何かいろいろ出てきましたが、 Product identificationがRAMDISK-MCPになっていて、確かにRAM Diskぽいということがわかりますね。 これでSSDより圧倒的に速いDiskを手に入れることができました。
$ sudo sg_inq /dev/sdc standard INQUIRY: PQual=0 Device_type=0 RMB=0 version=0x05 [SPC-3] [AERC=0] [TrmTsk=0] NormACA=0 HiSUP=0 Resp_data_format=2 SCCS=1 ACC=0 TPGS=3 3PC=1 Protect=0 [BQue=0] EncServ=0 MultiP=0 [MChngr=0] [ACKREQQ=0] Addr16=0 [RelAdr=0] WBus16=0 Sync=0 Linked=0 [TranDis=0] CmdQue=1 length=36 (0x24) Peripheral device type: disk Vendor identification: LIO-ORG Product identification: RAMDISK-MCP Product revision level: 4.0 Unit serial number: 7539e091-7407-4637-8edc-f8d4a531c92b
使う分にはあまり関係ないですが、このRAM Disk、 SATAではなくてSASディスクとして見えているようですね。
$ sudo sg_inq -i /dev/sdc VPD INQUIRY: Device Identification page Designation descriptor number 1, descriptor length: 20 designator_type: NAA, code_set: Binary associated with the addressed logical unit NAA 6, IEEE Company_id: 0x1405 Vendor Specific Identifier: 0x7539e0917 Vendor Specific Identifier Extension: 0x40746378edcf8d4a [0x60014057539e091740746378edcf8d4a] Designation descriptor number 2, descriptor length: 61 designator_type: T10 vendor identification, code_set: ASCII associated with the addressed logical unit vendor id: LIO-ORG vendor specific: RAMDISK-MCP:7539e091-7407-4637-8edc-f8d4a531c92b Designation descriptor number 3, descriptor length: 8 transport: Serial Attached SCSI Protocol (SPL-2) designator_type: Relative target port, code_set: Binary associated with the target port Relative target port: 0x1 Designation descriptor number 4, descriptor length: 8 transport: Serial Attached SCSI Protocol (SPL-2) designator_type: Target port group, code_set: Binary associated with the target port Target port group: 0x0 Designation descriptor number 5, descriptor length: 8 designator_type: Logical unit group, code_set: Binary associated with the addressed logical unit Logical unit group: 0x0 Designation descriptor number 6, descriptor length: 36 transport: Serial Attached SCSI Protocol (SPL-2) designator_type: SCSI name string, code_set: UTF-8 associated with the target port SCSI name string:
さいごに
LIOを使うとRAM Diskが簡単にできちゃうわけですが、 /dev/shm/以下もRAM Disk扱いになっているので、別に無理に作る必要はない。
Disk I/O性能測定するときに活躍する道具たち
この記事はピョッコリンアドベントカレンダーのために書かれたものです。
サマリ
Disk(HDD/SSD/仮想ディスク等)のI/O性能を測定するのに 活躍するツールについて、簡単に使い方を説明する。
はじめに
Diskに限定せずとも、性能測定と言えば、 おおまかには、スループット(Diskに関して言えば、1秒あたりどれくらいのI/Oを処理できるか?)と レイテンシ(Diskに関して言えば、1個のI/Oを処理するのにどれくらい時間がかかったか?) の2つについて見ることになる。 特にLinuxに関しては、このあたりをよく使うような気がする(主観)。
- fio:Diskに対してI/Oを発行してくれて、スループットやレイテンシを算出してくれるもの。
- iostat:Diskごとにスループットなどの情報をリアルタイムで見せてくれるもの
- blktrace:LinuxがDiskに対してどんなI/Oを出していたかの履歴(=トレース)を見せてくれるもの。
fioはスループットもレイテンシも算出してくれるので、基本的にはfioだけで事足りるのだけど、 本当にfioに指定したとおりにLinuxがI/Oを出しているのか、に関してはちゃんと確認する意義があるので、 iostatとblktraceを追加した。
以後、以下の環境を前提とするので、必要があれば適宜読み替えてほしい。
- Linux Mint 17.1(Ubuntu 14.04 LTSベースのディストリビューション)
- Kernel 3.13.0-37-generic x86_64
fio
fioのインストール
fioはメジャーなツールなので、こんな感じでaptでインストールできる。
$ sudo aptitude install fio
ソースからビルドしたい原理主義者は、作者様のgithubから落としてくると幸せになれる。 (ビルド前にlibaioを入れておくことを忘れなければ、さらに幸せになれる) github.com
手順としてはこんな感じ。
$ sudo aptitude install libaio1 libaio-dev $ git clone git@github.com:axboe/fio.git $ ./configure $ make $ sudo make install
使い方
コマンドとしてはこんな感じ。起動時に、どんなI/Oをかけるのかを記述した 設定ファイルを指定する。
$ fio -f script.fio --output output_filename # 場合によってはsudoが必要
fioはコマンド引数でもろもろ指定ができるんだけど、 後々の再利用を考えると、設定ファイルを逐一書いたほうがよいと思う。
設定ファイルの書き方
最小限ではないが、これだけ書けばI/Oをかけることができる。 細かいところはman fioを見るのがいちばん。 英語で書いてあるけど、割と簡単に読めると思う。
fioの設定ファイルは、全体で共通の[global]
以下の設定と、
個別の[job]
に分けられる。[global]
は予約語だけど、
[job]
に関しては好きな名前を指定できる。もちろん、[job]
は複数個書くことができる。
[global] ioengine=libaio # I/Oを発行するのにlibaioを使う。性能測るときは # とりあえずlibaioを指定しておけばよいと思う。 blocksize=4KB # 1回のI/Oの転送長 blockalign=4KB # I/Oのアラインメント。 # I/O先のアドレスが4KB刻みになる。 kb_base=1024 # K=1000かK=1024かわからなくなるので、 # 書いておくと親切。defaultは1024。 filesize=1-1GB # I/Oをかけるアドレス範囲の指定。 # この設定だと、各ファイルそれぞれについて、 # 1-1GBの範囲にI/Oをかける(1始まりなので注意)。 direct=1 # Disk I/O性能を測るので、Linuxのキャッシュを # 素通りするようにする。 ioscheduler=noop # Disk I/O性能を測るので、Linuxに何も考えず # I/Oしてもらうようにする。 [job] rw=randread # I/Oの種別。[global]で指定したアドレス範囲から # ランダムに選んでリードする。 filename=/dev/sdc:/dev/sdd:/dev/sde # どのファイルにI/Oをかけるかを指定。 # : で複数指定できる。 # 見ての通り、別にファイルでなくともよい。 ramp_time=30 # I/Oかけはじめの、性能測定はしない時間。 # I/Oかけはじめは安定しないこともあるので、 # 少し時間を置くほうがよいこともある。 runtime=60 # I/Oをかけて、性能測定をする時間。 time_based=1 # filesize分I/Oかけても、runtimeが経過するまで # I/Oをかけ続ける。 numjobs=6 # いくつのプロセスでI/Oするかを指定。 # thread=1とすると、forkではなく # pthreadを使ってくれる。 group_reporting=1 # プロセスごとに結果を報告するのではなく、 # ジョブごとにまとめて報告する。 iodepth=1 # I/Oの多重度。概して多重で出したほうが性能が上がる。
得られる結果
上記をtest.fioとして保存して、RAM Disk相手にfioを実行してみる。 I/O中はこんな感じで表示される。
$ sudo fio -f test.fio --output test.result Jobs: 6 (f=18): [r(6)] [60.0% done] [1137MB/0KB/0KB /s] [291K/0/0 iops] [eta 00m:36s]
test.resultの中身はこんな感じ。 注意すべきはレイテンシがslat、clat、latの3つあること。 それぞれは以下のとおり。
- slat:fioがI/O発行して、Linuxがコマンド発行までにかかっている時間。
- clat:コマンド発行から応答を受け取るまでにかかった時間(単にディスクのレイテンシを見るだけならここだけ見ればよいかも)。
- lat:全体。
read_test: (g=0): rw=randread, bs=4K-4K/4K-4K/4K-4K, ioengine=libaio, iodepth=1 (略) read_test: (groupid=0, jobs=6): err= 0: pid=8858: Sun Dec 13 22:27:14 2015 read : io=67192MB, bw=1119.9MB/s, iops=286680, runt= 60001msec slat (usec): min=4, max=11476, avg=16.24, stdev=13.23 clat (usec): min=0, max=10280, avg= 2.26, stdev= 8.96 lat (usec): min=11, max=10293, avg=18.94, stdev=14.98 clat percentiles (usec): | 1.00th=[ 0], 5.00th=[ 0], 10.00th=[ 0], 20.00th=[ 1], | 30.00th=[ 1], 40.00th=[ 1], 50.00th=[ 1], 60.00th=[ 1], | 70.00th=[ 1], 80.00th=[ 1], 90.00th=[ 12], 95.00th=[ 13], | 99.00th=[ 15], 99.50th=[ 15], 99.90th=[ 18], 99.95th=[ 20], | 99.99th=[ 47] bw (KB /s): min= 0, max=228328, per=16.54%, avg=189615.16, stdev=19299.85 lat (usec) : 2=88.03%, 4=0.33%, 10=0.02%, 20=11.56%, 50=0.05% lat (usec) : 100=0.01%, 250=0.01%, 500=0.01%, 750=0.01%, 1000=0.01% lat (msec) : 2=0.01%, 4=0.01%, 10=0.01%, 20=0.01% cpu : usr=12.58%, sys=37.56%, ctx=17215603, majf=0, minf=8 IO depths : 1=150.9%, 2=0.0%, 4=0.0%, 8=0.0%, 16=0.0%, 32=0.0%, >=64=0.0% submit : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0% complete : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0% issued : total=r=17201111/w=0/d=0, short=r=0/w=0/d=0, drop=r=0/w=0/d=0 latency : target=0, window=0, percentile=100.00%, depth=1 Run status group 0 (all jobs): READ: io=67192MB, aggrb=1119.9MB/s, minb=1119.9MB/s, maxb=1119.9MB/s, mint=60001msec, maxt=60001msec Disk stats (read/write): sdc: ios=8650516/0, merge=0/0, ticks=102172/0, in_queue=103080, util=61.34% sdd: ios=8650522/0, merge=0/0, ticks=104708/0, in_queue=103800, util=62.18% sde: ios=8650522/0, merge=0/0, ticks=101136/0, in_queue=101780, util=60.36%
iostat
fioはおおよそのスループットを表示してくれるわけだけど、いかんせんざっくりすぎるので、 iostatを使って、もう少し細かく見てみる。
iostatの入手
何も考えずにaptでinstallする。
$ sudo aptitude install sysstat
iostatの使い方
1秒間隔で表示させておけば困ることはないはず。
$ iostat -x 1 sdc sdd sde
得られる結果
こんな感じで毎秒表示してくれる。 よく使う前半部分に関しては、こんな感じ。
項目 | 意味 |
---|---|
rrqm/s | 1秒あたりにマージしたリードリクエストの回数 |
wrqm/s | 1秒あたりにマージしたライトリクエストの回数 |
r/s | 1秒あたりのリード回数 |
w/s | 1秒あたりのライト回数 |
rkB/s | 1秒あたりにリードしたデータ量 |
wkB/s | 1秒あたりのライトしたデータ量 |
%util | I/OリクエストにCPU時間をどれくらい使ったか?(100%=CPUが全力でI/Oを発行しまくっている) |
avg-cpu: %user %nice %system %iowait %steal %idle 12.31 0.00 64.45 0.00 0.00 23.24 Device: rrqm/s wrqm/s r/s w/s rkB/s wkB/s avgrq-sz avgqu-sz await r_await w_await svctm %util sdc 0.00 0.00 96948.00 0.00 387796.00 0.00 8.00 1.08 0.01 0.01 0.00 0.01 60.00 sdd 0.00 0.00 96949.00 0.00 387796.00 0.00 8.00 1.15 0.01 0.01 0.00 0.01 64.40 sde 0.00 0.00 96949.00 0.00 387796.00 0.00 8.00 1.08 0.01 0.01 0.00 0.01 57.60
blktrace
測定をしたはよいが、芳しくない結果が得られたときに、一体何が起こっていたのかを 確認するためのトレース採取ツール。困ったときに覗くとよいと思う。
blktraceの入手
考える前にまずはaptを叩く。
$ sudo aptitude install blktrace
blktraceの使い方
I/O中に、デバイスを指定してトレースを取得する。 トレースはコア数分できる。
$ sudo blktrace -d /dev/sdc # Ctrl-Cでトレース採取終了 $ ls sdc.blktrace.0 sdc.blktrace.2 sdc.blktrace.4 sdc.blktrace.6 sdc.blktrace.1 sdc.blktrace.3 sdc.blktrace.5 sdc.blktrace.7
得られる結果
得られたファイルはバイナリなので、blktraceとセットになっているblkparseというコマンドを使って、 読みやすい形に整形する。
$ blkparse -i sdc.blktrace.0 > sdc.blkparse.0
sdc.blkparse.0を覗いてみると、左から順に以下のようになっている(デフォルト設定なのでもちろん変えられる)。
- デバイスのmajor番号, minor番号
- CPUコア番号
- シーケンス番号
- トレース採取時からの経過時間(秒)
- I/Oしていたプロセスのpid
- コマンド状況を表すアルファベット(詳細はmanを参照のこと)
- 処理を表すアルファベット(詳細はmanを参照のこと)
- I/Oの先頭アドレス+オフセット
- プロセス名
8,32 0 1 0.000000000 9718 G R 1839944 + 8 [fio] 8,32 0 2 0.000000383 9718 P N [fio] 8,32 4 1 0.000000413 9717 G R 1831472 + 8 [fio] 8,32 4 2 0.000000643 9717 P N [fio] 8,32 0 3 0.000001059 9718 I R 1839944 + 8 [fio] 8,32 0 4 0.000001355 9718 U N [fio] 1
CPUコア0だけ取り出したのが以下。 多重でI/Oかけていると、トレースが混迷を極めそうだ・・・
8,32 0 1 0.000000000 9718 G R 1839944 + 8 [fio] → fioがI/Oの先頭アドレスを算出した 8,32 0 2 0.000000383 9718 P N [fio] → fioがカーネルに対してリードリクエストを発行するために コマンドをためておくキューに接続 8,32 0 3 0.000001059 9718 I R 1839944 + 8 [fio] → fioのリードリクエストがコマンドをためておくキューに、 コマンドを追加 8,32 0 4 0.000001355 9718 U N [fio] 1 → fioのリードリクエストがコマンドをためておくキューとの接続を解除 8,32 0 5 0.000001688 9718 D R 1839944 + 8 [fio] → コマンド発行 8,32 0 6 0.000014064 3 C R 1839944 + 8 [0] → 応答が返ってきた
さいごに
以上、Disk I/Oの性能測定をするときに活躍する道具たちとその使い方でした。 使い方とか説明が間違っていたらごめんね!
the platinum searcher(pt)をビルドして使ってみよう
※この記事はピョッコリンアドベントカレンダーのために書かれたものです。
サマリ
Golang初心者だけどthe platinum searcherをビルドして使ってみた。ついでにpull requestもしてみた。
the platinum searcherとは何か
ひとことで言うと、プログラマ向けの速いgrep。 作者様のblogはこちら。
世の中には改良版grepとして、以下のものがあるみたい。
- ack:プログラマ向けの便利なgrep
- the silver searcher(ag):プログラマ向けの高速grep
- the platinum searcher(pt):プログラマ向けの高速grep
ptとagの違いは、EUC-JPやShift_JISに対応しているか否か。 (agはUTF-8しか対応していなくて、EUC-JPやShift_JISのファイルが スキップされてしまうという罠がある)
the platinum searcherのビルドとインストール
README.mdにしたがっていけばOK。
$ go get -u github.com/monochromegane/the_platinum_searcher $ go get -u github.com/jessevdk/go-flags # なぜか入らなかったので別立て。普通はいらないはず。 $ go get -u github.com/monochromegane/conflag # なぜか入らなかったので別立て。普通はいらないはず。 $ go get -u github.com/monochromegane/terminal # なぜか入らなかったので別立て。普通はいらないはず。 $ cd $GOPATH/src/github.com/monochromegane/the_platinum_searcher $ go build -o $GOPATH/bin/pt cmd/pt/main.go
おまけ:pull requestを投げてみた
pull requestした背景
githubを使ってはいるが、恥ずかしながら1度もpull requestというものを投げたことがなかったので、 pull requestを投げてみたかったんです・・・ というわけで、A Tour of Goをひととおりやった程度の僕でもなんとかできそうなissueを選んでpull requestを投げてみた。
以下のissueは、.gitconfigにexcludesfileが指定されていないときに、グローバルなgitignoreファイルを 読んでくれないよ、っていうもの。
問題箇所の特定
おおよそこんな感じだったと思う。gdbで実行するまでもなかったかもしれないけど、 gdbを使ってみたかったのもあって、gdbでステップ実行しながら特定した。
gdbで追いかけられるように、まずはptをビルドし直す:
go build -v -gcflags "-N -l" -o pt_debug cmd/pt/main.go
ptなりgrepなりで"gitignore"をソースのどこで使っているか探す
ソースコードでは、option.goとoption_test.goとignore.goがひっかかるので、中を覗く。ignore.goだけが関係あることがわかる。
gdbでptを起動する。入り口と思しきignore.go内のglobalGitIgnore関数にbreakpointを作成する。"b globalGitIgnore"と打つと、gdbはそんな関数しらねとのたまうので、"b ignore.go:88"とファイルかつ行数指定でbreakpointを作成する。
gdbでステップ実行していくと、globalGitIgnoreName関数内で、gitconfig内にexcludesfilesが定義されていないときに空文字列が返されることがわかるので、その部分を修正すればよいことがわかる。
追記
何か中途半端だったし、元のソースコードとコンフリクトしてたしでプルリクを引っ込めた。
プログラミング言語pyoko
※この記事はピョッコリンアドベントカレンダーのために書かれたものです
はじめに
みんなのアイドルピョッコリンさんのために、 プログラミング言語pyoko(以後、単にpyokoと呼びます)を開発しました!!
pyokoとは
pyokoは、言語のシンプルさとピョコ感を出すことを念頭に置いて設計された言語です。 字面の似ているpythonと同等の表現力を持ちながらも、圧倒的可読性・記述性を達成しました。 また、ピョコ感を演出することうけあいでしょう。
サンプルコード
お決まりのHello, Worldをみてみましょう。 ね、圧倒的可読性・記述性とピョコ感が伝わるでしょう?
ピョコピョコピョコピョコピョコピョコピョコピョコピョコピョリコピョッコ ピョコピョコピョコピョコピョコピョコピョコピョコピョッコピョコピョコ ピョコピョコピョコピョコピョコピョコピョコピョコピョコピョッコピョコ ピョコピョコピョコピョコピョ?!ピョ?!ピョ?!ピョコリピョコ-ピョッコ ピョッコリンピョッコピョコピョコピョッコリンピョコピョコピョコピョコピョコ ピョコピョコピョッコリンピョッコリンピョコピョコピョコピョッコリンピョッコ ピョコリピョッコリンピョコリピョコリピョコリピョコリピョコリピョコリピョコリ ピョコリピョコリピョコリピョコリピョコリピョッコリンピョ?!ピョコピョコピョコ ピョコピョコピョコピョコピョコピョッコリンピョコリピョコリピョコリピョコリ ピョコリピョコリピョコリピョコリピョッコリンピョコピョコピョコピョッコリンピョコリ ピョコリピョコリピョコリピョコリピョコリピョッコリンピョコリピョコリピョコリピョコリ ピョコリピョコリピョコリピョコリピョッコリンピョッコピョコピョッコリン
上記テキストをhello.pyokoとして保存し、インタプリタを実行します。
$ pyoko hello.pyoko Hello, world!
ね、Hello, worldでしょう?
ソースコード
#!/usr/bin/env python #fileencoding: utf-8 from __future__ import print_function import sys import codecs class pyokolang(): plus = "+" minus = "-" gt = ">" lt = "<" lb = "[" rb = "]" comma = "," period = "." def __init__(self,filename): with codecs.open(filename, "r", "utf-8") as f: self.raw_code = ("".join(f.readlines())).strip() self.iptr = 0 self.dptr = 0 self.data = [0]*100 self.loopmap = {} self.insts = [] self.__parser() def __parser(self): self.__tokenizer() def __get_token(self, pos): is_same = lambda raw_code, pos, x: raw_code[pos:pos+len(x)] == x quasi_plus = u"ピョコ" quasi_minus = u"ピョコリ" quasi_gt = u"ピョッコ" quasi_lt = u"ピョ?!" quasi_lb = u"ピョリコ" quasi_rb = u"ピョコ-" quasi_comma = u"ピョコ?!" quasi_period = u"ピョッコリン" if is_same(self.raw_code, pos, quasi_minus): return quasi_minus, pyokolang.minus elif is_same(self.raw_code, pos, quasi_period): return quasi_period, pyokolang.period elif is_same(self.raw_code, pos, quasi_gt): return quasi_gt, pyokolang.gt elif is_same(self.raw_code, pos, quasi_lt): return quasi_lt, pyokolang.lt elif is_same(self.raw_code, pos, quasi_lb): return quasi_lb, pyokolang.lb elif is_same(self.raw_code, pos, quasi_rb): return quasi_rb, pyokolang.rb elif is_same(self.raw_code, pos, quasi_plus): return quasi_plus, pyokolang.plus elif self.raw_code[pos:pos+len(quasi_comma)] == quasi_comma: return quasi_comma, pyokolang.comma else: return False, False def __tokenizer(self): pos = 0 while pos < len(self.raw_code): token, symbol = self.__get_token(pos) if token is False: pos += 1 continue self.insts.append(symbol) pos += len(token) def __gen_loopmap(self): stack = [] for i, val in enumerate(self.insts): if val == pyokolang.lb: stack.append(i) elif val == pyokolang.rb: if len(stack) == 0: print("parse error: right brackets exists more than left brackets") sys.exit() lb_index = stack.pop() self.loopmap[i] = lb_index self.loopmap[lb_index] = i if len(stack) != 0: print("parse error: left brackets exists more than right brackets") sys.exit() def executor(self): self.__gen_loopmap() while self.iptr < len(self.insts): if self.insts[self.iptr] == pyokolang.gt: # > self.dptr += 1 if self.dptr >= len(self.data): for i in range(len(self.data)): self.data.append(0) elif self.insts[self.iptr] == pyokolang.lt: # < self.dptr -= 1 if self.dptr < 0: print("data pointer underflow", self.iptr, self.insts, self.dptr, self.data) elif self.insts[self.iptr] == pyokolang.plus: # + self.data[self.dptr] += 1 elif self.insts[self.iptr] == pyokolang.minus: # - self.data[self.dptr] -= 1 elif self.insts[self.iptr] == pyokolang.period: # . print(chr(self.data[self.dptr]), end="") elif self.insts[self.iptr] == pyokolang.comma: # , self.data[self.dptr] = sys.stdin.read(1) elif self.insts[self.iptr] == pyokolang.lb: # [ if self.data[self.dptr] == 0: self.iptr = self.loopmap[self.iptr] elif self.insts[self.iptr] == pyokolang.rb: # ] if self.data[self.dptr] != 0: self.iptr = self.loopmap[self.iptr] self.iptr += 1 def run(self): self.executor() def usage(): print("usage: python pyokolang.py <filename>") sys.exit() def main(): if len(sys.argv) != 2: usage() bp = pyokolang(sys.argv[1]) bp.run() print() if __name__ == "__main__": main()
実装に関して
ここまで来ればお気づきの方もいるかもしれませんね。 そう、pyokoは難読で有名なプログラミング言語brainf*ckを ベースに作られています。 実装方針は単純で、brainf*ckを構成する8個の実行命令(+/-/>/</[/]/,/.)を ピョコ感ある単語に置き換えるだけです。
処理の流れも単純で、以下の2ステップです。
ピョコ感ある単語をbrainf*ckの8個の実行命令に置き換え(__tokenizer)
brainf*ckインタプリタで実行(executor)
感想や反省など
- 感想:意外に簡単にできた。
- 反省:もっとhello.pyokoを複雑な文章にすべきだった。少々面白みに欠ける。
おまけ
インタプリタのソースコードはここにあります。 github.com
easy_perfmonをjavascriptで書きなおした
シルバーウィークに作ったおもちゃをjavascriptで書きなおしました、というお話。
サマリ
こんな感じになった。
Smoothie ChartからFlotに移行し、bottle.pyからexpressに移行した。 Flotはこまごましたところまでいじれるので楽しい。
ソースコードはこちら。 github.com
使ったツール
bottleには負けるがexpressも結構簡単に使えたと思う。 jadeの素晴らしさには感動した。あの面倒なhtmlファイルが簡単に書けちゃう。
以前 | 今 | |
---|---|---|
webフレームワーク | bottle | express |
テンプレートエンジン | なし | jade、stylus |
グラフ | Smoothie Charts | Flot |
使い方
app.jsを起動するだけ。ポート3000番でお待ちしております。
$ node app.js # => 3000番で待ち受け
はまったところ
Flotの凡例(legend)出すのにはまった。 最初にplot.pushするときには、ちゃんとラベルを指定していたんだけど、 アップデートのときにラベル指定が抜けてたせいで凡例が描画されなかった。 当たり前っちゃ当たり前なような気もするけど、全然気づかなかったなあ・・・
これが正しい例。
// 最初にpush plots.push($.plot(placeholders[0], [{label:"usr",data:usr},{label:"sys",data:sys},{label:"idle", data:idle}], options[0])); /* 略 */ // データ更新時に描画 plots[0].setData([{label:"usr",data:usr}, {label:"sys",data:sys}, {label:"idle",data:idle}]);
こっちが間違いの例。
// 最初にpush plots.push($.plot(placeholders[0], [{label:"usr",data:usr},{label:"sys",data:sys},{label:"idle", data:idle}], options[0])); /* 略 */ // データ更新時に描画 => labelがnullになるせいで、描画されない。 plots[0].setData([usr, sys, idle]);
Flot本体のコードを覗いてみると一目瞭然であった。さっさと読めという話ですね。
/* jquery.flot.js */ /* 略 */ function insertLegend() { /* 略 */ // Build a list of legend entries, with each having a label and a color for (var i = 0; i < series.length; ++i) { s = series[i]; // seriesには、setDataメソッドに渡したオブジェクトとグラフのoptionが入っている。 if (s.label) { // labelをつけてないと、このネストに入れない。 label = lf ? lf(s.label, s) : s.label; if (label) { entries.push({ label: label, color: s.color }); } } } /* 略 */ }
Smoothie ChartsとBottleを使ったリアルタイムパフォーマンスモニタ
今週はシルバーウィーク。 しばらく楽しくプログラムを書いてなかったし、 せっかくの長い休みなので、単にイカで遊び呆けるだけじゃなくて、 ちょっとしたものを工作してみようと思い立った。
サマリ
Smoothie ChartsとBottleを使って、こんな感じのものができた。
ソースコードはここ。READMEはまだ。。。 github.com
使い方
以下の通り立ち上げて、ブラウザでアクセスすれば上記画像のような画面が現れる(はず)。 mpstat、iostat、vmstat、sarを使うので、sysstatをaptとかで入れておく。
$ sudo aptitude install sysstat $ git clone https://github.com/bisco/easy_perfmon.git $ cd easy_perfmon ## 引数なしだとPort 8080をListenして立ち上がる。引数あげるとそのPort番号をListenして立ち上がる。 $ python easy_perfmon.py
経緯
Linuxサーバの負荷状況を見るツールはいろいろあるわけだけど、 CUIなのでいかんせん味気ない。グラフとして見えたほうがいいよね、ということで、 Linuxサーバの負荷状況を可視化することにした。
単に可視化するだけじゃなく、どうせならリアルタイムに負荷を見たい。 ということで、リアルタイムに負荷状況が見えるツールを作ることにした。
使ったライブラリ
Smoothie Charts
お手軽に可視化するにはブラウザ+Javascriptの組み合わせが一番(だと思う)。 なので、Javascriptで簡単にリアルタイムなグラフが描けるSmoothie Chartsを選んだ。 Smoothie Chartsは大変シンプルで、設定できる項目もあまりないんだけど、 その分簡単に使えるのがよいところ。 本家サイトに行けば、コードジェネレータ(!)があるので、 それを使いながら見た目の調整ができる。何とも楽ちん。
Bottle
BottleはPythonで書かれたweb frameworkの1つで、 非常にシンプルで簡単に使えるのが特徴。
今回は、主にSmoothie Chartsにデータを渡すために使用。 mpstatやiostatなどの結果をJSONに変換して、httpでアクセスできるようにした。
bootstrap
おなじみbootstrap。見た目調整のために使用。
プログラムのおおよその構造
JavascriptでBottleから定期的にJSONを取得し、Smoothie Chartsに渡しているだけ。 定期取得の部分はjQueryのAJAX関数を使って実装した。
はまったところ
AJAXで同期的に情報を取ろうとしてはまった。 AJAXは基本非同期動作なので、同期的に情報を取るときは、明示的に同期動作にしてやらないといけない。
$.ajaxSetup({ async: false }); $.getJSON(); $.ajaxSetup({ async: true });
まとめと感想
Smoothie ChartsとBottleでお手軽にLinuxの負荷状況を可視化するツールを作った。 グラフィカルに見えるものを作るのは大変でもあり、楽しくもあり、って感じでした。