Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 18a07e6

Browse files
committed
Move functionality into its own unit + add -X option
1 parent 2de11ea commit 18a07e6

File tree

7 files changed

+195
-13
lines changed

7 files changed

+195
-13
lines changed

Include/cpython/code.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,6 @@ typedef uint16_t _Py_CODEUNIT;
8989
PyObject *co_filename; /* unicode (where it was loaded from) */ \
9090
PyObject *co_name; /* unicode (name, for reference) */ \
9191
PyObject *co_qualname; /* unicode (qualname, for reference) */ \
92-
void* co_trampoline; \
9392
PyObject *co_linetable; /* bytes object that holds location info */ \
9493
PyObject *co_weakreflist; /* to support weakrefs to code objects */ \
9594
PyObject *_co_code; /* cached co_code object/attribute */ \

Include/cpython/initconfig.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ typedef struct PyConfig {
142142
unsigned long hash_seed;
143143
int faulthandler;
144144
int tracemalloc;
145+
int perf_profiling;
145146
int import_time;
146147
int code_debug_ranges;
147148
int show_ref_count;

Include/internal/pycore_ceval.h

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,6 @@ struct _ceval_runtime_state;
2727
#include "pycore_pystate.h" // _PyThreadState_GET()
2828
#include "pycore_frame.h" // _PyThreadState_GET()
2929

30-
#ifdef HAVE_PERF_TRAMPOLINE
31-
typedef PyObject* (*py_evaluator)(PyThreadState *, _PyInterpreterFrame *, int throwflag);
32-
typedef PyObject* (*py_trampoline)(py_evaluator, PyThreadState *, _PyInterpreterFrame *, int throwflag);
33-
#endif
34-
3530
extern void _Py_FinishPendingCalls(PyThreadState *tstate);
3631
extern void _PyEval_InitRuntimeState(struct _ceval_runtime_state *);
3732
extern void _PyEval_InitState(struct _ceval_state *, PyThread_type_lock);
@@ -70,19 +65,13 @@ extern PyObject* _PyEval_BuiltinsFromGlobals(
7065
PyThreadState *tstate,
7166
PyObject *globals);
7267

68+
extern int _PyPerfTrampoline_Init(int activate);
7369

7470
static inline PyObject*
7571
_PyEval_EvalFrame(PyThreadState *tstate, struct _PyInterpreterFrame *frame, int throwflag)
7672
{
7773
EVAL_CALL_STAT_INC(EVAL_CALL_TOTAL);
7874
if (tstate->interp->eval_frame == NULL) {
79-
#ifdef HAVE_PERF_TRAMPOLINE
80-
PyCodeObject *co = frame->f_code;
81-
py_trampoline f = (py_trampoline)(co->co_trampoline);
82-
if (f) {
83-
return f(_PyEval_EvalFrameDefault, tstate, frame, throwflag);
84-
}
85-
#endif
8675
return _PyEval_EvalFrameDefault(tstate, frame, throwflag);
8776
}
8877
return tstate->interp->eval_frame(tstate, frame, throwflag);

Objects/asm_trampoline.sx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,5 @@ _Py_trampoline_func_start:
1717
#endif
1818
.globl _Py_trampoline_func_end
1919
_Py_trampoline_func_end:
20+
.section .note.GNU-stack,"",@progbits
21+

Objects/perf_trampoline.c

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
#include "Python.h"
2+
3+
#include <stdio.h>
4+
#include <stdlib.h>
5+
#include <sys/mman.h>
6+
#include <sys/types.h>
7+
#include <unistd.h>
8+
9+
#include "pycore_ceval.h"
10+
#include "pycore_frame.h"
11+
#include "pycore_interp.h"
12+
13+
#ifdef HAVE_PERF_TRAMPOLINE
14+
typedef PyObject *(*py_evaluator)(PyThreadState *, _PyInterpreterFrame *,
15+
int throwflag);
16+
typedef PyObject *(*py_trampoline)(py_evaluator, PyThreadState *,
17+
_PyInterpreterFrame *, int throwflag);
18+
extern void *_Py_trampoline_func_start;
19+
extern void *_Py_trampoline_func_end;
20+
21+
typedef struct {
22+
char *start_addr;
23+
char *current_addr;
24+
size_t size;
25+
size_t size_left;
26+
size_t code_size;
27+
} code_arena_t;
28+
29+
static Py_ssize_t extra_code_index = -1;
30+
static code_arena_t code_arena;
31+
32+
static int
33+
new_code_arena()
34+
{
35+
size_t page_size = sysconf(_SC_PAGESIZE);
36+
char *memory = mmap(NULL, // address
37+
page_size, PROT_READ | PROT_WRITE | PROT_EXEC,
38+
MAP_PRIVATE | MAP_ANONYMOUS,
39+
-1, // fd (not used here)
40+
0); // offset (not used here)
41+
if (!memory) {
42+
Py_FatalError("Failed to allocate new code arena");
43+
return -1;
44+
}
45+
void *start = &_Py_trampoline_func_start;
46+
void *end = &_Py_trampoline_func_end;
47+
size_t code_size = end - start;
48+
49+
long n_copies = page_size / code_size;
50+
for (int i = 0; i < n_copies; i++) {
51+
memcpy(memory + i * code_size, start, code_size * sizeof(char));
52+
}
53+
54+
mprotect(memory, page_size, PROT_READ | PROT_EXEC);
55+
56+
code_arena.start_addr = memory;
57+
code_arena.current_addr = memory;
58+
code_arena.size = page_size;
59+
code_arena.size_left = page_size;
60+
code_arena.code_size = code_size;
61+
return 0;
62+
}
63+
64+
static py_trampoline
65+
code_arena_new_code(code_arena_t *code_arena)
66+
{
67+
py_trampoline trampoline = (py_trampoline)code_arena->current_addr;
68+
code_arena->size_left -= code_arena->code_size;
69+
code_arena->current_addr += code_arena->code_size;
70+
return trampoline;
71+
}
72+
73+
py_trampoline
74+
compile_trampoline(void)
75+
{
76+
if (code_arena.size_left <= code_arena.code_size) {
77+
if (new_code_arena() < 0) {
78+
return NULL;
79+
}
80+
}
81+
82+
assert(code_arena.size_left <= code_arena.size);
83+
return code_arena_new_code(&code_arena);
84+
}
85+
86+
FILE *
87+
perf_map_open(pid_t pid)
88+
{
89+
char filename[500];
90+
snprintf(filename, sizeof(filename), "/tmp/perf-%d.map", pid);
91+
FILE *res = fopen(filename, "a");
92+
if (!res) {
93+
fprintf(stderr, "Couldn't open %s: errno(%d)", filename, errno);
94+
exit(0);
95+
}
96+
return res;
97+
}
98+
99+
int
100+
perf_map_close(FILE *fp)
101+
{
102+
if (fp)
103+
return fclose(fp);
104+
else
105+
return 0;
106+
}
107+
108+
void
109+
perf_map_write_entry(FILE *method_file, const void *code_addr,
110+
unsigned int code_size, const char *entry,
111+
const char *file)
112+
{
113+
fprintf(method_file, "%lx %x py::%s:%s\n", (unsigned long)code_addr,
114+
code_size, entry, file);
115+
}
116+
117+
PyObject *
118+
py_trampoline_evaluator(PyThreadState *ts, _PyInterpreterFrame *frame,
119+
int throw)
120+
{
121+
PyCodeObject *co = frame->f_code;
122+
py_trampoline f = NULL;
123+
_PyCode_GetExtra((PyObject *)co, extra_code_index, (void **)&f);
124+
if (f == NULL) {
125+
if (extra_code_index == -1) {
126+
extra_code_index = _PyEval_RequestCodeExtraIndex(NULL);
127+
}
128+
py_trampoline new_trampoline = compile_trampoline();
129+
if (new_trampoline == NULL) {
130+
return NULL;
131+
}
132+
FILE *pfile = perf_map_open(getpid());
133+
perf_map_write_entry(pfile, new_trampoline, code_arena.code_size,
134+
PyUnicode_AsUTF8(co->co_qualname),
135+
PyUnicode_AsUTF8(co->co_filename));
136+
perf_map_close(pfile);
137+
_PyCode_SetExtra((PyObject *)co, extra_code_index,
138+
(void *)new_trampoline);
139+
f = new_trampoline;
140+
}
141+
assert(f != NULL);
142+
return f(_PyEval_EvalFrameDefault, ts, frame, throw);
143+
}
144+
#endif
145+
146+
int
147+
_PyPerfTrampoline_Init(int activate)
148+
{
149+
PyThreadState *tstate = _PyThreadState_GET();
150+
if (!activate) {
151+
tstate->interp->eval_frame = NULL;
152+
}
153+
else {
154+
#ifdef HAVE_PERF_TRAMPOLINE
155+
tstate->interp->eval_frame = py_trampoline_evaluator;
156+
if (new_code_arena() < 0) {
157+
return -1;
158+
}
159+
#endif
160+
}
161+
return 0;
162+
}

Python/initconfig.c

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -745,6 +745,7 @@ _PyConfig_InitCompatConfig(PyConfig *config)
745745
config->use_hash_seed = -1;
746746
config->faulthandler = -1;
747747
config->tracemalloc = -1;
748+
config->perf_profiling = -1;
748749
config->module_search_paths_set = 0;
749750
config->parse_argv = 0;
750751
config->site_import = -1;
@@ -829,6 +830,7 @@ PyConfig_InitIsolatedConfig(PyConfig *config)
829830
config->use_hash_seed = 0;
830831
config->faulthandler = 0;
831832
config->tracemalloc = 0;
833+
config->perf_profiling = 0;
832834
config->safe_path = 1;
833835
config->pathconfig_warnings = 0;
834836
#ifdef MS_WINDOWS
@@ -940,6 +942,7 @@ _PyConfig_Copy(PyConfig *config, const PyConfig *config2)
940942
COPY_ATTR(_install_importlib);
941943
COPY_ATTR(faulthandler);
942944
COPY_ATTR(tracemalloc);
945+
COPY_ATTR(perf_profiling);
943946
COPY_ATTR(import_time);
944947
COPY_ATTR(code_debug_ranges);
945948
COPY_ATTR(show_ref_count);
@@ -1050,6 +1053,7 @@ _PyConfig_AsDict(const PyConfig *config)
10501053
SET_ITEM_UINT(hash_seed);
10511054
SET_ITEM_INT(faulthandler);
10521055
SET_ITEM_INT(tracemalloc);
1056+
SET_ITEM_INT(perf_profiling);
10531057
SET_ITEM_INT(import_time);
10541058
SET_ITEM_INT(code_debug_ranges);
10551059
SET_ITEM_INT(show_ref_count);
@@ -1331,6 +1335,7 @@ _PyConfig_FromDict(PyConfig *config, PyObject *dict)
13311335
CHECK_VALUE("hash_seed", config->hash_seed <= MAX_HASH_SEED);
13321336
GET_UINT(faulthandler);
13331337
GET_UINT(tracemalloc);
1338+
GET_UINT(perf_profiling);
13341339
GET_UINT(import_time);
13351340
GET_UINT(code_debug_ranges);
13361341
GET_UINT(show_ref_count);
@@ -1687,6 +1692,16 @@ config_read_env_vars(PyConfig *config)
16871692
return _PyStatus_OK();
16881693
}
16891694

1695+
static PyStatus
1696+
config_init_perf_profiling(PyConfig *config)
1697+
{
1698+
const wchar_t *xoption = config_get_xoption(config, L"perf");
1699+
if (xoption) {
1700+
config->perf_profiling = 1;
1701+
}
1702+
return _PyStatus_OK();
1703+
1704+
}
16901705

16911706
static PyStatus
16921707
config_init_tracemalloc(PyConfig *config)
@@ -1788,6 +1803,12 @@ config_read_complex_options(PyConfig *config)
17881803
return status;
17891804
}
17901805
}
1806+
if (config->tracemalloc < 0) {
1807+
status = config_init_perf_profiling(config);
1808+
if (_PyStatus_EXCEPTION(status)) {
1809+
return status;
1810+
}
1811+
}
17911812

17921813
if (config->pycache_prefix == NULL) {
17931814
status = config_init_pycache_prefix(config);
@@ -2104,6 +2125,9 @@ config_read(PyConfig *config, int compute_path_config)
21042125
if (config->tracemalloc < 0) {
21052126
config->tracemalloc = 0;
21062127
}
2128+
if (config->perf_profiling < 0) {
2129+
config->perf_profiling = 0;
2130+
}
21072131
if (config->use_hash_seed < 0) {
21082132
config->use_hash_seed = 0;
21092133
config->hash_seed = 0;

Python/pylifecycle.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1149,6 +1149,11 @@ init_interp_main(PyThreadState *tstate)
11491149
if (_PyTraceMalloc_Init(config->tracemalloc) < 0) {
11501150
return _PyStatus_ERR("can't initialize tracemalloc");
11511151
}
1152+
1153+
if (_PyPerfTrampoline_Init(config->perf_profiling) < 0) {
1154+
return _PyStatus_ERR("can't initialize tracemalloc");
1155+
}
1156+
11521157
}
11531158

11541159
status = init_sys_streams(tstate);

0 commit comments

Comments
 (0)