Add threadframe and TracebackUtils.py so we can get tracebacks of dbus deadlocks
This commit is contained in:
parent
6530653636
commit
95c06280ca
@ -1,4 +1,4 @@
|
||||
SUBDIRS = activities shell sugar tools
|
||||
SUBDIRS = activities shell sugar tools threadframe
|
||||
|
||||
dbusconfdir = $(pkgdatadir)
|
||||
dbusconf_DATA = dbus-installed.conf
|
||||
|
@ -43,4 +43,5 @@ sugar/presence/Makefile
|
||||
po/Makefile.in
|
||||
tools/Makefile
|
||||
tools/sugar-setup-activity
|
||||
threadframe/Makefile
|
||||
])
|
||||
|
@ -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
37
sugar/TracebackUtils.py
Normal 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()
|
18
threadframe/GNUmakefile.mingw2
Normal file
18
threadframe/GNUmakefile.mingw2
Normal 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
34
threadframe/README
Normal 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
37
threadframe/sample.txt
Normal 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
21
threadframe/setup.py
Normal 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
57
threadframe/test.py
Normal 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)
|
3
threadframe/threadframe.def
Normal file
3
threadframe/threadframe.def
Normal file
@ -0,0 +1,3 @@
|
||||
EXPORTS
|
||||
initthreadframe
|
||||
|
111
threadframe/threadframemodule.c
Normal file
111
threadframe/threadframemodule.c
Normal 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);
|
||||
}
|
Loading…
Reference in New Issue
Block a user