mirror of
https://github.com/python/cpython.git
synced 2026-05-06 04:37:33 -04:00
GH-126910: Add GNU backtrace support for unwinding JIT frames (#149104)
Co-authored-by: Pablo Galindo Salgado <pablogsal@gmail.com>
This commit is contained in:
@@ -0,0 +1,31 @@
|
|||||||
|
#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
|
||||||
@@ -10,9 +10,15 @@
|
|||||||
|
|
||||||
#if defined(_Py_JIT) && defined(__linux__) && defined(__ELF__)
|
#if defined(_Py_JIT) && defined(__linux__) && defined(__ELF__)
|
||||||
# define PY_HAVE_JIT_GDB_UNWIND
|
# 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
|
#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)
|
#if defined(PY_HAVE_JIT_GDB_UNWIND)
|
||||||
extern PyMutex _Py_jit_debug_mutex;
|
extern PyMutex _Py_jit_debug_mutex;
|
||||||
@@ -63,6 +69,13 @@ void *_PyJitUnwind_GdbRegisterCode(const void *code_addr,
|
|||||||
|
|
||||||
void _PyJitUnwind_GdbUnregisterCode(void *handle);
|
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 // Py_INTERNAL_JIT_UNWIND_H
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ extern "C" {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "pycore_typedefs.h" // _PyInterpreterFrame
|
#include "pycore_typedefs.h" // _PyInterpreterFrame
|
||||||
|
#include "pycore_jit_publish.h"
|
||||||
#include "pycore_uop.h" // _PyUOpInstruction
|
#include "pycore_uop.h" // _PyUOpInstruction
|
||||||
#include "pycore_uop_ids.h"
|
#include "pycore_uop_ids.h"
|
||||||
#include "pycore_stackref.h" // _PyStackRef
|
#include "pycore_stackref.h" // _PyStackRef
|
||||||
@@ -198,7 +199,7 @@ typedef struct _PyExecutorObject {
|
|||||||
uint32_t code_size;
|
uint32_t code_size;
|
||||||
size_t jit_size;
|
size_t jit_size;
|
||||||
void *jit_code;
|
void *jit_code;
|
||||||
void *jit_gdb_handle;
|
_PyJitCodeRegistration *jit_registration;
|
||||||
_PyExitData exits[1];
|
_PyExitData exits[1];
|
||||||
} _PyExecutorObject;
|
} _PyExecutorObject;
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,9 @@ if not support.has_subprocess_support:
|
|||||||
raise unittest.SkipTest("test requires subprocess support")
|
raise unittest.SkipTest("test requires subprocess support")
|
||||||
|
|
||||||
|
|
||||||
|
STACK_DEPTH = 10
|
||||||
|
|
||||||
|
|
||||||
def _frame_pointers_expected(machine):
|
def _frame_pointers_expected(machine):
|
||||||
cflags = " ".join(
|
cflags = " ".join(
|
||||||
value for value in (
|
value for value in (
|
||||||
@@ -70,7 +73,7 @@ def _frame_pointers_expected(machine):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def _build_stack_and_unwind():
|
def _build_stack_and_unwind(unwinder):
|
||||||
import operator
|
import operator
|
||||||
|
|
||||||
def build_stack(n, unwinder, warming_up_caller=False):
|
def build_stack(n, unwinder, warming_up_caller=False):
|
||||||
@@ -89,7 +92,7 @@ def _build_stack_and_unwind():
|
|||||||
result = operator.call(build_stack, n - 1, unwinder, warming_up)
|
result = operator.call(build_stack, n - 1, unwinder, warming_up)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
stack = build_stack(10, _testinternalcapi.manual_frame_pointer_unwind)
|
stack = build_stack(STACK_DEPTH, unwinder)
|
||||||
return stack
|
return stack
|
||||||
|
|
||||||
|
|
||||||
@@ -112,8 +115,7 @@ def _classify_stack(stack, jit_enabled):
|
|||||||
return annotated, python_frames, jit_frames, other_frames
|
return annotated, python_frames, jit_frames, other_frames
|
||||||
|
|
||||||
|
|
||||||
def _annotate_unwind():
|
def _summarize_unwind(stack, unwinder_name):
|
||||||
stack = _build_stack_and_unwind()
|
|
||||||
jit_enabled = hasattr(sys, "_jit") and sys._jit.is_enabled()
|
jit_enabled = hasattr(sys, "_jit") and sys._jit.is_enabled()
|
||||||
jit_backend = _testinternalcapi.get_jit_backend()
|
jit_backend = _testinternalcapi.get_jit_backend()
|
||||||
ranges = _testinternalcapi.get_jit_code_ranges() if jit_enabled else []
|
ranges = _testinternalcapi.get_jit_code_ranges() if jit_enabled else []
|
||||||
@@ -126,19 +128,44 @@ def _annotate_unwind():
|
|||||||
)
|
)
|
||||||
for idx, addr, tag in annotated:
|
for idx, addr, tag in annotated:
|
||||||
print(f"#{idx:02d} {addr:#x} -> {tag}")
|
print(f"#{idx:02d} {addr:#x} -> {tag}")
|
||||||
return json.dumps({
|
return {
|
||||||
"length": len(stack),
|
"length": len(stack),
|
||||||
"python_frames": python_frames,
|
"python_frames": python_frames,
|
||||||
"jit_frames": jit_frames,
|
"jit_frames": jit_frames,
|
||||||
"other_frames": other_frames,
|
"other_frames": other_frames,
|
||||||
"jit_backend": jit_backend,
|
"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 = (
|
code = (
|
||||||
"from test.test_frame_pointer_unwind import _annotate_unwind; "
|
f"from test.test_frame_pointer_unwind import {helper_name}; "
|
||||||
"print(_annotate_unwind());"
|
f"print({helper_name}({unwinder_name!r}));"
|
||||||
)
|
)
|
||||||
run_env = os.environ.copy()
|
run_env = os.environ.copy()
|
||||||
run_env.update(env)
|
run_env.update(env)
|
||||||
@@ -166,6 +193,15 @@ def _manual_unwind_length(**env):
|
|||||||
) from exc
|
) 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")
|
@support.requires_gil_enabled("test requires the GIL enabled")
|
||||||
@unittest.skipIf(support.is_wasi, "test not supported on WASI")
|
@unittest.skipIf(support.is_wasi, "test not supported on WASI")
|
||||||
class FramePointerUnwindTests(unittest.TestCase):
|
class FramePointerUnwindTests(unittest.TestCase):
|
||||||
@@ -197,14 +233,14 @@ class FramePointerUnwindTests(unittest.TestCase):
|
|||||||
|
|
||||||
for env, using_jit in envs:
|
for env, using_jit in envs:
|
||||||
with self.subTest(env=env):
|
with self.subTest(env=env):
|
||||||
result = _manual_unwind_length(**env)
|
result = _unwind_result("manual_frame_pointer_unwind", **env)
|
||||||
jit_frames = result["jit_frames"]
|
jit_frames = result["jit_frames"]
|
||||||
python_frames = result.get("python_frames", 0)
|
python_frames = result.get("python_frames", 0)
|
||||||
jit_backend = result.get("jit_backend")
|
jit_backend = result.get("jit_backend")
|
||||||
if self.frame_pointers_expected:
|
if self.frame_pointers_expected:
|
||||||
self.assertGreater(
|
self.assertGreaterEqual(
|
||||||
python_frames,
|
python_frames,
|
||||||
0,
|
STACK_DEPTH,
|
||||||
f"expected to find Python frames on {self.machine} with env {env}",
|
f"expected to find Python frames on {self.machine} with env {env}",
|
||||||
)
|
)
|
||||||
if using_jit:
|
if using_jit:
|
||||||
@@ -240,5 +276,84 @@ class FramePointerUnwindTests(unittest.TestCase):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@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__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|||||||
@@ -470,6 +470,7 @@ PYTHON_OBJS= \
|
|||||||
Python/instruction_sequence.o \
|
Python/instruction_sequence.o \
|
||||||
Python/intrinsics.o \
|
Python/intrinsics.o \
|
||||||
Python/jit.o \
|
Python/jit.o \
|
||||||
|
Python/jit_publish.o \
|
||||||
$(JIT_OBJS) \
|
$(JIT_OBJS) \
|
||||||
Python/legacy_tracing.o \
|
Python/legacy_tracing.o \
|
||||||
Python/lock.o \
|
Python/lock.o \
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
Add support for unwinding JIT frames using GNU backtrace. Patch by Diego Russo and Pablo Galindo
|
||||||
@@ -47,6 +47,9 @@
|
|||||||
#if defined(HAVE_DLADDR) && !defined(__wasi__)
|
#if defined(HAVE_DLADDR) && !defined(__wasi__)
|
||||||
# include <dlfcn.h>
|
# include <dlfcn.h>
|
||||||
#endif
|
#endif
|
||||||
|
#if defined(HAVE_EXECINFO_H)
|
||||||
|
# include <execinfo.h>
|
||||||
|
#endif
|
||||||
#ifdef MS_WINDOWS
|
#ifdef MS_WINDOWS
|
||||||
# include <windows.h>
|
# include <windows.h>
|
||||||
# include <intrin.h>
|
# include <intrin.h>
|
||||||
@@ -58,6 +61,7 @@
|
|||||||
|
|
||||||
|
|
||||||
static const uintptr_t min_frame_pointer_addr = 0x1000;
|
static const uintptr_t min_frame_pointer_addr = 0x1000;
|
||||||
|
#define MAX_UNWIND_FRAMES 200
|
||||||
|
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
@@ -328,7 +332,6 @@ get_jit_backend(PyObject *self, PyObject *Py_UNUSED(args))
|
|||||||
static PyObject *
|
static PyObject *
|
||||||
manual_unwind_from_fp(uintptr_t *frame_pointer)
|
manual_unwind_from_fp(uintptr_t *frame_pointer)
|
||||||
{
|
{
|
||||||
Py_ssize_t max_depth = 200;
|
|
||||||
int stack_grows_down = _Py_STACK_GROWS_DOWN;
|
int stack_grows_down = _Py_STACK_GROWS_DOWN;
|
||||||
|
|
||||||
if (frame_pointer == NULL) {
|
if (frame_pointer == NULL) {
|
||||||
@@ -340,14 +343,20 @@ manual_unwind_from_fp(uintptr_t *frame_pointer)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (Py_ssize_t depth = 0;
|
Py_ssize_t depth = 0;
|
||||||
depth < max_depth && frame_pointer != NULL;
|
while (frame_pointer != NULL) {
|
||||||
depth++)
|
|
||||||
{
|
|
||||||
uintptr_t fp_addr = (uintptr_t)frame_pointer;
|
uintptr_t fp_addr = (uintptr_t)frame_pointer;
|
||||||
if ((fp_addr % sizeof(uintptr_t)) != 0) {
|
if ((fp_addr % sizeof(uintptr_t)) != 0) {
|
||||||
break;
|
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];
|
uintptr_t return_addr = frame_pointer[1];
|
||||||
|
|
||||||
PyObject *addr_obj = PyLong_FromUnsignedLongLong(return_addr);
|
PyObject *addr_obj = PyLong_FromUnsignedLongLong(return_addr);
|
||||||
@@ -361,6 +370,7 @@ manual_unwind_from_fp(uintptr_t *frame_pointer)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
Py_DECREF(addr_obj);
|
Py_DECREF(addr_obj);
|
||||||
|
depth++;
|
||||||
|
|
||||||
uintptr_t *next_fp = (uintptr_t *)frame_pointer[0];
|
uintptr_t *next_fp = (uintptr_t *)frame_pointer[0];
|
||||||
// Stop if the frame pointer is extremely low.
|
// Stop if the frame pointer is extremely low.
|
||||||
@@ -383,6 +393,49 @@ manual_unwind_from_fp(uintptr_t *frame_pointer)
|
|||||||
|
|
||||||
return result;
|
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__)
|
#if defined(__GNUC__) || defined(__clang__)
|
||||||
static PyObject *
|
static PyObject *
|
||||||
manual_frame_pointer_unwind(PyObject *self, PyObject *args)
|
manual_frame_pointer_unwind(PyObject *self, PyObject *args)
|
||||||
@@ -2914,6 +2967,7 @@ static PyMethodDef module_functions[] = {
|
|||||||
{"classify_stack_addresses", classify_stack_addresses, METH_VARARGS},
|
{"classify_stack_addresses", classify_stack_addresses, METH_VARARGS},
|
||||||
{"get_jit_code_ranges", get_jit_code_ranges, METH_NOARGS},
|
{"get_jit_code_ranges", get_jit_code_ranges, METH_NOARGS},
|
||||||
{"get_jit_backend", get_jit_backend, 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},
|
{"manual_frame_pointer_unwind", manual_frame_pointer_unwind, METH_NOARGS},
|
||||||
{"test_bswap", test_bswap, METH_NOARGS},
|
{"test_bswap", test_bswap, METH_NOARGS},
|
||||||
{"test_popcount", test_popcount, METH_NOARGS},
|
{"test_popcount", test_popcount, METH_NOARGS},
|
||||||
|
|||||||
@@ -237,6 +237,7 @@
|
|||||||
<ClCompile Include="..\Python\intrinsics.c" />
|
<ClCompile Include="..\Python\intrinsics.c" />
|
||||||
<ClCompile Include="..\Python\instrumentation.c" />
|
<ClCompile Include="..\Python\instrumentation.c" />
|
||||||
<ClCompile Include="..\Python\jit.c" />
|
<ClCompile Include="..\Python\jit.c" />
|
||||||
|
<ClCompile Include="..\Python\jit_publish.c" />
|
||||||
<ClCompile Include="..\Python\legacy_tracing.c" />
|
<ClCompile Include="..\Python\legacy_tracing.c" />
|
||||||
<ClCompile Include="..\Python\lock.c" />
|
<ClCompile Include="..\Python\lock.c" />
|
||||||
<ClCompile Include="..\Python\marshal.c" />
|
<ClCompile Include="..\Python\marshal.c" />
|
||||||
|
|||||||
@@ -265,6 +265,9 @@
|
|||||||
<ClCompile Include="..\Python\jit.c">
|
<ClCompile Include="..\Python\jit.c">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\Python\jit_publish.c">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
<ClCompile Include="..\Objects\lazyimportobject.c">
|
<ClCompile Include="..\Objects\lazyimportobject.c">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
|||||||
@@ -655,6 +655,7 @@
|
|||||||
<ClCompile Include="..\Python\instruction_sequence.c" />
|
<ClCompile Include="..\Python\instruction_sequence.c" />
|
||||||
<ClCompile Include="..\Python\instrumentation.c" />
|
<ClCompile Include="..\Python\instrumentation.c" />
|
||||||
<ClCompile Include="..\Python\jit.c" />
|
<ClCompile Include="..\Python\jit.c" />
|
||||||
|
<ClCompile Include="..\Python\jit_publish.c" />
|
||||||
<ClCompile Include="..\Python\legacy_tracing.c" />
|
<ClCompile Include="..\Python\legacy_tracing.c" />
|
||||||
<ClCompile Include="..\Python\lock.c" />
|
<ClCompile Include="..\Python\lock.c" />
|
||||||
<ClCompile Include="..\Python\marshal.c" />
|
<ClCompile Include="..\Python\marshal.c" />
|
||||||
|
|||||||
@@ -1493,6 +1493,9 @@
|
|||||||
<ClCompile Include="..\Python\jit.c">
|
<ClCompile Include="..\Python\jit.c">
|
||||||
<Filter>Python</Filter>
|
<Filter>Python</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\Python\jit_publish.c">
|
||||||
|
<Filter>Python</Filter>
|
||||||
|
</ClCompile>
|
||||||
<ClCompile Include="..\Python\legacy_tracing.c">
|
<ClCompile Include="..\Python\legacy_tracing.c">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
|||||||
+5
-42
@@ -15,7 +15,7 @@
|
|||||||
#include "pycore_interpframe.h"
|
#include "pycore_interpframe.h"
|
||||||
#include "pycore_interpolation.h"
|
#include "pycore_interpolation.h"
|
||||||
#include "pycore_intrinsics.h"
|
#include "pycore_intrinsics.h"
|
||||||
#include "pycore_jit_unwind.h"
|
#include "pycore_jit_publish.h"
|
||||||
#include "pycore_lazyimportobject.h"
|
#include "pycore_lazyimportobject.h"
|
||||||
#include "pycore_list.h"
|
#include "pycore_list.h"
|
||||||
#include "pycore_long.h"
|
#include "pycore_long.h"
|
||||||
@@ -61,40 +61,6 @@ jit_error(const char *message)
|
|||||||
PyErr_Format(PyExc_RuntimeWarning, "JIT %s (%d)", message, hint);
|
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
|
static int
|
||||||
address_in_executor_array(_PyExecutorObject **ptrs, size_t count, uintptr_t addr)
|
address_in_executor_array(_PyExecutorObject **ptrs, size_t count, uintptr_t addr)
|
||||||
{
|
{
|
||||||
@@ -750,7 +716,8 @@ _PyJIT_Compile(_PyExecutorObject *executor, const _PyUOpInstruction trace[], siz
|
|||||||
}
|
}
|
||||||
executor->jit_code = memory;
|
executor->jit_code = memory;
|
||||||
executor->jit_size = total_size;
|
executor->jit_size = total_size;
|
||||||
executor->jit_gdb_handle = jit_record_code(memory,
|
executor->jit_registration = _PyJit_RegisterCode(
|
||||||
|
memory,
|
||||||
code_size + state.trampolines.size,
|
code_size + state.trampolines.size,
|
||||||
"jit",
|
"jit",
|
||||||
"executor");
|
"executor");
|
||||||
@@ -766,12 +733,8 @@ _PyJIT_Free(_PyExecutorObject *executor)
|
|||||||
if (memory) {
|
if (memory) {
|
||||||
executor->jit_code = NULL;
|
executor->jit_code = NULL;
|
||||||
executor->jit_size = 0;
|
executor->jit_size = 0;
|
||||||
#if defined(PY_HAVE_JIT_GDB_UNWIND)
|
_PyJit_UnregisterCode(executor->jit_registration);
|
||||||
if (executor->jit_gdb_handle != NULL) {
|
executor->jit_registration = NULL;
|
||||||
_PyJitUnwind_GdbUnregisterCode(executor->jit_gdb_handle);
|
|
||||||
executor->jit_gdb_handle = NULL;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
if (jit_free(memory, size)) {
|
if (jit_free(memory, size)) {
|
||||||
PyErr_FormatUnraisable("Exception ignored while "
|
PyErr_FormatUnraisable("Exception ignored while "
|
||||||
"freeing JIT memory");
|
"freeing JIT memory");
|
||||||
|
|||||||
@@ -0,0 +1,137 @@
|
|||||||
|
#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
|
||||||
+65
-2
@@ -16,11 +16,22 @@
|
|||||||
# endif
|
# endif
|
||||||
#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)
|
#if defined(PY_HAVE_JIT_GDB_UNWIND)
|
||||||
# include <elf.h>
|
# include <elf.h>
|
||||||
#endif
|
#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 <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
@@ -983,4 +994,56 @@ _PyJitUnwind_GdbUnregisterCode(void *handle)
|
|||||||
#endif
|
#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
|
||||||
|
|||||||
+1
-1
@@ -1449,7 +1449,7 @@ allocate_executor(int exit_count, int length)
|
|||||||
res->trace = (_PyUOpInstruction *)(res->exits + exit_count);
|
res->trace = (_PyUOpInstruction *)(res->exits + exit_count);
|
||||||
res->code_size = length;
|
res->code_size = length;
|
||||||
res->exit_count = exit_count;
|
res->exit_count = exit_count;
|
||||||
res->jit_gdb_handle = NULL;
|
res->jit_registration = NULL;
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14424,6 +14424,52 @@ fi
|
|||||||
|
|
||||||
done
|
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
|
if test "x$ac_cv_require_ldl" = xyes
|
||||||
then :
|
then :
|
||||||
|
|
||||||
|
|||||||
@@ -3828,6 +3828,24 @@ AC_CHECK_HEADERS([execinfo.h link.h dlfcn.h], [
|
|||||||
])
|
])
|
||||||
])
|
])
|
||||||
|
|
||||||
|
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)
|
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_require_ldl], [yes], [
|
||||||
AS_VAR_IF([ac_cv_lib_dl_dlopen], [yes], [], [
|
AS_VAR_IF([ac_cv_lib_dl_dlopen], [yes], [], [
|
||||||
|
|||||||
@@ -701,6 +701,10 @@
|
|||||||
/* Define to 1 if you have the 'dld' library (-ldld). */
|
/* Define to 1 if you have the 'dld' library (-ldld). */
|
||||||
#undef HAVE_LIBDLD
|
#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). */
|
/* Define to 1 if you have the 'ieee' library (-lieee). */
|
||||||
#undef HAVE_LIBIEEE
|
#undef HAVE_LIBIEEE
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user