PageRenderTime 37ms CodeModel.GetById 20ms app.highlight 13ms RepoModel.GetById 2ms app.codeStats 0ms

/lib/galaxy/web/framework/middleware/profile.py

https://bitbucket.org/cistrome/cistrome-harvard/
Python | 183 lines | 173 code | 4 blank | 6 comment | 1 complexity | 30cf347faaa4a25006bee642cf2fce29 MD5 | raw file
  1"""
  2Middleware that profiles the request with cProfile and displays profiling
  3information at the bottom of each page.
  4"""
  5
  6import sys
  7import os
  8import threading
  9import cgi
 10import time
 11from cStringIO import StringIO
 12from paste import response
 13
 14try:
 15    # Included in Python 2.5
 16    import cProfile
 17except:
 18    try:
 19        # Included in lsprof package for Python 2.4
 20        import pkg_resources
 21        pkg_resources.require( "lsprof" )
 22        import cProfile
 23    except:
 24        cProfile = None
 25
 26import pstats
 27
 28template = """
 29<script>
 30function show_profile_output()
 31{
 32var win = window.open("", "win"); // a window object
 33var doc = win.document;
 34doc.open("text/html", "replace");
 35doc.write("<HTML><HEAD><TITLE>Profiler output</TITLE></HEAD><BODY>")
 36doc.write(document.getElementById( 'profile_output' ).innerHTML)
 37doc.write("</BODY></HTML>");
 38doc.close();
 39}
 40function show_inline()
 41{
 42document.getElementById( 'profile_output' ).style.display="block";
 43}
 44</script>
 45<div style="background-color: #ff9; color: #000; border: 2px solid #000; padding: 5px;">
 46show profile output: <a href="javascript:show_inline();">inline</a> | <a href="javascript:show_profile_output();">new window</a>
 47<div id="profile_output" style="display: none">
 48<hr />
 49%s
 50</div>
 51</div>
 52"""
 53
 54class ProfileMiddleware(object):
 55
 56    """
 57    Middleware that profiles all requests.
 58
 59    All HTML pages will have profiling information appended to them.
 60    The data is isolated to that single request, and does not include
 61    data from previous requests.
 62    """
 63
 64    def __init__( self, app, global_conf=None, limit=40 ):
 65        self.app = app
 66        self.lock = threading.Lock()
 67        self.limit = limit
 68
 69    def __call__(self, environ, start_response):
 70        catch_response = []
 71        body = []
 72        def replace_start_response(status, headers, exc_info=None):
 73            catch_response.extend([status, headers])
 74            start_response(status, headers, exc_info)
 75            return body.append
 76        def run_app():
 77            body.extend(self.app(environ, replace_start_response))
 78        # Run in profiler
 79        prof = cProfile.Profile()
 80        prof.runctx( "run_app()", globals(), locals() )
 81        # Build up body with stats
 82        body = ''.join(body)
 83        headers = catch_response[1]
 84        content_type = response.header_value(headers, 'content-type')
 85        if not content_type.startswith('text/html'):
 86            # We can't add info to non-HTML output
 87            return [body]
 88        stats = pstats.Stats( prof )
 89        stats.strip_dirs()
 90        stats.sort_stats( 'time', 'calls' )
 91        output = pstats_as_html( stats, self.limit )
 92        body += template % output
 93        return [body]
 94
 95def pstats_as_html( stats, *sel_list ):
 96    """
 97    Return an HTML representation of a pstats.Stats object.
 98    """
 99    rval = []
100    # Number of function calls, primitive calls, total time
101    rval.append( "<div>%d function calls (%d primitive) in %0.3f CPU seconds</div>"
102            % ( stats.total_calls, stats.prim_calls, stats.total_tt ) )
103    # Extract functions that match 'sel_list'
104    funcs, order_message, select_message = get_func_list( stats, sel_list )
105    # Deal with any ordering or selection messages
106    if order_message:
107        rval.append( "<div>%s</div>" % cgi.escape( order_message ) )
108    if select_message:
109        rval.append( "<div>%s</div>" % cgi.escape( select_message ) )
110    # Build a table for the functions
111    if list:
112        rval.append( "<table>" )
113        # Header
114        rval.append( "<tr><th>ncalls</th>"
115                     "<th>tottime</th>"
116                     "<th>percall</th>"
117                     "<th>cumtime</th>"
118                     "<th>percall</th>"
119                     "<th>filename:lineno(function)</th></tr>" )
120        for func in funcs:
121            rval.append( "<tr>" )
122            # Calculate each field
123            cc, nc, tt, ct, callers = stats.stats[ func ]
124            # ncalls
125            ncalls = str(nc)
126            if nc != cc:
127                ncalls = ncalls + '/' + str(cc)
128            rval.append( "<td>%s</td>" % cgi.escape( ncalls ) )
129            # tottime
130            rval.append( "<td>%0.8f</td>" %  tt )
131            # percall
132            if nc == 0:
133                percall = ""
134            else:
135                percall = "%0.8f" %  ( tt / nc )
136            rval.append( "<td>%s</td>" % cgi.escape( percall ) )
137            # cumtime
138            rval.append( "<td>%0.8f</td>" % ct )
139            # ctpercall
140            if cc == 0:
141                ctpercall = ""
142            else:
143                ctpercall = "%0.8f" % ( ct / cc )
144            rval.append( "<td>%s</td>" % cgi.escape( ctpercall ) )
145            # location
146            rval.append( "<td>%s</td>" % cgi.escape( func_std_string( func ) ) )
147            # row complete
148            rval.append( "</tr>" )
149        rval.append( "</table>")
150        # Concatenate result
151        return "".join( rval )
152
153def get_func_list( stats, sel_list ):
154    """
155    Use 'sel_list' to select a list of functions to display.
156    """
157    # Determine if an ordering was applied
158    if stats.fcn_list:
159        list = stats.fcn_list[:]
160        order_message = "Ordered by: " + stats.sort_type
161    else:
162        list = stats.stats.keys()
163        order_message = "Random listing order was used"
164    # Do the selection and accumulate messages
165    select_message = ""
166    for selection in sel_list:
167        list, select_message = stats.eval_print_amount( selection, list, select_message )
168    # Return the list of functions selected and the message
169    return list, order_message, select_message
170
171def func_std_string( func_name ):
172    """
173    Match what old profile produced
174    """
175    if func_name[:2] == ('~', 0):
176        # special case for built-in functions
177        name = func_name[2]
178        if name.startswith('<') and name.endswith('>'):
179            return '{%s}' % name[1:-1]
180        else:
181            return name
182    else:
183        return "%s:%d(%s)" % func_name