diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2018-06-05 09:38:39 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2018-06-05 09:38:39 -0700 |
commit | 3c89adb0d11117f64d5b501730be7fb2bf53a479 (patch) | |
tree | c259aec20deed6c0a0773cfda3a11f58ec9a077d /tools | |
parent | 11e7c21880617b8383ad8ad059ae9a07e5abe68e (diff) | |
parent | a24e16b1310ce7f474aa0caca0e66f0f174c022f (diff) |
Merge tag 'pm-4.18-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm
Pull power management updates from Rafael Wysocki:
"These include a significant update of the generic power domains
(genpd) and Operating Performance Points (OPP) frameworks, mostly
related to the introduction of power domain performance levels,
cpufreq updates (new driver for Qualcomm Kryo processors, updates of
the existing drivers, some core fixes, schedutil governor
improvements), PCI power management fixes, ACPI workaround for
EC-based wakeup events handling on resume from suspend-to-idle, and
major updates of the turbostat and pm-graph utilities.
Specifics:
- Introduce power domain performance levels into the the generic
power domains (genpd) and Operating Performance Points (OPP)
frameworks (Viresh Kumar, Rajendra Nayak, Dan Carpenter).
- Fix two issues in the runtime PM framework related to the
initialization and removal of devices using device links (Ulf
Hansson).
- Clean up the initialization of drivers for devices in PM domains
(Ulf Hansson, Geert Uytterhoeven).
- Fix a cpufreq core issue related to the policy sysfs interface
causing CPU online to fail for CPUs sharing one cpufreq policy in
some situations (Tao Wang).
- Make it possible to use platform-specific suspend/resume hooks in
the cpufreq-dt driver and make the Armada 37xx DVFS use that
feature (Viresh Kumar, Miquel Raynal).
- Optimize policy transition notifications in cpufreq (Viresh Kumar).
- Improve the iowait boost mechanism in the schedutil cpufreq
governor (Patrick Bellasi).
- Improve the handling of deferred frequency updates in the schedutil
cpufreq governor (Joel Fernandes, Dietmar Eggemann, Rafael Wysocki,
Viresh Kumar).
- Add a new cpufreq driver for Qualcomm Kryo (Ilia Lin).
- Fix and clean up some cpufreq drivers (Colin Ian King, Dmitry
Osipenko, Doug Smythies, Luc Van Oostenryck, Simon Horman, Viresh
Kumar).
- Fix the handling of PCI devices with the DPM_SMART_SUSPEND flag set
and update stale comments in the PCI core PM code (Rafael Wysocki).
- Work around an issue related to the handling of EC-based wakeup
events in the ACPI PM core during resume from suspend-to-idle if
the EC has been put into the low-power mode (Rafael Wysocki).
- Improve the handling of wakeup source objects in the PM core (Doug
Berger, Mahendran Ganesh, Rafael Wysocki).
- Update the driver core to prevent deferred probe from breaking
suspend/resume ordering (Feng Kan).
- Clean up the PM core somewhat (Bjorn Helgaas, Ulf Hansson, Rafael
Wysocki).
- Make the core suspend/resume code and cpufreq support the RT patch
(Sebastian Andrzej Siewior, Thomas Gleixner).
- Consolidate the PM QoS handling in cpuidle governors (Rafael
Wysocki).
- Fix a possible crash in the hibernation core (Tetsuo Handa).
- Update the rockchip-io Adaptive Voltage Scaling (AVS) driver (David
Wu).
- Update the turbostat utility (fixes, cleanups, new CPU IDs, new
command line options, built-in "Low Power Idle" counters support,
new POLL and POLL% columns) and add an entry for it to MAINTAINERS
(Len Brown, Artem Bityutskiy, Chen Yu, Laura Abbott, Matt Turner,
Prarit Bhargava, Srinivas Pandruvada).
- Update the pm-graph to version 5.1 (Todd Brandt).
- Update the intel_pstate_tracer utility (Doug Smythies)"
* tag 'pm-4.18-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm: (128 commits)
tools/power turbostat: update version number
tools/power turbostat: Add Node in output
tools/power turbostat: add node information into turbostat calculations
tools/power turbostat: remove num_ from cpu_topology struct
tools/power turbostat: rename num_cores_per_pkg to num_cores_per_node
tools/power turbostat: track thread ID in cpu_topology
tools/power turbostat: Calculate additional node information for a package
tools/power turbostat: Fix node and siblings lookup data
tools/power turbostat: set max_num_cpus equal to the cpumask length
tools/power turbostat: if --num_iterations, print for specific number of iterations
tools/power turbostat: Add Cannon Lake support
tools/power turbostat: delete duplicate #defines
x86: msr-index.h: Correct SNB_C1/C3_AUTO_UNDEMOTE defines
tools/power turbostat: Correct SNB_C1/C3_AUTO_UNDEMOTE defines
tools/power turbostat: add POLL and POLL% column
tools/power turbostat: Fix --hide Pk%pc10
tools/power turbostat: Build-in "Low Power Idle" counters support
tools/power turbostat: Don't make man pages executable
tools/power turbostat: remove blank lines
tools/power turbostat: a small C-states dump readability immprovement
...
Diffstat (limited to 'tools')
-rwxr-xr-x | tools/power/pm-graph/bootgraph.py | 2 | ||||
-rw-r--r-- | tools/power/pm-graph/sleepgraph.8 | 4 | ||||
-rwxr-xr-x | tools/power/pm-graph/sleepgraph.py | 399 | ||||
-rwxr-xr-x | tools/power/x86/intel_pstate_tracer/intel_pstate_tracer.py | 54 | ||||
-rw-r--r-- | tools/power/x86/turbostat/Makefile | 2 | ||||
-rw-r--r-- | tools/power/x86/turbostat/turbostat.8 | 26 | ||||
-rw-r--r-- | tools/power/x86/turbostat/turbostat.c | 908 | ||||
-rw-r--r-- | tools/power/x86/x86_energy_perf_policy/Makefile | 2 |
8 files changed, 973 insertions, 424 deletions
diff --git a/tools/power/pm-graph/bootgraph.py b/tools/power/pm-graph/bootgraph.py index abb4c38f029b..8ee626c0f6a5 100755 --- a/tools/power/pm-graph/bootgraph.py +++ b/tools/power/pm-graph/bootgraph.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/python2 # # Tool for analyzing boot timing # Copyright (c) 2013, Intel Corporation. diff --git a/tools/power/pm-graph/sleepgraph.8 b/tools/power/pm-graph/sleepgraph.8 index 18baaf6300c9..070be2cf7f74 100644 --- a/tools/power/pm-graph/sleepgraph.8 +++ b/tools/power/pm-graph/sleepgraph.8 @@ -168,6 +168,7 @@ Create a summary page of all tests in \fIindir\fR. Creates summary.html in the current folder. The output page is a table of tests with suspend and resume values sorted by suspend mode, host, and kernel. Includes test averages by mode and links to the test html files. +Use -genhtml to include tests with missing html. .TP \fB-modes\fR List available suspend modes. @@ -179,6 +180,9 @@ with any options you intend to use to see if they will work. \fB-fpdt\fR Print out the contents of the ACPI Firmware Performance Data Table. .TP +\fB-battery\fR +Print out battery status and current charge. +.TP \fB-sysinfo\fR Print out system info extracted from BIOS. Reads /dev/mem directly instead of going through dmidecode. .TP diff --git a/tools/power/pm-graph/sleepgraph.py b/tools/power/pm-graph/sleepgraph.py index 266409fb27ae..0c760478f7d7 100755 --- a/tools/power/pm-graph/sleepgraph.py +++ b/tools/power/pm-graph/sleepgraph.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/python2 # # Tool for analyzing suspend/resume timing # Copyright (c) 2013, Intel Corporation. @@ -69,7 +69,7 @@ from subprocess import call, Popen, PIPE # store system values and test parameters class SystemValues: title = 'SleepGraph' - version = '5.0' + version = '5.1' ansi = False rs = 0 display = 0 @@ -240,7 +240,7 @@ class SystemValues: kprobes = dict() timeformat = '%.3f' cmdline = '%s %s' % \ - (os.path.basename(sys.argv[0]), string.join(sys.argv[1:], ' ')) + (os.path.basename(sys.argv[0]), ' '.join(sys.argv[1:])) def __init__(self): self.archargs = 'args_'+platform.machine() self.hostname = platform.node() @@ -917,12 +917,18 @@ class Data: self.devicegroups.append([phase]) self.errorinfo = {'suspend':[],'resume':[]} def extractErrorInfo(self): + elist = { + 'HWERROR' : '.*\[ *Hardware Error *\].*', + 'FWBUG' : '.*\[ *Firmware Bug *\].*', + 'BUG' : '.*BUG.*', + 'ERROR' : '.*ERROR.*', + 'WARNING' : '.*WARNING.*', + 'IRQ' : '.*genirq: .*', + 'TASKFAIL': '.*Freezing of tasks failed.*', + } lf = sysvals.openlog(sysvals.dmesgfile, 'r') i = 0 list = [] - # sl = start line, et = error time, el = error line - type = 'ERROR' - sl = et = el = -1 for line in lf: i += 1 m = re.match('[ \t]*(\[ *)(?P<ktime>[0-9\.]*)(\]) (?P<msg>.*)', line) @@ -931,43 +937,13 @@ class Data: t = float(m.group('ktime')) if t < self.start or t > self.end: continue - if t < self.tSuspended: - dir = 'suspend' - else: - dir = 'resume' + dir = 'suspend' if t < self.tSuspended else 'resume' msg = m.group('msg') - if re.match('-*\[ *cut here *\]-*', msg): - type = 'WARNING' - sl = i - elif re.match('genirq: .*', msg): - type = 'IRQ' - sl = i - elif re.match('BUG: .*', msg) or re.match('kernel BUG .*', msg): - type = 'BUG' - sl = i - elif re.match('-*\[ *end trace .*\]-*', msg) or \ - re.match('R13: .*', msg): - if et >= 0 and sl >= 0: - list.append((type, dir, et, sl, i)) - self.kerror = True - sl = et = el = -1 - type = 'ERROR' - elif 'Call Trace:' in msg: - if el >= 0 and et >= 0: - list.append((type, dir, et, el, el)) + for err in elist: + if re.match(elist[err], msg): + list.append((err, dir, t, i, i)) self.kerror = True - et, el = t, i - if sl < 0 or type == 'BUG': - slval = i - if sl >= 0: - slval = sl - list.append((type, dir, et, slval, i)) - self.kerror = True - sl = et = el = -1 - type = 'ERROR' - if el >= 0 and et >= 0: - list.append((type, dir, et, el, el)) - self.kerror = True + break for e in list: type, dir, t, idx1, idx2 = e sysvals.vprint('kernel %s found in %s at %f' % (type, dir, t)) @@ -2331,12 +2307,14 @@ class TestProps: sv.suspendmode = data.stamp['mode'] if sv.suspendmode == 'command' and sv.ftracefile != '': modes = ['on', 'freeze', 'standby', 'mem', 'disk'] - out = Popen(['grep', 'machine_suspend', sv.ftracefile], - stderr=PIPE, stdout=PIPE).stdout.read() - m = re.match('.* machine_suspend\[(?P<mode>.*)\]', out) - if m and m.group('mode') in ['1', '2', '3', '4']: - sv.suspendmode = modes[int(m.group('mode'))] - data.stamp['mode'] = sv.suspendmode + fp = sysvals.openlog(sv.ftracefile, 'r') + for line in fp: + m = re.match('.* machine_suspend\[(?P<mode>.*)\]', line) + if m and m.group('mode') in ['1', '2', '3', '4']: + sv.suspendmode = modes[int(m.group('mode'))] + data.stamp['mode'] = sv.suspendmode + break + fp.close() m = re.match(self.cmdlinefmt, self.cmdline) if m: sv.cmdline = m.group('cmd') @@ -2413,7 +2391,7 @@ class ProcessMonitor: # markers, and/or kprobes required for primary parsing. def doesTraceLogHaveTraceEvents(): kpcheck = ['_cal: (', '_cpu_down()'] - techeck = sysvals.traceevents[:] + techeck = ['suspend_resume'] tmcheck = ['SUSPEND START', 'RESUME COMPLETE'] sysvals.usekprobes = False fp = sysvals.openlog(sysvals.ftracefile, 'r') @@ -2808,7 +2786,7 @@ def parseTraceLog(live=False): # -- phase changes -- # start of kernel suspend if(re.match('suspend_enter\[.*', t.name)): - if(isbegin): + if(isbegin and data.start == data.tKernSus): data.dmesg[phase]['start'] = t.time data.tKernSus = t.time continue @@ -3072,13 +3050,20 @@ def parseTraceLog(live=False): sysvals.vprint('Callgraph found for task %d: %.3fms, %s' % (cg.pid, (cg.end - cg.start)*1000, name)) cg.newActionFromFunction(data) if sysvals.suspendmode == 'command': - return testdata + return (testdata, '') # fill in any missing phases + error = [] for data in testdata: + tn = '' if len(testdata) == 1 else ('%d' % (data.testnumber + 1)) + terr = '' lp = data.phases[0] for p in data.phases: if(data.dmesg[p]['start'] < 0 and data.dmesg[p]['end'] < 0): + if not terr: + print 'TEST%s FAILED: %s failed in %s phase' % (tn, sysvals.suspendmode, lp) + terr = '%s%s failed in %s phase' % (sysvals.suspendmode, tn, lp) + error.append(terr) sysvals.vprint('WARNING: phase "%s" is missing!' % p) if(data.dmesg[p]['start'] < 0): data.dmesg[p]['start'] = data.dmesg[lp]['end'] @@ -3106,7 +3091,7 @@ def parseTraceLog(live=False): for j in range(i + 1, tc): testdata[j].mergeOverlapDevices(devlist) testdata[0].stitchTouchingThreads(testdata[1:]) - return testdata + return (testdata, ', '.join(error)) # Function: loadKernelLog # Description: @@ -3173,7 +3158,7 @@ def loadKernelLog(): if data: testruns.append(data) if len(testruns) < 1: - doError(' dmesg log has no suspend/resume data: %s' \ + print('ERROR: dmesg log has no suspend/resume data: %s' \ % sysvals.dmesgfile) # fix lines with same timestamp/function with the call and return swapped @@ -3521,68 +3506,144 @@ def createHTMLSummarySimple(testruns, htmlfile, folder): .summary {border:1px solid;}\n\ th {border: 1px solid black;background:#222;color:white;}\n\ td {font: 16px "Times New Roman";text-align: center;}\n\ - tr.alt td {background:#ddd;}\n\ - tr.avg td {background:#aaa;}\n\ + tr.head td {border: 1px solid black;background:#aaa;}\n\ + tr.alt {background-color:#ddd;}\n\ + tr.notice {color:red;}\n\ + .minval {background-color:#BBFFBB;}\n\ + .medval {background-color:#BBBBFF;}\n\ + .maxval {background-color:#FFBBBB;}\n\ + .head a {color:#000;text-decoration: none;}\n\ </style>\n</head>\n<body>\n' + # extract the test data into list + list = dict() + tAvg, tMin, tMax, tMed = [0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [[], []] + iMin, iMed, iMax = [0, 0], [0, 0], [0, 0] + num = 0 + lastmode = '' + cnt = {'pass':0, 'fail':0, 'hang':0} + for data in sorted(testruns, key=lambda v:(v['mode'], v['host'], v['kernel'], v['time'])): + mode = data['mode'] + if mode not in list: + list[mode] = {'data': [], 'avg': [0,0], 'min': [0,0], 'max': [0,0], 'med': [0,0]} + if lastmode and lastmode != mode and num > 0: + for i in range(2): + s = sorted(tMed[i]) + list[lastmode]['med'][i] = s[int(len(s)/2)] + iMed[i] = tMed[i].index(list[lastmode]['med'][i]) + list[lastmode]['avg'] = [tAvg[0] / num, tAvg[1] / num] + list[lastmode]['min'] = tMin + list[lastmode]['max'] = tMax + list[lastmode]['idx'] = (iMin, iMed, iMax) + tAvg, tMin, tMax, tMed = [0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [[], []] + iMin, iMed, iMax = [0, 0], [0, 0], [0, 0] + num = 0 + tVal = [float(data['suspend']), float(data['resume'])] + list[mode]['data'].append([data['host'], data['kernel'], + data['time'], tVal[0], tVal[1], data['url'], data['result'], + data['issues']]) + idx = len(list[mode]['data']) - 1 + if data['result'] == 'pass': + cnt['pass'] += 1 + for i in range(2): + tMed[i].append(tVal[i]) + tAvg[i] += tVal[i] + if tMin[i] == 0 or tVal[i] < tMin[i]: + iMin[i] = idx + tMin[i] = tVal[i] + if tMax[i] == 0 or tVal[i] > tMax[i]: + iMax[i] = idx + tMax[i] = tVal[i] + num += 1 + elif data['result'] == 'hang': + cnt['hang'] += 1 + elif data['result'] == 'fail': + cnt['fail'] += 1 + lastmode = mode + if lastmode and num > 0: + for i in range(2): + s = sorted(tMed[i]) + list[lastmode]['med'][i] = s[int(len(s)/2)] + iMed[i] = tMed[i].index(list[lastmode]['med'][i]) + list[lastmode]['avg'] = [tAvg[0] / num, tAvg[1] / num] + list[lastmode]['min'] = tMin + list[lastmode]['max'] = tMax + list[lastmode]['idx'] = (iMin, iMed, iMax) + # group test header - html += '<div class="stamp">%s (%d tests)</div>\n' % (folder, len(testruns)) + desc = [] + for ilk in sorted(cnt, reverse=True): + if cnt[ilk] > 0: + desc.append('%d %s' % (cnt[ilk], ilk)) + html += '<div class="stamp">%s (%d tests: %s)</div>\n' % (folder, len(testruns), ', '.join(desc)) th = '\t<th>{0}</th>\n' td = '\t<td>{0}</td>\n' + tdh = '\t<td{1}>{0}</td>\n' tdlink = '\t<td><a href="{0}">html</a></td>\n' # table header html += '<table class="summary">\n<tr>\n' + th.format('#') +\ th.format('Mode') + th.format('Host') + th.format('Kernel') +\ - th.format('Test Time') + th.format('Suspend') + th.format('Resume') +\ - th.format('Detail') + '</tr>\n' - - # test data, 1 row per test - avg = '<tr class="avg"><td></td><td></td><td></td><td></td>'+\ - '<td>Average of {0} {1} tests</td><td>{2}</td><td>{3}</td><td></td></tr>\n' - sTimeAvg = rTimeAvg = 0.0 - mode = '' - num = 0 - for data in sorted(testruns, key=lambda v:(v['mode'], v['host'], v['kernel'], v['time'])): - if mode != data['mode']: - # test average line - if(num > 0): - sTimeAvg /= (num - 1) - rTimeAvg /= (num - 1) - html += avg.format('%d' % (num - 1), mode, - '%3.3f ms' % sTimeAvg, '%3.3f ms' % rTimeAvg) - sTimeAvg = rTimeAvg = 0.0 - mode = data['mode'] - num = 1 - # alternate row color - if num % 2 == 1: - html += '<tr class="alt">\n' + th.format('Test Time') + th.format('Result') + th.format('Issues') +\ + th.format('Suspend') + th.format('Resume') + th.format('Detail') + '</tr>\n' + + # export list into html + head = '<tr class="head"><td>{0}</td><td>{1}</td>'+\ + '<td colspan=8 class="sus">Suspend Avg={2} '+\ + '<span class=minval><a href="#s{10}min">Min={3}</a></span> '+\ + '<span class=medval><a href="#s{10}med">Med={4}</a></span> '+\ + '<span class=maxval><a href="#s{10}max">Max={5}</a></span> '+\ + 'Resume Avg={6} '+\ + '<span class=minval><a href="#r{10}min">Min={7}</a></span> '+\ + '<span class=medval><a href="#r{10}med">Med={8}</a></span> '+\ + '<span class=maxval><a href="#r{10}max">Max={9}</a></span></td>'+\ + '</tr>\n' + headnone = '<tr class="head"><td>{0}</td><td>{1}</td><td colspan=8></td></tr>\n' + for mode in list: + # header line for each suspend mode + num = 0 + tAvg, tMin, tMax, tMed = list[mode]['avg'], list[mode]['min'],\ + list[mode]['max'], list[mode]['med'] + count = len(list[mode]['data']) + if 'idx' in list[mode]: + iMin, iMed, iMax = list[mode]['idx'] + html += head.format('%d' % count, mode.upper(), + '%.3f' % tAvg[0], '%.3f' % tMin[0], '%.3f' % tMed[0], '%.3f' % tMax[0], + '%.3f' % tAvg[1], '%.3f' % tMin[1], '%.3f' % tMed[1], '%.3f' % tMax[1], + mode.lower() + ) else: - html += '<tr>\n' - html += td.format("%d" % num) - num += 1 - # basic info - for item in ['mode', 'host', 'kernel', 'time']: - val = "unknown" - if(item in data): - val = data[item] - html += td.format(val) - # suspend time - sTime = float(data['suspend']) - sTimeAvg += sTime - html += td.format('%.3f ms' % sTime) - # resume time - rTime = float(data['resume']) - rTimeAvg += rTime - html += td.format('%.3f ms' % rTime) - # link to the output html - html += tdlink.format(data['url']) + '</tr>\n' - # last test average line - if(num > 0): - sTimeAvg /= (num - 1) - rTimeAvg /= (num - 1) - html += avg.format('%d' % (num - 1), mode, - '%3.3f ms' % sTimeAvg, '%3.3f ms' % rTimeAvg) + iMin = iMed = iMax = [-1, -1, -1] + html += headnone.format('%d' % count, mode.upper()) + for d in list[mode]['data']: + # row classes - alternate row color + rcls = ['alt'] if num % 2 == 1 else [] + if d[6] != 'pass': + rcls.append('notice') + html += '<tr class="'+(' '.join(rcls))+'">\n' if len(rcls) > 0 else '<tr>\n' + # figure out if the line has sus or res highlighted + idx = list[mode]['data'].index(d) + tHigh = ['', ''] + for i in range(2): + tag = 's%s' % mode if i == 0 else 'r%s' % mode + if idx == iMin[i]: + tHigh[i] = ' id="%smin" class=minval title="Minimum"' % tag + elif idx == iMax[i]: + tHigh[i] = ' id="%smax" class=maxval title="Maximum"' % tag + elif idx == iMed[i]: + tHigh[i] = ' id="%smed" class=medval title="Median"' % tag + html += td.format("%d" % (list[mode]['data'].index(d) + 1)) # row + html += td.format(mode) # mode + html += td.format(d[0]) # host + html += td.format(d[1]) # kernel + html += td.format(d[2]) # time + html += td.format(d[6]) # result + html += td.format(d[7]) # issues + html += tdh.format('%.3f ms' % d[3], tHigh[0]) if d[3] else td.format('') # suspend + html += tdh.format('%.3f ms' % d[4], tHigh[1]) if d[4] else td.format('') # resume + html += tdlink.format(d[5]) if d[5] else td.format('') # url + html += '</tr>\n' + num += 1 # flush the data to file hf = open(htmlfile, 'w') @@ -3607,7 +3668,7 @@ def ordinal(value): # testruns: array of Data objects from parseKernelLog or parseTraceLog # Output: # True if the html file was created, false if it failed -def createHTML(testruns): +def createHTML(testruns, testfail): if len(testruns) < 1: print('ERROR: Not enough test data to build a timeline') return @@ -3641,6 +3702,7 @@ def createHTML(testruns): '<td class="purple">{4}Firmware Resume: {2} ms</td>'\ '<td class="yellow" title="time from firmware mode to return from kernel enter_state({5}) [kernel time only]">{4}Kernel Resume: {3} ms</td>'\ '</tr>\n</table>\n' + html_fail = '<table class="testfail"><tr><td>{0}</td></tr></table>\n' # html format variables scaleH = 20 @@ -3708,6 +3770,9 @@ def createHTML(testruns): resume_time, testdesc, stitle, rtitle) devtl.html += thtml + if testfail: + devtl.html += html_fail.format(testfail) + # time scale for potentially multiple datasets t0 = testruns[0].start tMax = testruns[-1].end @@ -4006,6 +4071,7 @@ def addCSS(hf, sv, testcount=1, kerror=False, extra=''): .blue {background:rgba(169,208,245,0.4);}\n\ .time1 {font:22px Arial;border:1px solid;}\n\ .time2 {font:15px Arial;border-bottom:1px solid;border-left:1px solid;border-right:1px solid;}\n\ + .testfail {font:bold 22px Arial;color:red;border:1px dashed;}\n\ td {text-align:center;}\n\ r {color:#500000;font:15px Tahoma;}\n\ n {color:#505050;font:15px Tahoma;}\n\ @@ -4927,6 +4993,25 @@ def dmidecode(mempath, fatal=False): count += 1 return out +def getBattery(): + p = '/sys/class/power_supply' + bat = dict() + for d in os.listdir(p): + type = sysvals.getVal(os.path.join(p, d, 'type')).strip().lower() + if type != 'battery': + continue + for v in ['status', 'energy_now', 'capacity_now']: + bat[v] = sysvals.getVal(os.path.join(p, d, v)).strip().lower() + break + ac = True + if 'status' in bat and 'discharging' in bat['status']: + ac = False + charge = 0 + for v in ['energy_now', 'capacity_now']: + if v in bat and bat[v]: + charge = int(bat[v]) + return (ac, charge) + # Function: getFPDT # Description: # Read the acpi bios tables and pull out FPDT, the firmware data @@ -5202,8 +5287,9 @@ def getArgFloat(name, args, min, max, main=True): def processData(live=False): print('PROCESSING DATA') + error = '' if(sysvals.usetraceevents): - testruns = parseTraceLog(live) + testruns, error = parseTraceLog(live) if sysvals.dmesgfile: for data in testruns: data.extractErrorInfo() @@ -5220,15 +5306,18 @@ def processData(live=False): for data in testruns: data.debugPrint() sys.exit() - + if len(testruns) < 1: + return (testruns, {'error': 'timeline generation failed'}) sysvals.vprint('Creating the html timeline (%s)...' % sysvals.htmlfile) - createHTML(testruns) + createHTML(testruns, error) print('DONE') data = testruns[0] stamp = data.stamp stamp['suspend'], stamp['resume'] = data.getTimeValues() if data.fwValid: stamp['fwsuspend'], stamp['fwresume'] = data.fwSuspend, data.fwResume + if error: + stamp['error'] = error return (testruns, stamp) # Function: rerunTest @@ -5268,58 +5357,88 @@ def runTest(n=0): sysvals.sudouser(sysvals.testdir) sysvals.outputResult(stamp, n) -def find_in_html(html, strs, div=False): - for str in strs: - l = len(str) - i = html.find(str) - if i >= 0: +def find_in_html(html, start, end, firstonly=True): + n, out = 0, [] + while n < len(html): + m = re.search(start, html[n:]) + if not m: break - if i < 0: - return '' - if not div: - return re.search(r'[-+]?\d*\.\d+|\d+', html[i+l:i+l+50]).group() - n = html[i+l:].find('</div>') - if n < 0: + i = m.end() + m = re.search(end, html[n+i:]) + if not m: + break + j = m.start() + str = html[n+i:n+i+j] + if end == 'ms': + num = re.search(r'[-+]?\d*\.\d+|\d+', str) + str = num.group() if num else 'NaN' + if firstonly: + return str + out.append(str) + n += i+j + if firstonly: return '' - return html[i+l:i+l+n] + return out # Function: runSummary # Description: # create a summary of tests in a sub-directory -def runSummary(subdir, local=True): +def runSummary(subdir, local=True, genhtml=False): inpath = os.path.abspath(subdir) outpath = inpath if local: outpath = os.path.abspath('.') print('Generating a summary of folder "%s"' % inpath) + if genhtml: + for dirname, dirnames, filenames in os.walk(subdir): + sysvals.dmesgfile = sysvals.ftracefile = sysvals.htmlfile = '' + for filename in filenames: + if(re.match('.*_dmesg.txt', filename)): + sysvals.dmesgfile = os.path.join(dirname, filename) + elif(re.match('.*_ftrace.txt', filename)): + sysvals.ftracefile = os.path.join(dirname, filename) + sysvals.setOutputFile() + if sysvals.ftracefile and sysvals.htmlfile and \ + not os.path.exists(sysvals.htmlfile): + print('FTRACE: %s' % sysvals.ftracefile) + if sysvals.dmesgfile: + print('DMESG : %s' % sysvals.dmesgfile) + rerunTest() testruns = [] for dirname, dirnames, filenames in os.walk(subdir): for filename in filenames: if(not re.match('.*.html', filename)): continue file = os.path.join(dirname, filename) - html = open(file, 'r').read(10000) - suspend = find_in_html(html, - ['Kernel Suspend: ', 'Kernel Suspend Time: ']) - resume = find_in_html(html, - ['Kernel Resume: ', 'Kernel Resume Time: ']) - line = find_in_html(html, ['<div class="stamp">'], True) + html = open(file, 'r').read() + suspend = find_in_html(html, 'Kernel Suspend', 'ms') + resume = find_in_html(html, 'Kernel Resume', 'ms') + line = find_in_html(html, '<div class="stamp">', '</div>') stmp = line.split() - if not suspend or not resume or len(stmp) < 4: + if not suspend or not resume or len(stmp) != 8: continue + try: + dt = datetime.strptime(' '.join(stmp[3:]), '%B %d %Y, %I:%M:%S %p') + except: + continue + tstr = dt.strftime('%Y/%m/%d %H:%M:%S') + error = find_in_html(html, '<table class="testfail"><tr><td>', '</td>') + result = 'fail' if error else 'pass' + ilist = [] + e = find_in_html(html, 'class="err"[\w=":;\.%\- ]*>', '→</div>', False) + for i in list(set(e)): + ilist.append('%sx%d' % (i, e.count(i)) if e.count(i) > 1 else i) data = { + 'mode': stmp[2], 'host': stmp[0], 'kernel': stmp[1], - 'mode': stmp[2], - 'time': string.join(stmp[3:], ' '), + 'time': tstr, + 'result': result, + 'issues': ','.join(ilist), 'suspend': suspend, 'resume': resume, 'url': os.path.relpath(file, outpath), } - if len(stmp) == 7: - data['kernel'] = 'unknown' - data['mode'] = stmp[1] - data['time'] = string.join(stmp[2:], ' ') testruns.append(data) outfile = os.path.join(outpath, 'summary.html') print('Summary file: %s' % outfile) @@ -5609,11 +5728,12 @@ def printHelp(): print(' -modes List available suspend modes') print(' -status Test to see if the system is enabled to run this tool') print(' -fpdt Print out the contents of the ACPI Firmware Performance Data Table') + print(' -battery Print out battery info (if available)') print(' -sysinfo Print out system info extracted from BIOS') print(' -devinfo Print out the pm settings of all devices which support runtime suspend') print(' -flist Print the list of functions currently being captured in ftrace') print(' -flistall Print all functions capable of being captured in ftrace') - print(' -summary directory Create a summary of all test in this dir') + print(' -summary dir Create a summary of tests in this dir [-genhtml builds missing html]') print(' [redo]') print(' -ftrace ftracefile Create HTML output using ftrace input (used with -dmesg)') print(' -dmesg dmesgfile Create HTML output using dmesg (used with -ftrace)') @@ -5623,8 +5743,9 @@ def printHelp(): # ----------------- MAIN -------------------- # exec start (skipped if script is loaded as library) if __name__ == '__main__': + genhtml = False cmd = '' - simplecmds = ['-sysinfo', '-modes', '-fpdt', '-flist', '-flistall', '-devinfo', '-status'] + simplecmds = ['-sysinfo', '-modes', '-fpdt', '-flist', '-flistall', '-devinfo', '-status', '-battery'] if '-f' in sys.argv: sysvals.cgskip = sysvals.configFile('cgskip.txt') # loop through the command line arguments @@ -5660,6 +5781,8 @@ if __name__ == '__main__': sysvals.skiphtml = True elif(arg == '-cgdump'): sysvals.cgdump = True + elif(arg == '-genhtml'): + genhtml = True elif(arg == '-addlogs'): sysvals.dmesglog = sysvals.ftracelog = True elif(arg == '-verbose'): @@ -5856,6 +5979,8 @@ if __name__ == '__main__': statusCheck(True) elif(cmd == 'fpdt'): getFPDT(True) + elif(cmd == 'battery'): + print 'AC Connect: %s\nCharge: %d' % getBattery() elif(cmd == 'sysinfo'): sysvals.printSystemInfo(True) elif(cmd == 'devinfo'): @@ -5867,7 +5992,7 @@ if __name__ == '__main__': elif(cmd == 'flistall'): sysvals.getFtraceFilterFunctions(False) elif(cmd == 'summary'): - runSummary(sysvals.outdir, True) + runSummary(sysvals.outdir, True, genhtml) sys.exit() # if instructed, re-analyze existing data files @@ -5920,7 +6045,7 @@ if __name__ == '__main__': print('TEST (%d/%d) COMPLETE' % (i+1, sysvals.multitest['count'])) sysvals.logmsg = '' if not sysvals.skiphtml: - runSummary(sysvals.outdir, False) + runSummary(sysvals.outdir, False, False) sysvals.sudouser(sysvals.outdir) else: if sysvals.outdir: diff --git a/tools/power/x86/intel_pstate_tracer/intel_pstate_tracer.py b/tools/power/x86/intel_pstate_tracer/intel_pstate_tracer.py index 29f50d4cfea0..84e2b648e622 100755 --- a/tools/power/x86/intel_pstate_tracer/intel_pstate_tracer.py +++ b/tools/power/x86/intel_pstate_tracer/intel_pstate_tracer.py @@ -28,6 +28,7 @@ import subprocess import os import time import re +import signal import sys import getopt import Gnuplot @@ -78,11 +79,12 @@ def print_help(): print(' Or') print(' ./intel_pstate_tracer.py [--cpu cpus] ---trace_file <trace_file> --name <test_name>') print(' To generate trace file, parse and plot, use (sudo required):') - print(' sudo ./intel_pstate_tracer.py [-c cpus] -i <interval> -n <test_name>') + print(' sudo ./intel_pstate_tracer.py [-c cpus] -i <interval> -n <test_name> -m <kbytes>') print(' Or') - print(' sudo ./intel_pstate_tracer.py [--cpu cpus] --interval <interval> --name <test_name>') + print(' sudo ./intel_pstate_tracer.py [--cpu cpus] --interval <interval> --name <test_name> --memory <kbytes>') print(' Optional argument:') - print(' cpus: comma separated list of CPUs') + print(' cpus: comma separated list of CPUs') + print(' kbytes: Kilo bytes of memory per CPU to allocate to the trace buffer. Default: 10240') print(' Output:') print(' If not already present, creates a "results/test_name" folder in the current working directory with:') print(' cpu.csv - comma seperated values file with trace contents and some additional calculations.') @@ -379,7 +381,7 @@ def clear_trace_file(): f_handle.close() except: print('IO error clearing trace file ') - quit() + sys.exit(2) def enable_trace(): """ Enable trace """ @@ -389,7 +391,7 @@ def enable_trace(): , 'w').write("1") except: print('IO error enabling trace ') - quit() + sys.exit(2) def disable_trace(): """ Disable trace """ @@ -399,17 +401,17 @@ def disable_trace(): , 'w').write("0") except: print('IO error disabling trace ') - quit() + sys.exit(2) def set_trace_buffer_size(): """ Set trace buffer size """ try: - open('/sys/kernel/debug/tracing/buffer_size_kb' - , 'w').write("10240") + with open('/sys/kernel/debug/tracing/buffer_size_kb', 'w') as fp: + fp.write(memory) except: - print('IO error setting trace buffer size ') - quit() + print('IO error setting trace buffer size ') + sys.exit(2) def free_trace_buffer(): """ Free the trace buffer memory """ @@ -418,8 +420,8 @@ def free_trace_buffer(): open('/sys/kernel/debug/tracing/buffer_size_kb' , 'w').write("1") except: - print('IO error setting trace buffer size ') - quit() + print('IO error freeing trace buffer ') + sys.exit(2) def read_trace_data(filename): """ Read and parse trace data """ @@ -431,7 +433,7 @@ def read_trace_data(filename): data = open(filename, 'r').read() except: print('Error opening ', filename) - quit() + sys.exit(2) for line in data.splitlines(): search_obj = \ @@ -489,10 +491,22 @@ def read_trace_data(filename): # Now seperate the main overall csv file into per CPU csv files. split_csv() +def signal_handler(signal, frame): + print(' SIGINT: Forcing cleanup before exit.') + if interval: + disable_trace() + clear_trace_file() + # Free the memory + free_trace_buffer() + sys.exit(0) + +signal.signal(signal.SIGINT, signal_handler) + interval = "" filename = "" cpu_list = "" testname = "" +memory = "10240" graph_data_present = False; valid1 = False @@ -501,7 +515,7 @@ valid2 = False cpu_mask = zeros((MAX_CPUS,), dtype=int) try: - opts, args = getopt.getopt(sys.argv[1:],"ht:i:c:n:",["help","trace_file=","interval=","cpu=","name="]) + opts, args = getopt.getopt(sys.argv[1:],"ht:i:c:n:m:",["help","trace_file=","interval=","cpu=","name=","memory="]) except getopt.GetoptError: print_help() sys.exit(2) @@ -521,6 +535,8 @@ for opt, arg in opts: elif opt in ("-n", "--name"): valid2 = True testname = arg + elif opt in ("-m", "--memory"): + memory = arg if not (valid1 and valid2): print_help() @@ -569,6 +585,11 @@ current_max_cpu = 0 read_trace_data(filename) +clear_trace_file() +# Free the memory +if interval: + free_trace_buffer() + if graph_data_present == False: print('No valid data to plot') sys.exit(2) @@ -593,9 +614,4 @@ for root, dirs, files in os.walk('.'): for f in files: fix_ownership(f) -clear_trace_file() -# Free the memory -if interval: - free_trace_buffer() - os.chdir('../../') diff --git a/tools/power/x86/turbostat/Makefile b/tools/power/x86/turbostat/Makefile index a9bc914a8fe8..2ab25aa38263 100644 --- a/tools/power/x86/turbostat/Makefile +++ b/tools/power/x86/turbostat/Makefile @@ -25,4 +25,4 @@ install : turbostat install -d $(DESTDIR)$(PREFIX)/bin install $(BUILD_OUTPUT)/turbostat $(DESTDIR)$(PREFIX)/bin/turbostat install -d $(DESTDIR)$(PREFIX)/share/man/man8 - install turbostat.8 $(DESTDIR)$(PREFIX)/share/man/man8 + install -m 644 turbostat.8 $(DESTDIR)$(PREFIX)/share/man/man8 diff --git a/tools/power/x86/turbostat/turbostat.8 b/tools/power/x86/turbostat/turbostat.8 index ccf2a69365cc..ca9ef7017624 100644 --- a/tools/power/x86/turbostat/turbostat.8 +++ b/tools/power/x86/turbostat/turbostat.8 @@ -54,9 +54,12 @@ name as necessary to disambiguate it from others is necessary. Note that option .PP \fB--cpu cpu-set\fP limit output to system summary plus the specified cpu-set. If cpu-set is the string "core", then the system summary plus the first CPU in each core are printed -- eg. subsequent HT siblings are not printed. Or if cpu-set is the string "package", then the system summary plus the first CPU in each package is printed. Otherwise, the system summary plus the specified set of CPUs are printed. The cpu-set is ordered from low to high, comma delimited with ". |