diff options
author | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2018-06-03 04:12:30 -0400 |
---|---|---|
committer | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2018-06-03 04:12:30 -0400 |
commit | 9b34ffa09d227131dd764657dd38ed43b22c5611 (patch) | |
tree | 32c6cc4c352c6ae343306ceadc91273fd452c7e0 | |
parent | ba8042a85e8aebc8043deee2d396459f2c39a66d (diff) | |
parent | ffbb95aa2d8248eabd594e113718b478cdc576e3 (diff) |
Merge back earlier PM tools material for v4.18.
-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 |
4 files changed, 302 insertions, 157 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 @@ | |||
1 | #!/usr/bin/python | 1 | #!/usr/bin/python2 |
2 | # | 2 | # |
3 | # Tool for analyzing boot timing | 3 | # Tool for analyzing boot timing |
4 | # Copyright (c) 2013, Intel Corporation. | 4 | # 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 | |||
168 | in the current folder. The output page is a table of tests with | 168 | in the current folder. The output page is a table of tests with |
169 | suspend and resume values sorted by suspend mode, host, and kernel. | 169 | suspend and resume values sorted by suspend mode, host, and kernel. |
170 | Includes test averages by mode and links to the test html files. | 170 | Includes test averages by mode and links to the test html files. |
171 | Use -genhtml to include tests with missing html. | ||
171 | .TP | 172 | .TP |
172 | \fB-modes\fR | 173 | \fB-modes\fR |
173 | List available suspend modes. | 174 | List available suspend modes. |
@@ -179,6 +180,9 @@ with any options you intend to use to see if they will work. | |||
179 | \fB-fpdt\fR | 180 | \fB-fpdt\fR |
180 | Print out the contents of the ACPI Firmware Performance Data Table. | 181 | Print out the contents of the ACPI Firmware Performance Data Table. |
181 | .TP | 182 | .TP |
183 | \fB-battery\fR | ||
184 | Print out battery status and current charge. | ||
185 | .TP | ||
182 | \fB-sysinfo\fR | 186 | \fB-sysinfo\fR |
183 | Print out system info extracted from BIOS. Reads /dev/mem directly instead of going through dmidecode. | 187 | Print out system info extracted from BIOS. Reads /dev/mem directly instead of going through dmidecode. |
184 | .TP | 188 | .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 @@ | |||
1 | #!/usr/bin/python | 1 | #!/usr/bin/python2 |
2 | # | 2 | # |
3 | # Tool for analyzing suspend/resume timing | 3 | # Tool for analyzing suspend/resume timing |
4 | # Copyright (c) 2013, Intel Corporation. | 4 | # Copyright (c) 2013, Intel Corporation. |
@@ -69,7 +69,7 @@ from subprocess import call, Popen, PIPE | |||
69 | # store system values and test parameters | 69 | # store system values and test parameters |
70 | class SystemValues: | 70 | class SystemValues: |
71 | title = 'SleepGraph' | 71 | title = 'SleepGraph' |
72 | version = '5.0' | 72 | version = '5.1' |
73 | ansi = False | 73 | ansi = False |
74 | rs = 0 | 74 | rs = 0 |
75 | display = 0 | 75 | display = 0 |
@@ -240,7 +240,7 @@ class SystemValues: | |||
240 | kprobes = dict() | 240 | kprobes = dict() |
241 | timeformat = '%.3f' | 241 | timeformat = '%.3f' |
242 | cmdline = '%s %s' % \ | 242 | cmdline = '%s %s' % \ |
243 | (os.path.basename(sys.argv[0]), string.join(sys.argv[1:], ' ')) | 243 | (os.path.basename(sys.argv[0]), ' '.join(sys.argv[1:])) |
244 | def __init__(self): | 244 | def __init__(self): |
245 | self.archargs = 'args_'+platform.machine() | 245 | self.archargs = 'args_'+platform.machine() |
246 | self.hostname = platform.node() | 246 | self.hostname = platform.node() |
@@ -917,12 +917,18 @@ class Data: | |||
917 | self.devicegroups.append([phase]) | 917 | self.devicegroups.append([phase]) |
918 | self.errorinfo = {'suspend':[],'resume':[]} | 918 | self.errorinfo = {'suspend':[],'resume':[]} |
919 | def extractErrorInfo(self): | 919 | def extractErrorInfo(self): |
920 | elist = { | ||
921 | 'HWERROR' : '.*\[ *Hardware Error *\].*', | ||
922 | 'FWBUG' : '.*\[ *Firmware Bug *\].*', | ||
923 | 'BUG' : '.*BUG.*', | ||
924 | 'ERROR' : '.*ERROR.*', | ||
925 | 'WARNING' : '.*WARNING.*', | ||
926 | 'IRQ' : '.*genirq: .*', | ||
927 | 'TASKFAIL': '.*Freezing of tasks failed.*', | ||
928 | } | ||
920 | lf = sysvals.openlog(sysvals.dmesgfile, 'r') | 929 | lf = sysvals.openlog(sysvals.dmesgfile, 'r') |
921 | i = 0 | 930 | i = 0 |
922 | list = [] | 931 | list = [] |
923 | # sl = start line, et = error time, el = error line | ||
924 | type = 'ERROR' | ||
925 | sl = et = el = -1 | ||
926 | for line in lf: | 932 | for line in lf: |
927 | i += 1 | 933 | i += 1 |
928 | m = re.match('[ \t]*(\[ *)(?P<ktime>[0-9\.]*)(\]) (?P<msg>.*)', line) | 934 | m = re.match('[ \t]*(\[ *)(?P<ktime>[0-9\.]*)(\]) (?P<msg>.*)', line) |
@@ -931,43 +937,13 @@ class Data: | |||
931 | t = float(m.group('ktime')) | 937 | t = float(m.group('ktime')) |
932 | if t < self.start or t > self.end: | 938 | if t < self.start or t > self.end: |
933 | continue | 939 | continue |
934 | if t < self.tSuspended: | 940 | dir = 'suspend' if t < self.tSuspended else 'resume' |
935 | dir = 'suspend' | ||
936 | else: | ||
937 | dir = 'resume' | ||
938 | msg = m.group('msg') | 941 | msg = m.group('msg') |
939 | if re.match('-*\[ *cut here *\]-*', msg): | 942 | for err in elist: |
940 | type = 'WARNING' | 943 | if re.match(elist[err], msg): |
941 | sl = i | 944 | list.append((err, dir, t, i, i)) |
942 | elif re.match('genirq: .*', msg): | ||
943 | type = 'IRQ' | ||
944 | sl = i | ||
945 | elif re.match('BUG: .*', msg) or re.match('kernel BUG .*', msg): | ||
946 | type = 'BUG' | ||
947 | sl = i | ||
948 | elif re.match('-*\[ *end trace .*\]-*', msg) or \ | ||
949 | re.match('R13: .*', msg): | ||
950 | if et >= 0 and sl >= 0: | ||
951 | list.append((type, dir, et, sl, i)) | ||
952 | self.kerror = True | ||
953 | sl = et = el = -1 | ||
954 | type = 'ERROR' | ||
955 | elif 'Call Trace:' in msg: | ||
956 | if el >= 0 and et >= 0: | ||
957 | list.append((type, dir, et, el, el)) | ||
958 | self.kerror = True | 945 | self.kerror = True |
959 | et, el = t, i | 946 | break |
960 | if sl < 0 or type == 'BUG': | ||
961 | slval = i | ||
962 | if sl >= 0: | ||
963 | slval = sl | ||
964 | list.append((type, dir, et, slval, i)) | ||
965 | self.kerror = True | ||
966 | sl = et = el = -1 | ||
967 | type = 'ERROR' | ||
968 | if el >= 0 and et >= 0: | ||
969 | list.append((type, dir, et, el, el)) | ||
970 | self.kerror = True | ||
971 | for e in list: | 947 | for e in list: |
972 | type, dir, t, idx1, idx2 = e | 948 | type, dir, t, idx1, idx2 = e |
973 | sysvals.vprint('kernel %s found in %s at %f' % (type, dir, t)) | 949 | sysvals.vprint('kernel %s found in %s at %f' % (type, dir, t)) |
@@ -2331,12 +2307,14 @@ class TestProps: | |||
2331 | sv.suspendmode = data.stamp['mode'] | 2307 | sv.suspendmode = data.stamp['mode'] |
2332 | if sv.suspendmode == 'command' and sv.ftracefile != '': | 2308 | if sv.suspendmode == 'command' and sv.ftracefile != '': |
2333 | modes = ['on', 'freeze', 'standby', 'mem', 'disk'] | 2309 | modes = ['on', 'freeze', 'standby', 'mem', 'disk'] |
2334 | out = Popen(['grep', 'machine_suspend', sv.ftracefile], | 2310 | fp = sysvals.openlog(sv.ftracefile, 'r') |
2335 | stderr=PIPE, stdout=PIPE).stdout.read() | 2311 | for line in fp: |
2336 | m = re.match('.* machine_suspend\[(?P<mode>.*)\]', out) | 2312 | m = re.match('.* machine_suspend\[(?P<mode>.*)\]', line) |
2337 | if m and m.group('mode') in ['1', '2', '3', '4']: | 2313 | if m and m.group('mode') in ['1', '2', '3', '4']: |
2338 | sv.suspendmode = modes[int(m.group('mode'))] | 2314 | sv.suspendmode = modes[int(m.group('mode'))] |
2339 | data.stamp['mode'] = sv.suspendmode | 2315 | data.stamp['mode'] = sv.suspendmode |
2316 | break | ||
2317 | fp.close() | ||
2340 | m = re.match(self.cmdlinefmt, self.cmdline) | 2318 | m = re.match(self.cmdlinefmt, self.cmdline) |
2341 | if m: | 2319 | if m: |
2342 | sv.cmdline = m.group('cmd') | 2320 | sv.cmdline = m.group('cmd') |
@@ -2413,7 +2391,7 @@ class ProcessMonitor: | |||
2413 | # markers, and/or kprobes required for primary parsing. | 2391 | # markers, and/or kprobes required for primary parsing. |
2414 | def doesTraceLogHaveTraceEvents(): | 2392 | def doesTraceLogHaveTraceEvents(): |
2415 | kpcheck = ['_cal: (', '_cpu_down()'] | 2393 | kpcheck = ['_cal: (', '_cpu_down()'] |
2416 | techeck = sysvals.traceevents[:] | 2394 | techeck = ['suspend_resume'] |
2417 | tmcheck = ['SUSPEND START', 'RESUME COMPLETE'] | 2395 | tmcheck = ['SUSPEND START', 'RESUME COMPLETE'] |
2418 | sysvals.usekprobes = False | 2396 | sysvals.usekprobes = False |
2419 | fp = sysvals.openlog(sysvals.ftracefile, 'r') | 2397 | fp = sysvals.openlog(sysvals.ftracefile, 'r') |
@@ -2808,7 +2786,7 @@ def parseTraceLog(live=False): | |||
2808 | # -- phase changes -- | 2786 | # -- phase changes -- |
2809 | # start of kernel suspend | 2787 | # start of kernel suspend |
2810 | if(re.match('suspend_enter\[.*', t.name)): | 2788 | if(re.match('suspend_enter\[.*', t.name)): |
2811 | if(isbegin): | 2789 | if(isbegin and data.start == data.tKernSus): |
2812 | data.dmesg[phase]['start'] = t.time | 2790 | data.dmesg[phase]['start'] = t.time |
2813 | data.tKernSus = t.time | 2791 | data.tKernSus = t.time |
2814 | continue | 2792 | continue |
@@ -3072,13 +3050,20 @@ def parseTraceLog(live=False): | |||
3072 | sysvals.vprint('Callgraph found for task %d: %.3fms, %s' % (cg.pid, (cg.end - cg.start)*1000, name)) | 3050 | sysvals.vprint('Callgraph found for task %d: %.3fms, %s' % (cg.pid, (cg.end - cg.start)*1000, name)) |
3073 | cg.newActionFromFunction(data) | 3051 | cg.newActionFromFunction(data) |
3074 | if sysvals.suspendmode == 'command': | 3052 | if sysvals.suspendmode == 'command': |
3075 | return testdata | 3053 | return (testdata, '') |
3076 | 3054 | ||
3077 | # fill in any missing phases | 3055 | # fill in any missing phases |
3056 | error = [] | ||
3078 | for data in testdata: | 3057 | for data in testdata: |
3058 | tn = '' if len(testdata) == 1 else ('%d' % (data.testnumber + 1)) | ||
3059 | terr = '' | ||
3079 | lp = data.phases[0] | 3060 | lp = data.phases[0] |
3080 | for p in data.phases: | 3061 | for p in data.phases: |
3081 | if(data.dmesg[p]['start'] < 0 and data.dmesg[p]['end'] < 0): | 3062 | if(data.dmesg[p]['start'] < 0 and data.dmesg[p]['end'] < 0): |
3063 | if not terr: | ||
3064 | print 'TEST%s FAILED: %s failed in %s phase' % (tn, sysvals.suspendmode, lp) | ||
3065 | terr = '%s%s failed in %s phase' % (sysvals.suspendmode, tn, lp) | ||
3066 | error.append(terr) | ||
3082 | sysvals.vprint('WARNING: phase "%s" is missing!' % p) | 3067 | sysvals.vprint('WARNING: phase "%s" is missing!' % p) |
3083 | if(data.dmesg[p]['start'] < 0): | 3068 | if(data.dmesg[p]['start'] < 0): |
3084 | data.dmesg[p]['start'] = data.dmesg[lp]['end'] | 3069 | data.dmesg[p]['start'] = data.dmesg[lp]['end'] |
@@ -3106,7 +3091,7 @@ def parseTraceLog(live=False): | |||
3106 | for j in range(i + 1, tc): | 3091 | for j in range(i + 1, tc): |
3107 | testdata[j].mergeOverlapDevices(devlist) | 3092 | testdata[j].mergeOverlapDevices(devlist) |
3108 | testdata[0].stitchTouchingThreads(testdata[1:]) | 3093 | testdata[0].stitchTouchingThreads(testdata[1:]) |
3109 | return testdata | 3094 | return (testdata, ', '.join(error)) |
3110 | 3095 | ||
3111 | # Function: loadKernelLog | 3096 | # Function: loadKernelLog |
3112 | # Description: | 3097 | # Description: |
@@ -3173,7 +3158,7 @@ def loadKernelLog(): | |||
3173 | if data: | 3158 | if data: |
3174 | testruns.append(data) | 3159 | testruns.append(data) |
3175 | if len(testruns) < 1: | 3160 | if len(testruns) < 1: |
3176 | doError(' dmesg log has no suspend/resume data: %s' \ | 3161 | print('ERROR: dmesg log has no suspend/resume data: %s' \ |
3177 | % sysvals.dmesgfile) | 3162 | % sysvals.dmesgfile) |
3178 | 3163 | ||
3179 | # fix lines with same timestamp/function with the call and return swapped | 3164 | # fix lines with same timestamp/function with the call and return swapped |
@@ -3521,68 +3506,144 @@ def createHTMLSummarySimple(testruns, htmlfile, folder): | |||
3521 | .summary {border:1px solid;}\n\ | 3506 | .summary {border:1px solid;}\n\ |
3522 | th {border: 1px solid black;background:#222;color:white;}\n\ | 3507 | th {border: 1px solid black;background:#222;color:white;}\n\ |
3523 | td {font: 16px "Times New Roman";text-align: center;}\n\ | 3508 | td {font: 16px "Times New Roman";text-align: center;}\n\ |
3524 | tr.alt td {background:#ddd;}\n\ | 3509 | tr.head td {border: 1px solid black;background:#aaa;}\n\ |
3525 | tr.avg td {background:#aaa;}\n\ | 3510 | tr.alt {background-color:#ddd;}\n\ |
3511 | tr.notice {color:red;}\n\ | ||
3512 | .minval {background-color:#BBFFBB;}\n\ | ||
3513 | .medval {background-color:#BBBBFF;}\n\ | ||
3514 | .maxval {background-color:#FFBBBB;}\n\ | ||
3515 | .head a {color:#000;text-decoration: none;}\n\ | ||
3526 | </style>\n</head>\n<body>\n' | 3516 | </style>\n</head>\n<body>\n' |
3527 | 3517 | ||
3518 | # extract the test data into list | ||
3519 | list = dict() | ||
3520 | tAvg, tMin, tMax, tMed = [0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [[], []] | ||
3521 | iMin, iMed, iMax = [0, 0], [0, 0], [0, 0] | ||
3522 | num = 0 | ||
3523 | lastmode = '' | ||
3524 | cnt = {'pass':0, 'fail':0, 'hang':0} | ||
3525 | for data in sorted(testruns, key=lambda v:(v['mode'], v['host'], v['kernel'], v['time'])): | ||
3526 | mode = data['mode'] | ||
3527 | if mode not in list: | ||
3528 | list[mode] = {'data': [], 'avg': [0,0], 'min': [0,0], 'max': [0,0], 'med': [0,0]} | ||
3529 | if lastmode and lastmode != mode and num > 0: | ||
3530 | for i in range(2): | ||
3531 | s = sorted(tMed[i]) | ||
3532 | list[lastmode]['med'][i] = s[int(len(s)/2)] | ||
3533 | iMed[i] = tMed[i].index(list[lastmode]['med'][i]) | ||
3534 | list[lastmode]['avg'] = [tAvg[0] / num, tAvg[1] / num] | ||
3535 | list[lastmode]['min'] = tMin | ||
3536 | list[lastmode]['max'] = tMax | ||
3537 | list[lastmode]['idx'] = (iMin, iMed, iMax) | ||
3538 | tAvg, tMin, tMax, tMed = [0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [[], []] | ||
3539 | iMin, iMed, iMax = [0, 0], [0, 0], [0, 0] | ||
3540 | num = 0 | ||
3541 | tVal = [float(data['suspend']), float(data['resume'])] | ||
3542 | list[mode]['data'].append([data['host'], data['kernel'], | ||
3543 | data['time'], tVal[0], tVal[1], data['url'], data['result'], | ||
3544 | data['issues']]) | ||
3545 | idx = len(list[mode]['data']) - 1 | ||
3546 | if data['result'] == 'pass': | ||
3547 | cnt['pass'] += 1 | ||
3548 | for i in range(2): | ||
3549 | tMed[i].append(tVal[i]) | ||
3550 | tAvg[i] += tVal[i] | ||
3551 | if tMin[i] == 0 or tVal[i] < tMin[i]: | ||
3552 | iMin[i] = idx | ||
3553 | tMin[i] = tVal[i] | ||
3554 | if tMax[i] == 0 or tVal[i] > tMax[i]: | ||
3555 | iMax[i] = idx | ||
3556 | tMax[i] = tVal[i] | ||
3557 | num += 1 | ||
3558 | elif data['result'] == 'hang': | ||
3559 | cnt['hang'] += 1 | ||
3560 | elif data['result'] == 'fail': | ||
3561 | cnt['fail'] += 1 | ||
3562 | lastmode = mode | ||
3563 | if lastmode and num > 0: | ||
3564 | for i in range(2): | ||
3565 | s = sorted(tMed[i]) | ||
3566 | list[lastmode]['med'][i] = s[int(len(s)/2)] | ||
3567 | iMed[i] = tMed[i].index(list[lastmode]['med'][i]) | ||
3568 | list[lastmode]['avg'] = [tAvg[0] / num, tAvg[1] / num] | ||
3569 | list[lastmode]['min'] = tMin | ||
3570 | list[lastmode]['max'] = tMax | ||
3571 | list[lastmode]['idx'] = (iMin, iMed, iMax) | ||
3572 | |||
3528 | # group test header | 3573 | # group test header |
3529 | html += '<div class="stamp">%s (%d tests)</div>\n' % (folder, len(testruns)) | 3574 | desc = [] |
3575 | for ilk in sorted(cnt, reverse=True): | ||
3576 | if cnt[ilk] > 0: | ||
3577 | desc.append('%d %s' % (cnt[ilk], ilk)) | ||
3578 | html += '<div class="stamp">%s (%d tests: %s)</div>\n' % (folder, len(testruns), ', '.join(desc)) | ||
3530 | th = '\t<th>{0}</th>\n' | 3579 | th = '\t<th>{0}</th>\n' |
3531 | td = '\t<td>{0}</td>\n' | 3580 | td = '\t<td>{0}</td>\n' |
3581 | tdh = '\t<td{1}>{0}</td>\n' | ||
3532 | tdlink = '\t<td><a href="{0}">html</a></td>\n' | 3582 | tdlink = '\t<td><a href="{0}">html</a></td>\n' |
3533 | 3583 | ||
3534 | # table header | 3584 | # table header |
3535 | html += '<table class="summary">\n<tr>\n' + th.format('#') +\ | 3585 | html += '<table class="summary">\n<tr>\n' + th.format('#') +\ |
3536 | th.format('Mode') + th.format('Host') + th.format('Kernel') +\ | 3586 | th.format('Mode') + th.format('Host') + th.format('Kernel') +\ |
3537 | th.format('Test Time') + th.format('Suspend') + th.format('Resume') +\ | 3587 | th.format('Test Time') + th.format('Result') + th.format('Issues') +\ |
3538 | th.format('Detail') + '</tr>\n' | 3588 | th.format('Suspend') + th.format('Resume') + th.format('Detail') + '</tr>\n' |
3539 | 3589 | ||
3540 | # test data, 1 row per test | 3590 | # export list into html |
3541 | avg = '<tr class="avg"><td></td><td></td><td></td><td></td>'+\ | 3591 | head = '<tr class="head"><td>{0}</td><td>{1}</td>'+\ |
3542 | '<td>Average of {0} {1} tests</td><td>{2}</td><td>{3}</td><td></td></tr>\n' | 3592 | '<td colspan=8 class="sus">Suspend Avg={2} '+\ |
3543 | sTimeAvg = rTimeAvg = 0.0 | 3593 | '<span class=minval><a href="#s{10}min">Min={3}</a></span> '+\ |
3544 | mode = '' | 3594 | '<span class=medval><a href="#s{10}med">Med={4}</a></span> '+\ |
3545 | num = 0 | 3595 | '<span class=maxval><a href="#s{10}max">Max={5}</a></span> '+\ |
3546 | for data in sorted(testruns, key=lambda v:(v['mode'], v['host'], v['kernel'], v['time'])): | 3596 | 'Resume Avg={6} '+\ |
3547 | if mode != data['mode']: | 3597 | '<span class=minval><a href="#r{10}min">Min={7}</a></span> '+\ |
3548 | # test average line | 3598 | '<span class=medval><a href="#r{10}med">Med={8}</a></span> '+\ |
3549 | if(num > 0): | 3599 | '<span class=maxval><a href="#r{10}max">Max={9}</a></span></td>'+\ |
3550 | sTimeAvg /= (num - 1) | 3600 | '</tr>\n' |
3551 | rTimeAvg /= (num - 1) | 3601 | headnone = '<tr class="head"><td>{0}</td><td>{1}</td><td colspan=8></td></tr>\n' |
3552 | html += avg.format('%d' % (num - 1), mode, | 3602 | for mode in list: |
3553 | '%3.3f ms' % sTimeAvg, '%3.3f ms' % rTimeAvg) | 3603 | # header line for each suspend mode |
3554 | sTimeAvg = rTimeAvg = 0.0 | 3604 | num = 0 |
3555 | mode = data['mode'] | 3605 | tAvg, tMin, tMax, tMed = list[mode]['avg'], list[mode]['min'],\ |
3556 | num = 1 | 3606 | list[mode]['max'], list[mode]['med'] |
3557 | # alternate row color | 3607 | count = len(list[mode]['data']) |
3558 | if num % 2 == 1: | 3608 | if 'idx' in list[mode]: |
3559 | html += '<tr class="alt">\n' | 3609 | iMin, iMed, iMax = list[mode]['idx'] |
3610 | html += head.format('%d' % count, mode.upper(), | ||
3611 | '%.3f' % tAvg[0], '%.3f' % tMin[0], '%.3f' % tMed[0], '%.3f' % tMax[0], | ||
3612 | '%.3f' % tAvg[1], '%.3f' % tMin[1], '%.3f' % tMed[1], '%.3f' % tMax[1], | ||
3613 | mode.lower() | ||
3614 | ) | ||
3560 | else: | 3615 | else: |
3561 | html += '<tr>\n' | 3616 | iMin = iMed = iMax = [-1, -1, -1] |
3562 | html += td.format("%d" % num) | 3617 | html += headnone.format('%d' % count, mode.upper()) |
3563 | num += 1 | 3618 | for d in list[mode]['data']: |
3564 | # basic info | 3619 | # row classes - alternate row color |
3565 | for item in ['mode', 'host', 'kernel', 'time']: | 3620 | rcls = ['alt'] if num % 2 == 1 else [] |
3566 | val = "unknown" | 3621 | if d[6] != 'pass': |
3567 | if(item in data): | 3622 | rcls.append('notice') |
3568 | val = data[item] | 3623 | html += '<tr class="'+(' '.join(rcls))+'">\n' if len(rcls) > 0 else '<tr>\n' |
3569 | html += td.format(val) | 3624 | # figure out if the line has sus or res highlighted |
3570 | # suspend time | 3625 | idx = list[mode]['data'].index(d) |
3571 | sTime = float(data['suspend']) | 3626 | tHigh = ['', ''] |
3572 | sTimeAvg += sTime | 3627 | for i in range(2): |
3573 | html += td.format('%.3f ms' % sTime) | 3628 | tag = 's%s' % mode if i == 0 else 'r%s' % mode |
3574 | # resume time | 3629 | if idx == iMin[i]: |
3575 | rTime = float(data['resume']) | 3630 | tHigh[i] = ' id="%smin" class=minval title="Minimum"' % tag |
3576 | rTimeAvg += rTime | 3631 | elif idx == iMax[i]: |
3577 | html += td.format('%.3f ms' % rTime) | 3632 | tHigh[i] = ' id="%smax" class=maxval title="Maximum"' % tag |
3578 | # link to the output html | 3633 | elif idx == iMed[i]: |
3579 | html += tdlink.format(data['url']) + '</tr>\n' | 3634 | tHigh[i] = ' id="%smed" class=medval title="Median"' % tag |
3580 | # last test average line | 3635 | html += td.format("%d" % (list[mode]['data'].index(d) + 1)) # row |
3581 | if(num > 0): | 3636 | html += td.format(mode) # mode |
3582 | sTimeAvg /= (num - 1) | 3637 | html += td.format(d[0]) # host |
3583 | rTimeAvg /= (num - 1) | 3638 | html += td.format(d[1]) # kernel |
3584 | html += avg.format('%d' % (num - 1), mode, | 3639 | html += td.format(d[2]) # time |
3585 | '%3.3f ms' % sTimeAvg, '%3.3f ms' % rTimeAvg) | 3640 | html += td.format(d[6]) # result |
3641 | html += td.format(d[7]) # issues | ||
3642 | html += tdh.format('%.3f ms' % d[3], tHigh[0]) if d[3] else td.format('') # suspend | ||
3643 | html += tdh.format('%.3f ms' % d[4], tHigh[1]) if d[4] else td.format('') # resume | ||
3644 | html += tdlink.format(d[5]) if d[5] else td.format('') # url | ||
3645 | html += '</tr>\n' | ||
3646 | num += 1 | ||
3586 | 3647 | ||
3587 | # flush the data to file | 3648 | # flush the data to file |
3588 | hf = open(htmlfile, 'w') | 3649 | hf = open(htmlfile, 'w') |
@@ -3607,7 +3668,7 @@ def ordinal(value): | |||
3607 | # testruns: array of Data objects from parseKernelLog or parseTraceLog | 3668 | # testruns: array of Data objects from parseKernelLog or parseTraceLog |
3608 | # Output: | 3669 | # Output: |
3609 | # True if the html file was created, false if it failed | 3670 | # True if the html file was created, false if it failed |
3610 | def createHTML(testruns): | 3671 | def createHTML(testruns, testfail): |
3611 | if len(testruns) < 1: | 3672 | if len(testruns) < 1: |
3612 | print('ERROR: Not enough test data to build a timeline') | 3673 | print('ERROR: Not enough test data to build a timeline') |
3613 | return | 3674 | return |
@@ -3641,6 +3702,7 @@ def createHTML(testruns): | |||
3641 | '<td class="purple">{4}Firmware Resume: {2} ms</td>'\ | 3702 | '<td class="purple">{4}Firmware Resume: {2} ms</td>'\ |
3642 | '<td class="yellow" title="time from firmware mode to return from kernel enter_state({5}) [kernel time only]">{4}Kernel Resume: {3} ms</td>'\ | 3703 | '<td class="yellow" title="time from firmware mode to return from kernel enter_state({5}) [kernel time only]">{4}Kernel Resume: {3} ms</td>'\ |
3643 | '</tr>\n</table>\n' | 3704 | '</tr>\n</table>\n' |
3705 | html_fail = '<table class="testfail"><tr><td>{0}</td></tr></table>\n' | ||
3644 | 3706 | ||
3645 | # html format variables | 3707 | # html format variables |
3646 | scaleH = 20 | 3708 | scaleH = 20 |
@@ -3708,6 +3770,9 @@ def createHTML(testruns): | |||
3708 | resume_time, testdesc, stitle, rtitle) | 3770 | resume_time, testdesc, stitle, rtitle) |
3709 | devtl.html += thtml | 3771 | devtl.html += thtml |
3710 | 3772 | ||
3773 | if testfail: | ||
3774 | devtl.html += html_fail.format(testfail) | ||
3775 | |||
3711 | # time scale for potentially multiple datasets | 3776 | # time scale for potentially multiple datasets |
3712 | t0 = testruns[0].start | 3777 | t0 = testruns[0].start |
3713 | tMax = testruns[-1].end | 3778 | tMax = testruns[-1].end |
@@ -4006,6 +4071,7 @@ def addCSS(hf, sv, testcount=1, kerror=False, extra=''): | |||
4006 | .blue {background:rgba(169,208,245,0.4);}\n\ | 4071 | .blue {background:rgba(169,208,245,0.4);}\n\ |
4007 | .time1 {font:22px Arial;border:1px solid;}\n\ | 4072 | .time1 {font:22px Arial;border:1px solid;}\n\ |
4008 | .time2 {font:15px Arial;border-bottom:1px solid;border-left:1px solid;border-right:1px solid;}\n\ | 4073 | .time2 {font:15px Arial;border-bottom:1px solid;border-left:1px solid;border-right:1px solid;}\n\ |
4074 | .testfail {font:bold 22px Arial;color:red;border:1px dashed;}\n\ | ||
4009 | td {text-align:center;}\n\ | 4075 | td {text-align:center;}\n\ |
4010 | r {color:#500000;font:15px Tahoma;}\n\ | 4076 | r {color:#500000;font:15px Tahoma;}\n\ |
4011 | n {color:#505050;font:15px Tahoma;}\n\ | 4077 | n {color:#505050;font:15px Tahoma;}\n\ |
@@ -4927,6 +4993,25 @@ def dmidecode(mempath, fatal=False): | |||
4927 | count += 1 | 4993 | count += 1 |
4928 | return out | 4994 | return out |
4929 | 4995 | ||
4996 | def getBattery(): | ||
4997 | p = '/sys/class/power_supply' | ||
4998 | bat = dict() | ||
4999 | for d in os.listdir(p): | ||
5000 | type = sysvals.getVal(os.path.join(p, d, 'type')).strip().lower() | ||
5001 | if type != 'battery': | ||
5002 | continue | ||
5003 | for v in ['status', 'energy_now', 'capacity_now']: | ||
5004 | bat[v] = sysvals.getVal(os.path.join(p, d, v)).strip().lower() | ||
5005 | break | ||
5006 | ac = True | ||
5007 | if 'status' in bat and 'discharging' in bat['status']: | ||
5008 | ac = False | ||
5009 | charge = 0 | ||
5010 | for v in ['energy_now', 'capacity_now']: | ||
5011 | if v in bat and bat[v]: | ||
5012 | charge = int(bat[v]) | ||
5013 | return (ac, charge) | ||
5014 | |||
4930 | # Function: getFPDT | 5015 | # Function: getFPDT |
4931 | # Description: | 5016 | # Description: |
4932 | # Read the acpi bios tables and pull out FPDT, the firmware data | 5017 | # Read the acpi bios tables and pull out FPDT, the firmware data |
@@ -5202,8 +5287,9 @@ def getArgFloat(name, args, min, max, main=True): | |||
5202 | 5287 | ||
5203 | def processData(live=False): | 5288 | def processData(live=False): |
5204 | print('PROCESSING DATA') | 5289 | print('PROCESSING DATA') |
5290 | error = '' | ||
5205 | if(sysvals.usetraceevents): | 5291 | if(sysvals.usetraceevents): |
5206 | testruns = parseTraceLog(live) | 5292 | testruns, error = parseTraceLog(live) |
5207 | if sysvals.dmesgfile: | 5293 | if sysvals.dmesgfile: |
5208 | for data in testruns: | 5294 | for data in testruns: |
5209 | data.extractErrorInfo() | 5295 | data.extractErrorInfo() |
@@ -5220,15 +5306,18 @@ def processData(live=False): | |||
5220 | for data in testruns: | 5306 | for data in testruns: |
5221 | data.debugPrint() | 5307 | data.debugPrint() |
5222 | sys.exit() | 5308 | sys.exit() |
5223 | 5309 | if len(testruns) < 1: | |
5310 | return (testruns, {'error': 'timeline generation failed'}) | ||
5224 | sysvals.vprint('Creating the html timeline (%s)...' % sysvals.htmlfile) | 5311 | sysvals.vprint('Creating the html timeline (%s)...' % sysvals.htmlfile) |
5225 | createHTML(testruns) | 5312 | createHTML(testruns, error) |
5226 | print('DONE') | 5313 | print('DONE') |
5227 | data = testruns[0] | 5314 | data = testruns[0] |
5228 | stamp = data.stamp | 5315 | stamp = data.stamp |
5229 | stamp['suspend'], stamp['resume'] = data.getTimeValues() | 5316 | stamp['suspend'], stamp['resume'] = data.getTimeValues() |
5230 | if data.fwValid: | 5317 | if data.fwValid: |
5231 | stamp['fwsuspend'], stamp['fwresume'] = data.fwSuspend, data.fwResume | 5318 | stamp['fwsuspend'], stamp['fwresume'] = data.fwSuspend, data.fwResume |
5319 | if error: | ||
5320 | stamp['error'] = error | ||
5232 | return (testruns, stamp) | 5321 | return (testruns, stamp) |
5233 | 5322 | ||
5234 | # Function: rerunTest | 5323 | # Function: rerunTest |
@@ -5268,58 +5357,88 @@ def runTest(n=0): | |||
5268 | sysvals.sudouser(sysvals.testdir) | 5357 | sysvals.sudouser(sysvals.testdir) |
5269 | sysvals.outputResult(stamp, n) | 5358 | sysvals.outputResult(stamp, n) |
5270 | 5359 | ||
5271 | def find_in_html(html, strs, div=False): | 5360 | def find_in_html(html, start, end, firstonly=True): |
5272 | for str in strs: | 5361 | n, out = 0, [] |
5273 | l = len(str) | 5362 | while n < len(html): |
5274 | i = html.find(str) | 5363 | m = re.search(start, html[n:]) |
5275 | if i >= 0: | 5364 | if not m: |
5276 | break | 5365 | break |
5277 | if i < 0: | 5366 | i = m.end() |
5278 | return '' | 5367 | m = re.search(end, html[n+i:]) |
5279 | if not div: | 5368 | if not m: |
5280 | return re.search(r'[-+]?\d*\.\d+|\d+', html[i+l:i+l+50]).group() | 5369 | break |
5281 | n = html[i+l:].find('</div>') | 5370 | j = m.start() |
5282 | if n < 0: | 5371 | str = html[n+i:n+i+j] |
5372 | if end == 'ms': | ||
5373 | num = re.search(r'[-+]?\d*\.\d+|\d+', str) | ||
5374 | str = num.group() if num else 'NaN' | ||
5375 | if firstonly: | ||
5376 | return str | ||
5377 | out.append(str) | ||
5378 | n += i+j | ||
5379 | if firstonly: | ||
5283 | return '' | 5380 | return '' |
5284 | return html[i+l:i+l+n] | 5381 | return out |
5285 | 5382 | ||
5286 | # Function: runSummary | 5383 | # Function: runSummary |
5287 | # Description: | 5384 | # Description: |
5288 | # create a summary of tests in a sub-directory | 5385 | # create a summary of tests in a sub-directory |
5289 | def runSummary(subdir, local=True): | 5386 | def runSummary(subdir, local=True, genhtml=False): |
5290 | inpath = os.path.abspath(subdir) | 5387 | inpath = os.path.abspath(subdir) |
5291 | outpath = inpath | 5388 | outpath = inpath |
5292 | if local: | 5389 | if local: |
5293 | outpath = os.path.abspath('.') | 5390 | outpath = os.path.abspath('.') |
5294 | print('Generating a summary of folder "%s"' % inpath) | 5391 | print('Generating a summary of folder "%s"' % inpath) |
5392 | if genhtml: | ||
5393 | for dirname, dirnames, filenames in os.walk(subdir): | ||
5394 | sysvals.dmesgfile = sysvals.ftracefile = sysvals.htmlfile = '' | ||
5395 | for filename in filenames: | ||
5396 | if(re.match('.*_dmesg.txt', filename)): | ||
5397 | sysvals.dmesgfile = os.path.join(dirname, filename) | ||
5398 | elif(re.match('.*_ftrace.txt', filename)): | ||
5399 | sysvals.ftracefile = os.path.join(dirname, filename) | ||
5400 | sysvals.setOutputFile() | ||
5401 | if sysvals.ftracefile and sysvals.htmlfile and \ | ||
5402 | not os.path.exists(sysvals.htmlfile): | ||
5403 | print('FTRACE: %s' % sysvals.ftracefile) | ||
5404 | if sysvals.dmesgfile: | ||
5405 | print('DMESG : %s' % sysvals.dmesgfile) | ||
5406 | rerunTest() | ||
5295 | testruns = [] | 5407 | testruns = [] |
5296 | for dirname, dirnames, filenames in os.walk(subdir): | 5408 | for dirname, dirnames, filenames in os.walk(subdir): |
5297 | for filename in filenames: | 5409 | for filename in filenames: |
5298 | if(not re.match('.*.html', filename)): | 5410 | if(not re.match('.*.html', filename)): |
5299 | continue | 5411 | continue |
5300 | file = os.path.join(dirname, filename) | 5412 | file = os.path.join(dirname, filename) |
5301 | html = open(file, 'r').read(10000) | 5413 | html = open(file, 'r').read() |
5302 | suspend = find_in_html(html, | 5414 | suspend = find_in_html(html, 'Kernel Suspend', 'ms') |
5303 | ['Kernel Suspend: ', 'Kernel Suspend Time: ']) | 5415 | resume = find_in_html(html, 'Kernel Resume', 'ms') |
5304 | resume = find_in_html(html, | 5416 | line = find_in_html(html, '<div class="stamp">', '</div>') |
5305 | ['Kernel Resume: ', 'Kernel Resume Time: ']) | ||
5306 | line = find_in_html(html, ['<div class="stamp">'], True) | ||
5307 | stmp = line.split() | 5417 | stmp = line.split() |
5308 | if not suspend or not resume or len(stmp) < 4: | 5418 | if not suspend or not resume or len(stmp) != 8: |
5309 | continue | 5419 | continue |
5420 | try: | ||
5421 | dt = datetime.strptime(' '.join(stmp[3:]), '%B %d %Y, %I:%M:%S %p') | ||
5422 | except: | ||
5423 | continue | ||
5424 | tstr = dt.strftime('%Y/%m/%d %H:%M:%S') | ||
5425 | error = find_in_html(html, '<table class="testfail"><tr><td>', '</td>') | ||
5426 | result = 'fail' if error else 'pass' | ||
5427 | ilist = [] | ||
5428 | e = find_in_html(html, 'class="err"[\w=":;\.%\- ]*>', '→</div>', False) | ||
5429 | for i in list(set(e)): | ||
5430 | ilist.append('%sx%d' % (i, e.count(i)) if e.count(i) > 1 else i) | ||
5310 | data = { | 5431 | data = { |
5432 | 'mode': stmp[2], | ||
5311 | 'host': stmp[0], | 5433 | 'host': stmp[0], |
5312 | 'kernel': stmp[1], | 5434 | 'kernel': stmp[1], |
5313 | 'mode': stmp[2], | 5435 | 'time': tstr, |
5314 | 'time': string.join(stmp[3:], ' '), | 5436 | 'result': result, |
5437 | 'issues': ','.join(ilist), | ||
5315 | 'suspend': suspend, | 5438 | 'suspend': suspend, |
5316 | 'resume': resume, | 5439 | 'resume': resume, |
5317 | 'url': os.path.relpath(file, outpath), | 5440 | 'url': os.path.relpath(file, outpath), |
5318 | } | 5441 | } |
5319 | if len(stmp) == 7: | ||
5320 | data['kernel'] = 'unknown' | ||
5321 | data['mode'] = stmp[1] | ||
5322 | data['time'] = string.join(stmp[2:], ' ') | ||
5323 | testruns.append(data) | 5442 | testruns.append(data) |
5324 | outfile = os.path.join(outpath, 'summary.html') | 5443 | outfile = os.path.join(outpath, 'summary.html') |
5325 | print('Summary file: %s' % outfile) | 5444 | print('Summary file: %s' % outfile) |
@@ -5609,11 +5728,12 @@ def printHelp(): | |||
5609 | print(' -modes List available suspend modes') | 5728 | print(' -modes List available suspend modes') |
5610 | print(' -status Test to see if the system is enabled to run this tool') | 5729 | print(' -status Test to see if the system is enabled to run this tool') |
5611 | print(' -fpdt Print out the contents of the ACPI Firmware Performance Data Table') | 5730 | print(' -fpdt Print out the contents of the ACPI Firmware Performance Data Table') |
5731 | print(' -battery Print out battery info (if available)') | ||
5612 | print(' -sysinfo Print out system info extracted from BIOS') | 5732 | print(' -sysinfo Print out system info extracted from BIOS') |
5613 | print(' -devinfo Print out the pm settings of all devices which support runtime suspend') | 5733 | print(' -devinfo Print out the pm settings of all devices which support runtime suspend') |
5614 | print(' -flist Print the list of functions currently being captured in ftrace') | 5734 | print(' -flist Print the list of functions currently being captured in ftrace') |
5615 | print(' -flistall Print all functions capable of being captured in ftrace') | 5735 | print(' -flistall Print all functions capable of being captured in ftrace') |
5616 | print(' -summary directory Create a summary of all test in this dir') | 5736 | print(' -summary dir Create a summary of tests in this dir [-genhtml builds missing html]') |
5617 | print(' [redo]') | 5737 | print(' [redo]') |
5618 | print(' -ftrace ftracefile Create HTML output using ftrace input (used with -dmesg)') | 5738 | print(' -ftrace ftracefile Create HTML output using ftrace input (used with -dmesg)') |
5619 | print(' -dmesg dmesgfile Create HTML output using dmesg (used with -ftrace)') | 5739 | print(' -dmesg dmesgfile Create HTML output using dmesg (used with -ftrace)') |
@@ -5623,8 +5743,9 @@ def printHelp(): | |||
5623 | # ----------------- MAIN -------------------- | 5743 | # ----------------- MAIN -------------------- |
5624 | # exec start (skipped if script is loaded as library) | 5744 | # exec start (skipped if script is loaded as library) |
5625 | if __name__ == '__main__': | 5745 | if __name__ == '__main__': |
5746 | genhtml = False | ||
5626 | cmd = '' | 5747 | cmd = '' |
5627 | simplecmds = ['-sysinfo', '-modes', '-fpdt', '-flist', '-flistall', '-devinfo', '-status'] | 5748 | simplecmds = ['-sysinfo', '-modes', '-fpdt', '-flist', '-flistall', '-devinfo', '-status', '-battery'] |
5628 | if '-f' in sys.argv: | 5749 | if '-f' in sys.argv: |
5629 | sysvals.cgskip = sysvals.configFile('cgskip.txt') | 5750 | sysvals.cgskip = sysvals.configFile('cgskip.txt') |
5630 | # loop through the command line arguments | 5751 | # loop through the command line arguments |
@@ -5660,6 +5781,8 @@ if __name__ == '__main__': | |||
5660 | sysvals.skiphtml = True | 5781 | sysvals.skiphtml = True |
5661 | elif(arg == '-cgdump'): | 5782 | elif(arg == '-cgdump'): |
5662 | sysvals.cgdump = True | 5783 | sysvals.cgdump = True |
5784 | elif(arg == '-genhtml'): | ||
5785 | genhtml = True | ||
5663 | elif(arg == '-addlogs'): | 5786 | elif(arg == '-addlogs'): |
5664 | sysvals.dmesglog = sysvals.ftracelog = True | 5787 | sysvals.dmesglog = sysvals.ftracelog = True |
5665 | elif(arg == '-verbose'): | 5788 | elif(arg == '-verbose'): |
@@ -5856,6 +5979,8 @@ if __name__ == '__main__': | |||
5856 | statusCheck(True) | 5979 | statusCheck(True) |
5857 | elif(cmd == 'fpdt'): | 5980 | elif(cmd == 'fpdt'): |
5858 | getFPDT(True) | 5981 | getFPDT(True) |
5982 | elif(cmd == 'battery'): | ||
5983 | print 'AC Connect: %s\nCharge: %d' % getBattery() | ||
5859 | elif(cmd == 'sysinfo'): | 5984 | elif(cmd == 'sysinfo'): |
5860 | sysvals.printSystemInfo(True) | 5985 | sysvals.printSystemInfo(True) |
5861 | elif(cmd == 'devinfo'): | 5986 | elif(cmd == 'devinfo'): |
@@ -5867,7 +5992,7 @@ if __name__ == '__main__': | |||
5867 | elif(cmd == 'flistall'): | 5992 | elif(cmd == 'flistall'): |
5868 | sysvals.getFtraceFilterFunctions(False) | 5993 | sysvals.getFtraceFilterFunctions(False) |
5869 | elif(cmd == 'summary'): | 5994 | elif(cmd == 'summary'): |
5870 | runSummary(sysvals.outdir, True) | 5995 | runSummary(sysvals.outdir, True, genhtml) |
5871 | sys.exit() | 5996 | sys.exit() |
5872 | 5997 | ||
5873 | # if instructed, re-analyze existing data files | 5998 | # if instructed, re-analyze existing data files |
@@ -5920,7 +6045,7 @@ if __name__ == '__main__': | |||
5920 | print('TEST (%d/%d) COMPLETE' % (i+1, sysvals.multitest['count'])) | 6045 | print('TEST (%d/%d) COMPLETE' % (i+1, sysvals.multitest['count'])) |
5921 | sysvals.logmsg = '' | 6046 | sysvals.logmsg = '' |
5922 | if not sysvals.skiphtml: | 6047 | if not sysvals.skiphtml: |
5923 | runSummary(sysvals.outdir, False) | 6048 | runSummary(sysvals.outdir, False, False) |
5924 | sysvals.sudouser(sysvals.outdir) | 6049 | sysvals.sudouser(sysvals.outdir) |
5925 | else: | 6050 | else: |
5926 | if sysvals.outdir: | 6051 | 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 | |||
28 | import os | 28 | import os |
29 | import time | 29 | import time |
30 | import re | 30 | import re |
31 | import signal | ||
31 | import sys | 32 | import sys |
32 | import getopt | 33 | import getopt |
33 | import Gnuplot | 34 | import Gnuplot |
@@ -78,11 +79,12 @@ def print_help(): | |||
78 | print(' Or') | 79 | print(' Or') |
79 | print(' ./intel_pstate_tracer.py [--cpu cpus] ---trace_file <trace_file> --name <test_name>') | 80 | print(' ./intel_pstate_tracer.py [--cpu cpus] ---trace_file <trace_file> --name <test_name>') |
80 | print(' To generate trace file, parse and plot, use (sudo required):') | 81 | print(' To generate trace file, parse and plot, use (sudo required):') |
81 | print(' sudo ./intel_pstate_tracer.py [-c cpus] -i <interval> -n <test_name>') | 82 | print(' sudo ./intel_pstate_tracer.py [-c cpus] -i <interval> -n <test_name> -m <kbytes>') |
82 | print(' Or') | 83 | print(' Or') |
83 | print(' sudo ./intel_pstate_tracer.py [--cpu cpus] --interval <interval> --name <test_name>') | 84 | print(' sudo ./intel_pstate_tracer.py [--cpu cpus] --interval <interval> --name <test_name> --memory <kbytes>') |
84 | print(' Optional argument:') | 85 | print(' Optional argument:') |
85 | print(' cpus: comma separated list of CPUs') | 86 | print(' cpus: comma separated list of CPUs') |
87 | print(' kbytes: Kilo bytes of memory per CPU to allocate to the trace buffer. Default: 10240') | ||
86 | print(' Output:') | 88 | print(' Output:') |
87 | print(' If not already present, creates a "results/test_name" folder in the current working directory with:') | 89 | print(' If not already present, creates a "results/test_name" folder in the current working directory with:') |
88 | print(' cpu.csv - comma seperated values file with trace contents and some additional calculations.') | 90 | print(' cpu.csv - comma seperated values file with trace contents and some additional calculations.') |
@@ -379,7 +381,7 @@ def clear_trace_file(): | |||
379 | f_handle.close() | 381 | f_handle.close() |
380 | except: | 382 | except: |
381 | print('IO error clearing trace file ') | 383 | print('IO error clearing trace file ') |
382 | quit() | 384 | sys.exit(2) |
383 | 385 | ||
384 | def enable_trace(): | 386 | def enable_trace(): |
385 | """ Enable trace """ | 387 | """ Enable trace """ |
@@ -389,7 +391,7 @@ def enable_trace(): | |||
389 | , 'w').write("1") | 391 | , 'w').write("1") |
390 | except: | 392 | except: |
391 | print('IO error enabling trace ') | 393 | print('IO error enabling trace ') |
392 | quit() | 394 | sys.exit(2) |
393 | 395 | ||
394 | def disable_trace(): | 396 | def disable_trace(): |
395 | """ Disable trace """ | 397 | """ Disable trace """ |
@@ -399,17 +401,17 @@ def disable_trace(): | |||
399 | , 'w').write("0") | 401 | , 'w').write("0") |
400 | except: | 402 | except: |
401 | print('IO error disabling trace ') | 403 | print('IO error disabling trace ') |
402 | quit() | 404 | sys.exit(2) |
403 | 405 | ||
404 | def set_trace_buffer_size(): | 406 | def set_trace_buffer_size(): |
405 | """ Set trace buffer size """ | 407 | """ Set trace buffer size """ |
406 | 408 | ||
407 | try: | 409 | try: |
408 | open('/sys/kernel/debug/tracing/buffer_size_kb' | 410 | with open('/sys/kernel/debug/tracing/buffer_size_kb', 'w') as fp: |
409 | , 'w').write("10240") | 411 | fp.write(memory) |
410 | except: | 412 | except: |
411 | print('IO error setting trace buffer size ') | 413 | print('IO error setting trace buffer size ') |
412 | quit() | 414 | sys.exit(2) |
413 | 415 | ||
414 | def free_trace_buffer(): | 416 | def free_trace_buffer(): |
415 | """ Free the trace buffer memory """ | 417 | """ Free the trace buffer memory """ |
@@ -418,8 +420,8 @@ def free_trace_buffer(): | |||
418 | open('/sys/kernel/debug/tracing/buffer_size_kb' | 420 | open('/sys/kernel/debug/tracing/buffer_size_kb' |
419 | , 'w').write("1") | 421 | , 'w').write("1") |
420 | except: | 422 | except: |
421 | print('IO error setting trace buffer size ') | 423 | print('IO error freeing trace buffer ') |
422 | quit() | 424 | sys.exit(2) |
423 | 425 | ||
424 | def read_trace_data(filename): | 426 | def read_trace_data(filename): |
425 | """ Read and parse trace data """ | 427 | """ Read and parse trace data """ |
@@ -431,7 +433,7 @@ def read_trace_data(filename): | |||
431 | data = open(filename, 'r').read() | 433 | data = open(filename, 'r').read() |
432 | except: | 434 | except: |
433 | print('Error opening ', filename) | 435 | print('Error opening ', filename) |
434 | quit() | 436 | sys.exit(2) |
435 | 437 | ||
436 | for line in data.splitlines(): | 438 | for line in data.splitlines(): |
437 | search_obj = \ | 439 | search_obj = \ |
@@ -489,10 +491,22 @@ def read_trace_data(filename): | |||
489 | # Now seperate the main overall csv file into per CPU csv files. | 491 | # Now seperate the main overall csv file into per CPU csv files. |
490 | split_csv() | 492 | split_csv() |
491 | 493 | ||
494 | def signal_handler(signal, frame): | ||
495 | print(' SIGINT: Forcing cleanup before exit.') | ||
496 | if interval: | ||
497 | disable_trace() | ||
498 | clear_trace_file() | ||
499 | # Free the memory | ||
500 | free_trace_buffer() | ||
501 | sys.exit(0) | ||
502 | |||
503 | signal.signal(signal.SIGINT, signal_handler) | ||
504 | |||
492 | interval = "" | 505 | interval = "" |
493 | filename = "" | 506 | filename = "" |
494 | cpu_list = "" | 507 | cpu_list = "" |
495 | testname = "" | 508 | testname = "" |
509 | memory = "10240" | ||
496 | graph_data_present = False; | 510 | graph_data_present = False; |
497 | 511 | ||
498 | valid1 = False | 512 | valid1 = False |
@@ -501,7 +515,7 @@ valid2 = False | |||
501 | cpu_mask = zeros((MAX_CPUS,), dtype=int) | 515 | cpu_mask = zeros((MAX_CPUS,), dtype=int) |
502 | 516 | ||
503 | try: | 517 | try: |
504 | opts, args = getopt.getopt(sys.argv[1:],"ht:i:c:n:",["help","trace_file=","interval=","cpu=","name="]) | 518 | opts, args = getopt.getopt(sys.argv[1:],"ht:i:c:n:m:",["help","trace_file=","interval=","cpu=","name=","memory="]) |
505 | except getopt.GetoptError: | 519 | except getopt.GetoptError: |
506 | print_help() | 520 | print_help() |
507 | sys.exit(2) | 521 | sys.exit(2) |
@@ -521,6 +535,8 @@ for opt, arg in opts: | |||
521 | elif opt in ("-n", "--name"): | 535 | elif opt in ("-n", "--name"): |
522 | valid2 = True | 536 | valid2 = True |
523 | testname = arg | 537 | testname = arg |
538 | elif opt in ("-m", "--memory"): | ||
539 | memory = arg | ||
524 | 540 | ||
525 | if not (valid1 and valid2): | 541 | if not (valid1 and valid2): |
526 | print_help() | 542 | print_help() |
@@ -569,6 +585,11 @@ current_max_cpu = 0 | |||
569 | 585 | ||
570 | read_trace_data(filename) | 586 | read_trace_data(filename) |
571 | 587 | ||
588 | clear_trace_file() | ||
589 | # Free the memory | ||
590 | if interval: | ||
591 | free_trace_buffer() | ||
592 | |||
572 | if graph_data_present == False: | 593 | if graph_data_present == False: |
573 | print('No valid data to plot') | 594 | print('No valid data to plot') |
574 | sys.exit(2) | 595 | sys.exit(2) |
@@ -593,9 +614,4 @@ for root, dirs, files in os.walk('.'): | |||
593 | for f in files: | 614 | for f in files: |
594 | fix_ownership(f) | 615 | fix_ownership(f) |
595 | 616 | ||
596 | clear_trace_file() | ||
597 | # Free the memory | ||
598 | if interval: | ||
599 | free_trace_buffer() | ||
600 | |||
601 | os.chdir('../../') | 617 | os.chdir('../../') |