1# This file is part of pybootchartgui. 2 3# pybootchartgui is free software: you can redistribute it and/or modify 4# it under the terms of the GNU General Public License as published by 5# the Free Software Foundation, either version 3 of the License, or 6# (at your option) any later version. 7 8# pybootchartgui is distributed in the hope that it will be useful, 9# but WITHOUT ANY WARRANTY; without even the implied warranty of 10# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11# GNU General Public License for more details. 12 13# You should have received a copy of the GNU General Public License 14# along with pybootchartgui. If not, see <http://www.gnu.org/licenses/>. 15 16 17class DiskStatSample: 18 def __init__(self, time): 19 self.time = time 20 self.diskdata = [0, 0, 0] 21 def add_diskdata(self, new_diskdata): 22 self.diskdata = [ a + b for a, b in zip(self.diskdata, new_diskdata) ] 23 24class CPUSample: 25 def __init__(self, time, user, sys, io = 0.0, swap = 0.0): 26 self.time = time 27 self.user = user 28 self.sys = sys 29 self.io = io 30 self.swap = swap 31 32 @property 33 def cpu(self): 34 return self.user + self.sys 35 36 def __str__(self): 37 return str(self.time) + "\t" + str(self.user) + "\t" + \ 38 str(self.sys) + "\t" + str(self.io) + "\t" + str (self.swap) 39 40 41class NetSample: 42 def __init__(self, time, iface, received_bytes, transmitted_bytes, receive_diff, transmit_diff): 43 self.time = time 44 self.iface = iface 45 self.received_bytes = received_bytes 46 self.transmitted_bytes = transmitted_bytes 47 self.receive_diff = receive_diff 48 self.transmit_diff = transmit_diff 49 50class CPUPressureSample: 51 def __init__(self, time, avg10, avg60, avg300, deltaTotal): 52 self.time = time 53 self.avg10 = avg10 54 self.avg60 = avg60 55 self.avg300 = avg300 56 self.deltaTotal = deltaTotal 57 58class IOPressureSample: 59 def __init__(self, time, avg10, avg60, avg300, deltaTotal): 60 self.time = time 61 self.avg10 = avg10 62 self.avg60 = avg60 63 self.avg300 = avg300 64 self.deltaTotal = deltaTotal 65 66class MemPressureSample: 67 def __init__(self, time, avg10, avg60, avg300, deltaTotal): 68 self.time = time 69 self.avg10 = avg10 70 self.avg60 = avg60 71 self.avg300 = avg300 72 self.deltaTotal = deltaTotal 73 74 75class MemSample: 76 used_values = ('MemTotal', 'MemFree', 'Buffers', 'Cached', 'SwapTotal', 'SwapFree',) 77 78 def __init__(self, time): 79 self.time = time 80 self.records = {} 81 82 def add_value(self, name, value): 83 if name in MemSample.used_values: 84 self.records[name] = value 85 86 def valid(self): 87 keys = self.records.keys() 88 # discard incomplete samples 89 return [v for v in MemSample.used_values if v not in keys] == [] 90 91class DrawMemSample: 92 """ 93 Condensed version of a MemSample with exactly the values used by the drawing code. 94 Initialized either from a valid MemSample or 95 a tuple/list of buffer/used/cached/swap values. 96 """ 97 def __init__(self, mem_sample): 98 self.time = mem_sample.time 99 if isinstance(mem_sample, MemSample): 100 self.buffers = mem_sample.records['MemTotal'] - mem_sample.records['MemFree'] 101 self.used = mem_sample.records['MemTotal'] - mem_sample.records['MemFree'] - mem_sample.records['Buffers'] 102 self.cached = mem_sample.records['Cached'] 103 self.swap = mem_sample.records['SwapTotal'] - mem_sample.records['SwapFree'] 104 else: 105 self.buffers, self.used, self.cached, self.swap = mem_sample 106 107class DiskSpaceSample: 108 def __init__(self, time): 109 self.time = time 110 self.records = {} 111 112 def add_value(self, name, value): 113 self.records[name] = value 114 115 def valid(self): 116 return bool(self.records) 117 118class ProcessSample: 119 def __init__(self, time, state, cpu_sample): 120 self.time = time 121 self.state = state 122 self.cpu_sample = cpu_sample 123 124 def __str__(self): 125 return str(self.time) + "\t" + str(self.state) + "\t" + str(self.cpu_sample) 126 127class ProcessStats: 128 def __init__(self, writer, process_map, sample_count, sample_period, start_time, end_time): 129 self.process_map = process_map 130 self.sample_count = sample_count 131 self.sample_period = sample_period 132 self.start_time = start_time 133 self.end_time = end_time 134 writer.info ("%d samples, avg. sample length %f" % (self.sample_count, self.sample_period)) 135 writer.info ("process list size: %d" % len (self.process_map.values())) 136 137class Process: 138 def __init__(self, writer, pid, cmd, ppid, start_time): 139 self.writer = writer 140 self.pid = pid 141 self.cmd = cmd 142 self.exe = cmd 143 self.args = [] 144 self.ppid = ppid 145 self.start_time = start_time 146 self.duration = 0 147 self.samples = [] 148 self.parent = None 149 self.child_list = [] 150 151 self.active = None 152 self.last_user_cpu_time = None 153 self.last_sys_cpu_time = None 154 155 self.last_cpu_ns = 0 156 self.last_blkio_delay_ns = 0 157 self.last_swapin_delay_ns = 0 158 159 # split this process' run - triggered by a name change 160 def split(self, writer, pid, cmd, ppid, start_time): 161 split = Process (writer, pid, cmd, ppid, start_time) 162 163 split.last_cpu_ns = self.last_cpu_ns 164 split.last_blkio_delay_ns = self.last_blkio_delay_ns 165 split.last_swapin_delay_ns = self.last_swapin_delay_ns 166 167 return split 168 169 def __str__(self): 170 return " ".join([str(self.pid), self.cmd, str(self.ppid), '[ ' + str(len(self.samples)) + ' samples ]' ]) 171 172 def calc_stats(self, samplePeriod): 173 if self.samples: 174 firstSample = self.samples[0] 175 lastSample = self.samples[-1] 176 self.start_time = min(firstSample.time, self.start_time) 177 self.duration = lastSample.time - self.start_time + samplePeriod 178 179 activeCount = sum( [1 for sample in self.samples if sample.cpu_sample and sample.cpu_sample.sys + sample.cpu_sample.user + sample.cpu_sample.io > 0.0] ) 180 activeCount = activeCount + sum( [1 for sample in self.samples if sample.state == 'D'] ) 181 self.active = (activeCount>2) 182 183 def calc_load(self, userCpu, sysCpu, interval): 184 userCpuLoad = float(userCpu - self.last_user_cpu_time) / interval 185 sysCpuLoad = float(sysCpu - self.last_sys_cpu_time) / interval 186 cpuLoad = userCpuLoad + sysCpuLoad 187 # normalize 188 if cpuLoad > 1.0: 189 userCpuLoad = userCpuLoad / cpuLoad 190 sysCpuLoad = sysCpuLoad / cpuLoad 191 return (userCpuLoad, sysCpuLoad) 192 193 def set_parent(self, processMap): 194 if self.ppid != None: 195 self.parent = processMap.get (self.ppid) 196 if self.parent == None and self.pid // 1000 > 1 and \ 197 not (self.ppid == 2000 or self.pid == 2000): # kernel threads: ppid=2 198 self.writer.warn("Missing CONFIG_PROC_EVENTS: no parent for pid '%i' ('%s') with ppid '%i'" \ 199 % (self.pid,self.cmd,self.ppid)) 200 201 def get_end_time(self): 202 return self.start_time + self.duration 203 204class DiskSample: 205 def __init__(self, time, read, write, util): 206 self.time = time 207 self.read = read 208 self.write = write 209 self.util = util 210 self.tput = read + write 211 212 def __str__(self): 213 return "\t".join([str(self.time), str(self.read), str(self.write), str(self.util)]) 214