Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 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 | |
| 17 | class 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 | |
| 24 | class 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 | class 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 | |
Brad Bishop | 6e60e8b | 2018-02-01 10:27:11 -0500 | [diff] [blame] | 56 | class 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 | |
| 72 | class 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 | |
Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 83 | class 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 | |
| 92 | class 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 | |
| 102 | class 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 | |
| 169 | class 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)]) |