7. Debugging

There are a variety of features available to help with debugging in Shrapnel.

7.1. Backdoor

A very powerful feature of Shrapnel is the ability to access a running process via a backdoor. You can telnet to a socket (typically a unix-domain socket) and get a Python prompt. At this point, you can interact with anything in your Shrapnel process.

As an example of something you can do in the backdoor is call coro.where_all(). This will return a dictionary of every coroutine that is running with a string describing the call stack of where that coroutine is currently blocked.

To enable the backdoor, you typically start a backdoor coroutine before starting the event loop with the following code:

import coro.backdoor
coro.spawn(coro.backdoor.serve)

By default this will listen on all IP’s on the lowest port available from 8023 to 8033. This isn’t a very safe or secure thing to do. It’s best to specify a unix-domain socket with the unix_path parameter. See coro.backdoor.serve() for details.

By default, the globals available in a backdoor session is a copy of the globals from your applications __main__ module.

coro.backdoor.serve(port=None, ip='', unix_path=None, welcome_message=None, global_dict=None)

Backdoor server function.

This function will listen on the backdoor socket and spawn new threads for each connection.

Parameters :
  • port: The IPv4 port to listen on (defaults to automatically choose an unused port between 8023->8033). May also be a list of ports.
  • ip: The IP to listen on. Defaults to all IP’s.
  • unix_path: The unix path to listen on. If this is specified, then it will use unix-domain sockets, otherwise it will use IPv4 sockets.
  • welcome_message: A welcome message to display when a user logs in.
  • global_dict: The global dictionary to use for client sessions.

7.2. Stderr Output

Shrapnel provides some functions for printing debug information to stderr. The coro.print_stderr() function will print a string with a timestamp and the thread number. The coro.write_stderr() function writes the string verbatim with no newline.

Shrapnel keeps a reference to the “real” stderr (in saved_stderr) and the print_stderr and write_stderr functions always use the real stderr value. A particular reason for doing this is the backdoor module replaces sys.stderr and sys.stdout, but we do not want debug output to go to the interactive session.

coro.write_stderr()

write(str) -> None. Write string str to file.

Note that due to buffering, flush() or close() may be needed before the file on disk reflects the data written.

coro.print_stderr(s)

Print a string to stderr with a timestamp.

This will print the thread id, followed by a timestamp, followed by the string. If the string does not end with a newline, one will be added.

Parameters:s – A string to print.

7.3. Exceptions

7.3.1. Tracebacks

As a convenience, Shrapnel has a module for printing stack traces in a condensed format. The coro.tb module has the coro.tb.stack_string() function for printing the current stack, and coro.tb.traceback_string() for getting a traceback in an exception handler.

coro.tb.stack_string(f=None)

Return a compact string representing the current call stack.

Parameters :
  • f: Frame object. If not specified, will use the current call position.
Return :

Returns a string of the current stack trace.

coro.tb.traceback_string(t=None, v=None, tb=None)

Returns a compact string representing the current exception.

If an exception is not provided as an argument, then it will get the current exception from sys.exc_info.

Parameters :
  • t: Exception type.
  • v: Exception value.
  • tb: Traceback object.
Return :

Returns a string of the current exception and stack trace.

7.3.2. Exception Notifications

If an exception is raised in a coroutine and is never caught, then Shrapnel will by default display the exception to stderr. If you want to change this behavior, use coro.set_exception_notifier().

coro.set_exception_notifier(new_func)

Set the exception notifier.

The exception notifier is a function that is called when a coroutine exits due to an exception. The default exception notifier simply prints the name of the coroutine and a traceback of where the exception was raised.

Parameters:new_func – The exception notifier to call. It takes no arguments.
Returns:The old exception notifier.

7.4. Latency

Shrapnel will keep track of how long a coroutine runs before it yields. This is helpful to track down coroutines which are running for too long, or are potentially calling blocking calls. Here is an example of the output that would be sent to stderr when this happens:

Sat Apr 14 20:55:39 2012 High Latency: (3.884s)
    for <coro #1 name='<function my_func at 0x800fd32a8>'
         dead=0 started=1 scheduled=0 at 0x801424720>

You can change the threshold that will trigger this warning with the coro.set_latency_warning() function. However, doing this to silence warnings isn’t a good idea. It is best to fix whatever code is causing the warnings. You can either call coro.yield_slice() periodically to let other coroutines run, or make sure you are not calling any blocking operations.

coro.set_latency_warning(self, int factor)

Set the latency warning threshold multiplier.

The default latency warning threshold is 0.2 seconds. This will allow you to change the threshold by multiplying the 0.2 value.

Parameters:factor – The latency threshold multiplier. May be a number from 0 to 300. A value of 0 disables latency warnings.
Returns:The old multipler factor.
Raises ValueError:
 The factor is too small or too large.

7.5. Functions

The coro module defines the following functions:

coro.where(co)

Return a string indicating where the given coroutine thread is currently running.

Parameters:co – The coroutine object.
Returns:A string displaying where the coro thread is currently executing.
coro.where_all()

Get a dictionary of where all coroutines are currently executing.

Returns:A dictionary mapping the coroutine ID to a tuple of (name, coro, where) where where is a string representing where the coroutine is currently running.
coro.get_live_coros()

Get the number of live coroutines.

Note that this includes coroutines that have not started or have exited, but not deallocated, yet.

Returns:The number of live coroutine objects.

Table Of Contents

This Page