<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                從上一小節讀者可以了解到opcode在PHP內部的實現,那怎么找到某個opcode的處理函數呢?為了方便讀者在追蹤代碼的過程中找到各種opcode對應的處理函數實現,下面介紹幾種方法。 > 從PHP5.1開始,PHP對opcode的分發方式可以用戶自定義,分為CALL,SWITCH和GOTO三種類型。 默認使用的CALL的方式,本文也應用于這種方式。有關Zend虛擬機的介紹請閱讀后面相關內容。 ### Debug法 在學習研究PHP內核的過程中,經常通過opcode來查看代碼的執行順序,opcode的執行由在文件Zend/zend_vm_execute.h中的execute函數執行。 ZEND_API void execute(zend_op_array *op_array TSRMLS_DC) { ... zend_vm_enter: .... if ((ret = EX(opline)->handler(execute_data TSRMLS_CC)) > 0) { switch (ret) { case 1: EG(in_execution) = original_in_execution; return; case 2: op_array = EG(active_op_array); goto zend_vm_enter; case 3: execute_data = EG(current_execute_data); default: break; } } ... } 在執行的過程中,EX(opline)->handler(展開后為 *execute_data->opline->handler)存儲了處理當前操作的函數指針。使用gdb調試,在execute函數處增加斷點,使用p命令可以打印出類似這樣的結果: (gdb) p *execute_data->opline->handler $1 = {int (zend_execute_data *)} 0x10041f394 <ZEND_NOP_SPEC_HANDLER> 這樣就可以方便的知道當前要執行的處理函數了,這種debug的方法。這種方法比較麻煩,需要使用gdb來調試。 ### 計算法 在PHP內部有一個函數用來快速的返回特定opcode對應的opcode處理函數指針:zend_vm_get_opcode_handler()函數: static opcode_handler_t zend_vm_get_opcode_handler(zend_uchar opcode, zend_op* op) { static const int zend_vm_decode[] = { _UNUSED_CODE, /* 0 */ _CONST_CODE, /* 1 = IS_CONST */ _TMP_CODE, /* 2 = IS_TMP_VAR */ _UNUSED_CODE, /* 3 */ _VAR_CODE, /* 4 = IS_VAR */ _UNUSED_CODE, /* 5 */ _UNUSED_CODE, /* 6 */ _UNUSED_CODE, /* 7 */ _UNUSED_CODE, /* 8 = IS_UNUSED */ _UNUSED_CODE, /* 9 */ _UNUSED_CODE, /* 10 */ _UNUSED_CODE, /* 11 */ _UNUSED_CODE, /* 12 */ _UNUSED_CODE, /* 13 */ _UNUSED_CODE, /* 14 */ _UNUSED_CODE, /* 15 */ _CV_CODE /* 16 = IS_CV */ }; return zend_opcode_handlers[ opcode * 25 + zend_vm_decode[op->op1.op_type] * 5 + zend_vm_decode[op->op2.op_type]]; } 由上面的代碼可以看到,opcode到php內部函數指針的查找是由下面的公式來進行的: opcode * 25 + zend_vm_decode[op->op1.op_type] * 5 + zend_vm_decode[op->op2.op_type] 然后將其計算的數值作為索引到zend_init_opcodes_handlers數組中進行查找。不過這個數組實在是太大了,有3851個元素,手動查找和計算都比較麻煩。 ### 命名查找法 上面的兩種方法其實都是比較麻煩的,在定位某一opcode的實現執行代碼的過程中,都不得不對程序進行執行或者計算中間值。而在追蹤的過程中,筆者發現處理函數名稱是有一定規則的。這里以函數調用的opcode為例,調用某函數的opcode及其對應在php內核中實現的處理函數如下: //函數調用: DO_FCALL ==> ZEND_DO_FCALL_SPEC_CONST_HANDLER ? //變量賦值: ASSIGN => ZEND_ASSIGN_SPEC_VAR_CONST_HANDLER ZEND_ASSIGN_SPEC_VAR_TMP_HANDLER ZEND_ASSIGN_SPEC_VAR_VAR_HANDLER ZEND_ASSIGN_SPEC_VAR_CV_HANDLER //變量加法: ASSIGN_SUB => ZEND_ASSIGN_SUB_SPEC_VAR_CONST_HANDLER, ZEND_ASSIGN_SUB_SPEC_VAR_TMP_HANDLER, ZEND_ASSIGN_SUB_SPEC_VAR_VAR_HANDLER, ZEND_ASSIGN_SUB_SPEC_VAR_UNUSED_HANDLER, ZEND_ASSIGN_SUB_SPEC_VAR_CV_HANDLER, ZEND_ASSIGN_SUB_SPEC_UNUSED_CONST_HANDLER, ZEND_ASSIGN_SUB_SPEC_UNUSED_TMP_HANDLER, ZEND_ASSIGN_SUB_SPEC_UNUSED_VAR_HANDLER, ZEND_ASSIGN_SUB_SPEC_UNUSED_UNUSED_HANDLER, ZEND_ASSIGN_SUB_SPEC_UNUSED_CV_HANDLER, ZEND_ASSIGN_SUB_SPEC_CV_CONST_HANDLER, ZEND_ASSIGN_SUB_SPEC_CV_TMP_HANDLER, ZEND_ASSIGN_SUB_SPEC_CV_VAR_HANDLER, ZEND_ASSIGN_SUB_SPEC_CV_UNUSED_HANDLER, ZEND_ASSIGN_SUB_SPEC_CV_CV_HANDLER, 在上面的命名就會發現,其實處理函數的命名是有以下規律的: ZEND_[opcode]_SPEC_(變量類型1)_(變量類型2)_HANDLER 這里的變量類型1和變量類型2是可選的,如果同時存在,那就是左值和右值,歸納有下幾類:VAR TMP CV UNUSED CONST這樣可以根據相關的執行場景來判定。 ### 日志記錄法 這種方法是上面**計算法**的升級,同時也是比較精準的方式。在**zend_vm_get_opcode_handler** 方法中添加以下代碼: static opcode_handler_t zend_vm_get_opcode_handler(zend_uchar opcode, zend_op* op) { static const int zend_vm_decode[] = { _UNUSED_CODE, /* 0 */ _CONST_CODE, /* 1 = IS_CONST */ _TMP_CODE, /* 2 = IS_TMP_VAR */ _UNUSED_CODE, /* 3 */ _VAR_CODE, /* 4 = IS_VAR */ _UNUSED_CODE, /* 5 */ _UNUSED_CODE, /* 6 */ _UNUSED_CODE, /* 7 */ _UNUSED_CODE, /* 8 = IS_UNUSED */ _UNUSED_CODE, /* 9 */ _UNUSED_CODE, /* 10 */ _UNUSED_CODE, /* 11 */ _UNUSED_CODE, /* 12 */ _UNUSED_CODE, /* 13 */ _UNUSED_CODE, /* 14 */ _UNUSED_CODE, /* 15 */ _CV_CODE /* 16 = IS_CV */ }; ? //很顯然,我們把opcode和相對應的寫到了/tmp/php.log文件中 int op_index; op_index = opcode * 25 + zend_vm_decode[op->op1.op_type] * 5 + zend_vm_decode[op->op2.op_type]; ? FILE *stream; if((stream = fopen("/tmp/php.log", "a+")) != NULL){ fprintf(stream, "opcode: %d , zend_opcode_handlers_index:%d\n", opcode, op_index); } fclose(stream); ? ? return zend_opcode_handlers[ opcode * 25 + zend_vm_decode[op->op1.op_type] * 5 + zend_vm_decode[op->op2.op_type]]; } 然后,就可以在**/tmp/php.log**文件中生成類似如下結果: opcode: 38 , zend_opcode_handlers_index:970 前面的數字是opcode的,我們可以這里查到: http://php.net/manual/en/internals2.opcodes.list.php后面的數字是static const opcode_handler_t labels[] 索引,里面對應了處理函數的名稱,對應源碼文件是:Zend/zend_vm_execute.h (第30077行左右)。 這是一個超大的數組,php5.3.4中有3851個元素,在上面的例子里,看樣子我們要數到第970個了,當然,有很多種方法來避免人工去計算,這里就不多介紹了。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看