らんだむな記憶

blogというものを体験してみようか!的なー

CRuby (3)

CRuby (2) - らんだむな記憶 の続き。

cgdb --args ./ruby test.rb 

して

(gdb) b process.c:4739
Breakpoint 1 at 0x133308: file process.c, line 4739.
(gdb) r

し、ブレークしたところで、

(gdb) b string.c:815
Breakpoint 2 at 0x5579b7878d2d: file string.c, line 815.

に加えて

(gdb) b string.c:432
Breakpoint 3 at 0x5579b7877e88: file string.c, line 432.

する。これは https://github.com/ruby/ruby/blob/v3_0_1/string.c#L432 の部分であり、test.rbputs "Hello, ruby! (こんにちは、ruby!)" 部分でブレークさせたい狙いである。

何回か c で進めると、

Breakpoint 3, rb_setup_fake_str (fake_str=0x7fffcf3ff510, name=0x5579b79c51d0 "puts", len=4, enc=0x5579b8c9389
0) at string.c:432
432         return setup_fake_str(fake_str, name, len, rb_enc_to_index(enc));

が出てくる。いい感じである。
n で行を進めていくと、そのうち https://github.com/ruby/ruby/blob/v3_0_1/io.c#L7927 が見えてくる。なるほどこれが puts の実装か。
ということで最初から

(gdb) b io.c:7927
Breakpoint 1 at 0x72e93: file io.c, line 7927.

で良いことが分かった。ここから流れを辿ると、

(gdb) b vm_eval.c:135
Breakpoint 2 at 0x564e7bbde121: file vm_eval.c, line 135.

という足がかりが得られることになる。そこから更に追跡すると、https://github.com/ruby/ruby/blob/v3_0_1/io.c#L7889 が見えてくるので、

(gdb) b io.c:7889
Breakpoint 1 at 0x72d14: file io.c, line 7889.

が特に(?)puts の実装であることが分かる。この行でブレークすると、以下のように

(gdb) p rbimpl_rstring_getmem(argv[0]).as
$1 = {heap = {len = 38, ptr = 0x5629e17a1310 "Hello, ruby! (\343\201\223\343\202\223\343\201\253\343\201\241\343\201\257\343\200\201ruby!)", aux = {capa = 94737875647880, shared = 94737875647880}}, ary = "&\000\000\000\000\000\000\000\020\023z\341)V\000\000\210\275l\341)V\000"}

argv[0]"Hello, ruby! (こんにちは、ruby!)" が来ていることが分かる。何故、こんな感じで p すれば良いかは、後続の https://github.com/ruby/ruby/blob/v3_0_1/io.c#L7905 における

RSTRING_LEN(line)

の中にステップインして実装を見ると想像がつく。なお、文字列のエンコードされた部分は 8 進法表記になっているので、 Python でも使って以下のようにすると、UTF-8 で符号化されていることが分かる。

>>> b = b'\343\201\223\343\202\223\343\201\253\343\201\241\343\201\257\343\200\201'
>>> print(b.decode('utf_8'))
こんにちは、
>>> b
b'\xe3\x81\x93\xe3\x82\x93\xe3\x81\xab\xe3\x81\xa1\xe3\x81\xaf\xe3\x80\x81'
>>> f"{3*64+4*8+3:x}"
'e3'

なお、test.rb の先頭を

# -*- coding: cp932 -*-

にして、cp932 として保存すると

(gdb) p rbimpl_rstring_getmem(argv[0]).as
$1 = {heap = {len = 32, ptr = 0x55c23bdd10b0 "Hello, ruby! (\202\261\202\361\202\311\202\277\202\315\201Aruby!)", aux = {capa = 94292716566560, shared = 94292716566560}}, ary = "\000\000\000\000\000\000\000\260\020\335;\302U\000\0008\340;\302U\000"}

となって、もう一度 Python で 16 進数として見ると、

>>> b = b'\202\261\202\361\202\311\202\277\202\315\201'
>>> b
b'\x82\xb1\x82\xf1\x82\xc9\x82\xbf\x82\xcd\x81'

みんなが大好きな “2 バイト文字” の CP932 で符号化されていることが分かる。

最後に、argv[0] は結局何であるかと言うと、https://github.com/ruby/ruby/blob/v3_0_1/include/ruby/internal/core/rstring.h#L73-L86 の構造体 struct RString へのポインタである。
これは、https://github.com/ruby/ruby/blob/v3_0_1/include/ruby/internal/core/rstring.h#L122-L137https://github.com/ruby/ruby/blob/v3_0_1/include/ruby/internal/core/rstring.h#L35https://github.com/ruby/ruby/blob/v3_0_1/include/ruby/internal/cast.h#L33 から読み取れる rbimpl_rstring_getmem の実装による。