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)
|
dbusconfdir = $(pkgdatadir)
|
||||||
dbusconf_DATA = dbus-installed.conf
|
dbusconf_DATA = dbus-installed.conf
|
||||||
|
@ -43,4 +43,5 @@ sugar/presence/Makefile
|
|||||||
po/Makefile.in
|
po/Makefile.in
|
||||||
tools/Makefile
|
tools/Makefile
|
||||||
tools/sugar-setup-activity
|
tools/sugar-setup-activity
|
||||||
|
threadframe/Makefile
|
||||||
])
|
])
|
||||||
|
@ -8,6 +8,7 @@ sugar_PYTHON = \
|
|||||||
env.py \
|
env.py \
|
||||||
logger.py \
|
logger.py \
|
||||||
setup.py \
|
setup.py \
|
||||||
|
TracebackUtils.py \
|
||||||
util.py
|
util.py
|
||||||
|
|
||||||
EXTRA_DIST = __uninstalled__.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