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 | fileset, |
|
34 | fileset, | |
35 | hg, |
|
35 | hg, | |
36 | hook, |
|
36 | hook, | |
|
37 | profiling, | |||
37 | revset, |
|
38 | revset, | |
38 | templatefilters, |
|
39 | templatefilters, | |
39 | templatekw, |
|
40 | templatekw, | |
@@ -892,85 +893,6 b' def _dispatch(req):' | |||||
892 | if repo and repo != req.repo: |
|
893 | if repo and repo != req.repo: | |
893 | repo.close() |
|
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 | def _runcommand(ui, options, cmd, cmdfunc): |
|
896 | def _runcommand(ui, options, cmd, cmdfunc): | |
975 | """Enables the profiler if applicable. |
|
897 | """Enables the profiler if applicable. | |
976 |
|
898 | |||
@@ -983,39 +905,7 b' def _runcommand(ui, options, cmd, cmdfun' | |||||
983 | raise error.CommandError(cmd, _("invalid arguments")) |
|
905 | raise error.CommandError(cmd, _("invalid arguments")) | |
984 |
|
906 | |||
985 | if options['profile'] or ui.configbool('profiling', 'enabled'): |
|
907 | if options['profile'] or ui.configbool('profiling', 'enabled'): | |
986 | profiler = os.getenv('HGPROF') |
|
908 | return profiling.profile(ui, checkargs) | |
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() |
|
|||
1019 | else: |
|
909 | else: | |
1020 | return checkargs() |
|
910 | return checkargs() | |
1021 |
|
911 |
General Comments 0
You need to be logged in to leave comments.
Login now