LCOV - code coverage report
Current view: | top level - Python - perf_trampoline.c (source / functions) |  |  | Hit | Total | Coverage
Test: | CPython 3.12 LCOV report [commit 5e6661bce9] |  | Lines: | 12 | 168 | 7.1 %
Date: | 2023-03-20 08:15:36 |  | Functions: | 2 | 14 | 14.3 %
|  |  | Branches: | 3 | 64 | 4.7 %

               Branch data     Line data    Source code
           1                 :            : /*
           2                 :            :
           3                 :            : Perf trampoline instrumentation
           4                 :            : ===============================
           5                 :            :
           6                 :            : This file contains instrumentation to allow to associate
           7                 :            : calls to the CPython eval loop back to the names of the Python
           8                 :            : functions and filename being executed.
           9                 :            :
          10                 :            : Many native performance profilers like the Linux perf tools are
          11                 :            : only available to 'see' the C stack when sampling from the profiled
          12                 :            : process. This means that if we have the following python code:
          13                 :            :
          14                 :            :     import time
          15                 :            :     def foo(n):
          16                 :            :         # Some CPU intensive code
          17                 :            :
          18                 :            :     def bar(n):
          19                 :            :         foo(n)
          20                 :            :
          21                 :            :     def baz(n):
          22                 :            :         bar(n)
          23                 :            :
          24                 :            :     baz(10000000)
          25                 :            :
          26                 :            : A performance profiler that is only able to see native frames will
          27                 :            : produce the following backtrace when sampling from foo():
          28                 :            :
          29                 :            :     _PyEval_EvalFrameDefault -----> Evaluation frame of foo()
          30                 :            :     _PyEval_Vector
          31                 :            :     _PyFunction_Vectorcall
          32                 :            :     PyObject_Vectorcall
          33                 :            :     call_function
          34                 :            :
          35                 :            :     _PyEval_EvalFrameDefault ------> Evaluation frame of bar()
          36                 :            :     _PyEval_EvalFrame
          37                 :            :     _PyEval_Vector
          38                 :            :     _PyFunction_Vectorcall
          39                 :            :     PyObject_Vectorcall
          40                 :            :     call_function
          41                 :            :
          42                 :            :     _PyEval_EvalFrameDefault -------> Evaluation frame of baz()
          43                 :            :     _PyEval_EvalFrame
          44                 :            :     _PyEval_Vector
          45                 :            :     _PyFunction_Vectorcall
          46                 :            :     PyObject_Vectorcall
          47                 :            :     call_function
          48                 :            :
          49                 :            :     ...
          50                 :            :
          51                 :            :     Py_RunMain
          52                 :            :
          53                 :            : Because the profiler is only able to see the native frames and the native
          54                 :            : function that runs the evaluation loop is the same (_PyEval_EvalFrameDefault)
          55                 :            : then the profiler and any reporter generated by it will not be able to
          56                 :            : associate the names of the Python functions and the filenames associated with
          57                 :            : those calls, rendering the results useless in the Python world.
          58                 :            :
          59                 :            : To fix this problem, we introduce the concept of a trampoline frame. A
          60                 :            : trampoline frame is a piece of code that is unique per Python code object that
          61                 :            : is executed before entering the CPython eval loop. This piece of code just
          62                 :            : calls the original Python evaluation function (_PyEval_EvalFrameDefault) and
          63                 :            : forwards all the arguments received. In this way, when a profiler samples
          64                 :            : frames from the previous example it will see;
          65                 :            :
          66                 :            :     _PyEval_EvalFrameDefault -----> Evaluation frame of foo()
          67                 :            :     [Jit compiled code 3]
          68                 :            :     _PyEval_Vector
          69                 :            :     _PyFunction_Vectorcall
          70                 :            :     PyObject_Vectorcall
          71                 :            :     call_function
          72                 :            :
          73                 :            :     _PyEval_EvalFrameDefault ------> Evaluation frame of bar()
          74                 :            :     [Jit compiled code 2]
          75                 :            :     _PyEval_EvalFrame
          76                 :            :     _PyEval_Vector
          77                 :            :     _PyFunction_Vectorcall
          78                 :            :     PyObject_Vectorcall
          79                 :            :     call_function
          80                 :            :
          81                 :            :     _PyEval_EvalFrameDefault -------> Evaluation frame of baz()
          82                 :            :     [Jit compiled code 1]
          83                 :            :     _PyEval_EvalFrame
          84                 :            :     _PyEval_Vector
          85                 :            :     _PyFunction_Vectorcall
          86                 :            :     PyObject_Vectorcall
          87                 :            :     call_function
          88                 :            :
          89                 :            :     ...
          90                 :            :
          91                 :            :     Py_RunMain
          92                 :            :
          93                 :            : When we generate every unique copy of the trampoline (what here we called "[Jit
          94                 :            : compiled code N]") we write the relationship between the compiled code and the
          95                 :            : Python function that is associated with it. Every profiler requires this
          96                 :            : information in a different format. For example, the Linux "perf" profiler
          97                 :            : requires a file in "/tmp/perf-PID.map" (name and location not configurable)
          98                 :            : with the following format:
          99                 :            :
         100                 :            :     <compiled code address> <compiled code size> <name of the compiled code>
         101                 :            :
         102                 :            : If this file is available when "perf" generates reports, it will automatically
         103                 :            : associate every trampoline with the Python function that it is associated with
         104                 :            : allowing it to generate reports that include Python information. These reports
         105                 :            : then can also be filtered in a way that *only* Python information appears.
         106                 :            :
         107                 :            : Notice that for this to work, there must be a unique copied of the trampoline
         108                 :            : per Python code object even if the code in the trampoline is the same. To
         109                 :            : achieve this we have a assembly template in Objects/asm_trampiline.S that is
         110                 :            : compiled into the Python executable/shared library. This template generates a
         111                 :            : symbol that maps the start of the assembly code and another that marks the end
         112                 :            : of the assembly code for the trampoline.  Then, every time we need a unique
         113                 :            : trampoline for a Python code object, we copy the assembly code into a mmaped
         114                 :            : area that has executable permissions and we return the start of that area as
         115                 :            : our trampoline function.
         116                 :            :
         117                 :            : Asking for a mmap-ed memory area for trampoline is very wasteful so we
         118                 :            : allocate big arenas of memory in a single mmap call, we populate the entire
         119                 :            : arena with copies of the trampoline (this allows us to now have to invalidate
         120                 :            : the icache for the instructions in the page) and then we return the next
         121                 :            : available chunk every time someone asks for a new trampoline. We keep a linked
         122                 :            : list of arenas in case the current memory arena is exhausted and another one is
         123                 :            : needed.
         124                 :            :
         125                 :            : For the best results, Python should be compiled with
         126                 :            : CFLAGS="-fno-omit-frame-pointer -mno-omit-leaf-frame-pointer" as this allows
         127                 :            : profilers to unwind using only the frame pointer and not on DWARF debug
         128                 :            : information (note that as trampilines are dynamically generated there won't be
         129                 :            : any DWARF information available for them).
         130                 :            : */
         131                 :            :
         132                 :            : #include "Python.h"
         133                 :            : #include "pycore_ceval.h"
         134                 :            : #include "pycore_frame.h"
         135                 :            : #include "pycore_interp.h"
         136                 :            :
         137                 :            :
         138                 :            : #ifdef PY_HAVE_PERF_TRAMPOLINE
         139                 :            :
         140                 :            : #include <fcntl.h>
         141                 :            : #include <stdio.h>
         142                 :            : #include <stdlib.h>
         143                 :            : #include <sys/mman.h>
         144                 :            : #include <sys/types.h>
         145                 :            : #include <unistd.h>
         146                 :            :
         147                 :            : #if defined(__arm__) || defined(__arm64__) || defined(__aarch64__)
         148                 :            : #define PY_HAVE_INVALIDATE_ICACHE
         149                 :            :
         150                 :            : #if defined(__clang__) || defined(__GNUC__)
         151                 :            : extern void __clear_cache(void *, void*);
         152                 :            : #endif
         153                 :            :
         154                 :            : static void invalidate_icache(char* begin, char*end) {
         155                 :            : #if defined(__clang__) || defined(__GNUC__)
         156                 :            :     return __clear_cache(begin, end);
         157                 :            : #else
         158                 :            :     return;
         159                 :            : #endif
         160                 :            : }
         161                 :            : #endif
         162                 :            :
         163                 :            : /* The function pointer is passed as last argument. The other three arguments
         164                 :            :  * are passed in the same order as the function requires. This results in
         165                 :            :  * shorter, more efficient ASM code for trampoline.
         166                 :            :  */
         167                 :            : typedef PyObject *(*py_evaluator)(PyThreadState *, _PyInterpreterFrame *,
         168                 :            :                                   int throwflag);
         169                 :            : typedef PyObject *(*py_trampoline)(PyThreadState *, _PyInterpreterFrame *, int,
         170                 :            :                                    py_evaluator);
         171                 :            :
         172                 :            : extern void *_Py_trampoline_func_start;  // Start of the template of the
         173                 :            :                                          // assembly trampoline
         174                 :            : extern void *
         175                 :            :     _Py_trampoline_func_end;  // End of the template of the assembly trampoline
         176                 :            :
         177                 :            : struct code_arena_st {
         178                 :            :     char *start_addr;    // Start of the memory arena
         179                 :            :     char *current_addr;  // Address of the current trampoline within the arena
         180                 :            :     size_t size;         // Size of the memory arena
         181                 :            :     size_t size_left;    // Remaining size of the memory arena
         182                 :            :     size_t code_size;    // Size of the code of every trampoline in the arena
         183                 :            :     struct code_arena_st
         184                 :            :         *prev;  // Pointer to the arena  or NULL if this is the first arena.
         185                 :            : };
         186                 :            :
         187                 :            : typedef struct code_arena_st code_arena_t;
         188                 :            : typedef struct trampoline_api_st trampoline_api_t;
         189                 :            :
         190                 :            : #define perf_status _PyRuntime.ceval.perf.status
         191                 :            : #define extra_code_index _PyRuntime.ceval.perf.extra_code_index
         192                 :            : #define perf_code_arena _PyRuntime.ceval.perf.code_arena
         193                 :            : #define trampoline_api _PyRuntime.ceval.perf.trampoline_api
         194                 :            : #define perf_map_file _PyRuntime.ceval.perf.map_file
         195                 :            :
         196                 :            : static void *
         197                 :          0 : perf_map_get_file(void)
         198                 :            : {
         199         [ #  # ]:          0 :     if (perf_map_file) {
         200                 :          0 :         return perf_map_file;
         201                 :            :     }
         202                 :            :     char filename[100];
         203                 :          0 :     pid_t pid = getpid();
         204                 :            :     // Location and file name of perf map is hard-coded in perf tool.
         205                 :            :     // Use exclusive create flag wit nofollow to prevent symlink attacks.
         206                 :          0 :     int flags = O_WRONLY | O_CREAT | O_EXCL | O_NOFOLLOW | O_CLOEXEC;
         207                 :          0 :     snprintf(filename, sizeof(filename) - 1, "/tmp/perf-%jd.map",
         208                 :            :              (intmax_t)pid);
         209                 :          0 :     int fd = open(filename, flags, 0600);
         210         [ #  # ]:          0 :     if (fd == -1) {
         211                 :          0 :         perf_status = PERF_STATUS_FAILED;
         212                 :          0 :         PyErr_SetFromErrnoWithFilename(PyExc_OSError, filename);
         213                 :          0 :         return NULL;
         214                 :            :     }
         215                 :          0 :     perf_map_file = fdopen(fd, "w");
         216         [ #  # ]:          0 :     if (!perf_map_file) {
         217                 :          0 :         perf_status = PERF_STATUS_FAILED;
         218                 :          0 :         PyErr_SetFromErrnoWithFilename(PyExc_OSError, filename);
         219                 :          0 :         close(fd);
         220                 :          0 :         return NULL;
         221                 :            :     }
         222                 :          0 :     return perf_map_file;
         223                 :            : }
         224                 :            :
         225                 :            : static int
         226                 :          0 : perf_map_close(void *state)
         227                 :            : {
         228                 :          0 :     FILE *fp = (FILE *)state;
         229                 :          0 :     int ret = 0;
         230         [ #  # ]:          0 :     if (fp) {
         231                 :          0 :         ret = fclose(fp);
         232                 :            :     }
         233                 :          0 :     perf_map_file = NULL;
         234                 :          0 :     perf_status = PERF_STATUS_NO_INIT;
         235                 :          0 :     return ret;
         236                 :            : }
         237                 :            :
         238                 :            : static void
         239                 :          0 : perf_map_write_entry(void *state, const void *code_addr,
         240                 :            :                          unsigned int code_size, PyCodeObject *co)
         241                 :            : {
         242                 :            :     assert(state != NULL);
         243                 :          0 :     FILE *method_file = (FILE *)state;
         244                 :          0 :     const char *entry = PyUnicode_AsUTF8(co->co_qualname);
         245         [ #  # ]:          0 :     if (entry == NULL) {
         246                 :          0 :         _PyErr_WriteUnraisableMsg("Failed to get qualname from code object",
         247                 :            :                                   NULL);
         248                 :          0 :         return;
         249                 :            :     }
         250                 :          0 :     const char *filename = PyUnicode_AsUTF8(co->co_filename);
         251         [ #  # ]:          0 :     if (filename == NULL) {
         252                 :          0 :         _PyErr_WriteUnraisableMsg("Failed to get filename from code object",
         253                 :            :                                   NULL);
         254                 :          0 :         return;
         255                 :            :     }
         256                 :          0 :     fprintf(method_file, "%p %x py::%s:%s\n", code_addr, code_size, entry,
         257                 :            :             filename);
         258                 :          0 :     fflush(method_file);
         259                 :            : }
         260                 :            :
         261                 :            : _PyPerf_Callbacks _Py_perfmap_callbacks = {
         262                 :            :     &perf_map_get_file,
         263                 :            :     &perf_map_write_entry,
         264                 :            :     &perf_map_close
         265                 :            : };
         266                 :            :
         267                 :            : static int
         268                 :          0 : new_code_arena(void)
         269                 :            : {
         270                 :            :     // non-trivial programs typically need 64 to 256 kiB.
         271                 :          0 :     size_t mem_size = 4096 * 16;
         272                 :            :     assert(mem_size % sysconf(_SC_PAGESIZE) == 0);
         273                 :            :     char *memory =
         274                 :          0 :         mmap(NULL,  // address
         275                 :            :              mem_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS,
         276                 :            :              -1,  // fd (not used here)
         277                 :            :              0);  // offset (not used here)
         278         [ #  # ]:          0 :     if (!memory) {
         279                 :          0 :         PyErr_SetFromErrno(PyExc_OSError);
         280                 :          0 :         _PyErr_WriteUnraisableMsg(
         281                 :            :             "Failed to create new mmap for perf trampoline", NULL);
         282                 :          0 :         perf_status = PERF_STATUS_FAILED;
         283                 :          0 :         return -1;
         284                 :            :     }
         285                 :          0 :     void *start = &_Py_trampoline_func_start;
         286                 :          0 :     void *end = &_Py_trampoline_func_end;
         287                 :          0 :     size_t code_size = end - start;
         288                 :            :     // TODO: Check the effect of alignment of the code chunks. Initial investigation
         289                 :            :     // showed that this has no effect on performance in x86-64 or aarch64 and the current
         290                 :            :     // version has the advantage that the unwinder in GDB can unwind across JIT-ed code.
         291                 :            :     //
         292                 :            :     // We should check the values in the future and see if there is a
         293                 :            :     // measurable performance improvement by rounding trampolines up to 32-bit
         294                 :            :     // or 64-bit alignment.
         295                 :            :
         296                 :          0 :     size_t n_copies = mem_size / code_size;
         297         [ #  # ]:          0 :     for (size_t i = 0; i < n_copies; i++) {
         298                 :          0 :         memcpy(memory + i * code_size, start, code_size * sizeof(char));
         299                 :            :     }
         300                 :            :     // Some systems may prevent us from creating executable code on the fly.
         301                 :          0 :     int res = mprotect(memory, mem_size, PROT_READ | PROT_EXEC);
         302         [ #  # ]:          0 :     if (res == -1) {
         303                 :          0 :         PyErr_SetFromErrno(PyExc_OSError);
         304                 :          0 :         munmap(memory, mem_size);
         305                 :          0 :         _PyErr_WriteUnraisableMsg(
         306                 :            :             "Failed to set mmap for perf trampoline to PROT_READ | PROT_EXEC",
         307                 :            :             NULL);
         308                 :          0 :         return -1;
         309                 :            :     }
         310                 :            :
         311                 :            : #ifdef PY_HAVE_INVALIDATE_ICACHE
         312                 :            :     // Before the JIT can run a block of code that has been emitted it must invalidate
         313                 :            :     // the instruction cache on some platforms like arm and aarch64.
         314                 :            :     invalidate_icache(memory, memory + mem_size);
         315                 :            : #endif
         316                 :            :
         317                 :          0 :     code_arena_t *new_arena = PyMem_RawCalloc(1, sizeof(code_arena_t));
         318         [ #  # ]:          0 :     if (new_arena == NULL) {
         319                 :          0 :         PyErr_NoMemory();
         320                 :          0 :         munmap(memory, mem_size);
         321                 :          0 :         _PyErr_WriteUnraisableMsg("Failed to allocate new code arena struct",
         322                 :            :                                   NULL);
         323                 :          0 :         return -1;
         324                 :            :     }
         325                 :            :
         326                 :          0 :     new_arena->start_addr = memory;
         327                 :          0 :     new_arena->current_addr = memory;
         328                 :          0 :     new_arena->size = mem_size;
         329                 :          0 :     new_arena->size_left = mem_size;
         330                 :          0 :     new_arena->code_size = code_size;
         331                 :          0 :     new_arena->prev = perf_code_arena;
         332                 :          0 :     perf_code_arena = new_arena;
         333                 :          0 :     return 0;
         334                 :            : }
         335                 :            :
         336                 :            : static void
         337                 :         25 : free_code_arenas(void)
         338                 :            : {
         339                 :         25 :     code_arena_t *cur = perf_code_arena;
         340                 :            :     code_arena_t *prev;
         341                 :         25 :     perf_code_arena = NULL;  // invalid static pointer
         342         [ -  + ]:         25 :     while (cur) {
         343                 :          0 :         munmap(cur->start_addr, cur->size);
         344                 :          0 :         prev = cur->prev;
         345                 :          0 :         PyMem_RawFree(cur);
         346                 :          0 :         cur = prev;
         347                 :            :     }
         348                 :         25 : }
         349                 :            :
         350                 :            : static inline py_trampoline
         351                 :          0 : code_arena_new_code(code_arena_t *code_arena)
         352                 :            : {
         353                 :          0 :     py_trampoline trampoline = (py_trampoline)code_arena->current_addr;
         354                 :          0 :     code_arena->size_left -= code_arena->code_size;
         355                 :          0 :     code_arena->current_addr += code_arena->code_size;
         356                 :          0 :     return trampoline;
         357                 :            : }
         358                 :            :
         359                 :            : static inline py_trampoline
         360                 :          0 : compile_trampoline(void)
         361                 :            : {
         362         [ #  # ]:          0 :     if ((perf_code_arena == NULL) ||
         363         [ #  # ]:          0 :         (perf_code_arena->size_left <= perf_code_arena->code_size)) {
         364         [ #  # ]:          0 :         if (new_code_arena() < 0) {
         365                 :          0 :             return NULL;
         366                 :            :         }
         367                 :            :     }
         368                 :            :     assert(perf_code_arena->size_left <= perf_code_arena->size);
         369                 :          0 :     return code_arena_new_code(perf_code_arena);
         370                 :            : }
         371                 :            :
         372                 :            : static PyObject *
         373                 :          0 : py_trampoline_evaluator(PyThreadState *ts, _PyInterpreterFrame *frame,
         374                 :            :                         int throw)
         375                 :            : {
         376         [ #  # ]:          0 :     if (perf_status == PERF_STATUS_FAILED ||
         377         [ #  # ]:          0 :         perf_status == PERF_STATUS_NO_INIT) {
         378                 :          0 :         goto default_eval;
         379                 :            :     }
         380                 :          0 :     PyCodeObject *co = frame->f_code;
         381                 :          0 :     py_trampoline f = NULL;
         382                 :            :     assert(extra_code_index != -1);
         383                 :          0 :     int ret = _PyCode_GetExtra((PyObject *)co, extra_code_index, (void **)&f);
         384   [ #  #  #  # ]:          0 :     if (ret != 0 || f == NULL) {
         385                 :            :         // This is the first time we see this code object so we need
         386                 :            :         // to compile a trampoline for it.
         387                 :          0 :         py_trampoline new_trampoline = compile_trampoline();
         388         [ #  # ]:          0 :         if (new_trampoline == NULL) {
         389                 :          0 :             goto default_eval;
         390                 :            :         }
         391                 :          0 :         trampoline_api.write_state(trampoline_api.state, new_trampoline,
         392                 :          0 :                                    perf_code_arena->code_size, co);
         393                 :          0 :         _PyCode_SetExtra((PyObject *)co, extra_code_index,
         394                 :            :                          (void *)new_trampoline);
         395                 :          0 :         f = new_trampoline;
         396                 :            :     }
         397                 :            :     assert(f != NULL);
         398                 :          0 :     return f(ts, frame, throw, _PyEval_EvalFrameDefault);
         399                 :          0 : default_eval:
         400                 :            :     // Something failed, fall back to the default evaluator.
         401                 :          0 :     return _PyEval_EvalFrameDefault(ts, frame, throw);
         402                 :            : }
         403                 :            : #endif  // PY_HAVE_PERF_TRAMPOLINE
         404                 :            :
         405                 :            : int
         406                 :          0 : _PyIsPerfTrampolineActive(void)
         407                 :            : {
         408                 :            : #ifdef PY_HAVE_PERF_TRAMPOLINE
         409                 :          0 :     PyThreadState *tstate = _PyThreadState_GET();
         410                 :          0 :     return tstate->interp->eval_frame == py_trampoline_evaluator;
         411                 :            : #endif
         412                 :            :     return 0;
         413                 :            : }
         414                 :            :
         415                 :            : void
         416                 :          0 : _PyPerfTrampoline_GetCallbacks(_PyPerf_Callbacks *callbacks)
         417                 :            : {
         418         [ #  # ]:          0 :     if (callbacks == NULL) {
         419                 :          0 :         return;
         420                 :            :     }
         421                 :            : #ifdef PY_HAVE_PERF_TRAMPOLINE
         422                 :          0 :     callbacks->init_state = trampoline_api.init_state;
         423                 :          0 :     callbacks->write_state = trampoline_api.write_state;
         424                 :          0 :     callbacks->free_state = trampoline_api.free_state;
         425                 :            : #endif
         426                 :          0 :     return;
         427                 :            : }
         428                 :            :
         429                 :            : int
         430                 :          0 : _PyPerfTrampoline_SetCallbacks(_PyPerf_Callbacks *callbacks)
         431                 :            : {
         432         [ #  # ]:          0 :     if (callbacks == NULL) {
         433                 :          0 :         return -1;
         434                 :            :     }
         435                 :            : #ifdef PY_HAVE_PERF_TRAMPOLINE
         436         [ #  # ]:          0 :     if (trampoline_api.state) {
         437                 :          0 :         _PyPerfTrampoline_Fini();
         438                 :            :     }
         439                 :          0 :     trampoline_api.init_state = callbacks->init_state;
         440                 :          0 :     trampoline_api.write_state = callbacks->write_state;
         441                 :          0 :     trampoline_api.free_state = callbacks->free_state;
         442                 :          0 :     trampoline_api.state = NULL;
         443                 :          0 :     perf_status = PERF_STATUS_OK;
         444                 :            : #endif
         445                 :          0 :     return 0;
         446                 :            : }
         447                 :            :
         448                 :            : int
         449                 :          0 : _PyPerfTrampoline_Init(int activate)
         450                 :            : {
         451                 :            : #ifdef PY_HAVE_PERF_TRAMPOLINE
         452                 :          0 :     PyThreadState *tstate = _PyThreadState_GET();
         453         [ #  # ]:          0 :     if (tstate->interp->eval_frame &&
         454         [ #  # ]:          0 :         tstate->interp->eval_frame != py_trampoline_evaluator) {
         455                 :          0 :         PyErr_SetString(PyExc_RuntimeError,
         456                 :            :                         "Trampoline cannot be initialized as a custom eval "
         457                 :            :                         "frame is already present");
         458                 :          0 :         return -1;
         459                 :            :     }
         460         [ #  # ]:          0 :     if (!activate) {
         461                 :          0 :         tstate->interp->eval_frame = NULL;
         462                 :            :     }
         463                 :            :     else {
         464                 :          0 :         tstate->interp->eval_frame = py_trampoline_evaluator;
         465         [ #  # ]:          0 :         if (new_code_arena() < 0) {
         466                 :          0 :             return -1;
         467                 :            :         }
         468         [ #  # ]:          0 :         if (trampoline_api.state == NULL) {
         469                 :          0 :             void *state = trampoline_api.init_state();
         470         [ #  # ]:          0 :             if (state == NULL) {
         471                 :          0 :                 return -1;
         472                 :            :             }
         473                 :          0 :             trampoline_api.state = state;
         474                 :            :         }
         475                 :          0 :         extra_code_index = _PyEval_RequestCodeExtraIndex(NULL);
         476         [ #  # ]:          0 :         if (extra_code_index == -1) {
         477                 :          0 :             return -1;
         478                 :            :         }
         479                 :          0 :         perf_status = PERF_STATUS_OK;
         480                 :            :     }
         481                 :            : #endif
         482                 :          0 :     return 0;
         483                 :            : }
         484                 :            :
         485                 :            : int
         486                 :         25 : _PyPerfTrampoline_Fini(void)
         487                 :            : {
         488                 :            : #ifdef PY_HAVE_PERF_TRAMPOLINE
         489                 :         25 :     PyThreadState *tstate = _PyThreadState_GET();
         490         [ -  + ]:         25 :     if (tstate->interp->eval_frame == py_trampoline_evaluator) {
         491                 :          0 :         tstate->interp->eval_frame = NULL;
         492                 :            :     }
         493                 :         25 :     free_code_arenas();
         494         [ -  + ]:         25 :     if (trampoline_api.state != NULL) {
         495                 :          0 :         trampoline_api.free_state(trampoline_api.state);
         496                 :          0 :         trampoline_api.state = NULL;
         497                 :            :     }
         498                 :         25 :     extra_code_index = -1;
         499                 :            : #endif
         500                 :         25 :     return 0;
         501                 :            : }
         502                 :            :
         503                 :            : PyStatus
         504                 :          0 : _PyPerfTrampoline_AfterFork_Child(void)
         505                 :            : {
         506                 :            : #ifdef PY_HAVE_PERF_TRAMPOLINE
         507                 :            :     // Restart trampoline in file in child.
         508                 :          0 :     int was_active = _PyIsPerfTrampolineActive();
         509                 :          0 :     _PyPerfTrampoline_Fini();
         510         [ #  # ]:          0 :     if (was_active) {
         511                 :          0 :         _PyPerfTrampoline_Init(1);
         512                 :            :     }
         513                 :            : #endif
         514                 :          0 :     return PyStatus_Ok();
         515                 :            : }

Generated by: LCOV version 1.14