這篇文章為大家帶來了關於PHP的相關知識,其中主要介紹了關於使用gdb調試源碼的相關內容,下面一起來看一下,希望對大家有幫助。
推薦學習:《PHP影片教學》
php編譯時有一個debug模式,這個模式會關閉記憶體最佳化,提示記憶體洩露,屏蔽調用棧優化可以讓我們看到完整的php c層面的呼叫棧。
通常我會編譯兩個php版(一個正常,一個開啟debug)在不同的目錄,透過export決定要用哪一個。
透過php-config指令可以看到configure-options,修改其中的prefix 和with-config-file-path 到新的目錄,然後加入--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 ] --extension-dir [/data/env/runtime/php-7.1.33-debug/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] --php-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-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]
修改之後大概是這樣子,然後編譯安裝就可以得到debug版本了
--prefix=/data/env/runtime/php-7.1.33-debug --enable-debug --enable-phpdbg-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
php --version中看到DEBUG 就可以了
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 Technologie
注意:Debug版本的擴充需要再編譯安裝,不能拷貝正常版本的so,安裝方式和普通擴充功能一致,一般不需要開額外的debug參數。如果你需要調試擴充功能例如swoole你需要設定擴充的debug參數,可以參考擴充的./configure檔說明。
這裡簡單介紹gdb的基本使用,更詳細的使用方法可以自行google。
啟動gdb
擷取進程
gdb -p {pid}
run 方法啟動
gdb php run test3.php
透過core 檔案
gdb -c core.8451
斷點
break n :在第n行設定斷點(可以帶程式碼路徑和程式碼名稱)
//注意:这里只能断点c代码,php文件不行的,var.c:201在php-7.1.33是var_dump的入口 break var.c:201
b fn1 if a>b:條件斷點設定
break func(break縮寫為b):在函數func()的入口處設定斷點
//大部分php的方法在c层面的方法名都是zif_ + php方法名。 例如 var_dump 在c的方法名叫zif_var_dump break zif_var_dump
delete 斷點號n:刪除第n個斷點
disable 斷點號碼n:暫停第n個斷點
enable 斷點號n:開啟第n個斷點
clear 行號n:清除第n行的斷點
info b (info breakpoints) :顯示目前程式的斷點設定狀況
delete breakpoints:清除所有斷點
其他
list (簡寫l) ,其作用就是列出程式的原始程式碼,預設每次顯示10行。
list 行號:將顯示目前檔案以「行號」為中心的前後10行程式碼,
print a:將顯示a 的值
continue (簡寫c ):繼續執行,到下一個斷點(或運行結束)。設定斷點後需要按這個。
next (簡寫n):目前函數,下一行
step (簡寫s):跳入函數內部
where/bt :目前運行的堆疊清單;
這裡就是本文的重點了php為gdb提供了一組小工具,在原始碼目錄下的.gdbinit檔案中,它可以幫助我們更好的gdb php原始碼。
準備
為了更好的演示,我在這裡準備一個php檔案。
<?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);
啟動gdb,設定2個斷點。
gdb php //注意这里要用debug版本的 (gdb) break var.c:211 Breakpoint 1 at 0x76e717: file /data/env/runtime/php-7.1.33-src/ext/standard/var.c, line 211. (gdb) break zend_object_handlers.c:492 Breakpoint 2 at 0x86ce9d: file /data/env/runtime/php-7.1.33-src/Zend/zend_object_handlers.c, line 492. (gdb) r test4.php
然後載入小工具
source /data/env/runtime/php-7.1.33-src/.gdbinit
使用
zbacktrace 顯示目前的php呼叫堆疊
(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
dump_bt 查看目前呼叫堆疊和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
printzv 輸出zend value 的情況
(gdb) printzv &args[0] [0x7ffff1614250] (refcount=0) string: test
print_global_vars 輸出全域變數
(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:
print_const_table 輸出定義的常數#rrrere
print_zstr 輸出zend string(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 ......print_cvs 列印已編譯的變數及其值它需要傳入一個zend_execute_data 類型的值。可以先bt看一下呼叫棧。
(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"print_ht 輸出HashTable, HashTable是php底層一個重要的資料結構是php array的實作方式,你可以理解為是C層面的php array,在php來源碼中也大量使用HashTable儲存各類k v結構或數組結構。
(gdb) bt //这里看到 #2 层这里是 zend_vm_execute 的执行入口,这里有zend_execute_data 类型的值。 #0 zif_var_dump (execute_data=0x7ffff1614120, return_value=0x7fffffffa9b0) at /data/env/runtime/php-7.1.33-src/ext/standard/var.c:209 #1 0x0000000000ab08d4 in 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_execute.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_execute.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.33-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 //跳到#2 这一层 #2 0x0000000000ab01c3 in execute_ex (ex=0x7ffff1614030) at /data/env/runtime/php-7.1.33-src/Zend/zend_vm_execute.h:429 429 ((opcode_handler_t)OPLINE->handler)(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); (gdb) print_cvs ex //输出 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 }print_htptr 和print_ht 類似,它輸出的是zval的位址不是zval的值
(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 }print_htstr 和print_ht 類似,只是HashTable中存的不是zval 而是c char,但是這種情況在原始碼中好像很少見了,大部分存字串的情況會直接用zend string,我找了一圈在 php_cli_server_mime_type_ctor 有一處使用
(gdb) print_htptr args[1].value Hash(2)[0x7ffff170e300]: { [0] a1 => 0x7ffff1793e20 [1] a2 => 0x7ffff1793e40 }print_ft 和print_ht 類似,只是HashTable中存的是 zend_function 的位址
(gdb) print_htstr &server->extension_mime_types Hash(2)[0x11b9228]: { [0] ez => application/andrew-inset [1] aw => application/applixware }print_inh 輸出class相關訊息
(gdb) print_ft &args[2].value.obj.ce.function_table Hash(1)[0x7ffff1783210]: { [0] funb => "funB" }print_pi 輸出物件中屬性相關訊息,它需要傳入一個zend_property_info類型的位址,在zend_object_handlers.c:492中有使用,php中可以用get_object_vars($e) 觸發。
(gdb) print_inh &args[4].value.obj.ce class C extends B { class B { } }推薦學習:《
PHP影片教學》
以上是深入了解PHP:用gdb調試源碼的詳細內容。更多資訊請關注PHP中文網其他相關文章!