はじめに
🕒 この記事を読むのに必要な時間 🕒
全文: おおよそ30分
要約のみ: 3分 😃お薦め!
📜 必要な知識 📜
Linuxコマンドライン操作: 初級以上
Linuxに関する知識: 初級以上
C言語に関する知識: 中級以上 ※全文の場合のみ必要
💻 実行環境 💻
OS: Arch Linux x86_64
Kernel: 6.13.6-arch1-1
Shell: bash 5.2.37
DE: Xfce 4.20
WM: Xfwm4
Terminal: aur/mlterm-3.9.3-2, aur/mlterm-git-r3130.0e58dd2b-2
Package: libx11-1.8.12-1, fcitx5-5.1.12-1, htop-3.4.1-1, iotop-0.6-12, aur/yay-12.5.0-1
■ 問題の概要
● mlterm とは?
mlterm は元祖・多言語対応の UNIX互換OS用ターミナルエミュレーター。日本人が主導して開発しており、CJK と略される 中国語・日本語・韓国語 に完全に対応し、アラビア文字のような逆方向から文字列を綴る書き方や、和文の縦書きにも対応したニッチな端末だ。筆者が知る仮想端末の中では、最も日本語にしっかり対応した端末と言える。筆者が現在メインで使用しているのは xfce4-terminal だが、これが正式対応できていない Sixel にも対応していて、昔から非常に重宝している。
公式HPは以下。
● 急激に動作が遅くなる
ただ、最近この mlterm をしばらく使っていると、CPU 温度が急上昇し、ファンが周り始めるという症状に悩まされていた。何か重いプログラムを走らせている訳ではない。何もしていなくても突然、高負荷になるのである。
この時、 mlterm は入力不能でフリーズ状態になる。しばらくそのまま他のウィンドウなどで作業しつつ待っていると、フリーズも重いのも解消される場合がある。…気が短い俺ちゃんは、大抵はその前に根負けしてプロセスを kill してしまうのだが。
トリガーとしては、どうも他のアプリケーションにフォーカスを移して、しばらく作業し、戻ってきたときになることが多いようだった。CTRL+Tab キーなどで再びフォーカスを別のアプリケーションにすると、高負荷状態は一旦収まるが、フォーカスを戻すと再び高負荷になる。
俺ちゃんは最近は mlterm 自体をあまり使わなくなっていたのでなんとも言えないが、 2023 〜 2024 初期くらいの時点では出てはいなかった気もする。…単に気づいていなかっただけかもしれないが。
■ TL;DR 先に結論の要約を述べておく
原因は日本語入力メソッドに Fcitx5 を使っているのに、 mlterm の設定で XIM を入力メソッドにしてしまっていたせいだった。fcitx に変えれば解決した。
mlterm の画面を右クリックして設定ダイアログを出す。そこで [エンコーディング] タブの [入力メソッド] を XIM から fcitx に変更すれば問題は解消する。
…が、
Arch Linux の AUR からインストールしたものや、ソースコードからビルドしたものを使っている場合は 設定ダイアログ には上記の項目自体が表示されないようだ。そこで手動で設定ファイルを書き換えて対応する。
以下のようにする。
input_method=fcitx
input_method=XIM を input_method=fcitx に書き換えるだけ。
これで mlterm をすべて終了して、再起動させれば問題なく動作するようになる。
以下は経緯を含めた詳細ログ。長い。読まなくていい。
■ 高負荷は何が原因?プロセスの状態は?
高負荷の大きな理由はだいたいストレージへのアクセスであることが多い。そこで最初 iotop コマンドで 調べてみたのだが結果は、ゼロかほとんど I/O なし。つまりストレージへのアクセスが原因ではない。
では CPU リソースか?
htop コマンドを使って分析すると、 Xorg がやたらと CPU を消費していた。 60% 〜 90% 前後。平均で 70% くらい?
mlterm も 20% 前後使っている。
気になるのは xfwm4, fcitx5 も 50% 前後の高負荷になっている点だ。
通常はこんな高いことはない。ひと桁 % がいいところだ。
特に日本語入力をしているわけではないのに、fcitx5 がこんなに高いのはおかしい。
■ エラーは何か出ているの?
最初に dmesg や journalctl を見たが、ぜんぜんログは出ていなかった。
そこで xfce4-terminal から mlterm を実行してみた。
すると重くなったときに、しばらくしてから以下のようなエラーが出ていた。
$ mlterm
imDefLkup.c,419: The application disposed a key event with 12847 serial.
$
日本語に直訳すると、
「アプリケーションは、12847 シリアルを備えた重要なキーイベントを破棄しました」
シリアルのくだりがよくわからないが…
キーイベントが処理されずに破棄されたようなのは分かる。
imDefLkup.c は libx11 の IM に関するファイルのようだ。
この時点で Arch Linux でのパッケージは ver.1.8.12 で、ソースコードは以下の箇所になっていた。
_XimIsFabricatedSerial() 関数の中で起きている。
どうもこの関数だけ見てもよくわからない。そこで、このエラーメッセージで検索してみると、以下がヒットした。1
【参考】Re: mlterm のキー入力
以下関連部分を抜粋する。
`バッファに溜まっていたキー入力が吐き出される時に、
imDefLkup.c,359: Tried to fabricate a wrong key event.
や
imDefLkup.c,419: The application disposed a key event with 695 serial.
といったメッセージが、 mlterm を起動したターミナルに吐き出されているのに
気づきました。やはり青木さんのおっしゃるとおり XIM がらみの問題らしい
ということで、mlterm を FCITX オプションつきで build し、
mlterm 設定ウインドウ(mlterm ウインドウ上で ctrl + mouse 3)から、input
method を XIM -> FCITX に変更した所、おかしな挙動もぴたりと収まり、以
前と同様にマウスカーソルがタイトルバーやフレームにあってもキー入力がで
きるようになりました。
`
この方は FreeBSD のユーザーでポストしているのもそのメーリングリストのようだ。ウィンドウマネージャーも FVWM3 だし、問題もマウスカーソルが mlterm のタイトルバーやフレーム上にあったときに入力がうまくできないといったものなので、ちょっと違う。
ただし、 X11 および fcitx を使っている点では一緒だし、先の htop コマンドの結果を省みるに、それらが同じ原因から起きているのではないかと俺ちゃんに思わせた。
試しに、 mlterm を起動してタイトルバーにマウスポインタを合わせてから何か入力してみると… 確かに、入力されずに高負荷状態が再現された!!
この方の言うとおりである。
俺ちゃんは基本、キーボードにしか触らない。稀にしか使わない GUI の Webブラウザでも使わない限りマウスポインタなんか気にしたこともないので、気づかなかった…。こんな動作が見られるということは、どうやら mlterm はポインタがどこにあるかで、入力がどこに行われるかを決めているのかな??
この方が解決した方法を確かめるには、同じように mlterm を FCITX オプション付きでビルドし直す必要がある…。と思ったが、調べてみると どうやら FCITX オプションは有効になっているようだ。
yay を使って AUR からインストールしたときに保存しておいたログを見てみると、次のようになっている。
◆ configure の出力から抜粋
checking for FCITX... yes
checking for FCITXUTILS... yes
-- (snip) --
Input Methods : XIM kbd m17nlib fcitx5 skk
configure が実行されたときに fcitx のライブラリがあれば自動的に yes になるらしかった。
他に怪しい点がないか見てみたが、なさそうだった。
FCITX は有効になっていてビルドも問題なく実行されている。
…しかし、解決していない。
この人が言っていた mlterm 設定ウインドウから input method を XIM -> FCITX にするというような項目も出ていないように思える。
{ XIM, Skk, Keyboard, なし } のどれかしか選べない。
XIMサーバーは「自動(現在 fcitx)」となっているが…。
どういうことなんだろう?
■ コアダンプが吐かれていたので解析してみることに
別のアプローチから探ってみよう。
重くなっていたときの Xorg のプロセスを解析していると、実は裏でコアダンプを吐いていた事がわかった。
$ find /var -mtime -1 2>/dev/null | grep mlterm
/var/lib/systemd/coredump/core.mlterm.1000.f27b6c1196334a3c9ab11080073e6b15.100772.1745589846000000.zst
exit 1 0
$ ll /var/lib/systemd/coredump/core.mlterm.1000.f27b6c1196334a3c9ab11080073e6b15.100772.1745589846000000.zst
-rw-r-----+ 1 root root 465K 4月 25 23:04 /var/lib/systemd/coredump/core.mlterm.1000.f27b6c1196334a3c9ab11080073e6b15.100772.1745589846000000.zst
$
$ coredumpctl list mlterm
TIME PID UID GID SIG COREFILE EXE >
Fri 2025-04-25 23:04:06 JST 100772 1000 1000 SIGABRT present /usr/bin/mlterm >
$
$ coredumpctl info 100772
PID: 100772 (mlterm)
UID: 9999 (9hnder)
GID: 7777 (deadpool)
Signal: 6 (ABRT)
Timestamp: Fri 2025-04-25 23:04:06 JST (20min ago)
Command Line: mlterm
Executable: /usr/bin/mlterm
Control Group: /user.slice/user-1000.slice/session-2.scope
Unit: session-2.scope
Slice: user-1000.slice
Session: 2
Owner UID: 9999 (9hnder)
Boot ID: f27b6c1196334a3c9ab11080073e6b15
Machine ID: 9552149582014135bfaba4ee7413b8a6
Hostname: strong-machine
Storage: /var/lib/systemd/coredump/core.mlterm.1000.f27b6c1196334a3c9ab11080073e6b15.100772.17455>
Size on Disk: 464.7K
Message: Process 100772 (mlterm) of user 1000 dumped core.
$
ではなぜコアダンプを吐いたのだろうか?
"Signal: 6 (ABRT)" とあるので、SIGABRT を受けて終了している。SIGABRT はC言語の abort() 関数で異常終了されたという意味で、プログラム側、特に標準 Cライブラリの中などで重大なエラーがあった際に用いられることが多い。
…とりあえず GNUデバッガー(GDB) でコアファイルを読み込んでみることに。
systemd が吐いたコアダンプファイルは Zstandard 形式で圧縮されている。普通に GDB に食わせてみると zsd ファイルは読み込めないのでエラーになる。 zstd コマンドで解凍すれば読み込めるが…
もっと簡単に coredumpctl を使って間接的に GDB を起動するほうが良い。以下のようにする。
$ coredumpctl debug mlterm
これでデバッグした際のログが以下。
画像はスタック・バックトレースのみを写したもの。
../uitoolkit/ui_sb_view_factory.c ファイルの dlsym_sb_engine_new_func() から呼ばれる sprintf() が引き起こしていた。
sprintf() の方の引数がなんであったかは最適化のため、残念ながら見れない。だが、 dlsym_sb_engine_new_func() の方の引数 name は "pixmap_engine" のようだ。
ここに問題があるのは確かだ。
ソースを見てみよう。
画像の方は Github だと関数の全景が映らなくてわかりにくかったので Debian ソースの mlterm 3.9.3-3.1 のものを Web で見つけてスクショしたものにした。だが、このファイルは約 9年もの間まったく変わっていないので同じと思ってもらっていい。
sprintf() しているのは 123行目。
"ui_%s_sb_engine_new" の %s 部分に name 変数の値を代入して、 symbol ポインタの指し示す先へ保存している。が、その前の、
len = 16 + strlen(name) + 1;
if ((symbol = alloca(len)) == NULL) {
return NULL;
}
となっていて symbol の領域を確保している部分が妙だ。
ui_<なんちゃら>_sb_engine_new は <なんちゃら> を除いた部分が 17文字だ。なのに、 16 + strlen(name) + 1 で長さを決めてしまっている。
最後の +1 は null を残しておくためのものと思われるので、これはおかしい。2
他の似た部分ではちゃんとした数値になっているので、単純ミスの可能性が高い。
■ ソースを修正してビルドする
さて、
コアを吐いて死んでいる原因は掴めた。だが、どうも腑に落ちないのはしばらく放っておくと、動くようになる場合もあるということである。
毎回 SIGABRT で落ちるなら、動き続けることなどあるわけがない。
なので、おそらくこのコアダンプは副作用的なものであって、根本的な原因ではない気がする。おそらく XIM なのか Fcitx なのかはわからないが、インプットメソッド周りのバグか互換性の問題等があって引き起こされた別の問題があるはずだ。
それが先に挙げた FreeBSD ユーザーが ML に投稿していた内容と、おそらく一致する原因である気がしている。
…まあ、それはともかく、件の文字数違いがバグなのはほぼ確かなので、ビルドし直してみることにする。
デバッグ・シンボル付きでビルドしておけば、もしも直っていなくて再発した場合にも GDB でリアルタイムにアタッチしてデバッグができるだろうし。
…と思って PKGBUILD ファイルを見てみたが、最初から -O<数値> オプションは付加されておらず、ストリップもされていないようでデバッグシンボル付きでコンパイルされるようになっていた。なーんだ。
ソースコードは以下のように修正。
$ cd ~/.cache/yay/mlterm-git/src/mlterm-git/uitoolkit
$ cp --preserve=mode,timestamps ui_sb_view_factory.c ui_sb_view_factory.c.orig
$ ls -l ui_sb_view_factory.c ui_sb_view_factory.c.orig
-rw-r--r-- 1 root root 9705 4月 25 00:17 ui_sb_view_factory.c
-rw-r--r-- 1 root root 9705 4月 25 00:17 ui_sb_view_factory.c.orig
$ vim ui_sb_view_factory.c
$ diff ui_sb_view_factory.c ui_sb_view_factory.c.orig
118c118
< len = 17 + strlen(name) + 1;
---
> len = 16 + strlen(name) + 1;
exit 1
$
俺ちゃんは AURヘルパーに yay を使っているので、そのキャッシュ・ディレクトリに git clone されたソースコードを改造した。
めんどくさかったので、
$ yay -S mlterm-git --rebuild
で普通に ダウンロード&ビルド&インストール を開始して、 configure が始まるくらいのタイミングで CTRL+Z で yay のジョブをサスペンドさせて、上記のようにソースを修正してから fg コマンドで再開させた。
…が、これは dirty hack... 力技なので 良い子のみんなはマネすんなよ☆
本来なら --downloadonly などでソースをダウンロードするだけにしてから修正し -B オプションででビルドとインストールを行うようにするほうが良いだろう。たぶん。
なお、ソースコードは上記のキャッシュからは削除されてしまうが、完成したパッケージ・アーカイブ・ファイルには含まれるので、以下にインストールされていた。
/usr/src/debug/mlterm-git/mlterm-git/uitoolkit/ui_sb_view_factory.c
■ mlterm の設定ファイルを直接書き換えてみる
さてさて。
これでおそらく SIGABRT は起きなくなったと思われる。
だが、やはり相変わらずタイトルバーやスクロールバーなどにマウスポインターがあるときに mlterm へ入力しようとすると、入力を受け付けない。そして重くなる…。やっぱり根本原因を見つけて叩かないとダメだ。
現象をもっと突っ込んで調べてみると、どうやらタイトルバーなど mlterm のコンソール領域以外にマウスポインターがある場合に何かを入力すると、なぜか裏で 日本語入力が動作してしまっている ようだった。
マウスポインターを動かしてコンソール領域内に持ってくると、入力できるようになるのだがまだこの際は重い。ただし、CTRL+Space キーを押して Mozc を ON にすると、なぜかその前に入力していた内容が ドドドッっと出力された!
この挙動を見るに、mlterm の領域外にマウス・ポインターがあるときには Fcitx5 が入力内容を出力する先を見失って重くなっているのではないか?と思われた。しかも、何故かその際に 日本語入力機能(=Mozc)がオンになっている ようだ。
そして mlterm のコンソール領域内に戻ってきて、 CTRL+Space や 半角/全角 キーで日本語入力機能をオンにした途端、その行きどころをなくしていた入力内容が ダダダッ と堰を切ったように出力される ということだと思われる。
おそらく XIM の処理と Fcitx の処理で何かが異なるのだろう。
そうすると、やはり mlterm の設定をどうにかするしかない。
そこで mlterm の設定ファイルを直接書き換えてみた。
以下のように。
$ cd ~/.mlterm
$ vim main
$ grep input_method main
input_method=fcitx
$
input_method=XIM となっていたのを fcitx に書き換えただけ。
おそらく FCITX オプションは有効でエラーも無くビルドできていたので、機能的にはこれでちゃんと動くのではないか?と思われた。すると…
…ビンゴ!
mltem を再起動すると、問題なく動作するようになった。
高負荷も起こらないし、タイトルバーやスクロールバー、あるいは mlterm のウィンドウ外にマウス・ポインターを持っていってもフォーカスさえあっていればちゃんと入力できるようになった!
■ では、なぜ設定ダイアログに出ないのか?
問題は解決した。
だが、なぜ俺ちゃんの使っているバージョンの mlterm では FreeBSD の ML で言っていた人の設定項目が表示されていないのだろうか?
軽く調べてみたが、以下のファイルに Fcitx の定義が存在しないためのようだった。
/usr/src/debug/mlterm-git/mlterm-git/tool/mlconfig/mc_im.c
XIM, skk などはあるが、 fcitx が無い。
となると、逆に、なぜ FreeBSD ユーザーの人が項目があったのかが謎だ。ソースコードは mlterm-git を使用しており、公式の GitHub から取得されているので同じはずだが…
もしかしたら、FreeBSD のメンテナーがパッチを加えて追加したりしているのかもしれない。あるいは俺ちゃんの知らない設定画面が存在するのか…または何かの間違いか。
まあ、いいや。
以上。
LOVE, 🫶 Chimi-Changa!
-
なお Google は何もヒットせず、ダメだったので DuckDuckGo で検索している。最近の Google は ITリテラシー の低い一般人に合わせて結果を変えたり、 SEO 対策屋とのいたちごっこの結果、検索アルゴリズムが煩雑化・退化し こういった有用な情報のサイトが存在するにもかかわらず、ヒットしないことが多くなってしまった。(*やれ やれ*)
技術屋なら Google は捨て去り DuckDuckGo を使うべきだと強く宣伝しておく。 ↩ -
この方法は C言語では定石の方法だ。…あと、C言語 を使う諸氏なら同意してもらえると思うが、 strlen() ではなく strnlen() を常に使うべきだ。 strncpy() ではなく alloca() という maclloc() のラッパーぽい関数に食わせているのもあまり良くはない気がする。古いコードだから、リファクタリングされず、そのままになってしまっているのだろうが。 ↩