• 首页
  • vue
  • TypeScript
  • JavaScript
  • scss
  • css3
  • html5
  • php
  • MySQL
  • redis
  • jQuery
  • DTrace 动态跟踪

    包括 Solaris,macOS,Oracle Linux 和 BSD 在内的很多操作系统都提供了 DTrace 跟踪调试框架,它永远可用,并且额外消耗极低。 DTrace 可以跟踪操作系统行为和用户程序的执行情况。它可以显示参数值,也可以用来分析性能问题。用户可以使用 DTrace D 脚本语言创建脚本文件来监控探针,进而高效分析数据指针。

    除非用户使用 DTrace D 脚本激活并监控 PHP 探针,否则它并不会运行,所以不会给正常的应用执行带来任何性能损耗。即使 PHP 探针被激活,它的消耗也是非常低的,甚至可以直接在生产系统中使用。

    在运行时可以激活 PHP “用户级静态定义跟踪”(USDT)探针。举例说明,如果 D 脚本正在监控 PHPfunction-entry探针,那么,每当 PHP 脚本函数被调用的时候,这个探针将被触发,同时 D 脚本中所关联的动作代码将被执行。在脚本的动作代码中,可以打印出诸如函数所在源文件等信息的探针参数,也可以记录诸如函数执行次数这样的聚合数据。

    这里仅描述了 PHP USDT 探针。请参考各操作系统文档中关于 DTrace 的部分获取更多信息,例如,如何使用 DTrace 跟踪函数调用,如何跟踪操作系统行为等。需要注意的是,不同平台提供的 DTrace 功能并不完全相同。

    从 PHP 5.4 开始加入 DTrace 静态探针,之前的版本需要使用» PECL 扩展来实现跟踪功能。这个扩展现在已经废弃。

    在某些 Linux 发行版中,可以使用 SystemTap 工具来监控 PHP DTrace 静态探针。

    使用 PHP 和 DTrace

    在支持 DTrace 动态跟踪的平台上,可以配置 PHP 打开 DTrace 静态探针。

    PHP DTrace 静态探针配置

    请参考操作系统文档获取启用操作系统 DTrace 支持的信息。例如,在 Oracle Linux 系统上启动 UEK3 内核,并进行如下操作:

    # modprobe fasttrap
    # chmod 666 /dev/dtrace/helper

    除了chmod命令,您也可以使用 ACL 包规则来限制特定用户对于设备的访问权限。

    使用--enable-dtrace配置参数构建 PHP:

    # ./configure --enable-dtrace ...
    # make
    # make install

    这样就启用了 PHP 核心的静态探针。对于提供了自有探针的 PHP 扩展需要分别构建。

    PHP 核心的 DTrace 静态探针

    PHP 中包括以下可用的静态探针
    探针名称探针描述探针参数
    request-startup请求开始时触发。char *file, char *request_uri, char *request_method
    request-shutdown请求关闭时触发。char *file, char *request_uri, char *request_method
    compile-file-entry脚本开始编译时触发。char *compile_file, char *compile_file_translated
    compile-file-return脚本完成编译时触发。char *compile_file, char *compile_file_translated
    execute-entry操作数数组开始执行时触发。例如:函数调用,文件包含以及生成器恢复时会被触发。char *request_file, intlineno
    execute-return操作数数组执行完毕之后触发。char *request_file, intlineno
    function-entryPHP 引擎进入 PHP 函数或者方法调用时触发。char *function_name, char *request_file, intlineno, char *classname, char *scope
    function-returnPHP 引擎从 PHP 函数或者方法调用返回后触发。.char *function_name, char *request_file, intlineno, char *classname, char *scope
    exception-thrown有异常抛出时触发。char *classname
    exception-caught有异常被捕获时触发。char *classname
    error无论 error_reporting 的设定如何,在发生错误时都会触发。char *errormsg, char *request_file, intlineno

    PHP 扩展可以拥有额外的静态探针。

    列出 PHP 中可用的静态探针

    要列出 PHP 中可用的静态探针,开启一个 PHP 进程,然后运行:

    # dtrace -l
    

    输出信息类似如下所示:

       ID   PROVIDER            MODULE                          FUNCTION NAME
       [ . . . ]
        4   php15271               php               dtrace_compile_file compile-file-entry
        5   php15271               php               dtrace_compile_file compile-file-return
        6   php15271               php                        zend_error error
        7   php15271               php  ZEND_CATCH_SPEC_CONST_CV_HANDLER exception-caught
        8   php15271               php     zend_throw_exception_internal exception-thrown
        9   php15271               php                 dtrace_execute_ex execute-entry
       10   php15271               php           dtrace_execute_internal execute-entry
       11   php15271               php                 dtrace_execute_ex execute-return
       12   php15271               php           dtrace_execute_internal execute-return
       13   php15271               php                 dtrace_execute_ex function-entry
       14   php15271               php                 dtrace_execute_ex function-return
       15   php15271               php              php_request_shutdown request-shutdown
       16   php15271               php               php_request_startup request-startup
    

    Provider 一列由php和当前进程 id 的组成。

    如果运行的是 Apache web 服务器,那么模块名称可能是libphp5.so,并且可能会出现多块信息,每个运行中的 Apache 进程对应一个输出块。

    Function Name 一列表示 Provider 对应的 PHP 内部 C 实现函数名称。

    如果没有运行任何 PHP 进程,那么就不会显示任何 PHP 探针。

    PHP DTrace 示例

    下例展示了基本的 DTrace D 脚本。

    Example #1all_probes.dfor tracing all PHP Static Probes with DTrace

    #!/usr/sbin/dtrace -Zs
    #pragma D option quiet
    php*:::compile-file-entry
    {
        printf("PHP compile-file-entry\n");
        printf("  compile_file              %s\n", copyinstr(arg0));
        printf("  compile_file_translated   %s\n", copyinstr(arg1));
    }
    php*:::compile-file-return
    {
        printf("PHP compile-file-return\n");
        printf("  compile_file              %s\n", copyinstr(arg0));
        printf("  compile_file_translated   %s\n", copyinstr(arg1));
    }
    php*:::error
    {
        printf("PHP error\n");
        printf("  errormsg                  %s\n", copyinstr(arg0));
        printf("  request_file              %s\n", copyinstr(arg1));
        printf("  lineno                    %d\n", (int)arg2);
    }
    php*:::exception-caught
    {
        printf("PHP exception-caught\n");
        printf("  classname                 %s\n", copyinstr(arg0));
    }
    php*:::exception-thrown
    {
        printf("PHP exception-thrown\n");
        printf("  classname                 %s\n", copyinstr(arg0));
    }
    php*:::execute-entry
    {
        printf("PHP execute-entry\n");
        printf("  request_file              %s\n", copyinstr(arg0));
        printf("  lineno                    %d\n", (int)arg1);
    }
    php*:::execute-return
    {
        printf("PHP execute-return\n");
        printf("  request_file              %s\n", copyinstr(arg0));
        printf("  lineno                    %d\n", (int)arg1);
    }
    php*:::function-entry
    {
        printf("PHP function-entry\n");
        printf("  function_name             %s\n", copyinstr(arg0));
        printf("  request_file              %s\n", copyinstr(arg1));
        printf("  lineno                    %d\n", (int)arg2);
        printf("  classname                 %s\n", copyinstr(arg3));
        printf("  scope                     %s\n", copyinstr(arg4));
    }
    php*:::function-return
    {
        printf("PHP function-return\n");
        printf("  function_name             %s\n", copyinstr(arg0));
        printf("  request_file              %s\n", copyinstr(arg1));
        printf("  lineno                    %d\n", (int)arg2);
        printf("  classname                 %s\n", copyinstr(arg3));
        printf("  scope                     %s\n", copyinstr(arg4));
    }
    php*:::request-shutdown
    {
        printf("PHP request-shutdown\n");
        printf("  file                      %s\n", copyinstr(arg0));
        printf("  request_uri               %s\n", copyinstr(arg1));
        printf("  request_method            %s\n", copyinstr(arg2));
    }
    php*:::request-startup
    {
        printf("PHP request-startup\n");
        printf("  file                      %s\n", copyinstr(arg0));
        printf("  request_uri               %s\n", copyinstr(arg1));
        printf("  request_method            %s\n", copyinstr(arg2));
    }
    

    此脚本在dtrace命令中使用了-Z选项。此选项保证即使在没有任何 PHP 进程运行的时候脚本也能够正确执行。如果省略了此选项,当没有任何探针可监控的时候,脚本会立即终止执行。

    在运行此脚本的过程中,它将监控全部 PHP 核心静态探针。运行 D 脚本:

    # ./all_probes.d
    

    再运行一个 PHP 脚本或者 PHP 应用,用来进行监控的 D 脚本会输出每个探针被触发时所携带的参数。

    监控完成之后,使用^C来终止 D 脚本的执行。

    在多 CPU 的主机上,探针的显示顺序可能不是连续的,这取决于哪颗 CPU 执行探针以及多个 CPU 之间的线程迁移情况。可以通过显示探针时间戳来减少混淆,例如:

    php*:::function-entry
    {
          printf("%lld: PHP function-entry ", walltimestamp);
          [ . . .]
    }
    

    参见

    • OCI8 和 DTrace 动态跟踪

    使用 SystemTap 监控 PHP DTrace 静态探针

    在某些 Linux 发行版上,可以使用 SystemTap 跟踪工具来跟踪 PHP 的静态 DTrace 探针。此特性在 PHP 5.4.20 和 PHP 5.5 中具备。

    安装支持 SystemTap 的 PHP

    安装 SystemTap SDT 开发包。

    # yum install systemtap-sdt-devel

    安装 PHP 并启用 DTrace 探针:

    # ./configure --enable-dtrace ...
    # make

    使用 SystemTap 列出 PHP 静态探针

    使用stap命令列出 PHP 静态探针:

    # stap -l 'process.provider("php").mark("*")' -c 'sapi/cli/php -i'
    

    输出如下:

    process("sapi/cli/php").provider("php").mark("compile__file__entry")
    process("sapi/cli/php").provider("php").mark("compile__file__return")
    process("sapi/cli/php").provider("php").mark("error")
    process("sapi/cli/php").provider("php").mark("exception__caught")
    process("sapi/cli/php").provider("php").mark("exception__thrown")
    process("sapi/cli/php").provider("php").mark("execute__entry")
    process("sapi/cli/php").provider("php").mark("execute__return")
    process("sapi/cli/php").provider("php").mark("function__entry")
    process("sapi/cli/php").provider("php").mark("function__return")
    process("sapi/cli/php").provider("php").mark("request__shutdown")
    process("sapi/cli/php").provider("php").mark("request__startup")
    

    SystemTap 示例

    Example #1all_probes.stpfor tracing all PHP Static Probes with SystemTap

    probe process("sapi/cli/php").provider("php").mark("compile__file__entry") {
        printf("Probe compile__file__entry\n");
        printf("  compile_file %s\n", user_string($arg1));
        printf("  compile_file_translated %s\n", user_string($arg2));
    }
    probe process("sapi/cli/php").provider("php").mark("compile__file__return") {
        printf("Probe compile__file__return\n");
        printf("  compile_file %s\n", user_string($arg1));
        printf("  compile_file_translated %s\n", user_string($arg2));
    }
    probe process("sapi/cli/php").provider("php").mark("error") {
        printf("Probe error\n");
        printf("  errormsg %s\n", user_string($arg1));
        printf("  request_file %s\n", user_string($arg2));
        printf("  lineno %d\n", $arg3);
    }
    probe process("sapi/cli/php").provider("php").mark("exception__caught") {
        printf("Probe exception__caught\n");
        printf("  classname %s\n", user_string($arg1));
    }
    probe process("sapi/cli/php").provider("php").mark("exception__thrown") {
        printf("Probe exception__thrown\n");
        printf("  classname %s\n", user_string($arg1));
    }
    probe process("sapi/cli/php").provider("php").mark("execute__entry") {
        printf("Probe execute__entry\n");
        printf("  request_file %s\n", user_string($arg1));
        printf("  lineno %d\n", $arg2);
    }
    probe process("sapi/cli/php").provider("php").mark("execute__return") {
        printf("Probe execute__return\n");
        printf("  request_file %s\n", user_string($arg1));
        printf("  lineno %d\n", $arg2);
    }
    probe process("sapi/cli/php").provider("php").mark("function__entry") {
        printf("Probe function__entry\n");
        printf("  function_name %s\n", user_string($arg1));
        printf("  request_file %s\n", user_string($arg2));
        printf("  lineno %d\n", $arg3);
        printf("  classname %s\n", user_string($arg4));
        printf("  scope %s\n", user_string($arg5));
    }
    probe process("sapi/cli/php").provider("php").mark("function__return") {
        printf("Probe function__return: %s\n", user_string($arg1));
        printf(" function_name %s\n", user_string($arg1));
        printf("  request_file %s\n", user_string($arg2));
        printf("  lineno %d\n", $arg3);
        printf("  classname %s\n", user_string($arg4));
        printf("  scope %s\n", user_string($arg5));
    }
    probe process("sapi/cli/php").provider("php").mark("request__shutdown") {
        printf("Probe request__shutdown\n");
        printf("  file %s\n", user_string($arg1));
        printf("  request_uri %s\n", user_string($arg2));
        printf("  request_method %s\n", user_string($arg3));
    }
    probe process("sapi/cli/php").provider("php").mark("request__startup") {
        printf("Probe request__startup\n");
        printf("  file %s\n", user_string($arg1));
        printf("  request_uri %s\n", user_string($arg2));
        printf("  request_method %s\n", user_string($arg3));
    }

    在 PHP 脚本的执行过程中,上述脚本会跟踪所有的 PHP 核心静态探针:

    # stap -c 'sapi/cli/php test.php' all_probes.stp
    

    上篇:垃圾回收机制

    下篇:PHP 是什么