trace by debug.gem (2)
trace (call or line)
ふとdebug.gemのトレース機構にはcallの他にlineも有った亊を思い出した。どんな違いが有るか比べてみる亊にした。
外観比較
まずは、出来上がったログファイルのサイズ比較だな。
ob$ wc LOG* 1415163 8896921 165157996 LOGcall 449635 1798541 35993396 LOGline
圧倒的にlineの方が小さい。callは入口と出口でログされるから、line毎に比べて2倍の差が付くと予想してたんだけど、3倍近くになった。なんで?
ob$ head LOGline PID:36350 LineTracer (disabled) into: LOGline #th:1 #dpt:3 at /usr/local/lib/ruby/3.1/rubygems.rb:1220 #th:1 #dpt:3 at /usr/local/lib/ruby/3.1/rubygems.rb:1221 #th:1 #dpt:3 at /usr/local/lib/ruby/3.1/rubygems/specification.rb:1254 #th:1 #dpt:1 at /usr/local/bin/bundle:11
これ、すっきりしてるから、登場スクリプトを抽出するには便利そう。
ob$ head LOGcall
PID:30131 CallTracer (disabled) into: LOGcall
#th:1 #dpt:3 > Gem.find_unresolved_default_spec at /usr/local/lib/ruby/3.1/rubygems.rb:1219
#th:1 #dpt:4 > Hash#[] at /usr/local/lib/ruby/3.1/rubygems.rb:1220
#th:1 #dpt:4 < Hash#[] #=> nil at /usr/local/lib/ruby/3.1/rubygems.rb:1220
#th:1 #dpt:3 < Gem.find_unresolved_default_spec #=> nil at /usr/local/lib/ruby/3.1/rubygems.rb:1222
#th:1 #dpt:3 > Gem::Specification.unresolved_deps at /usr/local/lib/ruby/3.1/rubygems/specification.rb:1253
#th:1 #dpt:3 < Gem::Specification.unresolved_deps #=> {} at /usr/local/lib/ruby/3.1/rubygems/specification.rb:1255
#th:1 #dpt:2 > Array#first at /usr/local/bin/bundle:13
exec time
次は、実行時間にどれぐらい差が有るか? ログファイルのstatを出してみた。ファイルの作成時刻やら更新時刻やらが出てくるので、その差を取れば、実行時間になるな。下記は、見やすいように、行に展開した。
ob$ stat LOGcall 65280 51458 -rw-r--r-- 1 sakae wheel 206640 165157996 "May 11 14:03:32 2022" "May 11 14:15:01 2022" "May 11 14:15:01 2022" 8192 322752 0 LOGcall
で、不合理な時間差の計算ですよ。そういうのはコンピュータに押し付けてしまうのが良い。
irb(main):001:0> require 'time'
=> true
irb(main):002:0> st = Time.rfc2822('11 May 2022 14:03:32 +0900')
=> 2022-05-11 14:03:32 +0900
irb(main):003:0> sp = Time.rfc2822('11 May 2022 14:15:01 +0900')
=> 2022-05-11 14:15:01 +0900
irb(main):004:0> sp - st
=> 689.0
拡張ライブラリィーのtimeを入れると、RFC2822とかHTTP-date形式が扱えるようになる。 そのうちのrfc形式の方がオイラーには優しく写ったので使ってみた。文字列をTimeオブジェクトに変換するんだね。例では日付の前に曜日がツイているけど、無くてもOK。TZは飾りだけど、一応付けておかないと、エラーになる。
ob$ stat LOGline 65280 128642 -rw-r--r-- 1 sakae wheel 514128 35993396 "May 11 13:59:14 2022" "May 11 13:50:46 2022" "May 11 13:50:46 2022" 8192 70368 0 LOGline ob$ stat -r LOGline 65280 128642 0100644 1 1000 0 514128 35993396 1652245154 1652244646 1652244646 8192 70368 0 LOGline
実はstatにはrawモードが用意されてる。んで、それを見ればTimeオブジェクトの正体が一発で判明する。
irb(main):001:0> 1652244646 - 1652245154 => -508 irb(main):002:0> 508 / 60 => 8
which script use ?
ob$ cat LOGline | sed -e 's#.* at \(/.*\):.*#\1#' | sort | uniq -c | sort -nr | head 350391 /usr/local/lib/ruby/3.1/bundler/compact_index_client/cache.rb 8184 /usr/local/lib/ruby/3.1/rubygems/version.rb 5740 /usr/local/lib/ruby/3.1/rubygems/stub_specification.rb 5173 /usr/local/lib/ruby/3.1/psych/visitors/to_ruby.rb 4755 /usr/local/lib/ruby/3.1/psych/tree_builder.rb 3588 /usr/local/lib/ruby/3.1/rubygems/specification.rb 3067 /usr/local/lib/ruby/3.1/rubygems/requirement.rb 2904 /usr/local/lib/ruby/3.1/rubygems/resolver.rb 2735 /usr/local/lib/ruby/3.1/rubygems/resolver/molinillo/lib/molinillo/resolution.rb 2390 /usr/local/lib/ruby/3.1/rubygems/platform.rb
行き成りGemfileなんてのがアクセス・ランキングに出て来たんで、その前後を確認。やっぱりdslなんだな。
ob$ grep '/tmp/t/Gemfile' -n -C 4 LOGline LOGline-47952-#th:1 #dpt:19 at /usr/local/lib/ruby/3.1/bundler/dsl.rb:49 LOGline-47953-#th:1 #dpt:21 at /usr/local/lib/ruby/3.1/bundler/dsl.rb:49 LOGline:47954:#th:1 #dpt:21 at /tmp/t/Gemfile:3 LOGline-47955-#th:1 #dpt:22 at /usr/local/lib/ruby/3.1/bundler/plugin/dsl.rb:40 LOGline-47956-#th:1 #dpt:22 at /usr/local/lib/ruby/3.1/bundler/plugin/dsl.rb:41 -- LOGline-48245-#th:1 #dpt:24 at /usr/local/lib/ruby/3.1/bundler/source_list.rb:73 LOGline-48246-#th:1 #dpt:25 at /usr/local/lib/ruby/3.1/bundler/source_list.rb:12 LOGline:48247:#th:1 #dpt:21 at /tmp/t/Gemfile:5 LOGline-48248-#th:1 #dpt:22 at /usr/local/lib/ruby/3.1/bundler/plugin/dsl.rb:36 LOGline-48249-#th:1 #dpt:19 at /usr/local/lib/ruby/3.1/bundler/dsl.rb:57
BUG?の正体見たり
少し知恵を働かせて、grepと協調動作をさせてみた。分かり易い例を挙げる。
sakae@pen:/tmp/t$ grep -n -v '#th:1' -C 2 LOGcall | cut -b -120 : -- 113266-#th:1 #dpt:34> Kernel#freeze at /var/lib/gems/2.7.0/gems/bundler-2.3.12/lib/bundler/ruby_version.rb:52 113267-#th:1 #dpt:34< Kernel#freeze #=> / 113268: ruby\s 113269: ([\d.]+) # ruby ver... at /var/lib/gems/2.7.0/gems/bundler-2.3.12/lib/bundler/ruby_version.rb:52 113270-#th:1 #dpt:34> Module#private at /var/lib/gems/2.7.0/gems/bundler-2.3.12/lib/bundler/ruby_version.rb:113 113271-#th:1 #dpt:34< Module#private #=> Bundler::RubyVersion at /var/lib/gems/2.7.0/gems/bundler-2.3.12/lib/bundler/ru
該当するソースを閲覧。
ruby_version.rb
46 # @private 47 PATTERN = / 48 ruby\s 49 ([\d.]+) # ruby version 50 (?:p(-?\d+))? # optional patchlevel 51 (?:\s\((\S+)\s(.+)\))? # optional engine info 52 /xo.freeze
正規表現が返値として表示されてるだけ。幽霊の正体見たり、枯れすすき だったんですね。どうもお騒がせしました。まて、正規表現を文字列化するのが正解だろう。そんなメソッド知らないけど。
少しは反省しろよ。そうでないと、近頃のマスコミのように、自分の都合が良い部分だけを取出して、大騒ぎしてしまう輩になってしまうぞ。
sakae@pen:/tmp/t$ irb irb(main):001:0/ pat = /abc irb(main):002:0/ def/ => /abc ... irb(main):003:0> p pat /abc def/ => /abc def/ irb(main):004:0> p pat.to_s "(?-mix:abc\ndef)" => "(?-mix:abc\ndef)"
irbが勝手に推測して、候補を出してきてくれた。まあ、 to_s って、誰でも思いつくけど。
TAGS for emacs
折角なので、emacs用のTAGSを作ってみる。題材は2回目のbundle install のline traceのログ。
vbox$ cat LOG | sed -e 's#.* at \(/.*\):.*#\1#' >SLOG vbox$ cat SLOG | sort | uniq >ULOG vbox$ emacs ULOG vbox$ etags `cat ULOG`
ファイル名だけを取出し。ユニークなファイル名にまとめ。ゴミが多少混っているので除去。それを元にTAGSを作成。ファイル数が253ヶだったよ。
これでスイスイ行くはずなんだけど、、、、
/EXTd/opt/lib/ruby/3.1.0/bundler/vendor/uri/lib/uri/common.rb 18: RFC3986_PARSER /EXTd/opt/lib/ruby/3.1.0/bundler/vendor/uri/lib/uri/rfc3986_parser.rb 3: class RFC3986_Parser /EXTd/opt/lib/ruby/3.1.0/uri/common.rb 18: RFC3986_PARSER /EXTd/opt/lib/ruby/3.1.0/uri/rfc3986_parser.rb 3: class RFC3986_Parser
今のコンテキストが分っていないと、どれをみたらいいか迷うぞ。rdbgで案内してもらうのが楽でいいか。
でも、まあ、オーダーメードのTAGが簡単に作れる亊が分ったから佳とするか。但し、コンタミネーション(異物混入)には注意。まあ、医療分野みたいですから。
stat
上で出て来たstatを少し探検してみる。ソースが有るから、思い立ったら吉日さ。
manによると、Version 1 AT&T UNIX で掲載され Version 4 AT&T UNIXで消えて、 NetBSD 1.6で再登場。数奇な運命に翻弄されてる。
核は、 stat(2) を実行して、表示する亊だ。ファイルのメタ情報なんだな。
struct stat {
dev_t st_dev; /* inode's device */
ino_t st_ino; /* inode's number */
mode_t st_mode; /* inode protection mode */
nlink_t st_nlink; /* number of hard links */
uid_t st_uid; /* user ID of the file's owner */
gid_t st_gid; /* group ID of the file's group */
dev_t st_rdev; /* device type */
struct timespec st_atim; /* time of last access */
struct timespec st_mtim; /* time of last data modification */
struct timespec st_ctim; /* time of last file status change */
off_t st_size; /* file size, in bytes */
blkcnt_t st_blocks; /* blocks allocated for file */
blksize_t st_blksize; /* optimal blocksize for I/O */
u_int32_t st_flags; /* user defined flags for file */
u_int32_t st_gen; /* file generation number */
};
srcと対面
久し振りにソースを開いてみる。
#define SHELL_FORMAT \
"st_dev=%d st_ino=%i st_mode=%#p st_nlink=%l " \
"st_uid=%u st_gid=%g st_rdev=%r st_size=%z " \
"st_atime=%a st_mtime=%m st_ctime=%c " \
"st_blksize=%k st_blocks=%b st_flags=%f"
#define LINUX_FORMAT \
" File: \"%N\"%n" \
" Size: %-11z FileType: %HT%n" \
" Mode: (%01Mp%03OLp/%.10Sp) Uid: (%5u/%8Su) Gid: (%5g/%8Sg)%n" \
"Device: %Hd,%Ld Inode: %i Links: %l%n" \
"Access: %Sa%n" \
"Modify: %Sm%n" \
"Change: %Sc"
まず目についたのが、これ。BSDなシステムなのにリナ風にも表示出来るのか。ソースに埋め込んであるだけじゃなくて、もっと宣伝してあげればいいのに。と言う亊で、どんなスイッチでこのフォーマットを使うか、調べてみるね。
case 'x':
statfmt = LINUX_FORMAT;
if (timefmt == NULL)
timefmt = "%c";
break;
該当するmanの所を見ると、
-x Display information in a more verbose way.
何とまあ、控えめだ亊。それとも表立って Linuxと書きたくなかったか。
case SHOW_st_atime:
gottime = 1;
secs = st->st_atime;
nsecs = st->st_atimensec;
/* FALLTHROUGH */
アクセスタイムなんかもnsecの分解能で保持してるのか。これもそれも、きっとファイナンス部門の高速取引からの要求なんだろね。何だか、世の中せせこましいな。
kernel side
ファイルに対する操作をした時、どの様に反応するか、見ておく。
vm$ date; touch aa Fri May 13 05:42:20 JST 2022 vm$ echo now `date` >>aa vm$ date; cat aa Fri May 13 05:44:01 JST 2022 now Fri May 13 05:43:19 JST 2022 vm$ date; chmod 600 aa Fri May 13 05:44:53 JST 2022 vm$ stat -x aa File: "aa" Size: 33 FileType: Regular File Mode: (0600/-rw-------) Uid: ( 1000/ sakae) Gid: ( 1000/ sakae) Device: 0,0 Inode: 112682 Links: 1 Access: Fri May 13 05:44:01 2022 Modify: Fri May 13 05:43:19 2022 Change: Fri May 13 05:44:53 2022
そしてカーネル側へ。lstatなんてのも有るようだけど、まずは本流でチェック。
int
sys_stat(struct proc *p, void *v, register_t *retval)
{
struct sys_stat_args /* {
syscallarg(const char *) path;
syscallarg(struct stat *) ub;
B => } */ *uap = v;
return (dofstatat(p, AT_FDCWD, SCARG(uap, path), SCARG(uap, ub), 0));
}
実質は、 vfs_syscalls.c に丸投げ。
dofstatat(struct proc *p, int fd, const char *path, struct stat *buf, int flag)
{
struct stat sb;
int error, follow;
struct nameidata nd;
:
if ((error = namei(&nd)) != 0)
return (error);
=> error = vn_stat(nd.ni_vp, &sb, p);
vput(nd.ni_vp);
vn_stat を通過した所で、結果を確認。
(gdb) p sb
$12 = {
st_mode = 33133,
st_dev = 0,
st_ino = 52439,
st_nlink = 1,
st_uid = 0,
st_gid = 7,
st_rdev = 256720,
st_atim = {
tv_sec = 1652389270,
tv_nsec = 220699342
},
st_mtim = {
tv_sec = 1570899143,
tv_nsec = 0
},
st_ctim = {
tv_sec = 1581317013,
tv_nsec = 864459906
},
st_size = 13964,
st_blocks = 28,
st_blksize = 16384,
st_flags = 0,
st_gen = 1339754254,
__st_birthtim = {
tv_sec = 0,
tv_nsec = 0
}
}
思わせぶりな __st_birthtim なんてのが有るなあ。使われてはいないようだけど。
stat of Linux
リナでも潜れればいいんだけど、環境作りがとても大変なので諦め。代わりに今使ったOpenBSDのdisk-imageをstatして、お茶を濁しておく。
sakae@pen:~/sim/i386$ stat disk File: disk Size: 887160832 Blocks: 1732744 IO Block: 4096 regular file Device: 801h/2049d Inode: 1066059 Links: 1 Access: (0644/-rw-r--r--) Uid: ( 1000/ sakae) Gid: ( 1000/ sakae) Access: 2022-05-13 06:29:00.380652972 +0900 Modify: 2022-05-13 06:29:07.816730319 +0900 Change: 2022-05-13 06:29:07.816730319 +0900 Birth: 2020-03-02 07:03:59.258217893 +0900