summaryrefslogtreecommitdiff
path: root/spicedump.py
blob: 9f7c8bb6233c0f1c0a12cc14012fda6f9ba3bc1d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
#!/usr/bin/env python
import sys
from collections import defaultdict
from itertools import izip_longest
from time import time
import argparse
import textwrap
import logging
import re

import pcaputil
from proxy import proxy, closeallsockets

dt = 1.0
verbose = 0

class Histogram(defaultdict):
    def __init__(self):
        super(Histogram, self).__init__(lambda: (0,0))
        self.last = defaultdict(lambda: (0,0))
    def show(self):
        for t,k in sorted((t,k) for k,(t,c) in self.items()):
            diff = self[k][1] - self.last[k][1]
            yield '%20s: %6d %4d' % (k, self[k][1], diff)
        self.last.update(self)

def linesmerge(*line_sources):
    for same_height_lines in izip_longest(*line_sources):
        yield ''.join([x for x in same_height_lines if x is not None])

def show(*line_sources):
    print "------------------------------------------------"
    for line in linesmerge(*line_sources):
        print line

class SurfaceStatistics(object):
    def __init__(self):
        self.s = defaultdict(lambda: defaultdict(lambda: 0))
        self.current = set()
        self.streams = set()
        self.known_ops = dict(surface_create=self.on_surface_create,
            surface_destroy=self.on_surface_destroy,
            stream_create=self.on_stream_create)
    def on_stream_create(self, sid):
        self.streams.add(sid)
    def on_surface_destroy(self, sid):
        if sid in self.current:
            self.current.remove(sid)
    def on_surface_create(self, sid):
        self.current.add(sid)
    def add(self, sid, op):
        self.s[sid][op] += 1
        if op not in self.known_ops:
            import pdb; pdb.set_trace()
        else:
            self.known_ops[op](sid)
    def show(self):
        all_surfaces = self.s.keys()
        ds = self.s.values()
        ops = set(sum([d.keys() for d in ds], []))
        yield '%20s,%5s' % (len(self.current), len(self.streams))
        per_surface = dict([(o,
            sorted(set([
                sid for sid, d in self.s.items() if d[o] > 0
                       ]))) for o in ops])
        for k, v in per_surface.items():
            if k == 'stream_create' and len(v) > 1:
                import pdb; pdb.set_trace()
            for i, l in enumerate(textwrap.wrap(','.join(map(str, v)), width=40)):
                yield '%20s: %s' % (k if i == 0 else '', l)

def debug_break():
    import rpdb2; rpdb2.start_embedded_debugger('a')

def spicedump(p, opts, stdscr=None):
    show_every_command = True
    periodic_hist = False
    import pcapspice
    spice = pcapspice.spice_iter(p)
    hist = Histogram()
    surface_stat = SurfaceStatistics()
    last_print = start_time = time()
    messages = []
    filter_exp = opts.filter
    if filter_exp:
        if filter_exp[0] == '-':
            def filter(result_name, exp=re.compile(filter_exp[1:])):
                return exp.match(result_name)
        else:
            def filter(result_name, exp=re.compile(filter_exp)):
                return not exp.match(result_name)
    if stdscr:
        stdscr.erase()
    # replace the "for d in spice:" loop with a select
    while True:
        #rds, _ws, _xs = select([p.fileno()],[],[],dt)
        cur_time = time()
        do_read = True # = len(rds) > 0:
        if do_read:
            d = spice.next()
            result_name = d.msg.data.result_name
            result_value = d.msg.data.result_value
            if filter_exp and filter(result_name):
                continue
            if hasattr(p, 'drop_next'):
                pass
                #if result_name == 'draw_copy':
                #    print "dropping %s" % result_name
                #    p.drop_next()
            if (any(x in result_name for x in ['surface', 'stream'])
                and not result_name in
                    ['stream_data', 'stream_clip', 'stream_destroy', 'stream_destroy_all']):
                msg_d = dict(result_value)
                if 'surface_id' in msg_d:
                    surface_stat.add(msg_d['surface_id'], result_name)
                    if msg_d['surface_id'] != 0 and 'stream' in result_name:
                        messages.append("non zero surface id in stream: %s" % result_value)
                else:
                    import pdb; pdb.set_trace()
            if verbose:
                messages.extend(str(d).split('\n'))
            old_time, old_count = hist[result_name]
            hist[result_name] = (cur_time, old_count + 1)
        if show_every_command:
            print result_name
        if periodic_hist and cur_time - last_print > dt:
            show(hist.show(), surface_stat.show())
            print '\n'.join(messages[-20:])
            last_print = cur_time

def frompcap(opts, stdscr=None):
    p = pcaputil.packet_iter('lo')
    return spicedump(p, opts, stdscr)

def fromproxy(stdscr, local_port, remote_addr, opts):
    p = proxy(local_port=local_port, remote_addr=remote_addr)
    return spicedump(p, opts, stdscr=stdscr)

if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument('-p', '--proxy', dest='proxy', help='use proxy',
        action='store_true')
    parser.add_argument('-l', '--local-port', type=int, help='set proxy local port')
    parser.add_argument('-H', '--remote-host', default='localhost', help='set proxy remote address')
    parser.add_argument('-p', '--remote-port', type=int, required=True, help='set proxy remote address')
    parser.add_argument('-v', '--verbose', dest='verbose', action='count', help='verbosity', default=0)
    parser.add_argument('-c', '--curses', dest='curses', action='store_true', help='use curses')
    parser.add_argument('-f', '--filter', dest='filter', help='filter messages')
    parser.add_argument('--record', dest='record', help='TODO: record to file, can be used to playback')
    parser.add_argument('--playback', dest='playback', help='TODO: playback a previously recorded file')
    opts, rest = parser.parse_known_args(sys.argv[1:])
    if opts.verbose >= 2 in sys.argv:
        logging.basicConfig(filename='spicedump.log', level=logging.DEBUG)
        print "saving debug log to spicedump.log"
    if opts.proxy:
        local_port = opts.local_port
        remote_addr = (opts.remote_host, opts.remote_port)
        main = (lambda stdscr, opts, local_port=local_port, remote_addr=remote_addr:
                    fromproxy(stdscr, local_port, remote_addr, opts))
    else:
        main = frompcap
    verbose = opts.verbose
    try:
        if opts.curses in sys.argv:
            import curses
            curses.wrapper(lambda stdscr, opts=opts: main(stdscr=stdscr, opts=opts))
        else:
            main(stdscr=None, opts=opts)
    except KeyboardInterrupt, e:
        # XXX - ctrl-c doesn't reach here :(
        closeallsockets()