Show More
@@ -0,0 +1,132 b'' | |||
|
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() |
@@ -34,6 +34,7 b' from . import (' | |||
|
34 | 34 | fileset, |
|
35 | 35 | hg, |
|
36 | 36 | hook, |
|
37 | profiling, | |
|
37 | 38 | revset, |
|
38 | 39 | templatefilters, |
|
39 | 40 | templatekw, |
@@ -892,85 +893,6 b' def _dispatch(req):' | |||
|
892 | 893 | if repo and repo != req.repo: |
|
893 | 894 | repo.close() |
|
894 | 895 | |
|
895 | def lsprofile(ui, func, fp): | |
|
896 | format = ui.config('profiling', 'format', default='text') | |
|
897 | field = ui.config('profiling', 'sort', default='inlinetime') | |
|
898 | limit = ui.configint('profiling', 'limit', default=30) | |
|
899 | climit = ui.configint('profiling', 'nested', default=0) | |
|
900 | ||
|
901 | if format not in ['text', 'kcachegrind']: | |
|
902 | ui.warn(_("unrecognized profiling format '%s'" | |
|
903 | " - Ignored\n") % format) | |
|
904 | format = 'text' | |
|
905 | ||
|
906 | try: | |
|
907 | from . import lsprof | |
|
908 | except ImportError: | |
|
909 | raise error.Abort(_( | |
|
910 | 'lsprof not available - install from ' | |
|
911 | 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/')) | |
|
912 | p = lsprof.Profiler() | |
|
913 | p.enable(subcalls=True) | |
|
914 | try: | |
|
915 | return func() | |
|
916 | finally: | |
|
917 | p.disable() | |
|
918 | ||
|
919 | if format == 'kcachegrind': | |
|
920 | from . import lsprofcalltree | |
|
921 | calltree = lsprofcalltree.KCacheGrind(p) | |
|
922 | calltree.output(fp) | |
|
923 | else: | |
|
924 | # format == 'text' | |
|
925 | stats = lsprof.Stats(p.getstats()) | |
|
926 | stats.sort(field) | |
|
927 | stats.pprint(limit=limit, file=fp, climit=climit) | |
|
928 | ||
|
929 | def flameprofile(ui, func, fp): | |
|
930 | try: | |
|
931 | from flamegraph import flamegraph | |
|
932 | except ImportError: | |
|
933 | raise error.Abort(_( | |
|
934 | 'flamegraph not available - install from ' | |
|
935 | 'https://github.com/evanhempel/python-flamegraph')) | |
|
936 | # developer config: profiling.freq | |
|
937 | freq = ui.configint('profiling', 'freq', default=1000) | |
|
938 | filter_ = None | |
|
939 | collapse_recursion = True | |
|
940 | thread = flamegraph.ProfileThread(fp, 1.0 / freq, | |
|
941 | filter_, collapse_recursion) | |
|
942 | start_time = time.clock() | |
|
943 | try: | |
|
944 | thread.start() | |
|
945 | func() | |
|
946 | finally: | |
|
947 | thread.stop() | |
|
948 | thread.join() | |
|
949 | print('Collected %d stack frames (%d unique) in %2.2f seconds.' % ( | |
|
950 | time.clock() - start_time, thread.num_frames(), | |
|
951 | thread.num_frames(unique=True))) | |
|
952 | ||
|
953 | ||
|
954 | def statprofile(ui, func, fp): | |
|
955 | try: | |
|
956 | import statprof | |
|
957 | except ImportError: | |
|
958 | raise error.Abort(_( | |
|
959 | 'statprof not available - install using "easy_install statprof"')) | |
|
960 | ||
|
961 | freq = ui.configint('profiling', 'freq', default=1000) | |
|
962 | if freq > 0: | |
|
963 | statprof.reset(freq) | |
|
964 | else: | |
|
965 | ui.warn(_("invalid sampling frequency '%s' - ignoring\n") % freq) | |
|
966 | ||
|
967 | statprof.start() | |
|
968 | try: | |
|
969 | return func() | |
|
970 | finally: | |
|
971 | statprof.stop() | |
|
972 | statprof.display(fp) | |
|
973 | ||
|
974 | 896 | def _runcommand(ui, options, cmd, cmdfunc): |
|
975 | 897 | """Enables the profiler if applicable. |
|
976 | 898 | |
@@ -983,39 +905,7 b' def _runcommand(ui, options, cmd, cmdfun' | |||
|
983 | 905 | raise error.CommandError(cmd, _("invalid arguments")) |
|
984 | 906 | |
|
985 | 907 | if options['profile'] or ui.configbool('profiling', 'enabled'): |
|
986 | profiler = os.getenv('HGPROF') | |
|
987 | if profiler is None: | |
|
988 | profiler = ui.config('profiling', 'type', default='ls') | |
|
989 | if profiler not in ('ls', 'stat', 'flame'): | |
|
990 | ui.warn(_("unrecognized profiler '%s' - ignored\n") % profiler) | |
|
991 | profiler = 'ls' | |
|
992 | ||
|
993 | output = ui.config('profiling', 'output') | |
|
994 | ||
|
995 | if output == 'blackbox': | |
|
996 | fp = util.stringio() | |
|
997 | elif output: | |
|
998 | path = ui.expandpath(output) | |
|
999 | fp = open(path, 'wb') | |
|
1000 | else: | |
|
1001 | fp = sys.stderr | |
|
1002 | ||
|
1003 | try: | |
|
1004 | if profiler == 'ls': | |
|
1005 | return lsprofile(ui, checkargs, fp) | |
|
1006 | elif profiler == 'flame': | |
|
1007 | return flameprofile(ui, checkargs, fp) | |
|
1008 | else: | |
|
1009 | return statprofile(ui, checkargs, fp) | |
|
1010 | finally: | |
|
1011 | if output: | |
|
1012 | if output == 'blackbox': | |
|
1013 | val = "Profile:\n%s" % fp.getvalue() | |
|
1014 | # ui.log treats the input as a format string, | |
|
1015 | # so we need to escape any % signs. | |
|
1016 | val = val.replace('%', '%%') | |
|
1017 | ui.log('profile', val) | |
|
1018 | fp.close() | |
|
908 | return profiling.profile(ui, checkargs) | |
|
1019 | 909 | else: |
|
1020 | 910 | return checkargs() |
|
1021 | 911 |
General Comments 0
You need to be logged in to leave comments.
Login now