diff --git a/tools/show-profile-results.py b/tools/show-profile-results.py new file mode 100755 index 0000000000..9fac0ae404 --- /dev/null +++ b/tools/show-profile-results.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python +import sys +import pstats + +''' +This is a helper script to make it easy to show profile +results after using a Python decorator. It's meant to be +a simple example that you can hack on, or better yet, you +can find more advanced tools for showing profiler results. +''' + +try: + fn = sys.argv[1] +except: + print ''' + Please supply a filename. (If you use the profiled decorator, + the file will have a suffix of ".profile".) + ''' + sys.exit(1) + +p = pstats.Stats(fn) +p.strip_dirs().sort_stats('cumulative').print_stats(25) +p.strip_dirs().sort_stats('time').print_stats(25) + diff --git a/zephyr/decorator.py b/zephyr/decorator.py index 82cfa980ec..c5006a97bd 100644 --- a/zephyr/decorator.py +++ b/zephyr/decorator.py @@ -19,6 +19,7 @@ from zephyr.lib.rate_limiter import incr_ratelimit, is_ratelimited, \ from functools import wraps import base64 import logging +import cProfile class _RespondAsynchronously(object): pass @@ -416,3 +417,29 @@ def rate_limit(domain='all'): return func(request, *args, **kwargs) return wrapped_func return wrapper + +def profiled(func): + """ + This decorator should obviously be used only in a dev environment. + It works best when surrounding a function that you expect to be + called once. One strategy is to write a test case in zephyr/tests.py + and wrap the test case with the profiled decorator. + + You can run a single test case like this: + + # edit zephyr/tests.py and place @profiled above the test case below + ./tools/test-backend zephyr.RateLimitTests.test_ratelimit_decrease + + Then view the results like this: + + ./tools/show-profile-results.py test_ratelimit_decrease.profile + + """ + @wraps(func) + def wrapped_func(*args, **kwargs): + fn = func.__name__ + ".profile" + prof = cProfile.Profile() + retval = prof.runcall(func, *args, **kwargs) + prof.dump_stats(fn) + return retval + return wrapped_func diff --git a/zephyr/tests.py b/zephyr/tests.py index 99cee95899..8682da702e 100644 --- a/zephyr/tests.py +++ b/zephyr/tests.py @@ -10,7 +10,7 @@ from zephyr.models import Message, UserProfile, Stream, Recipient, Subscription, get_display_recipient, Realm, Client, \ PreregistrationUser, UserMessage from zephyr.tornadoviews import json_get_updates, api_get_messages -from zephyr.decorator import RespondAsynchronously, RequestVariableConversionError +from zephyr.decorator import RespondAsynchronously, RequestVariableConversionError, profiled from zephyr.lib.initial_password import initial_password from zephyr.lib.actions import do_send_message, gather_subscriptions, \ create_stream_if_needed, do_add_subscription