dumper.py 3.51 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
# (C) Copyright 2005 Nuxeo SARL <http://nuxeo.com>
# Author: Florent Guillaume <fg@nuxeo.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 as published
# by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
# 02111-1307, USA.
#
# $Id: dumper.py,v 1.1 2005/02/23 15:35:21 fguillaume Exp $

"""Debug running threads

ZServer hook to dump a traceback of the running python threads.
"""

import thread
26
from sys import _current_frames
27 28 29 30
import traceback
import time
from cStringIO import StringIO

31 32
from zLOG import LOG, DEBUG, ERROR
from App.config import getConfiguration
33 34 35 36 37 38 39 40 41

def dump_threads():
    """Dump running threads

    Returns a string with the tracebacks.
    """
    this_thread_id = thread.get_ident()
    now = time.strftime("%Y-%m-%d %H:%M:%S")
    res = ["Threads traceback dump at %s\n" % now]
42
    for thread_id, frame in _current_frames().iteritems():
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
        if thread_id == this_thread_id:
            continue

        # Find request in frame
        reqinfo = ''
        f = frame
        while f is not None:
            co = f.f_code
            if (co.co_name == 'publish' and
                co.co_filename.endswith('/ZPublisher/Publish.py')):
                request = f.f_locals.get('request')
                if request is not None:
                    reqinfo = (request.get('REQUEST_METHOD', '') + ' ' +
                               request.get('PATH_INFO', ''))
                    qs = request.get('QUERY_STRING')
                    if qs:
                        reqinfo += '?'+qs
                break
            f = f.f_back
        if reqinfo:
            reqinfo = " (%s)" % reqinfo

65
        mysql_info = ''
66 67 68 69 70 71
        f = frame
        try:
          from Products.ZMySQLDA.db import DB
          while f is not None:
            code = f.f_code
            if code is DB._query.func_code:
72
              mysql_info = "\nMySQL query:\n%s\n" % f.f_locals['query']
73
              break
74 75 76 77
            f = f.f_back
        except ImportError:
          pass

78 79
        output = StringIO()
        traceback.print_stack(frame, file=output)
80
        res.append("Thread %s%s:\n%s%s" %
81
            (thread_id, reqinfo, output.getvalue(), mysql_info))
82

83
    res.append("End of dump\n")
84 85 86 87
    result = '\n'.join(res)
    if isinstance(result, unicode):
      result = result.encode('utf-8')
    return result
88

89 90 91 92 93 94 95 96 97
config = getConfiguration()
deadlockdebugger = config.product_config.get('deadlockdebugger')
dump_url = ''
secret = ''
dump_url = deadlockdebugger['dump_url']
secret = deadlockdebugger.get('secret', '')

if dump_url and secret:
    dump_url += '?'+secret
98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118

def match(self, request):
    uri = request.uri

    # added hook
    if uri == dump_url:
        dump = dump_threads()
        request.channel.push('HTTP/1.0 200 OK\nContent-Type: text/plain\n\n')
        request.channel.push(dump)
        request.channel.close_when_done()
        LOG('DeadlockDebugger', DEBUG, dump)
        return 0
    # end hook

    if self.uri_regex.match(uri):
        return 1
    else:
        return 0

from ZServer.HTTPServer import zhttp_handler
zhttp_handler.match = match