sleepgraph.py (97318d6427f62b723c89f4150f8f48126ef74961) | sleepgraph.py (0417552730d0b1c30268fd32546914290b7c7ca0) |
---|---|
1#!/usr/bin/env python3 2# SPDX-License-Identifier: GPL-2.0-only 3# 4# Tool for analyzing suspend/resume timing 5# Copyright (c) 2013, Intel Corporation. 6# 7# This program is free software; you can redistribute it and/or modify it 8# under the terms and conditions of the GNU General Public License, --- 72 unchanged lines hidden (view full) --- 81# ----------------- CLASSES -------------------- 82 83# Class: SystemValues 84# Description: 85# A global, single-instance container used to 86# store system values and test parameters 87class SystemValues: 88 title = 'SleepGraph' | 1#!/usr/bin/env python3 2# SPDX-License-Identifier: GPL-2.0-only 3# 4# Tool for analyzing suspend/resume timing 5# Copyright (c) 2013, Intel Corporation. 6# 7# This program is free software; you can redistribute it and/or modify it 8# under the terms and conditions of the GNU General Public License, --- 72 unchanged lines hidden (view full) --- 81# ----------------- CLASSES -------------------- 82 83# Class: SystemValues 84# Description: 85# A global, single-instance container used to 86# store system values and test parameters 87class SystemValues: 88 title = 'SleepGraph' |
89 version = '5.10' | 89 version = '5.11' |
90 ansi = False 91 rs = 0 92 display = '' 93 gzip = False 94 sync = False 95 wifi = False 96 netfix = False 97 verbose = False --- 197 unchanged lines hidden (view full) --- 295 [0, 'cpuinfo', 'head', '-7', '/proc/cpuinfo'], 296 [0, 'kparams', 'cat', '/proc/cmdline'], 297 [0, 'mcelog', 'mcelog'], 298 [0, 'pcidevices', 'lspci', '-tv'], 299 [0, 'usbdevices', 'lsusb', '-tv'], 300 [0, 'acpidevices', 'sh', '-c', 'ls -l /sys/bus/acpi/devices/*/physical_node'], 301 [0, 's0ix_require', 'cat', '/sys/kernel/debug/pmc_core/substate_requirements'], 302 [0, 's0ix_debug', 'cat', '/sys/kernel/debug/pmc_core/slp_s0_debug_status'], | 90 ansi = False 91 rs = 0 92 display = '' 93 gzip = False 94 sync = False 95 wifi = False 96 netfix = False 97 verbose = False --- 197 unchanged lines hidden (view full) --- 295 [0, 'cpuinfo', 'head', '-7', '/proc/cpuinfo'], 296 [0, 'kparams', 'cat', '/proc/cmdline'], 297 [0, 'mcelog', 'mcelog'], 298 [0, 'pcidevices', 'lspci', '-tv'], 299 [0, 'usbdevices', 'lsusb', '-tv'], 300 [0, 'acpidevices', 'sh', '-c', 'ls -l /sys/bus/acpi/devices/*/physical_node'], 301 [0, 's0ix_require', 'cat', '/sys/kernel/debug/pmc_core/substate_requirements'], 302 [0, 's0ix_debug', 'cat', '/sys/kernel/debug/pmc_core/slp_s0_debug_status'], |
303 [0, 'ethtool', 'ethtool', '{ethdev}'], |
|
303 [1, 's0ix_residency', 'cat', '/sys/kernel/debug/pmc_core/slp_s0_residency_usec'], 304 [1, 'interrupts', 'cat', '/proc/interrupts'], 305 [1, 'wakeups', 'cat', '/sys/kernel/debug/wakeup_sources'], 306 [2, 'gpecounts', 'sh', '-c', 'grep -v invalid /sys/firmware/acpi/interrupts/*'], 307 [2, 'suspendstats', 'sh', '-c', 'grep -v invalid /sys/power/suspend_stats/*'], 308 [2, 'cpuidle', 'sh', '-c', 'grep -v invalid /sys/devices/system/cpu/cpu*/cpuidle/state*/s2idle/*'], 309 [2, 'battery', 'sh', '-c', 'grep -v invalid /sys/class/power_supply/*/*'], 310 [2, 'thermal', 'sh', '-c', 'grep . /sys/class/thermal/thermal_zone*/temp'], --- 762 unchanged lines hidden (view full) --- 1073 if delim in line: 1074 data = line.split(delim, 1) 1075 num = re.search(r'[\d]+', data[1]) 1076 if format == 2 and num: 1077 out[data[0].strip()] = num.group() 1078 else: 1079 out[data[0].strip()] = data[1] 1080 return out | 304 [1, 's0ix_residency', 'cat', '/sys/kernel/debug/pmc_core/slp_s0_residency_usec'], 305 [1, 'interrupts', 'cat', '/proc/interrupts'], 306 [1, 'wakeups', 'cat', '/sys/kernel/debug/wakeup_sources'], 307 [2, 'gpecounts', 'sh', '-c', 'grep -v invalid /sys/firmware/acpi/interrupts/*'], 308 [2, 'suspendstats', 'sh', '-c', 'grep -v invalid /sys/power/suspend_stats/*'], 309 [2, 'cpuidle', 'sh', '-c', 'grep -v invalid /sys/devices/system/cpu/cpu*/cpuidle/state*/s2idle/*'], 310 [2, 'battery', 'sh', '-c', 'grep -v invalid /sys/class/power_supply/*/*'], 311 [2, 'thermal', 'sh', '-c', 'grep . /sys/class/thermal/thermal_zone*/temp'], --- 762 unchanged lines hidden (view full) --- 1074 if delim in line: 1075 data = line.split(delim, 1) 1076 num = re.search(r'[\d]+', data[1]) 1077 if format == 2 and num: 1078 out[data[0].strip()] = num.group() 1079 else: 1080 out[data[0].strip()] = data[1] 1081 return out |
1082 def cmdinfovar(self, arg): 1083 if arg == 'ethdev': 1084 try: 1085 cmd = [self.getExec('ip'), '-4', '-o', '-br', 'addr'] 1086 fp = Popen(cmd, stdout=PIPE, stderr=PIPE).stdout 1087 info = ascii(fp.read()).strip() 1088 fp.close() 1089 except: 1090 return 'iptoolcrash' 1091 for line in info.split('\n'): 1092 if line[0] == 'e' and 'UP' in line: 1093 return line.split()[0] 1094 return 'nodevicefound' 1095 return 'unknown' |
|
1081 def cmdinfo(self, begin, debug=False): 1082 out = [] 1083 if begin: 1084 self.cmd1 = dict() 1085 for cargs in self.infocmds: | 1096 def cmdinfo(self, begin, debug=False): 1097 out = [] 1098 if begin: 1099 self.cmd1 = dict() 1100 for cargs in self.infocmds: |
1086 delta, name = cargs[0], cargs[1] 1087 cmdline, cmdpath = ' '.join(cargs[2:]), self.getExec(cargs[2]) | 1101 delta, name, args = cargs[0], cargs[1], cargs[2:] 1102 for i in range(len(args)): 1103 if args[i][0] == '{' and args[i][-1] == '}': 1104 args[i] = self.cmdinfovar(args[i][1:-1]) 1105 cmdline, cmdpath = ' '.join(args[0:]), self.getExec(args[0]) |
1088 if not cmdpath or (begin and not delta): 1089 continue 1090 self.dlog('[%s]' % cmdline) 1091 try: | 1106 if not cmdpath or (begin and not delta): 1107 continue 1108 self.dlog('[%s]' % cmdline) 1109 try: |
1092 fp = Popen([cmdpath]+cargs[3:], stdout=PIPE, stderr=PIPE).stdout | 1110 fp = Popen([cmdpath]+args[1:], stdout=PIPE, stderr=PIPE).stdout |
1093 info = ascii(fp.read()).strip() 1094 fp.close() 1095 except: 1096 continue 1097 if not debug and begin: 1098 self.cmd1[name] = self.dictify(info, delta) 1099 elif not debug and delta and name in self.cmd1: 1100 before, after = self.cmd1[name], self.dictify(info, delta) --- 346 unchanged lines hidden (view full) --- 1447 'resume_noirq': {'order': 6, 'color': '#FF9900'}, 1448 'resume_early': {'order': 7, 'color': '#FFCC00'}, 1449 'resume': {'order': 8, 'color': '#FFFF88'}, 1450 'resume_complete': {'order': 9, 'color': '#FFFFCC'}, 1451 } 1452 errlist = { 1453 'HWERROR' : r'.*\[ *Hardware Error *\].*', 1454 'FWBUG' : r'.*\[ *Firmware Bug *\].*', | 1111 info = ascii(fp.read()).strip() 1112 fp.close() 1113 except: 1114 continue 1115 if not debug and begin: 1116 self.cmd1[name] = self.dictify(info, delta) 1117 elif not debug and delta and name in self.cmd1: 1118 before, after = self.cmd1[name], self.dictify(info, delta) --- 346 unchanged lines hidden (view full) --- 1465 'resume_noirq': {'order': 6, 'color': '#FF9900'}, 1466 'resume_early': {'order': 7, 'color': '#FFCC00'}, 1467 'resume': {'order': 8, 'color': '#FFFF88'}, 1468 'resume_complete': {'order': 9, 'color': '#FFFFCC'}, 1469 } 1470 errlist = { 1471 'HWERROR' : r'.*\[ *Hardware Error *\].*', 1472 'FWBUG' : r'.*\[ *Firmware Bug *\].*', |
1473 'TASKFAIL': r'.*Freezing .*after *.*', |
|
1455 'BUG' : r'(?i).*\bBUG\b.*', 1456 'ERROR' : r'(?i).*\bERROR\b.*', 1457 'WARNING' : r'(?i).*\bWARNING\b.*', 1458 'FAULT' : r'(?i).*\bFAULT\b.*', 1459 'FAIL' : r'(?i).*\bFAILED\b.*', 1460 'INVALID' : r'(?i).*\bINVALID\b.*', 1461 'CRASH' : r'(?i).*\bCRASHED\b.*', 1462 'TIMEOUT' : r'(?i).*\bTIMEOUT\b.*', 1463 'ABORT' : r'(?i).*\bABORT\b.*', 1464 'IRQ' : r'.*\bgenirq: .*', | 1474 'BUG' : r'(?i).*\bBUG\b.*', 1475 'ERROR' : r'(?i).*\bERROR\b.*', 1476 'WARNING' : r'(?i).*\bWARNING\b.*', 1477 'FAULT' : r'(?i).*\bFAULT\b.*', 1478 'FAIL' : r'(?i).*\bFAILED\b.*', 1479 'INVALID' : r'(?i).*\bINVALID\b.*', 1480 'CRASH' : r'(?i).*\bCRASHED\b.*', 1481 'TIMEOUT' : r'(?i).*\bTIMEOUT\b.*', 1482 'ABORT' : r'(?i).*\bABORT\b.*', 1483 'IRQ' : r'.*\bgenirq: .*', |
1465 'TASKFAIL': r'.*Freezing .*after *.*', | |
1466 'ACPI' : r'.*\bACPI *(?P<b>[A-Za-z]*) *Error[: ].*', 1467 'DISKFULL': r'.*\bNo space left on device.*', 1468 'USBERR' : r'.*usb .*device .*, error [0-9-]*', 1469 'ATAERR' : r' *ata[0-9\.]*: .*failed.*', 1470 'MEIERR' : r' *mei.*: .*failed.*', 1471 'TPMERR' : r'(?i) *tpm *tpm[0-9]*: .*error.*', 1472 } 1473 def __init__(self, num): --- 123 unchanged lines hidden (view full) --- 1597 return True 1598 def sourcePhase(self, start): 1599 for phase in self.sortedPhases(): 1600 if 'machine' in phase: 1601 continue 1602 pend = self.dmesg[phase]['end'] 1603 if start <= pend: 1604 return phase | 1484 'ACPI' : r'.*\bACPI *(?P<b>[A-Za-z]*) *Error[: ].*', 1485 'DISKFULL': r'.*\bNo space left on device.*', 1486 'USBERR' : r'.*usb .*device .*, error [0-9-]*', 1487 'ATAERR' : r' *ata[0-9\.]*: .*failed.*', 1488 'MEIERR' : r' *mei.*: .*failed.*', 1489 'TPMERR' : r'(?i) *tpm *tpm[0-9]*: .*error.*', 1490 } 1491 def __init__(self, num): --- 123 unchanged lines hidden (view full) --- 1615 return True 1616 def sourcePhase(self, start): 1617 for phase in self.sortedPhases(): 1618 if 'machine' in phase: 1619 continue 1620 pend = self.dmesg[phase]['end'] 1621 if start <= pend: 1622 return phase |
1605 return 'resume_complete' | 1623 return 'resume_complete' if 'resume_complete' in self.dmesg else '' |
1606 def sourceDevice(self, phaselist, start, end, pid, type): 1607 tgtdev = '' 1608 for phase in phaselist: 1609 list = self.dmesg[phase]['list'] 1610 for devname in list: 1611 dev = list[devname] 1612 # pid must match 1613 if dev['pid'] != pid: --- 26 unchanged lines hidden (view full) --- 1640 tgtdev = self.sourceDevice(phases, start, end, pid, 'thread') 1641 # create new thread blocks, expand as new calls are found 1642 if not tgtdev: 1643 if proc == '<...>': 1644 threadname = 'kthread-%d' % (pid) 1645 else: 1646 threadname = '%s-%d' % (proc, pid) 1647 tgtphase = self.sourcePhase(start) | 1624 def sourceDevice(self, phaselist, start, end, pid, type): 1625 tgtdev = '' 1626 for phase in phaselist: 1627 list = self.dmesg[phase]['list'] 1628 for devname in list: 1629 dev = list[devname] 1630 # pid must match 1631 if dev['pid'] != pid: --- 26 unchanged lines hidden (view full) --- 1658 tgtdev = self.sourceDevice(phases, start, end, pid, 'thread') 1659 # create new thread blocks, expand as new calls are found 1660 if not tgtdev: 1661 if proc == '<...>': 1662 threadname = 'kthread-%d' % (pid) 1663 else: 1664 threadname = '%s-%d' % (proc, pid) 1665 tgtphase = self.sourcePhase(start) |
1666 if not tgtphase: 1667 return False |
|
1648 self.newAction(tgtphase, threadname, pid, '', start, end, '', ' kth', '') 1649 return self.addDeviceFunctionCall(displayname, kprobename, proc, pid, start, end, cdata, rdata) 1650 # this should not happen 1651 if not tgtdev: 1652 sysvals.vprint('[%f - %f] %s-%d %s %s %s' % \ 1653 (start, end, proc, pid, kprobename, cdata, rdata)) 1654 return False 1655 # place the call data inside the src element of the tgtdev --- 174 unchanged lines hidden (view full) --- 1830 if not self.hwstart or not self.hwend: 1831 return 1832 stime = (self.tSuspended - self.start) * 1000000 1833 rtime = (self.end - self.tResumed) * 1000000 1834 hws = self.hwstart + timedelta(microseconds=stime) 1835 hwr = self.hwend - timedelta(microseconds=rtime) 1836 self.tLow.append('%.0f'%((hwr - hws).total_seconds() * 1000)) 1837 def getTimeValues(self): | 1668 self.newAction(tgtphase, threadname, pid, '', start, end, '', ' kth', '') 1669 return self.addDeviceFunctionCall(displayname, kprobename, proc, pid, start, end, cdata, rdata) 1670 # this should not happen 1671 if not tgtdev: 1672 sysvals.vprint('[%f - %f] %s-%d %s %s %s' % \ 1673 (start, end, proc, pid, kprobename, cdata, rdata)) 1674 return False 1675 # place the call data inside the src element of the tgtdev --- 174 unchanged lines hidden (view full) --- 1850 if not self.hwstart or not self.hwend: 1851 return 1852 stime = (self.tSuspended - self.start) * 1000000 1853 rtime = (self.end - self.tResumed) * 1000000 1854 hws = self.hwstart + timedelta(microseconds=stime) 1855 hwr = self.hwend - timedelta(microseconds=rtime) 1856 self.tLow.append('%.0f'%((hwr - hws).total_seconds() * 1000)) 1857 def getTimeValues(self): |
1838 sktime = (self.tSuspended - self.tKernSus) * 1000 1839 rktime = (self.tKernRes - self.tResumed) * 1000 1840 return (sktime, rktime) | 1858 s = (self.tSuspended - self.tKernSus) * 1000 1859 r = (self.tKernRes - self.tResumed) * 1000 1860 return (max(s, 0), max(r, 0)) |
1841 def setPhase(self, phase, ktime, isbegin, order=-1): 1842 if(isbegin): 1843 # phase start over current phase 1844 if self.currphase: 1845 if 'resume_machine' not in self.currphase: 1846 sysvals.vprint('WARNING: phase %s failed to end' % self.currphase) 1847 self.dmesg[self.currphase]['end'] = ktime 1848 phases = self.dmesg.keys() --- 2107 unchanged lines hidden (view full) --- 3956 'PM: Suspending system .*'], 3957 'suspend_late': ['PM: suspend of devices complete after.*', 3958 'PM: freeze of devices complete after.*'], 3959 'suspend_noirq': ['PM: late suspend of devices complete after.*', 3960 'PM: late freeze of devices complete after.*'], 3961 'suspend_machine': ['PM: suspend-to-idle', 3962 'PM: noirq suspend of devices complete after.*', 3963 'PM: noirq freeze of devices complete after.*'], | 1861 def setPhase(self, phase, ktime, isbegin, order=-1): 1862 if(isbegin): 1863 # phase start over current phase 1864 if self.currphase: 1865 if 'resume_machine' not in self.currphase: 1866 sysvals.vprint('WARNING: phase %s failed to end' % self.currphase) 1867 self.dmesg[self.currphase]['end'] = ktime 1868 phases = self.dmesg.keys() --- 2107 unchanged lines hidden (view full) --- 3976 'PM: Suspending system .*'], 3977 'suspend_late': ['PM: suspend of devices complete after.*', 3978 'PM: freeze of devices complete after.*'], 3979 'suspend_noirq': ['PM: late suspend of devices complete after.*', 3980 'PM: late freeze of devices complete after.*'], 3981 'suspend_machine': ['PM: suspend-to-idle', 3982 'PM: noirq suspend of devices complete after.*', 3983 'PM: noirq freeze of devices complete after.*'], |
3964 'resume_machine': ['PM: Timekeeping suspended for.*', | 3984 'resume_machine': ['[PM: ]*Timekeeping suspended for.*', |
3965 'ACPI: Low-level resume complete.*', 3966 'ACPI: resume from mwait', 3967 'Suspended for [0-9\.]* seconds'], 3968 'resume_noirq': ['PM: resume from suspend-to-idle', 3969 'ACPI: Waking up from system sleep state.*'], 3970 'resume_early': ['PM: noirq resume of devices complete after.*', 3971 'PM: noirq restore of devices complete after.*'], 3972 'resume': ['PM: early resume of devices complete after.*', 3973 'PM: early restore of devices complete after.*'], 3974 'resume_complete': ['PM: resume of devices complete after.*', 3975 'PM: restore of devices complete after.*'], 3976 'post_resume': ['.*Restarting tasks \.\.\..*'], 3977 } 3978 3979 # action table (expected events that occur and show up in dmesg) 3980 at = { 3981 'sync_filesystems': { | 3985 'ACPI: Low-level resume complete.*', 3986 'ACPI: resume from mwait', 3987 'Suspended for [0-9\.]* seconds'], 3988 'resume_noirq': ['PM: resume from suspend-to-idle', 3989 'ACPI: Waking up from system sleep state.*'], 3990 'resume_early': ['PM: noirq resume of devices complete after.*', 3991 'PM: noirq restore of devices complete after.*'], 3992 'resume': ['PM: early resume of devices complete after.*', 3993 'PM: early restore of devices complete after.*'], 3994 'resume_complete': ['PM: resume of devices complete after.*', 3995 'PM: restore of devices complete after.*'], 3996 'post_resume': ['.*Restarting tasks \.\.\..*'], 3997 } 3998 3999 # action table (expected events that occur and show up in dmesg) 4000 at = { 4001 'sync_filesystems': { |
3982 'smsg': 'PM: Syncing filesystems.*', 3983 'emsg': 'PM: Preparing system for mem sleep.*' }, | 4002 'smsg': '.*[Ff]+ilesystems.*', 4003 'emsg': 'PM: Preparing system for[a-z]* sleep.*' }, |
3984 'freeze_user_processes': { | 4004 'freeze_user_processes': { |
3985 'smsg': 'Freezing user space processes .*', | 4005 'smsg': 'Freezing user space processes.*', |
3986 'emsg': 'Freezing remaining freezable tasks.*' }, 3987 'freeze_tasks': { 3988 'smsg': 'Freezing remaining freezable tasks.*', | 4006 'emsg': 'Freezing remaining freezable tasks.*' }, 4007 'freeze_tasks': { 4008 'smsg': 'Freezing remaining freezable tasks.*', |
3989 'emsg': 'PM: Entering (?P<mode>[a-z,A-Z]*) sleep.*' }, | 4009 'emsg': 'PM: Suspending system.*' }, |
3990 'ACPI prepare': { 3991 'smsg': 'ACPI: Preparing to enter system sleep state.*', 3992 'emsg': 'PM: Saving platform NVS memory.*' }, 3993 'PM vns': { 3994 'smsg': 'PM: Saving platform NVS memory.*', 3995 'emsg': 'Disabling non-boot CPUs .*' }, 3996 } 3997 --- 117 unchanged lines hidden (view full) --- 4115 dev['end'] = ktime 4116 4117 # if trace events are not available, these are better than nothing 4118 if(not sysvals.usetraceevents): 4119 # look for known actions 4120 for a in sorted(at): 4121 if(re.match(at[a]['smsg'], msg)): 4122 if(a not in actions): | 4010 'ACPI prepare': { 4011 'smsg': 'ACPI: Preparing to enter system sleep state.*', 4012 'emsg': 'PM: Saving platform NVS memory.*' }, 4013 'PM vns': { 4014 'smsg': 'PM: Saving platform NVS memory.*', 4015 'emsg': 'Disabling non-boot CPUs .*' }, 4016 } 4017 --- 117 unchanged lines hidden (view full) --- 4135 dev['end'] = ktime 4136 4137 # if trace events are not available, these are better than nothing 4138 if(not sysvals.usetraceevents): 4139 # look for known actions 4140 for a in sorted(at): 4141 if(re.match(at[a]['smsg'], msg)): 4142 if(a not in actions): |
4123 actions[a] = [] 4124 actions[a].append({'begin': ktime, 'end': ktime}) | 4143 actions[a] = [{'begin': ktime, 'end': ktime}] |
4125 if(re.match(at[a]['emsg'], msg)): | 4144 if(re.match(at[a]['emsg'], msg)): |
4126 if(a in actions): | 4145 if(a in actions and actions[a][-1]['begin'] == actions[a][-1]['end']): |
4127 actions[a][-1]['end'] = ktime 4128 # now look for CPU on/off events 4129 if(re.match('Disabling non-boot CPUs .*', msg)): 4130 # start of first cpu suspend 4131 cpu_start = ktime 4132 elif(re.match('Enabling non-boot CPUs .*', msg)): 4133 # start of first cpu resume 4134 cpu_start = ktime --- 2999 unchanged lines hidden --- | 4146 actions[a][-1]['end'] = ktime 4147 # now look for CPU on/off events 4148 if(re.match('Disabling non-boot CPUs .*', msg)): 4149 # start of first cpu suspend 4150 cpu_start = ktime 4151 elif(re.match('Enabling non-boot CPUs .*', msg)): 4152 # start of first cpu resume 4153 cpu_start = ktime --- 2999 unchanged lines hidden --- |