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