mirror of
https://github.com/python/cpython.git
synced 2026-05-09 03:50:56 +00:00
GH-126910: Add GNU backtrace support for unwinding JIT frames
This commit is contained in:
parent
efcac6f281
commit
db967dca64
14 changed files with 366 additions and 63 deletions
53
Python/jit.c
53
Python/jit.c
|
|
@ -15,7 +15,7 @@
|
|||
#include "pycore_interpframe.h"
|
||||
#include "pycore_interpolation.h"
|
||||
#include "pycore_intrinsics.h"
|
||||
#include "pycore_jit_unwind.h"
|
||||
#include "pycore_jit_publish.h"
|
||||
#include "pycore_lazyimportobject.h"
|
||||
#include "pycore_list.h"
|
||||
#include "pycore_long.h"
|
||||
|
|
@ -61,40 +61,6 @@ jit_error(const char *message)
|
|||
PyErr_Format(PyExc_RuntimeWarning, "JIT %s (%d)", message, hint);
|
||||
}
|
||||
|
||||
/*
|
||||
* Publish JIT code to optional tooling backends.
|
||||
*
|
||||
* The return value is a backend-specific deregistration handle, not a
|
||||
* success/failure indicator. NULL means there is nothing to unregister later:
|
||||
* perf does not need a handle, and GDB registration failures are intentionally
|
||||
* non-fatal because tooling support must not make JIT compilation fail.
|
||||
*/
|
||||
static void *
|
||||
jit_record_code(const void *code_addr, size_t code_size,
|
||||
const char *entry, const char *filename)
|
||||
{
|
||||
#ifdef PY_HAVE_PERF_TRAMPOLINE
|
||||
_PyPerf_Callbacks callbacks;
|
||||
_PyPerfTrampoline_GetCallbacks(&callbacks);
|
||||
if (callbacks.write_state == _Py_perfmap_jit_callbacks.write_state) {
|
||||
_PyPerfJit_WriteNamedCode(
|
||||
code_addr, code_size, entry, filename);
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(PY_HAVE_JIT_GDB_UNWIND)
|
||||
return _PyJitUnwind_GdbRegisterCode(
|
||||
code_addr, code_size, entry, filename);
|
||||
#else
|
||||
(void)code_addr;
|
||||
(void)code_size;
|
||||
(void)entry;
|
||||
(void)filename;
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
static int
|
||||
address_in_executor_array(_PyExecutorObject **ptrs, size_t count, uintptr_t addr)
|
||||
{
|
||||
|
|
@ -750,10 +716,11 @@ _PyJIT_Compile(_PyExecutorObject *executor, const _PyUOpInstruction trace[], siz
|
|||
}
|
||||
executor->jit_code = memory;
|
||||
executor->jit_size = total_size;
|
||||
executor->jit_gdb_handle = jit_record_code(memory,
|
||||
code_size + state.trampolines.size,
|
||||
"jit",
|
||||
"executor");
|
||||
executor->jit_registration = _PyJit_RegisterCode(
|
||||
memory,
|
||||
code_size + state.trampolines.size,
|
||||
"jit",
|
||||
"executor");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -766,12 +733,8 @@ _PyJIT_Free(_PyExecutorObject *executor)
|
|||
if (memory) {
|
||||
executor->jit_code = NULL;
|
||||
executor->jit_size = 0;
|
||||
#if defined(PY_HAVE_JIT_GDB_UNWIND)
|
||||
if (executor->jit_gdb_handle != NULL) {
|
||||
_PyJitUnwind_GdbUnregisterCode(executor->jit_gdb_handle);
|
||||
executor->jit_gdb_handle = NULL;
|
||||
}
|
||||
#endif
|
||||
_PyJit_UnregisterCode(executor->jit_registration);
|
||||
executor->jit_registration = NULL;
|
||||
if (jit_free(memory, size)) {
|
||||
PyErr_FormatUnraisable("Exception ignored while "
|
||||
"freeing JIT memory");
|
||||
|
|
|
|||
128
Python/jit_publish.c
Normal file
128
Python/jit_publish.c
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
#include "Python.h"
|
||||
|
||||
#include "pycore_ceval.h"
|
||||
#include "pycore_jit_publish.h"
|
||||
#include "pycore_jit_unwind.h"
|
||||
|
||||
#ifdef _Py_JIT
|
||||
|
||||
struct _PyJitCodeRegistration {
|
||||
void *gdb_handle;
|
||||
void *gnu_backtrace_handle;
|
||||
};
|
||||
|
||||
static void
|
||||
jit_register_perf_code(const void *code_addr, size_t code_size,
|
||||
const char *entry, const char *filename)
|
||||
{
|
||||
#ifdef PY_HAVE_PERF_TRAMPOLINE
|
||||
_PyPerf_Callbacks callbacks;
|
||||
_PyPerfTrampoline_GetCallbacks(&callbacks);
|
||||
if (callbacks.write_state == _Py_perfmap_jit_callbacks.write_state) {
|
||||
_PyPerfJit_WriteNamedCode(
|
||||
code_addr, code_size, entry, filename);
|
||||
}
|
||||
#else
|
||||
(void)code_addr;
|
||||
(void)code_size;
|
||||
(void)entry;
|
||||
(void)filename;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
jit_register_gdb_code(_PyJitCodeRegistration *registration,
|
||||
const void *code_addr, size_t code_size,
|
||||
const char *entry, const char *filename)
|
||||
{
|
||||
#if defined(PY_HAVE_JIT_GDB_UNWIND)
|
||||
registration->gdb_handle = _PyJitUnwind_GdbRegisterCode(
|
||||
code_addr, code_size, entry, filename);
|
||||
#else
|
||||
(void)registration;
|
||||
(void)code_addr;
|
||||
(void)code_size;
|
||||
(void)entry;
|
||||
(void)filename;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
jit_unregister_gdb_code(_PyJitCodeRegistration *registration)
|
||||
{
|
||||
#if defined(PY_HAVE_JIT_GDB_UNWIND)
|
||||
if (registration->gdb_handle != NULL) {
|
||||
_PyJitUnwind_GdbUnregisterCode(registration->gdb_handle);
|
||||
registration->gdb_handle = NULL;
|
||||
}
|
||||
#else
|
||||
(void)registration;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
jit_register_gnu_backtrace_code(_PyJitCodeRegistration *registration,
|
||||
const void *code_addr, size_t code_size)
|
||||
{
|
||||
#if defined(PY_HAVE_JIT_GNU_BACKTRACE_UNWIND)
|
||||
registration->gnu_backtrace_handle =
|
||||
_PyJitUnwind_GnuBacktraceRegisterCode(code_addr, code_size);
|
||||
#else
|
||||
(void)registration;
|
||||
(void)code_addr;
|
||||
(void)code_size;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
jit_unregister_gnu_backtrace_code(_PyJitCodeRegistration *registration)
|
||||
{
|
||||
#if defined(PY_HAVE_JIT_GNU_BACKTRACE_UNWIND)
|
||||
if (registration->gnu_backtrace_handle != NULL) {
|
||||
_PyJitUnwind_GnuBacktraceUnregisterCode(
|
||||
registration->gnu_backtrace_handle);
|
||||
registration->gnu_backtrace_handle = NULL;
|
||||
}
|
||||
#else
|
||||
(void)registration;
|
||||
#endif
|
||||
}
|
||||
|
||||
_PyJitCodeRegistration *
|
||||
_PyJit_RegisterCode(const void *code_addr, size_t code_size,
|
||||
const char *entry, const char *filename)
|
||||
{
|
||||
jit_register_perf_code(code_addr, code_size, entry, filename);
|
||||
|
||||
_PyJitCodeRegistration *registration = PyMem_RawCalloc(
|
||||
1, sizeof(*registration));
|
||||
if (registration == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
jit_register_gdb_code(
|
||||
registration, code_addr, code_size, entry, filename);
|
||||
jit_register_gnu_backtrace_code(
|
||||
registration, code_addr, code_size);
|
||||
if (registration->gdb_handle == NULL &&
|
||||
registration->gnu_backtrace_handle == NULL)
|
||||
{
|
||||
PyMem_RawFree(registration);
|
||||
return NULL;
|
||||
}
|
||||
return registration;
|
||||
}
|
||||
|
||||
void
|
||||
_PyJit_UnregisterCode(_PyJitCodeRegistration *registration)
|
||||
{
|
||||
if (registration == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
jit_unregister_gnu_backtrace_code(registration);
|
||||
jit_unregister_gdb_code(registration);
|
||||
PyMem_RawFree(registration);
|
||||
}
|
||||
|
||||
#endif // _Py_JIT
|
||||
|
|
@ -16,11 +16,22 @@
|
|||
# endif
|
||||
#endif
|
||||
|
||||
#if defined(PY_HAVE_PERF_TRAMPOLINE) || defined(PY_HAVE_JIT_GDB_UNWIND)
|
||||
#if defined(PY_HAVE_PERF_TRAMPOLINE) \
|
||||
|| defined(PY_HAVE_JIT_GDB_UNWIND) \
|
||||
|| defined(PY_HAVE_JIT_GNU_BACKTRACE_UNWIND)
|
||||
|
||||
#if defined(PY_HAVE_JIT_GDB_UNWIND)
|
||||
# include <elf.h>
|
||||
#endif
|
||||
#if defined(PY_HAVE_JIT_GNU_BACKTRACE_UNWIND)
|
||||
/*
|
||||
* libgcc exposes frame registration entry points, but GCC's public headers
|
||||
* on some distributions do not declare them even though the symbols are
|
||||
* available in libgcc_s.
|
||||
*/
|
||||
void __register_frame(const void *);
|
||||
void __deregister_frame(const void *);
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
|
|
@ -983,4 +994,56 @@ _PyJitUnwind_GdbUnregisterCode(void *handle)
|
|||
#endif
|
||||
}
|
||||
|
||||
#endif // defined(PY_HAVE_PERF_TRAMPOLINE) || defined(PY_HAVE_JIT_GDB_UNWIND)
|
||||
#if defined(PY_HAVE_JIT_GNU_BACKTRACE_UNWIND)
|
||||
void *
|
||||
_PyJitUnwind_GnuBacktraceRegisterCode(const void *code_addr, size_t code_size)
|
||||
{
|
||||
if (code_addr == NULL || code_size == 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_t eh_frame_size = _PyJitUnwind_EhFrameSize(1);
|
||||
if (eh_frame_size == 0) {
|
||||
return NULL;
|
||||
}
|
||||
size_t total_size = eh_frame_size + sizeof(uint32_t);
|
||||
if (total_size < eh_frame_size) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* libgcc's __register_frame walks a .eh_frame section until it finds a
|
||||
* zero-length terminator entry, so keep an extra zeroed word after the
|
||||
* generated CIE/FDE pair.
|
||||
*
|
||||
* See GCC's libgcc/unwind-dw2-fde.c (__register_frame) and
|
||||
* libgcc/unwind-dw2-fde.h (last_fde/next_fde):
|
||||
* https://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-dw2-fde.c
|
||||
* https://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-dw2-fde.h
|
||||
*/
|
||||
uint8_t *eh_frame = PyMem_RawCalloc(1, total_size);
|
||||
if (eh_frame == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
if (_PyJitUnwind_BuildEhFrame(
|
||||
eh_frame, eh_frame_size, code_addr, code_size, 1) == 0) {
|
||||
PyMem_RawFree(eh_frame);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
__register_frame(eh_frame);
|
||||
return eh_frame;
|
||||
}
|
||||
|
||||
void
|
||||
_PyJitUnwind_GnuBacktraceUnregisterCode(void *handle)
|
||||
{
|
||||
if (handle == NULL) {
|
||||
return;
|
||||
}
|
||||
__deregister_frame(handle);
|
||||
PyMem_RawFree(handle);
|
||||
}
|
||||
#endif // defined(PY_HAVE_JIT_GNU_BACKTRACE_UNWIND)
|
||||
|
||||
#endif // JIT unwind support
|
||||
|
|
|
|||
|
|
@ -1448,7 +1448,7 @@ allocate_executor(int exit_count, int length)
|
|||
res->trace = (_PyUOpInstruction *)(res->exits + exit_count);
|
||||
res->code_size = length;
|
||||
res->exit_count = exit_count;
|
||||
res->jit_gdb_handle = NULL;
|
||||
res->jit_registration = NULL;
|
||||
return res;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue