はじめに
Rinna から提供されている gpt モデルを使って遊んでみています。
- Rinna Gpt2 モデルを onnx データにした
-
IMEのリランカでGPTが使えるか試してみる
というところで、実装するのに mozc を読むことにしました。
mozc for Windows をビルドする
以下の環境で、ドキュメント通りの手続きで問題なくビルド終了しました。
- Windows 11 (10.0.22631)
- VS2022 Enterprise
TTD でトレースすると理解が深まる
仕事ではよくあることですが、既存の大きなコードを修正なり拡張しなければならないが、周りに概要すら教えてくれる人もなく、取っ掛かりすらつかめないことがあったりしますね。そういう場合、私はstaticにソースを読むのがとても苦手で、どちらかというとデバッガでトレースしながら必要な場所を読む方が何倍も早く理解できます。
そして、更に、それを手助けしてくれるのが、TTD (Time Travel Debugging) です。説明を引用すると「Time Travel Debugging は、プロセスの実行時にプロセスのトレースをキャプチャし、後で前後に再生できるツールです。」 これで知りたい挙動のプロセスのトレースをキャプチャしておき、あとから、デバッガでコードポインタを行ったり来たりさせて、知りたい場所の挙動を理解するのが、最近のお気に入りです。
詳細は、本家におまかせしますが、
g
: go, g-
: go abck, p
: step, p-
: step back, t
: trace-in, t-
: trace-back-in (?) など、一度見たかったところを通り過ぎたとしても、すぐに戻してやり直したり、後ろ向きにステップ捺せたり、まあパワフルにできます。
インストール
TTD のキャプチャデータを開くには windbg が必要なので、
- TTD https://aka.ms/ttd/downloadをターゲットVMに
- windbg を開発PCにインストールします。
アプリプロセスをキャプチャしてみる
ターゲットVMにて、
- notepad を起動して、プロセスIDを調べる
-
C:\Users\User\AppData\Local\Microsoft\WindowsApps\ttd -out c:\traces -attach {PID}
として、キャプチャを始める - IME ON/ローマ字入力/変換 など一連の動作を試して、トレースを終了
- c:\traces に
notepad01.run
というファイルをができるので、これをホストPCにコピーする - windbg に mozc のソースパスとシンボルパスを設定する (e.g. src:
D:\Git\mozc\src
, symbol:D:\Git\mozc\src\out_win\Release{,_x64,ReleaseDynamic_x64
) - windbg => Open trace file でキャプチャファイルを開く
TIP のキーイベントハンドラ関数 OnKeyDown にブレイクポイントを貼り (bm mozc_tip64!*OnKeyDown
)、その後トレースしていくと、message pipe の書き込みで終わります。
[0x3] mozc_tip64!mozc::`anonymous namespace'::SendIpcMessage+0x75 0xa51e9fe840 0x7166259c
[0x4] mozc_tip64!mozc::IPCClient::Call+0x5c 0xa51e9fe8d0 0x71580741
...
[0xa] mozc_tip64!mozc::win32::tsf::`anonymous namespace'::OnKey+0x8ee 0xa51e9fefe0 0x71529174
[0xb] mozc_tip64!mozc::win32::tsf::TipKeyeventHandler::OnKeyDown+0x23 0xa51e9ff500 0x7ffef9c4686b
[0xc] mozc_tip64!mozc::win32::tsf::`anonymous namespace'::TipTextServiceImpl::OnKeyDown+0x44 0xa51e9ff500 0x7ffef9c4686b
サーバープロセスをキャプチャしてみる
さて、そこで、再度 mozc_server.exe のプロセスIDから、プロセストレースをキャプチャして見ました
bm mozc_server!mozc::SessionHandler::EvalCommand
あたりがクライアントリクエスト最初のハンドラのようなので、そこにブレイクポイントを仕掛けて、変換キー「SPACE」の挙動を調べてみます。
[0x0] mozc_server!mozc::ImmutableConverterImpl::ConvertForRequest+0x16b 0x3398ff6c0 0x7ff652266875
[0x1] mozc_server!mozc::ConverterImpl::Convert+0xd1 0x3398ff770 0x7ff652121398
[0x2] mozc_server!mozc::ConverterImpl::StartConversionForRequest+0x1d5 0x3398ff770 0x7ff652121398
...
[0x8] mozc_server!mozc::SessionHandler::EvalCommand+0x163 0x3398ffa90 0x7ff6520f36c3
[0x9] mozc_server!mozc::SessionServer::Process+0xc3 0x3398ffb70 0x7ff652103dd5
[0xa] mozc_server!mozc::IPCServer::Loop+0x4a5 0x3398ffc00 0x7ff6520b6d2d
[0xb] mozc_server!mozc::server::MozcServer::Run+0x17d 0x3398ffda0 0x7ff6520b6189
[0xc] mozc_server!WinMainToMain+0xc5 0x3398ffe50 0x7ff6523013ee
[0xd] mozc_server!WinMain+0xf9 0x3398ffe50 0x7ff6523013ee
そいう感じで変換コードに届くようです。
そして変換コードを理解してみる
簡単に書くと、
-
ImmutableConverterImpl::MakeLattice
で lattice を作っている。このときはコスト計算はやってなさそう(ビームなど) -
ImmutableConverterImpl::Viterbi
で上記で作った lattice を評価している。Viterbi の Best Path がそのまま変換結果として使っているように見える - NBest リランキングなどしていない。(NBestGenerator というコードがあるが、次候補リスト用だと思われる)
なので、Viterbi が forward で累積スコアを保存しているので backward A* なぞ、書くことができるか思案中。
Mozc を応援するいくつかの方法
作業中、Mozc を応援するいくつかの方法 というリンクを見つけました。是非、皆さんやってみましょう。