mirror of
https://git.yoctoproject.org/poky
synced 2026-06-04 15:52:44 +02:00
pybootchartgui: Import pybootchartgui 0.14.5
This update the pybootchartgui code to the latest release from its new location at "https://github.com/mmeeks/bootchart". This only imports the relevant parts, and not all of bootchart2. (From OE-Core rev: 6f1568e54a7808b2ab568618fc5bb244249579f1) Signed-off-by: Peter Kjellerstedt <peter.kjellerstedt@axis.com> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
This commit is contained in:
committed by
Richard Purdie
parent
a52c9b9a76
commit
d0e513d437
@@ -1,3 +1,18 @@
|
||||
# This file is part of pybootchartgui.
|
||||
|
||||
# pybootchartgui is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
|
||||
# pybootchartgui is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with pybootchartgui. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
class ProcessTree:
|
||||
"""ProcessTree encapsulates a process tree. The tree is built from log files
|
||||
retrieved during the boot process. When building the process tree, it is
|
||||
@@ -19,55 +34,61 @@ class ProcessTree:
|
||||
are merged together.
|
||||
|
||||
"""
|
||||
LOGGER_PROC = 'bootchartd'
|
||||
LOGGER_PROC = 'bootchart-colle'
|
||||
EXPLODER_PROCESSES = set(['hwup'])
|
||||
|
||||
def __init__(self, psstats, monitoredApp, prune, for_testing = False):
|
||||
def __init__(self, writer, kernel, psstats, sample_period,
|
||||
monitoredApp, prune, idle, taskstats,
|
||||
accurate_parentage, for_testing = False):
|
||||
self.writer = writer
|
||||
self.process_tree = []
|
||||
self.psstats = psstats
|
||||
self.process_list = sorted(psstats.process_list, key = lambda p: p.pid)
|
||||
self.sample_period = psstats.sample_period
|
||||
self.taskstats = taskstats
|
||||
if psstats is None:
|
||||
process_list = kernel
|
||||
elif kernel is None:
|
||||
process_list = psstats.process_map.values()
|
||||
else:
|
||||
process_list = list(kernel) + list(psstats.process_map.values())
|
||||
self.process_list = sorted(process_list, key = lambda p: p.pid)
|
||||
self.sample_period = sample_period
|
||||
|
||||
self.build()
|
||||
self.update_ppids_for_daemons(self.process_list)
|
||||
self.build()
|
||||
if not accurate_parentage:
|
||||
self.update_ppids_for_daemons(self.process_list)
|
||||
|
||||
self.start_time = self.get_start_time(self.process_tree)
|
||||
self.end_time = self.get_end_time(self.process_tree)
|
||||
self.duration = self.end_time - self.start_time
|
||||
self.idle = idle
|
||||
|
||||
if for_testing:
|
||||
return
|
||||
|
||||
removed = self.merge_logger(self.process_tree, self.LOGGER_PROC, monitoredApp, False)
|
||||
writer.status("merged %i logger processes" % removed)
|
||||
|
||||
if prune:
|
||||
p_processes = self.prune(self.process_tree, None)
|
||||
p_exploders = self.merge_exploders(self.process_tree, self.EXPLODER_PROCESSES)
|
||||
p_threads = self.merge_siblings(self.process_tree)
|
||||
p_runs = self.merge_runs(self.process_tree)
|
||||
writer.status("pruned %i process, %i exploders, %i threads, and %i runs" % (p_processes, p_exploders, p_threads, p_runs))
|
||||
|
||||
self.sort(self.process_tree)
|
||||
|
||||
self.start_time = self.get_start_time(self.process_tree)
|
||||
self.end_time = self.get_end_time(self.process_tree)
|
||||
self.duration = self.end_time - self.start_time
|
||||
|
||||
if for_testing:
|
||||
return
|
||||
|
||||
# print 'proc_tree before prune: num_proc=%i, duration=%i' % (self.num_nodes(self.process_list), self.duration)
|
||||
|
||||
removed = self.merge_logger(self.process_tree, self.LOGGER_PROC, monitoredApp, False)
|
||||
print "Merged %i logger processes" % removed
|
||||
|
||||
if prune:
|
||||
removed = self.prune(self.process_tree, None)
|
||||
print "Pruned %i processes" % removed
|
||||
removed = self.merge_exploders(self.process_tree, self.EXPLODER_PROCESSES)
|
||||
print "Pruned %i exploders" % removed
|
||||
removed = self.merge_siblings(self.process_tree)
|
||||
print "Pruned %i threads" % removed
|
||||
removed = self.merge_runs(self.process_tree)
|
||||
print "Pruned %i runs" % removed
|
||||
|
||||
self.sort(self.process_tree)
|
||||
|
||||
self.start_time = self.get_start_time(self.process_tree)
|
||||
self.end_time = self.get_end_time(self.process_tree)
|
||||
self.duration = self.end_time - self.start_time
|
||||
|
||||
self.num_proc = self.num_nodes(self.process_tree)
|
||||
self.num_proc = self.num_nodes(self.process_tree)
|
||||
|
||||
def build(self):
|
||||
"""Build the process tree from the list of top samples."""
|
||||
self.process_tree = []
|
||||
for proc in self.process_list:
|
||||
for proc in self.process_list:
|
||||
if not proc.parent:
|
||||
self.process_tree.append(proc)
|
||||
else:
|
||||
else:
|
||||
proc.parent.child_list.append(proc)
|
||||
|
||||
def sort(self, process_subtree):
|
||||
@@ -85,11 +106,11 @@ class ProcessTree:
|
||||
|
||||
def get_start_time(self, process_subtree):
|
||||
"""Returns the start time of the process subtree. This is the start
|
||||
time of the earliest process.
|
||||
time of the earliest process.
|
||||
|
||||
"""
|
||||
if not process_subtree:
|
||||
return 100000000;
|
||||
return 100000000
|
||||
return min( [min(proc.start_time, self.get_start_time(proc.child_list)) for proc in process_subtree] )
|
||||
|
||||
def get_end_time(self, process_subtree):
|
||||
@@ -98,13 +119,13 @@ class ProcessTree:
|
||||
|
||||
"""
|
||||
if not process_subtree:
|
||||
return -100000000;
|
||||
return -100000000
|
||||
return max( [max(proc.start_time + proc.duration, self.get_end_time(proc.child_list)) for proc in process_subtree] )
|
||||
|
||||
def get_max_pid(self, process_subtree):
|
||||
"""Returns the max PID found in the process tree."""
|
||||
if not process_subtree:
|
||||
return -100000000;
|
||||
return -100000000
|
||||
return max( [max(proc.pid, self.get_max_pid(proc.child_list)) for proc in process_subtree] )
|
||||
|
||||
def update_ppids_for_daemons(self, process_list):
|
||||
@@ -118,29 +139,29 @@ class ProcessTree:
|
||||
rcendpid = -1
|
||||
rcproc = None
|
||||
for p in process_list:
|
||||
if p.cmd == "rc" and p.ppid == 1:
|
||||
if p.cmd == "rc" and p.ppid // 1000 == 1:
|
||||
rcproc = p
|
||||
rcstartpid = p.pid
|
||||
rcendpid = self.get_max_pid(p.child_list)
|
||||
if rcstartpid != -1 and rcendpid != -1:
|
||||
for p in process_list:
|
||||
if p.pid > rcstartpid and p.pid < rcendpid and p.ppid == 1:
|
||||
if p.pid > rcstartpid and p.pid < rcendpid and p.ppid // 1000 == 1:
|
||||
p.ppid = rcstartpid
|
||||
p.parent = rcproc
|
||||
for p in process_list:
|
||||
p.child_list = []
|
||||
self.build()
|
||||
|
||||
|
||||
def prune(self, process_subtree, parent):
|
||||
"""Prunes the process tree by removing idle processes and processes
|
||||
that only live for the duration of a single top sample. Sibling
|
||||
processes with the same command line (i.e. threads) are merged
|
||||
together. This filters out sleepy background processes, short-lived
|
||||
processes and bootcharts' analysis tools.
|
||||
that only live for the duration of a single top sample. Sibling
|
||||
processes with the same command line (i.e. threads) are merged
|
||||
together. This filters out sleepy background processes, short-lived
|
||||
processes and bootcharts' analysis tools.
|
||||
"""
|
||||
def is_idle_background_process_without_children(p):
|
||||
process_end = p.start_time + p.duration
|
||||
return not p.active and \
|
||||
return not p.active and \
|
||||
process_end >= self.start_time + self.duration and \
|
||||
p.start_time > self.start_time and \
|
||||
p.duration > 0.9 * self.duration and \
|
||||
@@ -175,8 +196,8 @@ class ProcessTree:
|
||||
|
||||
def merge_logger(self, process_subtree, logger_proc, monitored_app, app_tree):
|
||||
"""Merges the logger's process subtree. The logger will typically
|
||||
spawn lots of sleep and cat processes, thus polluting the
|
||||
process tree.
|
||||
spawn lots of sleep and cat processes, thus polluting the
|
||||
process tree.
|
||||
|
||||
"""
|
||||
num_removed = 0
|
||||
@@ -185,7 +206,7 @@ class ProcessTree:
|
||||
if logger_proc == p.cmd and not app_tree:
|
||||
is_app_tree = True
|
||||
num_removed += self.merge_logger(p.child_list, logger_proc, monitored_app, is_app_tree)
|
||||
# don't remove the logger itself
|
||||
# don't remove the logger itself
|
||||
continue
|
||||
|
||||
if app_tree and monitored_app != None and monitored_app == p.cmd:
|
||||
@@ -193,7 +214,7 @@ class ProcessTree:
|
||||
|
||||
if is_app_tree:
|
||||
for child in p.child_list:
|
||||
self.__merge_processes(p, child)
|
||||
self.merge_processes(p, child)
|
||||
num_removed += 1
|
||||
p.child_list = []
|
||||
else:
|
||||
@@ -202,7 +223,7 @@ class ProcessTree:
|
||||
|
||||
def merge_exploders(self, process_subtree, processes):
|
||||
"""Merges specific process subtrees (used for processes which usually
|
||||
spawn huge meaningless process trees).
|
||||
spawn huge meaningless process trees).
|
||||
|
||||
"""
|
||||
num_removed = 0
|
||||
@@ -210,7 +231,7 @@ class ProcessTree:
|
||||
if processes in processes and len(p.child_list) > 0:
|
||||
subtreemap = self.getProcessMap(p.child_list)
|
||||
for child in subtreemap.values():
|
||||
self.__merge_processes(p, child)
|
||||
self.merge_processes(p, child)
|
||||
num_removed += len(subtreemap)
|
||||
p.child_list = []
|
||||
p.cmd += " (+)"
|
||||
@@ -218,10 +239,10 @@ class ProcessTree:
|
||||
num_removed += self.merge_exploders(p.child_list, processes)
|
||||
return num_removed
|
||||
|
||||
def merge_siblings(self,process_subtree):
|
||||
def merge_siblings(self, process_subtree):
|
||||
"""Merges thread processes. Sibling processes with the same command
|
||||
line are merged together.
|
||||
|
||||
line are merged together.
|
||||
|
||||
"""
|
||||
num_removed = 0
|
||||
idx = 0
|
||||
@@ -233,7 +254,7 @@ class ProcessTree:
|
||||
idx -= 1
|
||||
num_removed += 1
|
||||
p.child_list.extend(nextp.child_list)
|
||||
self.__merge_processes(p, nextp)
|
||||
self.merge_processes(p, nextp)
|
||||
num_removed += self.merge_siblings(p.child_list)
|
||||
idx += 1
|
||||
if len(process_subtree) > 0:
|
||||
@@ -243,7 +264,7 @@ class ProcessTree:
|
||||
|
||||
def merge_runs(self, process_subtree):
|
||||
"""Merges process runs. Single child processes which share the same
|
||||
command line with the parent are merged.
|
||||
command line with the parent are merged.
|
||||
|
||||
"""
|
||||
num_removed = 0
|
||||
@@ -253,16 +274,17 @@ class ProcessTree:
|
||||
if len(p.child_list) == 1 and p.child_list[0].cmd == p.cmd:
|
||||
child = p.child_list[0]
|
||||
p.child_list = list(child.child_list)
|
||||
self.__merge_processes(p, child)
|
||||
self.merge_processes(p, child)
|
||||
num_removed += 1
|
||||
continue
|
||||
num_removed += self.merge_runs(p.child_list)
|
||||
idx += 1
|
||||
return num_removed
|
||||
|
||||
def __merge_processes(self, p1, p2):
|
||||
"""Merges two process samples."""
|
||||
def merge_processes(self, p1, p2):
|
||||
"""Merges two process' samples."""
|
||||
p1.samples.extend(p2.samples)
|
||||
p1.samples.sort( key = lambda p: p.time )
|
||||
p1time = p1.start_time
|
||||
p2time = p2.start_time
|
||||
p1.start_time = min(p1time, p2time)
|
||||
|
||||
Reference in New Issue
Block a user