さくらのレンタルサーバをずっと使っていたのですが、値段的にAWSのLightSailと変わらないし、仕事の関係でAWSの方が慣れてるので引っ越しました。
WordPressのexport / importで記事の移動はほとんどできました。画像のURLを変えた程度。アップロードしたファイルはちょっと消えたけど、古いしいいかw
サーバ止めて、Mediumあたりに変えようかとも思いましたが、wordpress触る機会がなくなっちゃうので継続です。
プログラミングに関するネタを載せるブログ
さくらのレンタルサーバをずっと使っていたのですが、値段的にAWSのLightSailと変わらないし、仕事の関係でAWSの方が慣れてるので引っ越しました。
WordPressのexport / importで記事の移動はほとんどできました。画像のURLを変えた程度。アップロードしたファイルはちょっと消えたけど、古いしいいかw
サーバ止めて、Mediumあたりに変えようかとも思いましたが、wordpress触る機会がなくなっちゃうので継続です。
携帯にxperia acroを使ってるんですが、自宅PC(NASとか)へのアクセスのために、pptpを使って
vpn接続した場合、十数分ぐらいで切断されてしまいます。
どうも、androidにpptpバグがあるっぽいandroid 2.3.7だと直ったようですが手元のxperia acroは、2.3.3・・・。次に携帯買うときはspモードかもしれないし、その場合pptpサポートされませんし・・・。ので、pptpの代わりにL2TP/IPsecを使うことにしました。
1万以下で買えるルータでL2TP/IPsec使えるものがなさそうなので、
余っていた昔のノートPC(Let’s note T2)にソフトウェアルータ(vyatta)をインストールして、
ネットワーク組みました。記録のため、ここに書いときます。
ネットワークを図にすると以下の通りです。
xperia acroから、グローバルIP(私はapnにmopera使ってます)でL2TP/IPsec vpn接続&通信はできました。とりあえず動いたから満足した。
vyattaの設定として、nat-traversal有効(かつ、udp 4500開放)にしてあるので、たぶんspモード(ローカルIP付与されたxperia acro)でもつながるでしょう。
設定内容は以下の通り。MACとか見せたくないので、[dummy]に編集してあります。
$ show configuration firewall { all-ping enable name inside_to_vyatta { default-action drop rule 1 { action accept protocol all } } name outside_to_vyatta { default-action drop rule 1 { action accept protocol all state { established enable related enable } } rule 20 { action accept destination { port ssh } protocol tcp state { new enable } } rule 30 { action accept destination { port 500 } protocol udp state { new enable } } rule 40 { action accept destination { port 4500 } protocol udp state { new enable } } rule 50 { action accept destination { port 1701 } ipsec { match-ipsec } protocol udp state { new enable } } rule 60 { action accept protocol esp state { new enable } } } name to_inside { default-action drop rule 1 { action accept protocol all state { established enable related enable } } } name to_outside { default-action drop rule 1 { action accept protocol all } } name vyatta_to_inside { default-action drop rule 1 { action accept protocol all } } } interfaces { ethernet eth0 { address 192.168.1.1/24 description INSIDE duplex auto hw-id [dummy] smp_affinity auto speed auto } ethernet eth1 { duplex auto hw-id [dummy] smp_affinity auto speed auto } ethernet eth2 { description OUTSIDE duplex auto hw-id [dummy] pppoe 0 { default-route auto mtu 1492 name-server auto password **************** user-id [dummy] } smp_affinity auto speed auto } loopback lo { } } service { dns { dynamic { interface pppoe0 { service dyndns { host-name [dummy] login [dummy] password **************** } } } forwarding { cache-size 150 listen-on eth0 system } } nat { rule 1 { outbound-interface pppoe0 type masquerade } } ssh { disable-password-authentication port 22 protocol-version v2 } } system { config-management { commit-revisions 20 } console { } host-name vyatta login { user ssh-forward { authentication { encrypted-password **************** plaintext-password **************** public-keys [dummy] { key **************** type ssh-dss } } level operator } user vyatta { authentication { encrypted-password **************** public-keys [dummy] { key **************** type ssh-dss } } level admin } } name-server 8.8.8.8 name-server 8.8.4.4 ntp { server 0.vyatta.pool.ntp.org { } server 1.vyatta.pool.ntp.org { } server 2.vyatta.pool.ntp.org { } } package { auto-sync 1 repository community { components main distribution stable password **************** url http://packages.vyatta.com/vyatta username "" } } syslog { global { facility all { level notice } facility protocols { level debug } } } time-zone GMT } vpn { ipsec { esp-group ESP-GW { compression disable lifetime 3600 mode tunnel pfs enable proposal 1 { encryption 3des hash sha1 } proposal 2 { encryption aes128 hash sha1 } } ike-group IKE-GW { lifetime 28800 proposal 1 { dh-group 2 encryption 3des hash sha1 } proposal 2 { dh-group 2 encryption aes128 hash sha1 } } ipsec-interfaces { interface pppoe0 } logging { log-modes all } nat-networks { allowed-network 0.0.0.0/0 { exclude 192.168.1.0/24 } } nat-traversal enable site-to-site { } } l2tp { remote-access { authentication { local-users { username [dummy] { password **************** } } mode local } client-ip-pool { start 192.168.1.90 stop 192.168.1.99 } dns-servers { server-1 8.8.8.8 } ipsec-settings { authentication { mode pre-shared-secret pre-shared-secret **************** } ike-lifetime 3600 } mtu 1280 outside-address [dummy] outside-nexthop [dummy] } } } zone-policy { zone inside { from outside { firewall { name to_inside } } from vyatta { firewall { name vyatta_to_inside } } interface eth0 interface l2tp+ } zone outside { from inside { firewall { name to_outside } } from vyatta { firewall { name to_outside } } interface eth2 interface pppoe0 } zone vyatta { from inside { firewall { name inside_to_vyatta } } from outside { firewall { name outside_to_vyatta } } local-zone } }
少し前に、ルータ(WZR-HP-AG300H)を買って自宅外からVPN接続してます。設定がWebブラウザから簡単にできますし、VPNサーバ用のマシンをルータと別に用意する必要もないので、結構便利です。
私の主な使い方は、
の2つです。
せっかくなので、設定メモを残しておきます。
このルータのVPNサーバの方式はPPTPです。設定手順はルータのマニュアルが詳しいです。
ちなみに、私はDDNSにDynDNSを使ってます。
マニュアルに見当たらなかった注意点として、クライアント側のPCに付与されるIPがプライベートIPで、クライアント側ルータでNATが行われる場合は、クライアント側ルータのPPTP(VPN)パススルー/マルチパススルーを有効にしないと、PPTPサーバ・クライアント間で接続できない点があります。
最近のルータなら、たいていルータの設定項目で有効にできます。ルータによっては、GREパケットを通すようにNAT設定する必要があったりします。
ただ、パススルー設定ができない環境(公衆無線LANとか)から自宅マシンにつなぎたいときもあります。
私の場合、Android端末から3G回線でVPN接続→WoL実施→マシン起動を行った上で、手元のPCから起動したマシンにsshログイン(とポートフォワーディング)で対応してます。
無事VPN接続したあと、http://<ルータのプライベートIP>/hosts.html にアクセスすると、WoLやhttpサーバなどのサービス一覧が表示されます。
httpサーバが動いているかどうかのポートチェックが、どのタイミングで行われるのか気になったので、tcpdumpでLAN内を監視したところ、以下に対してチェックしているようです。。
用は、ルータに届いたARPの送り元IPに対して、ARPが届いた直後にポートチェックしているようです
以下は、あるマシン(desktop-ubuntu.local)がプロードキャストARP(Target IP:192.168.1.7)を送った時のtcpdump出力(をちょっと省略したもの)です。ルータ(192.168.1.1)がftpやwwwなどのポートをチェックしているのがわかります。
ethertype ARP (0x0806), length 42: Request who-has 192.168.1.7 (Broadcast) tell desktop-ubuntu.local ethertype IPv4 (0x0800), length 74: 192.168.1.1.1584 > desktop-ubuntu.local.ftp ethertype IPv4 (0x0800), length 74: 192.168.1.1.1183 > desktop-ubuntu.local.www ethertype ARP (0x0806), length 60: Reply 192.168.1.7 is-at aa:bb:cc:cf:80:99 ethertype IPv4 (0x0800), length 74: 192.168.1.1.1943 > desktop-ubuntu.local.8000 ethertype IPv4 (0x0800), length 74: 192.168.1.1.3765 > desktop-ubuntu.local.http-alt ethertype IPv4 (0x0800), length 74: 192.168.1.1.3896 > desktop-ubuntu.local.3389 ethertype IPv4 (0x0800), length 92: 192.168.1.1.1141 > desktop-ubuntu.local.netbios-ns: NBT UDP PACKET(137)
メモは、これで終わりです。
VPN越しで自宅に溜めこんだ電子書籍を見るとかにも使えそうなので、色々試す価値がありそうです。
VPN越しで動画を見ようとしている方もいらっしゃいました。
[amazon asin=’B003RIU3XQ’ type=’banner’]
NetBeansのリモートデバッグについて、以前の記事で、以下のように書きましたが、
注意点としては、リモートと同じフルパス上にソースコードを置く必要がある点です。リモート先で、/var/www/railsprjにRoRのプロジェクトコードが置いてあるなら、ローカルでも/var/www/railsprjにプロジェクトコードを置く必要があります。
このままだと、Windows上のNetBeansでLinux上のRailsがリモートデバッグできません。
そこで、NetBeansとFast Debugger(ruby-debug-ide)の間に挟むproxyを用意して、proxyの中でファイルパス変換することでリモートデバッグできるようにしました。
せっかくなので、以下にproxyのソースコードを置いておきます(MIT Licenseにしときました)。
以下の構成で、基本的な機能(変数チェック、ステップ実行、breakpointセット辺り)が動作するのは確認しました。
Railsがデバッグできるかどうかは、まだ試してません。
[NetBeans (Debuger GUI)] on Windows ↓↑ [proxy] on Windows/Linux ↓↑ [ruby process (+ruby-debug-ide)] on Linux ・CRuby 1.8.7 および JRuby 1.5.1でproxy動作確認した。
なお、実際にデバッグするときは、NetBeansが動くマシン上にもデバッグ対象のソースコードを置かないとソースコードデバッグできません。
例えば、ソースコードは以下のように配置します。
・ruby-debug-ide側 /home/user/script |---test.rb \---lib \---somelib.rb ・NetBeans側 C:\win_script |---test.rb \---lib \---somelib.rb
以下の手順でproxyの起動やNetBeansからproxyへの接続を行えば、後は普通にデバッグできます。
1, 2の手順はどちらが先でもOKです。
1は以前の記事と同じなので、2, 3の説明だけ書きます。
proxyスクリプトをオプション指定して起動するだけです。proxyスクリプトのオプションは以下の通り。
Usage: ruby-debug-ide-proxy.rb -t[-p listen_port] [--rdbprefix prefix] [--ideprefix prefix] [-d] Example: ruby-debug-ide-proxy.rb -t localhost --rdbprefix "/home/user/script" --ideprefix "C:\\win_script" -d
Exampleの例を説明すると、
という指定になります。
NetBeansのメニューから「デバッグ -> デバッガを接続」を選択し、proxyが動作するホスト名やポート番号を指定して接続してください。
NetBeansからproxyに接続した時、proxyからruby-debug-ideへの接続が自動的に行われます。
正しくデバッガ接続できた場合、以下のようなログがproxyプログラムの端末に出力されます。
I, [2010-09-27T03:43:21.124000 #2240] INFO -- : proxy will replace /home/user/script with C:\win_script I, [2010-09-27T03:43:21.468000 #2240] INFO -- : proxy listens on 0.0.0.0:7000 I, [2010-09-27T03:43:24.530000 #2240] INFO -- : proxy connects to 192.168.1.7:1234 I, [2010-09-27T03:43:24.530000 #2240] INFO -- : debug start
後は、普通にNetBeansのデバッガGUIが使えます。
例えば、デバッガGUIからbreakpointをセットすると、以下のログがproxy端末上に出ます。(-dを指定して起動した場合のみ)
D, [2010-09-27T03:43:24.546000 #2240] DEBUG -- : (ide -> proxy) b C:\win_script\test.rb:5 D, [2010-09-27T03:43:24.546000 #2240] DEBUG -- : (proxy -> rdb) b /home/user/script/test.rb:5 D, [2010-09-27T03:43:24.562000 #2240] DEBUG -- : (rdb -> proxy)D, [2010-09-27T03:43:24.624000 #2240] DEBUG -- : (proxy -> ide)
手順は以上です。
Ruby/Railsの場合、デバッガはプログラムの動作理解に使うものというのが私の認識です
NetBeansには、rubyのデバッガフロントエンドが同梱されています。
今回は、それを使ってRuby on Rails(以下RoR)のリモートデバッグをするためのメモです。
個人的には、RoRが動いてるマシンにsshでログインした後、emacsとrubydb3x.elを使う方がメリットがある
なお、NetBeansをインストールしたローカルマシンで、RoRも動かす場合のデバッグ方法は、NetBeansのwikiを参照してください。
まあ、ローカルマシン上でデバッグする場合も、リモートマシンと接続してデバッグする場合もやり方はほとんど変わりません。
どちらの場合も、デバッギプロセス(と同じプロセス上で動くruby-debug-ide)がTCP(デフォルトだと1234)をListenし、Netbeansからそこに接続してデバッグを開始します。Netbeansから接続するデバッギプロセスがlocalhostにいるか、リモート先のホストにいるかの違いしかないです。
接続後、Netbeansからruby-debug-ideへbreakpointをしかける場所などを指示します。
デバッグ方法そのものは、ローカル・リモートともに一緒ですが、リモートのホストに対してローカルのNetBeansを接続してデバッグするには、リモート先のホストで以下の作業が必要です。
4, 5, 6の手順は、あまりネット上で見かけないので順に解説します。
Linuxだと、gemで簡単にインストールできます。(gcc, ruby-develをインストールしておけば。)
Fedora8だと以下で終わりでした。
$ gem install ruby-debug $ gem install ruby-debug-ide
Windows(のmswin版ruby)の場合、コンパイル環境の用意が面倒なので、コンパイル済みgemをダウンロードした上でインストールします。
> gem install -l linecache-0.43-mswin32.gem > gem install -l ruby-debug-base-0.10.3-mswin32.gem > del ruby-debug-base-0.10.3-mswin32.gem > gem install ruby-debug > gem install ruby-debug-ide -v 0.4.6
コンパイル済みgem (linecache, ruby-debug-base)は以下2つのリンク先から
それぞれダウンロードしてください。
ローカルにもソースコードを置かないと、breakpointで止まった時にNetbeansにソースコードが読み込まれません。
注意点としては、リモートと同じフルパス上にソースコードを置く必要がある点です。リモート先で、/var/www/railsprjにRoRのプロジェクトコードが置いてあるなら、ローカルでも/var/www/railsprjにプロジェクトコードを置く必要があります。
この辺の挙動理解には、NetBeans wikiのChecking debugger engine functionalityとか、ruby-debug-ide protocolとかを参考にして、実際にruby-debug-ideとtelnetで会話するといいです。
以下な感じでRoR Webサーバ起動します。(-dも付けると、NetBeansとの通信時の処理が良く見えます。)
$ rdebug-ide --stop script/server -h 0.0.0.0 Fast Debugger (ruby-debug-ide 0.4.6) listens on 0.0.0.0:1234
NetBeans IDEのメニュー -> デバッグ -> 接続をクリックし、ダイアログにリモート先ホスト名とポート番号(1234)を指定して接続します。
–stopを指定してあるので、RoRの処理先頭でstopするはずです。
あとは、IDEからbreakpoint指定するなり、なんなり自由に操作します。
watch変数として、paramsを追加しておくといい感じです。
Windowsのバッチファイルを作る機会があったので、その時のノウハウメモ。
サービスの起動・停止状態の出力させるには、sc.exe queryが使える。(ただし、Windows XP/Windows Server 2003以降)
後は、findstr (Windows用のgrep)と合わせて使えばいい。
たとえば、タスクスケジューラが起動しているかどうかの確認バッチファイルを
作ると以下の通り。
@echo off sc.exe query Schedule | findstr STATE | findstr RUNNING > nul if %ERRORLEVEL% == 1 echo Scheduler isn't running if %ERRORLEVEL% == 0 echo Scheduler running
実行結果(タスクスケジューラ起動時)は以下の通り。
C:\>service_check.bat Scheduler running
上のバッチファイルのコツは、
ちなみに、nulというnull device名の歴史をたどると、古い順に、
PIP > CP/M > MS-DOS > Windows
という流れで受け継がれてます。
バッチファイル上で、特殊記号として扱われるものがいくつかあります。
例えば、括弧()が特殊記号です。このような記号を普通の文字として扱いたいときは、^を特殊記号の前に付ける必要があります。
例は以下の通り。
@echo off if 1 == 1 ( echo foo ^(bar^) baz )
C:\>with_hat.bat foo (bar) baz
ちなみに^を抜くと、if文の終端(=echoの終わり)がbarの直後と判断されてしまい、
その後のbazをコマンドとみなして実行するためエラーになります。
C:\>no_hat.bat baz の使い方が誤っています。
他の特殊記号については、このWikiを参照してください。
tarファイルには、いくつかのフォーマットがあるのだけど、何らかのコマンドで作成したtarファイルが
どのフォーマットで作られているか判断するやり方が見当たらないのでメモしとく。
なお、メジャーなフォーマットは以下らしい
利用者から見たフォーマット間の大きな違いは、ustar形式のtarフォーマットの場合、tarでまとめる各ファイルのファイル名が256文字制限だったり、各ファイルのサイズ制限が8GBになってること。残りの2つのフォーマットは制限なし。その代わり、他と比べてustar形式は歴史がある形式なので、対応しているツールも多い。
ちなみに、普通のlinux上のtarプログラム(=GNU tar)がデフォルトで使うtarフォーマットは–helpを付ければわかる。
以下のように、最近のtarプログラムならgnuフォーマットのはず。
# tar --help [...略] *This* tar defaults to: --format=gnu -f- -b20 --quoting-style=escape --rmt-command=/sbin/rmt
hexdumpコマンドでtarファイルを出力し、tarファイル内にアーカイブされた各ファイルのtarヘッダ(とpaxヘッダ)で見分けます。
hexdump -C
それぞれのフォーマットごとに特徴的なヘッダ部分は以下。
00000100 ...(省略)... |.ustar.00root...|
00000100 ...(省略)... |.ustar .root...|
00000000 ...(省略)... |./PaxHeaders.108|
以下、各フォーマットの確認例です。
# echo "hello" > foo.txt # tar --format=gnu -cvf test.gnu.tar foo.txt # tar --format=ustar -cvf test.ustar.tar foo.txt # tar --format=pax -cvf test.pax.tar foo.txt # hexdump -C test.ustar.tar 00000000 66 6f 6f 2e 74 78 74 00 00 00 00 00 00 00 00 00 |foo.txt.........| 00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| * 00000060 00 00 00 00 30 30 30 30 36 34 34 00 30 30 30 30 |....0000644.0000| 00000070 30 30 30 00 30 30 30 30 30 30 30 00 30 30 30 30 |000.0000000.0000| 00000080 30 30 30 30 30 30 36 00 31 31 34 30 35 31 37 34 |0000006.11405174| 00000090 33 30 34 00 30 31 32 33 37 33 00 20 30 00 00 00 |304.012373. 0...| 000000a0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| * 00000100 00 75 73 74 61 72 00 30 30 72 6f 6f 74 00 00 00 |.ustar.00root...| 00000110 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000120 00 00 00 00 00 00 00 00 00 72 6f 6f 74 00 00 00 |.........root...| 00000130 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000140 00 00 00 00 00 00 00 00 00 30 30 30 30 30 30 30 |.........0000000| 00000150 00 30 30 30 30 30 30 30 00 00 00 00 00 00 00 00 |.0000000........| 00000160 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| * 00000200 68 65 6c 6c 6f 0a 00 00 00 00 00 00 00 00 00 00 |hello...........| 00000210 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| * 00002800 # hexdump -C test.gnu.tar 00000000 66 6f 6f 2e 74 78 74 00 00 00 00 00 00 00 00 00 |foo.txt.........| 00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| * 00000060 00 00 00 00 30 30 30 30 36 34 34 00 30 30 30 30 |....0000644.0000| 00000070 30 30 30 00 30 30 30 30 30 30 30 00 30 30 30 30 |000.0000000.0000| 00000080 30 30 30 30 30 30 36 00 31 31 34 30 35 31 37 34 |0000006.11405174| 00000090 33 30 34 00 30 31 31 30 37 33 00 20 30 00 00 00 |304.011073. 0...| 000000a0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| * 00000100 00 75 73 74 61 72 20 20 00 72 6f 6f 74 00 00 00 |.ustar .root...| 00000110 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000120 00 00 00 00 00 00 00 00 00 72 6f 6f 74 00 00 00 |.........root...| 00000130 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| * 00000200 68 65 6c 6c 6f 0a 00 00 00 00 00 00 00 00 00 00 |hello...........| 00000210 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| * 00002800 # hexdump -C test.pax.tar 00000000 2e 2f 50 61 78 48 65 61 64 65 72 73 2e 31 30 38 |./PaxHeaders.108| 00000010 31 2f 66 6f 6f 2e 74 78 74 00 00 00 00 00 00 00 |1/foo.txt.......| 00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| * 00000060 00 00 00 00 30 30 30 30 36 34 34 00 30 30 30 30 |....0000644.0000| 00000070 30 30 30 00 30 30 30 30 30 30 30 00 30 30 30 30 |000.0000000.0000| 00000080 30 30 30 30 30 35 30 00 31 31 34 30 35 31 37 34 |0000050.11405174| 00000090 34 30 31 00 30 31 33 34 34 31 00 20 78 00 00 00 |401.013441. x...| 000000a0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| * 00000100 00 75 73 74 61 72 00 30 30 00 00 00 00 00 00 00 |.ustar.00.......| 00000110 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| * 00000140 00 00 00 00 00 00 00 00 00 30 30 30 30 30 30 30 |.........0000000| 00000150 00 30 30 30 30 30 30 30 00 00 00 00 00 00 00 00 |.0000000........| 00000160 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| * 00000200 32 30 20 61 74 69 6d 65 3d 31 32 37 36 34 34 32 |20 atime=1276442| 00000210 38 37 30 0a 32 30 20 63 74 69 6d 65 3d 31 32 37 |870.20 ctime=127| 00000220 36 34 34 32 38 32 30 0a 00 00 00 00 00 00 00 00 |6442820.........| 00000230 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| * 00000400 66 6f 6f 2e 74 78 74 00 00 00 00 00 00 00 00 00 |foo.txt.........| 00000410 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| * 00000460 00 00 00 00 30 30 30 30 36 34 34 00 30 30 30 30 |....0000644.0000| 00000470 30 30 30 00 30 30 30 30 30 30 30 00 30 30 30 30 |000.0000000.0000| 00000480 30 30 30 30 30 30 36 00 31 31 34 30 35 31 37 34 |0000006.11405174| 00000490 33 30 34 00 30 31 32 33 37 33 00 20 30 00 00 00 |304.012373. 0...| 000004a0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| * 00000500 00 75 73 74 61 72 00 30 30 72 6f 6f 74 00 00 00 |.ustar.00root...| 00000510 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000520 00 00 00 00 00 00 00 00 00 72 6f 6f 74 00 00 00 |.........root...| 00000530 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000540 00 00 00 00 00 00 00 00 00 30 30 30 30 30 30 30 |.........0000000| 00000550 00 30 30 30 30 30 30 30 00 00 00 00 00 00 00 00 |.0000000........| 00000560 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| * 00000600 68 65 6c 6c 6f 0a 00 00 00 00 00 00 00 00 00 00 |hello...........| 00000610 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| * 00002800
メモ。
RubyやJRubyには、特定の条件(コード行が変化したとか、メソッドを呼び出したなど)を満たした時に呼び出されるフック処理を定義することができます。
Ruby-1.8の低レベルなフック追加API(=C言語API)は、
thread.cのrb_add_event_hook関数。シグネチャは以下。
JRuby-1.4.0で上記関数にほぼ対応するのは、Ruby.javaの
Ruby.addEventHookメソッド。シグネチャは以下。
引数の型であるEventHookやその継承クラスは以下参照。
フックAPIの挙動についての仕様書って、どこにもない感じだなあ。
Ruby自身のデバッガだと、任意のタイミングでアタッチしてbacktrace見るといったことができない(はず)なので、もっと低レベルのデバッガを利用して、それを実現する方法についてメモ。今回はJRubyに対してjdbでアタッチ&backtraceする方法についてのメモです。
なお、CRubyの場合は、以前の記事とか、さらにその先のリンク先を見れば良いです。
ちなみに、今回試したJRubyのversionは以下の通り。OSはCentOS 5.3です。
$ jruby --version jruby 1.3.1 (ruby 1.8.6p287) (2009-06-15 2fd6c3d) (Java HotSpot(TM) Client VM 1.6.0_16) [i386-java]
まず、アタッチ対象例のRubyコードとして以下を用意。
sample.rb
[RUBY]
def foo
p “abc”
bar
end
def bar
p “foge”
sleep(1000)
end
abc
[/RUBY]
そして、デバッガattach用のポートを開けて起動します
$ jruby -J-Xdebug -J-Xrunjdwp:transport=dt_socket,address=8000,server=y,suspend=n sample.rb
barメソッド内のsleepの実行をしているうちに、別のターミナルを開いてjdbでアタッチし、スレッドを停止させます。
$ jdb -attach 8000 > suspend すべてのスレッドが中断されました
そして、backtraceを表示させます。以下の方法で大体表示できます(いい加減なやり方ですが)。
> threads グループ system: (java.lang.ref.Reference$ReferenceHandler)0x9db Reference Handler 状況待機中 (java.lang.ref.Finalizer$FinalizerThread)0x9da Finalizer 状況待機中 (java.lang.Thread)0x9d9 Signal Dispatcher 実行中 グループ main: (java.lang.Thread)0x1 main 状況待機中 > thread 0x1 main[1] where [1] java.lang.Object.wait (ネイティブ メソッド) [2] org.jruby.RubyThread.sleep (RubyThread.java:718) [3] org.jruby.RubyKernel.sleep (RubyKernel.java:725) [4] org.jruby.RubyKernel$s_method_0_1$RUBYINVOKER$sleep.call (null) [5] org.jruby.internal.runtime.methods.JavaMethod$JavaMethodN.call (JavaMethod.java:620) ...(以下略) main[1] up 4 main[5] locals メソッド引数: context = instance of org.jruby.runtime.ThreadContext(id=2527) self = instance of org.jruby.RubyObject(id=2528) clazz = instance of org.jruby.MetaClass(id=2529) name = "sleep" arg0 = instance of org.jruby.RubyFixnum(id=2531) block = instance of org.jruby.runtime.Block(id=2532) ローカル変数: main[5] dump context.frameIndex context.frameIndex = 2 #frameStackの上限(たぶん) main[5] dump context.file + ":" + context.line context.file + ":" + context.line = "sample.rb:7" #現在位置 main[5] dump context.frameStack[2].fileName + ": " + context.frameStack[2].name + ":" + context.frameStack[2].line ...(省略)... = "sample.rb: bar:2" #1つ前のスタックフレーム main[5] dump context.frameStack[1].fileName + ": " + context.frameStack[1].name + ":" + context.frameStack[1].line ...(省略)... = "sample.rb: foo:10" #2つ前のスタックフレーム main[5] dump context.frameStack[0].fileName + ": " + context.frameStack[0].name + ":" + context.frameStack[0].line ...(省略)... = ": null:0" #トップレベル
以上になります。
今回はRuby on Railsについてメモ。
autoloadで自動読み込みされないファイル
解決策がないかと思って、ググってみたところ、以下のページに解決策が書いてありました。
http://www.pistolfly.jp/weblog/2007/06/require-dependency.html
requireじゃなくて、require_dependencyでファイル読み込みすれば良いということでした。
require_dependencyの挙動については、上のリンク先を参照してください。
この辺り(autoload周辺)って、Ruby on Rails公式なドキュメントが見当たらないですねえ。・・・実装を読めってことか。
以下、問題のコード例と解決方法。(ruby 1.8.5, rails 2.3.3, WEBrick 1.3.1で実験しました。)
ファイル読み込みの対象となるファイルを用意します。
<rails_project>/lib/hoge_fuga.rb
[RUBY]
class Hoge
def hoge
return “abc”
end
end
[/RUBY]
上のファイルを読み込むcontrollerを用意します。
このとき、requireの代わりにrequire_dependencyでファイルを読み込みます。
<rails_project>/app/controllers/test_controller.rb
[RUBY]
#require ‘hoge_fuga’
require_dependency ‘hoge_fuga’
class TestController < ApplicationController
def hoge
a = Hoge.new
render(:text => a.hoge)
end
end
[/RUBY]
んで、Webサーバを起動。
$ cd <rails_project> $ ruby script/server
で、ブラウザからtest/hogeにアクセスすると、ブラウザ上にabcが出力されます。
この状況で、<rails_project>/lib/hoge_fuga.rbを以下のように書き換えてみます。
[RUBY]
class Hoge
def hoge
return “123”
end
end
[/RUBY]
変更を保存した後、ブラウザからtest/hogeにアクセスすると、ブラウザ上に123が出力されます。
確かに、Webサーバを再起動しなくても、<rails_project>/lib/hoge_fuga.rbが再読み込みされてますね。
これでデバッグがしやすくなるなあ。