/lib/galaxy/web/framework/middleware/profile.py
https://bitbucket.org/cistrome/cistrome-harvard/ · Python · 183 lines · 139 code · 10 blank · 34 comment · 12 complexity · 30cf347faaa4a25006bee642cf2fce29 MD5 · raw file
- """
- Middleware that profiles the request with cProfile and displays profiling
- information at the bottom of each page.
- """
- import sys
- import os
- import threading
- import cgi
- import time
- from cStringIO import StringIO
- from paste import response
- try:
- # Included in Python 2.5
- import cProfile
- except:
- try:
- # Included in lsprof package for Python 2.4
- import pkg_resources
- pkg_resources.require( "lsprof" )
- import cProfile
- except:
- cProfile = None
- import pstats
- template = """
- <script>
- function show_profile_output()
- {
- var win = window.open("", "win"); // a window object
- var doc = win.document;
- doc.open("text/html", "replace");
- doc.write("<HTML><HEAD><TITLE>Profiler output</TITLE></HEAD><BODY>")
- doc.write(document.getElementById( 'profile_output' ).innerHTML)
- doc.write("</BODY></HTML>");
- doc.close();
- }
- function show_inline()
- {
- document.getElementById( 'profile_output' ).style.display="block";
- }
- </script>
- <div style="background-color: #ff9; color: #000; border: 2px solid #000; padding: 5px;">
- show profile output: <a href="javascript:show_inline();">inline</a> | <a href="javascript:show_profile_output();">new window</a>
- <div id="profile_output" style="display: none">
- <hr />
- %s
- </div>
- </div>
- """
- class ProfileMiddleware(object):
- """
- Middleware that profiles all requests.
- All HTML pages will have profiling information appended to them.
- The data is isolated to that single request, and does not include
- data from previous requests.
- """
- def __init__( self, app, global_conf=None, limit=40 ):
- self.app = app
- self.lock = threading.Lock()
- self.limit = limit
- def __call__(self, environ, start_response):
- catch_response = []
- body = []
- def replace_start_response(status, headers, exc_info=None):
- catch_response.extend([status, headers])
- start_response(status, headers, exc_info)
- return body.append
- def run_app():
- body.extend(self.app(environ, replace_start_response))
- # Run in profiler
- prof = cProfile.Profile()
- prof.runctx( "run_app()", globals(), locals() )
- # Build up body with stats
- body = ''.join(body)
- headers = catch_response[1]
- content_type = response.header_value(headers, 'content-type')
- if not content_type.startswith('text/html'):
- # We can't add info to non-HTML output
- return [body]
- stats = pstats.Stats( prof )
- stats.strip_dirs()
- stats.sort_stats( 'time', 'calls' )
- output = pstats_as_html( stats, self.limit )
- body += template % output
- return [body]
- def pstats_as_html( stats, *sel_list ):
- """
- Return an HTML representation of a pstats.Stats object.
- """
- rval = []
- # Number of function calls, primitive calls, total time
- rval.append( "<div>%d function calls (%d primitive) in %0.3f CPU seconds</div>"
- % ( stats.total_calls, stats.prim_calls, stats.total_tt ) )
- # Extract functions that match 'sel_list'
- funcs, order_message, select_message = get_func_list( stats, sel_list )
- # Deal with any ordering or selection messages
- if order_message:
- rval.append( "<div>%s</div>" % cgi.escape( order_message ) )
- if select_message:
- rval.append( "<div>%s</div>" % cgi.escape( select_message ) )
- # Build a table for the functions
- if list:
- rval.append( "<table>" )
- # Header
- rval.append( "<tr><th>ncalls</th>"
- "<th>tottime</th>"
- "<th>percall</th>"
- "<th>cumtime</th>"
- "<th>percall</th>"
- "<th>filename:lineno(function)</th></tr>" )
- for func in funcs:
- rval.append( "<tr>" )
- # Calculate each field
- cc, nc, tt, ct, callers = stats.stats[ func ]
- # ncalls
- ncalls = str(nc)
- if nc != cc:
- ncalls = ncalls + '/' + str(cc)
- rval.append( "<td>%s</td>" % cgi.escape( ncalls ) )
- # tottime
- rval.append( "<td>%0.8f</td>" % tt )
- # percall
- if nc == 0:
- percall = ""
- else:
- percall = "%0.8f" % ( tt / nc )
- rval.append( "<td>%s</td>" % cgi.escape( percall ) )
- # cumtime
- rval.append( "<td>%0.8f</td>" % ct )
- # ctpercall
- if cc == 0:
- ctpercall = ""
- else:
- ctpercall = "%0.8f" % ( ct / cc )
- rval.append( "<td>%s</td>" % cgi.escape( ctpercall ) )
- # location
- rval.append( "<td>%s</td>" % cgi.escape( func_std_string( func ) ) )
- # row complete
- rval.append( "</tr>" )
- rval.append( "</table>")
- # Concatenate result
- return "".join( rval )
- def get_func_list( stats, sel_list ):
- """
- Use 'sel_list' to select a list of functions to display.
- """
- # Determine if an ordering was applied
- if stats.fcn_list:
- list = stats.fcn_list[:]
- order_message = "Ordered by: " + stats.sort_type
- else:
- list = stats.stats.keys()
- order_message = "Random listing order was used"
- # Do the selection and accumulate messages
- select_message = ""
- for selection in sel_list:
- list, select_message = stats.eval_print_amount( selection, list, select_message )
- # Return the list of functions selected and the message
- return list, order_message, select_message
- def func_std_string( func_name ):
- """
- Match what old profile produced
- """
- if func_name[:2] == ('~', 0):
- # special case for built-in functions
- name = func_name[2]
- if name.startswith('<') and name.endswith('>'):
- return '{%s}' % name[1:-1]
- else:
- return name
- else:
- return "%s:%d(%s)" % func_name