When the CPU is close to 100%, how do you find the cause of the CPU surge? My idea is, first Find the line of code that the process is executing to identify the potentially problematic section of code. Then, carefully analyze the problematic code segment to find out the cause.
If your program is written in c or c++, then you can easily find the line of code being executed. However, the program is written in PHP, how to find the lines of code that may have problems? This problem is what this article will solve.
Background knowledge:
Everyone knows that php is an interpreted language. The PHP code written by the user will generate opcode, which will be interpreted and executed by the interpreter engine. During the interpretation execution process, there is a global variable that contains various data used during the execution process. It is executor_globals. His type definition can be found in the Zend/zend_globals.h file of the source code.
The code is as follows | |
struct _zend_executor_globals { zval uninitialized_zval; zval error_zval; zend_ptr_stack arg_types_stack; /* symbol table cache */ zend_op **opline_ptr; HashTable *active_symbol_table; HashTable included_files; /* files already included */ JMP_BUF *bailout; int error_reporting; zend_op_array *active_op_array; HashTable *function_table; /* function symbol table */ zend_class_entry *scope; zval *This; long precision; int ticks_count; zend_bool in_execution; /* for extended information support */ #ifdef ZEND_WIN32 HashTable regular_list; zend_vm_stack argument_stack; int user_error_handler_error_reporting; zend_error_handling_t error_handling; /* timeout support */ int lambda_count; HashTable *ini_directives; zend_objects_store objects_store; 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]; |
Here we only talk about two variables that are more important to us, active_op_array and current_execute_data.
The active_op_array variable stores the op_array that the engine is executing (if you want to know what op_array is, please click to view). There is a definition of the data type of op_array in Zend/zend_compile.h.
The code is as follows | |
struct _zend_op_array { zend_bool done_pass_two; zend_uint *refcount; zend_op *opcodes; zend_compiled_variable *vars; zend_uint T; zend_brk_cont_element *brk_cont_array; zend_try_catch_element *try_catch_array; /* static variables support */ zend_op *start_op; zend_uint this_var; char *filename; void *reserved[ZEND_MAX_RESERVED_RESOURCES]; |
After reading the definition, I don’t need to say more. In the definition, filename and function_name save the file name and method name being executed respectively.
Current_execute_data saves the execute_data of the op_array being executed. execute_data saves some data during the execution of each op_array. It is defined in, 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; zval *current_object; struct _zend_op *call_opline; }; |
The opline in the definition is the opcode being executed. The structure of opcode is defined as follows:
代码如下 | |
struct _zend_op { opcode_handler_t handler; znode result; znode op1; znode op2; ulong extended_value; uint lineno; zend_uchar opcode; }; |
Where lineno is the line number corresponding to opcode.
Example description:
After reading the above data structure definition, do you already know how to find the file name, method name and line number of PHP being executed? If you still have questions, then look at the following example. Create a file test.php with the following code:
代码如下 | |
function test1(){ while(true){ sleep(1); } } test1(); ?> |
Execute the php script in cli mode, and the process number added to the execution is 14973. We use the gdb command to debug the process.
代码如下 | |
$sudo gdb -p 14973 (gdb) print (char *)executor_globals.active_op_array->filename = 0x9853a34 "/home/xinhailong/test/php/test.php" (gdb) print (char *)executor_globals.active_op_array->function_name = 0x9854db8 "test1" (gdb) print executor_globals->current_execute_data->opline->lineno = 4 |
Obviously, he is executing the sleep method on the fourth line.
If you find the above method troublesome, you can use the .gdbinit file. This file is in the root directory of the php source code. How to use:
代码如下 | |
$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) |
Digression:
Starting from php5.6, a phpdbg tool is integrated into php. You can debug PHP programs just like gdb debugs C language programs. If you are interested, you can open the link below to take a look