[Python-Dev] draft PEP: Trace and Profile Support for Threads (original) (raw)

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.1Revision: 1.1 Revision:1.1 Last-Modified: Date:2002/08/3004:11:20Date: 2002/08/30 04:11:20 Date:2002/08/3004: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.