DebugClients/Python/PyProfile.py

changeset 0
de9c2efb9d02
child 13
1af94a91f439
equal deleted inserted replaced
-1:000000000000 0:de9c2efb9d02
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2002 - 2009 Detlev Offenbach <detlev@die-offenbachs.de>
4
5 """
6 Module defining additions to the standard Python profile.py.
7 """
8
9 import os
10 import marshal
11 import types
12 import profile
13 import atexit
14 import pickle
15
16 class PyProfile(profile.Profile):
17 """
18 Class extending the standard Python profiler with additional methods.
19
20 This class extends the standard Python profiler by the functionality to
21 save the collected timing data in a timing cache, to restore these data
22 on subsequent calls, to store a profile dump to a standard filename and
23 to erase these caches.
24 """
25 def __init__(self, basename, timer=None, bias=None):
26 """
27 Constructor
28
29 @param basename name of the script to be profiled (string)
30 @param timer function defining the timing calculation
31 @param bias calibration value (float)
32 """
33 try:
34 profile.Profile.__init__(self, timer, bias)
35 except TypeError:
36 profile.Profile.__init__(self, timer)
37
38 self.dispatch = self.__class__.dispatch
39
40 basename = os.path.splitext(basename)[0]
41 self.profileCache = "%s.profile" % basename
42 self.timingCache = "%s.timings" % basename
43
44 self.__restore()
45 atexit.register(self.save)
46
47 def __restore(self):
48 """
49 Private method to restore the timing data from the timing cache.
50 """
51 if not os.path.exists(self.timingCache):
52 return
53
54 try:
55 cache = open(self.timingCache, 'rb')
56 timings = marshal.load(cache)
57 cache.close()
58 if isinstance(timings, type.DictType):
59 self.timings = timings
60 except:
61 pass
62
63 def save(self):
64 """
65 Public method to store the collected profile data.
66 """
67 # dump the raw timing data
68 cache = open(self.timingCache, 'wb')
69 marshal.dump(self.timings, cache)
70 cache.close()
71
72 # dump the profile data
73 self.dump_stats(self.profileCache)
74
75 def dump_stats(self, file):
76 """
77 Public method to dump the statistics data.
78
79 @param file name of the file to write to (string)
80 """
81 try:
82 f = open(file, 'wb')
83 self.create_stats()
84 pickle.dump(self.stats, f, 2)
85 except (EnvironmentError, pickle.PickleError):
86 pass
87 finally:
88 f.close()
89
90 def erase(self):
91 """
92 Public method to erase the collected timing data.
93 """
94 self.timings = {}
95 if os.path.exists(self.timingCache):
96 os.remove(self.timingCache)
97
98 def fix_frame_filename(self, frame):
99 """
100 Public method used to fixup the filename for a given frame.
101
102 The logic employed here is that if a module was loaded
103 from a .pyc file, then the correct .py to operate with
104 should be in the same path as the .pyc. The reason this
105 logic is needed is that when a .pyc file is generated, the
106 filename embedded and thus what is readable in the code object
107 of the frame object is the fully qualified filepath when the
108 pyc is generated. If files are moved from machine to machine
109 this can break debugging as the .pyc will refer to the .py
110 on the original machine. Another case might be sharing
111 code over a network... This logic deals with that.
112
113 @param frame the frame object
114 """
115 # get module name from __file__
116 if not isinstance(frame, profile.Profile.fake_frame) and \
117 frame.f_globals.has_key('__file__'):
118 root, ext = os.path.splitext(frame.f_globals['__file__'])
119 if ext == '.pyc' or ext == '.py':
120 fixedName = root + '.py'
121 if os.path.exists(fixedName):
122 return fixedName
123
124 return frame.f_code.co_filename
125
126 def trace_dispatch_call(self, frame, t):
127 """
128 Private method used to trace functions calls.
129
130 This is a variant of the one found in the standard Python
131 profile.py calling fix_frame_filename above.
132 """
133 if self.cur and frame.f_back is not self.cur[-2]:
134 rpt, rit, ret, rfn, rframe, rcur = self.cur
135 if not isinstance(rframe, profile.Profile.fake_frame):
136 assert rframe.f_back is frame.f_back, ("Bad call", rfn,
137 rframe, rframe.f_back,
138 frame, frame.f_back)
139 self.trace_dispatch_return(rframe, 0)
140 assert (self.cur is None or \
141 frame.f_back is self.cur[-2]), ("Bad call",
142 self.cur[-3])
143 fcode = frame.f_code
144 fn = (self.fix_frame_filename(frame),
145 fcode.co_firstlineno, fcode.co_name)
146 self.cur = (t, 0, 0, fn, frame, self.cur)
147 timings = self.timings
148 if timings.has_key(fn):
149 cc, ns, tt, ct, callers = timings[fn]
150 timings[fn] = cc, ns + 1, tt, ct, callers
151 else:
152 timings[fn] = 0, 0, 0, 0, {}
153 return 1
154
155 dispatch = {
156 "call": trace_dispatch_call,
157 "exception": profile.Profile.trace_dispatch_exception,
158 "return": profile.Profile.trace_dispatch_return,
159 "c_call": profile.Profile.trace_dispatch_c_call,
160 "c_exception": profile.Profile.trace_dispatch_return, # the C function returned
161 "c_return": profile.Profile.trace_dispatch_return,
162 }

eric ide

mercurial