[Python-Dev] draft PEP: Trace and Profile Support for Threads

Jeremy Hylton jeremy@zope.com
22 Apr 2003 15:47:27 -0400


I've been working a little on the trace module lately, trying to get it
to work correctly with Zope.  One issue that remains open is how to
handle multi-threaded programs.  The PEP below proposes a solution.

Jeremy

PEP: XXX
Title: Trace and Profile Support for Threads
Version: $Revision: 1.1 $
Last-Modified: $Date: 2002/08/30 04:11:20 $
Author: Jeremy Hylton <jeremy@alum.mit.edu>
Status: Active
Type: Standards Track
Content-Type: text/x-rst
Created: 22-Apr-2003
Post-History: 22-Apr-2003

Abstract
========

This PEP describes a mechanism for attaching profile and trace
functions to a thread when it is created.  This mechanism allows
existing tools, like the profiler, to work with multi-threaded
programs.  The new functionality is exposed via a new event type for
trace functions.

Rationale
=========

The Python interpreter provides profile and trace hooks to support
tools like debuggers and profilers.  The hooks are associated with a
single thread, which makes them harder to use in a multi-threaded
environment.  For example, the profiler will only collect data for a
single thread.  If the profiled application spawns new threads, the
new threads will not be profiled.  This PEP describes a mechanism that
allows tools using profile and trace hooks to hook thread creation
events.  This mechanism would allow tools like the profiler to
automatically instrument new threads as soon as they are created.

The ability to hook thread creation makes a variety of tools more
useful.  It should allow them to work seamlessly with multi-threaded
applications.  The best alternative given the current interpreter
support is to edit a multi-threaded application to manually insert
calls to enable tracing or profiling.

Background
==========

There are two different hooks provided by the interpreter, one for
tracing and one for profiling.  The hooks are basically the same,
except that the trace hook is called for each line that is executed
but the profile hook is only called for each function.  The hooks are
exposed by the C API [1] and at the Python level by the sys module [2].
For simplicity, the rest of the section just talks about the trace
function. 

A trace function [3] is called with three arguments: a frame, an
event, and an event-dependent argument.  The event is one of the
following strings: "call," "line," "return," or "exception."  The C
API defines trace function that takes an int instead of a string to
define the trace event.

The sys.settrace() function sets the global trace function.  A global
trace function is called whenever a new local scope is entered.  If
the global trace function returns a value, it is used as the local
trace function.  If it returns None, no local tracing occurs.

Thread creation event
=====================

The proposed mechanism is to add a thread creation event called
"thread" and PyTrace_THREAD.  When thread.start_new_thread() is
called, the calling thread's trace function is called with a thread
event.  The frame passed is None or NULL and the argument is the
callable argument passed to start_new_thread().  If the trace function
returns a value from the thread event, it is used as the global trace
function for the newly created thread.

Implementation
==============

The bootstrap code in the thread module (Modules/threadmodule.c) must
be extended to take trace functions into account.  A thread's
bootstate must be extended to include pointers to the trace function
and its state object.  The t_bootstrap() code must call the trace
function before executing the boot function.

Compatibility and Limitations
=============================

An existing trace or profile function may be unprepared for the new
event type.  This may cause them to treat the thread event as some
other kind of event.

The thread event does not pass a valid frame object, because the frame
isn't available before the thread starts running.  Once the thread
starts running, it is too late to generate the thread event.

The hook is only available when a thread is created using the Python
thread module.  If a custom C extension calls
PyThread_start_new_thread() directly, the trace function will not be
called for that thread.  It's hard to judge whether this behavior is
good or bad.  It is driven partly by implementation details.  The
implementation of PyThread_start_new_thread() can not tell when or if
Python code will be executed by the thread.

References
==========

.. [1] Section 8.2, Profiling and Tracing, Python/C API Reference Manual
   (http://www.python.org/dev/doc/devel/api/profiling.html)

.. [2] Section 3.1, sys, Python Library Reference
   (http://www.python.org/dev/doc/devel/lib/module-sys.html)

.. [3] Section 9.2, How It Works (Python Debugger), Python Library
Reference
   (http://www.python.org/dev/doc/devel/lib/debugger-hooks.html)


Copyright
=========

This document has been placed in the public domain.