Projet

Général

Profil

Paste
Télécharger au format
Statistiques
| Branche: | Révision:

root / tools / profiling / munin-profile-node.py @ bde90ba9

Historique | Voir | Annoter | Télécharger (3,18 ko)

1
#!/usr/bin/python
2
# Copyright (C) 2013 Helmut Grohne <helmut@subdivi.de>
3
# License: GPLv2 like the rest of munin
4
"""
5
Usage:
6
    tcpdump -npi lo "tcp port 4949" -w munin.pcap
7
    # wait for one munin run, then press Ctrl-C
8
    ./munin-profile-node.py munin.pcap
9
"""
10

    
11
import collections
12
import sys
13
from scapy.utils import rdpcap
14
import scapy.layers.l2
15
from scapy.layers.inet import IP, TCP
16

    
17
class ConnectionProfile:
18
    """
19
    @ivar times: mapping of commands to durations waiting for answers in
20
        seconds
21
    @type times: {str: [float]}
22
    @ivar idles: list of durations waiting for client in seconds
23
    @type idles: [float]
24
    """
25
    def __init__(self):
26
        self.times = dict()
27
        self.idles = []
28
        self.curcommand = None
29
        self.commandstart = None
30

    
31
    def handle_to_node(self, timestamp, line):
32
        if self.curcommand is None and self.commandstart is not None:
33
            self.idles.append(timestamp - self.commandstart)
34
        self.curcommand = line
35
        self.commandstart = timestamp
36

    
37
    def handle_from_node(self, timestamp, line):
38
        if line != ".":
39
            return
40
        if self.curcommand is None:
41
            return
42
        duration = timestamp - self.commandstart
43
        self.times.setdefault(self.curcommand, []).append(duration)
44
        self.curcommand = None
45
        self.commandstart = timestamp
46

    
47
class MuninProfiler:
48
    def __init__(self):
49
        self.to_node = ""
50
        self.from_node = ""
51
        self.connprof = collections.defaultdict(ConnectionProfile)
52

    
53
    def handle_packet(self, packet):
54
        payload = str(packet[TCP].payload)
55
        if not payload:
56
            return
57
        if packet[TCP].dport == 4949:
58
            self.to_node += payload
59
            conn = (packet[IP].src, packet[TCP].sport, packet[IP].dst)
60
        elif packet[TCP].sport == 4949:
61
            self.from_node += payload
62
            conn = (packet[IP].dst, packet[TCP].dport, packet[IP].src)
63
        else:
64
            return
65
        lines = self.to_node.split("\n")
66
        self.to_node = lines.pop()
67
        for line in lines:
68
            self.connprof[conn].handle_to_node(packet.time, line)
69
        lines = self.from_node.split("\n")
70
        self.from_node = lines.pop()
71
        for line in lines:
72
            self.connprof[conn].handle_from_node(packet.time, line)
73

    
74
    @property
75
    def times(self):
76
        times = dict()
77
        for prof in self.connprof.values():
78
            for com, durations in prof.times.items():
79
                times.setdefault(com, []).extend(durations)
80
        return times
81

    
82
    @property
83
    def idles(self):
84
        return sum((prof.idles for prof in self.connprof.values()), [])
85

    
86
def main():
87
    mp = MuninProfiler()
88
    for pkt in rdpcap(sys.argv[1]):
89
        mp.handle_packet(pkt)
90
    print("Client idle time during connection: %.2fs" % sum(mp.idles))
91
    times = [(key, sum(value)) for key, value in mp.times.items()]
92
    times.sort(key=lambda tpl: -tpl[1])
93
    total = sum(value for key, value in times)
94
    print("Total time waiting for the node:    %.2fs" % total)
95
    print("Top 10 plugins using most of the time")
96
    for key, value in times[:10]:
97
        print("%-40s: %.2fs (%d%%)" % (key, value, 100 * value / total))
98

    
99
if __name__ == '__main__':
100
    main()