aboutsummaryrefslogtreecommitdiffstats
path: root/scripts
diff options
context:
space:
mode:
Diffstat (limited to 'scripts')
-rwxr-xr-xscripts/analyze_suspend.py1446
1 files changed, 1446 insertions, 0 deletions
diff --git a/scripts/analyze_suspend.py b/scripts/analyze_suspend.py
new file mode 100755
index 000000000000..4f2cc12dc7c7
--- /dev/null
+++ b/scripts/analyze_suspend.py
@@ -0,0 +1,1446 @@
1#!/usr/bin/python
2#
3# Tool for analyzing suspend/resume timing
4# Copyright (c) 2013, Intel Corporation.
5#
6# This program is free software; you can redistribute it and/or modify it
7# under the terms and conditions of the GNU General Public License,
8# version 2, as published by the Free Software Foundation.
9#
10# This program is distributed in the hope it will be useful, but WITHOUT
11# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13# more details.
14#
15# You should have received a copy of the GNU General Public License along with
16# this program; if not, write to the Free Software Foundation, Inc.,
17# 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
18#
19# Authors:
20# Todd Brandt <todd.e.brandt@linux.intel.com>
21#
22# Description:
23# This tool is designed to assist kernel and OS developers in optimizing
24# their linux stack's suspend/resume time. Using a kernel image built
25# with a few extra options enabled, the tool will execute a suspend and
26# will capture dmesg and ftrace data until resume is complete. This data
27# is transformed into a device timeline and a callgraph to give a quick
28# and detailed view of which devices and callbacks are taking the most
29# time in suspend/resume. The output is a single html file which can be
30# viewed in firefox or chrome.
31#
32# The following kernel build options are required:
33# CONFIG_PM_DEBUG=y
34# CONFIG_PM_SLEEP_DEBUG=y
35# CONFIG_FTRACE=y
36# CONFIG_FUNCTION_TRACER=y
37# CONFIG_FUNCTION_GRAPH_TRACER=y
38#
39# The following additional kernel parameters are required:
40# (e.g. in file /etc/default/grub)
41# GRUB_CMDLINE_LINUX_DEFAULT="... initcall_debug log_buf_len=16M ..."
42#
43
44import sys
45import time
46import os
47import string
48import re
49import array
50import platform
51import datetime
52import struct
53
54# -- classes --
55
56class SystemValues:
57 testdir = "."
58 tpath = "/sys/kernel/debug/tracing/"
59 mempath = "/dev/mem"
60 powerfile = "/sys/power/state"
61 suspendmode = "mem"
62 prefix = "test"
63 teststamp = ""
64 dmesgfile = ""
65 ftracefile = ""
66 htmlfile = ""
67 rtcwake = False
68 def setOutputFile(self):
69 if((self.htmlfile == "") and (self.dmesgfile != "")):
70 m = re.match(r"(?P<name>.*)_dmesg\.txt$", self.dmesgfile)
71 if(m):
72 self.htmlfile = m.group("name")+".html"
73 if((self.htmlfile == "") and (self.ftracefile != "")):
74 m = re.match(r"(?P<name>.*)_ftrace\.txt$", self.ftracefile)
75 if(m):
76 self.htmlfile = m.group("name")+".html"
77 if(self.htmlfile == ""):
78 self.htmlfile = "output.html"
79 def initTestOutput(self):
80 hostname = platform.node()
81 if(hostname != ""):
82 self.prefix = hostname
83 v = os.popen("cat /proc/version").read().strip()
84 kver = string.split(v)[2]
85 self.testdir = os.popen("date \"+suspend-%m%d%y-%H%M%S\"").read().strip()
86 self.teststamp = "# "+self.testdir+" "+self.prefix+" "+self.suspendmode+" "+kver
87 self.dmesgfile = self.testdir+"/"+self.prefix+"_"+self.suspendmode+"_dmesg.txt"
88 self.ftracefile = self.testdir+"/"+self.prefix+"_"+self.suspendmode+"_ftrace.txt"
89 self.htmlfile = self.testdir+"/"+self.prefix+"_"+self.suspendmode+".html"
90 os.mkdir(self.testdir)
91
92class Data:
93 altdevname = dict()
94 usedmesg = False
95 useftrace = False
96 notestrun = False
97 verbose = False
98 phases = []
99 dmesg = {} # root data structure
100 start = 0.0
101 end = 0.0
102 stamp = {'time': "", 'host': "", 'mode': ""}
103 id = 0
104 tSuspended = 0.0
105 fwValid = False
106 fwSuspend = 0
107 fwResume = 0
108 def initialize(self):
109 self.dmesg = { # dmesg log data
110 'suspend_general': {'list': dict(), 'start': -1.0, 'end': -1.0,
111 'row': 0, 'color': "#CCFFCC", 'order': 0},
112 'suspend_early': {'list': dict(), 'start': -1.0, 'end': -1.0,
113 'row': 0, 'color': "green", 'order': 1},
114 'suspend_noirq': {'list': dict(), 'start': -1.0, 'end': -1.0,
115 'row': 0, 'color': "#00FFFF", 'order': 2},
116 'suspend_cpu': {'list': dict(), 'start': -1.0, 'end': -1.0,
117 'row': 0, 'color': "blue", 'order': 3},
118 'resume_cpu': {'list': dict(), 'start': -1.0, 'end': -1.0,
119 'row': 0, 'color': "red", 'order': 4},
120 'resume_noirq': {'list': dict(), 'start': -1.0, 'end': -1.0,
121 'row': 0, 'color': "orange", 'order': 5},
122 'resume_early': {'list': dict(), 'start': -1.0, 'end': -1.0,
123 'row': 0, 'color': "yellow", 'order': 6},
124 'resume_general': {'list': dict(), 'start': -1.0, 'end': -1.0,
125 'row': 0, 'color': "#FFFFCC", 'order': 7}
126 }
127 self.phases = self.sortedPhases()
128 def normalizeTime(self):
129 tSus = tRes = self.tSuspended
130 if self.fwValid:
131 tSus -= -self.fwSuspend / 1000000000.0
132 tRes -= self.fwResume / 1000000000.0
133 self.tSuspended = 0.0
134 self.start -= tSus
135 self.end -= tRes
136 for phase in self.phases:
137 zero = tRes
138 if "suspend" in phase:
139 zero = tSus
140 p = self.dmesg[phase]
141 p['start'] -= zero
142 p['end'] -= zero
143 list = p['list']
144 for name in list:
145 d = list[name]
146 d['start'] -= zero
147 d['end'] -= zero
148 if('ftrace' in d):
149 cg = d['ftrace']
150 cg.start -= zero
151 cg.end -= zero
152 for line in cg.list:
153 line.time -= zero
154 if self.fwValid:
155 fws = -self.fwSuspend / 1000000000.0
156 fwr = self.fwResume / 1000000000.0
157 list = dict()
158 self.id += 1
159 devid = "dc%d" % self.id
160 list["firmware-suspend"] = \
161 {'start': fws, 'end': 0, 'pid': 0, 'par': "",
162 'length': -fws, 'row': 0, 'id': devid };
163 self.id += 1
164 devid = "dc%d" % self.id
165 list["firmware-resume"] = \
166 {'start': 0, 'end': fwr, 'pid': 0, 'par': "",
167 'length': fwr, 'row': 0, 'id': devid };
168 self.dmesg['BIOS'] = \
169 {'list': list, 'start': fws, 'end': fwr,
170 'row': 0, 'color': "purple", 'order': 4}
171 self.dmesg['resume_cpu']['order'] += 1
172 self.dmesg['resume_noirq']['order'] += 1
173 self.dmesg['resume_early']['order'] += 1
174 self.dmesg['resume_general']['order'] += 1
175 self.phases = self.sortedPhases()
176 def vprint(self, msg):
177 if(self.verbose):
178 print(msg)
179 def dmesgSortVal(self, phase):
180 return self.dmesg[phase]['order']
181 def sortedPhases(self):
182 return sorted(self.dmesg, key=self.dmesgSortVal)
183 def sortedDevices(self, phase):
184 list = self.dmesg[phase]['list']
185 slist = []
186 tmp = dict()
187 for devname in list:
188 dev = list[devname]
189 tmp[dev['start']] = devname
190 for t in sorted(tmp):
191 slist.append(tmp[t])
192 return slist
193 def fixupInitcalls(self, phase, end):