mirror of
https://github.com/jemalloc/jemalloc.git
synced 2026-07-22 12:43:16 +00:00
Compare commits
49 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
83789f4530 | ||
|
|
b5681fb20c | ||
|
|
dd0438ee6b | ||
|
|
d1b6e18a99 | ||
|
|
2625c8968e | ||
|
|
42ba90eb7f | ||
|
|
ba175a2bfb | ||
|
|
ae03bf6a57 | ||
|
|
7329a4f038 | ||
|
|
88393cb0eb | ||
|
|
38067483c5 | ||
|
|
14a2c6a698 | ||
|
|
13e4e24c42 | ||
|
|
72c1e59fd2 | ||
|
|
6e6164ae15 | ||
|
|
5135e34062 | ||
|
|
d0357f7a09 | ||
|
|
1bf2743e08 | ||
|
|
1271185b87 | ||
|
|
6eb84fbe31 | ||
|
|
9906660eb7 | ||
|
|
a3b3386ddd | ||
|
|
87499f6748 | ||
|
|
556ddc7fa9 | ||
|
|
abf6739317 | ||
|
|
ed90c97332 | ||
|
|
847ff223de | ||
|
|
e3d13060c8 | ||
|
|
34457f5144 | ||
|
|
12efefb195 | ||
|
|
3b1f3aca54 | ||
|
|
2b592b0f0b | ||
|
|
a5c80f893e | ||
|
|
609ae595f0 | ||
|
|
d0ffd8ed4f | ||
|
|
2cc11ff837 | ||
|
|
247d124847 | ||
|
|
b5225928fe | ||
|
|
20f1fc95ad | ||
|
|
7de92767c2 | ||
|
|
f4c3f8545b | ||
|
|
1d553f72cb | ||
|
|
35579afb55 | ||
|
|
dd03a2e377 | ||
|
|
5c710cee78 | ||
|
|
f1966e1dc7 | ||
|
|
174b70efb4 | ||
|
|
781fe75e0a | ||
|
|
3860eac170 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -18,7 +18,7 @@
|
||||
/src/*.[od]
|
||||
/test/*.[od]
|
||||
/test/*.out
|
||||
/test/[a-z]*
|
||||
/test/[a-zA-Z_]*
|
||||
!test/*.c
|
||||
!test/*.exp
|
||||
/VERSION
|
||||
|
||||
4
COPYING
4
COPYING
@@ -1,10 +1,10 @@
|
||||
Unless otherwise specified, files in the jemalloc source distribution are
|
||||
subject to the following license:
|
||||
--------------------------------------------------------------------------------
|
||||
Copyright (C) 2002-2012 Jason Evans <jasone@canonware.com>.
|
||||
Copyright (C) 2002-2013 Jason Evans <jasone@canonware.com>.
|
||||
All rights reserved.
|
||||
Copyright (C) 2007-2012 Mozilla Foundation. All rights reserved.
|
||||
Copyright (C) 2009-2012 Facebook, Inc. All rights reserved.
|
||||
Copyright (C) 2009-2013 Facebook, Inc. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
58
ChangeLog
58
ChangeLog
@@ -6,6 +6,64 @@ found in the git revision history:
|
||||
http://www.canonware.com/cgi-bin/gitweb.cgi?p=jemalloc.git
|
||||
git://canonware.com/jemalloc.git
|
||||
|
||||
* 3.3.0 (January 23, 2013)
|
||||
|
||||
This version includes a few minor performance improvements in addition to the
|
||||
listed new features and bug fixes.
|
||||
|
||||
New features:
|
||||
- Add clipping support to lg_chunk option processing.
|
||||
- Add the --enable-ivsalloc option.
|
||||
- Add the --without-export option.
|
||||
- Add the --disable-zone-allocator option.
|
||||
|
||||
Bug fixes:
|
||||
- Fix "arenas.extend" mallctl to output the number of arenas.
|
||||
- Fix chunk_recycyle() to unconditionally inform Valgrind that returned memory
|
||||
is undefined.
|
||||
- Fix build break on FreeBSD related to alloca.h.
|
||||
|
||||
* 3.2.0 (November 9, 2012)
|
||||
|
||||
In addition to a couple of bug fixes, this version modifies page run
|
||||
allocation and dirty page purging algorithms in order to better control
|
||||
page-level virtual memory fragmentation.
|
||||
|
||||
Incompatible changes:
|
||||
- Change the "opt.lg_dirty_mult" default from 5 to 3 (32:1 to 8:1).
|
||||
|
||||
Bug fixes:
|
||||
- Fix dss/mmap allocation precedence code to use recyclable mmap memory only
|
||||
after primary dss allocation fails.
|
||||
- Fix deadlock in the "arenas.purge" mallctl. This regression was introduced
|
||||
in 3.1.0 by the addition of the "arena.<i>.purge" mallctl.
|
||||
|
||||
* 3.1.0 (October 16, 2012)
|
||||
|
||||
New features:
|
||||
- Auto-detect whether running inside Valgrind, thus removing the need to
|
||||
manually specify MALLOC_CONF=valgrind:true.
|
||||
- Add the "arenas.extend" mallctl, which allows applications to create
|
||||
manually managed arenas.
|
||||
- Add the ALLOCM_ARENA() flag for {,r,d}allocm().
|
||||
- Add the "opt.dss", "arena.<i>.dss", and "stats.arenas.<i>.dss" mallctls,
|
||||
which provide control over dss/mmap precedence.
|
||||
- Add the "arena.<i>.purge" mallctl, which obsoletes "arenas.purge".
|
||||
- Define LG_QUANTUM for hppa.
|
||||
|
||||
Incompatible changes:
|
||||
- Disable tcache by default if running inside Valgrind, in order to avoid
|
||||
making unallocated objects appear reachable to Valgrind.
|
||||
- Drop const from malloc_usable_size() argument on Linux.
|
||||
|
||||
Bug fixes:
|
||||
- Fix heap profiling crash if sampled object is freed via realloc(p, 0).
|
||||
- Remove const from __*_hook variable declarations, so that glibc can modify
|
||||
them during process forking.
|
||||
- Fix mlockall(2)/madvise(2) interaction.
|
||||
- Fix fork(2)-related deadlocks.
|
||||
- Fix error return value for "thread.tcache.enabled" mallctl.
|
||||
|
||||
* 3.0.0 (May 11, 2012)
|
||||
|
||||
Although this version adds some major new features, the primary focus is on
|
||||
|
||||
15
INSTALL
15
INSTALL
@@ -55,6 +55,11 @@ any of the following arguments (not a definitive list) to 'configure':
|
||||
jemalloc overlays the default malloc zone, but makes no attempt to actually
|
||||
replace the "malloc", "calloc", etc. symbols.
|
||||
|
||||
--without-export
|
||||
Don't export public APIs. This can be useful when building jemalloc as a
|
||||
static library, or to avoid exporting public APIs when using the zone
|
||||
allocator on OSX.
|
||||
|
||||
--with-private-namespace=<prefix>
|
||||
Prefix all library-private APIs with <prefix>. For shared libraries,
|
||||
symbol visibility mechanisms prevent these symbols from being exported, but
|
||||
@@ -74,6 +79,12 @@ any of the following arguments (not a definitive list) to 'configure':
|
||||
--enable-debug
|
||||
Enable assertions and validation code. This incurs a substantial
|
||||
performance hit, but is very useful during application development.
|
||||
Implies --enable-ivsalloc.
|
||||
|
||||
--enable-ivsalloc
|
||||
Enable validation code, which verifies that pointers reside within
|
||||
jemalloc-owned chunks before dereferencing them. This incurs a substantial
|
||||
performance hit.
|
||||
|
||||
--disable-stats
|
||||
Disable statistics gathering functionality. See the "opt.stats_print"
|
||||
@@ -136,6 +147,10 @@ any of the following arguments (not a definitive list) to 'configure':
|
||||
--disable-experimental
|
||||
Disable support for the experimental API (*allocm()).
|
||||
|
||||
--disable-zone-allocator
|
||||
Disable zone allocator for Darwin. This means jemalloc won't be hooked as
|
||||
the default allocator on OSX/iOS.
|
||||
|
||||
--enable-utrace
|
||||
Enable utrace(2)-based allocation tracing. This feature is not broadly
|
||||
portable (FreeBSD has it, but Linux and OS X do not).
|
||||
|
||||
13
Makefile.in
13
Makefile.in
@@ -48,6 +48,7 @@ cfgoutputs_in := @cfgoutputs_in@
|
||||
cfgoutputs_out := @cfgoutputs_out@
|
||||
enable_autogen := @enable_autogen@
|
||||
enable_experimental := @enable_experimental@
|
||||
enable_zone_allocator := @enable_zone_allocator@
|
||||
DSO_LDFLAGS = @DSO_LDFLAGS@
|
||||
SOREV = @SOREV@
|
||||
PIC_CFLAGS = @PIC_CFLAGS@
|
||||
@@ -80,7 +81,7 @@ CSRCS := $(srcroot)src/jemalloc.c $(srcroot)src/arena.c $(srcroot)src/atomic.c \
|
||||
$(srcroot)src/mutex.c $(srcroot)src/prof.c $(srcroot)src/quarantine.c \
|
||||
$(srcroot)src/rtree.c $(srcroot)src/stats.c $(srcroot)src/tcache.c \
|
||||
$(srcroot)src/util.c $(srcroot)src/tsd.c
|
||||
ifeq (macho, $(ABI))
|
||||
ifeq ($(enable_zone_allocator), 1)
|
||||
CSRCS += $(srcroot)src/zone.c
|
||||
endif
|
||||
ifeq ($(IMPORTLIB),$(SO))
|
||||
@@ -101,9 +102,9 @@ DOCS_HTML := $(DOCS_XML:$(objroot)%.xml=$(srcroot)%.html)
|
||||
DOCS_MAN3 := $(DOCS_XML:$(objroot)%.xml=$(srcroot)%.3)
|
||||
DOCS := $(DOCS_HTML) $(DOCS_MAN3)
|
||||
CTESTS := $(srcroot)test/aligned_alloc.c $(srcroot)test/allocated.c \
|
||||
$(srcroot)test/bitmap.c $(srcroot)test/mremap.c \
|
||||
$(srcroot)test/posix_memalign.c $(srcroot)test/thread_arena.c \
|
||||
$(srcroot)test/thread_tcache_enabled.c
|
||||
$(srcroot)test/ALLOCM_ARENA.c $(srcroot)test/bitmap.c \
|
||||
$(srcroot)test/mremap.c $(srcroot)test/posix_memalign.c \
|
||||
$(srcroot)test/thread_arena.c $(srcroot)test/thread_tcache_enabled.c
|
||||
ifeq ($(enable_experimental), 1)
|
||||
CTESTS += $(srcroot)test/allocm.c $(srcroot)test/rallocm.c
|
||||
endif
|
||||
@@ -112,9 +113,9 @@ COBJS := $(CSRCS:$(srcroot)%.c=$(objroot)%.$(O))
|
||||
CPICOBJS := $(CSRCS:$(srcroot)%.c=$(objroot)%.pic.$(O))
|
||||
CTESTOBJS := $(CTESTS:$(srcroot)%.c=$(objroot)%.$(O))
|
||||
|
||||
.PHONY: all dist doc_html doc_man doc
|
||||
.PHONY: all dist build_doc_html build_doc_man build_doc
|
||||
.PHONY: install_bin install_include install_lib
|
||||
.PHONY: install_html install_man install_doc install
|
||||
.PHONY: install_doc_html install_doc_man install_doc install
|
||||
.PHONY: tests check clean distclean relclean
|
||||
|
||||
.SECONDARY : $(CTESTOBJS)
|
||||
|
||||
65
configure.ac
65
configure.ac
@@ -86,7 +86,7 @@ MANDIR=`eval echo $MANDIR`
|
||||
AC_SUBST([MANDIR])
|
||||
|
||||
dnl Support for building documentation.
|
||||
AC_PATH_PROG([XSLTPROC], [xsltproc], , [$PATH])
|
||||
AC_PATH_PROG([XSLTPROC], [xsltproc], [false], [$PATH])
|
||||
if test -d "/usr/share/xml/docbook/stylesheet/docbook-xsl" ; then
|
||||
DEFAULT_XSLROOT="/usr/share/xml/docbook/stylesheet/docbook-xsl"
|
||||
elif test -d "/usr/share/sgml/docbook/xsl-stylesheets" ; then
|
||||
@@ -237,6 +237,7 @@ dnl Define cpp macros in CPPFLAGS, rather than doing AC_DEFINE(macro), since the
|
||||
dnl definitions need to be seen before any headers are included, which is a pain
|
||||
dnl to make happen otherwise.
|
||||
default_munmap="1"
|
||||
JEMALLOC_USABLE_SIZE_CONST="const"
|
||||
case "${host}" in
|
||||
*-*-darwin*)
|
||||
CFLAGS="$CFLAGS"
|
||||
@@ -260,8 +261,10 @@ case "${host}" in
|
||||
CFLAGS="$CFLAGS"
|
||||
CPPFLAGS="$CPPFLAGS -D_GNU_SOURCE"
|
||||
abi="elf"
|
||||
AC_DEFINE([JEMALLOC_HAS_ALLOCA_H])
|
||||
AC_DEFINE([JEMALLOC_PURGE_MADVISE_DONTNEED], [ ])
|
||||
AC_DEFINE([JEMALLOC_THREADED_INIT], [ ])
|
||||
JEMALLOC_USABLE_SIZE_CONST=""
|
||||
default_munmap="0"
|
||||
;;
|
||||
*-*-netbsd*)
|
||||
@@ -323,6 +326,7 @@ case "${host}" in
|
||||
abi="elf"
|
||||
;;
|
||||
esac
|
||||
AC_DEFINE_UNQUOTED([JEMALLOC_USABLE_SIZE_CONST], [$JEMALLOC_USABLE_SIZE_CONST])
|
||||
AC_SUBST([abi])
|
||||
AC_SUBST([RPATH])
|
||||
AC_SUBST([LD_PRELOAD_VAR])
|
||||
@@ -399,9 +403,9 @@ AC_SUBST([enable_autogen])
|
||||
|
||||
AC_PROG_INSTALL
|
||||
AC_PROG_RANLIB
|
||||
AC_PATH_PROG([AR], [ar], , [$PATH])
|
||||
AC_PATH_PROG([LD], [ld], , [$PATH])
|
||||
AC_PATH_PROG([AUTOCONF], [autoconf], , [$PATH])
|
||||
AC_PATH_PROG([AR], [ar], [false], [$PATH])
|
||||
AC_PATH_PROG([LD], [ld], [false], [$PATH])
|
||||
AC_PATH_PROG([AUTOCONF], [autoconf], [false], [$PATH])
|
||||
|
||||
public_syms="malloc_conf malloc_message malloc calloc posix_memalign aligned_alloc realloc free malloc_usable_size malloc_stats_print mallctl mallctlnametomib mallctlbymib"
|
||||
|
||||
@@ -468,6 +472,13 @@ for stem in ${public_syms}; do
|
||||
AC_DEFINE_UNQUOTED([${n}], [${m}])
|
||||
done
|
||||
|
||||
AC_ARG_WITH([export],
|
||||
[AS_HELP_STRING([--without-export], [disable exporting jemalloc public APIs])],
|
||||
[if test "x$with_export" = "xno"; then
|
||||
AC_DEFINE([JEMALLOC_EXPORT],[])
|
||||
fi]
|
||||
)
|
||||
|
||||
dnl Do not mangle library-private APIs by default.
|
||||
AC_ARG_WITH([private_namespace],
|
||||
[AS_HELP_STRING([--with-private-namespace=<prefix>], [Prefix to prepend to all library-private APIs])],
|
||||
@@ -541,7 +552,7 @@ fi
|
||||
|
||||
dnl Do not compile with debugging by default.
|
||||
AC_ARG_ENABLE([debug],
|
||||
[AS_HELP_STRING([--enable-debug], [Build debugging code])],
|
||||
[AS_HELP_STRING([--enable-debug], [Build debugging code (implies --enable-ivsalloc)])],
|
||||
[if test "x$enable_debug" = "xno" ; then
|
||||
enable_debug="0"
|
||||
else
|
||||
@@ -552,10 +563,25 @@ fi
|
||||
)
|
||||
if test "x$enable_debug" = "x1" ; then
|
||||
AC_DEFINE([JEMALLOC_DEBUG], [ ])
|
||||
AC_DEFINE([JEMALLOC_IVSALLOC], [ ])
|
||||
enable_ivsalloc="1"
|
||||
fi
|
||||
AC_SUBST([enable_debug])
|
||||
|
||||
dnl Do not validate pointers by default.
|
||||
AC_ARG_ENABLE([ivsalloc],
|
||||
[AS_HELP_STRING([--enable-ivsalloc], [Validate pointers passed through the public API])],
|
||||
[if test "x$enable_ivsalloc" = "xno" ; then
|
||||
enable_ivsalloc="0"
|
||||
else
|
||||
enable_ivsalloc="1"
|
||||
fi
|
||||
],
|
||||
[enable_ivsalloc="0"]
|
||||
)
|
||||
if test "x$enable_ivsalloc" = "x1" ; then
|
||||
AC_DEFINE([JEMALLOC_IVSALLOC], [ ])
|
||||
fi
|
||||
|
||||
dnl Only optimize if not debugging.
|
||||
if test "x$enable_debug" = "x0" -a "x$no_CFLAGS" = "xyes" ; then
|
||||
dnl Make sure that an optimization flag was not specified in EXTRA_CFLAGS.
|
||||
@@ -903,7 +929,7 @@ AC_CACHE_CHECK([STATIC_PAGE_SHIFT],
|
||||
#include <stdio.h>
|
||||
]],
|
||||
[[
|
||||
long result;
|
||||
int result;
|
||||
FILE *f;
|
||||
|
||||
#ifdef _WIN32
|
||||
@@ -922,7 +948,7 @@ AC_CACHE_CHECK([STATIC_PAGE_SHIFT],
|
||||
if (f == NULL) {
|
||||
return 1;
|
||||
}
|
||||
fprintf(f, "%u\n", result);
|
||||
fprintf(f, "%d\n", result);
|
||||
fclose(f);
|
||||
|
||||
return 0;
|
||||
@@ -1067,11 +1093,13 @@ dnl Check for ffsl(3), and fail if not found. This function exists on all
|
||||
dnl platforms that jemalloc currently has a chance of functioning on without
|
||||
dnl modification.
|
||||
JE_COMPILABLE([a program using ffsl], [
|
||||
#include <stdio.h>
|
||||
#include <strings.h>
|
||||
#include <string.h>
|
||||
], [
|
||||
{
|
||||
int rv = ffsl(0x08);
|
||||
printf("%d\n", rv);
|
||||
}
|
||||
], [je_cv_function_ffsl])
|
||||
if test "x${je_cv_function_ffsl}" != "xyes" ; then
|
||||
@@ -1175,7 +1203,26 @@ fi
|
||||
dnl ============================================================================
|
||||
dnl Darwin-related configuration.
|
||||
|
||||
if test "x${abi}" = "xmacho" ; then
|
||||
AC_ARG_ENABLE([zone-allocator],
|
||||
[AS_HELP_STRING([--disable-zone-allocator],
|
||||
[Disable zone allocator for Darwin])],
|
||||
[if test "x$enable_zone_allocator" = "xno" ; then
|
||||
enable_zone_allocator="0"
|
||||
else
|
||||
enable_zone_allocator="1"
|
||||
fi
|
||||
],
|
||||
[if test "x${abi}" = "xmacho"; then
|
||||
enable_zone_allocator="1"
|
||||
fi
|
||||
]
|
||||
)
|
||||
AC_SUBST([enable_zone_allocator])
|
||||
|
||||
if test "x${enable_zone_allocator}" = "x1" ; then
|
||||
if test "x${abi}" != "xmacho"; then
|
||||
AC_MSG_ERROR([--enable-zone-allocator is only supported on Darwin])
|
||||
fi
|
||||
AC_DEFINE([JEMALLOC_IVSALLOC], [ ])
|
||||
AC_DEFINE([JEMALLOC_ZONE], [ ])
|
||||
|
||||
|
||||
@@ -368,6 +368,15 @@ for (i = 0; i < nbins; i++) {
|
||||
object. This constraint can apply to both growth and
|
||||
shrinkage.</para></listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><constant>ALLOCM_ARENA(<parameter>a</parameter>)
|
||||
</constant></term>
|
||||
|
||||
<listitem><para>Use the arena specified by the index
|
||||
<parameter>a</parameter>. This macro does not validate that
|
||||
<parameter>a</parameter> specifies an arena in the valid
|
||||
range.</para></listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
</para>
|
||||
|
||||
@@ -781,8 +790,25 @@ for (i = 0; i < nbins; i++) {
|
||||
(<type>size_t</type>)
|
||||
<literal>r-</literal>
|
||||
</term>
|
||||
<listitem><para>Virtual memory chunk size (log base 2). The default
|
||||
chunk size is 4 MiB (2^22).</para></listitem>
|
||||
<listitem><para>Virtual memory chunk size (log base 2). If a chunk
|
||||
size outside the supported size range is specified, the size is
|
||||
silently clipped to the minimum/maximum supported size. The default
|
||||
chunk size is 4 MiB (2^22).
|
||||
</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="opt.dss">
|
||||
<term>
|
||||
<mallctl>opt.dss</mallctl>
|
||||
(<type>const char *</type>)
|
||||
<literal>r-</literal>
|
||||
</term>
|
||||
<listitem><para>dss (<citerefentry><refentrytitle>sbrk</refentrytitle>
|
||||
<manvolnum>2</manvolnum></citerefentry>) allocation precedence as
|
||||
related to <citerefentry><refentrytitle>mmap</refentrytitle>
|
||||
<manvolnum>2</manvolnum></citerefentry> allocation. The following
|
||||
settings are supported: “disabled”, “primary”,
|
||||
and “secondary” (default).</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="opt.narenas">
|
||||
@@ -791,9 +817,9 @@ for (i = 0; i < nbins; i++) {
|
||||
(<type>size_t</type>)
|
||||
<literal>r-</literal>
|
||||
</term>
|
||||
<listitem><para>Maximum number of arenas to use. The default maximum
|
||||
number of arenas is four times the number of CPUs, or one if there is a
|
||||
single CPU.</para></listitem>
|
||||
<listitem><para>Maximum number of arenas to use for automatic
|
||||
multiplexing of threads and arenas. The default is four times the
|
||||
number of CPUs, or one if there is a single CPU.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="opt.lg_dirty_mult">
|
||||
@@ -810,7 +836,7 @@ for (i = 0; i < nbins; i++) {
|
||||
<manvolnum>2</manvolnum></citerefentry> or a similar system call. This
|
||||
provides the kernel with sufficient information to recycle dirty pages
|
||||
if physical memory becomes scarce and the pages remain unused. The
|
||||
default minimum ratio is 32:1 (2^5:1); an option value of -1 will
|
||||
default minimum ratio is 8:1 (2^3:1); an option value of -1 will
|
||||
disable dirty page purging.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
@@ -846,7 +872,9 @@ for (i = 0; i < nbins; i++) {
|
||||
<literal>0x5a</literal>. This is intended for debugging and will
|
||||
impact performance negatively. This option is disabled by default
|
||||
unless <option>--enable-debug</option> is specified during
|
||||
configuration, in which case it is enabled by default.</para></listitem>
|
||||
configuration, in which case it is enabled by default unless running
|
||||
inside <ulink
|
||||
url="http://valgrind.org/">Valgrind</ulink>.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="opt.quarantine">
|
||||
@@ -865,8 +893,9 @@ for (i = 0; i < nbins; i++) {
|
||||
enabled. This feature is of particular use in combination with <ulink
|
||||
url="http://valgrind.org/">Valgrind</ulink>, which can detect attempts
|
||||
to access quarantined objects. This is intended for debugging and will
|
||||
impact performance negatively. The default quarantine size is
|
||||
0.</para></listitem>
|
||||
impact performance negatively. The default quarantine size is 0 unless
|
||||
running inside Valgrind, in which case the default is 16
|
||||
MiB.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="opt.redzone">
|
||||
@@ -885,7 +914,7 @@ for (i = 0; i < nbins; i++) {
|
||||
which needs redzones in order to do effective buffer overflow/underflow
|
||||
detection. This option is intended for debugging and will impact
|
||||
performance negatively. This option is disabled by
|
||||
default.</para></listitem>
|
||||
default unless running inside Valgrind.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="opt.zero">
|
||||
@@ -926,15 +955,9 @@ for (i = 0; i < nbins; i++) {
|
||||
[<option>--enable-valgrind</option>]
|
||||
</term>
|
||||
<listitem><para><ulink url="http://valgrind.org/">Valgrind</ulink>
|
||||
support enabled/disabled. If enabled, several other options are
|
||||
automatically modified during options processing to work well with
|
||||
Valgrind: <link linkend="opt.junk"><mallctl>opt.junk</mallctl></link>
|
||||
and <link linkend="opt.zero"><mallctl>opt.zero</mallctl></link> are set
|
||||
to false, <link
|
||||
linkend="opt.quarantine"><mallctl>opt.quarantine</mallctl></link> is
|
||||
set to 16 MiB, and <link
|
||||
linkend="opt.redzone"><mallctl>opt.redzone</mallctl></link> is set to
|
||||
true. This option is disabled by default.</para></listitem>
|
||||
support enabled/disabled. This option is vestigal because jemalloc
|
||||
auto-detects whether it is running inside Valgrind. This option is
|
||||
disabled by default, unless running inside Valgrind.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="opt.xmalloc">
|
||||
@@ -972,7 +995,8 @@ malloc_conf = "xmalloc:true";]]></programlisting>
|
||||
<link
|
||||
linkend="opt.lg_tcache_max"><mallctl>opt.lg_tcache_max</mallctl></link>
|
||||
option for related tuning information. This option is enabled by
|
||||
default.</para></listitem>
|
||||
default unless running inside <ulink
|
||||
url="http://valgrind.org/">Valgrind</ulink>.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="opt.lg_tcache_max">
|
||||
@@ -1151,11 +1175,8 @@ malloc_conf = "xmalloc:true";]]></programlisting>
|
||||
<literal>rw</literal>
|
||||
</term>
|
||||
<listitem><para>Get or set the arena associated with the calling
|
||||
thread. The arena index must be less than the maximum number of arenas
|
||||
(see the <link
|
||||
linkend="arenas.narenas"><mallctl>arenas.narenas</mallctl></link>
|
||||
mallctl). If the specified arena was not initialized beforehand (see
|
||||
the <link
|
||||
thread. If the specified arena was not initialized beforehand (see the
|
||||
<link
|
||||
linkend="arenas.initialized"><mallctl>arenas.initialized</mallctl></link>
|
||||
mallctl), it will be automatically initialized as a side effect of
|
||||
calling this interface.</para></listitem>
|
||||
@@ -1247,13 +1268,40 @@ malloc_conf = "xmalloc:true";]]></programlisting>
|
||||
the developer may find manual flushing useful.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="arena.i.purge">
|
||||
<term>
|
||||
<mallctl>arena.<i>.purge</mallctl>
|
||||
(<type>unsigned</type>)
|
||||
<literal>--</literal>
|
||||
</term>
|
||||
<listitem><para>Purge unused dirty pages for arena <i>, or for
|
||||
all arenas if <i> equals <link
|
||||
linkend="arenas.narenas"><mallctl>arenas.narenas</mallctl></link>.
|
||||
</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="arena.i.dss">
|
||||
<term>
|
||||
<mallctl>arena.<i>.dss</mallctl>
|
||||
(<type>const char *</type>)
|
||||
<literal>rw</literal>
|
||||
</term>
|
||||
<listitem><para>Set the precedence of dss allocation as related to mmap
|
||||
allocation for arena <i>, or for all arenas if <i> equals
|
||||
<link
|
||||
linkend="arenas.narenas"><mallctl>arenas.narenas</mallctl></link>. See
|
||||
<link linkend="opt.dss"><mallctl>opt.dss</mallctl></link> for supported
|
||||
settings.
|
||||
</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="arenas.narenas">
|
||||
<term>
|
||||
<mallctl>arenas.narenas</mallctl>
|
||||
(<type>unsigned</type>)
|
||||
<literal>r-</literal>
|
||||
</term>
|
||||
<listitem><para>Maximum number of arenas.</para></listitem>
|
||||
<listitem><para>Current limit on number of arenas.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="arenas.initialized">
|
||||
@@ -1372,6 +1420,16 @@ malloc_conf = "xmalloc:true";]]></programlisting>
|
||||
for all arenas if none is specified.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term>
|
||||
<mallctl>arenas.extend</mallctl>
|
||||
(<type>unsigned</type>)
|
||||
<literal>r-</literal>
|
||||
</term>
|
||||
<listitem><para>Extend the array of arenas by appending a new arena,
|
||||
and returning the new arena index.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="prof.active">
|
||||
<term>
|
||||
<mallctl>prof.active</mallctl>
|
||||
@@ -1457,7 +1515,9 @@ malloc_conf = "xmalloc:true";]]></programlisting>
|
||||
application. This is a multiple of the page size, and greater than or
|
||||
equal to <link
|
||||
linkend="stats.allocated"><mallctl>stats.allocated</mallctl></link>.
|
||||
</para></listitem>
|
||||
This does not include <link linkend="stats.arenas.i.pdirty">
|
||||
<mallctl>stats.arenas.<i>.pdirty</mallctl></link> and pages
|
||||
entirely devoted to allocator metadata.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
@@ -1540,6 +1600,20 @@ malloc_conf = "xmalloc:true";]]></programlisting>
|
||||
</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term>
|
||||
<mallctl>stats.arenas.<i>.dss</mallctl>
|
||||
(<type>const char *</type>)
|
||||
<literal>r-</literal>
|
||||
</term>
|
||||
<listitem><para>dss (<citerefentry><refentrytitle>sbrk</refentrytitle>
|
||||
<manvolnum>2</manvolnum></citerefentry>) allocation precedence as
|
||||
related to <citerefentry><refentrytitle>mmap</refentrytitle>
|
||||
<manvolnum>2</manvolnum></citerefentry> allocation. See <link
|
||||
linkend="opt.dss"><mallctl>opt.dss</mallctl></link> for details.
|
||||
</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term>
|
||||
<mallctl>stats.arenas.<i>.nthreads</mallctl>
|
||||
@@ -1559,7 +1633,7 @@ malloc_conf = "xmalloc:true";]]></programlisting>
|
||||
<listitem><para>Number of pages in active runs.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<varlistentry id="stats.arenas.i.pdirty">
|
||||
<term>
|
||||
<mallctl>stats.arenas.<i>.pdirty</mallctl>
|
||||
(<type>size_t</type>)
|
||||
@@ -1865,9 +1939,7 @@ malloc_conf = "xmalloc:true";]]></programlisting>
|
||||
it detects, because the performance impact for storing such information
|
||||
would be prohibitive. However, jemalloc does integrate with the most
|
||||
excellent <ulink url="http://valgrind.org/">Valgrind</ulink> tool if the
|
||||
<option>--enable-valgrind</option> configuration option is enabled and the
|
||||
<link linkend="opt.valgrind"><mallctl>opt.valgrind</mallctl></link> option
|
||||
is enabled.</para>
|
||||
<option>--enable-valgrind</option> configuration option is enabled.</para>
|
||||
</refsect1>
|
||||
<refsect1 id="diagnostic_messages">
|
||||
<title>DIAGNOSTIC MESSAGES</title>
|
||||
|
||||
@@ -38,10 +38,10 @@
|
||||
*
|
||||
* (nactive >> opt_lg_dirty_mult) >= ndirty
|
||||
*
|
||||
* So, supposing that opt_lg_dirty_mult is 5, there can be no less than 32
|
||||
* times as many active pages as dirty pages.
|
||||
* So, supposing that opt_lg_dirty_mult is 3, there can be no less than 8 times
|
||||
* as many active pages as dirty pages.
|
||||
*/
|
||||
#define LG_DIRTY_MULT_DEFAULT 5
|
||||
#define LG_DIRTY_MULT_DEFAULT 3
|
||||
|
||||
typedef struct arena_chunk_map_s arena_chunk_map_t;
|
||||
typedef struct arena_chunk_s arena_chunk_t;
|
||||
@@ -69,7 +69,7 @@ struct arena_chunk_map_s {
|
||||
/*
|
||||
* Linkage for run trees. There are two disjoint uses:
|
||||
*
|
||||
* 1) arena_t's runs_avail_{clean,dirty} trees.
|
||||
* 1) arena_t's runs_avail tree.
|
||||
* 2) arena_run_t conceptually uses this linkage for in-use
|
||||
* non-full runs, rather than directly embedding linkage.
|
||||
*/
|
||||
@@ -162,20 +162,24 @@ typedef rb_tree(arena_chunk_map_t) arena_run_tree_t;
|
||||
/* Arena chunk header. */
|
||||
struct arena_chunk_s {
|
||||
/* Arena that owns the chunk. */
|
||||
arena_t *arena;
|
||||
arena_t *arena;
|
||||
|
||||
/* Linkage for the arena's chunks_dirty list. */
|
||||
ql_elm(arena_chunk_t) link_dirty;
|
||||
|
||||
/*
|
||||
* True if the chunk is currently in the chunks_dirty list, due to
|
||||
* having at some point contained one or more dirty pages. Removal
|
||||
* from chunks_dirty is lazy, so (dirtied && ndirty == 0) is possible.
|
||||
*/
|
||||
bool dirtied;
|
||||
/* Linkage for tree of arena chunks that contain dirty runs. */
|
||||
rb_node(arena_chunk_t) dirty_link;
|
||||
|
||||
/* Number of dirty pages. */
|
||||
size_t ndirty;
|
||||
size_t ndirty;
|
||||
|
||||
/* Number of available runs. */
|
||||
size_t nruns_avail;
|
||||
|
||||
/*
|
||||
* Number of available run adjacencies. Clean and dirty available runs
|
||||
* are not coalesced, which causes virtual memory fragmentation. The
|
||||
* ratio of (nruns_avail-nruns_adjac):nruns_adjac is used for tracking
|
||||
* this fragmentation.
|
||||
* */
|
||||
size_t nruns_adjac;
|
||||
|
||||
/*
|
||||
* Map of pages within chunk that keeps track of free/large/small. The
|
||||
@@ -183,7 +187,7 @@ struct arena_chunk_s {
|
||||
* need to be tracked in the map. This omission saves a header page
|
||||
* for common chunk sizes (e.g. 4 MiB).
|
||||
*/
|
||||
arena_chunk_map_t map[1]; /* Dynamically sized. */
|
||||
arena_chunk_map_t map[1]; /* Dynamically sized. */
|
||||
};
|
||||
typedef rb_tree(arena_chunk_t) arena_chunk_tree_t;
|
||||
|
||||
@@ -331,8 +335,10 @@ struct arena_s {
|
||||
|
||||
uint64_t prof_accumbytes;
|
||||
|
||||
/* List of dirty-page-containing chunks this arena manages. */
|
||||
ql_head(arena_chunk_t) chunks_dirty;
|
||||
dss_prec_t dss_prec;
|
||||
|
||||
/* Tree of dirty-page-containing chunks this arena manages. */
|
||||
arena_chunk_tree_t chunks_dirty;
|
||||
|
||||
/*
|
||||
* In order to avoid rapid chunk allocation/deallocation when an arena
|
||||
@@ -367,18 +373,9 @@ struct arena_s {
|
||||
|
||||
/*
|
||||
* Size/address-ordered trees of this arena's available runs. The trees
|
||||
* are used for first-best-fit run allocation. The dirty tree contains
|
||||
* runs with dirty pages (i.e. very likely to have been touched and
|
||||
* therefore have associated physical pages), whereas the clean tree
|
||||
* contains runs with pages that either have no associated physical
|
||||
* pages, or have pages that the kernel may recycle at any time due to
|
||||
* previous madvise(2) calls. The dirty tree is used in preference to
|
||||
* the clean tree for allocations, because using dirty pages reduces
|
||||
* the amount of dirty purging necessary to keep the active:dirty page
|
||||
* ratio below the purge threshold.
|
||||
* are used for first-best-fit run allocation.
|
||||
*/
|
||||
arena_avail_tree_t runs_avail_clean;
|
||||
arena_avail_tree_t runs_avail_dirty;
|
||||
arena_avail_tree_t runs_avail;
|
||||
|
||||
/* bins is used to store trees of free regions. */
|
||||
arena_bin_t bins[NBINS];
|
||||
@@ -403,7 +400,6 @@ extern arena_bin_info_t arena_bin_info[NBINS];
|
||||
#define nlclasses (chunk_npages - map_bias)
|
||||
|
||||
void arena_purge_all(arena_t *arena);
|
||||
void arena_prof_accum(arena_t *arena, uint64_t accumbytes);
|
||||
void arena_tcache_fill_small(arena_t *arena, tcache_bin_t *tbin,
|
||||
size_t binind, uint64_t prof_accumbytes);
|
||||
void arena_alloc_junk_small(void *ptr, arena_bin_info_t *bin_info,
|
||||
@@ -422,13 +418,16 @@ void arena_dalloc_small(arena_t *arena, arena_chunk_t *chunk, void *ptr,
|
||||
void arena_dalloc_large_locked(arena_t *arena, arena_chunk_t *chunk,
|
||||
void *ptr);
|
||||
void arena_dalloc_large(arena_t *arena, arena_chunk_t *chunk, void *ptr);
|
||||
void arena_stats_merge(arena_t *arena, size_t *nactive, size_t *ndirty,
|
||||
arena_stats_t *astats, malloc_bin_stats_t *bstats,
|
||||
malloc_large_stats_t *lstats);
|
||||
void *arena_ralloc_no_move(void *ptr, size_t oldsize, size_t size,
|
||||
size_t extra, bool zero);
|
||||
void *arena_ralloc(void *ptr, size_t oldsize, size_t size, size_t extra,
|
||||
size_t alignment, bool zero, bool try_tcache);
|
||||
void *arena_ralloc(arena_t *arena, void *ptr, size_t oldsize, size_t size,
|
||||
size_t extra, size_t alignment, bool zero, bool try_tcache_alloc,
|
||||
bool try_tcache_dalloc);
|
||||
dss_prec_t arena_dss_prec_get(arena_t *arena);
|
||||
void arena_dss_prec_set(arena_t *arena, dss_prec_t dss_prec);
|
||||
void arena_stats_merge(arena_t *arena, const char **dss, size_t *nactive,
|
||||
size_t *ndirty, arena_stats_t *astats, malloc_bin_stats_t *bstats,
|
||||
malloc_large_stats_t *lstats);
|
||||
bool arena_new(arena_t *arena, unsigned ind);
|
||||
void arena_boot(void);
|
||||
void arena_prefork(arena_t *arena);
|
||||
@@ -464,6 +463,9 @@ void arena_mapbits_small_set(arena_chunk_t *chunk, size_t pageind,
|
||||
size_t runind, size_t binind, size_t flags);
|
||||
void arena_mapbits_unzeroed_set(arena_chunk_t *chunk, size_t pageind,
|
||||
size_t unzeroed);
|
||||
void arena_prof_accum_impl(arena_t *arena, uint64_t accumbytes);
|
||||
void arena_prof_accum_locked(arena_t *arena, uint64_t accumbytes);
|
||||
void arena_prof_accum(arena_t *arena, uint64_t accumbytes);
|
||||
size_t arena_ptr_small_binind_get(const void *ptr, size_t mapbits);
|
||||
size_t arena_bin_index(arena_t *arena, arena_bin_t *bin);
|
||||
unsigned arena_run_regind(arena_run_t *run, arena_bin_info_t *bin_info,
|
||||
@@ -478,7 +480,7 @@ void arena_dalloc(arena_t *arena, arena_chunk_t *chunk, void *ptr,
|
||||
|
||||
#if (defined(JEMALLOC_ENABLE_INLINE) || defined(JEMALLOC_ARENA_C_))
|
||||
# ifdef JEMALLOC_ARENA_INLINE_A
|
||||
JEMALLOC_INLINE arena_chunk_map_t *
|
||||
JEMALLOC_ALWAYS_INLINE arena_chunk_map_t *
|
||||
arena_mapp_get(arena_chunk_t *chunk, size_t pageind)
|
||||
{
|
||||
|
||||
@@ -488,21 +490,21 @@ arena_mapp_get(arena_chunk_t *chunk, size_t pageind)
|
||||
return (&chunk->map[pageind-map_bias]);
|
||||
}
|
||||
|
||||
JEMALLOC_INLINE size_t *
|
||||
JEMALLOC_ALWAYS_INLINE size_t *
|
||||
arena_mapbitsp_get(arena_chunk_t *chunk, size_t pageind)
|
||||
{
|
||||
|
||||
return (&arena_mapp_get(chunk, pageind)->bits);
|
||||
}
|
||||
|
||||
JEMALLOC_INLINE size_t
|
||||
JEMALLOC_ALWAYS_INLINE size_t
|
||||
arena_mapbits_get(arena_chunk_t *chunk, size_t pageind)
|
||||
{
|
||||
|
||||
return (*arena_mapbitsp_get(chunk, pageind));
|
||||
}
|
||||
|
||||
JEMALLOC_INLINE size_t
|
||||
JEMALLOC_ALWAYS_INLINE size_t
|
||||
arena_mapbits_unallocated_size_get(arena_chunk_t *chunk, size_t pageind)
|
||||
{
|
||||
size_t mapbits;
|
||||
@@ -512,7 +514,7 @@ arena_mapbits_unallocated_size_get(arena_chunk_t *chunk, size_t pageind)
|
||||
return (mapbits & ~PAGE_MASK);
|
||||
}
|
||||
|
||||
JEMALLOC_INLINE size_t
|
||||
JEMALLOC_ALWAYS_INLINE size_t
|
||||
arena_mapbits_large_size_get(arena_chunk_t *chunk, size_t pageind)
|
||||
{
|
||||
size_t mapbits;
|
||||
@@ -523,7 +525,7 @@ arena_mapbits_large_size_get(arena_chunk_t *chunk, size_t pageind)
|
||||
return (mapbits & ~PAGE_MASK);
|
||||
}
|
||||
|
||||
JEMALLOC_INLINE size_t
|
||||
JEMALLOC_ALWAYS_INLINE size_t
|
||||
arena_mapbits_small_runind_get(arena_chunk_t *chunk, size_t pageind)
|
||||
{
|
||||
size_t mapbits;
|
||||
@@ -534,7 +536,7 @@ arena_mapbits_small_runind_get(arena_chunk_t *chunk, size_t pageind)
|
||||
return (mapbits >> LG_PAGE);
|
||||
}
|
||||
|
||||
JEMALLOC_INLINE size_t
|
||||
JEMALLOC_ALWAYS_INLINE size_t
|
||||
arena_mapbits_binind_get(arena_chunk_t *chunk, size_t pageind)
|
||||
{
|
||||
size_t mapbits;
|
||||
@@ -546,7 +548,7 @@ arena_mapbits_binind_get(arena_chunk_t *chunk, size_t pageind)
|
||||
return (binind);
|
||||
}
|
||||
|
||||
JEMALLOC_INLINE size_t
|
||||
JEMALLOC_ALWAYS_INLINE size_t
|
||||
arena_mapbits_dirty_get(arena_chunk_t *chunk, size_t pageind)
|
||||
{
|
||||
size_t mapbits;
|
||||
@@ -555,7 +557,7 @@ arena_mapbits_dirty_get(arena_chunk_t *chunk, size_t pageind)
|
||||
return (mapbits & CHUNK_MAP_DIRTY);
|
||||
}
|
||||
|
||||
JEMALLOC_INLINE size_t
|
||||
JEMALLOC_ALWAYS_INLINE size_t
|
||||
arena_mapbits_unzeroed_get(arena_chunk_t *chunk, size_t pageind)
|
||||
{
|
||||
size_t mapbits;
|
||||
@@ -564,7 +566,7 @@ arena_mapbits_unzeroed_get(arena_chunk_t *chunk, size_t pageind)
|
||||
return (mapbits & CHUNK_MAP_UNZEROED);
|
||||
}
|
||||
|
||||
JEMALLOC_INLINE size_t
|
||||
JEMALLOC_ALWAYS_INLINE size_t
|
||||
arena_mapbits_large_get(arena_chunk_t *chunk, size_t pageind)
|
||||
{
|
||||
size_t mapbits;
|
||||
@@ -573,7 +575,7 @@ arena_mapbits_large_get(arena_chunk_t *chunk, size_t pageind)
|
||||
return (mapbits & CHUNK_MAP_LARGE);
|
||||
}
|
||||
|
||||
JEMALLOC_INLINE size_t
|
||||
JEMALLOC_ALWAYS_INLINE size_t
|
||||
arena_mapbits_allocated_get(arena_chunk_t *chunk, size_t pageind)
|
||||
{
|
||||
size_t mapbits;
|
||||
@@ -582,7 +584,7 @@ arena_mapbits_allocated_get(arena_chunk_t *chunk, size_t pageind)
|
||||
return (mapbits & CHUNK_MAP_ALLOCATED);
|
||||
}
|
||||
|
||||
JEMALLOC_INLINE void
|
||||
JEMALLOC_ALWAYS_INLINE void
|
||||
arena_mapbits_unallocated_set(arena_chunk_t *chunk, size_t pageind, size_t size,
|
||||
size_t flags)
|
||||
{
|
||||
@@ -595,7 +597,7 @@ arena_mapbits_unallocated_set(arena_chunk_t *chunk, size_t pageind, size_t size,
|
||||
*mapbitsp = size | CHUNK_MAP_BININD_INVALID | flags;
|
||||
}
|
||||
|
||||
JEMALLOC_INLINE void
|
||||
JEMALLOC_ALWAYS_INLINE void
|
||||
arena_mapbits_unallocated_size_set(arena_chunk_t *chunk, size_t pageind,
|
||||
size_t size)
|
||||
{
|
||||
@@ -607,7 +609,7 @@ arena_mapbits_unallocated_size_set(arena_chunk_t *chunk, size_t pageind,
|
||||
*mapbitsp = size | (*mapbitsp & PAGE_MASK);
|
||||
}
|
||||
|
||||
JEMALLOC_INLINE void
|
||||
JEMALLOC_ALWAYS_INLINE void
|
||||
arena_mapbits_large_set(arena_chunk_t *chunk, size_t pageind, size_t size,
|
||||
size_t flags)
|
||||
{
|
||||
@@ -622,7 +624,7 @@ arena_mapbits_large_set(arena_chunk_t *chunk, size_t pageind, size_t size,
|
||||
CHUNK_MAP_LARGE | CHUNK_MAP_ALLOCATED;
|
||||
}
|
||||
|
||||
JEMALLOC_INLINE void
|
||||
JEMALLOC_ALWAYS_INLINE void
|
||||
arena_mapbits_large_binind_set(arena_chunk_t *chunk, size_t pageind,
|
||||
size_t binind)
|
||||
{
|
||||
@@ -635,7 +637,7 @@ arena_mapbits_large_binind_set(arena_chunk_t *chunk, size_t pageind,
|
||||
CHUNK_MAP_BININD_SHIFT);
|
||||
}
|
||||
|
||||
JEMALLOC_INLINE void
|
||||
JEMALLOC_ALWAYS_INLINE void
|
||||
arena_mapbits_small_set(arena_chunk_t *chunk, size_t pageind, size_t runind,
|
||||
size_t binind, size_t flags)
|
||||
{
|
||||
@@ -651,7 +653,7 @@ arena_mapbits_small_set(arena_chunk_t *chunk, size_t pageind, size_t runind,
|
||||
flags | unzeroed | CHUNK_MAP_ALLOCATED;
|
||||
}
|
||||
|
||||
JEMALLOC_INLINE void
|
||||
JEMALLOC_ALWAYS_INLINE void
|
||||
arena_mapbits_unzeroed_set(arena_chunk_t *chunk, size_t pageind,
|
||||
size_t unzeroed)
|
||||
{
|
||||
@@ -661,7 +663,45 @@ arena_mapbits_unzeroed_set(arena_chunk_t *chunk, size_t pageind,
|
||||
*mapbitsp = (*mapbitsp & ~CHUNK_MAP_UNZEROED) | unzeroed;
|
||||
}
|
||||
|
||||
JEMALLOC_INLINE size_t
|
||||
JEMALLOC_INLINE void
|
||||
arena_prof_accum_impl(arena_t *arena, uint64_t accumbytes)
|
||||
{
|
||||
|
||||
cassert(config_prof);
|
||||
assert(prof_interval != 0);
|
||||
|
||||
arena->prof_accumbytes += accumbytes;
|
||||
if (arena->prof_accumbytes >= prof_interval) {
|
||||
prof_idump();
|
||||
arena->prof_accumbytes -= prof_interval;
|
||||
}
|
||||
}
|
||||
|
||||
JEMALLOC_INLINE void
|
||||
arena_prof_accum_locked(arena_t *arena, uint64_t accumbytes)
|
||||
{
|
||||
|
||||
cassert(config_prof);
|
||||
|
||||
if (prof_interval == 0)
|
||||
return;
|
||||
arena_prof_accum_impl(arena, accumbytes);
|
||||
}
|
||||
|
||||
JEMALLOC_INLINE void
|
||||
arena_prof_accum(arena_t *arena, uint64_t accumbytes)
|
||||
{
|
||||
|
||||
cassert(config_prof);
|
||||
|
||||
if (prof_interval == 0)
|
||||
return;
|
||||
malloc_mutex_lock(&arena->lock);
|
||||
arena_prof_accum_impl(arena, accumbytes);
|
||||
malloc_mutex_unlock(&arena->lock);
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE size_t
|
||||
arena_ptr_small_binind_get(const void *ptr, size_t mapbits)
|
||||
{
|
||||
size_t binind;
|
||||
@@ -856,7 +896,7 @@ arena_prof_ctx_set(const void *ptr, prof_ctx_t *ctx)
|
||||
arena_mapp_get(chunk, pageind)->prof_ctx = ctx;
|
||||
}
|
||||
|
||||
JEMALLOC_INLINE void *
|
||||
JEMALLOC_ALWAYS_INLINE void *
|
||||
arena_malloc(arena_t *arena, size_t size, bool zero, bool try_tcache)
|
||||
{
|
||||
tcache_t *tcache;
|
||||
@@ -887,7 +927,7 @@ arena_malloc(arena_t *arena, size_t size, bool zero, bool try_tcache)
|
||||
}
|
||||
|
||||
/* Return the size of the allocation pointed to by ptr. */
|
||||
JEMALLOC_INLINE size_t
|
||||
JEMALLOC_ALWAYS_INLINE size_t
|
||||
arena_salloc(const void *ptr, bool demote)
|
||||
{
|
||||
size_t ret;
|
||||
@@ -933,7 +973,7 @@ arena_salloc(const void *ptr, bool demote)
|
||||
return (ret);
|
||||
}
|
||||
|
||||
JEMALLOC_INLINE void
|
||||
JEMALLOC_ALWAYS_INLINE void
|
||||
arena_dalloc(arena_t *arena, arena_chunk_t *chunk, void *ptr, bool try_tcache)
|
||||
{
|
||||
size_t pageind, mapbits;
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#ifdef JEMALLOC_H_EXTERNS
|
||||
|
||||
extern size_t opt_lg_chunk;
|
||||
extern const char *opt_dss;
|
||||
|
||||
/* Protects stats_chunks; currently not used for any other purpose. */
|
||||
extern malloc_mutex_t chunks_mtx;
|
||||
@@ -42,9 +43,14 @@ extern size_t chunk_npages;
|
||||
extern size_t map_bias; /* Number of arena chunk header pages. */
|
||||
extern size_t arena_maxclass; /* Max size class for arenas. */
|
||||
|
||||
void *chunk_alloc(size_t size, size_t alignment, bool base, bool *zero);
|
||||
void *chunk_alloc(size_t size, size_t alignment, bool base, bool *zero,
|
||||
dss_prec_t dss_prec);
|
||||
void chunk_unmap(void *chunk, size_t size);
|
||||
void chunk_dealloc(void *chunk, size_t size, bool unmap);
|
||||
bool chunk_boot(void);
|
||||
void chunk_prefork(void);
|
||||
void chunk_postfork_parent(void);
|
||||
void chunk_postfork_child(void);
|
||||
|
||||
#endif /* JEMALLOC_H_EXTERNS */
|
||||
/******************************************************************************/
|
||||
|
||||
@@ -1,14 +1,28 @@
|
||||
/******************************************************************************/
|
||||
#ifdef JEMALLOC_H_TYPES
|
||||
|
||||
typedef enum {
|
||||
dss_prec_disabled = 0,
|
||||
dss_prec_primary = 1,
|
||||
dss_prec_secondary = 2,
|
||||
|
||||
dss_prec_limit = 3
|
||||
} dss_prec_t ;
|
||||
#define DSS_PREC_DEFAULT dss_prec_secondary
|
||||
#define DSS_DEFAULT "secondary"
|
||||
|
||||
#endif /* JEMALLOC_H_TYPES */
|
||||
/******************************************************************************/
|
||||
#ifdef JEMALLOC_H_STRUCTS
|
||||
|
||||
extern const char *dss_prec_names[];
|
||||
|
||||
#endif /* JEMALLOC_H_STRUCTS */
|
||||
/******************************************************************************/
|
||||
#ifdef JEMALLOC_H_EXTERNS
|
||||
|
||||
dss_prec_t chunk_dss_prec_get(void);
|
||||
bool chunk_dss_prec_set(dss_prec_t dss_prec);
|
||||
void *chunk_alloc_dss(size_t size, size_t alignment, bool *zero);
|
||||
bool chunk_in_dss(void *chunk);
|
||||
bool chunk_dss_boot(void);
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
/******************************************************************************/
|
||||
#ifdef JEMALLOC_H_EXTERNS
|
||||
|
||||
void pages_purge(void *addr, size_t length);
|
||||
bool pages_purge(void *addr, size_t length);
|
||||
|
||||
void *chunk_alloc_mmap(size_t size, size_t alignment, bool *zero);
|
||||
bool chunk_dealloc_mmap(void *chunk, size_t size);
|
||||
|
||||
@@ -5,7 +5,7 @@ typedef struct ckh_s ckh_t;
|
||||
typedef struct ckhc_s ckhc_t;
|
||||
|
||||
/* Typedefs to allow easy function pointer passing. */
|
||||
typedef void ckh_hash_t (const void *, unsigned, size_t *, size_t *);
|
||||
typedef void ckh_hash_t (const void *, size_t[2]);
|
||||
typedef bool ckh_keycomp_t (const void *, const void *);
|
||||
|
||||
/* Maintain counters used to get an idea of performance. */
|
||||
@@ -75,11 +75,9 @@ bool ckh_insert(ckh_t *ckh, const void *key, const void *data);
|
||||
bool ckh_remove(ckh_t *ckh, const void *searchkey, void **key,
|
||||
void **data);
|
||||
bool ckh_search(ckh_t *ckh, const void *seachkey, void **key, void **data);
|
||||
void ckh_string_hash(const void *key, unsigned minbits, size_t *hash1,
|
||||
size_t *hash2);
|
||||
void ckh_string_hash(const void *key, size_t r_hash[2]);
|
||||
bool ckh_string_keycomp(const void *k1, const void *k2);
|
||||
void ckh_pointer_hash(const void *key, unsigned minbits, size_t *hash1,
|
||||
size_t *hash2);
|
||||
void ckh_pointer_hash(const void *key, size_t r_hash[2]);
|
||||
bool ckh_pointer_keycomp(const void *k1, const void *k2);
|
||||
|
||||
#endif /* JEMALLOC_H_EXTERNS */
|
||||
|
||||
@@ -33,6 +33,7 @@ struct ctl_indexed_node_s {
|
||||
struct ctl_arena_stats_s {
|
||||
bool initialized;
|
||||
unsigned nthreads;
|
||||
const char *dss;
|
||||
size_t pactive;
|
||||
size_t pdirty;
|
||||
arena_stats_t astats;
|
||||
@@ -61,6 +62,7 @@ struct ctl_stats_s {
|
||||
uint64_t nmalloc; /* huge_nmalloc */
|
||||
uint64_t ndalloc; /* huge_ndalloc */
|
||||
} huge;
|
||||
unsigned narenas;
|
||||
ctl_arena_stats_t *arenas; /* (narenas + 1) elements. */
|
||||
};
|
||||
|
||||
@@ -75,6 +77,9 @@ int ctl_nametomib(const char *name, size_t *mibp, size_t *miblenp);
|
||||
int ctl_bymib(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp,
|
||||
void *newp, size_t newlen);
|
||||
bool ctl_boot(void);
|
||||
void ctl_prefork(void);
|
||||
void ctl_postfork_parent(void);
|
||||
void ctl_postfork_child(void);
|
||||
|
||||
#define xmallctl(name, oldp, oldlenp, newp, newlen) do { \
|
||||
if (je_mallctl(name, oldp, oldlenp, newp, newlen) \
|
||||
|
||||
@@ -23,6 +23,9 @@ struct extent_node_s {
|
||||
|
||||
/* Total region size. */
|
||||
size_t size;
|
||||
|
||||
/* True if zero-filled; used by chunk recycling code. */
|
||||
bool zeroed;
|
||||
};
|
||||
typedef rb_tree(extent_node_t) extent_tree_t;
|
||||
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
/*
|
||||
* The following hash function is based on MurmurHash3, placed into the public
|
||||
* domain by Austin Appleby. See http://code.google.com/p/smhasher/ for
|
||||
* details.
|
||||
*/
|
||||
/******************************************************************************/
|
||||
#ifdef JEMALLOC_H_TYPES
|
||||
|
||||
@@ -14,55 +19,311 @@
|
||||
#ifdef JEMALLOC_H_INLINES
|
||||
|
||||
#ifndef JEMALLOC_ENABLE_INLINE
|
||||
uint64_t hash(const void *key, size_t len, uint64_t seed);
|
||||
void hash(const void *key, size_t len, const uint32_t seed,
|
||||
size_t r_hash[2]);
|
||||
#endif
|
||||
|
||||
#if (defined(JEMALLOC_ENABLE_INLINE) || defined(JEMALLOC_HASH_C_))
|
||||
/*
|
||||
* The following hash function is based on MurmurHash64A(), placed into the
|
||||
* public domain by Austin Appleby. See http://murmurhash.googlepages.com/ for
|
||||
* details.
|
||||
*/
|
||||
JEMALLOC_INLINE uint64_t
|
||||
hash(const void *key, size_t len, uint64_t seed)
|
||||
/******************************************************************************/
|
||||
/* Internal implementation. */
|
||||
JEMALLOC_INLINE uint32_t
|
||||
hash_rotl_32(uint32_t x, int8_t r)
|
||||
{
|
||||
const uint64_t m = UINT64_C(0xc6a4a7935bd1e995);
|
||||
const int r = 47;
|
||||
uint64_t h = seed ^ (len * m);
|
||||
const uint64_t *data = (const uint64_t *)key;
|
||||
const uint64_t *end = data + (len/8);
|
||||
const unsigned char *data2;
|
||||
|
||||
assert(((uintptr_t)key & 0x7) == 0);
|
||||
return (x << r) | (x >> (32 - r));
|
||||
}
|
||||
|
||||
while(data != end) {
|
||||
uint64_t k = *data++;
|
||||
JEMALLOC_INLINE uint64_t
|
||||
hash_rotl_64(uint64_t x, int8_t r)
|
||||
{
|
||||
return (x << r) | (x >> (64 - r));
|
||||
}
|
||||
|
||||
k *= m;
|
||||
k ^= k >> r;
|
||||
k *= m;
|
||||
JEMALLOC_INLINE uint32_t
|
||||
hash_get_block_32(const uint32_t *p, int i)
|
||||
{
|
||||
|
||||
h ^= k;
|
||||
h *= m;
|
||||
return p[i];
|
||||
}
|
||||
|
||||
JEMALLOC_INLINE uint64_t
|
||||
hash_get_block_64(const uint64_t *p, int i)
|
||||
{
|
||||
|
||||
return p[i];
|
||||
}
|
||||
|
||||
JEMALLOC_INLINE uint32_t
|
||||
hash_fmix_32(uint32_t h)
|
||||
{
|
||||
|
||||
h ^= h >> 16;
|
||||
h *= 0x85ebca6b;
|
||||
h ^= h >> 13;
|
||||
h *= 0xc2b2ae35;
|
||||
h ^= h >> 16;
|
||||
|
||||
return h;
|
||||
}
|
||||
|
||||
JEMALLOC_INLINE uint64_t
|
||||
hash_fmix_64(uint64_t k)
|
||||
{
|
||||
|
||||
k ^= k >> 33;
|
||||
k *= QU(0xff51afd7ed558ccdLLU);
|
||||
k ^= k >> 33;
|
||||
k *= QU(0xc4ceb9fe1a85ec53LLU);
|
||||
k ^= k >> 33;
|
||||
|
||||
return k;
|
||||
}
|
||||
|
||||
JEMALLOC_INLINE uint32_t
|
||||
hash_x86_32(const void *key, int len, uint32_t seed)
|
||||
{
|
||||
const uint8_t *data = (const uint8_t *) key;
|
||||
const int nblocks = len / 4;
|
||||
|
||||
uint32_t h1 = seed;
|
||||
|
||||
const uint32_t c1 = 0xcc9e2d51;
|
||||
const uint32_t c2 = 0x1b873593;
|
||||
|
||||
/* body */
|
||||
{
|
||||
const uint32_t *blocks = (const uint32_t *) (data + nblocks*4);
|
||||
int i;
|
||||
|
||||
for (i = -nblocks; i; i++) {
|
||||
uint32_t k1 = hash_get_block_32(blocks, i);
|
||||
|
||||
k1 *= c1;
|
||||
k1 = hash_rotl_32(k1, 15);
|
||||
k1 *= c2;
|
||||
|
||||
h1 ^= k1;
|
||||
h1 = hash_rotl_32(h1, 13);
|
||||
h1 = h1*5 + 0xe6546b64;
|
||||
}
|
||||
}
|
||||
|
||||
data2 = (const unsigned char *)data;
|
||||
switch(len & 7) {
|
||||
case 7: h ^= ((uint64_t)(data2[6])) << 48;
|
||||
case 6: h ^= ((uint64_t)(data2[5])) << 40;
|
||||
case 5: h ^= ((uint64_t)(data2[4])) << 32;
|
||||
case 4: h ^= ((uint64_t)(data2[3])) << 24;
|
||||
case 3: h ^= ((uint64_t)(data2[2])) << 16;
|
||||
case 2: h ^= ((uint64_t)(data2[1])) << 8;
|
||||
case 1: h ^= ((uint64_t)(data2[0]));
|
||||
h *= m;
|
||||
/* tail */
|
||||
{
|
||||
const uint8_t *tail = (const uint8_t *) (data + nblocks*4);
|
||||
|
||||
uint32_t k1 = 0;
|
||||
|
||||
switch (len & 3) {
|
||||
case 3: k1 ^= tail[2] << 16;
|
||||
case 2: k1 ^= tail[1] << 8;
|
||||
case 1: k1 ^= tail[0]; k1 *= c1; k1 = hash_rotl_32(k1, 15);
|
||||
k1 *= c2; h1 ^= k1;
|
||||
}
|
||||
}
|
||||
|
||||
h ^= h >> r;
|
||||
h *= m;
|
||||
h ^= h >> r;
|
||||
/* finalization */
|
||||
h1 ^= len;
|
||||
|
||||
return (h);
|
||||
h1 = hash_fmix_32(h1);
|
||||
|
||||
return h1;
|
||||
}
|
||||
|
||||
UNUSED JEMALLOC_INLINE void
|
||||
hash_x86_128(const void *key, const int len, uint32_t seed,
|
||||
uint64_t r_out[2])
|
||||
{
|
||||
const uint8_t * data = (const uint8_t *) key;
|
||||
const int nblocks = len / 16;
|
||||
|
||||
uint32_t h1 = seed;
|
||||
uint32_t h2 = seed;
|
||||
uint32_t h3 = seed;
|
||||
uint32_t h4 = seed;
|
||||
|
||||
const uint32_t c1 = 0x239b961b;
|
||||
const uint32_t c2 = 0xab0e9789;
|
||||
const uint32_t c3 = 0x38b34ae5;
|
||||
const uint32_t c4 = 0xa1e38b93;
|
||||
|
||||
/* body */
|
||||
{
|
||||
const uint32_t *blocks = (const uint32_t *) (data + nblocks*16);
|
||||
int i;
|
||||
|
||||
for (i = -nblocks; i; i++) {
|
||||
uint32_t k1 = hash_get_block_32(blocks, i*4 + 0);
|
||||
uint32_t k2 = hash_get_block_32(blocks, i*4 + 1);
|
||||
uint32_t k3 = hash_get_block_32(blocks, i*4 + 2);
|
||||
uint32_t k4 = hash_get_block_32(blocks, i*4 + 3);
|
||||
|
||||
k1 *= c1; k1 = hash_rotl_32(k1, 15); k1 *= c2; h1 ^= k1;
|
||||
|
||||
h1 = hash_rotl_32(h1, 19); h1 += h2;
|
||||
h1 = h1*5 + 0x561ccd1b;
|
||||
|
||||
k2 *= c2; k2 = hash_rotl_32(k2, 16); k2 *= c3; h2 ^= k2;
|
||||
|
||||
h2 = hash_rotl_32(h2, 17); h2 += h3;
|
||||
h2 = h2*5 + 0x0bcaa747;
|
||||
|
||||
k3 *= c3; k3 = hash_rotl_32(k3, 17); k3 *= c4; h3 ^= k3;
|
||||
|
||||
h3 = hash_rotl_32(h3, 15); h3 += h4;
|
||||
h3 = h3*5 + 0x96cd1c35;
|
||||
|
||||
k4 *= c4; k4 = hash_rotl_32(k4, 18); k4 *= c1; h4 ^= k4;
|
||||
|
||||
h4 = hash_rotl_32(h4, 13); h4 += h1;
|
||||
h4 = h4*5 + 0x32ac3b17;
|
||||
}
|
||||
}
|
||||
|
||||
/* tail */
|
||||
{
|
||||
const uint8_t *tail = (const uint8_t *) (data + nblocks*16);
|
||||
uint32_t k1 = 0;
|
||||
uint32_t k2 = 0;
|
||||
uint32_t k3 = 0;
|
||||
uint32_t k4 = 0;
|
||||
|
||||
switch (len & 15) {
|
||||
case 15: k4 ^= tail[14] << 16;
|
||||
case 14: k4 ^= tail[13] << 8;
|
||||
case 13: k4 ^= tail[12] << 0;
|
||||
k4 *= c4; k4 = hash_rotl_32(k4, 18); k4 *= c1; h4 ^= k4;
|
||||
|
||||
case 12: k3 ^= tail[11] << 24;
|
||||
case 11: k3 ^= tail[10] << 16;
|
||||
case 10: k3 ^= tail[ 9] << 8;
|
||||
case 9: k3 ^= tail[ 8] << 0;
|
||||
k3 *= c3; k3 = hash_rotl_32(k3, 17); k3 *= c4; h3 ^= k3;
|
||||
|
||||
case 8: k2 ^= tail[ 7] << 24;
|
||||
case 7: k2 ^= tail[ 6] << 16;
|
||||
case 6: k2 ^= tail[ 5] << 8;
|
||||
case 5: k2 ^= tail[ 4] << 0;
|
||||
k2 *= c2; k2 = hash_rotl_32(k2, 16); k2 *= c3; h2 ^= k2;
|
||||
|
||||
case 4: k1 ^= tail[ 3] << 24;
|
||||
case 3: k1 ^= tail[ 2] << 16;
|
||||
case 2: k1 ^= tail[ 1] << 8;
|
||||
case 1: k1 ^= tail[ 0] << 0;
|
||||
k1 *= c1; k1 = hash_rotl_32(k1, 15); k1 *= c2; h1 ^= k1;
|
||||
}
|
||||
}
|
||||
|
||||
/* finalization */
|
||||
h1 ^= len; h2 ^= len; h3 ^= len; h4 ^= len;
|
||||
|
||||
h1 += h2; h1 += h3; h1 += h4;
|
||||
h2 += h1; h3 += h1; h4 += h1;
|
||||
|
||||
h1 = hash_fmix_32(h1);
|
||||
h2 = hash_fmix_32(h2);
|
||||
h3 = hash_fmix_32(h3);
|
||||
h4 = hash_fmix_32(h4);
|
||||
|
||||
h1 += h2; h1 += h3; h1 += h4;
|
||||
h2 += h1; h3 += h1; h4 += h1;
|
||||
|
||||
r_out[0] = (((uint64_t) h2) << 32) | h1;
|
||||
r_out[1] = (((uint64_t) h4) << 32) | h3;
|
||||
}
|
||||
|
||||
UNUSED JEMALLOC_INLINE void
|
||||
hash_x64_128(const void *key, const int len, const uint32_t seed,
|
||||
uint64_t r_out[2])
|
||||
{
|
||||
const uint8_t *data = (const uint8_t *) key;
|
||||
const int nblocks = len / 16;
|
||||
|
||||
uint64_t h1 = seed;
|
||||
uint64_t h2 = seed;
|
||||
|
||||
const uint64_t c1 = QU(0x87c37b91114253d5LLU);
|
||||
const uint64_t c2 = QU(0x4cf5ad432745937fLLU);
|
||||
|
||||
/* body */
|
||||
{
|
||||
const uint64_t *blocks = (const uint64_t *) (data);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < nblocks; i++) {
|
||||
uint64_t k1 = hash_get_block_64(blocks, i*2 + 0);
|
||||
uint64_t k2 = hash_get_block_64(blocks, i*2 + 1);
|
||||
|
||||
k1 *= c1; k1 = hash_rotl_64(k1, 31); k1 *= c2; h1 ^= k1;
|
||||
|
||||
h1 = hash_rotl_64(h1, 27); h1 += h2;
|
||||
h1 = h1*5 + 0x52dce729;
|
||||
|
||||
k2 *= c2; k2 = hash_rotl_64(k2, 33); k2 *= c1; h2 ^= k2;
|
||||
|
||||
h2 = hash_rotl_64(h2, 31); h2 += h1;
|
||||
h2 = h2*5 + 0x38495ab5;
|
||||
}
|
||||
}
|
||||
|
||||
/* tail */
|
||||
{
|
||||
const uint8_t *tail = (const uint8_t*)(data + nblocks*16);
|
||||
uint64_t k1 = 0;
|
||||
uint64_t k2 = 0;
|
||||
|
||||
switch (len & 15) {
|
||||
case 15: k2 ^= ((uint64_t)(tail[14])) << 48;
|
||||
case 14: k2 ^= ((uint64_t)(tail[13])) << 40;
|
||||
case 13: k2 ^= ((uint64_t)(tail[12])) << 32;
|
||||
case 12: k2 ^= ((uint64_t)(tail[11])) << 24;
|
||||
case 11: k2 ^= ((uint64_t)(tail[10])) << 16;
|
||||
case 10: k2 ^= ((uint64_t)(tail[ 9])) << 8;
|
||||
case 9: k2 ^= ((uint64_t)(tail[ 8])) << 0;
|
||||
k2 *= c2; k2 = hash_rotl_64(k2, 33); k2 *= c1; h2 ^= k2;
|
||||
|
||||
case 8: k1 ^= ((uint64_t)(tail[ 7])) << 56;
|
||||
case 7: k1 ^= ((uint64_t)(tail[ 6])) << 48;
|
||||
case 6: k1 ^= ((uint64_t)(tail[ 5])) << 40;
|
||||
case 5: k1 ^= ((uint64_t)(tail[ 4])) << 32;
|
||||
case 4: k1 ^= ((uint64_t)(tail[ 3])) << 24;
|
||||
case 3: k1 ^= ((uint64_t)(tail[ 2])) << 16;
|
||||
case 2: k1 ^= ((uint64_t)(tail[ 1])) << 8;
|
||||
case 1: k1 ^= ((uint64_t)(tail[ 0])) << 0;
|
||||
k1 *= c1; k1 = hash_rotl_64(k1, 31); k1 *= c2; h1 ^= k1;
|
||||
}
|
||||
}
|
||||
|
||||
/* finalization */
|
||||
h1 ^= len; h2 ^= len;
|
||||
|
||||
h1 += h2;
|
||||
h2 += h1;
|
||||
|
||||
h1 = hash_fmix_64(h1);
|
||||
h2 = hash_fmix_64(h2);
|
||||
|
||||
h1 += h2;
|
||||
h2 += h1;
|
||||
|
||||
r_out[0] = h1;
|
||||
r_out[1] = h2;
|
||||
}
|
||||
|
||||
|
||||
/******************************************************************************/
|
||||
/* API. */
|
||||
JEMALLOC_INLINE void
|
||||
hash(const void *key, size_t len, const uint32_t seed, size_t r_hash[2])
|
||||
{
|
||||
#if (LG_SIZEOF_PTR == 3)
|
||||
hash_x64_128(key, len, seed, (uint64_t *)r_hash);
|
||||
#else
|
||||
uint64_t hashes[2];
|
||||
hash_x86_128(key, len, seed, hashes);
|
||||
r_hash[0] = (size_t)hashes[0];
|
||||
r_hash[1] = (size_t)hashes[1];
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ void *huge_palloc(size_t size, size_t alignment, bool zero);
|
||||
void *huge_ralloc_no_move(void *ptr, size_t oldsize, size_t size,
|
||||
size_t extra);
|
||||
void *huge_ralloc(void *ptr, size_t oldsize, size_t size, size_t extra,
|
||||
size_t alignment, bool zero);
|
||||
size_t alignment, bool zero, bool try_tcache_dalloc);
|
||||
void huge_dalloc(void *ptr, bool unmap);
|
||||
size_t huge_salloc(const void *ptr);
|
||||
prof_ctx_t *huge_prof_ctx_get(const void *ptr);
|
||||
|
||||
@@ -226,6 +226,7 @@ static const bool config_ivsalloc =
|
||||
#define ALLOCM_LG_ALIGN_MASK ((int)0x3f)
|
||||
|
||||
#define ZU(z) ((size_t)z)
|
||||
#define QU(q) ((uint64_t)q)
|
||||
|
||||
#ifndef __DECONST
|
||||
# define __DECONST(type, var) ((type)(uintptr_t)(const void *)(var))
|
||||
@@ -233,10 +234,17 @@ static const bool config_ivsalloc =
|
||||
|
||||
#ifdef JEMALLOC_DEBUG
|
||||
/* Disable inlining to make debugging easier. */
|
||||
# define JEMALLOC_ALWAYS_INLINE
|
||||
# define JEMALLOC_INLINE
|
||||
# define inline
|
||||
#else
|
||||
# define JEMALLOC_ENABLE_INLINE
|
||||
# ifdef JEMALLOC_HAVE_ATTR
|
||||
# define JEMALLOC_ALWAYS_INLINE \
|
||||
static inline JEMALLOC_ATTR(unused) JEMALLOC_ATTR(always_inline)
|
||||
# else
|
||||
# define JEMALLOC_ALWAYS_INLINE static inline
|
||||
# endif
|
||||
# define JEMALLOC_INLINE static inline
|
||||
# ifdef _MSC_VER
|
||||
# define inline _inline
|
||||
@@ -270,6 +278,9 @@ static const bool config_ivsalloc =
|
||||
# ifdef __arm__
|
||||
# define LG_QUANTUM 3
|
||||
# endif
|
||||
# ifdef __hppa__
|
||||
# define LG_QUANTUM 4
|
||||
# endif
|
||||
# ifdef __mips__
|
||||
# define LG_QUANTUM 3
|
||||
# endif
|
||||
@@ -356,7 +367,11 @@ static const bool config_ivsalloc =
|
||||
# include <malloc.h>
|
||||
# define alloca _alloca
|
||||
# else
|
||||
# include <alloca.h>
|
||||
# ifdef JEMALLOC_HAS_ALLOCA_H
|
||||
# include <alloca.h>
|
||||
# else
|
||||
# include <stdlib.h>
|
||||
# endif
|
||||
# endif
|
||||
# define VARIABLE_ARRAY(type, name, count) \
|
||||
type *name = alloca(sizeof(type) * count)
|
||||
@@ -424,6 +439,7 @@ static const bool config_ivsalloc =
|
||||
VALGRIND_FREELIKE_BLOCK(ptr, rzsize); \
|
||||
} while (0)
|
||||
#else
|
||||
#define RUNNING_ON_VALGRIND ((unsigned)0)
|
||||
#define VALGRIND_MALLOCLIKE_BLOCK(addr, sizeB, rzB, is_zeroed)
|
||||
#define VALGRIND_RESIZEINPLACE_BLOCK(addr, oldSizeB, newSizeB, rzB)
|
||||
#define VALGRIND_FREELIKE_BLOCK(addr, rzB)
|
||||
@@ -510,13 +526,19 @@ extern size_t opt_narenas;
|
||||
/* Number of CPUs. */
|
||||
extern unsigned ncpus;
|
||||
|
||||
extern malloc_mutex_t arenas_lock; /* Protects arenas initialization. */
|
||||
/* Protects arenas initialization (arenas, arenas_total). */
|
||||
extern malloc_mutex_t arenas_lock;
|
||||
/*
|
||||
* Arenas that are used to service external requests. Not all elements of the
|
||||
* arenas array are necessarily used; arenas are created lazily as needed.
|
||||
*
|
||||
* arenas[0..narenas_auto) are used for automatic multiplexing of threads and
|
||||
* arenas. arenas[narenas_auto..narenas_total) are only used if the application
|
||||
* takes some action to create them and allocate from them.
|
||||
*/
|
||||
extern arena_t **arenas;
|
||||
extern unsigned narenas;
|
||||
extern unsigned narenas_total;
|
||||
extern unsigned narenas_auto; /* Read-only after initialization. */
|
||||
|
||||
arena_t *arenas_extend(unsigned ind);
|
||||
void arenas_cleanup(void *arg);
|
||||
@@ -571,6 +593,7 @@ malloc_tsd_protos(JEMALLOC_ATTR(unused), arenas, arena_t *)
|
||||
|
||||
size_t s2u(size_t size);
|
||||
size_t sa2u(size_t size, size_t alignment);
|
||||
unsigned narenas_total_get(void);
|
||||
arena_t *choose_arena(arena_t *arena);
|
||||
#endif
|
||||
|
||||
@@ -580,13 +603,14 @@ arena_t *choose_arena(arena_t *arena);
|
||||
* for allocations.
|
||||
*/
|
||||
malloc_tsd_externs(arenas, arena_t *)
|
||||
malloc_tsd_funcs(JEMALLOC_INLINE, arenas, arena_t *, NULL, arenas_cleanup)
|
||||
malloc_tsd_funcs(JEMALLOC_ALWAYS_INLINE, arenas, arena_t *, NULL,
|
||||
arenas_cleanup)
|
||||
|
||||
/*
|
||||
* Compute usable size that would result from allocating an object with the
|
||||
* specified size.
|
||||
*/
|
||||
JEMALLOC_INLINE size_t
|
||||
JEMALLOC_ALWAYS_INLINE size_t
|
||||
s2u(size_t size)
|
||||
{
|
||||
|
||||
@@ -601,7 +625,7 @@ s2u(size_t size)
|
||||
* Compute usable size that would result from allocating an object with the
|
||||
* specified size and alignment.
|
||||
*/
|
||||
JEMALLOC_INLINE size_t
|
||||
JEMALLOC_ALWAYS_INLINE size_t
|
||||
sa2u(size_t size, size_t alignment)
|
||||
{
|
||||
size_t usize;
|
||||
@@ -675,6 +699,18 @@ sa2u(size_t size, size_t alignment)
|
||||
}
|
||||
}
|
||||
|
||||
JEMALLOC_INLINE unsigned
|
||||
narenas_total_get(void)
|
||||
{
|
||||
unsigned narenas;
|
||||
|
||||
malloc_mutex_lock(&arenas_lock);
|
||||
narenas = narenas_total;
|
||||
malloc_mutex_unlock(&arenas_lock);
|
||||
|
||||
return (narenas);
|
||||
}
|
||||
|
||||
/* Choose an arena based on a per-thread value. */
|
||||
JEMALLOC_INLINE arena_t *
|
||||
choose_arena(arena_t *arena)
|
||||
@@ -710,45 +746,69 @@ choose_arena(arena_t *arena)
|
||||
#include "jemalloc/internal/quarantine.h"
|
||||
|
||||
#ifndef JEMALLOC_ENABLE_INLINE
|
||||
void *imallocx(size_t size, bool try_tcache, arena_t *arena);
|
||||
void *imalloc(size_t size);
|
||||
void *icallocx(size_t size, bool try_tcache, arena_t *arena);
|
||||
void *icalloc(size_t size);
|
||||
void *ipallocx(size_t usize, size_t alignment, bool zero, bool try_tcache,
|
||||
arena_t *arena);
|
||||
void *ipalloc(size_t usize, size_t alignment, bool zero);
|
||||
size_t isalloc(const void *ptr, bool demote);
|
||||
size_t ivsalloc(const void *ptr, bool demote);
|
||||
size_t u2rz(size_t usize);
|
||||
size_t p2rz(const void *ptr);
|
||||
void idallocx(void *ptr, bool try_tcache);
|
||||
void idalloc(void *ptr);
|
||||
void iqallocx(void *ptr, bool try_tcache);
|
||||
void iqalloc(void *ptr);
|
||||
void *irallocx(void *ptr, size_t size, size_t extra, size_t alignment,
|
||||
bool zero, bool no_move, bool try_tcache_alloc, bool try_tcache_dalloc,
|
||||
arena_t *arena);
|
||||
void *iralloc(void *ptr, size_t size, size_t extra, size_t alignment,
|
||||
bool zero, bool no_move);
|
||||
malloc_tsd_protos(JEMALLOC_ATTR(unused), thread_allocated, thread_allocated_t)
|
||||
#endif
|
||||
|
||||
#if (defined(JEMALLOC_ENABLE_INLINE) || defined(JEMALLOC_C_))
|
||||
JEMALLOC_INLINE void *
|
||||
imalloc(size_t size)
|
||||
JEMALLOC_ALWAYS_INLINE void *
|
||||
imallocx(size_t size, bool try_tcache, arena_t *arena)
|
||||
{
|
||||
|
||||
assert(size != 0);
|
||||
|
||||
if (size <= arena_maxclass)
|
||||
return (arena_malloc(NULL, size, false, true));
|
||||
return (arena_malloc(arena, size, false, try_tcache));
|
||||
else
|
||||
return (huge_malloc(size, false));
|
||||
}
|
||||
|
||||
JEMALLOC_INLINE void *
|
||||
icalloc(size_t size)
|
||||
JEMALLOC_ALWAYS_INLINE void *
|
||||
imalloc(size_t size)
|
||||
{
|
||||
|
||||
return (imallocx(size, true, NULL));
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE void *
|
||||
icallocx(size_t size, bool try_tcache, arena_t *arena)
|
||||
{
|
||||
|
||||
if (size <= arena_maxclass)
|
||||
return (arena_malloc(NULL, size, true, true));
|
||||
return (arena_malloc(arena, size, true, try_tcache));
|
||||
else
|
||||
return (huge_malloc(size, true));
|
||||
}
|
||||
|
||||
JEMALLOC_INLINE void *
|
||||
ipalloc(size_t usize, size_t alignment, bool zero)
|
||||
JEMALLOC_ALWAYS_INLINE void *
|
||||
icalloc(size_t size)
|
||||
{
|
||||
|
||||
return (icallocx(size, true, NULL));
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE void *
|
||||
ipallocx(size_t usize, size_t alignment, bool zero, bool try_tcache,
|
||||
arena_t *arena)
|
||||
{
|
||||
void *ret;
|
||||
|
||||
@@ -756,11 +816,11 @@ ipalloc(size_t usize, size_t alignment, bool zero)
|
||||
assert(usize == sa2u(usize, alignment));
|
||||
|
||||
if (usize <= arena_maxclass && alignment <= PAGE)
|
||||
ret = arena_malloc(NULL, usize, zero, true);
|
||||
ret = arena_malloc(arena, usize, zero, try_tcache);
|
||||
else {
|
||||
if (usize <= arena_maxclass) {
|
||||
ret = arena_palloc(choose_arena(NULL), usize, alignment,
|
||||
zero);
|
||||
ret = arena_palloc(choose_arena(arena), usize,
|
||||
alignment, zero);
|
||||
} else if (alignment <= chunksize)
|
||||
ret = huge_malloc(usize, zero);
|
||||
else
|
||||
@@ -771,12 +831,19 @@ ipalloc(size_t usize, size_t alignment, bool zero)
|
||||
return (ret);
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE void *
|
||||
ipalloc(size_t usize, size_t alignment, bool zero)
|
||||
{
|
||||
|
||||
return (ipallocx(usize, alignment, zero, true, NULL));
|
||||
}
|
||||
|
||||
/*
|
||||
* Typical usage:
|
||||
* void *ptr = [...]
|
||||
* size_t sz = isalloc(ptr, config_prof);
|
||||
*/
|
||||
JEMALLOC_INLINE size_t
|
||||
JEMALLOC_ALWAYS_INLINE size_t
|
||||
isalloc(const void *ptr, bool demote)
|
||||
{
|
||||
size_t ret;
|
||||
@@ -795,7 +862,7 @@ isalloc(const void *ptr, bool demote)
|
||||
return (ret);
|
||||
}
|
||||
|
||||
JEMALLOC_INLINE size_t
|
||||
JEMALLOC_ALWAYS_INLINE size_t
|
||||
ivsalloc(const void *ptr, bool demote)
|
||||
{
|
||||
|
||||
@@ -828,8 +895,8 @@ p2rz(const void *ptr)
|
||||
return (u2rz(usize));
|
||||
}
|
||||
|
||||
JEMALLOC_INLINE void
|
||||
idalloc(void *ptr)
|
||||
JEMALLOC_ALWAYS_INLINE void
|
||||
idallocx(void *ptr, bool try_tcache)
|
||||
{
|
||||
arena_chunk_t *chunk;
|
||||
|
||||
@@ -837,24 +904,38 @@ idalloc(void *ptr)
|
||||
|
||||
chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr);
|
||||
if (chunk != ptr)
|
||||
arena_dalloc(chunk->arena, chunk, ptr, true);
|
||||
arena_dalloc(chunk->arena, chunk, ptr, try_tcache);
|
||||
else
|
||||
huge_dalloc(ptr, true);
|
||||
}
|
||||
|
||||
JEMALLOC_INLINE void
|
||||
iqalloc(void *ptr)
|
||||
JEMALLOC_ALWAYS_INLINE void
|
||||
idalloc(void *ptr)
|
||||
{
|
||||
|
||||
idallocx(ptr, true);
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE void
|
||||
iqallocx(void *ptr, bool try_tcache)
|
||||
{
|
||||
|
||||
if (config_fill && opt_quarantine)
|
||||
quarantine(ptr);
|
||||
else
|
||||
idalloc(ptr);
|
||||
idallocx(ptr, try_tcache);
|
||||
}
|
||||
|
||||
JEMALLOC_INLINE void *
|
||||
iralloc(void *ptr, size_t size, size_t extra, size_t alignment, bool zero,
|
||||
bool no_move)
|
||||
JEMALLOC_ALWAYS_INLINE void
|
||||
iqalloc(void *ptr)
|
||||
{
|
||||
|
||||
iqallocx(ptr, true);
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE void *
|
||||
irallocx(void *ptr, size_t size, size_t extra, size_t alignment, bool zero,
|
||||
bool no_move, bool try_tcache_alloc, bool try_tcache_dalloc, arena_t *arena)
|
||||
{
|
||||
void *ret;
|
||||
size_t oldsize;
|
||||
@@ -877,7 +958,7 @@ iralloc(void *ptr, size_t size, size_t extra, size_t alignment, bool zero,
|
||||
usize = sa2u(size + extra, alignment);
|
||||
if (usize == 0)
|
||||
return (NULL);
|
||||
ret = ipalloc(usize, alignment, zero);
|
||||
ret = ipallocx(usize, alignment, zero, try_tcache_alloc, arena);
|
||||
if (ret == NULL) {
|
||||
if (extra == 0)
|
||||
return (NULL);
|
||||
@@ -885,7 +966,8 @@ iralloc(void *ptr, size_t size, size_t extra, size_t alignment, bool zero,
|
||||
usize = sa2u(size, alignment);
|
||||
if (usize == 0)
|
||||
return (NULL);
|
||||
ret = ipalloc(usize, alignment, zero);
|
||||
ret = ipallocx(usize, alignment, zero, try_tcache_alloc,
|
||||
arena);
|
||||
if (ret == NULL)
|
||||
return (NULL);
|
||||
}
|
||||
@@ -896,7 +978,7 @@ iralloc(void *ptr, size_t size, size_t extra, size_t alignment, bool zero,
|
||||
*/
|
||||
copysize = (size < oldsize) ? size : oldsize;
|
||||
memcpy(ret, ptr, copysize);
|
||||
iqalloc(ptr);
|
||||
iqallocx(ptr, try_tcache_dalloc);
|
||||
return (ret);
|
||||
}
|
||||
|
||||
@@ -910,17 +992,27 @@ iralloc(void *ptr, size_t size, size_t extra, size_t alignment, bool zero,
|
||||
}
|
||||
} else {
|
||||
if (size + extra <= arena_maxclass) {
|
||||
return (arena_ralloc(ptr, oldsize, size, extra,
|
||||
alignment, zero, true));
|
||||
return (arena_ralloc(arena, ptr, oldsize, size, extra,
|
||||
alignment, zero, try_tcache_alloc,
|
||||
try_tcache_dalloc));
|
||||
} else {
|
||||
return (huge_ralloc(ptr, oldsize, size, extra,
|
||||
alignment, zero));
|
||||
alignment, zero, try_tcache_dalloc));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE void *
|
||||
iralloc(void *ptr, size_t size, size_t extra, size_t alignment, bool zero,
|
||||
bool no_move)
|
||||
{
|
||||
|
||||
return (irallocx(ptr, size, extra, alignment, zero, no_move, true, true,
|
||||
NULL));
|
||||
}
|
||||
|
||||
malloc_tsd_externs(thread_allocated, thread_allocated_t)
|
||||
malloc_tsd_funcs(JEMALLOC_INLINE, thread_allocated, thread_allocated_t,
|
||||
malloc_tsd_funcs(JEMALLOC_ALWAYS_INLINE, thread_allocated, thread_allocated_t,
|
||||
THREAD_ALLOCATED_INITIALIZER, malloc_tsd_no_cleanup)
|
||||
#endif
|
||||
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
#define arena_dalloc_large JEMALLOC_N(arena_dalloc_large)
|
||||
#define arena_dalloc_large_locked JEMALLOC_N(arena_dalloc_large_locked)
|
||||
#define arena_dalloc_small JEMALLOC_N(arena_dalloc_small)
|
||||
#define arena_dss_prec_get JEMALLOC_N(arena_dss_prec_get)
|
||||
#define arena_dss_prec_set JEMALLOC_N(arena_dss_prec_set)
|
||||
#define arena_malloc JEMALLOC_N(arena_malloc)
|
||||
#define arena_malloc_large JEMALLOC_N(arena_malloc_large)
|
||||
#define arena_malloc_small JEMALLOC_N(arena_malloc_small)
|
||||
@@ -39,6 +41,8 @@
|
||||
#define arena_postfork_parent JEMALLOC_N(arena_postfork_parent)
|
||||
#define arena_prefork JEMALLOC_N(arena_prefork)
|
||||
#define arena_prof_accum JEMALLOC_N(arena_prof_accum)
|
||||
#define arena_prof_accum_impl JEMALLOC_N(arena_prof_accum_impl)
|
||||
#define arena_prof_accum_locked JEMALLOC_N(arena_prof_accum_locked)
|
||||
#define arena_prof_ctx_get JEMALLOC_N(arena_prof_ctx_get)
|
||||
#define arena_prof_ctx_set JEMALLOC_N(arena_prof_ctx_set)
|
||||
#define arena_prof_promoted JEMALLOC_N(arena_prof_promoted)
|
||||
@@ -51,17 +55,17 @@
|
||||
#define arena_stats_merge JEMALLOC_N(arena_stats_merge)
|
||||
#define arena_tcache_fill_small JEMALLOC_N(arena_tcache_fill_small)
|
||||
#define arenas JEMALLOC_N(arenas)
|
||||
#define arenas_bin_i_index JEMALLOC_N(arenas_bin_i_index)
|
||||
#define arenas_booted JEMALLOC_N(arenas_booted)
|
||||
#define arenas_cleanup JEMALLOC_N(arenas_cleanup)
|
||||
#define arenas_extend JEMALLOC_N(arenas_extend)
|
||||
#define arenas_initialized JEMALLOC_N(arenas_initialized)
|
||||
#define arenas_lock JEMALLOC_N(arenas_lock)
|
||||
#define arenas_lrun_i_index JEMALLOC_N(arenas_lrun_i_index)
|
||||
#define arenas_tls JEMALLOC_N(arenas_tls)
|
||||
#define arenas_tsd JEMALLOC_N(arenas_tsd)
|
||||
#define arenas_tsd_boot JEMALLOC_N(arenas_tsd_boot)
|
||||
#define arenas_tsd_cleanup_wrapper JEMALLOC_N(arenas_tsd_cleanup_wrapper)
|
||||
#define arenas_tsd_get JEMALLOC_N(arenas_tsd_get)
|
||||
#define arenas_tsd_get_wrapper JEMALLOC_N(arenas_tsd_get_wrapper)
|
||||
#define arenas_tsd_set JEMALLOC_N(arenas_tsd_set)
|
||||
#define atomic_add_u JEMALLOC_N(atomic_add_u)
|
||||
#define atomic_add_uint32 JEMALLOC_N(atomic_add_uint32)
|
||||
@@ -101,9 +105,15 @@
|
||||
#define chunk_dss_boot JEMALLOC_N(chunk_dss_boot)
|
||||
#define chunk_dss_postfork_child JEMALLOC_N(chunk_dss_postfork_child)
|
||||
#define chunk_dss_postfork_parent JEMALLOC_N(chunk_dss_postfork_parent)
|
||||
#define chunk_dss_prec_get JEMALLOC_N(chunk_dss_prec_get)
|
||||
#define chunk_dss_prec_set JEMALLOC_N(chunk_dss_prec_set)
|
||||
#define chunk_dss_prefork JEMALLOC_N(chunk_dss_prefork)
|
||||
#define chunk_in_dss JEMALLOC_N(chunk_in_dss)
|
||||
#define chunk_npages JEMALLOC_N(chunk_npages)
|
||||
#define chunk_postfork_child JEMALLOC_N(chunk_postfork_child)
|
||||
#define chunk_postfork_parent JEMALLOC_N(chunk_postfork_parent)
|
||||
#define chunk_prefork JEMALLOC_N(chunk_prefork)
|
||||
#define chunk_unmap JEMALLOC_N(chunk_unmap)
|
||||
#define chunks_mtx JEMALLOC_N(chunks_mtx)
|
||||
#define chunks_rtree JEMALLOC_N(chunks_rtree)
|
||||
#define chunksize JEMALLOC_N(chunksize)
|
||||
@@ -129,6 +139,10 @@
|
||||
#define ctl_bymib JEMALLOC_N(ctl_bymib)
|
||||
#define ctl_byname JEMALLOC_N(ctl_byname)
|
||||
#define ctl_nametomib JEMALLOC_N(ctl_nametomib)
|
||||
#define ctl_postfork_child JEMALLOC_N(ctl_postfork_child)
|
||||
#define ctl_postfork_parent JEMALLOC_N(ctl_postfork_parent)
|
||||
#define ctl_prefork JEMALLOC_N(ctl_prefork)
|
||||
#define dss_prec_names JEMALLOC_N(dss_prec_names)
|
||||
#define extent_tree_ad_first JEMALLOC_N(extent_tree_ad_first)
|
||||
#define extent_tree_ad_insert JEMALLOC_N(extent_tree_ad_insert)
|
||||
#define extent_tree_ad_iter JEMALLOC_N(extent_tree_ad_iter)
|
||||
@@ -161,7 +175,17 @@
|
||||
#define extent_tree_szad_reverse_iter_recurse JEMALLOC_N(extent_tree_szad_reverse_iter_recurse)
|
||||
#define extent_tree_szad_reverse_iter_start JEMALLOC_N(extent_tree_szad_reverse_iter_start)
|
||||
#define extent_tree_szad_search JEMALLOC_N(extent_tree_szad_search)
|
||||
#define get_errno JEMALLOC_N(get_errno)
|
||||
#define hash JEMALLOC_N(hash)
|
||||
#define hash_fmix_32 JEMALLOC_N(hash_fmix_32)
|
||||
#define hash_fmix_64 JEMALLOC_N(hash_fmix_64)
|
||||
#define hash_get_block_32 JEMALLOC_N(hash_get_block_32)
|
||||
#define hash_get_block_64 JEMALLOC_N(hash_get_block_64)
|
||||
#define hash_rotl_32 JEMALLOC_N(hash_rotl_32)
|
||||
#define hash_rotl_64 JEMALLOC_N(hash_rotl_64)
|
||||
#define hash_x64_128 JEMALLOC_N(hash_x64_128)
|
||||
#define hash_x86_128 JEMALLOC_N(hash_x86_128)
|
||||
#define hash_x86_32 JEMALLOC_N(hash_x86_32)
|
||||
#define huge_allocated JEMALLOC_N(huge_allocated)
|
||||
#define huge_boot JEMALLOC_N(huge_boot)
|
||||
#define huge_dalloc JEMALLOC_N(huge_dalloc)
|
||||
@@ -180,11 +204,17 @@
|
||||
#define huge_salloc JEMALLOC_N(huge_salloc)
|
||||
#define iallocm JEMALLOC_N(iallocm)
|
||||
#define icalloc JEMALLOC_N(icalloc)
|
||||
#define icallocx JEMALLOC_N(icallocx)
|
||||
#define idalloc JEMALLOC_N(idalloc)
|
||||
#define idallocx JEMALLOC_N(idallocx)
|
||||
#define imalloc JEMALLOC_N(imalloc)
|
||||
#define imallocx JEMALLOC_N(imallocx)
|
||||
#define ipalloc JEMALLOC_N(ipalloc)
|
||||
#define ipallocx JEMALLOC_N(ipallocx)
|
||||
#define iqalloc JEMALLOC_N(iqalloc)
|
||||
#define iqallocx JEMALLOC_N(iqallocx)
|
||||
#define iralloc JEMALLOC_N(iralloc)
|
||||
#define irallocx JEMALLOC_N(irallocx)
|
||||
#define isalloc JEMALLOC_N(isalloc)
|
||||
#define isthreaded JEMALLOC_N(isthreaded)
|
||||
#define ivsalloc JEMALLOC_N(ivsalloc)
|
||||
@@ -212,7 +242,9 @@
|
||||
#define map_bias JEMALLOC_N(map_bias)
|
||||
#define mb_write JEMALLOC_N(mb_write)
|
||||
#define mutex_boot JEMALLOC_N(mutex_boot)
|
||||
#define narenas JEMALLOC_N(narenas)
|
||||
#define narenas_auto JEMALLOC_N(narenas_auto)
|
||||
#define narenas_total JEMALLOC_N(narenas_total)
|
||||
#define narenas_total_get JEMALLOC_N(narenas_total_get)
|
||||
#define ncpus JEMALLOC_N(ncpus)
|
||||
#define nhbins JEMALLOC_N(nhbins)
|
||||
#define opt_abort JEMALLOC_N(opt_abort)
|
||||
@@ -254,6 +286,9 @@
|
||||
#define prof_lookup JEMALLOC_N(prof_lookup)
|
||||
#define prof_malloc JEMALLOC_N(prof_malloc)
|
||||
#define prof_mdump JEMALLOC_N(prof_mdump)
|
||||
#define prof_postfork_child JEMALLOC_N(prof_postfork_child)
|
||||
#define prof_postfork_parent JEMALLOC_N(prof_postfork_parent)
|
||||
#define prof_prefork JEMALLOC_N(prof_prefork)
|
||||
#define prof_promote JEMALLOC_N(prof_promote)
|
||||
#define prof_realloc JEMALLOC_N(prof_realloc)
|
||||
#define prof_sample_accum_update JEMALLOC_N(prof_sample_accum_update)
|
||||
@@ -264,26 +299,30 @@
|
||||
#define prof_tdata_init JEMALLOC_N(prof_tdata_init)
|
||||
#define prof_tdata_initialized JEMALLOC_N(prof_tdata_initialized)
|
||||
#define prof_tdata_tls JEMALLOC_N(prof_tdata_tls)
|
||||
#define prof_tdata_tsd JEMALLOC_N(prof_tdata_tsd)
|
||||
#define prof_tdata_tsd_boot JEMALLOC_N(prof_tdata_tsd_boot)
|
||||
#define prof_tdata_tsd_cleanup_wrapper JEMALLOC_N(prof_tdata_tsd_cleanup_wrapper)
|
||||
#define prof_tdata_tsd_get JEMALLOC_N(prof_tdata_tsd_get)
|
||||
#define prof_tdata_tsd_get_wrapper JEMALLOC_N(prof_tdata_tsd_get_wrapper)
|
||||
#define prof_tdata_tsd_set JEMALLOC_N(prof_tdata_tsd_set)
|
||||
#define quarantine JEMALLOC_N(quarantine)
|
||||
#define quarantine_boot JEMALLOC_N(quarantine_boot)
|
||||
#define quarantine_tsd_boot JEMALLOC_N(quarantine_tsd_boot)
|
||||
#define quarantine_tsd_cleanup_wrapper JEMALLOC_N(quarantine_tsd_cleanup_wrapper)
|
||||
#define quarantine_tsd_get JEMALLOC_N(quarantine_tsd_get)
|
||||
#define quarantine_tsd_get_wrapper JEMALLOC_N(quarantine_tsd_get_wrapper)
|
||||
#define quarantine_tsd_set JEMALLOC_N(quarantine_tsd_set)
|
||||
#define register_zone JEMALLOC_N(register_zone)
|
||||
#define rtree_get JEMALLOC_N(rtree_get)
|
||||
#define rtree_get_locked JEMALLOC_N(rtree_get_locked)
|
||||
#define rtree_new JEMALLOC_N(rtree_new)
|
||||
#define rtree_postfork_child JEMALLOC_N(rtree_postfork_child)
|
||||
#define rtree_postfork_parent JEMALLOC_N(rtree_postfork_parent)
|
||||
#define rtree_prefork JEMALLOC_N(rtree_prefork)
|
||||
#define rtree_set JEMALLOC_N(rtree_set)
|
||||
#define s2u JEMALLOC_N(s2u)
|
||||
#define sa2u JEMALLOC_N(sa2u)
|
||||
#define stats_arenas_i_bins_j_index JEMALLOC_N(stats_arenas_i_bins_j_index)
|
||||
#define stats_arenas_i_index JEMALLOC_N(stats_arenas_i_index)
|
||||
#define stats_arenas_i_lruns_j_index JEMALLOC_N(stats_arenas_i_lruns_j_index)
|
||||
#define set_errno JEMALLOC_N(set_errno)
|
||||
#define stats_cactive JEMALLOC_N(stats_cactive)
|
||||
#define stats_cactive_add JEMALLOC_N(stats_cactive_add)
|
||||
#define stats_cactive_get JEMALLOC_N(stats_cactive_get)
|
||||
@@ -311,9 +350,11 @@
|
||||
#define tcache_enabled_initialized JEMALLOC_N(tcache_enabled_initialized)
|
||||
#define tcache_enabled_set JEMALLOC_N(tcache_enabled_set)
|
||||
#define tcache_enabled_tls JEMALLOC_N(tcache_enabled_tls)
|
||||
#define tcache_enabled_tsd JEMALLOC_N(tcache_enabled_tsd)
|
||||
#define tcache_enabled_tsd_boot JEMALLOC_N(tcache_enabled_tsd_boot)
|
||||
#define tcache_enabled_tsd_cleanup_wrapper JEMALLOC_N(tcache_enabled_tsd_cleanup_wrapper)
|
||||
#define tcache_enabled_tsd_get JEMALLOC_N(tcache_enabled_tsd_get)
|
||||
#define tcache_enabled_tsd_get_wrapper JEMALLOC_N(tcache_enabled_tsd_get_wrapper)
|
||||
#define tcache_enabled_tsd_set JEMALLOC_N(tcache_enabled_tsd_set)
|
||||
#define tcache_event JEMALLOC_N(tcache_event)
|
||||
#define tcache_event_hard JEMALLOC_N(tcache_event_hard)
|
||||
@@ -325,15 +366,19 @@
|
||||
#define tcache_stats_merge JEMALLOC_N(tcache_stats_merge)
|
||||
#define tcache_thread_cleanup JEMALLOC_N(tcache_thread_cleanup)
|
||||
#define tcache_tls JEMALLOC_N(tcache_tls)
|
||||
#define tcache_tsd JEMALLOC_N(tcache_tsd)
|
||||
#define tcache_tsd_boot JEMALLOC_N(tcache_tsd_boot)
|
||||
#define tcache_tsd_cleanup_wrapper JEMALLOC_N(tcache_tsd_cleanup_wrapper)
|
||||
#define tcache_tsd_get JEMALLOC_N(tcache_tsd_get)
|
||||
#define tcache_tsd_get_wrapper JEMALLOC_N(tcache_tsd_get_wrapper)
|
||||
#define tcache_tsd_set JEMALLOC_N(tcache_tsd_set)
|
||||
#define thread_allocated_booted JEMALLOC_N(thread_allocated_booted)
|
||||
#define thread_allocated_initialized JEMALLOC_N(thread_allocated_initialized)
|
||||
#define thread_allocated_tls JEMALLOC_N(thread_allocated_tls)
|
||||
#define thread_allocated_tsd JEMALLOC_N(thread_allocated_tsd)
|
||||
#define thread_allocated_tsd_boot JEMALLOC_N(thread_allocated_tsd_boot)
|
||||
#define thread_allocated_tsd_cleanup_wrapper JEMALLOC_N(thread_allocated_tsd_cleanup_wrapper)
|
||||
#define thread_allocated_tsd_get JEMALLOC_N(thread_allocated_tsd_get)
|
||||
#define thread_allocated_tsd_get_wrapper JEMALLOC_N(thread_allocated_tsd_get_wrapper)
|
||||
#define thread_allocated_tsd_set JEMALLOC_N(thread_allocated_tsd_set)
|
||||
#define u2rz JEMALLOC_N(u2rz)
|
||||
|
||||
@@ -223,6 +223,9 @@ void prof_tdata_cleanup(void *arg);
|
||||
void prof_boot0(void);
|
||||
void prof_boot1(void);
|
||||
bool prof_boot2(void);
|
||||
void prof_prefork(void);
|
||||
void prof_postfork_parent(void);
|
||||
void prof_postfork_child(void);
|
||||
|
||||
#endif /* JEMALLOC_H_EXTERNS */
|
||||
/******************************************************************************/
|
||||
@@ -506,7 +509,7 @@ prof_realloc(const void *ptr, size_t size, prof_thr_cnt_t *cnt,
|
||||
if ((uintptr_t)cnt > (uintptr_t)1U) {
|
||||
prof_ctx_set(ptr, cnt->ctx);
|
||||
cnt->epoch++;
|
||||
} else
|
||||
} else if (ptr != NULL)
|
||||
prof_ctx_set(ptr, (prof_ctx_t *)(uintptr_t)1U);
|
||||
/*********/
|
||||
mb_write();
|
||||
|
||||
@@ -36,6 +36,9 @@ struct rtree_s {
|
||||
#ifdef JEMALLOC_H_EXTERNS
|
||||
|
||||
rtree_t *rtree_new(unsigned bits);
|
||||
void rtree_prefork(rtree_t *rtree);
|
||||
void rtree_postfork_parent(rtree_t *rtree);
|
||||
void rtree_postfork_child(rtree_t *rtree);
|
||||
|
||||
#endif /* JEMALLOC_H_EXTERNS */
|
||||
/******************************************************************************/
|
||||
|
||||
@@ -140,11 +140,11 @@ void tcache_dalloc_large(tcache_t *tcache, void *ptr, size_t size);
|
||||
#if (defined(JEMALLOC_ENABLE_INLINE) || defined(JEMALLOC_TCACHE_C_))
|
||||
/* Map of thread-specific caches. */
|
||||
malloc_tsd_externs(tcache, tcache_t *)
|
||||
malloc_tsd_funcs(JEMALLOC_INLINE, tcache, tcache_t *, NULL,
|
||||
malloc_tsd_funcs(JEMALLOC_ALWAYS_INLINE, tcache, tcache_t *, NULL,
|
||||
tcache_thread_cleanup)
|
||||
/* Per thread flag that allows thread caches to be disabled. */
|
||||
malloc_tsd_externs(tcache_enabled, tcache_enabled_t)
|
||||
malloc_tsd_funcs(JEMALLOC_INLINE, tcache_enabled, tcache_enabled_t,
|
||||
malloc_tsd_funcs(JEMALLOC_ALWAYS_INLINE, tcache_enabled, tcache_enabled_t,
|
||||
tcache_enabled_default, malloc_tsd_no_cleanup)
|
||||
|
||||
JEMALLOC_INLINE void
|
||||
@@ -206,7 +206,7 @@ tcache_enabled_set(bool enabled)
|
||||
}
|
||||
}
|
||||
|
||||
JEMALLOC_INLINE tcache_t *
|
||||
JEMALLOC_ALWAYS_INLINE tcache_t *
|
||||
tcache_get(bool create)
|
||||
{
|
||||
tcache_t *tcache;
|
||||
@@ -258,7 +258,7 @@ tcache_get(bool create)
|
||||
return (tcache);
|
||||
}
|
||||
|
||||
JEMALLOC_INLINE void
|
||||
JEMALLOC_ALWAYS_INLINE void
|
||||
tcache_event(tcache_t *tcache)
|
||||
{
|
||||
|
||||
@@ -271,7 +271,7 @@ tcache_event(tcache_t *tcache)
|
||||
tcache_event_hard(tcache);
|
||||
}
|
||||
|
||||
JEMALLOC_INLINE void *
|
||||
JEMALLOC_ALWAYS_INLINE void *
|
||||
tcache_alloc_easy(tcache_bin_t *tbin)
|
||||
{
|
||||
void *ret;
|
||||
@@ -287,7 +287,7 @@ tcache_alloc_easy(tcache_bin_t *tbin)
|
||||
return (ret);
|
||||
}
|
||||
|
||||
JEMALLOC_INLINE void *
|
||||
JEMALLOC_ALWAYS_INLINE void *
|
||||
tcache_alloc_small(tcache_t *tcache, size_t size, bool zero)
|
||||
{
|
||||
void *ret;
|
||||
@@ -320,6 +320,7 @@ tcache_alloc_small(tcache_t *tcache, size_t size, bool zero)
|
||||
}
|
||||
VALGRIND_MAKE_MEM_UNDEFINED(ret, size);
|
||||
memset(ret, 0, size);
|
||||
VALGRIND_MAKE_MEM_UNDEFINED(ret, size);
|
||||
}
|
||||
|
||||
if (config_stats)
|
||||
@@ -330,7 +331,7 @@ tcache_alloc_small(tcache_t *tcache, size_t size, bool zero)
|
||||
return (ret);
|
||||
}
|
||||
|
||||
JEMALLOC_INLINE void *
|
||||
JEMALLOC_ALWAYS_INLINE void *
|
||||
tcache_alloc_large(tcache_t *tcache, size_t size, bool zero)
|
||||
{
|
||||
void *ret;
|
||||
@@ -370,6 +371,7 @@ tcache_alloc_large(tcache_t *tcache, size_t size, bool zero)
|
||||
} else {
|
||||
VALGRIND_MAKE_MEM_UNDEFINED(ret, size);
|
||||
memset(ret, 0, size);
|
||||
VALGRIND_MAKE_MEM_UNDEFINED(ret, size);
|
||||
}
|
||||
|
||||
if (config_stats)
|
||||
@@ -382,7 +384,7 @@ tcache_alloc_large(tcache_t *tcache, size_t size, bool zero)
|
||||
return (ret);
|
||||
}
|
||||
|
||||
JEMALLOC_INLINE void
|
||||
JEMALLOC_ALWAYS_INLINE void
|
||||
tcache_dalloc_small(tcache_t *tcache, void *ptr, size_t binind)
|
||||
{
|
||||
tcache_bin_t *tbin;
|
||||
@@ -406,7 +408,7 @@ tcache_dalloc_small(tcache_t *tcache, void *ptr, size_t binind)
|
||||
tcache_event(tcache);
|
||||
}
|
||||
|
||||
JEMALLOC_INLINE void
|
||||
JEMALLOC_ALWAYS_INLINE void
|
||||
tcache_dalloc_large(tcache_t *tcache, void *ptr, size_t size)
|
||||
{
|
||||
size_t binind;
|
||||
|
||||
@@ -25,6 +25,8 @@ extern "C" {
|
||||
#endif
|
||||
#define ALLOCM_ZERO ((int)0x40)
|
||||
#define ALLOCM_NO_MOVE ((int)0x80)
|
||||
/* Bias arena index bits so that 0 encodes "ALLOCM_ARENA() unspecified". */
|
||||
#define ALLOCM_ARENA(a) ((int)(((a)+1) << 8))
|
||||
|
||||
#define ALLOCM_SUCCESS 0
|
||||
#define ALLOCM_ERR_OOM 1
|
||||
@@ -59,7 +61,8 @@ JEMALLOC_EXPORT void * je_memalign(size_t alignment, size_t size)
|
||||
JEMALLOC_EXPORT void * je_valloc(size_t size) JEMALLOC_ATTR(malloc);
|
||||
#endif
|
||||
|
||||
JEMALLOC_EXPORT size_t je_malloc_usable_size(const void *ptr);
|
||||
JEMALLOC_EXPORT size_t je_malloc_usable_size(
|
||||
JEMALLOC_USABLE_SIZE_CONST void *ptr);
|
||||
JEMALLOC_EXPORT void je_malloc_stats_print(void (*write_cb)(void *,
|
||||
const char *), void *je_cbopaque, const char *opts);
|
||||
JEMALLOC_EXPORT int je_mallctl(const char *name, void *oldp,
|
||||
|
||||
@@ -221,6 +221,15 @@
|
||||
#undef JEMALLOC_OVERRIDE_MEMALIGN
|
||||
#undef JEMALLOC_OVERRIDE_VALLOC
|
||||
|
||||
/*
|
||||
* At least Linux omits the "const" in:
|
||||
*
|
||||
* size_t malloc_usable_size(const void *ptr);
|
||||
*
|
||||
* Match the operating system's prototype.
|
||||
*/
|
||||
#undef JEMALLOC_USABLE_SIZE_CONST
|
||||
|
||||
/*
|
||||
* Darwin (OS X) uses zones to work around Mach-O symbol override shortcomings.
|
||||
*/
|
||||
@@ -240,6 +249,11 @@
|
||||
#undef JEMALLOC_PURGE_MADVISE_DONTNEED
|
||||
#undef JEMALLOC_PURGE_MADVISE_FREE
|
||||
|
||||
/*
|
||||
* Define if operating system has alloca.h header.
|
||||
*/
|
||||
#undef JEMALLOC_HAS_ALLOCA_H
|
||||
|
||||
/* sizeof(void *) == 2^LG_SIZEOF_PTR. */
|
||||
#undef LG_SIZEOF_PTR
|
||||
|
||||
|
||||
776
src/arena.c
776
src/arena.c
File diff suppressed because it is too large
Load Diff
@@ -32,7 +32,8 @@ base_pages_alloc(size_t minsize)
|
||||
assert(minsize != 0);
|
||||
csize = CHUNK_CEILING(minsize);
|
||||
zero = false;
|
||||
base_pages = chunk_alloc(csize, chunksize, true, &zero);
|
||||
base_pages = chunk_alloc(csize, chunksize, true, &zero,
|
||||
chunk_dss_prec_get());
|
||||
if (base_pages == NULL)
|
||||
return (true);
|
||||
base_next_addr = base_pages;
|
||||
|
||||
180
src/chunk.c
180
src/chunk.c
@@ -4,7 +4,8 @@
|
||||
/******************************************************************************/
|
||||
/* Data. */
|
||||
|
||||
size_t opt_lg_chunk = LG_CHUNK_DEFAULT;
|
||||
const char *opt_dss = DSS_DEFAULT;
|
||||
size_t opt_lg_chunk = LG_CHUNK_DEFAULT;
|
||||
|
||||
malloc_mutex_t chunks_mtx;
|
||||
chunk_stats_t stats_chunks;
|
||||
@@ -15,8 +16,10 @@ chunk_stats_t stats_chunks;
|
||||
* address space. Depending on function, different tree orderings are needed,
|
||||
* which is why there are two trees with the same contents.
|
||||
*/
|
||||
static extent_tree_t chunks_szad;
|
||||
static extent_tree_t chunks_ad;
|
||||
static extent_tree_t chunks_szad_mmap;
|
||||
static extent_tree_t chunks_ad_mmap;
|
||||
static extent_tree_t chunks_szad_dss;
|
||||
static extent_tree_t chunks_ad_dss;
|
||||
|
||||
rtree_t *chunks_rtree;
|
||||
|
||||
@@ -30,19 +33,23 @@ size_t arena_maxclass; /* Max size class for arenas. */
|
||||
/******************************************************************************/
|
||||
/* Function prototypes for non-inline static functions. */
|
||||
|
||||
static void *chunk_recycle(size_t size, size_t alignment, bool base,
|
||||
static void *chunk_recycle(extent_tree_t *chunks_szad,
|
||||
extent_tree_t *chunks_ad, size_t size, size_t alignment, bool base,
|
||||
bool *zero);
|
||||
static void chunk_record(void *chunk, size_t size);
|
||||
static void chunk_record(extent_tree_t *chunks_szad,
|
||||
extent_tree_t *chunks_ad, void *chunk, size_t size);
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
static void *
|
||||
chunk_recycle(size_t size, size_t alignment, bool base, bool *zero)
|
||||
chunk_recycle(extent_tree_t *chunks_szad, extent_tree_t *chunks_ad, size_t size,
|
||||
size_t alignment, bool base, bool *zero)
|
||||
{
|
||||
void *ret;
|
||||
extent_node_t *node;
|
||||
extent_node_t key;
|
||||
size_t alloc_size, leadsize, trailsize;
|
||||
bool zeroed;
|
||||
|
||||
if (base) {
|
||||
/*
|
||||
@@ -61,7 +68,7 @@ chunk_recycle(size_t size, size_t alignment, bool base, bool *zero)
|
||||
key.addr = NULL;
|
||||
key.size = alloc_size;
|
||||
malloc_mutex_lock(&chunks_mtx);
|
||||
node = extent_tree_szad_nsearch(&chunks_szad, &key);
|
||||
node = extent_tree_szad_nsearch(chunks_szad, &key);
|
||||
if (node == NULL) {
|
||||
malloc_mutex_unlock(&chunks_mtx);
|
||||
return (NULL);
|
||||
@@ -71,14 +78,17 @@ chunk_recycle(size_t size, size_t alignment, bool base, bool *zero)
|
||||
assert(node->size >= leadsize + size);
|
||||
trailsize = node->size - leadsize - size;
|
||||
ret = (void *)((uintptr_t)node->addr + leadsize);
|
||||
zeroed = node->zeroed;
|
||||
if (zeroed)
|
||||
*zero = true;
|
||||
/* Remove node from the tree. */
|
||||
extent_tree_szad_remove(&chunks_szad, node);
|
||||
extent_tree_ad_remove(&chunks_ad, node);
|
||||
extent_tree_szad_remove(chunks_szad, node);
|
||||
extent_tree_ad_remove(chunks_ad, node);
|
||||
if (leadsize != 0) {
|
||||
/* Insert the leading space as a smaller chunk. */
|
||||
node->size = leadsize;
|
||||
extent_tree_szad_insert(&chunks_szad, node);
|
||||
extent_tree_ad_insert(&chunks_ad, node);
|
||||
extent_tree_szad_insert(chunks_szad, node);
|
||||
extent_tree_ad_insert(chunks_ad, node);
|
||||
node = NULL;
|
||||
}
|
||||
if (trailsize != 0) {
|
||||
@@ -101,23 +111,28 @@ chunk_recycle(size_t size, size_t alignment, bool base, bool *zero)
|
||||
}
|
||||
node->addr = (void *)((uintptr_t)(ret) + size);
|
||||
node->size = trailsize;
|
||||
extent_tree_szad_insert(&chunks_szad, node);
|
||||
extent_tree_ad_insert(&chunks_ad, node);
|
||||
extent_tree_szad_insert(chunks_szad, node);
|
||||
extent_tree_ad_insert(chunks_ad, node);
|
||||
node = NULL;
|
||||
}
|
||||
malloc_mutex_unlock(&chunks_mtx);
|
||||
|
||||
if (node != NULL)
|
||||
base_node_dealloc(node);
|
||||
#ifdef JEMALLOC_PURGE_MADVISE_DONTNEED
|
||||
/* Pages are zeroed as a side effect of pages_purge(). */
|
||||
*zero = true;
|
||||
#else
|
||||
VALGRIND_MAKE_MEM_UNDEFINED(ret, size);
|
||||
if (*zero) {
|
||||
VALGRIND_MAKE_MEM_UNDEFINED(ret, size);
|
||||
memset(ret, 0, size);
|
||||
if (zeroed == false)
|
||||
memset(ret, 0, size);
|
||||
else if (config_debug) {
|
||||
size_t i;
|
||||
size_t *p = (size_t *)(uintptr_t)ret;
|
||||
|
||||
VALGRIND_MAKE_MEM_DEFINED(ret, size);
|
||||
for (i = 0; i < size / sizeof(size_t); i++)
|
||||
assert(p[i] == 0);
|
||||
VALGRIND_MAKE_MEM_UNDEFINED(ret, size);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return (ret);
|
||||
}
|
||||
|
||||
@@ -128,7 +143,8 @@ chunk_recycle(size_t size, size_t alignment, bool base, bool *zero)
|
||||
* advantage of them if they are returned.
|
||||
*/
|
||||
void *
|
||||
chunk_alloc(size_t size, size_t alignment, bool base, bool *zero)
|
||||
chunk_alloc(size_t size, size_t alignment, bool base, bool *zero,
|
||||
dss_prec_t dss_prec)
|
||||
{
|
||||
void *ret;
|
||||
|
||||
@@ -137,17 +153,26 @@ chunk_alloc(size_t size, size_t alignment, bool base, bool *zero)
|
||||
assert(alignment != 0);
|
||||
assert((alignment & chunksize_mask) == 0);
|
||||
|
||||
ret = chunk_recycle(size, alignment, base, zero);
|
||||
if (ret != NULL)
|
||||
/* "primary" dss. */
|
||||
if (config_dss && dss_prec == dss_prec_primary) {
|
||||
if ((ret = chunk_recycle(&chunks_szad_dss, &chunks_ad_dss, size,
|
||||
alignment, base, zero)) != NULL)
|
||||
goto label_return;
|
||||
if ((ret = chunk_alloc_dss(size, alignment, zero)) != NULL)
|
||||
goto label_return;
|
||||
}
|
||||
/* mmap. */
|
||||
if ((ret = chunk_recycle(&chunks_szad_mmap, &chunks_ad_mmap, size,
|
||||
alignment, base, zero)) != NULL)
|
||||
goto label_return;
|
||||
|
||||
ret = chunk_alloc_mmap(size, alignment, zero);
|
||||
if (ret != NULL)
|
||||
if ((ret = chunk_alloc_mmap(size, alignment, zero)) != NULL)
|
||||
goto label_return;
|
||||
|
||||
if (config_dss) {
|
||||
ret = chunk_alloc_dss(size, alignment, zero);
|
||||
if (ret != NULL)
|
||||
/* "secondary" dss. */
|
||||
if (config_dss && dss_prec == dss_prec_secondary) {
|
||||
if ((ret = chunk_recycle(&chunks_szad_dss, &chunks_ad_dss, size,
|
||||
alignment, base, zero)) != NULL)
|
||||
goto label_return;
|
||||
if ((ret = chunk_alloc_dss(size, alignment, zero)) != NULL)
|
||||
goto label_return;
|
||||
}
|
||||
|
||||
@@ -176,24 +201,18 @@ label_return:
|
||||
if (config_prof && opt_prof && opt_prof_gdump && gdump)
|
||||
prof_gdump();
|
||||
}
|
||||
if (config_debug && *zero && ret != NULL) {
|
||||
size_t i;
|
||||
size_t *p = (size_t *)(uintptr_t)ret;
|
||||
|
||||
VALGRIND_MAKE_MEM_DEFINED(ret, size);
|
||||
for (i = 0; i < size / sizeof(size_t); i++)
|
||||
assert(p[i] == 0);
|
||||
}
|
||||
assert(CHUNK_ADDR2BASE(ret) == ret);
|
||||
return (ret);
|
||||
}
|
||||
|
||||
static void
|
||||
chunk_record(void *chunk, size_t size)
|
||||
chunk_record(extent_tree_t *chunks_szad, extent_tree_t *chunks_ad, void *chunk,
|
||||
size_t size)
|
||||
{
|
||||
bool unzeroed;
|
||||
extent_node_t *xnode, *node, *prev, key;
|
||||
|
||||
pages_purge(chunk, size);
|
||||
unzeroed = pages_purge(chunk, size);
|
||||
|
||||
/*
|
||||
* Allocate a node before acquiring chunks_mtx even though it might not
|
||||
@@ -205,7 +224,7 @@ chunk_record(void *chunk, size_t size)
|
||||
|
||||
malloc_mutex_lock(&chunks_mtx);
|
||||
key.addr = (void *)((uintptr_t)chunk + size);
|
||||
node = extent_tree_ad_nsearch(&chunks_ad, &key);
|
||||
node = extent_tree_ad_nsearch(chunks_ad, &key);
|
||||
/* Try to coalesce forward. */
|
||||
if (node != NULL && node->addr == key.addr) {
|
||||
/*
|
||||
@@ -213,10 +232,11 @@ chunk_record(void *chunk, size_t size)
|
||||
* not change the position within chunks_ad, so only
|
||||
* remove/insert from/into chunks_szad.
|
||||
*/
|
||||
extent_tree_szad_remove(&chunks_szad, node);
|
||||
extent_tree_szad_remove(chunks_szad, node);
|
||||
node->addr = chunk;
|
||||
node->size += size;
|
||||
extent_tree_szad_insert(&chunks_szad, node);
|
||||
node->zeroed = (node->zeroed && (unzeroed == false));
|
||||
extent_tree_szad_insert(chunks_szad, node);
|
||||
if (xnode != NULL)
|
||||
base_node_dealloc(xnode);
|
||||
} else {
|
||||
@@ -234,12 +254,13 @@ chunk_record(void *chunk, size_t size)
|
||||
node = xnode;
|
||||
node->addr = chunk;
|
||||
node->size = size;
|
||||
extent_tree_ad_insert(&chunks_ad, node);
|
||||
extent_tree_szad_insert(&chunks_szad, node);
|
||||
node->zeroed = (unzeroed == false);
|
||||
extent_tree_ad_insert(chunks_ad, node);
|
||||
extent_tree_szad_insert(chunks_szad, node);
|
||||
}
|
||||
|
||||
/* Try to coalesce backward. */
|
||||
prev = extent_tree_ad_prev(&chunks_ad, node);
|
||||
prev = extent_tree_ad_prev(chunks_ad, node);
|
||||
if (prev != NULL && (void *)((uintptr_t)prev->addr + prev->size) ==
|
||||
chunk) {
|
||||
/*
|
||||
@@ -247,19 +268,34 @@ chunk_record(void *chunk, size_t size)
|
||||
* not change the position within chunks_ad, so only
|
||||
* remove/insert node from/into chunks_szad.
|
||||
*/
|
||||
extent_tree_szad_remove(&chunks_szad, prev);
|
||||
extent_tree_ad_remove(&chunks_ad, prev);
|
||||
extent_tree_szad_remove(chunks_szad, prev);
|
||||
extent_tree_ad_remove(chunks_ad, prev);
|
||||
|
||||
extent_tree_szad_remove(&chunks_szad, node);
|
||||
extent_tree_szad_remove(chunks_szad, node);
|
||||
node->addr = prev->addr;
|
||||
node->size += prev->size;
|
||||
extent_tree_szad_insert(&chunks_szad, node);
|
||||
node->zeroed = (node->zeroed && prev->zeroed);
|
||||
extent_tree_szad_insert(chunks_szad, node);
|
||||
|
||||
base_node_dealloc(prev);
|
||||
}
|
||||
malloc_mutex_unlock(&chunks_mtx);
|
||||
}
|
||||
|
||||
void
|
||||
chunk_unmap(void *chunk, size_t size)
|
||||
{
|
||||
assert(chunk != NULL);
|
||||
assert(CHUNK_ADDR2BASE(chunk) == chunk);
|
||||
assert(size != 0);
|
||||
assert((size & chunksize_mask) == 0);
|
||||
|
||||
if (config_dss && chunk_in_dss(chunk))
|
||||
chunk_record(&chunks_szad_dss, &chunks_ad_dss, chunk, size);
|
||||
else if (chunk_dealloc_mmap(chunk, size))
|
||||
chunk_record(&chunks_szad_mmap, &chunks_ad_mmap, chunk, size);
|
||||
}
|
||||
|
||||
void
|
||||
chunk_dealloc(void *chunk, size_t size, bool unmap)
|
||||
{
|
||||
@@ -273,15 +309,13 @@ chunk_dealloc(void *chunk, size_t size, bool unmap)
|
||||
rtree_set(chunks_rtree, (uintptr_t)chunk, NULL);
|
||||
if (config_stats || config_prof) {
|
||||
malloc_mutex_lock(&chunks_mtx);
|
||||
assert(stats_chunks.curchunks >= (size / chunksize));
|
||||
stats_chunks.curchunks -= (size / chunksize);
|
||||
malloc_mutex_unlock(&chunks_mtx);
|
||||
}
|
||||
|
||||
if (unmap) {
|
||||
if ((config_dss && chunk_in_dss(chunk)) ||
|
||||
chunk_dealloc_mmap(chunk, size))
|
||||
chunk_record(chunk, size);
|
||||
}
|
||||
if (unmap)
|
||||
chunk_unmap(chunk, size);
|
||||
}
|
||||
|
||||
bool
|
||||
@@ -301,8 +335,10 @@ chunk_boot(void)
|
||||
}
|
||||
if (config_dss && chunk_dss_boot())
|
||||
return (true);
|
||||
extent_tree_szad_new(&chunks_szad);
|
||||
extent_tree_ad_new(&chunks_ad);
|
||||
extent_tree_szad_new(&chunks_szad_mmap);
|
||||
extent_tree_ad_new(&chunks_ad_mmap);
|
||||
extent_tree_szad_new(&chunks_szad_dss);
|
||||
extent_tree_ad_new(&chunks_ad_dss);
|
||||
if (config_ivsalloc) {
|
||||
chunks_rtree = rtree_new((ZU(1) << (LG_SIZEOF_PTR+3)) -
|
||||
opt_lg_chunk);
|
||||
@@ -312,3 +348,33 @@ chunk_boot(void)
|
||||
|
||||
return (false);
|
||||
}
|
||||
|
||||
void
|
||||
chunk_prefork(void)
|
||||
{
|
||||
|
||||
malloc_mutex_lock(&chunks_mtx);
|
||||
if (config_ivsalloc)
|
||||
rtree_prefork(chunks_rtree);
|
||||
chunk_dss_prefork();
|
||||
}
|
||||
|
||||
void
|
||||
chunk_postfork_parent(void)
|
||||
{
|
||||
|
||||
chunk_dss_postfork_parent();
|
||||
if (config_ivsalloc)
|
||||
rtree_postfork_parent(chunks_rtree);
|
||||
malloc_mutex_postfork_parent(&chunks_mtx);
|
||||
}
|
||||
|
||||
void
|
||||
chunk_postfork_child(void)
|
||||
{
|
||||
|
||||
chunk_dss_postfork_child();
|
||||
if (config_ivsalloc)
|
||||
rtree_postfork_child(chunks_rtree);
|
||||
malloc_mutex_postfork_child(&chunks_mtx);
|
||||
}
|
||||
|
||||
@@ -3,6 +3,16 @@
|
||||
/******************************************************************************/
|
||||
/* Data. */
|
||||
|
||||
const char *dss_prec_names[] = {
|
||||
"disabled",
|
||||
"primary",
|
||||
"secondary",
|
||||
"N/A"
|
||||
};
|
||||
|
||||
/* Current dss precedence default, used when creating new arenas. */
|
||||
static dss_prec_t dss_prec_default = DSS_PREC_DEFAULT;
|
||||
|
||||
/*
|
||||
* Protects sbrk() calls. This avoids malloc races among threads, though it
|
||||
* does not protect against races with threads that call sbrk() directly.
|
||||
@@ -29,6 +39,31 @@ sbrk(intptr_t increment)
|
||||
}
|
||||
#endif
|
||||
|
||||
dss_prec_t
|
||||
chunk_dss_prec_get(void)
|
||||
{
|
||||
dss_prec_t ret;
|
||||
|
||||
if (config_dss == false)
|
||||
return (dss_prec_disabled);
|
||||
malloc_mutex_lock(&dss_mtx);
|
||||
ret = dss_prec_default;
|
||||
malloc_mutex_unlock(&dss_mtx);
|
||||
return (ret);
|
||||
}
|
||||
|
||||
bool
|
||||
chunk_dss_prec_set(dss_prec_t dss_prec)
|
||||
{
|
||||
|
||||
if (config_dss == false)
|
||||
return (true);
|
||||
malloc_mutex_lock(&dss_mtx);
|
||||
dss_prec_default = dss_prec;
|
||||
malloc_mutex_unlock(&dss_mtx);
|
||||
return (false);
|
||||
}
|
||||
|
||||
void *
|
||||
chunk_alloc_dss(size_t size, size_t alignment, bool *zero)
|
||||
{
|
||||
@@ -88,10 +123,11 @@ chunk_alloc_dss(size_t size, size_t alignment, bool *zero)
|
||||
dss_max = dss_next;
|
||||
malloc_mutex_unlock(&dss_mtx);
|
||||
if (cpad_size != 0)
|
||||
chunk_dealloc(cpad, cpad_size, true);
|
||||
chunk_unmap(cpad, cpad_size);
|
||||
if (*zero) {
|
||||
VALGRIND_MAKE_MEM_UNDEFINED(ret, size);
|
||||
memset(ret, 0, size);
|
||||
VALGRIND_MAKE_MEM_UNDEFINED(ret, size);
|
||||
}
|
||||
return (ret);
|
||||
}
|
||||
|
||||
@@ -113,22 +113,30 @@ pages_trim(void *addr, size_t alloc_size, size_t leadsize, size_t size)
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
bool
|
||||
pages_purge(void *addr, size_t length)
|
||||
{
|
||||
bool unzeroed;
|
||||
|
||||
#ifdef _WIN32
|
||||
VirtualAlloc(addr, length, MEM_RESET, PAGE_READWRITE);
|
||||
unzeroed = true;
|
||||
#else
|
||||
# ifdef JEMALLOC_PURGE_MADVISE_DONTNEED
|
||||
# define JEMALLOC_MADV_PURGE MADV_DONTNEED
|
||||
# define JEMALLOC_MADV_ZEROS true
|
||||
# elif defined(JEMALLOC_PURGE_MADVISE_FREE)
|
||||
# define JEMALLOC_MADV_PURGE MADV_FREE
|
||||
# define JEMALLOC_MADV_ZEROS false
|
||||
# else
|
||||
# error "No method defined for purging unused dirty pages."
|
||||
# endif
|
||||
madvise(addr, length, JEMALLOC_MADV_PURGE);
|
||||
int err = madvise(addr, length, JEMALLOC_MADV_PURGE);
|
||||
unzeroed = (JEMALLOC_MADV_ZEROS == false || err != 0);
|
||||
# undef JEMALLOC_MADV_PURGE
|
||||
# undef JEMALLOC_MADV_ZEROS
|
||||
#endif
|
||||
return (unzeroed);
|
||||
}
|
||||
|
||||
static void *
|
||||
|
||||
86
src/ckh.c
86
src/ckh.c
@@ -70,20 +70,20 @@ ckh_bucket_search(ckh_t *ckh, size_t bucket, const void *key)
|
||||
JEMALLOC_INLINE size_t
|
||||
ckh_isearch(ckh_t *ckh, const void *key)
|
||||
{
|
||||
size_t hash1, hash2, bucket, cell;
|
||||
size_t hashes[2], bucket, cell;
|
||||
|
||||
assert(ckh != NULL);
|
||||
|
||||
ckh->hash(key, ckh->lg_curbuckets, &hash1, &hash2);
|
||||
ckh->hash(key, hashes);
|
||||
|
||||
/* Search primary bucket. */
|
||||
bucket = hash1 & ((ZU(1) << ckh->lg_curbuckets) - 1);
|
||||
bucket = hashes[0] & ((ZU(1) << ckh->lg_curbuckets) - 1);
|
||||
cell = ckh_bucket_search(ckh, bucket, key);
|
||||
if (cell != SIZE_T_MAX)
|
||||
return (cell);
|
||||
|
||||
/* Search secondary bucket. */
|
||||
bucket = hash2 & ((ZU(1) << ckh->lg_curbuckets) - 1);
|
||||
bucket = hashes[1] & ((ZU(1) << ckh->lg_curbuckets) - 1);
|
||||
cell = ckh_bucket_search(ckh, bucket, key);
|
||||
return (cell);
|
||||
}
|
||||
@@ -126,7 +126,7 @@ ckh_evict_reloc_insert(ckh_t *ckh, size_t argbucket, void const **argkey,
|
||||
{
|
||||
const void *key, *data, *tkey, *tdata;
|
||||
ckhc_t *cell;
|
||||
size_t hash1, hash2, bucket, tbucket;
|
||||
size_t hashes[2], bucket, tbucket;
|
||||
unsigned i;
|
||||
|
||||
bucket = argbucket;
|
||||
@@ -155,10 +155,11 @@ ckh_evict_reloc_insert(ckh_t *ckh, size_t argbucket, void const **argkey,
|
||||
#endif
|
||||
|
||||
/* Find the alternate bucket for the evicted item. */
|
||||
ckh->hash(key, ckh->lg_curbuckets, &hash1, &hash2);
|
||||
tbucket = hash2 & ((ZU(1) << ckh->lg_curbuckets) - 1);
|
||||
ckh->hash(key, hashes);
|
||||
tbucket = hashes[1] & ((ZU(1) << ckh->lg_curbuckets) - 1);
|
||||
if (tbucket == bucket) {
|
||||
tbucket = hash1 & ((ZU(1) << ckh->lg_curbuckets) - 1);
|
||||
tbucket = hashes[0] & ((ZU(1) << ckh->lg_curbuckets)
|
||||
- 1);
|
||||
/*
|
||||
* It may be that (tbucket == bucket) still, if the
|
||||
* item's hashes both indicate this bucket. However,
|
||||
@@ -192,19 +193,19 @@ ckh_evict_reloc_insert(ckh_t *ckh, size_t argbucket, void const **argkey,
|
||||
JEMALLOC_INLINE bool
|
||||
ckh_try_insert(ckh_t *ckh, void const**argkey, void const**argdata)
|
||||
{
|
||||
size_t hash1, hash2, bucket;
|
||||
size_t hashes[2], bucket;
|
||||
const void *key = *argkey;
|
||||
const void *data = *argdata;
|
||||
|
||||
ckh->hash(key, ckh->lg_curbuckets, &hash1, &hash2);
|
||||
ckh->hash(key, hashes);
|
||||
|
||||
/* Try to insert in primary bucket. */
|
||||
bucket = hash1 & ((ZU(1) << ckh->lg_curbuckets) - 1);
|
||||
bucket = hashes[0] & ((ZU(1) << ckh->lg_curbuckets) - 1);
|
||||
if (ckh_try_bucket_insert(ckh, bucket, key, data) == false)
|
||||
return (false);
|
||||
|
||||
/* Try to insert in secondary bucket. */
|
||||
bucket = hash2 & ((ZU(1) << ckh->lg_curbuckets) - 1);
|
||||
bucket = hashes[1] & ((ZU(1) << ckh->lg_curbuckets) - 1);
|
||||
if (ckh_try_bucket_insert(ckh, bucket, key, data) == false)
|
||||
return (false);
|
||||
|
||||
@@ -417,9 +418,8 @@ ckh_delete(ckh_t *ckh)
|
||||
#endif
|
||||
|
||||
idalloc(ckh->tab);
|
||||
#ifdef JEMALLOC_DEBUG
|
||||
memset(ckh, 0x5a, sizeof(ckh_t));
|
||||
#endif
|
||||
if (config_debug)
|
||||
memset(ckh, 0x5a, sizeof(ckh_t));
|
||||
}
|
||||
|
||||
size_t
|
||||
@@ -526,31 +526,10 @@ ckh_search(ckh_t *ckh, const void *searchkey, void **key, void **data)
|
||||
}
|
||||
|
||||
void
|
||||
ckh_string_hash(const void *key, unsigned minbits, size_t *hash1, size_t *hash2)
|
||||
ckh_string_hash(const void *key, size_t r_hash[2])
|
||||
{
|
||||
size_t ret1, ret2;
|
||||
uint64_t h;
|
||||
|
||||
assert(minbits <= 32 || (SIZEOF_PTR == 8 && minbits <= 64));
|
||||
assert(hash1 != NULL);
|
||||
assert(hash2 != NULL);
|
||||
|
||||
h = hash(key, strlen((const char *)key), UINT64_C(0x94122f335b332aea));
|
||||
if (minbits <= 32) {
|
||||
/*
|
||||
* Avoid doing multiple hashes, since a single hash provides
|
||||
* enough bits.
|
||||
*/
|
||||
ret1 = h & ZU(0xffffffffU);
|
||||
ret2 = h >> 32;
|
||||
} else {
|
||||
ret1 = h;
|
||||
ret2 = hash(key, strlen((const char *)key),
|
||||
UINT64_C(0x8432a476666bbc13));
|
||||
}
|
||||
|
||||
*hash1 = ret1;
|
||||
*hash2 = ret2;
|
||||
hash(key, strlen((const char *)key), 0x94122f33U, r_hash);
|
||||
}
|
||||
|
||||
bool
|
||||
@@ -564,41 +543,16 @@ ckh_string_keycomp(const void *k1, const void *k2)
|
||||
}
|
||||
|
||||
void
|
||||
ckh_pointer_hash(const void *key, unsigned minbits, size_t *hash1,
|
||||
size_t *hash2)
|
||||
ckh_pointer_hash(const void *key, size_t r_hash[2])
|
||||
{
|
||||
size_t ret1, ret2;
|
||||
uint64_t h;
|
||||
union {
|
||||
const void *v;
|
||||
uint64_t i;
|
||||
size_t i;
|
||||
} u;
|
||||
|
||||
assert(minbits <= 32 || (SIZEOF_PTR == 8 && minbits <= 64));
|
||||
assert(hash1 != NULL);
|
||||
assert(hash2 != NULL);
|
||||
|
||||
assert(sizeof(u.v) == sizeof(u.i));
|
||||
#if (LG_SIZEOF_PTR != LG_SIZEOF_INT)
|
||||
u.i = 0;
|
||||
#endif
|
||||
u.v = key;
|
||||
h = hash(&u.i, sizeof(u.i), UINT64_C(0xd983396e68886082));
|
||||
if (minbits <= 32) {
|
||||
/*
|
||||
* Avoid doing multiple hashes, since a single hash provides
|
||||
* enough bits.
|
||||
*/
|
||||
ret1 = h & ZU(0xffffffffU);
|
||||
ret2 = h >> 32;
|
||||
} else {
|
||||
assert(SIZEOF_PTR == 8);
|
||||
ret1 = h;
|
||||
ret2 = hash(&u.i, sizeof(u.i), UINT64_C(0x5e2be9aff8709a5d));
|
||||
}
|
||||
|
||||
*hash1 = ret1;
|
||||
*hash2 = ret2;
|
||||
hash(&u.i, sizeof(u.i), 0xd983396eU, r_hash);
|
||||
}
|
||||
|
||||
bool
|
||||
|
||||
383
src/ctl.c
383
src/ctl.c
@@ -48,8 +48,8 @@ static int n##_ctl(const size_t *mib, size_t miblen, void *oldp, \
|
||||
size_t *oldlenp, void *newp, size_t newlen);
|
||||
|
||||
#define INDEX_PROTO(n) \
|
||||
const ctl_named_node_t *n##_index(const size_t *mib, size_t miblen, \
|
||||
size_t i);
|
||||
static const ctl_named_node_t *n##_index(const size_t *mib, \
|
||||
size_t miblen, size_t i);
|
||||
|
||||
static bool ctl_arena_init(ctl_arena_stats_t *astats);
|
||||
static void ctl_arena_clear(ctl_arena_stats_t *astats);
|
||||
@@ -58,6 +58,7 @@ static void ctl_arena_stats_amerge(ctl_arena_stats_t *cstats,
|
||||
static void ctl_arena_stats_smerge(ctl_arena_stats_t *sstats,
|
||||
ctl_arena_stats_t *astats);
|
||||
static void ctl_arena_refresh(arena_t *arena, unsigned i);
|
||||
static bool ctl_grow(void);
|
||||
static void ctl_refresh(void);
|
||||
static bool ctl_init(void);
|
||||
static int ctl_lookup(const char *name, ctl_node_t const **nodesp,
|
||||
@@ -88,6 +89,7 @@ CTL_PROTO(config_utrace)
|
||||
CTL_PROTO(config_valgrind)
|
||||
CTL_PROTO(config_xmalloc)
|
||||
CTL_PROTO(opt_abort)
|
||||
CTL_PROTO(opt_dss)
|
||||
CTL_PROTO(opt_lg_chunk)
|
||||
CTL_PROTO(opt_narenas)
|
||||
CTL_PROTO(opt_lg_dirty_mult)
|
||||
@@ -110,6 +112,10 @@ CTL_PROTO(opt_prof_gdump)
|
||||
CTL_PROTO(opt_prof_final)
|
||||
CTL_PROTO(opt_prof_leak)
|
||||
CTL_PROTO(opt_prof_accum)
|
||||
CTL_PROTO(arena_i_purge)
|
||||
static void arena_purge(unsigned arena_ind);
|
||||
CTL_PROTO(arena_i_dss)
|
||||
INDEX_PROTO(arena_i)
|
||||
CTL_PROTO(arenas_bin_i_size)
|
||||
CTL_PROTO(arenas_bin_i_nregs)
|
||||
CTL_PROTO(arenas_bin_i_run_size)
|
||||
@@ -125,6 +131,7 @@ CTL_PROTO(arenas_nbins)
|
||||
CTL_PROTO(arenas_nhbins)
|
||||
CTL_PROTO(arenas_nlruns)
|
||||
CTL_PROTO(arenas_purge)
|
||||
CTL_PROTO(arenas_extend)
|
||||
CTL_PROTO(prof_active)
|
||||
CTL_PROTO(prof_dump)
|
||||
CTL_PROTO(prof_interval)
|
||||
@@ -158,6 +165,7 @@ CTL_PROTO(stats_arenas_i_lruns_j_nrequests)
|
||||
CTL_PROTO(stats_arenas_i_lruns_j_curruns)
|
||||
INDEX_PROTO(stats_arenas_i_lruns_j)
|
||||
CTL_PROTO(stats_arenas_i_nthreads)
|
||||
CTL_PROTO(stats_arenas_i_dss)
|
||||
CTL_PROTO(stats_arenas_i_pactive)
|
||||
CTL_PROTO(stats_arenas_i_pdirty)
|
||||
CTL_PROTO(stats_arenas_i_mapped)
|
||||
@@ -223,6 +231,7 @@ static const ctl_named_node_t config_node[] = {
|
||||
|
||||
static const ctl_named_node_t opt_node[] = {
|
||||
{NAME("abort"), CTL(opt_abort)},
|
||||
{NAME("dss"), CTL(opt_dss)},
|
||||
{NAME("lg_chunk"), CTL(opt_lg_chunk)},
|
||||
{NAME("narenas"), CTL(opt_narenas)},
|
||||
{NAME("lg_dirty_mult"), CTL(opt_lg_dirty_mult)},
|
||||
@@ -247,6 +256,18 @@ static const ctl_named_node_t opt_node[] = {
|
||||
{NAME("prof_accum"), CTL(opt_prof_accum)}
|
||||
};
|
||||
|
||||
static const ctl_named_node_t arena_i_node[] = {
|
||||
{NAME("purge"), CTL(arena_i_purge)},
|
||||
{NAME("dss"), CTL(arena_i_dss)}
|
||||
};
|
||||
static const ctl_named_node_t super_arena_i_node[] = {
|
||||
{NAME(""), CHILD(named, arena_i)}
|
||||
};
|
||||
|
||||
static const ctl_indexed_node_t arena_node[] = {
|
||||
{INDEX(arena_i)}
|
||||
};
|
||||
|
||||
static const ctl_named_node_t arenas_bin_i_node[] = {
|
||||
{NAME("size"), CTL(arenas_bin_i_size)},
|
||||
{NAME("nregs"), CTL(arenas_bin_i_nregs)},
|
||||
@@ -282,7 +303,8 @@ static const ctl_named_node_t arenas_node[] = {
|
||||
{NAME("bin"), CHILD(indexed, arenas_bin)},
|
||||
{NAME("nlruns"), CTL(arenas_nlruns)},
|
||||
{NAME("lrun"), CHILD(indexed, arenas_lrun)},
|
||||
{NAME("purge"), CTL(arenas_purge)}
|
||||
{NAME("purge"), CTL(arenas_purge)},
|
||||
{NAME("extend"), CTL(arenas_extend)}
|
||||
};
|
||||
|
||||
static const ctl_named_node_t prof_node[] = {
|
||||
@@ -352,6 +374,7 @@ static const ctl_indexed_node_t stats_arenas_i_lruns_node[] = {
|
||||
|
||||
static const ctl_named_node_t stats_arenas_i_node[] = {
|
||||
{NAME("nthreads"), CTL(stats_arenas_i_nthreads)},
|
||||
{NAME("dss"), CTL(stats_arenas_i_dss)},
|
||||
{NAME("pactive"), CTL(stats_arenas_i_pactive)},
|
||||
{NAME("pdirty"), CTL(stats_arenas_i_pdirty)},
|
||||
{NAME("mapped"), CTL(stats_arenas_i_mapped)},
|
||||
@@ -387,6 +410,7 @@ static const ctl_named_node_t root_node[] = {
|
||||
{NAME("thread"), CHILD(named, thread)},
|
||||
{NAME("config"), CHILD(named, config)},
|
||||
{NAME("opt"), CHILD(named, opt)},
|
||||
{NAME("arena"), CHILD(indexed, arena)},
|
||||
{NAME("arenas"), CHILD(named, arenas)},
|
||||
{NAME("prof"), CHILD(named, prof)},
|
||||
{NAME("stats"), CHILD(named, stats)}
|
||||
@@ -420,6 +444,7 @@ static void
|
||||
ctl_arena_clear(ctl_arena_stats_t *astats)
|
||||
{
|
||||
|
||||
astats->dss = dss_prec_names[dss_prec_limit];
|
||||
astats->pactive = 0;
|
||||
astats->pdirty = 0;
|
||||
if (config_stats) {
|
||||
@@ -439,8 +464,8 @@ ctl_arena_stats_amerge(ctl_arena_stats_t *cstats, arena_t *arena)
|
||||
{
|
||||
unsigned i;
|
||||
|
||||
arena_stats_merge(arena, &cstats->pactive, &cstats->pdirty,
|
||||
&cstats->astats, cstats->bstats, cstats->lstats);
|
||||
arena_stats_merge(arena, &cstats->dss, &cstats->pactive,
|
||||
&cstats->pdirty, &cstats->astats, cstats->bstats, cstats->lstats);
|
||||
|
||||
for (i = 0; i < NBINS; i++) {
|
||||
cstats->allocated_small += cstats->bstats[i].allocated;
|
||||
@@ -500,7 +525,7 @@ static void
|
||||
ctl_arena_refresh(arena_t *arena, unsigned i)
|
||||
{
|
||||
ctl_arena_stats_t *astats = &ctl_stats.arenas[i];
|
||||
ctl_arena_stats_t *sstats = &ctl_stats.arenas[narenas];
|
||||
ctl_arena_stats_t *sstats = &ctl_stats.arenas[ctl_stats.narenas];
|
||||
|
||||
ctl_arena_clear(astats);
|
||||
|
||||
@@ -518,11 +543,72 @@ ctl_arena_refresh(arena_t *arena, unsigned i)
|
||||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
ctl_grow(void)
|
||||
{
|
||||
size_t astats_size;
|
||||
ctl_arena_stats_t *astats;
|
||||
arena_t **tarenas;
|
||||
|
||||
/* Extend arena stats and arenas arrays. */
|
||||
astats_size = (ctl_stats.narenas + 2) * sizeof(ctl_arena_stats_t);
|
||||
if (ctl_stats.narenas == narenas_auto) {
|
||||
/* ctl_stats.arenas and arenas came from base_alloc(). */
|
||||
astats = (ctl_arena_stats_t *)imalloc(astats_size);
|
||||
if (astats == NULL)
|
||||
return (true);
|
||||
memcpy(astats, ctl_stats.arenas, (ctl_stats.narenas + 1) *
|
||||
sizeof(ctl_arena_stats_t));
|
||||
|
||||
tarenas = (arena_t **)imalloc((ctl_stats.narenas + 1) *
|
||||
sizeof(arena_t *));
|
||||
if (tarenas == NULL) {
|
||||
idalloc(astats);
|
||||
return (true);
|
||||
}
|
||||
memcpy(tarenas, arenas, ctl_stats.narenas * sizeof(arena_t *));
|
||||
} else {
|
||||
astats = (ctl_arena_stats_t *)iralloc(ctl_stats.arenas,
|
||||
astats_size, 0, 0, false, false);
|
||||
if (astats == NULL)
|
||||
return (true);
|
||||
|
||||
tarenas = (arena_t **)iralloc(arenas, (ctl_stats.narenas + 1) *
|
||||
sizeof(arena_t *), 0, 0, false, false);
|
||||
if (tarenas == NULL)
|
||||
return (true);
|
||||
}
|
||||
/* Initialize the new astats and arenas elements. */
|
||||
memset(&astats[ctl_stats.narenas + 1], 0, sizeof(ctl_arena_stats_t));
|
||||
if (ctl_arena_init(&astats[ctl_stats.narenas + 1]))
|
||||
return (true);
|
||||
tarenas[ctl_stats.narenas] = NULL;
|
||||
/* Swap merged stats to their new location. */
|
||||
{
|
||||
ctl_arena_stats_t tstats;
|
||||
memcpy(&tstats, &astats[ctl_stats.narenas],
|
||||
sizeof(ctl_arena_stats_t));
|
||||
memcpy(&astats[ctl_stats.narenas],
|
||||
&astats[ctl_stats.narenas + 1], sizeof(ctl_arena_stats_t));
|
||||
memcpy(&astats[ctl_stats.narenas + 1], &tstats,
|
||||
sizeof(ctl_arena_stats_t));
|
||||
}
|
||||
ctl_stats.arenas = astats;
|
||||
ctl_stats.narenas++;
|
||||
malloc_mutex_lock(&arenas_lock);
|
||||
arenas = tarenas;
|
||||
narenas_total++;
|
||||
arenas_extend(narenas_total - 1);
|
||||
malloc_mutex_unlock(&arenas_lock);
|
||||
|
||||
return (false);
|
||||
}
|
||||
|
||||
static void
|
||||
ctl_refresh(void)
|
||||
{
|
||||
unsigned i;
|
||||
VARIABLE_ARRAY(arena_t *, tarenas, narenas);
|
||||
VARIABLE_ARRAY(arena_t *, tarenas, ctl_stats.narenas);
|
||||
|
||||
if (config_stats) {
|
||||
malloc_mutex_lock(&chunks_mtx);
|
||||
@@ -542,19 +628,19 @@ ctl_refresh(void)
|
||||
* Clear sum stats, since they will be merged into by
|
||||
* ctl_arena_refresh().
|
||||
*/
|
||||
ctl_stats.arenas[narenas].nthreads = 0;
|
||||
ctl_arena_clear(&ctl_stats.arenas[narenas]);
|
||||
ctl_stats.arenas[ctl_stats.narenas].nthreads = 0;
|
||||
ctl_arena_clear(&ctl_stats.arenas[ctl_stats.narenas]);
|
||||
|
||||
malloc_mutex_lock(&arenas_lock);
|
||||
memcpy(tarenas, arenas, sizeof(arena_t *) * narenas);
|
||||
for (i = 0; i < narenas; i++) {
|
||||
memcpy(tarenas, arenas, sizeof(arena_t *) * ctl_stats.narenas);
|
||||
for (i = 0; i < ctl_stats.narenas; i++) {
|
||||
if (arenas[i] != NULL)
|
||||
ctl_stats.arenas[i].nthreads = arenas[i]->nthreads;
|
||||
else
|
||||
ctl_stats.arenas[i].nthreads = 0;
|
||||
}
|
||||
malloc_mutex_unlock(&arenas_lock);
|
||||
for (i = 0; i < narenas; i++) {
|
||||
for (i = 0; i < ctl_stats.narenas; i++) {
|
||||
bool initialized = (tarenas[i] != NULL);
|
||||
|
||||
ctl_stats.arenas[i].initialized = initialized;
|
||||
@@ -563,11 +649,13 @@ ctl_refresh(void)
|
||||
}
|
||||
|
||||
if (config_stats) {
|
||||
ctl_stats.allocated = ctl_stats.arenas[narenas].allocated_small
|
||||
+ ctl_stats.arenas[narenas].astats.allocated_large
|
||||
ctl_stats.allocated =
|
||||
ctl_stats.arenas[ctl_stats.narenas].allocated_small
|
||||
+ ctl_stats.arenas[ctl_stats.narenas].astats.allocated_large
|
||||
+ ctl_stats.huge.allocated;
|
||||
ctl_stats.active =
|
||||
(ctl_stats.arenas[ctl_stats.narenas].pactive << LG_PAGE)
|
||||
+ ctl_stats.huge.allocated;
|
||||
ctl_stats.active = (ctl_stats.arenas[narenas].pactive <<
|
||||
LG_PAGE) + ctl_stats.huge.allocated;
|
||||
ctl_stats.mapped = (ctl_stats.chunks.current << opt_lg_chunk);
|
||||
}
|
||||
|
||||
@@ -585,13 +673,15 @@ ctl_init(void)
|
||||
* Allocate space for one extra arena stats element, which
|
||||
* contains summed stats across all arenas.
|
||||
*/
|
||||
assert(narenas_auto == narenas_total_get());
|
||||
ctl_stats.narenas = narenas_auto;
|
||||
ctl_stats.arenas = (ctl_arena_stats_t *)base_alloc(
|
||||
(narenas + 1) * sizeof(ctl_arena_stats_t));
|
||||
(ctl_stats.narenas + 1) * sizeof(ctl_arena_stats_t));
|
||||
if (ctl_stats.arenas == NULL) {
|
||||
ret = true;
|
||||
goto label_return;
|
||||
}
|
||||
memset(ctl_stats.arenas, 0, (narenas + 1) *
|
||||
memset(ctl_stats.arenas, 0, (ctl_stats.narenas + 1) *
|
||||
sizeof(ctl_arena_stats_t));
|
||||
|
||||
/*
|
||||
@@ -601,14 +691,14 @@ ctl_init(void)
|
||||
*/
|
||||
if (config_stats) {
|
||||
unsigned i;
|
||||
for (i = 0; i <= narenas; i++) {
|
||||
for (i = 0; i <= ctl_stats.narenas; i++) {
|
||||
if (ctl_arena_init(&ctl_stats.arenas[i])) {
|
||||
ret = true;
|
||||
goto label_return;
|
||||
}
|
||||
}
|
||||
}
|
||||
ctl_stats.arenas[narenas].initialized = true;
|
||||
ctl_stats.arenas[ctl_stats.narenas].initialized = true;
|
||||
|
||||
ctl_epoch = 0;
|
||||
ctl_refresh();
|
||||
@@ -827,6 +917,27 @@ ctl_boot(void)
|
||||
return (false);
|
||||
}
|
||||
|
||||
void
|
||||
ctl_prefork(void)
|
||||
{
|
||||
|
||||
malloc_mutex_lock(&ctl_mtx);
|
||||
}
|
||||
|
||||
void
|
||||
ctl_postfork_parent(void)
|
||||
{
|
||||
|
||||
malloc_mutex_postfork_parent(&ctl_mtx);
|
||||
}
|
||||
|
||||
void
|
||||
ctl_postfork_child(void)
|
||||
{
|
||||
|
||||
malloc_mutex_postfork_child(&ctl_mtx);
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
/* *_ctl() functions. */
|
||||
|
||||
@@ -849,11 +960,11 @@ ctl_boot(void)
|
||||
if (*oldlenp != sizeof(t)) { \
|
||||
size_t copylen = (sizeof(t) <= *oldlenp) \
|
||||
? sizeof(t) : *oldlenp; \
|
||||
memcpy(oldp, (void *)&v, copylen); \
|
||||
memcpy(oldp, (void *)&(v), copylen); \
|
||||
ret = EINVAL; \
|
||||
goto label_return; \
|
||||
} else \
|
||||
*(t *)oldp = v; \
|
||||
*(t *)oldp = (v); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
@@ -863,7 +974,7 @@ ctl_boot(void)
|
||||
ret = EINVAL; \
|
||||
goto label_return; \
|
||||
} \
|
||||
v = *(t *)newp; \
|
||||
(v) = *(t *)newp; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
@@ -884,7 +995,7 @@ n##_ctl(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp, \
|
||||
if (l) \
|
||||
malloc_mutex_lock(&ctl_mtx); \
|
||||
READONLY(); \
|
||||
oldval = v; \
|
||||
oldval = (v); \
|
||||
READ(oldval, t); \
|
||||
\
|
||||
ret = 0; \
|
||||
@@ -906,7 +1017,7 @@ n##_ctl(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp, \
|
||||
return (ENOENT); \
|
||||
malloc_mutex_lock(&ctl_mtx); \
|
||||
READONLY(); \
|
||||
oldval = v; \
|
||||
oldval = (v); \
|
||||
READ(oldval, t); \
|
||||
\
|
||||
ret = 0; \
|
||||
@@ -925,7 +1036,7 @@ n##_ctl(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp, \
|
||||
\
|
||||
malloc_mutex_lock(&ctl_mtx); \
|
||||
READONLY(); \
|
||||
oldval = v; \
|
||||
oldval = (v); \
|
||||
READ(oldval, t); \
|
||||
\
|
||||
ret = 0; \
|
||||
@@ -949,7 +1060,7 @@ n##_ctl(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp, \
|
||||
if ((c) == false) \
|
||||
return (ENOENT); \
|
||||
READONLY(); \
|
||||
oldval = v; \
|
||||
oldval = (v); \
|
||||
READ(oldval, t); \
|
||||
\
|
||||
ret = 0; \
|
||||
@@ -966,7 +1077,7 @@ n##_ctl(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp, \
|
||||
t oldval; \
|
||||
\
|
||||
READONLY(); \
|
||||
oldval = v; \
|
||||
oldval = (v); \
|
||||
READ(oldval, t); \
|
||||
\
|
||||
ret = 0; \
|
||||
@@ -1032,8 +1143,8 @@ thread_tcache_enabled_ctl(const size_t *mib, size_t miblen, void *oldp,
|
||||
}
|
||||
READ(oldval, bool);
|
||||
|
||||
label_return:
|
||||
ret = 0;
|
||||
label_return:
|
||||
return (ret);
|
||||
}
|
||||
|
||||
@@ -1063,13 +1174,14 @@ thread_arena_ctl(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp,
|
||||
int ret;
|
||||
unsigned newind, oldind;
|
||||
|
||||
malloc_mutex_lock(&ctl_mtx);
|
||||
newind = oldind = choose_arena(NULL)->ind;
|
||||
WRITE(newind, unsigned);
|
||||
READ(oldind, unsigned);
|
||||
if (newind != oldind) {
|
||||
arena_t *arena;
|
||||
|
||||
if (newind >= narenas) {
|
||||
if (newind >= ctl_stats.narenas) {
|
||||
/* New arena index is out of range. */
|
||||
ret = EFAULT;
|
||||
goto label_return;
|
||||
@@ -1102,6 +1214,7 @@ thread_arena_ctl(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp,
|
||||
|
||||
ret = 0;
|
||||
label_return:
|
||||
malloc_mutex_unlock(&ctl_mtx);
|
||||
return (ret);
|
||||
}
|
||||
|
||||
@@ -1135,6 +1248,7 @@ CTL_RO_BOOL_CONFIG_GEN(config_xmalloc)
|
||||
/******************************************************************************/
|
||||
|
||||
CTL_RO_NL_GEN(opt_abort, opt_abort, bool)
|
||||
CTL_RO_NL_GEN(opt_dss, opt_dss, const char *)
|
||||
CTL_RO_NL_GEN(opt_lg_chunk, opt_lg_chunk, size_t)
|
||||
CTL_RO_NL_GEN(opt_narenas, opt_narenas, size_t)
|
||||
CTL_RO_NL_GEN(opt_lg_dirty_mult, opt_lg_dirty_mult, ssize_t)
|
||||
@@ -1158,12 +1272,123 @@ CTL_RO_NL_CGEN(config_prof, opt_prof_final, opt_prof_final, bool)
|
||||
CTL_RO_NL_CGEN(config_prof, opt_prof_leak, opt_prof_leak, bool)
|
||||
CTL_RO_NL_CGEN(config_prof, opt_prof_accum, opt_prof_accum, bool)
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
/* ctl_mutex must be held during execution of this function. */
|
||||
static void
|
||||
arena_purge(unsigned arena_ind)
|
||||
{
|
||||
VARIABLE_ARRAY(arena_t *, tarenas, ctl_stats.narenas);
|
||||
|
||||
malloc_mutex_lock(&arenas_lock);
|
||||
memcpy(tarenas, arenas, sizeof(arena_t *) * ctl_stats.narenas);
|
||||
malloc_mutex_unlock(&arenas_lock);
|
||||
|
||||
if (arena_ind == ctl_stats.narenas) {
|
||||
unsigned i;
|
||||
for (i = 0; i < ctl_stats.narenas; i++) {
|
||||
if (tarenas[i] != NULL)
|
||||
arena_purge_all(tarenas[i]);
|
||||
}
|
||||
} else {
|
||||
assert(arena_ind < ctl_stats.narenas);
|
||||
if (tarenas[arena_ind] != NULL)
|
||||
arena_purge_all(tarenas[arena_ind]);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
arena_i_purge_ctl(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp,
|
||||
void *newp, size_t newlen)
|
||||
{
|
||||
int ret;
|
||||
|
||||
READONLY();
|
||||
WRITEONLY();
|
||||
malloc_mutex_lock(&ctl_mtx);
|
||||
arena_purge(mib[1]);
|
||||
malloc_mutex_unlock(&ctl_mtx);
|
||||
|
||||
ret = 0;
|
||||
label_return:
|
||||
return (ret);
|
||||
}
|
||||
|
||||
static int
|
||||
arena_i_dss_ctl(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp,
|
||||
void *newp, size_t newlen)
|
||||
{
|
||||
int ret, i;
|
||||
bool match, err;
|
||||
const char *dss;
|
||||
unsigned arena_ind = mib[1];
|
||||
dss_prec_t dss_prec_old = dss_prec_limit;
|
||||
dss_prec_t dss_prec = dss_prec_limit;
|
||||
|
||||
malloc_mutex_lock(&ctl_mtx);
|
||||
WRITE(dss, const char *);
|
||||
match = false;
|
||||
for (i = 0; i < dss_prec_limit; i++) {
|
||||
if (strcmp(dss_prec_names[i], dss) == 0) {
|
||||
dss_prec = i;
|
||||
match = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (match == false) {
|
||||
ret = EINVAL;
|
||||
goto label_return;
|
||||
}
|
||||
|
||||
if (arena_ind < ctl_stats.narenas) {
|
||||
arena_t *arena = arenas[arena_ind];
|
||||
if (arena != NULL) {
|
||||
dss_prec_old = arena_dss_prec_get(arena);
|
||||
arena_dss_prec_set(arena, dss_prec);
|
||||
err = false;
|
||||
} else
|
||||
err = true;
|
||||
} else {
|
||||
dss_prec_old = chunk_dss_prec_get();
|
||||
err = chunk_dss_prec_set(dss_prec);
|
||||
}
|
||||
dss = dss_prec_names[dss_prec_old];
|
||||
READ(dss, const char *);
|
||||
if (err) {
|
||||
ret = EFAULT;
|
||||
goto label_return;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
label_return:
|
||||
malloc_mutex_unlock(&ctl_mtx);
|
||||
return (ret);
|
||||
}
|
||||
|
||||
static const ctl_named_node_t *
|
||||
arena_i_index(const size_t *mib, size_t miblen, size_t i)
|
||||
{
|
||||
const ctl_named_node_t * ret;
|
||||
|
||||
malloc_mutex_lock(&ctl_mtx);
|
||||
if (i > ctl_stats.narenas) {
|
||||
ret = NULL;
|
||||
goto label_return;
|
||||
}
|
||||
|
||||
ret = super_arena_i_node;
|
||||
label_return:
|
||||
malloc_mutex_unlock(&ctl_mtx);
|
||||
return (ret);
|
||||
}
|
||||
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
CTL_RO_NL_GEN(arenas_bin_i_size, arena_bin_info[mib[2]].reg_size, size_t)
|
||||
CTL_RO_NL_GEN(arenas_bin_i_nregs, arena_bin_info[mib[2]].nregs, uint32_t)
|
||||
CTL_RO_NL_GEN(arenas_bin_i_run_size, arena_bin_info[mib[2]].run_size, size_t)
|
||||
const ctl_named_node_t *
|
||||
static const ctl_named_node_t *
|
||||
arenas_bin_i_index(const size_t *mib, size_t miblen, size_t i)
|
||||
{
|
||||
|
||||
@@ -1173,7 +1398,7 @@ arenas_bin_i_index(const size_t *mib, size_t miblen, size_t i)
|
||||
}
|
||||
|
||||
CTL_RO_NL_GEN(arenas_lrun_i_size, ((mib[2]+1) << LG_PAGE), size_t)
|
||||
const ctl_named_node_t *
|
||||
static const ctl_named_node_t *
|
||||
arenas_lrun_i_index(const size_t *mib, size_t miblen, size_t i)
|
||||
{
|
||||
|
||||
@@ -1182,7 +1407,27 @@ arenas_lrun_i_index(const size_t *mib, size_t miblen, size_t i)
|
||||
return (super_arenas_lrun_i_node);
|
||||
}
|
||||
|
||||
CTL_RO_NL_GEN(arenas_narenas, narenas, unsigned)
|
||||
static int
|
||||
arenas_narenas_ctl(const size_t *mib, size_t miblen, void *oldp,
|
||||
size_t *oldlenp, void *newp, size_t newlen)
|
||||
{
|
||||
int ret;
|
||||
unsigned narenas;
|
||||
|
||||
malloc_mutex_lock(&ctl_mtx);
|
||||
READONLY();
|
||||
if (*oldlenp != sizeof(unsigned)) {
|
||||
ret = EINVAL;
|
||||
goto label_return;
|
||||
}
|
||||
narenas = ctl_stats.narenas;
|
||||
READ(narenas, unsigned);
|
||||
|
||||
ret = 0;
|
||||
label_return:
|
||||
malloc_mutex_unlock(&ctl_mtx);
|
||||
return (ret);
|
||||
}
|
||||
|
||||
static int
|
||||
arenas_initialized_ctl(const size_t *mib, size_t miblen, void *oldp,
|
||||
@@ -1193,13 +1438,13 @@ arenas_initialized_ctl(const size_t *mib, size_t miblen, void *oldp,
|
||||
|
||||
malloc_mutex_lock(&ctl_mtx);
|
||||
READONLY();
|
||||
if (*oldlenp != narenas * sizeof(bool)) {
|
||||
if (*oldlenp != ctl_stats.narenas * sizeof(bool)) {
|
||||
ret = EINVAL;
|
||||
nread = (*oldlenp < narenas * sizeof(bool))
|
||||
? (*oldlenp / sizeof(bool)) : narenas;
|
||||
nread = (*oldlenp < ctl_stats.narenas * sizeof(bool))
|
||||
? (*oldlenp / sizeof(bool)) : ctl_stats.narenas;
|
||||
} else {
|
||||
ret = 0;
|
||||
nread = narenas;
|
||||
nread = ctl_stats.narenas;
|
||||
}
|
||||
|
||||
for (i = 0; i < nread; i++)
|
||||
@@ -1222,36 +1467,45 @@ arenas_purge_ctl(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp,
|
||||
void *newp, size_t newlen)
|
||||
{
|
||||
int ret;
|
||||
unsigned arena;
|
||||
unsigned arena_ind;
|
||||
|
||||
malloc_mutex_lock(&ctl_mtx);
|
||||
WRITEONLY();
|
||||
arena = UINT_MAX;
|
||||
WRITE(arena, unsigned);
|
||||
if (newp != NULL && arena >= narenas) {
|
||||
arena_ind = UINT_MAX;
|
||||
WRITE(arena_ind, unsigned);
|
||||
if (newp != NULL && arena_ind >= ctl_stats.narenas)
|
||||
ret = EFAULT;
|
||||
goto label_return;
|
||||
} else {
|
||||
VARIABLE_ARRAY(arena_t *, tarenas, narenas);
|
||||
|
||||
malloc_mutex_lock(&arenas_lock);
|
||||
memcpy(tarenas, arenas, sizeof(arena_t *) * narenas);
|
||||
malloc_mutex_unlock(&arenas_lock);
|
||||
|
||||
if (arena == UINT_MAX) {
|
||||
unsigned i;
|
||||
for (i = 0; i < narenas; i++) {
|
||||
if (tarenas[i] != NULL)
|
||||
arena_purge_all(tarenas[i]);
|
||||
}
|
||||
} else {
|
||||
assert(arena < narenas);
|
||||
if (tarenas[arena] != NULL)
|
||||
arena_purge_all(tarenas[arena]);
|
||||
}
|
||||
else {
|
||||
if (arena_ind == UINT_MAX)
|
||||
arena_ind = ctl_stats.narenas;
|
||||
arena_purge(arena_ind);
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
label_return:
|
||||
malloc_mutex_unlock(&ctl_mtx);
|
||||
return (ret);
|
||||
}
|
||||
|
||||
static int
|
||||
arenas_extend_ctl(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp,
|
||||
void *newp, size_t newlen)
|
||||
{
|
||||
int ret;
|
||||
unsigned narenas;
|
||||
|
||||
malloc_mutex_lock(&ctl_mtx);
|
||||
READONLY();
|
||||
if (ctl_grow()) {
|
||||
ret = EAGAIN;
|
||||
goto label_return;
|
||||
}
|
||||
narenas = ctl_stats.narenas - 1;
|
||||
READ(narenas, unsigned);
|
||||
|
||||
ret = 0;
|
||||
label_return:
|
||||
malloc_mutex_unlock(&ctl_mtx);
|
||||
return (ret);
|
||||
}
|
||||
|
||||
@@ -1356,7 +1610,7 @@ CTL_RO_CGEN(config_stats, stats_arenas_i_bins_j_nreruns,
|
||||
CTL_RO_CGEN(config_stats, stats_arenas_i_bins_j_curruns,
|
||||
ctl_stats.arenas[mib[2]].bstats[mib[4]].curruns, size_t)
|
||||
|
||||
const ctl_named_node_t *
|
||||
static const ctl_named_node_t *
|
||||
stats_arenas_i_bins_j_index(const size_t *mib, size_t miblen, size_t j)
|
||||
{
|
||||
|
||||
@@ -1374,7 +1628,7 @@ CTL_RO_CGEN(config_stats, stats_arenas_i_lruns_j_nrequests,
|
||||
CTL_RO_CGEN(config_stats, stats_arenas_i_lruns_j_curruns,
|
||||
ctl_stats.arenas[mib[2]].lstats[mib[4]].curruns, size_t)
|
||||
|
||||
const ctl_named_node_t *
|
||||
static const ctl_named_node_t *
|
||||
stats_arenas_i_lruns_j_index(const size_t *mib, size_t miblen, size_t j)
|
||||
{
|
||||
|
||||
@@ -1384,6 +1638,7 @@ stats_arenas_i_lruns_j_index(const size_t *mib, size_t miblen, size_t j)
|
||||
}
|
||||
|
||||
CTL_RO_GEN(stats_arenas_i_nthreads, ctl_stats.arenas[mib[2]].nthreads, unsigned)
|
||||
CTL_RO_GEN(stats_arenas_i_dss, ctl_stats.arenas[mib[2]].dss, const char *)
|
||||
CTL_RO_GEN(stats_arenas_i_pactive, ctl_stats.arenas[mib[2]].pactive, size_t)
|
||||
CTL_RO_GEN(stats_arenas_i_pdirty, ctl_stats.arenas[mib[2]].pdirty, size_t)
|
||||
CTL_RO_CGEN(config_stats, stats_arenas_i_mapped,
|
||||
@@ -1395,13 +1650,13 @@ CTL_RO_CGEN(config_stats, stats_arenas_i_nmadvise,
|
||||
CTL_RO_CGEN(config_stats, stats_arenas_i_purged,
|
||||
ctl_stats.arenas[mib[2]].astats.purged, uint64_t)
|
||||
|
||||
const ctl_named_node_t *
|
||||
static const ctl_named_node_t *
|
||||
stats_arenas_i_index(const size_t *mib, size_t miblen, size_t i)
|
||||
{
|
||||
const ctl_named_node_t * ret;
|
||||
|
||||
malloc_mutex_lock(&ctl_mtx);
|
||||
if (ctl_stats.arenas[i].initialized == false) {
|
||||
if (i > ctl_stats.narenas || ctl_stats.arenas[i].initialized == false) {
|
||||
ret = NULL;
|
||||
goto label_return;
|
||||
}
|
||||
|
||||
@@ -48,7 +48,8 @@ huge_palloc(size_t size, size_t alignment, bool zero)
|
||||
* it is possible to make correct junk/zero fill decisions below.
|
||||
*/
|
||||
is_zeroed = zero;
|
||||
ret = chunk_alloc(csize, alignment, false, &is_zeroed);
|
||||
ret = chunk_alloc(csize, alignment, false, &is_zeroed,
|
||||
chunk_dss_prec_get());
|
||||
if (ret == NULL) {
|
||||
base_node_dealloc(node);
|
||||
return (NULL);
|
||||
@@ -101,7 +102,7 @@ huge_ralloc_no_move(void *ptr, size_t oldsize, size_t size, size_t extra)
|
||||
|
||||
void *
|
||||
huge_ralloc(void *ptr, size_t oldsize, size_t size, size_t extra,
|
||||
size_t alignment, bool zero)
|
||||
size_t alignment, bool zero, bool try_tcache_dalloc)
|
||||
{
|
||||
void *ret;
|
||||
size_t copysize;
|
||||
@@ -180,7 +181,7 @@ huge_ralloc(void *ptr, size_t oldsize, size_t size, size_t extra,
|
||||
#endif
|
||||
{
|
||||
memcpy(ret, ptr, copysize);
|
||||
iqalloc(ptr);
|
||||
iqallocx(ptr, try_tcache_dalloc);
|
||||
}
|
||||
return (ret);
|
||||
}
|
||||
|
||||
281
src/jemalloc.c
281
src/jemalloc.c
@@ -10,17 +10,20 @@ malloc_tsd_data(, thread_allocated, thread_allocated_t,
|
||||
|
||||
/* Runtime configuration options. */
|
||||
const char *je_malloc_conf;
|
||||
bool opt_abort =
|
||||
#ifdef JEMALLOC_DEBUG
|
||||
bool opt_abort = true;
|
||||
# ifdef JEMALLOC_FILL
|
||||
bool opt_junk = true;
|
||||
# else
|
||||
bool opt_junk = false;
|
||||
# endif
|
||||
true
|
||||
#else
|
||||
bool opt_abort = false;
|
||||
bool opt_junk = false;
|
||||
false
|
||||
#endif
|
||||
;
|
||||
bool opt_junk =
|
||||
#if (defined(JEMALLOC_DEBUG) && defined(JEMALLOC_FILL))
|
||||
true
|
||||
#else
|
||||
false
|
||||
#endif
|
||||
;
|
||||
size_t opt_quarantine = ZU(0);
|
||||
bool opt_redzone = false;
|
||||
bool opt_utrace = false;
|
||||
@@ -33,7 +36,8 @@ unsigned ncpus;
|
||||
|
||||
malloc_mutex_t arenas_lock;
|
||||
arena_t **arenas;
|
||||
unsigned narenas;
|
||||
unsigned narenas_total;
|
||||
unsigned narenas_auto;
|
||||
|
||||
/* Set to true once the allocator has been initialized. */
|
||||
static bool malloc_initialized = false;
|
||||
@@ -82,11 +86,13 @@ typedef struct {
|
||||
#ifdef JEMALLOC_UTRACE
|
||||
# define UTRACE(a, b, c) do { \
|
||||
if (opt_utrace) { \
|
||||
int utrace_serrno = errno; \
|
||||
malloc_utrace_t ut; \
|
||||
ut.p = (a); \
|
||||
ut.s = (b); \
|
||||
ut.r = (c); \
|
||||
utrace(&ut, sizeof(ut)); \
|
||||
errno = utrace_serrno; \
|
||||
} \
|
||||
} while (0)
|
||||
#else
|
||||
@@ -144,14 +150,14 @@ choose_arena_hard(void)
|
||||
{
|
||||
arena_t *ret;
|
||||
|
||||
if (narenas > 1) {
|
||||
if (narenas_auto > 1) {
|
||||
unsigned i, choose, first_null;
|
||||
|
||||
choose = 0;
|
||||
first_null = narenas;
|
||||
first_null = narenas_auto;
|
||||
malloc_mutex_lock(&arenas_lock);
|
||||
assert(arenas[0] != NULL);
|
||||
for (i = 1; i < narenas; i++) {
|
||||
for (i = 1; i < narenas_auto; i++) {
|
||||
if (arenas[i] != NULL) {
|
||||
/*
|
||||
* Choose the first arena that has the lowest
|
||||
@@ -160,7 +166,7 @@ choose_arena_hard(void)
|
||||
if (arenas[i]->nthreads <
|
||||
arenas[choose]->nthreads)
|
||||
choose = i;
|
||||
} else if (first_null == narenas) {
|
||||
} else if (first_null == narenas_auto) {
|
||||
/*
|
||||
* Record the index of the first uninitialized
|
||||
* arena, in case all extant arenas are in use.
|
||||
@@ -174,7 +180,8 @@ choose_arena_hard(void)
|
||||
}
|
||||
}
|
||||
|
||||
if (arenas[choose]->nthreads == 0 || first_null == narenas) {
|
||||
if (arenas[choose]->nthreads == 0
|
||||
|| first_null == narenas_auto) {
|
||||
/*
|
||||
* Use an unloaded arena, or the least loaded arena if
|
||||
* all arenas are already initialized.
|
||||
@@ -203,7 +210,7 @@ stats_print_atexit(void)
|
||||
{
|
||||
|
||||
if (config_tcache && config_stats) {
|
||||
unsigned i;
|
||||
unsigned narenas, i;
|
||||
|
||||
/*
|
||||
* Merge stats from extant threads. This is racy, since
|
||||
@@ -212,7 +219,7 @@ stats_print_atexit(void)
|
||||
* out of date by the time they are reported, if other threads
|
||||
* continue to allocate.
|
||||
*/
|
||||
for (i = 0; i < narenas; i++) {
|
||||
for (i = 0, narenas = narenas_total_get(); i < narenas; i++) {
|
||||
arena_t *arena = arenas[i];
|
||||
if (arena != NULL) {
|
||||
tcache_t *tcache;
|
||||
@@ -254,12 +261,13 @@ malloc_ncpus(void)
|
||||
result = si.dwNumberOfProcessors;
|
||||
#else
|
||||
result = sysconf(_SC_NPROCESSORS_ONLN);
|
||||
#endif
|
||||
if (result == -1) {
|
||||
/* Error. */
|
||||
ret = 1;
|
||||
}
|
||||
#endif
|
||||
ret = (unsigned)result;
|
||||
} else {
|
||||
ret = (unsigned)result;
|
||||
}
|
||||
|
||||
return (ret);
|
||||
}
|
||||
@@ -274,7 +282,7 @@ arenas_cleanup(void *arg)
|
||||
malloc_mutex_unlock(&arenas_lock);
|
||||
}
|
||||
|
||||
static inline bool
|
||||
static JEMALLOC_ATTR(always_inline) bool
|
||||
malloc_init(void)
|
||||
{
|
||||
|
||||
@@ -377,6 +385,22 @@ malloc_conf_init(void)
|
||||
const char *opts, *k, *v;
|
||||
size_t klen, vlen;
|
||||
|
||||
/*
|
||||
* Automatically configure valgrind before processing options. The
|
||||
* valgrind option remains in jemalloc 3.x for compatibility reasons.
|
||||
*/
|
||||
if (config_valgrind) {
|
||||
opt_valgrind = (RUNNING_ON_VALGRIND != 0) ? true : false;
|
||||
if (config_fill && opt_valgrind) {
|
||||
opt_junk = false;
|
||||
assert(opt_zero == false);
|
||||
opt_quarantine = JEMALLOC_VALGRIND_QUARANTINE_DEFAULT;
|
||||
opt_redzone = true;
|
||||
}
|
||||
if (config_tcache && opt_valgrind)
|
||||
opt_tcache = false;
|
||||
}
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
/* Get runtime configuration. */
|
||||
switch (i) {
|
||||
@@ -450,7 +474,7 @@ malloc_conf_init(void)
|
||||
|
||||
while (*opts != '\0' && malloc_conf_next(&opts, &k, &klen, &v,
|
||||
&vlen) == false) {
|
||||
#define CONF_HANDLE_BOOL_HIT(o, n, hit) \
|
||||
#define CONF_HANDLE_BOOL(o, n) \
|
||||
if (sizeof(n)-1 == klen && strncmp(n, k, \
|
||||
klen) == 0) { \
|
||||
if (strncmp("true", v, vlen) == 0 && \
|
||||
@@ -464,16 +488,9 @@ malloc_conf_init(void)
|
||||
"Invalid conf value", \
|
||||
k, klen, v, vlen); \
|
||||
} \
|
||||
hit = true; \
|
||||
} else \
|
||||
hit = false;
|
||||
#define CONF_HANDLE_BOOL(o, n) { \
|
||||
bool hit; \
|
||||
CONF_HANDLE_BOOL_HIT(o, n, hit); \
|
||||
if (hit) \
|
||||
continue; \
|
||||
}
|
||||
#define CONF_HANDLE_SIZE_T(o, n, min, max) \
|
||||
}
|
||||
#define CONF_HANDLE_SIZE_T(o, n, min, max, clip) \
|
||||
if (sizeof(n)-1 == klen && strncmp(n, k, \
|
||||
klen) == 0) { \
|
||||
uintmax_t um; \
|
||||
@@ -486,12 +503,22 @@ malloc_conf_init(void)
|
||||
malloc_conf_error( \
|
||||
"Invalid conf value", \
|
||||
k, klen, v, vlen); \
|
||||
} else if (um < min || um > max) { \
|
||||
malloc_conf_error( \
|
||||
"Out-of-range conf value", \
|
||||
k, klen, v, vlen); \
|
||||
} else \
|
||||
o = um; \
|
||||
} else if (clip) { \
|
||||
if (um < min) \
|
||||
o = min; \
|
||||
else if (um > max) \
|
||||
o = max; \
|
||||
else \
|
||||
o = um; \
|
||||
} else { \
|
||||
if (um < min || um > max) { \
|
||||
malloc_conf_error( \
|
||||
"Out-of-range " \
|
||||
"conf value", \
|
||||
k, klen, v, vlen); \
|
||||
} else \
|
||||
o = um; \
|
||||
} \
|
||||
continue; \
|
||||
}
|
||||
#define CONF_HANDLE_SSIZE_T(o, n, min, max) \
|
||||
@@ -536,16 +563,41 @@ malloc_conf_init(void)
|
||||
* config_fill.
|
||||
*/
|
||||
CONF_HANDLE_SIZE_T(opt_lg_chunk, "lg_chunk", LG_PAGE +
|
||||
(config_fill ? 2 : 1), (sizeof(size_t) << 3) - 1)
|
||||
(config_fill ? 2 : 1), (sizeof(size_t) << 3) - 1,
|
||||
true)
|
||||
if (strncmp("dss", k, klen) == 0) {
|
||||
int i;
|
||||
bool match = false;
|
||||
for (i = 0; i < dss_prec_limit; i++) {
|
||||
if (strncmp(dss_prec_names[i], v, vlen)
|
||||
== 0) {
|
||||
if (chunk_dss_prec_set(i)) {
|
||||
malloc_conf_error(
|
||||
"Error setting dss",
|
||||
k, klen, v, vlen);
|
||||
} else {
|
||||
opt_dss =
|
||||
dss_prec_names[i];
|
||||
match = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (match == false) {
|
||||
malloc_conf_error("Invalid conf value",
|
||||
k, klen, v, vlen);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
CONF_HANDLE_SIZE_T(opt_narenas, "narenas", 1,
|
||||
SIZE_T_MAX)
|
||||
SIZE_T_MAX, false)
|
||||
CONF_HANDLE_SSIZE_T(opt_lg_dirty_mult, "lg_dirty_mult",
|
||||
-1, (sizeof(size_t) << 3) - 1)
|
||||
CONF_HANDLE_BOOL(opt_stats_print, "stats_print")
|
||||
if (config_fill) {
|
||||
CONF_HANDLE_BOOL(opt_junk, "junk")
|
||||
CONF_HANDLE_SIZE_T(opt_quarantine, "quarantine",
|
||||
0, SIZE_T_MAX)
|
||||
0, SIZE_T_MAX, false)
|
||||
CONF_HANDLE_BOOL(opt_redzone, "redzone")
|
||||
CONF_HANDLE_BOOL(opt_zero, "zero")
|
||||
}
|
||||
@@ -553,20 +605,7 @@ malloc_conf_init(void)
|
||||
CONF_HANDLE_BOOL(opt_utrace, "utrace")
|
||||
}
|
||||
if (config_valgrind) {
|
||||
bool hit;
|
||||
CONF_HANDLE_BOOL_HIT(opt_valgrind,
|
||||
"valgrind", hit)
|
||||
if (config_fill && opt_valgrind && hit) {
|
||||
opt_junk = false;
|
||||
opt_zero = false;
|
||||
if (opt_quarantine == 0) {
|
||||
opt_quarantine =
|
||||
JEMALLOC_VALGRIND_QUARANTINE_DEFAULT;
|
||||
}
|
||||
opt_redzone = true;
|
||||
}
|
||||
if (hit)
|
||||
continue;
|
||||
CONF_HANDLE_BOOL(opt_valgrind, "valgrind")
|
||||
}
|
||||
if (config_xmalloc) {
|
||||
CONF_HANDLE_BOOL(opt_xmalloc, "xmalloc")
|
||||
@@ -695,9 +734,9 @@ malloc_init_hard(void)
|
||||
* Create enough scaffolding to allow recursive allocation in
|
||||
* malloc_ncpus().
|
||||
*/
|
||||
narenas = 1;
|
||||
narenas_total = narenas_auto = 1;
|
||||
arenas = init_arenas;
|
||||
memset(arenas, 0, sizeof(arena_t *) * narenas);
|
||||
memset(arenas, 0, sizeof(arena_t *) * narenas_auto);
|
||||
|
||||
/*
|
||||
* Initialize one arena here. The rest are lazily created in
|
||||
@@ -755,20 +794,21 @@ malloc_init_hard(void)
|
||||
else
|
||||
opt_narenas = 1;
|
||||
}
|
||||
narenas = opt_narenas;
|
||||
narenas_auto = opt_narenas;
|
||||
/*
|
||||
* Make sure that the arenas array can be allocated. In practice, this
|
||||
* limit is enough to allow the allocator to function, but the ctl
|
||||
* machinery will fail to allocate memory at far lower limits.
|
||||
*/
|
||||
if (narenas > chunksize / sizeof(arena_t *)) {
|
||||
narenas = chunksize / sizeof(arena_t *);
|
||||
if (narenas_auto > chunksize / sizeof(arena_t *)) {
|
||||
narenas_auto = chunksize / sizeof(arena_t *);
|
||||
malloc_printf("<jemalloc>: Reducing narenas to limit (%d)\n",
|
||||
narenas);
|
||||
narenas_auto);
|
||||
}
|
||||
narenas_total = narenas_auto;
|
||||
|
||||
/* Allocate and initialize arenas. */
|
||||
arenas = (arena_t **)base_alloc(sizeof(arena_t *) * narenas);
|
||||
arenas = (arena_t **)base_alloc(sizeof(arena_t *) * narenas_total);
|
||||
if (arenas == NULL) {
|
||||
malloc_mutex_unlock(&init_lock);
|
||||
return (true);
|
||||
@@ -777,7 +817,7 @@ malloc_init_hard(void)
|
||||
* Zero the array. In practice, this should always be pre-zeroed,
|
||||
* since it was just mmap()ed, but let's be sure.
|
||||
*/
|
||||
memset(arenas, 0, sizeof(arena_t *) * narenas);
|
||||
memset(arenas, 0, sizeof(arena_t *) * narenas_total);
|
||||
/* Copy the pointer to the one arena that was already initialized. */
|
||||
arenas[0] = init_arenas[0];
|
||||
|
||||
@@ -855,7 +895,7 @@ JEMALLOC_ATTR(nonnull(1))
|
||||
* Avoid any uncertainty as to how many backtrace frames to ignore in
|
||||
* PROF_ALLOC_PREP().
|
||||
*/
|
||||
JEMALLOC_ATTR(noinline)
|
||||
JEMALLOC_NOINLINE
|
||||
#endif
|
||||
static int
|
||||
imemalign(void **memptr, size_t alignment, size_t size,
|
||||
@@ -1262,11 +1302,10 @@ je_valloc(size_t size)
|
||||
* passed an extra argument for the caller return address, which will be
|
||||
* ignored.
|
||||
*/
|
||||
JEMALLOC_EXPORT void (* const __free_hook)(void *ptr) = je_free;
|
||||
JEMALLOC_EXPORT void *(* const __malloc_hook)(size_t size) = je_malloc;
|
||||
JEMALLOC_EXPORT void *(* const __realloc_hook)(void *ptr, size_t size) =
|
||||
je_realloc;
|
||||
JEMALLOC_EXPORT void *(* const __memalign_hook)(size_t alignment, size_t size) =
|
||||
JEMALLOC_EXPORT void (* __free_hook)(void *ptr) = je_free;
|
||||
JEMALLOC_EXPORT void *(* __malloc_hook)(size_t size) = je_malloc;
|
||||
JEMALLOC_EXPORT void *(* __realloc_hook)(void *ptr, size_t size) = je_realloc;
|
||||
JEMALLOC_EXPORT void *(* __memalign_hook)(size_t alignment, size_t size) =
|
||||
je_memalign;
|
||||
#endif
|
||||
|
||||
@@ -1279,7 +1318,7 @@ JEMALLOC_EXPORT void *(* const __memalign_hook)(size_t alignment, size_t size) =
|
||||
*/
|
||||
|
||||
size_t
|
||||
je_malloc_usable_size(const void *ptr)
|
||||
je_malloc_usable_size(JEMALLOC_USABLE_SIZE_CONST void *ptr)
|
||||
{
|
||||
size_t ret;
|
||||
|
||||
@@ -1342,19 +1381,20 @@ je_mallctlbymib(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp,
|
||||
*/
|
||||
#ifdef JEMALLOC_EXPERIMENTAL
|
||||
|
||||
JEMALLOC_INLINE void *
|
||||
iallocm(size_t usize, size_t alignment, bool zero)
|
||||
static JEMALLOC_ATTR(always_inline) void *
|
||||
iallocm(size_t usize, size_t alignment, bool zero, bool try_tcache,
|
||||
arena_t *arena)
|
||||
{
|
||||
|
||||
assert(usize == ((alignment == 0) ? s2u(usize) : sa2u(usize,
|
||||
alignment)));
|
||||
|
||||
if (alignment != 0)
|
||||
return (ipalloc(usize, alignment, zero));
|
||||
return (ipallocx(usize, alignment, zero, try_tcache, arena));
|
||||
else if (zero)
|
||||
return (icalloc(usize));
|
||||
return (icallocx(usize, try_tcache, arena));
|
||||
else
|
||||
return (imalloc(usize));
|
||||
return (imallocx(usize, try_tcache, arena));
|
||||
}
|
||||
|
||||
int
|
||||
@@ -1365,6 +1405,9 @@ je_allocm(void **ptr, size_t *rsize, size_t size, int flags)
|
||||
size_t alignment = (ZU(1) << (flags & ALLOCM_LG_ALIGN_MASK)
|
||||
& (SIZE_T_MAX-1));
|
||||
bool zero = flags & ALLOCM_ZERO;
|
||||
unsigned arena_ind = ((unsigned)(flags >> 8)) - 1;
|
||||
arena_t *arena;
|
||||
bool try_tcache;
|
||||
|
||||
assert(ptr != NULL);
|
||||
assert(size != 0);
|
||||
@@ -1372,6 +1415,14 @@ je_allocm(void **ptr, size_t *rsize, size_t size, int flags)
|
||||
if (malloc_init())
|
||||
goto label_oom;
|
||||
|
||||
if (arena_ind != UINT_MAX) {
|
||||
arena = arenas[arena_ind];
|
||||
try_tcache = false;
|
||||
} else {
|
||||
arena = NULL;
|
||||
try_tcache = true;
|
||||
}
|
||||
|
||||
usize = (alignment == 0) ? s2u(size) : sa2u(size, alignment);
|
||||
if (usize == 0)
|
||||
goto label_oom;
|
||||
@@ -1388,18 +1439,19 @@ je_allocm(void **ptr, size_t *rsize, size_t size, int flags)
|
||||
s2u(SMALL_MAXCLASS+1) : sa2u(SMALL_MAXCLASS+1,
|
||||
alignment);
|
||||
assert(usize_promoted != 0);
|
||||
p = iallocm(usize_promoted, alignment, zero);
|
||||
p = iallocm(usize_promoted, alignment, zero,
|
||||
try_tcache, arena);
|
||||
if (p == NULL)
|
||||
goto label_oom;
|
||||
arena_prof_promoted(p, usize);
|
||||
} else {
|
||||
p = iallocm(usize, alignment, zero);
|
||||
p = iallocm(usize, alignment, zero, try_tcache, arena);
|
||||
if (p == NULL)
|
||||
goto label_oom;
|
||||
}
|
||||
prof_malloc(p, usize, cnt);
|
||||
} else {
|
||||
p = iallocm(usize, alignment, zero);
|
||||
p = iallocm(usize, alignment, zero, try_tcache, arena);
|
||||
if (p == NULL)
|
||||
goto label_oom;
|
||||
}
|
||||
@@ -1436,6 +1488,9 @@ je_rallocm(void **ptr, size_t *rsize, size_t size, size_t extra, int flags)
|
||||
& (SIZE_T_MAX-1));
|
||||
bool zero = flags & ALLOCM_ZERO;
|
||||
bool no_move = flags & ALLOCM_NO_MOVE;
|
||||
unsigned arena_ind = ((unsigned)(flags >> 8)) - 1;
|
||||
bool try_tcache_alloc, try_tcache_dalloc;
|
||||
arena_t *arena;
|
||||
|
||||
assert(ptr != NULL);
|
||||
assert(*ptr != NULL);
|
||||
@@ -1443,6 +1498,19 @@ je_rallocm(void **ptr, size_t *rsize, size_t size, size_t extra, int flags)
|
||||
assert(SIZE_T_MAX - size >= extra);
|
||||
assert(malloc_initialized || IS_INITIALIZER);
|
||||
|
||||
if (arena_ind != UINT_MAX) {
|
||||
arena_chunk_t *chunk;
|
||||
try_tcache_alloc = true;
|
||||
chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(*ptr);
|
||||
try_tcache_dalloc = (chunk == *ptr || chunk->arena !=
|
||||
arenas[arena_ind]);
|
||||
arena = arenas[arena_ind];
|
||||
} else {
|
||||
try_tcache_alloc = true;
|
||||
try_tcache_dalloc = true;
|
||||
arena = NULL;
|
||||
}
|
||||
|
||||
p = *ptr;
|
||||
if (config_prof && opt_prof) {
|
||||
prof_thr_cnt_t *cnt;
|
||||
@@ -1469,9 +1537,10 @@ je_rallocm(void **ptr, size_t *rsize, size_t size, size_t extra, int flags)
|
||||
if (prof_promote && (uintptr_t)cnt != (uintptr_t)1U
|
||||
&& ((alignment == 0) ? s2u(size) : sa2u(size, alignment))
|
||||
<= SMALL_MAXCLASS) {
|
||||
q = iralloc(p, SMALL_MAXCLASS+1, (SMALL_MAXCLASS+1 >=
|
||||
q = irallocx(p, SMALL_MAXCLASS+1, (SMALL_MAXCLASS+1 >=
|
||||
size+extra) ? 0 : size+extra - (SMALL_MAXCLASS+1),
|
||||
alignment, zero, no_move);
|
||||
alignment, zero, no_move, try_tcache_alloc,
|
||||
try_tcache_dalloc, arena);
|
||||
if (q == NULL)
|
||||
goto label_err;
|
||||
if (max_usize < PAGE) {
|
||||
@@ -1480,7 +1549,8 @@ je_rallocm(void **ptr, size_t *rsize, size_t size, size_t extra, int flags)
|
||||
} else
|
||||
usize = isalloc(q, config_prof);
|
||||
} else {
|
||||
q = iralloc(p, size, extra, alignment, zero, no_move);
|
||||
q = irallocx(p, size, extra, alignment, zero, no_move,
|
||||
try_tcache_alloc, try_tcache_dalloc, arena);
|
||||
if (q == NULL)
|
||||
goto label_err;
|
||||
usize = isalloc(q, config_prof);
|
||||
@@ -1497,7 +1567,8 @@ je_rallocm(void **ptr, size_t *rsize, size_t size, size_t extra, int flags)
|
||||
old_size = isalloc(p, false);
|
||||
old_rzsize = u2rz(old_size);
|
||||
}
|
||||
q = iralloc(p, size, extra, alignment, zero, no_move);
|
||||
q = irallocx(p, size, extra, alignment, zero, no_move,
|
||||
try_tcache_alloc, try_tcache_dalloc, arena);
|
||||
if (q == NULL)
|
||||
goto label_err;
|
||||
if (config_stats)
|
||||
@@ -1558,10 +1629,19 @@ je_dallocm(void *ptr, int flags)
|
||||
{
|
||||
size_t usize;
|
||||
size_t rzsize JEMALLOC_CC_SILENCE_INIT(0);
|
||||
unsigned arena_ind = ((unsigned)(flags >> 8)) - 1;
|
||||
bool try_tcache;
|
||||
|
||||
assert(ptr != NULL);
|
||||
assert(malloc_initialized || IS_INITIALIZER);
|
||||
|
||||
if (arena_ind != UINT_MAX) {
|
||||
arena_chunk_t *chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr);
|
||||
try_tcache = (chunk == ptr || chunk->arena !=
|
||||
arenas[arena_ind]);
|
||||
} else
|
||||
try_tcache = true;
|
||||
|
||||
UTRACE(ptr, 0, 0);
|
||||
if (config_stats || config_valgrind)
|
||||
usize = isalloc(ptr, config_prof);
|
||||
@@ -1574,7 +1654,7 @@ je_dallocm(void *ptr, int flags)
|
||||
thread_allocated_tsd_get()->deallocated += usize;
|
||||
if (config_valgrind && opt_valgrind)
|
||||
rzsize = p2rz(ptr);
|
||||
iqalloc(ptr);
|
||||
iqallocx(ptr, try_tcache);
|
||||
JEMALLOC_VALGRIND_FREE(ptr, rzsize);
|
||||
|
||||
return (ALLOCM_SUCCESS);
|
||||
@@ -1611,6 +1691,27 @@ je_nallocm(size_t *rsize, size_t size, int flags)
|
||||
* malloc during fork().
|
||||
*/
|
||||
|
||||
/*
|
||||
* If an application creates a thread before doing any allocation in the main
|
||||
* thread, then calls fork(2) in the main thread followed by memory allocation
|
||||
* in the child process, a race can occur that results in deadlock within the
|
||||
* child: the main thread may have forked while the created thread had
|
||||
* partially initialized the allocator. Ordinarily jemalloc prevents
|
||||
* fork/malloc races via the following functions it registers during
|
||||
* initialization using pthread_atfork(), but of course that does no good if
|
||||
* the allocator isn't fully initialized at fork time. The following library
|
||||
* constructor is a partial solution to this problem. It may still possible to
|
||||
* trigger the deadlock described above, but doing so would involve forking via
|
||||
* a library constructor that runs before jemalloc's runs.
|
||||
*/
|
||||
JEMALLOC_ATTR(constructor)
|
||||
static void
|
||||
jemalloc_constructor(void)
|
||||
{
|
||||
|
||||
malloc_init();
|
||||
}
|
||||
|
||||
#ifndef JEMALLOC_MUTEX_INIT_CB
|
||||
void
|
||||
jemalloc_prefork(void)
|
||||
@@ -1628,14 +1729,16 @@ _malloc_prefork(void)
|
||||
assert(malloc_initialized);
|
||||
|
||||
/* Acquire all mutexes in a safe order. */
|
||||
ctl_prefork();
|
||||
malloc_mutex_prefork(&arenas_lock);
|
||||
for (i = 0; i < narenas; i++) {
|
||||
for (i = 0; i < narenas_total; i++) {
|
||||
if (arenas[i] != NULL)
|
||||
arena_prefork(arenas[i]);
|
||||
}
|
||||
prof_prefork();
|
||||
chunk_prefork();
|
||||
base_prefork();
|
||||
huge_prefork();
|
||||
chunk_dss_prefork();
|
||||
}
|
||||
|
||||
#ifndef JEMALLOC_MUTEX_INIT_CB
|
||||
@@ -1655,14 +1758,16 @@ _malloc_postfork(void)
|
||||
assert(malloc_initialized);
|
||||
|
||||
/* Release all mutexes, now that fork() has completed. */
|
||||
chunk_dss_postfork_parent();
|
||||
huge_postfork_parent();
|
||||
base_postfork_parent();
|
||||
for (i = 0; i < narenas; i++) {
|
||||
chunk_postfork_parent();
|
||||
prof_postfork_parent();
|
||||
for (i = 0; i < narenas_total; i++) {
|
||||
if (arenas[i] != NULL)
|
||||
arena_postfork_parent(arenas[i]);
|
||||
}
|
||||
malloc_mutex_postfork_parent(&arenas_lock);
|
||||
ctl_postfork_parent();
|
||||
}
|
||||
|
||||
void
|
||||
@@ -1673,14 +1778,16 @@ jemalloc_postfork_child(void)
|
||||
assert(malloc_initialized);
|
||||
|
||||
/* Release all mutexes, now that fork() has completed. */
|
||||
chunk_dss_postfork_child();
|
||||
huge_postfork_child();
|
||||
base_postfork_child();
|
||||
for (i = 0; i < narenas; i++) {
|
||||
chunk_postfork_child();
|
||||
prof_postfork_child();
|
||||
for (i = 0; i < narenas_total; i++) {
|
||||
if (arenas[i] != NULL)
|
||||
arena_postfork_child(arenas[i]);
|
||||
}
|
||||
malloc_mutex_postfork_child(&arenas_lock);
|
||||
ctl_postfork_child();
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
@@ -64,7 +64,7 @@ pthread_create(pthread_t *__restrict thread,
|
||||
/******************************************************************************/
|
||||
|
||||
#ifdef JEMALLOC_MUTEX_INIT_CB
|
||||
int _pthread_mutex_init_calloc_cb(pthread_mutex_t *mutex,
|
||||
JEMALLOC_EXPORT int _pthread_mutex_init_calloc_cb(pthread_mutex_t *mutex,
|
||||
void *(calloc_cb)(size_t, size_t));
|
||||
#endif
|
||||
|
||||
|
||||
76
src/prof.c
76
src/prof.c
@@ -26,7 +26,7 @@ bool opt_prof_leak = false;
|
||||
bool opt_prof_accum = false;
|
||||
char opt_prof_prefix[PATH_MAX + 1];
|
||||
|
||||
uint64_t prof_interval;
|
||||
uint64_t prof_interval = 0;
|
||||
bool prof_promote;
|
||||
|
||||
/*
|
||||
@@ -90,8 +90,7 @@ static bool prof_dump(bool propagate_err, const char *filename,
|
||||
bool leakcheck);
|
||||
static void prof_dump_filename(char *filename, char v, int64_t vseq);
|
||||
static void prof_fdump(void);
|
||||
static void prof_bt_hash(const void *key, unsigned minbits, size_t *hash1,
|
||||
size_t *hash2);
|
||||
static void prof_bt_hash(const void *key, size_t r_hash[2]);
|
||||
static bool prof_bt_keycomp(const void *k1, const void *k2);
|
||||
static malloc_mutex_t *prof_ctx_mutex_choose(void);
|
||||
|
||||
@@ -1043,34 +1042,13 @@ prof_gdump(void)
|
||||
}
|
||||
|
||||
static void
|
||||
prof_bt_hash(const void *key, unsigned minbits, size_t *hash1, size_t *hash2)
|
||||
prof_bt_hash(const void *key, size_t r_hash[2])
|
||||
{
|
||||
size_t ret1, ret2;
|
||||
uint64_t h;
|
||||
prof_bt_t *bt = (prof_bt_t *)key;
|
||||
|
||||
cassert(config_prof);
|
||||
assert(minbits <= 32 || (SIZEOF_PTR == 8 && minbits <= 64));
|
||||
assert(hash1 != NULL);
|
||||
assert(hash2 != NULL);
|
||||
|
||||
h = hash(bt->vec, bt->len * sizeof(void *),
|
||||
UINT64_C(0x94122f335b332aea));
|
||||
if (minbits <= 32) {
|
||||
/*
|
||||
* Avoid doing multiple hashes, since a single hash provides
|
||||
* enough bits.
|
||||
*/
|
||||
ret1 = h & ZU(0xffffffffU);
|
||||
ret2 = h >> 32;
|
||||
} else {
|
||||
ret1 = h;
|
||||
ret2 = hash(bt->vec, bt->len * sizeof(void *),
|
||||
UINT64_C(0x8432a476666bbc13));
|
||||
}
|
||||
|
||||
*hash1 = ret1;
|
||||
*hash2 = ret2;
|
||||
hash(bt->vec, bt->len * sizeof(void *), 0x94122f33U, r_hash);
|
||||
}
|
||||
|
||||
static bool
|
||||
@@ -1206,13 +1184,11 @@ prof_boot1(void)
|
||||
*/
|
||||
opt_prof = true;
|
||||
opt_prof_gdump = false;
|
||||
prof_interval = 0;
|
||||
} else if (opt_prof) {
|
||||
if (opt_lg_prof_interval >= 0) {
|
||||
prof_interval = (((uint64_t)1U) <<
|
||||
opt_lg_prof_interval);
|
||||
} else
|
||||
prof_interval = 0;
|
||||
}
|
||||
}
|
||||
|
||||
prof_promote = (opt_prof && opt_lg_prof_sample > LG_PAGE);
|
||||
@@ -1270,4 +1246,46 @@ prof_boot2(void)
|
||||
return (false);
|
||||
}
|
||||
|
||||
void
|
||||
prof_prefork(void)
|
||||
{
|
||||
|
||||
if (opt_prof) {
|
||||
unsigned i;
|
||||
|
||||
malloc_mutex_lock(&bt2ctx_mtx);
|
||||
malloc_mutex_lock(&prof_dump_seq_mtx);
|
||||
for (i = 0; i < PROF_NCTX_LOCKS; i++)
|
||||
malloc_mutex_lock(&ctx_locks[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
prof_postfork_parent(void)
|
||||
{
|
||||
|
||||
if (opt_prof) {
|
||||
unsigned i;
|
||||
|
||||
for (i = 0; i < PROF_NCTX_LOCKS; i++)
|
||||
malloc_mutex_postfork_parent(&ctx_locks[i]);
|
||||
malloc_mutex_postfork_parent(&prof_dump_seq_mtx);
|
||||
malloc_mutex_postfork_parent(&bt2ctx_mtx);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
prof_postfork_child(void)
|
||||
{
|
||||
|
||||
if (opt_prof) {
|
||||
unsigned i;
|
||||
|
||||
for (i = 0; i < PROF_NCTX_LOCKS; i++)
|
||||
malloc_mutex_postfork_child(&ctx_locks[i]);
|
||||
malloc_mutex_postfork_child(&prof_dump_seq_mtx);
|
||||
malloc_mutex_postfork_child(&bt2ctx_mtx);
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
21
src/rtree.c
21
src/rtree.c
@@ -44,3 +44,24 @@ rtree_new(unsigned bits)
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
||||
void
|
||||
rtree_prefork(rtree_t *rtree)
|
||||
{
|
||||
|
||||
malloc_mutex_prefork(&rtree->mutex);
|
||||
}
|
||||
|
||||
void
|
||||
rtree_postfork_parent(rtree_t *rtree)
|
||||
{
|
||||
|
||||
malloc_mutex_postfork_parent(&rtree->mutex);
|
||||
}
|
||||
|
||||
void
|
||||
rtree_postfork_child(rtree_t *rtree)
|
||||
{
|
||||
|
||||
malloc_mutex_postfork_child(&rtree->mutex);
|
||||
}
|
||||
|
||||
10
src/stats.c
10
src/stats.c
@@ -206,6 +206,7 @@ stats_arena_print(void (*write_cb)(void *, const char *), void *cbopaque,
|
||||
unsigned i, bool bins, bool large)
|
||||
{
|
||||
unsigned nthreads;
|
||||
const char *dss;
|
||||
size_t page, pactive, pdirty, mapped;
|
||||
uint64_t npurge, nmadvise, purged;
|
||||
size_t small_allocated;
|
||||
@@ -218,6 +219,9 @@ stats_arena_print(void (*write_cb)(void *, const char *), void *cbopaque,
|
||||
CTL_I_GET("stats.arenas.0.nthreads", &nthreads, unsigned);
|
||||
malloc_cprintf(write_cb, cbopaque,
|
||||
"assigned threads: %u\n", nthreads);
|
||||
CTL_I_GET("stats.arenas.0.dss", &dss, const char *);
|
||||
malloc_cprintf(write_cb, cbopaque, "dss allocation precedence: %s\n",
|
||||
dss);
|
||||
CTL_I_GET("stats.arenas.0.pactive", &pactive, size_t);
|
||||
CTL_I_GET("stats.arenas.0.pdirty", &pdirty, size_t);
|
||||
CTL_I_GET("stats.arenas.0.npurge", &npurge, uint64_t);
|
||||
@@ -370,6 +374,7 @@ stats_print(void (*write_cb)(void *, const char *), void *cbopaque,
|
||||
"Run-time option settings:\n");
|
||||
OPT_WRITE_BOOL(abort)
|
||||
OPT_WRITE_SIZE_T(lg_chunk)
|
||||
OPT_WRITE_CHAR_P(dss)
|
||||
OPT_WRITE_SIZE_T(narenas)
|
||||
OPT_WRITE_SSIZE_T(lg_dirty_mult)
|
||||
OPT_WRITE_BOOL(stats_print)
|
||||
@@ -400,7 +405,7 @@ stats_print(void (*write_cb)(void *, const char *), void *cbopaque,
|
||||
malloc_cprintf(write_cb, cbopaque, "CPUs: %u\n", ncpus);
|
||||
|
||||
CTL_GET("arenas.narenas", &uv, unsigned);
|
||||
malloc_cprintf(write_cb, cbopaque, "Max arenas: %u\n", uv);
|
||||
malloc_cprintf(write_cb, cbopaque, "Arenas: %u\n", uv);
|
||||
|
||||
malloc_cprintf(write_cb, cbopaque, "Pointer size: %zu\n",
|
||||
sizeof(void *));
|
||||
@@ -472,7 +477,8 @@ stats_print(void (*write_cb)(void *, const char *), void *cbopaque,
|
||||
CTL_GET("stats.chunks.current", &chunks_current, size_t);
|
||||
malloc_cprintf(write_cb, cbopaque, "chunks: nchunks "
|
||||
"highchunks curchunks\n");
|
||||
malloc_cprintf(write_cb, cbopaque, " %13"PRIu64"%13zu%13zu\n",
|
||||
malloc_cprintf(write_cb, cbopaque,
|
||||
" %13"PRIu64" %12zu %12zu\n",
|
||||
chunks_total, chunks_high, chunks_current);
|
||||
|
||||
/* Print huge stats. */
|
||||
|
||||
13
src/tcache.c
13
src/tcache.c
@@ -97,9 +97,7 @@ tcache_bin_flush_small(tcache_bin_t *tbin, size_t binind, unsigned rem,
|
||||
arena_bin_t *bin = &arena->bins[binind];
|
||||
|
||||
if (config_prof && arena == tcache->arena) {
|
||||
malloc_mutex_lock(&arena->lock);
|
||||
arena_prof_accum(arena, tcache->prof_accumbytes);
|
||||
malloc_mutex_unlock(&arena->lock);
|
||||
tcache->prof_accumbytes = 0;
|
||||
}
|
||||
|
||||
@@ -180,7 +178,7 @@ tcache_bin_flush_large(tcache_bin_t *tbin, size_t binind, unsigned rem,
|
||||
malloc_mutex_lock(&arena->lock);
|
||||
if ((config_prof || config_stats) && arena == tcache->arena) {
|
||||
if (config_prof) {
|
||||
arena_prof_accum(arena,
|
||||
arena_prof_accum_locked(arena,
|
||||
tcache->prof_accumbytes);
|
||||
tcache->prof_accumbytes = 0;
|
||||
}
|
||||
@@ -288,7 +286,7 @@ tcache_create(arena_t *arena)
|
||||
else if (size <= tcache_maxclass)
|
||||
tcache = (tcache_t *)arena_malloc_large(arena, size, true);
|
||||
else
|
||||
tcache = (tcache_t *)icalloc(size);
|
||||
tcache = (tcache_t *)icallocx(size, false, arena);
|
||||
|
||||
if (tcache == NULL)
|
||||
return (NULL);
|
||||
@@ -343,11 +341,8 @@ tcache_destroy(tcache_t *tcache)
|
||||
}
|
||||
}
|
||||
|
||||
if (config_prof && tcache->prof_accumbytes > 0) {
|
||||
malloc_mutex_lock(&tcache->arena->lock);
|
||||
if (config_prof && tcache->prof_accumbytes > 0)
|
||||
arena_prof_accum(tcache->arena, tcache->prof_accumbytes);
|
||||
malloc_mutex_unlock(&tcache->arena->lock);
|
||||
}
|
||||
|
||||
tcache_size = arena_salloc(tcache, false);
|
||||
if (tcache_size <= SMALL_MAXCLASS) {
|
||||
@@ -364,7 +359,7 @@ tcache_destroy(tcache_t *tcache)
|
||||
|
||||
arena_dalloc_large(arena, chunk, tcache);
|
||||
} else
|
||||
idalloc(tcache);
|
||||
idallocx(tcache, false);
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -377,7 +377,6 @@ malloc_vsnprintf(char *str, size_t size, const char *format, va_list ap)
|
||||
case '\0': goto label_out;
|
||||
case '%': {
|
||||
bool alt_form = false;
|
||||
bool zero_pad = false;
|
||||
bool left_justify = false;
|
||||
bool plus_space = false;
|
||||
bool plus_plus = false;
|
||||
@@ -398,10 +397,6 @@ malloc_vsnprintf(char *str, size_t size, const char *format, va_list ap)
|
||||
assert(alt_form == false);
|
||||
alt_form = true;
|
||||
break;
|
||||
case '0':
|
||||
assert(zero_pad == false);
|
||||
zero_pad = true;
|
||||
break;
|
||||
case '-':
|
||||
assert(left_justify == false);
|
||||
left_justify = true;
|
||||
|
||||
12
src/zone.c
12
src/zone.c
@@ -171,6 +171,16 @@ void
|
||||
register_zone(void)
|
||||
{
|
||||
|
||||
/*
|
||||
* If something else replaced the system default zone allocator, don't
|
||||
* register jemalloc's.
|
||||
*/
|
||||
malloc_zone_t *default_zone = malloc_default_zone();
|
||||
if (!default_zone->zone_name ||
|
||||
strcmp(default_zone->zone_name, "DefaultMallocZone") != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
zone.size = (void *)zone_size;
|
||||
zone.malloc = (void *)zone_malloc;
|
||||
zone.calloc = (void *)zone_calloc;
|
||||
@@ -241,7 +251,7 @@ register_zone(void)
|
||||
* then becomes the default.
|
||||
*/
|
||||
do {
|
||||
malloc_zone_t *default_zone = malloc_default_zone();
|
||||
default_zone = malloc_default_zone();
|
||||
malloc_zone_unregister(default_zone);
|
||||
malloc_zone_register(default_zone);
|
||||
} while (malloc_default_zone() != &zone);
|
||||
|
||||
66
test/ALLOCM_ARENA.c
Normal file
66
test/ALLOCM_ARENA.c
Normal file
@@ -0,0 +1,66 @@
|
||||
#define JEMALLOC_MANGLE
|
||||
#include "jemalloc_test.h"
|
||||
|
||||
#define NTHREADS 10
|
||||
|
||||
void *
|
||||
je_thread_start(void *arg)
|
||||
{
|
||||
unsigned thread_ind = (unsigned)(uintptr_t)arg;
|
||||
unsigned arena_ind;
|
||||
int r;
|
||||
void *p;
|
||||
size_t rsz, sz;
|
||||
|
||||
sz = sizeof(arena_ind);
|
||||
if (mallctl("arenas.extend", &arena_ind, &sz, NULL, 0)
|
||||
!= 0) {
|
||||
malloc_printf("Error in arenas.extend\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
if (thread_ind % 4 != 3) {
|
||||
size_t mib[3];
|
||||
size_t miblen = sizeof(mib) / sizeof(size_t);
|
||||
const char *dss_precs[] = {"disabled", "primary", "secondary"};
|
||||
const char *dss = dss_precs[thread_ind % 4];
|
||||
if (mallctlnametomib("arena.0.dss", mib, &miblen) != 0) {
|
||||
malloc_printf("Error in mallctlnametomib()\n");
|
||||
abort();
|
||||
}
|
||||
mib[1] = arena_ind;
|
||||
if (mallctlbymib(mib, miblen, NULL, NULL, (void *)&dss,
|
||||
sizeof(const char *))) {
|
||||
malloc_printf("Error in mallctlbymib()\n");
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
r = allocm(&p, &rsz, 1, ALLOCM_ARENA(arena_ind));
|
||||
if (r != ALLOCM_SUCCESS) {
|
||||
malloc_printf("Unexpected allocm() error\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
je_thread_t threads[NTHREADS];
|
||||
unsigned i;
|
||||
|
||||
malloc_printf("Test begin\n");
|
||||
|
||||
for (i = 0; i < NTHREADS; i++) {
|
||||
je_thread_create(&threads[i], je_thread_start,
|
||||
(void *)(uintptr_t)i);
|
||||
}
|
||||
|
||||
for (i = 0; i < NTHREADS; i++)
|
||||
je_thread_join(threads[i], NULL);
|
||||
|
||||
malloc_printf("Test end\n");
|
||||
return (0);
|
||||
}
|
||||
2
test/ALLOCM_ARENA.exp
Normal file
2
test/ALLOCM_ARENA.exp
Normal file
@@ -0,0 +1,2 @@
|
||||
Test begin
|
||||
Test end
|
||||
@@ -1,7 +1,7 @@
|
||||
#define JEMALLOC_MANGLE
|
||||
#include "jemalloc_test.h"
|
||||
|
||||
#define NTHREADS 10
|
||||
#define NTHREADS 10
|
||||
|
||||
void *
|
||||
je_thread_start(void *arg)
|
||||
@@ -66,8 +66,10 @@ main(void)
|
||||
goto label_return;
|
||||
}
|
||||
|
||||
for (i = 0; i < NTHREADS; i++)
|
||||
je_thread_create(&threads[i], je_thread_start, (void *)&arena_ind);
|
||||
for (i = 0; i < NTHREADS; i++) {
|
||||
je_thread_create(&threads[i], je_thread_start,
|
||||
(void *)&arena_ind);
|
||||
}
|
||||
|
||||
for (i = 0; i < NTHREADS; i++)
|
||||
je_thread_join(threads[i], (void *)&ret);
|
||||
|
||||
Reference in New Issue
Block a user