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 (本家)

仕事に活かせるmrubyの使い方を学ぼう

mruby/cの小さな世界

mruby/cで始めるオリジナルIoTデバイス作り (るびま)

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.csym_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の機能が、固定化されてコードに埋まってる。


This year's Index

Home