Today, I am not going to talk about how to debug a PHP Core file, nor do I introduce Coredump. I will introduce it in a relatively simple way, that is how to obtain some useful information from the PHP Core file for replaying this Core.
I have always wanted to write this series, but I feel a little unorganized when I think of the extensiveness of this topic.
Today, I am not going to talk about how to debug a PHP Core file, nor do I introduce Coredump. I will introduce it in a relatively simple way, that is how to obtain some useful information from the PHP Core file for replaying this Core.
This process involves PHP function calls, PHP parameter passing, and some global variables of PHP. this knowledge has been involved in my previous articles, you can refer to the following link: Functions of PHP principles, variable scopes of PHP principles, and so on.
First, let's generate a Core file for our example:
- function recurse($num) {
- recurse(++$num);
- }
-
- recurse(0);
Run the php file:
- $ php test.php
- Segmentation fault (core dumped
Due to wireless recursion, This PHP will cause stack explosion, resulting in segment fault and generate Coredump files in the current working directory of PHP (if your system does not generate Coredump files, query the ulimit settings ).
Okay. now, let's delete this test. php: Forget the above code. now we only have this Core file. The task is to find out the cause of this Core and its status.
First, let's open the core file with gdb:
- $ gdb php -c core.3165
We will see a lot of information. First, let's take note of this section:
- Core was generated by `php test.php'.
- Program terminated with signal 11, Segmentation fault
He told us the reason for the Core problem: "Segmentation fault ".
In general, this Core is the most common. resolving the null pointer, double free, and stack explosion will trigger SIGSEGV and then generate Coredump by default.
Now let's take a look at the stack at the time of Core occurrence:
- #0 execute (op_array=0xdc9a70) at /home/laruence/package/php-5.2.14/Zend/zend_vm_execute.h:53
- 53 memset(EX(CVs), 0, sizeof(zval**) * op_array->last_var);
- (gdb) bt
- #0 execute (op_array=0xdc9a70) at /home/laruence/package/php-5.2.14/Zend/zend_vm_execute.h:53
- #1 0x00000000006ea263 in zend_do_fcall_common_helper_SPEC (execute_data=0x7fbf400210) at /home/laruence/package/php-5.2.14/Zend/zend_vm_execute.h:234
- #2 0x00000000006e9f61 in execute (op_array=0xdc9a70) at /home/laruence/package/php-5.2.14/Zend/zend_vm_execute.h:92
- #3 0x00000000006ea263 in zend_do_fcall_common_helper_SPEC (execute_data=0x7fbf400440) at /home/laruence/package/php-5.2.14/Zend/zend_vm_execute.h:234
- #4 0x00000000006e9f61 in execute (op_array=0xdc9a70) at /home/laruence/package/php-5.2.14/Zend/zend_vm_execute.h:92
- #5 0x00000000006ea263 in zend_do_fcall_common_helper_SPEC (execute_data=0x7fbf400670) at /home/laruence/package/php-5.2.14/Zend/zend_vm_execute.h:234
- ....
By pressing the carriage return, we can see that the stack is very deep, and zend_do_fcall_common_helper_SPEC and execute are repeated. then we can conclude that the infinite recursion is generated (not necessarily infinite recursion, for example, I have introduced the maximum backtracking/recursive limitation of regular expressions (pcre) in my previous article ). core generated by Stack explosion.
OK. Now let's take a look at what function Core occurs in PHP. in PHP, for the FCALL _ * Opcode handler, execute_data represents a State called by the current function, this State contains information:
- (gdb)f 1
- #1 0x00000000006ea263 in zend_do_fcall_common_helper_SPEC (execute_data=0x7fbf400210) at /home/laruence/package/php-5.2.14/Zend/zend_vm_execute.h:234
- 234 zend_execute(EG(active_op_array) TSRMLS_CC);
- (gdb) p execute_data->function_state.function->common->function_name
- $3 = 0x2a95b65a78 "recurse"
- (gdb) p execute_data->function_state.function->op_array->filename
- $4 = 0x2a95b632a0 "/home/laruence/test.php"
- (gdb) p execute_data->function_state.function->op_array->line_start
- $5 =
Now we get that the PHP function called is recurse, which is defined in the second line of/home/laruence/test. php.
After several frame verification, we can see that this PHP function has been repeatedly called.
It should be noted that in order to introduce the principle of viewing execution information, I used the native gdb print for viewing. In fact, we can also use the PHP source code provided. gdbinit (gdb command to write scripts) to obtain the above information:
- (gdb) source /home/laruence/package/php-5.2.14/.gdbinit
- (gdb) zbacktrace
- [0xbf400210] recurse() /home/laruence/test.php:3
- [0xbf400440] recurse() /home/laruence/test.php:3
- [0xbf400670] recurse() /home/laruence/test.php:3
- [0xbf4008a0] recurse() /home/laruence/test.php:3
- [0xbf400ad0] recurse() /home/laruence/test.php:3
- [0xbf400d00] recurse() /home/laruence/test.php:3
- [0xbf400f30] recurse() /home/laruence/test.php:3
- [0xbf401160] recurse() /home/laruence/test.php:3
- ....
About. gdbinit is a small script file that defines some convenient debugging methods for PHP Core. you can also open it in a text editor to see some quick commands defined in it. generally, I often use the following methods:
- Zbacktrace
- Print_ht ** series
- Zmemchec
OK. Now we know that the problem occurs in the recursive call of the recurse function of/home/laruence/test. php.
Now, let's take a look at the parameters when calling this function?
The passing of PHP parameters relies on a global Stack, that is, EG (argument_stack). in non-multithreading, EG is executor_globals, which maintains many execution statuses. argument_statck is the parameter transfer stack, which stores the call parameters equivalent to the number of PHP function calls.
It should be noted that this PHP function call stack (number of layers) does not correspond to the backtrace shown by gdb in a simple one-to-one manner. Therefore, the parameters cannot directly correspond to the backtrace of gdb and need to be analyzed separately:
- // First, check the number of parameters called by the callback function.
- (Gdb) p (int) * (executor_globals-> argument_stack-> top_element-2)
- $13 = 1
-
- // Let's take a look at the final callback function call parameters.
- (Gdb) p ** (zval **) (executor_globals-> argument_stack-> top_element-3)
- $2 = {value = {lval = 22445, dval = 1.1089303420906779e-319, str = {val = 0x57ad, len = 7}, ht = 0x57ad, obj = {handle = 22445, handlers = 0x7 }},
- Refcount = 2, type = 1' \ 001', is_ref = 0' \ 0'
Okay, now we get that the last called parameter is an integer and the value is 22445.
At this step, we get the PHP-level information about the Core occurrence time. Next, we can hand it over to the corresponding PHP development engineer for troubleshooting. under this parameter, the possible causes of infinite recursion can be solved ..
Note: Debugging PHP Core is a process that requires rich experience. maybe the example I introduced today is too simple, but as long as you often challenge it and encounter relevant knowledge that you do not understand, I believe that everyone can become the PHP Core killer ..