「?:」演算子(続きの続き)
昨日の件ですが。
はてなブックマークでshimookaさんからは(嬉しい)励ましの言葉を頂いたり、heavenshellさんからは有益なURLを教えてもらったおかげなのかは分かりませんが、自分の中で「gdbの使い方を(より)理解してみよう!」というモチベーションが上がってきました。
熱しやすく冷めやすい性格なので、熱が冷めないうちに色々とやってみた。
ブレーク・ポイントはファイル(行)指定でも可能なんですね。これは知らなかった。。。
PHPが提供している関数は内部的には「zif_」というプレフィックスがつくのですが、こういうのが分からない時にザックリと(適当に)設定したい時に役に立ちそう。
「i_zend_is_true」関数ではなくて、ファイル(行)指定でブレーク・ポイントを設定して同じ結果になるか確認。
% cd $HOME/php-5.3-dev % gdb ./php-cli GNU gdb 6.4.90-debian Copyright (C) 2006 Free Software Foundation, Inc. GDB is free software, covered by the GNU General Public License, and you are welcome to change it and/or distribute copies of it under certain conditions. Type "show copying" to see the conditions. There is absolutely no warranty for GDB. Type "show warranty" for details. This GDB was configured as "i486-linux-gnu"...Using host libthread_db library "/lib/tls/i686/cmov/libthread_db.so.1". (gdb) b $HOME/php-5.3-dev/work/php5.3-200808250230/Zend/zend_vm_execute.h:9022 Breakpoint 1 at 0x824cef8: file $HOME/php-5.3-dev/work/php5.3-200808250230/Zend/zend_vm_execute.h, line 9022. (gdb) r -r '$_GET["user_id"] = "0"; $user_id = $_GET["user_id"] ?: "anonymous";' Starting program: $HOME/php-5.3-dev/php-cli -r '$_GET["user_id"] = "0"; $user_id = $_GET["user_id"] ?: "anonymous";' Failed to read a valid object file image from memory. Breakpoint 1, ZEND_JMP_SET_SPEC_VAR_HANDLER (execute_data=0xb7ca7048) at $HOME/php-5.3-dev/work/php5.3-200808250230/Zend/zend_vm_execute.h:9022 9022 if (i_zend_is_true(value)) { (gdb) bt #0 ZEND_JMP_SET_SPEC_VAR_HANDLER (execute_data=0xb7ca7048) at $HOME/php-5.3-dev/work/php5.3-200808250230/Zend/zend_vm_execute.h:9022 #1 0x08235a49 in execute (op_array=0xb7d1ea10) at $HOME/php-5.3-dev/work/php5.3-200808250230/Zend/zend_vm_execute.h:104 #2 0x081ffe56 in zend_eval_string (str=0xbfbbe97a "$_GET[\"user_id\"] = \"0\"; $user_id = $_GET[\"user_id\"] ?: \"anonymous\";", retval_ptr=0x0, string_name=0x834a4dc "Command line code") at $HOME/php-5.3-dev/work/php5.3-200808250230/Zend/zend_execute_API.c:1112 #3 0x08200011 in zend_eval_string_ex (str=0xbfbbe97a "$_GET[\"user_id\"] = \"0\"; $user_id = $_GET[\"user_id\"] ?: \"anonymous\";", retval_ptr=0x0, string_name=0x834a4dc "Command line code", handle_exceptions=1) at $HOME/php-5.3-dev/work/php5.3-200808250230/Zend/zend_execute_API.c:1147 #4 0x08293ecc in main (argc=3, argv=0xbfbbd3d4) at $HOME/php-5.3-dev/work/php5.3-200808250230/sapi/cli/php_cli.c:1169
うは。素晴しい。
次は「s(step)」と「l(list)」を実行して、ブレーク・ポイントを設定した場所から、どのように実行されていくか確認。
(gdb) l 9017 { 9018 zend_op *opline = EX(opline); 9019 zend_free_op free_op1; 9020 zval *value = _get_zval_ptr_var(&opline->op1, EX(Ts), &free_op1 TSRMLS_CC); 9021 9022 if (i_zend_is_true(value)) { 9023 EX_T(opline->result.u.var).tmp_var = *value; 9024 zendi_zval_copy_ctor(EX_T(opline->result.u.var).tmp_var); 9025 if (free_op1.var) {zval_ptr_dtor(&free_op1.var);}; 9026 #if DEBUG_ZEND>=2 (gdb) s i_zend_is_true (op=0xb7d332dc) at $HOME/php-5.3-dev/work/php5.3-200808250230/Zend/zend_execute.h:82 82 switch (Z_TYPE_P(op)) { (gdb) l 77 78 static inline int i_zend_is_true(zval *op) 79 { 80 int result; 81 82 switch (Z_TYPE_P(op)) { 83 case IS_NULL: 84 result = 0; 85 break; 86 case IS_LONG: (gdb) s 95 if (Z_STRLEN_P(op) == 0 (gdb) l 90 break; 91 case IS_DOUBLE: 92 result = (Z_DVAL_P(op) ? 1 : 0); 93 break; 94 case IS_STRING: 95 if (Z_STRLEN_P(op) == 0 96 || (Z_STRLEN_P(op)==1 && Z_STRVAL_P(op)[0]=='0')) { 97 result = 0; 98 } else { 99 result = 1; (gdb) s 97 result = 0; (gdb) l 92 result = (Z_DVAL_P(op) ? 1 : 0); 93 break; 94 case IS_STRING: 95 if (Z_STRLEN_P(op) == 0 96 || (Z_STRLEN_P(op)==1 && Z_STRVAL_P(op)[0]=='0')) { 97 result = 0; 98 } else { 99 result = 1; 100 } 101 break; (gdb) s 132 return result; (gdb) l 127 break; 128 default: 129 result = 0; 130 break; 131 } 132 return result; 133 } 134 135 ZEND_API int zval_update_constant(zval **pp, void *arg TSRMLS_DC); 136 ZEND_API int zval_update_constant_ex(zval **pp, void *arg, zend_class_entry *scope TSRMLS_DC); (gdb) s 133 } (gdb) l 128 default: 129 result = 0; 130 break; 131 } 132 return result; 133 } 134 135 ZEND_API int zval_update_constant(zval **pp, void *arg TSRMLS_DC); 136 ZEND_API int zval_update_constant_ex(zval **pp, void *arg, zend_class_entry *scope TSRMLS_DC); 137 (gdb) s ZEND_JMP_SET_SPEC_VAR_HANDLER (execute_data=0xb7cbc048) at $HOME/php-5.3-dev/work/php5.3-200808250230/Zend/zend_vm_execute.h:9032 9032 if (free_op1.var) {zval_ptr_dtor(&free_op1.var);}; (gdb) l 9027 printf("Conditional jmp to %d\n", opline->op2.u.opline_num); 9028 #endif 9029 ZEND_VM_JMP(opline->op2.u.jmp_addr); 9030 } 9031 9032 if (free_op1.var) {zval_ptr_dtor(&free_op1.var);}; 9033 ZEND_VM_NEXT_OPCODE(); 9034 } 9035 9036 static int ZEND_FASTCALL ZEND_QM_ASSIGN_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS) (gdb) s 9033 ZEND_VM_NEXT_OPCODE(); (gdb) l 9028 #endif 9029 ZEND_VM_JMP(opline->op2.u.jmp_addr); 9030 } 9031 9032 if (free_op1.var) {zval_ptr_dtor(&free_op1.var);}; 9033 ZEND_VM_NEXT_OPCODE(); 9034 } 9035 9036 static int ZEND_FASTCALL ZEND_QM_ASSIGN_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS) 9037 { (gdb) s 9034 } (gdb) l 9029 ZEND_VM_JMP(opline->op2.u.jmp_addr); 9030 } 9031 9032 if (free_op1.var) {zval_ptr_dtor(&free_op1.var);}; 9033 ZEND_VM_NEXT_OPCODE(); 9034 } 9035 9036 static int ZEND_FASTCALL ZEND_QM_ASSIGN_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS) 9037 { 9038 zend_op *opline = EX(opline); (gdb) s ZEND_QM_ASSIGN_SPEC_CONST_HANDLER (execute_data=0xb7cbc048) at $HOME/php-5.3-dev/work/php5.3-200808250230/Zend/zend_vm_execute.h:2307 2307 zend_op *opline = EX(opline); (gdb) l 2302 ZEND_VM_NEXT_OPCODE(); 2303 } 2304 2305 static int ZEND_FASTCALL ZEND_QM_ASSIGN_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) 2306 { 2307 zend_op *opline = EX(opline); 2308 2309 zval *value = &opline->op1.u.constant; 2310 2311 EX_T(opline->result.u.var).tmp_var = *value;
順を追って、どのように実行されているのかを理解できるのが面白い。。。なんか、巨大ロボットの中に入って操縦している気分。(w
実行されている(ファイル)箇所が変更されるとファイル名が表示されるのは嬉しい(より追いやすくなる)。
「s(step)」が「ステップイン実行」で「n(next)」が「ステップオーバ実行」らしいんだけど、違いが分からない。。。「s(step)」の方が(より)使えそうな感じだけど(今の状況では)どっちもそんなに変わらなそう。_| ̄|○
戻る事ができると嬉しいんだけどなぁ。。。ちょっと調べてみた限りでは無理そう。探し足りないだけかしら。
「c(continue)」で最後まで一気に実行できる、と。
(gdb) c Continuing. Program exited normally.
「h(help)」とか「i(info)」とか(まだ)よく分かっていないけど、「i(info)breakpoints」を実行する事で、現在のブレーク・ポイント設定状況が分かる、と。
でもって、「d(delete)number」を実行する事でブレーク・ポイントの設定が削除ができる、と。
(gdb) h List of classes of commands: aliases -- Aliases of other commands breakpoints -- Making program stop at certain points data -- Examining data files -- Specifying and examining files internals -- Maintenance commands obscure -- Obscure features running -- Running the program stack -- Examining the stack status -- Status inquiries support -- Support facilities tracepoints -- Tracing of program execution without stopping the program user-defined -- User-defined commands Type "help" followed by a class name for a list of commands in that class. Type "help" followed by command name for full documentation. Command name abbreviations are allowed if unambiguous. (gdb) h breakpoints Making program stop at certain points. List of commands: awatch -- Set a watchpoint for an expression break -- Set breakpoint at specified line or function catch -- Set catchpoints to catch events clear -- Clear breakpoint at specified line or function commands -- Set commands to be executed when a breakpoint is hit condition -- Specify breakpoint number N to break only if COND is true delete -- Delete some breakpoints or auto-display expressions disable -- Disable some breakpoints enable -- Enable some breakpoints hbreak -- Set a hardware assisted breakpoint ignore -- Set ignore-count of breakpoint number N to COUNT rbreak -- Set a breakpoint for all functions matching REGEXP rwatch -- Set a read watchpoint for an expression tbreak -- Set a temporary breakpoint tcatch -- Set temporary catchpoints to catch events thbreak -- Set a temporary hardware assisted breakpoint watch -- Set a watchpoint for an expression Type "help" followed by command name for full documentation. Command name abbreviations are allowed if unambiguous. (gdb) i "info" must be followed by the name of an info command. List of info subcommands: info address -- Describe where symbol SYM is stored info all-registers -- List of all registers and their contents info args -- Argument variables of current stack frame info auxv -- Display the inferior's auxiliary vector info breakpoints -- Status of user-settable breakpoints info catch -- Exceptions that can be caught in the current stack frame info checkpoints -- IDs of currently known forks/checkpoints info classes -- All Objective-C classes info common -- Print out the values contained in a Fortran COMMON block info copying -- Conditions for redistributing copies of GDB info dcache -- Print information on the dcache performance info display -- Expressions to display when program stops info extensions -- All filename extensions associated with a source language info files -- Names of targets and files being debugged info float -- Print the status of the floating point unit info forks -- IDs of currently known forks/checkpoints info frame -- All about selected stack frame info functions -- All function names info handle -- What debugger does when program gets various signals info line -- Core addresses of the code for a source line info locals -- Local variables of current stack frame info macro -- Show the definition of MACRO info mem -- Memory region attributes info proc -- Show /proc process information about any running process info program -- Execution status of the program info registers -- List of integer registers and their contents info scope -- List the variables local to a scope info selectors -- All Objective-C selectors info set -- Show all GDB settings info sharedlibrary -- Status of loaded shared object libraries info signals -- What debugger does when program gets various signals info source -- Information about the current source file info sources -- Source files in the program info stack -- Backtrace of the stack info symbol -- Describe what symbol is at location ADDR info target -- Names of targets and files being debugged info terminal -- Print inferior's saved terminal status info threads -- IDs of currently known threads info tracepoints -- Status of tracepoints info types -- All type names info variables -- All global and static variable names info vector -- Print the status of the vector unit info warranty -- Various kinds of warranty you do not have info watchpoints -- Synonym for ``info breakpoints'' info win -- List of all displayed windows Type "help info" followed by info subcommand name for full documentation. Command name abbreviations are allowed if unambiguous. (gdb) i breakpoints Num Type Disp Enb Address What 1 breakpoint keep y 0x0824cef8 in ZEND_JMP_SET_SPEC_VAR_HANDLER at $HOME/php-5.3-dev/work/php5.3-200808250230/Zend/zend_vm_execute.h:9022 breakpoint already hit 1 time (gdb) d 1 (gdb) i breakpoints No breakpoints or watchpoints. (gdb) q %
それでは、いつも枕を濡らしていた箇所にブレーク・ポイントを設定して実行してみると。。。
% cd $HOME/php-5.3-dev % gdb ./php-cli GNU gdb 6.4.90-debian Copyright (C) 2006 Free Software Foundation, Inc. GDB is free software, covered by the GNU General Public License, and you are welcome to change it and/or distribute copies of it under certain conditions. Type "show copying" to see the conditions. There is absolutely no warranty for GDB. Type "show warranty" for details. This GDB was configured as "i486-linux-gnu"...Using host libthread_db library "/lib/tls/i686/cmov/libthread_db.so.1". (gdb) b $HOME/php-5.3-dev/work/php5.3-200808250230/Zend/zend_compile.c:4703 Breakpoint 1 at 0x81f9e05: file $HOME/php-5.3-dev/work/php5.3-200808250230/Zend/zend_compile.c, line 4703. (gdb) r -r '$_GET["user_id"] = "0"; $user_id = $_GET["user_id"] ?: "anonymous";' Starting program: $HOME/php-5.3-dev/php-cli -r '$_GET["user_id"] = "0"; $user_id = $_GET["user_id"] ?: "anonymous";' Failed to read a valid object file image from memory. Breakpoint 1, zend_do_jmp_set (value=0xbfa50ca4, jmp_token=0xbfa50cb8, colon_token=0xbfa50ccc) at $HOME/php-5.3-dev/work/php5.3-200808250230/Zend/zend_compile.c:4703 4703 opline->opcode = ZEND_JMP_SET; (gdb) bt #0 zend_do_jmp_set (value=0xbf937384, jmp_token=0xbf937398, colon_token=0xbf9373ac) at $HOME/php-5.3-dev/work/php5.3-200808250230/Zend/zend_compile.c:4703 #1 0x081d2f6c in zendparse () at $HOME/php-5.3-dev/work/php5.3-200808250230/Zend/zend_language_parser.c:4411 #2 0x081d6313 in compile_string (source_string=0xbf938590, filename=0x834a4dc "Command line code") at Zend/zend_language_scanner.l:505 #3 0x081ffdf8 in zend_eval_string (str=0xbf93a97a "$_GET[\"user_id\"] = \"0\"; $user_id = $_GET[\"user_id\"] ?: \"anonymous\";", retval_ptr=0x0, string_name=0x834a4dc "Command line code") at $HOME/php-5.3-dev/work/php5.3-200808250230/Zend/zend_execute_API.c:1097 #4 0x08200011 in zend_eval_string_ex (str=0xbf93a97a "$_GET[\"user_id\"] = \"0\"; $user_id = $_GET[\"user_id\"] ?: \"anonymous\";", retval_ptr=0x0, string_name=0x834a4dc "Command line code", handle_exceptions=1) at $HOME/php-5.3-dev/work/php5.3-200808250230/Zend/zend_execute_API.c:1147 #5 0x08293ecc in main (argc=3, argv=0xbf938944) at $HOME/php-5.3-dev/work/php5.3-200808250230/sapi/cli/php_cli.c:1169 (gdb) l 4698 void zend_do_jmp_set(const znode *value, znode *jmp_token, znode *colon_token TSRMLS_DC) 4699 { 4700 int op_number = get_next_op_number(CG(active_op_array)); 4701 zend_op *opline = get_next_op(CG(active_op_array) TSRMLS_CC); 4702 4703 opline->opcode = ZEND_JMP_SET; 4704 opline->result.op_type = IS_TMP_VAR; 4705 opline->result.u.var = get_temporary_variable(CG(active_op_array)); 4706 opline->op1 = *value; 4707 SET_UNUSED(opline->op2); (gdb) s 4704 opline->result.op_type = IS_TMP_VAR; (gdb) 4705 opline->result.u.var = get_temporary_variable(CG(active_op_array)); (gdb) get_temporary_variable (op_array=0xb7df8a10) at $HOME/php-5.3-dev/work/php5.3-200808250230/Zend/zend_compile.c:240 240 return (op_array->T)++ * sizeof(temp_variable); (gdb) 241 } (gdb) zend_do_jmp_set (value=0xbf937384, jmp_token=0xbf937398, colon_token=0xbf9373ac) at $HOME/php-5.3-dev/work/php5.3-200808250230/Zend/zend_compile.c:4706 4706 opline->op1 = *value; 〜 省略 〜 (gdb) _get_zval_ptr_var (node=0xb7df8f24, Ts=0xb7d81098, should_free=0xbf938580) at $HOME/php-5.3-dev/work/php5.3-200808250230/Zend/zend_execute.c:185 185 return ptr; (gdb) 210 } (gdb) ZEND_JMP_SET_SPEC_VAR_HANDLER (execute_data=0xb7d81048) at $HOME/php-5.3-dev/work/php5.3-200808250230/Zend/zend_vm_execute.h:9022 9022 if (i_zend_is_true(value)) { (gdb) i_zend_is_true (op=0xb7df82dc) at $HOME/php-5.3-dev/work/php5.3-200808250230/Zend/zend_execute.h:82 82 switch (Z_TYPE_P(op)) { (gdb) 95 if (Z_STRLEN_P(op) == 0 (gdb) 97 result = 0; (gdb) 132 return result; (gdb) 133 } (gdb) ZEND_JMP_SET_SPEC_VAR_HANDLER (execute_data=0xb7d81048) at $HOME/php-5.3-dev/work/php5.3-200808250230/Zend/zend_vm_execute.h:9032 9032 if (free_op1.var) {zval_ptr_dtor(&free_op1.var);}; (gdb) 9033 ZEND_VM_NEXT_OPCODE(); (gdb) 9034 } (gdb) ZEND_QM_ASSIGN_SPEC_CONST_HANDLER (execute_data=0xb7d81048) at $HOME/php-5.3-dev/work/php5.3-200808250230/Zend/zend_vm_execute.h:2307 2307 zend_op *opline = EX(opline); (gdb) 2309 zval *value = &opline->op1.u.constant; (gdb) 2311 EX_T(opline->result.u.var).tmp_var = *value; (gdb) 2313 zval_copy_ctor(&EX_T(opline->result.u.var).tmp_var); (gdb) c Continuing. Program exited normally. (gdb) q %
何度エンター・キーを打ち込んだか分からない。。。300回くらいは押したのではないだろうか。盛大に自爆。_| ̄|○
この方法では分からなかったか。。。うーん。他を検討してみよっと。
色んなファイル(yy関連とかメモリ確保とか)を経由している事が分かっただけでも良しとしよう。
最後に変数を表示してみる。
% cd $HOME/php-5.3-dev % gdb ./php-cli GNU gdb 6.4.90-debian Copyright (C) 2006 Free Software Foundation, Inc. GDB is free software, covered by the GNU General Public License, and you are welcome to change it and/or distribute copies of it under certain conditions. Type "show copying" to see the conditions. There is absolutely no warranty for GDB. Type "show warranty" for details. This GDB was configured as "i486-linux-gnu"...Using host libthread_db library "/lib/tls/i686/cmov/libthread_db.so.1". (gdb) b $HOME/php-5.3-dev/work/php5.3-200808250230/Zend/zend_compile.c:4707 Breakpoint 1 at 0x81f9e4c: file $HOME/php-5.3-dev/work/php5.3-200808250230/Zend/zend_compile.c, line 4707. (gdb) r -r '$_GET["user_id"] = "0"; $user_id = $_GET["user_id"] ?: "anonymous";' Starting program: $HOME/php-5.3-dev/php-cli -r '$_GET["user_id"] = "0"; $user_id = $_GET["user_id"] ?: "anonymous";' Failed to read a valid object file image from memory. Breakpoint 1, zend_do_jmp_set (value=0xbf8fbb54, jmp_token=0xbf8fbb68, colon_token=0xbf8fbb7c) at $HOME/php-5.3-dev/work/php5.3-200808250230/Zend/zend_compile.c:4707 4707 SET_UNUSED(opline->op2); (gdb) p opline $1 = (zend_op *) 0xb7deef0c (gdb) p *opline $2 = {handler = 0, result = {op_type = 2, u = {constant = {value = {lval = 100, dval = 4.9406564584124654e-322, str = { val = 0x64 <Address 0x64 out of bounds>, len = 0}, ht = 0x64, obj = {handle = 100, handlers = 0x0}}, refcount__gc = 0, type = 0 '\0', is_ref__gc = 0 '\0'}, var = 100, opline_num = 100, op_array = 0x64, jmp_addr = 0x64, EA = {var = 100, type = 0}}}, op1 = {op_type = 4, u = {constant = {value = {lval = 80, dval = 3.3951932694969609e-313, str = {val = 0x50 <Address 0x50 out of bounds>, len = 16}, ht = 0x50, obj = {handle = 80, handlers = 0x10}}, refcount__gc = 0, type = 0 '\0', is_ref__gc = 0 '\0'}, var = 80, opline_num = 80, op_array = 0x50, jmp_addr = 0x50, EA = {var = 80, type = 16}}}, op2 = {op_type = 0, u = {constant = {value = { lval = 0, dval = 0, str = {val = 0x0, len = 0}, ht = 0x0, obj = {handle = 0, handlers = 0x0}}, refcount__gc = 0, type = 0 '\0', is_ref__gc = 0 '\0'}, var = 0, opline_num = 0, op_array = 0x0, jmp_addr = 0x0, EA = {var = 0, type = 0}}}, extended_value = 0, lineno = 1, opcode = 152 '\230'} (gdb) c Continuing. Program exited normally. (gdb) q %
余計に分からん。。。変数の選択に失敗か。_| ̄|○
次は、もっと簡単な変数を出力対象にしてみよっと。今日はここまで。
最後にですが、このページが大変参考になりました。
有益な情報を公開してくれて本当にありがとうございます。<(_ _)>
それにしても、もう少し自分の文章表現能力は何とかならないものだろうか。。。綺麗に段組するとかサ。
駄文を垂れ流すだけではなくて、もう少し見られる事を意識した方が(後から自分が見返す時とか)有益なのに。
さっき、自分が書いた過去の文書を見返した時に(あまりの意味不明さと見づらさに)ひっくり返りそうになりました。_| ̄|○