samples.py 6.4 KB

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