~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/lsprof.py

  • Committer: John Arbash Meinel
  • Date: 2007-03-01 21:56:19 UTC
  • mto: (2255.7.84 dirstate)
  • mto: This revision was merged to the branch mainline in revision 2322.
  • Revision ID: john@arbash-meinel.com-20070301215619-wpt6kz8yem3ypu1b
Update to dirstate locking.
Move all of WT4.lock_* functions locally, so that they can
properly interact and cleanup around when we lock/unlock the
dirstate file.
Change all Lock objects to be non-blocking. So that if someone
grabs a lock on the DirState we find out immediately, rather
than blocking.
Change WT4.unlock() so that if the dirstate is dirty, it will
save the contents even if it only has a read lock.
It does this by trying to take a write lock, if it fails
we just ignore it. If it succeeds, then we can flush to disk.
This is more important now that DirState tracks file changes.
It allows 'bzr status' to update the cached stat and sha values.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# this is copied from the lsprof distro because somehow
 
2
# it is not installed by distutils
 
3
# I made one modification to profile so that it returns a pair
 
4
# instead of just the Stats object
 
5
 
 
6
import sys
 
7
import thread
 
8
import threading
 
9
from _lsprof import Profiler, profiler_entry
 
10
 
 
11
__all__ = ['profile', 'Stats']
 
12
 
 
13
_g_threadmap = {}
 
14
 
 
15
 
 
16
def _thread_profile(f, *args, **kwds):
 
17
    # we lose the first profile point for a new thread in order to trampoline
 
18
    # a new Profile object into place
 
19
    global _g_threadmap
 
20
    thr = thread.get_ident()
 
21
    _g_threadmap[thr] = p = Profiler()
 
22
    # this overrides our sys.setprofile hook:
 
23
    p.enable(subcalls=True, builtins=True)
 
24
 
 
25
 
 
26
def profile(f, *args, **kwds):
 
27
    """XXX docstring"""
 
28
    global _g_threadmap
 
29
    p = Profiler()
 
30
    p.enable(subcalls=True)
 
31
    threading.setprofile(_thread_profile)
 
32
    try:
 
33
        ret = f(*args, **kwds)
 
34
    finally:
 
35
        p.disable()
 
36
        for pp in _g_threadmap.values():
 
37
            pp.disable()
 
38
        threading.setprofile(None)
 
39
    
 
40
    threads = {}
 
41
    for tid, pp in _g_threadmap.items():
 
42
        threads[tid] = Stats(pp.getstats(), {})
 
43
    _g_threadmap = {}
 
44
    return ret, Stats(p.getstats(), threads)
 
45
 
 
46
 
 
47
class Stats(object):
 
48
    """XXX docstring"""
 
49
 
 
50
    def __init__(self, data, threads):
 
51
        self.data = data
 
52
        self.threads = threads
 
53
 
 
54
    def sort(self, crit="inlinetime"):
 
55
        """XXX docstring"""
 
56
        if crit not in profiler_entry.__dict__:
 
57
            raise ValueError, "Can't sort by %s" % crit
 
58
        self.data.sort(lambda b, a: cmp(getattr(a, crit),
 
59
                                        getattr(b, crit)))
 
60
        for e in self.data:
 
61
            if e.calls:
 
62
                e.calls.sort(lambda b, a: cmp(getattr(a, crit),
 
63
                                              getattr(b, crit)))
 
64
 
 
65
    def pprint(self, top=None, file=None):
 
66
        """XXX docstring"""
 
67
        if file is None:
 
68
            file = sys.stdout
 
69
        d = self.data
 
70
        if top is not None:
 
71
            d = d[:top]
 
72
        cols = "% 12s %12s %11.4f %11.4f   %s\n"
 
73
        hcols = "% 12s %12s %12s %12s %s\n"
 
74
        cols2 = "+%12s %12s %11.4f %11.4f +  %s\n"
 
75
        file.write(hcols % ("CallCount", "Recursive", "Total(ms)",
 
76
                            "Inline(ms)", "module:lineno(function)"))
 
77
        for e in d:
 
78
            file.write(cols % (e.callcount, e.reccallcount, e.totaltime,
 
79
                               e.inlinetime, label(e.code)))
 
80
            if e.calls:
 
81
                for se in e.calls:
 
82
                    file.write(cols % ("+%s" % se.callcount, se.reccallcount,
 
83
                                       se.totaltime, se.inlinetime,
 
84
                                       "+%s" % label(se.code)))
 
85
 
 
86
    def freeze(self):
 
87
        """Replace all references to code objects with string
 
88
        descriptions; this makes it possible to pickle the instance."""
 
89
 
 
90
        # this code is probably rather ickier than it needs to be!
 
91
        for i in range(len(self.data)):
 
92
            e = self.data[i]
 
93
            if not isinstance(e.code, str):
 
94
                self.data[i] = type(e)((label(e.code),) + e[1:])
 
95
            if e.calls:
 
96
                for j in range(len(e.calls)):
 
97
                    se = e.calls[j]
 
98
                    if not isinstance(se.code, str):
 
99
                        e.calls[j] = type(se)((label(se.code),) + se[1:])
 
100
        for s in self.threads.values():
 
101
            s.freeze()
 
102
 
 
103
    def calltree(self, file):
 
104
        """Output profiling data in calltree format (for KCacheGrind)."""
 
105
        _CallTreeFilter(self.data).output(file)
 
106
 
 
107
 
 
108
class _CallTreeFilter(object):
 
109
 
 
110
    def __init__(self, data):
 
111
        self.data = data
 
112
        self.out_file = None
 
113
 
 
114
    def output(self, out_file):
 
115
        self.out_file = out_file        
 
116
        print >> out_file, 'events: Ticks'
 
117
        self._print_summary()
 
118
        for entry in self.data:
 
119
            self._entry(entry)
 
120
 
 
121
    def _print_summary(self):
 
122
        max_cost = 0
 
123
        for entry in self.data:
 
124
            totaltime = int(entry.totaltime * 1000)
 
125
            max_cost = max(max_cost, totaltime)
 
126
        print >> self.out_file, 'summary: %d' % (max_cost,)
 
127
 
 
128
    def _entry(self, entry):
 
129
        out_file = self.out_file
 
130
        code = entry.code
 
131
        inlinetime = int(entry.inlinetime * 1000)
 
132
        #print >> out_file, 'ob=%s' % (code.co_filename,)
 
133
        print >> out_file, 'fi=%s' % (code.co_filename,)
 
134
        print >> out_file, 'fn=%s' % (label(code, True),)
 
135
        print >> out_file, '%d %d' % (code.co_firstlineno, inlinetime)
 
136
        # recursive calls are counted in entry.calls
 
137
        if entry.calls:
 
138
            calls = entry.calls
 
139
        else:
 
140
            calls = []
 
141
        for subentry in calls:
 
142
            self._subentry(code.co_firstlineno, subentry)
 
143
        print >> out_file
 
144
 
 
145
    def _subentry(self, lineno, subentry):
 
146
        out_file = self.out_file
 
147
        code = subentry.code
 
148
        totaltime = int(subentry.totaltime * 1000)
 
149
        #print >> out_file, 'cob=%s' % (code.co_filename,)
 
150
        print >> out_file, 'cfn=%s' % (label(code, True),)
 
151
        print >> out_file, 'cfi=%s' % (code.co_filename,)
 
152
        print >> out_file, 'calls=%d %d' % (
 
153
            subentry.callcount, code.co_firstlineno)
 
154
        print >> out_file, '%d %d' % (lineno, totaltime)
 
155
 
 
156
 
 
157
_fn2mod = {}
 
158
 
 
159
def label(code, calltree=False):
 
160
    if isinstance(code, str):
 
161
        return code
 
162
    try:
 
163
        mname = _fn2mod[code.co_filename]
 
164
    except KeyError:
 
165
        for k, v in sys.modules.items():
 
166
            if v is None:
 
167
                continue
 
168
            if getattr(v, '__file__', None) is None:
 
169
                continue
 
170
            if not isinstance(v.__file__, str):
 
171
                continue
 
172
            if v.__file__.startswith(code.co_filename):
 
173
                mname = _fn2mod[code.co_filename] = k
 
174
                break
 
175
        else:
 
176
            mname = _fn2mod[code.co_filename] = '<%s>'%code.co_filename
 
177
    if calltree:
 
178
        return '%s %s:%d' % (code.co_name, mname, code.co_firstlineno)
 
179
    else:
 
180
        return '%s:%d(%s)' % (mname, code.co_firstlineno, code.co_name)
 
181
 
 
182
 
 
183
if __name__ == '__main__':
 
184
    import os
 
185
    sys.argv = sys.argv[1:]
 
186
    if not sys.argv:
 
187
        print >> sys.stderr, "usage: lsprof.py <script> <arguments...>"
 
188
        sys.exit(2)
 
189
    sys.path.insert(0, os.path.abspath(os.path.dirname(sys.argv[0])))
 
190
    stats = profile(execfile, sys.argv[0], globals(), locals())
 
191
    stats.sort()
 
192
    stats.pprint()