mirror of
https://github.com/samuelclay/NewsBlur.git
synced 2025-08-05 16:58:59 +00:00
213 lines
8.8 KiB
Python
213 lines
8.8 KiB
Python
"""
|
|
runprofileserver.py
|
|
|
|
Starts a lightweight Web server with profiling enabled.
|
|
|
|
Credits for kcachegrind support taken from lsprofcalltree.py go to:
|
|
David Allouche
|
|
Jp Calderone & Itamar Shtull-Trauring
|
|
Johan Dahlin
|
|
"""
|
|
|
|
from django.core.management.base import BaseCommand, CommandError
|
|
from optparse import make_option
|
|
import os
|
|
import sys
|
|
|
|
def label(code):
|
|
if isinstance(code, str):
|
|
return ('~', 0, code) # built-in functions ('~' sorts at the end)
|
|
else:
|
|
return '%s %s:%d' % (code.co_name,
|
|
code.co_filename,
|
|
code.co_firstlineno)
|
|
|
|
class KCacheGrind(object):
|
|
def __init__(self, profiler):
|
|
self.data = profiler.getstats()
|
|
self.out_file = None
|
|
|
|
def output(self, out_file):
|
|
self.out_file = out_file
|
|
print >> out_file, 'events: Ticks'
|
|
self._print_summary()
|
|
for entry in self.data:
|
|
self._entry(entry)
|
|
|
|
def _print_summary(self):
|
|
max_cost = 0
|
|
for entry in self.data:
|
|
totaltime = int(entry.totaltime * 1000)
|
|
max_cost = max(max_cost, totaltime)
|
|
print >> self.out_file, 'summary: %d' % (max_cost,)
|
|
|
|
def _entry(self, entry):
|
|
out_file = self.out_file
|
|
|
|
code = entry.code
|
|
#print >> out_file, 'ob=%s' % (code.co_filename,)
|
|
if isinstance(code, str):
|
|
print >> out_file, 'fi=~'
|
|
else:
|
|
print >> out_file, 'fi=%s' % (code.co_filename,)
|
|
print >> out_file, 'fn=%s' % (label(code),)
|
|
|
|
inlinetime = int(entry.inlinetime * 1000)
|
|
if isinstance(code, str):
|
|
print >> out_file, '0 ', inlinetime
|
|
else:
|
|
print >> out_file, '%d %d' % (code.co_firstlineno, inlinetime)
|
|
|
|
# recursive calls are counted in entry.calls
|
|
if entry.calls:
|
|
calls = entry.calls
|
|
else:
|
|
calls = []
|
|
|
|
if isinstance(code, str):
|
|
lineno = 0
|
|
else:
|
|
lineno = code.co_firstlineno
|
|
|
|
for subentry in calls:
|
|
self._subentry(lineno, subentry)
|
|
print >> out_file
|
|
|
|
def _subentry(self, lineno, subentry):
|
|
out_file = self.out_file
|
|
code = subentry.code
|
|
#print >> out_file, 'cob=%s' % (code.co_filename,)
|
|
print >> out_file, 'cfn=%s' % (label(code),)
|
|
if isinstance(code, str):
|
|
print >> out_file, 'cfi=~'
|
|
print >> out_file, 'calls=%d 0' % (subentry.callcount,)
|
|
else:
|
|
print >> out_file, 'cfi=%s' % (code.co_filename,)
|
|
print >> out_file, 'calls=%d %d' % (
|
|
subentry.callcount, code.co_firstlineno)
|
|
|
|
totaltime = int(subentry.totaltime * 1000)
|
|
print >> out_file, '%d %d' % (lineno, totaltime)
|
|
|
|
class Command(BaseCommand):
|
|
option_list = BaseCommand.option_list + (
|
|
make_option('--noreload', action='store_false', dest='use_reloader', default=True,
|
|
help='Tells Django to NOT use the auto-reloader.'),
|
|
make_option('--adminmedia', dest='admin_media_path', default='',
|
|
help='Specifies the directory from which to serve admin media.'),
|
|
make_option('--prof-path', dest='prof_path', default='/tmp',
|
|
help='Specifies the directory which to save profile information in.'),
|
|
make_option('--nomedia', action='store_true', dest='no_media', default=False,
|
|
help='Do not profile MEDIA_URL and ADMIN_MEDIA_URL'),
|
|
make_option('--use-cprofile', action='store_true', dest='use_cprofile', default=False,
|
|
help='Use cProfile if available, this is disabled per default because of incompatibilities.'),
|
|
make_option('--kcachegrind', action='store_true', dest='use_lsprof', default=False,
|
|
help='Create kcachegrind compatible lsprof files, this requires and automatically enables cProfile.'),
|
|
)
|
|
help = "Starts a lightweight Web server with profiling enabled."
|
|
args = '[optional port number, or ipaddr:port]'
|
|
|
|
# Validation is called explicitly each time the server is reloaded.
|
|
requires_model_validation = False
|
|
|
|
def handle(self, addrport='', *args, **options):
|
|
import django
|
|
from django.core.servers.basehttp import run, AdminMediaHandler, WSGIServerException
|
|
from django.core.handlers.wsgi import WSGIHandler
|
|
if args:
|
|
raise CommandError('Usage is runserver %s' % self.args)
|
|
if not addrport:
|
|
addr = ''
|
|
port = '8000'
|
|
else:
|
|
try:
|
|
addr, port = addrport.split(':')
|
|
except ValueError:
|
|
addr, port = '', addrport
|
|
if not addr:
|
|
addr = '127.0.0.1'
|
|
|
|
if not port.isdigit():
|
|
raise CommandError("%r is not a valid port number." % port)
|
|
|
|
use_reloader = options.get('use_reloader', True)
|
|
admin_media_path = options.get('admin_media_path', '')
|
|
shutdown_message = options.get('shutdown_message', '')
|
|
no_media = options.get('no_media', False)
|
|
quit_command = (sys.platform == 'win32') and 'CTRL-BREAK' or 'CONTROL-C'
|
|
|
|
def inner_run():
|
|
from django.conf import settings
|
|
|
|
import hotshot, time, os
|
|
USE_CPROFILE = options.get('use_cprofile', False)
|
|
USE_LSPROF = options.get('use_lsprof', False)
|
|
if USE_LSPROF:
|
|
USE_CPROFILE = True
|
|
if USE_CPROFILE:
|
|
try:
|
|
import cProfile
|
|
USE_CPROFILE = True
|
|
except ImportError:
|
|
print "cProfile disabled, module cannot be imported!"
|
|
USE_CPROFILE = False
|
|
if USE_LSPROF and not USE_CPROFILE:
|
|
raise SystemExit("Kcachegrind compatible output format required cProfile from Python 2.5")
|
|
prof_path = options.get('prof_path', '/tmp')
|
|
def make_profiler_handler(inner_handler):
|
|
def handler(environ, start_response):
|
|
path_info = environ['PATH_INFO']
|
|
# normally /media/ is MEDIA_URL, but in case still check it in case it's differently
|
|
# should be hardly a penalty since it's an OR expression.
|
|
# TODO: fix this to check the configuration settings and not make assumpsions about where media are on the url
|
|
if no_media and (path_info.startswith('/media') or path_info.startswith(settings.MEDIA_URL)):
|
|
return inner_handler(environ, start_response)
|
|
path_name = path_info.strip("/").replace('/', '.') or "root"
|
|
profname = "%s.%.3f.prof" % (path_name, time.time())
|
|
profname = os.path.join(prof_path, profname)
|
|
if USE_CPROFILE:
|
|
prof = cProfile.Profile()
|
|
else:
|
|
prof = hotshot.Profile(profname)
|
|
try:
|
|
return prof.runcall(inner_handler, environ, start_response)
|
|
finally:
|
|
if USE_LSPROF:
|
|
kg = KCacheGrind(prof)
|
|
kg.output(file(profname, 'w'))
|
|
elif USE_CPROFILE:
|
|
prof.dump_stats(profname)
|
|
return handler
|
|
|
|
print "Validating models..."
|
|
self.validate(display_num_errors=True)
|
|
print "\nDjango version %s, using settings %r" % (django.get_version(), settings.SETTINGS_MODULE)
|
|
print "Development server is running at http://%s:%s/" % (addr, port)
|
|
print "Quit the server with %s." % quit_command
|
|
try:
|
|
path = admin_media_path or django.__path__[0] + '/contrib/admin/media'
|
|
handler = make_profiler_handler(AdminMediaHandler(WSGIHandler(), path))
|
|
run(addr, int(port), handler)
|
|
except WSGIServerException, e:
|
|
# Use helpful error messages instead of ugly tracebacks.
|
|
ERRORS = {
|
|
13: "You don't have permission to access that port.",
|
|
98: "That port is already in use.",
|
|
99: "That IP address can't be assigned-to.",
|
|
}
|
|
try:
|
|
error_text = ERRORS[e.args[0].args[0]]
|
|
except (AttributeError, KeyError):
|
|
error_text = str(e)
|
|
sys.stderr.write(self.style.ERROR("Error: %s" % error_text) + '\n')
|
|
# Need to use an OS exit because sys.exit doesn't work in a thread
|
|
os._exit(1)
|
|
except KeyboardInterrupt:
|
|
if shutdown_message:
|
|
print shutdown_message
|
|
sys.exit(0)
|
|
if use_reloader:
|
|
from django.utils import autoreload
|
|
autoreload.main(inner_run)
|
|
else:
|
|
inner_run()
|