CubicLouve

Spring_MTの技術ブログ

特定のJSONの圧縮効率を調べてみる(snappy、gzip、xz、lz4、zstd)

snappyが入っているのは察してほしい

データの詳細は後で書く。(これも察して欲しい。だいぶ偏ったデータではある)

これから詳細をちゃんと書くが、一旦ここにおく

マシンスペック

iMac使っています。

% system_profiler SPHardwareDataType
Hardware:

    Hardware Overview:

      Model Name: iMac
      Model Identifier: iMac14,2
      Processor Name: Intel Core i5
      Processor Speed: 3.2 GHz
      Number of Processors: 1
      Total Number of Cores: 4
      L2 Cache (per Core): 256 KB
      L3 Cache: 6 MB
      Memory: 8 GB
      Boot ROM Version: IM142.0118.B00

% sysctl machdep.cpu.brand_string
machdep.cpu.brand_string: Intel(R) Core(TM) i5-4570 CPU @ 3.20GHz

結果

圧縮方式 圧縮時間(秒) 伸長時間(秒) 圧縮後サイズ(KB)
なし 159320(156KB)
snappy 18913(18K)
gzip 9982(9.7K)
xz 7024(6.9K)
lz4 23095(23K)
zstd 9960(9.7K)

実行時間の結果

github.com

圧縮時

Warming up --------------------------------------
              snappy    22.000  i/100ms
                gzip    20.000  i/100ms
                  xz     2.000  i/100ms
                 lz4    24.000  i/100ms
                zstd    21.000  i/100ms
Calculating -------------------------------------
              snappy    802.750  (±115.7%) i/s -      1.254k in   5.373229s
                gzip    295.138  (±40.7%) i/s -    980.000  in   5.055020s
                  xz     22.329  (± 4.5%) i/s -    112.000  in   5.030328s
                 lz4    949.545  (±110.1%) i/s -      1.224k in   5.053683s
                zstd    892.079  (±117.1%) i/s -      1.302k in   5.069196s

伸長時

Warming up --------------------------------------
              snappy   851.000  i/100ms
                gzip   414.000  i/100ms
                  xz   111.000  i/100ms
                 lz4   861.000  i/100ms
                zstd   577.000  i/100ms
Calculating -------------------------------------
              snappy     10.356k (±16.0%) i/s -     51.060k in   5.081812s
                gzip      4.655k (±10.2%) i/s -     23.184k in   5.039399s
                  xz      1.188k (± 7.7%) i/s -      5.994k in   5.080418s
                 lz4     10.312k (±12.0%) i/s -     50.799k in   5.000477s
                zstd      6.597k (± 8.4%) i/s -     32.889k in   5.023823s

デーモンとは

メモ書き程度に

デーモン

語源は悪魔のデーモンではなく、守護神のほうらしい。 デーモン (ソフトウェア) - Wikipedia

まあ、それはそれとして デーモン(daemon)は長時間動き続けるプロセスのことです。 APUEのデーモンより

  • ファイルモード作成時マスクをumask 0でリセットする

    • プロセスがファイルを作成するとき、マスクが許可をオフにすることを避けるため
  • 新しいセッションを作成する( 制御端末から切り離し、プロセスグループのリーダとなり、セッションのリーダーとなる)

    • forkして、親をexitする
    • setsidを呼ぶ
    • 制御端末がないので、出力の表示場所がない
      • ps のTTYが ? or ?? になっている
    • エラー状態の報告はsyslogを使う等する
  • cwdをルートディレクトリ(/)に変更する

  • 不必要なファイルディスクリプタをcloseしておく

  • 標準入力、標準出力、標準エラー出力を閉じる

    • 対話的なインターフェースから入力も受けない
    • 同じ端末装置に他のユーザーがログインしても、デーモンの出力が端末に現れないようにする

rubyだとProcess.daemonを呼ぶだけでデーモンになる

Refernce

2016年おせち料理

3年連続のおせち作りです。

買い出しは12/22から開始しています。

完成品はこちら

f:id:Spring_MT:20161231165556j:plain f:id:Spring_MT:20161231165615j:plain

重箱はここのを買いました。

かまぼこ、酢だこは既成品です。

買い物

クリスマス前に済ませておきましょう。 直前になると高くなったり、高級なものしか出回らなくなるので気をつけましょう

黒豆

黒豆は日持ちするので、安く済ますのであれば予め買っておきましょう。

いりこ

年末になると、田作り用のサイズが揃った高級ないりこが並ぶようになります。 通常打っている不揃いで安いものは売り切れてしますので、それでもよいのであれば予め買っておくのがよいでしょう。

かまぼこ

これも高級なものしか出回らなくなります。あまり日持ちがしないので、ギリギリをねらって買うとよいでしょう。 クリスマス前が良いタイミングです。

野菜類

レンコン、里芋、たけのこ、ごぼうは高くなります。 気をつけましょう。

ふるさと納税

レンコン、車海老はふるさと納税でいただきました。

だし

茅乃舎のあごだしを使いました。

www.kayanoya.com

白だし

これを使いました。

おせち

数の子(下ごしらえ)

材料
塩漬け数の子 180 g

29日(晩)

  1. 水一リットル、塩小さじ1の塩水をつくって10時間ほどつけておく

30日

  1. 薄皮を取って冷蔵庫に保存しておく

松前漬

材料
スルメイカ(小ぶり) 4枚
昆布 スルメと同量くらい
人参 1/2本
下ごしらえした数の子 80gくらい(3腹)

たれ

材料
300cc
醤油(関東濃い口) 150cc
みりん 150cc

30日

  1. 酒 300cc、醤油 150cc、みりん 150ccを火にかけひと煮立ちさせて冷ましておく
  2. スルメイカ、昆布はそのままキッチンバサミで5mm幅程度に切っておく(ここは適当)
  3. 人参は千切りにしておく
  4. 数の子は1センチくらいに切っておく
  5. 上記を全部混ぜる
  6. 半日位で混ぜなおす
  7. 出来上がり

最終的な出来栄え

いつも通り。 これは定番。

黒豆

材料
黒豆 200g

煮汁

材料
砂糖 170g
800c
醤油(濃口醤油) 大さじ 1
小さじ半分

29日

  1. 煮汁を合わせておいて、沸騰させる
  2. 黒豆を流水で洗う(やさしく洗う!皮がむけてしまう)
  3. 沸騰したら火を止めて、すぐに黒豆を投入
  4. 一晩寝かせる

30日

  1. 圧力鍋で強で15分火を入れる(前回の反省を活かして)

最終的な出来栄え

薄口を間違えて濃口にしてしまった。。。 豆はちょうどよい固さで、味も問題ないので来年からは濃口で問題ないかな。

田作り

材料
田作り(いりこ) 50g位(ライフで買った。一袋まるまる入れた)

たれ

材料
大さじ2
砂糖 大さじ2
醤油(九州 甘口) 大さじ1 + 1/2
みりん 大さじ1

30日

  1. 田作りをかりかりになるまで煎る -> 冷やす
  2. たれを中火にかけて、とろみがつくまで煮込む
  3. たれができたら、煎った田作りをいれてあえる
  4. 電子レンジの天板にクッキングシートをはっておきそれに広げて冷ます

最終的な出来栄え

美味しかった。 安いいりこだったが、苦味はないが旨味はしっかりあり、今後もこのいりこを使っていきたい。

なます

材料
大根 1/4本
人参 1/3本

たれ

材料
米酢 100cc
砂糖 大さじ3
だし 100cc

30日

  1. 大根と人参を千切りにする
  2. 千切りにした大根と人参を塩を振って水出しする
  3. 水がでたら、よく絞る
  4. たれを混ぜて完了

色どり的に 大根 3: 人参 1 がよい。

最終的な出来栄え

普通。 まあ、来年も同じようなレシピで。

お煮しめ

材料
里芋 小ぶりなものが12個位
しいたけ 8個
ニンジン 2/3本
絹さや 1パック(12個くらい)
ごぼう 1/2 本
レンコン 大きいのを1ブロック
こんにゃく 一枚
豆腐 一丁(厚揚げにした)
たけのこ 穂先だけのを4つ

味付け用調味料

材料
だし 700cc
醤油(薄口) 大さじ4
醤油(濃い口) 大さじ1
大さじ3
砂糖 大さじ2
みりん 大さじ1
小さじ1/2

ニンジンと絹さやにつける出汁

材料
だし 200cc
砂糖 小さじ1
1つまみ

大分前に仕込み済

里芋

  1. 里芋を洗わずに皮をむいて、洗って半分にして下茹で
  2. 冷凍

レンコン

  1. レンコンの飾り切り、酢水(深皿に水入れて、お酢を垂らす程度)につけておく
  2. レンコン下茹で(10分位)
  3. 冷凍

30日

  1. しいたけを水でもどしておく
  2. ニンジンを飾り切りする。ニンジン絹さやを塩入れて下茹で(10分くらい)
  3. ニンジンと絹さやにつける出汁を沸騰させて、粗熱をとっておく
  4. ゆでたニンジンと絹さやをニンジンと絹さやにつける出汁に浸けて冷蔵庫にいれておく
  5. ごぼうの皮むきして下茹でしておく(10分位)
  6. こんにゃくは下茹でしておく
  7. だし3カップ半でニンジンと絹さや以外の具材を10分炊く
  8. 出汁味付け用調味料を入れて更に15分炊く

最終的な出来栄え

出汁がきいて美味しかった。 冷凍の匂いも特に気にならなかった。

ニシンの昆布巻き

材料
身欠きにしん 6本
日高昆布 6枚(昆布はすぐにもどるのでにしんの量に合わせてもどせばよい)
かんぴょう 適量

だし

材料
昆布戻し汁 600cc
米酢 大さじ2
100cc

たれ

材料
砂糖 大さじ6(きび砂糖のほうがよさそう)
本みりん 大さじ4
しょうゆ 大さじ8(九州4 関東4)

28日(晩から)

  1. 身欠きにしんを米と昆布と一緒に水につける

29日(晩)

  1. 水を替えておく

30日

  1. 昆布は表面の汚れをさっと拭き取り、3〜4カップの水で戻す。柔らかく巻けるくらいになればよい。戻しすぎない。
  2. かんぴょうはさっと洗い、塩でもんで5分くらい水につけて戻しておく。これも戻しすぎないように。
  3. にしんは、汚れ、あたま、ウロコを洗い流し、一番茶を入れた湯で沸騰したら弱火にして約12分形が崩れない程度に煮る
  4. 水でさっと洗って使う
  5. 昆布でにしんを巻き、昆布の幅に応じてかんぴょうを2〜3ヶ所、2巻きして結ぶ。(かんぴょうはゆるめに巻く。)
  6. 圧力鍋に巻いた昆布を隙間なく並べ、だしの材料を入れて蓋をする。火にかけ、沸騰したら中火にし、12分圧力をかけ、火を止める。
  7. 圧が下がったらふたを開け、たれを加え、弱火で約20分煮含める。焦げないように注意

最終的な出来栄え

ちょうどよい! これで来年も作ろう。

海老の旨煮

材料
車海老 (有頭) 7尾

出汁

材料
だし 450cc(水でも可)
醤油(薄口) 50cc
みりん 50cc
50cc
砂糖 50cc

31日

  1. エビを解凍して下ごしらえをする(背わたを取る)
  2. エビを沸騰したお湯で2分間下茹でする。
  3. 鍋にたれを入れ中火にかける。沸騰したら火を弱める。
  4. 出汁ににエビをいれて冷やして完成(冷やしている時に、味が染み込む。)

最終的な出来栄え

車海老めっちゃうまい! 来年も車海老をもらおう。

伊達巻

家の玉子焼き器で二枚分くらいのレシピ

材料
ハンペン 1枚(80g)
玉子 4個

たれ

材料
みりん 小さじ1(よりちょっと多めくらい)
砂糖 大さじ2
少々
麺つゆ(3倍濃縮) 小さじ1と1/2
だし 60 cc位いれたかな
醤油 少々

31日

  1. 卵を4つ割り入れる
  2. フードプロセッサー(ナイフカッター使用)に、卵、ハンペン(手でちぎる程度でOK)、たれをいれて一分程度ミキサーにかける
  3. アルミホイルで蓋を作っておく
  4. 玉子焼き器(テフロン加工したやつ)に卵を流し込む(だいたい半分位をいれる)
  5. 強火で10秒かけた後に、アルミホイルの蓋をかぶせて弱火で10分位火にかける(匂いでこげていそうだったら)
  6. 表側が少し凸凹でもきにしない。あまりにも火が通っていない感じがしたらひっくり返す(最終手段、火を入れちゃうと出汁のジュワッと感がへります。)
  7. 鬼簾でまく

最終的な出来栄え

これももう特に手を入れず定番に。

豚の角煮

材料
豚バラブロック 268 g
ねぎ 青い部分を一本分
生姜 チューブのものを適量

たれ

材料
煮汁 200cc たらなければ水を足す
砂糖 大さじ 1
みりん 大さじ 1
醤油(九州) 大さじ 3
大さじ 3

30日

  1. ネギ、生姜、豚バラブロック、水 1000ccを圧力鍋にいれて沸騰後20分 加圧
  2. 豚バラを取り出し、カットした上で、たれを合わせて圧力なべで沸騰後10分加圧

最終的な出来栄え

たまたま肉が余っていたので作ったけど、来年も定番で作ってよいかも。

芋きんとん

材料
安納芋 小ぶりのを5つ
有塩バター 10g位

30日

  1. 水で濡らしたキッチンペーパーとアルミホイルで包んだ安納芋をグリルで40分じっくり焼く
  2. シリコンスチーマで2分電子レンジで加熱する
  3. 皮をむく
  4. バターをいれてよく混ぜる

最終的な出来栄え

安納芋が十分甘いので砂糖はいらない。 バターもいらないかも。

味付き数の子

材料
下ごしらえした数の子 適量

出汁

材料
200cc
白だし 大さじ 2
みりん 大さじ 2
本だし 顆粒 少々

30日

  1. 出汁をひと煮立ちさせる
  2. 出しを完全に冷やす
  3. 下ごしらえした数の子をつけて冷蔵庫にいれる

最終的な出来栄え

おつまみ

数の子からしマヨネーズ和え

材料
数の子(味付き) 適量
マヨネーズ 適量
からし 少々
  1. 数の子、マヨネーズ、和からしを混ぜて完了

最終的な出来栄え

おつまみとしてはよい。

来年への課題

きび砂糖を買っておく。 来年は工程の写真取る

gdbを使ってRuby 2.4.0のライブプロセスの情報を取得してみる

Ruby 2.4.0 リリース

2.4.0がリリースされたので、

spring-mt.hatenablog.com

と同じことをやってみようかと思います。

準備

AWSamazon Linux上で試しています。

# cat /etc/system-release
Amazon Linux AMI release 2016.09

gcc4.8.3gdb7.6.1を使ってます。

$ wget https://cache.ruby-lang.org/pub/ruby/2.4/ruby-2.4.0.tar.gz
$ tar zxf ruby-2.4.0.tar.gz
$ cd ruby-2.4.0
$ ./configure optflags="-O0" debugflags="-ggdb3" --prefix="${HOME}/.rbenv/versions/2.4.0-O0_g3"
$ make
$ make install

configureのサマリは下記の通りです。

---
Configuration summary for ruby version 2.4.0

   * Installation prefix: /home/ec2-user/.rbenv/versions/2.4.0-O0_g3
   * exec prefix:         ${prefix}
   * arch:                x86_64-linux
   * site arch:           ${arch}
   * RUBY_BASE_NAME:      ruby
   * ruby lib prefix:     ${libdir}/${RUBY_BASE_NAME}
   * site libraries path: ${rubylibprefix}/${sitearch}
   * vendor path:         ${rubylibprefix}/vendor_ruby
   * target OS:           linux
   * compiler:            gcc
   * with pthread:        yes
   * enable shared libs:  no
   * dynamic library ext: so
   * CFLAGS:              ${optflags} ${debugflags} ${warnflags}
   * LDFLAGS:             -L. -fstack-protector -rdynamic \
                          -Wl,-export-dynamic
   * optflags:            -O0 -fno-fast-math
   * debugflags:          -ggdb3
   * warnflags:           -Wall -Wextra -Wno-unused-parameter \
                          -Wno-parentheses -Wno-long-long \
                          -Wno-missing-field-initializers \
                          -Wno-tautological-compare \
                          -Wno-parentheses-equality \
                          -Wno-constant-logical-operand -Wno-self-assign \
                          -Wunused-variable -Wimplicit-int -Wpointer-arith \
                          -Wwrite-strings -Wdeclaration-after-statement \
                          -Wimplicit-function-declaration \
                          -Wdeprecated-declarations \
                          -Wno-packed-bitfield-compat \
                          -Wsuggest-attribute=noreturn \
                          -Wsuggest-attribute=format
   * strip command:       strip -S -x
   * install doc:         yes
   * man page type:       doc

---

gdbでアタッチするRubyプロセスを作る

まずは、かんたんなhttpサーバーを立ち上げてそのプロセスにアタッチしてみます。

今回は単純なhello worldを返すだけのhttpサーバーを立ててます。(pidは控えておいてください)

ソースは下記reposにあります。

rack-hello_world

アタッチしてみる

サクッとgdbでアタッチした体でいきます。

Cの世界

(gdb) p ruby_version
$1 = "2.4.0"

スレッド情報はどうでしょう。

(gdb) info threads
  Id   Target Id         Frame 
  2    Thread 0x7fe410aad700 (LWP 17067) "ruby-timer-thr" 0x00007fe40fc5f64d in poll () at ../sysdeps/unix/syscall-template.S:81
* 1    Thread 0x7fe410aa4740 (LWP 17058) "rackup" 0x00007fe40fc613c3 in select () at ../sysdeps/unix/syscall-template.S:81

メインスレッド(Rubyのスレッド)とタイマスレッドがいますね。

ここいらでRubyの世界にいきます。

Rubyの世界へ

まずは簡単にRubyのcallbackを表示してみます。

(gdb) call rb_vmdebug_stack_dump_raw_current()

そうすると、プロセスの標準出力(今回はrackupで立ち上げたプロセス)にrubyのCの関数呼出しも含まれて標準出力に表示されます。

 from /home/ec2-user/.rbenv/versions/2.4.0-O0_g3/bin/rackup:22:in `<main>'
    from /home/ec2-user/.rbenv/versions/2.4.0-O0_g3/bin/rackup:22:in `load'
    from /home/ec2-user/.rbenv/versions/2.4.0-O0_g3/lib/ruby/gems/2.4.0/gems/rack-2.0.1/bin/rackup:4:in `<top (required)>'
    from /home/ec2-user/.rbenv/versions/2.4.0-O0_g3/lib/ruby/gems/2.4.0/gems/rack-2.0.1/lib/rack/server.rb:147:in `start'
    from /home/ec2-user/.rbenv/versions/2.4.0-O0_g3/lib/ruby/gems/2.4.0/gems/rack-2.0.1/lib/rack/server.rb:296:in `start'
    from /home/ec2-user/.rbenv/versions/2.4.0-O0_g3/lib/ruby/gems/2.4.0/gems/rack-2.0.1/lib/rack/handler/webrick.rb:34:in `run'
    from /home/ec2-user/.rbenv/versions/2.4.0-O0_g3/lib/ruby/2.4.0/webrick/server.rb:158:in `start'
    from /home/ec2-user/.rbenv/versions/2.4.0-O0_g3/lib/ruby/2.4.0/webrick/server.rb:33:in `start'
    from /home/ec2-user/.rbenv/versions/2.4.0-O0_g3/lib/ruby/2.4.0/webrick/server.rb:171:in `block in start'
    from /home/ec2-user/.rbenv/versions/2.4.0-O0_g3/lib/ruby/2.4.0/webrick/server.rb:171:in `select'
-- Control frame information -----------------------------------------------
c:0011 p:---- s:0064 e:000063 CFUNC  :select
c:0010 p:0117 s:0056 e:000055 BLOCK  /home/ec2-user/.rbenv/versions/2.4.0-O0_g3/lib/ruby/2.4.0/webrick/server.rb:171
c:0009 p:0006 s:0046 e:000045 METHOD /home/ec2-user/.rbenv/versions/2.4.0-O0_g3/lib/ruby/2.4.0/webrick/server.rb:33
c:0008 p:0068 s:0042 e:000041 METHOD /home/ec2-user/.rbenv/versions/2.4.0-O0_g3/lib/ruby/2.4.0/webrick/server.rb:158
c:0007 p:0161 s:0036 e:000035 METHOD /home/ec2-user/.rbenv/versions/2.4.0-O0_g3/lib/ruby/gems/2.4.0/gems/rack-2.0.1/lib/rack/handler/webrick.rb:34
c:0006 p:0231 s:0028 E:0015a8 METHOD /home/ec2-user/.rbenv/versions/2.4.0-O0_g3/lib/ruby/gems/2.4.0/gems/rack-2.0.1/lib/rack/server.rb:296
c:0005 p:0016 s:0021 e:000020 METHOD /home/ec2-user/.rbenv/versions/2.4.0-O0_g3/lib/ruby/gems/2.4.0/gems/rack-2.0.1/lib/rack/server.rb:147
c:0004 p:0023 s:0016 e:000015 TOP    /home/ec2-user/.rbenv/versions/2.4.0-O0_g3/lib/ruby/gems/2.4.0/gems/rack-2.0.1/bin/rackup:4 [FINISH]
c:0003 p:---- s:0013 e:000012 CFUNC  :load
c:0002 p:0136 s:0008 E:0020a0 EVAL   /home/ec2-user/.rbenv/versions/2.4.0-O0_g3/bin/rackup:22 [FINISH]
c:0001 p:0000 s:0003 E:001850 (none) [FINISH]

最新のコールスタックの中身も見てみます。

(gdb) ruby_thread
running_thread set $ruby_thread 
(gdb) vm_frame_type $ruby_thread->cfp
There is no member named flag.

ん、cfp構造体の中からflagがなくなった?

d.hatena.ne.jp

ここで、rb_control_frame_t::flagのメンバをep[0]に移したとのこと。 https://github.com/ruby/ruby/blob/1407e52ba4915ed7f251dec548b5e68f2a5a6a6e/vm_core.h#L994

あと、vmのframe_typeの持ち方も変わっているので、前に定義したgdbvm_frame_type関数も変更しておきます。 ruby/vm_core.h at 1407e52ba4915ed7f251dec548b5e68f2a5a6a6e · ruby/ruby · GitHub

Ruby 2.4.0 for gdbinit · GitHub 再度アタッチ。 ついでにrb_vm_frame_method_entry()も一緒に見てみます。

(gdb) ruby_thread
running_thread set $ruby_thread 
(gdb) vm_frame_type $ruby_thread->cfp
vm_frame_type 55550001  CFUNC 
(gdb) rp rb_id2str(rb_vm_frame_method_entry($ruby_thread->cfp)->called_id)
[PROMOTED] T_STRING: "select" bytesize:6 (embed) encoding:2 coderange:7bit $1 = (struct RString *) 0x5635ba9a5010

ここまでくれば、2.3.3とほとんど変わってないですね。

前と同じように、4つ前のコールスタック(rubyのメソッド呼び出し)のコールスタックも見てみます。

(gdb) set $cfp = $ruby_thread->cfp + 4 # 4つ前のコールスタック
(gdb) vm_frame_type $cfp
vm_frame_type 11110001  METHOD 
(gdb) rp $cfp->iseq->body->location.path # ファイルパス
[PROMOTED] T_STRING: "/home/ec2-user/.rbenv/versions/2.4.0-O0_g3/lib/ruby/gems/2.4.0/gems/rack-2.0.1/lib/rack/handler/webrick.rb" bytesize:106 encoding:2 coderange:7bit $2 = (struct RString *) 0x5635ba951028
(gdb) p/d rb_vm_get_sourceline($cfp) # ファイルの行数
$3 = 34
(gdb) rb_p rb_iseq_disasm($cfp->iseq) # YARV命令列
(gdb) rb_p $cfp->ep[1] # 環境ポインタのデータの中身をdumpする

YARV命令列はこちら

== disasm: #<ISeq:run@/home/ec2-user/.rbenv/versions/2.4.0-O0_g3/lib/ruby/gems/2.4.0/gems/rack-2.0.1/lib/rack/handler/webrick.rb>
local table (size: 4, argc: 1 [opts: 1, rest: -1, post: 0, block: -1, kw: -1@-1, kwrest: -1])
[ 4] app<Arg>   [ 3] options<Opt=0>[ 2] environment[ 1] default_host
0000 newhash          0                                               (  25)
0002 setlocal_OP__WC__0 5
0004 trace            8
0006 trace            1                                               (  26)
0008 getinlinecache   15, <is:0>
0011 getconstant      :ENV
0013 setinlinecache   <is:0>
0015 opt_aref_with    <callinfo!mid:[], argc:1, ARGS_SIMPLE>, <callcache>, \"RACK_ENV\"
0019 dup              
0020 branchif         25
0022 pop              
0023 putstring        \"development\"
0025 setlocal_OP__WC__0 4
0027 trace            1                                               (  27)
0029 getlocal_OP__WC__0 4
0031 putstring        \"development\"
0033 opt_eq           <callinfo!mid:==, argc:1, ARGS_SIMPLE>, <callcache>
0036 branchunless     42
0038 putstring        \"localhost\"
0040 jump             43
0042 putnil           
0043 setlocal_OP__WC__0 3
0045 trace            1                                               (  29)
0047 getlocal_OP__WC__0 5
0049 putobject        :BindAddress
0051 getlocal_OP__WC__0 5
0053 putobject        :Host
0055 opt_send_without_block <callinfo!mid:delete, argc:1, ARGS_SIMPLE>, <callcache>
0058 dup              
0059 branchif         64
0061 pop              
0062 getlocal_OP__WC__0 3
0064 opt_aset         <callinfo!mid:[]=, argc:2, ARGS_SIMPLE>, <callcache>
0067 pop              
0068 trace            1                                               (  30)
0070 getlocal_OP__WC__0 5
0072 putobject        :Port
0074 dupn             2
0076 opt_aref         <callinfo!mid:[], argc:1, ARGS_SIMPLE>, <callcache>
0079 dup              
0080 branchif         91
0082 pop              
0083 putobject        8080
0085 opt_aset         <callinfo!mid:[]=, argc:2, ARGS_SIMPLE>, <callcache>
0088 pop              
0089 jump             93
0091 adjuststack      3
0093 trace            1                                               (  31)
0095 getinlinecache   107, <is:1>
0098 pop              
0099 putobject        Object
0101 getconstant      :WEBrick
0103 getconstant      :HTTPServer
0105 setinlinecache   <is:1>
0107 getlocal_OP__WC__0 5
0109 opt_send_without_block <callinfo!mid:new, argc:1, ARGS_SIMPLE>, <callcache>
0112 setinstancevariable :@server, <is:2>
0115 trace            1                                               (  32)
0117 getinstancevariable :@server, <is:2>
0120 putstring        \"/\"
0122 getinlinecache   133, <is:3>
0125 getconstant      :Rack
0127 getconstant      :Handler
0129 getconstant      :WEBrick
0131 setinlinecache   <is:3>
0133 getlocal_OP__WC__0 6
0135 opt_send_without_block <callinfo!mid:mount, argc:3, ARGS_SIMPLE>, <callcache>
0138 pop              
0139 trace            1                                               (  33)
0141 putself          
0142 opt_send_without_block <callinfo!mid:block_given?, argc:0, FCALL|ARGS_SIMPLE>, <callcache>
0145 branchunless     153
0147 getinstancevariable :@server, <is:2>
0150 invokeblock      <callinfo!argc:1, ARGS_SIMPLE>
0152 pop              
0153 trace            1                                               (  34)
0155 getinstancevariable :@server, <is:2>
0158 opt_send_without_block <callinfo!mid:start, argc:0, ARGS_SIMPLE>, <callcache>
0161 trace            16                                              (  35)
0163 leave                                                            (  34)

データはこちら。

#<WEBrick::HTTPServer:0x005635baae7978 @config={:BindAddress=>"localhost", :Port=>9292, :MaxClients=>100, :ServerType=>nil, :Logger=>#<WEBrick::Log:0x005635baae77e8 @level=4, @log=#<IO:<STDERR>>, 
@time_format="[%Y-%m-%d %H:%M:%S]">, :ServerSoftware=>"WEBrick/1.3.1 (Ruby/2.4.0/2016-12-24)", :TempDir=>"/tmp", :DoNotListen=>false, :StartCallback=>nil, :StopCallback=>nil, :AcceptCallback=>nil, :DoNotReverseLookup=>true, :ShutdownSocketWithoutClose=>false,
:RequestTimeout=>30, :HTTPVersion=>#<WEBrick::HTTPVersion:0x005635bafca738 @minor=1, @major=1>, :AccessLog=>[], :MimeTypes=>{"ai"=>"application/postscript", "asc"=>"text/plain", "avi"=>"video/x-msvideo", "bin"=>"application/octet-stream", "bmp"=>"image/bmp", "class"=>"application/octet-stream", "cer"=>"application/pkix-cert", 
"crl"=>"application/pkix-crl", "crt"=>"application/x-x509-ca-cert", "css"=>"text/css", "dms"=>"application/octet-stream", "doc"=>"application/msword", "dvi"=>"application/x-dvi", "eps"=>"application/postscript", "etx"=>"text/x-setext", "exe"=>"application/octet-stream", "gif"=>"image/gif", "htm"=>"text/html", "html"=>"text/html", "jpe"=>"image/jpeg", "jpeg"=>"image/jpeg", "jpg"=>"image/jpeg", "js"=>"application/javascript", "lha"=>"application/octet-stream", "lzh"=>"application/octet-stream", "mov"=>"video/quicktime", "mpe"=>"video/mpeg", "mpeg"=>"video/mpeg", "mpg"=>"video/mpeg", "pbm"=>"image/x-portable-bitmap", "pdf"=>"application/pdf", "pgm"=>"image/x-portable-graymap", "png"=>"image/png", "pnm"=>"image/x-portable-anymap", "ppm"=>"image/x-portable-pixmap", "ppt"=>"application/vnd.ms-powerpoint", "ps"=>"application/postscript", "qt"=>"video/quicktime", "ras"=>"image/x-cmu-raster", "rb"=>"text/plain", "rd"=>"text/plain", "rtf"=>"application/rtf", "sgm"=>"text/sgml", "sgml"=>"text/sgml", "svg"=>"image/svg+xml", "tif"=>"image/tiff", "tiff"=>"image/tiff", "txt"=>"text/plain", "xbm"=>"image/x-xbitmap", "xhtml"=>"text/html", "xls"=>"application/vnd.ms-excel", "xml"=>"text/xml", "xpm"=>"image/x-xpixmap", "xwd"=>"image/x-xwindowdump", "zip"=>"application/zip"}, 
:DirectoryIndex=>["index.html", "index.htm", "index.cgi", "index.rhtml"], :DocumentRoot=>nil, :DocumentRootOptions=>{:FancyIndexing=>true}, :RequestCallback=>nil, :ServerAlias=>nil, :InputBufferSize=>65536, :OutputBufferSize=>65536, :ProxyAuthProc=>nil, :ProxyContentHandler=>nil, :ProxyVia=>true, :ProxyTimeout=>true, :ProxyURI=>nil, :CGIInterpreter=>nil, :CGIPathEnv=>nil, :Escape8bitURI=>false, :environment=>"development", :pid=>nil, :config=>"/home/ec2-user/rack-hell_world/config.ru"}, @status=:Running, @logger=#<WEBrick::Log:0x005635baae77e8 @level=4, @log=#<IO:<STDERR>>, @time_format="[%Y-%m-%d %H:%M:%S]">, @tokens=#<Thread::SizedQueue:0x005635baae76d0>, @listeners=[#<TCPServer:fd 7>], @shutdown_pipe=[#<IO:fd 8>, #<IO:fd 9>], 
@http_version=#<WEBrick::HTTPVersion:0x005635bafca738 @minor=1, @major=1>, @mount_tab=#<WEBrick::HTTPServer::MountTable:0x005635baae67a8 @tab={""=>[Rack::Handler::WEBrick, [#<Rack::ContentLength:0x005635baaeb488 @app=#<Rack::Chunked:0x005635baaeb730 @app=#<Rack::CommonLogger:0x005635baaeb938 @app=#<Rack::ShowExceptions:0x005635bab2dfb8 @app=#<Rack::Lint:0x005635bab2e058 @app=#<Rack::TempfileReaper:0x005635bab2e120 @app=#<Application:0x005635bab3dda0>>, @content_length=nil>>,
@logger=#<IO:<STDERR>>>>>]]}, @scanner=/^()(?=\/|$)/>, @virtual_hosts=[]>

こちらも2.3.3のときと同じ感じでいけました。

cfp周りで変更が入っていそうなので、おいおい調べられればよいなあ。。。

Objective-Cのintとlongの最大値は同じという記事をみて

驚愕の事実。Objective-Cのintとlongの最大値は同じだった。 | 三度の飯とエレクトロン

を見て試してみました。

#import <Foundation/NSObject.h>
#import <stdio.h>

int main(void)
{
  int i = INT_MAX;
  NSLog(@"int i: %d", i);
  i++;
  NSLog(@"int i: %d", i);

  long l = (long)INT_MAX;
  NSLog(@"long l: %ld", l);
  l++;
  NSLog(@"long l: %ld", l);

  long long ll = (long long)INT_MAX;
  NSLog(@"long long ll: %lld", ll);
  ll++;
  NSLog(@"longlong ll: %lld", ll);                                                                                                

  return 0;
}
% clang -o long_test LongTest.mm -framework Foundation
% ./long_test
2014-12-08 21:00:57.396 long_test[79790:507] int i: 2147483647
2014-12-08 21:00:57.403 long_test[79790:507] int i: -2147483648
2014-12-08 21:00:57.403 long_test[79790:507] long l: 2147483647
2014-12-08 21:00:57.404 long_test[79790:507] long l: 2147483648
2014-12-08 21:00:57.404 long_test[79790:507] long long ll: 2147483647
2014-12-08 21:00:57.404 long_test[79790:507] longlong ll: 2147483648

ってなったけど、コンパイルするとき、アーキテクチャのデフォルト変わったんじゃないかと思って

% clang -v
Apple LLVM version 6.0 (clang-600.0.56) (based on LLVM 3.5svn)
Target: x86_64-apple-darwin13.3.0
Thread model: posix

64ビットやないか。。。

まあ、変わったんだよね、、きっと

32ビットでコンパイルしてみると、

% clang -o long_test LongTest.mm -framework Foundation -arch i386
./long_test
2014-12-08 21:04:29.950 long_test[79849:507] int i: 2147483647
2014-12-08 21:04:29.952 long_test[79849:507] int i: -2147483648
2014-12-08 21:04:29.952 long_test[79849:507] long l: 2147483647
2014-12-08 21:04:29.952 long_test[79849:507] long l: -2147483648
2014-12-08 21:04:29.953 long_test[79849:507] long long ll: 2147483647
2014-12-08 21:04:29.953 long_test[79849:507] longlong ll: 2147483648

オーバーフローしましたね。

まあ、iOS、64ビット化がデフォになるとはいえ、大きい数字使うなら、longじゃなくて、long longつかったほうが無難

gdbを使ってRuby(2.3.3)のライブプロセスの情報を取得する

Rubyのプロセスに対して、非破壊的に中をのぞいてみたいことが時々ありますよね???(詰まっているプロセスとか)

今回、gdbを使ってライブプロセスにアタッチして調べてみました。

準備

今回はAWSamazon Linux上で試しています。

# cat /etc/system-release
Amazon Linux AMI release 2016.09

gcc4.8.3gdb7.6.1を使ってます。

Rubyのバージョンは2.3.3を使いました。

予めrbenvをインストールしておき、rbenvでRubyは管理しておきます。

まずは、Rubyデバッグ用にビルドします。

ビルドオプションとして、最適化を切っておくのとgdbからデバッグ情報を使えるように指定します。

wget https://cache.ruby-lang.org/pub/ruby/2.3/ruby-2.3.3.tar.gz
tar zxf ruby-2.3.3.tar.gz 
cd ruby-2.3.3
./configure optflags="-O0" debugflags="-ggdb3" --prefix="${HOME}/.rbenv/versions/2.3.3-O0_g3"
make
make install

configureのオプションは ./configure --help で確認できます。

ちなみに最適化しない(-O0) + デバッグフラグつけてビルドしたRubyのバイナリ

最適化をかけて(-O3) + デバッグフラグつけていないrubyのバイナリの大きさはこれくらい違います。

  • 適化しない(-O0) + デバッグフラグつけた : 5.6MB
  • 最適化をかけて(-O3) + デバッグフラグつけていない : 約15MB

gdbでアタッチするRubyプロセスを作る

まずは、かんたんなhttpサーバーを立ち上げてそのプロセスにアタッチしてみます。

今回は単純なhello worldを返すだけのhttpサーバーを立ててます。(pidは控えておいてください)

ソースは下記reposにあります。

rack-hello_world

アタッチしてみる

Cの世界

では、gdbを使ってプロセスにアタッチしてみましょう。

gdbコマンドラインオプションで使いそうなのは下記の通りです。

  • -nw, -nowindows : 強制的にコマンドラインインターフェースを使う
  • -silent : 起動時のバージョン番号を表示しない
  • -x : コマンドを指定されたファイルから読み取って実行する
  • --args : コマンドライン上で実行ファイルより後ろに置かれたものを引数として実行ファイルへ渡す

今回Rubyのソースの中に入っている.gdbinitの内容にちょっと手を加えたgdbinitファイルを使いました。

手を加えた(追加のみですが)部分はこちら。

https://gist.github.com/SpringMT/dfc443f97804f61d1804d3c4a1f56625

アタッチするコマンドは下記の通りです。

gdb -nw -silent -x /PATH/TO/.gdbinit /PATH/TO/bin/ruby [PID]
例: gdb -nw -x ./ruby-2.3.3/.gdbinit -silent /home/ec2-user/.rbenv/versions/2.3.3_O0_ggdb3/bin/ruby 24676

アタッチした後で、

Missing separate debuginfos, use: debuginfo-install hoge

とかでたら、rootユーザーで

debuginfo-install hoge

をしましょう。

gdb経由でプロセスにアタッチしたら、setを使って設定を追加しておくと良いでしょう。

(今回はgdbinit内でやっています。)

(gdb) set print pretty on # 構造体の表示を見やすくします(一行ずつインデント付きで表示します。)
(gdb) set pagination off # ページングを切ります

アタッチしたら、まず手始めにRubyのバージョンを表示させてみましょう。

(gdb) p ruby_version
$1 = "2.3.3"

p はGBDのコマンドで、printエイリアスです。

print は変数、式の値を表示します。

ruby_versionはconst charで宣言されているグローバル変数です。

https://github.com/ruby/ruby/blob/202bbda2bf5f25343e286099140fb9282880ecba/version.c#L29

定義場所はlistコマンドで表示できます

(gdb) list ruby_version
24 const int ruby_api_version[] = {
25 RUBY_API_VERSION_MAJOR,
26 RUBY_API_VERSION_MINOR,
27 RUBY_API_VERSION_TEENY,
28 };
29 const char ruby_version[] = RUBY_VERSION;
30 const char ruby_release_date[] = RUBY_RELEASE_DATE;
31 const char ruby_platform[] = RUBY_PLATFORM;
32 const int ruby_patchlevel = RUBY_PATCHLEVEL;
33 const char ruby_description[] = RUBY_DESCRIPTION;

次に、今アタッチしているプロセスの環境変数をみてみましょう。

(gdb) show environ # environなんかポインタおかしい? environ[0]がでてないような。。。
LESS_TERMCAP_mb=
HOSTNAME=ip-172-30-0-109
TERM=xterm-256color
SHELL=/bin/bash
HISTSIZE=1000
・
・

スレッド情報はどうでしょう。

(gdb) info threads
Id Target Id Frame 
2 Thread 0x7fc266669700 (LWP 26309) "ruby-timer-thr" 0x00007fc26581b64d in poll () at ../sysdeps/unix/syscall-template.S:81
* 1 Thread 0x7fc266660740 (LWP 26300) "rackup" 0x00007fc26581d3c3 in select () at ../sysdeps/unix/syscall-template.S:81

メインスレッド(Rubyのスレッド)とタイマスレッドが見えます。

ここから、実際に実行されている実体に迫って行きます。

では、rackupのスレッドのbacktraceを情報をみてみましょう。

gdbのthreadコマンドでinfo threadsで表示されたIDを指定して、該当スレッドに移動し、backtraceコマンド(bt)を打つとCのバックトレース情報がみれます。

(gdb) thread 1
[Switching to thread 1 (Thread 0x7f416100d740 (LWP 6147))]
#0 0x00007f41601ca3c3 in select () at ../sysdeps/unix/syscall-template.S:81
81 T_PSEUDO (SYSCALL_SYMBOL, SYSCALL_NAME, SYSCALL_NARGS)
(gdb) backtrace 
#0 0x00007f41601ca3c3 in select () at ../sysdeps/unix/syscall-template.S:81
#1 0x0000556f9688b60a in rb_fd_select (n=9, readfds=0x7ffd5cbddc70, writefds=0x0, exceptfds=0x0, timeout=0x7ffd5cbdd8e0) at thread.c:3431
#2 0x0000556f96883f90 in native_fd_select (n=9, readfds=0x7ffd5cbddc70, writefds=0x0, exceptfds=0x0, timeout=0x7ffd5cbdd8e0, th=0x556f978c05d0) at thread_pthread.c:1090
#3 0x0000556f9688b86e in do_select (n=9, readfds=0x7ffd5cbddc70, writefds=0x0, exceptfds=0x0, timeout=0x7ffd5cbdd8e0) at thread.c:3570
#4 0x0000556f9688bb18 in rb_thread_fd_select (max=9, read=0x7ffd5cbddc70, write=0x0, except=0x0, timeout=0x7ffd5cbddc40) at thread.c:3643
#5 0x0000556f9676379a in select_internal (read=93937774090080, write=8, except=8, tp=0x7ffd5cbddc40, fds=0x7ffd5cbddc70) at io.c:8441
#6 0x0000556f96763bc3 in select_call (arg=140726159400016) at io.c:8511
#7 0x0000556f96727932 in rb_ensure (b_proc=0x556f96763b7d <select_call>, data1=140726159400016, e_proc=0x556f96763bc5 <select_end>, data2=140726159400016) at eval.c:914
#8 0x0000556f96764037 in rb_f_select (argc=4, argv=0x7f4160f0c188, obj=93937772561160) at io.c:8860
#9 0x0000556f96860559 in call_cfunc_m1 (func=0x556f96763f52 <rb_f_select>, recv=93937772561160, argc=4, argv=0x7f4160f0c188) at vm_insnhelper.c:1463
#10 0x0000556f9686107f in vm_call_cfunc_with_frame (th=0x556f978c05d0, reg_cfp=0x7f416100bd90, calling=0x7ffd5cbde930, ci=0x556f98160320, cc=0x556f98161240) at vm_insnhelper.c:1642
#11 0x0000556f9686119c in vm_call_cfunc (th=0x556f978c05d0, reg_cfp=0x7f416100bd90, calling=0x7ffd5cbde930, ci=0x556f98160320, cc=0x556f98161240) at vm_insnhelper.c:1737
#12 0x0000556f96865fcc in vm_exec_core (th=0x556f978c05d0, initial=0) at insns.def:994
#13 0x0000556f96877843 in vm_exec (th=0x556f978c05d0) at vm.c:1650
#14 0x0000556f96878455 in rb_iseq_eval (iseq=0x556f97acc3b0) at vm.c:1882
#15 0x0000556f9672ace7 in rb_load_internal0 (th=0x556f978c05d0, fname=93937774417160, wrap=0) at load.c:619
#16 0x0000556f9672ade6 in rb_load_internal (fname=93937774417160, wrap=0) at load.c:649
#17 0x0000556f9672b0d4 in rb_f_load (argc=1, argv=0x7f4160f0c048) at load.c:717
#18 0x0000556f96860559 in call_cfunc_m1 (func=0x556f9672aff4 <rb_f_load>, recv=93937772668480, argc=1, argv=0x7f4160f0c048) at vm_insnhelper.c:1463
#19 0x0000556f9686107f in vm_call_cfunc_with_frame (th=0x556f978c05d0, reg_cfp=0x7f416100bf90, calling=0x7ffd5cbdfe50, ci=0x556f97b9cdb0, cc=0x556f97b9cf60) at vm_insnhelper.c:1642
#20 0x0000556f9686119c in vm_call_cfunc (th=0x556f978c05d0, reg_cfp=0x7f416100bf90, calling=0x7ffd5cbdfe50, ci=0x556f97b9cdb0, cc=0x556f97b9cf60) at vm_insnhelper.c:1737
#21 0x0000556f96861fd0 in vm_call_method_each_type (th=0x556f978c05d0, cfp=0x7f416100bf90, calling=0x7ffd5cbdfe50, ci=0x556f97b9cdb0, cc=0x556f97b9cf60) at vm_insnhelper.c:2026
#22 0x0000556f968626f6 in vm_call_method (th=0x556f978c05d0, cfp=0x7f416100bf90, calling=0x7ffd5cbdfe50, ci=0x556f97b9cdb0, cc=0x556f97b9cf60) at vm_insnhelper.c:2161
#23 0x0000556f96862844 in vm_call_general (th=0x556f978c05d0, reg_cfp=0x7f416100bf90, calling=0x7ffd5cbdfe50, ci=0x556f97b9cdb0, cc=0x556f97b9cf60) at vm_insnhelper.c:2193
#24 0x0000556f96865fcc in vm_exec_core (th=0x556f978c05d0, initial=0) at insns.def:994
#25 0x0000556f96877843 in vm_exec (th=0x556f978c05d0) at vm.c:1650
#26 0x0000556f96878495 in rb_iseq_eval_main (iseq=0x556f97ad7d00) at vm.c:1893
#27 0x0000556f96725f97 in ruby_exec_internal (n=0x556f97ad7d00) at eval.c:245
#28 0x0000556f967260c0 in ruby_exec_node (n=0x556f97ad7d00) at eval.c:310
#29 0x0000556f96726093 in ruby_run_node (n=0x556f97ad7d00) at eval.c:302
#30 0x0000556f96723d55 in main (argc=2, argv=0x7ffd5cbe0668) at main.c:36

select() が呼ばれているところで止まっているところまではわかりました。

ここまでは、Cの世界ですが、Cの世界だけではわかりにくいので、今度はRubyの世界に降りていきましょう。

Rubyの世界へ

まずは簡単にRubyのcallbackを表示してみます。

rubyのrb_backtrace()を使って表示できます。

https://github.com/ruby/ruby/blob/9cbd6ee09770be3d73a17ab1195a094c59c9f9ee/vm_backtrace.c#L776

gdbのcallコマンドを使って、rb_backtrace()をよびます。

(gdb) call rb_backtrace()

そうすると、プロセスの標準エラー出力(今回はrackupで立ち上げたプロセス)にrubyのbacktraceが表示されます。

from /home/ec2-user/.rbenv/versions/2.3.3_O0_ggdb3/bin/rackup:22:in `<main>'
from /home/ec2-user/.rbenv/versions/2.3.3_O0_ggdb3/bin/rackup:22:in `load'
from /home/ec2-user/.rbenv/versions/2.3.3_O0_ggdb3/lib/ruby/gems/2.3.0/gems/rack-2.0.1/bin/rackup:4:in `<top (required)>'
from /home/ec2-user/.rbenv/versions/2.3.3_O0_ggdb3/lib/ruby/gems/2.3.0/gems/rack-2.0.1/lib/rack/server.rb:147:in `start'
from /home/ec2-user/.rbenv/versions/2.3.3_O0_ggdb3/lib/ruby/gems/2.3.0/gems/rack-2.0.1/lib/rack/server.rb:296:in `start'
from /home/ec2-user/.rbenv/versions/2.3.3_O0_ggdb3/lib/ruby/gems/2.3.0/gems/rack-2.0.1/lib/rack/handler/webrick.rb:34:in `run'
from /home/ec2-user/.rbenv/versions/2.3.3_O0_ggdb3/lib/ruby/2.3.0/webrick/server.rb:164:in `start'
from /home/ec2-user/.rbenv/versions/2.3.3_O0_ggdb3/lib/ruby/2.3.0/webrick/server.rb:33:in `start'
from /home/ec2-user/.rbenv/versions/2.3.3_O0_ggdb3/lib/ruby/2.3.0/webrick/server.rb:177:in `block in start'
from /home/ec2-user/.rbenv/versions/2.3.3_O0_ggdb3/lib/ruby/2.3.0/webrick/server.rb:177:in `select'

更にRubyとCの関数をあわせたbacktrace情報もみてましょう。

rb_backtrace()ではでてきませんでしたが、rb_vmdebug_stack_dump_raw_current()を使えばCの関数呼出しも含まれて標準エラー出力に表示されます。

https://github.com/ruby/ruby/blob/202bbda2bf5f25343e286099140fb9282880ecba/vm_dump.c#L178

実際にはRubyのコールスタックを見やすい形で全てdumpしてくれます。

(gdb) call rb_vmdebug_stack_dump_raw_current()
-- Control frame information -----------------------------------------------
c:0011 p:---- s:0053 e:000052 CFUNC :select
c:0010 p:0117 s:0046 e:000045 BLOCK /home/ec2-user/.rbenv/versions/2.3.3_O0_ggdb3/lib/ruby/2.3.0/webrick/server.rb:177
c:0009 p:0006 s:0037 e:000036 METHOD /home/ec2-user/.rbenv/versions/2.3.3_O0_ggdb3/lib/ruby/2.3.0/webrick/server.rb:33
c:0008 p:0068 s:0034 e:000033 METHOD /home/ec2-user/.rbenv/versions/2.3.3_O0_ggdb3/lib/ruby/2.3.0/webrick/server.rb:164
c:0007 p:0161 s:0029 e:000028 METHOD /home/ec2-user/.rbenv/versions/2.3.3_O0_ggdb3/lib/ruby/gems/2.3.0/gems/rack-2.0.1/lib/rack/handler/webrick.rb:34
c:0006 p:0231 s:0022 E:000558 METHOD /home/ec2-user/.rbenv/versions/2.3.3_O0_ggdb3/lib/ruby/gems/2.3.0/gems/rack-2.0.1/lib/rack/server.rb:296
c:0005 p:0016 s:0016 e:000015 METHOD /home/ec2-user/.rbenv/versions/2.3.3_O0_ggdb3/lib/ruby/gems/2.3.0/gems/rack-2.0.1/lib/rack/server.rb:147
c:0004 p:0023 s:0012 e:000011 TOP /home/ec2-user/.rbenv/versions/2.3.3_O0_ggdb3/lib/ruby/gems/2.3.0/gems/rack-2.0.1/bin/rackup:4 [FINISH]
c:0003 p:---- s:0010 e:000009 CFUNC :load
c:0002 p:0136 s:0006 E:000370 EVAL /home/ec2-user/.rbenv/versions/2.3.3_O0_ggdb3/bin/rackup:22 [FINISH]
c:0001 p:0000 s:0002 E:000aa0 (none) [FINISH]

CFUNCやMTHODとあるのが、スタックフレームのタイプとなり、CFUNCはC コードで書かれた関数のことになります。

MTHEODはRubyのメソッド呼出し、BLOCKはブロックの呼出しとなります。

次は各コールスタックの内容を見ていきましょう。

Rubyのコールスタックはrb_control_frame_t構造体に格納されています。

最新のコールスタックの中身をみてみます。

https://gist.github.com/SpringMT/dfc443f97804f61d1804d3c4a1f56625

ruby_threadvm_frame_type コマンドを使います。

(gdb) ruby_thread 
running_thread set $ruby_thread 
(gdb) vm_frame_type $ruby_thread->cfp 
vm_frame_type 61 CFUNC

CFUNCが返ってくるので、このコールスタックはCの関数呼出しをしていることがわかります。

Cの関数の場合、どういう関数が呼ばれているかはrb_vm_frame_method_entry()を使います。

https://github.com/ruby/ruby/blob/7bbab207cbe0c92777fcb30e21cde787ca9a3863/vm_insnhelper.c#L488

rprubyの.gdbinitに定義されているコマンドでよしなに値をRubyレベルの表示にしてくれます。

(gdb) rp rb_id2str(rb_vm_frame_method_entry($ruby_thread->cfp)->called_id)
[PROMOTED] T_STRING: "select" bytesize:6 (embed) encoding:2 coderange:7bit $19 = (struct RString *) 0x5644ed0393b8

これでselectが呼ばれていることがわかります。

では、ちょっと意図的ですが4つ前のコールスタック(rubyのメソッド呼び出し)のコールスタックも見てみましょう。

下記に定義してある、rb_iseq_disasmも使ってYARV命令列も表示してみます。(標準出力にでます。)

https://github.com/ruby/ruby/blob/4e60f99803e0b9ab49fb368f3660438934bf1a4e/iseq.c#L1402

iseq(instruction sequence、命令列)の部分はRubyの2.3系以降で大きな変更が入っているので、2.3未満のバージョンだと少し挙動が違う可能性があります。

make rb_iseq_t T_IMEMO object (type is imemo_iseq).

また、環境データは環境ポインタで示されるデータのindex 1に入っています。(下記参照)

https://github.com/ruby/ruby/blob/472959f9c20e7aff1776312d6c8e2236a52e29a7/vm_core.h#L994

(gdb) set $cfp = $ruby_thread->cfp + 4 # 4つ前のコールスタック
(gdb) vm_frame_type $cfp
vm_frame_type 11 METHOD # タイプ
(gdb) rp $cfp->iseq->body->location.path
[PROMOTED] T_STRING: "/home/ec2-user/.rbenv/versions/2.3.3_O0_ggdb3/lib/ruby/gems/2.3.0/gems/rack-2.0.1/lib/rack/handler/webrick.rb" bytesize:109 encoding:2 coderange:7bit $9 = (struct RString *) 0x56236cde86a0 # ファイルパス
(gdb) p/d rb_vm_get_sourceline($cfp)
$6 = 34 # ファイルの行数
(gdb) rb_p rb_iseq_disasm($cfp->iseq) # YARV命令列
(gdb) rb_p $cfp->ep[1] # 環境ポインタのデータの中身をdumpする

YARV命令列はこちら

== disasm: #<ISeq:run@/home/ec2-user/.rbenv/versions/2.3.3_O0_ggdb3/lib/ruby/gems/2.3.0/gems/rack-2.0.1/lib/rack/handler/webrick.rb>
local table (size: 5, argc: 1 [opts: 1, rest: -1, post: 0, block: -1, kw: -1@-1, kwrest: -1])
[ 5] app<Arg> [ 4] options<Opt=0>[ 3] environment[ 2] default_host
0000 newhash 0 ( 25)
0002 setlocal_OP__WC__0 4
0004 trace 8
0006 trace 1 ( 26)
0008 getinlinecache 15, <is:0>
0011 getconstant :ENV
0013 setinlinecache <is:0>
0015 opt_aref_with <callinfo!mid:[], argc:1, ARGS_SIMPLE>, <callcache>, \"RACK_ENV\"
0019 dup 
0020 branchif 25
0022 pop 
0023 putstring \"development\"
0025 setlocal_OP__WC__0 3
0027 trace 1 ( 27)
0029 getlocal_OP__WC__0 3
0031 putstring \"development\"
0033 opt_eq <callinfo!mid:==, argc:1, ARGS_SIMPLE>, <callcache>
0036 branchunless 42
0038 putstring \"localhost\"
0040 jump 43
0042 putnil 
0043 setlocal_OP__WC__0 2
0045 trace 1 ( 29)
0047 getlocal_OP__WC__0 4
0049 putobject :BindAddress
0051 getlocal_OP__WC__0 4
0053 putobject :Host
0055 opt_send_without_block <callinfo!mid:delete, argc:1, ARGS_SIMPLE>, <callcache>
0058 dup 
0059 branchif 64
0061 pop 
0062 getlocal_OP__WC__0 2
0064 opt_aset <callinfo!mid:[]=, argc:2, ARGS_SIMPLE>, <callcache>
0067 pop 
0068 trace 1 ( 30)
0070 getlocal_OP__WC__0 4
0072 putobject :Port
0074 dupn 2
0076 opt_aref <callinfo!mid:[], argc:1, ARGS_SIMPLE>, <callcache>
0079 dup 
0080 branchif 91
0082 pop 
0083 putobject 8080
0085 opt_aset <callinfo!mid:[]=, argc:2, ARGS_SIMPLE>, <callcache>
0088 pop 
0089 jump 93
0091 adjuststack 3
0093 trace 1 ( 31)
0095 getinlinecache 107, <is:1>
0098 pop 
0099 putobject Object
0101 getconstant :WEBrick
0103 getconstant :HTTPServer
0105 setinlinecache <is:1>
0107 getlocal_OP__WC__0 4
0109 opt_send_without_block <callinfo!mid:new, argc:1, ARGS_SIMPLE>, <callcache>
0112 setinstancevariable :@server, <is:2>
0115 trace 1 ( 32)
0117 getinstancevariable :@server, <is:3>
0120 putstring \"/\"
0122 getinlinecache 133, <is:4>
0125 getconstant :Rack
0127 getconstant :Handler
0129 getconstant :WEBrick
0131 setinlinecache <is:4>
0133 getlocal_OP__WC__0 5
0135 opt_send_without_block <callinfo!mid:mount, argc:3, ARGS_SIMPLE>, <callcache>
0138 pop 
0139 trace 1 ( 33)
0141 putself 
0142 opt_send_without_block <callinfo!mid:block_given?, argc:0, FCALL|ARGS_SIMPLE>, <callcache>
0145 branchunless 153
0147 getinstancevariable :@server, <is:5>
0150 invokeblock <callinfo!argc:1, ARGS_SIMPLE>
0152 pop 
0153 trace 1 ( 34)
0155 getinstancevariable :@server, <is:6>
0158 opt_send_without_block <callinfo!mid:start, argc:0, ARGS_SIMPLE>, <callcache>
0161 trace 16 ( 35)
0163 leave ( 34)

データはこちら。

#<WEBrick::HTTPServer:0x0055e7bb2e6960 @config={:ServerName=>"ip-172-30-0-109", :BindAddress=>"localhost", :Port=>"8000", :MaxClients=>100, :ServerType=>nil, :Logger=>#<WEBrick::Log:0x0055e7bb2e6550 @level=4, @log=#<IO:<STDERR>>, 
@time_format="[%Y-%m-%d %H:%M:%S]">, :ServerSoftware=>"WEBrick/1.3.1 (Ruby/2.3.3/2016-11-21)", :TempDir=>"/tmp", :DoNotListen=>false, :StartCallback=>nil, :StopCallback=>nil, :AcceptCallback=>nil, :DoNotReverseLookup=>nil, :ShutdownSocketWithoutClose=>false,
:RequestTimeout=>30, :HTTPVersion=>#<WEBrick::HTTPVersion:0x0055e7bb36c240 @minor=1, @major=1>, :AccessLog=>[], :MimeTypes=>{"ai"=>"application/postscript", "asc"=>"text/plain", "avi"=>"video/x-msvideo", "bin"=>"application/octet-stream", "bmp"=>"image/bmp", "class"=>"application/octet-stream", "cer"=>"application/pkix-cert", "crl"=>"application/pkix-crl", "crt"=>"application/x-x509-ca-cert", "css"=>"text/css", "dms"=>"application/octet-stream", "doc"=>"application/msword", "dvi"=>"application/x-dvi", "eps"=>"application/postscript", "etx"=>"text/x-setext", "exe"=>"application/octet-stream", "gif"=>"image/gif", "htm"=>"text/html", "html"=>"text/html", "jpe"=>"image/jpeg", "jpeg"=>"image/jpeg", "jpg"=>"image/jpeg", "js"=>"application/javascript", "lha"=>"application/octet-stream", "lzh"=>"application/octet-stream", "mov"=>"video/quicktime", "mpe"=>"video/mpeg", "mpeg"=>"video/mpeg", "mpg"=>"video/mpeg", "pbm"=>"image/x-portable-bitmap", "pdf"=>"application/pdf", "pgm"=>"image/x-portable-graymap", "png"=>"image/png", "pnm"=>"image/x-portable-anymap", "ppm"=>"image/x-portable-pixmap", "ppt"=>"application/vnd.ms-powerpoint", "ps"=>"application/postscript", "qt"=>"video/quicktime", "ras"=>"image/x-cmu-raster", "rb"=>"text/plain", "rd"=>"text/plain", "rtf"=>"application/rtf", "sgm"=>"text/sgml", "sgml"=>"text/sgml", "svg"=>"image/svg+xml", "tif"=>"image/tiff", "tiff"=>"image/tiff", "txt"=>"text/plain", "xbm"=>"image/x-xbitmap", "xhtml"=>"text/html", "xls"=>"application/vnd.ms-excel", "xml"=>"text/xml", "xpm"=>"image/x-xpixmap", "xwd"=>"image/x-xwindowdump", "zip"=>"application/zip"},
:DirectoryIndex=>["index.html", "index.htm", "index.cgi", "index.rhtml"], :DocumentRoot=>nil, :DocumentRootOptions=>{:FancyIndexing=>true}, :RequestCallback=>nil, :ServerAlias=>nil, :InputBufferSize=>65536,
:OutputBufferSize=>65536, :ProxyAuthProc=>nil, :ProxyContentHandler=>nil, :ProxyVia=>true, :ProxyTimeout=>true, :ProxyURI=>nil, :CGIInterpreter=>nil, :CGIPathEnv=>nil, :Escape8bitURI=>false, :environment=>"development", :pid=>nil, :config=>"/home/ec2-user/rack-hell_world/config.ru"}, 
@status=:Running, @logger=#<WEBrick::Log:0x0055e7bb2e6550 @level=4, @log=#<IO:<STDERR>>, @time_format="[%Y-%m-%d %H:%M:%S]">, @tokens=#<Thread::SizedQueue:0x0055e7bb2e64d8>, @listeners=[#<TCPServer:fd 7>],
@shutdown_pipe=[#<IO:fd 8>, #<IO:fd 9>],
@http_version=#<WEBrick::HTTPVersion:0x0055e7bb36c240 @minor=1, @major=1>, @mount_tab=#<WEBrick::HTTPServer::MountTable:0x0055e7bb2e3968 @tab={""=>[Rack::Handler::WEBrick, [#<Rack::ContentLength:0x0055e7bb2e8d00 @app=#<Rack::Chunked:0x0055e7bb2e8da0 @app=#<Rack::CommonLogger:0x0055e7bb2e8e68 @app=#<Rack::ShowExceptions:0x0055e7bb7efe48 @app=#<Rack::Lint:0x0055e7bb7efe98 @app=#<Rack::TempfileReaper:0x0055e7bb7efee8 @app=#<Application:0x0055e7bb17e280>>, @content_length=nil>>,
@logger=#<IO:<STDERR>>>>>]]}, @scanner=/^()(?=\/|$)/>, @virtual_hosts=[]>

ここまでくれば、処理の流れも追えますし、データもある程度見えているので、ここから大分デバッグもしやすくなるのではないでしょうか。

ライブプロセスの調査を非破壊的にやるにはいろいろ難しい面がありますが、その時の参考になればと思います。

参考にしたサイト・本