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

180 lines, 83 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 os
41import sys
42
43
44def log(msg, *args):
45 if args:
46 msg = msg % args
47 print(msg, file=sys.stderr)
48
49
50num_allocs = 0
51num_lists = 0
52
53gOutDir = None
54
55
56class 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
109gStats = None
110
111
112def 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
124def 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
167def uftrace_exit(ctx):
168 """Function exit"""
169 pass
170
171
172def 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')