~launchpad-pqm/launchpad/devel

8687.15.18 by Karl Fogel
Add the copyright header block to files under lib/canonical/.
1
# Copyright 2009 Canonical Ltd.  This software is licensed under the
2
# GNU Affero General Public License version 3 (see the file LICENSE).
3
6062.2.1 by Francis J. Lacoste
Add garbage debugging.
4
# We like globals!
5
# pylint: disable-msg=W0602
1946 by Canonical.com Patch Queue Manager
[trivial] Memory debug helpers for next time
6
"""
7
This code is from:
8
    http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/286222
9
    http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/65333
10
This is under the Python Licence.
11
12
It also contains spiv's code from:
13
    http://twistedmatrix.com/users/spiv/countrefs.py
14
This is under 'MIT Licence if I'm pressed'
15
16
None of this should be in day-to-day use. Feel free to document usage
17
and improve APIs as needed.
18
19
"""
20
5985.5.1 by Francis J. Lacoste
Add readCounts(), deltaCounts() function. Renamed existing mostRefs to classesWithMostRefs, and add new mostRefs() algorithm.
21
__metatype__ = type
22
__all__ = [
23
    'classesWithMostRefs',
6062.2.1 by Francis J. Lacoste
Add garbage debugging.
24
    'countsByType',
5985.5.1 by Francis J. Lacoste
Add readCounts(), deltaCounts() function. Renamed existing mostRefs to classesWithMostRefs, and add new mostRefs() algorithm.
25
    'deltaCounts',
26
    'logInThread',
27
    'memory',
28
    'mostRefs',
29
    'printCounts',
30
    'readCounts',
31
    'resident',
32
    'stacksize',
33
    ]
34
35
36
import gc
1946 by Canonical.com Patch Queue Manager
[trivial] Memory debug helpers for next time
37
import os
5985.5.1 by Francis J. Lacoste
Add readCounts(), deltaCounts() function. Renamed existing mostRefs to classesWithMostRefs, and add new mostRefs() algorithm.
38
import sys
39
import types
40
import threading
41
import time
1946 by Canonical.com Patch Queue Manager
[trivial] Memory debug helpers for next time
42
43
44
_proc_status = '/proc/%d/status' % os.getpid()
45
46
_scale = {'kB': 1024.0, 'mB': 1024.0*1024.0,
47
          'KB': 1024.0, 'MB': 1024.0*1024.0}
48
49
def _VmB(VmKey):
50
    '''Private.
51
    '''
52
    global _proc_status, _scale
53
     # get pseudo file  /proc/<pid>/status
54
    try:
55
        t = open(_proc_status)
56
        v = t.read()
57
        t.close()
58
    except OSError:
59
        return 0.0  # non-Linux?
60
     # get VmKey line e.g. 'VmRSS:  9999  kB\n ...'
61
    i = v.index(VmKey)
62
    v = v[i:].split(None, 3)  # whitespace
63
    if len(v) < 3:
64
        return 0.0  # invalid format?
65
     # convert Vm value to bytes
66
    return float(v[1]) * _scale[v[2]]
67
68
69
def memory(since=0.0):
70
    '''Return memory usage in bytes.
71
    '''
72
    return _VmB('VmSize:') - since
73
74
75
def resident(since=0.0):
76
    '''Return resident memory usage in bytes.
77
    '''
78
    return _VmB('VmRSS:') - since
79
80
81
def stacksize(since=0.0):
82
    '''Return stack size in bytes.
83
    '''
84
    return _VmB('VmStk:') - since
85
86
87
def dump_garbage():
88
    """
89
    show us what's the garbage about
90
91
    import gc
92
    gc.enable()
93
    gc.set_debug(gc.DEBUG_LEAK)
94
95
    """
4785.3.7 by Jeroen Vermeulen
Removed whitespace at ends of lines
96
1946 by Canonical.com Patch Queue Manager
[trivial] Memory debug helpers for next time
97
    # force collection
98
    print "\nGARBAGE:"
99
    gc.collect()
100
101
    print "\nGARBAGE OBJECTS:"
102
    for x in gc.garbage:
103
        s = str(x)
6062.2.1 by Francis J. Lacoste
Add garbage debugging.
104
        if len(s) > 80:
105
            s = s[:80]
106
        print type(x), "\n  ", s
1946 by Canonical.com Patch Queue Manager
[trivial] Memory debug helpers for next time
107
108
# This is spiv's reference count code, under 'MIT Licence if I'm pressed'.
109
#
5985.5.1 by Francis J. Lacoste
Add readCounts(), deltaCounts() function. Renamed existing mostRefs to classesWithMostRefs, and add new mostRefs() algorithm.
110
111
def classesWithMostRefs(n=30):
5985.5.6 by Francis J. Lacoste
English typo.
112
    """Return the n ClassType objects with the highest reference count.
5985.5.1 by Francis J. Lacoste
Add readCounts(), deltaCounts() function. Renamed existing mostRefs to classesWithMostRefs, and add new mostRefs() algorithm.
113
114
    This gives an idea of the number of objects in the system by type,
5985.5.6 by Francis J. Lacoste
English typo.
115
    since each instance will have at lest one reference to the class.
5985.5.1 by Francis J. Lacoste
Add readCounts(), deltaCounts() function. Renamed existing mostRefs to classesWithMostRefs, and add new mostRefs() algorithm.
116
117
    :return: A list of tuple (count, type).
118
    """
1946 by Canonical.com Patch Queue Manager
[trivial] Memory debug helpers for next time
119
    d = {}
120
    for obj in gc.get_objects():
121
        if type(obj) in (types.ClassType, types.TypeType):
122
            d[obj] = sys.getrefcount(obj)
6062.2.1 by Francis J. Lacoste
Add garbage debugging.
123
    counts = [(x[1], x[0]) for x in d.items()]
1946 by Canonical.com Patch Queue Manager
[trivial] Memory debug helpers for next time
124
    counts.sort()
5985.5.1 by Francis J. Lacoste
Add readCounts(), deltaCounts() function. Renamed existing mostRefs to classesWithMostRefs, and add new mostRefs() algorithm.
125
    return reversed(counts[-n:])
126
127
128
def mostRefs(n=30):
5985.5.6 by Francis J. Lacoste
English typo.
129
    """Return the n types with the highest reference count.
5985.5.1 by Francis J. Lacoste
Add readCounts(), deltaCounts() function. Renamed existing mostRefs to classesWithMostRefs, and add new mostRefs() algorithm.
130
5985.5.6 by Francis J. Lacoste
English typo.
131
    This one uses a different algorithm than classesWithMostRefs. Instead of
132
    retrieving the number of references to each ClassType, it counts the
133
    number of objects returned by gc.get_objects() and aggregates the results
134
    by type.
5985.5.1 by Francis J. Lacoste
Add readCounts(), deltaCounts() function. Renamed existing mostRefs to classesWithMostRefs, and add new mostRefs() algorithm.
135
136
    :return: A list of tuple (count, type).
137
    """
6062.2.1 by Francis J. Lacoste
Add garbage debugging.
138
    return countsByType(gc.get_objects(), n=n)
139
140
141
def countsByType(objects, n=30):
142
    """Return the n types with the highest instance count in a list.
143
144
    This takes a list of objects and count the number of objects by type.
145
    """
5985.5.1 by Francis J. Lacoste
Add readCounts(), deltaCounts() function. Renamed existing mostRefs to classesWithMostRefs, and add new mostRefs() algorithm.
146
    d = {}
6062.2.1 by Francis J. Lacoste
Add garbage debugging.
147
    for obj in objects:
5985.5.1 by Francis J. Lacoste
Add readCounts(), deltaCounts() function. Renamed existing mostRefs to classesWithMostRefs, and add new mostRefs() algorithm.
148
        if type(obj) is types.InstanceType:
149
            cls = obj.__class__
150
        else:
151
            cls = type(obj)
152
        d[cls] = d.get(cls, 0) + 1
6062.2.1 by Francis J. Lacoste
Add garbage debugging.
153
    counts = [(x[1], x[0]) for x in d.items()]
5985.5.1 by Francis J. Lacoste
Add readCounts(), deltaCounts() function. Renamed existing mostRefs to classesWithMostRefs, and add new mostRefs() algorithm.
154
    counts.sort()
155
    return reversed(counts[-n:])
156
157
158
def deltaCounts(counts1, counts2, n=30):
159
    """Compare two references counts lists and return the increase."""
160
    counts1_map = dict((ref_type, count) for count, ref_type in counts1)
161
    counts2_map = dict((ref_type, count) for count, ref_type in counts2)
162
    types1 = set(counts1_map.keys())
163
    types2 = set(counts2_map.keys())
164
    delta = []
165
    # Types that disappeared.
166
    for ref_type in types1.difference(types2):
167
        delta.append((-counts1_map[ref_type], ref_type))
168
169
    # Types that changed.
170
    for ref_type in types1.intersection(types2):
171
        diff = counts2_map[ref_type] - counts1_map[ref_type]
172
        if diff != 0:
173
            delta.append((diff, ref_type))
174
175
    # New types.
176
    for ref_type in types2.difference(types1):
177
        delta.append((counts2_map[ref_type], ref_type))
178
179
    delta.sort()
180
    return reversed(delta[-n:])
1946 by Canonical.com Patch Queue Manager
[trivial] Memory debug helpers for next time
181
182
183
def printCounts(counts, file=None):
184
    for c, obj in counts:
185
        if file is None:
186
            print c, obj
187
        else:
188
            file.write("%s %s\n" % (c, obj))
189
6062.2.1 by Francis J. Lacoste
Add garbage debugging.
190
def readCounts(file, marker=None):
191
    """Reverse of printCounts().
192
193
    If marker is not None, this will return the counts as soon as a line
194
    containging is encountered. Otherwise, it reads until the end of file.
195
    """
5985.5.1 by Francis J. Lacoste
Add readCounts(), deltaCounts() function. Renamed existing mostRefs to classesWithMostRefs, and add new mostRefs() algorithm.
196
    counts = []
6062.2.1 by Francis J. Lacoste
Add garbage debugging.
197
    # We read one line at a time because we want the file pointer to
198
    # be after the marker line if we encounters it.
199
    while True:
200
        line = file.readline()
201
        if not line or (marker is not None and line == marker):
202
            break
5985.5.1 by Francis J. Lacoste
Add readCounts(), deltaCounts() function. Renamed existing mostRefs to classesWithMostRefs, and add new mostRefs() algorithm.
203
        count, ref_type = line.strip().split(' ', 1)
204
        counts.append((int(count), ref_type))
205
    return counts
206
1946 by Canonical.com Patch Queue Manager
[trivial] Memory debug helpers for next time
207
208
def logInThread(n=30):
209
    reflog = file('/tmp/refs.log','w')
210
    t = threading.Thread(target=_logRefsEverySecond, args=(reflog, n))
6062.2.1 by Francis J. Lacoste
Add garbage debugging.
211
    # Allow process to exit without explicitly stopping thread.
212
    t.setDaemon(True)
1946 by Canonical.com Patch Queue Manager
[trivial] Memory debug helpers for next time
213
    t.start()
214
215
216
def _logRefsEverySecond(log, n):
217
    while True:
218
        printCounts(mostRefs(n=n), file=log)
219
        log.write('\n')
220
        log.flush()
221
        time.sleep(1)
222
5985.5.1 by Francis J. Lacoste
Add readCounts(), deltaCounts() function. Renamed existing mostRefs to classesWithMostRefs, and add new mostRefs() algorithm.
223
6062.2.1 by Francis J. Lacoste
Add garbage debugging.
224
if __name__ == "__main__":
1946 by Canonical.com Patch Queue Manager
[trivial] Memory debug helpers for next time
225
    counts = mostRefs()
226
    printCounts(counts)
227
228
    gc.enable()
229
    gc.set_debug(gc.DEBUG_LEAK)
230
231
    # make a leak
232
    l = []
233
    l.append(l)
234
    del l
235
236
    # show the dirt ;-)
237
    dump_garbage()
4785.3.7 by Jeroen Vermeulen
Removed whitespace at ends of lines
238