summaryrefslogtreecommitdiffstats
path: root/scripts/pybootchartgui/pybootchartgui/process_tree.py
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/pybootchartgui/pybootchartgui/process_tree.py')
-rw-r--r--scripts/pybootchartgui/pybootchartgui/process_tree.py126
1 files changed, 74 insertions, 52 deletions
diff --git a/scripts/pybootchartgui/pybootchartgui/process_tree.py b/scripts/pybootchartgui/pybootchartgui/process_tree.py
index bde29ebda8..cf88110b1c 100644
--- a/scripts/pybootchartgui/pybootchartgui/process_tree.py
+++ b/scripts/pybootchartgui/pybootchartgui/process_tree.py
@@ -1,3 +1,18 @@
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
1class ProcessTree: 16class ProcessTree:
2 """ProcessTree encapsulates a process tree. The tree is built from log files 17 """ProcessTree encapsulates a process tree. The tree is built from log files
3 retrieved during the boot process. When building the process tree, it is 18 retrieved during the boot process. When building the process tree, it is
@@ -19,55 +34,61 @@ class ProcessTree:
19 are merged together. 34 are merged together.
20 35
21 """ 36 """
22 LOGGER_PROC = 'bootchartd' 37 LOGGER_PROC = 'bootchart-colle'
23 EXPLODER_PROCESSES = set(['hwup']) 38 EXPLODER_PROCESSES = set(['hwup'])
24 39
25 def __init__(self, psstats, monitoredApp, prune, for_testing = False): 40 def __init__(self, writer, kernel, psstats, sample_period,
41 monitoredApp, prune, idle, taskstats,
42 accurate_parentage, for_testing = False):
43 self.writer = writer
26 self.process_tree = [] 44 self.process_tree = []
27 self.psstats = psstats 45 self.taskstats = taskstats
28 self.process_list = sorted(psstats.process_list, key = lambda p: p.pid) 46 if psstats is None:
29 self.sample_period = psstats.sample_period 47 process_list = kernel
30 48 elif kernel is None:
31 self.build() 49 process_list = psstats.process_map.values()
32 self.update_ppids_for_daemons(self.process_list) 50 else:
51 process_list = list(kernel) + list(psstats.process_map.values())
52 self.process_list = sorted(process_list, key = lambda p: p.pid)
53 self.sample_period = sample_period
54
55 self.build()
56 if not accurate_parentage:
57 self.update_ppids_for_daemons(self.process_list)
33 58
34 self.start_time = self.get_start_time(self.process_tree) 59 self.start_time = self.get_start_time(self.process_tree)
35 self.end_time = self.get_end_time(self.process_tree) 60 self.end_time = self.get_end_time(self.process_tree)
36 self.duration = self.end_time - self.start_time 61 self.duration = self.end_time - self.start_time
62 self.idle = idle
37 63
38 if for_testing: 64 if for_testing:
39 return 65 return
40 66
41 # print 'proc_tree before prune: num_proc=%i, duration=%i' % (self.num_nodes(self.process_list), self.duration) 67 removed = self.merge_logger(self.process_tree, self.LOGGER_PROC, monitoredApp, False)
42 68 writer.status("merged %i logger processes" % removed)
43 removed = self.merge_logger(self.process_tree, self.LOGGER_PROC, monitoredApp, False)
44 print "Merged %i logger processes" % removed
45 69
46 if prune: 70 if prune:
47 removed = self.prune(self.process_tree, None) 71 p_processes = self.prune(self.process_tree, None)
48 print "Pruned %i processes" % removed 72 p_exploders = self.merge_exploders(self.process_tree, self.EXPLODER_PROCESSES)
49 removed = self.merge_exploders(self.process_tree, self.EXPLODER_PROCESSES) 73 p_threads = self.merge_siblings(self.process_tree)
50 print "Pruned %i exploders" % removed 74 p_runs = self.merge_runs(self.process_tree)
51 removed = self.merge_siblings(self.process_tree) 75 writer.status("pruned %i process, %i exploders, %i threads, and %i runs" % (p_processes, p_exploders, p_threads, p_runs))
52 print "Pruned %i threads" % removed
53 removed = self.merge_runs(self.process_tree)
54 print "Pruned %i runs" % removed
55 76
56 self.sort(self.process_tree) 77 self.sort(self.process_tree)
57 78
58 self.start_time = self.get_start_time(self.process_tree) 79 self.start_time = self.get_start_time(self.process_tree)
59 self.end_time = self.get_end_time(self.process_tree) 80 self.end_time = self.get_end_time(self.process_tree)
60 self.duration = self.end_time - self.start_time 81 self.duration = self.end_time - self.start_time
61 82
62 self.num_proc = self.num_nodes(self.process_tree) 83 self.num_proc = self.num_nodes(self.process_tree)
63 84
64 def build(self): 85 def build(self):
65 """Build the process tree from the list of top samples.""" 86 """Build the process tree from the list of top samples."""
66 self.process_tree = [] 87 self.process_tree = []
67 for proc in self.process_list: 88 for proc in self.process_list:
68 if not proc.parent: 89 if not proc.parent:
69 self.process_tree.append(proc) 90 self.process_tree.append(proc)
70 else: 91 else:
71 proc.parent.child_list.append(proc) 92 proc.parent.child_list.append(proc)
72 93
73 def sort(self, process_subtree): 94 def sort(self, process_subtree):
@@ -85,11 +106,11 @@ class ProcessTree:
85 106
86 def get_start_time(self, process_subtree): 107 def get_start_time(self, process_subtree):
87 """Returns the start time of the process subtree. This is the start 108 """Returns the start time of the process subtree. This is the start
88 time of the earliest process. 109 time of the earliest process.
89 110
90 """ 111 """
91 if not process_subtree: 112 if not process_subtree:
92 return 100000000; 113 return 100000000
93 return min( [min(proc.start_time, self.get_start_time(proc.child_list)) for proc in process_subtree] ) 114 return min( [min(proc.start_time, self.get_start_time(proc.child_list)) for proc in process_subtree] )
94 115
95 def get_end_time(self, process_subtree): 116 def get_end_time(self, process_subtree):
@@ -98,13 +119,13 @@ class ProcessTree:
98 119
99 """ 120 """
100 if not process_subtree: 121 if not process_subtree:
101 return -100000000; 122 return -100000000
102 return max( [max(proc.start_time + proc.duration, self.get_end_time(proc.child_list)) for proc in process_subtree] ) 123 return max( [max(proc.start_time + proc.duration, self.get_end_time(proc.child_list)) for proc in process_subtree] )
103 124
104 def get_max_pid(self, process_subtree): 125 def get_max_pid(self, process_subtree):
105 """Returns the max PID found in the process tree.""" 126 """Returns the max PID found in the process tree."""
106 if not process_subtree: 127 if not process_subtree:
107 return -100000000; 128 return -100000000
108 return max( [max(proc.pid, self.get_max_pid(proc.child_list)) for proc in process_subtree] ) 129 return max( [max(proc.pid, self.get_max_pid(proc.child_list)) for proc in process_subtree] )
109 130
110 def update_ppids_for_daemons(self, process_list): 131 def update_ppids_for_daemons(self, process_list):
@@ -118,29 +139,29 @@ class ProcessTree:
118 rcendpid = -1 139 rcendpid = -1
119 rcproc = None 140 rcproc = None
120 for p in process_list: 141 for p in process_list:
121 if p.cmd == "rc" and p.ppid == 1: 142 if p.cmd == "rc" and p.ppid // 1000 == 1:
122 rcproc = p 143 rcproc = p
123 rcstartpid = p.pid 144 rcstartpid = p.pid
124 rcendpid = self.get_max_pid(p.child_list) 145 rcendpid = self.get_max_pid(p.child_list)
125 if rcstartpid != -1 and rcendpid != -1: 146 if rcstartpid != -1 and rcendpid != -1:
126 for p in process_list: 147 for p in process_list:
127 if p.pid > rcstartpid and p.pid < rcendpid and p.ppid == 1: 148 if p.pid > rcstartpid and p.pid < rcendpid and p.ppid // 1000 == 1:
128 p.ppid = rcstartpid 149 p.ppid = rcstartpid
129 p.parent = rcproc 150 p.parent = rcproc
130 for p in process_list: 151 for p in process_list:
131 p.child_list = [] 152 p.child_list = []
132 self.build() 153 self.build()
133 154
134 def prune(self, process_subtree, parent): 155 def prune(self, process_subtree, parent):
135 """Prunes the process tree by removing idle processes and processes 156 """Prunes the process tree by removing idle processes and processes
136 that only live for the duration of a single top sample. Sibling 157 that only live for the duration of a single top sample. Sibling
137 processes with the same command line (i.e. threads) are merged 158 processes with the same command line (i.e. threads) are merged
138 together. This filters out sleepy background processes, short-lived 159 together. This filters out sleepy background processes, short-lived
139 processes and bootcharts' analysis tools. 160 processes and bootcharts' analysis tools.
140 """ 161 """
141 def is_idle_background_process_without_children(p): 162 def is_idle_background_process_without_children(p):
142 process_end = p.start_time + p.duration 163 process_end = p.start_time + p.duration
143 return not p.active and \ 164 return not p.active and \
144 process_end >= self.start_time + self.duration and \ 165 process_end >= self.start_time + self.duration and \
145 p.start_time > self.start_time and \ 166 p.start_time > self.start_time and \
146 p.duration > 0.9 * self.duration and \ 167 p.duration > 0.9 * self.duration and \
@@ -175,8 +196,8 @@ class ProcessTree:
175 196
176 def merge_logger(self, process_subtree, logger_proc, monitored_app, app_tree): 197 def merge_logger(self, process_subtree, logger_proc, monitored_app, app_tree):
177 """Merges the logger's process subtree. The logger will typically 198 """Merges the logger's process subtree. The logger will typically
178 spawn lots of sleep and cat processes, thus polluting the 199 spawn lots of sleep and cat processes, thus polluting the
179 process tree. 200 process tree.
180 201
181 """ 202 """
182 num_removed = 0 203 num_removed = 0
@@ -185,7 +206,7 @@ class ProcessTree:
185 if logger_proc == p.cmd and not app_tree: 206 if logger_proc == p.cmd and not app_tree:
186 is_app_tree = True 207 is_app_tree = True
187 num_removed += self.merge_logger(p.child_list, logger_proc, monitored_app, is_app_tree) 208 num_removed += self.merge_logger(p.child_list, logger_proc, monitored_app, is_app_tree)
188 # don't remove the logger itself 209 # don't remove the logger itself
189 continue 210 continue
190 211
191 if app_tree and monitored_app != None and monitored_app == p.cmd: 212 if app_tree and monitored_app != None and monitored_app == p.cmd:
@@ -193,7 +214,7 @@ class ProcessTree:
193 214
194 if is_app_tree: 215 if is_app_tree:
195 for child in p.child_list: 216 for child in p.child_list:
196 self.__merge_processes(p, child) 217 self.merge_processes(p, child)
197 num_removed += 1 218 num_removed += 1
198 p.child_list = [] 219 p.child_list = []
199 else: 220 else:
@@ -202,7 +223,7 @@ class ProcessTree:
202 223
203 def merge_exploders(self, process_subtree, processes): 224 def merge_exploders(self, process_subtree, processes):
204 """Merges specific process subtrees (used for processes which usually 225 """Merges specific process subtrees (used for processes which usually
205 spawn huge meaningless process trees). 226 spawn huge meaningless process trees).
206 227
207 """ 228 """
208 num_removed = 0 229 num_removed = 0
@@ -210,7 +231,7 @@ class ProcessTree:
210 if processes in processes and len(p.child_list) > 0: 231 if processes in processes and len(p.child_list) > 0:
211 subtreemap = self.getProcessMap(p.child_list) 232 subtreemap = self.getProcessMap(p.child_list)
212 for child in subtreemap.values(): 233 for child in subtreemap.values():
213 self.__merge_processes(p, child) 234 self.merge_processes(p, child)
214 num_removed += len(subtreemap) 235 num_removed += len(subtreemap)
215 p.child_list = [] 236 p.child_list = []
216 p.cmd += " (+)" 237 p.cmd += " (+)"
@@ -218,10 +239,10 @@ class ProcessTree:
218 num_removed += self.merge_exploders(p.child_list, processes) 239 num_removed += self.merge_exploders(p.child_list, processes)
219 return num_removed 240 return num_removed
220 241
221 def merge_siblings(self,process_subtree): 242 def merge_siblings(self, process_subtree):
222 """Merges thread processes. Sibling processes with the same command 243 """Merges thread processes. Sibling processes with the same command
223 line are merged together. 244 line are merged together.
224 245
225 """ 246 """
226 num_removed = 0 247 num_removed = 0
227 idx = 0 248 idx = 0
@@ -233,7 +254,7 @@ class ProcessTree:
233 idx -= 1 254 idx -= 1
234 num_removed += 1 255 num_removed += 1
235 p.child_list.extend(nextp.child_list) 256 p.child_list.extend(nextp.child_list)
236 self.__merge_processes(p, nextp) 257 self.merge_processes(p, nextp)
237 num_removed += self.merge_siblings(p.child_list) 258 num_removed += self.merge_siblings(p.child_list)
238 idx += 1 259 idx += 1
239 if len(process_subtree) > 0: 260 if len(process_subtree) > 0:
@@ -243,7 +264,7 @@ class ProcessTree:
243 264
244 def merge_runs(self, process_subtree): 265 def merge_runs(self, process_subtree):
245 """Merges process runs. Single child processes which share the same 266 """Merges process runs. Single child processes which share the same
246 command line with the parent are merged. 267 command line with the parent are merged.
247 268
248 """ 269 """
249 num_removed = 0 270 num_removed = 0
@@ -253,16 +274,17 @@ class ProcessTree:
253 if len(p.child_list) == 1 and p.child_list[0].cmd == p.cmd: 274 if len(p.child_list) == 1 and p.child_list[0].cmd == p.cmd:
254 child = p.child_list[0] 275 child = p.child_list[0]
255 p.child_list = list(child.child_list) 276 p.child_list = list(child.child_list)
256 self.__merge_processes(p, child) 277 self.merge_processes(p, child)
257 num_removed += 1 278 num_removed += 1
258 continue 279 continue
259 num_removed += self.merge_runs(p.child_list) 280 num_removed += self.merge_runs(p.child_list)
260 idx += 1 281 idx += 1
261 return num_removed 282 return num_removed
262 283
263 def __merge_processes(self, p1, p2): 284 def merge_processes(self, p1, p2):
264 """Merges two process samples.""" 285 """Merges two process' samples."""
265 p1.samples.extend(p2.samples) 286 p1.samples.extend(p2.samples)
287 p1.samples.sort( key = lambda p: p.time )
266 p1time = p1.start_time 288 p1time = p1.start_time
267 p2time = p2.start_time 289 p2time = p2.start_time
268 p1.start_time = min(p1time, p2time) 290 p1.start_time = min(p1time, p2time)