mruby
stat
前回のstatの続き、ってか脱稿した後、色々と思う所有って、納豆のように糸を引くのよ。
statってのは、lsコマンドのネタ元だ。今迄誤解してた亊がある。 ファイルのchangeの項。今迄ずっと、作られた日時って思ってた。stat(2)したら
st_ctim Time when file status was last changed (inode data modification). Changed by the chmod(2), chown(2), link(2), rename(2), unlink(2), utimes(2), and write(2) system calls.
こんな説明がされてた。じゃ純粋な作成日って記録は無いの。結論を言うとない。
それじゃあんまりだって亊で、ext4fsでは追加されてるのね。
/sys/sys/stat.h
struct stat { mode_t st_mode; /* inode protection mode */ : #if __POSIX_VISIBLE >= 200809 || __BSD_VISIBLE struct timespec __st_birthtim; /* time of file creation */ #else time_t __st_birthtime; /* time of file creation */ long __st_birthtimensec; /* nsec of file creation */ #endif /* __POSIX_VISIBLE >= 200809 || __BSD_VISIBLE */ };
こんな定義が成されていて、一応OpenBSDでも、ext4fsに対応する素振りはある。ってかPOSIXに準拠する積もりみたいだ。内心は、ポータブル・ユニックスと言いいつつ、Linux陣営の分裂を防ぐ仕組み、、、だろうぐらいに思っているに違いない。そう、グローバル・スタンダードと言う圧しつけと一緒の構図ね。
いかにも知ったか振りだけど、ここまでスッと到達した訳では無い。リナでstatした時、Birthが出てくる場合と、そんなの知らないとばかり - になってるのと有ったんだ。何これと思って調べてみたら、上記に行き着いた。ちょいとした疑問も世の中に質問出来る、良い環境だなあ。
ruby pkg
FreeBSDのportsにあった、ご案内。これぐらいは知らないともぐり? /usr/ports/lang/ruby32/pkg-message
Some of the standard commands and libraries are provided as separate ports for ease of upgrading: devel/ruby-gems: gem - RubyGems package manager devel/rubygem-debug: debug - Debugging functionality for Ruby devel/rubygem-erb: erb - Templating system for Ruby devel/rubygem-irb: irb - Interactive Ruby devel/rubygem-minitest: minitest - Complete suite of testing facilities devel/rubygem-power_assert: power_assert - Power Assert for Ruby devel/rubygem-rake: rake - Ruby Make devel/rubygem-rbs: rbs - Language for type signatures for Ruby and standard library definitions devel/rubygem-rdoc: rdoc - Ruby Documentation System devel/rubygem-test-unit: test-unit - Unit testing framework for Ruby devel/rubygem-typeprof: typeprof - Type analysis tool for Ruby code ftp/rubygem-net-ftp: net-ftp - Support for the File Transfer Protocol mail/rubygem-net-imap: net-imap - Ruby client api for Internet Message Access Protocol mail/rubygem-net-pop: net-pop - Ruby client library for POP3 mail/rubygem-net-smtp: net-smtp - Simple Mail Transfer Protocol client library for Ruby math/rubygem-matrix: matrix - Implementation of Matrix and Vector classes math/rubygem-prime: prime - Prime numbers and factorization library sysutils/rubygem-bundler: bundler - Tool that manages gem dependencies for ruby applications textproc/rubygem-rexml: rexml - XML toolkit for Ruby www/rubygem-rss: rss - Family of libraries that support various formats of XML "feeds" And some of the standard libraries are provided as separate ports since they require extra dependencies: databases/rubygem-dbm: DBM module databases/rubygem-gdbm: GDBM module Install them as occasion demands.
mruby
何でも揃うと言うFreeBSDのportsに登録が無かった。これはもう、マツコも知らないmrubyな世界。mrubyの沼の確定だな。取り敢えず世間に聞いてみる。
ああ、何故か、OpenBSDにはmrubyのportsが有ったぞ。不思議な亊だ。
mruby (本家)
makeしてみる
mrubyを作るには、rubyが必要になってくる。説明にそう書いてある。ラズパイで頑張ってmrubyしようとして、開発環境がなくて困った人が続出してた。
make rake ./minirake
どれを使っても、コンパイル出来るぞ。まあ、rakeに帰着するんだけどね。で、rakeはMakefileならぬRakefileをレシピにして、コンパイルの流れを制御してる。
どんな風に動作してるか? 馬鹿の一つ覚えで、rdbgしてみた。
sakae@deb:/tmp/mruby$ rdbg -c -- rake [4, 13] in /usr/local/bin/rake 4| # 5| # The application 'rake' is installed as part of a gem, and 6| # this file is here to facilitate running it. 7| # 8| => 9| require 'rubygems' 10| 11| Gem.use_gemdeps 12| 13| version = ">= 0.a" =>#0 <main> at /usr/local/bin/rake:9 (rdbg) trace call into: LOG # command Enable CallTracer (enabled) into: LOG (rdbg) c # continue command /usr/local/lib/ruby/gems/3.1.0/gems/debug-1.5.0/lib/debug/tracer.rb:84:in `flush': No space left on device @ rb_io_flush_raw - LOG (Errno::ENOSPC) from /usr/local/lib/ruby/gems/3.1.0/gems/debug-1.5.0/lib/debug/tracer.rb:84:in `out' from /usr/local/lib/ruby/gems/3.1.0/gems/debug-1.5.0/lib/debug/tracer.rb:140:in `block in setup' from /usr/local/bin/rake:25:in `load' from /usr/local/bin/rake:25:in `<main>'
ログが膨大すぎる。それもこれもmrubyを打ち上げる為の1段目のrakeが非常に頑張っていて、そのログが出てきてるから。何気に使ってるmake(== rake)も裏では、ものすごく努力してるって亊が伺える。
ならば、ログの最後の方はどうなってる? ちょいと覗いてみた。
sakae@deb:/tmp/mruby$ tail -3000 LOG | head #th:1 #dpt:23> Hash#[] at /tmp/mruby/lib/mruby/gem.rb:403 #th:1 #dpt:23< Hash#[] #=> #<MRuby::Gem::Specification:0xb4edf65c @... at /tmp/mruby/lib/mruby/gem.rb:403 #th:1 #dpt:23> Hash#empty? at /tmp/mruby/lib/mruby/gem.rb:401 #th:1 #dpt:23< Hash#empty? #=> false at /tmp/mruby/lib/mruby/gem.rb:401 #th:1 #dpt:23> Hash#shift at /tmp/mruby/lib/mruby/gem.rb:402 #th:1 #dpt:23< Hash#shift #=> ["mruby-compiler", {:gem=>"mruby-compile... at /tmp/mruby/lib/mruby/gem.rb:402 #th:1 #dpt:23> Hash#[] at /tmp/mruby/lib/mruby/gem.rb:403 #th:1 #dpt:23< Hash#[] #=> #<MRuby::Gem::Specification:0xb4e688a4 @... at /tmp/mruby/lib/mruby/gem.rb:403 #th:1 #dpt:23> Hash#empty? at /tmp/mruby/lib/mruby/gem.rb:401 #th:1 #dpt:23< Hash#empty? #=> false at /tmp/mruby/lib/mruby/gem.rb:401
ここらへんで(tail -400 付近)、DISKが食い潰されたんだな。
#th:1 #dpt:34< Array#<< #=> ["mruby-enum-ext"] at /usr/local/lib/ruby/3.1.0/tsort.rb:413 #th:1 #dpt:34> Method#call at /usr/local/lib/ruby/3.1.0/tsort.rb:415 #th:1 #dpt:35> {"mruby-compar-ext"=>#<MRuby::Gem::Specification:0xb4ea4c8c @name="mruby-compar-ext", ....
gdb してみる
rdbgは諦めて、出来上がったmrubyをgdbしてみる。例の儀式だ。
sakae@deb:/tmp/mruby$ gdb -q bin/mruby Reading symbols from bin/mruby... (gdb) r Starting program: /tmp/mruby/bin/mruby ^C Program received signal SIGINT, Interrupt. 0xb7fd2559 in __kernel_vsyscall () (gdb) bt #0 0xb7fd2559 in __kernel_vsyscall () #1 0xb7dbaf97 in __GI___libc_read (fd=0, buf=0x533320, nbytes=1024) at ../sysdeps/unix/sysv/linux/read.c:26 #2 0xb7d4511b in _IO_new_file_underflow (fp=0xb7eae580 <_IO_2_1_stdin_>) at libioP.h:948 #3 0xb7d44149 in __GI__IO_file_xsgetn (fp=0xb7eae580 <_IO_2_1_stdin_>, data=0xbffff480, n=64) at fileops.c:1322 #4 0xb7d37e13 in __GI__IO_fread (buf=0xbffff480, size=1, count=64, fp=0xb7eae580 <_IO_2_1_stdin_>) at iofread.c:38 #5 0x0045f77c in mrb_load_detect_file_cxt (mrb=0x5131a0, fp=0xb7eae580 <_IO_2_1_stdin_>, c=0x5332e0) at mrbgems/mruby-compiler/core/parse.y:6930 #6 0x0040ce92 in main (argc=1, argv=0xbffff624) at /tmp/mruby/mrbgems/mruby-bin-mruby/tools/mruby/mruby.c:357
普通に起動すると、待ちに入る。で、どんな所で待機中?
(gdb) f 6 #6 0x0040ce92 in main (argc=1, argv=0xbffff624) at /tmp/mruby/mrbgems/mruby-bin-mruby/tools/mruby/mruby.c:357 357 v = mrb_load_detect_file_cxt(mrb, args.rfp, c); (gdb) l 352 /* Load program */ 353 if (args.mrbfile || mrb_extension_p(cmdline)) { 354 v = mrb_load_irep_file_cxt(mrb, args.rfp, c); 355 } 356 else if (args.rfp) { 357 v = mrb_load_detect_file_cxt(mrb, args.rfp, c); 358 } 359 else { 360 char* utf8 = mrb_utf8_from_locale(args.cmdline, -1); 361 if (!utf8) abort();
record by gdb
Processor Trace を使ってデバッグ時に詳細なトレースを取得する
GDB の Reverse Debugging を使ってみた
(gdb) record ;; TAB to complation btrace function-call-history save delete goto stop full instruction-history
記録を開始する。
sakae@deb:/tmp/mruby$ gdb -q bin/mruby Reading symbols from bin/mruby... (gdb) b main Breakpoint 1 at 0xc900: file /tmp/mruby/mrbgems/mruby-bin-mruby/tools/mruby/mrub y.c, line 278. (gdb) record Process record: the program is not being run. (gdb) r -v Starting program: /tmp/mruby/bin/mruby -v Breakpoint 1, main (argc=2, argv=0xbffff624) at /tmp/mruby/mrbgems/mruby-bin-mruby/tools/mruby/mruby.c:278 278 mrb_state *mrb = mrb_open(); (gdb) record (gdb) c Continuing. ^C Program received signal SIGINT, Interrupt. 0x0041c60f in incremental_sweep_phase (limit=<optimized out>, gc=0x5131fc, mrb=<optimized out>) at /tmp/mruby/src/gc.c:1153 1153 if (p->as.basic.tt != MRB_TT_FREE) {
遡って確認してく。ここまではrdbgでも出来るな。ってか、gdbの機能を思い切り取入れたと説明されてたな。
(gdb) reverse-next 0x0041c60f 1153 if (p->as.basic.tt != MRB_TT_FREE) { (gdb) 1152 if (is_dead(gc, &p->as.basic)) { (gdb) 1151 while (p<e) { (gdb) 1170 p++; (gdb) bt #0 incremental_sweep_phase (limit=<optimized out>, gc=0x5131fc, mrb=<optimized out>) at /tmp/mruby/src/gc.c:1170 #1 incremental_gc (mrb=<optimized out>, gc=0x5131fc, limit=<optimized out>) at /tmp/mruby/src/gc.c:1220 #2 0x0041d56b in incremental_gc_step (gc=0x5131fc, mrb=0x5131a0) at /tmp/mruby/src/gc.c:1246 #3 mrb_incremental_gc (mrb=0x5131a0) at /tmp/mruby/src/gc.c:1290 #4 0x0041daf9 in mrb_obj_alloc (mrb=0x5131a0, ttype=MRB_TT_CLASS, cls=0x0) at /tmp/mruby/src/gc.c:571 #5 0x00418e88 in boot_defclass (super=0x51ac20, mrb=0x5131a0) at /tmp/mruby/src/class.c:1352 #6 mrb_init_class (mrb=0x5131a0) at /tmp/mruby/src/class.c:2886 #7 0x00469e39 in mrb_init_core (mrb=0x5131a0) at /tmp/mruby/src/init.c:34 #8 0x00429f97 in init_gc_and_core (mrb=0x5131a0, opaque=0x0) at /tmp/mruby/src/state.c:35 #9 0x0041adeb in mrb_core_init_protect (mrb=0x5131a0, body=0x429f50 <init_gc_and_core>, opaque=0x0) at /tmp/mruby/src/error.c:575 #10 0x0042a14d in mrb_open_core (ud=0x0, f=0x429fb0 <mrb_default_allocf>) at /tmp/mruby/src/state.c:53 #11 mrb_open_allocf (ud=0x0, f=0x429fb0 <mrb_default_allocf>) at /tmp/mruby/src/state.c:92 #12 mrb_open () at /tmp/mruby/src/state.c:76 #13 0x0040c92c in main (argc=2, argv=0xbffff624) at /tmp/mruby/mrbgems/mruby-bin-mruby/tools/mruby/mruby.c:278
で、ここからがgdbの面白い所。レコードの方法を変更する。但し危い方法なので、途中のコメントにある様に、システムの設定を変更してあげる亊。
(gdb) record btrace bts Could not enable branch tracing for process 1698: You do not have permission to record the process. Try setting /proc/sys/kernel/perf_event_paranoid to 2 or less. ;;; root@deb:/home/sakae# echo 2 > /proc/sys/kernel/perf_event_paranoid (gdb) record btrace bts (gdb) c Continuing. ^C Program received signal SIGINT, Interrupt. 0xb7fd2559 in __kernel_vsyscall () (gdb) info record Active record target: record-btrace Recording format: Branch Trace Store. Buffer size: 64kB. Recorded 23295 instructions in 1292 functions (0 gaps) for thread 1 (process 1698). (gdb) record function-call-history /ilc 111 find_symbol inst 1938,1954 at /home/sakae/mruby/src/symbol.c:35,202 112 mrb_intern_cstr inst 1955,1963 at /home/sakae/mruby/src/symbol.c:212,271 113 mrb_define_method inst 1964,1978 at /home/sakae/mruby/src/class.c:787,801 114 mrb_define_method_raw inst 1979,1983 at /home/sakae/mruby/src/class.c:747 115 __x86.get_pc_thunk.bx inst 1984,1985 at 116 mrb_define_method_raw inst 1986,2008 at /home/sakae/mruby/src/class.c:747,777 117 mt_put inst 2009,2012 at /home/sakae/mruby/src/class.c:100 118 __x86.get_pc_thunk.si inst 2013,2014 at 119 mt_put inst 2015,2086 at /home/sakae/mruby/src/class.c:97,124 120 mrb_define_method_raw inst 2087,2102 at /home/sakae/mruby/src/class.c:779,1726 (gdb) record function-call-history /l 151 __memcmp_ssse3 at 152 find_symbol at /home/sakae/mruby/src/symbol.c:31,38 153 memcmp@plt at 154 __memcmp_ssse3 at 155 find_symbol at /home/sakae/mruby/src/symbol.c:35,202 156 mrb_intern_cstr at /home/sakae/mruby/src/symbol.c:212,271 157 mrb_define_method at /home/sakae/mruby/src/class.c:787,801 158 mrb_define_method_raw at /home/sakae/mruby/src/class.c:747 159 __x86.get_pc_thunk.bx at 160 mrb_define_method_raw at /home/sakae/mruby/src/class.c:747,777
こういう楽しい機能は、残念ながらリナでしか味わえない。gdbのinfoにもしっかりと説明されてた。まあ、しょうがないか。
つらつらコードを見ていたら、 symbol.c
に sym_intern
なんて言う、シンボルの収監手続を発見。場所取りもやってるから、追ってみろ。
ちょっと戯れる
コードを見ていたら、ちょっと面白い奴が見付かったので試してみる。まず、軽くruby語のソースを用意する。
vbox$ cat z.rb str = 'abcd' p str.size()
もう、見るだけで結果が分るコードだ。一応実行。
vbox$ bin/mruby z.rb 4
結果の出力に、ppは使えない。何せ弱小もとえ、極小なやつですから。
vbox$ bin/mrbc -v z.rb mruby 3.1.0 (2022-05-12) 00001 NODE_SCOPE: 00001 local variables: 00001 str 00001 NODE_BEGIN: 00001 NODE_ASGN: 00001 lhs: 00001 NODE_LVAR str 00001 rhs: 00001 NODE_STR "abcd" len 4 00002 NODE_FCALL: 00002 method='p' (26) 00002 args: 00002 NODE_CALL(.): 00002 NODE_LVAR str 00002 method='size' (293) irep 0x79929c00 nregs=5 nlocals=2 pools=1 syms=2 reps=0 ilen=17 local variable names: R1:str file: z.rb 1 000 STRING R1 L(0) ; abcd ; R1:str 2 003 MOVE R3 R1 ; R1:str 2 006 SEND R3 :size n=0 (0x00) 2 010 SSEND R2 :p n=1 (0x01) 2 014 RETURN R2 2 016 STOP
mrbcはmrubyから、実行機能を除いた、コンパイル機能だけのコマンドだ。そいつに -v を付けると、ruby語を、パースた結果を表示してくれる。そして次にそれを元にmatzさん設計の仮想コンピュータ様のアセンブラに変換してくれる。結果は、z,mrbってファイルに出力される。
勿論mrubyに -v を付けても良い。その場合は仮想CPUの実行結果まで表示される。なお、事前にコンパイルしておいたz.mrbをmrubyに与える亊も可能。その場合は、mrubyはコンパイルする手間が省けるので、ターンアラウンドは良くなる(それがmrbcの存在価値)。
vbox$ bin/mruby -v z.mrb mruby 3.1.0 (2022-05-12) irep 0x4ca00c80 nregs=5 nlocals=2 pools=1 syms=2 reps=0 ilen=17 local variable names: R1:str 000 STRING R1 L(0) ; abcd ; R1:str 003 MOVE R3 R1 ; R1:str 006 SEND R3 :size n=0 (0x00) 010 SSEND R2 :p n=1 (0x01) 014 RETURN R2 016 STOP 4
仮想CPUの命令語は、 The new bytecode で、公開されてるよ。(include/ops.h, opecode.h)
収監手続
上で出て来た収監手続をみておく。
vbox$ gdb -q bin/mruby Reading symbols from bin/mruby... (gdb) b sym_intern Breakpoint 1 at 0x709c4: file src/symbol.c, line 213. (gdb) r Starting program: /home/sakae/mruby/bin/mruby Breakpoint 1, sym_intern (mrb=0x57b5c000, name=0x1a780b3e "Proc", len=4, lit=<optimized out>) at src/symbol.c:213 213 sym_validate_len(mrb, len);
(gdb) bt #0 sym_intern (mrb=0x57b5c000, name=0x1a780b3e "Proc", len=4, lit=<optimized out>) at src/symbol.c:213 #1 0x1a7dfc41 in mrb_intern (mrb=0x57b5c000, name=0x1a780b3e "Proc", len=60142360) at src/symbol.c:260 #2 mrb_intern_cstr (mrb=0x57b5c000, name=0x1a780b3e "Proc") at src/symbol.c:272 #3 0x1a7c06d4 in mrb_define_class ( name=0x395b318 <error: Cannot access memory at address 0x395b318>, super=0x6eb5ffe8, mrb=<optimized out>) at src/class.c:545 #4 mrb_init_class (mrb=0x57b5c000) at src/class.c:2929 #5 0x1a7d92ce in mrb_init_core (mrb=0x57b5c000) at src/init.c:34 #6 0x1a7d8cc6 in init_gc_and_core (mrb=0x57b5c000, opaque=0x0) at src/state.c:34 #7 0x1a7c3f00 in mrb_core_init_protect (mrb=0x57b5c000, body=0x1a7d8c50 <init_gc_and_core>, opaque=0x0) at src/error.c:602 #8 0x1a7d8dae in mrb_open_core ( ud=<error reading variable: Cannot access memory at address 0x0>, f=<optimized out>) at src/state.c:52 #9 mrb_open_allocf ( ud=<error reading variable: Cannot access memory at address 0x0>, f=<optimized out>) at src/state.c:91 #10 mrb_open () at src/state.c:75 #11 0x1a7b661a in main (argc=1, argv=0xcf7c8334) at mrbgems/mruby-bin-mruby/tools/mruby/mruby.c:268
frame 11が何かと思ったら、mainで一番先に実行される mrb_open()
だった。なんか、凄い作りになってるな。こういうのもありか。
ここから掘り下げていくのも面白い。gemの機能が、固定化されてコードに埋まってる。