/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

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