用 Xdebug Trace 追踪 PHP 函数调用

最近接手了一个老项目,代码层级嵌套得像套娃一样,改个 bug 光找入口都得半天。本来想用 var_dump 一路打点过去,结果发现这方法太原始了,打印出来一堆乱七八糟的东西,而且每次还要手动删代码。

后来想起 Xdebug 有个 Trace 功能,可以直接记录整个请求过程中所有的函数调用,这不就是我要找的东西吗?

开启 Trace

首先要确保 Xdebug 已经装好了,这个网上教程一堆就不多说了。关键是配置,要在 php.ini 里加上这几行:

1
2
3
4
xdebug.mode = trace
xdebug.use_compression = false
xdebug.trace_output_dir = /tmp
xdebug.trace_output_name = trace.%c

这里有个坑,默认情况下 Xdebug 会把追踪文件压缩成 .xt.gz 格式。虽说压缩是好事,但我只想直接看文本文件,不想每次都解压。把 use_compression 设成 false 就行了,输出的就是纯 .xt 文件。

手动控制追踪范围

最简单的方式是在代码里手动调用:

1
2
3
4
5
6
7
8
9
10
<?php
// 开始追踪
xdebug_start_trace('/tmp/my_trace');

// 你的业务代码
$user = getUser(1);
$orders = getOrders($user);

// 停止追踪
xdebug_stop_trace();

这样只会记录这两个函数之间的调用过程,精准控制范围,不会产生一大堆无用的日志。

看看追踪结果

跑完之后打开 /tmp/my_trace.xt,大概长这样:

1
2
3
4
5
6
7
TRACE START [2026-05-19 10:30:00]
0.0001 397120 -> {main}() /var/www/app.php:0
0.0002 397120 -> getUser(1) /var/www/app.php:5
0.0003 397184 -> Database->query('SELECT * FROM users WHERE id = 1') /var/www/app.php:12
0.0005 397184 <- query() => ['id' => 1, 'name' => 'test'] /var/www/app.php:12
0.0006 397120 <- getUser() => ['id' => 1, 'name' => 'test'] /var/www/app.php:5
TRACE END [2026-05-19 10:30:00]

左边是时间戳,中间是内存占用,箭头表示调用方向。-> 是调用,<- 是返回。一眼就能看出来代码执行的顺序,哪个函数耗时长、占用内存大,清清楚楚。

几个常用的参数

xdebug_start_trace() 可以传第二个参数控制输出格式:

  • XDEBUG_TRACE_APPEND - 追加到现有文件,不会覆盖
  • XDEBUG_TRACE_COMPUTERIZED - 机器可读格式,方便写脚本分析
  • XDEBUG_TRACE_HTML - 输出成 HTML 格式,浏览器直接看

我一般就用默认的,文本格式已经很清晰了。

一些小建议

追踪文件会很大,特别是那种框架项目,一个请求下来几百个函数调用很正常。所以尽量缩小追踪范围,只追踪关键代码段。

另外,追踪对性能影响挺大的,生产环境千万别开,调试完记得关掉。本地开发环境用用就好。

最后

Xdebug Trace 这个功能确实好用,特别是分析不熟悉的代码时,比到处加 var_dump 优雅多了。输出的文件虽然长了点,但信息量足够,能帮你快速理清代码的调用链路。

希望这个小技巧能帮到同样在”看代码看到头秃”的小伙伴们。