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