| 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')
|