History

Merge tag 'pm-4.8-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm

Pull power management updates from Rafael Wysocki: "Again, the majority of changes go into the cpufreq subsystem, but there are no big features this time. The cpufreq changes that stand out somewhat are the governor interface rework and improvements related to the handling of frequency tables. Apart from those, there are fixes and new device/CPU IDs in drivers, cleanups and an improvement of the new schedutil governor. Next, there are some changes in the hibernation core, including a fix for a nasty problem related to the MONITOR/MWAIT usage by CPU offline during resume from hibernation, a few core improvements related to memory management during resume, a couple of additional debug features and cleanups. Finally, we have some fixes and cleanups in the devfreq subsystem, generic power domains framework improvements related to system suspend/resume, support for some new chips in intel_idle and in the power capping RAPL driver, a new version of the AnalyzeSuspend utility and some assorted fixes and cleanups. Specifics: - Rework the cpufreq governor interface to make it more straightforward and modify the conservative governor to avoid using transition notifications (Rafael Wysocki). - Rework the handling of frequency tables by the cpufreq core to make it more efficient (Viresh Kumar). - Modify the schedutil governor to reduce the number of wakeups it causes to occur in cases when the CPU frequency doesn't need to be changed (Steve Muckle, Viresh Kumar). - Fix some minor issues and clean up code in the cpufreq core and governors (Rafael Wysocki, Viresh Kumar). - Add Intel Broxton support to the intel_pstate driver (Srinivas Pandruvada). - Fix problems related to the config TDP feature and to the validity of the MSR_HWP_INTERRUPT register in intel_pstate (Jan Kiszka, Srinivas Pandruvada). - Make intel_pstate update the cpu_frequency tracepoint even if the frequency doesn't change to avoid confusing powertop (Rafael Wysocki). - Clean up the usage of __init/__initdata in intel_pstate, mark some of its internal variables as __read_mostly and drop an unused structure element from it (Jisheng Zhang, Carsten Emde). - Clean up the usage of some duplicate MSR symbols in intel_pstate and turbostat (Srinivas Pandruvada). - Update/fix the powernv, s3c24xx and mvebu cpufreq drivers (Akshay Adiga, Viresh Kumar, Ben Dooks). - Fix a regression (introduced during the 4.5 cycle) in the pcc-cpufreq driver by reverting the problematic commit (Andreas Herrmann). - Add support for Intel Denverton to intel_idle, clean up Broxton support in it and make it explicitly non-modular (Jacob Pan, Jan Beulich, Paul Gortmaker). - Add support for Denverton and Ivy Bridge server to the Intel RAPL power capping driver and make it more careful about the handing of MSRs that may not be present (Jacob Pan, Xiaolong Wang). - Fix resume from hibernation on x86-64 by making the CPU offline during resume avoid using MONITOR/MWAIT in the "play dead" loop which may lead to an inadvertent "revival" of a "dead" CPU and a page fault leading to a kernel crash from it (Rafael Wysocki). - Make memory management during resume from hibernation more straightforward (Rafael Wysocki). - Add debug features that should help to detect problems related to hibernation and resume from it (Rafael Wysocki, Chen Yu). - Clean up hibernation core somewhat (Rafael Wysocki). - Prevent KASAN from instrumenting the hibernation core which leads to large numbers of false-positives from it (James Morse). - Prevent PM (hibernate and suspend) notifiers from being called during the cleanup phase if they have not been called during the corresponding preparation phase which is possible if one of the other notifiers returns an error at that time (Lianwei Wang). - Improve suspend-related debug printout in the tasks freezer and clean up suspend-related console handling (Roger Lu, Borislav Petkov). - Update the AnalyzeSuspend script in the kernel sources to version 4.2 (Todd Brandt). - Modify the generic power domains framework to make it handle system suspend/resume better (Ulf Hansson). - Make the runtime PM framework avoid resuming devices synchronously when user space changes the runtime PM settings for them and improve its error reporting (Rafael Wysocki, Linus Walleij). - Fix error paths in devfreq drivers (exynos, exynos-ppmu, exynos-bus) and in the core, make some devfreq code explicitly non-modular and change some of it into tristate (Bartlomiej Zolnierkiewicz, Peter Chen, Paul Gortmaker). - Add DT support to the generic PM clocks management code and make it export some more symbols (Jon Hunter, Paul Gortmaker). - Make the PCI PM core code slightly more robust against possible driver errors (Andy Shevchenko). - Make it possible to change DESTDIR and PREFIX in turbostat (Andy Shevchenko)" * tag 'pm-4.8-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm: (89 commits) Revert "cpufreq: pcc-cpufreq: update default value of cpuinfo_transition_latency" PM / hibernate: Introduce test_resume mode for hibernation cpufreq: export cpufreq_driver_resolve_freq() cpufreq: Disallow ->resolve_freq() for drivers providing ->target_index() PCI / PM: check all fields in pci_set_platform_pm() cpufreq: acpi-cpufreq: use cached frequency mapping when possible cpufreq: schedutil: map raw required frequency to driver frequency cpufreq: add cpufreq_driver_resolve_freq() cpufreq: intel_pstate: Check cpuid for MSR_HWP_INTERRUPT intel_pstate: Update cpu_frequency tracepoint every time cpufreq: intel_pstate: clean remnant struct element PM / tools: scripts: AnalyzeSuspend v4.2 x86 / hibernate: Use hlt_play_dead() when resuming from hibernation cpufreq: powernv: Replacing pstate_id with frequency table index intel_pstate: Fix MSR_CONFIG_TDP_x addressing in core_get_max_pstate() PM / hibernate: Image data protection during restoration PM / hibernate: Add missing braces in __register_nosave_region() PM / hibernate: Clean up comments in snapshot.c PM / hibernate: Clean up function headers in snapshot.c PM / hibernate: Add missing braces in hibernate_setup() ...

Linus Torvalds committed 10 years ago

Commit 6453dbd2 parents 27b7902 bc841e265 files · +4332 −2821Browse files

Showing the changes to scripts (1 of 65 files). Show all

Unified · Split

scripts/analyze_suspend.py+2474−1167
@@ -19,6 +19,17 @@ · 18 unmodified lines
1919 # Authors:
2020 # Todd Brandt <todd.e.brandt@linux.intel.com>
2121 #
22+# Links:
23+# Home Page
24+# https://01.org/suspendresume
25+# Source repo
26+# https://github.com/01org/suspendresume
27+# Documentation
28+# Getting Started
29+# https://01.org/suspendresume/documentation/getting-started
30+# Command List:
31+# https://01.org/suspendresume/documentation/command-list
32+#
2233 # Description:
2334 # This tool is designed to assist kernel and OS developers in optimizing
2435 # their linux stack's suspend/resume time. Using a kernel image built
@@ -35,6 +46,8 @@ · 10 unmodified lines
3546 # CONFIG_FTRACE=y
3647 # CONFIG_FUNCTION_TRACER=y
3748 # CONFIG_FUNCTION_GRAPH_TRACER=y
49+# CONFIG_KPROBES=y
50+# CONFIG_KPROBES_ON_FTRACE=y
3851 #
3952 # For kernel versions older than 3.15:
4053 # The following additional kernel parameters are required:
@@ -52,6 +65,7 @@ import re · 11 unmodified lines
5265 import platform
5366 from datetime import datetime
5467 import struct
68+import ConfigParser
5569
5670 # ----------------- CLASSES --------------------
5771
@@ -60,8 +74,15 @@ import struct · 2 unmodified lines
6074 # A global, single-instance container used to
6175 # store system values and test parameters
6276 class SystemValues:
63− version = 3.0
77+ ansi = False
78+ version = '4.2'
6479 verbose = False
80+ addlogs = False
81+ mindevlen = 0.001
82+ mincglen = 1.0
83+ srgap = 0
84+ cgexp = False
85+ outdir = ''
6586 testdir = '.'
6687 tpath = '/sys/kernel/debug/tracing/'
6788 fpdtpath = '/sys/firmware/acpi/tables/FPDT'
@@ -71,26 +92,21 @@ class SystemValues: · 3 unmodified lines
7192 'device_pm_callback_end',
7293 'device_pm_callback_start'
7394 ]
74− modename = {
75− 'freeze': 'Suspend-To-Idle (S0)',
76− 'standby': 'Power-On Suspend (S1)',
77− 'mem': 'Suspend-to-RAM (S3)',
78− 'disk': 'Suspend-to-disk (S4)'
79− }
95+ testcommand = ''
8096 mempath = '/dev/mem'
8197 powerfile = '/sys/power/state'
8298 suspendmode = 'mem'
8399 hostname = 'localhost'
84100 prefix = 'test'
85101 teststamp = ''
102+ dmesgstart = 0.0
86103 dmesgfile = ''
87104 ftracefile = ''
88105 htmlfile = ''
106+ embedded = False
89107 rtcwake = False
90108 rtcwaketime = 10
91109 rtcpath = ''
92− android = False
93− adb = 'adb'
94110 devicefilter = []
95111 stamp = 0
96112 execcount = 1
@@ -98,16 +114,90 @@ class SystemValues: · 1 unmodified line
98114 usecallgraph = False
99115 usetraceevents = False
100116 usetraceeventsonly = False
117+ usetracemarkers = True
118+ usekprobes = True
119+ usedevsrc = False
101120 notestrun = False
102− altdevname = dict()
121+ devprops = dict()
103122 postresumetime = 0
123+ devpropfmt = '# Device Properties: .*'
104124 tracertypefmt = '# tracer: (?P<t>.*)'
105125 firmwarefmt = '# fwsuspend (?P<s>[0-9]*) fwresume (?P<r>[0-9]*)$'
106126 postresumefmt = '# post resume time (?P<t>[0-9]*)$'
107127 stampfmt = '# suspend-(?P<m>[0-9]{2})(?P<d>[0-9]{2})(?P<y>[0-9]{2})-'+\
108128 '(?P<H>[0-9]{2})(?P<M>[0-9]{2})(?P<S>[0-9]{2})'+\
109129 ' (?P<host>.*) (?P<mode>.*) (?P<kernel>.*)$'
130+ kprobecolor = 'rgba(204,204,204,0.5)'
131+ synccolor = 'rgba(204,204,204,0.5)'
132+ debugfuncs = []
133+ tracefuncs = {
134+ 'sys_sync': dict(),
135+ 'pm_prepare_console': dict(),
136+ 'pm_notifier_call_chain': dict(),
137+ 'freeze_processes': dict(),
138+ 'freeze_kernel_threads': dict(),
139+ 'pm_restrict_gfp_mask': dict(),
140+ 'acpi_suspend_begin': dict(),
141+ 'suspend_console': dict(),
142+ 'acpi_pm_prepare': dict(),
143+ 'syscore_suspend': dict(),
144+ 'arch_enable_nonboot_cpus_end': dict(),
145+ 'syscore_resume': dict(),
146+ 'acpi_pm_finish': dict(),
147+ 'resume_console': dict(),
148+ 'acpi_pm_end': dict(),
149+ 'pm_restore_gfp_mask': dict(),
150+ 'thaw_processes': dict(),
151+ 'pm_restore_console': dict(),
152+ 'CPU_OFF': {
153+ 'func':'_cpu_down',
154+ 'args_x86_64': {'cpu':'%di:s32'},
155+ 'format': 'CPU_OFF[{cpu}]',
156+ 'mask': 'CPU_.*_DOWN'
157+ },
158+ 'CPU_ON': {
159+ 'func':'_cpu_up',
160+ 'args_x86_64': {'cpu':'%di:s32'},
161+ 'format': 'CPU_ON[{cpu}]',
162+ 'mask': 'CPU_.*_UP'
163+ },
164+ }
165+ dev_tracefuncs = {
166+ # general wait/delay/sleep
167+ 'msleep': { 'args_x86_64': {'time':'%di:s32'} },
168+ 'udelay': { 'func':'__const_udelay', 'args_x86_64': {'loops':'%di:s32'} },
169+ 'acpi_os_stall': dict(),
170+ # ACPI
171+ 'acpi_resume_power_resources': dict(),
172+ 'acpi_ps_parse_aml': dict(),
173+ # filesystem
174+ 'ext4_sync_fs': dict(),
175+ # ATA
176+ 'ata_eh_recover': { 'args_x86_64': {'port':'+36(%di):s32'} },
177+ # i915
178+ 'i915_gem_restore_gtt_mappings': dict(),
179+ 'intel_opregion_setup': dict(),
180+ 'intel_dp_detect': dict(),
181+ 'intel_hdmi_detect': dict(),
182+ 'intel_opregion_init': dict(),
183+ }
184+ kprobes_postresume = [
185+ {
186+ 'name': 'ataportrst',
187+ 'func': 'ata_eh_recover',
188+ 'args': {'port':'+36(%di):s32'},
189+ 'format': 'ata{port}_port_reset',
190+ 'mask': 'ata.*_port_reset'
191+ }
192+ ]
193+ kprobes = dict()
194+ timeformat = '%.3f'
110195 def __init__(self):
196+ # if this is a phoronix test run, set some default options
197+ if('LOG_FILE' in os.environ and 'TEST_RESULTS_IDENTIFIER' in os.environ):
198+ self.embedded = True
199+ self.addlogs = True
200+ self.htmlfile = os.environ['LOG_FILE']
111201 self.hostname = platform.node()
112202 if(self.hostname == ''):
113203 self.hostname = 'localhost'
@@ -118,6 +208,12 @@ class SystemValues: · 4 unmodified lines
118208 if os.path.exists(rtc) and os.path.exists(rtc+'/date') and \
119209 os.path.exists(rtc+'/time') and os.path.exists(rtc+'/wakealarm'):
120210 self.rtcpath = rtc
211+ if (hasattr(sys.stdout, 'isatty') and sys.stdout.isatty()):
212+ self.ansi = True
213+ def setPrecision(self, num):
214+ if num < 0 or num > 6:
215+ return
216+ self.timeformat = '%.{0}f'.format(num)
121217 def setOutputFile(self):
122218 if((self.htmlfile == '') and (self.dmesgfile != '')):
123219 m = re.match('(?P<name>.*)_dmesg\.txt$', self.dmesgfile)
@@ -129,32 +225,37 @@ class SystemValues: · 5 unmodified lines
129225 self.htmlfile = m.group('name')+'.html'
130226 if(self.htmlfile == ''):
131227 self.htmlfile = 'output.html'
132− def initTestOutput(self, subdir):
133− if(not self.android):
134− self.prefix = self.hostname
135− v = open('/proc/version', 'r').read().strip()
136− kver = string.split(v)[2]
137− else:
138− self.prefix = 'android'
139− v = os.popen(self.adb+' shell cat /proc/version').read().strip()
140− kver = string.split(v)[2]
141− testtime = datetime.now().strftime('suspend-%m%d%y-%H%M%S')
228+ def initTestOutput(self, subdir, testpath=''):
229+ self.prefix = self.hostname
230+ v = open('/proc/version', 'r').read().strip()
231+ kver = string.split(v)[2]
232+ n = datetime.now()
233+ testtime = n.strftime('suspend-%m%d%y-%H%M%S')
234+ if not testpath:
235+ testpath = n.strftime('suspend-%y%m%d-%H%M%S')
142236 if(subdir != "."):
143− self.testdir = subdir+"/"+testtime
237+ self.testdir = subdir+"/"+testpath
144238 else:
145− self.testdir = testtime
239+ self.testdir = testpath
146240 self.teststamp = \
147241 '# '+testtime+' '+self.prefix+' '+self.suspendmode+' '+kver
242+ if(self.embedded):
243+ self.dmesgfile = \
244+ '/tmp/'+testtime+'_'+self.suspendmode+'_dmesg.txt'
245+ self.ftracefile = \
246+ '/tmp/'+testtime+'_'+self.suspendmode+'_ftrace.txt'
247+ return
148248 self.dmesgfile = \
149249 self.testdir+'/'+self.prefix+'_'+self.suspendmode+'_dmesg.txt'
150250 self.ftracefile = \
151251 self.testdir+'/'+self.prefix+'_'+self.suspendmode+'_ftrace.txt'
152252 self.htmlfile = \
153253 self.testdir+'/'+self.prefix+'_'+self.suspendmode+'.html'
154− os.mkdir(self.testdir)
254+ if not os.path.isdir(self.testdir):
255+ os.mkdir(self.testdir)
155256 def setDeviceFilter(self, devnames):
156257 self.devicefilter = string.split(devnames)
157− def rtcWakeAlarm(self):
258+ def rtcWakeAlarmOn(self):
158259 os.system('echo 0 > '+self.rtcpath+'/wakealarm')
159260 outD = open(self.rtcpath+'/date', 'r').read().strip()
160261 outT = open(self.rtcpath+'/time', 'r').read().strip()
@@ -172,9 +273,361 @@ class SystemValues: · 11 unmodified lines
172273 nowtime = int(datetime.now().strftime('%s'))
173274 alarm = nowtime + self.rtcwaketime
174275 os.system('echo %d > %s/wakealarm' % (alarm, self.rtcpath))
276+ def rtcWakeAlarmOff(self):
277+ os.system('echo 0 > %s/wakealarm' % self.rtcpath)
278+ def initdmesg(self):
279+ # get the latest time stamp from the dmesg log
280+ fp = os.popen('dmesg')
281+ ktime = '0'
282+ for line in fp:
283+ line = line.replace('\r\n', '')
284+ idx = line.find('[')
285+ if idx > 1:
286+ line = line[idx:]
287+ m = re.match('[ \t]*(\[ *)(?P<ktime>[0-9\.]*)(\]) (?P<msg>.*)', line)
288+ if(m):
289+ ktime = m.group('ktime')
290+ fp.close()
291+ self.dmesgstart = float(ktime)
292+ def getdmesg(self):
293+ # store all new dmesg lines since initdmesg was called
294+ fp = os.popen('dmesg')
295+ op = open(self.dmesgfile, 'a')
296+ for line in fp:
297+ line = line.replace('\r\n', '')
298+ idx = line.find('[')
299+ if idx > 1:
300+ line = line[idx:]
301+ m = re.match('[ \t]*(\[ *)(?P<ktime>[0-9\.]*)(\]) (?P<msg>.*)', line)
302+ if(not m):
303+ continue
304+ ktime = float(m.group('ktime'))
305+ if ktime > self.dmesgstart:
306+ op.write(line)
307+ fp.close()
308+ op.close()
309+ def addFtraceFilterFunctions(self, file):
310+ fp = open(file)
311+ list = fp.read().split('\n')
312+ fp.close()
313+ for i in list:
314+ if len(i) < 2:
315+ continue
316+ self.tracefuncs[i] = dict()
317+ def getFtraceFilterFunctions(self, current):
318+ rootCheck(True)
319+ if not current:
320+ os.system('cat '+self.tpath+'available_filter_functions')
321+ return
322+ fp = open(self.tpath+'available_filter_functions')
323+ master = fp.read().split('\n')
324+ fp.close()
325+ if len(self.debugfuncs) > 0:
326+ for i in self.debugfuncs:
327+ if i in master:
328+ print i
329+ else:
330+ print self.colorText(i)
331+ else:
332+ for i in self.tracefuncs:
333+ if 'func' in self.tracefuncs[i]:
334+ i = self.tracefuncs[i]['func']
335+ if i in master:
336+ print i
337+ else:
338+ print self.colorText(i)
339+ def setFtraceFilterFunctions(self, list):
340+ fp = open(self.tpath+'available_filter_functions')
341+ master = fp.read().split('\n')
342+ fp.close()
343+ flist = ''
344+ for i in list:
345+ if i not in master:
346+ continue
347+ if ' [' in i:
348+ flist += i.split(' ')[0]+'\n'
349+ else:
350+ flist += i+'\n'
351+ fp = open(self.tpath+'set_graph_function', 'w')
352+ fp.write(flist)
353+ fp.close()
354+ def kprobeMatch(self, name, target):
355+ if name not in self.kprobes:
356+ return False
357+ if re.match(self.kprobes[name]['mask'], target):
358+ return True
359+ return False
360+ def basicKprobe(self, name):
361+ self.kprobes[name] = {'name': name,'func': name,'args': dict(),'format': name,'mask': name}
362+ def defaultKprobe(self, name, kdata):
363+ k = kdata
364+ for field in ['name', 'format', 'mask', 'func']:
365+ if field not in k:
366+ k[field] = name
367+ archargs = 'args_'+platform.machine()
368+ if archargs in k:
369+ k['args'] = k[archargs]
370+ else:
371+ k['args'] = dict()
372+ k['format'] = name
373+ self.kprobes[name] = k
374+ def kprobeColor(self, name):
375+ if name not in self.kprobes or 'color' not in self.kprobes[name]:
376+ return ''
377+ return self.kprobes[name]['color']
378+ def kprobeDisplayName(self, name, dataraw):
379+ if name not in self.kprobes:
380+ self.basicKprobe(name)
381+ data = ''
382+ quote=0
383+ # first remvoe any spaces inside quotes, and the quotes
384+ for c in dataraw:
385+ if c == '"':
386+ quote = (quote + 1) % 2
387+ if quote and c == ' ':
388+ data += '_'
389+ elif c != '"':
390+ data += c
391+ fmt, args = self.kprobes[name]['format'], self.kprobes[name]['args']
392+ arglist = dict()
393+ # now process the args
394+ for arg in sorted(args):
395+ arglist[arg] = ''
396+ m = re.match('.* '+arg+'=(?P<arg>.*) ', data);
397+ if m:
398+ arglist[arg] = m.group('arg')
399+ else:
400+ m = re.match('.* '+arg+'=(?P<arg>.*)', data);
401+ if m:
402+ arglist[arg] = m.group('arg')
403+ out = fmt.format(**arglist)
404+ out = out.replace(' ', '_').replace('"', '')
405+ return out
406+ def kprobeText(self, kprobe):
407+ name, fmt, func, args = kprobe['name'], kprobe['format'], kprobe['func'], kprobe['args']
408+ if re.findall('{(?P<n>[a-z,A-Z,0-9]*)}', func):
409+ doError('Kprobe "%s" has format info in the function name "%s"' % (name, func), False)
410+ for arg in re.findall('{(?P<n>[a-z,A-Z,0-9]*)}', fmt):
411+ if arg not in args:
412+ doError('Kprobe "%s" is missing argument "%s"' % (name, arg), False)
413+ val = 'p:%s_cal %s' % (name, func)
414+ for i in sorted(args):
415+ val += ' %s=%s' % (i, args[i])
416+ val += '\nr:%s_ret %s $retval\n' % (name, func)
417+ return val
418+ def addKprobes(self):
419+ # first test each kprobe
420+ print('INITIALIZING KPROBES...')
421+ rejects = []
422+ for name in sorted(self.kprobes):
423+ if not self.testKprobe(self.kprobes[name]):
424+ rejects.append(name)
425+ # remove all failed ones from the list
426+ for name in rejects:
427+ vprint('Skipping KPROBE: %s' % name)
428+ self.kprobes.pop(name)
429+ self.fsetVal('', 'kprobe_events')
430+ kprobeevents = ''
431+ # set the kprobes all at once
432+ for kp in self.kprobes:
433+ val = self.kprobeText(self.kprobes[kp])
434+ vprint('Adding KPROBE: %s\n%s' % (kp, val.strip()))
435+ kprobeevents += self.kprobeText(self.kprobes[kp])
436+ self.fsetVal(kprobeevents, 'kprobe_events')
437+ # verify that the kprobes were set as ordered
438+ check = self.fgetVal('kprobe_events')
439+ linesout = len(kprobeevents.split('\n'))
440+ linesack = len(check.split('\n'))
441+ if linesack < linesout:
442+ # if not, try appending the kprobes 1 by 1
443+ for kp in self.kprobes:
444+ kprobeevents = self.kprobeText(self.kprobes[kp])
445+ self.fsetVal(kprobeevents, 'kprobe_events', 'a')
446+ self.fsetVal('1', 'events/kprobes/enable')
447+ def testKprobe(self, kprobe):
448+ kprobeevents = self.kprobeText(kprobe)
449+ if not kprobeevents:
450+ return False
451+ try:
452+ self.fsetVal(kprobeevents, 'kprobe_events')
453+ check = self.fgetVal('kprobe_events')
454+ except:
455+ return False
456+ linesout = len(kprobeevents.split('\n'))
457+ linesack = len(check.split('\n'))
458+ if linesack < linesout:
459+ return False
460+ return True
461+ def fsetVal(self, val, path, mode='w'):
462+ file = self.tpath+path
463+ if not os.path.exists(file):
464+ return False
465+ try:
466+ fp = open(file, mode)
467+ fp.write(val)
468+ fp.close()
469+ except:
470+ pass
471+ return True
472+ def fgetVal(self, path):
473+ file = self.tpath+path
474+ res = ''
475+ if not os.path.exists(file):
476+ return res
477+ try:
478+ fp = open(file, 'r')
479+ res = fp.read()
480+ fp.close()
481+ except:
482+ pass
483+ return res
484+ def cleanupFtrace(self):
485+ if(self.usecallgraph or self.usetraceevents):
486+ self.fsetVal('0', 'events/kprobes/enable')
487+ self.fsetVal('', 'kprobe_events')
488+ def setupAllKprobes(self):
489+ for name in self.tracefuncs:
490+ self.defaultKprobe(name, self.tracefuncs[name])
491+ for name in self.dev_tracefuncs:
492+ self.defaultKprobe(name, self.dev_tracefuncs[name])
493+ def isCallgraphFunc(self, name):
494+ if len(self.debugfuncs) < 1 and self.suspendmode == 'command':
495+ return True
496+ if name in self.debugfuncs:
497+ return True
498+ funclist = []
499+ for i in self.tracefuncs:
500+ if 'func' in self.tracefuncs[i]:
501+ funclist.append(self.tracefuncs[i]['func'])
502+ else:
503+ funclist.append(i)
504+ if name in funclist:
505+ return True
506+ return False
507+ def initFtrace(self, testing=False):
508+ tp = self.tpath
509+ print('INITIALIZING FTRACE...')
510+ # turn trace off
511+ self.fsetVal('0', 'tracing_on')
512+ self.cleanupFtrace()
513+ # set the trace clock to global
514+ self.fsetVal('global', 'trace_clock')
515+ # set trace buffer to a huge value
516+ self.fsetVal('nop', 'current_tracer')
517+ self.fsetVal('100000', 'buffer_size_kb')
518+ # go no further if this is just a status check
519+ if testing:
520+ return
521+ if self.usekprobes:
522+ # add tracefunc kprobes so long as were not using full callgraph
523+ if(not self.usecallgraph or len(self.debugfuncs) > 0):
524+ for name in self.tracefuncs:
525+ self.defaultKprobe(name, self.tracefuncs[name])
526+ if self.usedevsrc:
527+ for name in self.dev_tracefuncs:
528+ self.defaultKprobe(name, self.dev_tracefuncs[name])
529+ else:
530+ self.usedevsrc = False
531+ self.addKprobes()
532+ # initialize the callgraph trace, unless this is an x2 run
533+ if(self.usecallgraph):
534+ # set trace type
535+ self.fsetVal('function_graph', 'current_tracer')
536+ self.fsetVal('', 'set_ftrace_filter')
537+ # set trace format options
538+ self.fsetVal('print-parent', 'trace_options')
539+ self.fsetVal('funcgraph-abstime', 'trace_options')
540+ self.fsetVal('funcgraph-cpu', 'trace_options')
541+ self.fsetVal('funcgraph-duration', 'trace_options')
542+ self.fsetVal('funcgraph-proc', 'trace_options')
543+ self.fsetVal('funcgraph-tail', 'trace_options')
544+ self.fsetVal('nofuncgraph-overhead', 'trace_options')
545+ self.fsetVal('context-info', 'trace_options')
546+ self.fsetVal('graph-time', 'trace_options')
547+ self.fsetVal('0', 'max_graph_depth')
548+ if len(self.debugfuncs) > 0:
549+ self.setFtraceFilterFunctions(self.debugfuncs)
550+ elif self.suspendmode == 'command':
551+ self.fsetVal('', 'set_graph_function')
552+ else:
553+ cf = ['dpm_run_callback']
554+ if(self.usetraceeventsonly):
555+ cf += ['dpm_prepare', 'dpm_complete']
556+ for fn in self.tracefuncs:
557+ if 'func' in self.tracefuncs[fn]:
558+ cf.append(self.tracefuncs[fn]['func'])
559+ else:
560+ cf.append(fn)
561+ self.setFtraceFilterFunctions(cf)
562+ if(self.usetraceevents):
563+ # turn trace events on
564+ events = iter(self.traceevents)
565+ for e in events:
566+ self.fsetVal('1', 'events/power/'+e+'/enable')
567+ # clear the trace buffer
568+ self.fsetVal('', 'trace')
569+ def verifyFtrace(self):
570+ # files needed for any trace data
571+ files = ['buffer_size_kb', 'current_tracer', 'trace', 'trace_clock',
572+ 'trace_marker', 'trace_options', 'tracing_on']
573+ # files needed for callgraph trace data
574+ tp = self.tpath
575+ if(self.usecallgraph):
576+ files += [
577+ 'available_filter_functions',
578+ 'set_ftrace_filter',
579+ 'set_graph_function'
580+ ]
581+ for f in files:
582+ if(os.path.exists(tp+f) == False):
583+ return False
584+ return True
585+ def verifyKprobes(self):
586+ # files needed for kprobes to work
587+ files = ['kprobe_events', 'events']
588+ tp = self.tpath
589+ for f in files:
590+ if(os.path.exists(tp+f) == False):
591+ return False
592+ return True
593+ def colorText(self, str):
594+ if not self.ansi:
595+ return str
596+ return '\x1B[31;40m'+str+'\x1B[m'
175597
176598 sysvals = SystemValues()
177599
600+# Class: DevProps
601+# Description:
602+# Simple class which holds property values collected
603+# for all the devices used in the timeline.
604+class DevProps:
605+ syspath = ''
606+ altname = ''
607+ async = True
608+ xtraclass = ''
609+ xtrainfo = ''
610+ def out(self, dev):
611+ return '%s,%s,%d;' % (dev, self.altname, self.async)
612+ def debug(self, dev):
613+ print '%s:\n\taltname = %s\n\t async = %s' % (dev, self.altname, self.async)
614+ def altName(self, dev):
615+ if not self.altname or self.altname == dev:
616+ return dev
617+ return '%s [%s]' % (self.altname, dev)
618+ def xtraClass(self):
619+ if self.xtraclass:
620+ return ' '+self.xtraclass
621+ if not self.async:
622+ return ' sync'
623+ return ''
624+ def xtraInfo(self):
625+ if self.xtraclass:
626+ return ' '+self.xtraclass
627+ if self.async:
628+ return ' async'
629+ return ' sync'
630+
178631 # Class: DeviceNode
179632 # Description:
180633 # A container used to create a device hierachy, with a single root node
@@ -228,6 +681,7 @@ class Data: · 47 unmodified lines
228681 html_device_id = 0
229682 stamp = 0
230683 outfile = ''
684+ dev_ubiquitous = ['msleep', 'udelay']
231685 def __init__(self, num):
232686 idchar = 'abcdefghijklmnopqrstuvwxyz'
233687 self.testnumber = num
@@ -257,6 +711,9 @@ class Data: · 23 unmodified lines
257711 'row': 0, 'color': '#FFFFCC', 'order': 9}
258712 }
259713 self.phases = self.sortedPhases()
714+ self.devicegroups = []
715+ for phase in self.phases:
716+ self.devicegroups.append([phase])
260717 def getStart(self):
261718 return self.dmesg[self.phases[0]]['start']
262719 def setStart(self, time):
@@ -273,51 +730,61 @@ class Data: · 10 unmodified lines
273730 for dev in list:
274731 d = list[dev]
275732 if(d['pid'] == pid and time >= d['start'] and
276− time <= d['end']):
733+ time < d['end']):
277734 return False
278735 return True
279− def addIntraDevTraceEvent(self, action, name, pid, time):
280− if(action == 'mutex_lock_try'):
281− color = 'red'
282− elif(action == 'mutex_lock_pass'):
283− color = 'green'
284− elif(action == 'mutex_unlock'):
285− color = 'blue'
286− else:
287− # create separate colors based on the name
288− v1 = len(name)*10 % 256
289− v2 = string.count(name, 'e')*100 % 256
290− v3 = ord(name[0])*20 % 256
291− color = '#%06X' % ((v1*0x10000) + (v2*0x100) + v3)
292− for phase in self.phases:
736+ def targetDevice(self, phaselist, start, end, pid=-1):
737+ tgtdev = ''
738+ for phase in phaselist:
293739 list = self.dmesg[phase]['list']
294− for dev in list:
295− d = list[dev]
296− if(d['pid'] == pid and time >= d['start'] and
297− time <= d['end']):
298− e = TraceEvent(action, name, color, time)
299− if('traceevents' not in d):
300− d['traceevents'] = []
301− d['traceevents'].append(e)
302− return d
303− break
304− return 0
305− def capIntraDevTraceEvent(self, action, name, pid, time):
306− for phase in self.phases:
307− list = self.dmesg[phase]['list']
308− for dev in list:
309− d = list[dev]
310− if(d['pid'] == pid and time >= d['start'] and
311− time <= d['end']):
312− if('traceevents' not in d):
313− return
314− for e in d['traceevents']:
315− if(e.action == action and
316− e.name == name and not e.ready):
317− e.length = time - e.time
318− e.ready = True
319− break
320− return
740+ for devname in list:
741+ dev = list[devname]
742+ if(pid >= 0 and dev['pid'] != pid):
743+ continue
744+ devS = dev['start']
745+ devE = dev['end']
746+ if(start < devS or start >= devE or end <= devS or end > devE):
747+ continue
748+ tgtdev = dev
749+ break
750+ return tgtdev
751+ def addDeviceFunctionCall(self, displayname, kprobename, proc, pid, start, end, cdata, rdata):
752+ machstart = self.dmesg['suspend_machine']['start']
753+ machend = self.dmesg['resume_machine']['end']
754+ tgtdev = self.targetDevice(self.phases, start, end, pid)
755+ if not tgtdev and start >= machstart and end < machend:
756+ # device calls in machine phases should be serial
757+ tgtdev = self.targetDevice(['suspend_machine', 'resume_machine'], start, end)
758+ if not tgtdev:
759+ if 'scsi_eh' in proc:
760+ self.newActionGlobal(proc, start, end, pid)
761+ self.addDeviceFunctionCall(displayname, kprobename, proc, pid, start, end, cdata, rdata)
762+ else:
763+ vprint('IGNORE: %s[%s](%d) [%f - %f] | %s | %s | %s' % (displayname, kprobename,
764+ pid, start, end, cdata, rdata, proc))
765+ return False
766+ # detail block fits within tgtdev
767+ if('src' not in tgtdev):
768+ tgtdev['src'] = []
769+ title = cdata+' '+rdata
770+ mstr = '\(.*\) *(?P<args>.*) *\((?P<caller>.*)\+.* arg1=(?P<ret>.*)'
771+ m = re.match(mstr, title)
772+ if m:
773+ c = m.group('caller')
774+ a = m.group('args').strip()
775+ r = m.group('ret')
776+ if len(r) > 6:
777+ r = ''
778+ else:
779+ r = 'ret=%s ' % r
780+ l = '%0.3fms' % ((end - start) * 1000)
781+ if kprobename in self.dev_ubiquitous:
782+ title = '%s(%s) <- %s, %s(%s)' % (displayname, a, c, r, l)
783+ else:
784+ title = '%s(%s) %s(%s)' % (displayname, a, r, l)
785+ e = TraceEvent(title, kprobename, start, end - start)
786+ tgtdev['src'].append(e)
787+ return True
321788 def trimTimeVal(self, t, t0, dT, left):
322789 if left:
323790 if(t > t0):
@@ -353,11 +820,11 @@ class Data: · 29 unmodified lines
353820 cg.end = self.trimTimeVal(cg.end, t0, dT, left)
354821 for line in cg.list:
355822 line.time = self.trimTimeVal(line.time, t0, dT, left)
356− if('traceevents' in d):
357− for e in d['traceevents']:
823+ if('src' in d):
824+ for e in d['src']:
358825 e.time = self.trimTimeVal(e.time, t0, dT, left)
359826 def normalizeTime(self, tZero):
360− # first trim out any standby or freeze clock time
827+ # trim out any standby or freeze clock time
361828 if(self.tSuspended != self.tResumed):
362829 if(self.tResumed > tZero):
363830 self.trimTime(self.tSuspended, \
@@ -365,29 +832,6 @@ class Data: · 1 unmodified line
365832 else:
366833 self.trimTime(self.tSuspended, \
367834 self.tResumed-self.tSuspended, False)
368− # shift the timeline so that tZero is the new 0
369− self.tSuspended -= tZero
370− self.tResumed -= tZero
371− self.start -= tZero
372− self.end -= tZero
373− for phase in self.phases:
374− p = self.dmesg[phase]
375− p['start'] -= tZero
376− p['end'] -= tZero
377− list = p['list']
378− for name in list:
379− d = list[name]
380− d['start'] -= tZero
381− d['end'] -= tZero
382− if('ftrace' in d):
383− cg = d['ftrace']
384− cg.start -= tZero
385− cg.end -= tZero
386− for line in cg.list:
387− line.time -= tZero
388− if('traceevents' in d):
389− for e in d['traceevents']:
390− e.time -= tZero
391835 def newPhaseWithSingleAction(self, phasename, devname, start, end, color):
392836 for phase in self.phases:
393837 self.dmesg[phase]['order'] += 1
@@ -417,6 +861,7 @@ class Data: · 23 unmodified lines
417861 {'list': list, 'start': start, 'end': end,
418862 'row': 0, 'color': color, 'order': order}
419863 self.phases = self.sortedPhases()
864+ self.devicegroups.append([phasename])
420865 def setPhase(self, phase, ktime, isbegin):
421866 if(isbegin):
422867 self.dmesg[phase]['start'] = ktime
@@ -442,7 +887,10 @@ class Data: · 19 unmodified lines
442887 for devname in phaselist:
443888 dev = phaselist[devname]
444889 if(dev['end'] < 0):
445− dev['end'] = end
890+ for p in self.phases:
891+ if self.dmesg[p]['end'] > dev['start']:
892+ dev['end'] = self.dmesg[p]['end']
893+ break
446894 vprint('%s (%s): callback didnt return' % (devname, phase))
447895 def deviceFilter(self, devicefilter):
448896 # remove all by the relatives of the filter devnames
@@ -472,22 +920,58 @@ class Data: · 23 unmodified lines
472920 # if any calls never returned, clip them at system resume end
473921 for phase in self.phases:
474922 self.fixupInitcalls(phase, self.getEnd())
475− def newActionGlobal(self, name, start, end):
923+ def isInsideTimeline(self, start, end):
924+ if(self.start <= start and self.end > start):
925+ return True
926+ return False
927+ def phaseOverlap(self, phases):
928+ rmgroups = []
929+ newgroup = []
930+ for group in self.devicegroups:
931+ for phase in phases:
932+ if phase not in group:
933+ continue
934+ for p in group:
935+ if p not in newgroup:
936+ newgroup.append(p)
937+ if group not in rmgroups:
938+ rmgroups.append(group)
939+ for group in rmgroups:
940+ self.devicegroups.remove(group)
941+ self.devicegroups.append(newgroup)
942+ def newActionGlobal(self, name, start, end, pid=-1, color=''):
943+ # if event starts before timeline start, expand timeline
944+ if(start < self.start):
945+ self.setStart(start)
946+ # if event ends after timeline end, expand the timeline
947+ if(end > self.end):
948+ self.setEnd(end)
476949 # which phase is this device callback or action "in"
477950 targetphase = "none"
951+ htmlclass = ''
478952 overlap = 0.0
953+ phases = []
479954 for phase in self.phases:
480955 pstart = self.dmesg[phase]['start']
481956 pend = self.dmesg[phase]['end']
482957 o = max(0, min(end, pend) - max(start, pstart))
483− if(o > overlap):
958+ if o > 0:
959+ phases.append(phase)
960+ if o > overlap:
961+ if overlap > 0 and phase == 'post_resume':
962+ continue
484963 targetphase = phase
485964 overlap = o
965+ if pid == -2:
966+ htmlclass = ' bg'
967+ if len(phases) > 1:
968+ htmlclass = ' bg'
969+ self.phaseOverlap(phases)
486970 if targetphase in self.phases:
487− self.newAction(targetphase, name, -1, '', start, end, '')
488− return True
971+ newname = self.newAction(targetphase, name, pid, '', start, end, '', htmlclass, color)
972+ return (targetphase, newname)
489973 return False
490− def newAction(self, phase, name, pid, parent, start, end, drv):
974+ def newAction(self, phase, name, pid, parent, start, end, drv, htmlclass='', color=''):
491975 # new device callback for a specific phase
492976 self.html_device_id += 1
493977 devid = '%s%d' % (self.idstr, self.html_device_id)
@@ -495,8 +979,19 @@ class Data: · 1 unmodified line
495979 length = -1.0
496980 if(start >= 0 and end >= 0):
497981 length = end - start
982+ if pid == -2:
983+ i = 2
984+ origname = name
985+ while(name in list):
986+ name = '%s[%d]' % (origname, i)
987+ i += 1
498988 list[name] = {'start': start, 'end': end, 'pid': pid, 'par': parent,
499989 'length': length, 'row': 0, 'id': devid, 'drv': drv }
990+ if htmlclass:
991+ list[name]['htmlclass'] = htmlclass
992+ if color:
993+ list[name]['color'] = color
994+ return name
500995 def deviceIDs(self, devlist, phase):
501996 idlist = []
502997 list = self.dmesg[phase]['list']
@@ -536,10 +1031,21 @@ class Data: · 33 unmodified lines
5361031 vprint(' %16s: %f - %f (%d devices)' % (phase, \
5371032 self.dmesg[phase]['start'], self.dmesg[phase]['end'], dc))
5381033 vprint(' test end: %f' % self.end)
1034+ def deviceChildrenAllPhases(self, devname):
1035+ devlist = []
1036+ for phase in self.phases:
1037+ list = self.deviceChildren(devname, phase)
1038+ for dev in list:
1039+ if dev not in devlist:
1040+ devlist.append(dev)
1041+ return devlist
5391042 def masterTopology(self, name, list, depth):
5401043 node = DeviceNode(name, depth)
5411044 for cname in list:
542− clist = self.deviceChildren(cname, 'resume')
1045+ # avoid recursions
1046+ if name == cname:
1047+ continue
1048+ clist = self.deviceChildrenAllPhases(cname)
5431049 cnode = self.masterTopology(cname, clist, depth+1)
5441050 node.children.append(cnode)
5451051 return node
@@ -580,7 +1086,8 @@ class Data: · 34 unmodified lines
5801086 list = self.dmesg[phase]['list']
5811087 for dev in list:
5821088 pdev = list[dev]['par']
583− if(re.match('[0-9]*-[0-9]*\.[0-9]*[\.0-9]*\:[\.0-9]*$', pdev)):
1089+ pid = list[dev]['pid']
1090+ if(pid < 0 or re.match('[0-9]*-[0-9]*\.[0-9]*[\.0-9]*\:[\.0-9]*$', pdev)):
5841091 continue
5851092 if pdev and pdev not in real and pdev not in rootlist:
5861093 rootlist.append(pdev)
@@ -589,22 +1096,33 @@ class Data: · 2 unmodified lines
5891096 rootlist = self.rootDeviceList()
5901097 master = self.masterTopology('', rootlist, 0)
5911098 return self.printTopology(master)
1099+ def selectTimelineDevices(self, widfmt, tTotal, mindevlen):
1100+ # only select devices that will actually show up in html
1101+ self.tdevlist = dict()
1102+ for phase in self.dmesg:
1103+ devlist = []
1104+ list = self.dmesg[phase]['list']
1105+ for dev in list:
1106+ length = (list[dev]['end'] - list[dev]['start']) * 1000
1107+ width = widfmt % (((list[dev]['end']-list[dev]['start'])*100)/tTotal)
1108+ if width != '0.000000' and length >= mindevlen:
1109+ devlist.append(dev)
1110+ self.tdevlist[phase] = devlist
5921111
5931112 # Class: TraceEvent
5941113 # Description:
5951114 # A container for trace event data found in the ftrace file
5961115 class TraceEvent:
597− ready = False
598− name = ''
1116+ text = ''
5991117 time = 0.0
600− color = '#FFFFFF'
6011118 length = 0.0
602− action = ''
603− def __init__(self, a, n, c, t):
604− self.action = a
605− self.name = n
606− self.color = c
1119+ title = ''
1120+ row = 0
1121+ def __init__(self, a, n, t, l):
1122+ self.title = a
1123+ self.text = n
6071124 self.time = t
1125+ self.length = l
6081126
6091127 # Class: FTraceLine
6101128 # Description:
@@ -623,11 +1141,14 @@ class FTraceLine: · 12 unmodified lines
6231141 fcall = False
6241142 freturn = False
6251143 fevent = False
1144+ fkprobe = False
6261145 depth = 0
6271146 name = ''
6281147 type = ''
629− def __init__(self, t, m, d):
1148+ def __init__(self, t, m='', d=''):
6301149 self.time = float(t)
1150+ if not m and not d:
1151+ return
6311152 # is this a trace event
6321153 if(d == 'traceevent' or re.match('^ *\/\* *(?P<msg>.*) \*\/ *$', m)):
6331154 if(d == 'traceevent'):
@@ -644,6 +1165,18 @@ class FTraceLine: · 10 unmodified lines
6441165 self.type = emm.group('call')
6451166 else:
6461167 self.name = msg
1168+ km = re.match('^(?P<n>.*)_cal$', self.type)
1169+ if km:
1170+ self.fcall = True
1171+ self.fkprobe = True
1172+ self.type = km.group('n')
1173+ return
1174+ km = re.match('^(?P<n>.*)_ret$', self.type)
1175+ if km:
1176+ self.freturn = True
1177+ self.fkprobe = True
1178+ self.type = km.group('n')
1179+ return
6471180 self.fevent = True
6481181 return
6491182 # convert the duration to seconds
@@ -662,7 +1195,7 @@ class FTraceLine: · 12 unmodified lines
6621195 # includes comment with function name
6631196 match = re.match('^} *\/\* *(?P<n>.*) *\*\/$', m)
6641197 if(match):
665− self.name = match.group('n')
1198+ self.name = match.group('n').strip()
6661199 # function call
6671200 else:
6681201 self.fcall = True
@@ -670,19 +1203,19 @@ class FTraceLine: · 1 unmodified line
6701203 if(m[-1] == '{'):
6711204 match = re.match('^(?P<n>.*) *\(.*', m)
6721205 if(match):
673− self.name = match.group('n')
1206+ self.name = match.group('n').strip()
6741207 # function call with no children (leaf)
6751208 elif(m[-1] == ';'):
6761209 self.freturn = True
6771210 match = re.match('^(?P<n>.*) *\(.*', m)
6781211 if(match):
679− self.name = match.group('n')
1212+ self.name = match.group('n').strip()
6801213 # something else (possibly a trace marker)
6811214 else:
6821215 self.name = m
6831216 def getDepth(self, str):
6841217 return len(str)/2
685− def debugPrint(self, dev):
1218+ def debugPrint(self, dev=''):
6861219 if(self.freturn and self.fcall):
6871220 print('%s -- %f (%02d): %s(); (%.3f us)' % (dev, self.time, \
6881221 self.depth, self.name, self.length*1000000))
@@ -692,6 +1225,33 @@ class FTraceLine: · 3 unmodified lines
6921225 else:
6931226 print('%s -- %f (%02d): %s() { (%.3f us)' % (dev, self.time, \
6941227 self.depth, self.name, self.length*1000000))
1228+ def startMarker(self):
1229+ global sysvals
1230+ # Is this the starting line of a suspend?
1231+ if not self.fevent:
1232+ return False
1233+ if sysvals.usetracemarkers:
1234+ if(self.name == 'SUSPEND START'):
1235+ return True
1236+ return False
1237+ else:
1238+ if(self.type == 'suspend_resume' and
1239+ re.match('suspend_enter\[.*\] begin', self.name)):
1240+ return True
1241+ return False
1242+ def endMarker(self):
1243+ # Is this the ending line of a resume?
1244+ if not self.fevent:
1245+ return False
1246+ if sysvals.usetracemarkers:
1247+ if(self.name == 'RESUME COMPLETE'):
1248+ return True
1249+ return False
1250+ else:
1251+ if(self.type == 'suspend_resume' and
1252+ re.match('thaw_processes\[.*\] end', self.name)):
1253+ return True
1254+ return False
6951255
6961256 # Class: FTraceCallGraph
6971257 # Description:
@@ -705,54 +1265,124 @@ class FTraceCallGraph: · 7 unmodified lines
7051265 list = []
7061266 invalid = False
7071267 depth = 0
708− def __init__(self):
1268+ pid = 0
1269+ def __init__(self, pid):
7091270 self.start = -1.0
7101271 self.end = -1.0
7111272 self.list = []
7121273 self.depth = 0
713− def setDepth(self, line):
1274+ self.pid = pid
1275+ def addLine(self, line, debug=False):
1276+ # if this is already invalid, just leave
1277+ if(self.invalid):
1278+ return False
1279+ # invalidate on too much data or bad depth
1280+ if(len(self.list) >= 1000000 or self.depth < 0):
1281+ self.invalidate(line)
1282+ return False
1283+ # compare current depth with this lines pre-call depth
1284+ prelinedep = line.depth
1285+ if(line.freturn and not line.fcall):
1286+ prelinedep += 1
1287+ last = 0
1288+ lasttime = line.time
1289+ virtualfname = 'execution_misalignment'
1290+ if len(self.list) > 0:
1291+ last = self.list[-1]
1292+ lasttime = last.time
1293+ # handle low misalignments by inserting returns
1294+ if prelinedep < self.depth:
1295+ if debug and last:
1296+ print '-------- task %d --------' % self.pid
1297+ last.debugPrint()
1298+ idx = 0
1299+ # add return calls to get the depth down
1300+ while prelinedep < self.depth:
1301+ if debug:
1302+ print 'MISALIGN LOW (add returns): C%d - eC%d' % (self.depth, prelinedep)
1303+ self.depth -= 1
1304+ if idx == 0 and last and last.fcall and not last.freturn:
1305+ # special case, turn last call into a leaf
1306+ last.depth = self.depth
1307+ last.freturn = True
1308+ last.length = line.time - last.time
1309+ if debug:
1310+ last.debugPrint()
1311+ else:
1312+ vline = FTraceLine(lasttime)
1313+ vline.depth = self.depth
1314+ vline.name = virtualfname
1315+ vline.freturn = True
1316+ self.list.append(vline)
1317+ if debug:
1318+ vline.debugPrint()
1319+ idx += 1
1320+ if debug:
1321+ line.debugPrint()
1322+ print ''
1323+ # handle high misalignments by inserting calls
1324+ elif prelinedep > self.depth:
1325+ if debug and last:
1326+ print '-------- task %d --------' % self.pid
1327+ last.debugPrint()
1328+ idx = 0
1329+ # add calls to get the depth up
1330+ while prelinedep > self.depth:
1331+ if debug:
1332+ print 'MISALIGN HIGH (add calls): C%d - eC%d' % (self.depth, prelinedep)
1333+ if idx == 0 and line.freturn and not line.fcall:
1334+ # special case, turn this return into a leaf
1335+ line.fcall = True
1336+ prelinedep -= 1
1337+ else:
1338+ vline = FTraceLine(lasttime)
1339+ vline.depth = self.depth
1340+ vline.name = virtualfname
1341+ vline.fcall = True
1342+ if debug:
1343+ vline.debugPrint()
1344+ self.list.append(vline)
1345+ self.depth += 1
1346+ if not last:
1347+ self.start = vline.time
1348+ idx += 1
1349+ if debug:
1350+ line.debugPrint()
1351+ print ''
1352+ # process the call and set the new depth
7141353 if(line.fcall and not line.freturn):
715− line.depth = self.depth
7161354 self.depth += 1
7171355 elif(line.freturn and not line.fcall):
7181356 self.depth -= 1
719− line.depth = self.depth
720− else:
721− line.depth = self.depth
722− def addLine(self, line, match):
723− if(not self.invalid):
724− self.setDepth(line)
1357+ if len(self.list) < 1:
1358+ self.start = line.time
1359+ self.list.append(line)
7251360 if(line.depth == 0 and line.freturn):
7261361 if(self.start < 0):
7271362 self.start = line.time
7281363 self.end = line.time
729− self.list.append(line)
1364+ if line.fcall:
1365+ self.end += line.length
1366+ if self.list[0].name == virtualfname:
1367+ self.invalid = True
7301368 return True
731− if(self.invalid):
732− return False
733− if(len(self.list) >= 1000000 or self.depth < 0):
734− if(len(self.list) > 0):
735− first = self.list[0]
736− self.list = []
737− self.list.append(first)
738− self.invalid = True
739− if(not match):
740− return False
741− id = 'task %s cpu %s' % (match.group('pid'), match.group('cpu'))
742− window = '(%f - %f)' % (self.start, line.time)
743− if(self.depth < 0):
744− print('Too much data for '+id+\
745− ' (buffer overflow), ignoring this callback')
746− else:
747− print('Too much data for '+id+\
748− ' '+window+', ignoring this callback')
749− return False
750− self.list.append(line)
751− if(self.start < 0):
752− self.start = line.time
7531369 return False
1370+ def invalidate(self, line):
1371+ if(len(self.list) > 0):
1372+ first = self.list[0]
1373+ self.list = []
1374+ self.list.append(first)
1375+ self.invalid = True
1376+ id = 'task %s' % (self.pid)
1377+ window = '(%f - %f)' % (self.start, line.time)
1378+ if(self.depth < 0):
1379+ vprint('Too much data for '+id+\
1380+ ' (buffer overflow), ignoring this callback')
1381+ else:
1382+ vprint('Too much data for '+id+\
1383+ ' '+window+', ignoring this callback')
7541384 def slice(self, t0, tN):
755− minicg = FTraceCallGraph()
1385+ minicg = FTraceCallGraph(0)
7561386 count = -1
7571387 firstdepth = 0
7581388 for l in self.list:
@@ -764,13 +1394,26 @@ class FTraceCallGraph: · 5 unmodified lines
7641394 firstdepth = l.depth
7651395 count = 0
7661396 l.depth -= firstdepth
767− minicg.addLine(l, 0)
1397+ minicg.addLine(l)
7681398 if((count == 0 and l.freturn and l.fcall) or
7691399 (count > 0 and l.depth <= 0)):
7701400 break
7711401 count += 1
7721402 return minicg
773− def sanityCheck(self):
1403+ def repair(self, enddepth):
1404+ # bring the depth back to 0 with additional returns
1405+ fixed = False
1406+ last = self.list[-1]
1407+ for i in reversed(range(enddepth)):
1408+ t = FTraceLine(last.time)
1409+ t.depth = i
1410+ t.freturn = True
1411+ fixed = self.addLine(t)
1412+ if fixed:
1413+ self.end = last.time
1414+ return True
1415+ return False
1416+ def postProcess(self, debug=False):
7741417 stack = dict()
7751418 cnt = 0
7761419 for l in self.list:
@@ -779,98 +1422,317 @@ class FTraceCallGraph: · 2 unmodified lines
7791422 cnt += 1
7801423 elif(l.freturn and not l.fcall):
7811424 if(l.depth not in stack):
1425+ if debug:
1426+ print 'Post Process Error: Depth missing'
1427+ l.debugPrint()
7821428 return False
1429+ # transfer total time from return line to call line
7831430 stack[l.depth].length = l.length
784− stack[l.depth] = 0
1431+ stack.pop(l.depth)
7851432 l.length = 0
7861433 cnt -= 1
7871434 if(cnt == 0):
1435+ # trace caught the whole call tree
7881436 return True
789− return False
790− def debugPrint(self, filename):
791− if(filename == 'stdout'):
792− print('[%f - %f]') % (self.start, self.end)
793− for l in self.list:
794− if(l.freturn and l.fcall):
795− print('%f (%02d): %s(); (%.3f us)' % (l.time, \
796− l.depth, l.name, l.length*1000000))
797− elif(l.freturn):
798− print('%f (%02d): %s} (%.3f us)' % (l.time, \
799− l.depth, l.name, l.length*1000000))
800− else:
801− print('%f (%02d): %s() { (%.3f us)' % (l.time, \
802− l.depth, l.name, l.length*1000000))
803− print(' ')
804− else:
805− fp = open(filename, 'w')
806− print(filename)
807− for l in self.list:
808− if(l.freturn and l.fcall):
809− fp.write('%f (%02d): %s(); (%.3f us)\n' % (l.time, \
810− l.depth, l.name, l.length*1000000))
811− elif(l.freturn):
812− fp.write('%f (%02d): %s} (%.3f us)\n' % (l.time, \
813− l.depth, l.name, l.length*1000000))
814− else:
815− fp.write('%f (%02d): %s() { (%.3f us)\n' % (l.time, \
816− l.depth, l.name, l.length*1000000))
817− fp.close()
1437+ elif(cnt < 0):
1438+ if debug:
1439+ print 'Post Process Error: Depth is less than 0'
1440+ return False
1441+ # trace ended before call tree finished
1442+ return self.repair(cnt)
1443+ def deviceMatch(self, pid, data):
1444+ found = False
1445+ # add the callgraph data to the device hierarchy
1446+ borderphase = {
1447+ 'dpm_prepare': 'suspend_prepare',
1448+ 'dpm_complete': 'resume_complete'
1449+ }
1450+ if(self.list[0].name in borderphase):
1451+ p = borderphase[self.list[0].name]
1452+ list = data.dmesg[p]['list']
1453+ for devname in list:
1454+ dev = list[devname]
1455+ if(pid == dev['pid'] and
1456+ self.start <= dev['start'] and
1457+ self.end >= dev['end']):
1458+ dev['ftrace'] = self.slice(dev['start'], dev['end'])
1459+ found = True
1460+ return found
1461+ for p in data.phases:
1462+ if(data.dmesg[p]['start'] <= self.start and
1463+ self.start <= data.dmesg[p]['end']):
1464+ list = data.dmesg[p]['list']
1465+ for devname in list:
1466+ dev = list[devname]
1467+ if(pid == dev['pid'] and
1468+ self.start <= dev['start'] and
1469+ self.end >= dev['end']):
1470+ dev['ftrace'] = self
1471+ found = True
1472+ break
1473+ break
1474+ return found
1475+ def newActionFromFunction(self, data):
1476+ name = self.list[0].name
1477+ if name in ['dpm_run_callback', 'dpm_prepare', 'dpm_complete']:
1478+ return
1479+ fs = self.start
1480+ fe = self.end
1481+ if fs < data.start or fe > data.end:
1482+ return
1483+ phase = ''
1484+ for p in data.phases:
1485+ if(data.dmesg[p]['start'] <= self.start and
1486+ self.start < data.dmesg[p]['end']):
1487+ phase = p
1488+ break
1489+ if not phase:
1490+ return
1491+ out = data.newActionGlobal(name, fs, fe, -2)
1492+ if out:
1493+ phase, myname = out
1494+ data.dmesg[phase]['list'][myname]['ftrace'] = self
1495+ def debugPrint(self):
1496+ print('[%f - %f] %s (%d)') % (self.start, self.end, self.list[0].name, self.pid)
1497+ for l in self.list:
1498+ if(l.freturn and l.fcall):
1499+ print('%f (%02d): %s(); (%.3f us)' % (l.time, \
1500+ l.depth, l.name, l.length*1000000))
1501+ elif(l.freturn):
1502+ print('%f (%02d): %s} (%.3f us)' % (l.time, \
1503+ l.depth, l.name, l.length*1000000))
1504+ else:
1505+ print('%f (%02d): %s() { (%.3f us)' % (l.time, \
1506+ l.depth, l.name, l.length*1000000))
1507+ print(' ')
8181508
8191509 # Class: Timeline
8201510 # Description:
821−# A container for a suspend/resume html timeline. In older versions
822−# of the script there were multiple timelines, but in the latest
823−# there is only one.
1511+# A container for a device timeline which calculates
1512+# all the html properties to display it correctly
8241513 class Timeline:
8251514 html = {}
826− scaleH = 0.0 # height of the row as a percent of the timeline height
827− rowH = 0.0 # height of each row in percent of the timeline height
828− row_height_pixels = 30
829− maxrows = 0
830− height = 0
831− def __init__(self):
1515+ height = 0 # total timeline height
1516+ scaleH = 20 # timescale (top) row height
1517+ rowH = 30 # device row height
1518+ bodyH = 0 # body height
1519+ rows = 0 # total timeline rows
1520+ phases = []
1521+ rowmaxlines = dict()
1522+ rowcount = dict()
1523+ rowheight = dict()
1524+ def __init__(self, rowheight):
1525+ self.rowH = rowheight
8321526 self.html = {
1527+ 'header': '',
8331528 'timeline': '',
8341529 'legend': '',
835− 'scale': ''
8361530 }
837− def setRows(self, rows):
838− self.maxrows = int(rows)
839− self.scaleH = 100.0/float(self.maxrows)
840− self.height = self.maxrows*self.row_height_pixels
841− r = float(self.maxrows - 1)
842− if(r < 1.0):
843− r = 1.0
844− self.rowH = (100.0 - self.scaleH)/r
1531+ # Function: getDeviceRows
1532+ # Description:
1533+ # determine how may rows the device funcs will take
1534+ # Arguments:
1535+ # rawlist: the list of devices/actions for a single phase
1536+ # Output:
1537+ # The total number of rows needed to display this phase of the timeline
1538+ def getDeviceRows(self, rawlist):
1539+ # clear all rows and set them to undefined
1540+ lendict = dict()
1541+ for item in rawlist:
1542+ item.row = -1
1543+ lendict[item] = item.length
1544+ list = []
1545+ for i in sorted(lendict, key=lendict.get, reverse=True):
1546+ list.append(i)
1547+ remaining = len(list)
1548+ rowdata = dict()
1549+ row = 1
1550+ # try to pack each row with as many ranges as possible
1551+ while(remaining > 0):
1552+ if(row not in rowdata):
1553+ rowdata[row] = []
1554+ for i in list:
1555+ if(i.row >= 0):
1556+ continue
1557+ s = i.time
1558+ e = i.time + i.length
1559+ valid = True
1560+ for ritem in rowdata[row]:
1561+ rs = ritem.time
1562+ re = ritem.time + ritem.length
1563+ if(not (((s <= rs) and (e <= rs)) or
1564+ ((s >= re) and (e >= re)))):
1565+ valid = False
1566+ break
1567+ if(valid):
1568+ rowdata[row].append(i)
1569+ i.row = row
1570+ remaining -= 1
1571+ row += 1
1572+ return row
1573+ # Function: getPhaseRows
1574+ # Description:
1575+ # Organize the timeline entries into the smallest
1576+ # number of rows possible, with no entry overlapping
1577+ # Arguments:
1578+ # list: the list of devices/actions for a single phase
1579+ # devlist: string list of device names to use
1580+ # Output:
1581+ # The total number of rows needed to display this phase of the timeline
1582+ def getPhaseRows(self, dmesg, devlist):
1583+ # clear all rows and set them to undefined
1584+ remaining = len(devlist)
1585+ rowdata = dict()
1586+ row = 0
1587+ lendict = dict()
1588+ myphases = []
1589+ for item in devlist:
1590+ if item[0] not in self.phases:
1591+ self.phases.append(item[0])
1592+ if item[0] not in myphases:
1593+ myphases.append(item[0])
1594+ self.rowmaxlines[item[0]] = dict()
1595+ self.rowheight[item[0]] = dict()
1596+ dev = dmesg[item[0]]['list'][item[1]]
1597+ dev['row'] = -1
1598+ lendict[item] = float(dev['end']) - float(dev['start'])
1599+ if 'src' in dev:
1600+ dev['devrows'] = self.getDeviceRows(dev['src'])
1601+ lenlist = []
1602+ for i in sorted(lendict, key=lendict.get, reverse=True):
1603+ lenlist.append(i)
1604+ orderedlist = []
1605+ for item in lenlist:
1606+ dev = dmesg[item[0]]['list'][item[1]]
1607+ if dev['pid'] == -2:
1608+ orderedlist.append(item)
1609+ for item in lenlist:
1610+ if item not in orderedlist:
1611+ orderedlist.append(item)
1612+ # try to pack each row with as many ranges as possible
1613+ while(remaining > 0):
1614+ rowheight = 1
1615+ if(row not in rowdata):
1616+ rowdata[row] = []
1617+ for item in orderedlist:
1618+ dev = dmesg[item[0]]['list'][item[1]]
1619+ if(dev['row'] < 0):
1620+ s = dev['start']
1621+ e = dev['end']
1622+ valid = True
1623+ for ritem in rowdata[row]:
1624+ rs = ritem['start']
1625+ re = ritem['end']
1626+ if(not (((s <= rs) and (e <= rs)) or
1627+ ((s >= re) and (e >= re)))):
1628+ valid = False
1629+ break
1630+ if(valid):
1631+ rowdata[row].append(dev)
1632+ dev['row'] = row
1633+ remaining -= 1
1634+ if 'devrows' in dev and dev['devrows'] > rowheight:
1635+ rowheight = dev['devrows']
1636+ for phase in myphases:
1637+ self.rowmaxlines[phase][row] = rowheight
1638+ self.rowheight[phase][row] = rowheight * self.rowH
1639+ row += 1
1640+ if(row > self.rows):
1641+ self.rows = int(row)
1642+ for phase in myphases:
1643+ self.rowcount[phase] = row
1644+ return row
1645+ def phaseRowHeight(self, phase, row):
1646+ return self.rowheight[phase][row]
1647+ def phaseRowTop(self, phase, row):
1648+ top = 0
1649+ for i in sorted(self.rowheight[phase]):
1650+ if i >= row:
1651+ break
1652+ top += self.rowheight[phase][i]
1653+ return top
1654+ # Function: calcTotalRows
1655+ # Description:
1656+ # Calculate the heights and offsets for the header and rows
1657+ def calcTotalRows(self):
1658+ maxrows = 0
1659+ standardphases = []
1660+ for phase in self.phases:
1661+ total = 0
1662+ for i in sorted(self.rowmaxlines[phase]):
1663+ total += self.rowmaxlines[phase][i]
1664+ if total > maxrows:
1665+ maxrows = total
1666+ if total == self.rowcount[phase]:
1667+ standardphases.append(phase)
1668+ self.height = self.scaleH + (maxrows*self.rowH)
1669+ self.bodyH = self.height - self.scaleH
1670+ for phase in standardphases:
1671+ for i in sorted(self.rowheight[phase]):
1672+ self.rowheight[phase][i] = self.bodyH/self.rowcount[phase]
1673+ # Function: createTimeScale
1674+ # Description:
1675+ # Create the timescale for a timeline block
1676+ # Arguments:
1677+ # m0: start time (mode begin)
1678+ # mMax: end time (mode end)
1679+ # tTotal: total timeline time
1680+ # mode: suspend or resume
1681+ # Output:
1682+ # The html code needed to display the time scale
1683+ def createTimeScale(self, m0, mMax, tTotal, mode):
1684+ timescale = '<div class="t" style="right:{0}%">{1}</div>\n'
1685+ rline = '<div class="t" style="left:0;border-left:1px solid black;border-right:0;">Resume</div>\n'
1686+ output = '<div class="timescale">\n'
1687+ # set scale for timeline
1688+ mTotal = mMax - m0
1689+ tS = 0.1
1690+ if(tTotal <= 0):
1691+ return output+'</div>\n'
1692+ if(tTotal > 4):
1693+ tS = 1
1694+ divTotal = int(mTotal/tS) + 1
1695+ divEdge = (mTotal - tS*(divTotal-1))*100/mTotal
1696+ for i in range(divTotal):
1697+ htmlline = ''
1698+ if(mode == 'resume'):
1699+ pos = '%0.3f' % (100 - ((float(i)*tS*100)/mTotal))
1700+ val = '%0.fms' % (float(i)*tS*1000)
1701+ htmlline = timescale.format(pos, val)
1702+ if(i == 0):
1703+ htmlline = rline
1704+ else:
1705+ pos = '%0.3f' % (100 - ((float(i)*tS*100)/mTotal) - divEdge)
1706+ val = '%0.fms' % (float(i-divTotal+1)*tS*1000)
1707+ if(i == divTotal - 1):
1708+ val = 'Suspend'
1709+ htmlline = timescale.format(pos, val)
1710+ output += htmlline
1711+ output += '</div>\n'
1712+ return output
8451713
846−# Class: TestRun
1714+# Class: TestProps
8471715 # Description:
848−# A container for a suspend/resume test run. This is necessary as
849−# there could be more than one, and they need to be separate.
850−class TestRun:
1716+# A list of values describing the properties of these test runs
1717+class TestProps:
1718+ stamp = ''
1719+ tracertype = ''
1720+ S0i3 = False
1721+ fwdata = []
8511722 ftrace_line_fmt_fg = \
8521723 '^ *(?P<time>[0-9\.]*) *\| *(?P<cpu>[0-9]*)\)'+\
8531724 ' *(?P<proc>.*)-(?P<pid>[0-9]*) *\|'+\
854− '[ +!]*(?P<dur>[0-9\.]*) .*\| (?P<msg>.*)'
1725+ '[ +!#\*@$]*(?P<dur>[0-9\.]*) .*\| (?P<msg>.*)'
8551726 ftrace_line_fmt_nop = \
8561727 ' *(?P<proc>.*)-(?P<pid>[0-9]*) *\[(?P<cpu>[0-9]*)\] *'+\
8571728 '(?P<flags>.{4}) *(?P<time>[0-9\.]*): *'+\
8581729 '(?P<msg>.*)'
8591730 ftrace_line_fmt = ftrace_line_fmt_nop
8601731 cgformat = False
861− ftemp = dict()
862− ttemp = dict()
863− inthepipe = False
864− tracertype = ''
8651732 data = 0
866− def __init__(self, dataobj):
867− self.data = dataobj
868− self.ftemp = dict()
869− self.ttemp = dict()
870− def isReady(self):
871− if(tracertype == '' or not data):
872− return False
873− return True
1733+ ktemp = dict()
1734+ def __init__(self):
1735+ self.ktemp = dict()
8741736 def setTracerType(self, tracer):
8751737 self.tracertype = tracer
8761738 if(tracer == 'function_graph'):
@@ -881,6 +1743,19 @@ class TestRun: · 4 unmodified lines
8811743 else:
8821744 doError('Invalid tracer format: [%s]' % tracer, False)
8831745
1746+# Class: TestRun
1747+# Description:
1748+# A container for a suspend/resume test run. This is necessary as
1749+# there could be more than one, and they need to be separate.
1750+class TestRun:
1751+ ftemp = dict()
1752+ ttemp = dict()
1753+ data = 0
1754+ def __init__(self, dataobj):
1755+ self.data = dataobj
1756+ self.ftemp = dict()
1757+ self.ttemp = dict()
1758+
8841759 # ----------------- FUNCTIONS --------------------
8851760
8861761 # Function: vprint
@@ -893,104 +1768,16 @@ def vprint(msg): · 6 unmodified lines
8931768 if(sysvals.verbose):
8941769 print(msg)
8951770
896−# Function: initFtrace
897−# Description:
898−# Configure ftrace to use trace events and/or a callgraph
899−def initFtrace():
900− global sysvals
901−
902− tp = sysvals.tpath
903− cf = 'dpm_run_callback'
904− if(sysvals.usetraceeventsonly):
905− cf = '-e dpm_prepare -e dpm_complete -e dpm_run_callback'
906− if(sysvals.usecallgraph or sysvals.usetraceevents):
907− print('INITIALIZING FTRACE...')
908− # turn trace off
909− os.system('echo 0 > '+tp+'tracing_on')
910− # set the trace clock to global
911− os.system('echo global > '+tp+'trace_clock')
912− # set trace buffer to a huge value
913− os.system('echo nop > '+tp+'current_tracer')
914− os.system('echo 100000 > '+tp+'buffer_size_kb')
915− # initialize the callgraph trace, unless this is an x2 run
916− if(sysvals.usecallgraph and sysvals.execcount == 1):
917− # set trace type
918− os.system('echo function_graph > '+tp+'current_tracer')
919− os.system('echo "" > '+tp+'set_ftrace_filter')
920− # set trace format options
921− os.system('echo funcgraph-abstime > '+tp+'trace_options')
922− os.system('echo funcgraph-proc > '+tp+'trace_options')
923− # focus only on device suspend and resume
924− os.system('cat '+tp+'available_filter_functions | grep '+\
925− cf+' > '+tp+'set_graph_function')
926− if(sysvals.usetraceevents):
927− # turn trace events on
928− events = iter(sysvals.traceevents)
929− for e in events:
930− os.system('echo 1 > '+sysvals.epath+e+'/enable')
931− # clear the trace buffer
932− os.system('echo "" > '+tp+'trace')
933−
934−# Function: initFtraceAndroid
935−# Description:
936−# Configure ftrace to capture trace events
937−def initFtraceAndroid():
938− global sysvals
939−
940− tp = sysvals.tpath
941− if(sysvals.usetraceevents):
942− print('INITIALIZING FTRACE...')
943− # turn trace off
944− os.system(sysvals.adb+" shell 'echo 0 > "+tp+"tracing_on'")
945− # set the trace clock to global
946− os.system(sysvals.adb+" shell 'echo global > "+tp+"trace_clock'")
947− # set trace buffer to a huge value
948− os.system(sysvals.adb+" shell 'echo nop > "+tp+"current_tracer'")
949− os.system(sysvals.adb+" shell 'echo 10000 > "+tp+"buffer_size_kb'")
950− # turn trace events on
951− events = iter(sysvals.traceevents)
952− for e in events:
953− os.system(sysvals.adb+" shell 'echo 1 > "+\
954− sysvals.epath+e+"/enable'")
955− # clear the trace buffer
956− os.system(sysvals.adb+" shell 'echo \"\" > "+tp+"trace'")
957−
958−# Function: verifyFtrace
959−# Description:
960−# Check that ftrace is working on the system
961−# Output:
962−# True or False
963−def verifyFtrace():
964− global sysvals
965− # files needed for any trace data
966− files = ['buffer_size_kb', 'current_tracer', 'trace', 'trace_clock',
967− 'trace_marker', 'trace_options', 'tracing_on']
968− # files needed for callgraph trace data
969− tp = sysvals.tpath
970− if(sysvals.usecallgraph):
971− files += [
972− 'available_filter_functions',
973− 'set_ftrace_filter',
974− 'set_graph_function'
975− ]
976− for f in files:
977− if(sysvals.android):
978− out = os.popen(sysvals.adb+' shell ls '+tp+f).read().strip()
979− if(out != tp+f):
980− return False
981− else:
982− if(os.path.exists(tp+f) == False):
983− return False
984− return True
985−
9861771 # Function: parseStamp
9871772 # Description:
9881773 # Pull in the stamp comment line from the data file(s),
9891774 # create the stamp, and add it to the global sysvals object
9901775 # Arguments:
9911776 # m: the valid re.match output for the stamp line
992−def parseStamp(m, data):
1777+def parseStamp(line, data):
9931778 global sysvals
1779+
1780+ m = re.match(sysvals.stampfmt, line)
9941781 data.stamp = {'time': '', 'host': '', 'mode': ''}
9951782 dt = datetime(int(m.group('y'))+2000, int(m.group('m')),
9961783 int(m.group('d')), int(m.group('H')), int(m.group('M')),
@@ -999,6 +1786,7 @@ def parseStamp(m, data): · 2 unmodified lines
9991786 data.stamp['host'] = m.group('host')
10001787 data.stamp['mode'] = m.group('mode')
10011788 data.stamp['kernel'] = m.group('kernel')
1789+ sysvals.hostname = data.stamp['host']
10021790 sysvals.suspendmode = data.stamp['mode']
10031791 if not sysvals.stamp:
10041792 sysvals.stamp = data.stamp
@@ -1031,14 +1819,35 @@ def diffStamp(stamp1, stamp2): · 26 unmodified lines
10311819 def doesTraceLogHaveTraceEvents():
10321820 global sysvals
10331821
1822+ # check for kprobes
1823+ sysvals.usekprobes = False
1824+ out = os.system('grep -q "_cal: (" '+sysvals.ftracefile)
1825+ if(out == 0):
1826+ sysvals.usekprobes = True
1827+ # check for callgraph data on trace event blocks
1828+ out = os.system('grep -q "_cpu_down()" '+sysvals.ftracefile)
1829+ if(out == 0):
1830+ sysvals.usekprobes = True
1831+ out = os.popen('head -1 '+sysvals.ftracefile).read().replace('\n', '')
1832+ m = re.match(sysvals.stampfmt, out)
1833+ if m and m.group('mode') == 'command':
1834+ sysvals.usetraceeventsonly = True
1835+ sysvals.usetraceevents = True
1836+ return
1837+ # figure out what level of trace events are supported
10341838 sysvals.usetraceeventsonly = True
10351839 sysvals.usetraceevents = False
10361840 for e in sysvals.traceevents:
1037− out = os.popen('cat '+sysvals.ftracefile+' | grep "'+e+': "').read()
1038− if(not out):
1841+ out = os.system('grep -q "'+e+': " '+sysvals.ftracefile)
1842+ if(out != 0):
10391843 sysvals.usetraceeventsonly = False
1040− if(e == 'suspend_resume' and out):
1844+ if(e == 'suspend_resume' and out == 0):
10411845 sysvals.usetraceevents = True
1846+ # determine is this log is properly formatted
1847+ for e in ['SUSPEND START', 'RESUME COMPLETE']:
1848+ out = os.system('grep -q "'+e+'" '+sysvals.ftracefile)
1849+ if(out != 0):
1850+ sysvals.usetracemarkers = False
10421851
10431852 # Function: appendIncompleteTraceLog
10441853 # Description:
@@ -1055,44 +1864,42 @@ def appendIncompleteTraceLog(testruns): · 10 unmodified lines
10551864
10561865 # create TestRun vessels for ftrace parsing
10571866 testcnt = len(testruns)
1058− testidx = -1
1867+ testidx = 0
10591868 testrun = []
10601869 for data in testruns:
10611870 testrun.append(TestRun(data))
10621871
10631872 # extract the callgraph and traceevent data
10641873 vprint('Analyzing the ftrace data...')
1874+ tp = TestProps()
10651875 tf = open(sysvals.ftracefile, 'r')
1876+ data = 0
10661877 for line in tf:
10671878 # remove any latent carriage returns
10681879 line = line.replace('\r\n', '')
1069− # grab the time stamp first (signifies the start of the test run)
1880+ # grab the time stamp
10701881 m = re.match(sysvals.stampfmt, line)
10711882 if(m):
1072− testidx += 1
1073− parseStamp(m, testrun[testidx].data)
1074− continue
1075− # pull out any firmware data
1076− if(re.match(sysvals.firmwarefmt, line)):
1077− continue
1078− # if we havent found a test time stamp yet keep spinning til we do
1079− if(testidx < 0):
1883+ tp.stamp = line
10801884 continue
10811885 # determine the trace data type (required for further parsing)
10821886 m = re.match(sysvals.tracertypefmt, line)
10831887 if(m):
1084− tracer = m.group('t')
1085− testrun[testidx].setTracerType(tracer)
1888+ tp.setTracerType(m.group('t'))
1889+ continue
1890+ # device properties line
1891+ if(re.match(sysvals.devpropfmt, line)):
1892+ devProps(line)
10861893 continue
1087− # parse only valid lines, if this isnt one move on
1088− m = re.match(testrun[testidx].ftrace_line_fmt, line)
1894+ # parse only valid lines, if this is not one move on
1895+ m = re.match(tp.ftrace_line_fmt, line)
10891896 if(not m):
10901897 continue
10911898 # gather the basic message data from the line
10921899 m_time = m.group('time')
10931900 m_pid = m.group('pid')
10941901 m_msg = m.group('msg')
1095− if(testrun[testidx].cgformat):
1902+ if(tp.cgformat):
10961903 m_param3 = m.group('dur')
10971904 else:
10981905 m_param3 = 'traceevent'
@@ -1104,119 +1911,114 @@ def appendIncompleteTraceLog(testruns): · 5 unmodified lines
11041911 # the line should be a call, return, or event
11051912 if(not t.fcall and not t.freturn and not t.fevent):
11061913 continue
1107− # only parse the ftrace data during suspend/resume
1108− data = testrun[testidx].data
1109− if(not testrun[testidx].inthepipe):
1110− # look for the suspend start marker
1111− if(t.fevent):
1112− if(t.name == 'SUSPEND START'):
1113− testrun[testidx].inthepipe = True
1114− data.setStart(t.time)
1914+ # look for the suspend start marker
1915+ if(t.startMarker()):
1916+ data = testrun[testidx].data
1917+ parseStamp(tp.stamp, data)
1918+ data.setStart(t.time)
1919+ continue
1920+ if(not data):
1921+ continue
1922+ # find the end of resume
1923+ if(t.endMarker()):
1924+ data.setEnd(t.time)
1925+ testidx += 1
1926+ if(testidx >= testcnt):
1927+ break
1928+ continue
1929+ # trace event processing
1930+ if(t.fevent):
1931+ # general trace events have two types, begin and end
1932+ if(re.match('(?P<name>.*) begin$', t.name)):
1933+ isbegin = True
1934+ elif(re.match('(?P<name>.*) end$', t.name)):
1935+ isbegin = False
1936+ else:
11151937 continue
1116− else:
1117− # trace event processing
1118− if(t.fevent):
1119− if(t.name == 'RESUME COMPLETE'):
1120− testrun[testidx].inthepipe = False
1121− data.setEnd(t.time)
1122− if(testidx == testcnt - 1):
1123− break
1124− continue
1125− # general trace events have two types, begin and end
1126− if(re.match('(?P<name>.*) begin$', t.name)):
1127− isbegin = True
1128− elif(re.match('(?P<name>.*) end$', t.name)):
1129− isbegin = False
1938+ m = re.match('(?P<name>.*)\[(?P<val>[0-9]*)\] .*', t.name)
1939+ if(m):
1940+ val = m.group('val')
1941+ if val == '0':
1942+ name = m.group('name')
11301943 else:
1131− continue
1132− m = re.match('(?P<name>.*)\[(?P<val>[0-9]*)\] .*', t.name)
1133− if(m):
1134− val = m.group('val')
1135− if val == '0':
1136− name = m.group('name')
1137− else:
1138− name = m.group('name')+'['+val+']'
1944+ name = m.group('name')+'['+val+']'
1945+ else:
1946+ m = re.match('(?P<name>.*) .*', t.name)
1947+ name = m.group('name')
1948+ # special processing for trace events
1949+ if re.match('dpm_prepare\[.*', name):
1950+ continue
1951+ elif re.match('machine_suspend.*', name):
1952+ continue
1953+ elif re.match('suspend_enter\[.*', name):
1954+ if(not isbegin):
1955+ data.dmesg['suspend_prepare']['end'] = t.time
1956+ continue
1957+ elif re.match('dpm_suspend\[.*', name):
1958+ if(not isbegin):
1959+ data.dmesg['suspend']['end'] = t.time
1960+ continue
1961+ elif re.match('dpm_suspend_late\[.*', name):
1962+ if(isbegin):
1963+ data.dmesg['suspend_late']['start'] = t.time
11391964 else:
1140− m = re.match('(?P<name>.*) .*', t.name)
1141− name = m.group('name')
1142− # special processing for trace events
1143− if re.match('dpm_prepare\[.*', name):
1144− continue
1145− elif re.match('machine_suspend.*', name):
1146− continue
1147− elif re.match('suspend_enter\[.*', name):
1148− if(not isbegin):
1149− data.dmesg['suspend_prepare']['end'] = t.time
1150− continue
1151− elif re.match('dpm_suspend\[.*', name):
1152− if(not isbegin):
1153− data.dmesg['suspend']['end'] = t.time
1154− continue
1155− elif re.match('dpm_suspend_late\[.*', name):
1156− if(isbegin):
1157− data.dmesg['suspend_late']['start'] = t.time
1158− else:
1159− data.dmesg['suspend_late']['end'] = t.time
1160− continue
1161− elif re.match('dpm_suspend_noirq\[.*', name):
1162− if(isbegin):
1163− data.dmesg['suspend_noirq']['start'] = t.time
1164− else:
1165− data.dmesg['suspend_noirq']['end'] = t.time
1166− continue
1167− elif re.match('dpm_resume_noirq\[.*', name):
1168− if(isbegin):
1169− data.dmesg['resume_machine']['end'] = t.time
1170− data.dmesg['resume_noirq']['start'] = t.time
1171− else:
1172− data.dmesg['resume_noirq']['end'] = t.time
1173− continue
1174− elif re.match('dpm_resume_early\[.*', name):
1175− if(isbegin):
1176− data.dmesg['resume_early']['start'] = t.time
1177− else:
1178− data.dmesg['resume_early']['end'] = t.time
1179− continue
1180− elif re.match('dpm_resume\[.*', name):
1181− if(isbegin):
1182− data.dmesg['resume']['start'] = t.time
1183− else:
1184− data.dmesg['resume']['end'] = t.time
1185− continue
1186− elif re.match('dpm_complete\[.*', name):
1187− if(isbegin):
1188− data.dmesg['resume_complete']['start'] = t.time
1189− else:
1190− data.dmesg['resume_complete']['end'] = t.time
1191− continue
1192− # is this trace event outside of the devices calls
1193− if(data.isTraceEventOutsideDeviceCalls(pid, t.time)):
1194− # global events (outside device calls) are simply graphed
1195− if(isbegin):
1196− # store each trace event in ttemp
1197− if(name not in testrun[testidx].ttemp):
1198− testrun[testidx].ttemp[name] = []
1199− testrun[testidx].ttemp[name].append(\
1200− {'begin': t.time, 'end': t.time})
1201− else:
1202− # finish off matching trace event in ttemp
1203− if(name in testrun[testidx].ttemp):
1204− testrun[testidx].ttemp[name][-1]['end'] = t.time
1965+ data.dmesg['suspend_late']['end'] = t.time
1966+ continue
1967+ elif re.match('dpm_suspend_noirq\[.*', name):
1968+ if(isbegin):
1969+ data.dmesg['suspend_noirq']['start'] = t.time
12051970 else:
1206− if(isbegin):
1207− data.addIntraDevTraceEvent('', name, pid, t.time)
1208− else:
1209− data.capIntraDevTraceEvent('', name, pid, t.time)
1210− # call/return processing
1211− elif sysvals.usecallgraph:
1212− # create a callgraph object for the data
1213− if(pid not in testrun[testidx].ftemp):
1214− testrun[testidx].ftemp[pid] = []
1215− testrun[testidx].ftemp[pid].append(FTraceCallGraph())
1216− # when the call is finished, see which device matches it
1217− cg = testrun[testidx].ftemp[pid][-1]
1218− if(cg.addLine(t, m)):
1219− testrun[testidx].ftemp[pid].append(FTraceCallGraph())
1971+ data.dmesg['suspend_noirq']['end'] = t.time
1972+ continue
1973+ elif re.match('dpm_resume_noirq\[.*', name):
1974+ if(isbegin):
1975+ data.dmesg['resume_machine']['end'] = t.time
1976+ data.dmesg['resume_noirq']['start'] = t.time
1977+ else:
1978+ data.dmesg['resume_noirq']['end'] = t.time
1979+ continue
1980+ elif re.match('dpm_resume_early\[.*', name):
1981+ if(isbegin):
1982+ data.dmesg['resume_early']['start'] = t.time
1983+ else:
1984+ data.dmesg['resume_early']['end'] = t.time
1985+ continue
1986+ elif re.match('dpm_resume\[.*', name):
1987+ if(isbegin):
1988+ data.dmesg['resume']['start'] = t.time
1989+ else:
1990+ data.dmesg['resume']['end'] = t.time
1991+ continue
1992+ elif re.match('dpm_complete\[.*', name):
1993+ if(isbegin):
1994+ data.dmesg['resume_complete']['start'] = t.time
1995+ else:
1996+ data.dmesg['resume_complete']['end'] = t.time
1997+ continue
1998+ # skip trace events inside devices calls
1999+ if(not data.isTraceEventOutsideDeviceCalls(pid, t.time)):
2000+ continue
2001+ # global events (outside device calls) are simply graphed
2002+ if(isbegin):
2003+ # store each trace event in ttemp
2004+ if(name not in testrun[testidx].ttemp):
2005+ testrun[testidx].ttemp[name] = []
2006+ testrun[testidx].ttemp[name].append(\
2007+ {'begin': t.time, 'end': t.time})
2008+ else:
2009+ # finish off matching trace event in ttemp
2010+ if(name in testrun[testidx].ttemp):
2011+ testrun[testidx].ttemp[name][-1]['end'] = t.time
2012+ # call/return processing
2013+ elif sysvals.usecallgraph:
2014+ # create a callgraph object for the data
2015+ if(pid not in testrun[testidx].ftemp):
2016+ testrun[testidx].ftemp[pid] = []
2017+ testrun[testidx].ftemp[pid].append(FTraceCallGraph(pid))
2018+ # when the call is finished, see which device matches it
2019+ cg = testrun[testidx].ftemp[pid][-1]
2020+ if(cg.addLine(t)):
2021+ testrun[testidx].ftemp[pid].append(FTraceCallGraph(pid))
12202022 tf.close()
12212023
12222024 for test in testrun:
@@ -1224,20 +2026,14 @@ def appendIncompleteTraceLog(testruns): · 1 unmodified line
12242026 if(sysvals.usetraceevents):
12252027 for name in test.ttemp:
12262028 for event in test.ttemp[name]:
1227− begin = event['begin']
1228− end = event['end']
1229− # if event starts before timeline start, expand timeline
1230− if(begin < test.data.start):
1231− test.data.setStart(begin)
1232− # if event ends after timeline end, expand the timeline
1233− if(end > test.data.end):
1234− test.data.setEnd(end)
1235− test.data.newActionGlobal(name, begin, end)
2029+ test.data.newActionGlobal(name, event['begin'], event['end'])
12362030
12372031 # add the callgraph data to the device hierarchy
12382032 for pid in test.ftemp:
12392033 for cg in test.ftemp[pid]:
1240− if(not cg.sanityCheck()):
2034+ if len(cg.list) < 1 or cg.invalid:
2035+ continue
2036+ if(not cg.postProcess()):
12412037 id = 'task %s cpu %s' % (pid, m.group('cpu'))
12422038 vprint('Sanity check failed for '+\
12432039 id+', ignoring this callback')
@@ -1259,14 +2055,6 @@ def appendIncompleteTraceLog(testruns): · 15 unmodified lines
12592055 if(sysvals.verbose):
12602056 test.data.printDetails()
12612057
1262−
1263− # add the time in between the tests as a new phase so we can see it
1264− if(len(testruns) > 1):
1265− t1e = testruns[0].getEnd()
1266− t2s = testruns[-1].getStart()
1267− testruns[-1].newPhaseWithSingleAction('user mode', \
1268− 'user mode', t1e, t2s, '#FF9966')
1269−
12702058 # Function: parseTraceLog
12712059 # Description:
12722060 # Analyze an ftrace log output file generated from this app during
@@ -1280,9 +2068,16 @@ def parseTraceLog(): · 7 unmodified lines
12802068
12812069 vprint('Analyzing the ftrace data...')
12822070 if(os.path.exists(sysvals.ftracefile) == False):
1283− doError('%s doesnt exist' % sysvals.ftracefile, False)
2071+ doError('%s does not exist' % sysvals.ftracefile, False)
2072+
2073+ sysvals.setupAllKprobes()
2074+ tracewatch = ['suspend_enter']
2075+ if sysvals.usekprobes:
2076+ tracewatch += ['sync_filesystems', 'freeze_processes', 'syscore_suspend',
2077+ 'syscore_resume', 'resume_console', 'thaw_processes', 'CPU_ON', 'CPU_OFF']
12842078
12852079 # extract the callgraph and traceevent data
2080+ tp = TestProps()
12862081 testruns = []
12872082 testdata = []
12882083 testrun = 0
@@ -1295,27 +2090,17 @@ def parseTraceLog(): · 6 unmodified lines
12952090 # stamp line: each stamp means a new test run
12962091 m = re.match(sysvals.stampfmt, line)
12972092 if(m):
1298− data = Data(len(testdata))
1299− testdata.append(data)
1300− testrun = TestRun(data)
1301− testruns.append(testrun)
1302− parseStamp(m, data)
1303− continue
1304− if(not data):
2093+ tp.stamp = line
13052094 continue
13062095 # firmware line: pull out any firmware data
13072096 m = re.match(sysvals.firmwarefmt, line)
13082097 if(m):
1309− data.fwSuspend = int(m.group('s'))
1310− data.fwResume = int(m.group('r'))
1311− if(data.fwSuspend > 0 or data.fwResume > 0):
1312− data.fwValid = True
2098+ tp.fwdata.append((int(m.group('s')), int(m.group('r'))))
13132099 continue
13142100 # tracer type line: determine the trace data type
13152101 m = re.match(sysvals.tracertypefmt, line)
13162102 if(m):
1317− tracer = m.group('t')
1318− testrun.setTracerType(tracer)
2103+ tp.setTracerType(m.group('t'))
13192104 continue
13202105 # post resume time line: did this test run include post-resume data
13212106 m = re.match(sysvals.postresumefmt, line)
@@ -1324,15 +2109,20 @@ def parseTraceLog(): · 2 unmodified lines
13242109 if(t > 0):
13252110 sysvals.postresumetime = t
13262111 continue
2112+ # device properties line
2113+ if(re.match(sysvals.devpropfmt, line)):
2114+ devProps(line)
2115+ continue
13272116 # ftrace line: parse only valid lines
1328− m = re.match(testrun.ftrace_line_fmt, line)
2117+ m = re.match(tp.ftrace_line_fmt, line)
13292118 if(not m):
13302119 continue
13312120 # gather the basic message data from the line
13322121 m_time = m.group('time')
2122+ m_proc = m.group('proc')
13332123 m_pid = m.group('pid')
13342124 m_msg = m.group('msg')
1335− if(testrun.cgformat):
2125+ if(tp.cgformat):
13362126 m_param3 = m.group('dur')
13372127 else:
13382128 m_param3 = 'traceevent'
@@ -1344,24 +2134,38 @@ def parseTraceLog(): · 5 unmodified lines
13442134 # the line should be a call, return, or event
13452135 if(not t.fcall and not t.freturn and not t.fevent):
13462136 continue
1347− # only parse the ftrace data during suspend/resume
1348− if(not testrun.inthepipe):
1349− # look for the suspend start marker
1350− if(t.fevent):
1351− if(t.name == 'SUSPEND START'):
1352− testrun.inthepipe = True
1353− data.setStart(t.time)
2137+ # find the start of suspend
2138+ if(t.startMarker()):
2139+ phase = 'suspend_prepare'
2140+ data = Data(len(testdata))
2141+ testdata.append(data)
2142+ testrun = TestRun(data)
2143+ testruns.append(testrun)
2144+ parseStamp(tp.stamp, data)
2145+ if len(tp.fwdata) > data.testnumber:
2146+ data.fwSuspend, data.fwResume = tp.fwdata[data.testnumber]
2147+ if(data.fwSuspend > 0 or data.fwResume > 0):
2148+ data.fwValid = True
2149+ data.setStart(t.time)
2150+ continue
2151+ if(not data):
2152+ continue
2153+ # find the end of resume
2154+ if(t.endMarker()):
2155+ if(sysvals.usetracemarkers and sysvals.postresumetime > 0):
2156+ phase = 'post_resume'
2157+ data.newPhase(phase, t.time, t.time, '#F0F0F0', -1)
2158+ data.setEnd(t.time)
2159+ if(not sysvals.usetracemarkers):
2160+ # no trace markers? then quit and be sure to finish recording
2161+ # the event we used to trigger resume end
2162+ if(len(testrun.ttemp['thaw_processes']) > 0):
2163+ # if an entry exists, assume this is its end
2164+ testrun.ttemp['thaw_processes'][-1]['end'] = t.time
2165+ break
13542166 continue
13552167 # trace event processing
13562168 if(t.fevent):
1357− if(t.name == 'RESUME COMPLETE'):
1358− if(sysvals.postresumetime > 0):
1359− phase = 'post_resume'
1360− data.newPhase(phase, t.time, t.time, '#FF9966', -1)
1361− else:
1362− testrun.inthepipe = False
1363− data.setEnd(t.time)
1364− continue
13652169 if(phase == 'post_resume'):
13662170 data.setEnd(t.time)
13672171 if(t.type == 'suspend_resume'):
@@ -1383,8 +2187,7 @@ def parseTraceLog(): · 15 unmodified lines
13832187 m = re.match('(?P<name>.*) .*', t.name)
13842188 name = m.group('name')
13852189 # ignore these events
1386− if(re.match('acpi_suspend\[.*', t.name) or
1387− re.match('suspend_enter\[.*', name)):
2190+ if(name.split('[')[0] in tracewatch):
13882191 continue
13892192 # -- phase changes --
13902193 # suspend_prepare start
@@ -1418,7 +2221,7 @@ def parseTraceLog(): · 27 unmodified lines
14182221 data.dmesg[phase]['end'] = t.time
14192222 data.tSuspended = t.time
14202223 else:
1421− if(sysvals.suspendmode in ['mem', 'disk']):
2224+ if(sysvals.suspendmode in ['mem', 'disk'] and not tp.S0i3):
14222225 data.dmesg['suspend_machine']['end'] = t.time
14232226 data.tSuspended = t.time
14242227 phase = 'resume_machine'
@@ -1426,6 +2229,15 @@ def parseTraceLog(): · 1 unmodified line
14262229 data.tResumed = t.time
14272230 data.tLow = data.tResumed - data.tSuspended
14282231 continue
2232+ # acpi_suspend
2233+ elif(re.match('acpi_suspend\[.*', t.name)):
2234+ # acpi_suspend[0] S0i3
2235+ if(re.match('acpi_suspend\[0\] begin', t.name)):
2236+ if(sysvals.suspendmode == 'mem'):
2237+ tp.S0i3 = True
2238+ data.dmesg['suspend_machine']['end'] = t.time
2239+ data.tSuspended = t.time
2240+ continue
14292241 # resume_noirq start
14302242 elif(re.match('dpm_resume_noirq\[.*', t.name)):
14312243 phase = 'resume_noirq'
@@ -1449,30 +2261,25 @@ def parseTraceLog(): · 17 unmodified lines
14492261 if(isbegin):
14502262 data.dmesg[phase]['start'] = t.time
14512263 continue
1452−
1453− # is this trace event outside of the devices calls
1454− if(data.isTraceEventOutsideDeviceCalls(pid, t.time)):
1455− # global events (outside device calls) are simply graphed
1456− if(name not in testrun.ttemp):
1457− testrun.ttemp[name] = []
1458− if(isbegin):
1459− # create a new list entry
1460− testrun.ttemp[name].append(\
1461− {'begin': t.time, 'end': t.time})
1462− else:
1463− if(len(testrun.ttemp[name]) > 0):
1464− # if an antry exists, assume this is its end
1465− testrun.ttemp[name][-1]['end'] = t.time
1466− elif(phase == 'post_resume'):
1467− # post resume events can just have ends
1468− testrun.ttemp[name].append({
1469− 'begin': data.dmesg[phase]['start'],
1470− 'end': t.time})
2264+ # skip trace events inside devices calls
2265+ if(not data.isTraceEventOutsideDeviceCalls(pid, t.time)):
2266+ continue
2267+ # global events (outside device calls) are graphed
2268+ if(name not in testrun.ttemp):
2269+ testrun.ttemp[name] = []
2270+ if(isbegin):
2271+ # create a new list entry
2272+ testrun.ttemp[name].append(\
2273+ {'begin': t.time, 'end': t.time, 'pid': pid})
14712274 else:
1472− if(isbegin):
1473− data.addIntraDevTraceEvent('', name, pid, t.time)
1474− else:
1475− data.capIntraDevTraceEvent('', name, pid, t.time)
2275+ if(len(testrun.ttemp[name]) > 0):
2276+ # if an entry exists, assume this is its end
2277+ testrun.ttemp[name][-1]['end'] = t.time
2278+ elif(phase == 'post_resume'):
2279+ # post resume events can just have ends
2280+ testrun.ttemp[name].append({
2281+ 'begin': data.dmesg[phase]['start'],
2282+ 'end': t.time})
14762283 # device callback start
14772284 elif(t.type == 'device_pm_callback_start'):
14782285 m = re.match('(?P<drv>.*) (?P<d>.*), parent: *(?P<p>.*), .*',\
@@ -1495,75 +2302,127 @@ def parseTraceLog(): · 16 unmodified lines
14952302 dev = list[n]
14962303 dev['length'] = t.time - dev['start']
14972304 dev['end'] = t.time
2305+ # kprobe event processing
2306+ elif(t.fkprobe):
2307+ kprobename = t.type
2308+ kprobedata = t.name
2309+ key = (kprobename, pid)
2310+ # displayname is generated from kprobe data
2311+ displayname = ''
2312+ if(t.fcall):
2313+ displayname = sysvals.kprobeDisplayName(kprobename, kprobedata)
2314+ if not displayname:
2315+ continue
2316+ if(key not in tp.ktemp):
2317+ tp.ktemp[key] = []
2318+ tp.ktemp[key].append({
2319+ 'pid': pid,
2320+ 'begin': t.time,
2321+ 'end': t.time,
2322+ 'name': displayname,
2323+ 'cdata': kprobedata,
2324+ 'proc': m_proc,
2325+ })
2326+ elif(t.freturn):
2327+ if(key not in tp.ktemp) or len(tp.ktemp[key]) < 1:
2328+ continue
2329+ e = tp.ktemp[key][-1]
2330+ if e['begin'] < 0.0 or t.time - e['begin'] < 0.000001:
2331+ tp.ktemp[key].pop()
2332+ else:
2333+ e['end'] = t.time
2334+ e['rdata'] = kprobedata
14982335 # callgraph processing
14992336 elif sysvals.usecallgraph:
1500− # this shouldn't happen, but JIC, ignore callgraph data post-res
1501− if(phase == 'post_resume'):
1502− continue
15032337 # create a callgraph object for the data
1504− if(pid not in testrun.ftemp):
1505− testrun.ftemp[pid] = []
1506− testrun.ftemp[pid].append(FTraceCallGraph())
2338+ key = (m_proc, pid)
2339+ if(key not in testrun.ftemp):
2340+ testrun.ftemp[key] = []
2341+ testrun.ftemp[key].append(FTraceCallGraph(pid))
15072342 # when the call is finished, see which device matches it
1508− cg = testrun.ftemp[pid][-1]
1509− if(cg.addLine(t, m)):
1510− testrun.ftemp[pid].append(FTraceCallGraph())
2343+ cg = testrun.ftemp[key][-1]
2344+ if(cg.addLine(t)):
2345+ testrun.ftemp[key].append(FTraceCallGraph(pid))
15112346 tf.close()
15122347
2348+ if sysvals.suspendmode == 'command':
2349+ for test in testruns:
2350+ for p in test.data.phases:
2351+ if p == 'resume_complete':
2352+ test.data.dmesg[p]['start'] = test.data.start
2353+ test.data.dmesg[p]['end'] = test.data.end
2354+ else:
2355+ test.data.dmesg[p]['start'] = test.data.start
2356+ test.data.dmesg[p]['end'] = test.data.start
2357+ test.data.tSuspended = test.data.start
2358+ test.data.tResumed = test.data.start
2359+ test.data.tLow = 0
2360+ test.data.fwValid = False
2361+
15132362 for test in testruns:
15142363 # add the traceevent data to the device hierarchy
15152364 if(sysvals.usetraceevents):
2365+ # add actual trace funcs
15162366 for name in test.ttemp:
15172367 for event in test.ttemp[name]:
1518− begin = event['begin']
1519− end = event['end']
1520− # if event starts before timeline start, expand timeline
1521− if(begin < test.data.start):
1522− test.data.setStart(begin)
1523− # if event ends after timeline end, expand the timeline
1524− if(end > test.data.end):
1525− test.data.setEnd(end)
1526− test.data.newActionGlobal(name, begin, end)
1527−
1528− # add the callgraph data to the device hierarchy
1529− borderphase = {
1530− 'dpm_prepare': 'suspend_prepare',
1531− 'dpm_complete': 'resume_complete'
1532− }
1533− for pid in test.ftemp:
1534− for cg in test.ftemp[pid]:
1535− if len(cg.list) < 2:
2368+ test.data.newActionGlobal(name, event['begin'], event['end'], event['pid'])
2369+ # add the kprobe based virtual tracefuncs as actual devices
2370+ for key in tp.ktemp:
2371+ name, pid = key
2372+ if name not in sysvals.tracefuncs:
15362373 continue
1537− if(not cg.sanityCheck()):
1538− id = 'task %s cpu %s' % (pid, m.group('cpu'))
1539− vprint('Sanity check failed for '+\
1540− id+', ignoring this callback')
1541− continue
1542− callstart = cg.start
1543− callend = cg.end
1544− if(cg.list[0].name in borderphase):
1545− p = borderphase[cg.list[0].name]
1546− list = test.data.dmesg[p]['list']
1547− for devname in list:
1548− dev = list[devname]
1549− if(pid == dev['pid'] and
1550− callstart <= dev['start'] and
1551− callend >= dev['end']):
1552− dev['ftrace'] = cg.slice(dev['start'], dev['end'])
1553− continue
1554− if(cg.list[0].name != 'dpm_run_callback'):
2374+ for e in tp.ktemp[key]:
2375+ kb, ke = e['begin'], e['end']
2376+ if kb == ke or not test.data.isInsideTimeline(kb, ke):
2377+ continue
2378+ test.data.newActionGlobal(e['name'], kb, ke, pid)
2379+ # add config base kprobes and dev kprobes
2380+ for key in tp.ktemp:
2381+ name, pid = key
2382+ if name in sysvals.tracefuncs:
15552383 continue
1556− for p in test.data.phases:
1557− if(test.data.dmesg[p]['start'] <= callstart and
1558− callstart <= test.data.dmesg[p]['end']):
1559− list = test.data.dmesg[p]['list']
1560− for devname in list:
1561− dev = list[devname]
1562− if(pid == dev['pid'] and
1563− callstart <= dev['start'] and
1564− callend >= dev['end']):
1565− dev['ftrace'] = cg
1566− break
2384+ for e in tp.ktemp[key]:
2385+ kb, ke = e['begin'], e['end']
2386+ if kb == ke or not test.data.isInsideTimeline(kb, ke):
2387+ continue
2388+ color = sysvals.kprobeColor(e['name'])
2389+ if name not in sysvals.dev_tracefuncs:
2390+ # config base kprobe
2391+ test.data.newActionGlobal(e['name'], kb, ke, -2, color)
2392+ elif sysvals.usedevsrc:
2393+ # dev kprobe
2394+ data.addDeviceFunctionCall(e['name'], name, e['proc'], pid, kb,
2395+ ke, e['cdata'], e['rdata'])
2396+ if sysvals.usecallgraph:
2397+ # add the callgraph data to the device hierarchy
2398+ sortlist = dict()
2399+ for key in test.ftemp:
2400+ proc, pid = key
2401+ for cg in test.ftemp[key]:
2402+ if len(cg.list) < 1 or cg.invalid:
2403+ continue
2404+ if(not cg.postProcess()):
2405+ id = 'task %s' % (pid)
2406+ vprint('Sanity check failed for '+\
2407+ id+', ignoring this callback')
2408+ continue
2409+ # match cg data to devices
2410+ if sysvals.suspendmode == 'command' or not cg.deviceMatch(pid, test.data):
2411+ sortkey = '%f%f%d' % (cg.start, cg.end, pid)
2412+ sortlist[sortkey] = cg
2413+ # create blocks for orphan cg data
2414+ for sortkey in sorted(sortlist):
2415+ cg = sortlist[sortkey]
2416+ name = cg.list[0].name
2417+ if sysvals.isCallgraphFunc(name):
2418+ vprint('Callgraph found for task %d: %.3fms, %s' % (cg.pid, (cg.end - cg.start)*1000, name))
2419+ cg.newActionFromFunction(test.data)
2420+
2421+ if sysvals.suspendmode == 'command':
2422+ if(sysvals.verbose):
2423+ for data in testdata:
2424+ data.printDetails()
2425+ return testdata
15672426
15682427 # fill in any missing phases
15692428 for data in testdata:
@@ -1587,14 +2446,52 @@ def parseTraceLog(): · 17 unmodified lines
15872446 if(sysvals.verbose):
15882447 data.printDetails()
15892448
1590− # add the time in between the tests as a new phase so we can see it
1591− if(len(testdata) > 1):
1592− t1e = testdata[0].getEnd()
1593− t2s = testdata[-1].getStart()
1594− testdata[-1].newPhaseWithSingleAction('user mode', \
1595− 'user mode', t1e, t2s, '#FF9966')
15962449 return testdata
15972450
2451+# Function: loadRawKernelLog
2452+# Description:
2453+# Load a raw kernel log that wasn't created by this tool, it might be
2454+# possible to extract a valid suspend/resume log
2455+def loadRawKernelLog(dmesgfile):
2456+ global sysvals
2457+
2458+ stamp = {'time': '', 'host': '', 'mode': 'mem', 'kernel': ''}
2459+ stamp['time'] = datetime.now().strftime('%B %d %Y, %I:%M:%S %p')
2460+ stamp['host'] = sysvals.hostname
2461+
2462+ testruns = []
2463+ data = 0
2464+ lf = open(dmesgfile, 'r')
2465+ for line in lf:
2466+ line = line.replace('\r\n', '')
2467+ idx = line.find('[')
2468+ if idx > 1:
2469+ line = line[idx:]
2470+ m = re.match('[ \t]*(\[ *)(?P<ktime>[0-9\.]*)(\]) (?P<msg>.*)', line)
2471+ if(not m):
2472+ continue
2473+ msg = m.group("msg")
2474+ m = re.match('PM: Syncing filesystems.*', msg)
2475+ if(m):
2476+ if(data):
2477+ testruns.append(data)
2478+ data = Data(len(testruns))
2479+ data.stamp = stamp
2480+ if(data):
2481+ m = re.match('.* *(?P<k>[0-9]\.[0-9]{2}\.[0-9]-.*) .*', msg)
2482+ if(m):
2483+ stamp['kernel'] = m.group('k')
2484+ m = re.match('PM: Preparing system for (?P<m>.*) sleep', msg)
2485+ if(m):
2486+ stamp['mode'] = m.group('m')
2487+ data.dmesgtext.append(line)
2488+ if(data):
2489+ testruns.append(data)
2490+ sysvals.stamp = stamp
2491+ sysvals.suspendmode = stamp['mode']
2492+ lf.close()
2493+ return testruns
2494+
15982495 # Function: loadKernelLog
15992496 # Description:
16002497 # [deprecated for kernel 3.15.0 or newer]
@@ -1607,9 +2504,10 @@ def loadKernelLog(): · 6 unmodified lines
16072504
16082505 vprint('Analyzing the dmesg data...')
16092506 if(os.path.exists(sysvals.dmesgfile) == False):
1610− doError('%s doesnt exist' % sysvals.dmesgfile, False)
2507+ doError('%s does not exist' % sysvals.dmesgfile, False)
16112508
1612− # there can be multiple test runs in a single file delineated by stamps
2509+ # there can be multiple test runs in a single file
2510+ tp = TestProps()
16132511 testruns = []
16142512 data = 0
16152513 lf = open(sysvals.dmesgfile, 'r')
@@ -1620,35 +2518,43 @@ def loadKernelLog(): · 4 unmodified lines
16202518 line = line[idx:]
16212519 m = re.match(sysvals.stampfmt, line)
16222520 if(m):
1623− if(data):
1624− testruns.append(data)
1625− data = Data(len(testruns))
1626− parseStamp(m, data)
1627− continue
1628− if(not data):
2521+ tp.stamp = line
16292522 continue
16302523 m = re.match(sysvals.firmwarefmt, line)
16312524 if(m):
1632− data.fwSuspend = int(m.group('s'))
1633− data.fwResume = int(m.group('r'))
1634− if(data.fwSuspend > 0 or data.fwResume > 0):
1635− data.fwValid = True
2525+ tp.fwdata.append((int(m.group('s')), int(m.group('r'))))
16362526 continue
16372527 m = re.match('[ \t]*(\[ *)(?P<ktime>[0-9\.]*)(\]) (?P<msg>.*)', line)
1638− if(m):
1639− data.dmesgtext.append(line)
1640− if(re.match('ACPI: resume from mwait', m.group('msg'))):
1641− print('NOTE: This suspend appears to be freeze rather than'+\
1642− ' %s, it will be treated as such' % sysvals.suspendmode)
1643− sysvals.suspendmode = 'freeze'
1644− else:
1645− vprint('ignoring dmesg line: %s' % line.replace('\n', ''))
1646− testruns.append(data)
2528+ if(not m):
2529+ continue
2530+ msg = m.group("msg")
2531+ if(re.match('PM: Syncing filesystems.*', msg)):
2532+ if(data):
2533+ testruns.append(data)
2534+ data = Data(len(testruns))
2535+ parseStamp(tp.stamp, data)
2536+ if len(tp.fwdata) > data.testnumber:
2537+ data.fwSuspend, data.fwResume = tp.fwdata[data.testnumber]
2538+ if(data.fwSuspend > 0 or data.fwResume > 0):
2539+ data.fwValid = True
2540+ if(re.match('ACPI: resume from mwait', msg)):
2541+ print('NOTE: This suspend appears to be freeze rather than'+\
2542+ ' %s, it will be treated as such' % sysvals.suspendmode)
2543+ sysvals.suspendmode = 'freeze'
2544+ if(not data):
2545+ continue
2546+ data.dmesgtext.append(line)
2547+ if(data):
2548+ testruns.append(data)
16472549 lf.close()
16482550
1649− if(not data):
1650− print('ERROR: analyze_suspend header missing from dmesg log')
1651− sys.exit()
2551+ if(len(testruns) < 1):
2552+ # bad log, but see if you can extract something meaningful anyway
2553+ testruns = loadRawKernelLog(sysvals.dmesgfile)
2554+
2555+ if(len(testruns) < 1):
2556+ doError(' dmesg log is completely unreadable: %s' \
2557+ % sysvals.dmesgfile, False)
16522558
16532559 # fix lines with same timestamp/function with the call and return swapped
16542560 for data in testruns:
@@ -1865,7 +2771,8 @@ def parseKernelLog(data): · 210 unmodified lines
18652771 actions[a] = []
18662772 actions[a].append({'begin': ktime, 'end': ktime})
18672773 if(re.match(at[a]['emsg'], msg)):
1868− actions[a][-1]['end'] = ktime
2774+ if(a in actions):
2775+ actions[a][-1]['end'] = ktime
18692776 # now look for CPU on/off events
18702777 if(re.match('Disabling non-boot CPUs .*', msg)):
18712778 # start of first cpu suspend
@@ -1912,15 +2819,7 @@ def parseKernelLog(data): · 40 unmodified lines
19122819 # fill in any actions we've found
19132820 for name in actions:
19142821 for event in actions[name]:
1915− begin = event['begin']
1916− end = event['end']
1917− # if event starts before timeline start, expand timeline
1918− if(begin < data.start):
1919− data.setStart(begin)
1920− # if event ends after timeline end, expand the timeline
1921− if(end > data.end):
1922− data.setEnd(end)
1923− data.newActionGlobal(name, begin, end)
2822+ data.newActionGlobal(name, event['begin'], event['end'])
19242823
19252824 if(sysvals.verbose):
19262825 data.printDetails()
@@ -1929,92 +2828,6 @@ def parseKernelLog(data): · 2 unmodified lines
19292828 data.fixupInitcallsThatDidntReturn()
19302829 return True
19312830
1932−# Function: setTimelineRows
1933−# Description:
1934−# Organize the timeline entries into the smallest
1935−# number of rows possible, with no entry overlapping
1936−# Arguments:
1937−# list: the list of devices/actions for a single phase
1938−# sortedkeys: cronologically sorted key list to use
1939−# Output:
1940−# The total number of rows needed to display this phase of the timeline
1941−def setTimelineRows(list, sortedkeys):
1942−
1943− # clear all rows and set them to undefined
1944− remaining = len(list)
1945− rowdata = dict()
1946− row = 0
1947− for item in list:
1948− list[item]['row'] = -1
1949−
1950− # try to pack each row with as many ranges as possible
1951− while(remaining > 0):
1952− if(row not in rowdata):
1953− rowdata[row] = []
1954− for item in sortedkeys:
1955− if(list[item]['row'] < 0):
1956− s = list[item]['start']
1957− e = list[item]['end']
1958− valid = True
1959− for ritem in rowdata[row]:
1960− rs = ritem['start']
1961− re = ritem['end']
1962− if(not (((s <= rs) and (e <= rs)) or
1963− ((s >= re) and (e >= re)))):
1964− valid = False
1965− break
1966− if(valid):
1967− rowdata[row].append(list[item])
1968− list[item]['row'] = row
1969− remaining -= 1
1970− row += 1
1971− return row
1972−
1973−# Function: createTimeScale
1974−# Description:
1975−# Create the timescale header for the html timeline
1976−# Arguments:
1977−# t0: start time (suspend begin)
1978−# tMax: end time (resume end)
1979−# tSuspend: time when suspend occurs, i.e. the zero time
1980−# Output:
1981−# The html code needed to display the time scale
1982−def createTimeScale(t0, tMax, tSuspended):
1983− timescale = '<div class="t" style="right:{0}%">{1}</div>\n'
1984− output = '<div id="timescale">\n'
1985−
1986− # set scale for timeline
1987− tTotal = tMax - t0
1988− tS = 0.1
1989− if(tTotal <= 0):
1990− return output
1991− if(tTotal > 4):
1992− tS = 1
1993− if(tSuspended < 0):
1994− for i in range(int(tTotal/tS)+1):
1995− pos = '%0.3f' % (100 - ((float(i)*tS*100)/tTotal))
1996− if(i > 0):
1997− val = '%0.fms' % (float(i)*tS*1000)
1998− else:
1999− val = ''
2000− output += timescale.format(pos, val)
2001− else:
2002− tSuspend = tSuspended - t0
2003− divTotal = int(tTotal/tS) + 1
2004− divSuspend = int(tSuspend/tS)
2005− s0 = (tSuspend - tS*divSuspend)*100/tTotal
2006− for i in range(divTotal):
2007− pos = '%0.3f' % (100 - ((float(i)*tS*100)/tTotal) - s0)
2008− if((i == 0) and (s0 < 3)):
2009− val = ''
2010− elif(i == divSuspend):
2011− val = 'S/R'
2012− else:
2013− val = '%0.fms' % (float(i-divSuspend)*tS*1000)
2014− output += timescale.format(pos, val)
2015− output += '</div>\n'
2016− return output
2017−
20182831 # Function: createHTMLSummarySimple
20192832 # Description:
20202833 # Create summary html file for a series of tests
@@ -2146,6 +2959,32 @@ def createHTMLSummarySimple(testruns, htmlfile): · 125 unmodified lines
21462959 hf.write('</body>\n</html>\n')
21472960 hf.close()
21482961
2962+def htmlTitle():
2963+ global sysvals
2964+ modename = {
2965+ 'freeze': 'Freeze (S0)',
2966+ 'standby': 'Standby (S1)',
2967+ 'mem': 'Suspend (S3)',
2968+ 'disk': 'Hibernate (S4)'
2969+ }
2970+ kernel = sysvals.stamp['kernel']
2971+ host = sysvals.hostname[0].upper()+sysvals.hostname[1:]
2972+ mode = sysvals.suspendmode
2973+ if sysvals.suspendmode in modename:
2974+ mode = modename[sysvals.suspendmode]
2975+ return host+' '+mode+' '+kernel
2976+
2977+def ordinal(value):
2978+ suffix = 'th'
2979+ if value < 10 or value > 19:
2980+ if value % 10 == 1:
2981+ suffix = 'st'
2982+ elif value % 10 == 2:
2983+ suffix = 'nd'
2984+ elif value % 10 == 3:
2985+ suffix = 'rd'
2986+ return '%d%s' % (value, suffix)
2987+
21492988 # Function: createHTML
21502989 # Description:
21512990 # Create the output html file from the resident test data
@@ -2156,6 +2995,10 @@ def createHTMLSummarySimple(testruns, htmlfile): · 4 unmodified lines
21562995 def createHTML(testruns):
21572996 global sysvals
21582997
2998+ if len(testruns) < 1:
2999+ print('ERROR: Not enough test data to build a timeline')
3000+ return
3001+
21593002 for data in testruns:
21603003 data.normalizeTime(testruns[-1].tSuspended)
21613004
@@ -2163,16 +3006,18 @@ def createHTML(testruns): · 1 unmodified line
21633006 if len(testruns) > 1:
21643007 x2changes = ['1', 'relative']
21653008 # html function templates
3009+ headline_version = '<div class="version"><a href="https://01.org/suspendresume">AnalyzeSuspend v%s</a></div>' % sysvals.version
21663010 headline_stamp = '<div class="stamp">{0} {1} {2} {3}</div>\n'
21673011 html_devlist1 = '<button id="devlist1" class="devlist" style="float:left;">Device Detail%s</button>' % x2changes[0]
21683012 html_zoombox = '<center><button id="zoomin">ZOOM IN</button><button id="zoomout">ZOOM OUT</button><button id="zoomdef">ZOOM 1:1</button></center>\n'
21693013 html_devlist2 = '<button id="devlist2" class="devlist" style="float:right;">Device Detail2</button>\n'
21703014 html_timeline = '<div id="dmesgzoombox" class="zoombox">\n<div id="{0}" class="timeline" style="height:{1}px">\n'
2171− html_device = '<div id="{0}" title="{1}" class="thread" style="left:{2}%;top:{3}%;height:{4}%;width:{5}%;">{6}</div>\n'
2172− html_traceevent = '<div title="{0}" class="traceevent" style="left:{1}%;top:{2}%;height:{3}%;width:{4}%;border:1px solid {5};background-color:{5}">{6}</div>\n'
2173− html_phase = '<div class="phase" style="left:{0}%;width:{1}%;top:{2}%;height:{3}%;background-color:{4}">{5}</div>\n'
3015+ html_tblock = '<div id="block{0}" class="tblock" style="left:{1}%;width:{2}%;">\n'
3016+ html_device = '<div id="{0}" title="{1}" class="thread{7}" style="left:{2}%;top:{3}px;height:{4}px;width:{5}%;{8}">{6}</div>\n'
3017+ html_traceevent = '<div title="{0}" class="traceevent" style="left:{1}%;top:{2}px;height:{3}px;width:{4}%;line-height:{3}px;">{5}</div>\n'
3018+ html_phase = '<div class="phase" style="left:{0}%;width:{1}%;top:{2}px;height:{3}px;background-color:{4}">{5}</div>\n'
21743019 html_phaselet = '<div id="{0}" class="phaselet" style="left:{1}%;width:{2}%;background-color:{3}"></div>\n'
2175− html_legend = '<div class="square" style="left:{0}%;background-color:{1}">&nbsp;{2}</div>\n'
3020+ html_legend = '<div id="p{3}" class="square" style="left:{0}%;background-color:{1}">&nbsp;{2}</div>\n'
21763021 html_timetotal = '<table class="time1">\n<tr>'\
21773022 '<td class="green">{2} Suspend Time: <b>{0} ms</b></td>'\
21783023 '<td class="yellow">{2} Resume Time: <b>{1} ms</b></td>'\
@@ -2182,6 +3027,10 @@ def createHTML(testruns): · 3 unmodified lines
21823027 '<td class="gray">'+sysvals.suspendmode+' time: <b>{1} ms</b></td>'\
21833028 '<td class="yellow">{3} Resume Time: <b>{2} ms</b></td>'\
21843029 '</tr>\n</table>\n'
3030+ html_timetotal3 = '<table class="time1">\n<tr>'\
3031+ '<td class="green">Execution Time: <b>{0} ms</b></td>'\
3032+ '<td class="yellow">Command: <b>{1}</b></td>'\
3033+ '</tr>\n</table>\n'
21853034 html_timegroups = '<table class="time2">\n<tr>'\
21863035 '<td class="green">{4}Kernel Suspend: {0} ms</td>'\
21873036 '<td class="purple">{4}Firmware Suspend: {1} ms</td>'\
@@ -2189,12 +3038,21 @@ def createHTML(testruns): · 1 unmodified line
21893038 '<td class="yellow">{4}Kernel Resume: {3} ms</td>'\
21903039 '</tr>\n</table>\n'
21913040
3041+ # html format variables
3042+ rowheight = 30
3043+ devtextS = '14px'
3044+ devtextH = '30px'
3045+ hoverZ = 'z-index:10;'
3046+
3047+ if sysvals.usedevsrc:
3048+ hoverZ = ''
3049+
21923050 # device timeline
21933051 vprint('Creating Device Timeline...')
2194− devtl = Timeline()
3052+
3053+ devtl = Timeline(rowheight)
21953054
21963055 # Generate the header for this timeline
2197− textnum = ['First', 'Second']
21983056 for data in testruns:
21993057 tTotal = data.end - data.start
22003058 tEnd = data.dmesg['resume_complete']['end']
@@ -2203,7 +3061,17 @@ def createHTML(testruns): · 2 unmodified lines
22033061 sys.exit()
22043062 if(data.tLow > 0):
22053063 low_time = '%.0f'%(data.tLow*1000)
2206− if data.fwValid:
3064+ if sysvals.suspendmode == 'command':
3065+ run_time = '%.0f'%((data.end-data.start)*1000)
3066+ if sysvals.testcommand:
3067+ testdesc = sysvals.testcommand
3068+ else:
3069+ testdesc = 'unknown'
3070+ if(len(testruns) > 1):
3071+ testdesc = ordinal(data.testnumber+1)+' '+testdesc
3072+ thtml = html_timetotal3.format(run_time, testdesc)
3073+ devtl.html['header'] += thtml
3074+ elif data.fwValid:
22073075 suspend_time = '%.0f'%((data.tSuspended-data.start)*1000 + \
22083076 (data.fwSuspend/1000000.0))
22093077 resume_time = '%.0f'%((tEnd-data.tSuspended)*1000 + \
@@ -2211,7 +3079,7 @@ def createHTML(testruns): · 1 unmodified line
22113079 testdesc1 = 'Total'
22123080 testdesc2 = ''
22133081 if(len(testruns) > 1):
2214− testdesc1 = testdesc2 = textnum[data.testnumber]
3082+ testdesc1 = testdesc2 = ordinal(data.testnumber+1)
22153083 testdesc2 += ' '
22163084 if(data.tLow == 0):
22173085 thtml = html_timetotal.format(suspend_time, \
@@ -2219,28 +3087,28 @@ def createHTML(testruns): · 1 unmodified line
22193087 else:
22203088 thtml = html_timetotal2.format(suspend_time, low_time, \
22213089 resume_time, testdesc1)
2222− devtl.html['timeline'] += thtml
3090+ devtl.html['header'] += thtml
22233091 sktime = '%.3f'%((data.dmesg['suspend_machine']['end'] - \
22243092 data.getStart())*1000)
22253093 sftime = '%.3f'%(data.fwSuspend / 1000000.0)
22263094 rftime = '%.3f'%(data.fwResume / 1000000.0)
2227− rktime = '%.3f'%((data.getEnd() - \
3095+ rktime = '%.3f'%((data.dmesg['resume_complete']['end'] - \
22283096 data.dmesg['resume_machine']['start'])*1000)
2229− devtl.html['timeline'] += html_timegroups.format(sktime, \
3097+ devtl.html['header'] += html_timegroups.format(sktime, \
22303098 sftime, rftime, rktime, testdesc2)
22313099 else:
22323100 suspend_time = '%.0f'%((data.tSuspended-data.start)*1000)
22333101 resume_time = '%.0f'%((tEnd-data.tSuspended)*1000)
22343102 testdesc = 'Kernel'
22353103 if(len(testruns) > 1):
2236− testdesc = textnum[data.testnumber]+' '+testdesc
3104+ testdesc = ordinal(data.testnumber+1)+' '+testdesc
22373105 if(data.tLow == 0):
22383106 thtml = html_timetotal.format(suspend_time, \
22393107 resume_time, testdesc)
22403108 else:
22413109 thtml = html_timetotal2.format(suspend_time, low_time, \
22423110 resume_time, testdesc)
2243− devtl.html['timeline'] += thtml
3111+ devtl.html['header'] += thtml
22443112
22453113 # time scale for potentially multiple datasets
22463114 t0 = testruns[0].start
@@ -2249,153 +3117,241 @@ def createHTML(testruns): · 2 unmodified lines
22493117 tTotal = tMax - t0
22503118
22513119 # determine the maximum number of rows we need to draw
2252− timelinerows = 0
22533120 for data in testruns:
2254− for phase in data.dmesg:
2255− list = data.dmesg[phase]['list']
2256− rows = setTimelineRows(list, list)
2257− data.dmesg[phase]['row'] = rows
2258− if(rows > timelinerows):
2259− timelinerows = rows
2260−
2261− # calculate the timeline height and create bounding box, add buttons
2262− devtl.setRows(timelinerows + 1)
2263− devtl.html['timeline'] += html_devlist1
2264− if len(testruns) > 1:
2265− devtl.html['timeline'] += html_devlist2
3121+ data.selectTimelineDevices('%f', tTotal, sysvals.mindevlen)
3122+ for group in data.devicegroups:
3123+ devlist = []
3124+ for phase in group:
3125+ for devname in data.tdevlist[phase]:
3126+ devlist.append((phase,devname))
3127+ devtl.getPhaseRows(data.dmesg, devlist)
3128+ devtl.calcTotalRows()
3129+
3130+ # create bounding box, add buttons
3131+ if sysvals.suspendmode != 'command':
3132+ devtl.html['timeline'] += html_devlist1
3133+ if len(testruns) > 1:
3134+ devtl.html['timeline'] += html_devlist2
22663135 devtl.html['timeline'] += html_zoombox
22673136 devtl.html['timeline'] += html_timeline.format('dmesg', devtl.height)
22683137
2269− # draw the colored boxes for each of the phases
2270− for data in testruns:
2271− for b in data.dmesg:
2272− phase = data.dmesg[b]
2273− length = phase['end']-phase['start']
2274− left = '%.3f' % (((phase['start']-t0)*100.0)/tTotal)
2275− width = '%.3f' % ((length*100.0)/tTotal)
2276− devtl.html['timeline'] += html_phase.format(left, width, \
2277− '%.3f'%devtl.scaleH, '%.3f'%(100-devtl.scaleH), \
2278− data.dmesg[b]['color'], '')
3138+ # draw the full timeline
3139+ phases = {'suspend':[],'resume':[]}
3140+ for phase in data.dmesg:
3141+ if 'resume' in phase:
3142+ phases['resume'].append(phase)
3143+ else:
3144+ phases['suspend'].append(phase)
22793145
2280− # draw the time scale, try to make the number of labels readable
2281− devtl.html['scale'] = createTimeScale(t0, tMax, tSuspended)
2282− devtl.html['timeline'] += devtl.html['scale']
2283− for data in testruns:
2284− for b in data.dmesg:
2285− phaselist = data.dmesg[b]['list']
2286− for d in phaselist:
2287− name = d
2288− drv = ''
2289− dev = phaselist[d]
2290− if(d in sysvals.altdevname):
2291− name = sysvals.altdevname[d]
2292− if('drv' in dev and dev['drv']):
2293− drv = ' {%s}' % dev['drv']
2294− height = (100.0 - devtl.scaleH)/data.dmesg[b]['row']
2295− top = '%.3f' % ((dev['row']*height) + devtl.scaleH)
2296− left = '%.3f' % (((dev['start']-t0)*100)/tTotal)
2297− width = '%.3f' % (((dev['end']-dev['start'])*100)/tTotal)
2298− length = ' (%0.3f ms) ' % ((dev['end']-dev['start'])*1000)
2299− color = 'rgba(204,204,204,0.5)'
2300− devtl.html['timeline'] += html_device.format(dev['id'], \
2301− d+drv+length+b, left, top, '%.3f'%height, width, name+drv)
2302−
2303− # draw any trace events found
3146+ # draw each test run chronologically
23043147 for data in testruns:
2305− for b in data.dmesg:
2306− phaselist = data.dmesg[b]['list']
2307− for name in phaselist:
2308− dev = phaselist[name]
2309− if('traceevents' in dev):
2310− vprint('Debug trace events found for device %s' % name)
2311− vprint('%20s %20s %10s %8s' % ('action', \
3148+ # if nore than one test, draw a block to represent user mode
3149+ if(data.testnumber > 0):
3150+ m0 = testruns[data.testnumber-1].end
3151+ mMax = testruns[data.testnumber].start
3152+ mTotal = mMax - m0
3153+ name = 'usermode%d' % data.testnumber
3154+ top = '%d' % devtl.scaleH
3155+ left = '%f' % (((m0-t0)*100.0)/tTotal)
3156+ width = '%f' % ((mTotal*100.0)/tTotal)
3157+ title = 'user mode (%0.3f ms) ' % (mTotal*1000)
3158+ devtl.html['timeline'] += html_device.format(name, \
3159+ title, left, top, '%d'%devtl.bodyH, width, '', '', '')
3160+ # now draw the actual timeline blocks
3161+ for dir in phases:
3162+ # draw suspend and resume blocks separately
3163+ bname = '%s%d' % (dir[0], data.testnumber)
3164+ if dir == 'suspend':
3165+ m0 = testruns[data.testnumber].start
3166+ mMax = testruns[data.testnumber].tSuspended
3167+ mTotal = mMax - m0
3168+ left = '%f' % (((m0-t0)*100.0)/tTotal)
3169+ else:
3170+ m0 = testruns[data.testnumber].tSuspended
3171+ mMax = testruns[data.testnumber].end
3172+ mTotal = mMax - m0
3173+ left = '%f' % ((((m0-t0)*100.0)+sysvals.srgap/2)/tTotal)
3174+ # if a timeline block is 0 length, skip altogether
3175+ if mTotal == 0:
3176+ continue
3177+ width = '%f' % (((mTotal*100.0)-sysvals.srgap/2)/tTotal)
3178+ devtl.html['timeline'] += html_tblock.format(bname, left, width)
3179+ for b in sorted(phases[dir]):
3180+ # draw the phase color background
3181+ phase = data.dmesg[b]
3182+ length = phase['end']-phase['start']
3183+ left = '%f' % (((phase['start']-m0)*100.0)/mTotal)
3184+ width = '%f' % ((length*100.0)/mTotal)
3185+ devtl.html['timeline'] += html_phase.format(left, width, \
3186+ '%.3f'%devtl.scaleH, '%.3f'%devtl.bodyH, \
3187+ data.dmesg[b]['color'], '')
3188+ # draw the devices for this phase
3189+ phaselist = data.dmesg[b]['list']
3190+ for d in data.tdevlist[b]:
3191+ name = d
3192+ drv = ''
3193+ dev = phaselist[d]
3194+ xtraclass = ''
3195+ xtrainfo = ''
3196+ xtrastyle = ''
3197+ if 'htmlclass' in dev:
3198+ xtraclass = dev['htmlclass']
3199+ xtrainfo = dev['htmlclass']
3200+ if 'color' in dev:
3201+ xtrastyle = 'background-color:%s;' % dev['color']
3202+ if(d in sysvals.devprops):
3203+ name = sysvals.devprops[d].altName(d)
3204+ xtraclass = sysvals.devprops[d].xtraClass()
3205+ xtrainfo = sysvals.devprops[d].xtraInfo()
3206+ if('drv' in dev and dev['drv']):
3207+ drv = ' {%s}' % dev['drv']
3208+ rowheight = devtl.phaseRowHeight(b, dev['row'])
3209+ rowtop = devtl.phaseRowTop(b, dev['row'])
3210+ top = '%.3f' % (rowtop + devtl.scaleH)
3211+ left = '%f' % (((dev['start']-m0)*100)/mTotal)
3212+ width = '%f' % (((dev['end']-dev['start'])*100)/mTotal)
3213+ length = ' (%0.3f ms) ' % ((dev['end']-dev['start'])*1000)
3214+ if sysvals.suspendmode == 'command':
3215+ title = name+drv+xtrainfo+length+'cmdexec'
3216+ else:
3217+ title = name+drv+xtrainfo+length+b
3218+ devtl.html['timeline'] += html_device.format(dev['id'], \
3219+ title, left, top, '%.3f'%rowheight, width, \
3220+ d+drv, xtraclass, xtrastyle)
3221+ if('src' not in dev):
3222+ continue
3223+ # draw any trace events for this device
3224+ vprint('Debug trace events found for device %s' % d)
3225+ vprint('%20s %20s %10s %8s' % ('title', \
23123226 'name', 'time(ms)', 'length(ms)'))
2313− for e in dev['traceevents']:
2314− vprint('%20s %20s %10.3f %8.3f' % (e.action, \
2315− e.name, e.time*1000, e.length*1000))
2316− height = (100.0 - devtl.scaleH)/data.dmesg[b]['row']
2317− top = '%.3f' % ((dev['row']*height) + devtl.scaleH)
2318− left = '%.3f' % (((e.time-t0)*100)/tTotal)
2319− width = '%.3f' % (e.length*100/tTotal)
3227+ for e in dev['src']:
3228+ vprint('%20s %20s %10.3f %8.3f' % (e.title, \
3229+ e.text, e.time*1000, e.length*1000))
3230+ height = devtl.rowH
3231+ top = '%.3f' % (rowtop + devtl.scaleH + (e.row*devtl.rowH))
3232+ left = '%f' % (((e.time-m0)*100)/mTotal)
3233+ width = '%f' % (e.length*100/mTotal)
23203234 color = 'rgba(204,204,204,0.5)'
23213235 devtl.html['timeline'] += \
2322− html_traceevent.format(e.action+' '+e.name, \
3236+ html_traceevent.format(e.title, \
23233237 left, top, '%.3f'%height, \
2324− width, e.color, '')
3238+ width, e.text)
3239+ # draw the time scale, try to make the number of labels readable
3240+ devtl.html['timeline'] += devtl.createTimeScale(m0, mMax, tTotal, dir)
3241+ devtl.html['timeline'] += '</div>\n'
23253242
23263243 # timeline is finished
23273244 devtl.html['timeline'] += '</div>\n</div>\n'
23283245
23293246 # draw a legend which describes the phases by color
2330− data = testruns[-1]
2331− devtl.html['legend'] = '<div class="legend">\n'
2332− pdelta = 100.0/len(data.phases)
2333− pmargin = pdelta / 4.0
2334− for phase in data.phases:
2335− order = '%.2f' % ((data.dmesg[phase]['order'] * pdelta) + pmargin)
2336− name = string.replace(phase, '_', ' &nbsp;')
2337− devtl.html['legend'] += html_legend.format(order, \
2338− data.dmesg[phase]['color'], name)
2339− devtl.html['legend'] += '</div>\n'
3247+ if sysvals.suspendmode != 'command':
3248+ data = testruns[-1]
3249+ devtl.html['legend'] = '<div class="legend">\n'
3250+ pdelta = 100.0/len(data.phases)
3251+ pmargin = pdelta / 4.0
3252+ for phase in data.phases:
3253+ tmp = phase.split('_')
3254+ id = tmp[0][0]
3255+ if(len(tmp) > 1):
3256+ id += tmp[1][0]
3257+ order = '%.2f' % ((data.dmesg[phase]['order'] * pdelta) + pmargin)
3258+ name = string.replace(phase, '_', ' &nbsp;')
3259+ devtl.html['legend'] += html_legend.format(order, \
3260+ data.dmesg[phase]['color'], name, id)
3261+ devtl.html['legend'] += '</div>\n'
23403262
23413263 hf = open(sysvals.htmlfile, 'w')
2342− thread_height = 0
3264+
3265+ if not sysvals.cgexp:
3266+ cgchk = 'checked'
3267+ cgnchk = 'not(:checked)'
3268+ else:
3269+ cgchk = 'not(:checked)'
3270+ cgnchk = 'checked'
23433271
23443272 # write the html header first (html head, css code, up to body start)
23453273 html_header = '<!DOCTYPE html>\n<html>\n<head>\n\
23463274 <meta http-equiv="content-type" content="text/html; charset=UTF-8">\n\
2347− <title>AnalyzeSuspend</title>\n\
3275+ <title>'+htmlTitle()+'</title>\n\
23483276 <style type=\'text/css\'>\n\
2349− body {overflow-y: scroll;}\n\
2350− .stamp {width: 100%;text-align:center;background-color:gray;line-height:30px;color:white;font: 25px Arial;}\n\
2351− .callgraph {margin-top: 30px;box-shadow: 5px 5px 20px black;}\n\
2352− .callgraph article * {padding-left: 28px;}\n\
2353− h1 {color:black;font: bold 30px Times;}\n\
2354− t0 {color:black;font: bold 30px Times;}\n\
2355− t1 {color:black;font: 30px Times;}\n\
2356− t2 {color:black;font: 25px Times;}\n\
2357− t3 {color:black;font: 20px Times;white-space:nowrap;}\n\
2358− t4 {color:black;font: bold 30px Times;line-height:60px;white-space:nowrap;}\n\
3277+ body {overflow-y:scroll;}\n\
3278+ .stamp {width:100%;text-align:center;background-color:gray;line-height:30px;color:white;font:25px Arial;}\n\
3279+ .callgraph {margin-top:30px;box-shadow:5px 5px 20px black;}\n\
3280+ .callgraph article * {padding-left:28px;}\n\
3281+ h1 {color:black;font:bold 30px Times;}\n\
3282+ t0 {color:black;font:bold 30px Times;}\n\
3283+ t1 {color:black;font:30px Times;}\n\
3284+ t2 {color:black;font:25px Times;}\n\
3285+ t3 {color:black;font:20px Times;white-space:nowrap;}\n\
3286+ t4 {color:black;font:bold 30px Times;line-height:60px;white-space:nowrap;}\n\
3287+ cS {color:blue;font:bold 11px Times;}\n\
3288+ cR {color:red;font:bold 11px Times;}\n\
23593289 table {width:100%;}\n\
23603290 .gray {background-color:rgba(80,80,80,0.1);}\n\
23613291 .green {background-color:rgba(204,255,204,0.4);}\n\
23623292 .purple {background-color:rgba(128,0,128,0.2);}\n\
23633293 .yellow {background-color:rgba(255,255,204,0.4);}\n\
2364− .time1 {font: 22px Arial;border:1px solid;}\n\
2365− .time2 {font: 15px Arial;border-bottom:1px solid;border-left:1px solid;border-right:1px solid;}\n\
2366− td {text-align: center;}\n\
3294+ .time1 {font:22px Arial;border:1px solid;}\n\
3295+ .time2 {font:15px Arial;border-bottom:1px solid;border-left:1px solid;border-right:1px solid;}\n\
3296+ td {text-align:center;}\n\
23673297 r {color:#500000;font:15px Tahoma;}\n\
23683298 n {color:#505050;font:15px Tahoma;}\n\
2369− .tdhl {color: red;}\n\
2370− .hide {display: none;}\n\
2371− .pf {display: none;}\n\
2372− .pf:checked + label {background: url(\'data:image/svg+xml;utf,<?xml version="1.0" standalone="no"?><svg xmlns="http://www.w3.org/2000/svg" height="18" width="18" version="1.1"><circle cx="9" cy="9" r="8" stroke="black" stroke-width="1" fill="white"/><rect x="4" y="8" width="10" height="2" style="fill:black;stroke-width:0"/><rect x="8" y="4" width="2" height="10" style="fill:black;stroke-width:0"/></svg>\') no-repeat left center;}\n\
2373− .pf:not(:checked) ~ label {background: url(\'data:image/svg+xml;utf,<?xml version="1.0" standalone="no"?><svg xmlns="http://www.w3.org/2000/svg" height="18" width="18" version="1.1"><circle cx="9" cy="9" r="8" stroke="black" stroke-width="1" fill="white"/><rect x="4" y="8" width="10" height="2" style="fill:black;stroke-width:0"/></svg>\') no-repeat left center;}\n\
2374− .pf:checked ~ *:not(:nth-child(2)) {display: none;}\n\
2375− .zoombox {position: relative; width: 100%; overflow-x: scroll;}\n\
2376− .timeline {position: relative; font-size: 14px;cursor: pointer;width: 100%; overflow: hidden; background-color:#dddddd;}\n\
2377− .thread {position: absolute; height: '+'%.3f'%thread_height+'%; overflow: hidden; line-height: 30px; border:1px solid;text-align:center;white-space:nowrap;background-color:rgba(204,204,204,0.5);}\n\
2378− .thread:hover {background-color:white;border:1px solid red;z-index:10;}\n\
2379− .hover {background-color:white;border:1px solid red;z-index:10;}\n\
2380− .traceevent {position: absolute;opacity: 0.3;height: '+'%.3f'%thread_height+'%;width:0;overflow:hidden;line-height:30px;text-align:center;white-space:nowrap;}\n\
2381− .phase {position: absolute;overflow: hidden;border:0px;text-align:center;}\n\
3299+ .tdhl {color:red;}\n\
3300+ .hide {display:none;}\n\
3301+ .pf {display:none;}\n\
3302+ .pf:'+cgchk+' + label {background:url(\'data:image/svg+xml;utf,<?xml version="1.0" standalone="no"?><svg xmlns="http://www.w3.org/2000/svg" height="18" width="18" version="1.1"><circle cx="9" cy="9" r="8" stroke="black" stroke-width="1" fill="white"/><rect x="4" y="8" width="10" height="2" style="fill:black;stroke-width:0"/><rect x="8" y="4" width="2" height="10" style="fill:black;stroke-width:0"/></svg>\') no-repeat left center;}\n\
3303+ .pf:'+cgnchk+' ~ label {background:url(\'data:image/svg+xml;utf,<?xml version="1.0" standalone="no"?><svg xmlns="http://www.w3.org/2000/svg" height="18" width="18" version="1.1"><circle cx="9" cy="9" r="8" stroke="black" stroke-width="1" fill="white"/><rect x="4" y="8" width="10" height="2" style="fill:black;stroke-width:0"/></svg>\') no-repeat left center;}\n\
3304+ .pf:'+cgchk+' ~ *:not(:nth-child(2)) {display:none;}\n\
3305+ .zoombox {position:relative;width:100%;overflow-x:scroll;}\n\
3306+ .timeline {position:relative;font-size:14px;cursor:pointer;width:100%; overflow:hidden;background:linear-gradient(#cccccc, white);}\n\
3307+ .thread {position:absolute;height:0%;overflow:hidden;line-height:'+devtextH+';font-size:'+devtextS+';border:1px solid;text-align:center;white-space:nowrap;background-color:rgba(204,204,204,0.5);}\n\
3308+ .thread.sync {background-color:'+sysvals.synccolor+';}\n\
3309+ .thread.bg {background-color:'+sysvals.kprobecolor+';}\n\
3310+ .thread:hover {background-color:white;border:1px solid red;'+hoverZ+'}\n\
3311+ .hover {background-color:white;border:1px solid red;'+hoverZ+'}\n\
3312+ .hover.sync {background-color:white;}\n\
3313+ .hover.bg {background-color:white;}\n\
3314+ .traceevent {position:absolute;font-size:10px;overflow:hidden;color:black;text-align:center;white-space:nowrap;border-radius:5px;border:1px solid black;background:linear-gradient(to bottom right,rgba(204,204,204,1),rgba(150,150,150,1));}\n\
3315+ .traceevent:hover {background:white;}\n\
3316+ .phase {position:absolute;overflow:hidden;border:0px;text-align:center;}\n\
23823317 .phaselet {position:absolute;overflow:hidden;border:0px;text-align:center;height:100px;font-size:24px;}\n\
2383− .t {position:absolute;top:0%;height:100%;border-right:1px solid black;}\n\
2384− .legend {position: relative; width: 100%; height: 40px; text-align: center;margin-bottom:20px}\n\
2385− .legend .square {position:absolute;top:10px; width: 0px;height: 20px;border:1px solid;padding-left:20px;}\n\
3318+ .t {z-index:2;position:absolute;pointer-events:none;top:0%;height:100%;border-right:1px solid black;}\n\
3319+ .legend {position:relative; width:100%; height:40px; text-align:center;margin-bottom:20px}\n\
3320+ .legend .square {position:absolute;cursor:pointer;top:10px; width:0px;height:20px;border:1px solid;padding-left:20px;}\n\
23863321 button {height:40px;width:200px;margin-bottom:20px;margin-top:20px;font-size:24px;}\n\
3322+ .logbtn {position:relative;float:right;height:25px;width:50px;margin-top:3px;margin-bottom:0;font-size:10px;text-align:center;}\n\
23873323 .devlist {position:'+x2changes[1]+';width:190px;}\n\
2388− #devicedetail {height:100px;box-shadow: 5px 5px 20px black;}\n\
3324+ a:link {color:white;text-decoration:none;}\n\
3325+ a:visited {color:white;}\n\
3326+ a:hover {color:white;}\n\
3327+ a:active {color:white;}\n\
3328+ .version {position:relative;float:left;color:white;font-size:10px;line-height:30px;margin-left:10px;}\n\
3329+ #devicedetail {height:100px;box-shadow:5px 5px 20px black;}\n\
3330+ .tblock {position:absolute;height:100%;}\n\
3331+ .bg {z-index:1;}\n\
23893332 </style>\n</head>\n<body>\n'
2390− hf.write(html_header)
3333+
3334+ # no header or css if its embedded
3335+ if(sysvals.embedded):
3336+ hf.write('pass True tSus %.3f tRes %.3f tLow %.3f fwvalid %s tSus %.3f tRes %.3f\n' %
3337+ (data.tSuspended-data.start, data.end-data.tSuspended, data.tLow, data.fwValid, \
3338+ data.fwSuspend/1000000, data.fwResume/1000000))
3339+ else:
3340+ hf.write(html_header)
23913341
23923342 # write the test title and general info header
23933343 if(sysvals.stamp['time'] != ""):
3344+ hf.write(headline_version)
3345+ if sysvals.addlogs and sysvals.dmesgfile:
3346+ hf.write('<button id="showdmesg" class="logbtn">dmesg</button>')
3347+ if sysvals.addlogs and sysvals.ftracefile:
3348+ hf.write('<button id="showftrace" class="logbtn">ftrace</button>')
23943349 hf.write(headline_stamp.format(sysvals.stamp['host'],
23953350 sysvals.stamp['kernel'], sysvals.stamp['mode'], \
23963351 sysvals.stamp['time']))
23973352
23983353 # write the device timeline
3354+ hf.write(devtl.html['header'])
23993355 hf.write(devtl.html['timeline'])
24003356 hf.write(devtl.html['legend'])
24013357 hf.write('<div id="devicedetailtitle"></div>\n')
@@ -2410,12 +3366,15 @@ def createHTML(testruns): · 8 unmodified lines
24103366 width = '%.3f' % ((length*100.0)/tTotal)
24113367 hf.write(html_phaselet.format(b, left, width, \
24123368 data.dmesg[b]['color']))
3369+ if sysvals.suspendmode == 'command':
3370+ hf.write(html_phaselet.format('cmdexec', '0', '0', \
3371+ data.dmesg['resume_complete']['color']))
24133372 hf.write('</div>\n')
24143373 hf.write('</div>\n')
24153374
24163375 # write the ftrace data (callgraph)
24173376 data = testruns[-1]
2418− if(sysvals.usecallgraph):
3377+ if(sysvals.usecallgraph and not sysvals.embedded):
24193378 hf.write('<section id="callgraphs" class="callgraph">\n')
24203379 # write out the ftrace data converted to html
24213380 html_func_top = '<article id="{0}" class="atop" style="background-color:{1}">\n<input type="checkbox" class="pf" id="f{2}" checked/><label for="f{2}">{3} {4}</label>\n'
@@ -2428,22 +3387,29 @@ def createHTML(testruns): · 6 unmodified lines
24283387 for devname in data.sortedDevices(p):
24293388 if('ftrace' not in list[devname]):
24303389 continue
2431− name = devname
2432− if(devname in sysvals.altdevname):
2433− name = sysvals.altdevname[devname]
24343390 devid = list[devname]['id']
24353391 cg = list[devname]['ftrace']
2436− flen = '<r>(%.3f ms @ %.3f to %.3f)</r>' % \
2437− ((cg.end - cg.start)*1000, cg.start*1000, cg.end*1000)
3392+ clen = (cg.end - cg.start) * 1000
3393+ if clen < sysvals.mincglen:
3394+ continue
3395+ fmt = '<r>(%.3f ms @ '+sysvals.timeformat+' to '+sysvals.timeformat+')</r>'
3396+ flen = fmt % (clen, cg.start, cg.end)
3397+ name = devname
3398+ if(devname in sysvals.devprops):
3399+ name = sysvals.devprops[devname].altName(devname)
3400+ if sysvals.suspendmode == 'command':
3401+ ftitle = name
3402+ else:
3403+ ftitle = name+' '+p
24383404 hf.write(html_func_top.format(devid, data.dmesg[p]['color'], \
2439− num, name+' '+p, flen))
3405+ num, ftitle, flen))
24403406 num += 1
24413407 for line in cg.list:
24423408 if(line.length < 0.000000001):
24433409 flen = ''
24443410 else:
2445− flen = '<n>(%.3f ms @ %.3f)</n>' % (line.length*1000, \
2446− line.time*1000)
3411+ fmt = '<n>(%.3f ms @ '+sysvals.timeformat+')</n>'
3412+ flen = fmt % (line.length*1000, line.time)
24473413 if(line.freturn and line.fcall):
24483414 hf.write(html_func_leaf.format(line.name, flen))
24493415 elif(line.freturn):
@@ -2453,9 +3419,40 @@ def createHTML(testruns): · 3 unmodified lines
24533419 num += 1
24543420 hf.write(html_func_end)
24553421 hf.write('\n\n </section>\n')
2456− # write the footer and close
2457− addScriptCode(hf, testruns)
2458− hf.write('</body>\n</html>\n')
3422+
3423+ # add the dmesg log as a hidden div
3424+ if sysvals.addlogs and sysvals.dmesgfile:
3425+ hf.write('<div id="dmesglog" style="display:none;">\n')
3426+ lf = open(sysvals.dmesgfile, 'r')
3427+ for line in lf:
3428+ hf.write(line)
3429+ lf.close()
3430+ hf.write('</div>\n')
3431+ # add the ftrace log as a hidden div
3432+ if sysvals.addlogs and sysvals.ftracefile:
3433+ hf.write('<div id="ftracelog" style="display:none;">\n')
3434+ lf = open(sysvals.ftracefile, 'r')
3435+ for line in lf:
3436+ hf.write(line)
3437+ lf.close()
3438+ hf.write('</div>\n')
3439+
3440+ if(not sysvals.embedded):
3441+ # write the footer and close
3442+ addScriptCode(hf, testruns)
3443+ hf.write('</body>\n</html>\n')
3444+ else:
3445+ # embedded out will be loaded in a page, skip the js
3446+ t0 = (testruns[0].start - testruns[-1].tSuspended) * 1000
3447+ tMax = (testruns[-1].end - testruns[-1].tSuspended) * 1000
3448+ # add js code in a div entry for later evaluation
3449+ detail = 'var bounds = [%f,%f];\n' % (t0, tMax)
3450+ detail += 'var devtable = [\n'
3451+ for data in testruns:
3452+ topo = data.deviceTopology()
3453+ detail += '\t"%s",\n' % (topo)
3454+ detail += '];\n'
3455+ hf.write('<div id=customcode style=display:none>\n'+detail+'</div>\n')
24593456 hf.close()
24603457 return True
24613458
@@ -2466,8 +3463,8 @@ def createHTML(testruns): · 4 unmodified lines
24663463 # hf: the open html file pointer
24673464 # testruns: array of Data objects from parseKernelLog or parseTraceLog
24683465 def addScriptCode(hf, testruns):
2469− t0 = (testruns[0].start - testruns[-1].tSuspended) * 1000
2470− tMax = (testruns[-1].end - testruns[-1].tSuspended) * 1000
3466+ t0 = testruns[0].start * 1000
3467+ tMax = testruns[-1].end * 1000
24713468 # create an array in javascript memory with the device details
24723469 detail = ' var devtable = [];\n'
24733470 for data in testruns:
@@ -2477,8 +3474,43 @@ def addScriptCode(hf, testruns): · 3 unmodified lines
24773474 # add the code which will manipulate the data in the browser
24783475 script_code = \
24793476 '<script type="text/javascript">\n'+detail+\
3477+ ' var resolution = -1;\n'\
3478+ ' function redrawTimescale(t0, tMax, tS) {\n'\
3479+ ' var rline = \'<div class="t" style="left:0;border-left:1px solid black;border-right:0;"><cR><-R</cR></div>\';\n'\
3480+ ' var tTotal = tMax - t0;\n'\
3481+ ' var list = document.getElementsByClassName("tblock");\n'\
3482+ ' for (var i = 0; i < list.length; i++) {\n'\
3483+ ' var timescale = list[i].getElementsByClassName("timescale")[0];\n'\
3484+ ' var m0 = t0 + (tTotal*parseFloat(list[i].style.left)/100);\n'\
3485+ ' var mTotal = tTotal*parseFloat(list[i].style.width)/100;\n'\
3486+ ' var mMax = m0 + mTotal;\n'\
3487+ ' var html = "";\n'\
3488+ ' var divTotal = Math.floor(mTotal/tS) + 1;\n'\
3489+ ' if(divTotal > 1000) continue;\n'\
3490+ ' var divEdge = (mTotal - tS*(divTotal-1))*100/mTotal;\n'\
3491+ ' var pos = 0.0, val = 0.0;\n'\
3492+ ' for (var j = 0; j < divTotal; j++) {\n'\
3493+ ' var htmlline = "";\n'\
3494+ ' if(list[i].id[5] == "r") {\n'\
3495+ ' pos = 100 - (((j)*tS*100)/mTotal);\n'\
3496+ ' val = (j)*tS;\n'\
3497+ ' htmlline = \'<div class="t" style="right:\'+pos+\'%">\'+val+\'ms</div>\';\n'\
3498+ ' if(j == 0)\n'\
3499+ ' htmlline = rline;\n'\
3500+ ' } else {\n'\
3501+ ' pos = 100 - (((j)*tS*100)/mTotal) - divEdge;\n'\
3502+ ' val = (j-divTotal+1)*tS;\n'\
3503+ ' if(j == divTotal - 1)\n'\
3504+ ' htmlline = \'<div class="t" style="right:\'+pos+\'%"><cS>S-></cS></div>\';\n'\
3505+ ' else\n'\
3506+ ' htmlline = \'<div class="t" style="right:\'+pos+\'%">\'+val+\'ms</div>\';\n'\
3507+ ' }\n'\
3508+ ' html += htmlline;\n'\
3509+ ' }\n'\
3510+ ' timescale.innerHTML = html;\n'\
3511+ ' }\n'\
3512+ ' }\n'\
24803513 ' function zoomTimeline() {\n'\
2481− ' var timescale = document.getElementById("timescale");\n'\
24823514 ' var dmesg = document.getElementById("dmesg");\n'\
24833515 ' var zoombox = document.getElementById("dmesgzoombox");\n'\
24843516 ' var val = parseFloat(dmesg.style.width);\n'\
@@ -2486,7 +3518,7 @@ def addScriptCode(hf, testruns): · 1 unmodified line
24863518 ' var sh = window.outerWidth / 2;\n'\
24873519 ' if(this.id == "zoomin") {\n'\
24883520 ' newval = val * 1.2;\n'\
2489− ' if(newval > 40000) newval = 40000;\n'\
3521+ ' if(newval > 910034) newval = 910034;\n'\
24903522 ' dmesg.style.width = newval+"%";\n'\
24913523 ' zoombox.scrollLeft = ((zoombox.scrollLeft + sh) * newval / val) - sh;\n'\
24923524 ' } else if (this.id == "zoomout") {\n'\
@@ -2498,19 +3530,17 @@ def addScriptCode(hf, testruns): · 5 unmodified lines
24983530 ' zoombox.scrollLeft = 0;\n'\
24993531 ' dmesg.style.width = "100%";\n'\
25003532 ' }\n'\
2501− ' var html = "";\n'\
3533+ ' var tS = [10000, 5000, 2000, 1000, 500, 200, 100, 50, 20, 10, 5, 2, 1];\n'\
25023534 ' var t0 = bounds[0];\n'\
25033535 ' var tMax = bounds[1];\n'\
25043536 ' var tTotal = tMax - t0;\n'\
25053537 ' var wTotal = tTotal * 100.0 / newval;\n'\
2506− ' for(var tS = 1000; (wTotal / tS) < 3; tS /= 10);\n'\
2507− ' if(tS < 1) tS = 1;\n'\
2508− ' for(var s = ((t0 / tS)|0) * tS; s < tMax; s += tS) {\n'\
2509− ' var pos = (tMax - s) * 100.0 / tTotal;\n'\
2510− ' var name = (s == 0)?"S/R":(s+"ms");\n'\
2511− ' html += "<div class=\\"t\\" style=\\"right:"+pos+"%\\">"+name+"</div>";\n'\
2512− ' }\n'\
2513− ' timescale.innerHTML = html;\n'\
3538+ ' var idx = 7*window.innerWidth/1100;\n'\
3539+ ' for(var i = 0; (i < tS.length)&&((wTotal / tS[i]) < idx); i++);\n'\
3540+ ' if(i >= tS.length) i = tS.length - 1;\n'\
3541+ ' if(tS[i] == resolution) return;\n'\
3542+ ' resolution = tS[i];\n'\
3543+ ' redrawTimescale(t0, tMax, tS[i]);\n'\
25143544 ' }\n'\
25153545 ' function deviceHover() {\n'\
25163546 ' var name = this.title.slice(0, this.title.indexOf(" ("));\n'\
@@ -2523,12 +3553,13 @@ def addScriptCode(hf, testruns): · 6 unmodified lines
25233553 ' cpu = parseInt(name.slice(8));\n'\
25243554 ' for (var i = 0; i < dev.length; i++) {\n'\
25253555 ' dname = dev[i].title.slice(0, dev[i].title.indexOf(" ("));\n'\
3556+ ' var cname = dev[i].className.slice(dev[i].className.indexOf("thread"));\n'\
25263557 ' if((cpu >= 0 && dname.match("CPU_O[NF]*\\\[*"+cpu+"\\\]")) ||\n'\
25273558 ' (name == dname))\n'\
25283559 ' {\n'\
2529− ' dev[i].className = "thread hover";\n'\
3560+ ' dev[i].className = "hover "+cname;\n'\
25303561 ' } else {\n'\
2531− ' dev[i].className = "thread";\n'\
3562+ ' dev[i].className = cname;\n'\
25323563 ' }\n'\
25333564 ' }\n'\
25343565 ' }\n'\
@@ -2536,7 +3567,7 @@ def addScriptCode(hf, testruns): · 1 unmodified line
25363567 ' var dmesg = document.getElementById("dmesg");\n'\
25373568 ' var dev = dmesg.getElementsByClassName("thread");\n'\
25383569 ' for (var i = 0; i < dev.length; i++) {\n'\
2539− ' dev[i].className = "thread";\n'\
3570+ ' dev[i].className = dev[i].className.slice(dev[i].className.indexOf("thread"));\n'\
25403571 ' }\n'\
25413572 ' }\n'\
25423573 ' function deviceTitle(title, total, cpu) {\n'\
@@ -2547,7 +3578,7 @@ def addScriptCode(hf, testruns): · 4 unmodified lines
25473578 ' total[2] = (total[2]+total[4])/2;\n'\
25483579 ' }\n'\
25493580 ' var devtitle = document.getElementById("devicedetailtitle");\n'\
2550− ' var name = title.slice(0, title.indexOf(" "));\n'\
3581+ ' var name = title.slice(0, title.indexOf(" ("));\n'\
25513582 ' if(cpu >= 0) name = "CPU"+cpu;\n'\
25523583 ' var driver = "";\n'\
25533584 ' var tS = "<t2>(</t2>";\n'\
@@ -2579,6 +3610,8 @@ def addScriptCode(hf, testruns): · 25 unmodified lines
25793610 ' var dev = dmesg.getElementsByClassName("thread");\n'\
25803611 ' var idlist = [];\n'\
25813612 ' var pdata = [[]];\n'\
3613+ ' if(document.getElementById("devicedetail1"))\n'\
3614+ ' pdata = [[], []];\n'\
25823615 ' var pd = pdata[0];\n'\
25833616 ' var total = [0.0, 0.0, 0.0];\n'\
25843617 ' for (var i = 0; i < dev.length; i++) {\n'\
@@ -2634,6 +3667,7 @@ def addScriptCode(hf, testruns): · 49 unmodified lines
26343667 ' var cglist = document.getElementById("callgraphs");\n'\
26353668 ' if(!cglist) return;\n'\
26363669 ' var cg = cglist.getElementsByClassName("atop");\n'\
3670+ ' if(cg.length < 10) return;\n'\
26373671 ' for (var i = 0; i < cg.length; i++) {\n'\
26383672 ' if(idlist.indexOf(cg[i].id) >= 0) {\n'\
26393673 ' cg[i].style.display = "block";\n'\
@@ -2658,15 +3692,32 @@ def addScriptCode(hf, testruns): · 18 unmodified lines
26583692 ' dt = devtable[1];\n'\
26593693 ' win.document.write(html+dt);\n'\
26603694 ' }\n'\
3695+ ' function logWindow(e) {\n'\
3696+ ' var name = e.target.id.slice(4);\n'\
3697+ ' var win = window.open();\n'\
3698+ ' var log = document.getElementById(name+"log");\n'\
3699+ ' var title = "<title>"+document.title.split(" ")[0]+" "+name+" log</title>";\n'\
3700+ ' win.document.write(title+"<pre>"+log.innerHTML+"</pre>");\n'\
3701+ ' win.document.close();\n'\
3702+ ' }\n'\
3703+ ' function onClickPhase(e) {\n'\
3704+ ' }\n'\
3705+ ' window.addEventListener("resize", function () {zoomTimeline();});\n'\
26613706 ' window.addEventListener("load", function () {\n'\
26623707 ' var dmesg = document.getElementById("dmesg");\n'\
26633708 ' dmesg.style.width = "100%"\n'\
26643709 ' document.getElementById("zoomin").onclick = zoomTimeline;\n'\
26653710 ' document.getElementById("zoomout").onclick = zoomTimeline;\n'\
26663711 ' document.getElementById("zoomdef").onclick = zoomTimeline;\n'\
2667− ' var devlist = document.getElementsByClassName("devlist");\n'\
2668− ' for (var i = 0; i < devlist.length; i++)\n'\
2669− ' devlist[i].onclick = devListWindow;\n'\
3712+ ' var list = document.getElementsByClassName("square");\n'\
3713+ ' for (var i = 0; i < list.length; i++)\n'\
3714+ ' list[i].onclick = onClickPhase;\n'\
3715+ ' var list = document.getElementsByClassName("logbtn");\n'\
3716+ ' for (var i = 0; i < list.length; i++)\n'\
3717+ ' list[i].onclick = logWindow;\n'\
3718+ ' list = document.getElementsByClassName("devlist");\n'\
3719+ ' for (var i = 0; i < list.length; i++)\n'\
3720+ ' list[i].onclick = devListWindow;\n'\
26703721 ' var dev = dmesg.getElementsByClassName("thread");\n'\
26713722 ' for (var i = 0; i < dev.length; i++) {\n'\
26723723 ' dev[i].onclick = deviceDetail;\n'\
@@ -2685,141 +3736,87 @@ def addScriptCode(hf, testruns): · 12 unmodified lines
26853736 def executeSuspend():
26863737 global sysvals
26873738
2688− detectUSB(False)
26893739 t0 = time.time()*1000
26903740 tp = sysvals.tpath
3741+ fwdata = []
3742+ # mark the start point in the kernel ring buffer just as we start
3743+ sysvals.initdmesg()
3744+ # start ftrace
3745+ if(sysvals.usecallgraph or sysvals.usetraceevents):
3746+ print('START TRACING')
3747+ sysvals.fsetVal('1', 'tracing_on')
26913748 # execute however many s/r runs requested
26923749 for count in range(1,sysvals.execcount+1):
2693− # clear the kernel ring buffer just as we start
2694− os.system('dmesg -C')
2695− # enable callgraph ftrace only for the second run
2696− if(sysvals.usecallgraph and count == 2):
2697− # set trace type
2698− os.system('echo function_graph > '+tp+'current_tracer')
2699− os.system('echo "" > '+tp+'set_ftrace_filter')
2700− # set trace format options
2701− os.system('echo funcgraph-abstime > '+tp+'trace_options')
2702− os.system('echo funcgraph-proc > '+tp+'trace_options')
2703− # focus only on device suspend and resume
2704− os.system('cat '+tp+'available_filter_functions | '+\
2705− 'grep dpm_run_callback > '+tp+'set_graph_function')
27063750 # if this is test2 and there's a delay, start here
27073751 if(count > 1 and sysvals.x2delay > 0):
27083752 tN = time.time()*1000
27093753 while (tN - t0) < sysvals.x2delay:
27103754 tN = time.time()*1000
27113755 time.sleep(0.001)
2712− # start ftrace
2713− if(sysvals.usecallgraph or sysvals.usetraceevents):
2714− print('START TRACING')
2715− os.system('echo 1 > '+tp+'tracing_on')
27163756 # initiate suspend
27173757 if(sysvals.usecallgraph or sysvals.usetraceevents):
2718− os.system('echo SUSPEND START > '+tp+'trace_marker')
2719− if(sysvals.rtcwake):
2720− print('SUSPEND START')
2721− print('will autoresume in %d seconds' % sysvals.rtcwaketime)
2722− sysvals.rtcWakeAlarm()
3758+ sysvals.fsetVal('SUSPEND START', 'trace_marker')
3759+ if sysvals.suspendmode == 'command':
3760+ print('COMMAND START')
3761+ if(sysvals.rtcwake):
3762+ print('will issue an rtcwake in %d seconds' % sysvals.rtcwaketime)
3763+ sysvals.rtcWakeAlarmOn()
3764+ os.system(sysvals.testcommand)
27233765 else:
2724− print('SUSPEND START (press a key to resume)')
2725− pf = open(sysvals.powerfile, 'w')
2726− pf.write(sysvals.suspendmode)
2727− # execution will pause here
2728− pf.close()
3766+ if(sysvals.rtcwake):
3767+ print('SUSPEND START')
3768+ print('will autoresume in %d seconds' % sysvals.rtcwaketime)
3769+ sysvals.rtcWakeAlarmOn()
3770+ else:
3771+ print('SUSPEND START (press a key to resume)')
3772+ pf = open(sysvals.powerfile, 'w')
3773+ pf.write(sysvals.suspendmode)
3774+ # execution will pause here
3775+ try:
3776+ pf.close()
3777+ except:
3778+ pass
27293779 t0 = time.time()*1000
3780+ if(sysvals.rtcwake):
3781+ sysvals.rtcWakeAlarmOff()
27303782 # return from suspend
27313783 print('RESUME COMPLETE')
27323784 if(sysvals.usecallgraph or sysvals.usetraceevents):
2733− os.system('echo RESUME COMPLETE > '+tp+'trace_marker')
2734− # see if there's firmware timing data to be had
2735− t = sysvals.postresumetime
2736− if(t > 0):
2737− print('Waiting %d seconds for POST-RESUME trace events...' % t)
2738− time.sleep(t)
2739− # stop ftrace
2740− if(sysvals.usecallgraph or sysvals.usetraceevents):
2741− os.system('echo 0 > '+tp+'tracing_on')
2742− print('CAPTURING TRACE')
2743− writeDatafileHeader(sysvals.ftracefile)
2744− os.system('cat '+tp+'trace >> '+sysvals.ftracefile)
2745− os.system('echo "" > '+tp+'trace')
2746− # grab a copy of the dmesg output
2747− print('CAPTURING DMESG')
2748− writeDatafileHeader(sysvals.dmesgfile)
2749− os.system('dmesg -c >> '+sysvals.dmesgfile)
2750−
2751−def writeDatafileHeader(filename):
3785+ sysvals.fsetVal('RESUME COMPLETE', 'trace_marker')
3786+ if(sysvals.suspendmode == 'mem'):
3787+ fwdata.append(getFPDT(False))
3788+ # look for post resume events after the last test run
3789+ t = sysvals.postresumetime
3790+ if(t > 0):
3791+ print('Waiting %d seconds for POST-RESUME trace events...' % t)
3792+ time.sleep(t)
3793+ # stop ftrace
3794+ if(sysvals.usecallgraph or sysvals.usetraceevents):
3795+ sysvals.fsetVal('0', 'tracing_on')
3796+ print('CAPTURING TRACE')
3797+ writeDatafileHeader(sysvals.ftracefile, fwdata)
3798+ os.system('cat '+tp+'trace >> '+sysvals.ftracefile)
3799+ sysvals.fsetVal('', 'trace')
3800+ devProps()
3801+ # grab a copy of the dmesg output
3802+ print('CAPTURING DMESG')
3803+ writeDatafileHeader(sysvals.dmesgfile, fwdata)
3804+ sysvals.getdmesg()
3805+
3806+def writeDatafileHeader(filename, fwdata):
27523807 global sysvals
27533808
2754− fw = getFPDT(False)
27553809 prt = sysvals.postresumetime
27563810 fp = open(filename, 'a')
27573811 fp.write(sysvals.teststamp+'\n')
2758− if(fw):
2759− fp.write('# fwsuspend %u fwresume %u\n' % (fw[0], fw[1]))
3812+ if(sysvals.suspendmode == 'mem'):
3813+ for fw in fwdata:
3814+ if(fw):
3815+ fp.write('# fwsuspend %u fwresume %u\n' % (fw[0], fw[1]))
27603816 if(prt > 0):
27613817 fp.write('# post resume time %u\n' % prt)
27623818 fp.close()
27633819
2764−# Function: executeAndroidSuspend
2765−# Description:
2766−# Execute system suspend through the sysfs interface
2767−# on a remote android device, then transfer the output
2768−# dmesg and ftrace files to the local output directory.
2769−def executeAndroidSuspend():
2770− global sysvals
2771−
2772− # check to see if the display is currently off
2773− tp = sysvals.tpath
2774− out = os.popen(sysvals.adb+\
2775− ' shell dumpsys power | grep mScreenOn').read().strip()
2776− # if so we need to turn it on so we can issue a new suspend
2777− if(out.endswith('false')):
2778− print('Waking the device up for the test...')
2779− # send the KEYPAD_POWER keyevent to wake it up
2780− os.system(sysvals.adb+' shell input keyevent 26')
2781− # wait a few seconds so the user can see the device wake up
2782− time.sleep(3)
2783− # execute however many s/r runs requested
2784− for count in range(1,sysvals.execcount+1):
2785− # clear the kernel ring buffer just as we start
2786− os.system(sysvals.adb+' shell dmesg -c > /dev/null 2>&1')
2787− # start ftrace
2788− if(sysvals.usetraceevents):
2789− print('START TRACING')
2790− os.system(sysvals.adb+" shell 'echo 1 > "+tp+"tracing_on'")
2791− # initiate suspend
2792− for count in range(1,sysvals.execcount+1):
2793− if(sysvals.usetraceevents):
2794− os.system(sysvals.adb+\
2795− " shell 'echo SUSPEND START > "+tp+"trace_marker'")
2796− print('SUSPEND START (press a key on the device to resume)')
2797− os.system(sysvals.adb+" shell 'echo "+sysvals.suspendmode+\
2798− " > "+sysvals.powerfile+"'")
2799− # execution will pause here, then adb will exit
2800− while(True):
2801− check = os.popen(sysvals.adb+\
2802− ' shell pwd 2>/dev/null').read().strip()
2803− if(len(check) > 0):
2804− break
2805− time.sleep(1)
2806− if(sysvals.usetraceevents):
2807− os.system(sysvals.adb+" shell 'echo RESUME COMPLETE > "+tp+\
2808− "trace_marker'")
2809− # return from suspend
2810− print('RESUME COMPLETE')
2811− # stop ftrace
2812− if(sysvals.usetraceevents):
2813− os.system(sysvals.adb+" shell 'echo 0 > "+tp+"tracing_on'")
2814− print('CAPTURING TRACE')
2815− os.system('echo "'+sysvals.teststamp+'" > '+sysvals.ftracefile)
2816− os.system(sysvals.adb+' shell cat '+tp+\
2817− 'trace >> '+sysvals.ftracefile)
2818− # grab a copy of the dmesg output
2819− print('CAPTURING DMESG')
2820− os.system('echo "'+sysvals.teststamp+'" > '+sysvals.dmesgfile)
2821− os.system(sysvals.adb+' shell dmesg >> '+sysvals.dmesgfile)
2822−
28233820 # Function: setUSBDevicesAuto
28243821 # Description:
28253822 # Set the autosuspend control parameter of all USB devices to auto
@@ -2829,7 +3826,7 @@ def executeAndroidSuspend(): · 3 unmodified lines
28293826 def setUSBDevicesAuto():
28303827 global sysvals
28313828
2832− rootCheck()
3829+ rootCheck(True)
28333830 for dirname, dirnames, filenames in os.walk('/sys/devices'):
28343831 if(re.match('.*/usb[0-9]*.*', dirname) and
28353832 'idVendor' in filenames and 'idProduct' in filenames):
@@ -2874,9 +3871,7 @@ def ms2nice(val): · 38 unmodified lines
28743871 # Description:
28753872 # Detect all the USB hosts and devices currently connected and add
28763873 # a list of USB device names to sysvals for better timeline readability
2877−# Arguments:
2878−# output: True to output the info to stdout, False otherwise
2879−def detectUSB(output):
3874+def detectUSB():
28803875 global sysvals
28813876
28823877 field = {'idVendor':'', 'idProduct':'', 'product':'', 'speed':''}
@@ -2887,18 +3882,18 @@ def detectUSB(output): · 4 unmodified lines
28873882 'runtime_suspended_time':'',
28883883 'active_duration':'',
28893884 'connected_duration':''}
2890− if(output):
2891− print('LEGEND')
2892− print('---------------------------------------------------------------------------------------------')
2893− print(' A = async/sync PM queue Y/N D = autosuspend delay (seconds)')
2894− print(' S = autosuspend Y/N rACTIVE = runtime active (min/sec)')
2895− print(' P = persist across suspend Y/N rSUSPEN = runtime suspend (min/sec)')
2896− print(' E = runtime suspend enabled/forbidden Y/N ACTIVE = active duration (min/sec)')
2897− print(' R = runtime status active/suspended Y/N CONNECT = connected duration (min/sec)')
2898− print(' U = runtime usage count')
2899− print('---------------------------------------------------------------------------------------------')
2900− print(' NAME ID DESCRIPTION SPEED A S P E R U D rACTIVE rSUSPEN ACTIVE CONNECT')
2901− print('---------------------------------------------------------------------------------------------')
3885+
3886+ print('LEGEND')
3887+ print('---------------------------------------------------------------------------------------------')
3888+ print(' A = async/sync PM queue Y/N D = autosuspend delay (seconds)')
3889+ print(' S = autosuspend Y/N rACTIVE = runtime active (min/sec)')
3890+ print(' P = persist across suspend Y/N rSUSPEN = runtime suspend (min/sec)')
3891+ print(' E = runtime suspend enabled/forbidden Y/N ACTIVE = active duration (min/sec)')
3892+ print(' R = runtime status active/suspended Y/N CONNECT = connected duration (min/sec)')
3893+ print(' U = runtime usage count')
3894+ print('---------------------------------------------------------------------------------------------')
3895+ print(' NAME ID DESCRIPTION SPEED A S P E R U D rACTIVE rSUSPEN ACTIVE CONNECT')
3896+ print('---------------------------------------------------------------------------------------------')
29023897
29033898 for dirname, dirnames, filenames in os.walk('/sys/devices'):
29043899 if(re.match('.*/usb[0-9]*.*', dirname) and
@@ -2907,35 +3902,149 @@ def detectUSB(output): · 2 unmodified lines
29073902 field[i] = os.popen('cat %s/%s 2>/dev/null' % \
29083903 (dirname, i)).read().replace('\n', '')
29093904 name = dirname.split('/')[-1]
2910− if(len(field['product']) > 0):
2911− sysvals.altdevname[name] = \
2912− '%s [%s]' % (field['product'], name)
3905+ for i in power:
3906+ power[i] = os.popen('cat %s/power/%s 2>/dev/null' % \
3907+ (dirname, i)).read().replace('\n', '')
3908+ if(re.match('usb[0-9]*', name)):
3909+ first = '%-8s' % name
29133910 else:
2914− sysvals.altdevname[name] = \
2915− '%s:%s [%s]' % (field['idVendor'], \
2916− field['idProduct'], name)
2917− if(output):
2918− for i in power:
2919− power[i] = os.popen('cat %s/power/%s 2>/dev/null' % \
2920− (dirname, i)).read().replace('\n', '')
2921− if(re.match('usb[0-9]*', name)):
2922− first = '%-8s' % name
2923− else:
2924− first = '%8s' % name
2925− print('%s [%s:%s] %-20s %-4s %1s %1s %1s %1s %1s %1s %1s %s %s %s %s' % \
2926− (first, field['idVendor'], field['idProduct'], \
2927− field['product'][0:20], field['speed'], \
2928− yesno(power['async']), \
2929− yesno(power['control']), \
2930− yesno(power['persist']), \
2931− yesno(power['runtime_enabled']), \
2932− yesno(power['runtime_status']), \
2933− power['runtime_usage'], \
2934− power['autosuspend'], \
2935− ms2nice(power['runtime_active_time']), \
2936− ms2nice(power['runtime_suspended_time']), \
2937− ms2nice(power['active_duration']), \
2938− ms2nice(power['connected_duration'])))
3911+ first = '%8s' % name
3912+ print('%s [%s:%s] %-20s %-4s %1s %1s %1s %1s %1s %1s %1s %s %s %s %s' % \
3913+ (first, field['idVendor'], field['idProduct'], \
3914+ field['product'][0:20], field['speed'], \
3915+ yesno(power['async']), \
3916+ yesno(power['control']), \
3917+ yesno(power['persist']), \
3918+ yesno(power['runtime_enabled']), \
3919+ yesno(power['runtime_status']), \
3920+ power['runtime_usage'], \
3921+ power['autosuspend'], \
3922+ ms2nice(power['runtime_active_time']), \
3923+ ms2nice(power['runtime_suspended_time']), \
3924+ ms2nice(power['active_duration']), \
3925+ ms2nice(power['connected_duration'])))
3926+
3927+# Function: devProps
3928+# Description:
3929+# Retrieve a list of properties for all devices in the trace log
3930+def devProps(data=0):
3931+ global sysvals
3932+ props = dict()
3933+
3934+ if data:
3935+ idx = data.index(': ') + 2
3936+ if idx >= len(data):
3937+ return
3938+ devlist = data[idx:].split(';')
3939+ for dev in devlist:
3940+ f = dev.split(',')
3941+ if len(f) < 3:
3942+ continue
3943+ dev = f[0]
3944+ props[dev] = DevProps()
3945+ props[dev].altname = f[1]
3946+ if int(f[2]):
3947+ props[dev].async = True
3948+ else:
3949+ props[dev].async = False
3950+ sysvals.devprops = props
3951+ if sysvals.suspendmode == 'command' and 'testcommandstring' in props:
3952+ sysvals.testcommand = props['testcommandstring'].altname
3953+ return
3954+
3955+ if(os.path.exists(sysvals.ftracefile) == False):
3956+ doError('%s does not exist' % sysvals.ftracefile, False)
3957+
3958+ # first get the list of devices we need properties for
3959+ msghead = 'Additional data added by AnalyzeSuspend'
3960+ alreadystamped = False
3961+ tp = TestProps()
3962+ tf = open(sysvals.ftracefile, 'r')
3963+ for line in tf:
3964+ if msghead in line:
3965+ alreadystamped = True
3966+ continue
3967+ # determine the trace data type (required for further parsing)
3968+ m = re.match(sysvals.tracertypefmt, line)
3969+ if(m):
3970+ tp.setTracerType(m.group('t'))
3971+ continue
3972+ # parse only valid lines, if this is not one move on
3973+ m = re.match(tp.ftrace_line_fmt, line)
3974+ if(not m or 'device_pm_callback_start' not in line):
3975+ continue
3976+ m = re.match('.*: (?P<drv>.*) (?P<d>.*), parent: *(?P<p>.*), .*', m.group('msg'));
3977+ if(not m):
3978+ continue
3979+ drv, dev, par = m.group('drv'), m.group('d'), m.group('p')
3980+ if dev not in props:
3981+ props[dev] = DevProps()
3982+ tf.close()
3983+
3984+ if not alreadystamped and sysvals.suspendmode == 'command':
3985+ out = '#\n# '+msghead+'\n# Device Properties: '
3986+ out += 'testcommandstring,%s,0;' % (sysvals.testcommand)
3987+ with open(sysvals.ftracefile, 'a') as fp:
3988+ fp.write(out+'\n')
3989+ sysvals.devprops = props
3990+ return
3991+
3992+ # now get the syspath for each of our target devices
3993+ for dirname, dirnames, filenames in os.walk('/sys/devices'):
3994+ if(re.match('.*/power', dirname) and 'async' in filenames):
3995+ dev = dirname.split('/')[-2]
3996+ if dev in props and (not props[dev].syspath or len(dirname) < len(props[dev].syspath)):
3997+ props[dev].syspath = dirname[:-6]
3998+
3999+ # now fill in the properties for our target devices
4000+ for dev in props:
4001+ dirname = props[dev].syspath
4002+ if not dirname or not os.path.exists(dirname):
4003+ continue
4004+ with open(dirname+'/power/async') as fp:
4005+ text = fp.read()
4006+ props[dev].async = False
4007+ if 'enabled' in text:
4008+ props[dev].async = True
4009+ fields = os.listdir(dirname)
4010+ if 'product' in fields:
4011+ with open(dirname+'/product') as fp:
4012+ props[dev].altname = fp.read()
4013+ elif 'name' in fields:
4014+ with open(dirname+'/name') as fp:
4015+ props[dev].altname = fp.read()
4016+ elif 'model' in fields:
4017+ with open(dirname+'/model') as fp:
4018+ props[dev].altname = fp.read()
4019+ elif 'description' in fields:
4020+ with open(dirname+'/description') as fp:
4021+ props[dev].altname = fp.read()
4022+ elif 'id' in fields:
4023+ with open(dirname+'/id') as fp:
4024+ props[dev].altname = fp.read()
4025+ elif 'idVendor' in fields and 'idProduct' in fields:
4026+ idv, idp = '', ''
4027+ with open(dirname+'/idVendor') as fp:
4028+ idv = fp.read().strip()
4029+ with open(dirname+'/idProduct') as fp:
4030+ idp = fp.read().strip()
4031+ props[dev].altname = '%s:%s' % (idv, idp)
4032+
4033+ if props[dev].altname:
4034+ out = props[dev].altname.strip().replace('\n', ' ')
4035+ out = out.replace(',', ' ')
4036+ out = out.replace(';', ' ')
4037+ props[dev].altname = out
4038+
4039+ # and now write the data to the ftrace file
4040+ if not alreadystamped:
4041+ out = '#\n# '+msghead+'\n# Device Properties: '
4042+ for dev in sorted(props):
4043+ out += props[dev].out(dev)
4044+ with open(sysvals.ftracefile, 'a') as fp:
4045+ fp.write(out+'\n')
4046+
4047+ sysvals.devprops = props
29394048
29404049 # Function: getModes
29414050 # Description:
@@ -2945,15 +4054,10 @@ def detectUSB(output): · 3 unmodified lines
29454054 def getModes():
29464055 global sysvals
29474056 modes = ''
2948− if(not sysvals.android):
2949− if(os.path.exists(sysvals.powerfile)):
2950− fp = open(sysvals.powerfile, 'r')
2951− modes = string.split(fp.read())
2952− fp.close()
2953− else:
2954− line = os.popen(sysvals.adb+' shell cat '+\
2955− sysvals.powerfile).read().strip()
2956− modes = string.split(line)
4057+ if(os.path.exists(sysvals.powerfile)):
4058+ fp = open(sysvals.powerfile, 'r')
4059+ modes = string.split(fp.read())
4060+ fp.close()
29574061 return modes
29584062
29594063 # Function: getFPDT
@@ -2971,22 +4075,22 @@ def getFPDT(output): · 11 unmodified lines
29714075 prectype[0] = 'Basic S3 Resume Performance Record'
29724076 prectype[1] = 'Basic S3 Suspend Performance Record'
29734077
2974− rootCheck()
4078+ rootCheck(True)
29754079 if(not os.path.exists(sysvals.fpdtpath)):
29764080 if(output):
2977− doError('file doesnt exist: %s' % sysvals.fpdtpath, False)
4081+ doError('file does not exist: %s' % sysvals.fpdtpath, False)
29784082 return False
29794083 if(not os.access(sysvals.fpdtpath, os.R_OK)):
29804084 if(output):
2981− doError('file isnt readable: %s' % sysvals.fpdtpath, False)
4085+ doError('file is not readable: %s' % sysvals.fpdtpath, False)
29824086 return False
29834087 if(not os.path.exists(sysvals.mempath)):
29844088 if(output):
2985− doError('file doesnt exist: %s' % sysvals.mempath, False)
4089+ doError('file does not exist: %s' % sysvals.mempath, False)
29864090 return False
29874091 if(not os.access(sysvals.mempath, os.R_OK)):
29884092 if(output):
2989− doError('file isnt readable: %s' % sysvals.mempath, False)
4093+ doError('file is not readable: %s' % sysvals.mempath, False)
29904094 return False
29914095
29924096 fp = open(sysvals.fpdtpath, 'rb')
@@ -3027,15 +4131,19 @@ def getFPDT(output): · 34 unmodified lines
30274131 while(i < len(records)):
30284132 header = struct.unpack('HBB', records[i:i+4])
30294133 if(header[0] not in rectype):
4134+ i += header[1]
30304135 continue
30314136 if(header[1] != 16):
4137+ i += header[1]
30324138 continue
30334139 addr = struct.unpack('Q', records[i+8:i+16])[0]
30344140 try:
30354141 fp.seek(addr)
30364142 first = fp.read(8)
30374143 except:
3038− doError('Bad address 0x%x in %s' % (addr, sysvals.mempath), False)
4144+ if(output):
4145+ print('Bad address 0x%x in %s' % (addr, sysvals.mempath))
4146+ return [0, 0]
30394147 rechead = struct.unpack('4sI', first)
30404148 recdata = fp.read(rechead[1]-8)
30414149 if(rechead[0] == 'FBPT'):
@@ -3090,89 +4198,60 @@ def getFPDT(output): · 48 unmodified lines
30904198 # print the results to the terminal
30914199 # Output:
30924200 # True if the test will work, False if not
3093−def statusCheck():
4201+def statusCheck(probecheck=False):
30944202 global sysvals
30954203 status = True
30964204
3097− if(sysvals.android):
3098− print('Checking the android system ...')
3099− else:
3100− print('Checking this system (%s)...' % platform.node())
3101−
3102− # check if adb is connected to a device
3103− if(sysvals.android):
3104− res = 'NO'
3105− out = os.popen(sysvals.adb+' get-state').read().strip()
3106− if(out == 'device'):
3107− res = 'YES'
3108− print(' is android device connected: %s' % res)
3109− if(res != 'YES'):
3110− print(' Please connect the device before using this tool')
3111− return False
4205+ print('Checking this system (%s)...' % platform.node())
31124206
31134207 # check we have root access
3114− res = 'NO (No features of this tool will work!)'
3115− if(sysvals.android):
3116− out = os.popen(sysvals.adb+' shell id').read().strip()
3117− if('root' in out):
3118− res = 'YES'
3119− else:
3120− if(os.environ['USER'] == 'root'):
3121− res = 'YES'
4208+ res = sysvals.colorText('NO (No features of this tool will work!)')
4209+ if(rootCheck(False)):
4210+ res = 'YES'
31224211 print(' have root access: %s' % res)
31234212 if(res != 'YES'):
3124− if(sysvals.android):
3125− print(' Try running "adb root" to restart the daemon as root')
3126− else:
3127− print(' Try running this script with sudo')
4213+ print(' Try running this script with sudo')
31284214 return False
31294215
31304216 # check sysfs is mounted
3131− res = 'NO (No features of this tool will work!)'
3132− if(sysvals.android):
3133− out = os.popen(sysvals.adb+' shell ls '+\
3134− sysvals.powerfile).read().strip()
3135− if(out == sysvals.powerfile):
3136− res = 'YES'
3137− else:
3138− if(os.path.exists(sysvals.powerfile)):
3139− res = 'YES'
4217+ res = sysvals.colorText('NO (No features of this tool will work!)')
4218+ if(os.path.exists(sysvals.powerfile)):
4219+ res = 'YES'
31404220 print(' is sysfs mounted: %s' % res)
31414221 if(res != 'YES'):
31424222 return False
31434223
31444224 # check target mode is a valid mode
3145− res = 'NO'
3146− modes = getModes()
3147− if(sysvals.suspendmode in modes):
3148− res = 'YES'
3149− else:
3150− status = False
3151− print(' is "%s" a valid power mode: %s' % (sysvals.suspendmode, res))
3152− if(res == 'NO'):
3153− print(' valid power modes are: %s' % modes)
3154− print(' please choose one with -m')
3155−
3156− # check if the tool can unlock the device
3157− if(sysvals.android):
3158− res = 'YES'
3159− out1 = os.popen(sysvals.adb+\
3160− ' shell dumpsys power | grep mScreenOn').read().strip()
3161− out2 = os.popen(sysvals.adb+\
3162− ' shell input').read().strip()
3163− if(not out1.startswith('mScreenOn') or not out2.startswith('usage')):
3164− res = 'NO (wake the android device up before running the test)'
3165− print(' can I unlock the screen: %s' % res)
4225+ if sysvals.suspendmode != 'command':
4226+ res = sysvals.colorText('NO')
4227+ modes = getModes()
4228+ if(sysvals.suspendmode in modes):
4229+ res = 'YES'
4230+ else:
4231+ status = False
4232+ print(' is "%s" a valid power mode: %s' % (sysvals.suspendmode, res))
4233+ if(res == 'NO'):
4234+ print(' valid power modes are: %s' % modes)
4235+ print(' please choose one with -m')
31664236
31674237 # check if ftrace is available
3168− res = 'NO'
3169− ftgood = verifyFtrace()
4238+ res = sysvals.colorText('NO')
4239+ ftgood = sysvals.verifyFtrace()
31704240 if(ftgood):
31714241 res = 'YES'
31724242 elif(sysvals.usecallgraph):
31734243 status = False
31744244 print(' is ftrace supported: %s' % res)
31754245
4246+ # check if kprobes are available
4247+ res = sysvals.colorText('NO')
4248+ sysvals.usekprobes = sysvals.verifyKprobes()
4249+ if(sysvals.usekprobes):
4250+ res = 'YES'
4251+ else:
4252+ sysvals.usedevsrc = False
4253+ print(' are kprobes supported: %s' % res)
4254+
31764255 # what data source are we using
31774256 res = 'DMESG'
31784257 if(ftgood):
@@ -3180,14 +4259,8 @@ def statusCheck(): · 1 unmodified line
31804259 sysvals.usetraceevents = False
31814260 for e in sysvals.traceevents:
31824261 check = False
3183− if(sysvals.android):
3184− out = os.popen(sysvals.adb+' shell ls -d '+\
3185− sysvals.epath+e).read().strip()
3186− if(out == sysvals.epath+e):
3187− check = True
3188− else:
3189− if(os.path.exists(sysvals.epath+e)):
3190− check = True
4262+ if(os.path.exists(sysvals.epath+e)):
4263+ check = True
31914264 if(not check):
31924265 sysvals.usetraceeventsonly = False
31934266 if(e == 'suspend_resume' and check):
@@ -3199,13 +4272,48 @@ def statusCheck(): · 5 unmodified lines
31994272 print(' timeline data source: %s' % res)
32004273
32014274 # check if rtcwake
3202− res = 'NO'
4275+ res = sysvals.colorText('NO')
32034276 if(sysvals.rtcpath != ''):
32044277 res = 'YES'
32054278 elif(sysvals.rtcwake):
32064279 status = False
32074280 print(' is rtcwake supported: %s' % res)
32084281
4282+ if not probecheck:
4283+ return status
4284+
4285+ if (sysvals.usecallgraph and len(sysvals.debugfuncs) > 0) or len(sysvals.kprobes) > 0:
4286+ sysvals.initFtrace(True)
4287+
4288+ # verify callgraph debugfuncs
4289+ if sysvals.usecallgraph and len(sysvals.debugfuncs) > 0:
4290+ print(' verifying these ftrace callgraph functions work:')
4291+ sysvals.setFtraceFilterFunctions(sysvals.debugfuncs)
4292+ fp = open(sysvals.tpath+'set_graph_function', 'r')
4293+ flist = fp.read().split('\n')
4294+ fp.close()
4295+ for func in sysvals.debugfuncs:
4296+ res = sysvals.colorText('NO')
4297+ if func in flist:
4298+ res = 'YES'
4299+ else:
4300+ for i in flist:
4301+ if ' [' in i and func == i.split(' ')[0]:
4302+ res = 'YES'
4303+ break
4304+ print(' %s: %s' % (func, res))
4305+
4306+ # verify kprobes
4307+ if len(sysvals.kprobes) > 0:
4308+ print(' verifying these kprobes work:')
4309+ for name in sorted(sysvals.kprobes):
4310+ if name in sysvals.tracefuncs:
4311+ continue
4312+ res = sysvals.colorText('NO')
4313+ if sysvals.testKprobe(sysvals.kprobes[name]):
4314+ res = 'YES'
4315+ print(' %s: %s' % (name, res))
4316+
32094317 return status
32104318
32114319 # Function: doError
@@ -3226,7 +4334,7 @@ def doError(msg, help): · 14 unmodified lines
32264334 # Arguments:
32274335 # msg: the warning message to print
32284336 # file: If not empty, a filename to request be sent to the owner for debug
3229−def doWarning(msg, file):
4337+def doWarning(msg, file=''):
32304338 print('/* %s */') % msg
32314339 if(file):
32324340 print('/* For a fix, please send this'+\
@@ -3235,18 +4343,25 @@ def doWarning(msg, file): · 2 unmodified lines
32354343 # Function: rootCheck
32364344 # Description:
32374345 # quick check to see if we have root access
3238−def rootCheck():
3239− if(os.environ['USER'] != 'root'):
3240− doError('This script must be run as root', False)
4346+def rootCheck(fatal):
4347+ global sysvals
4348+ if(os.access(sysvals.powerfile, os.W_OK)):
4349+ return True
4350+ if fatal:
4351+ doError('This command must be run as root', False)
4352+ return False
32414353
32424354 # Function: getArgInt
32434355 # Description:
32444356 # pull out an integer argument from the command line with checks
3245−def getArgInt(name, args, min, max):
3246− try:
3247− arg = args.next()
3248− except:
3249− doError(name+': no argument supplied', True)
4357+def getArgInt(name, args, min, max, main=True):
4358+ if main:
4359+ try:
4360+ arg = args.next()
4361+ except:
4362+ doError(name+': no argument supplied', True)
4363+ else:
4364+ arg = args
32504365 try:
32514366 val = int(arg)
32524367 except:
@@ -3255,6 +4370,25 @@ def getArgInt(name, args, min, max): · 2 unmodified lines
32554370 doError(name+': value should be between %d and %d' % (min, max), True)
32564371 return val
32574372
4373+# Function: getArgFloat
4374+# Description:
4375+# pull out a float argument from the command line with checks
4376+def getArgFloat(name, args, min, max, main=True):
4377+ if main:
4378+ try:
4379+ arg = args.next()
4380+ except:
4381+ doError(name+': no argument supplied', True)
4382+ else:
4383+ arg = args
4384+ try:
4385+ val = float(arg)
4386+ except:
4387+ doError(name+': non-numerical value given', True)
4388+ if(val < min or val > max):
4389+ doError(name+': value should be between %f and %f' % (min, max), True)
4390+ return val
4391+
32584392 # Function: rerunTest
32594393 # Description:
32604394 # generate an output from an existing set of ftrace/dmesg logs
@@ -3282,15 +4416,12 @@ def rerunTest(): · 21 unmodified lines
32824416 # Function: runTest
32834417 # Description:
32844418 # execute a suspend/resume, gather the logs, and generate the output
3285−def runTest(subdir):
4419+def runTest(subdir, testpath=''):
32864420 global sysvals
32874421
32884422 # prepare for the test
3289− if(not sysvals.android):
3290− initFtrace()
3291− else:
3292− initFtraceAndroid()
3293− sysvals.initTestOutput(subdir)
4423+ sysvals.initFtrace()
4424+ sysvals.initTestOutput(subdir, testpath)
32944425
32954426 vprint('Output files:\n %s' % sysvals.dmesgfile)
32964427 if(sysvals.usecallgraph or
@@ -3300,10 +4431,8 @@ def runTest(subdir): · 3 unmodified lines
33004431 vprint(' %s' % sysvals.htmlfile)
33014432
33024433 # execute the test
3303− if(not sysvals.android):
3304− executeSuspend()
3305− else:
3306− executeAndroidSuspend()
4434+ executeSuspend()
4435+ sysvals.cleanupFtrace()
33074436
33084437 # analyze the data and create the html output
33094438 print('PROCESSING DATA')
@@ -3367,6 +4496,153 @@ def runSummary(subdir, output): · 57 unmodified lines
33674496
33684497 createHTMLSummarySimple(testruns, subdir+'/summary.html')
33694498
4499+# Function: checkArgBool
4500+# Description:
4501+# check if a boolean string value is true or false
4502+def checkArgBool(value):
4503+ yes = ['1', 'true', 'yes', 'on']
4504+ if value.lower() in yes:
4505+ return True
4506+ return False
4507+
4508+# Function: configFromFile
4509+# Description:
4510+# Configure the script via the info in a config file
4511+def configFromFile(file):
4512+ global sysvals
4513+ Config = ConfigParser.ConfigParser()
4514+
4515+ ignorekprobes = False
4516+ Config.read(file)
4517+ sections = Config.sections()
4518+ if 'Settings' in sections:
4519+ for opt in Config.options('Settings'):
4520+ value = Config.get('Settings', opt).lower()
4521+ if(opt.lower() == 'verbose'):
4522+ sysvals.verbose = checkArgBool(value)
4523+ elif(opt.lower() == 'addlogs'):
4524+ sysvals.addlogs = checkArgBool(value)
4525+ elif(opt.lower() == 'dev'):
4526+ sysvals.usedevsrc = checkArgBool(value)
4527+ elif(opt.lower() == 'ignorekprobes'):
4528+ ignorekprobes = checkArgBool(value)
4529+ elif(opt.lower() == 'x2'):
4530+ if checkArgBool(value):
4531+ sysvals.execcount = 2
4532+ elif(opt.lower() == 'callgraph'):
4533+ sysvals.usecallgraph = checkArgBool(value)
4534+ elif(opt.lower() == 'callgraphfunc'):
4535+ sysvals.debugfuncs = []
4536+ if value:
4537+ value = value.split(',')
4538+ for i in value:
4539+ sysvals.debugfuncs.append(i.strip())
4540+ elif(opt.lower() == 'expandcg'):
4541+ sysvals.cgexp = checkArgBool(value)
4542+ elif(opt.lower() == 'srgap'):
4543+ if checkArgBool(value):
4544+ sysvals.srgap = 5
4545+ elif(opt.lower() == 'mode'):
4546+ sysvals.suspendmode = value
4547+ elif(opt.lower() == 'command'):
4548+ sysvals.testcommand = value
4549+ elif(opt.lower() == 'x2delay'):
4550+ sysvals.x2delay = getArgInt('-x2delay', value, 0, 60000, False)
4551+ elif(opt.lower() == 'postres'):
4552+ sysvals.postresumetime = getArgInt('-postres', value, 0, 3600, False)
4553+ elif(opt.lower() == 'rtcwake'):
4554+ sysvals.rtcwake = True
4555+ sysvals.rtcwaketime = getArgInt('-rtcwake', value, 0, 3600, False)
4556+ elif(opt.lower() == 'timeprec'):
4557+ sysvals.setPrecision(getArgInt('-timeprec', value, 0, 6, False))
4558+ elif(opt.lower() == 'mindev'):
4559+ sysvals.mindevlen = getArgFloat('-mindev', value, 0.0, 10000.0, False)
4560+ elif(opt.lower() == 'mincg'):
4561+ sysvals.mincglen = getArgFloat('-mincg', value, 0.0, 10000.0, False)
4562+ elif(opt.lower() == 'kprobecolor'):
4563+ try:
4564+ val = int(value, 16)
4565+ sysvals.kprobecolor = '#'+value
4566+ except:
4567+ sysvals.kprobecolor = value
4568+ elif(opt.lower() == 'synccolor'):
4569+ try:
4570+ val = int(value, 16)
4571+ sysvals.synccolor = '#'+value
4572+ except:
4573+ sysvals.synccolor = value
4574+ elif(opt.lower() == 'output-dir'):
4575+ args = dict()
4576+ n = datetime.now()
4577+ args['date'] = n.strftime('%y%m%d')
4578+ args['time'] = n.strftime('%H%M%S')
4579+ args['hostname'] = sysvals.hostname
4580+ sysvals.outdir = value.format(**args)
4581+
4582+ if sysvals.suspendmode == 'command' and not sysvals.testcommand:
4583+ doError('No command supplied for mode "command"', False)
4584+ if sysvals.usedevsrc and sysvals.usecallgraph:
4585+ doError('dev and callgraph cannot both be true', False)
4586+ if sysvals.usecallgraph and sysvals.execcount > 1:
4587+ doError('-x2 is not compatible with -f', False)
4588+
4589+ if ignorekprobes:
4590+ return
4591+
4592+ kprobes = dict()
4593+ archkprobe = 'Kprobe_'+platform.machine()
4594+ if archkprobe in sections:
4595+ for name in Config.options(archkprobe):
4596+ kprobes[name] = Config.get(archkprobe, name)
4597+ if 'Kprobe' in sections:
4598+ for name in Config.options('Kprobe'):
4599+ kprobes[name] = Config.get('Kprobe', name)
4600+
4601+ for name in kprobes:
4602+ function = name
4603+ format = name
4604+ color = ''
4605+ args = dict()
4606+ data = kprobes[name].split()
4607+ i = 0
4608+ for val in data:
4609+ # bracketted strings are special formatting, read them separately
4610+ if val[0] == '[' and val[-1] == ']':
4611+ for prop in val[1:-1].split(','):
4612+ p = prop.split('=')
4613+ if p[0] == 'color':
4614+ try:
4615+ color = int(p[1], 16)
4616+ color = '#'+p[1]
4617+ except:
4618+ color = p[1]
4619+ continue
4620+ # first real arg should be the format string
4621+ if i == 0:
4622+ format = val
4623+ # all other args are actual function args
4624+ else:
4625+ d = val.split('=')
4626+ args[d[0]] = d[1]
4627+ i += 1
4628+ if not function or not format:
4629+ doError('Invalid kprobe: %s' % name, False)
4630+ for arg in re.findall('{(?P<n>[a-z,A-Z,0-9]*)}', format):
4631+ if arg not in args:
4632+ doError('Kprobe "%s" is missing argument "%s"' % (name, arg), False)
4633+ if name in sysvals.kprobes:
4634+ doError('Duplicate kprobe found "%s"' % (name), False)
4635+ vprint('Adding KPROBE: %s %s %s %s' % (name, function, format, args))
4636+ sysvals.kprobes[name] = {
4637+ 'name': name,
4638+ 'func': function,
4639+ 'format': format,
4640+ 'args': args,
4641+ 'mask': re.sub('{(?P<n>[a-z,A-Z,0-9]*)}', '.*', format)
4642+ }
4643+ if color:
4644+ sysvals.kprobes[name]['color'] = color
4645+
33704646 # Function: printHelp
33714647 # Description:
33724648 # print out the help text
@@ -3375,7 +4651,7 @@ def printHelp(): · 2 unmodified lines
33754651 modes = getModes()
33764652
33774653 print('')
3378− print('AnalyzeSuspend v%.1f' % sysvals.version)
4654+ print('AnalyzeSuspend v%s' % sysvals.version)
33794655 print('Usage: sudo analyze_suspend.py <options>')
33804656 print('')
33814657 print('Description:')
@@ -3396,27 +4672,38 @@ def printHelp(): · 14 unmodified lines
33964672 print(' [general]')
33974673 print(' -h Print this help text')
33984674 print(' -v Print the current tool version')
4675+ print(' -config file Pull arguments and config options from a file')
33994676 print(' -verbose Print extra information during execution and analysis')
34004677 print(' -status Test to see if the system is enabled to run this tool')
34014678 print(' -modes List available suspend modes')
34024679 print(' -m mode Mode to initiate for suspend %s (default: %s)') % (modes, sysvals.suspendmode)
3403− print(' -rtcwake t Use rtcwake to autoresume after <t> seconds (default: disabled)')
4680+ print(' -o subdir Override the output subdirectory')
34044681 print(' [advanced]')
4682+ print(' -rtcwake t Use rtcwake to autoresume after <t> seconds (default: disabled)')
4683+ print(' -addlogs Add the dmesg and ftrace logs to the html output')
4684+ print(' -multi n d Execute <n> consecutive tests at <d> seconds intervals. The outputs will')
4685+ print(' be created in a new subdirectory with a summary page.')
4686+ print(' -srgap Add a visible gap in the timeline between sus/res (default: disabled)')
4687+ print(' -cmd {s} Instead of suspend/resume, run a command, e.g. "sync -d"')
4688+ print(' -mindev ms Discard all device blocks shorter than ms milliseconds (e.g. 0.001 for us)')
4689+ print(' -mincg ms Discard all callgraphs shorter than ms milliseconds (e.g. 0.001 for us)')
4690+ print(' -timeprec N Number of significant digits in timestamps (0:S, [3:ms], 6:us)')
4691+ print(' [debug]')
34054692 print(' -f Use ftrace to create device callgraphs (default: disabled)')
3406− print(' -filter "d1 d2 ..." Filter out all but this list of dev names')
4693+ print(' -expandcg pre-expand the callgraph data in the html output (default: disabled)')
4694+ print(' -flist Print the list of functions currently being captured in ftrace')
4695+ print(' -flistall Print all functions capable of being captured in ftrace')
4696+ print(' -fadd file Add functions to be graphed in the timeline from a list in a text file')
4697+ print(' -filter "d1 d2 ..." Filter out all but this list of device names')
4698+ print(' -dev Display common low level functions in the timeline')
4699+ print(' [post-resume task analysis]')
34074700 print(' -x2 Run two suspend/resumes back to back (default: disabled)')
34084701 print(' -x2delay t Minimum millisecond delay <t> between the two test runs (default: 0 ms)')
34094702 print(' -postres t Time after resume completion to wait for post-resume events (default: 0 S)')
3410− print(' -multi n d Execute <n> consecutive tests at <d> seconds intervals. The outputs will')
3411− print(' be created in a new subdirectory with a summary page.')
34124703 print(' [utilities]')
34134704 print(' -fpdt Print out the contents of the ACPI Firmware Performance Data Table')
34144705 print(' -usbtopo Print out the current USB topology with power info')
34154706 print(' -usbauto Enable autosuspend for all connected USB devices')
3416− print(' [android testing]')
3417− print(' -adb binary Use the given adb binary to run the test on an android device.')
3418− print(' The device should already be connected and with root access.')
3419− print(' Commands will be executed on the device using "adb shell"')
34204707 print(' [re-analyze data from previous runs]')
34214708 print(' -ftrace ftracefile Create HTML output using ftrace input')
34224709 print(' -dmesg dmesgfile Create HTML output using dmesg (not needed for kernel >= 3.15)')
@@ -3430,6 +4717,7 @@ if __name__ == '__main__': · 7 unmodified lines
34304717 cmd = ''
34314718 cmdarg = ''
34324719 multitest = {'run': False, 'count': 0, 'delay': 0}
4720+ simplecmds = ['-modes', '-fpdt', '-flist', '-flistall', '-usbtopo', '-usbauto', '-status']
34334721 # loop through the command line arguments
34344722 args = iter(sys.argv[1:])
34354723 for arg in args:
@@ -3438,58 +4726,85 @@ if __name__ == '__main__': · 2 unmodified lines
34384726 val = args.next()
34394727 except:
34404728 doError('No mode supplied', True)
4729+ if val == 'command' and not sysvals.testcommand:
4730+ doError('No command supplied for mode "command"', True)
34414731 sysvals.suspendmode = val
3442− elif(arg == '-adb'):
3443− try:
3444− val = args.next()
3445− except:
3446− doError('No adb binary supplied', True)
3447− if(not os.path.exists(val)):
3448− doError('file doesnt exist: %s' % val, False)
3449− if(not os.access(val, os.X_OK)):
3450− doError('file isnt executable: %s' % val, False)
3451− try:
3452− check = os.popen(val+' version').read().strip()
3453− except:
3454− doError('adb version failed to execute', False)
3455− if(not re.match('Android Debug Bridge .*', check)):
3456− doError('adb version failed to execute', False)
3457− sysvals.adb = val
3458− sysvals.android = True
4732+ elif(arg in simplecmds):
4733+ cmd = arg[1:]
4734+ elif(arg == '-h'):
4735+ printHelp()
4736+ sys.exit()
4737+ elif(arg == '-v'):
4738+ print("Version %s" % sysvals.version)
4739+ sys.exit()
34594740 elif(arg == '-x2'):
3460− if(sysvals.postresumetime > 0):
3461− doError('-x2 is not compatible with -postres', False)
34624741 sysvals.execcount = 2
4742+ if(sysvals.usecallgraph):
4743+ doError('-x2 is not compatible with -f', False)
34634744 elif(arg == '-x2delay'):
34644745 sysvals.x2delay = getArgInt('-x2delay', args, 0, 60000)
34654746 elif(arg == '-postres'):
3466− if(sysvals.execcount != 1):
3467− doError('-x2 is not compatible with -postres', False)
34684747 sysvals.postresumetime = getArgInt('-postres', args, 0, 3600)
34694748 elif(arg == '-f'):
34704749 sysvals.usecallgraph = True
3471− elif(arg == '-modes'):
3472− cmd = 'modes'
3473− elif(arg == '-fpdt'):
3474− cmd = 'fpdt'
3475− elif(arg == '-usbtopo'):
3476− cmd = 'usbtopo'
3477− elif(arg == '-usbauto'):
3478− cmd = 'usbauto'
3479− elif(arg == '-status'):
3480− cmd = 'status'
4750+ if(sysvals.execcount > 1):
4751+ doError('-x2 is not compatible with -f', False)
4752+ if(sysvals.usedevsrc):
4753+ doError('-dev is not compatible with -f', False)
4754+ elif(arg == '-addlogs'):
4755+ sysvals.addlogs = True
34814756 elif(arg == '-verbose'):
34824757 sysvals.verbose = True
3483− elif(arg == '-v'):
3484− print("Version %.1f" % sysvals.version)
3485− sys.exit()
4758+ elif(arg == '-dev'):
4759+ sysvals.usedevsrc = True
4760+ if(sysvals.usecallgraph):
4761+ doError('-dev is not compatible with -f', False)
34864762 elif(arg == '-rtcwake'):
34874763 sysvals.rtcwake = True
34884764 sysvals.rtcwaketime = getArgInt('-rtcwake', args, 0, 3600)
4765+ elif(arg == '-timeprec'):
4766+ sysvals.setPrecision(getArgInt('-timeprec', args, 0, 6))
4767+ elif(arg == '-mindev'):
4768+ sysvals.mindevlen = getArgFloat('-mindev', args, 0.0, 10000.0)
4769+ elif(arg == '-mincg'):
4770+ sysvals.mincglen = getArgFloat('-mincg', args, 0.0, 10000.0)
4771+ elif(arg == '-cmd'):
4772+ try:
4773+ val = args.next()
4774+ except:
4775+ doError('No command string supplied', True)
4776+ sysvals.testcommand = val
4777+ sysvals.suspendmode = 'command'
4778+ elif(arg == '-expandcg'):
4779+ sysvals.cgexp = True
4780+ elif(arg == '-srgap'):
4781+ sysvals.srgap = 5
34894782 elif(arg == '-multi'):
34904783 multitest['run'] = True
34914784 multitest['count'] = getArgInt('-multi n (exec count)', args, 2, 1000000)
34924785 multitest['delay'] = getArgInt('-multi d (delay between tests)', args, 0, 3600)
4786+ elif(arg == '-o'):
4787+ try:
4788+ val = args.next()
4789+ except:
4790+ doError('No subdirectory name supplied', True)
4791+ sysvals.outdir = val
4792+ elif(arg == '-config'):
4793+ try:
4794+ val = args.next()
4795+ except:
4796+ doError('No text file supplied', True)
4797+ if(os.path.exists(val) == False):
4798+ doError('%s does not exist' % val, False)
4799+ configFromFile(val)
4800+ elif(arg == '-fadd'):
4801+ try:
4802+ val = args.next()
4803+ except:
4804+ doError('No text file supplied', True)
4805+ if(os.path.exists(val) == False):
4806+ doError('%s does not exist' % val, False)
4807+ sysvals.addFtraceFilterFunctions(val)
34934808 elif(arg == '-dmesg'):
34944809 try:
34954810 val = args.next()
@@ -3498,17 +4813,16 @@ if __name__ == '__main__': · 2 unmodified lines
34984813 sysvals.notestrun = True
34994814 sysvals.dmesgfile = val
35004815 if(os.path.exists(sysvals.dmesgfile) == False):
3501− doError('%s doesnt exist' % sysvals.dmesgfile, False)
4816+ doError('%s does not exist' % sysvals.dmesgfile, False)
35024817 elif(arg == '-ftrace'):
35034818 try:
35044819 val = args.next()
35054820 except:
35064821 doError('No ftrace file supplied', True)
35074822 sysvals.notestrun = True
3508− sysvals.usecallgraph = True
35094823 sysvals.ftracefile = val
35104824 if(os.path.exists(sysvals.ftracefile) == False):
3511− doError('%s doesnt exist' % sysvals.ftracefile, False)
4825+ doError('%s does not exist' % sysvals.ftracefile, False)
35124826 elif(arg == '-summary'):
35134827 try:
35144828 val = args.next()
@@ -3518,35 +4832,35 @@ if __name__ == '__main__': · 3 unmodified lines
35184832 cmdarg = val
35194833 sysvals.notestrun = True
35204834 if(os.path.isdir(val) == False):
3521− doError('%s isnt accesible' % val, False)
4835+ doError('%s is not accesible' % val, False)
35224836 elif(arg == '-filter'):
35234837 try:
35244838 val = args.next()
35254839 except:
35264840 doError('No devnames supplied', True)
35274841 sysvals.setDeviceFilter(val)
3528− elif(arg == '-h'):
3529− printHelp()
3530− sys.exit()
35314842 else:
35324843 doError('Invalid argument: '+arg, True)
35334844
4845+ # callgraph size cannot exceed device size
4846+ if sysvals.mincglen < sysvals.mindevlen:
4847+ sysvals.mincglen = sysvals.mindevlen
4848+
35344849 # just run a utility command and exit
35354850 if(cmd != ''):
35364851 if(cmd == 'status'):
3537− statusCheck()
4852+ statusCheck(True)
35384853 elif(cmd == 'fpdt'):
3539− if(sysvals.android):
3540− doError('cannot read FPDT on android device', False)
35414854 getFPDT(True)
35424855 elif(cmd == 'usbtopo'):
3543− if(sysvals.android):
3544− doError('cannot read USB topology '+\
3545− 'on an android device', False)
3546− detectUSB(True)
4856+ detectUSB()
35474857 elif(cmd == 'modes'):
35484858 modes = getModes()
35494859 print modes
4860+ elif(cmd == 'flist'):
4861+ sysvals.getFtraceFilterFunctions(True)
4862+ elif(cmd == 'flistall'):
4863+ sysvals.getFtraceFilterFunctions(False)
35504864 elif(cmd == 'usbauto'):
35514865 setUSBDevicesAuto()
35524866 elif(cmd == 'summary'):
@@ -3554,15 +4868,6 @@ if __name__ == '__main__': · 1 unmodified line
35544868 runSummary(cmdarg, True)
35554869 sys.exit()
35564870
3557− # run test on android device
3558− if(sysvals.android):
3559− if(sysvals.usecallgraph):
3560− doError('ftrace (-f) is not yet supported '+\
3561− 'in the android kernel', False)
3562− if(sysvals.notestrun):
3563− doError('cannot analyze test files on the '+\
3564− 'android device', False)
3565−
35664871 # if instructed, re-analyze existing data files
35674872 if(sysvals.notestrun):
35684873 rerunTest()
@@ -3574,18 +4879,20 @@ if __name__ == '__main__': · 5 unmodified lines
35744879 sys.exit()
35754880
35764881 if multitest['run']:
3577− # run multiple tests in a separte subdirectory
4882+ # run multiple tests in a separate subdirectory
35784883 s = 'x%d' % multitest['count']
3579− subdir = datetime.now().strftime('suspend-'+s+'-%m%d%y-%H%M%S')
3580− os.mkdir(subdir)
4884+ if not sysvals.outdir:
4885+ sysvals.outdir = datetime.now().strftime('suspend-'+s+'-%m%d%y-%H%M%S')
4886+ if not os.path.isdir(sysvals.outdir):
4887+ os.mkdir(sysvals.outdir)
35814888 for i in range(multitest['count']):
35824889 if(i != 0):
35834890 print('Waiting %d seconds...' % (multitest['delay']))
35844891 time.sleep(multitest['delay'])
35854892 print('TEST (%d/%d) START' % (i+1, multitest['count']))
3586− runTest(subdir)
4893+ runTest(sysvals.outdir)
35874894 print('TEST (%d/%d) COMPLETE' % (i+1, multitest['count']))
3588− runSummary(subdir, False)
4895+ runSummary(sysvals.outdir, False)
35894896 else:
35904897 # run the test in the current directory
3591− runTest(".")
4898+ runTest('.', sysvals.outdir)

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t