diff --git a/mercurial/dispatch.py b/mercurial/dispatch.py --- a/mercurial/dispatch.py +++ b/mercurial/dispatch.py @@ -687,6 +687,59 @@ def _dispatch(req): if repo and repo != req.repo: repo.close() +def lsprofile(ui, func, fp): + format = ui.config('profiling', 'format', default='text') + field = ui.config('profiling', 'sort', default='inlinetime') + climit = ui.configint('profiling', 'nested', default=5) + + if not format in ['text', 'kcachegrind']: + ui.warn(_("unrecognized profiling format '%s'" + " - Ignored\n") % format) + format = 'text' + + try: + from mercurial import lsprof + except ImportError: + raise util.Abort(_( + 'lsprof not available - install from ' + 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/')) + p = lsprof.Profiler() + p.enable(subcalls=True) + try: + return func() + finally: + p.disable() + + if format == 'kcachegrind': + import lsprofcalltree + calltree = lsprofcalltree.KCacheGrind(p) + calltree.output(fp) + else: + # format == 'text' + stats = lsprof.Stats(p.getstats()) + stats.sort(field) + stats.pprint(limit=30, file=fp, climit=climit) + +def statprofile(ui, func, fp): + try: + import statprof + except ImportError: + raise util.Abort(_( + 'statprof not available - install using "easy_install statprof"')) + + freq = ui.configint('profiling', 'freq', default=1000) + if freq > 0: + statprof.reset(freq) + else: + ui.warn(_("invalid sampling frequency '%s' - ignoring\n") % freq) + + statprof.start() + try: + return func() + finally: + statprof.stop() + statprof.display(fp) + def _runcommand(ui, options, cmd, cmdfunc): def checkargs(): try: @@ -695,47 +748,28 @@ def _runcommand(ui, options, cmd, cmdfun raise error.CommandError(cmd, _("invalid arguments")) if options['profile']: - format = ui.config('profiling', 'format', default='text') - field = ui.config('profiling', 'sort', default='inlinetime') - climit = ui.configint('profiling', 'nested', default=5) - - if not format in ['text', 'kcachegrind']: - ui.warn(_("unrecognized profiling format '%s'" - " - Ignored\n") % format) - format = 'text' + profiler = os.getenv('HGPROF') + if profiler is None: + profiler = ui.config('profiling', 'type', default='ls') + if profiler not in ('ls', 'stat'): + ui.warn(_("unrecognized profiler '%s' - ignored\n") % profiler) + profiler = 'ls' output = ui.config('profiling', 'output') if output: path = ui.expandpath(output) - ostream = open(path, 'wb') + fp = open(path, 'wb') else: - ostream = sys.stderr + fp = sys.stderr try: - from mercurial import lsprof - except ImportError: - raise util.Abort(_( - 'lsprof not available - install from ' - 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/')) - p = lsprof.Profiler() - p.enable(subcalls=True) - try: - return checkargs() + if profiler == 'ls': + return lsprofile(ui, checkargs, fp) + else: + return statprofile(ui, checkargs, fp) finally: - p.disable() - - if format == 'kcachegrind': - import lsprofcalltree - calltree = lsprofcalltree.KCacheGrind(p) - calltree.output(ostream) - else: - # format == 'text' - stats = lsprof.Stats(p.getstats()) - stats.sort(field) - stats.pprint(limit=30, file=ostream, climit=climit) - if output: - ostream.close() + fp.close() else: return checkargs() diff --git a/mercurial/help/config.txt b/mercurial/help/config.txt --- a/mercurial/help/config.txt +++ b/mercurial/help/config.txt @@ -938,14 +938,31 @@ information about working with phases. ``profiling`` """"""""""""" -Specifies profiling format and file output. In this section -description, 'profiling data' stands for the raw data collected -during profiling, while 'profiling report' stands for a statistical -text report generated from the profiling data. The profiling is done -using lsprof. +Specifies profiling type, format, and file output. Two profilers are +supported: an instrumenting profiler (named ``ls``), and a sampling +profiler (named ``stat``). + +In this section description, 'profiling data' stands for the raw data +collected during profiling, while 'profiling report' stands for a +statistical text report generated from the profiling data. The +profiling is done using lsprof. + +``type`` + The type of profiler to use. + Default: ls. + + ``ls`` + Use Python's built-in instrumenting profiler. This profiler + works on all platforms, but each line number it reports is the + first line of a function. This restriction makes it difficult to + identify the expensive parts of a non-trivial function. + ``stat`` + Use a third-party statistical profiler, statprof. This profiler + currently runs only on Unix systems, and is most useful for + profiling commands that run for longer than about 0.1 seconds. ``format`` - Profiling format. + Profiling format. Specific to the ``ls`` instrumenting profiler. Default: text. ``text`` @@ -957,6 +974,10 @@ using lsprof. file, the generated file can directly be loaded into kcachegrind. +``frequency`` + Sampling frequency. Specific to the ``stat`` sampling profiler. + Default: 1000. + ``output`` File path where profiling data or report should be saved. If the file exists, it is replaced. Default: None, data is printed on