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 40class MemSample: 41 used_values = ('MemTotal', 'MemFree', 'Buffers', 'Cached', 'SwapTotal', 'SwapFree',) 42 43 def __init__(self, time): 44 self.time = time 45 self.records = {} 46 47 def add_value(self, name, value): 48 if name in MemSample.used_values: 49 self.records[name] = value 50 51 def valid(self): 52 keys = self.records.keys() 53 # discard incomplete samples 54 return [v for v in MemSample.used_values if v not in keys] == [] 55 56class DrawMemSample: 57 """ 58 Condensed version of a MemSample with exactly the values used by the drawing code. 59 Initialized either from a valid MemSample or 60 a tuple/list of buffer/used/cached/swap values. 61 """ 62 def __init__(self, mem_sample): 63 self.time = mem_sample.time 64 if isinstance(mem_sample, MemSample): 65 self.buffers = mem_sample.records['MemTotal'] - mem_sample.records['MemFree'] 66 self.used = mem_sample.records['MemTotal'] - mem_sample.records['MemFree'] - mem_sample.records['Buffers'] 67 self.cached = mem_sample.records['Cached'] 68 self.swap = mem_sample.records['SwapTotal'] - mem_sample.records['SwapFree'] 69 else: 70 self.buffers, self.used, self.cached, self.swap = mem_sample 71 72class DiskSpaceSample: 73 def __init__(self, time): 74 self.time = time 75 self.records = {} 76 77 def add_value(self, name, value): 78 self.records[name] = value 79 80 def valid(self): 81 return bool(self.records) 82 83class ProcessSample: 84 def __init__(self, time, state, cpu_sample): 85 self.time = time 86 self.state = state 87 self.cpu_sample = cpu_sample 88 89 def __str__(self): 90 return str(self.time) + "\t" + str(self.state) + "\t" + str(self.cpu_sample) 91 92class ProcessStats: 93 def __init__(self, writer, process_map, sample_count, sample_period, start_time, end_time): 94 self.process_map = process_map 95 self.sample_count = sample_count 96 self.sample_period = sample_period 97 self.start_time = start_time 98 self.end_time = end_time 99 writer.info ("%d samples, avg. sample length %f" % (self.sample_count, self.sample_period)) 100 writer.info ("process list size: %d" % len (self.process_map.values())) 101 102class Process: 103 def __init__(self, writer, pid, cmd, ppid, start_time): 104 self.writer = writer 105 self.pid = pid 106 self.cmd = cmd 107 self.exe = cmd 108 self.args = [] 109 self.ppid = ppid 110 self.start_time = start_time 111 self.duration = 0 112 self.samples = [] 113 self.parent = None 114 self.child_list = [] 115 116 self.active = None 117 self.last_user_cpu_time = None 118 self.last_sys_cpu_time = None 119 120 self.last_cpu_ns = 0 121 self.last_blkio_delay_ns = 0 122 self.last_swapin_delay_ns = 0 123 124 # split this process' run - triggered by a name change 125 def split(self, writer, pid, cmd, ppid, start_time): 126 split = Process (writer, pid, cmd, ppid, start_time) 127 128 split.last_cpu_ns = self.last_cpu_ns 129 split.last_blkio_delay_ns = self.last_blkio_delay_ns 130 split.last_swapin_delay_ns = self.last_swapin_delay_ns 131 132 return split 133 134 def __str__(self): 135 return " ".join([str(self.pid), self.cmd, str(self.ppid), '[ ' + str(len(self.samples)) + ' samples ]' ]) 136 137 def calc_stats(self, samplePeriod): 138 if self.samples: 139 firstSample = self.samples[0] 140 lastSample = self.samples[-1] 141 self.start_time = min(firstSample.time, self.start_time) 142 self.duration = lastSample.time - self.start_time + samplePeriod 143 144 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] ) 145 activeCount = activeCount + sum( [1 for sample in self.samples if sample.state == 'D'] ) 146 self.active = (activeCount>2) 147 148 def calc_load(self, userCpu, sysCpu, interval): 149 userCpuLoad = float(userCpu - self.last_user_cpu_time) / interval 150 sysCpuLoad = float(sysCpu - self.last_sys_cpu_time) / interval 151 cpuLoad = userCpuLoad + sysCpuLoad 152 # normalize 153 if cpuLoad > 1.0: 154 userCpuLoad = userCpuLoad / cpuLoad 155 sysCpuLoad = sysCpuLoad / cpuLoad 156 return (userCpuLoad, sysCpuLoad) 157 158 def set_parent(self, processMap): 159 if self.ppid != None: 160 self.parent = processMap.get (self.ppid) 161 if self.parent == None and self.pid // 1000 > 1 and \ 162 not (self.ppid == 2000 or self.pid == 2000): # kernel threads: ppid=2 163 self.writer.warn("Missing CONFIG_PROC_EVENTS: no parent for pid '%i' ('%s') with ppid '%i'" \ 164 % (self.pid,self.cmd,self.ppid)) 165 166 def get_end_time(self): 167 return self.start_time + self.duration 168 169class DiskSample: 170 def __init__(self, time, read, write, util): 171 self.time = time 172 self.read = read 173 self.write = write 174 self.util = util 175 self.tput = read + write 176 177 def __str__(self): 178 return "\t".join([str(self.time), str(self.read), str(self.write), str(self.util)]) 179