|
62 | 62 | |
63 | 63 | """ |
64 | 64 |
|
| 65 | +from __future__ import print_function |
| 66 | + |
65 | 67 | import re |
66 | 68 | import sys |
67 | 69 |
|
68 | 70 | if len(sys.argv) <= 1: |
69 | | - print 'Usage: %s xperfoutput [processname]' % sys.argv[0] |
70 | | - print 'The first parameter is the name of a file containing the results' |
71 | | - print 'of "xperf -i trace.etl". The second (optional) parameter is a' |
72 | | - print 'process name substring filter used to restrict which results are' |
73 | | - print 'shown - only processes that match are displayed.' |
| 71 | + print('Usage: %s xperfoutput [processname]' % sys.argv[0]) |
| 72 | + print('The first parameter is the name of a file containing the results') |
| 73 | + print('of "xperf -i trace.etl". The second (optional) parameter is a') |
| 74 | + print('process name substring filter used to restrict which results are') |
| 75 | + print('shown - only processes that match are displayed.') |
74 | 76 | sys.exit(0) |
75 | 77 |
|
76 | 78 | xperfoutputfilename = sys.argv[1] |
|
95 | 97 | description = None |
96 | 98 |
|
97 | 99 | for x in range(len(l) - 1): |
98 | | - if l[x].startswith(" Pmc,"): |
99 | | - pmc_parts = l[x].split(",") |
| 100 | + if l[x].startswith(' Pmc,'): |
| 101 | + pmc_parts = l[x].split(',') |
100 | 102 | if not description: |
101 | 103 | # Grab the description of the Pmc counter records, see how many counters |
102 | 104 | # there are, and print the description. |
103 | 105 | num_counters = len(pmc_parts) - 3 |
104 | 106 | description = l[x].strip() |
105 | 107 | continue |
106 | | - counters = map(int, pmc_parts[3:]) |
| 108 | + counters = list(map(int, pmc_parts[3:])) |
| 109 | + assert(len(counters) == num_counters) |
107 | 110 | # Look for a CSwitch line. Ideally it will be next, but sometimes an Error: line |
108 | 111 | # might be in-between. |
109 | | - cswitch_line = "" |
110 | | - if l[x+1].startswith(" CSwitch,"): |
| 112 | + cswitch_line = '' |
| 113 | + if l[x+1].startswith(' CSwitch,'): |
111 | 114 | cswitch_line = l[x+1] |
112 | | - elif l[x+1].startswith("Error: ") and l[x+2].startswith(" CSwitch,"): |
| 115 | + elif l[x+1].startswith('Error: ') and l[x+2].startswith(' CSwitch,'): |
113 | 116 | cswitch_line = l[x+2] |
114 | 117 | if cswitch_line: |
115 | | - cswitch_parts = cswitch_line.split(",") |
| 118 | + cswitch_parts = cswitch_line.split(',') |
116 | 119 | CPU = int(cswitch_parts[16].strip()) |
117 | 120 | process = cswitch_parts[2].strip() |
118 | 121 | timeStamp = int(cswitch_parts[1]) |
119 | 122 | # See if we've got previous Pmc records for this CPU: |
120 | | - if countersByCPU.has_key(CPU): |
121 | | - diffs = map(lambda a,b : a - b, counters, countersByCPU[CPU]) |
| 123 | + if CPU in countersByCPU: |
| 124 | + diffs = list(map(lambda a,b : a - b, counters, countersByCPU[CPU])) |
122 | 125 | old_process = cswitch_parts[8].strip() |
123 | 126 | # Sanity checking... |
124 | 127 | if old_process != processByCPU[CPU]: |
125 | | - print "Old process mismatch at line %d, %s versus %s" % (x, old_process, processByCPU[CPU]) |
| 128 | + print('Old process mismatch at line %d, %s versus %s' % (x, old_process, processByCPU[CPU])) |
126 | 129 | sys.exit(0) |
127 | | - if old_process != "Idle ( 0)": |
128 | | - countersByProcess[old_process] = map(lambda x, y: x + y, countersByProcess.get(old_process, num_counters * [0]), diffs) |
| 130 | + if old_process != 'Idle ( 0)': |
| 131 | + countersByProcess[old_process] = list(map(lambda x, y: x + y, countersByProcess.get(old_process, num_counters * [0]), diffs)) |
| 132 | + assert(len(countersByProcess[old_process]) == num_counters) |
129 | 133 | contextSwitchesByProcess[old_process] = contextSwitchesByProcess.get(old_process, 0) + 1 |
130 | 134 | cpuTimeByProcess[old_process] = cpuTimeByProcess.get(old_process, 0) + (timeStamp - lastCSwitchTimeByCPU[CPU]) |
131 | 135 |
|
|
134 | 138 | countersByCPU[CPU] = counters |
135 | 139 | lastLineByCPU[CPU] = x |
136 | 140 | else: |
137 | | - print "Missing cswitch line at line %d" % x |
| 141 | + print('Missing cswitch line at line %d' % x) |
138 | 142 | sys.exit(0) |
139 | 143 |
|
140 | 144 | if len(sys.argv) == 2: |
141 | 145 | mincounter = 500000 |
142 | | - print 'Printing collated data for process names where the second counter exceeds %d' % mincounter |
| 146 | + print('Printing collated data for process names where the second counter exceeds %d' % mincounter) |
143 | 147 | else: |
144 | 148 | substring = sys.argv[2].lower() |
145 | | - print 'Printing per-process-data for processes that contain "%s"' % substring |
| 149 | + print('Printing per-process-data for processes that contain "%s"' % substring) |
146 | 150 |
|
147 | | -counterDescription = "<unknown counters>" |
| 151 | +counterDescription = '<unknown counters>' |
148 | 152 | if description: |
149 | 153 | counterDescription = ',' + ','.join(description.split(',')[3:]) |
150 | 154 |
|
151 | | -print "%43s: cnt1/cnt2%s" % ("Process name", counterDescription) |
| 155 | +format = '%43s: %6.2f%%, [' + ','.join(4 * ['%10d']) + '], %5d context switches, time: %8d' |
| 156 | + |
| 157 | +print('%43s: cnt1/cnt2%s' % ('Process name', counterDescription)) |
152 | 158 | procnameTotals = {} |
153 | 159 | for process in countersByProcess.keys(): |
154 | 160 | totals = countersByProcess[process] |
| 161 | + assert(len(totals) == num_counters) |
155 | 162 | # Extract the .exe name and separate it from the PID. |
156 | | - match = re.match(r"(.*).exe \(\d+\)", process) |
| 163 | + match = re.match(r'(.*).exe \(\d+\)', process) |
| 164 | + summarizeByName = True |
| 165 | + if len(sys.argv) > 2: |
| 166 | + # If we are filtering to a specific process name then we cannot |
| 167 | + # also summarize by name. |
| 168 | + summarizeByName = False |
157 | 169 | if match: |
158 | | - procname = match.groups()[0] |
159 | | - counter0, counter1, contextSwitches, cpuTime = procnameTotals.get(procname, (0, 0, 0, 0)) |
160 | | - procnameTotals[procname] = (counter0 + totals[0], |
161 | | - counter1 + totals[1], |
162 | | - contextSwitches + contextSwitchesByProcess[process], |
163 | | - cpuTime + cpuTimeByProcess[process]) |
| 170 | + if summarizeByName: |
| 171 | + procname = match.groups()[0] |
| 172 | + else: |
| 173 | + procname = process |
| 174 | + # First num_counters values are the CPU performance counters. The next two are contextSwitches and cpuTime. |
| 175 | + data = procnameTotals.get(procname, (num_counters + 2) * [0]) |
| 176 | + # Extend the totals list so that it also contains contextSwitches and cpuTime |
| 177 | + totals += [contextSwitchesByProcess[process], cpuTimeByProcess[process]] |
| 178 | + procnameTotals[procname] = list(map(lambda x, y: x + y, data, totals)) |
164 | 179 | # Filter to the specific process substring if requested. |
165 | 180 | if len(sys.argv) > 2 and process.lower().count(substring) > 0: |
166 | | - print "%43s: %5.2f%%, [%9d,%11d], %5d context switches, time: %8d" % (process, |
167 | | - totals[0] * 100.0 / totals[1], totals[0], totals[1], |
168 | | - contextSwitchesByProcess[process], cpuTimeByProcess[process]) |
| 181 | + totals0, totals1 = procnameTotals[procname][:2] |
| 182 | + args = tuple([procname, totals0 * 100.0 / totals1] + procnameTotals[procname]) |
| 183 | + print(format % args) |
169 | 184 |
|
170 | 185 | if len(sys.argv) == 2: |
171 | 186 | # Put one of the values and the keys into tuples, sort by the selected |
172 | 187 | # value, extract back out into two lists and grab the keys, which are |
173 | 188 | # now sorted by the specified value. The index in the map lambda specifies |
174 | 189 | # which of the values from the stored tuple is used for sorting. |
175 | | - sortingValues = map(lambda x: x[3], procnameTotals.values()) |
176 | | - orderedKeys = list(zip(*sorted(zip(sortingValues, procnameTotals.keys())))[1]) |
| 190 | + sortingValues = list(map(lambda x: x[3], procnameTotals.values())) |
| 191 | + orderedKeys = list(list(zip(*sorted(zip(sortingValues, procnameTotals.keys()))))[1]) |
177 | 192 | orderedKeys.reverse() |
178 | 193 |
|
179 | 194 | for procname in orderedKeys: |
180 | | - totals0, totals1, contextSwitches, cpuTime = procnameTotals[procname] |
| 195 | + totals0, totals1 = procnameTotals[procname][:2] |
181 | 196 | # Arbitrary filtering to just get the most interesting data. |
182 | 197 | if totals1 > mincounter: |
183 | | - print "%43s: %5.2f%%, [%9d,%11d], %5d context switches, time: %8d" % (procname, |
184 | | - totals0 * 100.0 / totals1, totals0, totals1, contextSwitches, cpuTime) |
| 198 | + args = tuple([procname, totals0 * 100.0 / totals1] + procnameTotals[procname]) |
| 199 | + print(format % args) |
0 commit comments