diff --git a/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/EventClass.py b/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/EventClass.py index 21a7a129809443..f04ecd22419dd7 100755 --- a/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/EventClass.py +++ b/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/EventClass.py @@ -1,4 +1,6 @@ -# EventClass.py +# EventClass.py - Python extension for perf script, used for +# performance event analysis + # SPDX-License-Identifier: GPL-2.0 # # This is a library defining some events types classes, which could @@ -13,65 +15,115 @@ import struct # Event types, user could add more here -EVTYPE_GENERIC = 0 -EVTYPE_PEBS = 1 # Basic PEBS event -EVTYPE_PEBS_LL = 2 # PEBS event with load latency info -EVTYPE_IBS = 3 +EVTYPE_GENERIC = 0 +EVTYPE_PEBS = 1 # Basic PEBS event +EVTYPE_PEBS_LL = 2 # PEBS event with load latency info +EVTYPE_IBS = 3 # # Currently we don't have good way to tell the event type, but by # the size of raw buffer, raw PEBS event with load latency data's # size is 176 bytes, while the pure PEBS event's size is 144 bytes. # + +# Function to create an event instance based on raw buffer size def create_event(name, comm, dso, symbol, raw_buf): - if (len(raw_buf) == 144): - event = PebsEvent(name, comm, dso, symbol, raw_buf) - elif (len(raw_buf) == 176): - event = PebsNHM(name, comm, dso, symbol, raw_buf) - else: - event = PerfEvent(name, comm, dso, symbol, raw_buf) - - return event - -class PerfEvent(object): - event_num = 0 - def __init__(self, name, comm, dso, symbol, raw_buf, ev_type=EVTYPE_GENERIC): - self.name = name - self.comm = comm - self.dso = dso - self.symbol = symbol - self.raw_buf = raw_buf - self.ev_type = ev_type - PerfEvent.event_num += 1 - - def show(self): - print("PMU event: name=%12s, symbol=%24s, comm=%8s, dso=%12s" % - (self.name, self.symbol, self.comm, self.dso)) + """ + Create an event instance based on raw buffer size. + + Args: + name (str): Event name. + comm (str): Command name. + dso (str): Dynamic shared object. + symbol (str): Symbol name. + raw_buf (bytes): Raw buffer containing event data. + + Returns: + PerfEvent: An instance of the appropriate event class. + """ + if len(raw_buf) == 144: + event = PebsEvent(name, comm, dso, symbol, raw_buf) + elif len(raw_buf) == 176: + event = PebsNHM(name, comm, dso, symbol, raw_buf) + else: + event = PerfEvent(name, comm, dso, symbol, raw_buf) + + return event + +class PerfEvent(): + """ + Base class for all perf event samples. + """ + event_num = 0 + + def __init__(self, name, comm, dso, symbol, raw_buf, ev_type=EVTYPE_GENERIC): + """ + Initialize PerfEvent instance. + + Args: + name (str): Event name. + comm (str): Command name. + dso (str): Dynamic shared object. + symbol (str): Symbol name. + raw_buf (bytes): Raw buffer containing event data. + ev_type (int): Event type identifier. + """ + self.name = name + self.comm = comm + self.dso = dso + self.symbol = symbol + self.raw_buf = raw_buf + self.ev_type = ev_type + PerfEvent.event_num += 1 + + def show(self): + """ + Print event information. + """ + print("PMU event: name=%12s, symbol=%24s, comm=%8s, dso=%12s" % + (self.name, self.symbol, self.comm, self.dso)) # # Basic Intel PEBS (Precise Event-based Sampling) event, whose raw buffer # contains the context info when that event happened: the EFLAGS and # linear IP info, as well as all the registers. # + class PebsEvent(PerfEvent): - pebs_num = 0 - def __init__(self, name, comm, dso, symbol, raw_buf, ev_type=EVTYPE_PEBS): - tmp_buf=raw_buf[0:80] - flags, ip, ax, bx, cx, dx, si, di, bp, sp = struct.unpack('QQQQQQQQQQ', tmp_buf) - self.flags = flags - self.ip = ip - self.ax = ax - self.bx = bx - self.cx = cx - self.dx = dx - self.si = si - self.di = di - self.bp = bp - self.sp = sp - - PerfEvent.__init__(self, name, comm, dso, symbol, raw_buf, ev_type) - PebsEvent.pebs_num += 1 - del tmp_buf + """ + Class for basic Intel PEBS events. + """ + pebs_num = 0 + + def __init__(self, name, comm, dso, symbol, raw_buf, ev_type=EVTYPE_PEBS): + """ + Initialize PebsEvent instance. + + Args: + name (str): Event name. + comm (str): Command name. + dso (str): Dynamic shared object. + symbol (str): Symbol name. + raw_buf (bytes): Raw buffer containing event data. + ev_type (int): Event type identifier. + """ + tmp_buf = raw_buf[0:80] + flags, ipa, reg_ax, reg_bx, reg_cx, reg_dx, reg_si, reg_di, reg_bp, reg_sp = struct.unpack( + 'QQQQQQQQQQ', tmp_buf) + self.flags = flags + self.ipa = ipa + self.reg_ax = reg_ax + self.reg_bx = reg_bx + self.reg_cx = reg_cx + self.reg_dx = reg_dx + self.reg_si = reg_si + self.reg_di = reg_di + self.reg_bp = reg_bp + self.reg_sp = reg_sp + + PerfEvent.__init__(name, comm, dso, symbol, raw_buf, ev_type) + PebsEvent.pebs_num += 1 + del tmp_buf # # Intel Nehalem and Westmere support PEBS plus Load Latency info which lie @@ -82,16 +134,32 @@ def __init__(self, name, comm, dso, symbol, raw_buf, ev_type=EVTYPE_PEBS): # in L1/L2/L3 or IO operations # LAT: the actual latency in cycles # + class PebsNHM(PebsEvent): - pebs_nhm_num = 0 - def __init__(self, name, comm, dso, symbol, raw_buf, ev_type=EVTYPE_PEBS_LL): - tmp_buf=raw_buf[144:176] - status, dla, dse, lat = struct.unpack('QQQQ', tmp_buf) - self.status = status - self.dla = dla - self.dse = dse - self.lat = lat - - PebsEvent.__init__(self, name, comm, dso, symbol, raw_buf, ev_type) - PebsNHM.pebs_nhm_num += 1 - del tmp_buf + """ + Class for Intel Nehalem and Westmere PEBS events with load latency info. + """ + pebs_nhm_num = 0 + + def __init__(self, name, comm, dso, symbol, raw_buf, ev_type=EVTYPE_PEBS_LL): + """ + Initialize PebsNHM instance. + + Args: + name (str): Event name. + comm (str): Command name. + dso (str): Dynamic shared object. + symbol (str): Symbol name. + raw_buf (bytes): Raw buffer containing event data. + ev_type (int): Event type identifier. + """ + tmp_buf = raw_buf[144:176] + status, dla, dse, lat = struct.unpack('QQQQ', tmp_buf) + self.status = status + self.dla = dla + self.dse = dse + self.lat = lat + + PebsEvent.__init__(name, comm, dso, symbol, raw_buf, ev_type) + PebsNHM.pebs_nhm_num += 1 + del tmp_buf diff --git a/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/Util.py b/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/Util.py index 7384dcb628c432..17c047cc98e2ab 100644 --- a/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/Util.py +++ b/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/Util.py @@ -1,13 +1,16 @@ -# Util.py - Python extension for perf script, miscellaneous utility code -# -# Copyright (C) 2010 by Tom Zanussi -# -# This software may be distributed under the terms of the GNU General -# Public License ("GPL") version 2 as published by the Free Software -# Foundation. +""" +Util.py - Python extension for perf script, miscellaneous utility code + +Copyright (C) 2010 by Tom Zanussi + +This software may be distributed under the terms of the GNU General +Public License ("GPL") version 2 as published by the Free Software +Foundation. +""" from __future__ import print_function -import errno, os +import errno +import os FUTEX_WAIT = 0 FUTEX_WAKE = 1 @@ -15,77 +18,95 @@ FUTEX_CLOCK_REALTIME = 256 FUTEX_CMD_MASK = ~(FUTEX_PRIVATE_FLAG | FUTEX_CLOCK_REALTIME) -NSECS_PER_SEC = 1000000000 +NSECS_PER_SEC = 1000000000 + + +def avg(total, num_elements): + """Calculate the average of a total sum.""" + return total / num_elements + + +def nsecs(secs, nanosecs): + """Convert seconds and nanoseconds to total nanoseconds.""" + return secs * NSECS_PER_SEC + nanosecs + -def avg(total, n): - return total / n +def nsecs_secs(nanosecs): + """Extract seconds from total nanoseconds.""" + return nanosecs // NSECS_PER_SEC -def nsecs(secs, nsecs): - return secs * NSECS_PER_SEC + nsecs -def nsecs_secs(nsecs): - return nsecs / NSECS_PER_SEC +def nsecs_nsecs(nanosecs): + """Extract nanoseconds from total nanoseconds.""" + return nanosecs % NSECS_PER_SEC -def nsecs_nsecs(nsecs): - return nsecs % NSECS_PER_SEC -def nsecs_str(nsecs): - str = "%5u.%09u" % (nsecs_secs(nsecs), nsecs_nsecs(nsecs)), - return str +def nsecs_str(nanosecs): + """Convert total nanoseconds to a formatted string.""" + return "{:5d}.{:09d}".format(nsecs_secs(nanosecs), nsecs_nsecs(nanosecs)) + + +def add_stats(stats_dict, key, value): + """Add statistics to a dictionary.""" + if key not in stats_dict: + stats_dict[key] = (value, value, value, 1) + else: + min_value, max_value, avg_value, count = stats_dict[key] + if value < min_value: + min_value = value + if value > max_value: + max_value = value + avg_value = (avg_value + value) / 2 + stats_dict[key] = (min_value, max_value, avg_value, count + 1) -def add_stats(dict, key, value): - if key not in dict: - dict[key] = (value, value, value, 1) - else: - min, max, avg, count = dict[key] - if value < min: - min = value - if value > max: - max = value - avg = (avg + value) / 2 - dict[key] = (min, max, avg, count + 1) def clear_term(): + """Clear the terminal screen.""" print("\x1b[H\x1b[2J") -audit_package_warned = False + +AUDIT_PACKAGE_WARNED = False try: - import audit - machine_to_id = { - 'x86_64': audit.MACH_86_64, - 'alpha' : audit.MACH_ALPHA, - 'ia64' : audit.MACH_IA64, - 'ppc' : audit.MACH_PPC, - 'ppc64' : audit.MACH_PPC64, - 'ppc64le' : audit.MACH_PPC64LE, - 's390' : audit.MACH_S390, - 's390x' : audit.MACH_S390X, - 'i386' : audit.MACH_X86, - 'i586' : audit.MACH_X86, - 'i686' : audit.MACH_X86, - } - try: - machine_to_id['armeb'] = audit.MACH_ARMEB - except: - pass - machine_id = machine_to_id[os.uname()[4]] -except: - if not audit_package_warned: - audit_package_warned = True - print("Install the audit-libs-python package to get syscall names.\n" - "For example:\n # apt-get install python-audit (Ubuntu)" - "\n # yum install audit-libs-python (Fedora)" - "\n etc.\n") - -def syscall_name(id): - try: - return audit.audit_syscall_to_name(id, machine_id) - except: - return str(id) - -def strerror(nr): - try: - return errno.errorcode[abs(nr)] - except: - return "Unknown %d errno" % nr + import audit + MACHINE_TO_ID = { + 'x86_64': audit.MACH_86_64, + 'alpha': audit.MACH_ALPHA, + 'ia64': audit.MACH_IA64, + 'ppc': audit.MACH_PPC, + 'ppc64': audit.MACH_PPC64, + 'ppc64le': audit.MACH_PPC64LE, + 's390': audit.MACH_S390, + 's390x': audit.MACH_S390X, + 'i386': audit.MACH_X86, + 'i586': audit.MACH_X86, + 'i686': audit.MACH_X86, + } + try: + MACHINE_TO_ID['armeb'] = audit.MACH_ARMEB + except AttributeError: + pass + MACHINE_ID = MACHINE_TO_ID[os.uname()[4]] +except ImportError: + if not AUDIT_PACKAGE_WARNED: + AUDIT_PACKAGE_WARNED = True + print("Install the audit-libs-python package to get syscall names.\n" + "For example:\n # apt-get install python-audit (Ubuntu)" + "\n # yum install audit-libs-python (Fedora)" + "\n etc.\n") + + +def syscall_name(syscall_id): + """Get the name of a syscall using its ID.""" + try: + return audit.audit_syscall_to_name(syscall_id, MACHINE_ID) + except AttributeError: + return str(syscall_id) + + +def strerror(errno_value): + """Convert an errno value to a human-readable string.""" + try: + return errno.errorcode[abs(errno_value)] + except KeyError: + return "Unknown {} errno".format(errno_value)