blob: 848bae5658ca3ab266df5e4ded7dbc3f0c94c862 [file] [log] [blame]
Benjamin Peterson90f5ba52010-03-11 22:53:45 +00001#! /usr/bin/env python3
Tim Peters21d7d4d2003-04-18 00:45:59 +00002
3"""
4combinerefs path
5
6A helper for analyzing PYTHONDUMPREFS output.
7
8When the PYTHONDUMPREFS envar is set in a debug build, at Python shutdown
Martin Panterb4ce1fc2015-11-30 03:18:29 +00009time Py_FinalizeEx() prints the list of all live objects twice: first it
Tim Peters21d7d4d2003-04-18 00:45:59 +000010prints the repr() of each object while the interpreter is still fully intact.
11After cleaning up everything it can, it prints all remaining live objects
12again, but the second time just prints their addresses, refcounts, and type
Tim Peters53f72d72003-04-19 18:21:04 +000013names (because the interpreter has been torn down, calling repr methods at
14this point can get into infinite loops or blow up).
Tim Peters21d7d4d2003-04-18 00:45:59 +000015
16Save all this output into a file, then run this script passing the path to
17that file. The script finds both output chunks, combines them, then prints
18a line of output for each object still alive at the end:
19
20 address refcnt typename repr
21
22address is the address of the object, in whatever format the platform C
23produces for a %p format code.
24
25refcnt is of the form
26
27 "[" ref "]"
28
29when the object's refcount is the same in both PYTHONDUMPREFS output blocks,
30or
31
32 "[" ref_before "->" ref_after "]"
33
34if the refcount changed.
35
Victor Stinner8182cc22020-07-10 12:40:38 +020036typename is Py_TYPE(object)->tp_name, extracted from the second PYTHONDUMPREFS
Tim Peters21d7d4d2003-04-18 00:45:59 +000037output block.
38
39repr is repr(object), extracted from the first PYTHONDUMPREFS output block.
Tim Peters53f72d72003-04-19 18:21:04 +000040CAUTION: If object is a container type, it may not actually contain all the
41objects shown in the repr: the repr was captured from the first output block,
42and some of the containees may have been released since then. For example,
43it's common for the line showing the dict of interned strings to display
Martin Panterb4ce1fc2015-11-30 03:18:29 +000044strings that no longer exist at the end of Py_FinalizeEx; this can be recognized
Tim Peters53f72d72003-04-19 18:21:04 +000045(albeit painfully) because such containees don't have a line of their own.
Tim Peters21d7d4d2003-04-18 00:45:59 +000046
47The objects are listed in allocation order, with most-recently allocated
48printed first, and the first object allocated printed last.
49
50
51Simple examples:
52
53 00857060 [14] str '__len__'
54
55The str object '__len__' is alive at shutdown time, and both PYTHONDUMPREFS
56output blocks said there were 14 references to it. This is probably due to
57C modules that intern the string "__len__" and keep a reference to it in a
58file static.
59
60 00857038 [46->5] tuple ()
61
6246-5 = 41 references to the empty tuple were removed by the cleanup actions
63between the times PYTHONDUMPREFS produced output.
64
65 00858028 [1025->1456] str '<dummy key>'
66
Tim Peters53f72d72003-04-19 18:21:04 +000067The string '<dummy key>', which is used in dictobject.c to overwrite a real
68key that gets deleted, grew several hundred references during cleanup. It
69suggests that stuff did get removed from dicts by cleanup, but that the dicts
70themselves are staying alive for some reason. """
Tim Peters21d7d4d2003-04-18 00:45:59 +000071
72import re
73import sys
74
75# Generate lines from fileiter. If whilematch is true, continue reading
76# while the regexp object pat matches line. If whilematch is false, lines
77# are read so long as pat doesn't match them. In any case, the first line
78# that doesn't match pat (when whilematch is true), or that does match pat
79# (when whilematch is false), is lost, and fileiter will resume at the line
80# following it.
81def read(fileiter, pat, whilematch):
Tim Peters21d7d4d2003-04-18 00:45:59 +000082 for line in fileiter:
83 if bool(pat.match(line)) == whilematch:
Tim Peters8d17a902003-04-18 01:02:37 +000084 yield line
Tim Peters21d7d4d2003-04-18 00:45:59 +000085 else:
86 break
Tim Peters21d7d4d2003-04-18 00:45:59 +000087
Serhiy Storchaka172bb392019-03-30 08:33:02 +020088def combinefile(f):
Tim Peters21d7d4d2003-04-18 00:45:59 +000089 fi = iter(f)
90
91 for line in read(fi, re.compile(r'^Remaining objects:$'), False):
92 pass
93
94 crack = re.compile(r'([a-zA-Z\d]+) \[(\d+)\] (.*)')
95 addr2rc = {}
96 addr2guts = {}
97 before = 0
98 for line in read(fi, re.compile(r'^Remaining object addresses:$'), False):
99 m = crack.match(line)
100 if m:
101 addr, addr2rc[addr], addr2guts[addr] = m.groups()
102 before += 1
103 else:
Collin Winter6afaeb72007-08-03 17:06:41 +0000104 print('??? skipped:', line)
Tim Peters21d7d4d2003-04-18 00:45:59 +0000105
106 after = 0
107 for line in read(fi, crack, True):
108 after += 1
109 m = crack.match(line)
110 assert m
111 addr, rc, guts = m.groups() # guts is type name here
112 if addr not in addr2rc:
Collin Winter6afaeb72007-08-03 17:06:41 +0000113 print('??? new object created while tearing down:', line.rstrip())
Tim Peters21d7d4d2003-04-18 00:45:59 +0000114 continue
Collin Winter6afaeb72007-08-03 17:06:41 +0000115 print(addr, end=' ')
Tim Peters21d7d4d2003-04-18 00:45:59 +0000116 if rc == addr2rc[addr]:
Collin Winter6afaeb72007-08-03 17:06:41 +0000117 print('[%s]' % rc, end=' ')
Tim Peters21d7d4d2003-04-18 00:45:59 +0000118 else:
Collin Winter6afaeb72007-08-03 17:06:41 +0000119 print('[%s->%s]' % (addr2rc[addr], rc), end=' ')
120 print(guts, addr2guts[addr])
Tim Peters21d7d4d2003-04-18 00:45:59 +0000121
Collin Winter6afaeb72007-08-03 17:06:41 +0000122 print("%d objects before, %d after" % (before, after))
Tim Peters21d7d4d2003-04-18 00:45:59 +0000123
Serhiy Storchaka172bb392019-03-30 08:33:02 +0200124def combine(fname):
125 with open(fname) as f:
126 combinefile(f)
127
Tim Peters21d7d4d2003-04-18 00:45:59 +0000128if __name__ == '__main__':
129 combine(sys.argv[1])