[转] http://www.syyong.com/php/Using-strace-GDB-and-tcpdump-debugging-tools-in-PHP.htmlphp
在php中咱们最常使用调试方式是输出打印方式,好比经过echo、var_dump输出信息到终端或者经过fwrite、file_put_contents将信息写入到文件中。这种普通方式能帮咱们解决绝大部分调试问题。但仍然有些问题是须要借助其余工具来分析的,好比死循环,程序执行时间超预期,占用cpu太高,php内核或者扩展错误等场景,这时若是借助strace、gdb、tcpdump这样的工具就能很好的去帮助咱们定位问题。html
strace是Linux环境下的一款程序调试工具,用来监察一个应用程序所使用的系统调用及它所接收的系统信息。linux
在Linux中,进程是不能直接去访问硬件设备(好比读取磁盘文件,接收网络数据等等),但能够将用户态模式切换至内核态模式,经过系统调用来访问硬件设备。这时strace就能够跟踪到一个进程产生的系统调用,包括参数,返回值,执行消耗的时间,调用次数,成功和失败的次数。git
好比咱们使用strace来跟踪cat查看一个文件作了什么:github
[root@syyong home]$ strace cat index.php execve("/bin/cat", ["cat", "index.php"], [/* 25 vars */]) = 0 brk(0) = 0x21b0000 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f5fd02fd000 access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory) open("/etc/ld.so.cache", O_RDONLY) = 3 fstat(3, {st_mode=S_IFREG|0644, st_size=41783, ...}) = 0 mmap(NULL, 41783, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f5fd02f2000 close(3) = 0 open("/lib64/libc.so.6", O_RDONLY) = 3 ... fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 3), ...}) = 0 open("index.php", O_RDONLY) = 3 fstat(3, {st_mode=S_IFREG|0664, st_size=27, ...}) = 0 read(3, "<?php\necho 'hello world';\n\n", 32768) = 27 write(1, "<?php\necho 'hello world';\n\n", 27<?php echo 'hello world'; ) = 27 read(3, "", 32768) = 0 close(3) = 0 close(1) = 0 close(2) = 0 exit_group(0) = ?
[root@syyong home]$ strace -e read cat index.php read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0p\356\1\0\0\0\0\0"..., 832) = 832 read(3, "<?php\necho 'hello world';\n\n", 32768) = 27 <?php echo 'hello world'; read(3, "", 32768) = 0 +++ exited with 0 +++
[root@syyong home]$ strace -c cat index.php <?php echo "hello world"; % time seconds usecs/call calls errors syscall ------ ----------- ----------- --------- --------- ---------------- 0.00 0.000000 0 3 read 0.00 0.000000 0 1 write 0.00 0.000000 0 4 open 0.00 0.000000 0 6 close 0.00 0.000000 0 5 fstat 0.00 0.000000 0 9 mmap 0.00 0.000000 0 3 mprotect 0.00 0.000000 0 1 munmap 0.00 0.000000 0 3 brk 0.00 0.000000 0 1 1 access 0.00 0.000000 0 1 execve 0.00 0.000000 0 1 arch_prctl ------ ----------- ----------- --------- --------- ---------------- 100.00 0.000000 38 1 total
[root@syyong home]$ strace -T cat index.php 2>&1|grep read read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0p\356\1\0\0\0\0\0"..., 832) = 832 <0.000015> read(3, "<?php\necho 'hello world';\n\n", 32768) = 27 <0.000019> read(3, "", 32768) = 0 <0.000014>
默认返回的结果每一行表明一条系统调用,规则为“系统调用的函数名及其参数=函数返回值”。也能够外加一些条件好比:-e指定返回的调用函数,-c对结果进行统计,-T查看绝对耗时,-p经过pid附着(attach)到任何运行的进程等等。shell
strace的使用方法这里就不作具体介绍了,能够经过strace --help去详细了解使用方法。express
那么经过strace拿到了全部程序去调用系统过程所产生的痕迹后,咱们能用来定位哪些问题呢?swoole
另外由于strace拿到的是系统调用相关信息,通常也便是IO操做信息,这个对于排查好比cpu占用100%问题是无能为力的。这个时候就可使用GDB工具了。网络
phptrace
由于strace只能追踪到系统调用信息,而拿不到php代码层的调用信息。phptrace扩展就是为了解决这个问题,phptrace包含两个功能:1. 打印当前PHP调用栈,2. 实时追踪PHP调用。这样就能更方便咱们去查看到咱们须要的信息。phptrace wiki➫tcp
gdb是一个由GNU开源组织发布运行在UNIX/LINUX操做系统下功能强大的程序调试工具。使用gdb能够在程序运行时观察程序的内部结构和内存的使用状况,当程序dump时还能够经过gdb观察到程序dump前发生了什么。主要来讲gdb具备如下2个功能:
由于php语言是c写的,那么使用gdb也就能很方便的去调试php代码。举例,咱们经过gdb来调试一个简单的php程序index.php:
// 程序代码: <?php for ($i = 0; $i < 3; $i ++) { echo $i . PHP_EOL; if ($i == 2) { $j = $i + 1; var_dump($j); } sleep(1); }
gdb开始调试:
[root@syyong home]$ sudo gdb php
(gdb)run index.php ... 0 1 2 int(3) [Inferior 1 (process 577) exited normally]
注:若是mac下使用gdb时报:“...please check gdb is codesigned - see taskgated(8)...”时可参考https://leandre.cn/search/gdb/➫。gdb在调试程序时,若是ulimit打开则会把错误信息打印到当前目录下的core.*文件中。ulimit -c若是为0则表示没打开,能够执行ulimit -c unlimited或者ulimit -c 大于0的数字。
使用.gdbinit脚本:
除了在gdb shell里输入命令,也能够预先编写好脚本让gdb执行。当gdb启动的时候会在当前目录下查找“.gdbinit”文件并加载,做为gdb命令进行执行。这样就能够不用在命令行中作一些重复的事,好比设定多个断点等操做。另外在gdb运行时也能够经过执行“(gdb) source [-s] [-v] filename”来解释gdb命令脚本文件。一个.gdbinit文件例子:
file index.php set args hello b main b foo r
其余gdb经常使用命令能够参考:
gdb有3种使用方式:
php在解释执行过程当中,zend引擎用executor_globals变量保存了执行过程当中的各类数据,包括执行函数、文件、代码行等。zend虚拟机是使用C编写,gdb来打印PHP的调用栈时,实际是打印的虚拟机的执行信息。
使用zbacktrace更简单的调试:
php源代码中还提供了zbacktrace这样的方便的对gdb命令的封装的工具。zbacktrace是PHP源码包提供的一个gdb自定义指令,功能与bt指令相似,与bt不一样的是zbacktrace看到的调用栈是PHP函数调用栈,而不是c函数。zbacktrace能够直接看到当前执行函数、文件名和行数,简化了直接使用gdb命令的不少步骤。在php-src➫的根目录中有一个.gdbinit文件,下载后再gdb shell中输入:
(gdb) source .gdbinit (gdb) zbacktrace
基于gdb的功能特色,咱们可使用gdb来排查好比这些问题:
一些使用gdb排查问题例子:
即dump the traffic on a network,是一个功能强大的,命令行的包分析器。它能够将网络中传送的数据包彻底截获下来提供分析。它支持针对网络层、协议、主机、网络或端口的过滤,并提供and、or、not等逻辑语句来帮助去掉无用的信息。这样咱们就能详细看到网络通讯的过程,能帮助咱们解决不少网络问题。好比能够经过tcpdump知道何时发起的3次握手何时发送FIN包,何时发送RST包。
命令格式为: tcpdump [-aAbdDefhHIJKlLnNOpqRStuUvxX#] [ -B size ] [ -c count ] [ -C file_size ] [ -E algo:secret ] [ -F file ] [ -G seconds ] [ -i interface ] [ -j tstamptype ] [ -M secret ] [ -Q metadata-filter-expression ] [ -r file ] [ -s snaplen ] [ -T type ] [ --version ] [ -V file ] [ -w file ] [ -W filecount ] [ -y datalinktype ] [ -z command ] [ -Z user ] [ 表达式 ]