yocto-autobuilder-helper/scripts/summarize_top_output.py
Richard Purdie 8702fdbb72 Clarify license and copyright information
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
2022-05-17 10:29:45 +01:00

182 lines
6.2 KiB
Python
Executable File

#!/usr/bin/env python3
#
# SPDX-License-Identifier: GPL-2.0-only
#
import os, sys, glob
# constants
HOME = "/home/pokybuild/yocto-worker/"
top_header = 7
max_cols = 11
cpu_hoggers = 5
zombie_proc_id = "<defunct>"
parser = "Parser"
# report the following whenever they occur
special_cmds = ["rm", "tar", "qemu"]
# string substitution to make things easier to read
subs = {
"/home/pokybuild/yocto-worker/" : "~/",
"/build/build/tmp/work/" : "/...WORK_DIR.../"
}
def usage():
print("Usage: " + sys.argv[0] + " <dest> <target>")
def list_top_outputs(logfile):
# top delimiter
top_start = "start: top output"
top_end = "end: top output"
# list of top outputs
top_outputs = []
# flag
collect = False
with open(logfile) as log:
top_output = []
for line in log:
lstrip = line.strip()
if collect:
if lstrip.startswith(top_end):
collect = False
top_outputs.append(top_output)
top_output = []
else:
top_output.append(lstrip)
if lstrip.startswith(top_start):
collect = True
return top_outputs
def summarize_top(top_outs, target):
summaries = []
kernel_summaries = []
zombie_summaries = []
short_summaries = []
other_builds = []
for top_out in top_outs:
summary = {}
kernel_summary = {}
zombie_summary = {}
short_summary = top_out[:top_header + cpu_hoggers]
for line in top_out[top_header:]:
cmd = line.split(maxsplit=max_cols)[-1]
if cmd.startswith(HOME):
b = cmd.split(HOME)[1].split("/")[0]
if b not in other_builds:
other_builds.append(b)
if cmd[0] == "[" and cmd[-1] == "]": # kernel processes
kproc = cmd[1:-1].split("/")[0]
if kproc not in kernel_summary:
kernel_summary[kproc] = 1
else:
kernel_summary[kproc] += 1
elif zombie_proc_id in cmd: # zombie processes
zproc = cmd.split()[0][1:-1]
if parser in zproc:
zproc = parser
if zproc not in zombie_summary:
zombie_summary[zproc] = 1
else:
zombie_summary[zproc] += 1
else: # userspace processes
cmd_split = cmd.split()
prog = cmd_split[0]
if prog not in summary:
summary[prog] = 1
else:
summary[prog] += 1
summary = dict(sorted(summary.items(), key=lambda item: item[1], reverse=True))
kernel_summary = dict(sorted(kernel_summary.items(), key=lambda item: item[1], reverse=True))
zombie_summary = dict(sorted(zombie_summary.items(), key=lambda item: item[1], reverse=True))
summaries.append(summary)
kernel_summaries.append(kernel_summary)
zombie_summaries.append(zombie_summary)
short_summaries.append(short_summary)
return (short_summaries, summaries, kernel_summaries, zombie_summaries, other_builds)
def summarize_path(path):
sub = ["/recipe-sysroot-native/", "/../../libexec/", "/gcc/"]
p = path
for k, v in subs.items():
p = p.replace(k, v)
if all(x in p for x in sub):
rsn_spl = p.split("/recipe-sysroot-native/")
gcc_spl = rsn_spl[-1].split("/gcc/")
p = rsn_spl[0] + "/...GCC.../" + gcc_spl[-1]
return p
def write_summary(short_summary, summary, kernel_summary, zombie_summary, other_build, target, logfile):
dirname = os.path.dirname(logfile)
fname = os.path.basename(logfile)
report_name = fname.split(".")[0] + "_summary.txt"
outfile = os.path.join(dirname, report_name)
out = "NOTE:\nProcesses that occur only once is not reported.\n"
out += "Program names have been shortened for better readability.\n"
out += "Substitutions are as follows:\n"
for k, v in subs.items():
out += (v + " = " + k + "\n")
out += "\n"
out += "top was invoked " + str(len(short_summary)) + " times.\n\n"
out += "Current build: " + target + "\n"
out += "Other builds:"
for b in other_build:
out += " " + b
out += "\n\n"
for i in range(len(short_summary)):
for l in short_summary[i]:
out += (l + "\n")
out += ("\nUserspace Process Summary: " + "\n")
if not summary:
out += "There were no userspace processes\n"
else:
for k, v in summary[i].items():
if v > 1 or any(k.startswith(x) for x in special_cmds):
r = summarize_path(k)
out += (str(v) + " " + r + "\n")
out += ("\nKernel Process Summary: " + "\n")
if not kernel_summary:
out += "There were no kernel processes\n"
else:
for k, v in kernel_summary[i].items():
if v > 1 or any(k.startswith(x) for x in special_cmds):
out += (str(v) + " " + k + "\n")
out += ("\nZombie Process Summary: " + "\n")
if not zombie_summary:
out += "There were no zombie processes\n"
else:
for k, v in zombie_summary[i].items():
if v > 1 or any(k.startswith(x) for x in special_cmds):
out += (str(v) + " " + k + "\n")
out += ("\n")
with open(outfile, "w") as of:
of.write(out)
def main():
if len(sys.argv) != 3:
usage()
sys.exit()
dest = sys.argv[1]
target = sys.argv[2]
host_data_dir = "intermittent_failure_host_data"
directory = os.path.join(dest, target, host_data_dir)
regs = (directory + "/*_top.txt", directory + "/*_failure_*.txt")
for exts in regs:
for f in glob.glob(exts):
outputs = list_top_outputs(f)
short_summary, summary, kernel_summary, zombie_summary, other_build = summarize_top(outputs, target)
write_summary(short_summary, summary, kernel_summary, zombie_summary, other_build, target, f)
main()