Remove frameptr-based unwinder.

The frameptr-based unwinder was an interim solution while issues with
libunwind were resolved. It's better if we remove it,
so we don't have to support it moving forward.
This commit is contained in:
Tony Printezis 2026-07-10 05:03:48 -07:00
parent afeda129b0
commit e173c17cf6
14 changed files with 2 additions and 345 deletions

View file

@ -139,7 +139,6 @@ any of the following arguments (not a definitive list) to 'configure':
in the following list that appears to function correctly:
+ libunwind (requires --enable-prof-libunwind)
+ frame pointer (requires --enable-prof-frameptr)
+ libgcc (unless --disable-prof-libgcc)
+ gcc intrinsics (unless --disable-prof-gcc)
@ -148,12 +147,6 @@ any of the following arguments (not a definitive list) to 'configure':
Use the libunwind library (http://www.nongnu.org/libunwind/) for stack
backtracing.
* `--enable-prof-frameptr`
Use the optimized frame pointer unwinder for stack backtracing. Safe
to use in mixed code (with and without frame pointers) - but requires
frame pointers to produce meaningful stacks. Linux only.
* `--disable-prof-libgcc`
Disable the use of libgcc's backtracing functionality.

View file

@ -146,7 +146,6 @@ C_SRCS := $(srcroot)src/jemalloc.c \
$(srcroot)src/prof_data.c \
$(srcroot)src/prof_log.c \
$(srcroot)src/prof_recent.c \
$(srcroot)src/prof_stack_range.c \
$(srcroot)src/prof_stats.c \
$(srcroot)src/prof_sys.c \
$(srcroot)src/psset.c \

View file

@ -1557,33 +1557,6 @@ if test "x$backtrace_method" = "x" -a "x$enable_prof_libunwind" = "x1" ; then
fi
fi
if test `uname -s` = "Linux"
then
AC_ARG_ENABLE([prof-frameptr],
[AS_HELP_STRING([--enable-prof-frameptr], [Use optimized frame pointer unwinder for backtracing (Linux only)])],
[if test "x$enable_prof_frameptr" = "xno" ; then
enable_prof_frameptr="0"
else
enable_prof_frameptr="1"
if test "x$enable_prof" = "x0" ; then
AC_MSG_ERROR([--enable-prof-frameptr should only be used with --enable-prof])
fi
fi
],
[enable_prof_frameptr="0"]
)
if test "x$backtrace_method" = "x" -a "x$enable_prof_frameptr" = "x1" \
-a "x$GCC" = "xyes" ; then
JE_CFLAGS_ADD([-fno-omit-frame-pointer])
backtrace_method="frame pointer linux"
AC_DEFINE([JEMALLOC_PROF_FRAME_POINTER], [ ], [ ])
else
enable_prof_frameptr="0"
fi
else
enable_prof_frameptr="0"
fi
AC_ARG_ENABLE([prof-libgcc],
[AS_HELP_STRING([--disable-prof-libgcc],
[Do not use libgcc for backtracing])],
@ -3186,7 +3159,6 @@ AC_MSG_RESULT([user_config : ${enable_user_config}])
AC_MSG_RESULT([experimental_smallocx : ${enable_experimental_smallocx}])
AC_MSG_RESULT([prof : ${enable_prof}])
AC_MSG_RESULT([prof-libunwind : ${enable_prof_libunwind}])
AC_MSG_RESULT([prof-frameptr : ${enable_prof_frameptr}])
AC_MSG_RESULT([prof-libgcc : ${enable_prof_libgcc}])
AC_MSG_RESULT([prof-gcc : ${enable_prof_gcc}])
AC_MSG_RESULT([fill : ${enable_fill}])

View file

@ -175,9 +175,6 @@
/* Use gcc intrinsics for profile backtracing if defined. */
#undef JEMALLOC_PROF_GCC
/* Use frame pointer for profile backtracing if defined. Linux only. */
#undef JEMALLOC_PROF_FRAME_POINTER
/* JEMALLOC_PAGEID enabled page id */
#undef JEMALLOC_PAGEID

View file

@ -130,13 +130,6 @@ static const bool config_prof_libunwind =
false
#endif
;
static const bool config_prof_frameptr =
#ifdef JEMALLOC_PROF_FRAME_POINTER
true
#else
false
#endif
;
static const bool maps_coalesce =
#ifdef JEMALLOC_MAPS_COALESCE
true

View file

@ -89,13 +89,4 @@ malloc_close(int fd) {
#endif
}
static inline off_t
malloc_lseek(int fd, off_t offset, int whence) {
#if defined(JEMALLOC_USE_SYSCALL) && defined(SYS_lseek)
return (off_t)syscall(SYS_lseek, fd, offset, whence);
#else
return lseek(fd, offset, whence);
#endif
}
#endif /* JEMALLOC_INTERNAL_MALLOC_IO_H */

View file

@ -21,7 +21,6 @@ void prof_fdump_impl(tsd_t *tsd);
void prof_idump_impl(tsd_t *tsd);
bool prof_mdump_impl(tsd_t *tsd, const char *filename);
void prof_gdump_impl(tsd_t *tsd);
int prof_thread_stack_range(uintptr_t fp, uintptr_t *low, uintptr_t *high);
/* Used in unit tests. */
typedef int(prof_sys_thread_name_read_t)(char *buf, size_t limit);

View file

@ -224,11 +224,8 @@ def generate_linux_job(arch):
if arch != ARM64:
exclude += [LARGE_HUGEPAGE]
linux_configure_flags = list(configure_flag_unusuals)
linux_configure_flags.append(Option.as_configure_flag("--enable-prof --enable-prof-frameptr"))
linux_unusuals = (compilers_unusual + feature_unusuals
+ linux_configure_flags + malloc_conf_unusuals)
+ configure_flag_unusuals + malloc_conf_unusuals)
matrix_entries = generate_job_matrix_entries(os, arch, exclude, max_unusual_opts, linux_unusuals)

View file

@ -264,11 +264,8 @@ def generate_linux(arch):
if arch != ARM64:
exclude += [LARGE_HUGEPAGE]
linux_configure_flags = list(configure_flag_unusuals)
linux_configure_flags.append(Option.as_configure_flag("--enable-prof --enable-prof-frameptr"))
linux_unusuals = (compilers_unusual + feature_unusuals
+ linux_configure_flags + malloc_conf_unusuals)
+ configure_flag_unusuals + malloc_conf_unusuals)
return generate_jobs(os, arch, exclude, max_unusual_opts, linux_unusuals)

View file

@ -121,7 +121,6 @@ CTL_PROTO(config_opt_safety_checks)
CTL_PROTO(config_prof)
CTL_PROTO(config_prof_libgcc)
CTL_PROTO(config_prof_libunwind)
CTL_PROTO(config_prof_frameptr)
CTL_PROTO(config_stats)
CTL_PROTO(config_utrace)
CTL_PROTO(config_xmalloc)
@ -495,7 +494,6 @@ static const ctl_named_node_t config_node[] = {
{NAME("prof"), CTL(config_prof)},
{NAME("prof_libgcc"), CTL(config_prof_libgcc)},
{NAME("prof_libunwind"), CTL(config_prof_libunwind)},
{NAME("prof_frameptr"), CTL(config_prof_frameptr)},
{NAME("stats"), CTL(config_stats)}, {NAME("utrace"), CTL(config_utrace)},
{NAME("xmalloc"), CTL(config_xmalloc)}};
@ -2210,7 +2208,6 @@ CTL_RO_CONFIG_GEN(config_opt_safety_checks, bool)
CTL_RO_CONFIG_GEN(config_prof, bool)
CTL_RO_CONFIG_GEN(config_prof_libgcc, bool)
CTL_RO_CONFIG_GEN(config_prof_libunwind, bool)
CTL_RO_CONFIG_GEN(config_prof_frameptr, bool)
CTL_RO_CONFIG_GEN(config_stats, bool)
CTL_RO_CONFIG_GEN(config_utrace, bool)
CTL_RO_CONFIG_GEN(config_xmalloc, bool)

View file

@ -1,174 +0,0 @@
#include "jemalloc/internal/jemalloc_preamble.h"
#include "jemalloc/internal/malloc_io.h"
#include "jemalloc/internal/prof_sys.h"
#if defined(__linux__) && defined(JEMALLOC_HAVE_GETTID)
# include <errno.h>
# include <fcntl.h>
# include <stdio.h>
# include <stdlib.h> // strtoul
# include <string.h>
# include <unistd.h>
/*
* Converts a string representing a hexadecimal number to an unsigned long long
* integer. Functionally equivalent to strtoull() (for base 16) but faster for
* that case.
*
* @param nptr Pointer to the string to be converted.
* @param endptr Pointer to a pointer to character, which will be set to the
* character in `nptr` where parsing stopped. Can be NULL.
* @return The converted unsigned long long integer value.
*/
static inline unsigned long long int
strtoull_hex(const char *nptr, char **endptr) {
unsigned long long int val = 0;
int ii = 0;
for (; ii < 16; ++ii) {
char c = nptr[ii];
if (c >= '0' && c <= '9') {
val = (val << 4) + (c - '0');
} else if (c >= 'a' && c <= 'f') {
val = (val << 4) + (c - 'a' + 10);
} else {
break;
}
}
if (endptr) {
*endptr = (char *)(nptr + ii);
}
return val;
}
static int
prof_mapping_containing_addr(uintptr_t addr, const char *maps_path,
uintptr_t *mm_start, uintptr_t *mm_end) {
int ret = ENOENT; /* not found */
*mm_start = *mm_end = 0;
/*
* Each line of /proc/<pid>/maps is:
* <start>-<end> <perms> <offset> <dev> <inode> <pathname>
*
* The fields we care about are always within the first 34 characters so
* as long as `buf` contains the start of a mapping line it can always be
* parsed.
*/
static const int kMappingFieldsWidth = 34;
int fd = -1;
char buf[4096];
ssize_t remaining = 0; /* actual number of bytes read to buf */
char *line = NULL;
while (1) {
if (fd < 0) {
/* case 0: initial open of maps file */
fd = malloc_open(maps_path, O_RDONLY);
if (fd < 0) {
return errno;
}
remaining = malloc_read_fd(fd, buf, sizeof(buf));
if (remaining < 0) {
ret = errno;
break;
} else if (remaining == 0) {
break;
}
line = buf;
} else if (line == NULL) {
/* case 1: no newline found in buf */
remaining = malloc_read_fd(fd, buf, sizeof(buf));
if (remaining < 0) {
ret = errno;
break;
} else if (remaining == 0) {
break;
}
line = memchr(buf, '\n', remaining);
if (line != NULL) {
line++; /* advance to character after newline */
remaining -= (line - buf);
}
} else if (line != NULL && remaining < kMappingFieldsWidth) {
/*
* case 2: found newline but insufficient characters remaining in
* buf
*/
memcpy(buf, line,
remaining); /* copy remaining characters to start of buf */
line = buf;
ssize_t count = malloc_read_fd(
fd, buf + remaining, sizeof(buf) - remaining);
if (count < 0) {
ret = errno;
break;
} else if (count == 0) {
break;
}
remaining +=
count; /* actual number of bytes read to buf */
} else {
/* case 3: found newline and sufficient characters to parse */
/* parse <start>-<end> */
char *tmp = line;
uintptr_t start_addr = (uintptr_t)strtoull_hex(
tmp, &tmp);
if (addr >= start_addr) {
tmp++; /* advance to character after '-' */
uintptr_t end_addr = (uintptr_t)strtoull_hex(
tmp, NULL);
if (addr < end_addr) {
*mm_start = start_addr;
*mm_end = end_addr;
ret = 0;
break;
}
}
/* Advance to character after next newline in the current buf. */
char *prev_line = line;
line = memchr(line, '\n', remaining);
if (line != NULL) {
line++; /* advance to character after newline */
remaining -= (line - prev_line);
}
}
}
malloc_close(fd);
return ret;
}
int
prof_thread_stack_range(uintptr_t fp, uintptr_t *low, uintptr_t *high) {
/*
* NOTE: Prior to kernel 4.5 an entry for every thread stack was included in
* /proc/<pid>/maps as [STACK:<tid>]. Starting with kernel 4.5 only the main
* thread stack remains as the [stack] mapping. For other thread stacks the
* mapping is still visible in /proc/<pid>/task/<tid>/maps (though not
* labeled as [STACK:tid]).
* https://lists.ubuntu.com/archives/kernel-team/2016-March/074681.html
*/
char maps_path[64]; // "/proc/<pid>/task/<tid>/maps"
malloc_snprintf(maps_path, sizeof(maps_path), "/proc/%d/task/%d/maps",
getpid(), gettid());
return prof_mapping_containing_addr(fp, maps_path, low, high);
}
#else
int
prof_thread_stack_range(
UNUSED uintptr_t addr, uintptr_t *stack_start, uintptr_t *stack_end) {
*stack_start = *stack_end = 0;
return ENOENT;
}
#endif // __linux__

View file

@ -28,11 +28,6 @@
JEMALLOC_TEST_HOOK(_Unwind_Backtrace, test_hooks_libc_hook)
#endif
#ifdef JEMALLOC_PROF_FRAME_POINTER
// execinfo backtrace() as fallback unwinder
# include <execinfo.h>
#endif
/******************************************************************************/
malloc_mutex_t prof_dump_filename_mtx;
@ -109,103 +104,6 @@ prof_backtrace_impl(void **vec, unsigned *len, unsigned max_len) {
_Unwind_Backtrace(prof_unwind_callback, &data);
}
#elif (defined(JEMALLOC_PROF_FRAME_POINTER))
JEMALLOC_DIAGNOSTIC_PUSH
JEMALLOC_DIAGNOSTIC_IGNORE_FRAME_ADDRESS
struct stack_range {
uintptr_t start;
uintptr_t end;
};
struct thread_unwind_info {
struct stack_range stack_range;
bool fallback;
};
static __thread struct thread_unwind_info unwind_info = {
.stack_range =
{
.start = 0,
.end = 0,
},
.fallback = false,
}; /* thread local */
static void
prof_backtrace_impl(void **vec, unsigned *len, unsigned max_len) {
/* fp: current stack frame pointer
*
* stack_range: readable stack memory range for the current thread.
* Used to validate frame addresses during stack unwinding.
* For most threads there is a single valid stack range
* that is fixed at thread creation time. This may not be
* the case when folly fibers or boost contexts are used.
* In those cases fall back to using execinfo backtrace()
* (DWARF unwind).
*/
/* always safe to get the current stack frame address */
uintptr_t fp = (uintptr_t)__builtin_frame_address(0);
/* new thread - get the stack range */
if (!unwind_info.fallback
&& unwind_info.stack_range.start == unwind_info.stack_range.end) {
if (prof_thread_stack_range(fp, &unwind_info.stack_range.start,
&unwind_info.stack_range.end)
!= 0) {
unwind_info.fallback = true;
} else {
assert(fp >= unwind_info.stack_range.start
&& fp < unwind_info.stack_range.end);
}
}
if (unwind_info.fallback) {
goto label_fallback;
}
unsigned ii = 0;
while (ii < max_len && fp != 0) {
if (fp < unwind_info.stack_range.start
|| fp >= unwind_info.stack_range.end) {
/*
* Determining the stack range from procfs can be
* relatively expensive especially for programs with
* many threads / shared libraries. If the stack
* range has changed, it is likely to change again
* in the future (fibers or some other stack
* manipulation). So fall back to backtrace for this
* thread.
*/
unwind_info.fallback = true;
goto label_fallback;
}
void *ip = ((void **)fp)[1];
if (ip == 0) {
break;
}
vec[ii++] = ip;
fp = ((uintptr_t *)fp)[0];
}
*len = ii;
return;
label_fallback:
/*
* Using the backtrace from execinfo.h here. Note that it may get
* redirected to libunwind when a libunwind not built with build-time
* flag --disable-weak-backtrace is linked.
*/
assert(unwind_info.fallback);
int nframes = backtrace(vec, max_len);
if (nframes > 0) {
*len = nframes;
} else {
*len = 0;
}
}
JEMALLOC_DIAGNOSTIC_POP
#elif (defined(JEMALLOC_PROF_GCC))
JEMALLOC_DIAGNOSTIC_PUSH
JEMALLOC_DIAGNOSTIC_IGNORE_FRAME_ADDRESS

View file

@ -1667,7 +1667,6 @@ stats_general_print(emitter_t *emitter) {
CONFIG_WRITE_BOOL(prof);
CONFIG_WRITE_BOOL(prof_libgcc);
CONFIG_WRITE_BOOL(prof_libunwind);
CONFIG_WRITE_BOOL(prof_frameptr);
CONFIG_WRITE_BOOL(stats);
CONFIG_WRITE_BOOL(utrace);
CONFIG_WRITE_BOOL(xmalloc);

View file

@ -489,7 +489,6 @@ TEST_BEGIN(test_mallctl_config) {
TEST_MALLCTL_CONFIG(prof, bool);
TEST_MALLCTL_CONFIG(prof_libgcc, bool);
TEST_MALLCTL_CONFIG(prof_libunwind, bool);
TEST_MALLCTL_CONFIG(prof_frameptr, bool);
TEST_MALLCTL_CONFIG(stats, bool);
TEST_MALLCTL_CONFIG(utrace, bool);
TEST_MALLCTL_CONFIG(xmalloc, bool);