[PATCH v3 01/49] perf python: Update syscall helpers and expose arch_strerrno

From: Ian Rogers

Date: Wed Sep 23 2026 - 14:13:15 EST


Update the PyArg_ParseTupleAndKeywords format string for
pyrf__syscall_name() and pyrf__syscall_id() from "i|$i" to "i|i" (and
"s|$i" to "s|i"), allowing elf_machine to be passed as an optional
positional argument as well as a keyword argument. Update syscall_id()
and syscall_name() in perf.pyi to match, and set the return type of
syscall_name() to Optional[str] as unknown syscall numbers return None.

Also expose perf_env__arch_strerrno() as perf.arch_strerrno(err,
elf_machine=None) so standalone Python scripts can resolve
architecture-specific errno names directly from the auto-generated
trace beauty tables instead of maintaining duplicate errno dictionaries
in Python. Tighten the macro pattern in arch_errno_names.sh to
E[A-Z0-9]+ so compiler -D flags containing underscores (such as LLVM's
-DEXPERIMENTAL_KEY_INSTRUCTIONS=1) do not shadow EPERM (1), and add
EM_SPARC32PLUS alongside EM_SPARC and EM_SPARCV9.

Assisted-by: Antigravity:gemini-3.1-pro
Signed-off-by: Ian Rogers <irogers@xxxxxxxxxx>
---
tools/perf/python/perf.pyi | 16 +++++-
tools/perf/trace/beauty/arch_errno_names.sh | 4 +-
tools/perf/util/python.c | 60 ++++++++++++++++++++-
3 files changed, 74 insertions(+), 6 deletions(-)

diff --git a/tools/perf/python/perf.pyi b/tools/perf/python/perf.pyi
index 58b0b3ed819d..c390e17708b1 100644
--- a/tools/perf/python/perf.pyi
+++ b/tools/perf/python/perf.pyi
@@ -20,7 +20,7 @@ def metrics() -> List[Dict[str, Union[str, List[str]]]]:
"""
...

-def syscall_name(id: int, *, elf_machine: Optional[int] = None) -> str:
+def syscall_name(id: int, elf_machine: Optional[int] = None) -> Optional[str]:
"""Convert a syscall number to its name.

Args:
@@ -32,7 +32,7 @@ def syscall_name(id: int, *, elf_machine: Optional[int] = None) -> str:
"""
...

-def syscall_id(name: str, *, elf_machine: Optional[int] = None) -> int:
+def syscall_id(name: str, elf_machine: Optional[int] = None) -> int:
"""Convert a syscall name to its number.

Args:
@@ -44,6 +44,18 @@ def syscall_id(name: str, *, elf_machine: Optional[int] = None) -> int:
"""
...

+def arch_strerrno(err: int, elf_machine: Optional[int] = None) -> Optional[str]:
+ """Convert an errno number to its symbolic name.
+
+ Args:
+ err: The errno number (positive or negative).
+ elf_machine: Optional ELF machine type.
+
+ Returns:
+ The symbolic name of the errno, or None if unknown.
+ """
+ ...
+
def parse_events(
event_string: str,
cpus: Optional[cpu_map] = None,
diff --git a/tools/perf/trace/beauty/arch_errno_names.sh b/tools/perf/trace/beauty/arch_errno_names.sh
index 8751bfa4a2b2..e75cd5bc4328 100755
--- a/tools/perf/trace/beauty/arch_errno_names.sh
+++ b/tools/perf/trace/beauty/arch_errno_names.sh
@@ -46,7 +46,7 @@ process_arch()
asm_errno=$(asm_errno_file "$arch")

$gcc $CFLAGS $include_path -E -dM -x c $asm_errno \
- |grep -hE '^#define[[:blank:]]+(E[^[:blank:]]+)[[:blank:]]+([[:digit:]]+).*' \
+ |grep -hE '^#define[[:blank:]]+(E[A-Z0-9]+)[[:blank:]]+([[:digit:]]+).*' \
|awk '{ print $2","$3; }' \
|sort -t, -k2 -nu \
|IFS=, create_errno_lookup_func "$arch"
@@ -69,7 +69,7 @@ arch_to_e_machine()
riscv) printf '\tcase EM_RISCV:\n' ;;
s390) printf '\tcase EM_S390:\n' ;;
sh) printf '\tcase EM_SH:\n' ;;
- sparc) printf '\tcase EM_SPARC:\n\tcase EM_SPARCV9:\n' ;;
+ sparc) printf '\tcase EM_SPARC:\n\tcase EM_SPARC32PLUS:\n\tcase EM_SPARCV9:\n' ;;
x86) printf '\tcase EM_386:\n\tcase EM_X86_64:\n' ;;
xtensa) printf '\tcase EM_XTENSA:\n' ;;
esac
diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c
index d8a842621cda..6ff0b4ed0bef 100644
--- a/tools/perf/util/python.c
+++ b/tools/perf/util/python.c
@@ -3,6 +3,7 @@
#include <Python.h>

#include <inttypes.h>
+#include <limits.h>
#include <string.h>

#include <linux/err.h>
@@ -25,6 +26,7 @@
#include "dso.h"
#include "dwarf-regs.h"
#include "event.h"
+#include "env.h"
#include "branch.h"
#include "evlist.h"
#include "evsel.h"
@@ -4104,14 +4106,35 @@ static int pyrf_session__setup_types(void)
return PyType_Ready(&pyrf_session__type);
}

+static int pyrf__parse_elf_machine(PyObject *obj, int *elf_machine)
+{
+ long val;
+
+ if (!obj || obj == Py_None) {
+ *elf_machine = EM_HOST;
+ return 0;
+ }
+ val = PyLong_AsLong(obj);
+ if (val == -1 && PyErr_Occurred())
+ return -1;
+ if (val <= 0 || val > UINT16_MAX)
+ *elf_machine = EM_HOST;
+ else
+ *elf_machine = (int)val;
+ return 0;
+}
+
static PyObject *pyrf__syscall_name(PyObject *self, PyObject *args, PyObject *kwargs)
{
const char *name;
int id;
int elf_machine = EM_HOST;
+ PyObject *elf_machine_obj = NULL;
static char *kwlist[] = { "id", "elf_machine", NULL };

- if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i|$i", kwlist, &id, &elf_machine))
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i|O", kwlist, &id, &elf_machine_obj))
+ return NULL;
+ if (pyrf__parse_elf_machine(elf_machine_obj, &elf_machine) < 0)
return NULL;

name = syscalltbl__name(elf_machine, id);
@@ -4125,9 +4148,12 @@ static PyObject *pyrf__syscall_id(PyObject *self, PyObject *args, PyObject *kwar
const char *name;
int id;
int elf_machine = EM_HOST;
+ PyObject *elf_machine_obj = NULL;
static char *kwlist[] = { "name", "elf_machine", NULL };

- if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|$i", kwlist, &name, &elf_machine))
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|O", kwlist, &name, &elf_machine_obj))
+ return NULL;
+ if (pyrf__parse_elf_machine(elf_machine_obj, &elf_machine) < 0)
return NULL;

id = syscalltbl__id(elf_machine, name);
@@ -4138,6 +4164,30 @@ static PyObject *pyrf__syscall_id(PyObject *self, PyObject *args, PyObject *kwar
return PyLong_FromLong(id);
}

+static PyObject *pyrf__arch_strerrno(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ const char *name;
+ int err;
+ int elf_machine = EM_HOST;
+ PyObject *elf_machine_obj = NULL;
+ static char *kwlist[] = { "err", "elf_machine", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i|O", kwlist, &err, &elf_machine_obj))
+ return NULL;
+ if (pyrf__parse_elf_machine(elf_machine_obj, &elf_machine) < 0)
+ return NULL;
+
+ if (err == INT_MIN)
+ Py_RETURN_NONE;
+ if (err < 0)
+ err = -err;
+
+ name = perf_env__arch_strerrno((uint16_t)elf_machine, err);
+ if (!name || !strcmp(name, "(unknown)"))
+ Py_RETURN_NONE;
+ return PyUnicode_FromString(name);
+}
+
static PyObject *pyrf__config_get(PyObject *self, PyObject *args)
{
const char *config_name, *val;
@@ -4202,6 +4252,12 @@ static PyMethodDef perf__methods[] = {
.ml_flags = METH_VARARGS | METH_KEYWORDS,
.ml_doc = PyDoc_STR("Turns a syscall name to a number.")
},
+ {
+ .ml_name = "arch_strerrno",
+ .ml_meth = (PyCFunction) pyrf__arch_strerrno,
+ .ml_flags = METH_VARARGS | METH_KEYWORDS,
+ .ml_doc = PyDoc_STR("Turns an errno number to a string.")
+ },
{ .ml_name = NULL, }
};

--
2.56.0.rc1.310.g51773c2048-goog