1 # SPDX-License-Identifier: GPL-2.0
2 # intel-pt-events.py: Print Intel PT Events including Power Events and PTWRITE
3 # Copyright (c) 2017-2021, Intel Corporation.
5 # This program is free software; you can redistribute it and/or modify it
6 # under the terms and conditions of the GNU General Public License,
7 # version 2, as published by the Free Software Foundation.
9 # This program is distributed in the hope it will be useful, but WITHOUT
10 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14 from __future__
import division
, print_function
23 from libxed
import LibXED
24 from ctypes
import create_string_buffer
, addressof
26 sys
.path
.append(os
.environ
['PERF_EXEC_PATH'] + \
27 '/scripts/python/Perf-Trace-Util/lib/Perf/Trace')
29 from perf_trace_context
import perf_set_itrace_options
, \
30 perf_sample_insn
, perf_sample_srccode
33 broken_pipe_exception
= BrokenPipeError
35 broken_pipe_exception
= IOError
39 glb_disassembler
= None
41 glb_source_file_name
= None
42 glb_line_number
= None
50 def get_optional_null(perf_dict
, field
):
51 if field
in perf_dict
:
52 return perf_dict
[field
]
55 def get_optional_zero(perf_dict
, field
):
56 if field
in perf_dict
:
57 return perf_dict
[field
]
60 def get_optional_bytes(perf_dict
, field
):
61 if field
in perf_dict
:
62 return perf_dict
[field
]
65 def get_optional(perf_dict
, field
):
66 if field
in perf_dict
:
67 return perf_dict
[field
]
70 def get_offset(perf_dict
, field
):
71 if field
in perf_dict
:
72 return "+%#x" % perf_dict
[field
]
76 ap
= argparse
.ArgumentParser(usage
= "", add_help
= False)
77 ap
.add_argument("--insn-trace", action
='store_true')
78 ap
.add_argument("--src-trace", action
='store_true')
79 ap
.add_argument("--all-switch-events", action
='store_true')
80 ap
.add_argument("--interleave", type=int, nargs
='?', const
=4, default
=0)
84 glb_args
= ap
.parse_args()
85 if glb_args
.insn_trace
:
86 print("Intel PT Instruction Trace")
89 elif glb_args
.src_trace
:
90 print("Intel PT Source Trace")
95 print("Intel PT Branch Trace, Power Events, Event Trace and PTWRITE")
97 global glb_disassembler
99 glb_disassembler
= LibXED()
101 glb_disassembler
= None
102 perf_set_itrace_options(perf_script_context
, itrace
)
105 if glb_args
.interleave
:
106 flush_stashed_output()
109 def trace_unhandled(event_name
, context
, event_fields_dict
):
110 print(' '.join(['%s=%s'%(k
,str(v
))for k
,v
in sorted(event_fields_dict
.items())]))
113 global glb_stash_dict
114 global glb_output_pos
115 output_str
= glb_output
.getvalue()[glb_output_pos
:]
119 if glb_cpu
not in glb_stash_dict
:
120 glb_stash_dict
[glb_cpu
] = []
121 glb_stash_dict
[glb_cpu
].append(output_str
)
123 def flush_stashed_output():
124 global glb_stash_dict
125 while glb_stash_dict
:
126 cpus
= list(glb_stash_dict
.keys())
127 # Output at most glb_args.interleave output strings per cpu
129 items
= glb_stash_dict
[cpu
]
130 countdown
= glb_args
.interleave
131 while len(items
) and countdown
:
132 sys
.stdout
.write(items
[0])
136 del glb_stash_dict
[cpu
]
138 def print_ptwrite(raw_buf
):
139 data
= struct
.unpack_from("<IQ", raw_buf
)
144 s
= payload
.to_bytes(8, "little").decode("ascii").rstrip("\x00")
145 if not s
.isprintable():
149 print("IP: %u payload: %#x" % (exact_ip
, payload
), s
, end
=' ')
151 def print_cbr(raw_buf
):
152 data
= struct
.unpack_from("<BBBBII", raw_buf
)
154 f
= (data
[4] + 500) / 1000
155 p
= ((cbr
* 1000 / data
[2]) + 5) / 10
156 print("%3u freq: %4u MHz (%3u%%)" % (cbr
, f
, p
), end
=' ')
158 def print_mwait(raw_buf
):
159 data
= struct
.unpack_from("<IQ", raw_buf
)
161 hints
= payload
& 0xff
162 extensions
= (payload
>> 32) & 0x3
163 print("hints: %#x extensions: %#x" % (hints
, extensions
), end
=' ')
165 def print_pwre(raw_buf
):
166 data
= struct
.unpack_from("<IQ", raw_buf
)
168 hw
= (payload
>> 7) & 1
169 cstate
= (payload
>> 12) & 0xf
170 subcstate
= (payload
>> 8) & 0xf
171 print("hw: %u cstate: %u sub-cstate: %u" % (hw
, cstate
, subcstate
),
174 def print_exstop(raw_buf
):
175 data
= struct
.unpack_from("<I", raw_buf
)
178 print("IP: %u" % (exact_ip
), end
=' ')
180 def print_pwrx(raw_buf
):
181 data
= struct
.unpack_from("<IQ", raw_buf
)
183 deepest_cstate
= payload
& 0xf
184 last_cstate
= (payload
>> 4) & 0xf
185 wake_reason
= (payload
>> 8) & 0xf
186 print("deepest cstate: %u last cstate: %u wake reason: %#x" %
187 (deepest_cstate
, last_cstate
, wake_reason
), end
=' ')
189 def print_psb(raw_buf
):
190 data
= struct
.unpack_from("<IQ", raw_buf
)
192 print("offset: %#x" % (offset
), end
=' ')
194 glb_cfe
= ["", "INTR", "IRET", "SMI", "RSM", "SIPI", "INIT", "VMENTRY", "VMEXIT",
195 "VMEXIT_INTR", "SHUTDOWN", "", "UINT", "UIRET"] + [""] * 18
196 glb_evd
= ["", "PFA", "VMXQ", "VMXR"] + [""] * 60
198 def print_evt(raw_buf
):
199 data
= struct
.unpack_from("<BBH", raw_buf
)
201 ip_flag
= (data
[0] & 0x80) >> 7
206 print(" cfe: %s IP: %u vector: %u" % (s
, ip_flag
, vector
), end
=' ')
208 print(" cfe: %u IP: %u vector: %u" % (typ
, ip_flag
, vector
), end
=' ')
210 for i
in range(evd_cnt
):
211 data
= struct
.unpack_from("<QQ", raw_buf
)
215 print("%s: %#x" % (s
, data
[1]), end
=' ')
217 print("EVD_%u: %#x" % (et
, data
[1]), end
=' ')
219 def print_iflag(raw_buf
):
220 data
= struct
.unpack_from("<IQ", raw_buf
)
222 old_iflag
= iflag ^
1
223 via_branch
= data
[0] & 2
229 print("IFLAG: %u->%u %s branch" % (old_iflag
, iflag
, s
), end
=' ')
231 def common_start_str(comm
, sample
):
236 if "machine_pid" in sample
:
237 machine_pid
= sample
["machine_pid"]
238 vcpu
= sample
["vcpu"]
239 return "VM:%5d VCPU:%03d %16s %5u/%-5u [%03u] %9u.%09u " % (machine_pid
, vcpu
, comm
, pid
, tid
, cpu
, ts
/ 1000000000, ts
%1000000000)
241 return "%16s %5u/%-5u [%03u] %9u.%09u " % (comm
, pid
, tid
, cpu
, ts
/ 1000000000, ts
%1000000000)
243 def print_common_start(comm
, sample
, name
):
244 flags_disp
= get_optional_null(sample
, "flags_disp")
246 # period = sample["period"]
247 # phys_addr = sample["phys_addr"]
248 # weight = sample["weight"]
249 # transaction = sample["transaction"]
250 # cpumode = get_optional_zero(sample, "cpumode")
251 print(common_start_str(comm
, sample
) + "%8s %21s" % (name
, flags_disp
), end
=' ')
253 def print_instructions_start(comm
, sample
):
254 if "x" in get_optional_null(sample
, "flags"):
255 print(common_start_str(comm
, sample
) + "x", end
=' ')
257 print(common_start_str(comm
, sample
), end
=' ')
259 def disassem(insn
, ip
):
260 inst
= glb_disassembler
.Instruction()
261 glb_disassembler
.SetMode(inst
, 0) # Assume 64-bit
262 buf
= create_string_buffer(64)
264 return glb_disassembler
.DisassembleOne(inst
, addressof(buf
), len(insn
), ip
)
266 def print_common_ip(param_dict
, sample
, symbol
, dso
):
268 offs
= get_offset(param_dict
, "symoff")
269 if "cyc_cnt" in sample
:
270 cyc_cnt
= sample
["cyc_cnt"]
271 insn_cnt
= get_optional_zero(sample
, "insn_cnt")
272 ipc_str
= " IPC: %#.2f (%u/%u)" % (insn_cnt
/ cyc_cnt
, insn_cnt
, cyc_cnt
)
275 if glb_insn
and glb_disassembler
is not None:
276 insn
= perf_sample_insn(perf_script_context
)
277 if insn
and len(insn
):
278 cnt
, text
= disassem(insn
, ip
)
279 byte_str
= ("%x" % ip
).rjust(16)
280 if sys
.version_info
.major
>= 3:
282 byte_str
+= " %02x" % insn
[k
]
284 for k
in xrange(cnt
):
285 byte_str
+= " %02x" % ord(insn
[k
])
286 print("%-40s %-30s" % (byte_str
, text
), end
=' ')
287 print("%s%s (%s)" % (symbol
, offs
, dso
), end
=' ')
289 print("%16x %s%s (%s)" % (ip
, symbol
, offs
, dso
), end
=' ')
290 if "addr_correlates_sym" in sample
:
291 addr
= sample
["addr"]
292 dso
= get_optional(sample
, "addr_dso")
293 symbol
= get_optional(sample
, "addr_symbol")
294 offs
= get_offset(sample
, "addr_symoff")
295 print("=> %x %s%s (%s)%s" % (addr
, symbol
, offs
, dso
, ipc_str
))
299 def print_srccode(comm
, param_dict
, sample
, symbol
, dso
, with_insn
):
301 if symbol
== "[unknown]":
302 start_str
= common_start_str(comm
, sample
) + ("%x" % ip
).rjust(16).ljust(40)
304 offs
= get_offset(param_dict
, "symoff")
305 start_str
= common_start_str(comm
, sample
) + (symbol
+ offs
).ljust(40)
307 if with_insn
and glb_insn
and glb_disassembler
is not None:
308 insn
= perf_sample_insn(perf_script_context
)
309 if insn
and len(insn
):
310 cnt
, text
= disassem(insn
, ip
)
311 start_str
+= text
.ljust(30)
313 global glb_source_file_name
314 global glb_line_number
317 source_file_name
, line_number
, source_line
= perf_sample_srccode(perf_script_context
)
319 if glb_line_number
== line_number
and glb_source_file_name
== source_file_name
:
322 if len(source_file_name
) > 40:
323 src_file
= ("..." + source_file_name
[-37:]) + " "
325 src_file
= source_file_name
.ljust(41)
326 if source_line
is None:
327 src_str
= src_file
+ str(line_number
).rjust(4) + " <source not found>"
329 src_str
= src_file
+ str(line_number
).rjust(4) + " " + source_line
337 glb_line_number
= line_number
338 glb_source_file_name
= source_file_name
340 print(start_str
, src_str
)
342 def do_process_event(param_dict
):
343 sample
= param_dict
["sample"]
344 raw_buf
= param_dict
["raw_buf"]
345 comm
= param_dict
["comm"]
346 name
= param_dict
["ev_name"]
348 # callchain = param_dict["callchain"]
349 # brstack = param_dict["brstack"]
350 # brstacksym = param_dict["brstacksym"]
351 # event_attr = param_dict["attr"]
353 # Symbol and dso info are not always resolved
354 dso
= get_optional(param_dict
, "dso")
355 symbol
= get_optional(param_dict
, "symbol")
358 if cpu
in glb_switch_str
:
359 print(glb_switch_str
[cpu
])
360 del glb_switch_str
[cpu
]
362 if name
.startswith("instructions"):
364 print_srccode(comm
, param_dict
, sample
, symbol
, dso
, True)
366 print_instructions_start(comm
, sample
)
367 print_common_ip(param_dict
, sample
, symbol
, dso
)
368 elif name
.startswith("branches"):
370 print_srccode(comm
, param_dict
, sample
, symbol
, dso
, False)
372 print_common_start(comm
, sample
, name
)
373 print_common_ip(param_dict
, sample
, symbol
, dso
)
374 elif name
== "ptwrite":
375 print_common_start(comm
, sample
, name
)
376 print_ptwrite(raw_buf
)
377 print_common_ip(param_dict
, sample
, symbol
, dso
)
379 print_common_start(comm
, sample
, name
)
381 print_common_ip(param_dict
, sample
, symbol
, dso
)
382 elif name
== "mwait":
383 print_common_start(comm
, sample
, name
)
385 print_common_ip(param_dict
, sample
, symbol
, dso
)
387 print_common_start(comm
, sample
, name
)
389 print_common_ip(param_dict
, sample
, symbol
, dso
)
390 elif name
== "exstop":
391 print_common_start(comm
, sample
, name
)
392 print_exstop(raw_buf
)
393 print_common_ip(param_dict
, sample
, symbol
, dso
)
395 print_common_start(comm
, sample
, name
)
397 print_common_ip(param_dict
, sample
, symbol
, dso
)
399 print_common_start(comm
, sample
, name
)
401 print_common_ip(param_dict
, sample
, symbol
, dso
)
403 print_common_start(comm
, sample
, name
)
405 print_common_ip(param_dict
, sample
, symbol
, dso
)
406 elif name
== "iflag":
407 print_common_start(comm
, sample
, name
)
409 print_common_ip(param_dict
, sample
, symbol
, dso
)
411 print_common_start(comm
, sample
, name
)
412 print_common_ip(param_dict
, sample
, symbol
, dso
)
414 def interleave_events(param_dict
):
418 global glb_output_pos
420 sample
= param_dict
["sample"]
421 glb_cpu
= sample
["cpu"]
426 flush_stashed_output()
429 with contextlib
.redirect_stdout(io
.StringIO()) as glb_output
:
430 do_process_event(param_dict
)
434 def process_event(param_dict
):
436 if glb_args
.interleave
:
437 interleave_events(param_dict
)
439 do_process_event(param_dict
)
440 except broken_pipe_exception
:
441 # Stop python printing broken pipe errors and traceback
442 sys
.stdout
= open(os
.devnull
, 'w')
445 def auxtrace_error(typ
, code
, cpu
, pid
, tid
, ip
, ts
, msg
, cpumode
, *x
):
446 if glb_args
.interleave
:
447 flush_stashed_output()
448 if len(x
) >= 2 and x
[0]:
456 print("VM:%5d VCPU:%03d %16s %5u/%-5u [%03u] %9u.%09u error type %u code %u: %s ip 0x%16x" %
457 (machine_pid
, vcpu
, "Trace error", pid
, tid
, cpu
, ts
/ 1000000000, ts
%1000000000, typ
, code
, msg
, ip
))
459 print("%16s %5u/%-5u [%03u] %9u.%09u error type %u code %u: %s ip 0x%16x" %
460 ("Trace error", pid
, tid
, cpu
, ts
/ 1000000000, ts
%1000000000, typ
, code
, msg
, ip
))
461 except broken_pipe_exception
:
462 # Stop python printing broken pipe errors and traceback
463 sys
.stdout
= open(os
.devnull
, 'w')
466 def context_switch(ts
, cpu
, pid
, tid
, np_pid
, np_tid
, machine_pid
, out
, out_preempt
, *x
):
467 if glb_args
.interleave
:
468 flush_stashed_output()
470 out_str
= "Switch out "
472 out_str
= "Switch In "
474 preempt_str
= "preempt"
477 if len(x
) >= 2 and x
[0]:
482 if machine_pid
== -1:
485 machine_str
= "machine PID %d" % machine_pid
487 machine_str
= "machine PID %d VCPU %d" % (machine_pid
, vcpu
)
488 switch_str
= "%16s %5d/%-5d [%03u] %9u.%09u %5d/%-5d %s %s" % \
489 (out_str
, pid
, tid
, cpu
, ts
/ 1000000000, ts
%1000000000, np_pid
, np_tid
, machine_str
, preempt_str
)
490 if glb_args
.all_switch_events
:
493 global glb_switch_str
494 glb_switch_str
[cpu
] = switch_str