特定の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) |
実行時間の結果
圧縮時
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から開始しています。
完成品はこちら
重箱はここのを買いました。
かまぼこ、酢だこは既成品です。
買い物
クリスマス前に済ませておきましょう。 直前になると高くなったり、高級なものしか出回らなくなるので気をつけましょう
黒豆
黒豆は日持ちするので、安く済ますのであれば予め買っておきましょう。
いりこ
年末になると、田作り用のサイズが揃った高級ないりこが並ぶようになります。 通常打っている不揃いで安いものは売り切れてしますので、それでもよいのであれば予め買っておくのがよいでしょう。
かまぼこ
これも高級なものしか出回らなくなります。あまり日持ちがしないので、ギリギリをねらって買うとよいでしょう。 クリスマス前が良いタイミングです。
野菜類
レンコン、里芋、たけのこ、ごぼうは高くなります。 気をつけましょう。
ふるさと納税
レンコン、車海老はふるさと納税でいただきました。
だし
茅乃舎のあごだしを使いました。
白だし
これを使いました。
おせち
数の子(下ごしらえ)
材料 | 量 |
---|---|
塩漬け数の子 | 180 g |
29日(晩)
- 水一リットル、塩小さじ1の塩水をつくって10時間ほどつけておく
30日
- 薄皮を取って冷蔵庫に保存しておく
松前漬け
材料 | 量 |
---|---|
スルメイカ(小ぶり) | 4枚 |
昆布 | スルメと同量くらい |
人参 | 1/2本 |
下ごしらえした数の子 | 80gくらい(3腹) |
たれ
材料 | 量 |
---|---|
酒 | 300cc |
醤油(関東濃い口) | 150cc |
みりん | 150cc |
30日
- 酒 300cc、醤油 150cc、みりん 150ccを火にかけひと煮立ちさせて冷ましておく
- スルメイカ、昆布はそのままキッチンバサミで5mm幅程度に切っておく(ここは適当)
- 人参は千切りにしておく
- 数の子は1センチくらいに切っておく
- 上記を全部混ぜる
- 半日位で混ぜなおす
- 出来上がり
最終的な出来栄え
いつも通り。 これは定番。
黒豆
材料 | 量 |
---|---|
黒豆 | 200g |
煮汁
材料 | 量 |
---|---|
砂糖 | 170g |
水 | 800c |
醤油(濃口醤油) | 大さじ 1 |
塩 | 小さじ半分 |
29日
- 煮汁を合わせておいて、沸騰させる
- 黒豆を流水で洗う(やさしく洗う!皮がむけてしまう)
- 沸騰したら火を止めて、すぐに黒豆を投入
- 一晩寝かせる
30日
- 圧力鍋で強で15分火を入れる(前回の反省を活かして)
最終的な出来栄え
薄口を間違えて濃口にしてしまった。。。 豆はちょうどよい固さで、味も問題ないので来年からは濃口で問題ないかな。
田作り
材料 | 量 |
---|---|
田作り(いりこ) | 50g位(ライフで買った。一袋まるまる入れた) |
たれ
材料 | 量 |
---|---|
水 | 大さじ2 |
砂糖 | 大さじ2 |
醤油(九州 甘口) | 大さじ1 + 1/2 |
みりん | 大さじ1 |
30日
- 田作りをかりかりになるまで煎る -> 冷やす
- たれを中火にかけて、とろみがつくまで煮込む
- たれができたら、煎った田作りをいれてあえる
- 電子レンジの天板にクッキングシートをはっておきそれに広げて冷ます
最終的な出来栄え
美味しかった。 安いいりこだったが、苦味はないが旨味はしっかりあり、今後もこのいりこを使っていきたい。
なます
材料 | 量 |
---|---|
大根 | 1/4本 |
人参 | 1/3本 |
たれ
材料 | 量 |
---|---|
米酢 | 100cc |
砂糖 | 大さじ3 |
だし | 100cc |
30日
- 大根と人参を千切りにする
- 千切りにした大根と人参を塩を振って水出しする
- 水がでたら、よく絞る
- たれを混ぜて完了
色どり的に 大根 3: 人参 1 がよい。
最終的な出来栄え
普通。 まあ、来年も同じようなレシピで。
お煮しめ
材料 | 量 |
---|---|
里芋 | 小ぶりなものが12個位 |
しいたけ | 8個 |
ニンジン | 2/3本 |
絹さや | 1パック(12個くらい) |
ごぼう | 1/2 本 |
レンコン | 大きいのを1ブロック |
こんにゃく | 一枚 |
豆腐 | 一丁(厚揚げにした) |
たけのこ | 穂先だけのを4つ |
味付け用調味料
材料 | 量 |
---|---|
だし | 700cc |
醤油(薄口) | 大さじ4 |
醤油(濃い口) | 大さじ1 |
酒 | 大さじ3 |
砂糖 | 大さじ2 |
みりん | 大さじ1 |
塩 | 小さじ1/2 |
ニンジンと絹さやにつける出汁
材料 | 量 |
---|---|
だし | 200cc |
砂糖 | 小さじ1 |
塩 | 1つまみ |
大分前に仕込み済
里芋
- 里芋を洗わずに皮をむいて、洗って半分にして下茹で
- 冷凍
レンコン
- レンコンの飾り切り、酢水(深皿に水入れて、お酢を垂らす程度)につけておく
- レンコン下茹で(10分位)
- 冷凍
30日
- しいたけを水でもどしておく
- ニンジンを飾り切りする。ニンジン絹さやを塩入れて下茹で(10分くらい)
ニンジンと絹さやにつける出汁
を沸騰させて、粗熱をとっておく- ゆでたニンジンと絹さやを
ニンジンと絹さやにつける出汁
に浸けて冷蔵庫にいれておく - ごぼうの皮むきして下茹でしておく(10分位)
- こんにゃくは下茹でしておく
- だし3カップ半でニンジンと絹さや以外の具材を10分炊く
- 出汁味付け用調味料を入れて更に15分炊く
最終的な出来栄え
出汁がきいて美味しかった。 冷凍の匂いも特に気にならなかった。
ニシンの昆布巻き
材料 | 量 |
---|---|
身欠きにしん | 6本 |
日高昆布 | 6枚(昆布はすぐにもどるのでにしんの量に合わせてもどせばよい) |
かんぴょう | 適量 |
だし
材料 | 量 |
---|---|
昆布戻し汁 | 600cc |
米酢 | 大さじ2 |
酒 | 100cc |
たれ
材料 | 量 |
---|---|
砂糖 | 大さじ6(きび砂糖のほうがよさそう) |
本みりん | 大さじ4 |
しょうゆ | 大さじ8(九州4 関東4) |
28日(晩から)
- 身欠きにしんを米と昆布と一緒に水につける
29日(晩)
- 水を替えておく
30日
- 昆布は表面の汚れをさっと拭き取り、3〜4カップの水で戻す。柔らかく巻けるくらいになればよい。戻しすぎない。
- かんぴょうはさっと洗い、塩でもんで5分くらい水につけて戻しておく。これも戻しすぎないように。
- にしんは、汚れ、あたま、ウロコを洗い流し、一番茶を入れた湯で沸騰したら弱火にして約12分形が崩れない程度に煮る
- 水でさっと洗って使う
- 昆布でにしんを巻き、昆布の幅に応じてかんぴょうを2〜3ヶ所、2巻きして結ぶ。(かんぴょうはゆるめに巻く。)
- 圧力鍋に巻いた昆布を隙間なく並べ、だしの材料を入れて蓋をする。火にかけ、沸騰したら中火にし、12分圧力をかけ、火を止める。
- 圧が下がったらふたを開け、たれを加え、弱火で約20分煮含める。焦げないように注意
最終的な出来栄え
ちょうどよい! これで来年も作ろう。
海老の旨煮
材料 | 量 |
---|---|
車海老 (有頭) | 7尾 |
出汁
材料 | 量 |
---|---|
だし | 450cc(水でも可) |
醤油(薄口) | 50cc |
みりん | 50cc |
酒 | 50cc |
砂糖 | 50cc |
31日
- エビを解凍して下ごしらえをする(背わたを取る)
- エビを沸騰したお湯で2分間下茹でする。
- 鍋にたれを入れ中火にかける。沸騰したら火を弱める。
- 出汁ににエビをいれて冷やして完成(冷やしている時に、味が染み込む。)
最終的な出来栄え
車海老めっちゃうまい! 来年も車海老をもらおう。
伊達巻
家の玉子焼き器で二枚分くらいのレシピ
材料 | 量 |
---|---|
ハンペン | 1枚(80g) |
玉子 | 4個 |
たれ
材料 | 量 |
---|---|
みりん | 小さじ1(よりちょっと多めくらい) |
砂糖 | 大さじ2 |
塩 | 少々 |
麺つゆ(3倍濃縮) | 小さじ1と1/2 |
だし | 60 cc位いれたかな |
醤油 | 少々 |
31日
- 卵を4つ割り入れる
- フードプロセッサー(ナイフカッター使用)に、卵、ハンペン(手でちぎる程度でOK)、たれをいれて一分程度ミキサーにかける
- アルミホイルで蓋を作っておく
- 玉子焼き器(テフロン加工したやつ)に卵を流し込む(だいたい半分位をいれる)
- 強火で10秒かけた後に、アルミホイルの蓋をかぶせて弱火で10分位火にかける(匂いでこげていそうだったら)
- 表側が少し凸凹でもきにしない。あまりにも火が通っていない感じがしたらひっくり返す(最終手段、火を入れちゃうと出汁のジュワッと感がへります。)
- 鬼簾でまく
最終的な出来栄え
これももう特に手を入れず定番に。
豚の角煮
材料 | 量 |
---|---|
豚バラブロック | 268 g |
ねぎ | 青い部分を一本分 |
生姜 | チューブのものを適量 |
たれ
材料 | 量 |
---|---|
煮汁 | 200cc たらなければ水を足す |
砂糖 | 大さじ 1 |
みりん | 大さじ 1 |
醤油(九州) | 大さじ 3 |
酒 | 大さじ 3 |
30日
- ネギ、生姜、豚バラブロック、水 1000ccを圧力鍋にいれて沸騰後20分 加圧
- 豚バラを取り出し、カットした上で、たれを合わせて圧力なべで沸騰後10分加圧
最終的な出来栄え
たまたま肉が余っていたので作ったけど、来年も定番で作ってよいかも。
芋きんとん
材料 | 量 |
---|---|
安納芋 | 小ぶりのを5つ |
有塩バター | 10g位 |
30日
- 水で濡らしたキッチンペーパーとアルミホイルで包んだ安納芋をグリルで40分じっくり焼く
- シリコンスチーマで2分電子レンジで加熱する
- 皮をむく
- バターをいれてよく混ぜる
最終的な出来栄え
安納芋が十分甘いので砂糖はいらない。 バターもいらないかも。
味付き数の子
材料 | 量 |
---|---|
下ごしらえした数の子 | 適量 |
出汁
材料 | 量 |
---|---|
水 | 200cc |
白だし | 大さじ 2 |
みりん | 大さじ 2 |
本だし | 顆粒 少々 |
30日
- 出汁をひと煮立ちさせる
- 出しを完全に冷やす
- 下ごしらえした数の子をつけて冷蔵庫にいれる
最終的な出来栄え
おつまみ
数の子のからしマヨネーズ和え
材料 | 量 |
---|---|
数の子(味付き) | 適量 |
マヨネーズ | 適量 |
和からし | 少々 |
最終的な出来栄え
おつまみとしてはよい。
来年への課題
きび砂糖を買っておく。 来年は工程の写真取る
gdbを使ってRuby 2.4.0のライブプロセスの情報を取得してみる
2.4.0がリリースされたので、
と同じことをやってみようかと思います。
準備
# cat /etc/system-release Amazon Linux AMI release 2016.09
$ 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にあります。
アタッチしてみる
サクッと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がなくなった?
ここで、rb_control_frame_t::flagのメンバをep[0]に移したとのこと。 https://github.com/ruby/ruby/blob/1407e52ba4915ed7f251dec548b5e68f2a5a6a6e/vm_core.h#L994
あと、vmのframe_typeの持ち方も変わっているので、前に定義したgdbのvm_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を使ってライブプロセスにアタッチして調べてみました。
準備
# cat /etc/system-release Amazon Linux AMI release 2016.09
Rubyのバージョンは2.3.3
を使いました。
予めrbenvをインストールしておき、rbenvで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のバイナリの大きさはこれくらい違います。
gdbでアタッチするRubyプロセスを作る
まずは、かんたんなhttpサーバーを立ち上げてそのプロセスにアタッチしてみます。
今回は単純なhello world
を返すだけのhttpサーバーを立ててます。(pidは控えておいてください)
ソースは下記reposにあります。
アタッチしてみる
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_thread
とvm_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
rp
はrubyの.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=[]>
ここまでくれば、処理の流れも追えますし、データもある程度見えているので、ここから大分デバッグもしやすくなるのではないでしょうか。
ライブプロセスの調査を非破壊的にやるにはいろいろ難しい面がありますが、その時の参考になればと思います。