OILS / benchmarks / uftrace_allocs.py View on Github | oilshell.org

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