root/galaxy-central/lib/galaxy/web/framework/middleware/profile.py

リビジョン 2, 5.9 KB (コミッタ: hatakeyama, 14 年 前)

import galaxy-central

行番号 
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
Note: リポジトリブラウザについてのヘルプは TracBrowser を参照してください。