--- /dev/null
+#ifndef Py_INTERNAL_JIT_PUBLISH_H
+#define Py_INTERNAL_JIT_PUBLISH_H
+
+#ifndef Py_BUILD_CORE
+# error "this header requires Py_BUILD_CORE define"
+#endif
+
+#include <stddef.h>
+
+typedef struct _PyJitCodeRegistration _PyJitCodeRegistration;
+
+#ifdef _Py_JIT
+
+/* 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/GNU backtrace registration failures
+ * are intentionally non-fatal because tooling support must not make JIT
+ * compilation fail.
+ */
+_PyJitCodeRegistration *_PyJit_RegisterCode(const void *code_addr,
+ size_t code_size,
+ const char *entry,
+ const char *filename);
+
+void _PyJit_UnregisterCode(_PyJitCodeRegistration *registration);
+
+#endif // _Py_JIT
+
+#endif // Py_INTERNAL_JIT_PUBLISH_H
#if defined(_Py_JIT) && defined(__linux__) && defined(__ELF__)
# define PY_HAVE_JIT_GDB_UNWIND
+# if defined(HAVE_EXECINFO_H) && defined(HAVE_BACKTRACE) && \
+ defined(HAVE_LIBGCC_EH_FRAME_REGISTRATION)
+# define PY_HAVE_JIT_GNU_BACKTRACE_UNWIND
+# 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)
extern PyMutex _Py_jit_debug_mutex;
void _PyJitUnwind_GdbUnregisterCode(void *handle);
-#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);
+
+void _PyJitUnwind_GnuBacktraceUnregisterCode(void *handle);
+#endif
+
+#endif // JIT unwind support
#endif // Py_INTERNAL_JIT_UNWIND_H
#endif
#include "pycore_typedefs.h" // _PyInterpreterFrame
+#include "pycore_jit_publish.h"
#include "pycore_uop.h" // _PyUOpInstruction
#include "pycore_uop_ids.h"
#include "pycore_stackref.h" // _PyStackRef
uint32_t code_size;
size_t jit_size;
void *jit_code;
- void *jit_gdb_handle;
+ _PyJitCodeRegistration *jit_registration;
_PyExitData exits[1];
} _PyExecutorObject;
raise unittest.SkipTest("test requires subprocess support")
+STACK_DEPTH = 10
+
+
def _frame_pointers_expected(machine):
cflags = " ".join(
value for value in (
return None
-def _build_stack_and_unwind():
+def _build_stack_and_unwind(unwinder):
import operator
def build_stack(n, unwinder, warming_up_caller=False):
result = operator.call(build_stack, n - 1, unwinder, warming_up)
return result
- stack = build_stack(10, _testinternalcapi.manual_frame_pointer_unwind)
+ stack = build_stack(STACK_DEPTH, unwinder)
return stack
return annotated, python_frames, jit_frames, other_frames
-def _annotate_unwind():
- stack = _build_stack_and_unwind()
+def _summarize_unwind(stack, unwinder_name):
jit_enabled = hasattr(sys, "_jit") and sys._jit.is_enabled()
jit_backend = _testinternalcapi.get_jit_backend()
ranges = _testinternalcapi.get_jit_code_ranges() if jit_enabled else []
)
for idx, addr, tag in annotated:
print(f"#{idx:02d} {addr:#x} -> {tag}")
- return json.dumps({
+ return {
"length": len(stack),
"python_frames": python_frames,
"jit_frames": jit_frames,
"other_frames": other_frames,
"jit_backend": jit_backend,
+ "unwinder": unwinder_name,
+ }
+
+
+def _annotate_unwind(unwinder_name="manual_frame_pointer_unwind"):
+ unwinder = getattr(_testinternalcapi, unwinder_name)
+ stack = _build_stack_and_unwind(unwinder)
+ return json.dumps(_summarize_unwind(stack, unwinder_name))
+
+
+def _annotate_unwind_after_executor_free(unwinder_name="gnu_backtrace_unwind"):
+ # The first unwind runs at the bottom of _build_stack_and_unwind(), while
+ # the recursive helper may be executing in JIT code. After it returns, this
+ # helper is back in normal test code; clearing executor caches should remove
+ # the old JIT ranges, so the second unwind must not report stale JIT frames.
+ live = json.loads(_annotate_unwind(unwinder_name))
+
+ sys._clear_internal_caches()
+ _testinternalcapi.clear_executor_deletion_list()
+
+ unwinder = getattr(_testinternalcapi, unwinder_name)
+ after_free = _summarize_unwind(unwinder(), unwinder_name)
+ return json.dumps({
+ "live": live,
+ "after_free": after_free,
})
-def _manual_unwind_length(**env):
+def _run_unwind_helper(helper_name, unwinder_name, **env):
code = (
- "from test.test_frame_pointer_unwind import _annotate_unwind; "
- "print(_annotate_unwind());"
+ f"from test.test_frame_pointer_unwind import {helper_name}; "
+ f"print({helper_name}({unwinder_name!r}));"
)
run_env = os.environ.copy()
run_env.update(env)
) from exc
+def _unwind_result(unwinder_name, **env):
+ return _run_unwind_helper("_annotate_unwind", unwinder_name, **env)
+
+
+def _unwind_after_executor_free_result(unwinder_name, **env):
+ return _run_unwind_helper(
+ "_annotate_unwind_after_executor_free", unwinder_name, **env)
+
+
@support.requires_gil_enabled("test requires the GIL enabled")
@unittest.skipIf(support.is_wasi, "test not supported on WASI")
class FramePointerUnwindTests(unittest.TestCase):
for env, using_jit in envs:
with self.subTest(env=env):
- result = _manual_unwind_length(**env)
+ result = _unwind_result("manual_frame_pointer_unwind", **env)
jit_frames = result["jit_frames"]
python_frames = result.get("python_frames", 0)
jit_backend = result.get("jit_backend")
if self.frame_pointers_expected:
- self.assertGreater(
+ self.assertGreaterEqual(
python_frames,
- 0,
+ STACK_DEPTH,
f"expected to find Python frames on {self.machine} with env {env}",
)
if using_jit:
)
+@support.requires_gil_enabled("test requires the GIL enabled")
+@unittest.skipIf(support.is_wasi, "test not supported on WASI")
+@unittest.skipUnless(sys.platform == "linux", "GNU backtrace unwinding test requires Linux")
+class GnuBacktraceUnwindTests(unittest.TestCase):
+
+ def setUp(self):
+ super().setUp()
+ try:
+ _testinternalcapi.gnu_backtrace_unwind()
+ except RuntimeError as exc:
+ if "not supported" in str(exc):
+ self.skipTest("gnu backtrace unwinding not supported on this platform")
+ raise
+
+ def test_gnu_backtrace_unwinds_through_jit_frames(self):
+ jit_available = hasattr(sys, "_jit") and sys._jit.is_available()
+ envs = [({"PYTHON_JIT": "0"}, False)]
+ if jit_available:
+ envs.append(({"PYTHON_JIT": "1"}, True))
+
+ for env, using_jit in envs:
+ with self.subTest(env=env):
+ result = _unwind_result("gnu_backtrace_unwind", **env)
+ python_frames = result.get("python_frames", 0)
+ jit_frames = result.get("jit_frames", 0)
+ jit_backend = result.get("jit_backend")
+
+ self.assertGreaterEqual(
+ python_frames,
+ STACK_DEPTH,
+ f"expected to find Python frames in GNU backtrace with env {env}",
+ )
+ if using_jit and jit_backend == "jit":
+ self.assertGreater(
+ jit_frames,
+ 0,
+ f"expected GNU backtrace to include JIT frames with env {env}",
+ )
+ else:
+ self.assertEqual(
+ jit_frames,
+ 0,
+ f"unexpected JIT frames counted in GNU backtrace with env {env}",
+ )
+
+ def test_gnu_backtrace_jit_frames_disappear_after_executor_free(self):
+ if not (hasattr(sys, "_jit") and sys._jit.is_available()):
+ self.skipTest("JIT is not available")
+
+ result = _unwind_after_executor_free_result(
+ "gnu_backtrace_unwind", PYTHON_JIT="1")
+ live = result["live"]
+ if live.get("jit_backend") != "jit":
+ self.skipTest("JIT backend is not active")
+
+ self.assertGreaterEqual(
+ live.get("python_frames", 0),
+ STACK_DEPTH,
+ "expected live GNU backtrace to include recursive Python frames",
+ )
+ self.assertGreater(
+ live.get("jit_frames", 0),
+ 0,
+ "expected live GNU backtrace to include JIT frames",
+ )
+
+ after_free = result["after_free"]
+ self.assertGreater(
+ after_free.get("python_frames", 0),
+ 0,
+ "expected GNU backtrace after executor free to include Python frames",
+ )
+ self.assertEqual(
+ after_free.get("jit_frames", 0),
+ 0,
+ "unexpected JIT frames in GNU backtrace after executor free",
+ )
+
+
if __name__ == "__main__":
unittest.main()
Python/instruction_sequence.o \
Python/intrinsics.o \
Python/jit.o \
+ Python/jit_publish.o \
$(JIT_OBJS) \
Python/legacy_tracing.o \
Python/lock.o \
--- /dev/null
+Add support for unwinding JIT frames using GNU backtrace. Patch by Diego Russo and Pablo Galindo
#if defined(HAVE_DLADDR) && !defined(__wasi__)
# include <dlfcn.h>
#endif
+#if defined(HAVE_EXECINFO_H)
+# include <execinfo.h>
+#endif
#ifdef MS_WINDOWS
# include <windows.h>
# include <intrin.h>
static const uintptr_t min_frame_pointer_addr = 0x1000;
+#define MAX_UNWIND_FRAMES 200
static PyObject *
static PyObject *
manual_unwind_from_fp(uintptr_t *frame_pointer)
{
- Py_ssize_t max_depth = 200;
int stack_grows_down = _Py_STACK_GROWS_DOWN;
if (frame_pointer == NULL) {
return NULL;
}
- for (Py_ssize_t depth = 0;
- depth < max_depth && frame_pointer != NULL;
- depth++)
- {
+ Py_ssize_t depth = 0;
+ while (frame_pointer != NULL) {
uintptr_t fp_addr = (uintptr_t)frame_pointer;
if ((fp_addr % sizeof(uintptr_t)) != 0) {
break;
}
+ if (depth >= MAX_UNWIND_FRAMES) {
+ Py_DECREF(result);
+ PyErr_Format(
+ PyExc_RuntimeError,
+ "manual frame pointer unwind returned more than %d frames",
+ MAX_UNWIND_FRAMES);
+ return NULL;
+ }
uintptr_t return_addr = frame_pointer[1];
PyObject *addr_obj = PyLong_FromUnsignedLongLong(return_addr);
return NULL;
}
Py_DECREF(addr_obj);
+ depth++;
uintptr_t *next_fp = (uintptr_t *)frame_pointer[0];
// Stop if the frame pointer is extremely low.
return result;
}
+
+#if defined(HAVE_EXECINFO_H) && defined(HAVE_BACKTRACE)
+static PyObject *
+gnu_backtrace_unwind(PyObject *self, PyObject *Py_UNUSED(args))
+{
+ void *addresses[MAX_UNWIND_FRAMES + 1];
+ int frame_count = backtrace(addresses, (int)Py_ARRAY_LENGTH(addresses));
+ if (frame_count < 0) {
+ PyErr_SetString(PyExc_RuntimeError, "backtrace() failed");
+ return NULL;
+ }
+ if (frame_count > MAX_UNWIND_FRAMES) {
+ PyErr_Format(
+ PyExc_RuntimeError,
+ "backtrace() returned more than %d frames",
+ MAX_UNWIND_FRAMES);
+ return NULL;
+ }
+
+ PyObject *result = PyList_New(frame_count);
+ if (result == NULL) {
+ return NULL;
+ }
+ for (int i = 0; i < frame_count; i++) {
+ PyObject *addr_obj = PyLong_FromUnsignedLongLong((uintptr_t)addresses[i]);
+ if (addr_obj == NULL) {
+ Py_DECREF(result);
+ return NULL;
+ }
+ PyList_SET_ITEM(result, i, addr_obj);
+ }
+ return result;
+}
+#else
+static PyObject *
+gnu_backtrace_unwind(PyObject *self, PyObject *Py_UNUSED(args))
+{
+ PyErr_SetString(PyExc_RuntimeError,
+ "gnu_backtrace_unwind is not supported on this platform");
+ return NULL;
+}
+#endif
+
#if defined(__GNUC__) || defined(__clang__)
static PyObject *
manual_frame_pointer_unwind(PyObject *self, PyObject *args)
{"classify_stack_addresses", classify_stack_addresses, METH_VARARGS},
{"get_jit_code_ranges", get_jit_code_ranges, METH_NOARGS},
{"get_jit_backend", get_jit_backend, METH_NOARGS},
+ {"gnu_backtrace_unwind", gnu_backtrace_unwind, METH_NOARGS},
{"manual_frame_pointer_unwind", manual_frame_pointer_unwind, METH_NOARGS},
{"test_bswap", test_bswap, METH_NOARGS},
{"test_popcount", test_popcount, METH_NOARGS},
<ClCompile Include="..\Python\intrinsics.c" />
<ClCompile Include="..\Python\instrumentation.c" />
<ClCompile Include="..\Python\jit.c" />
+ <ClCompile Include="..\Python\jit_publish.c" />
<ClCompile Include="..\Python\legacy_tracing.c" />
<ClCompile Include="..\Python\lock.c" />
<ClCompile Include="..\Python\marshal.c" />
<ClCompile Include="..\Python\jit.c">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="..\Python\jit_publish.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
<ClCompile Include="..\Objects\lazyimportobject.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\Python\instruction_sequence.c" />
<ClCompile Include="..\Python\instrumentation.c" />
<ClCompile Include="..\Python\jit.c" />
+ <ClCompile Include="..\Python\jit_publish.c" />
<ClCompile Include="..\Python\legacy_tracing.c" />
<ClCompile Include="..\Python\lock.c" />
<ClCompile Include="..\Python\marshal.c" />
<ClCompile Include="..\Python\jit.c">
<Filter>Python</Filter>
</ClCompile>
+ <ClCompile Include="..\Python\jit_publish.c">
+ <Filter>Python</Filter>
+ </ClCompile>
<ClCompile Include="..\Python\legacy_tracing.c">
<Filter>Source Files</Filter>
</ClCompile>
#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"
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)
{
}
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;
}
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");
--- /dev/null
+#include "Python.h"
+
+#include "pycore_ceval.h"
+#include "pycore_jit_publish.h"
+#include "pycore_jit_unwind.h"
+
+#ifdef _Py_JIT
+
+#if defined(PY_HAVE_JIT_GDB_UNWIND) \
+ || defined(PY_HAVE_JIT_GNU_BACKTRACE_UNWIND)
+struct _PyJitCodeRegistration {
+ void *gdb_handle;
+ void *gnu_backtrace_handle;
+};
+#endif
+
+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
+}
+
+#if defined(PY_HAVE_JIT_GDB_UNWIND)
+static void
+jit_register_gdb_code(_PyJitCodeRegistration *registration,
+ const void *code_addr, size_t code_size,
+ const char *entry, const char *filename)
+{
+ registration->gdb_handle = _PyJitUnwind_GdbRegisterCode(
+ code_addr, code_size, entry, filename);
+}
+
+static void
+jit_unregister_gdb_code(_PyJitCodeRegistration *registration)
+{
+ if (registration->gdb_handle != NULL) {
+ _PyJitUnwind_GdbUnregisterCode(registration->gdb_handle);
+ registration->gdb_handle = NULL;
+ }
+}
+#endif
+
+#if defined(PY_HAVE_JIT_GNU_BACKTRACE_UNWIND)
+static void
+jit_register_gnu_backtrace_code(_PyJitCodeRegistration *registration,
+ const void *code_addr, size_t code_size)
+{
+ registration->gnu_backtrace_handle =
+ _PyJitUnwind_GnuBacktraceRegisterCode(code_addr, code_size);
+}
+
+static void
+jit_unregister_gnu_backtrace_code(_PyJitCodeRegistration *registration)
+{
+ if (registration->gnu_backtrace_handle != NULL) {
+ _PyJitUnwind_GnuBacktraceUnregisterCode(
+ registration->gnu_backtrace_handle);
+ registration->gnu_backtrace_handle = NULL;
+ }
+}
+#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);
+ // Perf publication has no teardown handle, so it is intentionally
+ // not counted below.
+
+#if !defined(PY_HAVE_JIT_GDB_UNWIND) \
+ && !defined(PY_HAVE_JIT_GNU_BACKTRACE_UNWIND)
+ return NULL;
+#else
+ _PyJitCodeRegistration *registration = PyMem_RawCalloc(
+ 1, sizeof(*registration));
+ if (registration == NULL) {
+ return NULL;
+ }
+
+ // Partial failures are non-fatal: the JIT code can still execute, but
+ // unavailable tooling may not be able to unwind it.
+ int any_registered = 0;
+# if defined(PY_HAVE_JIT_GDB_UNWIND)
+ jit_register_gdb_code(
+ registration, code_addr, code_size, entry, filename);
+ any_registered |= registration->gdb_handle != NULL;
+# endif
+# if defined(PY_HAVE_JIT_GNU_BACKTRACE_UNWIND)
+ jit_register_gnu_backtrace_code(
+ registration, code_addr, code_size);
+ any_registered |= registration->gnu_backtrace_handle != NULL;
+# endif
+ if (!any_registered) {
+ PyMem_RawFree(registration);
+ return NULL;
+ }
+ return registration;
+#endif
+}
+
+void
+_PyJit_UnregisterCode(_PyJitCodeRegistration *registration)
+{
+#if !defined(PY_HAVE_JIT_GDB_UNWIND) \
+ && !defined(PY_HAVE_JIT_GNU_BACKTRACE_UNWIND)
+ assert(registration == NULL);
+ (void)registration;
+#else
+ if (registration == NULL) {
+ return;
+ }
+
+# if defined(PY_HAVE_JIT_GNU_BACKTRACE_UNWIND)
+ jit_unregister_gnu_backtrace_code(registration);
+# endif
+# if defined(PY_HAVE_JIT_GDB_UNWIND)
+ jit_unregister_gdb_code(registration);
+# endif
+ PyMem_RawFree(registration);
+#endif
+}
+
+#endif // _Py_JIT
# 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>
#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
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;
}
done
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for libgcc frame registration functions" >&5
+printf %s "checking for libgcc frame registration functions... " >&6; }
+if test ${ac_cv_have_libgcc_eh_frame_registration+y}
+then :
+ printf %s "(cached) " >&6
+else case e in #(
+ e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+void __register_frame(const void *);
+void __deregister_frame(const void *);
+
+int
+main (void)
+{
+
+__register_frame(0);
+__deregister_frame(0);
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"
+then :
+ ac_cv_have_libgcc_eh_frame_registration=yes
+else case e in #(
+ e) ac_cv_have_libgcc_eh_frame_registration=no ;;
+esac
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
+ conftest$ac_exeext conftest.$ac_ext
+ ;;
+esac
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have_libgcc_eh_frame_registration" >&5
+printf "%s\n" "$ac_cv_have_libgcc_eh_frame_registration" >&6; }
+if test "x$ac_cv_have_libgcc_eh_frame_registration" = xyes
+then :
+
+
+printf "%s\n" "#define HAVE_LIBGCC_EH_FRAME_REGISTRATION 1" >>confdefs.h
+
+
+fi
+
if test "x$ac_cv_require_ldl" = xyes
then :
])
])
+dnl for JIT GNU backtrace unwind registration
+AC_CACHE_CHECK([for libgcc frame registration functions],
+ [ac_cv_have_libgcc_eh_frame_registration],
+ [AC_LINK_IFELSE([AC_LANG_PROGRAM([[
+void __register_frame(const void *);
+void __deregister_frame(const void *);
+]], [[
+__register_frame(0);
+__deregister_frame(0);
+]])],
+ [ac_cv_have_libgcc_eh_frame_registration=yes],
+ [ac_cv_have_libgcc_eh_frame_registration=no])
+ ])
+AS_VAR_IF([ac_cv_have_libgcc_eh_frame_registration], [yes], [
+ AC_DEFINE([HAVE_LIBGCC_EH_FRAME_REGISTRATION], [1],
+ [Define to 1 if libgcc __register_frame and __deregister_frame are linkable.])
+])
+
dnl only add -ldl to LDFLAGS if it isn't already part of LIBS (GH-133081)
AS_VAR_IF([ac_cv_require_ldl], [yes], [
AS_VAR_IF([ac_cv_lib_dl_dlopen], [yes], [], [
/* Define to 1 if you have the 'dld' library (-ldld). */
#undef HAVE_LIBDLD
+/* Define to 1 if libgcc __register_frame and __deregister_frame are linkable.
+ */
+#undef HAVE_LIBGCC_EH_FRAME_REGISTRATION
+
/* Define to 1 if you have the 'ieee' library (-lieee). */
#undef HAVE_LIBIEEE