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