|
1 # profiling.py - profiling functions |
|
2 # |
|
3 # Copyright 2016 Gregory Szorc <gregory.szorc@gmail.com> |
|
4 # |
|
5 # This software may be used and distributed according to the terms of the |
|
6 # GNU General Public License version 2 or any later version. |
|
7 |
|
8 from __future__ import absolute_import, print_function |
|
9 |
|
10 import os |
|
11 import sys |
|
12 import time |
|
13 |
|
14 from .i18n import _ |
|
15 from . import ( |
|
16 error, |
|
17 util, |
|
18 ) |
|
19 |
|
20 def lsprofile(ui, func, fp): |
|
21 format = ui.config('profiling', 'format', default='text') |
|
22 field = ui.config('profiling', 'sort', default='inlinetime') |
|
23 limit = ui.configint('profiling', 'limit', default=30) |
|
24 climit = ui.configint('profiling', 'nested', default=0) |
|
25 |
|
26 if format not in ['text', 'kcachegrind']: |
|
27 ui.warn(_("unrecognized profiling format '%s'" |
|
28 " - Ignored\n") % format) |
|
29 format = 'text' |
|
30 |
|
31 try: |
|
32 from . import lsprof |
|
33 except ImportError: |
|
34 raise error.Abort(_( |
|
35 'lsprof not available - install from ' |
|
36 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/')) |
|
37 p = lsprof.Profiler() |
|
38 p.enable(subcalls=True) |
|
39 try: |
|
40 return func() |
|
41 finally: |
|
42 p.disable() |
|
43 |
|
44 if format == 'kcachegrind': |
|
45 from . import lsprofcalltree |
|
46 calltree = lsprofcalltree.KCacheGrind(p) |
|
47 calltree.output(fp) |
|
48 else: |
|
49 # format == 'text' |
|
50 stats = lsprof.Stats(p.getstats()) |
|
51 stats.sort(field) |
|
52 stats.pprint(limit=limit, file=fp, climit=climit) |
|
53 |
|
54 def flameprofile(ui, func, fp): |
|
55 try: |
|
56 from flamegraph import flamegraph |
|
57 except ImportError: |
|
58 raise error.Abort(_( |
|
59 'flamegraph not available - install from ' |
|
60 'https://github.com/evanhempel/python-flamegraph')) |
|
61 # developer config: profiling.freq |
|
62 freq = ui.configint('profiling', 'freq', default=1000) |
|
63 filter_ = None |
|
64 collapse_recursion = True |
|
65 thread = flamegraph.ProfileThread(fp, 1.0 / freq, |
|
66 filter_, collapse_recursion) |
|
67 start_time = time.clock() |
|
68 try: |
|
69 thread.start() |
|
70 func() |
|
71 finally: |
|
72 thread.stop() |
|
73 thread.join() |
|
74 print('Collected %d stack frames (%d unique) in %2.2f seconds.' % ( |
|
75 time.clock() - start_time, thread.num_frames(), |
|
76 thread.num_frames(unique=True))) |
|
77 |
|
78 def statprofile(ui, func, fp): |
|
79 try: |
|
80 import statprof |
|
81 except ImportError: |
|
82 raise error.Abort(_( |
|
83 'statprof not available - install using "easy_install statprof"')) |
|
84 |
|
85 freq = ui.configint('profiling', 'freq', default=1000) |
|
86 if freq > 0: |
|
87 statprof.reset(freq) |
|
88 else: |
|
89 ui.warn(_("invalid sampling frequency '%s' - ignoring\n") % freq) |
|
90 |
|
91 statprof.start() |
|
92 try: |
|
93 return func() |
|
94 finally: |
|
95 statprof.stop() |
|
96 statprof.display(fp) |
|
97 |
|
98 def profile(ui, fn): |
|
99 """Profile a function call.""" |
|
100 profiler = os.getenv('HGPROF') |
|
101 if profiler is None: |
|
102 profiler = ui.config('profiling', 'type', default='ls') |
|
103 if profiler not in ('ls', 'stat', 'flame'): |
|
104 ui.warn(_("unrecognized profiler '%s' - ignored\n") % profiler) |
|
105 profiler = 'ls' |
|
106 |
|
107 output = ui.config('profiling', 'output') |
|
108 |
|
109 if output == 'blackbox': |
|
110 fp = util.stringio() |
|
111 elif output: |
|
112 path = ui.expandpath(output) |
|
113 fp = open(path, 'wb') |
|
114 else: |
|
115 fp = sys.stderr |
|
116 |
|
117 try: |
|
118 if profiler == 'ls': |
|
119 return lsprofile(ui, fn, fp) |
|
120 elif profiler == 'flame': |
|
121 return flameprofile(ui, fn, fp) |
|
122 else: |
|
123 return statprofile(ui, fn, fp) |
|
124 finally: |
|
125 if output: |
|
126 if output == 'blackbox': |
|
127 val = 'Profile:\n%s' % fp.getvalue() |
|
128 # ui.log treats the input as a format string, |
|
129 # so we need to escape any % signs. |
|
130 val = val.replace('%', '%%') |
|
131 ui.log('profile', val) |
|
132 fp.close() |