Skip to content

Commit 0a7cc0b

Browse files
Gamal Sallamfacebook-github-bot
Gamal Sallam
authored andcommitted
User error codes and fix 32-bit system issues
Summary: Port the latest changes from the [perf-map C-API PR](python/cpython#103546). This includes using error codes instead of exceptions to avoid the need to hold the GIL. Additionally, there was an issue with Fedora on a 32-bit system that was fixed in this [PR](python/cpython#104811), so I also ported the fix. Reviewed By: carljm Differential Revision: D46041553 fbshipit-source-id: 6502189f489ca6cf11949ecd93c044e5ebe4fd2c
1 parent a8660c5 commit 0a7cc0b

File tree

6 files changed

+57
-48
lines changed

6 files changed

+57
-48
lines changed

Doc/c-api/perfmaps.rst

+27-16
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
2-
31
.. highlight:: c
42

53
.. _perfmaps:
@@ -10,30 +8,43 @@ Support for Perf Maps
108
On supported platforms (as of this writing, only Linux), the runtime can take
119
advantage of *perf map files* to make Python functions visible to an external
1210
profiling tool (such as `perf <https://perf.wiki.kernel.org/index.php/Main_Page>`_).
13-
A running process may create a file in the `/tmp` directory, which contains entries
11+
A running process may create a file in the ``/tmp`` directory, which contains entries
1412
that can map a section of executable code to a name. This interface is described in the
1513
`documentation of the Linux Perf tool <https://git.kernel.org/pub/scm/linux/
1614
kernel/git/torvalds/linux.git/tree/tools/perf/Documentation/jit-interface.txt>`_.
1715

1816
In Python, these helper APIs can be used by libraries and features that rely
1917
on generating machine code on the fly.
2018

19+
Note that holding the Global Interpreter Lock (GIL) is not required for these APIs.
20+
2121
.. c:function:: int PyUnstable_PerfMapState_Init(void)
22-
Open the `/tmp/perf-$pid.map` file, unless it's already opened, and create
22+
23+
Open the ``/tmp/perf-$pid.map`` file, unless it's already opened, and create
2324
a lock to ensure thread-safe writes to the file (provided the writes are
2425
done through :c:func:`PyUnstable_WritePerfMapEntry`). Normally, there's no need
25-
to call this explicitly, and it is safe to directly use :c:func:`PyUnstable_WritePerfMapEntry`
26-
in your code. If the state isn't already initialized, it will be created on
27-
the first call.
26+
to call this explicitly; just use :c:func:`PyUnstable_WritePerfMapEntry`
27+
and it will initialize the state on first call.
28+
29+
Returns ``0`` on success, ``-1`` on failure to create/open the perf map file,
30+
or ``-2`` on failure to create a lock. Check ``errno`` for more information
31+
about the cause of a failure.
32+
2833
.. c:function:: int PyUnstable_WritePerfMapEntry(const void *code_addr, unsigned int code_size, const char *entry_name)
29-
Write one single entry to the `/tmp/perf-$pid.map` file. This function is
34+
35+
Write one single entry to the ``/tmp/perf-$pid.map`` file. This function is
3036
thread safe. Here is what an example entry looks like::
37+
3138
# address size name
32-
0x7f3529fcf759 b py::bar:/run/t.py
33-
Extensions are encouraged to directly call this API when needed, instead of
34-
separately initializing the state by calling :c:func:`PyUnstable_PerfMapState_Init`.
35-
.. c:function:: int PyUnstable_PerfMapState_Fini(void)
36-
Close the perf map file, which was opened in `PyUnstable_PerfMapState_Init`. This
37-
API is called by the runtime itself, during interpreter shut-down. In general,
38-
there shouldn't be a reason to explicitly call this, except to handle specific
39-
scenarios such as forking.
39+
7f3529fcf759 b py::bar:/run/t.py
40+
41+
Will call :c:func:`PyUnstable_PerfMapState_Init` before writing the entry, if
42+
the perf map file is not already opened. Returns ``0`` on success, or the
43+
same error codes as :c:func:`PyUnstable_PerfMapState_Init` on failure.
44+
45+
.. c:function:: void PyUnstable_PerfMapState_Fini(void)
46+
47+
Close the perf map file opened by :c:func:`PyUnstable_PerfMapState_Init`.
48+
This is called by the runtime itself during interpreter shut-down. In
49+
general, there shouldn't be a reason to explicitly call this, except to
50+
handle specific scenarios such as forking.

Include/osmodule.h

-13
Original file line numberDiff line numberDiff line change
@@ -11,19 +11,6 @@ extern "C" {
1111
PyAPI_FUNC(PyObject *) PyOS_FSPath(PyObject *path);
1212
#endif
1313

14-
#if !defined(Py_LIMITED_API)
15-
typedef struct {
16-
FILE* perf_map;
17-
PyThread_type_lock map_lock;
18-
} PerfMapState;
19-
20-
PyAPI_FUNC(int) PyUnstable_PerfMapState_Init(void);
21-
22-
PyAPI_FUNC(int) PyUnstable_WritePerfMapEntry(const void *code_addr, unsigned int code_size, const char *entry_name);
23-
24-
PyAPI_FUNC(void) PyUnstable_PerfMapState_Fini(void);
25-
#endif
26-
2714
#ifdef __cplusplus
2815
}
2916
#endif

Include/sysmodule.h

+13
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,19 @@ PyAPI_FUNC(int) PySys_HasWarnOptions(void);
2929
PyAPI_FUNC(void) PySys_AddXOption(const wchar_t *);
3030
PyAPI_FUNC(PyObject *) PySys_GetXOptions(void);
3131

32+
#if !defined(Py_LIMITED_API)
33+
typedef struct {
34+
FILE* perf_map;
35+
PyThread_type_lock map_lock;
36+
} PerfMapState;
37+
38+
PyAPI_FUNC(int) PyUnstable_PerfMapState_Init(void);
39+
40+
PyAPI_FUNC(int) PyUnstable_WritePerfMapEntry(const void *code_addr, unsigned int code_size, const char *entry_name);
41+
42+
PyAPI_FUNC(void) PyUnstable_PerfMapState_Fini(void);
43+
#endif
44+
3245
#ifndef Py_LIMITED_API
3346
# define Py_CPYTHON_SYSMODULE_H
3447
# include "cpython/sysmodule.h"

Modules/_testinternalcapi.c

+9-4
Original file line numberDiff line numberDiff line change
@@ -413,19 +413,24 @@ test_gc_visit_objects(PyObject *Py_UNUSED(self), PyObject *Py_UNUSED(ignored)) {
413413
static PyObject *
414414
write_perf_map_entry(PyObject *self, PyObject *args)
415415
{
416+
PyObject *code_addr_v;
416417
const void *code_addr;
417418
unsigned int code_size;
418419
const char *entry_name;
419420

420-
if (!PyArg_ParseTuple(args, "KIs", &code_addr, &code_size, &entry_name))
421+
if (!PyArg_ParseTuple(args, "OIs", &code_addr_v, &code_size, &entry_name))
421422
return NULL;
423+
code_addr = PyLong_AsVoidPtr(code_addr_v);
424+
if (code_addr == NULL) {
425+
return NULL;
426+
}
422427

423428
int ret = PyUnstable_WritePerfMapEntry(code_addr, code_size, entry_name);
424-
if (ret == -1) {
425-
PyErr_SetString(PyExc_OSError, "Failed to write performance map entry");
429+
if (ret < 0) {
430+
PyErr_SetFromErrno(PyExc_OSError);
426431
return NULL;
427432
}
428-
return Py_BuildValue("i", ret);
433+
return PyLong_FromLong(ret);
429434
}
430435

431436
static PyObject *

Objects/perf_trampoline.c

+2-9
Original file line numberDiff line numberDiff line change
@@ -194,13 +194,13 @@ perf_map_write_entry(void *state, const void *code_addr,
194194
filename = PyUnicode_AsUTF8(co->co_filename);
195195
}
196196
size_t perf_map_entry_size = snprintf(NULL, 0, "py::%s:%s", entry, filename) + 1;
197-
char* perf_map_entry = (char*) malloc(perf_map_entry_size);
197+
char* perf_map_entry = (char*) PyMem_RawMalloc(perf_map_entry_size);
198198
if (perf_map_entry == NULL) {
199199
return;
200200
}
201201
snprintf(perf_map_entry, perf_map_entry_size, "py::%s:%s", entry, filename);
202202
PyUnstable_WritePerfMapEntry(code_addr, code_size, perf_map_entry);
203-
free(perf_map_entry);
203+
PyMem_RawFree(perf_map_entry);
204204
}
205205

206206
_PyPerf_Callbacks _Py_perfmap_callbacks = {
@@ -398,13 +398,6 @@ _PyPerfTrampoline_Init(int activate)
398398
if (new_code_arena() < 0) {
399399
return -1;
400400
}
401-
if (trampoline_api.state == NULL && trampoline_api.init_state != NULL) {
402-
void *state = trampoline_api.init_state();
403-
if (state == NULL) {
404-
return -1;
405-
}
406-
trampoline_api.state = state;
407-
}
408401
extra_code_index = _PyEval_RequestCodeExtraIndex(NULL);
409402
if (extra_code_index == -1) {
410403
return -1;

Python/sysmodule.c

+6-6
Original file line numberDiff line numberDiff line change
@@ -2110,20 +2110,19 @@ PyAPI_FUNC(int) PyUnstable_PerfMapState_Init(void) {
21102110
(intmax_t)pid);
21112111
int fd = open(filename, flags, 0600);
21122112
if (fd == -1) {
2113-
PyErr_SetFromErrnoWithFilename(PyExc_OSError, filename);
21142113
return -1;
21152114
}
21162115
else{
21172116
perf_map_state.perf_map = fdopen(fd, "a");
21182117
if (perf_map_state.perf_map == NULL) {
2119-
PyErr_SetFromErrnoWithFilename(PyExc_OSError, filename);
2118+
close(fd);
21202119
return -1;
21212120
}
21222121
}
21232122
perf_map_state.map_lock = PyThread_allocate_lock();
21242123
if (perf_map_state.map_lock == NULL) {
2125-
PyErr_SetString(PyExc_RuntimeError, "failed to create a lock for perf-maps");
2126-
return -1;
2124+
fclose(perf_map_state.perf_map);
2125+
return -2;
21272126
}
21282127
#endif
21292128
return 0;
@@ -2136,8 +2135,9 @@ PyAPI_FUNC(int) PyUnstable_WritePerfMapEntry(
21362135
) {
21372136
#ifndef MS_WINDOWS
21382137
if (perf_map_state.perf_map == NULL) {
2139-
if(PyUnstable_PerfMapState_Init() == -1){
2140-
return -1;
2138+
int ret = PyUnstable_PerfMapState_Init();
2139+
if(ret != 0){
2140+
return ret;
21412141
}
21422142
}
21432143
PyThread_acquire_lock(perf_map_state.map_lock, 1);

0 commit comments

Comments
 (0)