psutil
GC
FreeBSDのportsを点検してたんだ。そしたらこんな宣伝が掲げてあった。
Rust is an open-source systems programming language that runs blazingly fast, prevents almost all crashes, and eliminates data races. Some of its features: - Algebraic data types, type inference - Pattern matching and closures - Concurrency without data races - Guaranteed memory safety - Optional garbage collection - Zero-cost abstractions - Minimal runtime - Efficient C bindings WWW: https://www.rust-lang.org/
GCとは無縁と思っていたんだけど、どうやらそうではないみたい。現に、 gc 0.41 なんてのが見付かった。更に、 象牙の塔では、 Rust as a Language for High Performance Garbage Collector Implementation なんて亊が行われていた。まあ、好きにやってください。
それより気になったのは、そろそろ新しいrustがふって来る予感がするな。
去年のあれ
何と言うタイトルだ。。。って、近頃のTVの真似をしただけ。天下のNHKも民放宜しく、平気でやってるからね。
そう、去年見付けておいた@ITに出てた rustとpythonの力比べの記事を、FreeBSDでやろうとしてたんだ。
Compiling serde_urlencoded v0.7.0 Compiling psutil v3.2.1 error[E0432]: unresolved import `crate::cpu::cpu_times_percpu` --> /home/sakae/.cargo/registry/src/github.com-1285ae84e5963aae/psutil-3.2.1/src/cpu/cpu_times_percent.rs:6:29 | 6 | use crate::cpu::{cpu_times, cpu_times_percpu, CpuTimes}; | ^^^^^^^^^^^^^^^^ | | | no `cpu_times_percpu` in `cpu` | help: a similar name exists in the module: `cpu_times_percent`
この通り、psutilのcpu,network,disk,process等で、ことごとくエラーですよ。 これのREADME.mdを見ると
## Platform Support Currently, only Linux and macOS are supported, but support is planned for all major platforms.
rust-psutil / rust-psutil を見るまでもなく、BSD族は蚊帳の外である。
psutil
取り敢えず、どんな使いかたになってるか身辺調査。ええ、あわよくばOpenBSDにも引きいれられないかと思いましてね。
まずは、公開情報って亊で、友人関係を当る亊にしました。 Cargo.lock
name = "psutil" version = "3.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e780a52bf9358cb8257cac630b130dc603901f7488f8eef13e2d512cead10739" dependencies = [ "cfg-if 0.1.10", "darwin-libproc", "derive_more", "glob", "mach", "nix", "num_cpus", "once_cell", "platforms", "thiserror", "unescape", ]
色々な協力者がいるんですなあ。そんじゃ、専用の道具を使って、少し追跡。
sakae@deb:/tmp/web_engineer_in_rust/chapter2/rust$ cargo tree : ├── psutil v3.2.1 │ ├── cfg-if v0.1.10 │ ├── derive_more v0.99.16 (proc-macro) │ │ ├── proc-macro2 v1.0.30 (*) │ │ ├── quote v1.0.10 (*) │ │ └── syn v1.0.80 (*) │ ├── glob v0.3.0 │ ├── nix v0.17.0 │ │ ├── bitflags v1.3.2 │ │ ├── cfg-if v0.1.10 │ │ ├── libc v0.2.104 │ │ └── void v1.0.2 │ ├── num_cpus v1.13.0 (*) │ ├── once_cell v1.8.0 │ ├── platforms v0.2.1 │ ├── thiserror v1.0.30 (*) │ └── unescape v0.1.0
これと、rust-psutilを比べてみると面白いな。 単一故障点と思われる、gitの在処は、 rust-psutil / rust-psutil だ。
どんな使われ方をしてるか、元記事を当ってみた。
println!( "プロセスの使用メモリ量(参考):{:?}MB", psutil::process::Process::new(std::process::id()) .unwrap() .memory_info() .unwrap() .rss() as f64 / 1e6
ふーん、PIDを得てそいつのメモリー使用状況のうちRSSを取り出しているってパイプが組まれているんだな。ああ、ruby風に言うとメソッド・チェーンだ。途中に挟まっているunwrapは魔除けじゃなくて、エラーかも知れないって型に包まているのの梱包を剥す役割になってる。
example
このpsutilにはexampleってのが付属してる。そして、それを簡単に実行する機構がcargoに用意されている。rustの解説WEBを見てても、いきなり借用がどうのこうのと言語よりの話に行っちゃって、周辺の話は余り出てこない。んでもって、オイラーが実験しとく。
sakae@deb:/tmp/vendor/psutil$ ls examples/ all.rs collectors.rs kill.rs ps.rs
bashのプロンプトが /tmp/vendorで始まってるけど、これは元記事の所で cargo vendor して、出来たものを移動しといたんだ。この方が、見やすいからね。
最初はグーじゃなくて、ps してみる。
sakae@deb:/tmp/vendor/psutil$ cargo run --example ps PID %CPU %MEM COMMAND 1 0.0 0.2 /sbin/init 2 0.0 0.0 [kthreadd] : 7797 12.2 0.1 target/debug/examples/ps
動いているな。次は殺してやる。
sakae@deb:/tmp/vendor/psutil$ cargo run --example kill thread 'main' panicked at 'index out of bounds: the len is 1 but the index is 1', examples/kill.rs:7:32
反抗されたので、ソースしろ。
sakae@deb:/tmp/vendor/psutil$ cat examples/kill.rs //! Kill a process, reading it's PID as a cli argument. use psutil::process::Process; fn main() { let args: Vec<String> = std::env::args().collect(); let process = Process::new(args[1].parse().unwrap()).unwrap(); if let Err(error) = process.kill() { println!("Failed to kill process: {}.", error); }; }
そりゃそうだ。殺しのターゲットを指定しなきゃ、いかにゴルゴ13でも、実行出来ませんです。ターゲット番号は、コマンドラインから与えるんだな。
sakae@deb:/tmp/vendor/psutil$ sleep 1000 & [1] 7829 sakae@deb:/tmp/vendor/psutil$ cargo run --example kill -- 7829 [1]+ Killed sleep 1000
今度はちゃんと殺せた。
sakae@deb:/tmp/vendor/psutil$ cargo run --example kill -- 7829 thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: NoSuchProcess { pid: 7829 }', examples/kill.rs:7:58
そして、一度死亡したpidを指定すると、パニクってしまうのは、お約束? って、ちゃんと、その対処方法が記述されてるからね。
small app
use psutil::host::info; fn main() { println!("{:?}", info()); }
実行例
sakae@deb:/tmp/vendor/psutil/examples$ cargo r --example info Info { operating_system: Unknown, release: "5.10.0-10-686-pae", version: "#1 SMP Debian 5.10.84-1 (2021-12-08)", hostname: "deb", architecture: Unknown }
32ビット輝は、もう無視の段階だなあ。
Info { operating_system: Unknown, release: "5.10.0-10-amd64", version: "#1 SMP Debian 5.10.84-1 (2021-12-08)", hostname: "pen", architecture: X86_64 } Ok(6375.72s)
ついでに、アップタイムも出してみた。結果がリザルト型になってる。
use std::thread; use std::time::Duration; use psutil::*; fn main() { let hoge = host::info(); dbg!(hoge); }
こういう方法も有り
[examples/mini.rs:8] hoge = Info { operating_system: Unknown, release: "5.10.0-10-686-pae", version: "#1 SMP Debian 5.10.84-1 (2021-12-08)", hostname: "deb", architecture: Unknown, }
で、こちらの方が、かっこいい。
地獄の再来
ある日psutilを極めようとして、 Cargo.tomlに
[dependencies] psutil = "3.2.1"
とだけ書いてbuildしよとしたんだ。
sakae@deb:/tmp/hoge$ cargo b Updating crates.io index error: failed to select a version for the requirement `platforms = "^0.2.1"` candidate versions found which didn't match: 2.0.0, 1.1.0, 1.0.3, ... location searched: crates.io index required by package `psutil v3.2.1` ... which satisfies dependency `psutil = "^3.2.1"` of package `hoge v0.1.0 (/tmp/hoge)`
DLを拒否されたよ。psutilに記述してある、platformsが0.2.1ってなってて、それがもうこの世の中には存在しないので、駄目って亊だ。
こういう依存地獄は、haskellで頻繁に怒っていて、その解決に、パトロール隊が組織され、矛盾がないように目を光らせていたな。rustでもこの依存地獄が発生。これ、原理的には避けられない。みんな非同期に更新してるからね。
件のplatforms version によると、古い版は整理して削除されちゃったみたいだ。だから、依存はなるべく少ないライブラリィーを使うべきってな亊が、haskellの指南書に書かれていたな。
これの解決には、psutilの作者さんに通報して、新しい依存に変更したものをリリースしてもらうより方法は無い。作者さんが在命でかつ意欲もりもりを祈るしかない。
そんな受け身は、いかんぜよ。気が付いた人が通報すればよい。gitのアカウントを持ってるなら、リクエストするのが筋だろう。けど、オイラーは持っていない。そこで、git log して出て来る記録から、目星い人を選べばよい。あるいは、Cargo.tomlにも作者名が有るんで、それを使ってもよい。
ob$ git log | grep Author: | sort | uniq -c | sort -r
この結果を見ると、途中からオーナーが交代したみたいだ。一番最近の投稿(2021/04/11)をした人に、依頼メールを出しておいたよ。
Cargo.toml を壊れたままにしない こういう記事を発見した。オイラーの行為は一応正しかったみたいだ。まだ、修整された風ではないみたいだけど。
縮退運用
折角なんで、コンパイルエラーになる所を避けて、コンパイルを完了させてみる。 Cargo.toml を勘で修整
[features] default = [ "host", "memory", "sensors"] #default = ["cpu", "disk", "host", "memory", "network", "process", "sensors"]
面白い所を全部削除してみた。って、それじゃ話にならんじゃん。まあ、コンパイルが成功すれば、docも作成されるから、依存してるクレートの閲覧が出来るから佳としておこう。
ob$ cargo r --example info Finished dev [unoptimized + debuginfo] target(s) in 0.06s Running `target/debug/examples/info` [examples/info.rs:8] hoge = Info { operating_system: Unknown, release: "7.0", version: "GENERIC.MP#3", hostname: "ob.localhost.jp", architecture: Unknown, }
OpenBSD(amd64)なマシンなんだけど、OS名は不明、石の名前も不明って、どゆ亊? データの出所は、
ob$ uname -a OpenBSD ob.localhost.jp 7.0 GENERIC.MP#3 amd64
だと思うんだけど、それならOS名とかを取得出来そうだと思うんだけど。
ob$ rust-gdb -q target/debug/examples/info Reading symbols from target/debug/examples/info...done. (gdb) b 7 Breakpoint 1 at 0x18b5b: file examples/info.rs, line 7. (gdb) r Starting program: /tmp/rust-psutil/target/debug/examples/info Breakpoint 1, info::main::h4c381eab8bf58502 () at examples/info.rs:7 7 let hoge = host::info();
BPは、行番号だけでOKみたい。
(gdb) info break Num Type Disp Enb Address What 1 breakpoint keep y 0x000006b61f34ab5b in info::main::h4c381eab8bf58 502 at examples/info.rs:7 breakpoint already hit 1 time
アプリ名::mainってのがgdbが認識してるやつなのか。
(gdb) s psutil::host::sys::unix::info::info::hb01864db9d31455a () at src/host/sys/unix/info.rs:11 11 let utsname = sys::utsname::uname(); (gdb) n 13 let operating_system = OS::from_str(utsname.sysname()).unwrap_or(OS::Unknown ); (gdb) 14 let release = utsname.release().to_string();
もう少し分かり易く、俯瞰してみる。
(gdb) l 9 10 pub fn info() -> Info { 11 let utsname = sys::utsname::uname(); 12 13 let operating_system = OS::from_str(utsname.sysname()).unwrap_or(OS::Unknown); 14 let release = utsname.release().to_string(); 15 let version = utsname.version().to_string(); 16 let hostname = utsname.nodename().to_string(); 17 let architecture = Arch::from_str(utsname.machine()).unwrap_or(Arch::Unknown); 18 (gdb) bt 2 #0 psutil::host::sys::unix::info::info::hb01864db9d31455a () at src/host/sys/unix/info.rs:14 #1 0x000006b61f34ab68 in info::main::h4c381eab8bf58502 () at examples/info.rs:7 (More stack frames follow...)
OS名を聞いてもちゃんと答えてくれない亊を想定して、そんなの知らんって予防してたのね。
(gdb) n 15 let version = utsname.version().to_string(); (gdb) p utsname $1 = nix::sys::utsname::UtsName (libc::unix::bsd::utsname {sysname: [79, 112, 101, 110, 66, 83, 68, 0 <repeats 249 times>], nodename: [111, 98, 46, 108, 111, 99, 97, 108, 104, 111, 115, 116, 46, 106, 112, 0 <repeats 241 times>], release: [55, 46, 48, 0 <repeats 253 times>], version: [71, 69, 78, 69, 82, 73, 67, 46, 77, 80, 35, 51, 0 <repeats 244 times>], machine: [97, 109, 100, 54, 52, 0 <repeats 251 times>]})
あれ??
rust-gdbgui
FreeBSDに待望の2021式のrust族がやってきた。そこで面白いものを発見。
[sakae@fb ~]$ rust-gdbgui rust-gdbgui =========== gdbgui - https://gdbgui.com - is a graphical front-end to GDB that runs in a browser. This script invokes gdbgui with the Rust pretty printers loaded. Simple usage : rust-gdbgui target/debug/myprog With arguments: rust-gdbgui 'target/debug/myprog arg1 arg2...' (note the quotes) :
喜びいさんで起動してみると、肝心のgdbguiってのがなかった。糠喜びさせないでよね。プンプン。
[sakae@fb ~]$ PATH=/home/sakae/.local/bin:$PATH [sakae@fb ~]$ pipx install gdbgui installed package gdbgui 0.15.0.1, installed using Python 3.8.12 These apps are now globally available - gdbgui done! ✨ 🌟 ✨
起動するとブラウザーが動くのはいいんだけど、どうもgdbと接続出来無いみたいだ。リナでやってみるかな。コネクトに失敗したってなったら、再ロードすればいいみたい。
下記は、無理を承知で、あらましの確認モード。
[sakae@fb /tmp/vendor/psutil]$ rust-gdbgui target/debug/examples/info usage: gdbgui [-h] [-g GDB_CMD] [-p PORT] [--host HOST] [-r] [--auth-file AUTH_FILE] [--user USER] [--password PASSWORD] [--key KEY] [--cert CERT] [--remap-sources REMAP_SOURCES] [--project PROJECT] [-v] [-n] [-b BROWSER] [--debug] [--args ...] [debug_program] gdbgui: error: unrecognized arguments: --gdb-args target/debug/examples/info [sakae@fb /tmp/vendor/psutil]$ gdbgui target/debug/examples/info Opening gdbgui with default browser at http://127.0.0.1:5000 w3m: Can't load http://127.0.0.1:5000. View gdbgui dashboard at http://127.0.0.1:5000/dashboard exit gdbgui by pressing CTRL+C
どうも、コマンドの指定が間違っているみたいだなぁ。
at debian
どんな風にプロセスが動いているか。
3381 pts/0 Sl+ 0:01 /home/sakae/.local/pipx/venvs/gdbgui/bin/python /home/sakae/.local/bin/gdbgui --args target/debug/myprog 3412 ? Ssl 0:09 firefox-esr http://127.0.0.1:5000 3573 ? Sl 0:00 /usr/lib/firefox-esr/firefox-esr -contentproc -childID 4 -isForBrowser -prefsLen 5773 -prefMapSize 240761 3617 pts/6 Ssl+ 0:00 gdb -iex=new-ui mi2 /dev/pts/5 -iex=set inferior-tty /dev/pts/4 -iex=set pagination off
バックエンドでpython製のサーバーが走り、gdbが起動するんだな。それがpid=3617になる。3381の方はブラウザーと接続するWebサーバーも兼ている。どうやら実体はjavascriptに任せているそうだ。今風の作り。
昔は、stdin/stdout/stderrだったけど、それがブラウザーに置き換わってしまってるんだな。 でも、色々な窓を一辺に見せられると、オイラーはおろおろしちゃうぞ。
一度に数十の監視窓を確認出来無きゃ墜落しちゃうパイロットなんてのは絶対に無理。精々鼠捕りに引っ掛らないようにスピードメーターを確認しながら走る車ぐらいが関の山ですよ。
ああ無理してrustのデバックをこれでやるなら、 Debugging Rust Programs は、ちゃんと見ておくべき。今の所、大事な注意だからね。
sakae@deb:/tmp/myprog$ rust-gdb -q target/debug/myprog Reading symbols from target/debug/myprog... (gdb) b myprog::main Breakpoint 1 at 0x4e95: file src/main.rs, line 2. (gdb) r Starting program: /tmp/myprog/target/debug/myprog [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib/i386-linux-gnu/libthread_db.so.1". Breakpoint 1, myprog::main () at src/main.rs:2 2 println!("Hello, world!");
こちらの方法は、なかなか捨てられませんです。 emacsからなら、emacs -f gdb しておいて、
Run gdb (like this): rust-gdb -i=mi target/debug/myprog
これで用が足りちゃう。画面も、gdbのコントロールとソースの表示だけだしね。