PHP is compiled in debug mode, which turns off memory optimization, indicating a memory leak. Shielding the call stack optimization allows us to see the full CALL stack at the PHP C level.

Usually I compile two versions of PHP (one normal and one debug enabled) in different directories, using export to decide which one to use.

Using the php-config command, you can see configure-options, modify prefix and with-config-file-path to the new directory, and then add –enable-debug

Yongkbmaster ➜ ~ php-config Usage: /data/env/runtime/php-7.1.33-debug/bin/php-config [OPTION] Options: -- prefix [/ data/env/runtime/PHP - 7.1.33 - debug] - includes [-i/data/env/runtime/PHP - 7.1.33 - debug/include/PHP -i/data/env/runtime/PHP - 7.1.33 - debug/include/PHP/main - I/data/env/runtime/PHP - 7.1.33 - debug/include/PHP/TSRM -i/data/env/runtime/PHP - 7.1.33 - debug/include/PHP/Zend - I/data/env/runtime/PHP - 7.1.33 - debug/include/PHP/ext -i/data/env/runtime/PHP - 7.1.33 - debug/include/PHP/ext/date/lib] - ldflags [] - libs [lcrypt - lz - lexslt - lresolv - lcrypt  -lrt -lldap -llber -lpng -lz -ljpeg -lcurl -lbz2 -lz -lrt -lm -ldl -lnsl -lxml2 -lz -lm -ldl -lgssapi_krb5 -lkrb5 -lk5crypto -lcom_err -lssl -lcrypto -lcurl -lxml2 -lz -lm -ldl -lfreetype -lxml2 -lz -lm -ldl -lxml2 -lz -lm -ldl -lcrypt -lxml2 -lz -lm -ldl -lxml2 -lz -lm -ldl -lxml2 -lz -lm -ldl -lxml2 -lz -lm -ldl -lxslt -lxml2 -lz -ldl -lm -lssl - lcrypto - lcrypt] - the extension - dir/data/env/runtime - 7.1.33 - debug/PHP/lib/PHP/extensions/debug - non - ZTS - 20160303] - include - dir/data/env/runtime/PHP - 7.1.33 - debug/include/PHP] - man - dir/data/env/runtime/PHP - 7.1.33 - debug/PHP/man] -- binary [/data/env/runtime/php-7.1.33-debug/bin/ PHP] --php-sapis [cli FPM PHPDBG cgi] --configure-options [-- prefix = / data/env/runtime/PHP - 7.1.33 - debug - enable - debug - enable PHPDBG -- the debug --with-config-file-path=/data/env/runtime/php-7.1.33-debug/etc --with-curl --with-freetype-dir --with-gd --with-gettext --with-iconv-dir --with-kerberos --with-libdir=lib64 --with-libxml-dir --with-mysqli --with-openssl --with-pcre-regex --with-pdo-mysql --with-pdo-sqlite --with-pear --with-png-dir --with-jpeg-dir --with-xmlrpc --with-xsl --with-zlib --with-bz2 --with-mhash --enable-fpm --enable-bcmath --enable-libxml --enable-inline-optimization --enable-gd-native-ttf  --enable-mbregex --enable-mbstring --enable-opcache --enable-pcntl --enable-shmop --enable-soap --enable-sockets --enable-sysvsem --enable-sysvshm --enable-xml --enable-zip --with-ldap] --version [7.1.33] -- Vernum [70133]Copy the code

This is what it looks like, and then you compile and install it to get the debug version

-- prefix = / data/env/runtime/PHP - 7.1.33 - debug - enable - debug - enable PHPDBG -- the debug --with-config-file-path=/data/env/runtime/php-7.1.33-debug/etc --with-curl --with-freetype-dir --with-gd --with-gettext --with-iconv-dir --with-kerberos --with-libdir=lib64 --with-libxml-dir --with-mysqli --with-openssl --with-pcre-regex --with-pdo-mysql --with-pdo-sqlite --with-pear --with-png-dir --with-jpeg-dir --with-xmlrpc --with-xsl --with-zlib --with-bz2 --with-mhash --enable-fpm --enable-bcmath --enable-libxml --enable-inline-optimization --enable-gd-native-ttf  --enable-mbregex --enable-mbstring --enable-opcache --enable-pcntl --enable-shmop --enable-soap --enable-sockets --enable-sysvsem --enable-sysvshm --enable-xml --enable-zip --with-ldapCopy the code

See DEBUG in PHP –version

Yongkbmaster ➜ ~ /data/env/runtime/php-7.1.33-debug/bin/ PHP --version PHP 7.1.33 (cli) (built: Dec 29 2020 19:16:50) (NTS DEBUG) Copyright (C) 1997-2018 The PHP Group Zend Engine V3.1.0, Copyright (c) 1998-2018 Zend TechnologieCopy the code

Note: The Debug extension needs to be compiled and installed again. The normal version of SO cannot be copied. The installation method is the same as the ordinary extension. If you need to debug an extension like Swoole and you need to set the debug parameter for the extension, refer to the extension’s./configure file description.

GDB usage here is a brief introduction to the basic use of GDB, more detailed use method can be Google.

PHP run test3. PHP run test3.php through the core file GDB -c core.8451

The breakpoint

  • Break n: Set a breakpoint at line NTH (with code path and code name)

    Var. C :201 break var. C :201 break var. C :201 break var. C :201 break var. C :201 break var. C :201 break var. C :201 break var

  • B fn1 if A > B: Conditional breakpoint setting

  • Break func(break shortened to b) : Sets a breakpoint at the entrance to function func()

    // Most PHP methods are named zif_ + PHP methods at the C level. For example, the var_dump method in C is called zif_var_dump break zif_var_dump

  • Delete Breakpoint number n: deletes the NTH breakpoint

  • Disable Breakpoint number n: breaks the NTH breakpoint

  • Enable Breakpoint number n: enables the NTH breakpoint

  • Clear Line n: Clears the breakpoint on line n

  • Info breakpoints (INFO B) : displays the breakpoint Settings of the current program

  • Delete BreakPoints: Clears all breakpoints

  • List (l for short), which lists the program’s source code, 10 lines at a time by default.

  • List line number: Displays the first and last 10 lines of code centered around line number in the current file.

  • Print a: will display the value of a

  • Continue (c) : Execution continues until the next breakpoint (or the end of the run). You need to press this after setting breakpoints.

  • Next (short N) : Current function, next line

  • Step – s: Jump into a function

  • Where /bt: currently running stack list;

PHP GDB widgets this is the focus of this article PHP provides a set of widgets for GDB, in the source directory. Gdbinit file, which can help us better GDB PHP source code.

Preparation For a better demonstration, I’m going to prepare a PHP file.

<? php const A = 'test const'; const B = 'test const B'; class B { public $a = 'test'; public function funB() { var_dump('test funB'); } } class C extends B { public function funC() { var_dump('test funC'); } } $a = 'test'; $b = ['a1' => 1, 'a2' => 2]; $c = new B(); $d = [A, B]; $e = new C(); $f = $b; var_dump($a, $b, $c, $d, $e, $f); get_object_vars($e);Copy the code

Start GDB and set 2 breakpoints.

C :211 Breakpoint 1 at 0x76e717: The file/data/env/runtime - 7.1.33 - SRC/PHP/ext/standard/var. C, line 211. (gdb) break zend_object_handlers.c:492 Breakpoint 2 at 0x86ce9d: Handlers: file /data/env/runtime/php-7.1.33-src/Zend/zend_object_handlers. C, line 492. (GDB) r test4.phpCopy the code

Then load the gadget

The source/data/env/runtime/PHP - 7.1.33 - SRC /. GdbinitCopy the code

Displays the current PHP call stack using ZbackTrace

(gdb) zbacktrace
[0x7ffff1614200] var_dump("test", array(2)[0x7ffff1614260], object[0x7ffff1614270], array(2)[0x7ffff1614280], object[0x7ffff1614290], array(2)[0x7ffff16142a0]) [internal function]
[0x7ffff1614030] (main) /root/test4.php:26
Copy the code

Dump_bt checks that the current call stack is similar to zBackTrace

(gdb) dump_bt executor_globals.current_execute_data
[0x7ffff1614200] var_dump("test", array(2)[0x7ffff1614260], object[0x7ffff1614270], array(2)[0x7ffff1614280], object[0x7ffff1614290], array(2)[0x7ffff16142a0]) [internal function]
[0x7ffff1614030] (main) /root/test4.php:26
Copy the code

Printzv prints zend value

(gdb) printzv &args[0]
[0x7ffff1614250] (refcount=0) string: test
Copy the code

Print_global_vars prints global variables

(gdb) print_global_vars
Hash(13)[0x11bf0d0]: {
  [0] _GET => [0x7ffff1657100] (refcount=2) array:
  [1] _POST => [0x7ffff1657120] (refcount=2) array:
  [2] _COOKIE => [0x7ffff1657140] (refcount=2) array:
  [3] _FILES => [0x7ffff1657160] (refcount=2) array:
  [4] argv => [0x7ffff1657180] (refcount=2) array:
  [5] argc => [0x7ffff16571a0] long: 1
  [6] _SERVER => [0x7ffff16571c0] (refcount=2) array:
  [7] a => [0x7ffff16571e0] indirect: [0x7ffff1613080] (refcount=0) string: test
  [8] b => [0x7ffff1657200] indirect: [0x7ffff1613090] (refcount=5) array:
  [9] c => [0x7ffff1657220] indirect: [0x7ffff16130a0] (refcount=2) object(B) #2
  [10] d => [0x7ffff1657240] indirect: [0x7ffff16130b0] (refcount=2) array:
  [11] e => [0x7ffff1657260] indirect: [0x7ffff16130c0] (refcount=2) object(C) #3
  [12] f => [0x7ffff1657280] indirect: [0x7ffff16130d0] (refcount=5) array:
Copy the code

Print_const_table prints the defined constant

(gdb) print_const_table executor_globals.zend_constants
[0x14e8380] {
  Hash(2340)[0x14e8380]: {
    [0] E_ERROR => [0x14fd660] long: 1
    [1] E_RECOVERABLE_ERROR => [0x14fe8a0] long: 4096
    [2] E_WARNING => [0x14fe900] long: 2
    [3] E_PARSE => [0x14fe960] long: 4
    [4] E_NOTICE => [0x14fe9c0] long: 8
    [5] E_STRICT => [0x14fea20] long: 2048
    [6] E_DEPRECATED => [0x14fea80] long: 8192
    [7] E_CORE_ERROR => [0x14feae0] long: 16
    [8] E_CORE_WARNING => [0x14feb40] long: 32
    [9] E_COMPILE_ERROR => [0x14feba0] long: 64
    [10] E_COMPILE_WARNING => [0x14fec10] long: 128
    [11] E_USER_ERROR => [0x14fec70] long: 256
    [12] E_USER_WARNING => [0x14fecd0] long: 512
    [13] E_USER_NOTICE => [0x14fed30] long: 1024
    [14] E_USER_DEPRECATED => [0x14feda0] long: 16384
    [15] E_ALL => [0x14fee00] long: 32767
    [16] DEBUG_BACKTRACE_PROVIDE_OBJECT => [0x14fee70] long: 1
    [17] DEBUG_BACKTRACE_IGNORE_ARGS => [0x14feee0] long: 2
    [18] true => [0x14fef70] bool: true
    [19] false => [0x14ff000] bool: false
    [20] ZEND_THREAD_SAFE => [0x14ff070] bool: false
    [21] ZEND_DEBUG_BUILD => [0x14ff0e0] bool: true
    [22] null => [0x14ff170] NULL
    [23] PHP_VERSION => [0x1500380] (refcount=1) string: 7.1.33
 ......
Copy the code

Print_zstr prints the zend string

(gdb) print_zstr args[0]
string(4) "test"
(gdb) print_zstr args[0] 2
string(4) "te..."
(gdb) print_zstr args[0] 4
string(4) "test"
Copy the code

Print_cvs prints the compiled variable and its value. It needs to pass in a value of type zend_execute_data. You can start by bt the call stack.

(GDB) bt // See layer 2 here. This is the entry to zend_vm_execute. Here is the value of type zend_execute_data. #0 zif_var_dump (execute_data=0x7ffff1614120, Return_value = 0 x7fffffffa9b0) at/data/env/runtime - 7.1.33 - SRC/PHP/ext/standard/var. C: # 1 0 x0000000000ab08d4 in 209 ZEND_DO_ICALL_SPEC_RETVAL_UNUSED_HANDLER () at /data/env/runtime/php-7.1.33-src/Zend/zend_vm_execute. H :628 #2 0x0000000000AB01C3 in execute_ex (ex= 0x7ffFF1614030) at /data/env/runtime/ phP-7.1.33 - SRC /Zend/ zend_vm_execut. h:429 #3 0x0000000000ab02d5 in zend_execute (op_array=0x7ffff1672d00, Return_value =0x0) at /data/env/runtime/ phP-7.1.33-src /Zend/ zend_vm_execut. h:474 #4 0x0000000000a510f9 in zend_execute_scripts (type=8, retval=0x0, File_count =3) at /data/env/runtime/php-7.1.33-src/Zend/zend.c:1482 #5 0x00000000009c02F4 in php_execute_script (primary_file= 0x7fffffffDF30) at /data/env/runtime/ phP-7.1.33-src /main/main.c:2577 #6 0x0000000000b31387 in do_cli (argc=2, Argv =0x14e7f30) at /data/env/runtime/ php-7.1.333-src /sapi/cli/php_cli.c:993 #7 0x0000000000b32346 in main (argc=2, argv=0x14e7f30) at /data/env/runtime/ php-7.1.333-src /sapi/cli/php_cli.c:993 #7 0x0000000000b32346 in main (argc=2, Argv =0x14e7f30) at /data/env/runtime/php-7.1.33- SRC /sapi/cli/php_cli.c:1381 (GDB) f 2 In execute_ex (ex=0x7ffff1614030) at /data/env/runtime/ phP-7.1.33-src /Zend/ zend_vm_execut. h:429 429 ((opcode_handler_t)OPLINE->handler)(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); Compiled variables count: 6 [0] 'a' [0x7FFff1614080] (refCount =0) String: test [1] 'b' [0x7ffff1614090] (refcount=5) array: Hash(2)[0x7ffff170e300]: { [0] a1 => [0x7ffff1793e20] long: 1 [1] a2 => [0x7ffff1793e40] long: 2 } [2] 'c' [0x7ffff16140a0] (refcount=2) object(B) #2 Properties Hash(1)[0x7ffff170e480]: { [0] a => [0x7ffff1793f60] indirect: [0x7ffff170e388] (refcount=4) string: test } [3] 'd' [0x7ffff16140b0] (refcount=2) array: Packed(2)[0x7ffff170e3c0]: { [0] 0 => [0x7ffff1793688] (refcount=1) string: test const [1] 1 => [0x7ffff17936a8] (refcount=1) string: test const B } [4] 'e' [0x7ffff16140c0] (refcount=2) object(C) #3 Properties Hash(1)[0x7ffff170e4e0]: { [0] a => [0x7ffff17940a0] indirect: [0x7ffff170e448] (refcount=4) string: test } [5] 'f' [0x7ffff16140d0] (refcount=5) array: Hash(2)[0x7ffff170e300]: { [0] a1 => [0x7ffff1793e20] long: 1 [1] a2 => [0x7ffff1793e40] long: 2 }Copy the code

Print_ht Prints HashTable, which is an important data structure in PHP. HashTable is an implementation of PHP array. You can think of it as C level PHP array.

(gdb) print_ht args[1].value
Hash(2)[0x7ffff170e300]: {
  [0] a1 => [0x7ffff1793e20] long: 1
  [1] a2 => [0x7ffff1793e40] long: 2
}
(gdb) print_ht args[3].value
Packed(2)[0x7ffff170e3c0]: {
  [0] 0 => [0x7ffff1793688] (refcount=1) string: test const
  [1] 1 => [0x7ffff17936a8] (refcount=1) string: test const B
}
Copy the code

Print_htptr is similar to print_ht in that it prints the address of zval, not the value of zval

(gdb) print_htptr args[1].value
Hash(2)[0x7ffff170e300]: {
  [0] a1 => 0x7ffff1793e20
  [1] a2 => 0x7ffff1793e40
}
Copy the code

Print_htstr is similar to print_ht, except that instead of storing zval in HashTable, c char is used. I looked around for a place to use php_CLI_server_MIme_type_ctor

(gdb) print_htstr &server->extension_mime_types
Hash(2)[0x11b9228]: {
  [0] ez => application/andrew-inset
  [1] aw => application/applixware
}
Copy the code

Print_ft is similar to print_ht, except that HashTable stores the address of zend_function

(gdb) print_ft &args[2].value.obj.ce.function_table
Hash(1)[0x7ffff1783210]: {
  [0] funb => "funB"
}
Copy the code

Print_inh Displays class information

(gdb) print_inh &args[4].value.obj.ce
class C extends B {
 class B {
 }
}
Copy the code

Print_pi prints information about properties in the object, which requires an address of type zend_property_info, used in zend_object_handlers.c:492 and triggered in PHP with get_object_vars($e).

(gdb) c Continuing. Breakpoint 2, zend_check_property_access (zobj=0x7ffff170e420, Prop_info_name = 0 x7ffff173c2c0) at/data/env/runtime/PHP - 7.1.33 - SRC/Zend/zend_object_handlers. C: 492, 492 zend_string_release(member); (gdb) print_pi property_info [0x7ffff17833c8] { offset = 0x28 ce = [0x7ffff17831d0] B flags = 0x100 (ZEND_ACC_PUBLIC) name = string(1) "a" default value: [0x7ffff17690c0] (refcount=4) string: test }Copy the code