# (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
from sys import _current_frames
import traceback
import time
from cStringIO import StringIO

from zLOG import LOG, DEBUG, ERROR
from App.config import getConfiguration

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]
    for thread_id, frame in _current_frames().iteritems():
        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

        mysql_info = ''
        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:
              mysql_info = "\nMySQL query - %s:\n%s\n" % (
                getattr(f.f_locals['self'], "_start_time", ""),
                f.f_locals['query'])
              break
            f = f.f_back
        except ImportError:
          pass

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

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

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

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