forked from smeder/GreenletProfiler
/
GreenletProfiler.py
145 lines (117 loc) · 4.4 KB
/
GreenletProfiler.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
import os
import sys
import signal
import greenlet
import _vendorized_yappi
from _vendorized_yappi.yappi import (
is_running, convert2pstats, get_func_stats, get_thread_stats, clear_stats,
set_clock_type, get_clock_type, get_mem_usage)
from functools import wraps
__all__ = [
'start', 'stop', 'clear_stats', 'get_func_stats', 'get_thread_stats',
'is_running', 'get_clock_type', 'set_clock_type', 'get_mem_usage',
'convert2pstats',
]
def greenlet_profile(name, clock_type='cpu', save_type='callgrind'):
def outer(func):
@wraps(func)
def inner(*args, **kwargs):
set_clock_type(clock_type)
start()
retval = func(*args, **kwargs)
stop()
stats = get_func_stats()
stats.save(name, type=save_type)
return retval
return inner
return outer
def start(builtins=False, profile_threads=True):
"""Starts profiling all threads and all greenlets.
This function can be called from any thread at any time.
Resumes profiling if stop() was called previously.
* `builtins`: Profile builtin functions used by standart Python modules.
* `profile_threads`: Profile all threads if ``True``, else profile only the
calling thread.
"""
# TODO: what about builtins False or profile_threads False?
_vendorized_yappi.yappi.set_context_id_callback(
lambda: greenlet and id(greenlet.getcurrent()) or 0)
_vendorized_yappi.yappi.set_context_name_callback(
lambda: greenlet and greenlet.getcurrent().__class__.__name__ or '')
_vendorized_yappi.yappi.start(builtins, profile_threads)
def stop():
"""Stops the currently running yappi instance.
The same profiling session can be resumed later by calling start().
"""
_vendorized_yappi.yappi.stop()
_vendorized_yappi.yappi.set_context_id_callback(None)
def main():
from optparse import OptionParser
import atexit
usage = "python -m greenlet_profiler [-b] [-s] [scriptfile] args ..."
parser = OptionParser(usage=usage)
parser.allow_interspersed_args = False
parser.add_option(
"-b", "--builtins",
action="store_true", dest="profile_builtins",
default=False,
help="Profiles builtin functions when set. [default: False]")
parser.add_option(
"-s", "--single_thread",
action="store_true", dest="profile_single_thread",
default=False,
help="Profiles only the thread that calls start(). [default: False]")
parser.add_option(
"-l", "--log-file",
dest="log_file",
default=None,
help="Log file name")
parser.add_option(
"-u", "--signals",
action="store_true", dest="use_signals",
default=False,
help="Uses the USR1 (start) and USR2 (stop) signals to control when profiling happens. [default: False]")
clock_types = ['wall', 'cpu']
parser.add_option(
"-c", "--clock_type",
dest="clock_type",
type='choice',
choices=clock_types,
default='cpu',
help="One of %s" % clock_types)
options, args = parser.parse_args()
if len(args) > 0:
sys.path.insert(0, os.path.dirname(args[0]))
set_clock_type(options.clock_type)
if options.use_signals:
def signal_start(signal_number, frame):
start(options.profile_builtins, not options.profile_single_thread)
signal.signal(signal.SIGUSR1, signal_start)
def signal_stop(signal_number, frame):
cleanup(options)
signal.signal(signal.SIGUSR2, signal_stop)
else:
start(options.profile_builtins, not options.profile_single_thread)
atexit.register(cleanup, options)
if sys.version_info >= (3, 0):
exec (compile(open(args[0]).read(), args[0], 'exec'),
sys._getframe(1).f_globals, sys._getframe(1).f_locals)
else:
sys.argv = args
#execfile(args[0], {}, {})
execfile(args[0], sys._getframe(1).f_globals,
sys._getframe(1).f_locals)
else:
parser.print_usage()
sys.exit(2)
def cleanup(options):
stop()
stats = get_func_stats()
if options.log_file:
stats.save(options.log_file, type='callgrind')
else:
stats.print_all()
get_thread_stats().print_all()
clear_stats()
if __name__ == "__main__":
main()