1 | """
|
2 | uftrace_allocs.py - Python 3 plugin for uftrace
|
3 |
|
4 | Count allocations and show sizes.
|
5 |
|
6 | Annoying thing about uftrace: it swallows ImportError and other errors!
|
7 |
|
8 | TODO:
|
9 | Attribute allocations and sizes to Str, List, Dict, Token, etc.
|
10 | How do we do that? We need the call graph relationship
|
11 |
|
12 | Structures to catch:
|
13 |
|
14 | NewStr(12) {
|
15 | MarkSweepHeap::Allocate(25)
|
16 | }
|
17 |
|
18 | Alloc() {
|
19 | MarkSweepHeap::Allocate(24);
|
20 | syntax_asdl::Token::Token();
|
21 | }
|
22 |
|
23 | Alloc() {
|
24 | MarkSweepHeap::Allocate(24)
|
25 | List::List()
|
26 | # But what type is it? We don't know
|
27 | }
|
28 |
|
29 | // Some stuff missing here
|
30 | Alloc() {
|
31 | MarkSweepHeap::Allocate(32);
|
32 | Alloc() {
|
33 | MarkSweepHeap::Allocate(24);
|
34 | List::List();
|
35 | }
|
36 | }
|
37 | """
|
38 | from __future__ import print_function
|
39 |
|
40 | import os
|
41 | import sys
|
42 |
|
43 |
|
44 | def log(msg, *args):
|
45 | if args:
|
46 | msg = msg % args
|
47 | print(msg, file=sys.stderr)
|
48 |
|
49 |
|
50 | num_allocs = 0
|
51 | num_lists = 0
|
52 |
|
53 | gOutDir = None
|
54 |
|
55 |
|
56 | class Stats(object):
|
57 |
|
58 | def __init__(self, out_dir):
|
59 | p = os.path.join(out_dir, 'all-untyped.tsv')
|
60 | self.untyped = open(p, 'w')
|
61 | header = ['obj_len']
|
62 | print('\t'.join(header), file=self.untyped)
|
63 |
|
64 | p = os.path.join(out_dir, 'typed.tsv')
|
65 | self.typed = open(p, 'w')
|
66 | header = ['func_name']
|
67 | print('\t'.join(header), file=self.typed)
|
68 |
|
69 | p = os.path.join(out_dir, 'strings.tsv')
|
70 | self.strings = open(p, 'w')
|
71 | header = ['func_name', 'str_len']
|
72 | print('\t'.join(header), file=self.strings)
|
73 |
|
74 | # Note: we could extract Slab type
|
75 | p = os.path.join(out_dir, 'slabs.tsv')
|
76 | self.slabs = open(p, 'w')
|
77 | header = ['func_name', 'slab_len']
|
78 | print('\t'.join(header), file=self.slabs)
|
79 |
|
80 | # For the actual number of items
|
81 | p = os.path.join(out_dir, 'reserve.tsv')
|
82 | self.reserve = open(p, 'w')
|
83 | header = ['func_name', 'num_items']
|
84 | print('\t'.join(header), file=self.reserve)
|
85 |
|
86 | def EmitUntyped(self, obj_len):
|
87 | print('%d' % (obj_len), file=self.untyped)
|
88 |
|
89 | def EmitTyped(self, func):
|
90 | print('%s' % (func), file=self.typed)
|
91 |
|
92 | def EmitString(self, func, str_len):
|
93 | print('%s\t%d' % (func, str_len), file=self.strings)
|
94 |
|
95 | def EmitSlab(self, func, slab_len):
|
96 | print('%s\t%d' % (func, slab_len), file=self.slabs)
|
97 |
|
98 | def EmitReserve(self, func, num_items):
|
99 | print('%s\t%d' % (func, num_items), file=self.reserve)
|
100 |
|
101 | def Close(self):
|
102 | self.untyped.close()
|
103 | self.typed.close()
|
104 | self.strings.close()
|
105 | self.slabs.close()
|
106 | self.reserve.close()
|
107 |
|
108 |
|
109 | gStats = None
|
110 |
|
111 |
|
112 | def uftrace_begin(ctx):
|
113 | """Script begin"""
|
114 |
|
115 | #print(ctx)
|
116 | args = ctx['cmds']
|
117 | #log('args %r', args)
|
118 | out_dir = args[0]
|
119 |
|
120 | global gStats
|
121 | gStats = Stats(out_dir)
|
122 |
|
123 |
|
124 | def uftrace_entry(ctx):
|
125 | """Function entry"""
|
126 | global num_allocs
|
127 |
|
128 | func_name = ctx["name"]
|
129 |
|
130 | #print(ctx)
|
131 | #log('f %r', func_name)
|
132 |
|
133 | if func_name.startswith('MarkSweepHeap::Allocate'):
|
134 | #log("MSW !!")
|
135 | num_bytes = ctx['args'][0]
|
136 | #log("MSW %r %s", num_bytes, type(num_bytes))
|
137 | gStats.EmitUntyped(num_bytes)
|
138 | num_allocs += 1
|
139 | return
|
140 |
|
141 | if 'Alloc<' in func_name:
|
142 | # TODO: We don't have the size
|
143 | gStats.EmitTyped(func_name)
|
144 | return
|
145 |
|
146 | if func_name.startswith('NewStr') or func_name.startswith(
|
147 | 'OverAllocatedStr'):
|
148 | #log("Str")
|
149 | str_len = ctx['args'][0]
|
150 | #log("Str %d", str_len)
|
151 | gStats.EmitString(func_name, str_len)
|
152 | return
|
153 |
|
154 | if 'NewSlab<' in func_name:
|
155 | #log('SLAB %r', func_name)
|
156 | slab_len = ctx['args'][0]
|
157 | #log('len %d', slab_len)
|
158 | gStats.EmitSlab(func_name, slab_len)
|
159 | return
|
160 |
|
161 | if '::reserve(' in func_name:
|
162 | num_items = ctx['args'][0]
|
163 | gStats.EmitReserve(func_name, num_items)
|
164 | return
|
165 |
|
166 |
|
167 | def uftrace_exit(ctx):
|
168 | """Function exit"""
|
169 | pass
|
170 |
|
171 |
|
172 | def uftrace_end():
|
173 | log('num MarkSweepHeap::Allocate() = %d', num_allocs)
|
174 |
|
175 | gStats.Close()
|
176 |
|
177 | #print('zz', file=sys.stderr)
|
178 |
|
179 |
|
180 | #print('hi')
|