From c8021103d0d9c8cc34f1f37d5e7beb5cfb850495 Mon Sep 17 00:00:00 2001 From: Eric Reynolds Date: Mon, 10 Aug 2015 14:47:18 +0200 Subject: [PATCH] Added functionality to show stats for function defined in specific location. --- cprofilev.py | 82 ++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 64 insertions(+), 18 deletions(-) diff --git a/cprofilev.py b/cprofilev.py index 5e38dc3..7a8d363 100755 --- a/cprofilev.py +++ b/cprofilev.py @@ -41,16 +41,30 @@ % if callers:

Called By:

{{ !callers }}
+ % end % if callees:

Called:

{{ !callees }}
+ % end + +

Restrictions applied to get stats listed above:

+ % if restrictions: + + % else: + None + % end """ SORT_KEY = 'sort' FUNC_NAME_KEY = 'func_name' +FUNC_LOC_KEY = 'func_loc' class Stats(object): @@ -68,6 +82,7 @@ class Stats(object): STATS_LINE_REGEX = r'(.*)\((.*)\)$' HEADER_LINE_REGEX = r'ncalls|tottime|cumtime' + FUNCTION_SIG_HEADER = 'filename:lineno(function)' def __init__(self, profile_output=None, profile_obj=None): self.profile = profile_output or profile_obj @@ -83,19 +98,31 @@ def read_stream(self): def read(self): output = self.read_stream() lines = output.splitlines(True) - return "".join(map(self.process_line, lines)) + info = {} + for i in range(len(lines)): + lines[i] = self.process_line(lines[i], info) + return "".join(lines) @classmethod - def process_line(cls, line): + def process_line(cls, line, info): # Format header lines (such that clicking on a column header sorts by # that column). if re.search(cls.HEADER_LINE_REGEX, line): + + # Find and store the index of the function signature column + if 'func_col_pos' not in info: + match = line.find(cls.FUNCTION_SIG_HEADER) + if match >= 0: + info['func_col_pos'] = match + + # Replace sort keys with links for key, val in cls.SORT_ARGS.items(): url_link = bottle.template( "{{ key }}", - url=cls.get_updated_href(SORT_KEY, val), + url=cls.get_updated_href({SORT_KEY: val}), key=key) line = line.replace(key, url_link) + # Format stat lines (such that clicking on the function name drills into # the function call). match = re.search(cls.STATS_LINE_REGEX, line) @@ -103,34 +130,45 @@ def process_line(cls, line): prefix = match.group(1) func_name = match.group(2) if func_name not in cls.IGNORE_FUNC_NAMES: + if 'func_col_pos' in info: + func_loc = prefix[info['func_col_pos']:] + prefix = prefix[:info['func_col_pos']] + full_url_link = bottle.template( + "{{ func_loc }}", + url=cls.get_updated_href({FUNC_LOC_KEY: func_loc, FUNC_NAME_KEY: func_name}), + func_loc=func_loc) + else: + full_url_link = '' url_link = bottle.template( "{{ func_name }}", - url=cls.get_updated_href(FUNC_NAME_KEY, func_name), + url=cls.get_updated_href({FUNC_LOC_KEY: None, FUNC_NAME_KEY: func_name}), func_name=func_name) line = bottle.template( - "{{ prefix }}({{ !url_link }})\n", - prefix=prefix, url_link=url_link) + "{{ prefix }}{{ !full_url_link }}({{ !url_link }})\n", + prefix=prefix, full_url_link=full_url_link, url_link=url_link) return line @classmethod - def get_updated_href(cls, key, val): + def get_updated_href(cls, keyvals): href = '?' query = dict(bottle.request.query) - query[key] = val + for key, val in keyvals.items(): + query[key] = val for key in query.keys(): - href += '%s=%s&' % (key, query[key]) + if query[key] is not None: + href += '%s=%s&' % (key, query[key]) return href[:-1] - def show(self, restriction=''): - self.stats.print_stats(restriction) + def show(self, *restrictions): + self.stats.print_stats(*restrictions) return self - def show_callers(self, func_name): - self.stats.print_callers(func_name) + def show_callers(self, *restriction): + self.stats.print_callers(*restriction) return self - def show_callees(self, func_name): - self.stats.print_callees(func_name) + def show_callees(self, *restriction): + self.stats.print_callees(*restriction) return self def sort(self, sort=''): @@ -154,16 +192,24 @@ def route_handler(self): self.stats = Stats(self.profile) func_name = bottle.request.query.get(FUNC_NAME_KEY) or '' + func_loc = bottle.request.query.get(FUNC_LOC_KEY) or '' sort = bottle.request.query.get(SORT_KEY) or '' + restrictions = [] + if func_name: + restrictions.append(r"\(" + re.escape(func_name) + r"\)") + if func_loc: + restrictions.append("^" + re.escape(func_loc)) + self.stats.sort(sort) - callers = self.stats.show_callers(func_name).read() if func_name else '' - callees = self.stats.show_callees(func_name).read() if func_name else '' + callers = self.stats.show_callers(*restrictions).read() if func_name else '' + callees = self.stats.show_callees(*restrictions).read() if func_name else '' data = { 'title': self.title, - 'stats': self.stats.sort(sort).show(func_name).read(), + 'stats': self.stats.sort(sort).show(*restrictions).read(), 'callers': callers, 'callees': callees, + 'restrictions': restrictions } return bottle.template(STATS_TEMPLATE, **data)