バニキPの気ままに

気ままにやったこととか紹介していきます

bpftraceで2048作ってみた

はじめに

去年のアドベントカレンダーでeBPFについていろいろ調べた. その時に,ネコの人に「SystemTapで昔ゲームを作ったことがあるから,BPFでも作れるはずだから作ってみたら」と. ちょうどLT会のネタに困っていたこともあり,作るかとなったので,bpftraceで動作する2048を作りました.(SystenTapで動作する2048もあります)

動作環境

bpftraceの環境構築は公式に書いてあるinstallの手順に従って導入してください. Linux5.3以上である事が必須となっています. Linux5.2以前では,命令数制限でverifierでカーネルにロードされずエラーとなってしまいます. また,プログラム内でwhileを使用しており,whileもLinux5.3以上でないとサポートされてないため(確かこのバージョンで入ったはず...)動作しません. このあたりの環境構築をするのが面倒な人のためにアドベントカレンダーの時にAnsibleを書いてますので良かったら利用してもらえる楽になるはず...

動かし方

1.下記からソースをcloneなりして取得してください. https://github.com/shivashin/bpftrace-2048

2.実行

sudo bpftrace 2048.bt 

この時,sudoをつけて実行してください.(命令数制限が緩和されたのは特権ユーザのみであるため)

3.ゲームを楽しむ

f:id:shivashin495:20210325233237p:plain
bpftrace-2048

※実行時引数に「emacs」や「wasd」を指定すると操作キーが変わります

操作の方向は盤面の下に記載されているキーを押すと事で遊ぶ事ができます.

どうやって動いてるの?(工夫とか紹介)

操作の入力

キーボードドライバのイベント関数(kbd_event)にフックして入力を取得しようとしましたが,これだと本体に接続されたPS/2やUSBキーボードでないと動作しない. 仮想環境で作業していたこともあり,ssh環境でも動作するようkprobesを使ってpty_write関数にフックして入力を取得するようにしました.

盤面の表示

操作されるたびに盤面が更新されるので,perfのCPU clockのsoftware eventにプログラムをアタッチして,一定間隔で再描画しています.

盤面の値

値を常に保存しておく必要があるため,BPF mapに盤面の値を保存していて,再描画される時にBPFプログラム側からperf_event_outputでメッセージを出力して,メッセージを受け取ったbpftraceプログラムがANSIエスケープシーケンスを利用して描画してます.

最後に

今回はカーネルで動作する2048を作ってみました. 関数に分割するのができないため,辛いところでしたね...(何度も分割したいと思った) ループの書き方もフラグとかでループを抜け出すみたいな処理を書くとVerifierに弾かれて実行できなかったり,LOGサイズの上限に達してしまったりと多少の苦労はありました. これを,LT会で発表する時はこの使い方が本来の使い方でないことを説明しないと変な勘違いしそうだから,せめて言ってあげてと言われて,本来の使い方をしてないことをそこで思い出したw. 有名な人もWifi強度で音の変わる楽器のプログラムを書いたり,キーボードを叩くとタイプライターの音がするプログラムを書いたりしてるので,あながち違ってないのもかもしれないですねw. 良かったら,みなさんもbpftraceでゲームを作ってみてはいかがでしょうか?

参考にしたやつ

neocat.hatenablog.com