Add pcap dump parser for extracting S3 performance measurements.
[bluesky.git] / parsetrace / parse.py
1 #!/usr/bin/python
2
3 import impacket, pcapy, re, sys
4 import impacket.ImpactDecoder
5
6 start_time = None
7
8 flows = {}
9
10 STATE_START = 0
11 STATE_REQ_SENT = 1
12 STATE_REQ_ACKED = 2
13 STATE_RESP_START = 3
14
15 logfile = open('times.data', 'w')
16
17 def seq_after(x, y):
18     """Compares whether x >= y in sequence number space."""
19
20     delta = (x - y) % (1 << 32)
21     return delta < (1 << 31)
22
23 class Connection:
24     counter = 0
25
26     def __init__(self, endpoints):
27         self.endpoints = endpoints
28         self.packets = []
29         self.state = STATE_START
30         self.id = Connection.counter
31         self.times = []
32         self.transfer_count = 0
33         Connection.counter += 1
34
35     def finish_transfer(self):
36         if len(self.times) > 0:
37             rtt = self.times[0][0]
38             try:
39                 start = iter(t[0] for t in self.times if t[1] > 0).next()
40             except:
41                 start = 0.0
42             end = self.times[-1][0]
43             data = self.times[-1][1]
44             print "Connection %d Transfer #%d" % (self.id, self.transfer_count)
45             print "Network RTT:", rtt
46             print "Additional response delay:", start - rtt
47             print "Transfer time:", end - start
48             print "Bandwidth:", data / (end - start)
49             print
50             logfile.write("%d\t%d\t%d\t%f\t%f\t%f\n"
51                           % (self.id, self.transfer_count, data,
52                               rtt, start - rtt, end - start))
53             self.transfer_count += 1
54         self.times = []
55         self.state = STATE_START
56
57     def process(self, timestamp, packet):
58         ip = pkt.child()
59         tcp = ip.child()
60         self.packets.append(packet)
61
62         datalen = ip.get_ip_len() - ip.get_header_size() - tcp.get_header_size()
63         data = tcp.get_data_as_string()[0:datalen]
64
65         if tcp.get_th_sport() == 80:
66             # Incoming packet
67             direction = -1
68         elif tcp.get_th_dport() == 80:
69             # Outgoing packet
70             direction = 1
71         else:
72             direction = 0
73
74         seq = (tcp.get_th_seq(), tcp.get_th_seq() + datalen)
75         ack = tcp.get_th_ack()
76
77         # Previous request finished
78         if self.state == STATE_RESP_START and direction > 0 \
79                 and data.startswith('GET /'):
80             self.finish_transfer()
81
82         # New request seen on an idle connection...
83         if self.state == STATE_START and direction > 0 \
84                 and data.startswith('GET /'):
85             self.startseq = seq[1]
86             self.starttime = timestamp
87             self.state = STATE_REQ_SENT
88
89         # Request is acknowledged, but response not yet seen
90         if self.state == STATE_REQ_SENT and direction < 0 \
91                 and seq_after(ack, self.startseq):
92             self.state = STATE_REQ_ACKED
93             self.respseq = seq[0]
94             self.times.append(((timestamp - self.starttime) / 1e6, 0))
95
96         # Response header to request has been seen
97         if self.state == STATE_REQ_ACKED and direction < 0 \
98                 and data.startswith("HTTP/1."):
99             self.state = STATE_RESP_START
100
101         # Data packet in response
102         if self.state == STATE_RESP_START and direction < 0 and datalen > 0:
103             self.times.append(((timestamp - self.starttime) / 1e6,
104                                seq[1] - self.respseq))
105
106 def handler(header, data):
107     global start_time
108     global pkt
109     (sec, us) = header.getts()
110     ts = sec * 1000000 + us
111     if start_time is None:
112         start_time = ts
113     ts -= start_time
114     pkt = decoder.decode(data)
115
116     ip = pkt.child()
117     tcp = ip.child()
118     src = (ip.get_ip_src(), tcp.get_th_sport())
119     dst = (ip.get_ip_dst(), tcp.get_th_dport())
120     flow = tuple(sorted([src, dst]))
121     if flow not in flows:
122         #print "New flow", flow
123         flows[flow] = Connection(flow)
124
125     flows[flow].process(ts, pkt)
126
127 def process(filename):
128     global decoder
129     p = pcapy.open_offline(filename)
130     p.setfilter(r"ip proto \tcp")
131     assert p.datalink() == pcapy.DLT_EN10MB
132     decoder = impacket.ImpactDecoder.EthDecoder()
133     p.loop(0, handler)
134
135     for c in flows.values():
136         c.finish_transfer()
137
138 if __name__ == '__main__':
139     for f in sys.argv[1:]:
140         process(f)