Debug Tools - gregtampa/PHP-Daemon GitHub Wiki

Debug Tools

Version 2.0

Integrated Debug Console

The Workers API includes an integrated debug console. This is tremendously useful to debug multi-process communication between your Daemon application and worker processes. You can read more about it in the Debugging Workers guide.

However, it's designed to debug workers. If your application is single-process or just doesn't use the Workers API, the console app will not be available. Other debug tools in the ./scripts directory, however, are.

Shared Behaviors

  • Both the SHM and Signal consoles have a basic integrated help command to display currently applicable help options. Use it if you're lost.
  • Both have an exit command.
  • You cannot use the up-arrow to repeat the last command the way you can in Bash or in the integrated debug console.
  • You can however use abbreviated commands: For help, for example, you can type just h or he or hel, etc, etc. If two commands share the same prefix and are ambiguous a list of possible matches will be displayed.

The Signal Console

Out of the box, your application will respond to several POSIX signals to handle things like graceful restarts and dumping runtime data to the log. You can extend that by easily creating a custom signal handler. It's useful in debugging to create signal handlers that can mimic runtime tasks: call a certain function, emulate some input, etc.

You can pass signals from any Bash prompt using kill -[N] [pid] and if you're not using an array of custom signal handlers that is probably the easiest way to send them.

In cases like the ExampleWorkers daemon, however, we found it tedious and error prone to send several different signals to both daemon process and worker processes. The signal console makes the task much easier.


When you start your daemon you can pass it a -p parameter with a filesystem location to write a PID file. This is a standard practice for unix daemons. The easiest way to start a signal console and attach it to your daemon is to always use PID files (we create startup scripts like the one you can see in ./scripts/ew) and update the simple array at the top of signal_console.php with the location of the PID file.

Shortcuts are given simple numeric IDs and the table of IDs is displayed when you start the console. You can then type something simple like -0 thru -N at the prompt. The console will look for a PID file, read the PID, and attach the console to that process.

Using the Console

At any time you can use the help command to view available options.

When you start the console you will be at a pid> prompt. You can then type a shortcut or a numeric pid. Once it attaches to a process the prompt will change to sig>. You can release the attached process and change to any other process by entering pid at the sig> prompt, or even jump right to another process by adding the pid or shortcut ID right to that:

sig> pid 12345 OR sig> pid -1

From a sig> prompt you can send any valid signal to the attached process simply by entering it:

  • sig> 1 Send a SIGHUP (Graceful Restart) signal
  • sig> 2 Send a SIGINT (Graceful Shutdown) signal
  • sig> 10 Send a SIGUSR1 (Runtime Stats) signal
  • sig> 18 Send a SIGCONT (Break Sleep) signal
  • sig> ?? Send any signal you're watching using your custom signal handler.

The console will watch the attached process and if it dies, the sig> prompt will be released and you'll go back to a pid> prompt.

The SHM Console

If you're using the PHP SHM api to access shared memory in your operating system (and if you're using the Workers API you are), it's often important during debugging to inspect the contents of the memory block. Since PHP isn't shipped with a truly blissful REPL like Python, Ruby, etc, this can be rather tedious. The SHM console will make that a lot easier.


Shortcuts in the SHM console work the same as they do in the Signal Console. In this case, the shortcut value is the memory block address -- a 10 digit number. PHP ships with a wrapper to the ftok() function to create a shared memory address based on the filesystem location of the script calling it, and that function is used in the Worker API. Why does this matter? The point is that as long as you don't change the filesystem location of your daemon, or the name of the worker alias, the shared memory address will always be the same. So you can save it in the shortcut array and not have to worry about looking it up each time.

The first time you need it, though -- and if you do change the alias or the working directory -- you can get the shared memory addresses in use by using the SIGUSR1 (kill -10) signal. Among other details you'll see:

Loaded Workers: PrimeNumbers 1342197841 [AVAILABLE], GetFactors 1191202898 [AVAILABLE],

Those 10-digit numbers are the memory locations for that worker.

Using the Console

When you start the console you'll be at an ADDR > prompt. You can use a shortcut or a memory address here. Once you attach to a memory block, the prompt will change to an 'ID >' prompt. Each memory block is, basically, an array with numeric indexes for each key.

If you know the key, enter it. To see all keys currently in-use you can use the scan command. In the case of Workers, it will always have the 1 key in use: This is the memory header. If no header is found, it will still attach to the block but it'll warn you that the block appears to be uninitialized. If you're using the block for something other than a worker, just disregard that warning.

Alternatively to scan, you can chose the watch command. This will run scan several times per second to determine new, removed and updated keys. Note that since it does iterative polling it is both very inefficient and it's quite possible that a key could be added and removed and not show-up in the log. It can, however, still be very useful while debugging workers and using the integrated debug console to put a watch on a memory block and see what data is being written to the shared memory.

You can break a watch by hitting enter or CTRL+C (Using CTRL+C outside a watch will just exit the console).

Once broken, you can re-enter a watch using the rewatch command or remove any existing state information (used to inform you when keys are modified) by just using watch again.