2012年09月

最終出社日にYAPC::Asia 2012 に参加してきた

こんにちは、@sonots です。ブログを書くまでが YAPC::Asia とのことなので、私も書いておきます。

09/28(金)、09/29(土) と日本で最大規模の perl の祭典である YAPC::Asia 2012 が開催されたので、参加してきました!私は今回が初参加。

元々 rubyist だったというものあって、ちゃんと吸収できるか不安だったのですが、そんなものは吹っ飛ばすような熱気と一体感があって、楽しませていただきました。id:koba04さんが、

YAPCのPはPerlですが、Programmingなんじゃないかと思ったYAPC::ASIA2012でした。

と言ってましたが、まさしくその通りで、perl の話ばかりではなく、他の言語の話やインフラの話なんかもしていて、perl 書いてないから参加してなかったというのは今までもったいなかったなぁ、と悔やんでおります。

皆さんのトークを聞いていて、自分も「話したい熱」がフツフツと沸いてきました。私も来年にはなにか発表するために壇上に登りたいですね!パネルディスカッションで@miyagawaさんが言っていた「カンファレンスドリブン」で、これから1年かけてなにかネタを作ってみます!

ところで、昨日の記事で書かせていただきましたが、私は09/28(金)が最終出社日でした!YAPC::Asiaへの参加が私の最後の業務だったことになります!すごい!

最終出社日にも関わらず、私のわがままを通して、午後からYAPC::Asiaに行かせてくれた職場の上司には大変感謝しております!この場を借りてお礼申し上げます。皆さんにも応援メッセージを頂けて、本当に励みになります。頑張ります。また、新しい職場である DeNA の方々に挨拶できて良かったです!今後ともよろしくお願いいたします。

最後に、YAPC::Asiaの運営の方々、発表者の皆様、お疲れさまでした。Perl のコミュニティって素晴らしい!本当にありがとうございました。

PS. 9月28日は奥さんの誕生日でもありました。誕生日おめでとう!

退職しました&DeNAに就職しました

YAPC::Asiaですね!

本日9月28日付けで、日本に帰国してから3年半の間勤めさせていただいた会社を退職しました。

いわゆるメーカー系子会社というものに分類される会社でして、最初の2年間は Mac 用のスキャナドライバの開発に携わり(組み込みですね)、その後の1年半は、Rails を使ってウェブプラットフォームの開発をしていました。ETロボコンというプログラミングコンテストにも参加して、ロボットと戯れたりもしましたね。

その直近1年半では、親会社のほうに出向して、Webのお仕事を立ち上げからリリース、そして運用まで一通り経験させて頂きました。アジャイルのコンサルタントをお願いして、アジャイル開発の経験をつませて頂いたり、Chef を使ってサーバ構築を自動化したり、Jenkins を使って自動テスト、自動デプロイのパイプラインを作って継続的インテグレーションしてみたり、システムアーキテクトみたいないことをさせて頂いたり、テストを書かない奴はクズ、とか言ってみたりモヒカンぽいことも一杯させて頂きました。低レベルレイヤーから高レベルレイヤーまで幅広く経験させていただき、様々な知見を得ることができました。本当にありがとうございました。

この頃の話はサムライ・エピソードという書籍にも少し載っていますので、興味のある方は是非ご購入を検討してみてください!(宣伝です!)

サムライ・エピソード
26人のサムライ達
達人出版会
発行日: 2012-09-14
対応フォーマット: EPUB, PDF

話を戻しまして、10月1日からは、アプリ開発者からインフラエンジニアに転身して、DeNAのインフラを支える立場に回ります。今までは Dev 側で DevOps な勉強会に出席させていただいていたのですが、これからは Ops 側になるのか、と考えるとなかなか感慨深いものがありますね。

DeNAでは、前職ではできなかった大規模インフラの運用経験を積みながら、自動化のためのスクリプトを書いたり、fluen なんたらのプラグインを書いたり、なんだったら何か新しく DevOps なフレームワークを作ってみたり、@riywoさんが作ったTouryoを勝手にgithubに挙げてみたりとか(嘘)、そんなお仕事を Dev:Ops = 4:6 ぐらいの比率でできたらいいなぁと思っております(まず、何をやることになるかは知らないんですけどね!)。mobageを支える技術を持っていらっしゃるみなさまと一緒に働けることを、今から楽しみにしております。

というわけでみなさま、どうぞよろしくお願いいたします。

tcpdump コマンドの使いかたをまとめてみた

パケットキャプチャツールである tcpdump の使い方を整理しておきます。自分用メモ。

tcpdump の基本

まず実行してみる

$ sudo tcpdump
12:46:26.850710 IP 192.168.0.10.54181 > nrt19s02-in-f24.1e100.net.http: Flags [.], seq 3579718551:3579719953, ack 1130673671, win 8192, options [nop,nop,TS val 750304771 ecr 3577990075], length 1402
12:46:26.850712 IP 192.168.0.10.54181 > nrt19s02-in-f24.1e100.net.http: Flags [P.], seq 1402:1461, ack 1, win 8192, options [nop,nop,TS val 750304771 ecr 3577990075], length 59
12:46:26.901212 IP 192.168.0.10.54181 > nrt19s02-in-f24.1e100.net.http: Flags [.], ack 802, win 8141, options [nop,nop,TS val 750304820 ecr 3578009852], length 0
12:46:26.906398 IP 192.168.0.10.54139 > nrt19s02-in-f23.1e100.net.https: Flags [P.], seq 1468315812:1468315849, ack 1338670021, win 8192, options [nop,nop,TS val 750304825 ecr 3578007435], length 37
12:46:26.906514 IP 192.168.0.10.54139 > nrt19s02-in-f23.1e100.net.https: Flags [.], seq 37:1439, ack 1, win 8192, options [nop,nop,TS val 750304825 ecr 3578007435], length 1402
12:46:26.906515 IP 192.168.0.10.54139 > nrt19s02-in-f23.1e100.net.https: Flags [P.], seq 1439:1681, ack 1, win 8192, options [nop,nop,TS val 750304825 ecr 3578007435], length 242
12:46:26.915643 IP 192.168.0.10.54139 > nrt19s02-in-f23.1e100.net.https: Flags [.], ack 38, win 8189, options [nop,nop,TS val 750304834 ecr 3578026165], length 0

パケットのヘッダ情報が垂れ流されます。

いつ どこから(IPおよびポート) > どこへ(IPおよびポート): どんなパケットか

が表示されています。たとえば

21:48:36.476894 IP 192.168.24.73.4697 > 192.168.24.62.http: S 1751526227:1751526227(0) win 65535 
21:48:36.477798 IP 192.168.24.62.http > 192.168.24.73.4697: S 1906616760:1906616760(0) ack 1751526228 win 5840 
21:48:36.476970 IP 192.168.24.73.4697 > 192.168.24.62.http: . ack 1 win 65535

こんなかんじだったりすると、これがいわゆる TCP の3ウェイハンドシェイクになっていて、SYN 送信(S) -> SYN/ACK 受信(S/ack) -> ACK 送信(ack)になっているわけです。

見方については TCPDUMPの出力を見てみよう のサイトが詳しいのでそちらに譲ります。

フィルタをかける

ポートで絞る

なにもフィルタをかけないと出力が多すぎてわけがわからないので、例えば http (80番) ポートに絞る場合は次のようにします。

$ sudo tcpdump port 80

発信または受信だけに絞りたい場合は、それぞれ dst, src を使います。

$ sudo tcpdump dst port 80 # 発信
$ sudo tcpdump src port 80 # 受信

http および https に絞り込みたい場合は、or を使う事ができます。

$ sudo tcpdump src port 80 or src port 443

ホストで絞る

host を使うと IP アドレスで絞り込めます。

$ sudo tcpdump host 192.168.1.1
$ sudo tcpdump dst host 192.168.1.1
$ sudo tcpdump src host 192.168.1.1

ネットワークアドレスで絞る

net を使うとネットワークアドレスで絞れます。

$ tcpdump net 192.168.2.0 mask 255.255.255.0

192.168.2.0/24ネットワークとのパケットだけを表示します。

パケットの中身も表示したい

オプション -X を使用するとヘッダだけではなくパケットの中身も表示できます。16進とASCIIで表示してくれます。

$ sudo tcpdump -X dst port 80
13:11:43.075524 IP 176.32.121.17.http > 192.168.0.10.54636: Flags [F.], seq 1, ack 2, win 127, length 0
     0x0000:  4500 0028 430d 4000 ee06 5fde b020 7911  E..(C.@..._...y.
     0x0010:  c0a8 000a 0050 d56c 3f8f 3bf0 937c ba55  .....P.l?.;..|.U
     0x0020:  5011 007f 2662 0000 797f e526 fa34       P...&b..y..&.4
13:11:48.822171 IP nrt19s17-in-f31.1e100.net.http > 192.168.0.10.54659: Flags [S.], seq 1649666050, ack 1051684197, win 14180, options [mss 1414,sackOK,TS val 3520905570 ecr 751819267,nop,wscale 6], length 0
     0x0000:  4500 003c c703 0000 3606 28e5 adc2 265f  E..<....6.(...&_
     0x0010:  c0a8 000a 0050 d583 6253 e802 3eaf 6d65  .....P..bS..>.me
     0x0020:  a012 3764 1596 0000 0204 0586 0402 080a  ..7d............
     0x0030:  d1dc c162 2ccf da03 0103 0306            ...b,.......
13:11:48.826251 IP nrt19s17-in-f31.1e100.net.http > 192.168.0.10.54655: Flags [.], ack 2923, win 311, options [nop,nop,TS val 3520905574 ecr 751819271], length 0
     0x0000:  4500 0034 c704 0000 3706 27ec adc2 265f  E..4....7.'...&_
     0x0010:  c0a8 000a 0050 d57f 51ad 1e62 e596 78a4  .....P..Q..b..x.
     0x0020:  8010 0137 a27d 0000 0101 080a d1dc c166  ...7.}.........f
     0x0030:  2ccf da07                                ,...
13:11:48.883209 IP nrt19s17-in-f31.1e100.net.http > 192.168.0.10.54655: Flags [P.], seq 802:1603, ack 2923, win 311, options [nop,nop,TS val 3520905630 ecr 751819271], length 801
     0x0000:  4500 0355 c705 0000 3706 24ca adc2 265f  E..U....7.$...&_
     0x0010:  c0a8 000a 0050 d57f 51ad 1e62 e596 78a4  .....P..Q..b..x.
     0x0020:  8018 0137 8dbe 0000 0101 080a d1dc c19e  ...7............
     0x0030:  2ccf da07 4854 5450 2f31 2e31 2033 3032  ,...HTTP/1.1.302
     0x0040:  2046 6f75 6e64 0d0a 4c6f 6361 7469 6f6e  .Found..Location
     0x0050:  3a20 6874 7470 733a 2f2f 7777 772e 676f  :.https://www.go
     0x0060:  6f67 6c65 2e63 6f2e 6a70 2f73 6561 7263  ogle.co.jp/searc
     0x0070:  683f 713d 686f 6765 2673 7567 6578 703d  h?q=hoge&sugexp=
     0x0080:  6368 726f 6d65 2c6d 6f64 3d35 2673 6f75  chrome,mod=5&sou
     0x0090:  7263 6569 643d 6368 726f 6d65 2669 653d  rceid=chrome&ie=
     0x00a0:  5554 462d 380d 0a43 6163 6865 2d43 6f6e  UTF-8..Cache-Con
     0x00b0:  7472 6f6c 3a20 7072 6976 6174 650d 0a43  trol:.private..C

google への GET がみえてますね。-X ではなく-A を使うとパケットの中身を ASCII だけで表示してくれます。

$ sudo tcpdump -s0 -A dst port 80
00:21:17.219392 IP 192.168.0.10.49030 > kix01s02-in-f23.1e100.net.http: P 1:161(160) ack 1 win 46E...4.@.@..[!1.J}.....P2..=..IFP.......GET / HTTP/1.1User-Agent: curl/7.15.5 (x86_64-redhat-linux-gnu) libcurl/7.15.5 OpenSSL/0.9.8b zlib/1.2.3 libidn/0.6.5Host: www.google.co.jpAccept: */*00:21:17.223807 IP kix01s02-in-f23.1e100.net.http > 192.168.0.10.49030: . ack 161 win 668E..(-...6...J}..!1..P....IF2...P.............00:21:17.277463 IP kix01s02-in-f23.1e100.net.http > 192.168.0.10.49030: . 1:1431(1430) ack 161 win 668E...-...6..yJ}..!1..P....IF2...P....^..HTTP/1.1 200 OKDate: Thu, 17 Oct 2013 15:21:17 GMT
Expires: -1
Cache-Control: private, max-age=0
Content-Type: text/html; charset=Shift_JIS
Set-Cookie: PREF=ID=1154a5c406e9b205:FF=0:TM=1382023277:LM=1382023277:S=e6t3mjXS1VIJsfyK; expires=Sat, 17-Oct-2015 15:21:17 GMT; path=/; domain=.google.co.jp
Set-Cookie: NID=67=AETVOuLZ1B9FjJqqBGQ-ZsSaWX4FEjcuOKf4vgPMqAGGwKH0zzt-s2BnADI6EoUv0AIeDKIdR4U4wCB7Bh8WNlCf27M89Y1MnMXfr3lGkeJFgWCMseb_FhhSm_am0lae; expires=Fri, 18-Apr-2014 15:21:17 GMT; path=/; domain=.google.co.jp; HttpOnly
P3P: CP="This is not a P3P policy! See http://www.google.com/support/accounts/bin/answer.py?hl=en&answer=151657 for more info."
Server: gws
X-XSS-Protection: 1; mode=block
X-Frame-Options: SAMEORIGIN
Alternate-Protocol: 80:quic
Transfer-Encoding: chunked

4979
<!doctype html><html itemscope="" itemtype="http://schema.org/WebPage"><head><meta content="...E.................................c.[...................B...............@.\.....p.....A...T...........................B" name="description"><meta content="noodp" name="robots"
><meta itemprop="image" content="/images/google_favicon_128.png"><title>Google</title><script>(function(){
window.google={kEI:"bQBgUrfnDseGkQWniIGgAw",getEI:function(a){for(var b;a&&(!a.getAttribute||!(b=a.getAttribute("eid")));)a=a.parentNode;return b||google.kEI},https:function(){return"https:"==window.location.protocol},kEXPI:"17259,146147,600395,400011
00:21:17.277480 IP g

ただ、デフォルトではパケットの中身は途中で切られてしまうので(CentOS 6 ならデフォルト 65535 バイト、CentOS 5 だとデフォルトたったの 96 バイト)、もっとみたい場合は -s オプションでバイト長を指定します。0 を指定すると制限がなくなります。

良く使いそうなオプション

よく使いそうなオプションをリストアップしておきます。

-i キャプチャするインターフェースを指定。
-n アドレスやポート番号を名前に変換しない。DNS lookup の時間短縮
-nn ポート番号をプロトコル名に変換(80 を http など)しない
-p プロミスキャスモードにしない。自分宛のパケットのみキャプチャ wikipedia:プロミスキャス・モード
-s キャプチャするサイズを指定。0 だと全て
-w 生データをそのままファイルに書き込む。WireSharkやEtherealで開くこともできる
-r -w で保存したファイルを開く
-c 指定した個数のパケットを受信したら終了。ディスク圧迫しないように使う
-X 16進とASCII文字で表示を行う。
-A ASCII文字で表示を行う。
-G ファイル書き込みを指定時間間隔で rotate する。-w のファイル名に時間フォーマットを指定
-W -G での rotate の回数を制御する。-W の回数に達すると終了

よく使いそうなテンプレ

よく使いそうなテンプレをまとめておきます。

1) いつも使う基本形 -s0 -A

$ sudo tcpdump -s0 -A port 80 and host www.google.co.jp

2) ファイル吐き出しをする場合に、ファイルが巨大に1つになってしまうと追いづらいので、指定時間間隔で rotate させる。-G オプションを使う

$ sudo tcpdump -s0 -A port 80 -G <rotate_seconds> -w /tmp/tcpdump_%Y%m%d_%H%M.cap

3) crontab に仕込んで毎日 12:00 - 13:00 だけパケットキャプチャしたい場合

オプション -G 60 で毎分 rotate させ、-W 60 で 60回 rotate したら、つまり1時間たったら、tcpdump 終了。ただし、一切書き込みがない場合、ファイル rotate が発生せず、tcpdump が終了しない挙動となるため、念のために kill もしている。

0 12 * * * sudo /usr/sbin/tcpdump -i bond0 -s 512 -A -n -nn -p "tcp port 80" -G 60 -W 60 -w /tmp/tcpdump_%Y%m%d_%H%M.cap >/dev/null 2>&1 & echo $! > /tmp/tcpdump.pid
0 13 * * * sudo kill $(cat /tmp/tcpdump.pid)

4) localhost 間の通信をみる

loopback interface を指定すればOK

linux)

$ sudo tcpdump -i lo

mac)

$ sudo tcpdump -i lo0

この場合、なぜか port xxxx ではダメで、tcp port xxxx とのように tcp まで指定しないと port 番号で絞れない。

参考

謝辞

strace コマンドの使い方をまとめてみた

strace コマンドの使い方をまとめてみました。自分用メモ。

strace コマンドとは

strace を使う事で、プロセスが呼び出すシステムコールをトレースし、その内容を表示することができます。strace を使って、システムコールがエラーになる箇所を探すと、不具合の手がかりが得られることがあります。

strace コマンドの基本

$ strace [command]

strace からコマンドのプロセスを起動して、システムコールをトレースします。例えば、 

$ strace ls /hoge

のように実行すると、

execve("/bin/ls", ["ls", "/hoge"], [/* 45 vars */]) = 0
brk(0)                                  = 0x17a2000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f05b1456000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY)      = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=59300, ...}) = 0
…
(省略)
...
stat("/hoge", 0x17a20f0)                = -1 ENOENT (No such file or directory)
lstat("/hoge", 0x17a20f0)               = -1 ENOENT (No such file or directory)
write(2, "ls: ", 4ls: )                     = 4
write(2, "cannot access /hoge", 19cannot access /hoge)     = 19
write(2, ": No such file or directory", 27: No such file or directory) = 27

のように表示されます。エラー終了するような場合、strace の出力を最後から見ていくのが解決への近道です。

上記の例では、stat("/hoge", 0x17a20f0) の行で「No such file or directory」ということからファイルが存在しないためにエラーが発生していることがわかります。ls コマンドからは同様なメッセージがエラー出力されるので、原因解析のために strace を使うまでもありませんが、エラーメッセージ(or ログメッセージ)が出力されないようなアプリケーションのエラーの原因を調べるのに役に立ちます。

Linux のシステムコールは man コマンドで調べられます。システムコールはセクション2 なので -S 2 をつけて調べましょう。

$ man stat -S 2

プロセスへのアタッチ

strace からプロセスを起動させるのではなく、デーモンのようにすでに実行されているプロセスの動作を strace で確認するには -p オプションを使用します。

strace -p [pid]

tomcat を例にしてみます。まず ps コマンドを用いてデーモンのプロセス番号を調べます。

$ ps auxww | grep tomcat
root      1882  0.2 23.6 2249336 486784 ?      Sl   03:02   2:13 /usr/java/jdk1.6.0_25/bin/java -Djava.util.logging.config.file=/usr/java/tomcat
... 
(省略)

tomcat のプロセス番号 1882 にアタッチしてみます。

$ sudo strace -p 1882
Process 1882 attached - interrupt to quit
futex(0x7fbc848939e0, FUTEX_WAIT, 1912, NULL

tomcat にリクエストを送ってみます。

$ curl http://localhost:8080/

あれ、何も変わりませんね?pstree で調べるとわかるのですが、tomcat の場合、内部でスレッドが切られているので、スレッドもトレースする -f オプションを使用する必要があります。

$ pstree -p 1882
java(1882)-+-{java}(1912)
           |-{java}(1958)
           |-{java}(1959)
…
(省略)

オプション -f を付けて、

$ sudo strace -f -p 1882

再度、tomcat にリクエストを送ってみます。

$ curl http://localhost:8080/
(省略)
pid  1974] futex(0x403b6994, FUTEX_WAIT_BITSET_PRIVATE|FUTEX_CLOCK_REALTIME, 1, {1348625722, 878871000}, ffffffff 
[pid  2381] <... futex resumed> )       = -1 ETIMEDOUT (Connection timed out)
[pid  2381] futex(0x7fbc8037ca28, FUTEX_WAKE_PRIVATE, 1) = 0
[pid  2381] stat("/home/usr/java/apache-tomcat-6.0.33/webapps/ROOT", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
[pid  2381] stat("/home/usr/java/apache-tomcat-6.0.33/webapps/ROOT", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
[pid  2381] stat("/usr/java/tomcat/conf/context.xml", {st_mode=S_IFREG|0600, st_size=1395, ...}) = 0
(省略)

アクセスした瞬間にトレース結果が出力されるようになりました。ただ、大量に出力されすぎて追いにくいので、-e オプションを使用して特定のシステムコールに絞ってみます。

例えば stat に絞りたい場合は以下のようにします。

$ strace -f -p 1882 -e trace=stat
[pid  2381] stat("/home/usr/java/apache-tomcat-6.0.33/webapps/ROOT", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
[pid  2381] stat("/home/usr/java/apache-tomcat-6.0.33/webapps/ROOT", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
[pid  2381] stat("/usr/java/tomcat/conf/context.xml", {st_mode=S_IFREG|0600, st_size=1395, ...}) = 0
(省略)

その他のオプション

表示される文字数を増やす -s

1行あたりに表示される文字数は、デフォルトでは32文字でカットされてしまって大事なメッセージが見れなかったりするので、もっと表示したい場合は -s オプションで指定します。

$ strace -s 1024 command

ファイルに出力する -o

表示内容をファイルへ出力するには、-o オプションを用います。

$ strace -o output.log command

時刻を表示する -tt

各システムコールが発行された時刻を -t、または、-tt オプションで表示させることができます。

違いは、-t は秒単位で、-tt はマイクロ秒単位で表示することです。

$ strace -t command
$ strace -tt command

統計情報を表示する -c

システムコールの呼び出された回数、かかった時間などの統計情報を出力するには -c オプションを利用します。

一度統計情報を出してから、明らかに遅いシステムコールに対して -e で指定してあげるとチューニングが捗りそうです。

$ strace -c ls /hoge
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
100.00    0.000156           4        35           mmap
  0.00    0.000000           0        14           read
  0.00    0.000000           0         4           write
  0.00    0.000000           0        30        13 open
  0.00    0.000000           0        19           close
  0.00    0.000000           0         4         4 stat
  0.00    0.000000           0        17           fstat
  0.00    0.000000           0         1         1 lstat
  0.00    0.000000           0        18           mprotect
  0.00    0.000000           0         3           munmap
  0.00    0.000000           0         3           brk
  0.00    0.000000           0         2           rt_sigaction
  0.00    0.000000           0         1           rt_sigprocmask
  0.00    0.000000           0         2           ioctl
  0.00    0.000000           0         1         1 access
  0.00    0.000000           0         1           execve
  0.00    0.000000           0         1           getrlimit
  0.00    0.000000           0         1           statfs
  0.00    0.000000           0         1           arch_prctl
  0.00    0.000000           0         3         1 futex
  0.00    0.000000           0         1           set_tid_address
  0.00    0.000000           0         1           set_robust_list
------ ----------- ----------- --------- --------- ----------------
100.00    0.000156                   163        20 total

応用例

mysql がどの設定ファイルを開いているのか調べる

mysql は /etc/my.cnf 以外にもいろいろな場所から設定ファイルを読み込む可能性があります。実際どのファイルをどの順番で読み込んでいるのか調べられます。

$ strace mysql 2>&1  | grep 'open' | grep '.cnf'
open("/etc/mysql/my.cnf", O_RDONLY|O_LARGEFILE) = 3
open("/home/sonots/.my.cnf", O_RDONLY|O_LARGEFILE) = 3

strace コマンドの出力は標準エラー出力に出るので、 2>&1 で標準出力にリダイレクトして grep しています。

不審なプロセスを監視する

例えば、不審な時刻にシェルにログインしているユーザを発見した場合、そのシェルの pid に strace をアタッチすることで、そのユーザが何を実行しているのかがリアルタイムでわかります。

$ strace -f -e execve -p 26787
[pid 26807] execve("/usr/bin/less", ["less", "+F", "/usr/local/apache/logs/access_lo"...], [/* 33 vars */]) = 0[pid 26807] --- SIGCHLD (Child exited) ---
[pid 26807] --- SIGINT (Interrupt) ---
--- SIGCHLD (Child exited) ---
[pid 26809] execve("/bin/ls", ["ls", "-NF", "--show-control-chars", "-la"], [/* 33 vars */]) = 0
--- SIGCHLD (Child exited) ---

基本テンプレ

自分は -tt と -s オプションは常に指定してることが多いですね。

$ strace -tt -s 1024 -p [pid]

参考サイト

pry-nav よりも pry-debugger、よりも debugger がオススメ!?

後記: 以下の文章は ruby 1.9 時代の古い文章です。ruby 2.0 でのデバッガとしては byebug https://github.com/deivid-rodriguez/byebug がオススメです(そもそも debugger gem が ruby 2.0 では動きません)。ruby 1.9, 2.0 両方で動かしたい場合は機能的には劣りますが pry-nav が両方で動くのでオススメできます。

というわけで、pry-nav よりも pry-debugger がオススメです。私も今日オフィシャルのREADMEを読むまで、オワコンになっていることに気付かず pry-nav を使っていました。

pry-nav オワコン、ということで今後は pry-debugger を使いましょう。使い方は pry-nav と全く同じで、

next
step
continue

が使えるのに加え、

finish
break
breakpoints

が使えるようになっています。finish でステップアウトできたり、break でブレークポイントを貼れるのはイイですね!

pry-debugger よりも debugger!?

ただし、pry-debugger には機能制限があって、pry から debugger の機能を使おうとすると、debugger の本来持つ42個のコマンドのうち、break, breakpoints, continue, finish, next, step の6個のコマンドしか使えなくなってしまいます。

参考:debugger が持つ42個のコマンド

backtrace  delete   enable  help  list    ps       save    step       var  
break      disable  eval    info  method  putl     set     thread     where
catch      display  exit    irb   next    quit     show    trace    
condition  down     finish  jump  p       reload   skip    undisplay
continue   edit     frame   kill  pp      restart  source  up     

なので、特別 pry の機能を使う必要がなければ、現状では debugger を直接使うことをオススメします。

condition でブレークポイント条件を作れるとか、up で呼び出し元を掘っていったりできるのイイですね!

早く pry-debugger が debugger の全機能に対応してくれないかなぁ。

debugger の使い方

ruby-debug → 1.9.x で動かねー! → ruby-debug19 → メンテナンスされなくなってインストールで失敗する!→ pry かわいいよ pry

という流れで、pry をデバグに使っている Rubyist の方には蛇足以外の何者でもないと思いますが、debugger の使い方にはついては Rails Guide の Ruby on Rails Guides: Debugging Rails Applications にも載っているので、そちらを見てみましょう! ruby-debug と使い方は同じなので、ruby-debug でぐぐっても参考になるんじゃないかな。

調べるきっかけをくださった @sasata299 さんと、いろいろ教えてくださった @udzura さんに感謝。

A Ruby and Fluentd committer working at DeNA. 記事本文および記事中のコード片は引用および特記あるものを除いてすべて修正BSDライセンスとします。 #ruby #fluentd #growthforecast #haikanko #yohoushi #specinfra #serverspec #focuslight
はてぶ人気エントリー