web/lib/django_extensions/management/commands/runprofileserver.py
changeset 3 526ebd3988b0
equal deleted inserted replaced
1:ebaad720f88b 3:526ebd3988b0
       
     1 """
       
     2 runprofileserver.py
       
     3 
       
     4     Starts a lightweight Web server with profiling enabled.
       
     5 
       
     6 Credits for kcachegrind support taken from lsprofcalltree.py go to:
       
     7  David Allouche
       
     8  Jp Calderone & Itamar Shtull-Trauring
       
     9  Johan Dahlin
       
    10 """
       
    11 
       
    12 from django.core.management.base import BaseCommand, CommandError
       
    13 from optparse import make_option
       
    14 import os
       
    15 import sys
       
    16 
       
    17 def label(code):
       
    18     if isinstance(code, str):
       
    19         return ('~', 0, code)    # built-in functions ('~' sorts at the end)
       
    20     else:
       
    21         return '%s %s:%d' % (code.co_name,
       
    22                              code.co_filename,
       
    23                              code.co_firstlineno)
       
    24 
       
    25 class KCacheGrind(object):
       
    26     def __init__(self, profiler):
       
    27         self.data = profiler.getstats()
       
    28         self.out_file = None
       
    29 
       
    30     def output(self, out_file):
       
    31         self.out_file = out_file
       
    32         print >> out_file, 'events: Ticks'
       
    33         self._print_summary()
       
    34         for entry in self.data:
       
    35             self._entry(entry)
       
    36 
       
    37     def _print_summary(self):
       
    38         max_cost = 0
       
    39         for entry in self.data:
       
    40             totaltime = int(entry.totaltime * 1000)
       
    41             max_cost = max(max_cost, totaltime)
       
    42         print >> self.out_file, 'summary: %d' % (max_cost,)
       
    43 
       
    44     def _entry(self, entry):
       
    45         out_file = self.out_file
       
    46 
       
    47         code = entry.code
       
    48         #print >> out_file, 'ob=%s' % (code.co_filename,)
       
    49         if isinstance(code, str):
       
    50             print >> out_file, 'fi=~'
       
    51         else:
       
    52             print >> out_file, 'fi=%s' % (code.co_filename,)
       
    53         print >> out_file, 'fn=%s' % (label(code),)
       
    54 
       
    55         inlinetime = int(entry.inlinetime * 1000)
       
    56         if isinstance(code, str):
       
    57             print >> out_file, '0 ', inlinetime
       
    58         else:
       
    59             print >> out_file, '%d %d' % (code.co_firstlineno, inlinetime)
       
    60 
       
    61         # recursive calls are counted in entry.calls
       
    62         if entry.calls:
       
    63             calls = entry.calls
       
    64         else:
       
    65             calls = []
       
    66 
       
    67         if isinstance(code, str):
       
    68             lineno = 0
       
    69         else:
       
    70             lineno = code.co_firstlineno
       
    71 
       
    72         for subentry in calls:
       
    73             self._subentry(lineno, subentry)
       
    74         print >> out_file
       
    75 
       
    76     def _subentry(self, lineno, subentry):
       
    77         out_file = self.out_file
       
    78         code = subentry.code
       
    79         #print >> out_file, 'cob=%s' % (code.co_filename,)
       
    80         print >> out_file, 'cfn=%s' % (label(code),)
       
    81         if isinstance(code, str):
       
    82             print >> out_file, 'cfi=~'
       
    83             print >> out_file, 'calls=%d 0' % (subentry.callcount,)
       
    84         else:
       
    85             print >> out_file, 'cfi=%s' % (code.co_filename,)
       
    86             print >> out_file, 'calls=%d %d' % (
       
    87                 subentry.callcount, code.co_firstlineno)
       
    88 
       
    89         totaltime = int(subentry.totaltime * 1000)
       
    90         print >> out_file, '%d %d' % (lineno, totaltime)
       
    91 
       
    92 class Command(BaseCommand):
       
    93     option_list = BaseCommand.option_list + (
       
    94         make_option('--noreload', action='store_false', dest='use_reloader', default=True,
       
    95             help='Tells Django to NOT use the auto-reloader.'),
       
    96         make_option('--adminmedia', dest='admin_media_path', default='',
       
    97             help='Specifies the directory from which to serve admin media.'),
       
    98         make_option('--prof-path', dest='prof_path', default='/tmp',
       
    99             help='Specifies the directory which to save profile information in.'),
       
   100         make_option('--nomedia', action='store_true', dest='no_media', default=False,
       
   101             help='Do not profile MEDIA_URL and ADMIN_MEDIA_URL'),
       
   102         make_option('--use-cprofile', action='store_true', dest='use_cprofile', default=False,
       
   103             help='Use cProfile if available, this is disabled per default because of incompatibilities.'),
       
   104         make_option('--kcachegrind', action='store_true', dest='use_lsprof', default=False,
       
   105             help='Create kcachegrind compatible lsprof files, this requires and automatically enables cProfile.'),
       
   106     )
       
   107     help = "Starts a lightweight Web server with profiling enabled."
       
   108     args = '[optional port number, or ipaddr:port]'
       
   109 
       
   110     # Validation is called explicitly each time the server is reloaded.
       
   111     requires_model_validation = False
       
   112 
       
   113     def handle(self, addrport='', *args, **options):
       
   114         import django
       
   115         from django.core.servers.basehttp import run, AdminMediaHandler, WSGIServerException
       
   116         from django.core.handlers.wsgi import WSGIHandler
       
   117         if args:
       
   118             raise CommandError('Usage is runserver %s' % self.args)
       
   119         if not addrport:
       
   120             addr = ''
       
   121             port = '8000'
       
   122         else:
       
   123             try:
       
   124                 addr, port = addrport.split(':')
       
   125             except ValueError:
       
   126                 addr, port = '', addrport
       
   127         if not addr:
       
   128             addr = '127.0.0.1'
       
   129 
       
   130         if not port.isdigit():
       
   131             raise CommandError("%r is not a valid port number." % port)
       
   132 
       
   133         use_reloader = options.get('use_reloader', True)
       
   134         admin_media_path = options.get('admin_media_path', '')
       
   135         shutdown_message = options.get('shutdown_message', '')
       
   136         no_media = options.get('no_media', False)
       
   137         quit_command = (sys.platform == 'win32') and 'CTRL-BREAK' or 'CONTROL-C'
       
   138 
       
   139         def inner_run():
       
   140             from django.conf import settings
       
   141 
       
   142             import hotshot, time, os
       
   143             USE_CPROFILE = options.get('use_cprofile', False)
       
   144             USE_LSPROF = options.get('use_lsprof', False)
       
   145             if USE_LSPROF:
       
   146                USE_CPROFILE = True
       
   147             if USE_CPROFILE:
       
   148                 try:
       
   149                     import cProfile
       
   150                     USE_CPROFILE = True
       
   151                 except ImportError:
       
   152                     print "cProfile disabled, module cannot be imported!"
       
   153                     USE_CPROFILE = False
       
   154             if USE_LSPROF and not USE_CPROFILE:
       
   155                 raise SystemExit("Kcachegrind compatible output format required cProfile from Python 2.5")
       
   156             prof_path = options.get('prof_path', '/tmp')
       
   157             def make_profiler_handler(inner_handler):
       
   158                 def handler(environ, start_response):
       
   159                     path_info = environ['PATH_INFO']
       
   160                     # normally /media/ is MEDIA_URL, but in case still check it in case it's differently
       
   161                     # should be hardly a penalty since it's an OR expression.
       
   162                     # TODO: fix this to check the configuration settings and not make assumpsions about where media are on the url
       
   163                     if no_media and (path_info.startswith('/media') or path_info.startswith(settings.MEDIA_URL)):
       
   164                         return inner_handler(environ, start_response)
       
   165                     path_name = path_info.strip("/").replace('/', '.') or "root"
       
   166                     profname = "%s.%.3f.prof" % (path_name, time.time())
       
   167                     profname = os.path.join(prof_path, profname)
       
   168                     if USE_CPROFILE:
       
   169                         prof = cProfile.Profile()
       
   170                     else:
       
   171                         prof = hotshot.Profile(profname)
       
   172                     try:
       
   173                         return prof.runcall(inner_handler, environ, start_response)
       
   174                     finally:
       
   175                         if USE_LSPROF:
       
   176                             kg = KCacheGrind(prof)
       
   177                             kg.output(file(profname, 'w'))
       
   178                         elif USE_CPROFILE:
       
   179                             prof.dump_stats(profname)
       
   180                 return handler
       
   181 
       
   182             print "Validating models..."
       
   183             self.validate(display_num_errors=True)
       
   184             print "\nDjango version %s, using settings %r" % (django.get_version(), settings.SETTINGS_MODULE)
       
   185             print "Development server is running at http://%s:%s/" % (addr, port)
       
   186             print "Quit the server with %s." % quit_command
       
   187             try:
       
   188                 path = admin_media_path or django.__path__[0] + '/contrib/admin/media'
       
   189                 handler = make_profiler_handler(AdminMediaHandler(WSGIHandler(), path))
       
   190                 run(addr, int(port), handler)
       
   191             except WSGIServerException, e:
       
   192                 # Use helpful error messages instead of ugly tracebacks.
       
   193                 ERRORS = {
       
   194                     13: "You don't have permission to access that port.",
       
   195                     98: "That port is already in use.",
       
   196                     99: "That IP address can't be assigned-to.",
       
   197                 }
       
   198                 try:
       
   199                     error_text = ERRORS[e.args[0].args[0]]
       
   200                 except (AttributeError, KeyError):
       
   201                     error_text = str(e)
       
   202                 sys.stderr.write(self.style.ERROR("Error: %s" % error_text) + '\n')
       
   203                 # Need to use an OS exit because sys.exit doesn't work in a thread
       
   204                 os._exit(1)
       
   205             except KeyboardInterrupt:
       
   206                 if shutdown_message:
       
   207                     print shutdown_message
       
   208                 sys.exit(0)
       
   209         if use_reloader:
       
   210             from django.utils import autoreload
       
   211             autoreload.main(inner_run)
       
   212         else:
       
   213             inner_run()