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

This year's Index

Home