ます’s Blog - どうでもいい記事100選

どうでもいい記事100選

「?:」演算子(続きの続き)

昨日のですが。
はてなブックマークで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
%

余計に分からん。。。変数の選択に失敗か。_| ̄|○
次は、もっと簡単な変数を出力対象にしてみよっと。今日はここまで。


最後にですが、このページが大変参考になりました。
有益な情報を公開してくれて本当にありがとうございます。<(_ _)>


それにしても、もう少し自分の文章表現能力は何とかならないものだろうか。。。綺麗に段組するとかサ。
駄文を垂れ流すだけではなくて、もう少し見られる事を意識した方が(後から自分が見返す時とか)有益なのに。
さっき、自分が書いた過去の文書を見返した時に(あまりの意味不明さと見づらさに)ひっくり返りそうになりました。_| ̄|○