gist

2012年1月29日日曜日

「デバッグで問題を切り分けよう!」Nodeのデバッグ機能を使ってみる。

Nodeには標準でデバッグ機能が搭載されています。

server.js


var http = require('http');

http.createServer(function(rq, rs) {
    rs.writeHead(200, {'Content-Type': 'text/plain'});
    rs.end('Hello Debugger!');
}).listen(3000, 'localhost');

console.log('サーバーが起動しました: http://localhost:3000/');

デバッグモードで起動するには、nodeコマンドにdebugオプションをつけます。
node debug filePath 
$ node debug server.js 
< debugger listening on port 5858
connecting... ok
break in server.js:1
  1 var http = require('http');
  2 
  3 http.createServer(function(rq, rs) {
debug> 

debug> で入力待ちの状態になります。
break in server.js:1  
は、止まっている位置を示しています。server.jsの1行目で処理が中断しています。
  1 var http = require('http');
  2 
  3 http.createServer(function(rq, rs) {
は、中断している位置周辺のコードが表示されます。

この状態のまま、ブラウザから http://localhost:3000/ にアクセスすると、接続できないはずです。

server.js の1行目で処理が中断しているので、6行目のlisten関数が実行されておらず、ウェブサーバが起動していないためです。

処理を再開するには、debug> で「c」と入力します。

debug> c
< サーバーが起動しました: http://localhost:3000/
debug> 

「サーバーが起動しました」の出力があり、「break in server.js:N」の表示もありません。server.jsの最終行まで実行されています。ブラウザからアクセスすると、Hello Debugger! と表示されるはずです。

server.jsを修正し、クライアントからリクエストを受け取ったところで止めてみます。


var http = require('http');

http.createServer(function(rq, rs) {
    debugger;  // 追加。リクエストがきたら止める。
    rs.writeHead(200, {'Content-Type': 'text/plain'});
    rs.end('Hello Debugger!');
}).listen(3000, 'localhost');

console.log('サーバーが起動しました: http://localhost:3000/');

デバッグモードで実行してみます。

$ node debug server.js 
< debugger listening on port 5858
connecting... ok
break in server.js:1
  1 var http = require('http');
  2 
  3 http.createServer(function(rq, rs) {
debug%gt; c
< サーバーが起動しました: http://localhost:3000/
debug< 

「c」でサーバーが起動したら、ブラウザからアクセスしてみます。処理が完了しないはずです。

デバッガが起動しており、4行目で処理が中断しています。

(続き)
< サーバーが起動しました: http://localhost:3000/
break in server.js:4
  2 
  3 http.createServer(function(rq, rs) {
  4     debugger;
  5     rs.writeHead(200, {'Content-Type': 'text/plain'});
  6     rs.end('Hello Debugger!');
debug> 

インスタンス rq の中身を知るには、repl を使います。

(続き)
debug> repl
Press Ctrl + C to leave debug repl
>

replが起動します。REPL(Read-Eval-Print-Loop)は、読込→評価→表示を繰り返す対話型評価環境のこと。rubyやphythonなんかで使われています。

rqの中身を表示するには、rqと打ってReturnします。

> rq
{ httpVersion: '1.1',
  method: 'GET',
  headers: 
   { 'cache-control': 'max-age=0',
     'accept-encoding': 'gzip, deflate',
     'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_2) AppleWebKit/534.51.22 (KHTML, lik... (length: 119)',
     'accept-language': 'ja-jp',
     accept: 'text/html,application/xhtml xml,application/xml;q=0.9,*/*;q=0.8',
     connection: 'keep-alive',
     host: 'localhost:3000' },
  client: 

  (ズラーリ)

}
>

ポイントを絞ってHTTPヘッダだけ表示してみます。

> rq.headers
{ 'cache-control': 'max-age=0',
  'accept-encoding': 'gzip, deflate',
  'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_2) AppleWebKit/534.51.22 (KHTML, lik... (length: 119)',
  'accept-language': 'ja-jp',
  accept: 'text/html,application/xhtml xml,application/xml;q=0.9,*/*;q=0.8',
  connection: 'keep-alive',
  host: 'localhost:3000' }
> 

いい具合です。

REPLを抜けるにはCtrl+Cです。

中断している行がわからない場合、bt, listコマンドを使います。

debug> list()
  1 var http = require('http');
  2 
  3 http.createServer(function(rq, rs) {
  4     debugger;
  5     rs.writeHead(200, {'Content-Type': 'text/plain'});
  6     rs.end('Hello Debugger!');
  7 }).listen(3000, 'localhost');
  8 
  9 console.log('サーバーが起動しました: http://localhost:3000/');
debug> bt
#0 server.js:4:5
debug> 

listは、中断しているコードの前後を表示します。表示された行の真ん中が、現在の行です。(例では、1行目が近いので、実際どこかわかりません。)

btは、現在の行、スタックを表示します。(例では4行目で止まっていることがわかります。)

ウオッチリストを追加しています。

watchでウオッチ追加、unwatchでウオッチ削除、watchersでウオッチの一覧を表示します。

rs.finishedを監視に加え、nコマンドで一行進めます。

(続き)
debug> watch('rs.finished')
debug> n
break in server.js:5
Watchers:
  0: rs.finished = false

  3 http.createServer(function(rq, rs) {
  4     debugger;
  5     rs.writeHead(200, {'Content-Type': 'text/plain'});
  6     rs.end('Hello Debugger!');
  7 }).listen(3000, 'localhost');
debug>

行が1行進んで5行目に、ウオッチリストと値が表示されます。

nを続けてみます。

(続き)
debug> n
break in server.js:6
Watchers:
  0: rs.finished = false

  4     debugger;
  5     rs.writeHead(200, {'Content-Type': 'text/plain'});
  6     rs.end('Hello Debugger!');
  7 }).listen(3000, 'localhost');
  8 
debug> n
break in server.js:7
Watchers:
  0: rs.finished = true

  5     rs.writeHead(200, {'Content-Type': 'text/plain'});
  6     rs.end('Hello Debugger!');
  7 }).listen(3000, 'localhost');
  8 
  9 console.log('サーバーが起動しました: http://localhost:3000/');
debug>

7行目で、rs.finishedがtrueに変化しました。

ウオッチを削除してみます。

debug> watchers
  0: rs.finished = true
debug> unwatch(0)
debug> watchers
debug> 

現在の行にブレークポイントを追加してみます。sbコマンドを使います。

debug> sb()
  2 
  3 http.createServer(function(rq, rs) {
  4     debugger;
  5     rs.writeHead(200, {'Content-Type': 'text/plain'});
  6     rs.end('Hello Debugger!');
* 7 }).listen(3000, 'localhost');
  8 
  9 console.log('サーバーが起動しました: http://localhost:3000/');
 10 
 11 
 12 });
debug>

「*」印がついて7行目にブレークポイントが設定されました。

「c」で継続して、再度ブラウザからアクセスしてみます。

debug> c
break in server.js:4
  2 
  3 http.createServer(function(rq, rs) {
  4     debugger;
  5     rs.writeHead(200, {'Content-Type': 'text/plain'});
  6     rs.end('Hello Debugger!');
debug> c
break in server.js:7
  5     rs.writeHead(200, {'Content-Type': 'text/plain'});
  6     rs.end('Hello Debugger!');
* 7 }).listen(3000, 'localhost');
  8 
  9 console.log('サーバーが起動しました: http://localhost:3000/');
debug> 

4行目のdebuggerで一度止まります。c で継続するとブレークポイントの7行目まで処理が飛びます。

こんな感じでデバッグできます。より詳しいデバッグコマンドは、公式リファレンスを参考に。

0 件のコメント: