From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 8686DFED3F3 for ; Fri, 24 Apr 2026 16:50:30 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender:List-Subscribe:List-Help :List-Post:List-Archive:List-Unsubscribe:List-Id:Content-Type:Cc:To:From: Subject:Message-ID:References:Mime-Version:In-Reply-To:Date:Reply-To: Content-Transfer-Encoding:Content-ID:Content-Description:Resent-Date: Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:List-Owner; bh=d7S+OTPb5S4u3s/d56w9EK7O0PbnJggqb2yBbl0x9VU=; b=RQc6JWrciF5ErGC5Hjt/PPONuK 2QEeF/BnZKKt2T1+4HKBbmQ8s4YsZVYcA2IYvkoLRMuY5QEsXKZ5b02GukQcpwMnHsX4vu4taOw0Y OG+UPfd9Mn2odDEtw6e7YnIOyG9zmUT1Dze5ul3VihoUWxisiswYugmHdv+q07I8JkZ4a8/AZ4WMZ 0GIYlGBs9ac7j0YlpfnTdeddppvUCGsl838zT1CXSzjsar8JSwabs0gh8TxFLiEuTqlVnGHtPnibJ nHxJYaRrzbN3Yb8/AZzPgigHUQMeqlC7vIgFqvuf819pcIrFfMem5nYuriBOQrJ8DpUQDITVWDGp0 zoP2PPcA==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.98.2 #2 (Red Hat Linux)) id 1wGJjM-0000000DVeu-437e; Fri, 24 Apr 2026 16:50:17 +0000 Received: from desiato.infradead.org ([2001:8b0:10b:1:d65d:64ff:fe57:4e05]) by bombadil.infradead.org with esmtps (Exim 4.98.2 #2 (Red Hat Linux)) id 1wGJiL-0000000DUVy-2CLC for linux-arm-kernel@bombadil.infradead.org; Fri, 24 Apr 2026 16:49:13 +0000 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=infradead.org; s=desiato.20200630; h=Content-Type:Cc:To:From:Subject: Message-ID:References:Mime-Version:In-Reply-To:Date:Sender:Reply-To: Content-Transfer-Encoding:Content-ID:Content-Description; bh=d7S+OTPb5S4u3s/d56w9EK7O0PbnJggqb2yBbl0x9VU=; b=DR8+w0dFDj5EaABH/7kGoJbLL9 iiqlJ2o1lXhm5+dWgb+ELTPDI8/dgrAjXCA7hDqBZPOf4I05Xz7q8xe5thXR2spxLv52BAx+mgpWK 7EwVucflhyomtC2+LzIhU1bvsrj/ApYEjb0Yn7MnJWLbt3vWan5GEkC3FDpCa4tRT/aJD7qsnVrUC GRQ3Qb41fzaPHmQFJCcBcfKTYZqo/hKVAJ5hHh8JaIBByra/m2JItff3rzFd11rsRgYXXyiHAsVIy 2Hx4301v53+gKA2CKJwElUt2h/psTV7IN0lzDdfEOzHgaJORw4gWda6f4TWbIjkVvqlrhtBEXnX1u RMeBS/Pw==; Received: from mail-dy1-x134a.google.com ([2607:f8b0:4864:20::134a]) by desiato.infradead.org with esmtps (Exim 4.98.2 #2 (Red Hat Linux)) id 1wGJiI-0000000F9Tf-1cZK for linux-arm-kernel@lists.infradead.org; Fri, 24 Apr 2026 16:49:12 +0000 Received: by mail-dy1-x134a.google.com with SMTP id 5a478bee46e88-2c0f6593ef5so9932049eec.1 for ; Fri, 24 Apr 2026 09:49:09 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1777049348; x=1777654148; darn=lists.infradead.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=d7S+OTPb5S4u3s/d56w9EK7O0PbnJggqb2yBbl0x9VU=; b=cgpeIIca0NgT/WZ5aRGKw2Mb3NnYsiwWjDjznOB4EjdFKI55tBWJFHYnhp1qTCuiF3 QTBvID1I0DBRbmfIon2W16Bh/uUiANRnKVQyU5oX407EhyhTB7QUHW+BieD5c2QF7WFV R+u4rK2wJDHvnQgqAN9+PulgjFQyrtghVN4LgCIGQUe7CtuTjV/Gzf69ZhRoXyRxTQtM UQmwH2fwVCdEX05Tz6z7K0rmoWPxK4HvdoPNDwbYBNaFmn9QBaLiBzPPIYIB0q07fRd3 v9GSxePLxU8gw5rMLZECbZ9zTOhifgDesFu+7Nt+XcFpjRnwdXUMMQQHpdvj+1zYlDg3 Qhtg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1777049348; x=1777654148; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=d7S+OTPb5S4u3s/d56w9EK7O0PbnJggqb2yBbl0x9VU=; b=SRxMm+o8K04JeyJjk0qJM2r9mKmyxRyxXK55srMPEqHynSlgodLH/hT6EwHpJCWU4b zAZ3HuP7UX/qx1htlNUMFVowmYHIJGvhc2xwpPDGmKYbQ7nX6l2ak+b+diFTQKT8/MI7 MRQTYgSAdOafgU3V643TLGi0Uj0cmuJx7tqqrxHSUC9iy2dyDERo6jJGS2J9PWrhWc8e Gg4Be62Ge5/6a08MhAELla90c3GN3JcwnTMeT991zIHlsi3dqLlsFZZhb+GXu1+7XfFs xxx9CSLqZyEZpMBY/WWwY+X7q19GZbC5q6EeXUfUi1F8Ya6UoRmn1heqJl6AVSUFef/k SC0Q== X-Forwarded-Encrypted: i=1; AFNElJ89ItHIlH8xSM9WuCE9+T1bRTXgE5DouiRSoqMl110aUQgaad0UqAoKm+mHidFAoQOxd4l7JQ8TsLDlfBpAfhf5@lists.infradead.org X-Gm-Message-State: AOJu0Yxwuo/DDsr1YYKePeT37MeXWz39sQkurfA8ymb+KlRqKe3bQWEt IJprLbZlRGrnNKlc6o5pqVDbfqDg/RTKGhShcrZrFPPBaj429GEhwNa/S1DAMkY09Qy6NFXb0aN 6AZSgi9DJ9g== X-Received: from dyer22.prod.google.com ([2002:a05:7300:2316:b0:2d9:e328:e0db]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:7301:168a:b0:2d1:815f:19c1 with SMTP id 5a478bee46e88-2e479c0427dmr15813906eec.15.1777049347744; Fri, 24 Apr 2026 09:49:07 -0700 (PDT) Date: Fri, 24 Apr 2026 09:47:01 -0700 In-Reply-To: <20260424164721.2229025-1-irogers@google.com> Mime-Version: 1.0 References: <20260423163406.1779809-1-irogers@google.com> <20260424164721.2229025-1-irogers@google.com> X-Mailer: git-send-email 2.54.0.545.g6539524ca2-goog Message-ID: <20260424164721.2229025-40-irogers@google.com> Subject: [PATCH v5 39/58] perf intel-pt-events: Port intel-pt-events/libxed to use python module From: Ian Rogers To: acme@kernel.org, adrian.hunter@intel.com, james.clark@linaro.org, leo.yan@linux.dev, namhyung@kernel.org, tmricht@linux.ibm.com Cc: alice.mei.rogers@gmail.com, dapeng1.mi@linux.intel.com, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org, mingo@redhat.com, peterz@infradead.org, Ian Rogers Content-Type: text/plain; charset="UTF-8" X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20260424_174910_508929_3A94E320 X-CRM114-Status: GOOD ( 17.59 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+linux-arm-kernel=archiver.kernel.org@lists.infradead.org Ported from tools/perf/scripts/python/. - Refactored intel-pt-events.py to use a class structure to eliminate global state and improve maintainability. - Removed Python 2 compatibility checks. - Renamed methods in libxed.py to snake_case (Instruction -> instruction, SetMode -> set_mode, DisassembleOne -> disassemble_one) to comply with pylint. Assisted-by: Gemini:gemini-3.1-pro-preview Signed-off-by: Ian Rogers --- v2: 1. Robustness in print_cbr : Added checks to ensure raw_buf is at least 12 bytes long and that the frequency divisor is not zero, avoiding struct.error and ZeroDivisionError . 2. Robustness in print_evt : Added buffer length checks in the loop to prevent struct.error if event data count exceeds available buffer. 3. Buffer Handling in disassem : Used ctypes.create_string_buffer(insn, 64) to properly initialize the buffer with raw bytes, preventing truncation on \x00 bytes. 4. Corrected Field Names: Reverted short names to sample_ip , sample_time , and sample_cpu across multiple methods. 5. Comm Resolution: Used session.process(sample.sample_pid).comm() to get the thread name, rather than failing back to "Unknown" . 6. Event Name Cleanup: Stripped evsel( and ) from event names. 7. Fixed Broken Pipe Handling: Prevented sys.stdout from being closed before exiting in the handler. 8. Eliminated Hardcoded Offset in libxed.py : Added xed_decoded_inst_get_length from the official LibXED API rather than relying on the hardcoded byte offset 166 . --- tools/perf/python/intel-pt-events.py | 435 +++++++++++++++++++++++++++ tools/perf/python/libxed.py | 122 ++++++++ 2 files changed, 557 insertions(+) create mode 100755 tools/perf/python/intel-pt-events.py create mode 100755 tools/perf/python/libxed.py diff --git a/tools/perf/python/intel-pt-events.py b/tools/perf/python/intel-pt-events.py new file mode 100755 index 000000000000..682bf80becfe --- /dev/null +++ b/tools/perf/python/intel-pt-events.py @@ -0,0 +1,435 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0 +""" +Print Intel PT Events including Power Events and PTWRITE. +Ported from tools/perf/scripts/python/intel-pt-events.py +""" + +import argparse +import contextlib +from ctypes import addressof, create_string_buffer +import io +import os +import struct +import sys +from typing import Any, Optional +import perf + +# Try to import LibXED from legacy directory if available in PYTHONPATH +try: + from libxed import LibXED # type: ignore +except ImportError: + LibXED = None # type: ignore + + +class IntelPTAnalyzer: + """Analyzes Intel PT events and prints details.""" + + def __init__(self, cfg: argparse.Namespace): + self.args = cfg + self.session: Optional[perf.session] = None + self.insn = False + self.src = False + self.source_file_name: Optional[str] = None + self.line_number: int = 0 + self.dso: Optional[str] = None + self.stash_dict: dict[int, list[str]] = {} + self.output: Any = None + self.output_pos: int = 0 + self.cpu: int = -1 + self.time: int = 0 + self.switch_str: dict[int, str] = {} + + if cfg.insn_trace: + print("Intel PT Instruction Trace") + self.insn = True + elif cfg.src_trace: + print("Intel PT Source Trace") + self.insn = True + self.src = True + else: + print("Intel PT Branch Trace, Power Events, Event Trace and PTWRITE") + + self.disassembler: Any = None + if self.insn and LibXED is not None: + try: + self.disassembler = LibXED() + except Exception as e: + print(f"Failed to initialize LibXED: {e}") + self.disassembler = None + + def print_ptwrite(self, raw_buf: bytes) -> None: + """Print PTWRITE data.""" + data = struct.unpack_from(" None: + """Print CBR data.""" + if len(raw_buf) < 12: + return + data = struct.unpack_from(" None: + """Print MWAIT data.""" + data = struct.unpack_from("> 32) & 0x3 + print(f"hints: {hints:#x} extensions: {extensions:#x}", end=' ') + + def print_pwre(self, raw_buf: bytes) -> None: + """Print PWRE data.""" + data = struct.unpack_from("> 7) & 1 + cstate = (payload >> 12) & 0xf + subcstate = (payload >> 8) & 0xf + print(f"hw: {hw} cstate: {cstate} sub-cstate: {subcstate}", end=' ') + + def print_exstop(self, raw_buf: bytes) -> None: + """Print EXSTOP data.""" + data = struct.unpack_from(" None: + """Print PWRX data.""" + data = struct.unpack_from("> 4) & 0xf + wake_reason = (payload >> 8) & 0xf + print(f"deepest cstate: {deepest_cstate} last cstate: {last_cstate} " + f"wake reason: {wake_reason:#x}", end=' ') + + def print_psb(self, raw_buf: bytes) -> None: + """Print PSB data.""" + data = struct.unpack_from(" None: + """Print EVT data.""" + glb_cfe = ["", "INTR", "IRET", "SMI", "RSM", "SIPI", "INIT", "VMENTRY", "VMEXIT", + "VMEXIT_INTR", "SHUTDOWN", "", "UINT", "UIRET"] + [""] * 18 + glb_evd = ["", "PFA", "VMXQ", "VMXR"] + [""] * 60 + + data = struct.unpack_from("> 7 + vector = data[1] + evd_cnt = data[2] + s = glb_cfe[typ] + if s: + print(f" cfe: {s} IP: {ip_flag} vector: {vector}", end=' ') + else: + print(f" cfe: {typ} IP: {ip_flag} vector: {vector}", end=' ') + pos = 4 + for _ in range(evd_cnt): + if len(raw_buf) < pos + 16: + break + data = struct.unpack_from(" None: + """Print IFLAG data.""" + data = struct.unpack_from("{iflag} {s} branch", end=' ') + + def common_start_str(self, comm: str, sample: perf.sample_event) -> str: + """Return common start string for display.""" + ts = sample.sample_time + cpu = sample.sample_cpu + pid = sample.sample_pid + tid = sample.tid + machine_pid = getattr(sample, "machine_pid", 0) + if machine_pid: + vcpu = getattr(sample, "vcpu", -1) + return (f"VM:{machine_pid:5d} VCPU:{vcpu:03d} {comm:>16s} {pid:5u}/{tid:<5u} " + f"[{cpu:03u}] {ts // 1000000000:9u}.{ts % 1000000000:09u} ") + return (f"{comm:>16s} {pid:5u}/{tid:<5u} [{cpu:03u}] " + f"{ts // 1000000000:9u}.{ts % 1000000000:09u} ") + + def print_common_start(self, comm: str, sample: perf.sample_event, name: str) -> None: + """Print common start info.""" + flags_disp = getattr(sample, "flags_disp", "") + print(self.common_start_str(comm, sample) + f"{name:>8s} {flags_disp:>21s}", end=' ') + + def print_instructions_start(self, comm: str, sample: perf.sample_event) -> None: + """Print instructions start info.""" + flags = getattr(sample, "flags_disp", "") + if "x" in flags: + print(self.common_start_str(comm, sample) + "x", end=' ') + else: + print(self.common_start_str(comm, sample), end=' ') + + def disassem(self, insn: bytes, ip: int) -> tuple[int, str]: + """Disassemble instruction using LibXED.""" + inst = self.disassembler.instruction() + self.disassembler.set_mode(inst, 0) # Assume 64-bit + buf = create_string_buffer(insn, 64) + return self.disassembler.disassemble_one(inst, addressof(buf), len(insn), ip) + + def print_common_ip(self, sample: perf.sample_event, symbol: str, dso: str) -> None: + """Print IP and symbol info.""" + ip = sample.sample_ip + offs = f"+{sample.symoff:#x}" if hasattr(sample, "symoff") else "" + cyc_cnt = getattr(sample, "cyc_cnt", 0) + if cyc_cnt: + insn_cnt = getattr(sample, "insn_cnt", 0) + ipc_str = f" IPC: {insn_cnt / cyc_cnt:#.2f} ({insn_cnt}/{cyc_cnt})" + else: + ipc_str = "" + + if self.insn and self.disassembler is not None: + try: + insn = sample.insn() + except AttributeError: + insn = None + if insn: + cnt, text = self.disassem(insn, ip) + byte_str = (f"{ip:x}").rjust(16) + for k in range(cnt): + byte_str += f" {insn[k]:02x}" + print(f"{byte_str:-40s} {text:-30s}", end=' ') + print(f"{symbol}{offs} ({dso})", end=' ') + else: + print(f"{ip:16x} {symbol}{offs} ({dso})", end=' ') + + addr_correlates_sym = getattr(sample, "addr_correlates_sym", False) + if addr_correlates_sym: + addr = sample.addr + addr_dso = getattr(sample, "addr_dso", "[unknown]") + addr_symbol = getattr(sample, "addr_symbol", "[unknown]") + addr_offs = f"+{sample.addr_symoff:#x}" if hasattr(sample, "addr_symoff") else "" + print(f"=> {addr:x} {addr_symbol}{addr_offs} ({addr_dso}){ipc_str}") + else: + print(ipc_str) + + def print_srccode(self, comm: str, sample: perf.sample_event, + symbol: str, dso: str, with_insn: bool) -> None: + """Print source code info.""" + ip = sample.sample_ip + if symbol == "[unknown]": + start_str = self.common_start_str(comm, sample) + (f"{ip:x}").rjust(16).ljust(40) + else: + offs = f"+{sample.symoff:#x}" if hasattr(sample, "symoff") else "" + start_str = self.common_start_str(comm, sample) + (symbol + offs).ljust(40) + + if with_insn and self.insn and self.disassembler is not None: + try: + insn = sample.insn() + except AttributeError: + insn = None + if insn: + _, text = self.disassem(insn, ip) + start_str += text.ljust(30) + + try: + source_file_name, line_number, source_line = sample.srccode() + except (AttributeError, ValueError): + source_file_name, line_number, source_line = None, 0, None + + if source_file_name: + if self.line_number == line_number and self.source_file_name == source_file_name: + src_str = "" + else: + if len(source_file_name) > 40: + src_file = ("..." + source_file_name[-37:]) + " " + else: + src_file = source_file_name.ljust(41) + if source_line is None: + src_str = src_file + str(line_number).rjust(4) + " " + else: + src_str = src_file + str(line_number).rjust(4) + " " + source_line + self.dso = None + elif dso == self.dso: + src_str = "" + else: + src_str = dso + self.dso = dso + + self.line_number = line_number + self.source_file_name = source_file_name + print(start_str, src_str) + + def do_process_event(self, sample: perf.sample_event) -> None: + """Process event and print info.""" + comm = "Unknown" + if hasattr(self, 'session') and self.session: + try: + comm = self.session.process(sample.sample_pid).comm() + except Exception: + pass + name = getattr(sample.evsel, 'name', str(sample.evsel)) + if name.startswith("evsel("): + name = name[6:-1] + dso = getattr(sample, "dso", "[unknown]") + symbol = getattr(sample, "symbol", "[unknown]") + + cpu = sample.sample_cpu + if cpu in self.switch_str: + print(self.switch_str[cpu]) + del self.switch_str[cpu] + + try: + raw_buf = sample.raw_buf + except AttributeError: + raw_buf = b"" + + if name.startswith("instructions"): + if self.src: + self.print_srccode(comm, sample, symbol, dso, True) + else: + self.print_instructions_start(comm, sample) + self.print_common_ip(sample, symbol, dso) + elif name.startswith("branches"): + if self.src: + self.print_srccode(comm, sample, symbol, dso, False) + else: + self.print_common_start(comm, sample, name) + self.print_common_ip(sample, symbol, dso) + elif name == "ptwrite": + self.print_common_start(comm, sample, name) + self.print_ptwrite(raw_buf) + self.print_common_ip(sample, symbol, dso) + elif name == "cbr": + self.print_common_start(comm, sample, name) + self.print_cbr(raw_buf) + self.print_common_ip(sample, symbol, dso) + elif name == "mwait": + self.print_common_start(comm, sample, name) + self.print_mwait(raw_buf) + self.print_common_ip(sample, symbol, dso) + elif name == "pwre": + self.print_common_start(comm, sample, name) + self.print_pwre(raw_buf) + self.print_common_ip(sample, symbol, dso) + elif name == "exstop": + self.print_common_start(comm, sample, name) + self.print_exstop(raw_buf) + self.print_common_ip(sample, symbol, dso) + elif name == "pwrx": + self.print_common_start(comm, sample, name) + self.print_pwrx(raw_buf) + self.print_common_ip(sample, symbol, dso) + elif name == "psb": + self.print_common_start(comm, sample, name) + self.print_psb(raw_buf) + self.print_common_ip(sample, symbol, dso) + elif name == "evt": + self.print_common_start(comm, sample, name) + self.print_evt(raw_buf) + self.print_common_ip(sample, symbol, dso) + elif name == "iflag": + self.print_common_start(comm, sample, name) + self.print_iflag(raw_buf) + self.print_common_ip(sample, symbol, dso) + else: + self.print_common_start(comm, sample, name) + self.print_common_ip(sample, symbol, dso) + + def interleave_events(self, sample: perf.sample_event) -> None: + """Interleave output to avoid garbled lines from different CPUs.""" + self.cpu = sample.sample_cpu + ts = sample.sample_time + + if self.time != ts: + self.time = ts + self.flush_stashed_output() + + self.output_pos = 0 + with contextlib.redirect_stdout(io.StringIO()) as self.output: + self.do_process_event(sample) + + self.stash_output() + + def stash_output(self) -> None: + """Stash output for later flushing.""" + output_str = self.output.getvalue()[self.output_pos:] + n = len(output_str) + if n: + self.output_pos += n + if self.cpu not in self.stash_dict: + self.stash_dict[self.cpu] = [] + self.stash_dict[self.cpu].append(output_str) + if len(self.stash_dict[self.cpu]) > 1000: + self.flush_stashed_output() + + def flush_stashed_output(self) -> None: + """Flush stashed output.""" + while self.stash_dict: + cpus = list(self.stash_dict.keys()) + for cpu in cpus: + items = self.stash_dict[cpu] + countdown = self.args.interleave + while len(items) and countdown: + sys.stdout.write(items[0]) + del items[0] + countdown -= 1 + if not items: + del self.stash_dict[cpu] + + def process_event(self, sample: perf.sample_event) -> None: + """Wrapper to handle interleaving and exceptions.""" + try: + if self.args.interleave: + self.interleave_events(sample) + else: + self.do_process_event(sample) + except BrokenPipeError: + # Stop python printing broken pipe errors and traceback + sys.stdout = open(os.devnull, 'w', encoding='utf-8') + sys.exit(1) + + +if __name__ == "__main__": + ap = argparse.ArgumentParser() + ap.add_argument("-i", "--input", default="perf.data", help="Input file name") + ap.add_argument("--insn-trace", action='store_true') + ap.add_argument("--src-trace", action='store_true') + ap.add_argument("--all-switch-events", action='store_true') + ap.add_argument("--interleave", type=int, nargs='?', const=4, default=0) + args = ap.parse_args() + + analyzer = IntelPTAnalyzer(args) + + try: + session = perf.session(perf.data(args.input), sample=analyzer.process_event) + analyzer.session = session + session.process_events() + if args.interleave: + analyzer.flush_stashed_output() + print("End") + except KeyboardInterrupt: + if args.interleave: + analyzer.flush_stashed_output() + print("End") + except Exception as e: + print(f"Error processing events: {e}") diff --git a/tools/perf/python/libxed.py b/tools/perf/python/libxed.py new file mode 100755 index 000000000000..0e622e6959c2 --- /dev/null +++ b/tools/perf/python/libxed.py @@ -0,0 +1,122 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0 +""" +Python wrapper for libxed.so +Ported from tools/perf/scripts/python/libxed.py +""" + +from ctypes import CDLL, Structure, create_string_buffer, addressof, sizeof, \ + c_void_p, c_byte, c_int, c_uint, c_ulonglong + +# To use Intel XED, libxed.so must be present. To build and install +# libxed.so: +# git clone https://github.com/intelxed/mbuild.git mbuild +# git clone https://github.com/intelxed/xed +# cd xed +# ./mfile.py --share +# sudo ./mfile.py --prefix=/usr/local install +# sudo ldconfig +# + + +class XedStateT(Structure): + """xed_state_t structure.""" + _fields_ = [ + ("mode", c_int), + ("width", c_int) + ] + + +class XEDInstruction(): + """Represents a decoded instruction.""" + + def __init__(self, libxed): + # Current xed_decoded_inst_t structure is 192 bytes. Use 512 to allow for future expansion + xedd_t = c_byte * 512 + self.xedd = xedd_t() + self.xedp = addressof(self.xedd) + libxed.xed_decoded_inst_zero(self.xedp) + self.state = XedStateT() + self.statep = addressof(self.state) + # Buffer for disassembled instruction text + self.buffer = create_string_buffer(256) + self.bufferp = addressof(self.buffer) + + +class LibXED(): + """Wrapper for libxed.so.""" + + def __init__(self): + try: + self.libxed = CDLL("libxed.so") + except OSError: + self.libxed = None + if not self.libxed: + try: + self.libxed = CDLL("/usr/local/lib/libxed.so") + except OSError: + self.libxed = None + + if not self.libxed: + raise ImportError("libxed.so not found. Please install Intel XED.") + + self.xed_tables_init = self.libxed.xed_tables_init + self.xed_tables_init.restype = None + self.xed_tables_init.argtypes = [] + + self.xed_decoded_inst_zero = self.libxed.xed_decoded_inst_zero + self.xed_decoded_inst_zero.restype = None + self.xed_decoded_inst_zero.argtypes = [c_void_p] + + self.xed_operand_values_set_mode = self.libxed.xed_operand_values_set_mode + self.xed_operand_values_set_mode.restype = None + self.xed_operand_values_set_mode.argtypes = [c_void_p, c_void_p] + + self.xed_decoded_inst_zero_keep_mode = self.libxed.xed_decoded_inst_zero_keep_mode + self.xed_decoded_inst_zero_keep_mode.restype = None + self.xed_decoded_inst_zero_keep_mode.argtypes = [c_void_p] + + self.xed_decode = self.libxed.xed_decode + self.xed_decode.restype = c_int + self.xed_decode.argtypes = [c_void_p, c_void_p, c_uint] + + self.xed_format_context = self.libxed.xed_format_context + self.xed_format_context.restype = c_uint + self.xed_format_context.argtypes = [ + c_int, c_void_p, c_void_p, c_int, c_ulonglong, c_void_p, c_void_p + ] + + self.xed_decoded_inst_get_length = self.libxed.xed_decoded_inst_get_length + self.xed_decoded_inst_get_length.restype = c_uint + self.xed_decoded_inst_get_length.argtypes = [c_void_p] + + self.xed_tables_init() + + def instruction(self): + """Create a new XEDInstruction.""" + return XEDInstruction(self) + + def set_mode(self, inst, mode): + """Set 32-bit or 64-bit mode.""" + if mode: + inst.state.mode = 4 # 32-bit + inst.state.width = 4 # 4 bytes + else: + inst.state.mode = 1 # 64-bit + inst.state.width = 8 # 8 bytes + self.xed_operand_values_set_mode(inst.xedp, inst.statep) + + def disassemble_one(self, inst, bytes_ptr, bytes_cnt, ip): + """Disassemble one instruction.""" + self.xed_decoded_inst_zero_keep_mode(inst.xedp) + err = self.xed_decode(inst.xedp, bytes_ptr, bytes_cnt) + if err: + return 0, "" + # Use AT&T mode (2), alternative is Intel (3) + ok = self.xed_format_context(2, inst.xedp, inst.bufferp, sizeof(inst.buffer), ip, 0, 0) + if not ok: + return 0, "" + + result = inst.buffer.value.decode('utf-8') + # Return instruction length and the disassembled instruction text + return self.xed_decoded_inst_get_length(inst.xedp), result -- 2.54.0.545.g6539524ca2-goog