PHP Xdebug

in mDIS

mDIS Developer Guide, 'Debugging' section

How to do stepwise remote debugging with Xdebug 2 or Xdebug 3

Stepwise debugging is relatively easy. Setting up of your development environment, is what takes a lot of time, because it is difficult to get everything exactly right. To complicate things, each developer's environment is configured differently, so be ready to adapt the instructions below.

Simple is better

Keep things as simple as possible. If you want to debug an mDIS instance that

  • runs on a server protected by powerful firewalls or VPNs, and/or
  • has port forwarding disabled on the SSH server (or on some other layer of the network stack)
  • sits behind a proxy or a reverse proxy,
  • is virtualized or lives inside a container, or
  • is hosted by a cloud provider,

Then setting up Xdebug requires much more experimentation and patience than described below.
It is also quite possible that remote debugging might not work at all.
In this case you will need to ask your network administrator for help; or try "local" debugging: installing Xdebug on an mDIS instance your workstation and debugging from there.

The following describes my personal setup for Xdebug 2. It is not necessarily the best setup for your environment.

Config values for Xdebug 3 are different, and incompatible with the settings described here. (Xdebug 3.0.0 was released in late 2020, works with for PHP 7.4 and higher)

Debugging of web pages with PHP Xdebug and Visual Studio Code

You need to be administrator on your machine. Install 3 pieces of software to do remote debugging, and create a config file:

  1. The Xdebug (opens new window) php extension
  2. The VS Code extension "xdebug.php-debug" (opens new window)
  3. The browser extension Xdebug helper for Chrome (opens new window) or for Firefox (opens new window)
  4. A config file .vscode/launch.json at the top level of your workspace folder

All 3 pieces are free/gratis.

Xdebug is an extension for PHP to assist with debugging and development.

From the XDebug Homepage (opens new window)

  • It contains a single step debugger (opens new window) to use with IDEs. (This topic is the most important for mDIS debugging)
  • it upgrades PHP's var_dump() function
  • it adds stack traces for Notices, Warnings, Errors and Exceptions
  • it features functionality for recording every function call and variable assignment to disk
  • it contains a profiler
  • provides code coverage functionality for use with PHPUnit

Installation

Installation for Visual Studio Code

For remote debugging, there are two hosts who must communicate during a debugging session: (1) The mDIS Server, and (2) the developer's workstation, where the mDIS codebase is installed.

mDIS Server

On the mDIS Server, the XDebug Installation Process (opens new window) is straightforward. Do that first.

Instructions for Ubuntu Linux 18.04 and 20.04

sudo apt install php-xdebug
apt-file show php-xdebug # optional. lists 4 files in package.
sudo phpenmod xdebug
sudo apachectl restart
# check contents of the .ini file, add a few lines as mentioned below
1
2
3
4
5

The steps for installations on other Linux distributions or platforms are not repeated here. Please read the Xdebug documentation.
We have used Xdebug v2.6.0 from late 2019, and newer versions of Xdebug.

On the webserver, a configuration file xdebug.ini should be created and stored in an appropriate location, e.g. where the other php-ini files are stored. On Ubuntu Linux, the absolute path should look similar to this one: /etc/php/7.2/apache2/conf.d/20-xdebug.ini. Do a phpenmod xdebug and restart the web server.

Run php -i on the command line. Better yet, create a phpinfo() page and see phpinfo() output.
Check the phpinfo() page for xdebug entries.

(For Xdebug 2, if something blocks incoming traffic on port 9003, you can also explicitly set xdebug.remote_connect_back=0. Then change
;xdebug.remote_host=139.17.81.xx
to
xdebug.remote_host=139.17.81.45 # (or whatever the hostname or IP-address of your development machine is) In the output of PHPinfo (or php -i), the value for xdebug.remote_enable must exist and really set to on and not to off. ).

For Xdebug 2, the contents of xdebug.ini should look like this, if your network settings do not block traffic on port 9003:

; Xdebug v2
zend_extension=xdebug.so
xdebug.remote_enable=1
xdebug.remote_autostart=1
xdebug.remote_connect_back=1
;;; or explicitly allow ICDP-OSG Intranet
;xdebug.remote_host=139.17.81.xx

;;; for xdebug v2, default port was 9000
; use custom value 9003. 
; for xdebug v3, default port is 9003.
; This number must match value in launch.json config file
xdebug.remote_port=9003
;;; our preferred editor uses this value
xdebug.idekey=VSCODE
; troubleshooting, optional:
;xdebug.remote_log = /var/tmp/xdebug.log
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

For Xdebug 3, the contents of xdebug.ini could look like this:

# fewer settings are required for Xdebug 3
# and values are different than for v2.
xdebug.mode=debug
xdebug.start_with_request=yes
xdebug.remote_autostart=1
xdebug.remote_connect_back=1
xdebug.idekey=VSCODE
;;; default port is 9003 for Xdebug 3. 
;;; client settings must match this value.
1
2
3
4
5
6
7
8
9

If your webapp runs on localhost/your own PC, then you are all set.
If your webapp runs on a remote server, and you have mounted the directory of the webapp locally, then also issue this command on your local machine:

ssh -R 9003:YourLocalIP:9003 youruser@mdishost.com

The -R option means: The connection on 9003 will be established from the remote machine to your local machine, so the remote machine will send outbound data. SSH will also listen on port 9003 on the remote machine, and forward debugging data sent via that port to your local machine, also on port 9003.

(TBC):

For debugging it is easier to run mDIS inatively, without containers or virtual machines. For example, if your machine runs inside a Docker container, you need to add another port-forwarding rule to redirect incoming traffic on port 9003 to the container's port 9003.

Xdebug Troubleshooting

Firewall issues

If your server is protected by a firewall, you might need to open port 9003 or whatever port you prefer to listen on for xdebug. Alternatively you might also need to disable the software-firewall on your mDIS server, or add a new rule for incoming traffic. With Ubuntu, try something like this:

sudo ufw status numbered
sudo ufw allow from 139.17.0.0./24 to any port 9003
1
2

Alternatively, set a more specific hostname and preferred port number, respectively.

Another expert command to try out during debugging is

tcpdump -vv -nn -i any dst port 9003

but it needs tcpdump installed.

Config file mismatches

If your value for xdebug.remote_host is set to localhost in the ini-file, you sometimes cannot remote-debug when the value (in the .vscode/launch.json file) is set to 127.0.0.1 or ::1 (although it's semantically the same). Check the actual value in the output of your webserver access log.

Trying to send data back from server to your IDE

If you have specified a logfile location such as /var/tmp/xdebug.log, then this will be created on the mDIS server (where php-xdebug is installed).

If you haven't specified a file location, another xdebug logfile might be reachable at a directory similar to this one:

/var/tmp/systemd-private-SOME_ID-apache2.service-Ify9fj/tmp/xdebug.log

Both /var/tmp/xdebug.log and the one just mentioned contain this content.

sudo tail -f /var/tmp/systemd-private-9b637db8485f46859a97389f791b08b6-apache2.service-Ify9fj/tmp/xdebug.log
[1034122] Log closed at 2020-06-09 13:48:32

[1034122] Log opened at 2020-06-09 13:48:32
[1034122] I: Checking remote connect back address.
[1034122] I: Checking header 'HTTP_X_FORWARDED_FOR'.
[1034122] I: Checking header 'REMOTE_ADDR'.
[1034122] I: Remote address found, connecting to 188.100.66.140:9003.
[1034122] E: Time-out connecting to client (Waited: 200 ms). :-(
[1034122] Log closed at 2020-06-09 13:48:32

1
2
3
4
5
6
7
8
9
10
11
Debugger gets stuck

When the debugger cannot continue stepping through code during an interactive session:

  • Remove unneeded watch-variables, especially those that intercept "native code" such as the mysqli....() Functions.
  • Also check your value of the "ignore" setting in launch.json.

mDIS Client

On the client, the PC workstation where the developers want to carry out the debugging session, some files need to be set up as well.

First, a copy of the codebase of mDIS that runs on the server must be present on the client. Both client and server should use the same codebase. To do so, simply clone the same git repository, or mount the remote directory with SSHFS (or some other mechanism that is available to you).

In VS Code, there should be a workspace created. Add the local folder with the mDIS codebase to the workspace.
In the client directory, where that root folder of the mDIS codebase resides, there must be a subdirectory .vscode. If it is not there, create it.

Inside directory .vscode,create a file launch.json with this content (which you need to edit in places):

{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "php:xdebug",
            "type": "php",
            "request": "launch",
            "port": 9003,
            "stopOnEntry": false,
            "pathMappings": {
                "/var/www/dis": "${workspaceFolder}",
                "/app": "${workspaceFolder}/"
            },
            "ignore": [
                "**/Vendor/**/*.php"
            ],
            "xdebugSettings": {},
            "log": false,
            "hostname": "0.0.0.0"
        }
    ]
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

Adapt the pathMappings according to your needs.
In this example, two "pathMappings" are predefined, for two different mDIS Servers which can be debugged individually (by a roaming developer). On mDIS Server 1, the PHP code resides in /var/www/dis, on mDIS Server2 (a Docker container) the PHP code resides in Docker volume /app. They are mapped to the active VSCode workspace folder. The left side of the colon character identifies a remote directory that enables the debugger to be able to step into web/index.php. Sometimes you want third-party code to be ignored during debugging.

Browser Extensions

Install Extension "xdebug-helper" for Google Chrome (opens new window) or the equivalent extension "xdebug-helper for Firefox" (opens new window).

In the respective configuration page ("Preferences") of the Browser-Add-On, set the IDEKey Config Value to "VSCODE:

idekey

Enabling the debugging extension

The extension needs to be enabled in tow separate locations.

First, enable the browser extension in the extension settings.
Then look for the icon of this extension in the address-bar of the browser.

browserextension

Setting it to green means enabling it. The extension tells the browser to start or "switch on" a debugging session on the server and send some additional data with each HTTP request.

Do not have "Debug" enabled (set to green) when you are using the Adminer webbased SQL client (if the Adminer tool is reachable via the same URL). Xdebug will try to intercept the adminer.php script, and you do not want this. The code is incomprehensible.

Debugging

Launching a debugger

Start your debugging session by clicking on the "Debug" Icon in the Sidebar of the VSCode IDE (1). Then click on "Debug and run" (2). The debug configuration selected (3) should be php:xdebug.

xdebug

After clicking on "Debug and Run", see red (1) below, the DebugBar should appear.

debugbar

To determine where Xdebug looks for on your development workstation, in file .vscode/launch.json, set keys stopOnEntry to true and log to true temporarily.

  • stopOnEntry=true sets a breakpoint on the first line of code in the index.php file, so you can stop through your code from the very beginning. If the debugger is connected at all.
  • log=true writes verbose output into the debug pane of the VSCode IDE.

Then click on "Continue" or "StepOver" icon in the Debugbar, see (2) in figure above. If your pathMapping is wrong, a dialog box with an error message will appear, and tell you where Xdebug looked for PHP files on your server, and why it didn't find them, most likely because of an incorrect path mapping.

Happy debugging!

Hunting down bugs

There is no general strategy to find and remove bugs in a codebase. Every problem is different.

(TBC)