Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Track line numbers properly.

Previously, when sampling the stack, the first line number in a function was
being used instead of the current line number. Not very useful.
  • Loading branch information...
commit 9ae3cc6933584af2ef70e78bd3df4ae36a090e24 1 parent 456e86a
@bos authored
Showing with 60 additions and 19 deletions.
  1. +60 −19 statprof.py
View
79 statprof.py
@@ -1,4 +1,5 @@
## statprof.py
+## Copyright (C) 2012 Bryan O'Sullivan <bos@serpentine.com>
## Copyright (C) 2011 Alex Fraser <alex at phatcore dot com>
## Copyright (C) 2004,2005 Andy Wingo <wingo at pobox dot com>
## Copyright (C) 2001 Rob Browning <rlb at defaultvalue dot org>
@@ -123,8 +124,8 @@
from __future__ import division
-import signal
import os
+import signal
__all__ = ['start', 'stop', 'reset', 'display']
@@ -174,20 +175,56 @@ def accumulate_time(self, stop_time):
state = ProfileState()
-## call_data := { code object: CallData }
-call_data = {}
-class CallData(object):
- def __init__(self, code):
- self.name = code.co_name
+class CodeKey(object):
+ cache = {}
+
+ __slots__ = ('filename', 'lineno', 'name')
+
+ def __init__(self, frame):
+ code = frame.f_code
self.filename = code.co_filename
- self.lineno = code.co_firstlineno
+ self.lineno = frame.f_lineno
+ self.name = code.co_name
+
+ def __eq__(self, other):
+ try:
+ return (self.lineno == other.lineno and
+ self.filename == other.filename)
+ except:
+ return False
+
+ def __hash__(self):
+ return hash((self.lineno, self.filename))
+
+ @classmethod
+ def get(cls, frame):
+ k = (frame.f_code.co_filename, frame.f_lineno)
+ try:
+ return cls.cache[k]
+ except KeyError:
+ v = cls(frame)
+ cls.cache[k] = v
+ return v
+
+class CallData(object):
+ all_calls = {}
+
+ __slots__ = ('key', 'call_count', 'cum_sample_count', 'self_sample_count')
+
+ def __init__(self, key):
+ self.key = key
self.call_count = 0
self.cum_sample_count = 0
self.self_sample_count = 0
- call_data[code] = self
-def get_call_data(code):
- return call_data.get(code, None) or CallData(code)
+ @classmethod
+ def get(cls, key):
+ try:
+ return cls.all_calls[key]
+ except KeyError:
+ v = CallData(key)
+ cls.all_calls[key] = v
+ return v
###########################################################################
@@ -195,14 +232,16 @@ def get_call_data(code):
def sample_stack_procs(frame):
state.sample_count += 1
- get_call_data(frame.f_code).self_sample_count += 1
+ key = CodeKey.get(frame)
+ CallData.get(key).self_sample_count += 1
- code_seen = {}
+ keys_seen = set()
while frame:
- code_seen[frame.f_code] = True
+ key = CodeKey.get(frame)
+ keys_seen.add(key)
frame = frame.f_back
- for code in code_seen.keys():
- get_call_data(code).cum_sample_count += 1
+ for key in keys_seen:
+ CallData.get(key).cum_sample_count += 1
def profile_signal_handler(signum, frame):
if state.profile_level > 0:
@@ -242,7 +281,8 @@ def stop():
def reset(frequency=None):
assert state.profile_level == 0, "Can't reset() while statprof is running"
- call_data.clear()
+ CallData.all_calls.clear()
+ CodeKey.cache.clear()
state.reset(frequency)
@@ -255,9 +295,10 @@ def __init__(self, call_data):
cum_samples = call_data.cum_sample_count
nsamples = state.sample_count
secs_per_sample = state.accumulated_time / nsamples
- basename = os.path.basename(call_data.filename)
+ basename = os.path.basename(call_data.key.filename)
- self.name = '%s:%d:%s' % (basename, call_data.lineno, call_data.name)
+ self.name = '%s:%d:%s' % (basename, call_data.key.lineno,
+ call_data.key.name)
self.pcnt_time_in_proc = self_samples / nsamples * 100
self.cum_secs_in_proc = cum_samples * secs_per_sample
self.self_secs_in_proc = self_samples * secs_per_sample
@@ -279,7 +320,7 @@ def display(fp=None):
print >> fp, ('No samples recorded.')
return
- l = [CallStats(x) for x in call_data.values()]
+ l = [CallStats(x) for x in CallData.all_calls.itervalues()]
l.sort(reverse=True, key=lambda x: x.self_secs_in_proc)
l = [(x.self_secs_in_proc, x.cum_secs_in_proc, x) for x in l]
l = [x[2] for x in l]
Please sign in to comment.
Something went wrong with that request. Please try again.