Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDan Williams <dcbw@localhost.localdomain>2006-08-17 03:05:44 (GMT)
committer Dan Williams <dcbw@localhost.localdomain>2006-08-17 03:05:44 (GMT)
commit95c06280ca214fe13570e5aebd0d40857f90610c (patch)
treeb314f9585e6aeb0037fe767af45289205043e800
parent653065363639ebb59bcbbd682c6a0249dd475f11 (diff)
Add threadframe and TracebackUtils.py so we can get tracebacks of dbus deadlocks
-rw-r--r--Makefile.am2
-rw-r--r--configure.ac1
-rw-r--r--sugar/Makefile.am1
-rw-r--r--sugar/TracebackUtils.py37
-rw-r--r--threadframe/GNUmakefile.mingw218
-rw-r--r--threadframe/README34
-rw-r--r--threadframe/sample.txt37
-rw-r--r--threadframe/setup.py21
-rw-r--r--threadframe/test.py57
-rw-r--r--threadframe/threadframe.def3
-rw-r--r--threadframe/threadframemodule.c111
11 files changed, 321 insertions, 1 deletions
diff --git a/Makefile.am b/Makefile.am
index d1b0640..5d9791f 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,4 +1,4 @@
-SUBDIRS = activities shell sugar tools
+SUBDIRS = activities shell sugar tools threadframe
dbusconfdir = $(pkgdatadir)
dbusconf_DATA = dbus-installed.conf
diff --git a/configure.ac b/configure.ac
index f53cb50..95c4c46 100644
--- a/configure.ac
+++ b/configure.ac
@@ -43,4 +43,5 @@ sugar/presence/Makefile
po/Makefile.in
tools/Makefile
tools/sugar-setup-activity
+threadframe/Makefile
])
diff --git a/sugar/Makefile.am b/sugar/Makefile.am
index 086d291..6fad678 100644
--- a/sugar/Makefile.am
+++ b/sugar/Makefile.am
@@ -8,6 +8,7 @@ sugar_PYTHON = \
env.py \
logger.py \
setup.py \
+ TracebackUtils.py \
util.py
EXTRA_DIST = __uninstalled__.py
diff --git a/sugar/TracebackUtils.py b/sugar/TracebackUtils.py
new file mode 100644
index 0000000..3973da1
--- /dev/null
+++ b/sugar/TracebackUtils.py
@@ -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()
diff --git a/threadframe/GNUmakefile.mingw2 b/threadframe/GNUmakefile.mingw2
new file mode 100644
index 0000000..f5c0df4
--- /dev/null
+++ b/threadframe/GNUmakefile.mingw2
@@ -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
diff --git a/threadframe/README b/threadframe/README
new file mode 100644
index 0000000..ec63fa2
--- /dev/null
+++ b/threadframe/README
@@ -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.
diff --git a/threadframe/sample.txt b/threadframe/sample.txt
new file mode 100644
index 0000000..f5444d8
--- /dev/null
+++ b/threadframe/sample.txt
@@ -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)
diff --git a/threadframe/setup.py b/threadframe/setup.py
new file mode 100644
index 0000000..df8f46d
--- /dev/null
+++ b/threadframe/setup.py
@@ -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'],
+ ),
+ ],
+)
diff --git a/threadframe/test.py b/threadframe/test.py
new file mode 100644
index 0000000..6f1c82a
--- /dev/null
+++ b/threadframe/test.py
@@ -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)
diff --git a/threadframe/threadframe.def b/threadframe/threadframe.def
new file mode 100644
index 0000000..713e4b2
--- /dev/null
+++ b/threadframe/threadframe.def
@@ -0,0 +1,3 @@
+EXPORTS
+ initthreadframe
+
diff --git a/threadframe/threadframemodule.c b/threadframe/threadframemodule.c
new file mode 100644
index 0000000..2f67a45
--- /dev/null
+++ b/threadframe/threadframemodule.c
@@ -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);
+}