Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

okay, real first commit

  • Loading branch information...
commit da7d1915145aa81f928a36da6261102e770c5d3d 1 parent 154f874
authored September 09, 2010
42  attach_test.py
... ...
@@ -0,0 +1,42 @@
  1
+import sys
  2
+
  3
+sys.path = ['.'] + sys.path
  4
+
  5
+import time
  6
+import gevent
  7
+import gevent_profiler
  8
+from gevent import monkey
  9
+
  10
+def eat_up_cpu():
  11
+	for x in range(100):
  12
+		for y in range(100):
  13
+			z = x * y
  14
+
  15
+def eat_up_some_more_cpu():
  16
+	for x in range(100):
  17
+		for y in range(100):
  18
+			z = x * y
  19
+
  20
+def task():
  21
+	time.sleep(3)
  22
+	eat_up_cpu()
  23
+	eat_up_some_more_cpu()
  24
+	print "hi!"
  25
+
  26
+def main():
  27
+	monkey.patch_all()
  28
+
  29
+	tasks = []
  30
+
  31
+	gevent_profiler.attach_on_signal(duration=5)
  32
+
  33
+	while True:
  34
+		for x in range(3):
  35
+			y = gevent.spawn(task)
  36
+			tasks.append(y)
  37
+		
  38
+		gevent.joinall(tasks)
  39
+
  40
+if __name__ == "__main__":
  41
+	main()
  42
+
260  gevent_profiler/__init__.py
... ...
@@ -0,0 +1,260 @@
  1
+"""
  2
+Profiler for gevent
  3
+"""
  4
+import os
  5
+import sys
  6
+import time
  7
+import gevent
  8
+import signal
  9
+
  10
+_gls = {}
  11
+_curr_gl = None
  12
+_states = {}
  13
+_curr_states = {}
  14
+
  15
+_summary_output_file = sys.stdout
  16
+_trace_output_file = sys.stdout
  17
+
  18
+_time_blocking = False
  19
+
  20
+_attach_expiration = None
  21
+_attach_duration = 60
  22
+
  23
+class _State:
  24
+	def __init__(self):
  25
+		self.modulename = None
  26
+		self.co_name = None
  27
+		self.start_time = None
  28
+		self.elapsed = 0.0
  29
+		self.depth = 0
  30
+		self.calls = []
  31
+		self.parent = None
  32
+
  33
+def _modname(path):
  34
+    """Return a plausible module name for the path."""
  35
+
  36
+    base = os.path.basename(path)
  37
+    filename, ext = os.path.splitext(base)
  38
+    return filename
  39
+
  40
+def _globaltrace(frame, event, arg):
  41
+	global _curr_gl
  42
+
  43
+	if _attach_expiration is not None and time.time() > _attach_expiration:
  44
+		detach()
  45
+		return
  46
+
  47
+	gl = gevent.greenlet.getcurrent()
  48
+	if gl not in _states:
  49
+		_states[gl] = _State()
  50
+		_curr_states[gl] = _states[gl]
  51
+
  52
+	if _curr_gl is not gl:
  53
+		if _curr_gl is not None:
  54
+			_stop_timing(_curr_gl)
  55
+		_curr_gl = gl
  56
+		_start_timing(_curr_gl)
  57
+
  58
+	code = frame.f_code
  59
+	filename = code.co_filename
  60
+	if filename:
  61
+		modulename = _modname(filename)
  62
+		if modulename is not None:
  63
+			_trace_output_file.write("[%s] call: %s: %s\n" % (gl, modulename, code.co_name))
  64
+	state = _State()
  65
+	_curr_states[gl].calls.append(state)
  66
+	state.parent = _curr_states[gl]
  67
+	_curr_states[gl] = state
  68
+
  69
+	state.modulename = modulename
  70
+	state.co_name = code.co_name
  71
+	state.start_time = time.time()
  72
+
  73
+	tracefunc = _getlocaltrace(state)
  74
+	state.localtracefunc = tracefunc
  75
+
  76
+	if modulename == 'hub' and code.co_name == 'switch' and not _time_blocking:
  77
+		_stop_timing(gl)
  78
+
  79
+	return tracefunc
  80
+
  81
+def _getlocaltrace(state):
  82
+	return lambda f, e, a: _localtrace(state, f, e, a)
  83
+
  84
+def _localtrace(state, frame, event, arg):
  85
+	if _attach_expiration is not None and time.time() > _attach_expiration:
  86
+		detach()
  87
+		return
  88
+
  89
+	gl = gevent.greenlet.getcurrent()
  90
+	code = frame.f_code
  91
+	filename = code.co_filename
  92
+	modulename = None
  93
+	if filename:
  94
+		modulename = _modname(filename)
  95
+	if event == 'return':
  96
+		if modulename is not None:
  97
+			_trace_output_file.write("[%s] return: %s: %s: %s\n" % (gl, modulename, code.co_name, code.co_firstlineno))
  98
+		if state.start_time is not None:
  99
+			state.elapsed += time.time() - state.start_time
  100
+		assert _curr_states[gl].parent is not None
  101
+		_curr_states[gl] = _curr_states[gl].parent
  102
+		return None
  103
+	
  104
+	return state.localtracefunc
  105
+
  106
+def _stop_timing(gl):
  107
+
  108
+	def _stop_timing_r(state):
  109
+		if state.start_time is not None:
  110
+			state.elapsed += time.time() - state.start_time
  111
+			state.start_time = None
  112
+		if state.parent is not None:
  113
+			_stop_timing_r(state.parent)
  114
+
  115
+	if gl not in _curr_states:
  116
+		#if we're reattaching later, it's possible to call stop_timing
  117
+		#without a full set of current state
  118
+		return
  119
+	curr_state = _curr_states[gl]
  120
+	_stop_timing_r(curr_state)
  121
+
  122
+def _start_timing(gl):
  123
+
  124
+	def _start_timing_r(state):
  125
+		state.start_time = time.time()
  126
+		if state.parent is not None:
  127
+			_start_timing_r(state.parent)
  128
+
  129
+	if gl not in _curr_states:
  130
+		#if we're reattaching later, it's possible to call start_timing
  131
+		#without a full set of current state
  132
+		return
  133
+	curr_state = _curr_states[gl]
  134
+	_start_timing_r(curr_state)
  135
+
  136
+
  137
+def _print_state(state, depth=0):
  138
+	_summary_output_file.write("%s %s.%s %f\n" % ("."*depth, state.modulename, state.co_name, state.elapsed))
  139
+	for call in state.calls:
  140
+		_print_state(call, depth+2)
  141
+
  142
+def _print_output():
  143
+	for gl in _states.keys():
  144
+		_summary_output_file.write("%s\n" % gl)
  145
+		_print_state(_states[gl])
  146
+		_summary_output_file.write("\n")
  147
+	_summary_output_file.flush()
  148
+
  149
+def attach():
  150
+	"""
  151
+	Start execution tracing
  152
+	"""
  153
+	global _attach_expiration
  154
+	global _attach_duration
  155
+	if _attach_expiration is not None:
  156
+		return
  157
+	_attach_expiration = time.time() + _attach_duration
  158
+	sys.settrace(_globaltrace)
  159
+
  160
+def detach():
  161
+	"""
  162
+	Finish execution tracing, print the results and reset internal state
  163
+	"""
  164
+	global _gls
  165
+	global current_gl
  166
+	global _states
  167
+	global _curr_states
  168
+	global _attach_expiration
  169
+
  170
+	_attach_expiration = None
  171
+	sys.settrace(None)
  172
+	_trace_output_file.flush()
  173
+	_print_output()
  174
+	_gls = {}
  175
+	_curr_gl = None
  176
+	_states = {}
  177
+	_curr_states = {}
  178
+	curr_state = None
  179
+
  180
+def profile(func, args=[], kwargs={}):
  181
+	"""
  182
+	Takes a function and the arguments to pass to that function and runs it
  183
+	with profiling enabled.  On completion of that function, the profiling 
  184
+	results are printed.
  185
+	"""
  186
+	sys.settrace(_globaltrace)
  187
+	func(*args, **kwargs)
  188
+	sys.settrace(None)
  189
+	_trace_output_file.flush()
  190
+	_print_output()
  191
+
  192
+def set_summary_output(f):
  193
+	"""
  194
+	Takes a filename and will write the execution summary there
  195
+	"""
  196
+	global _summary_output_file
  197
+	_summary_output_file = open(f, 'w')
  198
+
  199
+def set_trace_output(f):
  200
+	"""
  201
+	Takes a filename and writes the execution trace information there
  202
+	"""
  203
+	global _trace_output_file
  204
+	_trace_output_file = open(f, 'w')
  205
+
  206
+def time_blocking(enabled=True):
  207
+	"""
  208
+	Pass True if you want to count time blocking on IO towards the execution
  209
+	totals for each function.  The default setting for this is False, which
  210
+	is probably what you're looking for in most cases.
  211
+	"""
  212
+	_time_blocking = enabled
  213
+
  214
+def _sighandler(signum, frame):
  215
+	attach()
  216
+
  217
+def attach_on_signal(signum=signal.SIGUSR1, duration=60):
  218
+	"""
  219
+	Sets up signal handlers so that, upon receiving the specified signal,
  220
+	the process starts outputting a full execution trace.  At the expiration
  221
+	of the specified duration, a summary of all the greenlet activity during
  222
+	that period is output.
  223
+	See set_summary_output and set_trace_output for information about how
  224
+	to configure where the output goes.
  225
+	By default, the signal is SIGUSR1.
  226
+	"""
  227
+	signal.signal(signum, _sighandler)
  228
+	global _attach_duration
  229
+	_attach_duration = duration
  230
+
  231
+if __name__ == "__main__":
  232
+	from optparse import OptionParser
  233
+	parser = OptionParser()
  234
+	parser.add_option("-s", "--summary", dest="summary",
  235
+			help="write the summary to a file",
  236
+			metavar="SUMMARY_FILE")
  237
+	parser.add_option("-t", "--trace", dest="trace",
  238
+			help="write the trace to a file",
  239
+			metavar="TRACE_FILE")
  240
+	parser.add_option("-b", "--blocking", dest="blocking",
  241
+			action='store_false',
  242
+			help="count blocked time toward execution totals")
  243
+	(options, args) = parser.parse_args()
  244
+	if options.summary is not None:
  245
+		set_summary_output(options.summary)
  246
+	if options.trace is not None:
  247
+		set_trace_output(options.trace)
  248
+	if options.blocking is not None:
  249
+		time_blocking()
  250
+	if len(args) < 1:
  251
+		print "what file should i be profiling?"
  252
+		sys.exit(1)
  253
+	file = args[0]
  254
+
  255
+	sys.settrace(_globaltrace)
  256
+	f = open(file, 'r')
  257
+	exec f
  258
+	
  259
+	sys.settrace(None)
  260
+	_print_output()
BIN  gevent_profiler/__init__.pyc
Binary file not shown
19  setup.py
... ...
@@ -0,0 +1,19 @@
  1
+from distutils.core import setup, Extension
  2
+
  3
+long_description = """Gevent profiler"""
  4
+
  5
+py_modules = [
  6
+		"gevent_profiler.__init__"
  7
+		#"gevent_profiler.gevent_profiler"
  8
+		]
  9
+
  10
+setup(name = 'python-gevent-profiler',
  11
+	  version='0.2',
  12
+	  description='profiling utilities for gevent',
  13
+	  long_description = long_description,
  14
+	  author='meebo',
  15
+	  author_email='server@meebo.com',
  16
+	  url='http://random.meebo.com',
  17
+	  py_modules = py_modules
  18
+)
  19
+

0 notes on commit da7d191

Please sign in to comment.
Something went wrong with that request. Please try again.