summaryrefslogtreecommitdiff
path: root/tools/perf/python/libxed.py
blob: 900bd3e0184b7af0d8d6fec60e00c26fad8d6a0a (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
#!/usr/bin/env python3
# SPDX-License-Identifier: GPL-2.0
"""
Python wrapper for libxed.so
Ported from tools/perf/scripts/python/libxed.py
"""
from __future__ import annotations

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