Add threadframe and TracebackUtils.py so we can get tracebacks of dbus deadlocks

This commit is contained in:
Dan Williams 2006-08-16 23:05:44 -04:00
parent 6530653636
commit 95c06280ca
11 changed files with 321 additions and 1 deletions

View File

@ -1,4 +1,4 @@
SUBDIRS = activities shell sugar tools
SUBDIRS = activities shell sugar tools threadframe
dbusconfdir = $(pkgdatadir)
dbusconf_DATA = dbus-installed.conf

View File

@ -43,4 +43,5 @@ sugar/presence/Makefile
po/Makefile.in
tools/Makefile
tools/sugar-setup-activity
threadframe/Makefile
])

View File

@ -8,6 +8,7 @@ sugar_PYTHON = \
env.py \
logger.py \
setup.py \
TracebackUtils.py \
util.py
EXTRA_DIST = __uninstalled__.py

37
sugar/TracebackUtils.py Normal file
View File

@ -0,0 +1,37 @@
import sys
import traceback
import os
import signal
haveThreadframe = True
try:
import threadframe
except ImportError:
haveThreadframe = False
class TracebackHelper(object):
def __init__(self):
fname = "%s-%d" % (os.path.basename(sys.argv[0]), os.getpid())
self._fpath = os.path.join("/tmp", fname)
print "Tracebacks will be written to %s on SIGUSR1" % self._fpath
signal.signal(signal.SIGUSR1, self._handler)
def __del__(self):
try:
os.remove(self._fpath)
except OSError:
pass
def _handler(self, signum, pframe):
f = open(self._fpath, "a")
if not haveThreadframe:
f.write("Threadframe not installed. No traceback available.\n")
else:
frames = threadframe.dict()
for thread_id, frame in frames.iteritems():
f.write(('-' * 79) + '\n')
f.write('[Thread %s] %d' % (thread_id, sys.getrefcount(frame)) + '\n')
traceback.print_stack(frame, limit=None, file=f)
f.write("\n")
f.write('\n')
f.close()

View File

@ -0,0 +1,18 @@
PYTHON:= $(shell python -c "import sys;print '%%d%%d' %% sys.version_info[:2]")
threadframe.pyd: threadframe.o libpython$(PYTHON).a
dllwrap --dllname threadframe.pyd --driver-name=gcc --def threadframe.def -o threadframe.pyd threadframe.o -s --entry _DllMain@12 --target=i386-mingw32 -L. -lpython$(PYTHON)
threadframe.o: threadframemodule.c
gcc -I"C:\Program Files\Python$(PYTHON)\include" -O3 -c -o $@ -DNDEBUG $<
libpython$(PYTHON).a: python$(PYTHON).def C:\WINNT\system32\python$(PYTHON).dll
dlltool --dllname python$(PYTHON).dll --def python$(PYTHON).def --output-lib libpython$(PYTHON).a
python$(PYTHON).def: C:\WINNT\system32\python$(PYTHON).dll
pexports C:\WINNT\system32\python$(PYTHON).dll > python$(PYTHON).def
clean:
-del threadframe.pyd
-del libpython$(PYTHON).a
-del threadframe.o
-del python$(PYTHON).def

34
threadframe/README Normal file
View File

@ -0,0 +1,34 @@
Note on the License
Dan Williams <dcbw at redhat com> 2006-08-16
Since 'setup.py' specifies the "Python" license, it is assumed that the
threadframe package is distributed under that license, even though there
is no license header at the top of the source file.
Obtaining tracebacks on other threads in Python
===============================================
by Fazal Majid (www.majid.info), 2004-06-10
David Beazley added advanced debugging functions to the Python interpreter,
and they have been folded into the 2.2 release. Guido van Rossum added in
Python 2.3 the thread ID to the interpreter state structure, and this allows
us to produce a dictionary mapping thread IDs to frames.
I used these hooks to build a debugging module that is useful when you
are looking for deadlocks in a multithreaded application. I've built
and tested this only on Solaris 8/x86, but the code should be pretty
portable.
Of course, I disclaim any liability if this code should crash your system,
erase your homework, eat your dog (who also ate your homework) or otherwise
have any undesirable effect.
Building and installing
=======================
Download threadframe-0.2.tar.gz. You can use the Makefile or the setup.py
script. There is a small test program test.py that illustrates how to use this
module to dump stack frames of all the Python interpreter threads. A sample
run is available for your perusal.

37
threadframe/sample.txt Normal file
View File

@ -0,0 +1,37 @@
Script started on Thu 10 Jun 2004 07:23:38 PM PDT
bayazid ~/threadframe-0.2>python test.py
ident of main thread is: 1
launching daemon thread... done
launching self-deadlocking thread... done
launching thread that will die before the end... done
[4] Spam spam spam spam. Lovely spam! Wonderful spam!
[4] Spam spam spam spam. Lovely spam! Wonderful spam!
[4] Spam spam spam spam. Lovely spam! Wonderful spam!
[4] Spam spam spam spam. Lovely spam! Wonderful spam!
------------------------------------------------------------------------
[1] 4
File "test.py", line 56, in ?
traceback.print_stack(frame)
------------------------------------------------------------------------
[4] 4
File "/usr/local/lib/python2.3/threading.py", line 436, in __bootstrap
self.run()
File "test.py", line 6, in run
time.sleep(1)
------------------------------------------------------------------------
[5] 4
File "/usr/local/lib/python2.3/threading.py", line 436, in __bootstrap
self.run()
File "test.py", line 13, in run
U_lock.acquire()
------------------------------------------------------------------------
[6] 3
File "/usr/local/lib/python2.3/threading.py", line 455, in __bootstrap
pass
File "test.py", line 20, in run
V_event.wait()
File "/usr/local/lib/python2.3/threading.py", line 352, in wait
self.__cond.release()
File "/usr/local/lib/python2.3/threading.py", line 235, in wait
self._acquire_restore(saved_state)

21
threadframe/setup.py Normal file
View File

@ -0,0 +1,21 @@
from distutils.core import setup
from distutils.extension import Extension
setup(
name = 'threadframe',
version = '0.2',
description = "Advanced thread debugging extension",
long_description = "Obtaining tracebacks on other threads than the current thread",
url = 'http://www.majid.info/mylos/stories/2004/06/10/threadframe.html',
maintainer = 'Fazal Majid',
maintainer_email = 'threadframe@majid.info',
license = 'Python',
platforms = [],
keywords = ['threading', 'thread'],
ext_modules=[
Extension('threadframe',
['threadframemodule.c'],
),
],
)

57
threadframe/test.py Normal file
View File

@ -0,0 +1,57 @@
import sys, time, threading, thread, os, traceback, threadframe, pprint
# daemon thread that spouts Monty Pythonesque nonsense
class T(threading.Thread):
def run(self):
while 1:
time.sleep(1)
print '[%d] Spam spam spam spam. Lovely spam! Wonderful spam!' % ( thread.get_ident(), )
# thread that cause a deliberate deadlock with itself
U_lock = threading.Lock()
class U(threading.Thread):
def run(self):
U_lock.acquire()
U_lock.acquire()
# thread that will exit after the thread frames are extracted but before
# they are printed
V_event = threading.Event()
class V(threading.Thread):
def run(self):
V_event.clear()
V_event.wait()
print 'ident of main thread is: %d' % (thread.get_ident(),)
print
print 'launching daemon thread...',
T().start()
print 'done'
print 'launching self-deadlocking thread...',
U().start()
print 'done'
print 'launching thread that will die before the end...',
v = V()
v.start()
print 'done'
time.sleep(5)
# Python 2.2 does not support threadframe.dict()
if sys.hexversion < 0x02030000:
frames = threadframe.threadframe()
else:
frames = threadframe.dict()
# signal the thread V to die, then wait for it to oblige
V_event.set()
v.join()
if sys.hexversion < 0x02030000:
for frame in frames:
print '-' * 72
print 'frame ref count = %d' % sys.getrefcount(frame)
traceback.print_stack(frame)
else:
for thread_id, frame in frames.iteritems():
print '-' * 72
print '[%s] %d' % (thread_id, sys.getrefcount(frame))
traceback.print_stack(frame)
os._exit(0)

View File

@ -0,0 +1,3 @@
EXPORTS
initthreadframe

View File

@ -0,0 +1,111 @@
/*
* module to access the stack frame of all Python interpreter threads
*
* works on Solaris and OS X, portability to other OSes unknown
*
* Fazal Majid, 2002-10-11
*
* with contributions from Bob Ippolito (http://bob.pycs.net/)
*
* Copyright (c) 2002-2004 Kefta Inc.
* All rights reserved
*
*/
#include "Python.h"
#include "compile.h"
#include "frameobject.h"
#include "patchlevel.h"
static PyObject *
threadframe_threadframe(PyObject *self, PyObject *args) {
PyInterpreterState *interp;
PyThreadState *tstate;
PyFrameObject *frame;
PyListObject *frames;
frames = (PyListObject*) PyList_New(0);
if (! frames) return NULL;
/* Walk down the interpreters and threads until we find the one
matching the supplied thread ID. */
for (interp = PyInterpreterState_Head(); interp != NULL;
interp = interp->next) {
for(tstate = interp->tstate_head; tstate != NULL;
tstate = tstate->next) {
frame = tstate->frame;
if (! frame) continue;
Py_INCREF(frame);
PyList_Append((PyObject*) frames, (PyObject*) frame);
}
}
return (PyObject*) frames;
}
/* the PyThreadState gained a thread_id member only in 2.3rc1 */
static PyObject *
threadframe_dict(PyObject *self, PyObject *args) {
#if PY_VERSION_HEX < 0x02030000
PyErr_SetString(PyExc_NotImplementedError,
"threadframe.dict() requires Python 2.3 or later");
return NULL;
#else
PyInterpreterState *interp;
PyThreadState *tstate;
PyFrameObject *frame;
PyObject *frames;
frames = (PyObject*) PyDict_New();
if (! frames) return NULL;
/* Walk down the interpreters and threads until we find the one
matching the supplied thread ID. */
for (interp = PyInterpreterState_Head(); interp != NULL;
interp = interp->next) {
for(tstate = interp->tstate_head; tstate != NULL;
tstate = tstate->next) {
PyObject *thread_id;
frame = tstate->frame;
if (! frame) continue;
thread_id = PyInt_FromLong(tstate->thread_id);
PyDict_SetItem(frames, thread_id, (PyObject*)frame);
Py_DECREF(thread_id);
}
}
return frames;
#endif
}
static char threadframe_doc[] =
"Returns a list of frame objects for all threads.\n"
"(equivalent to dict().values() on 2.3 and later).";
static char threadframe_dict_doc[] =
"Returns a dictionary, mapping for all threads the thread ID\n"
"(as returned by thread.get_ident() or by the keys to threading._active)\n"
"to the corresponding frame object.\n"
"Raises NotImplementedError on Python 2.2.";
/* List of functions defined in the module */
static PyMethodDef threadframe_methods[] = {
{"threadframe", threadframe_threadframe, METH_VARARGS, threadframe_doc},
{"dict", threadframe_dict, METH_VARARGS, threadframe_dict_doc},
{NULL, NULL} /* sentinel */
};
/* Initialization function for the module (*must* be called initthreadframe) */
static char module_doc[] =
"Debugging module to extract stack frames for all Python interpreter heads.\n"
"Useful in conjunction with traceback.print_stack().\n";
DL_EXPORT(void)
initthreadframe(void)
{
PyObject *m;
/* Create the module and add the functions */
m = Py_InitModule3("threadframe", threadframe_methods, module_doc);
}