Opcode查看利器之vld

简介

在PHP的生命周期中

词法分析(zend_language_scanner),将PHP代码转换为语言片段(Tokens)
语法分析(zend_language_parser)将Tokens转换成简单而有意义的表达式
编译(compiler),将表达式编译成Opocdes,返回zend_op_array指针
Zend Engine(zend_vm_execute),顺次执行Opcodes,每次一条, 根据传入的zend_op_array指针,执行opcode并将结果返回输出

在上述执行过程中,经常被人提起的解释型语言性能问题也就是因为每次执行脚本,上述过程都会重复执行。因此,也就出现了APC, xcache, eAccelerator等缓存,不过现在官方主推的是opcache

 

什么是opcode缓存

当解释器完成对脚本代码的分析后,便将它们生成可以直接运行的中间代码,也称为操作码(Operate Code,opcode)。Opcode cache的目地是避免重复编译,减少CPU和内存开销。如果动态内容的性能瓶颈不在于CPU和内存,而在于I/O操作,比如数据库查询带来的磁盘I/O 开销,那么opcode cache的性能提升是非常有限的。也就是opcode cache能带来CPU和内存开销的降低

APC, xcache, eAccelerator,opcache 使用共享内存进行存储,并且可以直接从中执行文件,而不用在执行前“反序列化”代码

词法分析

Zend/zend_language_scanner.c 会根据Zend/zend_language_scanner.l(Lex文件),来对输入的 PHP代码进行词法分析,从而得到一个一个的“词”,PHP4.2+开始提供了一个函数叫token_get_all ,这个函数就可以将一段PHP代码 Scanning成Tokens

 

一、安装VLD扩展

1、下载

地址:http://pecl.php.net/package/vld

wget  http://pecl.php.net/get/vld-0.13.0.tgz

 2、编译安装

cd /usr/local/src/
wget http://pecl.php.net/get/vld-0.13.0.tgz
tar -zxf vld-0.13.0.tgz 
cd vld-0.13.0
phpize && ./configure  --with-php-config=php-config --enable-vld
make && make install
echo extension=vld.so >> /etc/php.d/vld.ini
service php-fpm reload
php-config、phpize要加入环境变量

 3、opcode的结构

vim Zend/zend_compile.h

typedef struct _zend_op_array zend_op_array;
typedef struct _zend_op zend_op;

 

struct _zend_op {
        opcode_handler_t handler;
        znode_op op1;
        znode_op op2;
        znode_op result;
        ulong extended_value;
        uint lineno;
        zend_uchar opcode;
        zend_uchar op1_type;
        zend_uchar op2_type;
        zend_uchar result_type;
};

 

struct _zend_op_array {
        /* Common elements */
        zend_uchar type;
        const char *function_name;
        zend_class_entry *scope;
        zend_uint fn_flags;
        union _zend_function *prototype;
        zend_uint num_args;
        zend_uint required_num_args;
        zend_arg_info *arg_info;
        /* END of common elements */

        zend_uint *refcount;

        zend_op *opcodes;
        zend_uint last;

        zend_compiled_variable *vars;
        int last_var;

        zend_uint T;

        zend_brk_cont_element *brk_cont_array;
        int last_brk_cont;

        zend_try_catch_element *try_catch_array;
        int last_try_catch;

        /* static variables support */
        HashTable *static_variables;

        zend_uint this_var;

        const char *filename;
        zend_uint line_start;
        zend_uint line_end;
        const char *doc_comment;
        zend_uint doc_comment_len;
        zend_uint early_binding; /* the linked list of delayed declarations */

        zend_literal *literals;
        int last_literal;

        void **run_time_cache;
        int  last_cache_slot;

        void *reserved[ZEND_MAX_RESERVED_RESOURCES];
};

 4、opcode的使用

STD_PHP_INI_ENTRY("vld.active",       "0", PHP_INI_SYSTEM, OnUpdateBool, active,       zend_vld_globals, vld_globals)
STD_PHP_INI_ENTRY("vld.skip_prepend", "0", PHP_INI_SYSTEM, OnUpdateBool, skip_prepend, zend_vld_globals, vld_globals)
STD_PHP_INI_ENTRY("vld.skip_append",  "0", PHP_INI_SYSTEM, OnUpdateBool, skip_append,  zend_vld_globals, vld_globals)
STD_PHP_INI_ENTRY("vld.execute",      "1", PHP_INI_SYSTEM, OnUpdateBool, execute,      zend_vld_globals, vld_globals)
STD_PHP_INI_ENTRY("vld.verbosity",    "1", PHP_INI_SYSTEM, OnUpdateBool, verbosity,    zend_vld_globals, vld_globals)
STD_PHP_INI_ENTRY("vld.format",       "0", PHP_INI_SYSTEM, OnUpdateBool, format,       zend_vld_globals, vld_globals)
STD_PHP_INI_ENTRY("vld.col_sep",      "\t", PHP_INI_SYSTEM, OnUpdateString, col_sep,   zend_vld_globals, vld_globals)
STD_PHP_INI_ENTRY("vld.save_dir",     "/tmp", PHP_INI_SYSTEM, OnUpdateString, save_dir, zend_vld_globals, vld_globals)
STD_PHP_INI_ENTRY("vld.save_paths",   "0", PHP_INI_SYSTEM, OnUpdateBool, save_paths,   zend_vld_globals, vld_globals)
STD_PHP_INI_ENTRY("vld.dump_paths",   "1", PHP_INI_SYSTEM, OnUpdateBool, dump_paths,   zend_vld_globals, vld_globals)

 这些是vld的系统配置

默认值

static void vld_init_globals(zend_vld_globals *vld_globals)
{
        vld_globals->active       = 0;
        vld_globals->skip_prepend = 0;
        vld_globals->skip_append  = 0;
        vld_globals->execute      = 1;
        vld_globals->format       = 0;
        vld_globals->col_sep      = "\t";
        vld_globals->path_dump_file = NULL;
        vld_globals->dump_paths   = 1;
        vld_globals->save_paths   = 0;
        vld_globals->verbosity    = 1;
}

 php -dvld.active=1 -dvld.execute= -f  test.php

[root@localhost code]# php -dvld.active test.php    
Finding entry points
Branch analysis from position: 0
Jump found. Position 1 = -2
filename:       /data/code/test.php
function name:  (null)
number of ops:  5
compiled vars:  none
line     #* E I O op                           fetch          ext  return  operands
-------------------------------------------------------------------------------------
   2     0  E >   SEND_VAL                                                 'dbname'
         1        DO_FCALL                                      1  $0      'config'
         2        SEND_VAR_NO_REF                               6          $0
         3        DO_FCALL                                      1          'var_dump'
   3     4      > RETURN                                                   1

branch: #  0; line:     2-    3; sop:     0; eop:     4; out1:  -2
path #1: 0, 

更多指令参见:http://php.net/manual/en/internals2.opcodes.list.php,同时列出了每条指令的案例

posted @ 2015-07-15 13:25  踏雪无痕SS  阅读(878)  评论(0编辑  收藏  举报