123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293 |
- # -*- coding: utf-8 -*-
- """
- 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
- """
- import sys
- from datetime import datetime
- from django.conf import settings
- from django.contrib.staticfiles.handlers import StaticFilesHandler
- from django.core.management.base import BaseCommand, CommandError
- from django.core.servers.basehttp import get_internal_wsgi_application
- from django_extensions.management.utils import signalcommand
- USE_STATICFILES = 'django.contrib.staticfiles' in settings.INSTALLED_APPS
- class KCacheGrind:
- def __init__(self, profiler):
- self.data = profiler.getstats()
- self.out_file = None
- def output(self, out_file):
- self.out_file = out_file
- self.out_file.write('events: Ticks\n')
- 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)
- self.out_file.write('summary: %d\n' % (max_cost,))
- def _entry(self, entry):
- out_file = self.out_file
- code = entry.code
- if isinstance(code, str):
- out_file.write('fn=%s\n' % code)
- else:
- out_file.write('fl=%s\n' % code.co_filename)
- out_file.write('fn=%s\n' % code.co_name)
- inlinetime = int(entry.inlinetime * 1000)
- if isinstance(code, str):
- out_file.write('0 %s\n' % inlinetime)
- else:
- out_file.write('%d %d\n' % (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)
- out_file.write("\n")
- def _subentry(self, lineno, subentry):
- out_file = self.out_file
- code = subentry.code
- if isinstance(code, str):
- out_file.write('cfn=%s\n' % code)
- out_file.write('calls=%d 0\n' % (subentry.callcount,))
- else:
- out_file.write('cfl=%s\n' % code.co_filename)
- out_file.write('cfn=%s\n' % code.co_name)
- out_file.write('calls=%d %d\n' % (subentry.callcount, code.co_firstlineno))
- totaltime = int(subentry.totaltime * 1000)
- out_file.write('%d %d\n' % (lineno, totaltime))
- class Command(BaseCommand):
- help = "Starts a lightweight Web server with profiling enabled."
- args = '[optional port number, or ipaddr:port]'
- def add_arguments(self, parser):
- super().add_arguments(parser)
- parser.add_argument(
- 'addrport', nargs='?',
- help='Optional port number, or ipaddr:port'
- )
- parser.add_argument(
- '--noreload', action='store_false', dest='use_reloader',
- default=True,
- help='Tells Django to NOT use the auto-reloader.')
- parser.add_argument(
- '--nothreading', action='store_false', dest='use_threading', default=True,
- help='Tells Django to NOT use threading.',
- )
- parser.add_argument(
- '--prof-path', dest='prof_path', default='/tmp',
- help='Specifies the directory which to save profile information '
- 'in.'
- )
- parser.add_argument(
- '--prof-file', dest='prof_file',
- default='{path}.{duration:06d}ms.{time}',
- help='Set filename format, default if '
- '"{path}.{duration:06d}ms.{time}".'
- )
- parser.add_argument(
- '--nomedia', action='store_true', dest='no_media', default=False,
- help='Do not profile MEDIA_URL'
- )
- parser.add_argument(
- '--use-cprofile', action='store_true', dest='use_cprofile',
- default=False,
- help='Use cProfile if available, this is disabled per default '
- 'because of incompatibilities.'
- )
- parser.add_argument(
- '--kcachegrind', action='store_true', dest='use_lsprof',
- default=False,
- help='Create kcachegrind compatible lsprof files, this requires '
- 'and automatically enables cProfile.'
- )
- if USE_STATICFILES:
- parser.add_argument(
- '--nostatic', action="store_false", dest='use_static_handler',
- default=True,
- help='Tells Django to NOT automatically serve static files '
- 'at STATIC_URL.')
- parser.add_argument(
- '--insecure', action="store_true", dest='insecure_serving',
- default=False,
- help='Allows serving static files even if DEBUG is False.')
- @signalcommand
- def handle(self, addrport='', *args, **options):
- import django
- import socket
- import errno
- from django.core.servers.basehttp import run
- 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['use_reloader']
- shutdown_message = options.get('shutdown_message', '')
- no_media = options['no_media']
- quit_command = (sys.platform == 'win32') and 'CTRL-BREAK' or 'CONTROL-C'
- def inner_run():
- import os
- import time
- try:
- import hotshot
- HAS_HOTSHOT = True
- except ImportError:
- HAS_HOTSHOT = False # python 3.x
- USE_CPROFILE = options['use_cprofile']
- USE_LSPROF = options['use_lsprof']
- 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 CommandError("Kcachegrind compatible output format required cProfile from Python 2.5")
- if not HAS_HOTSHOT and not USE_CPROFILE:
- raise CommandError("Hotshot profile library not found. (and not using cProfile)")
- prof_path = options['prof_path']
- prof_file = options['prof_file']
- if not prof_file.format(path='1', duration=2, time=3):
- prof_file = '{path}.{duration:06d}ms.{time}'
- print("Filename format is wrong. Default format used: '{path}.{duration:06d}ms.{time}'.")
- def get_exclude_paths():
- exclude_paths = []
- media_url = getattr(settings, 'MEDIA_URL', None)
- if media_url:
- exclude_paths.append(media_url)
- static_url = getattr(settings, 'STATIC_URL', None)
- if static_url:
- exclude_paths.append(static_url)
- return exclude_paths
- def make_profiler_handler(inner_handler):
- def handler(environ, start_response):
- path_info = environ['PATH_INFO']
- # when using something like a dynamic site middleware is could be necessary
- # to refetch the exclude_paths every time since they could change per site.
- if no_media and any(path_info.startswith(p) for p in get_exclude_paths()):
- return inner_handler(environ, start_response)
- path_name = path_info.strip("/").replace('/', '.') or "root"
- profname = "%s.%d.prof" % (path_name, time.time())
- profname = os.path.join(prof_path, profname)
- if USE_CPROFILE:
- prof = cProfile.Profile()
- else:
- prof = hotshot.Profile(profname)
- start = datetime.now()
- try:
- return prof.runcall(inner_handler, environ, start_response)
- finally:
- # seeing how long the request took is important!
- elap = datetime.now() - start
- elapms = elap.seconds * 1000.0 + elap.microseconds / 1000.0
- if USE_LSPROF:
- kg = KCacheGrind(prof)
- with open(profname, 'w') as f:
- kg.output(f)
- elif USE_CPROFILE:
- prof.dump_stats(profname)
- profname2 = prof_file.format(path=path_name, duration=int(elapms), time=int(time.time()))
- profname2 = os.path.join(prof_path, "%s.prof" % profname2)
- if not USE_CPROFILE:
- prof.close()
- os.rename(profname, profname2)
- return handler
- print("Validating models...")
- if hasattr(self, 'check'):
- self.check(display_num_errors=True)
- else:
- 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:
- handler = get_internal_wsgi_application()
- if USE_STATICFILES:
- use_static_handler = options['use_static_handler']
- insecure_serving = options['insecure_serving']
- if use_static_handler and (settings.DEBUG or insecure_serving):
- handler = StaticFilesHandler(handler)
- handler = make_profiler_handler(handler)
- run(addr, int(port), handler, threading=options['use_threading'])
- except socket.error as e:
- # Use helpful error messages instead of ugly tracebacks.
- ERRORS = {
- errno.EACCES: "You don't have permission to access that port.",
- errno.EADDRINUSE: "That port is already in use.",
- errno.EADDRNOTAVAIL: "That IP address can't be assigned-to.",
- }
- try:
- error_text = ERRORS[e.errno]
- 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:
- try:
- from django.utils.autoreload import run_with_reloader
- run_with_reloader(inner_run)
- except ImportError:
- from django.utils import autoreload
- autoreload.main(inner_run)
- else:
- inner_run()
|