^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) #!/usr/bin/env python3
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) # SPDX-License-Identifier: GPL-2.0-only
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) #
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) # Tool for analyzing boot timing
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) # Copyright (c) 2013, Intel Corporation.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) #
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) # This program is free software; you can redistribute it and/or modify it
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8) # under the terms and conditions of the GNU General Public License,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9) # version 2, as published by the Free Software Foundation.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10) #
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11) # This program is distributed in the hope it will be useful, but WITHOUT
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12) # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) # more details.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15) #
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) # Authors:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17) # Todd Brandt <todd.e.brandt@linux.intel.com>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18) #
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19) # Description:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20) # This tool is designed to assist kernel and OS developers in optimizing
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21) # their linux stack's boot time. It creates an html representation of
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22) # the kernel boot timeline up to the start of the init process.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23) #
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25) # ----------------- LIBRARIES --------------------
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27) import sys
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28) import time
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) import os
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30) import string
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) import re
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32) import platform
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33) import shutil
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34) from datetime import datetime, timedelta
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) from subprocess import call, Popen, PIPE
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36) import sleepgraph as aslib
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38) def pprint(msg):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39) print(msg)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40) sys.stdout.flush()
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42) # ----------------- CLASSES --------------------
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) # Class: SystemValues
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45) # Description:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46) # A global, single-instance container used to
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) # store system values and test parameters
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48) class SystemValues(aslib.SystemValues):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49) title = 'BootGraph'
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50) version = '2.2'
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51) hostname = 'localhost'
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52) testtime = ''
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53) kernel = ''
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54) dmesgfile = ''
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55) ftracefile = ''
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56) htmlfile = 'bootgraph.html'
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57) testdir = ''
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58) kparams = ''
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59) result = ''
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) useftrace = False
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61) usecallgraph = False
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62) suspendmode = 'boot'
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63) max_graph_depth = 2
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) graph_filter = 'do_one_initcall'
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65) reboot = False
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66) manual = False
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67) iscronjob = False
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68) timeformat = '%.6f'
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69) bootloader = 'grub'
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70) blexec = []
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71) def __init__(self):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) self.hostname = platform.node()
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73) self.testtime = datetime.now().strftime('%Y-%m-%d_%H:%M:%S')
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74) if os.path.exists('/proc/version'):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75) fp = open('/proc/version', 'r')
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76) val = fp.read().strip()
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77) fp.close()
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78) self.kernel = self.kernelVersion(val)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79) else:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) self.kernel = 'unknown'
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81) self.testdir = datetime.now().strftime('boot-%y%m%d-%H%M%S')
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82) def kernelVersion(self, msg):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83) return msg.split()[2]
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84) def checkFtraceKernelVersion(self):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85) val = tuple(map(int, self.kernel.split('-')[0].split('.')))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86) if val >= (4, 10, 0):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87) return True
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88) return False
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89) def kernelParams(self):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90) cmdline = 'initcall_debug log_buf_len=32M'
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91) if self.useftrace:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) if self.cpucount > 0:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93) bs = min(self.memtotal // 2, 2*1024*1024) // self.cpucount
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94) else:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95) bs = 131072
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96) cmdline += ' trace_buf_size=%dK trace_clock=global '\
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97) 'trace_options=nooverwrite,funcgraph-abstime,funcgraph-cpu,'\
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98) 'funcgraph-duration,funcgraph-proc,funcgraph-tail,'\
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99) 'nofuncgraph-overhead,context-info,graph-time '\
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) 'ftrace=function_graph '\
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) 'ftrace_graph_max_depth=%d '\
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) 'ftrace_graph_filter=%s' % \
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103) (bs, self.max_graph_depth, self.graph_filter)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) return cmdline
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) def setGraphFilter(self, val):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) master = self.getBootFtraceFilterFunctions()
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) fs = ''
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108) for i in val.split(','):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) func = i.strip()
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) if func == '':
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) doError('badly formatted filter function string')
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112) if '[' in func or ']' in func:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) doError('loadable module functions not allowed - "%s"' % func)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) if ' ' in func:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) doError('spaces found in filter functions - "%s"' % func)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116) if func not in master:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117) doError('function "%s" not available for ftrace' % func)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118) if not fs:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) fs = func
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) else:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) fs += ','+func
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122) if not fs:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) doError('badly formatted filter function string')
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124) self.graph_filter = fs
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125) def getBootFtraceFilterFunctions(self):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126) self.rootCheck(True)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127) fp = open(self.tpath+'available_filter_functions')
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128) fulllist = fp.read().split('\n')
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129) fp.close()
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130) list = []
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131) for i in fulllist:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132) if not i or ' ' in i or '[' in i or ']' in i:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) continue
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134) list.append(i)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135) return list
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136) def myCronJob(self, line):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137) if '@reboot' not in line:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138) return False
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139) if 'bootgraph' in line or 'analyze_boot.py' in line or '-cronjob' in line:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140) return True
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141) return False
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142) def cronjobCmdString(self):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143) cmdline = '%s -cronjob' % os.path.abspath(sys.argv[0])
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144) args = iter(sys.argv[1:])
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145) for arg in args:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146) if arg in ['-h', '-v', '-cronjob', '-reboot', '-verbose']:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147) continue
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 148) elif arg in ['-o', '-dmesg', '-ftrace', '-func']:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 149) next(args)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 150) continue
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 151) elif arg == '-result':
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 152) cmdline += ' %s "%s"' % (arg, os.path.abspath(next(args)))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 153) continue
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 154) elif arg == '-cgskip':
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 155) file = self.configFile(next(args))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 156) cmdline += ' %s "%s"' % (arg, os.path.abspath(file))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 157) continue
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 158) cmdline += ' '+arg
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 159) if self.graph_filter != 'do_one_initcall':
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 160) cmdline += ' -func "%s"' % self.graph_filter
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 161) cmdline += ' -o "%s"' % os.path.abspath(self.testdir)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 162) return cmdline
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 163) def manualRebootRequired(self):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 164) cmdline = self.kernelParams()
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 165) pprint('To generate a new timeline manually, follow these steps:\n\n'\
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 166) '1. Add the CMDLINE string to your kernel command line.\n'\
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 167) '2. Reboot the system.\n'\
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 168) '3. After reboot, re-run this tool with the same arguments but no command (w/o -reboot or -manual).\n\n'\
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 169) 'CMDLINE="%s"' % cmdline)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 170) sys.exit()
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 171) def blGrub(self):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 172) blcmd = ''
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 173) for cmd in ['update-grub', 'grub-mkconfig', 'grub2-mkconfig']:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 174) if blcmd:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 175) break
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 176) blcmd = self.getExec(cmd)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 177) if not blcmd:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 178) doError('[GRUB] missing update command')
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 179) if not os.path.exists('/etc/default/grub'):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 180) doError('[GRUB] missing /etc/default/grub')
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 181) if 'grub2' in blcmd:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 182) cfg = '/boot/grub2/grub.cfg'
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 183) else:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 184) cfg = '/boot/grub/grub.cfg'
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 185) if not os.path.exists(cfg):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 186) doError('[GRUB] missing %s' % cfg)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 187) if 'update-grub' in blcmd:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 188) self.blexec = [blcmd]
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 189) else:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 190) self.blexec = [blcmd, '-o', cfg]
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 191) def getBootLoader(self):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 192) if self.bootloader == 'grub':
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 193) self.blGrub()
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 194) else:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 195) doError('unknown boot loader: %s' % self.bootloader)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 196) def writeDatafileHeader(self, filename):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 197) self.kparams = open('/proc/cmdline', 'r').read().strip()
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 198) fp = open(filename, 'w')
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 199) fp.write(self.teststamp+'\n')
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 200) fp.write(self.sysstamp+'\n')
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 201) fp.write('# command | %s\n' % self.cmdline)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 202) fp.write('# kparams | %s\n' % self.kparams)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 203) fp.close()
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 204)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 205) sysvals = SystemValues()
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 206)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 207) # Class: Data
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 208) # Description:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 209) # The primary container for test data.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 210) class Data(aslib.Data):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 211) dmesg = {} # root data structure
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 212) start = 0.0 # test start
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 213) end = 0.0 # test end
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 214) dmesgtext = [] # dmesg text file in memory
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 215) testnumber = 0
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 216) idstr = ''
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 217) html_device_id = 0
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 218) valid = False
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 219) tUserMode = 0.0
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 220) boottime = ''
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 221) phases = ['kernel', 'user']
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 222) do_one_initcall = False
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 223) def __init__(self, num):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 224) self.testnumber = num
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 225) self.idstr = 'a'
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 226) self.dmesgtext = []
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 227) self.dmesg = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 228) 'kernel': {'list': dict(), 'start': -1.0, 'end': -1.0, 'row': 0,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 229) 'order': 0, 'color': 'linear-gradient(to bottom, #fff, #bcf)'},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 230) 'user': {'list': dict(), 'start': -1.0, 'end': -1.0, 'row': 0,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 231) 'order': 1, 'color': '#fff'}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 232) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 233) def deviceTopology(self):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 234) return ''
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 235) def newAction(self, phase, name, pid, start, end, ret, ulen):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 236) # new device callback for a specific phase
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 237) self.html_device_id += 1
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 238) devid = '%s%d' % (self.idstr, self.html_device_id)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 239) list = self.dmesg[phase]['list']
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 240) length = -1.0
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 241) if(start >= 0 and end >= 0):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 242) length = end - start
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 243) i = 2
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 244) origname = name
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 245) while(name in list):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 246) name = '%s[%d]' % (origname, i)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 247) i += 1
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 248) list[name] = {'name': name, 'start': start, 'end': end,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 249) 'pid': pid, 'length': length, 'row': 0, 'id': devid,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 250) 'ret': ret, 'ulen': ulen }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 251) return name
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 252) def deviceMatch(self, pid, cg):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 253) if cg.end - cg.start == 0:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 254) return ''
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 255) for p in data.phases:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 256) list = self.dmesg[p]['list']
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 257) for devname in list:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 258) dev = list[devname]
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 259) if pid != dev['pid']:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 260) continue
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 261) if cg.name == 'do_one_initcall':
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 262) if(cg.start <= dev['start'] and cg.end >= dev['end'] and dev['length'] > 0):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 263) dev['ftrace'] = cg
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 264) self.do_one_initcall = True
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 265) return devname
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 266) else:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 267) if(cg.start > dev['start'] and cg.end < dev['end']):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 268) if 'ftraces' not in dev:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 269) dev['ftraces'] = []
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 270) dev['ftraces'].append(cg)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 271) return devname
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 272) return ''
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 273) def printDetails(self):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 274) sysvals.vprint('Timeline Details:')
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 275) sysvals.vprint(' Host: %s' % sysvals.hostname)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 276) sysvals.vprint(' Kernel: %s' % sysvals.kernel)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 277) sysvals.vprint(' Test time: %s' % sysvals.testtime)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 278) sysvals.vprint(' Boot time: %s' % self.boottime)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 279) for phase in self.phases:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 280) dc = len(self.dmesg[phase]['list'])
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 281) sysvals.vprint('%9s mode: %.3f - %.3f (%d initcalls)' % (phase,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 282) self.dmesg[phase]['start']*1000,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 283) self.dmesg[phase]['end']*1000, dc))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 284)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 285) # ----------------- FUNCTIONS --------------------
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 286)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 287) # Function: parseKernelLog
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 288) # Description:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 289) # parse a kernel log for boot data
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 290) def parseKernelLog():
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 291) sysvals.vprint('Analyzing the dmesg data (%s)...' % \
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 292) os.path.basename(sysvals.dmesgfile))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 293) phase = 'kernel'
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 294) data = Data(0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 295) data.dmesg['kernel']['start'] = data.start = ktime = 0.0
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 296) sysvals.stamp = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 297) 'time': datetime.now().strftime('%B %d %Y, %I:%M:%S %p'),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 298) 'host': sysvals.hostname,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 299) 'mode': 'boot', 'kernel': ''}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 300)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 301) tp = aslib.TestProps()
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 302) devtemp = dict()
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 303) if(sysvals.dmesgfile):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 304) lf = open(sysvals.dmesgfile, 'rb')
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 305) else:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 306) lf = Popen('dmesg', stdout=PIPE).stdout
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 307) for line in lf:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 308) line = aslib.ascii(line).replace('\r\n', '')
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 309) # grab the stamp and sysinfo
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 310) if re.match(tp.stampfmt, line):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 311) tp.stamp = line
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 312) continue
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 313) elif re.match(tp.sysinfofmt, line):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 314) tp.sysinfo = line
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 315) continue
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 316) elif re.match(tp.cmdlinefmt, line):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 317) tp.cmdline = line
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 318) continue
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 319) elif re.match(tp.kparamsfmt, line):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 320) tp.kparams = line
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 321) continue
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 322) idx = line.find('[')
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 323) if idx > 1:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 324) line = line[idx:]
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 325) m = re.match('[ \t]*(\[ *)(?P<ktime>[0-9\.]*)(\]) (?P<msg>.*)', line)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 326) if(not m):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 327) continue
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 328) ktime = float(m.group('ktime'))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 329) if(ktime > 120):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 330) break
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 331) msg = m.group('msg')
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 332) data.dmesgtext.append(line)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 333) if(ktime == 0.0 and re.match('^Linux version .*', msg)):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 334) if(not sysvals.stamp['kernel']):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 335) sysvals.stamp['kernel'] = sysvals.kernelVersion(msg)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 336) continue
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 337) m = re.match('.* setting system clock to (?P<d>[0-9\-]*)[ A-Z](?P<t>[0-9:]*) UTC.*', msg)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 338) if(m):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 339) bt = datetime.strptime(m.group('d')+' '+m.group('t'), '%Y-%m-%d %H:%M:%S')
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 340) bt = bt - timedelta(seconds=int(ktime))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 341) data.boottime = bt.strftime('%Y-%m-%d_%H:%M:%S')
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 342) sysvals.stamp['time'] = bt.strftime('%B %d %Y, %I:%M:%S %p')
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 343) continue
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 344) m = re.match('^calling *(?P<f>.*)\+.* @ (?P<p>[0-9]*)', msg)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 345) if(m):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 346) func = m.group('f')
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 347) pid = int(m.group('p'))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 348) devtemp[func] = (ktime, pid)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 349) continue
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 350) m = re.match('^initcall *(?P<f>.*)\+.* returned (?P<r>.*) after (?P<t>.*) usecs', msg)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 351) if(m):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 352) data.valid = True
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 353) data.end = ktime
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 354) f, r, t = m.group('f', 'r', 't')
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 355) if(f in devtemp):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 356) start, pid = devtemp[f]
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 357) data.newAction(phase, f, pid, start, ktime, int(r), int(t))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 358) del devtemp[f]
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 359) continue
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 360) if(re.match('^Freeing unused kernel .*', msg)):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 361) data.tUserMode = ktime
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 362) data.dmesg['kernel']['end'] = ktime
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 363) data.dmesg['user']['start'] = ktime
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 364) phase = 'user'
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 365)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 366) if tp.stamp:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 367) sysvals.stamp = 0
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 368) tp.parseStamp(data, sysvals)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 369) data.dmesg['user']['end'] = data.end
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 370) lf.close()
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 371) return data
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 372)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 373) # Function: parseTraceLog
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 374) # Description:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 375) # Check if trace is available and copy to a temp file
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 376) def parseTraceLog(data):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 377) sysvals.vprint('Analyzing the ftrace data (%s)...' % \
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 378) os.path.basename(sysvals.ftracefile))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 379) # if available, calculate cgfilter allowable ranges
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 380) cgfilter = []
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 381) if len(sysvals.cgfilter) > 0:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 382) for p in data.phases:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 383) list = data.dmesg[p]['list']
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 384) for i in sysvals.cgfilter:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 385) if i in list:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 386) cgfilter.append([list[i]['start']-0.0001,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 387) list[i]['end']+0.0001])
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 388) # parse the trace log
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 389) ftemp = dict()
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 390) tp = aslib.TestProps()
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 391) tp.setTracerType('function_graph')
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 392) tf = open(sysvals.ftracefile, 'r')
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 393) for line in tf:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 394) if line[0] == '#':
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 395) continue
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 396) m = re.match(tp.ftrace_line_fmt, line.strip())
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 397) if(not m):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 398) continue
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 399) m_time, m_proc, m_pid, m_msg, m_dur = \
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 400) m.group('time', 'proc', 'pid', 'msg', 'dur')
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 401) t = float(m_time)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 402) if len(cgfilter) > 0:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 403) allow = False
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 404) for r in cgfilter:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 405) if t >= r[0] and t < r[1]:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 406) allow = True
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 407) break
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 408) if not allow:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 409) continue
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 410) if t > data.end:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 411) break
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 412) if(m_time and m_pid and m_msg):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 413) t = aslib.FTraceLine(m_time, m_msg, m_dur)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 414) pid = int(m_pid)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 415) else:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 416) continue
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 417) if t.fevent or t.fkprobe:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 418) continue
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 419) key = (m_proc, pid)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 420) if(key not in ftemp):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 421) ftemp[key] = []
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 422) ftemp[key].append(aslib.FTraceCallGraph(pid, sysvals))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 423) cg = ftemp[key][-1]
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 424) res = cg.addLine(t)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 425) if(res != 0):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 426) ftemp[key].append(aslib.FTraceCallGraph(pid, sysvals))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 427) if(res == -1):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 428) ftemp[key][-1].addLine(t)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 429)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 430) tf.close()
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 431)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 432) # add the callgraph data to the device hierarchy
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 433) for key in ftemp:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 434) proc, pid = key
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 435) for cg in ftemp[key]:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 436) if len(cg.list) < 1 or cg.invalid or (cg.end - cg.start == 0):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 437) continue
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 438) if(not cg.postProcess()):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 439) pprint('Sanity check failed for %s-%d' % (proc, pid))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 440) continue
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 441) # match cg data to devices
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 442) devname = data.deviceMatch(pid, cg)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 443) if not devname:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 444) kind = 'Orphan'
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 445) if cg.partial:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 446) kind = 'Partial'
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 447) sysvals.vprint('%s callgraph found for %s %s-%d [%f - %f]' %\
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 448) (kind, cg.name, proc, pid, cg.start, cg.end))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 449) elif len(cg.list) > 1000000:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 450) pprint('WARNING: the callgraph found for %s is massive! (%d lines)' %\
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 451) (devname, len(cg.list)))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 452)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 453) # Function: retrieveLogs
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 454) # Description:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 455) # Create copies of dmesg and/or ftrace for later processing
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 456) def retrieveLogs():
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 457) # check ftrace is configured first
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 458) if sysvals.useftrace:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 459) tracer = sysvals.fgetVal('current_tracer').strip()
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 460) if tracer != 'function_graph':
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 461) doError('ftrace not configured for a boot callgraph')
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 462) # create the folder and get dmesg
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 463) sysvals.systemInfo(aslib.dmidecode(sysvals.mempath))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 464) sysvals.initTestOutput('boot')
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 465) sysvals.writeDatafileHeader(sysvals.dmesgfile)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 466) call('dmesg >> '+sysvals.dmesgfile, shell=True)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 467) if not sysvals.useftrace:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 468) return
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 469) # get ftrace
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 470) sysvals.writeDatafileHeader(sysvals.ftracefile)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 471) call('cat '+sysvals.tpath+'trace >> '+sysvals.ftracefile, shell=True)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 472)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 473) # Function: colorForName
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 474) # Description:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 475) # Generate a repeatable color from a list for a given name
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 476) def colorForName(name):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 477) list = [
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 478) ('c1', '#ec9999'),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 479) ('c2', '#ffc1a6'),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 480) ('c3', '#fff0a6'),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 481) ('c4', '#adf199'),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 482) ('c5', '#9fadea'),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 483) ('c6', '#a699c1'),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 484) ('c7', '#ad99b4'),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 485) ('c8', '#eaffea'),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 486) ('c9', '#dcecfb'),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 487) ('c10', '#ffffea')
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 488) ]
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 489) i = 0
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 490) total = 0
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 491) count = len(list)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 492) while i < len(name):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 493) total += ord(name[i])
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 494) i += 1
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 495) return list[total % count]
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 496)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 497) def cgOverview(cg, minlen):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 498) stats = dict()
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 499) large = []
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 500) for l in cg.list:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 501) if l.fcall and l.depth == 1:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 502) if l.length >= minlen:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 503) large.append(l)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 504) if l.name not in stats:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 505) stats[l.name] = [0, 0.0]
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 506) stats[l.name][0] += (l.length * 1000.0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 507) stats[l.name][1] += 1
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 508) return (large, stats)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 509)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 510) # Function: createBootGraph
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 511) # Description:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 512) # Create the output html file from the resident test data
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 513) # Arguments:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 514) # testruns: array of Data objects from parseKernelLog or parseTraceLog
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 515) # Output:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 516) # True if the html file was created, false if it failed
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 517) def createBootGraph(data):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 518) # html function templates
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 519) html_srccall = '<div id={6} title="{5}" class="srccall" style="left:{1}%;top:{2}px;height:{3}px;width:{4}%;line-height:{3}px;">{0}</div>\n'
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 520) html_timetotal = '<table class="time1">\n<tr>'\
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 521) '<td class="blue">Init process starts @ <b>{0} ms</b></td>'\
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 522) '<td class="blue">Last initcall ends @ <b>{1} ms</b></td>'\
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 523) '</tr>\n</table>\n'
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 524)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 525) # device timeline
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 526) devtl = aslib.Timeline(100, 20)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 527)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 528) # write the test title and general info header
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 529) devtl.createHeader(sysvals, sysvals.stamp)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 530)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 531) # Generate the header for this timeline
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 532) t0 = data.start
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 533) tMax = data.end
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 534) tTotal = tMax - t0
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 535) if(tTotal == 0):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 536) pprint('ERROR: No timeline data')
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 537) return False
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 538) user_mode = '%.0f'%(data.tUserMode*1000)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 539) last_init = '%.0f'%(tTotal*1000)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 540) devtl.html += html_timetotal.format(user_mode, last_init)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 541)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 542) # determine the maximum number of rows we need to draw
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 543) devlist = []
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 544) for p in data.phases:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 545) list = data.dmesg[p]['list']
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 546) for devname in list:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 547) d = aslib.DevItem(0, p, list[devname])
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 548) devlist.append(d)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 549) devtl.getPhaseRows(devlist, 0, 'start')
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 550) devtl.calcTotalRows()
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 551)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 552) # draw the timeline background
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 553) devtl.createZoomBox()
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 554) devtl.html += devtl.html_tblock.format('boot', '0', '100', devtl.scaleH)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 555) for p in data.phases:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 556) phase = data.dmesg[p]
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 557) length = phase['end']-phase['start']
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 558) left = '%.3f' % (((phase['start']-t0)*100.0)/tTotal)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 559) width = '%.3f' % ((length*100.0)/tTotal)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 560) devtl.html += devtl.html_phase.format(left, width, \
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 561) '%.3f'%devtl.scaleH, '%.3f'%devtl.bodyH, \
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 562) phase['color'], '')
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 563)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 564) # draw the device timeline
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 565) num = 0
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 566) devstats = dict()
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 567) for phase in data.phases:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 568) list = data.dmesg[phase]['list']
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 569) for devname in sorted(list):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 570) cls, color = colorForName(devname)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 571) dev = list[devname]
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 572) info = '@|%.3f|%.3f|%.3f|%d' % (dev['start']*1000.0, dev['end']*1000.0,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 573) dev['ulen']/1000.0, dev['ret'])
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 574) devstats[dev['id']] = {'info':info}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 575) dev['color'] = color
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 576) height = devtl.phaseRowHeight(0, phase, dev['row'])
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 577) top = '%.6f' % ((dev['row']*height) + devtl.scaleH)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 578) left = '%.6f' % (((dev['start']-t0)*100)/tTotal)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 579) width = '%.6f' % (((dev['end']-dev['start'])*100)/tTotal)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 580) length = ' (%0.3f ms) ' % ((dev['end']-dev['start'])*1000)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 581) devtl.html += devtl.html_device.format(dev['id'],
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 582) devname+length+phase+'_mode', left, top, '%.3f'%height,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 583) width, devname, ' '+cls, '')
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 584) rowtop = devtl.phaseRowTop(0, phase, dev['row'])
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 585) height = '%.6f' % (devtl.rowH / 2)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 586) top = '%.6f' % (rowtop + devtl.scaleH + (devtl.rowH / 2))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 587) if data.do_one_initcall:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 588) if('ftrace' not in dev):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 589) continue
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 590) cg = dev['ftrace']
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 591) large, stats = cgOverview(cg, 0.001)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 592) devstats[dev['id']]['fstat'] = stats
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 593) for l in large:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 594) left = '%f' % (((l.time-t0)*100)/tTotal)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 595) width = '%f' % (l.length*100/tTotal)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 596) title = '%s (%0.3fms)' % (l.name, l.length * 1000.0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 597) devtl.html += html_srccall.format(l.name, left,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 598) top, height, width, title, 'x%d'%num)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 599) num += 1
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 600) continue
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 601) if('ftraces' not in dev):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 602) continue
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 603) for cg in dev['ftraces']:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 604) left = '%f' % (((cg.start-t0)*100)/tTotal)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 605) width = '%f' % ((cg.end-cg.start)*100/tTotal)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 606) cglen = (cg.end - cg.start) * 1000.0
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 607) title = '%s (%0.3fms)' % (cg.name, cglen)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 608) cg.id = 'x%d' % num
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 609) devtl.html += html_srccall.format(cg.name, left,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 610) top, height, width, title, dev['id']+cg.id)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 611) num += 1
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 612)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 613) # draw the time scale, try to make the number of labels readable
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 614) devtl.createTimeScale(t0, tMax, tTotal, 'boot')
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 615) devtl.html += '</div>\n'
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 616)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 617) # timeline is finished
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 618) devtl.html += '</div>\n</div>\n'
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 619)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 620) # draw a legend which describes the phases by color
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 621) devtl.html += '<div class="legend">\n'
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 622) pdelta = 20.0
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 623) pmargin = 36.0
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 624) for phase in data.phases:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 625) order = '%.2f' % ((data.dmesg[phase]['order'] * pdelta) + pmargin)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 626) devtl.html += devtl.html_legend.format(order, \
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 627) data.dmesg[phase]['color'], phase+'_mode', phase[0])
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 628) devtl.html += '</div>\n'
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 629)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 630) hf = open(sysvals.htmlfile, 'w')
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 631)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 632) # add the css
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 633) extra = '\
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 634) .c1 {background:rgba(209,0,0,0.4);}\n\
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 635) .c2 {background:rgba(255,102,34,0.4);}\n\
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 636) .c3 {background:rgba(255,218,33,0.4);}\n\
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 637) .c4 {background:rgba(51,221,0,0.4);}\n\
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 638) .c5 {background:rgba(17,51,204,0.4);}\n\
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 639) .c6 {background:rgba(34,0,102,0.4);}\n\
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 640) .c7 {background:rgba(51,0,68,0.4);}\n\
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 641) .c8 {background:rgba(204,255,204,0.4);}\n\
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 642) .c9 {background:rgba(169,208,245,0.4);}\n\
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 643) .c10 {background:rgba(255,255,204,0.4);}\n\
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 644) .vt {transform:rotate(-60deg);transform-origin:0 0;}\n\
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 645) table.fstat {table-layout:fixed;padding:150px 15px 0 0;font-size:10px;column-width:30px;}\n\
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 646) .fstat th {width:55px;}\n\
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 647) .fstat td {text-align:left;width:35px;}\n\
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 648) .srccall {position:absolute;font-size:10px;z-index:7;overflow:hidden;color:black;text-align:center;white-space:nowrap;border-radius:5px;border:1px solid black;background:linear-gradient(to bottom right,#CCC,#969696);}\n\
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 649) .srccall:hover {color:white;font-weight:bold;border:1px solid white;}\n'
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 650) aslib.addCSS(hf, sysvals, 1, False, extra)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 651)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 652) # write the device timeline
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 653) hf.write(devtl.html)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 654)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 655) # add boot specific html
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 656) statinfo = 'var devstats = {\n'
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 657) for n in sorted(devstats):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 658) statinfo += '\t"%s": [\n\t\t"%s",\n' % (n, devstats[n]['info'])
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 659) if 'fstat' in devstats[n]:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 660) funcs = devstats[n]['fstat']
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 661) for f in sorted(funcs, key=lambda k:(funcs[k], k), reverse=True):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 662) if funcs[f][0] < 0.01 and len(funcs) > 10:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 663) break
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 664) statinfo += '\t\t"%f|%s|%d",\n' % (funcs[f][0], f, funcs[f][1])
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 665) statinfo += '\t],\n'
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 666) statinfo += '};\n'
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 667) html = \
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 668) '<div id="devicedetailtitle"></div>\n'\
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 669) '<div id="devicedetail" style="display:none;">\n'\
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 670) '<div id="devicedetail0">\n'
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 671) for p in data.phases:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 672) phase = data.dmesg[p]
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 673) html += devtl.html_phaselet.format(p+'_mode', '0', '100', phase['color'])
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 674) html += '</div>\n</div>\n'\
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 675) '<script type="text/javascript">\n'+statinfo+\
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 676) '</script>\n'
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 677) hf.write(html)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 678)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 679) # add the callgraph html
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 680) if(sysvals.usecallgraph):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 681) aslib.addCallgraphs(sysvals, hf, data)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 682)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 683) # add the test log as a hidden div
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 684) if sysvals.testlog and sysvals.logmsg:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 685) hf.write('<div id="testlog" style="display:none;">\n'+sysvals.logmsg+'</div>\n')
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 686) # add the dmesg log as a hidden div
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 687) if sysvals.dmesglog:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 688) hf.write('<div id="dmesglog" style="display:none;">\n')
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 689) for line in data.dmesgtext:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 690) line = line.replace('<', '<').replace('>', '>')
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 691) hf.write(line)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 692) hf.write('</div>\n')
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 693)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 694) # write the footer and close
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 695) aslib.addScriptCode(hf, [data])
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 696) hf.write('</body>\n</html>\n')
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 697) hf.close()
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 698) return True
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 699)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 700) # Function: updateCron
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 701) # Description:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 702) # (restore=False) Set the tool to run automatically on reboot
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 703) # (restore=True) Restore the original crontab
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 704) def updateCron(restore=False):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 705) if not restore:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 706) sysvals.rootUser(True)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 707) crondir = '/var/spool/cron/crontabs/'
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 708) if not os.path.exists(crondir):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 709) crondir = '/var/spool/cron/'
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 710) if not os.path.exists(crondir):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 711) doError('%s not found' % crondir)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 712) cronfile = crondir+'root'
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 713) backfile = crondir+'root-analyze_boot-backup'
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 714) cmd = sysvals.getExec('crontab')
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 715) if not cmd:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 716) doError('crontab not found')
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 717) # on restore: move the backup cron back into place
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 718) if restore:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 719) if os.path.exists(backfile):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 720) shutil.move(backfile, cronfile)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 721) call([cmd, cronfile])
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 722) return
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 723) # backup current cron and install new one with reboot
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 724) if os.path.exists(cronfile):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 725) shutil.move(cronfile, backfile)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 726) else:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 727) fp = open(backfile, 'w')
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 728) fp.close()
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 729) res = -1
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 730) try:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 731) fp = open(backfile, 'r')
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 732) op = open(cronfile, 'w')
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 733) for line in fp:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 734) if not sysvals.myCronJob(line):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 735) op.write(line)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 736) continue
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 737) fp.close()
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 738) op.write('@reboot python %s\n' % sysvals.cronjobCmdString())
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 739) op.close()
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 740) res = call([cmd, cronfile])
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 741) except Exception as e:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 742) pprint('Exception: %s' % str(e))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 743) shutil.move(backfile, cronfile)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 744) res = -1
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 745) if res != 0:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 746) doError('crontab failed')
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 747)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 748) # Function: updateGrub
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 749) # Description:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 750) # update grub.cfg for all kernels with our parameters
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 751) def updateGrub(restore=False):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 752) # call update-grub on restore
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 753) if restore:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 754) try:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 755) call(sysvals.blexec, stderr=PIPE, stdout=PIPE,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 756) env={'PATH': '.:/sbin:/usr/sbin:/usr/bin:/sbin:/bin'})
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 757) except Exception as e:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 758) pprint('Exception: %s\n' % str(e))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 759) return
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 760) # extract the option and create a grub config without it
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 761) sysvals.rootUser(True)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 762) tgtopt = 'GRUB_CMDLINE_LINUX_DEFAULT'
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 763) cmdline = ''
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 764) grubfile = '/etc/default/grub'
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 765) tempfile = '/etc/default/grub.analyze_boot'
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 766) shutil.move(grubfile, tempfile)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 767) res = -1
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 768) try:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 769) fp = open(tempfile, 'r')
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 770) op = open(grubfile, 'w')
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 771) cont = False
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 772) for line in fp:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 773) line = line.strip()
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 774) if len(line) == 0 or line[0] == '#':
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 775) continue
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 776) opt = line.split('=')[0].strip()
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 777) if opt == tgtopt:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 778) cmdline = line.split('=', 1)[1].strip('\\')
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 779) if line[-1] == '\\':
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 780) cont = True
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 781) elif cont:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 782) cmdline += line.strip('\\')
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 783) if line[-1] != '\\':
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 784) cont = False
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 785) else:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 786) op.write('%s\n' % line)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 787) fp.close()
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 788) # if the target option value is in quotes, strip them
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 789) sp = '"'
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 790) val = cmdline.strip()
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 791) if val and (val[0] == '\'' or val[0] == '"'):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 792) sp = val[0]
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 793) val = val.strip(sp)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 794) cmdline = val
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 795) # append our cmd line options
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 796) if len(cmdline) > 0:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 797) cmdline += ' '
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 798) cmdline += sysvals.kernelParams()
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 799) # write out the updated target option
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 800) op.write('\n%s=%s%s%s\n' % (tgtopt, sp, cmdline, sp))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 801) op.close()
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 802) res = call(sysvals.blexec)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 803) os.remove(grubfile)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 804) except Exception as e:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 805) pprint('Exception: %s' % str(e))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 806) res = -1
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 807) # cleanup
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 808) shutil.move(tempfile, grubfile)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 809) if res != 0:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 810) doError('update grub failed')
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 811)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 812) # Function: updateKernelParams
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 813) # Description:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 814) # update boot conf for all kernels with our parameters
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 815) def updateKernelParams(restore=False):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 816) # find the boot loader
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 817) sysvals.getBootLoader()
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 818) if sysvals.bootloader == 'grub':
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 819) updateGrub(restore)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 820)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 821) # Function: doError Description:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 822) # generic error function for catastrphic failures
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 823) # Arguments:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 824) # msg: the error message to print
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 825) # help: True if printHelp should be called after, False otherwise
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 826) def doError(msg, help=False):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 827) if help == True:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 828) printHelp()
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 829) pprint('ERROR: %s\n' % msg)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 830) sysvals.outputResult({'error':msg})
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 831) sys.exit()
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 832)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 833) # Function: printHelp
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 834) # Description:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 835) # print out the help text
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 836) def printHelp():
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 837) pprint('\n%s v%s\n'\
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 838) 'Usage: bootgraph <options> <command>\n'\
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 839) '\n'\
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 840) 'Description:\n'\
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 841) ' This tool reads in a dmesg log of linux kernel boot and\n'\
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 842) ' creates an html representation of the boot timeline up to\n'\
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 843) ' the start of the init process.\n'\
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 844) '\n'\
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 845) ' If no specific command is given the tool reads the current dmesg\n'\
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 846) ' and/or ftrace log and creates a timeline\n'\
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 847) '\n'\
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 848) ' Generates output files in subdirectory: boot-yymmdd-HHMMSS\n'\
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 849) ' HTML output: <hostname>_boot.html\n'\
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 850) ' raw dmesg output: <hostname>_boot_dmesg.txt\n'\
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 851) ' raw ftrace output: <hostname>_boot_ftrace.txt\n'\
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 852) '\n'\
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 853) 'Options:\n'\
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 854) ' -h Print this help text\n'\
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 855) ' -v Print the current tool version\n'\
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 856) ' -verbose Print extra information during execution and analysis\n'\
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 857) ' -addlogs Add the dmesg log to the html output\n'\
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 858) ' -result fn Export a results table to a text file for parsing.\n'\
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 859) ' -o name Overrides the output subdirectory name when running a new test\n'\
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 860) ' default: boot-{date}-{time}\n'\
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 861) ' [advanced]\n'\
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 862) ' -fstat Use ftrace to add function detail and statistics (default: disabled)\n'\
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 863) ' -f/-callgraph Add callgraph detail, can be very large (default: disabled)\n'\
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 864) ' -maxdepth N limit the callgraph data to N call levels (default: 2)\n'\
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 865) ' -mincg ms Discard all callgraphs shorter than ms milliseconds (e.g. 0.001 for us)\n'\
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 866) ' -timeprec N Number of significant digits in timestamps (0:S, 3:ms, [6:us])\n'\
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 867) ' -expandcg pre-expand the callgraph data in the html output (default: disabled)\n'\
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 868) ' -func list Limit ftrace to comma-delimited list of functions (default: do_one_initcall)\n'\
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 869) ' -cgfilter S Filter the callgraph output in the timeline\n'\
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 870) ' -cgskip file Callgraph functions to skip, off to disable (default: cgskip.txt)\n'\
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 871) ' -bl name Use the following boot loader for kernel params (default: grub)\n'\
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 872) ' -reboot Reboot the machine automatically and generate a new timeline\n'\
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 873) ' -manual Show the steps to generate a new timeline manually (used with -reboot)\n'\
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 874) '\n'\
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 875) 'Other commands:\n'\
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 876) ' -flistall Print all functions capable of being captured in ftrace\n'\
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 877) ' -sysinfo Print out system info extracted from BIOS\n'\
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 878) ' -which exec Print an executable path, should function even without PATH\n'\
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 879) ' [redo]\n'\
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 880) ' -dmesg file Create HTML output using dmesg input (used with -ftrace)\n'\
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 881) ' -ftrace file Create HTML output using ftrace input (used with -dmesg)\n'\
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 882) '' % (sysvals.title, sysvals.version))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 883) return True
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 884)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 885) # ----------------- MAIN --------------------
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 886) # exec start (skipped if script is loaded as library)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 887) if __name__ == '__main__':
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 888) # loop through the command line arguments
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 889) cmd = ''
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 890) testrun = True
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 891) switchoff = ['disable', 'off', 'false', '0']
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 892) simplecmds = ['-sysinfo', '-kpupdate', '-flistall', '-checkbl']
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 893) cgskip = ''
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 894) if '-f' in sys.argv:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 895) cgskip = sysvals.configFile('cgskip.txt')
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 896) args = iter(sys.argv[1:])
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 897) mdset = False
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 898) for arg in args:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 899) if(arg == '-h'):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 900) printHelp()
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 901) sys.exit()
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 902) elif(arg == '-v'):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 903) pprint("Version %s" % sysvals.version)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 904) sys.exit()
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 905) elif(arg == '-verbose'):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 906) sysvals.verbose = True
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 907) elif(arg in simplecmds):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 908) cmd = arg[1:]
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 909) elif(arg == '-fstat'):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 910) sysvals.useftrace = True
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 911) elif(arg == '-callgraph' or arg == '-f'):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 912) sysvals.useftrace = True
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 913) sysvals.usecallgraph = True
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 914) elif(arg == '-cgdump'):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 915) sysvals.cgdump = True
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 916) elif(arg == '-mincg'):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 917) sysvals.mincglen = aslib.getArgFloat('-mincg', args, 0.0, 10000.0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 918) elif(arg == '-cgfilter'):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 919) try:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 920) val = next(args)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 921) except:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 922) doError('No callgraph functions supplied', True)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 923) sysvals.setCallgraphFilter(val)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 924) elif(arg == '-cgskip'):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 925) try:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 926) val = next(args)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 927) except:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 928) doError('No file supplied', True)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 929) if val.lower() in switchoff:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 930) cgskip = ''
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 931) else:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 932) cgskip = sysvals.configFile(val)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 933) if(not cgskip):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 934) doError('%s does not exist' % cgskip)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 935) elif(arg == '-bl'):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 936) try:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 937) val = next(args)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 938) except:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 939) doError('No boot loader name supplied', True)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 940) if val.lower() not in ['grub']:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 941) doError('Unknown boot loader: %s' % val, True)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 942) sysvals.bootloader = val.lower()
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 943) elif(arg == '-timeprec'):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 944) sysvals.setPrecision(aslib.getArgInt('-timeprec', args, 0, 6))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 945) elif(arg == '-maxdepth'):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 946) mdset = True
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 947) sysvals.max_graph_depth = aslib.getArgInt('-maxdepth', args, 0, 1000)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 948) elif(arg == '-func'):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 949) try:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 950) val = next(args)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 951) except:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 952) doError('No filter functions supplied', True)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 953) sysvals.useftrace = True
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 954) sysvals.usecallgraph = True
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 955) sysvals.rootCheck(True)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 956) sysvals.setGraphFilter(val)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 957) elif(arg == '-ftrace'):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 958) try:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 959) val = next(args)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 960) except:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 961) doError('No ftrace file supplied', True)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 962) if(os.path.exists(val) == False):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 963) doError('%s does not exist' % val)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 964) testrun = False
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 965) sysvals.ftracefile = val
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 966) elif(arg == '-addlogs'):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 967) sysvals.dmesglog = True
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 968) elif(arg == '-expandcg'):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 969) sysvals.cgexp = True
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 970) elif(arg == '-dmesg'):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 971) try:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 972) val = next(args)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 973) except:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 974) doError('No dmesg file supplied', True)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 975) if(os.path.exists(val) == False):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 976) doError('%s does not exist' % val)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 977) testrun = False
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 978) sysvals.dmesgfile = val
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 979) elif(arg == '-o'):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 980) try:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 981) val = next(args)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 982) except:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 983) doError('No subdirectory name supplied', True)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 984) sysvals.testdir = sysvals.setOutputFolder(val)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 985) elif(arg == '-result'):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 986) try:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 987) val = next(args)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 988) except:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 989) doError('No result file supplied', True)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 990) sysvals.result = val
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 991) elif(arg == '-reboot'):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 992) sysvals.reboot = True
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 993) elif(arg == '-manual'):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 994) sysvals.reboot = True
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 995) sysvals.manual = True
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 996) # remaining options are only for cron job use
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 997) elif(arg == '-cronjob'):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 998) sysvals.iscronjob = True
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 999) elif(arg == '-which'):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1000) try:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1001) val = next(args)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1002) except:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1003) doError('No executable supplied', True)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1004) out = sysvals.getExec(val)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1005) if not out:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1006) print('%s not found' % val)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1007) sys.exit(1)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1008) print(out)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1009) sys.exit(0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1010) else:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1011) doError('Invalid argument: '+arg, True)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1012)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1013) # compatibility errors and access checks
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1014) if(sysvals.iscronjob and (sysvals.reboot or \
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1015) sysvals.dmesgfile or sysvals.ftracefile or cmd)):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1016) doError('-cronjob is meant for batch purposes only')
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1017) if(sysvals.reboot and (sysvals.dmesgfile or sysvals.ftracefile)):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1018) doError('-reboot and -dmesg/-ftrace are incompatible')
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1019) if cmd or sysvals.reboot or sysvals.iscronjob or testrun:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1020) sysvals.rootCheck(True)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1021) if (testrun and sysvals.useftrace) or cmd == 'flistall':
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1022) if not sysvals.verifyFtrace():
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1023) doError('Ftrace is not properly enabled')
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1024)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1025) # run utility commands
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1026) sysvals.cpuInfo()
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1027) if cmd != '':
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1028) if cmd == 'kpupdate':
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1029) updateKernelParams()
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1030) elif cmd == 'flistall':
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1031) for f in sysvals.getBootFtraceFilterFunctions():
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1032) print(f)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1033) elif cmd == 'checkbl':
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1034) sysvals.getBootLoader()
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1035) pprint('Boot Loader: %s\n%s' % (sysvals.bootloader, sysvals.blexec))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1036) elif(cmd == 'sysinfo'):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1037) sysvals.printSystemInfo(True)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1038) sys.exit()
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1039)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1040) # reboot: update grub, setup a cronjob, and reboot
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1041) if sysvals.reboot:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1042) if (sysvals.useftrace or sysvals.usecallgraph) and \
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1043) not sysvals.checkFtraceKernelVersion():
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1044) doError('Ftrace functionality requires kernel v4.10 or newer')
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1045) if not sysvals.manual:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1046) updateKernelParams()
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1047) updateCron()
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1048) call('reboot')
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1049) else:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1050) sysvals.manualRebootRequired()
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1051) sys.exit()
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1052)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1053) if sysvals.usecallgraph and cgskip:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1054) sysvals.vprint('Using cgskip file: %s' % cgskip)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1055) sysvals.setCallgraphBlacklist(cgskip)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1056)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1057) # cronjob: remove the cronjob, grub changes, and disable ftrace
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1058) if sysvals.iscronjob:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1059) updateCron(True)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1060) updateKernelParams(True)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1061) try:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1062) sysvals.fsetVal('0', 'tracing_on')
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1063) except:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1064) pass
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1065)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1066) # testrun: generate copies of the logs
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1067) if testrun:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1068) retrieveLogs()
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1069) else:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1070) sysvals.setOutputFile()
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1071)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1072) # process the log data
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1073) if sysvals.dmesgfile:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1074) if not mdset:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1075) sysvals.max_graph_depth = 0
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1076) data = parseKernelLog()
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1077) if(not data.valid):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1078) doError('No initcall data found in %s' % sysvals.dmesgfile)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1079) if sysvals.useftrace and sysvals.ftracefile:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1080) parseTraceLog(data)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1081) if sysvals.cgdump:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1082) data.debugPrint()
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1083) sys.exit()
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1084) else:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1085) doError('dmesg file required')
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1086)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1087) sysvals.vprint('Creating the html timeline (%s)...' % sysvals.htmlfile)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1088) sysvals.vprint('Command:\n %s' % sysvals.cmdline)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1089) sysvals.vprint('Kernel parameters:\n %s' % sysvals.kparams)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1090) data.printDetails()
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1091) createBootGraph(data)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1092)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1093) # if running as root, change output dir owner to sudo_user
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1094) if testrun and os.path.isdir(sysvals.testdir) and \
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1095) os.getuid() == 0 and 'SUDO_USER' in os.environ:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1096) cmd = 'chown -R {0}:{0} {1} > /dev/null 2>&1'
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1097) call(cmd.format(os.environ['SUDO_USER'], sysvals.testdir), shell=True)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1098)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1099) sysvals.stamp['boot'] = (data.tUserMode - data.start) * 1000
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1100) sysvals.stamp['lastinit'] = data.end * 1000
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1101) sysvals.outputResult(sysvals.stamp)