当cpu飙升时,找出php中可能有问题…

来源:互联网 发布:美国国债 知乎 编辑:程序博客网 时间:2024/05/22 08:23
当你发现一个平时占用cpu比较少的进程突然间占用cpu接近100%时,你如何找到导致cpu飙升的原因?我的思路是,首先找到进程正在执行的代码行,从而确定可能有问题的代码段。然后,再仔细分析有问题的代码段,从而找出原因。

  如果你的程序使用的是c、c++编写,那么你可以很容易的找到正在执行的代码行。但是,程序是php编写的,如何找到可能有问题的代码行呢?这个问题就是本文要解决的问题。

   背景知识:

  大家都知道php是一个解释性语言。用户编写的php代码会生成opcode,由解释器引擎去解释执行。在解释执行过程中,有一个全局变量包含了执行过程中用到的各种数据。它就是executor_globals。在源码的Zend/zend_globals.h文件中可以找到他的类型定义。

struct _zend_executor_globals {
    zval**return_value_ptr_ptr;
 
    zvaluninitialized_zval;
    zval*uninitialized_zval_ptr;
 
    zvalerror_zval;
    zval*error_zval_ptr;
 
   zend_ptr_stack arg_types_stack;
 
   
    HashTable*symtable_cache[SYMTABLE_CACHE_SIZE];
    HashTable**symtable_cache_limit;
    HashTable**symtable_cache_ptr;
 
    zend_op**opline_ptr;
 
    HashTable*active_symbol_table;
    HashTablesymbol_table;    
 
    HashTableincluded_files;  
 
    JMP_BUF*bailout;
 
    interror_reporting;
    intorig_error_reporting;
    intexit_status;
 
   zend_op_array *active_op_array;
 
    HashTable*function_table; 
    HashTable*class_table;    
    HashTable*zend_constants; 
 
   zend_class_entry *scope;
   zend_class_entry *called_scope;
 
    zval*This;
 
    longprecision;
 
    intticks_count;
 
    zend_boolin_execution;
    HashTable*in_autoload;
   zend_function *autoload_func;
    zend_boolfull_tables_cleanup;
 
   
    zend_boolno_extensions;
 
#ifdef ZEND_WIN32
    zend_booltimed_out;
   OSVERSIONINFOEX windows_version_info;
#endif
 
    HashTableregular_list;
    HashTablepersistent_list;
 
   zend_vm_stack argument_stack;
 
    intuser_error_handler_error_reporting;
    zval*user_error_handler;
    zval*user_exception_handler;
    zend_stackuser_error_handlers_error_reporting;
   zend_ptr_stack user_error_handlers;
   zend_ptr_stack user_exception_handlers;
 
   zend_error_handling_t  error_handling;
   zend_class_entry     *exception_class;
 
   
    inttimeout_seconds;
 
    intlambda_count;
 
    HashTable*ini_directives;
    HashTable*modified_ini_directives;
 
   zend_objects_store objects_store;
    zval*exception, *prev_exception;
    zend_op*opline_before_exception;
    zend_opexception_op[3];
 
    struct_zend_execute_data *current_execute_data;
 
    struct_zend_module_entry *current_module;
 
   zend_property_info std_property_info;
 
    zend_boolactive;
 
    void*saved_fpu_cw;
 
    void*reserved[ZEND_MAX_RESERVED_RESOURCES];
};

  这里我们只说两个对我们比较重要的变量,active_op_array 和 current_execute_data。

  active_op_array变量中保存了引擎正在执行的op_array(想了解什么是op_array请点击查看)。在Zend/zend_compile.h中有关于op_array的数据类型的定义。

struct _zend_op_array {
   
    zend_uchartype;
    char*function_name;       
   zend_class_entry *scope;
    zend_uintfn_flags;
    union_zend_function *prototype;
    zend_uintnum_args;
    zend_uintrequired_num_args;
   zend_arg_info *arg_info;
    zend_boolpass_rest_by_reference;
    unsignedchar return_reference;
   
 
    zend_booldone_pass_two;
 
    zend_uint*refcount;
 
    zend_op*opcodes;
    zend_uintlast, size;
 
   zend_compiled_variable *vars;
    intlast_var, size_var;
 
    zend_uintT;
 
   zend_brk_cont_element *brk_cont_array;
    intlast_brk_cont;
    intcurrent_brk_cont;
 
   zend_try_catch_element *try_catch_array;
    intlast_try_catch;
 
   
    HashTable*static_variables;
 
    zend_op*start_op;
    intbackpatch_count;
 
    zend_uintthis_var;
 
    char*filename;
    zend_uintline_start;
    zend_uintline_end;
    char*doc_comment;
    zend_uintdoc_comment_len;
    zend_uintearly_binding;
 
    void*reserved[ZEND_MAX_RESERVED_RESOURCES];
};

   看完定义,就不用我多说了把。定义中,filename和function_name分别保存了正在执行的文件名和方法名。

  current_execute_data保存了正在执行的op_array的execute_data。execute_data保存了每个op_array执行过程中的一些数据。其定义在,Zend/zend_compile.h:

struct _zend_execute_data { 
    struct_zend_op *opline; 
   zend_function_state function_state; 
   zend_function *fbc;
   zend_class_entry *called_scope; 
   zend_op_array *op_array; 
    zval*object; 
    union_temp_variable *Ts; 
    zval***CVs; 
    HashTable*symbol_table; 
    struct_zend_execute_data *prev_execute_data; 
    zval*old_error_reporting; 
    zend_boolnested; 
    zval**original_return_value; 
   zend_class_entry *current_scope; 
   zend_class_entry *current_called_scope; 
    zval*current_this; 
    zval*current_object; 
    struct_zend_op *call_opline; 
}; 

  定义中的opline就是正在执行的opcode。opcode的结构定义如下:

struct _zend_op {
   opcode_handler_t handler;
    znoderesult;
    znodeop1;
    znodeop2;
    ulongextended_value;
    uintlineno;
    zend_ucharopcode;
};

   其中lineno就是opcode所对应的行号。

   示例说明:

  看完上面的数据结构定义,你是否已经知道如何找php正在执行的文件名,方法名和行号呢?如果还有疑问的话,那就接着看下面的例子。创建一个文件test.php,代码如下:

0 0