设为首页 - 加入收藏 ASP站长网(Aspzz.Cn)- 科技、建站、经验、云计算、5G、大数据,站长网!
热搜: 创业者 手机 数据
当前位置: 首页 > 站长学院 > PHP教程 > 正文

迅速找出php中可能导致cpu飙升问题的代码行

发布时间:2022-02-23 15:01 所属栏目:121 来源:互联网
导读:cpu飙升占100%不但是与数据库或硬盘的io输入有关,但程序也是一个非常重要的地方了,今天小编找到一个代码可以测试你的php引起cpu占100%问题的解决办法,有兴趣的朋友可进入参考. 用cpu接近100%时,你如何找到导致cpu飙升的原因?我的思路是,首先找到进程正在执
  cpu飙升占100%不但是与数据库或硬盘的io输入有关,但程序也是一个非常重要的地方了,今天小编找到一个代码可以测试你的php引起cpu占100%问题的解决办法,有兴趣的朋友可进入参考.
 
  用cpu接近100%时,你如何找到导致cpu飙升的原因?我的思路是,首先找到进程正在执行的代码行,从而确定可能有问题的代码段,然后,再仔细分析有问题的代码段,从而找出原因.
 
  如果你的程序使用的是c、c++编写,那么你可以很容易的找到正在执行的代码行,但是,程序是php编写的,如何找到可能有问题的代码行呢?这个问题就是本文要解决的问题.
 
  背景知识:大家都知道php是一个解释性语言,用户编写的php代码会生成opcode,由解释器引擎去解释执行,在解释执行过程中,有一个全局变量包含了执行过 程中用到的各种数据,它就是executor_globals,在源码的Zend/zend_globals.h 文件中可以找到他的类型定义,代码如下:
 
  struct _zend_executor_globals {
      zval **return_value_ptr_ptr;
      zval uninitialized_zval;
      zval *uninitialized_zval_ptr;
      zval error_zval;
      zval *error_zval_ptr;
      zend_ptr_stack arg_types_stack;
      /* symbol table cache */
      HashTable *symtable_cache[SYMTABLE_CACHE_SIZE];
      HashTable **symtable_cache_limit;
      HashTable **symtable_cache_ptr;
      zend_op **opline_ptr;
      HashTable *active_symbol_table;
      HashTable symbol_table;     /* main symbol table */
      HashTable included_files;   /* files already included */
      JMP_BUF *bailout;
      int error_reporting;
      int orig_error_reporting;
      int exit_status;
      zend_op_array *active_op_array;
      HashTable *function_table;  /* function symbol table */
      HashTable *class_table;     /* class table */
      HashTable *zend_constants;  /* constants table */
      zend_class_entry *scope;
      zend_class_entry *called_scope; /* Scope of the calling class */
      zval *This;
      long precision;
      int ticks_count;
      zend_bool in_execution;
      HashTable *in_autoload;
      zend_function *autoload_func;
      zend_bool full_tables_cleanup;
      /* for extended information support */
      zend_bool no_extensions;
  #ifdef ZEND_WIN32
      zend_bool timed_out;
      OSVERSIONINFOEX windows_version_info;
  #endif
      HashTable regular_list;
      HashTable persistent_list;
      zend_vm_stack argument_stack;
      int user_error_handler_error_reporting;
      zval *user_error_handler;
      zval *user_exception_handler;
      zend_stack user_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;
      /* timeout support */
      int timeout_seconds;
      int lambda_count;
      HashTable *ini_directives;
      HashTable *modified_ini_directives;
      zend_objects_store objects_store;
      zval *exception, *prev_exception;
      zend_op *opline_before_exception;
      zend_op exception_op[3];
      struct _zend_execute_data *current_execute_data;
      struct _zend_module_entry *current_module;
      zend_property_info std_property_info;
      zend_bool active;
      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 {
      /* Common elements */
      zend_uchar type;
      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;
      zend_bool pass_rest_by_reference;
      unsigned char return_reference;
      /* END of common elements */
      zend_bool done_pass_two;
      zend_uint *refcount;
      zend_op *opcodes;
      zend_uint last, size;
      zend_compiled_variable *vars;
      int last_var, size_var;
      zend_uint T;
      zend_brk_cont_element *brk_cont_array;
      int last_brk_cont;
      int current_brk_cont;
      zend_try_catch_element *try_catch_array;
      int last_try_catch;
      /* static variables support */
      HashTable *static_variables;
      zend_op *start_op;
      int backpatch_count;
      zend_uint this_var;
      char *filename;
      zend_uint line_start;
      zend_uint line_end;
      char *doc_comment;
      zend_uint doc_comment_len;
      zend_uint early_binding; /* the linked list of delayed declarations */
      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; /* Function Being Called */
      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_bool nested;
      zval **original_return_value;
      zend_class_entry *current_scope;
      zend_class_entry *current_called_scope;
      zval *current_this;  //开源软件:Cuoxin.com
      zval *current_object;
      struct _zend_op *call_opline;
  };
  定义中的opline就是正在执行的opcode,opcode的结构定义如下:
 
  struct _zend_op {
      opcode_handler_t handler;
      znode result;
      znode op1;
      znode op2;
      ulong extended_value;
      uint lineno;
      zend_uchar opcode;
  };
  其中lineno就是opcode所对应的行号.
 
  示例说明:看完上面的数据结构定义,你是否已经知道如何找php正在执行的文件名,方法名和行号呢?如果还有疑问的话,那就接着看下面的例子,创建一个文件test.php,代码如下:
 
  <?php
  function test1(){
          while(true){
                sleep(1);
          }
  }
  test1();
  ?>
  cli方式执行php脚本,加入执行的进程号为14973,我们使用gdb命令来调试进程,代码如下:
 
  $sudo gdb -p 14973
  (gdb) print (char *)executor_globals.active_op_array->filename
  $1 = 0x9853a34 "/home/xinhailong/test/php/test.php"
  (gdb) print (char *)executor_globals.active_op_array->function_name
  $2 = 0x9854db8 "test1"
  (gdb) print executor_globals->current_execute_data->opline->lineno
  $3 = 4
  很显然,他正在执行第四行的sleep方法,如果上面的方法你感觉麻烦,那你可以使用.gdbinit文件,这个文件在php源码的根目录下,使用方法如下:
 
  $sudo gdb -p 14973
  (gdb) source /home/xinhailong/.gdbinit
  (gdb) zbacktrace
  [0xa453f34] sleep(1) /home/xinhailong/test/php/test.php:4
  [0xa453ed0] test1() /home/xinhailong/test/php/test.php:7
  (gdb)
  题外话:从php5.6开始,php中集成了一个phpdbg的工具,可以像gdb调试c语言程序一样.

(编辑:ASP站长网)

    网友评论
    推荐文章
      热点阅读