Profiling is an important part of developing an application. It tells you which parts of your program:

  • take the most time to execute
  • use the most memory
  • get called most often

What’s more, this information can help document the flow the program takes through your code. One way is letting you identify “hot paths” by making them visually obvious. Sometimes a hot path may be unavoidable, like a database call, but you can use this information to simplify or maybe reduce the number of times you call functions, and compare your different approaches scientifically.

Xdebug and QCachegrind together provide an alternative to proprietary profiling extensions. Xdebug integrates into PHP to provide robust debugging and profiling, while QCachegrind lets you visualize Xdebug’s profiling output on your own computer.

Generating profiling data with Xdebug

Installing, enabling, and configuring Xdebug

If you’re using the Seiden Group repository, you can install Xdebug in a single step:

yum install php-xdebug

Xdebug will be disabled by default, because it impacts performance. To enable Xdebug, uncomment the directive that loads Xdebug in the INI file for Xdebug, then restart the web server. The INI file can be found at /QOpenSys/etc/php/conf.d/99-xdebug.ini, and disabling/enabling is a matter of inserting/removing a comment marker (;) before the line and restarting PHP. Likewise, additional lines can be added to configure Xdebug. These will be explained more below or on the Xebug web site.

Unconditionally profiling all requests

You can set the xdebug.mode setting to profile all PHP programs and web requests. This is easiest, but keep in mind that profiling output can be quite large and fill up the IFS, so you may only want to turn it on when requested. For example:

; for profiling
xdebug.mode=profile
; for both profiling and develop mode (enhanced warnings)
xdebug.mode=profile,develop

You can enable other modes by separating them with a comma. For example, you can turn on both “develop” and “profile” modes.

Turning on Xdebug only when needed (PHP-CLI)

For PHP on the command line (PHP-CLI), you can take advantage of giving PHP INI settings over the command line, that only apply for that invocation of PHP. For example:

# The argument to the "-d" flag is as if you added a line to the INI file.
php -d "xdebug.mode=profile" script.php arguments

Turning on Xdebug only when needed (Web)

To turn on Xdebug only when requested, enable the profiling but, but also turn on xdebug.start_with_request to trigger. (This will also affect other components of Xdebug if enabled.) You can then enable Xdebug profiling on a request by either:

  1. Setting XDEBUG_TRIGGER (formerly XDEBUG_PROFILE) in your GET or POST request
  • i.e request.php?XDEBUG_TRIGGER=1
  1. Setting XDEBUG_TRIGGER in a cookie

When profiling is enabled, Xdebug will add a X-Xdebug-Profile-Filename HTTP header to the response. You can use the F12 developer tools in a browser to see headers from responses.

Where do profiler files end up? How do I change them?

By default, requests end up in the /tmp directory. The files begin with cachegrind.out. and are suffixed with the process ID.

You can change the directory these are written to with the INI setting xdebug.output_dir, and change the naming pattern with the INI setting xdebug.profiler_output_name. Consult the Xdebug documentation for how the pattern should be formatted.

Copying output to your computer

You can copy over the files using any method you prefer, be it ACS, NetServer, SFTP, FTP, etc.

Visualizing with QCachegrind

Installing

Seiden Group provides the latest version for Windows and macOS. For Linux users, look for KCachegrind in your system package manager.

Looking at the main window

QCachegrind Main View

QCachegrind Main View

Click the open button in toolbar (or from the menu) and select the profiling output file you copied. When it finishes loading, the interface is split into multiple components:

  • The left pane by default contains functions split into columns:
    • Inclusive: The time spent in the function and any functions called
    • Self: The time spent in the function and not any functions called
    • Called: How many times the function was called
    • Function: The function that was called
    • Location: The file (or if it is internal to PHP) the function was in
    • You can filter functions by name
    • The output can be grouped by class, source file, or recursive cycle
    • You can resize it by dragging the split between the left and right panes.
    • More sidebar panes can be added through the View and Settings menus
  • The top part of the right pane contains different tabs:
    • Types: Shows each measured component of the function.
    • Callers: Shows direct callers of the function.
    • All Callers: Shows indirect callers of the function.
    • Callee Map: Shows a heatmap representation of the callee view.
    • Source Code: Shows the lines of source in the file the function has.
  • The bottom part of the right pane contains different tabs:
    • Callees: Shows the direct functions called by the function.
    • Call Graph: Shows a visualization of call flow.
    • All Callers: Shows all functions that get called by the function
    • Caller Map: Shows a heatmap representation of the caller view.
    • Machine Code: Shows a low level disassembly. Not relevant to PHP, but used if you’re looking at C/C++ programs with Valgrind.
  • The toolbars and menu bar contain more options:
    • Detect Cycles: Try to handle recursion more optimally.
    • Event Type: Visualize by time spent or memory.
    • Show Percentage Relative to Parent
    • Show Relative Costs: Shows relative percentage instead of exact cost.
    • Export Graph: Renders the call graph to GraphViz or PDF files.

The help menu contains more documentation, including the “What’s this?” option that lets you learn more about specific elements on your screen.

The source view

QCachegrind Source View

QCachegrind Source View

The source view shows the gathered performance data inline with the source code of your program, including call counts and units.

By default, it won’t work out of the box unless the source code exists in the exact same location on both your IBM i and your local computer. This can be dealt with by having a copy of the directory structure and files on your computer that exists on the IBM i. For example, you can have a directory of source (say, C:/Src) matching C:/Src/www/example/htdocs/test.php to the IBM i path /www/example/htdocs/test.php. (It’s tolerant if you don’t match it exactly; it’ll try to find the first example of that filename, but it’s easier on yourself if you keep it close.)

If you have the source code from the IBM i mounted (through NetServer, SSHFS, etc.), then you might want to simply use that share instead. Keep in mind looking for the source files can result in network traffic.

To add this directory, go into the settings (Settings -> Configure), find the options for source annotation, and add the base directory you want. When you close it, the source view will reload and look for the file there. (The mention of ELF objects is because the program also supports profiling native code programs through Valgrind.)

QCachegrind Source Annotation Settings

QCachegrind Source Annotation Settings

Appendix: What’s the difference between KCachegrind and QCachegrind?

KCachegrind is the original version and was made for KDE, a free desktop environment. QCachegrind is a version with the KDE dependency reduced so it only needs Qt.

As for the “Cachegrind” part, it’s because the tool was originally designed for analyzing CPU cache usage in C/C++ applications. Despite the initial focus, it’s useful for measuring other things (like timing and memory usage) in other languages (like PHP).

Alternatives to KCachegrind

Other tools that can visualize the cachegrind files that Xdebug outputs include:

PhpStorm Profiler View

PhpStorm Profiler View

  • PhpStorm: Tools -> Analyze Xdebug Profiler Snapshot

Limitations

Xdebug profiling will log memory usage by PHP, not extensions. It will, however, keep track of the time spent in a function, even if it involved extensions (native code). This keeps it useful for looking at waits on database calls, for example.

References

If you need more resources on advanced Xdebug usage, please consult these additional articles: