diff --git a/Makefile.inc1 b/Makefile.inc1 index c65af05a7700..7544312c9e0a 100644 --- a/Makefile.inc1 +++ b/Makefile.inc1 @@ -234,11 +234,16 @@ INSTALLTMP!= /usr/bin/mktemp -d -u -t install # 2. build-tools stage [TMAKE] # This stage is responsible for creating the object # tree and building any tools that are needed during -# the build process. +# the build process. Some programs are listed during +# this phase because they build binaries to generate +# files needed to build these programs. This stage also +# builds the 'build-tools' target rather than 'all'. # 3. cross-tools stage [XMAKE] # This stage is responsible for creating any tools that # are needed for building the system. A cross-compiler is one -# of them. +# of them. This differs from build tools in two ways: +# 1. the 'all' target is built rather than 'build-tools' +# 2. these tools are installed into TMPPATH for stage 4. # 4. world stage [WMAKE] # This stage actually builds the world. # 5. install stage (optional) [IMAKE] @@ -459,7 +464,6 @@ LIB32WMAKEENV+= MAKEOBJDIRPREFIX=${LIB32_OBJTREE} \ PATH=${TMPPATH} \ LIBDIR=/usr/lib32 \ SHLIBDIR=/usr/lib32 \ - LIBPRIVATEDIR=/usr/lib32/private \ DTRACE="${DTRACE} -32" LIB32WMAKEFLAGS+= CC="${XCC} ${LIB32FLAGS}" \ CXX="${XCXX} ${LIB32FLAGS}" \ @@ -825,7 +829,7 @@ __installcheck_UGID: _zoneinfo= zic tzsetup .endif -ITOOLS= [ awk cap_mkdb cat chflags chmod chown \ +ITOOLS= [ awk cap_mkdb cat chflags chmod chown cmp cp \ date echo egrep find grep id install ${_install-info} \ ln lockf make mkdir mtree mv pwd_mkdb \ rm sed services_mkdb sh strip sysctl test true uname wc ${_zoneinfo} \ @@ -1155,6 +1159,16 @@ reinstallkernel reinstallkernel.debug: _installcheck_kernel cd ${KRNLOBJDIR}/${INSTALLKERNEL}; \ ${CROSSENV} PATH=${TMPPATH} \ ${MAKE} ${IMAKE_INSTALL} KERNEL=${INSTKERNNAME} ${.TARGET:S/kernel//} +.if ${BUILDKERNELS:[#]} > 1 +.for _kernel in ${BUILDKERNELS:[2..-1]} + @echo "--------------------------------------------------------------" + @echo ">>> Installing kernel ${_kernel}" + @echo "--------------------------------------------------------------" + cd ${KRNLOBJDIR}/${_kernel}; \ + ${CROSSENV} PATH=${TMPPATH} \ + ${MAKE} ${IMAKE_INSTALL} KERNEL=${INSTKERNNAME}.${_kernel} ${.TARGET:S/kernel//} +.endfor +.endif distributekernel distributekernel.debug: .if empty(INSTALLKERNEL) @@ -1174,7 +1188,8 @@ distributekernel distributekernel.debug: sed -e 's|^./kernel|.|' ${DESTDIR}/${DISTDIR}/kernel.premeta > \ ${DESTDIR}/${DISTDIR}/kernel.meta .endif -.for _kernel in ${BUILDKERNELS:S/${INSTALLKERNEL}//} +.if ${BUILDKERNELS:[#]} > 1 +.for _kernel in ${BUILDKERNELS:[2..-1]} .if defined(NO_ROOT) echo "#${MTREE_MAGIC}" > ${DESTDIR}/${DISTDIR}/kernel.${_kernel}.premeta .endif @@ -1190,27 +1205,32 @@ distributekernel distributekernel.debug: ${DESTDIR}/${DISTDIR}/kernel.${_kernel}.meta .endif .endfor +.endif packagekernel: .if defined(NO_ROOT) cd ${DESTDIR}/${DISTDIR}/kernel; \ tar cvf - @${DESTDIR}/${DISTDIR}/kernel.meta | \ ${XZ_CMD} > ${DESTDIR}/${DISTDIR}/kernel.txz -.for _kernel in ${BUILDKERNELS:S/${INSTALLKERNEL}//} +.if ${BUILDKERNELS:[#]} > 1 +.for _kernel in ${BUILDKERNELS:[2..-1]} cd ${DESTDIR}/${DISTDIR}/kernel.${_kernel}; \ tar cvf - @${DESTDIR}/${DISTDIR}/kernel.${_kernel}.meta | \ ${XZ_CMD} > ${DESTDIR}/${DISTDIR}/kernel.${_kernel}.txz .endfor +.endif .else cd ${DESTDIR}/${DISTDIR}/kernel; \ tar cvf - . | \ ${XZ_CMD} > ${DESTDIR}/${DISTDIR}/kernel.txz -.for _kernel in ${BUILDKERNELS:S/${INSTALLKERNEL}//} +.if ${BUILDKERNELS:[#]} > 1 +.for _kernel in ${BUILDKERNELS:[2..-1]} cd ${DESTDIR}/${DISTDIR}/kernel.${_kernel}; \ tar cvf - . | \ ${XZ_CMD} > ${DESTDIR}/${DISTDIR}/kernel.${_kernel}.txz .endfor .endif +.endif create-world-packages: @rm -f ${DESTDIR}/*.plist 2>/dev/null || : @@ -1349,16 +1369,23 @@ update: # # -# legacy: Build compatibility shims for the next three targets. This is a minimal -# set of tools and shims necessary to compensate for older systems which don't have -# the APIs that the targets built in bootstrap-tools, build-tools or cross-tools. +# legacy: Build compatibility shims for the next three targets. This is a +# minimal set of tools and shims necessary to compensate for older systems +# which don't have the APIs required by the targets built in bootstrap-tools, +# build-tools or cross-tools. # + +# ELF Tool Chain libraries are needed for ELF tools and dtrace tools. +.if ${BOOTSTRAPPING} < 1100006 +_elftoolchain_libs= lib/libelf lib/libdwarf +.endif + legacy: .if ${BOOTSTRAPPING} < 800107 && ${BOOTSTRAPPING} != 0 @echo "ERROR: Source upgrades from versions prior to 8.0 not supported."; \ false .endif -.for _tool in tools/build +.for _tool in tools/build ${_elftoolchain_libs} ${_+_}@${ECHODIR} "===> ${_tool} (obj,includes,depend,all,install)"; \ cd ${.CURDIR}/${_tool} && \ ${MAKE} DIRPRFX=${_tool}/ obj && \ @@ -1421,7 +1448,8 @@ _lex= usr.bin/lex # r277259 crunchide: Correct 64-bit section header offset # r281674 crunchide: always include both 32- and 64-bit ELF support -.if ${BOOTSTRAPPING} < 1100071 +# r285986 crunchen: use STRIPBIN rather than STRIP +.if ${BOOTSTRAPPING} < 1100078 _crunch= usr.sbin/crunch .endif @@ -1456,13 +1484,12 @@ ${_bt}-usr.bin/clang/tblgen: ${_bt}-lib/clang/libllvmtablegen ${_bt}-lib/clang/l # pre libdwarf .if ${BOOTSTRAPPING} < 1100006 || (${MACHINE} != ${TARGET} || \ ${MACHINE_ARCH} != ${TARGET_ARCH}) -_elftoolchain_libs= lib/libelf lib/libdwarf .if ${MK_CDDL} != "no" _dtrace_tools= cddl/usr.bin/sgsmsg cddl/lib/libctf cddl/usr.bin/ctfconvert \ cddl/usr.bin/ctfmerge -${_bt}-cddl/usr.bin/ctfconvert: ${_bt}-lib/libelf ${_bt}-lib/libdwarf ${_bt}-cddl/lib/libctf -${_bt}-cddl/usr.bin/ctfmerge: ${_bt}-lib/libelf ${_bt}-lib/libdwarf ${_bt}-cddl/lib/libctf +${_bt}-cddl/usr.bin/ctfconvert: ${_bt}-cddl/lib/libctf +${_bt}-cddl/usr.bin/ctfmerge: ${_bt}-cddl/lib/libctf .endif .endif @@ -1505,7 +1532,6 @@ bootstrap-tools: .PHONY .for _tool in \ ${_clang_tblgen} \ ${_kerberos5_bootstrap_tools} \ - ${_elftoolchain_libs} \ ${_dtrace_tools} \ ${_strfile} \ ${_gperf} \ @@ -1551,17 +1577,18 @@ _gcc_tools= gnu/usr.bin/cc/cc_tools .endif .if ${MK_RESCUE} != "no" -_rescue= rescue/rescue +# rescue includes programs that have build-tools targets +_rescue=rescue/rescue .endif build-tools: .MAKE .for _tool in \ bin/csh \ bin/sh \ - ${_rescue} \ ${LOCAL_TOOL_DIRS} \ lib/ncurses/ncurses \ lib/ncurses/ncursesw \ + ${_rescue} \ ${_share} \ usr.bin/awk \ lib/libmagic \ @@ -1606,7 +1633,7 @@ _btxld= usr.sbin/btxld .if ${MK_BINUTILS_BOOTSTRAP} != "no" _binutils= gnu/usr.bin/binutils .endif -.if ${MK_ELFTOOLCHAIN_TOOLS} != "no" +.if ${MK_ELFTOOLCHAIN_BOOTSTRAP} != "no" _elftctools= lib/libelftc \ usr.bin/elfcopy \ usr.bin/nm \ @@ -1616,7 +1643,7 @@ _elftctools= lib/libelftc \ # cross-build on a FreeBSD 10 host: _elftctools+= usr.bin/addr2line .endif -.elif ${TARGET_ARCH} != ${MACHINE_ARCH} && ${MK_ELFTOOLCHAIN_TOOLS} != "no" +.elif ${TARGET_ARCH} != ${MACHINE_ARCH} && ${MK_ELFTOOLCHAIN_BOOTSTRAP} != "no" # If cross-building with an external binutils we still need to build strip for # the target (for at least crunchide). _elftctools= lib/libelftc \ @@ -1890,9 +1917,7 @@ cddl/lib/libctf__L: lib/libz__L .endif # cddl/lib/libdtrace requires lib/libproc and lib/librtld_db; it's only built # on select architectures though (see cddl/lib/Makefile) -.if ${MACHINE_CPUARCH} == "aarch64" || ${MACHINE_ARCH} == "amd64" || \ - ${MACHINE_CPUARCH} == "arm" || ${MACHINE_ARCH} == "i386" || \ - ${MACHINE_CPUARCH} == "mips" || ${MACHINE_CPUARCH} == "powerpc" +.if ${MACHINE_CPUARCH} != "sparc64" _prebuild_libs+= lib/libproc lib/librtld_db .endif diff --git a/ObsoleteFiles.inc b/ObsoleteFiles.inc index 236da23f1cc2..0b09df34cb2d 100644 --- a/ObsoleteFiles.inc +++ b/ObsoleteFiles.inc @@ -38,6 +38,14 @@ # xargs -n1 | sort | uniq -d; # done +# 20150818: *allocm() are gone in jemalloc 4.0.0 +OLD_FILES+=usr/share/man/man3/allocm.3.gz +OLD_FILES+=usr/share/man/man3/dallocm.3.gz +OLD_FILES+=usr/share/man/man3/nallocm.3.gz +OLD_FILES+=usr/share/man/man3/rallocm.3.gz +OLD_FILES+=usr/share/man/man3/sallocm.3.gz +# 20150802: Remove netbsd's test on pw(8) +OLD_FILES+=usr/tests/usr.sbin/pw/pw_test # 20150719: Remove libarchive.pc OLD_FILES+=usr/libdata/pkgconfig/libarchive.pc # 20150705: Rename DTrace provider man pages. @@ -47,6 +55,9 @@ OLD_FILES+=usr/share/man/man4/dtrace-proc.4.gz OLD_FILES+=usr/share/man/man4/dtrace-sched.4.gz OLD_FILES+=usr/share/man/man4/dtrace-tcp.4.gz OLD_FILES+=usr/share/man/man4/dtrace-udp.4.gz +# 20150624 +OLD_LIBS+=usr/lib/libugidfw.so.4 +OLD_LIBS+=usr/lib32/libugidfw.so.4 # 20150604: Move nvlist man pages to section 9. OLD_FILES+=usr/share/man/man3/libnv.3.gz OLD_FILES+=usr/share/man/man3/nvlist.3.gz diff --git a/UPDATING b/UPDATING index 5756d78ef8f5..41c7bd919b4f 100644 --- a/UPDATING +++ b/UPDATING @@ -31,6 +31,79 @@ NOTE TO PEOPLE WHO THINK THAT FreeBSD 11.x IS SLOW: disable the most expensive debugging functionality run "ln -s 'abort:false,junk:false' /etc/malloc.conf".) +20150827: + The wireless drivers had undergone changes that remove the 'parent + interface' from the ifconfig -l output. The rc.d network scripts + used to check presence of a parent interface in the list, so old + scripts would fail to start wireless networking. Thus, etcupdate(3) + or mergemaster(8) run is required after kernel update, to update your + rc.d scripts in /etc. + +20150827: + pf no longer supports 'scrub fragment crop' or 'scrub fragment drop-ovl' + These configurations are now automatically interpreted as + 'scrub fragment reassemble'. + +20150817: + Kernel-loadable modules for the random(4) device are back. To use + them, the kernel must have + + device random + options RANDOM_LOADABLE + + kldload(8) can then be used to load random_fortuna.ko + or random_yarrow.ko. Please note that due to the indirect + function calls that the loadable modules need to provide, + the build-in variants will be slightly more efficient. + + The random(4) kernel option RANDOM_DUMMY has been retired due to + unpopularity. It was not all that useful anyway. + +20150813: + The WITHOUT_ELFTOOLCHAIN_TOOLS src.conf(5) knob has been retired. + Control over building the ELF Tool Chain tools is now provided by + the WITHOUT_TOOLCHAIN knob. + +20150810: + The polarity of Pulse Per Second (PPS) capture events with the + uart(4) driver has been corrected. Prior to this change the PPS + "assert" event corresponded to the trailing edge of a positive PPS + pulse and the "clear" event was the leading edge of the next pulse. + + As the width of a PPS pulse in a typical GPS receiver is on the + order of 1 millisecond, most users will not notice any significant + difference with this change. + + Anyone who has compensated for the historical polarity reversal by + configuring a negative offset equal to the pulse width will need to + remove that workaround. + +20150809: + The default group assigned to /dev/dri entries has been changed + from 'wheel' to 'video' with the id of '44'. If you want to have + access to the dri devices please add yourself to the video group + with: + + # pw groupmod video -m $USER + +20150806: + The menu.rc and loader.rc files will now be replaced during + upgrades. Please migrate local changes to menu.rc.local and + loader.rc.local instead. + +20150805: + GNU Binutils versions of addr2line, c++filt, nm, readelf, size, + strings and strip have been removed. The src.conf(5) knob + WITHOUT_ELFTOOLCHAIN_TOOLS no longer provides the binutils tools. + +20150728: + As ZFS requires more kernel stack pages than is the default on some + architectures e.g. i386, it now warns if KSTACK_PAGES is less than + ZFS_MIN_KSTACK_PAGES (which is 4 at the time of writing). + + Please consider using 'options KSTACK_PAGES=X' where X is greater + than or equal to ZFS_MIN_KSTACK_PAGES i.e. 4 in such configurations. + 20150706: sendmail has been updated to 8.15.2. Starting with FreeBSD 11.0 and sendmail 8.15, sendmail uses uncompressed IPv6 addresses by @@ -216,7 +289,7 @@ NOTE TO PEOPLE WHO THINK THAT FreeBSD 11.x IS SLOW: taken from the ELF Tool Chain project rather than GNU binutils. They should be drop-in replacements, with the addition of arm64 support. The WITHOUT_ELFTOOLCHAIN_TOOLS= knob may be used to obtain the - binutils tools, if necessary. + binutils tools, if necessary. See 20150805 for updated information. 20150105: The default Unbound configuration now enables remote control @@ -569,6 +642,10 @@ NOTE TO PEOPLE WHO THINK THAT FreeBSD 11.x IS SLOW: or # pkg install pkg; ldd /usr/local/sbin/pkg | grep bsdyml +20131010: + The stable/10 branch has been created in subversion from head + revision r256279. + 20131010: The rc.d/jail script has been updated to support jail(8) configuration file. The "jail__*" rc.conf(5) variables @@ -1112,6 +1189,13 @@ COMMON ITEMS: around can lead to problems if pam has changed too much from your starting point to allow continued authentication after the upgrade. + This file should be read as a log of events. When a later event changes + information of a prior event, the prior event should not be deleted. + Instead, a pointer to the entry with the new information should be + placed in the old entry. Readers of this file should also sanity check + older entries before relying on them blindly. Authors of new entries + should write them with this in mind. + ZFS notes --------- When upgrading the boot ZFS pool to a new version, always follow @@ -1282,7 +1366,7 @@ FORMAT: This file contains a list, in reverse chronological order, of major breakages in tracking -current. It is not guaranteed to be a complete -list of such breakages, and only contains entries since October 10, 2007. +list of such breakages, and only contains entries since September 23, 2011. If you need to see UPDATING entries from before that date, you will need to fetch an UPDATING file from an older FreeBSD release. diff --git a/bin/df/df.c b/bin/df/df.c index 997f789db787..f8d43bf95f59 100644 --- a/bin/df/df.c +++ b/bin/df/df.c @@ -311,7 +311,7 @@ main(int argc, char *argv[]) xo_close_container("storage-system-information"); xo_finish(); - return (rv); + exit(rv); } static char * diff --git a/bin/ls/Makefile b/bin/ls/Makefile index 422d89116da4..a9431f1b014c 100644 --- a/bin/ls/Makefile +++ b/bin/ls/Makefile @@ -5,7 +5,7 @@ PROG= ls SRCS= cmp.c ls.c print.c util.c -LIBADD= util xo +LIBADD= xo util .if !defined(RELEASE_CRUNCH) && \ ${MK_LS_COLORS} != no diff --git a/bin/pkill/Makefile b/bin/pkill/Makefile index 84a5af71ddf3..2985b37dbff1 100644 --- a/bin/pkill/Makefile +++ b/bin/pkill/Makefile @@ -5,7 +5,7 @@ PROG= pkill -LIBADD= kvm +LIBADD= kvm jail LINKS= ${BINDIR}/pkill ${BINDIR}/pgrep MLINKS= pkill.1 pgrep.1 diff --git a/bin/pkill/Makefile.depend b/bin/pkill/Makefile.depend index a1ac545aff09..2940edd9368d 100644 --- a/bin/pkill/Makefile.depend +++ b/bin/pkill/Makefile.depend @@ -9,6 +9,7 @@ DIRDEPS = \ lib/${CSU_DIR} \ lib/libc \ lib/libcompiler_rt \ + lib/libjail \ lib/libkvm \ diff --git a/bin/pkill/pkill.1 b/bin/pkill/pkill.1 index 3f219b69a0f3..70b791213895 100644 --- a/bin/pkill/pkill.1 +++ b/bin/pkill/pkill.1 @@ -29,7 +29,7 @@ .\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE .\" POSSIBILITY OF SUCH DAMAGE. .\" -.Dd August 9, 2013 +.Dd August 21, 2015 .Dt PKILL 1 .Os .Sh NAME @@ -47,7 +47,7 @@ .Op Fl c Ar class .Op Fl d Ar delim .Op Fl g Ar pgrp -.Op Fl j Ar jid +.Op Fl j Ar jail .Op Fl s Ar sid .Op Fl t Ar tty .Op Fl u Ar euid @@ -63,7 +63,7 @@ .Op Fl U Ar uid .Op Fl c Ar class .Op Fl g Ar pgrp -.Op Fl j Ar jid +.Op Fl j Ar jail .Op Fl s Ar sid .Op Fl t Ar tty .Op Fl u Ar euid @@ -149,16 +149,16 @@ or command. .It Fl i Ignore case distinctions in both the process table and the supplied pattern. -.It Fl j Ar jid -Restrict matches to processes inside jails with a jail ID in the comma-separated -list -.Ar jid . -The value +.It Fl j Ar jail +Restrict matches to processes inside the specified jails. +The argument +.Ar jail +may be .Dq Li any -matches processes in any jail. -The value +to match processes in any jail, .Dq Li none -matches processes not in jail. +to match processes not in jail, +or a comma-separated list of jail IDs or names. .It Fl l Long output. For diff --git a/bin/pkill/pkill.c b/bin/pkill/pkill.c index e73f5f258e3d..8c814ce4b0f7 100644 --- a/bin/pkill/pkill.c +++ b/bin/pkill/pkill.c @@ -59,6 +59,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #define STATUS_MATCH 0 #define STATUS_NOMATCH 1 @@ -78,7 +79,7 @@ enum listtype { LT_GROUP, LT_TTY, LT_PGRP, - LT_JID, + LT_JAIL, LT_SID, LT_CLASS }; @@ -245,7 +246,7 @@ main(int argc, char **argv) cflags |= REG_ICASE; break; case 'j': - makelist(&jidlist, LT_JID, optarg); + makelist(&jidlist, LT_JAIL, optarg); criteria = 1; break; case 'l': @@ -585,7 +586,7 @@ usage(void) fprintf(stderr, "usage: %s %s [-F pidfile] [-G gid] [-M core] [-N system]\n" - " [-P ppid] [-U uid] [-c class] [-g pgrp] [-j jid]\n" + " [-P ppid] [-U uid] [-c class] [-g pgrp] [-j jail]\n" " [-s sid] [-t tty] [-u euid] pattern ...\n", getprogname(), ustr); @@ -700,7 +701,7 @@ makelist(struct listhead *head, enum listtype type, char *src) if (li->li_number == 0) li->li_number = getsid(mypid); break; - case LT_JID: + case LT_JAIL: if (li->li_number < 0) errx(STATUS_BADUSAGE, "Negative jail ID `%s'", sp); @@ -766,15 +767,20 @@ foundtty: if ((st.st_mode & S_IFCHR) == 0) li->li_number = st.st_rdev; break; - case LT_JID: + case LT_JAIL: { + int jid; + if (strcmp(sp, "none") == 0) li->li_number = 0; else if (strcmp(sp, "any") == 0) li->li_number = -1; + else if ((jid = jail_getid(sp)) != -1) + li->li_number = jid; else if (*ep != '\0') errx(STATUS_BADUSAGE, - "Invalid jail ID `%s'", sp); + "Invalid jail ID or name `%s'", sp); break; + } case LT_CLASS: li->li_number = -1; li->li_name = strdup(sp); diff --git a/bin/pkill/tests/pgrep-j_test.sh b/bin/pkill/tests/pgrep-j_test.sh index e24e5c2ff5fe..1d6281368075 100644 --- a/bin/pkill/tests/pgrep-j_test.sh +++ b/bin/pkill/tests/pgrep-j_test.sh @@ -14,7 +14,7 @@ if [ `id -u` -ne 0 ]; then exit 0 fi -echo "1..3" +echo "1..4" sleep=$(pwd)/sleep.txt ln -sf /bin/sleep $sleep @@ -87,5 +87,30 @@ else fi [ -f ${PWD}/${base}_3_1.pid ] && kill $(cat $PWD/${base}_3_1.pid) [ -f ${PWD}/${base}_3_2.pid ] && kill $(cat $PWD/${base}_3_2.pid) +wait + +# test 4 is like test 1 except with jname instead of jid. +name="pgrep -j " +sleep_amount=8 +jail -c path=/ name=${base}_4_1 ip4.addr=127.0.0.1 \ + command=daemon -p ${PWD}/${base}_4_1.pid $sleep $sleep_amount & + +jail -c path=/ name=${base}_4_2 ip4.addr=127.0.0.1 \ + command=daemon -p ${PWD}/${base}_4_2.pid $sleep $sleep_amount & + +sleep 0.5 + +jname="${base}_4_1,${base}_4_2" +pid1="$(pgrep -f -x -j "$jname" "$sleep $sleep_amount" | sort)" +pid2=$(printf "%s\n%s" "$(cat ${PWD}/${base}_4_1.pid)" \ + $(cat ${PWD}/${base}_4_2.pid) | sort) +if [ "$pid1" = "$pid2" ]; then + echo "ok 4 - $name" +else + echo "not ok 4 - $name # pgrep output: '$(echo $pid1)', pidfile output: '$(echo $pid2)'" +fi +[ -f ${PWD}/${base}_4_1.pid ] && kill $(cat ${PWD}/${base}_4_1.pid) +[ -f ${PWD}/${base}_4_2.pid ] && kill $(cat ${PWD}/${base}_4_2.pid) +wait rm -f $sleep diff --git a/bin/pkill/tests/pkill-j_test.sh b/bin/pkill/tests/pkill-j_test.sh index 26e185fc8988..0af24cfc86fa 100644 --- a/bin/pkill/tests/pkill-j_test.sh +++ b/bin/pkill/tests/pkill-j_test.sh @@ -14,7 +14,7 @@ if [ `id -u` -ne 0 ]; then exit 0 fi -echo "1..3" +echo "1..4" sleep=$(pwd)/sleep.txt ln -sf /bin/sleep $sleep @@ -90,5 +90,31 @@ else fi 2>/dev/null [ -f ${PWD}/${base}_3_1.pid ] && kill $(cat ${base}_3_1.pid) [ -f ${PWD}/${base}_3_2.pid ] && kill $(cat ${base}_3_2.pid) +wait + +# test 4 is like test 1 except with jname instead of jid. +name="pkill -j " +sleep_amount=8 +jail -c path=/ name=${base}_4_1 ip4.addr=127.0.0.1 \ + command=daemon -p ${PWD}/${base}_4_1.pid $sleep $sleep_amount & + +jail -c path=/ name=${base}_4_2 ip4.addr=127.0.0.1 \ + command=daemon -p ${PWD}/${base}_4_2.pid $sleep $sleep_amount & + +$sleep $sleep_amount & + +sleep 0.5 + +jname="${base}_4_1,${base}_4_2" +if pkill -f -j "$jname" $sleep && sleep 0.5 && + ! -f ${PWD}/${base}_4_1.pid && + ! -f ${PWD}/${base}_4_2.pid ; then + echo "ok 4 - $name" +else + echo "not ok 4 - $name" +fi 2>/dev/null +[ -f ${PWD}/${base}_4_1.pid ] && kill $(cat ${PWD}/${base}_4_1.pid) +[ -f ${PWD}/${base}_4_2.pid ] && kill $(cat ${PWD}/${base}_4_2.pid) +wait rm -f $sleep diff --git a/bin/ps/Makefile b/bin/ps/Makefile index 79e9fc6cf0ec..5177832131a5 100644 --- a/bin/ps/Makefile +++ b/bin/ps/Makefile @@ -11,6 +11,6 @@ SRCS= fmt.c keyword.c nlist.c print.c ps.c # on large systems. # CFLAGS+=-DLAZY_PS -LIBADD= m kvm jail xo +LIBADD= m kvm jail xo util .include diff --git a/bin/rm/rm.c b/bin/rm/rm.c index 46807b9e4e58..d91af54a5c12 100644 --- a/bin/rm/rm.c +++ b/bin/rm/rm.c @@ -50,6 +50,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -86,6 +87,8 @@ main(int argc, char *argv[]) int ch; char *p; + (void)setlocale(LC_ALL, ""); + /* * Test for the special case where the utility is called as * "unlink", for which the functionality provided is greatly diff --git a/bin/setfacl/setfacl.1 b/bin/setfacl/setfacl.1 index 7894233c0f58..a310c6403daa 100644 --- a/bin/setfacl/setfacl.1 +++ b/bin/setfacl/setfacl.1 @@ -26,7 +26,7 @@ .\" .\" $FreeBSD$ .\" -.Dd April 1, 2013 +.Dd September 4, 2015 .Dt SETFACL 1 .Os .Sh NAME @@ -378,9 +378,11 @@ dir_inherit inherit_only .It n no_propagate +.It I +inherited .El .Pp -Inheritance flags may be only set on directories. +Other than the "inherited" flag, inheritance flags may be only set on directories. .It Ar "ACL type" The ACL type field is either .Dq Li allow diff --git a/bin/sh/expand.c b/bin/sh/expand.c index 193d65177e74..7411f1cc97f5 100644 --- a/bin/sh/expand.c +++ b/bin/sh/expand.c @@ -886,7 +886,7 @@ varvalue(const char *name, int quoted, int subtype, int flag) num = backgndpidval(); break; case '-': - for (i = 0 ; i < NOPTS ; i++) { + for (i = 0 ; i < NSHORTOPTS ; i++) { if (optlist[i].val) STPUTC(optlist[i].letter, expdest); } @@ -1464,21 +1464,11 @@ patmatch(const char *pattern, const char *string, int squoted) bt_q = q; break; case '[': { - const char *endp; + const char *savep, *saveq; int invert, found; wchar_t chr; - endp = p; - if (*endp == '!' || *endp == '^') - endp++; - do { - while (*endp == CTLQUOTEMARK) - endp++; - if (*endp == 0) - goto dft; /* no matching ] */ - if (*endp == CTLESC) - endp++; - } while (*++endp != ']'); + savep = p, saveq = q; invert = 0; if (*p == '!' || *p == '^') { invert++; @@ -1497,6 +1487,11 @@ patmatch(const char *pattern, const char *string, int squoted) chr = (unsigned char)*q++; c = *p++; do { + if (c == '\0') { + p = savep, q = saveq; + c = '['; + goto dft; + } if (c == CTLQUOTEMARK) continue; if (c == '[' && *p == ':') { diff --git a/bin/sh/miscbltin.c b/bin/sh/miscbltin.c index 715e324c6832..4de12760b54e 100644 --- a/bin/sh/miscbltin.c +++ b/bin/sh/miscbltin.c @@ -100,6 +100,7 @@ readcmd(int argc __unused, char **argv __unused) int i; int is_ifs; int saveall = 0; + ptrdiff_t lastnonifs, lastnonifsws; struct timeval tv; char *tvptr; fd_set ifds; @@ -169,6 +170,7 @@ readcmd(int argc __unused, char **argv __unused) startword = 2; backslash = 0; STARTSTACKSTR(p); + lastnonifs = lastnonifsws = -1; for (;;) { nread = read(STDIN_FILENO, &c, 1); if (nread == -1) { @@ -191,9 +193,11 @@ readcmd(int argc __unused, char **argv __unused) CHECKSTRSPACE(1, p); if (backslash) { backslash = 0; - startword = 0; - if (c != '\n') + if (c != '\n') { + startword = 0; + lastnonifs = lastnonifsws = p - stackblock(); USTPUTC(c, p); + } continue; } if (!rflag && c == '\\') { @@ -217,8 +221,10 @@ readcmd(int argc __unused, char **argv __unused) if (is_ifs == 2 && startword == 1) { /* Only one non-whitespace IFS per word */ startword = 2; - if (saveall) + if (saveall) { + lastnonifsws = p - stackblock(); USTPUTC(c, p); + } continue; } } @@ -229,6 +235,7 @@ readcmd(int argc __unused, char **argv __unused) if (saveall) /* Not just a spare terminator */ saveall++; + lastnonifs = lastnonifsws = p - stackblock(); USTPUTC(c, p); continue; } @@ -239,6 +246,8 @@ readcmd(int argc __unused, char **argv __unused) if (ap[1] == NULL) { /* Last variable needs all IFS chars */ saveall++; + if (is_ifs == 2) + lastnonifsws = p - stackblock(); USTPUTC(c, p); continue; } @@ -247,20 +256,17 @@ readcmd(int argc __unused, char **argv __unused) setvar(*ap, stackblock(), 0); ap++; STARTSTACKSTR(p); + lastnonifs = lastnonifsws = -1; } STACKSTRNUL(p); - /* Remove trailing IFS chars */ - for (; stackblock() <= --p; *p = 0) { - if (!strchr(ifs, *p)) - break; - if (strchr(" \t\n", *p)) - /* Always remove whitespace */ - continue; - if (saveall > 1) - /* Don't remove non-whitespace unless it was naked */ - break; - } + /* + * Remove trailing IFS chars: always remove whitespace, don't remove + * non-whitespace unless it was naked + */ + if (saveall <= 1) + lastnonifsws = lastnonifs; + stackblock()[lastnonifsws + 1] = '\0'; setvar(*ap, stackblock(), 0); /* Set any remaining args to "" */ diff --git a/bin/sh/options.c b/bin/sh/options.c index 2d0ddce5a3de..d1312005371c 100644 --- a/bin/sh/options.c +++ b/bin/sh/options.c @@ -73,6 +73,7 @@ char *minusc; /* argument to -c option */ static void options(int); static void minus_o(char *, int); static void setoption(int, int); +static void setoptionbyindex(int, int); static int getopts(char *, char *, char **, char ***, char **); @@ -269,7 +270,7 @@ minus_o(char *name, int val) } else { for (i = 0; i < NOPTS; i++) if (equal(name, optlist[i].name)) { - setoption(optlist[i].letter, val); + setoptionbyindex(i, val); return; } error("Illegal option -o %s", name); @@ -278,26 +279,32 @@ minus_o(char *name, int val) static void -setoption(int flag, int val) +setoptionbyindex(int idx, int val) { - int i; - - if (flag == 'p' && !val && privileged) { + if (optlist[idx].letter == 'p' && !val && privileged) { if (setgid(getgid()) == -1) error("setgid"); if (setuid(getuid()) == -1) error("setuid"); } - for (i = 0; i < NOPTS; i++) + optlist[idx].val = val; + if (val) { + /* #%$ hack for ksh semantics */ + if (optlist[idx].letter == 'V') + Eflag = 0; + else if (optlist[idx].letter == 'E') + Vflag = 0; + } +} + +static void +setoption(int flag, int val) +{ + int i; + + for (i = 0; i < NSHORTOPTS; i++) if (optlist[i].letter == flag) { - optlist[i].val = val; - if (val) { - /* #%$ hack for ksh semantics */ - if (flag == 'V') - Eflag = 0; - else if (flag == 'E') - Vflag = 0; - } + setoptionbyindex(i, val); return; } error("Illegal option -%c", flag); diff --git a/bin/sh/options.h b/bin/sh/options.h index 2048a354b6e5..b5cd67f4dea6 100644 --- a/bin/sh/options.h +++ b/bin/sh/options.h @@ -64,8 +64,10 @@ struct shparam { #define Tflag optlist[16].val #define Pflag optlist[17].val #define hflag optlist[18].val +#define nologflag optlist[19].val -#define NOPTS 19 +#define NSHORTOPTS 19 +#define NOPTS 20 struct optent { const char *name; @@ -95,6 +97,7 @@ struct optent optlist[NOPTS] = { { "trapsasync", 'T', 0 }, { "physical", 'P', 0 }, { "trackall", 'h', 0 }, + { "nolog", '\0', 0 }, }; #endif diff --git a/bin/sh/parser.c b/bin/sh/parser.c index 2bba84eb9136..302d1797c194 100644 --- a/bin/sh/parser.c +++ b/bin/sh/parser.c @@ -106,6 +106,8 @@ static int startlinno; /* line # where last token started */ static int funclinno; /* line # where the current function started */ static struct parser_temp *parser_temp; +#define NOEOFMARK ((const char *)&heredoclist) + static union node *list(int); static union node *andor(void); @@ -972,6 +974,10 @@ checkend(int c, const char *eofmark, int striptabs) pungetc(); pushstring(eofmark + 1, q - (eofmark + 1), NULL); } + } else if (c == '\n' && *eofmark == '\0') { + c = PEOF; + plinno++; + needprompt = doprompt; } return (c); } @@ -1195,7 +1201,8 @@ parsebackq(char *out, struct nodelist **pbqlist, static char * readcstyleesc(char *out) { - int c, v, i, n; + int c, vc, i, n; + unsigned int v; c = pgetc(); switch (c) { @@ -1310,12 +1317,12 @@ readcstyleesc(char *out) default: synerror("Bad escape sequence"); } - v = (char)v; + vc = (char)v; /* * We can't handle NUL bytes. * POSIX says we should skip till the closing quote. */ - if (v == '\0') { + if (vc == '\0') { while ((c = pgetc()) != '\'') { if (c == '\\') c = pgetc(); @@ -1332,9 +1339,9 @@ readcstyleesc(char *out) pungetc(); return out; } - if (SQSYNTAX[v] == CCTL) + if (SQSYNTAX[vc] == CCTL) USTPUTC(CTLESC, out); - USTPUTC(v, out); + USTPUTC(vc, out); return out; } @@ -1382,7 +1389,7 @@ readtoken1(int firstc, char const *initialsyntax, const char *eofmark, STARTSTACKSTR(out); loop: { /* for each line, until end of word */ - if (eofmark) + if (eofmark && eofmark != NOEOFMARK) /* set c to PEOF if at end of here document */ c = checkend(c, eofmark, striptabs); for (;;) { /* until end of line or end of word */ @@ -1661,7 +1668,7 @@ parsesub: { pungetc(); else if (c == '\n' || c == PEOF) synerror("Unexpected end of line in substitution"); - else + else if (BASESYNTAX[c] != CCTL) USTPUTC(c, out); } if (subtype == 0) { @@ -1677,7 +1684,8 @@ parsesub: { synerror("Unexpected end of line in substitution"); if (flags == VSNUL) STPUTC(':', out); - STPUTC(c, out); + if (BASESYNTAX[c] != CCTL) + STPUTC(c, out); subtype = VSERROR; } else subtype = p - types + VSNORMAL; @@ -2044,7 +2052,7 @@ expandstr(const char *ps) parser_temp = NULL; setinputstring(ps, 1); doprompt = 0; - readtoken1(pgetc(), DQSYNTAX, "", 0); + readtoken1(pgetc(), DQSYNTAX, NOEOFMARK, 0); if (backquotelist != NULL) error("Command substitution not allowed here"); diff --git a/bin/sh/sh.1 b/bin/sh/sh.1 index e193d5da03c6..14ae89833c8a 100644 --- a/bin/sh/sh.1 +++ b/bin/sh/sh.1 @@ -32,7 +32,7 @@ .\" from: @(#)sh.1 8.6 (Berkeley) 5/4/95 .\" $FreeBSD$ .\" -.Dd July 11, 2015 +.Dd August 29, 2015 .Dt SH 1 .Os .Sh NAME @@ -343,6 +343,11 @@ Write each command variable subjected to parameter expansion and arithmetic expansion) to standard error before it is executed. Useful for debugging. +.It nolog +Another do-nothing option for +.Tn POSIX +compliance. +It only has a long name. .El .Pp The @@ -1173,7 +1178,9 @@ The only special parameter that can be made local is .Ql - . Making .Ql - -local causes any shell options that are +local causes any shell options +(including those that only have long names) +that are changed via the .Ic set command inside the function to be diff --git a/bin/sh/tests/builtins/Makefile b/bin/sh/tests/builtins/Makefile index ad39aacf1be5..46a0b4159a1a 100644 --- a/bin/sh/tests/builtins/Makefile +++ b/bin/sh/tests/builtins/Makefile @@ -39,6 +39,7 @@ FILES+= case16.0 FILES+= case17.0 FILES+= case18.0 FILES+= case19.0 +FILES+= case20.0 FILES+= cd1.0 FILES+= cd2.0 FILES+= cd3.0 @@ -121,6 +122,8 @@ FILES+= read4.0 read4.0.stdout FILES+= read5.0 FILES+= read6.0 FILES+= read7.0 +FILES+= read8.0 +FILES+= read9.0 FILES+= return1.0 FILES+= return2.1 FILES+= return3.1 diff --git a/bin/sh/tests/builtins/case20.0 b/bin/sh/tests/builtins/case20.0 new file mode 100644 index 000000000000..03a4eb2c9a23 --- /dev/null +++ b/bin/sh/tests/builtins/case20.0 @@ -0,0 +1,9 @@ +# $FreeBSD$ + +# Shells do not agree about what this pattern should match, but it is +# certain that it must not crash and the missing close bracket must not +# be simply ignored. + +case B in +[[:alpha:]) echo bad ;; +esac diff --git a/bin/sh/tests/builtins/read8.0 b/bin/sh/tests/builtins/read8.0 new file mode 100644 index 000000000000..fb786ff008b9 --- /dev/null +++ b/bin/sh/tests/builtins/read8.0 @@ -0,0 +1,17 @@ +# $FreeBSD$ + +read a b c <<\EOF +\ +A\ + \ + \ + \ +B\ + \ + \ +C\ + \ + \ + \ +EOF +[ "$a.$b.$c" = "A.B.C" ] diff --git a/bin/sh/tests/builtins/read9.0 b/bin/sh/tests/builtins/read9.0 new file mode 100644 index 000000000000..080549889839 --- /dev/null +++ b/bin/sh/tests/builtins/read9.0 @@ -0,0 +1,10 @@ +# $FreeBSD$ + +empty='' +read a b c <&1 >/dev/null) +[ $? -ne 0 ] && [ -n "$v" ] diff --git a/bin/sh/tests/parser/dollar-quote13.0 b/bin/sh/tests/parser/dollar-quote13.0 new file mode 100644 index 000000000000..2247da7abbc9 --- /dev/null +++ b/bin/sh/tests/parser/dollar-quote13.0 @@ -0,0 +1,8 @@ +# $FreeBSD$ + +# This Unicode escape sequence that has never been in range should either +# fail to expand or expand to a fallback. + +c=$(eval printf %s \$\'\\Uffffff41\' 2>/dev/null) +r=$(($? != 0)) +[ "$r.$c" = '1.' ] || [ "$r.$c" = '0.?' ] || [ "$r.$c" = $'0.\u2222' ] diff --git a/bin/sh/tests/parser/heredoc13.0 b/bin/sh/tests/parser/heredoc13.0 new file mode 100644 index 000000000000..225d4f08f492 --- /dev/null +++ b/bin/sh/tests/parser/heredoc13.0 @@ -0,0 +1,21 @@ +# $FreeBSD$ + +failures=0 + +check() { + if ! eval "[ $* ]"; then + echo "Failed: $*" + : $((failures += 1)) + fi +} + +check '"$(cat <<"" + +echo yes)" = "yes"' + +check '"$(cat <<"" +yes + +)" = "yes"' + +exit $((failures != 0)) diff --git a/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/funcs/err.D_PROTO_LEN.motoofew.d b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/funcs/err.D_PROTO_LEN.motoofew.d index cf4dd5e0e550..823a98d4dd3e 100644 --- a/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/funcs/err.D_PROTO_LEN.motoofew.d +++ b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/funcs/err.D_PROTO_LEN.motoofew.d @@ -34,7 +34,7 @@ * */ -lockstat:kernel:mtx_lock:adaptive-acquire +lockstat:::adaptive-acquire { mutex_owned(); exit(1); diff --git a/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/funcs/err.D_PROTO_LEN.motoomany.d b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/funcs/err.D_PROTO_LEN.motoomany.d index 6cc4be03fa30..37637c052b20 100644 --- a/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/funcs/err.D_PROTO_LEN.motoomany.d +++ b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/funcs/err.D_PROTO_LEN.motoomany.d @@ -34,7 +34,7 @@ * */ -lockstat:kernel:mtx_lock:adaptive-acquire +lockstat:::adaptive-acquire { mutex_owned((kmutex_t *)arg0, 99); exit(1); diff --git a/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/funcs/err.D_PROTO_LEN.mtatoofew.d b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/funcs/err.D_PROTO_LEN.mtatoofew.d index 61d967a5b470..2d299d4b7312 100644 --- a/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/funcs/err.D_PROTO_LEN.mtatoofew.d +++ b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/funcs/err.D_PROTO_LEN.mtatoofew.d @@ -36,7 +36,7 @@ */ -lockstat:kernel:mtx_lock:adaptive-acquire +lockstat:::adaptive-acquire { mutex_type_adaptive(); exit(1); diff --git a/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/funcs/err.D_PROTO_LEN.mtatoomany.d b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/funcs/err.D_PROTO_LEN.mtatoomany.d index f2c3178e31c6..42ae0168644a 100644 --- a/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/funcs/err.D_PROTO_LEN.mtatoomany.d +++ b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/funcs/err.D_PROTO_LEN.mtatoomany.d @@ -35,7 +35,7 @@ */ -lockstat:kernel:mtx_lock:adaptive-acquire +lockstat:::adaptive-acquire { mutex_type_adaptive((kmutex_t *)arg0, 99); exit(1); diff --git a/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/funcs/tst.mutex_owner.d b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/funcs/tst.mutex_owner.d index dbb10c3fd598..0784eda4d775 100644 --- a/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/funcs/tst.mutex_owner.d +++ b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/funcs/tst.mutex_owner.d @@ -48,7 +48,7 @@ BEGIN i = 0; } -lockstat::mtx_lock:adaptive-acquire +lockstat:::adaptive-acquire { ptr = mutex_owner((struct mtx *)arg0); diff --git a/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/funcs/tst.mutex_type_adaptive.d b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/funcs/tst.mutex_type_adaptive.d index ac43e790b7b3..f953d2407374 100644 --- a/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/funcs/tst.mutex_type_adaptive.d +++ b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/funcs/tst.mutex_type_adaptive.d @@ -44,7 +44,7 @@ BEGIN ret = -99; } -mtx_lock:adaptive-acquire +lockstat:::adaptive-acquire { ret = mutex_type_adaptive((struct mtx *)arg0); i++; diff --git a/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/ip/get.ipv4remote.pl b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/ip/get.ipv4remote.pl index ccc247dec514..5f58eb8e0ca8 100755 --- a/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/ip/get.ipv4remote.pl +++ b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/ip/get.ipv4remote.pl @@ -1,4 +1,4 @@ -#!/usr/bin/perl -w +#!/usr/bin/env perl # # CDDL HEADER START # diff --git a/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/ip/get.ipv6remote.pl b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/ip/get.ipv6remote.pl index 35bea8e58fa1..fbfcdfdab35f 100755 --- a/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/ip/get.ipv6remote.pl +++ b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/ip/get.ipv6remote.pl @@ -1,4 +1,4 @@ -#!/usr/bin/perl -w +#!/usr/bin/env perl # # CDDL HEADER START # diff --git a/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/ip/tst.ipv4localtcp.ksh b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/ip/tst.ipv4localtcp.ksh index 2e3ffec52ba6..1d2a99237068 100755 --- a/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/ip/tst.ipv4localtcp.ksh +++ b/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/ip/tst.ipv4localtcp.ksh @@ -58,12 +58,25 @@ fi dtrace=$1 local=127.0.0.1 -tcpport=22 DIR=/var/tmp/dtest.$$ +tcpport=1024 +bound=5000 +while [ $tcpport -lt $bound ]; do + nc -z $local $tcpport >/dev/null || break + tcpport=$(($tcpport + 1)) +done +if [ $tcpport -eq $bound ]; then + echo "couldn't find an available TCP port" + exit 1 +fi + mkdir $DIR cd $DIR +# nc will exit when the connection is closed. +nc -l $local $tcpport & + cat > test.pl <<-EOPERL use IO::Socket; my \$s = IO::Socket::INET->new( @@ -76,7 +89,7 @@ cat > test.pl <<-EOPERL sleep(2); EOPERL -$dtrace -c '/usr/bin/perl test.pl' -qs /dev/stdin < test.pl <<-EOPERL sleep(2); EOPERL -$dtrace -c '/usr/bin/perl test.pl' -qs /dev/stdin < test.pl <<-EOPERL sleep(2); EOPERL -$dtrace -c '/usr/bin/perl test.pl' -qs /dev/stdin < test.pl <<-EOPERL sleep(2); EOPERL -$dtrace -c '/usr/bin/perl test.pl' -qs /dev/stdin < @@ -95,6 +95,8 @@ static int zopt_objects = 0; static libzfs_handle_t *g_zfs; static uint64_t max_inflight = 1000; +static void snprintf_blkptr_compact(char *, size_t, const blkptr_t *); + /* * These libumem hooks provide a reasonable set of defaults for the allocator's * debugging facilities. @@ -418,6 +420,79 @@ dump_zap(objset_t *os, uint64_t object, void *data, size_t size) zap_cursor_fini(&zc); } +static void +dump_bpobj(objset_t *os, uint64_t object, void *data, size_t size) +{ + bpobj_phys_t *bpop = data; + char bytes[32], comp[32], uncomp[32]; + + if (bpop == NULL) + return; + + zdb_nicenum(bpop->bpo_bytes, bytes); + zdb_nicenum(bpop->bpo_comp, comp); + zdb_nicenum(bpop->bpo_uncomp, uncomp); + + (void) printf("\t\tnum_blkptrs = %llu\n", + (u_longlong_t)bpop->bpo_num_blkptrs); + (void) printf("\t\tbytes = %s\n", bytes); + if (size >= BPOBJ_SIZE_V1) { + (void) printf("\t\tcomp = %s\n", comp); + (void) printf("\t\tuncomp = %s\n", uncomp); + } + if (size >= sizeof (*bpop)) { + (void) printf("\t\tsubobjs = %llu\n", + (u_longlong_t)bpop->bpo_subobjs); + (void) printf("\t\tnum_subobjs = %llu\n", + (u_longlong_t)bpop->bpo_num_subobjs); + } + + if (dump_opt['d'] < 5) + return; + + for (uint64_t i = 0; i < bpop->bpo_num_blkptrs; i++) { + char blkbuf[BP_SPRINTF_LEN]; + blkptr_t bp; + + int err = dmu_read(os, object, + i * sizeof (bp), sizeof (bp), &bp, 0); + if (err != 0) { + (void) printf("got error %u from dmu_read\n", err); + break; + } + snprintf_blkptr_compact(blkbuf, sizeof (blkbuf), &bp); + (void) printf("\t%s\n", blkbuf); + } +} + +/* ARGSUSED */ +static void +dump_bpobj_subobjs(objset_t *os, uint64_t object, void *data, size_t size) +{ + dmu_object_info_t doi; + + VERIFY0(dmu_object_info(os, object, &doi)); + uint64_t *subobjs = kmem_alloc(doi.doi_max_offset, KM_SLEEP); + + int err = dmu_read(os, object, 0, doi.doi_max_offset, subobjs, 0); + if (err != 0) { + (void) printf("got error %u from dmu_read\n", err); + kmem_free(subobjs, doi.doi_max_offset); + return; + } + + int64_t last_nonzero = -1; + for (uint64_t i = 0; i < doi.doi_max_offset / 8; i++) { + if (subobjs[i] != 0) + last_nonzero = i; + } + + for (int64_t i = 0; i <= last_nonzero; i++) { + (void) printf("\t%llu\n", (longlong_t)subobjs[i]); + } + kmem_free(subobjs, doi.doi_max_offset); +} + /*ARGSUSED*/ static void dump_ddt_zap(objset_t *os, uint64_t object, void *data, size_t size) @@ -1397,7 +1472,7 @@ dump_bpobj_cb(void *arg, const blkptr_t *bp, dmu_tx_t *tx) } static void -dump_bpobj(bpobj_t *bpo, char *name, int indent) +dump_full_bpobj(bpobj_t *bpo, char *name, int indent) { char bytes[32]; char comp[32]; @@ -1411,11 +1486,12 @@ dump_bpobj(bpobj_t *bpo, char *name, int indent) zdb_nicenum(bpo->bpo_phys->bpo_comp, comp); zdb_nicenum(bpo->bpo_phys->bpo_uncomp, uncomp); (void) printf(" %*s: object %llu, %llu local blkptrs, " - "%llu subobjs, %s (%s/%s comp)\n", + "%llu subobjs in object %llu, %s (%s/%s comp)\n", indent * 8, name, (u_longlong_t)bpo->bpo_object, (u_longlong_t)bpo->bpo_phys->bpo_num_blkptrs, (u_longlong_t)bpo->bpo_phys->bpo_num_subobjs, + (u_longlong_t)bpo->bpo_phys->bpo_subobjs, bytes, comp, uncomp); for (uint64_t i = 0; i < bpo->bpo_phys->bpo_num_subobjs; i++) { @@ -1432,7 +1508,7 @@ dump_bpobj(bpobj_t *bpo, char *name, int indent) error, (u_longlong_t)subobj); continue; } - dump_bpobj(&subbpo, "subobj", indent + 1); + dump_full_bpobj(&subbpo, "subobj", indent + 1); bpobj_close(&subbpo); } } else { @@ -1466,7 +1542,7 @@ dump_deadlist(dsl_deadlist_t *dl) return; if (dl->dl_oldfmt) { - dump_bpobj(&dl->dl_bpobj, "old-format deadlist", 0); + dump_full_bpobj(&dl->dl_bpobj, "old-format deadlist", 0); return; } @@ -1491,7 +1567,7 @@ dump_deadlist(dsl_deadlist_t *dl) (void) snprintf(buf, sizeof (buf), "mintxg %llu -> " "obj %llu", (longlong_t)dle->dle_mintxg, (longlong_t)dle->dle_bpobj.bpo_object); - dump_bpobj(&dle->dle_bpobj, buf, 0); + dump_full_bpobj(&dle->dle_bpobj, buf, 0); } else { (void) printf("mintxg %llu -> obj %llu\n", (longlong_t)dle->dle_mintxg, @@ -1682,8 +1758,8 @@ static object_viewer_t *object_viewer[DMU_OT_NUMTYPES + 1] = { dump_uint64, /* object array */ dump_none, /* packed nvlist */ dump_packed_nvlist, /* packed nvlist size */ - dump_none, /* bplist */ - dump_none, /* bplist header */ + dump_none, /* bpobj */ + dump_bpobj, /* bpobj header */ dump_none, /* SPA space map header */ dump_none, /* SPA space map */ dump_none, /* ZIL intent log */ @@ -1730,7 +1806,7 @@ static object_viewer_t *object_viewer[DMU_OT_NUMTYPES + 1] = { dump_zap, /* deadlist */ dump_none, /* deadlist hdr */ dump_zap, /* dsl clones */ - dump_none, /* bpobj subobjs */ + dump_bpobj_subobjs, /* bpobj subobjs */ dump_unknown, /* Unknown type, must be last */ }; @@ -2145,7 +2221,7 @@ dump_label(const char *dev) (void) close(fd); } -static uint64_t num_large_blocks; +static uint64_t dataset_feature_count[SPA_FEATURES]; /*ARGSUSED*/ static int @@ -2159,8 +2235,15 @@ dump_one_dir(const char *dsname, void *arg) (void) printf("Could not open %s, error %d\n", dsname, error); return (0); } - if (dmu_objset_ds(os)->ds_large_blocks) - num_large_blocks++; + + for (spa_feature_t f = 0; f < SPA_FEATURES; f++) { + if (!dmu_objset_ds(os)->ds_feature_inuse[f]) + continue; + ASSERT(spa_feature_table[f].fi_flags & + ZFEATURE_FLAG_PER_DATASET); + dataset_feature_count[f]++; + } + dump_dir(os); dmu_objset_disown(os, FTAG); fuid_table_destroy(); @@ -2352,6 +2435,9 @@ zdb_blkptr_cb(spa_t *spa, zilog_t *zilog, const blkptr_t *bp, dmu_object_type_t type; boolean_t is_metadata; + if (bp == NULL) + return (0); + if (dump_opt['b'] >= 5 && bp->blk_birth > 0) { char blkbuf[BP_SPRINTF_LEN]; snprintf_blkptr(blkbuf, sizeof (blkbuf), bp); @@ -2841,7 +2927,7 @@ zdb_ddt_add_cb(spa_t *spa, zilog_t *zilog, const blkptr_t *bp, avl_index_t where; zdb_ddt_entry_t *zdde, zdde_search; - if (BP_IS_HOLE(bp) || BP_IS_EMBEDDED(bp)) + if (bp == NULL || BP_IS_HOLE(bp) || BP_IS_EMBEDDED(bp)) return (0); if (dump_opt['S'] > 1 && zb->zb_level == ZB_ROOT_LEVEL) { @@ -2956,13 +3042,13 @@ dump_zpool(spa_t *spa) dump_metaslab_groups(spa); if (dump_opt['d'] || dump_opt['i']) { - uint64_t refcount; dump_dir(dp->dp_meta_objset); if (dump_opt['d'] >= 3) { - dump_bpobj(&spa->spa_deferred_bpobj, + dump_full_bpobj(&spa->spa_deferred_bpobj, "Deferred frees", 0); if (spa_version(spa) >= SPA_VERSION_DEADLISTS) { - dump_bpobj(&spa->spa_dsl_pool->dp_free_bpobj, + dump_full_bpobj( + &spa->spa_dsl_pool->dp_free_bpobj, "Pool snapshot frees", 0); } @@ -2977,17 +3063,29 @@ dump_zpool(spa_t *spa) (void) dmu_objset_find(spa_name(spa), dump_one_dir, NULL, DS_FIND_SNAPSHOTS | DS_FIND_CHILDREN); - (void) feature_get_refcount(spa, - &spa_feature_table[SPA_FEATURE_LARGE_BLOCKS], &refcount); - if (num_large_blocks != refcount) { - (void) printf("large_blocks feature refcount mismatch: " - "expected %lld != actual %lld\n", - (longlong_t)num_large_blocks, - (longlong_t)refcount); - rc = 2; - } else { - (void) printf("Verified large_blocks feature refcount " - "is correct (%llu)\n", (longlong_t)refcount); + for (spa_feature_t f = 0; f < SPA_FEATURES; f++) { + uint64_t refcount; + + if (!(spa_feature_table[f].fi_flags & + ZFEATURE_FLAG_PER_DATASET)) { + ASSERT0(dataset_feature_count[f]); + continue; + } + (void) feature_get_refcount(spa, + &spa_feature_table[f], &refcount); + if (dataset_feature_count[f] != refcount) { + (void) printf("%s feature refcount mismatch: " + "%lld datasets != %lld refcount\n", + spa_feature_table[f].fi_uname, + (longlong_t)dataset_feature_count[f], + (longlong_t)refcount); + rc = 2; + } else { + (void) printf("Verified %s feature refcount " + "of %llu is correct\n", + spa_feature_table[f].fi_uname, + (longlong_t)refcount); + } } } if (rc == 0 && (dump_opt['b'] || dump_opt['c'])) diff --git a/cddl/contrib/opensolaris/cmd/zfs/zfs.8 b/cddl/contrib/opensolaris/cmd/zfs/zfs.8 index f2852cfc84a3..4da6d0b179d8 100644 --- a/cddl/contrib/opensolaris/cmd/zfs/zfs.8 +++ b/cddl/contrib/opensolaris/cmd/zfs/zfs.8 @@ -27,11 +27,11 @@ .\" Copyright (c) 2013, Steven Hartland .\" Copyright (c) 2014 Nexenta Systems, Inc. All Rights Reserved. .\" Copyright (c) 2014, Xin LI -.\" Copyright (c) 2014, The FreeBSD Foundation, All Rights Reserved. +.\" Copyright (c) 2014-2015, The FreeBSD Foundation, All Rights Reserved. .\" .\" $FreeBSD$ .\" -.Dd December 12, 2014 +.Dd July 30, 2015 .Dt ZFS 8 .Os .Sh NAME @@ -191,11 +191,13 @@ .Nm .Cm receive Ns | Ns Cm recv .Op Fl vnFu +.Op Fl o Sy origin Ns = Ns Ar snapshot .Ar filesystem Ns | Ns Ar volume Ns | Ns Ar snapshot .Nm .Cm receive Ns | Ns Cm recv .Op Fl vnFu .Op Fl d | e +.Op Fl o Sy origin Ns = Ns Ar snapshot .Ar filesystem .Nm .Cm allow @@ -938,8 +940,24 @@ not be used by any other dataset. Disabling checksums is .Em NOT a recommended practice. -.It Sy compression Ns = Ns Cm on | off | lzjb | gzip | gzip- Ns Ar N | zle | Cm lz4 -Controls the compression algorithm used for this dataset. The +.It Sy compression Ns = Ns Cm on | off | lzjb | gzip | gzip- Ns Ar N | Cm zle | Cm lz4 +Controls the compression algorithm used for this dataset. +Setting compression to +.Cm on +indicates that the current default compression algorithm should be used. +The default balances compression and decompression speed, with compression +ratio and is expected to work well on a wide variety of workloads. +Unlike all other settings for this property, on does not select a fixed +compression type. +As new compression algorithms are added to ZFS and enabled on a pool, the +default compression algorithm may change. +The current default compression algorthm is either +.Cm lzjb +or, if the +.Sy lz4_compress +feature is enabled, +.Cm lz4 . +The .Cm lzjb compression algorithm is optimized for performance while providing decent data compression. Setting compression to @@ -2689,6 +2707,7 @@ feature. .Nm .Cm receive Ns | Ns Cm recv .Op Fl vnFu +.Op Fl o Sy origin Ns = Ns Ar snapshot .Ar filesystem Ns | Ns Ar volume Ns | Ns Ar snapshot .Xc .It Xo @@ -2696,6 +2715,7 @@ feature. .Cm receive Ns | Ns Cm recv .Op Fl vnFu .Op Fl d | e +.Op Fl o Sy origin Ns = Ns Ar snapshot .Ar filesystem .Xc .Pp @@ -2780,6 +2800,10 @@ receive operation. Do not actually receive the stream. This can be useful in conjunction with the .Fl v option to verify the name the receive operation would use. +.It Fl o Sy origin Ns = Ns Ar snapshot +Forces the stream to be received as a clone of the given snapshot. +This is only valid if the stream is an incremental stream whose source +is the same as the provided origin. .It Fl F Force a rollback of the file system to the most recent snapshot before performing the receive operation. If receiving an incremental replication diff --git a/cddl/contrib/opensolaris/cmd/zfs/zfs_main.c b/cddl/contrib/opensolaris/cmd/zfs/zfs_main.c index 389b248283d5..9d80b9b0313f 100644 --- a/cddl/contrib/opensolaris/cmd/zfs/zfs_main.c +++ b/cddl/contrib/opensolaris/cmd/zfs/zfs_main.c @@ -264,8 +264,9 @@ get_usage(zfs_help_t idx) return (gettext("\tpromote \n")); case HELP_RECEIVE: return (gettext("\treceive|recv [-vnFu] \n" - "\treceive|recv [-vnFu] [-d | -e] \n")); + "snapshot>\n" + "\treceive|recv [-vnFu] [-o origin=] [-d | -e] " + "\n")); case HELP_RENAME: return (gettext("\trename [-f] " "\n" @@ -791,7 +792,7 @@ zfs_do_create(int argc, char **argv) nomem(); break; case 'o': - if (parseprop(props, optarg)) + if (parseprop(props, optarg) != 0) goto error; break; case 's': @@ -3659,7 +3660,7 @@ zfs_do_snapshot(int argc, char **argv) while ((c = getopt(argc, argv, "ro:")) != -1) { switch (c) { case 'o': - if (parseprop(props, optarg)) + if (parseprop(props, optarg) != 0) return (1); break; case 'r': @@ -3918,10 +3919,19 @@ zfs_do_receive(int argc, char **argv) { int c, err; recvflags_t flags = { 0 }; + nvlist_t *props; + nvpair_t *nvp = NULL; + + if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0) + nomem(); /* check options */ - while ((c = getopt(argc, argv, ":denuvF")) != -1) { + while ((c = getopt(argc, argv, ":o:denuvF")) != -1) { switch (c) { + case 'o': + if (parseprop(props, optarg) != 0) + return (1); + break; case 'd': flags.isprefix = B_TRUE; break; @@ -3966,6 +3976,13 @@ zfs_do_receive(int argc, char **argv) usage(B_FALSE); } + while ((nvp = nvlist_next_nvpair(props, nvp))) { + if (strcmp(nvpair_name(nvp), "origin") != 0) { + (void) fprintf(stderr, gettext("invalid option")); + usage(B_FALSE); + } + } + if (isatty(STDIN_FILENO)) { (void) fprintf(stderr, gettext("Error: Backup stream can not be read " @@ -3974,7 +3991,7 @@ zfs_do_receive(int argc, char **argv) return (1); } - err = zfs_receive(g_zfs, argv[0], &flags, STDIN_FILENO, NULL); + err = zfs_receive(g_zfs, argv[0], props, &flags, STDIN_FILENO, NULL); return (err != 0); } diff --git a/cddl/contrib/opensolaris/cmd/zhack/zhack.c b/cddl/contrib/opensolaris/cmd/zhack/zhack.c index 6e3f0294a623..f4434a1a468d 100644 --- a/cddl/contrib/opensolaris/cmd/zhack/zhack.c +++ b/cddl/contrib/opensolaris/cmd/zhack/zhack.c @@ -20,7 +20,7 @@ */ /* - * Copyright (c) 2011, 2014 by Delphix. All rights reserved. + * Copyright (c) 2011, 2015 by Delphix. All rights reserved. * Copyright (c) 2013 Steven Hartland. All rights reserved. */ @@ -294,8 +294,8 @@ zhack_feature_enable_sync(void *arg, dmu_tx_t *tx) feature_enable_sync(spa, feature, tx); spa_history_log_internal(spa, "zhack enable feature", tx, - "name=%s can_readonly=%u", - feature->fi_guid, feature->fi_can_readonly); + "guid=%s flags=%x", + feature->fi_guid, feature->fi_flags); } static void @@ -314,9 +314,7 @@ zhack_do_feature_enable(int argc, char **argv) */ desc = NULL; feature.fi_uname = "zhack"; - feature.fi_mos = B_FALSE; - feature.fi_can_readonly = B_FALSE; - feature.fi_activate_on_enable = B_FALSE; + feature.fi_flags = 0; feature.fi_depends = nodeps; feature.fi_feature = SPA_FEATURE_NONE; @@ -324,7 +322,7 @@ zhack_do_feature_enable(int argc, char **argv) while ((c = getopt(argc, argv, "rmd:")) != -1) { switch (c) { case 'r': - feature.fi_can_readonly = B_TRUE; + feature.fi_flags |= ZFEATURE_FLAG_READONLY_COMPAT; break; case 'd': desc = strdup(optarg); @@ -413,7 +411,7 @@ zhack_do_feature_ref(int argc, char **argv) * disk later. */ feature.fi_uname = "zhack"; - feature.fi_mos = B_FALSE; + feature.fi_flags = 0; feature.fi_desc = NULL; feature.fi_depends = nodeps; feature.fi_feature = SPA_FEATURE_NONE; @@ -422,7 +420,7 @@ zhack_do_feature_ref(int argc, char **argv) while ((c = getopt(argc, argv, "md")) != -1) { switch (c) { case 'm': - feature.fi_mos = B_TRUE; + feature.fi_flags |= ZFEATURE_FLAG_MOS; break; case 'd': decr = B_TRUE; @@ -455,10 +453,10 @@ zhack_do_feature_ref(int argc, char **argv) if (0 == zap_contains(mos, spa->spa_feat_for_read_obj, feature.fi_guid)) { - feature.fi_can_readonly = B_FALSE; + feature.fi_flags &= ~ZFEATURE_FLAG_READONLY_COMPAT; } else if (0 == zap_contains(mos, spa->spa_feat_for_write_obj, feature.fi_guid)) { - feature.fi_can_readonly = B_TRUE; + feature.fi_flags |= ZFEATURE_FLAG_READONLY_COMPAT; } else { fatal(spa, FTAG, "feature is not enabled: %s", feature.fi_guid); } diff --git a/cddl/contrib/opensolaris/cmd/zpool/zpool_main.c b/cddl/contrib/opensolaris/cmd/zpool/zpool_main.c index d8243f32ad3a..a3eabd1e0eb4 100644 --- a/cddl/contrib/opensolaris/cmd/zpool/zpool_main.c +++ b/cddl/contrib/opensolaris/cmd/zpool/zpool_main.c @@ -22,7 +22,7 @@ /* * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2011 Nexenta Systems, Inc. All rights reserved. - * Copyright (c) 2011, 2014 by Delphix. All rights reserved. + * Copyright (c) 2011, 2015 by Delphix. All rights reserved. * Copyright (c) 2012 by Frederik Wessels. All rights reserved. * Copyright (c) 2012 Martin Matuska . All rights reserved. * Copyright (c) 2013 by Prasad Joshi (sTec). All rights reserved. @@ -4986,7 +4986,8 @@ zpool_do_upgrade(int argc, char **argv) "---------------\n"); for (i = 0; i < SPA_FEATURES; i++) { zfeature_info_t *fi = &spa_feature_table[i]; - const char *ro = fi->fi_can_readonly ? + const char *ro = + (fi->fi_flags & ZFEATURE_FLAG_READONLY_COMPAT) ? " (read-only compatible)" : ""; (void) printf("%-37s%s\n", fi->fi_uname, ro); diff --git a/cddl/contrib/opensolaris/cmd/zstreamdump/zstreamdump.c b/cddl/contrib/opensolaris/cmd/zstreamdump/zstreamdump.c index d99d8014f049..f6dedc2fa384 100644 --- a/cddl/contrib/opensolaris/cmd/zstreamdump/zstreamdump.c +++ b/cddl/contrib/opensolaris/cmd/zstreamdump/zstreamdump.c @@ -25,7 +25,7 @@ */ /* - * Copyright (c) 2013 by Delphix. All rights reserved. + * Copyright (c) 2013, 2014 by Delphix. All rights reserved. */ #include @@ -34,6 +34,7 @@ #include #include #include +#include #include #include @@ -83,7 +84,6 @@ safe_malloc(size_t size) * * Read while computing incremental checksum */ - static size_t ssread(void *buf, size_t len, zio_cksum_t *cksum) { @@ -92,7 +92,7 @@ ssread(void *buf, size_t len, zio_cksum_t *cksum) if ((outlen = fread(buf, len, 1, send_stream)) == 0) return (0); - if (do_cksum && cksum) { + if (do_cksum) { if (do_byteswap) fletcher_4_incremental_byteswap(buf, len, cksum); else @@ -102,6 +102,34 @@ ssread(void *buf, size_t len, zio_cksum_t *cksum) return (outlen); } +static size_t +read_hdr(dmu_replay_record_t *drr, zio_cksum_t *cksum) +{ + ASSERT3U(offsetof(dmu_replay_record_t, drr_u.drr_checksum.drr_checksum), + ==, sizeof (dmu_replay_record_t) - sizeof (zio_cksum_t)); + size_t r = ssread(drr, sizeof (*drr) - sizeof (zio_cksum_t), cksum); + if (r == 0) + return (0); + zio_cksum_t saved_cksum = *cksum; + r = ssread(&drr->drr_u.drr_checksum.drr_checksum, + sizeof (zio_cksum_t), cksum); + if (r == 0) + return (0); + if (!ZIO_CHECKSUM_IS_ZERO(&drr->drr_u.drr_checksum.drr_checksum) && + !ZIO_CHECKSUM_EQUAL(saved_cksum, + drr->drr_u.drr_checksum.drr_checksum)) { + fprintf(stderr, "invalid checksum\n"); + (void) printf("Incorrect checksum in record header.\n"); + (void) printf("Expected checksum = %llx/%llx/%llx/%llx\n", + saved_cksum.zc_word[0], + saved_cksum.zc_word[1], + saved_cksum.zc_word[2], + saved_cksum.zc_word[3]); + exit(1); + } + return (sizeof (*drr)); +} + /* * Print part of a block in ASCII characters */ @@ -183,8 +211,10 @@ main(int argc, char *argv[]) struct drr_free *drrf = &thedrr.drr_u.drr_free; struct drr_spill *drrs = &thedrr.drr_u.drr_spill; struct drr_write_embedded *drrwe = &thedrr.drr_u.drr_write_embedded; + struct drr_checksum *drrc = &thedrr.drr_u.drr_checksum; char c; boolean_t verbose = B_FALSE; + boolean_t very_verbose = B_FALSE; boolean_t first = B_TRUE; /* * dump flag controls whether the contents of any modified data blocks @@ -202,11 +232,14 @@ main(int argc, char *argv[]) do_cksum = B_FALSE; break; case 'v': + if (verbose) + very_verbose = B_TRUE; verbose = B_TRUE; break; case 'd': dump = B_TRUE; verbose = B_TRUE; + very_verbose = B_TRUE; break; case ':': (void) fprintf(stderr, @@ -230,7 +263,7 @@ main(int argc, char *argv[]) send_stream = stdin; pcksum = zc; - while (ssread(drr, sizeof (dmu_replay_record_t), &zc)) { + while (read_hdr(drr, &zc)) { /* * If this is the first DMU record being processed, check for @@ -432,7 +465,7 @@ main(int argc, char *argv[]) if (verbose) { (void) printf("WRITE object = %llu type = %u " "checksum type = %u\n" - "offset = %llu length = %llu " + " offset = %llu length = %llu " "props = %llx\n", (u_longlong_t)drrw->drr_object, drrw->drr_type, @@ -476,9 +509,9 @@ main(int argc, char *argv[]) if (verbose) { (void) printf("WRITE_BYREF object = %llu " "checksum type = %u props = %llx\n" - "offset = %llu length = %llu\n" + " offset = %llu length = %llu\n" "toguid = %llx refguid = %llx\n" - "refobject = %llu refoffset = %llu\n", + " refobject = %llu refoffset = %llu\n", (u_longlong_t)drrwbr->drr_object, drrwbr->drr_checksumtype, (u_longlong_t)drrwbr->drr_key.ddk_prop, @@ -538,7 +571,7 @@ main(int argc, char *argv[]) if (verbose) { (void) printf("WRITE_EMBEDDED object = %llu " "offset = %llu length = %llu\n" - "toguid = %llx comp = %u etype = %u " + " toguid = %llx comp = %u etype = %u " "lsize = %u psize = %u\n", (u_longlong_t)drrwe->drr_object, (u_longlong_t)drrwe->drr_offset, @@ -553,6 +586,13 @@ main(int argc, char *argv[]) P2ROUNDUP(drrwe->drr_psize, 8), &zc); break; } + if (drr->drr_type != DRR_BEGIN && very_verbose) { + (void) printf(" checksum = %llx/%llx/%llx/%llx\n", + (longlong_t)drrc->drr_checksum.zc_word[0], + (longlong_t)drrc->drr_checksum.zc_word[1], + (longlong_t)drrc->drr_checksum.zc_word[2], + (longlong_t)drrc->drr_checksum.zc_word[3]); + } pcksum = zc; } free(buf); diff --git a/cddl/contrib/opensolaris/cmd/ztest/ztest.c b/cddl/contrib/opensolaris/cmd/ztest/ztest.c index 069583495e44..7cc8d5f3639a 100644 --- a/cddl/contrib/opensolaris/cmd/ztest/ztest.c +++ b/cddl/contrib/opensolaris/cmd/ztest/ztest.c @@ -3586,7 +3586,8 @@ ztest_dmu_read_write(ztest_ds_t *zd, uint64_t id) */ n = ztest_random(regions) * stride + ztest_random(width); s = 1 + ztest_random(2 * width - 1); - dmu_prefetch(os, bigobj, n * chunksize, s * chunksize); + dmu_prefetch(os, bigobj, 0, n * chunksize, s * chunksize, + ZIO_PRIORITY_SYNC_READ); /* * Pick a random index and compute the offsets into packobj and bigobj. @@ -5705,8 +5706,10 @@ ztest_run(ztest_shared_t *zs) * Right before closing the pool, kick off a bunch of async I/O; * spa_close() should wait for it to complete. */ - for (uint64_t object = 1; object < 50; object++) - dmu_prefetch(spa->spa_meta_objset, object, 0, 1ULL << 20); + for (uint64_t object = 1; object < 50; object++) { + dmu_prefetch(spa->spa_meta_objset, object, 0, 0, 1ULL << 20, + ZIO_PRIORITY_SYNC_READ); + } spa_close(spa, FTAG); @@ -5905,6 +5908,7 @@ ztest_init(ztest_shared_t *zs) } VERIFY3U(0, ==, spa_create(ztest_opts.zo_pool, nvroot, props, NULL)); nvlist_free(nvroot); + nvlist_free(props); VERIFY3U(0, ==, spa_open(ztest_opts.zo_pool, &spa, FTAG)); zs->zs_metaslab_sz = diff --git a/cddl/contrib/opensolaris/lib/libdtrace/common/dt_printf.c b/cddl/contrib/opensolaris/lib/libdtrace/common/dt_printf.c index ae26d55ba8be..d408aed45199 100644 --- a/cddl/contrib/opensolaris/lib/libdtrace/common/dt_printf.c +++ b/cddl/contrib/opensolaris/lib/libdtrace/common/dt_printf.c @@ -1348,6 +1348,7 @@ dt_printf_format(dtrace_hdl_t *dtp, FILE *fp, const dt_pfargv_t *pfv, dtrace_aggdesc_t *agg; caddr_t lim = (caddr_t)buf + len, limit; char format[64] = "%"; + size_t ret; int i, aggrec, curagg = -1; uint64_t normal; @@ -1379,7 +1380,9 @@ dt_printf_format(dtrace_hdl_t *dtp, FILE *fp, const dt_pfargv_t *pfv, int prec = pfd->pfd_prec; int rval; + const char *start; char *f = format + 1; /* skip initial '%' */ + size_t fmtsz = sizeof(format) - 1; const dtrace_recdesc_t *rec; dt_pfprint_f *func; caddr_t addr; @@ -1536,6 +1539,7 @@ dt_printf_format(dtrace_hdl_t *dtp, FILE *fp, const dt_pfargv_t *pfv, break; } + start = f; if (pfd->pfd_flags & DT_PFCONV_ALT) *f++ = '#'; if (pfd->pfd_flags & DT_PFCONV_ZPAD) @@ -1548,6 +1552,7 @@ dt_printf_format(dtrace_hdl_t *dtp, FILE *fp, const dt_pfargv_t *pfv, *f++ = '\''; if (pfd->pfd_flags & DT_PFCONV_SPACE) *f++ = ' '; + fmtsz -= f - start; /* * If we're printing a stack and DT_PFCONV_LEFT is set, we @@ -1558,13 +1563,20 @@ dt_printf_format(dtrace_hdl_t *dtp, FILE *fp, const dt_pfargv_t *pfv, if (func == pfprint_stack && (pfd->pfd_flags & DT_PFCONV_LEFT)) width = 0; - if (width != 0) - f += snprintf(f, sizeof (format), "%d", ABS(width)); + if (width != 0) { + ret = snprintf(f, fmtsz, "%d", ABS(width)); + f += ret; + fmtsz = MAX(0, fmtsz - ret); + } - if (prec > 0) - f += snprintf(f, sizeof (format), ".%d", prec); + if (prec > 0) { + ret = snprintf(f, fmtsz, ".%d", prec); + f += ret; + fmtsz = MAX(0, fmtsz - ret); + } - (void) strcpy(f, pfd->pfd_fmt); + if (strlcpy(f, pfd->pfd_fmt, fmtsz) >= fmtsz) + return (dt_set_errno(dtp, EDT_COMPILER)); pfd->pfd_rec = rec; if (func(dtp, fp, format, pfd, addr, size, normal) < 0) diff --git a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs.h b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs.h index fbfaab1d6cc5..968732ede989 100644 --- a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs.h +++ b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs.h @@ -668,8 +668,8 @@ typedef struct recvflags { boolean_t nomount; } recvflags_t; -extern int zfs_receive(libzfs_handle_t *, const char *, recvflags_t *, - int, avl_tree_t *); +extern int zfs_receive(libzfs_handle_t *, const char *, nvlist_t *, + recvflags_t *, int, avl_tree_t *); typedef enum diff_flags { ZFS_DIFF_PARSEABLE = 0x1, diff --git a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_pool.c b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_pool.c index b9db421dcbcf..5c78e813b690 100644 --- a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_pool.c +++ b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_pool.c @@ -3535,7 +3535,7 @@ zpool_vdev_name(libzfs_handle_t *hdl, zpool_handle_t *zhp, nvlist_t *nv, } static int -zbookmark_compare(const void *a, const void *b) +zbookmark_mem_compare(const void *a, const void *b) { return (memcmp(a, b, sizeof (zbookmark_phys_t))); } @@ -3598,7 +3598,7 @@ zpool_get_errlog(zpool_handle_t *zhp, nvlist_t **nverrlistp) zc.zc_nvlist_dst_size; count -= zc.zc_nvlist_dst_size; - qsort(zb, count, sizeof (zbookmark_phys_t), zbookmark_compare); + qsort(zb, count, sizeof (zbookmark_phys_t), zbookmark_mem_compare); verify(nvlist_alloc(nverrlistp, 0, KM_SLEEP) == 0); diff --git a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_sendrecv.c b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_sendrecv.c index 954adac6c239..f6efa97569c4 100644 --- a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_sendrecv.c +++ b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_sendrecv.c @@ -64,8 +64,9 @@ extern void zfs_setprop_error(libzfs_handle_t *, zfs_prop_t, int, char *); /* We need to use something for ENODATA. */ #define ENODATA EIDRM -static int zfs_receive_impl(libzfs_handle_t *, const char *, recvflags_t *, - int, const char *, nvlist_t *, avl_tree_t *, char **, int, uint64_t *); +static int zfs_receive_impl(libzfs_handle_t *, const char *, const char *, + recvflags_t *, int, const char *, nvlist_t *, avl_tree_t *, char **, int, + uint64_t *); static const zio_cksum_t zero_cksum = { 0 }; @@ -188,10 +189,28 @@ ddt_update(libzfs_handle_t *hdl, dedup_table_t *ddt, zio_cksum_t *cs, } static int -cksum_and_write(const void *buf, uint64_t len, zio_cksum_t *zc, int outfd) +dump_record(dmu_replay_record_t *drr, void *payload, int payload_len, + zio_cksum_t *zc, int outfd) { - fletcher_4_incremental_native(buf, len, zc); - return (write(outfd, buf, len)); + ASSERT3U(offsetof(dmu_replay_record_t, drr_u.drr_checksum.drr_checksum), + ==, sizeof (dmu_replay_record_t) - sizeof (zio_cksum_t)); + fletcher_4_incremental_native(drr, + offsetof(dmu_replay_record_t, drr_u.drr_checksum.drr_checksum), zc); + if (drr->drr_type != DRR_BEGIN) { + ASSERT(ZIO_CHECKSUM_IS_ZERO(&drr->drr_u. + drr_checksum.drr_checksum)); + drr->drr_u.drr_checksum.drr_checksum = *zc; + } + fletcher_4_incremental_native(&drr->drr_u.drr_checksum.drr_checksum, + sizeof (zio_cksum_t), zc); + if (write(outfd, drr, sizeof (*drr)) == -1) + return (errno); + if (payload_len != 0) { + fletcher_4_incremental_native(payload, payload_len, zc); + if (write(outfd, payload, payload_len) == -1) + return (errno); + } + return (0); } /* @@ -218,26 +237,18 @@ cksummer(void *arg) char *buf = zfs_alloc(dda->dedup_hdl, SPA_MAXBLOCKSIZE); dmu_replay_record_t thedrr; dmu_replay_record_t *drr = &thedrr; - struct drr_begin *drrb = &thedrr.drr_u.drr_begin; - struct drr_end *drre = &thedrr.drr_u.drr_end; - struct drr_object *drro = &thedrr.drr_u.drr_object; - struct drr_write *drrw = &thedrr.drr_u.drr_write; - struct drr_spill *drrs = &thedrr.drr_u.drr_spill; - struct drr_write_embedded *drrwe = &thedrr.drr_u.drr_write_embedded; FILE *ofp; int outfd; - dmu_replay_record_t wbr_drr = {0}; - struct drr_write_byref *wbr_drrr = &wbr_drr.drr_u.drr_write_byref; dedup_table_t ddt; zio_cksum_t stream_cksum; uint64_t physmem = sysconf(_SC_PHYS_PAGES) * sysconf(_SC_PAGESIZE); uint64_t numbuckets; ddt.max_ddt_size = - MAX((physmem * MAX_DDT_PHYSMEM_PERCENT)/100, - SMALLEST_POSSIBLE_MAX_DDT_MB<<20); + MAX((physmem * MAX_DDT_PHYSMEM_PERCENT) / 100, + SMALLEST_POSSIBLE_MAX_DDT_MB << 20); - numbuckets = ddt.max_ddt_size/(sizeof (dedup_entry_t)); + numbuckets = ddt.max_ddt_size / (sizeof (dedup_entry_t)); /* * numbuckets must be a power of 2. Increase number to @@ -253,32 +264,29 @@ cksummer(void *arg) ddt.numhashbits = high_order_bit(numbuckets) - 1; ddt.ddt_full = B_FALSE; - /* Initialize the write-by-reference block. */ - wbr_drr.drr_type = DRR_WRITE_BYREF; - wbr_drr.drr_payloadlen = 0; - outfd = dda->outputfd; ofp = fdopen(dda->inputfd, "r"); - while (ssread(drr, sizeof (dmu_replay_record_t), ofp) != 0) { + while (ssread(drr, sizeof (*drr), ofp) != 0) { switch (drr->drr_type) { case DRR_BEGIN: { - int fflags; + struct drr_begin *drrb = &drr->drr_u.drr_begin; + int fflags; + int sz = 0; ZIO_SET_CHECKSUM(&stream_cksum, 0, 0, 0, 0); + ASSERT3U(drrb->drr_magic, ==, DMU_BACKUP_MAGIC); + /* set the DEDUP feature flag for this stream */ fflags = DMU_GET_FEATUREFLAGS(drrb->drr_versioninfo); fflags |= (DMU_BACKUP_FEATURE_DEDUP | DMU_BACKUP_FEATURE_DEDUPPROPS); DMU_SET_FEATUREFLAGS(drrb->drr_versioninfo, fflags); - if (cksum_and_write(drr, sizeof (dmu_replay_record_t), - &stream_cksum, outfd) == -1) - goto out; if (DMU_GET_STREAM_HDRTYPE(drrb->drr_versioninfo) == DMU_COMPOUNDSTREAM && drr->drr_payloadlen != 0) { - int sz = drr->drr_payloadlen; + sz = drr->drr_payloadlen; if (sz > SPA_MAXBLOCKSIZE) { buf = zfs_realloc(dda->dedup_hdl, buf, @@ -287,64 +295,60 @@ cksummer(void *arg) (void) ssread(buf, sz, ofp); if (ferror(stdin)) perror("fread"); - if (cksum_and_write(buf, sz, &stream_cksum, - outfd) == -1) - goto out; } + if (dump_record(drr, buf, sz, &stream_cksum, + outfd) != 0) + goto out; break; } case DRR_END: { + struct drr_end *drre = &drr->drr_u.drr_end; /* use the recalculated checksum */ - ZIO_SET_CHECKSUM(&drre->drr_checksum, - stream_cksum.zc_word[0], stream_cksum.zc_word[1], - stream_cksum.zc_word[2], stream_cksum.zc_word[3]); - if ((write(outfd, drr, - sizeof (dmu_replay_record_t))) == -1) + drre->drr_checksum = stream_cksum; + if (dump_record(drr, NULL, 0, &stream_cksum, + outfd) != 0) goto out; break; } case DRR_OBJECT: { - if (cksum_and_write(drr, sizeof (dmu_replay_record_t), - &stream_cksum, outfd) == -1) - goto out; + struct drr_object *drro = &drr->drr_u.drr_object; if (drro->drr_bonuslen > 0) { (void) ssread(buf, P2ROUNDUP((uint64_t)drro->drr_bonuslen, 8), ofp); - if (cksum_and_write(buf, - P2ROUNDUP((uint64_t)drro->drr_bonuslen, 8), - &stream_cksum, outfd) == -1) - goto out; } + if (dump_record(drr, buf, + P2ROUNDUP((uint64_t)drro->drr_bonuslen, 8), + &stream_cksum, outfd) != 0) + goto out; break; } case DRR_SPILL: { - if (cksum_and_write(drr, sizeof (dmu_replay_record_t), - &stream_cksum, outfd) == -1) - goto out; + struct drr_spill *drrs = &drr->drr_u.drr_spill; (void) ssread(buf, drrs->drr_length, ofp); - if (cksum_and_write(buf, drrs->drr_length, - &stream_cksum, outfd) == -1) + if (dump_record(drr, buf, drrs->drr_length, + &stream_cksum, outfd) != 0) goto out; break; } case DRR_FREEOBJECTS: { - if (cksum_and_write(drr, sizeof (dmu_replay_record_t), - &stream_cksum, outfd) == -1) + if (dump_record(drr, NULL, 0, &stream_cksum, + outfd) != 0) goto out; break; } case DRR_WRITE: { + struct drr_write *drrw = &drr->drr_u.drr_write; dataref_t dataref; (void) ssread(buf, drrw->drr_length, ofp); @@ -382,7 +386,13 @@ cksummer(void *arg) if (ddt_update(dda->dedup_hdl, &ddt, &drrw->drr_key.ddk_cksum, drrw->drr_key.ddk_prop, &dataref)) { + dmu_replay_record_t wbr_drr = {0}; + struct drr_write_byref *wbr_drrr = + &wbr_drr.drr_u.drr_write_byref; + /* block already present in stream */ + wbr_drr.drr_type = DRR_WRITE_BYREF; + wbr_drrr->drr_object = drrw->drr_object; wbr_drrr->drr_offset = drrw->drr_offset; wbr_drrr->drr_length = drrw->drr_length; @@ -402,19 +412,13 @@ cksummer(void *arg) wbr_drrr->drr_key.ddk_prop = drrw->drr_key.ddk_prop; - if (cksum_and_write(&wbr_drr, - sizeof (dmu_replay_record_t), &stream_cksum, - outfd) == -1) + if (dump_record(&wbr_drr, NULL, 0, + &stream_cksum, outfd) != 0) goto out; } else { /* block not previously seen */ - if (cksum_and_write(drr, - sizeof (dmu_replay_record_t), &stream_cksum, - outfd) == -1) - goto out; - if (cksum_and_write(buf, - drrw->drr_length, - &stream_cksum, outfd) == -1) + if (dump_record(drr, buf, drrw->drr_length, + &stream_cksum, outfd) != 0) goto out; } break; @@ -422,28 +426,27 @@ cksummer(void *arg) case DRR_WRITE_EMBEDDED: { - if (cksum_and_write(drr, sizeof (dmu_replay_record_t), - &stream_cksum, outfd) == -1) - goto out; + struct drr_write_embedded *drrwe = + &drr->drr_u.drr_write_embedded; (void) ssread(buf, P2ROUNDUP((uint64_t)drrwe->drr_psize, 8), ofp); - if (cksum_and_write(buf, + if (dump_record(drr, buf, P2ROUNDUP((uint64_t)drrwe->drr_psize, 8), - &stream_cksum, outfd) == -1) + &stream_cksum, outfd) != 0) goto out; break; } case DRR_FREE: { - if (cksum_and_write(drr, sizeof (dmu_replay_record_t), - &stream_cksum, outfd) == -1) + if (dump_record(drr, NULL, 0, &stream_cksum, + outfd) != 0) goto out; break; } default: - (void) printf("INVALID record type 0x%x\n", + (void) fprintf(stderr, "INVALID record type 0x%x\n", drr->drr_type); /* should never happen, so assert */ assert(B_FALSE); @@ -1470,18 +1473,11 @@ zfs_send(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap, sizeof (drr.drr_u.drr_begin.drr_toname), "%s@%s", zhp->zfs_name, tosnap); drr.drr_payloadlen = buflen; - err = cksum_and_write(&drr, sizeof (drr), &zc, outfd); - /* write header nvlist */ - if (err != -1 && packbuf != NULL) { - err = cksum_and_write(packbuf, buflen, &zc, - outfd); - } + err = dump_record(&drr, packbuf, buflen, &zc, outfd); free(packbuf); - if (err == -1) { - err = errno; + if (err != 0) goto stderr_out; - } /* write end record */ bzero(&drr, sizeof (drr)); @@ -1714,6 +1710,8 @@ recv_read(libzfs_handle_t *hdl, int fd, void *buf, int ilen, int rv; int len = ilen; + assert(ilen <= SPA_MAXBLOCKSIZE); + do { rv = read(fd, cp, len); cp += rv; @@ -2501,7 +2499,7 @@ zfs_receive_package(libzfs_handle_t *hdl, int fd, const char *destname, * zfs_receive_one() will take care of it (ie, * recv_skip() and return 0). */ - error = zfs_receive_impl(hdl, destname, flags, fd, + error = zfs_receive_impl(hdl, destname, NULL, flags, fd, sendfs, stream_nv, stream_avl, top_zfs, cleanup_fd, action_handlep); if (error == ENODATA) { @@ -2634,9 +2632,9 @@ recv_skip(libzfs_handle_t *hdl, int fd, boolean_t byteswap) */ static int zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap, - recvflags_t *flags, dmu_replay_record_t *drr, - dmu_replay_record_t *drr_noswap, const char *sendfs, - nvlist_t *stream_nv, avl_tree_t *stream_avl, char **top_zfs, int cleanup_fd, + const char *originsnap, recvflags_t *flags, dmu_replay_record_t *drr, + dmu_replay_record_t *drr_noswap, const char *sendfs, nvlist_t *stream_nv, + avl_tree_t *stream_avl, char **top_zfs, int cleanup_fd, uint64_t *action_handlep) { zfs_cmd_t zc = { 0 }; @@ -2801,10 +2799,15 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap, } if (flags->verbose) (void) printf("found clone origin %s\n", zc.zc_string); + } else if (originsnap) { + (void) strncpy(zc.zc_string, originsnap, ZFS_MAXNAMELEN); + if (flags->verbose) + (void) printf("using provided clone origin %s\n", + zc.zc_string); } stream_wantsnewfs = (drrb->drr_fromguid == 0 || - (drrb->drr_flags & DRR_FLAG_CLONE)); + (drrb->drr_flags & DRR_FLAG_CLONE) || originsnap); if (stream_wantsnewfs) { /* @@ -3182,9 +3185,10 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap, } static int -zfs_receive_impl(libzfs_handle_t *hdl, const char *tosnap, recvflags_t *flags, - int infd, const char *sendfs, nvlist_t *stream_nv, avl_tree_t *stream_avl, - char **top_zfs, int cleanup_fd, uint64_t *action_handlep) +zfs_receive_impl(libzfs_handle_t *hdl, const char *tosnap, + const char *originsnap, recvflags_t *flags, int infd, const char *sendfs, + nvlist_t *stream_nv, avl_tree_t *stream_avl, char **top_zfs, int cleanup_fd, + uint64_t *action_handlep) { int err; dmu_replay_record_t drr, drr_noswap; @@ -3203,6 +3207,12 @@ zfs_receive_impl(libzfs_handle_t *hdl, const char *tosnap, recvflags_t *flags, "(%s) does not exist"), tosnap); return (zfs_error(hdl, EZFS_NOENT, errbuf)); } + if (originsnap && + !zfs_dataset_exists(hdl, originsnap, ZFS_TYPE_DATASET)) { + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "specified origin fs " + "(%s) does not exist"), originsnap); + return (zfs_error(hdl, EZFS_NOENT, errbuf)); + } /* read in the BEGIN record */ if (0 != (err = recv_read(hdl, infd, &drr, sizeof (drr), B_FALSE, @@ -3275,14 +3285,14 @@ zfs_receive_impl(libzfs_handle_t *hdl, const char *tosnap, recvflags_t *flags, *cp = '\0'; sendfs = nonpackage_sendfs; } - return (zfs_receive_one(hdl, infd, tosnap, flags, - &drr, &drr_noswap, sendfs, stream_nv, stream_avl, - top_zfs, cleanup_fd, action_handlep)); + return (zfs_receive_one(hdl, infd, tosnap, originsnap, flags, + &drr, &drr_noswap, sendfs, stream_nv, stream_avl, top_zfs, + cleanup_fd, action_handlep)); } else { assert(DMU_GET_STREAM_HDRTYPE(drrb->drr_versioninfo) == DMU_COMPOUNDSTREAM); - return (zfs_receive_package(hdl, infd, tosnap, flags, - &drr, &zcksum, top_zfs, cleanup_fd, action_handlep)); + return (zfs_receive_package(hdl, infd, tosnap, flags, &drr, + &zcksum, top_zfs, cleanup_fd, action_handlep)); } } @@ -3293,18 +3303,24 @@ zfs_receive_impl(libzfs_handle_t *hdl, const char *tosnap, recvflags_t *flags, * (-1 will override -2). */ int -zfs_receive(libzfs_handle_t *hdl, const char *tosnap, recvflags_t *flags, - int infd, avl_tree_t *stream_avl) +zfs_receive(libzfs_handle_t *hdl, const char *tosnap, nvlist_t *props, + recvflags_t *flags, int infd, avl_tree_t *stream_avl) { char *top_zfs = NULL; int err; int cleanup_fd; uint64_t action_handle = 0; + char *originsnap = NULL; + if (props) { + err = nvlist_lookup_string(props, "origin", &originsnap); + if (err && err != ENOENT) + return (err); + } cleanup_fd = open(ZFS_DEV, O_RDWR|O_EXCL); VERIFY(cleanup_fd >= 0); - err = zfs_receive_impl(hdl, tosnap, flags, infd, NULL, NULL, + err = zfs_receive_impl(hdl, tosnap, originsnap, flags, infd, NULL, NULL, stream_avl, &top_zfs, cleanup_fd, &action_handle); VERIFY(0 == close(cleanup_fd)); diff --git a/cddl/contrib/opensolaris/lib/libzfs_core/common/libzfs_core.c b/cddl/contrib/opensolaris/lib/libzfs_core/common/libzfs_core.c index 52bd580c47df..5ba660de6ba5 100644 --- a/cddl/contrib/opensolaris/lib/libzfs_core/common/libzfs_core.c +++ b/cddl/contrib/opensolaris/lib/libzfs_core/common/libzfs_core.c @@ -20,7 +20,7 @@ */ /* - * Copyright (c) 2013 by Delphix. All rights reserved. + * Copyright (c) 2012, 2014 by Delphix. All rights reserved. * Copyright (c) 2013 Steven Hartland. All rights reserved. */ @@ -532,18 +532,30 @@ lzc_send(const char *snapname, const char *from, int fd, } /* - * If fromsnap is NULL, a full (non-incremental) stream will be estimated. + * "from" can be NULL, a snapshot, or a bookmark. + * + * If from is NULL, a full (non-incremental) stream will be estimated. This + * is calculated very efficiently. + * + * If from is a snapshot, lzc_send_space uses the deadlists attached to + * each snapshot to efficiently estimate the stream size. + * + * If from is a bookmark, the indirect blocks in the destination snapshot + * are traversed, looking for blocks with a birth time since the creation TXG of + * the snapshot this bookmark was created from. This will result in + * significantly more I/O and be less efficient than a send space estimation on + * an equivalent snapshot. */ int -lzc_send_space(const char *snapname, const char *fromsnap, uint64_t *spacep) +lzc_send_space(const char *snapname, const char *from, uint64_t *spacep) { nvlist_t *args; nvlist_t *result; int err; args = fnvlist_alloc(); - if (fromsnap != NULL) - fnvlist_add_string(args, "fromsnap", fromsnap); + if (from != NULL) + fnvlist_add_string(args, "from", from); err = lzc_ioctl(ZFS_IOC_SEND_SPACE, snapname, args, &result); nvlist_free(args); if (err == 0) diff --git a/cddl/contrib/opensolaris/lib/libzpool/common/sys/zfs_context.h b/cddl/contrib/opensolaris/lib/libzpool/common/sys/zfs_context.h index 493be334891f..22774af1bbb5 100644 --- a/cddl/contrib/opensolaris/lib/libzpool/common/sys/zfs_context.h +++ b/cddl/contrib/opensolaris/lib/libzpool/common/sys/zfs_context.h @@ -135,8 +135,18 @@ extern int aok; /* * DTrace SDT probes have different signatures in userland than they do in - * kernel. If they're being used in kernel code, re-define them out of + * the kernel. If they're being used in kernel code, re-define them out of * existence for their counterparts in libzpool. + * + * Here's an example of how to use the set-error probes in userland: + * zfs$target:::set-error /arg0 == EBUSY/ {stack();} + * + * Here's an example of how to use DTRACE_PROBE probes in userland: + * If there is a probe declared as follows: + * DTRACE_PROBE2(zfs__probe_name, uint64_t, blkid, dnode_t *, dn); + * Then you can use it as follows: + * zfs$target:::probe2 /copyinstr(arg0) == "zfs__probe_name"/ + * {printf("%u %p\n", arg1, arg2);} */ #ifdef DTRACE_PROBE @@ -645,13 +655,6 @@ extern int zfs_secpolicy_rename_perms(const char *from, const char *to, extern int zfs_secpolicy_destroy_perms(const char *name, cred_t *cr); extern zoneid_t getzoneid(void); /* Random compatibility stuff. */ -#define lbolt (gethrtime() >> 23) -#define lbolt64 (gethrtime() >> 23) - -extern uint64_t physmem; - -#define gethrestime_sec() time(NULL) - #define pwrite64(d, p, n, o) pwrite(d, p, n, o) #define readdir64(d) readdir(d) #define SIGPENDING(td) (0) diff --git a/cddl/contrib/opensolaris/tools/ctf/cvt/merge.c b/cddl/contrib/opensolaris/tools/ctf/cvt/merge.c index 27966af0cf50..d366f3182731 100644 --- a/cddl/contrib/opensolaris/tools/ctf/cvt/merge.c +++ b/cddl/contrib/opensolaris/tools/ctf/cvt/merge.c @@ -349,7 +349,7 @@ equiv_node(tdesc_t *ctdp, tdesc_t *mtdp, equiv_data_t *ed) int (*equiv)(tdesc_t *, tdesc_t *, equiv_data_t *); int mapping; - if (ctdp->t_emark > ed->ed_clear_mark || + if (ctdp->t_emark > ed->ed_clear_mark && mtdp->t_emark > ed->ed_clear_mark) return (ctdp->t_emark == mtdp->t_emark); diff --git a/cddl/lib/Makefile b/cddl/lib/Makefile index f418993200ed..9371865ca428 100644 --- a/cddl/lib/Makefile +++ b/cddl/lib/Makefile @@ -26,9 +26,7 @@ _libzpool= libzpool .endif .endif -.if ${MACHINE_CPUARCH} == "aarch64" || ${MACHINE_ARCH} == "amd64" || \ - ${MACHINE_CPUARCH} == "arm" || ${MACHINE_ARCH} == "i386" || \ - ${MACHINE_CPUARCH} == "mips" || ${MACHINE_CPUARCH} == "powerpc" +.if ${MACHINE_CPUARCH} != "sparc64" _drti= drti _libdtrace= libdtrace .endif diff --git a/cddl/lib/libdtrace/io.d b/cddl/lib/libdtrace/io.d index 18a54afdb937..41f7aa1533b8 100644 --- a/cddl/lib/libdtrace/io.d +++ b/cddl/lib/libdtrace/io.d @@ -25,8 +25,7 @@ * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" - +#pragma D depends_on module kernel #pragma D depends_on provider io typedef struct devinfo { diff --git a/cddl/lib/libdtrace/ip.d b/cddl/lib/libdtrace/ip.d index a1a2996362fd..33fb007e6fff 100644 --- a/cddl/lib/libdtrace/ip.d +++ b/cddl/lib/libdtrace/ip.d @@ -25,6 +25,7 @@ * Copyright (c) 2013 Mark Johnston */ +#pragma D depends_on module kernel #pragma D depends_on provider ip /* diff --git a/cddl/lib/libdtrace/nfs.d b/cddl/lib/libdtrace/nfs.d index be342286f1b5..ae864ed363d2 100644 --- a/cddl/lib/libdtrace/nfs.d +++ b/cddl/lib/libdtrace/nfs.d @@ -30,6 +30,7 @@ #pragma D depends_on library ip.d #pragma D depends_on library net.d +#pragma D depends_on module kernel #pragma D depends_on module nfs typedef struct nfsv4opinfo { diff --git a/cddl/lib/libdtrace/nfssrv.d b/cddl/lib/libdtrace/nfssrv.d index 68ac08b58cde..37842f7963a1 100644 --- a/cddl/lib/libdtrace/nfssrv.d +++ b/cddl/lib/libdtrace/nfssrv.d @@ -30,7 +30,8 @@ #pragma D depends_on library ip.d #pragma D depends_on library net.d -#pragma D depends_on module nfs.d +#pragma D depends_on library nfs.d +#pragma D depends_on module kernel #pragma D depends_on module nfssrv #pragma D binding "1.5" translator diff --git a/cddl/lib/libdtrace/psinfo.d b/cddl/lib/libdtrace/psinfo.d index c2219f70a35d..1b13863df0e3 100644 --- a/cddl/lib/libdtrace/psinfo.d +++ b/cddl/lib/libdtrace/psinfo.d @@ -28,6 +28,8 @@ * Use is subject to license terms. */ +#pragma D depends_on module kernel + typedef struct psinfo { int pr_nlwp; /* number of threads */ pid_t pr_pid; /* unique process id */ diff --git a/cddl/lib/libdtrace/regs_x86.d b/cddl/lib/libdtrace/regs_x86.d index 7dce19717642..03528a6423d3 100644 --- a/cddl/lib/libdtrace/regs_x86.d +++ b/cddl/lib/libdtrace/regs_x86.d @@ -28,8 +28,6 @@ * Use is subject to license terms. */ -#pragma ident "@(#)regs.d.in 1.1 04/09/28 SMI" - inline int R_GS = 0; #pragma D binding "1.0" R_GS inline int R_FS = 1; diff --git a/cddl/lib/libdtrace/sched.d b/cddl/lib/libdtrace/sched.d index d91d3c5318f0..104fd571e4c0 100644 --- a/cddl/lib/libdtrace/sched.d +++ b/cddl/lib/libdtrace/sched.d @@ -27,9 +27,7 @@ * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" - -#pragma D depends_on module unix +#pragma D depends_on module kernel #pragma D depends_on provider sched struct cpuinfo { diff --git a/cddl/lib/libdtrace/siftr.d b/cddl/lib/libdtrace/siftr.d index a6ff844901dc..37bc30ff3e06 100644 --- a/cddl/lib/libdtrace/siftr.d +++ b/cddl/lib/libdtrace/siftr.d @@ -21,6 +21,7 @@ * $FreeBSD$ */ +#pragma D depends_on module kernel #pragma D depends_on module siftr #pragma D depends_on provider tcp diff --git a/cddl/lib/libdtrace/tcp.d b/cddl/lib/libdtrace/tcp.d index 4b826f175572..8398cd3ec6ad 100644 --- a/cddl/lib/libdtrace/tcp.d +++ b/cddl/lib/libdtrace/tcp.d @@ -26,6 +26,7 @@ */ #pragma D depends_on library ip.d +#pragma D depends_on module kernel #pragma D depends_on provider tcp /* diff --git a/cddl/lib/libdtrace/udp.d b/cddl/lib/libdtrace/udp.d index 21538eb0ef3c..eeba58c8752f 100644 --- a/cddl/lib/libdtrace/udp.d +++ b/cddl/lib/libdtrace/udp.d @@ -26,6 +26,7 @@ */ #pragma D depends_on library ip.d +#pragma D depends_on module kernel #pragma D depends_on provider udp /* diff --git a/cddl/usr.sbin/dtrace/Makefile b/cddl/usr.sbin/dtrace/Makefile index 8c5e3abe5a07..551f6cf80031 100644 --- a/cddl/usr.sbin/dtrace/Makefile +++ b/cddl/usr.sbin/dtrace/Makefile @@ -1,5 +1,7 @@ # $FreeBSD$ +.include + .PATH: ${.CURDIR}/../../../cddl/contrib/opensolaris/cmd/dtrace PROG= dtrace @@ -22,4 +24,8 @@ CFLAGS+= -I${.CURDIR}/../../../sys/cddl/compat/opensolaris \ LIBADD= dtrace +.if ${MK_DTRACE_TESTS} != "no" +SUBDIR+= tests +.endif + .include diff --git a/contrib/apr/CHANGES b/contrib/apr/CHANGES index d04ad11a9b4b..f300ec0f87d3 100644 --- a/contrib/apr/CHANGES +++ b/contrib/apr/CHANGES @@ -1,4 +1,63 @@ -*- coding: utf-8 -*- +Changes for APR 1.5.2 + + *) SECURITY: CVE-2015-1829 (cve.mitre.org) + APR applications using APR named pipe support on Windows can be + vulnerable to a pipe squatting attack from a local process; the extent + of the vulnerability, when present, depends on the application. + Initial analysis and report was provided by John Hernandez of Casaba + Security via HP SSRT Security Alert. [Yann Ylavic] + + *) apr_atomic: Fix errors when building on Visual Studio 2013 while + maintaining the ability to build on Visual Studio 6 with Windows + Server 2003 R2 SDK. PR 57191. [Gregg Smith] + + *) Switch to generic atomics for early/unpatched Solaris 10 not exporting + some atomic functions. PR 55418. [Yann Ylavic] + + *) apr_file_mktemp() on HP-UX: Remove limitation of 26 temporary files + per process. PR 57677. [Jeff Trawick] + + *) apr_escape: Correctly calculate the size of the returned string in + apr_escape_path and set the correct return value in case we actually + escape the string. [] PR 57230. + + *) pollcb on Windows: Handle calls with no file/socket descriptors. + Follow up to PR 49882. [Jeff Trawick, Yann Ylavic] + + *) apr_poll(cb): fix error paths returned values and leaks. [Yann Ylavic] + + *) apr_thread_cond_*wait() on BeOS: Fix broken logic. PR 45800. + [Jochen Voss (no e-mail)] + + *) apr_skiplist: Optimize the number of allocations by reusing pooled or + malloc()ed nodes for the lifetime of the skiplist. [Yann Ylavic] + + *) apr_skiplist: Fix possible multiple-free() on the same value in + apr_skiplist_remove_all(). [Yann Ylavic] + + *) apr_pollset: On z/OS, threadsafe apr_pollset_poll() may return + "EDC8102I Operation would block" under load. + [Pat Odonnell ] + + *) On z/OS, apr_sockaddr_info_get() with family == APR_UNSPEC was not + returning IPv4 addresses if any IPv6 addresses were returned. + [Eric Covener] + + *) Windows cmake build: Fix an incompatibility with cmake 2.8.12 and + later. [Jeff Trawick] + + *) apr_global_mutex/apr_proc_mutex: Resolve failures with the + POSIX sem implementation in environments which receive signals. + [Jeff Trawick] + + *) apr_skiplist: Fix potential corruption of skiplists leading to + results or crashes. [Takashi Sato , Eric Covener] + PR 56654. + + *) Improve platform detection by updating config.guess and config.sub. + [Rainer Jung] + Changes for APR 1.5.1 *) apr_os_proc_mutex_get() on Unix: Avoid segfault for cross- @@ -37,8 +96,8 @@ Changes for APR 1.5.1 *) Correct a regression in 1.5.0 which affected out-of-tree builds on Unix. [Rainer Jung] - *) Improve platform detection for bundled expat by updating - config.guess and config.sub. [Rainer Jung] + *) Improve platform detection by updating config.guess and config.sub. + [Rainer Jung] Changes for APR 1.5.0 diff --git a/contrib/apr/CMakeLists.txt b/contrib/apr/CMakeLists.txt index 04903f9f9ed6..75f35c7cd559 100644 --- a/contrib/apr/CMakeLists.txt +++ b/contrib/apr/CMakeLists.txt @@ -234,6 +234,7 @@ SET(APR_TEST_SOURCES test/testprocmutex.c test/testrand.c test/testshm.c + test/testskiplist.c test/testsleep.c test/testsock.c test/testsockets.c @@ -252,7 +253,6 @@ SET(APR_TEST_SOURCES SET(install_targets) SET(install_bin_pdb) -SET(install_lib_pdb) # libapr-1 is shared, apr-1 is static ADD_LIBRARY(libapr-1 SHARED ${APR_SOURCES} ${APR_PUBLIC_HEADERS_GENERATED} libapr.rc) @@ -264,7 +264,6 @@ ADD_DEPENDENCIES(libapr-1 test_char_header) ADD_LIBRARY(apr-1 STATIC ${APR_SOURCES} ${APR_PUBLIC_HEADERS_GENERATED}) SET(install_targets ${install_targets} apr-1) -SET(install_lib_pdb ${install_lib_pdb} ${PROJECT_BINARY_DIR}/apr-1.pdb) TARGET_LINK_LIBRARIES(apr-1 ${APR_SYSTEM_LIBS}) SET_TARGET_PROPERTIES(apr-1 PROPERTIES COMPILE_DEFINITIONS "APR_DECLARE_STATIC;WINNT") ADD_DEPENDENCIES(apr-1 test_char_header) @@ -272,12 +271,10 @@ ADD_DEPENDENCIES(apr-1 test_char_header) # libaprapp-1 and aprapp-1 are static ADD_LIBRARY(libaprapp-1 STATIC misc/win32/apr_app.c misc/win32/internal.c ${APR_PUBLIC_HEADERS_GENERATED}) SET(install_targets ${install_targets} libaprapp-1) -SET(install_lib_pdb ${install_lib_pdb} ${PROJECT_BINARY_DIR}/libaprapp-1.pdb) SET_TARGET_PROPERTIES(libaprapp-1 PROPERTIES COMPILE_DEFINITIONS "APR_APP;WINNT") ADD_LIBRARY(aprapp-1 STATIC misc/win32/apr_app.c misc/win32/internal.c ${APR_PUBLIC_HEADERS_GENERATED}) SET(install_targets ${install_targets} aprapp-1) -SET(install_lib_pdb ${install_lib_pdb} ${PROJECT_BINARY_DIR}/aprapp-1.pdb) SET_TARGET_PROPERTIES(aprapp-1 PROPERTIES COMPILE_DEFINITIONS "APR_DECLARE_STATIC;APR_APP;WINNT") IF(APR_BUILD_TESTAPR) @@ -394,10 +391,6 @@ IF(INSTALL_PDB) INSTALL(FILES ${install_bin_pdb} DESTINATION bin CONFIGURATIONS RelWithDebInfo Debug) - - INSTALL(FILES ${install_lib_pdb} - DESTINATION lib - CONFIGURATIONS RelWithDebInfo Debug) ENDIF() INSTALL(FILES ${APR_PUBLIC_HEADERS_STATIC} ${APR_PUBLIC_HEADERS_GENERATED} DESTINATION include) diff --git a/contrib/apr/Makefile.in b/contrib/apr/Makefile.in index 6f99733bdf5e..a2a5194375d6 100644 --- a/contrib/apr/Makefile.in +++ b/contrib/apr/Makefile.in @@ -129,11 +129,11 @@ check: $(TARGET_LIB) etags: etags `find . -name '*.[ch]'` -make_tools_dir: - $(APR_MKDIR) tools - OBJECTS_gen_test_char = tools/gen_test_char.lo $(LOCAL_LIBS) -tools/gen_test_char.lo: make_tools_dir +tools/gen_test_char.lo: tools/gen_test_char.c + $(APR_MKDIR) tools + $(LT_COMPILE) + tools/gen_test_char@EXEEXT@: $(OBJECTS_gen_test_char) $(LINK_PROG) $(OBJECTS_gen_test_char) $(ALL_LIBS) diff --git a/contrib/apr/NOTICE b/contrib/apr/NOTICE index b16965be9488..cd3576db205f 100644 --- a/contrib/apr/NOTICE +++ b/contrib/apr/NOTICE @@ -1,5 +1,5 @@ Apache Portable Runtime -Copyright (c) 2000-2014 The Apache Software Foundation. +Copyright (c) 2000-2015 The Apache Software Foundation. This product includes software developed at The Apache Software Foundation (http://www.apache.org/). diff --git a/contrib/apr/NWGNUmakefile b/contrib/apr/NWGNUmakefile index 3f789ebca663..db15c0d91eab 100644 --- a/contrib/apr/NWGNUmakefile +++ b/contrib/apr/NWGNUmakefile @@ -50,6 +50,7 @@ include $(APR_WORK)/build/NWGNUhead.inc # XINCDIRS += \ $(APR)/include \ + $(APR)/include/private \ $(APR)/include/arch/NetWare \ $(APR)/include/arch/unix \ $(APR)/memory/unix \ @@ -293,11 +294,13 @@ FILES_nlm_exports = \ FILES_lib_objs = \ $(OBJDIR)/apr_atomic.o \ $(OBJDIR)/apr_cpystrn.o \ + $(OBJDIR)/apr_escape.o \ $(OBJDIR)/apr_fnmatch.o \ $(OBJDIR)/apr_getpass.o \ $(OBJDIR)/apr_hash.o \ $(OBJDIR)/apr_pools.o \ $(OBJDIR)/apr_random.o \ + $(OBJDIR)/apr_skiplist.o \ $(OBJDIR)/apr_snprintf.o \ $(OBJDIR)/apr_strings.o \ $(OBJDIR)/apr_strnatcmp.o \ @@ -407,7 +410,7 @@ endif vpath %.c atomic/netware:strings:tables:passwd:lib:time/unix vpath %.c file_io/unix:locks/netware:misc/netware:misc/unix:threadproc/netware vpath %.c poll/unix:shmem/unix:support/unix:random/unix -vpath %.c dso/netware:memory/unix:mmap/unix:user/netware +vpath %.c dso/netware:memory/unix:mmap/unix:user/netware:encoding # Use the win32 network_io if Winsock is being used ifndef USE_STDSOCKETS diff --git a/contrib/apr/apr.dsp b/contrib/apr/apr.dsp index bbba9267fcc8..6e254378c298 100644 --- a/contrib/apr/apr.dsp +++ b/contrib/apr/apr.dsp @@ -907,69 +907,6 @@ SOURCE=.\include\apr_version.h # Begin Source File SOURCE=.\include\apr_want.h - -!IF "$(CFG)" == "apr - Win32 Release" - -# Begin Custom Build -InputPath=.\include\apr_want.h - -".\LibR\gen_test_char.exe" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" - type .\include\apr.hw > .\include\apr.h - -# End Custom Build - -!ELSEIF "$(CFG)" == "apr - Win32 Debug" - -# Begin Custom Build -InputPath=.\include\apr_want.h - -".\LibD\gen_test_char.exe" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" - type .\include\apr.hw > .\include\apr.h - -# End Custom Build - -!ELSEIF "$(CFG)" == "apr - Win32 Release9x" - -# Begin Custom Build -InputPath=.\include\apr_want.h - -".\9x\LibR\gen_test_char.exe" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" - type .\include\apr.hw > .\include\apr.h - -# End Custom Build - -!ELSEIF "$(CFG)" == "apr - Win32 Debug9x" - -# Begin Custom Build -InputPath=.\include\apr_want.h - -".\9x\LibD\gen_test_char.exe" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" - type .\include\apr.hw > .\include\apr.h - -# End Custom Build - -!ELSEIF "$(CFG)" == "apr - x64 Release" - -# Begin Custom Build -InputPath=.\include\apr_want.h - -".\x64\LibR\gen_test_char.exe" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" - type .\include\apr.hw > .\include\apr.h - -# End Custom Build - -!ELSEIF "$(CFG)" == "apr - x64 Debug" - -# Begin Custom Build -InputPath=.\include\apr_want.h - -".\x64\LibD\gen_test_char.exe" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" - type .\include\apr.hw > .\include\apr.h - -# End Custom Build - -!ENDIF - # End Source File # End Group # End Target diff --git a/contrib/apr/apr.spec b/contrib/apr/apr.spec index b3db1675d561..9761158d5ea1 100644 --- a/contrib/apr/apr.spec +++ b/contrib/apr/apr.spec @@ -3,7 +3,7 @@ Summary: Apache Portable Runtime library Name: apr -Version: 1.5.1 +Version: 1.5.2 Release: 1 License: Apache Software License Group: System Environment/Libraries @@ -76,7 +76,7 @@ rm -rf $RPM_BUILD_ROOT %defattr(-,root,root,-) %doc docs/APRDesign.html docs/canonical_filenames.html %doc docs/incomplete_types docs/non_apr_programs -%doc --parents html +%doc html %{_bindir}/apr*config %{_libdir}/libapr-%{aprver}.*a %{_libdir}/libapr-%{aprver}.so diff --git a/contrib/apr/build-outputs.mk b/contrib/apr/build-outputs.mk index 526f179a4762..275e3ecff55f 100644 --- a/contrib/apr/build-outputs.mk +++ b/contrib/apr/build-outputs.mk @@ -251,7 +251,7 @@ file_io/win32/filestat.lo: file_io/win32/filestat.c .make.dirs include/apr_alloc file_io/win32/filesys.lo: file_io/win32/filesys.c .make.dirs include/apr_allocator.h include/apr_errno.h include/apr_general.h include/apr_pools.h include/apr_strings.h include/apr_thread_mutex.h include/apr_want.h file_io/win32/flock.lo: file_io/win32/flock.c .make.dirs file_io/win32/open.lo: file_io/win32/open.c .make.dirs include/apr_allocator.h include/apr_dso.h include/apr_errno.h include/apr_file_info.h include/apr_file_io.h include/apr_general.h include/apr_global_mutex.h include/apr_inherit.h include/apr_network_io.h include/apr_pools.h include/apr_portable.h include/apr_proc_mutex.h include/apr_shm.h include/apr_strings.h include/apr_tables.h include/apr_thread_mutex.h include/apr_thread_proc.h include/apr_time.h include/apr_user.h include/apr_want.h -file_io/win32/pipe.lo: file_io/win32/pipe.c .make.dirs include/apr_allocator.h include/apr_errno.h include/apr_file_info.h include/apr_file_io.h include/apr_general.h include/apr_inherit.h include/apr_pools.h include/apr_strings.h include/apr_tables.h include/apr_thread_mutex.h include/apr_time.h include/apr_user.h include/apr_want.h +file_io/win32/pipe.lo: file_io/win32/pipe.c .make.dirs include/apr_allocator.h include/apr_errno.h include/apr_escape.h include/apr_file_info.h include/apr_file_io.h include/apr_general.h include/apr_inherit.h include/apr_pools.h include/apr_strings.h include/apr_tables.h include/apr_thread_mutex.h include/apr_time.h include/apr_user.h include/apr_want.h file_io/win32/readwrite.lo: file_io/win32/readwrite.c .make.dirs include/apr_allocator.h include/apr_errno.h include/apr_file_info.h include/apr_file_io.h include/apr_general.h include/apr_inherit.h include/apr_lib.h include/apr_pools.h include/apr_strings.h include/apr_tables.h include/apr_thread_mutex.h include/apr_time.h include/apr_user.h include/apr_want.h file_io/win32/seek.lo: file_io/win32/seek.c .make.dirs include/apr_allocator.h include/apr_errno.h include/apr_file_info.h include/apr_file_io.h include/apr_general.h include/apr_inherit.h include/apr_pools.h include/apr_tables.h include/apr_thread_mutex.h include/apr_time.h include/apr_user.h include/apr_want.h diff --git a/contrib/apr/configure b/contrib/apr/configure index 860c65b8f560..449c8842e1f0 100755 --- a/contrib/apr/configure +++ b/contrib/apr/configure @@ -6802,10 +6802,10 @@ if test "x$apr_preload_done" != "xyes" ; then *-apple-darwin*) if test "x$CPPFLAGS" = "x"; then - test "x$silent" != "xyes" && echo " setting CPPFLAGS to \"-DDARWIN -DSIGPROCMASK_SETS_THREAD_MASK -no-cpp-precomp\"" - CPPFLAGS="-DDARWIN -DSIGPROCMASK_SETS_THREAD_MASK -no-cpp-precomp" + test "x$silent" != "xyes" && echo " setting CPPFLAGS to \"-DDARWIN -DSIGPROCMASK_SETS_THREAD_MASK\"" + CPPFLAGS="-DDARWIN -DSIGPROCMASK_SETS_THREAD_MASK" else - apr_addto_bugger="-DDARWIN -DSIGPROCMASK_SETS_THREAD_MASK -no-cpp-precomp" + apr_addto_bugger="-DDARWIN -DSIGPROCMASK_SETS_THREAD_MASK" for i in $apr_addto_bugger; do apr_addto_duplicate="0" for j in $CPPFLAGS; do @@ -18794,7 +18794,34 @@ if test "${enable_nonportable_atomics+set}" = set; then : else case $host_cpu in i[456]86) force_generic_atomics=yes ;; - *) force_generic_atomics=no ;; + *) force_generic_atomics=no + case $host in + *solaris2.10*) + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main () +{ +void *ptr = NULL; atomic_cas_ptr(&ptr, NULL, NULL); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + +else + force_generic_atomics=yes + +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + if test $force_generic_atomics = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: nonportable atomic support disabled, system needs Patch-ID 118884 or 118885" >&5 +$as_echo "$as_me: nonportable atomic support disabled, system needs Patch-ID 118884 or 118885" >&6;} + fi + ;; + esac + ;; esac fi @@ -22292,7 +22319,7 @@ else fi done -for ac_func in getpass getpassphrase gmtime_r localtime_r mkstemp +for ac_func in getpass getpassphrase gmtime_r localtime_r do : as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" @@ -22304,6 +22331,23 @@ _ACEOF fi done +case $host in + *-hp-hpux*) + ;; + *) + for ac_func in mkstemp +do : + ac_fn_c_check_func "$LINENO" "mkstemp" "ac_cv_func_mkstemp" +if test "x$ac_cv_func_mkstemp" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_MKSTEMP 1 +_ACEOF + +fi +done + + ;; +esac @@ -23902,7 +23946,7 @@ _ACEOF if test "${ac_cv_sizeof_off_t}${apr_cv_use_lfs64}" = "4yes"; then # Enable LFS aprlfs=1 - for ac_func in mmap64 sendfile64 sendfilev64 mkstemp64 readdir64_r + for ac_func in mmap64 sendfile64 sendfilev64 readdir64_r do : as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" @@ -23914,6 +23958,23 @@ _ACEOF fi done + case $host in + *-hp-hpux*) + ;; + *) + for ac_func in mkstemp64 +do : + ac_fn_c_check_func "$LINENO" "mkstemp64" "ac_cv_func_mkstemp64" +if test "x$ac_cv_func_mkstemp64" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_MKSTEMP64 1 +_ACEOF + +fi +done + + ;; + esac elif test "${ac_cv_sizeof_off_t}" != "${ac_cv_sizeof_size_t}"; then # unsure of using -gt above is as portable, can can't forsee where # off_t can legitimately be smaller than size_t diff --git a/contrib/apr/configure.in b/contrib/apr/configure.in index 2a37a30bd5f2..e01774be18a4 100644 --- a/contrib/apr/configure.in +++ b/contrib/apr/configure.in @@ -640,7 +640,20 @@ AC_ARG_ENABLE(nonportable-atomics, ], [case $host_cpu in i[[456]]86) force_generic_atomics=yes ;; - *) force_generic_atomics=no ;; + *) force_generic_atomics=no + case $host in + *solaris2.10*) + AC_TRY_COMPILE( + [#include ], + [void *ptr = NULL; atomic_cas_ptr(&ptr, NULL, NULL);],, + [force_generic_atomics=yes] + ) + if test $force_generic_atomics = yes; then + AC_MSG_NOTICE([nonportable atomic support disabled, system needs Patch-ID 118884 or 118885]) + fi + ;; + esac + ;; esac ]) @@ -1400,7 +1413,15 @@ if test "$native_mmap_emul" = "1"; then mmap="1" fi AC_CHECK_FUNCS(memmove, [ have_memmove="1" ], [have_memmove="0" ]) -AC_CHECK_FUNCS([getpass getpassphrase gmtime_r localtime_r mkstemp]) +AC_CHECK_FUNCS([getpass getpassphrase gmtime_r localtime_r]) +case $host in + *-hp-hpux*) + dnl mkstemp is limited to 26 temporary files (a-z); use APR replacement + ;; + *) + AC_CHECK_FUNCS(mkstemp) + ;; +esac AC_SUBST(fork) AC_SUBST(have_inet_addr) @@ -1801,7 +1822,15 @@ APR_CHECK_SIZEOF_EXTENDED([#include ], off_t, 8) if test "${ac_cv_sizeof_off_t}${apr_cv_use_lfs64}" = "4yes"; then # Enable LFS aprlfs=1 - AC_CHECK_FUNCS([mmap64 sendfile64 sendfilev64 mkstemp64 readdir64_r]) + AC_CHECK_FUNCS([mmap64 sendfile64 sendfilev64 readdir64_r]) + case $host in + *-hp-hpux*) + dnl mkstemp64 is limited to 26 temporary files (a-z); use APR replacement + ;; + *) + AC_CHECK_FUNCS(mkstemp64) + ;; + esac elif test "${ac_cv_sizeof_off_t}" != "${ac_cv_sizeof_size_t}"; then # unsure of using -gt above is as portable, can can't forsee where # off_t can legitimately be smaller than size_t diff --git a/contrib/apr/encoding/apr_escape.c b/contrib/apr/encoding/apr_escape.c index 2bcfec418fe7..702a94130089 100644 --- a/contrib/apr/encoding/apr_escape.c +++ b/contrib/apr/encoding/apr_escape.c @@ -436,6 +436,8 @@ APR_DECLARE(apr_status_t) apr_escape_path(char *escaped, const char *path, while ((c = *s) && slen) { if (TEST_CHAR(c, T_OS_ESCAPE_PATH)) { d = c2x(c, '%', d); + size += 2; + found = 1; } else { *d++ = c; diff --git a/contrib/apr/include/apr_skiplist.h b/contrib/apr/include/apr_skiplist.h index bc17efd95fa0..f56ff22c36d6 100644 --- a/contrib/apr/include/apr_skiplist.h +++ b/contrib/apr/include/apr_skiplist.h @@ -40,7 +40,9 @@ extern "C" { /** * apr_skiplist_compare is the function type that must be implemented * per object type that is used in a skip list for comparisons to maintain - * order + * order. A value <0 indicates placement after this node; a value of 0 + * indicates collision with this exact node; a value >0 indicates placement + * before this node. * */ typedef int (*apr_skiplist_compare) (void *, void *); @@ -171,7 +173,8 @@ APR_DECLARE(void *) apr_skiplist_next(apr_skiplist *sl, apr_skiplistnode **iter) APR_DECLARE(void *) apr_skiplist_previous(apr_skiplist *sl, apr_skiplistnode **iter); /** - * Insert an element into the skip list using the specified comparison function. + * Insert an element into the skip list using the specified comparison function + * if it does not already exist. * @param sl The skip list * @param data The element to insert * @param comp The comparison function to use for placement into the skip list @@ -180,7 +183,8 @@ APR_DECLARE(apr_skiplistnode *) apr_skiplist_insert_compare(apr_skiplist *sl, void *data, apr_skiplist_compare comp); /** - * Insert an element into the skip list using the existing comparison function. + * Insert an element into the skip list using the existing comparison function + * if it does not already exist (as determined by the comparison function) * @param sl The skip list * @param data The element to insert * @remark If no comparison function has been set for the skip list, the element @@ -190,7 +194,7 @@ APR_DECLARE(apr_skiplistnode *) apr_skiplist_insert(apr_skiplist* sl, void *data /** * Remove an element from the skip list using the specified comparison function for - * locating the element. + * locating the element. In the case of duplicates, the 1st entry will be removed. * @param sl The skip list * @param data The element to remove * @param myfree A function to be called for each removed element @@ -203,7 +207,7 @@ APR_DECLARE(int) apr_skiplist_remove_compare(apr_skiplist *sl, void *data, /** * Remove an element from the skip list using the existing comparison function for - * locating the element. + * locating the element. In the case of duplicates, the 1st entry will be removed. * @param sl The skip list * @param data The element to remove * @param myfree A function to be called for each removed element @@ -229,7 +233,7 @@ APR_DECLARE(void) apr_skiplist_remove_all(apr_skiplist *sl, apr_skiplist_freefun APR_DECLARE(void) apr_skiplist_destroy(apr_skiplist *sl, apr_skiplist_freefunc myfree); /** - * Return the first element in the skip list, leaving the element in the skip list. + * Return the first element in the skip list, removing the element from the skip list. * @param sl The skip list * @param myfree A function to be called for the removed element * @remark NULL will be returned if there are no elements diff --git a/contrib/apr/include/apr_version.h b/contrib/apr/include/apr_version.h index 21110538bf94..c7cbb8f60d5f 100644 --- a/contrib/apr/include/apr_version.h +++ b/contrib/apr/include/apr_version.h @@ -38,7 +38,7 @@ */ -#define APR_COPYRIGHT "Copyright (c) 2000-2014 The Apache Software " \ +#define APR_COPYRIGHT "Copyright (c) 2000-2015 The Apache Software " \ "Foundation or its licensors, as applicable." /* The numeric compile-time version constants. These constants are the @@ -62,7 +62,7 @@ * The Patch Level never includes API changes, simply bug fixes. * Reset to 0 when upgrading APR_MINOR_VERSION */ -#define APR_PATCH_VERSION 1 +#define APR_PATCH_VERSION 2 /** * The symbol APR_IS_DEV_VERSION is only defined for internal, diff --git a/contrib/apr/libapr.dsp b/contrib/apr/libapr.dsp index f121ef9ff7f3..267883227dd2 100644 --- a/contrib/apr/libapr.dsp +++ b/contrib/apr/libapr.dsp @@ -765,7 +765,7 @@ SOURCE=.\include\apr_escape.h # Begin Custom Build - Creating gen_test_char.exe and apr_escape_test_char.h InputPath=.\include\apr_escape.h -".\Release\gen_test_char.exe" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" +".\include\apr_escape_test_char.h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" cl.exe /nologo /W3 /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /FD /I ".\include" /Fo.\Release\gen_test_char /Fe.\Release\gen_test_char.exe .\tools\gen_test_char.c .\Release\gen_test_char.exe > .\include\apr_escape_test_char.h @@ -776,7 +776,7 @@ InputPath=.\include\apr_escape.h # Begin Custom Build - Creating gen_test_char.exe and apr_escape_test_char.h InputPath=.\include\apr_escape.h -".\Debug\gen_test_char.exe" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" +".\include\apr_escape_test_char.h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" cl.exe /nologo /W3 /EHsc /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FD /I ".\include" /Fo.\Debug\gen_test_char /Fe.\Debug\gen_test_char.exe .\tools\gen_test_char.c .\Debug\gen_test_char.exe > .\include\apr_escape_test_char.h @@ -787,7 +787,7 @@ InputPath=.\include\apr_escape.h # Begin Custom Build - Creating gen_test_char.exe and apr_escape_test_char.h InputPath=.\include\apr_escape.h -".\9x\Release\gen_test_char.exe" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" +".\include\apr_escape_test_char.h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" cl.exe /nologo /W3 /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /FD /I ".\include" /Fo.\9x\Release\gen_test_char /Fe.\9x\Release\gen_test_char.exe .\tools\gen_test_char.c .\9x\Release\gen_test_char.exe > .\include\apr_escape_test_char.h @@ -798,7 +798,7 @@ InputPath=.\include\apr_escape.h # Begin Custom Build - Creating gen_test_char.exe and apr_escape_test_char.h InputPath=.\include\apr_escape.h -".\9x\Debug\gen_test_char.exe" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" +".\include\apr_escape_test_char.h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" cl.exe /nologo /W3 /EHsc /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FD /I ".\include" /Fo.\9x\Debug\gen_test_char /Fe.\9x\Debug\gen_test_char.exe .\tools\gen_test_char.c .\9x\Debug\gen_test_char.exe > .\include\apr_escape_test_char.h @@ -809,7 +809,7 @@ InputPath=.\include\apr_escape.h # Begin Custom Build - Creating gen_test_char.exe and apr_escape_test_char.h InputPath=.\include\apr_escape.h -".\x64\Release\gen_test_char.exe" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" +".\include\apr_escape_test_char.h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" cl.exe /nologo /W3 /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /FD /I ".\include" /Fo.\x64\Release\gen_test_char /Fe.\x64\Release\gen_test_char.exe .\tools\gen_test_char.c .\x64\Release\gen_test_char.exe > .\include\apr_escape_test_char.h @@ -820,7 +820,7 @@ InputPath=.\include\apr_escape.h # Begin Custom Build - Creating gen_test_char.exe and apr_escape_test_char.h InputPath=.\include\apr_escape.h -".\x64\Debug\gen_test_char.exe" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" +".\include\apr_escape_test_char.h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" cl.exe /nologo /W3 /EHsc /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FD /I ".\include" /Fo.\x64\Debug\gen_test_char /Fe.\x64\Debug\gen_test_char.exe .\tools\gen_test_char.c .\x64\Debug\gen_test_char.exe > .\include\apr_escape_test_char.h @@ -952,69 +952,6 @@ SOURCE=.\include\apr_version.h # Begin Source File SOURCE=.\include\apr_want.h - -!IF "$(CFG)" == "libapr - Win32 Release" - -# Begin Custom Build -InputPath=.\include\apr_want.h - -".\include\apr_escape_test_char.h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" - type .\include\apr.hw > .\include\apr.h - -# End Custom Build - -!ELSEIF "$(CFG)" == "libapr - Win32 Debug" - -# Begin Custom Build -InputPath=.\include\apr_want.h - -".\include\apr_escape_test_char.h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" - type .\include\apr.hw > .\include\apr.h - -# End Custom Build - -!ELSEIF "$(CFG)" == "libapr - Win32 Release9x" - -# Begin Custom Build -InputPath=.\include\apr_want.h - -".\include\apr_escape_test_char.h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" - type .\include\apr.hw > .\include\apr.h - -# End Custom Build - -!ELSEIF "$(CFG)" == "libapr - Win32 Debug9x" - -# Begin Custom Build -InputPath=.\include\apr_want.h - -".\include\apr_escape_test_char.h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" - type .\include\apr.hw > .\include\apr.h - -# End Custom Build - -!ELSEIF "$(CFG)" == "libapr - x64 Release" - -# Begin Custom Build -InputPath=.\include\apr_want.h - -".\include\apr_escape_test_char.h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" - type .\include\apr.hw > .\include\apr.h - -# End Custom Build - -!ELSEIF "$(CFG)" == "libapr - x64 Debug" - -# Begin Custom Build -InputPath=.\include\apr_want.h - -".\include\apr_escape_test_char.h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" - type .\include\apr.hw > .\include\apr.h - -# End Custom Build - -!ENDIF - # End Source File # End Group # Begin Source File diff --git a/contrib/apr/locks/unix/proc_mutex.c b/contrib/apr/locks/unix/proc_mutex.c index fa8a872f1f8a..32012a7613cf 100644 --- a/contrib/apr/locks/unix/proc_mutex.c +++ b/contrib/apr/locks/unix/proc_mutex.c @@ -114,7 +114,9 @@ static apr_status_t proc_mutex_posix_create(apr_proc_mutex_t *new_mutex, usec = apr_time_usec(now); apr_snprintf(semname, sizeof(semname), "/ApR.%lxZ%lx", sec, usec); } - psem = sem_open(semname, O_CREAT | O_EXCL, 0644, 1); + do { + psem = sem_open(semname, O_CREAT | O_EXCL, 0644, 1); + } while (psem == (sem_t *)SEM_FAILED && errno == EINTR); if (psem == (sem_t *)SEM_FAILED) { if (errno == ENAMETOOLONG) { /* Oh well, good try */ @@ -122,7 +124,9 @@ static apr_status_t proc_mutex_posix_create(apr_proc_mutex_t *new_mutex, } else { return errno; } - psem = sem_open(semname, O_CREAT | O_EXCL, 0644, 1); + do { + psem = sem_open(semname, O_CREAT | O_EXCL, 0644, 1); + } while (psem == (sem_t *)SEM_FAILED && errno == EINTR); } if (psem == (sem_t *)SEM_FAILED) { @@ -140,7 +144,12 @@ static apr_status_t proc_mutex_posix_create(apr_proc_mutex_t *new_mutex, static apr_status_t proc_mutex_posix_acquire(apr_proc_mutex_t *mutex) { - if (sem_wait(mutex->psem_interproc) < 0) { + int rc; + + do { + rc = sem_wait(mutex->psem_interproc); + } while (rc < 0 && errno == EINTR); + if (rc < 0) { return errno; } mutex->curr_locked = 1; @@ -149,7 +158,12 @@ static apr_status_t proc_mutex_posix_acquire(apr_proc_mutex_t *mutex) static apr_status_t proc_mutex_posix_tryacquire(apr_proc_mutex_t *mutex) { - if (sem_trywait(mutex->psem_interproc) < 0) { + int rc; + + do { + rc = sem_trywait(mutex->psem_interproc); + } while (rc < 0 && errno == EINTR); + if (rc < 0) { if (errno == EAGAIN) { return APR_EBUSY; } diff --git a/contrib/apr/memory/unix/apr_pools.c b/contrib/apr/memory/unix/apr_pools.c index 5c1a1ff98853..20e4254fbb09 100644 --- a/contrib/apr/memory/unix/apr_pools.c +++ b/contrib/apr/memory/unix/apr_pools.c @@ -1135,21 +1135,12 @@ APR_DECLARE(char *) apr_pvsprintf(apr_pool_t *pool, const char *fmt, va_list ap) * room to hold the NUL terminator. */ if (ps.node->first_avail == ps.node->endp) { - if (psprintf_flush(&ps.vbuff) == -1) { - if (pool->abort_fn) { - pool->abort_fn(APR_ENOMEM); - } - - return NULL; - } + if (psprintf_flush(&ps.vbuff) == -1) + goto error; } - if (apr_vformatter(psprintf_flush, &ps.vbuff, fmt, ap) == -1) { - if (pool->abort_fn) - pool->abort_fn(APR_ENOMEM); - - return NULL; - } + if (apr_vformatter(psprintf_flush, &ps.vbuff, fmt, ap) == -1) + goto error; strp = ps.vbuff.curpos; *strp++ = '\0'; @@ -1195,6 +1186,15 @@ APR_DECLARE(char *) apr_pvsprintf(apr_pool_t *pool, const char *fmt, va_list ap) list_insert(active, node); return strp; + +error: + if (pool->abort_fn) + pool->abort_fn(APR_ENOMEM); + if (ps.got_a_new_node) { + ps.node->next = ps.free; + allocator_free(pool->allocator, ps.node); + } + return NULL; } diff --git a/contrib/apr/misc/unix/errorcodes.c b/contrib/apr/misc/unix/errorcodes.c index 75567c246576..f553a37b614d 100644 --- a/contrib/apr/misc/unix/errorcodes.c +++ b/contrib/apr/misc/unix/errorcodes.c @@ -39,6 +39,8 @@ static char *stuffbuffer(char *buf, apr_size_t bufsize, const char *s) static char *apr_error_string(apr_status_t statcode) { switch (statcode) { + case APR_ENOSTAT: + return "Could not perform a stat on the file."; case APR_ENOPOOL: return "A new pool could not be created."; case APR_EBADDATE: @@ -73,7 +75,10 @@ static char *apr_error_string(apr_status_t statcode) return "The specified IP address is invalid."; case APR_EBADMASK: return "The specified network mask is invalid."; - + case APR_ESYMNOTFOUND: + return "Could not find the requested symbol."; + case APR_ENOTENOUGHENTROPY: + return "Not enough entropy to continue."; case APR_INCHILD: return "Your code just forked, and you are currently executing in the " @@ -128,10 +133,12 @@ static char *apr_error_string(apr_status_t statcode) return "The given path is misformatted or contained invalid characters"; case APR_EPATHWILD: return "The given path contained wildcard characters"; + case APR_EBUSY: + return "The given lock was busy."; case APR_EPROC_UNKNOWN: return "The process is not recognized."; case APR_EGENERAL: - return "Internal error"; + return "Internal error (specific information not available)"; default: return "Error string not specified yet"; } diff --git a/contrib/apr/network_io/unix/sockaddr.c b/contrib/apr/network_io/unix/sockaddr.c index 0dd1a2d4518d..e6d7e0be93d8 100644 --- a/contrib/apr/network_io/unix/sockaddr.c +++ b/contrib/apr/network_io/unix/sockaddr.c @@ -325,6 +325,16 @@ static apr_status_t call_resolver(apr_sockaddr_t **sa, hints.ai_flags = AI_ADDRCONFIG; } #endif + +#ifdef __MVS__ + /* z/OS will not return IPv4 address under AF_UNSPEC if any IPv6 results + * are returned, w/o AI_ALL. + */ + if (family == APR_UNSPEC) { + hints.ai_flags |= AI_ALL; + } +#endif + if(hostname == NULL) { #ifdef AI_PASSIVE /* If hostname is NULL, assume we are trying to bind to all diff --git a/contrib/apr/network_io/unix/sockets.c b/contrib/apr/network_io/unix/sockets.c index 514edb1a499a..b95794f183ad 100644 --- a/contrib/apr/network_io/unix/sockets.c +++ b/contrib/apr/network_io/unix/sockets.c @@ -145,13 +145,22 @@ apr_status_t apr_socket_create(apr_socket_t **new, int ofamily, int type, #ifndef HAVE_SOCK_CLOEXEC { int flags; + apr_status_t rv; - if ((flags = fcntl((*new)->socketdes, F_GETFD)) == -1) - return errno; + if ((flags = fcntl((*new)->socketdes, F_GETFD)) == -1) { + rv = errno; + close((*new)->socketdes); + (*new)->socketdes = -1; + return rv; + } flags |= FD_CLOEXEC; - if (fcntl((*new)->socketdes, F_SETFD, flags) == -1) - return errno; + if (fcntl((*new)->socketdes, F_SETFD, flags) == -1) { + rv = errno; + close((*new)->socketdes); + (*new)->socketdes = -1; + return rv; + } } #endif @@ -306,13 +315,22 @@ apr_status_t apr_socket_accept(apr_socket_t **new, apr_socket_t *sock, #ifndef HAVE_ACCEPT4 { int flags; + apr_status_t rv; - if ((flags = fcntl((*new)->socketdes, F_GETFD)) == -1) - return errno; + if ((flags = fcntl((*new)->socketdes, F_GETFD)) == -1) { + rv = errno; + close((*new)->socketdes); + (*new)->socketdes = -1; + return rv; + } flags |= FD_CLOEXEC; - if (fcntl((*new)->socketdes, F_SETFD, flags) == -1) - return errno; + if (fcntl((*new)->socketdes, F_SETFD, flags) == -1) { + rv = errno; + close((*new)->socketdes); + (*new)->socketdes = -1; + return rv; + } } #endif diff --git a/contrib/apr/poll/unix/epoll.c b/contrib/apr/poll/unix/epoll.c index 326dac7b1e90..fe006db013c0 100644 --- a/contrib/apr/poll/unix/epoll.c +++ b/contrib/apr/poll/unix/epoll.c @@ -104,14 +104,22 @@ static apr_status_t impl_pollset_create(apr_pollset_t *pollset, #ifndef HAVE_EPOLL_CREATE1 { - int flags; + int fd_flags; - if ((flags = fcntl(fd, F_GETFD)) == -1) - return errno; + if ((fd_flags = fcntl(fd, F_GETFD)) == -1) { + rv = errno; + close(fd); + pollset->p = NULL; + return rv; + } - flags |= FD_CLOEXEC; - if (fcntl(fd, F_SETFD, flags) == -1) - return errno; + fd_flags |= FD_CLOEXEC; + if (fcntl(fd, F_SETFD, fd_flags) == -1) { + rv = errno; + close(fd); + pollset->p = NULL; + return rv; + } } #endif @@ -122,11 +130,13 @@ static apr_status_t impl_pollset_create(apr_pollset_t *pollset, ((rv = apr_thread_mutex_create(&pollset->p->ring_lock, APR_THREAD_MUTEX_DEFAULT, p)) != APR_SUCCESS)) { + close(fd); pollset->p = NULL; return rv; } #else if (flags & APR_POLLSET_THREADSAFE) { + close(fd); pollset->p = NULL; return APR_ENOTIMPL; } @@ -345,14 +355,23 @@ static apr_status_t impl_pollcb_create(apr_pollcb_t *pollcb, #ifndef HAVE_EPOLL_CREATE1 { - int flags; + int fd_flags; + apr_status_t rv; - if ((flags = fcntl(fd, F_GETFD)) == -1) - return errno; + if ((fd_flags = fcntl(fd, F_GETFD)) == -1) { + rv = errno; + close(fd); + pollcb->fd = -1; + return rv; + } - flags |= FD_CLOEXEC; - if (fcntl(fd, F_SETFD, flags) == -1) - return errno; + fd_flags |= FD_CLOEXEC; + if (fcntl(fd, F_SETFD, fd_flags) == -1) { + rv = errno; + close(fd); + pollcb->fd = -1; + return rv; + } } #endif diff --git a/contrib/apr/poll/unix/kqueue.c b/contrib/apr/poll/unix/kqueue.c index dbe785a1546e..efc589869a69 100644 --- a/contrib/apr/poll/unix/kqueue.c +++ b/contrib/apr/poll/unix/kqueue.c @@ -115,12 +115,20 @@ static apr_status_t impl_pollset_create(apr_pollset_t *pollset, { int flags; - if ((flags = fcntl(pollset->p->kqueue_fd, F_GETFD)) == -1) - return errno; + if ((flags = fcntl(pollset->p->kqueue_fd, F_GETFD)) == -1) { + rv = errno; + close(pollset->p->kqueue_fd); + pollset->p = NULL; + return rv; + } flags |= FD_CLOEXEC; - if (fcntl(pollset->p->kqueue_fd, F_SETFD, flags) == -1) - return errno; + if (fcntl(pollset->p->kqueue_fd, F_SETFD, flags) == -1) { + rv = errno; + close(pollset->p->kqueue_fd); + pollset->p = NULL; + return rv; + } } pollset->p->result_set = apr_palloc(p, pollset->p->setsize * sizeof(apr_pollfd_t)); @@ -338,13 +346,22 @@ static apr_status_t impl_pollcb_create(apr_pollcb_t *pollcb, { int flags; + apr_status_t rv; - if ((flags = fcntl(fd, F_GETFD)) == -1) - return errno; + if ((flags = fcntl(fd, F_GETFD)) == -1) { + rv = errno; + close(fd); + pollcb->fd = -1; + return rv; + } flags |= FD_CLOEXEC; - if (fcntl(fd, F_SETFD, flags) == -1) - return errno; + if (fcntl(fd, F_SETFD, flags) == -1) { + rv = errno; + close(fd); + pollcb->fd = -1; + return rv; + } } pollcb->fd = fd; diff --git a/contrib/apr/poll/unix/poll.c b/contrib/apr/poll/unix/poll.c index 7d157367b723..d7a436fd9a14 100644 --- a/contrib/apr/poll/unix/poll.c +++ b/contrib/apr/poll/unix/poll.c @@ -240,26 +240,25 @@ static apr_status_t impl_pollset_poll(apr_pollset_t *pollset, { int ret; apr_status_t rv = APR_SUCCESS; -#ifdef WIN32 - apr_interval_time_t orig_timeout = timeout; -#endif - if (timeout > 0) { - timeout /= 1000; - } #ifdef WIN32 /* WSAPoll() requires at least one socket. */ if (pollset->nelts == 0) { *num = 0; - if (orig_timeout > 0) { - apr_sleep(orig_timeout); + if (timeout > 0) { + apr_sleep(timeout); return APR_TIMEUP; } return APR_SUCCESS; } - + if (timeout > 0) { + timeout /= 1000; + } ret = WSAPoll(pollset->p->pollset, pollset->nelts, (int)timeout); #else + if (timeout > 0) { + timeout /= 1000; + } ret = poll(pollset->p->pollset, pollset->nelts, timeout); #endif (*num) = ret; @@ -398,12 +397,23 @@ static apr_status_t impl_pollcb_poll(apr_pollcb_t *pollcb, apr_status_t rv = APR_SUCCESS; apr_uint32_t i; +#ifdef WIN32 + /* WSAPoll() requires at least one socket. */ + if (pollcb->nelts == 0) { + if (timeout > 0) { + apr_sleep(timeout); + return APR_TIMEUP; + } + return APR_SUCCESS; + } if (timeout > 0) { timeout /= 1000; } -#ifdef WIN32 ret = WSAPoll(pollcb->pollset.ps, pollcb->nelts, (int)timeout); #else + if (timeout > 0) { + timeout /= 1000; + } ret = poll(pollcb->pollset.ps, pollcb->nelts, timeout); #endif if (ret < 0) { diff --git a/contrib/apr/poll/unix/pollcb.c b/contrib/apr/poll/unix/pollcb.c index 24f80106a44f..da12f015a74f 100644 --- a/contrib/apr/poll/unix/pollcb.c +++ b/contrib/apr/poll/unix/pollcb.c @@ -136,6 +136,9 @@ APR_DECLARE(apr_status_t) apr_pollcb_create_ex(apr_pollcb_t **ret_pollcb, } pollcb->provider = provider; } + else if (rv != APR_SUCCESS) { + return rv; + } *ret_pollcb = pollcb; return APR_SUCCESS; diff --git a/contrib/apr/poll/unix/port.c b/contrib/apr/poll/unix/port.c index 7a31c4683243..5002dfdbe24c 100644 --- a/contrib/apr/poll/unix/port.c +++ b/contrib/apr/poll/unix/port.c @@ -188,12 +188,20 @@ static apr_status_t impl_pollset_create(apr_pollset_t *pollset, { int flags; - if ((flags = fcntl(pollset->p->port_fd, F_GETFD)) == -1) - return errno; + if ((flags = fcntl(pollset->p->port_fd, F_GETFD)) == -1) { + rv = errno; + close(pollset->p->port_fd); + pollset->p = NULL; + return rv; + } flags |= FD_CLOEXEC; - if (fcntl(pollset->p->port_fd, F_SETFD, flags) == -1) - return errno; + if (fcntl(pollset->p->port_fd, F_SETFD, flags) == -1) { + rv = errno; + close(pollset->p->port_fd); + pollset->p = NULL; + return rv; + } } pollset->p->result_set = apr_palloc(p, size * sizeof(apr_pollfd_t)); @@ -478,13 +486,22 @@ static apr_status_t impl_pollcb_create(apr_pollcb_t *pollcb, { int flags; + apr_status_t rv; - if ((flags = fcntl(pollcb->fd, F_GETFD)) == -1) - return errno; + if ((flags = fcntl(pollcb->fd, F_GETFD)) == -1) { + rv = errno; + close(pollcb->fd); + pollcb->fd = -1; + return rv; + } flags |= FD_CLOEXEC; - if (fcntl(pollcb->fd, F_SETFD, flags) == -1) - return errno; + if (fcntl(pollcb->fd, F_SETFD, flags) == -1) { + rv = errno; + close(pollcb->fd); + pollcb->fd = -1; + return rv; + } } pollcb->pollset.port = apr_palloc(p, size * sizeof(port_event_t)); diff --git a/contrib/apr/poll/unix/z_asio.c b/contrib/apr/poll/unix/z_asio.c index ce158d3169cd..7e0fd89a549c 100644 --- a/contrib/apr/poll/unix/z_asio.c +++ b/contrib/apr/poll/unix/z_asio.c @@ -272,7 +272,7 @@ static apr_status_t asio_pollset_create(apr_pollset_t *pollset, APR_THREAD_MUTEX_DEFAULT, p) != APR_SUCCESS) { DBG1(1, "apr_thread_mutex_create returned %d\n", rv); - pollset = NULL; + pollset->p = NULL; return rv; } rv = msgget(IPC_PRIVATE, S_IWUSR+S_IRUSR); /* user r/w perms */ @@ -280,7 +280,7 @@ static apr_status_t asio_pollset_create(apr_pollset_t *pollset, #if DEBUG perror(__FUNCTION__ " msgget returned < 0 "); #endif - pollset = NULL; + pollset->p = NULL; return rv; } @@ -292,7 +292,7 @@ static apr_status_t asio_pollset_create(apr_pollset_t *pollset, APR_RING_INIT(&priv->prior_ready_ring, asio_elem_t, link); #else /* APR doesn't have threads but caller wants a threadsafe pollset */ - pollset = NULL; + pollset->p = NULL; return APR_ENOTIMPL; #endif @@ -304,6 +304,7 @@ static apr_status_t asio_pollset_create(apr_pollset_t *pollset, priv->query_set = apr_palloc(p, size * sizeof(apr_pollfd_t)); if ((!priv->pollset) || (!priv->query_set)) { + pollset->p = NULL; return APR_ENOMEM; } } @@ -314,6 +315,10 @@ static apr_status_t asio_pollset_create(apr_pollset_t *pollset, priv->size = size; priv->result_set = apr_palloc(p, size * sizeof(apr_pollfd_t)); if (!priv->result_set) { + if (flags & APR_POLLSET_THREADSAFE) { + msgctl(priv->msg_q, IPC_RMID, NULL); + } + pollset->p = NULL; return APR_ENOMEM; } @@ -379,6 +384,7 @@ static apr_status_t asio_pollset_add(apr_pollset_t *pollset, APR_RING_REMOVE(elem, link); DBG1(3, "used recycled memory at %08p\n", elem); elem->state = ASIO_INIT; + elem->a.aio_cflags = 0; } else { elem = (asio_elem_t *) apr_pcalloc(pollset->pool, sizeof(asio_elem_t)); @@ -659,6 +665,7 @@ static apr_status_t asio_pollset_poll(apr_pollset_t *pollset, if (ret == 1) { DBG(4, "asyncio() completed inline\n"); /* it's ready now */ + elem->state = ASIO_COMPLETE; APR_RING_INSERT_TAIL(&(priv->ready_ring), elem, asio_elem_t, link); } diff --git a/contrib/apr/tables/apr_skiplist.c b/contrib/apr/tables/apr_skiplist.c index effcf603b5fe..b4696bd41a5c 100644 --- a/contrib/apr/tables/apr_skiplist.c +++ b/contrib/apr/tables/apr_skiplist.c @@ -25,12 +25,18 @@ #include "apr_skiplist.h" +typedef struct { + apr_skiplistnode **data; + size_t size, pos; + apr_pool_t *p; +} apr_skiplist_q; + struct apr_skiplist { apr_skiplist_compare compare; apr_skiplist_compare comparek; int height; int preheight; - int size; + size_t size; apr_skiplistnode *top; apr_skiplistnode *bottom; /* These two are needed for appending */ @@ -38,6 +44,8 @@ struct apr_skiplist { apr_skiplistnode *bottomend; apr_skiplist *index; apr_array_header_t *memlist; + apr_skiplist_q nodes_q, + stack_q; apr_pool_t *pool; }; @@ -52,20 +60,15 @@ struct apr_skiplistnode { apr_skiplist *sl; }; -#ifndef MIN -#define MIN(a,b) ((a 31) { /* Num bits in return of rand() */ ph = 0; - randseq = (apr_uint32_t) rand(); + randseq = rand(); } - ph++; - return ((randseq & (1 << (ph - 1))) >> (ph - 1)); + return randseq & (1 << ph++); } typedef struct { @@ -103,7 +106,7 @@ APR_DECLARE(void *) apr_skiplist_alloc(apr_skiplist *sl, size_t size) memlist++; } /* no free chunks */ - ptr = apr_pcalloc(sl->pool, size); + ptr = apr_palloc(sl->pool, size); if (!ptr) { return ptr; } @@ -122,7 +125,7 @@ APR_DECLARE(void *) apr_skiplist_alloc(apr_skiplist *sl, size_t size) return ptr; } else { - return calloc(1, size); + return malloc(size); } } @@ -149,27 +152,73 @@ APR_DECLARE(void) apr_skiplist_free(apr_skiplist *sl, void *mem) } } +static apr_status_t skiplist_qpush(apr_skiplist_q *q, apr_skiplistnode *m) +{ + if (q->pos >= q->size) { + apr_skiplistnode **data; + size_t size = (q->pos) ? q->pos * 2 : 32; + if (q->p) { + data = apr_palloc(q->p, size * sizeof(*data)); + if (data) { + memcpy(data, q->data, q->pos * sizeof(*data)); + } + } + else { + data = realloc(q->data, size * sizeof(*data)); + } + if (!data) { + return APR_ENOMEM; + } + q->data = data; + q->size = size; + } + q->data[q->pos++] = m; + return APR_SUCCESS; +} + +static APR_INLINE apr_skiplistnode *skiplist_qpop(apr_skiplist_q *q) +{ + return (q->pos > 0) ? q->data[--q->pos] : NULL; +} + +static APR_INLINE void skiplist_qclear(apr_skiplist_q *q) +{ + q->pos = 0; +} + +static apr_skiplistnode *skiplist_new_node(apr_skiplist *sl) +{ + apr_skiplistnode *m = skiplist_qpop(&sl->nodes_q); + if (!m) { + if (sl->pool) { + m = apr_palloc(sl->pool, sizeof *m); + } + else { + m = malloc(sizeof *m); + } + } + return m; +} + +static apr_status_t skiplist_free_node(apr_skiplist *sl, apr_skiplistnode *m) +{ + return skiplist_qpush(&sl->nodes_q, m); +} + static apr_status_t skiplisti_init(apr_skiplist **s, apr_pool_t *p) { apr_skiplist *sl; if (p) { sl = apr_pcalloc(p, sizeof(apr_skiplist)); sl->memlist = apr_array_make(p, 20, sizeof(memlist_t)); + sl->pool = sl->nodes_q.p = sl->stack_q.p = p; } else { sl = calloc(1, sizeof(apr_skiplist)); + if (!sl) { + return APR_ENOMEM; + } } -#if 0 - sl->compare = (apr_skiplist_compare) NULL; - sl->comparek = (apr_skiplist_compare) NULL; - sl->height = 0; - sl->preheight = 0; - sl->size = 0; - sl->top = NULL; - sl->bottom = NULL; - sl->index = NULL; -#endif - sl->pool = p; *s = sl; return APR_SUCCESS; } @@ -248,56 +297,32 @@ APR_DECLARE(void) apr_skiplist_add_index(apr_skiplist *sl, } } -APR_DECLARE(apr_skiplistnode *) apr_skiplist_getlist(apr_skiplist *sl) -{ - if (!sl->bottom) { - return NULL; - } - return sl->bottom->next; -} - -APR_DECLARE(void *) apr_skiplist_find(apr_skiplist *sl, void *data, apr_skiplistnode **iter) -{ - void *ret; - apr_skiplistnode *aiter; - if (!sl->compare) { - return 0; - } - if (iter) { - ret = apr_skiplist_find_compare(sl, data, iter, sl->compare); - } - else { - ret = apr_skiplist_find_compare(sl, data, &aiter, sl->compare); - } - return ret; -} - static int skiplisti_find_compare(apr_skiplist *sl, void *data, apr_skiplistnode **ret, apr_skiplist_compare comp) { - apr_skiplistnode *m = NULL; int count = 0; + apr_skiplistnode *m; m = sl->top; while (m) { - int compared; - compared = (m->next) ? comp(data, m->next->data) : -1; - if (compared == 0) { - m = m->next; - while (m->down) { - m = m->down; + if (m->next) { + int compared = comp(data, m->next->data); + if (compared == 0) { + m = m->next; + while (m->down) { + m = m->down; + } + *ret = m; + return count; + } + if (compared > 0) { + m = m->next; + count++; + continue; } - *ret = m; - return count; - } - if ((m->next == NULL) || (compared < 0)) { - m = m->down; - count++; - } - else { - m = m->next; - count++; } + m = m->down; + count++; } *ret = NULL; return count; @@ -307,19 +332,47 @@ APR_DECLARE(void *) apr_skiplist_find_compare(apr_skiplist *sli, void *data, apr_skiplistnode **iter, apr_skiplist_compare comp) { - apr_skiplistnode *m = NULL; + apr_skiplistnode *m; apr_skiplist *sl; + if (!comp) { + if (iter) { + *iter = NULL; + } + return NULL; + } if (comp == sli->compare || !sli->index) { sl = sli; } else { apr_skiplist_find(sli->index, (void *)comp, &m); + if (!m) { + if (iter) { + *iter = NULL; + } + return NULL; + } sl = (apr_skiplist *) m->data; } - skiplisti_find_compare(sl, data, iter, sl->comparek); - return (iter && *iter) ? ((*iter)->data) : NULL; + skiplisti_find_compare(sl, data, &m, sl->comparek); + if (iter) { + *iter = m; + } + return (m) ? m->data : NULL; } +APR_DECLARE(void *) apr_skiplist_find(apr_skiplist *sl, void *data, apr_skiplistnode **iter) +{ + return apr_skiplist_find_compare(sl, data, iter, sl->compare); +} + + +APR_DECLARE(apr_skiplistnode *) apr_skiplist_getlist(apr_skiplist *sl) +{ + if (!sl->bottom) { + return NULL; + } + return sl->bottom->next; +} APR_DECLARE(void *) apr_skiplist_next(apr_skiplist *sl, apr_skiplistnode **iter) { @@ -339,98 +392,74 @@ APR_DECLARE(void *) apr_skiplist_previous(apr_skiplist *sl, apr_skiplistnode **i return (*iter) ? ((*iter)->data) : NULL; } -APR_DECLARE(apr_skiplistnode *) apr_skiplist_insert(apr_skiplist *sl, void *data) +static APR_INLINE int skiplist_height(const apr_skiplist *sl) { - if (!sl->compare) { - return 0; - } - return apr_skiplist_insert_compare(sl, data, sl->compare); + /* Skiplists (even empty) always have a top node, although this + * implementation defers its creation until the first insert, or + * deletes it with the last remove. We want the real height here. + */ + return sl->height ? sl->height : 1; } APR_DECLARE(apr_skiplistnode *) apr_skiplist_insert_compare(apr_skiplist *sl, void *data, apr_skiplist_compare comp) { - apr_skiplistnode *m, *p, *tmp, *ret = NULL, **stack; - int nh = 1, ch, stacki; - if (!sl->top) { - sl->height = 1; - sl->topend = sl->bottomend = sl->top = sl->bottom = - (apr_skiplistnode *)apr_skiplist_alloc(sl, sizeof(apr_skiplistnode)); -#if 0 - sl->top->next = (apr_skiplistnode *)NULL; - sl->top->data = (apr_skiplistnode *)NULL; - sl->top->prev = (apr_skiplistnode *)NULL; - sl->top->up = (apr_skiplistnode *)NULL; - sl->top->down = (apr_skiplistnode *)NULL; - sl->top->nextindex = (apr_skiplistnode *)NULL; - sl->top->previndex = (apr_skiplistnode *)NULL; -#endif - sl->top->sl = sl; + apr_skiplistnode *m, *p, *tmp, *ret = NULL; + int ch, nh = 1; + + if (!comp) { + return NULL; } + + ch = skiplist_height(sl); if (sl->preheight) { while (nh < sl->preheight && get_b_rand()) { nh++; } } else { - while (nh <= sl->height && get_b_rand()) { + while (nh <= ch && get_b_rand()) { nh++; } } - /* Now we have the new height at which we wish to insert our new node */ - /* - * Let us make sure that our tree is a least that tall (grow if - * necessary) + + /* Now we have in nh the height at which we wish to insert our new node, + * and in ch the current height: don't create skip paths to the inserted + * element until the walk down through the tree (which decrements ch) + * reaches nh. From there, any walk down pushes the current node on a + * stack (the node(s) after which we would insert) to pop back through + * for insertion later. */ - for (; sl->height < nh; sl->height++) { - sl->top->up = - (apr_skiplistnode *)apr_skiplist_alloc(sl, sizeof(apr_skiplistnode)); - sl->top->up->down = sl->top; - sl->top = sl->topend = sl->top->up; -#if 0 - sl->top->prev = sl->top->next = sl->top->nextindex = - sl->top->previndex = sl->top->up = NULL; - sl->top->data = NULL; -#endif - sl->top->sl = sl; - } - ch = sl->height; - /* Find the node (or node after which we would insert) */ - /* Keep a stack to pop back through for insertion */ - /* malloc() is OK since we free the temp stack */ m = sl->top; - stack = (apr_skiplistnode **)malloc(sizeof(apr_skiplistnode *) * (nh)); - stacki = 0; while (m) { - int compared = -1; if (m->next) { - compared = comp(data, m->next->data); - } - if (compared == 0) { - free(stack); /* OK. was malloc'ed */ - return 0; - } - if ((m->next == NULL) || (compared < 0)) { - if (ch <= nh) { - /* push on stack */ - stack[stacki++] = m; + int compared = comp(data, m->next->data); + if (compared == 0) { + /* Keep the existing element(s) */ + skiplist_qclear(&sl->stack_q); + return NULL; + } + if (compared > 0) { + m = m->next; + continue; } - m = m->down; - ch--; } - else { - m = m->next; + if (ch <= nh) { + /* push on stack */ + skiplist_qpush(&sl->stack_q, m); } + m = m->down; + ch--; } /* Pop the stack and insert nodes */ p = NULL; - for (; stacki > 0; stacki--) { - m = stack[stacki - 1]; - tmp = (apr_skiplistnode *)apr_skiplist_alloc(sl, sizeof(apr_skiplistnode)); + while ((m = skiplist_qpop(&sl->stack_q))) { + tmp = skiplist_new_node(sl); tmp->next = m->next; if (m->next) { m->next->prev = tmp; } + m->next = tmp; tmp->prev = m; tmp->up = NULL; tmp->nextindex = tmp->previndex = NULL; @@ -438,17 +467,44 @@ APR_DECLARE(apr_skiplistnode *) apr_skiplist_insert_compare(apr_skiplist *sl, vo if (p) { p->up = tmp; } + else { + /* This sets ret to the bottom-most node we are inserting */ + ret = tmp; + } tmp->data = data; tmp->sl = sl; + p = tmp; + } + + /* Now we are sure the node is inserted, grow our tree to 'nh' tall */ + for (; sl->height < nh; sl->height++) { + m = skiplist_new_node(sl); + tmp = skiplist_new_node(sl); + m->up = m->prev = m->nextindex = m->previndex = NULL; m->next = tmp; - /* This sets ret to the bottom-most node we are inserting */ - if (!p) { + m->down = sl->top; + m->data = NULL; + m->sl = sl; + if (sl->top) { + sl->top->up = m; + } + else { + sl->bottom = sl->bottomend = m; + } + sl->top = sl->topend = tmp->prev = m; + tmp->up = tmp->next = tmp->nextindex = tmp->previndex = NULL; + tmp->down = p; + tmp->data = data; + tmp->sl = sl; + if (p) { + p->up = tmp; + } + else { + /* This sets ret to the bottom-most node we are inserting */ ret = tmp; - sl->size++; /* this seems to go here got each element to be counted */ } p = tmp; } - free(stack); /* OK. was malloc'ed */ if (sl->index != NULL) { /* * this is a external insertion, we must insert into each index as @@ -457,25 +513,20 @@ APR_DECLARE(apr_skiplistnode *) apr_skiplist_insert_compare(apr_skiplist *sl, vo apr_skiplistnode *ni, *li; li = ret; for (p = apr_skiplist_getlist(sl->index); p; apr_skiplist_next(sl->index, &p)) { - ni = apr_skiplist_insert((apr_skiplist *) p->data, ret->data); + apr_skiplist *sli = (apr_skiplist *)p->data; + ni = apr_skiplist_insert_compare(sli, ret->data, sli->compare); li->nextindex = ni; ni->previndex = li; li = ni; } } - else { - /* sl->size++; */ - } sl->size++; return ret; } -APR_DECLARE(int) apr_skiplist_remove(apr_skiplist *sl, void *data, apr_skiplist_freefunc myfree) +APR_DECLARE(apr_skiplistnode *) apr_skiplist_insert(apr_skiplist *sl, void *data) { - if (!sl->compare) { - return 0; - } - return apr_skiplist_remove_compare(sl, data, myfree, sl->comparek); + return apr_skiplist_insert_compare(sl, data, sl->compare); } #if 0 @@ -520,7 +571,7 @@ static int skiplisti_remove(apr_skiplist *sl, apr_skiplistnode *m, apr_skiplist_ if (!m && myfree && p->data) { myfree(p->data); } - apr_skiplist_free(sl, p); + skiplist_free_node(sl, p); } sl->size--; while (sl->top && sl->top->next == NULL) { @@ -530,13 +581,14 @@ static int skiplisti_remove(apr_skiplist *sl, apr_skiplistnode *m, apr_skiplist_ if (sl->top) { sl->top->up = NULL; /* Make it think its the top */ } - apr_skiplist_free(sl, p); + skiplist_free_node(sl, p); sl->height--; } if (!sl->top) { - sl->bottom = NULL; + sl->bottom = sl->bottomend = NULL; + sl->topend = NULL; } - return sl->height; /* return 1; ?? */ + return skiplist_height(sl); } APR_DECLARE(int) apr_skiplist_remove_compare(apr_skiplist *sli, @@ -545,11 +597,17 @@ APR_DECLARE(int) apr_skiplist_remove_compare(apr_skiplist *sli, { apr_skiplistnode *m; apr_skiplist *sl; + if (!comp) { + return 0; + } if (comp == sli->comparek || !sli->index) { sl = sli; } else { apr_skiplist_find(sli->index, (void *)comp, &m); + if (!m) { + return 0; + } sl = (apr_skiplist *) m->data; } skiplisti_find_compare(sl, data, &m, comp); @@ -562,6 +620,11 @@ APR_DECLARE(int) apr_skiplist_remove_compare(apr_skiplist *sli, return skiplisti_remove(sl, m, myfree); } +APR_DECLARE(int) apr_skiplist_remove(apr_skiplist *sl, void *data, apr_skiplist_freefunc myfree) +{ + return apr_skiplist_remove_compare(sl, data, myfree, sl->comparek); +} + APR_DECLARE(void) apr_skiplist_remove_all(apr_skiplist *sl, apr_skiplist_freefunc myfree) { /* @@ -573,16 +636,18 @@ APR_DECLARE(void) apr_skiplist_remove_all(apr_skiplist *sl, apr_skiplist_freefun m = sl->bottom; while (m) { p = m->next; - if (p && myfree && p->data) + if (myfree && p && p->data) { myfree(p->data); - while (m) { - u = m->up; - apr_skiplist_free(sl, p); - m = u; } + do { + u = m->up; + skiplist_free_node(sl, m); + m = u; + } while (m); m = p; } sl->top = sl->bottom = NULL; + sl->topend = sl->bottomend = NULL; sl->height = 0; sl->size = 0; } @@ -611,8 +676,7 @@ APR_DECLARE(void *) apr_skiplist_peek(apr_skiplist *a) static void skiplisti_destroy(void *vsl) { - apr_skiplist_destroy((apr_skiplist *) vsl, NULL); - apr_skiplist_free((apr_skiplist *) vsl, vsl); + apr_skiplist_destroy(vsl, NULL); } APR_DECLARE(void) apr_skiplist_destroy(apr_skiplist *sl, apr_skiplist_freefunc myfree) @@ -620,6 +684,13 @@ APR_DECLARE(void) apr_skiplist_destroy(apr_skiplist *sl, apr_skiplist_freefunc m while (apr_skiplist_pop(sl->index, skiplisti_destroy) != NULL) ; apr_skiplist_remove_all(sl, myfree); + if (!sl->pool) { + while (sl->nodes_q.pos) + free(sl->nodes_q.data[--sl->nodes_q.pos]); + free(sl->nodes_q.data); + free(sl->stack_q.data); + free(sl); + } } APR_DECLARE(apr_skiplist *) apr_skiplist_merge(apr_skiplist *sl1, apr_skiplist *sl2) diff --git a/contrib/binutils/gas/config/tc-i386.c b/contrib/binutils/gas/config/tc-i386.c index 5c8e6b14f984..2029c87f8c51 100644 --- a/contrib/binutils/gas/config/tc-i386.c +++ b/contrib/binutils/gas/config/tc-i386.c @@ -914,8 +914,8 @@ fits_in_signed_long (offsetT num ATTRIBUTE_UNUSED) #ifndef BFD64 return 1; #else - return (!(((offsetT) -1 << 31) & num) - || (((offsetT) -1 << 31) & num) == ((offsetT) -1 << 31)); + return (!(-((offsetT) 1 << 31) & num) + || (-((offsetT) 1 << 31) & num) == -((offsetT) 1 << 31)); #endif } /* fits_in_signed_long() */ diff --git a/contrib/bsnmp/snmp_mibII/mibII_ip.c b/contrib/bsnmp/snmp_mibII/mibII_ip.c index 11efe8274dd8..1e33dc974903 100644 --- a/contrib/bsnmp/snmp_mibII/mibII_ip.c +++ b/contrib/bsnmp/snmp_mibII/mibII_ip.c @@ -151,7 +151,7 @@ int op_ip(struct snmp_context *ctx, struct snmp_value *value, u_int sub, u_int idx __unused, enum snmp_op op) { - int old; + int old = 0; switch (op) { diff --git a/contrib/compiler-rt/lib/builtins/floatditf.c b/contrib/compiler-rt/lib/builtins/floatditf.c index 01261c68db98..1a5f8e539470 100644 --- a/contrib/compiler-rt/lib/builtins/floatditf.c +++ b/contrib/compiler-rt/lib/builtins/floatditf.c @@ -34,7 +34,7 @@ COMPILER_RT_ABI fp_t __floatditf(di_int a) { } // Exponent of (fp_t)a is the width of abs(a). - const int exponent = (aWidth - 1) - __builtin_clz(a); + const int exponent = (aWidth - 1) - __builtin_clzll(a); rep_t result; // Shift a into the significand field and clear the implicit bit. Extra diff --git a/contrib/compiler-rt/lib/builtins/floatunditf.c b/contrib/compiler-rt/lib/builtins/floatunditf.c index 7533f8106d39..8098e95e82bc 100644 --- a/contrib/compiler-rt/lib/builtins/floatunditf.c +++ b/contrib/compiler-rt/lib/builtins/floatunditf.c @@ -25,7 +25,7 @@ COMPILER_RT_ABI fp_t __floatunditf(du_int a) { if (a == 0) return fromRep(0); // Exponent of (fp_t)a is the width of abs(a). - const int exponent = (aWidth - 1) - __builtin_clz(a); + const int exponent = (aWidth - 1) - __builtin_clzll(a); rep_t result; // Shift a into the significand field and clear the implicit bit. diff --git a/contrib/compiler-rt/lib/builtins/multc3.c b/contrib/compiler-rt/lib/builtins/multc3.c index b9953cfd6111..0518bc2569f1 100644 --- a/contrib/compiler-rt/lib/builtins/multc3.c +++ b/contrib/compiler-rt/lib/builtins/multc3.c @@ -18,55 +18,50 @@ /* Returns: the product of a + ib and c + id */ COMPILER_RT_ABI long double _Complex -__multc3(long double __a, long double __b, long double __c, long double __d) +__multc3(long double a, long double b, long double c, long double d) { - long double __ac = __a * __c; - long double __bd = __b * __d; - long double __ad = __a * __d; - long double __bc = __b * __c; + long double ac = a * c; + long double bd = b * d; + long double ad = a * d; + long double bc = b * c; long double _Complex z; - __real__ z = __ac - __bd; - __imag__ z = __ad + __bc; - if (crt_isnan(__real__ z) && crt_isnan(__imag__ z)) - { - int __recalc = 0; - if (crt_isinf(__a) || crt_isinf(__b)) - { - __a = crt_copysignl(crt_isinf(__a) ? 1 : 0, __a); - __b = crt_copysignl(crt_isinf(__b) ? 1 : 0, __b); - if (crt_isnan(__c)) - __c = crt_copysignl(0, __c); - if (crt_isnan(__d)) - __d = crt_copysignl(0, __d); - __recalc = 1; + __real__ z = ac - bd; + __imag__ z = ad + bc; + if (crt_isnan(__real__ z) && crt_isnan(__imag__ z)) { + int recalc = 0; + if (crt_isinf(a) || crt_isinf(b)) { + a = crt_copysignl(crt_isinf(a) ? 1 : 0, a); + b = crt_copysignl(crt_isinf(b) ? 1 : 0, b); + if (crt_isnan(c)) + c = crt_copysignl(0, c); + if (crt_isnan(d)) + d = crt_copysignl(0, d); + recalc = 1; } - if (crt_isinf(__c) || crt_isinf(__d)) - { - __c = crt_copysignl(crt_isinf(__c) ? 1 : 0, __c); - __d = crt_copysignl(crt_isinf(__d) ? 1 : 0, __d); - if (crt_isnan(__a)) - __a = crt_copysignl(0, __a); - if (crt_isnan(__b)) - __b = crt_copysignl(0, __b); - __recalc = 1; + if (crt_isinf(c) || crt_isinf(d)) { + c = crt_copysignl(crt_isinf(c) ? 1 : 0, c); + d = crt_copysignl(crt_isinf(d) ? 1 : 0, d); + if (crt_isnan(a)) + a = crt_copysignl(0, a); + if (crt_isnan(b)) + b = crt_copysignl(0, b); + recalc = 1; } - if (!__recalc && (crt_isinf(__ac) || crt_isinf(__bd) || - crt_isinf(__ad) || crt_isinf(__bc))) - { - if (crt_isnan(__a)) - __a = crt_copysignl(0, __a); - if (crt_isnan(__b)) - __b = crt_copysignl(0, __b); - if (crt_isnan(__c)) - __c = crt_copysignl(0, __c); - if (crt_isnan(__d)) - __d = crt_copysignl(0, __d); - __recalc = 1; + if (!recalc && (crt_isinf(ac) || crt_isinf(bd) || + crt_isinf(ad) || crt_isinf(bc))) { + if (crt_isnan(a)) + a = crt_copysignl(0, a); + if (crt_isnan(b)) + b = crt_copysignl(0, b); + if (crt_isnan(c)) + c = crt_copysignl(0, c); + if (crt_isnan(d)) + d = crt_copysignl(0, d); + recalc = 1; } - if (__recalc) - { - __real__ z = CRT_INFINITY * (__a * __c - __b * __d); - __imag__ z = CRT_INFINITY * (__a * __d + __b * __c); + if (recalc) { + __real__ z = CRT_INFINITY * (a * c - b * d); + __imag__ z = CRT_INFINITY * (a * d + b * c); } } return z; diff --git a/contrib/elftoolchain/ar/Makefile b/contrib/elftoolchain/ar/Makefile new file mode 100644 index 000000000000..ddd811338d92 --- /dev/null +++ b/contrib/elftoolchain/ar/Makefile @@ -0,0 +1,35 @@ +# $Id: Makefile 3107 2014-12-20 08:31:58Z kaiwang27 $ + +TOP= .. + +PROG= ar +SRCS= ar.c read.c util.c write.c +LSRC= acplex.l +YSRC= acpyacc.y + +WARNS?= 5 + +DPADD= ${LIBARCHIVE} ${LIBELFTC} ${LIBELF} ${LIBZ} +LDADD= -larchive -lelftc -lelf -lz + +CFLAGS+=-I. -I${.CURDIR} + +LINKS= ${BINDIR}/ar ${BINDIR}/ranlib + +EXTRA_TARGETS= ranlib + +CLEANFILES+= ${EXTRA_TARGETS} + +MAN= ar.1 ranlib.1 ar.5 + +all: ${EXTRA_TARGETS} + +${EXTRA_TARGETS}: ${PROG} + ln -s ${PROG} ${.TARGET} + +.include "${TOP}/mk/elftoolchain.prog.mk" + +.if ${OS_HOST} == "OpenBSD" +CFLAGS+= -I/usr/local/include +LDFLAGS+= -L/usr/local/lib +.endif diff --git a/contrib/elftoolchain/ar/acplex.l b/contrib/elftoolchain/ar/acplex.l new file mode 100644 index 000000000000..6b50fb3b19dd --- /dev/null +++ b/contrib/elftoolchain/ar/acplex.l @@ -0,0 +1,83 @@ +%{ +/*- + * Copyright (c) 2008 Kai Wang + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer + * in this position and unchanged. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include + +#include "_elftc.h" + +ELFTC_VCSID("$Id: acplex.l 3174 2015-03-27 17:13:41Z emaste $"); + +#include "acpyacc.h" + +#define YY_NO_UNPUT +#if !defined(ELFTC_BROKEN_YY_NO_INPUT) +#define YY_NO_INPUT +#endif + +int lineno = 1; + +int yylex(void); + +%} + +%option nounput +%option noyywrap + +%% + +ADDLIB|addlib { return (ADDLIB); } +ADDMOD|addmod { return (ADDMOD); } +CLEAR|clear { return (CLEAR); } +CREATE|create { return (CREATE); } +DELETE|delete { return (DELETE); } +DIRECTORY|directory { return (DIRECTORY); } +END|end { return (END); } +EXTRACT|extract { return (EXTRACT); } +LIST|list { return (LIST); } +OPEN|open { return (OPEN); } +REPLACE|replace { return (REPLACE); } +VERBOSE|verbose { return (VERBOSE); } +SAVE|save { return (SAVE); } +"(" { return (LP); } +")" { return (RP); } +"," { return (COMMA); } + +[-_A-Za-z0-9/:$.\\]+ { + yylval.str = strdup(yytext); + if (yylval.str == NULL) + err(EXIT_FAILURE, "strdup failed"); + return (FNAME); +} + +[ \t] /* whitespace */ +"*".* /* comment */ +";".* /* comment */ +"+\n" { lineno++; /* '+' is line continuation char */ } +"\n" { lineno++; return (EOL); } diff --git a/contrib/elftoolchain/ar/acpyacc.y b/contrib/elftoolchain/ar/acpyacc.y new file mode 100644 index 000000000000..84f08e2393af --- /dev/null +++ b/contrib/elftoolchain/ar/acpyacc.y @@ -0,0 +1,658 @@ +%{ +/*- + * Copyright (c) 2008 Kai Wang + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer + * in this position and unchanged. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libelftc.h" + +#include "ar.h" + +ELFTC_VCSID("$Id"); + + +#define TEMPLATE "arscp.XXXXXXXX" + +struct list { + char *str; + struct list *next; +}; + + +extern int yylex(void); +extern int yyparse(void); + +static void yyerror(const char *); +static void arscp_addlib(char *archive, struct list *list); +static void arscp_addmod(struct list *list); +static void arscp_clear(void); +static void arscp_create(char *in, char *out); +static void arscp_delete(struct list *list); +static void arscp_dir(char *archive, struct list *list, char *rlt); +static void arscp_end(int eval); +static void arscp_extract(struct list *list); +static void arscp_free_argv(void); +static void arscp_free_mlist(struct list *list); +static void arscp_list(void); +static struct list *arscp_mlist(struct list *list, char *str); +static void arscp_mlist2argv(struct list *list); +static int arscp_mlist_len(struct list *list); +static void arscp_open(char *fname); +static void arscp_prompt(void); +static void arscp_replace(struct list *list); +static void arscp_save(void); +static int arscp_target_exist(void); + +extern int lineno; + +static struct bsdar *bsdar; +static char *target; +static char *tmpac; +static int interactive; +static int verbose; + +%} + +%token ADDLIB +%token ADDMOD +%token CLEAR +%token CREATE +%token DELETE +%token DIRECTORY +%token END +%token EXTRACT +%token LIST +%token OPEN +%token REPLACE +%token VERBOSE +%token SAVE +%token LP +%token RP +%token COMMA +%token EOL +%token FNAME +%type mod_list + +%union { + char *str; + struct list *list; +} + +%% + +begin + : { arscp_prompt(); } ar_script + ; + +ar_script + : cmd_list + | + ; + +mod_list + : FNAME { $$ = arscp_mlist(NULL, $1); } + | mod_list separator FNAME { $$ = arscp_mlist($1, $3); } + ; + +separator + : COMMA + | + ; + +cmd_list + : rawcmd + | cmd_list rawcmd + ; + +rawcmd + : cmd EOL { arscp_prompt(); } + ; + +cmd + : addlib_cmd + | addmod_cmd + | clear_cmd + | create_cmd + | delete_cmd + | directory_cmd + | end_cmd + | extract_cmd + | list_cmd + | open_cmd + | replace_cmd + | verbose_cmd + | save_cmd + | invalid_cmd + | empty_cmd + | error + ; + +addlib_cmd + : ADDLIB FNAME LP mod_list RP { arscp_addlib($2, $4); } + | ADDLIB FNAME { arscp_addlib($2, NULL); } + ; + +addmod_cmd + : ADDMOD mod_list { arscp_addmod($2); } + ; + +clear_cmd + : CLEAR { arscp_clear(); } + ; + +create_cmd + : CREATE FNAME { arscp_create(NULL, $2); } + ; + +delete_cmd + : DELETE mod_list { arscp_delete($2); } + ; + +directory_cmd + : DIRECTORY FNAME { arscp_dir($2, NULL, NULL); } + | DIRECTORY FNAME LP mod_list RP { arscp_dir($2, $4, NULL); } + | DIRECTORY FNAME LP mod_list RP FNAME { arscp_dir($2, $4, $6); } + ; + +end_cmd + : END { arscp_end(EXIT_SUCCESS); } + ; + +extract_cmd + : EXTRACT mod_list { arscp_extract($2); } + ; + +list_cmd + : LIST { arscp_list(); } + ; + +open_cmd + : OPEN FNAME { arscp_open($2); } + ; + +replace_cmd + : REPLACE mod_list { arscp_replace($2); } + ; + +save_cmd + : SAVE { arscp_save(); } + ; + +verbose_cmd + : VERBOSE { verbose = !verbose; } + ; + +empty_cmd + : + ; + +invalid_cmd + : FNAME { yyerror(NULL); } + ; + +%% + +/* ARGSUSED */ +static void +yyerror(const char *s) +{ + + (void) s; + printf("Syntax error in archive script, line %d\n", lineno); +} + +/* + * The arscp_open() function will first open an archive and check its + * validity. If the archive format is valid, it will call + * arscp_create() to create a temporary copy of the archive. + */ +static void +arscp_open(char *fname) +{ + struct archive *a; + struct archive_entry *entry; + int r; + + if ((a = archive_read_new()) == NULL) + bsdar_errc(bsdar, 0, "archive_read_new failed"); + archive_read_support_format_ar(a); + AC(archive_read_open_filename(a, fname, DEF_BLKSZ)); + if ((r = archive_read_next_header(a, &entry))) + bsdar_warnc(bsdar, 0, "%s", archive_error_string(a)); + AC(archive_read_close(a)); + ACV(archive_read_free(a)); + if (r != ARCHIVE_OK) + return; + arscp_create(fname, fname); +} + +/* + * Create an archive. + * + * If the parameter 'in' is NULL (the 'CREATE' command), a new empty + * archive will be created. If the parameter 'in' is not NULL (the + * 'OPEN' command), the resulting archive will be a modified version + * of the existing archive. + */ +static void +arscp_create(char *in, char *out) +{ + struct archive *a; + int ifd, ofd; + + /* Delete the previously created temporary archive, if any. */ + if (tmpac) { + if (unlink(tmpac) < 0) + bsdar_errc(bsdar, errno, "unlink failed"); + free(tmpac); + } + + tmpac = strdup(TEMPLATE); + if (tmpac == NULL) + bsdar_errc(bsdar, errno, "strdup failed"); + if ((ofd = mkstemp(tmpac)) < 0) + bsdar_errc(bsdar, errno, "mkstemp failed"); + + if (in) { + /* + * The 'OPEN' command creates a temporary copy of the + * input archive. + */ + if ((ifd = open(in, O_RDONLY)) < 0 || + elftc_copyfile(ifd, ofd) < 0) { + bsdar_warnc(bsdar, errno, "'OPEN' failed"); + (void) close(ofd); + if (ifd != -1) + (void) close(ifd); + return; + } + (void) close(ifd); + (void) close(ofd); + } else { + /* + * The 'CREATE' command creates an "empty" archive (an + * archive consisting only of the archive header). + */ + if ((a = archive_write_new()) == NULL) + bsdar_errc(bsdar, 0, "archive_write_new failed"); + archive_write_set_format_ar_svr4(a); + AC(archive_write_open_fd(a, ofd)); + AC(archive_write_close(a)); + ACV(archive_write_free(a)); + } + + /* Override the previous target, if any. */ + if (target) + free(target); + + target = out; + bsdar->filename = tmpac; +} + +/* + * Add all modules of an archive to the current archive. If the + * parameter 'list' is not NULL, only those modules specified by + * 'list' will be added. + */ +static void +arscp_addlib(char *archive, struct list *list) +{ + + if (!arscp_target_exist()) + return; + arscp_mlist2argv(list); + bsdar->addlib = archive; + ar_write_archive(bsdar, 'A'); + arscp_free_argv(); + arscp_free_mlist(list); +} + +/* + * Add modules to the current archive. + */ +static void +arscp_addmod(struct list *list) +{ + + if (!arscp_target_exist()) + return; + arscp_mlist2argv(list); + ar_write_archive(bsdar, 'q'); + arscp_free_argv(); + arscp_free_mlist(list); +} + +/* + * Delete modules from the current archive. + */ +static void +arscp_delete(struct list *list) +{ + + if (!arscp_target_exist()) + return; + arscp_mlist2argv(list); + ar_write_archive(bsdar, 'd'); + arscp_free_argv(); + arscp_free_mlist(list); +} + +/* + * Extract modules from the current archive. + */ +static void +arscp_extract(struct list *list) +{ + + if (!arscp_target_exist()) + return; + arscp_mlist2argv(list); + ar_read_archive(bsdar, 'x'); + arscp_free_argv(); + arscp_free_mlist(list); +} + +/* + * List the contents of an archive (simple mode). + */ +static void +arscp_list(void) +{ + + if (!arscp_target_exist()) + return; + bsdar->argc = 0; + bsdar->argv = NULL; + /* Always verbose. */ + bsdar->options |= AR_V; + ar_read_archive(bsdar, 't'); + bsdar->options &= ~AR_V; +} + +/* + * List the contents of an archive (advanced mode). + */ +static void +arscp_dir(char *archive, struct list *list, char *rlt) +{ + FILE *out; + + /* If rlt != NULL, redirect the output to it. */ + out = NULL; + if (rlt) { + out = bsdar->output; + if ((bsdar->output = fopen(rlt, "w")) == NULL) + bsdar_errc(bsdar, errno, "fopen %s failed", rlt); + } + + bsdar->filename = archive; + if (list) + arscp_mlist2argv(list); + else { + bsdar->argc = 0; + bsdar->argv = NULL; + } + if (verbose) + bsdar->options |= AR_V; + ar_read_archive(bsdar, 't'); + bsdar->options &= ~AR_V; + + if (rlt) { + if (fclose(bsdar->output) == EOF) + bsdar_errc(bsdar, errno, "fclose %s failed", rlt); + bsdar->output = out; + free(rlt); + } + free(archive); + bsdar->filename = tmpac; + arscp_free_argv(); + arscp_free_mlist(list); +} + + +/* + * Replace modules in the current archive. + */ +static void +arscp_replace(struct list *list) +{ + + if (!arscp_target_exist()) + return; + arscp_mlist2argv(list); + ar_write_archive(bsdar, 'r'); + arscp_free_argv(); + arscp_free_mlist(list); +} + +/* + * Rename the temporary archive to the target archive. + */ +static void +arscp_save(void) +{ + mode_t mask; + + if (target) { + if (rename(tmpac, target) < 0) + bsdar_errc(bsdar, errno, "rename failed"); + /* + * Because mkstemp() creates temporary files with mode + * 0600, we set target archive's mode as per the + * process umask. + */ + mask = umask(0); + umask(mask); + if (chmod(target, 0666 & ~mask) < 0) + bsdar_errc(bsdar, errno, "chmod failed"); + free(tmpac); + free(target); + tmpac = NULL; + target= NULL; + bsdar->filename = NULL; + } else + bsdar_warnc(bsdar, 0, "no open output archive"); +} + +/* + * Discard the contents of the current archive. This is achieved by + * invoking the 'CREATE' cmd on the current archive. + */ +static void +arscp_clear(void) +{ + char *new_target; + + if (target) { + new_target = strdup(target); + if (new_target == NULL) + bsdar_errc(bsdar, errno, "strdup failed"); + arscp_create(NULL, new_target); + } +} + +/* + * Quit ar(1). Note that the 'END' cmd will not 'SAVE' the current + * archive before exiting. + */ +static void +arscp_end(int eval) +{ + + if (target) + free(target); + if (tmpac) { + if (unlink(tmpac) == -1) + bsdar_errc(bsdar, errno, "unlink %s failed", tmpac); + free(tmpac); + } + + exit(eval); +} + +/* + * Check if a target was specified, i.e, whether an 'OPEN' or 'CREATE' + * had been issued by the user. + */ +static int +arscp_target_exist(void) +{ + + if (target) + return (1); + + bsdar_warnc(bsdar, 0, "no open output archive"); + return (0); +} + +/* + * Construct the list of modules. + */ +static struct list * +arscp_mlist(struct list *list, char *str) +{ + struct list *l; + + l = malloc(sizeof(*l)); + if (l == NULL) + bsdar_errc(bsdar, errno, "malloc failed"); + l->str = str; + l->next = list; + + return (l); +} + +/* + * Calculate the length of an mlist. + */ +static int +arscp_mlist_len(struct list *list) +{ + int len; + + for(len = 0; list; list = list->next) + len++; + + return (len); +} + +/* + * Free the space allocated for a module list. + */ +static void +arscp_free_mlist(struct list *list) +{ + struct list *l; + + /* Note: list->str was freed in arscp_free_argv(). */ + for(; list; list = l) { + l = list->next; + free(list); + } +} + +/* + * Convert a module list to an 'argv' array. + */ +static void +arscp_mlist2argv(struct list *list) +{ + char **argv; + int i, n; + + n = arscp_mlist_len(list); + argv = malloc(n * sizeof(*argv)); + if (argv == NULL) + bsdar_errc(bsdar, errno, "malloc failed"); + + /* Note that module names are stored in reverse order. */ + for(i = n - 1; i >= 0; i--, list = list->next) { + if (list == NULL) + bsdar_errc(bsdar, errno, "invalid mlist"); + argv[i] = list->str; + } + + bsdar->argc = n; + bsdar->argv = argv; +} + +/* + * Free the space allocated for an argv array and its elements. + */ +static void +arscp_free_argv(void) +{ + int i; + + for(i = 0; i < bsdar->argc; i++) + free(bsdar->argv[i]); + + free(bsdar->argv); +} + +/* + * Show a prompt if we are in interactive mode. + */ +static void +arscp_prompt(void) +{ + + if (interactive) { + printf("AR >"); + fflush(stdout); + } +} + +/* + * The main function implementing script mode. + */ +void +ar_mode_script(struct bsdar *ar) +{ + + bsdar = ar; + interactive = isatty(fileno(stdin)); + while(yyparse()) { + if (!interactive) + arscp_end(EXIT_FAILURE); + } + + /* Script ends without END */ + arscp_end(EXIT_SUCCESS); +} diff --git a/contrib/elftoolchain/ar/ar.1 b/contrib/elftoolchain/ar/ar.1 new file mode 100644 index 000000000000..2bfa908d6edb --- /dev/null +++ b/contrib/elftoolchain/ar/ar.1 @@ -0,0 +1,603 @@ +.\" Copyright (c) 2007,2009-2012 Joseph Koshy. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" This software is provided by Joseph Koshy ``as is'' and +.\" any express or implied warranties, including, but not limited to, the +.\" implied warranties of merchantability and fitness for a particular purpose +.\" are disclaimed. in no event shall Joseph Koshy be liable +.\" for any direct, indirect, incidental, special, exemplary, or consequential +.\" damages (including, but not limited to, procurement of substitute goods +.\" or services; loss of use, data, or profits; or business interruption) +.\" however caused and on any theory of liability, whether in contract, strict +.\" liability, or tort (including negligence or otherwise) arising in any way +.\" out of the use of this software, even if advised of the possibility of +.\" such damage. +.\" +.\" $Id: ar.1 3195 2015-05-12 17:22:19Z emaste $ +.\" +.Dd December 10, 2012 +.Os +.Dt AR 1 +.Sh NAME +.Nm ar +.Nd manage archives +.Sh SYNOPSIS +.Nm +.Fl d +.Op Fl T +.Op Fl f +.Op Fl j +.Op Fl v +.Op Fl z +.Ar archive +.Ar +.Nm +.Fl m +.Op Fl T +.Op Fl a Ar position-after +.Op Fl b Ar position-before +.Op Fl f +.Op Fl i Ar position-before +.Op Fl j +.Op Fl s | Fl S +.Op Fl z +.Ar archive +.Ar +.Nm +.Fl p +.Op Fl T +.Op Fl f +.Op Fl v +.Ar archive +.Op Ar +.Nm +.Fl q +.Op Fl T +.Op Fl c +.Op Fl D +.Op Fl f +.Op Fl F Ar flavor | Fl -flavor Ar flavor +.Op Fl s | Fl S +.Op Fl v +.Op Fl z +.Ar archive +.Ar +.Nm +.Fl r +.Op Fl T +.Op Fl a Ar position-after +.Op Fl b Ar position-before +.Op Fl c +.Op Fl D +.Op Fl f +.Op Fl F Ar flavor | Fl -flavor Ar flavor +.Op Fl i Ar position-before +.Op Fl j +.Op Fl s | Fl S +.Op Fl u +.Op Fl v +.Op Fl z +.Ar archive +.Ar +.Nm +.Fl s +.Op Fl D +.Op Fl j +.Op Fl z +.Ar archive +.Nm +.Fl t +.Op Fl f +.Op Fl T +.Op Fl v +.Ar archive +.Op Ar +.Nm +.Fl x +.Op Fl C +.Op Fl T +.Op Fl f +.Op Fl o +.Op Fl u +.Op Fl v +.Ar archive +.Op Ar +.Nm +.Fl M +.Nm +.Fl V +.Sh DESCRIPTION +The +.Nm +utility creates and maintains groups of files combined into an +archive. +Once an archive has been created, new files can be added to it, and +existing files can be extracted, deleted or replaced. +.Pp +Files are named in the archive by their last file name component, +so if a file referenced by a path containing a +.Dq / +is archived, it will be named by the last component of the path. +Similarly when matching paths listed on the command line against +file names stored in the archive, only the last component of the +path will be compared. +.Pp +The normal use of +.Nm +is for the creation and maintenance of libraries suitable for use +with the link editor +.Xr ld 1 , +although it is not restricted to this purpose. +The +.Nm +utility can create and manage an archive symbol table (see +.Xr ar 5 ) +used to speed up link editing operations. +If a symbol table is present in an archive, it will be +kept up-to-date by subsequent operations on the archive. +.Sh OPTIONS +The +.Nm +utility supports the following options: +.Bl -tag -width indent +.It Fl a Ar member-after +When used with option +.Fl m +this option specifies that the archive members specified by +arguments +.Ar +are moved to after the archive member named by argument +.Ar member-after . +When used with option +.Fl r +this option specifies that the files specified by arguments +.Ar +are added after the archive member named by argument +.Ar member-after . +.It Fl b Ar member-before +When used with option +.Fl m +this option specifies that the archive members specified by +arguments +.Ar +are moved to before the archive member named by argument +.Ar member-before . +When used with option +.Fl r +this option specifies that the files specified by arguments +.Ar +are added before the archive member named by argument +.Ar member-before . +.It Fl c +Suppress the informational message printed when a new archive is +created using the +.Fl r +and +.Fl q +options. +.It Fl C +Prevent extracted files from replacing like-named files +in the file system. +.It Fl d +Delete the members named by arguments +.Ar +from the archive specified by argument +.Ar archive . +The archive's symbol table, if present, is updated to reflect +the new contents of the archive. +.It Fl D +When used in combination with the +.Fl r +or +.Fl q +option, insert 0's instead of the real mtime, uid and gid values +and 0644 instead of file mode from the members named by arguments +.Ar . +This ensures that checksums on the resulting archives are reproducible +when member contents are identical. +.It Fl f +Synonymous with option +.Fl T . +.It Fl F Ar flavor | Fl -flavor Ar flavor +Create archives with the specified archive format. +Legal values for argument +.Ar flavor +are: +.Bl -tag -width indent -compact +.It Ar bsd +Create BSD format archives. +.It Ar gnu +An alias for +.Ar svr4 . +.It Ar svr4 +Create SVR4 format archives. +.El +If this option is not specified, +.Nm +will create archives using the SVR4 format. +.It Fl i Ar member-before +Synonymous with option +.Fl b . +.It Fl j +This option is accepted for compatibility with the +.Tn FreeBSD +version of the +.Nm +utility, but is ignored. +.It Fl l +This option is accepted for compatibility with GNU +.Xr ar 1 , +but is ignored. +.It Fl m +Move archive members specified by arguments +.Ar +within the archive. +If a position has been specified by one of the +.Fl a , +.Fl b +or +.Fl i +options, the members are moved to before or after the specified +position. +If no position has been specified, the specified members are moved +to the end of the archive. +If the archive has a symbol table, it is updated to reflect the +new contents of the archive. +.It Fl M +Read and execute MRI librarian commands from standard input. +The commands understood by the +.Nm +utility are described in the section +.Sx "MRI Librarian Commands" . +.It Fl o +Preserve the original modification times of members when extracting +them. +.It Fl p +Write the contents of the specified archive members named by +arguments +.Ar +to standard output. +If no members were specified, the contents of all the files in the +archive are written in the order they appear in the archive. +.It Fl q +Append the files specified by arguments +.Ar +to the archive specified by argument +.Ar archive +without checking if the files already exist in the archive. +The archive symbol table will be updated as needed. +If the file specified by the argument +.Ar archive +does not already exist, a new archive will be created. +.It Fl r +Replace (add) the files specified by arguments +.Ar +in the archive specified by argument +.Ar archive , +creating the archive if necessary. +Replacing existing members will not change the order of members within +the archive. +If a file named in arguments +.Ar +does not exist, existing members in the archive that match that +name are not changed. +New files are added to the end of the archive unless one of the +positioning options +.Fl a , +.Fl b +or +.Fl i +is specified. +The archive symbol table, if it exists, is updated to reflect the +new state of the archive. +.It Fl s +Add an archive symbol table (see +.Xr ar 5 ) +to the archive specified by argument +.Ar archive . +Invoking +.Nm +with the +.Fl s +option alone is equivalent to invoking +.Xr ranlib 1 . +.It Fl S +Do not generate an archive symbol table. +.It Fl t +For +.Nm , +list the files specified by arguments +.Ar +in the order in which they appear in the archive, one per line. +If no files are specified, all files in the archive are listed. +.It Fl T +Use only the first fifteen characters of the archive member name or +command line file name argument when naming archive members. +.It Fl u +Conditionally update the archive or extract members. +When used with the +.Fl r +option, files named by arguments +.Ar +will be replaced in the archive if they are newer than their +archived versions. +When used with the +.Fl x +option, the members specified by arguments +.Ar +will be extracted only if they are newer than the corresponding +files in the file system. +.It Fl v +Provide verbose output. +When used with the +.Fl d , +.Fl m , +.Fl q +or +.Fl x +options, +.Nm +gives a file-by-file description of the archive modification being +performed, which consists of three white-space separated fields: +the option letter, a dash +.Dq "-" , +and the file name. +When used with the +.Fl r +option, +.Nm +displays the description as above, but the initial letter is an +.Dq a +if the file is added to the archive, or an +.Dq r +if the file replaces a file already in the archive. +When used with the +.Fl p +option, the name of the file enclosed in +.Dq < +and +.Dq > +characters is written to standard output preceded by a single newline +character and followed by two newline characters. +The contents of the named file follow the file name. +When used with the +.Fl t +option, +.Nm +displays eight whitespace separated fields: +the file permissions as displayed by +.Xr strmode 3 , +decimal user and group IDs separated by a slash ( +.Dq / Ns ) , +the file size in bytes, the file modification time in +.Xr strftime 3 +format +.Dq "%b %e %H:%M %Y" , +and the name of the file. +.It Fl V +Print a version identifier and exit. +.It Fl x +Extract archive members specified by arguments +.Ar +into the current directory. +If no members have been specified, extract all members of the archive. +If the file corresponding to an extracted member does not exist it +will be created. +If the file corresponding to an extracted member does exist, its owner +and group will not be changed while its contents will be overwritten +and its permissions will set to that entered in the archive. +The file's access and modification time would be that of the time +of extraction unless the +.Fl o +option was specified. +.It Fl z +This option is accepted for compatibility with the +.Tn FreeBSD +version of the +.Nm +utility, but is ignored. +.El +.Ss "MRI Librarian Commands" +If the +.Fl M +option is specified, the +.Nm +utility will read and execute commands from its standard input. +If standard input is a terminal, the +.Nm +utility will display the prompt +.Dq Li "AR >" +before reading a line, and will continue operation even if errors are +encountered. +If standard input is not a terminal, the +.Nm +utility will not display a prompt and will terminate execution on +encountering an error. +.Pp +Each input line contains a single command. +Words in an input line are separated by whitespace characters. +The first word of the line is the command, the remaining words are +the arguments to the command. +The command word may be specified in either case. +Arguments may be separated by commas or blanks. +.Pp +Empty lines are allowed and are ignored. +Long lines are continued by ending them with the +.Dq Li + +character. +.Pp +The +.Dq Li * +and +.Dq Li "\;" +characters start a comment. +Comments extend till the end of the line. +.Pp +When executing an MRI librarian script the +.Nm +utility works on a temporary copy of an archive. +Changes to the copy are made permanent using the +.Ic save +command. +.Pp +Commands understood by the +.Nm +utility are: +.Bl -tag -width indent +.It Ic addlib Ar archive | Ic addlib Ar archive Pq Ar member Oo Li , Ar member Oc Ns ... +Add the contents of the archive named by argument +.Ar archive +to the current archive. +If specific members are named using the arguments +.Ar member , +then those members are added to the current archive. +If no members are specified, the entire contents of the archive +are added to the current archive. +.It Ic addmod Ar member Oo Li , Ar member Oc Ns ... +Add the files named by arguments +.Ar member +to the current archive. +.It Ic clear +Discard all the contents of the current archive. +.It Ic create Ar archive +Create a new archive named by the argument +.Ar archive , +and makes it the current archive. +If the named archive already exists, it will be overwritten +when the +.Ic save +command is issued. +.It Ic delete Ar module Oo Li , Ar member Oc Ns ... +Delete the modules named by the arguments +.Ar member +from the current archive. +.It Ic directory Ar archive Po Ar member Oo Li , Ar member Oc Ns ... Pc Op Ar outputfile +List each named module in the archive. +The format of the output depends on the verbosity setting set using +the +.Ic verbose +command. +Output is sent to standard output, or to the file specified by +argument +.Ar outputfile . +.It Ic end +Exit successfully from the +.Nm +utility. +Any unsaved changes to the current archive will be discarded. +.It Ic extract Ar member Oo Li , Ar member Oc Ns ... +Extract the members named by the arguments +.Ar member +from the current archive. +.It Ic list +Display the contents of the current archive in verbose style. +.It Ic open Ar archive +Open the archive named by argument +.Ar archive +and make it the current archive. +.It Ic replace Ar member Oo Li , Ar member Oc Ns ... +Replace named members in the current archive with the files specified +by arguments +.Ar member . +The files must be present in the current directory and the named +modules must already exist in the current archive. +.It Ic save +Commit all changes to the current archive. +.It Ic verbose +Toggle the verbosity of the +.Ic directory +command. +.El +.Sh EXAMPLES +To create a new archive +.Pa ex.a +containing three files +.Pa ex1.o , +.Pa ex2.o +and +.Pa ex3.o , +use: +.Dl "ar -rc ex.a ex1.o ex2.o ex3.o" +.Pp +To add an archive symbol table to an existing archive +.Pa ex.a , +use: +.Dl "ar -s ex.a" +.Pp +To delete file +.Pa ex1.o +from archive +.Pa ex.a , +use: +.D1 "ar -d ex.a ex1.o" +.Pp +To verbosely list the contents of archive +.Pa ex.a , +use: +.D1 "ar -tv ex.a" +.Pp +To create a new archive +.Pa ex.a +containing the files +.Pa ex1.o , +and +.Pa ex2.o , +using MRI librarian commands, use the following script: +.Bd -literal -offset indent +create ex.a * specify the output archive +addmod ex1.o ex2.o * add modules +save * save pending changes +end * exit the utility +.Ed +.Sh DIAGNOSTICS +.Ex -std +.Sh SEE ALSO +.Xr ld 1 , +.Xr ranlib 1 , +.Xr archive 3 , +.Xr elf 3 , +.Xr strftime 3 , +.Xr strmode 3 , +.Xr ar 5 +.Sh STANDARDS COMPLIANCE +The +.Nm +utility's support for the +.Fl a , +.Fl b , +.Fl c , +.Fl i , +.Fl m , +.Fl p , +.Fl q , +.Fl r , +.Fl s , +.Fl t , +.Fl u , +.Fl v , +.Fl C +and +.Fl T +options is believed to be compliant with +.St -p1003.2 . +.Sh HISTORY +An +.Nm +command first appeared in AT&T UNIX Version 1. +In +.Fx 8.0 , +.An Kai Wang Aq Mt kaiw@FreeBSD.org +reimplemented +.Nm +using the +.Lb libarchive +and the +.Lb libelf . diff --git a/contrib/elftoolchain/ar/ar.5 b/contrib/elftoolchain/ar/ar.5 new file mode 100644 index 000000000000..45961e05cf61 --- /dev/null +++ b/contrib/elftoolchain/ar/ar.5 @@ -0,0 +1,327 @@ +.\" Copyright (c) 2010 Joseph Koshy. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $Id: ar.5 3182 2015-04-10 16:08:10Z emaste $ +.\" +.Dd November 28, 2010 +.Os +.Dt AR 5 +.Sh NAME +.Nm ar +.Nd archive file format for +.Xr ar 1 +and +.Xr ranlib 1 +.Sh SYNOPSIS +.In ar.h +.Sh DESCRIPTION +.Xr ar 1 +archives are created and managed by the +.Xr ar 1 +and +.Xr ranlib 1 +utilities. +These archives are typically used during program development to +hold libraries of program objects. +An +.Xr ar 1 +archive is contained in a single operating system file. +.Pp +This manual page documents two variants of the +.Xr ar 1 +archive format: the BSD archive format, and the SVR4/GNU archive +format. +.Pp +In both variants the archive file starts with an identifying byte +sequence of the seven ASCII characters +.Sq Li "!" +followed by a ASCII linefeed character +.Po +see the constant +.Dq ARMAG +in the header file +.In ar.h +.Pc . +.Pp +Archive members follow the initial identifying byte sequence. +Each archive member is prefixed by a fixed size header describing the +file attributes associated with the member. +.Ss "Archive Headers" +An archive header describes the file attributes for the archive member that +follows it. +The +.Xr ar 5 +format only supports a limited number of attributes: the file name, +the file creation time stamp, the uid and gid of the creator, the file +mode and the file size. +.Pp +Archive headers are placed at an even byte offset in the archive file. +If the data for an archive member ends at an odd byte offset, then a +padding byte with value 0x0A is used to position the next archive +header on an even byte offset. +.Pp +An archive header comprises the following fixed sized fields: +.Bl -tag -width "Li ar_name" +.It Ar ar_name +(16 bytes) The file name of the archive member. +The format of this field varies between the BSD and SVR4/GNU formats and +is described in more detail in the section +.Sx "Representing File Names" +below. +.It Ar ar_date +(12 bytes) The file modification time for the member in seconds since the +epoch, encoded as a decimal number. +.It Ar ar_uid +(6 bytes) The uid associated with the archive member, encoded as a +decimal number. +.It Ar ar_gid +(6 bytes) The gid associated with the archive member, encoded as a +decimal number. +.It Ar ar_mode +(8 bytes) The file mode for the archive member, encoded as an octal +number. +.It Ar ar_size +(10 bytes) In the SVR4/GNU archive format this field holds the size in +bytes of the archive member, encoded as a decimal number. +In the BSD archive format, for short file names, this field +holds the size in bytes of the archive member, encoded as a decimal +number. +For long file names +.Po +see +.Sx "Representing File Names" +below +.Pc , +the field contains the combined size of the +archive member and its file name, encoded as a decimal number. +.It Ar ar_fmag +(2 bytes) This field holds 2 bytes with values 0x96 and 0x0A +respectively, marking the end of the header. +.El +.Pp +Unused bytes in the fields of an archive header are set to the value +0x20. +.Ss "Representing File Names" +The BSD and SVR4/GNU variants use different schemes for encoding file +names for members. +.Bl -tag -width "SVR4/GNU" +.It "BSD" +File names that are up to 16 bytes long and which do not contain +embedded spaces are stored directly in the +.Ar ar_name +field of the archive header. +File names that are either longer than 16 bytes or which contain +embedded spaces are stored immediately after the archive header +and the +.Ar ar_name +field of the archive header is set to the string +.Dq "#1/" +followed by a decimal representation of the number of bytes needed for +the file name. +In addition, the +.Ar ar_size +field of the archive header is set to the decimal representation of +the combined sizes of the archive member and the file name. +The file contents of the member follows the file name without further +padding. +.Pp +As an example, if the file name for a member was +.Dq "A B" +and its contents was the string +.Dq "C D" , +then the +.Ar ar_name +field of the header would contain +.Dq Li "#1/3" , +the +.Ar ar_size +field of the header would contain +.Dq Li 6 , +and the bytes immediately following the header would be 0x41, 0x20, +0x42, 0x43, 0x20 and 0x44 +.Po +ASCII +.Dq "A BC D" +.Pc . +.It "SVR4/GNU" +File names that are up to 15 characters long are stored directly in the +.Ar ar_name +field of the header, terminated by a +.Dq Li / +character. +.Pp +If the file name is larger than would fit in space for the +.Ar ar_name +field, then the actual file name is kept in the archive +string table +.Po +see +.Sx "Archive String Tables" +below +.Pc , +and the decimal offset of the file name in the string table is stored +in the +.Ar ar_name +field, prefixed by a +.Dq Li / +character. +.Pp +As an example, if the real file name has been stored at offset 768 in +the archive string table, the +.Ar ar_name +field of the header will contain the string +.Dq /768 . +.El +.Ss "Special Archive Members" +The following archive members are special. +.Bl -tag -width indent +.It Dq Li / +In the SVR4/GNU variant of the archive format, the archive member with +name +.Dq Li / +denotes an archive symbol table. +If present, this member will be the very first member in the +archive. +.It Dq Li // +In the SVR4/GNU variant of the archive format, the archive member with +name +.Dq Li // +denotes the archive string table. +This special member is used to hold filenames that do not fit in the +file name field of the header +.Po +see +.Sx "Representing File Names" +above +.Pc . +If present, this member immediately follows the archive symbol table +if an archive symbol table is present, or is the first member otherwise. +.It Dq Li "__.SYMDEF" +This special member contains the archive symbol table in the BSD +variant of the archive format. +If present, this member will be the very first member in the +archive. +.El +.Ss "Archive String Tables" +An archive string table is used in the SVR4/GNU archive format to hold +file names that are too large to fit into the constraints of the +.Ar ar_name +field of the archive header. +An archive string table contains a sequence of file names. +Each file name in the archive string table is terminated by the +byte sequence 0x2F, 0x0A +.Po +the ASCII string +.Dq "/\en" +.Pc . +No padding is used to separate adjacent file names. +.Ss "Archive Symbol Tables" +Archive symbol tables are used to speed up link editing by providing a +mapping between the program symbols defined in the archive +and the corresponding archive members. +Archive symbol tables are managed by the +.Xr ranlib 1 +utility. +.Pp +The format of archive symbol tables is as follows: +.Bl -tag -width "SVR4/GNU" +.It BSD +In the BSD archive format, the archive symbol table comprises +of two parts: a part containing an array of +.Vt "struct ranlib" +descriptors, followed by a part containing a symbol string table. +The sizes and layout of the structures that make up a BSD format +archive symbol table are machine dependent. +.Pp +The part containing +.Vt "struct ranlib" +descriptors begins with a field containing the size in bytes of the +array of +.Vt "struct ranlib" +descriptors encoded as a C +.Vt long +value. +.Pp +The array of +.Vt "struct ranlib" +descriptors follows the size field. +Each +.Vt "struct ranlib" +descriptor describes one symbol. +.Pp +A +.Vt "struct ranlib" +descriptor comprises two fields: +.Bl -tag -width "Ar ran_strx" -compact +.It Ar ran_strx +.Pq C Vt long +This field contains the zero-based offset of the symbol name in the +symbol string table. +.It Ar ran_off +.Pq C Vt long +This field is the file offset to the archive header for the archive +member defining the symbol. +.El +.Pp +The part containing the symbol string table begins with a field +containing the size in bytes of the string table, encoded as a C +.Vt long +value. +This string table follows the size field, and contains +NUL-terminated strings for the symbols in the symbol table. +.It SVR4/GNU +In the SVR4/GNU archive format, the archive symbol table starts with a +4-byte binary value containing the number of entries contained in the +archive symbol table. +This count of entries is stored most significant byte first. +.Pp +Next, there are +.Ar count +4-byte numbers, each stored most significant byte first. +Each number is a binary offset to the archive header for the member in +the archive file for the corresponding symbol table entry. +.Pp +After the binary offset values, there are +.Ar count +NUL-terminated strings in sequence, holding the symbol names for +the corresponding symbol table entries. +.El +.Sh STANDARDS COMPLIANCE +The +.Xr ar 1 +archive format is not currently specified by a standard. +.Pp +This manual page documents the +.Xr ar 1 +archive formats used by the +.Bx 4.4 +and +.Ux SVR4 +operating system releases. +.Sh SEE ALSO +.Xr ar 1 , +.Xr ld 1 , +.Xr ranlib 1 , +.Xr elf 3 , +.Xr elf_getarsym 3 , +.Xr elf_rand 3 diff --git a/contrib/elftoolchain/ar/ar.c b/contrib/elftoolchain/ar/ar.c new file mode 100644 index 000000000000..ceecbd93920b --- /dev/null +++ b/contrib/elftoolchain/ar/ar.c @@ -0,0 +1,433 @@ +/*- + * Copyright (c) 2007 Kai Wang + * Copyright (c) 2007 Tim Kientzle + * Copyright (c) 2007 Joseph Koshy + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer + * in this position and unchanged. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/*- + * Copyright (c) 1990, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Hugh Smith at The University of Guelph. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ar.h" + +ELFTC_VCSID("$Id: ar.c 3183 2015-04-10 16:18:42Z emaste $"); + +enum options +{ + OPTION_HELP +}; + +static struct option longopts[] = +{ + {"flavor", required_argument, NULL, 'F'}, + {"help", no_argument, NULL, OPTION_HELP}, + {"version", no_argument, NULL, 'V'}, + {NULL, 0, NULL, 0} +}; + +static void bsdar_usage(void); +static void ranlib_usage(void); +static void set_mode(struct bsdar *bsdar, char opt); +static void only_mode(struct bsdar *bsdar, const char *opt, + const char *valid_modes); +static void bsdar_version(void); + +int +main(int argc, char **argv) +{ + struct bsdar *bsdar, bsdar_storage; + char *arcmd, *argv1_saved; + size_t len; + int i, opt; + + bsdar = &bsdar_storage; + memset(bsdar, 0, sizeof(*bsdar)); + + arcmd = argv1_saved = NULL; + bsdar->output = stdout; + + if ((bsdar->progname = ELFTC_GETPROGNAME()) == NULL) + bsdar->progname = "ar"; + + if (elf_version(EV_CURRENT) == EV_NONE) + bsdar_errc(bsdar, 0, "ELF library initialization failed: %s", + elf_errmsg(-1)); + + /* + * Act like ranlib if our name ends in "ranlib"; this + * accommodates names like "arm-freebsd7.1-ranlib", + * "bsdranlib", etc. + */ + len = strlen(bsdar->progname); + if (len >= strlen("ranlib") && + strcmp(bsdar->progname + len - strlen("ranlib"), "ranlib") == 0) { + while ((opt = getopt_long(argc, argv, "tDV", longopts, + NULL)) != -1) { + switch(opt) { + case 't': + /* Ignored. */ + break; + case 'D': + bsdar->options |= AR_D; + break; + case 'V': + bsdar_version(); + break; + case OPTION_HELP: + ranlib_usage(); + default: + ranlib_usage(); + } + } + argv += optind; + argc -= optind; + + if (*argv == NULL) + ranlib_usage(); + + bsdar->options |= AR_S; + for (;(bsdar->filename = *argv++) != NULL;) + ar_write_archive(bsdar, 's'); + + exit(EXIT_SUCCESS); + } else { + if (argc < 2) + bsdar_usage(); + + /* + * Tack on a leading '-', for old-style usage. + */ + if (*argv[1] != '-') { + argv1_saved = argv[1]; + len = strlen(argv[1]) + 2; + if ((arcmd = malloc(len)) == NULL) + bsdar_errc(bsdar, errno, "malloc failed"); + (void) snprintf(arcmd, len, "-%s", argv[1]); + argv[1] = arcmd; + } + } + + while ((opt = getopt_long(argc, argv, "abCcdDfF:ijlMmopqrSsTtuVvxz", + longopts, NULL)) != -1) { + switch(opt) { + case 'a': + bsdar->options |= AR_A; + break; + case 'b': + case 'i': + bsdar->options |= AR_B; + break; + case 'C': + bsdar->options |= AR_CC; + break; + case 'c': + bsdar->options |= AR_C; + break; + case 'd': + set_mode(bsdar, opt); + break; + case 'D': + bsdar->options |= AR_D; + break; + case 'F': + if (!strcasecmp(optarg, "svr4") || + !strcasecmp(optarg, "gnu")) + bsdar->options &= ~AR_BSD; + else if (!strcasecmp(optarg, "bsd")) + bsdar->options |= AR_BSD; + else + bsdar_usage(); + break; + case 'f': + case 'T': + bsdar->options |= AR_TR; + break; + case 'j': + /* ignored */ + break; + case 'l': + /* ignored, for GNU ar comptibility */ + break; + case 'M': + set_mode(bsdar, opt); + break; + case 'm': + set_mode(bsdar, opt); + break; + case 'o': + bsdar->options |= AR_O; + break; + case 'p': + set_mode(bsdar, opt); + break; + case 'q': + set_mode(bsdar, opt); + break; + case 'r': + set_mode(bsdar, opt); + break; + case 'S': + bsdar->options |= AR_SS; + break; + case 's': + bsdar->options |= AR_S; + break; + case 't': + set_mode(bsdar, opt); + break; + case 'u': + bsdar->options |= AR_U; + break; + case 'V': + bsdar_version(); + break; + case 'v': + bsdar->options |= AR_V; + break; + case 'x': + set_mode(bsdar, opt); + break; + case 'z': + /* ignored */ + break; + case OPTION_HELP: + bsdar_usage(); + default: + bsdar_usage(); + } + } + + /* Restore argv[1] if we had modified it. */ + if (arcmd != NULL) { + argv[1] = argv1_saved; + free(arcmd); + arcmd = argv1_saved = NULL; + } + + argv += optind; + argc -= optind; + + if (*argv == NULL && bsdar->mode != 'M') + bsdar_usage(); + + if (bsdar->options & AR_A && bsdar->options & AR_B) + bsdar_errc(bsdar, 0, + "only one of -a and -[bi] options allowed"); + + if (bsdar->options & AR_J && bsdar->options & AR_Z) + bsdar_errc(bsdar, 0, + "only one of -j and -z options allowed"); + + if (bsdar->options & AR_S && bsdar->options & AR_SS) + bsdar_errc(bsdar, 0, + "only one of -s and -S options allowed"); + + if (bsdar->options & (AR_A | AR_B)) { + if (*argv == NULL) + bsdar_errc(bsdar, 0, + "no position operand specified"); + if ((bsdar->posarg = basename(*argv)) == NULL) + bsdar_errc(bsdar, errno, + "basename failed"); + argc--; + argv++; + } + + if (bsdar->options & AR_A) + only_mode(bsdar, "-a", "mqr"); + if (bsdar->options & AR_B) + only_mode(bsdar, "-b", "mqr"); + if (bsdar->options & AR_C) + only_mode(bsdar, "-c", "qr"); + if (bsdar->options & AR_CC) + only_mode(bsdar, "-C", "x"); + if (bsdar->options & AR_D) + only_mode(bsdar, "-D", "qr"); + if (bsdar->options & AR_O) + only_mode(bsdar, "-o", "x"); + if (bsdar->options & AR_SS) + only_mode(bsdar, "-S", "mqr"); + if (bsdar->options & AR_U) + only_mode(bsdar, "-u", "qrx"); + + if (bsdar->mode == 'M') { + ar_mode_script(bsdar); + exit(EXIT_SUCCESS); + } + + if ((bsdar->filename = *argv) == NULL) + bsdar_usage(); + + bsdar->argc = --argc; + bsdar->argv = ++argv; + + if ((!bsdar->mode || strchr("ptx", bsdar->mode)) && + bsdar->options & AR_S) { + ar_write_archive(bsdar, 's'); + if (!bsdar->mode) + exit(EXIT_SUCCESS); + } + + switch(bsdar->mode) { + case 'd': case 'm': case 'q': case 'r': + ar_write_archive(bsdar, bsdar->mode); + break; + + case 'p': case 't': case 'x': + ar_read_archive(bsdar, bsdar->mode); + break; + default: + bsdar_usage(); + /* NOTREACHED */ + } + + for (i = 0; i < bsdar->argc; i++) + if (bsdar->argv[i] != NULL) + bsdar_warnc(bsdar, 0, "%s: not found in archive", + bsdar->argv[i]); + + exit(EXIT_SUCCESS); +} + +static void +set_mode(struct bsdar *bsdar, char opt) +{ + + if (bsdar->mode != '\0' && bsdar->mode != opt) + bsdar_errc(bsdar, 0, "Can't specify both -%c and -%c", + opt, bsdar->mode); + bsdar->mode = opt; +} + +static void +only_mode(struct bsdar *bsdar, const char *opt, const char *valid_modes) +{ + + if (strchr(valid_modes, bsdar->mode) == NULL) + bsdar_errc(bsdar, 0, "Option %s is not permitted in mode -%c", + opt, bsdar->mode); +} + +#define AR_USAGE_MESSAGE "\ +Usage: %s [options] archive file...\n\ + Manage archives.\n\n\ + Where is one of:\n\ + -d Delete members from the archive.\n\ + -m Move archive members within the archive.\n\ + -p Write the contents of members to standard output.\n\ + -q Append files to an archive.\n\ + -r Replace (add) files to an archive.\n\ + -s Add an archive symbol to an archive.\n\ + -t List files in an archive.\n\ + -x Extract members from an archive.\n\ + -M Execute MRI librarian commands.\n\ + -V Print a version identifier and exit.\n\n\ + Options:\n\ + -a MEMBER Add members after the specified member.\n\ + -b MEMBER | -i MEMBER\n\ + Add members before the specified member.\n\ + -c Do not print a message when creating a new archive.\n\ + -f | -T Only use the first fifteen characters of the member name.\n\ + -j (This option is accepted, but is ignored).\n\ + -l (This option is accepted, but is ignored).\n\ + -o Preserve modification times when extracting members.\n\ + -u Conditionally update or extract members.\n\ + -v Be verbose.\n\ + -z (This option is accepted, but is ignored).\n\ + -C Do not overwrite existing files in the file system.\n\ + -D Use fixed metadata, for consistent archive checksums.\n\ + -F FORMAT | --flavor=FORMAT\n\ + Create archives with the specified format.\n\ + -S Do not generate an archive symbol table.\n" + +static void +bsdar_usage(void) +{ + (void) fprintf(stderr, AR_USAGE_MESSAGE, ELFTC_GETPROGNAME()); + exit(EXIT_FAILURE); +} + +#define RANLIB_USAGE_MESSAGE "\ +Usage: %s [options] archive...\n\ + Update or create archive symbol tables.\n\n\ + Options:\n\ + -t (This option is accepted, but ignored).\n\ + -D Use fixed metadata, for consistent archive checksums.\n\ + -V Print a version identifier and exit.\n" + +static void +ranlib_usage(void) +{ + (void)fprintf(stderr, RANLIB_USAGE_MESSAGE, ELFTC_GETPROGNAME()); + exit(EXIT_FAILURE); +} + +static void +bsdar_version(void) +{ + (void)printf("%s (%s, %s)\n", ELFTC_GETPROGNAME(), archive_version_string(), + elftc_version()); + exit(EXIT_SUCCESS); +} diff --git a/contrib/elftoolchain/ar/ar.h b/contrib/elftoolchain/ar/ar.h new file mode 100644 index 000000000000..a75b9a9eb856 --- /dev/null +++ b/contrib/elftoolchain/ar/ar.h @@ -0,0 +1,143 @@ +/*- + * Copyright (c) 2007 Kai Wang + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer + * in this position and unchanged. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $Id: ar.h 2496 2012-04-24 02:33:40Z jkoshy $ + */ + +#include + +#include "_elftc.h" + +/* + * ar(1) options. + */ +#define AR_A 0x0001 /* position-after */ +#define AR_B 0x0002 /* position-before */ +#define AR_C 0x0004 /* creating new archive */ +#define AR_CC 0x0008 /* do not overwrite when extracting */ +#define AR_J 0x0010 /* bzip2 compression */ +#define AR_O 0x0020 /* preserve original mtime when extracting */ +#define AR_S 0x0040 /* write archive symbol table */ +#define AR_SS 0x0080 /* do not write archive symbol table */ +#define AR_TR 0x0100 /* only keep first 15 chars for member name */ +#define AR_U 0x0200 /* only extract or update newer members.*/ +#define AR_V 0x0400 /* verbose mode */ +#define AR_Z 0x0800 /* gzip compression */ +#define AR_D 0x1000 /* insert dummy mode, mtime, uid and gid */ +#define AR_BSD 0x2000 /* use the BSD archive format */ + +#define DEF_BLKSZ 10240 /* default block size */ + +/* Special names. */ + +#define AR_STRINGTAB_NAME_SVR4 "//" +#define AR_SYMTAB_NAME_BSD "__.SYMDEF" +#define AR_SYMTAB_NAME_SVR4 "/" + +/* + * Convenient wrapper for general libarchive error handling. + */ +#define AC(CALL) do { \ + if ((CALL)) \ + bsdar_errc(bsdar, 0, "%s", \ + archive_error_string(a)); \ +} while (0) + +/* + * The 'ACV' wrapper is used for libarchive APIs that changed from + * returning 'void' to returning an 'int' in later versions of libarchive. + */ +#if ARCHIVE_VERSION_NUMBER >= 2000000 +#define ACV(CALL) AC(CALL) +#else +#define ACV(CALL) do { \ + (CALL); \ + } while (0) +#endif + +/* + * In-memory representation of archive member(object). + */ +struct ar_obj { + Elf *elf; /* object file descriptor */ + char *name; /* member name */ + uid_t uid; /* user id */ + gid_t gid; /* group id */ + mode_t md; /* octal file permissions */ + size_t size; /* member size */ + time_t mtime; /* modification time */ + dev_t dev; /* inode's device */ + ino_t ino; /* inode's number */ + + TAILQ_ENTRY(ar_obj) objs; +}; + +/* + * Structure encapsulates the "global" data for "ar" program. + */ +struct bsdar { + const char *filename; /* archive name. */ + const char *addlib; /* target of ADDLIB. */ + const char *posarg; /* position arg for modifiers -a, -b. */ + char mode; /* program mode */ + int options; /* command line options */ + FILE *output; /* default output stream */ + + const char *progname; /* program name */ + int argc; + char **argv; + + dev_t ar_dev; /* archive device. */ + ino_t ar_ino; /* archive inode. */ + + /* + * Fields for the archive string table. + */ + char *as; /* buffer for archive string table. */ + size_t as_sz; /* current size of as table. */ + size_t as_cap; /* capacity of as table buffer. */ + + /* + * Fields for the archive symbol table. + */ + uint32_t s_cnt; /* current number of symbols. */ + uint32_t *s_so; /* symbol offset table. */ + size_t s_so_cap; /* capacity of so table buffer. */ + char *s_sn; /* symbol name table */ + size_t s_sn_cap; /* capacity of sn table buffer. */ + size_t s_sn_sz; /* current size of sn table. */ + /* Current member's offset (relative to the end of pseudo members.) */ + off_t rela_off; + + TAILQ_HEAD(, ar_obj) v_obj; /* object(member) list */ +}; + +void ar_mode_script(struct bsdar *ar); +void ar_read_archive(struct bsdar *_ar, int _mode); +void ar_write_archive(struct bsdar *_ar, int _mode); +void bsdar_errc(struct bsdar *, int _code, const char *fmt, ...); +int bsdar_is_pseudomember(struct bsdar *_ar, const char *_name); +const char *bsdar_strmode(mode_t m); +void bsdar_warnc(struct bsdar *, int _code, const char *fmt, ...); diff --git a/contrib/elftoolchain/ar/benchmark/acp.sh b/contrib/elftoolchain/ar/benchmark/acp.sh new file mode 100755 index 000000000000..11be68bf401e --- /dev/null +++ b/contrib/elftoolchain/ar/benchmark/acp.sh @@ -0,0 +1,65 @@ +#!/bin/sh +# $Id: acp.sh 2086 2011-10-27 05:18:01Z jkoshy $ + +# This script is adapted from Jan Psota's Tar Comparison Program(TCP). + +n=3 # number of repetitions +AR="bsdar gnuar" # ar archivers to compare + +test $# -ge 2 || { + echo "usage: $0 source_dir where_to_place_archive [where_to_extract_it]" + exit 0 +} + +THISDIR=`/bin/pwd` +src=$1 +dst=$2/acp.a +ext=${3:-$2}/acptmp +test -e $dst -o -e /tmp/acp \ + && { echo "$dst or /tmp/acp exists, exiting"; exit 1; } +mkdir -p $ext || exit 1 + +show_result () +{ + awk -vL="`du -k $dst`" '{printf "%s\t%s\t%s\%10.1d KB/s\n", +$1, $3, $5, ($1>0?L/$1:0)}' /tmp/acp | sort | head -n 1 +} + +test -d $src || { echo "'$src' is not a directory"; exit 1; } + +# ar versions +for ar in $AR; do echo -n "$ar: "; $ar -V | head -n 1; +done + +echo +echo "best time of $n repetitions" +echo -n " src=$src, " +echo -n "`du -sh $src | awk '{print $1}'`" +echo -n " in " +echo "`find $src | wc -l` files" +echo " archive=$dst, extract to $ext" + +echo "program operation real user system speed" +for op in "cru $dst $src/*" "t $dst" "x `basename $dst`"; do + for ar in $AR; do + echo -n "$ar " + echo $op | grep -q ^cr && echo -n "create " + echo $op | grep -q ^t && echo -n "list " + echo $op | grep -q ^x && echo -n "extract " + num=0 + while [ $num -lt $n ]; do + echo $op | grep -q ^cr && rm -f $dst + echo $op | grep -q ^x && { rm -rf $ext; mkdir -p $ext + cp $dst $ext; cd $ext; } + sync + time $ar $op > /dev/null 2>> /tmp/acp + echo $op | grep -q ^x && cd $THISDIR + num=`expr $num + 1` + done + show_result + rm -rf /tmp/acp + done + echo +done +rm -rf $ext $dst +rm -f /tmp/acp diff --git a/contrib/elftoolchain/ar/os.Linux.mk b/contrib/elftoolchain/ar/os.Linux.mk new file mode 100644 index 000000000000..daed864eee9e --- /dev/null +++ b/contrib/elftoolchain/ar/os.Linux.mk @@ -0,0 +1,9 @@ +.if ${OS_DISTRIBUTION} == "Ubuntu" +.if ${OS_DISTRIBUTION_VERSION} >= 14 +# Ubuntu Trusty Tahr and later. + +# Use the --nounput option to flex(1), to prevent unused functions from +# being generated. +LFLAGS += --nounput +.endif +.endif diff --git a/contrib/elftoolchain/ar/ranlib.1 b/contrib/elftoolchain/ar/ranlib.1 new file mode 100644 index 000000000000..89ab4ab49cab --- /dev/null +++ b/contrib/elftoolchain/ar/ranlib.1 @@ -0,0 +1,86 @@ +.\" Copyright (c) 2007,2009-2012 Joseph Koshy. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" This software is provided by Joseph Koshy ``as is'' and +.\" any express or implied warranties, including, but not limited to, the +.\" implied warranties of merchantability and fitness for a particular purpose +.\" are disclaimed. in no event shall Joseph Koshy be liable +.\" for any direct, indirect, incidental, special, exemplary, or consequential +.\" damages (including, but not limited to, procurement of substitute goods +.\" or services; loss of use, data, or profits; or business interruption) +.\" however caused and on any theory of liability, whether in contract, strict +.\" liability, or tort (including negligence or otherwise) arising in any way +.\" out of the use of this software, even if advised of the possibility of +.\" such damage. +.\" +.\" $Id: ranlib.1 3195 2015-05-12 17:22:19Z emaste $ +.\" +.Dd December 9, 2012 +.Os +.Dt RANLIB 1 +.Sh NAME +.Nm ranlib +.Nd update archive symbol tables +.Sh SYNOPSIS +.Nm +.Op Fl D +.Op Fl t +.Ar archive Ns ... +.Nm +.Fl V +.Sh DESCRIPTION +The +.Nm ranlib +utility is used to update an existing archive symbol table in an +.Xr ar 1 +archive, or to add an archive symbol table to an archive lacking one. +.Sh OPTIONS +The +.Nm +utility supports the following options: +.Bl -tag -width indent +.It Fl D +Use zeros for the mtime, uid and gid fields, and use mode 0644 for the +file mode field for all archive member headers. +This ensures that checksums on the resulting archives are reproducible +when member contents are identical. +.It Fl t +This option is accepted, but is ignored. +.It Fl V +Print a version identifier and exit. +.El +.Sh EXAMPLES +To update the archive symbol table for an archive +.Pa lib.a , +use: +.Dl "ranlib lib.a" +.Sh DIAGNOSTICS +.Ex -std +.Sh SEE ALSO +.Xr ar 1 , +.Xr ld 1 , +.Xr archive 3 , +.Xr elf 3 , +.Xr ar 5 +.Sh HISTORY +The +.Nm +command first appeared in AT&T UNIX Version 7. +.Pp +In +.Fx 8.0 , +.An Kai Wang Aq Mt kaiw@FreeBSD.org +reimplemented +.Nm +using the +.Lb libarchive +and the +.Lb libelf . diff --git a/contrib/elftoolchain/ar/read.c b/contrib/elftoolchain/ar/read.c new file mode 100644 index 000000000000..08b3224db17e --- /dev/null +++ b/contrib/elftoolchain/ar/read.c @@ -0,0 +1,199 @@ +/*- + * Copyright (c) 2007 Kai Wang + * Copyright (c) 2007 Tim Kientzle + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer + * in this position and unchanged. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "ar.h" + +ELFTC_VCSID("$Id: read.c 3180 2015-04-09 15:13:57Z emaste $"); + +/* + * Handle read modes: 'x', 't' and 'p'. + */ +void +ar_read_archive(struct bsdar *bsdar, int mode) +{ + FILE *out; + struct archive *a; + struct archive_entry *entry; + struct stat sb; + struct tm *tp; + const char *bname; + const char *name; + mode_t md; + size_t size; + time_t mtime; + uid_t uid; + gid_t gid; + char **av; + char buf[25]; + char find; + int i, flags, r; + + assert(mode == 'p' || mode == 't' || mode == 'x'); + + if ((a = archive_read_new()) == NULL) + bsdar_errc(bsdar, 0, "archive_read_new failed"); + archive_read_support_format_ar(a); + AC(archive_read_open_filename(a, bsdar->filename, DEF_BLKSZ)); + + out = bsdar->output; + + for (;;) { + r = archive_read_next_header(a, &entry); + if (r == ARCHIVE_WARN || r == ARCHIVE_RETRY || + r == ARCHIVE_FATAL) + bsdar_warnc(bsdar, 0, "%s", archive_error_string(a)); + if (r == ARCHIVE_EOF || r == ARCHIVE_FATAL) + break; + if (r == ARCHIVE_RETRY) { + bsdar_warnc(bsdar, 0, "Retrying..."); + continue; + } + + if (archive_format(a) == ARCHIVE_FORMAT_AR_BSD) + bsdar->options |= AR_BSD; + else + bsdar->options &= ~AR_BSD; + + if ((name = archive_entry_pathname(entry)) == NULL) + break; + + /* Skip pseudo members. */ + if (bsdar_is_pseudomember(bsdar, name)) + continue; + + if (bsdar->argc > 0) { + find = 0; + for(i = 0; i < bsdar->argc; i++) { + av = &bsdar->argv[i]; + if (*av == NULL) + continue; + if ((bname = basename(*av)) == NULL) + bsdar_errc(bsdar, errno, + "basename failed"); + if (strcmp(bname, name) != 0) + continue; + + *av = NULL; + find = 1; + break; + } + if (!find) + continue; + } + + if (mode == 't') { + if (bsdar->options & AR_V) { + md = archive_entry_mode(entry); + uid = archive_entry_uid(entry); + gid = archive_entry_gid(entry); + size = archive_entry_size(entry); + mtime = archive_entry_mtime(entry); + (void)fprintf(out, "%s %6d/%-6d %8ju ", + bsdar_strmode(md) + 1, uid, gid, + (uintmax_t)size); + tp = localtime(&mtime); + (void)strftime(buf, sizeof(buf), + "%b %e %H:%M %Y", tp); + (void)fprintf(out, "%s %s", buf, name); + } else + (void)fprintf(out, "%s", name); + r = archive_read_data_skip(a); + if (r == ARCHIVE_WARN || r == ARCHIVE_RETRY || + r == ARCHIVE_FATAL) { + (void)fprintf(out, "\n"); + bsdar_warnc(bsdar, 0, "%s", + archive_error_string(a)); + } + + if (r == ARCHIVE_FATAL) + break; + + (void)fprintf(out, "\n"); + } else { + /* mode == 'x' || mode = 'p' */ + if (mode == 'p') { + if (bsdar->options & AR_V) { + (void)fprintf(out, "\n<%s>\n\n", + name); + fflush(out); + } + r = archive_read_data_into_fd(a, fileno(out)); + } else { + /* mode == 'x' */ + if (stat(name, &sb) != 0) { + if (errno != ENOENT) { + bsdar_warnc(bsdar, 0, + "stat %s failed", + bsdar->filename); + continue; + } + } else { + /* stat success, file exist */ + if (bsdar->options & AR_CC) + continue; + if (bsdar->options & AR_U && + archive_entry_mtime(entry) <= + sb.st_mtime) + continue; + } + + if (bsdar->options & AR_V) + (void)fprintf(out, "x - %s\n", name); + /* Disallow absolute paths. */ + if (name[0] == '/') { + bsdar_warnc(bsdar, 0, + "Absolute path '%s'", name); + continue; + } + /* Basic path security flags. */ + flags = ARCHIVE_EXTRACT_SECURE_SYMLINKS | + ARCHIVE_EXTRACT_SECURE_NODOTDOT; + if (bsdar->options & AR_O) + flags |= ARCHIVE_EXTRACT_TIME; + + r = archive_read_extract(a, entry, flags); + } + + if (r) + bsdar_warnc(bsdar, 0, "%s", + archive_error_string(a)); + } + } + AC(archive_read_close(a)); + ACV(archive_read_free(a)); +} diff --git a/contrib/elftoolchain/ar/util.c b/contrib/elftoolchain/ar/util.c new file mode 100644 index 000000000000..f22542e67db2 --- /dev/null +++ b/contrib/elftoolchain/ar/util.c @@ -0,0 +1,184 @@ +/*- + * Copyright (c) 2003-2007 Tim Kientzle + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer + * in this position and unchanged. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ar.h" + +ELFTC_VCSID("$Id: util.c 3174 2015-03-27 17:13:41Z emaste $"); + +static void bsdar_vwarnc(struct bsdar *, int code, + const char *fmt, va_list ap); +static void bsdar_verrc(struct bsdar *bsdar, int code, + const char *fmt, va_list ap); + +static void +bsdar_vwarnc(struct bsdar *bsdar, int code, const char *fmt, va_list ap) +{ + + fprintf(stderr, "%s: warning: ", bsdar->progname); + vfprintf(stderr, fmt, ap); + if (code != 0) + fprintf(stderr, ": %s", strerror(code)); + fprintf(stderr, "\n"); +} + +void +bsdar_warnc(struct bsdar *bsdar, int code, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + bsdar_vwarnc(bsdar, code, fmt, ap); + va_end(ap); +} + +static void +bsdar_verrc(struct bsdar *bsdar, int code, const char *fmt, va_list ap) +{ + + fprintf(stderr, "%s: fatal: ", bsdar->progname); + vfprintf(stderr, fmt, ap); + if (code != 0) + fprintf(stderr, ": %s", strerror(code)); + fprintf(stderr, "\n"); +} + +void +bsdar_errc(struct bsdar *bsdar, int code, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + bsdar_verrc(bsdar, code, fmt, ap); + va_end(ap); + exit(EXIT_FAILURE); +} + +#define AR_STRMODE_SIZE 12 +const char * +bsdar_strmode(mode_t m) +{ + static char buf[AR_STRMODE_SIZE]; + +#if ELFTC_HAVE_STRMODE + /* Use the system's strmode(3). */ + strmode(m, buf); + return buf; + +#else + char c; + + /* + * The first character of the string denotes the type of the + * entry. + */ + if (S_ISBLK(m)) + c = 'b'; + else if (S_ISCHR(m)) + c = 'c'; + else if (S_ISDIR(m)) + c = 'd'; +#if defined(S_ISFIFO) + else if (S_ISFIFO(m)) + c = 'p'; +#endif +#if defined(S_ISLNK) + else if (S_ISLNK(m)) + c = 'l'; +#endif + else if (S_ISREG(m)) + c = '-'; +#if defined(S_ISSOCK) + else if (S_ISSOCK(m)) + c = 's'; +#endif + else + c = '?'; + buf[0] = c; + + /* The next 3 characters show permissions for the owner. */ + buf[1] = (m & S_IRUSR) ? 'r' : '-'; + buf[2] = m & S_IWUSR ? 'w' : '-'; + if (m & S_ISUID) + c = (m & S_IXUSR) ? 's' : 'S'; + else + c = (m & S_IXUSR) ? 'x' : '-'; + buf[3] = c; + + /* The next 3 characters describe permissions for the group. */ + buf[4] = (m & S_IRGRP) ? 'r' : '-'; + buf[5] = m & S_IWGRP ? 'w' : '-'; + if (m & S_ISGID) + c = (m & S_IXGRP) ? 's' : 'S'; + else + c = (m & S_IXGRP) ? 'x' : '-'; + buf[6] = c; + + + /* The next 3 characters describe permissions for others. */ + buf[7] = (m & S_IROTH) ? 'r' : '-'; + buf[8] = m & S_IWOTH ? 'w' : '-'; + if (m & S_ISVTX) /* sticky bit */ + c = (m & S_IXOTH) ? 't' : 'T'; + else + c = (m & S_IXOTH) ? 'x' : '-'; + buf[9] = c; + + /* End the string with a blank and NUL-termination. */ + buf[10] = ' '; + buf[11] = '\0'; + + return buf; +#endif /* !ELTC_HAVE_STRMODE */ +} + +int +bsdar_is_pseudomember(struct bsdar *bsdar, const char *name) +{ + /* + * The "__.SYMDEF" member is special in the BSD format + * variant. + */ + if (bsdar->options & AR_BSD) + return (strcmp(name, AR_SYMTAB_NAME_BSD) == 0); + else + /* + * The names "/ " and "// " are special in the SVR4 + * variant. + */ + return (strcmp(name, AR_STRINGTAB_NAME_SVR4) == 0 || + strcmp(name, AR_SYMTAB_NAME_SVR4) == 0); +} diff --git a/contrib/elftoolchain/ar/write.c b/contrib/elftoolchain/ar/write.c new file mode 100644 index 000000000000..90be5da11937 --- /dev/null +++ b/contrib/elftoolchain/ar/write.c @@ -0,0 +1,975 @@ +/*- + * Copyright (c) 2007 Kai Wang + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer + * in this position and unchanged. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ar.h" + +ELFTC_VCSID("$Id: write.c 3183 2015-04-10 16:18:42Z emaste $"); + +#define _ARMAG_LEN 8 /* length of the magic string */ +#define _ARHDR_LEN 60 /* length of the archive header */ +#define _INIT_AS_CAP 128 /* initial archive string table size */ +#define _INIT_SYMOFF_CAP (256*(sizeof(uint32_t))) /* initial so table size */ +#define _INIT_SYMNAME_CAP 1024 /* initial sn table size */ +#define _MAXNAMELEN_SVR4 15 /* max member name length in svr4 variant */ +#define _MAXNAMELEN_BSD 16 /* max member name length in bsd variant */ +#define _TRUNCATE_LEN 15 /* number of bytes to keep for member name */ + +static void add_to_ar_str_table(struct bsdar *bsdar, const char *name); +static void add_to_ar_sym_table(struct bsdar *bsdar, const char *name); +static struct ar_obj *create_obj_from_file(struct bsdar *bsdar, + const char *name, time_t mtime); +static void create_symtab_entry(struct bsdar *bsdar, Elf *e); +static void free_obj(struct ar_obj *obj); +static void insert_obj(struct bsdar *bsdar, struct ar_obj *obj, + struct ar_obj *pos); +static void read_objs(struct bsdar *bsdar, const char *archive, + int checkargv); +static void write_cleanup(struct bsdar *bsdar); +static void write_data(struct bsdar *bsdar, struct archive *a, + const void *buf, size_t s); +static void write_objs(struct bsdar *bsdar); + +/* + * Create an object from a file, and return the created object + * descriptor. Return NULL if either an error occurs, or if the '-u' + * option was specified and the member is not newer than the existing + * one in the archive. + */ +static struct ar_obj * +create_obj_from_file(struct bsdar *bsdar, const char *name, time_t mtime) +{ + struct ar_obj *obj; + struct stat sb; + const char *bname; + char *tmpname; + int fd; + + if (name == NULL) + return (NULL); + + obj = malloc(sizeof(struct ar_obj)); + if (obj == NULL) + bsdar_errc(bsdar, errno, "malloc failed"); + + obj->elf = NULL; + + if ((fd = open(name, O_RDONLY, 0)) < 0) { + bsdar_warnc(bsdar, errno, "can't open file: %s", name); + free(obj); + return (NULL); + } + + tmpname = strdup(name); + if ((bname = basename(tmpname)) == NULL) + bsdar_errc(bsdar, errno, "basename failed"); + if (bsdar->options & AR_TR && strlen(bname) > _TRUNCATE_LEN) { + if ((obj->name = malloc(_TRUNCATE_LEN + 1)) == NULL) + bsdar_errc(bsdar, errno, "malloc failed"); + (void)strncpy(obj->name, bname, _TRUNCATE_LEN); + obj->name[_TRUNCATE_LEN] = '\0'; + } else + if ((obj->name = strdup(bname)) == NULL) + bsdar_errc(bsdar, errno, "strdup failed"); + free(tmpname); + + if (fstat(fd, &sb) < 0) { + bsdar_warnc(bsdar, errno, "can't fstat file: %s", obj->name); + goto giveup; + } + if (!S_ISREG(sb.st_mode)) { + bsdar_warnc(bsdar, 0, "%s is not an ordinary file", obj->name); + goto giveup; + } + + if (sb.st_dev == bsdar->ar_dev && sb.st_ino == bsdar->ar_ino) { + bsdar_warnc(bsdar, 0, "cannot add archive \"%s\" to itself", + obj->name); + goto giveup; + } + + /* + * If the '-u' option is specified and member is not newer + * than the existing one, we should not replace the member. + * However, if mtime == 0, i.e., if nonexistent members are to + * be forcibly replaced, then the '-u' option is to be ignored. + */ + if (mtime != 0 && bsdar->options & AR_U && sb.st_mtime <= mtime) + goto giveup; + + /* + * When the '-D' option is specified, the mtime and UID/GID of + * the member will be set to 0, and the file mode will be set + * to 644. This ensures that checksums will match for two + * archives containing identical content. + */ + if (bsdar->options & AR_D) { + obj->uid = 0; + obj->gid = 0; + obj->mtime = 0; + obj->md = S_IFREG | 0644; + } else { + obj->uid = sb.st_uid; + obj->gid = sb.st_gid; + obj->mtime = sb.st_mtime; + obj->md = sb.st_mode; + } + obj->size = sb.st_size; + obj->dev = sb.st_dev; + obj->ino = sb.st_ino; + + if (obj->size == 0) { + return (obj); + } + + if ((obj->elf = elf_open(fd)) == NULL) { + bsdar_warnc(bsdar, 0, "file initialization failed for %s: %s", + obj->name, elf_errmsg(-1)); + goto giveup; + } + + /* + * Read the object fully into memory and close its file + * descriptor. + */ + if (elf_cntl(obj->elf, ELF_C_FDREAD) < 0) { + bsdar_warnc(bsdar, 0, "%s could not be read in: %s", + obj->name, elf_errmsg(-1)); + goto giveup; + } + + if (close(fd) < 0) + bsdar_errc(bsdar, errno, "close failed: %s", + obj->name); + + return (obj); + +giveup: + if (obj->elf) + elf_end(obj->elf); + + if (close(fd) < 0) + bsdar_errc(bsdar, errno, "close failed: %s", + obj->name); + free(obj->name); + free(obj); + return (NULL); +} + +/* + * Free an object and its associated allocations. + */ +static void +free_obj(struct ar_obj *obj) +{ + if (obj->elf) + elf_end(obj->elf); + + free(obj->name); + free(obj); +} + +/* + * Insert an object into a list, either before/after the 'pos' obj or + * at the end of the list. + */ +static void +insert_obj(struct bsdar *bsdar, struct ar_obj *obj, struct ar_obj *pos) +{ + if (obj == NULL) + bsdar_errc(bsdar, 0, "try to insert a null obj"); + + if (pos == NULL || obj == pos) + /* + * If the object to move happens to be the position + * obj, or if there is no position obj, move the + * object to the end. + */ + goto tail; + + if (bsdar->options & AR_B) { + TAILQ_INSERT_BEFORE(pos, obj, objs); + return; + } + if (bsdar->options & AR_A) { + TAILQ_INSERT_AFTER(&bsdar->v_obj, pos, obj, objs); + return; + } + +tail: + TAILQ_INSERT_TAIL(&bsdar->v_obj, obj, objs); + +} + +/* + * Read objects from archive into the 'v_obj' list. Note that + * 'checkargv' is set when read_objs() is used to read objects from + * the target of 'ADDLIB' command in ar script mode; in this case the + * 'argv' array specifies the members that 'ADDLIB' is to operate on. + */ +static void +read_objs(struct bsdar *bsdar, const char *archive, int checkargv) +{ + struct archive *a; + struct archive_entry *entry; + struct ar_obj *obj; + const char *name; + const char *bname; + char *buff; + char **av; + size_t size; + int i, r, find; + + if ((a = archive_read_new()) == NULL) + bsdar_errc(bsdar, 0, "archive_read_new failed"); + archive_read_support_format_ar(a); + AC(archive_read_open_filename(a, archive, DEF_BLKSZ)); + for (;;) { + r = archive_read_next_header(a, &entry); + if (r == ARCHIVE_FATAL) + bsdar_errc(bsdar, 0, "%s", archive_error_string(a)); + if (r == ARCHIVE_EOF) + break; + if (r == ARCHIVE_WARN || r == ARCHIVE_RETRY) + bsdar_warnc(bsdar, 0, "%s", archive_error_string(a)); + if (r == ARCHIVE_RETRY) { + bsdar_warnc(bsdar, 0, "Retrying..."); + continue; + } + + name = archive_entry_pathname(entry); + + /* + * Skip pseudo members. + */ + if (bsdar_is_pseudomember(bsdar, name)) + continue; + + /* + * If 'checkargv' is set, only read those members + * specified in argv. + */ + if (checkargv && bsdar->argc > 0) { + find = 0; + for(i = 0; i < bsdar->argc; i++) { + av = &bsdar->argv[i]; + if (*av == NULL) + continue; + if ((bname = basename(*av)) == NULL) + bsdar_errc(bsdar, errno, + "basename failed"); + if (strcmp(bname, name) != 0) + continue; + + *av = NULL; + find = 1; + break; + } + if (!find) + continue; + } + + size = archive_entry_size(entry); + + if (size > 0) { + if ((buff = malloc(size)) == NULL) + bsdar_errc(bsdar, errno, "malloc failed"); + if (archive_read_data(a, buff, size) != (ssize_t)size) { + bsdar_warnc(bsdar, 0, "%s", + archive_error_string(a)); + free(buff); + continue; + } + } else + buff = NULL; + + obj = malloc(sizeof(struct ar_obj)); + if (obj == NULL) + bsdar_errc(bsdar, errno, "malloc failed"); + obj->elf = NULL; + if (buff) { + obj->elf = elf_openmemory(buff, size); + if (obj->elf == NULL) { + bsdar_warnc(bsdar, 0, "elf_openmemory() " + "failed for %s: %s", name, + elf_errmsg(-1)); + free(buff); + free(obj); + continue; + } + } + if ((obj->name = strdup(name)) == NULL) + bsdar_errc(bsdar, errno, "strdup failed"); + obj->size = size; + obj->uid = archive_entry_uid(entry); + obj->gid = archive_entry_gid(entry); + obj->md = archive_entry_mode(entry); + obj->mtime = archive_entry_mtime(entry); + obj->dev = 0; + obj->ino = 0; + + TAILQ_INSERT_TAIL(&bsdar->v_obj, obj, objs); + } + AC(archive_read_close(a)); + ACV(archive_read_free(a)); +} + +/* + * Write an archive. + */ +void +ar_write_archive(struct bsdar *bsdar, int mode) +{ + struct ar_obj *nobj, *obj, *obj_temp, *pos; + struct stat sb; + const char *bname; + char **av; + int i; + + TAILQ_INIT(&bsdar->v_obj); + nobj = NULL; + pos = NULL; + memset(&sb, 0, sizeof(sb)); + + assert(mode == 'A' || mode == 'd' || mode == 'm' || mode == 'q' || + mode == 'r' || mode == 's'); + + /* + * Test if the specified archive exists, to determine + * whether we are creating a new archive. + */ + if (stat(bsdar->filename, &sb) != 0) { + if (errno != ENOENT) { + bsdar_warnc(bsdar, 0, "stat %s failed", + bsdar->filename); + return; + } + + /* We do not create archive in mode 'd', 'm' and 's'. */ + if (mode != 'r' && mode != 'q') { + bsdar_warnc(bsdar, 0, "%s: no such file", + bsdar->filename); + return; + } + + /* Issue a message if the '-c' option was not specified. */ + if (!(bsdar->options & AR_C)) + bsdar_warnc(bsdar, 0, "creating %s", bsdar->filename); + goto new_archive; + } + + bsdar->ar_dev = sb.st_dev; + bsdar->ar_ino = sb.st_ino; + + /* + * First read members from the existing archive. + */ + read_objs(bsdar, bsdar->filename, 0); + + /* + * For mode 's', no member will be moved, deleted or replaced. + */ + if (mode == 's') + goto write_objs; + + /* + * For mode 'q', we don't need to adjust existing members either. + * Also, -a, -b and -i are ignored in this mode. New members are + * always inserted at tail. + */ + if (mode == 'q') + goto new_archive; + + /* + * Mode 'A' adds the contents of another archive to the tail + * of current archive. Note that mode 'A' is a special mode + * for the 'ADDLIB' command in ar's script mode. Currently + * there is no option that invokes this function from ar's + * command line. + */ + if (mode == 'A') { + /* + * Read objects from the target archive of the + * 'ADDLIB' command. If there are members specified in + * 'argv', read those members only, otherwise the + * entire archive will be read. + */ + read_objs(bsdar, bsdar->addlib, 1); + goto write_objs; + } + + /* + * Try to find the position member specified by user. + */ + if (bsdar->options & AR_A || bsdar->options & AR_B) { + TAILQ_FOREACH(obj, &bsdar->v_obj, objs) { + if (strcmp(obj->name, bsdar->posarg) == 0) { + pos = obj; + break; + } + } + + /* + * If we cannot find the position specified by the + * user, silently insert objects at the tail of the + * list. + */ + if (pos == NULL) + bsdar->options &= ~(AR_A | AR_B); + } + + for (i = 0; i < bsdar->argc; i++) { + av = &bsdar->argv[i]; + + TAILQ_FOREACH_SAFE(obj, &bsdar->v_obj, objs, obj_temp) { + if ((bname = basename(*av)) == NULL) + bsdar_errc(bsdar, errno, "basename failed"); + if (bsdar->options & AR_TR) { + if (strncmp(bname, obj->name, _TRUNCATE_LEN)) + continue; + } else + if (strcmp(bname, obj->name) != 0) + continue; + + if (mode == 'r') { + /* + * If the new member should not + * replace the old one, skip it. + */ + nobj = create_obj_from_file(bsdar, *av, + obj->mtime); + if (nobj == NULL) + goto skip_obj; + } + + if (bsdar->options & AR_V) + (void)fprintf(bsdar->output, "%c - %s\n", + mode, *av); + + TAILQ_REMOVE(&bsdar->v_obj, obj, objs); + if (mode == 'd' || mode == 'r') + free_obj(obj); + + if (mode == 'm') + insert_obj(bsdar, obj, pos); + if (mode == 'r') + insert_obj(bsdar, nobj, pos); + + skip_obj: + *av = NULL; + break; + } + + } + +new_archive: + /* + * When operating in mode 'r', directly add the specified + * objects which do not exist in current archive. When + * operating in mode 'q', all objects specified by the command + * line args are appended to the archive, without checking + * existing members in the archive. + */ + for (i = 0; i < bsdar->argc; i++) { + av = &bsdar->argv[i]; + if (*av != NULL && (mode == 'r' || mode == 'q')) { + nobj = create_obj_from_file(bsdar, *av, 0); + if (nobj != NULL) + insert_obj(bsdar, nobj, pos); + if (bsdar->options & AR_V && nobj != NULL) + (void)fprintf(bsdar->output, "a - %s\n", *av); + *av = NULL; + } + } + +write_objs: + write_objs(bsdar); + write_cleanup(bsdar); +} + +/* + * Release memory. + */ +static void +write_cleanup(struct bsdar *bsdar) +{ + struct ar_obj *obj, *obj_temp; + + TAILQ_FOREACH_SAFE(obj, &bsdar->v_obj, objs, obj_temp) { + TAILQ_REMOVE(&bsdar->v_obj, obj, objs); + free_obj(obj); + } + + free(bsdar->as); + free(bsdar->s_so); + free(bsdar->s_sn); + bsdar->as = NULL; + bsdar->s_so = NULL; + bsdar->s_sn = NULL; +} + +/* + * Wrapper for archive_write_data(). + */ +static void +write_data(struct bsdar *bsdar, struct archive *a, const void *buf, size_t s) +{ + if (archive_write_data(a, buf, s) != (ssize_t)s) + bsdar_errc(bsdar, 0, "%s", archive_error_string(a)); +} + +/* + * Compute the size of the symbol table for an archive. + */ +static size_t +bsdar_symtab_size(struct bsdar *bsdar) +{ + size_t sz; + + if (bsdar->options & AR_BSD) { + /* + * A BSD style symbol table has two parts. + * Each part is preceded by its size in bytes, + * encoded as a C 'long'. In the first part, + * there are 's_cnt' entries, each entry being + * 2 'long's in size. The second part + * contains a string table. + */ + sz = 2 * sizeof(long) + (bsdar->s_cnt * 2 * sizeof(long)) + + bsdar->s_sn_sz; + } else { + /* + * An SVR4 style symbol table comprises of a 32 bit + * number holding the number of entries, followed by + * that many 32-bit offsets, followed by a string + * table. + */ + sz = sizeof(uint32_t) + bsdar->s_cnt * sizeof(uint32_t) + + bsdar->s_sn_sz; + } + + return (sz); +} + +static void +write_svr4_symtab_entry(struct bsdar *bsdar, struct archive *a) +{ + int nr; + uint32_t i; + + /* Translate offsets to big-endian form. */ + for (i = 0; i < bsdar->s_cnt; i++) + bsdar->s_so[i] = htobe32(bsdar->s_so[i]); + + nr = htobe32(bsdar->s_cnt); + write_data(bsdar, a, &nr, sizeof(uint32_t)); + write_data(bsdar, a, bsdar->s_so, sizeof(uint32_t) * + bsdar->s_cnt); + write_data(bsdar, a, bsdar->s_sn, bsdar->s_sn_sz); +} + +static void +write_bsd_symtab_entry(struct bsdar *bsdar, struct archive *a) +{ + long br_sz, br_off, br_strx; + char *s; + uint32_t i; + + /* + * Write out the size in the byte of the array of 'ranlib' + * descriptors to follow. + */ + + br_sz = (long) (bsdar->s_cnt * 2 * sizeof(long)); + write_data(bsdar, a, &br_sz, sizeof(long)); + + /* + * Write out the array of 'ranlib' descriptors. Each + * descriptor comprises of (a) an offset into the following + * string table and (b) a file offset to the relevant member. + */ + for (i = 0, s = bsdar->s_sn; i < bsdar->s_cnt; i++) { + br_strx = (long) (s - bsdar->s_sn); + br_off = (long) bsdar->s_so[i]; + write_data(bsdar, a, &br_strx, sizeof(long)); + write_data(bsdar, a, &br_off, sizeof(long)); + + /* Find the start of the next symbol in the string table. */ + while (*s++ != '\0') + ; + } + + /* + * Write out the size of the string table as a 'long', + * followed by the string table itself. + */ + br_sz = (long) bsdar->s_sn_sz; + write_data(bsdar, a, &br_sz, sizeof(long)); + write_data(bsdar, a, bsdar->s_sn, bsdar->s_sn_sz); +} + + +/* + * Write the resulting archive members. + */ +static void +write_objs(struct bsdar *bsdar) +{ + struct ar_obj *obj; + struct archive *a; + struct archive_entry *entry; + size_t s_sz; /* size of archive symbol table. */ + size_t pm_sz; /* size of pseudo members */ + size_t namelen; /* size of member name. */ + size_t obj_sz; /* size of object + extended header. */ + int i; + char *buf; + const char *entry_name; + + bsdar->rela_off = 0; + + /* + * Create the archive symbol table and the archive string + * table, if needed. + */ + TAILQ_FOREACH(obj, &bsdar->v_obj, objs) { + if (!(bsdar->options & AR_SS) && obj->elf != NULL) + create_symtab_entry(bsdar, obj->elf); + + obj_sz = 0; + namelen = strlen(obj->name); + if (bsdar->options & AR_BSD) { + /* Account for the space used by the file name. */ + if (namelen > _MAXNAMELEN_BSD || + strchr(obj->name, ' ')) + obj_sz += namelen; + } else if (namelen > _MAXNAMELEN_SVR4) + add_to_ar_str_table(bsdar, obj->name); + + obj_sz += obj->size; /* add the actual object size */ + + /* Roundup the final size and add the header length. */ + bsdar->rela_off += _ARHDR_LEN + obj_sz + (obj_sz & 1); + } + + /* + * Pad the symbol name string table. It is treated specially + * because symbol name table should be padded by a '\0', and + * not '\n' as for normal members. The size of the 'sn' table + * includes the pad byte. + */ + if (bsdar->s_cnt != 0 && bsdar->s_sn_sz % 2 != 0) + bsdar->s_sn[bsdar->s_sn_sz++] = '\0'; + + /* + * The archive string table is padded by a "\n" like a normal + * member. The difference is that the size of archive string + * table includes the pad byte, while normal members' size + * fields do not. + */ + if (bsdar->as != NULL && bsdar->as_sz % 2 != 0) + bsdar->as[bsdar->as_sz++] = '\n'; + + /* + * If there is a symbol table, calculate the size of pseudo + * members, and convert previously stored relative offsets to + * absolute ones. + * + * absolute_offset = relative_offset + size_of_pseudo_members) + */ + + s_sz = bsdar_symtab_size(bsdar); + if (bsdar->s_cnt != 0) { + pm_sz = _ARMAG_LEN + (_ARHDR_LEN + s_sz); + if (bsdar->as != NULL) /* SVR4 archives only */ + pm_sz += _ARHDR_LEN + bsdar->as_sz; + for (i = 0; (size_t) i < bsdar->s_cnt; i++) + bsdar->s_so[i] = bsdar->s_so[i] + pm_sz; + } + + if ((a = archive_write_new()) == NULL) + bsdar_errc(bsdar, 0, "archive_write_new failed"); + + if (bsdar->options & AR_BSD) + archive_write_set_format_ar_bsd(a); + else + archive_write_set_format_ar_svr4(a); + + AC(archive_write_open_filename(a, bsdar->filename)); + + /* + * Write the archive symbol table, if there is one. If + * options '-s' was explicitly specified or if we were invoked + * as 'ranlib', write the symbol table even if it is empty. + */ + if ((bsdar->s_cnt != 0 && !(bsdar->options & AR_SS)) || + bsdar->options & AR_S) { + if (bsdar->options & AR_BSD) + entry_name = AR_SYMTAB_NAME_BSD; + else + entry_name = AR_SYMTAB_NAME_SVR4; + + entry = archive_entry_new(); + archive_entry_copy_pathname(entry, entry_name); + if ((bsdar->options & AR_D) == 0) + archive_entry_set_mtime(entry, time(NULL), 0); + archive_entry_set_size(entry, s_sz); + AC(archive_write_header(a, entry)); + if (bsdar->options & AR_BSD) + write_bsd_symtab_entry(bsdar, a); + else + write_svr4_symtab_entry(bsdar, a); + archive_entry_free(entry); + } + + /* Write the archive string table, if any. */ + if (bsdar->as != NULL) { + entry = archive_entry_new(); + archive_entry_copy_pathname(entry, AR_STRINGTAB_NAME_SVR4); + archive_entry_set_size(entry, bsdar->as_sz); + AC(archive_write_header(a, entry)); + write_data(bsdar, a, bsdar->as, bsdar->as_sz); + archive_entry_free(entry); + } + + /* Write normal members. */ + TAILQ_FOREACH(obj, &bsdar->v_obj, objs) { + if ((buf = elf_rawfile(obj->elf, NULL)) == NULL) { + bsdar_warnc(bsdar, 0, "elf_rawfile() failed: %s", + elf_errmsg(-1)); + continue; + } + + entry = archive_entry_new(); + archive_entry_copy_pathname(entry, obj->name); + archive_entry_set_uid(entry, obj->uid); + archive_entry_set_gid(entry, obj->gid); + archive_entry_set_mode(entry, obj->md); + archive_entry_set_size(entry, obj->size); + archive_entry_set_mtime(entry, obj->mtime, 0); + archive_entry_set_dev(entry, obj->dev); + archive_entry_set_ino(entry, obj->ino); + archive_entry_set_filetype(entry, AE_IFREG); + AC(archive_write_header(a, entry)); + write_data(bsdar, a, buf, obj->size); + archive_entry_free(entry); + } + + AC(archive_write_close(a)); + ACV(archive_write_free(a)); +} + +/* + * Extract global symbols from ELF binary members. + */ +static void +create_symtab_entry(struct bsdar *bsdar, Elf *e) +{ + Elf_Scn *scn; + GElf_Shdr shdr; + GElf_Sym sym; + Elf_Data *data; + char *name; + size_t n, shstrndx; + int elferr, tabndx, len, i; + + if (elf_kind(e) != ELF_K_ELF) { + /* Silently a ignore non-ELF member. */ + return; + } + if (elf_getshstrndx(e, &shstrndx) == 0) { + bsdar_warnc(bsdar, 0, "elf_getshstrndx failed: %s", + elf_errmsg(-1)); + return; + } + + tabndx = -1; + scn = NULL; + while ((scn = elf_nextscn(e, scn)) != NULL) { + if (gelf_getshdr(scn, &shdr) != &shdr) { + bsdar_warnc(bsdar, 0, + "elf_getshdr failed: %s", elf_errmsg(-1)); + continue; + } + if ((name = elf_strptr(e, shstrndx, shdr.sh_name)) == NULL) { + bsdar_warnc(bsdar, 0, + "elf_strptr failed: %s", elf_errmsg(-1)); + continue; + } + if (strcmp(name, ".strtab") == 0) { + tabndx = elf_ndxscn(scn); + break; + } + } + elferr = elf_errno(); + if (elferr != 0) + bsdar_warnc(bsdar, 0, "elf_nextscn failed: %s", + elf_errmsg(elferr)); + if (tabndx == -1) { + bsdar_warnc(bsdar, 0, "can't find .strtab section"); + return; + } + + scn = NULL; + while ((scn = elf_nextscn(e, scn)) != NULL) { + if (gelf_getshdr(scn, &shdr) != &shdr) { + bsdar_warnc(bsdar, 0, "elf_getshdr failed: %s", + elf_errmsg(-1)); + continue; + } + if (shdr.sh_type != SHT_SYMTAB) + continue; + + data = NULL; + n = 0; + while (n < shdr.sh_size && + (data = elf_getdata(scn, data)) != NULL) { + len = data->d_size / shdr.sh_entsize; + for (i = 0; i < len; i++) { + if (gelf_getsym(data, i, &sym) != &sym) { + bsdar_warnc(bsdar, 0, + "gelf_getsym failed: %s", + elf_errmsg(-1)); + continue; + } + + /* Keep only global and weak symbols. */ + if (GELF_ST_BIND(sym.st_info) != STB_GLOBAL && + GELF_ST_BIND(sym.st_info) != STB_WEAK) + continue; + + /* Keep only defined symbols. */ + if (sym.st_shndx == SHN_UNDEF) + continue; + + if ((name = elf_strptr(e, tabndx, + sym.st_name)) == NULL) { + bsdar_warnc(bsdar, 0, + "elf_strptr failed: %s", + elf_errmsg(-1)); + continue; + } + + add_to_ar_sym_table(bsdar, name); + } + } + } + elferr = elf_errno(); + if (elferr != 0) + bsdar_warnc(bsdar, 0, "elf_nextscn failed: %s", + elf_errmsg(elferr)); +} + +/* + * Append to the archive string table buffer. + */ +static void +add_to_ar_str_table(struct bsdar *bsdar, const char *name) +{ + + if (bsdar->as == NULL) { + bsdar->as_cap = _INIT_AS_CAP; + bsdar->as_sz = 0; + if ((bsdar->as = malloc(bsdar->as_cap)) == NULL) + bsdar_errc(bsdar, errno, "malloc failed"); + } + + /* + * The space required for holding one member name in the 'as' + * table includes: strlen(name) + (1 for '/') + (1 for '\n') + + * (possibly 1 for padding). + */ + while (bsdar->as_sz + strlen(name) + 3 > bsdar->as_cap) { + bsdar->as_cap *= 2; + bsdar->as = realloc(bsdar->as, bsdar->as_cap); + if (bsdar->as == NULL) + bsdar_errc(bsdar, errno, "realloc failed"); + } + strncpy(&bsdar->as[bsdar->as_sz], name, strlen(name)); + bsdar->as_sz += strlen(name); + bsdar->as[bsdar->as_sz++] = '/'; + bsdar->as[bsdar->as_sz++] = '\n'; +} + +/* + * Append to the archive symbol table buffer. + */ +static void +add_to_ar_sym_table(struct bsdar *bsdar, const char *name) +{ + + if (bsdar->s_so == NULL) { + if ((bsdar->s_so = malloc(_INIT_SYMOFF_CAP)) == + NULL) + bsdar_errc(bsdar, errno, "malloc failed"); + bsdar->s_so_cap = _INIT_SYMOFF_CAP; + bsdar->s_cnt = 0; + } + + if (bsdar->s_sn == NULL) { + if ((bsdar->s_sn = malloc(_INIT_SYMNAME_CAP)) == NULL) + bsdar_errc(bsdar, errno, "malloc failed"); + bsdar->s_sn_cap = _INIT_SYMNAME_CAP; + bsdar->s_sn_sz = 0; + } + + if (bsdar->s_cnt * sizeof(uint32_t) >= bsdar->s_so_cap) { + bsdar->s_so_cap *= 2; + bsdar->s_so = realloc(bsdar->s_so, bsdar->s_so_cap); + if (bsdar->s_so == NULL) + bsdar_errc(bsdar, errno, "realloc failed"); + } + bsdar->s_so[bsdar->s_cnt] = bsdar->rela_off; + bsdar->s_cnt++; + + /* + * The space required for holding one symbol name in the 'sn' + * table includes: strlen(name) + (1 for '\n') + (possibly 1 + * for padding). + */ + while (bsdar->s_sn_sz + strlen(name) + 2 > bsdar->s_sn_cap) { + bsdar->s_sn_cap *= 2; + bsdar->s_sn = realloc(bsdar->s_sn, bsdar->s_sn_cap); + if (bsdar->s_sn == NULL) + bsdar_errc(bsdar, errno, "realloc failed"); + } + strncpy(&bsdar->s_sn[bsdar->s_sn_sz], name, strlen(name)); + bsdar->s_sn_sz += strlen(name); + bsdar->s_sn[bsdar->s_sn_sz++] = '\0'; +} diff --git a/contrib/elftoolchain/brandelf/Makefile b/contrib/elftoolchain/brandelf/Makefile new file mode 100644 index 000000000000..28ba3e0df01c --- /dev/null +++ b/contrib/elftoolchain/brandelf/Makefile @@ -0,0 +1,9 @@ +# $Id: Makefile 2066 2011-10-26 15:40:28Z jkoshy $ + +TOP= .. + +PROG= brandelf +WARNS?= 6 +LDADD= -lelftc -lelf + +.include "${TOP}/mk/elftoolchain.prog.mk" diff --git a/contrib/elftoolchain/brandelf/brandelf.1 b/contrib/elftoolchain/brandelf/brandelf.1 new file mode 100644 index 000000000000..1c2e485e44e1 --- /dev/null +++ b/contrib/elftoolchain/brandelf/brandelf.1 @@ -0,0 +1,151 @@ +.\" Copyright (c) 1997 +.\" John-Mark Gurney. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the author nor the names of any co-contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY John-Mark Gurney AND CONTRIBUTORS ``AS IS'' +.\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $FreeBSD: src/usr.bin/brandelf/brandelf.1,v 1.17 2007/03/09 14:36:18 ru Exp $ +.\" $Id: brandelf.1 3195 2015-05-12 17:22:19Z emaste $ +.\" +.Dd October 27, 2014 +.Dt BRANDELF 1 +.Os +.Sh NAME +.Nm brandelf +.Nd mark an ELF binary for a specific ABI +.Sh SYNOPSIS +.Nm +.Op Fl V | Fl -version +.Op Fl f Ar ELF_ABI_number +.Op Fl h | Fl -help +.Op Fl l +.Op Fl t Ar brand +.Op Fl v +.Ar +.Sh DESCRIPTION +The +.Nm +utility marks an ELF binary to be run under a certain ABI. +.Pp +The options are as follows: +.Bl -tag -width indent +.It Fl f Ar ELF_ABI_number +Forces branding with the supplied ELF ABI number. +Incompatible with the +.Fl t +option. +These values are assigned by SCO/USL. +.It Fl h | Fl -help +Print a usage message and exit. +.It Fl l +Writes the list of all known ELF types to standard output. +.It Fl t Ar brand +Brands the given ELF binaries to be of the ABI specified by argument +.Ar brand . +Supported ABIs include +.Dq Li 86Open , +.Dq Li AIX , +.Dq Li ARM , +.Dq Li AROS , +.Dq Li FreeBSD , +.Dq Li GNU , +.Dq Li HP/UX , +.Dq Li Hurd , +.Dq Li IRIX , +.Dq Li Linux +(an alias for +.Dq Li GNU ) , +.Dq Li Modesto , +.Dq Li NSK , +.Dq Li NetBSD , +.Dq Li None , +.Dq Li OpenBSD , +.Dq Li OpenVMS , +.Dq Li Standalone , +.Dq Li SVR4 +(an alias for +.Dq Li None ) , +.Dq Li Solaris +and +.Dq Li Tru64 . +.It Fl v +This option is accepted for compatibility with other versions of +.Nm , +but is otherwise ignored. +.It Fl V | Fl -version +Print a version identifier and exit. +.El +.Pp +If the options +.Fl f Ar ELF_ABI_number +or +.Fl t Ar brand +were specified, +.Nm +will brand the files named by command-line arguments +.Ar +to be of type +.Ar ELF_ABI_number +or +.Ar brand +respectively. +.Pp +If neither of the +.Fl f +or +.Fl t +options were specified, +.Nm +will display the current branding for the files named by the arguments +.Ar . +.Sh EXIT STATUS +Exit status is 0 on success, and 1 if the command +fails if a file does not exist, is too short, fails to brand properly, +or the brand requested is not one of the known types and the +.Fl f +option is not set. +.Sh EXAMPLES +The following is an example of a typical usage +of the +.Nm +command: +.Bd -literal -offset indent +brandelf file +brandelf -t GNU file +.Ed +.Sh SEE ALSO +.Rs +.%A The Santa Cruz Operation, Inc. +.%T System V Application Binary Interface +.%D April 29, 1998 (DRAFT) +.%O http://www.sco.com/developer/devspecs/ +.Re +.Sh HISTORY +The +.Nm +manual page first appeared in +.Fx 2.2 . +.Sh AUTHORS +This manual page was written by +.An John-Mark Gurney Aq Mt gurney_j@efn.org . diff --git a/contrib/elftoolchain/brandelf/brandelf.c b/contrib/elftoolchain/brandelf/brandelf.c new file mode 100644 index 000000000000..5f5e6317b7b6 --- /dev/null +++ b/contrib/elftoolchain/brandelf/brandelf.c @@ -0,0 +1,311 @@ +/*- + * Copyright (c) 2008 Hyogeol Lee + * Copyright (c) 2000, 2001 David O'Brien + * Copyright (c) 1996 Søren Schmidt + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer + * in this position and unchanged. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "_elftc.h" + +ELFTC_VCSID("$Id: brandelf.c 3174 2015-03-27 17:13:41Z emaste $"); + +static int elftype(const char *); +static const char *iselftype(int); +static void printelftypes(void); +static void printversion(void); +static void usage(void); + +struct ELFtypes { + const char *str; + int value; +}; +/* XXX - any more types? */ +static struct ELFtypes elftypes[] = { + { "86Open", ELFOSABI_86OPEN }, + { "AIX", ELFOSABI_AIX }, + { "ARM", ELFOSABI_ARM }, + { "AROS", ELFOSABI_AROS }, + { "FreeBSD", ELFOSABI_FREEBSD }, + { "GNU", ELFOSABI_GNU }, + { "HP/UX", ELFOSABI_HPUX}, + { "Hurd", ELFOSABI_HURD }, + { "IRIX", ELFOSABI_IRIX }, + { "Linux", ELFOSABI_GNU }, + { "Modesto", ELFOSABI_MODESTO }, + { "NSK", ELFOSABI_NSK }, + { "NetBSD", ELFOSABI_NETBSD}, + { "None", ELFOSABI_NONE}, + { "OpenBSD", ELFOSABI_OPENBSD }, + { "OpenVMS", ELFOSABI_OPENVMS }, + { "Standalone", ELFOSABI_STANDALONE }, + { "SVR4", ELFOSABI_NONE }, + { "Solaris", ELFOSABI_SOLARIS }, + { "Tru64", ELFOSABI_TRU64 } +}; + +static struct option brandelf_longopts[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'V' }, + { NULL, 0, NULL, 0 } +}; + +int +main(int argc, char **argv) +{ + GElf_Ehdr ehdr; + Elf *elf; + Elf_Kind kind; + int type = ELFOSABI_NONE; + int retval = 0; + int ch, change = 0, force = 0, listed = 0; + + if (elf_version(EV_CURRENT) == EV_NONE) + errx(EXIT_FAILURE, "elf_version error"); + + while ((ch = getopt_long(argc, argv, "Vf:hlt:v", brandelf_longopts, + NULL)) != -1) + switch (ch) { + case 'f': + if (change) + errx(EXIT_FAILURE, "ERROR: the -f option is " + "incompatible with the -t option."); + force = 1; + type = atoi(optarg); + if (errno == ERANGE || type < 0 || type > 255) { + warnx("ERROR: invalid argument to option " + "-f: %s", optarg); + usage(); + } + break; + case 'h': + usage(); + break; + case 'l': + printelftypes(); + listed = 1; + break; + case 'v': + /* This flag is ignored. */ + break; + case 't': + if (force) + errx(EXIT_FAILURE, "the -t option is " + "incompatible with the -f option."); + if ((type = elftype(optarg)) == -1) { + warnx("ERROR: invalid ELF type '%s'", optarg); + usage(); + } + + change = 1; + break; + case 'V': + printversion(); + break; + default: + usage(); + } + argc -= optind; + argv += optind; + if (!argc) { + if (listed) + exit(0); + else { + warnx("no file(s) specified"); + usage(); + } + } + + while (argc) { + int fd; + + elf = NULL; + + if ((fd = open(argv[0], (change || force) ? O_RDWR : + O_RDONLY, 0)) < 0) { + warn("error opening file %s", argv[0]); + retval = 1; + goto fail; + } + + if ((elf = elf_begin(fd, (change || force) ? ELF_C_RDWR : + ELF_C_READ, NULL)) == NULL) { + warnx("elf_begin failed: %s", elf_errmsg(-1)); + retval = 1; + goto fail; + } + + if ((kind = elf_kind(elf)) != ELF_K_ELF) { + if (kind == ELF_K_AR) + warnx("file '%s' is an archive.", argv[0]); + else + warnx("file '%s' is not an ELF file.", + argv[0]); + retval = 1; + goto fail; + } + + if (gelf_getehdr(elf, &ehdr) == NULL) { + warnx("gelf_getehdr: %s", elf_errmsg(-1)); + retval = 1; + goto fail; + } + + if (!change && !force) { + fprintf(stdout, + "File '%s' is of brand '%s' (%u).\n", + argv[0], iselftype(ehdr.e_ident[EI_OSABI]), + ehdr.e_ident[EI_OSABI]); + if (!iselftype(type)) { + warnx("ELF ABI Brand '%u' is unknown", + type); + printelftypes(); + } + } else { + + /* + * Keep the existing layout of the ELF object. + */ + if (elf_flagelf(elf, ELF_C_SET, ELF_F_LAYOUT) == 0) { + warnx("elf_flagelf failed: %s", + elf_errmsg(-1)); + retval = 1; + goto fail; + } + + /* + * Update the ABI type. + */ + ehdr.e_ident[EI_OSABI] = type; + if (gelf_update_ehdr(elf, &ehdr) == 0) { + warnx("gelf_update_ehdr error: %s", + elf_errmsg(-1)); + retval = 1; + goto fail; + } + + /* + * Write back changes. + */ + if (elf_update(elf, ELF_C_WRITE) == -1) { + warnx("elf_update error: %s", elf_errmsg(-1)); + retval = 1; + goto fail; + } + } +fail: + + if (elf) + elf_end(elf); + + if (fd >= 0 && close(fd) == -1) { + warnx("%s: close error", argv[0]); + retval = 1; + } + + argc--; + argv++; + } + + return (retval); +} + +#define USAGE_MESSAGE "\ +Usage: %s [options] file...\n\ + Set or display the ABI field for an ELF object.\n\n\ + Supported options are:\n\ + -f NUM Set the ELF ABI to the number 'NUM'.\n\ + -h | --help Print a usage message and exit.\n\ + -l List known ELF ABI names.\n\ + -t ABI Set the ELF ABI to the value named by \"ABI\".\n\ + -V | --version Print a version identifier and exit.\n" + +static void +usage(void) +{ + (void) fprintf(stderr, USAGE_MESSAGE, ELFTC_GETPROGNAME()); + exit(1); +} + +static void +printversion(void) +{ + (void) printf("%s (%s)\n", ELFTC_GETPROGNAME(), elftc_version()); + exit(0); +} + +static const char * +iselftype(int etype) +{ + size_t elfwalk; + + for (elfwalk = 0; + elfwalk < sizeof(elftypes)/sizeof(elftypes[0]); + elfwalk++) + if (etype == elftypes[elfwalk].value) + return (elftypes[elfwalk].str); + return (0); +} + +static int +elftype(const char *elfstrtype) +{ + size_t elfwalk; + + for (elfwalk = 0; + elfwalk < sizeof(elftypes)/sizeof(elftypes[0]); + elfwalk++) + if (strcasecmp(elfstrtype, elftypes[elfwalk].str) == 0) + return (elftypes[elfwalk].value); + return (-1); +} + +static void +printelftypes(void) +{ + size_t elfwalk; + + (void) printf("Known ELF types are: "); + for (elfwalk = 0; + elfwalk < sizeof(elftypes)/sizeof(elftypes[0]); + elfwalk++) + (void) printf("%s(%u) ", elftypes[elfwalk].str, + elftypes[elfwalk].value); + (void) printf("\n"); +} diff --git a/contrib/elftoolchain/elfdump/Makefile b/contrib/elftoolchain/elfdump/Makefile new file mode 100644 index 000000000000..b78d4652664e --- /dev/null +++ b/contrib/elftoolchain/elfdump/Makefile @@ -0,0 +1,11 @@ +# $Id: Makefile 2289 2011-12-04 07:11:47Z jkoshy $ + +TOP= .. + +PROG= elfdump +WARNS?= 6 + +DPADD= ${LIBELFTC} ${LIBELF} +LDADD= -lelftc -lelf + +.include "${TOP}/mk/elftoolchain.prog.mk" diff --git a/contrib/elftoolchain/elfdump/elfdump.1 b/contrib/elftoolchain/elfdump/elfdump.1 new file mode 100644 index 000000000000..eae7262900a8 --- /dev/null +++ b/contrib/elftoolchain/elfdump/elfdump.1 @@ -0,0 +1,158 @@ +.\" Copyright (c) 2003 David O'Brien +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $FreeBSD: src/usr.bin/elfdump/elfdump.1,v 1.6 2005/01/18 13:43:48 ru Exp $ +.\" $Id: elfdump.1 3195 2015-05-12 17:22:19Z emaste $ +.\" +.Dd August 25, 2011 +.Dt ELFDUMP 1 +.Os +.Sh NAME +.Nm elfdump +.Nd "display information about" +.Tn ELF +files +.Sh SYNOPSIS +.Nm +.Fl a | cdeGhiknprsv +.Op Fl S +.Op Fl V +.Op Fl N Ar name +.Op Fl w Ar file +.Ar file ... +.Sh DESCRIPTION +The +.Nm +utility +dumps various information about the specified +.Tn ELF +.Ar file . +.Pp +The options are as follows: +.Bl -tag -width ".Fl w Ar file" +.It Fl a +Dump all information. +.It Fl c +Dump shared headers. +.It Fl d +Dump dynamic symbols. +.It Fl e +Dump ELF header. +.It Fl G +Dump the GOT. +.It Fl h +Dump the hash values. +.It Fl i +Dump the dynamic interpreter. +.It Fl k +Dump the ELF checksum. +.It Fl n +Dump note sections. +.It Fl N Ar name +Only dump the section with the specific +.Ar name . +Archive symbol table can be specified with +the special section name ARSYM. +More than one +.Fl N +option may appear. +.It Fl p +Dump the program header. +.It Fl r +Dump relocations. +.It Fl s +Dump the symbol table. +.It Fl S +Output in the Solaris +.Nm +format. +.It Fl v +Dump the symbol-versioning sections. +.It Fl V +Print a version identifier and exit. +.It Fl w Ar file +Write output to a +.Ar file +instead of the standard output. +.El +.Sh EXIT STATUS +.Ex -std +.Sh EXAMPLES +The following is an example of a typical usage +of the +.Nm +command: +.Pp +.Dl "elfdump -a -w output /bin/ls" +.Pp +To dump the content of '.dynsym' symbol table: +.Pp +.Dl "elfdump -s -N .dynsym /bin/ls" +.Pp +To dump the archive symbol table, +but not the symbol tables of archive members: +.Pp +.Dl "elfdump -s -N ARSYM /usr/lib/libelf.a" +.Pp +To dump the content of .got section and +the symbol-versioning sections in Solaris +.Nm +format: +.Pp +.Dl "elfdump -S -Gv /bin/ls" +.Sh SEE ALSO +.Xr objdump 1 , +.Xr readelf 1 , +.Xr elf 3 +.Rs +.%A "AT&T Unix Systems Labs" +.%T "System V Application Binary Interface" +.%O http://www.sco.com/developers/gabi/ +.Re +.Sh HISTORY +The +.Nm +utility first appeared in +.Fx 5.0 . +.Sh AUTHORS +.An -nosplit +The +.Nm +utility +was written by +.An Jake Burkholder Aq Mt jake@FreeBSD.org . +Later it was rewritten based on the +libelf library. +This +manual page was written by +.An David O'Brien Aq Mt obrien@FreeBSD.org . +.Pp +.An Kai Wang Aq Mt kaiw@FreeBSD.org +rewrote it using the +.Lb libelf +and implemented additional functionality. +.Sh BUGS +Does not fully implement the +.Tn ELF +gABI. diff --git a/contrib/elftoolchain/elfdump/elfdump.c b/contrib/elftoolchain/elfdump/elfdump.c new file mode 100644 index 000000000000..e4e565bdd5c5 --- /dev/null +++ b/contrib/elftoolchain/elfdump/elfdump.c @@ -0,0 +1,2819 @@ +/*- + * Copyright (c) 2007-2012 Kai Wang + * Copyright (c) 2003 David O'Brien. All rights reserved. + * Copyright (c) 2001 Jake Burkholder + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef USE_LIBARCHIVE_AR +#include +#include +#endif + +#include "_elftc.h" + +ELFTC_VCSID("$Id: elfdump.c 3198 2015-05-14 18:36:19Z emaste $"); + +#if defined(ELFTC_NEED_ELF_NOTE_DEFINITION) +#include "native-elf-format.h" +#if ELFTC_CLASS == ELFCLASS32 +typedef Elf32_Nhdr Elf_Note; +#else +typedef Elf64_Nhdr Elf_Note; +#endif +#endif + +/* elfdump(1) options. */ +#define ED_DYN (1<<0) +#define ED_EHDR (1<<1) +#define ED_GOT (1<<2) +#define ED_HASH (1<<3) +#define ED_INTERP (1<<4) +#define ED_NOTE (1<<5) +#define ED_PHDR (1<<6) +#define ED_REL (1<<7) +#define ED_SHDR (1<<8) +#define ED_SYMTAB (1<<9) +#define ED_SYMVER (1<<10) +#define ED_CHECKSUM (1<<11) +#define ED_ALL ((1<<12)-1) + +/* elfdump(1) run control flags. */ +#define SOLARIS_FMT (1<<0) +#define PRINT_FILENAME (1<<1) +#define PRINT_ARSYM (1<<2) +#define ONLY_ARSYM (1<<3) + +/* Convenient print macro. */ +#define PRT(...) fprintf(ed->out, __VA_ARGS__) + +/* Internal data structure for sections. */ +struct section { + const char *name; /* section name */ + Elf_Scn *scn; /* section scn */ + uint64_t off; /* section offset */ + uint64_t sz; /* section size */ + uint64_t entsize; /* section entsize */ + uint64_t align; /* section alignment */ + uint64_t type; /* section type */ + uint64_t flags; /* section flags */ + uint64_t addr; /* section virtual addr */ + uint32_t link; /* section link ndx */ + uint32_t info; /* section info ndx */ +}; + +struct spec_name { + const char *name; + STAILQ_ENTRY(spec_name) sn_list; +}; + +/* Structure encapsulates the global data for readelf(1). */ +struct elfdump { + FILE *out; /* output redirection. */ + const char *filename; /* current processing file. */ + const char *archive; /* archive name */ + int options; /* command line options. */ + int flags; /* run control flags. */ + Elf *elf; /* underlying ELF descriptor. */ +#ifndef USE_LIBARCHIVE_AR + Elf *ar; /* ar(1) archive descriptor. */ +#endif + GElf_Ehdr ehdr; /* ELF header. */ + int ec; /* ELF class. */ + size_t shnum; /* #sections. */ + struct section *sl; /* list of sections. */ + STAILQ_HEAD(, spec_name) snl; /* list of names specified by -N. */ +}; + +/* Relocation entry. */ +struct rel_entry { + union { + GElf_Rel rel; + GElf_Rela rela; + } u_r; + const char *symn; + uint32_t type; +}; + +#if defined(ELFTC_NEED_BYTEORDER_EXTENSIONS) +static __inline uint32_t +be32dec(const void *pp) +{ + unsigned char const *p = (unsigned char const *)pp; + + return ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]); +} + +static __inline uint32_t +le32dec(const void *pp) +{ + unsigned char const *p = (unsigned char const *)pp; + + return ((p[3] << 24) | (p[2] << 16) | (p[1] << 8) | p[0]); +} +#endif + +/* http://www.sco.com/developers/gabi/latest/ch5.dynamic.html#tag_encodings */ +static const char * +d_tags(uint64_t tag) +{ + switch (tag) { + case 0: return "DT_NULL"; + case 1: return "DT_NEEDED"; + case 2: return "DT_PLTRELSZ"; + case 3: return "DT_PLTGOT"; + case 4: return "DT_HASH"; + case 5: return "DT_STRTAB"; + case 6: return "DT_SYMTAB"; + case 7: return "DT_RELA"; + case 8: return "DT_RELASZ"; + case 9: return "DT_RELAENT"; + case 10: return "DT_STRSZ"; + case 11: return "DT_SYMENT"; + case 12: return "DT_INIT"; + case 13: return "DT_FINI"; + case 14: return "DT_SONAME"; + case 15: return "DT_RPATH"; + case 16: return "DT_SYMBOLIC"; + case 17: return "DT_REL"; + case 18: return "DT_RELSZ"; + case 19: return "DT_RELENT"; + case 20: return "DT_PLTREL"; + case 21: return "DT_DEBUG"; + case 22: return "DT_TEXTREL"; + case 23: return "DT_JMPREL"; + case 24: return "DT_BIND_NOW"; + case 25: return "DT_INIT_ARRAY"; + case 26: return "DT_FINI_ARRAY"; + case 27: return "DT_INIT_ARRAYSZ"; + case 28: return "DT_FINI_ARRAYSZ"; + case 29: return "DT_RUNPATH"; + case 30: return "DT_FLAGS"; + case 32: return "DT_PREINIT_ARRAY"; /* XXX: DT_ENCODING */ + case 33: return "DT_PREINIT_ARRAYSZ"; + /* 0x6000000D - 0x6ffff000 operating system-specific semantics */ + case 0x6ffffdf5: return "DT_GNU_PRELINKED"; + case 0x6ffffdf6: return "DT_GNU_CONFLICTSZ"; + case 0x6ffffdf7: return "DT_GNU_LIBLISTSZ"; + case 0x6ffffdf8: return "DT_SUNW_CHECKSUM"; + case 0x6ffffdf9: return "DT_PLTPADSZ"; + case 0x6ffffdfa: return "DT_MOVEENT"; + case 0x6ffffdfb: return "DT_MOVESZ"; + case 0x6ffffdfc: return "DT_FEATURE"; + case 0x6ffffdfd: return "DT_POSFLAG_1"; + case 0x6ffffdfe: return "DT_SYMINSZ"; + case 0x6ffffdff: return "DT_SYMINENT (DT_VALRNGHI)"; + case 0x6ffffe00: return "DT_ADDRRNGLO"; + case 0x6ffffef5: return "DT_GNU_HASH"; + case 0x6ffffef8: return "DT_GNU_CONFLICT"; + case 0x6ffffef9: return "DT_GNU_LIBLIST"; + case 0x6ffffefa: return "DT_SUNW_CONFIG"; + case 0x6ffffefb: return "DT_SUNW_DEPAUDIT"; + case 0x6ffffefc: return "DT_SUNW_AUDIT"; + case 0x6ffffefd: return "DT_SUNW_PLTPAD"; + case 0x6ffffefe: return "DT_SUNW_MOVETAB"; + case 0x6ffffeff: return "DT_SYMINFO (DT_ADDRRNGHI)"; + case 0x6ffffff9: return "DT_RELACOUNT"; + case 0x6ffffffa: return "DT_RELCOUNT"; + case 0x6ffffffb: return "DT_FLAGS_1"; + case 0x6ffffffc: return "DT_VERDEF"; + case 0x6ffffffd: return "DT_VERDEFNUM"; + case 0x6ffffffe: return "DT_VERNEED"; + case 0x6fffffff: return "DT_VERNEEDNUM"; + case 0x6ffffff0: return "DT_GNU_VERSYM"; + /* 0x70000000 - 0x7fffffff processor-specific semantics */ + case 0x70000000: return "DT_IA_64_PLT_RESERVE"; + case 0x7ffffffd: return "DT_SUNW_AUXILIARY"; + case 0x7ffffffe: return "DT_SUNW_USED"; + case 0x7fffffff: return "DT_SUNW_FILTER"; + default: return "ERROR: TAG NOT DEFINED"; + } +} + +static const char * +e_machines(unsigned int mach) +{ + static char machdesc[64]; + + switch (mach) { + case EM_NONE: return "EM_NONE"; + case EM_M32: return "EM_M32"; + case EM_SPARC: return "EM_SPARC"; + case EM_386: return "EM_386"; + case EM_68K: return "EM_68K"; + case EM_88K: return "EM_88K"; + case EM_IAMCU: return "EM_IAMCU"; + case EM_860: return "EM_860"; + case EM_MIPS: return "EM_MIPS"; + case EM_PPC: return "EM_PPC"; + case EM_ARM: return "EM_ARM"; + case EM_ALPHA: return "EM_ALPHA (legacy)"; + case EM_SPARCV9:return "EM_SPARCV9"; + case EM_IA_64: return "EM_IA_64"; + case EM_X86_64: return "EM_X86_64"; + } + snprintf(machdesc, sizeof(machdesc), + "(unknown machine) -- type 0x%x", mach); + return (machdesc); +} + +static const char *e_types[] = { + "ET_NONE", "ET_REL", "ET_EXEC", "ET_DYN", "ET_CORE" +}; + +static const char *ei_versions[] = { + "EV_NONE", "EV_CURRENT" +}; + +static const char *ei_classes[] = { + "ELFCLASSNONE", "ELFCLASS32", "ELFCLASS64" +}; + +static const char *ei_data[] = { + "ELFDATANONE", "ELFDATA2LSB", "ELFDATA2MSB" +}; + +static const char *ei_abis[] = { + "ELFOSABI_SYSV", "ELFOSABI_HPUX", "ELFOSABI_NETBSD", "ELFOSABI_LINUX", + "ELFOSABI_HURD", "ELFOSABI_86OPEN", "ELFOSABI_SOLARIS", + "ELFOSABI_MONTEREY", "ELFOSABI_IRIX", "ELFOSABI_FREEBSD", + "ELFOSABI_TRU64", "ELFOSABI_MODESTO", "ELFOSABI_OPENBSD" +}; + +static const char *p_types[] = { + "PT_NULL", "PT_LOAD", "PT_DYNAMIC", "PT_INTERP", "PT_NOTE", + "PT_SHLIB", "PT_PHDR", "PT_TLS" +}; + +static const char *p_flags[] = { + "", "PF_X", "PF_W", "PF_X|PF_W", "PF_R", "PF_X|PF_R", "PF_W|PF_R", + "PF_X|PF_W|PF_R" +}; + +static const char * +sh_name(struct elfdump *ed, int ndx) +{ + static char num[10]; + + switch (ndx) { + case SHN_UNDEF: return "UNDEF"; + case SHN_ABS: return "ABS"; + case SHN_COMMON: return "COMMON"; + default: + if ((uint64_t)ndx < ed->shnum) + return (ed->sl[ndx].name); + else { + snprintf(num, sizeof(num), "%d", ndx); + return (num); + } + } +} + +/* http://www.sco.com/developers/gabi/latest/ch4.sheader.html#sh_type */ +static const char * +sh_types(u_int64_t sht) { + switch (sht) { + case 0: return "SHT_NULL"; + case 1: return "SHT_PROGBITS"; + case 2: return "SHT_SYMTAB"; + case 3: return "SHT_STRTAB"; + case 4: return "SHT_RELA"; + case 5: return "SHT_HASH"; + case 6: return "SHT_DYNAMIC"; + case 7: return "SHT_NOTE"; + case 8: return "SHT_NOBITS"; + case 9: return "SHT_REL"; + case 10: return "SHT_SHLIB"; + case 11: return "SHT_DYNSYM"; + case 14: return "SHT_INIT_ARRAY"; + case 15: return "SHT_FINI_ARRAY"; + case 16: return "SHT_PREINIT_ARRAY"; + case 17: return "SHT_GROUP"; + case 18: return "SHT_SYMTAB_SHNDX"; + /* 0x60000000 - 0x6fffffff operating system-specific semantics */ + case 0x6ffffff0: return "XXX:VERSYM"; + case 0x6ffffff6: return "SHT_GNU_HASH"; + case 0x6ffffff7: return "SHT_GNU_LIBLIST"; + case 0x6ffffffc: return "XXX:VERDEF"; + case 0x6ffffffd: return "SHT_SUNW(GNU)_verdef"; + case 0x6ffffffe: return "SHT_SUNW(GNU)_verneed"; + case 0x6fffffff: return "SHT_SUNW(GNU)_versym"; + /* 0x70000000 - 0x7fffffff processor-specific semantics */ + case 0x70000000: return "SHT_IA_64_EXT"; + case 0x70000001: return "SHT_IA_64_UNWIND"; + case 0x7ffffffd: return "XXX:AUXILIARY"; + case 0x7fffffff: return "XXX:FILTER"; + /* 0x80000000 - 0xffffffff application programs */ + default: return "ERROR: SHT NOT DEFINED"; + } +} + +/* + * Define known section flags. These flags are defined in the order + * they are to be printed out. + */ +#define DEFINE_SHFLAGS() \ + DEFINE_SHF(WRITE) \ + DEFINE_SHF(ALLOC) \ + DEFINE_SHF(EXECINSTR) \ + DEFINE_SHF(MERGE) \ + DEFINE_SHF(STRINGS) \ + DEFINE_SHF(INFO_LINK) \ + DEFINE_SHF(LINK_ORDER) \ + DEFINE_SHF(OS_NONCONFORMING) \ + DEFINE_SHF(GROUP) \ + DEFINE_SHF(TLS) + +#undef DEFINE_SHF +#define DEFINE_SHF(F) "SHF_" #F "|" +#define ALLSHFLAGS DEFINE_SHFLAGS() + +static const char * +sh_flags(uint64_t shf) +{ + static char flg[sizeof(ALLSHFLAGS)+1]; + + flg[0] = '\0'; + +#undef DEFINE_SHF +#define DEFINE_SHF(N) \ + if (shf & SHF_##N) \ + strcat(flg, "SHF_" #N "|"); \ + + DEFINE_SHFLAGS() + + flg[strlen(flg) - 1] = '\0'; /* Remove the trailing "|". */ + + return (flg); +} + +static const char *st_types[] = { + "STT_NOTYPE", "STT_OBJECT", "STT_FUNC", "STT_SECTION", "STT_FILE", + "STT_COMMON", "STT_TLS" +}; + +static const char *st_types_S[] = { + "NOTY", "OBJT", "FUNC", "SECT", "FILE" +}; + +static const char *st_bindings[] = { + "STB_LOCAL", "STB_GLOBAL", "STB_WEAK" +}; + +static const char *st_bindings_S[] = { + "LOCL", "GLOB", "WEAK" +}; + +static unsigned char st_others[] = { + 'D', 'I', 'H', 'P' +}; + +static const char * +r_type(unsigned int mach, unsigned int type) +{ + switch(mach) { + case EM_NONE: return ""; + case EM_386: + case EM_IAMCU: + switch(type) { + case 0: return "R_386_NONE"; + case 1: return "R_386_32"; + case 2: return "R_386_PC32"; + case 3: return "R_386_GOT32"; + case 4: return "R_386_PLT32"; + case 5: return "R_386_COPY"; + case 6: return "R_386_GLOB_DAT"; + case 7: return "R_386_JMP_SLOT"; + case 8: return "R_386_RELATIVE"; + case 9: return "R_386_GOTOFF"; + case 10: return "R_386_GOTPC"; + case 14: return "R_386_TLS_TPOFF"; + case 15: return "R_386_TLS_IE"; + case 16: return "R_386_TLS_GOTIE"; + case 17: return "R_386_TLS_LE"; + case 18: return "R_386_TLS_GD"; + case 19: return "R_386_TLS_LDM"; + case 24: return "R_386_TLS_GD_32"; + case 25: return "R_386_TLS_GD_PUSH"; + case 26: return "R_386_TLS_GD_CALL"; + case 27: return "R_386_TLS_GD_POP"; + case 28: return "R_386_TLS_LDM_32"; + case 29: return "R_386_TLS_LDM_PUSH"; + case 30: return "R_386_TLS_LDM_CALL"; + case 31: return "R_386_TLS_LDM_POP"; + case 32: return "R_386_TLS_LDO_32"; + case 33: return "R_386_TLS_IE_32"; + case 34: return "R_386_TLS_LE_32"; + case 35: return "R_386_TLS_DTPMOD32"; + case 36: return "R_386_TLS_DTPOFF32"; + case 37: return "R_386_TLS_TPOFF32"; + default: return ""; + } + case EM_ARM: + switch(type) { + case 0: return "R_ARM_NONE"; + case 1: return "R_ARM_PC24"; + case 2: return "R_ARM_ABS32"; + case 3: return "R_ARM_REL32"; + case 4: return "R_ARM_PC13"; + case 5: return "R_ARM_ABS16"; + case 6: return "R_ARM_ABS12"; + case 7: return "R_ARM_THM_ABS5"; + case 8: return "R_ARM_ABS8"; + case 9: return "R_ARM_SBREL32"; + case 10: return "R_ARM_THM_PC22"; + case 11: return "R_ARM_THM_PC8"; + case 12: return "R_ARM_AMP_VCALL9"; + case 13: return "R_ARM_SWI24"; + case 14: return "R_ARM_THM_SWI8"; + case 15: return "R_ARM_XPC25"; + case 16: return "R_ARM_THM_XPC22"; + case 20: return "R_ARM_COPY"; + case 21: return "R_ARM_GLOB_DAT"; + case 22: return "R_ARM_JUMP_SLOT"; + case 23: return "R_ARM_RELATIVE"; + case 24: return "R_ARM_GOTOFF"; + case 25: return "R_ARM_GOTPC"; + case 26: return "R_ARM_GOT32"; + case 27: return "R_ARM_PLT32"; + case 100: return "R_ARM_GNU_VTENTRY"; + case 101: return "R_ARM_GNU_VTINHERIT"; + case 250: return "R_ARM_RSBREL32"; + case 251: return "R_ARM_THM_RPC22"; + case 252: return "R_ARM_RREL32"; + case 253: return "R_ARM_RABS32"; + case 254: return "R_ARM_RPC24"; + case 255: return "R_ARM_RBASE"; + default: return ""; + } + case EM_IA_64: + switch(type) { + case 0: return "R_IA_64_NONE"; + case 33: return "R_IA_64_IMM14"; + case 34: return "R_IA_64_IMM22"; + case 35: return "R_IA_64_IMM64"; + case 36: return "R_IA_64_DIR32MSB"; + case 37: return "R_IA_64_DIR32LSB"; + case 38: return "R_IA_64_DIR64MSB"; + case 39: return "R_IA_64_DIR64LSB"; + case 42: return "R_IA_64_GPREL22"; + case 43: return "R_IA_64_GPREL64I"; + case 44: return "R_IA_64_GPREL32MSB"; + case 45: return "R_IA_64_GPREL32LSB"; + case 46: return "R_IA_64_GPREL64MSB"; + case 47: return "R_IA_64_GPREL64LSB"; + case 50: return "R_IA_64_LTOFF22"; + case 51: return "R_IA_64_LTOFF64I"; + case 58: return "R_IA_64_PLTOFF22"; + case 59: return "R_IA_64_PLTOFF64I"; + case 62: return "R_IA_64_PLTOFF64MSB"; + case 63: return "R_IA_64_PLTOFF64LSB"; + case 67: return "R_IA_64_FPTR64I"; + case 68: return "R_IA_64_FPTR32MSB"; + case 69: return "R_IA_64_FPTR32LSB"; + case 70: return "R_IA_64_FPTR64MSB"; + case 71: return "R_IA_64_FPTR64LSB"; + case 72: return "R_IA_64_PCREL60B"; + case 73: return "R_IA_64_PCREL21B"; + case 74: return "R_IA_64_PCREL21M"; + case 75: return "R_IA_64_PCREL21F"; + case 76: return "R_IA_64_PCREL32MSB"; + case 77: return "R_IA_64_PCREL32LSB"; + case 78: return "R_IA_64_PCREL64MSB"; + case 79: return "R_IA_64_PCREL64LSB"; + case 82: return "R_IA_64_LTOFF_FPTR22"; + case 83: return "R_IA_64_LTOFF_FPTR64I"; + case 84: return "R_IA_64_LTOFF_FPTR32MSB"; + case 85: return "R_IA_64_LTOFF_FPTR32LSB"; + case 86: return "R_IA_64_LTOFF_FPTR64MSB"; + case 87: return "R_IA_64_LTOFF_FPTR64LSB"; + case 92: return "R_IA_64_SEGREL32MSB"; + case 93: return "R_IA_64_SEGREL32LSB"; + case 94: return "R_IA_64_SEGREL64MSB"; + case 95: return "R_IA_64_SEGREL64LSB"; + case 100: return "R_IA_64_SECREL32MSB"; + case 101: return "R_IA_64_SECREL32LSB"; + case 102: return "R_IA_64_SECREL64MSB"; + case 103: return "R_IA_64_SECREL64LSB"; + case 108: return "R_IA_64_REL32MSB"; + case 109: return "R_IA_64_REL32LSB"; + case 110: return "R_IA_64_REL64MSB"; + case 111: return "R_IA_64_REL64LSB"; + case 116: return "R_IA_64_LTV32MSB"; + case 117: return "R_IA_64_LTV32LSB"; + case 118: return "R_IA_64_LTV64MSB"; + case 119: return "R_IA_64_LTV64LSB"; + case 121: return "R_IA_64_PCREL21BI"; + case 122: return "R_IA_64_PCREL22"; + case 123: return "R_IA_64_PCREL64I"; + case 128: return "R_IA_64_IPLTMSB"; + case 129: return "R_IA_64_IPLTLSB"; + case 133: return "R_IA_64_SUB"; + case 134: return "R_IA_64_LTOFF22X"; + case 135: return "R_IA_64_LDXMOV"; + case 145: return "R_IA_64_TPREL14"; + case 146: return "R_IA_64_TPREL22"; + case 147: return "R_IA_64_TPREL64I"; + case 150: return "R_IA_64_TPREL64MSB"; + case 151: return "R_IA_64_TPREL64LSB"; + case 154: return "R_IA_64_LTOFF_TPREL22"; + case 166: return "R_IA_64_DTPMOD64MSB"; + case 167: return "R_IA_64_DTPMOD64LSB"; + case 170: return "R_IA_64_LTOFF_DTPMOD22"; + case 177: return "R_IA_64_DTPREL14"; + case 178: return "R_IA_64_DTPREL22"; + case 179: return "R_IA_64_DTPREL64I"; + case 180: return "R_IA_64_DTPREL32MSB"; + case 181: return "R_IA_64_DTPREL32LSB"; + case 182: return "R_IA_64_DTPREL64MSB"; + case 183: return "R_IA_64_DTPREL64LSB"; + case 186: return "R_IA_64_LTOFF_DTPREL22"; + default: return ""; + } + case EM_MIPS: + switch(type) { + case 0: return "R_MIPS_NONE"; + case 1: return "R_MIPS_16"; + case 2: return "R_MIPS_32"; + case 3: return "R_MIPS_REL32"; + case 4: return "R_MIPS_26"; + case 5: return "R_MIPS_HI16"; + case 6: return "R_MIPS_LO16"; + case 7: return "R_MIPS_GPREL16"; + case 8: return "R_MIPS_LITERAL"; + case 9: return "R_MIPS_GOT16"; + case 10: return "R_MIPS_PC16"; + case 11: return "R_MIPS_CALL16"; + case 12: return "R_MIPS_GPREL32"; + case 21: return "R_MIPS_GOTHI16"; + case 22: return "R_MIPS_GOTLO16"; + case 30: return "R_MIPS_CALLHI16"; + case 31: return "R_MIPS_CALLLO16"; + default: return ""; + } + case EM_PPC: + switch(type) { + case 0: return "R_PPC_NONE"; + case 1: return "R_PPC_ADDR32"; + case 2: return "R_PPC_ADDR24"; + case 3: return "R_PPC_ADDR16"; + case 4: return "R_PPC_ADDR16_LO"; + case 5: return "R_PPC_ADDR16_HI"; + case 6: return "R_PPC_ADDR16_HA"; + case 7: return "R_PPC_ADDR14"; + case 8: return "R_PPC_ADDR14_BRTAKEN"; + case 9: return "R_PPC_ADDR14_BRNTAKEN"; + case 10: return "R_PPC_REL24"; + case 11: return "R_PPC_REL14"; + case 12: return "R_PPC_REL14_BRTAKEN"; + case 13: return "R_PPC_REL14_BRNTAKEN"; + case 14: return "R_PPC_GOT16"; + case 15: return "R_PPC_GOT16_LO"; + case 16: return "R_PPC_GOT16_HI"; + case 17: return "R_PPC_GOT16_HA"; + case 18: return "R_PPC_PLTREL24"; + case 19: return "R_PPC_COPY"; + case 20: return "R_PPC_GLOB_DAT"; + case 21: return "R_PPC_JMP_SLOT"; + case 22: return "R_PPC_RELATIVE"; + case 23: return "R_PPC_LOCAL24PC"; + case 24: return "R_PPC_UADDR32"; + case 25: return "R_PPC_UADDR16"; + case 26: return "R_PPC_REL32"; + case 27: return "R_PPC_PLT32"; + case 28: return "R_PPC_PLTREL32"; + case 29: return "R_PPC_PLT16_LO"; + case 30: return "R_PPC_PLT16_HI"; + case 31: return "R_PPC_PLT16_HA"; + case 32: return "R_PPC_SDAREL16"; + case 33: return "R_PPC_SECTOFF"; + case 34: return "R_PPC_SECTOFF_LO"; + case 35: return "R_PPC_SECTOFF_HI"; + case 36: return "R_PPC_SECTOFF_HA"; + case 67: return "R_PPC_TLS"; + case 68: return "R_PPC_DTPMOD32"; + case 69: return "R_PPC_TPREL16"; + case 70: return "R_PPC_TPREL16_LO"; + case 71: return "R_PPC_TPREL16_HI"; + case 72: return "R_PPC_TPREL16_HA"; + case 73: return "R_PPC_TPREL32"; + case 74: return "R_PPC_DTPREL16"; + case 75: return "R_PPC_DTPREL16_LO"; + case 76: return "R_PPC_DTPREL16_HI"; + case 77: return "R_PPC_DTPREL16_HA"; + case 78: return "R_PPC_DTPREL32"; + case 79: return "R_PPC_GOT_TLSGD16"; + case 80: return "R_PPC_GOT_TLSGD16_LO"; + case 81: return "R_PPC_GOT_TLSGD16_HI"; + case 82: return "R_PPC_GOT_TLSGD16_HA"; + case 83: return "R_PPC_GOT_TLSLD16"; + case 84: return "R_PPC_GOT_TLSLD16_LO"; + case 85: return "R_PPC_GOT_TLSLD16_HI"; + case 86: return "R_PPC_GOT_TLSLD16_HA"; + case 87: return "R_PPC_GOT_TPREL16"; + case 88: return "R_PPC_GOT_TPREL16_LO"; + case 89: return "R_PPC_GOT_TPREL16_HI"; + case 90: return "R_PPC_GOT_TPREL16_HA"; + case 101: return "R_PPC_EMB_NADDR32"; + case 102: return "R_PPC_EMB_NADDR16"; + case 103: return "R_PPC_EMB_NADDR16_LO"; + case 104: return "R_PPC_EMB_NADDR16_HI"; + case 105: return "R_PPC_EMB_NADDR16_HA"; + case 106: return "R_PPC_EMB_SDAI16"; + case 107: return "R_PPC_EMB_SDA2I16"; + case 108: return "R_PPC_EMB_SDA2REL"; + case 109: return "R_PPC_EMB_SDA21"; + case 110: return "R_PPC_EMB_MRKREF"; + case 111: return "R_PPC_EMB_RELSEC16"; + case 112: return "R_PPC_EMB_RELST_LO"; + case 113: return "R_PPC_EMB_RELST_HI"; + case 114: return "R_PPC_EMB_RELST_HA"; + case 115: return "R_PPC_EMB_BIT_FLD"; + case 116: return "R_PPC_EMB_RELSDA"; + default: return ""; + } + case EM_SPARC: + case EM_SPARCV9: + switch(type) { + case 0: return "R_SPARC_NONE"; + case 1: return "R_SPARC_8"; + case 2: return "R_SPARC_16"; + case 3: return "R_SPARC_32"; + case 4: return "R_SPARC_DISP8"; + case 5: return "R_SPARC_DISP16"; + case 6: return "R_SPARC_DISP32"; + case 7: return "R_SPARC_WDISP30"; + case 8: return "R_SPARC_WDISP22"; + case 9: return "R_SPARC_HI22"; + case 10: return "R_SPARC_22"; + case 11: return "R_SPARC_13"; + case 12: return "R_SPARC_LO10"; + case 13: return "R_SPARC_GOT10"; + case 14: return "R_SPARC_GOT13"; + case 15: return "R_SPARC_GOT22"; + case 16: return "R_SPARC_PC10"; + case 17: return "R_SPARC_PC22"; + case 18: return "R_SPARC_WPLT30"; + case 19: return "R_SPARC_COPY"; + case 20: return "R_SPARC_GLOB_DAT"; + case 21: return "R_SPARC_JMP_SLOT"; + case 22: return "R_SPARC_RELATIVE"; + case 23: return "R_SPARC_UA32"; + case 24: return "R_SPARC_PLT32"; + case 25: return "R_SPARC_HIPLT22"; + case 26: return "R_SPARC_LOPLT10"; + case 27: return "R_SPARC_PCPLT32"; + case 28: return "R_SPARC_PCPLT22"; + case 29: return "R_SPARC_PCPLT10"; + case 30: return "R_SPARC_10"; + case 31: return "R_SPARC_11"; + case 32: return "R_SPARC_64"; + case 33: return "R_SPARC_OLO10"; + case 34: return "R_SPARC_HH22"; + case 35: return "R_SPARC_HM10"; + case 36: return "R_SPARC_LM22"; + case 37: return "R_SPARC_PC_HH22"; + case 38: return "R_SPARC_PC_HM10"; + case 39: return "R_SPARC_PC_LM22"; + case 40: return "R_SPARC_WDISP16"; + case 41: return "R_SPARC_WDISP19"; + case 42: return "R_SPARC_GLOB_JMP"; + case 43: return "R_SPARC_7"; + case 44: return "R_SPARC_5"; + case 45: return "R_SPARC_6"; + case 46: return "R_SPARC_DISP64"; + case 47: return "R_SPARC_PLT64"; + case 48: return "R_SPARC_HIX22"; + case 49: return "R_SPARC_LOX10"; + case 50: return "R_SPARC_H44"; + case 51: return "R_SPARC_M44"; + case 52: return "R_SPARC_L44"; + case 53: return "R_SPARC_REGISTER"; + case 54: return "R_SPARC_UA64"; + case 55: return "R_SPARC_UA16"; + case 56: return "R_SPARC_TLS_GD_HI22"; + case 57: return "R_SPARC_TLS_GD_LO10"; + case 58: return "R_SPARC_TLS_GD_ADD"; + case 59: return "R_SPARC_TLS_GD_CALL"; + case 60: return "R_SPARC_TLS_LDM_HI22"; + case 61: return "R_SPARC_TLS_LDM_LO10"; + case 62: return "R_SPARC_TLS_LDM_ADD"; + case 63: return "R_SPARC_TLS_LDM_CALL"; + case 64: return "R_SPARC_TLS_LDO_HIX22"; + case 65: return "R_SPARC_TLS_LDO_LOX10"; + case 66: return "R_SPARC_TLS_LDO_ADD"; + case 67: return "R_SPARC_TLS_IE_HI22"; + case 68: return "R_SPARC_TLS_IE_LO10"; + case 69: return "R_SPARC_TLS_IE_LD"; + case 70: return "R_SPARC_TLS_IE_LDX"; + case 71: return "R_SPARC_TLS_IE_ADD"; + case 72: return "R_SPARC_TLS_LE_HIX22"; + case 73: return "R_SPARC_TLS_LE_LOX10"; + case 74: return "R_SPARC_TLS_DTPMOD32"; + case 75: return "R_SPARC_TLS_DTPMOD64"; + case 76: return "R_SPARC_TLS_DTPOFF32"; + case 77: return "R_SPARC_TLS_DTPOFF64"; + case 78: return "R_SPARC_TLS_TPOFF32"; + case 79: return "R_SPARC_TLS_TPOFF64"; + default: return ""; + } + case EM_X86_64: + switch(type) { + case 0: return "R_X86_64_NONE"; + case 1: return "R_X86_64_64"; + case 2: return "R_X86_64_PC32"; + case 3: return "R_X86_64_GOT32"; + case 4: return "R_X86_64_PLT32"; + case 5: return "R_X86_64_COPY"; + case 6: return "R_X86_64_GLOB_DAT"; + case 7: return "R_X86_64_JMP_SLOT"; + case 8: return "R_X86_64_RELATIVE"; + case 9: return "R_X86_64_GOTPCREL"; + case 10: return "R_X86_64_32"; + case 11: return "R_X86_64_32S"; + case 12: return "R_X86_64_16"; + case 13: return "R_X86_64_PC16"; + case 14: return "R_X86_64_8"; + case 15: return "R_X86_64_PC8"; + case 16: return "R_X86_64_DTPMOD64"; + case 17: return "R_X86_64_DTPOFF64"; + case 18: return "R_X86_64_TPOFF64"; + case 19: return "R_X86_64_TLSGD"; + case 20: return "R_X86_64_TLSLD"; + case 21: return "R_X86_64_DTPOFF32"; + case 22: return "R_X86_64_GOTTPOFF"; + case 23: return "R_X86_64_TPOFF32"; + default: return ""; + } + default: return ""; + } +} + +static void add_name(struct elfdump *ed, const char *name); +static void elf_print_object(struct elfdump *ed); +static void elf_print_elf(struct elfdump *ed); +static void elf_print_ehdr(struct elfdump *ed); +static void elf_print_phdr(struct elfdump *ed); +static void elf_print_shdr(struct elfdump *ed); +static void elf_print_symtab(struct elfdump *ed, int i); +static void elf_print_symtabs(struct elfdump *ed); +static void elf_print_symver(struct elfdump *ed); +static void elf_print_verdef(struct elfdump *ed, struct section *s); +static void elf_print_verneed(struct elfdump *ed, struct section *s); +static void elf_print_interp(struct elfdump *ed); +static void elf_print_dynamic(struct elfdump *ed); +static void elf_print_rel_entry(struct elfdump *ed, struct section *s, + int j, struct rel_entry *r); +static void elf_print_rela(struct elfdump *ed, struct section *s, + Elf_Data *data); +static void elf_print_rel(struct elfdump *ed, struct section *s, + Elf_Data *data); +static void elf_print_reloc(struct elfdump *ed); +static void elf_print_got(struct elfdump *ed); +static void elf_print_got_section(struct elfdump *ed, struct section *s); +static void elf_print_note(struct elfdump *ed); +static void elf_print_svr4_hash(struct elfdump *ed, struct section *s); +static void elf_print_svr4_hash64(struct elfdump *ed, struct section *s); +static void elf_print_gnu_hash(struct elfdump *ed, struct section *s); +static void elf_print_hash(struct elfdump *ed); +static void elf_print_checksum(struct elfdump *ed); +static void find_gotrel(struct elfdump *ed, struct section *gs, + struct rel_entry *got); +static struct spec_name *find_name(struct elfdump *ed, const char *name); +static const char *get_symbol_name(struct elfdump *ed, int symtab, int i); +static const char *get_string(struct elfdump *ed, int strtab, size_t off); +static void get_versym(struct elfdump *ed, int i, uint16_t **vs, int *nvs); +static void load_sections(struct elfdump *ed); +static void unload_sections(struct elfdump *ed); +static void usage(void); +#ifdef USE_LIBARCHIVE_AR +static int ac_detect_ar(int fd); +static void ac_print_ar(struct elfdump *ed, int fd); +#else +static void elf_print_ar(struct elfdump *ed, int fd); +#endif /* USE_LIBARCHIVE_AR */ + +static struct option elfdump_longopts[] = +{ + { "help", no_argument, NULL, 'H' }, + { "version", no_argument, NULL, 'V' }, + { NULL, 0, NULL, 0 } +}; + +int +main(int ac, char **av) +{ + struct elfdump *ed, ed_storage; + struct spec_name *sn; + int ch, i; + + ed = &ed_storage; + memset(ed, 0, sizeof(*ed)); + STAILQ_INIT(&ed->snl); + ed->out = stdout; + while ((ch = getopt_long(ac, av, "acdeiGHhknN:prsSvVw:", + elfdump_longopts, NULL)) != -1) + switch (ch) { + case 'a': + ed->options = ED_ALL; + break; + case 'c': + ed->options |= ED_SHDR; + break; + case 'd': + ed->options |= ED_DYN; + break; + case 'e': + ed->options |= ED_EHDR; + break; + case 'i': + ed->options |= ED_INTERP; + break; + case 'G': + ed->options |= ED_GOT; + break; + case 'h': + ed->options |= ED_HASH; + break; + case 'k': + ed->options |= ED_CHECKSUM; + break; + case 'n': + ed->options |= ED_NOTE; + break; + case 'N': + add_name(ed, optarg); + break; + case 'p': + ed->options |= ED_PHDR; + break; + case 'r': + ed->options |= ED_REL; + break; + case 's': + ed->options |= ED_SYMTAB; + break; + case 'S': + ed->flags |= SOLARIS_FMT; + break; + case 'v': + ed->options |= ED_SYMVER; + break; + case 'V': + (void) printf("%s (%s)\n", ELFTC_GETPROGNAME(), + elftc_version()); + exit(EXIT_SUCCESS); + break; + case 'w': + if ((ed->out = fopen(optarg, "w")) == NULL) + err(EXIT_FAILURE, "%s", optarg); + break; + case '?': + case 'H': + default: + usage(); + } + + ac -= optind; + av += optind; + + if (ed->options == 0) + ed->options = ED_ALL; + sn = NULL; + if (ed->options & ED_SYMTAB && + (STAILQ_EMPTY(&ed->snl) || (sn = find_name(ed, "ARSYM")) != NULL)) { + ed->flags |= PRINT_ARSYM; + if (sn != NULL) { + STAILQ_REMOVE(&ed->snl, sn, spec_name, sn_list); + if (STAILQ_EMPTY(&ed->snl)) + ed->flags |= ONLY_ARSYM; + } + } + if (ac == 0) + usage(); + if (ac > 1) + ed->flags |= PRINT_FILENAME; + if (elf_version(EV_CURRENT) == EV_NONE) + errx(EXIT_FAILURE, "ELF library initialization failed: %s", + elf_errmsg(-1)); + + for (i = 0; i < ac; i++) { + ed->filename = av[i]; + ed->archive = NULL; + elf_print_object(ed); + } + + exit(EXIT_SUCCESS); +} + +#ifdef USE_LIBARCHIVE_AR + +/* Archive symbol table entry. */ +struct arsym_entry { + char *sym_name; + size_t off; +}; + +/* + * Convenient wrapper for general libarchive error handling. + */ +#define AC(CALL) do { \ + if ((CALL)) { \ + warnx("%s", archive_error_string(a)); \ + return; \ + } \ +} while (0) + +/* + * Detect an ar(1) archive using libarchive(3). + */ +static int +ac_detect_ar(int fd) +{ + struct archive *a; + struct archive_entry *entry; + int r; + + r = -1; + if ((a = archive_read_new()) == NULL) + return (0); + archive_read_support_format_ar(a); + if (archive_read_open_fd(a, fd, 10240) == ARCHIVE_OK) + r = archive_read_next_header(a, &entry); + archive_read_close(a); + archive_read_free(a); + + return (r == ARCHIVE_OK); +} + +/* + * Dump an ar(1) archive using libarchive(3). + */ +static void +ac_print_ar(struct elfdump *ed, int fd) +{ + struct archive *a; + struct archive_entry *entry; + struct arsym_entry *arsym; + const char *name; + char idx[10], *b; + void *buff; + size_t size; + uint32_t cnt; + int i, r; + + if (lseek(fd, 0, SEEK_SET) == -1) + err(EXIT_FAILURE, "lseek failed"); + if ((a = archive_read_new()) == NULL) + errx(EXIT_FAILURE, "%s", archive_error_string(a)); + archive_read_support_format_ar(a); + AC(archive_read_open_fd(a, fd, 10240)); + for(;;) { + r = archive_read_next_header(a, &entry); + if (r == ARCHIVE_FATAL) + errx(EXIT_FAILURE, "%s", archive_error_string(a)); + if (r == ARCHIVE_EOF) + break; + if (r == ARCHIVE_WARN || r == ARCHIVE_RETRY) + warnx("%s", archive_error_string(a)); + if (r == ARCHIVE_RETRY) + continue; + name = archive_entry_pathname(entry); + size = archive_entry_size(entry); + if (size == 0) + continue; + if ((buff = malloc(size)) == NULL) { + warn("malloc failed"); + continue; + } + if (archive_read_data(a, buff, size) != (ssize_t)size) { + warnx("%s", archive_error_string(a)); + free(buff); + continue; + } + + /* + * Note that when processing arsym via libarchive, there is + * no way to tell which member a certain symbol belongs to, + * since we can not just "lseek" to a member offset and read + * the member header. + */ + if (!strcmp(name, "/") && ed->flags & PRINT_ARSYM) { + b = buff; + cnt = be32dec(b); + if (cnt == 0) { + free(buff); + continue; + } + arsym = calloc(cnt, sizeof(*arsym)); + if (arsym == NULL) + err(EXIT_FAILURE, "calloc failed"); + b += sizeof(uint32_t); + for (i = 0; (size_t)i < cnt; i++) { + arsym[i].off = be32dec(b); + b += sizeof(uint32_t); + } + for (i = 0; (size_t)i < cnt; i++) { + arsym[i].sym_name = b; + b += strlen(b) + 1; + } + if (ed->flags & SOLARIS_FMT) { + PRT("\nSymbol Table: (archive)\n"); + PRT(" index offset symbol\n"); + } else + PRT("\nsymbol table (archive):\n"); + for (i = 0; (size_t)i < cnt; i++) { + if (ed->flags & SOLARIS_FMT) { + snprintf(idx, sizeof(idx), "[%d]", i); + PRT("%10s ", idx); + PRT("0x%8.8jx ", + (uintmax_t)arsym[i].off); + PRT("%s\n", arsym[i].sym_name); + } else { + PRT("\nentry: %d\n", i); + PRT("\toffset: %#jx\n", + (uintmax_t)arsym[i].off); + PRT("\tsymbol: %s\n", + arsym[i].sym_name); + } + } + free(arsym); + free(buff); + /* No need to continue if we only dump ARSYM. */ + if (ed->flags & ONLY_ARSYM) { + AC(archive_read_close(a)); + AC(archive_read_free(a)); + return; + } + continue; + } + if ((ed->elf = elf_memory(buff, size)) == NULL) { + warnx("elf_memroy() failed: %s", + elf_errmsg(-1)); + free(buff); + continue; + } + /* Skip non-ELF member. */ + if (elf_kind(ed->elf) == ELF_K_ELF) { + printf("\n%s(%s):\n", ed->archive, name); + elf_print_elf(ed); + } + elf_end(ed->elf); + free(buff); + } + AC(archive_read_close(a)); + AC(archive_read_free(a)); +} + +#else /* USE_LIBARCHIVE_AR */ + +/* + * Dump an ar(1) archive. + */ +static void +elf_print_ar(struct elfdump *ed, int fd) +{ + Elf *e; + Elf_Arhdr *arh; + Elf_Arsym *arsym; + Elf_Cmd cmd; + char idx[10]; + size_t cnt; + int i; + + ed->ar = ed->elf; + + if (ed->flags & PRINT_ARSYM) { + cnt = 0; + if ((arsym = elf_getarsym(ed->ar, &cnt)) == NULL) { + warnx("elf_getarsym failed: %s", elf_errmsg(-1)); + goto print_members; + } + if (cnt == 0) + goto print_members; + if (ed->flags & SOLARIS_FMT) { + PRT("\nSymbol Table: (archive)\n"); + PRT(" index offset member name and symbol\n"); + } else + PRT("\nsymbol table (archive):\n"); + for (i = 0; (size_t)i < cnt - 1; i++) { + if (elf_rand(ed->ar, arsym[i].as_off) != + arsym[i].as_off) { + warnx("elf_rand failed: %s", elf_errmsg(-1)); + break; + } + if ((e = elf_begin(fd, ELF_C_READ, ed->ar)) == NULL) { + warnx("elf_begin failed: %s", elf_errmsg(-1)); + break; + } + if ((arh = elf_getarhdr(e)) == NULL) { + warnx("elf_getarhdr failed: %s", + elf_errmsg(-1)); + break; + } + if (ed->flags & SOLARIS_FMT) { + snprintf(idx, sizeof(idx), "[%d]", i); + PRT("%10s ", idx); + PRT("0x%8.8jx ", + (uintmax_t)arsym[i].as_off); + PRT("(%s):%s\n", arh->ar_name, + arsym[i].as_name); + } else { + PRT("\nentry: %d\n", i); + PRT("\toffset: %#jx\n", + (uintmax_t)arsym[i].as_off); + PRT("\tmember: %s\n", arh->ar_name); + PRT("\tsymbol: %s\n", arsym[i].as_name); + } + elf_end(e); + } + + /* No need to continue if we only dump ARSYM. */ + if (ed->flags & ONLY_ARSYM) + return; + } + +print_members: + + /* Rewind the archive. */ + if (elf_rand(ed->ar, SARMAG) != SARMAG) { + warnx("elf_rand failed: %s", elf_errmsg(-1)); + return; + } + + /* Dump each member of the archive. */ + cmd = ELF_C_READ; + while ((ed->elf = elf_begin(fd, cmd, ed->ar)) != NULL) { + /* Skip non-ELF member. */ + if (elf_kind(ed->elf) == ELF_K_ELF) { + if ((arh = elf_getarhdr(ed->elf)) == NULL) { + warnx("elf_getarhdr failed: %s", + elf_errmsg(-1)); + break; + } + printf("\n%s(%s):\n", ed->archive, arh->ar_name); + elf_print_elf(ed); + } + cmd = elf_next(ed->elf); + elf_end(ed->elf); + } +} + +#endif /* USE_LIBARCHIVE_AR */ + +/* + * Dump an object. (ELF object or ar(1) archive) + */ +static void +elf_print_object(struct elfdump *ed) +{ + int fd; + + if ((fd = open(ed->filename, O_RDONLY)) == -1) { + warn("open %s failed", ed->filename); + return; + } + +#ifdef USE_LIBARCHIVE_AR + if (ac_detect_ar(fd)) { + ed->archive = ed->filename; + ac_print_ar(ed, fd); + return; + } +#endif /* USE_LIBARCHIVE_AR */ + + if ((ed->elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) { + warnx("elf_begin() failed: %s", elf_errmsg(-1)); + return; + } + + switch (elf_kind(ed->elf)) { + case ELF_K_NONE: + warnx("Not an ELF file."); + return; + case ELF_K_ELF: + if (ed->flags & PRINT_FILENAME) + printf("\n%s:\n", ed->filename); + elf_print_elf(ed); + break; + case ELF_K_AR: +#ifndef USE_LIBARCHIVE_AR + ed->archive = ed->filename; + elf_print_ar(ed, fd); +#endif + break; + default: + warnx("Internal: libelf returned unknown elf kind."); + return; + } + + elf_end(ed->elf); +} + +/* + * Dump an ELF object. + */ +static void +elf_print_elf(struct elfdump *ed) +{ + + if (gelf_getehdr(ed->elf, &ed->ehdr) == NULL) { + warnx("gelf_getehdr failed: %s", elf_errmsg(-1)); + return; + } + if ((ed->ec = gelf_getclass(ed->elf)) == ELFCLASSNONE) { + warnx("gelf_getclass failed: %s", elf_errmsg(-1)); + return; + } + + if (ed->options & (ED_SHDR | ED_DYN | ED_REL | ED_GOT | ED_SYMTAB | + ED_SYMVER | ED_NOTE | ED_HASH)) + load_sections(ed); + + if (ed->options & ED_EHDR) + elf_print_ehdr(ed); + if (ed->options & ED_PHDR) + elf_print_phdr(ed); + if (ed->options & ED_INTERP) + elf_print_interp(ed); + if (ed->options & ED_SHDR) + elf_print_shdr(ed); + if (ed->options & ED_DYN) + elf_print_dynamic(ed); + if (ed->options & ED_REL) + elf_print_reloc(ed); + if (ed->options & ED_GOT) + elf_print_got(ed); + if (ed->options & ED_SYMTAB) + elf_print_symtabs(ed); + if (ed->options & ED_SYMVER) + elf_print_symver(ed); + if (ed->options & ED_NOTE) + elf_print_note(ed); + if (ed->options & ED_HASH) + elf_print_hash(ed); + if (ed->options & ED_CHECKSUM) + elf_print_checksum(ed); + + unload_sections(ed); +} + +/* + * Read the section headers from ELF object and store them in the + * internal cache. + */ +static void +load_sections(struct elfdump *ed) +{ + struct section *s; + const char *name; + Elf_Scn *scn; + GElf_Shdr sh; + size_t shstrndx, ndx; + int elferr; + + assert(ed->sl == NULL); + + if (!elf_getshnum(ed->elf, &ed->shnum)) { + warnx("elf_getshnum failed: %s", elf_errmsg(-1)); + return; + } + if (ed->shnum == 0) + return; + if ((ed->sl = calloc(ed->shnum, sizeof(*ed->sl))) == NULL) + err(EXIT_FAILURE, "calloc failed"); + if (!elf_getshstrndx(ed->elf, &shstrndx)) { + warnx("elf_getshstrndx failed: %s", elf_errmsg(-1)); + return; + } + if ((scn = elf_getscn(ed->elf, 0)) == NULL) { + warnx("elf_getscn failed: %s", elf_errmsg(-1)); + return; + } + (void) elf_errno(); + do { + if (gelf_getshdr(scn, &sh) == NULL) { + warnx("gelf_getshdr failed: %s", elf_errmsg(-1)); + (void) elf_errno(); + continue; + } + if ((name = elf_strptr(ed->elf, shstrndx, sh.sh_name)) == NULL) { + (void) elf_errno(); + name = "ERROR"; + } + if ((ndx = elf_ndxscn(scn)) == SHN_UNDEF) + if ((elferr = elf_errno()) != 0) { + warnx("elf_ndxscn failed: %s", + elf_errmsg(elferr)); + continue; + } + if (ndx >= ed->shnum) { + warnx("section index of '%s' out of range", name); + continue; + } + s = &ed->sl[ndx]; + s->name = name; + s->scn = scn; + s->off = sh.sh_offset; + s->sz = sh.sh_size; + s->entsize = sh.sh_entsize; + s->align = sh.sh_addralign; + s->type = sh.sh_type; + s->flags = sh.sh_flags; + s->addr = sh.sh_addr; + s->link = sh.sh_link; + s->info = sh.sh_info; + } while ((scn = elf_nextscn(ed->elf, scn)) != NULL); + elferr = elf_errno(); + if (elferr != 0) + warnx("elf_nextscn failed: %s", elf_errmsg(elferr)); +} + +/* + * Release section related resources. + */ +static void +unload_sections(struct elfdump *ed) +{ + if (ed->sl != NULL) { + free(ed->sl); + ed->sl = NULL; + } +} + +/* + * Add a name to the '-N' name list. + */ +static void +add_name(struct elfdump *ed, const char *name) +{ + struct spec_name *sn; + + if (find_name(ed, name)) + return; + if ((sn = malloc(sizeof(*sn))) == NULL) { + warn("malloc failed"); + return; + } + sn->name = name; + STAILQ_INSERT_TAIL(&ed->snl, sn, sn_list); +} + +/* + * Lookup a name in the '-N' name list. + */ +static struct spec_name * +find_name(struct elfdump *ed, const char *name) +{ + struct spec_name *sn; + + STAILQ_FOREACH(sn, &ed->snl, sn_list) { + if (!strcmp(sn->name, name)) + return (sn); + } + + return (NULL); +} + +/* + * Retrieve the name of a symbol using the section index of the symbol + * table and the index of the symbol within that table. + */ +static const char * +get_symbol_name(struct elfdump *ed, int symtab, int i) +{ + static char sname[64]; + struct section *s; + const char *name; + GElf_Sym sym; + Elf_Data *data; + int elferr; + + s = &ed->sl[symtab]; + if (s->type != SHT_SYMTAB && s->type != SHT_DYNSYM) + return (""); + (void) elf_errno(); + if ((data = elf_getdata(s->scn, NULL)) == NULL) { + elferr = elf_errno(); + if (elferr != 0) + warnx("elf_getdata failed: %s", elf_errmsg(elferr)); + return (""); + } + if (gelf_getsym(data, i, &sym) != &sym) + return (""); + if (GELF_ST_TYPE(sym.st_info) == STT_SECTION) { + if (sym.st_shndx < ed->shnum) { + snprintf(sname, sizeof(sname), "%s (section)", + ed->sl[sym.st_shndx].name); + return (sname); + } else + return (""); + } + if ((name = elf_strptr(ed->elf, s->link, sym.st_name)) == NULL) + return (""); + + return (name); +} + +/* + * Retrieve a string using string table section index and the string offset. + */ +static const char* +get_string(struct elfdump *ed, int strtab, size_t off) +{ + const char *name; + + if ((name = elf_strptr(ed->elf, strtab, off)) == NULL) + return (""); + + return (name); +} + +/* + * Dump the ELF Executable Header. + */ +static void +elf_print_ehdr(struct elfdump *ed) +{ + + if (!STAILQ_EMPTY(&ed->snl)) + return; + + if (ed->flags & SOLARIS_FMT) { + PRT("\nELF Header\n"); + PRT(" ei_magic: { %#x, %c, %c, %c }\n", + ed->ehdr.e_ident[0], ed->ehdr.e_ident[1], + ed->ehdr.e_ident[2], ed->ehdr.e_ident[3]); + PRT(" ei_class: %-18s", + ei_classes[ed->ehdr.e_ident[EI_CLASS]]); + PRT(" ei_data: %s\n", ei_data[ed->ehdr.e_ident[EI_DATA]]); + PRT(" e_machine: %-18s", e_machines(ed->ehdr.e_machine)); + PRT(" e_version: %s\n", ei_versions[ed->ehdr.e_version]); + PRT(" e_type: %s\n", e_types[ed->ehdr.e_type]); + PRT(" e_flags: %18d\n", ed->ehdr.e_flags); + PRT(" e_entry: %#18jx", (uintmax_t)ed->ehdr.e_entry); + PRT(" e_ehsize: %6d", ed->ehdr.e_ehsize); + PRT(" e_shstrndx:%5d\n", ed->ehdr.e_shstrndx); + PRT(" e_shoff: %#18jx", (uintmax_t)ed->ehdr.e_shoff); + PRT(" e_shentsize: %3d", ed->ehdr.e_shentsize); + PRT(" e_shnum: %5d\n", ed->ehdr.e_shnum); + PRT(" e_phoff: %#18jx", (uintmax_t)ed->ehdr.e_phoff); + PRT(" e_phentsize: %3d", ed->ehdr.e_phentsize); + PRT(" e_phnum: %5d\n", ed->ehdr.e_phnum); + } else { + PRT("\nelf header:\n"); + PRT("\n"); + PRT("\te_ident: %s %s %s\n", + ei_classes[ed->ehdr.e_ident[EI_CLASS]], + ei_data[ed->ehdr.e_ident[EI_DATA]], + ei_abis[ed->ehdr.e_ident[EI_OSABI]]); + PRT("\te_type: %s\n", e_types[ed->ehdr.e_type]); + PRT("\te_machine: %s\n", e_machines(ed->ehdr.e_machine)); + PRT("\te_version: %s\n", ei_versions[ed->ehdr.e_version]); + PRT("\te_entry: %#jx\n", (uintmax_t)ed->ehdr.e_entry); + PRT("\te_phoff: %ju\n", (uintmax_t)ed->ehdr.e_phoff); + PRT("\te_shoff: %ju\n", (uintmax_t) ed->ehdr.e_shoff); + PRT("\te_flags: %u\n", ed->ehdr.e_flags); + PRT("\te_ehsize: %u\n", ed->ehdr.e_ehsize); + PRT("\te_phentsize: %u\n", ed->ehdr.e_phentsize); + PRT("\te_phnum: %u\n", ed->ehdr.e_phnum); + PRT("\te_shentsize: %u\n", ed->ehdr.e_shentsize); + PRT("\te_shnum: %u\n", ed->ehdr.e_shnum); + PRT("\te_shstrndx: %u\n", ed->ehdr.e_shstrndx); + } +} + +/* + * Dump the ELF Program Header Table. + */ +static void +elf_print_phdr(struct elfdump *ed) +{ + GElf_Phdr ph; + size_t phnum; + int header, i; + + if (elf_getphnum(ed->elf, &phnum) == 0) { + warnx("elf_getphnum failed: %s", elf_errmsg(-1)); + return; + } + header = 0; + for (i = 0; (u_int64_t) i < phnum; i++) { + if (gelf_getphdr(ed->elf, i, &ph) != &ph) { + warnx("elf_getphdr failed: %s", elf_errmsg(-1)); + continue; + } + if (!STAILQ_EMPTY(&ed->snl) && + find_name(ed, p_types[ph.p_type & 0x7]) == NULL) + continue; + if (ed->flags & SOLARIS_FMT) { + PRT("\nProgram Header[%d]:\n", i); + PRT(" p_vaddr: %#-14jx", (uintmax_t)ph.p_vaddr); + PRT(" p_flags: [ %s ]\n", p_flags[ph.p_flags]); + PRT(" p_paddr: %#-14jx", (uintmax_t)ph.p_paddr); + PRT(" p_type: [ %s ]\n", p_types[ph.p_type & 0x7]); + PRT(" p_filesz: %#-14jx", + (uintmax_t)ph.p_filesz); + PRT(" p_memsz: %#jx\n", (uintmax_t)ph.p_memsz); + PRT(" p_offset: %#-14jx", + (uintmax_t)ph.p_offset); + PRT(" p_align: %#jx\n", (uintmax_t)ph.p_align); + } else { + if (!header) { + PRT("\nprogram header:\n"); + header = 1; + } + PRT("\n"); + PRT("entry: %d\n", i); + PRT("\tp_type: %s\n", p_types[ph.p_type & 0x7]); + PRT("\tp_offset: %ju\n", (uintmax_t)ph.p_offset); + PRT("\tp_vaddr: %#jx\n", (uintmax_t)ph.p_vaddr); + PRT("\tp_paddr: %#jx\n", (uintmax_t)ph.p_paddr); + PRT("\tp_filesz: %ju\n", (uintmax_t)ph.p_filesz); + PRT("\tp_memsz: %ju\n", (uintmax_t)ph.p_memsz); + PRT("\tp_flags: %s\n", p_flags[ph.p_flags]); + PRT("\tp_align: %ju\n", (uintmax_t)ph.p_align); + } + } +} + +/* + * Dump the ELF Section Header Table. + */ +static void +elf_print_shdr(struct elfdump *ed) +{ + struct section *s; + int i; + + if (!STAILQ_EMPTY(&ed->snl)) + return; + + if ((ed->flags & SOLARIS_FMT) == 0) + PRT("\nsection header:\n"); + for (i = 0; (size_t)i < ed->shnum; i++) { + s = &ed->sl[i]; + if (ed->flags & SOLARIS_FMT) { + if (i == 0) + continue; + PRT("\nSection Header[%d]:", i); + PRT(" sh_name: %s\n", s->name); + PRT(" sh_addr: %#-14jx", (uintmax_t)s->addr); + if (s->flags != 0) + PRT(" sh_flags: [ %s ]\n", sh_flags(s->flags)); + else + PRT(" sh_flags: 0\n"); + PRT(" sh_size: %#-14jx", (uintmax_t)s->sz); + PRT(" sh_type: [ %s ]\n", sh_types(s->type)); + PRT(" sh_offset: %#-14jx", (uintmax_t)s->off); + PRT(" sh_entsize: %#jx\n", (uintmax_t)s->entsize); + PRT(" sh_link: %-14u", s->link); + PRT(" sh_info: %u\n", s->info); + PRT(" sh_addralign: %#jx\n", (uintmax_t)s->align); + } else { + PRT("\n"); + PRT("entry: %ju\n", (uintmax_t)i); + PRT("\tsh_name: %s\n", s->name); + PRT("\tsh_type: %s\n", sh_types(s->type)); + PRT("\tsh_flags: %s\n", sh_flags(s->flags)); + PRT("\tsh_addr: %#jx\n", (uintmax_t)s->addr); + PRT("\tsh_offset: %ju\n", (uintmax_t)s->off); + PRT("\tsh_size: %ju\n", (uintmax_t)s->sz); + PRT("\tsh_link: %u\n", s->link); + PRT("\tsh_info: %u\n", s->info); + PRT("\tsh_addralign: %ju\n", (uintmax_t)s->align); + PRT("\tsh_entsize: %ju\n", (uintmax_t)s->entsize); + } + } +} + +/* + * Retrieve the content of the corresponding SHT_SUNW_versym section for + * a symbol table section. + */ +static void +get_versym(struct elfdump *ed, int i, uint16_t **vs, int *nvs) +{ + struct section *s; + Elf_Data *data; + int j, elferr; + + s = NULL; + for (j = 0; (size_t)j < ed->shnum; j++) { + s = &ed->sl[j]; + if (s->type == SHT_SUNW_versym && s->link == (uint32_t)i) + break; + } + if ((size_t)j >= ed->shnum) { + *vs = NULL; + return; + } + (void) elf_errno(); + if ((data = elf_getdata(s->scn, NULL)) == NULL) { + elferr = elf_errno(); + if (elferr != 0) + warnx("elf_getdata failed: %s", elf_errmsg(elferr)); + *vs = NULL; + return; + } + + *vs = data->d_buf; + *nvs = data->d_size / s->entsize; +} + +/* + * Dump the symbol table section. + */ +static void +elf_print_symtab(struct elfdump *ed, int i) +{ + struct section *s; + const char *name; + uint16_t *vs; + char idx[10]; + Elf_Data *data; + GElf_Sym sym; + int len, j, elferr, nvs; + + s = &ed->sl[i]; + if (ed->flags & SOLARIS_FMT) + PRT("\nSymbol Table Section: %s\n", s->name); + else + PRT("\nsymbol table (%s):\n", s->name); + (void) elf_errno(); + if ((data = elf_getdata(s->scn, NULL)) == NULL) { + elferr = elf_errno(); + if (elferr != 0) + warnx("elf_getdata failed: %s", elf_errmsg(elferr)); + return; + } + vs = NULL; + nvs = 0; + len = data->d_size / s->entsize; + if (ed->flags & SOLARIS_FMT) { + if (ed->ec == ELFCLASS32) + PRT(" index value "); + else + PRT(" index value "); + PRT("size type bind oth ver shndx name\n"); + get_versym(ed, i, &vs, &nvs); + if (vs != NULL && nvs != len) { + warnx("#symbol not equal to #versym"); + vs = NULL; + } + } + for (j = 0; j < len; j++) { + if (gelf_getsym(data, j, &sym) != &sym) { + warnx("gelf_getsym failed: %s", elf_errmsg(-1)); + continue; + } + name = get_string(ed, s->link, sym.st_name); + if (ed->flags & SOLARIS_FMT) { + snprintf(idx, sizeof(idx), "[%d]", j); + if (ed->ec == ELFCLASS32) + PRT("%10s ", idx); + else + PRT("%10s ", idx); + PRT("0x%8.8jx ", (uintmax_t)sym.st_value); + if (ed->ec == ELFCLASS32) + PRT("0x%8.8jx ", (uintmax_t)sym.st_size); + else + PRT("0x%12.12jx ", (uintmax_t)sym.st_size); + PRT("%s ", st_types_S[GELF_ST_TYPE(sym.st_info)]); + PRT("%s ", st_bindings_S[GELF_ST_BIND(sym.st_info)]); + PRT("%c ", st_others[sym.st_other]); + PRT("%3u ", (vs == NULL ? 0 : vs[j])); + PRT("%-11.11s ", sh_name(ed, sym.st_shndx)); + PRT("%s\n", name); + } else { + PRT("\nentry: %d\n", j); + PRT("\tst_name: %s\n", name); + PRT("\tst_value: %#jx\n", (uintmax_t)sym.st_value); + PRT("\tst_size: %ju\n", (uintmax_t)sym.st_size); + PRT("\tst_info: %s %s\n", + st_types[GELF_ST_TYPE(sym.st_info)], + st_bindings[GELF_ST_BIND(sym.st_info)]); + PRT("\tst_shndx: %ju\n", (uintmax_t)sym.st_shndx); + } + } +} + +/* + * Dump the symbol tables. (.dynsym and .symtab) + */ +static void +elf_print_symtabs(struct elfdump *ed) +{ + int i; + + for (i = 0; (size_t)i < ed->shnum; i++) + if ((ed->sl[i].type == SHT_SYMTAB || + ed->sl[i].type == SHT_DYNSYM) && + (STAILQ_EMPTY(&ed->snl) || find_name(ed, ed->sl[i].name))) + elf_print_symtab(ed, i); +} + +/* + * Dump the content of .dynamic section. + */ +static void +elf_print_dynamic(struct elfdump *ed) +{ + struct section *s; + const char *name; + char idx[10]; + Elf_Data *data; + GElf_Dyn dyn; + int elferr, i, len; + + s = NULL; + for (i = 0; (size_t)i < ed->shnum; i++) { + s = &ed->sl[i]; + if (s->type == SHT_DYNAMIC && + (STAILQ_EMPTY(&ed->snl) || find_name(ed, s->name))) + break; + } + if ((size_t)i >= ed->shnum) + return; + + if (ed->flags & SOLARIS_FMT) { + PRT("Dynamic Section: %s\n", s->name); + PRT(" index tag value\n"); + } else + PRT("\ndynamic:\n"); + (void) elf_errno(); + if ((data = elf_getdata(s->scn, NULL)) == NULL) { + elferr = elf_errno(); + if (elferr != 0) + warnx("elf_getdata failed: %s", elf_errmsg(elferr)); + return; + } + len = data->d_size / s->entsize; + for (i = 0; i < len; i++) { + if (gelf_getdyn(data, i, &dyn) != &dyn) { + warnx("gelf_getdyn failed: %s", elf_errmsg(-1)); + continue; + } + + if (ed->flags & SOLARIS_FMT) { + snprintf(idx, sizeof(idx), "[%d]", i); + PRT("%10s %-16s ", idx, d_tags(dyn.d_tag)); + } else { + PRT("\n"); + PRT("entry: %d\n", i); + PRT("\td_tag: %s\n", d_tags(dyn.d_tag)); + } + switch(dyn.d_tag) { + case DT_NEEDED: + case DT_SONAME: + case DT_RPATH: + if ((name = elf_strptr(ed->elf, s->link, + dyn.d_un.d_val)) == NULL) + name = ""; + if (ed->flags & SOLARIS_FMT) + PRT("%#-16jx %s\n", (uintmax_t)dyn.d_un.d_val, + name); + else + PRT("\td_val: %s\n", name); + break; + case DT_PLTRELSZ: + case DT_RELA: + case DT_RELASZ: + case DT_RELAENT: + case DT_RELACOUNT: + case DT_STRSZ: + case DT_SYMENT: + case DT_RELSZ: + case DT_RELENT: + case DT_PLTREL: + case DT_VERDEF: + case DT_VERDEFNUM: + case DT_VERNEED: + case DT_VERNEEDNUM: + case DT_VERSYM: + if (ed->flags & SOLARIS_FMT) + PRT("%#jx\n", (uintmax_t)dyn.d_un.d_val); + else + PRT("\td_val: %ju\n", + (uintmax_t)dyn.d_un.d_val); + break; + case DT_PLTGOT: + case DT_HASH: + case DT_GNU_HASH: + case DT_STRTAB: + case DT_SYMTAB: + case DT_INIT: + case DT_FINI: + case DT_REL: + case DT_JMPREL: + case DT_DEBUG: + if (ed->flags & SOLARIS_FMT) + PRT("%#jx\n", (uintmax_t)dyn.d_un.d_ptr); + else + PRT("\td_ptr: %#jx\n", + (uintmax_t)dyn.d_un.d_ptr); + break; + case DT_NULL: + case DT_SYMBOLIC: + case DT_TEXTREL: + default: + if (ed->flags & SOLARIS_FMT) + PRT("\n"); + break; + } + } +} + +/* + * Dump a .rel/.rela section entry. + */ +static void +elf_print_rel_entry(struct elfdump *ed, struct section *s, int j, + struct rel_entry *r) +{ + + if (ed->flags & SOLARIS_FMT) { + PRT(" %-23s ", r_type(ed->ehdr.e_machine, + GELF_R_TYPE(r->u_r.rel.r_info))); + PRT("%#12jx ", (uintmax_t)r->u_r.rel.r_offset); + if (r->type == SHT_RELA) + PRT("%10jd ", (intmax_t)r->u_r.rela.r_addend); + else + PRT(" "); + PRT("%-14s ", s->name); + PRT("%s\n", r->symn); + } else { + PRT("\n"); + PRT("entry: %d\n", j); + PRT("\tr_offset: %#jx\n", (uintmax_t)r->u_r.rel.r_offset); + if (ed->ec == ELFCLASS32) + PRT("\tr_info: %#jx\n", (uintmax_t) + ELF32_R_INFO(ELF64_R_SYM(r->u_r.rel.r_info), + ELF64_R_TYPE(r->u_r.rel.r_info))); + else + PRT("\tr_info: %#jx\n", (uintmax_t)r->u_r.rel.r_info); + if (r->type == SHT_RELA) + PRT("\tr_addend: %jd\n", + (intmax_t)r->u_r.rela.r_addend); + } +} + +/* + * Dump a relocation section of type SHT_RELA. + */ +static void +elf_print_rela(struct elfdump *ed, struct section *s, Elf_Data *data) +{ + struct rel_entry r; + int j, len; + + if (ed->flags & SOLARIS_FMT) { + PRT("\nRelocation Section: %s\n", s->name); + PRT(" type offset " + "addend section with respect to\n"); + } else + PRT("\nrelocation with addend (%s):\n", s->name); + r.type = SHT_RELA; + len = data->d_size / s->entsize; + for (j = 0; j < len; j++) { + if (gelf_getrela(data, j, &r.u_r.rela) != &r.u_r.rela) { + warnx("gelf_getrela failed: %s", + elf_errmsg(-1)); + continue; + } + r.symn = get_symbol_name(ed, s->link, + GELF_R_SYM(r.u_r.rela.r_info)); + elf_print_rel_entry(ed, s, j, &r); + } +} + +/* + * Dump a relocation section of type SHT_REL. + */ +static void +elf_print_rel(struct elfdump *ed, struct section *s, Elf_Data *data) +{ + struct rel_entry r; + int j, len; + + if (ed->flags & SOLARIS_FMT) { + PRT("\nRelocation Section: %s\n", s->name); + PRT(" type offset " + "section with respect to\n"); + } else + PRT("\nrelocation (%s):\n", s->name); + r.type = SHT_REL; + len = data->d_size / s->entsize; + for (j = 0; j < len; j++) { + if (gelf_getrel(data, j, &r.u_r.rel) != &r.u_r.rel) { + warnx("gelf_getrel failed: %s", elf_errmsg(-1)); + continue; + } + r.symn = get_symbol_name(ed, s->link, + GELF_R_SYM(r.u_r.rel.r_info)); + elf_print_rel_entry(ed, s, j, &r); + } +} + +/* + * Dump relocation sections. + */ +static void +elf_print_reloc(struct elfdump *ed) +{ + struct section *s; + Elf_Data *data; + int i, elferr; + + for (i = 0; (size_t)i < ed->shnum; i++) { + s = &ed->sl[i]; + if ((s->type == SHT_REL || s->type == SHT_RELA) && + (STAILQ_EMPTY(&ed->snl) || find_name(ed, s->name))) { + (void) elf_errno(); + if ((data = elf_getdata(s->scn, NULL)) == NULL) { + elferr = elf_errno(); + if (elferr != 0) + warnx("elf_getdata failed: %s", + elf_errmsg(elferr)); + continue; + } + if (s->type == SHT_REL) + elf_print_rel(ed, s, data); + else + elf_print_rela(ed, s, data); + } + } +} + +/* + * Dump the content of PT_INTERP segment. + */ +static void +elf_print_interp(struct elfdump *ed) +{ + const char *s; + GElf_Phdr phdr; + size_t phnum; + int i; + + if (!STAILQ_EMPTY(&ed->snl) && find_name(ed, "PT_INTERP") == NULL) + return; + + if ((s = elf_rawfile(ed->elf, NULL)) == NULL) { + warnx("elf_rawfile failed: %s", elf_errmsg(-1)); + return; + } + if (!elf_getphnum(ed->elf, &phnum)) { + warnx("elf_getphnum failed: %s", elf_errmsg(-1)); + return; + } + for (i = 0; (size_t)i < phnum; i++) { + if (gelf_getphdr(ed->elf, i, &phdr) != &phdr) { + warnx("elf_getphdr failed: %s", elf_errmsg(-1)); + continue; + } + if (phdr.p_type == PT_INTERP) { + PRT("\ninterp:\n"); + PRT("\t%s\n", s + phdr.p_offset); + } + } +} + +/* + * Search the relocation sections for entries refering to the .got section. + */ +static void +find_gotrel(struct elfdump *ed, struct section *gs, struct rel_entry *got) +{ + struct section *s; + struct rel_entry r; + Elf_Data *data; + int elferr, i, j, k, len; + + for(i = 0; (size_t)i < ed->shnum; i++) { + s = &ed->sl[i]; + if (s->type != SHT_REL && s->type != SHT_RELA) + continue; + (void) elf_errno(); + if ((data = elf_getdata(s->scn, NULL)) == NULL) { + elferr = elf_errno(); + if (elferr != 0) + warnx("elf_getdata failed: %s", + elf_errmsg(elferr)); + return; + } + memset(&r, 0, sizeof(struct rel_entry)); + r.type = s->type; + len = data->d_size / s->entsize; + for (j = 0; j < len; j++) { + if (s->type == SHT_REL) { + if (gelf_getrel(data, j, &r.u_r.rel) != + &r.u_r.rel) { + warnx("gelf_getrel failed: %s", + elf_errmsg(-1)); + continue; + } + } else { + if (gelf_getrela(data, j, &r.u_r.rela) != + &r.u_r.rela) { + warnx("gelf_getrel failed: %s", + elf_errmsg(-1)); + continue; + } + } + if (r.u_r.rel.r_offset >= gs->addr && + r.u_r.rel.r_offset < gs->addr + gs->sz) { + r.symn = get_symbol_name(ed, s->link, + GELF_R_SYM(r.u_r.rel.r_info)); + k = (r.u_r.rel.r_offset - gs->addr) / + gs->entsize; + memcpy(&got[k], &r, sizeof(struct rel_entry)); + } + } + } +} + +static void +elf_print_got_section(struct elfdump *ed, struct section *s) +{ + struct rel_entry *got; + Elf_Data *data, dst; + int elferr, i, len; + + if (s->entsize == 0) { + /* XXX IA64 GOT section generated by gcc has entry size 0. */ + if (s->align != 0) + s->entsize = s->align; + else + return; + } + + if (ed->flags & SOLARIS_FMT) + PRT("\nGlobal Offset Table Section: %s (%jd entries)\n", + s->name, s->sz / s->entsize); + else + PRT("\nglobal offset table: %s\n", s->name); + (void) elf_errno(); + if ((data = elf_getdata(s->scn, NULL)) == NULL) { + elferr = elf_errno(); + if (elferr != 0) + warnx("elf_getdata failed: %s", elf_errmsg(elferr)); + return; + } + + /* + * GOT section has section type SHT_PROGBITS, thus libelf treats it as + * byte stream and will not perfrom any translation on it. As a result, + * an exlicit call to gelf_xlatetom is needed here. Depends on arch, + * GOT section should be translated to either WORD or XWORD. + */ + if (ed->ec == ELFCLASS32) + data->d_type = ELF_T_WORD; + else + data->d_type = ELF_T_XWORD; + memcpy(&dst, data, sizeof(Elf_Data)); + if (gelf_xlatetom(ed->elf, &dst, data, ed->ehdr.e_ident[EI_DATA]) != + &dst) { + warnx("gelf_xlatetom failed: %s", elf_errmsg(-1)); + return; + } + len = dst.d_size / s->entsize; + if (ed->flags & SOLARIS_FMT) { + /* + * In verbose/Solaris mode, we search the relocation sections + * and try to find the corresponding reloc entry for each GOT + * section entry. + */ + if ((got = calloc(len, sizeof(struct rel_entry))) == NULL) + err(EXIT_FAILURE, "calloc failed"); + find_gotrel(ed, s, got); + if (ed->ec == ELFCLASS32) { + PRT(" ndx addr value reloc "); + PRT("addend symbol\n"); + } else { + PRT(" ndx addr value "); + PRT("reloc addend symbol\n"); + } + for(i = 0; i < len; i++) { + PRT("[%5.5d] ", i); + if (ed->ec == ELFCLASS32) { + PRT("%-8.8jx ", s->addr + i * s->entsize); + PRT("%-8.8x ", *((uint32_t *)dst.d_buf + i)); + } else { + PRT("%-16.16jx ", s->addr + i * s->entsize); + PRT("%-16.16jx ", *((uint64_t *)dst.d_buf + i)); + } + PRT("%-18s ", r_type(ed->ehdr.e_machine, + GELF_R_TYPE(got[i].u_r.rel.r_info))); + if (ed->ec == ELFCLASS32) + PRT("%-8.8jd ", + (intmax_t)got[i].u_r.rela.r_addend); + else + PRT("%-12.12jd ", + (intmax_t)got[i].u_r.rela.r_addend); + if (got[i].symn == NULL) + got[i].symn = ""; + PRT("%s\n", got[i].symn); + } + free(got); + } else { + for(i = 0; i < len; i++) { + PRT("\nentry: %d\n", i); + if (ed->ec == ELFCLASS32) + PRT("\t%#x\n", *((uint32_t *)dst.d_buf + i)); + else + PRT("\t%#jx\n", *((uint64_t *)dst.d_buf + i)); + } + } +} + +/* + * Dump the content of Global Offset Table section. + */ +static void +elf_print_got(struct elfdump *ed) +{ + struct section *s; + int i; + + if (!STAILQ_EMPTY(&ed->snl)) + return; + + s = NULL; + for (i = 0; (size_t)i < ed->shnum; i++) { + s = &ed->sl[i]; + if (s->name && !strncmp(s->name, ".got", 4) && + (STAILQ_EMPTY(&ed->snl) || find_name(ed, s->name))) + elf_print_got_section(ed, s); + } +} + +/* + * Dump the content of .note.ABI-tag section. + */ +static void +elf_print_note(struct elfdump *ed) +{ + struct section *s; + Elf_Data *data; + Elf_Note *en; + uint32_t namesz; + uint32_t descsz; + uint32_t desc; + size_t count; + int elferr, i; + char *src, idx[10]; + + s = NULL; + for (i = 0; (size_t)i < ed->shnum; i++) { + s = &ed->sl[i]; + if (s->type == SHT_NOTE && s->name && + !strcmp(s->name, ".note.ABI-tag") && + (STAILQ_EMPTY(&ed->snl) || find_name(ed, s->name))) + break; + } + if ((size_t)i >= ed->shnum) + return; + if (ed->flags & SOLARIS_FMT) + PRT("\nNote Section: %s\n", s->name); + else + PRT("\nnote (%s):\n", s->name); + (void) elf_errno(); + if ((data = elf_getdata(s->scn, NULL)) == NULL) { + elferr = elf_errno(); + if (elferr != 0) + warnx("elf_getdata failed: %s", elf_errmsg(elferr)); + return; + } + src = data->d_buf; + count = data->d_size; + while (count > sizeof(Elf_Note)) { + en = (Elf_Note *) (uintptr_t) src; + namesz = en->n_namesz; + descsz = en->n_descsz; + src += sizeof(Elf_Note); + count -= sizeof(Elf_Note); + if (ed->flags & SOLARIS_FMT) { + PRT("\n type %#x\n", en->n_type); + PRT(" namesz %#x:\n", en->n_namesz); + PRT("%s\n", src); + } else + PRT("\t%s ", src); + src += roundup2(namesz, 4); + count -= roundup2(namesz, 4); + + /* + * Note that we dump the whole desc part if we're in + * "Solaris mode", while in the normal mode, we only look + * at the first 4 bytes (a 32bit word) of the desc, i.e, + * we assume that it's always a FreeBSD version number. + */ + if (ed->flags & SOLARIS_FMT) { + PRT(" descsz %#x:", en->n_descsz); + for (i = 0; (uint32_t)i < descsz; i++) { + if ((i & 0xF) == 0) { + snprintf(idx, sizeof(idx), "desc[%d]", + i); + PRT("\n %-9s", idx); + } else if ((i & 0x3) == 0) + PRT(" "); + PRT(" %2.2x", src[i]); + } + PRT("\n"); + } else { + if (ed->ehdr.e_ident[EI_DATA] == ELFDATA2MSB) + desc = be32dec(src); + else + desc = le32dec(src); + PRT("%d\n", desc); + } + src += roundup2(descsz, 4); + count -= roundup2(descsz, 4); + } +} + +/* + * Dump a hash table. + */ +static void +elf_print_svr4_hash(struct elfdump *ed, struct section *s) +{ + Elf_Data *data; + uint32_t *buf; + uint32_t *bucket, *chain; + uint32_t nbucket, nchain; + uint32_t *bl, *c, maxl, total; + int i, j, first, elferr; + char idx[10]; + + if (ed->flags & SOLARIS_FMT) + PRT("\nHash Section: %s\n", s->name); + else + PRT("\nhash table (%s):\n", s->name); + (void) elf_errno(); + if ((data = elf_getdata(s->scn, NULL)) == NULL) { + elferr = elf_errno(); + if (elferr != 0) + warnx("elf_getdata failed: %s", + elf_errmsg(elferr)); + return; + } + if (data->d_size < 2 * sizeof(uint32_t)) { + warnx(".hash section too small"); + return; + } + buf = data->d_buf; + nbucket = buf[0]; + nchain = buf[1]; + if (nbucket <= 0 || nchain <= 0) { + warnx("Malformed .hash section"); + return; + } + if (data->d_size != (nbucket + nchain + 2) * sizeof(uint32_t)) { + warnx("Malformed .hash section"); + return; + } + bucket = &buf[2]; + chain = &buf[2 + nbucket]; + + if (ed->flags & SOLARIS_FMT) { + maxl = 0; + if ((bl = calloc(nbucket, sizeof(*bl))) == NULL) + err(EXIT_FAILURE, "calloc failed"); + for (i = 0; (uint32_t)i < nbucket; i++) + for (j = bucket[i]; j > 0 && (uint32_t)j < nchain; + j = chain[j]) + if (++bl[i] > maxl) + maxl = bl[i]; + if ((c = calloc(maxl + 1, sizeof(*c))) == NULL) + err(EXIT_FAILURE, "calloc failed"); + for (i = 0; (uint32_t)i < nbucket; i++) + c[bl[i]]++; + PRT(" bucket symndx name\n"); + for (i = 0; (uint32_t)i < nbucket; i++) { + first = 1; + for (j = bucket[i]; j > 0 && (uint32_t)j < nchain; + j = chain[j]) { + if (first) { + PRT("%10d ", i); + first = 0; + } else + PRT(" "); + snprintf(idx, sizeof(idx), "[%d]", j); + PRT("%-10s ", idx); + PRT("%s\n", get_symbol_name(ed, s->link, j)); + } + } + PRT("\n"); + total = 0; + for (i = 0; (uint32_t)i <= maxl; i++) { + total += c[i] * i; + PRT("%10u buckets contain %8d symbols\n", c[i], i); + } + PRT("%10u buckets %8u symbols (globals)\n", nbucket, + total); + } else { + PRT("\nnbucket: %u\n", nbucket); + PRT("nchain: %u\n\n", nchain); + for (i = 0; (uint32_t)i < nbucket; i++) + PRT("bucket[%d]:\n\t%u\n\n", i, bucket[i]); + for (i = 0; (uint32_t)i < nchain; i++) + PRT("chain[%d]:\n\t%u\n\n", i, chain[i]); + } +} + +/* + * Dump a 64bit hash table. + */ +static void +elf_print_svr4_hash64(struct elfdump *ed, struct section *s) +{ + Elf_Data *data, dst; + uint64_t *buf; + uint64_t *bucket, *chain; + uint64_t nbucket, nchain; + uint64_t *bl, *c, maxl, total; + int i, j, elferr, first; + char idx[10]; + + if (ed->flags & SOLARIS_FMT) + PRT("\nHash Section: %s\n", s->name); + else + PRT("\nhash table (%s):\n", s->name); + + /* + * ALPHA uses 64-bit hash entries. Since libelf assumes that + * .hash section contains only 32-bit entry, an explicit + * gelf_xlatetom is needed here. + */ + (void) elf_errno(); + if ((data = elf_rawdata(s->scn, NULL)) == NULL) { + elferr = elf_errno(); + if (elferr != 0) + warnx("elf_rawdata failed: %s", + elf_errmsg(elferr)); + return; + } + data->d_type = ELF_T_XWORD; + memcpy(&dst, data, sizeof(Elf_Data)); + if (gelf_xlatetom(ed->elf, &dst, data, + ed->ehdr.e_ident[EI_DATA]) != &dst) { + warnx("gelf_xlatetom failed: %s", elf_errmsg(-1)); + return; + } + if (dst.d_size < 2 * sizeof(uint64_t)) { + warnx(".hash section too small"); + return; + } + buf = dst.d_buf; + nbucket = buf[0]; + nchain = buf[1]; + if (nbucket <= 0 || nchain <= 0) { + warnx("Malformed .hash section"); + return; + } + if (dst.d_size != (nbucket + nchain + 2) * sizeof(uint64_t)) { + warnx("Malformed .hash section"); + return; + } + bucket = &buf[2]; + chain = &buf[2 + nbucket]; + + if (ed->flags & SOLARIS_FMT) { + maxl = 0; + if ((bl = calloc(nbucket, sizeof(*bl))) == NULL) + err(EXIT_FAILURE, "calloc failed"); + for (i = 0; (uint64_t)i < nbucket; i++) + for (j = bucket[i]; j > 0 && (uint64_t)j < nchain; + j = chain[j]) + if (++bl[i] > maxl) + maxl = bl[i]; + if ((c = calloc(maxl + 1, sizeof(*c))) == NULL) + err(EXIT_FAILURE, "calloc failed"); + for (i = 0; (uint64_t)i < nbucket; i++) + c[bl[i]]++; + PRT(" bucket symndx name\n"); + for (i = 0; (uint64_t)i < nbucket; i++) { + first = 1; + for (j = bucket[i]; j > 0 && (uint64_t)j < nchain; + j = chain[j]) { + if (first) { + PRT("%10d ", i); + first = 0; + } else + PRT(" "); + snprintf(idx, sizeof(idx), "[%d]", j); + PRT("%-10s ", idx); + PRT("%s\n", get_symbol_name(ed, s->link, j)); + } + } + PRT("\n"); + total = 0; + for (i = 0; (uint64_t)i <= maxl; i++) { + total += c[i] * i; + PRT("%10ju buckets contain %8d symbols\n", + (uintmax_t)c[i], i); + } + PRT("%10ju buckets %8ju symbols (globals)\n", + (uintmax_t)nbucket, (uintmax_t)total); + } else { + PRT("\nnbucket: %ju\n", (uintmax_t)nbucket); + PRT("nchain: %ju\n\n", (uintmax_t)nchain); + for (i = 0; (uint64_t)i < nbucket; i++) + PRT("bucket[%d]:\n\t%ju\n\n", i, (uintmax_t)bucket[i]); + for (i = 0; (uint64_t)i < nchain; i++) + PRT("chain[%d]:\n\t%ju\n\n", i, (uintmax_t)chain[i]); + } + +} + +/* + * Dump a GNU hash table. + */ +static void +elf_print_gnu_hash(struct elfdump *ed, struct section *s) +{ + struct section *ds; + Elf_Data *data; + uint32_t *buf; + uint32_t *bucket, *chain; + uint32_t nbucket, nchain, symndx, maskwords, shift2; + uint32_t *bl, *c, maxl, total; + int i, j, first, elferr, dynsymcount; + char idx[10]; + + if (ed->flags & SOLARIS_FMT) + PRT("\nGNU Hash Section: %s\n", s->name); + else + PRT("\ngnu hash table (%s):\n", s->name); + (void) elf_errno(); + if ((data = elf_getdata(s->scn, NULL)) == NULL) { + elferr = elf_errno(); + if (elferr != 0) + warnx("elf_getdata failed: %s", + elf_errmsg(elferr)); + return; + } + if (data->d_size < 4 * sizeof(uint32_t)) { + warnx(".gnu.hash section too small"); + return; + } + buf = data->d_buf; + nbucket = buf[0]; + symndx = buf[1]; + maskwords = buf[2]; + shift2 = buf[3]; + buf += 4; + ds = &ed->sl[s->link]; + dynsymcount = ds->sz / ds->entsize; + nchain = dynsymcount - symndx; + if (data->d_size != 4 * sizeof(uint32_t) + maskwords * + (ed->ec == ELFCLASS32 ? sizeof(uint32_t) : sizeof(uint64_t)) + + (nbucket + nchain) * sizeof(uint32_t)) { + warnx("Malformed .gnu.hash section"); + return; + } + bucket = buf + (ed->ec == ELFCLASS32 ? maskwords : maskwords * 2); + chain = bucket + nbucket; + + if (ed->flags & SOLARIS_FMT) { + maxl = 0; + if ((bl = calloc(nbucket, sizeof(*bl))) == NULL) + err(EXIT_FAILURE, "calloc failed"); + for (i = 0; (uint32_t)i < nbucket; i++) + for (j = bucket[i]; + j > 0 && (uint32_t)j - symndx < nchain; + j++) { + if (++bl[i] > maxl) + maxl = bl[i]; + if (chain[j - symndx] & 1) + break; + } + if ((c = calloc(maxl + 1, sizeof(*c))) == NULL) + err(EXIT_FAILURE, "calloc failed"); + for (i = 0; (uint32_t)i < nbucket; i++) + c[bl[i]]++; + PRT(" bucket symndx name\n"); + for (i = 0; (uint32_t)i < nbucket; i++) { + first = 1; + for (j = bucket[i]; + j > 0 && (uint32_t)j - symndx < nchain; + j++) { + if (first) { + PRT("%10d ", i); + first = 0; + } else + PRT(" "); + snprintf(idx, sizeof(idx), "[%d]", j ); + PRT("%-10s ", idx); + PRT("%s\n", get_symbol_name(ed, s->link, j)); + if (chain[j - symndx] & 1) + break; + } + } + PRT("\n"); + total = 0; + for (i = 0; (uint32_t)i <= maxl; i++) { + total += c[i] * i; + PRT("%10u buckets contain %8d symbols\n", c[i], i); + } + PRT("%10u buckets %8u symbols (globals)\n", nbucket, + total); + } else { + PRT("\nnbucket: %u\n", nbucket); + PRT("symndx: %u\n", symndx); + PRT("maskwords: %u\n", maskwords); + PRT("shift2: %u\n", shift2); + PRT("nchain: %u\n\n", nchain); + for (i = 0; (uint32_t)i < nbucket; i++) + PRT("bucket[%d]:\n\t%u\n\n", i, bucket[i]); + for (i = 0; (uint32_t)i < nchain; i++) + PRT("chain[%d]:\n\t%u\n\n", i, chain[i]); + } +} + +/* + * Dump hash tables. + */ +static void +elf_print_hash(struct elfdump *ed) +{ + struct section *s; + int i; + + for (i = 0; (size_t)i < ed->shnum; i++) { + s = &ed->sl[i]; + if ((s->type == SHT_HASH || s->type == SHT_GNU_HASH) && + (STAILQ_EMPTY(&ed->snl) || find_name(ed, s->name))) { + if (s->type == SHT_GNU_HASH) + elf_print_gnu_hash(ed, s); + else if (ed->ehdr.e_machine == EM_ALPHA && + s->entsize == 8) + elf_print_svr4_hash64(ed, s); + else + elf_print_svr4_hash(ed, s); + } + } +} + +/* + * Dump the content of a Version Definition(SHT_SUNW_Verdef) Section. + */ +static void +elf_print_verdef(struct elfdump *ed, struct section *s) +{ + Elf_Data *data; + Elf32_Verdef *vd; + Elf32_Verdaux *vda; + const char *str; + char idx[10]; + uint8_t *buf, *end, *buf2; + int i, j, elferr, count; + + if (ed->flags & SOLARIS_FMT) + PRT("Version Definition Section: %s\n", s->name); + else + PRT("\nversion definition section (%s):\n", s->name); + (void) elf_errno(); + if ((data = elf_getdata(s->scn, NULL)) == NULL) { + elferr = elf_errno(); + if (elferr != 0) + warnx("elf_getdata failed: %s", + elf_errmsg(elferr)); + return; + } + buf = data->d_buf; + end = buf + data->d_size; + i = 0; + if (ed->flags & SOLARIS_FMT) + PRT(" index version dependency\n"); + while (buf + sizeof(Elf32_Verdef) <= end) { + vd = (Elf32_Verdef *) (uintptr_t) buf; + if (ed->flags & SOLARIS_FMT) { + snprintf(idx, sizeof(idx), "[%d]", vd->vd_ndx); + PRT("%10s ", idx); + } else { + PRT("\nentry: %d\n", i++); + PRT("\tvd_version: %u\n", vd->vd_version); + PRT("\tvd_flags: %u\n", vd->vd_flags); + PRT("\tvd_ndx: %u\n", vd->vd_ndx); + PRT("\tvd_cnt: %u\n", vd->vd_cnt); + PRT("\tvd_hash: %u\n", vd->vd_hash); + PRT("\tvd_aux: %u\n", vd->vd_aux); + PRT("\tvd_next: %u\n\n", vd->vd_next); + } + buf2 = buf + vd->vd_aux; + j = 0; + count = 0; + while (buf2 + sizeof(Elf32_Verdaux) <= end && j < vd->vd_cnt) { + vda = (Elf32_Verdaux *) (uintptr_t) buf2; + str = get_string(ed, s->link, vda->vda_name); + if (ed->flags & SOLARIS_FMT) { + if (count == 0) + PRT("%-26.26s", str); + else if (count == 1) + PRT(" %-20.20s", str); + else { + PRT("\n%40.40s", ""); + PRT("%s", str); + } + } else { + PRT("\t\tvda: %d\n", j++); + PRT("\t\t\tvda_name: %s\n", str); + PRT("\t\t\tvda_next: %u\n", vda->vda_next); + } + if (vda->vda_next == 0) { + if (ed->flags & SOLARIS_FMT) { + if (vd->vd_flags & VER_FLG_BASE) { + if (count == 0) + PRT("%-20.20s", ""); + PRT("%s", "[ BASE ]"); + } + PRT("\n"); + } + break; + } + if (ed->flags & SOLARIS_FMT) + count++; + buf2 += vda->vda_next; + } + if (vd->vd_next == 0) + break; + buf += vd->vd_next; + } +} + +/* + * Dump the content of a Version Needed(SHT_SUNW_Verneed) Section. + */ +static void +elf_print_verneed(struct elfdump *ed, struct section *s) +{ + Elf_Data *data; + Elf32_Verneed *vn; + Elf32_Vernaux *vna; + uint8_t *buf, *end, *buf2; + int i, j, elferr, first; + + if (ed->flags & SOLARIS_FMT) + PRT("\nVersion Needed Section: %s\n", s->name); + else + PRT("\nversion need section (%s):\n", s->name); + (void) elf_errno(); + if ((data = elf_getdata(s->scn, NULL)) == NULL) { + elferr = elf_errno(); + if (elferr != 0) + warnx("elf_getdata failed: %s", + elf_errmsg(elferr)); + return; + } + buf = data->d_buf; + end = buf + data->d_size; + if (ed->flags & SOLARIS_FMT) + PRT(" file version\n"); + i = 0; + while (buf + sizeof(Elf32_Verneed) <= end) { + vn = (Elf32_Verneed *) (uintptr_t) buf; + if (ed->flags & SOLARIS_FMT) + PRT(" %-26.26s ", + get_string(ed, s->link, vn->vn_file)); + else { + PRT("\nentry: %d\n", i++); + PRT("\tvn_version: %u\n", vn->vn_version); + PRT("\tvn_cnt: %u\n", vn->vn_cnt); + PRT("\tvn_file: %s\n", + get_string(ed, s->link, vn->vn_file)); + PRT("\tvn_aux: %u\n", vn->vn_aux); + PRT("\tvn_next: %u\n\n", vn->vn_next); + } + buf2 = buf + vn->vn_aux; + j = 0; + first = 1; + while (buf2 + sizeof(Elf32_Vernaux) <= end && j < vn->vn_cnt) { + vna = (Elf32_Vernaux *) (uintptr_t) buf2; + if (ed->flags & SOLARIS_FMT) { + if (!first) + PRT("%40.40s", ""); + else + first = 0; + PRT("%s\n", get_string(ed, s->link, + vna->vna_name)); + } else { + PRT("\t\tvna: %d\n", j++); + PRT("\t\t\tvna_hash: %u\n", vna->vna_hash); + PRT("\t\t\tvna_flags: %u\n", vna->vna_flags); + PRT("\t\t\tvna_other: %u\n", vna->vna_other); + PRT("\t\t\tvna_name: %s\n", + get_string(ed, s->link, vna->vna_name)); + PRT("\t\t\tvna_next: %u\n", vna->vna_next); + } + if (vna->vna_next == 0) + break; + buf2 += vna->vna_next; + } + if (vn->vn_next == 0) + break; + buf += vn->vn_next; + } +} + +/* + * Dump the symbol-versioning sections. + */ +static void +elf_print_symver(struct elfdump *ed) +{ + struct section *s; + int i; + + for (i = 0; (size_t)i < ed->shnum; i++) { + s = &ed->sl[i]; + if (!STAILQ_EMPTY(&ed->snl) && !find_name(ed, s->name)) + continue; + if (s->type == SHT_SUNW_verdef) + elf_print_verdef(ed, s); + if (s->type == SHT_SUNW_verneed) + elf_print_verneed(ed, s); + } +} + +/* + * Dump the ELF checksum. See gelf_checksum(3) for details. + */ +static void +elf_print_checksum(struct elfdump *ed) +{ + + if (!STAILQ_EMPTY(&ed->snl)) + return; + + PRT("\nelf checksum: %#lx\n", gelf_checksum(ed->elf)); +} + +#define USAGE_MESSAGE "\ +Usage: %s [options] file...\n\ + Display information about ELF objects and ar(1) archives.\n\n\ + Options:\n\ + -a Show all information.\n\ + -c Show shared headers.\n\ + -d Show dynamic symbols.\n\ + -e Show the ELF header.\n\ + -G Show the GOT.\n\ + -H | --help Show a usage message and exit.\n\ + -h Show hash values.\n\ + -i Show the dynamic interpreter.\n\ + -k Show the ELF checksum.\n\ + -n Show the contents of note sections.\n\ + -N NAME Show the section named \"NAME\".\n\ + -p Show the program header.\n\ + -r Show relocations.\n\ + -s Show the symbol table.\n\ + -S Use the Solaris elfdump format.\n\ + -v Show symbol-versioning information.\n\ + -V | --version Print a version identifier and exit.\n\ + -w FILE Write output to \"FILE\".\n" + +static void +usage(void) +{ + fprintf(stderr, USAGE_MESSAGE, ELFTC_GETPROGNAME()); + exit(EXIT_FAILURE); +} diff --git a/contrib/expat/lib/xmlparse.c b/contrib/expat/lib/xmlparse.c index f35aa36ba8a7..ede7b5bb6673 100644 --- a/contrib/expat/lib/xmlparse.c +++ b/contrib/expat/lib/xmlparse.c @@ -1678,6 +1678,12 @@ XML_ParseBuffer(XML_Parser parser, int len, int isFinal) void * XMLCALL XML_GetBuffer(XML_Parser parser, int len) { +/* BEGIN MOZILLA CHANGE (sanity check len) */ + if (len < 0) { + errorCode = XML_ERROR_NO_MEMORY; + return NULL; + } +/* END MOZILLA CHANGE */ switch (ps_parsing) { case XML_SUSPENDED: errorCode = XML_ERROR_SUSPENDED; @@ -1689,8 +1695,13 @@ XML_GetBuffer(XML_Parser parser, int len) } if (len > bufferLim - bufferEnd) { - /* FIXME avoid integer overflow */ int neededSize = len + (int)(bufferEnd - bufferPtr); +/* BEGIN MOZILLA CHANGE (sanity check neededSize) */ + if (neededSize < 0) { + errorCode = XML_ERROR_NO_MEMORY; + return NULL; + } +/* END MOZILLA CHANGE */ #ifdef XML_CONTEXT_BYTES int keep = (int)(bufferPtr - buffer); @@ -1719,7 +1730,15 @@ XML_GetBuffer(XML_Parser parser, int len) bufferSize = INIT_BUFFER_SIZE; do { bufferSize *= 2; - } while (bufferSize < neededSize); +/* BEGIN MOZILLA CHANGE (prevent infinite loop on overflow) */ + } while (bufferSize < neededSize && bufferSize > 0); +/* END MOZILLA CHANGE */ +/* BEGIN MOZILLA CHANGE (sanity check bufferSize) */ + if (bufferSize <= 0) { + errorCode = XML_ERROR_NO_MEMORY; + return NULL; + } +/* END MOZILLA CHANGE */ newBuf = (char *)MALLOC(bufferSize); if (newBuf == 0) { errorCode = XML_ERROR_NO_MEMORY; diff --git a/contrib/file/ChangeLog b/contrib/file/ChangeLog index 0922fc706f1c..1fb44a26be89 100644 --- a/contrib/file/ChangeLog +++ b/contrib/file/ChangeLog @@ -1,3 +1,7 @@ +2015-07-09 10:35 Christos Zoulas + + * release 5.24 + 2015-06-11 8:52 Christos Zoulas * redo long option encoding to fix off-by-one in 5.23 diff --git a/contrib/file/configure b/contrib/file/configure index e77c3b0ad08f..7a23ea646f43 100755 --- a/contrib/file/configure +++ b/contrib/file/configure @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.69 for file 5.23. +# Generated by GNU Autoconf 2.69 for file 5.24. # # Report bugs to . # @@ -590,8 +590,8 @@ MAKEFLAGS= # Identity of this package. PACKAGE_NAME='file' PACKAGE_TARNAME='file' -PACKAGE_VERSION='5.23' -PACKAGE_STRING='file 5.23' +PACKAGE_VERSION='5.24' +PACKAGE_STRING='file 5.24' PACKAGE_BUGREPORT='christos@astron.com' PACKAGE_URL='' @@ -1327,7 +1327,7 @@ if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures file 5.23 to adapt to many kinds of systems. +\`configure' configures file 5.24 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1397,7 +1397,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of file 5.23:";; + short | recursive ) echo "Configuration of file 5.24:";; esac cat <<\_ACEOF @@ -1507,7 +1507,7 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -file configure 5.23 +file configure 5.24 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. @@ -2163,7 +2163,7 @@ cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by file $as_me 5.23, which was +It was created by file $as_me 5.24, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ @@ -3029,7 +3029,7 @@ fi # Define the identity of the package. PACKAGE='file' - VERSION='5.23' + VERSION='5.24' cat >>confdefs.h <<_ACEOF @@ -15036,7 +15036,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by file $as_me 5.23, which was +This file was extended by file $as_me 5.24, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -15102,7 +15102,7 @@ _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ -file config.status 5.23 +file config.status 5.24 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" diff --git a/contrib/file/configure.ac b/contrib/file/configure.ac index 857b7fe6ec44..a48e445fccdf 100644 --- a/contrib/file/configure.ac +++ b/contrib/file/configure.ac @@ -1,5 +1,5 @@ dnl Process this file with autoconf to produce a configure script. -AC_INIT([file],[5.23],[christos@astron.com]) +AC_INIT([file],[5.24],[christos@astron.com]) AM_INIT_AUTOMAKE([subdir-objects foreign]) m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) diff --git a/contrib/file/magic/Magdir/database b/contrib/file/magic/Magdir/database index b00252bc29f8..7213d76fe6ee 100644 --- a/contrib/file/magic/Magdir/database +++ b/contrib/file/magic/Magdir/database @@ -1,6 +1,6 @@ #------------------------------------------------------------------------------ -# $File: database,v 1.43 2014/10/28 15:47:39 christos Exp $ +# $File: database,v 1.44 2015/07/02 18:25:57 christos Exp $ # database: file(1) magic for various databases # # extracted from header/code files by Graeme Wilford (eep2gw@ee.surrey.ac.uk) @@ -533,7 +533,9 @@ # From: Stephane Blondon http://www.yaal.fr # Database file for Zope (done by FileStorage) -0 string FS21 Zope Object Database File Storage (data) +0 string FS21 Zope Object Database File Storage v3 (data) +0 string FS30 Zope Object Database File Storage v4 (data) + # Cache file for the database of Zope (done by ClientStorage) 0 string ZEC3 Zope Object Database Client Cache File (data) diff --git a/contrib/file/magic/Magdir/elf b/contrib/file/magic/Magdir/elf index 04ee37ed5c7c..1509c44ab7b8 100644 --- a/contrib/file/magic/Magdir/elf +++ b/contrib/file/magic/Magdir/elf @@ -1,6 +1,6 @@ #------------------------------------------------------------------------------ -# $File: elf,v 1.68 2014/09/19 19:05:57 christos Exp $ +# $File: elf,v 1.69 2015/06/16 17:23:08 christos Exp $ # elf: file(1) magic for ELF executables # # We have to check the byte order flag to see what byte order all the @@ -15,6 +15,32 @@ # Modified by (4): (VMS Itanium) # Modified by (5): Matthias Urlichs (Listing of many architectures) +0 name elf-mips +>0 lelong&0xf0000000 0x00000000 MIPS-I +>0 lelong&0xf0000000 0x10000000 MIPS-II +>0 lelong&0xf0000000 0x20000000 MIPS-III +>0 lelong&0xf0000000 0x30000000 MIPS-IV +>0 lelong&0xf0000000 0x40000000 MIPS-V +>0 lelong&0xf0000000 0x50000000 MIPS32 +>0 lelong&0xf0000000 0x60000000 MIPS64 +>0 lelong&0xf0000000 0x70000000 MIPS32 rel2 +>0 lelong&0xf0000000 0x80000000 MIPS64 rel2 +>0 lelong&0xf0000000 0x90000000 MIPS32 rel6 +>0 lelong&0xf0000000 0xa0000000 MIPS64 rel6 + +0 name elf-sparc +>0 lelong&0x00ffff00 0x00000100 V8+ Required, +>0 lelong&0x00ffff00 0x00000200 Sun UltraSPARC1 Extensions Required, +>0 lelong&0x00ffff00 0x00000400 HaL R1 Extensions Required, +>0 lelong&0x00ffff00 0x00000800 Sun UltraSPARC3 Extensions Required, +>0 lelong&0x3 0 total store ordering, +>0 lelong&0x3 1 partial store ordering, +>0 lelong&0x3 2 relaxed memory ordering, + +0 name elf-pa-risc +>2 leshort 0x0214 2.0 +>0 leshort &0x0008 (LP64) + 0 name elf-le >16 leshort 0 no file type, !:mime application/octet-stream @@ -55,47 +81,26 @@ >18 leshort 8 # only for 32-bit >>4 byte 1 ->>>36 lelong&0xf0000000 0x00000000 MIPS-I ->>>36 lelong&0xf0000000 0x10000000 MIPS-II ->>>36 lelong&0xf0000000 0x20000000 MIPS-III ->>>36 lelong&0xf0000000 0x30000000 MIPS-IV ->>>36 lelong&0xf0000000 0x40000000 MIPS-V ->>>36 lelong&0xf0000000 0x50000000 MIPS32 ->>>36 lelong&0xf0000000 0x60000000 MIPS64 ->>>36 lelong&0xf0000000 0x70000000 MIPS32 rel2 ->>>36 lelong&0xf0000000 0x80000000 MIPS64 rel2 +>>>36 use elf-mips # only for 64-bit >>4 byte 2 ->>>48 lelong&0xf0000000 0x00000000 MIPS-I ->>>48 lelong&0xf0000000 0x10000000 MIPS-II ->>>48 lelong&0xf0000000 0x20000000 MIPS-III ->>>48 lelong&0xf0000000 0x30000000 MIPS-IV ->>>48 lelong&0xf0000000 0x40000000 MIPS-V ->>>48 lelong&0xf0000000 0x50000000 MIPS32 ->>>48 lelong&0xf0000000 0x60000000 MIPS64 ->>>48 lelong&0xf0000000 0x70000000 MIPS32 rel2 ->>>48 lelong&0xf0000000 0x80000000 MIPS64 rel2 +>>>48 use elf-mips >18 leshort 9 Amdahl, >18 leshort 10 MIPS (deprecated), >18 leshort 11 RS6000, >18 leshort 15 PA-RISC, # only for 32-bit >>4 byte 1 ->>>38 leshort 0x0214 2.0 ->>>36 leshort &0x0008 (LP64) +>>>36 use elf-pa-risc # only for 64-bit >>4 byte 2 ->>>50 leshort 0x0214 2.0 ->>>48 leshort &0x0008 (LP64) +>>>48 use elf-pa-risc >18 leshort 16 nCUBE, >18 leshort 17 Fujitsu VPP500, >18 leshort 18 SPARC32PLUS, # only for 32-bit >>4 byte 1 ->>>36 lelong&0xffff00 0x000100 V8+ Required, ->>>36 lelong&0xffff00 0x000200 Sun UltraSPARC1 Extensions Required, ->>>36 lelong&0xffff00 0x000400 HaL R1 Extensions Required, ->>>36 lelong&0xffff00 0x000800 Sun UltraSPARC3 Extensions Required, +>>>36 use elf-sparc >18 leshort 19 Intel 80960, >18 leshort 20 PowerPC or cisco 4500, >18 leshort 21 64-bit PowerPC or cisco 7500, @@ -117,12 +122,7 @@ >18 leshort 42 Renesas SH, >18 leshort 43 SPARC V9, >>4 byte 2 ->>>48 lelong&0xffff00 0x000200 Sun UltraSPARC1 Extensions Required, ->>>48 lelong&0xffff00 0x000400 HaL R1 Extensions Required, ->>>48 lelong&0xffff00 0x000800 Sun UltraSPARC3 Extensions Required, ->>>48 lelong&0x3 0 total store ordering, ->>>48 lelong&0x3 1 partial store ordering, ->>>48 lelong&0x3 2 relaxed memory ordering, +>>>48 use elf-sparc >18 leshort 44 Siemens Tricore Embedded Processor, >18 leshort 45 Argonaut RISC Core, Argonaut Technologies Inc., >18 leshort 46 Renesas H8/300, diff --git a/contrib/file/magic/Magdir/fortran b/contrib/file/magic/Magdir/fortran index 921beec39681..826e9123e27c 100644 --- a/contrib/file/magic/Magdir/fortran +++ b/contrib/file/magic/Magdir/fortran @@ -1,7 +1,7 @@ #------------------------------------------------------------------------------ -# $File: fortran,v 1.8 2014/06/03 19:01:34 christos Exp $ +# $File: fortran,v 1.9 2015/06/17 19:55:27 christos Exp $ # FORTRAN source -0 regex/100l \^[Cc][\ \t] FORTRAN program +0 regex/100l \^[Cc][\ \t] FORTRAN program text !:mime text/x-fortran !:strength - 5 diff --git a/contrib/file/magic/Magdir/mail.news b/contrib/file/magic/Magdir/mail.news index 7a8123af149a..a61bc72cb8d9 100644 --- a/contrib/file/magic/Magdir/mail.news +++ b/contrib/file/magic/Magdir/mail.news @@ -1,5 +1,5 @@ #------------------------------------------------------------------------------ -# $File: mail.news,v 1.22 2013/01/04 14:22:07 christos Exp $ +# $File: mail.news,v 1.23 2015/06/29 14:44:26 christos Exp $ # mail.news: file(1) magic for mail and news # # Unfortunately, saved netnews also has From line added in some news software. @@ -41,6 +41,7 @@ # From: Simon Matter 0 string \241\002\213\015skiplist\ file\0\0\0 Cyrus skiplist DB +0 string \241\002\213\015twoskip\ file\0\0\0\0 Cyrus twoskip DB # JAM(mbp) Fidonet message area databases # JHR file diff --git a/contrib/file/magic/Magdir/make b/contrib/file/magic/Magdir/make index ba7630d22dd1..5575686a69c1 100644 --- a/contrib/file/magic/Magdir/make +++ b/contrib/file/magic/Magdir/make @@ -2,14 +2,20 @@ # $File: make,v 1.1 2011/12/08 12:12:46 rrt Exp $ # make: file(1) magic for makefiles # -0 regex \^CFLAGS makefile script text +0 regex/100l \^CFLAGS makefile script text !:mime text/x-makefile -0 regex \^LDFLAGS makefile script text +0 regex/100l \^VPATH makefile script text !:mime text/x-makefile -0 regex \^all: makefile script text +0 regex/100l \^LDFLAGS makefile script text !:mime text/x-makefile -0 regex \^.PRECIOUS makefile script text +0 regex/100l \^all: makefile script text +!:mime text/x-makefile +0 regex/100l \^\.PRECIOUS makefile script text +!:mime text/x-makefile +0 regex/100l \^\.BEGIN BSD makefile script text +!:mime text/x-makefile +0 regex/100l \^\.include BSD makefile script text !:mime text/x-makefile -0 regex \^SUBDIRS automake makefile script text +0 regex/100l \^SUBDIRS automake makefile script text !:mime text/x-makefile diff --git a/contrib/file/magic/Magdir/map b/contrib/file/magic/Magdir/map index d9471fe51b62..5013fa60c542 100644 --- a/contrib/file/magic/Magdir/map +++ b/contrib/file/magic/Magdir/map @@ -1,7 +1,7 @@ #------------------------------------------------------------------------------ -# $File: map,v 1.1 2014/06/03 18:22:25 christos Exp $ +# $File: map,v 1.3 2015/07/09 15:16:41 christos Exp $ # map: file(1) magic for Map data # @@ -9,9 +9,11 @@ 8 string .FIT FIT Map data >15 byte 0 >>35 belong x \b, unit id %d -# 20 years after unix epoch >>39 lelong x \b, serial %u ->>43 ledate/631152000 x \b, %s +# http://pub.ks-and-ks.ne.jp/cycling/edge500_fit.shtml +# 20 years after unix epoch +# TZ=GMT date -d '1989-12-31 0:00' +%s +>>43 leldate+631065600 x \b, %s >>47 leshort x \b, manufacturer %d >>47 leshort 1 \b (garmin) diff --git a/contrib/file/src/file.c b/contrib/file/src/file.c index c700f669763b..44f4ccea5735 100644 --- a/contrib/file/src/file.c +++ b/contrib/file/src/file.c @@ -32,7 +32,7 @@ #include "file.h" #ifndef lint -FILE_RCSID("@(#)$File: file.c,v 1.164 2015/06/03 18:21:24 christos Exp $") +FILE_RCSID("@(#)$File: file.c,v 1.165 2015/06/11 12:52:32 christos Exp $") #endif /* lint */ #include "magic.h" diff --git a/contrib/file/src/funcs.c b/contrib/file/src/funcs.c index dc7bbd182068..16f2c4ef9cd8 100644 --- a/contrib/file/src/funcs.c +++ b/contrib/file/src/funcs.c @@ -27,7 +27,7 @@ #include "file.h" #ifndef lint -FILE_RCSID("@(#)$File: funcs.c,v 1.82 2015/06/03 18:01:20 christos Exp $") +FILE_RCSID("@(#)$File: funcs.c,v 1.83 2015/06/16 14:17:37 christos Exp $") #endif /* lint */ #include "magic.h" @@ -107,8 +107,10 @@ file_error_core(struct magic_set *ms, int error, const char *f, va_list va, if (lineno != 0) { free(ms->o.buf); ms->o.buf = NULL; - file_printf(ms, "line %" SIZE_T_FORMAT "u: ", lineno); + file_printf(ms, "line %" SIZE_T_FORMAT "u:", lineno); } + if (ms->o.buf && *ms->o.buf) + file_printf(ms, " "); file_vprintf(ms, f, va); if (error > 0) file_printf(ms, " (%s)", strerror(error)); diff --git a/contrib/file/src/readelf.c b/contrib/file/src/readelf.c index 55009e80a08f..9631591622ab 100644 --- a/contrib/file/src/readelf.c +++ b/contrib/file/src/readelf.c @@ -27,7 +27,7 @@ #include "file.h" #ifndef lint -FILE_RCSID("@(#)$File: readelf.c,v 1.119 2015/04/09 20:01:41 christos Exp $") +FILE_RCSID("@(#)$File: readelf.c,v 1.120 2015/06/16 14:18:07 christos Exp $") #endif #ifdef BUILTIN_ELF @@ -1048,9 +1048,18 @@ doshn(struct magic_set *ms, int clazz, int swap, int fd, off_t off, int num, break; } + /* Things we can determine when we seek */ switch (xsh_type) { case SHT_NOTE: + if (xsh_size + (uintmax_t)xsh_offset > (uintmax_t)fsize) { + if (file_printf(ms, + ", note offset/size 0x%jx+0x%jx exceeds" + " file size 0x%jx", (uintmax_t)xsh_offset, + (uintmax_t)xsh_size, (uintmax_t)fsize) == -1) + return -1; + return 0; + } if ((nbuf = malloc(xsh_size)) == NULL) { file_error(ms, errno, "Cannot allocate memory" " for note"); diff --git a/contrib/gcc/c-cppbuiltin.c b/contrib/gcc/c-cppbuiltin.c index 6b65cdf55571..511104f2f954 100644 --- a/contrib/gcc/c-cppbuiltin.c +++ b/contrib/gcc/c-cppbuiltin.c @@ -553,7 +553,9 @@ c_cpp_builtins (cpp_reader *pfile) /* Make the choice of the stack protector runtime visible to source code. The macro names and values here were chosen for compatibility with an earlier implementation, i.e. ProPolice. */ - if (flag_stack_protect == 2) + if (flag_stack_protect == 3) + cpp_define (pfile, "__SSP_STRONG__=3"); + else if (flag_stack_protect == 2) cpp_define (pfile, "__SSP_ALL__=2"); else if (flag_stack_protect == 1) cpp_define (pfile, "__SSP__=1"); diff --git a/contrib/gcc/cfgexpand.c b/contrib/gcc/cfgexpand.c index b688917cc72b..ccb534f0e18c 100644 --- a/contrib/gcc/cfgexpand.c +++ b/contrib/gcc/cfgexpand.c @@ -810,6 +810,12 @@ clear_tree_used (tree block) clear_tree_used (t); } +enum { + SPCT_FLAG_DEFAULT = 1, + SPCT_FLAG_ALL = 2, + SPCT_FLAG_STRONG = 3 +}; + /* Examine TYPE and determine a bit mask of the following features. */ #define SPCT_HAS_LARGE_CHAR_ARRAY 1 @@ -879,7 +885,8 @@ stack_protect_decl_phase (tree decl) if (bits & SPCT_HAS_SMALL_CHAR_ARRAY) has_short_buffer = true; - if (flag_stack_protect == 2) + if (flag_stack_protect == SPCT_FLAG_ALL + || flag_stack_protect == SPCT_FLAG_STRONG) { if ((bits & (SPCT_HAS_SMALL_CHAR_ARRAY | SPCT_HAS_LARGE_CHAR_ARRAY)) && !(bits & SPCT_HAS_AGGREGATE)) @@ -947,12 +954,36 @@ create_stack_guard (void) cfun->stack_protect_guard = guard; } +/* Helper routine to check if a record or union contains an array field. */ + +static int +record_or_union_type_has_array_p (tree tree_type) +{ + tree fields = TYPE_FIELDS (tree_type); + tree f; + + for (f = fields; f; f = TREE_CHAIN (f)) + if (TREE_CODE (f) == FIELD_DECL) + { + tree field_type = TREE_TYPE (f); + if ((TREE_CODE (field_type) == RECORD_TYPE + || TREE_CODE (field_type) == UNION_TYPE + || TREE_CODE (field_type) == QUAL_UNION_TYPE) + && record_or_union_type_has_array_p (field_type)) + return 1; + if (TREE_CODE (field_type) == ARRAY_TYPE) + return 1; + } + return 0; +} + /* Expand all variables used in the function. */ static void expand_used_vars (void) { tree t, outer_block = DECL_INITIAL (current_function_decl); + bool gen_stack_protect_signal = false; /* Compute the phase of the stack frame for this function. */ { @@ -972,6 +1003,29 @@ expand_used_vars (void) has_protected_decls = false; has_short_buffer = false; + if (flag_stack_protect == SPCT_FLAG_STRONG) + for (t = cfun->unexpanded_var_list; t; t = TREE_CHAIN (t)) + { + tree var = TREE_VALUE (t); + if (!is_global_var (var)) + { + tree var_type = TREE_TYPE (var); + /* Examine local referenced variables that have their addresses + * taken, contain an array, or are arrays. */ + if (TREE_CODE (var) == VAR_DECL + && (TREE_CODE (var_type) == ARRAY_TYPE + || TREE_ADDRESSABLE (var) + || ((TREE_CODE (var_type) == RECORD_TYPE + || TREE_CODE (var_type) == UNION_TYPE + || TREE_CODE (var_type) == QUAL_UNION_TYPE) + && record_or_union_type_has_array_p (var_type)))) + { + gen_stack_protect_signal = true; + break; + } + } + } + /* At this point all variables on the unexpanded_var_list with TREE_USED set are not associated with any block scope. Lay them out. */ for (t = cfun->unexpanded_var_list; t; t = TREE_CHAIN (t)) @@ -1032,12 +1086,26 @@ expand_used_vars (void) dump_stack_var_partition (); } - /* There are several conditions under which we should create a - stack guard: protect-all, alloca used, protected decls present. */ - if (flag_stack_protect == 2 - || (flag_stack_protect - && (current_function_calls_alloca || has_protected_decls))) - create_stack_guard (); + switch (flag_stack_protect) + { + case SPCT_FLAG_ALL: + create_stack_guard (); + break; + + case SPCT_FLAG_STRONG: + if (gen_stack_protect_signal + || current_function_calls_alloca || has_protected_decls) + create_stack_guard (); + break; + + case SPCT_FLAG_DEFAULT: + if (current_function_calls_alloca || has_protected_decls) + create_stack_guard(); + break; + + default: + ; + } /* Assign rtl to each variable based on these partitions. */ if (stack_vars_num > 0) diff --git a/contrib/gcc/common.opt b/contrib/gcc/common.opt index 7dd3909aa5b9..c185d1f6a45a 100644 --- a/contrib/gcc/common.opt +++ b/contrib/gcc/common.opt @@ -878,6 +878,10 @@ fstack-protector-all Common Report RejectNegative Var(flag_stack_protect, 2) VarExists Use a stack protection method for every function +fstack-protector-strong +Common Report RejectNegative Var(flag_stack_protect, 3) +Use a smart stack protection method for certain functions + fstrength-reduce Common Does nothing. Preserved for backward compatibility. diff --git a/contrib/gcc/doc/cpp.texi b/contrib/gcc/doc/cpp.texi index 7c21c56e4c2a..26bc6b70080c 100644 --- a/contrib/gcc/doc/cpp.texi +++ b/contrib/gcc/doc/cpp.texi @@ -2134,6 +2134,10 @@ use. This macro is defined, with value 2, when @option{-fstack-protector-all} is in use. +@item __SSP_STRONG__ +This macro is defined, with value 3, when @option{-fstack-protector-strong} is +in use. + @item __TIMESTAMP__ This macro expands to a string constant that describes the date and time of the last modification of the current source file. The string constant diff --git a/contrib/gcc/doc/gcc.1 b/contrib/gcc/doc/gcc.1 index 1879d76955cc..8afd6aa317f6 100644 --- a/contrib/gcc/doc/gcc.1 +++ b/contrib/gcc/doc/gcc.1 @@ -339,7 +339,7 @@ in the following sections. \&\fB\-fsched2\-use\-superblocks \&\-fsched2\-use\-traces \-fsee \-freschedule\-modulo\-scheduled\-loops \&\-fsection\-anchors \-fsignaling\-nans \-fsingle\-precision\-constant -\&\-fstack\-protector \-fstack\-protector\-all +\&\-fstack\-protector \-fstack\-protector\-all \-fstack\-protector\-strong \&\-fstrict\-aliasing \-fstrict\-overflow \-ftracer \-fthread\-jumps \&\-funroll\-all\-loops \-funroll\-loops \-fpeel\-loops \&\-fsplit\-ivs\-in\-unroller \-funswitch\-loops @@ -5193,6 +5193,11 @@ If a guard check fails, an error message is printed and the program exits. .IP "\fB\-fstack\-protector\-all\fR" 4 .IX Item "-fstack-protector-all" Like \fB\-fstack\-protector\fR except that all functions are protected. +.IP "\fB\-fstack\-protector\-strong\fR" 4 +.IX Item "-fstack-protector-strong" +Like \fB\-fstack\-protector\fR but includes additional functions to +be protected \-\-\- those that have local array definitions, or have +references to local frame addresses. .IP "\fB\-fsection\-anchors\fR" 4 .IX Item "-fsection-anchors" Try to reduce the number of symbolic address calculations by using diff --git a/contrib/gcc/doc/invoke.texi b/contrib/gcc/doc/invoke.texi index 4779bbd1f765..2bf48c3cb8aa 100644 --- a/contrib/gcc/doc/invoke.texi +++ b/contrib/gcc/doc/invoke.texi @@ -331,7 +331,7 @@ in the following sections. -fsched2-use-superblocks @gol -fsched2-use-traces -fsee -freschedule-modulo-scheduled-loops @gol -fsection-anchors -fsignaling-nans -fsingle-precision-constant @gol --fstack-protector -fstack-protector-all @gol +-fstack-protector -fstack-protector-all -fstack-protector-strong @gol -fstrict-aliasing -fstrict-overflow -ftracer -fthread-jumps @gol -funroll-all-loops -funroll-loops -fpeel-loops @gol -fsplit-ivs-in-unroller -funswitch-loops @gol @@ -5810,6 +5810,11 @@ If a guard check fails, an error message is printed and the program exits. @item -fstack-protector-all Like @option{-fstack-protector} except that all functions are protected. +@item -fstack-protector-strong +Like @option{-fstack-protector} but includes additional functions to +be protected --- those that have local array definitions, or have +references to local frame addresses. + @item -fsection-anchors @opindex fsection-anchors Try to reduce the number of symbolic address calculations by using diff --git a/contrib/gcc/gcc.c b/contrib/gcc/gcc.c index 5ed3a82727f3..91e750add240 100644 --- a/contrib/gcc/gcc.c +++ b/contrib/gcc/gcc.c @@ -680,7 +680,7 @@ proper position among the other output files. */ #ifdef TARGET_LIBC_PROVIDES_SSP #define LINK_SSP_SPEC "%{fstack-protector:}" #else -#define LINK_SSP_SPEC "%{fstack-protector|fstack-protector-all:-lssp_nonshared -lssp}" +#define LINK_SSP_SPEC "%{fstack-protector|fstack-protector-strong|fstack-protector-all:-lssp_nonshared -lssp}" #endif #endif diff --git a/contrib/gcclibs/libcpp/files.c b/contrib/gcclibs/libcpp/files.c index 366d30ab0e3e..95cda0e6e07f 100644 --- a/contrib/gcclibs/libcpp/files.c +++ b/contrib/gcclibs/libcpp/files.c @@ -567,7 +567,7 @@ read_file_guts (cpp_reader *pfile, _cpp_file *file) SSIZE_MAX to be much smaller than the actual range of the type. Use INTTYPE_MAXIMUM unconditionally to ensure this does not bite us. */ - if (file->st.st_size > INTTYPE_MAXIMUM (ssize_t)) + if (file->st.st_size > SSIZE_MAX) { cpp_error (pfile, CPP_DL_ERROR, "%s is too large", file->path); return false; @@ -581,7 +581,7 @@ read_file_guts (cpp_reader *pfile, _cpp_file *file) file->path); return false; } - else if (offset > INTTYPE_MAXIMUM (ssize_t) || (ssize_t)offset > size) + else if (offset > SSIZE_MAX || (ssize_t)offset > size) { cpp_error (pfile, CPP_DL_ERROR, "current position of %s is too large", file->path); diff --git a/contrib/hyperv/tools/hv_kvp_daemon.c b/contrib/hyperv/tools/hv_kvp_daemon.c index 1c31d3f9b626..50ae3ed74558 100644 --- a/contrib/hyperv/tools/hv_kvp_daemon.c +++ b/contrib/hyperv/tools/hv_kvp_daemon.c @@ -811,7 +811,7 @@ kvp_get_ip_info(int family, char *if_name, int op, int error = 0; char *buffer; size_t buffer_length; - struct hv_kvp_ipaddr_value *ip_buffer; + struct hv_kvp_ipaddr_value *ip_buffer = NULL; char cidr_mask[5]; int weight; int i; diff --git a/contrib/jemalloc/COPYING b/contrib/jemalloc/COPYING index bdda0feb9e5d..611968cda508 100644 --- a/contrib/jemalloc/COPYING +++ b/contrib/jemalloc/COPYING @@ -1,10 +1,10 @@ Unless otherwise specified, files in the jemalloc source distribution are subject to the following license: -------------------------------------------------------------------------------- -Copyright (C) 2002-2014 Jason Evans . +Copyright (C) 2002-2015 Jason Evans . All rights reserved. Copyright (C) 2007-2012 Mozilla Foundation. All rights reserved. -Copyright (C) 2009-2014 Facebook, Inc. All rights reserved. +Copyright (C) 2009-2015 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: diff --git a/contrib/jemalloc/ChangeLog b/contrib/jemalloc/ChangeLog index d56ee999e69c..0cf887c2344a 100644 --- a/contrib/jemalloc/ChangeLog +++ b/contrib/jemalloc/ChangeLog @@ -1,10 +1,166 @@ Following are change highlights associated with official releases. Important -bug fixes are all mentioned, but internal enhancements are omitted here for -brevity (even though they are more fun to write about). Much more detail can be -found in the git revision history: +bug fixes are all mentioned, but some internal enhancements are omitted here for +brevity. Much more detail can be found in the git revision history: https://github.com/jemalloc/jemalloc +* 4.0.0 (August 17, 2015) + + This version contains many speed and space optimizations, both minor and + major. The major themes are generalization, unification, and simplification. + Although many of these optimizations cause no visible behavior change, their + cumulative effect is substantial. + + New features: + - Normalize size class spacing to be consistent across the complete size + range. By default there are four size classes per size doubling, but this + is now configurable via the --with-lg-size-class-group option. Also add the + --with-lg-page, --with-lg-page-sizes, --with-lg-quantum, and + --with-lg-tiny-min options, which can be used to tweak page and size class + settings. Impacts: + + Worst case performance for incrementally growing/shrinking reallocation + is improved because there are far fewer size classes, and therefore + copying happens less often. + + Internal fragmentation is limited to 20% for all but the smallest size + classes (those less than four times the quantum). (1B + 4 KiB) + and (1B + 4 MiB) previously suffered nearly 50% internal fragmentation. + + Chunk fragmentation tends to be lower because there are fewer distinct run + sizes to pack. + - Add support for explicit tcaches. The "tcache.create", "tcache.flush", and + "tcache.destroy" mallctls control tcache lifetime and flushing, and the + MALLOCX_TCACHE(tc) and MALLOCX_TCACHE_NONE flags to the *allocx() API + control which tcache is used for each operation. + - Implement per thread heap profiling, as well as the ability to + enable/disable heap profiling on a per thread basis. Add the "prof.reset", + "prof.lg_sample", "thread.prof.name", "thread.prof.active", + "opt.prof_thread_active_init", "prof.thread_active_init", and + "thread.prof.active" mallctls. + - Add support for per arena application-specified chunk allocators, configured + via the "arena..chunk_hooks" mallctl. + - Refactor huge allocation to be managed by arenas, so that arenas now + function as general purpose independent allocators. This is important in + the context of user-specified chunk allocators, aside from the scalability + benefits. Related new statistics: + + The "stats.arenas..huge.allocated", "stats.arenas..huge.nmalloc", + "stats.arenas..huge.ndalloc", and "stats.arenas..huge.nrequests" + mallctls provide high level per arena huge allocation statistics. + + The "arenas.nhchunks", "arenas.hchunk..size", + "stats.arenas..hchunks..nmalloc", + "stats.arenas..hchunks..ndalloc", + "stats.arenas..hchunks..nrequests", and + "stats.arenas..hchunks..curhchunks" mallctls provide per size class + statistics. + - Add the 'util' column to malloc_stats_print() output, which reports the + proportion of available regions that are currently in use for each small + size class. + - Add "alloc" and "free" modes for for junk filling (see the "opt.junk" + mallctl), so that it is possible to separately enable junk filling for + allocation versus deallocation. + - Add the jemalloc-config script, which provides information about how + jemalloc was configured, and how to integrate it into application builds. + - Add metadata statistics, which are accessible via the "stats.metadata", + "stats.arenas..metadata.mapped", and + "stats.arenas..metadata.allocated" mallctls. + - Add the "stats.resident" mallctl, which reports the upper limit of + physically resident memory mapped by the allocator. + - Add per arena control over unused dirty page purging, via the + "arenas.lg_dirty_mult", "arena..lg_dirty_mult", and + "stats.arenas..lg_dirty_mult" mallctls. + - Add the "prof.gdump" mallctl, which makes it possible to toggle the gdump + feature on/off during program execution. + - Add sdallocx(), which implements sized deallocation. The primary + optimization over dallocx() is the removal of a metadata read, which often + suffers an L1 cache miss. + - Add missing header includes in jemalloc/jemalloc.h, so that applications + only have to #include . + - Add support for additional platforms: + + Bitrig + + Cygwin + + DragonFlyBSD + + iOS + + OpenBSD + + OpenRISC/or1k + + Optimizations: + - Maintain dirty runs in per arena LRUs rather than in per arena trees of + dirty-run-containing chunks. In practice this change significantly reduces + dirty page purging volume. + - Integrate whole chunks into the unused dirty page purging machinery. This + reduces the cost of repeated huge allocation/deallocation, because it + effectively introduces a cache of chunks. + - Split the arena chunk map into two separate arrays, in order to increase + cache locality for the frequently accessed bits. + - Move small run metadata out of runs, into arena chunk headers. This reduces + run fragmentation, smaller runs reduce external fragmentation for small size + classes, and packed (less uniformly aligned) metadata layout improves CPU + cache set distribution. + - Randomly distribute large allocation base pointer alignment relative to page + boundaries in order to more uniformly utilize CPU cache sets. This can be + disabled via the --disable-cache-oblivious configure option, and queried via + the "config.cache_oblivious" mallctl. + - Micro-optimize the fast paths for the public API functions. + - Refactor thread-specific data to reside in a single structure. This assures + that only a single TLS read is necessary per call into the public API. + - Implement in-place huge allocation growing and shrinking. + - Refactor rtree (radix tree for chunk lookups) to be lock-free, and make + additional optimizations that reduce maximum lookup depth to one or two + levels. This resolves what was a concurrency bottleneck for per arena huge + allocation, because a global data structure is critical for determining + which arenas own which huge allocations. + + Incompatible changes: + - Replace --enable-cc-silence with --disable-cc-silence to suppress spurious + warnings by default. + - Assure that the constness of malloc_usable_size()'s return type matches that + of the system implementation. + - Change the heap profile dump format to support per thread heap profiling, + rename pprof to jeprof, and enhance it with the --thread= option. As a + result, the bundled jeprof must now be used rather than the upstream + (gperftools) pprof. + - Disable "opt.prof_final" by default, in order to avoid atexit(3), which can + internally deadlock on some platforms. + - Change the "arenas.nlruns" mallctl type from size_t to unsigned. + - Replace the "stats.arenas..bins..allocated" mallctl with + "stats.arenas..bins..curregs". + - Ignore MALLOC_CONF in set{uid,gid,cap} binaries. + - Ignore MALLOCX_ARENA(a) in dallocx(), in favor of using the + MALLOCX_TCACHE(tc) and MALLOCX_TCACHE_NONE flags to control tcache usage. + + Removed features: + - Remove the *allocm() API, which is superseded by the *allocx() API. + - Remove the --enable-dss options, and make dss non-optional on all platforms + which support sbrk(2). + - Remove the "arenas.purge" mallctl, which was obsoleted by the + "arena..purge" mallctl in 3.1.0. + - Remove the unnecessary "opt.valgrind" mallctl; jemalloc automatically + detects whether it is running inside Valgrind. + - Remove the "stats.huge.allocated", "stats.huge.nmalloc", and + "stats.huge.ndalloc" mallctls. + - Remove the --enable-mremap option. + - Remove the "stats.chunks.current", "stats.chunks.total", and + "stats.chunks.high" mallctls. + + Bug fixes: + - Fix the cactive statistic to decrease (rather than increase) when active + memory decreases. This regression was first released in 3.5.0. + - Fix OOM handling in memalign() and valloc(). A variant of this bug existed + in all releases since 2.0.0, which introduced these functions. + - Fix an OOM-related regression in arena_tcache_fill_small(), which could + cause cache corruption on OOM. This regression was present in all releases + from 2.2.0 through 3.6.0. + - Fix size class overflow handling for malloc(), posix_memalign(), memalign(), + calloc(), and realloc() when profiling is enabled. + - Fix the "arena..dss" mallctl to return an error if "primary" or + "secondary" precedence is specified, but sbrk(2) is not supported. + - Fix fallback lg_floor() implementations to handle extremely large inputs. + - Ensure the default purgeable zone is after the default zone on OS X. + - Fix latent bugs in atomic_*(). + - Fix the "arena..dss" mallctl to handle read-only calls. + - Fix tls_model configuration to enable the initial-exec model when possible. + - Mark malloc_conf as a weak symbol so that the application can override it. + - Correctly detect glibc's adaptive pthread mutexes. + - Fix the --without-export configure option. + * 3.6.0 (March 31, 2014) This version contains a critical bug fix for a regression present in 3.5.0 and @@ -21,7 +177,7 @@ found in the git revision history: backtracing to be reliable. - Use dss allocation precedence for huge allocations as well as small/large allocations. - - Fix test assertion failure message formatting. This bug did not manifect on + - Fix test assertion failure message formatting. This bug did not manifest on x86_64 systems because of implementation subtleties in va_list. - Fix inconsequential test failures for hash and SFMT code. @@ -516,7 +672,7 @@ found in the git revision history: - Make it possible for the application to manually flush a thread's cache, via the "tcache.flush" mallctl. - Base maximum dirty page count on proportion of active memory. - - Compute various addtional run-time statistics, including per size class + - Compute various additional run-time statistics, including per size class statistics for large objects. - Expose malloc_stats_print(), which can be called repeatedly by the application. diff --git a/contrib/jemalloc/FREEBSD-Xlist b/contrib/jemalloc/FREEBSD-Xlist index 8655754d198d..f0903484cff0 100644 --- a/contrib/jemalloc/FREEBSD-Xlist +++ b/contrib/jemalloc/FREEBSD-Xlist @@ -1,6 +1,6 @@ $FreeBSD$ -.git -.gitignore +.autom4te.cfg +.git* FREEBSD-* INSTALL Makefile* @@ -40,7 +40,10 @@ include/jemalloc/jemalloc_protos.h include/jemalloc/jemalloc_protos.h.in include/jemalloc/jemalloc_rename.h include/jemalloc/jemalloc_rename.sh +include/jemalloc/jemalloc_typedefs.h.in include/msvc_compat/ install-sh +jemalloc.pc* +src/valgrind.c src/zone.c test/ diff --git a/contrib/jemalloc/FREEBSD-diffs b/contrib/jemalloc/FREEBSD-diffs index c8cc9c1656ba..7d6955d773b2 100644 --- a/contrib/jemalloc/FREEBSD-diffs +++ b/contrib/jemalloc/FREEBSD-diffs @@ -1,15 +1,14 @@ diff --git a/doc/jemalloc.xml.in b/doc/jemalloc.xml.in -index d8e2e71..330ba2a 100644 +index 8fc774b..fdbef95 100644 --- a/doc/jemalloc.xml.in +++ b/doc/jemalloc.xml.in -@@ -57,12 +57,23 @@ +@@ -53,11 +53,23 @@ This manual describes jemalloc @jemalloc_version@. More information can be found at the jemalloc website. + + The following configuration options are enabled in libc's built-in -+ jemalloc: , -+ , , ++ jemalloc: , + , , + , , + , , and @@ -17,17 +16,18 @@ index d8e2e71..330ba2a 100644 + is enabled in development versions of + FreeBSD (controlled by the MALLOC_PRODUCTION make + variable). ++ SYNOPSIS - #include <stdlib.h> --#include <jemalloc/jemalloc.h> +- #include <jemalloc/jemalloc.h> ++ #include <stdlib.h> +#include <malloc_np.h> Standard API -@@ -2342,4 +2353,19 @@ malloc_conf = "lg_chunk:24";]]> +@@ -2759,4 +2771,18 @@ malloc_conf = "lg_chunk:24";]]> The posix_memalign function conforms to IEEE Std 1003.1-2001 (“POSIX.1”). @@ -38,9 +38,8 @@ index d8e2e71..330ba2a 100644 + FreeBSD 7.0. + + The aligned_alloc, -+ malloc_stats_print, -+ mallctl*, and -+ *allocm functions first appeared in ++ malloc_stats_print, and ++ mallctl* functions first appeared in + FreeBSD 10.0. + + The *allocx functions first appeared @@ -48,20 +47,11 @@ index d8e2e71..330ba2a 100644 + diff --git a/include/jemalloc/internal/jemalloc_internal.h.in b/include/jemalloc/internal/jemalloc_internal.h.in -index 574bbb1..e3eafdf 100644 +index 7a137b6..b0001e9 100644 --- a/include/jemalloc/internal/jemalloc_internal.h.in +++ b/include/jemalloc/internal/jemalloc_internal.h.in -@@ -1,5 +1,8 @@ - #ifndef JEMALLOC_INTERNAL_H - #define JEMALLOC_INTERNAL_H -+#include "libc_private.h" -+#include "namespace.h" -+ - #include - #ifdef _WIN32 - # include -@@ -65,6 +68,9 @@ typedef intptr_t ssize_t; - #include +@@ -8,6 +8,9 @@ + #include #endif +#include "un-namespace.h" @@ -70,7 +60,7 @@ index 574bbb1..e3eafdf 100644 #define JEMALLOC_NO_DEMANGLE #ifdef JEMALLOC_JET # define JEMALLOC_N(n) jet_##n -@@ -99,13 +105,7 @@ static const bool config_fill = +@@ -42,13 +45,7 @@ static const bool config_fill = false #endif ; @@ -85,11 +75,25 @@ index 574bbb1..e3eafdf 100644 static const bool config_prof = #ifdef JEMALLOC_PROF true +diff --git a/include/jemalloc/internal/jemalloc_internal_decls.h b/include/jemalloc/internal/jemalloc_internal_decls.h +index a601d6e..e7094b2 100644 +--- a/include/jemalloc/internal/jemalloc_internal_decls.h ++++ b/include/jemalloc/internal/jemalloc_internal_decls.h +@@ -1,6 +1,9 @@ + #ifndef JEMALLOC_INTERNAL_DECLS_H + #define JEMALLOC_INTERNAL_DECLS_H + ++#include "libc_private.h" ++#include "namespace.h" ++ + #include + #ifdef _WIN32 + # include diff --git a/include/jemalloc/internal/mutex.h b/include/jemalloc/internal/mutex.h -index de44e14..564d604 100644 +index f051f29..561378f 100644 --- a/include/jemalloc/internal/mutex.h +++ b/include/jemalloc/internal/mutex.h -@@ -43,9 +43,6 @@ struct malloc_mutex_s { +@@ -47,15 +47,13 @@ struct malloc_mutex_s { #ifdef JEMALLOC_LAZY_LOCK extern bool isthreaded; @@ -99,24 +103,31 @@ index de44e14..564d604 100644 #endif bool malloc_mutex_init(malloc_mutex_t *mutex); + void malloc_mutex_prefork(malloc_mutex_t *mutex); + void malloc_mutex_postfork_parent(malloc_mutex_t *mutex); + void malloc_mutex_postfork_child(malloc_mutex_t *mutex); ++bool malloc_mutex_first_thread(void); + bool mutex_boot(void); + + #endif /* JEMALLOC_H_EXTERNS */ diff --git a/include/jemalloc/internal/private_symbols.txt b/include/jemalloc/internal/private_symbols.txt -index 93516d2..22f9af9 100644 +index dbf6aa7..f87dba8 100644 --- a/include/jemalloc/internal/private_symbols.txt +++ b/include/jemalloc/internal/private_symbols.txt -@@ -226,7 +226,6 @@ iralloc - iralloct - iralloct_realign +@@ -277,7 +277,6 @@ iralloct_realign isalloc + isdalloct + isqalloc -isthreaded ivsalloc ixalloc jemalloc_postfork_child diff --git a/include/jemalloc/jemalloc_FreeBSD.h b/include/jemalloc/jemalloc_FreeBSD.h new file mode 100644 -index 0000000..94554bc +index 0000000..737542e --- /dev/null +++ b/include/jemalloc/jemalloc_FreeBSD.h -@@ -0,0 +1,134 @@ +@@ -0,0 +1,142 @@ +/* + * Override settings that were generated in jemalloc_defs.h as necessary. + */ @@ -131,7 +142,6 @@ index 0000000..94554bc + * The following are architecture-dependent, so conditionally define them for + * each supported architecture. + */ -+#undef CPU_SPINWAIT +#undef JEMALLOC_TLS_MODEL +#undef STATIC_PAGE_SHIFT +#undef LG_SIZEOF_PTR @@ -141,7 +151,6 @@ index 0000000..94554bc + +#ifdef __i386__ +# define LG_SIZEOF_PTR 2 -+# define CPU_SPINWAIT __asm__ volatile("pause") +# define JEMALLOC_TLS_MODEL __attribute__((tls_model("initial-exec"))) +#endif +#ifdef __ia64__ @@ -153,12 +162,14 @@ index 0000000..94554bc +#endif +#ifdef __amd64__ +# define LG_SIZEOF_PTR 3 -+# define CPU_SPINWAIT __asm__ volatile("pause") +# define JEMALLOC_TLS_MODEL __attribute__((tls_model("initial-exec"))) +#endif +#ifdef __arm__ +# define LG_SIZEOF_PTR 2 +#endif ++#ifdef __aarch64__ ++# define LG_SIZEOF_PTR 3 ++#endif +#ifdef __mips__ +#ifdef __mips_n64 +# define LG_SIZEOF_PTR 3 @@ -181,6 +192,11 @@ index 0000000..94554bc +#define LG_SIZEOF_LONG LG_SIZEOF_PTR +#define LG_SIZEOF_INTMAX_T 3 + ++#undef CPU_SPINWAIT ++#include ++#include ++#define CPU_SPINWAIT cpu_spinwait() ++ +/* Disable lazy-lock machinery, mangle isthreaded, and adjust its type. */ +#undef JEMALLOC_LAZY_LOCK +extern int __isthreaded; @@ -192,6 +208,7 @@ index 0000000..94554bc +#undef je_realloc +#undef je_free +#undef je_posix_memalign ++#undef je_aligned_alloc +#undef je_malloc_usable_size +#undef je_mallocx +#undef je_rallocx @@ -209,6 +226,7 @@ index 0000000..94554bc +#define je_realloc __realloc +#define je_free __free +#define je_posix_memalign __posix_memalign ++#define je_aligned_alloc __aligned_alloc +#define je_malloc_usable_size __malloc_usable_size +#define je_mallocx __mallocx +#define je_rallocx __rallocx @@ -238,6 +256,7 @@ index 0000000..94554bc +__weak_reference(__realloc, realloc); +__weak_reference(__free, free); +__weak_reference(__posix_memalign, posix_memalign); ++__weak_reference(__aligned_alloc, aligned_alloc); +__weak_reference(__malloc_usable_size, malloc_usable_size); +__weak_reference(__mallocx, mallocx); +__weak_reference(__rallocx, rallocx); @@ -263,32 +282,142 @@ index f943891..47d032c 100755 +#include "jemalloc_FreeBSD.h" EOF diff --git a/src/jemalloc.c b/src/jemalloc.c -index 204778b..9e5f2df 100644 +index ed7863b..d078a1f 100644 --- a/src/jemalloc.c +++ b/src/jemalloc.c -@@ -8,6 +8,10 @@ malloc_tsd_data(, arenas, arena_t *, NULL) - malloc_tsd_data(, thread_allocated, thread_allocated_t, - THREAD_ALLOCATED_INITIALIZER) +@@ -4,6 +4,10 @@ + /******************************************************************************/ + /* Data. */ +/* Work around : */ +const char *__malloc_options_1_0 = NULL; +__sym_compat(_malloc_options, __malloc_options_1_0, FBSD_1.0); + /* Runtime configuration options. */ - const char *je_malloc_conf; + const char *je_malloc_conf JEMALLOC_ATTR(weak); bool opt_abort = -@@ -457,7 +461,8 @@ malloc_conf_init(void) - #endif - ; +@@ -2475,6 +2479,107 @@ je_malloc_usable_size(JEMALLOC_USABLE_SIZE_CONST void *ptr) + */ + /******************************************************************************/ + /* ++ * Begin compatibility functions. ++ */ ++ ++#define ALLOCM_LG_ALIGN(la) (la) ++#define ALLOCM_ALIGN(a) (ffsl(a)-1) ++#define ALLOCM_ZERO ((int)0x40) ++#define ALLOCM_NO_MOVE ((int)0x80) ++ ++#define ALLOCM_SUCCESS 0 ++#define ALLOCM_ERR_OOM 1 ++#define ALLOCM_ERR_NOT_MOVED 2 ++ ++int ++je_allocm(void **ptr, size_t *rsize, size_t size, int flags) ++{ ++ void *p; ++ ++ assert(ptr != NULL); ++ ++ p = je_mallocx(size, flags); ++ if (p == NULL) ++ return (ALLOCM_ERR_OOM); ++ if (rsize != NULL) ++ *rsize = isalloc(p, config_prof); ++ *ptr = p; ++ return (ALLOCM_SUCCESS); ++} ++ ++int ++je_rallocm(void **ptr, size_t *rsize, size_t size, size_t extra, int flags) ++{ ++ int ret; ++ bool no_move = flags & ALLOCM_NO_MOVE; ++ ++ assert(ptr != NULL); ++ assert(*ptr != NULL); ++ assert(size != 0); ++ assert(SIZE_T_MAX - size >= extra); ++ ++ if (no_move) { ++ size_t usize = je_xallocx(*ptr, size, extra, flags); ++ ret = (usize >= size) ? ALLOCM_SUCCESS : ALLOCM_ERR_NOT_MOVED; ++ if (rsize != NULL) ++ *rsize = usize; ++ } else { ++ void *p = je_rallocx(*ptr, size+extra, flags); ++ if (p != NULL) { ++ *ptr = p; ++ ret = ALLOCM_SUCCESS; ++ } else ++ ret = ALLOCM_ERR_OOM; ++ if (rsize != NULL) ++ *rsize = isalloc(*ptr, config_prof); ++ } ++ return (ret); ++} ++ ++int ++je_sallocm(const void *ptr, size_t *rsize, int flags) ++{ ++ ++ assert(rsize != NULL); ++ *rsize = je_sallocx(ptr, flags); ++ return (ALLOCM_SUCCESS); ++} ++ ++int ++je_dallocm(void *ptr, int flags) ++{ ++ ++ je_dallocx(ptr, flags); ++ return (ALLOCM_SUCCESS); ++} ++ ++int ++je_nallocm(size_t *rsize, size_t size, int flags) ++{ ++ size_t usize; ++ ++ usize = je_nallocx(size, flags); ++ if (usize == 0) ++ return (ALLOCM_ERR_OOM); ++ if (rsize != NULL) ++ *rsize = usize; ++ return (ALLOCM_SUCCESS); ++} ++ ++#undef ALLOCM_LG_ALIGN ++#undef ALLOCM_ALIGN ++#undef ALLOCM_ZERO ++#undef ALLOCM_NO_MOVE ++ ++#undef ALLOCM_SUCCESS ++#undef ALLOCM_ERR_OOM ++#undef ALLOCM_ERR_NOT_MOVED ++ ++/* ++ * End compatibility functions. ++ */ ++/******************************************************************************/ ++/* + * The following functions are used by threading libraries for protection of + * malloc during fork(). + */ +@@ -2575,4 +2680,11 @@ jemalloc_postfork_child(void) + ctl_postfork_child(); + } -- if ((opts = getenv(envname)) != NULL) { -+ if (issetugid() == 0 && (opts = getenv(envname)) != -+ NULL) { - /* - * Do nothing; opts is already initialized to - * the value of the MALLOC_CONF environment ++void ++_malloc_first_thread(void) ++{ ++ ++ (void)malloc_mutex_first_thread(); ++} ++ + /******************************************************************************/ diff --git a/src/mutex.c b/src/mutex.c -index 788eca3..6f5954e 100644 +index 2d47af9..934d5aa 100644 --- a/src/mutex.c +++ b/src/mutex.c @@ -66,6 +66,17 @@ pthread_create(pthread_t *__restrict thread, @@ -296,21 +425,45 @@ index 788eca3..6f5954e 100644 JEMALLOC_EXPORT int _pthread_mutex_init_calloc_cb(pthread_mutex_t *mutex, void *(calloc_cb)(size_t, size_t)); + -+__weak_reference(_pthread_mutex_init_calloc_cb_stub, -+ _pthread_mutex_init_calloc_cb); -+ ++#pragma weak _pthread_mutex_init_calloc_cb +int -+_pthread_mutex_init_calloc_cb_stub(pthread_mutex_t *mutex, ++_pthread_mutex_init_calloc_cb(pthread_mutex_t *mutex, + void *(calloc_cb)(size_t, size_t)) +{ + -+ return (0); ++ return (((int (*)(pthread_mutex_t *, void *(*)(size_t, size_t))) ++ __libc_interposing[INTERPOS__pthread_mutex_init_calloc_cb])(mutex, ++ calloc_cb)); +} #endif bool +@@ -137,7 +148,7 @@ malloc_mutex_postfork_child(malloc_mutex_t *mutex) + } + + bool +-mutex_boot(void) ++malloc_mutex_first_thread(void) + { + + #ifdef JEMALLOC_MUTEX_INIT_CB +@@ -151,3 +162,14 @@ mutex_boot(void) + #endif + return (false); + } ++ ++bool ++mutex_boot(void) ++{ ++ ++#ifndef JEMALLOC_MUTEX_INIT_CB ++ return (malloc_mutex_first_thread()); ++#else ++ return (false); ++#endif ++} diff --git a/src/util.c b/src/util.c -index 93a19fd..70b3e45 100644 +index 4cb0d6c..25b61c2 100644 --- a/src/util.c +++ b/src/util.c @@ -58,6 +58,22 @@ wrtmessage(void *cbopaque, const char *s) diff --git a/contrib/jemalloc/FREEBSD-upgrade b/contrib/jemalloc/FREEBSD-upgrade index 6407ff1300f7..6ee6cc91e42e 100755 --- a/contrib/jemalloc/FREEBSD-upgrade +++ b/contrib/jemalloc/FREEBSD-upgrade @@ -72,15 +72,21 @@ do_extract() { patch -p1 < "${src}/FREEBSD-diffs" find . -name '*.orig' -delete # Generate various files. - ./autogen.sh --enable-cc-silence --enable-dss --enable-xmalloc \ - --enable-utrace --with-xslroot=/usr/local/share/xsl/docbook \ - --with-private-namespace=__ + ./autogen.sh --enable-cc-silence --enable-xmalloc --enable-utrace \ + --with-xslroot=/usr/local/share/xsl/docbook --with-private-namespace=__ \ + --with-lg-page-sizes=12,13,14,16 gmake dist ) } do_diff() { - (cd ${work}; git add -A; git diff --cached) > FREEBSD-diffs + ( + cd ${work} + find . -name '*.orig' -delete + find . -name '*.rej' -delete + git add -A + git diff --cached + ) > FREEBSD-diffs } command=$1 diff --git a/contrib/jemalloc/VERSION b/contrib/jemalloc/VERSION index dace31ba7b6a..6802c56546d6 100644 --- a/contrib/jemalloc/VERSION +++ b/contrib/jemalloc/VERSION @@ -1 +1 @@ -3.6.0-0-g46c0af68bd248b04df75e4f92d5fb804c3d75340 +4.0.0-0-g6e98caf8f064482b9ab292ef3638dea67420bbc2 diff --git a/contrib/jemalloc/doc/jemalloc.3 b/contrib/jemalloc/doc/jemalloc.3 index db43d648e738..2afb8628c65a 100644 --- a/contrib/jemalloc/doc/jemalloc.3 +++ b/contrib/jemalloc/doc/jemalloc.3 @@ -2,12 +2,12 @@ .\" Title: JEMALLOC .\" Author: Jason Evans .\" Generator: DocBook XSL Stylesheets v1.76.1 -.\" Date: 03/31/2014 +.\" Date: 08/18/2015 .\" Manual: User Manual -.\" Source: jemalloc 3.6.0-0-g46c0af68bd248b04df75e4f92d5fb804c3d75340 +.\" Source: jemalloc 4.0.0-0-g6e98caf8f064482b9ab292ef3638dea67420bbc2 .\" Language: English .\" -.TH "JEMALLOC" "3" "03/31/2014" "jemalloc 3.6.0-0-g46c0af68bd24" "User Manual" +.TH "JEMALLOC" "3" "08/18/2015" "jemalloc 4.0.0-0-g6e98caf8f064" "User Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- @@ -31,12 +31,10 @@ jemalloc \- general purpose memory allocation functions .SH "LIBRARY" .PP -This manual describes jemalloc 3\&.6\&.0\-0\-g46c0af68bd248b04df75e4f92d5fb804c3d75340\&. More information can be found at the +This manual describes jemalloc 4\&.0\&.0\-0\-g6e98caf8f064482b9ab292ef3638dea67420bbc2\&. More information can be found at the \m[blue]\fBjemalloc website\fR\m[]\&\s-2\u[1]\d\s+2\&. .PP The following configuration options are enabled in libc\*(Aqs built\-in jemalloc: -\fB\-\-enable\-dss\fR, -\fB\-\-enable\-experimental\fR, \fB\-\-enable\-fill\fR, \fB\-\-enable\-lazy\-lock\fR, \fB\-\-enable\-munmap\fR, @@ -81,6 +79,8 @@ make variable)\&. .BI "size_t sallocx(void\ *" "ptr" ", int\ " "flags" ");" .HP \w'void\ dallocx('u .BI "void dallocx(void\ *" "ptr" ", int\ " "flags" ");" +.HP \w'void\ sdallocx('u +.BI "void sdallocx(void\ *" "ptr" ", size_t\ " "size" ", int\ " "flags" ");" .HP \w'size_t\ nallocx('u .BI "size_t nallocx(size_t\ " "size" ", int\ " "flags" ");" .HP \w'int\ mallctl('u @@ -97,17 +97,6 @@ make variable)\&. .BI "void (*malloc_message)(void\ *" "cbopaque" ", const\ char\ *" "s" ");" .PP const char *\fImalloc_conf\fR; -.SS "Experimental API" -.HP \w'int\ allocm('u -.BI "int allocm(void\ **" "ptr" ", size_t\ *" "rsize" ", size_t\ " "size" ", int\ " "flags" ");" -.HP \w'int\ rallocm('u -.BI "int rallocm(void\ **" "ptr" ", size_t\ *" "rsize" ", size_t\ " "size" ", size_t\ " "extra" ", int\ " "flags" ");" -.HP \w'int\ sallocm('u -.BI "int sallocm(const\ void\ *" "ptr" ", size_t\ *" "rsize" ", int\ " "flags" ");" -.HP \w'int\ dallocm('u -.BI "int dallocm(void\ *" "ptr" ", int\ " "flags" ");" -.HP \w'int\ nallocm('u -.BI "int nallocm(size_t\ *" "rsize" ", size_t\ " "size" ", int\ " "flags" ");" .SH "DESCRIPTION" .SS "Standard API" .PP @@ -134,7 +123,7 @@ The \fBposix_memalign\fR\fB\fR function allocates \fIsize\fR -bytes of memory such that the allocation\*(Aqs base address is an even multiple of +bytes of memory such that the allocation\*(Aqs base address is a multiple of \fIalignment\fR, and returns the allocation in the value pointed to by \fIptr\fR\&. The requested \fIalignment\fR @@ -145,7 +134,7 @@ The \fBaligned_alloc\fR\fB\fR function allocates \fIsize\fR -bytes of memory such that the allocation\*(Aqs base address is an even multiple of +bytes of memory such that the allocation\*(Aqs base address is a multiple of \fIalignment\fR\&. The requested \fIalignment\fR must be a power of 2\&. Behavior is undefined if @@ -188,7 +177,8 @@ The \fBrallocx\fR\fB\fR, \fBxallocx\fR\fB\fR, \fBsallocx\fR\fB\fR, -\fBdallocx\fR\fB\fR, and +\fBdallocx\fR\fB\fR, +\fBsdallocx\fR\fB\fR, and \fBnallocx\fR\fB\fR functions all have a \fIflags\fR @@ -217,11 +207,32 @@ is a power of 2\&. Initialize newly allocated memory to contain zero bytes\&. In the growing reallocation case, the real size prior to reallocation defines the boundary between untouched bytes and those that are initialized to contain zero bytes\&. If this macro is absent, newly allocated memory is uninitialized\&. .RE .PP +\fBMALLOCX_TCACHE(\fR\fB\fItc\fR\fR\fB) \fR +.RS 4 +Use the thread\-specific cache (tcache) specified by the identifier +\fItc\fR, which must have been acquired via the +"tcache\&.create" +mallctl\&. This macro does not validate that +\fItc\fR +specifies a valid identifier\&. +.RE +.PP +\fBMALLOCX_TCACHE_NONE\fR +.RS 4 +Do not use a thread\-specific cache (tcache)\&. Unless +\fBMALLOCX_TCACHE(\fR\fB\fItc\fR\fR\fB)\fR +or +\fBMALLOCX_TCACHE_NONE\fR +is specified, an automatically managed tcache will be used under many circumstances\&. This macro cannot be used in the same +\fIflags\fR +argument as +\fBMALLOCX_TCACHE(\fR\fB\fItc\fR\fR\fB)\fR\&. +.RE +.PP \fBMALLOCX_ARENA(\fR\fB\fIa\fR\fR\fB) \fR .RS 4 Use the arena specified by the index -\fIa\fR -(and by necessity bypass the thread cache)\&. This macro has no effect for huge regions, nor for regions that were allocated via an arena other than the one specified\&. This macro does not validate that +\fIa\fR\&. This macro has no effect for regions that were allocated via an arena other than the one specified\&. This macro does not validate that \fIa\fR specifies an arena index in the valid range\&. .RE @@ -274,6 +285,17 @@ function causes the memory referenced by to be made available for future allocations\&. .PP The +\fBsdallocx\fR\fB\fR +function is an extension of +\fBdallocx\fR\fB\fR +with a +\fIsize\fR +parameter to allow the caller to pass in the allocation size as an optimization\&. The minimum valid input size is the original requested size of the allocation, and the maximum valid input size is the corresponding value returned by +\fBnallocx\fR\fB\fR +or +\fBsallocx\fR\fB\fR\&. +.PP +The \fBnallocx\fR\fB\fR function allocates no memory, but it performs the same size computation as the \fBmallocx\fR\fB\fR @@ -367,7 +389,7 @@ uses the \fBmallctl*\fR\fB\fR functions internally, so inconsistent statistics can be reported if multiple threads use these functions simultaneously\&. If \fB\-\-enable\-stats\fR -is specified during configuration, \(lqm\(rq and \(lqa\(rq can be specified to omit merged arena and per arena statistics, respectively; \(lqb\(rq and \(lql\(rq can be specified to omit per size class statistics for bins and large objects, respectively\&. Unrecognized characters are silently ignored\&. Note that thread caching may prevent some statistics from being completely up to date, since extra locking would be required to merge counters that track thread cache operations\&. +is specified during configuration, \(lqm\(rq and \(lqa\(rq can be specified to omit merged arena and per arena statistics, respectively; \(lqb\(rq, \(lql\(rq, and \(lqh\(rq can be specified to omit per size class statistics for bins, large objects, and huge objects, respectively\&. Unrecognized characters are silently ignored\&. Note that thread caching may prevent some statistics from being completely up to date, since extra locking would be required to merge counters that track thread cache operations\&. .PP The \fBmalloc_usable_size\fR\fB\fR @@ -378,126 +400,6 @@ function is not a mechanism for in\-place \fBrealloc\fR\fB\fR; rather it is provided solely as a tool for introspection purposes\&. Any discrepancy between the requested allocation size and the size reported by \fBmalloc_usable_size\fR\fB\fR should not be depended on, since such behavior is entirely implementation\-dependent\&. -.SS "Experimental API" -.PP -The experimental API is subject to change or removal without regard for backward compatibility\&. If -\fB\-\-disable\-experimental\fR -is specified during configuration, the experimental API is omitted\&. -.PP -The -\fBallocm\fR\fB\fR, -\fBrallocm\fR\fB\fR, -\fBsallocm\fR\fB\fR, -\fBdallocm\fR\fB\fR, and -\fBnallocm\fR\fB\fR -functions all have a -\fIflags\fR -argument that can be used to specify options\&. The functions only check the options that are contextually relevant\&. Use bitwise or (|) operations to specify one or more of the following: -.PP -\fBALLOCM_LG_ALIGN(\fR\fB\fIla\fR\fR\fB) \fR -.RS 4 -Align the memory allocation to start at an address that is a multiple of -(1 << \fIla\fR)\&. This macro does not validate that -\fIla\fR -is within the valid range\&. -.RE -.PP -\fBALLOCM_ALIGN(\fR\fB\fIa\fR\fR\fB) \fR -.RS 4 -Align the memory allocation to start at an address that is a multiple of -\fIa\fR, where -\fIa\fR -is a power of two\&. This macro does not validate that -\fIa\fR -is a power of 2\&. -.RE -.PP -\fBALLOCM_ZERO\fR -.RS 4 -Initialize newly allocated memory to contain zero bytes\&. In the growing reallocation case, the real size prior to reallocation defines the boundary between untouched bytes and those that are initialized to contain zero bytes\&. If this macro is absent, newly allocated memory is uninitialized\&. -.RE -.PP -\fBALLOCM_NO_MOVE\fR -.RS 4 -For reallocation, fail rather than moving the object\&. This constraint can apply to both growth and shrinkage\&. -.RE -.PP -\fBALLOCM_ARENA(\fR\fB\fIa\fR\fR\fB) \fR -.RS 4 -Use the arena specified by the index -\fIa\fR -(and by necessity bypass the thread cache)\&. This macro has no effect for huge regions, nor for regions that were allocated via an arena other than the one specified\&. This macro does not validate that -\fIa\fR -specifies an arena index in the valid range\&. -.RE -.PP -The -\fBallocm\fR\fB\fR -function allocates at least -\fIsize\fR -bytes of memory, sets -\fI*ptr\fR -to the base address of the allocation, and sets -\fI*rsize\fR -to the real size of the allocation if -\fIrsize\fR -is not -\fBNULL\fR\&. Behavior is undefined if -\fIsize\fR -is -\fB0\fR, or if request size overflows due to size class and/or alignment constraints\&. -.PP -The -\fBrallocm\fR\fB\fR -function resizes the allocation at -\fI*ptr\fR -to be at least -\fIsize\fR -bytes, sets -\fI*ptr\fR -to the base address of the allocation if it moved, and sets -\fI*rsize\fR -to the real size of the allocation if -\fIrsize\fR -is not -\fBNULL\fR\&. If -\fIextra\fR -is non\-zero, an attempt is made to resize the allocation to be at least -(\fIsize\fR + \fIextra\fR) -bytes, though inability to allocate the extra byte(s) will not by itself result in failure\&. Behavior is undefined if -\fIsize\fR -is -\fB0\fR, if request size overflows due to size class and/or alignment constraints, or if -(\fIsize\fR + \fIextra\fR > \fBSIZE_T_MAX\fR)\&. -.PP -The -\fBsallocm\fR\fB\fR -function sets -\fI*rsize\fR -to the real size of the allocation\&. -.PP -The -\fBdallocm\fR\fB\fR -function causes the memory referenced by -\fIptr\fR -to be made available for future allocations\&. -.PP -The -\fBnallocm\fR\fB\fR -function allocates no memory, but it performs the same size computation as the -\fBallocm\fR\fB\fR -function, and if -\fIrsize\fR -is not -\fBNULL\fR -it sets -\fI*rsize\fR -to the real size of the allocation that would result from the equivalent -\fBallocm\fR\fB\fR -function call\&. Behavior is undefined if -\fIsize\fR -is -\fB0\fR, or if request size overflows due to size class and/or alignment constraints\&. .SH "TUNING" .PP Once, when the first call is made to one of the memory allocation routines, the allocator initializes its internals based in part on various options that can be specified at compile\- or run\-time\&. @@ -535,8 +437,8 @@ options\&. Some options have boolean values (true/false), others have integer va Traditionally, allocators have used \fBsbrk\fR(2) to obtain memory, which is suboptimal for several reasons, including race conditions, increased fragmentation, and artificial limitations on maximum usable memory\&. If -\fB\-\-enable\-dss\fR -is specified during configuration, this allocator uses both +\fBsbrk\fR(2) +is supported by the operating system, this allocator uses both \fBmmap\fR(2) and \fBsbrk\fR(2), in that order of preference; otherwise only @@ -551,18 +453,29 @@ is specified during configuration, this allocator supports thread\-specific cach .PP Memory is conceptually broken into equal\-sized chunks, where the chunk size is a power of two that is greater than the page size\&. Chunks are always aligned to multiples of the chunk size\&. This alignment makes it possible to find metadata for user objects very quickly\&. .PP -User objects are broken into three categories according to size: small, large, and huge\&. Small objects are smaller than one page\&. Large objects are smaller than the chunk size\&. Huge objects are a multiple of the chunk size\&. Small and large objects are managed by arenas; huge objects are managed separately in a single data structure that is shared by all threads\&. Huge objects are used by applications infrequently enough that this single data structure is not a scalability issue\&. +User objects are broken into three categories according to size: small, large, and huge\&. Small and large objects are managed entirely by arenas; huge objects are additionally aggregated in a single data structure that is shared by all threads\&. Huge objects are typically used by applications infrequently enough that this single data structure is not a scalability issue\&. .PP Each chunk that is managed by an arena tracks its contents as runs of contiguous pages (unused, backing a set of small objects, or backing one large object)\&. The combination of chunk alignment and chunk page maps makes it possible to determine all metadata regarding small and large allocations in constant time\&. .PP -Small objects are managed in groups by page runs\&. Each run maintains a frontier and free list to track which regions are in use\&. Allocation requests that are no more than half the quantum (8 or 16, depending on architecture) are rounded up to the nearest power of two that is at least -sizeof(\fBdouble\fR)\&. All other small object size classes are multiples of the quantum, spaced such that internal fragmentation is limited to approximately 25% for all but the smallest size classes\&. Allocation requests that are larger than the maximum small size class, but small enough to fit in an arena\-managed chunk (see the +Small objects are managed in groups by page runs\&. Each run maintains a bitmap to track which regions are in use\&. Allocation requests that are no more than half the quantum (8 or 16, depending on architecture) are rounded up to the nearest power of two that is at least +sizeof(\fBdouble\fR)\&. All other object size classes are multiples of the quantum, spaced such that there are four size classes for each doubling in size, which limits internal fragmentation to approximately 20% for all but the smallest size classes\&. Small size classes are smaller than four times the page size, large size classes are smaller than the chunk size (see the "opt\&.lg_chunk" -option), are rounded up to the nearest run size\&. Allocation requests that are too large to fit in an arena\-managed chunk are rounded up to the nearest multiple of the chunk size\&. +option), and huge size classes extend from the chunk size up to one size class less than the full address space size\&. .PP Allocations are packed tightly together, which can be an issue for multi\-threaded applications\&. If you need to assure that allocations do not suffer from cacheline sharing, round your allocation requests up to the nearest multiple of the cacheline size, or specify cacheline alignment when allocating\&. .PP -Assuming 4 MiB chunks, 4 KiB pages, and a 16\-byte quantum on a 64\-bit system, the size classes in each category are as shown in +The +\fBrealloc\fR\fB\fR, +\fBrallocx\fR\fB\fR, and +\fBxallocx\fR\fB\fR +functions may resize allocations without moving them under limited circumstances\&. Unlike the +\fB*allocx\fR\fB\fR +API, the standard API does not officially round up the usable size of an allocation to the nearest size class, so technically it is necessary to call +\fBrealloc\fR\fB\fR +to grow e\&.g\&. a 9\-byte allocation to 16 bytes, or shrink a 16\-byte allocation to 9 bytes\&. Growth and shrinkage trivially succeeds in place as long as the pre\-size and post\-size both round up to the same size class\&. No other API guarantees are made regarding in\-place resizing, but the current implementation also tries to resize large and huge allocations in place, as long as the pre\-size and post\-size are both large or both huge\&. In such cases shrinkage always succeeds for large size classes, but for huge size classes the chunk allocator must support splitting (see +"arena\&.\&.chunk_hooks")\&. Growth only succeeds if the trailing memory is currently available, and additionally for huge size classes the chunk allocator must support merging\&. +.PP +Assuming 2 MiB chunks, 4 KiB pages, and a 16\-byte quantum on a 64\-bit system, the size classes in each category are as shown in Table 1\&. .sp .it 1 an-trap @@ -588,8 +501,23 @@ l r l ^ r l ^ r l ^ r l +^ r l +^ r l l r l -l r l. +^ r l +^ r l +^ r l +^ r l +^ r l +^ r l +^ r l +l r l +^ r l +^ r l +^ r l +^ r l +^ r l +^ r l. T{ Small T}:T{ @@ -600,7 +528,7 @@ T} :T{ 16 T}:T{ -[16, 32, 48, \&.\&.\&., 128] +[16, 32, 48, 64, 80, 96, 112, 128] T} :T{ 32 @@ -625,21 +553,96 @@ T} :T{ 512 T}:T{ -[2560, 3072, 3584] +[2560, 3072, 3584, 4096] +T} +:T{ +1 KiB +T}:T{ +[5 KiB, 6 KiB, 7 KiB, 8 KiB] +T} +:T{ +2 KiB +T}:T{ +[10 KiB, 12 KiB, 14 KiB] T} T{ Large T}:T{ +2 KiB +T}:T{ +[16 KiB] +T} +:T{ 4 KiB T}:T{ -[4 KiB, 8 KiB, 12 KiB, \&.\&.\&., 4072 KiB] +[20 KiB, 24 KiB, 28 KiB, 32 KiB] +T} +:T{ +8 KiB +T}:T{ +[40 KiB, 48 KiB, 54 KiB, 64 KiB] +T} +:T{ +16 KiB +T}:T{ +[80 KiB, 96 KiB, 112 KiB, 128 KiB] +T} +:T{ +32 KiB +T}:T{ +[160 KiB, 192 KiB, 224 KiB, 256 KiB] +T} +:T{ +64 KiB +T}:T{ +[320 KiB, 384 KiB, 448 KiB, 512 KiB] +T} +:T{ +128 KiB +T}:T{ +[640 KiB, 768 KiB, 896 KiB, 1 MiB] +T} +:T{ +256 KiB +T}:T{ +[1280 KiB, 1536 KiB, 1792 KiB] T} T{ Huge T}:T{ +256 KiB +T}:T{ +[2 MiB] +T} +:T{ +512 KiB +T}:T{ +[2560 KiB, 3 MiB, 3584 KiB, 4 MiB] +T} +:T{ +1 MiB +T}:T{ +[5 MiB, 6 MiB, 7 MiB, 8 MiB] +T} +:T{ +2 MiB +T}:T{ +[10 MiB, 12 MiB, 14 MiB, 16 MiB] +T} +:T{ 4 MiB T}:T{ -[4 MiB, 8 MiB, 12 MiB, \&.\&.\&.] +[20 MiB, 24 MiB, 28 MiB, 32 MiB] +T} +:T{ +8 MiB +T}:T{ +[40 MiB, 48 MiB, 56 MiB, 64 MiB] +T} +:T{ +\&.\&.\&. +T}:T{ +\&.\&.\&. T} .TE .sp 1 @@ -676,15 +679,15 @@ If a value is passed in, refresh the data from which the functions report values, and increment the epoch\&. Return the current epoch\&. This is useful for detecting whether another thread caused a refresh\&. .RE .PP -"config\&.debug" (\fBbool\fR) r\- +"config\&.cache_oblivious" (\fBbool\fR) r\- .RS 4 -\fB\-\-enable\-debug\fR +\fB\-\-enable\-cache\-oblivious\fR was specified during build configuration\&. .RE .PP -"config\&.dss" (\fBbool\fR) r\- +"config\&.debug" (\fBbool\fR) r\- .RS 4 -\fB\-\-enable\-dss\fR +\fB\-\-enable\-debug\fR was specified during build configuration\&. .RE .PP @@ -700,12 +703,6 @@ was specified during build configuration\&. was specified during build configuration\&. .RE .PP -"config\&.mremap" (\fBbool\fR) r\- -.RS 4 -\fB\-\-enable\-mremap\fR -was specified during build configuration\&. -.RE -.PP "config\&.munmap" (\fBbool\fR) r\- .RS 4 \fB\-\-enable\-munmap\fR @@ -779,14 +776,16 @@ is specified during configuration, in which case it is enabled by default\&. .RS 4 dss (\fBsbrk\fR(2)) allocation precedence as related to \fBmmap\fR(2) -allocation\&. The following settings are supported: \(lqdisabled\(rq, \(lqprimary\(rq, and \(lqsecondary\(rq\&. The default is \(lqsecondary\(rq if -"config\&.dss" -is true, \(lqdisabled\(rq otherwise\&. +allocation\&. The following settings are supported if +\fBsbrk\fR(2) +is supported by the operating system: \(lqdisabled\(rq, \(lqprimary\(rq, and \(lqsecondary\(rq; otherwise only \(lqdisabled\(rq is supported\&. The default is \(lqsecondary\(rq if +\fBsbrk\fR(2) +is supported by the operating system; \(lqdisabled\(rq otherwise\&. .RE .PP "opt\&.lg_chunk" (\fBsize_t\fR) r\- .RS 4 -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)\&. +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 2 MiB (2^21)\&. .RE .PP "opt\&.narenas" (\fBsize_t\fR) r\- @@ -798,7 +797,11 @@ Maximum number of arenas to use for automatic multiplexing of threads and arenas .RS 4 Per\-arena minimum ratio (log base 2) of active to dirty pages\&. Some dirty unused pages may be allowed to accumulate, within the limit set by the ratio (or one chunk worth of dirty pages, whichever is greater), before informing the kernel about some of those pages via \fBmadvise\fR(2) -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 8:1 (2^3:1); an option value of \-1 will disable dirty page purging\&. +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 8:1 (2^3:1); an option value of \-1 will disable dirty page purging\&. See +"arenas\&.lg_dirty_mult" +and +"arena\&.\&.lg_dirty_mult" +for related dynamic control options\&. .RE .PP "opt\&.stats_print" (\fBbool\fR) r\- @@ -809,16 +812,21 @@ function is called at program exit via an \fBatexit\fR(3) function\&. If \fB\-\-enable\-stats\fR -is specified during configuration, this has the potential to cause deadlock for a multi\-threaded process that exits while one or more threads are executing in the memory allocation functions\&. Therefore, this option should only be used with care; it is primarily intended as a performance tuning aid during application development\&. This option is disabled by default\&. +is specified during configuration, this has the potential to cause deadlock for a multi\-threaded process that exits while one or more threads are executing in the memory allocation functions\&. Furthermore, +\fBatexit\fR\fB\fR +may allocate memory during application initialization and then deadlock internally when jemalloc in turn calls +\fBatexit\fR\fB\fR, so this option is not univerally usable (though the application can register its own +\fBatexit\fR\fB\fR +function with equivalent functionality)\&. Therefore, this option should only be used with care; it is primarily intended as a performance tuning aid during application development\&. This option is disabled by default\&. .RE .PP -"opt\&.junk" (\fBbool\fR) r\- [\fB\-\-enable\-fill\fR] +"opt\&.junk" (\fBconst char *\fR) r\- [\fB\-\-enable\-fill\fR] .RS 4 -Junk filling enabled/disabled\&. If enabled, each byte of uninitialized allocated memory will be initialized to -0xa5\&. All deallocated memory will be initialized to -0x5a\&. This is intended for debugging and will impact performance negatively\&. This option is disabled by default unless +Junk filling\&. If set to "alloc", each byte of uninitialized allocated memory will be initialized to +0xa5\&. If set to "free", all deallocated memory will be initialized to +0x5a\&. If set to "true", both allocated and deallocated memory will be initialized, and if set to "false", junk filling be disabled entirely\&. This is intended for debugging and will impact performance negatively\&. This option is "false" by default unless \fB\-\-enable\-debug\fR -is specified during configuration, in which case it is enabled by default unless running inside +is specified during configuration, in which case it is "true" by default unless running inside \m[blue]\fBValgrind\fR\m[]\&\s-2\u[2]\d\s+2\&. .RE .PP @@ -841,10 +849,9 @@ option is enabled, the redzones are checked for corruption during deallocation\& "opt\&.zero" (\fBbool\fR) r\- [\fB\-\-enable\-fill\fR] .RS 4 Zero filling enabled/disabled\&. If enabled, each byte of uninitialized allocated memory will be initialized to 0\&. Note that this initialization only happens once for each byte, so -\fBrealloc\fR\fB\fR, -\fBrallocx\fR\fB\fR +\fBrealloc\fR\fB\fR and -\fBrallocm\fR\fB\fR +\fBrallocx\fR\fB\fR calls do not zero memory that was previously allocated\&. This is intended for debugging and will impact performance negatively\&. This option is disabled by default\&. .RE .PP @@ -855,12 +862,6 @@ Allocation tracing based on enabled/disabled\&. This option is disabled by default\&. .RE .PP -"opt\&.valgrind" (\fBbool\fR) r\- [\fB\-\-enable\-valgrind\fR] -.RS 4 -\m[blue]\fBValgrind\fR\m[]\&\s-2\u[2]\d\s+2 -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\&. -.RE -.PP "opt\&.xmalloc" (\fBbool\fR) r\- [\fB\-\-enable\-xmalloc\fR] .RS 4 Abort\-on\-out\-of\-memory enabled/disabled\&. If enabled, rather than returning failure for any allocation function, display a diagnostic message on @@ -883,15 +884,15 @@ This option is disabled by default\&. .PP "opt\&.tcache" (\fBbool\fR) r\- [\fB\-\-enable\-tcache\fR] .RS 4 -Thread\-specific caching enabled/disabled\&. When there are multiple threads, each thread uses a thread\-specific cache for objects up to a certain size\&. Thread\-specific caching allows many allocations to be satisfied without performing any thread synchronization, at the cost of increased memory use\&. See the +Thread\-specific caching (tcache) enabled/disabled\&. When there are multiple threads, each thread uses a tcache for objects up to a certain size\&. Thread\-specific caching allows many allocations to be satisfied without performing any thread synchronization, at the cost of increased memory use\&. See the "opt\&.lg_tcache_max" option for related tuning information\&. This option is enabled by default unless running inside -\m[blue]\fBValgrind\fR\m[]\&\s-2\u[2]\d\s+2\&. +\m[blue]\fBValgrind\fR\m[]\&\s-2\u[2]\d\s+2, in which case it is forcefully disabled\&. .RE .PP "opt\&.lg_tcache_max" (\fBsize_t\fR) r\- [\fB\-\-enable\-tcache\fR] .RS 4 -Maximum size class (log base 2) to cache in the thread\-specific cache\&. At a minimum, all small size classes are cached, and at a maximum all large size classes are cached\&. The default maximum is 32 KiB (2^15)\&. +Maximum size class (log base 2) to cache in the thread\-specific cache (tcache)\&. At a minimum, all small size classes are cached, and at a maximum all large size classes are cached\&. The default maximum is 32 KiB (2^15)\&. .RE .PP "opt\&.prof" (\fBbool\fR) r\- [\fB\-\-enable\-prof\fR] @@ -908,9 +909,11 @@ option for information on interval\-triggered profile dumping, the "opt\&.prof_gdump" option for information on high\-water\-triggered profile dumping, and the "opt\&.prof_final" -option for final profile dumping\&. Profile output is compatible with the included +option for final profile dumping\&. Profile output is compatible with the +\fBjeprof\fR +command, which is based on the \fBpprof\fR -Perl script, which originates from the +that is developed as part of the \m[blue]\fBgperftools package\fR\m[]\&\s-2\u[3]\d\s+2\&. .RE .PP @@ -920,7 +923,7 @@ Filename prefix for profile dumps\&. If the prefix is set to the empty string, n jeprof\&. .RE .PP -"opt\&.prof_active" (\fBbool\fR) rw [\fB\-\-enable\-prof\fR] +"opt\&.prof_active" (\fBbool\fR) r\- [\fB\-\-enable\-prof\fR] .RS 4 Profiling activated/deactivated\&. This is a secondary control mechanism that makes it possible to start the application with profiling enabled (see the "opt\&.prof" @@ -929,7 +932,16 @@ option) but inactive, then toggle profiling at any time during program execution mallctl\&. This option is enabled by default\&. .RE .PP -"opt\&.lg_prof_sample" (\fBssize_t\fR) r\- [\fB\-\-enable\-prof\fR] +"opt\&.prof_thread_active_init" (\fBbool\fR) r\- [\fB\-\-enable\-prof\fR] +.RS 4 +Initial setting for +"thread\&.prof\&.active" +in newly created threads\&. The initial setting for newly created threads can also be changed during execution via the +"prof\&.thread_active_init" +mallctl\&. This option is enabled by default\&. +.RE +.PP +"opt\&.lg_prof_sample" (\fBsize_t\fR) r\- [\fB\-\-enable\-prof\fR] .RS 4 Average interval (log base 2) between allocation samples, as measured in bytes of allocation activity\&. Increasing the sampling interval decreases profile fidelity, but also decreases the computational overhead\&. The default sample interval is 512 KiB (2^19 B)\&. .RE @@ -951,12 +963,8 @@ option\&. By default, interval\-triggered profile dumping is disabled (encoded a .PP "opt\&.prof_gdump" (\fBbool\fR) r\- [\fB\-\-enable\-prof\fR] .RS 4 -Trigger a memory profile dump every time the total virtual memory exceeds the previous maximum\&. Profiles are dumped to files named according to the pattern -\&.\&.\&.u\&.heap, where - -is controlled by the -"opt\&.prof_prefix" -option\&. This option is disabled by default\&. +Set the initial state of +"prof\&.gdump", which when enabled triggers a memory profile dump every time the total virtual memory exceeds the previous maximum\&. This option is disabled by default\&. .RE .PP "opt\&.prof_final" (\fBbool\fR) r\- [\fB\-\-enable\-prof\fR] @@ -968,7 +976,12 @@ function to dump final memory usage to a file named according to the pattern is controlled by the "opt\&.prof_prefix" -option\&. This option is enabled by default\&. +option\&. Note that +\fBatexit\fR\fB\fR +may allocate memory during application initialization and then deadlock internally when jemalloc in turn calls +\fBatexit\fR\fB\fR, so this option is not univerally usable (though the application can register its own +\fBatexit\fR\fB\fR +function with equivalent functionality)\&. This option is disabled by default\&. .RE .PP "opt\&.prof_leak" (\fBbool\fR) r\- [\fB\-\-enable\-prof\fR] @@ -1023,10 +1036,42 @@ Enable/disable calling thread\*(Aqs tcache\&. The tcache is implicitly flushed a .PP "thread\&.tcache\&.flush" (\fBvoid\fR) \-\- [\fB\-\-enable\-tcache\fR] .RS 4 -Flush calling thread\*(Aqs tcache\&. This interface releases all cached objects and internal data structures associated with the calling thread\*(Aqs thread\-specific cache\&. Ordinarily, this interface need not be called, since automatic periodic incremental garbage collection occurs, and the thread cache is automatically discarded when a thread exits\&. However, garbage collection is triggered by allocation activity, so it is possible for a thread that stops allocating/deallocating to retain its cache indefinitely, in which case the developer may find manual flushing useful\&. +Flush calling thread\*(Aqs thread\-specific cache (tcache)\&. This interface releases all cached objects and internal data structures associated with the calling thread\*(Aqs tcache\&. Ordinarily, this interface need not be called, since automatic periodic incremental garbage collection occurs, and the thread cache is automatically discarded when a thread exits\&. However, garbage collection is triggered by allocation activity, so it is possible for a thread that stops allocating/deallocating to retain its cache indefinitely, in which case the developer may find manual flushing useful\&. .RE .PP -"arena\&.\&.purge" (\fBunsigned\fR) \-\- +"thread\&.prof\&.name" (\fBconst char *\fR) r\- or \-w [\fB\-\-enable\-prof\fR] +.RS 4 +Get/set the descriptive name associated with the calling thread in memory profile dumps\&. An internal copy of the name string is created, so the input string need not be maintained after this interface completes execution\&. The output string of this interface should be copied for non\-ephemeral uses, because multiple implementation details can cause asynchronous string deallocation\&. Furthermore, each invocation of this interface can only read or write; simultaneous read/write is not supported due to string lifetime limitations\&. The name string must nil\-terminated and comprised only of characters in the sets recognized by +\fBisgraph\fR(3) +and +\fBisblank\fR(3)\&. +.RE +.PP +"thread\&.prof\&.active" (\fBbool\fR) rw [\fB\-\-enable\-prof\fR] +.RS 4 +Control whether sampling is currently active for the calling thread\&. This is an activation mechanism in addition to +"prof\&.active"; both must be active for the calling thread to sample\&. This flag is enabled by default\&. +.RE +.PP +"tcache\&.create" (\fBunsigned\fR) r\- [\fB\-\-enable\-tcache\fR] +.RS 4 +Create an explicit thread\-specific cache (tcache) and return an identifier that can be passed to the +\fBMALLOCX_TCACHE(\fR\fB\fItc\fR\fR\fB)\fR +macro to explicitly use the specified cache rather than the automatically managed one that is used by default\&. Each explicit cache can be used by only one thread at a time; the application must assure that this constraint holds\&. +.RE +.PP +"tcache\&.flush" (\fBunsigned\fR) \-w [\fB\-\-enable\-tcache\fR] +.RS 4 +Flush the specified thread\-specific cache (tcache)\&. The same considerations apply to this interface as to +"thread\&.tcache\&.flush", except that the tcache will never be automatically be discarded\&. +.RE +.PP +"tcache\&.destroy" (\fBunsigned\fR) \-w [\fB\-\-enable\-tcache\fR] +.RS 4 +Flush the specified thread\-specific cache (tcache) and make the identifier available for use during a future tcache creation\&. +.RE +.PP +"arena\&.\&.purge" (\fBvoid\fR) \-\- .RS 4 Purge unused dirty pages for arena , or for all arenas if equals "arenas\&.narenas"\&. @@ -1035,11 +1080,237 @@ Purge unused dirty pages for arena , or for all arenas if equals "arena\&.\&.dss" (\fBconst char *\fR) rw .RS 4 Set the precedence of dss allocation as related to mmap allocation for arena , or for all arenas if equals -"arenas\&.narenas"\&. Note that even during huge allocation this setting is read from the arena that would be chosen for small or large allocation so that applications can depend on consistent dss versus mmap allocation regardless of allocation size\&. See +"arenas\&.narenas"\&. See "opt\&.dss" for supported settings\&. .RE .PP +"arena\&.\&.lg_dirty_mult" (\fBssize_t\fR) rw +.RS 4 +Current per\-arena minimum ratio (log base 2) of active to dirty pages for arena \&. Each time this interface is set and the ratio is increased, pages are synchronously purged as necessary to impose the new ratio\&. See +"opt\&.lg_dirty_mult" +for additional information\&. +.RE +.PP +"arena\&.\&.chunk_hooks" (\fBchunk_hooks_t\fR) rw +.RS 4 +Get or set the chunk management hook functions for arena \&. The functions must be capable of operating on all extant chunks associated with arena , usually by passing unknown chunks to the replaced functions\&. In practice, it is feasible to control allocation for arenas created via +"arenas\&.extend" +such that all chunks originate from an application\-supplied chunk allocator (by setting custom chunk hook functions just after arena creation), but the automatically created arenas may have already created chunks prior to the application having an opportunity to take over chunk allocation\&. +.sp +.if n \{\ +.RS 4 +.\} +.nf +typedef struct { + chunk_alloc_t *alloc; + chunk_dalloc_t *dalloc; + chunk_commit_t *commit; + chunk_decommit_t *decommit; + chunk_purge_t *purge; + chunk_split_t *split; + chunk_merge_t *merge; +} chunk_hooks_t; +.fi +.if n \{\ +.RE +.\} +.sp +The +\fBchunk_hooks_t\fR +structure comprises function pointers which are described individually below\&. jemalloc uses these functions to manage chunk lifetime, which starts off with allocation of mapped committed memory, in the simplest case followed by deallocation\&. However, there are performance and platform reasons to retain chunks for later reuse\&. Cleanup attempts cascade from deallocation to decommit to purging, which gives the chunk management functions opportunities to reject the most permanent cleanup operations in favor of less permanent (and often less costly) operations\&. The chunk splitting and merging operations can also be opted out of, but this is mainly intended to support platforms on which virtual memory mappings provided by the operating system kernel do not automatically coalesce and split, e\&.g\&. Windows\&. +.HP \w'typedef\ void\ *(chunk_alloc_t)('u +.BI "typedef void *(chunk_alloc_t)(void\ *" "chunk" ", size_t\ " "size" ", size_t\ " "alignment" ", bool\ *" "zero" ", bool\ *" "commit" ", unsigned\ " "arena_ind" ");" +.sp +.if n \{\ +.RS 4 +.\} +.nf +.fi +.if n \{\ +.RE +.\} +.sp +A chunk allocation function conforms to the +\fBchunk_alloc_t\fR +type and upon success returns a pointer to +\fIsize\fR +bytes of mapped memory on behalf of arena +\fIarena_ind\fR +such that the chunk\*(Aqs base address is a multiple of +\fIalignment\fR, as well as setting +\fI*zero\fR +to indicate whether the chunk is zeroed and +\fI*commit\fR +to indicate whether the chunk is committed\&. Upon error the function returns +\fBNULL\fR +and leaves +\fI*zero\fR +and +\fI*commit\fR +unmodified\&. The +\fIsize\fR +parameter is always a multiple of the chunk size\&. The +\fIalignment\fR +parameter is always a power of two at least as large as the chunk size\&. Zeroing is mandatory if +\fI*zero\fR +is true upon function entry\&. Committing is mandatory if +\fI*commit\fR +is true upon function entry\&. If +\fIchunk\fR +is not +\fBNULL\fR, the returned pointer must be +\fIchunk\fR +on success or +\fBNULL\fR +on error\&. Committed memory may be committed in absolute terms as on a system that does not overcommit, or in implicit terms as on a system that overcommits and satisfies physical memory needs on demand via soft page faults\&. Note that replacing the default chunk allocation function makes the arena\*(Aqs +"arena\&.\&.dss" +setting irrelevant\&. +.HP \w'typedef\ bool\ (chunk_dalloc_t)('u +.BI "typedef bool (chunk_dalloc_t)(void\ *" "chunk" ", size_t\ " "size" ", bool\ " "committed" ", unsigned\ " "arena_ind" ");" +.sp +.if n \{\ +.RS 4 +.\} +.nf +.fi +.if n \{\ +.RE +.\} +.sp +A chunk deallocation function conforms to the +\fBchunk_dalloc_t\fR +type and deallocates a +\fIchunk\fR +of given +\fIsize\fR +with +\fIcommitted\fR/decommited memory as indicated, on behalf of arena +\fIarena_ind\fR, returning false upon success\&. If the function returns true, this indicates opt\-out from deallocation; the virtual memory mapping associated with the chunk remains mapped, in the same commit state, and available for future use, in which case it will be automatically retained for later reuse\&. +.HP \w'typedef\ bool\ (chunk_commit_t)('u +.BI "typedef bool (chunk_commit_t)(void\ *" "chunk" ", size_t\ " "size" ", size_t\ " "offset" ", size_t\ " "length" ", unsigned\ " "arena_ind" ");" +.sp +.if n \{\ +.RS 4 +.\} +.nf +.fi +.if n \{\ +.RE +.\} +.sp +A chunk commit function conforms to the +\fBchunk_commit_t\fR +type and commits zeroed physical memory to back pages within a +\fIchunk\fR +of given +\fIsize\fR +at +\fIoffset\fR +bytes, extending for +\fIlength\fR +on behalf of arena +\fIarena_ind\fR, returning false upon success\&. Committed memory may be committed in absolute terms as on a system that does not overcommit, or in implicit terms as on a system that overcommits and satisfies physical memory needs on demand via soft page faults\&. If the function returns true, this indicates insufficient physical memory to satisfy the request\&. +.HP \w'typedef\ bool\ (chunk_decommit_t)('u +.BI "typedef bool (chunk_decommit_t)(void\ *" "chunk" ", size_t\ " "size" ", size_t\ " "offset" ", size_t\ " "length" ", unsigned\ " "arena_ind" ");" +.sp +.if n \{\ +.RS 4 +.\} +.nf +.fi +.if n \{\ +.RE +.\} +.sp +A chunk decommit function conforms to the +\fBchunk_decommit_t\fR +type and decommits any physical memory that is backing pages within a +\fIchunk\fR +of given +\fIsize\fR +at +\fIoffset\fR +bytes, extending for +\fIlength\fR +on behalf of arena +\fIarena_ind\fR, returning false upon success, in which case the pages will be committed via the chunk commit function before being reused\&. If the function returns true, this indicates opt\-out from decommit; the memory remains committed and available for future use, in which case it will be automatically retained for later reuse\&. +.HP \w'typedef\ bool\ (chunk_purge_t)('u +.BI "typedef bool (chunk_purge_t)(void\ *" "chunk" ", size_t" "size" ", size_t\ " "offset" ", size_t\ " "length" ", unsigned\ " "arena_ind" ");" +.sp +.if n \{\ +.RS 4 +.\} +.nf +.fi +.if n \{\ +.RE +.\} +.sp +A chunk purge function conforms to the +\fBchunk_purge_t\fR +type and optionally discards physical pages within the virtual memory mapping associated with +\fIchunk\fR +of given +\fIsize\fR +at +\fIoffset\fR +bytes, extending for +\fIlength\fR +on behalf of arena +\fIarena_ind\fR, returning false if pages within the purged virtual memory range will be zero\-filled the next time they are accessed\&. +.HP \w'typedef\ bool\ (chunk_split_t)('u +.BI "typedef bool (chunk_split_t)(void\ *" "chunk" ", size_t\ " "size" ", size_t\ " "size_a" ", size_t\ " "size_b" ", bool\ " "committed" ", unsigned\ " "arena_ind" ");" +.sp +.if n \{\ +.RS 4 +.\} +.nf +.fi +.if n \{\ +.RE +.\} +.sp +A chunk split function conforms to the +\fBchunk_split_t\fR +type and optionally splits +\fIchunk\fR +of given +\fIsize\fR +into two adjacent chunks, the first of +\fIsize_a\fR +bytes, and the second of +\fIsize_b\fR +bytes, operating on +\fIcommitted\fR/decommitted memory as indicated, on behalf of arena +\fIarena_ind\fR, returning false upon success\&. If the function returns true, this indicates that the chunk remains unsplit and therefore should continue to be operated on as a whole\&. +.HP \w'typedef\ bool\ (chunk_merge_t)('u +.BI "typedef bool (chunk_merge_t)(void\ *" "chunk_a" ", size_t\ " "size_a" ", void\ *" "chunk_b" ", size_t\ " "size_b" ", bool\ " "committed" ", unsigned\ " "arena_ind" ");" +.sp +.if n \{\ +.RS 4 +.\} +.nf +.fi +.if n \{\ +.RE +.\} +.sp +A chunk merge function conforms to the +\fBchunk_merge_t\fR +type and optionally merges adjacent chunks, +\fIchunk_a\fR +of given +\fIsize_a\fR +and +\fIchunk_b\fR +of given +\fIsize_b\fR +into one contiguous chunk, operating on +\fIcommitted\fR/decommitted memory as indicated, on behalf of arena +\fIarena_ind\fR, returning false upon success\&. If the function returns true, this indicates that the chunks remain distinct mappings and therefore should continue to be operated on independently\&. +.RE +.PP "arenas\&.narenas" (\fBunsigned\fR) r\- .RS 4 Current limit on number of arenas\&. @@ -1052,6 +1323,15 @@ An array of booleans\&. Each boolean indicates whether the corresponding arena is initialized\&. .RE .PP +"arenas\&.lg_dirty_mult" (\fBssize_t\fR) rw +.RS 4 +Current default per\-arena minimum ratio (log base 2) of active to dirty pages, used to initialize +"arena\&.\&.lg_dirty_mult" +during arena creation\&. See +"opt\&.lg_dirty_mult" +for additional information\&. +.RE +.PP "arenas\&.quantum" (\fBsize_t\fR) r\- .RS 4 Quantum size\&. @@ -1092,7 +1372,7 @@ Number of regions per page run\&. Number of bytes per page run\&. .RE .PP -"arenas\&.nlruns" (\fBsize_t\fR) r\- +"arenas\&.nlruns" (\fBunsigned\fR) r\- .RS 4 Total number of large size classes\&. .RE @@ -1102,9 +1382,14 @@ Total number of large size classes\&. Maximum size supported by this large size class\&. .RE .PP -"arenas\&.purge" (\fBunsigned\fR) \-w +"arenas\&.nhchunks" (\fBunsigned\fR) r\- .RS 4 -Purge unused dirty pages for the specified arena, or for all arenas if none is specified\&. +Total number of huge size classes\&. +.RE +.PP +"arenas\&.hchunk\&.\&.size" (\fBsize_t\fR) r\- +.RS 4 +Maximum size supported by this huge size class\&. .RE .PP "arenas\&.extend" (\fBunsigned\fR) r\- @@ -1112,11 +1397,22 @@ Purge unused dirty pages for the specified arena, or for all arenas if none is s Extend the array of arenas by appending a new arena, and returning the new arena index\&. .RE .PP +"prof\&.thread_active_init" (\fBbool\fR) rw [\fB\-\-enable\-prof\fR] +.RS 4 +Control the initial setting for +"thread\&.prof\&.active" +in newly created threads\&. See the +"opt\&.prof_thread_active_init" +option for additional information\&. +.RE +.PP "prof\&.active" (\fBbool\fR) rw [\fB\-\-enable\-prof\fR] .RS 4 Control whether sampling is currently active\&. See the "opt\&.prof_active" -option for additional information\&. +option for additional information, as well as the interrelated +"thread\&.prof\&.active" +mallctl\&. .RE .PP "prof\&.dump" (\fBconst char *\fR) \-w [\fB\-\-enable\-prof\fR] @@ -1129,6 +1425,30 @@ is controlled by the option\&. .RE .PP +"prof\&.gdump" (\fBbool\fR) rw [\fB\-\-enable\-prof\fR] +.RS 4 +When enabled, trigger a memory profile dump every time the total virtual memory exceeds the previous maximum\&. Profiles are dumped to files named according to the pattern +\&.\&.\&.u\&.heap, where + +is controlled by the +"opt\&.prof_prefix" +option\&. +.RE +.PP +"prof\&.reset" (\fBsize_t\fR) \-w [\fB\-\-enable\-prof\fR] +.RS 4 +Reset all memory profile statistics, and optionally update the sample rate (see +"opt\&.lg_prof_sample" +and +"prof\&.lg_sample")\&. +.RE +.PP +"prof\&.lg_sample" (\fBsize_t\fR) r\- [\fB\-\-enable\-prof\fR] +.RS 4 +Get the current sample rate (see +"opt\&.lg_prof_sample")\&. +.RE +.PP "prof\&.interval" (\fBuint64_t\fR) r\- [\fB\-\-enable\-prof\fR] .RS 4 Average number of bytes allocated between inverval\-based profile dumps\&. See the @@ -1138,7 +1458,7 @@ option for additional information\&. .PP "stats\&.cactive" (\fBsize_t *\fR) r\- [\fB\-\-enable\-stats\fR] .RS 4 -Pointer to a counter that contains an approximate count of the current number of bytes in active pages\&. The estimate may be high, but never low, because each arena rounds up to the nearest multiple of the chunk size when computing its contribution to the counter\&. Note that the +Pointer to a counter that contains an approximate count of the current number of bytes in active pages\&. The estimate may be high, but never low, because each arena rounds up when computing its contribution to the counter\&. Note that the "epoch" mallctl has no bearing on this counter\&. Furthermore, counter consistency is maintained via atomic operations, so it is necessary to use an atomic operation in order to guarantee a consistent read when dereferencing the pointer\&. .RE @@ -1152,44 +1472,27 @@ Total number of bytes allocated by the application\&. .RS 4 Total number of bytes in active pages allocated by the application\&. This is a multiple of the page size, and greater than or equal to "stats\&.allocated"\&. This does not include -"stats\&.arenas\&.\&.pdirty" -and pages entirely devoted to allocator metadata\&. +"stats\&.arenas\&.\&.pdirty", nor pages entirely devoted to allocator metadata\&. +.RE +.PP +"stats\&.metadata" (\fBsize_t\fR) r\- [\fB\-\-enable\-stats\fR] +.RS 4 +Total number of bytes dedicated to metadata, which comprise base allocations used for bootstrap\-sensitive internal allocator data structures, arena chunk headers (see +"stats\&.arenas\&.\&.metadata\&.mapped"), and internal allocations (see +"stats\&.arenas\&.\&.metadata\&.allocated")\&. +.RE +.PP +"stats\&.resident" (\fBsize_t\fR) r\- [\fB\-\-enable\-stats\fR] +.RS 4 +Maximum number of bytes in physically resident data pages mapped by the allocator, comprising all pages dedicated to allocator metadata, pages backing active allocations, and unused dirty pages\&. This is a maximum rather than precise because pages may not actually be physically resident if they correspond to demand\-zeroed virtual memory that has not yet been touched\&. This is a multiple of the page size, and is larger than +"stats\&.active"\&. .RE .PP "stats\&.mapped" (\fBsize_t\fR) r\- [\fB\-\-enable\-stats\fR] .RS 4 -Total number of bytes in chunks mapped on behalf of the application\&. This is a multiple of the chunk size, and is at least as large as -"stats\&.active"\&. This does not include inactive chunks\&. -.RE -.PP -"stats\&.chunks\&.current" (\fBsize_t\fR) r\- [\fB\-\-enable\-stats\fR] -.RS 4 -Total number of chunks actively mapped on behalf of the application\&. This does not include inactive chunks\&. -.RE -.PP -"stats\&.chunks\&.total" (\fBuint64_t\fR) r\- [\fB\-\-enable\-stats\fR] -.RS 4 -Cumulative number of chunks allocated\&. -.RE -.PP -"stats\&.chunks\&.high" (\fBsize_t\fR) r\- [\fB\-\-enable\-stats\fR] -.RS 4 -Maximum number of active chunks at any time thus far\&. -.RE -.PP -"stats\&.huge\&.allocated" (\fBsize_t\fR) r\- [\fB\-\-enable\-stats\fR] -.RS 4 -Number of bytes currently allocated by huge objects\&. -.RE -.PP -"stats\&.huge\&.nmalloc" (\fBuint64_t\fR) r\- [\fB\-\-enable\-stats\fR] -.RS 4 -Cumulative number of huge allocation requests\&. -.RE -.PP -"stats\&.huge\&.ndalloc" (\fBuint64_t\fR) r\- [\fB\-\-enable\-stats\fR] -.RS 4 -Cumulative number of huge deallocation requests\&. +Total number of bytes in active chunks mapped by the allocator\&. This is a multiple of the chunk size, and is larger than +"stats\&.active"\&. This does not include inactive chunks, even those that contain unused dirty pages, which means that there is no strict ordering between this and +"stats\&.resident"\&. .RE .PP "stats\&.arenas\&.\&.dss" (\fBconst char *\fR) r\- @@ -1201,6 +1504,13 @@ allocation\&. See for details\&. .RE .PP +"stats\&.arenas\&.\&.lg_dirty_mult" (\fBssize_t\fR) r\- +.RS 4 +Minimum ratio (log base 2) of active to dirty pages\&. See +"opt\&.lg_dirty_mult" +for details\&. +.RE +.PP "stats\&.arenas\&.\&.nthreads" (\fBunsigned\fR) r\- .RS 4 Number of threads currently assigned to arena\&. @@ -1223,6 +1533,24 @@ or similar has not been called\&. Number of mapped bytes\&. .RE .PP +"stats\&.arenas\&.\&.metadata\&.mapped" (\fBsize_t\fR) r\- [\fB\-\-enable\-stats\fR] +.RS 4 +Number of mapped bytes in arena chunk headers, which track the states of the non\-metadata pages\&. +.RE +.PP +"stats\&.arenas\&.\&.metadata\&.allocated" (\fBsize_t\fR) r\- [\fB\-\-enable\-stats\fR] +.RS 4 +Number of bytes dedicated to internal allocations\&. Internal allocations differ from application\-originated allocations in that they are for internal use, and that they are omitted from heap profiles\&. This statistic is reported separately from +"stats\&.metadata" +and +"stats\&.arenas\&.\&.metadata\&.mapped" +because it overlaps with e\&.g\&. the +"stats\&.allocated" +and +"stats\&.active" +statistics, whereas the other metadata statistics do not\&. +.RE +.PP "stats\&.arenas\&.\&.npurge" (\fBuint64_t\fR) r\- [\fB\-\-enable\-stats\fR] .RS 4 Number of dirty page purge sweeps performed\&. @@ -1280,9 +1608,24 @@ Cumulative number of large deallocation requests served directly by the arena\&. Cumulative number of large allocation requests\&. .RE .PP -"stats\&.arenas\&.\&.bins\&.\&.allocated" (\fBsize_t\fR) r\- [\fB\-\-enable\-stats\fR] +"stats\&.arenas\&.\&.huge\&.allocated" (\fBsize_t\fR) r\- [\fB\-\-enable\-stats\fR] .RS 4 -Current number of bytes allocated by bin\&. +Number of bytes currently allocated by huge objects\&. +.RE +.PP +"stats\&.arenas\&.\&.huge\&.nmalloc" (\fBuint64_t\fR) r\- [\fB\-\-enable\-stats\fR] +.RS 4 +Cumulative number of huge allocation requests served directly by the arena\&. +.RE +.PP +"stats\&.arenas\&.\&.huge\&.ndalloc" (\fBuint64_t\fR) r\- [\fB\-\-enable\-stats\fR] +.RS 4 +Cumulative number of huge deallocation requests served directly by the arena\&. +.RE +.PP +"stats\&.arenas\&.\&.huge\&.nrequests" (\fBuint64_t\fR) r\- [\fB\-\-enable\-stats\fR] +.RS 4 +Cumulative number of huge allocation requests\&. .RE .PP "stats\&.arenas\&.\&.bins\&.\&.nmalloc" (\fBuint64_t\fR) r\- [\fB\-\-enable\-stats\fR] @@ -1300,6 +1643,11 @@ Cumulative number of allocations returned to bin\&. Cumulative number of allocation requests\&. .RE .PP +"stats\&.arenas\&.\&.bins\&.\&.curregs" (\fBsize_t\fR) r\- [\fB\-\-enable\-stats\fR] +.RS 4 +Current number of regions for this size class\&. +.RE +.PP "stats\&.arenas\&.\&.bins\&.\&.nfills" (\fBuint64_t\fR) r\- [\fB\-\-enable\-stats\fR \fB\-\-enable\-tcache\fR] .RS 4 Cumulative number of tcache fills\&. @@ -1344,6 +1692,26 @@ Cumulative number of allocation requests for this size class\&. .RS 4 Current number of runs for this size class\&. .RE +.PP +"stats\&.arenas\&.\&.hchunks\&.\&.nmalloc" (\fBuint64_t\fR) r\- [\fB\-\-enable\-stats\fR] +.RS 4 +Cumulative number of allocation requests for this size class served directly by the arena\&. +.RE +.PP +"stats\&.arenas\&.\&.hchunks\&.\&.ndalloc" (\fBuint64_t\fR) r\- [\fB\-\-enable\-stats\fR] +.RS 4 +Cumulative number of deallocation requests for this size class served directly by the arena\&. +.RE +.PP +"stats\&.arenas\&.\&.hchunks\&.\&.nrequests" (\fBuint64_t\fR) r\- [\fB\-\-enable\-stats\fR] +.RS 4 +Cumulative number of allocation requests for this size class\&. +.RE +.PP +"stats\&.arenas\&.\&.hchunks\&.\&.curhchunks" (\fBsize_t\fR) r\- [\fB\-\-enable\-stats\fR] +.RS 4 +Current number of huge allocations for this size class\&. +.RE .SH "DEBUGGING MALLOC PROBLEMS" .PP When debugging, it is a good idea to configure/build jemalloc with the @@ -1529,44 +1897,6 @@ The \fBmalloc_usable_size\fR\fB\fR function returns the usable size of the allocation pointed to by \fIptr\fR\&. -.SS "Experimental API" -.PP -The -\fBallocm\fR\fB\fR, -\fBrallocm\fR\fB\fR, -\fBsallocm\fR\fB\fR, -\fBdallocm\fR\fB\fR, and -\fBnallocm\fR\fB\fR -functions return -\fBALLOCM_SUCCESS\fR -on success; otherwise they return an error value\&. The -\fBallocm\fR\fB\fR, -\fBrallocm\fR\fB\fR, and -\fBnallocm\fR\fB\fR -functions will fail if: -.PP -ALLOCM_ERR_OOM -.RS 4 -Out of memory\&. Insufficient contiguous memory was available to service the allocation request\&. The -\fBallocm\fR\fB\fR -function additionally sets -\fI*ptr\fR -to -\fBNULL\fR, whereas the -\fBrallocm\fR\fB\fR -function leaves -\fB*ptr\fR -unmodified\&. -.RE -The -\fBrallocm\fR\fB\fR -function will also fail if: -.PP -ALLOCM_ERR_NOT_MOVED -.RS 4 -\fBALLOCM_NO_MOVE\fR -was specified, but the reallocation request could not be serviced without moving the object\&. -.RE .SH "ENVIRONMENT" .PP The following environment variable affects the execution of the allocation functions: @@ -1633,9 +1963,8 @@ functions first appeared in FreeBSD 7\&.0\&. .PP The \fBaligned_alloc\fR\fB\fR, -\fBmalloc_stats_print\fR\fB\fR, -\fBmallctl*\fR\fB\fR, and -\fB*allocm\fR\fB\fR +\fBmalloc_stats_print\fR\fB\fR, and +\fBmallctl*\fR\fB\fR functions first appeared in FreeBSD 10\&.0\&. .PP The diff --git a/contrib/jemalloc/include/jemalloc/internal/arena.h b/contrib/jemalloc/include/jemalloc/internal/arena.h index 9d000c03decb..cb015eedc601 100644 --- a/contrib/jemalloc/include/jemalloc/internal/arena.h +++ b/contrib/jemalloc/include/jemalloc/internal/arena.h @@ -1,30 +1,10 @@ /******************************************************************************/ #ifdef JEMALLOC_H_TYPES -/* - * RUN_MAX_OVRHD indicates maximum desired run header overhead. Runs are sized - * as small as possible such that this setting is still honored, without - * violating other constraints. The goal is to make runs as small as possible - * without exceeding a per run external fragmentation threshold. - * - * We use binary fixed point math for overhead computations, where the binary - * point is implicitly RUN_BFP bits to the left. - * - * Note that it is possible to set RUN_MAX_OVRHD low enough that it cannot be - * honored for some/all object sizes, since when heap profiling is enabled - * there is one pointer of header overhead per object (plus a constant). This - * constraint is relaxed (ignored) for runs that are so small that the - * per-region overhead is greater than: - * - * (RUN_MAX_OVRHD / (reg_interval << (3+RUN_BFP)) - */ -#define RUN_BFP 12 -/* \/ Implicit binary fixed point. */ -#define RUN_MAX_OVRHD 0x0000003dU -#define RUN_MAX_OVRHD_RELAX 0x00001800U +#define LARGE_MINCLASS (ZU(1) << LG_LARGE_MINCLASS) /* Maximum number of regions in one run. */ -#define LG_RUN_MAXREGS 11 +#define LG_RUN_MAXREGS (LG_PAGE - LG_TINY_MIN) #define RUN_MAXREGS (1U << LG_RUN_MAXREGS) /* @@ -36,16 +16,18 @@ /* * The minimum ratio of active:dirty pages per arena is computed as: * - * (nactive >> opt_lg_dirty_mult) >= ndirty + * (nactive >> lg_dirty_mult) >= ndirty * - * So, supposing that opt_lg_dirty_mult is 3, there can be no less than 8 times - * as many active pages as dirty pages. + * So, supposing that 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 3 -typedef struct arena_chunk_map_s arena_chunk_map_t; -typedef struct arena_chunk_s arena_chunk_t; +typedef struct arena_runs_dirty_link_s arena_runs_dirty_link_t; typedef struct arena_run_s arena_run_t; +typedef struct arena_chunk_map_bits_s arena_chunk_map_bits_t; +typedef struct arena_chunk_map_misc_s arena_chunk_map_misc_t; +typedef struct arena_chunk_s arena_chunk_t; typedef struct arena_bin_info_s arena_bin_info_t; typedef struct arena_bin_s arena_bin_t; typedef struct arena_s arena_t; @@ -54,54 +36,34 @@ typedef struct arena_s arena_t; /******************************************************************************/ #ifdef JEMALLOC_H_STRUCTS +#ifdef JEMALLOC_ARENA_STRUCTS_A +struct arena_run_s { + /* Index of bin this run is associated with. */ + index_t binind; + + /* Number of free regions in run. */ + unsigned nfree; + + /* Per region allocated/deallocated bitmap. */ + bitmap_t bitmap[BITMAP_GROUPS_MAX]; +}; + /* Each element of the chunk map corresponds to one page within the chunk. */ -struct arena_chunk_map_s { -#ifndef JEMALLOC_PROF - /* - * Overlay prof_ctx in order to allow it to be referenced by dead code. - * Such antics aren't warranted for per arena data structures, but - * chunk map overhead accounts for a percentage of memory, rather than - * being just a fixed cost. - */ - union { -#endif - union { - /* - * Linkage for run trees. There are two disjoint uses: - * - * 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. - */ - rb_node(arena_chunk_map_t) rb_link; - /* - * List of runs currently in purgatory. arena_chunk_purge() - * temporarily allocates runs that contain dirty pages while - * purging, so that other threads cannot use the runs while the - * purging thread is operating without the arena lock held. - */ - ql_elm(arena_chunk_map_t) ql_link; - } u; - - /* Profile counters, used for large object runs. */ - prof_ctx_t *prof_ctx; -#ifndef JEMALLOC_PROF - }; /* union { ... }; */ -#endif - +struct arena_chunk_map_bits_s { /* * Run address (or size) and various flags are stored together. The bit * layout looks like (assuming 32-bit system): * - * ???????? ???????? ????nnnn nnnndula + * ???????? ???????? ???nnnnn nnndumla * * ? : Unallocated: Run address for first/last pages, unset for internal * pages. * Small: Run page offset. - * Large: Run size for first page, unset for trailing pages. + * Large: Run page count for first page, unset for trailing pages. * n : binind for small size class, BININD_INVALID for large size class. * d : dirty? * u : unzeroed? + * m : decommitted? * l : large? * a : allocated? * @@ -110,78 +72,109 @@ struct arena_chunk_map_s { * p : run page offset * s : run size * n : binind for size class; large objects set these to BININD_INVALID - * except for promoted allocations (see prof_promote) * x : don't care * - : 0 * + : 1 - * [DULA] : bit set - * [dula] : bit unset + * [DUMLA] : bit set + * [dumla] : bit unset * * Unallocated (clean): - * ssssssss ssssssss ssss++++ ++++du-a - * xxxxxxxx xxxxxxxx xxxxxxxx xxxx-Uxx - * ssssssss ssssssss ssss++++ ++++dU-a + * ssssssss ssssssss sss+++++ +++dum-a + * xxxxxxxx xxxxxxxx xxxxxxxx xxx-Uxxx + * ssssssss ssssssss sss+++++ +++dUm-a * * Unallocated (dirty): - * ssssssss ssssssss ssss++++ ++++D--a + * ssssssss ssssssss sss+++++ +++D-m-a * xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx - * ssssssss ssssssss ssss++++ ++++D--a + * ssssssss ssssssss sss+++++ +++D-m-a * * Small: - * pppppppp pppppppp ppppnnnn nnnnd--A - * pppppppp pppppppp ppppnnnn nnnn---A - * pppppppp pppppppp ppppnnnn nnnnd--A + * pppppppp pppppppp pppnnnnn nnnd---A + * pppppppp pppppppp pppnnnnn nnn----A + * pppppppp pppppppp pppnnnnn nnnd---A * * Large: - * ssssssss ssssssss ssss++++ ++++D-LA + * ssssssss ssssssss sss+++++ +++D--LA * xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx - * -------- -------- ----++++ ++++D-LA + * -------- -------- ---+++++ +++D--LA * - * Large (sampled, size <= PAGE): - * ssssssss ssssssss ssssnnnn nnnnD-LA + * Large (sampled, size <= LARGE_MINCLASS): + * ssssssss ssssssss sssnnnnn nnnD--LA + * xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx + * -------- -------- ---+++++ +++D--LA * - * Large (not sampled, size == PAGE): - * ssssssss ssssssss ssss++++ ++++D-LA + * Large (not sampled, size == LARGE_MINCLASS): + * ssssssss ssssssss sss+++++ +++D--LA + * xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx + * -------- -------- ---+++++ +++D--LA */ size_t bits; -#define CHUNK_MAP_BININD_SHIFT 4 -#define BININD_INVALID ((size_t)0xffU) -/* CHUNK_MAP_BININD_MASK == (BININD_INVALID << CHUNK_MAP_BININD_SHIFT) */ -#define CHUNK_MAP_BININD_MASK ((size_t)0xff0U) -#define CHUNK_MAP_BININD_INVALID CHUNK_MAP_BININD_MASK -#define CHUNK_MAP_FLAGS_MASK ((size_t)0xcU) -#define CHUNK_MAP_DIRTY ((size_t)0x8U) -#define CHUNK_MAP_UNZEROED ((size_t)0x4U) -#define CHUNK_MAP_LARGE ((size_t)0x2U) -#define CHUNK_MAP_ALLOCATED ((size_t)0x1U) -#define CHUNK_MAP_KEY CHUNK_MAP_ALLOCATED -}; -typedef rb_tree(arena_chunk_map_t) arena_avail_tree_t; -typedef rb_tree(arena_chunk_map_t) arena_run_tree_t; -typedef ql_head(arena_chunk_map_t) arena_chunk_mapelms_t; +#define CHUNK_MAP_ALLOCATED ((size_t)0x01U) +#define CHUNK_MAP_LARGE ((size_t)0x02U) +#define CHUNK_MAP_STATE_MASK ((size_t)0x3U) +#define CHUNK_MAP_DECOMMITTED ((size_t)0x04U) +#define CHUNK_MAP_UNZEROED ((size_t)0x08U) +#define CHUNK_MAP_DIRTY ((size_t)0x10U) +#define CHUNK_MAP_FLAGS_MASK ((size_t)0x1cU) + +#define CHUNK_MAP_BININD_SHIFT 5 +#define BININD_INVALID ((size_t)0xffU) +#define CHUNK_MAP_BININD_MASK (BININD_INVALID << CHUNK_MAP_BININD_SHIFT) +#define CHUNK_MAP_BININD_INVALID CHUNK_MAP_BININD_MASK + +#define CHUNK_MAP_RUNIND_SHIFT (CHUNK_MAP_BININD_SHIFT + 8) +#define CHUNK_MAP_SIZE_SHIFT (CHUNK_MAP_RUNIND_SHIFT - LG_PAGE) +#define CHUNK_MAP_SIZE_MASK \ + (~(CHUNK_MAP_BININD_MASK | CHUNK_MAP_FLAGS_MASK | CHUNK_MAP_STATE_MASK)) +}; + +struct arena_runs_dirty_link_s { + qr(arena_runs_dirty_link_t) rd_link; +}; + +/* + * Each arena_chunk_map_misc_t corresponds to one page within the chunk, just + * like arena_chunk_map_bits_t. Two separate arrays are stored within each + * chunk header in order to improve cache locality. + */ +struct arena_chunk_map_misc_s { + /* + * Linkage for run trees. There are two disjoint uses: + * + * 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. + */ + rb_node(arena_chunk_map_misc_t) rb_link; + + union { + /* Linkage for list of dirty runs. */ + arena_runs_dirty_link_t rd; + + /* Profile counters, used for large object runs. */ + union { + void *prof_tctx_pun; + prof_tctx_t *prof_tctx; + }; + + /* Small region run metadata. */ + arena_run_t run; + }; +}; +typedef rb_tree(arena_chunk_map_misc_t) arena_avail_tree_t; +typedef rb_tree(arena_chunk_map_misc_t) arena_run_tree_t; +#endif /* JEMALLOC_ARENA_STRUCTS_A */ + +#ifdef JEMALLOC_ARENA_STRUCTS_B /* Arena chunk header. */ struct arena_chunk_s { - /* Arena that owns the chunk. */ - arena_t *arena; - - /* Linkage for tree of arena chunks that contain dirty runs. */ - rb_node(arena_chunk_t) dirty_link; - - /* Number of dirty pages. */ - size_t ndirty; - - /* Number of available runs. */ - size_t nruns_avail; - /* - * Number of available run adjacencies that purging could coalesce. - * 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. + * A pointer to the arena that owns the chunk is stored within the node. + * This field as a whole is used by chunks_rtree to support both + * ivsalloc() and core-based debugging. */ - size_t nruns_adjac; + extent_node_t node; /* * Map of pages within chunk that keeps track of free/large/small. The @@ -189,19 +182,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. */ -}; -typedef rb_tree(arena_chunk_t) arena_chunk_tree_t; - -struct arena_run_s { - /* Bin this run is associated with. */ - arena_bin_t *bin; - - /* Index of next region that has never been allocated, or nregs. */ - uint32_t nextind; - - /* Number of free regions in run. */ - unsigned nfree; + arena_chunk_map_bits_t map_bits[1]; /* Dynamically sized. */ }; /* @@ -212,12 +193,7 @@ struct arena_run_s { * Each run has the following layout: * * /--------------------\ - * | arena_run_t header | - * | ... | - * bitmap_offset | bitmap | - * | ... | - * ctx0_offset | ctx map | - * | ... | + * | pad? | * |--------------------| * | redzone | * reg0_offset | region 0 | @@ -258,24 +234,12 @@ struct arena_bin_info_s { /* Total number of regions in a run for this bin's size class. */ uint32_t nregs; - /* - * Offset of first bitmap_t element in a run header for this bin's size - * class. - */ - uint32_t bitmap_offset; - /* * Metadata used to manipulate bitmaps for runs associated with this * bin. */ bitmap_info_t bitmap_info; - /* - * Offset of first (prof_ctx_t *) in a run header for this bin's size - * class, or 0 if (config_prof == false || opt_prof == false). - */ - uint32_t ctx0_offset; - /* Offset of first region in a run for this bin's size class. */ uint32_t reg0_offset; }; @@ -321,8 +285,7 @@ struct arena_s { /* * There are three classes of arena operations from a locking * perspective: - * 1) Thread asssignment (modifies nthreads) is protected by - * arenas_lock. + * 1) Thread assignment (modifies nthreads) is protected by arenas_lock. * 2) Bin-related operations are protected by bin locks. * 3) Chunk- and run-related operations are protected by this mutex. */ @@ -331,16 +294,20 @@ struct arena_s { arena_stats_t stats; /* * List of tcaches for extant threads associated with this arena. - * Stats from these are merged incrementally, and at exit. + * Stats from these are merged incrementally, and at exit if + * opt_stats_print is enabled. */ ql_head(tcache_t) tcache_ql; uint64_t prof_accumbytes; - dss_prec_t dss_prec; + /* + * PRNG state for cache index randomization of large allocation base + * pointers. + */ + uint64_t offset_state; - /* Tree of dirty-page-containing chunks this arena manages. */ - arena_chunk_tree_t chunks_dirty; + dss_prec_t dss_prec; /* * In order to avoid rapid chunk allocation/deallocation when an arena @@ -354,7 +321,13 @@ struct arena_s { */ arena_chunk_t *spare; - /* Number of pages in active runs. */ + /* Minimum ratio (log base 2) of nactive:ndirty. */ + ssize_t lg_dirty_mult; + + /* True if a thread is currently executing arena_purge(). */ + bool purging; + + /* Number of pages in active runs and huge regions. */ size_t nactive; /* @@ -366,44 +339,116 @@ struct arena_s { size_t ndirty; /* - * Approximate number of pages being purged. It is possible for - * multiple threads to purge dirty pages concurrently, and they use - * npurgatory to indicate the total number of pages all threads are - * attempting to purge. - */ - size_t npurgatory; - - /* - * Size/address-ordered trees of this arena's available runs. The trees - * are used for first-best-fit run allocation. + * Size/address-ordered tree of this arena's available runs. The tree + * is used for first-best-fit run allocation. */ arena_avail_tree_t runs_avail; + /* + * Unused dirty memory this arena manages. Dirty memory is conceptually + * tracked as an arbitrarily interleaved LRU of dirty runs and cached + * chunks, but the list linkage is actually semi-duplicated in order to + * avoid extra arena_chunk_map_misc_t space overhead. + * + * LRU-----------------------------------------------------------MRU + * + * /-- arena ---\ + * | | + * | | + * |------------| /- chunk -\ + * ...->|chunks_cache|<--------------------------->| /----\ |<--... + * |------------| | |node| | + * | | | | | | + * | | /- run -\ /- run -\ | | | | + * | | | | | | | | | | + * | | | | | | | | | | + * |------------| |-------| |-------| | |----| | + * ...->|runs_dirty |<-->|rd |<-->|rd |<---->|rd |<----... + * |------------| |-------| |-------| | |----| | + * | | | | | | | | | | + * | | | | | | | \----/ | + * | | \-------/ \-------/ | | + * | | | | + * | | | | + * \------------/ \---------/ + */ + arena_runs_dirty_link_t runs_dirty; + extent_node_t chunks_cache; + + /* Extant huge allocations. */ + ql_head(extent_node_t) huge; + /* Synchronizes all huge allocation/update/deallocation. */ + malloc_mutex_t huge_mtx; + + /* + * Trees of chunks that were previously allocated (trees differ only in + * node ordering). These are used when allocating chunks, in an attempt + * to re-use address space. Depending on function, different tree + * orderings are needed, which is why there are two trees with the same + * contents. + */ + extent_tree_t chunks_szad_cached; + extent_tree_t chunks_ad_cached; + extent_tree_t chunks_szad_retained; + extent_tree_t chunks_ad_retained; + + malloc_mutex_t chunks_mtx; + /* Cache of nodes that were allocated via base_alloc(). */ + ql_head(extent_node_t) node_cache; + malloc_mutex_t node_cache_mtx; + + /* User-configurable chunk hook functions. */ + chunk_hooks_t chunk_hooks; + /* bins is used to store trees of free regions. */ arena_bin_t bins[NBINS]; }; +#endif /* JEMALLOC_ARENA_STRUCTS_B */ #endif /* JEMALLOC_H_STRUCTS */ /******************************************************************************/ #ifdef JEMALLOC_H_EXTERNS -extern ssize_t opt_lg_dirty_mult; -/* - * small_size2bin is a compact lookup table that rounds request sizes up to - * size classes. In order to reduce cache footprint, the table is compressed, - * and all accesses are via the SMALL_SIZE2BIN macro. - */ -extern uint8_t const small_size2bin[]; -#define SMALL_SIZE2BIN(s) (small_size2bin[(s-1) >> LG_TINY_MIN]) +static const size_t large_pad = +#ifdef JEMALLOC_CACHE_OBLIVIOUS + PAGE +#else + 0 +#endif + ; + +extern ssize_t opt_lg_dirty_mult; extern arena_bin_info_t arena_bin_info[NBINS]; -/* Number of large size classes. */ -#define nlclasses (chunk_npages - map_bias) +extern size_t map_bias; /* Number of arena chunk header pages. */ +extern size_t map_misc_offset; +extern size_t arena_maxrun; /* Max run size for arenas. */ +extern size_t arena_maxclass; /* Max size class for arenas. */ +extern unsigned nlclasses; /* Number of large size classes. */ +extern unsigned nhclasses; /* Number of huge size classes. */ +void arena_chunk_cache_maybe_insert(arena_t *arena, extent_node_t *node, + bool cache); +void arena_chunk_cache_maybe_remove(arena_t *arena, extent_node_t *node, + bool cache); +extent_node_t *arena_node_alloc(arena_t *arena); +void arena_node_dalloc(arena_t *arena, extent_node_t *node); +void *arena_chunk_alloc_huge(arena_t *arena, size_t usize, size_t alignment, + bool *zero); +void arena_chunk_dalloc_huge(arena_t *arena, void *chunk, size_t usize); +void arena_chunk_ralloc_huge_similar(arena_t *arena, void *chunk, + size_t oldsize, size_t usize); +void arena_chunk_ralloc_huge_shrink(arena_t *arena, void *chunk, + size_t oldsize, size_t usize); +bool arena_chunk_ralloc_huge_expand(arena_t *arena, void *chunk, + size_t oldsize, size_t usize, bool *zero); +ssize_t arena_lg_dirty_mult_get(arena_t *arena); +bool arena_lg_dirty_mult_set(arena_t *arena, ssize_t lg_dirty_mult); +void arena_maybe_purge(arena_t *arena); void arena_purge_all(arena_t *arena); void arena_tcache_fill_small(arena_t *arena, tcache_bin_t *tbin, - size_t binind, uint64_t prof_accumbytes); + index_t binind, uint64_t prof_accumbytes); void arena_alloc_junk_small(void *ptr, arena_bin_info_t *bin_info, bool zero); #ifdef JEMALLOC_JET @@ -418,19 +463,22 @@ void arena_dalloc_junk_small(void *ptr, arena_bin_info_t *bin_info); void arena_quarantine_junk_small(void *ptr, size_t usize); void *arena_malloc_small(arena_t *arena, size_t size, bool zero); void *arena_malloc_large(arena_t *arena, size_t size, bool zero); -void *arena_palloc(arena_t *arena, size_t size, size_t alignment, bool zero); +void *arena_palloc(tsd_t *tsd, arena_t *arena, size_t usize, + size_t alignment, bool zero, tcache_t *tcache); void arena_prof_promoted(const void *ptr, size_t size); -void arena_dalloc_bin_locked(arena_t *arena, arena_chunk_t *chunk, void *ptr, - arena_chunk_map_t *mapelm); +void arena_dalloc_bin_junked_locked(arena_t *arena, arena_chunk_t *chunk, + void *ptr, arena_chunk_map_bits_t *bitselm); void arena_dalloc_bin(arena_t *arena, arena_chunk_t *chunk, void *ptr, - size_t pageind, arena_chunk_map_t *mapelm); + size_t pageind, arena_chunk_map_bits_t *bitselm); void arena_dalloc_small(arena_t *arena, arena_chunk_t *chunk, void *ptr, size_t pageind); #ifdef JEMALLOC_JET typedef void (arena_dalloc_junk_large_t)(void *, size_t); extern arena_dalloc_junk_large_t *arena_dalloc_junk_large; +#else +void arena_dalloc_junk_large(void *ptr, size_t usize); #endif -void arena_dalloc_large_locked(arena_t *arena, arena_chunk_t *chunk, +void arena_dalloc_large_junked_locked(arena_t *arena, arena_chunk_t *chunk, void *ptr); void arena_dalloc_large(arena_t *arena, arena_chunk_t *chunk, void *ptr); #ifdef JEMALLOC_JET @@ -439,16 +487,18 @@ extern arena_ralloc_junk_large_t *arena_ralloc_junk_large; #endif bool arena_ralloc_no_move(void *ptr, size_t oldsize, size_t size, size_t extra, bool zero); -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); +void *arena_ralloc(tsd_t *tsd, arena_t *arena, void *ptr, size_t oldsize, + size_t size, size_t extra, size_t alignment, bool zero, tcache_t *tcache); 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); +bool arena_dss_prec_set(arena_t *arena, dss_prec_t dss_prec); +ssize_t arena_lg_dirty_mult_default_get(void); +bool arena_lg_dirty_mult_default_set(ssize_t lg_dirty_mult); +void arena_stats_merge(arena_t *arena, const char **dss, + ssize_t *lg_dirty_mult, size_t *nactive, size_t *ndirty, + arena_stats_t *astats, malloc_bin_stats_t *bstats, + malloc_large_stats_t *lstats, malloc_huge_stats_t *hstats); +arena_t *arena_new(unsigned ind); +bool arena_boot(void); void arena_prefork(arena_t *arena); void arena_postfork_parent(arena_t *arena); void arena_postfork_child(arena_t *arena); @@ -458,7 +508,14 @@ void arena_postfork_child(arena_t *arena); #ifdef JEMALLOC_H_INLINES #ifndef JEMALLOC_ENABLE_INLINE -arena_chunk_map_t *arena_mapp_get(arena_chunk_t *chunk, size_t pageind); +arena_chunk_map_bits_t *arena_bitselm_get(arena_chunk_t *chunk, + size_t pageind); +arena_chunk_map_misc_t *arena_miscelm_get(arena_chunk_t *chunk, + size_t pageind); +size_t arena_miscelm_to_pageind(arena_chunk_map_misc_t *miscelm); +void *arena_miscelm_to_rpages(arena_chunk_map_misc_t *miscelm); +arena_chunk_map_misc_t *arena_rd_to_miscelm(arena_runs_dirty_link_t *rd); +arena_chunk_map_misc_t *arena_run_to_miscelm(arena_run_t *run); size_t *arena_mapbitsp_get(arena_chunk_t *chunk, size_t pageind); size_t arena_mapbitsp_read(size_t *mapbitsp); size_t arena_mapbits_get(arena_chunk_t *chunk, size_t pageind); @@ -466,9 +523,10 @@ size_t arena_mapbits_unallocated_size_get(arena_chunk_t *chunk, size_t pageind); size_t arena_mapbits_large_size_get(arena_chunk_t *chunk, size_t pageind); size_t arena_mapbits_small_runind_get(arena_chunk_t *chunk, size_t pageind); -size_t arena_mapbits_binind_get(arena_chunk_t *chunk, size_t pageind); +index_t arena_mapbits_binind_get(arena_chunk_t *chunk, size_t pageind); size_t arena_mapbits_dirty_get(arena_chunk_t *chunk, size_t pageind); size_t arena_mapbits_unzeroed_get(arena_chunk_t *chunk, size_t pageind); +size_t arena_mapbits_decommitted_get(arena_chunk_t *chunk, size_t pageind); size_t arena_mapbits_large_get(arena_chunk_t *chunk, size_t pageind); size_t arena_mapbits_allocated_get(arena_chunk_t *chunk, size_t pageind); void arena_mapbitsp_write(size_t *mapbitsp, size_t mapbits); @@ -476,46 +534,108 @@ void arena_mapbits_unallocated_set(arena_chunk_t *chunk, size_t pageind, size_t size, size_t flags); void arena_mapbits_unallocated_size_set(arena_chunk_t *chunk, size_t pageind, size_t size); +void arena_mapbits_internal_set(arena_chunk_t *chunk, size_t pageind, + size_t flags); void arena_mapbits_large_set(arena_chunk_t *chunk, size_t pageind, size_t size, size_t flags); void arena_mapbits_large_binind_set(arena_chunk_t *chunk, size_t pageind, - size_t binind); + index_t binind); 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); + size_t runind, index_t binind, size_t flags); +void arena_metadata_allocated_add(arena_t *arena, size_t size); +void arena_metadata_allocated_sub(arena_t *arena, size_t size); +size_t arena_metadata_allocated_get(arena_t *arena); bool arena_prof_accum_impl(arena_t *arena, uint64_t accumbytes); bool arena_prof_accum_locked(arena_t *arena, uint64_t accumbytes); bool 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); +index_t arena_ptr_small_binind_get(const void *ptr, size_t mapbits); +index_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, const void *ptr); -prof_ctx_t *arena_prof_ctx_get(const void *ptr); -void arena_prof_ctx_set(const void *ptr, size_t usize, prof_ctx_t *ctx); -void *arena_malloc(arena_t *arena, size_t size, bool zero, bool try_tcache); +prof_tctx_t *arena_prof_tctx_get(const void *ptr); +void arena_prof_tctx_set(const void *ptr, prof_tctx_t *tctx); +void *arena_malloc(tsd_t *tsd, arena_t *arena, size_t size, bool zero, + tcache_t *tcache); +arena_t *arena_aalloc(const void *ptr); size_t arena_salloc(const void *ptr, bool demote); -void arena_dalloc(arena_t *arena, arena_chunk_t *chunk, void *ptr, - bool try_tcache); +void arena_dalloc(tsd_t *tsd, void *ptr, tcache_t *tcache); +void arena_sdalloc(tsd_t *tsd, void *ptr, size_t size, tcache_t *tcache); #endif #if (defined(JEMALLOC_ENABLE_INLINE) || defined(JEMALLOC_ARENA_C_)) # ifdef JEMALLOC_ARENA_INLINE_A -JEMALLOC_ALWAYS_INLINE arena_chunk_map_t * -arena_mapp_get(arena_chunk_t *chunk, size_t pageind) +JEMALLOC_ALWAYS_INLINE arena_chunk_map_bits_t * +arena_bitselm_get(arena_chunk_t *chunk, size_t pageind) { assert(pageind >= map_bias); assert(pageind < chunk_npages); - return (&chunk->map[pageind-map_bias]); + return (&chunk->map_bits[pageind-map_bias]); +} + +JEMALLOC_ALWAYS_INLINE arena_chunk_map_misc_t * +arena_miscelm_get(arena_chunk_t *chunk, size_t pageind) +{ + + assert(pageind >= map_bias); + assert(pageind < chunk_npages); + + return ((arena_chunk_map_misc_t *)((uintptr_t)chunk + + (uintptr_t)map_misc_offset) + pageind-map_bias); +} + +JEMALLOC_ALWAYS_INLINE size_t +arena_miscelm_to_pageind(arena_chunk_map_misc_t *miscelm) +{ + arena_chunk_t *chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(miscelm); + size_t pageind = ((uintptr_t)miscelm - ((uintptr_t)chunk + + map_misc_offset)) / sizeof(arena_chunk_map_misc_t) + map_bias; + + assert(pageind >= map_bias); + assert(pageind < chunk_npages); + + return (pageind); +} + +JEMALLOC_ALWAYS_INLINE void * +arena_miscelm_to_rpages(arena_chunk_map_misc_t *miscelm) +{ + arena_chunk_t *chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(miscelm); + size_t pageind = arena_miscelm_to_pageind(miscelm); + + return ((void *)((uintptr_t)chunk + (pageind << LG_PAGE))); +} + +JEMALLOC_ALWAYS_INLINE arena_chunk_map_misc_t * +arena_rd_to_miscelm(arena_runs_dirty_link_t *rd) +{ + arena_chunk_map_misc_t *miscelm = (arena_chunk_map_misc_t + *)((uintptr_t)rd - offsetof(arena_chunk_map_misc_t, rd)); + + assert(arena_miscelm_to_pageind(miscelm) >= map_bias); + assert(arena_miscelm_to_pageind(miscelm) < chunk_npages); + + return (miscelm); +} + +JEMALLOC_ALWAYS_INLINE arena_chunk_map_misc_t * +arena_run_to_miscelm(arena_run_t *run) +{ + arena_chunk_map_misc_t *miscelm = (arena_chunk_map_misc_t + *)((uintptr_t)run - offsetof(arena_chunk_map_misc_t, run)); + + assert(arena_miscelm_to_pageind(miscelm) >= map_bias); + assert(arena_miscelm_to_pageind(miscelm) < chunk_npages); + + return (miscelm); } JEMALLOC_ALWAYS_INLINE size_t * arena_mapbitsp_get(arena_chunk_t *chunk, size_t pageind) { - return (&arena_mapp_get(chunk, pageind)->bits); + return (&arena_bitselm_get(chunk, pageind)->bits); } JEMALLOC_ALWAYS_INLINE size_t @@ -539,7 +659,7 @@ arena_mapbits_unallocated_size_get(arena_chunk_t *chunk, size_t pageind) mapbits = arena_mapbits_get(chunk, pageind); assert((mapbits & (CHUNK_MAP_LARGE|CHUNK_MAP_ALLOCATED)) == 0); - return (mapbits & ~PAGE_MASK); + return ((mapbits & CHUNK_MAP_SIZE_MASK) >> CHUNK_MAP_SIZE_SHIFT); } JEMALLOC_ALWAYS_INLINE size_t @@ -550,7 +670,7 @@ arena_mapbits_large_size_get(arena_chunk_t *chunk, size_t pageind) mapbits = arena_mapbits_get(chunk, pageind); assert((mapbits & (CHUNK_MAP_LARGE|CHUNK_MAP_ALLOCATED)) == (CHUNK_MAP_LARGE|CHUNK_MAP_ALLOCATED)); - return (mapbits & ~PAGE_MASK); + return ((mapbits & CHUNK_MAP_SIZE_MASK) >> CHUNK_MAP_SIZE_SHIFT); } JEMALLOC_ALWAYS_INLINE size_t @@ -561,14 +681,14 @@ arena_mapbits_small_runind_get(arena_chunk_t *chunk, size_t pageind) mapbits = arena_mapbits_get(chunk, pageind); assert((mapbits & (CHUNK_MAP_LARGE|CHUNK_MAP_ALLOCATED)) == CHUNK_MAP_ALLOCATED); - return (mapbits >> LG_PAGE); + return (mapbits >> CHUNK_MAP_RUNIND_SHIFT); } -JEMALLOC_ALWAYS_INLINE size_t +JEMALLOC_ALWAYS_INLINE index_t arena_mapbits_binind_get(arena_chunk_t *chunk, size_t pageind) { size_t mapbits; - size_t binind; + index_t binind; mapbits = arena_mapbits_get(chunk, pageind); binind = (mapbits & CHUNK_MAP_BININD_MASK) >> CHUNK_MAP_BININD_SHIFT; @@ -582,6 +702,8 @@ arena_mapbits_dirty_get(arena_chunk_t *chunk, size_t pageind) size_t mapbits; mapbits = arena_mapbits_get(chunk, pageind); + assert((mapbits & CHUNK_MAP_DECOMMITTED) == 0 || (mapbits & + (CHUNK_MAP_DIRTY|CHUNK_MAP_UNZEROED)) == 0); return (mapbits & CHUNK_MAP_DIRTY); } @@ -591,9 +713,22 @@ arena_mapbits_unzeroed_get(arena_chunk_t *chunk, size_t pageind) size_t mapbits; mapbits = arena_mapbits_get(chunk, pageind); + assert((mapbits & CHUNK_MAP_DECOMMITTED) == 0 || (mapbits & + (CHUNK_MAP_DIRTY|CHUNK_MAP_UNZEROED)) == 0); return (mapbits & CHUNK_MAP_UNZEROED); } +JEMALLOC_ALWAYS_INLINE size_t +arena_mapbits_decommitted_get(arena_chunk_t *chunk, size_t pageind) +{ + size_t mapbits; + + mapbits = arena_mapbits_get(chunk, pageind); + assert((mapbits & CHUNK_MAP_DECOMMITTED) == 0 || (mapbits & + (CHUNK_MAP_DIRTY|CHUNK_MAP_UNZEROED)) == 0); + return (mapbits & CHUNK_MAP_DECOMMITTED); +} + JEMALLOC_ALWAYS_INLINE size_t arena_mapbits_large_get(arena_chunk_t *chunk, size_t pageind) { @@ -626,9 +761,12 @@ arena_mapbits_unallocated_set(arena_chunk_t *chunk, size_t pageind, size_t size, size_t *mapbitsp = arena_mapbitsp_get(chunk, pageind); assert((size & PAGE_MASK) == 0); - assert((flags & ~CHUNK_MAP_FLAGS_MASK) == 0); - assert((flags & (CHUNK_MAP_DIRTY|CHUNK_MAP_UNZEROED)) == flags); - arena_mapbitsp_write(mapbitsp, size | CHUNK_MAP_BININD_INVALID | flags); + assert(((size << CHUNK_MAP_SIZE_SHIFT) & ~CHUNK_MAP_SIZE_MASK) == 0); + assert((flags & CHUNK_MAP_FLAGS_MASK) == flags); + assert((flags & CHUNK_MAP_DECOMMITTED) == 0 || (flags & + (CHUNK_MAP_DIRTY|CHUNK_MAP_UNZEROED)) == 0); + arena_mapbitsp_write(mapbitsp, (size << CHUNK_MAP_SIZE_SHIFT) | + CHUNK_MAP_BININD_INVALID | flags); } JEMALLOC_ALWAYS_INLINE void @@ -639,8 +777,19 @@ arena_mapbits_unallocated_size_set(arena_chunk_t *chunk, size_t pageind, size_t mapbits = arena_mapbitsp_read(mapbitsp); assert((size & PAGE_MASK) == 0); + assert(((size << CHUNK_MAP_SIZE_SHIFT) & ~CHUNK_MAP_SIZE_MASK) == 0); assert((mapbits & (CHUNK_MAP_LARGE|CHUNK_MAP_ALLOCATED)) == 0); - arena_mapbitsp_write(mapbitsp, size | (mapbits & PAGE_MASK)); + arena_mapbitsp_write(mapbitsp, (size << CHUNK_MAP_SIZE_SHIFT) | (mapbits + & ~CHUNK_MAP_SIZE_MASK)); +} + +JEMALLOC_ALWAYS_INLINE void +arena_mapbits_internal_set(arena_chunk_t *chunk, size_t pageind, size_t flags) +{ + size_t *mapbitsp = arena_mapbitsp_get(chunk, pageind); + + assert((flags & CHUNK_MAP_UNZEROED) == flags); + arena_mapbitsp_write(mapbitsp, flags); } JEMALLOC_ALWAYS_INLINE void @@ -648,54 +797,63 @@ arena_mapbits_large_set(arena_chunk_t *chunk, size_t pageind, size_t size, size_t flags) { size_t *mapbitsp = arena_mapbitsp_get(chunk, pageind); - size_t mapbits = arena_mapbitsp_read(mapbitsp); - size_t unzeroed; assert((size & PAGE_MASK) == 0); - assert((flags & CHUNK_MAP_DIRTY) == flags); - unzeroed = mapbits & CHUNK_MAP_UNZEROED; /* Preserve unzeroed. */ - arena_mapbitsp_write(mapbitsp, size | CHUNK_MAP_BININD_INVALID | flags - | unzeroed | CHUNK_MAP_LARGE | CHUNK_MAP_ALLOCATED); + assert(((size << CHUNK_MAP_SIZE_SHIFT) & ~CHUNK_MAP_SIZE_MASK) == 0); + assert((flags & CHUNK_MAP_FLAGS_MASK) == flags); + assert((flags & CHUNK_MAP_DECOMMITTED) == 0 || (flags & + (CHUNK_MAP_DIRTY|CHUNK_MAP_UNZEROED)) == 0); + arena_mapbitsp_write(mapbitsp, (size << CHUNK_MAP_SIZE_SHIFT) | + CHUNK_MAP_BININD_INVALID | flags | CHUNK_MAP_LARGE | + CHUNK_MAP_ALLOCATED); } JEMALLOC_ALWAYS_INLINE void arena_mapbits_large_binind_set(arena_chunk_t *chunk, size_t pageind, - size_t binind) + index_t binind) { size_t *mapbitsp = arena_mapbitsp_get(chunk, pageind); size_t mapbits = arena_mapbitsp_read(mapbitsp); assert(binind <= BININD_INVALID); - assert(arena_mapbits_large_size_get(chunk, pageind) == PAGE); + assert(arena_mapbits_large_size_get(chunk, pageind) == LARGE_MINCLASS + + large_pad); arena_mapbitsp_write(mapbitsp, (mapbits & ~CHUNK_MAP_BININD_MASK) | (binind << CHUNK_MAP_BININD_SHIFT)); } JEMALLOC_ALWAYS_INLINE void arena_mapbits_small_set(arena_chunk_t *chunk, size_t pageind, size_t runind, - size_t binind, size_t flags) + index_t binind, size_t flags) { size_t *mapbitsp = arena_mapbitsp_get(chunk, pageind); - size_t mapbits = arena_mapbitsp_read(mapbitsp); - size_t unzeroed; assert(binind < BININD_INVALID); assert(pageind - runind >= map_bias); - assert((flags & CHUNK_MAP_DIRTY) == flags); - unzeroed = mapbits & CHUNK_MAP_UNZEROED; /* Preserve unzeroed. */ - arena_mapbitsp_write(mapbitsp, (runind << LG_PAGE) | (binind << - CHUNK_MAP_BININD_SHIFT) | flags | unzeroed | CHUNK_MAP_ALLOCATED); + assert((flags & CHUNK_MAP_UNZEROED) == flags); + arena_mapbitsp_write(mapbitsp, (runind << CHUNK_MAP_RUNIND_SHIFT) | + (binind << CHUNK_MAP_BININD_SHIFT) | flags | CHUNK_MAP_ALLOCATED); } -JEMALLOC_ALWAYS_INLINE void -arena_mapbits_unzeroed_set(arena_chunk_t *chunk, size_t pageind, - size_t unzeroed) +JEMALLOC_INLINE void +arena_metadata_allocated_add(arena_t *arena, size_t size) { - size_t *mapbitsp = arena_mapbitsp_get(chunk, pageind); - size_t mapbits = arena_mapbitsp_read(mapbitsp); - arena_mapbitsp_write(mapbitsp, (mapbits & ~CHUNK_MAP_UNZEROED) | - unzeroed); + atomic_add_z(&arena->stats.metadata_allocated, size); +} + +JEMALLOC_INLINE void +arena_metadata_allocated_sub(arena_t *arena, size_t size) +{ + + atomic_sub_z(&arena->stats.metadata_allocated, size); +} + +JEMALLOC_INLINE size_t +arena_metadata_allocated_get(arena_t *arena) +{ + + return (atomic_read_z(&arena->stats.metadata_allocated)); } JEMALLOC_INLINE bool @@ -719,7 +877,7 @@ arena_prof_accum_locked(arena_t *arena, uint64_t accumbytes) cassert(config_prof); - if (prof_interval == 0) + if (likely(prof_interval == 0)) return (false); return (arena_prof_accum_impl(arena, accumbytes)); } @@ -730,7 +888,7 @@ arena_prof_accum(arena_t *arena, uint64_t accumbytes) cassert(config_prof); - if (prof_interval == 0) + if (likely(prof_interval == 0)) return (false); { @@ -743,10 +901,10 @@ arena_prof_accum(arena_t *arena, uint64_t accumbytes) } } -JEMALLOC_ALWAYS_INLINE size_t +JEMALLOC_ALWAYS_INLINE index_t arena_ptr_small_binind_get(const void *ptr, size_t mapbits) { - size_t binind; + index_t binind; binind = (mapbits & CHUNK_MAP_BININD_MASK) >> CHUNK_MAP_BININD_SHIFT; @@ -755,27 +913,34 @@ arena_ptr_small_binind_get(const void *ptr, size_t mapbits) arena_t *arena; size_t pageind; size_t actual_mapbits; + size_t rpages_ind; arena_run_t *run; arena_bin_t *bin; - size_t actual_binind; + index_t run_binind, actual_binind; arena_bin_info_t *bin_info; + arena_chunk_map_misc_t *miscelm; + void *rpages; assert(binind != BININD_INVALID); assert(binind < NBINS); chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr); - arena = chunk->arena; + arena = extent_node_arena_get(&chunk->node); pageind = ((uintptr_t)ptr - (uintptr_t)chunk) >> LG_PAGE; actual_mapbits = arena_mapbits_get(chunk, pageind); assert(mapbits == actual_mapbits); assert(arena_mapbits_large_get(chunk, pageind) == 0); assert(arena_mapbits_allocated_get(chunk, pageind) != 0); - run = (arena_run_t *)((uintptr_t)chunk + (uintptr_t)((pageind - - (actual_mapbits >> LG_PAGE)) << LG_PAGE)); - bin = run->bin; + rpages_ind = pageind - arena_mapbits_small_runind_get(chunk, + pageind); + miscelm = arena_miscelm_get(chunk, rpages_ind); + run = &miscelm->run; + run_binind = run->binind; + bin = &arena->bins[run_binind]; actual_binind = bin - arena->bins; - assert(binind == actual_binind); + assert(run_binind == actual_binind); bin_info = &arena_bin_info[actual_binind]; - assert(((uintptr_t)ptr - ((uintptr_t)run + + rpages = arena_miscelm_to_rpages(miscelm); + assert(((uintptr_t)ptr - ((uintptr_t)rpages + (uintptr_t)bin_info->reg0_offset)) % bin_info->reg_interval == 0); } @@ -785,10 +950,10 @@ arena_ptr_small_binind_get(const void *ptr, size_t mapbits) # endif /* JEMALLOC_ARENA_INLINE_A */ # ifdef JEMALLOC_ARENA_INLINE_B -JEMALLOC_INLINE size_t +JEMALLOC_INLINE index_t arena_bin_index(arena_t *arena, arena_bin_t *bin) { - size_t binind = bin - arena->bins; + index_t binind = bin - arena->bins; assert(binind < NBINS); return (binind); } @@ -798,24 +963,26 @@ arena_run_regind(arena_run_t *run, arena_bin_info_t *bin_info, const void *ptr) { unsigned shift, diff, regind; size_t interval; + arena_chunk_map_misc_t *miscelm = arena_run_to_miscelm(run); + void *rpages = arena_miscelm_to_rpages(miscelm); /* * Freeing a pointer lower than region zero can cause assertion * failure. */ - assert((uintptr_t)ptr >= (uintptr_t)run + + assert((uintptr_t)ptr >= (uintptr_t)rpages + (uintptr_t)bin_info->reg0_offset); /* * Avoid doing division with a variable divisor if possible. Using * actual division here can reduce allocator throughput by over 20%! */ - diff = (unsigned)((uintptr_t)ptr - (uintptr_t)run - + diff = (unsigned)((uintptr_t)ptr - (uintptr_t)rpages - bin_info->reg0_offset); /* Rescale (factor powers of 2 out of the numerator and denominator). */ interval = bin_info->reg_interval; - shift = ffs(interval) - 1; + shift = jemalloc_ffs(interval) - 1; diff >>= shift; interval >>= shift; @@ -850,8 +1017,8 @@ arena_run_regind(arena_run_t *run, arena_bin_info_t *bin_info, const void *ptr) SIZE_INV(28), SIZE_INV(29), SIZE_INV(30), SIZE_INV(31) }; - if (interval <= ((sizeof(interval_invs) / sizeof(unsigned)) + - 2)) { + if (likely(interval <= ((sizeof(interval_invs) / + sizeof(unsigned)) + 2))) { regind = (diff * interval_invs[interval - 3]) >> SIZE_INV_SHIFT; } else @@ -865,113 +1032,96 @@ arena_run_regind(arena_run_t *run, arena_bin_info_t *bin_info, const void *ptr) return (regind); } -JEMALLOC_INLINE prof_ctx_t * -arena_prof_ctx_get(const void *ptr) +JEMALLOC_INLINE prof_tctx_t * +arena_prof_tctx_get(const void *ptr) { - prof_ctx_t *ret; + prof_tctx_t *ret; arena_chunk_t *chunk; - size_t pageind, mapbits; cassert(config_prof); assert(ptr != NULL); - assert(CHUNK_ADDR2BASE(ptr) != ptr); chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr); - pageind = ((uintptr_t)ptr - (uintptr_t)chunk) >> LG_PAGE; - mapbits = arena_mapbits_get(chunk, pageind); - assert((mapbits & CHUNK_MAP_ALLOCATED) != 0); - if ((mapbits & CHUNK_MAP_LARGE) == 0) { - if (prof_promote) - ret = (prof_ctx_t *)(uintptr_t)1U; + if (likely(chunk != ptr)) { + size_t pageind = ((uintptr_t)ptr - (uintptr_t)chunk) >> LG_PAGE; + size_t mapbits = arena_mapbits_get(chunk, pageind); + assert((mapbits & CHUNK_MAP_ALLOCATED) != 0); + if (likely((mapbits & CHUNK_MAP_LARGE) == 0)) + ret = (prof_tctx_t *)(uintptr_t)1U; else { - arena_run_t *run = (arena_run_t *)((uintptr_t)chunk + - (uintptr_t)((pageind - (mapbits >> LG_PAGE)) << - LG_PAGE)); - size_t binind = arena_ptr_small_binind_get(ptr, - mapbits); - arena_bin_info_t *bin_info = &arena_bin_info[binind]; - unsigned regind; - - regind = arena_run_regind(run, bin_info, ptr); - ret = *(prof_ctx_t **)((uintptr_t)run + - bin_info->ctx0_offset + (regind * - sizeof(prof_ctx_t *))); + arena_chunk_map_misc_t *elm = arena_miscelm_get(chunk, + pageind); + ret = atomic_read_p(&elm->prof_tctx_pun); } } else - ret = arena_mapp_get(chunk, pageind)->prof_ctx; + ret = huge_prof_tctx_get(ptr); return (ret); } JEMALLOC_INLINE void -arena_prof_ctx_set(const void *ptr, size_t usize, prof_ctx_t *ctx) +arena_prof_tctx_set(const void *ptr, prof_tctx_t *tctx) { arena_chunk_t *chunk; - size_t pageind; cassert(config_prof); assert(ptr != NULL); - assert(CHUNK_ADDR2BASE(ptr) != ptr); chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr); - pageind = ((uintptr_t)ptr - (uintptr_t)chunk) >> LG_PAGE; - assert(arena_mapbits_allocated_get(chunk, pageind) != 0); + if (likely(chunk != ptr)) { + size_t pageind = ((uintptr_t)ptr - (uintptr_t)chunk) >> LG_PAGE; + assert(arena_mapbits_allocated_get(chunk, pageind) != 0); - if (usize > SMALL_MAXCLASS || (prof_promote && - ((uintptr_t)ctx != (uintptr_t)1U || arena_mapbits_large_get(chunk, - pageind) != 0))) { - assert(arena_mapbits_large_get(chunk, pageind) != 0); - arena_mapp_get(chunk, pageind)->prof_ctx = ctx; - } else { - assert(arena_mapbits_large_get(chunk, pageind) == 0); - if (prof_promote == false) { - size_t mapbits = arena_mapbits_get(chunk, pageind); - arena_run_t *run = (arena_run_t *)((uintptr_t)chunk + - (uintptr_t)((pageind - (mapbits >> LG_PAGE)) << - LG_PAGE)); - size_t binind; - arena_bin_info_t *bin_info; - unsigned regind; - - binind = arena_ptr_small_binind_get(ptr, mapbits); - bin_info = &arena_bin_info[binind]; - regind = arena_run_regind(run, bin_info, ptr); - - *((prof_ctx_t **)((uintptr_t)run + - bin_info->ctx0_offset + (regind * sizeof(prof_ctx_t - *)))) = ctx; + if (unlikely(arena_mapbits_large_get(chunk, pageind) != 0)) { + arena_chunk_map_misc_t *elm = arena_miscelm_get(chunk, + pageind); + atomic_write_p(&elm->prof_tctx_pun, tctx); } - } + } else + huge_prof_tctx_set(ptr, tctx); } JEMALLOC_ALWAYS_INLINE void * -arena_malloc(arena_t *arena, size_t size, bool zero, bool try_tcache) +arena_malloc(tsd_t *tsd, arena_t *arena, size_t size, bool zero, + tcache_t *tcache) { - tcache_t *tcache; assert(size != 0); - assert(size <= arena_maxclass); - if (size <= SMALL_MAXCLASS) { - if (try_tcache && (tcache = tcache_get(true)) != NULL) - return (tcache_alloc_small(tcache, size, zero)); - else { - return (arena_malloc_small(choose_arena(arena), size, + arena = arena_choose(tsd, arena); + if (unlikely(arena == NULL)) + return (NULL); + + if (likely(size <= SMALL_MAXCLASS)) { + if (likely(tcache != NULL)) { + return (tcache_alloc_small(tsd, arena, tcache, size, zero)); - } - } else { + } else + return (arena_malloc_small(arena, size, zero)); + } else if (likely(size <= arena_maxclass)) { /* * Initialize tcache after checking size in order to avoid * infinite recursion during tcache initialization. */ - if (try_tcache && size <= tcache_maxclass && (tcache = - tcache_get(true)) != NULL) - return (tcache_alloc_large(tcache, size, zero)); - else { - return (arena_malloc_large(choose_arena(arena), size, + if (likely(tcache != NULL) && size <= tcache_maxclass) { + return (tcache_alloc_large(tsd, arena, tcache, size, zero)); - } - } + } else + return (arena_malloc_large(arena, size, zero)); + } else + return (huge_malloc(tsd, arena, size, zero, tcache)); +} + +JEMALLOC_ALWAYS_INLINE arena_t * +arena_aalloc(const void *ptr) +{ + arena_chunk_t *chunk; + + chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr); + if (likely(chunk != ptr)) + return (extent_node_arena_get(&chunk->node)); + else + return (huge_aalloc(ptr)); } /* Return the size of the allocation pointed to by ptr. */ @@ -980,81 +1130,139 @@ arena_salloc(const void *ptr, bool demote) { size_t ret; arena_chunk_t *chunk; - size_t pageind, binind; + size_t pageind; + index_t binind; assert(ptr != NULL); - assert(CHUNK_ADDR2BASE(ptr) != ptr); chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr); - pageind = ((uintptr_t)ptr - (uintptr_t)chunk) >> LG_PAGE; - assert(arena_mapbits_allocated_get(chunk, pageind) != 0); - binind = arena_mapbits_binind_get(chunk, pageind); - if (binind == BININD_INVALID || (config_prof && demote == false && - prof_promote && arena_mapbits_large_get(chunk, pageind) != 0)) { - /* - * Large allocation. In the common case (demote == true), and - * as this is an inline function, most callers will only end up - * looking at binind to determine that ptr is a small - * allocation. - */ - assert(((uintptr_t)ptr & PAGE_MASK) == 0); - ret = arena_mapbits_large_size_get(chunk, pageind); - assert(ret != 0); - assert(pageind + (ret>>LG_PAGE) <= chunk_npages); - assert(ret == PAGE || arena_mapbits_large_size_get(chunk, - pageind+(ret>>LG_PAGE)-1) == 0); - assert(binind == arena_mapbits_binind_get(chunk, - pageind+(ret>>LG_PAGE)-1)); - assert(arena_mapbits_dirty_get(chunk, pageind) == - arena_mapbits_dirty_get(chunk, pageind+(ret>>LG_PAGE)-1)); - } else { - /* - * Small allocation (possibly promoted to a large object due to - * prof_promote). - */ - assert(arena_mapbits_large_get(chunk, pageind) != 0 || - arena_ptr_small_binind_get(ptr, arena_mapbits_get(chunk, - pageind)) == binind); - ret = arena_bin_info[binind].reg_size; - } + if (likely(chunk != ptr)) { + pageind = ((uintptr_t)ptr - (uintptr_t)chunk) >> LG_PAGE; + assert(arena_mapbits_allocated_get(chunk, pageind) != 0); + binind = arena_mapbits_binind_get(chunk, pageind); + if (unlikely(binind == BININD_INVALID || (config_prof && !demote + && arena_mapbits_large_get(chunk, pageind) != 0))) { + /* + * Large allocation. In the common case (demote), and + * as this is an inline function, most callers will only + * end up looking at binind to determine that ptr is a + * small allocation. + */ + assert(config_cache_oblivious || ((uintptr_t)ptr & + PAGE_MASK) == 0); + ret = arena_mapbits_large_size_get(chunk, pageind) - + large_pad; + assert(ret != 0); + assert(pageind + ((ret+large_pad)>>LG_PAGE) <= + chunk_npages); + assert(arena_mapbits_dirty_get(chunk, pageind) == + arena_mapbits_dirty_get(chunk, + pageind+((ret+large_pad)>>LG_PAGE)-1)); + } else { + /* + * Small allocation (possibly promoted to a large + * object). + */ + assert(arena_mapbits_large_get(chunk, pageind) != 0 || + arena_ptr_small_binind_get(ptr, + arena_mapbits_get(chunk, pageind)) == binind); + ret = index2size(binind); + } + } else + ret = huge_salloc(ptr); return (ret); } JEMALLOC_ALWAYS_INLINE void -arena_dalloc(arena_t *arena, arena_chunk_t *chunk, void *ptr, bool try_tcache) +arena_dalloc(tsd_t *tsd, void *ptr, tcache_t *tcache) { + arena_chunk_t *chunk; size_t pageind, mapbits; - tcache_t *tcache; - assert(arena != NULL); - assert(chunk->arena == arena); assert(ptr != NULL); - assert(CHUNK_ADDR2BASE(ptr) != ptr); - pageind = ((uintptr_t)ptr - (uintptr_t)chunk) >> LG_PAGE; - mapbits = arena_mapbits_get(chunk, pageind); - assert(arena_mapbits_allocated_get(chunk, pageind) != 0); - if ((mapbits & CHUNK_MAP_LARGE) == 0) { - /* Small allocation. */ - if (try_tcache && (tcache = tcache_get(false)) != NULL) { - size_t binind; + chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr); + if (likely(chunk != ptr)) { + pageind = ((uintptr_t)ptr - (uintptr_t)chunk) >> LG_PAGE; + mapbits = arena_mapbits_get(chunk, pageind); + assert(arena_mapbits_allocated_get(chunk, pageind) != 0); + if (likely((mapbits & CHUNK_MAP_LARGE) == 0)) { + /* Small allocation. */ + if (likely(tcache != NULL)) { + index_t binind = arena_ptr_small_binind_get(ptr, + mapbits); + tcache_dalloc_small(tsd, tcache, ptr, binind); + } else { + arena_dalloc_small(extent_node_arena_get( + &chunk->node), chunk, ptr, pageind); + } + } else { + size_t size = arena_mapbits_large_size_get(chunk, + pageind); - binind = arena_ptr_small_binind_get(ptr, mapbits); - tcache_dalloc_small(tcache, ptr, binind); - } else - arena_dalloc_small(arena, chunk, ptr, pageind); - } else { - size_t size = arena_mapbits_large_size_get(chunk, pageind); + assert(config_cache_oblivious || ((uintptr_t)ptr & + PAGE_MASK) == 0); - assert(((uintptr_t)ptr & PAGE_MASK) == 0); + if (likely(tcache != NULL) && size - large_pad <= + tcache_maxclass) { + tcache_dalloc_large(tsd, tcache, ptr, size - + large_pad); + } else { + arena_dalloc_large(extent_node_arena_get( + &chunk->node), chunk, ptr); + } + } + } else + huge_dalloc(tsd, ptr, tcache); +} - if (try_tcache && size <= tcache_maxclass && (tcache = - tcache_get(false)) != NULL) { - tcache_dalloc_large(tcache, ptr, size); - } else - arena_dalloc_large(arena, chunk, ptr); - } +JEMALLOC_ALWAYS_INLINE void +arena_sdalloc(tsd_t *tsd, void *ptr, size_t size, tcache_t *tcache) +{ + arena_chunk_t *chunk; + + chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr); + if (likely(chunk != ptr)) { + if (config_prof && opt_prof) { + size_t pageind = ((uintptr_t)ptr - (uintptr_t)chunk) >> + LG_PAGE; + assert(arena_mapbits_allocated_get(chunk, pageind) != 0); + if (arena_mapbits_large_get(chunk, pageind) != 0) { + /* + * Make sure to use promoted size, not request + * size. + */ + size = arena_mapbits_large_size_get(chunk, + pageind) - large_pad; + } + } + assert(s2u(size) == s2u(arena_salloc(ptr, false))); + + if (likely(size <= SMALL_MAXCLASS)) { + /* Small allocation. */ + if (likely(tcache != NULL)) { + index_t binind = size2index(size); + tcache_dalloc_small(tsd, tcache, ptr, binind); + } else { + size_t pageind = ((uintptr_t)ptr - + (uintptr_t)chunk) >> LG_PAGE; + arena_dalloc_small(extent_node_arena_get( + &chunk->node), chunk, ptr, pageind); + } + } else { + assert(config_cache_oblivious || ((uintptr_t)ptr & + PAGE_MASK) == 0); + + if (likely(tcache != NULL) && size <= tcache_maxclass) + tcache_dalloc_large(tsd, tcache, ptr, size); + else { + arena_dalloc_large(extent_node_arena_get( + &chunk->node), chunk, ptr); + } + } + } else + huge_dalloc(tsd, ptr, tcache); } # endif /* JEMALLOC_ARENA_INLINE_B */ #endif diff --git a/contrib/jemalloc/include/jemalloc/internal/atomic.h b/contrib/jemalloc/include/jemalloc/internal/atomic.h index 11a7b47fe0f9..a9aad35d1213 100644 --- a/contrib/jemalloc/include/jemalloc/internal/atomic.h +++ b/contrib/jemalloc/include/jemalloc/internal/atomic.h @@ -11,6 +11,7 @@ #define atomic_read_uint64(p) atomic_add_uint64(p, 0) #define atomic_read_uint32(p) atomic_add_uint32(p, 0) +#define atomic_read_p(p) atomic_add_p(p, NULL) #define atomic_read_z(p) atomic_add_z(p, 0) #define atomic_read_u(p) atomic_add_u(p, 0) @@ -18,89 +19,139 @@ /******************************************************************************/ #ifdef JEMALLOC_H_INLINES +/* + * All arithmetic functions return the arithmetic result of the atomic + * operation. Some atomic operation APIs return the value prior to mutation, in + * which case the following functions must redundantly compute the result so + * that it can be returned. These functions are normally inlined, so the extra + * operations can be optimized away if the return values aren't used by the + * callers. + * + * atomic_read_( *p) { return (*p); } + * atomic_add_( *p, x) { return (*p + x); } + * atomic_sub_( *p, x) { return (*p - x); } + * bool atomic_cas_( *p, c, s) + * { + * if (*p != c) + * return (true); + * *p = s; + * return (false); + * } + * void atomic_write_( *p, x) { *p = x; } + */ + #ifndef JEMALLOC_ENABLE_INLINE uint64_t atomic_add_uint64(uint64_t *p, uint64_t x); uint64_t atomic_sub_uint64(uint64_t *p, uint64_t x); +bool atomic_cas_uint64(uint64_t *p, uint64_t c, uint64_t s); +void atomic_write_uint64(uint64_t *p, uint64_t x); uint32_t atomic_add_uint32(uint32_t *p, uint32_t x); uint32_t atomic_sub_uint32(uint32_t *p, uint32_t x); +bool atomic_cas_uint32(uint32_t *p, uint32_t c, uint32_t s); +void atomic_write_uint32(uint32_t *p, uint32_t x); +void *atomic_add_p(void **p, void *x); +void *atomic_sub_p(void **p, void *x); +bool atomic_cas_p(void **p, void *c, void *s); +void atomic_write_p(void **p, const void *x); size_t atomic_add_z(size_t *p, size_t x); size_t atomic_sub_z(size_t *p, size_t x); +bool atomic_cas_z(size_t *p, size_t c, size_t s); +void atomic_write_z(size_t *p, size_t x); unsigned atomic_add_u(unsigned *p, unsigned x); unsigned atomic_sub_u(unsigned *p, unsigned x); +bool atomic_cas_u(unsigned *p, unsigned c, unsigned s); +void atomic_write_u(unsigned *p, unsigned x); #endif #if (defined(JEMALLOC_ENABLE_INLINE) || defined(JEMALLOC_ATOMIC_C_)) /******************************************************************************/ /* 64-bit operations. */ #if (LG_SIZEOF_PTR == 3 || LG_SIZEOF_INT == 3) -# ifdef __GCC_HAVE_SYNC_COMPARE_AND_SWAP_8 -JEMALLOC_INLINE uint64_t -atomic_add_uint64(uint64_t *p, uint64_t x) -{ - - return (__sync_add_and_fetch(p, x)); -} - -JEMALLOC_INLINE uint64_t -atomic_sub_uint64(uint64_t *p, uint64_t x) -{ - - return (__sync_sub_and_fetch(p, x)); -} -#elif (defined(_MSC_VER)) -JEMALLOC_INLINE uint64_t -atomic_add_uint64(uint64_t *p, uint64_t x) -{ - - return (InterlockedExchangeAdd64(p, x)); -} - -JEMALLOC_INLINE uint64_t -atomic_sub_uint64(uint64_t *p, uint64_t x) -{ - - return (InterlockedExchangeAdd64(p, -((int64_t)x))); -} -#elif (defined(JEMALLOC_OSATOMIC)) -JEMALLOC_INLINE uint64_t -atomic_add_uint64(uint64_t *p, uint64_t x) -{ - - return (OSAtomicAdd64((int64_t)x, (int64_t *)p)); -} - -JEMALLOC_INLINE uint64_t -atomic_sub_uint64(uint64_t *p, uint64_t x) -{ - - return (OSAtomicAdd64(-((int64_t)x), (int64_t *)p)); -} -# elif (defined(__amd64__) || defined(__x86_64__)) +# if (defined(__amd64__) || defined(__x86_64__)) JEMALLOC_INLINE uint64_t atomic_add_uint64(uint64_t *p, uint64_t x) { + uint64_t t = x; asm volatile ( "lock; xaddq %0, %1;" - : "+r" (x), "=m" (*p) /* Outputs. */ + : "+r" (t), "=m" (*p) /* Outputs. */ : "m" (*p) /* Inputs. */ ); - return (x); + return (t + x); } JEMALLOC_INLINE uint64_t atomic_sub_uint64(uint64_t *p, uint64_t x) { + uint64_t t; x = (uint64_t)(-(int64_t)x); + t = x; asm volatile ( "lock; xaddq %0, %1;" - : "+r" (x), "=m" (*p) /* Outputs. */ + : "+r" (t), "=m" (*p) /* Outputs. */ : "m" (*p) /* Inputs. */ ); - return (x); + return (t + x); +} + +JEMALLOC_INLINE bool +atomic_cas_uint64(uint64_t *p, uint64_t c, uint64_t s) +{ + uint8_t success; + + asm volatile ( + "lock; cmpxchgq %4, %0;" + "sete %1;" + : "=m" (*p), "=a" (success) /* Outputs. */ + : "m" (*p), "a" (c), "r" (s) /* Inputs. */ + : "memory" /* Clobbers. */ + ); + + return (!(bool)success); +} + +JEMALLOC_INLINE void +atomic_write_uint64(uint64_t *p, uint64_t x) +{ + + asm volatile ( + "xchgq %1, %0;" /* Lock is implied by xchgq. */ + : "=m" (*p), "+r" (x) /* Outputs. */ + : "m" (*p) /* Inputs. */ + : "memory" /* Clobbers. */ + ); +} +# elif (defined(JEMALLOC_C11ATOMICS)) +JEMALLOC_INLINE uint64_t +atomic_add_uint64(uint64_t *p, uint64_t x) +{ + volatile atomic_uint_least64_t *a = (volatile atomic_uint_least64_t *)p; + return (atomic_fetch_add(a, x) + x); +} + +JEMALLOC_INLINE uint64_t +atomic_sub_uint64(uint64_t *p, uint64_t x) +{ + volatile atomic_uint_least64_t *a = (volatile atomic_uint_least64_t *)p; + return (atomic_fetch_sub(a, x) - x); +} + +JEMALLOC_INLINE bool +atomic_cas_uint64(uint64_t *p, uint64_t c, uint64_t s) +{ + volatile atomic_uint_least64_t *a = (volatile atomic_uint_least64_t *)p; + return (!atomic_compare_exchange_strong(a, &c, s)); +} + +JEMALLOC_INLINE void +atomic_write_uint64(uint64_t *p, uint64_t x) +{ + volatile atomic_uint_least64_t *a = (volatile atomic_uint_least64_t *)p; + atomic_store(a, x); } # elif (defined(JEMALLOC_ATOMIC9)) JEMALLOC_INLINE uint64_t @@ -124,7 +175,88 @@ atomic_sub_uint64(uint64_t *p, uint64_t x) return (atomic_fetchadd_long(p, (unsigned long)(-(long)x)) - x); } -# elif (defined(JE_FORCE_SYNC_COMPARE_AND_SWAP_8)) + +JEMALLOC_INLINE bool +atomic_cas_uint64(uint64_t *p, uint64_t c, uint64_t s) +{ + + assert(sizeof(uint64_t) == sizeof(unsigned long)); + + return (!atomic_cmpset_long(p, (unsigned long)c, (unsigned long)s)); +} + +JEMALLOC_INLINE void +atomic_write_uint64(uint64_t *p, uint64_t x) +{ + + assert(sizeof(uint64_t) == sizeof(unsigned long)); + + atomic_store_rel_long(p, x); +} +# elif (defined(JEMALLOC_OSATOMIC)) +JEMALLOC_INLINE uint64_t +atomic_add_uint64(uint64_t *p, uint64_t x) +{ + + return (OSAtomicAdd64((int64_t)x, (int64_t *)p)); +} + +JEMALLOC_INLINE uint64_t +atomic_sub_uint64(uint64_t *p, uint64_t x) +{ + + return (OSAtomicAdd64(-((int64_t)x), (int64_t *)p)); +} + +JEMALLOC_INLINE bool +atomic_cas_uint64(uint64_t *p, uint64_t c, uint64_t s) +{ + + return (!OSAtomicCompareAndSwap64(c, s, (int64_t *)p)); +} + +JEMALLOC_INLINE void +atomic_write_uint64(uint64_t *p, uint64_t x) +{ + uint64_t o; + + /*The documented OSAtomic*() API does not expose an atomic exchange. */ + do { + o = atomic_read_uint64(p); + } while (atomic_cas_uint64(p, o, x)); +} +# elif (defined(_MSC_VER)) +JEMALLOC_INLINE uint64_t +atomic_add_uint64(uint64_t *p, uint64_t x) +{ + + return (InterlockedExchangeAdd64(p, x) + x); +} + +JEMALLOC_INLINE uint64_t +atomic_sub_uint64(uint64_t *p, uint64_t x) +{ + + return (InterlockedExchangeAdd64(p, -((int64_t)x)) - x); +} + +JEMALLOC_INLINE bool +atomic_cas_uint64(uint64_t *p, uint64_t c, uint64_t s) +{ + uint64_t o; + + o = InterlockedCompareExchange64(p, s, c); + return (o != c); +} + +JEMALLOC_INLINE void +atomic_write_uint64(uint64_t *p, uint64_t x) +{ + + InterlockedExchange64(p, x); +} +# elif (defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_8) || \ + defined(JE_FORCE_SYNC_COMPARE_AND_SWAP_8)) JEMALLOC_INLINE uint64_t atomic_add_uint64(uint64_t *p, uint64_t x) { @@ -138,6 +270,20 @@ atomic_sub_uint64(uint64_t *p, uint64_t x) return (__sync_sub_and_fetch(p, x)); } + +JEMALLOC_INLINE bool +atomic_cas_uint64(uint64_t *p, uint64_t c, uint64_t s) +{ + + return (!__sync_bool_compare_and_swap(p, c, s)); +} + +JEMALLOC_INLINE void +atomic_write_uint64(uint64_t *p, uint64_t x) +{ + + __sync_lock_test_and_set(p, x); +} # else # error "Missing implementation for 64-bit atomic operations" # endif @@ -145,74 +291,91 @@ atomic_sub_uint64(uint64_t *p, uint64_t x) /******************************************************************************/ /* 32-bit operations. */ -#ifdef __GCC_HAVE_SYNC_COMPARE_AND_SWAP_4 -JEMALLOC_INLINE uint32_t -atomic_add_uint32(uint32_t *p, uint32_t x) -{ - - return (__sync_add_and_fetch(p, x)); -} - -JEMALLOC_INLINE uint32_t -atomic_sub_uint32(uint32_t *p, uint32_t x) -{ - - return (__sync_sub_and_fetch(p, x)); -} -#elif (defined(_MSC_VER)) -JEMALLOC_INLINE uint32_t -atomic_add_uint32(uint32_t *p, uint32_t x) -{ - - return (InterlockedExchangeAdd(p, x)); -} - -JEMALLOC_INLINE uint32_t -atomic_sub_uint32(uint32_t *p, uint32_t x) -{ - - return (InterlockedExchangeAdd(p, -((int32_t)x))); -} -#elif (defined(JEMALLOC_OSATOMIC)) -JEMALLOC_INLINE uint32_t -atomic_add_uint32(uint32_t *p, uint32_t x) -{ - - return (OSAtomicAdd32((int32_t)x, (int32_t *)p)); -} - -JEMALLOC_INLINE uint32_t -atomic_sub_uint32(uint32_t *p, uint32_t x) -{ - - return (OSAtomicAdd32(-((int32_t)x), (int32_t *)p)); -} -#elif (defined(__i386__) || defined(__amd64__) || defined(__x86_64__)) +#if (defined(__i386__) || defined(__amd64__) || defined(__x86_64__)) JEMALLOC_INLINE uint32_t atomic_add_uint32(uint32_t *p, uint32_t x) { + uint32_t t = x; asm volatile ( "lock; xaddl %0, %1;" - : "+r" (x), "=m" (*p) /* Outputs. */ + : "+r" (t), "=m" (*p) /* Outputs. */ : "m" (*p) /* Inputs. */ ); - return (x); + return (t + x); } JEMALLOC_INLINE uint32_t atomic_sub_uint32(uint32_t *p, uint32_t x) { + uint32_t t; x = (uint32_t)(-(int32_t)x); + t = x; asm volatile ( "lock; xaddl %0, %1;" - : "+r" (x), "=m" (*p) /* Outputs. */ + : "+r" (t), "=m" (*p) /* Outputs. */ : "m" (*p) /* Inputs. */ ); - return (x); + return (t + x); +} + +JEMALLOC_INLINE bool +atomic_cas_uint32(uint32_t *p, uint32_t c, uint32_t s) +{ + uint8_t success; + + asm volatile ( + "lock; cmpxchgl %4, %0;" + "sete %1;" + : "=m" (*p), "=a" (success) /* Outputs. */ + : "m" (*p), "a" (c), "r" (s) /* Inputs. */ + : "memory" + ); + + return (!(bool)success); +} + +JEMALLOC_INLINE void +atomic_write_uint32(uint32_t *p, uint32_t x) +{ + + asm volatile ( + "xchgl %1, %0;" /* Lock is implied by xchgl. */ + : "=m" (*p), "+r" (x) /* Outputs. */ + : "m" (*p) /* Inputs. */ + : "memory" /* Clobbers. */ + ); +} +# elif (defined(JEMALLOC_C11ATOMICS)) +JEMALLOC_INLINE uint32_t +atomic_add_uint32(uint32_t *p, uint32_t x) +{ + volatile atomic_uint_least32_t *a = (volatile atomic_uint_least32_t *)p; + return (atomic_fetch_add(a, x) + x); +} + +JEMALLOC_INLINE uint32_t +atomic_sub_uint32(uint32_t *p, uint32_t x) +{ + volatile atomic_uint_least32_t *a = (volatile atomic_uint_least32_t *)p; + return (atomic_fetch_sub(a, x) - x); +} + +JEMALLOC_INLINE bool +atomic_cas_uint32(uint32_t *p, uint32_t c, uint32_t s) +{ + volatile atomic_uint_least32_t *a = (volatile atomic_uint_least32_t *)p; + return (!atomic_compare_exchange_strong(a, &c, s)); +} + +JEMALLOC_INLINE void +atomic_write_uint32(uint32_t *p, uint32_t x) +{ + volatile atomic_uint_least32_t *a = (volatile atomic_uint_least32_t *)p; + atomic_store(a, x); } #elif (defined(JEMALLOC_ATOMIC9)) JEMALLOC_INLINE uint32_t @@ -228,7 +391,84 @@ atomic_sub_uint32(uint32_t *p, uint32_t x) return (atomic_fetchadd_32(p, (uint32_t)(-(int32_t)x)) - x); } -#elif (defined(JE_FORCE_SYNC_COMPARE_AND_SWAP_4)) + +JEMALLOC_INLINE bool +atomic_cas_uint32(uint32_t *p, uint32_t c, uint32_t s) +{ + + return (!atomic_cmpset_32(p, c, s)); +} + +JEMALLOC_INLINE void +atomic_write_uint32(uint32_t *p, uint32_t x) +{ + + atomic_store_rel_32(p, x); +} +#elif (defined(JEMALLOC_OSATOMIC)) +JEMALLOC_INLINE uint32_t +atomic_add_uint32(uint32_t *p, uint32_t x) +{ + + return (OSAtomicAdd32((int32_t)x, (int32_t *)p)); +} + +JEMALLOC_INLINE uint32_t +atomic_sub_uint32(uint32_t *p, uint32_t x) +{ + + return (OSAtomicAdd32(-((int32_t)x), (int32_t *)p)); +} + +JEMALLOC_INLINE bool +atomic_cas_uint32(uint32_t *p, uint32_t c, uint32_t s) +{ + + return (!OSAtomicCompareAndSwap32(c, s, (int32_t *)p)); +} + +JEMALLOC_INLINE void +atomic_write_uint32(uint32_t *p, uint32_t x) +{ + uint32_t o; + + /*The documented OSAtomic*() API does not expose an atomic exchange. */ + do { + o = atomic_read_uint32(p); + } while (atomic_cas_uint32(p, o, x)); +} +#elif (defined(_MSC_VER)) +JEMALLOC_INLINE uint32_t +atomic_add_uint32(uint32_t *p, uint32_t x) +{ + + return (InterlockedExchangeAdd(p, x) + x); +} + +JEMALLOC_INLINE uint32_t +atomic_sub_uint32(uint32_t *p, uint32_t x) +{ + + return (InterlockedExchangeAdd(p, -((int32_t)x)) - x); +} + +JEMALLOC_INLINE bool +atomic_cas_uint32(uint32_t *p, uint32_t c, uint32_t s) +{ + uint32_t o; + + o = InterlockedCompareExchange(p, s, c); + return (o != c); +} + +JEMALLOC_INLINE void +atomic_write_uint32(uint32_t *p, uint32_t x) +{ + + InterlockedExchange(p, x); +} +#elif (defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4) || \ + defined(JE_FORCE_SYNC_COMPARE_AND_SWAP_4)) JEMALLOC_INLINE uint32_t atomic_add_uint32(uint32_t *p, uint32_t x) { @@ -242,10 +482,72 @@ atomic_sub_uint32(uint32_t *p, uint32_t x) return (__sync_sub_and_fetch(p, x)); } + +JEMALLOC_INLINE bool +atomic_cas_uint32(uint32_t *p, uint32_t c, uint32_t s) +{ + + return (!__sync_bool_compare_and_swap(p, c, s)); +} + +JEMALLOC_INLINE void +atomic_write_uint32(uint32_t *p, uint32_t x) +{ + + __sync_lock_test_and_set(p, x); +} #else # error "Missing implementation for 32-bit atomic operations" #endif +/******************************************************************************/ +/* Pointer operations. */ +JEMALLOC_INLINE void * +atomic_add_p(void **p, void *x) +{ + +#if (LG_SIZEOF_PTR == 3) + return ((void *)atomic_add_uint64((uint64_t *)p, (uint64_t)x)); +#elif (LG_SIZEOF_PTR == 2) + return ((void *)atomic_add_uint32((uint32_t *)p, (uint32_t)x)); +#endif +} + +JEMALLOC_INLINE void * +atomic_sub_p(void **p, void *x) +{ + +#if (LG_SIZEOF_PTR == 3) + return ((void *)atomic_add_uint64((uint64_t *)p, + (uint64_t)-((int64_t)x))); +#elif (LG_SIZEOF_PTR == 2) + return ((void *)atomic_add_uint32((uint32_t *)p, + (uint32_t)-((int32_t)x))); +#endif +} + +JEMALLOC_INLINE bool +atomic_cas_p(void **p, void *c, void *s) +{ + +#if (LG_SIZEOF_PTR == 3) + return (atomic_cas_uint64((uint64_t *)p, (uint64_t)c, (uint64_t)s)); +#elif (LG_SIZEOF_PTR == 2) + return (atomic_cas_uint32((uint32_t *)p, (uint32_t)c, (uint32_t)s)); +#endif +} + +JEMALLOC_INLINE void +atomic_write_p(void **p, const void *x) +{ + +#if (LG_SIZEOF_PTR == 3) + atomic_write_uint64((uint64_t *)p, (uint64_t)x); +#elif (LG_SIZEOF_PTR == 2) + atomic_write_uint32((uint32_t *)p, (uint32_t)x); +#endif +} + /******************************************************************************/ /* size_t operations. */ JEMALLOC_INLINE size_t @@ -272,6 +574,28 @@ atomic_sub_z(size_t *p, size_t x) #endif } +JEMALLOC_INLINE bool +atomic_cas_z(size_t *p, size_t c, size_t s) +{ + +#if (LG_SIZEOF_PTR == 3) + return (atomic_cas_uint64((uint64_t *)p, (uint64_t)c, (uint64_t)s)); +#elif (LG_SIZEOF_PTR == 2) + return (atomic_cas_uint32((uint32_t *)p, (uint32_t)c, (uint32_t)s)); +#endif +} + +JEMALLOC_INLINE void +atomic_write_z(size_t *p, size_t x) +{ + +#if (LG_SIZEOF_PTR == 3) + atomic_write_uint64((uint64_t *)p, (uint64_t)x); +#elif (LG_SIZEOF_PTR == 2) + atomic_write_uint32((uint32_t *)p, (uint32_t)x); +#endif +} + /******************************************************************************/ /* unsigned operations. */ JEMALLOC_INLINE unsigned @@ -297,6 +621,29 @@ atomic_sub_u(unsigned *p, unsigned x) (uint32_t)-((int32_t)x))); #endif } + +JEMALLOC_INLINE bool +atomic_cas_u(unsigned *p, unsigned c, unsigned s) +{ + +#if (LG_SIZEOF_INT == 3) + return (atomic_cas_uint64((uint64_t *)p, (uint64_t)c, (uint64_t)s)); +#elif (LG_SIZEOF_INT == 2) + return (atomic_cas_uint32((uint32_t *)p, (uint32_t)c, (uint32_t)s)); +#endif +} + +JEMALLOC_INLINE void +atomic_write_u(unsigned *p, unsigned x) +{ + +#if (LG_SIZEOF_INT == 3) + atomic_write_uint64((uint64_t *)p, (uint64_t)x); +#elif (LG_SIZEOF_INT == 2) + atomic_write_uint32((uint32_t *)p, (uint32_t)x); +#endif +} + /******************************************************************************/ #endif diff --git a/contrib/jemalloc/include/jemalloc/internal/base.h b/contrib/jemalloc/include/jemalloc/internal/base.h index 9cf75ffb0b3c..39e46ee445d3 100644 --- a/contrib/jemalloc/include/jemalloc/internal/base.h +++ b/contrib/jemalloc/include/jemalloc/internal/base.h @@ -10,9 +10,7 @@ #ifdef JEMALLOC_H_EXTERNS void *base_alloc(size_t size); -void *base_calloc(size_t number, size_t size); -extent_node_t *base_node_alloc(void); -void base_node_dealloc(extent_node_t *node); +void base_stats_get(size_t *allocated, size_t *resident, size_t *mapped); bool base_boot(void); void base_prefork(void); void base_postfork_parent(void); diff --git a/contrib/jemalloc/include/jemalloc/internal/bitmap.h b/contrib/jemalloc/include/jemalloc/internal/bitmap.h index 605ebac58c17..fcc6005c795b 100644 --- a/contrib/jemalloc/include/jemalloc/internal/bitmap.h +++ b/contrib/jemalloc/include/jemalloc/internal/bitmap.h @@ -3,6 +3,7 @@ /* Maximum bitmap bit count is 2^LG_BITMAP_MAXBITS. */ #define LG_BITMAP_MAXBITS LG_RUN_MAXREGS +#define BITMAP_MAXBITS (ZU(1) << LG_BITMAP_MAXBITS) typedef struct bitmap_level_s bitmap_level_t; typedef struct bitmap_info_s bitmap_info_t; @@ -14,6 +15,51 @@ typedef unsigned long bitmap_t; #define BITMAP_GROUP_NBITS (ZU(1) << LG_BITMAP_GROUP_NBITS) #define BITMAP_GROUP_NBITS_MASK (BITMAP_GROUP_NBITS-1) +/* Number of groups required to store a given number of bits. */ +#define BITMAP_BITS2GROUPS(nbits) \ + ((nbits + BITMAP_GROUP_NBITS_MASK) >> LG_BITMAP_GROUP_NBITS) + +/* + * Number of groups required at a particular level for a given number of bits. + */ +#define BITMAP_GROUPS_L0(nbits) \ + BITMAP_BITS2GROUPS(nbits) +#define BITMAP_GROUPS_L1(nbits) \ + BITMAP_BITS2GROUPS(BITMAP_BITS2GROUPS(nbits)) +#define BITMAP_GROUPS_L2(nbits) \ + BITMAP_BITS2GROUPS(BITMAP_BITS2GROUPS(BITMAP_BITS2GROUPS((nbits)))) +#define BITMAP_GROUPS_L3(nbits) \ + BITMAP_BITS2GROUPS(BITMAP_BITS2GROUPS(BITMAP_BITS2GROUPS( \ + BITMAP_BITS2GROUPS((nbits))))) + +/* + * Assuming the number of levels, number of groups required for a given number + * of bits. + */ +#define BITMAP_GROUPS_1_LEVEL(nbits) \ + BITMAP_GROUPS_L0(nbits) +#define BITMAP_GROUPS_2_LEVEL(nbits) \ + (BITMAP_GROUPS_1_LEVEL(nbits) + BITMAP_GROUPS_L1(nbits)) +#define BITMAP_GROUPS_3_LEVEL(nbits) \ + (BITMAP_GROUPS_2_LEVEL(nbits) + BITMAP_GROUPS_L2(nbits)) +#define BITMAP_GROUPS_4_LEVEL(nbits) \ + (BITMAP_GROUPS_3_LEVEL(nbits) + BITMAP_GROUPS_L3(nbits)) + +/* + * Maximum number of groups required to support LG_BITMAP_MAXBITS. + */ +#if LG_BITMAP_MAXBITS <= LG_BITMAP_GROUP_NBITS +# define BITMAP_GROUPS_MAX BITMAP_GROUPS_1_LEVEL(BITMAP_MAXBITS) +#elif LG_BITMAP_MAXBITS <= LG_BITMAP_GROUP_NBITS * 2 +# define BITMAP_GROUPS_MAX BITMAP_GROUPS_2_LEVEL(BITMAP_MAXBITS) +#elif LG_BITMAP_MAXBITS <= LG_BITMAP_GROUP_NBITS * 3 +# define BITMAP_GROUPS_MAX BITMAP_GROUPS_3_LEVEL(BITMAP_MAXBITS) +#elif LG_BITMAP_MAXBITS <= LG_BITMAP_GROUP_NBITS * 4 +# define BITMAP_GROUPS_MAX BITMAP_GROUPS_4_LEVEL(BITMAP_MAXBITS) +#else +# error "Unsupported bitmap size" +#endif + /* Maximum number of levels possible. */ #define BITMAP_MAX_LEVELS \ (LG_BITMAP_MAXBITS / LG_SIZEOF_BITMAP) \ @@ -93,7 +139,7 @@ bitmap_set(bitmap_t *bitmap, const bitmap_info_t *binfo, size_t bit) bitmap_t g; assert(bit < binfo->nbits); - assert(bitmap_get(bitmap, binfo, bit) == false); + assert(!bitmap_get(bitmap, binfo, bit)); goff = bit >> LG_BITMAP_GROUP_NBITS; gp = &bitmap[goff]; g = *gp; @@ -126,15 +172,15 @@ bitmap_sfu(bitmap_t *bitmap, const bitmap_info_t *binfo) bitmap_t g; unsigned i; - assert(bitmap_full(bitmap, binfo) == false); + assert(!bitmap_full(bitmap, binfo)); i = binfo->nlevels - 1; g = bitmap[binfo->levels[i].group_offset]; - bit = ffsl(g) - 1; + bit = jemalloc_ffsl(g) - 1; while (i > 0) { i--; g = bitmap[binfo->levels[i].group_offset + bit]; - bit = (bit << LG_BITMAP_GROUP_NBITS) + (ffsl(g) - 1); + bit = (bit << LG_BITMAP_GROUP_NBITS) + (jemalloc_ffsl(g) - 1); } bitmap_set(bitmap, binfo, bit); @@ -158,7 +204,7 @@ bitmap_unset(bitmap_t *bitmap, const bitmap_info_t *binfo, size_t bit) assert((g & (1LU << (bit & BITMAP_GROUP_NBITS_MASK))) == 0); g ^= 1LU << (bit & BITMAP_GROUP_NBITS_MASK); *gp = g; - assert(bitmap_get(bitmap, binfo, bit) == false); + assert(!bitmap_get(bitmap, binfo, bit)); /* Propagate group state transitions up the tree. */ if (propagate) { unsigned i; @@ -172,7 +218,7 @@ bitmap_unset(bitmap_t *bitmap, const bitmap_info_t *binfo, size_t bit) == 0); g ^= 1LU << (bit & BITMAP_GROUP_NBITS_MASK); *gp = g; - if (propagate == false) + if (!propagate) break; } } diff --git a/contrib/jemalloc/include/jemalloc/internal/chunk.h b/contrib/jemalloc/include/jemalloc/internal/chunk.h index 87d8700dac8a..5d1938353033 100644 --- a/contrib/jemalloc/include/jemalloc/internal/chunk.h +++ b/contrib/jemalloc/include/jemalloc/internal/chunk.h @@ -5,7 +5,7 @@ * Size and alignment of memory chunks that are allocated by the OS's virtual * memory system. */ -#define LG_CHUNK_DEFAULT 22 +#define LG_CHUNK_DEFAULT 21 /* Return the chunk address for allocation address a. */ #define CHUNK_ADDR2BASE(a) \ @@ -19,6 +19,16 @@ #define CHUNK_CEILING(s) \ (((s) + chunksize_mask) & ~chunksize_mask) +#define CHUNK_HOOKS_INITIALIZER { \ + NULL, \ + NULL, \ + NULL, \ + NULL, \ + NULL, \ + NULL, \ + NULL \ +} + #endif /* JEMALLOC_H_TYPES */ /******************************************************************************/ #ifdef JEMALLOC_H_STRUCTS @@ -30,23 +40,36 @@ 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; -/* Chunk statistics. */ -extern chunk_stats_t stats_chunks; - -extern rtree_t *chunks_rtree; +extern rtree_t chunks_rtree; extern size_t chunksize; extern size_t chunksize_mask; /* (chunksize - 1). */ 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, - dss_prec_t dss_prec); -void chunk_unmap(void *chunk, size_t size); -void chunk_dealloc(void *chunk, size_t size, bool unmap); +extern const chunk_hooks_t chunk_hooks_default; + +chunk_hooks_t chunk_hooks_get(arena_t *arena); +chunk_hooks_t chunk_hooks_set(arena_t *arena, + const chunk_hooks_t *chunk_hooks); + +bool chunk_register(const void *chunk, const extent_node_t *node); +void chunk_deregister(const void *chunk, const extent_node_t *node); +void *chunk_alloc_base(size_t size); +void *chunk_alloc_cache(arena_t *arena, chunk_hooks_t *chunk_hooks, + void *new_addr, size_t size, size_t alignment, bool *zero, + bool dalloc_node); +void *chunk_alloc_wrapper(arena_t *arena, chunk_hooks_t *chunk_hooks, + void *new_addr, size_t size, size_t alignment, bool *zero, bool *commit); +void chunk_dalloc_cache(arena_t *arena, chunk_hooks_t *chunk_hooks, + void *chunk, size_t size, bool committed); +void chunk_dalloc_arena(arena_t *arena, chunk_hooks_t *chunk_hooks, + void *chunk, size_t size, bool zeroed, bool committed); +void chunk_dalloc_wrapper(arena_t *arena, chunk_hooks_t *chunk_hooks, + void *chunk, size_t size, bool committed); +bool chunk_purge_arena(arena_t *arena, void *chunk, size_t offset, + size_t length); +bool chunk_purge_wrapper(arena_t *arena, chunk_hooks_t *chunk_hooks, + void *chunk, size_t size, size_t offset, size_t length); bool chunk_boot(void); void chunk_prefork(void); void chunk_postfork_parent(void); @@ -56,6 +79,19 @@ void chunk_postfork_child(void); /******************************************************************************/ #ifdef JEMALLOC_H_INLINES +#ifndef JEMALLOC_ENABLE_INLINE +extent_node_t *chunk_lookup(const void *chunk, bool dependent); +#endif + +#if (defined(JEMALLOC_ENABLE_INLINE) || defined(JEMALLOC_CHUNK_C_)) +JEMALLOC_INLINE extent_node_t * +chunk_lookup(const void *ptr, bool dependent) +{ + + return (rtree_get(&chunks_rtree, (uintptr_t)ptr, dependent)); +} +#endif + #endif /* JEMALLOC_H_INLINES */ /******************************************************************************/ diff --git a/contrib/jemalloc/include/jemalloc/internal/chunk_dss.h b/contrib/jemalloc/include/jemalloc/internal/chunk_dss.h index 4535ce09c09a..388f46be0801 100644 --- a/contrib/jemalloc/include/jemalloc/internal/chunk_dss.h +++ b/contrib/jemalloc/include/jemalloc/internal/chunk_dss.h @@ -23,7 +23,8 @@ extern const char *dss_prec_names[]; 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); +void *chunk_alloc_dss(arena_t *arena, void *new_addr, size_t size, + size_t alignment, bool *zero, bool *commit); bool chunk_in_dss(void *chunk); bool chunk_dss_boot(void); void chunk_dss_prefork(void); diff --git a/contrib/jemalloc/include/jemalloc/internal/chunk_mmap.h b/contrib/jemalloc/include/jemalloc/internal/chunk_mmap.h index f24abac75382..7d8014c58173 100644 --- a/contrib/jemalloc/include/jemalloc/internal/chunk_mmap.h +++ b/contrib/jemalloc/include/jemalloc/internal/chunk_mmap.h @@ -9,10 +9,9 @@ /******************************************************************************/ #ifdef JEMALLOC_H_EXTERNS -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); +void *chunk_alloc_mmap(size_t size, size_t alignment, bool *zero, + bool *commit); +bool chunk_dalloc_mmap(void *chunk, size_t size); #endif /* JEMALLOC_H_EXTERNS */ /******************************************************************************/ diff --git a/contrib/jemalloc/include/jemalloc/internal/ckh.h b/contrib/jemalloc/include/jemalloc/internal/ckh.h index 58712a6a763e..75c1c979f270 100644 --- a/contrib/jemalloc/include/jemalloc/internal/ckh.h +++ b/contrib/jemalloc/include/jemalloc/internal/ckh.h @@ -66,13 +66,13 @@ struct ckh_s { /******************************************************************************/ #ifdef JEMALLOC_H_EXTERNS -bool ckh_new(ckh_t *ckh, size_t minitems, ckh_hash_t *hash, +bool ckh_new(tsd_t *tsd, ckh_t *ckh, size_t minitems, ckh_hash_t *hash, ckh_keycomp_t *keycomp); -void ckh_delete(ckh_t *ckh); +void ckh_delete(tsd_t *tsd, ckh_t *ckh); size_t ckh_count(ckh_t *ckh); bool ckh_iter(ckh_t *ckh, size_t *tabind, void **key, void **data); -bool ckh_insert(ckh_t *ckh, const void *key, const void *data); -bool ckh_remove(ckh_t *ckh, const void *searchkey, void **key, +bool ckh_insert(tsd_t *tsd, ckh_t *ckh, const void *key, const void *data); +bool ckh_remove(tsd_t *tsd, 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, size_t r_hash[2]); diff --git a/contrib/jemalloc/include/jemalloc/internal/ctl.h b/contrib/jemalloc/include/jemalloc/internal/ctl.h index 0ffecc5f2a23..751c14b5bada 100644 --- a/contrib/jemalloc/include/jemalloc/internal/ctl.h +++ b/contrib/jemalloc/include/jemalloc/internal/ctl.h @@ -34,6 +34,7 @@ struct ctl_arena_stats_s { bool initialized; unsigned nthreads; const char *dss; + ssize_t lg_dirty_mult; size_t pactive; size_t pdirty; arena_stats_t astats; @@ -46,22 +47,15 @@ struct ctl_arena_stats_s { malloc_bin_stats_t bstats[NBINS]; malloc_large_stats_t *lstats; /* nlclasses elements. */ + malloc_huge_stats_t *hstats; /* nhclasses elements. */ }; struct ctl_stats_s { size_t allocated; size_t active; + size_t metadata; + size_t resident; size_t mapped; - struct { - size_t current; /* stats_chunks.curchunks */ - uint64_t total; /* stats_chunks.nchunks */ - size_t high; /* stats_chunks.highchunks */ - } chunks; - struct { - size_t allocated; /* huge_allocated */ - uint64_t nmalloc; /* huge_nmalloc */ - uint64_t ndalloc; /* huge_ndalloc */ - } huge; unsigned narenas; ctl_arena_stats_t *arenas; /* (narenas + 1) elements. */ }; diff --git a/contrib/jemalloc/include/jemalloc/internal/extent.h b/contrib/jemalloc/include/jemalloc/internal/extent.h index ba95ca816bd9..386d50ef4cd5 100644 --- a/contrib/jemalloc/include/jemalloc/internal/extent.h +++ b/contrib/jemalloc/include/jemalloc/internal/extent.h @@ -7,25 +7,53 @@ typedef struct extent_node_s extent_node_t; /******************************************************************************/ #ifdef JEMALLOC_H_STRUCTS -/* Tree of extents. */ +/* Tree of extents. Use accessor functions for en_* fields. */ struct extent_node_s { - /* Linkage for the size/address-ordered tree. */ - rb_node(extent_node_t) link_szad; - - /* Linkage for the address-ordered tree. */ - rb_node(extent_node_t) link_ad; - - /* Profile counters, used for huge objects. */ - prof_ctx_t *prof_ctx; + /* Arena from which this extent came, if any. */ + arena_t *en_arena; /* Pointer to the extent that this tree node is responsible for. */ - void *addr; + void *en_addr; /* Total region size. */ - size_t size; + size_t en_size; - /* True if zero-filled; used by chunk recycling code. */ - bool zeroed; + /* + * The zeroed flag is used by chunk recycling code to track whether + * memory is zero-filled. + */ + bool en_zeroed; + + /* + * True if physical memory is committed to the extent, whether + * explicitly or implicitly as on a system that overcommits and + * satisfies physical memory needs on demand via soft page faults. + */ + bool en_committed; + + /* + * The achunk flag is used to validate that huge allocation lookups + * don't return arena chunks. + */ + bool en_achunk; + + /* Profile counters, used for huge objects. */ + prof_tctx_t *en_prof_tctx; + + /* Linkage for arena's runs_dirty and chunks_cache rings. */ + arena_runs_dirty_link_t rd; + qr(extent_node_t) cc_link; + + union { + /* Linkage for the size/address-ordered tree. */ + rb_node(extent_node_t) szad_link; + + /* Linkage for arena's huge and node_cache lists. */ + ql_elm(extent_node_t) ql_link; + }; + + /* Linkage for the address-ordered tree. */ + rb_node(extent_node_t) ad_link; }; typedef rb_tree(extent_node_t) extent_tree_t; @@ -41,6 +69,171 @@ rb_proto(, extent_tree_ad_, extent_tree_t, extent_node_t) /******************************************************************************/ #ifdef JEMALLOC_H_INLINES +#ifndef JEMALLOC_ENABLE_INLINE +arena_t *extent_node_arena_get(const extent_node_t *node); +void *extent_node_addr_get(const extent_node_t *node); +size_t extent_node_size_get(const extent_node_t *node); +bool extent_node_zeroed_get(const extent_node_t *node); +bool extent_node_committed_get(const extent_node_t *node); +bool extent_node_achunk_get(const extent_node_t *node); +prof_tctx_t *extent_node_prof_tctx_get(const extent_node_t *node); +void extent_node_arena_set(extent_node_t *node, arena_t *arena); +void extent_node_addr_set(extent_node_t *node, void *addr); +void extent_node_size_set(extent_node_t *node, size_t size); +void extent_node_zeroed_set(extent_node_t *node, bool zeroed); +void extent_node_committed_set(extent_node_t *node, bool committed); +void extent_node_achunk_set(extent_node_t *node, bool achunk); +void extent_node_prof_tctx_set(extent_node_t *node, prof_tctx_t *tctx); +void extent_node_init(extent_node_t *node, arena_t *arena, void *addr, + size_t size, bool zeroed, bool committed); +void extent_node_dirty_linkage_init(extent_node_t *node); +void extent_node_dirty_insert(extent_node_t *node, + arena_runs_dirty_link_t *runs_dirty, extent_node_t *chunks_dirty); +void extent_node_dirty_remove(extent_node_t *node); +#endif + +#if (defined(JEMALLOC_ENABLE_INLINE) || defined(JEMALLOC_EXTENT_C_)) +JEMALLOC_INLINE arena_t * +extent_node_arena_get(const extent_node_t *node) +{ + + return (node->en_arena); +} + +JEMALLOC_INLINE void * +extent_node_addr_get(const extent_node_t *node) +{ + + return (node->en_addr); +} + +JEMALLOC_INLINE size_t +extent_node_size_get(const extent_node_t *node) +{ + + return (node->en_size); +} + +JEMALLOC_INLINE bool +extent_node_zeroed_get(const extent_node_t *node) +{ + + return (node->en_zeroed); +} + +JEMALLOC_INLINE bool +extent_node_committed_get(const extent_node_t *node) +{ + + assert(!node->en_achunk); + return (node->en_committed); +} + +JEMALLOC_INLINE bool +extent_node_achunk_get(const extent_node_t *node) +{ + + return (node->en_achunk); +} + +JEMALLOC_INLINE prof_tctx_t * +extent_node_prof_tctx_get(const extent_node_t *node) +{ + + return (node->en_prof_tctx); +} + +JEMALLOC_INLINE void +extent_node_arena_set(extent_node_t *node, arena_t *arena) +{ + + node->en_arena = arena; +} + +JEMALLOC_INLINE void +extent_node_addr_set(extent_node_t *node, void *addr) +{ + + node->en_addr = addr; +} + +JEMALLOC_INLINE void +extent_node_size_set(extent_node_t *node, size_t size) +{ + + node->en_size = size; +} + +JEMALLOC_INLINE void +extent_node_zeroed_set(extent_node_t *node, bool zeroed) +{ + + node->en_zeroed = zeroed; +} + +JEMALLOC_INLINE void +extent_node_committed_set(extent_node_t *node, bool committed) +{ + + node->en_committed = committed; +} + +JEMALLOC_INLINE void +extent_node_achunk_set(extent_node_t *node, bool achunk) +{ + + node->en_achunk = achunk; +} + +JEMALLOC_INLINE void +extent_node_prof_tctx_set(extent_node_t *node, prof_tctx_t *tctx) +{ + + node->en_prof_tctx = tctx; +} + +JEMALLOC_INLINE void +extent_node_init(extent_node_t *node, arena_t *arena, void *addr, size_t size, + bool zeroed, bool committed) +{ + + extent_node_arena_set(node, arena); + extent_node_addr_set(node, addr); + extent_node_size_set(node, size); + extent_node_zeroed_set(node, zeroed); + extent_node_committed_set(node, committed); + extent_node_achunk_set(node, false); + if (config_prof) + extent_node_prof_tctx_set(node, NULL); +} + +JEMALLOC_INLINE void +extent_node_dirty_linkage_init(extent_node_t *node) +{ + + qr_new(&node->rd, rd_link); + qr_new(node, cc_link); +} + +JEMALLOC_INLINE void +extent_node_dirty_insert(extent_node_t *node, + arena_runs_dirty_link_t *runs_dirty, extent_node_t *chunks_dirty) +{ + + qr_meld(runs_dirty, &node->rd, rd_link); + qr_meld(chunks_dirty, node, cc_link); +} + +JEMALLOC_INLINE void +extent_node_dirty_remove(extent_node_t *node) +{ + + qr_remove(&node->rd, rd_link); + qr_remove(node, cc_link); +} + +#endif + #endif /* JEMALLOC_H_INLINES */ /******************************************************************************/ diff --git a/contrib/jemalloc/include/jemalloc/internal/hash.h b/contrib/jemalloc/include/jemalloc/internal/hash.h index c7183ede82d7..bcead337abc1 100644 --- a/contrib/jemalloc/include/jemalloc/internal/hash.h +++ b/contrib/jemalloc/include/jemalloc/internal/hash.h @@ -35,13 +35,14 @@ JEMALLOC_INLINE uint32_t hash_rotl_32(uint32_t x, int8_t r) { - return (x << r) | (x >> (32 - r)); + return ((x << r) | (x >> (32 - r))); } JEMALLOC_INLINE uint64_t hash_rotl_64(uint64_t x, int8_t r) { - return (x << r) | (x >> (64 - r)); + + return ((x << r) | (x >> (64 - r))); } JEMALLOC_INLINE uint32_t @@ -76,9 +77,9 @@ hash_fmix_64(uint64_t k) { k ^= k >> 33; - k *= QU(0xff51afd7ed558ccdLLU); + k *= KQU(0xff51afd7ed558ccd); k ^= k >> 33; - k *= QU(0xc4ceb9fe1a85ec53LLU); + k *= KQU(0xc4ceb9fe1a85ec53); k ^= k >> 33; return (k); @@ -247,8 +248,8 @@ hash_x64_128(const void *key, const int len, const uint32_t seed, uint64_t h1 = seed; uint64_t h2 = seed; - const uint64_t c1 = QU(0x87c37b91114253d5LLU); - const uint64_t c2 = QU(0x4cf5ad432745937fLLU); + const uint64_t c1 = KQU(0x87c37b91114253d5); + const uint64_t c2 = KQU(0x4cf5ad432745937f); /* body */ { diff --git a/contrib/jemalloc/include/jemalloc/internal/huge.h b/contrib/jemalloc/include/jemalloc/internal/huge.h index a2b9c779191f..8b6c6cec2ae9 100644 --- a/contrib/jemalloc/include/jemalloc/internal/huge.h +++ b/contrib/jemalloc/include/jemalloc/internal/huge.h @@ -9,34 +9,24 @@ /******************************************************************************/ #ifdef JEMALLOC_H_EXTERNS -/* Huge allocation statistics. */ -extern uint64_t huge_nmalloc; -extern uint64_t huge_ndalloc; -extern size_t huge_allocated; - -/* Protects chunk-related data structures. */ -extern malloc_mutex_t huge_mtx; - -void *huge_malloc(size_t size, bool zero, dss_prec_t dss_prec); -void *huge_palloc(size_t size, size_t alignment, bool zero, - dss_prec_t dss_prec); +void *huge_malloc(tsd_t *tsd, arena_t *arena, size_t size, bool zero, + tcache_t *tcache); +void *huge_palloc(tsd_t *tsd, arena_t *arena, size_t size, size_t alignment, + bool zero, tcache_t *tcache); bool 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, bool try_tcache_dalloc, dss_prec_t dss_prec); + size_t extra, bool zero); +void *huge_ralloc(tsd_t *tsd, arena_t *arena, void *ptr, size_t oldsize, + size_t size, size_t extra, size_t alignment, bool zero, + tcache_t *tcache); #ifdef JEMALLOC_JET typedef void (huge_dalloc_junk_t)(void *, size_t); extern huge_dalloc_junk_t *huge_dalloc_junk; #endif -void huge_dalloc(void *ptr, bool unmap); +void huge_dalloc(tsd_t *tsd, void *ptr, tcache_t *tcache); +arena_t *huge_aalloc(const void *ptr); size_t huge_salloc(const void *ptr); -dss_prec_t huge_dss_prec_get(arena_t *arena); -prof_ctx_t *huge_prof_ctx_get(const void *ptr); -void huge_prof_ctx_set(const void *ptr, prof_ctx_t *ctx); -bool huge_boot(void); -void huge_prefork(void); -void huge_postfork_parent(void); -void huge_postfork_child(void); +prof_tctx_t *huge_prof_tctx_get(const void *ptr); +void huge_prof_tctx_set(const void *ptr, prof_tctx_t *tctx); #endif /* JEMALLOC_H_EXTERNS */ /******************************************************************************/ diff --git a/contrib/jemalloc/include/jemalloc/internal/jemalloc_internal.h b/contrib/jemalloc/include/jemalloc/internal/jemalloc_internal.h index dd6e6a301705..d9d8b5bbb6ad 100644 --- a/contrib/jemalloc/include/jemalloc/internal/jemalloc_internal.h +++ b/contrib/jemalloc/include/jemalloc/internal/jemalloc_internal.h @@ -1,73 +1,13 @@ #ifndef JEMALLOC_INTERNAL_H #define JEMALLOC_INTERNAL_H -#include "libc_private.h" -#include "namespace.h" - -#include -#ifdef _WIN32 -# include -# define ENOENT ERROR_PATH_NOT_FOUND -# define EINVAL ERROR_BAD_ARGUMENTS -# define EAGAIN ERROR_OUTOFMEMORY -# define EPERM ERROR_WRITE_FAULT -# define EFAULT ERROR_INVALID_ADDRESS -# define ENOMEM ERROR_NOT_ENOUGH_MEMORY -# undef ERANGE -# define ERANGE ERROR_INVALID_DATA -#else -# include -# include -# include -# if !defined(SYS_write) && defined(__NR_write) -# define SYS_write __NR_write -# endif -# include -# include -# include -#endif -#include - -#include -#ifndef SIZE_T_MAX -# define SIZE_T_MAX SIZE_MAX -#endif -#include -#include -#include -#include -#include -#include -#ifndef offsetof -# define offsetof(type, member) ((size_t)&(((type *)NULL)->member)) -#endif -#include -#include -#include -#include -#ifdef _MSC_VER -# include -typedef intptr_t ssize_t; -# define PATH_MAX 1024 -# define STDERR_FILENO 2 -# define __func__ __FUNCTION__ -/* Disable warnings about deprecated system functions */ -# pragma warning(disable: 4996) -#else -# include -#endif -#include #include "jemalloc_internal_defs.h" +#include "jemalloc/internal/jemalloc_internal_decls.h" #ifdef JEMALLOC_UTRACE #include #endif -#ifdef JEMALLOC_VALGRIND -#include -#include -#endif - #include "un-namespace.h" #include "libc_private.h" @@ -91,7 +31,7 @@ static const bool config_debug = false #endif ; -static const bool config_dss = +static const bool have_dss = #ifdef JEMALLOC_DSS true #else @@ -127,8 +67,8 @@ static const bool config_prof_libunwind = false #endif ; -static const bool config_mremap = -#ifdef JEMALLOC_MREMAP +static const bool maps_coalesce = +#ifdef JEMALLOC_MAPS_COALESCE true #else false @@ -190,6 +130,17 @@ static const bool config_ivsalloc = false #endif ; +static const bool config_cache_oblivious = +#ifdef JEMALLOC_CACHE_OBLIVIOUS + true +#else + false +#endif + ; + +#ifdef JEMALLOC_C11ATOMICS +#include +#endif #ifdef JEMALLOC_ATOMIC9 #include @@ -229,15 +180,43 @@ static const bool config_ivsalloc = #include "jemalloc/internal/jemalloc_internal_macros.h" +/* Size class index type. */ +typedef unsigned index_t; + +/* + * Flags bits: + * + * a: arena + * t: tcache + * 0: unused + * z: zero + * n: alignment + * + * aaaaaaaa aaaatttt tttttttt 0znnnnnn + */ +#define MALLOCX_ARENA_MASK ((int)~0xfffff) +#define MALLOCX_ARENA_MAX 0xffe +#define MALLOCX_TCACHE_MASK ((int)~0xfff000ffU) +#define MALLOCX_TCACHE_MAX 0xffd #define MALLOCX_LG_ALIGN_MASK ((int)0x3f) -#define ALLOCM_LG_ALIGN_MASK ((int)0x3f) +/* Use MALLOCX_ALIGN_GET() if alignment may not be specified in flags. */ +#define MALLOCX_ALIGN_GET_SPECIFIED(flags) \ + (ZU(1) << (flags & MALLOCX_LG_ALIGN_MASK)) +#define MALLOCX_ALIGN_GET(flags) \ + (MALLOCX_ALIGN_GET_SPECIFIED(flags) & (SIZE_T_MAX-1)) +#define MALLOCX_ZERO_GET(flags) \ + ((bool)(flags & MALLOCX_ZERO)) + +#define MALLOCX_TCACHE_GET(flags) \ + (((unsigned)((flags & MALLOCX_TCACHE_MASK) >> 8)) - 2) +#define MALLOCX_ARENA_GET(flags) \ + (((unsigned)(((unsigned)flags) >> 20)) - 1) /* Smallest size class to support. */ -#define LG_TINY_MIN 3 #define TINY_MIN (1U << LG_TINY_MIN) /* - * Minimum alignment of allocations is 2^LG_QUANTUM bytes (ignoring tiny size + * Minimum allocation alignment is 2^LG_QUANTUM bytes (ignoring tiny size * classes). */ #ifndef LG_QUANTUM @@ -268,6 +247,9 @@ static const bool config_ivsalloc = # ifdef __mips__ # define LG_QUANTUM 3 # endif +# ifdef __or1k__ +# define LG_QUANTUM 3 +# endif # ifdef __powerpc__ # define LG_QUANTUM 4 # endif @@ -280,8 +262,12 @@ static const bool config_ivsalloc = # ifdef __tile__ # define LG_QUANTUM 4 # endif +# ifdef __le32__ +# define LG_QUANTUM 4 +# endif # ifndef LG_QUANTUM -# error "No LG_QUANTUM definition for architecture; specify via CPPFLAGS" +# error "Unknown minimum alignment for architecture; specify via " + "--with-lg-quantum" # endif #endif @@ -321,12 +307,11 @@ static const bool config_ivsalloc = #define CACHELINE_CEILING(s) \ (((s) + CACHELINE_MASK) & ~CACHELINE_MASK) -/* Page size. STATIC_PAGE_SHIFT is determined by the configure script. */ +/* Page size. LG_PAGE is determined by the configure script. */ #ifdef PAGE_MASK # undef PAGE_MASK #endif -#define LG_PAGE STATIC_PAGE_SHIFT -#define PAGE ((size_t)(1U << STATIC_PAGE_SHIFT)) +#define PAGE ((size_t)(1U << LG_PAGE)) #define PAGE_MASK ((size_t)(PAGE - 1)) /* Return the smallest pagesize multiple that is >= s. */ @@ -345,7 +330,7 @@ static const bool config_ivsalloc = #define ALIGNMENT_CEILING(s, alignment) \ (((s) + (alignment - 1)) & (-(alignment))) -/* Declare a variable length array */ +/* Declare a variable-length array. */ #if __STDC_VERSION__ < 199901L # ifdef _MSC_VER # include @@ -358,86 +343,12 @@ static const bool config_ivsalloc = # endif # endif # define VARIABLE_ARRAY(type, name, count) \ - type *name = alloca(sizeof(type) * count) + type *name = alloca(sizeof(type) * (count)) #else -# define VARIABLE_ARRAY(type, name, count) type name[count] -#endif - -#ifdef JEMALLOC_VALGRIND -/* - * The JEMALLOC_VALGRIND_*() macros must be macros rather than functions - * so that when Valgrind reports errors, there are no extra stack frames - * in the backtraces. - * - * The size that is reported to valgrind must be consistent through a chain of - * malloc..realloc..realloc calls. Request size isn't recorded anywhere in - * jemalloc, so it is critical that all callers of these macros provide usize - * rather than request size. As a result, buffer overflow detection is - * technically weakened for the standard API, though it is generally accepted - * practice to consider any extra bytes reported by malloc_usable_size() as - * usable space. - */ -#define JEMALLOC_VALGRIND_MALLOC(cond, ptr, usize, zero) do { \ - if (config_valgrind && opt_valgrind && cond) \ - VALGRIND_MALLOCLIKE_BLOCK(ptr, usize, p2rz(ptr), zero); \ -} while (0) -#define JEMALLOC_VALGRIND_REALLOC(ptr, usize, old_ptr, old_usize, \ - old_rzsize, zero) do { \ - if (config_valgrind && opt_valgrind) { \ - size_t rzsize = p2rz(ptr); \ - \ - if (ptr == old_ptr) { \ - VALGRIND_RESIZEINPLACE_BLOCK(ptr, old_usize, \ - usize, rzsize); \ - if (zero && old_usize < usize) { \ - VALGRIND_MAKE_MEM_DEFINED( \ - (void *)((uintptr_t)ptr + \ - old_usize), usize - old_usize); \ - } \ - } else { \ - if (old_ptr != NULL) { \ - VALGRIND_FREELIKE_BLOCK(old_ptr, \ - old_rzsize); \ - } \ - if (ptr != NULL) { \ - size_t copy_size = (old_usize < usize) \ - ? old_usize : usize; \ - size_t tail_size = usize - copy_size; \ - VALGRIND_MALLOCLIKE_BLOCK(ptr, usize, \ - rzsize, false); \ - if (copy_size > 0) { \ - VALGRIND_MAKE_MEM_DEFINED(ptr, \ - copy_size); \ - } \ - if (zero && tail_size > 0) { \ - VALGRIND_MAKE_MEM_DEFINED( \ - (void *)((uintptr_t)ptr + \ - copy_size), tail_size); \ - } \ - } \ - } \ - } \ -} while (0) -#define JEMALLOC_VALGRIND_FREE(ptr, rzsize) do { \ - if (config_valgrind && opt_valgrind) \ - VALGRIND_FREELIKE_BLOCK(ptr, rzsize); \ -} while (0) -#else -#define RUNNING_ON_VALGRIND ((unsigned)0) -#define VALGRIND_MALLOCLIKE_BLOCK(addr, sizeB, rzB, is_zeroed) \ - do {} while (0) -#define VALGRIND_RESIZEINPLACE_BLOCK(addr, oldSizeB, newSizeB, rzB) \ - do {} while (0) -#define VALGRIND_FREELIKE_BLOCK(addr, rzB) do {} while (0) -#define VALGRIND_MAKE_MEM_NOACCESS(_qzz_addr, _qzz_len) do {} while (0) -#define VALGRIND_MAKE_MEM_UNDEFINED(_qzz_addr, _qzz_len) do {} while (0) -#define VALGRIND_MAKE_MEM_DEFINED(_qzz_addr, _qzz_len) do {} while (0) -#define JEMALLOC_VALGRIND_MALLOC(cond, ptr, usize, zero) do {} while (0) -#define JEMALLOC_VALGRIND_REALLOC(ptr, usize, old_ptr, old_usize, \ - old_rzsize, zero) do {} while (0) -#define JEMALLOC_VALGRIND_FREE(ptr, rzsize) do {} while (0) +# define VARIABLE_ARRAY(type, name, count) type name[(count)] #endif +#include "jemalloc/internal/valgrind.h" #include "jemalloc/internal/util.h" #include "jemalloc/internal/atomic.h" #include "jemalloc/internal/prng.h" @@ -452,9 +363,10 @@ static const bool config_ivsalloc = #include "jemalloc/internal/arena.h" #include "jemalloc/internal/bitmap.h" #include "jemalloc/internal/base.h" +#include "jemalloc/internal/rtree.h" +#include "jemalloc/internal/pages.h" #include "jemalloc/internal/chunk.h" #include "jemalloc/internal/huge.h" -#include "jemalloc/internal/rtree.h" #include "jemalloc/internal/tcache.h" #include "jemalloc/internal/hash.h" #include "jemalloc/internal/quarantine.h" @@ -464,6 +376,7 @@ static const bool config_ivsalloc = /******************************************************************************/ #define JEMALLOC_H_STRUCTS +#include "jemalloc/internal/valgrind.h" #include "jemalloc/internal/util.h" #include "jemalloc/internal/atomic.h" #include "jemalloc/internal/prng.h" @@ -472,68 +385,83 @@ static const bool config_ivsalloc = #include "jemalloc/internal/stats.h" #include "jemalloc/internal/ctl.h" #include "jemalloc/internal/mutex.h" -#include "jemalloc/internal/tsd.h" #include "jemalloc/internal/mb.h" #include "jemalloc/internal/bitmap.h" -#include "jemalloc/internal/extent.h" +#define JEMALLOC_ARENA_STRUCTS_A #include "jemalloc/internal/arena.h" +#undef JEMALLOC_ARENA_STRUCTS_A +#include "jemalloc/internal/extent.h" +#define JEMALLOC_ARENA_STRUCTS_B +#include "jemalloc/internal/arena.h" +#undef JEMALLOC_ARENA_STRUCTS_B #include "jemalloc/internal/base.h" +#include "jemalloc/internal/rtree.h" +#include "jemalloc/internal/pages.h" #include "jemalloc/internal/chunk.h" #include "jemalloc/internal/huge.h" -#include "jemalloc/internal/rtree.h" #include "jemalloc/internal/tcache.h" #include "jemalloc/internal/hash.h" #include "jemalloc/internal/quarantine.h" #include "jemalloc/internal/prof.h" -typedef struct { - uint64_t allocated; - uint64_t deallocated; -} thread_allocated_t; -/* - * The JEMALLOC_ARG_CONCAT() wrapper is necessary to pass {0, 0} via a cpp macro - * argument. - */ -#define THREAD_ALLOCATED_INITIALIZER JEMALLOC_ARG_CONCAT({0, 0}) +#include "jemalloc/internal/tsd.h" #undef JEMALLOC_H_STRUCTS /******************************************************************************/ #define JEMALLOC_H_EXTERNS extern bool opt_abort; -extern bool opt_junk; +extern const char *opt_junk; +extern bool opt_junk_alloc; +extern bool opt_junk_free; extern size_t opt_quarantine; extern bool opt_redzone; extern bool opt_utrace; -extern bool opt_valgrind; extern bool opt_xmalloc; extern bool opt_zero; extern size_t opt_narenas; +extern bool in_valgrind; + /* Number of CPUs. */ extern unsigned ncpus; -/* 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. + * index2size_tab encodes the same information as could be computed (at + * unacceptable cost in some code paths) by index2size_compute(). */ -extern arena_t **arenas; -extern unsigned narenas_total; -extern unsigned narenas_auto; /* Read-only after initialization. */ +extern size_t const index2size_tab[NSIZES]; +/* + * size2index_tab is a compact lookup table that rounds request sizes up to + * size classes. In order to reduce cache footprint, the table is compressed, + * and all accesses are via size2index(). + */ +extern uint8_t const size2index_tab[]; +arena_t *a0get(void); +void *a0malloc(size_t size); +void a0dalloc(void *ptr); +void *bootstrap_malloc(size_t size); +void *bootstrap_calloc(size_t num, size_t size); +void bootstrap_free(void *ptr); arena_t *arenas_extend(unsigned ind); -void arenas_cleanup(void *arg); -arena_t *choose_arena_hard(void); +arena_t *arena_init(unsigned ind); +unsigned narenas_total_get(void); +arena_t *arena_get_hard(tsd_t *tsd, unsigned ind, bool init_if_missing); +arena_t *arena_choose_hard(tsd_t *tsd); +void arena_migrate(tsd_t *tsd, unsigned oldind, unsigned newind); +unsigned arena_nbound(unsigned ind); +void thread_allocated_cleanup(tsd_t *tsd); +void thread_deallocated_cleanup(tsd_t *tsd); +void arena_cleanup(tsd_t *tsd); +void arenas_cache_cleanup(tsd_t *tsd); +void narenas_cache_cleanup(tsd_t *tsd); +void arenas_cache_bypass_cleanup(tsd_t *tsd); void jemalloc_prefork(void); void jemalloc_postfork_parent(void); void jemalloc_postfork_child(void); +#include "jemalloc/internal/valgrind.h" #include "jemalloc/internal/util.h" #include "jemalloc/internal/atomic.h" #include "jemalloc/internal/prng.h" @@ -542,24 +470,26 @@ void jemalloc_postfork_child(void); #include "jemalloc/internal/stats.h" #include "jemalloc/internal/ctl.h" #include "jemalloc/internal/mutex.h" -#include "jemalloc/internal/tsd.h" #include "jemalloc/internal/mb.h" #include "jemalloc/internal/bitmap.h" #include "jemalloc/internal/extent.h" #include "jemalloc/internal/arena.h" #include "jemalloc/internal/base.h" +#include "jemalloc/internal/rtree.h" +#include "jemalloc/internal/pages.h" #include "jemalloc/internal/chunk.h" #include "jemalloc/internal/huge.h" -#include "jemalloc/internal/rtree.h" #include "jemalloc/internal/tcache.h" #include "jemalloc/internal/hash.h" #include "jemalloc/internal/quarantine.h" #include "jemalloc/internal/prof.h" +#include "jemalloc/internal/tsd.h" #undef JEMALLOC_H_EXTERNS /******************************************************************************/ #define JEMALLOC_H_INLINES +#include "jemalloc/internal/valgrind.h" #include "jemalloc/internal/util.h" #include "jemalloc/internal/atomic.h" #include "jemalloc/internal/prng.h" @@ -572,26 +502,158 @@ void jemalloc_postfork_child(void); #include "jemalloc/internal/mb.h" #include "jemalloc/internal/extent.h" #include "jemalloc/internal/base.h" +#include "jemalloc/internal/rtree.h" +#include "jemalloc/internal/pages.h" #include "jemalloc/internal/chunk.h" #include "jemalloc/internal/huge.h" #ifndef JEMALLOC_ENABLE_INLINE -malloc_tsd_protos(JEMALLOC_ATTR(unused), arenas, arena_t *) - +index_t size2index_compute(size_t size); +index_t size2index_lookup(size_t size); +index_t size2index(size_t size); +size_t index2size_compute(index_t index); +size_t index2size_lookup(index_t index); +size_t index2size(index_t index); +size_t s2u_compute(size_t size); +size_t s2u_lookup(size_t size); 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); +arena_t *arena_choose(tsd_t *tsd, arena_t *arena); +arena_t *arena_get(tsd_t *tsd, unsigned ind, bool init_if_missing, + bool refresh_if_missing); #endif #if (defined(JEMALLOC_ENABLE_INLINE) || defined(JEMALLOC_C_)) -/* - * Map of pthread_self() --> arenas[???], used for selecting an arena to use - * for allocations. - */ -malloc_tsd_externs(arenas, arena_t *) -malloc_tsd_funcs(JEMALLOC_ALWAYS_INLINE, arenas, arena_t *, NULL, - arenas_cleanup) +JEMALLOC_INLINE index_t +size2index_compute(size_t size) +{ + +#if (NTBINS != 0) + if (size <= (ZU(1) << LG_TINY_MAXCLASS)) { + size_t lg_tmin = LG_TINY_MAXCLASS - NTBINS + 1; + size_t lg_ceil = lg_floor(pow2_ceil(size)); + return (lg_ceil < lg_tmin ? 0 : lg_ceil - lg_tmin); + } +#endif + { + size_t x = unlikely(ZI(size) < 0) ? ((size<<1) ? + (ZU(1)<<(LG_SIZEOF_PTR+3)) : ((ZU(1)<<(LG_SIZEOF_PTR+3))-1)) + : lg_floor((size<<1)-1); + size_t shift = (x < LG_SIZE_CLASS_GROUP + LG_QUANTUM) ? 0 : + x - (LG_SIZE_CLASS_GROUP + LG_QUANTUM); + size_t grp = shift << LG_SIZE_CLASS_GROUP; + + size_t lg_delta = (x < LG_SIZE_CLASS_GROUP + LG_QUANTUM + 1) + ? LG_QUANTUM : x - LG_SIZE_CLASS_GROUP - 1; + + size_t delta_inverse_mask = ZI(-1) << lg_delta; + size_t mod = ((((size-1) & delta_inverse_mask) >> lg_delta)) & + ((ZU(1) << LG_SIZE_CLASS_GROUP) - 1); + + size_t index = NTBINS + grp + mod; + return (index); + } +} + +JEMALLOC_ALWAYS_INLINE index_t +size2index_lookup(size_t size) +{ + + assert(size <= LOOKUP_MAXCLASS); + { + size_t ret = ((size_t)(size2index_tab[(size-1) >> + LG_TINY_MIN])); + assert(ret == size2index_compute(size)); + return (ret); + } +} + +JEMALLOC_ALWAYS_INLINE index_t +size2index(size_t size) +{ + + assert(size > 0); + if (likely(size <= LOOKUP_MAXCLASS)) + return (size2index_lookup(size)); + return (size2index_compute(size)); +} + +JEMALLOC_INLINE size_t +index2size_compute(index_t index) +{ + +#if (NTBINS > 0) + if (index < NTBINS) + return (ZU(1) << (LG_TINY_MAXCLASS - NTBINS + 1 + index)); +#endif + { + size_t reduced_index = index - NTBINS; + size_t grp = reduced_index >> LG_SIZE_CLASS_GROUP; + size_t mod = reduced_index & ((ZU(1) << LG_SIZE_CLASS_GROUP) - + 1); + + size_t grp_size_mask = ~((!!grp)-1); + size_t grp_size = ((ZU(1) << (LG_QUANTUM + + (LG_SIZE_CLASS_GROUP-1))) << grp) & grp_size_mask; + + size_t shift = (grp == 0) ? 1 : grp; + size_t lg_delta = shift + (LG_QUANTUM-1); + size_t mod_size = (mod+1) << lg_delta; + + size_t usize = grp_size + mod_size; + return (usize); + } +} + +JEMALLOC_ALWAYS_INLINE size_t +index2size_lookup(index_t index) +{ + size_t ret = (size_t)index2size_tab[index]; + assert(ret == index2size_compute(index)); + return (ret); +} + +JEMALLOC_ALWAYS_INLINE size_t +index2size(index_t index) +{ + + assert(index < NSIZES); + return (index2size_lookup(index)); +} + +JEMALLOC_ALWAYS_INLINE size_t +s2u_compute(size_t size) +{ + +#if (NTBINS > 0) + if (size <= (ZU(1) << LG_TINY_MAXCLASS)) { + size_t lg_tmin = LG_TINY_MAXCLASS - NTBINS + 1; + size_t lg_ceil = lg_floor(pow2_ceil(size)); + return (lg_ceil < lg_tmin ? (ZU(1) << lg_tmin) : + (ZU(1) << lg_ceil)); + } +#endif + { + size_t x = unlikely(ZI(size) < 0) ? ((size<<1) ? + (ZU(1)<<(LG_SIZEOF_PTR+3)) : ((ZU(1)<<(LG_SIZEOF_PTR+3))-1)) + : lg_floor((size<<1)-1); + size_t lg_delta = (x < LG_SIZE_CLASS_GROUP + LG_QUANTUM + 1) + ? LG_QUANTUM : x - LG_SIZE_CLASS_GROUP - 1; + size_t delta = ZU(1) << lg_delta; + size_t delta_mask = delta - 1; + size_t usize = (size + delta_mask) & ~delta_mask; + return (usize); + } +} + +JEMALLOC_ALWAYS_INLINE size_t +s2u_lookup(size_t size) +{ + size_t ret = index2size_lookup(size2index_lookup(size)); + + assert(ret == s2u_compute(size)); + return (ret); +} /* * Compute usable size that would result from allocating an object with the @@ -601,11 +663,10 @@ JEMALLOC_ALWAYS_INLINE size_t s2u(size_t size) { - if (size <= SMALL_MAXCLASS) - return (arena_bin_info[SMALL_SIZE2BIN(size)].reg_size); - if (size <= arena_maxclass) - return (PAGE_CEILING(size)); - return (CHUNK_CEILING(size)); + assert(size > 0); + if (likely(size <= LOOKUP_MAXCLASS)) + return (s2u_lookup(size)); + return (s2u_compute(size)); } /* @@ -619,108 +680,128 @@ sa2u(size_t size, size_t alignment) assert(alignment != 0 && ((alignment - 1) & alignment) == 0); - /* - * Round size up to the nearest multiple of alignment. - * - * This done, we can take advantage of the fact that for each small - * size class, every object is aligned at the smallest power of two - * that is non-zero in the base two representation of the size. For - * example: - * - * Size | Base 2 | Minimum alignment - * -----+----------+------------------ - * 96 | 1100000 | 32 - * 144 | 10100000 | 32 - * 192 | 11000000 | 64 - */ - usize = ALIGNMENT_CEILING(size, alignment); - /* - * (usize < size) protects against the combination of maximal - * alignment and size greater than maximal alignment. - */ - if (usize < size) { - /* size_t overflow. */ - return (0); + /* Try for a small size class. */ + if (size <= SMALL_MAXCLASS && alignment < PAGE) { + /* + * Round size up to the nearest multiple of alignment. + * + * This done, we can take advantage of the fact that for each + * small size class, every object is aligned at the smallest + * power of two that is non-zero in the base two representation + * of the size. For example: + * + * Size | Base 2 | Minimum alignment + * -----+----------+------------------ + * 96 | 1100000 | 32 + * 144 | 10100000 | 32 + * 192 | 11000000 | 64 + */ + usize = s2u(ALIGNMENT_CEILING(size, alignment)); + if (usize < LARGE_MINCLASS) + return (usize); } - if (usize <= arena_maxclass && alignment <= PAGE) { - if (usize <= SMALL_MAXCLASS) - return (arena_bin_info[SMALL_SIZE2BIN(usize)].reg_size); - return (PAGE_CEILING(usize)); - } else { - size_t run_size; - + /* Try for a large size class. */ + if (likely(size <= arena_maxclass) && likely(alignment < chunksize)) { /* * We can't achieve subpage alignment, so round up alignment - * permanently; it makes later calculations simpler. + * to the minimum that can actually be supported. */ alignment = PAGE_CEILING(alignment); - usize = PAGE_CEILING(size); - /* - * (usize < size) protects against very large sizes within - * PAGE of SIZE_T_MAX. - * - * (usize + alignment < usize) protects against the - * combination of maximal alignment and usize large enough - * to cause overflow. This is similar to the first overflow - * check above, but it needs to be repeated due to the new - * usize value, which may now be *equal* to maximal - * alignment, whereas before we only detected overflow if the - * original size was *greater* than maximal alignment. - */ - if (usize < size || usize + alignment < usize) { - /* size_t overflow. */ - return (0); - } + + /* Make sure result is a large size class. */ + usize = (size <= LARGE_MINCLASS) ? LARGE_MINCLASS : s2u(size); /* * Calculate the size of the over-size run that arena_palloc() * would need to allocate in order to guarantee the alignment. - * If the run wouldn't fit within a chunk, round up to a huge - * allocation size. */ - run_size = usize + alignment - PAGE; - if (run_size <= arena_maxclass) - return (PAGE_CEILING(usize)); - return (CHUNK_CEILING(usize)); + if (usize + large_pad + alignment - PAGE <= arena_maxrun) + return (usize); } -} -JEMALLOC_INLINE unsigned -narenas_total_get(void) -{ - unsigned narenas; + /* Huge size class. Beware of size_t overflow. */ - malloc_mutex_lock(&arenas_lock); - narenas = narenas_total; - malloc_mutex_unlock(&arenas_lock); + /* + * We can't achieve subchunk alignment, so round up alignment to the + * minimum that can actually be supported. + */ + alignment = CHUNK_CEILING(alignment); + if (alignment == 0) { + /* size_t overflow. */ + return (0); + } - return (narenas); + /* Make sure result is a huge size class. */ + if (size <= chunksize) + usize = chunksize; + else { + usize = s2u(size); + if (usize < size) { + /* size_t overflow. */ + return (0); + } + } + + /* + * Calculate the multi-chunk mapping that huge_palloc() would need in + * order to guarantee the alignment. + */ + if (usize + alignment - PAGE < usize) { + /* size_t overflow. */ + return (0); + } + return (usize); } /* Choose an arena based on a per-thread value. */ JEMALLOC_INLINE arena_t * -choose_arena(arena_t *arena) +arena_choose(tsd_t *tsd, arena_t *arena) { arena_t *ret; if (arena != NULL) return (arena); - if ((ret = *arenas_tsd_get()) == NULL) { - ret = choose_arena_hard(); - assert(ret != NULL); - } + if (unlikely((ret = tsd_arena_get(tsd)) == NULL)) + ret = arena_choose_hard(tsd); return (ret); } + +JEMALLOC_INLINE arena_t * +arena_get(tsd_t *tsd, unsigned ind, bool init_if_missing, + bool refresh_if_missing) +{ + arena_t *arena; + arena_t **arenas_cache = tsd_arenas_cache_get(tsd); + + /* init_if_missing requires refresh_if_missing. */ + assert(!init_if_missing || refresh_if_missing); + + if (unlikely(arenas_cache == NULL)) { + /* arenas_cache hasn't been initialized yet. */ + return (arena_get_hard(tsd, ind, init_if_missing)); + } + if (unlikely(ind >= tsd_narenas_cache_get(tsd))) { + /* + * ind is invalid, cache is old (too small), or arena to be + * initialized. + */ + return (refresh_if_missing ? arena_get_hard(tsd, ind, + init_if_missing) : NULL); + } + arena = arenas_cache[ind]; + if (likely(arena != NULL) || !refresh_if_missing) + return (arena); + return (arena_get_hard(tsd, ind, init_if_missing)); +} #endif #include "jemalloc/internal/bitmap.h" -#include "jemalloc/internal/rtree.h" /* - * Include arena.h twice in order to resolve circular dependencies with - * tcache.h. + * Include portions of arena.h interleaved with tcache.h in order to resolve + * circular dependencies. */ #define JEMALLOC_ARENA_INLINE_A #include "jemalloc/internal/arena.h" @@ -733,100 +814,47 @@ choose_arena(arena_t *arena) #include "jemalloc/internal/quarantine.h" #ifndef JEMALLOC_ENABLE_INLINE -void *imalloct(size_t size, bool try_tcache, arena_t *arena); -void *imalloc(size_t size); -void *icalloct(size_t size, bool try_tcache, arena_t *arena); -void *icalloc(size_t size); -void *ipalloct(size_t usize, size_t alignment, bool zero, bool try_tcache, - arena_t *arena); -void *ipalloc(size_t usize, size_t alignment, bool zero); +arena_t *iaalloc(const void *ptr); size_t isalloc(const void *ptr, bool demote); +void *iallocztm(tsd_t *tsd, size_t size, bool zero, tcache_t *tcache, + bool is_metadata, arena_t *arena); +void *imalloct(tsd_t *tsd, size_t size, tcache_t *tcache, arena_t *arena); +void *imalloc(tsd_t *tsd, size_t size); +void *icalloct(tsd_t *tsd, size_t size, tcache_t *tcache, arena_t *arena); +void *icalloc(tsd_t *tsd, size_t size); +void *ipallocztm(tsd_t *tsd, size_t usize, size_t alignment, bool zero, + tcache_t *tcache, bool is_metadata, arena_t *arena); +void *ipalloct(tsd_t *tsd, size_t usize, size_t alignment, bool zero, + tcache_t *tcache, arena_t *arena); +void *ipalloc(tsd_t *tsd, size_t usize, size_t alignment, bool zero); size_t ivsalloc(const void *ptr, bool demote); size_t u2rz(size_t usize); size_t p2rz(const void *ptr); -void idalloct(void *ptr, bool try_tcache); -void idalloc(void *ptr); -void iqalloct(void *ptr, bool try_tcache); -void iqalloc(void *ptr); -void *iralloct_realign(void *ptr, size_t oldsize, size_t size, size_t extra, - size_t alignment, bool zero, bool try_tcache_alloc, bool try_tcache_dalloc, +void idalloctm(tsd_t *tsd, void *ptr, tcache_t *tcache, bool is_metadata); +void idalloct(tsd_t *tsd, void *ptr, tcache_t *tcache); +void idalloc(tsd_t *tsd, void *ptr); +void iqalloc(tsd_t *tsd, void *ptr, tcache_t *tcache); +void isdalloct(tsd_t *tsd, void *ptr, size_t size, tcache_t *tcache); +void isqalloc(tsd_t *tsd, void *ptr, size_t size, tcache_t *tcache); +void *iralloct_realign(tsd_t *tsd, void *ptr, size_t oldsize, size_t size, + size_t extra, size_t alignment, bool zero, tcache_t *tcache, arena_t *arena); -void *iralloct(void *ptr, size_t size, size_t extra, size_t alignment, - bool zero, 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 ixalloc(void *ptr, size_t size, size_t extra, size_t alignment, - bool zero); -malloc_tsd_protos(JEMALLOC_ATTR(unused), thread_allocated, thread_allocated_t) +void *iralloct(tsd_t *tsd, void *ptr, size_t oldsize, size_t size, + size_t alignment, bool zero, tcache_t *tcache, arena_t *arena); +void *iralloc(tsd_t *tsd, void *ptr, size_t oldsize, size_t size, + size_t alignment, bool zero); +bool ixalloc(void *ptr, size_t oldsize, size_t size, size_t extra, + size_t alignment, bool zero); #endif #if (defined(JEMALLOC_ENABLE_INLINE) || defined(JEMALLOC_C_)) -JEMALLOC_ALWAYS_INLINE void * -imalloct(size_t size, bool try_tcache, arena_t *arena) +JEMALLOC_ALWAYS_INLINE arena_t * +iaalloc(const void *ptr) { - assert(size != 0); + assert(ptr != NULL); - if (size <= arena_maxclass) - return (arena_malloc(arena, size, false, try_tcache)); - else - return (huge_malloc(size, false, huge_dss_prec_get(arena))); -} - -JEMALLOC_ALWAYS_INLINE void * -imalloc(size_t size) -{ - - return (imalloct(size, true, NULL)); -} - -JEMALLOC_ALWAYS_INLINE void * -icalloct(size_t size, bool try_tcache, arena_t *arena) -{ - - if (size <= arena_maxclass) - return (arena_malloc(arena, size, true, try_tcache)); - else - return (huge_malloc(size, true, huge_dss_prec_get(arena))); -} - -JEMALLOC_ALWAYS_INLINE void * -icalloc(size_t size) -{ - - return (icalloct(size, true, NULL)); -} - -JEMALLOC_ALWAYS_INLINE void * -ipalloct(size_t usize, size_t alignment, bool zero, bool try_tcache, - arena_t *arena) -{ - void *ret; - - assert(usize != 0); - assert(usize == sa2u(usize, alignment)); - - if (usize <= arena_maxclass && alignment <= PAGE) - ret = arena_malloc(arena, usize, zero, try_tcache); - else { - if (usize <= arena_maxclass) { - ret = arena_palloc(choose_arena(arena), usize, - alignment, zero); - } else if (alignment <= chunksize) - ret = huge_malloc(usize, zero, huge_dss_prec_get(arena)); - else - ret = huge_palloc(usize, alignment, zero, huge_dss_prec_get(arena)); - } - - assert(ALIGNMENT_ADDR2BASE(ret, alignment) == ret); - return (ret); -} - -JEMALLOC_ALWAYS_INLINE void * -ipalloc(size_t usize, size_t alignment, bool zero) -{ - - return (ipalloct(usize, alignment, zero, true, NULL)); + return (arena_aalloc(ptr)); } /* @@ -837,29 +865,104 @@ ipalloc(size_t usize, size_t alignment, bool zero) JEMALLOC_ALWAYS_INLINE size_t isalloc(const void *ptr, bool demote) { - size_t ret; - arena_chunk_t *chunk; assert(ptr != NULL); /* Demotion only makes sense if config_prof is true. */ - assert(config_prof || demote == false); + assert(config_prof || !demote); - chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr); - if (chunk != ptr) - ret = arena_salloc(ptr, demote); - else - ret = huge_salloc(ptr); + return (arena_salloc(ptr, demote)); +} +JEMALLOC_ALWAYS_INLINE void * +iallocztm(tsd_t *tsd, size_t size, bool zero, tcache_t *tcache, bool is_metadata, + arena_t *arena) +{ + void *ret; + + assert(size != 0); + + ret = arena_malloc(tsd, arena, size, zero, tcache); + if (config_stats && is_metadata && likely(ret != NULL)) { + arena_metadata_allocated_add(iaalloc(ret), isalloc(ret, + config_prof)); + } return (ret); } +JEMALLOC_ALWAYS_INLINE void * +imalloct(tsd_t *tsd, size_t size, tcache_t *tcache, arena_t *arena) +{ + + return (iallocztm(tsd, size, false, tcache, false, arena)); +} + +JEMALLOC_ALWAYS_INLINE void * +imalloc(tsd_t *tsd, size_t size) +{ + + return (iallocztm(tsd, size, false, tcache_get(tsd, true), false, NULL)); +} + +JEMALLOC_ALWAYS_INLINE void * +icalloct(tsd_t *tsd, size_t size, tcache_t *tcache, arena_t *arena) +{ + + return (iallocztm(tsd, size, true, tcache, false, arena)); +} + +JEMALLOC_ALWAYS_INLINE void * +icalloc(tsd_t *tsd, size_t size) +{ + + return (iallocztm(tsd, size, true, tcache_get(tsd, true), false, NULL)); +} + +JEMALLOC_ALWAYS_INLINE void * +ipallocztm(tsd_t *tsd, size_t usize, size_t alignment, bool zero, + tcache_t *tcache, bool is_metadata, arena_t *arena) +{ + void *ret; + + assert(usize != 0); + assert(usize == sa2u(usize, alignment)); + + ret = arena_palloc(tsd, arena, usize, alignment, zero, tcache); + assert(ALIGNMENT_ADDR2BASE(ret, alignment) == ret); + if (config_stats && is_metadata && likely(ret != NULL)) { + arena_metadata_allocated_add(iaalloc(ret), isalloc(ret, + config_prof)); + } + return (ret); +} + +JEMALLOC_ALWAYS_INLINE void * +ipalloct(tsd_t *tsd, size_t usize, size_t alignment, bool zero, + tcache_t *tcache, arena_t *arena) +{ + + return (ipallocztm(tsd, usize, alignment, zero, tcache, false, arena)); +} + +JEMALLOC_ALWAYS_INLINE void * +ipalloc(tsd_t *tsd, size_t usize, size_t alignment, bool zero) +{ + + return (ipallocztm(tsd, usize, alignment, zero, tcache_get(tsd, + NULL), false, NULL)); +} + JEMALLOC_ALWAYS_INLINE size_t ivsalloc(const void *ptr, bool demote) { + extent_node_t *node; /* Return 0 if ptr is not within a chunk managed by jemalloc. */ - if (rtree_get(chunks_rtree, (uintptr_t)CHUNK_ADDR2BASE(ptr)) == 0) + node = chunk_lookup(ptr, false); + if (node == NULL) return (0); + /* Only arena chunks should be looked up via interior pointers. */ + assert(extent_node_addr_get(node) == ptr || + extent_node_achunk_get(node)); return (isalloc(ptr, demote)); } @@ -870,7 +973,7 @@ u2rz(size_t usize) size_t ret; if (usize <= SMALL_MAXCLASS) { - size_t binind = SMALL_SIZE2BIN(usize); + index_t binind = size2index(usize); ret = arena_bin_info[binind].redzone_size; } else ret = 0; @@ -887,47 +990,62 @@ p2rz(const void *ptr) } JEMALLOC_ALWAYS_INLINE void -idalloct(void *ptr, bool try_tcache) +idalloctm(tsd_t *tsd, void *ptr, tcache_t *tcache, bool is_metadata) { - arena_chunk_t *chunk; assert(ptr != NULL); + if (config_stats && is_metadata) { + arena_metadata_allocated_sub(iaalloc(ptr), isalloc(ptr, + config_prof)); + } - chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr); - if (chunk != ptr) - arena_dalloc(chunk->arena, chunk, ptr, try_tcache); + arena_dalloc(tsd, ptr, tcache); +} + +JEMALLOC_ALWAYS_INLINE void +idalloct(tsd_t *tsd, void *ptr, tcache_t *tcache) +{ + + idalloctm(tsd, ptr, tcache, false); +} + +JEMALLOC_ALWAYS_INLINE void +idalloc(tsd_t *tsd, void *ptr) +{ + + idalloctm(tsd, ptr, tcache_get(tsd, false), false); +} + +JEMALLOC_ALWAYS_INLINE void +iqalloc(tsd_t *tsd, void *ptr, tcache_t *tcache) +{ + + if (config_fill && unlikely(opt_quarantine)) + quarantine(tsd, ptr); else - huge_dalloc(ptr, true); + idalloctm(tsd, ptr, tcache, false); } JEMALLOC_ALWAYS_INLINE void -idalloc(void *ptr) +isdalloct(tsd_t *tsd, void *ptr, size_t size, tcache_t *tcache) { - idalloct(ptr, true); + arena_sdalloc(tsd, ptr, size, tcache); } JEMALLOC_ALWAYS_INLINE void -iqalloct(void *ptr, bool try_tcache) +isqalloc(tsd_t *tsd, void *ptr, size_t size, tcache_t *tcache) { - if (config_fill && opt_quarantine) - quarantine(ptr); + if (config_fill && unlikely(opt_quarantine)) + quarantine(tsd, ptr); else - idalloct(ptr, try_tcache); -} - -JEMALLOC_ALWAYS_INLINE void -iqalloc(void *ptr) -{ - - iqalloct(ptr, true); + isdalloct(tsd, ptr, size, tcache); } JEMALLOC_ALWAYS_INLINE void * -iralloct_realign(void *ptr, size_t oldsize, size_t size, size_t extra, - size_t alignment, bool zero, bool try_tcache_alloc, bool try_tcache_dalloc, - arena_t *arena) +iralloct_realign(tsd_t *tsd, void *ptr, size_t oldsize, size_t size, + size_t extra, size_t alignment, bool zero, tcache_t *tcache, arena_t *arena) { void *p; size_t usize, copysize; @@ -935,7 +1053,7 @@ iralloct_realign(void *ptr, size_t oldsize, size_t size, size_t extra, usize = sa2u(size + extra, alignment); if (usize == 0) return (NULL); - p = ipalloct(usize, alignment, zero, try_tcache_alloc, arena); + p = ipalloct(tsd, usize, alignment, zero, tcache, arena); if (p == NULL) { if (extra == 0) return (NULL); @@ -943,7 +1061,7 @@ iralloct_realign(void *ptr, size_t oldsize, size_t size, size_t extra, usize = sa2u(size, alignment); if (usize == 0) return (NULL); - p = ipalloct(usize, alignment, zero, try_tcache_alloc, arena); + p = ipalloct(tsd, usize, alignment, zero, tcache, arena); if (p == NULL) return (NULL); } @@ -953,72 +1071,57 @@ iralloct_realign(void *ptr, size_t oldsize, size_t size, size_t extra, */ copysize = (size < oldsize) ? size : oldsize; memcpy(p, ptr, copysize); - iqalloct(ptr, try_tcache_dalloc); + isqalloc(tsd, ptr, oldsize, tcache); return (p); } JEMALLOC_ALWAYS_INLINE void * -iralloct(void *ptr, size_t size, size_t extra, size_t alignment, bool zero, - bool try_tcache_alloc, bool try_tcache_dalloc, arena_t *arena) +iralloct(tsd_t *tsd, void *ptr, size_t oldsize, size_t size, size_t alignment, + bool zero, tcache_t *tcache, arena_t *arena) { - size_t oldsize; assert(ptr != NULL); assert(size != 0); - oldsize = isalloc(ptr, config_prof); - if (alignment != 0 && ((uintptr_t)ptr & ((uintptr_t)alignment-1)) != 0) { /* * Existing object alignment is inadequate; allocate new space * and copy. */ - return (iralloct_realign(ptr, oldsize, size, extra, alignment, - zero, try_tcache_alloc, try_tcache_dalloc, arena)); + return (iralloct_realign(tsd, ptr, oldsize, size, 0, alignment, + zero, tcache, arena)); } - if (size + extra <= arena_maxclass) { - 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, try_tcache_dalloc, huge_dss_prec_get(arena))); - } + return (arena_ralloc(tsd, arena, ptr, oldsize, size, 0, alignment, zero, + tcache)); } JEMALLOC_ALWAYS_INLINE void * -iralloc(void *ptr, size_t size, size_t extra, size_t alignment, bool zero) +iralloc(tsd_t *tsd, void *ptr, size_t oldsize, size_t size, size_t alignment, + bool zero) { - return (iralloct(ptr, size, extra, alignment, zero, true, true, NULL)); + return (iralloct(tsd, ptr, oldsize, size, alignment, zero, + tcache_get(tsd, true), NULL)); } JEMALLOC_ALWAYS_INLINE bool -ixalloc(void *ptr, size_t size, size_t extra, size_t alignment, bool zero) +ixalloc(void *ptr, size_t oldsize, size_t size, size_t extra, size_t alignment, + bool zero) { - size_t oldsize; assert(ptr != NULL); assert(size != 0); - oldsize = isalloc(ptr, config_prof); if (alignment != 0 && ((uintptr_t)ptr & ((uintptr_t)alignment-1)) != 0) { /* Existing object alignment is inadequate. */ return (true); } - if (size <= arena_maxclass) - return (arena_ralloc_no_move(ptr, oldsize, size, extra, zero)); - else - return (huge_ralloc_no_move(ptr, oldsize, size, extra)); + return (arena_ralloc_no_move(ptr, oldsize, size, extra, zero)); } - -malloc_tsd_externs(thread_allocated, thread_allocated_t) -malloc_tsd_funcs(JEMALLOC_ALWAYS_INLINE, thread_allocated, thread_allocated_t, - THREAD_ALLOCATED_INITIALIZER, malloc_tsd_no_cleanup) #endif #include "jemalloc/internal/prof.h" diff --git a/contrib/jemalloc/include/jemalloc/internal/jemalloc_internal_decls.h b/contrib/jemalloc/include/jemalloc/internal/jemalloc_internal_decls.h new file mode 100644 index 000000000000..e7094b22d1d4 --- /dev/null +++ b/contrib/jemalloc/include/jemalloc/internal/jemalloc_internal_decls.h @@ -0,0 +1,67 @@ +#ifndef JEMALLOC_INTERNAL_DECLS_H +#define JEMALLOC_INTERNAL_DECLS_H + +#include "libc_private.h" +#include "namespace.h" + +#include +#ifdef _WIN32 +# include +# include "msvc_compat/windows_extra.h" + +#else +# include +# include +# if !defined(__pnacl__) && !defined(__native_client__) +# include +# if !defined(SYS_write) && defined(__NR_write) +# define SYS_write __NR_write +# endif +# include +# endif +# include +# include +#endif +#include + +#include +#ifndef SIZE_T_MAX +# define SIZE_T_MAX SIZE_MAX +#endif +#include +#include +#include +#include +#include +#include +#ifndef offsetof +# define offsetof(type, member) ((size_t)&(((type *)NULL)->member)) +#endif +#include +#include +#include +#ifdef _MSC_VER +# include +typedef intptr_t ssize_t; +# define PATH_MAX 1024 +# define STDERR_FILENO 2 +# define __func__ __FUNCTION__ +# ifdef JEMALLOC_HAS_RESTRICT +# define restrict __restrict +# endif +/* Disable warnings about deprecated system functions. */ +# pragma warning(disable: 4996) +#if _MSC_VER < 1800 +static int +isblank(int c) +{ + + return (c == '\t' || c == ' '); +} +#endif +#else +# include +#endif +#include + +#endif /* JEMALLOC_INTERNAL_H */ diff --git a/contrib/jemalloc/include/jemalloc/internal/jemalloc_internal_defs.h b/contrib/jemalloc/include/jemalloc/internal/jemalloc_internal_defs.h index 1ca25573a128..fa871fbfaee0 100644 --- a/contrib/jemalloc/include/jemalloc/internal/jemalloc_internal_defs.h +++ b/contrib/jemalloc/include/jemalloc/internal/jemalloc_internal_defs.h @@ -23,6 +23,9 @@ */ #define CPU_SPINWAIT __asm__ volatile("pause") +/* Defined if C11 atomics are available. */ +/* #undef JEMALLOC_C11ATOMICS */ + /* Defined if the equivalent of FreeBSD's atomic(9) functions are available. */ #define JEMALLOC_ATOMIC9 1 @@ -36,7 +39,7 @@ * Defined if __sync_add_and_fetch(uint32_t *, uint32_t) and * __sync_sub_and_fetch(uint32_t *, uint32_t) are available, despite * __GCC_HAVE_SYNC_COMPARE_AND_SWAP_4 not being defined (which means the - * functions are defined in libgcc instead of being inlines) + * functions are defined in libgcc instead of being inlines). */ /* #undef JE_FORCE_SYNC_COMPARE_AND_SWAP_4 */ @@ -44,16 +47,36 @@ * Defined if __sync_add_and_fetch(uint64_t *, uint64_t) and * __sync_sub_and_fetch(uint64_t *, uint64_t) are available, despite * __GCC_HAVE_SYNC_COMPARE_AND_SWAP_8 not being defined (which means the - * functions are defined in libgcc instead of being inlines) + * functions are defined in libgcc instead of being inlines). */ /* #undef JE_FORCE_SYNC_COMPARE_AND_SWAP_8 */ +/* + * Defined if __builtin_clz() and __builtin_clzl() are available. + */ +#define JEMALLOC_HAVE_BUILTIN_CLZ + +/* + * Defined if madvise(2) is available. + */ +#define JEMALLOC_HAVE_MADVISE + /* * Defined if OSSpin*() functions are available, as provided by Darwin, and * documented in the spinlock(3) manual page. */ /* #undef JEMALLOC_OSSPIN */ +/* + * Defined if secure_getenv(3) is available. + */ +/* #undef JEMALLOC_HAVE_SECURE_GETENV */ + +/* + * Defined if issetugid(2) is available. + */ +#define JEMALLOC_HAVE_ISSETUGID + /* * Defined if _malloc_thread_cleanup() exists. At least in the case of * FreeBSD, pthread_key_create() allocates, which if used during malloc @@ -77,9 +100,6 @@ */ #define JEMALLOC_MUTEX_INIT_CB 1 -/* Defined if sbrk() is supported. */ -#define JEMALLOC_HAVE_SBRK - /* Non-empty if the tls_model attribute is supported. */ #define JEMALLOC_TLS_MODEL __attribute__((tls_model("initial-exec"))) @@ -138,8 +158,26 @@ /* Support lazy locking (avoid locking unless a second thread is launched). */ #define JEMALLOC_LAZY_LOCK -/* One page is 2^STATIC_PAGE_SHIFT bytes. */ -#define STATIC_PAGE_SHIFT 12 +/* Minimum size class to support is 2^LG_TINY_MIN bytes. */ +#define LG_TINY_MIN 3 + +/* + * Minimum allocation alignment is 2^LG_QUANTUM bytes (ignoring tiny size + * classes). + */ +/* #undef LG_QUANTUM */ + +/* One page is 2^LG_PAGE bytes. */ +#define LG_PAGE 12 + +/* + * If defined, adjacent virtual memory mappings with identical attributes + * automatically coalesce, and they fragment when changes are made to subranges. + * This is the normal order of things for mmap()/munmap(), but on Windows + * VirtualAlloc()/VirtualFree() operations must be precisely matched, i.e. + * mappings do *not* coalesce/fragment. + */ +#define JEMALLOC_MAPS_COALESCE /* * If defined, use munmap() to unmap freed chunks, rather than storing them for @@ -148,22 +186,28 @@ */ #define JEMALLOC_MUNMAP -/* - * If defined, use mremap(...MREMAP_FIXED...) for huge realloc(). This is - * disabled by default because it is Linux-specific and it will cause virtual - * memory map holes, much like munmap(2) does. - */ -/* #undef JEMALLOC_MREMAP */ - /* TLS is used to map arenas and magazine caches to threads. */ #define JEMALLOC_TLS +/* + * ffs()/ffsl() functions to use for bitmapping. Don't use these directly; + * instead, use jemalloc_ffs() or jemalloc_ffsl() from util.h. + */ +#define JEMALLOC_INTERNAL_FFSL __builtin_ffsl +#define JEMALLOC_INTERNAL_FFS __builtin_ffs + /* * JEMALLOC_IVSALLOC enables ivsalloc(), which verifies that pointers reside * within jemalloc-owned chunks before dereferencing them. */ /* #undef JEMALLOC_IVSALLOC */ +/* + * If defined, explicitly attempt to more uniformly distribute large allocation + * pointer alignments across all cache indices. + */ +#define JEMALLOC_CACHE_OBLIVIOUS + /* * Darwin (OS X) uses zones to work around Mach-O symbol override shortcomings. */ @@ -183,9 +227,7 @@ /* #undef JEMALLOC_PURGE_MADVISE_DONTNEED */ #define JEMALLOC_PURGE_MADVISE_FREE -/* - * Define if operating system has alloca.h header. - */ +/* Define if operating system has alloca.h header. */ /* #undef JEMALLOC_HAS_ALLOCA_H */ /* C99 restrict keyword supported. */ @@ -203,4 +245,19 @@ /* sizeof(intmax_t) == 2^LG_SIZEOF_INTMAX_T. */ #define LG_SIZEOF_INTMAX_T 3 +/* glibc malloc hooks (__malloc_hook, __realloc_hook, __free_hook). */ +/* #undef JEMALLOC_GLIBC_MALLOC_HOOK */ + +/* glibc memalign hook. */ +/* #undef JEMALLOC_GLIBC_MEMALIGN_HOOK */ + +/* Adaptive mutex support in pthreads. */ +#define JEMALLOC_HAVE_PTHREAD_MUTEX_ADAPTIVE_NP + +/* + * If defined, jemalloc symbols are not exported (doesn't work when + * JEMALLOC_PREFIX is not defined). + */ +/* #undef JEMALLOC_EXPORT */ + #endif /* JEMALLOC_INTERNAL_DEFS_H_ */ diff --git a/contrib/jemalloc/include/jemalloc/internal/jemalloc_internal_macros.h b/contrib/jemalloc/include/jemalloc/internal/jemalloc_internal_macros.h index 4e2392302c76..a08ba772ead4 100644 --- a/contrib/jemalloc/include/jemalloc/internal/jemalloc_internal_macros.h +++ b/contrib/jemalloc/include/jemalloc/internal/jemalloc_internal_macros.h @@ -39,9 +39,15 @@ #endif #define ZU(z) ((size_t)z) +#define ZI(z) ((ssize_t)z) #define QU(q) ((uint64_t)q) #define QI(q) ((int64_t)q) +#define KZU(z) ZU(z##ULL) +#define KZI(z) ZI(z##LL) +#define KQU(q) QU(q##ULL) +#define KQI(q) QI(q##LL) + #ifndef __DECONST # define __DECONST(type, var) ((type)(uintptr_t)(const void *)(var)) #endif diff --git a/contrib/jemalloc/include/jemalloc/internal/mutex.h b/contrib/jemalloc/include/jemalloc/internal/mutex.h index dcd91470485a..561378fd8ad0 100644 --- a/contrib/jemalloc/include/jemalloc/internal/mutex.h +++ b/contrib/jemalloc/include/jemalloc/internal/mutex.h @@ -10,7 +10,7 @@ typedef struct malloc_mutex_s malloc_mutex_t; #elif (defined(JEMALLOC_MUTEX_INIT_CB)) # define MALLOC_MUTEX_INITIALIZER {PTHREAD_MUTEX_INITIALIZER, NULL} #else -# if (defined(PTHREAD_MUTEX_ADAPTIVE_NP) && \ +# if (defined(JEMALLOC_HAVE_PTHREAD_MUTEX_ADAPTIVE_NP) && \ defined(PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP)) # define MALLOC_MUTEX_TYPE PTHREAD_MUTEX_ADAPTIVE_NP # define MALLOC_MUTEX_INITIALIZER {PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP} @@ -26,7 +26,11 @@ typedef struct malloc_mutex_s malloc_mutex_t; struct malloc_mutex_s { #ifdef _WIN32 +# if _WIN32_WINNT >= 0x0600 + SRWLOCK lock; +# else CRITICAL_SECTION lock; +# endif #elif (defined(JEMALLOC_OSSPIN)) OSSpinLock lock; #elif (defined(JEMALLOC_MUTEX_INIT_CB)) @@ -68,7 +72,11 @@ malloc_mutex_lock(malloc_mutex_t *mutex) if (isthreaded) { #ifdef _WIN32 +# if _WIN32_WINNT >= 0x0600 + AcquireSRWLockExclusive(&mutex->lock); +# else EnterCriticalSection(&mutex->lock); +# endif #elif (defined(JEMALLOC_OSSPIN)) OSSpinLockLock(&mutex->lock); #else @@ -83,7 +91,11 @@ malloc_mutex_unlock(malloc_mutex_t *mutex) if (isthreaded) { #ifdef _WIN32 +# if _WIN32_WINNT >= 0x0600 + ReleaseSRWLockExclusive(&mutex->lock); +# else LeaveCriticalSection(&mutex->lock); +# endif #elif (defined(JEMALLOC_OSSPIN)) OSSpinLockUnlock(&mutex->lock); #else diff --git a/contrib/jemalloc/include/jemalloc/internal/pages.h b/contrib/jemalloc/include/jemalloc/internal/pages.h new file mode 100644 index 000000000000..da7eb9686db8 --- /dev/null +++ b/contrib/jemalloc/include/jemalloc/internal/pages.h @@ -0,0 +1,26 @@ +/******************************************************************************/ +#ifdef JEMALLOC_H_TYPES + +#endif /* JEMALLOC_H_TYPES */ +/******************************************************************************/ +#ifdef JEMALLOC_H_STRUCTS + +#endif /* JEMALLOC_H_STRUCTS */ +/******************************************************************************/ +#ifdef JEMALLOC_H_EXTERNS + +void *pages_map(void *addr, size_t size); +void pages_unmap(void *addr, size_t size); +void *pages_trim(void *addr, size_t alloc_size, size_t leadsize, + size_t size); +bool pages_commit(void *addr, size_t size); +bool pages_decommit(void *addr, size_t size); +bool pages_purge(void *addr, size_t size); + +#endif /* JEMALLOC_H_EXTERNS */ +/******************************************************************************/ +#ifdef JEMALLOC_H_INLINES + +#endif /* JEMALLOC_H_INLINES */ +/******************************************************************************/ + diff --git a/contrib/jemalloc/include/jemalloc/internal/private_namespace.h b/contrib/jemalloc/include/jemalloc/internal/private_namespace.h index d790c8b8ab1b..10fab6583c25 100644 --- a/contrib/jemalloc/include/jemalloc/internal/private_namespace.h +++ b/contrib/jemalloc/include/jemalloc/internal/private_namespace.h @@ -1,44 +1,75 @@ -#define a0calloc JEMALLOC_N(a0calloc) -#define a0free JEMALLOC_N(a0free) +#define a0dalloc JEMALLOC_N(a0dalloc) +#define a0get JEMALLOC_N(a0get) #define a0malloc JEMALLOC_N(a0malloc) +#define arena_aalloc JEMALLOC_N(arena_aalloc) #define arena_alloc_junk_small JEMALLOC_N(arena_alloc_junk_small) #define arena_bin_index JEMALLOC_N(arena_bin_index) #define arena_bin_info JEMALLOC_N(arena_bin_info) +#define arena_bitselm_get JEMALLOC_N(arena_bitselm_get) #define arena_boot JEMALLOC_N(arena_boot) +#define arena_choose JEMALLOC_N(arena_choose) +#define arena_choose_hard JEMALLOC_N(arena_choose_hard) +#define arena_chunk_alloc_huge JEMALLOC_N(arena_chunk_alloc_huge) +#define arena_chunk_cache_maybe_insert JEMALLOC_N(arena_chunk_cache_maybe_insert) +#define arena_chunk_cache_maybe_remove JEMALLOC_N(arena_chunk_cache_maybe_remove) +#define arena_chunk_dalloc_huge JEMALLOC_N(arena_chunk_dalloc_huge) +#define arena_chunk_ralloc_huge_expand JEMALLOC_N(arena_chunk_ralloc_huge_expand) +#define arena_chunk_ralloc_huge_shrink JEMALLOC_N(arena_chunk_ralloc_huge_shrink) +#define arena_chunk_ralloc_huge_similar JEMALLOC_N(arena_chunk_ralloc_huge_similar) +#define arena_cleanup JEMALLOC_N(arena_cleanup) #define arena_dalloc JEMALLOC_N(arena_dalloc) #define arena_dalloc_bin JEMALLOC_N(arena_dalloc_bin) -#define arena_dalloc_bin_locked JEMALLOC_N(arena_dalloc_bin_locked) +#define arena_dalloc_bin_junked_locked JEMALLOC_N(arena_dalloc_bin_junked_locked) #define arena_dalloc_junk_large JEMALLOC_N(arena_dalloc_junk_large) #define arena_dalloc_junk_small JEMALLOC_N(arena_dalloc_junk_small) #define arena_dalloc_large JEMALLOC_N(arena_dalloc_large) -#define arena_dalloc_large_locked JEMALLOC_N(arena_dalloc_large_locked) +#define arena_dalloc_large_junked_locked JEMALLOC_N(arena_dalloc_large_junked_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_get JEMALLOC_N(arena_get) +#define arena_get_hard JEMALLOC_N(arena_get_hard) +#define arena_init JEMALLOC_N(arena_init) +#define arena_lg_dirty_mult_default_get JEMALLOC_N(arena_lg_dirty_mult_default_get) +#define arena_lg_dirty_mult_default_set JEMALLOC_N(arena_lg_dirty_mult_default_set) +#define arena_lg_dirty_mult_get JEMALLOC_N(arena_lg_dirty_mult_get) +#define arena_lg_dirty_mult_set JEMALLOC_N(arena_lg_dirty_mult_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) #define arena_mapbits_allocated_get JEMALLOC_N(arena_mapbits_allocated_get) #define arena_mapbits_binind_get JEMALLOC_N(arena_mapbits_binind_get) +#define arena_mapbits_decommitted_get JEMALLOC_N(arena_mapbits_decommitted_get) #define arena_mapbits_dirty_get JEMALLOC_N(arena_mapbits_dirty_get) #define arena_mapbits_get JEMALLOC_N(arena_mapbits_get) +#define arena_mapbits_internal_set JEMALLOC_N(arena_mapbits_internal_set) #define arena_mapbits_large_binind_set JEMALLOC_N(arena_mapbits_large_binind_set) #define arena_mapbits_large_get JEMALLOC_N(arena_mapbits_large_get) #define arena_mapbits_large_set JEMALLOC_N(arena_mapbits_large_set) #define arena_mapbits_large_size_get JEMALLOC_N(arena_mapbits_large_size_get) +#define arena_mapbitsp_get JEMALLOC_N(arena_mapbitsp_get) +#define arena_mapbitsp_read JEMALLOC_N(arena_mapbitsp_read) +#define arena_mapbitsp_write JEMALLOC_N(arena_mapbitsp_write) #define arena_mapbits_small_runind_get JEMALLOC_N(arena_mapbits_small_runind_get) #define arena_mapbits_small_set JEMALLOC_N(arena_mapbits_small_set) #define arena_mapbits_unallocated_set JEMALLOC_N(arena_mapbits_unallocated_set) #define arena_mapbits_unallocated_size_get JEMALLOC_N(arena_mapbits_unallocated_size_get) #define arena_mapbits_unallocated_size_set JEMALLOC_N(arena_mapbits_unallocated_size_set) #define arena_mapbits_unzeroed_get JEMALLOC_N(arena_mapbits_unzeroed_get) -#define arena_mapbits_unzeroed_set JEMALLOC_N(arena_mapbits_unzeroed_set) -#define arena_mapbitsp_get JEMALLOC_N(arena_mapbitsp_get) -#define arena_mapbitsp_read JEMALLOC_N(arena_mapbitsp_read) -#define arena_mapbitsp_write JEMALLOC_N(arena_mapbitsp_write) -#define arena_mapp_get JEMALLOC_N(arena_mapp_get) #define arena_maxclass JEMALLOC_N(arena_maxclass) +#define arena_maxrun JEMALLOC_N(arena_maxrun) +#define arena_maybe_purge JEMALLOC_N(arena_maybe_purge) +#define arena_metadata_allocated_add JEMALLOC_N(arena_metadata_allocated_add) +#define arena_metadata_allocated_get JEMALLOC_N(arena_metadata_allocated_get) +#define arena_metadata_allocated_sub JEMALLOC_N(arena_metadata_allocated_sub) +#define arena_migrate JEMALLOC_N(arena_migrate) +#define arena_miscelm_get JEMALLOC_N(arena_miscelm_get) +#define arena_miscelm_to_pageind JEMALLOC_N(arena_miscelm_to_pageind) +#define arena_miscelm_to_rpages JEMALLOC_N(arena_miscelm_to_rpages) +#define arena_nbound JEMALLOC_N(arena_nbound) #define arena_new JEMALLOC_N(arena_new) +#define arena_node_alloc JEMALLOC_N(arena_node_alloc) +#define arena_node_dalloc JEMALLOC_N(arena_node_dalloc) #define arena_palloc JEMALLOC_N(arena_palloc) #define arena_postfork_child JEMALLOC_N(arena_postfork_child) #define arena_postfork_parent JEMALLOC_N(arena_postfork_parent) @@ -46,50 +77,46 @@ #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) +#define arena_prof_tctx_get JEMALLOC_N(arena_prof_tctx_get) +#define arena_prof_tctx_set JEMALLOC_N(arena_prof_tctx_set) #define arena_ptr_small_binind_get JEMALLOC_N(arena_ptr_small_binind_get) #define arena_purge_all JEMALLOC_N(arena_purge_all) #define arena_quarantine_junk_small JEMALLOC_N(arena_quarantine_junk_small) #define arena_ralloc JEMALLOC_N(arena_ralloc) #define arena_ralloc_junk_large JEMALLOC_N(arena_ralloc_junk_large) #define arena_ralloc_no_move JEMALLOC_N(arena_ralloc_no_move) +#define arena_rd_to_miscelm JEMALLOC_N(arena_rd_to_miscelm) #define arena_redzone_corruption JEMALLOC_N(arena_redzone_corruption) #define arena_run_regind JEMALLOC_N(arena_run_regind) +#define arena_run_to_miscelm JEMALLOC_N(arena_run_to_miscelm) #define arena_salloc JEMALLOC_N(arena_salloc) +#define arenas_cache_bypass_cleanup JEMALLOC_N(arenas_cache_bypass_cleanup) +#define arenas_cache_cleanup JEMALLOC_N(arenas_cache_cleanup) +#define arena_sdalloc JEMALLOC_N(arena_sdalloc) #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_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_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_init_head JEMALLOC_N(arenas_tsd_init_head) -#define arenas_tsd_set JEMALLOC_N(arenas_tsd_set) +#define atomic_add_p JEMALLOC_N(atomic_add_p) #define atomic_add_u JEMALLOC_N(atomic_add_u) #define atomic_add_uint32 JEMALLOC_N(atomic_add_uint32) #define atomic_add_uint64 JEMALLOC_N(atomic_add_uint64) #define atomic_add_z JEMALLOC_N(atomic_add_z) +#define atomic_cas_p JEMALLOC_N(atomic_cas_p) +#define atomic_cas_u JEMALLOC_N(atomic_cas_u) +#define atomic_cas_uint32 JEMALLOC_N(atomic_cas_uint32) +#define atomic_cas_uint64 JEMALLOC_N(atomic_cas_uint64) +#define atomic_cas_z JEMALLOC_N(atomic_cas_z) +#define atomic_sub_p JEMALLOC_N(atomic_sub_p) #define atomic_sub_u JEMALLOC_N(atomic_sub_u) #define atomic_sub_uint32 JEMALLOC_N(atomic_sub_uint32) #define atomic_sub_uint64 JEMALLOC_N(atomic_sub_uint64) #define atomic_sub_z JEMALLOC_N(atomic_sub_z) #define base_alloc JEMALLOC_N(base_alloc) #define base_boot JEMALLOC_N(base_boot) -#define base_calloc JEMALLOC_N(base_calloc) -#define base_node_alloc JEMALLOC_N(base_node_alloc) -#define base_node_dealloc JEMALLOC_N(base_node_dealloc) #define base_postfork_child JEMALLOC_N(base_postfork_child) #define base_postfork_parent JEMALLOC_N(base_postfork_parent) #define base_prefork JEMALLOC_N(base_prefork) +#define base_stats_get JEMALLOC_N(base_stats_get) #define bitmap_full JEMALLOC_N(bitmap_full) #define bitmap_get JEMALLOC_N(bitmap_get) #define bitmap_info_init JEMALLOC_N(bitmap_info_init) @@ -99,49 +126,54 @@ #define bitmap_sfu JEMALLOC_N(bitmap_sfu) #define bitmap_size JEMALLOC_N(bitmap_size) #define bitmap_unset JEMALLOC_N(bitmap_unset) +#define bootstrap_calloc JEMALLOC_N(bootstrap_calloc) +#define bootstrap_free JEMALLOC_N(bootstrap_free) +#define bootstrap_malloc JEMALLOC_N(bootstrap_malloc) #define bt_init JEMALLOC_N(bt_init) #define buferror JEMALLOC_N(buferror) -#define choose_arena JEMALLOC_N(choose_arena) -#define choose_arena_hard JEMALLOC_N(choose_arena_hard) -#define chunk_alloc JEMALLOC_N(chunk_alloc) +#define chunk_alloc_base JEMALLOC_N(chunk_alloc_base) +#define chunk_alloc_cache JEMALLOC_N(chunk_alloc_cache) #define chunk_alloc_dss JEMALLOC_N(chunk_alloc_dss) #define chunk_alloc_mmap JEMALLOC_N(chunk_alloc_mmap) +#define chunk_alloc_wrapper JEMALLOC_N(chunk_alloc_wrapper) #define chunk_boot JEMALLOC_N(chunk_boot) -#define chunk_dealloc JEMALLOC_N(chunk_dealloc) -#define chunk_dealloc_mmap JEMALLOC_N(chunk_dealloc_mmap) +#define chunk_dalloc_arena JEMALLOC_N(chunk_dalloc_arena) +#define chunk_dalloc_cache JEMALLOC_N(chunk_dalloc_cache) +#define chunk_dalloc_mmap JEMALLOC_N(chunk_dalloc_mmap) +#define chunk_dalloc_wrapper JEMALLOC_N(chunk_dalloc_wrapper) +#define chunk_deregister JEMALLOC_N(chunk_deregister) #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_hooks_default JEMALLOC_N(chunk_hooks_default) +#define chunk_hooks_get JEMALLOC_N(chunk_hooks_get) +#define chunk_hooks_set JEMALLOC_N(chunk_hooks_set) #define chunk_in_dss JEMALLOC_N(chunk_in_dss) +#define chunk_lookup JEMALLOC_N(chunk_lookup) #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 chunk_purge_arena JEMALLOC_N(chunk_purge_arena) +#define chunk_purge_wrapper JEMALLOC_N(chunk_purge_wrapper) +#define chunk_register JEMALLOC_N(chunk_register) #define chunksize JEMALLOC_N(chunksize) #define chunksize_mask JEMALLOC_N(chunksize_mask) -#define ckh_bucket_search JEMALLOC_N(ckh_bucket_search) +#define chunks_rtree JEMALLOC_N(chunks_rtree) #define ckh_count JEMALLOC_N(ckh_count) #define ckh_delete JEMALLOC_N(ckh_delete) -#define ckh_evict_reloc_insert JEMALLOC_N(ckh_evict_reloc_insert) #define ckh_insert JEMALLOC_N(ckh_insert) -#define ckh_isearch JEMALLOC_N(ckh_isearch) #define ckh_iter JEMALLOC_N(ckh_iter) #define ckh_new JEMALLOC_N(ckh_new) #define ckh_pointer_hash JEMALLOC_N(ckh_pointer_hash) #define ckh_pointer_keycomp JEMALLOC_N(ckh_pointer_keycomp) -#define ckh_rebuild JEMALLOC_N(ckh_rebuild) #define ckh_remove JEMALLOC_N(ckh_remove) #define ckh_search JEMALLOC_N(ckh_search) #define ckh_string_hash JEMALLOC_N(ckh_string_hash) #define ckh_string_keycomp JEMALLOC_N(ckh_string_keycomp) -#define ckh_try_bucket_insert JEMALLOC_N(ckh_try_bucket_insert) -#define ckh_try_insert JEMALLOC_N(ckh_try_insert) #define ctl_boot JEMALLOC_N(ctl_boot) #define ctl_bymib JEMALLOC_N(ctl_bymib) #define ctl_byname JEMALLOC_N(ctl_byname) @@ -150,6 +182,23 @@ #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_node_achunk_get JEMALLOC_N(extent_node_achunk_get) +#define extent_node_achunk_set JEMALLOC_N(extent_node_achunk_set) +#define extent_node_addr_get JEMALLOC_N(extent_node_addr_get) +#define extent_node_addr_set JEMALLOC_N(extent_node_addr_set) +#define extent_node_arena_get JEMALLOC_N(extent_node_arena_get) +#define extent_node_arena_set JEMALLOC_N(extent_node_arena_set) +#define extent_node_dirty_insert JEMALLOC_N(extent_node_dirty_insert) +#define extent_node_dirty_linkage_init JEMALLOC_N(extent_node_dirty_linkage_init) +#define extent_node_dirty_remove JEMALLOC_N(extent_node_dirty_remove) +#define extent_node_init JEMALLOC_N(extent_node_init) +#define extent_node_prof_tctx_get JEMALLOC_N(extent_node_prof_tctx_get) +#define extent_node_prof_tctx_set JEMALLOC_N(extent_node_prof_tctx_set) +#define extent_node_size_get JEMALLOC_N(extent_node_size_get) +#define extent_node_size_set JEMALLOC_N(extent_node_size_set) +#define extent_node_zeroed_get JEMALLOC_N(extent_node_zeroed_get) +#define extent_node_zeroed_set JEMALLOC_N(extent_node_zeroed_set) +#define extent_tree_ad_empty JEMALLOC_N(extent_tree_ad_empty) #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) @@ -166,6 +215,7 @@ #define extent_tree_ad_reverse_iter_recurse JEMALLOC_N(extent_tree_ad_reverse_iter_recurse) #define extent_tree_ad_reverse_iter_start JEMALLOC_N(extent_tree_ad_reverse_iter_start) #define extent_tree_ad_search JEMALLOC_N(extent_tree_ad_search) +#define extent_tree_szad_empty JEMALLOC_N(extent_tree_szad_empty) #define extent_tree_szad_first JEMALLOC_N(extent_tree_szad_first) #define extent_tree_szad_insert JEMALLOC_N(extent_tree_szad_insert) #define extent_tree_szad_iter JEMALLOC_N(extent_tree_szad_iter) @@ -193,44 +243,46 @@ #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_aalloc JEMALLOC_N(huge_aalloc) #define huge_dalloc JEMALLOC_N(huge_dalloc) #define huge_dalloc_junk JEMALLOC_N(huge_dalloc_junk) -#define huge_dss_prec_get JEMALLOC_N(huge_dss_prec_get) #define huge_malloc JEMALLOC_N(huge_malloc) -#define huge_mtx JEMALLOC_N(huge_mtx) -#define huge_ndalloc JEMALLOC_N(huge_ndalloc) -#define huge_nmalloc JEMALLOC_N(huge_nmalloc) #define huge_palloc JEMALLOC_N(huge_palloc) -#define huge_postfork_child JEMALLOC_N(huge_postfork_child) -#define huge_postfork_parent JEMALLOC_N(huge_postfork_parent) -#define huge_prefork JEMALLOC_N(huge_prefork) -#define huge_prof_ctx_get JEMALLOC_N(huge_prof_ctx_get) -#define huge_prof_ctx_set JEMALLOC_N(huge_prof_ctx_set) +#define huge_prof_tctx_get JEMALLOC_N(huge_prof_tctx_get) +#define huge_prof_tctx_set JEMALLOC_N(huge_prof_tctx_set) #define huge_ralloc JEMALLOC_N(huge_ralloc) #define huge_ralloc_no_move JEMALLOC_N(huge_ralloc_no_move) #define huge_salloc JEMALLOC_N(huge_salloc) -#define iallocm JEMALLOC_N(iallocm) +#define iaalloc JEMALLOC_N(iaalloc) +#define iallocztm JEMALLOC_N(iallocztm) #define icalloc JEMALLOC_N(icalloc) #define icalloct JEMALLOC_N(icalloct) #define idalloc JEMALLOC_N(idalloc) #define idalloct JEMALLOC_N(idalloct) +#define idalloctm JEMALLOC_N(idalloctm) #define imalloc JEMALLOC_N(imalloc) #define imalloct JEMALLOC_N(imalloct) +#define index2size JEMALLOC_N(index2size) +#define index2size_compute JEMALLOC_N(index2size_compute) +#define index2size_lookup JEMALLOC_N(index2size_lookup) +#define index2size_tab JEMALLOC_N(index2size_tab) +#define in_valgrind JEMALLOC_N(in_valgrind) #define ipalloc JEMALLOC_N(ipalloc) #define ipalloct JEMALLOC_N(ipalloct) +#define ipallocztm JEMALLOC_N(ipallocztm) #define iqalloc JEMALLOC_N(iqalloc) -#define iqalloct JEMALLOC_N(iqalloct) #define iralloc JEMALLOC_N(iralloc) #define iralloct JEMALLOC_N(iralloct) #define iralloct_realign JEMALLOC_N(iralloct_realign) #define isalloc JEMALLOC_N(isalloc) +#define isdalloct JEMALLOC_N(isdalloct) +#define isqalloc JEMALLOC_N(isqalloc) #define ivsalloc JEMALLOC_N(ivsalloc) #define ixalloc JEMALLOC_N(ixalloc) #define jemalloc_postfork_child JEMALLOC_N(jemalloc_postfork_child) #define jemalloc_postfork_parent JEMALLOC_N(jemalloc_postfork_parent) #define jemalloc_prefork JEMALLOC_N(jemalloc_prefork) +#define lg_floor JEMALLOC_N(lg_floor) #define malloc_cprintf JEMALLOC_N(malloc_cprintf) #define malloc_mutex_init JEMALLOC_N(malloc_mutex_init) #define malloc_mutex_lock JEMALLOC_N(malloc_mutex_lock) @@ -241,7 +293,8 @@ #define malloc_printf JEMALLOC_N(malloc_printf) #define malloc_snprintf JEMALLOC_N(malloc_snprintf) #define malloc_strtoumax JEMALLOC_N(malloc_strtoumax) -#define malloc_tsd_boot JEMALLOC_N(malloc_tsd_boot) +#define malloc_tsd_boot0 JEMALLOC_N(malloc_tsd_boot0) +#define malloc_tsd_boot1 JEMALLOC_N(malloc_tsd_boot1) #define malloc_tsd_cleanup_register JEMALLOC_N(malloc_tsd_cleanup_register) #define malloc_tsd_dalloc JEMALLOC_N(malloc_tsd_dalloc) #define malloc_tsd_malloc JEMALLOC_N(malloc_tsd_malloc) @@ -250,16 +303,18 @@ #define malloc_vsnprintf JEMALLOC_N(malloc_vsnprintf) #define malloc_write JEMALLOC_N(malloc_write) #define map_bias JEMALLOC_N(map_bias) +#define map_misc_offset JEMALLOC_N(map_misc_offset) #define mb_write JEMALLOC_N(mb_write) #define mutex_boot JEMALLOC_N(mutex_boot) -#define narenas_auto JEMALLOC_N(narenas_auto) -#define narenas_total JEMALLOC_N(narenas_total) +#define narenas_cache_cleanup JEMALLOC_N(narenas_cache_cleanup) #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) #define opt_dss JEMALLOC_N(opt_dss) #define opt_junk JEMALLOC_N(opt_junk) +#define opt_junk_alloc JEMALLOC_N(opt_junk_alloc) +#define opt_junk_free JEMALLOC_N(opt_junk_free) #define opt_lg_chunk JEMALLOC_N(opt_lg_chunk) #define opt_lg_dirty_mult JEMALLOC_N(opt_lg_dirty_mult) #define opt_lg_prof_interval JEMALLOC_N(opt_lg_prof_interval) @@ -273,84 +328,98 @@ #define opt_prof_gdump JEMALLOC_N(opt_prof_gdump) #define opt_prof_leak JEMALLOC_N(opt_prof_leak) #define opt_prof_prefix JEMALLOC_N(opt_prof_prefix) +#define opt_prof_thread_active_init JEMALLOC_N(opt_prof_thread_active_init) #define opt_quarantine JEMALLOC_N(opt_quarantine) #define opt_redzone JEMALLOC_N(opt_redzone) #define opt_stats_print JEMALLOC_N(opt_stats_print) #define opt_tcache JEMALLOC_N(opt_tcache) #define opt_utrace JEMALLOC_N(opt_utrace) -#define opt_valgrind JEMALLOC_N(opt_valgrind) #define opt_xmalloc JEMALLOC_N(opt_xmalloc) #define opt_zero JEMALLOC_N(opt_zero) #define p2rz JEMALLOC_N(p2rz) +#define pages_commit JEMALLOC_N(pages_commit) +#define pages_decommit JEMALLOC_N(pages_decommit) +#define pages_map JEMALLOC_N(pages_map) #define pages_purge JEMALLOC_N(pages_purge) +#define pages_trim JEMALLOC_N(pages_trim) +#define pages_unmap JEMALLOC_N(pages_unmap) #define pow2_ceil JEMALLOC_N(pow2_ceil) +#define prof_active_get JEMALLOC_N(prof_active_get) +#define prof_active_get_unlocked JEMALLOC_N(prof_active_get_unlocked) +#define prof_active_set JEMALLOC_N(prof_active_set) +#define prof_alloc_prep JEMALLOC_N(prof_alloc_prep) +#define prof_alloc_rollback JEMALLOC_N(prof_alloc_rollback) #define prof_backtrace JEMALLOC_N(prof_backtrace) #define prof_boot0 JEMALLOC_N(prof_boot0) #define prof_boot1 JEMALLOC_N(prof_boot1) #define prof_boot2 JEMALLOC_N(prof_boot2) -#define prof_bt_count JEMALLOC_N(prof_bt_count) -#define prof_ctx_get JEMALLOC_N(prof_ctx_get) -#define prof_ctx_set JEMALLOC_N(prof_ctx_set) +#define prof_dump_header JEMALLOC_N(prof_dump_header) #define prof_dump_open JEMALLOC_N(prof_dump_open) #define prof_free JEMALLOC_N(prof_free) +#define prof_free_sampled_object JEMALLOC_N(prof_free_sampled_object) #define prof_gdump JEMALLOC_N(prof_gdump) +#define prof_gdump_get JEMALLOC_N(prof_gdump_get) +#define prof_gdump_get_unlocked JEMALLOC_N(prof_gdump_get_unlocked) +#define prof_gdump_set JEMALLOC_N(prof_gdump_set) +#define prof_gdump_val JEMALLOC_N(prof_gdump_val) #define prof_idump JEMALLOC_N(prof_idump) #define prof_interval JEMALLOC_N(prof_interval) #define prof_lookup JEMALLOC_N(prof_lookup) #define prof_malloc JEMALLOC_N(prof_malloc) +#define prof_malloc_sample_object JEMALLOC_N(prof_malloc_sample_object) #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_reset JEMALLOC_N(prof_reset) #define prof_sample_accum_update JEMALLOC_N(prof_sample_accum_update) #define prof_sample_threshold_update JEMALLOC_N(prof_sample_threshold_update) -#define prof_tdata_booted JEMALLOC_N(prof_tdata_booted) +#define prof_tctx_get JEMALLOC_N(prof_tctx_get) +#define prof_tctx_set JEMALLOC_N(prof_tctx_set) #define prof_tdata_cleanup JEMALLOC_N(prof_tdata_cleanup) #define prof_tdata_get JEMALLOC_N(prof_tdata_get) #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_init_head JEMALLOC_N(prof_tdata_tsd_init_head) -#define prof_tdata_tsd_set JEMALLOC_N(prof_tdata_tsd_set) +#define prof_tdata_reinit JEMALLOC_N(prof_tdata_reinit) +#define prof_thread_active_get JEMALLOC_N(prof_thread_active_get) +#define prof_thread_active_init_get JEMALLOC_N(prof_thread_active_init_get) +#define prof_thread_active_init_set JEMALLOC_N(prof_thread_active_init_set) +#define prof_thread_active_set JEMALLOC_N(prof_thread_active_set) +#define prof_thread_name_get JEMALLOC_N(prof_thread_name_get) +#define prof_thread_name_set JEMALLOC_N(prof_thread_name_set) #define quarantine JEMALLOC_N(quarantine) #define quarantine_alloc_hook JEMALLOC_N(quarantine_alloc_hook) -#define quarantine_boot JEMALLOC_N(quarantine_boot) -#define quarantine_booted JEMALLOC_N(quarantine_booted) +#define quarantine_alloc_hook_work JEMALLOC_N(quarantine_alloc_hook_work) #define quarantine_cleanup JEMALLOC_N(quarantine_cleanup) -#define quarantine_init JEMALLOC_N(quarantine_init) -#define quarantine_tls JEMALLOC_N(quarantine_tls) -#define quarantine_tsd JEMALLOC_N(quarantine_tsd) -#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_init_head JEMALLOC_N(quarantine_tsd_init_head) -#define quarantine_tsd_set JEMALLOC_N(quarantine_tsd_set) #define register_zone JEMALLOC_N(register_zone) +#define rtree_child_read JEMALLOC_N(rtree_child_read) +#define rtree_child_read_hard JEMALLOC_N(rtree_child_read_hard) +#define rtree_child_tryread JEMALLOC_N(rtree_child_tryread) #define rtree_delete JEMALLOC_N(rtree_delete) #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_node_valid JEMALLOC_N(rtree_node_valid) #define rtree_set JEMALLOC_N(rtree_set) +#define rtree_start_level JEMALLOC_N(rtree_start_level) +#define rtree_subkey JEMALLOC_N(rtree_subkey) +#define rtree_subtree_read JEMALLOC_N(rtree_subtree_read) +#define rtree_subtree_read_hard JEMALLOC_N(rtree_subtree_read_hard) +#define rtree_subtree_tryread JEMALLOC_N(rtree_subtree_tryread) +#define rtree_val_read JEMALLOC_N(rtree_val_read) +#define rtree_val_write JEMALLOC_N(rtree_val_write) #define s2u JEMALLOC_N(s2u) +#define s2u_compute JEMALLOC_N(s2u_compute) +#define s2u_lookup JEMALLOC_N(s2u_lookup) #define sa2u JEMALLOC_N(sa2u) #define set_errno JEMALLOC_N(set_errno) -#define small_size2bin JEMALLOC_N(small_size2bin) +#define size2index JEMALLOC_N(size2index) +#define size2index_compute JEMALLOC_N(size2index_compute) +#define size2index_lookup JEMALLOC_N(size2index_lookup) +#define size2index_tab JEMALLOC_N(size2index_tab) #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) #define stats_cactive_sub JEMALLOC_N(stats_cactive_sub) -#define stats_chunks JEMALLOC_N(stats_chunks) #define stats_print JEMALLOC_N(stats_print) #define tcache_alloc_easy JEMALLOC_N(tcache_alloc_easy) #define tcache_alloc_large JEMALLOC_N(tcache_alloc_large) @@ -358,55 +427,67 @@ #define tcache_alloc_small_hard JEMALLOC_N(tcache_alloc_small_hard) #define tcache_arena_associate JEMALLOC_N(tcache_arena_associate) #define tcache_arena_dissociate JEMALLOC_N(tcache_arena_dissociate) +#define tcache_arena_reassociate JEMALLOC_N(tcache_arena_reassociate) #define tcache_bin_flush_large JEMALLOC_N(tcache_bin_flush_large) #define tcache_bin_flush_small JEMALLOC_N(tcache_bin_flush_small) #define tcache_bin_info JEMALLOC_N(tcache_bin_info) -#define tcache_boot0 JEMALLOC_N(tcache_boot0) -#define tcache_boot1 JEMALLOC_N(tcache_boot1) -#define tcache_booted JEMALLOC_N(tcache_booted) +#define tcache_boot JEMALLOC_N(tcache_boot) +#define tcache_cleanup JEMALLOC_N(tcache_cleanup) #define tcache_create JEMALLOC_N(tcache_create) #define tcache_dalloc_large JEMALLOC_N(tcache_dalloc_large) #define tcache_dalloc_small JEMALLOC_N(tcache_dalloc_small) -#define tcache_destroy JEMALLOC_N(tcache_destroy) -#define tcache_enabled_booted JEMALLOC_N(tcache_enabled_booted) +#define tcache_enabled_cleanup JEMALLOC_N(tcache_enabled_cleanup) #define tcache_enabled_get JEMALLOC_N(tcache_enabled_get) -#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_init_head JEMALLOC_N(tcache_enabled_tsd_init_head) -#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) #define tcache_flush JEMALLOC_N(tcache_flush) #define tcache_get JEMALLOC_N(tcache_get) -#define tcache_initialized JEMALLOC_N(tcache_initialized) +#define tcache_get_hard JEMALLOC_N(tcache_get_hard) #define tcache_maxclass JEMALLOC_N(tcache_maxclass) +#define tcaches JEMALLOC_N(tcaches) #define tcache_salloc JEMALLOC_N(tcache_salloc) +#define tcaches_create JEMALLOC_N(tcaches_create) +#define tcaches_destroy JEMALLOC_N(tcaches_destroy) +#define tcaches_flush JEMALLOC_N(tcaches_flush) +#define tcaches_get JEMALLOC_N(tcaches_get) #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_init_head JEMALLOC_N(tcache_tsd_init_head) -#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_init_head JEMALLOC_N(thread_allocated_tsd_init_head) -#define thread_allocated_tsd_set JEMALLOC_N(thread_allocated_tsd_set) +#define thread_allocated_cleanup JEMALLOC_N(thread_allocated_cleanup) +#define thread_deallocated_cleanup JEMALLOC_N(thread_deallocated_cleanup) +#define tsd_arena_get JEMALLOC_N(tsd_arena_get) +#define tsd_arena_set JEMALLOC_N(tsd_arena_set) +#define tsd_boot JEMALLOC_N(tsd_boot) +#define tsd_boot0 JEMALLOC_N(tsd_boot0) +#define tsd_boot1 JEMALLOC_N(tsd_boot1) +#define tsd_booted JEMALLOC_N(tsd_booted) +#define tsd_cleanup JEMALLOC_N(tsd_cleanup) +#define tsd_cleanup_wrapper JEMALLOC_N(tsd_cleanup_wrapper) +#define tsd_fetch JEMALLOC_N(tsd_fetch) +#define tsd_get JEMALLOC_N(tsd_get) +#define tsd_wrapper_get JEMALLOC_N(tsd_wrapper_get) +#define tsd_wrapper_set JEMALLOC_N(tsd_wrapper_set) +#define tsd_initialized JEMALLOC_N(tsd_initialized) #define tsd_init_check_recursion JEMALLOC_N(tsd_init_check_recursion) #define tsd_init_finish JEMALLOC_N(tsd_init_finish) +#define tsd_init_head JEMALLOC_N(tsd_init_head) +#define tsd_nominal JEMALLOC_N(tsd_nominal) +#define tsd_quarantine_get JEMALLOC_N(tsd_quarantine_get) +#define tsd_quarantine_set JEMALLOC_N(tsd_quarantine_set) +#define tsd_set JEMALLOC_N(tsd_set) +#define tsd_tcache_enabled_get JEMALLOC_N(tsd_tcache_enabled_get) +#define tsd_tcache_enabled_set JEMALLOC_N(tsd_tcache_enabled_set) +#define tsd_tcache_get JEMALLOC_N(tsd_tcache_get) +#define tsd_tcache_set JEMALLOC_N(tsd_tcache_set) +#define tsd_tls JEMALLOC_N(tsd_tls) +#define tsd_tsd JEMALLOC_N(tsd_tsd) +#define tsd_prof_tdata_get JEMALLOC_N(tsd_prof_tdata_get) +#define tsd_prof_tdata_set JEMALLOC_N(tsd_prof_tdata_set) +#define tsd_thread_allocated_get JEMALLOC_N(tsd_thread_allocated_get) +#define tsd_thread_allocated_set JEMALLOC_N(tsd_thread_allocated_set) +#define tsd_thread_deallocated_get JEMALLOC_N(tsd_thread_deallocated_get) +#define tsd_thread_deallocated_set JEMALLOC_N(tsd_thread_deallocated_set) #define u2rz JEMALLOC_N(u2rz) +#define valgrind_freelike_block JEMALLOC_N(valgrind_freelike_block) +#define valgrind_make_mem_defined JEMALLOC_N(valgrind_make_mem_defined) +#define valgrind_make_mem_noaccess JEMALLOC_N(valgrind_make_mem_noaccess) +#define valgrind_make_mem_undefined JEMALLOC_N(valgrind_make_mem_undefined) diff --git a/contrib/jemalloc/include/jemalloc/internal/prng.h b/contrib/jemalloc/include/jemalloc/internal/prng.h index 7b2b06512ffc..216d0ef47bda 100644 --- a/contrib/jemalloc/include/jemalloc/internal/prng.h +++ b/contrib/jemalloc/include/jemalloc/internal/prng.h @@ -15,7 +15,7 @@ * See Knuth's TAOCP 3rd Ed., Vol. 2, pg. 17 for details on these constraints. * * This choice of m has the disadvantage that the quality of the bits is - * proportional to bit position. For example. the lowest bit has a cycle of 2, + * proportional to bit position. For example, the lowest bit has a cycle of 2, * the next has a cycle of 4, etc. For this reason, we prefer to use the upper * bits. * @@ -26,22 +26,22 @@ * const uint32_t a, c : See above discussion. */ #define prng32(r, lg_range, state, a, c) do { \ - assert(lg_range > 0); \ - assert(lg_range <= 32); \ + assert((lg_range) > 0); \ + assert((lg_range) <= 32); \ \ r = (state * (a)) + (c); \ state = r; \ - r >>= (32 - lg_range); \ + r >>= (32 - (lg_range)); \ } while (false) /* Same as prng32(), but 64 bits of pseudo-randomness, using uint64_t. */ #define prng64(r, lg_range, state, a, c) do { \ - assert(lg_range > 0); \ - assert(lg_range <= 64); \ + assert((lg_range) > 0); \ + assert((lg_range) <= 64); \ \ r = (state * (a)) + (c); \ state = r; \ - r >>= (64 - lg_range); \ + r >>= (64 - (lg_range)); \ } while (false) #endif /* JEMALLOC_H_TYPES */ diff --git a/contrib/jemalloc/include/jemalloc/internal/prof.h b/contrib/jemalloc/include/jemalloc/internal/prof.h index 6f162d21e840..2e2271168d45 100644 --- a/contrib/jemalloc/include/jemalloc/internal/prof.h +++ b/contrib/jemalloc/include/jemalloc/internal/prof.h @@ -3,8 +3,8 @@ typedef struct prof_bt_s prof_bt_t; typedef struct prof_cnt_s prof_cnt_t; -typedef struct prof_thr_cnt_s prof_thr_cnt_t; -typedef struct prof_ctx_s prof_ctx_t; +typedef struct prof_tctx_s prof_tctx_t; +typedef struct prof_gctx_s prof_gctx_t; typedef struct prof_tdata_s prof_tdata_t; /* Option defaults. */ @@ -23,9 +23,6 @@ typedef struct prof_tdata_s prof_tdata_t; */ #define PROF_BT_MAX 128 -/* Maximum number of backtraces to store in each per thread LRU cache. */ -#define PROF_TCMAX 1024 - /* Initial hash table size. */ #define PROF_CKH_MINITEMS 64 @@ -36,11 +33,17 @@ typedef struct prof_tdata_s prof_tdata_t; #define PROF_PRINTF_BUFSIZE 128 /* - * Number of mutexes shared among all ctx's. No space is allocated for these + * Number of mutexes shared among all gctx's. No space is allocated for these * unless profiling is enabled, so it's okay to over-provision. */ #define PROF_NCTX_LOCKS 1024 +/* + * Number of mutexes shared among all tdata's. No space is allocated for these + * unless profiling is enabled, so it's okay to over-provision. + */ +#define PROF_NTDATA_LOCKS 256 + /* * prof_tdata pointers close to NULL are used to encode state information that * is used for cleaning up during thread shutdown. @@ -63,141 +66,185 @@ struct prof_bt_s { /* Data structure passed to libgcc _Unwind_Backtrace() callback functions. */ typedef struct { prof_bt_t *bt; - unsigned nignore; unsigned max; } prof_unwind_data_t; #endif struct prof_cnt_s { - /* - * Profiling counters. An allocation/deallocation pair can operate on - * different prof_thr_cnt_t objects that are linked into the same - * prof_ctx_t cnts_ql, so it is possible for the cur* counters to go - * negative. In principle it is possible for the *bytes counters to - * overflow/underflow, but a general solution would require something - * like 128-bit counters; this implementation doesn't bother to solve - * that problem. - */ - int64_t curobjs; - int64_t curbytes; + /* Profiling counters. */ + uint64_t curobjs; + uint64_t curbytes; uint64_t accumobjs; uint64_t accumbytes; }; -struct prof_thr_cnt_s { - /* Linkage into prof_ctx_t's cnts_ql. */ - ql_elm(prof_thr_cnt_t) cnts_link; +typedef enum { + prof_tctx_state_initializing, + prof_tctx_state_nominal, + prof_tctx_state_dumping, + prof_tctx_state_purgatory /* Dumper must finish destroying. */ +} prof_tctx_state_t; - /* Linkage into thread's LRU. */ - ql_elm(prof_thr_cnt_t) lru_link; +struct prof_tctx_s { + /* Thread data for thread that performed the allocation. */ + prof_tdata_t *tdata; /* - * Associated context. If a thread frees an object that it did not - * allocate, it is possible that the context is not cached in the - * thread's hash table, in which case it must be able to look up the - * context, insert a new prof_thr_cnt_t into the thread's hash table, - * and link it into the prof_ctx_t's cnts_ql. + * Copy of tdata->thr_uid, necessary because tdata may be defunct during + * teardown. */ - prof_ctx_t *ctx; + uint64_t thr_uid; - /* - * Threads use memory barriers to update the counters. Since there is - * only ever one writer, the only challenge is for the reader to get a - * consistent read of the counters. - * - * The writer uses this series of operations: - * - * 1) Increment epoch to an odd number. - * 2) Update counters. - * 3) Increment epoch to an even number. - * - * The reader must assure 1) that the epoch is even while it reads the - * counters, and 2) that the epoch doesn't change between the time it - * starts and finishes reading the counters. - */ - unsigned epoch; - - /* Profiling counters. */ + /* Profiling counters, protected by tdata->lock. */ prof_cnt_t cnts; + + /* Associated global context. */ + prof_gctx_t *gctx; + + /* + * UID that distinguishes multiple tctx's created by the same thread, + * but coexisting in gctx->tctxs. There are two ways that such + * coexistence can occur: + * - A dumper thread can cause a tctx to be retained in the purgatory + * state. + * - Although a single "producer" thread must create all tctx's which + * share the same thr_uid, multiple "consumers" can each concurrently + * execute portions of prof_tctx_destroy(). prof_tctx_destroy() only + * gets called once each time cnts.cur{objs,bytes} drop to 0, but this + * threshold can be hit again before the first consumer finishes + * executing prof_tctx_destroy(). + */ + uint64_t tctx_uid; + + /* Linkage into gctx's tctxs. */ + rb_node(prof_tctx_t) tctx_link; + + /* + * True during prof_alloc_prep()..prof_malloc_sample_object(), prevents + * sample vs destroy race. + */ + bool prepared; + + /* Current dump-related state, protected by gctx->lock. */ + prof_tctx_state_t state; + + /* + * Copy of cnts snapshotted during early dump phase, protected by + * dump_mtx. + */ + prof_cnt_t dump_cnts; }; +typedef rb_tree(prof_tctx_t) prof_tctx_tree_t; -struct prof_ctx_s { - /* Associated backtrace. */ - prof_bt_t *bt; - - /* Protects nlimbo, cnt_merged, and cnts_ql. */ +struct prof_gctx_s { + /* Protects nlimbo, cnt_summed, and tctxs. */ malloc_mutex_t *lock; /* - * Number of threads that currently cause this ctx to be in a state of + * Number of threads that currently cause this gctx to be in a state of * limbo due to one of: - * - Initializing per thread counters associated with this ctx. - * - Preparing to destroy this ctx. - * - Dumping a heap profile that includes this ctx. + * - Initializing this gctx. + * - Initializing per thread counters associated with this gctx. + * - Preparing to destroy this gctx. + * - Dumping a heap profile that includes this gctx. * nlimbo must be 1 (single destroyer) in order to safely destroy the - * ctx. + * gctx. */ unsigned nlimbo; + /* + * Tree of profile counters, one for each thread that has allocated in + * this context. + */ + prof_tctx_tree_t tctxs; + + /* Linkage for tree of contexts to be dumped. */ + rb_node(prof_gctx_t) dump_link; + /* Temporary storage for summation during dump. */ prof_cnt_t cnt_summed; - /* When threads exit, they merge their stats into cnt_merged. */ - prof_cnt_t cnt_merged; + /* Associated backtrace. */ + prof_bt_t bt; - /* - * List of profile counters, one for each thread that has allocated in - * this context. - */ - ql_head(prof_thr_cnt_t) cnts_ql; - - /* Linkage for list of contexts to be dumped. */ - ql_elm(prof_ctx_t) dump_link; + /* Backtrace vector, variable size, referred to by bt. */ + void *vec[1]; }; -typedef ql_head(prof_ctx_t) prof_ctx_list_t; +typedef rb_tree(prof_gctx_t) prof_gctx_tree_t; struct prof_tdata_s { + malloc_mutex_t *lock; + + /* Monotonically increasing unique thread identifier. */ + uint64_t thr_uid; + /* - * Hash of (prof_bt_t *)-->(prof_thr_cnt_t *). Each thread keeps a - * cache of backtraces, with associated thread-specific prof_thr_cnt_t - * objects. Other threads may read the prof_thr_cnt_t contents, but no - * others will ever write them. - * - * Upon thread exit, the thread must merge all the prof_thr_cnt_t - * counter data into the associated prof_ctx_t objects, and unlink/free - * the prof_thr_cnt_t objects. + * Monotonically increasing discriminator among tdata structures + * associated with the same thr_uid. */ - ckh_t bt2cnt; + uint64_t thr_discrim; - /* LRU for contents of bt2cnt. */ - ql_head(prof_thr_cnt_t) lru_ql; + /* Included in heap profile dumps if non-NULL. */ + char *thread_name; - /* Backtrace vector, used for calls to prof_backtrace(). */ - void **vec; + bool attached; + bool expired; + + rb_node(prof_tdata_t) tdata_link; + + /* + * Counter used to initialize prof_tctx_t's tctx_uid. No locking is + * necessary when incrementing this field, because only one thread ever + * does so. + */ + uint64_t tctx_uid_next; + + /* + * Hash of (prof_bt_t *)-->(prof_tctx_t *). Each thread tracks + * backtraces for which it has non-zero allocation/deallocation counters + * associated with thread-specific prof_tctx_t objects. Other threads + * may write to prof_tctx_t contents when freeing associated objects. + */ + ckh_t bt2tctx; /* Sampling state. */ uint64_t prng_state; - uint64_t threshold; - uint64_t accum; + uint64_t bytes_until_sample; /* State used to avoid dumping while operating on prof internals. */ bool enq; bool enq_idump; bool enq_gdump; + + /* + * Set to true during an early dump phase for tdata's which are + * currently being dumped. New threads' tdata's have this initialized + * to false so that they aren't accidentally included in later dump + * phases. + */ + bool dumping; + + /* + * True if profiling is active for this tdata's thread + * (thread.prof.active mallctl). + */ + bool active; + + /* Temporary storage for summation during dump. */ + prof_cnt_t cnt_summed; + + /* Backtrace vector, used for calls to prof_backtrace(). */ + void *vec[PROF_BT_MAX]; }; +typedef rb_tree(prof_tdata_t) prof_tdata_tree_t; #endif /* JEMALLOC_H_STRUCTS */ /******************************************************************************/ #ifdef JEMALLOC_H_EXTERNS extern bool opt_prof; -/* - * Even if opt_prof is true, sampling can be temporarily disabled by setting - * opt_prof_active to false. No locking is used when updating opt_prof_active, - * so there are no guarantees regarding how long it will take for all threads - * to notice state changes. - */ extern bool opt_prof_active; +extern bool opt_prof_thread_active_init; extern size_t opt_lg_prof_sample; /* Mean bytes between samples. */ extern ssize_t opt_lg_prof_interval; /* lg(prof_interval). */ extern bool opt_prof_gdump; /* High-water memory dumping. */ @@ -211,6 +258,12 @@ extern char opt_prof_prefix[ #endif 1]; +/* Accessed via prof_active_[gs]et{_unlocked,}(). */ +extern bool prof_active; + +/* Accessed via prof_gdump_[gs]et{_unlocked,}(). */ +extern bool prof_gdump_val; + /* * Profile dump interval, measured in bytes allocated. Each arena triggers a * profile dump when it reaches this threshold. The effect is that the @@ -221,391 +274,248 @@ extern char opt_prof_prefix[ extern uint64_t prof_interval; /* - * If true, promote small sampled objects to large objects, since small run - * headers do not have embedded profile context pointers. + * Initialized as opt_lg_prof_sample, and potentially modified during profiling + * resets. */ -extern bool prof_promote; +extern size_t lg_prof_sample; +void prof_alloc_rollback(tsd_t *tsd, prof_tctx_t *tctx, bool updated); +void prof_malloc_sample_object(const void *ptr, size_t usize, + prof_tctx_t *tctx); +void prof_free_sampled_object(tsd_t *tsd, size_t usize, prof_tctx_t *tctx); void bt_init(prof_bt_t *bt, void **vec); -void prof_backtrace(prof_bt_t *bt, unsigned nignore); -prof_thr_cnt_t *prof_lookup(prof_bt_t *bt); +void prof_backtrace(prof_bt_t *bt); +prof_tctx_t *prof_lookup(tsd_t *tsd, prof_bt_t *bt); #ifdef JEMALLOC_JET +size_t prof_tdata_count(void); size_t prof_bt_count(void); +const prof_cnt_t *prof_cnt_all(void); typedef int (prof_dump_open_t)(bool, const char *); extern prof_dump_open_t *prof_dump_open; +typedef bool (prof_dump_header_t)(bool, const prof_cnt_t *); +extern prof_dump_header_t *prof_dump_header; #endif void prof_idump(void); bool prof_mdump(const char *filename); void prof_gdump(void); -prof_tdata_t *prof_tdata_init(void); -void prof_tdata_cleanup(void *arg); +prof_tdata_t *prof_tdata_init(tsd_t *tsd); +prof_tdata_t *prof_tdata_reinit(tsd_t *tsd, prof_tdata_t *tdata); +void prof_reset(tsd_t *tsd, size_t lg_sample); +void prof_tdata_cleanup(tsd_t *tsd); +const char *prof_thread_name_get(void); +bool prof_active_get(void); +bool prof_active_set(bool active); +int prof_thread_name_set(tsd_t *tsd, const char *thread_name); +bool prof_thread_active_get(void); +bool prof_thread_active_set(bool active); +bool prof_thread_active_init_get(void); +bool prof_thread_active_init_set(bool active_init); +bool prof_gdump_get(void); +bool prof_gdump_set(bool active); 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); +void prof_sample_threshold_update(prof_tdata_t *tdata); #endif /* JEMALLOC_H_EXTERNS */ /******************************************************************************/ #ifdef JEMALLOC_H_INLINES -#define PROF_ALLOC_PREP(nignore, size, ret) do { \ - prof_tdata_t *prof_tdata; \ - prof_bt_t bt; \ - \ - assert(size == s2u(size)); \ - \ - prof_tdata = prof_tdata_get(true); \ - if ((uintptr_t)prof_tdata <= (uintptr_t)PROF_TDATA_STATE_MAX) { \ - if (prof_tdata != NULL) \ - ret = (prof_thr_cnt_t *)(uintptr_t)1U; \ - else \ - ret = NULL; \ - break; \ - } \ - \ - if (opt_prof_active == false) { \ - /* Sampling is currently inactive, so avoid sampling. */\ - ret = (prof_thr_cnt_t *)(uintptr_t)1U; \ - } else if (opt_lg_prof_sample == 0) { \ - /* Don't bother with sampling logic, since sampling */\ - /* interval is 1. */\ - bt_init(&bt, prof_tdata->vec); \ - prof_backtrace(&bt, nignore); \ - ret = prof_lookup(&bt); \ - } else { \ - if (prof_tdata->threshold == 0) { \ - /* Initialize. Seed the prng differently for */\ - /* each thread. */\ - prof_tdata->prng_state = \ - (uint64_t)(uintptr_t)&size; \ - prof_sample_threshold_update(prof_tdata); \ - } \ - \ - /* Determine whether to capture a backtrace based on */\ - /* whether size is enough for prof_accum to reach */\ - /* prof_tdata->threshold. However, delay updating */\ - /* these variables until prof_{m,re}alloc(), because */\ - /* we don't know for sure that the allocation will */\ - /* succeed. */\ - /* */\ - /* Use subtraction rather than addition to avoid */\ - /* potential integer overflow. */\ - if (size >= prof_tdata->threshold - \ - prof_tdata->accum) { \ - bt_init(&bt, prof_tdata->vec); \ - prof_backtrace(&bt, nignore); \ - ret = prof_lookup(&bt); \ - } else \ - ret = (prof_thr_cnt_t *)(uintptr_t)1U; \ - } \ -} while (0) - #ifndef JEMALLOC_ENABLE_INLINE -malloc_tsd_protos(JEMALLOC_ATTR(unused), prof_tdata, prof_tdata_t *) - -prof_tdata_t *prof_tdata_get(bool create); -void prof_sample_threshold_update(prof_tdata_t *prof_tdata); -prof_ctx_t *prof_ctx_get(const void *ptr); -void prof_ctx_set(const void *ptr, size_t usize, prof_ctx_t *ctx); -bool prof_sample_accum_update(size_t size); -void prof_malloc(const void *ptr, size_t usize, prof_thr_cnt_t *cnt); -void prof_realloc(const void *ptr, size_t usize, prof_thr_cnt_t *cnt, - size_t old_usize, prof_ctx_t *old_ctx); -void prof_free(const void *ptr, size_t size); +bool prof_active_get_unlocked(void); +bool prof_gdump_get_unlocked(void); +prof_tdata_t *prof_tdata_get(tsd_t *tsd, bool create); +bool prof_sample_accum_update(tsd_t *tsd, size_t usize, bool commit, + prof_tdata_t **tdata_out); +prof_tctx_t *prof_alloc_prep(tsd_t *tsd, size_t usize, bool update); +prof_tctx_t *prof_tctx_get(const void *ptr); +void prof_tctx_set(const void *ptr, prof_tctx_t *tctx); +void prof_malloc_sample_object(const void *ptr, size_t usize, + prof_tctx_t *tctx); +void prof_malloc(const void *ptr, size_t usize, prof_tctx_t *tctx); +void prof_realloc(tsd_t *tsd, const void *ptr, size_t usize, + prof_tctx_t *tctx, bool updated, size_t old_usize, prof_tctx_t *old_tctx); +void prof_free(tsd_t *tsd, const void *ptr, size_t usize); #endif #if (defined(JEMALLOC_ENABLE_INLINE) || defined(JEMALLOC_PROF_C_)) -/* Thread-specific backtrace cache, used to reduce bt2ctx contention. */ -malloc_tsd_externs(prof_tdata, prof_tdata_t *) -malloc_tsd_funcs(JEMALLOC_INLINE, prof_tdata, prof_tdata_t *, NULL, - prof_tdata_cleanup) - -JEMALLOC_INLINE prof_tdata_t * -prof_tdata_get(bool create) +JEMALLOC_ALWAYS_INLINE bool +prof_active_get_unlocked(void) { - prof_tdata_t *prof_tdata; + + /* + * Even if opt_prof is true, sampling can be temporarily disabled by + * setting prof_active to false. No locking is used when reading + * prof_active in the fast path, so there are no guarantees regarding + * how long it will take for all threads to notice state changes. + */ + return (prof_active); +} + +JEMALLOC_ALWAYS_INLINE bool +prof_gdump_get_unlocked(void) +{ + + /* + * No locking is used when reading prof_gdump_val in the fast path, so + * there are no guarantees regarding how long it will take for all + * threads to notice state changes. + */ + return (prof_gdump_val); +} + +JEMALLOC_ALWAYS_INLINE prof_tdata_t * +prof_tdata_get(tsd_t *tsd, bool create) +{ + prof_tdata_t *tdata; cassert(config_prof); - prof_tdata = *prof_tdata_tsd_get(); - if (create && prof_tdata == NULL) - prof_tdata = prof_tdata_init(); + tdata = tsd_prof_tdata_get(tsd); + if (create) { + if (unlikely(tdata == NULL)) { + if (tsd_nominal(tsd)) { + tdata = prof_tdata_init(tsd); + tsd_prof_tdata_set(tsd, tdata); + } + } else if (unlikely(tdata->expired)) { + tdata = prof_tdata_reinit(tsd, tdata); + tsd_prof_tdata_set(tsd, tdata); + } + assert(tdata == NULL || tdata->attached); + } - return (prof_tdata); + return (tdata); } -JEMALLOC_INLINE void -prof_sample_threshold_update(prof_tdata_t *prof_tdata) +JEMALLOC_ALWAYS_INLINE prof_tctx_t * +prof_tctx_get(const void *ptr) { - /* - * The body of this function is compiled out unless heap profiling is - * enabled, so that it is possible to compile jemalloc with floating - * point support completely disabled. Avoiding floating point code is - * important on memory-constrained systems, but it also enables a - * workaround for versions of glibc that don't properly save/restore - * floating point registers during dynamic lazy symbol loading (which - * internally calls into whatever malloc implementation happens to be - * integrated into the application). Note that some compilers (e.g. - * gcc 4.8) may use floating point registers for fast memory moves, so - * jemalloc must be compiled with such optimizations disabled (e.g. - * -mno-sse) in order for the workaround to be complete. - */ -#ifdef JEMALLOC_PROF - uint64_t r; - double u; - - cassert(config_prof); - - /* - * Compute sample threshold as a geometrically distributed random - * variable with mean (2^opt_lg_prof_sample). - * - * __ __ - * | log(u) | 1 - * prof_tdata->threshold = | -------- |, where p = ------------------- - * | log(1-p) | opt_lg_prof_sample - * 2 - * - * For more information on the math, see: - * - * Non-Uniform Random Variate Generation - * Luc Devroye - * Springer-Verlag, New York, 1986 - * pp 500 - * (http://luc.devroye.org/rnbookindex.html) - */ - prng64(r, 53, prof_tdata->prng_state, - UINT64_C(6364136223846793005), UINT64_C(1442695040888963407)); - u = (double)r * (1.0/9007199254740992.0L); - prof_tdata->threshold = (uint64_t)(log(u) / - log(1.0 - (1.0 / (double)((uint64_t)1U << opt_lg_prof_sample)))) - + (uint64_t)1U; -#endif -} - -JEMALLOC_INLINE prof_ctx_t * -prof_ctx_get(const void *ptr) -{ - prof_ctx_t *ret; - arena_chunk_t *chunk; cassert(config_prof); assert(ptr != NULL); - chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr); - if (chunk != ptr) { - /* Region. */ - ret = arena_prof_ctx_get(ptr); - } else - ret = huge_prof_ctx_get(ptr); + return (arena_prof_tctx_get(ptr)); +} + +JEMALLOC_ALWAYS_INLINE void +prof_tctx_set(const void *ptr, prof_tctx_t *tctx) +{ + + cassert(config_prof); + assert(ptr != NULL); + + arena_prof_tctx_set(ptr, tctx); +} + +JEMALLOC_ALWAYS_INLINE bool +prof_sample_accum_update(tsd_t *tsd, size_t usize, bool update, + prof_tdata_t **tdata_out) +{ + prof_tdata_t *tdata; + + cassert(config_prof); + + tdata = prof_tdata_get(tsd, true); + if ((uintptr_t)tdata <= (uintptr_t)PROF_TDATA_STATE_MAX) + tdata = NULL; + + if (tdata_out != NULL) + *tdata_out = tdata; + + if (tdata == NULL) + return (true); + + if (tdata->bytes_until_sample >= usize) { + if (update) + tdata->bytes_until_sample -= usize; + return (true); + } else { + /* Compute new sample threshold. */ + if (update) + prof_sample_threshold_update(tdata); + return (!tdata->active); + } +} + +JEMALLOC_ALWAYS_INLINE prof_tctx_t * +prof_alloc_prep(tsd_t *tsd, size_t usize, bool update) +{ + prof_tctx_t *ret; + prof_tdata_t *tdata; + prof_bt_t bt; + + assert(usize == s2u(usize)); + + if (!prof_active_get_unlocked() || likely(prof_sample_accum_update(tsd, + usize, update, &tdata))) + ret = (prof_tctx_t *)(uintptr_t)1U; + else { + bt_init(&bt, tdata->vec); + prof_backtrace(&bt); + ret = prof_lookup(tsd, &bt); + } return (ret); } -JEMALLOC_INLINE void -prof_ctx_set(const void *ptr, size_t usize, prof_ctx_t *ctx) -{ - arena_chunk_t *chunk; - - cassert(config_prof); - assert(ptr != NULL); - - chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr); - if (chunk != ptr) { - /* Region. */ - arena_prof_ctx_set(ptr, usize, ctx); - } else - huge_prof_ctx_set(ptr, ctx); -} - -JEMALLOC_INLINE bool -prof_sample_accum_update(size_t size) -{ - prof_tdata_t *prof_tdata; - - cassert(config_prof); - /* Sampling logic is unnecessary if the interval is 1. */ - assert(opt_lg_prof_sample != 0); - - prof_tdata = prof_tdata_get(false); - if ((uintptr_t)prof_tdata <= (uintptr_t)PROF_TDATA_STATE_MAX) - return (true); - - /* Take care to avoid integer overflow. */ - if (size >= prof_tdata->threshold - prof_tdata->accum) { - prof_tdata->accum -= (prof_tdata->threshold - size); - /* Compute new sample threshold. */ - prof_sample_threshold_update(prof_tdata); - while (prof_tdata->accum >= prof_tdata->threshold) { - prof_tdata->accum -= prof_tdata->threshold; - prof_sample_threshold_update(prof_tdata); - } - return (false); - } else { - prof_tdata->accum += size; - return (true); - } -} - -JEMALLOC_INLINE void -prof_malloc(const void *ptr, size_t usize, prof_thr_cnt_t *cnt) +JEMALLOC_ALWAYS_INLINE void +prof_malloc(const void *ptr, size_t usize, prof_tctx_t *tctx) { cassert(config_prof); assert(ptr != NULL); assert(usize == isalloc(ptr, true)); - if (opt_lg_prof_sample != 0) { - if (prof_sample_accum_update(usize)) { - /* - * Don't sample. For malloc()-like allocation, it is - * always possible to tell in advance how large an - * object's usable size will be, so there should never - * be a difference between the usize passed to - * PROF_ALLOC_PREP() and prof_malloc(). - */ - assert((uintptr_t)cnt == (uintptr_t)1U); - } - } - - if ((uintptr_t)cnt > (uintptr_t)1U) { - prof_ctx_set(ptr, usize, cnt->ctx); - - cnt->epoch++; - /*********/ - mb_write(); - /*********/ - cnt->cnts.curobjs++; - cnt->cnts.curbytes += usize; - if (opt_prof_accum) { - cnt->cnts.accumobjs++; - cnt->cnts.accumbytes += usize; - } - /*********/ - mb_write(); - /*********/ - cnt->epoch++; - /*********/ - mb_write(); - /*********/ - } else - prof_ctx_set(ptr, usize, (prof_ctx_t *)(uintptr_t)1U); + if (unlikely((uintptr_t)tctx > (uintptr_t)1U)) + prof_malloc_sample_object(ptr, usize, tctx); + else + prof_tctx_set(ptr, (prof_tctx_t *)(uintptr_t)1U); } -JEMALLOC_INLINE void -prof_realloc(const void *ptr, size_t usize, prof_thr_cnt_t *cnt, - size_t old_usize, prof_ctx_t *old_ctx) +JEMALLOC_ALWAYS_INLINE void +prof_realloc(tsd_t *tsd, const void *ptr, size_t usize, prof_tctx_t *tctx, + bool updated, size_t old_usize, prof_tctx_t *old_tctx) { - prof_thr_cnt_t *told_cnt; cassert(config_prof); - assert(ptr != NULL || (uintptr_t)cnt <= (uintptr_t)1U); + assert(ptr != NULL || (uintptr_t)tctx <= (uintptr_t)1U); - if (ptr != NULL) { + if (!updated && ptr != NULL) { assert(usize == isalloc(ptr, true)); - if (opt_lg_prof_sample != 0) { - if (prof_sample_accum_update(usize)) { - /* - * Don't sample. The usize passed to - * PROF_ALLOC_PREP() was larger than what - * actually got allocated, so a backtrace was - * captured for this allocation, even though - * its actual usize was insufficient to cross - * the sample threshold. - */ - cnt = (prof_thr_cnt_t *)(uintptr_t)1U; - } - } - } - - if ((uintptr_t)old_ctx > (uintptr_t)1U) { - told_cnt = prof_lookup(old_ctx->bt); - if (told_cnt == NULL) { + if (prof_sample_accum_update(tsd, usize, true, NULL)) { /* - * It's too late to propagate OOM for this realloc(), - * so operate directly on old_cnt->ctx->cnt_merged. + * Don't sample. The usize passed to PROF_ALLOC_PREP() + * was larger than what actually got allocated, so a + * backtrace was captured for this allocation, even + * though its actual usize was insufficient to cross the + * sample threshold. */ - malloc_mutex_lock(old_ctx->lock); - old_ctx->cnt_merged.curobjs--; - old_ctx->cnt_merged.curbytes -= old_usize; - malloc_mutex_unlock(old_ctx->lock); - told_cnt = (prof_thr_cnt_t *)(uintptr_t)1U; + tctx = (prof_tctx_t *)(uintptr_t)1U; } - } else - told_cnt = (prof_thr_cnt_t *)(uintptr_t)1U; + } - if ((uintptr_t)told_cnt > (uintptr_t)1U) - told_cnt->epoch++; - if ((uintptr_t)cnt > (uintptr_t)1U) { - prof_ctx_set(ptr, usize, cnt->ctx); - cnt->epoch++; - } else if (ptr != NULL) - prof_ctx_set(ptr, usize, (prof_ctx_t *)(uintptr_t)1U); - /*********/ - mb_write(); - /*********/ - if ((uintptr_t)told_cnt > (uintptr_t)1U) { - told_cnt->cnts.curobjs--; - told_cnt->cnts.curbytes -= old_usize; - } - if ((uintptr_t)cnt > (uintptr_t)1U) { - cnt->cnts.curobjs++; - cnt->cnts.curbytes += usize; - if (opt_prof_accum) { - cnt->cnts.accumobjs++; - cnt->cnts.accumbytes += usize; - } - } - /*********/ - mb_write(); - /*********/ - if ((uintptr_t)told_cnt > (uintptr_t)1U) - told_cnt->epoch++; - if ((uintptr_t)cnt > (uintptr_t)1U) - cnt->epoch++; - /*********/ - mb_write(); /* Not strictly necessary. */ + if (unlikely((uintptr_t)old_tctx > (uintptr_t)1U)) + prof_free_sampled_object(tsd, old_usize, old_tctx); + if (unlikely((uintptr_t)tctx > (uintptr_t)1U)) + prof_malloc_sample_object(ptr, usize, tctx); + else + prof_tctx_set(ptr, (prof_tctx_t *)(uintptr_t)1U); } -JEMALLOC_INLINE void -prof_free(const void *ptr, size_t size) +JEMALLOC_ALWAYS_INLINE void +prof_free(tsd_t *tsd, const void *ptr, size_t usize) { - prof_ctx_t *ctx = prof_ctx_get(ptr); + prof_tctx_t *tctx = prof_tctx_get(ptr); cassert(config_prof); + assert(usize == isalloc(ptr, true)); - if ((uintptr_t)ctx > (uintptr_t)1) { - prof_thr_cnt_t *tcnt; - assert(size == isalloc(ptr, true)); - tcnt = prof_lookup(ctx->bt); - - if (tcnt != NULL) { - tcnt->epoch++; - /*********/ - mb_write(); - /*********/ - tcnt->cnts.curobjs--; - tcnt->cnts.curbytes -= size; - /*********/ - mb_write(); - /*********/ - tcnt->epoch++; - /*********/ - mb_write(); - /*********/ - } else { - /* - * OOM during free() cannot be propagated, so operate - * directly on cnt->ctx->cnt_merged. - */ - malloc_mutex_lock(ctx->lock); - ctx->cnt_merged.curobjs--; - ctx->cnt_merged.curbytes -= size; - malloc_mutex_unlock(ctx->lock); - } - } + if (unlikely((uintptr_t)tctx > (uintptr_t)1U)) + prof_free_sampled_object(tsd, usize, tctx); } #endif diff --git a/contrib/jemalloc/include/jemalloc/internal/public_namespace.h b/contrib/jemalloc/include/jemalloc/internal/public_namespace.h index 32c72b60e43b..d5a4de800d20 100644 --- a/contrib/jemalloc/include/jemalloc/internal/public_namespace.h +++ b/contrib/jemalloc/include/jemalloc/internal/public_namespace.h @@ -11,6 +11,7 @@ #define je_xallocx JEMALLOC_N(xallocx) #define je_sallocx JEMALLOC_N(sallocx) #define je_dallocx JEMALLOC_N(dallocx) +#define je_sdallocx JEMALLOC_N(sdallocx) #define je_nallocx JEMALLOC_N(nallocx) #define je_mallctl JEMALLOC_N(mallctl) #define je_mallctlnametomib JEMALLOC_N(mallctlnametomib) @@ -18,8 +19,3 @@ #define je_malloc_stats_print JEMALLOC_N(malloc_stats_print) #define je_malloc_usable_size JEMALLOC_N(malloc_usable_size) #define je_valloc JEMALLOC_N(valloc) -#define je_allocm JEMALLOC_N(allocm) -#define je_dallocm JEMALLOC_N(dallocm) -#define je_nallocm JEMALLOC_N(nallocm) -#define je_rallocm JEMALLOC_N(rallocm) -#define je_sallocm JEMALLOC_N(sallocm) diff --git a/contrib/jemalloc/include/jemalloc/internal/ql.h b/contrib/jemalloc/include/jemalloc/internal/ql.h index f70c5f6f3919..1834bb8557ac 100644 --- a/contrib/jemalloc/include/jemalloc/internal/ql.h +++ b/contrib/jemalloc/include/jemalloc/internal/ql.h @@ -1,6 +1,4 @@ -/* - * List definitions. - */ +/* List definitions. */ #define ql_head(a_type) \ struct { \ a_type *qlh_first; \ diff --git a/contrib/jemalloc/include/jemalloc/internal/qr.h b/contrib/jemalloc/include/jemalloc/internal/qr.h index 602944b9b4fa..0fbaec25e7c0 100644 --- a/contrib/jemalloc/include/jemalloc/internal/qr.h +++ b/contrib/jemalloc/include/jemalloc/internal/qr.h @@ -40,8 +40,10 @@ struct { \ (a_qr_b)->a_field.qre_prev = t; \ } while (0) -/* qr_meld() and qr_split() are functionally equivalent, so there's no need to - * have two copies of the code. */ +/* + * qr_meld() and qr_split() are functionally equivalent, so there's no need to + * have two copies of the code. + */ #define qr_split(a_qr_a, a_qr_b, a_field) \ qr_meld((a_qr_a), (a_qr_b), a_field) diff --git a/contrib/jemalloc/include/jemalloc/internal/quarantine.h b/contrib/jemalloc/include/jemalloc/internal/quarantine.h index 16f677f73da0..ae607399f6d7 100644 --- a/contrib/jemalloc/include/jemalloc/internal/quarantine.h +++ b/contrib/jemalloc/include/jemalloc/internal/quarantine.h @@ -29,36 +29,29 @@ struct quarantine_s { /******************************************************************************/ #ifdef JEMALLOC_H_EXTERNS -quarantine_t *quarantine_init(size_t lg_maxobjs); -void quarantine(void *ptr); -void quarantine_cleanup(void *arg); -bool quarantine_boot(void); +void quarantine_alloc_hook_work(tsd_t *tsd); +void quarantine(tsd_t *tsd, void *ptr); +void quarantine_cleanup(tsd_t *tsd); #endif /* JEMALLOC_H_EXTERNS */ /******************************************************************************/ #ifdef JEMALLOC_H_INLINES #ifndef JEMALLOC_ENABLE_INLINE -malloc_tsd_protos(JEMALLOC_ATTR(unused), quarantine, quarantine_t *) - void quarantine_alloc_hook(void); #endif #if (defined(JEMALLOC_ENABLE_INLINE) || defined(JEMALLOC_QUARANTINE_C_)) -malloc_tsd_externs(quarantine, quarantine_t *) -malloc_tsd_funcs(JEMALLOC_ALWAYS_INLINE, quarantine, quarantine_t *, NULL, - quarantine_cleanup) - JEMALLOC_ALWAYS_INLINE void quarantine_alloc_hook(void) { - quarantine_t *quarantine; + tsd_t *tsd; assert(config_fill && opt_quarantine); - quarantine = *quarantine_tsd_get(); - if (quarantine == NULL) - quarantine_init(LG_MAXOBJS_INIT); + tsd = tsd_fetch(); + if (tsd_quarantine_get(tsd) == NULL) + quarantine_alloc_hook_work(tsd); } #endif diff --git a/contrib/jemalloc/include/jemalloc/internal/rb.h b/contrib/jemalloc/include/jemalloc/internal/rb.h index 423802eb2dce..2ca8e5933b28 100644 --- a/contrib/jemalloc/include/jemalloc/internal/rb.h +++ b/contrib/jemalloc/include/jemalloc/internal/rb.h @@ -158,6 +158,8 @@ struct { \ #define rb_proto(a_attr, a_prefix, a_rbt_type, a_type) \ a_attr void \ a_prefix##new(a_rbt_type *rbtree); \ +a_attr bool \ +a_prefix##empty(a_rbt_type *rbtree); \ a_attr a_type * \ a_prefix##first(a_rbt_type *rbtree); \ a_attr a_type * \ @@ -198,7 +200,7 @@ a_prefix##reverse_iter(a_rbt_type *rbtree, a_type *start, \ * int (a_cmp *)(a_type *a_node, a_type *a_other); * ^^^^^^ * or a_key - * Interpretation of comparision function return values: + * Interpretation of comparison function return values: * -1 : a_node < a_other * 0 : a_node == a_other * 1 : a_node > a_other @@ -224,6 +226,13 @@ a_prefix##reverse_iter(a_rbt_type *rbtree, a_type *start, \ * Args: * tree: Pointer to an uninitialized red-black tree object. * + * static bool + * ex_empty(ex_t *tree); + * Description: Determine whether tree is empty. + * Args: + * tree: Pointer to an initialized red-black tree object. + * Ret: True if tree is empty, false otherwise. + * * static ex_node_t * * ex_first(ex_t *tree); * static ex_node_t * @@ -309,6 +318,10 @@ a_attr void \ a_prefix##new(a_rbt_type *rbtree) { \ rb_new(a_type, a_field, rbtree); \ } \ +a_attr bool \ +a_prefix##empty(a_rbt_type *rbtree) { \ + return (rbtree->rbt_root == &rbtree->rbt_nil); \ +} \ a_attr a_type * \ a_prefix##first(a_rbt_type *rbtree) { \ a_type *ret; \ @@ -580,7 +593,7 @@ a_prefix##remove(a_rbt_type *rbtree, a_type *node) { \ if (left != &rbtree->rbt_nil) { \ /* node has no successor, but it has a left child. */\ /* Splice node out, without losing the left child. */\ - assert(rbtn_red_get(a_type, a_field, node) == false); \ + assert(!rbtn_red_get(a_type, a_field, node)); \ assert(rbtn_red_get(a_type, a_field, left)); \ rbtn_black_set(a_type, a_field, left); \ if (pathp == path) { \ @@ -616,8 +629,7 @@ a_prefix##remove(a_rbt_type *rbtree, a_type *node) { \ if (pathp->cmp < 0) { \ rbtn_left_set(a_type, a_field, pathp->node, \ pathp[1].node); \ - assert(rbtn_red_get(a_type, a_field, pathp[1].node) \ - == false); \ + assert(!rbtn_red_get(a_type, a_field, pathp[1].node)); \ if (rbtn_red_get(a_type, a_field, pathp->node)) { \ a_type *right = rbtn_right_get(a_type, a_field, \ pathp->node); \ @@ -681,7 +693,7 @@ a_prefix##remove(a_rbt_type *rbtree, a_type *node) { \ rbtn_rotate_left(a_type, a_field, pathp->node, \ tnode); \ /* Balance restored, but rotation modified */\ - /* subree root, which may actually be the tree */\ + /* subtree root, which may actually be the tree */\ /* root. */\ if (pathp == path) { \ /* Set root. */ \ @@ -849,7 +861,7 @@ a_prefix##remove(a_rbt_type *rbtree, a_type *node) { \ } \ /* Set root. */ \ rbtree->rbt_root = path->node; \ - assert(rbtn_red_get(a_type, a_field, rbtree->rbt_root) == false); \ + assert(!rbtn_red_get(a_type, a_field, rbtree->rbt_root)); \ } \ a_attr a_type * \ a_prefix##iter_recurse(a_rbt_type *rbtree, a_type *node, \ diff --git a/contrib/jemalloc/include/jemalloc/internal/rtree.h b/contrib/jemalloc/include/jemalloc/internal/rtree.h index bc74769f50ed..28ae9d1dd2d1 100644 --- a/contrib/jemalloc/include/jemalloc/internal/rtree.h +++ b/contrib/jemalloc/include/jemalloc/internal/rtree.h @@ -1,170 +1,292 @@ /* * This radix tree implementation is tailored to the singular purpose of - * tracking which chunks are currently owned by jemalloc. This functionality - * is mandatory for OS X, where jemalloc must be able to respond to object - * ownership queries. + * associating metadata with chunks that are currently owned by jemalloc. * ******************************************************************************* */ #ifdef JEMALLOC_H_TYPES +typedef struct rtree_node_elm_s rtree_node_elm_t; +typedef struct rtree_level_s rtree_level_t; typedef struct rtree_s rtree_t; /* - * Size of each radix tree node (must be a power of 2). This impacts tree - * depth. + * RTREE_BITS_PER_LEVEL must be a power of two that is no larger than the + * machine address width. */ -#define RTREE_NODESIZE (1U << 16) +#define LG_RTREE_BITS_PER_LEVEL 4 +#define RTREE_BITS_PER_LEVEL (ZU(1) << LG_RTREE_BITS_PER_LEVEL) +#define RTREE_HEIGHT_MAX \ + ((ZU(1) << (LG_SIZEOF_PTR+3)) / RTREE_BITS_PER_LEVEL) -typedef void *(rtree_alloc_t)(size_t); -typedef void (rtree_dalloc_t)(void *); +/* Used for two-stage lock-free node initialization. */ +#define RTREE_NODE_INITIALIZING ((rtree_node_elm_t *)0x1) + +/* + * The node allocation callback function's argument is the number of contiguous + * rtree_node_elm_t structures to allocate, and the resulting memory must be + * zeroed. + */ +typedef rtree_node_elm_t *(rtree_node_alloc_t)(size_t); +typedef void (rtree_node_dalloc_t)(rtree_node_elm_t *); #endif /* JEMALLOC_H_TYPES */ /******************************************************************************/ #ifdef JEMALLOC_H_STRUCTS +struct rtree_node_elm_s { + union { + void *pun; + rtree_node_elm_t *child; + extent_node_t *val; + }; +}; + +struct rtree_level_s { + /* + * A non-NULL subtree points to a subtree rooted along the hypothetical + * path to the leaf node corresponding to key 0. Depending on what keys + * have been used to store to the tree, an arbitrary combination of + * subtree pointers may remain NULL. + * + * Suppose keys comprise 48 bits, and LG_RTREE_BITS_PER_LEVEL is 4. + * This results in a 3-level tree, and the leftmost leaf can be directly + * accessed via subtrees[2], the subtree prefixed by 0x0000 (excluding + * 0x00000000) can be accessed via subtrees[1], and the remainder of the + * tree can be accessed via subtrees[0]. + * + * levels[0] : [ | 0x0001******** | 0x0002******** | ...] + * + * levels[1] : [ | 0x00000001**** | 0x00000002**** | ... ] + * + * levels[2] : [val(0x000000000000) | val(0x000000000001) | ...] + * + * This has practical implications on x64, which currently uses only the + * lower 47 bits of virtual address space in userland, thus leaving + * subtrees[0] unused and avoiding a level of tree traversal. + */ + union { + void *subtree_pun; + rtree_node_elm_t *subtree; + }; + /* Number of key bits distinguished by this level. */ + unsigned bits; + /* + * Cumulative number of key bits distinguished by traversing to + * corresponding tree level. + */ + unsigned cumbits; +}; + struct rtree_s { - rtree_alloc_t *alloc; - rtree_dalloc_t *dalloc; - malloc_mutex_t mutex; - void **root; - unsigned height; - unsigned level2bits[1]; /* Dynamically sized. */ + rtree_node_alloc_t *alloc; + rtree_node_dalloc_t *dalloc; + unsigned height; + /* + * Precomputed table used to convert from the number of leading 0 key + * bits to which subtree level to start at. + */ + unsigned start_level[RTREE_HEIGHT_MAX]; + rtree_level_t levels[RTREE_HEIGHT_MAX]; }; #endif /* JEMALLOC_H_STRUCTS */ /******************************************************************************/ #ifdef JEMALLOC_H_EXTERNS -rtree_t *rtree_new(unsigned bits, rtree_alloc_t *alloc, rtree_dalloc_t *dalloc); +bool rtree_new(rtree_t *rtree, unsigned bits, rtree_node_alloc_t *alloc, + rtree_node_dalloc_t *dalloc); void rtree_delete(rtree_t *rtree); -void rtree_prefork(rtree_t *rtree); -void rtree_postfork_parent(rtree_t *rtree); -void rtree_postfork_child(rtree_t *rtree); +rtree_node_elm_t *rtree_subtree_read_hard(rtree_t *rtree, + unsigned level); +rtree_node_elm_t *rtree_child_read_hard(rtree_t *rtree, + rtree_node_elm_t *elm, unsigned level); #endif /* JEMALLOC_H_EXTERNS */ /******************************************************************************/ #ifdef JEMALLOC_H_INLINES #ifndef JEMALLOC_ENABLE_INLINE -#ifdef JEMALLOC_DEBUG -uint8_t rtree_get_locked(rtree_t *rtree, uintptr_t key); -#endif -uint8_t rtree_get(rtree_t *rtree, uintptr_t key); -bool rtree_set(rtree_t *rtree, uintptr_t key, uint8_t val); +unsigned rtree_start_level(rtree_t *rtree, uintptr_t key); +uintptr_t rtree_subkey(rtree_t *rtree, uintptr_t key, unsigned level); + +bool rtree_node_valid(rtree_node_elm_t *node); +rtree_node_elm_t *rtree_child_tryread(rtree_node_elm_t *elm); +rtree_node_elm_t *rtree_child_read(rtree_t *rtree, rtree_node_elm_t *elm, + unsigned level); +extent_node_t *rtree_val_read(rtree_t *rtree, rtree_node_elm_t *elm, + bool dependent); +void rtree_val_write(rtree_t *rtree, rtree_node_elm_t *elm, + const extent_node_t *val); +rtree_node_elm_t *rtree_subtree_tryread(rtree_t *rtree, unsigned level); +rtree_node_elm_t *rtree_subtree_read(rtree_t *rtree, unsigned level); + +extent_node_t *rtree_get(rtree_t *rtree, uintptr_t key, bool dependent); +bool rtree_set(rtree_t *rtree, uintptr_t key, const extent_node_t *val); #endif #if (defined(JEMALLOC_ENABLE_INLINE) || defined(JEMALLOC_RTREE_C_)) -#define RTREE_GET_GENERATE(f) \ -/* The least significant bits of the key are ignored. */ \ -JEMALLOC_INLINE uint8_t \ -f(rtree_t *rtree, uintptr_t key) \ -{ \ - uint8_t ret; \ - uintptr_t subkey; \ - unsigned i, lshift, height, bits; \ - void **node, **child; \ - \ - RTREE_LOCK(&rtree->mutex); \ - for (i = lshift = 0, height = rtree->height, node = rtree->root;\ - i < height - 1; \ - i++, lshift += bits, node = child) { \ - bits = rtree->level2bits[i]; \ - subkey = (key << lshift) >> ((ZU(1) << (LG_SIZEOF_PTR + \ - 3)) - bits); \ - child = (void**)node[subkey]; \ - if (child == NULL) { \ - RTREE_UNLOCK(&rtree->mutex); \ - return (0); \ - } \ - } \ - \ - /* \ - * node is a leaf, so it contains values rather than node \ - * pointers. \ - */ \ - bits = rtree->level2bits[i]; \ - subkey = (key << lshift) >> ((ZU(1) << (LG_SIZEOF_PTR+3)) - \ - bits); \ - { \ - uint8_t *leaf = (uint8_t *)node; \ - ret = leaf[subkey]; \ - } \ - RTREE_UNLOCK(&rtree->mutex); \ - \ - RTREE_GET_VALIDATE \ - return (ret); \ +JEMALLOC_INLINE unsigned +rtree_start_level(rtree_t *rtree, uintptr_t key) +{ + unsigned start_level; + + if (unlikely(key == 0)) + return (rtree->height - 1); + + start_level = rtree->start_level[lg_floor(key) >> + LG_RTREE_BITS_PER_LEVEL]; + assert(start_level < rtree->height); + return (start_level); } -#ifdef JEMALLOC_DEBUG -# define RTREE_LOCK(l) malloc_mutex_lock(l) -# define RTREE_UNLOCK(l) malloc_mutex_unlock(l) -# define RTREE_GET_VALIDATE -RTREE_GET_GENERATE(rtree_get_locked) -# undef RTREE_LOCK -# undef RTREE_UNLOCK -# undef RTREE_GET_VALIDATE -#endif +JEMALLOC_INLINE uintptr_t +rtree_subkey(rtree_t *rtree, uintptr_t key, unsigned level) +{ -#define RTREE_LOCK(l) -#define RTREE_UNLOCK(l) -#ifdef JEMALLOC_DEBUG - /* - * Suppose that it were possible for a jemalloc-allocated chunk to be - * munmap()ped, followed by a different allocator in another thread re-using - * overlapping virtual memory, all without invalidating the cached rtree - * value. The result would be a false positive (the rtree would claim that - * jemalloc owns memory that it had actually discarded). This scenario - * seems impossible, but the following assertion is a prudent sanity check. - */ -# define RTREE_GET_VALIDATE \ - assert(rtree_get_locked(rtree, key) == ret); -#else -# define RTREE_GET_VALIDATE -#endif -RTREE_GET_GENERATE(rtree_get) -#undef RTREE_LOCK -#undef RTREE_UNLOCK -#undef RTREE_GET_VALIDATE + return ((key >> ((ZU(1) << (LG_SIZEOF_PTR+3)) - + rtree->levels[level].cumbits)) & ((ZU(1) << + rtree->levels[level].bits) - 1)); +} JEMALLOC_INLINE bool -rtree_set(rtree_t *rtree, uintptr_t key, uint8_t val) +rtree_node_valid(rtree_node_elm_t *node) +{ + + return ((uintptr_t)node > (uintptr_t)RTREE_NODE_INITIALIZING); +} + +JEMALLOC_INLINE rtree_node_elm_t * +rtree_child_tryread(rtree_node_elm_t *elm) +{ + rtree_node_elm_t *child; + + /* Double-checked read (first read may be stale. */ + child = elm->child; + if (!rtree_node_valid(child)) + child = atomic_read_p(&elm->pun); + return (child); +} + +JEMALLOC_INLINE rtree_node_elm_t * +rtree_child_read(rtree_t *rtree, rtree_node_elm_t *elm, unsigned level) +{ + rtree_node_elm_t *child; + + child = rtree_child_tryread(elm); + if (unlikely(!rtree_node_valid(child))) + child = rtree_child_read_hard(rtree, elm, level); + return (child); +} + +JEMALLOC_INLINE extent_node_t * +rtree_val_read(rtree_t *rtree, rtree_node_elm_t *elm, bool dependent) +{ + + if (dependent) { + /* + * Reading a val on behalf of a pointer to a valid allocation is + * guaranteed to be a clean read even without synchronization, + * because the rtree update became visible in memory before the + * pointer came into existence. + */ + return (elm->val); + } else { + /* + * An arbitrary read, e.g. on behalf of ivsalloc(), may not be + * dependent on a previous rtree write, which means a stale read + * could result if synchronization were omitted here. + */ + return (atomic_read_p(&elm->pun)); + } +} + +JEMALLOC_INLINE void +rtree_val_write(rtree_t *rtree, rtree_node_elm_t *elm, const extent_node_t *val) +{ + + atomic_write_p(&elm->pun, val); +} + +JEMALLOC_INLINE rtree_node_elm_t * +rtree_subtree_tryread(rtree_t *rtree, unsigned level) +{ + rtree_node_elm_t *subtree; + + /* Double-checked read (first read may be stale. */ + subtree = rtree->levels[level].subtree; + if (!rtree_node_valid(subtree)) + subtree = atomic_read_p(&rtree->levels[level].subtree_pun); + return (subtree); +} + +JEMALLOC_INLINE rtree_node_elm_t * +rtree_subtree_read(rtree_t *rtree, unsigned level) +{ + rtree_node_elm_t *subtree; + + subtree = rtree_subtree_tryread(rtree, level); + if (unlikely(!rtree_node_valid(subtree))) + subtree = rtree_subtree_read_hard(rtree, level); + return (subtree); +} + +JEMALLOC_INLINE extent_node_t * +rtree_get(rtree_t *rtree, uintptr_t key, bool dependent) { uintptr_t subkey; - unsigned i, lshift, height, bits; - void **node, **child; + unsigned i, start_level; + rtree_node_elm_t *node, *child; - malloc_mutex_lock(&rtree->mutex); - for (i = lshift = 0, height = rtree->height, node = rtree->root; - i < height - 1; - i++, lshift += bits, node = child) { - bits = rtree->level2bits[i]; - subkey = (key << lshift) >> ((ZU(1) << (LG_SIZEOF_PTR+3)) - - bits); - child = (void**)node[subkey]; - if (child == NULL) { - size_t size = ((i + 1 < height - 1) ? sizeof(void *) - : (sizeof(uint8_t))) << rtree->level2bits[i+1]; - child = (void**)rtree->alloc(size); - if (child == NULL) { - malloc_mutex_unlock(&rtree->mutex); - return (true); - } - memset(child, 0, size); - node[subkey] = child; + start_level = rtree_start_level(rtree, key); + + for (i = start_level, node = rtree_subtree_tryread(rtree, start_level); + /**/; i++, node = child) { + if (!dependent && unlikely(!rtree_node_valid(node))) + return (NULL); + subkey = rtree_subkey(rtree, key, i); + if (i == rtree->height - 1) { + /* + * node is a leaf, so it contains values rather than + * child pointers. + */ + return (rtree_val_read(rtree, &node[subkey], + dependent)); } + assert(i < rtree->height - 1); + child = rtree_child_tryread(&node[subkey]); } + not_reached(); +} - /* node is a leaf, so it contains values rather than node pointers. */ - bits = rtree->level2bits[i]; - subkey = (key << lshift) >> ((ZU(1) << (LG_SIZEOF_PTR+3)) - bits); - { - uint8_t *leaf = (uint8_t *)node; - leaf[subkey] = val; +JEMALLOC_INLINE bool +rtree_set(rtree_t *rtree, uintptr_t key, const extent_node_t *val) +{ + uintptr_t subkey; + unsigned i, start_level; + rtree_node_elm_t *node, *child; + + start_level = rtree_start_level(rtree, key); + + node = rtree_subtree_read(rtree, start_level); + if (node == NULL) + return (true); + for (i = start_level; /**/; i++, node = child) { + subkey = rtree_subkey(rtree, key, i); + if (i == rtree->height - 1) { + /* + * node is a leaf, so it contains values rather than + * child pointers. + */ + rtree_val_write(rtree, &node[subkey], val); + return (false); + } + assert(i + 1 < rtree->height); + child = rtree_child_read(rtree, &node[subkey], i); + if (child == NULL) + return (true); } - malloc_mutex_unlock(&rtree->mutex); - - return (false); + not_reached(); } #endif diff --git a/contrib/jemalloc/include/jemalloc/internal/size_classes.h b/contrib/jemalloc/include/jemalloc/internal/size_classes.h index 821102e5c1cd..eed6fbdf342a 100644 --- a/contrib/jemalloc/include/jemalloc/internal/size_classes.h +++ b/contrib/jemalloc/include/jemalloc/internal/size_classes.h @@ -2,689 +2,5559 @@ /******************************************************************************/ #ifdef JEMALLOC_H_TYPES -#if (LG_TINY_MIN == 3 && LG_QUANTUM == 3 && LG_PAGE == 12) -#define SIZE_CLASSES_DEFINED -/* SIZE_CLASS(bin, delta, sz) */ -#define SIZE_CLASSES \ - SIZE_CLASS(0, 8, 8) \ - SIZE_CLASS(1, 8, 16) \ - SIZE_CLASS(2, 8, 24) \ - SIZE_CLASS(3, 8, 32) \ - SIZE_CLASS(4, 8, 40) \ - SIZE_CLASS(5, 8, 48) \ - SIZE_CLASS(6, 8, 56) \ - SIZE_CLASS(7, 8, 64) \ - SIZE_CLASS(8, 16, 80) \ - SIZE_CLASS(9, 16, 96) \ - SIZE_CLASS(10, 16, 112) \ - SIZE_CLASS(11, 16, 128) \ - SIZE_CLASS(12, 32, 160) \ - SIZE_CLASS(13, 32, 192) \ - SIZE_CLASS(14, 32, 224) \ - SIZE_CLASS(15, 32, 256) \ - SIZE_CLASS(16, 64, 320) \ - SIZE_CLASS(17, 64, 384) \ - SIZE_CLASS(18, 64, 448) \ - SIZE_CLASS(19, 64, 512) \ - SIZE_CLASS(20, 128, 640) \ - SIZE_CLASS(21, 128, 768) \ - SIZE_CLASS(22, 128, 896) \ - SIZE_CLASS(23, 128, 1024) \ - SIZE_CLASS(24, 256, 1280) \ - SIZE_CLASS(25, 256, 1536) \ - SIZE_CLASS(26, 256, 1792) \ - SIZE_CLASS(27, 256, 2048) \ - SIZE_CLASS(28, 512, 2560) \ - SIZE_CLASS(29, 512, 3072) \ - SIZE_CLASS(30, 512, 3584) \ +/* + * This header requires LG_SIZEOF_PTR, LG_TINY_MIN, LG_QUANTUM, and LG_PAGE to + * be defined prior to inclusion, and it in turn defines: + * + * LG_SIZE_CLASS_GROUP: Lg of size class count for each size doubling. + * SIZE_CLASSES: Complete table of + * SC(index, lg_grp, lg_delta, ndelta, bin, lg_delta_lookup) + * tuples. + * index: Size class index. + * lg_grp: Lg group base size (no deltas added). + * lg_delta: Lg delta to previous size class. + * ndelta: Delta multiplier. size == 1< 255) # error "Too many small size classes" diff --git a/contrib/jemalloc/include/jemalloc/internal/stats.h b/contrib/jemalloc/include/jemalloc/internal/stats.h index 27f68e3681cf..c91dba99dbe2 100644 --- a/contrib/jemalloc/include/jemalloc/internal/stats.h +++ b/contrib/jemalloc/include/jemalloc/internal/stats.h @@ -4,6 +4,7 @@ typedef struct tcache_bin_stats_s tcache_bin_stats_t; typedef struct malloc_bin_stats_s malloc_bin_stats_t; typedef struct malloc_large_stats_s malloc_large_stats_t; +typedef struct malloc_huge_stats_s malloc_huge_stats_t; typedef struct arena_stats_s arena_stats_t; typedef struct chunk_stats_s chunk_stats_t; @@ -20,12 +21,6 @@ struct tcache_bin_stats_s { }; struct malloc_bin_stats_s { - /* - * Current number of bytes allocated, including objects currently - * cached by tcache. - */ - size_t allocated; - /* * Total number of allocation/deallocation requests served directly by * the bin. Note that tcache may allocate an object, then recycle it @@ -42,6 +37,12 @@ struct malloc_bin_stats_s { */ uint64_t nrequests; + /* + * Current number of regions of this size class, including regions + * currently cached by tcache. + */ + size_t curregs; + /* Number of tcache fills from this bin. */ uint64_t nfills; @@ -78,10 +79,25 @@ struct malloc_large_stats_s { */ uint64_t nrequests; - /* Current number of runs of this size class. */ + /* + * Current number of runs of this size class, including runs currently + * cached by tcache. + */ size_t curruns; }; +struct malloc_huge_stats_s { + /* + * Total number of allocation/deallocation requests served directly by + * the arena. + */ + uint64_t nmalloc; + uint64_t ndalloc; + + /* Current number of (multi-)chunk allocations of this size class. */ + size_t curhchunks; +}; + struct arena_stats_s { /* Number of bytes currently mapped. */ size_t mapped; @@ -95,34 +111,28 @@ struct arena_stats_s { uint64_t nmadvise; uint64_t purged; + /* + * Number of bytes currently mapped purely for metadata purposes, and + * number of bytes currently allocated for internal metadata. + */ + size_t metadata_mapped; + size_t metadata_allocated; /* Protected via atomic_*_z(). */ + /* Per-size-category statistics. */ size_t allocated_large; uint64_t nmalloc_large; uint64_t ndalloc_large; uint64_t nrequests_large; - /* - * One element for each possible size class, including sizes that - * overlap with bin size classes. This is necessary because ipalloc() - * sometimes has to use such large objects in order to assure proper - * alignment. - */ + size_t allocated_huge; + uint64_t nmalloc_huge; + uint64_t ndalloc_huge; + + /* One element for each large size class. */ malloc_large_stats_t *lstats; -}; -struct chunk_stats_s { - /* Number of chunks that were allocated. */ - uint64_t nchunks; - - /* High-water mark for number of chunks allocated. */ - size_t highchunks; - - /* - * Current number of chunks allocated. This value isn't maintained for - * any other purpose, so keep track of it in order to be able to set - * highchunks. - */ - size_t curchunks; + /* One element for each huge size class. */ + malloc_huge_stats_t *hstats; }; #endif /* JEMALLOC_H_STRUCTS */ diff --git a/contrib/jemalloc/include/jemalloc/internal/tcache.h b/contrib/jemalloc/include/jemalloc/internal/tcache.h index c3d4b58d4dc5..493f4575b61f 100644 --- a/contrib/jemalloc/include/jemalloc/internal/tcache.h +++ b/contrib/jemalloc/include/jemalloc/internal/tcache.h @@ -4,6 +4,7 @@ typedef struct tcache_bin_info_s tcache_bin_info_t; typedef struct tcache_bin_s tcache_bin_t; typedef struct tcache_s tcache_t; +typedef struct tcaches_s tcaches_t; /* * tcache pointers close to NULL are used to encode state information that is @@ -15,6 +16,11 @@ typedef struct tcache_s tcache_t; #define TCACHE_STATE_PURGATORY ((tcache_t *)(uintptr_t)3) #define TCACHE_STATE_MAX TCACHE_STATE_PURGATORY +/* + * Absolute minimum number of cache slots for each small bin. + */ +#define TCACHE_NSLOTS_SMALL_MIN 20 + /* * Absolute maximum number of cache slots for each small bin in the thread * cache. This is an additional constraint beyond that imposed as: twice the @@ -69,10 +75,9 @@ struct tcache_bin_s { struct tcache_s { ql_elm(tcache_t) link; /* Used for aggregating stats. */ - uint64_t prof_accumbytes;/* Cleared after arena_prof_accum() */ - arena_t *arena; /* This thread's arena. */ + uint64_t prof_accumbytes;/* Cleared after arena_prof_accum(). */ unsigned ev_cnt; /* Event count since incremental GC. */ - unsigned next_gc_bin; /* Next bin to GC. */ + index_t next_gc_bin; /* Next bin to GC. */ tcache_bin_t tbins[1]; /* Dynamically sized. */ /* * The pointer stacks associated with tbins follow as a contiguous @@ -82,6 +87,14 @@ struct tcache_s { */ }; +/* Linkage for list of available (previously used) explicit tcache IDs. */ +struct tcaches_s { + union { + tcache_t *tcache; + tcaches_t *next; + }; +}; + #endif /* JEMALLOC_H_STRUCTS */ /******************************************************************************/ #ifdef JEMALLOC_H_EXTERNS @@ -95,84 +108,90 @@ extern tcache_bin_info_t *tcache_bin_info; * Number of tcache bins. There are NBINS small-object bins, plus 0 or more * large-object bins. */ -extern size_t nhbins; +extern size_t nhbins; /* Maximum cached size class. */ -extern size_t tcache_maxclass; +extern size_t tcache_maxclass; + +/* + * Explicit tcaches, managed via the tcache.{create,flush,destroy} mallctls and + * usable via the MALLOCX_TCACHE() flag. The automatic per thread tcaches are + * completely disjoint from this data structure. tcaches starts off as a sparse + * array, so it has no physical memory footprint until individual pages are + * touched. This allows the entire array to be allocated the first time an + * explicit tcache is created without a disproportionate impact on memory usage. + */ +extern tcaches_t *tcaches; size_t tcache_salloc(const void *ptr); -void tcache_event_hard(tcache_t *tcache); -void *tcache_alloc_small_hard(tcache_t *tcache, tcache_bin_t *tbin, - size_t binind); -void tcache_bin_flush_small(tcache_bin_t *tbin, size_t binind, unsigned rem, - tcache_t *tcache); -void tcache_bin_flush_large(tcache_bin_t *tbin, size_t binind, unsigned rem, - tcache_t *tcache); +void tcache_event_hard(tsd_t *tsd, tcache_t *tcache); +void *tcache_alloc_small_hard(tsd_t *tsd, arena_t *arena, tcache_t *tcache, + tcache_bin_t *tbin, index_t binind); +void tcache_bin_flush_small(tsd_t *tsd, tcache_t *tcache, tcache_bin_t *tbin, + index_t binind, unsigned rem); +void tcache_bin_flush_large(tsd_t *tsd, tcache_bin_t *tbin, index_t binind, + unsigned rem, tcache_t *tcache); void tcache_arena_associate(tcache_t *tcache, arena_t *arena); -void tcache_arena_dissociate(tcache_t *tcache); -tcache_t *tcache_create(arena_t *arena); -void tcache_destroy(tcache_t *tcache); -void tcache_thread_cleanup(void *arg); +void tcache_arena_reassociate(tcache_t *tcache, arena_t *oldarena, + arena_t *newarena); +void tcache_arena_dissociate(tcache_t *tcache, arena_t *arena); +tcache_t *tcache_get_hard(tsd_t *tsd); +tcache_t *tcache_create(tsd_t *tsd, arena_t *arena); +void tcache_cleanup(tsd_t *tsd); +void tcache_enabled_cleanup(tsd_t *tsd); void tcache_stats_merge(tcache_t *tcache, arena_t *arena); -bool tcache_boot0(void); -bool tcache_boot1(void); +bool tcaches_create(tsd_t *tsd, unsigned *r_ind); +void tcaches_flush(tsd_t *tsd, unsigned ind); +void tcaches_destroy(tsd_t *tsd, unsigned ind); +bool tcache_boot(void); #endif /* JEMALLOC_H_EXTERNS */ /******************************************************************************/ #ifdef JEMALLOC_H_INLINES #ifndef JEMALLOC_ENABLE_INLINE -malloc_tsd_protos(JEMALLOC_ATTR(unused), tcache, tcache_t *) -malloc_tsd_protos(JEMALLOC_ATTR(unused), tcache_enabled, tcache_enabled_t) - -void tcache_event(tcache_t *tcache); +void tcache_event(tsd_t *tsd, tcache_t *tcache); void tcache_flush(void); bool tcache_enabled_get(void); -tcache_t *tcache_get(bool create); +tcache_t *tcache_get(tsd_t *tsd, bool create); void tcache_enabled_set(bool enabled); void *tcache_alloc_easy(tcache_bin_t *tbin); -void *tcache_alloc_small(tcache_t *tcache, size_t size, bool zero); -void *tcache_alloc_large(tcache_t *tcache, size_t size, bool zero); -void tcache_dalloc_small(tcache_t *tcache, void *ptr, size_t binind); -void tcache_dalloc_large(tcache_t *tcache, void *ptr, size_t size); +void *tcache_alloc_small(tsd_t *tsd, arena_t *arena, tcache_t *tcache, + size_t size, bool zero); +void *tcache_alloc_large(tsd_t *tsd, arena_t *arena, tcache_t *tcache, + size_t size, bool zero); +void tcache_dalloc_small(tsd_t *tsd, tcache_t *tcache, void *ptr, + index_t binind); +void tcache_dalloc_large(tsd_t *tsd, tcache_t *tcache, void *ptr, + size_t size); +tcache_t *tcaches_get(tsd_t *tsd, unsigned ind); #endif #if (defined(JEMALLOC_ENABLE_INLINE) || defined(JEMALLOC_TCACHE_C_)) -/* Map of thread-specific caches. */ -malloc_tsd_externs(tcache, tcache_t *) -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_ALWAYS_INLINE, tcache_enabled, tcache_enabled_t, - tcache_enabled_default, malloc_tsd_no_cleanup) - JEMALLOC_INLINE void tcache_flush(void) { - tcache_t *tcache; + tsd_t *tsd; cassert(config_tcache); - tcache = *tcache_tsd_get(); - if ((uintptr_t)tcache <= (uintptr_t)TCACHE_STATE_MAX) - return; - tcache_destroy(tcache); - tcache = NULL; - tcache_tsd_set(&tcache); + tsd = tsd_fetch(); + tcache_cleanup(tsd); } JEMALLOC_INLINE bool tcache_enabled_get(void) { + tsd_t *tsd; tcache_enabled_t tcache_enabled; cassert(config_tcache); - tcache_enabled = *tcache_enabled_tsd_get(); + tsd = tsd_fetch(); + tcache_enabled = tsd_tcache_enabled_get(tsd); if (tcache_enabled == tcache_enabled_default) { tcache_enabled = (tcache_enabled_t)opt_tcache; - tcache_enabled_tsd_set(&tcache_enabled); + tsd_tcache_enabled_set(tsd, tcache_enabled); } return ((bool)tcache_enabled); @@ -181,85 +200,41 @@ tcache_enabled_get(void) JEMALLOC_INLINE void tcache_enabled_set(bool enabled) { + tsd_t *tsd; tcache_enabled_t tcache_enabled; - tcache_t *tcache; cassert(config_tcache); + tsd = tsd_fetch(); + tcache_enabled = (tcache_enabled_t)enabled; - tcache_enabled_tsd_set(&tcache_enabled); - tcache = *tcache_tsd_get(); - if (enabled) { - if (tcache == TCACHE_STATE_DISABLED) { - tcache = NULL; - tcache_tsd_set(&tcache); - } - } else /* disabled */ { - if (tcache > TCACHE_STATE_MAX) { - tcache_destroy(tcache); - tcache = NULL; - } - if (tcache == NULL) { - tcache = TCACHE_STATE_DISABLED; - tcache_tsd_set(&tcache); - } - } + tsd_tcache_enabled_set(tsd, tcache_enabled); + + if (!enabled) + tcache_cleanup(tsd); } JEMALLOC_ALWAYS_INLINE tcache_t * -tcache_get(bool create) +tcache_get(tsd_t *tsd, bool create) { tcache_t *tcache; - if (config_tcache == false) - return (NULL); - if (config_lazy_lock && isthreaded == false) + if (!config_tcache) return (NULL); - tcache = *tcache_tsd_get(); - if ((uintptr_t)tcache <= (uintptr_t)TCACHE_STATE_MAX) { - if (tcache == TCACHE_STATE_DISABLED) - return (NULL); - if (tcache == NULL) { - if (create == false) { - /* - * Creating a tcache here would cause - * allocation as a side effect of free(). - * Ordinarily that would be okay since - * tcache_create() failure is a soft failure - * that doesn't propagate. However, if TLS - * data are freed via free() as in glibc, - * subtle corruption could result from setting - * a TLS variable after its backing memory is - * freed. - */ - return (NULL); - } - if (tcache_enabled_get() == false) { - tcache_enabled_set(false); /* Memoize. */ - return (NULL); - } - return (tcache_create(choose_arena(NULL))); - } - if (tcache == TCACHE_STATE_PURGATORY) { - /* - * Make a note that an allocator function was called - * after tcache_thread_cleanup() was called. - */ - tcache = TCACHE_STATE_REINCARNATED; - tcache_tsd_set(&tcache); - return (NULL); - } - if (tcache == TCACHE_STATE_REINCARNATED) - return (NULL); - not_reached(); + tcache = tsd_tcache_get(tsd); + if (!create) + return (tcache); + if (unlikely(tcache == NULL) && tsd_nominal(tsd)) { + tcache = tcache_get_hard(tsd); + tsd_tcache_set(tsd, tcache); } return (tcache); } JEMALLOC_ALWAYS_INLINE void -tcache_event(tcache_t *tcache) +tcache_event(tsd_t *tsd, tcache_t *tcache) { if (TCACHE_GC_INCR == 0) @@ -267,8 +242,8 @@ tcache_event(tcache_t *tcache) tcache->ev_cnt++; assert(tcache->ev_cnt <= TCACHE_GC_INCR); - if (tcache->ev_cnt == TCACHE_GC_INCR) - tcache_event_hard(tcache); + if (unlikely(tcache->ev_cnt == TCACHE_GC_INCR)) + tcache_event_hard(tsd, tcache); } JEMALLOC_ALWAYS_INLINE void * @@ -276,85 +251,87 @@ tcache_alloc_easy(tcache_bin_t *tbin) { void *ret; - if (tbin->ncached == 0) { + if (unlikely(tbin->ncached == 0)) { tbin->low_water = -1; return (NULL); } tbin->ncached--; - if ((int)tbin->ncached < tbin->low_water) + if (unlikely((int)tbin->ncached < tbin->low_water)) tbin->low_water = tbin->ncached; ret = tbin->avail[tbin->ncached]; return (ret); } JEMALLOC_ALWAYS_INLINE void * -tcache_alloc_small(tcache_t *tcache, size_t size, bool zero) +tcache_alloc_small(tsd_t *tsd, arena_t *arena, tcache_t *tcache, size_t size, + bool zero) { void *ret; - size_t binind; + index_t binind; + size_t usize; tcache_bin_t *tbin; - binind = SMALL_SIZE2BIN(size); + binind = size2index(size); assert(binind < NBINS); tbin = &tcache->tbins[binind]; - size = arena_bin_info[binind].reg_size; + usize = index2size(binind); ret = tcache_alloc_easy(tbin); - if (ret == NULL) { - ret = tcache_alloc_small_hard(tcache, tbin, binind); + if (unlikely(ret == NULL)) { + ret = tcache_alloc_small_hard(tsd, arena, tcache, tbin, binind); if (ret == NULL) return (NULL); } - assert(tcache_salloc(ret) == arena_bin_info[binind].reg_size); + assert(tcache_salloc(ret) == usize); - if (zero == false) { + if (likely(!zero)) { if (config_fill) { - if (opt_junk) { + if (unlikely(opt_junk_alloc)) { arena_alloc_junk_small(ret, &arena_bin_info[binind], false); - } else if (opt_zero) - memset(ret, 0, size); + } else if (unlikely(opt_zero)) + memset(ret, 0, usize); } - VALGRIND_MAKE_MEM_UNDEFINED(ret, size); } else { - if (config_fill && opt_junk) { + if (config_fill && unlikely(opt_junk_alloc)) { arena_alloc_junk_small(ret, &arena_bin_info[binind], true); } - VALGRIND_MAKE_MEM_UNDEFINED(ret, size); - memset(ret, 0, size); + memset(ret, 0, usize); } if (config_stats) tbin->tstats.nrequests++; if (config_prof) - tcache->prof_accumbytes += arena_bin_info[binind].reg_size; - tcache_event(tcache); + tcache->prof_accumbytes += usize; + tcache_event(tsd, tcache); return (ret); } JEMALLOC_ALWAYS_INLINE void * -tcache_alloc_large(tcache_t *tcache, size_t size, bool zero) +tcache_alloc_large(tsd_t *tsd, arena_t *arena, tcache_t *tcache, size_t size, + bool zero) { void *ret; - size_t binind; + index_t binind; + size_t usize; tcache_bin_t *tbin; - size = PAGE_CEILING(size); - assert(size <= tcache_maxclass); - binind = NBINS + (size >> LG_PAGE) - 1; + binind = size2index(size); + usize = index2size(binind); + assert(usize <= tcache_maxclass); assert(binind < nhbins); tbin = &tcache->tbins[binind]; ret = tcache_alloc_easy(tbin); - if (ret == NULL) { + if (unlikely(ret == NULL)) { /* * Only allocate one large object at a time, because it's quite * expensive to create one and not use it. */ - ret = arena_malloc_large(tcache->arena, size, zero); + ret = arena_malloc_large(arena, usize, zero); if (ret == NULL) return (NULL); } else { - if (config_prof && prof_promote && size == PAGE) { + if (config_prof && usize == LARGE_MINCLASS) { arena_chunk_t *chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ret); size_t pageind = (((uintptr_t)ret - (uintptr_t)chunk) >> @@ -362,57 +339,54 @@ tcache_alloc_large(tcache_t *tcache, size_t size, bool zero) arena_mapbits_large_binind_set(chunk, pageind, BININD_INVALID); } - if (zero == false) { + if (likely(!zero)) { if (config_fill) { - if (opt_junk) - memset(ret, 0xa5, size); - else if (opt_zero) - memset(ret, 0, size); + if (unlikely(opt_junk_alloc)) + memset(ret, 0xa5, usize); + else if (unlikely(opt_zero)) + memset(ret, 0, usize); } - VALGRIND_MAKE_MEM_UNDEFINED(ret, size); - } else { - VALGRIND_MAKE_MEM_UNDEFINED(ret, size); - memset(ret, 0, size); - } + } else + memset(ret, 0, usize); if (config_stats) tbin->tstats.nrequests++; if (config_prof) - tcache->prof_accumbytes += size; + tcache->prof_accumbytes += usize; } - tcache_event(tcache); + tcache_event(tsd, tcache); return (ret); } JEMALLOC_ALWAYS_INLINE void -tcache_dalloc_small(tcache_t *tcache, void *ptr, size_t binind) +tcache_dalloc_small(tsd_t *tsd, tcache_t *tcache, void *ptr, index_t binind) { tcache_bin_t *tbin; tcache_bin_info_t *tbin_info; assert(tcache_salloc(ptr) <= SMALL_MAXCLASS); - if (config_fill && opt_junk) + if (config_fill && unlikely(opt_junk_free)) arena_dalloc_junk_small(ptr, &arena_bin_info[binind]); tbin = &tcache->tbins[binind]; tbin_info = &tcache_bin_info[binind]; - if (tbin->ncached == tbin_info->ncached_max) { - tcache_bin_flush_small(tbin, binind, (tbin_info->ncached_max >> - 1), tcache); + if (unlikely(tbin->ncached == tbin_info->ncached_max)) { + tcache_bin_flush_small(tsd, tcache, tbin, binind, + (tbin_info->ncached_max >> 1)); } assert(tbin->ncached < tbin_info->ncached_max); tbin->avail[tbin->ncached] = ptr; tbin->ncached++; - tcache_event(tcache); + tcache_event(tsd, tcache); } JEMALLOC_ALWAYS_INLINE void -tcache_dalloc_large(tcache_t *tcache, void *ptr, size_t size) +tcache_dalloc_large(tsd_t *tsd, tcache_t *tcache, void *ptr, size_t size) { - size_t binind; + index_t binind; tcache_bin_t *tbin; tcache_bin_info_t *tbin_info; @@ -420,22 +394,31 @@ tcache_dalloc_large(tcache_t *tcache, void *ptr, size_t size) assert(tcache_salloc(ptr) > SMALL_MAXCLASS); assert(tcache_salloc(ptr) <= tcache_maxclass); - binind = NBINS + (size >> LG_PAGE) - 1; + binind = size2index(size); - if (config_fill && opt_junk) - memset(ptr, 0x5a, size); + if (config_fill && unlikely(opt_junk_free)) + arena_dalloc_junk_large(ptr, size); tbin = &tcache->tbins[binind]; tbin_info = &tcache_bin_info[binind]; - if (tbin->ncached == tbin_info->ncached_max) { - tcache_bin_flush_large(tbin, binind, (tbin_info->ncached_max >> - 1), tcache); + if (unlikely(tbin->ncached == tbin_info->ncached_max)) { + tcache_bin_flush_large(tsd, tbin, binind, + (tbin_info->ncached_max >> 1), tcache); } assert(tbin->ncached < tbin_info->ncached_max); tbin->avail[tbin->ncached] = ptr; tbin->ncached++; - tcache_event(tcache); + tcache_event(tsd, tcache); +} + +JEMALLOC_ALWAYS_INLINE tcache_t * +tcaches_get(tsd_t *tsd, unsigned ind) +{ + tcaches_t *elm = &tcaches[ind]; + if (unlikely(elm->tcache == NULL)) + elm->tcache = tcache_create(tsd, arena_choose(tsd, NULL)); + return (elm->tcache); } #endif diff --git a/contrib/jemalloc/include/jemalloc/internal/tsd.h b/contrib/jemalloc/include/jemalloc/internal/tsd.h index 9fb4a23ec6bf..62a887e617ac 100644 --- a/contrib/jemalloc/include/jemalloc/internal/tsd.h +++ b/contrib/jemalloc/include/jemalloc/internal/tsd.h @@ -2,7 +2,7 @@ #ifdef JEMALLOC_H_TYPES /* Maximum number of malloc_tsd users with cleanup functions. */ -#define MALLOC_TSD_CLEANUPS_MAX 8 +#define MALLOC_TSD_CLEANUPS_MAX 2 typedef bool (*malloc_tsd_cleanup_t)(void); @@ -12,9 +12,18 @@ typedef struct tsd_init_block_s tsd_init_block_t; typedef struct tsd_init_head_s tsd_init_head_t; #endif +typedef struct tsd_s tsd_t; + +typedef enum { + tsd_state_uninitialized, + tsd_state_nominal, + tsd_state_purgatory, + tsd_state_reincarnated +} tsd_state_t; + /* * TLS/TSD-agnostic macro-based implementation of thread-specific data. There - * are four macros that support (at least) three use cases: file-private, + * are five macros that support (at least) three use cases: file-private, * library-private, and library-private inlined. Following is an example * library-private tsd variable: * @@ -24,34 +33,36 @@ typedef struct tsd_init_head_s tsd_init_head_t; * int y; * } example_t; * #define EX_INITIALIZER JEMALLOC_CONCAT({0, 0}) - * malloc_tsd_protos(, example, example_t *) - * malloc_tsd_externs(example, example_t *) + * malloc_tsd_types(example_, example_t) + * malloc_tsd_protos(, example_, example_t) + * malloc_tsd_externs(example_, example_t) * In example.c: - * malloc_tsd_data(, example, example_t *, EX_INITIALIZER) - * malloc_tsd_funcs(, example, example_t *, EX_INITIALIZER, + * malloc_tsd_data(, example_, example_t, EX_INITIALIZER) + * malloc_tsd_funcs(, example_, example_t, EX_INITIALIZER, * example_tsd_cleanup) * * The result is a set of generated functions, e.g.: * * bool example_tsd_boot(void) {...} - * example_t **example_tsd_get() {...} - * void example_tsd_set(example_t **val) {...} + * example_t *example_tsd_get() {...} + * void example_tsd_set(example_t *val) {...} * * Note that all of the functions deal in terms of (a_type *) rather than - * (a_type) so that it is possible to support non-pointer types (unlike + * (a_type) so that it is possible to support non-pointer types (unlike * pthreads TSD). example_tsd_cleanup() is passed an (a_type *) pointer that is - * cast to (void *). This means that the cleanup function needs to cast *and* - * dereference the function argument, e.g.: + * cast to (void *). This means that the cleanup function needs to cast the + * function argument to (a_type *), then dereference the resulting pointer to + * access fields, e.g. * * void * example_tsd_cleanup(void *arg) * { - * example_t *example = *(example_t **)arg; + * example_t *example = (example_t *)arg; * + * example->x = 42; * [...] - * if ([want the cleanup function to be called again]) { - * example_tsd_set(&example); - * } + * if ([want the cleanup function to be called again]) + * example_tsd_set(example); * } * * If example_tsd_set() is called within example_tsd_cleanup(), it will be @@ -60,63 +71,96 @@ typedef struct tsd_init_head_s tsd_init_head_t; * non-NULL. */ +/* malloc_tsd_types(). */ +#ifdef JEMALLOC_MALLOC_THREAD_CLEANUP +#define malloc_tsd_types(a_name, a_type) +#elif (defined(JEMALLOC_TLS)) +#define malloc_tsd_types(a_name, a_type) +#elif (defined(_WIN32)) +#define malloc_tsd_types(a_name, a_type) \ +typedef struct { \ + bool initialized; \ + a_type val; \ +} a_name##tsd_wrapper_t; +#else +#define malloc_tsd_types(a_name, a_type) \ +typedef struct { \ + bool initialized; \ + a_type val; \ +} a_name##tsd_wrapper_t; +#endif + /* malloc_tsd_protos(). */ #define malloc_tsd_protos(a_attr, a_name, a_type) \ a_attr bool \ -a_name##_tsd_boot(void); \ -a_attr a_type * \ -a_name##_tsd_get(void); \ +a_name##tsd_boot0(void); \ a_attr void \ -a_name##_tsd_set(a_type *val); +a_name##tsd_boot1(void); \ +a_attr bool \ +a_name##tsd_boot(void); \ +a_attr a_type * \ +a_name##tsd_get(void); \ +a_attr void \ +a_name##tsd_set(a_type *val); /* malloc_tsd_externs(). */ #ifdef JEMALLOC_MALLOC_THREAD_CLEANUP #define malloc_tsd_externs(a_name, a_type) \ -extern __thread a_type a_name##_tls; \ -extern __thread bool a_name##_initialized; \ -extern bool a_name##_booted; +extern __thread a_type a_name##tsd_tls; \ +extern __thread bool a_name##tsd_initialized; \ +extern bool a_name##tsd_booted; #elif (defined(JEMALLOC_TLS)) #define malloc_tsd_externs(a_name, a_type) \ -extern __thread a_type a_name##_tls; \ -extern pthread_key_t a_name##_tsd; \ -extern bool a_name##_booted; +extern __thread a_type a_name##tsd_tls; \ +extern pthread_key_t a_name##tsd_tsd; \ +extern bool a_name##tsd_booted; #elif (defined(_WIN32)) #define malloc_tsd_externs(a_name, a_type) \ -extern DWORD a_name##_tsd; \ -extern bool a_name##_booted; +extern DWORD a_name##tsd_tsd; \ +extern a_name##tsd_wrapper_t a_name##tsd_boot_wrapper; \ +extern bool a_name##tsd_booted; #else #define malloc_tsd_externs(a_name, a_type) \ -extern pthread_key_t a_name##_tsd; \ -extern tsd_init_head_t a_name##_tsd_init_head; \ -extern bool a_name##_booted; +extern pthread_key_t a_name##tsd_tsd; \ +extern tsd_init_head_t a_name##tsd_init_head; \ +extern a_name##tsd_wrapper_t a_name##tsd_boot_wrapper; \ +extern bool a_name##tsd_booted; #endif /* malloc_tsd_data(). */ #ifdef JEMALLOC_MALLOC_THREAD_CLEANUP #define malloc_tsd_data(a_attr, a_name, a_type, a_initializer) \ a_attr __thread a_type JEMALLOC_TLS_MODEL \ - a_name##_tls = a_initializer; \ + a_name##tsd_tls = a_initializer; \ a_attr __thread bool JEMALLOC_TLS_MODEL \ - a_name##_initialized = false; \ -a_attr bool a_name##_booted = false; + a_name##tsd_initialized = false; \ +a_attr bool a_name##tsd_booted = false; #elif (defined(JEMALLOC_TLS)) #define malloc_tsd_data(a_attr, a_name, a_type, a_initializer) \ a_attr __thread a_type JEMALLOC_TLS_MODEL \ - a_name##_tls = a_initializer; \ -a_attr pthread_key_t a_name##_tsd; \ -a_attr bool a_name##_booted = false; + a_name##tsd_tls = a_initializer; \ +a_attr pthread_key_t a_name##tsd_tsd; \ +a_attr bool a_name##tsd_booted = false; #elif (defined(_WIN32)) #define malloc_tsd_data(a_attr, a_name, a_type, a_initializer) \ -a_attr DWORD a_name##_tsd; \ -a_attr bool a_name##_booted = false; +a_attr DWORD a_name##tsd_tsd; \ +a_attr a_name##tsd_wrapper_t a_name##tsd_boot_wrapper = { \ + false, \ + a_initializer \ +}; \ +a_attr bool a_name##tsd_booted = false; #else #define malloc_tsd_data(a_attr, a_name, a_type, a_initializer) \ -a_attr pthread_key_t a_name##_tsd; \ -a_attr tsd_init_head_t a_name##_tsd_init_head = { \ +a_attr pthread_key_t a_name##tsd_tsd; \ +a_attr tsd_init_head_t a_name##tsd_init_head = { \ ql_head_initializer(blocks), \ MALLOC_MUTEX_INITIALIZER \ }; \ -a_attr bool a_name##_booted = false; +a_attr a_name##tsd_wrapper_t a_name##tsd_boot_wrapper = { \ + false, \ + a_initializer \ +}; \ +a_attr bool a_name##tsd_booted = false; #endif /* malloc_tsd_funcs(). */ @@ -125,75 +169,100 @@ a_attr bool a_name##_booted = false; a_cleanup) \ /* Initialization/cleanup. */ \ a_attr bool \ -a_name##_tsd_cleanup_wrapper(void) \ +a_name##tsd_cleanup_wrapper(void) \ { \ \ - if (a_name##_initialized) { \ - a_name##_initialized = false; \ - a_cleanup(&a_name##_tls); \ + if (a_name##tsd_initialized) { \ + a_name##tsd_initialized = false; \ + a_cleanup(&a_name##tsd_tls); \ } \ - return (a_name##_initialized); \ + return (a_name##tsd_initialized); \ } \ a_attr bool \ -a_name##_tsd_boot(void) \ +a_name##tsd_boot0(void) \ { \ \ if (a_cleanup != malloc_tsd_no_cleanup) { \ malloc_tsd_cleanup_register( \ - &a_name##_tsd_cleanup_wrapper); \ + &a_name##tsd_cleanup_wrapper); \ } \ - a_name##_booted = true; \ + a_name##tsd_booted = true; \ return (false); \ } \ +a_attr void \ +a_name##tsd_boot1() \ +{ \ + \ + /* Do nothing. */ \ +} \ +a_attr bool \ +a_name##tsd_boot(void) \ +{ \ + \ + return (a_name##tsd_boot0()); \ +} \ /* Get/set. */ \ a_attr a_type * \ -a_name##_tsd_get(void) \ +a_name##tsd_get(void) \ { \ \ - assert(a_name##_booted); \ - return (&a_name##_tls); \ + assert(a_name##tsd_booted); \ + return (&a_name##tsd_tls); \ } \ a_attr void \ -a_name##_tsd_set(a_type *val) \ +a_name##tsd_set(a_type *val) \ { \ \ - assert(a_name##_booted); \ - a_name##_tls = (*val); \ + assert(a_name##tsd_booted); \ + a_name##tsd_tls = (*val); \ if (a_cleanup != malloc_tsd_no_cleanup) \ - a_name##_initialized = true; \ + a_name##tsd_initialized = true; \ } #elif (defined(JEMALLOC_TLS)) #define malloc_tsd_funcs(a_attr, a_name, a_type, a_initializer, \ a_cleanup) \ /* Initialization/cleanup. */ \ a_attr bool \ -a_name##_tsd_boot(void) \ +a_name##tsd_boot0(void) \ { \ \ if (a_cleanup != malloc_tsd_no_cleanup) { \ - if (pthread_key_create(&a_name##_tsd, a_cleanup) != 0) \ + if (pthread_key_create(&a_name##tsd_tsd, a_cleanup) != \ + 0) \ return (true); \ } \ - a_name##_booted = true; \ + a_name##tsd_booted = true; \ return (false); \ } \ +a_attr void \ +a_name##tsd_boot1() \ +{ \ + \ + /* Do nothing. */ \ +} \ +a_attr bool \ +a_name##tsd_boot(void) \ +{ \ + \ + return (a_name##tsd_boot0()); \ +} \ /* Get/set. */ \ a_attr a_type * \ -a_name##_tsd_get(void) \ +a_name##tsd_get(void) \ { \ \ - assert(a_name##_booted); \ - return (&a_name##_tls); \ + assert(a_name##tsd_booted); \ + return (&a_name##tsd_tls); \ } \ a_attr void \ -a_name##_tsd_set(a_type *val) \ +a_name##tsd_set(a_type *val) \ { \ \ - assert(a_name##_booted); \ - a_name##_tls = (*val); \ + assert(a_name##tsd_booted); \ + a_name##tsd_tls = (*val); \ if (a_cleanup != malloc_tsd_no_cleanup) { \ - if (pthread_setspecific(a_name##_tsd, \ - (void *)(&a_name##_tls))) { \ + if (pthread_setspecific(a_name##tsd_tsd, \ + (void *)(&a_name##tsd_tls))) { \ malloc_write(": Error" \ " setting TSD for "#a_name"\n"); \ if (opt_abort) \ @@ -204,27 +273,21 @@ a_name##_tsd_set(a_type *val) \ #elif (defined(_WIN32)) #define malloc_tsd_funcs(a_attr, a_name, a_type, a_initializer, \ a_cleanup) \ -/* Data structure. */ \ -typedef struct { \ - bool initialized; \ - a_type val; \ -} a_name##_tsd_wrapper_t; \ /* Initialization/cleanup. */ \ a_attr bool \ -a_name##_tsd_cleanup_wrapper(void) \ +a_name##tsd_cleanup_wrapper(void) \ { \ - a_name##_tsd_wrapper_t *wrapper; \ + DWORD error = GetLastError(); \ + a_name##tsd_wrapper_t *wrapper = (a_name##tsd_wrapper_t *) \ + TlsGetValue(a_name##tsd_tsd); \ + SetLastError(error); \ \ - wrapper = (a_name##_tsd_wrapper_t *) TlsGetValue(a_name##_tsd); \ if (wrapper == NULL) \ return (false); \ if (a_cleanup != malloc_tsd_no_cleanup && \ wrapper->initialized) { \ - a_type val = wrapper->val; \ - a_type tsd_static_data = a_initializer; \ wrapper->initialized = false; \ - wrapper->val = tsd_static_data; \ - a_cleanup(&val); \ + a_cleanup(&wrapper->val); \ if (wrapper->initialized) { \ /* Trigger another cleanup round. */ \ return (true); \ @@ -233,63 +296,95 @@ a_name##_tsd_cleanup_wrapper(void) \ malloc_tsd_dalloc(wrapper); \ return (false); \ } \ -a_attr bool \ -a_name##_tsd_boot(void) \ +a_attr void \ +a_name##tsd_wrapper_set(a_name##tsd_wrapper_t *wrapper) \ { \ \ - a_name##_tsd = TlsAlloc(); \ - if (a_name##_tsd == TLS_OUT_OF_INDEXES) \ - return (true); \ - if (a_cleanup != malloc_tsd_no_cleanup) { \ - malloc_tsd_cleanup_register( \ - &a_name##_tsd_cleanup_wrapper); \ + if (!TlsSetValue(a_name##tsd_tsd, (void *)wrapper)) { \ + malloc_write(": Error setting" \ + " TSD for "#a_name"\n"); \ + abort(); \ } \ - a_name##_booted = true; \ - return (false); \ } \ -/* Get/set. */ \ -a_attr a_name##_tsd_wrapper_t * \ -a_name##_tsd_get_wrapper(void) \ +a_attr a_name##tsd_wrapper_t * \ +a_name##tsd_wrapper_get(void) \ { \ - a_name##_tsd_wrapper_t *wrapper = (a_name##_tsd_wrapper_t *) \ - TlsGetValue(a_name##_tsd); \ + DWORD error = GetLastError(); \ + a_name##tsd_wrapper_t *wrapper = (a_name##tsd_wrapper_t *) \ + TlsGetValue(a_name##tsd_tsd); \ + SetLastError(error); \ \ - if (wrapper == NULL) { \ - wrapper = (a_name##_tsd_wrapper_t *) \ - malloc_tsd_malloc(sizeof(a_name##_tsd_wrapper_t)); \ + if (unlikely(wrapper == NULL)) { \ + wrapper = (a_name##tsd_wrapper_t *) \ + malloc_tsd_malloc(sizeof(a_name##tsd_wrapper_t)); \ if (wrapper == NULL) { \ malloc_write(": Error allocating" \ " TSD for "#a_name"\n"); \ abort(); \ } else { \ - static a_type tsd_static_data = a_initializer; \ wrapper->initialized = false; \ - wrapper->val = tsd_static_data; \ - } \ - if (!TlsSetValue(a_name##_tsd, (void *)wrapper)) { \ - malloc_write(": Error setting" \ - " TSD for "#a_name"\n"); \ - abort(); \ + wrapper->val = a_initializer; \ } \ + a_name##tsd_wrapper_set(wrapper); \ } \ return (wrapper); \ } \ -a_attr a_type * \ -a_name##_tsd_get(void) \ +a_attr bool \ +a_name##tsd_boot0(void) \ { \ - a_name##_tsd_wrapper_t *wrapper; \ \ - assert(a_name##_booted); \ - wrapper = a_name##_tsd_get_wrapper(); \ + a_name##tsd_tsd = TlsAlloc(); \ + if (a_name##tsd_tsd == TLS_OUT_OF_INDEXES) \ + return (true); \ + if (a_cleanup != malloc_tsd_no_cleanup) { \ + malloc_tsd_cleanup_register( \ + &a_name##tsd_cleanup_wrapper); \ + } \ + a_name##tsd_wrapper_set(&a_name##tsd_boot_wrapper); \ + a_name##tsd_booted = true; \ + return (false); \ +} \ +a_attr void \ +a_name##tsd_boot1() \ +{ \ + a_name##tsd_wrapper_t *wrapper; \ + wrapper = (a_name##tsd_wrapper_t *) \ + malloc_tsd_malloc(sizeof(a_name##tsd_wrapper_t)); \ + if (wrapper == NULL) { \ + malloc_write(": Error allocating" \ + " TSD for "#a_name"\n"); \ + abort(); \ + } \ + memcpy(wrapper, &a_name##tsd_boot_wrapper, \ + sizeof(a_name##tsd_wrapper_t)); \ + a_name##tsd_wrapper_set(wrapper); \ +} \ +a_attr bool \ +a_name##tsd_boot(void) \ +{ \ + \ + if (a_name##tsd_boot0()) \ + return (true); \ + a_name##tsd_boot1(); \ + return (false); \ +} \ +/* Get/set. */ \ +a_attr a_type * \ +a_name##tsd_get(void) \ +{ \ + a_name##tsd_wrapper_t *wrapper; \ + \ + assert(a_name##tsd_booted); \ + wrapper = a_name##tsd_wrapper_get(); \ return (&wrapper->val); \ } \ a_attr void \ -a_name##_tsd_set(a_type *val) \ +a_name##tsd_set(a_type *val) \ { \ - a_name##_tsd_wrapper_t *wrapper; \ + a_name##tsd_wrapper_t *wrapper; \ \ - assert(a_name##_booted); \ - wrapper = a_name##_tsd_get_wrapper(); \ + assert(a_name##tsd_booted); \ + wrapper = a_name##tsd_wrapper_get(); \ wrapper->val = *(val); \ if (a_cleanup != malloc_tsd_no_cleanup) \ wrapper->initialized = true; \ @@ -297,16 +392,11 @@ a_name##_tsd_set(a_type *val) \ #else #define malloc_tsd_funcs(a_attr, a_name, a_type, a_initializer, \ a_cleanup) \ -/* Data structure. */ \ -typedef struct { \ - bool initialized; \ - a_type val; \ -} a_name##_tsd_wrapper_t; \ /* Initialization/cleanup. */ \ a_attr void \ -a_name##_tsd_cleanup_wrapper(void *arg) \ +a_name##tsd_cleanup_wrapper(void *arg) \ { \ - a_name##_tsd_wrapper_t *wrapper = (a_name##_tsd_wrapper_t *)arg;\ + a_name##tsd_wrapper_t *wrapper = (a_name##tsd_wrapper_t *)arg; \ \ if (a_cleanup != malloc_tsd_no_cleanup && \ wrapper->initialized) { \ @@ -314,7 +404,7 @@ a_name##_tsd_cleanup_wrapper(void *arg) \ a_cleanup(&wrapper->val); \ if (wrapper->initialized) { \ /* Trigger another cleanup round. */ \ - if (pthread_setspecific(a_name##_tsd, \ + if (pthread_setspecific(a_name##tsd_tsd, \ (void *)wrapper)) { \ malloc_write(": Error" \ " setting TSD for "#a_name"\n"); \ @@ -326,67 +416,97 @@ a_name##_tsd_cleanup_wrapper(void *arg) \ } \ malloc_tsd_dalloc(wrapper); \ } \ -a_attr bool \ -a_name##_tsd_boot(void) \ +a_attr void \ +a_name##tsd_wrapper_set(a_name##tsd_wrapper_t *wrapper) \ { \ \ - if (pthread_key_create(&a_name##_tsd, \ - a_name##_tsd_cleanup_wrapper) != 0) \ - return (true); \ - a_name##_booted = true; \ - return (false); \ + if (pthread_setspecific(a_name##tsd_tsd, \ + (void *)wrapper)) { \ + malloc_write(": Error setting" \ + " TSD for "#a_name"\n"); \ + abort(); \ + } \ } \ -/* Get/set. */ \ -a_attr a_name##_tsd_wrapper_t * \ -a_name##_tsd_get_wrapper(void) \ +a_attr a_name##tsd_wrapper_t * \ +a_name##tsd_wrapper_get(void) \ { \ - a_name##_tsd_wrapper_t *wrapper = (a_name##_tsd_wrapper_t *) \ - pthread_getspecific(a_name##_tsd); \ + a_name##tsd_wrapper_t *wrapper = (a_name##tsd_wrapper_t *) \ + pthread_getspecific(a_name##tsd_tsd); \ \ - if (wrapper == NULL) { \ + if (unlikely(wrapper == NULL)) { \ tsd_init_block_t block; \ wrapper = tsd_init_check_recursion( \ - &a_name##_tsd_init_head, &block); \ + &a_name##tsd_init_head, &block); \ if (wrapper) \ return (wrapper); \ - wrapper = (a_name##_tsd_wrapper_t *) \ - malloc_tsd_malloc(sizeof(a_name##_tsd_wrapper_t)); \ + wrapper = (a_name##tsd_wrapper_t *) \ + malloc_tsd_malloc(sizeof(a_name##tsd_wrapper_t)); \ block.data = wrapper; \ if (wrapper == NULL) { \ malloc_write(": Error allocating" \ " TSD for "#a_name"\n"); \ abort(); \ } else { \ - static a_type tsd_static_data = a_initializer; \ wrapper->initialized = false; \ - wrapper->val = tsd_static_data; \ + wrapper->val = a_initializer; \ } \ - if (pthread_setspecific(a_name##_tsd, \ - (void *)wrapper)) { \ - malloc_write(": Error setting" \ - " TSD for "#a_name"\n"); \ - abort(); \ - } \ - tsd_init_finish(&a_name##_tsd_init_head, &block); \ + a_name##tsd_wrapper_set(wrapper); \ + tsd_init_finish(&a_name##tsd_init_head, &block); \ } \ return (wrapper); \ } \ -a_attr a_type * \ -a_name##_tsd_get(void) \ +a_attr bool \ +a_name##tsd_boot0(void) \ { \ - a_name##_tsd_wrapper_t *wrapper; \ \ - assert(a_name##_booted); \ - wrapper = a_name##_tsd_get_wrapper(); \ + if (pthread_key_create(&a_name##tsd_tsd, \ + a_name##tsd_cleanup_wrapper) != 0) \ + return (true); \ + a_name##tsd_wrapper_set(&a_name##tsd_boot_wrapper); \ + a_name##tsd_booted = true; \ + return (false); \ +} \ +a_attr void \ +a_name##tsd_boot1() \ +{ \ + a_name##tsd_wrapper_t *wrapper; \ + wrapper = (a_name##tsd_wrapper_t *) \ + malloc_tsd_malloc(sizeof(a_name##tsd_wrapper_t)); \ + if (wrapper == NULL) { \ + malloc_write(": Error allocating" \ + " TSD for "#a_name"\n"); \ + abort(); \ + } \ + memcpy(wrapper, &a_name##tsd_boot_wrapper, \ + sizeof(a_name##tsd_wrapper_t)); \ + a_name##tsd_wrapper_set(wrapper); \ +} \ +a_attr bool \ +a_name##tsd_boot(void) \ +{ \ + \ + if (a_name##tsd_boot0()) \ + return (true); \ + a_name##tsd_boot1(); \ + return (false); \ +} \ +/* Get/set. */ \ +a_attr a_type * \ +a_name##tsd_get(void) \ +{ \ + a_name##tsd_wrapper_t *wrapper; \ + \ + assert(a_name##tsd_booted); \ + wrapper = a_name##tsd_wrapper_get(); \ return (&wrapper->val); \ } \ a_attr void \ -a_name##_tsd_set(a_type *val) \ +a_name##tsd_set(a_type *val) \ { \ - a_name##_tsd_wrapper_t *wrapper; \ + a_name##tsd_wrapper_t *wrapper; \ \ - assert(a_name##_booted); \ - wrapper = a_name##_tsd_get_wrapper(); \ + assert(a_name##tsd_booted); \ + wrapper = a_name##tsd_wrapper_get(); \ wrapper->val = *(val); \ if (a_cleanup != malloc_tsd_no_cleanup) \ wrapper->initialized = true; \ @@ -410,25 +530,136 @@ struct tsd_init_head_s { }; #endif +#define MALLOC_TSD \ +/* O(name, type) */ \ + O(tcache, tcache_t *) \ + O(thread_allocated, uint64_t) \ + O(thread_deallocated, uint64_t) \ + O(prof_tdata, prof_tdata_t *) \ + O(arena, arena_t *) \ + O(arenas_cache, arena_t **) \ + O(narenas_cache, unsigned) \ + O(arenas_cache_bypass, bool) \ + O(tcache_enabled, tcache_enabled_t) \ + O(quarantine, quarantine_t *) \ + +#define TSD_INITIALIZER { \ + tsd_state_uninitialized, \ + NULL, \ + 0, \ + 0, \ + NULL, \ + NULL, \ + NULL, \ + 0, \ + false, \ + tcache_enabled_default, \ + NULL \ +} + +struct tsd_s { + tsd_state_t state; +#define O(n, t) \ + t n; +MALLOC_TSD +#undef O +}; + +static const tsd_t tsd_initializer = TSD_INITIALIZER; + +malloc_tsd_types(, tsd_t) + #endif /* JEMALLOC_H_STRUCTS */ /******************************************************************************/ #ifdef JEMALLOC_H_EXTERNS void *malloc_tsd_malloc(size_t size); void malloc_tsd_dalloc(void *wrapper); -void malloc_tsd_no_cleanup(void *); +void malloc_tsd_no_cleanup(void *arg); void malloc_tsd_cleanup_register(bool (*f)(void)); -void malloc_tsd_boot(void); +bool malloc_tsd_boot0(void); +void malloc_tsd_boot1(void); #if (!defined(JEMALLOC_MALLOC_THREAD_CLEANUP) && !defined(JEMALLOC_TLS) && \ !defined(_WIN32)) void *tsd_init_check_recursion(tsd_init_head_t *head, tsd_init_block_t *block); void tsd_init_finish(tsd_init_head_t *head, tsd_init_block_t *block); #endif +void tsd_cleanup(void *arg); #endif /* JEMALLOC_H_EXTERNS */ /******************************************************************************/ #ifdef JEMALLOC_H_INLINES +#ifndef JEMALLOC_ENABLE_INLINE +malloc_tsd_protos(JEMALLOC_ATTR(unused), , tsd_t) + +tsd_t *tsd_fetch(void); +bool tsd_nominal(tsd_t *tsd); +#define O(n, t) \ +t *tsd_##n##p_get(tsd_t *tsd); \ +t tsd_##n##_get(tsd_t *tsd); \ +void tsd_##n##_set(tsd_t *tsd, t n); +MALLOC_TSD +#undef O +#endif + +#if (defined(JEMALLOC_ENABLE_INLINE) || defined(JEMALLOC_TSD_C_)) +malloc_tsd_externs(, tsd_t) +malloc_tsd_funcs(JEMALLOC_ALWAYS_INLINE, , tsd_t, tsd_initializer, tsd_cleanup) + +JEMALLOC_ALWAYS_INLINE tsd_t * +tsd_fetch(void) +{ + tsd_t *tsd = tsd_get(); + + if (unlikely(tsd->state != tsd_state_nominal)) { + if (tsd->state == tsd_state_uninitialized) { + tsd->state = tsd_state_nominal; + /* Trigger cleanup handler registration. */ + tsd_set(tsd); + } else if (tsd->state == tsd_state_purgatory) { + tsd->state = tsd_state_reincarnated; + tsd_set(tsd); + } else + assert(tsd->state == tsd_state_reincarnated); + } + + return (tsd); +} + +JEMALLOC_INLINE bool +tsd_nominal(tsd_t *tsd) +{ + + return (tsd->state == tsd_state_nominal); +} + +#define O(n, t) \ +JEMALLOC_ALWAYS_INLINE t * \ +tsd_##n##p_get(tsd_t *tsd) \ +{ \ + \ + return (&tsd->n); \ +} \ + \ +JEMALLOC_ALWAYS_INLINE t \ +tsd_##n##_get(tsd_t *tsd) \ +{ \ + \ + return (*tsd_##n##p_get(tsd)); \ +} \ + \ +JEMALLOC_ALWAYS_INLINE void \ +tsd_##n##_set(tsd_t *tsd, t n) \ +{ \ + \ + assert(tsd->state == tsd_state_nominal); \ + tsd->n = n; \ +} +MALLOC_TSD +#undef O +#endif + #endif /* JEMALLOC_H_INLINES */ /******************************************************************************/ diff --git a/contrib/jemalloc/include/jemalloc/internal/util.h b/contrib/jemalloc/include/jemalloc/internal/util.h index 6b938f746889..b2ea740fdc76 100644 --- a/contrib/jemalloc/include/jemalloc/internal/util.h +++ b/contrib/jemalloc/include/jemalloc/internal/util.h @@ -1,6 +1,36 @@ /******************************************************************************/ #ifdef JEMALLOC_H_TYPES +#ifdef _WIN32 +# ifdef _WIN64 +# define FMT64_PREFIX "ll" +# define FMTPTR_PREFIX "ll" +# else +# define FMT64_PREFIX "ll" +# define FMTPTR_PREFIX "" +# endif +# define FMTd32 "d" +# define FMTu32 "u" +# define FMTx32 "x" +# define FMTd64 FMT64_PREFIX "d" +# define FMTu64 FMT64_PREFIX "u" +# define FMTx64 FMT64_PREFIX "x" +# define FMTdPTR FMTPTR_PREFIX "d" +# define FMTuPTR FMTPTR_PREFIX "u" +# define FMTxPTR FMTPTR_PREFIX "x" +#else +# include +# define FMTd32 PRId32 +# define FMTu32 PRIu32 +# define FMTx32 PRIx32 +# define FMTd64 PRId64 +# define FMTu64 PRIu64 +# define FMTx64 PRIx64 +# define FMTdPTR PRIdPTR +# define FMTuPTR PRIuPTR +# define FMTxPTR PRIxPTR +#endif + /* Size of stack-allocated buffer passed to buferror(). */ #define BUFERROR_BUF 64 @@ -22,9 +52,33 @@ * uninitialized. */ #ifdef JEMALLOC_CC_SILENCE -# define JEMALLOC_CC_SILENCE_INIT(v) = v +# define JEMALLOC_CC_SILENCE_INIT(v) = v #else -# define JEMALLOC_CC_SILENCE_INIT(v) +# define JEMALLOC_CC_SILENCE_INIT(v) +#endif + +#define JEMALLOC_GNUC_PREREQ(major, minor) \ + (!defined(__clang__) && \ + (__GNUC__ > (major) || (__GNUC__ == (major) && __GNUC_MINOR__ >= (minor)))) +#ifndef __has_builtin +# define __has_builtin(builtin) (0) +#endif +#define JEMALLOC_CLANG_HAS_BUILTIN(builtin) \ + (defined(__clang__) && __has_builtin(builtin)) + +#ifdef __GNUC__ +# define likely(x) __builtin_expect(!!(x), 1) +# define unlikely(x) __builtin_expect(!!(x), 0) +# if JEMALLOC_GNUC_PREREQ(4, 6) || \ + JEMALLOC_CLANG_HAS_BUILTIN(__builtin_unreachable) +# define unreachable() __builtin_unreachable() +# else +# define unreachable() +# endif +#else +# define likely(x) !!(x) +# define unlikely(x) !!(x) +# define unreachable() #endif /* @@ -33,7 +87,7 @@ */ #ifndef assert #define assert(e) do { \ - if (config_debug && !(e)) { \ + if (unlikely(config_debug && !(e))) { \ malloc_printf( \ ": %s:%d: Failed assertion: \"%s\"\n", \ __FILE__, __LINE__, #e); \ @@ -50,6 +104,7 @@ __FILE__, __LINE__); \ abort(); \ } \ + unreachable(); \ } while (0) #endif @@ -65,14 +120,14 @@ #ifndef assert_not_implemented #define assert_not_implemented(e) do { \ - if (config_debug && !(e)) \ + if (unlikely(config_debug && !(e))) \ not_implemented(); \ } while (0) #endif /* Use to assert a particular configuration, e.g., cassert(config_debug). */ #define cassert(c) do { \ - if ((c) == false) \ + if (unlikely(!(c))) \ not_reached(); \ } while (0) @@ -96,25 +151,47 @@ void malloc_write(const char *s); int malloc_vsnprintf(char *str, size_t size, const char *format, va_list ap); int malloc_snprintf(char *str, size_t size, const char *format, ...) - JEMALLOC_ATTR(format(printf, 3, 4)); + JEMALLOC_FORMAT_PRINTF(3, 4); void malloc_vcprintf(void (*write_cb)(void *, const char *), void *cbopaque, const char *format, va_list ap); void malloc_cprintf(void (*write)(void *, const char *), void *cbopaque, - const char *format, ...) JEMALLOC_ATTR(format(printf, 3, 4)); -void malloc_printf(const char *format, ...) - JEMALLOC_ATTR(format(printf, 1, 2)); + const char *format, ...) JEMALLOC_FORMAT_PRINTF(3, 4); +void malloc_printf(const char *format, ...) JEMALLOC_FORMAT_PRINTF(1, 2); #endif /* JEMALLOC_H_EXTERNS */ /******************************************************************************/ #ifdef JEMALLOC_H_INLINES #ifndef JEMALLOC_ENABLE_INLINE +int jemalloc_ffsl(long bitmap); +int jemalloc_ffs(int bitmap); size_t pow2_ceil(size_t x); +size_t lg_floor(size_t x); void set_errno(int errnum); int get_errno(void); #endif #if (defined(JEMALLOC_ENABLE_INLINE) || defined(JEMALLOC_UTIL_C_)) + +/* Sanity check. */ +#if !defined(JEMALLOC_INTERNAL_FFSL) || !defined(JEMALLOC_INTERNAL_FFS) +# error Both JEMALLOC_INTERNAL_FFSL && JEMALLOC_INTERNAL_FFS should have been defined by configure +#endif + +JEMALLOC_ALWAYS_INLINE int +jemalloc_ffsl(long bitmap) +{ + + return (JEMALLOC_INTERNAL_FFSL(bitmap)); +} + +JEMALLOC_ALWAYS_INLINE int +jemalloc_ffs(int bitmap) +{ + + return (JEMALLOC_INTERNAL_FFS(bitmap)); +} + /* Compute the smallest power of 2 that is >= x. */ JEMALLOC_INLINE size_t pow2_ceil(size_t x) @@ -133,7 +210,82 @@ pow2_ceil(size_t x) return (x); } -/* Sets error code */ +#if (defined(__i386__) || defined(__amd64__) || defined(__x86_64__)) +JEMALLOC_INLINE size_t +lg_floor(size_t x) +{ + size_t ret; + + assert(x != 0); + + asm ("bsr %1, %0" + : "=r"(ret) // Outputs. + : "r"(x) // Inputs. + ); + return (ret); +} +#elif (defined(_MSC_VER)) +JEMALLOC_INLINE size_t +lg_floor(size_t x) +{ + unsigned long ret; + + assert(x != 0); + +#if (LG_SIZEOF_PTR == 3) + _BitScanReverse64(&ret, x); +#elif (LG_SIZEOF_PTR == 2) + _BitScanReverse(&ret, x); +#else +# error "Unsupported type sizes for lg_floor()" +#endif + return (ret); +} +#elif (defined(JEMALLOC_HAVE_BUILTIN_CLZ)) +JEMALLOC_INLINE size_t +lg_floor(size_t x) +{ + + assert(x != 0); + +#if (LG_SIZEOF_PTR == LG_SIZEOF_INT) + return (((8 << LG_SIZEOF_PTR) - 1) - __builtin_clz(x)); +#elif (LG_SIZEOF_PTR == LG_SIZEOF_LONG) + return (((8 << LG_SIZEOF_PTR) - 1) - __builtin_clzl(x)); +#else +# error "Unsupported type sizes for lg_floor()" +#endif +} +#else +JEMALLOC_INLINE size_t +lg_floor(size_t x) +{ + + assert(x != 0); + + x |= (x >> 1); + x |= (x >> 2); + x |= (x >> 4); + x |= (x >> 8); + x |= (x >> 16); +#if (LG_SIZEOF_PTR == 3 && LG_SIZEOF_PTR == LG_SIZEOF_LONG) + x |= (x >> 32); + if (x == KZU(0xffffffffffffffff)) + return (63); + x++; + return (jemalloc_ffsl(x) - 2); +#elif (LG_SIZEOF_PTR == 2) + if (x == KZU(0xffffffff)) + return (31); + x++; + return (jemalloc_ffs(x) - 2); +#else +# error "Unsupported type sizes for lg_floor()" +#endif +} +#endif + +/* Set error code. */ JEMALLOC_INLINE void set_errno(int errnum) { @@ -145,7 +297,7 @@ set_errno(int errnum) #endif } -/* Get last error code */ +/* Get last error code. */ JEMALLOC_INLINE int get_errno(void) { diff --git a/contrib/jemalloc/include/jemalloc/internal/valgrind.h b/contrib/jemalloc/include/jemalloc/internal/valgrind.h new file mode 100644 index 000000000000..a3380df9265d --- /dev/null +++ b/contrib/jemalloc/include/jemalloc/internal/valgrind.h @@ -0,0 +1,112 @@ +/******************************************************************************/ +#ifdef JEMALLOC_H_TYPES + +#ifdef JEMALLOC_VALGRIND +#include + +/* + * The size that is reported to Valgrind must be consistent through a chain of + * malloc..realloc..realloc calls. Request size isn't recorded anywhere in + * jemalloc, so it is critical that all callers of these macros provide usize + * rather than request size. As a result, buffer overflow detection is + * technically weakened for the standard API, though it is generally accepted + * practice to consider any extra bytes reported by malloc_usable_size() as + * usable space. + */ +#define JEMALLOC_VALGRIND_MAKE_MEM_NOACCESS(ptr, usize) do { \ + if (unlikely(in_valgrind)) \ + valgrind_make_mem_noaccess(ptr, usize); \ +} while (0) +#define JEMALLOC_VALGRIND_MAKE_MEM_UNDEFINED(ptr, usize) do { \ + if (unlikely(in_valgrind)) \ + valgrind_make_mem_undefined(ptr, usize); \ +} while (0) +#define JEMALLOC_VALGRIND_MAKE_MEM_DEFINED(ptr, usize) do { \ + if (unlikely(in_valgrind)) \ + valgrind_make_mem_defined(ptr, usize); \ +} while (0) +/* + * The VALGRIND_MALLOCLIKE_BLOCK() and VALGRIND_RESIZEINPLACE_BLOCK() macro + * calls must be embedded in macros rather than in functions so that when + * Valgrind reports errors, there are no extra stack frames in the backtraces. + */ +#define JEMALLOC_VALGRIND_MALLOC(cond, ptr, usize, zero) do { \ + if (unlikely(in_valgrind && cond)) \ + VALGRIND_MALLOCLIKE_BLOCK(ptr, usize, p2rz(ptr), zero); \ +} while (0) +#define JEMALLOC_VALGRIND_REALLOC(maybe_moved, ptr, usize, \ + ptr_maybe_null, old_ptr, old_usize, old_rzsize, old_ptr_maybe_null, \ + zero) do { \ + if (unlikely(in_valgrind)) { \ + size_t rzsize = p2rz(ptr); \ + \ + if (!maybe_moved || ptr == old_ptr) { \ + VALGRIND_RESIZEINPLACE_BLOCK(ptr, old_usize, \ + usize, rzsize); \ + if (zero && old_usize < usize) { \ + valgrind_make_mem_defined( \ + (void *)((uintptr_t)ptr + \ + old_usize), usize - old_usize); \ + } \ + } else { \ + if (!old_ptr_maybe_null || old_ptr != NULL) { \ + valgrind_freelike_block(old_ptr, \ + old_rzsize); \ + } \ + if (!ptr_maybe_null || ptr != NULL) { \ + size_t copy_size = (old_usize < usize) \ + ? old_usize : usize; \ + size_t tail_size = usize - copy_size; \ + VALGRIND_MALLOCLIKE_BLOCK(ptr, usize, \ + rzsize, false); \ + if (copy_size > 0) { \ + valgrind_make_mem_defined(ptr, \ + copy_size); \ + } \ + if (zero && tail_size > 0) { \ + valgrind_make_mem_defined( \ + (void *)((uintptr_t)ptr + \ + copy_size), tail_size); \ + } \ + } \ + } \ + } \ +} while (0) +#define JEMALLOC_VALGRIND_FREE(ptr, rzsize) do { \ + if (unlikely(in_valgrind)) \ + valgrind_freelike_block(ptr, rzsize); \ +} while (0) +#else +#define RUNNING_ON_VALGRIND ((unsigned)0) +#define JEMALLOC_VALGRIND_MAKE_MEM_NOACCESS(ptr, usize) do {} while (0) +#define JEMALLOC_VALGRIND_MAKE_MEM_UNDEFINED(ptr, usize) do {} while (0) +#define JEMALLOC_VALGRIND_MAKE_MEM_DEFINED(ptr, usize) do {} while (0) +#define JEMALLOC_VALGRIND_MALLOC(cond, ptr, usize, zero) do {} while (0) +#define JEMALLOC_VALGRIND_REALLOC(maybe_moved, ptr, usize, \ + ptr_maybe_null, old_ptr, old_usize, old_rzsize, old_ptr_maybe_null, \ + zero) do {} while (0) +#define JEMALLOC_VALGRIND_FREE(ptr, rzsize) do {} while (0) +#endif + +#endif /* JEMALLOC_H_TYPES */ +/******************************************************************************/ +#ifdef JEMALLOC_H_STRUCTS + +#endif /* JEMALLOC_H_STRUCTS */ +/******************************************************************************/ +#ifdef JEMALLOC_H_EXTERNS + +#ifdef JEMALLOC_VALGRIND +void valgrind_make_mem_noaccess(void *ptr, size_t usize); +void valgrind_make_mem_undefined(void *ptr, size_t usize); +void valgrind_make_mem_defined(void *ptr, size_t usize); +void valgrind_freelike_block(void *ptr, size_t usize); +#endif + +#endif /* JEMALLOC_H_EXTERNS */ +/******************************************************************************/ +#ifdef JEMALLOC_H_INLINES + +#endif /* JEMALLOC_H_INLINES */ +/******************************************************************************/ + diff --git a/contrib/jemalloc/include/jemalloc/jemalloc.h b/contrib/jemalloc/include/jemalloc/jemalloc.h index 3dcccc3a5054..cd8f7da8bfe4 100644 --- a/contrib/jemalloc/include/jemalloc/jemalloc.h +++ b/contrib/jemalloc/include/jemalloc/jemalloc.h @@ -7,8 +7,14 @@ extern "C" { /* Defined if __attribute__((...)) syntax is supported. */ #define JEMALLOC_HAVE_ATTR -/* Support the experimental API. */ -#define JEMALLOC_EXPERIMENTAL +/* Defined if alloc_size attribute is supported. */ +/* #undef JEMALLOC_HAVE_ATTR_ALLOC_SIZE */ + +/* Defined if format(gnu_printf, ...) attribute is supported. */ +/* #undef JEMALLOC_HAVE_ATTR_FORMAT_GNU_PRINTF */ + +/* Defined if format(printf, ...) attribute is supported. */ +#define JEMALLOC_HAVE_ATTR_FORMAT_PRINTF /* * Define overrides for non-standard allocator-related functions if they are @@ -26,6 +32,13 @@ extern "C" { */ #define JEMALLOC_USABLE_SIZE_CONST const +/* + * If defined, specify throw() for the public function prototypes when compiling + * with C++. The only justification for this is to match the prototypes that + * glibc defines. + */ +/* #undef JEMALLOC_USE_CXX_THROW */ + /* sizeof(void *) == 2^LG_SIZEOF_PTR. */ #define LG_SIZEOF_PTR 3 @@ -48,6 +61,7 @@ extern "C" { # define je_xallocx xallocx # define je_sallocx sallocx # define je_dallocx dallocx +# define je_sdallocx sdallocx # define je_nallocx nallocx # define je_mallctl mallctl # define je_mallctlnametomib mallctlnametomib @@ -55,24 +69,22 @@ extern "C" { # define je_malloc_stats_print malloc_stats_print # define je_malloc_usable_size malloc_usable_size # define je_valloc valloc -# define je_allocm allocm -# define je_dallocm dallocm -# define je_nallocm nallocm -# define je_rallocm rallocm -# define je_sallocm sallocm #endif #include "jemalloc_FreeBSD.h" +#include +#include +#include #include #include -#define JEMALLOC_VERSION "3.6.0-0-g46c0af68bd248b04df75e4f92d5fb804c3d75340" -#define JEMALLOC_VERSION_MAJOR 3 -#define JEMALLOC_VERSION_MINOR 6 +#define JEMALLOC_VERSION "4.0.0-0-g6e98caf8f064482b9ab292ef3638dea67420bbc2" +#define JEMALLOC_VERSION_MAJOR 4 +#define JEMALLOC_VERSION_MINOR 0 #define JEMALLOC_VERSION_BUGFIX 0 #define JEMALLOC_VERSION_NREV 0 -#define JEMALLOC_VERSION_GID "46c0af68bd248b04df75e4f92d5fb804c3d75340" +#define JEMALLOC_VERSION_GID "6e98caf8f064482b9ab292ef3638dea67420bbc2" # define MALLOCX_LG_ALIGN(la) (la) # if LG_SIZEOF_PTR == 2 @@ -82,48 +94,86 @@ extern "C" { ((a < (size_t)INT_MAX) ? ffs(a)-1 : ffs(a>>32)+31) # endif # define MALLOCX_ZERO ((int)0x40) -/* Bias arena index bits so that 0 encodes "MALLOCX_ARENA() unspecified". */ -# define MALLOCX_ARENA(a) ((int)(((a)+1) << 8)) +/* + * Bias tcache index bits so that 0 encodes "automatic tcache management", and 1 + * encodes MALLOCX_TCACHE_NONE. + */ +# define MALLOCX_TCACHE(tc) ((int)(((tc)+2) << 8)) +# define MALLOCX_TCACHE_NONE MALLOCX_TCACHE(-1) +/* + * Bias arena index bits so that 0 encodes "use an automatically chosen arena". + */ +# define MALLOCX_ARENA(a) ((int)(((a)+1) << 20)) -#ifdef JEMALLOC_EXPERIMENTAL -# define ALLOCM_LG_ALIGN(la) (la) -# if LG_SIZEOF_PTR == 2 -# define ALLOCM_ALIGN(a) (ffs(a)-1) -# else -# define ALLOCM_ALIGN(a) \ - ((a < (size_t)INT_MAX) ? ffs(a)-1 : ffs(a>>32)+31) -# 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 -# define ALLOCM_ERR_NOT_MOVED 2 +#if defined(__cplusplus) && defined(JEMALLOC_USE_CXX_THROW) +# define JEMALLOC_CXX_THROW throw() +#else +# define JEMALLOC_CXX_THROW #endif #ifdef JEMALLOC_HAVE_ATTR # define JEMALLOC_ATTR(s) __attribute__((s)) -# define JEMALLOC_EXPORT JEMALLOC_ATTR(visibility("default")) # define JEMALLOC_ALIGNED(s) JEMALLOC_ATTR(aligned(s)) -# define JEMALLOC_SECTION(s) JEMALLOC_ATTR(section(s)) +# ifdef JEMALLOC_HAVE_ATTR_ALLOC_SIZE +# define JEMALLOC_ALLOC_SIZE(s) JEMALLOC_ATTR(alloc_size(s)) +# define JEMALLOC_ALLOC_SIZE2(s1, s2) JEMALLOC_ATTR(alloc_size(s1, s2)) +# else +# define JEMALLOC_ALLOC_SIZE(s) +# define JEMALLOC_ALLOC_SIZE2(s1, s2) +# endif +# ifndef JEMALLOC_EXPORT +# define JEMALLOC_EXPORT JEMALLOC_ATTR(visibility("default")) +# endif +# ifdef JEMALLOC_HAVE_ATTR_FORMAT_GNU_PRINTF +# define JEMALLOC_FORMAT_PRINTF(s, i) JEMALLOC_ATTR(format(gnu_printf, s, i)) +# elif defined(JEMALLOC_HAVE_ATTR_FORMAT_PRINTF) +# define JEMALLOC_FORMAT_PRINTF(s, i) JEMALLOC_ATTR(format(printf, s, i)) +# else +# define JEMALLOC_FORMAT_PRINTF(s, i) +# endif # define JEMALLOC_NOINLINE JEMALLOC_ATTR(noinline) +# define JEMALLOC_NOTHROW JEMALLOC_ATTR(nothrow) +# define JEMALLOC_SECTION(s) JEMALLOC_ATTR(section(s)) +# define JEMALLOC_RESTRICT_RETURN +# define JEMALLOC_ALLOCATOR #elif _MSC_VER # define JEMALLOC_ATTR(s) -# ifdef DLLEXPORT -# define JEMALLOC_EXPORT __declspec(dllexport) -# else -# define JEMALLOC_EXPORT __declspec(dllimport) -# endif # define JEMALLOC_ALIGNED(s) __declspec(align(s)) -# define JEMALLOC_SECTION(s) __declspec(allocate(s)) +# define JEMALLOC_ALLOC_SIZE(s) +# define JEMALLOC_ALLOC_SIZE2(s1, s2) +# ifndef JEMALLOC_EXPORT +# ifdef DLLEXPORT +# define JEMALLOC_EXPORT __declspec(dllexport) +# else +# define JEMALLOC_EXPORT __declspec(dllimport) +# endif +# endif +# define JEMALLOC_FORMAT_PRINTF(s, i) # define JEMALLOC_NOINLINE __declspec(noinline) +# ifdef __cplusplus +# define JEMALLOC_NOTHROW __declspec(nothrow) +# else +# define JEMALLOC_NOTHROW +# endif +# define JEMALLOC_SECTION(s) __declspec(allocate(s)) +# define JEMALLOC_RESTRICT_RETURN __declspec(restrict) +# if _MSC_VER >= 1900 && !defined(__EDG__) +# define JEMALLOC_ALLOCATOR __declspec(allocator) +# else +# define JEMALLOC_ALLOCATOR +# endif #else # define JEMALLOC_ATTR(s) -# define JEMALLOC_EXPORT # define JEMALLOC_ALIGNED(s) -# define JEMALLOC_SECTION(s) +# define JEMALLOC_ALLOC_SIZE(s) +# define JEMALLOC_ALLOC_SIZE2(s1, s2) +# define JEMALLOC_EXPORT +# define JEMALLOC_FORMAT_PRINTF(s, i) # define JEMALLOC_NOINLINE +# define JEMALLOC_NOTHROW +# define JEMALLOC_SECTION(s) +# define JEMALLOC_RESTRICT_RETURN +# define JEMALLOC_ALLOCATOR #endif /* @@ -135,55 +185,121 @@ extern JEMALLOC_EXPORT const char *je_malloc_conf; extern JEMALLOC_EXPORT void (*je_malloc_message)(void *cbopaque, const char *s); -JEMALLOC_EXPORT void *je_malloc(size_t size) JEMALLOC_ATTR(malloc); -JEMALLOC_EXPORT void *je_calloc(size_t num, size_t size) - JEMALLOC_ATTR(malloc); -JEMALLOC_EXPORT int je_posix_memalign(void **memptr, size_t alignment, - size_t size) JEMALLOC_ATTR(nonnull(1)); -JEMALLOC_EXPORT void *je_aligned_alloc(size_t alignment, size_t size) - JEMALLOC_ATTR(malloc); -JEMALLOC_EXPORT void *je_realloc(void *ptr, size_t size); -JEMALLOC_EXPORT void je_free(void *ptr); +JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN + void JEMALLOC_NOTHROW *je_malloc(size_t size) + JEMALLOC_CXX_THROW JEMALLOC_ATTR(malloc) JEMALLOC_ALLOC_SIZE(1); +JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN + void JEMALLOC_NOTHROW *je_calloc(size_t num, size_t size) + JEMALLOC_CXX_THROW JEMALLOC_ATTR(malloc) JEMALLOC_ALLOC_SIZE2(1, 2); +JEMALLOC_EXPORT int JEMALLOC_NOTHROW je_posix_memalign(void **memptr, + size_t alignment, size_t size) JEMALLOC_CXX_THROW JEMALLOC_ATTR(nonnull(1)); +JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN + void JEMALLOC_NOTHROW *je_aligned_alloc(size_t alignment, + size_t size) JEMALLOC_CXX_THROW JEMALLOC_ATTR(malloc) + JEMALLOC_ALLOC_SIZE(2); +JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN + void JEMALLOC_NOTHROW *je_realloc(void *ptr, size_t size) + JEMALLOC_CXX_THROW JEMALLOC_ALLOC_SIZE(2); +JEMALLOC_EXPORT void JEMALLOC_NOTHROW je_free(void *ptr) + JEMALLOC_CXX_THROW; -JEMALLOC_EXPORT void *je_mallocx(size_t size, int flags); -JEMALLOC_EXPORT void *je_rallocx(void *ptr, size_t size, int flags); -JEMALLOC_EXPORT size_t je_xallocx(void *ptr, size_t size, size_t extra, +JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN + void JEMALLOC_NOTHROW *je_mallocx(size_t size, int flags) + JEMALLOC_ATTR(malloc) JEMALLOC_ALLOC_SIZE(1); +JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN + void JEMALLOC_NOTHROW *je_rallocx(void *ptr, size_t size, + int flags) JEMALLOC_ALLOC_SIZE(2); +JEMALLOC_EXPORT size_t JEMALLOC_NOTHROW je_xallocx(void *ptr, size_t size, + size_t extra, int flags); +JEMALLOC_EXPORT size_t JEMALLOC_NOTHROW je_sallocx(const void *ptr, + int flags) JEMALLOC_ATTR(pure); +JEMALLOC_EXPORT void JEMALLOC_NOTHROW je_dallocx(void *ptr, int flags); +JEMALLOC_EXPORT void JEMALLOC_NOTHROW je_sdallocx(void *ptr, size_t size, int flags); -JEMALLOC_EXPORT size_t je_sallocx(const void *ptr, int flags); -JEMALLOC_EXPORT void je_dallocx(void *ptr, int flags); -JEMALLOC_EXPORT size_t je_nallocx(size_t size, int flags); +JEMALLOC_EXPORT size_t JEMALLOC_NOTHROW je_nallocx(size_t size, int flags) + JEMALLOC_ATTR(pure); -JEMALLOC_EXPORT int je_mallctl(const char *name, void *oldp, - size_t *oldlenp, void *newp, size_t newlen); -JEMALLOC_EXPORT int je_mallctlnametomib(const char *name, size_t *mibp, - size_t *miblenp); -JEMALLOC_EXPORT int je_mallctlbymib(const size_t *mib, size_t miblen, +JEMALLOC_EXPORT int JEMALLOC_NOTHROW je_mallctl(const char *name, void *oldp, size_t *oldlenp, void *newp, size_t newlen); -JEMALLOC_EXPORT void je_malloc_stats_print(void (*write_cb)(void *, - const char *), void *je_cbopaque, const char *opts); -JEMALLOC_EXPORT size_t je_malloc_usable_size( - JEMALLOC_USABLE_SIZE_CONST void *ptr); +JEMALLOC_EXPORT int JEMALLOC_NOTHROW je_mallctlnametomib(const char *name, + size_t *mibp, size_t *miblenp); +JEMALLOC_EXPORT int JEMALLOC_NOTHROW je_mallctlbymib(const size_t *mib, + size_t miblen, void *oldp, size_t *oldlenp, void *newp, size_t newlen); +JEMALLOC_EXPORT void JEMALLOC_NOTHROW je_malloc_stats_print( + void (*write_cb)(void *, const char *), void *je_cbopaque, + const char *opts); +JEMALLOC_EXPORT size_t JEMALLOC_NOTHROW je_malloc_usable_size( + JEMALLOC_USABLE_SIZE_CONST void *ptr) JEMALLOC_CXX_THROW; #ifdef JEMALLOC_OVERRIDE_MEMALIGN -JEMALLOC_EXPORT void * je_memalign(size_t alignment, size_t size) +JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN + void JEMALLOC_NOTHROW *je_memalign(size_t alignment, size_t size) JEMALLOC_ATTR(malloc); #endif #ifdef JEMALLOC_OVERRIDE_VALLOC -JEMALLOC_EXPORT void * je_valloc(size_t size) JEMALLOC_ATTR(malloc); +JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN + void JEMALLOC_NOTHROW *je_valloc(size_t size) JEMALLOC_CXX_THROW + JEMALLOC_ATTR(malloc); #endif -#ifdef JEMALLOC_EXPERIMENTAL -JEMALLOC_EXPORT int je_allocm(void **ptr, size_t *rsize, size_t size, - int flags) JEMALLOC_ATTR(nonnull(1)); -JEMALLOC_EXPORT int je_rallocm(void **ptr, size_t *rsize, size_t size, - size_t extra, int flags) JEMALLOC_ATTR(nonnull(1)); -JEMALLOC_EXPORT int je_sallocm(const void *ptr, size_t *rsize, int flags) - JEMALLOC_ATTR(nonnull(1)); -JEMALLOC_EXPORT int je_dallocm(void *ptr, int flags) - JEMALLOC_ATTR(nonnull(1)); -JEMALLOC_EXPORT int je_nallocm(size_t *rsize, size_t size, int flags); -#endif +/* + * void * + * chunk_alloc(void *new_addr, size_t size, size_t alignment, bool *zero, + * bool *commit, unsigned arena_ind); + */ +typedef void *(chunk_alloc_t)(void *, size_t, size_t, bool *, bool *, unsigned); + +/* + * bool + * chunk_dalloc(void *chunk, size_t size, bool committed, unsigned arena_ind); + */ +typedef bool (chunk_dalloc_t)(void *, size_t, bool, unsigned); + +/* + * bool + * chunk_commit(void *chunk, size_t size, size_t offset, size_t length, + * unsigned arena_ind); + */ +typedef bool (chunk_commit_t)(void *, size_t, size_t, size_t, unsigned); + +/* + * bool + * chunk_decommit(void *chunk, size_t size, size_t offset, size_t length, + * unsigned arena_ind); + */ +typedef bool (chunk_decommit_t)(void *, size_t, size_t, size_t, unsigned); + +/* + * bool + * chunk_purge(void *chunk, size_t size, size_t offset, size_t length, + * unsigned arena_ind); + */ +typedef bool (chunk_purge_t)(void *, size_t, size_t, size_t, unsigned); + +/* + * bool + * chunk_split(void *chunk, size_t size, size_t size_a, size_t size_b, + * bool committed, unsigned arena_ind); + */ +typedef bool (chunk_split_t)(void *, size_t, size_t, size_t, bool, unsigned); + +/* + * bool + * chunk_merge(void *chunk_a, size_t size_a, void *chunk_b, size_t size_b, + * bool committed, unsigned arena_ind); + */ +typedef bool (chunk_merge_t)(void *, size_t, void *, size_t, bool, unsigned); + +typedef struct { + chunk_alloc_t *alloc; + chunk_dalloc_t *dalloc; + chunk_commit_t *commit; + chunk_decommit_t *decommit; + chunk_purge_t *purge; + chunk_split_t *split; + chunk_merge_t *merge; +} chunk_hooks_t; /* * By default application code must explicitly refer to mangled symbol names, @@ -209,6 +325,7 @@ JEMALLOC_EXPORT int je_nallocm(size_t *rsize, size_t size, int flags); # define xallocx je_xallocx # define sallocx je_sallocx # define dallocx je_dallocx +# define sdallocx je_sdallocx # define nallocx je_nallocx # define mallctl je_mallctl # define mallctlnametomib je_mallctlnametomib @@ -216,11 +333,6 @@ JEMALLOC_EXPORT int je_nallocm(size_t *rsize, size_t size, int flags); # define malloc_stats_print je_malloc_stats_print # define malloc_usable_size je_malloc_usable_size # define valloc je_valloc -# define allocm je_allocm -# define dallocm je_dallocm -# define nallocm je_nallocm -# define rallocm je_rallocm -# define sallocm je_sallocm #endif /* @@ -244,6 +356,7 @@ JEMALLOC_EXPORT int je_nallocm(size_t *rsize, size_t size, int flags); # undef je_xallocx # undef je_sallocx # undef je_dallocx +# undef je_sdallocx # undef je_nallocx # undef je_mallctl # undef je_mallctlnametomib @@ -251,14 +364,9 @@ JEMALLOC_EXPORT int je_nallocm(size_t *rsize, size_t size, int flags); # undef je_malloc_stats_print # undef je_malloc_usable_size # undef je_valloc -# undef je_allocm -# undef je_dallocm -# undef je_nallocm -# undef je_rallocm -# undef je_sallocm #endif #ifdef __cplusplus -}; +} #endif #endif /* JEMALLOC_H_ */ diff --git a/contrib/jemalloc/include/jemalloc/jemalloc_FreeBSD.h b/contrib/jemalloc/include/jemalloc/jemalloc_FreeBSD.h index 75b9b91e75fb..737542e09d85 100644 --- a/contrib/jemalloc/include/jemalloc/jemalloc_FreeBSD.h +++ b/contrib/jemalloc/include/jemalloc/jemalloc_FreeBSD.h @@ -12,7 +12,6 @@ * The following are architecture-dependent, so conditionally define them for * each supported architecture. */ -#undef CPU_SPINWAIT #undef JEMALLOC_TLS_MODEL #undef STATIC_PAGE_SHIFT #undef LG_SIZEOF_PTR @@ -22,7 +21,6 @@ #ifdef __i386__ # define LG_SIZEOF_PTR 2 -# define CPU_SPINWAIT __asm__ volatile("pause") # define JEMALLOC_TLS_MODEL __attribute__((tls_model("initial-exec"))) #endif #ifdef __ia64__ @@ -34,7 +32,6 @@ #endif #ifdef __amd64__ # define LG_SIZEOF_PTR 3 -# define CPU_SPINWAIT __asm__ volatile("pause") # define JEMALLOC_TLS_MODEL __attribute__((tls_model("initial-exec"))) #endif #ifdef __arm__ @@ -65,6 +62,11 @@ #define LG_SIZEOF_LONG LG_SIZEOF_PTR #define LG_SIZEOF_INTMAX_T 3 +#undef CPU_SPINWAIT +#include +#include +#define CPU_SPINWAIT cpu_spinwait() + /* Disable lazy-lock machinery, mangle isthreaded, and adjust its type. */ #undef JEMALLOC_LAZY_LOCK extern int __isthreaded; @@ -76,6 +78,7 @@ extern int __isthreaded; #undef je_realloc #undef je_free #undef je_posix_memalign +#undef je_aligned_alloc #undef je_malloc_usable_size #undef je_mallocx #undef je_rallocx @@ -93,6 +96,7 @@ extern int __isthreaded; #define je_realloc __realloc #define je_free __free #define je_posix_memalign __posix_memalign +#define je_aligned_alloc __aligned_alloc #define je_malloc_usable_size __malloc_usable_size #define je_mallocx __mallocx #define je_rallocx __rallocx @@ -122,6 +126,7 @@ __weak_reference(__calloc, calloc); __weak_reference(__realloc, realloc); __weak_reference(__free, free); __weak_reference(__posix_memalign, posix_memalign); +__weak_reference(__aligned_alloc, aligned_alloc); __weak_reference(__malloc_usable_size, malloc_usable_size); __weak_reference(__mallocx, mallocx); __weak_reference(__rallocx, rallocx); diff --git a/contrib/jemalloc/include/jemalloc/jemalloc_typedefs.h b/contrib/jemalloc/include/jemalloc/jemalloc_typedefs.h new file mode 100644 index 000000000000..fa7b350adcda --- /dev/null +++ b/contrib/jemalloc/include/jemalloc/jemalloc_typedefs.h @@ -0,0 +1,57 @@ +/* + * void * + * chunk_alloc(void *new_addr, size_t size, size_t alignment, bool *zero, + * bool *commit, unsigned arena_ind); + */ +typedef void *(chunk_alloc_t)(void *, size_t, size_t, bool *, bool *, unsigned); + +/* + * bool + * chunk_dalloc(void *chunk, size_t size, bool committed, unsigned arena_ind); + */ +typedef bool (chunk_dalloc_t)(void *, size_t, bool, unsigned); + +/* + * bool + * chunk_commit(void *chunk, size_t size, size_t offset, size_t length, + * unsigned arena_ind); + */ +typedef bool (chunk_commit_t)(void *, size_t, size_t, size_t, unsigned); + +/* + * bool + * chunk_decommit(void *chunk, size_t size, size_t offset, size_t length, + * unsigned arena_ind); + */ +typedef bool (chunk_decommit_t)(void *, size_t, size_t, size_t, unsigned); + +/* + * bool + * chunk_purge(void *chunk, size_t size, size_t offset, size_t length, + * unsigned arena_ind); + */ +typedef bool (chunk_purge_t)(void *, size_t, size_t, size_t, unsigned); + +/* + * bool + * chunk_split(void *chunk, size_t size, size_t size_a, size_t size_b, + * bool committed, unsigned arena_ind); + */ +typedef bool (chunk_split_t)(void *, size_t, size_t, size_t, bool, unsigned); + +/* + * bool + * chunk_merge(void *chunk_a, size_t size_a, void *chunk_b, size_t size_b, + * bool committed, unsigned arena_ind); + */ +typedef bool (chunk_merge_t)(void *, size_t, void *, size_t, bool, unsigned); + +typedef struct { + chunk_alloc_t *alloc; + chunk_dalloc_t *dalloc; + chunk_commit_t *commit; + chunk_decommit_t *decommit; + chunk_purge_t *purge; + chunk_split_t *split; + chunk_merge_t *merge; +} chunk_hooks_t; diff --git a/contrib/jemalloc/src/arena.c b/contrib/jemalloc/src/arena.c index dad707b63d05..af48b39d5955 100644 --- a/contrib/jemalloc/src/arena.c +++ b/contrib/jemalloc/src/arena.c @@ -5,37 +5,17 @@ /* Data. */ ssize_t opt_lg_dirty_mult = LG_DIRTY_MULT_DEFAULT; +static ssize_t lg_dirty_mult_default; arena_bin_info_t arena_bin_info[NBINS]; -JEMALLOC_ALIGNED(CACHELINE) -const uint8_t small_size2bin[] = { -#define S2B_8(i) i, -#define S2B_16(i) S2B_8(i) S2B_8(i) -#define S2B_32(i) S2B_16(i) S2B_16(i) -#define S2B_64(i) S2B_32(i) S2B_32(i) -#define S2B_128(i) S2B_64(i) S2B_64(i) -#define S2B_256(i) S2B_128(i) S2B_128(i) -#define S2B_512(i) S2B_256(i) S2B_256(i) -#define S2B_1024(i) S2B_512(i) S2B_512(i) -#define S2B_2048(i) S2B_1024(i) S2B_1024(i) -#define S2B_4096(i) S2B_2048(i) S2B_2048(i) -#define S2B_8192(i) S2B_4096(i) S2B_4096(i) -#define SIZE_CLASS(bin, delta, size) \ - S2B_##delta(bin) - SIZE_CLASSES -#undef S2B_8 -#undef S2B_16 -#undef S2B_32 -#undef S2B_64 -#undef S2B_128 -#undef S2B_256 -#undef S2B_512 -#undef S2B_1024 -#undef S2B_2048 -#undef S2B_4096 -#undef S2B_8192 -#undef SIZE_CLASS -}; +size_t map_bias; +size_t map_misc_offset; +size_t arena_maxrun; /* Max run size for arenas. */ +size_t arena_maxclass; /* Max size class for arenas. */ +static size_t small_maxrun; /* Max run size used for small size classes. */ +static bool *small_run_tab; /* Valid small run page multiples. */ +unsigned nlclasses; /* Number of large size classes. */ +unsigned nhclasses; /* Number of huge size classes. */ /******************************************************************************/ /* @@ -45,7 +25,7 @@ const uint8_t small_size2bin[] = { static void arena_purge(arena_t *arena, bool all); static void arena_run_dalloc(arena_t *arena, arena_run_t *run, bool dirty, - bool cleaned); + bool cleaned, bool decommitted); static void arena_dalloc_bin_run(arena_t *arena, arena_chunk_t *chunk, arena_run_t *run, arena_bin_t *bin); static void arena_bin_lower_run(arena_t *arena, arena_chunk_t *chunk, @@ -53,296 +33,327 @@ static void arena_bin_lower_run(arena_t *arena, arena_chunk_t *chunk, /******************************************************************************/ -static inline int -arena_run_comp(arena_chunk_map_t *a, arena_chunk_map_t *b) +#define CHUNK_MAP_KEY ((uintptr_t)0x1U) + +JEMALLOC_INLINE_C arena_chunk_map_misc_t * +arena_miscelm_key_create(size_t size) { - uintptr_t a_mapelm = (uintptr_t)a; - uintptr_t b_mapelm = (uintptr_t)b; + + return ((arena_chunk_map_misc_t *)((size << CHUNK_MAP_SIZE_SHIFT) | + CHUNK_MAP_KEY)); +} + +JEMALLOC_INLINE_C bool +arena_miscelm_is_key(const arena_chunk_map_misc_t *miscelm) +{ + + return (((uintptr_t)miscelm & CHUNK_MAP_KEY) != 0); +} + +#undef CHUNK_MAP_KEY + +JEMALLOC_INLINE_C size_t +arena_miscelm_key_size_get(const arena_chunk_map_misc_t *miscelm) +{ + + assert(arena_miscelm_is_key(miscelm)); + + return (((uintptr_t)miscelm & CHUNK_MAP_SIZE_MASK) >> + CHUNK_MAP_SIZE_SHIFT); +} + +JEMALLOC_INLINE_C size_t +arena_miscelm_size_get(arena_chunk_map_misc_t *miscelm) +{ + arena_chunk_t *chunk; + size_t pageind, mapbits; + + assert(!arena_miscelm_is_key(miscelm)); + + chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(miscelm); + pageind = arena_miscelm_to_pageind(miscelm); + mapbits = arena_mapbits_get(chunk, pageind); + return ((mapbits & CHUNK_MAP_SIZE_MASK) >> CHUNK_MAP_SIZE_SHIFT); +} + +JEMALLOC_INLINE_C int +arena_run_comp(arena_chunk_map_misc_t *a, arena_chunk_map_misc_t *b) +{ + uintptr_t a_miscelm = (uintptr_t)a; + uintptr_t b_miscelm = (uintptr_t)b; assert(a != NULL); assert(b != NULL); - return ((a_mapelm > b_mapelm) - (a_mapelm < b_mapelm)); + return ((a_miscelm > b_miscelm) - (a_miscelm < b_miscelm)); } /* Generate red-black tree functions. */ -rb_gen(static UNUSED, arena_run_tree_, arena_run_tree_t, arena_chunk_map_t, - u.rb_link, arena_run_comp) +rb_gen(static UNUSED, arena_run_tree_, arena_run_tree_t, arena_chunk_map_misc_t, + rb_link, arena_run_comp) -static inline int -arena_avail_comp(arena_chunk_map_t *a, arena_chunk_map_t *b) +static size_t +run_quantize(size_t size) +{ + size_t qsize; + + assert(size != 0); + assert(size == PAGE_CEILING(size)); + + /* Don't change sizes that are valid small run sizes. */ + if (size <= small_maxrun && small_run_tab[size >> LG_PAGE]) + return (size); + + /* + * Round down to the nearest run size that can actually be requested + * during normal large allocation. Add large_pad so that cache index + * randomization can offset the allocation from the page boundary. + */ + qsize = index2size(size2index(size - large_pad + 1) - 1) + large_pad; + if (qsize <= SMALL_MAXCLASS + large_pad) + return (run_quantize(size - large_pad)); + assert(qsize <= size); + return (qsize); +} + +static size_t +run_quantize_next(size_t size) +{ + size_t large_run_size_next; + + assert(size != 0); + assert(size == PAGE_CEILING(size)); + + /* + * Return the next quantized size greater than the input size. + * Quantized sizes comprise the union of run sizes that back small + * region runs, and run sizes that back large regions with no explicit + * alignment constraints. + */ + + if (size > SMALL_MAXCLASS) { + large_run_size_next = PAGE_CEILING(index2size(size2index(size - + large_pad) + 1) + large_pad); + } else + large_run_size_next = SIZE_T_MAX; + if (size >= small_maxrun) + return (large_run_size_next); + + while (true) { + size += PAGE; + assert(size <= small_maxrun); + if (small_run_tab[size >> LG_PAGE]) { + if (large_run_size_next < size) + return (large_run_size_next); + return (size); + } + } +} + +static size_t +run_quantize_first(size_t size) +{ + size_t qsize = run_quantize(size); + + if (qsize < size) { + /* + * Skip a quantization that may have an adequately large run, + * because under-sized runs may be mixed in. This only happens + * when an unusual size is requested, i.e. for aligned + * allocation, and is just one of several places where linear + * search would potentially find sufficiently aligned available + * memory somewhere lower. + */ + qsize = run_quantize_next(size); + } + return (qsize); +} + +JEMALLOC_INLINE_C int +arena_avail_comp(arena_chunk_map_misc_t *a, arena_chunk_map_misc_t *b) { int ret; - size_t a_size = a->bits & ~PAGE_MASK; - size_t b_size = b->bits & ~PAGE_MASK; + uintptr_t a_miscelm = (uintptr_t)a; + size_t a_qsize = run_quantize(arena_miscelm_is_key(a) ? + arena_miscelm_key_size_get(a) : arena_miscelm_size_get(a)); + size_t b_qsize = run_quantize(arena_miscelm_size_get(b)); - ret = (a_size > b_size) - (a_size < b_size); + /* + * Compare based on quantized size rather than size, in order to sort + * equally useful runs only by address. + */ + ret = (a_qsize > b_qsize) - (a_qsize < b_qsize); if (ret == 0) { - uintptr_t a_mapelm, b_mapelm; + if (!arena_miscelm_is_key(a)) { + uintptr_t b_miscelm = (uintptr_t)b; - if ((a->bits & CHUNK_MAP_KEY) != CHUNK_MAP_KEY) - a_mapelm = (uintptr_t)a; - else { + ret = (a_miscelm > b_miscelm) - (a_miscelm < b_miscelm); + } else { /* - * Treat keys as though they are lower than anything - * else. + * Treat keys as if they are lower than anything else. */ - a_mapelm = 0; + ret = -1; } - b_mapelm = (uintptr_t)b; - - ret = (a_mapelm > b_mapelm) - (a_mapelm < b_mapelm); } return (ret); } /* Generate red-black tree functions. */ -rb_gen(static UNUSED, arena_avail_tree_, arena_avail_tree_t, arena_chunk_map_t, - u.rb_link, arena_avail_comp) - -static inline int -arena_chunk_dirty_comp(arena_chunk_t *a, arena_chunk_t *b) -{ - - assert(a != NULL); - assert(b != NULL); - - /* - * Short-circuit for self comparison. The following comparison code - * would come to the same result, but at the cost of executing the slow - * path. - */ - if (a == b) - return (0); - - /* - * Order such that chunks with higher fragmentation are "less than" - * those with lower fragmentation -- purging order is from "least" to - * "greatest". Fragmentation is measured as: - * - * mean current avail run size - * -------------------------------- - * mean defragmented avail run size - * - * navail - * ----------- - * nruns_avail nruns_avail-nruns_adjac - * = ========================= = ----------------------- - * navail nruns_avail - * ----------------------- - * nruns_avail-nruns_adjac - * - * The following code multiplies away the denominator prior to - * comparison, in order to avoid division. - * - */ - { - size_t a_val = (a->nruns_avail - a->nruns_adjac) * - b->nruns_avail; - size_t b_val = (b->nruns_avail - b->nruns_adjac) * - a->nruns_avail; - - if (a_val < b_val) - return (1); - if (a_val > b_val) - return (-1); - } - /* - * Break ties by chunk address. For fragmented chunks, report lower - * addresses as "lower", so that fragmentation reduction happens first - * at lower addresses. However, use the opposite ordering for - * unfragmented chunks, in order to increase the chances of - * re-allocating dirty runs. - */ - { - uintptr_t a_chunk = (uintptr_t)a; - uintptr_t b_chunk = (uintptr_t)b; - int ret = ((a_chunk > b_chunk) - (a_chunk < b_chunk)); - if (a->nruns_adjac == 0) { - assert(b->nruns_adjac == 0); - ret = -ret; - } - return (ret); - } -} - -/* Generate red-black tree functions. */ -rb_gen(static UNUSED, arena_chunk_dirty_, arena_chunk_tree_t, arena_chunk_t, - dirty_link, arena_chunk_dirty_comp) - -static inline bool -arena_avail_adjac_pred(arena_chunk_t *chunk, size_t pageind) -{ - bool ret; - - if (pageind-1 < map_bias) - ret = false; - else { - ret = (arena_mapbits_allocated_get(chunk, pageind-1) == 0); - assert(ret == false || arena_mapbits_dirty_get(chunk, - pageind-1) != arena_mapbits_dirty_get(chunk, pageind)); - } - return (ret); -} - -static inline bool -arena_avail_adjac_succ(arena_chunk_t *chunk, size_t pageind, size_t npages) -{ - bool ret; - - if (pageind+npages == chunk_npages) - ret = false; - else { - assert(pageind+npages < chunk_npages); - ret = (arena_mapbits_allocated_get(chunk, pageind+npages) == 0); - assert(ret == false || arena_mapbits_dirty_get(chunk, pageind) - != arena_mapbits_dirty_get(chunk, pageind+npages)); - } - return (ret); -} - -static inline bool -arena_avail_adjac(arena_chunk_t *chunk, size_t pageind, size_t npages) -{ - - return (arena_avail_adjac_pred(chunk, pageind) || - arena_avail_adjac_succ(chunk, pageind, npages)); -} +rb_gen(static UNUSED, arena_avail_tree_, arena_avail_tree_t, + arena_chunk_map_misc_t, rb_link, arena_avail_comp) static void arena_avail_insert(arena_t *arena, arena_chunk_t *chunk, size_t pageind, - size_t npages, bool maybe_adjac_pred, bool maybe_adjac_succ) + size_t npages) { assert(npages == (arena_mapbits_unallocated_size_get(chunk, pageind) >> LG_PAGE)); - - /* - * chunks_dirty is keyed by nruns_{avail,adjac}, so the chunk must be - * removed and reinserted even if the run to be inserted is clean. - */ - if (chunk->ndirty != 0) - arena_chunk_dirty_remove(&arena->chunks_dirty, chunk); - - if (maybe_adjac_pred && arena_avail_adjac_pred(chunk, pageind)) - chunk->nruns_adjac++; - if (maybe_adjac_succ && arena_avail_adjac_succ(chunk, pageind, npages)) - chunk->nruns_adjac++; - chunk->nruns_avail++; - assert(chunk->nruns_avail > chunk->nruns_adjac); - - if (arena_mapbits_dirty_get(chunk, pageind) != 0) { - arena->ndirty += npages; - chunk->ndirty += npages; - } - if (chunk->ndirty != 0) - arena_chunk_dirty_insert(&arena->chunks_dirty, chunk); - - arena_avail_tree_insert(&arena->runs_avail, arena_mapp_get(chunk, + arena_avail_tree_insert(&arena->runs_avail, arena_miscelm_get(chunk, pageind)); } static void arena_avail_remove(arena_t *arena, arena_chunk_t *chunk, size_t pageind, - size_t npages, bool maybe_adjac_pred, bool maybe_adjac_succ) + size_t npages) { assert(npages == (arena_mapbits_unallocated_size_get(chunk, pageind) >> LG_PAGE)); - - /* - * chunks_dirty is keyed by nruns_{avail,adjac}, so the chunk must be - * removed and reinserted even if the run to be removed is clean. - */ - if (chunk->ndirty != 0) - arena_chunk_dirty_remove(&arena->chunks_dirty, chunk); - - if (maybe_adjac_pred && arena_avail_adjac_pred(chunk, pageind)) - chunk->nruns_adjac--; - if (maybe_adjac_succ && arena_avail_adjac_succ(chunk, pageind, npages)) - chunk->nruns_adjac--; - chunk->nruns_avail--; - assert(chunk->nruns_avail > chunk->nruns_adjac || (chunk->nruns_avail - == 0 && chunk->nruns_adjac == 0)); - - if (arena_mapbits_dirty_get(chunk, pageind) != 0) { - arena->ndirty -= npages; - chunk->ndirty -= npages; - } - if (chunk->ndirty != 0) - arena_chunk_dirty_insert(&arena->chunks_dirty, chunk); - - arena_avail_tree_remove(&arena->runs_avail, arena_mapp_get(chunk, + arena_avail_tree_remove(&arena->runs_avail, arena_miscelm_get(chunk, pageind)); } -static inline void * +static void +arena_run_dirty_insert(arena_t *arena, arena_chunk_t *chunk, size_t pageind, + size_t npages) +{ + arena_chunk_map_misc_t *miscelm = arena_miscelm_get(chunk, pageind); + + assert(npages == (arena_mapbits_unallocated_size_get(chunk, pageind) >> + LG_PAGE)); + assert(arena_mapbits_dirty_get(chunk, pageind) == CHUNK_MAP_DIRTY); + assert(arena_mapbits_dirty_get(chunk, pageind+npages-1) == + CHUNK_MAP_DIRTY); + + qr_new(&miscelm->rd, rd_link); + qr_meld(&arena->runs_dirty, &miscelm->rd, rd_link); + arena->ndirty += npages; +} + +static void +arena_run_dirty_remove(arena_t *arena, arena_chunk_t *chunk, size_t pageind, + size_t npages) +{ + arena_chunk_map_misc_t *miscelm = arena_miscelm_get(chunk, pageind); + + assert(npages == (arena_mapbits_unallocated_size_get(chunk, pageind) >> + LG_PAGE)); + assert(arena_mapbits_dirty_get(chunk, pageind) == CHUNK_MAP_DIRTY); + assert(arena_mapbits_dirty_get(chunk, pageind+npages-1) == + CHUNK_MAP_DIRTY); + + qr_remove(&miscelm->rd, rd_link); + assert(arena->ndirty >= npages); + arena->ndirty -= npages; +} + +static size_t +arena_chunk_dirty_npages(const extent_node_t *node) +{ + + return (extent_node_size_get(node) >> LG_PAGE); +} + +void +arena_chunk_cache_maybe_insert(arena_t *arena, extent_node_t *node, bool cache) +{ + + if (cache) { + extent_node_dirty_linkage_init(node); + extent_node_dirty_insert(node, &arena->runs_dirty, + &arena->chunks_cache); + arena->ndirty += arena_chunk_dirty_npages(node); + } +} + +void +arena_chunk_cache_maybe_remove(arena_t *arena, extent_node_t *node, bool dirty) +{ + + if (dirty) { + extent_node_dirty_remove(node); + assert(arena->ndirty >= arena_chunk_dirty_npages(node)); + arena->ndirty -= arena_chunk_dirty_npages(node); + } +} + +JEMALLOC_INLINE_C void * arena_run_reg_alloc(arena_run_t *run, arena_bin_info_t *bin_info) { void *ret; unsigned regind; - bitmap_t *bitmap = (bitmap_t *)((uintptr_t)run + - (uintptr_t)bin_info->bitmap_offset); + arena_chunk_map_misc_t *miscelm; + void *rpages; assert(run->nfree > 0); - assert(bitmap_full(bitmap, &bin_info->bitmap_info) == false); + assert(!bitmap_full(run->bitmap, &bin_info->bitmap_info)); - regind = bitmap_sfu(bitmap, &bin_info->bitmap_info); - ret = (void *)((uintptr_t)run + (uintptr_t)bin_info->reg0_offset + + regind = bitmap_sfu(run->bitmap, &bin_info->bitmap_info); + miscelm = arena_run_to_miscelm(run); + rpages = arena_miscelm_to_rpages(miscelm); + ret = (void *)((uintptr_t)rpages + (uintptr_t)bin_info->reg0_offset + (uintptr_t)(bin_info->reg_interval * regind)); run->nfree--; - if (regind == run->nextind) - run->nextind++; - assert(regind < run->nextind); return (ret); } -static inline void +JEMALLOC_INLINE_C void arena_run_reg_dalloc(arena_run_t *run, void *ptr) { arena_chunk_t *chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(run); size_t pageind = ((uintptr_t)ptr - (uintptr_t)chunk) >> LG_PAGE; size_t mapbits = arena_mapbits_get(chunk, pageind); - size_t binind = arena_ptr_small_binind_get(ptr, mapbits); + index_t binind = arena_ptr_small_binind_get(ptr, mapbits); arena_bin_info_t *bin_info = &arena_bin_info[binind]; unsigned regind = arena_run_regind(run, bin_info, ptr); - bitmap_t *bitmap = (bitmap_t *)((uintptr_t)run + - (uintptr_t)bin_info->bitmap_offset); assert(run->nfree < bin_info->nregs); /* Freeing an interior pointer can cause assertion failure. */ - assert(((uintptr_t)ptr - ((uintptr_t)run + + assert(((uintptr_t)ptr - + ((uintptr_t)arena_miscelm_to_rpages(arena_run_to_miscelm(run)) + (uintptr_t)bin_info->reg0_offset)) % (uintptr_t)bin_info->reg_interval == 0); - assert((uintptr_t)ptr >= (uintptr_t)run + + assert((uintptr_t)ptr >= + (uintptr_t)arena_miscelm_to_rpages(arena_run_to_miscelm(run)) + (uintptr_t)bin_info->reg0_offset); /* Freeing an unallocated pointer can cause assertion failure. */ - assert(bitmap_get(bitmap, &bin_info->bitmap_info, regind)); + assert(bitmap_get(run->bitmap, &bin_info->bitmap_info, regind)); - bitmap_unset(bitmap, &bin_info->bitmap_info, regind); + bitmap_unset(run->bitmap, &bin_info->bitmap_info, regind); run->nfree++; } -static inline void +JEMALLOC_INLINE_C void arena_run_zero(arena_chunk_t *chunk, size_t run_ind, size_t npages) { - VALGRIND_MAKE_MEM_UNDEFINED((void *)((uintptr_t)chunk + (run_ind << - LG_PAGE)), (npages << LG_PAGE)); + JEMALLOC_VALGRIND_MAKE_MEM_UNDEFINED((void *)((uintptr_t)chunk + + (run_ind << LG_PAGE)), (npages << LG_PAGE)); memset((void *)((uintptr_t)chunk + (run_ind << LG_PAGE)), 0, (npages << LG_PAGE)); } -static inline void +JEMALLOC_INLINE_C void arena_run_page_mark_zeroed(arena_chunk_t *chunk, size_t run_ind) { - VALGRIND_MAKE_MEM_DEFINED((void *)((uintptr_t)chunk + (run_ind << - LG_PAGE)), PAGE); + JEMALLOC_VALGRIND_MAKE_MEM_DEFINED((void *)((uintptr_t)chunk + (run_ind + << LG_PAGE)), PAGE); } -static inline void +JEMALLOC_INLINE_C void arena_run_page_validate_zeroed(arena_chunk_t *chunk, size_t run_ind) { size_t i; @@ -358,9 +369,9 @@ arena_cactive_update(arena_t *arena, size_t add_pages, size_t sub_pages) { if (config_stats) { - ssize_t cactive_diff = CHUNK_CEILING((arena->nactive + - add_pages) << LG_PAGE) - CHUNK_CEILING((arena->nactive - - sub_pages) << LG_PAGE); + ssize_t cactive_diff = CHUNK_CEILING((arena->nactive + add_pages + - sub_pages) << LG_PAGE) - CHUNK_CEILING(arena->nactive << + LG_PAGE); if (cactive_diff != 0) stats_cactive_add(cactive_diff); } @@ -368,10 +379,12 @@ arena_cactive_update(arena_t *arena, size_t add_pages, size_t sub_pages) static void arena_run_split_remove(arena_t *arena, arena_chunk_t *chunk, size_t run_ind, - size_t flag_dirty, size_t need_pages) + size_t flag_dirty, size_t flag_decommitted, size_t need_pages) { size_t total_pages, rem_pages; + assert(flag_dirty == 0 || flag_decommitted == 0); + total_pages = arena_mapbits_unallocated_size_get(chunk, run_ind) >> LG_PAGE; assert(arena_mapbits_dirty_get(chunk, run_ind+total_pages-1) == @@ -379,54 +392,70 @@ arena_run_split_remove(arena_t *arena, arena_chunk_t *chunk, size_t run_ind, assert(need_pages <= total_pages); rem_pages = total_pages - need_pages; - arena_avail_remove(arena, chunk, run_ind, total_pages, true, true); + arena_avail_remove(arena, chunk, run_ind, total_pages); + if (flag_dirty != 0) + arena_run_dirty_remove(arena, chunk, run_ind, total_pages); arena_cactive_update(arena, need_pages, 0); arena->nactive += need_pages; /* Keep track of trailing unused pages for later use. */ if (rem_pages > 0) { + size_t flags = flag_dirty | flag_decommitted; + size_t flag_unzeroed_mask = (flags == 0) ? CHUNK_MAP_UNZEROED : + 0; + + arena_mapbits_unallocated_set(chunk, run_ind+need_pages, + (rem_pages << LG_PAGE), flags | + (arena_mapbits_unzeroed_get(chunk, run_ind+need_pages) & + flag_unzeroed_mask)); + arena_mapbits_unallocated_set(chunk, run_ind+total_pages-1, + (rem_pages << LG_PAGE), flags | + (arena_mapbits_unzeroed_get(chunk, run_ind+total_pages-1) & + flag_unzeroed_mask)); if (flag_dirty != 0) { - arena_mapbits_unallocated_set(chunk, - run_ind+need_pages, (rem_pages << LG_PAGE), - flag_dirty); - arena_mapbits_unallocated_set(chunk, - run_ind+total_pages-1, (rem_pages << LG_PAGE), - flag_dirty); - } else { - arena_mapbits_unallocated_set(chunk, run_ind+need_pages, - (rem_pages << LG_PAGE), - arena_mapbits_unzeroed_get(chunk, - run_ind+need_pages)); - arena_mapbits_unallocated_set(chunk, - run_ind+total_pages-1, (rem_pages << LG_PAGE), - arena_mapbits_unzeroed_get(chunk, - run_ind+total_pages-1)); + arena_run_dirty_insert(arena, chunk, run_ind+need_pages, + rem_pages); } - arena_avail_insert(arena, chunk, run_ind+need_pages, rem_pages, - false, true); + arena_avail_insert(arena, chunk, run_ind+need_pages, rem_pages); } } -static void +static bool arena_run_split_large_helper(arena_t *arena, arena_run_t *run, size_t size, bool remove, bool zero) { arena_chunk_t *chunk; - size_t flag_dirty, run_ind, need_pages, i; + arena_chunk_map_misc_t *miscelm; + size_t flag_dirty, flag_decommitted, run_ind, need_pages, i; + size_t flag_unzeroed_mask; chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(run); - run_ind = (unsigned)(((uintptr_t)run - (uintptr_t)chunk) >> LG_PAGE); + miscelm = arena_run_to_miscelm(run); + run_ind = arena_miscelm_to_pageind(miscelm); flag_dirty = arena_mapbits_dirty_get(chunk, run_ind); + flag_decommitted = arena_mapbits_decommitted_get(chunk, run_ind); need_pages = (size >> LG_PAGE); assert(need_pages > 0); + if (flag_decommitted != 0 && arena->chunk_hooks.commit(chunk, chunksize, + run_ind << LG_PAGE, size, arena->ind)) + return (true); + if (remove) { arena_run_split_remove(arena, chunk, run_ind, flag_dirty, - need_pages); + flag_decommitted, need_pages); } if (zero) { - if (flag_dirty == 0) { + if (flag_decommitted != 0) { + /* The run is untouched, and therefore zeroed. */ + JEMALLOC_VALGRIND_MAKE_MEM_DEFINED((void + *)((uintptr_t)chunk + (run_ind << LG_PAGE)), + (need_pages << LG_PAGE)); + } else if (flag_dirty != 0) { + /* The run is dirty, so all pages must be zeroed. */ + arena_run_zero(chunk, run_ind, need_pages); + } else { /* * The run is clean, so some pages may be zeroed (i.e. * never before touched). @@ -443,12 +472,9 @@ arena_run_split_large_helper(arena_t *arena, arena_run_t *run, size_t size, run_ind+i); } } - } else { - /* The run is dirty, so all pages must be zeroed. */ - arena_run_zero(chunk, run_ind, need_pages); } } else { - VALGRIND_MAKE_MEM_UNDEFINED((void *)((uintptr_t)chunk + + JEMALLOC_VALGRIND_MAKE_MEM_UNDEFINED((void *)((uintptr_t)chunk + (run_ind << LG_PAGE)), (need_pages << LG_PAGE)); } @@ -456,68 +482,66 @@ arena_run_split_large_helper(arena_t *arena, arena_run_t *run, size_t size, * Set the last element first, in case the run only contains one page * (i.e. both statements set the same element). */ - arena_mapbits_large_set(chunk, run_ind+need_pages-1, 0, flag_dirty); - arena_mapbits_large_set(chunk, run_ind, size, flag_dirty); + flag_unzeroed_mask = (flag_dirty | flag_decommitted) == 0 ? + CHUNK_MAP_UNZEROED : 0; + arena_mapbits_large_set(chunk, run_ind+need_pages-1, 0, flag_dirty | + (flag_unzeroed_mask & arena_mapbits_unzeroed_get(chunk, + run_ind+need_pages-1))); + arena_mapbits_large_set(chunk, run_ind, size, flag_dirty | + (flag_unzeroed_mask & arena_mapbits_unzeroed_get(chunk, run_ind))); + return (false); } -static void +static bool arena_run_split_large(arena_t *arena, arena_run_t *run, size_t size, bool zero) { - arena_run_split_large_helper(arena, run, size, true, zero); + return (arena_run_split_large_helper(arena, run, size, true, zero)); } -static void +static bool arena_run_init_large(arena_t *arena, arena_run_t *run, size_t size, bool zero) { - arena_run_split_large_helper(arena, run, size, false, zero); + return (arena_run_split_large_helper(arena, run, size, false, zero)); } -static void +static bool arena_run_split_small(arena_t *arena, arena_run_t *run, size_t size, - size_t binind) + index_t binind) { arena_chunk_t *chunk; - size_t flag_dirty, run_ind, need_pages, i; + arena_chunk_map_misc_t *miscelm; + size_t flag_dirty, flag_decommitted, run_ind, need_pages, i; assert(binind != BININD_INVALID); chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(run); - run_ind = (unsigned)(((uintptr_t)run - (uintptr_t)chunk) >> LG_PAGE); + miscelm = arena_run_to_miscelm(run); + run_ind = arena_miscelm_to_pageind(miscelm); flag_dirty = arena_mapbits_dirty_get(chunk, run_ind); + flag_decommitted = arena_mapbits_decommitted_get(chunk, run_ind); need_pages = (size >> LG_PAGE); assert(need_pages > 0); - arena_run_split_remove(arena, chunk, run_ind, flag_dirty, need_pages); + if (flag_decommitted != 0 && arena->chunk_hooks.commit(chunk, chunksize, + run_ind << LG_PAGE, size, arena->ind)) + return (true); - /* - * Propagate the dirty and unzeroed flags to the allocated small run, - * so that arena_dalloc_bin_run() has the ability to conditionally trim - * clean pages. - */ - arena_mapbits_small_set(chunk, run_ind, 0, binind, flag_dirty); - /* - * The first page will always be dirtied during small run - * initialization, so a validation failure here would not actually - * cause an observable failure. - */ - if (config_debug && flag_dirty == 0 && arena_mapbits_unzeroed_get(chunk, - run_ind) == 0) - arena_run_page_validate_zeroed(chunk, run_ind); - for (i = 1; i < need_pages - 1; i++) { - arena_mapbits_small_set(chunk, run_ind+i, i, binind, 0); - if (config_debug && flag_dirty == 0 && - arena_mapbits_unzeroed_get(chunk, run_ind+i) == 0) + arena_run_split_remove(arena, chunk, run_ind, flag_dirty, + flag_decommitted, need_pages); + + for (i = 0; i < need_pages; i++) { + size_t flag_unzeroed = arena_mapbits_unzeroed_get(chunk, + run_ind+i); + arena_mapbits_small_set(chunk, run_ind+i, i, binind, + flag_unzeroed); + if (config_debug && flag_dirty == 0 && flag_unzeroed == 0) arena_run_page_validate_zeroed(chunk, run_ind+i); } - arena_mapbits_small_set(chunk, run_ind+need_pages-1, need_pages-1, - binind, flag_dirty); - if (config_debug && flag_dirty == 0 && arena_mapbits_unzeroed_get(chunk, - run_ind+need_pages-1) == 0) - arena_run_page_validate_zeroed(chunk, run_ind+need_pages-1); - VALGRIND_MAKE_MEM_UNDEFINED((void *)((uintptr_t)chunk + + JEMALLOC_VALGRIND_MAKE_MEM_UNDEFINED((void *)((uintptr_t)chunk + (run_ind << LG_PAGE)), (need_pages << LG_PAGE)); + return (false); } static arena_chunk_t * @@ -533,76 +557,143 @@ arena_chunk_init_spare(arena_t *arena) assert(arena_mapbits_allocated_get(chunk, map_bias) == 0); assert(arena_mapbits_allocated_get(chunk, chunk_npages-1) == 0); assert(arena_mapbits_unallocated_size_get(chunk, map_bias) == - arena_maxclass); + arena_maxrun); assert(arena_mapbits_unallocated_size_get(chunk, chunk_npages-1) == - arena_maxclass); + arena_maxrun); assert(arena_mapbits_dirty_get(chunk, map_bias) == arena_mapbits_dirty_get(chunk, chunk_npages-1)); return (chunk); } +static bool +arena_chunk_register(arena_t *arena, arena_chunk_t *chunk, bool zero) +{ + + /* + * The extent node notion of "committed" doesn't directly apply to + * arena chunks. Arbitrarily mark them as committed. The commit state + * of runs is tracked individually, and upon chunk deallocation the + * entire chunk is in a consistent commit state. + */ + extent_node_init(&chunk->node, arena, chunk, chunksize, zero, true); + extent_node_achunk_set(&chunk->node, true); + return (chunk_register(chunk, &chunk->node)); +} + +static arena_chunk_t * +arena_chunk_alloc_internal_hard(arena_t *arena, chunk_hooks_t *chunk_hooks, + bool *zero, bool *commit) +{ + arena_chunk_t *chunk; + + malloc_mutex_unlock(&arena->lock); + + chunk = (arena_chunk_t *)chunk_alloc_wrapper(arena, chunk_hooks, NULL, + chunksize, chunksize, zero, commit); + if (chunk != NULL && !*commit) { + /* Commit header. */ + if (chunk_hooks->commit(chunk, chunksize, 0, map_bias << + LG_PAGE, arena->ind)) { + chunk_dalloc_wrapper(arena, chunk_hooks, + (void *)chunk, chunksize, *commit); + chunk = NULL; + } + } + if (chunk != NULL && arena_chunk_register(arena, chunk, *zero)) { + if (!*commit) { + /* Undo commit of header. */ + chunk_hooks->decommit(chunk, chunksize, 0, map_bias << + LG_PAGE, arena->ind); + } + chunk_dalloc_wrapper(arena, chunk_hooks, (void *)chunk, + chunksize, *commit); + chunk = NULL; + } + + malloc_mutex_lock(&arena->lock); + return (chunk); +} + +static arena_chunk_t * +arena_chunk_alloc_internal(arena_t *arena, bool *zero, bool *commit) +{ + arena_chunk_t *chunk; + chunk_hooks_t chunk_hooks = CHUNK_HOOKS_INITIALIZER; + + chunk = chunk_alloc_cache(arena, &chunk_hooks, NULL, chunksize, + chunksize, zero, true); + if (chunk != NULL) { + if (arena_chunk_register(arena, chunk, *zero)) { + chunk_dalloc_cache(arena, &chunk_hooks, chunk, + chunksize, true); + return (NULL); + } + *commit = true; + } + if (chunk == NULL) { + chunk = arena_chunk_alloc_internal_hard(arena, &chunk_hooks, + zero, commit); + } + + if (config_stats && chunk != NULL) { + arena->stats.mapped += chunksize; + arena->stats.metadata_mapped += (map_bias << LG_PAGE); + } + + return (chunk); +} + static arena_chunk_t * arena_chunk_init_hard(arena_t *arena) { arena_chunk_t *chunk; - bool zero; - size_t unzeroed, i; + bool zero, commit; + size_t flag_unzeroed, flag_decommitted, i; assert(arena->spare == NULL); zero = false; - malloc_mutex_unlock(&arena->lock); - chunk = (arena_chunk_t *)chunk_alloc(chunksize, chunksize, false, - &zero, arena->dss_prec); - malloc_mutex_lock(&arena->lock); + commit = false; + chunk = arena_chunk_alloc_internal(arena, &zero, &commit); if (chunk == NULL) return (NULL); - if (config_stats) - arena->stats.mapped += chunksize; - - chunk->arena = arena; - - /* - * Claim that no pages are in use, since the header is merely overhead. - */ - chunk->ndirty = 0; - - chunk->nruns_avail = 0; - chunk->nruns_adjac = 0; /* * Initialize the map to contain one maximal free untouched run. Mark - * the pages as zeroed iff chunk_alloc() returned a zeroed chunk. + * the pages as zeroed if chunk_alloc() returned a zeroed or decommitted + * chunk. */ - unzeroed = zero ? 0 : CHUNK_MAP_UNZEROED; - arena_mapbits_unallocated_set(chunk, map_bias, arena_maxclass, - unzeroed); + flag_unzeroed = (zero || !commit) ? 0 : CHUNK_MAP_UNZEROED; + flag_decommitted = commit ? 0 : CHUNK_MAP_DECOMMITTED; + arena_mapbits_unallocated_set(chunk, map_bias, arena_maxrun, + flag_unzeroed | flag_decommitted); /* * There is no need to initialize the internal page map entries unless * the chunk is not zeroed. */ - if (zero == false) { - VALGRIND_MAKE_MEM_UNDEFINED((void *)arena_mapp_get(chunk, - map_bias+1), (size_t)((uintptr_t) arena_mapp_get(chunk, - chunk_npages-1) - (uintptr_t)arena_mapp_get(chunk, + if (!zero) { + JEMALLOC_VALGRIND_MAKE_MEM_UNDEFINED( + (void *)arena_bitselm_get(chunk, map_bias+1), + (size_t)((uintptr_t) arena_bitselm_get(chunk, + chunk_npages-1) - (uintptr_t)arena_bitselm_get(chunk, map_bias+1))); for (i = map_bias+1; i < chunk_npages-1; i++) - arena_mapbits_unzeroed_set(chunk, i, unzeroed); + arena_mapbits_internal_set(chunk, i, flag_unzeroed); } else { - VALGRIND_MAKE_MEM_DEFINED((void *)arena_mapp_get(chunk, - map_bias+1), (size_t)((uintptr_t) arena_mapp_get(chunk, - chunk_npages-1) - (uintptr_t)arena_mapp_get(chunk, - map_bias+1))); + JEMALLOC_VALGRIND_MAKE_MEM_DEFINED((void + *)arena_bitselm_get(chunk, map_bias+1), (size_t)((uintptr_t) + arena_bitselm_get(chunk, chunk_npages-1) - + (uintptr_t)arena_bitselm_get(chunk, map_bias+1))); if (config_debug) { for (i = map_bias+1; i < chunk_npages-1; i++) { assert(arena_mapbits_unzeroed_get(chunk, i) == - unzeroed); + flag_unzeroed); } } } - arena_mapbits_unallocated_set(chunk, chunk_npages-1, arena_maxclass, - unzeroed); + arena_mapbits_unallocated_set(chunk, chunk_npages-1, arena_maxrun, + flag_unzeroed); return (chunk); } @@ -621,65 +712,383 @@ arena_chunk_alloc(arena_t *arena) } /* Insert the run into the runs_avail tree. */ - arena_avail_insert(arena, chunk, map_bias, chunk_npages-map_bias, - false, false); + arena_avail_insert(arena, chunk, map_bias, chunk_npages-map_bias); return (chunk); } static void -arena_chunk_dealloc(arena_t *arena, arena_chunk_t *chunk) +arena_chunk_dalloc(arena_t *arena, arena_chunk_t *chunk) { + assert(arena_mapbits_allocated_get(chunk, map_bias) == 0); assert(arena_mapbits_allocated_get(chunk, chunk_npages-1) == 0); assert(arena_mapbits_unallocated_size_get(chunk, map_bias) == - arena_maxclass); + arena_maxrun); assert(arena_mapbits_unallocated_size_get(chunk, chunk_npages-1) == - arena_maxclass); + arena_maxrun); assert(arena_mapbits_dirty_get(chunk, map_bias) == arena_mapbits_dirty_get(chunk, chunk_npages-1)); + assert(arena_mapbits_decommitted_get(chunk, map_bias) == + arena_mapbits_decommitted_get(chunk, chunk_npages-1)); /* * Remove run from the runs_avail tree, so that the arena does not use * it. */ - arena_avail_remove(arena, chunk, map_bias, chunk_npages-map_bias, - false, false); + arena_avail_remove(arena, chunk, map_bias, chunk_npages-map_bias); if (arena->spare != NULL) { arena_chunk_t *spare = arena->spare; + chunk_hooks_t chunk_hooks = CHUNK_HOOKS_INITIALIZER; + bool committed; arena->spare = chunk; - malloc_mutex_unlock(&arena->lock); - chunk_dealloc((void *)spare, chunksize, true); - malloc_mutex_lock(&arena->lock); - if (config_stats) + if (arena_mapbits_dirty_get(spare, map_bias) != 0) { + arena_run_dirty_remove(arena, spare, map_bias, + chunk_npages-map_bias); + } + + chunk_deregister(spare, &spare->node); + + committed = (arena_mapbits_decommitted_get(spare, map_bias) == + 0); + if (!committed) { + /* + * Decommit the header. Mark the chunk as decommitted + * even if header decommit fails, since treating a + * partially committed chunk as committed has a high + * potential for causing later access of decommitted + * memory. + */ + chunk_hooks = chunk_hooks_get(arena); + chunk_hooks.decommit(spare, chunksize, 0, map_bias << + LG_PAGE, arena->ind); + } + + chunk_dalloc_cache(arena, &chunk_hooks, (void *)spare, + chunksize, committed); + + if (config_stats) { arena->stats.mapped -= chunksize; + arena->stats.metadata_mapped -= (map_bias << LG_PAGE); + } } else arena->spare = chunk; } +static void +arena_huge_malloc_stats_update(arena_t *arena, size_t usize) +{ + index_t index = size2index(usize) - nlclasses - NBINS; + + cassert(config_stats); + + arena->stats.nmalloc_huge++; + arena->stats.allocated_huge += usize; + arena->stats.hstats[index].nmalloc++; + arena->stats.hstats[index].curhchunks++; +} + +static void +arena_huge_malloc_stats_update_undo(arena_t *arena, size_t usize) +{ + index_t index = size2index(usize) - nlclasses - NBINS; + + cassert(config_stats); + + arena->stats.nmalloc_huge--; + arena->stats.allocated_huge -= usize; + arena->stats.hstats[index].nmalloc--; + arena->stats.hstats[index].curhchunks--; +} + +static void +arena_huge_dalloc_stats_update(arena_t *arena, size_t usize) +{ + index_t index = size2index(usize) - nlclasses - NBINS; + + cassert(config_stats); + + arena->stats.ndalloc_huge++; + arena->stats.allocated_huge -= usize; + arena->stats.hstats[index].ndalloc++; + arena->stats.hstats[index].curhchunks--; +} + +static void +arena_huge_dalloc_stats_update_undo(arena_t *arena, size_t usize) +{ + index_t index = size2index(usize) - nlclasses - NBINS; + + cassert(config_stats); + + arena->stats.ndalloc_huge--; + arena->stats.allocated_huge += usize; + arena->stats.hstats[index].ndalloc--; + arena->stats.hstats[index].curhchunks++; +} + +static void +arena_huge_ralloc_stats_update(arena_t *arena, size_t oldsize, size_t usize) +{ + + arena_huge_dalloc_stats_update(arena, oldsize); + arena_huge_malloc_stats_update(arena, usize); +} + +static void +arena_huge_ralloc_stats_update_undo(arena_t *arena, size_t oldsize, + size_t usize) +{ + + arena_huge_dalloc_stats_update_undo(arena, oldsize); + arena_huge_malloc_stats_update_undo(arena, usize); +} + +extent_node_t * +arena_node_alloc(arena_t *arena) +{ + extent_node_t *node; + + malloc_mutex_lock(&arena->node_cache_mtx); + node = ql_last(&arena->node_cache, ql_link); + if (node == NULL) { + malloc_mutex_unlock(&arena->node_cache_mtx); + return (base_alloc(sizeof(extent_node_t))); + } + ql_tail_remove(&arena->node_cache, extent_node_t, ql_link); + malloc_mutex_unlock(&arena->node_cache_mtx); + return (node); +} + +void +arena_node_dalloc(arena_t *arena, extent_node_t *node) +{ + + malloc_mutex_lock(&arena->node_cache_mtx); + ql_elm_new(node, ql_link); + ql_tail_insert(&arena->node_cache, node, ql_link); + malloc_mutex_unlock(&arena->node_cache_mtx); +} + +static void * +arena_chunk_alloc_huge_hard(arena_t *arena, chunk_hooks_t *chunk_hooks, + size_t usize, size_t alignment, bool *zero, size_t csize) +{ + void *ret; + bool commit = true; + + ret = chunk_alloc_wrapper(arena, chunk_hooks, NULL, csize, alignment, + zero, &commit); + if (ret == NULL) { + /* Revert optimistic stats updates. */ + malloc_mutex_lock(&arena->lock); + if (config_stats) { + arena_huge_malloc_stats_update_undo(arena, usize); + arena->stats.mapped -= usize; + } + arena->nactive -= (usize >> LG_PAGE); + malloc_mutex_unlock(&arena->lock); + } + + return (ret); +} + +void * +arena_chunk_alloc_huge(arena_t *arena, size_t usize, size_t alignment, + bool *zero) +{ + void *ret; + chunk_hooks_t chunk_hooks = CHUNK_HOOKS_INITIALIZER; + size_t csize = CHUNK_CEILING(usize); + + malloc_mutex_lock(&arena->lock); + + /* Optimistically update stats. */ + if (config_stats) { + arena_huge_malloc_stats_update(arena, usize); + arena->stats.mapped += usize; + } + arena->nactive += (usize >> LG_PAGE); + + ret = chunk_alloc_cache(arena, &chunk_hooks, NULL, csize, alignment, + zero, true); + malloc_mutex_unlock(&arena->lock); + if (ret == NULL) { + ret = arena_chunk_alloc_huge_hard(arena, &chunk_hooks, usize, + alignment, zero, csize); + } + + if (config_stats && ret != NULL) + stats_cactive_add(usize); + return (ret); +} + +void +arena_chunk_dalloc_huge(arena_t *arena, void *chunk, size_t usize) +{ + chunk_hooks_t chunk_hooks = CHUNK_HOOKS_INITIALIZER; + size_t csize; + + csize = CHUNK_CEILING(usize); + malloc_mutex_lock(&arena->lock); + if (config_stats) { + arena_huge_dalloc_stats_update(arena, usize); + arena->stats.mapped -= usize; + stats_cactive_sub(usize); + } + arena->nactive -= (usize >> LG_PAGE); + + chunk_dalloc_cache(arena, &chunk_hooks, chunk, csize, true); + malloc_mutex_unlock(&arena->lock); +} + +void +arena_chunk_ralloc_huge_similar(arena_t *arena, void *chunk, size_t oldsize, + size_t usize) +{ + + assert(CHUNK_CEILING(oldsize) == CHUNK_CEILING(usize)); + assert(oldsize != usize); + + malloc_mutex_lock(&arena->lock); + if (config_stats) + arena_huge_ralloc_stats_update(arena, oldsize, usize); + if (oldsize < usize) { + size_t udiff = usize - oldsize; + arena->nactive += udiff >> LG_PAGE; + if (config_stats) + stats_cactive_add(udiff); + } else { + size_t udiff = oldsize - usize; + arena->nactive -= udiff >> LG_PAGE; + if (config_stats) + stats_cactive_sub(udiff); + } + malloc_mutex_unlock(&arena->lock); +} + +void +arena_chunk_ralloc_huge_shrink(arena_t *arena, void *chunk, size_t oldsize, + size_t usize) +{ + size_t udiff = oldsize - usize; + size_t cdiff = CHUNK_CEILING(oldsize) - CHUNK_CEILING(usize); + + malloc_mutex_lock(&arena->lock); + if (config_stats) { + arena_huge_ralloc_stats_update(arena, oldsize, usize); + if (cdiff != 0) { + arena->stats.mapped -= cdiff; + stats_cactive_sub(udiff); + } + } + arena->nactive -= udiff >> LG_PAGE; + + if (cdiff != 0) { + chunk_hooks_t chunk_hooks = CHUNK_HOOKS_INITIALIZER; + void *nchunk = (void *)((uintptr_t)chunk + + CHUNK_CEILING(usize)); + + chunk_dalloc_cache(arena, &chunk_hooks, nchunk, cdiff, true); + } + malloc_mutex_unlock(&arena->lock); +} + +static bool +arena_chunk_ralloc_huge_expand_hard(arena_t *arena, chunk_hooks_t *chunk_hooks, + void *chunk, size_t oldsize, size_t usize, bool *zero, void *nchunk, + size_t udiff, size_t cdiff) +{ + bool err; + bool commit = true; + + err = (chunk_alloc_wrapper(arena, chunk_hooks, nchunk, cdiff, chunksize, + zero, &commit) == NULL); + if (err) { + /* Revert optimistic stats updates. */ + malloc_mutex_lock(&arena->lock); + if (config_stats) { + arena_huge_ralloc_stats_update_undo(arena, oldsize, + usize); + arena->stats.mapped -= cdiff; + } + arena->nactive -= (udiff >> LG_PAGE); + malloc_mutex_unlock(&arena->lock); + } else if (chunk_hooks->merge(chunk, CHUNK_CEILING(oldsize), nchunk, + cdiff, true, arena->ind)) { + chunk_dalloc_arena(arena, chunk_hooks, nchunk, cdiff, *zero, + true); + err = true; + } + return (err); +} + +bool +arena_chunk_ralloc_huge_expand(arena_t *arena, void *chunk, size_t oldsize, + size_t usize, bool *zero) +{ + bool err; + chunk_hooks_t chunk_hooks = chunk_hooks_get(arena); + void *nchunk = (void *)((uintptr_t)chunk + CHUNK_CEILING(oldsize)); + size_t udiff = usize - oldsize; + size_t cdiff = CHUNK_CEILING(usize) - CHUNK_CEILING(oldsize); + + malloc_mutex_lock(&arena->lock); + + /* Optimistically update stats. */ + if (config_stats) { + arena_huge_ralloc_stats_update(arena, oldsize, usize); + arena->stats.mapped += cdiff; + } + arena->nactive += (udiff >> LG_PAGE); + + err = (chunk_alloc_cache(arena, &arena->chunk_hooks, nchunk, cdiff, + chunksize, zero, true) == NULL); + malloc_mutex_unlock(&arena->lock); + if (err) { + err = arena_chunk_ralloc_huge_expand_hard(arena, &chunk_hooks, + chunk, oldsize, usize, zero, nchunk, udiff, + cdiff); + } else if (chunk_hooks.merge(chunk, CHUNK_CEILING(oldsize), nchunk, + cdiff, true, arena->ind)) { + chunk_dalloc_arena(arena, &chunk_hooks, nchunk, cdiff, *zero, + true); + err = true; + } + + if (config_stats && !err) + stats_cactive_add(udiff); + return (err); +} + +/* + * Do first-best-fit run selection, i.e. select the lowest run that best fits. + * Run sizes are quantized, so not all candidate runs are necessarily exactly + * the same size. + */ +static arena_run_t * +arena_run_first_best_fit(arena_t *arena, size_t size) +{ + size_t search_size = run_quantize_first(size); + arena_chunk_map_misc_t *key = arena_miscelm_key_create(search_size); + arena_chunk_map_misc_t *miscelm = + arena_avail_tree_nsearch(&arena->runs_avail, key); + if (miscelm == NULL) + return (NULL); + return (&miscelm->run); +} + static arena_run_t * arena_run_alloc_large_helper(arena_t *arena, size_t size, bool zero) { - arena_run_t *run; - arena_chunk_map_t *mapelm, key; - - key.bits = size | CHUNK_MAP_KEY; - mapelm = arena_avail_tree_nsearch(&arena->runs_avail, &key); - if (mapelm != NULL) { - arena_chunk_t *run_chunk = CHUNK_ADDR2BASE(mapelm); - size_t pageind = (((uintptr_t)mapelm - - (uintptr_t)run_chunk->map) / sizeof(arena_chunk_map_t)) - + map_bias; - - run = (arena_run_t *)((uintptr_t)run_chunk + (pageind << - LG_PAGE)); - arena_run_split_large(arena, run, size, zero); - return (run); + arena_run_t *run = arena_run_first_best_fit(arena, s2u(size)); + if (run != NULL) { + if (arena_run_split_large(arena, run, size, zero)) + run = NULL; } - - return (NULL); + return (run); } static arena_run_t * @@ -688,8 +1097,8 @@ arena_run_alloc_large(arena_t *arena, size_t size, bool zero) arena_chunk_t *chunk; arena_run_t *run; - assert(size <= arena_maxclass); - assert((size & PAGE_MASK) == 0); + assert(size <= arena_maxrun); + assert(size == PAGE_CEILING(size)); /* Search the arena's chunks for the lowest best fit. */ run = arena_run_alloc_large_helper(arena, size, zero); @@ -701,8 +1110,9 @@ arena_run_alloc_large(arena_t *arena, size_t size, bool zero) */ chunk = arena_chunk_alloc(arena); if (chunk != NULL) { - run = (arena_run_t *)((uintptr_t)chunk + (map_bias << LG_PAGE)); - arena_run_split_large(arena, run, size, zero); + run = &arena_miscelm_get(chunk, map_bias)->run; + if (arena_run_split_large(arena, run, size, zero)) + run = NULL; return (run); } @@ -715,36 +1125,24 @@ arena_run_alloc_large(arena_t *arena, size_t size, bool zero) } static arena_run_t * -arena_run_alloc_small_helper(arena_t *arena, size_t size, size_t binind) +arena_run_alloc_small_helper(arena_t *arena, size_t size, index_t binind) { - arena_run_t *run; - arena_chunk_map_t *mapelm, key; - - key.bits = size | CHUNK_MAP_KEY; - mapelm = arena_avail_tree_nsearch(&arena->runs_avail, &key); - if (mapelm != NULL) { - arena_chunk_t *run_chunk = CHUNK_ADDR2BASE(mapelm); - size_t pageind = (((uintptr_t)mapelm - - (uintptr_t)run_chunk->map) / sizeof(arena_chunk_map_t)) - + map_bias; - - run = (arena_run_t *)((uintptr_t)run_chunk + (pageind << - LG_PAGE)); - arena_run_split_small(arena, run, size, binind); - return (run); + arena_run_t *run = arena_run_first_best_fit(arena, size); + if (run != NULL) { + if (arena_run_split_small(arena, run, size, binind)) + run = NULL; } - - return (NULL); + return (run); } static arena_run_t * -arena_run_alloc_small(arena_t *arena, size_t size, size_t binind) +arena_run_alloc_small(arena_t *arena, size_t size, index_t binind) { arena_chunk_t *chunk; arena_run_t *run; - assert(size <= arena_maxclass); - assert((size & PAGE_MASK) == 0); + assert(size <= arena_maxrun); + assert(size == PAGE_CEILING(size)); assert(binind != BININD_INVALID); /* Search the arena's chunks for the lowest best fit. */ @@ -757,8 +1155,9 @@ arena_run_alloc_small(arena_t *arena, size_t size, size_t binind) */ chunk = arena_chunk_alloc(arena); if (chunk != NULL) { - run = (arena_run_t *)((uintptr_t)chunk + (map_bias << LG_PAGE)); - arena_run_split_small(arena, run, size, binind); + run = &arena_miscelm_get(chunk, map_bias)->run; + if (arena_run_split_small(arena, run, size, binind)) + run = NULL; return (run); } @@ -770,313 +1169,373 @@ arena_run_alloc_small(arena_t *arena, size_t size, size_t binind) return (arena_run_alloc_small_helper(arena, size, binind)); } -static inline void -arena_maybe_purge(arena_t *arena) +static bool +arena_lg_dirty_mult_valid(ssize_t lg_dirty_mult) { - size_t npurgeable, threshold; - /* Don't purge if the option is disabled. */ - if (opt_lg_dirty_mult < 0) - return; - /* Don't purge if all dirty pages are already being purged. */ - if (arena->ndirty <= arena->npurgatory) - return; - npurgeable = arena->ndirty - arena->npurgatory; - threshold = (arena->nactive >> opt_lg_dirty_mult); - /* - * Don't purge unless the number of purgeable pages exceeds the - * threshold. - */ - if (npurgeable <= threshold) - return; - - arena_purge(arena, false); + return (lg_dirty_mult >= -1 && lg_dirty_mult < (ssize_t)(sizeof(size_t) + << 3)); } -static arena_chunk_t * -chunks_dirty_iter_cb(arena_chunk_tree_t *tree, arena_chunk_t *chunk, void *arg) +ssize_t +arena_lg_dirty_mult_get(arena_t *arena) { - size_t *ndirty = (size_t *)arg; + ssize_t lg_dirty_mult; - assert(chunk->ndirty != 0); - *ndirty += chunk->ndirty; - return (NULL); + malloc_mutex_lock(&arena->lock); + lg_dirty_mult = arena->lg_dirty_mult; + malloc_mutex_unlock(&arena->lock); + + return (lg_dirty_mult); +} + +bool +arena_lg_dirty_mult_set(arena_t *arena, ssize_t lg_dirty_mult) +{ + + if (!arena_lg_dirty_mult_valid(lg_dirty_mult)) + return (true); + + malloc_mutex_lock(&arena->lock); + arena->lg_dirty_mult = lg_dirty_mult; + arena_maybe_purge(arena); + malloc_mutex_unlock(&arena->lock); + + return (false); +} + +void +arena_maybe_purge(arena_t *arena) +{ + + /* Don't purge if the option is disabled. */ + if (arena->lg_dirty_mult < 0) + return; + /* Don't recursively purge. */ + if (arena->purging) + return; + /* + * Iterate, since preventing recursive purging could otherwise leave too + * many dirty pages. + */ + while (true) { + size_t threshold = (arena->nactive >> arena->lg_dirty_mult); + if (threshold < chunk_npages) + threshold = chunk_npages; + /* + * Don't purge unless the number of purgeable pages exceeds the + * threshold. + */ + if (arena->ndirty <= threshold) + return; + arena_purge(arena, false); + } } static size_t -arena_compute_npurgatory(arena_t *arena, bool all) +arena_dirty_count(arena_t *arena) { - size_t npurgatory, npurgeable; + size_t ndirty = 0; + arena_runs_dirty_link_t *rdelm; + extent_node_t *chunkselm; + + for (rdelm = qr_next(&arena->runs_dirty, rd_link), + chunkselm = qr_next(&arena->chunks_cache, cc_link); + rdelm != &arena->runs_dirty; rdelm = qr_next(rdelm, rd_link)) { + size_t npages; + + if (rdelm == &chunkselm->rd) { + npages = extent_node_size_get(chunkselm) >> LG_PAGE; + chunkselm = qr_next(chunkselm, cc_link); + } else { + arena_chunk_t *chunk = (arena_chunk_t *)CHUNK_ADDR2BASE( + rdelm); + arena_chunk_map_misc_t *miscelm = + arena_rd_to_miscelm(rdelm); + size_t pageind = arena_miscelm_to_pageind(miscelm); + assert(arena_mapbits_allocated_get(chunk, pageind) == + 0); + assert(arena_mapbits_large_get(chunk, pageind) == 0); + assert(arena_mapbits_dirty_get(chunk, pageind) != 0); + npages = arena_mapbits_unallocated_size_get(chunk, + pageind) >> LG_PAGE; + } + ndirty += npages; + } + + return (ndirty); +} + +static size_t +arena_compute_npurge(arena_t *arena, bool all) +{ + size_t npurge; /* * Compute the minimum number of pages that this thread should try to * purge. */ - npurgeable = arena->ndirty - arena->npurgatory; + if (!all) { + size_t threshold = (arena->nactive >> arena->lg_dirty_mult); + threshold = threshold < chunk_npages ? chunk_npages : threshold; - if (all == false) { - size_t threshold = (arena->nactive >> opt_lg_dirty_mult); - - npurgatory = npurgeable - threshold; + npurge = arena->ndirty - threshold; } else - npurgatory = npurgeable; + npurge = arena->ndirty; - return (npurgatory); + return (npurge); } -static void -arena_chunk_stash_dirty(arena_t *arena, arena_chunk_t *chunk, bool all, - arena_chunk_mapelms_t *mapelms) +static size_t +arena_stash_dirty(arena_t *arena, chunk_hooks_t *chunk_hooks, bool all, + size_t npurge, arena_runs_dirty_link_t *purge_runs_sentinel, + extent_node_t *purge_chunks_sentinel) { - size_t pageind, npages; + arena_runs_dirty_link_t *rdelm, *rdelm_next; + extent_node_t *chunkselm; + size_t nstashed = 0; - /* - * Temporarily allocate free dirty runs within chunk. If all is false, - * only operate on dirty runs that are fragments; otherwise operate on - * all dirty runs. - */ - for (pageind = map_bias; pageind < chunk_npages; pageind += npages) { - arena_chunk_map_t *mapelm = arena_mapp_get(chunk, pageind); - if (arena_mapbits_allocated_get(chunk, pageind) == 0) { + /* Stash at least npurge pages. */ + for (rdelm = qr_next(&arena->runs_dirty, rd_link), + chunkselm = qr_next(&arena->chunks_cache, cc_link); + rdelm != &arena->runs_dirty; rdelm = rdelm_next) { + size_t npages; + rdelm_next = qr_next(rdelm, rd_link); + + if (rdelm == &chunkselm->rd) { + extent_node_t *chunkselm_next; + bool zero; + UNUSED void *chunk; + + chunkselm_next = qr_next(chunkselm, cc_link); + /* + * Allocate. chunkselm remains valid due to the + * dalloc_node=false argument to chunk_alloc_cache(). + */ + zero = false; + chunk = chunk_alloc_cache(arena, chunk_hooks, + extent_node_addr_get(chunkselm), + extent_node_size_get(chunkselm), chunksize, &zero, + false); + assert(chunk == extent_node_addr_get(chunkselm)); + assert(zero == extent_node_zeroed_get(chunkselm)); + extent_node_dirty_insert(chunkselm, purge_runs_sentinel, + purge_chunks_sentinel); + npages = extent_node_size_get(chunkselm) >> LG_PAGE; + chunkselm = chunkselm_next; + } else { + arena_chunk_t *chunk = + (arena_chunk_t *)CHUNK_ADDR2BASE(rdelm); + arena_chunk_map_misc_t *miscelm = + arena_rd_to_miscelm(rdelm); + size_t pageind = arena_miscelm_to_pageind(miscelm); + arena_run_t *run = &miscelm->run; size_t run_size = arena_mapbits_unallocated_size_get(chunk, pageind); npages = run_size >> LG_PAGE; + assert(pageind + npages <= chunk_npages); assert(arena_mapbits_dirty_get(chunk, pageind) == arena_mapbits_dirty_get(chunk, pageind+npages-1)); - if (arena_mapbits_dirty_get(chunk, pageind) != 0 && - (all || arena_avail_adjac(chunk, pageind, - npages))) { - arena_run_t *run = (arena_run_t *)((uintptr_t) - chunk + (uintptr_t)(pageind << LG_PAGE)); + /* + * If purging the spare chunk's run, make it available + * prior to allocation. + */ + if (chunk == arena->spare) + arena_chunk_alloc(arena); - arena_run_split_large(arena, run, run_size, - false); - /* Append to list for later processing. */ - ql_elm_new(mapelm, u.ql_link); - ql_tail_insert(mapelms, mapelm, u.ql_link); - } - } else { - /* Skip run. */ - if (arena_mapbits_large_get(chunk, pageind) != 0) { - npages = arena_mapbits_large_size_get(chunk, - pageind) >> LG_PAGE; - } else { - size_t binind; - arena_bin_info_t *bin_info; - arena_run_t *run = (arena_run_t *)((uintptr_t) - chunk + (uintptr_t)(pageind << LG_PAGE)); - - assert(arena_mapbits_small_runind_get(chunk, - pageind) == 0); - binind = arena_bin_index(arena, run->bin); - bin_info = &arena_bin_info[binind]; - npages = bin_info->run_size >> LG_PAGE; + /* Temporarily allocate the free dirty run. */ + arena_run_split_large(arena, run, run_size, false); + /* Stash. */ + if (false) + qr_new(rdelm, rd_link); /* Redundant. */ + else { + assert(qr_next(rdelm, rd_link) == rdelm); + assert(qr_prev(rdelm, rd_link) == rdelm); } + qr_meld(purge_runs_sentinel, rdelm, rd_link); } + + nstashed += npages; + if (!all && nstashed >= npurge) + break; } - assert(pageind == chunk_npages); - assert(chunk->ndirty == 0 || all == false); - assert(chunk->nruns_adjac == 0); + + return (nstashed); } static size_t -arena_chunk_purge_stashed(arena_t *arena, arena_chunk_t *chunk, - arena_chunk_mapelms_t *mapelms) +arena_purge_stashed(arena_t *arena, chunk_hooks_t *chunk_hooks, + arena_runs_dirty_link_t *purge_runs_sentinel, + extent_node_t *purge_chunks_sentinel) { - size_t npurged, pageind, npages, nmadvise; - arena_chunk_map_t *mapelm; + size_t npurged, nmadvise; + arena_runs_dirty_link_t *rdelm; + extent_node_t *chunkselm; - malloc_mutex_unlock(&arena->lock); if (config_stats) nmadvise = 0; npurged = 0; - ql_foreach(mapelm, mapelms, u.ql_link) { - bool unzeroed; - size_t flag_unzeroed, i; - pageind = (((uintptr_t)mapelm - (uintptr_t)chunk->map) / - sizeof(arena_chunk_map_t)) + map_bias; - npages = arena_mapbits_large_size_get(chunk, pageind) >> - LG_PAGE; - assert(pageind + npages <= chunk_npages); - unzeroed = pages_purge((void *)((uintptr_t)chunk + (pageind << - LG_PAGE)), (npages << LG_PAGE)); - flag_unzeroed = unzeroed ? CHUNK_MAP_UNZEROED : 0; - /* - * Set the unzeroed flag for all pages, now that pages_purge() - * has returned whether the pages were zeroed as a side effect - * of purging. This chunk map modification is safe even though - * the arena mutex isn't currently owned by this thread, - * because the run is marked as allocated, thus protecting it - * from being modified by any other thread. As long as these - * writes don't perturb the first and last elements' - * CHUNK_MAP_ALLOCATED bits, behavior is well defined. - */ - for (i = 0; i < npages; i++) { - arena_mapbits_unzeroed_set(chunk, pageind+i, - flag_unzeroed); + malloc_mutex_unlock(&arena->lock); + for (rdelm = qr_next(purge_runs_sentinel, rd_link), + chunkselm = qr_next(purge_chunks_sentinel, cc_link); + rdelm != purge_runs_sentinel; rdelm = qr_next(rdelm, rd_link)) { + size_t npages; + + if (rdelm == &chunkselm->rd) { + /* + * Don't actually purge the chunk here because 1) + * chunkselm is embedded in the chunk and must remain + * valid, and 2) we deallocate the chunk in + * arena_unstash_purged(), where it is destroyed, + * decommitted, or purged, depending on chunk + * deallocation policy. + */ + size_t size = extent_node_size_get(chunkselm); + npages = size >> LG_PAGE; + chunkselm = qr_next(chunkselm, cc_link); + } else { + size_t pageind, run_size, flag_unzeroed, flags, i; + bool decommitted; + arena_chunk_t *chunk = + (arena_chunk_t *)CHUNK_ADDR2BASE(rdelm); + arena_chunk_map_misc_t *miscelm = + arena_rd_to_miscelm(rdelm); + pageind = arena_miscelm_to_pageind(miscelm); + run_size = arena_mapbits_large_size_get(chunk, pageind); + npages = run_size >> LG_PAGE; + + assert(pageind + npages <= chunk_npages); + assert(!arena_mapbits_decommitted_get(chunk, pageind)); + assert(!arena_mapbits_decommitted_get(chunk, + pageind+npages-1)); + decommitted = !chunk_hooks->decommit(chunk, chunksize, + pageind << LG_PAGE, npages << LG_PAGE, arena->ind); + if (decommitted) { + flag_unzeroed = 0; + flags = CHUNK_MAP_DECOMMITTED; + } else { + flag_unzeroed = chunk_purge_wrapper(arena, + chunk_hooks, chunk, chunksize, pageind << + LG_PAGE, run_size) ? CHUNK_MAP_UNZEROED : 0; + flags = flag_unzeroed; + } + arena_mapbits_large_set(chunk, pageind+npages-1, 0, + flags); + arena_mapbits_large_set(chunk, pageind, run_size, + flags); + + /* + * Set the unzeroed flag for internal pages, now that + * chunk_purge_wrapper() has returned whether the pages + * were zeroed as a side effect of purging. This chunk + * map modification is safe even though the arena mutex + * isn't currently owned by this thread, because the run + * is marked as allocated, thus protecting it from being + * modified by any other thread. As long as these + * writes don't perturb the first and last elements' + * CHUNK_MAP_ALLOCATED bits, behavior is well defined. + */ + for (i = 1; i < npages-1; i++) { + arena_mapbits_internal_set(chunk, pageind+i, + flag_unzeroed); + } } + npurged += npages; if (config_stats) nmadvise++; } malloc_mutex_lock(&arena->lock); - if (config_stats) + + if (config_stats) { arena->stats.nmadvise += nmadvise; + arena->stats.purged += npurged; + } return (npurged); } static void -arena_chunk_unstash_purged(arena_t *arena, arena_chunk_t *chunk, - arena_chunk_mapelms_t *mapelms) +arena_unstash_purged(arena_t *arena, chunk_hooks_t *chunk_hooks, + arena_runs_dirty_link_t *purge_runs_sentinel, + extent_node_t *purge_chunks_sentinel) { - arena_chunk_map_t *mapelm; - size_t pageind; + arena_runs_dirty_link_t *rdelm, *rdelm_next; + extent_node_t *chunkselm; - /* Deallocate runs. */ - for (mapelm = ql_first(mapelms); mapelm != NULL; - mapelm = ql_first(mapelms)) { - arena_run_t *run; - - pageind = (((uintptr_t)mapelm - (uintptr_t)chunk->map) / - sizeof(arena_chunk_map_t)) + map_bias; - run = (arena_run_t *)((uintptr_t)chunk + (uintptr_t)(pageind << - LG_PAGE)); - ql_remove(mapelms, mapelm, u.ql_link); - arena_run_dalloc(arena, run, false, true); + /* Deallocate chunks/runs. */ + for (rdelm = qr_next(purge_runs_sentinel, rd_link), + chunkselm = qr_next(purge_chunks_sentinel, cc_link); + rdelm != purge_runs_sentinel; rdelm = rdelm_next) { + rdelm_next = qr_next(rdelm, rd_link); + if (rdelm == &chunkselm->rd) { + extent_node_t *chunkselm_next = qr_next(chunkselm, + cc_link); + void *addr = extent_node_addr_get(chunkselm); + size_t size = extent_node_size_get(chunkselm); + bool zeroed = extent_node_zeroed_get(chunkselm); + bool committed = extent_node_committed_get(chunkselm); + extent_node_dirty_remove(chunkselm); + arena_node_dalloc(arena, chunkselm); + chunkselm = chunkselm_next; + chunk_dalloc_arena(arena, chunk_hooks, addr, size, + zeroed, committed); + } else { + arena_chunk_t *chunk = + (arena_chunk_t *)CHUNK_ADDR2BASE(rdelm); + arena_chunk_map_misc_t *miscelm = + arena_rd_to_miscelm(rdelm); + size_t pageind = arena_miscelm_to_pageind(miscelm); + bool decommitted = (arena_mapbits_decommitted_get(chunk, + pageind) != 0); + arena_run_t *run = &miscelm->run; + qr_remove(rdelm, rd_link); + arena_run_dalloc(arena, run, false, true, decommitted); + } } } -static inline size_t -arena_chunk_purge(arena_t *arena, arena_chunk_t *chunk, bool all) -{ - size_t npurged; - arena_chunk_mapelms_t mapelms; - - ql_new(&mapelms); - - /* - * If chunk is the spare, temporarily re-allocate it, 1) so that its - * run is reinserted into runs_avail, and 2) so that it cannot be - * completely discarded by another thread while arena->lock is dropped - * by this thread. Note that the arena_run_dalloc() call will - * implicitly deallocate the chunk, so no explicit action is required - * in this function to deallocate the chunk. - * - * Note that once a chunk contains dirty pages, it cannot again contain - * a single run unless 1) it is a dirty run, or 2) this function purges - * dirty pages and causes the transition to a single clean run. Thus - * (chunk == arena->spare) is possible, but it is not possible for - * this function to be called on the spare unless it contains a dirty - * run. - */ - if (chunk == arena->spare) { - assert(arena_mapbits_dirty_get(chunk, map_bias) != 0); - assert(arena_mapbits_dirty_get(chunk, chunk_npages-1) != 0); - - arena_chunk_alloc(arena); - } - - if (config_stats) - arena->stats.purged += chunk->ndirty; - - /* - * Operate on all dirty runs if there is no clean/dirty run - * fragmentation. - */ - if (chunk->nruns_adjac == 0) - all = true; - - arena_chunk_stash_dirty(arena, chunk, all, &mapelms); - npurged = arena_chunk_purge_stashed(arena, chunk, &mapelms); - arena_chunk_unstash_purged(arena, chunk, &mapelms); - - return (npurged); -} - static void arena_purge(arena_t *arena, bool all) { - arena_chunk_t *chunk; - size_t npurgatory; - if (config_debug) { - size_t ndirty = 0; + chunk_hooks_t chunk_hooks = chunk_hooks_get(arena); + size_t npurge, npurgeable, npurged; + arena_runs_dirty_link_t purge_runs_sentinel; + extent_node_t purge_chunks_sentinel; - arena_chunk_dirty_iter(&arena->chunks_dirty, NULL, - chunks_dirty_iter_cb, (void *)&ndirty); + arena->purging = true; + + /* + * Calls to arena_dirty_count() are disabled even for debug builds + * because overhead grows nonlinearly as memory usage increases. + */ + if (false && config_debug) { + size_t ndirty = arena_dirty_count(arena); assert(ndirty == arena->ndirty); } - assert(arena->ndirty > arena->npurgatory || all); - assert((arena->nactive >> opt_lg_dirty_mult) < (arena->ndirty - - arena->npurgatory) || all); + assert((arena->nactive >> arena->lg_dirty_mult) < arena->ndirty || all); if (config_stats) arena->stats.npurge++; - /* - * Add the minimum number of pages this thread should try to purge to - * arena->npurgatory. This will keep multiple threads from racing to - * reduce ndirty below the threshold. - */ - npurgatory = arena_compute_npurgatory(arena, all); - arena->npurgatory += npurgatory; + npurge = arena_compute_npurge(arena, all); + qr_new(&purge_runs_sentinel, rd_link); + extent_node_dirty_linkage_init(&purge_chunks_sentinel); - while (npurgatory > 0) { - size_t npurgeable, npurged, nunpurged; + npurgeable = arena_stash_dirty(arena, &chunk_hooks, all, npurge, + &purge_runs_sentinel, &purge_chunks_sentinel); + assert(npurgeable >= npurge); + npurged = arena_purge_stashed(arena, &chunk_hooks, &purge_runs_sentinel, + &purge_chunks_sentinel); + assert(npurged == npurgeable); + arena_unstash_purged(arena, &chunk_hooks, &purge_runs_sentinel, + &purge_chunks_sentinel); - /* Get next chunk with dirty pages. */ - chunk = arena_chunk_dirty_first(&arena->chunks_dirty); - if (chunk == NULL) { - /* - * This thread was unable to purge as many pages as - * originally intended, due to races with other threads - * that either did some of the purging work, or re-used - * dirty pages. - */ - arena->npurgatory -= npurgatory; - return; - } - npurgeable = chunk->ndirty; - assert(npurgeable != 0); - - if (npurgeable > npurgatory && chunk->nruns_adjac == 0) { - /* - * This thread will purge all the dirty pages in chunk, - * so set npurgatory to reflect this thread's intent to - * purge the pages. This tends to reduce the chances - * of the following scenario: - * - * 1) This thread sets arena->npurgatory such that - * (arena->ndirty - arena->npurgatory) is at the - * threshold. - * 2) This thread drops arena->lock. - * 3) Another thread causes one or more pages to be - * dirtied, and immediately determines that it must - * purge dirty pages. - * - * If this scenario *does* play out, that's okay, - * because all of the purging work being done really - * needs to happen. - */ - arena->npurgatory += npurgeable - npurgatory; - npurgatory = npurgeable; - } - - /* - * Keep track of how many pages are purgeable, versus how many - * actually get purged, and adjust counters accordingly. - */ - arena->npurgatory -= npurgeable; - npurgatory -= npurgeable; - npurged = arena_chunk_purge(arena, chunk, all); - nunpurged = npurgeable - npurged; - arena->npurgatory += nunpurged; - npurgatory += nunpurged; - } + arena->purging = false; } void @@ -1090,7 +1549,8 @@ arena_purge_all(arena_t *arena) static void arena_run_coalesce(arena_t *arena, arena_chunk_t *chunk, size_t *p_size, - size_t *p_run_ind, size_t *p_run_pages, size_t flag_dirty) + size_t *p_run_ind, size_t *p_run_pages, size_t flag_dirty, + size_t flag_decommitted) { size_t size = *p_size; size_t run_ind = *p_run_ind; @@ -1099,7 +1559,9 @@ arena_run_coalesce(arena_t *arena, arena_chunk_t *chunk, size_t *p_size, /* Try to coalesce forward. */ if (run_ind + run_pages < chunk_npages && arena_mapbits_allocated_get(chunk, run_ind+run_pages) == 0 && - arena_mapbits_dirty_get(chunk, run_ind+run_pages) == flag_dirty) { + arena_mapbits_dirty_get(chunk, run_ind+run_pages) == flag_dirty && + arena_mapbits_decommitted_get(chunk, run_ind+run_pages) == + flag_decommitted) { size_t nrun_size = arena_mapbits_unallocated_size_get(chunk, run_ind+run_pages); size_t nrun_pages = nrun_size >> LG_PAGE; @@ -1112,8 +1574,18 @@ arena_run_coalesce(arena_t *arena, arena_chunk_t *chunk, size_t *p_size, run_ind+run_pages+nrun_pages-1) == nrun_size); assert(arena_mapbits_dirty_get(chunk, run_ind+run_pages+nrun_pages-1) == flag_dirty); - arena_avail_remove(arena, chunk, run_ind+run_pages, nrun_pages, - false, true); + assert(arena_mapbits_decommitted_get(chunk, + run_ind+run_pages+nrun_pages-1) == flag_decommitted); + arena_avail_remove(arena, chunk, run_ind+run_pages, nrun_pages); + + /* + * If the successor is dirty, remove it from the set of dirty + * pages. + */ + if (flag_dirty != 0) { + arena_run_dirty_remove(arena, chunk, run_ind+run_pages, + nrun_pages); + } size += nrun_size; run_pages += nrun_pages; @@ -1126,7 +1598,8 @@ arena_run_coalesce(arena_t *arena, arena_chunk_t *chunk, size_t *p_size, /* Try to coalesce backward. */ if (run_ind > map_bias && arena_mapbits_allocated_get(chunk, run_ind-1) == 0 && arena_mapbits_dirty_get(chunk, run_ind-1) == - flag_dirty) { + flag_dirty && arena_mapbits_decommitted_get(chunk, run_ind-1) == + flag_decommitted) { size_t prun_size = arena_mapbits_unallocated_size_get(chunk, run_ind-1); size_t prun_pages = prun_size >> LG_PAGE; @@ -1140,8 +1613,18 @@ arena_run_coalesce(arena_t *arena, arena_chunk_t *chunk, size_t *p_size, assert(arena_mapbits_unallocated_size_get(chunk, run_ind) == prun_size); assert(arena_mapbits_dirty_get(chunk, run_ind) == flag_dirty); - arena_avail_remove(arena, chunk, run_ind, prun_pages, true, - false); + assert(arena_mapbits_decommitted_get(chunk, run_ind) == + flag_decommitted); + arena_avail_remove(arena, chunk, run_ind, prun_pages); + + /* + * If the predecessor is dirty, remove it from the set of dirty + * pages. + */ + if (flag_dirty != 0) { + arena_run_dirty_remove(arena, chunk, run_ind, + prun_pages); + } size += prun_size; run_pages += prun_pages; @@ -1156,26 +1639,53 @@ arena_run_coalesce(arena_t *arena, arena_chunk_t *chunk, size_t *p_size, *p_run_pages = run_pages; } -static void -arena_run_dalloc(arena_t *arena, arena_run_t *run, bool dirty, bool cleaned) +static size_t +arena_run_size_get(arena_t *arena, arena_chunk_t *chunk, arena_run_t *run, + size_t run_ind) { - arena_chunk_t *chunk; - size_t size, run_ind, run_pages, flag_dirty; + size_t size; - chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(run); - run_ind = (size_t)(((uintptr_t)run - (uintptr_t)chunk) >> LG_PAGE); assert(run_ind >= map_bias); assert(run_ind < chunk_npages); + if (arena_mapbits_large_get(chunk, run_ind) != 0) { size = arena_mapbits_large_size_get(chunk, run_ind); - assert(size == PAGE || - arena_mapbits_large_size_get(chunk, + assert(size == PAGE || arena_mapbits_large_size_get(chunk, run_ind+(size>>LG_PAGE)-1) == 0); } else { - size_t binind = arena_bin_index(arena, run->bin); - arena_bin_info_t *bin_info = &arena_bin_info[binind]; + arena_bin_info_t *bin_info = &arena_bin_info[run->binind]; size = bin_info->run_size; } + + return (size); +} + +static bool +arena_run_decommit(arena_t *arena, arena_chunk_t *chunk, arena_run_t *run) +{ + arena_chunk_map_misc_t *miscelm = arena_run_to_miscelm(run); + size_t run_ind = arena_miscelm_to_pageind(miscelm); + size_t offset = run_ind << LG_PAGE; + size_t length = arena_run_size_get(arena, chunk, run, run_ind); + + return (arena->chunk_hooks.decommit(chunk, chunksize, offset, length, + arena->ind)); +} + +static void +arena_run_dalloc(arena_t *arena, arena_run_t *run, bool dirty, bool cleaned, + bool decommitted) +{ + arena_chunk_t *chunk; + arena_chunk_map_misc_t *miscelm; + size_t size, run_ind, run_pages, flag_dirty, flag_decommitted; + + chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(run); + miscelm = arena_run_to_miscelm(run); + run_ind = arena_miscelm_to_pageind(miscelm); + assert(run_ind >= map_bias); + assert(run_ind < chunk_npages); + size = arena_run_size_get(arena, chunk, run, run_ind); run_pages = (size >> LG_PAGE); arena_cactive_update(arena, 0, run_pages); arena->nactive -= run_pages; @@ -1187,16 +1697,18 @@ arena_run_dalloc(arena_t *arena, arena_run_t *run, bool dirty, bool cleaned) */ assert(arena_mapbits_dirty_get(chunk, run_ind) == arena_mapbits_dirty_get(chunk, run_ind+run_pages-1)); - if (cleaned == false && arena_mapbits_dirty_get(chunk, run_ind) != 0) + if (!cleaned && !decommitted && arena_mapbits_dirty_get(chunk, run_ind) + != 0) dirty = true; flag_dirty = dirty ? CHUNK_MAP_DIRTY : 0; + flag_decommitted = decommitted ? CHUNK_MAP_DECOMMITTED : 0; /* Mark pages as unallocated in the chunk map. */ - if (dirty) { - arena_mapbits_unallocated_set(chunk, run_ind, size, - CHUNK_MAP_DIRTY); + if (dirty || decommitted) { + size_t flags = flag_dirty | flag_decommitted; + arena_mapbits_unallocated_set(chunk, run_ind, size, flags); arena_mapbits_unallocated_set(chunk, run_ind+run_pages-1, size, - CHUNK_MAP_DIRTY); + flags); } else { arena_mapbits_unallocated_set(chunk, run_ind, size, arena_mapbits_unzeroed_get(chunk, run_ind)); @@ -1205,20 +1717,25 @@ arena_run_dalloc(arena_t *arena, arena_run_t *run, bool dirty, bool cleaned) } arena_run_coalesce(arena, chunk, &size, &run_ind, &run_pages, - flag_dirty); + flag_dirty, flag_decommitted); /* Insert into runs_avail, now that coalescing is complete. */ assert(arena_mapbits_unallocated_size_get(chunk, run_ind) == arena_mapbits_unallocated_size_get(chunk, run_ind+run_pages-1)); assert(arena_mapbits_dirty_get(chunk, run_ind) == arena_mapbits_dirty_get(chunk, run_ind+run_pages-1)); - arena_avail_insert(arena, chunk, run_ind, run_pages, true, true); + assert(arena_mapbits_decommitted_get(chunk, run_ind) == + arena_mapbits_decommitted_get(chunk, run_ind+run_pages-1)); + arena_avail_insert(arena, chunk, run_ind, run_pages); + + if (dirty) + arena_run_dirty_insert(arena, chunk, run_ind, run_pages); /* Deallocate chunk if it is now completely unused. */ - if (size == arena_maxclass) { + if (size == arena_maxrun) { assert(run_ind == map_bias); - assert(run_pages == (arena_maxclass >> LG_PAGE)); - arena_chunk_dealloc(arena, chunk); + assert(run_pages == (arena_maxrun >> LG_PAGE)); + arena_chunk_dalloc(arena, chunk); } /* @@ -1232,13 +1749,26 @@ arena_run_dalloc(arena_t *arena, arena_run_t *run, bool dirty, bool cleaned) arena_maybe_purge(arena); } +static void +arena_run_dalloc_decommit(arena_t *arena, arena_chunk_t *chunk, + arena_run_t *run) +{ + bool committed = arena_run_decommit(arena, chunk, run); + + arena_run_dalloc(arena, run, committed, false, !committed); +} + static void arena_run_trim_head(arena_t *arena, arena_chunk_t *chunk, arena_run_t *run, size_t oldsize, size_t newsize) { - size_t pageind = ((uintptr_t)run - (uintptr_t)chunk) >> LG_PAGE; + arena_chunk_map_misc_t *miscelm = arena_run_to_miscelm(run); + size_t pageind = arena_miscelm_to_pageind(miscelm); size_t head_npages = (oldsize - newsize) >> LG_PAGE; size_t flag_dirty = arena_mapbits_dirty_get(chunk, pageind); + size_t flag_decommitted = arena_mapbits_decommitted_get(chunk, pageind); + size_t flag_unzeroed_mask = (flag_dirty | flag_decommitted) == 0 ? + CHUNK_MAP_UNZEROED : 0; assert(oldsize > newsize); @@ -1248,8 +1778,11 @@ arena_run_trim_head(arena_t *arena, arena_chunk_t *chunk, arena_run_t *run, * run first, in case of single-page runs. */ assert(arena_mapbits_large_size_get(chunk, pageind) == oldsize); - arena_mapbits_large_set(chunk, pageind+head_npages-1, 0, flag_dirty); - arena_mapbits_large_set(chunk, pageind, oldsize-newsize, flag_dirty); + arena_mapbits_large_set(chunk, pageind+head_npages-1, 0, flag_dirty | + (flag_unzeroed_mask & arena_mapbits_unzeroed_get(chunk, + pageind+head_npages-1))); + arena_mapbits_large_set(chunk, pageind, oldsize-newsize, flag_dirty | + (flag_unzeroed_mask & arena_mapbits_unzeroed_get(chunk, pageind))); if (config_debug) { UNUSED size_t tail_npages = newsize >> LG_PAGE; @@ -1259,18 +1792,25 @@ arena_run_trim_head(arena_t *arena, arena_chunk_t *chunk, arena_run_t *run, pageind+head_npages+tail_npages-1) == flag_dirty); } arena_mapbits_large_set(chunk, pageind+head_npages, newsize, - flag_dirty); + flag_dirty | (flag_unzeroed_mask & arena_mapbits_unzeroed_get(chunk, + pageind+head_npages))); - arena_run_dalloc(arena, run, false, false); + arena_run_dalloc(arena, run, false, false, (flag_decommitted != 0)); } static void arena_run_trim_tail(arena_t *arena, arena_chunk_t *chunk, arena_run_t *run, size_t oldsize, size_t newsize, bool dirty) { - size_t pageind = ((uintptr_t)run - (uintptr_t)chunk) >> LG_PAGE; + arena_chunk_map_misc_t *miscelm = arena_run_to_miscelm(run); + size_t pageind = arena_miscelm_to_pageind(miscelm); size_t head_npages = newsize >> LG_PAGE; size_t flag_dirty = arena_mapbits_dirty_get(chunk, pageind); + size_t flag_decommitted = arena_mapbits_decommitted_get(chunk, pageind); + size_t flag_unzeroed_mask = (flag_dirty | flag_decommitted) == 0 ? + CHUNK_MAP_UNZEROED : 0; + arena_chunk_map_misc_t *tail_miscelm; + arena_run_t *tail_run; assert(oldsize > newsize); @@ -1280,8 +1820,11 @@ arena_run_trim_tail(arena_t *arena, arena_chunk_t *chunk, arena_run_t *run, * run first, in case of single-page runs. */ assert(arena_mapbits_large_size_get(chunk, pageind) == oldsize); - arena_mapbits_large_set(chunk, pageind+head_npages-1, 0, flag_dirty); - arena_mapbits_large_set(chunk, pageind, newsize, flag_dirty); + arena_mapbits_large_set(chunk, pageind+head_npages-1, 0, flag_dirty | + (flag_unzeroed_mask & arena_mapbits_unzeroed_get(chunk, + pageind+head_npages-1))); + arena_mapbits_large_set(chunk, pageind, newsize, flag_dirty | + (flag_unzeroed_mask & arena_mapbits_unzeroed_get(chunk, pageind))); if (config_debug) { UNUSED size_t tail_npages = (oldsize - newsize) >> LG_PAGE; @@ -1291,29 +1834,21 @@ arena_run_trim_tail(arena_t *arena, arena_chunk_t *chunk, arena_run_t *run, pageind+head_npages+tail_npages-1) == flag_dirty); } arena_mapbits_large_set(chunk, pageind+head_npages, oldsize-newsize, - flag_dirty); + flag_dirty | (flag_unzeroed_mask & arena_mapbits_unzeroed_get(chunk, + pageind+head_npages))); - arena_run_dalloc(arena, (arena_run_t *)((uintptr_t)run + newsize), - dirty, false); + tail_miscelm = arena_miscelm_get(chunk, pageind + head_npages); + tail_run = &tail_miscelm->run; + arena_run_dalloc(arena, tail_run, dirty, false, (flag_decommitted != + 0)); } static arena_run_t * arena_bin_runs_first(arena_bin_t *bin) { - arena_chunk_map_t *mapelm = arena_run_tree_first(&bin->runs); - if (mapelm != NULL) { - arena_chunk_t *chunk; - size_t pageind; - arena_run_t *run; - - chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(mapelm); - pageind = ((((uintptr_t)mapelm - (uintptr_t)chunk->map) / - sizeof(arena_chunk_map_t))) + map_bias; - run = (arena_run_t *)((uintptr_t)chunk + (uintptr_t)((pageind - - arena_mapbits_small_runind_get(chunk, pageind)) << - LG_PAGE)); - return (run); - } + arena_chunk_map_misc_t *miscelm = arena_run_tree_first(&bin->runs); + if (miscelm != NULL) + return (&miscelm->run); return (NULL); } @@ -1321,25 +1856,21 @@ arena_bin_runs_first(arena_bin_t *bin) static void arena_bin_runs_insert(arena_bin_t *bin, arena_run_t *run) { - arena_chunk_t *chunk = CHUNK_ADDR2BASE(run); - size_t pageind = ((uintptr_t)run - (uintptr_t)chunk) >> LG_PAGE; - arena_chunk_map_t *mapelm = arena_mapp_get(chunk, pageind); + arena_chunk_map_misc_t *miscelm = arena_run_to_miscelm(run); - assert(arena_run_tree_search(&bin->runs, mapelm) == NULL); + assert(arena_run_tree_search(&bin->runs, miscelm) == NULL); - arena_run_tree_insert(&bin->runs, mapelm); + arena_run_tree_insert(&bin->runs, miscelm); } static void arena_bin_runs_remove(arena_bin_t *bin, arena_run_t *run) { - arena_chunk_t *chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(run); - size_t pageind = ((uintptr_t)run - (uintptr_t)chunk) >> LG_PAGE; - arena_chunk_map_t *mapelm = arena_mapp_get(chunk, pageind); + arena_chunk_map_misc_t *miscelm = arena_run_to_miscelm(run); - assert(arena_run_tree_search(&bin->runs, mapelm) != NULL); + assert(arena_run_tree_search(&bin->runs, miscelm) != NULL); - arena_run_tree_remove(&bin->runs, mapelm); + arena_run_tree_remove(&bin->runs, miscelm); } static arena_run_t * @@ -1358,7 +1889,7 @@ static arena_run_t * arena_bin_nonfull_run_get(arena_t *arena, arena_bin_t *bin) { arena_run_t *run; - size_t binind; + index_t binind; arena_bin_info_t *bin_info; /* Look for a usable run. */ @@ -1376,14 +1907,10 @@ arena_bin_nonfull_run_get(arena_t *arena, arena_bin_t *bin) malloc_mutex_lock(&arena->lock); run = arena_run_alloc_small(arena, bin_info->run_size, binind); if (run != NULL) { - bitmap_t *bitmap = (bitmap_t *)((uintptr_t)run + - (uintptr_t)bin_info->bitmap_offset); - /* Initialize run internals. */ - run->bin = bin; - run->nextind = 0; + run->binind = binind; run->nfree = bin_info->nregs; - bitmap_init(bitmap, &bin_info->bitmap_info); + bitmap_init(run->bitmap, &bin_info->bitmap_info); } malloc_mutex_unlock(&arena->lock); /********************************/ @@ -1413,7 +1940,7 @@ static void * arena_bin_malloc_hard(arena_t *arena, arena_bin_t *bin) { void *ret; - size_t binind; + index_t binind; arena_bin_info_t *bin_info; arena_run_t *run; @@ -1459,7 +1986,7 @@ arena_bin_malloc_hard(arena_t *arena, arena_bin_t *bin) } void -arena_tcache_fill_small(arena_t *arena, tcache_bin_t *tbin, size_t binind, +arena_tcache_fill_small(arena_t *arena, tcache_bin_t *tbin, index_t binind, uint64_t prof_accumbytes) { unsigned i, nfill; @@ -1479,9 +2006,20 @@ arena_tcache_fill_small(arena_t *arena, tcache_bin_t *tbin, size_t binind, ptr = arena_run_reg_alloc(run, &arena_bin_info[binind]); else ptr = arena_bin_malloc_hard(arena, bin); - if (ptr == NULL) + if (ptr == NULL) { + /* + * OOM. tbin->avail isn't yet filled down to its first + * element, so the successful allocations (if any) must + * be moved to the base of tbin->avail before bailing + * out. + */ + if (i > 0) { + memmove(tbin->avail, &tbin->avail[nfill - i], + i * sizeof(void *)); + } break; - if (config_fill && opt_junk) { + } + if (config_fill && unlikely(opt_junk_alloc)) { arena_alloc_junk_small(ptr, &arena_bin_info[binind], true); } @@ -1489,9 +2027,9 @@ arena_tcache_fill_small(arena_t *arena, tcache_bin_t *tbin, size_t binind, tbin->avail[nfill - 1 - i] = ptr; } if (config_stats) { - bin->stats.allocated += i * arena_bin_info[binind].reg_size; bin->stats.nmalloc += i; bin->stats.nrequests += tbin->tstats.nrequests; + bin->stats.curregs += i; bin->stats.nfills++; tbin->tstats.nrequests = 0; } @@ -1543,24 +2081,29 @@ arena_redzones_validate(void *ptr, arena_bin_info_t *bin_info, bool reset) size_t i; bool error = false; - for (i = 1; i <= redzone_size; i++) { - uint8_t *byte = (uint8_t *)((uintptr_t)ptr - i); - if (*byte != 0xa5) { - error = true; - arena_redzone_corruption(ptr, size, false, i, *byte); - if (reset) - *byte = 0xa5; - } - } - for (i = 0; i < redzone_size; i++) { - uint8_t *byte = (uint8_t *)((uintptr_t)ptr + size + i); - if (*byte != 0xa5) { - error = true; - arena_redzone_corruption(ptr, size, true, i, *byte); - if (reset) - *byte = 0xa5; + if (opt_junk_alloc) { + for (i = 1; i <= redzone_size; i++) { + uint8_t *byte = (uint8_t *)((uintptr_t)ptr - i); + if (*byte != 0xa5) { + error = true; + arena_redzone_corruption(ptr, size, false, i, + *byte); + if (reset) + *byte = 0xa5; + } + } + for (i = 0; i < redzone_size; i++) { + uint8_t *byte = (uint8_t *)((uintptr_t)ptr + size + i); + if (*byte != 0xa5) { + error = true; + arena_redzone_corruption(ptr, size, true, i, + *byte); + if (reset) + *byte = 0xa5; + } } } + if (opt_abort && error) abort(); } @@ -1588,14 +2131,14 @@ arena_dalloc_junk_small_t *arena_dalloc_junk_small = void arena_quarantine_junk_small(void *ptr, size_t usize) { - size_t binind; + index_t binind; arena_bin_info_t *bin_info; cassert(config_fill); - assert(opt_junk); + assert(opt_junk_free); assert(opt_quarantine); assert(usize <= SMALL_MAXCLASS); - binind = SMALL_SIZE2BIN(usize); + binind = size2index(usize); bin_info = &arena_bin_info[binind]; arena_redzones_validate(ptr, bin_info, true); } @@ -1606,12 +2149,12 @@ arena_malloc_small(arena_t *arena, size_t size, bool zero) void *ret; arena_bin_t *bin; arena_run_t *run; - size_t binind; + index_t binind; - binind = SMALL_SIZE2BIN(size); + binind = size2index(size); assert(binind < NBINS); bin = &arena->bins[binind]; - size = arena_bin_info[binind].reg_size; + size = index2size(binind); malloc_mutex_lock(&bin->lock); if ((run = bin->runcur) != NULL && run->nfree > 0) @@ -1625,29 +2168,29 @@ arena_malloc_small(arena_t *arena, size_t size, bool zero) } if (config_stats) { - bin->stats.allocated += size; bin->stats.nmalloc++; bin->stats.nrequests++; + bin->stats.curregs++; } malloc_mutex_unlock(&bin->lock); - if (config_prof && isthreaded == false && arena_prof_accum(arena, size)) + if (config_prof && !isthreaded && arena_prof_accum(arena, size)) prof_idump(); - if (zero == false) { + if (!zero) { if (config_fill) { - if (opt_junk) { + if (unlikely(opt_junk_alloc)) { arena_alloc_junk_small(ret, &arena_bin_info[binind], false); - } else if (opt_zero) + } else if (unlikely(opt_zero)) memset(ret, 0, size); } - VALGRIND_MAKE_MEM_UNDEFINED(ret, size); + JEMALLOC_VALGRIND_MAKE_MEM_UNDEFINED(ret, size); } else { - if (config_fill && opt_junk) { + if (config_fill && unlikely(opt_junk_alloc)) { arena_alloc_junk_small(ret, &arena_bin_info[binind], true); } - VALGRIND_MAKE_MEM_UNDEFINED(ret, size); + JEMALLOC_VALGRIND_MAKE_MEM_UNDEFINED(ret, size); memset(ret, 0, size); } @@ -1658,36 +2201,59 @@ void * arena_malloc_large(arena_t *arena, size_t size, bool zero) { void *ret; + size_t usize; + uintptr_t random_offset; + arena_run_t *run; + arena_chunk_map_misc_t *miscelm; UNUSED bool idump; /* Large allocation. */ - size = PAGE_CEILING(size); + usize = s2u(size); malloc_mutex_lock(&arena->lock); - ret = (void *)arena_run_alloc_large(arena, size, zero); - if (ret == NULL) { + if (config_cache_oblivious) { + uint64_t r; + + /* + * Compute a uniformly distributed offset within the first page + * that is a multiple of the cacheline size, e.g. [0 .. 63) * 64 + * for 4 KiB pages and 64-byte cachelines. + */ + prng64(r, LG_PAGE - LG_CACHELINE, arena->offset_state, + UINT64_C(6364136223846793009), + UINT64_C(1442695040888963409)); + random_offset = ((uintptr_t)r) << LG_CACHELINE; + } else + random_offset = 0; + run = arena_run_alloc_large(arena, usize + large_pad, zero); + if (run == NULL) { malloc_mutex_unlock(&arena->lock); return (NULL); } + miscelm = arena_run_to_miscelm(run); + ret = (void *)((uintptr_t)arena_miscelm_to_rpages(miscelm) + + random_offset); if (config_stats) { + index_t index = size2index(usize) - NBINS; + arena->stats.nmalloc_large++; arena->stats.nrequests_large++; - arena->stats.allocated_large += size; - arena->stats.lstats[(size >> LG_PAGE) - 1].nmalloc++; - arena->stats.lstats[(size >> LG_PAGE) - 1].nrequests++; - arena->stats.lstats[(size >> LG_PAGE) - 1].curruns++; + arena->stats.allocated_large += usize; + arena->stats.lstats[index].nmalloc++; + arena->stats.lstats[index].nrequests++; + arena->stats.lstats[index].curruns++; } if (config_prof) - idump = arena_prof_accum_locked(arena, size); + idump = arena_prof_accum_locked(arena, usize); malloc_mutex_unlock(&arena->lock); if (config_prof && idump) prof_idump(); - if (zero == false) { + if (!zero) { if (config_fill) { - if (opt_junk) - memset(ret, 0xa5, size); - else if (opt_zero) - memset(ret, 0, size); + if (unlikely(opt_junk_alloc)) + memset(ret, 0xa5, usize); + else if (unlikely(opt_zero)) + memset(ret, 0, usize); } } @@ -1695,18 +2261,25 @@ arena_malloc_large(arena_t *arena, size_t size, bool zero) } /* Only handles large allocations that require more than page alignment. */ -void * -arena_palloc(arena_t *arena, size_t size, size_t alignment, bool zero) +static void * +arena_palloc_large(tsd_t *tsd, arena_t *arena, size_t usize, size_t alignment, + bool zero) { void *ret; size_t alloc_size, leadsize, trailsize; arena_run_t *run; arena_chunk_t *chunk; + arena_chunk_map_misc_t *miscelm; + void *rpages; - assert((size & PAGE_MASK) == 0); + assert(usize == PAGE_CEILING(usize)); + + arena = arena_choose(tsd, arena); + if (unlikely(arena == NULL)) + return (NULL); alignment = PAGE_CEILING(alignment); - alloc_size = size + alignment - PAGE; + alloc_size = usize + large_pad + alignment - PAGE; malloc_mutex_lock(&arena->lock); run = arena_run_alloc_large(arena, alloc_size, false); @@ -1715,37 +2288,94 @@ arena_palloc(arena_t *arena, size_t size, size_t alignment, bool zero) return (NULL); } chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(run); + miscelm = arena_run_to_miscelm(run); + rpages = arena_miscelm_to_rpages(miscelm); - leadsize = ALIGNMENT_CEILING((uintptr_t)run, alignment) - - (uintptr_t)run; - assert(alloc_size >= leadsize + size); - trailsize = alloc_size - leadsize - size; - ret = (void *)((uintptr_t)run + leadsize); + leadsize = ALIGNMENT_CEILING((uintptr_t)rpages, alignment) - + (uintptr_t)rpages; + assert(alloc_size >= leadsize + usize); + trailsize = alloc_size - leadsize - usize - large_pad; if (leadsize != 0) { - arena_run_trim_head(arena, chunk, run, alloc_size, alloc_size - - leadsize); + arena_chunk_map_misc_t *head_miscelm = miscelm; + arena_run_t *head_run = run; + + miscelm = arena_miscelm_get(chunk, + arena_miscelm_to_pageind(head_miscelm) + (leadsize >> + LG_PAGE)); + run = &miscelm->run; + + arena_run_trim_head(arena, chunk, head_run, alloc_size, + alloc_size - leadsize); } if (trailsize != 0) { - arena_run_trim_tail(arena, chunk, ret, size + trailsize, size, - false); + arena_run_trim_tail(arena, chunk, run, usize + large_pad + + trailsize, usize + large_pad, false); } - arena_run_init_large(arena, (arena_run_t *)ret, size, zero); + if (arena_run_init_large(arena, run, usize + large_pad, zero)) { + size_t run_ind = + arena_miscelm_to_pageind(arena_run_to_miscelm(run)); + bool dirty = (arena_mapbits_dirty_get(chunk, run_ind) != 0); + bool decommitted = (arena_mapbits_decommitted_get(chunk, + run_ind) != 0); + + assert(decommitted); /* Cause of OOM. */ + arena_run_dalloc(arena, run, dirty, false, decommitted); + malloc_mutex_unlock(&arena->lock); + return (NULL); + } + ret = arena_miscelm_to_rpages(miscelm); if (config_stats) { + index_t index = size2index(usize) - NBINS; + arena->stats.nmalloc_large++; arena->stats.nrequests_large++; - arena->stats.allocated_large += size; - arena->stats.lstats[(size >> LG_PAGE) - 1].nmalloc++; - arena->stats.lstats[(size >> LG_PAGE) - 1].nrequests++; - arena->stats.lstats[(size >> LG_PAGE) - 1].curruns++; + arena->stats.allocated_large += usize; + arena->stats.lstats[index].nmalloc++; + arena->stats.lstats[index].nrequests++; + arena->stats.lstats[index].curruns++; } malloc_mutex_unlock(&arena->lock); - if (config_fill && zero == false) { - if (opt_junk) - memset(ret, 0xa5, size); - else if (opt_zero) - memset(ret, 0, size); + if (config_fill && !zero) { + if (unlikely(opt_junk_alloc)) + memset(ret, 0xa5, usize); + else if (unlikely(opt_zero)) + memset(ret, 0, usize); + } + return (ret); +} + +void * +arena_palloc(tsd_t *tsd, arena_t *arena, size_t usize, size_t alignment, + bool zero, tcache_t *tcache) +{ + void *ret; + + if (usize <= SMALL_MAXCLASS && (alignment < PAGE || (alignment == PAGE + && (usize & PAGE_MASK) == 0))) { + /* Small; alignment doesn't require special run placement. */ + ret = arena_malloc(tsd, arena, usize, zero, tcache); + } else if (usize <= arena_maxclass && alignment <= PAGE) { + /* + * Large; alignment doesn't require special run placement. + * However, the cached pointer may be at a random offset from + * the base of the run, so do some bit manipulation to retrieve + * the base. + */ + ret = arena_malloc(tsd, arena, usize, zero, tcache); + if (config_cache_oblivious) + ret = (void *)((uintptr_t)ret & ~PAGE_MASK); + } else { + if (likely(usize <= arena_maxclass)) { + ret = arena_palloc_large(tsd, arena, usize, alignment, + zero); + } else if (likely(alignment <= chunksize)) + ret = huge_malloc(tsd, arena, usize, zero, tcache); + else { + ret = huge_palloc(tsd, arena, usize, alignment, zero, + tcache); + } } return (ret); } @@ -1754,22 +2384,23 @@ void arena_prof_promoted(const void *ptr, size_t size) { arena_chunk_t *chunk; - size_t pageind, binind; + size_t pageind; + index_t binind; cassert(config_prof); assert(ptr != NULL); assert(CHUNK_ADDR2BASE(ptr) != ptr); - assert(isalloc(ptr, false) == PAGE); - assert(isalloc(ptr, true) == PAGE); + assert(isalloc(ptr, false) == LARGE_MINCLASS); + assert(isalloc(ptr, true) == LARGE_MINCLASS); assert(size <= SMALL_MAXCLASS); chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr); pageind = ((uintptr_t)ptr - (uintptr_t)chunk) >> LG_PAGE; - binind = SMALL_SIZE2BIN(size); + binind = size2index(size); assert(binind < NBINS); arena_mapbits_large_binind_set(chunk, pageind, binind); - assert(isalloc(ptr, false) == PAGE); + assert(isalloc(ptr, false) == LARGE_MINCLASS); assert(isalloc(ptr, true) == size); } @@ -1782,7 +2413,8 @@ arena_dissociate_bin_run(arena_chunk_t *chunk, arena_run_t *run, if (run == bin->runcur) bin->runcur = NULL; else { - size_t binind = arena_bin_index(chunk->arena, bin); + index_t binind = arena_bin_index(extent_node_arena_get( + &chunk->node), bin); arena_bin_info_t *bin_info = &arena_bin_info[binind]; if (bin_info->nregs != 1) { @@ -1800,46 +2432,15 @@ static void arena_dalloc_bin_run(arena_t *arena, arena_chunk_t *chunk, arena_run_t *run, arena_bin_t *bin) { - size_t binind; - arena_bin_info_t *bin_info; - size_t npages, run_ind, past; assert(run != bin->runcur); - assert(arena_run_tree_search(&bin->runs, - arena_mapp_get(chunk, ((uintptr_t)run-(uintptr_t)chunk)>>LG_PAGE)) - == NULL); - - binind = arena_bin_index(chunk->arena, run->bin); - bin_info = &arena_bin_info[binind]; + assert(arena_run_tree_search(&bin->runs, arena_run_to_miscelm(run)) == + NULL); malloc_mutex_unlock(&bin->lock); /******************************/ - npages = bin_info->run_size >> LG_PAGE; - run_ind = (size_t)(((uintptr_t)run - (uintptr_t)chunk) >> LG_PAGE); - past = (size_t)(PAGE_CEILING((uintptr_t)run + - (uintptr_t)bin_info->reg0_offset + (uintptr_t)(run->nextind * - bin_info->reg_interval - bin_info->redzone_size) - - (uintptr_t)chunk) >> LG_PAGE); malloc_mutex_lock(&arena->lock); - - /* - * If the run was originally clean, and some pages were never touched, - * trim the clean pages before deallocating the dirty portion of the - * run. - */ - assert(arena_mapbits_dirty_get(chunk, run_ind) == - arena_mapbits_dirty_get(chunk, run_ind+npages-1)); - if (arena_mapbits_dirty_get(chunk, run_ind) == 0 && past - run_ind < - npages) { - /* Trim clean pages. Convert to large run beforehand. */ - assert(npages > 0); - arena_mapbits_large_set(chunk, run_ind, bin_info->run_size, 0); - arena_mapbits_large_set(chunk, run_ind+npages-1, 0, 0); - arena_run_trim_tail(arena, chunk, run, (npages << LG_PAGE), - ((past - run_ind) << LG_PAGE), false); - /* npages = past - run_ind; */ - } - arena_run_dalloc(arena, run, true, false); + arena_run_dalloc_decommit(arena, chunk, run); malloc_mutex_unlock(&arena->lock); /****************************/ malloc_mutex_lock(&bin->lock); @@ -1868,26 +2469,24 @@ arena_bin_lower_run(arena_t *arena, arena_chunk_t *chunk, arena_run_t *run, arena_bin_runs_insert(bin, run); } -void -arena_dalloc_bin_locked(arena_t *arena, arena_chunk_t *chunk, void *ptr, - arena_chunk_map_t *mapelm) +static void +arena_dalloc_bin_locked_impl(arena_t *arena, arena_chunk_t *chunk, void *ptr, + arena_chunk_map_bits_t *bitselm, bool junked) { - size_t pageind; + size_t pageind, rpages_ind; arena_run_t *run; arena_bin_t *bin; arena_bin_info_t *bin_info; - size_t size, binind; + index_t binind; pageind = ((uintptr_t)ptr - (uintptr_t)chunk) >> LG_PAGE; - run = (arena_run_t *)((uintptr_t)chunk + (uintptr_t)((pageind - - arena_mapbits_small_runind_get(chunk, pageind)) << LG_PAGE)); - bin = run->bin; - binind = arena_ptr_small_binind_get(ptr, mapelm->bits); + rpages_ind = pageind - arena_mapbits_small_runind_get(chunk, pageind); + run = &arena_miscelm_get(chunk, rpages_ind)->run; + binind = run->binind; + bin = &arena->bins[binind]; bin_info = &arena_bin_info[binind]; - if (config_fill || config_stats) - size = bin_info->reg_size; - if (config_fill && opt_junk) + if (!junked && config_fill && unlikely(opt_junk_free)) arena_dalloc_junk_small(ptr, bin_info); arena_run_reg_dalloc(run, ptr); @@ -1898,23 +2497,32 @@ arena_dalloc_bin_locked(arena_t *arena, arena_chunk_t *chunk, void *ptr, arena_bin_lower_run(arena, chunk, run, bin); if (config_stats) { - bin->stats.allocated -= size; bin->stats.ndalloc++; + bin->stats.curregs--; } } +void +arena_dalloc_bin_junked_locked(arena_t *arena, arena_chunk_t *chunk, void *ptr, + arena_chunk_map_bits_t *bitselm) +{ + + arena_dalloc_bin_locked_impl(arena, chunk, ptr, bitselm, true); +} + void arena_dalloc_bin(arena_t *arena, arena_chunk_t *chunk, void *ptr, - size_t pageind, arena_chunk_map_t *mapelm) + size_t pageind, arena_chunk_map_bits_t *bitselm) { arena_run_t *run; arena_bin_t *bin; + size_t rpages_ind; - run = (arena_run_t *)((uintptr_t)chunk + (uintptr_t)((pageind - - arena_mapbits_small_runind_get(chunk, pageind)) << LG_PAGE)); - bin = run->bin; + rpages_ind = pageind - arena_mapbits_small_runind_get(chunk, pageind); + run = &arena_miscelm_get(chunk, rpages_ind)->run; + bin = &arena->bins[run->binind]; malloc_mutex_lock(&bin->lock); - arena_dalloc_bin_locked(arena, chunk, ptr, mapelm); + arena_dalloc_bin_locked_impl(arena, chunk, ptr, bitselm, false); malloc_mutex_unlock(&bin->lock); } @@ -1922,26 +2530,26 @@ void arena_dalloc_small(arena_t *arena, arena_chunk_t *chunk, void *ptr, size_t pageind) { - arena_chunk_map_t *mapelm; + arena_chunk_map_bits_t *bitselm; if (config_debug) { /* arena_ptr_small_binind_get() does extra sanity checking. */ assert(arena_ptr_small_binind_get(ptr, arena_mapbits_get(chunk, pageind)) != BININD_INVALID); } - mapelm = arena_mapp_get(chunk, pageind); - arena_dalloc_bin(arena, chunk, ptr, pageind, mapelm); + bitselm = arena_bitselm_get(chunk, pageind); + arena_dalloc_bin(arena, chunk, ptr, pageind, bitselm); } #ifdef JEMALLOC_JET #undef arena_dalloc_junk_large #define arena_dalloc_junk_large JEMALLOC_N(arena_dalloc_junk_large_impl) #endif -static void +void arena_dalloc_junk_large(void *ptr, size_t usize) { - if (config_fill && opt_junk) + if (config_fill && unlikely(opt_junk_free)) memset(ptr, 0x5a, usize); } #ifdef JEMALLOC_JET @@ -1952,23 +2560,38 @@ arena_dalloc_junk_large_t *arena_dalloc_junk_large = #endif void -arena_dalloc_large_locked(arena_t *arena, arena_chunk_t *chunk, void *ptr) +arena_dalloc_large_locked_impl(arena_t *arena, arena_chunk_t *chunk, + void *ptr, bool junked) { + size_t pageind = ((uintptr_t)ptr - (uintptr_t)chunk) >> LG_PAGE; + arena_chunk_map_misc_t *miscelm = arena_miscelm_get(chunk, pageind); + arena_run_t *run = &miscelm->run; if (config_fill || config_stats) { - size_t pageind = ((uintptr_t)ptr - (uintptr_t)chunk) >> LG_PAGE; - size_t usize = arena_mapbits_large_size_get(chunk, pageind); + size_t usize = arena_mapbits_large_size_get(chunk, pageind) - + large_pad; - arena_dalloc_junk_large(ptr, usize); + if (!junked) + arena_dalloc_junk_large(ptr, usize); if (config_stats) { + index_t index = size2index(usize) - NBINS; + arena->stats.ndalloc_large++; arena->stats.allocated_large -= usize; - arena->stats.lstats[(usize >> LG_PAGE) - 1].ndalloc++; - arena->stats.lstats[(usize >> LG_PAGE) - 1].curruns--; + arena->stats.lstats[index].ndalloc++; + arena->stats.lstats[index].curruns--; } } - arena_run_dalloc(arena, (arena_run_t *)ptr, true, false); + arena_run_dalloc_decommit(arena, chunk, run); +} + +void +arena_dalloc_large_junked_locked(arena_t *arena, arena_chunk_t *chunk, + void *ptr) +{ + + arena_dalloc_large_locked_impl(arena, chunk, ptr, true); } void @@ -1976,7 +2599,7 @@ arena_dalloc_large(arena_t *arena, arena_chunk_t *chunk, void *ptr) { malloc_mutex_lock(&arena->lock); - arena_dalloc_large_locked(arena, chunk, ptr); + arena_dalloc_large_locked_impl(arena, chunk, ptr, false); malloc_mutex_unlock(&arena->lock); } @@ -1984,6 +2607,9 @@ static void arena_ralloc_large_shrink(arena_t *arena, arena_chunk_t *chunk, void *ptr, size_t oldsize, size_t size) { + size_t pageind = ((uintptr_t)ptr - (uintptr_t)chunk) >> LG_PAGE; + arena_chunk_map_misc_t *miscelm = arena_miscelm_get(chunk, pageind); + arena_run_t *run = &miscelm->run; assert(size < oldsize); @@ -1992,20 +2618,23 @@ arena_ralloc_large_shrink(arena_t *arena, arena_chunk_t *chunk, void *ptr, * allocations. */ malloc_mutex_lock(&arena->lock); - arena_run_trim_tail(arena, chunk, (arena_run_t *)ptr, oldsize, size, - true); + arena_run_trim_tail(arena, chunk, run, oldsize + large_pad, size + + large_pad, true); if (config_stats) { + index_t oldindex = size2index(oldsize) - NBINS; + index_t index = size2index(size) - NBINS; + arena->stats.ndalloc_large++; arena->stats.allocated_large -= oldsize; - arena->stats.lstats[(oldsize >> LG_PAGE) - 1].ndalloc++; - arena->stats.lstats[(oldsize >> LG_PAGE) - 1].curruns--; + arena->stats.lstats[oldindex].ndalloc++; + arena->stats.lstats[oldindex].curruns--; arena->stats.nmalloc_large++; arena->stats.nrequests_large++; arena->stats.allocated_large += size; - arena->stats.lstats[(size >> LG_PAGE) - 1].nmalloc++; - arena->stats.lstats[(size >> LG_PAGE) - 1].nrequests++; - arena->stats.lstats[(size >> LG_PAGE) - 1].curruns++; + arena->stats.lstats[index].nmalloc++; + arena->stats.lstats[index].nrequests++; + arena->stats.lstats[index].curruns++; } malloc_mutex_unlock(&arena->lock); } @@ -2015,31 +2644,42 @@ arena_ralloc_large_grow(arena_t *arena, arena_chunk_t *chunk, void *ptr, size_t oldsize, size_t size, size_t extra, bool zero) { size_t pageind = ((uintptr_t)ptr - (uintptr_t)chunk) >> LG_PAGE; - size_t npages = oldsize >> LG_PAGE; + size_t npages = (oldsize + large_pad) >> LG_PAGE; size_t followsize; + size_t usize_min = s2u(size); - assert(oldsize == arena_mapbits_large_size_get(chunk, pageind)); + assert(oldsize == arena_mapbits_large_size_get(chunk, pageind) - + large_pad); /* Try to extend the run. */ - assert(size + extra > oldsize); + assert(usize_min > oldsize); malloc_mutex_lock(&arena->lock); - if (pageind + npages < chunk_npages && + if (pageind+npages < chunk_npages && arena_mapbits_allocated_get(chunk, pageind+npages) == 0 && (followsize = arena_mapbits_unallocated_size_get(chunk, - pageind+npages)) >= size - oldsize) { + pageind+npages)) >= usize_min - oldsize) { /* * The next run is available and sufficiently large. Split the * following run, then merge the first part with the existing * allocation. */ - size_t flag_dirty; - size_t splitsize = (oldsize + followsize <= size + extra) - ? followsize : size + extra - oldsize; - arena_run_split_large(arena, (arena_run_t *)((uintptr_t)chunk + - ((pageind+npages) << LG_PAGE)), splitsize, zero); + arena_run_t *run; + size_t flag_dirty, flag_unzeroed_mask, splitsize, usize; + + usize = s2u(size + extra); + while (oldsize + followsize < usize) + usize = index2size(size2index(usize)-1); + assert(usize >= usize_min); + splitsize = usize - oldsize; + + run = &arena_miscelm_get(chunk, pageind+npages)->run; + if (arena_run_split_large(arena, run, splitsize, zero)) { + malloc_mutex_unlock(&arena->lock); + return (true); + } size = oldsize + splitsize; - npages = size >> LG_PAGE; + npages = (size + large_pad) >> LG_PAGE; /* * Mark the extended run as dirty if either portion of the run @@ -2051,21 +2691,29 @@ arena_ralloc_large_grow(arena_t *arena, arena_chunk_t *chunk, void *ptr, */ flag_dirty = arena_mapbits_dirty_get(chunk, pageind) | arena_mapbits_dirty_get(chunk, pageind+npages-1); - arena_mapbits_large_set(chunk, pageind, size, flag_dirty); - arena_mapbits_large_set(chunk, pageind+npages-1, 0, flag_dirty); + flag_unzeroed_mask = flag_dirty == 0 ? CHUNK_MAP_UNZEROED : 0; + arena_mapbits_large_set(chunk, pageind, size + large_pad, + flag_dirty | (flag_unzeroed_mask & + arena_mapbits_unzeroed_get(chunk, pageind))); + arena_mapbits_large_set(chunk, pageind+npages-1, 0, flag_dirty | + (flag_unzeroed_mask & arena_mapbits_unzeroed_get(chunk, + pageind+npages-1))); if (config_stats) { + index_t oldindex = size2index(oldsize) - NBINS; + index_t index = size2index(size) - NBINS; + arena->stats.ndalloc_large++; arena->stats.allocated_large -= oldsize; - arena->stats.lstats[(oldsize >> LG_PAGE) - 1].ndalloc++; - arena->stats.lstats[(oldsize >> LG_PAGE) - 1].curruns--; + arena->stats.lstats[oldindex].ndalloc++; + arena->stats.lstats[oldindex].curruns--; arena->stats.nmalloc_large++; arena->stats.nrequests_large++; arena->stats.allocated_large += size; - arena->stats.lstats[(size >> LG_PAGE) - 1].nmalloc++; - arena->stats.lstats[(size >> LG_PAGE) - 1].nrequests++; - arena->stats.lstats[(size >> LG_PAGE) - 1].curruns++; + arena->stats.lstats[index].nmalloc++; + arena->stats.lstats[index].nrequests++; + arena->stats.lstats[index].curruns++; } malloc_mutex_unlock(&arena->lock); return (false); @@ -2083,7 +2731,7 @@ static void arena_ralloc_junk_large(void *ptr, size_t old_usize, size_t usize) { - if (config_fill && opt_junk) { + if (config_fill && unlikely(opt_junk_free)) { memset((void *)((uintptr_t)ptr + usize), 0x5a, old_usize - usize); } @@ -2103,10 +2751,14 @@ static bool arena_ralloc_large(void *ptr, size_t oldsize, size_t size, size_t extra, bool zero) { - size_t psize; + size_t usize; - psize = PAGE_CEILING(size + extra); - if (psize == oldsize) { + /* Make sure extra can't cause size_t overflow. */ + if (unlikely(extra >= arena_maxclass)) + return (true); + + usize = s2u(size + extra); + if (usize == oldsize) { /* Same size class. */ return (false); } else { @@ -2114,24 +2766,23 @@ arena_ralloc_large(void *ptr, size_t oldsize, size_t size, size_t extra, arena_t *arena; chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr); - arena = chunk->arena; + arena = extent_node_arena_get(&chunk->node); - if (psize < oldsize) { + if (usize < oldsize) { /* Fill before shrinking in order avoid a race. */ - arena_ralloc_junk_large(ptr, oldsize, psize); + arena_ralloc_junk_large(ptr, oldsize, usize); arena_ralloc_large_shrink(arena, chunk, ptr, oldsize, - psize); + usize); return (false); } else { bool ret = arena_ralloc_large_grow(arena, chunk, ptr, - oldsize, PAGE_CEILING(size), - psize - PAGE_CEILING(size), zero); - if (config_fill && ret == false && zero == false) { - if (opt_junk) { + oldsize, size, extra, zero); + if (config_fill && !ret && !zero) { + if (unlikely(opt_junk_alloc)) { memset((void *)((uintptr_t)ptr + oldsize), 0xa5, isalloc(ptr, config_prof) - oldsize); - } else if (opt_zero) { + } else if (unlikely(opt_zero)) { memset((void *)((uintptr_t)ptr + oldsize), 0, isalloc(ptr, config_prof) - oldsize); @@ -2147,84 +2798,103 @@ arena_ralloc_no_move(void *ptr, size_t oldsize, size_t size, size_t extra, bool zero) { - /* - * Avoid moving the allocation if the size class can be left the same. - */ - if (oldsize <= arena_maxclass) { - if (oldsize <= SMALL_MAXCLASS) { - assert(arena_bin_info[SMALL_SIZE2BIN(oldsize)].reg_size - == oldsize); - if ((size + extra <= SMALL_MAXCLASS && - SMALL_SIZE2BIN(size + extra) == - SMALL_SIZE2BIN(oldsize)) || (size <= oldsize && - size + extra >= oldsize)) - return (false); - } else { - assert(size <= arena_maxclass); - if (size + extra > SMALL_MAXCLASS) { - if (arena_ralloc_large(ptr, oldsize, size, - extra, zero) == false) + if (likely(size <= arena_maxclass)) { + /* + * Avoid moving the allocation if the size class can be left the + * same. + */ + if (likely(oldsize <= arena_maxclass)) { + if (oldsize <= SMALL_MAXCLASS) { + assert( + arena_bin_info[size2index(oldsize)].reg_size + == oldsize); + if ((size + extra <= SMALL_MAXCLASS && + size2index(size + extra) == + size2index(oldsize)) || (size <= oldsize && + size + extra >= oldsize)) return (false); + } else { + assert(size <= arena_maxclass); + if (size + extra > SMALL_MAXCLASS) { + if (!arena_ralloc_large(ptr, oldsize, + size, extra, zero)) + return (false); + } } } - } - /* Reallocation would require a move. */ - return (true); + /* Reallocation would require a move. */ + return (true); + } else + return (huge_ralloc_no_move(ptr, oldsize, size, extra, zero)); } 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) +arena_ralloc(tsd_t *tsd, arena_t *arena, void *ptr, size_t oldsize, size_t size, + size_t extra, size_t alignment, bool zero, tcache_t *tcache) { void *ret; - size_t copysize; - /* Try to avoid moving the allocation. */ - if (arena_ralloc_no_move(ptr, oldsize, size, extra, zero) == false) - return (ptr); + if (likely(size <= arena_maxclass)) { + size_t copysize; - /* - * size and oldsize are different enough that we need to move the - * object. In that case, fall back to allocating new space and - * copying. - */ - if (alignment != 0) { - size_t usize = sa2u(size + extra, alignment); - if (usize == 0) - return (NULL); - ret = ipalloct(usize, alignment, zero, try_tcache_alloc, arena); - } else - ret = arena_malloc(arena, size + extra, zero, try_tcache_alloc); + /* Try to avoid moving the allocation. */ + if (!arena_ralloc_no_move(ptr, oldsize, size, extra, zero)) + return (ptr); - if (ret == NULL) { - if (extra == 0) - return (NULL); - /* Try again, this time without extra. */ + /* + * size and oldsize are different enough that we need to move + * the object. In that case, fall back to allocating new space + * and copying. + */ if (alignment != 0) { - size_t usize = sa2u(size, alignment); + size_t usize = sa2u(size + extra, alignment); if (usize == 0) return (NULL); - ret = ipalloct(usize, alignment, zero, try_tcache_alloc, + ret = ipalloct(tsd, usize, alignment, zero, tcache, arena); - } else - ret = arena_malloc(arena, size, zero, try_tcache_alloc); + } else { + ret = arena_malloc(tsd, arena, size + extra, zero, + tcache); + } - if (ret == NULL) - return (NULL); + if (ret == NULL) { + if (extra == 0) + return (NULL); + /* Try again, this time without extra. */ + if (alignment != 0) { + size_t usize = sa2u(size, alignment); + if (usize == 0) + return (NULL); + ret = ipalloct(tsd, usize, alignment, zero, + tcache, arena); + } else { + ret = arena_malloc(tsd, arena, size, zero, + tcache); + } + + if (ret == NULL) + return (NULL); + } + + /* + * Junk/zero-filling were already done by + * ipalloc()/arena_malloc(). + */ + + /* + * Copy at most size bytes (not size+extra), since the caller + * has no expectation that the extra bytes will be reliably + * preserved. + */ + copysize = (size < oldsize) ? size : oldsize; + JEMALLOC_VALGRIND_MAKE_MEM_UNDEFINED(ret, copysize); + memcpy(ret, ptr, copysize); + isqalloc(tsd, ptr, oldsize, tcache); + } else { + ret = huge_ralloc(tsd, arena, ptr, oldsize, size, extra, + alignment, zero, tcache); } - - /* Junk/zero-filling were already done by ipalloc()/arena_malloc(). */ - - /* - * Copy at most size bytes (not size+extra), since the caller has no - * expectation that the extra bytes will be reliably preserved. - */ - copysize = (size < oldsize) ? size : oldsize; - VALGRIND_MAKE_MEM_UNDEFINED(ret, copysize); - memcpy(ret, ptr, copysize); - iqalloct(ptr, try_tcache_dalloc); return (ret); } @@ -2239,24 +2909,46 @@ arena_dss_prec_get(arena_t *arena) return (ret); } -void +bool arena_dss_prec_set(arena_t *arena, dss_prec_t dss_prec) { + if (!have_dss) + return (dss_prec != dss_prec_disabled); malloc_mutex_lock(&arena->lock); arena->dss_prec = dss_prec; malloc_mutex_unlock(&arena->lock); + return (false); +} + +ssize_t +arena_lg_dirty_mult_default_get(void) +{ + + return ((ssize_t)atomic_read_z((size_t *)&lg_dirty_mult_default)); +} + +bool +arena_lg_dirty_mult_default_set(ssize_t lg_dirty_mult) +{ + + if (!arena_lg_dirty_mult_valid(lg_dirty_mult)) + return (true); + atomic_write_z((size_t *)&lg_dirty_mult_default, (size_t)lg_dirty_mult); + return (false); } 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) +arena_stats_merge(arena_t *arena, const char **dss, ssize_t *lg_dirty_mult, + size_t *nactive, size_t *ndirty, arena_stats_t *astats, + malloc_bin_stats_t *bstats, malloc_large_stats_t *lstats, + malloc_huge_stats_t *hstats) { unsigned i; malloc_mutex_lock(&arena->lock); *dss = dss_prec_names[arena->dss_prec]; + *lg_dirty_mult = arena->lg_dirty_mult; *nactive += arena->nactive; *ndirty += arena->ndirty; @@ -2264,10 +2956,15 @@ arena_stats_merge(arena_t *arena, const char **dss, size_t *nactive, astats->npurge += arena->stats.npurge; astats->nmadvise += arena->stats.nmadvise; astats->purged += arena->stats.purged; + astats->metadata_mapped += arena->stats.metadata_mapped; + astats->metadata_allocated += arena_metadata_allocated_get(arena); astats->allocated_large += arena->stats.allocated_large; astats->nmalloc_large += arena->stats.nmalloc_large; astats->ndalloc_large += arena->stats.ndalloc_large; astats->nrequests_large += arena->stats.nrequests_large; + astats->allocated_huge += arena->stats.allocated_huge; + astats->nmalloc_huge += arena->stats.nmalloc_huge; + astats->ndalloc_huge += arena->stats.ndalloc_huge; for (i = 0; i < nlclasses; i++) { lstats[i].nmalloc += arena->stats.lstats[i].nmalloc; @@ -2275,16 +2972,22 @@ arena_stats_merge(arena_t *arena, const char **dss, size_t *nactive, lstats[i].nrequests += arena->stats.lstats[i].nrequests; lstats[i].curruns += arena->stats.lstats[i].curruns; } + + for (i = 0; i < nhclasses; i++) { + hstats[i].nmalloc += arena->stats.hstats[i].nmalloc; + hstats[i].ndalloc += arena->stats.hstats[i].ndalloc; + hstats[i].curhchunks += arena->stats.hstats[i].curhchunks; + } malloc_mutex_unlock(&arena->lock); for (i = 0; i < NBINS; i++) { arena_bin_t *bin = &arena->bins[i]; malloc_mutex_lock(&bin->lock); - bstats[i].allocated += bin->stats.allocated; bstats[i].nmalloc += bin->stats.nmalloc; bstats[i].ndalloc += bin->stats.ndalloc; bstats[i].nrequests += bin->stats.nrequests; + bstats[i].curregs += bin->stats.curregs; if (config_tcache) { bstats[i].nfills += bin->stats.nfills; bstats[i].nflushes += bin->stats.nflushes; @@ -2296,27 +2999,42 @@ arena_stats_merge(arena_t *arena, const char **dss, size_t *nactive, } } -bool -arena_new(arena_t *arena, unsigned ind) +arena_t * +arena_new(unsigned ind) { + arena_t *arena; unsigned i; arena_bin_t *bin; + /* + * Allocate arena, arena->lstats, and arena->hstats contiguously, mainly + * because there is no way to clean up if base_alloc() OOMs. + */ + if (config_stats) { + arena = (arena_t *)base_alloc(CACHELINE_CEILING(sizeof(arena_t)) + + QUANTUM_CEILING(nlclasses * sizeof(malloc_large_stats_t) + + nhclasses) * sizeof(malloc_huge_stats_t)); + } else + arena = (arena_t *)base_alloc(sizeof(arena_t)); + if (arena == NULL) + return (NULL); + arena->ind = ind; arena->nthreads = 0; - if (malloc_mutex_init(&arena->lock)) - return (true); + return (NULL); if (config_stats) { memset(&arena->stats, 0, sizeof(arena_stats_t)); - arena->stats.lstats = - (malloc_large_stats_t *)base_alloc(nlclasses * - sizeof(malloc_large_stats_t)); - if (arena->stats.lstats == NULL) - return (true); + arena->stats.lstats = (malloc_large_stats_t *)((uintptr_t)arena + + CACHELINE_CEILING(sizeof(arena_t))); memset(arena->stats.lstats, 0, nlclasses * sizeof(malloc_large_stats_t)); + arena->stats.hstats = (malloc_huge_stats_t *)((uintptr_t)arena + + CACHELINE_CEILING(sizeof(arena_t)) + + QUANTUM_CEILING(nlclasses * sizeof(malloc_large_stats_t))); + memset(arena->stats.hstats, 0, nhclasses * + sizeof(malloc_huge_stats_t)); if (config_tcache) ql_new(&arena->tcache_ql); } @@ -2324,56 +3042,76 @@ arena_new(arena_t *arena, unsigned ind) if (config_prof) arena->prof_accumbytes = 0; + if (config_cache_oblivious) { + /* + * A nondeterministic seed based on the address of arena reduces + * the likelihood of lockstep non-uniform cache index + * utilization among identical concurrent processes, but at the + * cost of test repeatability. For debug builds, instead use a + * deterministic seed. + */ + arena->offset_state = config_debug ? ind : + (uint64_t)(uintptr_t)arena; + } + arena->dss_prec = chunk_dss_prec_get(); - /* Initialize chunks. */ - arena_chunk_dirty_new(&arena->chunks_dirty); arena->spare = NULL; + arena->lg_dirty_mult = arena_lg_dirty_mult_default_get(); + arena->purging = false; arena->nactive = 0; arena->ndirty = 0; - arena->npurgatory = 0; arena_avail_tree_new(&arena->runs_avail); + qr_new(&arena->runs_dirty, rd_link); + qr_new(&arena->chunks_cache, cc_link); + + ql_new(&arena->huge); + if (malloc_mutex_init(&arena->huge_mtx)) + return (NULL); + + extent_tree_szad_new(&arena->chunks_szad_cached); + extent_tree_ad_new(&arena->chunks_ad_cached); + extent_tree_szad_new(&arena->chunks_szad_retained); + extent_tree_ad_new(&arena->chunks_ad_retained); + if (malloc_mutex_init(&arena->chunks_mtx)) + return (NULL); + ql_new(&arena->node_cache); + if (malloc_mutex_init(&arena->node_cache_mtx)) + return (NULL); + + arena->chunk_hooks = chunk_hooks_default; /* Initialize bins. */ for (i = 0; i < NBINS; i++) { bin = &arena->bins[i]; if (malloc_mutex_init(&bin->lock)) - return (true); + return (NULL); bin->runcur = NULL; arena_run_tree_new(&bin->runs); if (config_stats) memset(&bin->stats, 0, sizeof(malloc_bin_stats_t)); } - return (false); + return (arena); } /* * Calculate bin_info->run_size such that it meets the following constraints: * - * *) bin_info->run_size >= min_run_size - * *) bin_info->run_size <= arena_maxclass - * *) run header overhead <= RUN_MAX_OVRHD (or header overhead relaxed). + * *) bin_info->run_size <= arena_maxrun * *) bin_info->nregs <= RUN_MAXREGS * - * bin_info->nregs, bin_info->bitmap_offset, and bin_info->reg0_offset are also - * calculated here, since these settings are all interdependent. + * bin_info->nregs and bin_info->reg0_offset are also calculated here, since + * these settings are all interdependent. */ -static size_t -bin_info_run_size_calc(arena_bin_info_t *bin_info, size_t min_run_size) +static void +bin_info_run_size_calc(arena_bin_info_t *bin_info) { size_t pad_size; - size_t try_run_size, good_run_size; - uint32_t try_nregs, good_nregs; - uint32_t try_hdr_size, good_hdr_size; - uint32_t try_bitmap_offset, good_bitmap_offset; - uint32_t try_ctx0_offset, good_ctx0_offset; - uint32_t try_redzone0_offset, good_redzone0_offset; - - assert(min_run_size >= PAGE); - assert(min_run_size <= arena_maxclass); + size_t try_run_size, perfect_run_size, actual_run_size; + uint32_t try_nregs, perfect_nregs, actual_nregs; /* * Determine redzone size based on minimum alignment and minimum @@ -2382,8 +3120,9 @@ bin_info_run_size_calc(arena_bin_info_t *bin_info, size_t min_run_size) * minimum alignment; without the padding, each redzone would have to * be twice as large in order to maintain alignment. */ - if (config_fill && opt_redzone) { - size_t align_min = ZU(1) << (ffs(bin_info->reg_size) - 1); + if (config_fill && unlikely(opt_redzone)) { + size_t align_min = ZU(1) << (jemalloc_ffs(bin_info->reg_size) - + 1); if (align_min <= REDZONE_MINSIZE) { bin_info->redzone_size = REDZONE_MINSIZE; pad_size = 0; @@ -2399,127 +3138,114 @@ bin_info_run_size_calc(arena_bin_info_t *bin_info, size_t min_run_size) (bin_info->redzone_size << 1); /* - * Calculate known-valid settings before entering the run_size - * expansion loop, so that the first part of the loop always copies - * valid settings. - * - * The do..while loop iteratively reduces the number of regions until - * the run header and the regions no longer overlap. A closed formula - * would be quite messy, since there is an interdependency between the - * header's mask length and the number of regions. + * Compute run size under ideal conditions (no redzones, no limit on run + * size). */ - try_run_size = min_run_size; - try_nregs = ((try_run_size - sizeof(arena_run_t)) / - bin_info->reg_interval) - + 1; /* Counter-act try_nregs-- in loop. */ - if (try_nregs > RUN_MAXREGS) { - try_nregs = RUN_MAXREGS - + 1; /* Counter-act try_nregs-- in loop. */ - } + try_run_size = PAGE; + try_nregs = try_run_size / bin_info->reg_size; do { - try_nregs--; - try_hdr_size = sizeof(arena_run_t); - /* Pad to a long boundary. */ - try_hdr_size = LONG_CEILING(try_hdr_size); - try_bitmap_offset = try_hdr_size; - /* Add space for bitmap. */ - try_hdr_size += bitmap_size(try_nregs); - if (config_prof && opt_prof && prof_promote == false) { - /* Pad to a quantum boundary. */ - try_hdr_size = QUANTUM_CEILING(try_hdr_size); - try_ctx0_offset = try_hdr_size; - /* Add space for one (prof_ctx_t *) per region. */ - try_hdr_size += try_nregs * sizeof(prof_ctx_t *); - } else - try_ctx0_offset = 0; - try_redzone0_offset = try_run_size - (try_nregs * - bin_info->reg_interval) - pad_size; - } while (try_hdr_size > try_redzone0_offset); + perfect_run_size = try_run_size; + perfect_nregs = try_nregs; - /* run_size expansion loop. */ - do { - /* - * Copy valid settings before trying more aggressive settings. - */ - good_run_size = try_run_size; - good_nregs = try_nregs; - good_hdr_size = try_hdr_size; - good_bitmap_offset = try_bitmap_offset; - good_ctx0_offset = try_ctx0_offset; - good_redzone0_offset = try_redzone0_offset; - - /* Try more aggressive settings. */ try_run_size += PAGE; - try_nregs = ((try_run_size - sizeof(arena_run_t) - pad_size) / - bin_info->reg_interval) - + 1; /* Counter-act try_nregs-- in loop. */ - if (try_nregs > RUN_MAXREGS) { - try_nregs = RUN_MAXREGS - + 1; /* Counter-act try_nregs-- in loop. */ - } - do { - try_nregs--; - try_hdr_size = sizeof(arena_run_t); - /* Pad to a long boundary. */ - try_hdr_size = LONG_CEILING(try_hdr_size); - try_bitmap_offset = try_hdr_size; - /* Add space for bitmap. */ - try_hdr_size += bitmap_size(try_nregs); - if (config_prof && opt_prof && prof_promote == false) { - /* Pad to a quantum boundary. */ - try_hdr_size = QUANTUM_CEILING(try_hdr_size); - try_ctx0_offset = try_hdr_size; - /* - * Add space for one (prof_ctx_t *) per region. - */ - try_hdr_size += try_nregs * - sizeof(prof_ctx_t *); - } - try_redzone0_offset = try_run_size - (try_nregs * - bin_info->reg_interval) - pad_size; - } while (try_hdr_size > try_redzone0_offset); - } while (try_run_size <= arena_maxclass - && RUN_MAX_OVRHD * (bin_info->reg_interval << 3) > - RUN_MAX_OVRHD_RELAX - && (try_redzone0_offset << RUN_BFP) > RUN_MAX_OVRHD * try_run_size - && try_nregs < RUN_MAXREGS); + try_nregs = try_run_size / bin_info->reg_size; + } while (perfect_run_size != perfect_nregs * bin_info->reg_size); + assert(perfect_nregs <= RUN_MAXREGS); - assert(good_hdr_size <= good_redzone0_offset); + actual_run_size = perfect_run_size; + actual_nregs = (actual_run_size - pad_size) / bin_info->reg_interval; + + /* + * Redzones can require enough padding that not even a single region can + * fit within the number of pages that would normally be dedicated to a + * run for this size class. Increase the run size until at least one + * region fits. + */ + while (actual_nregs == 0) { + assert(config_fill && unlikely(opt_redzone)); + + actual_run_size += PAGE; + actual_nregs = (actual_run_size - pad_size) / + bin_info->reg_interval; + } + + /* + * Make sure that the run will fit within an arena chunk. + */ + while (actual_run_size > arena_maxrun) { + actual_run_size -= PAGE; + actual_nregs = (actual_run_size - pad_size) / + bin_info->reg_interval; + } + assert(actual_nregs > 0); + assert(actual_run_size == s2u(actual_run_size)); /* Copy final settings. */ - bin_info->run_size = good_run_size; - bin_info->nregs = good_nregs; - bin_info->bitmap_offset = good_bitmap_offset; - bin_info->ctx0_offset = good_ctx0_offset; - bin_info->reg0_offset = good_redzone0_offset + bin_info->redzone_size; + bin_info->run_size = actual_run_size; + bin_info->nregs = actual_nregs; + bin_info->reg0_offset = actual_run_size - (actual_nregs * + bin_info->reg_interval) - pad_size + bin_info->redzone_size; + + if (actual_run_size > small_maxrun) + small_maxrun = actual_run_size; assert(bin_info->reg0_offset - bin_info->redzone_size + (bin_info->nregs * bin_info->reg_interval) + pad_size == bin_info->run_size); - - return (good_run_size); } static void bin_info_init(void) { arena_bin_info_t *bin_info; - size_t prev_run_size = PAGE; -#define SIZE_CLASS(bin, delta, size) \ - bin_info = &arena_bin_info[bin]; \ +#define BIN_INFO_INIT_bin_yes(index, size) \ + bin_info = &arena_bin_info[index]; \ bin_info->reg_size = size; \ - prev_run_size = bin_info_run_size_calc(bin_info, prev_run_size);\ + bin_info_run_size_calc(bin_info); \ bitmap_info_init(&bin_info->bitmap_info, bin_info->nregs); +#define BIN_INFO_INIT_bin_no(index, size) +#define SC(index, lg_grp, lg_delta, ndelta, bin, lg_delta_lookup) \ + BIN_INFO_INIT_bin_##bin(index, (ZU(1)<> + LG_PAGE)); + if (small_run_tab == NULL) + return (true); + +#define TAB_INIT_bin_yes(index, size) { \ + arena_bin_info_t *bin_info = &arena_bin_info[index]; \ + small_run_tab[bin_info->run_size >> LG_PAGE] = true; \ + } +#define TAB_INIT_bin_no(index, size) +#define SC(index, lg_grp, lg_delta, ndelta, bin, lg_delta_lookup) \ + TAB_INIT_bin_##bin(index, (ZU(1)<> LG_PAGE) + ((header_size & PAGE_MASK) - != 0); + header_size = offsetof(arena_chunk_t, map_bits) + + ((sizeof(arena_chunk_map_bits_t) + + sizeof(arena_chunk_map_misc_t)) * (chunk_npages-map_bias)); + map_bias = (header_size + PAGE_MASK) >> LG_PAGE; } assert(map_bias > 0); - arena_maxclass = chunksize - (map_bias << LG_PAGE); + map_misc_offset = offsetof(arena_chunk_t, map_bits) + + sizeof(arena_chunk_map_bits_t) * (chunk_npages-map_bias); + + arena_maxrun = chunksize - (map_bias << LG_PAGE); + assert(arena_maxrun > 0); + arena_maxclass = index2size(size2index(chunksize)-1); + if (arena_maxclass > arena_maxrun) { + /* + * For small chunk sizes it's possible for there to be fewer + * non-header pages available than are necessary to serve the + * size classes just below chunksize. + */ + arena_maxclass = arena_maxrun; + } + assert(arena_maxclass > 0); + nlclasses = size2index(arena_maxclass) - size2index(SMALL_MAXCLASS); + nhclasses = NSIZES - nlclasses - NBINS; bin_info_init(); + return (small_run_size_init()); } void @@ -2552,6 +3295,9 @@ arena_prefork(arena_t *arena) unsigned i; malloc_mutex_prefork(&arena->lock); + malloc_mutex_prefork(&arena->huge_mtx); + malloc_mutex_prefork(&arena->chunks_mtx); + malloc_mutex_prefork(&arena->node_cache_mtx); for (i = 0; i < NBINS; i++) malloc_mutex_prefork(&arena->bins[i].lock); } @@ -2563,6 +3309,9 @@ arena_postfork_parent(arena_t *arena) for (i = 0; i < NBINS; i++) malloc_mutex_postfork_parent(&arena->bins[i].lock); + malloc_mutex_postfork_parent(&arena->node_cache_mtx); + malloc_mutex_postfork_parent(&arena->chunks_mtx); + malloc_mutex_postfork_parent(&arena->huge_mtx); malloc_mutex_postfork_parent(&arena->lock); } @@ -2573,5 +3322,8 @@ arena_postfork_child(arena_t *arena) for (i = 0; i < NBINS; i++) malloc_mutex_postfork_child(&arena->bins[i].lock); + malloc_mutex_postfork_child(&arena->node_cache_mtx); + malloc_mutex_postfork_child(&arena->chunks_mtx); + malloc_mutex_postfork_child(&arena->huge_mtx); malloc_mutex_postfork_child(&arena->lock); } diff --git a/contrib/jemalloc/src/base.c b/contrib/jemalloc/src/base.c index 4e62e8fa9189..7cdcfed86bd8 100644 --- a/contrib/jemalloc/src/base.c +++ b/contrib/jemalloc/src/base.c @@ -5,107 +5,138 @@ /* Data. */ static malloc_mutex_t base_mtx; - -/* - * Current pages that are being used for internal memory allocations. These - * pages are carved up in cacheline-size quanta, so that there is no chance of - * false cache line sharing. - */ -static void *base_pages; -static void *base_next_addr; -static void *base_past_addr; /* Addr immediately past base_pages. */ +static extent_tree_t base_avail_szad; static extent_node_t *base_nodes; - -/******************************************************************************/ -/* Function prototypes for non-inline static functions. */ - -static bool base_pages_alloc(size_t minsize); +static size_t base_allocated; +static size_t base_resident; +static size_t base_mapped; /******************************************************************************/ -static bool -base_pages_alloc(size_t minsize) +/* base_mtx must be held. */ +static extent_node_t * +base_node_try_alloc(void) { - size_t csize; - bool zero; + extent_node_t *node; - assert(minsize != 0); - csize = CHUNK_CEILING(minsize); - zero = false; - base_pages = chunk_alloc(csize, chunksize, true, &zero, - chunk_dss_prec_get()); - if (base_pages == NULL) - return (true); - base_next_addr = base_pages; - base_past_addr = (void *)((uintptr_t)base_pages + csize); - - return (false); + if (base_nodes == NULL) + return (NULL); + node = base_nodes; + base_nodes = *(extent_node_t **)node; + JEMALLOC_VALGRIND_MAKE_MEM_UNDEFINED(node, sizeof(extent_node_t)); + return (node); } +/* base_mtx must be held. */ +static void +base_node_dalloc(extent_node_t *node) +{ + + JEMALLOC_VALGRIND_MAKE_MEM_UNDEFINED(node, sizeof(extent_node_t)); + *(extent_node_t **)node = base_nodes; + base_nodes = node; +} + +/* base_mtx must be held. */ +static extent_node_t * +base_chunk_alloc(size_t minsize) +{ + extent_node_t *node; + size_t csize, nsize; + void *addr; + + assert(minsize != 0); + node = base_node_try_alloc(); + /* Allocate enough space to also carve a node out if necessary. */ + nsize = (node == NULL) ? CACHELINE_CEILING(sizeof(extent_node_t)) : 0; + csize = CHUNK_CEILING(minsize + nsize); + addr = chunk_alloc_base(csize); + if (addr == NULL) { + if (node != NULL) + base_node_dalloc(node); + return (NULL); + } + base_mapped += csize; + if (node == NULL) { + node = (extent_node_t *)addr; + addr = (void *)((uintptr_t)addr + nsize); + csize -= nsize; + if (config_stats) { + base_allocated += nsize; + base_resident += PAGE_CEILING(nsize); + } + } + extent_node_init(node, NULL, addr, csize, true, true); + return (node); +} + +/* + * base_alloc() guarantees demand-zeroed memory, in order to make multi-page + * sparse data structures such as radix tree nodes efficient with respect to + * physical memory usage. + */ void * base_alloc(size_t size) { void *ret; - size_t csize; + size_t csize, usize; + extent_node_t *node; + extent_node_t key; - /* Round size up to nearest multiple of the cacheline size. */ + /* + * Round size up to nearest multiple of the cacheline size, so that + * there is no chance of false cache line sharing. + */ csize = CACHELINE_CEILING(size); + usize = s2u(csize); + extent_node_init(&key, NULL, NULL, usize, false, false); malloc_mutex_lock(&base_mtx); - /* Make sure there's enough space for the allocation. */ - if ((uintptr_t)base_next_addr + csize > (uintptr_t)base_past_addr) { - if (base_pages_alloc(csize)) { - malloc_mutex_unlock(&base_mtx); - return (NULL); - } - } - /* Allocate. */ - ret = base_next_addr; - base_next_addr = (void *)((uintptr_t)base_next_addr + csize); - malloc_mutex_unlock(&base_mtx); - VALGRIND_MAKE_MEM_UNDEFINED(ret, csize); - - return (ret); -} - -void * -base_calloc(size_t number, size_t size) -{ - void *ret = base_alloc(number * size); - - if (ret != NULL) - memset(ret, 0, number * size); - - return (ret); -} - -extent_node_t * -base_node_alloc(void) -{ - extent_node_t *ret; - - malloc_mutex_lock(&base_mtx); - if (base_nodes != NULL) { - ret = base_nodes; - base_nodes = *(extent_node_t **)ret; - malloc_mutex_unlock(&base_mtx); - VALGRIND_MAKE_MEM_UNDEFINED(ret, sizeof(extent_node_t)); + node = extent_tree_szad_nsearch(&base_avail_szad, &key); + if (node != NULL) { + /* Use existing space. */ + extent_tree_szad_remove(&base_avail_szad, node); } else { - malloc_mutex_unlock(&base_mtx); - ret = (extent_node_t *)base_alloc(sizeof(extent_node_t)); + /* Try to allocate more space. */ + node = base_chunk_alloc(csize); + } + if (node == NULL) { + ret = NULL; + goto label_return; } + ret = extent_node_addr_get(node); + if (extent_node_size_get(node) > csize) { + extent_node_addr_set(node, (void *)((uintptr_t)ret + csize)); + extent_node_size_set(node, extent_node_size_get(node) - csize); + extent_tree_szad_insert(&base_avail_szad, node); + } else + base_node_dalloc(node); + if (config_stats) { + base_allocated += csize; + /* + * Add one PAGE to base_resident for every page boundary that is + * crossed by the new allocation. + */ + base_resident += PAGE_CEILING((uintptr_t)ret + csize) - + PAGE_CEILING((uintptr_t)ret); + } + JEMALLOC_VALGRIND_MAKE_MEM_DEFINED(ret, csize); +label_return: + malloc_mutex_unlock(&base_mtx); return (ret); } void -base_node_dealloc(extent_node_t *node) +base_stats_get(size_t *allocated, size_t *resident, size_t *mapped) { - VALGRIND_MAKE_MEM_UNDEFINED(node, sizeof(extent_node_t)); malloc_mutex_lock(&base_mtx); - *(extent_node_t **)node = base_nodes; - base_nodes = node; + assert(base_allocated <= base_resident); + assert(base_resident <= base_mapped); + *allocated = base_allocated; + *resident = base_resident; + *mapped = base_mapped; malloc_mutex_unlock(&base_mtx); } @@ -113,9 +144,10 @@ bool base_boot(void) { - base_nodes = NULL; if (malloc_mutex_init(&base_mtx)) return (true); + extent_tree_szad_new(&base_avail_szad); + base_nodes = NULL; return (false); } diff --git a/contrib/jemalloc/src/bitmap.c b/contrib/jemalloc/src/bitmap.c index e2bd907d558d..c733372b4cb2 100644 --- a/contrib/jemalloc/src/bitmap.c +++ b/contrib/jemalloc/src/bitmap.c @@ -2,19 +2,6 @@ #include "jemalloc/internal/jemalloc_internal.h" /******************************************************************************/ -/* Function prototypes for non-inline static functions. */ - -static size_t bits2groups(size_t nbits); - -/******************************************************************************/ - -static size_t -bits2groups(size_t nbits) -{ - - return ((nbits >> LG_BITMAP_GROUP_NBITS) + - !!(nbits & BITMAP_GROUP_NBITS_MASK)); -} void bitmap_info_init(bitmap_info_t *binfo, size_t nbits) @@ -31,15 +18,16 @@ bitmap_info_init(bitmap_info_t *binfo, size_t nbits) * that requires only one group. */ binfo->levels[0].group_offset = 0; - group_count = bits2groups(nbits); + group_count = BITMAP_BITS2GROUPS(nbits); for (i = 1; group_count > 1; i++) { assert(i < BITMAP_MAX_LEVELS); binfo->levels[i].group_offset = binfo->levels[i-1].group_offset + group_count; - group_count = bits2groups(group_count); + group_count = BITMAP_BITS2GROUPS(group_count); } binfo->levels[i].group_offset = binfo->levels[i-1].group_offset + group_count; + assert(binfo->levels[i].group_offset <= BITMAP_GROUPS_MAX); binfo->nlevels = i; binfo->nbits = nbits; } diff --git a/contrib/jemalloc/src/chunk.c b/contrib/jemalloc/src/chunk.c index 90ab116ae5fa..6ba1ca7a51ba 100644 --- a/contrib/jemalloc/src/chunk.c +++ b/contrib/jemalloc/src/chunk.c @@ -5,129 +5,315 @@ /* Data. */ const char *opt_dss = DSS_DEFAULT; -size_t opt_lg_chunk = LG_CHUNK_DEFAULT; +size_t opt_lg_chunk = 0; -malloc_mutex_t chunks_mtx; -chunk_stats_t stats_chunks; +/* Used exclusively for gdump triggering. */ +static size_t curchunks; +static size_t highchunks; -/* - * Trees of chunks that were previously allocated (trees differ only in node - * ordering). These are used when allocating chunks, in an attempt to re-use - * 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_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; +rtree_t chunks_rtree; /* Various chunk-related settings. */ size_t chunksize; size_t chunksize_mask; /* (chunksize - 1). */ size_t chunk_npages; -size_t map_bias; -size_t arena_maxclass; /* Max size class for arenas. */ + +static void *chunk_alloc_default(void *new_addr, size_t size, + size_t alignment, bool *zero, bool *commit, unsigned arena_ind); +static bool chunk_dalloc_default(void *chunk, size_t size, bool committed, + unsigned arena_ind); +static bool chunk_commit_default(void *chunk, size_t size, size_t offset, + size_t length, unsigned arena_ind); +static bool chunk_decommit_default(void *chunk, size_t size, size_t offset, + size_t length, unsigned arena_ind); +static bool chunk_purge_default(void *chunk, size_t size, size_t offset, + size_t length, unsigned arena_ind); +static bool chunk_split_default(void *chunk, size_t size, size_t size_a, + size_t size_b, bool committed, unsigned arena_ind); +static bool chunk_merge_default(void *chunk_a, size_t size_a, void *chunk_b, + size_t size_b, bool committed, unsigned arena_ind); + +const chunk_hooks_t chunk_hooks_default = { + chunk_alloc_default, + chunk_dalloc_default, + chunk_commit_default, + chunk_decommit_default, + chunk_purge_default, + chunk_split_default, + chunk_merge_default +}; /******************************************************************************/ -/* Function prototypes for non-inline static functions. */ +/* + * Function prototypes for static functions that are referenced prior to + * definition. + */ -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(extent_tree_t *chunks_szad, - extent_tree_t *chunks_ad, void *chunk, size_t size); +static void chunk_record(arena_t *arena, chunk_hooks_t *chunk_hooks, + extent_tree_t *chunks_szad, extent_tree_t *chunks_ad, bool cache, + void *chunk, size_t size, bool zeroed, bool committed); /******************************************************************************/ +static chunk_hooks_t +chunk_hooks_get_locked(arena_t *arena) +{ + + return (arena->chunk_hooks); +} + +chunk_hooks_t +chunk_hooks_get(arena_t *arena) +{ + chunk_hooks_t chunk_hooks; + + malloc_mutex_lock(&arena->chunks_mtx); + chunk_hooks = chunk_hooks_get_locked(arena); + malloc_mutex_unlock(&arena->chunks_mtx); + + return (chunk_hooks); +} + +chunk_hooks_t +chunk_hooks_set(arena_t *arena, const chunk_hooks_t *chunk_hooks) +{ + chunk_hooks_t old_chunk_hooks; + + malloc_mutex_lock(&arena->chunks_mtx); + old_chunk_hooks = arena->chunk_hooks; + /* + * Copy each field atomically so that it is impossible for readers to + * see partially updated pointers. There are places where readers only + * need one hook function pointer (therefore no need to copy the + * entirety of arena->chunk_hooks), and stale reads do not affect + * correctness, so they perform unlocked reads. + */ +#define ATOMIC_COPY_HOOK(n) do { \ + union { \ + chunk_##n##_t **n; \ + void **v; \ + } u; \ + u.n = &arena->chunk_hooks.n; \ + atomic_write_p(u.v, chunk_hooks->n); \ +} while (0) + ATOMIC_COPY_HOOK(alloc); + ATOMIC_COPY_HOOK(dalloc); + ATOMIC_COPY_HOOK(commit); + ATOMIC_COPY_HOOK(decommit); + ATOMIC_COPY_HOOK(purge); + ATOMIC_COPY_HOOK(split); + ATOMIC_COPY_HOOK(merge); +#undef ATOMIC_COPY_HOOK + malloc_mutex_unlock(&arena->chunks_mtx); + + return (old_chunk_hooks); +} + +static void +chunk_hooks_assure_initialized_impl(arena_t *arena, chunk_hooks_t *chunk_hooks, + bool locked) +{ + static const chunk_hooks_t uninitialized_hooks = + CHUNK_HOOKS_INITIALIZER; + + if (memcmp(chunk_hooks, &uninitialized_hooks, sizeof(chunk_hooks_t)) == + 0) { + *chunk_hooks = locked ? chunk_hooks_get_locked(arena) : + chunk_hooks_get(arena); + } +} + +static void +chunk_hooks_assure_initialized_locked(arena_t *arena, + chunk_hooks_t *chunk_hooks) +{ + + chunk_hooks_assure_initialized_impl(arena, chunk_hooks, true); +} + +static void +chunk_hooks_assure_initialized(arena_t *arena, chunk_hooks_t *chunk_hooks) +{ + + chunk_hooks_assure_initialized_impl(arena, chunk_hooks, false); +} + +bool +chunk_register(const void *chunk, const extent_node_t *node) +{ + + assert(extent_node_addr_get(node) == chunk); + + if (rtree_set(&chunks_rtree, (uintptr_t)chunk, node)) + return (true); + if (config_prof && opt_prof) { + size_t size = extent_node_size_get(node); + size_t nadd = (size == 0) ? 1 : size / chunksize; + size_t cur = atomic_add_z(&curchunks, nadd); + size_t high = atomic_read_z(&highchunks); + while (cur > high && atomic_cas_z(&highchunks, high, cur)) { + /* + * Don't refresh cur, because it may have decreased + * since this thread lost the highchunks update race. + */ + high = atomic_read_z(&highchunks); + } + if (cur > high && prof_gdump_get_unlocked()) + prof_gdump(); + } + + return (false); +} + +void +chunk_deregister(const void *chunk, const extent_node_t *node) +{ + bool err; + + err = rtree_set(&chunks_rtree, (uintptr_t)chunk, NULL); + assert(!err); + if (config_prof && opt_prof) { + size_t size = extent_node_size_get(node); + size_t nsub = (size == 0) ? 1 : size / chunksize; + assert(atomic_read_z(&curchunks) >= nsub); + atomic_sub_z(&curchunks, nsub); + } +} + +/* + * Do first-best-fit chunk selection, i.e. select the lowest chunk that best + * fits. + */ +static extent_node_t * +chunk_first_best_fit(arena_t *arena, extent_tree_t *chunks_szad, + extent_tree_t *chunks_ad, size_t size) +{ + extent_node_t key; + + assert(size == CHUNK_CEILING(size)); + + extent_node_init(&key, arena, NULL, size, false, false); + return (extent_tree_szad_nsearch(chunks_szad, &key)); +} + static void * -chunk_recycle(extent_tree_t *chunks_szad, extent_tree_t *chunks_ad, size_t size, - size_t alignment, bool base, bool *zero) +chunk_recycle(arena_t *arena, chunk_hooks_t *chunk_hooks, + extent_tree_t *chunks_szad, extent_tree_t *chunks_ad, bool cache, + void *new_addr, size_t size, size_t alignment, bool *zero, bool *commit, + bool dalloc_node) { void *ret; extent_node_t *node; - extent_node_t key; size_t alloc_size, leadsize, trailsize; - bool zeroed; + bool zeroed, committed; - if (base) { - /* - * This function may need to call base_node_{,de}alloc(), but - * the current chunk allocation request is on behalf of the - * base allocator. Avoid deadlock (and if that weren't an - * issue, potential for infinite recursion) by returning NULL. - */ - return (NULL); - } + assert(new_addr == NULL || alignment == chunksize); + /* + * Cached chunks use the node linkage embedded in their headers, in + * which case dalloc_node is true, and new_addr is non-NULL because + * we're operating on a specific chunk. + */ + assert(dalloc_node || new_addr != NULL); - alloc_size = size + alignment - chunksize; + alloc_size = CHUNK_CEILING(s2u(size + alignment - chunksize)); /* Beware size_t wrap-around. */ if (alloc_size < size) return (NULL); - key.addr = NULL; - key.size = alloc_size; - malloc_mutex_lock(&chunks_mtx); - node = extent_tree_szad_nsearch(chunks_szad, &key); - if (node == NULL) { - malloc_mutex_unlock(&chunks_mtx); + malloc_mutex_lock(&arena->chunks_mtx); + chunk_hooks_assure_initialized_locked(arena, chunk_hooks); + if (new_addr != NULL) { + extent_node_t key; + extent_node_init(&key, arena, new_addr, alloc_size, false, + false); + node = extent_tree_ad_search(chunks_ad, &key); + } else { + node = chunk_first_best_fit(arena, chunks_szad, chunks_ad, + alloc_size); + } + if (node == NULL || (new_addr != NULL && extent_node_size_get(node) < + size)) { + malloc_mutex_unlock(&arena->chunks_mtx); return (NULL); } - leadsize = ALIGNMENT_CEILING((uintptr_t)node->addr, alignment) - - (uintptr_t)node->addr; - assert(node->size >= leadsize + size); - trailsize = node->size - leadsize - size; - ret = (void *)((uintptr_t)node->addr + leadsize); - zeroed = node->zeroed; + leadsize = ALIGNMENT_CEILING((uintptr_t)extent_node_addr_get(node), + alignment) - (uintptr_t)extent_node_addr_get(node); + assert(new_addr == NULL || leadsize == 0); + assert(extent_node_size_get(node) >= leadsize + size); + trailsize = extent_node_size_get(node) - leadsize - size; + ret = (void *)((uintptr_t)extent_node_addr_get(node) + leadsize); + zeroed = extent_node_zeroed_get(node); if (zeroed) - *zero = true; + *zero = true; + committed = extent_node_committed_get(node); + if (committed) + *commit = true; + /* Split the lead. */ + if (leadsize != 0 && + chunk_hooks->split(extent_node_addr_get(node), + extent_node_size_get(node), leadsize, size, false, arena->ind)) { + malloc_mutex_unlock(&arena->chunks_mtx); + return (NULL); + } /* Remove node from the tree. */ extent_tree_szad_remove(chunks_szad, node); extent_tree_ad_remove(chunks_ad, node); + arena_chunk_cache_maybe_remove(arena, node, cache); if (leadsize != 0) { /* Insert the leading space as a smaller chunk. */ - node->size = leadsize; + extent_node_size_set(node, leadsize); extent_tree_szad_insert(chunks_szad, node); extent_tree_ad_insert(chunks_ad, node); + arena_chunk_cache_maybe_insert(arena, node, cache); node = NULL; } if (trailsize != 0) { + /* Split the trail. */ + if (chunk_hooks->split(ret, size + trailsize, size, + trailsize, false, arena->ind)) { + if (dalloc_node && node != NULL) + arena_node_dalloc(arena, node); + malloc_mutex_unlock(&arena->chunks_mtx); + chunk_record(arena, chunk_hooks, chunks_szad, chunks_ad, + cache, ret, size + trailsize, zeroed, committed); + return (NULL); + } /* Insert the trailing space as a smaller chunk. */ if (node == NULL) { - /* - * An additional node is required, but - * base_node_alloc() can cause a new base chunk to be - * allocated. Drop chunks_mtx in order to avoid - * deadlock, and if node allocation fails, deallocate - * the result before returning an error. - */ - malloc_mutex_unlock(&chunks_mtx); - node = base_node_alloc(); + node = arena_node_alloc(arena); if (node == NULL) { - chunk_dealloc(ret, size, true); + malloc_mutex_unlock(&arena->chunks_mtx); + chunk_record(arena, chunk_hooks, chunks_szad, + chunks_ad, cache, ret, size + trailsize, + zeroed, committed); return (NULL); } - malloc_mutex_lock(&chunks_mtx); } - node->addr = (void *)((uintptr_t)(ret) + size); - node->size = trailsize; - node->zeroed = zeroed; + extent_node_init(node, arena, (void *)((uintptr_t)(ret) + size), + trailsize, zeroed, committed); extent_tree_szad_insert(chunks_szad, node); extent_tree_ad_insert(chunks_ad, node); + arena_chunk_cache_maybe_insert(arena, node, cache); node = NULL; } - malloc_mutex_unlock(&chunks_mtx); + if (!committed && chunk_hooks->commit(ret, size, 0, size, arena->ind)) { + malloc_mutex_unlock(&arena->chunks_mtx); + chunk_record(arena, chunk_hooks, chunks_szad, chunks_ad, cache, + ret, size, zeroed, committed); + return (NULL); + } + malloc_mutex_unlock(&arena->chunks_mtx); - if (node != NULL) - base_node_dealloc(node); + assert(dalloc_node || node != NULL); + if (dalloc_node && node != NULL) + arena_node_dalloc(arena, node); if (*zero) { - if (zeroed == false) + if (!zeroed) 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); + JEMALLOC_VALGRIND_MAKE_MEM_DEFINED(ret, size); for (i = 0; i < size / sizeof(size_t); i++) assert(p[i] == 0); } @@ -136,138 +322,214 @@ chunk_recycle(extent_tree_t *chunks_szad, extent_tree_t *chunks_ad, size_t size, } /* - * If the caller specifies (*zero == false), it is still possible to receive - * zeroed memory, in which case *zero is toggled to true. arena_chunk_alloc() - * takes advantage of this to avoid demanding zeroed chunks, but taking - * advantage of them if they are returned. + * If the caller specifies (!*zero), it is still possible to receive zeroed + * memory, in which case *zero is toggled to true. arena_chunk_alloc() takes + * advantage of this to avoid demanding zeroed chunks, but taking advantage of + * them if they are returned. */ -void * -chunk_alloc(size_t size, size_t alignment, bool base, bool *zero, - dss_prec_t dss_prec) +static void * +chunk_alloc_core(arena_t *arena, void *new_addr, size_t size, size_t alignment, + bool *zero, bool *commit, dss_prec_t dss_prec) { void *ret; + chunk_hooks_t chunk_hooks = CHUNK_HOOKS_INITIALIZER; assert(size != 0); assert((size & chunksize_mask) == 0); assert(alignment != 0); assert((alignment & chunksize_mask) == 0); + /* Retained. */ + if ((ret = chunk_recycle(arena, &chunk_hooks, + &arena->chunks_szad_retained, &arena->chunks_ad_retained, false, + new_addr, size, alignment, zero, commit, true)) != NULL) + return (ret); + /* "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; - if ((ret = chunk_alloc_mmap(size, alignment, zero)) != NULL) - goto label_return; + if (have_dss && dss_prec == dss_prec_primary && (ret = + chunk_alloc_dss(arena, new_addr, size, alignment, zero, commit)) != + NULL) + return (ret); + /* + * mmap. Requesting an address is not implemented for + * chunk_alloc_mmap(), so only call it if (new_addr == NULL). + */ + if (new_addr == NULL && (ret = chunk_alloc_mmap(size, alignment, zero, + commit)) != NULL) + return (ret); /* "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; - } + if (have_dss && dss_prec == dss_prec_secondary && (ret = + chunk_alloc_dss(arena, new_addr, size, alignment, zero, commit)) != + NULL) + return (ret); /* All strategies for allocation failed. */ - ret = NULL; -label_return: - if (ret != NULL) { - if (config_ivsalloc && base == false) { - if (rtree_set(chunks_rtree, (uintptr_t)ret, 1)) { - chunk_dealloc(ret, size, true); - return (NULL); - } - } - if (config_stats || config_prof) { - bool gdump; - malloc_mutex_lock(&chunks_mtx); - if (config_stats) - stats_chunks.nchunks += (size / chunksize); - stats_chunks.curchunks += (size / chunksize); - if (stats_chunks.curchunks > stats_chunks.highchunks) { - stats_chunks.highchunks = - stats_chunks.curchunks; - if (config_prof) - gdump = true; - } else if (config_prof) - gdump = false; - malloc_mutex_unlock(&chunks_mtx); - if (config_prof && opt_prof && opt_prof_gdump && gdump) - prof_gdump(); - } - if (config_valgrind) - VALGRIND_MAKE_MEM_UNDEFINED(ret, size); - } - assert(CHUNK_ADDR2BASE(ret) == ret); + return (NULL); +} + +void * +chunk_alloc_base(size_t size) +{ + void *ret; + bool zero, commit; + + /* + * Directly call chunk_alloc_mmap() rather than chunk_alloc_core() + * because it's critical that chunk_alloc_base() return untouched + * demand-zeroed virtual memory. + */ + zero = true; + commit = true; + ret = chunk_alloc_mmap(size, chunksize, &zero, &commit); + if (ret == NULL) + return (NULL); + if (config_valgrind) + JEMALLOC_VALGRIND_MAKE_MEM_UNDEFINED(ret, size); + + return (ret); +} + +void * +chunk_alloc_cache(arena_t *arena, chunk_hooks_t *chunk_hooks, void *new_addr, + size_t size, size_t alignment, bool *zero, bool dalloc_node) +{ + void *ret; + bool commit; + + assert(size != 0); + assert((size & chunksize_mask) == 0); + assert(alignment != 0); + assert((alignment & chunksize_mask) == 0); + + commit = true; + ret = chunk_recycle(arena, chunk_hooks, &arena->chunks_szad_cached, + &arena->chunks_ad_cached, true, new_addr, size, alignment, zero, + &commit, dalloc_node); + if (ret == NULL) + return (NULL); + assert(commit); + if (config_valgrind) + JEMALLOC_VALGRIND_MAKE_MEM_UNDEFINED(ret, size); + return (ret); +} + +static arena_t * +chunk_arena_get(unsigned arena_ind) +{ + arena_t *arena; + + /* Dodge tsd for a0 in order to avoid bootstrapping issues. */ + arena = (arena_ind == 0) ? a0get() : arena_get(tsd_fetch(), arena_ind, + false, true); + /* + * The arena we're allocating on behalf of must have been initialized + * already. + */ + assert(arena != NULL); + return (arena); +} + +static void * +chunk_alloc_default(void *new_addr, size_t size, size_t alignment, bool *zero, + bool *commit, unsigned arena_ind) +{ + void *ret; + arena_t *arena; + + arena = chunk_arena_get(arena_ind); + ret = chunk_alloc_core(arena, new_addr, size, alignment, zero, + commit, arena->dss_prec); + if (ret == NULL) + return (NULL); + if (config_valgrind) + JEMALLOC_VALGRIND_MAKE_MEM_UNDEFINED(ret, size); + + return (ret); +} + +void * +chunk_alloc_wrapper(arena_t *arena, chunk_hooks_t *chunk_hooks, void *new_addr, + size_t size, size_t alignment, bool *zero, bool *commit) +{ + void *ret; + + chunk_hooks_assure_initialized(arena, chunk_hooks); + ret = chunk_hooks->alloc(new_addr, size, alignment, zero, commit, + arena->ind); + if (ret == NULL) + return (NULL); + if (config_valgrind && chunk_hooks->alloc != chunk_alloc_default) + JEMALLOC_VALGRIND_MAKE_MEM_UNDEFINED(ret, chunksize); return (ret); } static void -chunk_record(extent_tree_t *chunks_szad, extent_tree_t *chunks_ad, void *chunk, - size_t size) +chunk_record(arena_t *arena, chunk_hooks_t *chunk_hooks, + extent_tree_t *chunks_szad, extent_tree_t *chunks_ad, bool cache, + void *chunk, size_t size, bool zeroed, bool committed) { bool unzeroed; - extent_node_t *xnode, *node, *prev, *xprev, key; + extent_node_t *node, *prev; + extent_node_t key; - unzeroed = pages_purge(chunk, size); - VALGRIND_MAKE_MEM_NOACCESS(chunk, size); + assert(!cache || !zeroed); + unzeroed = cache || !zeroed; + JEMALLOC_VALGRIND_MAKE_MEM_NOACCESS(chunk, size); - /* - * Allocate a node before acquiring chunks_mtx even though it might not - * be needed, because base_node_alloc() may cause a new base chunk to - * be allocated, which could cause deadlock if chunks_mtx were already - * held. - */ - xnode = base_node_alloc(); - /* Use xprev to implement conditional deferred deallocation of prev. */ - xprev = NULL; - - malloc_mutex_lock(&chunks_mtx); - key.addr = (void *)((uintptr_t)chunk + size); + malloc_mutex_lock(&arena->chunks_mtx); + chunk_hooks_assure_initialized_locked(arena, chunk_hooks); + extent_node_init(&key, arena, (void *)((uintptr_t)chunk + size), 0, + false, false); node = extent_tree_ad_nsearch(chunks_ad, &key); /* Try to coalesce forward. */ - if (node != NULL && node->addr == key.addr) { + if (node != NULL && extent_node_addr_get(node) == + extent_node_addr_get(&key) && extent_node_committed_get(node) == + committed && !chunk_hooks->merge(chunk, size, + extent_node_addr_get(node), extent_node_size_get(node), false, + arena->ind)) { /* * Coalesce chunk with the following address range. This does * not change the position within chunks_ad, so only * remove/insert from/into chunks_szad. */ extent_tree_szad_remove(chunks_szad, node); - node->addr = chunk; - node->size += size; - node->zeroed = (node->zeroed && (unzeroed == false)); + arena_chunk_cache_maybe_remove(arena, node, cache); + extent_node_addr_set(node, chunk); + extent_node_size_set(node, size + extent_node_size_get(node)); + extent_node_zeroed_set(node, extent_node_zeroed_get(node) && + !unzeroed); extent_tree_szad_insert(chunks_szad, node); + arena_chunk_cache_maybe_insert(arena, node, cache); } else { /* Coalescing forward failed, so insert a new node. */ - if (xnode == NULL) { + node = arena_node_alloc(arena); + if (node == NULL) { /* - * base_node_alloc() failed, which is an exceedingly - * unlikely failure. Leak chunk; its pages have - * already been purged, so this is only a virtual - * memory leak. + * Node allocation failed, which is an exceedingly + * unlikely failure. Leak chunk after making sure its + * pages have already been purged, so that this is only + * a virtual memory leak. */ + if (cache) { + chunk_purge_wrapper(arena, chunk_hooks, chunk, + size, 0, size); + } goto label_return; } - node = xnode; - xnode = NULL; /* Prevent deallocation below. */ - node->addr = chunk; - node->size = size; - node->zeroed = (unzeroed == false); + extent_node_init(node, arena, chunk, size, !unzeroed, + committed); extent_tree_ad_insert(chunks_ad, node); extent_tree_szad_insert(chunks_szad, node); + arena_chunk_cache_maybe_insert(arena, node, cache); } /* Try to coalesce backward. */ prev = extent_tree_ad_prev(chunks_ad, node); - if (prev != NULL && (void *)((uintptr_t)prev->addr + prev->size) == - chunk) { + if (prev != NULL && (void *)((uintptr_t)extent_node_addr_get(prev) + + extent_node_size_get(prev)) == chunk && + extent_node_committed_get(prev) == committed && + !chunk_hooks->merge(extent_node_addr_get(prev), + extent_node_size_get(prev), chunk, size, false, arena->ind)) { /* * Coalesce chunk with the previous address range. This does * not change the position within chunks_ad, so only @@ -275,44 +537,27 @@ chunk_record(extent_tree_t *chunks_szad, extent_tree_t *chunks_ad, void *chunk, */ extent_tree_szad_remove(chunks_szad, prev); extent_tree_ad_remove(chunks_ad, prev); - + arena_chunk_cache_maybe_remove(arena, prev, cache); extent_tree_szad_remove(chunks_szad, node); - node->addr = prev->addr; - node->size += prev->size; - node->zeroed = (node->zeroed && prev->zeroed); + arena_chunk_cache_maybe_remove(arena, node, cache); + extent_node_addr_set(node, extent_node_addr_get(prev)); + extent_node_size_set(node, extent_node_size_get(prev) + + extent_node_size_get(node)); + extent_node_zeroed_set(node, extent_node_zeroed_get(prev) && + extent_node_zeroed_get(node)); extent_tree_szad_insert(chunks_szad, node); + arena_chunk_cache_maybe_insert(arena, node, cache); - xprev = prev; + arena_node_dalloc(arena, prev); } label_return: - malloc_mutex_unlock(&chunks_mtx); - /* - * Deallocate xnode and/or xprev after unlocking chunks_mtx in order to - * avoid potential deadlock. - */ - if (xnode != NULL) - base_node_dealloc(xnode); - if (xprev != NULL) - base_node_dealloc(xprev); + malloc_mutex_unlock(&arena->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) +chunk_dalloc_cache(arena_t *arena, chunk_hooks_t *chunk_hooks, void *chunk, + size_t size, bool committed) { assert(chunk != NULL); @@ -320,22 +565,164 @@ chunk_dealloc(void *chunk, size_t size, bool unmap) assert(size != 0); assert((size & chunksize_mask) == 0); - if (config_ivsalloc) - rtree_set(chunks_rtree, (uintptr_t)chunk, 0); - 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); + chunk_record(arena, chunk_hooks, &arena->chunks_szad_cached, + &arena->chunks_ad_cached, true, chunk, size, false, committed); + arena_maybe_purge(arena); +} + +void +chunk_dalloc_arena(arena_t *arena, chunk_hooks_t *chunk_hooks, void *chunk, + size_t size, bool zeroed, bool committed) +{ + + assert(chunk != NULL); + assert(CHUNK_ADDR2BASE(chunk) == chunk); + assert(size != 0); + assert((size & chunksize_mask) == 0); + + chunk_hooks_assure_initialized(arena, chunk_hooks); + /* Try to deallocate. */ + if (!chunk_hooks->dalloc(chunk, size, committed, arena->ind)) + return; + /* Try to decommit; purge if that fails. */ + if (committed) { + committed = chunk_hooks->decommit(chunk, size, 0, size, + arena->ind); } + zeroed = !committed || !chunk_hooks->purge(chunk, size, 0, size, + arena->ind); + chunk_record(arena, chunk_hooks, &arena->chunks_szad_retained, + &arena->chunks_ad_retained, false, chunk, size, zeroed, committed); +} - if (unmap) - chunk_unmap(chunk, size); +static bool +chunk_dalloc_default(void *chunk, size_t size, bool committed, + unsigned arena_ind) +{ + + if (!have_dss || !chunk_in_dss(chunk)) + return (chunk_dalloc_mmap(chunk, size)); + return (true); +} + +void +chunk_dalloc_wrapper(arena_t *arena, chunk_hooks_t *chunk_hooks, void *chunk, + size_t size, bool committed) +{ + + chunk_hooks_assure_initialized(arena, chunk_hooks); + chunk_hooks->dalloc(chunk, size, committed, arena->ind); + if (config_valgrind && chunk_hooks->dalloc != chunk_dalloc_default) + JEMALLOC_VALGRIND_MAKE_MEM_NOACCESS(chunk, size); +} + +static bool +chunk_commit_default(void *chunk, size_t size, size_t offset, size_t length, + unsigned arena_ind) +{ + + return (pages_commit((void *)((uintptr_t)chunk + (uintptr_t)offset), + length)); +} + +static bool +chunk_decommit_default(void *chunk, size_t size, size_t offset, size_t length, + unsigned arena_ind) +{ + + return (pages_decommit((void *)((uintptr_t)chunk + (uintptr_t)offset), + length)); +} + +bool +chunk_purge_arena(arena_t *arena, void *chunk, size_t offset, size_t length) +{ + + assert(chunk != NULL); + assert(CHUNK_ADDR2BASE(chunk) == chunk); + assert((offset & PAGE_MASK) == 0); + assert(length != 0); + assert((length & PAGE_MASK) == 0); + + return (pages_purge((void *)((uintptr_t)chunk + (uintptr_t)offset), + length)); +} + +static bool +chunk_purge_default(void *chunk, size_t size, size_t offset, size_t length, + unsigned arena_ind) +{ + + return (chunk_purge_arena(chunk_arena_get(arena_ind), chunk, offset, + length)); +} + +bool +chunk_purge_wrapper(arena_t *arena, chunk_hooks_t *chunk_hooks, void *chunk, + size_t size, size_t offset, size_t length) +{ + + chunk_hooks_assure_initialized(arena, chunk_hooks); + return (chunk_hooks->purge(chunk, size, offset, length, arena->ind)); +} + +static bool +chunk_split_default(void *chunk, size_t size, size_t size_a, size_t size_b, + bool committed, unsigned arena_ind) +{ + + if (!maps_coalesce) + return (true); + return (false); +} + +static bool +chunk_merge_default(void *chunk_a, size_t size_a, void *chunk_b, size_t size_b, + bool committed, unsigned arena_ind) +{ + + if (!maps_coalesce) + return (true); + if (have_dss && chunk_in_dss(chunk_a) != chunk_in_dss(chunk_b)) + return (true); + + return (false); +} + +static rtree_node_elm_t * +chunks_rtree_node_alloc(size_t nelms) +{ + + return ((rtree_node_elm_t *)base_alloc(nelms * + sizeof(rtree_node_elm_t))); } bool chunk_boot(void) { +#ifdef _WIN32 + SYSTEM_INFO info; + GetSystemInfo(&info); + + /* + * Verify actual page size is equal to or an integral multiple of + * configured page size. + */ + if (info.dwPageSize & ((1U << LG_PAGE) - 1)) + return (true); + + /* + * Configure chunksize (if not set) to match granularity (usually 64K), + * so pages_map will always take fast path. + */ + if (!opt_lg_chunk) { + opt_lg_chunk = jemalloc_ffs((int)info.dwAllocationGranularity) + - 1; + } +#else + if (!opt_lg_chunk) + opt_lg_chunk = LG_CHUNK_DEFAULT; +#endif /* Set variables according to the value of opt_lg_chunk. */ chunksize = (ZU(1) << opt_lg_chunk); @@ -343,23 +730,11 @@ chunk_boot(void) chunksize_mask = chunksize - 1; chunk_npages = (chunksize >> LG_PAGE); - if (config_stats || config_prof) { - if (malloc_mutex_init(&chunks_mtx)) - return (true); - memset(&stats_chunks, 0, sizeof(chunk_stats_t)); - } - if (config_dss && chunk_dss_boot()) + if (have_dss && chunk_dss_boot()) + return (true); + if (rtree_new(&chunks_rtree, (ZU(1) << (LG_SIZEOF_PTR+3)) - + opt_lg_chunk, chunks_rtree_node_alloc, NULL)) return (true); - 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, base_alloc, NULL); - if (chunks_rtree == NULL) - return (true); - } return (false); } @@ -368,9 +743,6 @@ void chunk_prefork(void) { - malloc_mutex_prefork(&chunks_mtx); - if (config_ivsalloc) - rtree_prefork(chunks_rtree); chunk_dss_prefork(); } @@ -379,9 +751,6 @@ chunk_postfork_parent(void) { chunk_dss_postfork_parent(); - if (config_ivsalloc) - rtree_postfork_parent(chunks_rtree); - malloc_mutex_postfork_parent(&chunks_mtx); } void @@ -389,7 +758,4 @@ chunk_postfork_child(void) { chunk_dss_postfork_child(); - if (config_ivsalloc) - rtree_postfork_child(chunks_rtree); - malloc_mutex_postfork_child(&chunks_mtx); } diff --git a/contrib/jemalloc/src/chunk_dss.c b/contrib/jemalloc/src/chunk_dss.c index 510bb8bee859..de0546d00226 100644 --- a/contrib/jemalloc/src/chunk_dss.c +++ b/contrib/jemalloc/src/chunk_dss.c @@ -32,7 +32,7 @@ static void * chunk_dss_sbrk(intptr_t increment) { -#ifdef JEMALLOC_HAVE_SBRK +#ifdef JEMALLOC_DSS return (sbrk(increment)); #else not_implemented(); @@ -45,7 +45,7 @@ chunk_dss_prec_get(void) { dss_prec_t ret; - if (config_dss == false) + if (!have_dss) return (dss_prec_disabled); malloc_mutex_lock(&dss_mtx); ret = dss_prec_default; @@ -57,8 +57,8 @@ bool chunk_dss_prec_set(dss_prec_t dss_prec) { - if (config_dss == false) - return (true); + if (!have_dss) + return (dss_prec != dss_prec_disabled); malloc_mutex_lock(&dss_mtx); dss_prec_default = dss_prec; malloc_mutex_unlock(&dss_mtx); @@ -66,11 +66,12 @@ chunk_dss_prec_set(dss_prec_t dss_prec) } void * -chunk_alloc_dss(size_t size, size_t alignment, bool *zero) +chunk_alloc_dss(arena_t *arena, void *new_addr, size_t size, size_t alignment, + bool *zero, bool *commit) { void *ret; - cassert(config_dss); + cassert(have_dss); assert(size > 0 && (size & chunksize_mask) == 0); assert(alignment > 0 && (alignment & chunksize_mask) == 0); @@ -93,8 +94,17 @@ chunk_alloc_dss(size_t size, size_t alignment, bool *zero) * malloc. */ do { + /* Avoid an unnecessary system call. */ + if (new_addr != NULL && dss_max != new_addr) + break; + /* Get the current end of the DSS. */ dss_max = chunk_dss_sbrk(0); + + /* Make sure the earlier condition still holds. */ + if (new_addr != NULL && dss_max != new_addr) + break; + /* * Calculate how much padding is necessary to * chunk-align the end of the DSS. @@ -123,12 +133,20 @@ chunk_alloc_dss(size_t size, size_t alignment, bool *zero) /* Success. */ dss_max = dss_next; malloc_mutex_unlock(&dss_mtx); - if (cpad_size != 0) - chunk_unmap(cpad, cpad_size); + if (cpad_size != 0) { + chunk_hooks_t chunk_hooks = + CHUNK_HOOKS_INITIALIZER; + chunk_dalloc_wrapper(arena, + &chunk_hooks, cpad, cpad_size, + true); + } if (*zero) { - VALGRIND_MAKE_MEM_UNDEFINED(ret, size); + JEMALLOC_VALGRIND_MAKE_MEM_UNDEFINED( + ret, size); memset(ret, 0, size); } + if (!*commit) + *commit = pages_decommit(ret, size); return (ret); } } while (dss_prev != (void *)-1); @@ -143,7 +161,7 @@ chunk_in_dss(void *chunk) { bool ret; - cassert(config_dss); + cassert(have_dss); malloc_mutex_lock(&dss_mtx); if ((uintptr_t)chunk >= (uintptr_t)dss_base @@ -160,7 +178,7 @@ bool chunk_dss_boot(void) { - cassert(config_dss); + cassert(have_dss); if (malloc_mutex_init(&dss_mtx)) return (true); @@ -175,7 +193,7 @@ void chunk_dss_prefork(void) { - if (config_dss) + if (have_dss) malloc_mutex_prefork(&dss_mtx); } @@ -183,7 +201,7 @@ void chunk_dss_postfork_parent(void) { - if (config_dss) + if (have_dss) malloc_mutex_postfork_parent(&dss_mtx); } @@ -191,7 +209,7 @@ void chunk_dss_postfork_child(void) { - if (config_dss) + if (have_dss) malloc_mutex_postfork_child(&dss_mtx); } diff --git a/contrib/jemalloc/src/chunk_mmap.c b/contrib/jemalloc/src/chunk_mmap.c index 2056d793f053..36eb07548fc9 100644 --- a/contrib/jemalloc/src/chunk_mmap.c +++ b/contrib/jemalloc/src/chunk_mmap.c @@ -1,146 +1,10 @@ #define JEMALLOC_CHUNK_MMAP_C_ #include "jemalloc/internal/jemalloc_internal.h" -/******************************************************************************/ -/* Function prototypes for non-inline static functions. */ - -static void *pages_map(void *addr, size_t size); -static void pages_unmap(void *addr, size_t size); -static void *chunk_alloc_mmap_slow(size_t size, size_t alignment, - bool *zero); - /******************************************************************************/ static void * -pages_map(void *addr, size_t size) -{ - void *ret; - - assert(size != 0); - -#ifdef _WIN32 - /* - * If VirtualAlloc can't allocate at the given address when one is - * given, it fails and returns NULL. - */ - ret = VirtualAlloc(addr, size, MEM_COMMIT | MEM_RESERVE, - PAGE_READWRITE); -#else - /* - * We don't use MAP_FIXED here, because it can cause the *replacement* - * of existing mappings, and we only want to create new mappings. - */ - ret = mmap(addr, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, - -1, 0); - assert(ret != NULL); - - if (ret == MAP_FAILED) - ret = NULL; - else if (addr != NULL && ret != addr) { - /* - * We succeeded in mapping memory, but not in the right place. - */ - if (munmap(ret, size) == -1) { - char buf[BUFERROR_BUF]; - - buferror(get_errno(), buf, sizeof(buf)); - malloc_printf(": Error in " -#ifdef _WIN32 - "VirtualFree" -#else - "munmap" -#endif - "(): %s\n", buf); - if (opt_abort) - abort(); - } -} - -static void * -pages_trim(void *addr, size_t alloc_size, size_t leadsize, size_t size) -{ - void *ret = (void *)((uintptr_t)addr + leadsize); - - assert(alloc_size >= leadsize + size); -#ifdef _WIN32 - { - void *new_addr; - - pages_unmap(addr, alloc_size); - new_addr = pages_map(ret, size); - if (new_addr == ret) - return (ret); - if (new_addr) - pages_unmap(new_addr, size); - return (NULL); - } -#else - { - size_t trailsize = alloc_size - leadsize - size; - - if (leadsize != 0) - pages_unmap(addr, leadsize); - if (trailsize != 0) - pages_unmap((void *)((uintptr_t)ret + size), trailsize); - return (ret); - } -#endif -} - -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 - 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 * -chunk_alloc_mmap_slow(size_t size, size_t alignment, bool *zero) +chunk_alloc_mmap_slow(size_t size, size_t alignment, bool *zero, bool *commit) { void *ret, *pages; size_t alloc_size, leadsize; @@ -160,11 +24,13 @@ chunk_alloc_mmap_slow(size_t size, size_t alignment, bool *zero) assert(ret != NULL); *zero = true; + if (!*commit) + *commit = pages_decommit(ret, size); return (ret); } void * -chunk_alloc_mmap(size_t size, size_t alignment, bool *zero) +chunk_alloc_mmap(size_t size, size_t alignment, bool *zero, bool *commit) { void *ret; size_t offset; @@ -191,20 +57,22 @@ chunk_alloc_mmap(size_t size, size_t alignment, bool *zero) offset = ALIGNMENT_ADDR2OFFSET(ret, alignment); if (offset != 0) { pages_unmap(ret, size); - return (chunk_alloc_mmap_slow(size, alignment, zero)); + return (chunk_alloc_mmap_slow(size, alignment, zero, commit)); } assert(ret != NULL); *zero = true; + if (!*commit) + *commit = pages_decommit(ret, size); return (ret); } bool -chunk_dealloc_mmap(void *chunk, size_t size) +chunk_dalloc_mmap(void *chunk, size_t size) { if (config_munmap) pages_unmap(chunk, size); - return (config_munmap == false); + return (!config_munmap); } diff --git a/contrib/jemalloc/src/ckh.c b/contrib/jemalloc/src/ckh.c index 04c52966193a..53a1c1ef11d2 100644 --- a/contrib/jemalloc/src/ckh.c +++ b/contrib/jemalloc/src/ckh.c @@ -40,8 +40,8 @@ /******************************************************************************/ /* Function prototypes for non-inline static functions. */ -static bool ckh_grow(ckh_t *ckh); -static void ckh_shrink(ckh_t *ckh); +static bool ckh_grow(tsd_t *tsd, ckh_t *ckh); +static void ckh_shrink(tsd_t *tsd, ckh_t *ckh); /******************************************************************************/ @@ -185,7 +185,7 @@ ckh_evict_reloc_insert(ckh_t *ckh, size_t argbucket, void const **argkey, } bucket = tbucket; - if (ckh_try_bucket_insert(ckh, bucket, key, data) == false) + if (!ckh_try_bucket_insert(ckh, bucket, key, data)) return (false); } } @@ -201,12 +201,12 @@ ckh_try_insert(ckh_t *ckh, void const**argkey, void const**argdata) /* Try to insert in primary bucket. */ bucket = hashes[0] & ((ZU(1) << ckh->lg_curbuckets) - 1); - if (ckh_try_bucket_insert(ckh, bucket, key, data) == false) + if (!ckh_try_bucket_insert(ckh, bucket, key, data)) return (false); /* Try to insert in secondary bucket. */ bucket = hashes[1] & ((ZU(1) << ckh->lg_curbuckets) - 1); - if (ckh_try_bucket_insert(ckh, bucket, key, data) == false) + if (!ckh_try_bucket_insert(ckh, bucket, key, data)) return (false); /* @@ -243,7 +243,7 @@ ckh_rebuild(ckh_t *ckh, ckhc_t *aTab) } static bool -ckh_grow(ckh_t *ckh) +ckh_grow(tsd_t *tsd, ckh_t *ckh) { bool ret; ckhc_t *tab, *ttab; @@ -270,7 +270,8 @@ ckh_grow(ckh_t *ckh) ret = true; goto label_return; } - tab = (ckhc_t *)ipalloc(usize, CACHELINE, true); + tab = (ckhc_t *)ipallocztm(tsd, usize, CACHELINE, true, NULL, + true, NULL); if (tab == NULL) { ret = true; goto label_return; @@ -281,13 +282,13 @@ ckh_grow(ckh_t *ckh) tab = ttab; ckh->lg_curbuckets = lg_curcells - LG_CKH_BUCKET_CELLS; - if (ckh_rebuild(ckh, tab) == false) { - idalloc(tab); + if (!ckh_rebuild(ckh, tab)) { + idalloctm(tsd, tab, tcache_get(tsd, false), true); break; } /* Rebuilding failed, so back out partially rebuilt table. */ - idalloc(ckh->tab); + idalloctm(tsd, ckh->tab, tcache_get(tsd, false), true); ckh->tab = tab; ckh->lg_curbuckets = lg_prevbuckets; } @@ -298,7 +299,7 @@ ckh_grow(ckh_t *ckh) } static void -ckh_shrink(ckh_t *ckh) +ckh_shrink(tsd_t *tsd, ckh_t *ckh) { ckhc_t *tab, *ttab; size_t lg_curcells, usize; @@ -313,7 +314,8 @@ ckh_shrink(ckh_t *ckh) usize = sa2u(sizeof(ckhc_t) << lg_curcells, CACHELINE); if (usize == 0) return; - tab = (ckhc_t *)ipalloc(usize, CACHELINE, true); + tab = (ckhc_t *)ipallocztm(tsd, usize, CACHELINE, true, NULL, true, + NULL); if (tab == NULL) { /* * An OOM error isn't worth propagating, since it doesn't @@ -327,8 +329,8 @@ ckh_shrink(ckh_t *ckh) tab = ttab; ckh->lg_curbuckets = lg_curcells - LG_CKH_BUCKET_CELLS; - if (ckh_rebuild(ckh, tab) == false) { - idalloc(tab); + if (!ckh_rebuild(ckh, tab)) { + idalloctm(tsd, tab, tcache_get(tsd, false), true); #ifdef CKH_COUNT ckh->nshrinks++; #endif @@ -336,7 +338,7 @@ ckh_shrink(ckh_t *ckh) } /* Rebuilding failed, so back out partially rebuilt table. */ - idalloc(ckh->tab); + idalloctm(tsd, ckh->tab, tcache_get(tsd, false), true); ckh->tab = tab; ckh->lg_curbuckets = lg_prevbuckets; #ifdef CKH_COUNT @@ -345,7 +347,8 @@ ckh_shrink(ckh_t *ckh) } bool -ckh_new(ckh_t *ckh, size_t minitems, ckh_hash_t *hash, ckh_keycomp_t *keycomp) +ckh_new(tsd_t *tsd, ckh_t *ckh, size_t minitems, ckh_hash_t *hash, + ckh_keycomp_t *keycomp) { bool ret; size_t mincells, usize; @@ -366,10 +369,10 @@ ckh_new(ckh_t *ckh, size_t minitems, ckh_hash_t *hash, ckh_keycomp_t *keycomp) ckh->count = 0; /* - * Find the minimum power of 2 that is large enough to fit aBaseCount + * Find the minimum power of 2 that is large enough to fit minitems * entries. We are using (2+,2) cuckoo hashing, which has an expected * maximum load factor of at least ~0.86, so 0.75 is a conservative load - * factor that will typically allow 2^aLgMinItems to fit without ever + * factor that will typically allow mincells items to fit without ever * growing the table. */ assert(LG_CKH_BUCKET_CELLS > 0); @@ -388,7 +391,8 @@ ckh_new(ckh_t *ckh, size_t minitems, ckh_hash_t *hash, ckh_keycomp_t *keycomp) ret = true; goto label_return; } - ckh->tab = (ckhc_t *)ipalloc(usize, CACHELINE, true); + ckh->tab = (ckhc_t *)ipallocztm(tsd, usize, CACHELINE, true, NULL, true, + NULL); if (ckh->tab == NULL) { ret = true; goto label_return; @@ -400,16 +404,16 @@ ckh_new(ckh_t *ckh, size_t minitems, ckh_hash_t *hash, ckh_keycomp_t *keycomp) } void -ckh_delete(ckh_t *ckh) +ckh_delete(tsd_t *tsd, ckh_t *ckh) { assert(ckh != NULL); #ifdef CKH_VERBOSE malloc_printf( - "%s(%p): ngrows: %"PRIu64", nshrinks: %"PRIu64"," - " nshrinkfails: %"PRIu64", ninserts: %"PRIu64"," - " nrelocs: %"PRIu64"\n", __func__, ckh, + "%s(%p): ngrows: %"FMTu64", nshrinks: %"FMTu64"," + " nshrinkfails: %"FMTu64", ninserts: %"FMTu64"," + " nrelocs: %"FMTu64"\n", __func__, ckh, (unsigned long long)ckh->ngrows, (unsigned long long)ckh->nshrinks, (unsigned long long)ckh->nshrinkfails, @@ -417,7 +421,7 @@ ckh_delete(ckh_t *ckh) (unsigned long long)ckh->nrelocs); #endif - idalloc(ckh->tab); + idalloctm(tsd, ckh->tab, tcache_get(tsd, false), true); if (config_debug) memset(ckh, 0x5a, sizeof(ckh_t)); } @@ -452,7 +456,7 @@ ckh_iter(ckh_t *ckh, size_t *tabind, void **key, void **data) } bool -ckh_insert(ckh_t *ckh, const void *key, const void *data) +ckh_insert(tsd_t *tsd, ckh_t *ckh, const void *key, const void *data) { bool ret; @@ -464,7 +468,7 @@ ckh_insert(ckh_t *ckh, const void *key, const void *data) #endif while (ckh_try_insert(ckh, &key, &data)) { - if (ckh_grow(ckh)) { + if (ckh_grow(tsd, ckh)) { ret = true; goto label_return; } @@ -476,7 +480,8 @@ ckh_insert(ckh_t *ckh, const void *key, const void *data) } bool -ckh_remove(ckh_t *ckh, const void *searchkey, void **key, void **data) +ckh_remove(tsd_t *tsd, ckh_t *ckh, const void *searchkey, void **key, + void **data) { size_t cell; @@ -497,7 +502,7 @@ ckh_remove(ckh_t *ckh, const void *searchkey, void **key, void **data) + LG_CKH_BUCKET_CELLS - 2)) && ckh->lg_curbuckets > ckh->lg_minbuckets) { /* Ignore error due to OOM. */ - ckh_shrink(ckh); + ckh_shrink(tsd, ckh); } return (false); diff --git a/contrib/jemalloc/src/ctl.c b/contrib/jemalloc/src/ctl.c index cc2c5aef570f..3de8e602d11f 100644 --- a/contrib/jemalloc/src/ctl.c +++ b/contrib/jemalloc/src/ctl.c @@ -7,7 +7,6 @@ /* * ctl_mtx protects the following: * - ctl_stats.* - * - opt_prof_active */ static malloc_mutex_t ctl_mtx; static bool ctl_initialized; @@ -17,14 +16,14 @@ static ctl_stats_t ctl_stats; /******************************************************************************/ /* Helpers for named and indexed nodes. */ -static inline const ctl_named_node_t * +JEMALLOC_INLINE_C const ctl_named_node_t * ctl_named_node(const ctl_node_t *node) { return ((node->named) ? (const ctl_named_node_t *)node : NULL); } -static inline const ctl_named_node_t * +JEMALLOC_INLINE_C const ctl_named_node_t * ctl_named_children(const ctl_named_node_t *node, int index) { const ctl_named_node_t *children = ctl_named_node(node->children); @@ -32,12 +31,11 @@ ctl_named_children(const ctl_named_node_t *node, int index) return (children ? &children[index] : NULL); } -static inline const ctl_indexed_node_t * +JEMALLOC_INLINE_C const ctl_indexed_node_t * ctl_indexed_node(const ctl_node_t *node) { - return ((node->named == false) ? (const ctl_indexed_node_t *)node : - NULL); + return (!node->named ? (const ctl_indexed_node_t *)node : NULL); } /******************************************************************************/ @@ -68,16 +66,17 @@ CTL_PROTO(version) CTL_PROTO(epoch) CTL_PROTO(thread_tcache_enabled) CTL_PROTO(thread_tcache_flush) +CTL_PROTO(thread_prof_name) +CTL_PROTO(thread_prof_active) CTL_PROTO(thread_arena) CTL_PROTO(thread_allocated) CTL_PROTO(thread_allocatedp) CTL_PROTO(thread_deallocated) CTL_PROTO(thread_deallocatedp) +CTL_PROTO(config_cache_oblivious) CTL_PROTO(config_debug) -CTL_PROTO(config_dss) CTL_PROTO(config_fill) CTL_PROTO(config_lazy_lock) -CTL_PROTO(config_mremap) CTL_PROTO(config_munmap) CTL_PROTO(config_prof) CTL_PROTO(config_prof_libgcc) @@ -99,22 +98,27 @@ CTL_PROTO(opt_zero) CTL_PROTO(opt_quarantine) CTL_PROTO(opt_redzone) CTL_PROTO(opt_utrace) -CTL_PROTO(opt_valgrind) CTL_PROTO(opt_xmalloc) CTL_PROTO(opt_tcache) CTL_PROTO(opt_lg_tcache_max) CTL_PROTO(opt_prof) CTL_PROTO(opt_prof_prefix) CTL_PROTO(opt_prof_active) +CTL_PROTO(opt_prof_thread_active_init) CTL_PROTO(opt_lg_prof_sample) CTL_PROTO(opt_lg_prof_interval) CTL_PROTO(opt_prof_gdump) CTL_PROTO(opt_prof_final) CTL_PROTO(opt_prof_leak) CTL_PROTO(opt_prof_accum) +CTL_PROTO(tcache_create) +CTL_PROTO(tcache_flush) +CTL_PROTO(tcache_destroy) CTL_PROTO(arena_i_purge) static void arena_purge(unsigned arena_ind); CTL_PROTO(arena_i_dss) +CTL_PROTO(arena_i_lg_dirty_mult) +CTL_PROTO(arena_i_chunk_hooks) INDEX_PROTO(arena_i) CTL_PROTO(arenas_bin_i_size) CTL_PROTO(arenas_bin_i_nregs) @@ -122,25 +126,26 @@ CTL_PROTO(arenas_bin_i_run_size) INDEX_PROTO(arenas_bin_i) CTL_PROTO(arenas_lrun_i_size) INDEX_PROTO(arenas_lrun_i) +CTL_PROTO(arenas_hchunk_i_size) +INDEX_PROTO(arenas_hchunk_i) CTL_PROTO(arenas_narenas) CTL_PROTO(arenas_initialized) +CTL_PROTO(arenas_lg_dirty_mult) CTL_PROTO(arenas_quantum) CTL_PROTO(arenas_page) CTL_PROTO(arenas_tcache_max) CTL_PROTO(arenas_nbins) CTL_PROTO(arenas_nhbins) CTL_PROTO(arenas_nlruns) -CTL_PROTO(arenas_purge) +CTL_PROTO(arenas_nhchunks) CTL_PROTO(arenas_extend) +CTL_PROTO(prof_thread_active_init) CTL_PROTO(prof_active) CTL_PROTO(prof_dump) +CTL_PROTO(prof_gdump) +CTL_PROTO(prof_reset) CTL_PROTO(prof_interval) -CTL_PROTO(stats_chunks_current) -CTL_PROTO(stats_chunks_total) -CTL_PROTO(stats_chunks_high) -CTL_PROTO(stats_huge_allocated) -CTL_PROTO(stats_huge_nmalloc) -CTL_PROTO(stats_huge_ndalloc) +CTL_PROTO(lg_prof_sample) CTL_PROTO(stats_arenas_i_small_allocated) CTL_PROTO(stats_arenas_i_small_nmalloc) CTL_PROTO(stats_arenas_i_small_ndalloc) @@ -149,10 +154,14 @@ CTL_PROTO(stats_arenas_i_large_allocated) CTL_PROTO(stats_arenas_i_large_nmalloc) CTL_PROTO(stats_arenas_i_large_ndalloc) CTL_PROTO(stats_arenas_i_large_nrequests) -CTL_PROTO(stats_arenas_i_bins_j_allocated) +CTL_PROTO(stats_arenas_i_huge_allocated) +CTL_PROTO(stats_arenas_i_huge_nmalloc) +CTL_PROTO(stats_arenas_i_huge_ndalloc) +CTL_PROTO(stats_arenas_i_huge_nrequests) CTL_PROTO(stats_arenas_i_bins_j_nmalloc) CTL_PROTO(stats_arenas_i_bins_j_ndalloc) CTL_PROTO(stats_arenas_i_bins_j_nrequests) +CTL_PROTO(stats_arenas_i_bins_j_curregs) CTL_PROTO(stats_arenas_i_bins_j_nfills) CTL_PROTO(stats_arenas_i_bins_j_nflushes) CTL_PROTO(stats_arenas_i_bins_j_nruns) @@ -164,18 +173,28 @@ CTL_PROTO(stats_arenas_i_lruns_j_ndalloc) 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_hchunks_j_nmalloc) +CTL_PROTO(stats_arenas_i_hchunks_j_ndalloc) +CTL_PROTO(stats_arenas_i_hchunks_j_nrequests) +CTL_PROTO(stats_arenas_i_hchunks_j_curhchunks) +INDEX_PROTO(stats_arenas_i_hchunks_j) CTL_PROTO(stats_arenas_i_nthreads) CTL_PROTO(stats_arenas_i_dss) +CTL_PROTO(stats_arenas_i_lg_dirty_mult) CTL_PROTO(stats_arenas_i_pactive) CTL_PROTO(stats_arenas_i_pdirty) CTL_PROTO(stats_arenas_i_mapped) CTL_PROTO(stats_arenas_i_npurge) CTL_PROTO(stats_arenas_i_nmadvise) CTL_PROTO(stats_arenas_i_purged) +CTL_PROTO(stats_arenas_i_metadata_mapped) +CTL_PROTO(stats_arenas_i_metadata_allocated) INDEX_PROTO(stats_arenas_i) CTL_PROTO(stats_cactive) CTL_PROTO(stats_allocated) CTL_PROTO(stats_active) +CTL_PROTO(stats_metadata) +CTL_PROTO(stats_resident) CTL_PROTO(stats_mapped) /******************************************************************************/ @@ -197,71 +216,84 @@ CTL_PROTO(stats_mapped) */ #define INDEX(i) {false}, i##_index -static const ctl_named_node_t tcache_node[] = { +static const ctl_named_node_t thread_tcache_node[] = { {NAME("enabled"), CTL(thread_tcache_enabled)}, {NAME("flush"), CTL(thread_tcache_flush)} }; +static const ctl_named_node_t thread_prof_node[] = { + {NAME("name"), CTL(thread_prof_name)}, + {NAME("active"), CTL(thread_prof_active)} +}; + static const ctl_named_node_t thread_node[] = { {NAME("arena"), CTL(thread_arena)}, {NAME("allocated"), CTL(thread_allocated)}, {NAME("allocatedp"), CTL(thread_allocatedp)}, {NAME("deallocated"), CTL(thread_deallocated)}, {NAME("deallocatedp"), CTL(thread_deallocatedp)}, - {NAME("tcache"), CHILD(named, tcache)} + {NAME("tcache"), CHILD(named, thread_tcache)}, + {NAME("prof"), CHILD(named, thread_prof)} }; static const ctl_named_node_t config_node[] = { - {NAME("debug"), CTL(config_debug)}, - {NAME("dss"), CTL(config_dss)}, - {NAME("fill"), CTL(config_fill)}, - {NAME("lazy_lock"), CTL(config_lazy_lock)}, - {NAME("mremap"), CTL(config_mremap)}, - {NAME("munmap"), CTL(config_munmap)}, - {NAME("prof"), CTL(config_prof)}, - {NAME("prof_libgcc"), CTL(config_prof_libgcc)}, - {NAME("prof_libunwind"), CTL(config_prof_libunwind)}, - {NAME("stats"), CTL(config_stats)}, - {NAME("tcache"), CTL(config_tcache)}, - {NAME("tls"), CTL(config_tls)}, - {NAME("utrace"), CTL(config_utrace)}, - {NAME("valgrind"), CTL(config_valgrind)}, - {NAME("xmalloc"), CTL(config_xmalloc)} + {NAME("cache_oblivious"), CTL(config_cache_oblivious)}, + {NAME("debug"), CTL(config_debug)}, + {NAME("fill"), CTL(config_fill)}, + {NAME("lazy_lock"), CTL(config_lazy_lock)}, + {NAME("munmap"), CTL(config_munmap)}, + {NAME("prof"), CTL(config_prof)}, + {NAME("prof_libgcc"), CTL(config_prof_libgcc)}, + {NAME("prof_libunwind"), CTL(config_prof_libunwind)}, + {NAME("stats"), CTL(config_stats)}, + {NAME("tcache"), CTL(config_tcache)}, + {NAME("tls"), CTL(config_tls)}, + {NAME("utrace"), CTL(config_utrace)}, + {NAME("valgrind"), CTL(config_valgrind)}, + {NAME("xmalloc"), CTL(config_xmalloc)} }; 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)}, - {NAME("stats_print"), CTL(opt_stats_print)}, - {NAME("junk"), CTL(opt_junk)}, - {NAME("zero"), CTL(opt_zero)}, - {NAME("quarantine"), CTL(opt_quarantine)}, - {NAME("redzone"), CTL(opt_redzone)}, - {NAME("utrace"), CTL(opt_utrace)}, - {NAME("valgrind"), CTL(opt_valgrind)}, - {NAME("xmalloc"), CTL(opt_xmalloc)}, - {NAME("tcache"), CTL(opt_tcache)}, - {NAME("lg_tcache_max"), CTL(opt_lg_tcache_max)}, - {NAME("prof"), CTL(opt_prof)}, - {NAME("prof_prefix"), CTL(opt_prof_prefix)}, - {NAME("prof_active"), CTL(opt_prof_active)}, - {NAME("lg_prof_sample"), CTL(opt_lg_prof_sample)}, - {NAME("lg_prof_interval"), CTL(opt_lg_prof_interval)}, - {NAME("prof_gdump"), CTL(opt_prof_gdump)}, - {NAME("prof_final"), CTL(opt_prof_final)}, - {NAME("prof_leak"), CTL(opt_prof_leak)}, - {NAME("prof_accum"), CTL(opt_prof_accum)} + {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)}, + {NAME("stats_print"), CTL(opt_stats_print)}, + {NAME("junk"), CTL(opt_junk)}, + {NAME("zero"), CTL(opt_zero)}, + {NAME("quarantine"), CTL(opt_quarantine)}, + {NAME("redzone"), CTL(opt_redzone)}, + {NAME("utrace"), CTL(opt_utrace)}, + {NAME("xmalloc"), CTL(opt_xmalloc)}, + {NAME("tcache"), CTL(opt_tcache)}, + {NAME("lg_tcache_max"), CTL(opt_lg_tcache_max)}, + {NAME("prof"), CTL(opt_prof)}, + {NAME("prof_prefix"), CTL(opt_prof_prefix)}, + {NAME("prof_active"), CTL(opt_prof_active)}, + {NAME("prof_thread_active_init"), CTL(opt_prof_thread_active_init)}, + {NAME("lg_prof_sample"), CTL(opt_lg_prof_sample)}, + {NAME("lg_prof_interval"), CTL(opt_lg_prof_interval)}, + {NAME("prof_gdump"), CTL(opt_prof_gdump)}, + {NAME("prof_final"), CTL(opt_prof_final)}, + {NAME("prof_leak"), CTL(opt_prof_leak)}, + {NAME("prof_accum"), CTL(opt_prof_accum)} +}; + +static const ctl_named_node_t tcache_node[] = { + {NAME("create"), CTL(tcache_create)}, + {NAME("flush"), CTL(tcache_flush)}, + {NAME("destroy"), CTL(tcache_destroy)} }; static const ctl_named_node_t arena_i_node[] = { - {NAME("purge"), CTL(arena_i_purge)}, - {NAME("dss"), CTL(arena_i_dss)} + {NAME("purge"), CTL(arena_i_purge)}, + {NAME("dss"), CTL(arena_i_dss)}, + {NAME("lg_dirty_mult"), CTL(arena_i_lg_dirty_mult)}, + {NAME("chunk_hooks"), CTL(arena_i_chunk_hooks)} }; static const ctl_named_node_t super_arena_i_node[] = { - {NAME(""), CHILD(named, arena_i)} + {NAME(""), CHILD(named, arena_i)} }; static const ctl_indexed_node_t arena_node[] = { @@ -269,12 +301,12 @@ static const ctl_indexed_node_t arena_node[] = { }; static const ctl_named_node_t arenas_bin_i_node[] = { - {NAME("size"), CTL(arenas_bin_i_size)}, - {NAME("nregs"), CTL(arenas_bin_i_nregs)}, - {NAME("run_size"), CTL(arenas_bin_i_run_size)} + {NAME("size"), CTL(arenas_bin_i_size)}, + {NAME("nregs"), CTL(arenas_bin_i_nregs)}, + {NAME("run_size"), CTL(arenas_bin_i_run_size)} }; static const ctl_named_node_t super_arenas_bin_i_node[] = { - {NAME(""), CHILD(named, arenas_bin_i)} + {NAME(""), CHILD(named, arenas_bin_i)} }; static const ctl_indexed_node_t arenas_bin_node[] = { @@ -282,76 +314,93 @@ static const ctl_indexed_node_t arenas_bin_node[] = { }; static const ctl_named_node_t arenas_lrun_i_node[] = { - {NAME("size"), CTL(arenas_lrun_i_size)} + {NAME("size"), CTL(arenas_lrun_i_size)} }; static const ctl_named_node_t super_arenas_lrun_i_node[] = { - {NAME(""), CHILD(named, arenas_lrun_i)} + {NAME(""), CHILD(named, arenas_lrun_i)} }; static const ctl_indexed_node_t arenas_lrun_node[] = { {INDEX(arenas_lrun_i)} }; +static const ctl_named_node_t arenas_hchunk_i_node[] = { + {NAME("size"), CTL(arenas_hchunk_i_size)} +}; +static const ctl_named_node_t super_arenas_hchunk_i_node[] = { + {NAME(""), CHILD(named, arenas_hchunk_i)} +}; + +static const ctl_indexed_node_t arenas_hchunk_node[] = { + {INDEX(arenas_hchunk_i)} +}; + static const ctl_named_node_t arenas_node[] = { - {NAME("narenas"), CTL(arenas_narenas)}, - {NAME("initialized"), CTL(arenas_initialized)}, - {NAME("quantum"), CTL(arenas_quantum)}, - {NAME("page"), CTL(arenas_page)}, - {NAME("tcache_max"), CTL(arenas_tcache_max)}, - {NAME("nbins"), CTL(arenas_nbins)}, - {NAME("nhbins"), CTL(arenas_nhbins)}, - {NAME("bin"), CHILD(indexed, arenas_bin)}, - {NAME("nlruns"), CTL(arenas_nlruns)}, - {NAME("lrun"), CHILD(indexed, arenas_lrun)}, - {NAME("purge"), CTL(arenas_purge)}, - {NAME("extend"), CTL(arenas_extend)} + {NAME("narenas"), CTL(arenas_narenas)}, + {NAME("initialized"), CTL(arenas_initialized)}, + {NAME("lg_dirty_mult"), CTL(arenas_lg_dirty_mult)}, + {NAME("quantum"), CTL(arenas_quantum)}, + {NAME("page"), CTL(arenas_page)}, + {NAME("tcache_max"), CTL(arenas_tcache_max)}, + {NAME("nbins"), CTL(arenas_nbins)}, + {NAME("nhbins"), CTL(arenas_nhbins)}, + {NAME("bin"), CHILD(indexed, arenas_bin)}, + {NAME("nlruns"), CTL(arenas_nlruns)}, + {NAME("lrun"), CHILD(indexed, arenas_lrun)}, + {NAME("nhchunks"), CTL(arenas_nhchunks)}, + {NAME("hchunk"), CHILD(indexed, arenas_hchunk)}, + {NAME("extend"), CTL(arenas_extend)} }; static const ctl_named_node_t prof_node[] = { + {NAME("thread_active_init"), CTL(prof_thread_active_init)}, {NAME("active"), CTL(prof_active)}, {NAME("dump"), CTL(prof_dump)}, - {NAME("interval"), CTL(prof_interval)} + {NAME("gdump"), CTL(prof_gdump)}, + {NAME("reset"), CTL(prof_reset)}, + {NAME("interval"), CTL(prof_interval)}, + {NAME("lg_sample"), CTL(lg_prof_sample)} }; -static const ctl_named_node_t stats_chunks_node[] = { - {NAME("current"), CTL(stats_chunks_current)}, - {NAME("total"), CTL(stats_chunks_total)}, - {NAME("high"), CTL(stats_chunks_high)} -}; - -static const ctl_named_node_t stats_huge_node[] = { - {NAME("allocated"), CTL(stats_huge_allocated)}, - {NAME("nmalloc"), CTL(stats_huge_nmalloc)}, - {NAME("ndalloc"), CTL(stats_huge_ndalloc)} +static const ctl_named_node_t stats_arenas_i_metadata_node[] = { + {NAME("mapped"), CTL(stats_arenas_i_metadata_mapped)}, + {NAME("allocated"), CTL(stats_arenas_i_metadata_allocated)} }; static const ctl_named_node_t stats_arenas_i_small_node[] = { - {NAME("allocated"), CTL(stats_arenas_i_small_allocated)}, - {NAME("nmalloc"), CTL(stats_arenas_i_small_nmalloc)}, - {NAME("ndalloc"), CTL(stats_arenas_i_small_ndalloc)}, - {NAME("nrequests"), CTL(stats_arenas_i_small_nrequests)} + {NAME("allocated"), CTL(stats_arenas_i_small_allocated)}, + {NAME("nmalloc"), CTL(stats_arenas_i_small_nmalloc)}, + {NAME("ndalloc"), CTL(stats_arenas_i_small_ndalloc)}, + {NAME("nrequests"), CTL(stats_arenas_i_small_nrequests)} }; static const ctl_named_node_t stats_arenas_i_large_node[] = { - {NAME("allocated"), CTL(stats_arenas_i_large_allocated)}, - {NAME("nmalloc"), CTL(stats_arenas_i_large_nmalloc)}, - {NAME("ndalloc"), CTL(stats_arenas_i_large_ndalloc)}, - {NAME("nrequests"), CTL(stats_arenas_i_large_nrequests)} + {NAME("allocated"), CTL(stats_arenas_i_large_allocated)}, + {NAME("nmalloc"), CTL(stats_arenas_i_large_nmalloc)}, + {NAME("ndalloc"), CTL(stats_arenas_i_large_ndalloc)}, + {NAME("nrequests"), CTL(stats_arenas_i_large_nrequests)} +}; + +static const ctl_named_node_t stats_arenas_i_huge_node[] = { + {NAME("allocated"), CTL(stats_arenas_i_huge_allocated)}, + {NAME("nmalloc"), CTL(stats_arenas_i_huge_nmalloc)}, + {NAME("ndalloc"), CTL(stats_arenas_i_huge_ndalloc)}, + {NAME("nrequests"), CTL(stats_arenas_i_huge_nrequests)} }; static const ctl_named_node_t stats_arenas_i_bins_j_node[] = { - {NAME("allocated"), CTL(stats_arenas_i_bins_j_allocated)}, - {NAME("nmalloc"), CTL(stats_arenas_i_bins_j_nmalloc)}, - {NAME("ndalloc"), CTL(stats_arenas_i_bins_j_ndalloc)}, - {NAME("nrequests"), CTL(stats_arenas_i_bins_j_nrequests)}, - {NAME("nfills"), CTL(stats_arenas_i_bins_j_nfills)}, - {NAME("nflushes"), CTL(stats_arenas_i_bins_j_nflushes)}, - {NAME("nruns"), CTL(stats_arenas_i_bins_j_nruns)}, - {NAME("nreruns"), CTL(stats_arenas_i_bins_j_nreruns)}, - {NAME("curruns"), CTL(stats_arenas_i_bins_j_curruns)} + {NAME("nmalloc"), CTL(stats_arenas_i_bins_j_nmalloc)}, + {NAME("ndalloc"), CTL(stats_arenas_i_bins_j_ndalloc)}, + {NAME("nrequests"), CTL(stats_arenas_i_bins_j_nrequests)}, + {NAME("curregs"), CTL(stats_arenas_i_bins_j_curregs)}, + {NAME("nfills"), CTL(stats_arenas_i_bins_j_nfills)}, + {NAME("nflushes"), CTL(stats_arenas_i_bins_j_nflushes)}, + {NAME("nruns"), CTL(stats_arenas_i_bins_j_nruns)}, + {NAME("nreruns"), CTL(stats_arenas_i_bins_j_nreruns)}, + {NAME("curruns"), CTL(stats_arenas_i_bins_j_curruns)} }; static const ctl_named_node_t super_stats_arenas_i_bins_j_node[] = { - {NAME(""), CHILD(named, stats_arenas_i_bins_j)} + {NAME(""), CHILD(named, stats_arenas_i_bins_j)} }; static const ctl_indexed_node_t stats_arenas_i_bins_node[] = { @@ -359,35 +408,53 @@ static const ctl_indexed_node_t stats_arenas_i_bins_node[] = { }; static const ctl_named_node_t stats_arenas_i_lruns_j_node[] = { - {NAME("nmalloc"), CTL(stats_arenas_i_lruns_j_nmalloc)}, - {NAME("ndalloc"), CTL(stats_arenas_i_lruns_j_ndalloc)}, - {NAME("nrequests"), CTL(stats_arenas_i_lruns_j_nrequests)}, - {NAME("curruns"), CTL(stats_arenas_i_lruns_j_curruns)} + {NAME("nmalloc"), CTL(stats_arenas_i_lruns_j_nmalloc)}, + {NAME("ndalloc"), CTL(stats_arenas_i_lruns_j_ndalloc)}, + {NAME("nrequests"), CTL(stats_arenas_i_lruns_j_nrequests)}, + {NAME("curruns"), CTL(stats_arenas_i_lruns_j_curruns)} }; static const ctl_named_node_t super_stats_arenas_i_lruns_j_node[] = { - {NAME(""), CHILD(named, stats_arenas_i_lruns_j)} + {NAME(""), CHILD(named, stats_arenas_i_lruns_j)} }; static const ctl_indexed_node_t stats_arenas_i_lruns_node[] = { {INDEX(stats_arenas_i_lruns_j)} }; +static const ctl_named_node_t stats_arenas_i_hchunks_j_node[] = { + {NAME("nmalloc"), CTL(stats_arenas_i_hchunks_j_nmalloc)}, + {NAME("ndalloc"), CTL(stats_arenas_i_hchunks_j_ndalloc)}, + {NAME("nrequests"), CTL(stats_arenas_i_hchunks_j_nrequests)}, + {NAME("curhchunks"), CTL(stats_arenas_i_hchunks_j_curhchunks)} +}; +static const ctl_named_node_t super_stats_arenas_i_hchunks_j_node[] = { + {NAME(""), CHILD(named, stats_arenas_i_hchunks_j)} +}; + +static const ctl_indexed_node_t stats_arenas_i_hchunks_node[] = { + {INDEX(stats_arenas_i_hchunks_j)} +}; + 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)}, - {NAME("npurge"), CTL(stats_arenas_i_npurge)}, - {NAME("nmadvise"), CTL(stats_arenas_i_nmadvise)}, - {NAME("purged"), CTL(stats_arenas_i_purged)}, - {NAME("small"), CHILD(named, stats_arenas_i_small)}, - {NAME("large"), CHILD(named, stats_arenas_i_large)}, - {NAME("bins"), CHILD(indexed, stats_arenas_i_bins)}, - {NAME("lruns"), CHILD(indexed, stats_arenas_i_lruns)} + {NAME("nthreads"), CTL(stats_arenas_i_nthreads)}, + {NAME("dss"), CTL(stats_arenas_i_dss)}, + {NAME("lg_dirty_mult"), CTL(stats_arenas_i_lg_dirty_mult)}, + {NAME("pactive"), CTL(stats_arenas_i_pactive)}, + {NAME("pdirty"), CTL(stats_arenas_i_pdirty)}, + {NAME("mapped"), CTL(stats_arenas_i_mapped)}, + {NAME("npurge"), CTL(stats_arenas_i_npurge)}, + {NAME("nmadvise"), CTL(stats_arenas_i_nmadvise)}, + {NAME("purged"), CTL(stats_arenas_i_purged)}, + {NAME("metadata"), CHILD(named, stats_arenas_i_metadata)}, + {NAME("small"), CHILD(named, stats_arenas_i_small)}, + {NAME("large"), CHILD(named, stats_arenas_i_large)}, + {NAME("huge"), CHILD(named, stats_arenas_i_huge)}, + {NAME("bins"), CHILD(indexed, stats_arenas_i_bins)}, + {NAME("lruns"), CHILD(indexed, stats_arenas_i_lruns)}, + {NAME("hchunks"), CHILD(indexed, stats_arenas_i_hchunks)} }; static const ctl_named_node_t super_stats_arenas_i_node[] = { - {NAME(""), CHILD(named, stats_arenas_i)} + {NAME(""), CHILD(named, stats_arenas_i)} }; static const ctl_indexed_node_t stats_arenas_node[] = { @@ -395,13 +462,13 @@ static const ctl_indexed_node_t stats_arenas_node[] = { }; static const ctl_named_node_t stats_node[] = { - {NAME("cactive"), CTL(stats_cactive)}, - {NAME("allocated"), CTL(stats_allocated)}, - {NAME("active"), CTL(stats_active)}, - {NAME("mapped"), CTL(stats_mapped)}, - {NAME("chunks"), CHILD(named, stats_chunks)}, - {NAME("huge"), CHILD(named, stats_huge)}, - {NAME("arenas"), CHILD(indexed, stats_arenas)} + {NAME("cactive"), CTL(stats_cactive)}, + {NAME("allocated"), CTL(stats_allocated)}, + {NAME("active"), CTL(stats_active)}, + {NAME("metadata"), CTL(stats_metadata)}, + {NAME("resident"), CTL(stats_resident)}, + {NAME("mapped"), CTL(stats_mapped)}, + {NAME("arenas"), CHILD(indexed, stats_arenas)} }; static const ctl_named_node_t root_node[] = { @@ -410,6 +477,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("tcache"), CHILD(named, tcache)}, {NAME("arena"), CHILD(indexed, arena)}, {NAME("arenas"), CHILD(named, arenas)}, {NAME("prof"), CHILD(named, prof)}, @@ -431,12 +499,19 @@ ctl_arena_init(ctl_arena_stats_t *astats) { if (astats->lstats == NULL) { - astats->lstats = (malloc_large_stats_t *)base_alloc(nlclasses * + astats->lstats = (malloc_large_stats_t *)a0malloc(nlclasses * sizeof(malloc_large_stats_t)); if (astats->lstats == NULL) return (true); } + if (astats->hstats == NULL) { + astats->hstats = (malloc_huge_stats_t *)a0malloc(nhclasses * + sizeof(malloc_huge_stats_t)); + if (astats->hstats == NULL) + return (true); + } + return (false); } @@ -445,6 +520,7 @@ ctl_arena_clear(ctl_arena_stats_t *astats) { astats->dss = dss_prec_names[dss_prec_limit]; + astats->lg_dirty_mult = -1; astats->pactive = 0; astats->pdirty = 0; if (config_stats) { @@ -456,6 +532,8 @@ ctl_arena_clear(ctl_arena_stats_t *astats) memset(astats->bstats, 0, NBINS * sizeof(malloc_bin_stats_t)); memset(astats->lstats, 0, nlclasses * sizeof(malloc_large_stats_t)); + memset(astats->hstats, 0, nhclasses * + sizeof(malloc_huge_stats_t)); } } @@ -464,11 +542,13 @@ ctl_arena_stats_amerge(ctl_arena_stats_t *cstats, arena_t *arena) { unsigned i; - arena_stats_merge(arena, &cstats->dss, &cstats->pactive, - &cstats->pdirty, &cstats->astats, cstats->bstats, cstats->lstats); + arena_stats_merge(arena, &cstats->dss, &cstats->lg_dirty_mult, + &cstats->pactive, &cstats->pdirty, &cstats->astats, cstats->bstats, + cstats->lstats, cstats->hstats); for (i = 0; i < NBINS; i++) { - cstats->allocated_small += cstats->bstats[i].allocated; + cstats->allocated_small += cstats->bstats[i].curregs * + index2size(i); cstats->nmalloc_small += cstats->bstats[i].nmalloc; cstats->ndalloc_small += cstats->bstats[i].ndalloc; cstats->nrequests_small += cstats->bstats[i].nrequests; @@ -488,6 +568,9 @@ ctl_arena_stats_smerge(ctl_arena_stats_t *sstats, ctl_arena_stats_t *astats) sstats->astats.nmadvise += astats->astats.nmadvise; sstats->astats.purged += astats->astats.purged; + sstats->astats.metadata_mapped += astats->astats.metadata_mapped; + sstats->astats.metadata_allocated += astats->astats.metadata_allocated; + sstats->allocated_small += astats->allocated_small; sstats->nmalloc_small += astats->nmalloc_small; sstats->ndalloc_small += astats->ndalloc_small; @@ -498,18 +581,15 @@ ctl_arena_stats_smerge(ctl_arena_stats_t *sstats, ctl_arena_stats_t *astats) sstats->astats.ndalloc_large += astats->astats.ndalloc_large; sstats->astats.nrequests_large += astats->astats.nrequests_large; - for (i = 0; i < nlclasses; i++) { - sstats->lstats[i].nmalloc += astats->lstats[i].nmalloc; - sstats->lstats[i].ndalloc += astats->lstats[i].ndalloc; - sstats->lstats[i].nrequests += astats->lstats[i].nrequests; - sstats->lstats[i].curruns += astats->lstats[i].curruns; - } + sstats->astats.allocated_huge += astats->astats.allocated_huge; + sstats->astats.nmalloc_huge += astats->astats.nmalloc_huge; + sstats->astats.ndalloc_huge += astats->astats.ndalloc_huge; for (i = 0; i < NBINS; i++) { - sstats->bstats[i].allocated += astats->bstats[i].allocated; sstats->bstats[i].nmalloc += astats->bstats[i].nmalloc; sstats->bstats[i].ndalloc += astats->bstats[i].ndalloc; sstats->bstats[i].nrequests += astats->bstats[i].nrequests; + sstats->bstats[i].curregs += astats->bstats[i].curregs; if (config_tcache) { sstats->bstats[i].nfills += astats->bstats[i].nfills; sstats->bstats[i].nflushes += @@ -519,6 +599,19 @@ ctl_arena_stats_smerge(ctl_arena_stats_t *sstats, ctl_arena_stats_t *astats) sstats->bstats[i].reruns += astats->bstats[i].reruns; sstats->bstats[i].curruns += astats->bstats[i].curruns; } + + for (i = 0; i < nlclasses; i++) { + sstats->lstats[i].nmalloc += astats->lstats[i].nmalloc; + sstats->lstats[i].ndalloc += astats->lstats[i].ndalloc; + sstats->lstats[i].nrequests += astats->lstats[i].nrequests; + sstats->lstats[i].curruns += astats->lstats[i].curruns; + } + + for (i = 0; i < nhclasses; i++) { + sstats->hstats[i].nmalloc += astats->hstats[i].nmalloc; + sstats->hstats[i].ndalloc += astats->hstats[i].ndalloc; + sstats->hstats[i].curhchunks += astats->hstats[i].curhchunks; + } } static void @@ -547,27 +640,23 @@ static bool ctl_grow(void) { ctl_arena_stats_t *astats; - arena_t **tarenas; - /* Allocate extended arena stats and arenas arrays. */ - astats = (ctl_arena_stats_t *)imalloc((ctl_stats.narenas + 2) * + /* Initialize new arena. */ + if (arena_init(ctl_stats.narenas) == NULL) + return (true); + + /* Allocate extended arena stats. */ + astats = (ctl_arena_stats_t *)a0malloc((ctl_stats.narenas + 2) * sizeof(ctl_arena_stats_t)); if (astats == NULL) return (true); - tarenas = (arena_t **)imalloc((ctl_stats.narenas + 1) * - sizeof(arena_t *)); - if (tarenas == NULL) { - idalloc(astats); - return (true); - } /* Initialize the new astats element. */ memcpy(astats, ctl_stats.arenas, (ctl_stats.narenas + 1) * sizeof(ctl_arena_stats_t)); memset(&astats[ctl_stats.narenas + 1], 0, sizeof(ctl_arena_stats_t)); if (ctl_arena_init(&astats[ctl_stats.narenas + 1])) { - idalloc(tarenas); - idalloc(astats); + a0dalloc(astats); return (true); } /* Swap merged stats to their new location. */ @@ -580,32 +669,7 @@ ctl_grow(void) memcpy(&astats[ctl_stats.narenas + 1], &tstats, sizeof(ctl_arena_stats_t)); } - /* Initialize the new arenas element. */ - tarenas[ctl_stats.narenas] = NULL; - { - arena_t **arenas_old = arenas; - /* - * Swap extended arenas array into place. Although ctl_mtx - * protects this function from other threads extending the - * array, it does not protect from other threads mutating it - * (i.e. initializing arenas and setting array elements to - * point to them). Therefore, array copying must happen under - * the protection of arenas_lock. - */ - malloc_mutex_lock(&arenas_lock); - arenas = tarenas; - memcpy(arenas, arenas_old, ctl_stats.narenas * - sizeof(arena_t *)); - narenas_total++; - arenas_extend(narenas_total - 1); - malloc_mutex_unlock(&arenas_lock); - /* - * Deallocate arenas_old only if it came from imalloc() (not - * base_alloc()). - */ - if (ctl_stats.narenas != narenas_auto) - idalloc(arenas_old); - } + a0dalloc(ctl_stats.arenas); ctl_stats.arenas = astats; ctl_stats.narenas++; @@ -615,23 +679,11 @@ ctl_grow(void) static void ctl_refresh(void) { + tsd_t *tsd; unsigned i; + bool refreshed; VARIABLE_ARRAY(arena_t *, tarenas, ctl_stats.narenas); - if (config_stats) { - malloc_mutex_lock(&chunks_mtx); - ctl_stats.chunks.current = stats_chunks.curchunks; - ctl_stats.chunks.total = stats_chunks.nchunks; - ctl_stats.chunks.high = stats_chunks.highchunks; - malloc_mutex_unlock(&chunks_mtx); - - malloc_mutex_lock(&huge_mtx); - ctl_stats.huge.allocated = huge_allocated; - ctl_stats.huge.nmalloc = huge_nmalloc; - ctl_stats.huge.ndalloc = huge_ndalloc; - malloc_mutex_unlock(&huge_mtx); - } - /* * Clear sum stats, since they will be merged into by * ctl_arena_refresh(). @@ -639,15 +691,22 @@ ctl_refresh(void) 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 *) * ctl_stats.narenas); + tsd = tsd_fetch(); + for (i = 0, refreshed = false; i < ctl_stats.narenas; i++) { + tarenas[i] = arena_get(tsd, i, false, false); + if (tarenas[i] == NULL && !refreshed) { + tarenas[i] = arena_get(tsd, i, false, true); + refreshed = true; + } + } + for (i = 0; i < ctl_stats.narenas; i++) { - if (arenas[i] != NULL) - ctl_stats.arenas[i].nthreads = arenas[i]->nthreads; + if (tarenas[i] != NULL) + ctl_stats.arenas[i].nthreads = arena_nbound(i); else ctl_stats.arenas[i].nthreads = 0; } - malloc_mutex_unlock(&arenas_lock); + for (i = 0; i < ctl_stats.narenas; i++) { bool initialized = (tarenas[i] != NULL); @@ -657,14 +716,24 @@ ctl_refresh(void) } if (config_stats) { + size_t base_allocated, base_resident, base_mapped; + base_stats_get(&base_allocated, &base_resident, &base_mapped); 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.arenas[ctl_stats.narenas].allocated_small + + ctl_stats.arenas[ctl_stats.narenas].astats.allocated_large + + ctl_stats.arenas[ctl_stats.narenas].astats.allocated_huge; ctl_stats.active = - (ctl_stats.arenas[ctl_stats.narenas].pactive << LG_PAGE) - + ctl_stats.huge.allocated; - ctl_stats.mapped = (ctl_stats.chunks.current << opt_lg_chunk); + (ctl_stats.arenas[ctl_stats.narenas].pactive << LG_PAGE); + ctl_stats.metadata = base_allocated + + ctl_stats.arenas[ctl_stats.narenas].astats.metadata_mapped + + ctl_stats.arenas[ctl_stats.narenas].astats + .metadata_allocated; + ctl_stats.resident = base_resident + + ctl_stats.arenas[ctl_stats.narenas].astats.metadata_mapped + + ((ctl_stats.arenas[ctl_stats.narenas].pactive + + ctl_stats.arenas[ctl_stats.narenas].pdirty) << LG_PAGE); + ctl_stats.mapped = base_mapped + + ctl_stats.arenas[ctl_stats.narenas].astats.mapped; } ctl_epoch++; @@ -676,14 +745,13 @@ ctl_init(void) bool ret; malloc_mutex_lock(&ctl_mtx); - if (ctl_initialized == false) { + if (!ctl_initialized) { /* * 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( + ctl_stats.narenas = narenas_total_get(); + ctl_stats.arenas = (ctl_arena_stats_t *)a0malloc( (ctl_stats.narenas + 1) * sizeof(ctl_arena_stats_t)); if (ctl_stats.arenas == NULL) { ret = true; @@ -701,6 +769,15 @@ ctl_init(void) unsigned i; for (i = 0; i <= ctl_stats.narenas; i++) { if (ctl_arena_init(&ctl_stats.arenas[i])) { + unsigned j; + for (j = 0; j < i; j++) { + a0dalloc( + ctl_stats.arenas[j].lstats); + a0dalloc( + ctl_stats.arenas[j].hstats); + } + a0dalloc(ctl_stats.arenas); + ctl_stats.arenas = NULL; ret = true; goto label_return; } @@ -826,7 +903,7 @@ ctl_byname(const char *name, void *oldp, size_t *oldlenp, void *newp, size_t mib[CTL_MAX_DEPTH]; const ctl_named_node_t *node; - if (ctl_initialized == false && ctl_init()) { + if (!ctl_initialized && ctl_init()) { ret = EAGAIN; goto label_return; } @@ -853,7 +930,7 @@ ctl_nametomib(const char *name, size_t *mibp, size_t *miblenp) { int ret; - if (ctl_initialized == false && ctl_init()) { + if (!ctl_initialized && ctl_init()) { ret = EAGAIN; goto label_return; } @@ -871,7 +948,7 @@ ctl_bymib(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp, const ctl_named_node_t *node; size_t i; - if (ctl_initialized == false && ctl_init()) { + if (!ctl_initialized && ctl_init()) { ret = EAGAIN; goto label_return; } @@ -963,6 +1040,14 @@ ctl_postfork_child(void) } \ } while (0) +#define READ_XOR_WRITE() do { \ + if ((oldp != NULL && oldlenp != NULL) && (newp != NULL || \ + newlen != 0)) { \ + ret = EPERM; \ + goto label_return; \ + } \ +} while (0) + #define READ(v, t) do { \ if (oldp != NULL && oldlenp != NULL) { \ if (*oldlenp != sizeof(t)) { \ @@ -971,8 +1056,8 @@ ctl_postfork_child(void) memcpy(oldp, (void *)&(v), copylen); \ ret = EINVAL; \ goto label_return; \ - } else \ - *(t *)oldp = (v); \ + } \ + *(t *)oldp = (v); \ } \ } while (0) @@ -998,7 +1083,7 @@ n##_ctl(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp, \ int ret; \ t oldval; \ \ - if ((c) == false) \ + if (!(c)) \ return (ENOENT); \ if (l) \ malloc_mutex_lock(&ctl_mtx); \ @@ -1021,7 +1106,7 @@ n##_ctl(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp, \ int ret; \ t oldval; \ \ - if ((c) == false) \ + if (!(c)) \ return (ENOENT); \ malloc_mutex_lock(&ctl_mtx); \ READONLY(); \ @@ -1065,7 +1150,7 @@ n##_ctl(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp, \ int ret; \ t oldval; \ \ - if ((c) == false) \ + if (!(c)) \ return (ENOENT); \ READONLY(); \ oldval = (v); \ @@ -1093,6 +1178,27 @@ label_return: \ return (ret); \ } +#define CTL_TSD_RO_NL_CGEN(c, n, m, t) \ +static int \ +n##_ctl(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp, \ + void *newp, size_t newlen) \ +{ \ + int ret; \ + t oldval; \ + tsd_t *tsd; \ + \ + if (!(c)) \ + return (ENOENT); \ + READONLY(); \ + tsd = tsd_fetch(); \ + oldval = (m(tsd)); \ + READ(oldval, t); \ + \ + ret = 0; \ +label_return: \ + return (ret); \ +} + #define CTL_RO_BOOL_CONFIG_GEN(n) \ static int \ n##_ctl(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp, \ @@ -1135,11 +1241,10 @@ epoch_ctl(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp, /******************************************************************************/ +CTL_RO_BOOL_CONFIG_GEN(config_cache_oblivious) CTL_RO_BOOL_CONFIG_GEN(config_debug) -CTL_RO_BOOL_CONFIG_GEN(config_dss) CTL_RO_BOOL_CONFIG_GEN(config_fill) CTL_RO_BOOL_CONFIG_GEN(config_lazy_lock) -CTL_RO_BOOL_CONFIG_GEN(config_mremap) CTL_RO_BOOL_CONFIG_GEN(config_munmap) CTL_RO_BOOL_CONFIG_GEN(config_prof) CTL_RO_BOOL_CONFIG_GEN(config_prof_libgcc) @@ -1159,18 +1264,19 @@ 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) CTL_RO_NL_GEN(opt_stats_print, opt_stats_print, bool) -CTL_RO_NL_CGEN(config_fill, opt_junk, opt_junk, bool) +CTL_RO_NL_CGEN(config_fill, opt_junk, opt_junk, const char *) CTL_RO_NL_CGEN(config_fill, opt_quarantine, opt_quarantine, size_t) CTL_RO_NL_CGEN(config_fill, opt_redzone, opt_redzone, bool) CTL_RO_NL_CGEN(config_fill, opt_zero, opt_zero, bool) CTL_RO_NL_CGEN(config_utrace, opt_utrace, opt_utrace, bool) -CTL_RO_NL_CGEN(config_valgrind, opt_valgrind, opt_valgrind, bool) CTL_RO_NL_CGEN(config_xmalloc, opt_xmalloc, opt_xmalloc, bool) CTL_RO_NL_CGEN(config_tcache, opt_tcache, opt_tcache, bool) CTL_RO_NL_CGEN(config_tcache, opt_lg_tcache_max, opt_lg_tcache_max, ssize_t) CTL_RO_NL_CGEN(config_prof, opt_prof, opt_prof, bool) CTL_RO_NL_CGEN(config_prof, opt_prof_prefix, opt_prof_prefix, const char *) -CTL_RO_CGEN(config_prof, opt_prof_active, opt_prof_active, bool) /* Mutable. */ +CTL_RO_NL_CGEN(config_prof, opt_prof_active, opt_prof_active, bool) +CTL_RO_NL_CGEN(config_prof, opt_prof_thread_active_init, + opt_prof_thread_active_init, bool) CTL_RO_NL_CGEN(config_prof, opt_lg_prof_sample, opt_lg_prof_sample, size_t) CTL_RO_NL_CGEN(config_prof, opt_prof_accum, opt_prof_accum, bool) CTL_RO_NL_CGEN(config_prof, opt_lg_prof_interval, opt_lg_prof_interval, ssize_t) @@ -1185,14 +1291,21 @@ thread_arena_ctl(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp, void *newp, size_t newlen) { int ret; + tsd_t *tsd; + arena_t *oldarena; unsigned newind, oldind; + tsd = tsd_fetch(); + oldarena = arena_choose(tsd, NULL); + if (oldarena == NULL) + return (EAGAIN); + malloc_mutex_lock(&ctl_mtx); - newind = oldind = choose_arena(NULL)->ind; + newind = oldind = oldarena->ind; WRITE(newind, unsigned); READ(oldind, unsigned); if (newind != oldind) { - arena_t *arena; + arena_t *newarena; if (newind >= ctl_stats.narenas) { /* New arena index is out of range. */ @@ -1201,28 +1314,20 @@ thread_arena_ctl(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp, } /* Initialize arena if necessary. */ - malloc_mutex_lock(&arenas_lock); - if ((arena = arenas[newind]) == NULL && (arena = - arenas_extend(newind)) == NULL) { - malloc_mutex_unlock(&arenas_lock); + newarena = arena_get(tsd, newind, true, true); + if (newarena == NULL) { ret = EAGAIN; goto label_return; } - assert(arena == arenas[newind]); - arenas[oldind]->nthreads--; - arenas[newind]->nthreads++; - malloc_mutex_unlock(&arenas_lock); - - /* Set new arena association. */ + /* Set new arena/tcache associations. */ + arena_migrate(tsd, oldind, newind); if (config_tcache) { - tcache_t *tcache; - if ((uintptr_t)(tcache = *tcache_tsd_get()) > - (uintptr_t)TCACHE_STATE_MAX) { - tcache_arena_dissociate(tcache); - tcache_arena_associate(tcache, arena); + tcache_t *tcache = tsd_tcache_get(tsd); + if (tcache != NULL) { + tcache_arena_reassociate(tcache, oldarena, + newarena); } } - arenas_tsd_set(&arena); } ret = 0; @@ -1231,14 +1336,14 @@ thread_arena_ctl(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp, return (ret); } -CTL_RO_NL_CGEN(config_stats, thread_allocated, - thread_allocated_tsd_get()->allocated, uint64_t) -CTL_RO_NL_CGEN(config_stats, thread_allocatedp, - &thread_allocated_tsd_get()->allocated, uint64_t *) -CTL_RO_NL_CGEN(config_stats, thread_deallocated, - thread_allocated_tsd_get()->deallocated, uint64_t) -CTL_RO_NL_CGEN(config_stats, thread_deallocatedp, - &thread_allocated_tsd_get()->deallocated, uint64_t *) +CTL_TSD_RO_NL_CGEN(config_stats, thread_allocated, tsd_thread_allocated_get, + uint64_t) +CTL_TSD_RO_NL_CGEN(config_stats, thread_allocatedp, tsd_thread_allocatedp_get, + uint64_t *) +CTL_TSD_RO_NL_CGEN(config_stats, thread_deallocated, tsd_thread_deallocated_get, + uint64_t) +CTL_TSD_RO_NL_CGEN(config_stats, thread_deallocatedp, + tsd_thread_deallocatedp_get, uint64_t *) static int thread_tcache_enabled_ctl(const size_t *mib, size_t miblen, void *oldp, @@ -1247,7 +1352,7 @@ thread_tcache_enabled_ctl(const size_t *mib, size_t miblen, void *oldp, int ret; bool oldval; - if (config_tcache == false) + if (!config_tcache) return (ENOENT); oldval = tcache_enabled_get(); @@ -1271,7 +1376,7 @@ thread_tcache_flush_ctl(const size_t *mib, size_t miblen, void *oldp, { int ret; - if (config_tcache == false) + if (!config_tcache) return (ENOENT); READONLY(); @@ -1284,17 +1389,170 @@ thread_tcache_flush_ctl(const size_t *mib, size_t miblen, void *oldp, return (ret); } +static int +thread_prof_name_ctl(const size_t *mib, size_t miblen, void *oldp, + size_t *oldlenp, void *newp, size_t newlen) +{ + int ret; + + if (!config_prof) + return (ENOENT); + + READ_XOR_WRITE(); + + if (newp != NULL) { + tsd_t *tsd; + + if (newlen != sizeof(const char *)) { + ret = EINVAL; + goto label_return; + } + + tsd = tsd_fetch(); + + if ((ret = prof_thread_name_set(tsd, *(const char **)newp)) != + 0) + goto label_return; + } else { + const char *oldname = prof_thread_name_get(); + READ(oldname, const char *); + } + + ret = 0; +label_return: + return (ret); +} + +static int +thread_prof_active_ctl(const size_t *mib, size_t miblen, void *oldp, + size_t *oldlenp, void *newp, size_t newlen) +{ + int ret; + bool oldval; + + if (!config_prof) + return (ENOENT); + + oldval = prof_thread_active_get(); + if (newp != NULL) { + if (newlen != sizeof(bool)) { + ret = EINVAL; + goto label_return; + } + if (prof_thread_active_set(*(bool *)newp)) { + ret = EAGAIN; + goto label_return; + } + } + READ(oldval, bool); + + ret = 0; +label_return: + return (ret); +} + +/******************************************************************************/ + +static int +tcache_create_ctl(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp, + void *newp, size_t newlen) +{ + int ret; + tsd_t *tsd; + unsigned tcache_ind; + + if (!config_tcache) + return (ENOENT); + + tsd = tsd_fetch(); + + malloc_mutex_lock(&ctl_mtx); + READONLY(); + if (tcaches_create(tsd, &tcache_ind)) { + ret = EFAULT; + goto label_return; + } + READ(tcache_ind, unsigned); + + ret = 0; +label_return: + malloc_mutex_unlock(&ctl_mtx); + return (ret); +} + +static int +tcache_flush_ctl(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp, + void *newp, size_t newlen) +{ + int ret; + tsd_t *tsd; + unsigned tcache_ind; + + if (!config_tcache) + return (ENOENT); + + tsd = tsd_fetch(); + + WRITEONLY(); + tcache_ind = UINT_MAX; + WRITE(tcache_ind, unsigned); + if (tcache_ind == UINT_MAX) { + ret = EFAULT; + goto label_return; + } + tcaches_flush(tsd, tcache_ind); + + ret = 0; +label_return: + return (ret); +} + +static int +tcache_destroy_ctl(const size_t *mib, size_t miblen, void *oldp, + size_t *oldlenp, void *newp, size_t newlen) +{ + int ret; + tsd_t *tsd; + unsigned tcache_ind; + + if (!config_tcache) + return (ENOENT); + + tsd = tsd_fetch(); + + WRITEONLY(); + tcache_ind = UINT_MAX; + WRITE(tcache_ind, unsigned); + if (tcache_ind == UINT_MAX) { + ret = EFAULT; + goto label_return; + } + tcaches_destroy(tsd, tcache_ind); + + ret = 0; +label_return: + return (ret); +} + /******************************************************************************/ /* ctl_mutex must be held during execution of this function. */ static void arena_purge(unsigned arena_ind) { + tsd_t *tsd; + unsigned i; + bool refreshed; 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); + tsd = tsd_fetch(); + for (i = 0, refreshed = false; i < ctl_stats.narenas; i++) { + tarenas[i] = arena_get(tsd, i, false, false); + if (tarenas[i] == NULL && !refreshed) { + tarenas[i] = arena_get(tsd, i, false, true); + refreshed = true; + } + } if (arena_ind == ctl_stats.narenas) { unsigned i; @@ -1330,47 +1588,117 @@ 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; + int ret; + const char *dss = NULL; 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 (dss != NULL) { + int i; + bool 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) { + ret = EINVAL; + goto label_return; } - } - 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; + arena_t *arena = arena_get(tsd_fetch(), arena_ind, false, true); + if (arena == NULL || (dss_prec != dss_prec_limit && + arena_dss_prec_set(arena, dss_prec))) { + ret = EFAULT; + goto label_return; + } + dss_prec_old = arena_dss_prec_get(arena); } else { + if (dss_prec != dss_prec_limit && + chunk_dss_prec_set(dss_prec)) { + ret = EFAULT; + goto label_return; + } 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 = 0; +label_return: + malloc_mutex_unlock(&ctl_mtx); + return (ret); +} + +static int +arena_i_lg_dirty_mult_ctl(const size_t *mib, size_t miblen, void *oldp, + size_t *oldlenp, void *newp, size_t newlen) +{ + int ret; + unsigned arena_ind = mib[1]; + arena_t *arena; + + arena = arena_get(tsd_fetch(), arena_ind, false, true); + if (arena == NULL) { ret = EFAULT; goto label_return; } + if (oldp != NULL && oldlenp != NULL) { + size_t oldval = arena_lg_dirty_mult_get(arena); + READ(oldval, ssize_t); + } + if (newp != NULL) { + if (newlen != sizeof(ssize_t)) { + ret = EINVAL; + goto label_return; + } + if (arena_lg_dirty_mult_set(arena, *(ssize_t *)newp)) { + ret = EFAULT; + goto label_return; + } + } + + ret = 0; +label_return: + return (ret); +} + +static int +arena_i_chunk_hooks_ctl(const size_t *mib, size_t miblen, void *oldp, + size_t *oldlenp, void *newp, size_t newlen) +{ + int ret; + unsigned arena_ind = mib[1]; + arena_t *arena; + + malloc_mutex_lock(&ctl_mtx); + if (arena_ind < narenas_total_get() && (arena = + arena_get(tsd_fetch(), arena_ind, false, true)) != NULL) { + if (newp != NULL) { + chunk_hooks_t old_chunk_hooks, new_chunk_hooks; + WRITE(new_chunk_hooks, chunk_hooks_t); + old_chunk_hooks = chunk_hooks_set(arena, + &new_chunk_hooks); + READ(old_chunk_hooks, chunk_hooks_t); + } else { + chunk_hooks_t old_chunk_hooks = chunk_hooks_get(arena); + READ(old_chunk_hooks, chunk_hooks_t); + } + } else { + ret = EFAULT; + goto label_return; + } ret = 0; label_return: malloc_mutex_unlock(&ctl_mtx); @@ -1444,6 +1772,32 @@ arenas_initialized_ctl(const size_t *mib, size_t miblen, void *oldp, return (ret); } +static int +arenas_lg_dirty_mult_ctl(const size_t *mib, size_t miblen, void *oldp, + size_t *oldlenp, void *newp, size_t newlen) +{ + int ret; + + if (oldp != NULL && oldlenp != NULL) { + size_t oldval = arena_lg_dirty_mult_default_get(); + READ(oldval, ssize_t); + } + if (newp != NULL) { + if (newlen != sizeof(ssize_t)) { + ret = EINVAL; + goto label_return; + } + if (arena_lg_dirty_mult_default_set(*(ssize_t *)newp)) { + ret = EFAULT; + goto label_return; + } + } + + ret = 0; +label_return: + return (ret); +} + CTL_RO_NL_GEN(arenas_quantum, QUANTUM, size_t) CTL_RO_NL_GEN(arenas_page, PAGE, size_t) CTL_RO_NL_CGEN(config_tcache, arenas_tcache_max, tcache_maxclass, size_t) @@ -1461,8 +1815,8 @@ arenas_bin_i_index(const size_t *mib, size_t miblen, size_t i) return (super_arenas_bin_i_node); } -CTL_RO_NL_GEN(arenas_nlruns, nlclasses, size_t) -CTL_RO_NL_GEN(arenas_lrun_i_size, ((mib[2]+1) << LG_PAGE), size_t) +CTL_RO_NL_GEN(arenas_nlruns, nlclasses, unsigned) +CTL_RO_NL_GEN(arenas_lrun_i_size, index2size(NBINS+mib[2]), size_t) static const ctl_named_node_t * arenas_lrun_i_index(const size_t *mib, size_t miblen, size_t i) { @@ -1472,29 +1826,15 @@ arenas_lrun_i_index(const size_t *mib, size_t miblen, size_t i) return (super_arenas_lrun_i_node); } -static int -arenas_purge_ctl(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp, - void *newp, size_t newlen) +CTL_RO_NL_GEN(arenas_nhchunks, nhclasses, unsigned) +CTL_RO_NL_GEN(arenas_hchunk_i_size, index2size(NBINS+nlclasses+mib[2]), size_t) +static const ctl_named_node_t * +arenas_hchunk_i_index(const size_t *mib, size_t miblen, size_t i) { - int ret; - unsigned arena_ind; - malloc_mutex_lock(&ctl_mtx); - WRITEONLY(); - arena_ind = UINT_MAX; - WRITE(arena_ind, unsigned); - if (newp != NULL && arena_ind >= ctl_stats.narenas) - ret = EFAULT; - 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); + if (i > nhclasses) + return (NULL); + return (super_arenas_hchunk_i_node); } static int @@ -1521,6 +1861,31 @@ arenas_extend_ctl(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp, /******************************************************************************/ +static int +prof_thread_active_init_ctl(const size_t *mib, size_t miblen, void *oldp, + size_t *oldlenp, void *newp, size_t newlen) +{ + int ret; + bool oldval; + + if (!config_prof) + return (ENOENT); + + if (newp != NULL) { + if (newlen != sizeof(bool)) { + ret = EINVAL; + goto label_return; + } + oldval = prof_thread_active_init_set(*(bool *)newp); + } else + oldval = prof_thread_active_init_get(); + READ(oldval, bool); + + ret = 0; +label_return: + return (ret); +} + static int prof_active_ctl(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp, void *newp, size_t newlen) @@ -1528,25 +1893,21 @@ prof_active_ctl(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp, int ret; bool oldval; - if (config_prof == false) + if (!config_prof) return (ENOENT); - malloc_mutex_lock(&ctl_mtx); /* Protect opt_prof_active. */ - oldval = opt_prof_active; if (newp != NULL) { - /* - * The memory barriers will tend to make opt_prof_active - * propagate faster on systems with weak memory ordering. - */ - mb_write(); - WRITE(opt_prof_active, bool); - mb_write(); - } + if (newlen != sizeof(bool)) { + ret = EINVAL; + goto label_return; + } + oldval = prof_active_set(*(bool *)newp); + } else + oldval = prof_active_get(); READ(oldval, bool); ret = 0; label_return: - malloc_mutex_unlock(&ctl_mtx); return (ret); } @@ -1557,7 +1918,7 @@ prof_dump_ctl(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp, int ret; const char *filename = NULL; - if (config_prof == false) + if (!config_prof) return (ENOENT); WRITEONLY(); @@ -1573,24 +1934,71 @@ prof_dump_ctl(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp, return (ret); } +static int +prof_gdump_ctl(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp, + void *newp, size_t newlen) +{ + int ret; + bool oldval; + + if (!config_prof) + return (ENOENT); + + if (newp != NULL) { + if (newlen != sizeof(bool)) { + ret = EINVAL; + goto label_return; + } + oldval = prof_gdump_set(*(bool *)newp); + } else + oldval = prof_gdump_get(); + READ(oldval, bool); + + ret = 0; +label_return: + return (ret); +} + +static int +prof_reset_ctl(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp, + void *newp, size_t newlen) +{ + int ret; + size_t lg_sample = lg_prof_sample; + tsd_t *tsd; + + if (!config_prof) + return (ENOENT); + + WRITEONLY(); + WRITE(lg_sample, size_t); + if (lg_sample >= (sizeof(uint64_t) << 3)) + lg_sample = (sizeof(uint64_t) << 3) - 1; + + tsd = tsd_fetch(); + + prof_reset(tsd, lg_sample); + + ret = 0; +label_return: + return (ret); +} + CTL_RO_NL_CGEN(config_prof, prof_interval, prof_interval, uint64_t) +CTL_RO_NL_CGEN(config_prof, lg_prof_sample, lg_prof_sample, size_t) /******************************************************************************/ CTL_RO_CGEN(config_stats, stats_cactive, &stats_cactive, size_t *) CTL_RO_CGEN(config_stats, stats_allocated, ctl_stats.allocated, size_t) CTL_RO_CGEN(config_stats, stats_active, ctl_stats.active, size_t) +CTL_RO_CGEN(config_stats, stats_metadata, ctl_stats.metadata, size_t) +CTL_RO_CGEN(config_stats, stats_resident, ctl_stats.resident, size_t) CTL_RO_CGEN(config_stats, stats_mapped, ctl_stats.mapped, size_t) -CTL_RO_CGEN(config_stats, stats_chunks_current, ctl_stats.chunks.current, - size_t) -CTL_RO_CGEN(config_stats, stats_chunks_total, ctl_stats.chunks.total, uint64_t) -CTL_RO_CGEN(config_stats, stats_chunks_high, ctl_stats.chunks.high, size_t) -CTL_RO_CGEN(config_stats, stats_huge_allocated, huge_allocated, size_t) -CTL_RO_CGEN(config_stats, stats_huge_nmalloc, huge_nmalloc, uint64_t) -CTL_RO_CGEN(config_stats, stats_huge_ndalloc, huge_ndalloc, uint64_t) - CTL_RO_GEN(stats_arenas_i_dss, ctl_stats.arenas[mib[2]].dss, const char *) +CTL_RO_GEN(stats_arenas_i_lg_dirty_mult, ctl_stats.arenas[mib[2]].lg_dirty_mult, + ssize_t) CTL_RO_GEN(stats_arenas_i_nthreads, ctl_stats.arenas[mib[2]].nthreads, unsigned) 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) @@ -1602,6 +2010,10 @@ CTL_RO_CGEN(config_stats, stats_arenas_i_nmadvise, ctl_stats.arenas[mib[2]].astats.nmadvise, uint64_t) CTL_RO_CGEN(config_stats, stats_arenas_i_purged, ctl_stats.arenas[mib[2]].astats.purged, uint64_t) +CTL_RO_CGEN(config_stats, stats_arenas_i_metadata_mapped, + ctl_stats.arenas[mib[2]].astats.metadata_mapped, size_t) +CTL_RO_CGEN(config_stats, stats_arenas_i_metadata_allocated, + ctl_stats.arenas[mib[2]].astats.metadata_allocated, size_t) CTL_RO_CGEN(config_stats, stats_arenas_i_small_allocated, ctl_stats.arenas[mib[2]].allocated_small, size_t) @@ -1619,15 +2031,23 @@ CTL_RO_CGEN(config_stats, stats_arenas_i_large_ndalloc, ctl_stats.arenas[mib[2]].astats.ndalloc_large, uint64_t) CTL_RO_CGEN(config_stats, stats_arenas_i_large_nrequests, ctl_stats.arenas[mib[2]].astats.nrequests_large, uint64_t) +CTL_RO_CGEN(config_stats, stats_arenas_i_huge_allocated, + ctl_stats.arenas[mib[2]].astats.allocated_huge, size_t) +CTL_RO_CGEN(config_stats, stats_arenas_i_huge_nmalloc, + ctl_stats.arenas[mib[2]].astats.nmalloc_huge, uint64_t) +CTL_RO_CGEN(config_stats, stats_arenas_i_huge_ndalloc, + ctl_stats.arenas[mib[2]].astats.ndalloc_huge, uint64_t) +CTL_RO_CGEN(config_stats, stats_arenas_i_huge_nrequests, + ctl_stats.arenas[mib[2]].astats.nmalloc_huge, uint64_t) /* Intentional. */ -CTL_RO_CGEN(config_stats, stats_arenas_i_bins_j_allocated, - ctl_stats.arenas[mib[2]].bstats[mib[4]].allocated, size_t) CTL_RO_CGEN(config_stats, stats_arenas_i_bins_j_nmalloc, ctl_stats.arenas[mib[2]].bstats[mib[4]].nmalloc, uint64_t) CTL_RO_CGEN(config_stats, stats_arenas_i_bins_j_ndalloc, ctl_stats.arenas[mib[2]].bstats[mib[4]].ndalloc, uint64_t) CTL_RO_CGEN(config_stats, stats_arenas_i_bins_j_nrequests, ctl_stats.arenas[mib[2]].bstats[mib[4]].nrequests, uint64_t) +CTL_RO_CGEN(config_stats, stats_arenas_i_bins_j_curregs, + ctl_stats.arenas[mib[2]].bstats[mib[4]].curregs, size_t) CTL_RO_CGEN(config_stats && config_tcache, stats_arenas_i_bins_j_nfills, ctl_stats.arenas[mib[2]].bstats[mib[4]].nfills, uint64_t) CTL_RO_CGEN(config_stats && config_tcache, stats_arenas_i_bins_j_nflushes, @@ -1666,13 +2086,32 @@ stats_arenas_i_lruns_j_index(const size_t *mib, size_t miblen, size_t j) return (super_stats_arenas_i_lruns_j_node); } +CTL_RO_CGEN(config_stats, stats_arenas_i_hchunks_j_nmalloc, + ctl_stats.arenas[mib[2]].hstats[mib[4]].nmalloc, uint64_t) +CTL_RO_CGEN(config_stats, stats_arenas_i_hchunks_j_ndalloc, + ctl_stats.arenas[mib[2]].hstats[mib[4]].ndalloc, uint64_t) +CTL_RO_CGEN(config_stats, stats_arenas_i_hchunks_j_nrequests, + ctl_stats.arenas[mib[2]].hstats[mib[4]].nmalloc, /* Intentional. */ + uint64_t) +CTL_RO_CGEN(config_stats, stats_arenas_i_hchunks_j_curhchunks, + ctl_stats.arenas[mib[2]].hstats[mib[4]].curhchunks, size_t) + +static const ctl_named_node_t * +stats_arenas_i_hchunks_j_index(const size_t *mib, size_t miblen, size_t j) +{ + + if (j > nhclasses) + return (NULL); + return (super_stats_arenas_i_hchunks_j_node); +} + 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 (i > ctl_stats.narenas || ctl_stats.arenas[i].initialized == false) { + if (i > ctl_stats.narenas || !ctl_stats.arenas[i].initialized) { ret = NULL; goto label_return; } diff --git a/contrib/jemalloc/src/extent.c b/contrib/jemalloc/src/extent.c index 8c09b486ed81..13f94411c15a 100644 --- a/contrib/jemalloc/src/extent.c +++ b/contrib/jemalloc/src/extent.c @@ -3,17 +3,32 @@ /******************************************************************************/ -static inline int +JEMALLOC_INLINE_C size_t +extent_quantize(size_t size) +{ + + /* + * Round down to the nearest chunk size that can actually be requested + * during normal huge allocation. + */ + return (index2size(size2index(size + 1) - 1)); +} + +JEMALLOC_INLINE_C int extent_szad_comp(extent_node_t *a, extent_node_t *b) { int ret; - size_t a_size = a->size; - size_t b_size = b->size; + size_t a_qsize = extent_quantize(extent_node_size_get(a)); + size_t b_qsize = extent_quantize(extent_node_size_get(b)); - ret = (a_size > b_size) - (a_size < b_size); + /* + * Compare based on quantized size rather than size, in order to sort + * equally useful extents only by address. + */ + ret = (a_qsize > b_qsize) - (a_qsize < b_qsize); if (ret == 0) { - uintptr_t a_addr = (uintptr_t)a->addr; - uintptr_t b_addr = (uintptr_t)b->addr; + uintptr_t a_addr = (uintptr_t)extent_node_addr_get(a); + uintptr_t b_addr = (uintptr_t)extent_node_addr_get(b); ret = (a_addr > b_addr) - (a_addr < b_addr); } @@ -22,18 +37,17 @@ extent_szad_comp(extent_node_t *a, extent_node_t *b) } /* Generate red-black tree functions. */ -rb_gen(, extent_tree_szad_, extent_tree_t, extent_node_t, link_szad, +rb_gen(, extent_tree_szad_, extent_tree_t, extent_node_t, szad_link, extent_szad_comp) -static inline int +JEMALLOC_INLINE_C int extent_ad_comp(extent_node_t *a, extent_node_t *b) { - uintptr_t a_addr = (uintptr_t)a->addr; - uintptr_t b_addr = (uintptr_t)b->addr; + uintptr_t a_addr = (uintptr_t)extent_node_addr_get(a); + uintptr_t b_addr = (uintptr_t)extent_node_addr_get(b); return ((a_addr > b_addr) - (a_addr < b_addr)); } /* Generate red-black tree functions. */ -rb_gen(, extent_tree_ad_, extent_tree_t, extent_node_t, link_ad, - extent_ad_comp) +rb_gen(, extent_tree_ad_, extent_tree_t, extent_node_t, ad_link, extent_ad_comp) diff --git a/contrib/jemalloc/src/huge.c b/contrib/jemalloc/src/huge.c index d72f21357021..54c2114ce2ba 100644 --- a/contrib/jemalloc/src/huge.c +++ b/contrib/jemalloc/src/huge.c @@ -2,44 +2,68 @@ #include "jemalloc/internal/jemalloc_internal.h" /******************************************************************************/ -/* Data. */ -uint64_t huge_nmalloc; -uint64_t huge_ndalloc; -size_t huge_allocated; +static extent_node_t * +huge_node_get(const void *ptr) +{ + extent_node_t *node; -malloc_mutex_t huge_mtx; + node = chunk_lookup(ptr, true); + assert(!extent_node_achunk_get(node)); -/******************************************************************************/ + return (node); +} -/* Tree of chunks that are stand-alone huge allocations. */ -static extent_tree_t huge; - -void * -huge_malloc(size_t size, bool zero, dss_prec_t dss_prec) +static bool +huge_node_set(const void *ptr, extent_node_t *node) { - return (huge_palloc(size, chunksize, zero, dss_prec)); + assert(extent_node_addr_get(node) == ptr); + assert(!extent_node_achunk_get(node)); + return (chunk_register(ptr, node)); +} + +static void +huge_node_unset(const void *ptr, const extent_node_t *node) +{ + + chunk_deregister(ptr, node); } void * -huge_palloc(size_t size, size_t alignment, bool zero, dss_prec_t dss_prec) +huge_malloc(tsd_t *tsd, arena_t *arena, size_t size, bool zero, + tcache_t *tcache) +{ + size_t usize; + + usize = s2u(size); + if (usize == 0) { + /* size_t overflow. */ + return (NULL); + } + + return (huge_palloc(tsd, arena, usize, chunksize, zero, tcache)); +} + +void * +huge_palloc(tsd_t *tsd, arena_t *arena, size_t size, size_t alignment, + bool zero, tcache_t *tcache) { void *ret; - size_t csize; + size_t usize; extent_node_t *node; bool is_zeroed; /* Allocate one or more contiguous chunks for this request. */ - csize = CHUNK_CEILING(size); - if (csize == 0) { - /* size is large enough to cause size_t wrap-around. */ + usize = sa2u(size, alignment); + if (unlikely(usize == 0)) return (NULL); - } + assert(usize >= chunksize); /* Allocate an extent node with which to track the chunk. */ - node = base_node_alloc(); + node = ipallocztm(tsd, CACHELINE_CEILING(sizeof(extent_node_t)), + CACHELINE, false, tcache, true, arena); if (node == NULL) return (NULL); @@ -48,145 +72,33 @@ huge_palloc(size_t size, size_t alignment, bool zero, dss_prec_t dss_prec) * it is possible to make correct junk/zero fill decisions below. */ is_zeroed = zero; - ret = chunk_alloc(csize, alignment, false, &is_zeroed, dss_prec); - if (ret == NULL) { - base_node_dealloc(node); + arena = arena_choose(tsd, arena); + if (unlikely(arena == NULL) || (ret = arena_chunk_alloc_huge(arena, + size, alignment, &is_zeroed)) == NULL) { + idalloctm(tsd, node, tcache, true); + return (NULL); + } + + extent_node_init(node, arena, ret, size, is_zeroed, true); + + if (huge_node_set(ret, node)) { + arena_chunk_dalloc_huge(arena, ret, size); + idalloctm(tsd, node, tcache, true); return (NULL); } /* Insert node into huge. */ - node->addr = ret; - node->size = csize; + malloc_mutex_lock(&arena->huge_mtx); + ql_elm_new(node, ql_link); + ql_tail_insert(&arena->huge, node, ql_link); + malloc_mutex_unlock(&arena->huge_mtx); - malloc_mutex_lock(&huge_mtx); - extent_tree_ad_insert(&huge, node); - if (config_stats) { - stats_cactive_add(csize); - huge_nmalloc++; - huge_allocated += csize; - } - malloc_mutex_unlock(&huge_mtx); + if (zero || (config_fill && unlikely(opt_zero))) { + if (!is_zeroed) + memset(ret, 0, size); + } else if (config_fill && unlikely(opt_junk_alloc)) + memset(ret, 0xa5, size); - if (config_fill && zero == false) { - if (opt_junk) - memset(ret, 0xa5, csize); - else if (opt_zero && is_zeroed == false) - memset(ret, 0, csize); - } - - return (ret); -} - -bool -huge_ralloc_no_move(void *ptr, size_t oldsize, size_t size, size_t extra) -{ - - /* - * Avoid moving the allocation if the size class can be left the same. - */ - if (oldsize > arena_maxclass - && CHUNK_CEILING(oldsize) >= CHUNK_CEILING(size) - && CHUNK_CEILING(oldsize) <= CHUNK_CEILING(size+extra)) { - assert(CHUNK_CEILING(oldsize) == oldsize); - return (false); - } - - /* Reallocation would require a move. */ - return (true); -} - -void * -huge_ralloc(void *ptr, size_t oldsize, size_t size, size_t extra, - size_t alignment, bool zero, bool try_tcache_dalloc, dss_prec_t dss_prec) -{ - void *ret; - size_t copysize; - - /* Try to avoid moving the allocation. */ - if (huge_ralloc_no_move(ptr, oldsize, size, extra) == false) - return (ptr); - - /* - * size and oldsize are different enough that we need to use a - * different size class. In that case, fall back to allocating new - * space and copying. - */ - if (alignment > chunksize) - ret = huge_palloc(size + extra, alignment, zero, dss_prec); - else - ret = huge_malloc(size + extra, zero, dss_prec); - - if (ret == NULL) { - if (extra == 0) - return (NULL); - /* Try again, this time without extra. */ - if (alignment > chunksize) - ret = huge_palloc(size, alignment, zero, dss_prec); - else - ret = huge_malloc(size, zero, dss_prec); - - if (ret == NULL) - return (NULL); - } - - /* - * Copy at most size bytes (not size+extra), since the caller has no - * expectation that the extra bytes will be reliably preserved. - */ - copysize = (size < oldsize) ? size : oldsize; - -#ifdef JEMALLOC_MREMAP - /* - * Use mremap(2) if this is a huge-->huge reallocation, and neither the - * source nor the destination are in dss. - */ - if (oldsize >= chunksize && (config_dss == false || (chunk_in_dss(ptr) - == false && chunk_in_dss(ret) == false))) { - size_t newsize = huge_salloc(ret); - - /* - * Remove ptr from the tree of huge allocations before - * performing the remap operation, in order to avoid the - * possibility of another thread acquiring that mapping before - * this one removes it from the tree. - */ - huge_dalloc(ptr, false); - if (mremap(ptr, oldsize, newsize, MREMAP_MAYMOVE|MREMAP_FIXED, - ret) == MAP_FAILED) { - /* - * Assuming no chunk management bugs in the allocator, - * the only documented way an error can occur here is - * if the application changed the map type for a - * portion of the old allocation. This is firmly in - * undefined behavior territory, so write a diagnostic - * message, and optionally abort. - */ - char buf[BUFERROR_BUF]; - - buferror(get_errno(), buf, sizeof(buf)); - malloc_printf(": Error in mremap(): %s\n", - buf); - if (opt_abort) - abort(); - memcpy(ret, ptr, copysize); - chunk_dealloc_mmap(ptr, oldsize); - } else if (config_fill && zero == false && opt_junk && oldsize - < newsize) { - /* - * mremap(2) clobbers the original mapping, so - * junk/zero filling is not preserved. There is no - * need to zero fill here, since any trailing - * uninititialized memory is demand-zeroed by the - * kernel, but junk filling must be redone. - */ - memset(ret + oldsize, 0xa5, newsize - oldsize); - } - } else -#endif - { - memcpy(ret, ptr, copysize); - iqalloct(ptr, try_tcache_dalloc); - } return (ret); } @@ -198,12 +110,12 @@ static void huge_dalloc_junk(void *ptr, size_t usize) { - if (config_fill && config_dss && opt_junk) { + if (config_fill && have_dss && unlikely(opt_junk_free)) { /* * Only bother junk filling if the chunk isn't about to be * unmapped. */ - if (config_munmap == false || (config_dss && chunk_in_dss(ptr))) + if (!config_munmap || (have_dss && chunk_in_dss(ptr))) memset(ptr, 0x5a, usize); } } @@ -213,135 +125,317 @@ huge_dalloc_junk(void *ptr, size_t usize) huge_dalloc_junk_t *huge_dalloc_junk = JEMALLOC_N(huge_dalloc_junk_impl); #endif -void -huge_dalloc(void *ptr, bool unmap) +static void +huge_ralloc_no_move_similar(void *ptr, size_t oldsize, size_t usize, + size_t size, size_t extra, bool zero) { - extent_node_t *node, key; + size_t usize_next; + extent_node_t *node; + arena_t *arena; + chunk_hooks_t chunk_hooks = CHUNK_HOOKS_INITIALIZER; + bool zeroed; - malloc_mutex_lock(&huge_mtx); + /* Increase usize to incorporate extra. */ + while (usize < s2u(size+extra) && (usize_next = s2u(usize+1)) < oldsize) + usize = usize_next; - /* Extract from tree of huge allocations. */ - key.addr = ptr; - node = extent_tree_ad_search(&huge, &key); - assert(node != NULL); - assert(node->addr == ptr); - extent_tree_ad_remove(&huge, node); + if (oldsize == usize) + return; - if (config_stats) { - stats_cactive_sub(node->size); - huge_ndalloc++; - huge_allocated -= node->size; + node = huge_node_get(ptr); + arena = extent_node_arena_get(node); + + /* Fill if necessary (shrinking). */ + if (oldsize > usize) { + size_t sdiff = oldsize - usize; + zeroed = !chunk_purge_wrapper(arena, &chunk_hooks, ptr, + CHUNK_CEILING(usize), usize, sdiff); + if (config_fill && unlikely(opt_junk_free)) { + memset((void *)((uintptr_t)ptr + usize), 0x5a, sdiff); + zeroed = false; + } + } else + zeroed = true; + + malloc_mutex_lock(&arena->huge_mtx); + /* Update the size of the huge allocation. */ + assert(extent_node_size_get(node) != usize); + extent_node_size_set(node, usize); + /* Clear node's zeroed field if zeroing failed above. */ + extent_node_zeroed_set(node, extent_node_zeroed_get(node) && zeroed); + malloc_mutex_unlock(&arena->huge_mtx); + + arena_chunk_ralloc_huge_similar(arena, ptr, oldsize, usize); + + /* Fill if necessary (growing). */ + if (oldsize < usize) { + if (zero || (config_fill && unlikely(opt_zero))) { + if (!zeroed) { + memset((void *)((uintptr_t)ptr + oldsize), 0, + usize - oldsize); + } + } else if (config_fill && unlikely(opt_junk_alloc)) { + memset((void *)((uintptr_t)ptr + oldsize), 0xa5, usize - + oldsize); + } + } +} + +static bool +huge_ralloc_no_move_shrink(void *ptr, size_t oldsize, size_t usize) +{ + extent_node_t *node; + arena_t *arena; + chunk_hooks_t chunk_hooks; + size_t cdiff; + bool zeroed; + + node = huge_node_get(ptr); + arena = extent_node_arena_get(node); + chunk_hooks = chunk_hooks_get(arena); + + /* Split excess chunks. */ + cdiff = CHUNK_CEILING(oldsize) - CHUNK_CEILING(usize); + if (cdiff != 0 && chunk_hooks.split(ptr, CHUNK_CEILING(oldsize), + CHUNK_CEILING(usize), cdiff, true, arena->ind)) + return (true); + + if (oldsize > usize) { + size_t sdiff = oldsize - usize; + zeroed = !chunk_purge_wrapper(arena, &chunk_hooks, + CHUNK_ADDR2BASE((uintptr_t)ptr + usize), + CHUNK_CEILING(usize), CHUNK_ADDR2OFFSET((uintptr_t)ptr + + usize), sdiff); + if (config_fill && unlikely(opt_junk_free)) { + huge_dalloc_junk((void *)((uintptr_t)ptr + usize), + sdiff); + zeroed = false; + } + } else + zeroed = true; + + malloc_mutex_lock(&arena->huge_mtx); + /* Update the size of the huge allocation. */ + extent_node_size_set(node, usize); + /* Clear node's zeroed field if zeroing failed above. */ + extent_node_zeroed_set(node, extent_node_zeroed_get(node) && zeroed); + malloc_mutex_unlock(&arena->huge_mtx); + + /* Zap the excess chunks. */ + arena_chunk_ralloc_huge_shrink(arena, ptr, oldsize, usize); + + return (false); +} + +static bool +huge_ralloc_no_move_expand(void *ptr, size_t oldsize, size_t size, bool zero) { + size_t usize; + extent_node_t *node; + arena_t *arena; + bool is_zeroed_subchunk, is_zeroed_chunk; + + usize = s2u(size); + if (usize == 0) { + /* size_t overflow. */ + return (true); } - malloc_mutex_unlock(&huge_mtx); + node = huge_node_get(ptr); + arena = extent_node_arena_get(node); + malloc_mutex_lock(&arena->huge_mtx); + is_zeroed_subchunk = extent_node_zeroed_get(node); + malloc_mutex_unlock(&arena->huge_mtx); - if (unmap) - huge_dalloc_junk(node->addr, node->size); + /* + * Copy zero into is_zeroed_chunk and pass the copy to chunk_alloc(), so + * that it is possible to make correct junk/zero fill decisions below. + */ + is_zeroed_chunk = zero; - chunk_dealloc(node->addr, node->size, unmap); - - base_node_dealloc(node); -} - -size_t -huge_salloc(const void *ptr) -{ - size_t ret; - extent_node_t *node, key; - - malloc_mutex_lock(&huge_mtx); - - /* Extract from tree of huge allocations. */ - key.addr = __DECONST(void *, ptr); - node = extent_tree_ad_search(&huge, &key); - assert(node != NULL); - - ret = node->size; - - malloc_mutex_unlock(&huge_mtx); - - return (ret); -} - -dss_prec_t -huge_dss_prec_get(arena_t *arena) -{ - - return (arena_dss_prec_get(choose_arena(arena))); -} - -prof_ctx_t * -huge_prof_ctx_get(const void *ptr) -{ - prof_ctx_t *ret; - extent_node_t *node, key; - - malloc_mutex_lock(&huge_mtx); - - /* Extract from tree of huge allocations. */ - key.addr = __DECONST(void *, ptr); - node = extent_tree_ad_search(&huge, &key); - assert(node != NULL); - - ret = node->prof_ctx; - - malloc_mutex_unlock(&huge_mtx); - - return (ret); -} - -void -huge_prof_ctx_set(const void *ptr, prof_ctx_t *ctx) -{ - extent_node_t *node, key; - - malloc_mutex_lock(&huge_mtx); - - /* Extract from tree of huge allocations. */ - key.addr = __DECONST(void *, ptr); - node = extent_tree_ad_search(&huge, &key); - assert(node != NULL); - - node->prof_ctx = ctx; - - malloc_mutex_unlock(&huge_mtx); -} - -bool -huge_boot(void) -{ - - /* Initialize chunks data. */ - if (malloc_mutex_init(&huge_mtx)) + if (arena_chunk_ralloc_huge_expand(arena, ptr, oldsize, usize, + &is_zeroed_chunk)) return (true); - extent_tree_ad_new(&huge); - if (config_stats) { - huge_nmalloc = 0; - huge_ndalloc = 0; - huge_allocated = 0; + malloc_mutex_lock(&arena->huge_mtx); + /* Update the size of the huge allocation. */ + extent_node_size_set(node, usize); + malloc_mutex_unlock(&arena->huge_mtx); + + if (zero || (config_fill && unlikely(opt_zero))) { + if (!is_zeroed_subchunk) { + memset((void *)((uintptr_t)ptr + oldsize), 0, + CHUNK_CEILING(oldsize) - oldsize); + } + if (!is_zeroed_chunk) { + memset((void *)((uintptr_t)ptr + + CHUNK_CEILING(oldsize)), 0, usize - + CHUNK_CEILING(oldsize)); + } + } else if (config_fill && unlikely(opt_junk_alloc)) { + memset((void *)((uintptr_t)ptr + oldsize), 0xa5, usize - + oldsize); } return (false); } -void -huge_prefork(void) +bool +huge_ralloc_no_move(void *ptr, size_t oldsize, size_t size, size_t extra, + bool zero) { + size_t usize; - malloc_mutex_prefork(&huge_mtx); + /* Both allocations must be huge to avoid a move. */ + if (oldsize < chunksize) + return (true); + + assert(s2u(oldsize) == oldsize); + usize = s2u(size); + if (usize == 0) { + /* size_t overflow. */ + return (true); + } + + /* + * Avoid moving the allocation if the existing chunk size accommodates + * the new size. + */ + if (CHUNK_CEILING(oldsize) >= CHUNK_CEILING(usize) + && CHUNK_CEILING(oldsize) <= CHUNK_CEILING(s2u(size+extra))) { + huge_ralloc_no_move_similar(ptr, oldsize, usize, size, extra, + zero); + return (false); + } + + /* Attempt to shrink the allocation in-place. */ + if (CHUNK_CEILING(oldsize) >= CHUNK_CEILING(usize)) + return (huge_ralloc_no_move_shrink(ptr, oldsize, usize)); + + /* Attempt to expand the allocation in-place. */ + if (huge_ralloc_no_move_expand(ptr, oldsize, size + extra, zero)) { + if (extra == 0) + return (true); + + /* Try again, this time without extra. */ + return (huge_ralloc_no_move_expand(ptr, oldsize, size, zero)); + } + return (false); +} + +void * +huge_ralloc(tsd_t *tsd, arena_t *arena, void *ptr, size_t oldsize, size_t size, + size_t extra, size_t alignment, bool zero, tcache_t *tcache) +{ + void *ret; + size_t copysize; + + /* Try to avoid moving the allocation. */ + if (!huge_ralloc_no_move(ptr, oldsize, size, extra, zero)) + return (ptr); + + /* + * size and oldsize are different enough that we need to use a + * different size class. In that case, fall back to allocating new + * space and copying. + */ + if (alignment > chunksize) { + ret = huge_palloc(tsd, arena, size + extra, alignment, zero, + tcache); + } else + ret = huge_malloc(tsd, arena, size + extra, zero, tcache); + + if (ret == NULL) { + if (extra == 0) + return (NULL); + /* Try again, this time without extra. */ + if (alignment > chunksize) { + ret = huge_palloc(tsd, arena, size, alignment, zero, + tcache); + } else + ret = huge_malloc(tsd, arena, size, zero, tcache); + + if (ret == NULL) + return (NULL); + } + + /* + * Copy at most size bytes (not size+extra), since the caller has no + * expectation that the extra bytes will be reliably preserved. + */ + copysize = (size < oldsize) ? size : oldsize; + memcpy(ret, ptr, copysize); + isqalloc(tsd, ptr, oldsize, tcache); + return (ret); } void -huge_postfork_parent(void) +huge_dalloc(tsd_t *tsd, void *ptr, tcache_t *tcache) +{ + extent_node_t *node; + arena_t *arena; + + node = huge_node_get(ptr); + arena = extent_node_arena_get(node); + huge_node_unset(ptr, node); + malloc_mutex_lock(&arena->huge_mtx); + ql_remove(&arena->huge, node, ql_link); + malloc_mutex_unlock(&arena->huge_mtx); + + huge_dalloc_junk(extent_node_addr_get(node), + extent_node_size_get(node)); + arena_chunk_dalloc_huge(extent_node_arena_get(node), + extent_node_addr_get(node), extent_node_size_get(node)); + idalloctm(tsd, node, tcache, true); +} + +arena_t * +huge_aalloc(const void *ptr) { - malloc_mutex_postfork_parent(&huge_mtx); + return (extent_node_arena_get(huge_node_get(ptr))); +} + +size_t +huge_salloc(const void *ptr) +{ + size_t size; + extent_node_t *node; + arena_t *arena; + + node = huge_node_get(ptr); + arena = extent_node_arena_get(node); + malloc_mutex_lock(&arena->huge_mtx); + size = extent_node_size_get(node); + malloc_mutex_unlock(&arena->huge_mtx); + + return (size); +} + +prof_tctx_t * +huge_prof_tctx_get(const void *ptr) +{ + prof_tctx_t *tctx; + extent_node_t *node; + arena_t *arena; + + node = huge_node_get(ptr); + arena = extent_node_arena_get(node); + malloc_mutex_lock(&arena->huge_mtx); + tctx = extent_node_prof_tctx_get(node); + malloc_mutex_unlock(&arena->huge_mtx); + + return (tctx); } void -huge_postfork_child(void) +huge_prof_tctx_set(const void *ptr, prof_tctx_t *tctx) { + extent_node_t *node; + arena_t *arena; - malloc_mutex_postfork_child(&huge_mtx); + node = huge_node_get(ptr); + arena = extent_node_arena_get(node); + malloc_mutex_lock(&arena->huge_mtx); + extent_node_prof_tctx_set(node, tctx); + malloc_mutex_unlock(&arena->huge_mtx); } diff --git a/contrib/jemalloc/src/jemalloc.c b/contrib/jemalloc/src/jemalloc.c index 1ffa06468d6b..d078a1fb8f92 100644 --- a/contrib/jemalloc/src/jemalloc.c +++ b/contrib/jemalloc/src/jemalloc.c @@ -4,16 +4,12 @@ /******************************************************************************/ /* Data. */ -malloc_tsd_data(, arenas, arena_t *, NULL) -malloc_tsd_data(, thread_allocated, thread_allocated_t, - THREAD_ALLOCATED_INITIALIZER) - /* Work around : */ const char *__malloc_options_1_0 = NULL; __sym_compat(_malloc_options, __malloc_options_1_0, FBSD_1.0); /* Runtime configuration options. */ -const char *je_malloc_conf; +const char *je_malloc_conf JEMALLOC_ATTR(weak); bool opt_abort = #ifdef JEMALLOC_DEBUG true @@ -21,30 +17,152 @@ bool opt_abort = false #endif ; -bool opt_junk = +const char *opt_junk = +#if (defined(JEMALLOC_DEBUG) && defined(JEMALLOC_FILL)) + "true" +#else + "false" +#endif + ; +bool opt_junk_alloc = #if (defined(JEMALLOC_DEBUG) && defined(JEMALLOC_FILL)) true #else false #endif ; +bool opt_junk_free = +#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; -bool opt_valgrind = false; bool opt_xmalloc = false; bool opt_zero = false; size_t opt_narenas = 0; +/* Initialized to true if the process is running inside Valgrind. */ +bool in_valgrind; + unsigned ncpus; -malloc_mutex_t arenas_lock; -arena_t **arenas; -unsigned narenas_total; -unsigned narenas_auto; +/* Protects arenas initialization (arenas, narenas_total). */ +static 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. + */ +static arena_t **arenas; +static unsigned narenas_total; +static arena_t *a0; /* arenas[0]; read-only after initialization. */ +static unsigned narenas_auto; /* Read-only after initialization. */ -/* Set to true once the allocator has been initialized. */ -static bool malloc_initialized = false; +typedef enum { + malloc_init_uninitialized = 3, + malloc_init_a0_initialized = 2, + malloc_init_recursible = 1, + malloc_init_initialized = 0 /* Common case --> jnz. */ +} malloc_init_t; +static malloc_init_t malloc_init_state = malloc_init_uninitialized; + +JEMALLOC_ALIGNED(CACHELINE) +const size_t index2size_tab[NSIZES] = { +#define SC(index, lg_grp, lg_delta, ndelta, bin, lg_delta_lookup) \ + ((ZU(1)<= 0x0600 +static malloc_mutex_t init_lock = SRWLOCK_INIT; +#else static malloc_mutex_t init_lock; JEMALLOC_ATTR(constructor) @@ -76,7 +197,7 @@ _init_init_lock(void) JEMALLOC_SECTION(".CRT$XCU") JEMALLOC_ATTR(used) static const void (WINAPI *init_init_lock)(void) = _init_init_lock; #endif - +#endif #else static malloc_mutex_t init_lock = MALLOC_MUTEX_INITIALIZER; #endif @@ -89,7 +210,7 @@ typedef struct { #ifdef JEMALLOC_UTRACE # define UTRACE(a, b, c) do { \ - if (opt_utrace) { \ + if (unlikely(opt_utrace)) { \ int utrace_serrno = errno; \ malloc_utrace_t ut; \ ut.p = (a); \ @@ -109,6 +230,7 @@ typedef struct { * definition. */ +static bool malloc_init_hard_a0(void); static bool malloc_init_hard(void); /******************************************************************************/ @@ -116,35 +238,332 @@ static bool malloc_init_hard(void); * Begin miscellaneous support functions. */ -/* Create a new arena and insert it into the arenas array at index ind. */ -arena_t * -arenas_extend(unsigned ind) +JEMALLOC_ALWAYS_INLINE_C bool +malloc_initialized(void) { - arena_t *ret; - ret = (arena_t *)base_alloc(sizeof(arena_t)); - if (ret != NULL && arena_new(ret, ind) == false) { - arenas[ind] = ret; - return (ret); - } - /* Only reached if there is an OOM error. */ - - /* - * OOM here is quite inconvenient to propagate, since dealing with it - * would require a check for failure in the fast path. Instead, punt - * by using arenas[0]. In practice, this is an extremely unlikely - * failure. - */ - malloc_write(": Error initializing arena\n"); - if (opt_abort) - abort(); - - return (arenas[0]); + return (malloc_init_state == malloc_init_initialized); } -/* Slow path, called only by choose_arena(). */ +JEMALLOC_ALWAYS_INLINE_C void +malloc_thread_init(void) +{ + + /* + * TSD initialization can't be safely done as a side effect of + * deallocation, because it is possible for a thread to do nothing but + * deallocate its TLS data via free(), in which case writing to TLS + * would cause write-after-free memory corruption. The quarantine + * facility *only* gets used as a side effect of deallocation, so make + * a best effort attempt at initializing its TSD by hooking all + * allocation events. + */ + if (config_fill && unlikely(opt_quarantine)) + quarantine_alloc_hook(); +} + +JEMALLOC_ALWAYS_INLINE_C bool +malloc_init_a0(void) +{ + + if (unlikely(malloc_init_state == malloc_init_uninitialized)) + return (malloc_init_hard_a0()); + return (false); +} + +JEMALLOC_ALWAYS_INLINE_C bool +malloc_init(void) +{ + + if (unlikely(!malloc_initialized()) && malloc_init_hard()) + return (true); + malloc_thread_init(); + + return (false); +} + +/* + * The a0*() functions are used instead of i[mcd]alloc() in situations that + * cannot tolerate TLS variable access. + */ + arena_t * -choose_arena_hard(void) +a0get(void) +{ + + assert(a0 != NULL); + return (a0); +} + +static void * +a0ialloc(size_t size, bool zero, bool is_metadata) +{ + + if (unlikely(malloc_init_a0())) + return (NULL); + + return (iallocztm(NULL, size, zero, false, is_metadata, a0get())); +} + +static void +a0idalloc(void *ptr, bool is_metadata) +{ + + idalloctm(NULL, ptr, false, is_metadata); +} + +void * +a0malloc(size_t size) +{ + + return (a0ialloc(size, false, true)); +} + +void +a0dalloc(void *ptr) +{ + + a0idalloc(ptr, true); +} + +/* + * FreeBSD's libc uses the bootstrap_*() functions in bootstrap-senstive + * situations that cannot tolerate TLS variable access (TLS allocation and very + * early internal data structure initialization). + */ + +void * +bootstrap_malloc(size_t size) +{ + + if (unlikely(size == 0)) + size = 1; + + return (a0ialloc(size, false, false)); +} + +void * +bootstrap_calloc(size_t num, size_t size) +{ + size_t num_size; + + num_size = num * size; + if (unlikely(num_size == 0)) { + assert(num == 0 || size == 0); + num_size = 1; + } + + return (a0ialloc(num_size, true, false)); +} + +void +bootstrap_free(void *ptr) +{ + + if (unlikely(ptr == NULL)) + return; + + a0idalloc(ptr, false); +} + +/* Create a new arena and insert it into the arenas array at index ind. */ +static arena_t * +arena_init_locked(unsigned ind) +{ + arena_t *arena; + + /* Expand arenas if necessary. */ + assert(ind <= narenas_total); + if (ind > MALLOCX_ARENA_MAX) + return (NULL); + if (ind == narenas_total) { + unsigned narenas_new = narenas_total + 1; + arena_t **arenas_new = + (arena_t **)a0malloc(CACHELINE_CEILING(narenas_new * + sizeof(arena_t *))); + if (arenas_new == NULL) + return (NULL); + memcpy(arenas_new, arenas, narenas_total * sizeof(arena_t *)); + arenas_new[ind] = NULL; + /* + * Deallocate only if arenas came from a0malloc() (not + * base_alloc()). + */ + if (narenas_total != narenas_auto) + a0dalloc(arenas); + arenas = arenas_new; + narenas_total = narenas_new; + } + + /* + * Another thread may have already initialized arenas[ind] if it's an + * auto arena. + */ + arena = arenas[ind]; + if (arena != NULL) { + assert(ind < narenas_auto); + return (arena); + } + + /* Actually initialize the arena. */ + arena = arenas[ind] = arena_new(ind); + return (arena); +} + +arena_t * +arena_init(unsigned ind) +{ + arena_t *arena; + + malloc_mutex_lock(&arenas_lock); + arena = arena_init_locked(ind); + malloc_mutex_unlock(&arenas_lock); + return (arena); +} + +unsigned +narenas_total_get(void) +{ + unsigned narenas; + + malloc_mutex_lock(&arenas_lock); + narenas = narenas_total; + malloc_mutex_unlock(&arenas_lock); + + return (narenas); +} + +static void +arena_bind_locked(tsd_t *tsd, unsigned ind) +{ + arena_t *arena; + + arena = arenas[ind]; + arena->nthreads++; + + if (tsd_nominal(tsd)) + tsd_arena_set(tsd, arena); +} + +static void +arena_bind(tsd_t *tsd, unsigned ind) +{ + + malloc_mutex_lock(&arenas_lock); + arena_bind_locked(tsd, ind); + malloc_mutex_unlock(&arenas_lock); +} + +void +arena_migrate(tsd_t *tsd, unsigned oldind, unsigned newind) +{ + arena_t *oldarena, *newarena; + + malloc_mutex_lock(&arenas_lock); + oldarena = arenas[oldind]; + newarena = arenas[newind]; + oldarena->nthreads--; + newarena->nthreads++; + malloc_mutex_unlock(&arenas_lock); + tsd_arena_set(tsd, newarena); +} + +unsigned +arena_nbound(unsigned ind) +{ + unsigned nthreads; + + malloc_mutex_lock(&arenas_lock); + nthreads = arenas[ind]->nthreads; + malloc_mutex_unlock(&arenas_lock); + return (nthreads); +} + +static void +arena_unbind(tsd_t *tsd, unsigned ind) +{ + arena_t *arena; + + malloc_mutex_lock(&arenas_lock); + arena = arenas[ind]; + arena->nthreads--; + malloc_mutex_unlock(&arenas_lock); + tsd_arena_set(tsd, NULL); +} + +arena_t * +arena_get_hard(tsd_t *tsd, unsigned ind, bool init_if_missing) +{ + arena_t *arena; + arena_t **arenas_cache = tsd_arenas_cache_get(tsd); + unsigned narenas_cache = tsd_narenas_cache_get(tsd); + unsigned narenas_actual = narenas_total_get(); + + /* Deallocate old cache if it's too small. */ + if (arenas_cache != NULL && narenas_cache < narenas_actual) { + a0dalloc(arenas_cache); + arenas_cache = NULL; + narenas_cache = 0; + tsd_arenas_cache_set(tsd, arenas_cache); + tsd_narenas_cache_set(tsd, narenas_cache); + } + + /* Allocate cache if it's missing. */ + if (arenas_cache == NULL) { + bool *arenas_cache_bypassp = tsd_arenas_cache_bypassp_get(tsd); + assert(ind < narenas_actual || !init_if_missing); + narenas_cache = (ind < narenas_actual) ? narenas_actual : ind+1; + + if (!*arenas_cache_bypassp) { + *arenas_cache_bypassp = true; + arenas_cache = (arena_t **)a0malloc(sizeof(arena_t *) * + narenas_cache); + *arenas_cache_bypassp = false; + } else + arenas_cache = NULL; + if (arenas_cache == NULL) { + /* + * This function must always tell the truth, even if + * it's slow, so don't let OOM or recursive allocation + * avoidance (note arenas_cache_bypass check) get in the + * way. + */ + if (ind >= narenas_actual) + return (NULL); + malloc_mutex_lock(&arenas_lock); + arena = arenas[ind]; + malloc_mutex_unlock(&arenas_lock); + return (arena); + } + tsd_arenas_cache_set(tsd, arenas_cache); + tsd_narenas_cache_set(tsd, narenas_cache); + } + + /* + * Copy to cache. It's possible that the actual number of arenas has + * increased since narenas_total_get() was called above, but that causes + * no correctness issues unless two threads concurrently execute the + * arenas.extend mallctl, which we trust mallctl synchronization to + * prevent. + */ + malloc_mutex_lock(&arenas_lock); + memcpy(arenas_cache, arenas, sizeof(arena_t *) * narenas_actual); + malloc_mutex_unlock(&arenas_lock); + if (narenas_cache > narenas_actual) { + memset(&arenas_cache[narenas_actual], 0, sizeof(arena_t *) * + (narenas_cache - narenas_actual)); + } + + /* Read the refreshed cache, and init the arena if necessary. */ + arena = arenas_cache[ind]; + if (init_if_missing && arena == NULL) + arena = arenas_cache[ind] = arena_init(ind); + return (arena); +} + +/* Slow path, called only by arena_choose(). */ +arena_t * +arena_choose_hard(tsd_t *tsd) { arena_t *ret; @@ -154,7 +573,7 @@ choose_arena_hard(void) choose = 0; first_null = narenas_auto; malloc_mutex_lock(&arenas_lock); - assert(arenas[0] != NULL); + assert(a0get() != NULL); for (i = 1; i < narenas_auto; i++) { if (arenas[i] != NULL) { /* @@ -187,22 +606,71 @@ choose_arena_hard(void) ret = arenas[choose]; } else { /* Initialize a new arena. */ - ret = arenas_extend(first_null); + choose = first_null; + ret = arena_init_locked(choose); + if (ret == NULL) { + malloc_mutex_unlock(&arenas_lock); + return (NULL); + } } - ret->nthreads++; + arena_bind_locked(tsd, choose); malloc_mutex_unlock(&arenas_lock); } else { - ret = arenas[0]; - malloc_mutex_lock(&arenas_lock); - ret->nthreads++; - malloc_mutex_unlock(&arenas_lock); + ret = a0get(); + arena_bind(tsd, 0); } - arenas_tsd_set(&ret); - return (ret); } +void +thread_allocated_cleanup(tsd_t *tsd) +{ + + /* Do nothing. */ +} + +void +thread_deallocated_cleanup(tsd_t *tsd) +{ + + /* Do nothing. */ +} + +void +arena_cleanup(tsd_t *tsd) +{ + arena_t *arena; + + arena = tsd_arena_get(tsd); + if (arena != NULL) + arena_unbind(tsd, arena->ind); +} + +void +arenas_cache_cleanup(tsd_t *tsd) +{ + arena_t **arenas_cache; + + arenas_cache = tsd_arenas_cache_get(tsd); + if (arenas_cache != NULL) + a0dalloc(arenas_cache); +} + +void +narenas_cache_cleanup(tsd_t *tsd) +{ + + /* Do nothing. */ +} + +void +arenas_cache_bypass_cleanup(tsd_t *tsd) +{ + + /* Do nothing. */ +} + static void stats_print_atexit(void) { @@ -247,6 +715,19 @@ stats_print_atexit(void) * Begin initialization functions. */ +#ifndef JEMALLOC_HAVE_SECURE_GETENV +static char * +secure_getenv(const char *name) +{ + +# ifdef JEMALLOC_HAVE_ISSETUGID + if (issetugid() != 0) + return (NULL); +# endif + return (getenv(name)); +} +#endif + static unsigned malloc_ncpus(void) { @@ -262,44 +743,6 @@ malloc_ncpus(void) return ((result == -1) ? 1 : (unsigned)result); } -void -arenas_cleanup(void *arg) -{ - arena_t *arena = *(arena_t **)arg; - - malloc_mutex_lock(&arenas_lock); - arena->nthreads--; - malloc_mutex_unlock(&arenas_lock); -} - -JEMALLOC_ALWAYS_INLINE_C void -malloc_thread_init(void) -{ - - /* - * TSD initialization can't be safely done as a side effect of - * deallocation, because it is possible for a thread to do nothing but - * deallocate its TLS data via free(), in which case writing to TLS - * would cause write-after-free memory corruption. The quarantine - * facility *only* gets used as a side effect of deallocation, so make - * a best effort attempt at initializing its TSD by hooking all - * allocation events. - */ - if (config_fill && opt_quarantine) - quarantine_alloc_hook(); -} - -JEMALLOC_ALWAYS_INLINE_C bool -malloc_init(void) -{ - - if (malloc_initialized == false && malloc_init_hard()) - return (true); - malloc_thread_init(); - - return (false); -} - static bool malloc_conf_next(char const **opts_p, char const **k_p, size_t *klen_p, char const **v_p, size_t *vlen_p) @@ -309,7 +752,7 @@ malloc_conf_next(char const **opts_p, char const **k_p, size_t *klen_p, *k_p = opts; - for (accept = false; accept == false;) { + for (accept = false; !accept;) { switch (*opts) { case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': case 'H': case 'I': case 'J': case 'K': case 'L': @@ -344,7 +787,7 @@ malloc_conf_next(char const **opts_p, char const **k_p, size_t *klen_p, } } - for (accept = false; accept == false;) { + for (accept = false; !accept;) { switch (*opts) { case ',': opts++; @@ -398,14 +841,16 @@ malloc_conf_init(void) * 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); + in_valgrind = (RUNNING_ON_VALGRIND != 0) ? true : false; + if (config_fill && unlikely(in_valgrind)) { + opt_junk = "false"; + opt_junk_alloc = false; + opt_junk_free = false; + assert(!opt_zero); opt_quarantine = JEMALLOC_VALGRIND_QUARANTINE_DEFAULT; opt_redzone = true; } - if (config_tcache && opt_valgrind) + if (config_tcache && unlikely(in_valgrind)) opt_tcache = false; } @@ -445,7 +890,7 @@ malloc_conf_init(void) if (linklen == -1) { /* No configuration specified. */ linklen = 0; - /* restore errno */ + /* Restore errno. */ set_errno(saved_errno); } #endif @@ -461,8 +906,7 @@ malloc_conf_init(void) #endif ; - if (issetugid() == 0 && (opts = getenv(envname)) != - NULL) { + if ((opts = secure_getenv(envname)) != NULL) { /* * Do nothing; opts is already initialized to * the value of the MALLOC_CONF environment @@ -480,27 +924,28 @@ malloc_conf_init(void) opts = buf; } - while (*opts != '\0' && malloc_conf_next(&opts, &k, &klen, &v, - &vlen) == false) { -#define CONF_HANDLE_BOOL(o, n) \ - if (sizeof(n)-1 == klen && strncmp(n, k, \ - klen) == 0) { \ - if (strncmp("true", v, vlen) == 0 && \ - vlen == sizeof("true")-1) \ + while (*opts != '\0' && !malloc_conf_next(&opts, &k, &klen, &v, + &vlen)) { +#define CONF_MATCH(n) \ + (sizeof(n)-1 == klen && strncmp(n, k, klen) == 0) +#define CONF_MATCH_VALUE(n) \ + (sizeof(n)-1 == vlen && strncmp(n, v, vlen) == 0) +#define CONF_HANDLE_BOOL(o, n, cont) \ + if (CONF_MATCH(n)) { \ + if (CONF_MATCH_VALUE("true")) \ o = true; \ - else if (strncmp("false", v, vlen) == \ - 0 && vlen == sizeof("false")-1) \ + else if (CONF_MATCH_VALUE("false")) \ o = false; \ else { \ malloc_conf_error( \ "Invalid conf value", \ k, klen, v, vlen); \ } \ - continue; \ + if (cont) \ + continue; \ } #define CONF_HANDLE_SIZE_T(o, n, min, max, clip) \ - if (sizeof(n)-1 == klen && strncmp(n, k, \ - klen) == 0) { \ + if (CONF_MATCH(n)) { \ uintmax_t um; \ char *end; \ \ @@ -512,15 +957,15 @@ malloc_conf_init(void) "Invalid conf value", \ k, klen, v, vlen); \ } else if (clip) { \ - if (min != 0 && um < min) \ - o = min; \ - else if (um > max) \ - o = max; \ + if ((min) != 0 && um < (min)) \ + o = (min); \ + else if (um > (max)) \ + o = (max); \ else \ o = um; \ } else { \ - if ((min != 0 && um < min) || \ - um > max) { \ + if (((min) != 0 && um < (min)) \ + || um > (max)) { \ malloc_conf_error( \ "Out-of-range " \ "conf value", \ @@ -531,8 +976,7 @@ malloc_conf_init(void) continue; \ } #define CONF_HANDLE_SSIZE_T(o, n, min, max) \ - if (sizeof(n)-1 == klen && strncmp(n, k, \ - klen) == 0) { \ + if (CONF_MATCH(n)) { \ long l; \ char *end; \ \ @@ -543,8 +987,8 @@ malloc_conf_init(void) malloc_conf_error( \ "Invalid conf value", \ k, klen, v, vlen); \ - } else if (l < (ssize_t)min || l > \ - (ssize_t)max) { \ + } else if (l < (ssize_t)(min) || l > \ + (ssize_t)(max)) { \ malloc_conf_error( \ "Out-of-range conf value", \ k, klen, v, vlen); \ @@ -553,8 +997,7 @@ malloc_conf_init(void) continue; \ } #define CONF_HANDLE_CHAR_P(o, n, d) \ - if (sizeof(n)-1 == klen && strncmp(n, k, \ - klen) == 0) { \ + if (CONF_MATCH(n)) { \ size_t cpylen = (vlen <= \ sizeof(o)-1) ? vlen : \ sizeof(o)-1; \ @@ -563,17 +1006,18 @@ malloc_conf_init(void) continue; \ } - CONF_HANDLE_BOOL(opt_abort, "abort") + CONF_HANDLE_BOOL(opt_abort, "abort", true) /* - * Chunks always require at least one header page, plus - * one data page in the absence of redzones, or three - * pages in the presence of redzones. In order to - * simplify options processing, fix the limit based on - * config_fill. + * Chunks always require at least one header page, + * as many as 2^(LG_SIZE_CLASS_GROUP+1) data pages, and + * possibly an additional page in the presence of + * redzones. In order to simplify options processing, + * use a conservative bound that accommodates all these + * constraints. */ CONF_HANDLE_SIZE_T(opt_lg_chunk, "lg_chunk", LG_PAGE + - (config_fill ? 2 : 1), (sizeof(size_t) << 3) - 1, - true) + LG_SIZE_CLASS_GROUP + (config_fill ? 2 : 1), + (sizeof(size_t) << 3) - 1, true) if (strncmp("dss", k, klen) == 0) { int i; bool match = false; @@ -592,7 +1036,7 @@ malloc_conf_init(void) } } } - if (match == false) { + if (!match) { malloc_conf_error("Invalid conf value", k, klen, v, vlen); } @@ -602,47 +1046,87 @@ malloc_conf_init(void) 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") + CONF_HANDLE_BOOL(opt_stats_print, "stats_print", true) if (config_fill) { - CONF_HANDLE_BOOL(opt_junk, "junk") + if (CONF_MATCH("junk")) { + if (CONF_MATCH_VALUE("true")) { + opt_junk = "true"; + opt_junk_alloc = opt_junk_free = + true; + } else if (CONF_MATCH_VALUE("false")) { + opt_junk = "false"; + opt_junk_alloc = opt_junk_free = + false; + } else if (CONF_MATCH_VALUE("alloc")) { + opt_junk = "alloc"; + opt_junk_alloc = true; + opt_junk_free = false; + } else if (CONF_MATCH_VALUE("free")) { + opt_junk = "free"; + opt_junk_alloc = false; + opt_junk_free = true; + } else { + malloc_conf_error( + "Invalid conf value", k, + klen, v, vlen); + } + continue; + } CONF_HANDLE_SIZE_T(opt_quarantine, "quarantine", 0, SIZE_T_MAX, false) - CONF_HANDLE_BOOL(opt_redzone, "redzone") - CONF_HANDLE_BOOL(opt_zero, "zero") + CONF_HANDLE_BOOL(opt_redzone, "redzone", true) + CONF_HANDLE_BOOL(opt_zero, "zero", true) } if (config_utrace) { - CONF_HANDLE_BOOL(opt_utrace, "utrace") - } - if (config_valgrind) { - CONF_HANDLE_BOOL(opt_valgrind, "valgrind") + CONF_HANDLE_BOOL(opt_utrace, "utrace", true) } if (config_xmalloc) { - CONF_HANDLE_BOOL(opt_xmalloc, "xmalloc") + CONF_HANDLE_BOOL(opt_xmalloc, "xmalloc", true) } if (config_tcache) { - CONF_HANDLE_BOOL(opt_tcache, "tcache") + CONF_HANDLE_BOOL(opt_tcache, "tcache", + !config_valgrind || !in_valgrind) + if (CONF_MATCH("tcache")) { + assert(config_valgrind && in_valgrind); + if (opt_tcache) { + opt_tcache = false; + malloc_conf_error( + "tcache cannot be enabled " + "while running inside Valgrind", + k, klen, v, vlen); + } + continue; + } CONF_HANDLE_SSIZE_T(opt_lg_tcache_max, "lg_tcache_max", -1, (sizeof(size_t) << 3) - 1) } if (config_prof) { - CONF_HANDLE_BOOL(opt_prof, "prof") + CONF_HANDLE_BOOL(opt_prof, "prof", true) CONF_HANDLE_CHAR_P(opt_prof_prefix, "prof_prefix", "jeprof") - CONF_HANDLE_BOOL(opt_prof_active, "prof_active") - CONF_HANDLE_SSIZE_T(opt_lg_prof_sample, + CONF_HANDLE_BOOL(opt_prof_active, "prof_active", + true) + CONF_HANDLE_BOOL(opt_prof_thread_active_init, + "prof_thread_active_init", true) + CONF_HANDLE_SIZE_T(opt_lg_prof_sample, "lg_prof_sample", 0, - (sizeof(uint64_t) << 3) - 1) - CONF_HANDLE_BOOL(opt_prof_accum, "prof_accum") + (sizeof(uint64_t) << 3) - 1, true) + CONF_HANDLE_BOOL(opt_prof_accum, "prof_accum", + true) CONF_HANDLE_SSIZE_T(opt_lg_prof_interval, "lg_prof_interval", -1, (sizeof(uint64_t) << 3) - 1) - CONF_HANDLE_BOOL(opt_prof_gdump, "prof_gdump") - CONF_HANDLE_BOOL(opt_prof_final, "prof_final") - CONF_HANDLE_BOOL(opt_prof_leak, "prof_leak") + CONF_HANDLE_BOOL(opt_prof_gdump, "prof_gdump", + true) + CONF_HANDLE_BOOL(opt_prof_final, "prof_final", + true) + CONF_HANDLE_BOOL(opt_prof_leak, "prof_leak", + true) } malloc_conf_error("Invalid conf pair", k, klen, v, vlen); +#undef CONF_MATCH #undef CONF_HANDLE_BOOL #undef CONF_HANDLE_SIZE_T #undef CONF_HANDLE_SSIZE_T @@ -651,41 +1135,44 @@ malloc_conf_init(void) } } +/* init_lock must be held. */ static bool -malloc_init_hard(void) +malloc_init_hard_needed(void) { - arena_t *init_arenas[1]; - malloc_mutex_lock(&init_lock); - if (malloc_initialized || IS_INITIALIZER) { + if (malloc_initialized() || (IS_INITIALIZER && malloc_init_state == + malloc_init_recursible)) { /* * Another thread initialized the allocator before this one * acquired init_lock, or this thread is the initializing * thread, and it is recursively allocating. */ - malloc_mutex_unlock(&init_lock); return (false); } #ifdef JEMALLOC_THREADED_INIT - if (malloc_initializer != NO_INITIALIZER && IS_INITIALIZER == false) { + if (malloc_initializer != NO_INITIALIZER && !IS_INITIALIZER) { /* Busy-wait until the initializing thread completes. */ do { malloc_mutex_unlock(&init_lock); CPU_SPINWAIT; malloc_mutex_lock(&init_lock); - } while (malloc_initialized == false); - malloc_mutex_unlock(&init_lock); + } while (!malloc_initialized()); return (false); } #endif + return (true); +} + +/* init_lock must be held. */ +static bool +malloc_init_hard_a0_locked(void) +{ + malloc_initializer = INITIALIZER; - malloc_tsd_boot(); if (config_prof) prof_boot0(); - malloc_conf_init(); - if (opt_stats_print) { /* Print statistics at exit. */ if (atexit(stats_print_atexit) != 0) { @@ -694,94 +1181,64 @@ malloc_init_hard(void) abort(); } } - - if (base_boot()) { - malloc_mutex_unlock(&init_lock); + if (base_boot()) return (true); - } - - if (chunk_boot()) { - malloc_mutex_unlock(&init_lock); + if (chunk_boot()) return (true); - } - - if (ctl_boot()) { - malloc_mutex_unlock(&init_lock); + if (ctl_boot()) return (true); - } - if (config_prof) prof_boot1(); - - arena_boot(); - - if (config_tcache && tcache_boot0()) { - malloc_mutex_unlock(&init_lock); + if (arena_boot()) return (true); - } - - if (huge_boot()) { - malloc_mutex_unlock(&init_lock); + if (config_tcache && tcache_boot()) return (true); - } - - if (malloc_mutex_init(&arenas_lock)) { - malloc_mutex_unlock(&init_lock); + if (malloc_mutex_init(&arenas_lock)) return (true); - } - /* * Create enough scaffolding to allow recursive allocation in * malloc_ncpus(). */ narenas_total = narenas_auto = 1; - arenas = init_arenas; + arenas = &a0; memset(arenas, 0, sizeof(arena_t *) * narenas_auto); - /* * Initialize one arena here. The rest are lazily created in - * choose_arena_hard(). + * arena_choose_hard(). */ - arenas_extend(0); - if (arenas[0] == NULL) { - malloc_mutex_unlock(&init_lock); + if (arena_init(0) == NULL) return (true); - } + malloc_init_state = malloc_init_a0_initialized; + return (false); +} - /* Initialize allocation counters before any allocations can occur. */ - if (config_stats && thread_allocated_tsd_boot()) { - malloc_mutex_unlock(&init_lock); - return (true); - } - - if (arenas_tsd_boot()) { - malloc_mutex_unlock(&init_lock); - return (true); - } - - if (config_tcache && tcache_boot1()) { - malloc_mutex_unlock(&init_lock); - return (true); - } - - if (config_fill && quarantine_boot()) { - malloc_mutex_unlock(&init_lock); - return (true); - } - - if (config_prof && prof_boot2()) { - malloc_mutex_unlock(&init_lock); - return (true); - } +static bool +malloc_init_hard_a0(void) +{ + bool ret; + malloc_mutex_lock(&init_lock); + ret = malloc_init_hard_a0_locked(); + malloc_mutex_unlock(&init_lock); + return (ret); +} + +/* + * Initialize data structures which may trigger recursive allocation. + * + * init_lock must be held. + */ +static void +malloc_init_hard_recursible(void) +{ + + malloc_init_state = malloc_init_recursible; malloc_mutex_unlock(&init_lock); - /**********************************************************************/ - /* Recursive allocation may follow. */ ncpus = malloc_ncpus(); #if (!defined(JEMALLOC_MUTEX_INIT_CB) && !defined(JEMALLOC_ZONE) \ - && !defined(_WIN32)) + && !defined(_WIN32) && !defined(__native_client__)) /* LinuxThreads's pthread_atfork() allocates. */ if (pthread_atfork(jemalloc_prefork, jemalloc_postfork_parent, jemalloc_postfork_child) != 0) { @@ -790,15 +1247,16 @@ malloc_init_hard(void) abort(); } #endif - - /* Done recursively allocating. */ - /**********************************************************************/ malloc_mutex_lock(&init_lock); +} - if (mutex_boot()) { - malloc_mutex_unlock(&init_lock); +/* init_lock must be held. */ +static bool +malloc_init_hard_finish(void) +{ + + if (mutex_boot()) return (true); - } if (opt_narenas == 0) { /* @@ -825,21 +1283,53 @@ malloc_init_hard(void) /* Allocate and initialize arenas. */ arenas = (arena_t **)base_alloc(sizeof(arena_t *) * narenas_total); - if (arenas == NULL) { - malloc_mutex_unlock(&init_lock); + if (arenas == NULL) return (true); - } /* * 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_total); /* Copy the pointer to the one arena that was already initialized. */ - arenas[0] = init_arenas[0]; + arenas[0] = a0; + + malloc_init_state = malloc_init_initialized; + return (false); +} + +static bool +malloc_init_hard(void) +{ + + malloc_mutex_lock(&init_lock); + if (!malloc_init_hard_needed()) { + malloc_mutex_unlock(&init_lock); + return (false); + } + + if (malloc_init_state != malloc_init_a0_initialized && + malloc_init_hard_a0_locked()) { + malloc_mutex_unlock(&init_lock); + return (true); + } + if (malloc_tsd_boot0()) { + malloc_mutex_unlock(&init_lock); + return (true); + } + if (config_prof && prof_boot2()) { + malloc_mutex_unlock(&init_lock); + return (true); + } + + malloc_init_hard_recursible(); + + if (malloc_init_hard_finish()) { + malloc_mutex_unlock(&init_lock); + return (true); + } - malloc_initialized = true; malloc_mutex_unlock(&init_lock); - + malloc_tsd_boot1(); return (false); } @@ -852,98 +1342,87 @@ malloc_init_hard(void) */ static void * -imalloc_prof_sample(size_t usize, prof_thr_cnt_t *cnt) +imalloc_prof_sample(tsd_t *tsd, size_t usize, prof_tctx_t *tctx) { void *p; - if (cnt == NULL) + if (tctx == NULL) return (NULL); - if (prof_promote && usize <= SMALL_MAXCLASS) { - p = imalloc(SMALL_MAXCLASS+1); + if (usize <= SMALL_MAXCLASS) { + p = imalloc(tsd, LARGE_MINCLASS); if (p == NULL) return (NULL); arena_prof_promoted(p, usize); } else - p = imalloc(usize); + p = imalloc(tsd, usize); return (p); } JEMALLOC_ALWAYS_INLINE_C void * -imalloc_prof(size_t usize, prof_thr_cnt_t *cnt) +imalloc_prof(tsd_t *tsd, size_t usize) { void *p; + prof_tctx_t *tctx; - if ((uintptr_t)cnt != (uintptr_t)1U) - p = imalloc_prof_sample(usize, cnt); + tctx = prof_alloc_prep(tsd, usize, true); + if (unlikely((uintptr_t)tctx != (uintptr_t)1U)) + p = imalloc_prof_sample(tsd, usize, tctx); else - p = imalloc(usize); - if (p == NULL) + p = imalloc(tsd, usize); + if (unlikely(p == NULL)) { + prof_alloc_rollback(tsd, tctx, true); return (NULL); - prof_malloc(p, usize, cnt); + } + prof_malloc(p, usize, tctx); return (p); } -/* - * MALLOC_BODY() is a macro rather than a function because its contents are in - * the fast path, but inlining would cause reliability issues when determining - * how many frames to discard from heap profiling backtraces. - */ -#define MALLOC_BODY(ret, size, usize) do { \ - if (malloc_init()) \ - ret = NULL; \ - else { \ - if (config_prof && opt_prof) { \ - prof_thr_cnt_t *cnt; \ - \ - usize = s2u(size); \ - /* \ - * Call PROF_ALLOC_PREP() here rather than in \ - * imalloc_prof() so that imalloc_prof() can be \ - * inlined without introducing uncertainty \ - * about the number of backtrace frames to \ - * ignore. imalloc_prof() is in the fast path \ - * when heap profiling is enabled, so inlining \ - * is critical to performance. (For \ - * consistency all callers of PROF_ALLOC_PREP() \ - * are structured similarly, even though e.g. \ - * realloc() isn't called enough for inlining \ - * to be critical.) \ - */ \ - PROF_ALLOC_PREP(1, usize, cnt); \ - ret = imalloc_prof(usize, cnt); \ - } else { \ - if (config_stats || (config_valgrind && \ - opt_valgrind)) \ - usize = s2u(size); \ - ret = imalloc(size); \ - } \ - } \ -} while (0) +JEMALLOC_ALWAYS_INLINE_C void * +imalloc_body(size_t size, tsd_t **tsd, size_t *usize) +{ -void * + if (unlikely(malloc_init())) + return (NULL); + *tsd = tsd_fetch(); + + if (config_prof && opt_prof) { + *usize = s2u(size); + if (unlikely(*usize == 0)) + return (NULL); + return (imalloc_prof(*tsd, *usize)); + } + + if (config_stats || (config_valgrind && unlikely(in_valgrind))) + *usize = s2u(size); + return (imalloc(*tsd, size)); +} + +JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN +void JEMALLOC_NOTHROW * +JEMALLOC_ATTR(malloc) JEMALLOC_ALLOC_SIZE(1) je_malloc(size_t size) { void *ret; + tsd_t *tsd; size_t usize JEMALLOC_CC_SILENCE_INIT(0); if (size == 0) size = 1; - MALLOC_BODY(ret, size, usize); - - if (ret == NULL) { - if (config_xmalloc && opt_xmalloc) { + ret = imalloc_body(size, &tsd, &usize); + if (unlikely(ret == NULL)) { + if (config_xmalloc && unlikely(opt_xmalloc)) { malloc_write(": Error in malloc(): " "out of memory\n"); abort(); } set_errno(ENOMEM); } - if (config_stats && ret != NULL) { + if (config_stats && likely(ret != NULL)) { assert(usize == isalloc(ret, config_prof)); - thread_allocated_tsd_get()->allocated += usize; + *tsd_thread_allocatedp_get(tsd) += usize; } UTRACE(0, size, ret); JEMALLOC_VALGRIND_MALLOC(ret != NULL, ret, usize, false); @@ -951,107 +1430,103 @@ je_malloc(size_t size) } static void * -imemalign_prof_sample(size_t alignment, size_t usize, prof_thr_cnt_t *cnt) +imemalign_prof_sample(tsd_t *tsd, size_t alignment, size_t usize, + prof_tctx_t *tctx) { void *p; - if (cnt == NULL) + if (tctx == NULL) return (NULL); - if (prof_promote && usize <= SMALL_MAXCLASS) { - assert(sa2u(SMALL_MAXCLASS+1, alignment) != 0); - p = ipalloc(sa2u(SMALL_MAXCLASS+1, alignment), alignment, - false); + if (usize <= SMALL_MAXCLASS) { + assert(sa2u(LARGE_MINCLASS, alignment) == LARGE_MINCLASS); + p = ipalloc(tsd, LARGE_MINCLASS, alignment, false); if (p == NULL) return (NULL); arena_prof_promoted(p, usize); } else - p = ipalloc(usize, alignment, false); + p = ipalloc(tsd, usize, alignment, false); return (p); } JEMALLOC_ALWAYS_INLINE_C void * -imemalign_prof(size_t alignment, size_t usize, prof_thr_cnt_t *cnt) +imemalign_prof(tsd_t *tsd, size_t alignment, size_t usize) { void *p; + prof_tctx_t *tctx; - if ((uintptr_t)cnt != (uintptr_t)1U) - p = imemalign_prof_sample(alignment, usize, cnt); + tctx = prof_alloc_prep(tsd, usize, true); + if (unlikely((uintptr_t)tctx != (uintptr_t)1U)) + p = imemalign_prof_sample(tsd, alignment, usize, tctx); else - p = ipalloc(usize, alignment, false); - if (p == NULL) + p = ipalloc(tsd, usize, alignment, false); + if (unlikely(p == NULL)) { + prof_alloc_rollback(tsd, tctx, true); return (NULL); - prof_malloc(p, usize, cnt); + } + prof_malloc(p, usize, tctx); return (p); } JEMALLOC_ATTR(nonnull(1)) -#ifdef JEMALLOC_PROF -/* - * Avoid any uncertainty as to how many backtrace frames to ignore in - * PROF_ALLOC_PREP(). - */ -JEMALLOC_NOINLINE -#endif static int imemalign(void **memptr, size_t alignment, size_t size, size_t min_alignment) { int ret; + tsd_t *tsd; size_t usize; void *result; assert(min_alignment != 0); - if (malloc_init()) { + if (unlikely(malloc_init())) { result = NULL; goto label_oom; - } else { - if (size == 0) - size = 1; - - /* Make sure that alignment is a large enough power of 2. */ - if (((alignment - 1) & alignment) != 0 - || (alignment < min_alignment)) { - if (config_xmalloc && opt_xmalloc) { - malloc_write(": Error allocating " - "aligned memory: invalid alignment\n"); - abort(); - } - result = NULL; - ret = EINVAL; - goto label_return; - } - - usize = sa2u(size, alignment); - if (usize == 0) { - result = NULL; - goto label_oom; - } - - if (config_prof && opt_prof) { - prof_thr_cnt_t *cnt; - - PROF_ALLOC_PREP(2, usize, cnt); - result = imemalign_prof(alignment, usize, cnt); - } else - result = ipalloc(usize, alignment, false); - if (result == NULL) - goto label_oom; } + tsd = tsd_fetch(); + if (size == 0) + size = 1; + + /* Make sure that alignment is a large enough power of 2. */ + if (unlikely(((alignment - 1) & alignment) != 0 + || (alignment < min_alignment))) { + if (config_xmalloc && unlikely(opt_xmalloc)) { + malloc_write(": Error allocating " + "aligned memory: invalid alignment\n"); + abort(); + } + result = NULL; + ret = EINVAL; + goto label_return; + } + + usize = sa2u(size, alignment); + if (unlikely(usize == 0)) { + result = NULL; + goto label_oom; + } + + if (config_prof && opt_prof) + result = imemalign_prof(tsd, alignment, usize); + else + result = ipalloc(tsd, usize, alignment, false); + if (unlikely(result == NULL)) + goto label_oom; + assert(((uintptr_t)result & (alignment - 1)) == ZU(0)); *memptr = result; ret = 0; label_return: - if (config_stats && result != NULL) { + if (config_stats && likely(result != NULL)) { assert(usize == isalloc(result, config_prof)); - thread_allocated_tsd_get()->allocated += usize; + *tsd_thread_allocatedp_get(tsd) += usize; } UTRACE(0, size, result); return (ret); label_oom: assert(result == NULL); - if (config_xmalloc && opt_xmalloc) { + if (config_xmalloc && unlikely(opt_xmalloc)) { malloc_write(": Error allocating aligned memory: " "out of memory\n"); abort(); @@ -1060,7 +1535,8 @@ imemalign(void **memptr, size_t alignment, size_t size, size_t min_alignment) goto label_return; } -int +JEMALLOC_EXPORT int JEMALLOC_NOTHROW +JEMALLOC_ATTR(nonnull(1)) je_posix_memalign(void **memptr, size_t alignment, size_t size) { int ret = imemalign(memptr, alignment, size, sizeof(void *)); @@ -1069,13 +1545,15 @@ je_posix_memalign(void **memptr, size_t alignment, size_t size) return (ret); } -void * +JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN +void JEMALLOC_NOTHROW * +JEMALLOC_ATTR(malloc) JEMALLOC_ALLOC_SIZE(2) je_aligned_alloc(size_t alignment, size_t size) { void *ret; int err; - if ((err = imemalign(&ret, alignment, size, 1)) != 0) { + if (unlikely((err = imemalign(&ret, alignment, size, 1)) != 0)) { ret = NULL; set_errno(err); } @@ -1085,54 +1563,62 @@ je_aligned_alloc(size_t alignment, size_t size) } static void * -icalloc_prof_sample(size_t usize, prof_thr_cnt_t *cnt) +icalloc_prof_sample(tsd_t *tsd, size_t usize, prof_tctx_t *tctx) { void *p; - if (cnt == NULL) + if (tctx == NULL) return (NULL); - if (prof_promote && usize <= SMALL_MAXCLASS) { - p = icalloc(SMALL_MAXCLASS+1); + if (usize <= SMALL_MAXCLASS) { + p = icalloc(tsd, LARGE_MINCLASS); if (p == NULL) return (NULL); arena_prof_promoted(p, usize); } else - p = icalloc(usize); + p = icalloc(tsd, usize); return (p); } JEMALLOC_ALWAYS_INLINE_C void * -icalloc_prof(size_t usize, prof_thr_cnt_t *cnt) +icalloc_prof(tsd_t *tsd, size_t usize) { void *p; + prof_tctx_t *tctx; - if ((uintptr_t)cnt != (uintptr_t)1U) - p = icalloc_prof_sample(usize, cnt); + tctx = prof_alloc_prep(tsd, usize, true); + if (unlikely((uintptr_t)tctx != (uintptr_t)1U)) + p = icalloc_prof_sample(tsd, usize, tctx); else - p = icalloc(usize); - if (p == NULL) + p = icalloc(tsd, usize); + if (unlikely(p == NULL)) { + prof_alloc_rollback(tsd, tctx, true); return (NULL); - prof_malloc(p, usize, cnt); + } + prof_malloc(p, usize, tctx); return (p); } -void * +JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN +void JEMALLOC_NOTHROW * +JEMALLOC_ATTR(malloc) JEMALLOC_ALLOC_SIZE2(1, 2) je_calloc(size_t num, size_t size) { void *ret; + tsd_t *tsd; size_t num_size; size_t usize JEMALLOC_CC_SILENCE_INIT(0); - if (malloc_init()) { + if (unlikely(malloc_init())) { num_size = 0; ret = NULL; goto label_return; } + tsd = tsd_fetch(); num_size = num * size; - if (num_size == 0) { + if (unlikely(num_size == 0)) { if (num == 0 || size == 0) num_size = 1; else { @@ -1144,37 +1630,38 @@ je_calloc(size_t num, size_t size) * overflow during multiplication if neither operand uses any of the * most significant half of the bits in a size_t. */ - } else if (((num | size) & (SIZE_T_MAX << (sizeof(size_t) << 2))) - && (num_size / size != num)) { + } else if (unlikely(((num | size) & (SIZE_T_MAX << (sizeof(size_t) << + 2))) && (num_size / size != num))) { /* size_t overflow. */ ret = NULL; goto label_return; } if (config_prof && opt_prof) { - prof_thr_cnt_t *cnt; - usize = s2u(num_size); - PROF_ALLOC_PREP(1, usize, cnt); - ret = icalloc_prof(usize, cnt); + if (unlikely(usize == 0)) { + ret = NULL; + goto label_return; + } + ret = icalloc_prof(tsd, usize); } else { - if (config_stats || (config_valgrind && opt_valgrind)) + if (config_stats || (config_valgrind && unlikely(in_valgrind))) usize = s2u(num_size); - ret = icalloc(num_size); + ret = icalloc(tsd, num_size); } label_return: - if (ret == NULL) { - if (config_xmalloc && opt_xmalloc) { + if (unlikely(ret == NULL)) { + if (config_xmalloc && unlikely(opt_xmalloc)) { malloc_write(": Error in calloc(): out of " "memory\n"); abort(); } set_errno(ENOMEM); } - if (config_stats && ret != NULL) { + if (config_stats && likely(ret != NULL)) { assert(usize == isalloc(ret, config_prof)); - thread_allocated_tsd_get()->allocated += usize; + *tsd_thread_allocatedp_get(tsd) += usize; } UTRACE(0, num_size, ret); JEMALLOC_VALGRIND_MALLOC(ret != NULL, ret, usize, true); @@ -1182,135 +1669,157 @@ je_calloc(size_t num, size_t size) } static void * -irealloc_prof_sample(void *oldptr, size_t usize, prof_thr_cnt_t *cnt) +irealloc_prof_sample(tsd_t *tsd, void *oldptr, size_t old_usize, size_t usize, + prof_tctx_t *tctx) { void *p; - if (cnt == NULL) + if (tctx == NULL) return (NULL); - if (prof_promote && usize <= SMALL_MAXCLASS) { - p = iralloc(oldptr, SMALL_MAXCLASS+1, 0, 0, false); + if (usize <= SMALL_MAXCLASS) { + p = iralloc(tsd, oldptr, old_usize, LARGE_MINCLASS, 0, false); if (p == NULL) return (NULL); arena_prof_promoted(p, usize); } else - p = iralloc(oldptr, usize, 0, 0, false); + p = iralloc(tsd, oldptr, old_usize, usize, 0, false); return (p); } JEMALLOC_ALWAYS_INLINE_C void * -irealloc_prof(void *oldptr, size_t old_usize, size_t usize, prof_thr_cnt_t *cnt) +irealloc_prof(tsd_t *tsd, void *oldptr, size_t old_usize, size_t usize) { void *p; - prof_ctx_t *old_ctx; + prof_tctx_t *old_tctx, *tctx; - old_ctx = prof_ctx_get(oldptr); - if ((uintptr_t)cnt != (uintptr_t)1U) - p = irealloc_prof_sample(oldptr, usize, cnt); + old_tctx = prof_tctx_get(oldptr); + tctx = prof_alloc_prep(tsd, usize, true); + if (unlikely((uintptr_t)tctx != (uintptr_t)1U)) + p = irealloc_prof_sample(tsd, oldptr, old_usize, usize, tctx); else - p = iralloc(oldptr, usize, 0, 0, false); + p = iralloc(tsd, oldptr, old_usize, usize, 0, false); if (p == NULL) return (NULL); - prof_realloc(p, usize, cnt, old_usize, old_ctx); + prof_realloc(tsd, p, usize, tctx, true, old_usize, old_tctx); return (p); } JEMALLOC_INLINE_C void -ifree(void *ptr) +ifree(tsd_t *tsd, void *ptr, tcache_t *tcache) { size_t usize; UNUSED size_t rzsize JEMALLOC_CC_SILENCE_INIT(0); assert(ptr != NULL); - assert(malloc_initialized || IS_INITIALIZER); + assert(malloc_initialized() || IS_INITIALIZER); if (config_prof && opt_prof) { usize = isalloc(ptr, config_prof); - prof_free(ptr, usize); + prof_free(tsd, ptr, usize); } else if (config_stats || config_valgrind) usize = isalloc(ptr, config_prof); if (config_stats) - thread_allocated_tsd_get()->deallocated += usize; - if (config_valgrind && opt_valgrind) + *tsd_thread_deallocatedp_get(tsd) += usize; + if (config_valgrind && unlikely(in_valgrind)) rzsize = p2rz(ptr); - iqalloc(ptr); + iqalloc(tsd, ptr, tcache); JEMALLOC_VALGRIND_FREE(ptr, rzsize); } -void * +JEMALLOC_INLINE_C void +isfree(tsd_t *tsd, void *ptr, size_t usize, tcache_t *tcache) +{ + UNUSED size_t rzsize JEMALLOC_CC_SILENCE_INIT(0); + + assert(ptr != NULL); + assert(malloc_initialized() || IS_INITIALIZER); + + if (config_prof && opt_prof) + prof_free(tsd, ptr, usize); + if (config_stats) + *tsd_thread_deallocatedp_get(tsd) += usize; + if (config_valgrind && unlikely(in_valgrind)) + rzsize = p2rz(ptr); + isqalloc(tsd, ptr, usize, tcache); + JEMALLOC_VALGRIND_FREE(ptr, rzsize); +} + +JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN +void JEMALLOC_NOTHROW * +JEMALLOC_ALLOC_SIZE(2) je_realloc(void *ptr, size_t size) { void *ret; + tsd_t *tsd JEMALLOC_CC_SILENCE_INIT(NULL); size_t usize JEMALLOC_CC_SILENCE_INIT(0); size_t old_usize = 0; UNUSED size_t old_rzsize JEMALLOC_CC_SILENCE_INIT(0); - if (size == 0) { + if (unlikely(size == 0)) { if (ptr != NULL) { /* realloc(ptr, 0) is equivalent to free(ptr). */ UTRACE(ptr, 0, 0); - ifree(ptr); + tsd = tsd_fetch(); + ifree(tsd, ptr, tcache_get(tsd, false)); return (NULL); } size = 1; } - if (ptr != NULL) { - assert(malloc_initialized || IS_INITIALIZER); + if (likely(ptr != NULL)) { + assert(malloc_initialized() || IS_INITIALIZER); malloc_thread_init(); + tsd = tsd_fetch(); - if ((config_prof && opt_prof) || config_stats || - (config_valgrind && opt_valgrind)) - old_usize = isalloc(ptr, config_prof); - if (config_valgrind && opt_valgrind) + old_usize = isalloc(ptr, config_prof); + if (config_valgrind && unlikely(in_valgrind)) old_rzsize = config_prof ? p2rz(ptr) : u2rz(old_usize); if (config_prof && opt_prof) { - prof_thr_cnt_t *cnt; - usize = s2u(size); - PROF_ALLOC_PREP(1, usize, cnt); - ret = irealloc_prof(ptr, old_usize, usize, cnt); + ret = unlikely(usize == 0) ? NULL : irealloc_prof(tsd, + ptr, old_usize, usize); } else { - if (config_stats || (config_valgrind && opt_valgrind)) + if (config_stats || (config_valgrind && + unlikely(in_valgrind))) usize = s2u(size); - ret = iralloc(ptr, size, 0, 0, false); + ret = iralloc(tsd, ptr, old_usize, size, 0, false); } } else { /* realloc(NULL, size) is equivalent to malloc(size). */ - MALLOC_BODY(ret, size, usize); + ret = imalloc_body(size, &tsd, &usize); } - if (ret == NULL) { - if (config_xmalloc && opt_xmalloc) { + if (unlikely(ret == NULL)) { + if (config_xmalloc && unlikely(opt_xmalloc)) { malloc_write(": Error in realloc(): " "out of memory\n"); abort(); } set_errno(ENOMEM); } - if (config_stats && ret != NULL) { - thread_allocated_t *ta; + if (config_stats && likely(ret != NULL)) { assert(usize == isalloc(ret, config_prof)); - ta = thread_allocated_tsd_get(); - ta->allocated += usize; - ta->deallocated += old_usize; + *tsd_thread_allocatedp_get(tsd) += usize; + *tsd_thread_deallocatedp_get(tsd) += old_usize; } UTRACE(ptr, size, ret); - JEMALLOC_VALGRIND_REALLOC(ret, usize, ptr, old_usize, old_rzsize, - false); + JEMALLOC_VALGRIND_REALLOC(true, ret, usize, true, ptr, old_usize, + old_rzsize, true, false); return (ret); } -void +JEMALLOC_EXPORT void JEMALLOC_NOTHROW je_free(void *ptr) { UTRACE(ptr, 0, 0); - if (ptr != NULL) - ifree(ptr); + if (likely(ptr != NULL)) { + tsd_t *tsd = tsd_fetch(); + ifree(tsd, ptr, tcache_get(tsd, false)); + } } /* @@ -1322,22 +1831,28 @@ je_free(void *ptr) */ #ifdef JEMALLOC_OVERRIDE_MEMALIGN -void * +JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN +void JEMALLOC_NOTHROW * +JEMALLOC_ATTR(malloc) je_memalign(size_t alignment, size_t size) { void *ret JEMALLOC_CC_SILENCE_INIT(NULL); - imemalign(&ret, alignment, size, 1); + if (unlikely(imemalign(&ret, alignment, size, 1) != 0)) + ret = NULL; JEMALLOC_VALGRIND_MALLOC(ret != NULL, ret, size, false); return (ret); } #endif #ifdef JEMALLOC_OVERRIDE_VALLOC -void * +JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN +void JEMALLOC_NOTHROW * +JEMALLOC_ATTR(malloc) je_valloc(size_t size) { void *ret JEMALLOC_CC_SILENCE_INIT(NULL); - imemalign(&ret, PAGE, size, 1); + if (unlikely(imemalign(&ret, PAGE, size, 1) != 0)) + ret = NULL; JEMALLOC_VALGRIND_MALLOC(ret != NULL, ret, size, false); return (ret); } @@ -1351,7 +1866,7 @@ je_valloc(size_t size) #define is_malloc_(a) malloc_is_ ## a #define is_malloc(a) is_malloc_(a) -#if ((is_malloc(je_malloc) == 1) && defined(__GLIBC__) && !defined(__UCLIBC__)) +#if ((is_malloc(je_malloc) == 1) && defined(JEMALLOC_GLIBC_MALLOC_HOOK)) /* * glibc provides the RTLD_DEEPBIND flag for dlopen which can make it possible * to inconsistently reference libc's malloc(3)-compatible functions @@ -1361,11 +1876,13 @@ je_valloc(size_t size) * passed an extra argument for the caller return address, which will be * ignored. */ -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) = +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; +# ifdef JEMALLOC_GLIBC_MEMALIGN_HOOK +JEMALLOC_EXPORT void *(*__memalign_hook)(size_t alignment, size_t size) = je_memalign; +# endif #endif /* @@ -1376,111 +1893,185 @@ JEMALLOC_EXPORT void *(* __memalign_hook)(size_t alignment, size_t size) = * Begin non-standard functions. */ -JEMALLOC_ALWAYS_INLINE_C void * -imallocx(size_t usize, size_t alignment, bool zero, bool try_tcache, - arena_t *arena) +JEMALLOC_ALWAYS_INLINE_C bool +imallocx_flags_decode_hard(tsd_t *tsd, size_t size, int flags, size_t *usize, + size_t *alignment, bool *zero, tcache_t **tcache, arena_t **arena) { - assert(usize == ((alignment == 0) ? s2u(usize) : sa2u(usize, - alignment))); + if ((flags & MALLOCX_LG_ALIGN_MASK) == 0) { + *alignment = 0; + *usize = s2u(size); + } else { + *alignment = MALLOCX_ALIGN_GET_SPECIFIED(flags); + *usize = sa2u(size, *alignment); + } + *zero = MALLOCX_ZERO_GET(flags); + if ((flags & MALLOCX_TCACHE_MASK) != 0) { + if ((flags & MALLOCX_TCACHE_MASK) == MALLOCX_TCACHE_NONE) + *tcache = NULL; + else + *tcache = tcaches_get(tsd, MALLOCX_TCACHE_GET(flags)); + } else + *tcache = tcache_get(tsd, true); + if ((flags & MALLOCX_ARENA_MASK) != 0) { + unsigned arena_ind = MALLOCX_ARENA_GET(flags); + *arena = arena_get(tsd, arena_ind, true, true); + if (unlikely(*arena == NULL)) + return (true); + } else + *arena = NULL; + return (false); +} + +JEMALLOC_ALWAYS_INLINE_C bool +imallocx_flags_decode(tsd_t *tsd, size_t size, int flags, size_t *usize, + size_t *alignment, bool *zero, tcache_t **tcache, arena_t **arena) +{ + + if (likely(flags == 0)) { + *usize = s2u(size); + assert(*usize != 0); + *alignment = 0; + *zero = false; + *tcache = tcache_get(tsd, true); + *arena = NULL; + return (false); + } else { + return (imallocx_flags_decode_hard(tsd, size, flags, usize, + alignment, zero, tcache, arena)); + } +} + +JEMALLOC_ALWAYS_INLINE_C void * +imallocx_flags(tsd_t *tsd, size_t usize, size_t alignment, bool zero, + tcache_t *tcache, arena_t *arena) +{ if (alignment != 0) - return (ipalloct(usize, alignment, zero, try_tcache, arena)); - else if (zero) - return (icalloct(usize, try_tcache, arena)); - else - return (imalloct(usize, try_tcache, arena)); + return (ipalloct(tsd, usize, alignment, zero, tcache, arena)); + if (zero) + return (icalloct(tsd, usize, tcache, arena)); + return (imalloct(tsd, usize, tcache, arena)); +} + +JEMALLOC_ALWAYS_INLINE_C void * +imallocx_maybe_flags(tsd_t *tsd, size_t size, int flags, size_t usize, + size_t alignment, bool zero, tcache_t *tcache, arena_t *arena) +{ + + if (likely(flags == 0)) + return (imalloc(tsd, size)); + return (imallocx_flags(tsd, usize, alignment, zero, tcache, arena)); } static void * -imallocx_prof_sample(size_t usize, size_t alignment, bool zero, bool try_tcache, - arena_t *arena, prof_thr_cnt_t *cnt) +imallocx_prof_sample(tsd_t *tsd, size_t size, int flags, size_t usize, + size_t alignment, bool zero, tcache_t *tcache, arena_t *arena) { void *p; - if (cnt == NULL) - return (NULL); - if (prof_promote && usize <= SMALL_MAXCLASS) { - size_t usize_promoted = (alignment == 0) ? - s2u(SMALL_MAXCLASS+1) : sa2u(SMALL_MAXCLASS+1, alignment); - assert(usize_promoted != 0); - p = imallocx(usize_promoted, alignment, zero, try_tcache, - arena); + if (usize <= SMALL_MAXCLASS) { + assert(((alignment == 0) ? s2u(LARGE_MINCLASS) : + sa2u(LARGE_MINCLASS, alignment)) == LARGE_MINCLASS); + p = imallocx_maybe_flags(tsd, LARGE_MINCLASS, flags, + LARGE_MINCLASS, alignment, zero, tcache, arena); if (p == NULL) return (NULL); arena_prof_promoted(p, usize); - } else - p = imallocx(usize, alignment, zero, try_tcache, arena); + } else { + p = imallocx_maybe_flags(tsd, size, flags, usize, alignment, + zero, tcache, arena); + } return (p); } JEMALLOC_ALWAYS_INLINE_C void * -imallocx_prof(size_t usize, size_t alignment, bool zero, bool try_tcache, - arena_t *arena, prof_thr_cnt_t *cnt) +imallocx_prof(tsd_t *tsd, size_t size, int flags, size_t *usize) { void *p; + size_t alignment; + bool zero; + tcache_t *tcache; + arena_t *arena; + prof_tctx_t *tctx; - if ((uintptr_t)cnt != (uintptr_t)1U) { - p = imallocx_prof_sample(usize, alignment, zero, try_tcache, - arena, cnt); - } else - p = imallocx(usize, alignment, zero, try_tcache, arena); - if (p == NULL) + if (unlikely(imallocx_flags_decode(tsd, size, flags, usize, &alignment, + &zero, &tcache, &arena))) return (NULL); - prof_malloc(p, usize, cnt); + tctx = prof_alloc_prep(tsd, *usize, true); + if (likely((uintptr_t)tctx == (uintptr_t)1U)) { + p = imallocx_maybe_flags(tsd, size, flags, *usize, alignment, + zero, tcache, arena); + } else if ((uintptr_t)tctx > (uintptr_t)1U) { + p = imallocx_prof_sample(tsd, size, flags, *usize, alignment, + zero, tcache, arena); + } else + p = NULL; + if (unlikely(p == NULL)) { + prof_alloc_rollback(tsd, tctx, true); + return (NULL); + } + prof_malloc(p, *usize, tctx); + assert(alignment == 0 || ((uintptr_t)p & (alignment - 1)) == ZU(0)); return (p); } -void * -je_mallocx(size_t size, int flags) +JEMALLOC_ALWAYS_INLINE_C void * +imallocx_no_prof(tsd_t *tsd, size_t size, int flags, size_t *usize) { void *p; - size_t usize; - size_t alignment = (ZU(1) << (flags & MALLOCX_LG_ALIGN_MASK) - & (SIZE_T_MAX-1)); - bool zero = flags & MALLOCX_ZERO; - unsigned arena_ind = ((unsigned)(flags >> 8)) - 1; + size_t alignment; + bool zero; + tcache_t *tcache; arena_t *arena; - bool try_tcache; + + if (likely(flags == 0)) { + if (config_stats || (config_valgrind && unlikely(in_valgrind))) + *usize = s2u(size); + return (imalloc(tsd, size)); + } + + if (unlikely(imallocx_flags_decode_hard(tsd, size, flags, usize, + &alignment, &zero, &tcache, &arena))) + return (NULL); + p = imallocx_flags(tsd, *usize, alignment, zero, tcache, arena); + assert(alignment == 0 || ((uintptr_t)p & (alignment - 1)) == ZU(0)); + return (p); +} + +JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN +void JEMALLOC_NOTHROW * +JEMALLOC_ATTR(malloc) JEMALLOC_ALLOC_SIZE(1) +je_mallocx(size_t size, int flags) +{ + tsd_t *tsd; + void *p; + size_t usize; assert(size != 0); - if (malloc_init()) + if (unlikely(malloc_init())) goto label_oom; + tsd = tsd_fetch(); - 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); - assert(usize != 0); - - if (config_prof && opt_prof) { - prof_thr_cnt_t *cnt; - - PROF_ALLOC_PREP(1, usize, cnt); - p = imallocx_prof(usize, alignment, zero, try_tcache, arena, - cnt); - } else - p = imallocx(usize, alignment, zero, try_tcache, arena); - if (p == NULL) + if (config_prof && opt_prof) + p = imallocx_prof(tsd, size, flags, &usize); + else + p = imallocx_no_prof(tsd, size, flags, &usize); + if (unlikely(p == NULL)) goto label_oom; if (config_stats) { assert(usize == isalloc(p, config_prof)); - thread_allocated_tsd_get()->allocated += usize; + *tsd_thread_allocatedp_get(tsd) += usize; } UTRACE(0, size, p); - JEMALLOC_VALGRIND_MALLOC(true, p, usize, zero); + JEMALLOC_VALGRIND_MALLOC(true, p, usize, MALLOCX_ZERO_GET(flags)); return (p); label_oom: - if (config_xmalloc && opt_xmalloc) { + if (config_xmalloc && unlikely(opt_xmalloc)) { malloc_write(": Error in mallocx(): out of memory\n"); abort(); } @@ -1489,47 +2080,49 @@ je_mallocx(size_t size, int flags) } static void * -irallocx_prof_sample(void *oldptr, size_t size, size_t alignment, size_t usize, - bool zero, bool try_tcache_alloc, bool try_tcache_dalloc, arena_t *arena, - prof_thr_cnt_t *cnt) +irallocx_prof_sample(tsd_t *tsd, void *oldptr, size_t old_usize, size_t size, + size_t alignment, size_t usize, bool zero, tcache_t *tcache, arena_t *arena, + prof_tctx_t *tctx) { void *p; - if (cnt == NULL) + if (tctx == NULL) return (NULL); - if (prof_promote && usize <= SMALL_MAXCLASS) { - p = iralloct(oldptr, SMALL_MAXCLASS+1, (SMALL_MAXCLASS+1 >= - size) ? 0 : size - (SMALL_MAXCLASS+1), alignment, zero, - try_tcache_alloc, try_tcache_dalloc, arena); + if (usize <= SMALL_MAXCLASS) { + p = iralloct(tsd, oldptr, old_usize, LARGE_MINCLASS, alignment, + zero, tcache, arena); if (p == NULL) return (NULL); arena_prof_promoted(p, usize); } else { - p = iralloct(oldptr, size, 0, alignment, zero, - try_tcache_alloc, try_tcache_dalloc, arena); + p = iralloct(tsd, oldptr, old_usize, size, alignment, zero, + tcache, arena); } return (p); } JEMALLOC_ALWAYS_INLINE_C void * -irallocx_prof(void *oldptr, size_t old_usize, size_t size, size_t alignment, - size_t *usize, bool zero, bool try_tcache_alloc, bool try_tcache_dalloc, - arena_t *arena, prof_thr_cnt_t *cnt) +irallocx_prof(tsd_t *tsd, void *oldptr, size_t old_usize, size_t size, + size_t alignment, size_t *usize, bool zero, tcache_t *tcache, + arena_t *arena) { void *p; - prof_ctx_t *old_ctx; + prof_tctx_t *old_tctx, *tctx; - old_ctx = prof_ctx_get(oldptr); - if ((uintptr_t)cnt != (uintptr_t)1U) - p = irallocx_prof_sample(oldptr, size, alignment, *usize, zero, - try_tcache_alloc, try_tcache_dalloc, arena, cnt); - else { - p = iralloct(oldptr, size, 0, alignment, zero, - try_tcache_alloc, try_tcache_dalloc, arena); + old_tctx = prof_tctx_get(oldptr); + tctx = prof_alloc_prep(tsd, *usize, false); + if (unlikely((uintptr_t)tctx != (uintptr_t)1U)) { + p = irallocx_prof_sample(tsd, oldptr, old_usize, size, + alignment, *usize, zero, tcache, arena, tctx); + } else { + p = iralloct(tsd, oldptr, old_usize, size, alignment, zero, + tcache, arena); } - if (p == NULL) + if (unlikely(p == NULL)) { + prof_alloc_rollback(tsd, tctx, false); return (NULL); + } if (p == oldptr && alignment != 0) { /* @@ -1542,78 +2135,79 @@ irallocx_prof(void *oldptr, size_t old_usize, size_t size, size_t alignment, */ *usize = isalloc(p, config_prof); } - prof_realloc(p, *usize, cnt, old_usize, old_ctx); + prof_realloc(tsd, p, *usize, tctx, false, old_usize, old_tctx); return (p); } -void * +JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN +void JEMALLOC_NOTHROW * +JEMALLOC_ALLOC_SIZE(2) je_rallocx(void *ptr, size_t size, int flags) { void *p; - size_t usize, old_usize; + tsd_t *tsd; + size_t usize; + size_t old_usize; UNUSED size_t old_rzsize JEMALLOC_CC_SILENCE_INIT(0); - size_t alignment = (ZU(1) << (flags & MALLOCX_LG_ALIGN_MASK) - & (SIZE_T_MAX-1)); + size_t alignment = MALLOCX_ALIGN_GET(flags); bool zero = flags & MALLOCX_ZERO; - unsigned arena_ind = ((unsigned)(flags >> 8)) - 1; - bool try_tcache_alloc, try_tcache_dalloc; arena_t *arena; + tcache_t *tcache; assert(ptr != NULL); assert(size != 0); - assert(malloc_initialized || IS_INITIALIZER); + assert(malloc_initialized() || IS_INITIALIZER); malloc_thread_init(); + tsd = tsd_fetch(); - if (arena_ind != UINT_MAX) { - arena_chunk_t *chunk; - try_tcache_alloc = false; - 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; + if (unlikely((flags & MALLOCX_ARENA_MASK) != 0)) { + unsigned arena_ind = MALLOCX_ARENA_GET(flags); + arena = arena_get(tsd, arena_ind, true, true); + if (unlikely(arena == NULL)) + goto label_oom; + } else arena = NULL; - } - if ((config_prof && opt_prof) || config_stats || - (config_valgrind && opt_valgrind)) - old_usize = isalloc(ptr, config_prof); - if (config_valgrind && opt_valgrind) + if (unlikely((flags & MALLOCX_TCACHE_MASK) != 0)) { + if ((flags & MALLOCX_TCACHE_MASK) == MALLOCX_TCACHE_NONE) + tcache = NULL; + else + tcache = tcaches_get(tsd, MALLOCX_TCACHE_GET(flags)); + } else + tcache = tcache_get(tsd, true); + + old_usize = isalloc(ptr, config_prof); + if (config_valgrind && unlikely(in_valgrind)) old_rzsize = u2rz(old_usize); if (config_prof && opt_prof) { - prof_thr_cnt_t *cnt; - usize = (alignment == 0) ? s2u(size) : sa2u(size, alignment); assert(usize != 0); - PROF_ALLOC_PREP(1, usize, cnt); - p = irallocx_prof(ptr, old_usize, size, alignment, &usize, zero, - try_tcache_alloc, try_tcache_dalloc, arena, cnt); - if (p == NULL) + p = irallocx_prof(tsd, ptr, old_usize, size, alignment, &usize, + zero, tcache, arena); + if (unlikely(p == NULL)) goto label_oom; } else { - p = iralloct(ptr, size, 0, alignment, zero, try_tcache_alloc, - try_tcache_dalloc, arena); - if (p == NULL) + p = iralloct(tsd, ptr, old_usize, size, alignment, zero, + tcache, arena); + if (unlikely(p == NULL)) goto label_oom; - if (config_stats || (config_valgrind && opt_valgrind)) + if (config_stats || (config_valgrind && unlikely(in_valgrind))) usize = isalloc(p, config_prof); } + assert(alignment == 0 || ((uintptr_t)p & (alignment - 1)) == ZU(0)); if (config_stats) { - thread_allocated_t *ta; - ta = thread_allocated_tsd_get(); - ta->allocated += usize; - ta->deallocated += old_usize; + *tsd_thread_allocatedp_get(tsd) += usize; + *tsd_thread_deallocatedp_get(tsd) += old_usize; } UTRACE(ptr, size, p); - JEMALLOC_VALGRIND_REALLOC(p, usize, ptr, old_usize, old_rzsize, zero); + JEMALLOC_VALGRIND_REALLOC(true, p, usize, false, ptr, old_usize, + old_rzsize, false, zero); return (p); label_oom: - if (config_xmalloc && opt_xmalloc) { + if (config_xmalloc && unlikely(opt_xmalloc)) { malloc_write(": Error in rallocx(): out of memory\n"); abort(); } @@ -1623,11 +2217,11 @@ je_rallocx(void *ptr, size_t size, int flags) JEMALLOC_ALWAYS_INLINE_C size_t ixallocx_helper(void *ptr, size_t old_usize, size_t size, size_t extra, - size_t alignment, bool zero, arena_t *arena) + size_t alignment, bool zero) { size_t usize; - if (ixalloc(ptr, size, extra, alignment, zero)) + if (ixalloc(ptr, old_usize, size, extra, alignment, zero)) return (old_usize); usize = isalloc(ptr, config_prof); @@ -1636,215 +2230,227 @@ ixallocx_helper(void *ptr, size_t old_usize, size_t size, size_t extra, static size_t ixallocx_prof_sample(void *ptr, size_t old_usize, size_t size, size_t extra, - size_t alignment, size_t max_usize, bool zero, arena_t *arena, - prof_thr_cnt_t *cnt) + size_t alignment, size_t max_usize, bool zero, prof_tctx_t *tctx) { size_t usize; - if (cnt == NULL) + if (tctx == NULL) return (old_usize); /* Use minimum usize to determine whether promotion may happen. */ - if (prof_promote && ((alignment == 0) ? s2u(size) : sa2u(size, - alignment)) <= SMALL_MAXCLASS) { - if (ixalloc(ptr, SMALL_MAXCLASS+1, (SMALL_MAXCLASS+1 >= - size+extra) ? 0 : size+extra - (SMALL_MAXCLASS+1), - alignment, zero)) + if (((alignment == 0) ? s2u(size) : sa2u(size, alignment)) <= + SMALL_MAXCLASS) { + if (ixalloc(ptr, old_usize, SMALL_MAXCLASS+1, + (SMALL_MAXCLASS+1 >= size+extra) ? 0 : size+extra - + (SMALL_MAXCLASS+1), alignment, zero)) return (old_usize); usize = isalloc(ptr, config_prof); - if (max_usize < PAGE) + if (max_usize < LARGE_MINCLASS) arena_prof_promoted(ptr, usize); } else { usize = ixallocx_helper(ptr, old_usize, size, extra, alignment, - zero, arena); + zero); } return (usize); } JEMALLOC_ALWAYS_INLINE_C size_t -ixallocx_prof(void *ptr, size_t old_usize, size_t size, size_t extra, - size_t alignment, size_t max_usize, bool zero, arena_t *arena, - prof_thr_cnt_t *cnt) +ixallocx_prof(tsd_t *tsd, void *ptr, size_t old_usize, size_t size, + size_t extra, size_t alignment, bool zero) { - size_t usize; - prof_ctx_t *old_ctx; + size_t max_usize, usize; + prof_tctx_t *old_tctx, *tctx; - old_ctx = prof_ctx_get(ptr); - if ((uintptr_t)cnt != (uintptr_t)1U) { + old_tctx = prof_tctx_get(ptr); + /* + * usize isn't knowable before ixalloc() returns when extra is non-zero. + * Therefore, compute its maximum possible value and use that in + * prof_alloc_prep() to decide whether to capture a backtrace. + * prof_realloc() will use the actual usize to decide whether to sample. + */ + max_usize = (alignment == 0) ? s2u(size+extra) : sa2u(size+extra, + alignment); + tctx = prof_alloc_prep(tsd, max_usize, false); + if (unlikely((uintptr_t)tctx != (uintptr_t)1U)) { usize = ixallocx_prof_sample(ptr, old_usize, size, extra, - alignment, zero, max_usize, arena, cnt); + alignment, zero, max_usize, tctx); } else { usize = ixallocx_helper(ptr, old_usize, size, extra, alignment, - zero, arena); + zero); } - if (usize == old_usize) + if (unlikely(usize == old_usize)) { + prof_alloc_rollback(tsd, tctx, false); return (usize); - prof_realloc(ptr, usize, cnt, old_usize, old_ctx); + } + prof_realloc(tsd, ptr, usize, tctx, false, old_usize, old_tctx); return (usize); } -size_t +JEMALLOC_EXPORT size_t JEMALLOC_NOTHROW je_xallocx(void *ptr, size_t size, size_t extra, int flags) { + tsd_t *tsd; size_t usize, old_usize; UNUSED size_t old_rzsize JEMALLOC_CC_SILENCE_INIT(0); - size_t alignment = (ZU(1) << (flags & MALLOCX_LG_ALIGN_MASK) - & (SIZE_T_MAX-1)); + size_t alignment = MALLOCX_ALIGN_GET(flags); bool zero = flags & MALLOCX_ZERO; - unsigned arena_ind = ((unsigned)(flags >> 8)) - 1; - arena_t *arena; assert(ptr != NULL); assert(size != 0); assert(SIZE_T_MAX - size >= extra); - assert(malloc_initialized || IS_INITIALIZER); + assert(malloc_initialized() || IS_INITIALIZER); malloc_thread_init(); - - if (arena_ind != UINT_MAX) - arena = arenas[arena_ind]; - else - arena = NULL; + tsd = tsd_fetch(); old_usize = isalloc(ptr, config_prof); - if (config_valgrind && opt_valgrind) + if (config_valgrind && unlikely(in_valgrind)) old_rzsize = u2rz(old_usize); if (config_prof && opt_prof) { - prof_thr_cnt_t *cnt; - /* - * usize isn't knowable before ixalloc() returns when extra is - * non-zero. Therefore, compute its maximum possible value and - * use that in PROF_ALLOC_PREP() to decide whether to capture a - * backtrace. prof_realloc() will use the actual usize to - * decide whether to sample. - */ - size_t max_usize = (alignment == 0) ? s2u(size+extra) : - sa2u(size+extra, alignment); - PROF_ALLOC_PREP(1, max_usize, cnt); - usize = ixallocx_prof(ptr, old_usize, size, extra, alignment, - max_usize, zero, arena, cnt); + usize = ixallocx_prof(tsd, ptr, old_usize, size, extra, + alignment, zero); } else { usize = ixallocx_helper(ptr, old_usize, size, extra, alignment, - zero, arena); + zero); } - if (usize == old_usize) + if (unlikely(usize == old_usize)) goto label_not_resized; if (config_stats) { - thread_allocated_t *ta; - ta = thread_allocated_tsd_get(); - ta->allocated += usize; - ta->deallocated += old_usize; + *tsd_thread_allocatedp_get(tsd) += usize; + *tsd_thread_deallocatedp_get(tsd) += old_usize; } - JEMALLOC_VALGRIND_REALLOC(ptr, usize, ptr, old_usize, old_rzsize, zero); + JEMALLOC_VALGRIND_REALLOC(false, ptr, usize, false, ptr, old_usize, + old_rzsize, false, zero); label_not_resized: UTRACE(ptr, size, ptr); return (usize); } -size_t +JEMALLOC_EXPORT size_t JEMALLOC_NOTHROW +JEMALLOC_ATTR(pure) je_sallocx(const void *ptr, int flags) { size_t usize; - assert(malloc_initialized || IS_INITIALIZER); + assert(malloc_initialized() || IS_INITIALIZER); malloc_thread_init(); if (config_ivsalloc) usize = ivsalloc(ptr, config_prof); - else { - assert(ptr != NULL); + else usize = isalloc(ptr, config_prof); - } return (usize); } -void +JEMALLOC_EXPORT void JEMALLOC_NOTHROW je_dallocx(void *ptr, int flags) { - size_t usize; - UNUSED size_t rzsize JEMALLOC_CC_SILENCE_INIT(0); - unsigned arena_ind = ((unsigned)(flags >> 8)) - 1; - bool try_tcache; + tsd_t *tsd; + tcache_t *tcache; assert(ptr != NULL); - assert(malloc_initialized || IS_INITIALIZER); + 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]); + tsd = tsd_fetch(); + if (unlikely((flags & MALLOCX_TCACHE_MASK) != 0)) { + if ((flags & MALLOCX_TCACHE_MASK) == MALLOCX_TCACHE_NONE) + tcache = NULL; + else + tcache = tcaches_get(tsd, MALLOCX_TCACHE_GET(flags)); } else - try_tcache = true; + tcache = tcache_get(tsd, false); UTRACE(ptr, 0, 0); - if (config_stats || config_valgrind) - usize = isalloc(ptr, config_prof); - if (config_prof && opt_prof) { - if (config_stats == false && config_valgrind == false) - usize = isalloc(ptr, config_prof); - prof_free(ptr, usize); - } - if (config_stats) - thread_allocated_tsd_get()->deallocated += usize; - if (config_valgrind && opt_valgrind) - rzsize = p2rz(ptr); - iqalloct(ptr, try_tcache); - JEMALLOC_VALGRIND_FREE(ptr, rzsize); + ifree(tsd_fetch(), ptr, tcache); } -size_t -je_nallocx(size_t size, int flags) +JEMALLOC_ALWAYS_INLINE_C size_t +inallocx(size_t size, int flags) { size_t usize; - size_t alignment = (ZU(1) << (flags & MALLOCX_LG_ALIGN_MASK) - & (SIZE_T_MAX-1)); - assert(size != 0); - - if (malloc_init()) - return (0); - - usize = (alignment == 0) ? s2u(size) : sa2u(size, alignment); + if (likely((flags & MALLOCX_LG_ALIGN_MASK) == 0)) + usize = s2u(size); + else + usize = sa2u(size, MALLOCX_ALIGN_GET_SPECIFIED(flags)); assert(usize != 0); return (usize); } -int +JEMALLOC_EXPORT void JEMALLOC_NOTHROW +je_sdallocx(void *ptr, size_t size, int flags) +{ + tsd_t *tsd; + tcache_t *tcache; + size_t usize; + + assert(ptr != NULL); + assert(malloc_initialized() || IS_INITIALIZER); + usize = inallocx(size, flags); + assert(usize == isalloc(ptr, config_prof)); + + tsd = tsd_fetch(); + if (unlikely((flags & MALLOCX_TCACHE_MASK) != 0)) { + if ((flags & MALLOCX_TCACHE_MASK) == MALLOCX_TCACHE_NONE) + tcache = NULL; + else + tcache = tcaches_get(tsd, MALLOCX_TCACHE_GET(flags)); + } else + tcache = tcache_get(tsd, false); + + UTRACE(ptr, 0, 0); + isfree(tsd, ptr, usize, tcache); +} + +JEMALLOC_EXPORT size_t JEMALLOC_NOTHROW +JEMALLOC_ATTR(pure) +je_nallocx(size_t size, int flags) +{ + + assert(size != 0); + + if (unlikely(malloc_init())) + return (0); + + return (inallocx(size, flags)); +} + +JEMALLOC_EXPORT int JEMALLOC_NOTHROW je_mallctl(const char *name, void *oldp, size_t *oldlenp, void *newp, size_t newlen) { - if (malloc_init()) + if (unlikely(malloc_init())) return (EAGAIN); return (ctl_byname(name, oldp, oldlenp, newp, newlen)); } -int +JEMALLOC_EXPORT int JEMALLOC_NOTHROW je_mallctlnametomib(const char *name, size_t *mibp, size_t *miblenp) { - if (malloc_init()) + if (unlikely(malloc_init())) return (EAGAIN); return (ctl_nametomib(name, mibp, miblenp)); } -int +JEMALLOC_EXPORT int JEMALLOC_NOTHROW je_mallctlbymib(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp, void *newp, size_t newlen) { - if (malloc_init()) + if (unlikely(malloc_init())) return (EAGAIN); return (ctl_bymib(mib, miblen, oldp, oldlenp, newp, newlen)); } -void +JEMALLOC_EXPORT void JEMALLOC_NOTHROW je_malloc_stats_print(void (*write_cb)(void *, const char *), void *cbopaque, const char *opts) { @@ -1852,18 +2458,18 @@ je_malloc_stats_print(void (*write_cb)(void *, const char *), void *cbopaque, stats_print(write_cb, cbopaque, opts); } -size_t +JEMALLOC_EXPORT size_t JEMALLOC_NOTHROW je_malloc_usable_size(JEMALLOC_USABLE_SIZE_CONST void *ptr) { size_t ret; - assert(malloc_initialized || IS_INITIALIZER); + assert(malloc_initialized() || IS_INITIALIZER); malloc_thread_init(); if (config_ivsalloc) ret = ivsalloc(ptr, config_prof); else - ret = (ptr != NULL) ? isalloc(ptr, config_prof) : 0; + ret = (ptr == NULL) ? 0 : isalloc(ptr, config_prof); return (ret); } @@ -1873,9 +2479,17 @@ je_malloc_usable_size(JEMALLOC_USABLE_SIZE_CONST void *ptr) */ /******************************************************************************/ /* - * Begin experimental functions. + * Begin compatibility functions. */ -#ifdef JEMALLOC_EXPERIMENTAL + +#define ALLOCM_LG_ALIGN(la) (la) +#define ALLOCM_ALIGN(a) (ffsl(a)-1) +#define ALLOCM_ZERO ((int)0x40) +#define ALLOCM_NO_MOVE ((int)0x80) + +#define ALLOCM_SUCCESS 0 +#define ALLOCM_ERR_OOM 1 +#define ALLOCM_ERR_NOT_MOVED 2 int je_allocm(void **ptr, size_t *rsize, size_t size, int flags) @@ -1952,9 +2566,17 @@ je_nallocm(size_t *rsize, size_t size, int flags) return (ALLOCM_SUCCESS); } -#endif +#undef ALLOCM_LG_ALIGN +#undef ALLOCM_ALIGN +#undef ALLOCM_ZERO +#undef ALLOCM_NO_MOVE + +#undef ALLOCM_SUCCESS +#undef ALLOCM_ERR_OOM +#undef ALLOCM_ERR_NOT_MOVED + /* - * End experimental functions. + * End compatibility functions. */ /******************************************************************************/ /* @@ -1971,9 +2593,9 @@ je_nallocm(size_t *rsize, size_t size, int flags) * 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. + * constructor is a partial solution to this problem. It may still be 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 @@ -1994,10 +2616,10 @@ _malloc_prefork(void) unsigned i; #ifdef JEMALLOC_MUTEX_INIT_CB - if (malloc_initialized == false) + if (!malloc_initialized()) return; #endif - assert(malloc_initialized); + assert(malloc_initialized()); /* Acquire all mutexes in a safe order. */ ctl_prefork(); @@ -2009,7 +2631,6 @@ _malloc_prefork(void) } chunk_prefork(); base_prefork(); - huge_prefork(); } #ifndef JEMALLOC_MUTEX_INIT_CB @@ -2023,13 +2644,12 @@ _malloc_postfork(void) unsigned i; #ifdef JEMALLOC_MUTEX_INIT_CB - if (malloc_initialized == false) + if (!malloc_initialized()) return; #endif - assert(malloc_initialized); + assert(malloc_initialized()); /* Release all mutexes, now that fork() has completed. */ - huge_postfork_parent(); base_postfork_parent(); chunk_postfork_parent(); for (i = 0; i < narenas_total; i++) { @@ -2046,10 +2666,9 @@ jemalloc_postfork_child(void) { unsigned i; - assert(malloc_initialized); + assert(malloc_initialized()); /* Release all mutexes, now that fork() has completed. */ - huge_postfork_child(); base_postfork_child(); chunk_postfork_child(); for (i = 0; i < narenas_total; i++) { @@ -2069,55 +2688,3 @@ _malloc_first_thread(void) } /******************************************************************************/ -/* - * The following functions are used for TLS allocation/deallocation in static - * binaries on FreeBSD. The primary difference between these and i[mcd]alloc() - * is that these avoid accessing TLS variables. - */ - -static void * -a0alloc(size_t size, bool zero) -{ - - if (malloc_init()) - return (NULL); - - if (size == 0) - size = 1; - - if (size <= arena_maxclass) - return (arena_malloc(arenas[0], size, zero, false)); - else - return (huge_malloc(size, zero, huge_dss_prec_get(arenas[0]))); -} - -void * -a0malloc(size_t size) -{ - - return (a0alloc(size, false)); -} - -void * -a0calloc(size_t num, size_t size) -{ - - return (a0alloc(num * size, true)); -} - -void -a0free(void *ptr) -{ - arena_chunk_t *chunk; - - if (ptr == NULL) - return; - - chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr); - if (chunk != ptr) - arena_dalloc(chunk->arena, chunk, ptr, false); - else - huge_dalloc(ptr, true); -} - -/******************************************************************************/ diff --git a/contrib/jemalloc/src/mutex.c b/contrib/jemalloc/src/mutex.c index 0f90d0505852..934d5aa5f28e 100644 --- a/contrib/jemalloc/src/mutex.c +++ b/contrib/jemalloc/src/mutex.c @@ -74,8 +74,8 @@ _pthread_mutex_init_calloc_cb(pthread_mutex_t *mutex, { return (((int (*)(pthread_mutex_t *, void *(*)(size_t, size_t))) - __libc_interposing[INTERPOS__pthread_mutex_init_calloc_cb])( - mutex, calloc_cb)); + __libc_interposing[INTERPOS__pthread_mutex_init_calloc_cb])(mutex, + calloc_cb)); } #endif @@ -84,9 +84,13 @@ malloc_mutex_init(malloc_mutex_t *mutex) { #ifdef _WIN32 +# if _WIN32_WINNT >= 0x0600 + InitializeSRWLock(&mutex->lock); +# else if (!InitializeCriticalSectionAndSpinCount(&mutex->lock, _CRT_SPINCOUNT)) return (true); +# endif #elif (defined(JEMALLOC_OSSPIN)) mutex->lock = 0; #elif (defined(JEMALLOC_MUTEX_INIT_CB)) @@ -94,8 +98,8 @@ malloc_mutex_init(malloc_mutex_t *mutex) mutex->postponed_next = postponed_mutexes; postponed_mutexes = mutex; } else { - if (_pthread_mutex_init_calloc_cb(&mutex->lock, base_calloc) != - 0) + if (_pthread_mutex_init_calloc_cb(&mutex->lock, + bootstrap_calloc) != 0) return (true); } #else @@ -151,7 +155,7 @@ malloc_mutex_first_thread(void) postpone_init = false; while (postponed_mutexes != NULL) { if (_pthread_mutex_init_calloc_cb(&postponed_mutexes->lock, - base_calloc) != 0) + bootstrap_calloc) != 0) return (true); postponed_mutexes = postponed_mutexes->postponed_next; } diff --git a/contrib/jemalloc/src/pages.c b/contrib/jemalloc/src/pages.c new file mode 100644 index 000000000000..83a167f67012 --- /dev/null +++ b/contrib/jemalloc/src/pages.c @@ -0,0 +1,173 @@ +#define JEMALLOC_PAGES_C_ +#include "jemalloc/internal/jemalloc_internal.h" + +/******************************************************************************/ + +void * +pages_map(void *addr, size_t size) +{ + void *ret; + + assert(size != 0); + +#ifdef _WIN32 + /* + * If VirtualAlloc can't allocate at the given address when one is + * given, it fails and returns NULL. + */ + ret = VirtualAlloc(addr, size, MEM_COMMIT | MEM_RESERVE, + PAGE_READWRITE); +#else + /* + * We don't use MAP_FIXED here, because it can cause the *replacement* + * of existing mappings, and we only want to create new mappings. + */ + ret = mmap(addr, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, + -1, 0); + assert(ret != NULL); + + if (ret == MAP_FAILED) + ret = NULL; + else if (addr != NULL && ret != addr) { + /* + * We succeeded in mapping memory, but not in the right place. + */ + pages_unmap(ret, size); + ret = NULL; + } +#endif + assert(ret == NULL || (addr == NULL && ret != addr) + || (addr != NULL && ret == addr)); + return (ret); +} + +void +pages_unmap(void *addr, size_t size) +{ + +#ifdef _WIN32 + if (VirtualFree(addr, 0, MEM_RELEASE) == 0) +#else + if (munmap(addr, size) == -1) +#endif + { + char buf[BUFERROR_BUF]; + + buferror(get_errno(), buf, sizeof(buf)); + malloc_printf(": Error in " +#ifdef _WIN32 + "VirtualFree" +#else + "munmap" +#endif + "(): %s\n", buf); + if (opt_abort) + abort(); + } +} + +void * +pages_trim(void *addr, size_t alloc_size, size_t leadsize, size_t size) +{ + void *ret = (void *)((uintptr_t)addr + leadsize); + + assert(alloc_size >= leadsize + size); +#ifdef _WIN32 + { + void *new_addr; + + pages_unmap(addr, alloc_size); + new_addr = pages_map(ret, size); + if (new_addr == ret) + return (ret); + if (new_addr) + pages_unmap(new_addr, size); + return (NULL); + } +#else + { + size_t trailsize = alloc_size - leadsize - size; + + if (leadsize != 0) + pages_unmap(addr, leadsize); + if (trailsize != 0) + pages_unmap((void *)((uintptr_t)ret + size), trailsize); + return (ret); + } +#endif +} + +static bool +pages_commit_impl(void *addr, size_t size, bool commit) +{ + +#ifndef _WIN32 + /* + * The following decommit/commit implementation is functional, but + * always disabled because it doesn't add value beyong improved + * debugging (at the cost of extra system calls) on systems that + * overcommit. + */ + if (false) { + int prot = commit ? (PROT_READ | PROT_WRITE) : PROT_NONE; + void *result = mmap(addr, size, prot, MAP_PRIVATE | MAP_ANON | + MAP_FIXED, -1, 0); + if (result == MAP_FAILED) + return (true); + if (result != addr) { + /* + * We succeeded in mapping memory, but not in the right + * place. + */ + pages_unmap(result, size); + return (true); + } + return (false); + } +#endif + return (true); +} + +bool +pages_commit(void *addr, size_t size) +{ + + return (pages_commit_impl(addr, size, true)); +} + +bool +pages_decommit(void *addr, size_t size) +{ + + return (pages_commit_impl(addr, size, false)); +} + +bool +pages_purge(void *addr, size_t size) +{ + bool unzeroed; + +#ifdef _WIN32 + VirtualAlloc(addr, size, MEM_RESET, PAGE_READWRITE); + unzeroed = true; +#elif defined(JEMALLOC_HAVE_MADVISE) +# 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 madvise(2) flag defined for purging unused dirty pages." +# endif + int err = madvise(addr, size, JEMALLOC_MADV_PURGE); + unzeroed = (!JEMALLOC_MADV_ZEROS || err != 0); +# undef JEMALLOC_MADV_PURGE +# undef JEMALLOC_MADV_ZEROS +#else + /* Last resort no-op. */ + unzeroed = true; +#endif + return (unzeroed); +} + diff --git a/contrib/jemalloc/src/prof.c b/contrib/jemalloc/src/prof.c index 7722b7b43739..a05792fd6b5b 100644 --- a/contrib/jemalloc/src/prof.c +++ b/contrib/jemalloc/src/prof.c @@ -14,14 +14,13 @@ /******************************************************************************/ /* Data. */ -malloc_tsd_data(, prof_tdata, prof_tdata_t *, NULL) - bool opt_prof = false; bool opt_prof_active = true; +bool opt_prof_thread_active_init = true; size_t opt_lg_prof_sample = LG_PROF_SAMPLE_DEFAULT; ssize_t opt_lg_prof_interval = LG_PROF_INTERVAL_DEFAULT; bool opt_prof_gdump = false; -bool opt_prof_final = true; +bool opt_prof_final = false; bool opt_prof_leak = false; bool opt_prof_accum = false; char opt_prof_prefix[ @@ -31,25 +30,65 @@ char opt_prof_prefix[ #endif 1]; -uint64_t prof_interval = 0; -bool prof_promote; +/* + * Initialized as opt_prof_active, and accessed via + * prof_active_[gs]et{_unlocked,}(). + */ +bool prof_active; +static malloc_mutex_t prof_active_mtx; /* - * Table of mutexes that are shared among ctx's. These are leaf locks, so - * there is no problem with using them for more than one ctx at the same time. - * The primary motivation for this sharing though is that ctx's are ephemeral, + * Initialized as opt_prof_thread_active_init, and accessed via + * prof_thread_active_init_[gs]et(). + */ +static bool prof_thread_active_init; +static malloc_mutex_t prof_thread_active_init_mtx; + +/* + * Initialized as opt_prof_gdump, and accessed via + * prof_gdump_[gs]et{_unlocked,}(). + */ +bool prof_gdump_val; +static malloc_mutex_t prof_gdump_mtx; + +uint64_t prof_interval = 0; + +size_t lg_prof_sample; + +/* + * Table of mutexes that are shared among gctx's. These are leaf locks, so + * there is no problem with using them for more than one gctx at the same time. + * The primary motivation for this sharing though is that gctx's are ephemeral, * and destroying mutexes causes complications for systems that allocate when * creating/destroying mutexes. */ -static malloc_mutex_t *ctx_locks; -static unsigned cum_ctxs; /* Atomic counter. */ +static malloc_mutex_t *gctx_locks; +static unsigned cum_gctxs; /* Atomic counter. */ /* - * Global hash of (prof_bt_t *)-->(prof_ctx_t *). This is the master data + * Table of mutexes that are shared among tdata's. No operations require + * holding multiple tdata locks, so there is no problem with using them for more + * than one tdata at the same time, even though a gctx lock may be acquired + * while holding a tdata lock. + */ +static malloc_mutex_t *tdata_locks; + +/* + * Global hash of (prof_bt_t *)-->(prof_gctx_t *). This is the master data * structure that knows about all backtraces currently captured. */ -static ckh_t bt2ctx; -static malloc_mutex_t bt2ctx_mtx; +static ckh_t bt2gctx; +static malloc_mutex_t bt2gctx_mtx; + +/* + * Tree of all extant prof_tdata_t structures, regardless of state, + * {attached,detached,expired}. + */ +static prof_tdata_tree_t tdatas; +static malloc_mutex_t tdatas_mtx; + +static uint64_t next_thr_uid; +static malloc_mutex_t next_thr_uid_mtx; static malloc_mutex_t prof_dump_seq_mtx; static uint64_t prof_dump_seq; @@ -77,6 +116,137 @@ static int prof_dump_fd; static bool prof_booted = false; /******************************************************************************/ +/* + * Function prototypes for static functions that are referenced prior to + * definition. + */ + +static bool prof_tctx_should_destroy(prof_tctx_t *tctx); +static void prof_tctx_destroy(tsd_t *tsd, prof_tctx_t *tctx); +static bool prof_tdata_should_destroy(prof_tdata_t *tdata, + bool even_if_attached); +static void prof_tdata_destroy(tsd_t *tsd, prof_tdata_t *tdata, + bool even_if_attached); +static char *prof_thread_name_alloc(tsd_t *tsd, const char *thread_name); + +/******************************************************************************/ +/* Red-black trees. */ + +JEMALLOC_INLINE_C int +prof_tctx_comp(const prof_tctx_t *a, const prof_tctx_t *b) +{ + uint64_t a_thr_uid = a->thr_uid; + uint64_t b_thr_uid = b->thr_uid; + int ret = (a_thr_uid > b_thr_uid) - (a_thr_uid < b_thr_uid); + if (ret == 0) { + uint64_t a_tctx_uid = a->tctx_uid; + uint64_t b_tctx_uid = b->tctx_uid; + ret = (a_tctx_uid > b_tctx_uid) - (a_tctx_uid < b_tctx_uid); + } + return (ret); +} + +rb_gen(static UNUSED, tctx_tree_, prof_tctx_tree_t, prof_tctx_t, + tctx_link, prof_tctx_comp) + +JEMALLOC_INLINE_C int +prof_gctx_comp(const prof_gctx_t *a, const prof_gctx_t *b) +{ + unsigned a_len = a->bt.len; + unsigned b_len = b->bt.len; + unsigned comp_len = (a_len < b_len) ? a_len : b_len; + int ret = memcmp(a->bt.vec, b->bt.vec, comp_len * sizeof(void *)); + if (ret == 0) + ret = (a_len > b_len) - (a_len < b_len); + return (ret); +} + +rb_gen(static UNUSED, gctx_tree_, prof_gctx_tree_t, prof_gctx_t, dump_link, + prof_gctx_comp) + +JEMALLOC_INLINE_C int +prof_tdata_comp(const prof_tdata_t *a, const prof_tdata_t *b) +{ + int ret; + uint64_t a_uid = a->thr_uid; + uint64_t b_uid = b->thr_uid; + + ret = ((a_uid > b_uid) - (a_uid < b_uid)); + if (ret == 0) { + uint64_t a_discrim = a->thr_discrim; + uint64_t b_discrim = b->thr_discrim; + + ret = ((a_discrim > b_discrim) - (a_discrim < b_discrim)); + } + return (ret); +} + +rb_gen(static UNUSED, tdata_tree_, prof_tdata_tree_t, prof_tdata_t, tdata_link, + prof_tdata_comp) + +/******************************************************************************/ + +void +prof_alloc_rollback(tsd_t *tsd, prof_tctx_t *tctx, bool updated) +{ + prof_tdata_t *tdata; + + cassert(config_prof); + + if (updated) { + /* + * Compute a new sample threshold. This isn't very important in + * practice, because this function is rarely executed, so the + * potential for sample bias is minimal except in contrived + * programs. + */ + tdata = prof_tdata_get(tsd, true); + if (tdata != NULL) + prof_sample_threshold_update(tctx->tdata); + } + + if ((uintptr_t)tctx > (uintptr_t)1U) { + malloc_mutex_lock(tctx->tdata->lock); + tctx->prepared = false; + if (prof_tctx_should_destroy(tctx)) + prof_tctx_destroy(tsd, tctx); + else + malloc_mutex_unlock(tctx->tdata->lock); + } +} + +void +prof_malloc_sample_object(const void *ptr, size_t usize, prof_tctx_t *tctx) +{ + + prof_tctx_set(ptr, tctx); + + malloc_mutex_lock(tctx->tdata->lock); + tctx->cnts.curobjs++; + tctx->cnts.curbytes += usize; + if (opt_prof_accum) { + tctx->cnts.accumobjs++; + tctx->cnts.accumbytes += usize; + } + tctx->prepared = false; + malloc_mutex_unlock(tctx->tdata->lock); +} + +void +prof_free_sampled_object(tsd_t *tsd, size_t usize, prof_tctx_t *tctx) +{ + + malloc_mutex_lock(tctx->tdata->lock); + assert(tctx->cnts.curobjs > 0); + assert(tctx->cnts.curbytes >= usize); + tctx->cnts.curobjs--; + tctx->cnts.curbytes -= usize; + + if (prof_tctx_should_destroy(tctx)) + prof_tctx_destroy(tsd, tctx); + else + malloc_mutex_unlock(tctx->tdata->lock); +} void bt_init(prof_bt_t *bt, void **vec) @@ -88,109 +258,61 @@ bt_init(prof_bt_t *bt, void **vec) bt->len = 0; } -static void -bt_destroy(prof_bt_t *bt) +JEMALLOC_INLINE_C void +prof_enter(tsd_t *tsd, prof_tdata_t *tdata) { cassert(config_prof); + assert(tdata == prof_tdata_get(tsd, false)); - idalloc(bt); + if (tdata != NULL) { + assert(!tdata->enq); + tdata->enq = true; + } + + malloc_mutex_lock(&bt2gctx_mtx); } -static prof_bt_t * -bt_dup(prof_bt_t *bt) -{ - prof_bt_t *ret; - - cassert(config_prof); - - /* - * Create a single allocation that has space for vec immediately - * following the prof_bt_t structure. The backtraces that get - * stored in the backtrace caches are copied from stack-allocated - * temporary variables, so size is known at creation time. Making this - * a contiguous object improves cache locality. - */ - ret = (prof_bt_t *)imalloc(QUANTUM_CEILING(sizeof(prof_bt_t)) + - (bt->len * sizeof(void *))); - if (ret == NULL) - return (NULL); - ret->vec = (void **)((uintptr_t)ret + - QUANTUM_CEILING(sizeof(prof_bt_t))); - memcpy(ret->vec, bt->vec, bt->len * sizeof(void *)); - ret->len = bt->len; - - return (ret); -} - -static inline void -prof_enter(prof_tdata_t *prof_tdata) +JEMALLOC_INLINE_C void +prof_leave(tsd_t *tsd, prof_tdata_t *tdata) { cassert(config_prof); + assert(tdata == prof_tdata_get(tsd, false)); - assert(prof_tdata->enq == false); - prof_tdata->enq = true; + malloc_mutex_unlock(&bt2gctx_mtx); - malloc_mutex_lock(&bt2ctx_mtx); -} + if (tdata != NULL) { + bool idump, gdump; -static inline void -prof_leave(prof_tdata_t *prof_tdata) -{ - bool idump, gdump; + assert(tdata->enq); + tdata->enq = false; + idump = tdata->enq_idump; + tdata->enq_idump = false; + gdump = tdata->enq_gdump; + tdata->enq_gdump = false; - cassert(config_prof); - - malloc_mutex_unlock(&bt2ctx_mtx); - - assert(prof_tdata->enq); - prof_tdata->enq = false; - idump = prof_tdata->enq_idump; - prof_tdata->enq_idump = false; - gdump = prof_tdata->enq_gdump; - prof_tdata->enq_gdump = false; - - if (idump) - prof_idump(); - if (gdump) - prof_gdump(); + if (idump) + prof_idump(); + if (gdump) + prof_gdump(); + } } #ifdef JEMALLOC_PROF_LIBUNWIND void -prof_backtrace(prof_bt_t *bt, unsigned nignore) +prof_backtrace(prof_bt_t *bt) { - unw_context_t uc; - unw_cursor_t cursor; - unsigned i; - int err; + int nframes; cassert(config_prof); assert(bt->len == 0); assert(bt->vec != NULL); - unw_getcontext(&uc); - unw_init_local(&cursor, &uc); - - /* Throw away (nignore+1) stack frames, if that many exist. */ - for (i = 0; i < nignore + 1; i++) { - err = unw_step(&cursor); - if (err <= 0) - return; - } - - /* - * Iterate over stack frames until there are no more, or until no space - * remains in bt. - */ - for (i = 0; i < PROF_BT_MAX; i++) { - unw_get_reg(&cursor, UNW_REG_IP, (unw_word_t *)&bt->vec[i]); - bt->len++; - err = unw_step(&cursor); - if (err <= 0) - break; - } + nframes = unw_backtrace(bt->vec, PROF_BT_MAX); + if (nframes <= 0) + return; + bt->len = nframes; } #elif (defined(JEMALLOC_PROF_LIBGCC)) static _Unwind_Reason_Code @@ -206,25 +328,25 @@ static _Unwind_Reason_Code prof_unwind_callback(struct _Unwind_Context *context, void *arg) { prof_unwind_data_t *data = (prof_unwind_data_t *)arg; + void *ip; cassert(config_prof); - if (data->nignore > 0) - data->nignore--; - else { - data->bt->vec[data->bt->len] = (void *)_Unwind_GetIP(context); - data->bt->len++; - if (data->bt->len == data->max) - return (_URC_END_OF_STACK); - } + ip = (void *)_Unwind_GetIP(context); + if (ip == NULL) + return (_URC_END_OF_STACK); + data->bt->vec[data->bt->len] = ip; + data->bt->len++; + if (data->bt->len == data->max) + return (_URC_END_OF_STACK); return (_URC_NO_REASON); } void -prof_backtrace(prof_bt_t *bt, unsigned nignore) +prof_backtrace(prof_bt_t *bt) { - prof_unwind_data_t data = {bt, nignore, PROF_BT_MAX}; + prof_unwind_data_t data = {bt, PROF_BT_MAX}; cassert(config_prof); @@ -232,25 +354,22 @@ prof_backtrace(prof_bt_t *bt, unsigned nignore) } #elif (defined(JEMALLOC_PROF_GCC)) void -prof_backtrace(prof_bt_t *bt, unsigned nignore) +prof_backtrace(prof_bt_t *bt) { #define BT_FRAME(i) \ - if ((i) < nignore + PROF_BT_MAX) { \ + if ((i) < PROF_BT_MAX) { \ void *p; \ if (__builtin_frame_address(i) == 0) \ return; \ p = __builtin_return_address(i); \ if (p == NULL) \ return; \ - if (i >= nignore) { \ - bt->vec[(i) - nignore] = p; \ - bt->len = (i) - nignore + 1; \ - } \ + bt->vec[(i)] = p; \ + bt->len = (i) + 1; \ } else \ return; cassert(config_prof); - assert(nignore <= 3); BT_FRAME(0) BT_FRAME(1) @@ -392,16 +511,11 @@ prof_backtrace(prof_bt_t *bt, unsigned nignore) BT_FRAME(125) BT_FRAME(126) BT_FRAME(127) - - /* Extras to compensate for nignore. */ - BT_FRAME(128) - BT_FRAME(129) - BT_FRAME(130) #undef BT_FRAME } #else void -prof_backtrace(prof_bt_t *bt, unsigned nignore) +prof_backtrace(prof_bt_t *bt) { cassert(config_prof); @@ -410,256 +524,393 @@ prof_backtrace(prof_bt_t *bt, unsigned nignore) #endif static malloc_mutex_t * -prof_ctx_mutex_choose(void) +prof_gctx_mutex_choose(void) { - unsigned nctxs = atomic_add_u(&cum_ctxs, 1); + unsigned ngctxs = atomic_add_u(&cum_gctxs, 1); - return (&ctx_locks[(nctxs - 1) % PROF_NCTX_LOCKS]); + return (&gctx_locks[(ngctxs - 1) % PROF_NCTX_LOCKS]); } -static void -prof_ctx_init(prof_ctx_t *ctx, prof_bt_t *bt) +static malloc_mutex_t * +prof_tdata_mutex_choose(uint64_t thr_uid) { - ctx->bt = bt; - ctx->lock = prof_ctx_mutex_choose(); + return (&tdata_locks[thr_uid % PROF_NTDATA_LOCKS]); +} + +static prof_gctx_t * +prof_gctx_create(tsd_t *tsd, prof_bt_t *bt) +{ + /* + * Create a single allocation that has space for vec of length bt->len. + */ + prof_gctx_t *gctx = (prof_gctx_t *)iallocztm(tsd, offsetof(prof_gctx_t, + vec) + (bt->len * sizeof(void *)), false, tcache_get(tsd, true), + true, NULL); + if (gctx == NULL) + return (NULL); + gctx->lock = prof_gctx_mutex_choose(); /* * Set nlimbo to 1, in order to avoid a race condition with - * prof_ctx_merge()/prof_ctx_destroy(). + * prof_tctx_destroy()/prof_gctx_try_destroy(). */ - ctx->nlimbo = 1; - ql_elm_new(ctx, dump_link); - memset(&ctx->cnt_merged, 0, sizeof(prof_cnt_t)); - ql_new(&ctx->cnts_ql); + gctx->nlimbo = 1; + tctx_tree_new(&gctx->tctxs); + /* Duplicate bt. */ + memcpy(gctx->vec, bt->vec, bt->len * sizeof(void *)); + gctx->bt.vec = gctx->vec; + gctx->bt.len = bt->len; + return (gctx); } static void -prof_ctx_destroy(prof_ctx_t *ctx) +prof_gctx_try_destroy(tsd_t *tsd, prof_tdata_t *tdata_self, prof_gctx_t *gctx, + prof_tdata_t *tdata) { - prof_tdata_t *prof_tdata; cassert(config_prof); /* - * Check that ctx is still unused by any thread cache before destroying - * it. prof_lookup() increments ctx->nlimbo in order to avoid a race - * condition with this function, as does prof_ctx_merge() in order to - * avoid a race between the main body of prof_ctx_merge() and entry + * Check that gctx is still unused by any thread cache before destroying + * it. prof_lookup() increments gctx->nlimbo in order to avoid a race + * condition with this function, as does prof_tctx_destroy() in order to + * avoid a race between the main body of prof_tctx_destroy() and entry * into this function. */ - prof_tdata = prof_tdata_get(false); - assert((uintptr_t)prof_tdata > (uintptr_t)PROF_TDATA_STATE_MAX); - prof_enter(prof_tdata); - malloc_mutex_lock(ctx->lock); - if (ql_first(&ctx->cnts_ql) == NULL && ctx->cnt_merged.curobjs == 0 && - ctx->nlimbo == 1) { - assert(ctx->cnt_merged.curbytes == 0); - assert(ctx->cnt_merged.accumobjs == 0); - assert(ctx->cnt_merged.accumbytes == 0); - /* Remove ctx from bt2ctx. */ - if (ckh_remove(&bt2ctx, ctx->bt, NULL, NULL)) + prof_enter(tsd, tdata_self); + malloc_mutex_lock(gctx->lock); + assert(gctx->nlimbo != 0); + if (tctx_tree_empty(&gctx->tctxs) && gctx->nlimbo == 1) { + /* Remove gctx from bt2gctx. */ + if (ckh_remove(tsd, &bt2gctx, &gctx->bt, NULL, NULL)) not_reached(); - prof_leave(prof_tdata); - /* Destroy ctx. */ - malloc_mutex_unlock(ctx->lock); - bt_destroy(ctx->bt); - idalloc(ctx); + prof_leave(tsd, tdata_self); + /* Destroy gctx. */ + malloc_mutex_unlock(gctx->lock); + idalloctm(tsd, gctx, tcache_get(tsd, false), true); } else { /* - * Compensate for increment in prof_ctx_merge() or + * Compensate for increment in prof_tctx_destroy() or * prof_lookup(). */ - ctx->nlimbo--; - malloc_mutex_unlock(ctx->lock); - prof_leave(prof_tdata); + gctx->nlimbo--; + malloc_mutex_unlock(gctx->lock); + prof_leave(tsd, tdata_self); } } -static void -prof_ctx_merge(prof_ctx_t *ctx, prof_thr_cnt_t *cnt) +/* tctx->tdata->lock must be held. */ +static bool +prof_tctx_should_destroy(prof_tctx_t *tctx) { - bool destroy; - cassert(config_prof); - - /* Merge cnt stats and detach from ctx. */ - malloc_mutex_lock(ctx->lock); - ctx->cnt_merged.curobjs += cnt->cnts.curobjs; - ctx->cnt_merged.curbytes += cnt->cnts.curbytes; - ctx->cnt_merged.accumobjs += cnt->cnts.accumobjs; - ctx->cnt_merged.accumbytes += cnt->cnts.accumbytes; - ql_remove(&ctx->cnts_ql, cnt, cnts_link); - if (opt_prof_accum == false && ql_first(&ctx->cnts_ql) == NULL && - ctx->cnt_merged.curobjs == 0 && ctx->nlimbo == 0) { - /* - * Increment ctx->nlimbo in order to keep another thread from - * winning the race to destroy ctx while this one has ctx->lock - * dropped. Without this, it would be possible for another - * thread to: - * - * 1) Sample an allocation associated with ctx. - * 2) Deallocate the sampled object. - * 3) Successfully prof_ctx_destroy(ctx). - * - * The result would be that ctx no longer exists by the time - * this thread accesses it in prof_ctx_destroy(). - */ - ctx->nlimbo++; - destroy = true; - } else - destroy = false; - malloc_mutex_unlock(ctx->lock); - if (destroy) - prof_ctx_destroy(ctx); + if (opt_prof_accum) + return (false); + if (tctx->cnts.curobjs != 0) + return (false); + if (tctx->prepared) + return (false); + return (true); } static bool -prof_lookup_global(prof_bt_t *bt, prof_tdata_t *prof_tdata, void **p_btkey, - prof_ctx_t **p_ctx, bool *p_new_ctx) +prof_gctx_should_destroy(prof_gctx_t *gctx) +{ + + if (opt_prof_accum) + return (false); + if (!tctx_tree_empty(&gctx->tctxs)) + return (false); + if (gctx->nlimbo != 0) + return (false); + return (true); +} + +/* tctx->tdata->lock is held upon entry, and released before return. */ +static void +prof_tctx_destroy(tsd_t *tsd, prof_tctx_t *tctx) +{ + prof_tdata_t *tdata = tctx->tdata; + prof_gctx_t *gctx = tctx->gctx; + bool destroy_tdata, destroy_tctx, destroy_gctx; + + assert(tctx->cnts.curobjs == 0); + assert(tctx->cnts.curbytes == 0); + assert(!opt_prof_accum); + assert(tctx->cnts.accumobjs == 0); + assert(tctx->cnts.accumbytes == 0); + + ckh_remove(tsd, &tdata->bt2tctx, &gctx->bt, NULL, NULL); + destroy_tdata = prof_tdata_should_destroy(tdata, false); + malloc_mutex_unlock(tdata->lock); + + malloc_mutex_lock(gctx->lock); + switch (tctx->state) { + case prof_tctx_state_nominal: + tctx_tree_remove(&gctx->tctxs, tctx); + destroy_tctx = true; + if (prof_gctx_should_destroy(gctx)) { + /* + * Increment gctx->nlimbo in order to keep another + * thread from winning the race to destroy gctx while + * this one has gctx->lock dropped. Without this, it + * would be possible for another thread to: + * + * 1) Sample an allocation associated with gctx. + * 2) Deallocate the sampled object. + * 3) Successfully prof_gctx_try_destroy(gctx). + * + * The result would be that gctx no longer exists by the + * time this thread accesses it in + * prof_gctx_try_destroy(). + */ + gctx->nlimbo++; + destroy_gctx = true; + } else + destroy_gctx = false; + break; + case prof_tctx_state_dumping: + /* + * A dumping thread needs tctx to remain valid until dumping + * has finished. Change state such that the dumping thread will + * complete destruction during a late dump iteration phase. + */ + tctx->state = prof_tctx_state_purgatory; + destroy_tctx = false; + destroy_gctx = false; + break; + default: + not_reached(); + destroy_tctx = false; + destroy_gctx = false; + } + malloc_mutex_unlock(gctx->lock); + if (destroy_gctx) { + prof_gctx_try_destroy(tsd, prof_tdata_get(tsd, false), gctx, + tdata); + } + + if (destroy_tdata) + prof_tdata_destroy(tsd, tdata, false); + + if (destroy_tctx) + idalloctm(tsd, tctx, tcache_get(tsd, false), true); +} + +static bool +prof_lookup_global(tsd_t *tsd, prof_bt_t *bt, prof_tdata_t *tdata, + void **p_btkey, prof_gctx_t **p_gctx, bool *p_new_gctx) { union { - prof_ctx_t *p; + prof_gctx_t *p; void *v; - } ctx; + } gctx; union { prof_bt_t *p; void *v; } btkey; - bool new_ctx; + bool new_gctx; - prof_enter(prof_tdata); - if (ckh_search(&bt2ctx, bt, &btkey.v, &ctx.v)) { + prof_enter(tsd, tdata); + if (ckh_search(&bt2gctx, bt, &btkey.v, &gctx.v)) { /* bt has never been seen before. Insert it. */ - ctx.v = imalloc(sizeof(prof_ctx_t)); - if (ctx.v == NULL) { - prof_leave(prof_tdata); + gctx.p = prof_gctx_create(tsd, bt); + if (gctx.v == NULL) { + prof_leave(tsd, tdata); return (true); } - btkey.p = bt_dup(bt); - if (btkey.v == NULL) { - prof_leave(prof_tdata); - idalloc(ctx.v); - return (true); - } - prof_ctx_init(ctx.p, btkey.p); - if (ckh_insert(&bt2ctx, btkey.v, ctx.v)) { + btkey.p = &gctx.p->bt; + if (ckh_insert(tsd, &bt2gctx, btkey.v, gctx.v)) { /* OOM. */ - prof_leave(prof_tdata); - idalloc(btkey.v); - idalloc(ctx.v); + prof_leave(tsd, tdata); + idalloctm(tsd, gctx.v, tcache_get(tsd, false), true); return (true); } - new_ctx = true; + new_gctx = true; } else { /* * Increment nlimbo, in order to avoid a race condition with - * prof_ctx_merge()/prof_ctx_destroy(). + * prof_tctx_destroy()/prof_gctx_try_destroy(). */ - malloc_mutex_lock(ctx.p->lock); - ctx.p->nlimbo++; - malloc_mutex_unlock(ctx.p->lock); - new_ctx = false; + malloc_mutex_lock(gctx.p->lock); + gctx.p->nlimbo++; + malloc_mutex_unlock(gctx.p->lock); + new_gctx = false; } - prof_leave(prof_tdata); + prof_leave(tsd, tdata); *p_btkey = btkey.v; - *p_ctx = ctx.p; - *p_new_ctx = new_ctx; + *p_gctx = gctx.p; + *p_new_gctx = new_gctx; return (false); } -prof_thr_cnt_t * -prof_lookup(prof_bt_t *bt) +prof_tctx_t * +prof_lookup(tsd_t *tsd, prof_bt_t *bt) { union { - prof_thr_cnt_t *p; + prof_tctx_t *p; void *v; } ret; - prof_tdata_t *prof_tdata; + prof_tdata_t *tdata; + bool not_found; cassert(config_prof); - prof_tdata = prof_tdata_get(false); - if ((uintptr_t)prof_tdata <= (uintptr_t)PROF_TDATA_STATE_MAX) + tdata = prof_tdata_get(tsd, false); + if (tdata == NULL) return (NULL); - if (ckh_search(&prof_tdata->bt2cnt, bt, NULL, &ret.v)) { + malloc_mutex_lock(tdata->lock); + not_found = ckh_search(&tdata->bt2tctx, bt, NULL, &ret.v); + if (!not_found) /* Note double negative! */ + ret.p->prepared = true; + malloc_mutex_unlock(tdata->lock); + if (not_found) { + tcache_t *tcache; void *btkey; - prof_ctx_t *ctx; - bool new_ctx; + prof_gctx_t *gctx; + bool new_gctx, error; /* * This thread's cache lacks bt. Look for it in the global * cache. */ - if (prof_lookup_global(bt, prof_tdata, &btkey, &ctx, &new_ctx)) + if (prof_lookup_global(tsd, bt, tdata, &btkey, &gctx, + &new_gctx)) return (NULL); - /* Link a prof_thd_cnt_t into ctx for this thread. */ - if (ckh_count(&prof_tdata->bt2cnt) == PROF_TCMAX) { - assert(ckh_count(&prof_tdata->bt2cnt) > 0); - /* - * Flush the least recently used cnt in order to keep - * bt2cnt from becoming too large. - */ - ret.p = ql_last(&prof_tdata->lru_ql, lru_link); - assert(ret.v != NULL); - if (ckh_remove(&prof_tdata->bt2cnt, ret.p->ctx->bt, - NULL, NULL)) - not_reached(); - ql_remove(&prof_tdata->lru_ql, ret.p, lru_link); - prof_ctx_merge(ret.p->ctx, ret.p); - /* ret can now be re-used. */ - } else { - assert(ckh_count(&prof_tdata->bt2cnt) < PROF_TCMAX); - /* Allocate and partially initialize a new cnt. */ - ret.v = imalloc(sizeof(prof_thr_cnt_t)); - if (ret.p == NULL) { - if (new_ctx) - prof_ctx_destroy(ctx); - return (NULL); - } - ql_elm_new(ret.p, cnts_link); - ql_elm_new(ret.p, lru_link); - } - /* Finish initializing ret. */ - ret.p->ctx = ctx; - ret.p->epoch = 0; - memset(&ret.p->cnts, 0, sizeof(prof_cnt_t)); - if (ckh_insert(&prof_tdata->bt2cnt, btkey, ret.v)) { - if (new_ctx) - prof_ctx_destroy(ctx); - idalloc(ret.v); + /* Link a prof_tctx_t into gctx for this thread. */ + tcache = tcache_get(tsd, true); + ret.v = iallocztm(tsd, sizeof(prof_tctx_t), false, tcache, true, + NULL); + if (ret.p == NULL) { + if (new_gctx) + prof_gctx_try_destroy(tsd, tdata, gctx, tdata); return (NULL); } - ql_head_insert(&prof_tdata->lru_ql, ret.p, lru_link); - malloc_mutex_lock(ctx->lock); - ql_tail_insert(&ctx->cnts_ql, ret.p, cnts_link); - ctx->nlimbo--; - malloc_mutex_unlock(ctx->lock); - } else { - /* Move ret to the front of the LRU. */ - ql_remove(&prof_tdata->lru_ql, ret.p, lru_link); - ql_head_insert(&prof_tdata->lru_ql, ret.p, lru_link); + ret.p->tdata = tdata; + ret.p->thr_uid = tdata->thr_uid; + memset(&ret.p->cnts, 0, sizeof(prof_cnt_t)); + ret.p->gctx = gctx; + ret.p->tctx_uid = tdata->tctx_uid_next++; + ret.p->prepared = true; + ret.p->state = prof_tctx_state_initializing; + malloc_mutex_lock(tdata->lock); + error = ckh_insert(tsd, &tdata->bt2tctx, btkey, ret.v); + malloc_mutex_unlock(tdata->lock); + if (error) { + if (new_gctx) + prof_gctx_try_destroy(tsd, tdata, gctx, tdata); + idalloctm(tsd, ret.v, tcache, true); + return (NULL); + } + malloc_mutex_lock(gctx->lock); + ret.p->state = prof_tctx_state_nominal; + tctx_tree_insert(&gctx->tctxs, ret.p); + gctx->nlimbo--; + malloc_mutex_unlock(gctx->lock); } return (ret.p); } +void +prof_sample_threshold_update(prof_tdata_t *tdata) +{ + /* + * The body of this function is compiled out unless heap profiling is + * enabled, so that it is possible to compile jemalloc with floating + * point support completely disabled. Avoiding floating point code is + * important on memory-constrained systems, but it also enables a + * workaround for versions of glibc that don't properly save/restore + * floating point registers during dynamic lazy symbol loading (which + * internally calls into whatever malloc implementation happens to be + * integrated into the application). Note that some compilers (e.g. + * gcc 4.8) may use floating point registers for fast memory moves, so + * jemalloc must be compiled with such optimizations disabled (e.g. + * -mno-sse) in order for the workaround to be complete. + */ +#ifdef JEMALLOC_PROF + uint64_t r; + double u; + + if (!config_prof) + return; + + if (lg_prof_sample == 0) { + tdata->bytes_until_sample = 0; + return; + } + + /* + * Compute sample interval as a geometrically distributed random + * variable with mean (2^lg_prof_sample). + * + * __ __ + * | log(u) | 1 + * tdata->bytes_until_sample = | -------- |, where p = --------------- + * | log(1-p) | lg_prof_sample + * 2 + * + * For more information on the math, see: + * + * Non-Uniform Random Variate Generation + * Luc Devroye + * Springer-Verlag, New York, 1986 + * pp 500 + * (http://luc.devroye.org/rnbookindex.html) + */ + prng64(r, 53, tdata->prng_state, UINT64_C(6364136223846793005), + UINT64_C(1442695040888963407)); + u = (double)r * (1.0/9007199254740992.0L); + tdata->bytes_until_sample = (uint64_t)(log(u) / + log(1.0 - (1.0 / (double)((uint64_t)1U << lg_prof_sample)))) + + (uint64_t)1U; +#endif +} + +#ifdef JEMALLOC_JET +static prof_tdata_t * +prof_tdata_count_iter(prof_tdata_tree_t *tdatas, prof_tdata_t *tdata, void *arg) +{ + size_t *tdata_count = (size_t *)arg; + + (*tdata_count)++; + + return (NULL); +} + +size_t +prof_tdata_count(void) +{ + size_t tdata_count = 0; + + malloc_mutex_lock(&tdatas_mtx); + tdata_tree_iter(&tdatas, NULL, prof_tdata_count_iter, + (void *)&tdata_count); + malloc_mutex_unlock(&tdatas_mtx); + + return (tdata_count); +} +#endif + #ifdef JEMALLOC_JET size_t prof_bt_count(void) { size_t bt_count; - prof_tdata_t *prof_tdata; + tsd_t *tsd; + prof_tdata_t *tdata; - prof_tdata = prof_tdata_get(false); - if ((uintptr_t)prof_tdata <= (uintptr_t)PROF_TDATA_STATE_MAX) + tsd = tsd_fetch(); + tdata = prof_tdata_get(tsd, false); + if (tdata == NULL) return (0); - prof_enter(prof_tdata); - bt_count = ckh_count(&bt2ctx); - prof_leave(prof_tdata); + malloc_mutex_lock(&bt2gctx_mtx); + bt_count = ckh_count(&bt2gctx); + malloc_mutex_unlock(&bt2gctx_mtx); return (bt_count); } @@ -675,7 +926,7 @@ prof_dump_open(bool propagate_err, const char *filename) int fd; fd = creat(filename, 0644); - if (fd == -1 && propagate_err == false) { + if (fd == -1 && !propagate_err) { malloc_printf(": creat(\"%s\"), 0644) failed\n", filename); if (opt_abort) @@ -700,7 +951,7 @@ prof_dump_flush(bool propagate_err) err = write(prof_dump_fd, prof_dump_buf, prof_dump_buf_end); if (err == -1) { - if (propagate_err == false) { + if (!propagate_err) { malloc_write(": write() failed during heap " "profile flush\n"); if (opt_abort) @@ -756,7 +1007,7 @@ prof_dump_write(bool propagate_err, const char *s) return (false); } -JEMALLOC_ATTR(format(printf, 2, 3)) +JEMALLOC_FORMAT_PRINTF(2, 3) static bool prof_dump_printf(bool propagate_err, const char *format, ...) { @@ -772,176 +1023,355 @@ prof_dump_printf(bool propagate_err, const char *format, ...) return (ret); } +/* tctx->tdata->lock is held. */ static void -prof_dump_ctx_prep(prof_ctx_t *ctx, prof_cnt_t *cnt_all, size_t *leak_nctx, - prof_ctx_list_t *ctx_ql) +prof_tctx_merge_tdata(prof_tctx_t *tctx, prof_tdata_t *tdata) +{ + + malloc_mutex_lock(tctx->gctx->lock); + + switch (tctx->state) { + case prof_tctx_state_initializing: + malloc_mutex_unlock(tctx->gctx->lock); + return; + case prof_tctx_state_nominal: + tctx->state = prof_tctx_state_dumping; + malloc_mutex_unlock(tctx->gctx->lock); + + memcpy(&tctx->dump_cnts, &tctx->cnts, sizeof(prof_cnt_t)); + + tdata->cnt_summed.curobjs += tctx->dump_cnts.curobjs; + tdata->cnt_summed.curbytes += tctx->dump_cnts.curbytes; + if (opt_prof_accum) { + tdata->cnt_summed.accumobjs += + tctx->dump_cnts.accumobjs; + tdata->cnt_summed.accumbytes += + tctx->dump_cnts.accumbytes; + } + break; + case prof_tctx_state_dumping: + case prof_tctx_state_purgatory: + not_reached(); + } +} + +/* gctx->lock is held. */ +static void +prof_tctx_merge_gctx(prof_tctx_t *tctx, prof_gctx_t *gctx) +{ + + gctx->cnt_summed.curobjs += tctx->dump_cnts.curobjs; + gctx->cnt_summed.curbytes += tctx->dump_cnts.curbytes; + if (opt_prof_accum) { + gctx->cnt_summed.accumobjs += tctx->dump_cnts.accumobjs; + gctx->cnt_summed.accumbytes += tctx->dump_cnts.accumbytes; + } +} + +/* tctx->gctx is held. */ +static prof_tctx_t * +prof_tctx_merge_iter(prof_tctx_tree_t *tctxs, prof_tctx_t *tctx, void *arg) +{ + + switch (tctx->state) { + case prof_tctx_state_nominal: + /* New since dumping started; ignore. */ + break; + case prof_tctx_state_dumping: + case prof_tctx_state_purgatory: + prof_tctx_merge_gctx(tctx, tctx->gctx); + break; + default: + not_reached(); + } + + return (NULL); +} + +/* gctx->lock is held. */ +static prof_tctx_t * +prof_tctx_dump_iter(prof_tctx_tree_t *tctxs, prof_tctx_t *tctx, void *arg) +{ + bool propagate_err = *(bool *)arg; + + if (prof_dump_printf(propagate_err, + " t%"FMTu64": %"FMTu64": %"FMTu64" [%"FMTu64": %"FMTu64"]\n", + tctx->thr_uid, tctx->dump_cnts.curobjs, tctx->dump_cnts.curbytes, + tctx->dump_cnts.accumobjs, tctx->dump_cnts.accumbytes)) + return (tctx); + return (NULL); +} + +/* tctx->gctx is held. */ +static prof_tctx_t * +prof_tctx_finish_iter(prof_tctx_tree_t *tctxs, prof_tctx_t *tctx, void *arg) +{ + prof_tctx_t *ret; + + switch (tctx->state) { + case prof_tctx_state_nominal: + /* New since dumping started; ignore. */ + break; + case prof_tctx_state_dumping: + tctx->state = prof_tctx_state_nominal; + break; + case prof_tctx_state_purgatory: + ret = tctx; + goto label_return; + default: + not_reached(); + } + + ret = NULL; +label_return: + return (ret); +} + +static void +prof_dump_gctx_prep(prof_gctx_t *gctx, prof_gctx_tree_t *gctxs) { - prof_thr_cnt_t *thr_cnt; - prof_cnt_t tcnt; cassert(config_prof); - malloc_mutex_lock(ctx->lock); + malloc_mutex_lock(gctx->lock); /* - * Increment nlimbo so that ctx won't go away before dump. - * Additionally, link ctx into the dump list so that it is included in + * Increment nlimbo so that gctx won't go away before dump. + * Additionally, link gctx into the dump list so that it is included in * prof_dump()'s second pass. */ - ctx->nlimbo++; - ql_tail_insert(ctx_ql, ctx, dump_link); + gctx->nlimbo++; + gctx_tree_insert(gctxs, gctx); - memcpy(&ctx->cnt_summed, &ctx->cnt_merged, sizeof(prof_cnt_t)); - ql_foreach(thr_cnt, &ctx->cnts_ql, cnts_link) { - volatile unsigned *epoch = &thr_cnt->epoch; + memset(&gctx->cnt_summed, 0, sizeof(prof_cnt_t)); - while (true) { - unsigned epoch0 = *epoch; - - /* Make sure epoch is even. */ - if (epoch0 & 1U) - continue; - - memcpy(&tcnt, &thr_cnt->cnts, sizeof(prof_cnt_t)); - - /* Terminate if epoch didn't change while reading. */ - if (*epoch == epoch0) - break; - } - - ctx->cnt_summed.curobjs += tcnt.curobjs; - ctx->cnt_summed.curbytes += tcnt.curbytes; - if (opt_prof_accum) { - ctx->cnt_summed.accumobjs += tcnt.accumobjs; - ctx->cnt_summed.accumbytes += tcnt.accumbytes; - } - } - - if (ctx->cnt_summed.curobjs != 0) - (*leak_nctx)++; - - /* Add to cnt_all. */ - cnt_all->curobjs += ctx->cnt_summed.curobjs; - cnt_all->curbytes += ctx->cnt_summed.curbytes; - if (opt_prof_accum) { - cnt_all->accumobjs += ctx->cnt_summed.accumobjs; - cnt_all->accumbytes += ctx->cnt_summed.accumbytes; - } - - malloc_mutex_unlock(ctx->lock); + malloc_mutex_unlock(gctx->lock); } +static prof_gctx_t * +prof_gctx_merge_iter(prof_gctx_tree_t *gctxs, prof_gctx_t *gctx, void *arg) +{ + size_t *leak_ngctx = (size_t *)arg; + + malloc_mutex_lock(gctx->lock); + tctx_tree_iter(&gctx->tctxs, NULL, prof_tctx_merge_iter, NULL); + if (gctx->cnt_summed.curobjs != 0) + (*leak_ngctx)++; + malloc_mutex_unlock(gctx->lock); + + return (NULL); +} + +static void +prof_gctx_finish(tsd_t *tsd, prof_gctx_tree_t *gctxs) +{ + prof_tdata_t *tdata = prof_tdata_get(tsd, false); + prof_gctx_t *gctx; + + /* + * Standard tree iteration won't work here, because as soon as we + * decrement gctx->nlimbo and unlock gctx, another thread can + * concurrently destroy it, which will corrupt the tree. Therefore, + * tear down the tree one node at a time during iteration. + */ + while ((gctx = gctx_tree_first(gctxs)) != NULL) { + gctx_tree_remove(gctxs, gctx); + malloc_mutex_lock(gctx->lock); + { + prof_tctx_t *next; + + next = NULL; + do { + prof_tctx_t *to_destroy = + tctx_tree_iter(&gctx->tctxs, next, + prof_tctx_finish_iter, NULL); + if (to_destroy != NULL) { + next = tctx_tree_next(&gctx->tctxs, + to_destroy); + tctx_tree_remove(&gctx->tctxs, + to_destroy); + idalloctm(tsd, to_destroy, + tcache_get(tsd, false), true); + } else + next = NULL; + } while (next != NULL); + } + gctx->nlimbo--; + if (prof_gctx_should_destroy(gctx)) { + gctx->nlimbo++; + malloc_mutex_unlock(gctx->lock); + prof_gctx_try_destroy(tsd, tdata, gctx, tdata); + } else + malloc_mutex_unlock(gctx->lock); + } +} + +static prof_tdata_t * +prof_tdata_merge_iter(prof_tdata_tree_t *tdatas, prof_tdata_t *tdata, void *arg) +{ + prof_cnt_t *cnt_all = (prof_cnt_t *)arg; + + malloc_mutex_lock(tdata->lock); + if (!tdata->expired) { + size_t tabind; + union { + prof_tctx_t *p; + void *v; + } tctx; + + tdata->dumping = true; + memset(&tdata->cnt_summed, 0, sizeof(prof_cnt_t)); + for (tabind = 0; !ckh_iter(&tdata->bt2tctx, &tabind, NULL, + &tctx.v);) + prof_tctx_merge_tdata(tctx.p, tdata); + + cnt_all->curobjs += tdata->cnt_summed.curobjs; + cnt_all->curbytes += tdata->cnt_summed.curbytes; + if (opt_prof_accum) { + cnt_all->accumobjs += tdata->cnt_summed.accumobjs; + cnt_all->accumbytes += tdata->cnt_summed.accumbytes; + } + } else + tdata->dumping = false; + malloc_mutex_unlock(tdata->lock); + + return (NULL); +} + +static prof_tdata_t * +prof_tdata_dump_iter(prof_tdata_tree_t *tdatas, prof_tdata_t *tdata, void *arg) +{ + bool propagate_err = *(bool *)arg; + + if (!tdata->dumping) + return (NULL); + + if (prof_dump_printf(propagate_err, + " t%"FMTu64": %"FMTu64": %"FMTu64" [%"FMTu64": %"FMTu64"]%s%s\n", + tdata->thr_uid, tdata->cnt_summed.curobjs, + tdata->cnt_summed.curbytes, tdata->cnt_summed.accumobjs, + tdata->cnt_summed.accumbytes, + (tdata->thread_name != NULL) ? " " : "", + (tdata->thread_name != NULL) ? tdata->thread_name : "")) + return (tdata); + return (NULL); +} + +#ifdef JEMALLOC_JET +#undef prof_dump_header +#define prof_dump_header JEMALLOC_N(prof_dump_header_impl) +#endif static bool prof_dump_header(bool propagate_err, const prof_cnt_t *cnt_all) { + bool ret; - if (opt_lg_prof_sample == 0) { - if (prof_dump_printf(propagate_err, - "heap profile: %"PRId64": %"PRId64 - " [%"PRIu64": %"PRIu64"] @ heapprofile\n", - cnt_all->curobjs, cnt_all->curbytes, - cnt_all->accumobjs, cnt_all->accumbytes)) - return (true); - } else { - if (prof_dump_printf(propagate_err, - "heap profile: %"PRId64": %"PRId64 - " [%"PRIu64": %"PRIu64"] @ heap_v2/%"PRIu64"\n", - cnt_all->curobjs, cnt_all->curbytes, - cnt_all->accumobjs, cnt_all->accumbytes, - ((uint64_t)1U << opt_lg_prof_sample))) - return (true); - } + if (prof_dump_printf(propagate_err, + "heap_v2/%"FMTu64"\n" + " t*: %"FMTu64": %"FMTu64" [%"FMTu64": %"FMTu64"]\n", + ((uint64_t)1U << lg_prof_sample), cnt_all->curobjs, + cnt_all->curbytes, cnt_all->accumobjs, cnt_all->accumbytes)) + return (true); - return (false); -} - -static void -prof_dump_ctx_cleanup_locked(prof_ctx_t *ctx, prof_ctx_list_t *ctx_ql) -{ - - ctx->nlimbo--; - ql_remove(ctx_ql, ctx, dump_link); -} - -static void -prof_dump_ctx_cleanup(prof_ctx_t *ctx, prof_ctx_list_t *ctx_ql) -{ - - malloc_mutex_lock(ctx->lock); - prof_dump_ctx_cleanup_locked(ctx, ctx_ql); - malloc_mutex_unlock(ctx->lock); + malloc_mutex_lock(&tdatas_mtx); + ret = (tdata_tree_iter(&tdatas, NULL, prof_tdata_dump_iter, + (void *)&propagate_err) != NULL); + malloc_mutex_unlock(&tdatas_mtx); + return (ret); } +#ifdef JEMALLOC_JET +#undef prof_dump_header +#define prof_dump_header JEMALLOC_N(prof_dump_header) +prof_dump_header_t *prof_dump_header = JEMALLOC_N(prof_dump_header_impl); +#endif +/* gctx->lock is held. */ static bool -prof_dump_ctx(bool propagate_err, prof_ctx_t *ctx, const prof_bt_t *bt, - prof_ctx_list_t *ctx_ql) +prof_dump_gctx(bool propagate_err, prof_gctx_t *gctx, const prof_bt_t *bt, + prof_gctx_tree_t *gctxs) { bool ret; unsigned i; cassert(config_prof); - /* - * Current statistics can sum to 0 as a result of unmerged per thread - * statistics. Additionally, interval- and growth-triggered dumps can - * occur between the time a ctx is created and when its statistics are - * filled in. Avoid dumping any ctx that is an artifact of either - * implementation detail. - */ - malloc_mutex_lock(ctx->lock); - if ((opt_prof_accum == false && ctx->cnt_summed.curobjs == 0) || - (opt_prof_accum && ctx->cnt_summed.accumobjs == 0)) { - assert(ctx->cnt_summed.curobjs == 0); - assert(ctx->cnt_summed.curbytes == 0); - assert(ctx->cnt_summed.accumobjs == 0); - assert(ctx->cnt_summed.accumbytes == 0); + /* Avoid dumping such gctx's that have no useful data. */ + if ((!opt_prof_accum && gctx->cnt_summed.curobjs == 0) || + (opt_prof_accum && gctx->cnt_summed.accumobjs == 0)) { + assert(gctx->cnt_summed.curobjs == 0); + assert(gctx->cnt_summed.curbytes == 0); + assert(gctx->cnt_summed.accumobjs == 0); + assert(gctx->cnt_summed.accumbytes == 0); ret = false; goto label_return; } - if (prof_dump_printf(propagate_err, "%"PRId64": %"PRId64 - " [%"PRIu64": %"PRIu64"] @", - ctx->cnt_summed.curobjs, ctx->cnt_summed.curbytes, - ctx->cnt_summed.accumobjs, ctx->cnt_summed.accumbytes)) { + if (prof_dump_printf(propagate_err, "@")) { ret = true; goto label_return; } - for (i = 0; i < bt->len; i++) { - if (prof_dump_printf(propagate_err, " %#"PRIxPTR, + if (prof_dump_printf(propagate_err, " %#"FMTxPTR, (uintptr_t)bt->vec[i])) { ret = true; goto label_return; } } - if (prof_dump_write(propagate_err, "\n")) { + if (prof_dump_printf(propagate_err, + "\n" + " t*: %"FMTu64": %"FMTu64" [%"FMTu64": %"FMTu64"]\n", + gctx->cnt_summed.curobjs, gctx->cnt_summed.curbytes, + gctx->cnt_summed.accumobjs, gctx->cnt_summed.accumbytes)) { + ret = true; + goto label_return; + } + + if (tctx_tree_iter(&gctx->tctxs, NULL, prof_tctx_dump_iter, + (void *)&propagate_err) != NULL) { ret = true; goto label_return; } ret = false; label_return: - prof_dump_ctx_cleanup_locked(ctx, ctx_ql); - malloc_mutex_unlock(ctx->lock); return (ret); } +JEMALLOC_FORMAT_PRINTF(1, 2) +static int +prof_open_maps(const char *format, ...) +{ + int mfd; + va_list ap; + char filename[PATH_MAX + 1]; + + va_start(ap, format); + malloc_vsnprintf(filename, sizeof(filename), format, ap); + va_end(ap); + mfd = open(filename, O_RDONLY); + + return (mfd); +} + static bool prof_dump_maps(bool propagate_err) { bool ret; int mfd; - char filename[PATH_MAX + 1]; cassert(config_prof); #ifdef __FreeBSD__ - malloc_snprintf(filename, sizeof(filename), "/proc/curproc/map"); + mfd = prof_open_maps("/proc/curproc/map"); #else - malloc_snprintf(filename, sizeof(filename), "/proc/%d/maps", - (int)getpid()); + { + int pid = getpid(); + + mfd = prof_open_maps("/proc/%d/task/%d/maps", pid, pid); + if (mfd == -1) + mfd = prof_open_maps("/proc/%d/maps", pid); + } #endif - mfd = open(filename, O_RDONLY); if (mfd != -1) { ssize_t nread; @@ -977,51 +1407,85 @@ prof_dump_maps(bool propagate_err) } static void -prof_leakcheck(const prof_cnt_t *cnt_all, size_t leak_nctx, +prof_leakcheck(const prof_cnt_t *cnt_all, size_t leak_ngctx, const char *filename) { if (cnt_all->curbytes != 0) { - malloc_printf(": Leak summary: %"PRId64" byte%s, %" - PRId64" object%s, %zu context%s\n", + malloc_printf(": Leak summary: %"FMTu64" byte%s, %" + FMTu64" object%s, %zu context%s\n", cnt_all->curbytes, (cnt_all->curbytes != 1) ? "s" : "", cnt_all->curobjs, (cnt_all->curobjs != 1) ? "s" : "", - leak_nctx, (leak_nctx != 1) ? "s" : ""); + leak_ngctx, (leak_ngctx != 1) ? "s" : ""); malloc_printf( - ": Run pprof on \"%s\" for leak detail\n", + ": Run jeprof on \"%s\" for leak detail\n", filename); } } -static bool -prof_dump(bool propagate_err, const char *filename, bool leakcheck) +static prof_gctx_t * +prof_gctx_dump_iter(prof_gctx_tree_t *gctxs, prof_gctx_t *gctx, void *arg) { - prof_tdata_t *prof_tdata; + prof_gctx_t *ret; + bool propagate_err = *(bool *)arg; + + malloc_mutex_lock(gctx->lock); + + if (prof_dump_gctx(propagate_err, gctx, &gctx->bt, gctxs)) { + ret = gctx; + goto label_return; + } + + ret = NULL; +label_return: + malloc_mutex_unlock(gctx->lock); + return (ret); +} + +static bool +prof_dump(tsd_t *tsd, bool propagate_err, const char *filename, bool leakcheck) +{ + prof_tdata_t *tdata; prof_cnt_t cnt_all; size_t tabind; union { - prof_ctx_t *p; + prof_gctx_t *p; void *v; - } ctx; - size_t leak_nctx; - prof_ctx_list_t ctx_ql; + } gctx; + size_t leak_ngctx; + prof_gctx_tree_t gctxs; cassert(config_prof); - prof_tdata = prof_tdata_get(false); - if ((uintptr_t)prof_tdata <= (uintptr_t)PROF_TDATA_STATE_MAX) + tdata = prof_tdata_get(tsd, true); + if (tdata == NULL) return (true); malloc_mutex_lock(&prof_dump_mtx); + prof_enter(tsd, tdata); - /* Merge per thread profile stats, and sum them in cnt_all. */ + /* + * Put gctx's in limbo and clear their counters in preparation for + * summing. + */ + gctx_tree_new(&gctxs); + for (tabind = 0; !ckh_iter(&bt2gctx, &tabind, NULL, &gctx.v);) + prof_dump_gctx_prep(gctx.p, &gctxs); + + /* + * Iterate over tdatas, and for the non-expired ones snapshot their tctx + * stats and merge them into the associated gctx's. + */ memset(&cnt_all, 0, sizeof(prof_cnt_t)); - leak_nctx = 0; - ql_new(&ctx_ql); - prof_enter(prof_tdata); - for (tabind = 0; ckh_iter(&bt2ctx, &tabind, NULL, &ctx.v) == false;) - prof_dump_ctx_prep(ctx.p, &cnt_all, &leak_nctx, &ctx_ql); - prof_leave(prof_tdata); + malloc_mutex_lock(&tdatas_mtx); + tdata_tree_iter(&tdatas, NULL, prof_tdata_merge_iter, (void *)&cnt_all); + malloc_mutex_unlock(&tdatas_mtx); + + /* Merge tctx stats into gctx's. */ + leak_ngctx = 0; + gctx_tree_iter(&gctxs, NULL, prof_gctx_merge_iter, (void *)&leak_ngctx); + + prof_leave(tsd, tdata); /* Create dump file. */ if ((prof_dump_fd = prof_dump_open(propagate_err, filename)) == -1) @@ -1031,11 +1495,10 @@ prof_dump(bool propagate_err, const char *filename, bool leakcheck) if (prof_dump_header(propagate_err, &cnt_all)) goto label_write_error; - /* Dump per ctx profile stats. */ - while ((ctx.p = ql_first(&ctx_ql)) != NULL) { - if (prof_dump_ctx(propagate_err, ctx.p, ctx.p->bt, &ctx_ql)) - goto label_write_error; - } + /* Dump per gctx profile stats. */ + if (gctx_tree_iter(&gctxs, NULL, prof_gctx_dump_iter, + (void *)&propagate_err) != NULL) + goto label_write_error; /* Dump /proc//maps if possible. */ if (prof_dump_maps(propagate_err)) @@ -1044,17 +1507,17 @@ prof_dump(bool propagate_err, const char *filename, bool leakcheck) if (prof_dump_close(propagate_err)) goto label_open_close_error; + prof_gctx_finish(tsd, &gctxs); malloc_mutex_unlock(&prof_dump_mtx); if (leakcheck) - prof_leakcheck(&cnt_all, leak_nctx, filename); + prof_leakcheck(&cnt_all, leak_ngctx, filename); return (false); label_write_error: prof_dump_close(propagate_err); label_open_close_error: - while ((ctx.p = ql_first(&ctx_ql)) != NULL) - prof_dump_ctx_cleanup(ctx.p, &ctx_ql); + prof_gctx_finish(tsd, &gctxs); malloc_mutex_unlock(&prof_dump_mtx); return (true); } @@ -1062,7 +1525,7 @@ prof_dump(bool propagate_err, const char *filename, bool leakcheck) #define DUMP_FILENAME_BUFSIZE (PATH_MAX + 1) #define VSEQ_INVALID UINT64_C(0xffffffffffffffff) static void -prof_dump_filename(char *filename, char v, int64_t vseq) +prof_dump_filename(char *filename, char v, uint64_t vseq) { cassert(config_prof); @@ -1070,12 +1533,12 @@ prof_dump_filename(char *filename, char v, int64_t vseq) if (vseq != VSEQ_INVALID) { /* "...v.heap" */ malloc_snprintf(filename, DUMP_FILENAME_BUFSIZE, - "%s.%d.%"PRIu64".%c%"PRId64".heap", + "%s.%d.%"FMTu64".%c%"FMTu64".heap", opt_prof_prefix, (int)getpid(), prof_dump_seq, v, vseq); } else { /* "....heap" */ malloc_snprintf(filename, DUMP_FILENAME_BUFSIZE, - "%s.%d.%"PRIu64".%c.heap", + "%s.%d.%"FMTu64".%c.heap", opt_prof_prefix, (int)getpid(), prof_dump_seq, v); } prof_dump_seq++; @@ -1084,36 +1547,40 @@ prof_dump_filename(char *filename, char v, int64_t vseq) static void prof_fdump(void) { + tsd_t *tsd; char filename[DUMP_FILENAME_BUFSIZE]; cassert(config_prof); + assert(opt_prof_final); + assert(opt_prof_prefix[0] != '\0'); - if (prof_booted == false) + if (!prof_booted) return; + tsd = tsd_fetch(); - if (opt_prof_final && opt_prof_prefix[0] != '\0') { - malloc_mutex_lock(&prof_dump_seq_mtx); - prof_dump_filename(filename, 'f', VSEQ_INVALID); - malloc_mutex_unlock(&prof_dump_seq_mtx); - prof_dump(false, filename, opt_prof_leak); - } + malloc_mutex_lock(&prof_dump_seq_mtx); + prof_dump_filename(filename, 'f', VSEQ_INVALID); + malloc_mutex_unlock(&prof_dump_seq_mtx); + prof_dump(tsd, false, filename, opt_prof_leak); } void prof_idump(void) { - prof_tdata_t *prof_tdata; + tsd_t *tsd; + prof_tdata_t *tdata; char filename[PATH_MAX + 1]; cassert(config_prof); - if (prof_booted == false) + if (!prof_booted) return; - prof_tdata = prof_tdata_get(false); - if ((uintptr_t)prof_tdata <= (uintptr_t)PROF_TDATA_STATE_MAX) + tsd = tsd_fetch(); + tdata = prof_tdata_get(tsd, false); + if (tdata == NULL) return; - if (prof_tdata->enq) { - prof_tdata->enq_idump = true; + if (tdata->enq) { + tdata->enq_idump = true; return; } @@ -1122,19 +1589,21 @@ prof_idump(void) prof_dump_filename(filename, 'i', prof_dump_iseq); prof_dump_iseq++; malloc_mutex_unlock(&prof_dump_seq_mtx); - prof_dump(false, filename, false); + prof_dump(tsd, false, filename, false); } } bool prof_mdump(const char *filename) { + tsd_t *tsd; char filename_buf[DUMP_FILENAME_BUFSIZE]; cassert(config_prof); - if (opt_prof == false || prof_booted == false) + if (!opt_prof || !prof_booted) return (true); + tsd = tsd_fetch(); if (filename == NULL) { /* No filename specified, so automatically generate one. */ @@ -1146,24 +1615,26 @@ prof_mdump(const char *filename) malloc_mutex_unlock(&prof_dump_seq_mtx); filename = filename_buf; } - return (prof_dump(true, filename, false)); + return (prof_dump(tsd, true, filename, false)); } void prof_gdump(void) { - prof_tdata_t *prof_tdata; + tsd_t *tsd; + prof_tdata_t *tdata; char filename[DUMP_FILENAME_BUFSIZE]; cassert(config_prof); - if (prof_booted == false) + if (!prof_booted) return; - prof_tdata = prof_tdata_get(false); - if ((uintptr_t)prof_tdata <= (uintptr_t)PROF_TDATA_STATE_MAX) + tsd = tsd_fetch(); + tdata = prof_tdata_get(tsd, false); + if (tdata == NULL) return; - if (prof_tdata->enq) { - prof_tdata->enq_gdump = true; + if (tdata->enq) { + tdata->enq_gdump = true; return; } @@ -1172,7 +1643,7 @@ prof_gdump(void) prof_dump_filename(filename, 'u', prof_dump_useq); prof_dump_useq++; malloc_mutex_unlock(&prof_dump_seq_mtx); - prof_dump(false, filename, false); + prof_dump(tsd, false, filename, false); } } @@ -1199,88 +1670,375 @@ prof_bt_keycomp(const void *k1, const void *k2) return (memcmp(bt1->vec, bt2->vec, bt1->len * sizeof(void *)) == 0); } -prof_tdata_t * -prof_tdata_init(void) +JEMALLOC_INLINE_C uint64_t +prof_thr_uid_alloc(void) { - prof_tdata_t *prof_tdata; + uint64_t thr_uid; + + malloc_mutex_lock(&next_thr_uid_mtx); + thr_uid = next_thr_uid; + next_thr_uid++; + malloc_mutex_unlock(&next_thr_uid_mtx); + + return (thr_uid); +} + +static prof_tdata_t * +prof_tdata_init_impl(tsd_t *tsd, uint64_t thr_uid, uint64_t thr_discrim, + char *thread_name, bool active) +{ + prof_tdata_t *tdata; + tcache_t *tcache; cassert(config_prof); /* Initialize an empty cache for this thread. */ - prof_tdata = (prof_tdata_t *)imalloc(sizeof(prof_tdata_t)); - if (prof_tdata == NULL) + tcache = tcache_get(tsd, true); + tdata = (prof_tdata_t *)iallocztm(tsd, sizeof(prof_tdata_t), false, + tcache, true, NULL); + if (tdata == NULL) return (NULL); - if (ckh_new(&prof_tdata->bt2cnt, PROF_CKH_MINITEMS, + tdata->lock = prof_tdata_mutex_choose(thr_uid); + tdata->thr_uid = thr_uid; + tdata->thr_discrim = thr_discrim; + tdata->thread_name = thread_name; + tdata->attached = true; + tdata->expired = false; + tdata->tctx_uid_next = 0; + + if (ckh_new(tsd, &tdata->bt2tctx, PROF_CKH_MINITEMS, prof_bt_hash, prof_bt_keycomp)) { - idalloc(prof_tdata); - return (NULL); - } - ql_new(&prof_tdata->lru_ql); - - prof_tdata->vec = imalloc(sizeof(void *) * PROF_BT_MAX); - if (prof_tdata->vec == NULL) { - ckh_delete(&prof_tdata->bt2cnt); - idalloc(prof_tdata); + idalloctm(tsd, tdata, tcache, true); return (NULL); } - prof_tdata->prng_state = 0; - prof_tdata->threshold = 0; - prof_tdata->accum = 0; + tdata->prng_state = (uint64_t)(uintptr_t)tdata; + prof_sample_threshold_update(tdata); - prof_tdata->enq = false; - prof_tdata->enq_idump = false; - prof_tdata->enq_gdump = false; + tdata->enq = false; + tdata->enq_idump = false; + tdata->enq_gdump = false; - prof_tdata_tsd_set(&prof_tdata); + tdata->dumping = false; + tdata->active = active; - return (prof_tdata); + malloc_mutex_lock(&tdatas_mtx); + tdata_tree_insert(&tdatas, tdata); + malloc_mutex_unlock(&tdatas_mtx); + + return (tdata); +} + +prof_tdata_t * +prof_tdata_init(tsd_t *tsd) +{ + + return (prof_tdata_init_impl(tsd, prof_thr_uid_alloc(), 0, NULL, + prof_thread_active_init_get())); +} + +/* tdata->lock must be held. */ +static bool +prof_tdata_should_destroy(prof_tdata_t *tdata, bool even_if_attached) +{ + + if (tdata->attached && !even_if_attached) + return (false); + if (ckh_count(&tdata->bt2tctx) != 0) + return (false); + return (true); +} + +/* tdatas_mtx must be held. */ +static void +prof_tdata_destroy_locked(tsd_t *tsd, prof_tdata_t *tdata, + bool even_if_attached) +{ + tcache_t *tcache; + + assert(prof_tdata_should_destroy(tdata, even_if_attached)); + assert(tsd_prof_tdata_get(tsd) != tdata); + + tdata_tree_remove(&tdatas, tdata); + + tcache = tcache_get(tsd, false); + if (tdata->thread_name != NULL) + idalloctm(tsd, tdata->thread_name, tcache, true); + ckh_delete(tsd, &tdata->bt2tctx); + idalloctm(tsd, tdata, tcache, true); +} + +static void +prof_tdata_destroy(tsd_t *tsd, prof_tdata_t *tdata, bool even_if_attached) +{ + + malloc_mutex_lock(&tdatas_mtx); + prof_tdata_destroy_locked(tsd, tdata, even_if_attached); + malloc_mutex_unlock(&tdatas_mtx); +} + +static void +prof_tdata_detach(tsd_t *tsd, prof_tdata_t *tdata) +{ + bool destroy_tdata; + + malloc_mutex_lock(tdata->lock); + if (tdata->attached) { + destroy_tdata = prof_tdata_should_destroy(tdata, true); + /* + * Only detach if !destroy_tdata, because detaching would allow + * another thread to win the race to destroy tdata. + */ + if (!destroy_tdata) + tdata->attached = false; + tsd_prof_tdata_set(tsd, NULL); + } else + destroy_tdata = false; + malloc_mutex_unlock(tdata->lock); + if (destroy_tdata) + prof_tdata_destroy(tsd, tdata, true); +} + +prof_tdata_t * +prof_tdata_reinit(tsd_t *tsd, prof_tdata_t *tdata) +{ + uint64_t thr_uid = tdata->thr_uid; + uint64_t thr_discrim = tdata->thr_discrim + 1; + char *thread_name = (tdata->thread_name != NULL) ? + prof_thread_name_alloc(tsd, tdata->thread_name) : NULL; + bool active = tdata->active; + + prof_tdata_detach(tsd, tdata); + return (prof_tdata_init_impl(tsd, thr_uid, thr_discrim, thread_name, + active)); +} + +static bool +prof_tdata_expire(prof_tdata_t *tdata) +{ + bool destroy_tdata; + + malloc_mutex_lock(tdata->lock); + if (!tdata->expired) { + tdata->expired = true; + destroy_tdata = tdata->attached ? false : + prof_tdata_should_destroy(tdata, false); + } else + destroy_tdata = false; + malloc_mutex_unlock(tdata->lock); + + return (destroy_tdata); +} + +static prof_tdata_t * +prof_tdata_reset_iter(prof_tdata_tree_t *tdatas, prof_tdata_t *tdata, void *arg) +{ + + return (prof_tdata_expire(tdata) ? tdata : NULL); } void -prof_tdata_cleanup(void *arg) +prof_reset(tsd_t *tsd, size_t lg_sample) { - prof_thr_cnt_t *cnt; - prof_tdata_t *prof_tdata = *(prof_tdata_t **)arg; + prof_tdata_t *next; - cassert(config_prof); + assert(lg_sample < (sizeof(uint64_t) << 3)); - if (prof_tdata == PROF_TDATA_STATE_REINCARNATED) { - /* - * Another destructor deallocated memory after this destructor - * was called. Reset prof_tdata to PROF_TDATA_STATE_PURGATORY - * in order to receive another callback. - */ - prof_tdata = PROF_TDATA_STATE_PURGATORY; - prof_tdata_tsd_set(&prof_tdata); - } else if (prof_tdata == PROF_TDATA_STATE_PURGATORY) { - /* - * The previous time this destructor was called, we set the key - * to PROF_TDATA_STATE_PURGATORY so that other destructors - * wouldn't cause re-creation of the prof_tdata. This time, do - * nothing, so that the destructor will not be called again. - */ - } else if (prof_tdata != NULL) { - /* - * Delete the hash table. All of its contents can still be - * iterated over via the LRU. - */ - ckh_delete(&prof_tdata->bt2cnt); - /* - * Iteratively merge cnt's into the global stats and delete - * them. - */ - while ((cnt = ql_last(&prof_tdata->lru_ql, lru_link)) != NULL) { - ql_remove(&prof_tdata->lru_ql, cnt, lru_link); - prof_ctx_merge(cnt->ctx, cnt); - idalloc(cnt); - } - idalloc(prof_tdata->vec); - idalloc(prof_tdata); - prof_tdata = PROF_TDATA_STATE_PURGATORY; - prof_tdata_tsd_set(&prof_tdata); + malloc_mutex_lock(&prof_dump_mtx); + malloc_mutex_lock(&tdatas_mtx); + + lg_prof_sample = lg_sample; + + next = NULL; + do { + prof_tdata_t *to_destroy = tdata_tree_iter(&tdatas, next, + prof_tdata_reset_iter, NULL); + if (to_destroy != NULL) { + next = tdata_tree_next(&tdatas, to_destroy); + prof_tdata_destroy_locked(tsd, to_destroy, false); + } else + next = NULL; + } while (next != NULL); + + malloc_mutex_unlock(&tdatas_mtx); + malloc_mutex_unlock(&prof_dump_mtx); +} + +void +prof_tdata_cleanup(tsd_t *tsd) +{ + prof_tdata_t *tdata; + + if (!config_prof) + return; + + tdata = tsd_prof_tdata_get(tsd); + if (tdata != NULL) + prof_tdata_detach(tsd, tdata); +} + +bool +prof_active_get(void) +{ + bool prof_active_current; + + malloc_mutex_lock(&prof_active_mtx); + prof_active_current = prof_active; + malloc_mutex_unlock(&prof_active_mtx); + return (prof_active_current); +} + +bool +prof_active_set(bool active) +{ + bool prof_active_old; + + malloc_mutex_lock(&prof_active_mtx); + prof_active_old = prof_active; + prof_active = active; + malloc_mutex_unlock(&prof_active_mtx); + return (prof_active_old); +} + +const char * +prof_thread_name_get(void) +{ + tsd_t *tsd; + prof_tdata_t *tdata; + + tsd = tsd_fetch(); + tdata = prof_tdata_get(tsd, true); + if (tdata == NULL) + return (""); + return (tdata->thread_name != NULL ? tdata->thread_name : ""); +} + +static char * +prof_thread_name_alloc(tsd_t *tsd, const char *thread_name) +{ + char *ret; + size_t size; + + if (thread_name == NULL) + return (NULL); + + size = strlen(thread_name) + 1; + if (size == 1) + return (""); + + ret = iallocztm(tsd, size, false, tcache_get(tsd, true), true, NULL); + if (ret == NULL) + return (NULL); + memcpy(ret, thread_name, size); + return (ret); +} + +int +prof_thread_name_set(tsd_t *tsd, const char *thread_name) +{ + prof_tdata_t *tdata; + unsigned i; + char *s; + + tdata = prof_tdata_get(tsd, true); + if (tdata == NULL) + return (EAGAIN); + + /* Validate input. */ + if (thread_name == NULL) + return (EFAULT); + for (i = 0; thread_name[i] != '\0'; i++) { + char c = thread_name[i]; + if (!isgraph(c) && !isblank(c)) + return (EFAULT); } + + s = prof_thread_name_alloc(tsd, thread_name); + if (s == NULL) + return (EAGAIN); + + if (tdata->thread_name != NULL) { + idalloctm(tsd, tdata->thread_name, tcache_get(tsd, false), + true); + tdata->thread_name = NULL; + } + if (strlen(s) > 0) + tdata->thread_name = s; + return (0); +} + +bool +prof_thread_active_get(void) +{ + tsd_t *tsd; + prof_tdata_t *tdata; + + tsd = tsd_fetch(); + tdata = prof_tdata_get(tsd, true); + if (tdata == NULL) + return (false); + return (tdata->active); +} + +bool +prof_thread_active_set(bool active) +{ + tsd_t *tsd; + prof_tdata_t *tdata; + + tsd = tsd_fetch(); + tdata = prof_tdata_get(tsd, true); + if (tdata == NULL) + return (true); + tdata->active = active; + return (false); +} + +bool +prof_thread_active_init_get(void) +{ + bool active_init; + + malloc_mutex_lock(&prof_thread_active_init_mtx); + active_init = prof_thread_active_init; + malloc_mutex_unlock(&prof_thread_active_init_mtx); + return (active_init); +} + +bool +prof_thread_active_init_set(bool active_init) +{ + bool active_init_old; + + malloc_mutex_lock(&prof_thread_active_init_mtx); + active_init_old = prof_thread_active_init; + prof_thread_active_init = active_init; + malloc_mutex_unlock(&prof_thread_active_init_mtx); + return (active_init_old); +} + +bool +prof_gdump_get(void) +{ + bool prof_gdump_current; + + malloc_mutex_lock(&prof_gdump_mtx); + prof_gdump_current = prof_gdump_val; + malloc_mutex_unlock(&prof_gdump_mtx); + return (prof_gdump_current); +} + +bool +prof_gdump_set(bool gdump) +{ + bool prof_gdump_old; + + malloc_mutex_lock(&prof_gdump_mtx); + prof_gdump_old = prof_gdump_val; + prof_gdump_val = gdump; + malloc_mutex_unlock(&prof_gdump_mtx); + return (prof_gdump_old); } void @@ -1300,11 +2058,11 @@ prof_boot1(void) cassert(config_prof); /* - * opt_prof and prof_promote must be in their final state before any - * arenas are initialized, so this function must be executed early. + * opt_prof must be in its final state before any arenas are + * initialized, so this function must be executed early. */ - if (opt_prof_leak && opt_prof == false) { + if (opt_prof_leak && !opt_prof) { /* * Enable opt_prof, but in such a way that profiles are never * automatically dumped. @@ -1317,8 +2075,6 @@ prof_boot1(void) opt_lg_prof_interval); } } - - prof_promote = (opt_prof && opt_lg_prof_sample > LG_PAGE); } bool @@ -1328,36 +2084,65 @@ prof_boot2(void) cassert(config_prof); if (opt_prof) { + tsd_t *tsd; unsigned i; - if (ckh_new(&bt2ctx, PROF_CKH_MINITEMS, prof_bt_hash, + lg_prof_sample = opt_lg_prof_sample; + + prof_active = opt_prof_active; + if (malloc_mutex_init(&prof_active_mtx)) + return (true); + + prof_gdump_val = opt_prof_gdump; + if (malloc_mutex_init(&prof_gdump_mtx)) + return (true); + + prof_thread_active_init = opt_prof_thread_active_init; + if (malloc_mutex_init(&prof_thread_active_init_mtx)) + return (true); + + tsd = tsd_fetch(); + if (ckh_new(tsd, &bt2gctx, PROF_CKH_MINITEMS, prof_bt_hash, prof_bt_keycomp)) return (true); - if (malloc_mutex_init(&bt2ctx_mtx)) + if (malloc_mutex_init(&bt2gctx_mtx)) + return (true); + + tdata_tree_new(&tdatas); + if (malloc_mutex_init(&tdatas_mtx)) + return (true); + + next_thr_uid = 0; + if (malloc_mutex_init(&next_thr_uid_mtx)) return (true); - if (prof_tdata_tsd_boot()) { - malloc_write( - ": Error in pthread_key_create()\n"); - abort(); - } if (malloc_mutex_init(&prof_dump_seq_mtx)) return (true); if (malloc_mutex_init(&prof_dump_mtx)) return (true); - if (atexit(prof_fdump) != 0) { + if (opt_prof_final && opt_prof_prefix[0] != '\0' && + atexit(prof_fdump) != 0) { malloc_write(": Error in atexit()\n"); if (opt_abort) abort(); } - ctx_locks = (malloc_mutex_t *)base_alloc(PROF_NCTX_LOCKS * + gctx_locks = (malloc_mutex_t *)base_alloc(PROF_NCTX_LOCKS * sizeof(malloc_mutex_t)); - if (ctx_locks == NULL) + if (gctx_locks == NULL) return (true); for (i = 0; i < PROF_NCTX_LOCKS; i++) { - if (malloc_mutex_init(&ctx_locks[i])) + if (malloc_mutex_init(&gctx_locks[i])) + return (true); + } + + tdata_locks = (malloc_mutex_t *)base_alloc(PROF_NTDATA_LOCKS * + sizeof(malloc_mutex_t)); + if (tdata_locks == NULL) + return (true); + for (i = 0; i < PROF_NTDATA_LOCKS; i++) { + if (malloc_mutex_init(&tdata_locks[i])) return (true); } } @@ -1382,10 +2167,14 @@ prof_prefork(void) if (opt_prof) { unsigned i; - malloc_mutex_prefork(&bt2ctx_mtx); + malloc_mutex_prefork(&tdatas_mtx); + malloc_mutex_prefork(&bt2gctx_mtx); + malloc_mutex_prefork(&next_thr_uid_mtx); malloc_mutex_prefork(&prof_dump_seq_mtx); for (i = 0; i < PROF_NCTX_LOCKS; i++) - malloc_mutex_prefork(&ctx_locks[i]); + malloc_mutex_prefork(&gctx_locks[i]); + for (i = 0; i < PROF_NTDATA_LOCKS; i++) + malloc_mutex_prefork(&tdata_locks[i]); } } @@ -1396,10 +2185,14 @@ prof_postfork_parent(void) if (opt_prof) { unsigned i; + for (i = 0; i < PROF_NTDATA_LOCKS; i++) + malloc_mutex_postfork_parent(&tdata_locks[i]); for (i = 0; i < PROF_NCTX_LOCKS; i++) - malloc_mutex_postfork_parent(&ctx_locks[i]); + malloc_mutex_postfork_parent(&gctx_locks[i]); malloc_mutex_postfork_parent(&prof_dump_seq_mtx); - malloc_mutex_postfork_parent(&bt2ctx_mtx); + malloc_mutex_postfork_parent(&next_thr_uid_mtx); + malloc_mutex_postfork_parent(&bt2gctx_mtx); + malloc_mutex_postfork_parent(&tdatas_mtx); } } @@ -1410,10 +2203,14 @@ prof_postfork_child(void) if (opt_prof) { unsigned i; + for (i = 0; i < PROF_NTDATA_LOCKS; i++) + malloc_mutex_postfork_child(&tdata_locks[i]); for (i = 0; i < PROF_NCTX_LOCKS; i++) - malloc_mutex_postfork_child(&ctx_locks[i]); + malloc_mutex_postfork_child(&gctx_locks[i]); malloc_mutex_postfork_child(&prof_dump_seq_mtx); - malloc_mutex_postfork_child(&bt2ctx_mtx); + malloc_mutex_postfork_child(&next_thr_uid_mtx); + malloc_mutex_postfork_child(&bt2gctx_mtx); + malloc_mutex_postfork_child(&tdatas_mtx); } } diff --git a/contrib/jemalloc/src/quarantine.c b/contrib/jemalloc/src/quarantine.c index 5431511640a5..6c43dfcaa3af 100644 --- a/contrib/jemalloc/src/quarantine.c +++ b/contrib/jemalloc/src/quarantine.c @@ -2,34 +2,33 @@ #include "jemalloc/internal/jemalloc_internal.h" /* - * quarantine pointers close to NULL are used to encode state information that + * Quarantine pointers close to NULL are used to encode state information that * is used for cleaning up during thread shutdown. */ #define QUARANTINE_STATE_REINCARNATED ((quarantine_t *)(uintptr_t)1) #define QUARANTINE_STATE_PURGATORY ((quarantine_t *)(uintptr_t)2) #define QUARANTINE_STATE_MAX QUARANTINE_STATE_PURGATORY -/******************************************************************************/ -/* Data. */ - -malloc_tsd_data(, quarantine, quarantine_t *, NULL) - /******************************************************************************/ /* Function prototypes for non-inline static functions. */ -static quarantine_t *quarantine_grow(quarantine_t *quarantine); -static void quarantine_drain_one(quarantine_t *quarantine); -static void quarantine_drain(quarantine_t *quarantine, size_t upper_bound); +static quarantine_t *quarantine_grow(tsd_t *tsd, quarantine_t *quarantine); +static void quarantine_drain_one(tsd_t *tsd, quarantine_t *quarantine); +static void quarantine_drain(tsd_t *tsd, quarantine_t *quarantine, + size_t upper_bound); /******************************************************************************/ -quarantine_t * -quarantine_init(size_t lg_maxobjs) +static quarantine_t * +quarantine_init(tsd_t *tsd, size_t lg_maxobjs) { quarantine_t *quarantine; - quarantine = (quarantine_t *)imalloc(offsetof(quarantine_t, objs) + - ((ZU(1) << lg_maxobjs) * sizeof(quarantine_obj_t))); + assert(tsd_nominal(tsd)); + + quarantine = (quarantine_t *)iallocztm(tsd, offsetof(quarantine_t, objs) + + ((ZU(1) << lg_maxobjs) * sizeof(quarantine_obj_t)), false, + tcache_get(tsd, true), true, NULL); if (quarantine == NULL) return (NULL); quarantine->curbytes = 0; @@ -37,19 +36,36 @@ quarantine_init(size_t lg_maxobjs) quarantine->first = 0; quarantine->lg_maxobjs = lg_maxobjs; - quarantine_tsd_set(&quarantine); - return (quarantine); } +void +quarantine_alloc_hook_work(tsd_t *tsd) +{ + quarantine_t *quarantine; + + if (!tsd_nominal(tsd)) + return; + + quarantine = quarantine_init(tsd, LG_MAXOBJS_INIT); + /* + * Check again whether quarantine has been initialized, because + * quarantine_init() may have triggered recursive initialization. + */ + if (tsd_quarantine_get(tsd) == NULL) + tsd_quarantine_set(tsd, quarantine); + else + idalloctm(tsd, quarantine, tcache_get(tsd, false), true); +} + static quarantine_t * -quarantine_grow(quarantine_t *quarantine) +quarantine_grow(tsd_t *tsd, quarantine_t *quarantine) { quarantine_t *ret; - ret = quarantine_init(quarantine->lg_maxobjs + 1); + ret = quarantine_init(tsd, quarantine->lg_maxobjs + 1); if (ret == NULL) { - quarantine_drain_one(quarantine); + quarantine_drain_one(tsd, quarantine); return (quarantine); } @@ -71,17 +87,18 @@ quarantine_grow(quarantine_t *quarantine) memcpy(&ret->objs[ncopy_a], quarantine->objs, ncopy_b * sizeof(quarantine_obj_t)); } - idalloc(quarantine); + idalloctm(tsd, quarantine, tcache_get(tsd, false), true); + tsd_quarantine_set(tsd, ret); return (ret); } static void -quarantine_drain_one(quarantine_t *quarantine) +quarantine_drain_one(tsd_t *tsd, quarantine_t *quarantine) { quarantine_obj_t *obj = &quarantine->objs[quarantine->first]; assert(obj->usize == isalloc(obj->ptr, config_prof)); - idalloc(obj->ptr); + idalloctm(tsd, obj->ptr, NULL, false); quarantine->curbytes -= obj->usize; quarantine->curobjs--; quarantine->first = (quarantine->first + 1) & ((ZU(1) << @@ -89,15 +106,15 @@ quarantine_drain_one(quarantine_t *quarantine) } static void -quarantine_drain(quarantine_t *quarantine, size_t upper_bound) +quarantine_drain(tsd_t *tsd, quarantine_t *quarantine, size_t upper_bound) { while (quarantine->curbytes > upper_bound && quarantine->curobjs > 0) - quarantine_drain_one(quarantine); + quarantine_drain_one(tsd, quarantine); } void -quarantine(void *ptr) +quarantine(tsd_t *tsd, void *ptr) { quarantine_t *quarantine; size_t usize = isalloc(ptr, config_prof); @@ -105,17 +122,8 @@ quarantine(void *ptr) cassert(config_fill); assert(opt_quarantine); - quarantine = *quarantine_tsd_get(); - if ((uintptr_t)quarantine <= (uintptr_t)QUARANTINE_STATE_MAX) { - if (quarantine == QUARANTINE_STATE_PURGATORY) { - /* - * Make a note that quarantine() was called after - * quarantine_cleanup() was called. - */ - quarantine = QUARANTINE_STATE_REINCARNATED; - quarantine_tsd_set(&quarantine); - } - idalloc(ptr); + if ((quarantine = tsd_quarantine_get(tsd)) == NULL) { + idalloctm(tsd, ptr, NULL, false); return; } /* @@ -125,11 +133,11 @@ quarantine(void *ptr) if (quarantine->curbytes + usize > opt_quarantine) { size_t upper_bound = (opt_quarantine >= usize) ? opt_quarantine - usize : 0; - quarantine_drain(quarantine, upper_bound); + quarantine_drain(tsd, quarantine, upper_bound); } /* Grow the quarantine ring buffer if it's full. */ if (quarantine->curobjs == (ZU(1) << quarantine->lg_maxobjs)) - quarantine = quarantine_grow(quarantine); + quarantine = quarantine_grow(tsd, quarantine); /* quarantine_grow() must free a slot if it fails to grow. */ assert(quarantine->curobjs < (ZU(1) << quarantine->lg_maxobjs)); /* Append ptr if its size doesn't exceed the quarantine size. */ @@ -141,12 +149,12 @@ quarantine(void *ptr) obj->usize = usize; quarantine->curbytes += usize; quarantine->curobjs++; - if (config_fill && opt_junk) { + if (config_fill && unlikely(opt_junk_free)) { /* * Only do redzone validation if Valgrind isn't in * operation. */ - if ((config_valgrind == false || opt_valgrind == false) + if ((!config_valgrind || likely(!in_valgrind)) && usize <= SMALL_MAXCLASS) arena_quarantine_junk_small(ptr, usize); else @@ -154,46 +162,22 @@ quarantine(void *ptr) } } else { assert(quarantine->curbytes == 0); - idalloc(ptr); + idalloctm(tsd, ptr, NULL, false); } } void -quarantine_cleanup(void *arg) +quarantine_cleanup(tsd_t *tsd) { - quarantine_t *quarantine = *(quarantine_t **)arg; + quarantine_t *quarantine; - if (quarantine == QUARANTINE_STATE_REINCARNATED) { - /* - * Another destructor deallocated memory after this destructor - * was called. Reset quarantine to QUARANTINE_STATE_PURGATORY - * in order to receive another callback. - */ - quarantine = QUARANTINE_STATE_PURGATORY; - quarantine_tsd_set(&quarantine); - } else if (quarantine == QUARANTINE_STATE_PURGATORY) { - /* - * The previous time this destructor was called, we set the key - * to QUARANTINE_STATE_PURGATORY so that other destructors - * wouldn't cause re-creation of the quarantine. This time, do - * nothing, so that the destructor will not be called again. - */ - } else if (quarantine != NULL) { - quarantine_drain(quarantine, 0); - idalloc(quarantine); - quarantine = QUARANTINE_STATE_PURGATORY; - quarantine_tsd_set(&quarantine); + if (!config_fill) + return; + + quarantine = tsd_quarantine_get(tsd); + if (quarantine != NULL) { + quarantine_drain(tsd, quarantine, 0); + idalloctm(tsd, quarantine, tcache_get(tsd, false), true); + tsd_quarantine_set(tsd, NULL); } } - -bool -quarantine_boot(void) -{ - - cassert(config_fill); - - if (quarantine_tsd_boot()) - return (true); - - return (false); -} diff --git a/contrib/jemalloc/src/rtree.c b/contrib/jemalloc/src/rtree.c index 205957ac4e1a..af0d97e75301 100644 --- a/contrib/jemalloc/src/rtree.c +++ b/contrib/jemalloc/src/rtree.c @@ -1,73 +1,74 @@ #define JEMALLOC_RTREE_C_ #include "jemalloc/internal/jemalloc_internal.h" -rtree_t * -rtree_new(unsigned bits, rtree_alloc_t *alloc, rtree_dalloc_t *dalloc) +static unsigned +hmin(unsigned ha, unsigned hb) { - rtree_t *ret; - unsigned bits_per_level, bits_in_leaf, height, i; + + return (ha < hb ? ha : hb); +} + +/* Only the most significant bits of keys passed to rtree_[gs]et() are used. */ +bool +rtree_new(rtree_t *rtree, unsigned bits, rtree_node_alloc_t *alloc, + rtree_node_dalloc_t *dalloc) +{ + unsigned bits_in_leaf, height, i; assert(bits > 0 && bits <= (sizeof(uintptr_t) << 3)); - bits_per_level = ffs(pow2_ceil((RTREE_NODESIZE / sizeof(void *)))) - 1; - bits_in_leaf = ffs(pow2_ceil((RTREE_NODESIZE / sizeof(uint8_t)))) - 1; + bits_in_leaf = (bits % RTREE_BITS_PER_LEVEL) == 0 ? RTREE_BITS_PER_LEVEL + : (bits % RTREE_BITS_PER_LEVEL); if (bits > bits_in_leaf) { - height = 1 + (bits - bits_in_leaf) / bits_per_level; - if ((height-1) * bits_per_level + bits_in_leaf != bits) + height = 1 + (bits - bits_in_leaf) / RTREE_BITS_PER_LEVEL; + if ((height-1) * RTREE_BITS_PER_LEVEL + bits_in_leaf != bits) height++; - } else { - height = 1; - } - assert((height-1) * bits_per_level + bits_in_leaf >= bits); - - ret = (rtree_t*)alloc(offsetof(rtree_t, level2bits) + - (sizeof(unsigned) * height)); - if (ret == NULL) - return (NULL); - memset(ret, 0, offsetof(rtree_t, level2bits) + (sizeof(unsigned) * - height)); - - ret->alloc = alloc; - ret->dalloc = dalloc; - if (malloc_mutex_init(&ret->mutex)) { - if (dalloc != NULL) - dalloc(ret); - return (NULL); - } - ret->height = height; - if (height > 1) { - if ((height-1) * bits_per_level + bits_in_leaf > bits) { - ret->level2bits[0] = (bits - bits_in_leaf) % - bits_per_level; - } else - ret->level2bits[0] = bits_per_level; - for (i = 1; i < height-1; i++) - ret->level2bits[i] = bits_per_level; - ret->level2bits[height-1] = bits_in_leaf; } else - ret->level2bits[0] = bits; + height = 1; + assert((height-1) * RTREE_BITS_PER_LEVEL + bits_in_leaf == bits); - ret->root = (void**)alloc(sizeof(void *) << ret->level2bits[0]); - if (ret->root == NULL) { - if (dalloc != NULL) - dalloc(ret); - return (NULL); + rtree->alloc = alloc; + rtree->dalloc = dalloc; + rtree->height = height; + + /* Root level. */ + rtree->levels[0].subtree = NULL; + rtree->levels[0].bits = (height > 1) ? RTREE_BITS_PER_LEVEL : + bits_in_leaf; + rtree->levels[0].cumbits = rtree->levels[0].bits; + /* Interior levels. */ + for (i = 1; i < height-1; i++) { + rtree->levels[i].subtree = NULL; + rtree->levels[i].bits = RTREE_BITS_PER_LEVEL; + rtree->levels[i].cumbits = rtree->levels[i-1].cumbits + + RTREE_BITS_PER_LEVEL; + } + /* Leaf level. */ + if (height > 1) { + rtree->levels[height-1].subtree = NULL; + rtree->levels[height-1].bits = bits_in_leaf; + rtree->levels[height-1].cumbits = bits; } - memset(ret->root, 0, sizeof(void *) << ret->level2bits[0]); - return (ret); + /* Compute lookup table to be used by rtree_start_level(). */ + for (i = 0; i < RTREE_HEIGHT_MAX; i++) { + rtree->start_level[i] = hmin(RTREE_HEIGHT_MAX - 1 - i, height - + 1); + } + + return (false); } static void -rtree_delete_subtree(rtree_t *rtree, void **node, unsigned level) +rtree_delete_subtree(rtree_t *rtree, rtree_node_elm_t *node, unsigned level) { - if (level < rtree->height - 1) { + if (level + 1 < rtree->height) { size_t nchildren, i; - nchildren = ZU(1) << rtree->level2bits[level]; + nchildren = ZU(1) << rtree->levels[level].bits; for (i = 0; i < nchildren; i++) { - void **child = (void **)node[i]; + rtree_node_elm_t *child = node[i].child; if (child != NULL) rtree_delete_subtree(rtree, child, level + 1); } @@ -78,28 +79,49 @@ rtree_delete_subtree(rtree_t *rtree, void **node, unsigned level) void rtree_delete(rtree_t *rtree) { + unsigned i; - rtree_delete_subtree(rtree, rtree->root, 0); - rtree->dalloc(rtree); + for (i = 0; i < rtree->height; i++) { + rtree_node_elm_t *subtree = rtree->levels[i].subtree; + if (subtree != NULL) + rtree_delete_subtree(rtree, subtree, i); + } } -void -rtree_prefork(rtree_t *rtree) +static rtree_node_elm_t * +rtree_node_init(rtree_t *rtree, unsigned level, rtree_node_elm_t **elmp) +{ + rtree_node_elm_t *node; + + if (atomic_cas_p((void **)elmp, NULL, RTREE_NODE_INITIALIZING)) { + /* + * Another thread is already in the process of initializing. + * Spin-wait until initialization is complete. + */ + do { + CPU_SPINWAIT; + node = atomic_read_p((void **)elmp); + } while (node == RTREE_NODE_INITIALIZING); + } else { + node = rtree->alloc(ZU(1) << rtree->levels[level].bits); + if (node == NULL) + return (NULL); + atomic_write_p((void **)elmp, node); + } + + return (node); +} + +rtree_node_elm_t * +rtree_subtree_read_hard(rtree_t *rtree, unsigned level) { - malloc_mutex_prefork(&rtree->mutex); + return (rtree_node_init(rtree, level, &rtree->levels[level].subtree)); } -void -rtree_postfork_parent(rtree_t *rtree) +rtree_node_elm_t * +rtree_child_read_hard(rtree_t *rtree, rtree_node_elm_t *elm, unsigned level) { - malloc_mutex_postfork_parent(&rtree->mutex); -} - -void -rtree_postfork_child(rtree_t *rtree) -{ - - malloc_mutex_postfork_child(&rtree->mutex); + return (rtree_node_init(rtree, level, &elm->child)); } diff --git a/contrib/jemalloc/src/stats.c b/contrib/jemalloc/src/stats.c index bef2ab33cd4d..154c3e74cd36 100644 --- a/contrib/jemalloc/src/stats.c +++ b/contrib/jemalloc/src/stats.c @@ -6,31 +6,22 @@ xmallctl(n, v, &sz, NULL, 0); \ } while (0) -#define CTL_I_GET(n, v, t) do { \ +#define CTL_M2_GET(n, i, v, t) do { \ size_t mib[6]; \ size_t miblen = sizeof(mib) / sizeof(size_t); \ size_t sz = sizeof(t); \ xmallctlnametomib(n, mib, &miblen); \ - mib[2] = i; \ + mib[2] = (i); \ xmallctlbymib(mib, miblen, v, &sz, NULL, 0); \ } while (0) -#define CTL_J_GET(n, v, t) do { \ +#define CTL_M2_M4_GET(n, i, j, v, t) do { \ size_t mib[6]; \ size_t miblen = sizeof(mib) / sizeof(size_t); \ size_t sz = sizeof(t); \ xmallctlnametomib(n, mib, &miblen); \ - mib[2] = j; \ - xmallctlbymib(mib, miblen, v, &sz, NULL, 0); \ -} while (0) - -#define CTL_IJ_GET(n, v, t) do { \ - size_t mib[6]; \ - size_t miblen = sizeof(mib) / sizeof(size_t); \ - size_t sz = sizeof(t); \ - xmallctlnametomib(n, mib, &miblen); \ - mib[2] = i; \ - mib[4] = j; \ + mib[2] = (i); \ + mib[4] = (j); \ xmallctlbymib(mib, miblen, v, &sz, NULL, 0); \ } while (0) @@ -48,8 +39,10 @@ static void stats_arena_bins_print(void (*write_cb)(void *, const char *), void *cbopaque, unsigned i); static void stats_arena_lruns_print(void (*write_cb)(void *, const char *), void *cbopaque, unsigned i); +static void stats_arena_hchunks_print( + void (*write_cb)(void *, const char *), void *cbopaque, unsigned i); static void stats_arena_print(void (*write_cb)(void *, const char *), - void *cbopaque, unsigned i, bool bins, bool large); + void *cbopaque, unsigned i, bool bins, bool large, bool huge); /******************************************************************************/ @@ -58,100 +51,109 @@ stats_arena_bins_print(void (*write_cb)(void *, const char *), void *cbopaque, unsigned i) { size_t page; - bool config_tcache; - unsigned nbins, j, gap_start; + bool config_tcache, in_gap; + unsigned nbins, j; CTL_GET("arenas.page", &page, size_t); CTL_GET("config.tcache", &config_tcache, bool); if (config_tcache) { malloc_cprintf(write_cb, cbopaque, - "bins: bin size regs pgs allocated nmalloc" - " ndalloc nrequests nfills nflushes" - " newruns reruns curruns\n"); + "bins: size ind allocated nmalloc" + " ndalloc nrequests curregs curruns regs" + " pgs util nfills nflushes newruns" + " reruns\n"); } else { malloc_cprintf(write_cb, cbopaque, - "bins: bin size regs pgs allocated nmalloc" - " ndalloc newruns reruns curruns\n"); + "bins: size ind allocated nmalloc" + " ndalloc nrequests curregs curruns regs" + " pgs util newruns reruns\n"); } CTL_GET("arenas.nbins", &nbins, unsigned); - for (j = 0, gap_start = UINT_MAX; j < nbins; j++) { + for (j = 0, in_gap = false; j < nbins; j++) { uint64_t nruns; - CTL_IJ_GET("stats.arenas.0.bins.0.nruns", &nruns, uint64_t); - if (nruns == 0) { - if (gap_start == UINT_MAX) - gap_start = j; - } else { - size_t reg_size, run_size, allocated; + CTL_M2_M4_GET("stats.arenas.0.bins.0.nruns", i, j, &nruns, + uint64_t); + if (nruns == 0) + in_gap = true; + else { + size_t reg_size, run_size, curregs, availregs, milli; + size_t curruns; uint32_t nregs; uint64_t nmalloc, ndalloc, nrequests, nfills, nflushes; uint64_t reruns; - size_t curruns; + char util[6]; /* "x.yyy". */ - if (gap_start != UINT_MAX) { - if (j > gap_start + 1) { - /* Gap of more than one size class. */ - malloc_cprintf(write_cb, cbopaque, - "[%u..%u]\n", gap_start, - j - 1); - } else { - /* Gap of one size class. */ - malloc_cprintf(write_cb, cbopaque, - "[%u]\n", gap_start); - } - gap_start = UINT_MAX; + if (in_gap) { + malloc_cprintf(write_cb, cbopaque, + " ---\n"); + in_gap = false; } - CTL_J_GET("arenas.bin.0.size", ®_size, size_t); - CTL_J_GET("arenas.bin.0.nregs", &nregs, uint32_t); - CTL_J_GET("arenas.bin.0.run_size", &run_size, size_t); - CTL_IJ_GET("stats.arenas.0.bins.0.allocated", - &allocated, size_t); - CTL_IJ_GET("stats.arenas.0.bins.0.nmalloc", - &nmalloc, uint64_t); - CTL_IJ_GET("stats.arenas.0.bins.0.ndalloc", - &ndalloc, uint64_t); - if (config_tcache) { - CTL_IJ_GET("stats.arenas.0.bins.0.nrequests", - &nrequests, uint64_t); - CTL_IJ_GET("stats.arenas.0.bins.0.nfills", - &nfills, uint64_t); - CTL_IJ_GET("stats.arenas.0.bins.0.nflushes", - &nflushes, uint64_t); - } - CTL_IJ_GET("stats.arenas.0.bins.0.nreruns", &reruns, - uint64_t); - CTL_IJ_GET("stats.arenas.0.bins.0.curruns", &curruns, + CTL_M2_GET("arenas.bin.0.size", j, ®_size, size_t); + CTL_M2_GET("arenas.bin.0.nregs", j, &nregs, uint32_t); + CTL_M2_GET("arenas.bin.0.run_size", j, &run_size, size_t); + CTL_M2_M4_GET("stats.arenas.0.bins.0.nmalloc", i, j, + &nmalloc, uint64_t); + CTL_M2_M4_GET("stats.arenas.0.bins.0.ndalloc", i, j, + &ndalloc, uint64_t); + CTL_M2_M4_GET("stats.arenas.0.bins.0.curregs", i, j, + &curregs, size_t); + CTL_M2_M4_GET("stats.arenas.0.bins.0.nrequests", i, j, + &nrequests, uint64_t); + if (config_tcache) { + CTL_M2_M4_GET("stats.arenas.0.bins.0.nfills", i, + j, &nfills, uint64_t); + CTL_M2_M4_GET("stats.arenas.0.bins.0.nflushes", + i, j, &nflushes, uint64_t); + } + CTL_M2_M4_GET("stats.arenas.0.bins.0.nreruns", i, j, + &reruns, uint64_t); + CTL_M2_M4_GET("stats.arenas.0.bins.0.curruns", i, j, + &curruns, size_t); + + availregs = nregs * curruns; + milli = (availregs != 0) ? (1000 * curregs) / availregs + : 1000; + assert(milli <= 1000); + if (milli < 10) { + malloc_snprintf(util, sizeof(util), + "0.00%zu", milli); + } else if (milli < 100) { + malloc_snprintf(util, sizeof(util), "0.0%zu", + milli); + } else if (milli < 1000) { + malloc_snprintf(util, sizeof(util), "0.%zu", + milli); + } else + malloc_snprintf(util, sizeof(util), "1"); + if (config_tcache) { malloc_cprintf(write_cb, cbopaque, - "%13u %5zu %4u %3zu %12zu %12"PRIu64 - " %12"PRIu64" %12"PRIu64" %12"PRIu64 - " %12"PRIu64" %12"PRIu64" %12"PRIu64 - " %12zu\n", - j, reg_size, nregs, run_size / page, - allocated, nmalloc, ndalloc, nrequests, - nfills, nflushes, nruns, reruns, curruns); + "%20zu %3u %12zu %12"FMTu64 + " %12"FMTu64" %12"FMTu64" %12zu" + " %12zu %4u %3zu %-5s %12"FMTu64 + " %12"FMTu64" %12"FMTu64" %12"FMTu64"\n", + reg_size, j, curregs * reg_size, nmalloc, + ndalloc, nrequests, curregs, curruns, nregs, + run_size / page, util, nfills, nflushes, + nruns, reruns); } else { malloc_cprintf(write_cb, cbopaque, - "%13u %5zu %4u %3zu %12zu %12"PRIu64 - " %12"PRIu64" %12"PRIu64" %12"PRIu64 - " %12zu\n", - j, reg_size, nregs, run_size / page, - allocated, nmalloc, ndalloc, nruns, reruns, - curruns); + "%20zu %3u %12zu %12"FMTu64 + " %12"FMTu64" %12"FMTu64" %12zu" + " %12zu %4u %3zu %-5s %12"FMTu64 + " %12"FMTu64"\n", + reg_size, j, curregs * reg_size, nmalloc, + ndalloc, nrequests, curregs, curruns, nregs, + run_size / page, util, nruns, reruns); } } } - if (gap_start != UINT_MAX) { - if (j > gap_start + 1) { - /* Gap of more than one size class. */ - malloc_cprintf(write_cb, cbopaque, "[%u..%u]\n", - gap_start, j - 1); - } else { - /* Gap of one size class. */ - malloc_cprintf(write_cb, cbopaque, "[%u]\n", gap_start); - } + if (in_gap) { + malloc_cprintf(write_cb, cbopaque, + " ---\n"); } } @@ -159,110 +161,199 @@ static void stats_arena_lruns_print(void (*write_cb)(void *, const char *), void *cbopaque, unsigned i) { - size_t page, nlruns, j; - ssize_t gap_start; - - CTL_GET("arenas.page", &page, size_t); + unsigned nbins, nlruns, j; + bool in_gap; malloc_cprintf(write_cb, cbopaque, - "large: size pages nmalloc ndalloc nrequests" - " curruns\n"); - CTL_GET("arenas.nlruns", &nlruns, size_t); - for (j = 0, gap_start = -1; j < nlruns; j++) { + "large: size ind allocated nmalloc ndalloc" + " nrequests curruns\n"); + CTL_GET("arenas.nbins", &nbins, unsigned); + CTL_GET("arenas.nlruns", &nlruns, unsigned); + for (j = 0, in_gap = false; j < nlruns; j++) { uint64_t nmalloc, ndalloc, nrequests; size_t run_size, curruns; - CTL_IJ_GET("stats.arenas.0.lruns.0.nmalloc", &nmalloc, + CTL_M2_M4_GET("stats.arenas.0.lruns.0.nmalloc", i, j, &nmalloc, uint64_t); - CTL_IJ_GET("stats.arenas.0.lruns.0.ndalloc", &ndalloc, + CTL_M2_M4_GET("stats.arenas.0.lruns.0.ndalloc", i, j, &ndalloc, uint64_t); - CTL_IJ_GET("stats.arenas.0.lruns.0.nrequests", &nrequests, - uint64_t); - if (nrequests == 0) { - if (gap_start == -1) - gap_start = j; - } else { - CTL_J_GET("arenas.lrun.0.size", &run_size, size_t); - CTL_IJ_GET("stats.arenas.0.lruns.0.curruns", &curruns, - size_t); - if (gap_start != -1) { - malloc_cprintf(write_cb, cbopaque, "[%zu]\n", - j - gap_start); - gap_start = -1; + CTL_M2_M4_GET("stats.arenas.0.lruns.0.nrequests", i, j, + &nrequests, uint64_t); + if (nrequests == 0) + in_gap = true; + else { + CTL_M2_GET("arenas.lrun.0.size", j, &run_size, size_t); + CTL_M2_M4_GET("stats.arenas.0.lruns.0.curruns", i, j, + &curruns, size_t); + if (in_gap) { + malloc_cprintf(write_cb, cbopaque, + " ---\n"); + in_gap = false; } malloc_cprintf(write_cb, cbopaque, - "%13zu %5zu %12"PRIu64" %12"PRIu64" %12"PRIu64 - " %12zu\n", - run_size, run_size / page, nmalloc, ndalloc, - nrequests, curruns); + "%20zu %3u %12zu %12"FMTu64" %12"FMTu64 + " %12"FMTu64" %12zu\n", + run_size, nbins + j, curruns * run_size, nmalloc, + ndalloc, nrequests, curruns); } } - if (gap_start != -1) - malloc_cprintf(write_cb, cbopaque, "[%zu]\n", j - gap_start); + if (in_gap) { + malloc_cprintf(write_cb, cbopaque, + " ---\n"); + } +} + +static void +stats_arena_hchunks_print(void (*write_cb)(void *, const char *), + void *cbopaque, unsigned i) +{ + unsigned nbins, nlruns, nhchunks, j; + bool in_gap; + + malloc_cprintf(write_cb, cbopaque, + "huge: size ind allocated nmalloc ndalloc" + " nrequests curhchunks\n"); + CTL_GET("arenas.nbins", &nbins, unsigned); + CTL_GET("arenas.nlruns", &nlruns, unsigned); + CTL_GET("arenas.nhchunks", &nhchunks, unsigned); + for (j = 0, in_gap = false; j < nhchunks; j++) { + uint64_t nmalloc, ndalloc, nrequests; + size_t hchunk_size, curhchunks; + + CTL_M2_M4_GET("stats.arenas.0.hchunks.0.nmalloc", i, j, + &nmalloc, uint64_t); + CTL_M2_M4_GET("stats.arenas.0.hchunks.0.ndalloc", i, j, + &ndalloc, uint64_t); + CTL_M2_M4_GET("stats.arenas.0.hchunks.0.nrequests", i, j, + &nrequests, uint64_t); + if (nrequests == 0) + in_gap = true; + else { + CTL_M2_GET("arenas.hchunk.0.size", j, &hchunk_size, + size_t); + CTL_M2_M4_GET("stats.arenas.0.hchunks.0.curhchunks", i, + j, &curhchunks, size_t); + if (in_gap) { + malloc_cprintf(write_cb, cbopaque, + " ---\n"); + in_gap = false; + } + malloc_cprintf(write_cb, cbopaque, + "%20zu %3u %12zu %12"FMTu64" %12"FMTu64 + " %12"FMTu64" %12zu\n", + hchunk_size, nbins + nlruns + j, + curhchunks * hchunk_size, nmalloc, ndalloc, + nrequests, curhchunks); + } + } + if (in_gap) { + malloc_cprintf(write_cb, cbopaque, + " ---\n"); + } } static void stats_arena_print(void (*write_cb)(void *, const char *), void *cbopaque, - unsigned i, bool bins, bool large) + unsigned i, bool bins, bool large, bool huge) { unsigned nthreads; const char *dss; + ssize_t lg_dirty_mult; size_t page, pactive, pdirty, mapped; + size_t metadata_mapped, metadata_allocated; uint64_t npurge, nmadvise, purged; size_t small_allocated; uint64_t small_nmalloc, small_ndalloc, small_nrequests; size_t large_allocated; uint64_t large_nmalloc, large_ndalloc, large_nrequests; + size_t huge_allocated; + uint64_t huge_nmalloc, huge_ndalloc, huge_nrequests; CTL_GET("arenas.page", &page, size_t); - CTL_I_GET("stats.arenas.0.nthreads", &nthreads, unsigned); + CTL_M2_GET("stats.arenas.0.nthreads", i, &nthreads, unsigned); malloc_cprintf(write_cb, cbopaque, "assigned threads: %u\n", nthreads); - CTL_I_GET("stats.arenas.0.dss", &dss, const char *); + CTL_M2_GET("stats.arenas.0.dss", i, &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); - CTL_I_GET("stats.arenas.0.nmadvise", &nmadvise, uint64_t); - CTL_I_GET("stats.arenas.0.purged", &purged, uint64_t); + CTL_M2_GET("stats.arenas.0.lg_dirty_mult", i, &lg_dirty_mult, ssize_t); + if (lg_dirty_mult >= 0) { + malloc_cprintf(write_cb, cbopaque, + "min active:dirty page ratio: %u:1\n", + (1U << lg_dirty_mult)); + } else { + malloc_cprintf(write_cb, cbopaque, + "min active:dirty page ratio: N/A\n"); + } + CTL_M2_GET("stats.arenas.0.pactive", i, &pactive, size_t); + CTL_M2_GET("stats.arenas.0.pdirty", i, &pdirty, size_t); + CTL_M2_GET("stats.arenas.0.npurge", i, &npurge, uint64_t); + CTL_M2_GET("stats.arenas.0.nmadvise", i, &nmadvise, uint64_t); + CTL_M2_GET("stats.arenas.0.purged", i, &purged, uint64_t); malloc_cprintf(write_cb, cbopaque, - "dirty pages: %zu:%zu active:dirty, %"PRIu64" sweep%s," - " %"PRIu64" madvise%s, %"PRIu64" purged\n", - pactive, pdirty, npurge, npurge == 1 ? "" : "s", - nmadvise, nmadvise == 1 ? "" : "s", purged); + "dirty pages: %zu:%zu active:dirty, %"FMTu64" sweep%s, %"FMTu64 + " madvise%s, %"FMTu64" purged\n", pactive, pdirty, npurge, npurge == + 1 ? "" : "s", nmadvise, nmadvise == 1 ? "" : "s", purged); malloc_cprintf(write_cb, cbopaque, - " allocated nmalloc ndalloc nrequests\n"); - CTL_I_GET("stats.arenas.0.small.allocated", &small_allocated, size_t); - CTL_I_GET("stats.arenas.0.small.nmalloc", &small_nmalloc, uint64_t); - CTL_I_GET("stats.arenas.0.small.ndalloc", &small_ndalloc, uint64_t); - CTL_I_GET("stats.arenas.0.small.nrequests", &small_nrequests, uint64_t); + " allocated nmalloc ndalloc" + " nrequests\n"); + CTL_M2_GET("stats.arenas.0.small.allocated", i, &small_allocated, + size_t); + CTL_M2_GET("stats.arenas.0.small.nmalloc", i, &small_nmalloc, uint64_t); + CTL_M2_GET("stats.arenas.0.small.ndalloc", i, &small_ndalloc, uint64_t); + CTL_M2_GET("stats.arenas.0.small.nrequests", i, &small_nrequests, + uint64_t); malloc_cprintf(write_cb, cbopaque, - "small: %12zu %12"PRIu64" %12"PRIu64" %12"PRIu64"\n", + "small: %12zu %12"FMTu64" %12"FMTu64 + " %12"FMTu64"\n", small_allocated, small_nmalloc, small_ndalloc, small_nrequests); - CTL_I_GET("stats.arenas.0.large.allocated", &large_allocated, size_t); - CTL_I_GET("stats.arenas.0.large.nmalloc", &large_nmalloc, uint64_t); - CTL_I_GET("stats.arenas.0.large.ndalloc", &large_ndalloc, uint64_t); - CTL_I_GET("stats.arenas.0.large.nrequests", &large_nrequests, uint64_t); + CTL_M2_GET("stats.arenas.0.large.allocated", i, &large_allocated, + size_t); + CTL_M2_GET("stats.arenas.0.large.nmalloc", i, &large_nmalloc, uint64_t); + CTL_M2_GET("stats.arenas.0.large.ndalloc", i, &large_ndalloc, uint64_t); + CTL_M2_GET("stats.arenas.0.large.nrequests", i, &large_nrequests, + uint64_t); malloc_cprintf(write_cb, cbopaque, - "large: %12zu %12"PRIu64" %12"PRIu64" %12"PRIu64"\n", + "large: %12zu %12"FMTu64" %12"FMTu64 + " %12"FMTu64"\n", large_allocated, large_nmalloc, large_ndalloc, large_nrequests); + CTL_M2_GET("stats.arenas.0.huge.allocated", i, &huge_allocated, size_t); + CTL_M2_GET("stats.arenas.0.huge.nmalloc", i, &huge_nmalloc, uint64_t); + CTL_M2_GET("stats.arenas.0.huge.ndalloc", i, &huge_ndalloc, uint64_t); + CTL_M2_GET("stats.arenas.0.huge.nrequests", i, &huge_nrequests, + uint64_t); malloc_cprintf(write_cb, cbopaque, - "total: %12zu %12"PRIu64" %12"PRIu64" %12"PRIu64"\n", - small_allocated + large_allocated, - small_nmalloc + large_nmalloc, - small_ndalloc + large_ndalloc, - small_nrequests + large_nrequests); - malloc_cprintf(write_cb, cbopaque, "active: %12zu\n", pactive * page); - CTL_I_GET("stats.arenas.0.mapped", &mapped, size_t); - malloc_cprintf(write_cb, cbopaque, "mapped: %12zu\n", mapped); + "huge: %12zu %12"FMTu64" %12"FMTu64 + " %12"FMTu64"\n", + huge_allocated, huge_nmalloc, huge_ndalloc, huge_nrequests); + malloc_cprintf(write_cb, cbopaque, + "total: %12zu %12"FMTu64" %12"FMTu64 + " %12"FMTu64"\n", + small_allocated + large_allocated + huge_allocated, + small_nmalloc + large_nmalloc + huge_nmalloc, + small_ndalloc + large_ndalloc + huge_ndalloc, + small_nrequests + large_nrequests + huge_nrequests); + malloc_cprintf(write_cb, cbopaque, + "active: %12zu\n", pactive * page); + CTL_M2_GET("stats.arenas.0.mapped", i, &mapped, size_t); + malloc_cprintf(write_cb, cbopaque, + "mapped: %12zu\n", mapped); + CTL_M2_GET("stats.arenas.0.metadata.mapped", i, &metadata_mapped, + size_t); + CTL_M2_GET("stats.arenas.0.metadata.allocated", i, &metadata_allocated, + size_t); + malloc_cprintf(write_cb, cbopaque, + "metadata: mapped: %zu, allocated: %zu\n", + metadata_mapped, metadata_allocated); if (bins) stats_arena_bins_print(write_cb, cbopaque, i); if (large) stats_arena_lruns_print(write_cb, cbopaque, i); + if (huge) + stats_arena_hchunks_print(write_cb, cbopaque, i); } void @@ -277,6 +368,7 @@ stats_print(void (*write_cb)(void *, const char *), void *cbopaque, bool unmerged = true; bool bins = true; bool large = true; + bool huge = true; /* * Refresh stats, in case mallctl() was called by the application. @@ -319,6 +411,9 @@ stats_print(void (*write_cb)(void *, const char *), void *cbopaque, case 'l': large = false; break; + case 'h': + huge = false; + break; default:; } } @@ -327,7 +422,6 @@ stats_print(void (*write_cb)(void *, const char *), void *cbopaque, malloc_cprintf(write_cb, cbopaque, "___ Begin jemalloc statistics ___\n"); if (general) { - int err; const char *cpv; bool bv; unsigned uv; @@ -346,26 +440,40 @@ stats_print(void (*write_cb)(void *, const char *), void *cbopaque, bv ? "enabled" : "disabled"); #define OPT_WRITE_BOOL(n) \ - if ((err = je_mallctl("opt."#n, &bv, &bsz, NULL, 0)) \ - == 0) { \ + if (je_mallctl("opt."#n, &bv, &bsz, NULL, 0) == 0) { \ malloc_cprintf(write_cb, cbopaque, \ " opt."#n": %s\n", bv ? "true" : "false"); \ } +#define OPT_WRITE_BOOL_MUTABLE(n, m) { \ + bool bv2; \ + if (je_mallctl("opt."#n, &bv, &bsz, NULL, 0) == 0 && \ + je_mallctl(#m, &bv2, &bsz, NULL, 0) == 0) { \ + malloc_cprintf(write_cb, cbopaque, \ + " opt."#n": %s ("#m": %s)\n", bv ? "true" \ + : "false", bv2 ? "true" : "false"); \ + } \ +} #define OPT_WRITE_SIZE_T(n) \ - if ((err = je_mallctl("opt."#n, &sv, &ssz, NULL, 0)) \ - == 0) { \ + if (je_mallctl("opt."#n, &sv, &ssz, NULL, 0) == 0) { \ malloc_cprintf(write_cb, cbopaque, \ " opt."#n": %zu\n", sv); \ } #define OPT_WRITE_SSIZE_T(n) \ - if ((err = je_mallctl("opt."#n, &ssv, &sssz, NULL, 0)) \ - == 0) { \ + if (je_mallctl("opt."#n, &ssv, &sssz, NULL, 0) == 0) { \ malloc_cprintf(write_cb, cbopaque, \ " opt."#n": %zd\n", ssv); \ } +#define OPT_WRITE_SSIZE_T_MUTABLE(n, m) { \ + ssize_t ssv2; \ + if (je_mallctl("opt."#n, &ssv, &sssz, NULL, 0) == 0 && \ + je_mallctl(#m, &ssv2, &sssz, NULL, 0) == 0) { \ + malloc_cprintf(write_cb, cbopaque, \ + " opt."#n": %zd ("#m": %zd)\n", \ + ssv, ssv2); \ + } \ +} #define OPT_WRITE_CHAR_P(n) \ - if ((err = je_mallctl("opt."#n, &cpv, &cpsz, NULL, 0)) \ - == 0) { \ + if (je_mallctl("opt."#n, &cpv, &cpsz, NULL, 0) == 0) { \ malloc_cprintf(write_cb, cbopaque, \ " opt."#n": \"%s\"\n", cpv); \ } @@ -376,9 +484,9 @@ stats_print(void (*write_cb)(void *, const char *), void *cbopaque, 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_SSIZE_T_MUTABLE(lg_dirty_mult, arenas.lg_dirty_mult) OPT_WRITE_BOOL(stats_print) - OPT_WRITE_BOOL(junk) + OPT_WRITE_CHAR_P(junk) OPT_WRITE_SIZE_T(quarantine) OPT_WRITE_BOOL(redzone) OPT_WRITE_BOOL(zero) @@ -389,7 +497,9 @@ stats_print(void (*write_cb)(void *, const char *), void *cbopaque, OPT_WRITE_SSIZE_T(lg_tcache_max) OPT_WRITE_BOOL(prof) OPT_WRITE_CHAR_P(prof_prefix) - OPT_WRITE_BOOL(prof_active) + OPT_WRITE_BOOL_MUTABLE(prof_active, prof.active) + OPT_WRITE_BOOL_MUTABLE(prof_thread_active_init, + prof.thread_active_init) OPT_WRITE_SSIZE_T(lg_prof_sample) OPT_WRITE_BOOL(prof_accum) OPT_WRITE_SSIZE_T(lg_prof_interval) @@ -398,6 +508,7 @@ stats_print(void (*write_cb)(void *, const char *), void *cbopaque, OPT_WRITE_BOOL(prof_leak) #undef OPT_WRITE_BOOL +#undef OPT_WRITE_BOOL_MUTABLE #undef OPT_WRITE_SIZE_T #undef OPT_WRITE_SSIZE_T #undef OPT_WRITE_CHAR_P @@ -411,12 +522,13 @@ stats_print(void (*write_cb)(void *, const char *), void *cbopaque, sizeof(void *)); CTL_GET("arenas.quantum", &sv, size_t); - malloc_cprintf(write_cb, cbopaque, "Quantum size: %zu\n", sv); + malloc_cprintf(write_cb, cbopaque, "Quantum size: %zu\n", + sv); CTL_GET("arenas.page", &sv, size_t); malloc_cprintf(write_cb, cbopaque, "Page size: %zu\n", sv); - CTL_GET("opt.lg_dirty_mult", &ssv, ssize_t); + CTL_GET("arenas.lg_dirty_mult", &ssv, ssize_t); if (ssv >= 0) { malloc_cprintf(write_cb, cbopaque, "Min active:dirty page ratio per arena: %u:1\n", @@ -425,22 +537,20 @@ stats_print(void (*write_cb)(void *, const char *), void *cbopaque, malloc_cprintf(write_cb, cbopaque, "Min active:dirty page ratio per arena: N/A\n"); } - if ((err = je_mallctl("arenas.tcache_max", &sv, &ssz, NULL, 0)) - == 0) { + if (je_mallctl("arenas.tcache_max", &sv, &ssz, NULL, 0) == 0) { malloc_cprintf(write_cb, cbopaque, "Maximum thread-cached size class: %zu\n", sv); } - if ((err = je_mallctl("opt.prof", &bv, &bsz, NULL, 0)) == 0 && - bv) { - CTL_GET("opt.lg_prof_sample", &sv, size_t); + if (je_mallctl("opt.prof", &bv, &bsz, NULL, 0) == 0 && bv) { + CTL_GET("prof.lg_sample", &sv, size_t); malloc_cprintf(write_cb, cbopaque, - "Average profile sample interval: %"PRIu64 + "Average profile sample interval: %"FMTu64 " (2^%zu)\n", (((uint64_t)1U) << sv), sv); CTL_GET("opt.lg_prof_interval", &ssv, ssize_t); if (ssv >= 0) { malloc_cprintf(write_cb, cbopaque, - "Average profile dump interval: %"PRIu64 + "Average profile dump interval: %"FMTu64 " (2^%zd)\n", (((uint64_t)1U) << ssv), ssv); } else { @@ -449,47 +559,27 @@ stats_print(void (*write_cb)(void *, const char *), void *cbopaque, } } CTL_GET("opt.lg_chunk", &sv, size_t); - malloc_cprintf(write_cb, cbopaque, "Chunk size: %zu (2^%zu)\n", - (ZU(1) << sv), sv); + malloc_cprintf(write_cb, cbopaque, + "Chunk size: %zu (2^%zu)\n", (ZU(1) << sv), sv); } if (config_stats) { size_t *cactive; - size_t allocated, active, mapped; - size_t chunks_current, chunks_high; - uint64_t chunks_total; - size_t huge_allocated; - uint64_t huge_nmalloc, huge_ndalloc; + size_t allocated, active, metadata, resident, mapped; CTL_GET("stats.cactive", &cactive, size_t *); CTL_GET("stats.allocated", &allocated, size_t); CTL_GET("stats.active", &active, size_t); + CTL_GET("stats.metadata", &metadata, size_t); + CTL_GET("stats.resident", &resident, size_t); CTL_GET("stats.mapped", &mapped, size_t); malloc_cprintf(write_cb, cbopaque, - "Allocated: %zu, active: %zu, mapped: %zu\n", - allocated, active, mapped); + "Allocated: %zu, active: %zu, metadata: %zu," + " resident: %zu, mapped: %zu\n", + allocated, active, metadata, resident, mapped); malloc_cprintf(write_cb, cbopaque, - "Current active ceiling: %zu\n", atomic_read_z(cactive)); - - /* Print chunk stats. */ - CTL_GET("stats.chunks.total", &chunks_total, uint64_t); - CTL_GET("stats.chunks.high", &chunks_high, size_t); - 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" %12zu %12zu\n", - chunks_total, chunks_high, chunks_current); - - /* Print huge stats. */ - CTL_GET("stats.huge.nmalloc", &huge_nmalloc, uint64_t); - CTL_GET("stats.huge.ndalloc", &huge_ndalloc, uint64_t); - CTL_GET("stats.huge.allocated", &huge_allocated, size_t); - malloc_cprintf(write_cb, cbopaque, - "huge: nmalloc ndalloc allocated\n"); - malloc_cprintf(write_cb, cbopaque, - " %12"PRIu64" %12"PRIu64" %12zu\n", - huge_nmalloc, huge_ndalloc, huge_allocated); + "Current active ceiling: %zu\n", + atomic_read_z(cactive)); if (merged) { unsigned narenas; @@ -508,12 +598,12 @@ stats_print(void (*write_cb)(void *, const char *), void *cbopaque, ninitialized++; } - if (ninitialized > 1 || unmerged == false) { + if (ninitialized > 1 || !unmerged) { /* Print merged arena stats. */ malloc_cprintf(write_cb, cbopaque, "\nMerged arenas stats:\n"); stats_arena_print(write_cb, cbopaque, - narenas, bins, large); + narenas, bins, large, huge); } } } @@ -539,7 +629,8 @@ stats_print(void (*write_cb)(void *, const char *), void *cbopaque, cbopaque, "\narenas[%u]:\n", i); stats_arena_print(write_cb, - cbopaque, i, bins, large); + cbopaque, i, bins, large, + huge); } } } diff --git a/contrib/jemalloc/src/tcache.c b/contrib/jemalloc/src/tcache.c index 6de92960b2df..3814365ce4f9 100644 --- a/contrib/jemalloc/src/tcache.c +++ b/contrib/jemalloc/src/tcache.c @@ -4,9 +4,6 @@ /******************************************************************************/ /* Data. */ -malloc_tsd_data(, tcache, tcache_t *, NULL) -malloc_tsd_data(, tcache_enabled, tcache_enabled_t, tcache_enabled_default) - bool opt_tcache = true; ssize_t opt_lg_tcache_max = LG_TCACHE_MAXCLASS_DEFAULT; @@ -16,6 +13,14 @@ static unsigned stack_nelms; /* Total stack elms per tcache. */ size_t nhbins; size_t tcache_maxclass; +tcaches_t *tcaches; + +/* Index of first element within tcaches that has never been used. */ +static unsigned tcaches_past; + +/* Head of singly linked list tracking available tcaches elements. */ +static tcaches_t *tcaches_avail; + /******************************************************************************/ size_t tcache_salloc(const void *ptr) @@ -25,9 +30,9 @@ size_t tcache_salloc(const void *ptr) } void -tcache_event_hard(tcache_t *tcache) +tcache_event_hard(tsd_t *tsd, tcache_t *tcache) { - size_t binind = tcache->next_gc_bin; + index_t binind = tcache->next_gc_bin; tcache_bin_t *tbin = &tcache->tbins[binind]; tcache_bin_info_t *tbin_info = &tcache_bin_info[binind]; @@ -36,11 +41,12 @@ tcache_event_hard(tcache_t *tcache) * Flush (ceiling) 3/4 of the objects below the low water mark. */ if (binind < NBINS) { - tcache_bin_flush_small(tbin, binind, tbin->ncached - - tbin->low_water + (tbin->low_water >> 2), tcache); + tcache_bin_flush_small(tsd, tcache, tbin, binind, + tbin->ncached - tbin->low_water + (tbin->low_water + >> 2)); } else { - tcache_bin_flush_large(tbin, binind, tbin->ncached - - tbin->low_water + (tbin->low_water >> 2), tcache); + tcache_bin_flush_large(tsd, tbin, binind, tbin->ncached + - tbin->low_water + (tbin->low_water >> 2), tcache); } /* * Reduce fill count by 2X. Limit lg_fill_div such that the @@ -65,12 +71,13 @@ tcache_event_hard(tcache_t *tcache) } void * -tcache_alloc_small_hard(tcache_t *tcache, tcache_bin_t *tbin, size_t binind) +tcache_alloc_small_hard(tsd_t *tsd, arena_t *arena, tcache_t *tcache, + tcache_bin_t *tbin, index_t binind) { void *ret; - arena_tcache_fill_small(tcache->arena, tbin, binind, - config_prof ? tcache->prof_accumbytes : 0); + arena_tcache_fill_small(arena, tbin, binind, config_prof ? + tcache->prof_accumbytes : 0); if (config_prof) tcache->prof_accumbytes = 0; ret = tcache_alloc_easy(tbin); @@ -79,9 +86,10 @@ tcache_alloc_small_hard(tcache_t *tcache, tcache_bin_t *tbin, size_t binind) } void -tcache_bin_flush_small(tcache_bin_t *tbin, size_t binind, unsigned rem, - tcache_t *tcache) +tcache_bin_flush_small(tsd_t *tsd, tcache_t *tcache, tcache_bin_t *tbin, + index_t binind, unsigned rem) { + arena_t *arena; void *ptr; unsigned i, nflush, ndeferred; bool merged_stats = false; @@ -89,22 +97,24 @@ tcache_bin_flush_small(tcache_bin_t *tbin, size_t binind, unsigned rem, assert(binind < NBINS); assert(rem <= tbin->ncached); + arena = arena_choose(tsd, NULL); + assert(arena != NULL); for (nflush = tbin->ncached - rem; nflush > 0; nflush = ndeferred) { /* Lock the arena bin associated with the first object. */ arena_chunk_t *chunk = (arena_chunk_t *)CHUNK_ADDR2BASE( tbin->avail[0]); - arena_t *arena = chunk->arena; - arena_bin_t *bin = &arena->bins[binind]; + arena_t *bin_arena = extent_node_arena_get(&chunk->node); + arena_bin_t *bin = &bin_arena->bins[binind]; - if (config_prof && arena == tcache->arena) { + if (config_prof && bin_arena == arena) { if (arena_prof_accum(arena, tcache->prof_accumbytes)) prof_idump(); tcache->prof_accumbytes = 0; } malloc_mutex_lock(&bin->lock); - if (config_stats && arena == tcache->arena) { - assert(merged_stats == false); + if (config_stats && bin_arena == arena) { + assert(!merged_stats); merged_stats = true; bin->stats.nflushes++; bin->stats.nrequests += tbin->tstats.nrequests; @@ -115,17 +125,13 @@ tcache_bin_flush_small(tcache_bin_t *tbin, size_t binind, unsigned rem, ptr = tbin->avail[i]; assert(ptr != NULL); chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr); - if (chunk->arena == arena) { + if (extent_node_arena_get(&chunk->node) == bin_arena) { size_t pageind = ((uintptr_t)ptr - (uintptr_t)chunk) >> LG_PAGE; - arena_chunk_map_t *mapelm = - arena_mapp_get(chunk, pageind); - if (config_fill && opt_junk) { - arena_alloc_junk_small(ptr, - &arena_bin_info[binind], true); - } - arena_dalloc_bin_locked(arena, chunk, ptr, - mapelm); + arena_chunk_map_bits_t *bitselm = + arena_bitselm_get(chunk, pageind); + arena_dalloc_bin_junked_locked(bin_arena, chunk, + ptr, bitselm); } else { /* * This object was allocated via a different @@ -139,12 +145,12 @@ tcache_bin_flush_small(tcache_bin_t *tbin, size_t binind, unsigned rem, } malloc_mutex_unlock(&bin->lock); } - if (config_stats && merged_stats == false) { + if (config_stats && !merged_stats) { /* * The flush loop didn't happen to flush to this thread's * arena, so the stats didn't get merged. Manually do so now. */ - arena_bin_t *bin = &tcache->arena->bins[binind]; + arena_bin_t *bin = &arena->bins[binind]; malloc_mutex_lock(&bin->lock); bin->stats.nflushes++; bin->stats.nrequests += tbin->tstats.nrequests; @@ -160,9 +166,10 @@ tcache_bin_flush_small(tcache_bin_t *tbin, size_t binind, unsigned rem, } void -tcache_bin_flush_large(tcache_bin_t *tbin, size_t binind, unsigned rem, - tcache_t *tcache) +tcache_bin_flush_large(tsd_t *tsd, tcache_bin_t *tbin, index_t binind, + unsigned rem, tcache_t *tcache) { + arena_t *arena; void *ptr; unsigned i, nflush, ndeferred; bool merged_stats = false; @@ -170,17 +177,19 @@ tcache_bin_flush_large(tcache_bin_t *tbin, size_t binind, unsigned rem, assert(binind < nhbins); assert(rem <= tbin->ncached); + arena = arena_choose(tsd, NULL); + assert(arena != NULL); for (nflush = tbin->ncached - rem; nflush > 0; nflush = ndeferred) { /* Lock the arena associated with the first object. */ arena_chunk_t *chunk = (arena_chunk_t *)CHUNK_ADDR2BASE( tbin->avail[0]); - arena_t *arena = chunk->arena; + arena_t *locked_arena = extent_node_arena_get(&chunk->node); UNUSED bool idump; if (config_prof) idump = false; - malloc_mutex_lock(&arena->lock); - if ((config_prof || config_stats) && arena == tcache->arena) { + malloc_mutex_lock(&locked_arena->lock); + if ((config_prof || config_stats) && locked_arena == arena) { if (config_prof) { idump = arena_prof_accum_locked(arena, tcache->prof_accumbytes); @@ -200,9 +209,11 @@ tcache_bin_flush_large(tcache_bin_t *tbin, size_t binind, unsigned rem, ptr = tbin->avail[i]; assert(ptr != NULL); chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr); - if (chunk->arena == arena) - arena_dalloc_large_locked(arena, chunk, ptr); - else { + if (extent_node_arena_get(&chunk->node) == + locked_arena) { + arena_dalloc_large_junked_locked(locked_arena, + chunk, ptr); + } else { /* * This object was allocated via a different * arena than the one that is currently locked. @@ -213,16 +224,15 @@ tcache_bin_flush_large(tcache_bin_t *tbin, size_t binind, unsigned rem, ndeferred++; } } - malloc_mutex_unlock(&arena->lock); + malloc_mutex_unlock(&locked_arena->lock); if (config_prof && idump) prof_idump(); } - if (config_stats && merged_stats == false) { + if (config_stats && !merged_stats) { /* * The flush loop didn't happen to flush to this thread's * arena, so the stats didn't get merged. Manually do so now. */ - arena_t *arena = tcache->arena; malloc_mutex_lock(&arena->lock); arena->stats.nrequests_large += tbin->tstats.nrequests; arena->stats.lstats[binind - NBINS].nrequests += @@ -249,24 +259,58 @@ tcache_arena_associate(tcache_t *tcache, arena_t *arena) ql_tail_insert(&arena->tcache_ql, tcache, link); malloc_mutex_unlock(&arena->lock); } - tcache->arena = arena; } void -tcache_arena_dissociate(tcache_t *tcache) +tcache_arena_reassociate(tcache_t *tcache, arena_t *oldarena, arena_t *newarena) +{ + + tcache_arena_dissociate(tcache, oldarena); + tcache_arena_associate(tcache, newarena); +} + +void +tcache_arena_dissociate(tcache_t *tcache, arena_t *arena) { if (config_stats) { /* Unlink from list of extant tcaches. */ - malloc_mutex_lock(&tcache->arena->lock); - ql_remove(&tcache->arena->tcache_ql, tcache, link); - tcache_stats_merge(tcache, tcache->arena); - malloc_mutex_unlock(&tcache->arena->lock); + malloc_mutex_lock(&arena->lock); + if (config_debug) { + bool in_ql = false; + tcache_t *iter; + ql_foreach(iter, &arena->tcache_ql, link) { + if (iter == tcache) { + in_ql = true; + break; + } + } + assert(in_ql); + } + ql_remove(&arena->tcache_ql, tcache, link); + tcache_stats_merge(tcache, arena); + malloc_mutex_unlock(&arena->lock); } } tcache_t * -tcache_create(arena_t *arena) +tcache_get_hard(tsd_t *tsd) +{ + arena_t *arena; + + if (!tcache_enabled_get()) { + if (tsd_nominal(tsd)) + tcache_enabled_set(false); /* Memoize. */ + return (NULL); + } + arena = arena_choose(tsd, NULL); + if (unlikely(arena == NULL)) + return (NULL); + return (tcache_create(tsd, arena)); +} + +tcache_t * +tcache_create(tsd_t *tsd, arena_t *arena) { tcache_t *tcache; size_t size, stack_offset; @@ -277,23 +321,10 @@ tcache_create(arena_t *arena) size = PTR_CEILING(size); stack_offset = size; size += stack_nelms * sizeof(void *); - /* - * Round up to the nearest multiple of the cacheline size, in order to - * avoid the possibility of false cacheline sharing. - * - * That this works relies on the same logic as in ipalloc(), but we - * cannot directly call ipalloc() here due to tcache bootstrapping - * issues. - */ - size = (size + CACHELINE_MASK) & (-CACHELINE); - - if (size <= SMALL_MAXCLASS) - tcache = (tcache_t *)arena_malloc_small(arena, size, true); - else if (size <= tcache_maxclass) - tcache = (tcache_t *)arena_malloc_large(arena, size, true); - else - tcache = (tcache_t *)icalloct(size, false, arena); + /* Avoid false cacheline sharing. */ + size = sa2u(size, CACHELINE); + tcache = ipallocztm(tsd, size, CACHELINE, true, false, true, a0get()); if (tcache == NULL) return (NULL); @@ -307,25 +338,23 @@ tcache_create(arena_t *arena) stack_offset += tcache_bin_info[i].ncached_max * sizeof(void *); } - tcache_tsd_set(&tcache); - return (tcache); } -void -tcache_destroy(tcache_t *tcache) +static void +tcache_destroy(tsd_t *tsd, tcache_t *tcache) { + arena_t *arena; unsigned i; - size_t tcache_size; - tcache_arena_dissociate(tcache); + arena = arena_choose(tsd, NULL); + tcache_arena_dissociate(tcache, arena); for (i = 0; i < NBINS; i++) { tcache_bin_t *tbin = &tcache->tbins[i]; - tcache_bin_flush_small(tbin, i, 0, tcache); + tcache_bin_flush_small(tsd, tcache, tbin, i, 0); if (config_stats && tbin->tstats.nrequests != 0) { - arena_t *arena = tcache->arena; arena_bin_t *bin = &arena->bins[i]; malloc_mutex_lock(&bin->lock); bin->stats.nrequests += tbin->tstats.nrequests; @@ -335,10 +364,9 @@ tcache_destroy(tcache_t *tcache) for (; i < nhbins; i++) { tcache_bin_t *tbin = &tcache->tbins[i]; - tcache_bin_flush_large(tbin, i, 0, tcache); + tcache_bin_flush_large(tsd, tbin, i, 0, tcache); if (config_stats && tbin->tstats.nrequests != 0) { - arena_t *arena = tcache->arena; malloc_mutex_lock(&arena->lock); arena->stats.nrequests_large += tbin->tstats.nrequests; arena->stats.lstats[i - NBINS].nrequests += @@ -348,57 +376,33 @@ tcache_destroy(tcache_t *tcache) } if (config_prof && tcache->prof_accumbytes > 0 && - arena_prof_accum(tcache->arena, tcache->prof_accumbytes)) + arena_prof_accum(arena, tcache->prof_accumbytes)) prof_idump(); - tcache_size = arena_salloc(tcache, false); - if (tcache_size <= SMALL_MAXCLASS) { - arena_chunk_t *chunk = CHUNK_ADDR2BASE(tcache); - arena_t *arena = chunk->arena; - size_t pageind = ((uintptr_t)tcache - (uintptr_t)chunk) >> - LG_PAGE; - arena_chunk_map_t *mapelm = arena_mapp_get(chunk, pageind); - - arena_dalloc_bin(arena, chunk, tcache, pageind, mapelm); - } else if (tcache_size <= tcache_maxclass) { - arena_chunk_t *chunk = CHUNK_ADDR2BASE(tcache); - arena_t *arena = chunk->arena; - - arena_dalloc_large(arena, chunk, tcache); - } else - idalloct(tcache, false); + idalloctm(tsd, tcache, false, true); } void -tcache_thread_cleanup(void *arg) +tcache_cleanup(tsd_t *tsd) { - tcache_t *tcache = *(tcache_t **)arg; + tcache_t *tcache; - if (tcache == TCACHE_STATE_DISABLED) { - /* Do nothing. */ - } else if (tcache == TCACHE_STATE_REINCARNATED) { - /* - * Another destructor called an allocator function after this - * destructor was called. Reset tcache to - * TCACHE_STATE_PURGATORY in order to receive another callback. - */ - tcache = TCACHE_STATE_PURGATORY; - tcache_tsd_set(&tcache); - } else if (tcache == TCACHE_STATE_PURGATORY) { - /* - * The previous time this destructor was called, we set the key - * to TCACHE_STATE_PURGATORY so that other destructors wouldn't - * cause re-creation of the tcache. This time, do nothing, so - * that the destructor will not be called again. - */ - } else if (tcache != NULL) { - assert(tcache != TCACHE_STATE_PURGATORY); - tcache_destroy(tcache); - tcache = TCACHE_STATE_PURGATORY; - tcache_tsd_set(&tcache); + if (!config_tcache) + return; + + if ((tcache = tsd_tcache_get(tsd)) != NULL) { + tcache_destroy(tsd, tcache); + tsd_tcache_set(tsd, NULL); } } +void +tcache_enabled_cleanup(tsd_t *tsd) +{ + + /* Do nothing. */ +} + /* Caller must own arena->lock. */ void tcache_stats_merge(tcache_t *tcache, arena_t *arena) @@ -427,7 +431,67 @@ tcache_stats_merge(tcache_t *tcache, arena_t *arena) } bool -tcache_boot0(void) +tcaches_create(tsd_t *tsd, unsigned *r_ind) +{ + tcache_t *tcache; + tcaches_t *elm; + + if (tcaches == NULL) { + tcaches = base_alloc(sizeof(tcache_t *) * + (MALLOCX_TCACHE_MAX+1)); + if (tcaches == NULL) + return (true); + } + + if (tcaches_avail == NULL && tcaches_past > MALLOCX_TCACHE_MAX) + return (true); + tcache = tcache_create(tsd, a0get()); + if (tcache == NULL) + return (true); + + if (tcaches_avail != NULL) { + elm = tcaches_avail; + tcaches_avail = tcaches_avail->next; + elm->tcache = tcache; + *r_ind = elm - tcaches; + } else { + elm = &tcaches[tcaches_past]; + elm->tcache = tcache; + *r_ind = tcaches_past; + tcaches_past++; + } + + return (false); +} + +static void +tcaches_elm_flush(tsd_t *tsd, tcaches_t *elm) +{ + + if (elm->tcache == NULL) + return; + tcache_destroy(tsd, elm->tcache); + elm->tcache = NULL; +} + +void +tcaches_flush(tsd_t *tsd, unsigned ind) +{ + + tcaches_elm_flush(tsd, &tcaches[ind]); +} + +void +tcaches_destroy(tsd_t *tsd, unsigned ind) +{ + tcaches_t *elm = &tcaches[ind]; + tcaches_elm_flush(tsd, elm); + elm->next = tcaches_avail; + tcaches_avail = elm; +} + +bool +tcache_boot(void) { unsigned i; @@ -442,7 +506,7 @@ tcache_boot0(void) else tcache_maxclass = (1U << opt_lg_tcache_max); - nhbins = NBINS + (tcache_maxclass >> LG_PAGE); + nhbins = size2index(tcache_maxclass) + 1; /* Initialize tcache_bin_info. */ tcache_bin_info = (tcache_bin_info_t *)base_alloc(nhbins * @@ -451,7 +515,11 @@ tcache_boot0(void) return (true); stack_nelms = 0; for (i = 0; i < NBINS; i++) { - if ((arena_bin_info[i].nregs << 1) <= TCACHE_NSLOTS_SMALL_MAX) { + if ((arena_bin_info[i].nregs << 1) <= TCACHE_NSLOTS_SMALL_MIN) { + tcache_bin_info[i].ncached_max = + TCACHE_NSLOTS_SMALL_MIN; + } else if ((arena_bin_info[i].nregs << 1) <= + TCACHE_NSLOTS_SMALL_MAX) { tcache_bin_info[i].ncached_max = (arena_bin_info[i].nregs << 1); } else { @@ -467,13 +535,3 @@ tcache_boot0(void) return (false); } - -bool -tcache_boot1(void) -{ - - if (tcache_tsd_boot() || tcache_enabled_tsd_boot()) - return (true); - - return (false); -} diff --git a/contrib/jemalloc/src/tsd.c b/contrib/jemalloc/src/tsd.c index 700caabfe477..2100833a89e4 100644 --- a/contrib/jemalloc/src/tsd.c +++ b/contrib/jemalloc/src/tsd.c @@ -7,21 +7,22 @@ static unsigned ncleanups; static malloc_tsd_cleanup_t cleanups[MALLOC_TSD_CLEANUPS_MAX]; +malloc_tsd_data(, , tsd_t, TSD_INITIALIZER) + /******************************************************************************/ void * malloc_tsd_malloc(size_t size) { - /* Avoid choose_arena() in order to dodge bootstrapping issues. */ - return (arena_malloc(arenas[0], size, false, false)); + return (a0malloc(CACHELINE_CEILING(size))); } void malloc_tsd_dalloc(void *wrapper) { - idalloct(wrapper, false); + a0dalloc(wrapper); } void @@ -67,10 +68,58 @@ malloc_tsd_cleanup_register(bool (*f)(void)) } void -malloc_tsd_boot(void) +tsd_cleanup(void *arg) +{ + tsd_t *tsd = (tsd_t *)arg; + + switch (tsd->state) { + case tsd_state_nominal: +#define O(n, t) \ + n##_cleanup(tsd); +MALLOC_TSD +#undef O + tsd->state = tsd_state_purgatory; + tsd_set(tsd); + break; + case tsd_state_purgatory: + /* + * The previous time this destructor was called, we set the + * state to tsd_state_purgatory so that other destructors + * wouldn't cause re-creation of the tsd. This time, do + * nothing, and do not request another callback. + */ + break; + case tsd_state_reincarnated: + /* + * Another destructor deallocated memory after this destructor + * was called. Reset state to tsd_state_purgatory and request + * another callback. + */ + tsd->state = tsd_state_purgatory; + tsd_set(tsd); + break; + default: + not_reached(); + } +} + +bool +malloc_tsd_boot0(void) { ncleanups = 0; + if (tsd_boot0()) + return (true); + *tsd_arenas_cache_bypassp_get(tsd_fetch()) = true; + return (false); +} + +void +malloc_tsd_boot1(void) +{ + + tsd_boot1(); + *tsd_arenas_cache_bypassp_get(tsd_fetch()) = false; } #ifdef _WIN32 @@ -102,7 +151,7 @@ _tls_callback(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) # pragma section(".CRT$XLY",long,read) #endif JEMALLOC_SECTION(".CRT$XLY") JEMALLOC_ATTR(used) -static const BOOL (WINAPI *tls_callback)(HINSTANCE hinstDLL, +static BOOL (WINAPI *const tls_callback)(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) = _tls_callback; #endif diff --git a/contrib/jemalloc/src/util.c b/contrib/jemalloc/src/util.c index 70b3e4534b2f..25b61c207e68 100644 --- a/contrib/jemalloc/src/util.c +++ b/contrib/jemalloc/src/util.c @@ -97,10 +97,10 @@ buferror(int err, char *buf, size_t buflen) { #ifdef _WIN32 - FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), 0, + FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, err, 0, (LPSTR)buf, buflen, NULL); return (0); -#elif defined(_GNU_SOURCE) +#elif defined(__GLIBC__) && defined(_GNU_SOURCE) char *b = strerror_r(err, buf, buflen); if (b != buf) { strncpy(buf, b, buflen); @@ -116,7 +116,7 @@ uintmax_t malloc_strtoumax(const char *restrict nptr, char **restrict endptr, int base) { uintmax_t ret, digit; - int b; + unsigned b; bool neg; const char *p, *ns; @@ -282,7 +282,7 @@ d2s(intmax_t x, char sign, char *s, size_t *slen_p) sign = '-'; switch (sign) { case '-': - if (neg == false) + if (!neg) break; /* Fall through. */ case ' ': @@ -345,7 +345,7 @@ malloc_vsnprintf(char *str, size_t size, const char *format, va_list ap) /* Left padding. */ \ size_t pad_len = (width == -1) ? 0 : ((slen < (size_t)width) ? \ (size_t)width - slen : 0); \ - if (left_justify == false && pad_len != 0) { \ + if (!left_justify && pad_len != 0) { \ size_t j; \ for (j = 0; j < pad_len; j++) \ APPEND_C(' '); \ @@ -397,7 +397,9 @@ malloc_vsnprintf(char *str, size_t size, const char *format, va_list ap) case 'p': /* Synthetic; used for %p. */ \ val = va_arg(ap, uintptr_t); \ break; \ - default: not_reached(); \ + default: \ + not_reached(); \ + val = 0; \ } \ } while (0) @@ -420,19 +422,19 @@ malloc_vsnprintf(char *str, size_t size, const char *format, va_list ap) while (true) { switch (*f) { case '#': - assert(alt_form == false); + assert(!alt_form); alt_form = true; break; case '-': - assert(left_justify == false); + assert(!left_justify); left_justify = true; break; case ' ': - assert(plus_space == false); + assert(!plus_space); plus_space = true; break; case '+': - assert(plus_plus == false); + assert(!plus_plus); plus_plus = true; break; default: goto label_width; @@ -564,7 +566,7 @@ malloc_vsnprintf(char *str, size_t size, const char *format, va_list ap) assert(len == '?' || len == 'l'); assert_not_implemented(len != 'l'); s = va_arg(ap, char *); - slen = (prec < 0) ? strlen(s) : prec; + slen = (prec < 0) ? strlen(s) : (size_t)prec; APPEND_PADDED_S(s, slen, width, left_justify); f++; break; @@ -600,7 +602,7 @@ malloc_vsnprintf(char *str, size_t size, const char *format, va_list ap) return (ret); } -JEMALLOC_ATTR(format(printf, 3, 4)) +JEMALLOC_FORMAT_PRINTF(3, 4) int malloc_snprintf(char *str, size_t size, const char *format, ...) { @@ -639,7 +641,7 @@ malloc_vcprintf(void (*write_cb)(void *, const char *), void *cbopaque, * Print to a callback function in such a way as to (hopefully) avoid memory * allocation. */ -JEMALLOC_ATTR(format(printf, 3, 4)) +JEMALLOC_FORMAT_PRINTF(3, 4) void malloc_cprintf(void (*write_cb)(void *, const char *), void *cbopaque, const char *format, ...) @@ -652,7 +654,7 @@ malloc_cprintf(void (*write_cb)(void *, const char *), void *cbopaque, } /* Print to stderr in such a way as to avoid memory allocation. */ -JEMALLOC_ATTR(format(printf, 1, 2)) +JEMALLOC_FORMAT_PRINTF(1, 2) void malloc_printf(const char *format, ...) { diff --git a/contrib/libarchive/libarchive/archive_read_support_format_tar.c b/contrib/libarchive/libarchive/archive_read_support_format_tar.c index fde339a638c7..c2003e78acb6 100644 --- a/contrib/libarchive/libarchive/archive_read_support_format_tar.c +++ b/contrib/libarchive/libarchive/archive_read_support_format_tar.c @@ -585,13 +585,23 @@ static int archive_read_format_tar_skip(struct archive_read *a) { int64_t bytes_skipped; + int64_t request; + struct sparse_block *p; struct tar* tar; tar = (struct tar *)(a->format->data); - bytes_skipped = __archive_read_consume(a, - tar->entry_bytes_remaining + tar->entry_padding + - tar->entry_bytes_unconsumed); + /* Do not consume the hole of a sparse file. */ + request = 0; + for (p = tar->sparse_list; p != NULL; p = p->next) { + if (!p->hole) + request += p->remaining; + } + if (request > tar->entry_bytes_remaining) + request = tar->entry_bytes_remaining; + request += tar->entry_padding + tar->entry_bytes_unconsumed; + + bytes_skipped = __archive_read_consume(a, request); if (bytes_skipped < 0) return (ARCHIVE_FATAL); diff --git a/contrib/libarchive/libarchive/test/test_read_format_gtar_sparse_skip_entry.c b/contrib/libarchive/libarchive/test/test_read_format_gtar_sparse_skip_entry.c new file mode 100644 index 000000000000..20567c7bc0f1 --- /dev/null +++ b/contrib/libarchive/libarchive/test/test_read_format_gtar_sparse_skip_entry.c @@ -0,0 +1,135 @@ +/*- + * Copyright (c) 2014 Michihiro NAKAJIMA + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "test.h" +__FBSDID("$FreeBSD"); + +/* + * To test skip a sparse file entry, this test does not read file data. + */ +DEFINE_TEST(test_read_format_gtar_sparse_skip_entry) +{ +#ifndef __FreeBSD__ /* Backport test. */ + const char *refname = "test_read_format_gtar_sparse_skip_entry.tar.Z.uu"; +#else + const char *refname = "test_read_format_gtar_sparse_skip_entry.tar.Z"; +#endif + struct archive *a; + struct archive_entry *ae; + const void *p; + size_t s; + int64_t o; + +#ifndef __FreeBSD__ /* Backport test. */ + copy_reference_file(refname); +#else + extract_reference_file(refname); +#endif + assert((a = archive_read_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a)); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_open_filename(a, refname, 10240)); + + /* Verify regular first file. */ + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString("a", archive_entry_pathname(ae)); + assertEqualInt(10737418244, archive_entry_size(ae)); +#ifndef __FreeBSD__ /* Backport test. */ + assertEqualInt(archive_entry_is_encrypted(ae), 0); + assertEqualIntA(a, archive_read_has_encrypted_entries(a), + ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED); +#endif + + /* Verify regular second file. */ + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString("b", archive_entry_pathname(ae)); + assertEqualInt(4, archive_entry_size(ae)); +#ifndef __FreeBSD__ /* Backport test. */ + assertEqualInt(archive_entry_is_encrypted(ae), 0); + assertEqualIntA(a, archive_read_has_encrypted_entries(a), + ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED); +#endif + + + /* End of archive. */ + assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); + + /* Verify archive format. */ + assertEqualIntA(a, ARCHIVE_FILTER_COMPRESS, archive_filter_code(a, 0)); + assertEqualIntA(a, ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE, + archive_format(a)); + + /* Close the archive. */ + assertEqualInt(ARCHIVE_OK, archive_read_close(a)); + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); + + + /* + * Read just one block of a sparse file and skip it. + */ + assert((a = archive_read_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a)); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_open_filename(a, refname, 10240)); + + /* Verify regular first file. */ + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString("a", archive_entry_pathname(ae)); + assertEqualInt(10737418244, archive_entry_size(ae)); +#ifndef __FreeBSD__ /* Backport test. */ + assertEqualInt(archive_entry_is_encrypted(ae), 0); + assertEqualIntA(a, archive_read_has_encrypted_entries(a), + ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED); +#endif + assertEqualInt(0, archive_read_data_block(a, &p, &s, &o)); + assertEqualInt(4096, s); + assertEqualInt(0, o); + + + /* Verify regular second file. */ + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString("b", archive_entry_pathname(ae)); + assertEqualInt(4, archive_entry_size(ae)); +#ifndef __FreeBSD__ /* Backport test. */ + assertEqualInt(archive_entry_is_encrypted(ae), 0); + assertEqualIntA(a, archive_read_has_encrypted_entries(a), + ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED); +#endif + + + /* End of archive. */ + assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); + + /* Verify archive format. */ + assertEqualIntA(a, ARCHIVE_FILTER_COMPRESS, archive_filter_code(a, 0)); + assertEqualIntA(a, ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE, + archive_format(a)); + + /* Close the archive. */ + assertEqualInt(ARCHIVE_OK, archive_read_close(a)); + assertEqualInt(ARCHIVE_OK, archive_read_free(a)); +} + diff --git a/contrib/libarchive/libarchive/test/test_read_format_gtar_sparse_skip_entry.tar.Z.uu b/contrib/libarchive/libarchive/test/test_read_format_gtar_sparse_skip_entry.tar.Z.uu new file mode 100644 index 000000000000..dc0daae9e1e2 --- /dev/null +++ b/contrib/libarchive/libarchive/test/test_read_format_gtar_sparse_skip_entry.tar.Z.uu @@ -0,0 +1,15 @@ +begin 644 - +M'YV04,+@05(F#)DRBD:;,V!@T8-6)NE&'#10T<-#;>R(%CAEV28_3R9?LW\(P8-F[`<#%C +M)@T<->#6>`PBC.2^E07;J#'#Q>J-F5DJ<`GBB),J+N;`<3JGC(LV8=2\D<-V +M]DO;N'7S]MTFC9OA/6#,CE'[=N[=$V +M9]RY=212"9YD1EOO*&`DE!&*>645%9IY9589JGE +MEEQVZ>678(8IYIADEFGFF6BFJ>::;+;IYIMPQBGGG'36:>>=>.:IYYY\]NGG +MGX`&*NB@A!9JJ)YB](D@1PZ>U&B#*468484RT11###7<8!8(&-)4PX=^DN@4 +B5%*E6.J*746JTHN'2LFDDZW&*NNLM-9JZZVXYJKKKKR&!0`` +` +end diff --git a/contrib/libxo/.gitignore b/contrib/libxo/.gitignore index 386bfc883cb5..8d70b6cc1550 100644 --- a/contrib/libxo/.gitignore +++ b/contrib/libxo/.gitignore @@ -27,14 +27,17 @@ config.guess config.h.in config.sub depcomp +install-sh ltmain.sh missing +m4 Makefile.in configure .DS_Store xoconfig.h.in +xo_config.h.in .gdbinit .gdbinit.local diff --git a/contrib/libxo/.travis.yml b/contrib/libxo/.travis.yml index e26a7699ca23..1173578bbd5d 100644 --- a/contrib/libxo/.travis.yml +++ b/contrib/libxo/.travis.yml @@ -1,6 +1,6 @@ language: c -script: printenv && uname -a && /bin/sh ./bin/setup.sh && cd build && ../configure --enable-warnings && make && sudo make install && make test +script: printenv && uname -a && ls -l && /bin/sh -x ./bin/setup.sh && cd build && ../configure --enable-warnings && make && sudo make install && make test notifications: recipients: diff --git a/contrib/libxo/INSTALL.md b/contrib/libxo/INSTALL.md new file mode 100644 index 000000000000..70b80bcea926 --- /dev/null +++ b/contrib/libxo/INSTALL.md @@ -0,0 +1,15 @@ + + +## Instructions for building libxo + +Instructions for building libxo are now available in the +[wiki](http://juniper.github.io/libxo/libxo-manual.html#getting-libxo). diff --git a/contrib/libxo/Makefile.am b/contrib/libxo/Makefile.am index 1abfd5e7cf25..e050bc46f339 100644 --- a/contrib/libxo/Makefile.am +++ b/contrib/libxo/Makefile.am @@ -10,7 +10,7 @@ ACLOCAL_AMFLAGS = -I m4 -SUBDIRS = libxo xo xolint xohtml tests doc +SUBDIRS = libxo xo xopo xolint xohtml tests doc encoder bin_SCRIPTS=libxo-config dist_doc_DATA = Copyright @@ -94,3 +94,9 @@ packages: && git commit -m 'new packaging data' \ ${GH_PACKAGING_DIR} \ && git push origin gh-pages ) ; true + +ANALYZE_DIR = ~/trash/libxo +ANALYZE_CMD = scan-build-mp-3.6 + +analyze: + ${ANALYZE_CMD} -o ${ANALYZE_DIR} ${MAKE} diff --git a/contrib/libxo/bin/Zaliases b/contrib/libxo/bin/Zaliases index b8fb5dbe5220..04cdec7720b3 100644 --- a/contrib/libxo/bin/Zaliases +++ b/contrib/libxo/bin/Zaliases @@ -6,6 +6,7 @@ set opts=' \ --enable-debug \ --enable-warnings \ --enable-printflike \ +--with-gettext=/opt/local \ --prefix ${HOME}/work/root \ ' set opts=`echo $opts` @@ -22,3 +23,7 @@ cd build alias xx 'cc -I.. -W -Wall -Wstrict-prototypes -Wmissing-prototypes -Wpointer-arith -Werror -Waggregate-return -Wcast-align -Wcast-qual -Wchar-subscripts -Wcomment -Wformat -Wimplicit -Wmissing-declarations -Wnested-externs -Wparentheses -Wreturn-type -Wshadow -Wswitch -Wtrigraphs -Wuninitialized -Wunused -Wwrite-strings -fno-inline-functions-called-once -g -O2 -o xtest -DUNIT_TEST libxo.c' + +alias mm "make CFLAGS='-O0 -g'" + +alias mmi 'mm && mi' diff --git a/contrib/libxo/bin/setup.sh b/contrib/libxo/bin/setup.sh index 5e03ff35e142..f49dd48dd860 100755 --- a/contrib/libxo/bin/setup.sh +++ b/contrib/libxo/bin/setup.sh @@ -11,6 +11,8 @@ if [ ! -f configure ]; then vers=`autoreconf --version | head -1` echo "Using" $vers + mkdir -p m4 + autoreconf --install if [ ! -f configure ]; then diff --git a/contrib/libxo/configure.ac b/contrib/libxo/configure.ac index 5491d573b18c..1783120e4e57 100644 --- a/contrib/libxo/configure.ac +++ b/contrib/libxo/configure.ac @@ -12,7 +12,7 @@ # AC_PREREQ(2.2) -AC_INIT([libxo], [0.3.2], [phil@juniper.net]) +AC_INIT([libxo], [0.4.5], [phil@juniper.net]) AM_INIT_AUTOMAKE([-Wall -Werror foreign -Wno-portability]) # Support silent build rules. Requires at least automake-1.11. @@ -54,11 +54,15 @@ AC_CHECK_FUNCS([sranddev srand strlcpy]) AC_CHECK_FUNCS([fdopen getrusage]) AC_CHECK_FUNCS([gettimeofday ctime]) AC_CHECK_FUNCS([getpass]) +AC_CHECK_FUNCS([getprogname]) AC_CHECK_FUNCS([sysctlbyname]) AC_CHECK_FUNCS([flock]) AC_CHECK_FUNCS([asprintf]) AC_CHECK_FUNCS([__flbf]) +AC_CHECK_FUNCS([sysctlbyname]) + +AC_CHECK_HEADERS([dlfcn.h]) AC_CHECK_HEADERS([dlfcn.h]) AC_CHECK_HEADERS([stdio_ext.h]) AC_CHECK_HEADERS([tzfile.h]) @@ -69,10 +73,139 @@ AC_CHECK_HEADERS([sys/time.h]) AC_CHECK_HEADERS([ctype.h errno.h stdio.h stdlib.h]) AC_CHECK_HEADERS([string.h sys/param.h unistd.h ]) AC_CHECK_HEADERS([sys/sysctl.h]) +AC_CHECK_HEADERS([threads.h]) +dnl humanize_number(3) is a great function, but it's not standard. +dnl Note Macosx has the function in libutil.a but doesn't ship the +dnl header file, so I'll need to carry my own implementation. See: +dnl https://devforums.apple.com/thread/271121 +AC_CHECK_HEADERS([libutil.h]) +AC_CHECK_LIB([util], [humanize_number], + [HAVE_HUMANIZE_NUMBER=$ac_cv_header_libutil_h], + [HAVE_HUMANIZE_NUMBER=no]) + +AC_MSG_RESULT(humanize_number results: :${HAVE_HUMANIZE_NUMBER}:${ac_cv_header_libutil_h}:) + +if test "$HAVE_HUMANIZE_NUMBER" = "yes"; then + AC_DEFINE([HAVE_HUMANIZE_NUMBER], [1], [humanize_number(3)]) +fi + +AM_CONDITIONAL([HAVE_HUMANIZE_NUMBER], [test "$HAVE_HUMANIZE_NUMBER" = "yes"]) + +AC_ARG_ENABLE([gettext], + [ --disable-gettext Turn off support for gettext], + [GETTEXT_ENABLE=$enableval], + [GETTEXT_ENABLE=yes]) + +dnl Looking for gettext(), assumably in libintl +AC_ARG_WITH(gettext, + [ --with-gettext=[PFX] Specify location of gettext installation], + [GETTEXT_PREFIX=$withval], + [GETTEXT_PREFIX=/usr], +) + +HAVE_GETTEXT=no + +if test "$GETTEXT_ENABLE" != "no"; then + + AC_MSG_CHECKING([gettext in ${GETTEXT_PREFIX}]) + + _save_cflags="$CFLAGS" + CFLAGS="$CFLAGS -I${GETTEXT_PREFIX}/include -L${GETTEXT_PREFIX}/lib -Werror -lintl" + AC_LINK_IFELSE([AC_LANG_SOURCE([[#include ] + [int main() {char *cp = dgettext(NULL, "xx"); return 0; }]])], + [HAVE_GETTEXT=yes], + [HAVE_GETTEXT=no]) + CFLAGS="$_save_cflags" + + AC_MSG_RESULT([$HAVE_GETTEXT]) + + if test "$HAVE_GETTEXT" != "yes"; then + GETTEXT_PREFIX=/opt/local + AC_MSG_CHECKING([gettext in ${GETTEXT_PREFIX}]) + + _save_cflags="$CFLAGS" + CFLAGS="$CFLAGS -I${GETTEXT_PREFIX}/include -L${GETTEXT_PREFIX}/lib -Werror -lintl" + AC_LINK_IFELSE([AC_LANG_SOURCE([[#include ] + [int main() {char *cp = dgettext(NULL, "xx"); return 0; }]])], + [HAVE_GETTEXT=yes], + [HAVE_GETTEXT=no]) + CFLAGS="$_save_cflags" + + AC_MSG_RESULT([$HAVE_GETTEXT]) + fi +fi + +if test "$HAVE_GETTEXT" = "yes"; then + AC_DEFINE([HAVE_GETTEXT], [1], [gettext(3)]) + GETTEXT_CFLAGS="-I${GETTEXT_PREFIX}/include" + GETTEXT_LIBS="-L${GETTEXT_PREFIX}/lib -lintl" +else + GETTEXT_PREFIX=none + GETTEXT_CFLAGS= + GETTEXT_LIBS= +fi +AC_SUBST(GETTEXT_CFLAGS) +AC_SUBST(GETTEXT_LIBS) + +GETTEXT_BINDIR=${GETTEXT_PREFIX}/bin +AC_SUBST(GETTEXT_BINDIR) +GETTEXT_LIBDIR=${GETTEXT_PREFIX}/lib +AC_SUBST(GETTEXT_LIBDIR) + +AM_CONDITIONAL([HAVE_GETTEXT], [test "$HAVE_GETTEXT" = "yes"]) + +dnl Looking for how to do thread-local variables +AC_ARG_WITH(threads, + [ --with-threads=[STYLE] Specify style of thread-local support (none)], + [THREAD_LOCAL=$withval], + [THREAD_LOCAL=unknown], +) + +AC_MSG_CHECKING([thread-locals are ${THREAD_LOCAL}]) + +if test "$THREAD_LOCAL" = "unknown"; then + AC_LINK_IFELSE([AC_LANG_SOURCE([[] + [__thread int foo; int main() { foo++; return foo; }]])], + [THREAD_LOCAL=before], + [THREAD_LOCAL=unknown]) + + AC_MSG_RESULT([$THREAD_LOCAL]) +fi + +if test "$THREAD_LOCAL" = "unknown"; then + AC_LINK_IFELSE([AC_LANG_SOURCE([[] + [int __thread foo; int main() { foo++; return foo; }]])], + [THREAD_LOCAL=after], + [THREAD_LOCAL=unknown]) + AC_MSG_RESULT([$THREAD_LOCAL]) +fi + +if test "$THREAD_LOCAL" = "unknown"; then + AC_LINK_IFELSE([AC_LANG_SOURCE([[] + [__declspec(int) foo; int main() { foo++; return foo; }]])], + [THREAD_LOCAL=declspec], + [THREAD_LOCAL=unknown]) + AC_MSG_RESULT([$THREAD_LOCAL]) +fi + +if test "$THREAD_LOCAL" != "unknown"; then + AC_DEFINE_UNQUOTED([HAVE_THREAD_LOCAL], + THREAD_LOCAL_${THREAD_LOCAL}, [thread-local setting]) +fi + +dnl Looking for libcrypto.... AC_CHECK_LIB([crypto], [MD5_Init]) AM_CONDITIONAL([HAVE_LIBCRYPTO], [test "$HAVE_LIBCRYPTO" != "no"]) +AC_CHECK_MEMBER([struct sockaddr_un.sun_len], + [HAVE_SUN_LEN=yes ; + AC_DEFINE([HAVE_SUN_LEN], [1], [Have struct sockaddr_un.sun_len])], + [HAS_SUN_LEN=no], [[#include ]]) + +AC_CHECK_DECLS([__isthreaded], [], [], [#include ]) +HAVE_ISTHREADED=${ac_cv_have_decl___isthreaded} + dnl dnl Some packages need to be checked against version numbers so we dnl define a function here for later use @@ -107,12 +240,15 @@ then SLAX_BINDIR="`$SLAX_CONFIG --bindir | head -1`" SLAX_OXTRADOCDIR="`$SLAX_CONFIG --oxtradoc | head -1`" AC_MSG_RESULT($LIBSLAX_VERSION found) + HAVE_OXTRADOC=yes else LIBSLAX_VERSION= SLAX_BINDIR= SLAX_OXTRADOCDIR= AC_MSG_RESULT([no]) + HAVE_OXTRADOC=no fi +AM_CONDITIONAL([HAVE_OXTRADOC], [test "$HAVE_OXTRADOC" != "no"]) AC_SUBST(SLAX_BINDIR) AC_SUBST(SLAX_OXTRADOCDIR) @@ -141,6 +277,16 @@ AC_ARG_ENABLE([text-only], AC_MSG_RESULT([$LIBXO_TEXT_ONLY]) AM_CONDITIONAL([LIBXO_TEXT_ONLY], [test "$LIBXO_TEXT_ONLY" != "no"]) +AC_MSG_CHECKING([whether to build with local wcwidth implementation]) +AC_ARG_ENABLE([wcwidth], + [ --disable-wcwidth Disable local wcwidth implementation], + [LIBXO_WCWIDTH=$enableval], + [LIBXO_WCWIDTH=yes]) +AC_MSG_RESULT([$LIBXO_WCWIDTH]) +if test "${LIBXO_WCWIDTH}" != "no"; then + AC_DEFINE([LIBXO_WCWIDTH], [1], [Enable local wcwidth implementation]) +fi + AC_CHECK_LIB([m], [lrint]) AM_CONDITIONAL([HAVE_LIBM], [test "$HAVE_LIBM" != "no"]) @@ -177,13 +323,16 @@ AM_CONDITIONAL([NO_LIBXO_OPTIONS], [test "$LIBXO_OPTS" != "yes"]) case $host_os in darwin*) LIBTOOL=glibtool + XO_LIBEXT=dylib ;; Linux*|linux*) CFLAGS="-D_GNU_SOURCE $CFLAGS" LDFLAGS=-ldl + XO_LIBEXT=so ;; cygwin*|CYGWIN*) LDFLAGS=-no-undefined + XO_LIBEXT=ddl ;; esac @@ -203,6 +352,14 @@ AC_SUBST(XO_SRCDIR) AC_SUBST(XO_LIBDIR) AC_SUBST(XO_BINDIR) AC_SUBST(XO_INCLUDEDIR) +AC_SUBST(XO_LIBEXT) + +AC_ARG_WITH(encoder-dir, + [ --with-encoder-dir=[DIR] Specify location of encoder libraries], + [XO_ENCODERDIR=$withval], + [XO_ENCODERDIR=$libdir/libxo/encoder] +) +AC_SUBST(XO_ENCODERDIR) AC_ARG_WITH(share-dir, [ --with-share-dir=[DIR] Specify location of shared files], @@ -232,20 +389,34 @@ AC_SUBST(LIBXO_VERSION) AC_SUBST(LIBXO_VERSION_NUMBER) AC_SUBST(LIBXO_VERSION_EXTRA) -AC_CONFIG_HEADERS([libxo/xoconfig.h]) +AC_DEFINE_UNQUOTED(LIBXO_VERSION, ["$LIBXO_VERSION"], + [Version number as dotted value]) +AC_DEFINE_UNQUOTED(LIBXO_VERSION_NUMBER, [$LIBXO_VERSION_NUMBER], + [Version number as a number]) +AC_DEFINE_UNQUOTED(LIBXO_VERSION_STRING, ["$LIBXO_VERSION_NUMBER"], + [Version number as string]) +AC_DEFINE_UNQUOTED(LIBXO_VERSION_EXTRA, ["$LIBXO_VERSION_EXTRA"], + [Version number extra information]) + +AC_CONFIG_HEADERS([libxo/xo_config.h]) AC_CONFIG_FILES([ Makefile libxo-config xohtml/xohtml.sh libxo/Makefile - libxo/xoversion.h + libxo/add.man + encoder/Makefile + encoder/cbor/Makefile + encoder/test/Makefile xo/Makefile xolint/Makefile xohtml/Makefile + xopo/Makefile packaging/libxo.pc doc/Makefile tests/Makefile tests/core/Makefile + tests/gettext/Makefile tests/xo/Makefile packaging/libxo.spec packaging/libxo.rb.base @@ -262,6 +433,7 @@ AC_MSG_NOTICE([summary of build options: bindir: ${XO_BINDIR} includedir: ${XO_INCLUDEDIR} share dir: ${XO_SHAREDIR} + extensions dir: ${XO_ENCODERDIR} oxtradoc dir: ${SLAX_OXTRADOCDIR} compiler: ${CC} (${HAVE_GCC:-no}) @@ -273,4 +445,8 @@ AC_MSG_NOTICE([summary of build options: printf-like: ${HAVE_PRINTFLIKE:-no} libxo-options: ${LIBXO_OPTS:-no} text-only: ${LIBXO_TEXT_ONLY:-no} + gettext: ${HAVE_GETTEXT:-no} (${GETTEXT_PREFIX}) + isthreaded: ${HAVE_ISTHREADED:-no} + thread-local: ${THREAD_LOCAL:-no} + local wcwidth: ${LIBXO_WCWIDTH:-no} ]) diff --git a/contrib/libxo/doc/Makefile.am b/contrib/libxo/doc/Makefile.am index c0c327197a0b..16d6ba5bffaf 100644 --- a/contrib/libxo/doc/Makefile.am +++ b/contrib/libxo/doc/Makefile.am @@ -8,6 +8,7 @@ # using the SOFTWARE, you agree to be bound by the terms of that # LICENSE. +if HAVE_OXTRADOC OXTRADOC_DIR = ${SLAX_OXTRADOCDIR} OXTRADOC_PREFIX = ${OXTRADOC_DIR} OXTRADOC = ${OXTRADOC_DIR}/oxtradoc @@ -38,28 +39,32 @@ OX_ARGS += -S ${SLAXPROC} -p doc OX_CMD = ${PERL} ${PERLOPTS} ${OXTRADOC} ${OX_ARGS} OXTRADOC_CMD = ${OX_CMD} - OUTPUT = libxo-manual -INPUT = libxo.txt +INPUT = libxo EXTRA_DIST = \ - ${INPUT} \ + ${INPUT}.txt \ ${OUTPUT}.html \ ${OUTPUT}.txt doc docs: ${OUTPUT}.txt ${OUTPUT}.html -${OUTPUT}.txt: ${INPUT} ${OXTRADOC} xolint.txt +${OUTPUT}.txt: ${INPUT}.txt ${OXTRADOC} xolint.txt ${OXTRADOC_CMD} -m text -o $@ $< -${OUTPUT}.html: ${INPUT} ${OXTRADOC} ${XML2HTMLBIN} xolint.txt +${OUTPUT}.html: ${INPUT}.txt ${OXTRADOC} ${XML2HTMLBIN} xolint.txt ${OXTRADOC_CMD} -m html -o $@ $< xolint.txt: ${top_srcdir}/xolint/xolint.pl perl ${top_srcdir}/xolint/xolint.pl -D > xolint.txt CLEANFILES = \ -${OUTPUT}.xml \ -${OUTPUT}.txt \ -${OUTPUT}.fxml \ -${OUTPUT}.html +xolint.txt \ +${INPUT}.xml \ +${INPUT}.txt \ +${INPUT}.fxml \ +${INPUT}.html +else +doc docs: + @${ECHO} "The 'oxtradoc' tool is not installed; see libslax.org" +endif diff --git a/contrib/libxo/doc/libxo.txt b/contrib/libxo/doc/libxo.txt index 81a40b61f06d..1e7acc7984be 100644 --- a/contrib/libxo/doc/libxo.txt +++ b/contrib/libxo/doc/libxo.txt @@ -8,7 +8,7 @@ # Phil Shafer, July 2014 # -* libxo +* Overview libxo - A Library for Generating Text, XML, JSON, and HTML Output @@ -28,10 +28,10 @@ decides at run time which output style should be produced. The application calls a function "xo_emit" to product output that is described in a format string. A "field descriptor" tells libxo what the field is and what it means. Each field descriptor is placed in -braces with a printf-like format string: +braces with a printf-like format string (^format-strings^): xo_emit(" {:lines/%7ju} {:words/%7ju} " - "{:characters/%7ju}{d:filename/%s}\n", + "{:characters/%7ju} {d:filename/%s}\n", linect, wordct, charct, file); Each field can have a role, with the 'value' role being the default, @@ -43,10 +43,10 @@ can then be generated in various style, using the "--libxo" option: % wc --libxo xml,pretty,warn /etc/motd - /etc/motd 25 165 1140 + /etc/motd % wc --libxo json,pretty,warn /etc/motd @@ -54,10 +54,10 @@ can then be generated in various style, using the "--libxo" option: "wc": { "file": [ { - "filename": "/etc/motd", "lines": 25, "words": 165, - "characters": 1140 + "characters": 1140, + "filename": "/etc/motd" } ] } @@ -95,10 +95,151 @@ command: We're using semantic release numbering, as defined in ^http://semver.org/spec/v2.0.0.html^. -libxo is open source, distributed under the BSD license. It -is shipped as part of FreeBSD 11.0. +libxo is open source, distributed under the BSD license. It shipped +as part of the FreeBSD operating system starting with release 11.0. -* Overview +Issues, problems, and bugs should be directly to the issues page on +our github site. + +*** Downloading libxo Source Code + +You can retrieve the source for libxo in two ways: + +A) Use a "distfile" for a specific release. We use +github to maintain our releases. Visit +github release page (^https://github.com/Juniper/libxo/releases^) +to see the list of releases. To download the latest, look for the +release with the green "Latest release" button and the green +"libxo-RELEASE.tar.gz" button under that section. + +After downloading that release's distfile, untar it as follows: + + tar -zxf libxo-RELEASE.tar.gz + cd libxo-RELEASE + +[Note: for Solaris users, your "tar" command lacks the "-z" flag, +so you'll need to substitute "gzip -dc "file" | tar xf -" instead of +"tar -zxf "file"".] + +B) Use the current build from github. This gives you the most recent +source code, which might be less stable than a specific release. To +build libxo from the git repo: + + git clone https://github.com/Juniper/libxo.git + cd libxo + +_BE AWARE_: The github repository does _not_ contain the files +generated by "autoreconf", with the notable exception of the "m4" +directory. Since these files (depcomp, configure, missing, +install-sh, etc) are generated files, we keep them out of the source +code repository. + +This means that if you download the a release distfile, these files +will be ready and you'll just need to run "configure", but if you +download the source code from svn, then you'll need to run +"autoreconf" by hand. This step is done for you by the "setup.sh" +script, described in the next section. + +*** Building libxo + +To build libxo, you'll need to set up the build, run the "configure" +script, run the "make" command, and run the regression tests. + +The following is a summary of the commands needed. These commands are +explained in detail in the rest of this section. + + sh bin/setup.sh + cd build + ../configure + make + make test + sudo make install + +The following sections will walk thru each of these steps with +additional details and options, but the above directions should be all +that's needed. + +**** Setting up the build + +[If you downloaded a distfile, you can skip this step.] + +Run the "setup.sh" script to set up the build. This script runs the +"autoreconf" command to generate the "configure" script and other +generated files. + + sh bin/setup.sh + +Note: We're are currently using autoreconf version 2.69. + +**** Running the "configure" Script + +Configure (and autoconf in general) provides a means of building +software in diverse environments. Our configure script supports +a set of options that can be used to adjust to your operating +environment. Use "configure --help" to view these options. + +We use the "build" directory to keep object files and generated files +away from the source tree. + +To run the configure script, change into the "build" directory, and +run the "configure" script. Add any required options to the +"../configure" command line. + + cd build + ../configure + +Expect to see the "configure" script generate the following error: + + /usr/bin/rm: cannot remove `libtoolT': No such file or directory + +This error is harmless and can be safely ignored. + +By default, libxo installs architecture-independent files, including +extension library files, in the /usr/local directories. To specify an +installation prefix other than /usr/local for all installation files, +include the --prefix=prefix option and specify an alternate +location. To install just the extension library files in a different, +user-defined location, include the --with-extensions-dir=dir option +and specify the location where the extension libraries will live. + + cd build + ../configure [OPTION]... [VAR=VALUE]... + +**** Running the "make" command + +Once the "configure" script is run, build the images using the "make" +command: + + make + +**** Running the Regression Tests + +libxo includes a set of regression tests that can be run to ensure +the software is working properly. These test are optional, but will +help determine if there are any issues running libxo on your +machine. To run the regression tests: + + make test + +**** Installing libxo + +Once the software is built, you'll need to install libxo using the +"make install" command. If you are the root user, or the owner of the +installation directory, simply issue the command: + + make install + +If you are not the "root" user and are using the "sudo" package, use: + + sudo make install + +Verify the installation by viewing the output of "xo --version": + + % xo --version + libxo version 0.3.5-git-develop + xo version 0.3.5-git-develop + +* Formatting with libxo Most unix commands emit text output aimed at humans. It is designed to be parsed and understood by a user. Humans are gifted at @@ -128,26 +269,41 @@ A single libxo function call in source code is all that's required: xo_emit("Connecting to {:host}.{:domain}...\n", host, domain); - Text: - Connection to my-box.example.com... + TEXT: + Connecting to my-box.example.com... XML: my-box example.com JSON: "host": "my-box", "domain": "example.com" - -For brevity, the HTML output is emitted. + HTML: +
+
Connecting to
+
my-box
+
.
+
example.com
+
...
+
** Encoding Styles -There are four encoding styles supported by libxo: TEXT, HTML, JSON, -and XML. JSON and XML are suitable for encoding data, while TEXT and -HTML are suited for display to the user. TEXT output can be display -on a terminal session, allowing compatibility with traditional usage. -HTML can be matched with a small CSS file to permit rendering in any -HTML5 browser. XML output is suitable for tools like XPath and -protocols like NETCONF. JSON output can be used for RESTful APIs. +There are four encoding styles supported by libxo: + +- TEXT output can be display on a terminal session, allowing +compatibility with traditional command line usage. +- XML output is suitable for tools like XPath and protocols like +NETCONF. +- JSON output can be used for RESTful APIs and integration with +languages like Javascript and Python. +- HTML can be matched with a small CSS file to permit rendering in any +HTML5 browser. + +In general, XML and JSON are suitable for encoding data, while TEXT is +suited for terminal output and HTML is suited for display in a web +browser (see ^xohtml^). *** Text Output @@ -164,7 +320,7 @@ data might look like: printf("%d\t%s\n", num_blocks, path); Simple, direct, obvious. But it's only making text output. Imagine -using a single code path to make text, XML, JSON or HTML, deciding at +using a single code path to make TEXT, XML, JSON or HTML, deciding at run time which to generate. libxo expands on the idea of printf format strings to make a single @@ -257,7 +413,7 @@ field descriptions within the format string. The field description is given as follows: - '{' [ role | modifier ]* ':' [ content ] + '{' [ role | modifier ]* [',' long-names ]* ':' [ content ] [ '/' field-format [ '/' encoding-format ]] '}' The role describes the function of the field, while the modifiers @@ -271,23 +427,31 @@ label ("In stock"), and the third is a value field ("in-stock"). The in-stock field has a "%u" format that will parse the next argument passed to the xo_emit function as an unsigned integer. - xo_emit("{P: }{Lwc:In stock}{:in-stock/%u}\n", 65); + xo_emit("{P: }{Lwc:In stock}{:in-stock/%u}\n", 65); This single line of code can generate text (" In stock: 65\n"), XML ("65"), JSON ('"in-stock": 6'), or HTML (too lengthy to be listed here). -*** Modifier Roles +While roles and modifiers typically use single character for brevity, +there are alternative names for each which allow more verbose +formatting strings. These names must be preceded by a comma, and may +follow any single-character values: -Modifiers are optional, and indicate the role and formatting of the + xo_emit("{L,white,colon:In stock}{,key:in-stock/%u}\n", 65); + +*** Field Roles + +Field roles are optional, and indicate the role and formatting of the content. The roles are listed below; only one role is permitted: |---+--------------+-------------------------------------------------| -| M | Name | Description | +| R | Name | Description | |---+--------------+-------------------------------------------------| -| C | color/effect | Field has color and effect controls | +| C | color | Field has color and effect controls | | D | decoration | Field is non-text (e.g., colon, comma) | | E | error | Field is an error message | +| G | gettext | Call gettext(3) on the format string | | L | label | Field is text that prefixes a value | | N | note | Field is text that follows a value | | P | padding | Field is spaces needed for vertical alignment | @@ -295,25 +459,59 @@ content. The roles are listed below; only one role is permitted: | U | units | Field is the units for the previous value field | | V | value | Field is the name of field (the default) | | W | warning | Field is a warning message | -| [ | start anchor | Begin a section of anchored variable-width text | -| ] | stop anchor | End a section of anchored variable-width text | +| [ | start-anchor | Begin a section of anchored variable-width text | +| ] | stop-anchor | End a section of anchored variable-width text | |---+--------------+-------------------------------------------------| -**** The Color Role ({C:}) + EXAMPLE: + xo_emit("{L:Free}{D::}{P: }{:free/%u} {U:Blocks}\n", + free_blocks); + +When a role is not provided, the "value" role is used as the default. + +Roles and modifiers can also use more verbose names, when preceeded by +a comma: + + EXAMPLE: + xo_emit("{,label:Free}{,decoration::}{,padding: }" + "{,value:free/%u} {,units:Blocks}\n", + free_blocks); + +**** The Color Role ({C:}) @color-role@ Colors and effects control how text values are displayed; they are -used for display styles (TEXT and HTML). The color content can be -either static, when placed directly within the field descriptor, or a -printf-style format descriptor can be used, if preceded by a slash ("/"): +used for display styles (TEXT and HTML). + + xo_emit("{C:bold}{:value}{C:no-bold}\n", value); + +Colors and effects remain in effect until modified by other "C"-role +fields. + + xo_emit("{C:bold}{C:inverse}both{C:no-bold}only inverse\n"); + +If the content is empty, the "reset" action is performed. + + xo_emit("{C:both,underline}{:value}{C:}\n", value); + +The content should be a comma-separated list of zero or more colors or +display effects. + + xo_emit("{C:bold,inverse}Ugly{C:no-bold,no-inverse}\n"); + +The color content can be either static, when placed directly within +the field descriptor, or a printf-style format descriptor can be used, +if preceded by a slash ("/"): + + xo_emit("{C:/%s%s}{:value}{C:}", need_bold ? "bold" : "", + need_underline ? "underline" : "", value); + +Color names are prefixed with either "fg-" or "bg-" to change the +foreground and background colors, respectively. - xo_emit("{C:bold}{Lwc:Cost}{:cost/%u}{C:reset}\n", cost); xo_emit("{C:/fg-%s,bg-%s}{Lwc:Cost}{:cost/%u}{C:reset}\n", fg_color, bg_color, cost); -The content should be a comma-separated list of zero or more colors or -display effects. Colors and effects remain in effect until -modified by other "C" roles. If the content is empty, the "reset" -action is performed. +The following table lists the supported effects: |---------------+-------------------------------------------------| | Name | Description | @@ -332,22 +530,19 @@ action is performed. The following color names are supported: -|---------------| -| Name | -|---------------| -| black | -| blue | -| cyan | -| default | -| green | -| magenta | -| red | -| white | -| yellow | -|---------------| - -Color names are prefixed with either "fg-" or "bg-" to change the -foreground and background colors, respectively. +|---------+--------------------------------------------| +| Name | Description | +|---------+--------------------------------------------| +| black | | +| blue | | +| cyan | | +| default | Default color for foreground or background | +| green | | +| magenta | | +| red | | +| white | | +| yellow | | +|---------+--------------------------------------------| **** The Decoration Role ({D:}) @@ -358,6 +553,37 @@ can use CSS to direct their display parameters. xo_emit("{D:((}{:name}{D:))}\n", name); +**** The Gettext Role ({G:}) @gettext-role@ + +libxo supports internationalization (i18n) through its use of +gettext(3). Use the "{G:}" role to request that the remaining part of +the format string, following the "{G:}" field, be handled using +gettext(). + +Since gettext() uses the string as the key into the message catalog, +libxo uses a simplified version of the format string that removes +unimportant field formatting and modifiers, stopping minor formatting +changes from impacting the expensive translation process. A developer +change such as changing "/%06d" to "/%08d" should not force hand +inspection of all .po files. + +The simplified version can be generated for a single message using the +"xopo -s " command, or an entire .pot can be translated using +the "xopo -f -o " command. + + xo_emit("{G:}Invalid token\n"); + +The {G:} role allows a domain name to be set. gettext calls will +continue to use that domain name until the current format string +processing is complete, enabling a library function to emit strings +using it's own catalog. The domain name can be either static as the +content of the field, or a format can be used to get the domain name +from the arguments. + + xo_emit("{G:libc}Service unavailable in restricted mode\n"); + +See ^howto-i18n^ for additional details. + **** The Label Role ({L:}) Labels are text that appears before a value. @@ -370,7 +596,7 @@ Notes are text that appears after a value. xo_emit("{:cost/%u} {N:per year}\n", cost); -**** The Padding Role ({P:}) +**** The Padding Role ({P:}) @padding-role@ Padding represents whitespace used before and between fields. @@ -440,7 +666,7 @@ format descriptors default to "%s". xo_emit("{:author} wrote \"{:poem}\" in {:year/%4d}\n, author, poem, year); -**** The Anchor Modifiers ({[:} and {]:}) +**** The Anchor Roles ({[:} and {]:}) @anchor-role@ The anchor roles allow a set of strings by be padded as a group, but still be visible to xo_emit as distinct fields. Either the start @@ -468,29 +694,38 @@ than the absolute value of the given width, nothing happens. Widths over 8k are considered probable errors and not supported. If XOF_WARN is set, a warning will be generated. -*** Modifier Flags +*** Field Modifiers -The modifiers can also include the following flags, which modify the -content emitted for some output styles: +Field modifiers are flags which modify the way content emitted for +particular output styles: -|---+--------------+-------------------------------------------------| -| M | Name | Description | -|---+--------------+-------------------------------------------------| -| c | colon | A colon (":") is appended after the label | -| d | display | Only emit field for display styles (text/HTML) | -| e | encoding | Only emit for encoding styles (XML/JSON) | -| k | key | Field is a key, suitable for XPath predicates | -| l | leaf-list | Field is a leaf-list -| n | no-quotes | Do not quote the field when using JSON style | -| q | quotes | Quote the field when using JSON style | -| w | white space | A blank (" ") is appended after the label | -|---+--------------+-------------------------------------------------| +|---+---------------+-------------------------------------------------| +| M | Name | Description | +|---+---------------+-------------------------------------------------| +| c | colon | A colon (":") is appended after the label | +| d | display | Only emit field for display styles (text/HTML) | +| e | encoding | Only emit for encoding styles (XML/JSON) | +| g | gettext | Call gettext on field's render content | +| h | humanize (hn) | Format large numbers in human-readable style | +| | hn-space | Humanize: Place space between numeric and unit | +| | hn-decimal | Humanize: Add a decimal digit, if number < 10 | +| | hn-1000 | Humanize: Use 1000 as divisor instead of 1024 | +| k | key | Field is a key, suitable for XPath predicates | +| l | leaf-list | Field is a leaf-list | +| n | no-quotes | Do not quote the field when using JSON style | +| p | plural | Gettext: Use comma-separated plural form | +| q | quotes | Quote the field when using JSON style | +| t | trim | Trim leading and trailing whitespace | +| w | white | A blank (" ") is appended after the label | +|---+---------------+-------------------------------------------------| -For example, the modifier string "Lwc" means the field has a label -role (text that describes the next field) and should be followed by a -colon ('c') and a space ('w'). The modifier string "Vkq" means the -field has a value role, that it is a key for the current instance, and -that the value should be quoted when encoded for JSON. +Roles and modifiers can also use more verbose names, when preceeded by +a comma. For example, the modifier string "Lwc" (or "L,white,colon") +means the field has a label role (text that describes the next field) +and should be followed by a colon ('c') and a space ('w'). The +modifier string "Vkq" (or ":key,quote") means the field has a value +role (the default role), that it is a key for the current instance, +and that the value should be quoted when encoded for JSON. **** The Colon Modifier ({c:}) @@ -535,6 +770,58 @@ the display output styles, TEXT and HTML. The encoding modifier is the opposite of the display modifier, and they are often used to give to distinct views of the underlying data. +**** The Gettext Modifier ({g:}) @gettext-modifier@ + +The gettext modifier is used to translate individual fields using the +gettext domain (typically set using the "{G:}" role) and current +language settings. Once libxo renders the field value, it is passed +to gettext(3), where it is used as a key to find the native language +translation. + +In the following example, the strings "State" and "full" are passed +to gettext() to find locale-based translated strings. + + xo_emit("{Lgwc:State}{g:state}\n", "full"); + +See ^gettext-role^, ^plural-modifier^, and ^howto-i18n^ for additional +details. + +**** The Humanize Modifier ({h:}) + +The humanize modifier is used to render large numbers as in a +human-readable format. While numbers like "44470272" are completely +readable to computers and savants, humans will generally find "44M" +more meaningful. + +"hn" can be used as an alias for "humanize". + +The humanize modifier only affects display styles (TEXT and HMTL). +The "no-humanize" option (See ^LIBXO_OPTIONS^) will block the function of +the humanize modifier. + +There are a number of modifiers that affect details of humanization. +These are only available in as full names, not single characters. The +"hn-space" modifier places a space between the number and any +multiplier symbol, such as "M" or "K" (ex: "44 K"). The "hn-decimal" +modifier will add a decimal point and a single tenths digit when the number is +less than 10 (ex: "4.4K"). The "hn-1000" modifier will use 1000 as divisor +instead of 1024, following the JEDEC-standard instead of the more +natural binary powers-of-two tradition. + + EXAMPLE: + xo_emit("{h:input/%u}, {h,hn-space:output/%u}, " + "{h,hn-decimal:errors/%u}, {h,hn-1000:capacity/%u}, " + "{h,hn-decimal:remaining/%u}\n", + input, output, errors, capacity, remaining); + TEXT: + 21, 57 K, 96M, 44M, 1.2G + +In the HTML style, the original numeric value is rendered in the +"data-number" attribute on the
element: + +
96M
+ **** The Key Modifier ({k:}) The key modifier is used to indicate that a particular field helps @@ -586,6 +873,27 @@ needed, but often this needs to be controlled by the caller. JSON: "fancy": true +**** The Plural Modifier ({p:}) @plural-modifier@ + +The plural modifier selects the appropriate plural form of an +expression based on the most recent number emitted and the current +language settings. The contents of the field should be the singular +and plural English values, separated by a comma: + + xo_emit("{:bytes} {Ngp:byte,bytes}\n", bytes); + +The plural modifier is meant to work with the gettext modifier ({g:}) +but can work independently. See ^gettext-modifier^. + +When used without the gettext modifier or when the message does not +appear in the message catalog, the first token is chosen when the last +numeric value is equal to 1; otherwise the second value is used, +mimicking the simple pluralization rules of English. + +When used with the gettext modifier, the ngettext(3) function is +called to handle the heavy lifting, using the message catalog to +convert the singular and plural forms into the native language. + **** The Quotes Modifier ({q:}) The quotes modifier (and its twin, the 'no-quotes' modifier) affect @@ -757,8 +1065,8 @@ number of columns to emit. xo_emit uses the precision as the former, and adds a third value for specifying the maximum number of columns. In this example, the name field is printed with a minimum of 3 columns -and a maximum of 6. Up to ten bytes are in used in filling those -columns. +and a maximum of 6. Up to ten bytes of data at the location given by +'name' are in used in filling those columns. xo_emit("{:name/%3.10.6s}", name); @@ -782,7 +1090,17 @@ placed in a
with class "text".
extra small
.
-*** "%n" is Not Supported +*** "%m" Is Supported + +libxo supports the '%m' directive, which formats the error message +associated with the current value of "errno". It is the equivalent +of "%s" with the argument strerror(errno). + + xo_emit("{:filename} cannot be opened: {:error/%m}", filename); + xo_emit("{:filename} cannot be opened: {:error/%s}", + filename, strerror(errno)); + +*** "%n" Is Not Supported libxo does not support the '%n' directive. It's a bad idea and we just don't do it. @@ -799,6 +1117,52 @@ default to "%s". For padding and labels, the content string is considered the content, unless a format is given. +*** Argument Validation @printf-like@ + +Many compilers and tool chains support validation of printf-like +arguments. When the format string fails to match the argument list, +a warning is generated. This is a valuable feature and while the +formatting strings for libxo differ considerably from printf, many of +these checks can still provide build-time protection against bugs. + +libxo provide variants of functions that provide this ability, if the +"--enable-printflike" option is passed to the "configure" script. +These functions use the "_p" suffix, like "xo_emit_p()", +xo_emit_hp()", etc. + +The following are features of libxo formatting strings that are +incompatible with printf-like testing: + +- implicit formats, where "{:tag}" has an implicit "%s"; +- the "max" parameter for strings, where "{:tag/%4.10.6s}" means up to +ten bytes of data can be inspected to fill a minimum of 4 columns and +a maximum of 6; +- percent signs in strings, where "{:filled}%" makes a single, +trailing percent sign; +- the "l" and "h" modifiers for strings, where "{:tag/%hs}" means +locale-based string and "{:tag/%ls}" means a wide character string; +- distinct encoding formats, where "{:tag/#%s/%s}" means the display +styles (text and HTML) will use "#%s" where other styles use "%s"; + +If none of these features are in use by your code, then using the "_p" +variants might be wise. + +|------------------+------------------------| +| Function | printf-like Equivalent | +|------------------+------------------------| +| xo_emit_hv | xo_emit_hvp | +| xo_emit_h | xo_emit_hp | +| xo_emit | xo_emit_p | +| xo_emit_warn_hcv | xo_emit_warn_hcvp | +| xo_emit_warn_hc | xo_emit_warn_hcp | +| xo_emit_warn_c | xo_emit_warn_cp | +| xo_emit_warn | xo_emit_warn_p | +| xo_emit_warnx_ | xo_emit_warnx_p | +| xo_emit_err | xo_emit_err_p | +| xo_emit_errx | xo_emit_errx_p | +| xo_emit_errc | xo_emit_errc_p | +|------------------+------------------------| + *** Example In this example, the value for the number of items in stock is emitted: @@ -844,26 +1208,31 @@ following options are recognised: Options is a comma-separated list of tokens that correspond to output styles, flags, or features: -|-----------+-------------------------------------------------------| -| Token | Action | -|-----------+-------------------------------------------------------| -| dtrt | Enable "Do The Right Thing" mode | -| html | Emit HTML output | -| indent=xx | Set the indentation level | -| info | Add info attributes (HTML) | -| json | Emit JSON output | -| keys | Emit the key attribute for keys (XML) | -| no-locale | Do not initialize the locale setting | -| no-top | Do not emit a top set of braces (JSON) | -| not-first | Pretend the 1st output item was not 1st (JSON) | -| pretty | Emit pretty-printed output | -| text | Emit TEXT output | -| units | Add the 'units' (XML) or 'data-units (HTML) attribute | -| warn | Emit warnings when libxo detects bad calls | -| warn-xml | Emit warnings in XML | -| xml | Emit XML output | -| xpath | Add XPath expressions (HTML) | -|-----------+-------------------------------------------------------| +|-------------+-------------------------------------------------------| +| Token | Action | +|-------------+-------------------------------------------------------| +| color | Enable colors/effects for display styles (TEXT, HTML) | +| dtrt | Enable "Do The Right Thing" mode | +| html | Emit HTML output | +| indent=xx | Set the indentation level | +| info | Add info attributes (HTML) | +| json | Emit JSON output | +| keys | Emit the key attribute for keys (XML) | +| log-gettext | Log (via stderr) each gettext(3) string lookup | +| log-syslog | Log (via stderr) each syslog message (via xo_syslog) | +| no-humanize | Ignore the {h:} modifier (TEXT, HTML) | +| no-locale | Do not initialize the locale setting | +| no-top | Do not emit a top set of braces (JSON) | +| not-first | Pretend the 1st output item was not 1st (JSON) | +| pretty | Emit pretty-printed output | +| text | Emit TEXT output | +| underscores | Replace XML-friendly "-"s with JSON friendly "_"s e | +| units | Add the 'units' (XML) or 'data-units (HTML) attribute | +| warn | Emit warnings when libxo detects bad calls | +| warn-xml | Emit warnings in XML | +| xml | Emit XML output | +| xpath | Add XPath expressions (HTML) | +|-------------+-------------------------------------------------------| The brief options are detailed in ^LIBXO_OPTIONS^. @@ -1013,7 +1382,7 @@ properly. xo_close_marker("fish-guts"); } -** Handles +** Handles @handles@ libxo uses "handles" to control its rendering functionality. The handle contains state and buffered data, as well as callback functions @@ -1164,27 +1533,34 @@ To use the default handle, pass a NULL handle: The set of valid flags include: -|-----------------+---------------------------------------| -| Flag | Description | -|-----------------+---------------------------------------| -| XOF_CLOSE_FP | Close file pointer on xo_destroy() | -| XOF_DTRT | Enable "do the right thing" mode | -| XOF_INFO | Display info data attributes (HTML) | -| XOF_KEYS | Emit the key attribute (XML) | -| XOF_NO_ENV | Do not use the LIBXO_OPTIONS env var | -| XOF_PRETTY | Make 'pretty printed' output | -| XOF_UNDERSCORES | Replaces hyphens with underscores | -| XOF_UNITS | Display units (XML and HMTL) | -| XOF_WARN | Generate warnings for broken calls | -| XOF_WARN_XML | Generate warnings in XML on stdout | -| XOF_XPATH | Emit XPath expressions (HTML) | -| XOF_COLUMNS | Force xo_emit to return columns used | -| XOF_FLUSH | Flush output after each xo_emit call | -|-----------------+---------------------------------------| +|-------------------+----------------------------------------| +| Flag | Description | +|-------------------+----------------------------------------| +| XOF_CLOSE_FP | Close file pointer on xo_destroy() | +| XOF_COLOR | Enable color and effects in output | +| XOF_COLOR_ALLOWED | Allow color/effect for terminal output | +| XOF_DTRT | Enable "do the right thing" mode | +| XOF_INFO | Display info data attributes (HTML) | +| XOF_KEYS | Emit the key attribute (XML) | +| XOF_NO_ENV | Do not use the LIBXO_OPTIONS env var | +| XOF_NO_HUMANIZE | Display humanization (TEXT, HTML) | +| XOF_PRETTY | Make 'pretty printed' output | +| XOF_UNDERSCORES | Replaces hyphens with underscores | +| XOF_UNITS | Display units (XML, HMTL) | +| XOF_WARN | Generate warnings for broken calls | +| XOF_WARN_XML | Generate warnings in XML on stdout | +| XOF_XPATH | Emit XPath expressions (HTML) | +| XOF_COLUMNS | Force xo_emit to return columns used | +| XOF_FLUSH | Flush output after each xo_emit call | +|-------------------+----------------------------------------| The XOF_CLOSE_FP flag will trigger the call of the close_func (provided via xo_set_writer()) when the handle is destroyed. +The XOF_COLOR flag enables color and effects in output regardless of +output device, while the XOF_COLOR_ALLOWED flag allows color and +effects only if the output device is a terminal. + The XOF_PRETTY flag requests 'pretty printing', which will trigger the addition of indentation and newlines to enhance the readability of XML, JSON, and HTML output. Text output is not affected. @@ -1328,12 +1704,15 @@ When the program is ready to exit or close a handle, a call to xo_finish() is required. This flushes any buffered data, closes open libxo constructs, and completes any pending operations. - void xo_finish (void); - void xo_finish_h (xo_handle_t *xop); + int xo_finish (void); + int xo_finish_h (xo_handle_t *xop); + void xo_finish_atexit (void); Calling this function is vital to the proper operation of libxo, especially for the non-TEXT output styles. +xo_finish_atexit is suitable for use with atexit(3). + ** Emitting Hierarchy libxo represents to types of hierarchy: containers and lists. A @@ -1430,9 +1809,9 @@ style and usage expectations. } ] -** Additional Functionality +** Support Functions -*** Parsing Command-line Arguments (xo_parse_args) +*** Parsing Command-line Arguments (xo_parse_args) @xo_parse_args@ The xo_parse_args() function is used to process a program's arguments. libxo-specific options are processed and removed @@ -1442,7 +1821,7 @@ is returned. On failure, a message it emitted and -1 is returned. argc = xo_parse_args(argc, argv); if (argc < 0) - exit(1); + exit(EXIT_FAILURE); Following the call to xo_parse_args, the application can process the remaining arguments in a normal manner. See ^command-line-arguments^ @@ -1539,19 +1918,25 @@ By default, the standard realloc() and free() functions are used. The environment variable "LIBXO_OPTIONS" can be set to a string of options: -|--------+-------------------------------------------| -| Option | Action | -|--------+-------------------------------------------| -| H | Enable HTML output (XO_STYLE_HTML) | -| I | Enable info output (XOF_INFO) | -| i | Indent by | -| J | Enable JSON output (XO_STYLE_JSON) | -| P | Enable pretty-printed output (XOF_PRETTY) | -| T | Enable text output (XO_STYLE_TEXT) | -| W | Enable warnings (XOF_WARN) | -| X | Enable XML output (XO_STYLE_XML) | -| x | Enable XPath data (XOF_XPATH) | -|--------+-------------------------------------------| +|--------+---------------------------------------------| +| Option | Action | +|--------+---------------------------------------------| +| c | Enable color/effects for TEXT/HTML | +| F | Force line-buffered flushing | +| H | Enable HTML output (XO_STYLE_HTML) | +| I | Enable info output (XOF_INFO) | +| i | Indent by | +| J | Enable JSON output (XO_STYLE_JSON) | +| k | Add keys to XPATH expressions in HTML | +| n | Disable humanization (TEXT, HTML) | +| P | Enable pretty-printed output (XOF_PRETTY) | +| T | Enable text output (XO_STYLE_TEXT) | +| U | Add units to HTML output | +| u | Change "-"s to "_"s in element names (JSON) | +| W | Enable warnings (XOF_WARN) | +| X | Enable XML output (XO_STYLE_XML) | +| x | Enable XPath data (XOF_XPATH) | +|--------+---------------------------------------------| For example, warnings can be enabled by: @@ -1619,6 +2004,327 @@ can do so by calling the xo_no_setlocale() function. void xo_no_setlocale (void); +** Emitting syslog Messages + +syslog is the system logging facility used throughout the unix world. +Messages are sent from commands, applications, and daemons to a +hierarchy of servers, where they are filtered, saved, and forwarded +based on configuration behaviors. + +syslog is an older protocol, originally documented only in source +code. By the time RFC 3164 published, variation and mutation left the +leading "" string as only common content. RFC 5424 defines a new +version (version 1) of syslog and introduces structured data into the +messages. Structured data is a set of name/value pairs transmitted +distinctly alongside the traditional text message, allowing filtering +on precise values instead of regular expressions. + +These name/value pairs are scoped by a two-part identifier; an +enterprise identifier names the party responsible for the message +catalog and a name identifying that message. Enterprise IDs are +defined by IANA, the Internet Assigned Numbers Authority: + +https://www.iana.org/assignments/enterprise-numbers/enterprise-numbers + +Use the ^xo_set_syslog_enterprise_id^() function to set the Enterprise +ID, as needed. + +The message name should follow the conventions in ^good-field-names^, +as should the fields within the message. + + /* Both of these calls are optional */ + xo_set_syslog_enterprise_id(32473); + xo_open_log("my-program", 0, LOG_DAEMON); + + /* Generate a syslog message */ + xo_syslog(LOG_ERR, "upload-failed", + "error <%d> uploading file '{:filename}' " + "as '{:target/%s:%s}'", + code, filename, protocol, remote); + + xo_syslog(LOG_INFO, "poofd-invalid-state", + "state {:current/%u} is invalid {:connection/%u}", + state, conn); + +The developer should be aware that the message name may be used in the +future to allow access to further information, including +documentation. Care should be taken to choose quality, descriptive +names. + +*** Priority, Facility, and Flags @priority@ + +The xo_syslog, xo_vsyslog, and xo_open_log functions accept a set of +flags which provide the priority of the message, the source facility, +and some additional features. These values are OR'd together to +create a single integer argument: + + xo_syslog(LOG_ERR | LOG_AUTH, "login-failed", + "Login failed; user '{:user}' from host '{:address}'", + user, addr); + +These values are defined in . + +The priority value indicates the importance and potential impact of +each message. + +|-------------+-------------------------------------------------------| +| Priority | Description | +|-------------+-------------------------------------------------------| +| LOG_EMERG | A panic condition, normally broadcast to all users | +| LOG_ALERT | A condition that should be corrected immediately | +| LOG_CRIT | Critical conditions | +| LOG_ERR | Generic errors | +| LOG_WARNING | Warning messages | +| LOG_NOTICE | Non-error conditions that might need special handling | +| LOG_INFO | Informational messages | +| LOG_DEBUG | Developer-oriented messages | +|-------------+-------------------------------------------------------| + +The facility value indicates the source of message, in fairly generic +terms. + +|---------------+-------------------------------------------------| +| Facility | Description | +|---------------+-------------------------------------------------| +| LOG_AUTH | The authorization system (e.g. login(1)) | +| LOG_AUTHPRIV | As LOG_AUTH, but logged to a privileged file | +| LOG_CRON | The cron daemon: cron(8) | +| LOG_DAEMON | System daemons, not otherwise explicitly listed | +| LOG_FTP | The file transfer protocol daemons | +| LOG_KERN | Messages generated by the kernel | +| LOG_LPR | The line printer spooling system | +| LOG_MAIL | The mail system | +| LOG_NEWS | The network news system | +| LOG_SECURITY | Security subsystems, such as ipfw(4) | +| LOG_SYSLOG | Messages generated internally by syslogd(8) | +| LOG_USER | Messages generated by user processes (default) | +| LOG_UUCP | The uucp system | +| LOG_LOCAL0..7 | Reserved for local use | +|---------------+-------------------------------------------------| + +In addition to the values listed above, xo_open_log accepts a set of +addition flags requesting specific behaviors. + +|------------+----------------------------------------------------| +| Flag | Description | +|------------+----------------------------------------------------| +| LOG_CONS | If syslogd fails, attempt to write to /dev/console | +| LOG_NDELAY | Open the connection to syslogd(8) immediately | +| LOG_PERROR | Write the message also to standard error output | +| LOG_PID | Log the process id with each message | +|------------+----------------------------------------------------| + +*** xo_syslog + +Use the xo_syslog function to generate syslog messages by calling it +with a log priority and facility, a message name, a format string, and +a set of arguments. The priority/facility argument are discussed +above, as is the message name. + +The format string follows the same conventions as xo_emit's format +string, with each field being rendered as an SD-PARAM pair. + + xo_syslog(LOG_ERR, "poofd-missing-file", + "'{:filename}' not found: {:error/%m}", filename); + + ... [poofd-missing-file@32473 filename="/etc/poofd.conf" + error="Permission denied"] '/etc/poofd.conf' not + found: Permission denied + +*** Support functions + +**** xo_vsyslog + +xo_vsyslog is identical in function to xo_syslog, but takes the set of +arguments using a va_list. + + void my_log (const char *name, const char *fmt, ...) + { + va_list vap; + va_start(vap, fmt); + xo_vsyslog(LOG_ERR, name, fmt, vap); + va_end(vap); + } + +**** xo_open_log + +xo_open_log functions similar to openlog(3), allowing customization of +the program name, the log facility number, and the additional option +flags described in ^priority^. + + void + xo_open_log (const char *ident, int logopt, int facility); + +**** xo_close_log + +xo_close_log functions similar to closelog(3), closing the log file +and releasing any associated resources. + + void + xo_close_log (void); + +**** xo_set_logmask + +xo_set_logmask function similar to setlogmask(3), restricting the set +of generated log event to those whose associated bit is set in +maskpri. Use LOG_MASK(pri) to find the appropriate bit, or +LOG_UPTO(toppri) to create a mask for all priorities up to and +including toppri. + + int + xo_set_logmask (int maskpri); + + Example: + setlogmask(LOG_UPTO(LOG_WARN)); + +**** xo_set_syslog_enterprise_id + +Use the xo_set_syslog_enterprise_id to supply a platform- or +application-specific enterprise id. This value is used in any +future syslog messages. + +Ideally, the operating system should supply a default value via the +"kern.syslog.enterprise_id" sysctl value. Lacking that, the +application should provide a suitable value. + + void + xo_set_syslog_enterprise_id (unsigned short eid); + +Enterprise IDs are administered by IANA, the Internet Assigned Number +Authority. The complete list is EIDs on their web site: + + https://www.iana.org/assignments/enterprise-numbers/enterprise-numbers + +New EIDs can be requested from IANA using the following page: + + http://pen.iana.org/pen/PenApplication.page + +Each software development organization that defines a set of syslog +messages should register their own EID and use that value in their +software to ensure that messages can be uniquely identified by the +combination of EID + message name. + +** Creating Custom Encoders + +The number of encoding schemes in current use is staggering, with new +and distinct schemes appearing daily. While libxo provide XML, JSON, +HMTL, and text natively, there are requirements for other encodings. + +Rather than bake support for all possible encoders into libxo, the API +allows them to be defined externally. libxo can then interfaces with +these encoding modules using a simplistic API. libxo processes all +functions calls, handles state transitions, performs all formatting, +and then passes the results as operations to a customized encoding +function, which implements specific encoding logic as required. This +means your encoder doesn't need to detect errors with unbalanced +open/close operations but can rely on libxo to pass correct data. + +By making a simple API, libxo internals are not exposed, insulating the +encoder and the library from future or internal changes. + +The three elements of the API are: +- loading +- initialization +- operations + +The following sections provide details about these topics. + +libxo source contain an encoder for Concise Binary Object +Representation, aka CBOR (RFC 7049) which can be used as used as an +example for the API. + +*** Loading Encoders + +Encoders can be registered statically or discovered dynamically. +Applications can choose to call the xo_encoder_register() +function to explicitly register encoders, but more typically they are +built as shared libraries, placed in the libxo/extensions directory, +and loaded based on name. libxo looks for a file with the name of the encoder +and an extension of ".enc". This can be a file or a symlink to the +shared library file that supports the encoder. + + % ls -1 lib/libxo/extensions/*.enc + lib/libxo/extensions/cbor.enc + lib/libxo/extensions/test.enc + +*** Encoder Initialization + +Each encoder must export a symbol used to access the library, which +must have the following signature: + + int xo_encoder_library_init (XO_ENCODER_INIT_ARGS); + +XO_ENCODER_INIT_ARGS is a macro defined in xo_encoder.h that defines +an argument called "arg", a pointer of the type +xo_encoder_init_args_t. This structure contains two fields: + +- xei_version is the version number of the API as implemented within +libxo. This version is currently as 1 using XO_ENCODER_VERSION. This +number can be checked to ensure compatibility. The working assumption +is that all versions should be backward compatible, but each side may +need to accurately know the version supported by the other side. +xo_encoder_library_init can optionally check this value, and must then +set it to the version number used by the encoder, allowing libxo to +detect version differences and react accordingly. For example, if +version 2 adds new operations, then libxo will know that an encoding +library that set xei_version to 1 cannot be expected to handle those +new operations. + +- xei_handler must be set to a pointer to a function of type +xo_encoder_func_t, as defined in xo_encoder.h. This function +takes a set of parameters: +-- xop is a pointer to the opaque xo_handle_t structure +-- op is an integer representing the current operation +-- name is a string whose meaning differs by operation +-- value is a string whose meaning differs by operation +-- private is an opaque structure provided by the encoder + +Additional arguments may be added in the future, so handler functions +should use the XO_ENCODER_HANDLER_ARGS macro. An appropriate +"extern" declaration is provided to help catch errors. + +Once the encoder initialization function has completed processing, it +should return zero to indicate that no error has occurred. A non-zero +return code will cause the handle initialization to fail. + +*** Operations + +The encoder API defines a set of operations representing the +processing model of libxo. Content is formatted within libxo, and +callbacks are made to the encoder's handler function when data is +ready to be processed. + +|-----------------------+---------------------------------------| +| Operation | Meaning (Base function) | +|-----------------------+---------------------------------------| +| XO_OP_CREATE | Called when the handle is created | +| XO_OP_OPEN_CONTAINER | Container opened (xo_open_container) | +| XO_OP_CLOSE_CONTAINER | Container closed (xo_close_container) | +| XO_OP_OPEN_LIST | List opened (xo_open_list) | +| XO_OP_CLOSE_LIST | List closed (xo_close_list) | +| XO_OP_OPEN_LEAF_LIST | Leaf list opened (xo_open_leaf_list) | +| XO_OP_CLOSE_LEAF_LIST | Leaf list closed (xo_close_leaf_list) | +| XO_OP_OPEN_INSTANCE | Instance opened (xo_open_instance) | +| XO_OP_CLOSE_INSTANCE | Instance closed (xo_close_instance) | +| XO_OP_STRING | Field with Quoted UTF-8 string | +| XO_OP_CONTENT | Field with content | +| XO_OP_FINISH | Finish any pending output | +| XO_OP_FLUSH | Flush any buffered output | +| XO_OP_DESTROY | Clean up resources | +| XO_OP_ATTRIBUTE | An attribute name/value pair | +| XO_OP_VERSION | A version string | +|-----------------------+---------------------------------------| + +For all the open and close operations, the name parameter holds the +name of the construct. For string, content, and attribute operations, +the name parameter is the name of the field and the value parameter is +the value. "string" are differentiated from "content" to allow differing +treatment of true, false, null, and numbers from real strings, though +content values are formatted as strings before the handler is called. +For version operations, the value parameter contains the version. + +All strings are encoded in UTF-8. + * The "xo" Utility The "xo" utility allows command line access to the functionality of @@ -1768,7 +2474,7 @@ The "-V" option does not report errors, but prints a complete list of all field names, sorted alphabetically. The output can help spot inconsistencies and spelling errors. -* xohtml +* xohtml @xohtml@ xohtml is a tool for turning the output of libxo-enabled commands into html files suitable for display in modern HTML web browsers. It can @@ -1794,6 +2500,46 @@ The "-c" option takes a full command with arguments, including any libxo options needed to generate html ("--libxo=html"). This value must be quoted if it consists of multiple tokens. +* xopo + +The "xopo" utility filters ".pot" files generated by the "xgettext" +utility to remove formatting information suitable for use with +the "{G:}" modifier. This means that when the developer changes the +formatting portion of the field definitions, or the fields modifiers, +the string passed to gettext(3) is unchanged, avoiding the expense of +updating any existing translation files (".po" files). + +The syntax for the xopo command is one of two forms; it can be used as +a filter for processing a .po or .pot file, rewriting the "msgid" +strings with a simplified message string. In this mode, the input is +either standard input or a file given by the "-f" option, and the +output is either standard output or a file given by the "-o" option. + +In the second mode, a simple message given using the "-s" option on +the command, and the simplified version of that message is printed on +stdout. + +|-----------+---------------------------------| +| Option | Meaning | +|-----------+---------------------------------| +| -o | Output file name | +| -f | Use the given .po file as input | +| -s | Simplify a format string | +|-----------+---------------------------------| + + EXAMPLE: + % xopo -s "There are {:count/%u} {:event/%.6s} events\n" + There are {:count} {:event} events\n + + % xgettext --default-domain=foo --no-wrap \ + --add-comments --keyword=xo_emit --keyword=xo_emit_h \ + --keyword=xo_emit_warn -C -E -n --foreign-user \ + -o foo.pot.raw foo.c + % xopo -f foo.pot.raw -o foo.pot + +Use of the "--no-wrap" option for xgettext is required to ensure that +incoming msgid strings are not wrapped across multiple lines. + * FAQs This section contains the set of questions that users typically ask, @@ -1909,7 +2655,7 @@ be lost. libxo is a new implementation of these ideas and is distinct from the previous implementation in JUNOS. -*** What makes a good field name? +*** What makes a good field name? @good-field-names@ To make useful, consistent field names, follow these guidelines: @@ -1937,7 +2683,8 @@ Use "receive-after-window-packets" instead of Nothing's worse than writing expressions like: if ($src1/process[pid == $pid]/name == - $src2/proc-table/proc/p[process-id == $pid]/proc-name) { + $src2/proc-table/proc-list + /proc-entry[process-id == $pid]/proc-name) { ... } @@ -1945,6 +2692,9 @@ Find someone else who is expressing similar data and follow their fields and hierarchy. Remember the quote is not "Consistency is the hobgoblin of little minds", but "A foolish consistency is the hobgoblin of little minds". += Use containment as scoping +In the previous example, all the names are prefixed with "proc-", +which is redundant given that they are nested under the process table. = Think about your users Have empathy for your users, choosing clear and useful fields that contain clear and useful data. You may need to augment the display @@ -1979,6 +2729,331 @@ names to make that difference more obvious. !!include-file xolint.txt +* Howtos: Focused Directions + +This section provides task-oriented instructions for selected tasks. +If you have a task that needs instructions, please open a request as +an enhancement issue on github. + +** Howto: Report bugs + +libxo uses github to track bugs or request enhancements. Please use +the following URL: + + https://github.com/Juniper/libxo/issues + +** Howto: Install libxo + +libxo is open source, under a new BSD license. Source code is +available on github, as are recent releases. To get the most +current release, please visit: + + https://github.com/Juniper/libxo/releases + +After downloading and untarring the source code, building involves the +following steps: + + sh bin/setup.sh + cd build + ../configure + make + make test + sudo make install + +libxo uses a distinct "build" directory to keep generated files +separated from source files. + +Use "../configure --help" to display available configuration options, +which include the following: + + --enable-warnings Turn on compiler warnings + --enable-debug Turn on debugging + --enable-text-only Turn on text-only rendering + --enable-printflike Enable use of GCC __printflike attribute + --disable-libxo-options Turn off support for LIBXO_OPTIONS + --with-gettext=PFX Specify location of gettext installation + --with-libslax-prefix=PFX Specify location of libslax config + +Compiler warnings are a very good thing, but recent compiler version +have added some very pedantic checks. While every attempt is made to +keep libxo code warning-free, warnings are now optional. If you are +doing development work on libxo, it is required that you use +--enable-warnings to keep the code warning free, but most users need +not use this option. + +libxo provides the --enable-text-only option to reduce the footprint +of the library for smaller installations. XML, JSON, and HTML +rendering logic is removed. + +The gettext library does not provide a simple means of learning its +location, but libxo will look for it in /usr and /opt/local. If +installed elsewhere, the installer will need to provide this +information using the --with-gettext=/dir/path option. + +libslax is not required by libxo; it contains the "oxtradoc" program +used to format documentation. + +For additional information, see ^building-libxo^. + +** Howto: Convert command line applications + + How do I convert an existing command line application? + +There are three basic steps for converting command line application to +use libxo. + +- Setting up the context +- Converting printf calls +- Creating hierarchy +- Converting error functions + +*** Setting up the context + +To use libxo, you'll need to include the "xo.h" header file in your +source code files: + + #include + +In your main() function, you'll need to call xo_parse_args to handling +argument parsing (^xo_parse_args^). This function removes +libxo-specific arguments the program's argv and returns either the +number of remaining arguments or -1 to indicate an error. + + int main (int argc, char **argv) + { + argc = xo_parse_args(argc, argv); + if (argc < 0) + return argc; + .... + } + +At the bottom of your main(), you'll need to call xo_finish() to +complete output processing for the default handle (^handles^). libxo +provides the xo_finish_atexit function that is suitable for use with +the atexit(3) function. + + atexit(xo_finish_atexit); + +*** Converting printf Calls + +The second task is inspecting code for printf(3) calls and replacing +them with xo_emit() calls. The format strings are similar in task, +but libxo format strings wrap output fields in braces. The following +two calls produce identical text output: + + printf("There are %d %s events\n", count, etype); + xo_emit("There are {:count/%d} {:event} events\n", count, etype); + +"count" and "event" are used as names for JSON and XML output. The +"count" field uses the format "%d" and "event" uses the default "%s" +format. Both are "value" roles, which is the default role. + +Since text outside of output fields is passed verbatim, other roles +are less important, but their proper use can help make output more +useful. The "note" and "label" roles allow HTML output to recognize +the relationship between text and the associated values, allowing +appropriate "hover" and "onclick" behavior. Using the "units" role +allows the presentation layer to perform conversions when needed. The +"warning" and "error" roles allows use of color and font to draw +attention to warnings. The "padding" role makes the use of vital +whitespace more clear (^padding-role^). + +The "title" role indicates the headings of table and sections. This +allows HTML output to use CSS to make this relationship more obvious. + + printf("Statistics:\n"); + xo_emit("{T:Statistics}:\n"); + +The "color" roles controls foreground and background colors, as well +as effects like bold and underline (see ^color-role^). + + xo_emit("{C:bold}required{C:}\n"); + +Finally, the start- and stop-anchor roles allow justification and +padding over multiple fields (see ^anchor-role^). + + snprintf(buf, sizeof(buf), "(%u/%u/%u)", min, ave, max); + printf("%30s", buf); + + xo_emit("{[:30}({:minimum/%u}/{:average/%u}/{:maximum/%u}{]:}", + min, ave, max); + +*** Creating Hierarchy + +Text output doesn't have any sort of hierarchy, but XML and JSON +require this. Typically applications use indentation to represent +these relationship: + + printf("table %d\n", tnum); + for (i = 0; i < tmax; i++) { + printf(" %s %d\n", table[i].name, table[i].size); + } + + xo_emit("{T:/table %d}\n", tnum); + xo_open_list("table"); + for (i = 0; i < tmax; i++) { + xo_open_instance("table"); + xo_emit("{P: }{k:name} {:size/%d}\n", + table[i].name, table[i].size); + xo_close_instance("table"); + } + xo_close_list("table"); + +The open and close list functions are used before and after the list, +and the open and close instance functions are used before and after +each instance with in the list. + +Typically these developer looks for a "for" loop as an indication of +where to put these calls. + +In addition, the open and close container functions allow for +organization levels of hierarchy. + + printf("Paging information:\n"); + printf(" Free: %lu\n", free); + printf(" Active: %lu\n", active); + printf(" Inactive: %lu\n", inactive); + + xo_open_container("paging-information"); + xo_emit("{P: }{L:Free: }{:free/%lu}\n", free); + xo_emit("{P: }{L:Active: }{:active/%lu}\n", active); + xo_emit("{P: }{L:Inactive: }{:inactive/%lu}\n", inactive); + xo_close_container("paging-information"); + +*** Converting Error Functions + +libxo provides variants of the standard error and warning functions, +err(3) and warn(3). There are two variants, one for putting the +errors on standard error, and the other writes the errors and warnings +to the handle using the appropriate encoding style: + + err(1, "cannot open output file: %s", file); + + xo_err(1, "cannot open output file: %s", file); + xo_emit_err(1, "cannot open output file: {:filename}", file); + +** Howto: Use "xo" in Shell Scripts + +** Howto: Internationalization (i18n) @howto-i18n@ + + How do I use libxo to support internationalization? + +libxo allows format and field strings to be used a keys into message +catalogs to enable translation into a user's native language by +invoking the standard gettext(3) functions. + +gettext setup is a bit complicated: text strings are extracted from +source files into "portable object template" (.pot) files using the +"xgettext" command. For each language, this template file is used as +the source for a message catalog in the "portable object" (.po) +format, which are translated by hand and compiled into "machine +object" (.mo) files using the "msgfmt" command. The .mo files are +then typically installed in the /usr/share/locale or +/opt/local/share/locale directories. At run time, the user's language +settings are used to select a .mo file which is searched for matching +messages. Text strings in the source code are used as keys to look up +the native language strings in the .mo file. + +Since the xo_emit format string is used as the key into the message +catalog, libxo removes unimportant field formatting and modifiers from +the format string before use so that minor formatting changes will not +impact the expensive translation process. We don't want a developer +change such as changing "/%06d" to "/%08d" to force hand inspection of +all .po files. The simplified version can be generated for a single +message using the "xopo -s " command, or an entire .pot can be +translated using the "xopo -f -o " command. + + EXAMPLE: + % xopo -s "There are {:count/%u} {:event/%.6s} events\n" + There are {:count} {:event} events\n + + Recommended workflow: + # Extract text messages + xgettext --default-domain=foo --no-wrap \ + --add-comments --keyword=xo_emit --keyword=xo_emit_h \ + --keyword=xo_emit_warn -C -E -n --foreign-user \ + -o foo.pot.raw foo.c + + # Simplify format strings for libxo + xopo -f foo.pot.raw -o foo.pot + + # For a new language, just copy the file + cp foo.pot po/LC/my_lang/foo.po + + # For an existing language: + msgmerge --no-wrap po/LC/my_lang/foo.po \ + foo.pot -o po/LC/my_lang/foo.po.new + + # Now the hard part: translate foo.po using tools + # like poedit or emacs' po-mode + + # Compile the finished file; Use of msgfmt's "-v" option is + # strongly encouraged, so that "fuzzy" entries are reported. + msgfmt -v -o po/my_lang/LC_MESSAGES/foo.mo po/my_lang/foo.po + + # Install the .mo file + sudo cp po/my_lang/LC_MESSAGES/foo.mo \ + /opt/local/share/locale/my_lang/LC_MESSAGE/ + +Once these steps are complete, you can use the "gettext" command to +test the message catalog: + + gettext -d foo -e "some text" + +*** i18n and xo_emit + +There are three features used in libxo used to support i18n: + +- The "{G:}" role looks for a translation of the format string. +- The "{g:}" modifier looks for a translation of the field. +- The "{p:}" modifier looks for a pluralized version of the field. + +Together these three flags allows a single function call to give +native language support, as well as libxo's normal XML, JSON, and HTML +support. + + printf(gettext("Received %zu %s from {g:server} server\n"), + counter, ngettext("byte", "bytes", counter), + gettext("web")); + + xo_emit("{G:}Received {:received/%zu} {Ngp:byte,bytes} " + "from {g:server} server\n", counter, "web"); + +libxo will see the "{G:}" role and will first simplify the format +string, removing field formats and modifiers. + + "Received {:received} {N:byte,bytes} from {:server} server\n" + +libxo calls gettext(3) with that string to get a localized version. +If your language were Pig Latin, the result might look like: + + "Eceivedray {:received} {N:byte,bytes} omfray " + "{:server} erversay\n" + +Note the field names do not change and they should not be translated. +The contents of the note ("byte,bytes") should also not be translated, +since the "g" modifier will need the untranslated value as the key for +the message catalog. + +The field "{g:server}" requests the rendered value of the field be +translated using gettext(3). In this example, "web" would be used. + +The field "{Ngp:byte,bytes}" shows an example of plural form using the +"p" modifier with the "g" modifier. The base singular and plural +forms appear inside the field, separated by a comma. At run time, +libxo uses the previous field's numeric value to decide which form to +use by calling ngettext(3). + +If a domain name is needed, it can be supplied as the content of the +{G:} role. Domain names remain in use throughout the format string +until cleared with another domain name. + + printf(dgettext("dns", "Host %s not found: %d(%s)\n"), + name, errno, dgettext("strerror", strerror(errno))); + + xo_emit("{G:dns}Host {:hostname} not found: " + "%d({G:strerror}{g:%m})\n", name, errno); + * Examples ** Unit Test @@ -2022,7 +3097,7 @@ Here is the unit test example: argc = xo_parse_args(argc, argv); if (argc < 0) - exit(1); + exit(EXIT_FAILURE); xo_set_info(NULL, info, info_count); diff --git a/contrib/libxo/encoder/Makefile.am b/contrib/libxo/encoder/Makefile.am new file mode 100644 index 000000000000..10d19de73225 --- /dev/null +++ b/contrib/libxo/encoder/Makefile.am @@ -0,0 +1,9 @@ +# +# Copyright 2015, Juniper Networks, Inc. +# All rights reserved. +# This SOFTWARE is licensed under the LICENSE provided in the +# ../Copyright file. By downloading, installing, copying, or otherwise +# using the SOFTWARE, you agree to be bound by the terms of that +# LICENSE. + +SUBDIRS = cbor test diff --git a/contrib/libxo/encoder/cbor/Makefile.am b/contrib/libxo/encoder/cbor/Makefile.am new file mode 100644 index 000000000000..7ce44e09fedd --- /dev/null +++ b/contrib/libxo/encoder/cbor/Makefile.am @@ -0,0 +1,51 @@ +# +# $Id$ +# +# Copyright 2015, Juniper Networks, Inc. +# All rights reserved. +# This SOFTWARE is licensed under the LICENSE provided in the +# ../Copyright file. By downloading, installing, copying, or otherwise +# using the SOFTWARE, you agree to be bound by the terms of that +# LICENSE. + +if LIBXO_WARNINGS_HIGH +LIBXO_WARNINGS = HIGH +endif +if HAVE_GCC +GCC_WARNINGS = yes +endif +include ${top_srcdir}/warnings.mk + +enc_cborincdir = ${includedir}/libxo + +AM_CFLAGS = \ + -I${top_srcdir}/libxo \ + -I${top_builddir}/libxo \ + ${WARNINGS} + +LIBNAME = libenc_cbor +pkglib_LTLIBRARIES = libenc_cbor.la +LIBS = \ + -L${top_builddir}/libxo -lxo + +LDADD = ${top_builddir}/libxo/libxo.la + +libenc_cbor_la_SOURCES = \ + enc_cbor.c + +pkglibdir = ${XO_ENCODERDIR} + +UGLY_NAME = cbor.enc + +install-exec-hook: + @DLNAME=`sh -c '. ./libenc_cbor.la ; echo $$dlname'` ; \ + if [ x"$$DLNAME" = x ]; \ + then DLNAME=${LIBNAME}.${XO_LIBEXT}; fi ; \ + if [ "$(build_os)" = "cygwin" ]; \ + then DLNAME="../bin/$$DLNAME"; fi ; \ + echo Install link $$DLNAME "->" ${UGLY_NAME} "..." ; \ + mkdir -p ${DESTDIR}${XO_ENCODERDIR} ; \ + cd ${DESTDIR}${XO_ENCODERDIR} \ + && chmod +w . \ + && rm -f ${UGLY_NAME} \ + && ${LN_S} $$DLNAME ${UGLY_NAME} diff --git a/contrib/libxo/encoder/cbor/enc_cbor.c b/contrib/libxo/encoder/cbor/enc_cbor.c new file mode 100644 index 000000000000..513063fb1508 --- /dev/null +++ b/contrib/libxo/encoder/cbor/enc_cbor.c @@ -0,0 +1,365 @@ +/* + * Copyright (c) 2015, Juniper Networks, Inc. + * All rights reserved. + * This SOFTWARE is licensed under the LICENSE provided in the + * ../Copyright file. By downloading, installing, copying, or otherwise + * using the SOFTWARE, you agree to be bound by the terms of that + * LICENSE. + * Phil Shafer, August 2015 + */ + +/* + * CBOR (RFC 7049) mades a suitable test case for libxo's external + * encoder API. It's simple, streaming, well documented, and an + * IETF standard. + * + * This encoder uses the "pretty" flag for diagnostics, which isn't + * really kosher, but it's example code. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "xo.h" +#include "xo_encoder.h" +#include "xo_buf.h" + +/* + * memdump(): dump memory contents in hex/ascii +0 1 2 3 4 5 6 7 +0123456789012345678901234567890123456789012345678901234567890123456789012345 +XX XX XX XX XX XX XX XX - XX XX XX XX XX XX XX XX abcdefghijklmnop + */ +static void +cbor_memdump (FILE *fp, const char *title, const char *data, + size_t len, const char *tag, int indent) +{ + enum { MAX_PER_LINE = 16 }; + char buf[ 80 ]; + char text[ 80 ]; + char *bp, *tp; + size_t i; +#if 0 + static const int ends[ MAX_PER_LINE ] = { 2, 5, 8, 11, 15, 18, 21, 24, + 29, 32, 35, 38, 42, 45, 48, 51 }; +#endif + + if (fp == NULL) + fp = stdout; + if (tag == NULL) + tag = ""; + + fprintf(fp, "%*s[%s] @ %p (%lx/%lu)\n", indent + 1, tag, + title, data, (unsigned long) len, (unsigned long) len); + + while (len > 0) { + bp = buf; + tp = text; + + for (i = 0; i < MAX_PER_LINE && i < len; i++) { + if (i && (i % 4) == 0) *bp++ = ' '; + if (i == 8) { + *bp++ = '-'; + *bp++ = ' '; + } + sprintf(bp, "%02x ", (unsigned char) *data); + bp += strlen(bp); + *tp++ = (isprint((int) *data) && *data >= ' ') ? *data : '.'; + data += 1; + } + + *tp = 0; + *bp = 0; + fprintf(fp, "%*s%-54s%s\n", indent + 1, tag, buf, text); + len -= i; + } +} + +/* + * CBOR breaks the first byte into two pieces, the major type in the + * top 3 bits and the minor value in the low 5 bits. The value can be + * a small value (0 .. 23), an 8-bit value (24), a 16-bit value (25), + * a 32-bit value (26), or a 64-bit value (27). A value of 31 + * represents an unknown length, which we'll use extensively for + * streaming our content. + */ +#define CBOR_MAJOR_MASK 0xE0 +#define CBOR_MINOR_MASK 0x1F +#define CBOR_MAJOR_SHIFT 5 + +#define CBOR_MAJOR(_x) ((_x) & CBOR_MAJOR_MASK) +#define CBOR_MAJOR_VAL(_x) ((_x) << CBOR_MAJOR_SHIFT) +#define CBOR_MINOR_VAL(_x) ((_x) & CBOR_MINOR_MASK) + +/* Major type codes */ +#define CBOR_UNSIGNED CBOR_MAJOR_VAL(0) /* 0x00 */ +#define CBOR_NEGATIVE CBOR_MAJOR_VAL(1) /* 0x20 */ +#define CBOR_BYTES CBOR_MAJOR_VAL(2) /* 0x40 */ +#define CBOR_STRING CBOR_MAJOR_VAL(3) /* 0x60 */ +#define CBOR_ARRAY CBOR_MAJOR_VAL(4) /* 0x80 */ +#define CBOR_MAP CBOR_MAJOR_VAL(5) /* 0xa0 */ +#define CBOR_SEMANTIC CBOR_MAJOR_VAL(6) /* 0xc0 */ +#define CBOR_SPECIAL CBOR_MAJOR_VAL(7) /* 0xe0 */ + +#define CBOR_ULIMIT 24 /* Largest unsigned value */ +#define CBOR_NLIMIT 23 /* Largest negative value */ + +#define CBOR_BREAK 0xFF +#define CBOR_INDEF 0x1F + +#define CBOR_FALSE 0xF4 +#define CBOR_TRUE 0xF5 +#define CBOR_NULL 0xF6 +#define CBOR_UNDEF 0xF7 + +#define CBOR_LEN8 0x18 /* 24 - 8-bit value */ +#define CBOR_LEN16 0x19 /* 25 - 16-bit value */ +#define CBOR_LEN32 0x1a /* 26 - 32-bit value */ +#define CBOR_LEN64 0x1b /* 27 - 64-bit value */ +#define CBOR_LEN128 0x1c /* 28 - 128-bit value */ + +typedef struct cbor_private_s { + xo_buffer_t c_data; /* Our data buffer */ + unsigned c_indent; /* Indent level */ + unsigned c_open_leaf_list; /* Open leaf list construct? */ +} cbor_private_t; + +static void +cbor_encode_uint (xo_buffer_t *xbp, uint64_t minor, unsigned limit) +{ + char *bp = xbp->xb_curp; + int i, m; + + if (minor > (1UL<<32)) { + *bp++ |= CBOR_LEN64; + m = 64; + + } else if (minor > (1<<16)) { + *bp++ |= CBOR_LEN32; + m = 32; + + } else if (minor > (1<<8)) { + *bp++ |= CBOR_LEN16; + m = 16; + + } else if (minor > limit) { + *bp++ |= CBOR_LEN8; + m = 8; + } else { + *bp++ |= minor & CBOR_MINOR_MASK; + m = 0; + } + + if (m) { + for (i = m - 8; i >= 0; i -= 8) + *bp++ = minor >> i; + } + + xbp->xb_curp = bp; +} + +static void +cbor_append (xo_handle_t *xop, cbor_private_t *cbor, xo_buffer_t *xbp, + unsigned major, unsigned minor, const char *data) +{ + if (!xo_buf_has_room(xbp, minor + 2)) + return; + + unsigned offset = xo_buf_offset(xbp); + + *xbp->xb_curp = major; + cbor_encode_uint(xbp, minor, CBOR_ULIMIT); + if (data) + xo_buf_append(xbp, data, minor); + + if (xo_get_flags(xop) & XOF_PRETTY) + cbor_memdump(stdout, "append", xo_buf_data(xbp, offset), + xbp->xb_curp - xbp->xb_bufp - offset, "", + cbor->c_indent * 2); +} + +static int +cbor_create (xo_handle_t *xop) +{ + cbor_private_t *cbor = xo_realloc(NULL, sizeof(*cbor)); + if (cbor == NULL) + return -1; + + bzero(cbor, sizeof(*cbor)); + xo_buf_init(&cbor->c_data); + + xo_set_private(xop, cbor); + + cbor_append(xop, cbor, &cbor->c_data, CBOR_MAP | CBOR_INDEF, 0, NULL); + + return 0; +} + +static int +cbor_content (xo_handle_t *xop, cbor_private_t *cbor, xo_buffer_t *xbp, + const char *value) +{ + int rc = 0; + + unsigned offset = xo_buf_offset(xbp); + + if (value == NULL || *value == '\0' || strcmp(value, "true") == 0) + cbor_append(xop, cbor, &cbor->c_data, CBOR_TRUE, 0, NULL); + else if (strcmp(value, "false") == 0) + cbor_append(xop, cbor, &cbor->c_data, CBOR_FALSE, 0, NULL); + else { + int negative = 0; + if (*value == '-') { + value += 1; + negative = 1; + } + + char *ep; + unsigned long long ival; + ival = strtoull(value, &ep, 0); + if (ival == ULLONG_MAX) /* Sometimes a string is just a string */ + cbor_append(xop, cbor, xbp, CBOR_STRING, strlen(value), value); + else { + *xbp->xb_curp = negative ? CBOR_NEGATIVE : CBOR_UNSIGNED; + if (negative) + ival -= 1; /* Don't waste a negative zero */ + cbor_encode_uint(xbp, ival, negative ? CBOR_NLIMIT : CBOR_ULIMIT); + } + } + + if (xo_get_flags(xop) & XOF_PRETTY) + cbor_memdump(stdout, "content", xo_buf_data(xbp, offset), + xbp->xb_curp - xbp->xb_bufp - offset, "", + cbor->c_indent * 2); + + return rc; +} + +static int +cbor_handler (XO_ENCODER_HANDLER_ARGS) +{ + int rc = 0; + cbor_private_t *cbor = private; + xo_buffer_t *xbp = cbor ? &cbor->c_data : NULL; + + if (xo_get_flags(xop) & XOF_PRETTY) { + printf("%*sop %s: [%s] [%s]\n", cbor ? cbor->c_indent * 2 + 4 : 0, "", + xo_encoder_op_name(op), name ?: "", value ?: ""); + fflush(stdout); + } + + /* If we don't have private data, we're sunk */ + if (cbor == NULL && op != XO_OP_CREATE) + return -1; + + switch (op) { + case XO_OP_CREATE: /* Called when the handle is init'd */ + rc = cbor_create(xop); + break; + + case XO_OP_OPEN_CONTAINER: + cbor_append(xop, cbor, xbp, CBOR_STRING, strlen(name), name); + cbor_append(xop, cbor, xbp, CBOR_MAP | CBOR_INDEF, 0, NULL); + cbor->c_indent += 1; + break; + + case XO_OP_CLOSE_CONTAINER: + cbor_append(xop, cbor, xbp, CBOR_BREAK, 0, NULL); + cbor->c_indent -= 1; + break; + + case XO_OP_OPEN_LIST: + cbor_append(xop, cbor, xbp, CBOR_STRING, strlen(name), name); + cbor_append(xop, cbor, xbp, CBOR_ARRAY | CBOR_INDEF, 0, NULL); + cbor->c_indent += 1; + break; + + case XO_OP_CLOSE_LIST: + cbor_append(xop, cbor, xbp, CBOR_BREAK, 0, NULL); + cbor->c_indent -= 1; + break; + + case XO_OP_OPEN_LEAF_LIST: + cbor_append(xop, cbor, xbp, CBOR_STRING, strlen(name), name); + cbor_append(xop, cbor, xbp, CBOR_ARRAY | CBOR_INDEF, 0, NULL); + cbor->c_indent += 1; + cbor->c_open_leaf_list = 1; + break; + + case XO_OP_CLOSE_LEAF_LIST: + cbor_append(xop, cbor, xbp, CBOR_BREAK, 0, NULL); + cbor->c_indent -= 1; + cbor->c_open_leaf_list = 0; + break; + + case XO_OP_OPEN_INSTANCE: + cbor_append(xop, cbor, xbp, CBOR_MAP | CBOR_INDEF, 0, NULL); + cbor->c_indent += 1; + break; + + case XO_OP_CLOSE_INSTANCE: + cbor_append(xop, cbor, xbp, CBOR_BREAK, 0, NULL); + cbor->c_indent -= 1; + break; + + case XO_OP_STRING: /* Quoted UTF-8 string */ + if (!cbor->c_open_leaf_list) + cbor_append(xop, cbor, xbp, CBOR_STRING, strlen(name), name); + cbor_append(xop, cbor, xbp, CBOR_STRING, strlen(value), value); + break; + + case XO_OP_CONTENT: /* Other content */ + if (!cbor->c_open_leaf_list) + cbor_append(xop, cbor, xbp, CBOR_STRING, strlen(name), name); + + /* + * It's content, not string, so we need to look at the + * string and build some content. Turns out we only + * care about true, false, null, and numbers. + */ + cbor_content(xop, cbor, xbp, value); + break; + + case XO_OP_FINISH: /* Clean up function */ + cbor_append(xop, cbor, xbp, CBOR_BREAK, 0, NULL); + cbor->c_indent -= 1; + break; + + case XO_OP_FLUSH: /* Clean up function */ + if (xo_get_flags(xop) & XOF_PRETTY) + cbor_memdump(stdout, "cbor", + xbp->xb_bufp, xbp->xb_curp - xbp->xb_bufp, + ">", 0); + else { + rc = write(1, xbp->xb_bufp, xbp->xb_curp - xbp->xb_bufp); + if (rc > 0) + rc = 0; + } + break; + + case XO_OP_DESTROY: /* Clean up function */ + break; + + case XO_OP_ATTRIBUTE: /* Attribute name/value */ + break; + + case XO_OP_VERSION: /* Version string */ + break; + + } + + return rc; +} + +int +xo_encoder_library_init (XO_ENCODER_INIT_ARGS) +{ + arg->xei_handler = cbor_handler; + + return 0; +} diff --git a/contrib/libxo/encoder/test/Makefile.am b/contrib/libxo/encoder/test/Makefile.am new file mode 100644 index 000000000000..1d8518e8e7ea --- /dev/null +++ b/contrib/libxo/encoder/test/Makefile.am @@ -0,0 +1,51 @@ +# +# $Id$ +# +# Copyright 2015, Juniper Networks, Inc. +# All rights reserved. +# This SOFTWARE is licensed under the LICENSE provided in the +# ../Copyright file. By downloading, installing, copying, or otherwise +# using the SOFTWARE, you agree to be bound by the terms of that +# LICENSE. + +if LIBXO_WARNINGS_HIGH +LIBXO_WARNINGS = HIGH +endif +if HAVE_GCC +GCC_WARNINGS = yes +endif +include ${top_srcdir}/warnings.mk + +enc_testincdir = ${includedir}/libxo + +AM_CFLAGS = \ + -I${top_srcdir}/libxo \ + -I${top_builddir}/libxo \ + ${WARNINGS} + +LIBNAME = libenc_test +pkglib_LTLIBRARIES = libenc_test.la +LIBS = \ + -L${top_builddir}/libxo -lxo + +LDADD = ${top_builddir}/libxo/libxo.la + +libenc_test_la_SOURCES = \ + enc_test.c + +pkglibdir = ${XO_ENCODERDIR} + +UGLY_NAME = test.enc + +install-exec-hook: + @DLNAME=`sh -c '. ./libenc_test.la ; echo $$dlname'` ; \ + if [ x"$$DLNAME" = x ]; \ + then DLNAME=${LIBNAME}.${XO_LIBEXT}; fi ; \ + if [ "$(build_os)" = "cygwin" ]; \ + then DLNAME="../bin/$$DLNAME"; fi ; \ + echo Install link $$DLNAME "->" ${UGLY_NAME} "..." ; \ + mkdir -p ${DESTDIR}${XO_ENCODERDIR} ; \ + cd ${DESTDIR}${XO_ENCODERDIR} \ + && chmod +w . \ + && rm -f ${UGLY_NAME} \ + && ${LN_S} $$DLNAME ${UGLY_NAME} diff --git a/contrib/libxo/encoder/test/enc_test.c b/contrib/libxo/encoder/test/enc_test.c new file mode 100644 index 000000000000..ec49499c00c0 --- /dev/null +++ b/contrib/libxo/encoder/test/enc_test.c @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2015, Juniper Networks, Inc. + * All rights reserved. + * This SOFTWARE is licensed under the LICENSE provided in the + * ../Copyright file. By downloading, installing, copying, or otherwise + * using the SOFTWARE, you agree to be bound by the terms of that + * LICENSE. + * Phil Shafer, August 2015 + */ + +#include "xo.h" +#include "xo_encoder.h" + +static int +test_handler (XO_ENCODER_HANDLER_ARGS) +{ + printf("op %s: [%s] [%s]\n", xo_encoder_op_name(op), + name ?: "", value ?: ""); + + return 0; +} + +int +xo_encoder_library_init (XO_ENCODER_INIT_ARGS) +{ + arg->xei_version = XO_ENCODER_VERSION; + arg->xei_handler = test_handler; + + return 0; +} diff --git a/contrib/libxo/libxo/Makefile.am b/contrib/libxo/libxo/Makefile.am index 0047b6322586..b11c311e70b2 100644 --- a/contrib/libxo/libxo/Makefile.am +++ b/contrib/libxo/libxo/Makefile.am @@ -16,25 +16,44 @@ include ${top_srcdir}/warnings.mk libxoincdir = ${includedir}/libxo -AM_CFLAGS = -I${top_srcdir} ${WARNINGS} +AM_CFLAGS = \ + -I${top_srcdir} \ + ${WARNINGS} \ + ${GETTEXT_CFLAGS} + +AM_CFLAGS += \ + -DXO_ENCODERDIR=\"${XO_ENCODERDIR}\" lib_LTLIBRARIES = libxo.la +LIBS = \ + ${GETTEXT_LIBS} + libxoinc_HEADERS = \ - xo.h + xo.h \ + xo_encoder.h + +noinst_HEADERS = \ + xo_buf.h \ + xo_humanize.h \ + xo_wcwidth.h libxo_la_SOURCES = \ - libxo.c + libxo.c \ + xo_encoder.c \ + xo_syslog.c -man_MANS = \ +man3_files = \ libxo.3 \ xo_attr.3 \ xo_create.3 \ xo_emit.3 \ + xo_emit_err.3 \ xo_err.3 \ + xo_error.3 \ xo_finish.3 \ xo_flush.3 \ - xo_format.5 \ + xo_message.3 \ xo_no_setlocale.3 \ xo_open_container.3 \ xo_open_list.3 \ @@ -45,8 +64,26 @@ man_MANS = \ xo_set_info.3 \ xo_set_options.3 \ xo_set_style.3 \ + xo_set_syslog_enterprise_id.3 \ xo_set_version.3 \ - xo_set_writer.3 + xo_set_writer.3 \ + xo_syslog.3 -EXTRA_DIST = ${man_MANS} +man5_files = \ + xo_format.5 +man_MANS = ${man3_files} ${man5_files} + +EXTRA_DIST = \ + ${man_MANS} + +call-graph: + ${RM} libxo.o + ${MAKE} CC="clang -Xclang -analyze -Xclang \ + -analyzer-checker=debug.ViewCallGraph" libxo.o + +install-data-hook: + for file in ${man3_files}; do \ + cat ../libxo/add.man >> ${DESTDIR}${man3dir}/$$file ; done + for file in ${man5_files}; do \ + cat ../libxo/add.man >> ${DESTDIR}${man5dir}/$$file ; done diff --git a/contrib/libxo/libxo/add.man b/contrib/libxo/libxo/add.man new file mode 100644 index 000000000000..a1e3e11e9984 --- /dev/null +++ b/contrib/libxo/libxo/add.man @@ -0,0 +1,29 @@ +.Sh ADDITIONAL DOCUMENTATION +.Fx +uses +.Nm libxo +version 0.4.3. +Complete documentation can be found on github: +.Bd -literal -offset indent +http://juniper.github.io/libxo/0.4.3/libxo\-manual.html +.Ed +.Pp +.Nm libxo +lives on github as: +.Bd -literal -offset indent +https://github.com/Juniper/libxo +.Ed +.Pp +The latest release of +.Nm libxo +is available at: +.Bd -literal -offset indent +https://github.com/Juniper/libxo/releases +.Ed +.Sh HISTORY +The +.Nm libxo +library was added in +.Fx 11.0 . +.Sh AUTHOR +Phil Shafer diff --git a/contrib/libxo/libxo/add.man.in b/contrib/libxo/libxo/add.man.in new file mode 100644 index 000000000000..4eae26566aee --- /dev/null +++ b/contrib/libxo/libxo/add.man.in @@ -0,0 +1,29 @@ +.Sh ADDITIONAL DOCUMENTATION +.Fx +uses +.Nm libxo +version @LIBXO_VERSION@. +Complete documentation can be found on github: +.Bd -literal -offset indent +http://juniper.github.io/libxo/@LIBXO_VERSION@/libxo\-manual.html +.Ed +.Pp +.Nm libxo +lives on github as: +.Bd -literal -offset indent +https://github.com/Juniper/libxo +.Ed +.Pp +The latest release of +.Nm libxo +is available at: +.Bd -literal -offset indent +https://github.com/Juniper/libxo/releases +.Ed +.Sh HISTORY +The +.Nm libxo +library was added in +.Fx 11.0 . +.Sh AUTHOR +Phil Shafer diff --git a/contrib/libxo/libxo/libxo.3 b/contrib/libxo/libxo/libxo.3 index f9b0e6f8b2a9..4e2488cd5f57 100644 --- a/contrib/libxo/libxo/libxo.3 +++ b/contrib/libxo/libxo/libxo.3 @@ -66,17 +66,25 @@ output, with attributes that detail how to render the data. .Pp There are four encoding styles supported by .Nm : -TEXT, HTML, JSON, -and XML. -JSON and XML are suitable for encoding data, while TEXT and -HTML are suited for display to the user. -TEXT output can be display -on a terminal session, allowing compatibility with traditional usage. +.Bl -bullet +.It +TEXT output can be display on a terminal session, allowing +compatibility with traditional command line usage. +.It +XML output is suitable for tools like XPath and protocols like +NETCONF. +.It +JSON output can be used for RESTful APIs and integration with +languages like Javascript and Python. +.It HTML can be matched with a small CSS file to permit rendering in any HTML5 browser. -XML output is suitable for tools like XPath and -protocols like NETCONF. -JSON output can be used for RESTful APIs. +.El +.Pp +In general, XML and JSON are suitable for encoding data, while TEXT is +suited for terminal output and HTML is suited for display in a web +browser (see +.Xr xohtml 1 ). .Pp The .Nm @@ -177,6 +185,19 @@ formatted output. See .Xr xo_format 5 for details. +.It Fn xo_emit_warn +.It Fn xo_emit_warnx +.It Fn xo_emit_warn_c +.It Fn xo_emit_warn_hc +.It Fn xo_emit_err +.It Fn xo_emit_errc +.It Fn xo_emit_errx +These functions are mildly compatible with their standard libc +namesakes, but use the format string defined in +.Xr xo_format 5 . +While there is an increased cost for converting the strings, the +output provided can be richer and more useful. See also +.Xr xo_err 3 .It Fn xo_warn .It Fn xo_warnx .It Fn xo_warn_c @@ -269,30 +290,13 @@ Instructs .Nm to use an alternative set of low-level output functions. .El -.Sh ADDITIONAL DOCUMENTATION -Complete documentation can be found on github: -.Bd -literal -offset indent -http://juniper.github.io/libxo/libxo-manual.html -.Ed -.Pp -.Nm -lives on github as: -.Bd -literal -offset indent -https://github.com/Juniper/libxo -.Ed -.Pp -The latest release of -.Nm -is available at: -.Bd -literal -offset indent -https://github.com/Juniper/libxo/releases -.Ed .Sh SEE ALSO .Xr xo 1 , .Xr xolint 1 , .Xr xo_attr 3 , .Xr xo_create 3 , .Xr xo_emit 3 , +.Xr xo_emit_err 3 , .Xr xo_err 3 , .Xr xo_finish 3 , .Xr xo_flush 3 , @@ -307,10 +311,3 @@ https://github.com/Juniper/libxo/releases .Xr xo_set_style 3 , .Xr xo_set_writer 3 , .Xr xo_format 5 -.Sh HISTORY -The -.Nm -library was added in -.Fx 11.0 . -.Sh AUTHOR -Phil Shafer diff --git a/contrib/libxo/libxo/libxo.c b/contrib/libxo/libxo/libxo.c index bb4ce2e7e8af..b531371170d7 100644 --- a/contrib/libxo/libxo/libxo.c +++ b/contrib/libxo/libxo/libxo.c @@ -6,6 +6,24 @@ * using the SOFTWARE, you agree to be bound by the terms of that * LICENSE. * Phil Shafer, July 2014 + * + * This is the implementation of libxo, the formatting library that + * generates multiple styles of output from a single code path. + * Command line utilities can have their normal text output while + * automation tools can see XML or JSON output, and web tools can use + * HTML output that encodes the text output annotated with additional + * information. Specialized encoders can be built that allow custom + * encoding including binary ones like CBOR, thrift, protobufs, etc. + * + * Full documentation is available in ./doc/libxo.txt or online at: + * http://juniper.github.io/libxo/libxo-manual.html + * + * For first time readers, the core bits of code to start looking at are: + * - xo_do_emit() -- the central function of the library + * - xo_do_format_field() -- handles formatting a single field + * - xo_transiton() -- the state machine that keeps things sane + * and of course the "xo_handle_t" data structure, which carries all + * configuration and state. */ #include @@ -24,14 +42,82 @@ #include #include -#include "xoconfig.h" +#include "xo_config.h" #include "xo.h" -#include "xoversion.h" +#include "xo_encoder.h" +#include "xo_buf.h" + +/* + * We ask wcwidth() to do an impossible job, really. It's supposed to + * need to tell us the number of columns consumed to display a unicode + * character. It returns that number without any sort of context, but + * we know they are characters whose glyph differs based on placement + * (end of word, middle of word, etc) and many that affect characters + * previously emitted. Without content, it can't hope to tell us. + * But it's the only standard tool we've got, so we use it. We would + * use wcswidth() but it typically just loops thru adding the results + * of wcwidth() calls in an entirely unhelpful way. + * + * Even then, there are many poor implementations (macosx), so we have + * to carry our own. We could have configure.ac test this (with + * something like 'assert(wcwidth(0x200d) == 0)'), but it would have + * to run a binary, which breaks cross-compilation. Hmm... I could + * run this test at init time and make a warning for our dear user. + * + * Anyhow, it remains a best-effort sort of thing. And it's all made + * more hopeless because we assume the display code doing the rendering is + * playing by the same rules we are. If it display 0x200d as a square + * box or a funky question mark, the output will be hosed. + */ +#ifdef LIBXO_WCWIDTH +#include "xo_wcwidth.h" +#else /* LIBXO_WCWIDTH */ +#define xo_wcwidth(_x) wcwidth(_x) +#endif /* LIBXO_WCWIDTH */ #ifdef HAVE_STDIO_EXT_H #include #endif /* HAVE_STDIO_EXT_H */ +/* + * humanize_number is a great function, unless you don't have it. So + * we carry one in our pocket. + */ +#ifdef HAVE_HUMANIZE_NUMBER +#include +#define xo_humanize_number humanize_number +#else /* HAVE_HUMANIZE_NUMBER */ +#include "xo_humanize.h" +#endif /* HAVE_HUMANIZE_NUMBER */ + +#ifdef HAVE_GETTEXT +#include +#endif /* HAVE_GETTEXT */ + +/* + * Three styles of specifying thread-local variables are supported. + * configure.ac has the brains to run each possibility thru the + * compiler and see what works; we are left to define the THREAD_LOCAL + * macro to the right value. Most toolchains (clang, gcc) use + * "before", but some (borland) use "after" and I've heard of some + * (ms) that use __declspec. Any others out there? + */ +#define THREAD_LOCAL_before 1 +#define THREAD_LOCAL_after 2 +#define THREAD_LOCAL_declspec 3 + +#ifndef HAVE_THREAD_LOCAL +#define THREAD_LOCAL(_x) _x +#elif HAVE_THREAD_LOCAL == THREAD_LOCAL_before +#define THREAD_LOCAL(_x) __thread _x +#elif HAVE_THREAD_LOCAL == THREAD_LOCAL_after +#define THREAD_LOCAL(_x) _x __thread +#elif HAVE_THREAD_LOCAL == THREAD_LOCAL_declspec +#define THREAD_LOCAL(_x) __declspec(_x) +#else +#error unknown thread-local setting +#endif /* HAVE_THREADS_H */ + const char xo_version[] = LIBXO_VERSION; const char xo_version_extra[] = LIBXO_VERSION_EXTRA; @@ -40,22 +126,11 @@ const char xo_version_extra[] = LIBXO_VERSION_EXTRA; #endif /* UNUSED */ #define XO_INDENT_BY 2 /* Amount to indent when pretty printing */ -#define XO_BUFSIZ (8*1024) /* Initial buffer size */ -#define XO_DEPTH 512 /* Default stack depth */ +#define XO_DEPTH 128 /* Default stack depth */ #define XO_MAX_ANCHOR_WIDTH (8*1024) /* Anything wider is just sillyb */ #define XO_FAILURE_NAME "failure" -/* - * xo_buffer_t: a memory buffer that can be grown as needed. We - * use them for building format strings and output data. - */ -typedef struct xo_buffer_s { - char *xb_bufp; /* Buffer memory */ - char *xb_curp; /* Current insertion point */ - int xb_size; /* Size of buffer */ -} xo_buffer_t; - /* Flags for the stack frame */ typedef unsigned xo_xsf_flags_t; /* XSF_* flags */ #define XSF_NOT_FIRST (1<<0) /* Not the first element */ @@ -73,17 +148,17 @@ typedef unsigned xo_xsf_flags_t; /* XSF_* flags */ (XSF_NOT_FIRST | XSF_CONTENT | XSF_EMIT | XSF_EMIT_KEY | XSF_EMIT_LEAF_LIST ) /* - * A word about states: We're moving to a finite state machine (FMS) - * approach to help remove fragility from the caller's code. Instead - * of requiring a specific order of calls, we'll allow the caller more + * A word about states: We use a finite state machine (FMS) approach + * to help remove fragility from the caller's code. Instead of + * requiring a specific order of calls, we'll allow the caller more * flexibility and make the library responsible for recovering from - * missed steps. The goal is that the library should not be capable of - * emitting invalid xml or json, but the developer shouldn't need + * missed steps. The goal is that the library should not be capable + * of emitting invalid xml or json, but the developer shouldn't need * to know or understand all the details about these encodings. * - * You can think of states as either states or event, since they + * You can think of states as either states or events, since they * function rather like both. None of the XO_CLOSE_* events will - * persist as states, since their stack frame will be popped. + * persist as states, since the matching stack frame will be popped. * Same is true of XSS_EMIT, which is an event that asks us to * prep for emitting output fields. */ @@ -121,7 +196,11 @@ typedef struct xo_stack_s { char *xs_keys; /* XPath predicate for any key fields */ } xo_stack_t; -/* "colors" refers to fancy ansi codes */ +/* + * libxo supports colors and effects, for those who like them. + * XO_COL_* ("colors") refers to fancy ansi codes, while X__EFF_* + * ("effects") are bits since we need to maintain state. + */ #define XO_COL_DEFAULT 0 #define XO_COL_BLACK 1 #define XO_COL_RED 2 @@ -134,20 +213,19 @@ typedef struct xo_stack_s { #define XO_NUM_COLORS 9 -/* "effects" refers to fancy ansi codes */ /* * Yes, there's no blink. We're civilized. We like users. Blink * isn't something one does to someone you like. Friends don't let * friends use blink. On friends. You know what I mean. Blink is * like, well, it's like bursting into show tunes at a funeral. It's * just not done. Not something anyone wants. And on those rare - * instances where it might actually be appropriate, it's still wrong. - * It's likely done my the wrong person for the wrong reason. Just - * like blink. And if I implemented blink, I'd be like a funeral + * instances where it might actually be appropriate, it's still wrong, + * since it's likely done by the wrong person for the wrong reason. + * Just like blink. And if I implemented blink, I'd be like a funeral * director who adds "Would you like us to burst into show tunes?" on - * the list of questions asking while making funeral arrangements. + * the list of questions asked while making funeral arrangements. * It's formalizing wrongness in the wrong way. And we're just too - * civilized to do that. Hhhmph! + * civilized to do that. Hhhmph! */ #define XO_EFF_RESET (1<<0) #define XO_EFF_NORMAL (1<<1) @@ -155,7 +233,7 @@ typedef struct xo_stack_s { #define XO_EFF_UNDERLINE (1<<3) #define XO_EFF_INVERSE (1<<4) -#define XO_EFF_CLEAR_BITS XO_EFF_RESET +#define XO_EFF_CLEAR_BITS XO_EFF_RESET /* Reset gets reset, surprisingly */ typedef uint8_t xo_effect_t; typedef uint8_t xo_color_t; @@ -167,11 +245,13 @@ typedef struct xo_colors_s { /* * xo_handle_t: this is the principle data structure for libxo. - * It's used as a store for state, options, and content. + * It's used as a store for state, options, content, and all manor + * of other information. */ struct xo_handle_s { - xo_xof_flags_t xo_flags; /* Flags */ - unsigned short xo_style; /* XO_STYLE_* value */ + xo_xof_flags_t xo_flags; /* Flags (XOF_*) from the user*/ + xo_xof_flags_t xo_iflags; /* Internal flags (XOIF_*) */ + xo_style_t xo_style; /* XO_STYLE_* value */ unsigned short xo_indent; /* Indent level (if pretty) */ unsigned short xo_indent_by; /* Indent amount (tab stop) */ xo_write_func_t xo_write; /* Write callback */ @@ -202,18 +282,44 @@ struct xo_handle_s { xo_colors_t xo_colors; /* Current color and effect values */ xo_buffer_t xo_color_buf; /* HTML: buffer of colors and effects */ char *xo_version; /* Version string */ + int xo_errno; /* Saved errno for "%m" */ + char *xo_gt_domain; /* Gettext domain, suitable for dgettext(3) */ + xo_encoder_func_t xo_encoder; /* Encoding function */ + void *xo_private; /* Private data for external encoders */ }; +/* Flag operations */ +#define XOF_BIT_ISSET(_flag, _bit) (((_flag) & (_bit)) ? 1 : 0) +#define XOF_BIT_SET(_flag, _bit) do { (_flag) |= (_bit); } while (0) +#define XOF_BIT_CLEAR(_flag, _bit) do { (_flag) &= ~(_bit); } while (0) + +#define XOF_ISSET(_xop, _bit) XOF_BIT_ISSET(_xop->xo_flags, _bit) +#define XOF_SET(_xop, _bit) XOF_BIT_SET(_xop->xo_flags, _bit) +#define XOF_CLEAR(_xop, _bit) XOF_BIT_CLEAR(_xop->xo_flags, _bit) + +#define XOIF_ISSET(_xop, _bit) XOF_BIT_ISSET(_xop->xo_iflags, _bit) +#define XOIF_SET(_xop, _bit) XOF_BIT_SET(_xop->xo_iflags, _bit) +#define XOIF_CLEAR(_xop, _bit) XOF_BIT_CLEAR(_xop->xo_iflags, _bit) + +/* Internal flags */ +#define XOIF_REORDER XOF_BIT(0) /* Reordering fields; record field info */ +#define XOIF_DIV_OPEN XOF_BIT(1) /* A
is open */ +#define XOIF_TOP_EMITTED XOF_BIT(2) /* The top JSON braces have been emitted */ +#define XOIF_ANCHOR XOF_BIT(3) /* An anchor is in place */ + +#define XOIF_UNITS_PENDING XOF_BIT(4) /* We have a units-insertion pending */ +#define XOIF_INIT_IN_PROGRESS XOF_BIT(5) /* Init of handle is in progress */ + /* Flags for formatting functions */ typedef unsigned long xo_xff_flags_t; #define XFF_COLON (1<<0) /* Append a ":" */ #define XFF_COMMA (1<<1) /* Append a "," iff there's more output */ #define XFF_WS (1<<2) /* Append a blank */ -#define XFF_ENCODE_ONLY (1<<3) /* Only emit for encoding formats (xml, json) */ +#define XFF_ENCODE_ONLY (1<<3) /* Only emit for encoding styles (XML, JSON) */ #define XFF_QUOTE (1<<4) /* Force quotes */ #define XFF_NOQUOTE (1<<5) /* Force no quotes */ -#define XFF_DISPLAY_ONLY (1<<6) /* Only emit for display formats (text and html) */ +#define XFF_DISPLAY_ONLY (1<<6) /* Only emit for display styles (text, html) */ #define XFF_KEY (1<<7) /* Field is a key (for XPath) */ #define XFF_XML (1<<8) /* Force XML encoding style (for XPath) */ @@ -224,6 +330,17 @@ typedef unsigned long xo_xff_flags_t; #define XFF_TRIM_WS (1<<12) /* Trim whitespace off encoded values */ #define XFF_LEAF_LIST (1<<13) /* A leaf-list (list of values) */ #define XFF_UNESCAPE (1<<14) /* Need to printf-style unescape the value */ +#define XFF_HUMANIZE (1<<15) /* Humanize the value (for display styles) */ + +#define XFF_HN_SPACE (1<<16) /* Humanize: put space before suffix */ +#define XFF_HN_DECIMAL (1<<17) /* Humanize: add one decimal place if <10 */ +#define XFF_HN_1000 (1<<18) /* Humanize: use 1000, not 1024 */ +#define XFF_GT_FIELD (1<<19) /* Call gettext() on a field */ + +#define XFF_GT_PLURAL (1<<20) /* Call dngettext to find plural form */ + +/* Flags to turn off when we don't want i18n processing */ +#define XFF_GT_FLAGS (XFF_GT_FIELD | XFF_GT_PLURAL) /* * Normal printf has width and precision, which for strings operate as @@ -258,7 +375,6 @@ typedef unsigned long xo_xff_flags_t; * that is C string handling. The simplicity and completenesss are * sunk in ways we haven't even begun to understand. */ - #define XF_WIDTH_MIN 0 /* Minimal width */ #define XF_WIDTH_SIZE 1 /* Maximum number of bytes to examine */ #define XF_WIDTH_MAX 2 /* Maximum width */ @@ -291,12 +407,39 @@ typedef struct xo_format_s { } xo_format_t; /* - * We keep a default handle to allow callers to avoid having to - * allocate one. Passing NULL to any of our functions will use - * this default handle. + * This structure represents the parsed field information, suitable for + * processing by xo_do_emit and anything else that needs to parse fields. + * Note that all pointers point to the main format string. + * + * XXX This is a first step toward compilable or cachable format + * strings. We can also cache the results of dgettext when no format + * is used, assuming the 'p' modifier has _not_ been set. */ -static xo_handle_t xo_default_handle; -static int xo_default_inited; +typedef struct xo_field_info_s { + xo_xff_flags_t xfi_flags; /* Flags for this field */ + unsigned xfi_ftype; /* Field type, as character (e.g. 'V') */ + const char *xfi_start; /* Start of field in the format string */ + const char *xfi_content; /* Field's content */ + const char *xfi_format; /* Field's Format */ + const char *xfi_encoding; /* Field's encoding format */ + const char *xfi_next; /* Next character in format string */ + unsigned xfi_len; /* Length of field */ + unsigned xfi_clen; /* Content length */ + unsigned xfi_flen; /* Format length */ + unsigned xfi_elen; /* Encoding length */ + unsigned xfi_fnum; /* Field number (if used; 0 otherwise) */ + unsigned xfi_renum; /* Reordered number (0 == no renumbering) */ +} xo_field_info_t; + +/* + * We keep a 'default' handle to allow callers to avoid having to + * allocate one. Passing NULL to any of our functions will use + * this default handle. Most functions have a variant that doesn't + * require a handle at all, since most output is to stdout, which + * the default handle handles handily. + */ +static THREAD_LOCAL(xo_handle_t) xo_default_handle; +static THREAD_LOCAL(int) xo_default_inited; static int xo_locale_inited; static const char *xo_program; @@ -304,8 +447,8 @@ static const char *xo_program; * To allow libxo to be used in diverse environment, we allow the * caller to give callbacks for memory allocation. */ -static xo_realloc_func_t xo_realloc = realloc; -static xo_free_func_t xo_free = free; +xo_realloc_func_t xo_realloc = realloc; +xo_free_func_t xo_free = free; /* Forward declarations */ static void @@ -332,7 +475,7 @@ xo_anchor_clear (xo_handle_t *xop); * trims our code nicely without needing to trampel perfectly readable * code with ifdefs. */ -static inline unsigned short +static inline xo_style_t xo_style (xo_handle_t *xop UNUSED) { #ifdef LIBXO_TEXT_ONLY @@ -376,43 +519,41 @@ xo_flush_file (void *opaque) } /* - * Initialize the contents of an xo_buffer_t. + * Use a rotating stock of buffers to make a printable string */ -static void -xo_buf_init (xo_buffer_t *xbp) -{ - xbp->xb_size = XO_BUFSIZ; - xbp->xb_bufp = xo_realloc(NULL, xbp->xb_size); - xbp->xb_curp = xbp->xb_bufp; -} +#define XO_NUMBUFS 8 +#define XO_SMBUFSZ 128 -/* - * Reset the buffer to empty - */ -static void -xo_buf_reset (xo_buffer_t *xbp) +static const char * +xo_printable (const char *str) { - xbp->xb_curp = xbp->xb_bufp; -} + static THREAD_LOCAL(char) bufset[XO_NUMBUFS][XO_SMBUFSZ]; + static THREAD_LOCAL(int) bufnum = 0; -/* - * Reset the buffer to empty - */ -static int -xo_buf_is_empty (xo_buffer_t *xbp) -{ - return (xbp->xb_curp == xbp->xb_bufp); -} + if (str == NULL) + return ""; -/* - * Initialize the contents of an xo_buffer_t. - */ -static void -xo_buf_cleanup (xo_buffer_t *xbp) -{ - if (xbp->xb_bufp) - xo_free(xbp->xb_bufp); - bzero(xbp, sizeof(*xbp)); + if (++bufnum == XO_NUMBUFS) + bufnum = 0; + + char *res = bufset[bufnum], *cp, *ep; + + for (cp = res, ep = res + XO_SMBUFSZ - 1; *str && cp < ep; cp++, str++) { + if (*str == '\n') { + *cp++ = '\\'; + *cp = 'n'; + } else if (*str == '\r') { + *cp++ = '\\'; + *cp = 'r'; + } else if (*str == '\"') { + *cp++ = '\\'; + *cp = '"'; + } else + *cp = *str; + } + + *cp = '\0'; + return res; } static int @@ -421,11 +562,12 @@ xo_depth_check (xo_handle_t *xop, int depth) xo_stack_t *xsp; if (depth >= xop->xo_stack_size) { - depth += 16; + depth += XO_DEPTH; /* Extra room */ + xsp = xo_realloc(xop->xo_stack, sizeof(xop->xo_stack[0]) * depth); if (xsp == NULL) { xo_failure(xop, "xo_depth_check: out of memory (%d)", depth); - return 0; + return -1; } int count = depth - xop->xo_stack_size; @@ -480,14 +622,14 @@ xo_init_handle (xo_handle_t *xop) xop->xo_flush = xo_flush_file; if (xo_is_line_buffered(stdout)) - xop->xo_flags |= XOF_FLUSH_LINE; + XOF_SET(xop, XOF_FLUSH_LINE); /* * We only want to do color output on terminals, but we only want * to do this if the user has asked for color. */ - if ((xop->xo_flags & XOF_COLOR_ALLOWED) && isatty(1)) - xop->xo_flags |= XOF_COLOR; + if (XOF_ISSET(xop, XOF_COLOR_ALLOWED) && isatty(1)) + XOF_SET(xop, XOF_COLOR); /* * We need to initialize the locale, which isn't really pretty. @@ -504,7 +646,7 @@ xo_init_handle (xo_handle_t *xop) if (cp == NULL) cp = getenv("LC_ALL"); if (cp == NULL) - cp = "UTF-8"; /* Optimistic? */ + cp = "C"; /* Default for C programs */ (void) setlocale(LC_CTYPE, cp); } @@ -515,16 +657,23 @@ xo_init_handle (xo_handle_t *xop) xo_buf_init(&xop->xo_data); xo_buf_init(&xop->xo_fmt); + if (XOIF_ISSET(xop, XOIF_INIT_IN_PROGRESS)) + return; + XOIF_SET(xop, XOIF_INIT_IN_PROGRESS); + xop->xo_indent_by = XO_INDENT_BY; xo_depth_check(xop, XO_DEPTH); #if !defined(NO_LIBXO_OPTIONS) - if (!(xop->xo_flags & XOF_NO_ENV)) { + if (!XOF_ISSET(xop, XOF_NO_ENV)) { char *env = getenv("LIBXO_OPTIONS"); if (env) xo_set_options(xop, env); + } #endif /* NO_GETENV */ + + XOIF_CLEAR(xop, XOIF_INIT_IN_PROGRESS); } /* @@ -540,34 +689,6 @@ xo_default_init (void) xo_default_inited = 1; } -/* - * Does the buffer have room for the given number of bytes of data? - * If not, realloc the buffer to make room. If that fails, we - * return 0 to tell the caller they are in trouble. - */ -static int -xo_buf_has_room (xo_buffer_t *xbp, int len) -{ - if (xbp->xb_curp + len >= xbp->xb_bufp + xbp->xb_size) { - int sz = xbp->xb_size + XO_BUFSIZ; - char *bp = xo_realloc(xbp->xb_bufp, sz); - if (bp == NULL) { - /* - * XXX If we wanted to put a stick XOF_ENOMEM on xop, - * this would be the place to do it. But we'd need - * to churn the code to pass xop in here.... - */ - return 0; - } - - xbp->xb_curp = bp + (xbp->xb_curp - xbp->xb_bufp); - xbp->xb_bufp = bp; - xbp->xb_size = sz; - } - - return 1; -} - /* * Cheap convenience function to return either the argument, or * the internal handle, after it has been initialized. The usage @@ -597,9 +718,9 @@ xo_indent (xo_handle_t *xop) xop = xo_default(xop); - if (xop->xo_flags & XOF_PRETTY) { + if (XOF_ISSET(xop, XOF_PRETTY)) { rc = xop->xo_indent * xop->xo_indent_by; - if (xop->xo_flags & XOF_TOP_EMITTED) + if (XOIF_ISSET(xop, XOIF_TOP_EMITTED)) rc += xop->xo_indent_by; } @@ -627,12 +748,13 @@ static char xo_xml_gt[] = ">"; static char xo_xml_quot[] = """; static int -xo_escape_xml (xo_buffer_t *xbp, int len, int attr) +xo_escape_xml (xo_buffer_t *xbp, int len, xo_xff_flags_t flags) { int slen; unsigned delta = 0; char *cp, *ep, *ip; const char *sp; + int attr = (flags & XFF_ATTR); for (cp = xbp->xb_curp, ep = cp + len; cp < ep; cp++) { /* We're subtracting 2: 1 for the NUL, 1 for the char we replace */ @@ -682,7 +804,7 @@ xo_escape_xml (xo_buffer_t *xbp, int len, int attr) } static int -xo_escape_json (xo_buffer_t *xbp, int len) +xo_escape_json (xo_buffer_t *xbp, int len, xo_xff_flags_t flags UNUSED) { unsigned delta = 0; char *cp, *ep, *ip; @@ -726,31 +848,43 @@ xo_escape_json (xo_buffer_t *xbp, int len) } /* - * Append the given string to the given buffer + * PARAM-VALUE = UTF-8-STRING ; characters '"', '\' and + * ; ']' MUST be escaped. */ -static void -xo_buf_append (xo_buffer_t *xbp, const char *str, int len) +static int +xo_escape_sdparams (xo_buffer_t *xbp, int len, xo_xff_flags_t flags UNUSED) { - if (!xo_buf_has_room(xbp, len)) - return; + unsigned delta = 0; + char *cp, *ep, *ip; - memcpy(xbp->xb_curp, str, len); - xbp->xb_curp += len; -} + for (cp = xbp->xb_curp, ep = cp + len; cp < ep; cp++) { + if (*cp == '\\' || *cp == '"' || *cp == ']') + delta += 1; + } -/* - * Append the given NUL-terminated string to the given buffer - */ -static void -xo_buf_append_str (xo_buffer_t *xbp, const char *str) -{ - int len = strlen(str); + if (delta == 0) /* Nothing to escape; bail */ + return len; - if (!xo_buf_has_room(xbp, len)) - return; + if (!xo_buf_has_room(xbp, delta)) /* No room; bail, but don't append */ + return 0; - memcpy(xbp->xb_curp, str, len); - xbp->xb_curp += len; + ep = xbp->xb_curp; + cp = ep + len; + ip = cp + delta; + do { + cp -= 1; + ip -= 1; + + if (*cp == '\\' || *cp == '"' || *cp == ']') { + *ip-- = *cp; + *ip = '\\'; + } else { + *ip = *cp; + } + + } while (cp > ep && cp != ip); + + return len + delta; } static void @@ -765,11 +899,15 @@ xo_buf_escape (xo_handle_t *xop, xo_buffer_t *xbp, switch (xo_style(xop)) { case XO_STYLE_XML: case XO_STYLE_HTML: - len = xo_escape_xml(xbp, len, (flags & XFF_ATTR)); + len = xo_escape_xml(xbp, len, flags); break; case XO_STYLE_JSON: - len = xo_escape_json(xbp, len); + len = xo_escape_json(xbp, len, flags); + break; + + case XO_STYLE_SDPARAMS: + len = xo_escape_sdparams(xbp, len, flags); break; } @@ -789,12 +927,13 @@ xo_write (xo_handle_t *xop) if (xbp->xb_curp != xbp->xb_bufp) { xo_buf_append(xbp, "", 1); /* Append ending NUL */ xo_anchor_clear(xop); - rc = xop->xo_write(xop->xo_opaque, xbp->xb_bufp); + if (xop->xo_write) + rc = xop->xo_write(xop->xo_opaque, xbp->xb_bufp); xbp->xb_curp = xbp->xb_bufp; } /* Turn off the flags that don't survive across writes */ - xop->xo_flags &= ~(XOF_UNITS_PENDING); + XOIF_CLEAR(xop, XOIF_UNITS_PENDING); return rc; } @@ -860,7 +999,7 @@ xo_printf_v (xo_handle_t *xop, const char *fmt, va_list vap) rc = vsnprintf(xbp->xb_curp, left, fmt, va_local); - if (rc > xbp->xb_size) { + if (rc >= left) { if (!xo_buf_has_room(xbp, rc)) { va_end(va_local); return -1; @@ -1045,7 +1184,7 @@ xo_buf_append_locale_from_utf8 (xo_handle_t *xop, xo_buffer_t *xbp, return 0; } - if (xop->xo_flags & XOF_NO_LOCALE) { + if (XOF_ISSET(xop, XOF_NO_LOCALE)) { if (!xo_buf_has_room(xbp, ilen)) return 0; @@ -1067,7 +1206,7 @@ xo_buf_append_locale_from_utf8 (xo_handle_t *xop, xo_buffer_t *xbp, xbp->xb_curp += len; } - return wcwidth(wc); + return xo_wcwidth(wc); } static void @@ -1111,9 +1250,9 @@ xo_buf_append_locale (xo_handle_t *xop, xo_buffer_t *xbp, } /* Update column values */ - if (xop->xo_flags & XOF_COLUMNS) + if (XOF_ISSET(xop, XOF_COLUMNS)) xop->xo_columns += cols; - if (xop->xo_flags & XOF_ANCHOR) + if (XOIF_ISSET(xop, XOIF_ANCHOR)) xop->xo_anchor_columns += cols; /* Before we fall into the basic logic below, we need reset len */ @@ -1123,7 +1262,9 @@ xo_buf_append_locale (xo_handle_t *xop, xo_buffer_t *xbp, } /* - * Append the given string to the given buffer + * Append the given string to the given buffer, without escaping or + * character set conversion. This is the straight copy to the data + * buffer with no fanciness. */ static void xo_data_append (xo_handle_t *xop, const char *str, int len) @@ -1150,7 +1291,7 @@ xo_warn_hcv (xo_handle_t *xop, int code, int check_warn, const char *fmt, va_list vap) { xop = xo_default(xop); - if (check_warn && !(xop->xo_flags & XOF_WARN)) + if (check_warn && !XOF_ISSET(xop, XOF_WARN)) return; if (fmt == NULL) @@ -1168,7 +1309,7 @@ xo_warn_hcv (xo_handle_t *xop, int code, int check_warn, memcpy(newfmt + plen, fmt, len); newfmt[len + plen] = '\0'; - if (xop->xo_flags & XOF_WARN_XML) { + if (XOF_ISSET(xop, XOF_WARN_XML)) { static char err_open[] = ""; static char err_close[] = ""; static char msg_open[] = ""; @@ -1184,7 +1325,7 @@ xo_warn_hcv (xo_handle_t *xop, int code, int check_warn, int left = xbp->xb_size - (xbp->xb_curp - xbp->xb_bufp); int rc = vsnprintf(xbp->xb_curp, left, newfmt, vap); - if (rc > xbp->xb_size) { + if (rc >= left) { if (!xo_buf_has_room(xbp, rc)) { va_end(va_local); return; @@ -1212,7 +1353,7 @@ xo_warn_hcv (xo_handle_t *xop, int code, int check_warn, } } - xo_buf_append(xbp, "\n", 2); /* Append newline and NUL to string */ + xo_buf_append(xbp, "\n", 1); /* Append newline and NUL to string */ (void) xo_write(xop); } else { @@ -1328,7 +1469,7 @@ xo_message_hcv (xo_handle_t *xop, int code, const char *fmt, va_list vap) switch (xo_style(xop)) { case XO_STYLE_XML: xbp = &xop->xo_data; - if (xop->xo_flags & XOF_PRETTY) + if (XOF_ISSET(xop, XOF_PRETTY)) xo_buf_indent(xop, xop->xo_indent_by); xo_buf_append(xbp, msg_open, sizeof(msg_open) - 1); @@ -1336,7 +1477,7 @@ xo_message_hcv (xo_handle_t *xop, int code, const char *fmt, va_list vap) int left = xbp->xb_size - (xbp->xb_curp - xbp->xb_bufp); rc = vsnprintf(xbp->xb_curp, left, fmt, vap); - if (rc > xbp->xb_size) { + if (rc >= left) { if (!xo_buf_has_room(xbp, rc)) { va_end(va_local); return; @@ -1350,7 +1491,7 @@ xo_message_hcv (xo_handle_t *xop, int code, const char *fmt, va_list vap) } va_end(va_local); - rc = xo_escape_xml(xbp, rc, 1); + rc = xo_escape_xml(xbp, rc, 0); xbp->xb_curp += rc; if (need_nl && code > 0) { @@ -1361,9 +1502,14 @@ xo_message_hcv (xo_handle_t *xop, int code, const char *fmt, va_list vap) } } - xo_buf_append(xbp, msg_close, sizeof(msg_close) - 1); if (need_nl) - xo_buf_append(xbp, "\n", 2); /* Append newline and NUL to string */ + xo_buf_append(xbp, "\n", 1); /* Append newline and NUL to string */ + + xo_buf_append(xbp, msg_close, sizeof(msg_close) - 1); + + if (XOF_ISSET(xop, XOF_PRETTY)) + xo_buf_append(xbp, "\n", 1); /* Append newline and NUL to string */ + (void) xo_write(xop); break; @@ -1399,8 +1545,10 @@ xo_message_hcv (xo_handle_t *xop, int code, const char *fmt, va_list vap) break; case XO_STYLE_JSON: - /* No meanings of representing messages in JSON */ - break; + case XO_STYLE_SDPARAMS: + case XO_STYLE_ENCODER: + /* No means of representing messages */ + return; case XO_STYLE_TEXT: rc = xo_printf_v(xop, fmt, vap); @@ -1408,9 +1556,9 @@ xo_message_hcv (xo_handle_t *xop, int code, const char *fmt, va_list vap) * XXX need to handle UTF-8 widths */ if (rc > 0) { - if (xop->xo_flags & XOF_COLUMNS) + if (XOF_ISSET(xop, XOF_COLUMNS)) xop->xo_columns += rc; - if (xop->xo_flags & XOF_ANCHOR) + if (XOIF_ISSET(xop, XOIF_ANCHOR)) xop->xo_anchor_columns += rc; } @@ -1450,7 +1598,7 @@ xo_message_c (int code, const char *fmt, ...) } void -xo_message (const char *fmt, ...) +xo_message_e (const char *fmt, ...) { int code = errno; va_list vap; @@ -1460,10 +1608,20 @@ xo_message (const char *fmt, ...) va_end(vap); } +void +xo_message (const char *fmt, ...) +{ + va_list vap; + + va_start(vap, fmt); + xo_message_hcv(NULL, 0, fmt, vap); + va_end(vap); +} + static void xo_failure (xo_handle_t *xop, const char *fmt, ...) { - if (!(xop->xo_flags & XOF_WARN)) + if (!XOF_ISSET(xop, XOF_WARN)) return; va_list vap; @@ -1490,9 +1648,10 @@ xo_create (xo_style_t style, xo_xof_flags_t flags) if (xop) { bzero(xop, sizeof(*xop)); - xop->xo_style = style; - xop->xo_flags = flags; + xop->xo_style = style; + XOF_SET(xop, flags); xo_init_handle(xop); + xop->xo_style = style; /* Reset style (see LIBXO_OPTIONS) */ } return xop; @@ -1529,7 +1688,9 @@ xo_destroy (xo_handle_t *xop_arg) { xo_handle_t *xop = xo_default(xop_arg); - if (xop->xo_close && (xop->xo_flags & XOF_CLOSE_FP)) + xo_flush_h(xop); + + if (xop->xo_close && XOF_ISSET(xop, XOF_CLOSE_FP)) xop->xo_close(xop->xo_opaque); xo_free(xop->xo_stack); @@ -1577,14 +1738,103 @@ xo_name_to_style (const char *name) return XO_STYLE_XML; else if (strcmp(name, "json") == 0) return XO_STYLE_JSON; + else if (strcmp(name, "encoder") == 0) + return XO_STYLE_ENCODER; else if (strcmp(name, "text") == 0) return XO_STYLE_TEXT; else if (strcmp(name, "html") == 0) return XO_STYLE_HTML; + else if (strcmp(name, "sdparams") == 0) + return XO_STYLE_SDPARAMS; return -1; } +/* + * Indicate if the style is an "encoding" one as opposed to a "display" one. + */ +static int +xo_style_is_encoding (xo_handle_t *xop) +{ + if (xo_style(xop) == XO_STYLE_JSON + || xo_style(xop) == XO_STYLE_XML + || xo_style(xop) == XO_STYLE_SDPARAMS + || xo_style(xop) == XO_STYLE_ENCODER) + return 1; + return 0; +} + +/* Simple name-value mapping */ +typedef struct xo_mapping_s { + xo_xff_flags_t xm_value; + const char *xm_name; +} xo_mapping_t; + +static xo_xff_flags_t +xo_name_lookup (xo_mapping_t *map, const char *value, int len) +{ + if (len == 0) + return 0; + + if (len < 0) + len = strlen(value); + + while (isspace((int) *value)) { + value += 1; + len -= 1; + } + + while (isspace((int) value[len])) + len -= 1; + + if (*value == '\0') + return 0; + + for ( ; map->xm_name; map++) + if (strncmp(map->xm_name, value, len) == 0) + return map->xm_value; + + return 0; +} + +#ifdef NOT_NEEDED_YET +static const char * +xo_value_lookup (xo_mapping_t *map, xo_xff_flags_t value) +{ + if (value == 0) + return NULL; + + for ( ; map->xm_name; map++) + if (map->xm_value == value) + return map->xm_name; + + return NULL; +} +#endif /* NOT_NEEDED_YET */ + +static xo_mapping_t xo_xof_names[] = { + { XOF_COLOR_ALLOWED, "color" }, + { XOF_COLUMNS, "columns" }, + { XOF_DTRT, "dtrt" }, + { XOF_FLUSH, "flush" }, + { XOF_IGNORE_CLOSE, "ignore-close" }, + { XOF_INFO, "info" }, + { XOF_KEYS, "keys" }, + { XOF_LOG_GETTEXT, "log-gettext" }, + { XOF_LOG_SYSLOG, "log-syslog" }, + { XOF_NO_HUMANIZE, "no-humanize" }, + { XOF_NO_LOCALE, "no-locale" }, + { XOF_NO_TOP, "no-top" }, + { XOF_NOT_FIRST, "not-first" }, + { XOF_PRETTY, "pretty" }, + { XOF_UNDERSCORES, "underscores" }, + { XOF_UNITS, "units" }, + { XOF_WARN, "warn" }, + { XOF_WARN_XML, "warn-xml" }, + { XOF_XPATH, "xpath" }, + { 0, NULL } +}; + /* * Convert string name to XOF_* flag value. * Not all are useful. Or safe. Or sane. @@ -1592,40 +1842,7 @@ xo_name_to_style (const char *name) static unsigned xo_name_to_flag (const char *name) { - if (strcmp(name, "pretty") == 0) - return XOF_PRETTY; - if (strcmp(name, "warn") == 0) - return XOF_WARN; - if (strcmp(name, "xpath") == 0) - return XOF_XPATH; - if (strcmp(name, "info") == 0) - return XOF_INFO; - if (strcmp(name, "warn-xml") == 0) - return XOF_WARN_XML; - if (strcmp(name, "color") == 0) - return XOF_COLOR_ALLOWED; - if (strcmp(name, "columns") == 0) - return XOF_COLUMNS; - if (strcmp(name, "dtrt") == 0) - return XOF_DTRT; - if (strcmp(name, "flush") == 0) - return XOF_FLUSH; - if (strcmp(name, "keys") == 0) - return XOF_KEYS; - if (strcmp(name, "ignore-close") == 0) - return XOF_IGNORE_CLOSE; - if (strcmp(name, "not-first") == 0) - return XOF_NOT_FIRST; - if (strcmp(name, "no-locale") == 0) - return XOF_NO_LOCALE; - if (strcmp(name, "no-top") == 0) - return XOF_NO_TOP; - if (strcmp(name, "units") == 0) - return XOF_UNITS; - if (strcmp(name, "underscores") == 0) - return XOF_UNDERSCORES; - - return 0; + return (unsigned) xo_name_lookup(xo_xof_names, name, -1); } int @@ -1661,7 +1878,7 @@ xo_set_options (xo_handle_t *xop, const char *input) #ifdef LIBXO_COLOR_ON_BY_DEFAULT /* If the installer used --enable-color-on-by-default, then we allow it */ - xop->xo_flags |= XOF_COLOR_ALLOWED; + XOF_SET(xop, XOF_COLOR_ALLOWED); #endif /* LIBXO_COLOR_ON_BY_DEFAULT */ /* @@ -1675,15 +1892,19 @@ xo_set_options (xo_handle_t *xop, const char *input) for (input++ ; *input; input++) { switch (*input) { case 'c': - xop->xo_flags |= XOF_COLOR_ALLOWED; + XOF_SET(xop, XOF_COLOR_ALLOWED); break; case 'f': - xop->xo_flags |= XOF_FLUSH; + XOF_SET(xop, XOF_FLUSH); break; case 'F': - xop->xo_flags |= XOF_FLUSH_LINE; + XOF_SET(xop, XOF_FLUSH_LINE); + break; + + case 'g': + XOF_SET(xop, XOF_LOG_GETTEXT); break; case 'H': @@ -1691,7 +1912,7 @@ xo_set_options (xo_handle_t *xop, const char *input) break; case 'I': - xop->xo_flags |= XOF_INFO; + XOF_SET(xop, XOF_INFO); break; case 'i': @@ -1702,16 +1923,20 @@ xo_set_options (xo_handle_t *xop, const char *input) } break; - case 'k': - xop->xo_flags |= XOF_KEYS; - break; - case 'J': xop->xo_style = XO_STYLE_JSON; break; + case 'k': + XOF_SET(xop, XOF_KEYS); + break; + + case 'n': + XOF_SET(xop, XOF_NO_HUMANIZE); + break; + case 'P': - xop->xo_flags |= XOF_PRETTY; + XOF_SET(xop, XOF_PRETTY); break; case 'T': @@ -1719,15 +1944,15 @@ xo_set_options (xo_handle_t *xop, const char *input) break; case 'U': - xop->xo_flags |= XOF_UNITS; + XOF_SET(xop, XOF_UNITS); break; case 'u': - xop->xo_flags |= XOF_UNDERSCORES; + XOF_SET(xop, XOF_UNDERSCORES); break; case 'W': - xop->xo_flags |= XOF_WARN; + XOF_SET(xop, XOF_WARN); break; case 'X': @@ -1735,7 +1960,7 @@ xo_set_options (xo_handle_t *xop, const char *input) break; case 'x': - xop->xo_flags |= XOF_XPATH; + XOF_SET(xop, XOF_XPATH); break; } } @@ -1760,8 +1985,12 @@ xo_set_options (xo_handle_t *xop, const char *input) continue; } + /* + * For options, we don't allow "encoder" since we want to + * handle it explicitly below as "encoder=xxx". + */ new_style = xo_name_to_style(cp); - if (new_style >= 0) { + if (new_style >= 0 && new_style != XO_STYLE_ENCODER) { if (style >= 0) xo_warnx("ignoring multiple styles: '%s'", cp); else @@ -1769,14 +1998,27 @@ xo_set_options (xo_handle_t *xop, const char *input) } else { new_flag = xo_name_to_flag(cp); if (new_flag != 0) - xop->xo_flags |= new_flag; + XOF_SET(xop, new_flag); else { if (strcmp(cp, "no-color") == 0) { - xop->xo_flags &= ~XOF_COLOR_ALLOWED; + XOF_CLEAR(xop, XOF_COLOR_ALLOWED); } else if (strcmp(cp, "indent") == 0) { - xop->xo_indent_by = atoi(vp); + if (vp) + xop->xo_indent_by = atoi(vp); + else + xo_failure(xop, "missing value for indent option"); + } else if (strcmp(cp, "encoder") == 0) { + if (vp == NULL) + xo_failure(xop, "missing value for encoder option"); + else { + if (xo_encoder_init(xop, vp)) { + xo_failure(xop, "encoder not found: %s", vp); + rc = -1; + } + } + } else { - xo_warnx("unknown option: '%s'", cp); + xo_warnx("unknown libxo option value: '%s'", cp); rc = -1; } } @@ -1801,7 +2043,7 @@ xo_set_flags (xo_handle_t *xop, xo_xof_flags_t flags) { xop = xo_default(xop); - xop->xo_flags |= flags; + XOF_SET(xop, flags); } xo_xof_flags_t @@ -1812,6 +2054,24 @@ xo_get_flags (xo_handle_t *xop) return xop->xo_flags; } +/* + * strndup with a twist: len < 0 means strlen + */ +static char * +xo_strndup (const char *str, int len) +{ + if (len < 0) + len = strlen(str); + + char *cp = xo_realloc(NULL, len + 1); + if (cp) { + memcpy(cp, str, len); + cp[len] = '\0'; + } + + return cp; +} + /** * Record a leading prefix for the XPath we generate. This allows the * generated data to be placed within an XML hierarchy but still have @@ -1833,11 +2093,7 @@ xo_set_leading_xpath (xo_handle_t *xop, const char *path) if (path == NULL) return; - int len = strlen(path); - xop->xo_leading_xpath = xo_realloc(NULL, len + 1); - if (xop->xo_leading_xpath) { - memcpy(xop->xo_leading_xpath, path, len + 1); - } + xop->xo_leading_xpath = xo_strndup(path, -1); } /** @@ -1890,7 +2146,7 @@ xo_clear_flags (xo_handle_t *xop, xo_xof_flags_t flags) { xop = xo_default(xop); - xop->xo_flags &= ~flags; + XOF_CLEAR(xop, flags); } static const char * @@ -1926,19 +2182,19 @@ xo_line_ensure_open (xo_handle_t *xop, xo_xff_flags_t flags UNUSED) static char div_open[] = "
"; static char div_open_blank[] = "
"; - if (xop->xo_flags & XOF_DIV_OPEN) + if (XOIF_ISSET(xop, XOIF_DIV_OPEN)) return; if (xo_style(xop) != XO_STYLE_HTML) return; - xop->xo_flags |= XOF_DIV_OPEN; + XOIF_SET(xop, XOIF_DIV_OPEN); if (flags & XFF_BLANK_LINE) xo_data_append(xop, div_open_blank, sizeof(div_open_blank) - 1); else xo_data_append(xop, div_open, sizeof(div_open) - 1); - if (xop->xo_flags & XOF_PRETTY) + if (XOF_ISSET(xop, XOF_PRETTY)) xo_data_append(xop, "\n", 1); } @@ -1949,13 +2205,13 @@ xo_line_close (xo_handle_t *xop) switch (xo_style(xop)) { case XO_STYLE_HTML: - if (!(xop->xo_flags & XOF_DIV_OPEN)) + if (!XOIF_ISSET(xop, XOIF_DIV_OPEN)) xo_line_ensure_open(xop, 0); - xop->xo_flags &= ~XOF_DIV_OPEN; + XOIF_CLEAR(xop, XOIF_DIV_OPEN); xo_data_append(xop, div_close, sizeof(div_close) - 1); - if (xop->xo_flags & XOF_PRETTY) + if (XOF_ISSET(xop, XOF_PRETTY)) xo_data_append(xop, "\n", 1); break; @@ -2055,7 +2311,7 @@ xo_format_string_direct (xo_handle_t *xop, xo_buffer_t *xbp, ilen = xo_utf8_to_wc_len(cp); if (ilen < 0) { xo_failure(xop, "invalid UTF-8 character: %02hhx", *cp); - return -1; + return -1; /* Can't continue; we can't find the end */ } if (len > 0 && len < ilen) { @@ -2067,7 +2323,7 @@ xo_format_string_direct (xo_handle_t *xop, xo_buffer_t *xbp, if (wc == (wchar_t) -1) { xo_failure(xop, "invalid UTF-8 character: %02hhx/%d", *cp, ilen); - return -1; + return -1; /* Can't continue; we can't find the end */ } cp += ilen; break; @@ -2080,6 +2336,7 @@ xo_format_string_direct (xo_handle_t *xop, xo_buffer_t *xbp, wc = L'?'; ilen = 1; } + if (ilen == 0) { /* Hit a wide NUL character */ len = 0; continue; @@ -2101,7 +2358,7 @@ xo_format_string_direct (xo_handle_t *xop, xo_buffer_t *xbp, * in wide characters, since we lack a mbswidth() function. If * it doesn't fit */ - width = wcwidth(wc); + width = xo_wcwidth(wc); if (width < 0) width = iswcntrl(wc) ? 0 : 1; @@ -2152,6 +2409,18 @@ xo_format_string_direct (xo_handle_t *xop, xo_buffer_t *xbp, *xbp->xb_curp++ = wc; goto done_with_encoding; + + case XO_STYLE_SDPARAMS: + if (wc != '\\' && wc != '"' && wc != ']') + break; + + if (!xo_buf_has_room(xbp, 2)) + return -1; + + *xbp->xb_curp++ = '\\'; + wc = wc & 0x7f; + *xbp->xb_curp++ = wc; + goto done_with_encoding; } olen = xo_utf8_emit_len(wc); @@ -2175,7 +2444,6 @@ xo_format_string_direct (xo_handle_t *xop, xo_buffer_t *xbp, if (olen <= 0) { xo_failure(xop, "could not convert wide char: %lx", (unsigned long) wc); - olen = 1; width = 1; *xbp->xb_curp++ = '?'; } else @@ -2190,25 +2458,43 @@ xo_format_string_direct (xo_handle_t *xop, xo_buffer_t *xbp, return cols; } +static int +xo_needed_encoding (xo_handle_t *xop) +{ + if (XOF_ISSET(xop, XOF_UTF8)) /* Check the override flag */ + return XF_ENC_UTF8; + + if (xo_style(xop) == XO_STYLE_TEXT) /* Text means locale */ + return XF_ENC_LOCALE; + + return XF_ENC_UTF8; /* Otherwise, we love UTF-8 */ +} + static int xo_format_string (xo_handle_t *xop, xo_buffer_t *xbp, xo_xff_flags_t flags, xo_format_t *xfp) { static char null[] = "(null)"; + static char null_no_quotes[] = "null"; char *cp = NULL; wchar_t *wcp = NULL; int len, cols = 0, rc = 0; int off = xbp->xb_curp - xbp->xb_bufp, off2; - int need_enc = (xo_style(xop) == XO_STYLE_TEXT) - ? XF_ENC_LOCALE : XF_ENC_UTF8; + int need_enc = xo_needed_encoding(xop); if (xo_check_conversion(xop, xfp->xf_enc, need_enc)) return 0; len = xfp->xf_width[XF_WIDTH_SIZE]; - if (xfp->xf_enc == XF_ENC_WIDE) { + if (xfp->xf_fc == 'm') { + cp = strerror(xop->xo_errno); + if (len < 0) + len = cp ? strlen(cp) : 0; + goto normal_string; + + } else if (xfp->xf_enc == XF_ENC_WIDE) { wcp = va_arg(xop->xo_vap, wchar_t *); if (xfp->xf_skip) return 0; @@ -2224,13 +2510,20 @@ xo_format_string (xo_handle_t *xop, xo_buffer_t *xbp, xo_xff_flags_t flags, } else { cp = va_arg(xop->xo_vap, char *); /* UTF-8 or native */ + + normal_string: if (xfp->xf_skip) return 0; /* Echo "Dont' deref NULL" logic */ if (cp == NULL) { - cp = null; - len = sizeof(null) - 1; + if ((flags & XFF_NOQUOTE) && xo_style_is_encoding(xop)) { + cp = null_no_quotes; + len = sizeof(null_no_quotes) - 1; + } else { + cp = null; + len = sizeof(null) - 1; + } } /* @@ -2241,7 +2534,8 @@ xo_format_string (xo_handle_t *xop, xo_buffer_t *xbp, xo_xff_flags_t flags, && xfp->xf_width[XF_WIDTH_MIN] < 0 && xfp->xf_width[XF_WIDTH_SIZE] < 0 && xfp->xf_width[XF_WIDTH_MAX] < 0 - && !(xop->xo_flags & (XOF_ANCHOR | XOF_COLUMNS))) { + && !(XOIF_ISSET(xop, XOIF_ANCHOR) + || XOF_ISSET(xop, XOF_COLUMNS))) { len = strlen(cp); xo_buf_escape(xop, xbp, cp, len, flags); @@ -2298,9 +2592,9 @@ xo_format_string (xo_handle_t *xop, xo_buffer_t *xbp, xo_xff_flags_t flags, cols += delta; } - if (xop->xo_flags & XOF_COLUMNS) + if (XOF_ISSET(xop, XOF_COLUMNS)) xop->xo_columns += cols; - if (xop->xo_flags & XOF_ANCHOR) + if (XOIF_ISSET(xop, XOIF_ANCHOR)) xop->xo_anchor_columns += cols; return rc; @@ -2310,20 +2604,227 @@ xo_format_string (xo_handle_t *xop, xo_buffer_t *xbp, xo_xff_flags_t flags, return 0; } +/* + * Look backwards in a buffer to find a numeric value + */ +static int +xo_buf_find_last_number (xo_buffer_t *xbp, int start_offset) +{ + int rc = 0; /* Fail with zero */ + int digit = 1; + char *sp = xbp->xb_bufp; + char *cp = sp + start_offset; + + while (--cp >= sp) + if (isdigit((int) *cp)) + break; + + for ( ; cp >= sp; cp--) { + if (!isdigit((int) *cp)) + break; + rc += (*cp - '0') * digit; + digit *= 10; + } + + return rc; +} + +static int +xo_count_utf8_cols (const char *str, int len) +{ + int tlen; + wchar_t wc; + int cols = 0; + const char *ep = str + len; + + while (str < ep) { + tlen = xo_utf8_to_wc_len(str); + if (tlen < 0) /* Broken input is very bad */ + return cols; + + wc = xo_utf8_char(str, tlen); + if (wc == (wchar_t) -1) + return cols; + + /* We only print printable characters */ + if (iswprint((wint_t) wc)) { + /* + * Find the width-in-columns of this character, which must be done + * in wide characters, since we lack a mbswidth() function. + */ + int width = xo_wcwidth(wc); + if (width < 0) + width = iswcntrl(wc) ? 0 : 1; + + cols += width; + } + + str += tlen; + } + + return cols; +} + +#ifdef HAVE_GETTEXT +static inline const char * +xo_dgettext (xo_handle_t *xop, const char *str) +{ + const char *domainname = xop->xo_gt_domain; + const char *res; + + res = dgettext(domainname, str); + + if (XOF_ISSET(xop, XOF_LOG_GETTEXT)) + fprintf(stderr, "xo: gettext: %s%s%smsgid \"%s\" returns \"%s\"\n", + domainname ? "domain \"" : "", xo_printable(domainname), + domainname ? "\", " : "", xo_printable(str), xo_printable(res)); + + return res; +} + +static inline const char * +xo_dngettext (xo_handle_t *xop, const char *sing, const char *plural, + unsigned long int n) +{ + const char *domainname = xop->xo_gt_domain; + const char *res; + + res = dngettext(domainname, sing, plural, n); + if (XOF_ISSET(xop, XOF_LOG_GETTEXT)) + fprintf(stderr, "xo: gettext: %s%s%s" + "msgid \"%s\", msgid_plural \"%s\" (%lu) returns \"%s\"\n", + domainname ? "domain \"" : "", + xo_printable(domainname), domainname ? "\", " : "", + xo_printable(sing), + xo_printable(plural), n, xo_printable(res)); + + return res; +} +#else /* HAVE_GETTEXT */ +static inline const char * +xo_dgettext (xo_handle_t *xop UNUSED, const char *str) +{ + return str; +} + +static inline const char * +xo_dngettext (xo_handle_t *xop UNUSED, const char *singular, + const char *plural, unsigned long int n) +{ + return (n == 1) ? singular : plural; +} +#endif /* HAVE_GETTEXT */ + +/* + * This is really _re_formatting, since the normal format code has + * generated a beautiful string into xo_data, starting at + * start_offset. We need to see if it's plural, which means + * comma-separated options, or singular. Then we make the appropriate + * call to d[n]gettext() to get the locale-based version. Note that + * both input and output of gettext() this should be UTF-8. + */ +static int +xo_format_gettext (xo_handle_t *xop, xo_xff_flags_t flags, + int start_offset, int cols, int need_enc) +{ + xo_buffer_t *xbp = &xop->xo_data; + + if (!xo_buf_has_room(xbp, 1)) + return cols; + + xbp->xb_curp[0] = '\0'; /* NUL-terminate the input string */ + + char *cp = xbp->xb_bufp + start_offset; + int len = xbp->xb_curp - cp; + const char *newstr = NULL; + + /* + * The plural flag asks us to look backwards at the last numeric + * value rendered and disect the string into two pieces. + */ + if (flags & XFF_GT_PLURAL) { + int n = xo_buf_find_last_number(xbp, start_offset); + char *two = memchr(cp, (int) ',', len); + if (two == NULL) { + xo_failure(xop, "no comma in plural gettext field: '%s'", cp); + return cols; + } + + if (two == cp) { + xo_failure(xop, "nothing before comma in plural gettext " + "field: '%s'", cp); + return cols; + } + + if (two == xbp->xb_curp) { + xo_failure(xop, "nothing after comma in plural gettext " + "field: '%s'", cp); + return cols; + } + + *two++ = '\0'; + if (flags & XFF_GT_FIELD) { + newstr = xo_dngettext(xop, cp, two, n); + } else { + /* Don't do a gettext() look up, just get the plural form */ + newstr = (n == 1) ? cp : two; + } + + /* + * If we returned the first string, optimize a bit by + * backing up over comma + */ + if (newstr == cp) { + xbp->xb_curp = two - 1; /* One for comma */ + /* + * If the caller wanted UTF8, we're done; nothing changed, + * but we need to count the columns used. + */ + if (need_enc == XF_ENC_UTF8) + return xo_count_utf8_cols(cp, xbp->xb_curp - cp); + } + + } else { + /* The simple case (singular) */ + newstr = xo_dgettext(xop, cp); + + if (newstr == cp) { + /* If the caller wanted UTF8, we're done; nothing changed */ + if (need_enc == XF_ENC_UTF8) + return cols; + } + } + + /* + * Since the new string string might be in gettext's buffer or + * in the buffer (as the plural form), we make a copy. + */ + int nlen = strlen(newstr); + char *newcopy = alloca(nlen + 1); + memcpy(newcopy, newstr, nlen + 1); + + xbp->xb_curp = xbp->xb_bufp + start_offset; /* Reset the buffer */ + return xo_format_string_direct(xop, xbp, flags, NULL, newcopy, nlen, 0, + need_enc, XF_ENC_UTF8); +} + static void -xo_data_append_content (xo_handle_t *xop, const char *str, int len) +xo_data_append_content (xo_handle_t *xop, const char *str, int len, + xo_xff_flags_t flags) { int cols; - int need_enc = (xo_style(xop) == XO_STYLE_TEXT) - ? XF_ENC_LOCALE : XF_ENC_UTF8; + int need_enc = xo_needed_encoding(xop); + int start_offset = xo_buf_offset(&xop->xo_data); - cols = xo_format_string_direct(xop, &xop->xo_data, XFF_UNESCAPE, + cols = xo_format_string_direct(xop, &xop->xo_data, XFF_UNESCAPE | flags, NULL, str, len, -1, need_enc, XF_ENC_UTF8); + if (flags & XFF_GT_FLAGS) + cols = xo_format_gettext(xop, flags, start_offset, cols, need_enc); - if (xop->xo_flags & XOF_COLUMNS) + if (XOF_ISSET(xop, XOF_COLUMNS)) xop->xo_columns += cols; - if (xop->xo_flags & XOF_ANCHOR) + if (XOIF_ISSET(xop, XOIF_ANCHOR)) xop->xo_anchor_columns += cols; } @@ -2368,8 +2869,13 @@ xo_trim_ws (xo_buffer_t *xbp, int len) return len; } +/* + * Interface to format a single field. The arguments are in xo_vap, + * and the format is in 'fmt'. If 'xbp' is null, we use xop->xo_data; + * this is the most common case. + */ static int -xo_format_data (xo_handle_t *xop, xo_buffer_t *xbp, +xo_do_format_field (xo_handle_t *xop, xo_buffer_t *xbp, const char *fmt, int flen, xo_xff_flags_t flags) { xo_format_t xf; @@ -2377,13 +2883,25 @@ xo_format_data (xo_handle_t *xop, xo_buffer_t *xbp, int rc, cols; int style = (flags & XFF_XML) ? XO_STYLE_XML : xo_style(xop); unsigned make_output = !(flags & XFF_NO_OUTPUT); - int need_enc = (xo_style(xop) == XO_STYLE_TEXT) - ? XF_ENC_LOCALE : XF_ENC_UTF8; - + int need_enc = xo_needed_encoding(xop); + int real_need_enc = need_enc; + int old_cols = xop->xo_columns; + + /* The gettext interface is UTF-8, so we'll need that for now */ + if (flags & XFF_GT_FIELD) + need_enc = XF_ENC_UTF8; + if (xbp == NULL) xbp = &xop->xo_data; + unsigned start_offset = xo_buf_offset(xbp); + for (cp = fmt, ep = fmt + flen; cp < ep; cp++) { + /* + * Since we're starting a new field, save the starting offset. + * We'll need this later for field-related operations. + */ + if (*cp != '%') { add_one: if (xp == NULL) @@ -2403,9 +2921,9 @@ xo_format_data (xo_handle_t *xop, xo_buffer_t *xbp, cols = xo_format_string_direct(xop, xbp, flags | XFF_UNESCAPE, NULL, xp, cp - xp, -1, need_enc, XF_ENC_UTF8); - if (xop->xo_flags & XOF_COLUMNS) + if (XOF_ISSET(xop, XOF_COLUMNS)) xop->xo_columns += cols; - if (xop->xo_flags & XOF_ANCHOR) + if (XOIF_ISSET(xop, XOIF_ANCHOR)) xop->xo_anchor_columns += cols; } @@ -2430,18 +2948,18 @@ xo_format_data (xo_handle_t *xop, xo_buffer_t *xbp, * '*' means there's a "%*.*s" value in vap that * we want to ignore */ - if (!(xop->xo_flags & XOF_NO_VA_ARG)) + if (!XOF_ISSET(xop, XOF_NO_VA_ARG)) va_arg(xop->xo_vap, int); } } } /* Hidden fields are only visible to JSON and XML */ - if (xop->xo_flags & XFF_ENCODE_ONLY) { + if (XOF_ISSET(xop, XFF_ENCODE_ONLY)) { if (style != XO_STYLE_XML - && xo_style(xop) != XO_STYLE_JSON) + && !xo_style_is_encoding(xop)) xf.xf_skip = 1; - } else if (xop->xo_flags & XFF_DISPLAY_ONLY) { + } else if (XOF_ISSET(xop, XFF_DISPLAY_ONLY)) { if (style != XO_STYLE_TEXT && xo_style(xop) != XO_STYLE_HTML) xf.xf_skip = 1; @@ -2484,7 +3002,7 @@ xo_format_data (xo_handle_t *xop, xo_buffer_t *xbp, } else if (*cp == '*') { xf.xf_stars += 1; xf.xf_star[xf.xf_dots] = 1; - } else if (strchr("diouxXDOUeEfFgGaAcCsSp", *cp) != NULL) + } else if (strchr("diouxXDOUeEfFgGaAcCsSpm", *cp) != NULL) break; else if (*cp == 'n' || *cp == 'v') { xo_failure(xop, "unsupported format: '%s'", fmt); @@ -2498,7 +3016,7 @@ xo_format_data (xo_handle_t *xop, xo_buffer_t *xbp, xf.xf_fc = *cp; - if (!(xop->xo_flags & XOF_NO_VA_ARG)) { + if (!XOF_ISSET(xop, XOF_NO_VA_ARG)) { if (*cp == 's' || *cp == 'S') { /* Handle "%*.*.*s" */ int s; @@ -2543,14 +3061,16 @@ xo_format_data (xo_handle_t *xop, xo_buffer_t *xbp, * correctly. So we have to handle this ourselves. */ if (xop->xo_formatter == NULL - && (xf.xf_fc == 's' || xf.xf_fc == 'S')) { - xf.xf_enc = (xf.xf_lflag || (xf.xf_fc == 'S')) - ? XF_ENC_WIDE : xf.xf_hflag ? XF_ENC_LOCALE : XF_ENC_UTF8; + && (xf.xf_fc == 's' || xf.xf_fc == 'S' + || xf.xf_fc == 'm')) { + + xf.xf_enc = (xf.xf_fc == 'm') ? XF_ENC_UTF8 + : (xf.xf_lflag || (xf.xf_fc == 'S')) ? XF_ENC_WIDE + : xf.xf_hflag ? XF_ENC_LOCALE : XF_ENC_UTF8; + rc = xo_format_string(xop, xbp, flags, &xf); - if ((flags & XFF_TRIM_WS) - && (xo_style(xop) == XO_STYLE_XML - || xo_style(xop) == XO_STYLE_JSON)) + if ((flags & XFF_TRIM_WS) && xo_style_is_encoding(xop)) rc = xo_trim_ws(xbp, rc); } else { @@ -2572,19 +3092,31 @@ xo_format_data (xo_handle_t *xop, xo_buffer_t *xbp, case XO_STYLE_JSON: if (flags & XFF_TRIM_WS) columns = rc = xo_trim_ws(xbp, rc); - rc = xo_escape_json(xbp, rc); + rc = xo_escape_json(xbp, rc, 0); + break; + + case XO_STYLE_SDPARAMS: + if (flags & XFF_TRIM_WS) + columns = rc = xo_trim_ws(xbp, rc); + rc = xo_escape_sdparams(xbp, rc, 0); + break; + + case XO_STYLE_ENCODER: + if (flags & XFF_TRIM_WS) + columns = rc = xo_trim_ws(xbp, rc); break; } /* - * We can assume all the data we've added is ASCII, so - * the columns and bytes are the same. xo_format_string - * handles all the fancy string conversions and updates - * xo_anchor_columns accordingly. + * We can assume all the non-%s data we've + * added is ASCII, so the columns and bytes are the + * same. xo_format_string handles all the fancy + * string conversions and updates xo_anchor_columns + * accordingly. */ - if (xop->xo_flags & XOF_COLUMNS) + if (XOF_ISSET(xop, XOF_COLUMNS)) xop->xo_columns += columns; - if (xop->xo_flags & XOF_ANCHOR) + if (XOIF_ISSET(xop, XOIF_ANCHOR)) xop->xo_anchor_columns += columns; } @@ -2595,7 +3127,7 @@ xo_format_data (xo_handle_t *xop, xo_buffer_t *xbp, * Now for the tricky part: we need to move the argument pointer * along by the amount needed. */ - if (!(xop->xo_flags & XOF_NO_VA_ARG)) { + if (!XOF_ISSET(xop, XOF_NO_VA_ARG)) { if (xf.xf_fc == 's' ||xf.xf_fc == 'S') { /* @@ -2606,6 +3138,9 @@ xo_format_data (xo_handle_t *xop, xo_buffer_t *xbp, if (xf.xf_skip) va_arg(xop->xo_vap, char *); + } else if (xf.xf_fc == 'm') { + /* Nothing on the stack for "%m" */ + } else { int s; for (s = 0; s < XF_WIDTH_NUM; s++) { @@ -2664,15 +3199,31 @@ xo_format_data (xo_handle_t *xop, xo_buffer_t *xbp, cols = xo_format_string_direct(xop, xbp, flags | XFF_UNESCAPE, NULL, xp, cp - xp, -1, need_enc, XF_ENC_UTF8); - if (xop->xo_flags & XOF_COLUMNS) + + if (XOF_ISSET(xop, XOF_COLUMNS)) xop->xo_columns += cols; - if (xop->xo_flags & XOF_ANCHOR) + if (XOIF_ISSET(xop, XOIF_ANCHOR)) xop->xo_anchor_columns += cols; } xp = NULL; } + if (flags & XFF_GT_FLAGS) { + /* + * Handle gettext()ing the field by looking up the value + * and then copying it in, while converting to locale, if + * needed. + */ + int new_cols = xo_format_gettext(xop, flags, start_offset, + old_cols, real_need_enc); + + if (XOF_ISSET(xop, XOF_COLUMNS)) + xop->xo_columns += new_cols - old_cols; + if (XOIF_ISSET(xop, XOIF_ANCHOR)) + xop->xo_anchor_columns += new_cols - old_cols; + } + return 0; } @@ -2709,6 +3260,107 @@ xo_color_append_html (xo_handle_t *xop) } } +/* + * A wrapper for humanize_number that autoscales, since the + * HN_AUTOSCALE flag scales as needed based on the size of + * the output buffer, not the size of the value. I also + * wish HN_DECIMAL was more imperative, without the <10 + * test. But the boat only goes where we want when we hold + * the rudder, so xo_humanize fixes part of the problem. + */ +static int +xo_humanize (char *buf, int len, uint64_t value, int flags) +{ + int scale = 0; + + if (value) { + uint64_t left = value; + + if (flags & HN_DIVISOR_1000) { + for ( ; left; scale++) + left /= 1000; + } else { + for ( ; left; scale++) + left /= 1024; + } + scale -= 1; + } + + return xo_humanize_number(buf, len, value, "", scale, flags); +} + +/* + * This is an area where we can save information from the handle for + * later restoration. We need to know what data was rendered to know + * what needs cleaned up. + */ +typedef struct xo_humanize_save_s { + unsigned xhs_offset; /* Saved xo_offset */ + unsigned xhs_columns; /* Saved xo_columns */ + unsigned xhs_anchor_columns; /* Saved xo_anchor_columns */ +} xo_humanize_save_t; + +/* + * Format a "humanized" value for a numeric, meaning something nice + * like "44M" instead of "44470272". We autoscale, choosing the + * most appropriate value for K/M/G/T/P/E based on the value given. + */ +static void +xo_format_humanize (xo_handle_t *xop, xo_buffer_t *xbp, + xo_humanize_save_t *savep, xo_xff_flags_t flags) +{ + if (XOF_ISSET(xop, XOF_NO_HUMANIZE)) + return; + + unsigned end_offset = xbp->xb_curp - xbp->xb_bufp; + if (end_offset == savep->xhs_offset) /* Huh? Nothing to render */ + return; + + /* + * We have a string that's allegedly a number. We want to + * humanize it, which means turning it back into a number + * and calling xo_humanize_number on it. + */ + uint64_t value; + char *ep; + + xo_buf_append(xbp, "", 1); /* NUL-terminate it */ + + value = strtoull(xbp->xb_bufp + savep->xhs_offset, &ep, 0); + if (!(value == ULLONG_MAX && errno == ERANGE) + && (ep != xbp->xb_bufp + savep->xhs_offset)) { + /* + * There are few values where humanize_number needs + * more bytes than the original value. I've used + * 10 as a rectal number to cover those scenarios. + */ + if (xo_buf_has_room(xbp, 10)) { + xbp->xb_curp = xbp->xb_bufp + savep->xhs_offset; + + int rc; + int left = (xbp->xb_bufp + xbp->xb_size) - xbp->xb_curp; + int hn_flags = HN_NOSPACE; /* On by default */ + + if (flags & XFF_HN_SPACE) + hn_flags &= ~HN_NOSPACE; + + if (flags & XFF_HN_DECIMAL) + hn_flags |= HN_DECIMAL; + + if (flags & XFF_HN_1000) + hn_flags |= HN_DIVISOR_1000; + + rc = xo_humanize(xbp->xb_curp, + left, value, hn_flags); + if (rc > 0) { + xbp->xb_curp += rc; + xop->xo_columns = savep->xhs_columns + rc; + xop->xo_anchor_columns = savep->xhs_anchor_columns + rc; + } + } + } +} + static void xo_buf_append_div (xo_handle_t *xop, const char *class, xo_xff_flags_t flags, const char *name, int nlen, @@ -2731,7 +3383,7 @@ xo_buf_append_div (xo_handle_t *xop, const char *class, xo_xff_flags_t flags, */ int need_predidate = (name && (flags & XFF_KEY) && !(flags & XFF_DISPLAY_ONLY) - && (xop->xo_flags & XOF_XPATH)); + && XOF_ISSET(xop, XOF_XPATH)); if (need_predidate) { va_list va_local; @@ -2749,7 +3401,7 @@ xo_buf_append_div (xo_handle_t *xop, const char *class, xo_xff_flags_t flags, xo_buf_append(pbp, "[", 1); xo_buf_escape(xop, pbp, name, nlen, 0); - if (xop->xo_flags & XOF_PRETTY) + if (XOF_ISSET(xop, XOF_PRETTY)) xo_buf_append(pbp, " = '", 4); else xo_buf_append(pbp, "='", 2); @@ -2763,7 +3415,9 @@ xo_buf_append_div (xo_handle_t *xop, const char *class, xo_xff_flags_t flags, elen = strlen(encoding); } - xo_format_data(xop, pbp, encoding, elen, XFF_XML | XFF_ATTR); + xo_xff_flags_t pflags = flags | XFF_XML | XFF_ATTR; + pflags &= ~(XFF_NO_OUTPUT | XFF_ENCODE_ONLY); + xo_do_format_field(xop, pbp, encoding, elen, pflags); xo_buf_append(pbp, "']", 2); @@ -2793,14 +3447,14 @@ xo_buf_append_div (xo_handle_t *xop, const char *class, xo_xff_flags_t flags, * work of formatting it to make sure the args are cleared * from xo_vap. */ - xo_format_data(xop, &xop->xo_data, encoding, elen, + xo_do_format_field(xop, NULL, encoding, elen, flags | XFF_NO_OUTPUT); return; } xo_line_ensure_open(xop, 0); - if (xop->xo_flags & XOF_PRETTY) + if (XOF_ISSET(xop, XOF_PRETTY)) xo_buf_indent(xop, xop->xo_indent_by); xo_data_append(xop, div_start, sizeof(div_start) - 1); @@ -2823,8 +3477,8 @@ xo_buf_append_div (xo_handle_t *xop, const char *class, xo_xff_flags_t flags, /* * Save the offset at which we'd place units. See xo_format_units. */ - if (xop->xo_flags & XOF_UNITS) { - xop->xo_flags |= XOF_UNITS_PENDING; + if (XOF_ISSET(xop, XOF_UNITS)) { + XOIF_SET(xop, XOIF_UNITS_PENDING); /* * Note: We need the '+1' here because we know we've not * added the closing quote. We add one, knowing the quote @@ -2833,10 +3487,8 @@ xo_buf_append_div (xo_handle_t *xop, const char *class, xo_xff_flags_t flags, xop->xo_units_offset = xop->xo_data.xb_curp -xop->xo_data.xb_bufp + 1; } - } - if (name) { - if (xop->xo_flags & XOF_XPATH) { + if (XOF_ISSET(xop, XOF_XPATH)) { int i; xo_stack_t *xsp; @@ -2872,7 +3524,7 @@ xo_buf_append_div (xo_handle_t *xop, const char *class, xo_xff_flags_t flags, xo_data_escape(xop, name, nlen); } - if ((xop->xo_flags & XOF_INFO) && xop->xo_info) { + if (XOF_ISSET(xop, XOF_INFO) && xop->xo_info) { static char in_type[] = "\" data-type=\""; static char in_help[] = "\" data-help=\""; @@ -2889,17 +3541,60 @@ xo_buf_append_div (xo_handle_t *xop, const char *class, xo_xff_flags_t flags, } } - if ((flags & XFF_KEY) && (xop->xo_flags & XOF_KEYS)) + if ((flags & XFF_KEY) && XOF_ISSET(xop, XOF_KEYS)) xo_data_append(xop, div_key, sizeof(div_key) - 1); } + xo_buffer_t *xbp = &xop->xo_data; + unsigned base_offset = xbp->xb_curp - xbp->xb_bufp; + xo_data_append(xop, div_end, sizeof(div_end) - 1); - xo_format_data(xop, NULL, value, vlen, 0); + xo_humanize_save_t save; /* Save values for humanizing logic */ + + save.xhs_offset = xbp->xb_curp - xbp->xb_bufp; + save.xhs_columns = xop->xo_columns; + save.xhs_anchor_columns = xop->xo_anchor_columns; + + xo_do_format_field(xop, NULL, value, vlen, flags); + + if (flags & XFF_HUMANIZE) { + /* + * Unlike text style, we want to retain the original value and + * stuff it into the "data-number" attribute. + */ + static const char div_number[] = "\" data-number=\""; + int div_len = sizeof(div_number) - 1; + + unsigned end_offset = xbp->xb_curp - xbp->xb_bufp; + int olen = end_offset - save.xhs_offset; + + char *cp = alloca(olen + 1); + memcpy(cp, xbp->xb_bufp + save.xhs_offset, olen); + cp[olen] = '\0'; + + xo_format_humanize(xop, xbp, &save, flags); + + if (xo_buf_has_room(xbp, div_len + olen)) { + unsigned new_offset = xbp->xb_curp - xbp->xb_bufp; + + + /* Move the humanized string off to the left */ + memmove(xbp->xb_bufp + base_offset + div_len + olen, + xbp->xb_bufp + base_offset, new_offset - base_offset); + + /* Copy the data_number attribute name */ + memcpy(xbp->xb_bufp + base_offset, div_number, div_len); + + /* Copy the original long value */ + memcpy(xbp->xb_bufp + base_offset + div_len, cp, olen); + xbp->xb_curp += div_len + olen; + } + } xo_data_append(xop, div_close, sizeof(div_close) - 1); - if (xop->xo_flags & XOF_PRETTY) + if (XOF_ISSET(xop, XOF_PRETTY)) xo_data_append(xop, "\n", 1); } @@ -2918,9 +3613,14 @@ xo_format_text (xo_handle_t *xop, const char *str, int len) } static void -xo_format_title (xo_handle_t *xop, const char *str, int len, - const char *fmt, int flen) +xo_format_title (xo_handle_t *xop, xo_field_info_t *xfip) { + const char *str = xfip->xfi_content; + unsigned len = xfip->xfi_clen; + const char *fmt = xfip->xfi_format; + unsigned flen = xfip->xfi_flen; + xo_xff_flags_t flags = xfip->xfi_flags; + static char div_open[] = "
"; static char div_close[] = "
"; @@ -2933,12 +3633,14 @@ xo_format_title (xo_handle_t *xop, const char *str, int len, switch (xo_style(xop)) { case XO_STYLE_XML: case XO_STYLE_JSON: + case XO_STYLE_SDPARAMS: + case XO_STYLE_ENCODER: /* * Even though we don't care about text, we need to do * enough parsing work to skip over the right bits of xo_vap. */ if (len == 0) - xo_format_data(xop, NULL, fmt, flen, XFF_NO_OUTPUT); + xo_do_format_field(xop, NULL, fmt, flen, flags | XFF_NO_OUTPUT); return; } @@ -2946,12 +3648,10 @@ xo_format_title (xo_handle_t *xop, const char *str, int len, int start = xbp->xb_curp - xbp->xb_bufp; int left = xbp->xb_size - start; int rc; - int need_enc = XF_ENC_LOCALE; if (xo_style(xop) == XO_STYLE_HTML) { - need_enc = XF_ENC_UTF8; xo_line_ensure_open(xop, 0); - if (xop->xo_flags & XOF_PRETTY) + if (XOF_ISSET(xop, XOF_PRETTY)) xo_buf_indent(xop, xop->xo_indent_by); xo_buf_append(&xop->xo_data, div_open, sizeof(div_open) - 1); xo_color_append_html(xop); @@ -2970,7 +3670,6 @@ xo_format_title (xo_handle_t *xop, const char *str, int len, newstr[len] = '\0'; if (newstr[len - 1] == 's') { - int cols; char *bp; rc = snprintf(NULL, 0, newfmt, newstr); @@ -2981,20 +3680,14 @@ xo_format_title (xo_handle_t *xop, const char *str, int len, */ bp = alloca(rc + 1); rc = snprintf(bp, rc + 1, newfmt, newstr); - cols = xo_format_string_direct(xop, xbp, 0, NULL, bp, rc, -1, - need_enc, XF_ENC_UTF8); - if (cols > 0) { - if (xop->xo_flags & XOF_COLUMNS) - xop->xo_columns += cols; - if (xop->xo_flags & XOF_ANCHOR) - xop->xo_anchor_columns += cols; - } + + xo_data_append_content(xop, bp, rc, flags); } goto move_along; } else { rc = snprintf(xbp->xb_curp, left, newfmt, newstr); - if (rc > left) { + if (rc >= left) { if (!xo_buf_has_room(xbp, rc)) return; left = xbp->xb_size - (xbp->xb_curp - xbp->xb_bufp); @@ -3002,17 +3695,17 @@ xo_format_title (xo_handle_t *xop, const char *str, int len, } if (rc > 0) { - if (xop->xo_flags & XOF_COLUMNS) + if (XOF_ISSET(xop, XOF_COLUMNS)) xop->xo_columns += rc; - if (xop->xo_flags & XOF_ANCHOR) + if (XOIF_ISSET(xop, XOIF_ANCHOR)) xop->xo_anchor_columns += rc; } } } else { - xo_format_data(xop, NULL, fmt, flen, 0); + xo_do_format_field(xop, NULL, fmt, flen, flags); - /* xo_format_data moved curp, so we need to reset it */ + /* xo_do_format_field moved curp, so we need to reset it */ rc = xbp->xb_curp - (xbp->xb_bufp + start); xbp->xb_curp = xbp->xb_bufp + start; } @@ -3028,7 +3721,7 @@ xo_format_title (xo_handle_t *xop, const char *str, int len, move_along: if (xo_style(xop) == XO_STYLE_HTML) { xo_data_append(xop, div_close, sizeof(div_close) - 1); - if (xop->xo_flags & XOF_PRETTY) + if (XOF_ISSET(xop, XOF_PRETTY)) xo_data_append(xop, "\n", 1); } } @@ -3038,7 +3731,7 @@ xo_format_prep (xo_handle_t *xop, xo_xff_flags_t flags) { if (xop->xo_stack[xop->xo_depth].xs_flags & XSF_NOT_FIRST) { xo_data_append(xop, ",", 1); - if (!(flags & XFF_LEAF_LIST) && (xop->xo_flags & XOF_PRETTY)) + if (!(flags & XFF_LEAF_LIST) && XOF_ISSET(xop, XOF_PRETTY)) xo_data_append(xop, "\n", 1); } else xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST; @@ -3058,12 +3751,11 @@ xo_arg (xo_handle_t *xop) static void xo_format_value (xo_handle_t *xop, const char *name, int nlen, - const char *format, int flen, - const char *encoding, int elen, xo_xff_flags_t flags) + const char *format, int flen, + const char *encoding, int elen, xo_xff_flags_t flags) { - int pretty = (xop->xo_flags & XOF_PRETTY); + int pretty = XOF_ISSET(xop, XOF_PRETTY); int quote; - xo_buffer_t *xbp; /* * Before we emit a value, we need to know that the frame is ready. @@ -3134,16 +3826,28 @@ xo_format_value (xo_handle_t *xop, const char *name, int nlen, } } + xo_buffer_t *xbp = &xop->xo_data; + xo_humanize_save_t save; /* Save values for humanizing logic */ + switch (xo_style(xop)) { case XO_STYLE_TEXT: if (flags & XFF_ENCODE_ONLY) flags |= XFF_NO_OUTPUT; - xo_format_data(xop, NULL, format, flen, flags); + + save.xhs_offset = xbp->xb_curp - xbp->xb_bufp; + save.xhs_columns = xop->xo_columns; + save.xhs_anchor_columns = xop->xo_anchor_columns; + + xo_do_format_field(xop, NULL, format, flen, flags); + + if (flags & XFF_HUMANIZE) + xo_format_humanize(xop, xbp, &save, flags); break; case XO_STYLE_HTML: if (flags & XFF_ENCODE_ONLY) flags |= XFF_NO_OUTPUT; + xo_buf_append_div(xop, "data", flags, name, nlen, format, flen, encoding, elen); break; @@ -3155,7 +3859,7 @@ xo_format_value (xo_handle_t *xop, const char *name, int nlen, */ if (flags & XFF_DISPLAY_ONLY) { flags |= XFF_NO_OUTPUT; - xo_format_data(xop, NULL, format, flen, flags); + xo_do_format_field(xop, NULL, format, flen, flags); break; } @@ -3194,7 +3898,7 @@ xo_format_value (xo_handle_t *xop, const char *name, int nlen, * data, it's often useful. Especially when format meta-data is * difficult to come by. */ - if ((flags & XFF_KEY) && (xop->xo_flags & XOF_KEYS)) { + if ((flags & XFF_KEY) && XOF_ISSET(xop, XOF_KEYS)) { static char attr[] = " key=\"key\""; xo_data_append(xop, attr, sizeof(attr) - 1); } @@ -3202,13 +3906,13 @@ xo_format_value (xo_handle_t *xop, const char *name, int nlen, /* * Save the offset at which we'd place units. See xo_format_units. */ - if (xop->xo_flags & XOF_UNITS) { - xop->xo_flags |= XOF_UNITS_PENDING; + if (XOF_ISSET(xop, XOF_UNITS)) { + XOIF_SET(xop, XOIF_UNITS_PENDING); xop->xo_units_offset = xop->xo_data.xb_curp -xop->xo_data.xb_bufp; } xo_data_append(xop, ">", 1); - xo_format_data(xop, NULL, format, flen, flags); + xo_do_format_field(xop, NULL, format, flen, flags); xo_data_append(xop, "", 1); @@ -3219,7 +3923,7 @@ xo_format_value (xo_handle_t *xop, const char *name, int nlen, case XO_STYLE_JSON: if (flags & XFF_DISPLAY_ONLY) { flags |= XFF_NO_OUTPUT; - xo_format_data(xop, NULL, format, flen, flags); + xo_do_format_field(xop, NULL, format, flen, flags); break; } @@ -3273,7 +3977,7 @@ xo_format_value (xo_handle_t *xop, const char *name, int nlen, xo_data_escape(xop, name, nlen); - if (xop->xo_flags & XOF_UNDERSCORES) { + if (XOF_ISSET(xop, XOF_UNDERSCORES)) { int now = xbp->xb_curp - xbp->xb_bufp; for ( ; off < now; off++) if (xbp->xb_bufp[off] == '-') @@ -3287,25 +3991,145 @@ xo_format_value (xo_handle_t *xop, const char *name, int nlen, if (quote) xo_data_append(xop, "\"", 1); - xo_format_data(xop, NULL, format, flen, flags); + xo_do_format_field(xop, NULL, format, flen, flags); if (quote) xo_data_append(xop, "\"", 1); break; + + case XO_STYLE_SDPARAMS: + if (flags & XFF_DISPLAY_ONLY) { + flags |= XFF_NO_OUTPUT; + xo_do_format_field(xop, NULL, format, flen, flags); + break; + } + + if (encoding) { + format = encoding; + flen = elen; + } else { + char *enc = alloca(flen + 1); + memcpy(enc, format, flen); + enc[flen] = '\0'; + format = xo_fix_encoding(xop, enc); + flen = strlen(format); + } + + if (nlen == 0) { + static char missing[] = "missing-field-name"; + xo_failure(xop, "missing field name: %s", format); + name = missing; + nlen = sizeof(missing) - 1; + } + + xo_data_escape(xop, name, nlen); + xo_data_append(xop, "=\"", 2); + xo_do_format_field(xop, NULL, format, flen, flags); + xo_data_append(xop, "\" ", 2); + break; + + case XO_STYLE_ENCODER: + if (flags & XFF_DISPLAY_ONLY) { + flags |= XFF_NO_OUTPUT; + xo_do_format_field(xop, NULL, format, flen, flags); + break; + } + + if (flags & XFF_QUOTE) + quote = 1; + else if (flags & XFF_NOQUOTE) + quote = 0; + else if (flen == 0) { + quote = 0; + format = "true"; /* JSON encodes empty tags as a boolean true */ + flen = 4; + } else if (strchr("diouxXDOUeEfFgGaAcCp", format[flen - 1]) == NULL) + quote = 1; + else + quote = 0; + + if (encoding) { + format = encoding; + flen = elen; + } else { + char *enc = alloca(flen + 1); + memcpy(enc, format, flen); + enc[flen] = '\0'; + format = xo_fix_encoding(xop, enc); + flen = strlen(format); + } + + if (nlen == 0) { + static char missing[] = "missing-field-name"; + xo_failure(xop, "missing field name: %s", format); + name = missing; + nlen = sizeof(missing) - 1; + } + + unsigned name_offset = xo_buf_offset(&xop->xo_data); + xo_data_append(xop, name, nlen); + xo_data_append(xop, "", 1); + + unsigned value_offset = xo_buf_offset(&xop->xo_data); + xo_do_format_field(xop, NULL, format, flen, flags); + xo_data_append(xop, "", 1); + + xo_encoder_handle(xop, quote ? XO_OP_STRING : XO_OP_CONTENT, + xo_buf_data(&xop->xo_data, name_offset), + xo_buf_data(&xop->xo_data, value_offset)); + xo_buf_reset(&xop->xo_data); + break; } } +static void +xo_set_gettext_domain (xo_handle_t *xop, xo_field_info_t *xfip) +{ + const char *str = xfip->xfi_content; + unsigned len = xfip->xfi_clen; + const char *fmt = xfip->xfi_format; + unsigned flen = xfip->xfi_flen; + + /* Start by discarding previous domain */ + if (xop->xo_gt_domain) { + xo_free(xop->xo_gt_domain); + xop->xo_gt_domain = NULL; + } + + /* An empty {G:} means no domainname */ + if (len == 0 && flen == 0) + return; + + int start_offset = -1; + if (len == 0 && flen != 0) { + /* Need to do format the data to get the domainname from args */ + start_offset = xop->xo_data.xb_curp - xop->xo_data.xb_bufp; + xo_do_format_field(xop, NULL, fmt, flen, 0); + + int end_offset = xop->xo_data.xb_curp - xop->xo_data.xb_bufp; + len = end_offset - start_offset; + str = xop->xo_data.xb_bufp + start_offset; + } + + xop->xo_gt_domain = xo_strndup(str, len); + + /* Reset the current buffer point to avoid emitting the name as output */ + if (start_offset >= 0) + xop->xo_data.xb_curp = xop->xo_data.xb_bufp + start_offset; +} + static void xo_format_content (xo_handle_t *xop, const char *class_name, - const char *xml_tag, int display_only, - const char *str, int len, const char *fmt, int flen) + const char *tag_name, + const char *str, int len, const char *fmt, int flen, + xo_xff_flags_t flags) { switch (xo_style(xop)) { case XO_STYLE_TEXT: - if (len) { - xo_data_append_content(xop, str, len); - } else - xo_format_data(xop, NULL, fmt, flen, 0); + if (len) + xo_data_append_content(xop, str, len, flags); + else + xo_do_format_field(xop, NULL, fmt, flen, flags); break; case XO_STYLE_HTML: @@ -3314,19 +4138,21 @@ xo_format_content (xo_handle_t *xop, const char *class_name, len = flen; } - xo_buf_append_div(xop, class_name, 0, NULL, 0, str, len, NULL, 0); + xo_buf_append_div(xop, class_name, flags, NULL, 0, str, len, NULL, 0); break; case XO_STYLE_XML: - if (xml_tag) { + case XO_STYLE_JSON: + case XO_STYLE_SDPARAMS: + if (tag_name) { if (len == 0) { str = fmt; len = flen; } - xo_open_container_h(xop, xml_tag); - xo_format_value(xop, "message", 7, str, len, NULL, 0, 0); - xo_close_container_h(xop, xml_tag); + xo_open_container_h(xop, tag_name); + xo_format_value(xop, "message", 7, str, len, NULL, 0, flags); + xo_close_container_h(xop, tag_name); } else { /* @@ -3334,21 +4160,15 @@ xo_format_content (xo_handle_t *xop, const char *class_name, * enough parsing work to skip over the right bits of xo_vap. */ if (len == 0) - xo_format_data(xop, NULL, fmt, flen, XFF_NO_OUTPUT); + xo_do_format_field(xop, NULL, fmt, flen, + flags | XFF_NO_OUTPUT); } break; - case XO_STYLE_JSON: - /* - * Even though we don't care about labels, we need to do - * enough parsing work to skip over the right bits of xo_vap. - */ - if (display_only) { - if (len == 0) - xo_format_data(xop, NULL, fmt, flen, XFF_NO_OUTPUT); - break; - } - /* XXX need schem for representing errors in JSON */ + case XO_STYLE_ENCODER: + if (len == 0) + xo_do_format_field(xop, NULL, fmt, flen, + flags | XFF_NO_OUTPUT); break; } } @@ -3401,7 +4221,7 @@ static const char *xo_effect_on_codes[] = { /* * See comment below re: joy of terminal standards. These can * be use by just adding: - * if (newp->xoc_effects & bit) + * + if (newp->xoc_effects & bit) * code = xo_effect_on_codes[i]; * + else * + code = xo_effect_off_codes[i]; @@ -3499,7 +4319,7 @@ xo_colors_parse (xo_handle_t *xop, xo_colors_t *xocp, char *str) continue; unknown: - if (xop->xo_flags & XOF_WARN) + if (XOF_ISSET(xop, XOF_WARN)) xo_failure(xop, "unknown color/effect string detected: '%s'", cp); } } @@ -3510,7 +4330,7 @@ xo_colors_enabled (xo_handle_t *xop UNUSED) #ifdef LIBXO_TEXT_ONLY return 0; #else /* LIBXO_TEXT_ONLY */ - return ((xop->xo_flags & XOF_COLOR) ? 1 : 0); + return XOF_ISSET(xop, XOF_COLOR); #endif /* LIBXO_TEXT_ONLY */ } @@ -3547,8 +4367,7 @@ xo_colors_handle_text (xo_handle_t *xop UNUSED, xo_colors_t *newp) if ((newp->xoc_effects & bit) == (oldp->xoc_effects & bit)) continue; - if (newp->xoc_effects & bit) - code = xo_effect_on_codes[i]; + code = xo_effect_on_codes[i]; cp += snprintf(cp, ep - cp, ";%s", code); if (cp >= ep) @@ -3641,14 +4460,17 @@ xo_colors_handle_html (xo_handle_t *xop, xo_colors_t *newp) } static void -xo_format_colors (xo_handle_t *xop, const char *str, int len, - const char *fmt, int flen) +xo_format_colors (xo_handle_t *xop, xo_field_info_t *xfip) { + const char *str = xfip->xfi_content; + unsigned len = xfip->xfi_clen; + const char *fmt = xfip->xfi_format; + unsigned flen = xfip->xfi_flen; + xo_buffer_t xb; /* If the string is static and we've in an encoding style, bail */ - if (len != 0 - && (xo_style(xop) == XO_STYLE_XML || xo_style(xop) == XO_STYLE_JSON)) + if (len != 0 && xo_style_is_encoding(xop)) return; xo_buf_init(&xb); @@ -3656,7 +4478,7 @@ xo_format_colors (xo_handle_t *xop, const char *str, int len, if (len) xo_buf_append(&xb, str, len); else if (flen) - xo_format_data(xop, &xb, fmt, flen, 0); + xo_do_format_field(xop, &xb, fmt, flen, 0); else xo_buf_append(&xb, "reset", 6); /* Default if empty */ @@ -3698,6 +4520,8 @@ xo_format_colors (xo_handle_t *xop, const char *str, int len, case XO_STYLE_XML: case XO_STYLE_JSON: + case XO_STYLE_SDPARAMS: + case XO_STYLE_ENCODER: /* * Nothing to do; we did all that work just to clear the stack of * formatting arguments. @@ -3710,14 +4534,19 @@ xo_format_colors (xo_handle_t *xop, const char *str, int len, } static void -xo_format_units (xo_handle_t *xop, const char *str, int len, - const char *fmt, int flen) +xo_format_units (xo_handle_t *xop, xo_field_info_t *xfip) { + const char *str = xfip->xfi_content; + unsigned len = xfip->xfi_clen; + const char *fmt = xfip->xfi_format; + unsigned flen = xfip->xfi_flen; + xo_xff_flags_t flags = xfip->xfi_flags; + static char units_start_xml[] = " units=\""; static char units_start_html[] = " data-units=\""; - if (!(xop->xo_flags & XOF_UNITS_PENDING)) { - xo_format_content(xop, "units", NULL, 1, str, len, fmt, flen); + if (!XOIF_ISSET(xop, XOIF_UNITS_PENDING)) { + xo_format_content(xop, "units", NULL, str, len, fmt, flen, flags); return; } @@ -3733,15 +4562,15 @@ xo_format_units (xo_handle_t *xop, const char *str, int len, return; if (len) - xo_data_append(xop, str, len); + xo_data_escape(xop, str, len); else - xo_format_data(xop, NULL, fmt, flen, 0); + xo_do_format_field(xop, NULL, fmt, flen, flags); xo_buf_append(xbp, "\"", 1); int now = xbp->xb_curp - xbp->xb_bufp; int delta = now - stop; - if (delta < 0) { /* Strange; no output to move */ + if (delta <= 0) { /* Strange; no output to move */ xbp->xb_curp = xbp->xb_bufp + stop; /* Reset buffer to prior state */ return; } @@ -3760,9 +4589,13 @@ xo_format_units (xo_handle_t *xop, const char *str, int len, } static int -xo_find_width (xo_handle_t *xop, const char *str, int len, - const char *fmt, int flen) +xo_find_width (xo_handle_t *xop, xo_field_info_t *xfip) { + const char *str = xfip->xfi_content; + unsigned len = xfip->xfi_clen; + const char *fmt = xfip->xfi_format; + unsigned flen = xfip->xfi_flen; + long width = 0; char *bp; char *cp; @@ -3781,7 +4614,7 @@ xo_find_width (xo_handle_t *xop, const char *str, int len, } else if (flen) { if (flen != 2 || strncmp("%d", fmt, flen) != 0) xo_failure(xop, "invalid width format: '%*.*s'", flen, flen, fmt); - if (!(xop->xo_flags & XOF_NO_VA_ARG)) + if (!XOF_ISSET(xop, XOF_NO_VA_ARG)) width = va_arg(xop->xo_vap, int); } @@ -3791,7 +4624,7 @@ xo_find_width (xo_handle_t *xop, const char *str, int len, static void xo_anchor_clear (xo_handle_t *xop) { - xop->xo_flags &= ~XOF_ANCHOR; + XOIF_CLEAR(xop, XOIF_ANCHOR); xop->xo_anchor_offset = 0; xop->xo_anchor_columns = 0; xop->xo_anchor_min_width = 0; @@ -3806,16 +4639,15 @@ xo_anchor_clear (xo_handle_t *xop) * format it when the end anchor tag is seen. */ static void -xo_anchor_start (xo_handle_t *xop, const char *str, int len, - const char *fmt, int flen) +xo_anchor_start (xo_handle_t *xop, xo_field_info_t *xfip) { if (xo_style(xop) != XO_STYLE_TEXT && xo_style(xop) != XO_STYLE_HTML) return; - if (xop->xo_flags & XOF_ANCHOR) + if (XOIF_ISSET(xop, XOIF_ANCHOR)) xo_failure(xop, "the anchor already recording is discarded"); - xop->xo_flags |= XOF_ANCHOR; + XOIF_SET(xop, XOIF_ANCHOR); xo_buffer_t *xbp = &xop->xo_data; xop->xo_anchor_offset = xbp->xb_curp - xbp->xb_bufp; xop->xo_anchor_columns = 0; @@ -3824,24 +4656,23 @@ xo_anchor_start (xo_handle_t *xop, const char *str, int len, * Now we find the width, if possible. If it's not there, * we'll get it on the end anchor. */ - xop->xo_anchor_min_width = xo_find_width(xop, str, len, fmt, flen); + xop->xo_anchor_min_width = xo_find_width(xop, xfip); } static void -xo_anchor_stop (xo_handle_t *xop, const char *str, int len, - const char *fmt, int flen) +xo_anchor_stop (xo_handle_t *xop, xo_field_info_t *xfip) { if (xo_style(xop) != XO_STYLE_TEXT && xo_style(xop) != XO_STYLE_HTML) return; - if (!(xop->xo_flags & XOF_ANCHOR)) { + if (!XOIF_ISSET(xop, XOIF_ANCHOR)) { xo_failure(xop, "no start anchor"); return; } - xop->xo_flags &= ~XOF_UNITS_PENDING; + XOIF_CLEAR(xop, XOIF_UNITS_PENDING); - int width = xo_find_width(xop, str, len, fmt, flen); + int width = xo_find_width(xop, xfip); if (width == 0) width = xop->xo_anchor_min_width; @@ -3866,14 +4697,14 @@ xo_anchor_stop (xo_handle_t *xop, const char *str, int len, /* Make a suitable padding field and emit it */ char *buf = alloca(blen); memset(buf, ' ', blen); - xo_format_content(xop, "padding", NULL, 1, buf, blen, NULL, 0); + xo_format_content(xop, "padding", NULL, buf, blen, NULL, 0, 0); if (width < 0) /* Already left justified */ goto done; int now = xbp->xb_curp - xbp->xb_bufp; int delta = now - stop; - if (delta < 0) /* Strange; no output to move */ + if (delta <= 0) /* Strange; no output to move */ goto done; /* @@ -3894,179 +4725,488 @@ xo_anchor_stop (xo_handle_t *xop, const char *str, int len, xo_anchor_clear(xop); } -static int -xo_do_emit (xo_handle_t *xop, const char *fmt) +static const char * +xo_class_name (int ftype) { - int rc = 0; - const char *cp, *sp, *ep, *basep; - char *newp = NULL; - int flush = (xop->xo_flags & XOF_FLUSH) ? 1 : 0; - int flush_line = (xop->xo_flags & XOF_FLUSH_LINE) ? 1 : 0; + switch (ftype) { + case 'D': return "decoration"; + case 'E': return "error"; + case 'L': return "label"; + case 'N': return "note"; + case 'P': return "padding"; + case 'W': return "warning"; + } - xop->xo_columns = 0; /* Always reset it */ + return NULL; +} - for (cp = fmt; *cp; ) { - if (*cp == '\n') { - xo_line_close(xop); - if (flush_line && xo_flush_h(xop) < 0) - return -1; - cp += 1; +static const char * +xo_tag_name (int ftype) +{ + switch (ftype) { + case 'E': return "__error"; + case 'W': return "__warning"; + } + + return NULL; +} + +static int +xo_role_wants_default_format (int ftype) +{ + switch (ftype) { + /* These roles can be completely empty and/or without formatting */ + case 'C': + case 'G': + case '[': + case ']': + return 0; + } + + return 1; +} + +static xo_mapping_t xo_role_names[] = { + { 'C', "color" }, + { 'D', "decoration" }, + { 'E', "error" }, + { 'L', "label" }, + { 'N', "note" }, + { 'P', "padding" }, + { 'T', "title" }, + { 'U', "units" }, + { 'V', "value" }, + { 'W', "warning" }, + { '[', "start-anchor" }, + { ']', "stop-anchor" }, + { 0, NULL } +}; + +#define XO_ROLE_EBRACE '{' /* Escaped braces */ +#define XO_ROLE_TEXT '+' +#define XO_ROLE_NEWLINE '\n' + +static xo_mapping_t xo_modifier_names[] = { + { XFF_COLON, "colon" }, + { XFF_COMMA, "comma" }, + { XFF_DISPLAY_ONLY, "display" }, + { XFF_ENCODE_ONLY, "encoding" }, + { XFF_GT_FIELD, "gettext" }, + { XFF_HUMANIZE, "humanize" }, + { XFF_HUMANIZE, "hn" }, + { XFF_HN_SPACE, "hn-space" }, + { XFF_HN_DECIMAL, "hn-decimal" }, + { XFF_HN_1000, "hn-1000" }, + { XFF_KEY, "key" }, + { XFF_LEAF_LIST, "leaf-list" }, + { XFF_LEAF_LIST, "list" }, + { XFF_NOQUOTE, "no-quotes" }, + { XFF_NOQUOTE, "no-quote" }, + { XFF_GT_PLURAL, "plural" }, + { XFF_QUOTE, "quotes" }, + { XFF_QUOTE, "quote" }, + { XFF_TRIM_WS, "trim" }, + { XFF_WS, "white" }, + { 0, NULL } +}; + +#ifdef NOT_NEEDED_YET +static xo_mapping_t xo_modifier_short_names[] = { + { XFF_COLON, "c" }, + { XFF_DISPLAY_ONLY, "d" }, + { XFF_ENCODE_ONLY, "e" }, + { XFF_GT_FIELD, "g" }, + { XFF_HUMANIZE, "h" }, + { XFF_KEY, "k" }, + { XFF_LEAF_LIST, "l" }, + { XFF_NOQUOTE, "n" }, + { XFF_GT_PLURAL, "p" }, + { XFF_QUOTE, "q" }, + { XFF_TRIM_WS, "t" }, + { XFF_WS, "w" }, + { 0, NULL } +}; +#endif /* NOT_NEEDED_YET */ + +static int +xo_count_fields (xo_handle_t *xop UNUSED, const char *fmt) +{ + int rc = 1; + const char *cp; + + for (cp = fmt; *cp; cp++) + if (*cp == '{' || *cp == '\n') + rc += 1; + + return rc * 2 + 1; +} + +/* + * The field format is: + * '{' modifiers ':' content [ '/' print-fmt [ '/' encode-fmt ]] '}' + * Roles are optional and include the following field types: + * 'D': decoration; something non-text and non-data (colons, commmas) + * 'E': error message + * 'G': gettext() the entire string; optional domainname as content + * 'L': label; text preceding data + * 'N': note; text following data + * 'P': padding; whitespace + * 'T': Title, where 'content' is a column title + * 'U': Units, where 'content' is the unit label + * 'V': value, where 'content' is the name of the field (the default) + * 'W': warning message + * '[': start a section of anchored text + * ']': end a section of anchored text + * The following modifiers are also supported: + * 'c': flag: emit a colon after the label + * 'd': field is only emitted for display styles (text and html) + * 'e': field is only emitted for encoding styles (xml and json) + * 'g': gettext() the field + * 'h': humanize a numeric value (only for display styles) + * 'k': this field is a key, suitable for XPath predicates + * 'l': a leaf-list, a simple list of values + * 'n': no quotes around this field + * 'p': the field has plural gettext semantics (ngettext) + * 'q': add quotes around this field + * 't': trim whitespace around the value + * 'w': emit a blank after the label + * The print-fmt and encode-fmt strings is the printf-style formating + * for this data. JSON and XML will use the encoding-fmt, if present. + * If the encode-fmt is not provided, it defaults to the print-fmt. + * If the print-fmt is not provided, it defaults to 's'. + */ +static const char * +xo_parse_roles (xo_handle_t *xop, const char *fmt, + const char *basep, xo_field_info_t *xfip) +{ + const char *sp; + unsigned ftype = 0; + xo_xff_flags_t flags = 0; + uint8_t fnum = 0; + + for (sp = basep; sp; sp++) { + if (*sp == ':' || *sp == '/' || *sp == '}') + break; + + if (*sp == '\\') { + if (sp[1] == '\0') { + xo_failure(xop, "backslash at the end of string"); + return NULL; + } + + /* Anything backslashed is ignored */ + sp += 1; + continue; + } + + if (*sp == ',') { + const char *np; + for (np = ++sp; *np; np++) + if (*np == ':' || *np == '/' || *np == '}' || *np == ',') + break; + + int slen = np - sp; + if (slen > 0) { + xo_xff_flags_t value; + + value = xo_name_lookup(xo_role_names, sp, slen); + if (value) + ftype = value; + else { + value = xo_name_lookup(xo_modifier_names, sp, slen); + if (value) + flags |= value; + else + xo_failure(xop, "unknown keyword ignored: '%.*s'", + slen, sp); + } + } + + sp = np - 1; + continue; + } + + switch (*sp) { + case 'C': + case 'D': + case 'E': + case 'G': + case 'L': + case 'N': + case 'P': + case 'T': + case 'U': + case 'V': + case 'W': + case '[': + case ']': + if (ftype != 0) { + xo_failure(xop, "field descriptor uses multiple types: '%s'", + xo_printable(fmt)); + return NULL; + } + ftype = *sp; + break; + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + fnum = (fnum * 10) + (*sp - '0'); + break; + + case 'c': + flags |= XFF_COLON; + break; + + case 'd': + flags |= XFF_DISPLAY_ONLY; + break; + + case 'e': + flags |= XFF_ENCODE_ONLY; + break; + + case 'g': + flags |= XFF_GT_FIELD; + break; + + case 'h': + flags |= XFF_HUMANIZE; + break; + + case 'k': + flags |= XFF_KEY; + break; + + case 'l': + flags |= XFF_LEAF_LIST; + break; + + case 'n': + flags |= XFF_NOQUOTE; + break; + + case 'p': + flags |= XFF_GT_PLURAL; + break; + + case 'q': + flags |= XFF_QUOTE; + break; + + case 't': + flags |= XFF_TRIM_WS; + break; + + case 'w': + flags |= XFF_WS; + break; + + default: + xo_failure(xop, "field descriptor uses unknown modifier: '%s'", + xo_printable(fmt)); + /* + * No good answer here; a bad format will likely + * mean a core file. We just return and hope + * the caller notices there's no output, and while + * that seems, well, bad, there's nothing better. + */ + return NULL; + } + + if (ftype == 'N' || ftype == 'U') { + if (flags & XFF_COLON) { + xo_failure(xop, "colon modifier on 'N' or 'U' field ignored: " + "'%s'", xo_printable(fmt)); + flags &= ~XFF_COLON; + } + } + } + + xfip->xfi_flags = flags; + xfip->xfi_ftype = ftype ?: 'V'; + xfip->xfi_fnum = fnum; + + return sp; +} + +/* + * Number any remaining fields that need numbers. Note that some + * field types (text, newline, escaped braces) never get numbers. + */ +static void +xo_gettext_finish_numbering_fields (xo_handle_t *xop UNUSED, + const char *fmt UNUSED, + xo_field_info_t *fields) +{ + xo_field_info_t *xfip; + unsigned fnum, max_fields; + uint64_t bits = 0; + + /* First make a list of add the explicitly used bits */ + for (xfip = fields, fnum = 0; xfip->xfi_ftype; xfip++) { + switch (xfip->xfi_ftype) { + case XO_ROLE_NEWLINE: /* Don't get numbered */ + case XO_ROLE_TEXT: + case XO_ROLE_EBRACE: + case 'G': + continue; + } + + fnum += 1; + if (fnum >= 63) + break; + + if (xfip->xfi_fnum) + bits |= 1 << xfip->xfi_fnum; + } + + max_fields = fnum; + + for (xfip = fields, fnum = 0; xfip->xfi_ftype; xfip++) { + switch (xfip->xfi_ftype) { + case XO_ROLE_NEWLINE: /* Don't get numbered */ + case XO_ROLE_TEXT: + case XO_ROLE_EBRACE: + case 'G': + continue; + } + + if (xfip->xfi_fnum != 0) continue; - } else if (*cp == '{') { - if (cp[1] == '{') { /* Start of {{escaped braces}} */ + /* Find the next unassigned field */ + for (fnum++; bits & (1 << fnum); fnum++) + continue; - cp += 2; /* Skip over _both_ characters */ - for (sp = cp; *sp; sp++) { - if (*sp == '}' && sp[1] == '}') - break; - } - if (*sp == '\0') { - xo_failure(xop, "missing closing '}}': %s", fmt); - return -1; - } + if (fnum > max_fields) + break; - xo_format_text(xop, cp, sp - cp); + xfip->xfi_fnum = fnum; /* Mark the field number */ + bits |= 1 << fnum; /* Mark it used */ + } +} - /* Move along the string, but don't run off the end */ - if (*sp == '}' && sp[1] == '}') - sp += 2; - cp = *sp ? sp + 1 : sp; - continue; +/* + * The format string uses field numbers, so we need to whiffle thru it + * and make sure everything's sane and lovely. + */ +static int +xo_parse_field_numbers (xo_handle_t *xop, const char *fmt, + xo_field_info_t *fields, unsigned num_fields) +{ + xo_field_info_t *xfip; + unsigned field, fnum; + uint64_t bits = 0; + + for (xfip = fields, field = 0; field < num_fields; xfip++, field++) { + /* Fields default to 1:1 with natural position */ + if (xfip->xfi_fnum == 0) + xfip->xfi_fnum = field + 1; + else if (xfip->xfi_fnum > num_fields) { + xo_failure(xop, "field number exceeds number of fields: '%s'", fmt); + return -1; + } + + fnum = xfip->xfi_fnum - 1; /* Move to zero origin */ + if (fnum < 64) { /* Only test what fits */ + if (bits & (1 << fnum)) { + xo_failure(xop, "field number %u reused: '%s'", + xfip->xfi_fnum, fmt); + return -1; } - /* Else fall thru to the code below */ + bits |= 1 << fnum; + } + } - } else { + return 0; +} + +static int +xo_parse_fields (xo_handle_t *xop, xo_field_info_t *fields, + unsigned num_fields, const char *fmt) +{ + static const char default_format[] = "%s"; + const char *cp, *sp, *ep, *basep; + unsigned field = 0; + xo_field_info_t *xfip = fields; + unsigned seen_fnum = 0; + + for (cp = fmt; *cp && field < num_fields; field++, xfip++) { + xfip->xfi_start = cp; + + if (*cp == '\n') { + xfip->xfi_ftype = XO_ROLE_NEWLINE; + xfip->xfi_len = 1; + cp += 1; + continue; + } + + if (*cp != '{') { /* Normal text */ for (sp = cp; *sp; sp++) { if (*sp == '{' || *sp == '\n') break; } - xo_format_text(xop, cp, sp - cp); + + xfip->xfi_ftype = XO_ROLE_TEXT; + xfip->xfi_content = cp; + xfip->xfi_clen = sp - cp; + xfip->xfi_next = sp; cp = sp; continue; } - basep = cp + 1; + if (cp[1] == '{') { /* Start of {{escaped braces}} */ + xfip->xfi_start = cp + 1; /* Start at second brace */ + xfip->xfi_ftype = XO_ROLE_EBRACE; - /* - * We are looking at the start of a field definition. The format is: - * '{' modifiers ':' content [ '/' print-fmt [ '/' encode-fmt ]] '}' - * Modifiers are optional and include the following field types: - * 'D': decoration; something non-text and non-data (colons, commmas) - * 'E': error message - * 'L': label; text preceding data - * 'N': note; text following data - * 'P': padding; whitespace - * 'T': Title, where 'content' is a column title - * 'U': Units, where 'content' is the unit label - * 'V': value, where 'content' is the name of the field (the default) - * 'W': warning message - * '[': start a section of anchored text - * ']': end a section of anchored text - * The following flags are also supported: - * 'c': flag: emit a colon after the label - * 'd': field is only emitted for display formats (text and html) - * 'e': field is only emitted for encoding formats (xml and json) - * 'k': this field is a key, suitable for XPath predicates - * 'l': a leaf-list, a simple list of values - * 'n': no quotes around this field - * 'q': add quotes around this field - * 't': trim whitespace around the value - * 'w': emit a blank after the label - * The print-fmt and encode-fmt strings is the printf-style formating - * for this data. JSON and XML will use the encoding-fmt, if present. - * If the encode-fmt is not provided, it defaults to the print-fmt. - * If the print-fmt is not provided, it defaults to 's'. - */ - unsigned ftype = 0, flags = 0; - const char *content = NULL, *format = NULL, *encoding = NULL; - int clen = 0, flen = 0, elen = 0; - - for (sp = basep; sp; sp++) { - if (*sp == ':' || *sp == '/' || *sp == '}') - break; - - if (*sp == '\\') { - if (sp[1] == '\0') { - xo_failure(xop, "backslash at the end of string"); - return -1; - } - sp += 1; - continue; + cp += 2; /* Skip over _both_ characters */ + for (sp = cp; *sp; sp++) { + if (*sp == '}' && sp[1] == '}') + break; } - - switch (*sp) { - case 'C': - case 'D': - case 'E': - case 'L': - case 'N': - case 'P': - case 'T': - case 'U': - case 'V': - case 'W': - case '[': - case ']': - if (ftype != 0) { - xo_failure(xop, "field descriptor uses multiple types: %s", - fmt); - return -1; - } - ftype = *sp; - break; - - case 'c': - flags |= XFF_COLON; - break; - - case 'd': - flags |= XFF_DISPLAY_ONLY; - break; - - case 'e': - flags |= XFF_ENCODE_ONLY; - break; - - case 'k': - flags |= XFF_KEY; - break; - - case 'l': - flags |= XFF_LEAF_LIST; - break; - - case 'n': - flags |= XFF_NOQUOTE; - break; - - case 'q': - flags |= XFF_QUOTE; - break; - - case 't': - flags |= XFF_TRIM_WS; - break; - - case 'w': - flags |= XFF_WS; - break; - - default: - xo_failure(xop, "field descriptor uses unknown modifier: %s", - fmt); - /* - * No good answer here; a bad format will likely - * mean a core file. We just return and hope - * the caller notices there's no output, and while - * that seems, well, bad. There's nothing better. - */ + if (*sp == '\0') { + xo_failure(xop, "missing closing '}}': '%s'", + xo_printable(fmt)); return -1; } + + xfip->xfi_len = sp - xfip->xfi_start + 1; + + /* Move along the string, but don't run off the end */ + if (*sp == '}' && sp[1] == '}') + sp += 2; + cp = *sp ? sp : sp; + xfip->xfi_next = cp; + continue; } + /* We are looking at the start of a field definition */ + xfip->xfi_start = basep = cp + 1; + + const char *format = NULL; + int flen = 0; + + /* Looking at roles and modifiers */ + sp = xo_parse_roles(xop, fmt, basep, xfip); + if (sp == NULL) { + /* xo_failure has already been called */ + return -1; + } + + if (xfip->xfi_fnum) + seen_fnum = 1; + + /* Looking at content */ if (*sp == ':') { for (ep = ++sp; *sp; sp++) { if (*sp == '}' || *sp == '/') @@ -4081,14 +5221,15 @@ xo_do_emit (xo_handle_t *xop, const char *fmt) } } if (ep != sp) { - clen = sp - ep; - content = ep; + xfip->xfi_clen = sp - ep; + xfip->xfi_content = ep; } } else { - xo_failure(xop, "missing content (':'): %s", fmt); + xo_failure(xop, "missing content (':'): '%s'", xo_printable(fmt)); return -1; } + /* Looking at main (display) format */ if (*sp == '/') { for (ep = ++sp; *sp; sp++) { if (*sp == '}' || *sp == '/') @@ -4106,95 +5247,696 @@ xo_do_emit (xo_handle_t *xop, const char *fmt) format = ep; } + /* Looking at encoding format */ if (*sp == '/') { for (ep = ++sp; *sp; sp++) { if (*sp == '}') break; } - elen = sp - ep; - encoding = ep; + + xfip->xfi_encoding = ep; + xfip->xfi_elen = sp - ep; } - if (*sp == '}') { - sp += 1; - } else { - xo_failure(xop, "missing closing '}': %s", fmt); + if (*sp != '}') { + xo_failure(xop, "missing closing '}': %s", xo_printable(fmt)); return -1; } - if (ftype == 0 || ftype == 'V') { - if (format == NULL) { - /* Default format for value fields is '%s' */ - format = "%s"; - flen = 2; + xfip->xfi_len = sp - xfip->xfi_start; + xfip->xfi_next = ++sp; + + /* If we have content, then we have a default format */ + if (xfip->xfi_clen || format) { + if (format) { + xfip->xfi_format = format; + xfip->xfi_flen = flen; + } else if (xo_role_wants_default_format(xfip->xfi_ftype)) { + xfip->xfi_format = default_format; + xfip->xfi_flen = 2; } - - xo_format_value(xop, content, clen, format, flen, - encoding, elen, flags); - - } else if (ftype == '[') - xo_anchor_start(xop, content, clen, format, flen); - else if (ftype == ']') - xo_anchor_stop(xop, content, clen, format, flen); - else if (ftype == 'C') - xo_format_colors(xop, content, clen, format, flen); - - else if (clen || format) { /* Need either content or format */ - if (format == NULL) { - /* Default format for value fields is '%s' */ - format = "%s"; - flen = 2; - } - - if (ftype == 'D') - xo_format_content(xop, "decoration", NULL, 1, - content, clen, format, flen); - else if (ftype == 'E') - xo_format_content(xop, "error", "error", 0, - content, clen, format, flen); - else if (ftype == 'L') - xo_format_content(xop, "label", NULL, 1, - content, clen, format, flen); - else if (ftype == 'N') - xo_format_content(xop, "note", NULL, 1, - content, clen, format, flen); - else if (ftype == 'P') - xo_format_content(xop, "padding", NULL, 1, - content, clen, format, flen); - else if (ftype == 'T') - xo_format_title(xop, content, clen, format, flen); - else if (ftype == 'U') { - if (flags & XFF_WS) - xo_format_content(xop, "padding", NULL, 1, " ", 1, NULL, 0); - xo_format_units(xop, content, clen, format, flen); - } else if (ftype == 'W') - xo_format_content(xop, "warning", "warning", 0, - content, clen, format, flen); } - if (flags & XFF_COLON) - xo_format_content(xop, "decoration", NULL, 1, ":", 1, NULL, 0); - if (ftype != 'U' && (flags & XFF_WS)) - xo_format_content(xop, "padding", NULL, 1, " ", 1, NULL, 0); + cp = sp; + } - cp += sp - basep + 1; - if (newp) { - xo_free(newp); - newp = NULL; + int rc = 0; + + /* + * If we saw a field number on at least one field, then we need + * to enforce some rules and/or guidelines. + */ + if (seen_fnum) + rc = xo_parse_field_numbers(xop, fmt, fields, field); + + return rc; +} + +/* + * We are passed a pointer to a format string just past the "{G:}" + * field. We build a simplified version of the format string. + */ +static int +xo_gettext_simplify_format (xo_handle_t *xop UNUSED, + xo_buffer_t *xbp, + xo_field_info_t *fields, + int this_field, + const char *fmt UNUSED, + xo_simplify_field_func_t field_cb) +{ + unsigned ftype; + xo_xff_flags_t flags; + int field = this_field + 1; + xo_field_info_t *xfip; + char ch; + + for (xfip = &fields[field]; xfip->xfi_ftype; xfip++, field++) { + ftype = xfip->xfi_ftype; + flags = xfip->xfi_flags; + + if ((flags & XFF_GT_FIELD) && xfip->xfi_content && ftype != 'V') { + if (field_cb) + field_cb(xfip->xfi_content, xfip->xfi_clen, + (flags & XFF_GT_PLURAL) ? 1 : 0); + } + + switch (ftype) { + case 'G': + /* Ignore gettext roles */ + break; + + case XO_ROLE_NEWLINE: + xo_buf_append(xbp, "\n", 1); + break; + + case XO_ROLE_EBRACE: + xo_buf_append(xbp, "{", 1); + xo_buf_append(xbp, xfip->xfi_content, xfip->xfi_clen); + xo_buf_append(xbp, "}", 1); + break; + + case XO_ROLE_TEXT: + xo_buf_append(xbp, xfip->xfi_content, xfip->xfi_clen); + break; + + default: + xo_buf_append(xbp, "{", 1); + if (ftype != 'V') { + ch = ftype; + xo_buf_append(xbp, &ch, 1); + } + + unsigned fnum = xfip->xfi_fnum ?: 0; + if (fnum) { + char num[12]; + /* Field numbers are origin 1, not 0, following printf(3) */ + snprintf(num, sizeof(num), "%u", fnum); + xo_buf_append(xbp, num, strlen(num)); + } + + xo_buf_append(xbp, ":", 1); + xo_buf_append(xbp, xfip->xfi_content, xfip->xfi_clen); + xo_buf_append(xbp, "}", 1); } } + xo_buf_append(xbp, "", 1); + return 0; +} + +void +xo_dump_fields (xo_field_info_t *); /* Fake prototype for debug function */ +void +xo_dump_fields (xo_field_info_t *fields) +{ + xo_field_info_t *xfip; + + for (xfip = fields; xfip->xfi_ftype; xfip++) { + printf("%lu(%u): %lx [%c/%u] [%.*s] [%.*s] [%.*s]\n", + (unsigned long) (xfip - fields), xfip->xfi_fnum, + (unsigned long) xfip->xfi_flags, + isprint((int) xfip->xfi_ftype) ? xfip->xfi_ftype : ' ', + xfip->xfi_ftype, + xfip->xfi_clen, xfip->xfi_content ?: "", + xfip->xfi_flen, xfip->xfi_format ?: "", + xfip->xfi_elen, xfip->xfi_encoding ?: ""); + } +} + +#ifdef HAVE_GETTEXT +/* + * Find the field that matches the given field number + */ +static xo_field_info_t * +xo_gettext_find_field (xo_field_info_t *fields, unsigned fnum) +{ + xo_field_info_t *xfip; + + for (xfip = fields; xfip->xfi_ftype; xfip++) + if (xfip->xfi_fnum == fnum) + return xfip; + + return NULL; +} + +/* + * At this point, we need to consider if the fields have been reordered, + * such as "The {:adjective} {:noun}" to "La {:noun} {:adjective}". + * + * We need to rewrite the new_fields using the old fields order, + * so that we can render the message using the arguments as they + * appear on the stack. It's a lot of work, but we don't really + * want to (eventually) fall into the standard printf code which + * means using the arguments straight (and in order) from the + * varargs we were originally passed. + */ +static void +xo_gettext_rewrite_fields (xo_handle_t *xop UNUSED, + xo_field_info_t *fields, unsigned max_fields) +{ + xo_field_info_t tmp[max_fields]; + bzero(tmp, max_fields * sizeof(tmp[0])); + + unsigned fnum = 0; + xo_field_info_t *newp, *outp, *zp; + for (newp = fields, outp = tmp; newp->xfi_ftype; newp++, outp++) { + switch (newp->xfi_ftype) { + case XO_ROLE_NEWLINE: /* Don't get numbered */ + case XO_ROLE_TEXT: + case XO_ROLE_EBRACE: + case 'G': + *outp = *newp; + outp->xfi_renum = 0; + continue; + } + + zp = xo_gettext_find_field(fields, ++fnum); + if (zp == NULL) { /* Should not occur */ + *outp = *newp; + outp->xfi_renum = 0; + continue; + } + + *outp = *zp; + outp->xfi_renum = newp->xfi_fnum; + } + + memcpy(fields, tmp, max_fields * sizeof(tmp[0])); +} + +/* + * We've got two lists of fields, the old list from the original + * format string and the new one from the parsed gettext reply. The + * new list has the localized words, where the old list has the + * formatting information. We need to combine them into a single list + * (the new list). + * + * If the list needs to be reordered, then we've got more serious work + * to do. + */ +static int +xo_gettext_combine_formats (xo_handle_t *xop, const char *fmt UNUSED, + const char *gtfmt, xo_field_info_t *old_fields, + xo_field_info_t *new_fields, unsigned new_max_fields, + int *reorderedp) +{ + int reordered = 0; + xo_field_info_t *newp, *oldp, *startp = old_fields; + + xo_gettext_finish_numbering_fields(xop, fmt, old_fields); + + for (newp = new_fields; newp->xfi_ftype; newp++) { + switch (newp->xfi_ftype) { + case XO_ROLE_NEWLINE: + case XO_ROLE_TEXT: + case XO_ROLE_EBRACE: + continue; + + case 'V': + for (oldp = startp; oldp->xfi_ftype; oldp++) { + if (oldp->xfi_ftype != 'V') + continue; + if (newp->xfi_clen != oldp->xfi_clen + || strncmp(newp->xfi_content, oldp->xfi_content, + oldp->xfi_clen) != 0) { + reordered = 1; + continue; + } + startp = oldp + 1; + break; + } + + /* Didn't find it on the first pass (starting from start) */ + if (oldp->xfi_ftype == 0) { + for (oldp = old_fields; oldp < startp; oldp++) { + if (oldp->xfi_ftype != 'V') + continue; + if (newp->xfi_clen != oldp->xfi_clen) + continue; + if (strncmp(newp->xfi_content, oldp->xfi_content, + oldp->xfi_clen) != 0) + continue; + reordered = 1; + break; + } + if (oldp == startp) { + /* Field not found */ + xo_failure(xop, "post-gettext format can't find field " + "'%.*s' in format '%s'", + newp->xfi_clen, newp->xfi_content, + xo_printable(gtfmt)); + return -1; + } + } + break; + + default: + /* + * Other fields don't have names for us to use, so if + * the types aren't the same, then we'll have to assume + * the original field is a match. + */ + for (oldp = startp; oldp->xfi_ftype; oldp++) { + if (oldp->xfi_ftype == 'V') /* Can't go past these */ + break; + if (oldp->xfi_ftype == newp->xfi_ftype) + goto copy_it; /* Assumably we have a match */ + } + continue; + } + + /* + * Found a match; copy over appropriate fields + */ + copy_it: + newp->xfi_flags = oldp->xfi_flags; + newp->xfi_fnum = oldp->xfi_fnum; + newp->xfi_format = oldp->xfi_format; + newp->xfi_flen = oldp->xfi_flen; + newp->xfi_encoding = oldp->xfi_encoding; + newp->xfi_elen = oldp->xfi_elen; + } + + *reorderedp = reordered; + if (reordered) { + xo_gettext_finish_numbering_fields(xop, fmt, new_fields); + xo_gettext_rewrite_fields(xop, new_fields, new_max_fields); + } + + return 0; +} + +/* + * We don't want to make gettext() calls here with a complete format + * string, since that means changing a flag would mean a + * labor-intensive re-translation expense. Instead we build a + * simplified form with a reduced level of detail, perform a lookup on + * that string and then re-insert the formating info. + * + * So something like: + * xo_emit("{G:}close {:fd/%ld} returned {g:error/%m} {:test/%6.6s}\n", ...) + * would have a lookup string of: + * "close {:fd} returned {:error} {:test}\n" + * + * We also need to handling reordering of fields, where the gettext() + * reply string uses fields in a different order than the original + * format string: + * "cluse-a {:fd} retoorned {:test}. Bork {:error} Bork. Bork.\n" + * If we have to reorder fields within the message, then things get + * complicated. See xo_gettext_rewrite_fields. + * + * Summary: i18n aighn't cheap. + */ +static const char * +xo_gettext_build_format (xo_handle_t *xop UNUSED, + xo_field_info_t *fields UNUSED, + int this_field UNUSED, + const char *fmt, char **new_fmtp) +{ + if (xo_style_is_encoding(xop)) + goto bail; + + xo_buffer_t xb; + xo_buf_init(&xb); + + if (xo_gettext_simplify_format(xop, &xb, fields, + this_field, fmt, NULL)) + goto bail2; + + const char *gtfmt = xo_dgettext(xop, xb.xb_bufp); + if (gtfmt == NULL || gtfmt == fmt || strcmp(gtfmt, fmt) == 0) + goto bail2; + + xo_buf_cleanup(&xb); + + char *new_fmt = xo_strndup(gtfmt, -1); + if (new_fmt == NULL) + goto bail2; + + *new_fmtp = new_fmt; + return new_fmt; + + bail2: + xo_buf_cleanup(&xb); + bail: + *new_fmtp = NULL; + return fmt; +} + +static void +xo_gettext_rebuild_content (xo_handle_t *xop, xo_field_info_t *fields, + unsigned *fstart, unsigned min_fstart, + unsigned *fend, unsigned max_fend) +{ + xo_field_info_t *xfip; + char *buf; + unsigned base = fstart[min_fstart]; + unsigned blen = fend[max_fend] - base; + xo_buffer_t *xbp = &xop->xo_data; + + if (blen == 0) + return; + + buf = xo_realloc(NULL, blen); + if (buf == NULL) + return; + + memcpy(buf, xbp->xb_bufp + fstart[min_fstart], blen); /* Copy our data */ + + unsigned field = min_fstart, soff, doff = base, len, fnum; + xo_field_info_t *zp; + + /* + * Be aware there are two competing views of "field number": we + * want the user to thing in terms of "The {1:size}" where {G:}, + * newlines, escaped braces, and text don't have numbers. But is + * also the internal view, where we have an array of + * xo_field_info_t and every field have an index. fnum, fstart[] + * and fend[] are the latter, but xfi_renum is the former. + */ + for (xfip = fields + field; xfip->xfi_ftype; xfip++, field++) { + fnum = field; + if (xfip->xfi_renum) { + zp = xo_gettext_find_field(fields, xfip->xfi_renum); + fnum = zp ? zp - fields : field; + } + + soff = fstart[fnum]; + len = fend[fnum] - soff; + + if (len > 0) { + soff -= base; + memcpy(xbp->xb_bufp + doff, buf + soff, len); + doff += len; + } + } + + xo_free(buf); +} +#else /* HAVE_GETTEXT */ +static const char * +xo_gettext_build_format (xo_handle_t *xop UNUSED, + xo_field_info_t *fields UNUSED, + int this_field UNUSED, + const char *fmt UNUSED, char **new_fmtp) +{ + *new_fmtp = NULL; + return fmt; +} + +static int +xo_gettext_combine_formats (xo_handle_t *xop UNUSED, const char *fmt UNUSED, + const char *gtfmt UNUSED, + xo_field_info_t *old_fields UNUSED, + xo_field_info_t *new_fields UNUSED, + unsigned new_max_fields UNUSED, + int *reorderedp UNUSED) +{ + return -1; +} + +static void +xo_gettext_rebuild_content (xo_handle_t *xop UNUSED, + xo_field_info_t *fields UNUSED, + unsigned *fstart UNUSED, unsigned min_fstart UNUSED, + unsigned *fend UNUSED, unsigned max_fend UNUSED) +{ + return; +} +#endif /* HAVE_GETTEXT */ + +/* + * The central function for emitting libxo output. + */ +static int +xo_do_emit (xo_handle_t *xop, const char *fmt) +{ + int gettext_inuse = 0; + int gettext_changed = 0; + int gettext_reordered = 0; + xo_field_info_t *new_fields = NULL; + + int rc = 0; + int flush = XOF_ISSET(xop, XOF_FLUSH); + int flush_line = XOF_ISSET(xop, XOF_FLUSH_LINE); + char *new_fmt = NULL; + + if (XOIF_ISSET(xop, XOIF_REORDER) || xo_style(xop) == XO_STYLE_ENCODER) + flush_line = 0; + + xop->xo_columns = 0; /* Always reset it */ + xop->xo_errno = errno; /* Save for "%m" */ + + unsigned max_fields = xo_count_fields(xop, fmt), field; + xo_field_info_t fields[max_fields], *xfip; + + bzero(fields, max_fields * sizeof(fields[0])); + + if (xo_parse_fields(xop, fields, max_fields, fmt)) + return -1; /* Warning already displayed */ + + unsigned ftype; + xo_xff_flags_t flags; + + /* + * Some overhead for gettext; if the fields in the msgstr returned + * by gettext are reordered, then we need to record start and end + * for each field. We'll go ahead and render the fields in the + * normal order, but later we can then reconstruct the reordered + * fields using these fstart/fend values. + */ + unsigned flimit = max_fields * 2; /* Pessimistic limit */ + unsigned min_fstart = flimit - 1; + unsigned max_fend = 0; /* Highest recorded fend[] entry */ + unsigned fstart[flimit]; + bzero(fstart, flimit * sizeof(fstart[0])); + unsigned fend[flimit]; + bzero(fend, flimit * sizeof(fend[0])); + + for (xfip = fields, field = 0; xfip->xfi_ftype && field < max_fields; + xfip++, field++) { + ftype = xfip->xfi_ftype; + flags = xfip->xfi_flags; + + /* Record field start offset */ + if (gettext_reordered) { + fstart[field] = xo_buf_offset(&xop->xo_data); + if (min_fstart > field) + min_fstart = field; + } + + if (ftype == XO_ROLE_NEWLINE) { + xo_line_close(xop); + if (flush_line && xo_flush_h(xop) < 0) + return -1; + goto bottom; + + } else if (ftype == XO_ROLE_EBRACE) { + xo_format_text(xop, xfip->xfi_start, xfip->xfi_len); + goto bottom; + + } else if (ftype == XO_ROLE_TEXT) { + /* Normal text */ + xo_format_text(xop, xfip->xfi_content, xfip->xfi_clen); + goto bottom; + } + + /* + * Notes and units need the 'w' flag handled before the content. + */ + if (ftype == 'N' || ftype == 'U') { + if (flags & XFF_WS) { + xo_format_content(xop, "padding", NULL, " ", 1, + NULL, 0, flags); + flags &= ~XFF_WS; /* Block later handling of this */ + } + } + + if (ftype == 'V') + xo_format_value(xop, xfip->xfi_content, xfip->xfi_clen, + xfip->xfi_format, xfip->xfi_flen, + xfip->xfi_encoding, xfip->xfi_elen, flags); + else if (ftype == '[') + xo_anchor_start(xop, xfip); + else if (ftype == ']') + xo_anchor_stop(xop, xfip); + else if (ftype == 'C') + xo_format_colors(xop, xfip); + + else if (ftype == 'G') { + /* + * A {G:domain} field; disect the domain name and translate + * the remaining portion of the input string. If the user + * didn't put the {G:} at the start of the format string, then + * assumably they just want us to translate the rest of it. + * Since gettext returns strings in a static buffer, we make + * a copy in new_fmt. + */ + xo_set_gettext_domain(xop, xfip); + + if (!gettext_inuse) { /* Only translate once */ + gettext_inuse = 1; + if (new_fmt) { + xo_free(new_fmt); + new_fmt = NULL; + } + + xo_gettext_build_format(xop, fields, field, + xfip->xfi_next, &new_fmt); + if (new_fmt) { + gettext_changed = 1; + + unsigned new_max_fields = xo_count_fields(xop, new_fmt); + + if (++new_max_fields < max_fields) + new_max_fields = max_fields; + + /* Leave a blank slot at the beginning */ + int sz = (new_max_fields + 1) * sizeof(xo_field_info_t); + new_fields = alloca(sz); + bzero(new_fields, sz); + + if (!xo_parse_fields(xop, new_fields + 1, + new_max_fields, new_fmt)) { + gettext_reordered = 0; + + if (!xo_gettext_combine_formats(xop, fmt, new_fmt, + fields, new_fields + 1, + new_max_fields, &gettext_reordered)) { + + if (gettext_reordered) { + if (XOF_ISSET(xop, XOF_LOG_GETTEXT)) + xo_failure(xop, "gettext finds reordered " + "fields in '%s' and '%s'", + xo_printable(fmt), + xo_printable(new_fmt)); + flush_line = 0; /* Must keep at content */ + XOIF_SET(xop, XOIF_REORDER); + } + + field = -1; /* Will be incremented at top of loop */ + xfip = new_fields; + max_fields = new_max_fields; + } + } + } + } + continue; + + } else if (xfip->xfi_clen || xfip->xfi_format) { + + const char *class_name = xo_class_name(ftype); + if (class_name) + xo_format_content(xop, class_name, xo_tag_name(ftype), + xfip->xfi_content, xfip->xfi_clen, + xfip->xfi_format, xfip->xfi_flen, flags); + else if (ftype == 'T') + xo_format_title(xop, xfip); + else if (ftype == 'U') + xo_format_units(xop, xfip); + else + xo_failure(xop, "unknown field type: '%c'", ftype); + } + + if (flags & XFF_COLON) + xo_format_content(xop, "decoration", NULL, ":", 1, NULL, 0, 0); + + if (flags & XFF_WS) + xo_format_content(xop, "padding", NULL, " ", 1, NULL, 0, 0); + + bottom: + /* Record the end-of-field offset */ + if (gettext_reordered) { + fend[field] = xo_buf_offset(&xop->xo_data); + max_fend = field; + } + } + + if (gettext_changed && gettext_reordered) { + /* Final step: rebuild the content using the rendered fields */ + xo_gettext_rebuild_content(xop, new_fields + 1, fstart, min_fstart, + fend, max_fend); + } + + XOIF_CLEAR(xop, XOIF_REORDER); + /* If we don't have an anchor, write the text out */ - if (flush && !(xop->xo_flags & XOF_ANCHOR)) { + if (flush && !XOIF_ISSET(xop, XOIF_ANCHOR)) { if (xo_write(xop) < 0) rc = -1; /* Report failure */ else if (xop->xo_flush && xop->xo_flush(xop->xo_opaque) < 0) rc = -1; } + if (new_fmt) + xo_free(new_fmt); + + /* + * We've carried the gettext domainname inside our handle just for + * convenience, but we need to ensure it doesn't survive across + * xo_emit calls. + */ + if (xop->xo_gt_domain) { + xo_free(xop->xo_gt_domain); + xop->xo_gt_domain = NULL; + } + return (rc < 0) ? rc : (int) xop->xo_columns; } +/* + * Rebuild a format string in a gettext-friendly format. This function + * is exposed to tools can perform this function. See xo(1). + */ +char * +xo_simplify_format (xo_handle_t *xop, const char *fmt, int with_numbers, + xo_simplify_field_func_t field_cb) +{ + xop = xo_default(xop); + + xop->xo_columns = 0; /* Always reset it */ + xop->xo_errno = errno; /* Save for "%m" */ + + unsigned max_fields = xo_count_fields(xop, fmt); + xo_field_info_t fields[max_fields]; + + bzero(fields, max_fields * sizeof(fields[0])); + + if (xo_parse_fields(xop, fields, max_fields, fmt)) + return NULL; /* Warning already displayed */ + + xo_buffer_t xb; + xo_buf_init(&xb); + + if (with_numbers) + xo_gettext_finish_numbering_fields(xop, fmt, fields); + + if (xo_gettext_simplify_format(xop, &xb, fields, -1, fmt, field_cb)) + return NULL; + + return xb.xb_bufp; +} + int xo_emit_hv (xo_handle_t *xop, const char *fmt, va_list vap) { @@ -4243,35 +5985,55 @@ xo_attr_hv (xo_handle_t *xop, const char *name, const char *fmt, va_list vap) const int extra = 5; /* space, equals, quote, quote, and nul */ xop = xo_default(xop); - if (xo_style(xop) != XO_STYLE_XML) - return 0; - + int rc = 0; int nlen = strlen(name); xo_buffer_t *xbp = &xop->xo_attrs; + unsigned name_offset, value_offset; - if (!xo_buf_has_room(xbp, nlen + extra)) - return -1; + switch (xo_style(xop)) { + case XO_STYLE_XML: + if (!xo_buf_has_room(xbp, nlen + extra)) + return -1; - *xbp->xb_curp++ = ' '; - memcpy(xbp->xb_curp, name, nlen); - xbp->xb_curp += nlen; - *xbp->xb_curp++ = '='; - *xbp->xb_curp++ = '"'; + *xbp->xb_curp++ = ' '; + memcpy(xbp->xb_curp, name, nlen); + xbp->xb_curp += nlen; + *xbp->xb_curp++ = '='; + *xbp->xb_curp++ = '"'; - int rc = xo_vsnprintf(xop, xbp, fmt, vap); + rc = xo_vsnprintf(xop, xbp, fmt, vap); - if (rc > 0) { - rc = xo_escape_xml(xbp, rc, 1); - xbp->xb_curp += rc; + if (rc >= 0) { + rc = xo_escape_xml(xbp, rc, 1); + xbp->xb_curp += rc; + } + + if (!xo_buf_has_room(xbp, 2)) + return -1; + + *xbp->xb_curp++ = '"'; + *xbp->xb_curp = '\0'; + + rc += nlen + extra; + break; + + case XO_STYLE_ENCODER: + name_offset = xo_buf_offset(xbp); + xo_buf_append(xbp, name, nlen); + xo_buf_append(xbp, "", 1); + + value_offset = xo_buf_offset(xbp); + rc = xo_vsnprintf(xop, xbp, fmt, vap); + if (rc >= 0) { + xbp->xb_curp += rc; + *xbp->xb_curp = '\0'; + rc = xo_encoder_handle(xop, XO_OP_ATTRIBUTE, + xo_buf_data(xbp, name_offset), + xo_buf_data(xbp, value_offset)); + } } - if (!xo_buf_has_room(xbp, 2)) - return -1; - - *xbp->xb_curp++ = '"'; - *xbp->xb_curp = '\0'; - - return rc + nlen + extra; + return rc; } int @@ -4303,11 +6065,11 @@ xo_attr (const char *name, const char *fmt, ...) static void xo_stack_set_flags (xo_handle_t *xop) { - if (xop->xo_flags & XOF_NOT_FIRST) { + if (XOF_ISSET(xop, XOF_NOT_FIRST)) { xo_stack_t *xsp = &xop->xo_stack[xop->xo_depth]; xsp->xs_flags |= XSF_NOT_FIRST; - xop->xo_flags &= ~XOF_NOT_FIRST; + XOF_CLEAR(xop, XOF_NOT_FIRST); } } @@ -4318,7 +6080,7 @@ xo_depth_change (xo_handle_t *xop, const char *name, if (xo_style(xop) == XO_STYLE_HTML || xo_style(xop) == XO_STYLE_TEXT) indent = 0; - if (xop->xo_flags & XOF_DTRT) + if (XOF_ISSET(xop, XOF_DTRT)) flags |= XSF_DTRT; if (delta >= 0) { /* Push operation */ @@ -4333,22 +6095,17 @@ xo_depth_change (xo_handle_t *xop, const char *name, if (name == NULL) name = XO_FAILURE_NAME; - int len = strlen(name) + 1; - char *cp = xo_realloc(NULL, len); - if (cp) { - memcpy(cp, name, len); - xsp->xs_name = cp; - } + xsp->xs_name = xo_strndup(name, -1); } else { /* Pop operation */ if (xop->xo_depth == 0) { - if (!(xop->xo_flags & XOF_IGNORE_CLOSE)) + if (!XOF_ISSET(xop, XOF_IGNORE_CLOSE)) xo_failure(xop, "close with empty stack: '%s'", name); return; } xo_stack_t *xsp = &xop->xo_stack[xop->xo_depth]; - if (xop->xo_flags & XOF_WARN) { + if (XOF_ISSET(xop, XOF_WARN)) { const char *top = xsp->xs_name; if (top && strcmp(name, top) != 0) { xo_failure(xop, "incorrect close: '%s' .vs. '%s'", @@ -4405,7 +6162,7 @@ static void xo_emit_top (xo_handle_t *xop, const char *ppn) { xo_printf(xop, "%*s{%s", xo_indent(xop), "", ppn); - xop->xo_flags |= XOF_TOP_EMITTED; + XOIF_SET(xop, XOIF_TOP_EMITTED); if (xop->xo_version) { xo_printf(xop, "%*s\"__version\": \"%s\", %s", @@ -4419,7 +6176,7 @@ static int xo_do_open_container (xo_handle_t *xop, xo_xof_flags_t flags, const char *name) { int rc = 0; - const char *ppn = (xop->xo_flags & XOF_PRETTY) ? "\n" : ""; + const char *ppn = XOF_ISSET(xop, XOF_PRETTY) ? "\n" : ""; const char *pre_nl = ""; if (name == NULL) { @@ -4446,17 +6203,24 @@ xo_do_open_container (xo_handle_t *xop, xo_xof_flags_t flags, const char *name) case XO_STYLE_JSON: xo_stack_set_flags(xop); - if (!(xop->xo_flags & XOF_NO_TOP) - && !(xop->xo_flags & XOF_TOP_EMITTED)) + if (!XOF_ISSET(xop, XOF_NO_TOP) + && !XOIF_ISSET(xop, XOIF_TOP_EMITTED)) xo_emit_top(xop, ppn); if (xop->xo_stack[xop->xo_depth].xs_flags & XSF_NOT_FIRST) - pre_nl = (xop->xo_flags & XOF_PRETTY) ? ",\n" : ", "; + pre_nl = XOF_ISSET(xop, XOF_PRETTY) ? ",\n" : ", "; xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST; rc = xo_printf(xop, "%s%*s\"%s\": {%s", pre_nl, xo_indent(xop), "", name, ppn); break; + + case XO_STYLE_SDPARAMS: + break; + + case XO_STYLE_ENCODER: + rc = xo_encoder_handle(xop, XO_OP_OPEN_CONTAINER, name, NULL); + break; } xo_depth_change(xop, name, 1, 1, XSS_OPEN_CONTAINER, @@ -4501,7 +6265,7 @@ xo_do_close_container (xo_handle_t *xop, const char *name) xop = xo_default(xop); int rc = 0; - const char *ppn = (xop->xo_flags & XOF_PRETTY) ? "\n" : ""; + const char *ppn = XOF_ISSET(xop, XOF_PRETTY) ? "\n" : ""; const char *pre_nl = ""; if (name == NULL) { @@ -4527,7 +6291,7 @@ xo_do_close_container (xo_handle_t *xop, const char *name) break; case XO_STYLE_JSON: - pre_nl = (xop->xo_flags & XOF_PRETTY) ? "\n" : ""; + pre_nl = XOF_ISSET(xop, XOF_PRETTY) ? "\n" : ""; ppn = (xop->xo_depth <= 1) ? "\n" : ""; xo_depth_change(xop, name, -1, -1, XSS_CLOSE_CONTAINER, 0); @@ -4539,6 +6303,14 @@ xo_do_close_container (xo_handle_t *xop, const char *name) case XO_STYLE_TEXT: xo_depth_change(xop, name, -1, 0, XSS_CLOSE_CONTAINER, 0); break; + + case XO_STYLE_SDPARAMS: + break; + + case XO_STYLE_ENCODER: + xo_depth_change(xop, name, -1, 0, XSS_CLOSE_CONTAINER, 0); + rc = xo_encoder_handle(xop, XO_OP_CLOSE_CONTAINER, name, NULL); + break; } return rc; @@ -4576,13 +6348,15 @@ xo_do_open_list (xo_handle_t *xop, xo_xsf_flags_t flags, const char *name) xop = xo_default(xop); - if (xo_style(xop) == XO_STYLE_JSON) { - const char *ppn = (xop->xo_flags & XOF_PRETTY) ? "\n" : ""; - const char *pre_nl = ""; + const char *ppn = XOF_ISSET(xop, XOF_PRETTY) ? "\n" : ""; + const char *pre_nl = ""; + + switch (xo_style(xop)) { + case XO_STYLE_JSON: indent = 1; - if (!(xop->xo_flags & XOF_NO_TOP) - && !(xop->xo_flags & XOF_TOP_EMITTED)) + if (!XOF_ISSET(xop, XOF_NO_TOP) + && !XOIF_ISSET(xop, XOIF_TOP_EMITTED)) xo_emit_top(xop, ppn); if (name == NULL) { @@ -4593,11 +6367,16 @@ xo_do_open_list (xo_handle_t *xop, xo_xsf_flags_t flags, const char *name) xo_stack_set_flags(xop); if (xop->xo_stack[xop->xo_depth].xs_flags & XSF_NOT_FIRST) - pre_nl = (xop->xo_flags & XOF_PRETTY) ? ",\n" : ", "; + pre_nl = XOF_ISSET(xop, XOF_PRETTY) ? ",\n" : ", "; xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST; rc = xo_printf(xop, "%s%*s\"%s\": [%s", pre_nl, xo_indent(xop), "", name, ppn); + break; + + case XO_STYLE_ENCODER: + rc = xo_encoder_handle(xop, XO_OP_OPEN_LIST, name, NULL); + break; } xo_depth_change(xop, name, 1, indent, XSS_OPEN_LIST, @@ -4658,18 +6437,26 @@ xo_do_close_list (xo_handle_t *xop, const char *name) } } - if (xo_style(xop) == XO_STYLE_JSON) { + switch (xo_style(xop)) { + case XO_STYLE_JSON: if (xop->xo_stack[xop->xo_depth].xs_flags & XSF_NOT_FIRST) - pre_nl = (xop->xo_flags & XOF_PRETTY) ? "\n" : ""; + pre_nl = XOF_ISSET(xop, XOF_PRETTY) ? "\n" : ""; xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST; xo_depth_change(xop, name, -1, -1, XSS_CLOSE_LIST, XSF_LIST); rc = xo_printf(xop, "%s%*s]", pre_nl, xo_indent(xop), ""); xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST; + break; - } else { + case XO_STYLE_ENCODER: + xo_depth_change(xop, name, -1, 0, XSS_CLOSE_LIST, XSF_LIST); + rc = xo_encoder_handle(xop, XO_OP_CLOSE_LIST, name, NULL); + break; + + default: xo_depth_change(xop, name, -1, 0, XSS_CLOSE_LIST, XSF_LIST); xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST; + break; } return rc; @@ -4707,16 +6494,17 @@ xo_do_open_leaf_list (xo_handle_t *xop, xo_xsf_flags_t flags, const char *name) xop = xo_default(xop); - if (xo_style(xop) == XO_STYLE_JSON) { - const char *ppn = (xop->xo_flags & XOF_PRETTY) ? "\n" : ""; - const char *pre_nl = ""; + const char *ppn = XOF_ISSET(xop, XOF_PRETTY) ? "\n" : ""; + const char *pre_nl = ""; + switch (xo_style(xop)) { + case XO_STYLE_JSON: indent = 1; - if (!(xop->xo_flags & XOF_NO_TOP)) { - if (!(xop->xo_flags & XOF_TOP_EMITTED)) { + if (!XOF_ISSET(xop, XOF_NO_TOP)) { + if (!XOIF_ISSET(xop, XOIF_TOP_EMITTED)) { xo_printf(xop, "%*s{%s", xo_indent(xop), "", ppn); - xop->xo_flags |= XOF_TOP_EMITTED; + XOIF_SET(xop, XOIF_TOP_EMITTED); } } @@ -4728,11 +6516,16 @@ xo_do_open_leaf_list (xo_handle_t *xop, xo_xsf_flags_t flags, const char *name) xo_stack_set_flags(xop); if (xop->xo_stack[xop->xo_depth].xs_flags & XSF_NOT_FIRST) - pre_nl = (xop->xo_flags & XOF_PRETTY) ? ",\n" : ", "; + pre_nl = XOF_ISSET(xop, XOF_PRETTY) ? ",\n" : ", "; xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST; rc = xo_printf(xop, "%s%*s\"%s\": [%s", pre_nl, xo_indent(xop), "", name, ppn); + break; + + case XO_STYLE_ENCODER: + rc = xo_encoder_handle(xop, XO_OP_OPEN_LEAF_LIST, name, NULL); + break; } xo_depth_change(xop, name, 1, indent, XSS_OPEN_LEAF_LIST, @@ -4763,18 +6556,25 @@ xo_do_close_leaf_list (xo_handle_t *xop, const char *name) } } - if (xo_style(xop) == XO_STYLE_JSON) { + switch (xo_style(xop)) { + case XO_STYLE_JSON: if (xop->xo_stack[xop->xo_depth].xs_flags & XSF_NOT_FIRST) - pre_nl = (xop->xo_flags & XOF_PRETTY) ? "\n" : ""; + pre_nl = XOF_ISSET(xop, XOF_PRETTY) ? "\n" : ""; xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST; xo_depth_change(xop, name, -1, -1, XSS_CLOSE_LEAF_LIST, XSF_LIST); rc = xo_printf(xop, "%s%*s]", pre_nl, xo_indent(xop), ""); xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST; + break; - } else { + case XO_STYLE_ENCODER: + rc = xo_encoder_handle(xop, XO_OP_CLOSE_LEAF_LIST, name, NULL); + /*fallthru*/ + + default: xo_depth_change(xop, name, -1, 0, XSS_CLOSE_LEAF_LIST, XSF_LIST); xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST; + break; } return rc; @@ -4786,7 +6586,7 @@ xo_do_open_instance (xo_handle_t *xop, xo_xsf_flags_t flags, const char *name) xop = xo_default(xop); int rc = 0; - const char *ppn = (xop->xo_flags & XOF_PRETTY) ? "\n" : ""; + const char *ppn = XOF_ISSET(xop, XOF_PRETTY) ? "\n" : ""; const char *pre_nl = ""; flags |= xop->xo_flags; @@ -4814,12 +6614,19 @@ xo_do_open_instance (xo_handle_t *xop, xo_xsf_flags_t flags, const char *name) xo_stack_set_flags(xop); if (xop->xo_stack[xop->xo_depth].xs_flags & XSF_NOT_FIRST) - pre_nl = (xop->xo_flags & XOF_PRETTY) ? ",\n" : ", "; + pre_nl = XOF_ISSET(xop, XOF_PRETTY) ? ",\n" : ", "; xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST; rc = xo_printf(xop, "%s%*s{%s", pre_nl, xo_indent(xop), "", ppn); break; + + case XO_STYLE_SDPARAMS: + break; + + case XO_STYLE_ENCODER: + rc = xo_encoder_handle(xop, XO_OP_OPEN_INSTANCE, name, NULL); + break; } xo_depth_change(xop, name, 1, 1, XSS_OPEN_INSTANCE, xo_stack_flags(flags)); @@ -4863,7 +6670,7 @@ xo_do_close_instance (xo_handle_t *xop, const char *name) xop = xo_default(xop); int rc = 0; - const char *ppn = (xop->xo_flags & XOF_PRETTY) ? "\n" : ""; + const char *ppn = XOF_ISSET(xop, XOF_PRETTY) ? "\n" : ""; const char *pre_nl = ""; if (name == NULL) { @@ -4889,7 +6696,7 @@ xo_do_close_instance (xo_handle_t *xop, const char *name) break; case XO_STYLE_JSON: - pre_nl = (xop->xo_flags & XOF_PRETTY) ? "\n" : ""; + pre_nl = XOF_ISSET(xop, XOF_PRETTY) ? "\n" : ""; xo_depth_change(xop, name, -1, -1, XSS_CLOSE_INSTANCE, 0); rc = xo_printf(xop, "%s%*s}", pre_nl, xo_indent(xop), ""); @@ -4900,6 +6707,14 @@ xo_do_close_instance (xo_handle_t *xop, const char *name) case XO_STYLE_TEXT: xo_depth_change(xop, name, -1, 0, XSS_CLOSE_INSTANCE, 0); break; + + case XO_STYLE_SDPARAMS: + break; + + case XO_STYLE_ENCODER: + xo_depth_change(xop, name, -1, 0, XSS_CLOSE_INSTANCE, 0); + rc = xo_encoder_handle(xop, XO_OP_CLOSE_INSTANCE, name, NULL); + break; } return rc; @@ -5338,6 +7153,10 @@ xo_close_marker (const char *name) return xo_close_marker_h(NULL, name); } +/* + * Record custom output functions into the xo handle, allowing + * integration with a variety of output frameworks. + */ void xo_set_writer (xo_handle_t *xop, void *opaque, xo_write_func_t write_func, xo_close_func_t close_func, xo_flush_func_t flush_func) @@ -5367,14 +7186,17 @@ xo_flush_h (xo_handle_t *xop) switch (xo_style(xop)) { case XO_STYLE_HTML: - if (xop->xo_flags & XOF_DIV_OPEN) { - xop->xo_flags &= ~XOF_DIV_OPEN; + if (XOIF_ISSET(xop, XOIF_DIV_OPEN)) { + XOIF_CLEAR(xop, XOIF_DIV_OPEN); xo_data_append(xop, div_close, sizeof(div_close) - 1); - if (xop->xo_flags & XOF_PRETTY) + if (XOF_ISSET(xop, XOF_PRETTY)) xo_data_append(xop, "\n", 1); } break; + + case XO_STYLE_ENCODER: + xo_encoder_handle(xop, XO_OP_FLUSH, NULL, NULL); } rc = xo_write(xop); @@ -5397,19 +7219,23 @@ xo_finish_h (xo_handle_t *xop) const char *cp = ""; xop = xo_default(xop); - if (!(xop->xo_flags & XOF_NO_CLOSE)) + if (!XOF_ISSET(xop, XOF_NO_CLOSE)) xo_do_close_all(xop, xop->xo_stack); switch (xo_style(xop)) { case XO_STYLE_JSON: - if (!(xop->xo_flags & XOF_NO_TOP)) { - if (xop->xo_flags & XOF_TOP_EMITTED) - xop->xo_flags &= ~XOF_TOP_EMITTED; /* Turn off before output */ + if (!XOF_ISSET(xop, XOF_NO_TOP)) { + if (XOIF_ISSET(xop, XOIF_TOP_EMITTED)) + XOIF_CLEAR(xop, XOIF_TOP_EMITTED); /* Turn off before output */ else cp = "{ "; xo_printf(xop, "%*s%s}\n",xo_indent(xop), "", cp); } break; + + case XO_STYLE_ENCODER: + xo_encoder_handle(xop, XO_OP_FINISH, NULL, NULL); + break; } return xo_flush_h(xop); @@ -5421,6 +7247,16 @@ xo_finish (void) return xo_finish_h(NULL); } +/* + * xo_finish_atexit is suitable for atexit() calls, to force clear up + * and finalizing output. + */ +void +xo_finish_atexit (void) +{ + (void) xo_finish_h(NULL); +} + /* * Generate an error message, such as would be displayed on stderr */ @@ -5452,7 +7288,7 @@ xo_error_hv (xo_handle_t *xop, const char *fmt, va_list vap) xo_buf_append_div(xop, "error", 0, NULL, 0, fmt, strlen(fmt), NULL, 0); - if (xop->xo_flags & XOF_DIV_OPEN) + if (XOIF_ISSET(xop, XOIF_DIV_OPEN)) xo_line_close(xop); xo_write(xop); @@ -5472,6 +7308,10 @@ xo_error_hv (xo_handle_t *xop, const char *fmt, va_list vap) va_end(xop->xo_vap); bzero(&xop->xo_vap, sizeof(xop->xo_vap)); break; + + case XO_STYLE_SDPARAMS: + case XO_STYLE_ENCODER: + break; } } @@ -5498,6 +7338,12 @@ xo_error (const char *fmt, ...) va_end(vap); } +/* + * Parse any libxo-specific options from the command line, removing them + * so the main() argument parsing won't see them. We return the new value + * for argc or -1 for error. If an error occurred, the program should + * exit. A suitable error message has already been displayed. + */ int xo_parse_args (int argc, char **argv) { @@ -5557,6 +7403,10 @@ xo_parse_args (int argc, char **argv) return save; } +/* + * Debugging function that dumps the current stack of open libxo constructs, + * suitable for calling from the debugger. + */ void xo_dump_stack (xo_handle_t *xop) { @@ -5575,6 +7425,9 @@ xo_dump_stack (xo_handle_t *xop) } } +/* + * Record the program name used for error messages + */ void xo_set_program (const char *name) { @@ -5589,6 +7442,9 @@ xo_set_version_h (xo_handle_t *xop, const char *version UNUSED) if (version == NULL || strchr(version, '"') != NULL) return; + if (!xo_style_is_encoding(xop)) + return; + switch (xo_style(xop)) { case XO_STYLE_XML: /* For XML, we record this as an attribute for the first tag */ @@ -5596,153 +7452,208 @@ xo_set_version_h (xo_handle_t *xop, const char *version UNUSED) break; case XO_STYLE_JSON: - { - /* - * For XML, we record the version string in our handle, and emit - * it in xo_emit_top. - */ - int len = strlen(version) + 1; - xop->xo_version = xo_realloc(NULL, len); - if (xop->xo_version) - memcpy(xop->xo_version, version, len); - } + /* + * For JSON, we record the version string in our handle, and emit + * it in xo_emit_top. + */ + xop->xo_version = xo_strndup(version, -1); + break; + + case XO_STYLE_ENCODER: + xo_encoder_handle(xop, XO_OP_VERSION, NULL, version); break; } } +/* + * Set the version number for the API content being carried thru + * the xo handle. + */ void xo_set_version (const char *version) { xo_set_version_h(NULL, version); } -#ifdef UNIT_TEST -int -main (int argc, char **argv) +/* + * Generate a warning. Normally, this is a text message written to + * standard error. If the XOF_WARN_XML flag is set, then we generate + * XMLified content on standard output. + */ +void +xo_emit_warn_hcv (xo_handle_t *xop, int as_warning, int code, + const char *fmt, va_list vap) { - static char base_grocery[] = "GRO"; - static char base_hardware[] = "HRD"; - struct item { - const char *i_title; - int i_sold; - int i_instock; - int i_onorder; - const char *i_sku_base; - int i_sku_num; - }; - struct item list[] = { - { "gum&this&that", 1412, 54, 10, base_grocery, 415 }, - { "", 85, 4, 2, base_hardware, 212 }, - { "ladder", 0, 2, 1, base_hardware, 517 }, - { "\"bolt\"", 4123, 144, 42, base_hardware, 632 }, - { "water\\blue", 17, 14, 2, base_grocery, 2331 }, - { NULL, 0, 0, 0, NULL, 0 } - }; - struct item list2[] = { - { "fish", 1321, 45, 1, base_grocery, 533 }, - { NULL, 0, 0, 0, NULL, 0 } - }; - struct item *ip; - xo_info_t info[] = { - { "in-stock", "number", "Number of items in stock" }, - { "name", "string", "Name of the item" }, - { "on-order", "number", "Number of items on order" }, - { "sku", "string", "Stock Keeping Unit" }, - { "sold", "number", "Number of items sold" }, - { NULL, NULL, NULL }, - }; - int info_count = (sizeof(info) / sizeof(info[0])) - 1; - - argc = xo_parse_args(argc, argv); - if (argc < 0) - exit(1); + xop = xo_default(xop); - xo_set_info(NULL, info, info_count); + if (fmt == NULL) + return; - xo_open_container_h(NULL, "top"); + xo_open_marker_h(xop, "xo_emit_warn_hcv"); + xo_open_container_h(xop, as_warning ? "__warning" : "__error"); - xo_open_container("data"); - xo_open_list("item"); + if (xo_program) + xo_emit("{wc:program}", xo_program); - xo_emit("{T:Item/%-15s}{T:Total Sold/%12s}{T:In Stock/%12s}" - "{T:On Order/%12s}{T:SKU/%5s}\n"); + if (xo_style(xop) == XO_STYLE_XML || xo_style(xop) == XO_STYLE_JSON) { + va_list ap; + xo_handle_t temp; - for (ip = list; ip->i_title; ip++) { - xo_open_instance("item"); + bzero(&temp, sizeof(temp)); + temp.xo_style = XO_STYLE_TEXT; + xo_buf_init(&temp.xo_data); + xo_depth_check(&temp, XO_DEPTH); - xo_emit("{k:name/%-15s/%s}{n:sold/%12u/%u}{:in-stock/%12u/%u}" - "{:on-order/%12u/%u} {q:sku/%5s-000-%u/%s-000-%u}\n", - ip->i_title, ip->i_sold, ip->i_instock, ip->i_onorder, - ip->i_sku_base, ip->i_sku_num); + va_copy(ap, vap); + (void) xo_emit_hv(&temp, fmt, ap); + va_end(ap); - xo_close_instance("item"); + xo_buffer_t *src = &temp.xo_data; + xo_format_value(xop, "message", 7, src->xb_bufp, + src->xb_curp - src->xb_bufp, NULL, 0, 0); + + xo_free(temp.xo_stack); + xo_buf_cleanup(src); } - xo_close_list("item"); - xo_close_container("data"); + (void) xo_emit_hv(xop, fmt, vap); - xo_emit("\n\n"); - - xo_open_container("data"); - xo_open_list("item"); - - for (ip = list; ip->i_title; ip++) { - xo_open_instance("item"); - - xo_attr("fancy", "%s%d", "item", ip - list); - xo_emit("{L:Item} '{k:name/%s}':\n", ip->i_title); - xo_emit("{P: }{L:Total sold}: {n:sold/%u%s}{e:percent/%u}\n", - ip->i_sold, ip->i_sold ? ".0" : "", 44); - xo_emit("{P: }{Lcw:In stock}{:in-stock/%u}\n", ip->i_instock); - xo_emit("{P: }{Lcw:On order}{:on-order/%u}\n", ip->i_onorder); - xo_emit("{P: }{L:SKU}: {q:sku/%s-000-%u}\n", - ip->i_sku_base, ip->i_sku_num); - - xo_close_instance("item"); - } - - xo_close_list("item"); - xo_close_container("data"); - - xo_open_container("data"); - xo_open_list("item"); - - for (ip = list2; ip->i_title; ip++) { - xo_open_instance("item"); - - xo_emit("{L:Item} '{k:name/%s}':\n", ip->i_title); - xo_emit("{P: }{L:Total sold}: {n:sold/%u%s}\n", - ip->i_sold, ip->i_sold ? ".0" : ""); - xo_emit("{P: }{Lcw:In stock}{:in-stock/%u}\n", ip->i_instock); - xo_emit("{P: }{Lcw:On order}{:on-order/%u}\n", ip->i_onorder); - xo_emit("{P: }{L:SKU}: {q:sku/%s-000-%u}\n", - ip->i_sku_base, ip->i_sku_num); - - xo_open_list("month"); - - const char *months[] = { "Jan", "Feb", "Mar", NULL }; - int discounts[] = { 10, 20, 25, 0 }; - int i; - for (i = 0; months[i]; i++) { - xo_open_instance("month"); - xo_emit("{P: }" - "{Lwc:Month}{k:month}, {Lwc:Special}{:discount/%d}\n", - months[i], discounts[i]); - xo_close_instance("month"); + int len = strlen(fmt); + if (len > 0 && fmt[len - 1] != '\n') { + if (code > 0) { + const char *msg = strerror(code); + if (msg) + xo_emit_h(xop, ": {G:strerror}{g:error/%s}", msg); } - - xo_close_list("month"); - - xo_close_instance("item"); + xo_emit("\n"); } - xo_close_list("item"); - xo_close_container("data"); - - xo_close_container_h(NULL, "top"); - - xo_finish(); - - return 0; + xo_close_marker_h(xop, "xo_emit_warn_hcv"); + xo_flush_h(xop); +} + +void +xo_emit_warn_hc (xo_handle_t *xop, int code, const char *fmt, ...) +{ + va_list vap; + + va_start(vap, fmt); + xo_emit_warn_hcv(xop, 1, code, fmt, vap); + va_end(vap); +} + +void +xo_emit_warn_c (int code, const char *fmt, ...) +{ + va_list vap; + + va_start(vap, fmt); + xo_emit_warn_hcv(NULL, 1, code, fmt, vap); + va_end(vap); +} + +void +xo_emit_warn (const char *fmt, ...) +{ + int code = errno; + va_list vap; + + va_start(vap, fmt); + xo_emit_warn_hcv(NULL, 1, code, fmt, vap); + va_end(vap); +} + +void +xo_emit_warnx (const char *fmt, ...) +{ + va_list vap; + + va_start(vap, fmt); + xo_emit_warn_hcv(NULL, 1, -1, fmt, vap); + va_end(vap); +} + +void +xo_emit_err_v (int eval, int code, const char *fmt, va_list vap) +{ + xo_emit_warn_hcv(NULL, 0, code, fmt, vap); + xo_finish(); + exit(eval); +} + +void +xo_emit_err (int eval, const char *fmt, ...) +{ + int code = errno; + va_list vap; + va_start(vap, fmt); + xo_emit_err_v(0, code, fmt, vap); + va_end(vap); + exit(eval); +} + +void +xo_emit_errx (int eval, const char *fmt, ...) +{ + va_list vap; + + va_start(vap, fmt); + xo_emit_err_v(0, -1, fmt, vap); + va_end(vap); + xo_finish(); + exit(eval); +} + +void +xo_emit_errc (int eval, int code, const char *fmt, ...) +{ + va_list vap; + + va_start(vap, fmt); + xo_emit_warn_hcv(NULL, 0, code, fmt, vap); + va_end(vap); + xo_finish(); + exit(eval); +} + +/* + * Get the opaque private pointer for an xo handle + */ +void * +xo_get_private (xo_handle_t *xop) +{ + xop = xo_default(xop); + return xop->xo_private; +} + +/* + * Set the opaque private pointer for an xo handle. + */ +void +xo_set_private (xo_handle_t *xop, void *opaque) +{ + xop = xo_default(xop); + xop->xo_private = opaque; +} + +/* + * Get the encoder function + */ +xo_encoder_func_t +xo_get_encoder (xo_handle_t *xop) +{ + xop = xo_default(xop); + return xop->xo_encoder; +} + +/* + * Record an encoder callback function in an xo handle. + */ +void +xo_set_encoder (xo_handle_t *xop, xo_encoder_func_t encoder) +{ + xop = xo_default(xop); + + xop->xo_style = XO_STYLE_ENCODER; + xop->xo_encoder = encoder; } -#endif /* UNIT_TEST */ diff --git a/contrib/libxo/libxo/xo.h b/contrib/libxo/libxo/xo.h index c06574032e89..88bcce2999df 100644 --- a/contrib/libxo/libxo/xo.h +++ b/contrib/libxo/libxo/xo.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Juniper Networks, Inc. + * Copyright (c) 2014-2015, Juniper Networks, Inc. * All rights reserved. * This SOFTWARE is licensed under the LICENSE provided in the * ../Copyright file. By downloading, installing, copying, or otherwise @@ -12,12 +12,19 @@ * libxo provides a means of generating text, XML, JSON, and HTML output * using a single set of function calls, maximizing the value of output * while minimizing the cost/impact on the code. + * + * Full documentation is available in ./doc/libxo.txt or online at: + * http://juniper.github.io/libxo/libxo-manual.html */ #ifndef INCLUDE_XO_H #define INCLUDE_XO_H +#include #include +#include +#include +#include #ifdef __dead2 #define NORETURN __dead2 @@ -39,19 +46,21 @@ #endif /* NO_PRINTFLIKE */ /** Formatting types */ -typedef unsigned xo_style_t; +typedef unsigned short xo_style_t; #define XO_STYLE_TEXT 0 /** Generate text output */ #define XO_STYLE_XML 1 /** Generate XML output */ #define XO_STYLE_JSON 2 /** Generate JSON output */ #define XO_STYLE_HTML 3 /** Generate HTML output */ +#define XO_STYLE_SDPARAMS 4 /* Generate syslog structured data params */ +#define XO_STYLE_ENCODER 5 /* Generate calls to external encoder */ /** Flags for libxo */ typedef unsigned long long xo_xof_flags_t; #define XOF_BIT(_n) ((xo_xof_flags_t) 1 << (_n)) #define XOF_CLOSE_FP XOF_BIT(0) /** Close file pointer on xo_close() */ #define XOF_PRETTY XOF_BIT(1) /** Make 'pretty printed' output */ -#define XOF_DIV_OPEN XOF_BIT(2) /** Internal use only: a
is open */ -#define XOF_LINE_OPEN XOF_BIT(3) /** Internal use only:
*/ +#define XOF_LOG_SYSLOG XOF_BIT(2) /** Log (on stderr) our syslog content */ +#define XOF_RESV3 XOF_BIT(3) /* Unused */ #define XOF_WARN XOF_BIT(4) /** Generate warnings for broken calls */ #define XOF_XPATH XOF_BIT(5) /** Emit XPath attributes in HTML */ @@ -66,12 +75,12 @@ typedef unsigned long long xo_xof_flags_t; #define XOF_IGNORE_CLOSE XOF_BIT(12) /** Ignore errors on close tags */ #define XOF_NOT_FIRST XOF_BIT(13) /* Not the first item (JSON) */ #define XOF_NO_LOCALE XOF_BIT(14) /** Don't bother with locale */ -#define XOF_TOP_EMITTED XOF_BIT(15) /* The top JSON braces have been emitted */ +#define XOF_RESV15 XOF_BIT(15) /* Unused */ #define XOF_NO_TOP XOF_BIT(16) /** Don't emit the top braces in JSON */ -#define XOF_ANCHOR XOF_BIT(17) /** An anchor is in place */ +#define XOF_RESV17 XOF_BIT(17) /* Unused */ #define XOF_UNITS XOF_BIT(18) /** Encode units in XML */ -#define XOF_UNITS_PENDING XOF_BIT(19) /** We have a units-insertion pending */ +#define XOF_RESV19 XOF_BIT(19) /* Unused */ #define XOF_UNDERSCORES XOF_BIT(20) /** Replace dashes with underscores (JSON)*/ #define XOF_COLUMNS XOF_BIT(21) /** xo_emit should return a column count */ @@ -81,6 +90,10 @@ typedef unsigned long long xo_xof_flags_t; #define XOF_NO_CLOSE XOF_BIT(24) /** xo_finish won't close open elements */ #define XOF_COLOR_ALLOWED XOF_BIT(25) /** Allow color/effects to be enabled */ #define XOF_COLOR XOF_BIT(26) /** Enable color and effects */ +#define XOF_NO_HUMANIZE XOF_BIT(27) /** Block the {h:} modifier */ + +#define XOF_LOG_GETTEXT XOF_BIT(28) /** Log (stderr) gettext lookup strings */ +#define XOF_UTF8 XOF_BIT(29) /** Force text output to be UTF8 */ /* * The xo_info_t structure provides a mapping between names and @@ -92,6 +105,8 @@ typedef struct xo_info_s { const char *xi_help; /* Description of field */ } xo_info_t; +#define XO_INFO_NULL NULL, NULL, NULL /* Use '{ XO_INFO_NULL }' to end lists */ + struct xo_handle_s; /* Opaque structure forward */ typedef struct xo_handle_s xo_handle_t; /* Handle for XO output */ @@ -165,6 +180,35 @@ xo_emit_h (xo_handle_t *xop, const char *fmt, ...); int xo_emit (const char *fmt, ...); +PRINTFLIKE(2, 0) +static inline int +xo_emit_hvp (xo_handle_t *xop, const char *fmt, va_list vap) +{ + return xo_emit_hv(xop, fmt, vap); +} + +PRINTFLIKE(2, 3) +static inline int +xo_emit_hp (xo_handle_t *xop, const char *fmt, ...) +{ + va_list vap; + va_start(vap, fmt); + int rc = xo_emit_hv(xop, fmt, vap); + va_end(vap); + return rc; +} + +PRINTFLIKE(1, 2) +static inline int +xo_emit_p (const char *fmt, ...) +{ + va_list vap; + va_start(vap, fmt); + int rc = xo_emit_hv(NULL, fmt, vap); + va_end(vap); + return rc; +} + int xo_open_container_h (xo_handle_t *xop, const char *name); @@ -279,6 +323,9 @@ xo_finish_h (xo_handle_t *xop); int xo_finish (void); +void +xo_finish_atexit (void); + void xo_set_leading_xpath (xo_handle_t *xop, const char *path); @@ -304,7 +351,7 @@ void xo_errc (int eval, int code, const char *fmt, ...) NORETURN PRINTFLIKE(3, 4); void -xo_message_hcv (xo_handle_t *xop, int code, const char *fmt, va_list vap); +xo_message_hcv (xo_handle_t *xop, int code, const char *fmt, va_list vap) PRINTFLIKE(3, 0); void xo_message_hc (xo_handle_t *xop, int code, const char *fmt, ...) PRINTFLIKE(3, 4); @@ -312,9 +359,124 @@ xo_message_hc (xo_handle_t *xop, int code, const char *fmt, ...) PRINTFLIKE(3, 4 void xo_message_c (int code, const char *fmt, ...) PRINTFLIKE(2, 3); +void +xo_message_e (const char *fmt, ...) PRINTFLIKE(1, 2); + void xo_message (const char *fmt, ...) PRINTFLIKE(1, 2); +void +xo_emit_warn_hcv (xo_handle_t *xop, int as_warning, int code, + const char *fmt, va_list vap); + +void +xo_emit_warn_hc (xo_handle_t *xop, int code, const char *fmt, ...); + +void +xo_emit_warn_c (int code, const char *fmt, ...); + +void +xo_emit_warn (const char *fmt, ...); + +void +xo_emit_warnx (const char *fmt, ...); + +void +xo_emit_err (int eval, const char *fmt, ...) NORETURN; + +void +xo_emit_errx (int eval, const char *fmt, ...) NORETURN; + +void +xo_emit_errc (int eval, int code, const char *fmt, ...) NORETURN; + +PRINTFLIKE(4, 0) +static inline void +xo_emit_warn_hcvp (xo_handle_t *xop, int as_warning, int code, + const char *fmt, va_list vap) +{ + xo_emit_warn_hcv(xop, as_warning, code, fmt, vap); +} + +PRINTFLIKE(3, 4) +static inline void +xo_emit_warn_hcp (xo_handle_t *xop, int code, const char *fmt, ...) +{ + va_list vap; + va_start(vap, fmt); + xo_emit_warn_hcv(xop, 1, code, fmt, vap); + va_end(vap); +} + +PRINTFLIKE(2, 3) +static inline void +xo_emit_warn_cp (int code, const char *fmt, ...) +{ + va_list vap; + va_start(vap, fmt); + xo_emit_warn_hcv(NULL, 1, code, fmt, vap); + va_end(vap); +} + +PRINTFLIKE(1, 2) +static inline void +xo_emit_warn_p (const char *fmt, ...) +{ + int code = errno; + va_list vap; + va_start(vap, fmt); + xo_emit_warn_hcv(NULL, 1, code, fmt, vap); + va_end(vap); +} + +PRINTFLIKE(1, 2) +static inline void +xo_emit_warnx_p (const char *fmt, ...) +{ + va_list vap; + va_start(vap, fmt); + xo_emit_warn_hcv(NULL, 1, -1, fmt, vap); + va_end(vap); +} + +NORETURN PRINTFLIKE(2, 3) +static inline void +xo_emit_err_p (int eval, const char *fmt, ...) +{ + int code = errno; + va_list vap; + va_start(vap, fmt); + xo_emit_warn_hcv(NULL, 0, code, fmt, vap); + va_end(vap); + + exit(eval); +} + +PRINTFLIKE(2, 3) +static inline void +xo_emit_errx_p (int eval, const char *fmt, ...) +{ + va_list vap; + va_start(vap, fmt); + xo_emit_warn_hcv(NULL, 0, -1, fmt, vap); + va_end(vap); + exit(eval); +} + +PRINTFLIKE(3, 4) +static inline void +xo_emit_errc_p (int eval, int code, const char *fmt, ...) +{ + va_list vap; + va_start(vap, fmt); + xo_emit_warn_hcv(NULL, 0, code, fmt, vap); + va_end(vap); + exit(eval); +} + +void +xo_emit_err_v (int eval, int code, const char *fmt, va_list vap) NORETURN PRINTFLIKE(3, 0); + void xo_no_setlocale (void); @@ -395,4 +557,40 @@ xo_set_version (const char *version); void xo_set_version_h (xo_handle_t *xop, const char *version); +void +xo_open_log (const char *ident, int logopt, int facility); + +void +xo_close_log (void); + +int +xo_set_logmask (int maskpri); + +void +xo_set_unit_test_mode (int value); + +void +xo_syslog (int priority, const char *name, const char *message, ...); + +void +xo_vsyslog (int priority, const char *name, const char *message, va_list args); + +typedef void (*xo_syslog_open_t)(void); +typedef void (*xo_syslog_send_t)(const char *full_msg, + const char *v0_hdr, const char *text_only); +typedef void (*xo_syslog_close_t)(void); + +void +xo_set_syslog_handler (xo_syslog_open_t open_func, xo_syslog_send_t send_func, + xo_syslog_close_t close_func); + +void +xo_set_syslog_enterprise_id (unsigned short eid); + +typedef void (*xo_simplify_field_func_t)(const char *, unsigned, int); + +char * +xo_simplify_format (xo_handle_t *xop, const char *fmt, int with_numbers, + xo_simplify_field_func_t field_cb); + #endif /* INCLUDE_XO_H */ diff --git a/contrib/libxo/libxo/xo_attr.3 b/contrib/libxo/libxo/xo_attr.3 index 1c183605975b..c71377fd17eb 100644 --- a/contrib/libxo/libxo/xo_attr.3 +++ b/contrib/libxo/libxo/xo_attr.3 @@ -11,8 +11,8 @@ .Dt LIBXO 3 .Os .Sh NAME -.Nm xo_emit -.Nd emit formatted output based on format string and arguments +.Nm xo_attr , xo_attr_h , xo_attr_hv +.Nd Add attribute name/value pairs to formatted output .Sh LIBRARY .Lb libxo .Sh SYNOPSIS @@ -55,27 +55,6 @@ parameter as passed to Since attributes are only emitted in XML, their use should be limited to meta-data and additional or redundant representations of data already emitted in other form. -.Sh ADDITIONAL DOCUMENTATION -.Pp -Complete documentation can be found on github: -.Bd -literal -offset indent -http://juniper.github.io/libxo/libxo-manual.html -.Ed -.Pp -libxo lives on github as: -.Bd -literal -offset indent -https://github.com/Juniper/libxo -.Ed -.Pp -The latest release of libxo is available at: -.Bd -literal -offset indent -https://github.com/Juniper/libxo/releases -.Ed .Sh SEE ALSO -.Xr xo_emit 3 -.Sh HISTORY -The -.Fa libxo -library was added in FreeBSD 11.0. -.Sh AUTHOR -Phil Shafer +.Xr xo_emit 3 , +.Xr libxo 3 diff --git a/contrib/libxo/libxo/xo_buf.h b/contrib/libxo/libxo/xo_buf.h new file mode 100644 index 000000000000..349e9adefcc5 --- /dev/null +++ b/contrib/libxo/libxo/xo_buf.h @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2015, Juniper Networks, Inc. + * All rights reserved. + * This SOFTWARE is licensed under the LICENSE provided in the + * ../Copyright file. By downloading, installing, copying, or otherwise + * using the SOFTWARE, you agree to be bound by the terms of that + * LICENSE. + * Phil Shafer, August 2015 + */ + +/* + * This file is an _internal_ part of the libxo plumbing, not suitable + * for external use. It is not considered part of the libxo API and + * will not be a stable part of that API. Mine, not your's, dude... + * The real hope is that something like this will become a standard part + * of libc and I can kill this off. + */ + +#ifndef XO_BUF_H +#define XO_BUF_H + +#define XO_BUFSIZ (8*1024) /* Initial buffer size */ + +/* + * xo_buffer_t: a memory buffer that can be grown as needed. We + * use them for building format strings and output data. + */ +typedef struct xo_buffer_s { + char *xb_bufp; /* Buffer memory */ + char *xb_curp; /* Current insertion point */ + unsigned xb_size; /* Size of buffer */ +} xo_buffer_t; + +/* + * Initialize the contents of an xo_buffer_t. + */ +static inline void +xo_buf_init (xo_buffer_t *xbp) +{ + xbp->xb_size = XO_BUFSIZ; + xbp->xb_bufp = xo_realloc(NULL, xbp->xb_size); + xbp->xb_curp = xbp->xb_bufp; +} + +/* + * Reset the buffer to empty + */ +static inline void +xo_buf_reset (xo_buffer_t *xbp) +{ + xbp->xb_curp = xbp->xb_bufp; +} + +/* + * Return the number of bytes left in the buffer + */ +static inline int +xo_buf_left (xo_buffer_t *xbp) +{ + return xbp->xb_size - (xbp->xb_curp - xbp->xb_bufp); +} + +/* + * See if the buffer to empty + */ +static inline int +xo_buf_is_empty (xo_buffer_t *xbp) +{ + return (xbp->xb_curp == xbp->xb_bufp); +} + +/* + * Return the current offset + */ +static inline unsigned +xo_buf_offset (xo_buffer_t *xbp) +{ + return xbp ? (xbp->xb_curp - xbp->xb_bufp) : 0; +} + +static inline char * +xo_buf_data (xo_buffer_t *xbp, unsigned offset) +{ + if (xbp == NULL) + return NULL; + return xbp->xb_bufp + offset; +} + +static inline char * +xo_buf_cur (xo_buffer_t *xbp) +{ + if (xbp == NULL) + return NULL; + return xbp->xb_curp; +} + +/* + * Initialize the contents of an xo_buffer_t. + */ +static inline void +xo_buf_cleanup (xo_buffer_t *xbp) +{ + if (xbp->xb_bufp) + xo_free(xbp->xb_bufp); + bzero(xbp, sizeof(*xbp)); +} + +/* + * Does the buffer have room for the given number of bytes of data? + * If not, realloc the buffer to make room. If that fails, we + * return 0 to tell the caller they are in trouble. + */ +static inline int +xo_buf_has_room (xo_buffer_t *xbp, int len) +{ + if (xbp->xb_curp + len >= xbp->xb_bufp + xbp->xb_size) { + int sz = xbp->xb_size + XO_BUFSIZ; + char *bp = xo_realloc(xbp->xb_bufp, sz); + if (bp == NULL) + return 0; + + xbp->xb_curp = bp + (xbp->xb_curp - xbp->xb_bufp); + xbp->xb_bufp = bp; + xbp->xb_size = sz; + } + + return 1; +} + +/* + * Append the given string to the given buffer + */ +static inline void +xo_buf_append (xo_buffer_t *xbp, const char *str, int len) +{ + if (!xo_buf_has_room(xbp, len)) + return; + + memcpy(xbp->xb_curp, str, len); + xbp->xb_curp += len; +} + +/* + * Append the given NUL-terminated string to the given buffer + */ +static inline void +xo_buf_append_str (xo_buffer_t *xbp, const char *str) +{ + int len = strlen(str); + + if (!xo_buf_has_room(xbp, len)) + return; + + memcpy(xbp->xb_curp, str, len); + xbp->xb_curp += len; +} + +#endif /* XO_BUF_H */ diff --git a/contrib/libxo/libxo/xoconfig.h b/contrib/libxo/libxo/xo_config.h similarity index 82% rename from contrib/libxo/libxo/xoconfig.h rename to contrib/libxo/libxo/xo_config.h index dd1823e54be2..9177962f08d9 100644 --- a/contrib/libxo/libxo/xoconfig.h +++ b/contrib/libxo/libxo/xo_config.h @@ -1,5 +1,5 @@ -/* libxo/xoconfig.h. Generated from xoconfig.h.in by configure. */ -/* libxo/xoconfig.h.in. Generated from configure.ac by autoheader. */ +/* libxo/xo_config.h. Generated from xo_config.h.in by configure. */ +/* libxo/xo_config.h.in. Generated from configure.ac by autoheader. */ /* Define to one of `_getb67', `GETB67', `getb67' for Cray-2 and Cray-YMP systems. This function is required for `alloca.c' support on those systems. @@ -28,6 +28,10 @@ /* Define to 1 if you have the header file. */ #define HAVE_CTYPE_H 1 +/* Define to 1 if you have the declaration of `__isthreaded', and to 0 if you + don't. */ +#define HAVE_DECL___ISTHREADED 1 + /* Define to 1 if you have the header file. */ #define HAVE_DLFCN_H 1 @@ -46,12 +50,21 @@ /* Define to 1 if you have the `getpass' function. */ #define HAVE_GETPASS 1 +/* Define to 1 if you have the `getprogname' function. */ +#define HAVE_GETPROGNAME 1 + /* Define to 1 if you have the `getrusage' function. */ #define HAVE_GETRUSAGE 1 +/* gettext(3) */ +/* #undef HAVE_GETTEXT */ + /* Define to 1 if you have the `gettimeofday' function. */ #define HAVE_GETTIMEOFDAY 1 +/* humanize_number(3) */ +#define HAVE_HUMANIZE_NUMBER 1 + /* Define to 1 if you have the header file. */ #define HAVE_INTTYPES_H 1 @@ -61,6 +74,9 @@ /* Define to 1 if you have the `m' library (-lm). */ #define HAVE_LIBM 1 +/* Define to 1 if you have the header file. */ +#define HAVE_LIBUTIL_H 1 + /* Define to 1 if your system has a GNU libc compatible `malloc' function, and to 0 otherwise. */ #define HAVE_MALLOC 1 @@ -120,6 +136,9 @@ /* Define to 1 if you have the `strspn' function. */ #define HAVE_STRSPN 1 +/* Have struct sockaddr_un.sun_len */ +#define HAVE_SUN_LEN 1 + /* Define to 1 if you have the `sysctlbyname' function. */ #define HAVE_SYSCTLBYNAME 1 @@ -138,6 +157,12 @@ /* Define to 1 if you have the header file. */ #define HAVE_SYS_TYPES_H 1 +/* Define to 1 if you have the header file. */ +#define HAVE_THREADS_H 1 + +/* thread-local setting */ +#define HAVE_THREAD_LOCAL THREAD_LOCAL_before + /* Define to 1 if you have the header file. */ /* #undef HAVE_TZFILE_H */ @@ -153,6 +178,21 @@ /* Enable text-only rendering */ /* #undef LIBXO_TEXT_ONLY */ +/* Version number as dotted value */ +#define LIBXO_VERSION "0.4.3" + +/* Version number extra information */ +#define LIBXO_VERSION_EXTRA "" + +/* Version number as a number */ +#define LIBXO_VERSION_NUMBER 4003 + +/* Version number as string */ +#define LIBXO_VERSION_STRING "4003" + +/* Enable local wcwidth implementation */ +#define LIBXO_WCWIDTH 1 + /* Define to the sub-directory where libtool stores uninstalled libraries. */ #define LT_OBJDIR ".libs/" @@ -166,7 +206,7 @@ #define PACKAGE_NAME "libxo" /* Define to the full name and version of this package. */ -#define PACKAGE_STRING "libxo 0.3.2" +#define PACKAGE_STRING "libxo 0.4.3" /* Define to the one symbol short name of this package. */ #define PACKAGE_TARNAME "libxo" @@ -175,7 +215,7 @@ #define PACKAGE_URL "" /* Define to the version of this package. */ -#define PACKAGE_VERSION "0.3.2" +#define PACKAGE_VERSION "0.4.3" /* If using the C implementation of alloca, define if you know the direction of stack growth for your system; otherwise it will be @@ -189,7 +229,7 @@ #define STDC_HEADERS 1 /* Version number of package */ -#define VERSION "0.3.2" +#define VERSION "0.4.3" /* Define to `__inline__' or `__inline' if that's what the C compiler calls it, or to nothing if 'inline' is not supported under any name. */ diff --git a/contrib/libxo/libxo/xo_create.3 b/contrib/libxo/libxo/xo_create.3 index b0e896522707..bfbadc4e6ed6 100644 --- a/contrib/libxo/libxo/xo_create.3 +++ b/contrib/libxo/libxo/xo_create.3 @@ -11,8 +11,8 @@ .Dt LIBXO 3 .Os .Sh NAME -.Nm xo_emit -.Nd emit formatted output based on format string and arguments +.Nm xo_create , xo_create_to_file , xo_destroy +.Nd create and destroy libxo output handles .Sh LIBRARY .Lb libxo .Sh SYNOPSIS @@ -61,31 +61,7 @@ with a .Dv NULL handle will release any resources associated with the default handle. -.Sh ADDITIONAL DOCUMENTATION -Complete documentation can be found on github: -.Bd -literal -offset indent -http://juniper.github.io/libxo/libxo-manual.html -.Ed -.Pp -.Nm libxo -lives on github as: -.Bd -literal -offset indent -https://github.com/Juniper/libxo -.Ed -.Pp -The latest release of -.Nm libxo -is available at: -.Bd -literal -offset indent -https://github.com/Juniper/libxo/releases -.Ed .Sh SEE ALSO .Xr xo_emit 3 , -.Xr xo_set_options 3 -.Sh HISTORY -The -.Nm libxo -library was added in -.Fx 11.0 . -.Sh AUTHOR -Phil Shafer +.Xr xo_set_options 3 , +.Xr libxo 3 diff --git a/contrib/libxo/libxo/xo_emit.3 b/contrib/libxo/libxo/xo_emit.3 index 706082450986..155ea75d1fdf 100644 --- a/contrib/libxo/libxo/xo_emit.3 +++ b/contrib/libxo/libxo/xo_emit.3 @@ -11,7 +11,7 @@ .Dt LIBXO 3 .Os .Sh NAME -.Nm xo_emit +.Nm xo_emit , xo_emit_h , xo_emit_hv .Nd emit formatted output based on format string and arguments .Sh LIBRARY .Lb libxo @@ -43,30 +43,62 @@ uses an explicit handle. accepts a .Fa va_list for additional flexibility. -.Sh ADDITIONAL DOCUMENTATION -Complete documentation can be found on github: -.Bd -literal -offset indent -http://juniper.github.io/libxo/libxo-manual.html +.Sh EXAMPLES +In this example, a set of four values is emitted using the following +source code: +.Bd -literal -offset indent + xo_emit(" {:lines/%7ju} {:words/%7ju} " + "{:characters/%7ju} {d:filename/%s}\n", + linect, wordct, charct, file); .Ed -.Pp -.Nm libxo -lives on github as: -.Bd -literal -offset indent -https://github.com/Juniper/libxo -.Ed -.Pp -The latest release of libxo is available at: -.Bd -literal -offset indent -https://github.com/Juniper/libxo/releases +Output can then be generated in various style, using +the "--libxo" option: +.Bd -literal -offset indent + % wc /etc/motd + 25 165 1140 /etc/motd + % wc --libxo xml,pretty,warn /etc/motd + + + 25 + 165 + 1140 + /etc/motd + + + % wc --libxo json,pretty,warn /etc/motd + { + "wc": { + "file": [ + { + "lines": 25, + "words": 165, + "characters": 1140, + "filename": "/etc/motd" + } + ] + } + } + % wc --libxo html,pretty,warn /etc/motd +
+
+
25
+
+
165
+
+
1140
+
+
/etc/motd
+
.Ed +.Sh RETURN CODE +.Nm +returns a negative value on error. If the +.Nm XOF_COLUMNS +flag has been turned on for the specific handle using +.Xr xo_set_flags 3 , +then the number of display columns consumed by the output will be returned. .Sh SEE ALSO .Xr xo_open_container 3 , .Xr xo_open_list 3 , -.Xr xo_format 5 -.Sh HISTORY -The -.Nm libxo -library was added in -.Fx 11.0 . -.Sh AUTHOR -Phil Shafer +.Xr xo_format 5 , +.Xr libxo 3 diff --git a/contrib/libxo/libxo/xo_emit_err.3 b/contrib/libxo/libxo/xo_emit_err.3 new file mode 100644 index 000000000000..bb1ca640c6e7 --- /dev/null +++ b/contrib/libxo/libxo/xo_emit_err.3 @@ -0,0 +1,72 @@ +.\" # +.\" # Copyright (c) 2014, Juniper Networks, Inc. +.\" # All rights reserved. +.\" # This SOFTWARE is licensed under the LICENSE provided in the +.\" # ../Copyright file. By downloading, installing, copying, or +.\" # using the SOFTWARE, you agree to be bound by the terms of that +.\" # LICENSE. +.\" # Phil Shafer, July 2014 +.\" +.Dd December 4, 2014 +.Dt LIBXO 3 +.Os +.Sh NAME +.Nm xo_emit_err , xo_emit_errc , xo_emit_errx +.Nm xo_emit_warn , xo_emit_warnx , xo_emit_warn_c , xo_emit_warn_hc +.Nd emit errors and warnings in multiple output styles +.Sh LIBRARY +.Lb libxo +.Sh SYNOPSIS +.In libxo/xo.h +.Ft void +.Fn xo_emit_warn "const char *fmt" "..." +.Ft void +.Fn xo_emit_warnx "const char *fmt" "..." +.Ft void +.Fn xo_emit_warn_c "int code" "const char *fmt" "..." +.Ft void +.Fn xo_emit_warn_hc "xo_handle_t *xop" "int code, const char *fmt" "..." +.Ft void +.Fn xo_emit_err "int eval" "const char *fmt" "..." +.Ft void +.Fn xo_emit_errc "int eval" "int code" "const char *fmt" "..." +.Ft void +.Fn xo_emit_errx "int eval" "const char *fmt" "..." +.Sh DESCRIPTION +Many programs make use of the standard library functions +.Xr err 3 +and +.Xr warn 3 +to generate errors and warnings for the user. +.Nm libxo +wants to +pass that information via the current output style, and provides +compatible functions to allow this. +.Pp +The +.Fa fmt +argument is one compatible with +.Xr xo_emit 3 +which allows these functions make structured data. +To generate unstructured data, +use the +.Xr xo_err 3 +functions. +.Pp +These functions display the program name, a colon, a formatted message +based on the arguments, and then optionally a colon and an error +message associated with either +.Fa errno +or the +.Fa code +parameter. +.Bd -literal -offset indent + EXAMPLE: + if (open(filename, O_RDONLY) < 0) + xo_err(1, "cannot open file '%s'", filename); +.Ed +.Sh SEE ALSO +.Xr xo_emit 3 , +.Xr xo_format 5 , +.Xr xo_err 3 , +.Xr libxo 3 diff --git a/contrib/libxo/libxo/xo_encoder.c b/contrib/libxo/libxo/xo_encoder.c new file mode 100644 index 000000000000..70195ecaa32e --- /dev/null +++ b/contrib/libxo/libxo/xo_encoder.c @@ -0,0 +1,375 @@ +/* + * Copyright (c) 2015, Juniper Networks, Inc. + * All rights reserved. + * This SOFTWARE is licensed under the LICENSE provided in the + * ../Copyright file. By downloading, installing, copying, or otherwise + * using the SOFTWARE, you agree to be bound by the terms of that + * LICENSE. + * Phil Shafer, August 2015 + */ + +/** + * libxo includes a number of fixed encoding styles. But other + * external encoders are need to deal with new encoders. Rather + * than expose a swarm of libxo internals, we create a distinct + * API, with a simpler API than we use internally. + */ + +#include +#include +#include +#include +#include +#include + +#include "xo_config.h" +#include "xo.h" +#include "xo_encoder.h" + +#ifdef HAVE_DLFCN_H +#include +#if !defined(HAVE_DLFUNC) +#define dlfunc(_p, _n) dlsym(_p, _n) +#endif +#else /* HAVE_DLFCN_H */ +#define dlopen(_n, _f) NULL /* Fail */ +#define dlsym(_p, _n) NULL /* Fail */ +#define dlfunc(_p, _n) NULL /* Fail */ +#endif /* HAVE_DLFCN_H */ + +static void xo_encoder_setup (void); /* Forward decl */ + +/* + * Need a simple string collection + */ +typedef struct xo_string_node_s { + TAILQ_ENTRY(xo_string_node_s) xs_link; /* Next string */ + char xs_data[0]; /* String data */ +} xo_string_node_t; + +typedef TAILQ_HEAD(xo_string_list_s, xo_string_node_s) xo_string_list_t; + +static inline void +xo_string_list_init (xo_string_list_t *listp) +{ + if (listp->tqh_last == NULL) + TAILQ_INIT(listp); +} + +static inline xo_string_node_t * +xo_string_add (xo_string_list_t *listp, const char *str) +{ + if (listp == NULL || str == NULL) + return NULL; + + xo_string_list_init(listp); + size_t len = strlen(str); + xo_string_node_t *xsp; + + xsp = xo_realloc(NULL, sizeof(*xsp) + len + 1); + if (xsp) { + memcpy(xsp->xs_data, str, len); + xsp->xs_data[len] = '\0'; + TAILQ_INSERT_TAIL(listp, xsp, xs_link); + } + + return xsp; +} + +#define XO_STRING_LIST_FOREACH(_xsp, _listp) \ + xo_string_list_init(_listp); \ + TAILQ_FOREACH(_xsp, _listp, xs_link) + +static inline void +xo_string_list_clean (xo_string_list_t *listp) +{ + xo_string_node_t *xsp; + + xo_string_list_init(listp); + + for (;;) { + xsp = TAILQ_FIRST(listp); + if (xsp == NULL) + break; + TAILQ_REMOVE(listp, xsp, xs_link); + xo_free(xsp); + } +} + +static xo_string_list_t xo_encoder_path; + +void +xo_encoder_path_add (const char *path) +{ + xo_encoder_setup(); + + if (path) + xo_string_add(&xo_encoder_path, path); +} + +/* ---------------------------------------------------------------------- */ + +typedef struct xo_encoder_node_s { + TAILQ_ENTRY(xo_encoder_node_s) xe_link; /* Next session */ + char *xe_name; /* Name for this encoder */ + xo_encoder_func_t xe_handler; /* Callback function */ + void *xe_dlhandle; /* dlopen handle */ +} xo_encoder_node_t; + +typedef TAILQ_HEAD(xo_encoder_list_s, xo_encoder_node_s) xo_encoder_list_t; + +#define XO_ENCODER_LIST_FOREACH(_xep, _listp) \ + xo_encoder_list_init(_listp); \ + TAILQ_FOREACH(_xep, _listp, xe_link) + +static xo_encoder_list_t xo_encoders; + +static void +xo_encoder_list_init (xo_encoder_list_t *listp) +{ + if (listp->tqh_last == NULL) + TAILQ_INIT(listp); +} + +static xo_encoder_node_t * +xo_encoder_list_add (const char *name) +{ + if (name == NULL) + return NULL; + + xo_encoder_node_t *xep = xo_realloc(NULL, sizeof(*xep)); + if (xep) { + int len = strlen(name) + 1; + xep->xe_name = xo_realloc(NULL, len); + if (xep->xe_name == NULL) { + xo_free(xep); + return NULL; + } + + memcpy(xep->xe_name, name, len); + + TAILQ_INSERT_TAIL(&xo_encoders, xep, xe_link); + } + + return xep; +} + +void +xo_encoders_clean (void) +{ + xo_encoder_node_t *xep; + + xo_encoder_setup(); + + for (;;) { + xep = TAILQ_FIRST(&xo_encoders); + if (xep == NULL) + break; + + TAILQ_REMOVE(&xo_encoders, xep, xe_link); + + if (xep->xe_dlhandle) + dlclose(xep->xe_dlhandle); + + xo_free(xep); + } + + xo_string_list_clean(&xo_encoder_path); +} + +static void +xo_encoder_setup (void) +{ + static int initted; + if (!initted) { + initted = 1; + + xo_string_list_init(&xo_encoder_path); + xo_encoder_list_init(&xo_encoders); + + xo_encoder_path_add(XO_ENCODERDIR); + } +} + +static xo_encoder_node_t * +xo_encoder_find (const char *name) +{ + xo_encoder_node_t *xep; + + xo_encoder_list_init(&xo_encoders); + + XO_ENCODER_LIST_FOREACH(xep, &xo_encoders) { + if (strcmp(xep->xe_name, name) == 0) + return xep; + } + + return NULL; +} + +static xo_encoder_node_t * +xo_encoder_discover (const char *name) +{ + void *dlp = NULL; + char buf[MAXPATHLEN]; + xo_string_node_t *xsp; + xo_encoder_node_t *xep = NULL; + + XO_STRING_LIST_FOREACH(xsp, &xo_encoder_path) { + static const char fmt[] = "%s/%s.enc"; + char *dir = xsp->xs_data; + size_t len = snprintf(buf, sizeof(buf), fmt, dir, name); + + if (len > sizeof(buf)) /* Should not occur */ + continue; + + dlp = dlopen((const char *) buf, RTLD_NOW); + if (dlp) + break; + } + + if (dlp) { + /* + * If the library exists, find the initializer function and + * call it. + */ + xo_encoder_init_func_t func; + + func = (xo_encoder_init_func_t) dlfunc(dlp, XO_ENCODER_INIT_NAME); + if (func) { + xo_encoder_init_args_t xei; + + bzero(&xei, sizeof(xei)); + + xei.xei_version = XO_ENCODER_VERSION; + int rc = func(&xei); + if (rc == 0 && xei.xei_handler) { + xep = xo_encoder_list_add(name); + if (xep) { + xep->xe_handler = xei.xei_handler; + xep->xe_dlhandle = dlp; + } + } + } + + if (xep == NULL) + dlclose(dlp); + } + + return xep; +} + +void +xo_encoder_register (const char *name, xo_encoder_func_t func) +{ + xo_encoder_setup(); + + xo_encoder_node_t *xep = xo_encoder_find(name); + + if (xep) /* "We alla-ready got one" */ + return; + + xep = xo_encoder_list_add(name); + if (xep) + xep->xe_handler = func; +} + +void +xo_encoder_unregister (const char *name) +{ + xo_encoder_setup(); + + xo_encoder_node_t *xep = xo_encoder_find(name); + if (xep) { + TAILQ_REMOVE(&xo_encoders, xep, xe_link); + xo_free(xep); + } +} + +int +xo_encoder_init (xo_handle_t *xop, const char *name) +{ + xo_encoder_setup(); + + /* Can't have names containing '/' or ':' */ + if (strchr(name, '/') != NULL || strchr(name, ':') != NULL) + return -1; + + /* + * First we look on the list of known (registered) encoders. + * If we don't find it, we follow the set of paths to find + * the encoding library. + */ + xo_encoder_node_t *xep = xo_encoder_find(name); + if (xep == NULL) { + xep = xo_encoder_discover(name); + if (xep == NULL) + return -1; + } + + xo_set_encoder(xop, xep->xe_handler); + + return xo_encoder_handle(xop, XO_OP_CREATE, NULL, NULL); +} + +/* + * A couple of function varieties here, to allow for multiple + * use cases. This varient is for when the main program knows + * its own encoder needs. + */ +xo_handle_t * +xo_encoder_create (const char *name, xo_xof_flags_t flags) +{ + xo_handle_t *xop; + + xop = xo_create(XO_STYLE_ENCODER, flags); + if (xop) { + if (xo_encoder_init(xop, name)) { + xo_destroy(xop); + xop = NULL; + } + } + + return xop; +} + +int +xo_encoder_handle (xo_handle_t *xop, xo_encoder_op_t op, + const char *name, const char *value) +{ + void *private = xo_get_private(xop); + xo_encoder_func_t func = xo_get_encoder(xop); + + if (func == NULL) + return -1; + + return func(xop, op, name, value, private); +} + +const char * +xo_encoder_op_name (xo_encoder_op_t op) +{ + static const char *names[] = { + /* 0 */ "unknown", + /* 1 */ "create", + /* 2 */ "open_container", + /* 3 */ "close_container", + /* 4 */ "open_list", + /* 5 */ "close_list", + /* 6 */ "open_leaf_list", + /* 7 */ "close_leaf_list", + /* 8 */ "open_instance", + /* 9 */ "close_instance", + /* 10 */ "string", + /* 11 */ "content", + /* 12 */ "finish", + /* 13 */ "flush", + /* 14 */ "destroy", + /* 15 */ "attr", + /* 16 */ "version", + }; + + if (op > sizeof(names) / sizeof(names[0])) + return "unknown"; + + return names[op]; +} diff --git a/contrib/libxo/libxo/xo_encoder.h b/contrib/libxo/libxo/xo_encoder.h new file mode 100644 index 000000000000..f73552b11a66 --- /dev/null +++ b/contrib/libxo/libxo/xo_encoder.h @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2015, Juniper Networks, Inc. + * All rights reserved. + * This SOFTWARE is licensed under the LICENSE provided in the + * ../Copyright file. By downloading, installing, copying, or otherwise + * using the SOFTWARE, you agree to be bound by the terms of that + * LICENSE. + * Phil Shafer, August 2015 + */ + +/* + * NOTE WELL: This file is needed to software that implements an + * external encoder for libxo that allows libxo data to be encoded in + * new and bizarre formats. General libxo code should _never_ + * include this header file. + */ + +#ifndef XO_ENCODER_H +#define XO_ENCODER_H + +/* + * Expose libxo's memory allocation functions + */ +extern xo_realloc_func_t xo_realloc; +extern xo_free_func_t xo_free; + +typedef unsigned xo_encoder_op_t; + +/* Encoder operations; names are in xo_encoder.c:xo_encoder_op_name() */ +#define XO_OP_UNKNOWN 0 +#define XO_OP_CREATE 1 /* Called when the handle is init'd */ +#define XO_OP_OPEN_CONTAINER 2 +#define XO_OP_CLOSE_CONTAINER 3 +#define XO_OP_OPEN_LIST 4 +#define XO_OP_CLOSE_LIST 5 +#define XO_OP_OPEN_LEAF_LIST 6 +#define XO_OP_CLOSE_LEAF_LIST 7 +#define XO_OP_OPEN_INSTANCE 8 +#define XO_OP_CLOSE_INSTANCE 9 +#define XO_OP_STRING 10 /* Quoted UTF-8 string */ +#define XO_OP_CONTENT 11 /* Other content */ +#define XO_OP_FINISH 12 /* Finish any pending output */ +#define XO_OP_FLUSH 13 /* Flush any buffered output */ +#define XO_OP_DESTROY 14 /* Clean up function */ +#define XO_OP_ATTRIBUTE 15 /* Attribute name/value */ +#define XO_OP_VERSION 16 /* Version string */ + +#define XO_ENCODER_HANDLER_ARGS \ + xo_handle_t *xop __attribute__ ((__unused__)), \ + xo_encoder_op_t op __attribute__ ((__unused__)), \ + const char *name __attribute__ ((__unused__)), \ + const char *value __attribute__ ((__unused__)), \ + void *private __attribute__ ((__unused__)) + +typedef int (*xo_encoder_func_t)(XO_ENCODER_HANDLER_ARGS); + +typedef struct xo_encoder_init_args_s { + unsigned xei_version; /* Current version */ + xo_encoder_func_t xei_handler; /* Encoding handler */ +} xo_encoder_init_args_t; + +#define XO_ENCODER_VERSION 1 /* Current version */ + +#define XO_ENCODER_INIT_ARGS \ + xo_encoder_init_args_t *arg __attribute__ ((__unused__)) + +typedef int (*xo_encoder_init_func_t)(XO_ENCODER_INIT_ARGS); +/* + * Each encoder library must define a function named xo_encoder_init + * that takes the arguments defined in XO_ENCODER_INIT_ARGS. It + * should return zero for success. + */ +#define XO_ENCODER_INIT_NAME_TOKEN xo_encoder_library_init +#define XO_STRINGIFY(_x) #_x +#define XO_STRINGIFY2(_x) XO_STRINGIFY(_x) +#define XO_ENCODER_INIT_NAME XO_STRINGIFY2(XO_ENCODER_INIT_NAME_TOKEN) +extern int XO_ENCODER_INIT_NAME_TOKEN (XO_ENCODER_INIT_ARGS); + +void +xo_encoder_register (const char *name, xo_encoder_func_t func); + +void +xo_encoder_unregister (const char *name); + +void * +xo_get_private (xo_handle_t *xop); + +void +xo_encoder_path_add (const char *path); + +void +xo_set_private (xo_handle_t *xop, void *opaque); + +xo_encoder_func_t +xo_get_encoder (xo_handle_t *xop); + +void +xo_set_encoder (xo_handle_t *xop, xo_encoder_func_t encoder); + +int +xo_encoder_init (xo_handle_t *xop, const char *name); + +xo_handle_t * +xo_encoder_create (const char *name, xo_xof_flags_t flags); + +int +xo_encoder_handle (xo_handle_t *xop, xo_encoder_op_t op, + const char *name, const char *value); + +void +xo_encoders_clean (void); + +const char * +xo_encoder_op_name (xo_encoder_op_t op); + +#endif /* XO_ENCODER_H */ diff --git a/contrib/libxo/libxo/xo_err.3 b/contrib/libxo/libxo/xo_err.3 index 5584309f3bf0..532899ab85ff 100644 --- a/contrib/libxo/libxo/xo_err.3 +++ b/contrib/libxo/libxo/xo_err.3 @@ -11,8 +11,9 @@ .Dt LIBXO 3 .Os .Sh NAME -.Nm xo_err -.Nd emit errors and warnings in multiple formats +.Nm xo_err , xo_errc , xo_errx +.Nm xo_warn , xo_warnx , xo_warn_c , xo_warn_hc +.Nd emit errors and warnings in multiple output styles .Sh LIBRARY .Lb libxo .Sh SYNOPSIS @@ -31,14 +32,6 @@ .Fn xo_errc "int eval" "int code" "const char *fmt" "..." .Ft void .Fn xo_errx "int eval" "const char *fmt" "..." -.Ft void -.Fn xo_message "const char *fmt" "..." -.Ft void -.Fn xo_message_c "int code" "const char *fmt" "..." -.Ft void -.Fn xo_message_hc "xo_handle_t *xop" "int code, const char *fmt" "..." -.Ft void -.Fn xo_message_hcv "xo_handle_t *xop" "int code" "const char *fmt" "va_list vap" .Sh DESCRIPTION Many programs make use of the standard library functions .Xr err 3 @@ -50,6 +43,19 @@ wants to pass that information via the current output style, and provides compatible functions to allow this. .Pp +The +.Fa fmt +argument is one compatible with +.Xr printf 3 +rather than +.Xr xo_emit 3 +to aid in simple conversion. This means +these functions make unstructured data. +To generate structured data, +use the +.Xr xo_emit_err 3 +functions. +.Pp These functions display the program name, a colon, a formatted message based on the arguments, and then optionally a colon and an error message associated with either @@ -62,30 +68,7 @@ parameter. if (open(filename, O_RDONLY) < 0) xo_err(1, "cannot open file '%s'", filename); .Ed -.Sh ADDITIONAL DOCUMENTATION -Complete documentation can be found on github: -.Bd -literal -offset indent -http://juniper.github.io/libxo/libxo-manual.html -.Ed -.Pp -.Nm libxo -lives on github as: -.Bd -literal -offset indent -https://github.com/Juniper/libxo -.Ed -.Pp -The latest release of -.Nm libxo -is available at: -.Bd -literal -offset indent -https://github.com/Juniper/libxo/releases -.Ed .Sh SEE ALSO -.Xr xo_emit 3 -.Sh HISTORY -The -.Nm libxo -library was added in -.Fx 11.0 . -.Sh AUTHOR -Phil Shafer +.Xr xo_emit 3 , +.Xr xo_emit_err 3 , +.Xr libxo 3 diff --git a/contrib/libxo/libxo/xo_error.3 b/contrib/libxo/libxo/xo_error.3 index 01431cb04345..e5c99e9dd923 100644 --- a/contrib/libxo/libxo/xo_error.3 +++ b/contrib/libxo/libxo/xo_error.3 @@ -12,7 +12,7 @@ .Os .Sh NAME .Nm xo_error -.Nd generate error messages +.Nd generate simple error messages in multiple output styles .Sh LIBRARY .Lb libxo .Sh SYNOPSIS @@ -35,31 +35,7 @@ one can replace calls with .Fn xo_error calls. -.Sh ADDITIONAL DOCUMENTATION -Complete documentation can be found on github: -.Bd -literal -offset indent -http://juniper.github.io/libxo/libxo-manual.html -.Ed -.Pp -.Nm libxo -lives on github as: -.Bd -literal -offset indent -https://github.com/Juniper/libxo -.Ed -.Pp -The latest release of -.Nm libxo -is available at: -.Bd -literal -offset indent -https://github.com/Juniper/libxo/releases -.Ed .Sh SEE ALSO .Xr printf 3 , -.Xr xo_emit 3 -.Sh HISTORY -The -.Nm libxo -library was added in -.Fx 11.0 . -.Sh AUTHOR -Phil Shafer +.Xr xo_emit 3 , +.Xr libxo 3 diff --git a/contrib/libxo/libxo/xo_finish.3 b/contrib/libxo/libxo/xo_finish.3 index 421e94557bf9..221b1c1779b4 100644 --- a/contrib/libxo/libxo/xo_finish.3 +++ b/contrib/libxo/libxo/xo_finish.3 @@ -11,8 +11,8 @@ .Dt LIBXO 3 .Os .Sh NAME -.Nm xo_emit -.Nd emit formatted output based on format string and arguments +.Nm xo_finish , xo_finish_h +.Nd finish formatting output .Sh LIBRARY .Lb libxo .Sh SYNOPSIS @@ -34,30 +34,6 @@ Calling this function is .Em vital to the proper operation of libxo, especially for the non-TEXT output styles. -.Sh ADDITIONAL DOCUMENTATION -Complete documentation can be found on github: -.Bd -literal -offset indent -http://juniper.github.io/libxo/libxo-manual.html -.Ed -.Pp -.Nm libxo -lives on github as: -.Bd -literal -offset indent -https://github.com/Juniper/libxo -.Ed -.Pp -The latest release of -.Nm libxo -is available at: -.Bd -literal -offset indent -https://github.com/Juniper/libxo/releases -.Ed .Sh SEE ALSO -.Xr xo_emit 3 -.Sh HISTORY -The -.Nm libxo -library was added in -.Fx 11.0 . -.Sh AUTHOR -Phil Shafer +.Xr xo_emit 3 , +.Xr libxo 3 diff --git a/contrib/libxo/libxo/xo_flush.3 b/contrib/libxo/libxo/xo_flush.3 index b85e9f786d0c..e43bae0057e7 100644 --- a/contrib/libxo/libxo/xo_flush.3 +++ b/contrib/libxo/libxo/xo_flush.3 @@ -11,8 +11,8 @@ .Dt LIBXO 3 .Os .Sh NAME -.Nm xo_emit -.Nd emit formatted output based on format string and arguments +.Nm xo_flush , xo_flush_h +.Nd flush formatted output from libxo handle .Sh LIBRARY .Lb libxo .Sh SYNOPSIS @@ -30,30 +30,6 @@ caller may wish to flush any data buffered within the library. The .Fn xo_flush function is used for this. -.Sh ADDITIONAL DOCUMENTATION -Complete documentation can be found on github: -.Bd -literal -offset indent -http://juniper.github.io/libxo/libxo-manual.html -.Ed -.Pp -.Nm libxo -lives on github as: -.Bd -literal -offset indent -https://github.com/Juniper/libxo -.Ed -.Pp -The latest release of -.Nm libxo -is available at: -.Bd -literal -offset indent -https://github.com/Juniper/libxo/releases -.Ed .Sh SEE ALSO -.Xr xo_emit 3 -.Sh HISTORY -The -.Nm libxo -library was added in -.Fx 11.0 . -.Sh AUTHOR -Phil Shafer +.Xr xo_emit 3 , +.Xr libxo 3 diff --git a/contrib/libxo/libxo/xo_format.5 b/contrib/libxo/libxo/xo_format.5 index bce5dc5ae33d..1db4fc8dc118 100644 --- a/contrib/libxo/libxo/xo_format.5 +++ b/contrib/libxo/libxo/xo_format.5 @@ -2,11 +2,11 @@ .\" # Copyright (c) 2014, Juniper Networks, Inc. .\" # All rights reserved. .\" # This SOFTWARE is licensed under the LICENSE provided in the -.\" # ../Copyright file. By downloading, installing, copying, or +.\" # ../Copyright file. By downloading, installing, copying, or .\" # using the SOFTWARE, you agree to be bound by the terms of that .\" # LICENSE. .\" # Phil Shafer, July 2014 -.\" +.\" .Dd December 4, 2014 .Dt LIBXO 3 .Os @@ -20,18 +20,18 @@ uses format strings to control the rendering of data into various output styles, including .Em text , .Em XML , -.EM JSON , +.Em JSON , and .Em HTML . Each format string contains a set of zero or more -.Dq field descriptions , +.Dq "field descriptions" , which describe independent data fields. Each field description contains a set of .Dq modifiers , a -.Dq content string , +.Dq "content string" , and zero, one, or two -.Dq format descriptors . +.Dq "format descriptors" . The modifiers tell .Nm libxo what the field is and how to treat it, while the format descriptors are @@ -51,7 +51,7 @@ field descriptions within the format string. .Pp The field description is given as follows: .Bd -literal -offset indent - '{' [ role | modifier ]* ':' [ content ] + '{' [ role | modifier ]* [',' long-names ]* ':' [ content ] [ '/' field-format [ '/' encoding-format ]] '}' .Ed .Pp @@ -61,6 +61,10 @@ The contents, field-format, and encoding-format are used in varying ways, based on the role. These are described in the following sections. .Pp +Braces can be escaped by using double braces, similar to "%%" in +.Xr printf 3 . +The format string "{{braces}}" would emit "{braces}". +.Pp In the following example, three field descriptors appear. The first is a padding field containing three spaces of padding, the second is a @@ -70,19 +74,28 @@ passed to the .Xr xo_emit 3 , function as an unsigned integer. .Bd -literal -offset indent - xo_emit("{P: }{Lwc:In stock}{:in-stock/%u}\\n", 65); + xo_emit("{P: }{Lwc:In stock}{:in-stock/%u}\\n", 65); .Ed .Pp This single line of code can generate text ("In stock: 65\\n"), XML ("65"), JSON ('"in-stock": 65'), or HTML (too lengthy to be listed here). -.Ss Modifier Roles -Modifiers are optional, and indicate the role and formatting of the +.Pp +While roles and modifiers typically use single character for brevity, +there are alternative names for each which allow more verbose +formatting strings. +These names must be preceded by a comma, and may follow any +single-character values: +.Bd -literal -offset indent + xo_emit("{L,white,colon:In stock}{,key:in-stock/%u}\n", 65); +.Ed +.Ss "Field Roles" +Field roles are optional, and indicate the role and formatting of the content. The roles are listed below; only one role is permitted: -.Pp .Bl -column "M" "Name12341234" -.It Sy "M Name Description" +.It Sy "M" "Name " "Description" +.It C "color " "Field is a color or effect" .It D "decoration " "Field is non-text (e.g. colon, comma)" .It E "error " "Field is an error message" .It L "label " "Field is text that prefixes a value" @@ -92,38 +105,74 @@ The roles are listed below; only one role is permitted: .It U "units " "Field is the units for the previous value field" .It V "value " "Field is the name of field (the default)" .It W "warning " "Field is a warning message" -.It \&[ "start anchor" "Begin a section of anchored variable-width text" -.It \&] "stop anchor " "End a section of anchored variable-width text" +.It \&[ "start-anchor" "Begin a section of anchored variable-width text" +.It \&] "stop-anchor " "End a section of anchored variable-width text" .El +.Bd -literal -offset indent + EXAMPLE: + xo_emit("{L:Free}{D::}{P: }{:free/%u} {U:Blocks}\n", + free_blocks); +.Ed .Pp -.Ss The Color Role ({C:}) +When a role is not provided, the "value" role is used as the default. +.Pp +Roles and modifiers can also use more verbose names, when preceeded by +a comma: +.Bd -literal -offset indent + EXAMPLE: + xo_emit("{,label:Free}{,decoration::}{,padding: }" + "{,value:free/%u} {,units:Blocks}\n", + free_blocks); +.Ed +.Ss "The Color Role ({C:})" Colors and effects control how text values are displayed; they are used for display styles (TEXT and HTML). -The color content can be -either static, when placed directly within the field descriptor, or a -printf-style format descriptor can be used, if preceded by a slash ("/"): .Bd -literal -offset indent - xo_emit("{C:bold}{Lwc:Cost}{:cost/%u}{C:reset}\n", cost); - xo_emit("{C:/fg-%s,bg-%s}{Lwc:Cost}{:cost/%u}{C:reset}\n", - fg_color, bg_color, cost); + xo_emit("{C:bold}{:value}{C:no-bold}\n", value); +.Ed +.Pp +Colors and effects remain in effect until modified by other "C"-role +fields. +.Bd -literal -offset indent + xo_emit("{C:bold}{C:inverse}both{C:no-bold}only inverse\n"); +.Ed +.Pp +If the content is empty, the "reset" action is performed. +.Bd -literal -offset indent + xo_emit("{C:both,underline}{:value}{C:}\n", value); .Ed .Pp The content should be a comma-separated list of zero or more colors or display effects. +.Bd -literal -offset indent + xo_emit("{C:bold,underline,inverse}All three{C:no-bold,no-inverse}\n"); +.Ed .Pp -Colors and effects remain in effect until modified by other "C" roles. +The color content can be either static, when placed directly within +the field descriptor, or a printf-style format descriptor can be used, +if preceded by a slash ("/"): +.Bd -literal -offset indent + xo_emit("{C:/%s%s}{:value}{C:}", need_bold ? "bold" : "", + need_underline ? "underline" : "", value); +.Ed .Pp -If the content is empty, the "reset" action is performed. +Color names are prefixed with either "fg-" or "bg-" to change the +foreground and background colors, respectively. +.Bd -literal -offset indent + xo_emit("{C:/fg-%s,bg-%s}{Lwc:Cost}{:cost/%u}{C:reset}\n", + fg_color, bg_color, cost); +.Ed .Pp +The following table lists the supported effects: .Bl -column "no-underline" -.It Sy "Name Description" -.It "bg-xxxxx " "Change background color" +.It Sy "Name " "Description" +.It "bg\-xxxxx " "Change background color" .It "bold " "Start bold text effect" -.It "fg-xxxxx " "Change foreground color" +.It "fg\-xxxxx " "Change foreground color" .It "inverse " "Start inverse (aka reverse) text effect" -.It "no-bold " "Stop bold text effect" -.It "no-inverse " "Stop inverse (aka reverse) text effect" -.It "no-underline " "Stop underline text effect" +.It "no\-bold " "Stop bold text effect" +.It "no\-inverse " "Stop inverse (aka reverse) text effect" +.It "no\-underline " "Stop underline text effect" .It "normal " "Reset effects (only)" .It "reset " "Reset colors and effects (restore defaults)" .It "underline " "Start underline text effect" @@ -142,10 +191,7 @@ The following color names are supported: .It white .It yellow .El -.Pp -Color names are prefixed with either "fg-" or "bg-" to change the -foreground and background colors, respectively. -.Ss The Decoration Role ({D:}) +.Ss "The Decoration Role ({D:})" Decorations are typically punctuation marks such as colons, semi-colons, and commas used to decorate the text and make it simpler for human readers. @@ -154,17 +200,53 @@ can use CSS to direct their display parameters. .Bd -literal -offset indent xo_emit("{D:((}{:name}{D:))}\\n", name); .Ed -.Ss The Label Role ({L:}) +.Ss "The Gettext Role ({G:})" +.Nm libxo +supports internationalization (i18n) through its use of +.Xr gettext 3 . +Use the "{G:}" role to request that the remaining part of +the format string, following the "{G:}" field, be handled using +.Fn gettext . +Since +.Fn gettext +uses the string as the key into the message catalog, +.Nm libxo +uses a simplified version of the format string that removes +unimportant field formatting and modifiers, stopping minor formatting +changes from impacting the expensive translation process. +A developer +change such as changing "/%06d" to "/%08d" should not force hand +inspection of all .po files. +.Pp +The simplified version can be generated for a single message using the +"xopo -s " command, or an entire .pot can be translated using +the "xopo -f -o " command. +.Bd -literal -offset indent + xo_emit("{G:}Invalid token\n"); +.Ed +The {G:} role allows a domain name to be set. +.Fn gettext +calls will +continue to use that domain name until the current format string +processing is complete, enabling a library function to emit strings +using it's own catalog. +The domain name can be either static as the +content of the field, or a format can be used to get the domain name +from the arguments. +.Bd -literal -offset indent + xo_emit("{G:libc}Service unavailable in restricted mode\n"); +.Ed +.Ss "The Label Role ({L:})" Labels are text that appears before a value. .Bd -literal -offset indent xo_emit("{Lwc:Cost}{:cost/%u}\\n", cost); .Ed -.Ss The Note Role ({N:}) +.Ss "The Note Role ({N:})" Notes are text that appears after a value. .Bd -literal -offset indent xo_emit("{:cost/%u} {N:per year}\\n", cost); .Ed -.Ss The Padding Role ({P:}) +.Ss "The Padding Role ({P:})" Padding represents whitespace used before and between fields. The padding content can be either static, when placed directly within the field descriptor, or a printf-style format descriptor can be used, @@ -173,7 +255,7 @@ if preceded by a slash ("/"): xo_emit("{P: }{Lwc:Cost}{:cost/%u}\\n", cost); xo_emit("{P:/30s}{Lwc:Cost}{:cost/%u}\\n", "", cost); .Ed -.Ss The Title Role ({T:}) +.Ss "The Title Role ({T:})" Titles are heading or column headers that are meant to be displayed to the user. The title can be either static, when placed directly within @@ -183,7 +265,7 @@ if preceded by a slash ("/"): xo_emit("{T:Interface Statistics}\\n"); xo_emit("{T:/%20.20s}{T:/%6.6s}\\n", "Item Name", "Cost"); .Ed -.Ss The Units Role ({U:}) +.Ss "The Units Role ({U:})" Units are the dimension by which values are measured, such as degrees, miles, bytes, and decibels. The units field carries this information @@ -209,7 +291,7 @@ Units can also be rendered in HTML as the "data-units" attribute:
50
.Ed -.Ss The Value Role ({V:} and {:}) +.Ss "The Value Role ({V:} and {:})" The value role is used to represent the a data value that is interesting for the non-display output styles (XML and JSON). Value @@ -231,7 +313,7 @@ format descriptors default to "%s". xo_emit("{:author} wrote \"{:poem}\" in {:year/%4d}\\n, author, poem, year); .Ed -.Ss The Anchor Modifiers ({[:} and {]:}) +.Ss "The Anchor Roles ({[:} and {]:})" The anchor roles allow a set of strings by be padded as a group, but still be visible to .Xr xo_emit 3 @@ -269,20 +351,24 @@ Widths over 8k are considered probable errors and not supported. If .Dv XOF_WARN is set, a warning will be generated. -.Ss Modifier Flags -The modifiers can also include the following flags, which modify the -content emitted for some output styles: -.Pp -.Bl -column M "Name12341234" -.It Sy M "Name Description" -.It c "colon " "A colon ("":"") is appended after the label" -.It d "display " "Only emit field for display styles (text/HTML)" -.It e "encoding " "Only emit for encoding styles (XML/JSON)" -.It k "key " "Field is a key, suitable for XPath predicates" -.It l "leaf " "Field is a leaf-list, a list of leaf values" -.It n "no-quotes " "Do not quote the field when using JSON style" -.It q "quotes " "Quote the field when using JSON style" -.It w "white space " "A blank ("" "") is appended after the label" +.Ss "Field Modifiers" +Field modifiers are flags which modify the way content emitted for +particular output styles: +.Bl -column M "Name123456789" +.It Sy M "Name " "Description" +.It c "colon " "A colon ("":"") is appended after the label" +.It d "display " "Only emit field for display styles (text/HTML)" +.It e "encoding " "Only emit for encoding styles (XML/JSON)" +.It h "humanize (hn) " "Format large numbers in human-readable style" +.It " " "hn-space " "Humanize: Place space between numeric and unit" +.It " " "hn-decimal " "Humanize: Add a decimal digit, if number < 10" +.It " " "hn-1000 " "Humanize: Use 1000 as divisor instead of 1024" +.It k "key " "Field is a key, suitable for XPath predicates" +.It l "leaf-list " "Field is a leaf-list, a list of leaf values" +.It n "no-quotes " "Do not quote the field when using JSON style" +.It q "quotes " "Quote the field when using JSON style" +.It q "trim " "Trim leading and trailing whitespace" +.It w "white space " "A blank ("" "") is appended after the label" .El .Pp For example, the modifier string "Lwc" means the field has a label @@ -291,7 +377,16 @@ colon ('c') and a space ('w'). The modifier string "Vkq" means the field has a value role, that it is a key for the current instance, and that the value should be quoted when encoded for JSON. -.Ss The Colon Modifier ({c:}) +.Pp +Roles and modifiers can also use more verbose names, when preceeded by +a comma. +For example, the modifier string "Lwc" (or "L,white,colon") +means the field has a label role (text that describes the next field) +and should be followed by a colon ('c') and a space ('w'). +The modifier string "Vkq" (or ":key,quote") means the field has a value +role (the default role), that it is a key for the current instance, +and that the value should be quoted when encoded for JSON. +.Ss "The Colon Modifier ({c:})" The colon modifier appends a single colon to the data value: .Bd -literal -offset indent EXAMPLE: @@ -304,7 +399,7 @@ The colon modifier is only used for the TEXT and HTML output styles. It is commonly combined with the space modifier ('{w:}'). It is purely a convenience feature. -.Ss The Display Modifier ({d:}) +.Ss "The Display Modifier ({d:})" The display modifier indicated the field should only be generated for the display output styles, TEXT and HTML. .Bd -literal -offset indent @@ -318,7 +413,7 @@ the display output styles, TEXT and HTML. .Pp The display modifier is the opposite of the encoding modifier, and they are often used to give to distinct views of the underlying data. -.Ss The Encoding Modifier ({e:}) +.Ss "The Encoding Modifier ({e:})" The encoding modifier indicated the field should only be generated for the encoding output styles, such as JSON and XML. .Bd -literal -offset indent @@ -332,7 +427,60 @@ the encoding output styles, such as JSON and XML. .Pp The encoding modifier is the opposite of the display modifier, and they are often used to give to distinct views of the underlying data. -.Ss The Key Modifier ({k:}) +.Ss "The Humanize Modifier ({h:})" +The humanize modifier is used to render large numbers as in a +human-readable format. +While numbers like "44470272" are completely readable to computers and +savants, humans will generally find "44M" more meaningful. +.Pp +"hn" can be used as an alias for "humanize". +.Pp +The humanize modifier only affects display styles (TEXT and HMTL). +The "no-humanize" option will block the function of the humanize modifier. +.Pp +There are a number of modifiers that affect details of humanization. +These are only available in as full names, not single characters. +The "hn-space" modifier places a space between the number and any +multiplier symbol, such as "M" or "K" (ex: "44 K"). +The "hn-decimal" modifier will add a decimal point and a single tenths digit +when the number is less than 10 (ex: "4.4K"). +The "hn-1000" modifier will use 1000 as divisor instead of 1024, following the +JEDEC-standard instead of the more natural binary powers-of-two +tradition. +.Bd -literal -offset indent + EXAMPLE: + xo_emit("{h:input/%u}, {h,hn-space:output/%u}, " + "{h,hn-decimal:errors/%u}, {h,hn-1000:capacity/%u}, " + "{h,hn-decimal:remaining/%u}\n", + input, output, errors, capacity, remaining); + TEXT: + 21, 57 K, 96M, 44M, 1.2G +.Ed +.Pp +In the HTML style, the original numeric value is rendered in the +"data-number" attribute on the
element: +.Bd -literal -offset indent +
96M
+.Ed +.Ss "The Gettext Modifier ({g:})" +The gettext modifier is used to translate individual fields using the +gettext domain (typically set using the "{G:}" role) and current +language settings. +Once libxo renders the field value, it is passed +to +.Xr gettext 3 , +where it is used as a key to find the native language +translation. +.Pp +In the following example, the strings "State" and "full" are passed +to +.Fn gettext +to find locale-based translated strings. +.Bd -literal -offset indent + xo_emit("{Lgwc:State}{g:state}\n", "full"); +.Ed +.Ss "The Key Modifier ({k:})" The key modifier is used to indicate that a particular field helps uniquely identify an instance of list data. .Bd -literal -offset indent @@ -351,7 +499,7 @@ Currently the key modifier is only used when generating XPath values for the HTML output style when .Dv XOF_XPATH is set, but other uses are likely in the near future. -.Ss The Leaf-List Modifier ({l:}) +.Ss "The Leaf-List Modifier ({l:})" The leaf-list modifier is used to distinguish lists where each instance consists of only a single value. In XML, these are rendered as single elements, where JSON renders them as arrays. @@ -368,7 +516,7 @@ rendered as single elements, where JSON renders them as arrays. JSON: "user": [ "phil", "pallavi" ] .Ed -.Ss The No-Quotes Modifier ({n:}) +.Ss "The No-Quotes Modifier ({n:})" The no-quotes modifier (and its twin, the 'quotes' modifier) affect the quoting of values in the JSON output style. JSON uses quotes for @@ -383,7 +531,29 @@ needed, but often this needs to be controlled by the caller. JSON: "fancy": true .Ed -.Ss The Quotes Modifier ({q:}) +.Ss "The Plural Modifier ({p:})" +The plural modifier selects the appropriate plural form of an +expression based on the most recent number emitted and the current +language settings. +The contents of the field should be the singular +and plural English values, separated by a comma: +.Bd -literal -offset indent + xo_emit("{:bytes} {Ngp:byte,bytes}\n", bytes); +.Ed +The plural modifier is meant to work with the gettext modifier ({g:}) +but can work independently. +.Pp +When used without the gettext modifier or when the message does not +appear in the message catalog, the first token is chosen when the last +numeric value is equal to 1; otherwise the second value is used, +mimicking the simple pluralization rules of English. +.Pp +When used with the gettext modifier, the +.Xr ngettext 3 +function is +called to handle the heavy lifting, using the message catalog to +convert the singular and plural forms into the native language. +.Ss "The Quotes Modifier ({q:})" The quotes modifier (and its twin, the 'no-quotes' modifier) affect the quoting of values in the JSON output style. JSON uses quotes for @@ -397,7 +567,7 @@ needed, but often this needs to be controlled by the caller. JSON: "year": "2014" .Ed -.Ss The White Space Modifier ({w:}) +.Ss "The White Space Modifier ({w:})" The white space modifier appends a single space to the data value: .Bd -literal -offset indent EXAMPLE: @@ -413,7 +583,7 @@ It is purely a convenience feature. .Pp Note that the sense of the 'w' modifier is reversed for the units role ({Uw:}); a blank is added before the contents, rather than after it. -.Ss Field Formatting +.Ss "Field Formatting" The field format is similar to the format string for .Xr printf 3 . Its use varies based on the role of the field, but generally is used to @@ -440,7 +610,7 @@ The format- modifier can be: .Bl -bullet .It a '#' character, indicating the output value should be prefixed with -'0x', typically to indicate a base 16 (hex) value. +"0x", typically to indicate a base 16 (hex) value. .It a minus sign ('-'), indicating the output value should be padded on the right instead of the left. @@ -464,7 +634,7 @@ will never dereference memory beyond the given number of bytes. .It a second period followed by one or more digits indicating the maximum width for a string argument. -This modifier cannot be given for non-string arguments. +This modifier cannot be given for non-string arguments. .It one or more 'h' characters, indicating shorter input data. .It @@ -484,9 +654,8 @@ Note that 'q', 'D', 'O', and 'U' are considered deprecated and will be removed eventually. .Pp The format character is described in the following table: -.Pp .Bl -column C "Argument Type12" -.It Sy "C Argument Type Format" +.It Sy "C" "Argument Type " "Format" .It d "int " "base 10 (decimal)" .It i "int " "base 10 (decimal)" .It o "int " "base 8 (octal)" @@ -524,8 +693,7 @@ argument: .It "z " "size_t " "size_t" .It "q " "quad_t " "u_quad_t" .El -.Pp -.Ss UTF-8 and Locale Strings +.Ss "UTF-8 and Locale Strings" All strings for .Nm libxo must be UTF-8. @@ -542,10 +710,10 @@ Since UTF-8 is compatible with data, a normal 7-bit .Em ASCII string can be used. -'%ls' expects a -'wchar_t *' pointer to a wide-character string, encoded as 32-bit +"%ls" expects a +"wchar_t *" pointer to a wide-character string, encoded as 32-bit Unicode values. -'%hs' expects a 'char *' pointer to a multi-byte +"%hs" expects a "char *" pointer to a multi-byte string encoded with the current locale, as given by the .Ev LC_CTYPE , .Ev LANG , @@ -612,11 +780,11 @@ and adds a third value for specifying the maximum number of columns. .Pp In this example, the name field is printed with a minimum of 3 columns and a maximum of 6. -Up to ten bytes are in used in filling those columns. +Up to ten bytes are in used in filling those columns. .Bd -literal -offset indent xo_emit("{:name/%3.10.6s}", name); .Ed -.Ss Characters Outside of Field Definitions +.Ss "Characters Outside of Field Definitions" Characters in the format string that are not part of a field definition are copied to the output for the TEXT style, and are ignored for the JSON and XML styles. @@ -635,12 +803,12 @@ For HTML, these characters are placed in a
with class "text".
extra small
.
.Ed -.Ss "%n" is Not Supported +.Ss "'%n' is Not Supported" .Nm libxo does not support the '%n' directive. It is a bad idea and we just do not do it. -.Ss The Encoding Format (eformat) +.Ss "The Encoding Format (eformat)" The "eformat" string is the format string used when encoding the field for JSON and XML. If not provided, it defaults to the primary format @@ -655,7 +823,7 @@ In this example, the value for the number of items in stock is emitted: .Pp This call will generate the following output: .Bd -literal -offset indent - TEXT: + TEXT: In stock: 144 XML: 144 @@ -685,25 +853,24 @@ data, which would expand the penultimate line to: .Ed .Sh WHAT MAKES A GOOD FIELD NAME? To make useful, consistent field names, follow these guidelines: -.Pp -.Ss Use lower case, even for TLAs +.Ss "Use lower case, even for TLAs" Lower case is more civilized. Even TLAs should be lower case to avoid scenarios where the differences between "XPath" and "Xpath" drive your users crazy. Using "xpath" is simpler and better. -.Ss Use hyphens, not underscores +.Ss "Use hyphens, not underscores" Use of hyphens is traditional in XML, and the .Dv XOF_UNDERSCORES flag can be used to generate underscores in JSON, if desired. But the raw field name should use hyphens. -.Ss Use full words +.Ss "Use full words" Do not abbreviate especially when the abbreviation is not obvious or not widely used. Use "data-size", not "dsz" or "dsize". Use "interface" instead of "ifname", "if-name", "iface", "if", or "intf". -.Ss Use - +.Ss "Use -" Using the form - or -- helps in making consistent, useful names, avoiding the situation where one app uses "sent-packet" and another "packets-sent" and another @@ -712,10 +879,10 @@ The can be dropped when it is obvious, as can obvious words in the classification. Use "receive-after-window-packets" instead of "received-packets-of-data-after-window". -.Ss Reuse existing field names +.Ss "Reuse existing field names" Nothing is worse than writing expressions like: .Bd -literal -offset indent - if ($src1/process[pid == $pid]/name == + if ($src1/process[pid == $pid]/name == $src2/proc-table/proc/p[process-id == $pid]/proc-name) { ... } @@ -724,16 +891,16 @@ Nothing is worse than writing expressions like: Find someone else who is expressing similar data and follow their fields and hierarchy. Remember the quote is not -.Dq Consistency is the hobgoblin of little minds +.Dq "Consistency is the hobgoblin of little minds" but -.Dq A foolish consistency is the hobgoblin of little minds . -.Ss Think about your users +.Dq "A foolish consistency is the hobgoblin of little minds" . +.Ss "Think about your users" Have empathy for your users, choosing clear and useful fields that contain clear and useful data. You may need to augment the display content with .Xr xo_attr 3 calls or "{e:}" fields to make the data useful. -.Ss Do not use an arbitrary number postfix +.Ss "Do not use an arbitrary number postfix" What does "errors2" mean? No one will know. "errors-after-restart" would be a better choice. @@ -741,7 +908,7 @@ Think of your users, and think of the future. If you make "errors2", the next guy will happily make "errors3" and before you know it, someone will be asking what is the difference between errors37 and errors63. -.Ss Be consistent, uniform, unsurprising, and predictable +.Ss "Be consistent, uniform, unsurprising, and predictable" Think of your field vocabulary as an API. You want it useful, expressive, meaningful, direct, and obvious. @@ -770,31 +937,7 @@ If there is no difference, use only one of the field names. If there is a difference, change the names to make that difference more obvious. -.Sh ADDITIONAL DOCUMENTATION -Complete documentation can be found on github: -.Bd -literal -offset indent -http://juniper.github.io/libxo/libxo-manual.html -.Ed -.Pp -.Nm libxo -lives on github as: -.Bd -literal -offset indent -https://github.com/Juniper/libxo -.Ed -.Pp -The latest release of -.Nm libxo -is available at: -.Bd -literal -offset indent -https://github.com/Juniper/libxo/releases -.Ed .Sh SEE ALSO +.Xr libxo 3 , .Xr xolint 1 , .Xr xo_emit 3 -.Sh HISTORY -The -.Nm libxo -library was added in -.Fx 11.0 . -.Sh AUTHOR -Phil Shafer diff --git a/contrib/libxo/libxo/xo_humanize.h b/contrib/libxo/libxo/xo_humanize.h new file mode 100644 index 000000000000..edf85b8bb1ba --- /dev/null +++ b/contrib/libxo/libxo/xo_humanize.h @@ -0,0 +1,169 @@ +/* $NetBSD: humanize_number.c,v 1.8 2004/07/27 01:56:24 enami Exp $ */ + +/* + * Copyright (c) 1997, 1998, 1999, 2002 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, + * NASA Ames Research Center, by Luke Mewburn and by Tomas Svensson. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* humanize_number(3) */ +#define HN_DECIMAL 0x01 +#define HN_NOSPACE 0x02 +#define HN_B 0x04 +#define HN_DIVISOR_1000 0x08 + +#define HN_GETSCALE 0x10 +#define HN_AUTOSCALE 0x20 + +static int +xo_humanize_number (char *buf, size_t len, int64_t bytes, + const char *suffix, int scale, int flags) +{ + const char *prefixes, *sep; + int b, i, r, maxscale, s1, s2, sign; + int64_t divisor, max; + // We multiply bytes by 100 to deal with rounding, so we need something + // big enough to hold LLONG_MAX * 100. On 64-bit we can use 128-bit wide + // integers with __int128_t, but on 32-bit we have to use long double. +#ifdef __LP64__ + __int128_t scalable = (__int128_t)bytes; +#else + long double scalable = (long double)bytes; +#endif + size_t baselen; + + assert(buf != NULL); + assert(suffix != NULL); + assert(scale >= 0); + + if (flags & HN_DIVISOR_1000) { + /* SI for decimal multiplies */ + divisor = 1000; + if (flags & HN_B) + prefixes = "B\0k\0M\0G\0T\0P\0E"; + else + prefixes = "\0\0k\0M\0G\0T\0P\0E"; + } else { + /* + * binary multiplies + * XXX IEC 60027-2 recommends Ki, Mi, Gi... + */ + divisor = 1024; + if (flags & HN_B) + prefixes = "B\0K\0M\0G\0T\0P\0E"; + else + prefixes = "\0\0K\0M\0G\0T\0P\0E"; + } + +#define SCALE2PREFIX(scale) (&prefixes[(scale) << 1]) + maxscale = 7; + + if (scale >= maxscale && + (scale & (HN_AUTOSCALE | HN_GETSCALE)) == 0) + return (-1); + + if (buf == NULL || suffix == NULL) + return (-1); + + if (len > 0) + buf[0] = '\0'; + if (bytes < 0) { + sign = -1; + scalable *= -100; + baselen = 3; /* sign, digit, prefix */ + } else { + sign = 1; + scalable *= 100; + baselen = 2; /* digit, prefix */ + } + if (flags & HN_NOSPACE) + sep = ""; + else { + sep = " "; + baselen++; + } + baselen += strlen(suffix); + + /* Check if enough room for `x y' + suffix + `\0' */ + if (len < baselen + 1) + return (-1); + + if (scale & (HN_AUTOSCALE | HN_GETSCALE)) { + /* See if there is additional columns can be used. */ + for (max = 100, i = len - baselen; i-- > 0;) + max *= 10; + + for (i = 0; scalable >= max && i < maxscale; i++) + scalable /= divisor; + + if (scale & HN_GETSCALE) + return (i); + } else + for (i = 0; i < scale && i < maxscale; i++) + scalable /= divisor; + + /* If a value <= 9.9 after rounding and ... */ + if (scalable < 995 && i > 0 && flags & HN_DECIMAL) { + /* baselen + \0 + .N */ + if (len < baselen + 1 + 2) + return (-1); + b = ((int)scalable + 5) / 10; + s1 = b / 10; + s2 = b % 10; + r = snprintf(buf, len, "%s%d%s%d%s%s%s", + ((sign == -1) ? "-" : ""), + s1, localeconv()->decimal_point, s2, + sep, SCALE2PREFIX(i), suffix); + } else + r = snprintf(buf, len, "%s%lld%s%s%s", + /* LONGLONG */ + ((sign == -1) ? "-" : ""), + (long long)((scalable + 50) / 100), + sep, SCALE2PREFIX(i), suffix); + + return (r); +} diff --git a/contrib/libxo/libxo/xo_message.3 b/contrib/libxo/libxo/xo_message.3 new file mode 100644 index 000000000000..36f014884999 --- /dev/null +++ b/contrib/libxo/libxo/xo_message.3 @@ -0,0 +1,68 @@ +.\" # +.\" # Copyright (c) 2014, Juniper Networks, Inc. +.\" # All rights reserved. +.\" # This SOFTWARE is licensed under the LICENSE provided in the +.\" # ../Copyright file. By downloading, installing, copying, or +.\" # using the SOFTWARE, you agree to be bound by the terms of that +.\" # LICENSE. +.\" # Phil Shafer, July 2014 +.\" +.Dd December 4, 2014 +.Dt LIBXO 3 +.Os +.Sh NAME +.Nm xo_message , xo_message_c , xo_message_hc , xo_message_hcv +.Nd emit messages in multiple output styles +.Sh LIBRARY +.Lb libxo +.Sh SYNOPSIS +.In libxo/xo.h +.Ft void +.Fn xo_message "const char *fmt" "..." +.Ft void +.Fn xo_message_e "const char *fmt" "..." +.Ft void +.Fn xo_message_c "int code" "const char *fmt" "..." +.Ft void +.Fn xo_message_hc "xo_handle_t *xop" "int code, const char *fmt" "..." +.Ft void +.Fn xo_message_hcv "xo_handle_t *xop" "int code" "const char *fmt" "va_list vap" +.Sh DESCRIPTION +.Nm xo_message +generates text message which lack any sort of structure. +These functions should not be used under normal conditions, since +they completely defeat the value of using libxo. They are provided +for scenarios when the output's content is genuinely unknown and +unusable. +It is used in converting programs where err/warn where not used, +and error messages went to +.Nm stdout , +not +.Nm stderr . +Use of +.Nm xo_message +allows backwards compatibility with that output, but does not put +the error in a useful form. +.Pp +The +.Nm xo_message +function generates output strings using the printf-style format string +and arguments provided. +If the format string does not end in a newline, +.Nm xo_message_e +will appear a colon, a space, and the error associated with the current +.Nm errno +value. +.Nm xo_message_c behaves similarly for the value passed in the +.Fa code +parameter. +.Nm xo_message_hc +accepts a +.Fa handle +as opened by +.Xr xo_create 3 +and +.Nm xo_message_hcv accepts a va_list parameter of arguments. +.Sh SEE ALSO +.Xr xo_emit 3 , +.Xr libxo 3 diff --git a/contrib/libxo/libxo/xo_no_setlocale.3 b/contrib/libxo/libxo/xo_no_setlocale.3 index c3f32a403d03..f91abc005107 100644 --- a/contrib/libxo/libxo/xo_no_setlocale.3 +++ b/contrib/libxo/libxo/xo_no_setlocale.3 @@ -12,8 +12,7 @@ .Os .Sh NAME .Nm xo_no_setlocale -.Nd prevent implicit call to -.Fn setlocale +.Nd prevent implicit call to setlocale .Sh LIBRARY .Lb libxo .Sh SYNOPSIS @@ -36,33 +35,9 @@ The caller may wish to avoid this behavior, and can do so by calling the .Fn xo_no_setlocale function. -.Sh ADDITIONAL DOCUMENTATION -Complete documentation can be found on github: -.Bd -literal -offset indent -http://juniper.github.io/libxo/libxo-manual.html -.Ed -.Pp -.Nm libxo -lives on github as: -.Bd -literal -offset indent -https://github.com/Juniper/libxo -.Ed -.Pp -The latest release of -.Nm libxo -is available at: -.Bd -literal -offset indent -https://github.com/Juniper/libxo/releases -.Ed .Sh SEE ALSO .Xr xo_emit 3 , .Xr xo_open_container 3 , .Xr xo_open_list 3 , -.Xr xo_format 5 -.Sh HISTORY -The -.Nm libxo -library was added in -.Fx 11.0 . -.Sh AUTHOR -Phil Shafer +.Xr xo_format 5 , +.Xr libxo 3 diff --git a/contrib/libxo/libxo/xo_open_container.3 b/contrib/libxo/libxo/xo_open_container.3 index 364955628964..8b016dc15ef6 100644 --- a/contrib/libxo/libxo/xo_open_container.3 +++ b/contrib/libxo/libxo/xo_open_container.3 @@ -11,25 +11,13 @@ .Dt LIBXO 3 .Os .Sh NAME -.Nm xo_open_container +.Nm xo_open_container , xo_open_container_h , xo_open_container_hd , xo_open_container_d +.Nm xo_close_container , xo_close_container_h , xo_close_container_hd , xo_close_container_d .Nd open (and close) container constructs .Sh LIBRARY .Lb libxo .Sh SYNOPSIS .In libxo/xo.h -.Sh NAME -.Nm xo_open_container -.Nm xo_open_container_h -.Nm xo_open_container_hd -.Nm xo_open_container_d -.Nm xo_close_container -.Nm xo_close_container_h -.Nm xo_close_container_hd -.Nm xo_close_container_d -.Nd open and close containers -.Sh LIBRARY -.Lb libxo -.Sh SYNOPSIS .Ft int .Fn xo_open_container "const char *name" .Ft int @@ -195,30 +183,6 @@ to track open containers, lists, and instances. A warning is generated when the name given to the close function and the name recorded do not match. -.Sh ADDITIONAL DOCUMENTATION -Complete documentation can be found on github: -.Bd -literal -offset indent -http://juniper.github.io/libxo/libxo-manual.html -.Ed -.Pp -.Nm libxo -lives on github as: -.Bd -literal -offset indent -https://github.com/Juniper/libxo -.Ed -.Pp -The latest release of -.Nm libxo -is available at: -.Bd -literal -offset indent -https://github.com/Juniper/libxo/releases -.Ed .Sh SEE ALSO -.Xr xo_emit 3 -.Sh HISTORY -The -.Nm libxo -library was added in -.Fx 11.0 . -.Sh AUTHOR -Phil Shafer +.Xr xo_emit 3 , +.Xr libxo 3 diff --git a/contrib/libxo/libxo/xo_open_list.3 b/contrib/libxo/libxo/xo_open_list.3 index 047af87596fa..7930dd1356d3 100644 --- a/contrib/libxo/libxo/xo_open_list.3 +++ b/contrib/libxo/libxo/xo_open_list.3 @@ -11,33 +11,15 @@ .Dt LIBXO 3 .Os .Sh NAME -.Nm xo_emit -.Nd emit formatted output based on format string and arguments -.Sh LIBRARY -.Lb libxo -.Sh SYNOPSIS -.In libxo/xo.h -.Sh NAME -.Nm xo_open_list -.Nm xo_open_list_h -.Nm xo_open_list_hd -.Nm xo_open_list_d -.Nm xo_open_instance -.Nm xo_open_instance_h -.Nm xo_open_instance_hd -.Nm xo_open_instance_d -.Nm xo_close_instance -.Nm xo_close_instance_h -.Nm xo_close_instance_hd -.Nm xo_close_instance_d -.Nm xo_close_list -.Nm xo_close_list_h -.Nm xo_close_list_hd -.Nm xo_close_list_d +.Nm xo_open_list , xo_open_list_h , xo_open_list_hd , xo_open_list_d +.Nm xo_open_instance , xo_open_instance_h , xo_open_instance_hd , xo_open_instance_d +.Nm xo_close_instance , xo_close_instance_h , xo_close_instance_hd , xo_close_instnace_d +.Nm xo_close_list , xo_close_list_h , xo_close_list_hd , xo_close_list_d .Nd open and close lists and instances .Sh LIBRARY .Lb libxo .Sh SYNOPSIS +.In libxo/xo.h .Ft int .Fn xo_open_list_h "xo_handle_t *xop" "const char *name" .Ft int @@ -171,30 +153,6 @@ are rendered as multiple leaf elements. hammer nail .Ed -.Sh ADDITIONAL DOCUMENTATION -Complete documentation can be found on github: -.Bd -literal -offset indent -http://juniper.github.io/libxo/libxo-manual.html -.Ed -.Pp -.Nm libxo -lives on github as: -.Bd -literal -offset indent -https://github.com/Juniper/libxo -.Ed -.Pp -The latest release of -.Nm libxo -is available at: -.Bd -literal -offset indent -https://github.com/Juniper/libxo/releases -.Ed .Sh SEE ALSO -.Xr xo_emit 3 -.Sh HISTORY -The -.Nm libxo -library was added in -.Fx 11.0 . -.Sh AUTHOR -Phil Shafer +.Xr xo_emit 3 , +.Xr libxo 3 diff --git a/contrib/libxo/libxo/xo_open_marker.3 b/contrib/libxo/libxo/xo_open_marker.3 index d7a858c8c207..e7356ba6e6f6 100644 --- a/contrib/libxo/libxo/xo_open_marker.3 +++ b/contrib/libxo/libxo/xo_open_marker.3 @@ -11,21 +11,12 @@ .Dt LIBXO 3 .Os .Sh NAME -.Nm xo_open_marker +.Nm xo_open_marker , xo_open_marker_h , xo_close_marker , xo_close_marker_h .Nd prevent and allow closing of open constructs .Sh LIBRARY .Lb libxo .Sh SYNOPSIS .In libxo/xo.h -.Sh NAME -.Nm xo_open_marker -.Nm xo_open_marker_h -.Nm xo_close_marker -.Nm xo_close_marker_h -.Nd open and close markers -.Sh LIBRARY -.Lb libxo -.Sh SYNOPSIS .Ft int .Fn xo_open_marker "const char *name" .Ft int @@ -109,30 +100,6 @@ properly. xo_close_marker("fish-guts"); } .Ed -.Sh ADDITIONAL DOCUMENTATION -Complete documentation can be found on github: -.Bd -literal -offset indent -http://juniper.github.io/libxo/libxo-manual.html -.Ed -.Pp -.Nm libxo -lives on github as: -.Bd -literal -offset indent -https://github.com/Juniper/libxo -.Ed -.Pp -The latest release of -.Nm libxo -is available at: -.Bd -literal -offset indent -https://github.com/Juniper/libxo/releases -.Ed .Sh SEE ALSO -.Xr xo_emit 3 -.Sh HISTORY -The -.Nm libxo -library was added in -.Fx 11.0 . -.Sh AUTHOR -Phil Shafer +.Xr xo_emit 3 , +.Xr libxo 3 diff --git a/contrib/libxo/libxo/xo_parse_args.3 b/contrib/libxo/libxo/xo_parse_args.3 index f66546bd3c9e..80dceca916be 100644 --- a/contrib/libxo/libxo/xo_parse_args.3 +++ b/contrib/libxo/libxo/xo_parse_args.3 @@ -11,7 +11,7 @@ .Dt LIBXO 3 .Os .Sh NAME -.Nm xo_parse_args +.Nm xo_parse_args , xo_set_program .Nd detect, parse, and remove arguments for libxo .Sh LIBRARY .Lb libxo @@ -35,7 +35,7 @@ On failure, a message it emitted and -1 is returned. .Bd -literal -offset indent argc = xo_parse_args(argc, argv); if (argc < 0) - exit(1); + exit(EXIT_FAILURE); .Ed .Pp Following the call to @@ -72,6 +72,15 @@ Add info attributes (HTML) Emit JSON output .It Dv keys Emit the key attribute for keys (XML) +.It Dv log-gettext +Log (via stderr) each +.Xr gettext 3 +string lookup +.It Dv log-syslog +Log (via stderr) each syslog message (via +.Xr xo_syslog 3 ) +.If Dv no-humanize +Ignore the {h:} modifier (TEXT, HTML) .It Dv no-locale Do not initialize the locale setting .It Dv no-top @@ -82,6 +91,8 @@ Pretend the 1st output item was not 1st (JSON) Emit pretty-printed output .It Dv text Emit TEXT output +.If Dv underscores +Replace XML-friendly "-"s with JSON friendly "_"s e .It Dv units Add the 'units' (XML) or 'data-units (HTML) attribute .It Dv warn @@ -132,30 +143,6 @@ Note that the value is not copied, so the memory passed to .Fn xo_parse_args ) must be maintained by the caller. .Pp -.Sh ADDITIONAL DOCUMENTATION -Complete documentation can be found on github: -.Bd -literal -offset indent -http://juniper.github.io/libxo/libxo-manual.html -.Ed -.Pp -.Nm libxo -lives on github as: -.Bd -literal -offset indent -https://github.com/Juniper/libxo -.Ed -.Pp -The latest release of -.Nm libxo -is available at: -.Bd -literal -offset indent -https://github.com/Juniper/libxo/releases -.Ed .Sh SEE ALSO -.Xr xo_emit 3 -.Sh HISTORY -The -.Nm libxo -library was added in -.Fx 11.0 . -.Sh AUTHOR -Phil Shafer +.Xr xo_emit 3 , +.Xr libxo 3 diff --git a/contrib/libxo/libxo/xo_set_allocator.3 b/contrib/libxo/libxo/xo_set_allocator.3 index 70bfdd59a45d..c20a0f5230e5 100644 --- a/contrib/libxo/libxo/xo_set_allocator.3 +++ b/contrib/libxo/libxo/xo_set_allocator.3 @@ -11,8 +11,8 @@ .Dt LIBXO 3 .Os .Sh NAME -.Nm xo_emit -.Nd emit formatted output based on format string and arguments +.Nm xo_set_allocator +.Nd set allocation functions for libxo .Sh LIBRARY .Lb libxo .Sh SYNOPSIS @@ -49,30 +49,6 @@ By default, the standard and .Xr free 3 functions are used. -.Sh ADDITIONAL DOCUMENTATION -Complete documentation can be found on github: -.Bd -literal -offset indent -http://juniper.github.io/libxo/libxo-manual.html -.Ed -.Pp -.Nm libxo -lives on github as: -.Bd -literal -offset indent -https://github.com/Juniper/libxo -.Ed -.Pp -The latest release of -.Nm libxo -is available at: -.Bd -literal -offset indent -https://github.com/Juniper/libxo/releases -.Ed .Sh SEE ALSO -.Xr xo_emit 3 -.Sh HISTORY -The -.Nm libxo -library was added in -.Fx 11.0 . -.Sh AUTHOR -Phil Shafer +.Xr xo_emit 3 , +.Xr libxo 3 diff --git a/contrib/libxo/libxo/xo_set_flags.3 b/contrib/libxo/libxo/xo_set_flags.3 index ca6655360d21..52997c5ec1c1 100644 --- a/contrib/libxo/libxo/xo_set_flags.3 +++ b/contrib/libxo/libxo/xo_set_flags.3 @@ -11,8 +11,8 @@ .Dt LIBXO 3 .Os .Sh NAME -.Nm xo_emit -.Nd emit formatted output based on format string and arguments +.Nm xo_set_flags , xo_clear_flags +.Nd set operational flags for a libxo handle .Sh LIBRARY .Lb libxo .Sh SYNOPSIS @@ -42,12 +42,21 @@ This flag will trigger the call of the (provided via .Xr xo_set_writer 3 ) when the handle is destroyed. -.It Dv XOF_DTRT -Enable "do the right thing" mode +.It Dv XOF_COLOR +Enable color and effects in output regardless of output device. +.It Dv XOF_COLOR_ALLOWED +Allow color and effects if the output device is a terminal. .It Dv XOF_INFO Display info data attributes (HTML) .It Dv XOF_KEYS Emit the key attribute (XML) +.It Dv XOF_LOG_GETTEXT +Log (via stderr) each +.Xr gettext 3 +string lookup +.It Dv XOF_LOG_SYSLOG +Log (via stderr) each syslog message (via +.Xr xo_syslog 3 ) .It Dv XOF_NO_ENV Do not use the .Ev LIBXO_OPTIONS @@ -125,30 +134,6 @@ The .Fn xo_clear_flags function turns off the given flags in a specific handle. -.Sh ADDITIONAL DOCUMENTATION -Complete documentation can be found on github: -.Bd -literal -offset indent -http://juniper.github.io/libxo/libxo-manual.html -.Ed -.Pp -.Nm libxo -lives on github as: -.Bd -literal -offset indent -https://github.com/Juniper/libxo -.Ed -.Pp -The latest release of -.Nm libxo -is available at: -.Bd -literal -offset indent -https://github.com/Juniper/libxo/releases -.Ed .Sh SEE ALSO -.Xr xo_emit 3 -.Sh HISTORY -The -.Nm libxo -library was added in -.Fx 11.0 . -.Sh AUTHOR -Phil Shafer +.Xr xo_emit 3 , +.Xr libxo 3 diff --git a/contrib/libxo/libxo/xo_set_info.3 b/contrib/libxo/libxo/xo_set_info.3 index 4f8c5877c72a..8ea06570a68e 100644 --- a/contrib/libxo/libxo/xo_set_info.3 +++ b/contrib/libxo/libxo/xo_set_info.3 @@ -11,8 +11,8 @@ .Dt LIBXO 3 .Os .Sh NAME -.Nm xo_emit -.Nd emit formatted output based on format string and arguments +.Nm xo_set_info +.Nd set the field information data for libxo .Sh LIBRARY .Lb libxo .Sh SYNOPSIS @@ -97,30 +97,6 @@ and "data-help" attributes:
GRO-000-533
.Ed -.Sh ADDITIONAL DOCUMENTATION -Complete documentation can be found on github: -.Bd -literal -offset indent -http://juniper.github.io/libxo/libxo-manual.html -.Ed -.Pp -.Nm libxo -lives on github as: -.Bd -literal -offset indent -https://github.com/Juniper/libxo -.Ed -.Pp -The latest release of -.Nm libxo -is available at: -.Bd -literal -offset indent -https://github.com/Juniper/libxo/releases -.Ed .Sh SEE ALSO -.Xr xo_emit 3 -.Sh HISTORY -The -.Nm libxo -library was added in -.Fx 11.0 . -.Sh AUTHOR -Phil Shafer +.Xr xo_emit 3 , +.Xr libxo 3 diff --git a/contrib/libxo/libxo/xo_set_options.3 b/contrib/libxo/libxo/xo_set_options.3 index bedbf912da15..5b7c8eda72a5 100644 --- a/contrib/libxo/libxo/xo_set_options.3 +++ b/contrib/libxo/libxo/xo_set_options.3 @@ -12,7 +12,7 @@ .Os .Sh NAME .Nm xo_set_options -.Nd change options used by a handle +.Nd change options used by a libxo handle .Sh LIBRARY .Lb libxo .Sh SYNOPSIS @@ -26,30 +26,6 @@ function accepts a comma-separated list of styles and flags and enables them for a specific handle. The options are identical to those listed in .Xr xo_parse_args 3 . -.Sh ADDITIONAL DOCUMENTATION -Complete documentation can be found on github: -.Bd -literal -offset indent -http://juniper.github.io/libxo/libxo-manual.html -.Ed -.Pp -.Nm libxo -lives on github as: -.Bd -literal -offset indent -https://github.com/Juniper/libxo -.Ed -.Pp -The latest release of -.Nm libxo -is available at: -.Bd -literal -offset indent -https://github.com/Juniper/libxo/releases -.Ed .Sh SEE ALSO -.Xr xo_emit 3 -.Sh HISTORY -The -.Nm libxo -library was added in -.Fx 11.0 . -.Sh AUTHOR -Phil Shafer +.Xr xo_emit 3 , +.Xr libxo 3 diff --git a/contrib/libxo/libxo/xo_set_style.3 b/contrib/libxo/libxo/xo_set_style.3 index f11f190bf13f..8e34033a5953 100644 --- a/contrib/libxo/libxo/xo_set_style.3 +++ b/contrib/libxo/libxo/xo_set_style.3 @@ -11,8 +11,8 @@ .Dt LIBXO 3 .Os .Sh NAME -.Nm xo_emit -.Nd emit formatted output based on format string and arguments +.Nm xo_set_style , xo_set_style_name +.Nd set the output style for a libxo handle .Sh LIBRARY .Lb libxo .Sh SYNOPSIS @@ -48,30 +48,6 @@ The name can be any of the styles: "text", "xml", "json", or "html". EXAMPLE: xo_set_style_name(NULL, "html"); .Ed -.Sh ADDITIONAL DOCUMENTATION -Complete documentation can be found on github: -.Bd -literal -offset indent -http://juniper.github.io/libxo/libxo-manual.html -.Ed -.Pp -.Nm libxo -lives on github as: -.Bd -literal -offset indent -https://github.com/Juniper/libxo -.Ed -.Pp -The latest release of -.Nm libxo -is available at: -.Bd -literal -offset indent -https://github.com/Juniper/libxo/releases -.Ed .Sh SEE ALSO -.Xr xo_emit 3 -.Sh HISTORY -The -.Nm libxo -library was added in -.Fx 11.0 . -.Sh AUTHOR -Phil Shafer +.Xr xo_emit 3 , +.Xr libxo 3 diff --git a/contrib/libxo/libxo/xo_set_syslog_enterprise_id.3 b/contrib/libxo/libxo/xo_set_syslog_enterprise_id.3 new file mode 100644 index 000000000000..da2eed73a939 --- /dev/null +++ b/contrib/libxo/libxo/xo_set_syslog_enterprise_id.3 @@ -0,0 +1,36 @@ +.\" # +.\" # Copyright (c) 2015, Juniper Networks, Inc. +.\" # All rights reserved. +.\" # This SOFTWARE is licensed under the LICENSE provided in the +.\" # ../Copyright file. By downloading, installing, copying, or +.\" # using the SOFTWARE, you agree to be bound by the terms of that +.\" # LICENSE. +.\" # Phil Shafer, July 2015 +.\" +.Dd July 20, 2015 +.Dt LIBXO 3 +.Os +.Sh NAME +.Nm xo_set_syslog_enterprise_id +.Nd Set the enterprise identifier for syslog content +.Sh LIBRARY +.Lb libxo +.Sh SYNOPSIS +.In libxo/xo.h +.Ft void +.Fn xo_set_syslog_enterprise_id "unsigned short eid" +.Ft void +.Sh DESCRIPTION +The +.Fn xo_set_syslog_enterprise_id +function records an enterprise identifier used for subsequent +.Xr xo_syslog 3 +calls. +Enterprise IDs are +defined by IANA, the Internet Assigned Numbers Authority: +.Bd -literal -offset indent +https://www.iana.org/assignments/enterprise-numbers/enterprise-numbers +.Ed +.Sh SEE ALSO +.Xr xo_syslog 3 , +.Xr libxo 3 diff --git a/contrib/libxo/libxo/xo_set_version.3 b/contrib/libxo/libxo/xo_set_version.3 index 888aef5e61ff..5f9394d5c6d6 100644 --- a/contrib/libxo/libxo/xo_set_version.3 +++ b/contrib/libxo/libxo/xo_set_version.3 @@ -11,8 +11,8 @@ .Dt LIBXO 3 .Os .Sh NAME -.Nm xo_set_version -.Nd record content version information in encoded output +.Nm xo_set_version , xo_set_version_h +.Nd record content-version information in encoded output .Sh LIBRARY .Lb libxo .Sh SYNOPSIS @@ -29,31 +29,6 @@ part of the data for encoding styles (XML and JSON). This version number is suitable for tracking changes in the content, allowing a user of the data to discern which version of the data model is in use. -.Sh ADDITIONAL DOCUMENTATION -Complete documentation can be found on github: -.Bd -literal -offset indent -http://juniper.github.io/libxo/libxo-manual.html -.Ed -.Pp -.Nm libxo -lives on github as: -.Bd -literal -offset indent -https://github.com/Juniper/libxo -.Ed -.Pp -The latest release of -.Nm libxo -is available at: -.Bd -literal -offset indent -https://github.com/Juniper/libxo/releases -.Ed .Sh SEE ALSO .Xr xo_emit 3 , .Xr libxo 3 -.Sh HISTORY -The -.Nm libxo -library was added in -.Fx 11.0 . -.Sh AUTHOR -Phil Shafer diff --git a/contrib/libxo/libxo/xo_set_writer.3 b/contrib/libxo/libxo/xo_set_writer.3 index 942bcc2866b6..2f93bd94a44f 100644 --- a/contrib/libxo/libxo/xo_set_writer.3 +++ b/contrib/libxo/libxo/xo_set_writer.3 @@ -11,8 +11,8 @@ .Dt LIBXO 3 .Os .Sh NAME -.Nm xo_emit -.Nd emit formatted output based on format string and arguments +.Nm xo_set_writer +.Nd set custom writer functions for a libxo handle .Sh LIBRARY .Lb libxo .Sh SYNOPSIS @@ -51,30 +51,6 @@ The .Fa flush_func function should flush any pending data associated with the opaque pointer. -.Sh ADDITIONAL DOCUMENTATION -Complete documentation can be found on github: -.Bd -literal -offset indent -http://juniper.github.io/libxo/libxo-manual.html -.Ed -.Pp -.Nm libxo -lives on github as: -.Bd -literal -offset indent -https://github.com/Juniper/libxo -.Ed -.Pp -The latest release of -.Nm libxo -is available at: -.Bd -literal -offset indent -https://github.com/Juniper/libxo/releases -.Ed .Sh SEE ALSO -.Xr xo_emit 3 -.Sh HISTORY -The -.Nm libxo -library was added in -.Fx 11.0 . -.Sh AUTHOR -Phil Shafer +.Xr xo_emit 3 , +.Xr libxo 3 diff --git a/contrib/libxo/libxo/xo_syslog.3 b/contrib/libxo/libxo/xo_syslog.3 new file mode 100644 index 000000000000..db92d3693c53 --- /dev/null +++ b/contrib/libxo/libxo/xo_syslog.3 @@ -0,0 +1,79 @@ +.\" # +.\" # Copyright (c) 2015, Juniper Networks, Inc. +.\" # All rights reserved. +.\" # This SOFTWARE is licensed under the LICENSE provided in the +.\" # ../Copyright file. By downloading, installing, copying, or +.\" # using the SOFTWARE, you agree to be bound by the terms of that +.\" # LICENSE. +.\" # Phil Shafer, July 2015 +.\" +.Dd July 20, 2015 +.Dt LIBXO 3 +.Os +.Sh NAME +.Nm xo_syslog , xo_vsyslog , xo_open_log , xo_close_log , xo_set_logmask +.Nd create SYSLOG (RFC5424) log records using libxo formatting +.Sh LIBRARY +.Lb libxo +.Sh SYNOPSIS +.In libxo/xo.h +.Ft void +.Fn xo_syslog "int pri" "const char *name" "const char *fmt" "..." +.Ft void +.Fn xo_vsyslog "int pri" "const char *name" "const char *fmt" "va_list vap" +.Ft void +.Fn xo_close_log "void" +.Ft void +.Fn xo_open_log "const char *ident" "int logstat" "int logfac" +.Ft int +.Fn xo_set_logmask "int pmask" +.Sh DESCRIPTION +The +.Fn xo_syslog +function creates log entries following the standard defined in +RFC5424. +These messages are sent to the log +.Xr syslogd 8 +daemon, where they can be filtered, forwarded, and archived. +.Nm libxo +format strings are used to create both the message text and the +.Nm SD-PARAMS +content, containing name/value pairs that can be parsed by suitable +automation software. +.Pp +Refer to +.Xr xo_format 5 +for basic information about formatting strings. +.Nm xo_syslog +encodes all value fields at SD-PARAMS within the syslog message. +An exception is made for fields with the "{d:}" modifier; such fields +appear in the message text only, with fields with the "{e:}" modifier +appear as SD-PARAMS, but not in the message text. +.Pp +.Fn xo_vsyslog +accepts a +.Fa va_list +for additional flexibility. +.Pp +.Fn xo_open_log , +.Fn xo_close_log , and +.Fn xo_set_logmask +are all analogous to their libs counterparts, +.Xr openlog 3 , +.Xr closelog 3 , and +.Xr setlogmask 3 . +The extra underscores in the names are unfortunate, but keep +consistency in +.Nm libxo +function names. +.Sh EXAMPLES +.Bd -literal -offset indent + xo_syslog(LOG_LOCAL4 | LOG_NOTICE, "ID47", + "{e:iut/%u}An {:event-source} {:event-id/%u} log entry", + iut, source, id); +.Ed +.Sh SEE ALSO +.Xr xo_syslog 3 , +.Xr xo_set_syslog_enterprise_id 3 , +.Xr xo_format 5 , +.Xr libxo 3 diff --git a/contrib/libxo/libxo/xo_syslog.c b/contrib/libxo/libxo/xo_syslog.c new file mode 100644 index 000000000000..da53e0c2e58e --- /dev/null +++ b/contrib/libxo/libxo/xo_syslog.c @@ -0,0 +1,706 @@ +/* + * Copyright (c) 2015, Juniper Networks, Inc. + * All rights reserved. + * This SOFTWARE is licensed under the LICENSE provided in the + * ../Copyright file. By downloading, installing, copying, or otherwise + * using the SOFTWARE, you agree to be bound by the terms of that + * LICENSE. + * Phil Shafer, June 2015 + */ + +/* + * Portions of this file are: + * Copyright (c) 1983, 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "xo_config.h" +#include "xo.h" +#include "xo_encoder.h" /* For xo_realloc */ +#include "xo_buf.h" + +/* + * SYSLOG (RFC 5424) requires an enterprise identifier. This turns + * out to be a fickle little issue. For a single-vendor box, the + * system should have a single EID that all software can use. When + * VendorX turns FreeBSD into a product, all software (kernel and + * utilities) should report VendorX's EID. But when software is + * installed on top of an external operating system, the application + * should report it's own EID, distinct from the base OS. + * + * To make this happen, the kernel should support a sysctl to assign a + * custom enterprise-id ("kern.syslog.enterprise_id"). libxo then + * allows an application to set a custom EID to override that system + * wide value, if needed. + * + * We try to set the stock IANA assigned Enterprise ID value for the + * vendors we know about (FreeBSD, macosx), but fallback to the + * "example" EID defined by IANA. See: + * https://www.iana.org/assignments/enterprise-numbers/enterprise-numbers + */ + +#define XO_SYSLOG_ENTERPRISE_ID "kern.syslog.enterprise_id" + +#if defined(__FreeBSD__) +#define XO_DEFAULT_EID 2238 +#elif defined(__macosx__) +#define XO_DEFAULT_EID 63 +#else +#define XO_DEFAULT_EID 32473 /* Bail; use "example" number */ +#endif + +#ifdef _SC_HOST_NAME_MAX +#define HOST_NAME_MAX _SC_HOST_NAME_MAX +#else +#define HOST_NAME_MAX 255 +#endif /* _SC_HOST_NAME_MAX */ + +#ifndef UNUSED +#define UNUSED __attribute__ ((__unused__)) +#endif /* UNUSED */ + +static int xo_logfile = -1; /* fd for log */ +static int xo_status; /* connection xo_status */ +static int xo_opened; /* have done openlog() */ +static int xo_logstat = 0; /* xo_status bits, set by openlog() */ +static const char *xo_logtag = NULL; /* string to tag the entry with */ +static int xo_logfacility = LOG_USER; /* default facility code */ +static int xo_logmask = 0xff; /* mask of priorities to be logged */ +static pthread_mutex_t xo_syslog_mutex UNUSED = PTHREAD_MUTEX_INITIALIZER; +static int xo_unit_test; /* Fake data for unit test */ + +#define REAL_VOID(_x) \ + do { int really_ignored = _x; if (really_ignored) { }} while (0) + +#if !defined(HAVE_DECL___ISTHREADED) || !HAVE_DECL___ISTHREADED +#define __isthreaded 1 +#endif + +#define THREAD_LOCK() \ + do { \ + if (__isthreaded) pthread_mutex_lock(&xo_syslog_mutex); \ + } while(0) +#define THREAD_UNLOCK() \ + do { \ + if (__isthreaded) pthread_mutex_unlock(&xo_syslog_mutex); \ + } while(0) + +static void xo_disconnect_log(void); /* disconnect from syslogd */ +static void xo_connect_log(void); /* (re)connect to syslogd */ +static void xo_open_log_unlocked(const char *, int, int); + +enum { + NOCONN = 0, + CONNDEF, + CONNPRIV, +}; + +static xo_syslog_open_t xo_syslog_open; +static xo_syslog_send_t xo_syslog_send; +static xo_syslog_close_t xo_syslog_close; + +static char xo_syslog_enterprise_id[12]; + +/* + * Record an enterprise ID, which functions as a namespace for syslog + * messages. The value is pre-formatted into a string. This allows + * applications to customize their syslog message set, when needed. + */ +void +xo_set_syslog_enterprise_id (unsigned short eid) +{ + snprintf(xo_syslog_enterprise_id, sizeof(xo_syslog_enterprise_id), + "%u", eid); +} + +/* + * Handle the work of transmitting the syslog message + */ +static void +xo_send_syslog (char *full_msg, char *v0_hdr, + char *text_only) +{ + if (xo_syslog_send) { + xo_syslog_send(full_msg, v0_hdr, text_only); + return; + } + + int fd; + int full_len = strlen(full_msg); + + /* Output to stderr if requested. */ + if (xo_logstat & LOG_PERROR) { + struct iovec iov[3]; + struct iovec *v = iov; + char newline[] = "\n"; + + v->iov_base = v0_hdr; + v->iov_len = strlen(v0_hdr); + v += 1; + v->iov_base = text_only; + v->iov_len = strlen(text_only); + v += 1; + v->iov_base = newline; + v->iov_len = 1; + v += 1; + REAL_VOID(writev(STDERR_FILENO, iov, 3)); + } + + /* Get connected, output the message to the local logger. */ + if (!xo_opened) + xo_open_log_unlocked(xo_logtag, xo_logstat | LOG_NDELAY, 0); + xo_connect_log(); + + /* + * If the send() fails, there are two likely scenarios: + * 1) syslogd was restarted + * 2) /var/run/log is out of socket buffer space, which + * in most cases means local DoS. + * If the error does not indicate a full buffer, we address + * case #1 by attempting to reconnect to /var/run/log[priv] + * and resending the message once. + * + * If we are working with a privileged socket, the retry + * attempts end there, because we don't want to freeze a + * critical application like su(1) or sshd(8). + * + * Otherwise, we address case #2 by repeatedly retrying the + * send() to give syslogd a chance to empty its socket buffer. + */ + + if (send(xo_logfile, full_msg, full_len, 0) < 0) { + if (errno != ENOBUFS) { + /* + * Scenario 1: syslogd was restarted + * reconnect and resend once + */ + xo_disconnect_log(); + xo_connect_log(); + if (send(xo_logfile, full_msg, full_len, 0) >= 0) { + return; + } + /* + * if the resend failed, fall through to + * possible scenario 2 + */ + } + while (errno == ENOBUFS) { + /* + * Scenario 2: out of socket buffer space + * possible DoS, fail fast on a privileged + * socket + */ + if (xo_status == CONNPRIV) + break; + usleep(1); + if (send(xo_logfile, full_msg, full_len, 0) >= 0) { + return; + } + } + } else { + return; + } + + /* + * Output the message to the console; try not to block + * as a blocking console should not stop other processes. + * Make sure the error reported is the one from the syslogd failure. + */ + int flags = O_WRONLY | O_NONBLOCK; +#ifdef O_CLOEXEC + flags |= O_CLOEXEC; +#endif /* O_CLOEXEC */ + + if (xo_logstat & LOG_CONS + && (fd = open(_PATH_CONSOLE, flags, 0)) >= 0) { + struct iovec iov[2]; + struct iovec *v = iov; + char crnl[] = "\r\n"; + char *p; + + p = strchr(full_msg, '>') + 1; + v->iov_base = p; + v->iov_len = full_len - (p - full_msg); + ++v; + v->iov_base = crnl; + v->iov_len = 2; + REAL_VOID(writev(fd, iov, 2)); + (void) close(fd); + } +} + +/* Should be called with mutex acquired */ +static void +xo_disconnect_log (void) +{ + if (xo_syslog_close) { + xo_syslog_close(); + return; + } + + /* + * If the user closed the FD and opened another in the same slot, + * that's their problem. They should close it before calling on + * system services. + */ + if (xo_logfile != -1) { + close(xo_logfile); + xo_logfile = -1; + } + xo_status = NOCONN; /* retry connect */ +} + +/* Should be called with mutex acquired */ +static void +xo_connect_log (void) +{ + if (xo_syslog_open) { + xo_syslog_open(); + return; + } + + struct sockaddr_un saddr; /* AF_UNIX address of local logger */ + + if (xo_logfile == -1) { + int flags = SOCK_DGRAM; +#ifdef SOCK_CLOEXEC + flags |= SOCK_CLOEXEC; +#endif /* SOCK_CLOEXEC */ + if ((xo_logfile = socket(AF_UNIX, flags, 0)) == -1) + return; + } + if (xo_logfile != -1 && xo_status == NOCONN) { +#ifdef HAVE_SUN_LEN + saddr.sun_len = sizeof(saddr); +#endif /* HAVE_SUN_LEN */ + saddr.sun_family = AF_UNIX; + + /* + * First try privileged socket. If no success, + * then try default socket. + */ + +#ifdef _PATH_LOG_PRIV + (void) strncpy(saddr.sun_path, _PATH_LOG_PRIV, + sizeof saddr.sun_path); + if (connect(xo_logfile, (struct sockaddr *) &saddr, + sizeof(saddr)) != -1) + xo_status = CONNPRIV; +#endif /* _PATH_LOG_PRIV */ + +#ifdef _PATH_LOG + if (xo_status == NOCONN) { + (void) strncpy(saddr.sun_path, _PATH_LOG, + sizeof saddr.sun_path); + if (connect(xo_logfile, (struct sockaddr *)&saddr, + sizeof(saddr)) != -1) + xo_status = CONNDEF; + } +#endif /* _PATH_LOG */ + +#ifdef _PATH_OLDLOG + if (xo_status == NOCONN) { + /* + * Try the old "/dev/log" path, for backward + * compatibility. + */ + (void) strncpy(saddr.sun_path, _PATH_OLDLOG, + sizeof saddr.sun_path); + if (connect(xo_logfile, (struct sockaddr *)&saddr, + sizeof(saddr)) != -1) + xo_status = CONNDEF; + } +#endif /* _PATH_OLDLOG */ + + if (xo_status == NOCONN) { + (void) close(xo_logfile); + xo_logfile = -1; + } + } +} + +static void +xo_open_log_unlocked (const char *ident, int logstat, int logfac) +{ + if (ident != NULL) + xo_logtag = ident; + xo_logstat = logstat; + if (logfac != 0 && (logfac &~ LOG_FACMASK) == 0) + xo_logfacility = logfac; + + if (xo_logstat & LOG_NDELAY) /* open immediately */ + xo_connect_log(); + + xo_opened = 1; /* ident and facility has been set */ +} + +void +xo_open_log (const char *ident, int logstat, int logfac) +{ + THREAD_LOCK(); + xo_open_log_unlocked(ident, logstat, logfac); + THREAD_UNLOCK(); +} + + +void +xo_close_log (void) +{ + THREAD_LOCK(); + if (xo_logfile != -1) { + (void) close(xo_logfile); + xo_logfile = -1; + } + xo_logtag = NULL; + xo_status = NOCONN; + THREAD_UNLOCK(); +} + +/* xo_set_logmask -- set the log mask level */ +int +xo_set_logmask (int pmask) +{ + int omask; + + THREAD_LOCK(); + omask = xo_logmask; + if (pmask != 0) + xo_logmask = pmask; + THREAD_UNLOCK(); + return (omask); +} + +void +xo_set_syslog_handler (xo_syslog_open_t open_func, + xo_syslog_send_t send_func, + xo_syslog_close_t close_func) +{ + xo_syslog_open = open_func; + xo_syslog_send = send_func; + xo_syslog_close = close_func; +} + +static size_t +xo_snprintf (char *out, size_t outsize, const char *fmt, ...) +{ + int status; + size_t retval = 0; + va_list ap; + if (out && outsize) { + va_start(ap, fmt); + status = vsnprintf(out, outsize, fmt, ap); + if (status < 0) { /* this should never happen, */ + *out = 0; /* handle it in the safest way possible if it does */ + retval = 0; + } else { + retval = status; + retval = retval > outsize ? outsize : retval; + } + va_end(ap); + } + return retval; +} + +static int +xo_syslog_handle_write (void *opaque, const char *data) +{ + xo_buffer_t *xbp = opaque; + int len = strlen(data); + int left = xo_buf_left(xbp); + + if (len > left - 1) + len = left - 1; + + memcpy(xbp->xb_curp, data, len); + xbp->xb_curp += len; + *xbp->xb_curp = '\0'; + + return len; +} + +static void +xo_syslog_handle_close (void *opaque UNUSED) +{ +} + +static int +xo_syslog_handle_flush (void *opaque UNUSED) +{ + return 0; +} + +void +xo_set_unit_test_mode (int value) +{ + xo_unit_test = value; +} + +void +xo_vsyslog (int pri, const char *name, const char *fmt, va_list vap) +{ + int saved_errno = errno; + char tbuf[2048]; + char *tp = NULL, *ep = NULL; + unsigned start_of_msg = 0; + char *v0_hdr = NULL; + xo_buffer_t xb; + static pid_t my_pid; + unsigned log_offset; + + if (my_pid == 0) + my_pid = xo_unit_test ? 222 : getpid(); + + /* Check for invalid bits */ + if (pri & ~(LOG_PRIMASK|LOG_FACMASK)) { + xo_syslog(LOG_ERR | LOG_CONS | LOG_PERROR | LOG_PID, + "syslog-unknown-priority", + "syslog: unknown facility/priority: %#x", pri); + pri &= LOG_PRIMASK|LOG_FACMASK; + } + + THREAD_LOCK(); + + /* Check priority against setlogmask values. */ + if (!(LOG_MASK(LOG_PRI(pri)) & xo_logmask)) { + THREAD_UNLOCK(); + return; + } + + /* Set default facility if none specified. */ + if ((pri & LOG_FACMASK) == 0) + pri |= xo_logfacility; + + /* Create the primary stdio hook */ + xb.xb_bufp = tbuf; + xb.xb_curp = tbuf; + xb.xb_size = sizeof(tbuf); + + xo_handle_t *xop = xo_create(XO_STYLE_SDPARAMS, 0); + if (xop == NULL) { + THREAD_UNLOCK(); + return; + } + +#ifdef HAVE_GETPROGNAME + if (xo_logtag == NULL) + xo_logtag = getprogname(); +#endif /* HAVE_GETPROGNAME */ + + xo_set_writer(xop, &xb, xo_syslog_handle_write, xo_syslog_handle_close, + xo_syslog_handle_flush); + + /* Build the message; start by getting the time */ + struct tm tm; + struct timeval tv; + + /* Unit test hack: fake a fixed time */ + if (xo_unit_test) { + tv.tv_sec = 1435085229; + tv.tv_usec = 123456; + } else + gettimeofday(&tv, NULL); + + (void) localtime_r(&tv.tv_sec, &tm); + + if (xo_logstat & LOG_PERROR) { + /* + * For backwards compatibility, we need to make the old-style + * message. This message can be emitted to the console/tty. + */ + v0_hdr = alloca(2048); + tp = v0_hdr; + ep = v0_hdr + 2048; + + if (xo_logtag != NULL) + tp += xo_snprintf(tp, ep - tp, "%s", xo_logtag); + if (xo_logstat & LOG_PID) + tp += xo_snprintf(tp, ep - tp, "[%d]", my_pid); + if (xo_logtag) + tp += xo_snprintf(tp, ep - tp, ": "); + } + + log_offset = xb.xb_curp - xb.xb_bufp; + + /* Add PRI, PRIVAL, and VERSION */ + xb.xb_curp += xo_snprintf(xb.xb_curp, xo_buf_left(&xb), "<%d>1 ", pri); + + /* Add TIMESTAMP with milliseconds and TZOFFSET */ + xb.xb_curp += strftime(xb.xb_curp, xo_buf_left(&xb), "%FT%T", &tm); + xb.xb_curp += xo_snprintf(xb.xb_curp, xo_buf_left(&xb), + ".%03.3u", tv.tv_usec / 1000); + xb.xb_curp += strftime(xb.xb_curp, xo_buf_left(&xb), "%z ", &tm); + + /* + * Add HOSTNAME; we rely on gethostname and don't fluff with + * ip addresses. Might need to revisit..... + */ + char hostname[HOST_NAME_MAX]; + hostname[0] = '\0'; + if (xo_unit_test) + strcpy(hostname, "worker-host"); + else + (void) gethostname(hostname, sizeof(hostname)); + + xb.xb_curp += xo_snprintf(xb.xb_curp, xo_buf_left(&xb), "%s ", + hostname[0] ? hostname : "-"); + + /* Add APP-NAME */ + xb.xb_curp += xo_snprintf(xb.xb_curp, xo_buf_left(&xb), "%s ", + xo_logtag ?: "-"); + + /* Add PROCID */ + xb.xb_curp += xo_snprintf(xb.xb_curp, xo_buf_left(&xb), "%d ", my_pid); + + /* + * Add MSGID. The user should provide us with a name, which we + * prefix with the current enterprise ID, as learned from the kernel. + * If the kernel won't tell us, we use the stock/builtin number. + */ + char *buf UNUSED = NULL; + const char *eid = xo_syslog_enterprise_id; + const char *at_sign = "@"; + + if (name == NULL) { + name = "-"; + eid = at_sign = ""; + + } else if (*name == '@') { + /* Our convention is to prefix IANA-defined names with an "@" */ + name += 1; + eid = at_sign = ""; + + } else if (eid[0] == '\0') { +#ifdef HAVE_SYSCTLBYNAME + /* + * See if the kernel knows the sysctl for the enterprise ID + */ + size_t size = 0; + if (sysctlbyname(XO_SYSLOG_ENTERPRISE_ID, NULL, &size, NULL, 0) == 0 + && size > 0) { + buf = alloca(size); + if (sysctlbyname(XO_SYSLOG_ENTERPRISE_ID, buf, &size, NULL, 0) == 0 + && size > 0) + eid = buf; + } +#endif /* HAVE_SYSCTLBYNAME */ + + if (eid[0] == '\0') { + /* Fallback to our base default */ + xo_set_syslog_enterprise_id(XO_DEFAULT_EID); + eid = xo_syslog_enterprise_id; + } + } + + xb.xb_curp += xo_snprintf(xb.xb_curp, xo_buf_left(&xb), "%s [%s%s%s ", + name, name, at_sign, eid); + + /* + * Now for the real content. We make two distinct passes thru the + * xo_emit engine, first for the SD-PARAMS and then for the text + * message. + */ + va_list ap; + va_copy(ap, vap); + + errno = saved_errno; /* Restore saved error value */ + xo_emit_hv(xop, fmt, ap); + xo_flush_h(xop); + + va_end(ap); + + /* Trim trailing space */ + if (xb.xb_curp[-1] == ' ') + xb.xb_curp -= 1; + + /* Close the structured data (SD-ELEMENT) */ + xb.xb_curp += xo_snprintf(xb.xb_curp, xo_buf_left(&xb), "] "); + + /* + * Since our MSG is known to be UTF-8, we MUST prefix it with + * that most-annoying-of-all-UTF-8 features, the BOM (0xEF.BB.BF). + */ + xb.xb_curp += xo_snprintf(xb.xb_curp, xo_buf_left(&xb), + "%c%c%c", 0xEF, 0xBB, 0xBF); + + /* Save the start of the message */ + if (xo_logstat & LOG_PERROR) + start_of_msg = xb.xb_curp - xb.xb_bufp; + + xo_set_style(xop, XO_STYLE_TEXT); + xo_set_flags(xop, XOF_UTF8); + + errno = saved_errno; /* Restore saved error value */ + xo_emit_hv(xop, fmt, ap); + xo_flush_h(xop); + + /* Remove a trailing newline */ + if (xb.xb_curp[-1] == '\n') + *--xb.xb_curp = '\0'; + + if (xo_get_flags(xop) & XOF_LOG_SYSLOG) + fprintf(stderr, "xo: syslog: %s\n", xb.xb_bufp + log_offset); + + xo_send_syslog(xb.xb_bufp, v0_hdr, xb.xb_bufp + start_of_msg); + + xo_destroy(xop); + + THREAD_UNLOCK(); +} + +/* + * syslog - print message on log file; output is intended for syslogd(8). + */ +void +xo_syslog (int pri, const char *name, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + xo_vsyslog(pri, name, fmt, ap); + va_end(ap); +} diff --git a/contrib/libxo/libxo/xo_wcwidth.h b/contrib/libxo/libxo/xo_wcwidth.h new file mode 100644 index 000000000000..46d83f0365d2 --- /dev/null +++ b/contrib/libxo/libxo/xo_wcwidth.h @@ -0,0 +1,313 @@ +/* + * This is an implementation of wcwidth() and wcswidth() (defined in + * IEEE Std 1002.1-2001) for Unicode. + * + * http://www.opengroup.org/onlinepubs/007904975/functions/wcwidth.html + * http://www.opengroup.org/onlinepubs/007904975/functions/wcswidth.html + * + * In fixed-width output devices, Latin characters all occupy a single + * "cell" position of equal width, whereas ideographic CJK characters + * occupy two such cells. Interoperability between terminal-line + * applications and (teletype-style) character terminals using the + * UTF-8 encoding requires agreement on which character should advance + * the cursor by how many cell positions. No established formal + * standards exist at present on which Unicode character shall occupy + * how many cell positions on character terminals. These routines are + * a first attempt of defining such behavior based on simple rules + * applied to data provided by the Unicode Consortium. + * + * For some graphical characters, the Unicode standard explicitly + * defines a character-cell width via the definition of the East Asian + * FullWidth (F), Wide (W), Half-width (H), and Narrow (Na) classes. + * In all these cases, there is no ambiguity about which width a + * terminal shall use. For characters in the East Asian Ambiguous (A) + * class, the width choice depends purely on a preference of backward + * compatibility with either historic CJK or Western practice. + * Choosing single-width for these characters is easy to justify as + * the appropriate long-term solution, as the CJK practice of + * displaying these characters as double-width comes from historic + * implementation simplicity (8-bit encoded characters were displayed + * single-width and 16-bit ones double-width, even for Greek, + * Cyrillic, etc.) and not any typographic considerations. + * + * Much less clear is the choice of width for the Not East Asian + * (Neutral) class. Existing practice does not dictate a width for any + * of these characters. It would nevertheless make sense + * typographically to allocate two character cells to characters such + * as for instance EM SPACE or VOLUME INTEGRAL, which cannot be + * represented adequately with a single-width glyph. The following + * routines at present merely assign a single-cell width to all + * neutral characters, in the interest of simplicity. This is not + * entirely satisfactory and should be reconsidered before + * establishing a formal standard in this area. At the moment, the + * decision which Not East Asian (Neutral) characters should be + * represented by double-width glyphs cannot yet be answered by + * applying a simple rule from the Unicode database content. Setting + * up a proper standard for the behavior of UTF-8 character terminals + * will require a careful analysis not only of each Unicode character, + * but also of each presentation form, something the author of these + * routines has avoided to do so far. + * + * http://www.unicode.org/unicode/reports/tr11/ + * + * Markus Kuhn -- 2007-05-26 (Unicode 5.0) + * + * Permission to use, copy, modify, and distribute this software + * for any purpose and without fee is hereby granted. The author + * disclaims all warranties with regard to this software. + * + * Latest version: http://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c + */ + +#include + +struct interval { + wchar_t first; + wchar_t last; +}; + +/* auxiliary function for binary search in interval table */ +static int +xo_bisearch (wchar_t ucs, const struct interval *table, int max) +{ + int min = 0; + int mid; + + if (ucs < table[0].first || ucs > table[max].last) + return 0; + while (max >= min) { + mid = (min + max) / 2; + if (ucs > table[mid].last) + min = mid + 1; + else if (ucs < table[mid].first) + max = mid - 1; + else + return 1; + } + + return 0; +} + + +/* The following two functions define the column width of an ISO 10646 + * character as follows: + * + * - The null character (U+0000) has a column width of 0. + * + * - Other C0/C1 control characters and DEL will lead to a return + * value of -1. + * + * - Non-spacing and enclosing combining characters (general + * category code Mn or Me in the Unicode database) have a + * column width of 0. + * + * - SOFT HYPHEN (U+00AD) has a column width of 1. + * + * - Other format characters (general category code Cf in the Unicode + * database) and ZERO WIDTH SPACE (U+200B) have a column width of 0. + * + * - Hangul Jamo medial vowels and final consonants (U+1160-U+11FF) + * have a column width of 0. + * + * - Spacing characters in the East Asian Wide (W) or East Asian + * Full-width (F) category as defined in Unicode Technical + * Report #11 have a column width of 2. + * + * - All remaining characters (including all printable + * ISO 8859-1 and WGL4 characters, Unicode control characters, + * etc.) have a column width of 1. + * + * This implementation assumes that wchar_t characters are encoded + * in ISO 10646. + */ + +static int +xo_wcwidth (wchar_t ucs) +{ + /* sorted list of non-overlapping intervals of non-spacing characters */ + /* generated by "uniset +cat=Me +cat=Mn +cat=Cf -00AD +1160-11FF +200B c" */ + static const struct interval combining[] = { + { 0x0300, 0x036F }, { 0x0483, 0x0486 }, { 0x0488, 0x0489 }, + { 0x0591, 0x05BD }, { 0x05BF, 0x05BF }, { 0x05C1, 0x05C2 }, + { 0x05C4, 0x05C5 }, { 0x05C7, 0x05C7 }, { 0x0600, 0x0603 }, + { 0x0610, 0x0615 }, { 0x064B, 0x065E }, { 0x0670, 0x0670 }, + { 0x06D6, 0x06E4 }, { 0x06E7, 0x06E8 }, { 0x06EA, 0x06ED }, + { 0x070F, 0x070F }, { 0x0711, 0x0711 }, { 0x0730, 0x074A }, + { 0x07A6, 0x07B0 }, { 0x07EB, 0x07F3 }, { 0x0901, 0x0902 }, + { 0x093C, 0x093C }, { 0x0941, 0x0948 }, { 0x094D, 0x094D }, + { 0x0951, 0x0954 }, { 0x0962, 0x0963 }, { 0x0981, 0x0981 }, + { 0x09BC, 0x09BC }, { 0x09C1, 0x09C4 }, { 0x09CD, 0x09CD }, + { 0x09E2, 0x09E3 }, { 0x0A01, 0x0A02 }, { 0x0A3C, 0x0A3C }, + { 0x0A41, 0x0A42 }, { 0x0A47, 0x0A48 }, { 0x0A4B, 0x0A4D }, + { 0x0A70, 0x0A71 }, { 0x0A81, 0x0A82 }, { 0x0ABC, 0x0ABC }, + { 0x0AC1, 0x0AC5 }, { 0x0AC7, 0x0AC8 }, { 0x0ACD, 0x0ACD }, + { 0x0AE2, 0x0AE3 }, { 0x0B01, 0x0B01 }, { 0x0B3C, 0x0B3C }, + { 0x0B3F, 0x0B3F }, { 0x0B41, 0x0B43 }, { 0x0B4D, 0x0B4D }, + { 0x0B56, 0x0B56 }, { 0x0B82, 0x0B82 }, { 0x0BC0, 0x0BC0 }, + { 0x0BCD, 0x0BCD }, { 0x0C3E, 0x0C40 }, { 0x0C46, 0x0C48 }, + { 0x0C4A, 0x0C4D }, { 0x0C55, 0x0C56 }, { 0x0CBC, 0x0CBC }, + { 0x0CBF, 0x0CBF }, { 0x0CC6, 0x0CC6 }, { 0x0CCC, 0x0CCD }, + { 0x0CE2, 0x0CE3 }, { 0x0D41, 0x0D43 }, { 0x0D4D, 0x0D4D }, + { 0x0DCA, 0x0DCA }, { 0x0DD2, 0x0DD4 }, { 0x0DD6, 0x0DD6 }, + { 0x0E31, 0x0E31 }, { 0x0E34, 0x0E3A }, { 0x0E47, 0x0E4E }, + { 0x0EB1, 0x0EB1 }, { 0x0EB4, 0x0EB9 }, { 0x0EBB, 0x0EBC }, + { 0x0EC8, 0x0ECD }, { 0x0F18, 0x0F19 }, { 0x0F35, 0x0F35 }, + { 0x0F37, 0x0F37 }, { 0x0F39, 0x0F39 }, { 0x0F71, 0x0F7E }, + { 0x0F80, 0x0F84 }, { 0x0F86, 0x0F87 }, { 0x0F90, 0x0F97 }, + { 0x0F99, 0x0FBC }, { 0x0FC6, 0x0FC6 }, { 0x102D, 0x1030 }, + { 0x1032, 0x1032 }, { 0x1036, 0x1037 }, { 0x1039, 0x1039 }, + { 0x1058, 0x1059 }, { 0x1160, 0x11FF }, { 0x135F, 0x135F }, + { 0x1712, 0x1714 }, { 0x1732, 0x1734 }, { 0x1752, 0x1753 }, + { 0x1772, 0x1773 }, { 0x17B4, 0x17B5 }, { 0x17B7, 0x17BD }, + { 0x17C6, 0x17C6 }, { 0x17C9, 0x17D3 }, { 0x17DD, 0x17DD }, + { 0x180B, 0x180D }, { 0x18A9, 0x18A9 }, { 0x1920, 0x1922 }, + { 0x1927, 0x1928 }, { 0x1932, 0x1932 }, { 0x1939, 0x193B }, + { 0x1A17, 0x1A18 }, { 0x1B00, 0x1B03 }, { 0x1B34, 0x1B34 }, + { 0x1B36, 0x1B3A }, { 0x1B3C, 0x1B3C }, { 0x1B42, 0x1B42 }, + { 0x1B6B, 0x1B73 }, { 0x1DC0, 0x1DCA }, { 0x1DFE, 0x1DFF }, + { 0x200B, 0x200F }, { 0x202A, 0x202E }, { 0x2060, 0x2063 }, + { 0x206A, 0x206F }, { 0x20D0, 0x20EF }, { 0x302A, 0x302F }, + { 0x3099, 0x309A }, { 0xA806, 0xA806 }, { 0xA80B, 0xA80B }, + { 0xA825, 0xA826 }, { 0xFB1E, 0xFB1E }, { 0xFE00, 0xFE0F }, + { 0xFE20, 0xFE23 }, { 0xFEFF, 0xFEFF }, { 0xFFF9, 0xFFFB }, + { 0x10A01, 0x10A03 }, { 0x10A05, 0x10A06 }, { 0x10A0C, 0x10A0F }, + { 0x10A38, 0x10A3A }, { 0x10A3F, 0x10A3F }, { 0x1D167, 0x1D169 }, + { 0x1D173, 0x1D182 }, { 0x1D185, 0x1D18B }, { 0x1D1AA, 0x1D1AD }, + { 0x1D242, 0x1D244 }, { 0xE0001, 0xE0001 }, { 0xE0020, 0xE007F }, + { 0xE0100, 0xE01EF } + }; + + /* test for 8-bit control characters */ + if (ucs == 0) + return 0; + if (ucs < 32 || (ucs >= 0x7f && ucs < 0xa0)) + return -1; + + /* binary search in table of non-spacing characters */ + if (xo_bisearch(ucs, combining, + sizeof(combining) / sizeof(struct interval) - 1)) + return 0; + + /* if we arrive here, ucs is not a combining or C0/C1 control character */ + + return 1 + + (ucs >= 0x1100 && + (ucs <= 0x115f || /* Hangul Jamo init. consonants */ + ucs == 0x2329 || ucs == 0x232a || + (ucs >= 0x2e80 && ucs <= 0xa4cf && + ucs != 0x303f) || /* CJK ... Yi */ + (ucs >= 0xac00 && ucs <= 0xd7a3) || /* Hangul Syllables */ + (ucs >= 0xf900 && ucs <= 0xfaff) || /* CJK Compatibility Ideographs */ + (ucs >= 0xfe10 && ucs <= 0xfe19) || /* Vertical forms */ + (ucs >= 0xfe30 && ucs <= 0xfe6f) || /* CJK Compatibility Forms */ + (ucs >= 0xff00 && ucs <= 0xff60) || /* Fullwidth Forms */ + (ucs >= 0xffe0 && ucs <= 0xffe6) || + (ucs >= 0x20000 && ucs <= 0x2fffd) || + (ucs >= 0x30000 && ucs <= 0x3fffd))); +} + +#if UNUSED_CODE +static int xo_wcswidth(const wchar_t *pwcs, size_t n) +{ + int w, width = 0; + + for (;*pwcs && n-- > 0; pwcs++) + if ((w = mk_wcwidth(*pwcs)) < 0) + return -1; + else + width += w; + + return width; +} + + +/* + * The following functions are the same as mk_wcwidth() and + * mk_wcswidth(), except that spacing characters in the East Asian + * Ambiguous (A) category as defined in Unicode Technical Report #11 + * have a column width of 2. This variant might be useful for users of + * CJK legacy encodings who want to migrate to UCS without changing + * the traditional terminal character-width behaviour. It is not + * otherwise recommended for general use. + */ +int mk_wcwidth_cjk(wchar_t ucs) +{ + /* sorted list of non-overlapping intervals of East Asian Ambiguous + * characters, generated by "uniset +WIDTH-A -cat=Me -cat=Mn -cat=Cf c" */ + static const struct interval ambiguous[] = { + { 0x00A1, 0x00A1 }, { 0x00A4, 0x00A4 }, { 0x00A7, 0x00A8 }, + { 0x00AA, 0x00AA }, { 0x00AE, 0x00AE }, { 0x00B0, 0x00B4 }, + { 0x00B6, 0x00BA }, { 0x00BC, 0x00BF }, { 0x00C6, 0x00C6 }, + { 0x00D0, 0x00D0 }, { 0x00D7, 0x00D8 }, { 0x00DE, 0x00E1 }, + { 0x00E6, 0x00E6 }, { 0x00E8, 0x00EA }, { 0x00EC, 0x00ED }, + { 0x00F0, 0x00F0 }, { 0x00F2, 0x00F3 }, { 0x00F7, 0x00FA }, + { 0x00FC, 0x00FC }, { 0x00FE, 0x00FE }, { 0x0101, 0x0101 }, + { 0x0111, 0x0111 }, { 0x0113, 0x0113 }, { 0x011B, 0x011B }, + { 0x0126, 0x0127 }, { 0x012B, 0x012B }, { 0x0131, 0x0133 }, + { 0x0138, 0x0138 }, { 0x013F, 0x0142 }, { 0x0144, 0x0144 }, + { 0x0148, 0x014B }, { 0x014D, 0x014D }, { 0x0152, 0x0153 }, + { 0x0166, 0x0167 }, { 0x016B, 0x016B }, { 0x01CE, 0x01CE }, + { 0x01D0, 0x01D0 }, { 0x01D2, 0x01D2 }, { 0x01D4, 0x01D4 }, + { 0x01D6, 0x01D6 }, { 0x01D8, 0x01D8 }, { 0x01DA, 0x01DA }, + { 0x01DC, 0x01DC }, { 0x0251, 0x0251 }, { 0x0261, 0x0261 }, + { 0x02C4, 0x02C4 }, { 0x02C7, 0x02C7 }, { 0x02C9, 0x02CB }, + { 0x02CD, 0x02CD }, { 0x02D0, 0x02D0 }, { 0x02D8, 0x02DB }, + { 0x02DD, 0x02DD }, { 0x02DF, 0x02DF }, { 0x0391, 0x03A1 }, + { 0x03A3, 0x03A9 }, { 0x03B1, 0x03C1 }, { 0x03C3, 0x03C9 }, + { 0x0401, 0x0401 }, { 0x0410, 0x044F }, { 0x0451, 0x0451 }, + { 0x2010, 0x2010 }, { 0x2013, 0x2016 }, { 0x2018, 0x2019 }, + { 0x201C, 0x201D }, { 0x2020, 0x2022 }, { 0x2024, 0x2027 }, + { 0x2030, 0x2030 }, { 0x2032, 0x2033 }, { 0x2035, 0x2035 }, + { 0x203B, 0x203B }, { 0x203E, 0x203E }, { 0x2074, 0x2074 }, + { 0x207F, 0x207F }, { 0x2081, 0x2084 }, { 0x20AC, 0x20AC }, + { 0x2103, 0x2103 }, { 0x2105, 0x2105 }, { 0x2109, 0x2109 }, + { 0x2113, 0x2113 }, { 0x2116, 0x2116 }, { 0x2121, 0x2122 }, + { 0x2126, 0x2126 }, { 0x212B, 0x212B }, { 0x2153, 0x2154 }, + { 0x215B, 0x215E }, { 0x2160, 0x216B }, { 0x2170, 0x2179 }, + { 0x2190, 0x2199 }, { 0x21B8, 0x21B9 }, { 0x21D2, 0x21D2 }, + { 0x21D4, 0x21D4 }, { 0x21E7, 0x21E7 }, { 0x2200, 0x2200 }, + { 0x2202, 0x2203 }, { 0x2207, 0x2208 }, { 0x220B, 0x220B }, + { 0x220F, 0x220F }, { 0x2211, 0x2211 }, { 0x2215, 0x2215 }, + { 0x221A, 0x221A }, { 0x221D, 0x2220 }, { 0x2223, 0x2223 }, + { 0x2225, 0x2225 }, { 0x2227, 0x222C }, { 0x222E, 0x222E }, + { 0x2234, 0x2237 }, { 0x223C, 0x223D }, { 0x2248, 0x2248 }, + { 0x224C, 0x224C }, { 0x2252, 0x2252 }, { 0x2260, 0x2261 }, + { 0x2264, 0x2267 }, { 0x226A, 0x226B }, { 0x226E, 0x226F }, + { 0x2282, 0x2283 }, { 0x2286, 0x2287 }, { 0x2295, 0x2295 }, + { 0x2299, 0x2299 }, { 0x22A5, 0x22A5 }, { 0x22BF, 0x22BF }, + { 0x2312, 0x2312 }, { 0x2460, 0x24E9 }, { 0x24EB, 0x254B }, + { 0x2550, 0x2573 }, { 0x2580, 0x258F }, { 0x2592, 0x2595 }, + { 0x25A0, 0x25A1 }, { 0x25A3, 0x25A9 }, { 0x25B2, 0x25B3 }, + { 0x25B6, 0x25B7 }, { 0x25BC, 0x25BD }, { 0x25C0, 0x25C1 }, + { 0x25C6, 0x25C8 }, { 0x25CB, 0x25CB }, { 0x25CE, 0x25D1 }, + { 0x25E2, 0x25E5 }, { 0x25EF, 0x25EF }, { 0x2605, 0x2606 }, + { 0x2609, 0x2609 }, { 0x260E, 0x260F }, { 0x2614, 0x2615 }, + { 0x261C, 0x261C }, { 0x261E, 0x261E }, { 0x2640, 0x2640 }, + { 0x2642, 0x2642 }, { 0x2660, 0x2661 }, { 0x2663, 0x2665 }, + { 0x2667, 0x266A }, { 0x266C, 0x266D }, { 0x266F, 0x266F }, + { 0x273D, 0x273D }, { 0x2776, 0x277F }, { 0xE000, 0xF8FF }, + { 0xFFFD, 0xFFFD }, { 0xF0000, 0xFFFFD }, { 0x100000, 0x10FFFD } + }; + + /* binary search in table of non-spacing characters */ + if (xo_bisearch(ucs, ambiguous, + sizeof(ambiguous) / sizeof(struct interval) - 1)) + return 2; + + return mk_wcwidth(ucs); +} + + +int mk_wcswidth_cjk(const wchar_t *pwcs, size_t n) +{ + int w, width = 0; + + for (;*pwcs && n-- > 0; pwcs++) + if ((w = mk_wcwidth_cjk(*pwcs)) < 0) + return -1; + else + width += w; + + return width; +} +#endif /* UNUSED_CODE */ diff --git a/contrib/libxo/libxo/xoconfig.h.in b/contrib/libxo/libxo/xoconfig.h.in deleted file mode 100644 index ad992f35527a..000000000000 --- a/contrib/libxo/libxo/xoconfig.h.in +++ /dev/null @@ -1,206 +0,0 @@ -/* libxo/xoconfig.h.in. Generated from configure.ac by autoheader. */ - -/* Define to one of `_getb67', `GETB67', `getb67' for Cray-2 and Cray-YMP - systems. This function is required for `alloca.c' support on those systems. - */ -#undef CRAY_STACKSEG_END - -/* Define to 1 if using `alloca.c'. */ -#undef C_ALLOCA - -/* Define to 1 if you have `alloca', as a function or macro. */ -#undef HAVE_ALLOCA - -/* Define to 1 if you have and it should be used (not on Ultrix). - */ -#undef HAVE_ALLOCA_H - -/* Define to 1 if you have the `asprintf' function. */ -#undef HAVE_ASPRINTF - -/* Define to 1 if you have the `bzero' function. */ -#undef HAVE_BZERO - -/* Define to 1 if you have the `ctime' function. */ -#undef HAVE_CTIME - -/* Define to 1 if you have the header file. */ -#undef HAVE_CTYPE_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_DLFCN_H - -/* Define to 1 if you have the `dlfunc' function. */ -#undef HAVE_DLFUNC - -/* Define to 1 if you have the header file. */ -#undef HAVE_ERRNO_H - -/* Define to 1 if you have the `fdopen' function. */ -#undef HAVE_FDOPEN - -/* Define to 1 if you have the `flock' function. */ -#undef HAVE_FLOCK - -/* Define to 1 if you have the `getpass' function. */ -#undef HAVE_GETPASS - -/* Define to 1 if you have the `getrusage' function. */ -#undef HAVE_GETRUSAGE - -/* Define to 1 if you have the `gettimeofday' function. */ -#undef HAVE_GETTIMEOFDAY - -/* Define to 1 if you have the header file. */ -#undef HAVE_INTTYPES_H - -/* Define to 1 if you have the `crypto' library (-lcrypto). */ -#undef HAVE_LIBCRYPTO - -/* Define to 1 if you have the `m' library (-lm). */ -#undef HAVE_LIBM - -/* Define to 1 if your system has a GNU libc compatible `malloc' function, and - to 0 otherwise. */ -#undef HAVE_MALLOC - -/* Define to 1 if you have the `memmove' function. */ -#undef HAVE_MEMMOVE - -/* Define to 1 if you have the header file. */ -#undef HAVE_MEMORY_H - -/* Support printflike */ -#undef HAVE_PRINTFLIKE - -/* Define to 1 if your system has a GNU libc compatible `realloc' function, - and to 0 otherwise. */ -#undef HAVE_REALLOC - -/* Define to 1 if you have the `srand' function. */ -#undef HAVE_SRAND - -/* Define to 1 if you have the `sranddev' function. */ -#undef HAVE_SRANDDEV - -/* Define to 1 if you have the header file. */ -#undef HAVE_STDINT_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_STDIO_EXT_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_STDIO_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_STDLIB_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_STDTIME_TZFILE_H - -/* Define to 1 if you have the `strchr' function. */ -#undef HAVE_STRCHR - -/* Define to 1 if you have the `strcspn' function. */ -#undef HAVE_STRCSPN - -/* Define to 1 if you have the `strerror' function. */ -#undef HAVE_STRERROR - -/* Define to 1 if you have the header file. */ -#undef HAVE_STRINGS_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_STRING_H - -/* Define to 1 if you have the `strlcpy' function. */ -#undef HAVE_STRLCPY - -/* Define to 1 if you have the `strspn' function. */ -#undef HAVE_STRSPN - -/* Define to 1 if you have the `sysctlbyname' function. */ -#undef HAVE_SYSCTLBYNAME - -/* Define to 1 if you have the header file. */ -#undef HAVE_SYS_PARAM_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_SYS_STAT_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_SYS_SYSCTL_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_SYS_TIME_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_SYS_TYPES_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_TZFILE_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_UNISTD_H - -/* Define to 1 if you have the `__flbf' function. */ -#undef HAVE___FLBF - -/* Enable debugging */ -#undef LIBXO_DEBUG - -/* Enable text-only rendering */ -#undef LIBXO_TEXT_ONLY - -/* Define to the sub-directory where libtool stores uninstalled libraries. */ -#undef LT_OBJDIR - -/* Name of package */ -#undef PACKAGE - -/* Define to the address where bug reports for this package should be sent. */ -#undef PACKAGE_BUGREPORT - -/* Define to the full name of this package. */ -#undef PACKAGE_NAME - -/* Define to the full name and version of this package. */ -#undef PACKAGE_STRING - -/* Define to the one symbol short name of this package. */ -#undef PACKAGE_TARNAME - -/* Define to the home page for this package. */ -#undef PACKAGE_URL - -/* Define to the version of this package. */ -#undef PACKAGE_VERSION - -/* If using the C implementation of alloca, define if you know the - direction of stack growth for your system; otherwise it will be - automatically deduced at runtime. - STACK_DIRECTION > 0 => grows toward higher addresses - STACK_DIRECTION < 0 => grows toward lower addresses - STACK_DIRECTION = 0 => direction of growth unknown */ -#undef STACK_DIRECTION - -/* Define to 1 if you have the ANSI C header files. */ -#undef STDC_HEADERS - -/* Version number of package */ -#undef VERSION - -/* Define to `__inline__' or `__inline' if that's what the C compiler - calls it, or to nothing if 'inline' is not supported under any name. */ -#ifndef __cplusplus -#undef inline -#endif - -/* Define to rpl_malloc if the replacement function should be used. */ -#undef malloc - -/* Define to rpl_realloc if the replacement function should be used. */ -#undef realloc - -/* Define to `unsigned int' if does not define. */ -#undef size_t diff --git a/contrib/libxo/libxo/xoversion.h b/contrib/libxo/libxo/xoversion.h deleted file mode 100644 index 6a60596594a5..000000000000 --- a/contrib/libxo/libxo/xoversion.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * $Id$ - * - * Copyright (c) 2014, Juniper Networks, Inc. - * All rights reserved. - * This SOFTWARE is licensed under the LICENSE provided in the - * ../Copyright file. By downloading, installing, copying, or otherwise - * using the SOFTWARE, you agree to be bound by the terms of that - * LICENSE. - * - * xoversion.h -- compile time constants for libxo - * NOTE: This file is generated from xoversion.h.in. - */ - -#ifndef LIBXO_XOVERSION_H -#define LIBXO_XOVERSION_H - -/** - * The version string - */ -#define LIBXO_VERSION "0.3.2" - -/** - * The version number - */ -#define LIBXO_VERSION_NUMBER 3002 - -/** - * The version number as a string - */ -#define LIBXO_VERSION_STRING "3002" - -/** - * The version number extra info as a string - */ -#define LIBXO_VERSION_EXTRA "" - -#endif /* LIBXO_XOVERSION_H */ diff --git a/contrib/libxo/libxo/xoversion.h.in b/contrib/libxo/libxo/xoversion.h.in deleted file mode 100644 index 777e83e3f05e..000000000000 --- a/contrib/libxo/libxo/xoversion.h.in +++ /dev/null @@ -1,38 +0,0 @@ -/* - * $Id$ - * - * Copyright (c) 2014, Juniper Networks, Inc. - * All rights reserved. - * This SOFTWARE is licensed under the LICENSE provided in the - * ../Copyright file. By downloading, installing, copying, or otherwise - * using the SOFTWARE, you agree to be bound by the terms of that - * LICENSE. - * - * xoversion.h -- compile time constants for libxo - * NOTE: This file is generated from xoversion.h.in. - */ - -#ifndef LIBXO_XOVERSION_H -#define LIBXO_XOVERSION_H - -/** - * The version string - */ -#define LIBXO_VERSION "@PACKAGE_VERSION@" - -/** - * The version number - */ -#define LIBXO_VERSION_NUMBER @LIBXO_VERSION_NUMBER@ - -/** - * The version number as a string - */ -#define LIBXO_VERSION_STRING "@LIBXO_VERSION_NUMBER@" - -/** - * The version number extra info as a string - */ -#define LIBXO_VERSION_EXTRA "@LIBXO_VERSION_EXTRA@" - -#endif /* LIBXO_XOVERSION_H */ diff --git a/contrib/libxo/tests/Makefile.am b/contrib/libxo/tests/Makefile.am index c69d51162eb4..b6d3e7199fc8 100644 --- a/contrib/libxo/tests/Makefile.am +++ b/contrib/libxo/tests/Makefile.am @@ -6,7 +6,11 @@ # using the SOFTWARE, you agree to be bound by the terms of that # LICENSE. -SUBDIRS=core xo +SUBDIRS = core xo + +if HAVE_GETTEXT +SUBDIRS += gettext +endif test tests: @(cur=`pwd` ; for dir in $(SUBDIRS) ; do \ diff --git a/contrib/libxo/tests/core/Makefile.am b/contrib/libxo/tests/core/Makefile.am index 92f5f364b74b..0131a6f50ac5 100644 --- a/contrib/libxo/tests/core/Makefile.am +++ b/contrib/libxo/tests/core/Makefile.am @@ -21,7 +21,8 @@ test_06.c \ test_07.c \ test_08.c \ test_09.c \ -test_10.c +test_10.c \ +test_11.c test_01_test_SOURCES = test_01.c test_02_test_SOURCES = test_02.c @@ -33,6 +34,7 @@ test_07_test_SOURCES = test_07.c test_08_test_SOURCES = test_08.c test_09_test_SOURCES = test_09.c test_10_test_SOURCES = test_10.c +test_11_test_SOURCES = test_11.c # TEST_CASES := $(shell cd ${srcdir} ; echo *.c ) @@ -41,6 +43,10 @@ noinst_PROGRAMS = ${TEST_CASES:.c=.test} LDADD = \ ${top_builddir}/libxo/libxo.la +if HAVE_HUMANIZE_NUMBER +LDADD += -lutil +endif + EXTRA_DIST = \ ${TEST_CASES} \ ${addprefix saved/, ${TEST_CASES:.c=.T.err}} \ @@ -70,13 +76,18 @@ valgrind: #TEST_TRACE = set -x ; -TEST_ONE = \ - LIBXO_OPTIONS=:W$$fmt \ +TEST_JIG = \ ${CHECKER} ./$$base.test ${TEST_OPTS} \ > out/$$base.$$fmt.out 2> out/$$base.$$fmt.err ; \ ${DIFF} -Nu ${srcdir}/saved/$$base.$$fmt.out out/$$base.$$fmt.out ${S2O} ; \ ${DIFF} -Nu ${srcdir}/saved/$$base.$$fmt.err out/$$base.$$fmt.err ${S2O} +TEST_ONE = \ + LIBXO_OPTIONS=:W$$fmt ${TEST_JIG} + +TEST_TWO = \ + LIBXO_OPTIONS=warn,encoder=test ${TEST_JIG} + TEST_FORMATS = T XP JP HP X J H HIPx test tests: ${bin_PROGRAMS} @@ -87,6 +98,11 @@ test tests: ${bin_PROGRAMS} echo "... $$test ... $$fmt ..."; \ ${TEST_ONE}; \ true; \ + done) ; \ + (for fmt in E; do \ + echo "... $$test ... $$fmt ..."; \ + ${TEST_TWO}; \ + true; \ done) \ done) @@ -96,7 +112,7 @@ one: accept: -@(for test in ${TEST_CASES} ; do \ base=`${BASENAME} $$test .c` ; \ - (for fmt in ${TEST_FORMATS}; do \ + (for fmt in ${TEST_FORMATS} E; do \ echo "... $$test ... $$fmt ..."; \ ${CP} out/$$base.$$fmt.out ${srcdir}/saved/$$base.$$fmt.out ; \ ${CP} out/$$base.$$fmt.err ${srcdir}/saved/$$base.$$fmt.err ; \ diff --git a/contrib/libxo/tests/core/saved/test_01.E.err b/contrib/libxo/tests/core/saved/test_01.E.err new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/contrib/libxo/tests/core/saved/test_01.E.out b/contrib/libxo/tests/core/saved/test_01.E.out new file mode 100644 index 000000000000..296a34ef996a --- /dev/null +++ b/contrib/libxo/tests/core/saved/test_01.E.out @@ -0,0 +1,119 @@ +op create: [] [] +op open_container: [top] [] +op string: [host] [my-box] +op string: [domain] [example.com] +op attr: [test] [value] +op open_container: [data] [] +op open_list: [item] [] +op attr: [test2] [value2] +op open_instance: [item] [] +op attr: [test3] [value3] +op string: [sku] [GRO-000-415] +op string: [name] [gum] +op content: [sold] [1412] +op content: [in-stock] [54] +op content: [on-order] [10] +op close_instance: [item] [] +op open_instance: [item] [] +op attr: [test3] [value3] +op string: [sku] [HRD-000-212] +op string: [name] [rope] +op content: [sold] [85] +op content: [in-stock] [4] +op content: [on-order] [2] +op close_instance: [item] [] +op open_instance: [item] [] +op attr: [test3] [value3] +op string: [sku] [HRD-000-517] +op string: [name] [ladder] +op content: [sold] [0] +op content: [in-stock] [2] +op content: [on-order] [1] +op close_instance: [item] [] +op open_instance: [item] [] +op attr: [test3] [value3] +op string: [sku] [HRD-000-632] +op string: [name] [bolt] +op content: [sold] [4123] +op content: [in-stock] [144] +op content: [on-order] [42] +op close_instance: [item] [] +op open_instance: [item] [] +op attr: [test3] [value3] +op string: [sku] [GRO-000-2331] +op string: [name] [water] +op content: [sold] [17] +op content: [in-stock] [14] +op content: [on-order] [2] +op close_instance: [item] [] +op close_list: [item] [] +op close_container: [data] [] +op open_container: [data2] [] +op open_list: [item] [] +op open_instance: [item] [] +op string: [sku] [GRO-000-415] +op string: [name] [gum] +op content: [sold] [1412.0] +op content: [in-stock] [54] +op content: [on-order] [10] +op close_instance: [item] [] +op open_instance: [item] [] +op string: [sku] [HRD-000-212] +op string: [name] [rope] +op content: [sold] [85.0] +op content: [in-stock] [4] +op content: [on-order] [2] +op close_instance: [item] [] +op open_instance: [item] [] +op string: [sku] [HRD-000-517] +op string: [name] [ladder] +op content: [sold] [0] +op content: [in-stock] [2] +op content: [on-order] [1] +op close_instance: [item] [] +op open_instance: [item] [] +op string: [sku] [HRD-000-632] +op string: [name] [bolt] +op content: [sold] [4123.0] +op content: [in-stock] [144] +op content: [on-order] [42] +op close_instance: [item] [] +op open_instance: [item] [] +op string: [sku] [GRO-000-2331] +op string: [name] [water] +op content: [sold] [17.0] +op content: [in-stock] [14] +op content: [on-order] [2] +op close_instance: [item] [] +op close_list: [item] [] +op close_container: [data2] [] +op open_container: [data3] [] +op open_list: [item] [] +op open_instance: [item] [] +op string: [sku] [GRO-000-533] +op string: [name] [fish] +op content: [sold] [1321.0] +op content: [in-stock] [45] +op content: [on-order] [1] +op close_instance: [item] [] +op close_list: [item] [] +op close_container: [data3] [] +op open_container: [data4] [] +op open_list: [item] [] +op attr: [test4] [value4] +op string: [item] [gum] +op attr: [test4] [value4] +op string: [item] [rope] +op attr: [test4] [value4] +op string: [item] [ladder] +op attr: [test4] [value4] +op string: [item] [bolt] +op attr: [test4] [value4] +op string: [item] [water] +op close_list: [item] [] +op close_container: [data4] [] +op content: [cost] [425] +op content: [cost] [455] +op close_container: [top] [] +op finish: [] [] +op flush: [] [] diff --git a/contrib/libxo/tests/core/saved/test_01.H.out b/contrib/libxo/tests/core/saved/test_01.H.out index 4d4f2f171b60..ead320e0e610 100644 --- a/contrib/libxo/tests/core/saved/test_01.H.out +++ b/contrib/libxo/tests/core/saved/test_01.H.out @@ -1 +1 @@ -
Item
Total Sold
In Stock
On Order
SKU
gum
1412
54
10
GRO-000-415
rope
85
4
2
HRD-000-212
ladder
0
2
1
HRD-000-517
bolt
4123
144
42
HRD-000-632
water
17
14
2
GRO-000-2331
Item
'
gum
':
Total sold
:
1412.0
In stock
:
54
On order
:
10
SKU
:
GRO-000-415
Item
'
rope
':
Total sold
:
85.0
In stock
:
4
On order
:
2
SKU
:
HRD-000-212
Item
'
ladder
':
Total sold
:
0
In stock
:
2
On order
:
1
SKU
:
HRD-000-517
Item
'
bolt
':
Total sold
:
4123.0
In stock
:
144
On order
:
42
SKU
:
HRD-000-632
Item
'
water
':
Total sold
:
17.0
In stock
:
14
On order
:
2
SKU
:
GRO-000-2331
Item
'
fish
':
Total sold
:
1321.0
In stock
:
45
On order
:
1
SKU
:
GRO-000-533
Item
:
gum
Item
:
rope
Item
:
ladder
Item
:
bolt
Item
:
water
X
X
X
X
X
X
X
X
X
X
Cost
:
425
X
X
Cost
:
455
\ No newline at end of file +
Connecting to
my-box
.
example.com
...
Item
Total Sold
In Stock
On Order
SKU
gum
1412
54
10
GRO-000-415
rope
85
4
2
HRD-000-212
ladder
0
2
1
HRD-000-517
bolt
4123
144
42
HRD-000-632
water
17
14
2
GRO-000-2331
Item
'
gum
':
Total sold
:
1412.0
In stock
:
54
On order
:
10
SKU
:
GRO-000-415
Item
'
rope
':
Total sold
:
85.0
In stock
:
4
On order
:
2
SKU
:
HRD-000-212
Item
'
ladder
':
Total sold
:
0
In stock
:
2
On order
:
1
SKU
:
HRD-000-517
Item
'
bolt
':
Total sold
:
4123.0
In stock
:
144
On order
:
42
SKU
:
HRD-000-632
Item
'
water
':
Total sold
:
17.0
In stock
:
14
On order
:
2
SKU
:
GRO-000-2331
Item
'
fish
':
Total sold
:
1321.0
In stock
:
45
On order
:
1
SKU
:
GRO-000-533
Item
:
gum
Item
:
rope
Item
:
ladder
Item
:
bolt
Item
:
water
X
X
X
X
X
X
X
X
X
X
Cost
:
425
X
X
Cost
:
455
\ No newline at end of file diff --git a/contrib/libxo/tests/core/saved/test_01.HIPx.out b/contrib/libxo/tests/core/saved/test_01.HIPx.out index 2bafff91c45c..2b8e2969a062 100644 --- a/contrib/libxo/tests/core/saved/test_01.HIPx.out +++ b/contrib/libxo/tests/core/saved/test_01.HIPx.out @@ -1,3 +1,10 @@ +
+
Connecting to
+
my-box
+
.
+
example.com
+
...
+
Item
Total Sold
@@ -47,224 +54,224 @@
Item
'
-
gum
+
gum
':
Total sold
:
-
1412.0
+
1412.0
In stock
:
-
54
+
54
On order
:
-
10
+
10
SKU
:
-
GRO-000-415
+
GRO-000-415
Item
'
-
rope
+
rope
':
Total sold
:
-
85.0
+
85.0
In stock
:
-
4
+
4
On order
:
-
2
+
2
SKU
:
-
HRD-000-212
+
HRD-000-212
Item
'
-
ladder
+
ladder
':
Total sold
:
-
0
+
0
In stock
:
-
2
+
2
On order
:
-
1
+
1
SKU
:
-
HRD-000-517
+
HRD-000-517
Item
'
-
bolt
+
bolt
':
Total sold
:
-
4123.0
+
4123.0
In stock
:
-
144
+
144
On order
:
-
42
+
42
SKU
:
-
HRD-000-632
+
HRD-000-632
Item
'
-
water
+
water
':
Total sold
:
-
17.0
+
17.0
In stock
:
-
14
+
14
On order
:
-
2
+
2
SKU
:
-
GRO-000-2331
+
GRO-000-2331
Item
'
-
fish
+
fish
':
Total sold
:
-
1321.0
+
1321.0
In stock
:
-
45
+
45
On order
:
-
1
+
1
SKU
:
-
GRO-000-533
+
GRO-000-533
Item
:
-
gum
+
gum
Item
:
-
rope
+
rope
Item
:
-
ladder
+
ladder
Item
:
-
bolt
+
bolt
Item
:
-
water
+
water
X
diff --git a/contrib/libxo/tests/core/saved/test_01.HP.out b/contrib/libxo/tests/core/saved/test_01.HP.out index a007778c39ae..c8f2dbcb329f 100644 --- a/contrib/libxo/tests/core/saved/test_01.HP.out +++ b/contrib/libxo/tests/core/saved/test_01.HP.out @@ -1,3 +1,10 @@ +
+
Connecting to
+
my-box
+
.
+
example.com
+
...
+
Item
Total Sold
diff --git a/contrib/libxo/tests/core/saved/test_01.J.out b/contrib/libxo/tests/core/saved/test_01.J.out index 6fcdbd416307..69e3faa62126 100644 --- a/contrib/libxo/tests/core/saved/test_01.J.out +++ b/contrib/libxo/tests/core/saved/test_01.J.out @@ -1,2 +1,2 @@ -{"top": {"data": {"item": [{"sku":"GRO-000-415","name":"gum","sold":1412,"in-stock":54,"on-order":10}, {"sku":"HRD-000-212","name":"rope","sold":85,"in-stock":4,"on-order":2}, {"sku":"HRD-000-517","name":"ladder","sold":0,"in-stock":2,"on-order":1}, {"sku":"HRD-000-632","name":"bolt","sold":4123,"in-stock":144,"on-order":42}, {"sku":"GRO-000-2331","name":"water","sold":17,"in-stock":14,"on-order":2}]}, "data": {"item": [{"sku":"GRO-000-415","name":"gum","sold":1412.0,"in-stock":54,"on-order":10}, {"sku":"HRD-000-212","name":"rope","sold":85.0,"in-stock":4,"on-order":2}, {"sku":"HRD-000-517","name":"ladder","sold":0,"in-stock":2,"on-order":1}, {"sku":"HRD-000-632","name":"bolt","sold":4123.0,"in-stock":144,"on-order":42}, {"sku":"GRO-000-2331","name":"water","sold":17.0,"in-stock":14,"on-order":2}]}, "data": {"item": [{"sku":"GRO-000-533","name":"fish","sold":1321.0,"in-stock":45,"on-order":1}]}, "data": {"item": ["gum","rope","ladder","bolt","water"]},"cost":425,"cost":455} +{"top": {"host":"my-box","domain":"example.com", "data": {"item": [{"sku":"GRO-000-415","name":"gum","sold":1412,"in-stock":54,"on-order":10}, {"sku":"HRD-000-212","name":"rope","sold":85,"in-stock":4,"on-order":2}, {"sku":"HRD-000-517","name":"ladder","sold":0,"in-stock":2,"on-order":1}, {"sku":"HRD-000-632","name":"bolt","sold":4123,"in-stock":144,"on-order":42}, {"sku":"GRO-000-2331","name":"water","sold":17,"in-stock":14,"on-order":2}]}, "data2": {"item": [{"sku":"GRO-000-415","name":"gum","sold":1412.0,"in-stock":54,"on-order":10}, {"sku":"HRD-000-212","name":"rope","sold":85.0,"in-stock":4,"on-order":2}, {"sku":"HRD-000-517","name":"ladder","sold":0,"in-stock":2,"on-order":1}, {"sku":"HRD-000-632","name":"bolt","sold":4123.0,"in-stock":144,"on-order":42}, {"sku":"GRO-000-2331","name":"water","sold":17.0,"in-stock":14,"on-order":2}]}, "data3": {"item": [{"sku":"GRO-000-533","name":"fish","sold":1321.0,"in-stock":45,"on-order":1}]}, "data4": {"item": ["gum","rope","ladder","bolt","water"]},"cost":425,"cost":455} } diff --git a/contrib/libxo/tests/core/saved/test_01.JP.out b/contrib/libxo/tests/core/saved/test_01.JP.out index 479006525306..e65897fe7244 100644 --- a/contrib/libxo/tests/core/saved/test_01.JP.out +++ b/contrib/libxo/tests/core/saved/test_01.JP.out @@ -1,5 +1,7 @@ { "top": { + "host": "my-box", + "domain": "example.com", "data": { "item": [ { @@ -39,7 +41,7 @@ } ] }, - "data": { + "data2": { "item": [ { "sku": "GRO-000-415", @@ -78,7 +80,7 @@ } ] }, - "data": { + "data3": { "item": [ { "sku": "GRO-000-533", @@ -89,7 +91,7 @@ } ] }, - "data": { + "data4": { "item": [ "gum", "rope", diff --git a/contrib/libxo/tests/core/saved/test_01.T.out b/contrib/libxo/tests/core/saved/test_01.T.out index c45b13001a18..2ecf537dd21b 100644 --- a/contrib/libxo/tests/core/saved/test_01.T.out +++ b/contrib/libxo/tests/core/saved/test_01.T.out @@ -1,3 +1,4 @@ +Connecting to my-box.example.com... Item Total Sold In Stock On Order SKU gum 1412 54 10 GRO-000-415 rope 85 4 2 HRD-000-212 diff --git a/contrib/libxo/tests/core/saved/test_01.X.out b/contrib/libxo/tests/core/saved/test_01.X.out index ce2719268aad..46f501e24724 100644 --- a/contrib/libxo/tests/core/saved/test_01.X.out +++ b/contrib/libxo/tests/core/saved/test_01.X.out @@ -1 +1 @@ -GRO-000-415gum14125410HRD-000-212rope8542HRD-000-517ladder021HRD-000-632bolt412314442GRO-000-2331water17142GRO-000-415gum1412.05410HRD-000-212rope85.042HRD-000-517ladder021HRD-000-632bolt4123.014442GRO-000-2331water17.0142GRO-000-533fish1321.0451gumropeladderboltwater425455 \ No newline at end of file +my-boxexample.comGRO-000-415gum14125410HRD-000-212rope8542HRD-000-517ladder021HRD-000-632bolt412314442GRO-000-2331water17142GRO-000-415gum1412.05410HRD-000-212rope85.042HRD-000-517ladder021HRD-000-632bolt4123.014442GRO-000-2331water17.0142GRO-000-533fish1321.0451gumropeladderboltwater425455 \ No newline at end of file diff --git a/contrib/libxo/tests/core/saved/test_01.XP.out b/contrib/libxo/tests/core/saved/test_01.XP.out index e5ea3e088976..c7f4bfe8d98b 100644 --- a/contrib/libxo/tests/core/saved/test_01.XP.out +++ b/contrib/libxo/tests/core/saved/test_01.XP.out @@ -1,4 +1,6 @@ + my-box + example.com GRO-000-415 @@ -36,7 +38,7 @@ 2 - + GRO-000-415 gum @@ -72,8 +74,8 @@ 14 2 - - + + GRO-000-533 fish @@ -81,14 +83,14 @@ 45 1 - - + + gum rope ladder bolt water - + 425 455 diff --git a/contrib/libxo/tests/core/saved/test_02.E.err b/contrib/libxo/tests/core/saved/test_02.E.err new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/contrib/libxo/tests/core/saved/test_02.E.out b/contrib/libxo/tests/core/saved/test_02.E.out new file mode 100644 index 000000000000..9e1ec96be97e --- /dev/null +++ b/contrib/libxo/tests/core/saved/test_02.E.out @@ -0,0 +1,68 @@ +op create: [] [] +op open_container: [top] [] +op open_container: [data] [] +op string: [what] [braces] +op string: [length] [abcdef] +op content: [fd] [-1] +op string: [error] [Bad file descriptor] +op string: [test] [good] +op content: [fd] [-1] +op string: [error] [Bad fi] +op string: [test] [good] +op content: [lines] [20] +op content: [words] [30] +op content: [characters] [40] +op open_leaf_list: [bytes] [] +op content: [bytes] [0] +op content: [bytes] [1] +op content: [bytes] [2] +op content: [bytes] [3] +op content: [bytes] [4] +op close_leaf_list: [bytes] [] +op content: [mbuf-current] [10] +op content: [mbuf-cache] [20] +op content: [mbuf-total] [30] +op content: [distance] [50] +op string: [location] [Boston] +op content: [memory] [64] +op content: [total] [640] +op content: [memory] [64] +op content: [total] [640] +op content: [ten] [10] +op content: [eleven] [11] +op content: [unknown] [1010] +op content: [unknown] [1010] +op content: [min] [15] +op content: [cur] [20] +op content: [max] [30] +op content: [min] [15] +op content: [cur] [20] +op content: [max] [125] +op content: [min] [15] +op content: [cur] [20] +op content: [max] [125] +op content: [min] [15] +op content: [cur] [20] +op content: [max] [125] +op content: [val1] [21] +op content: [val2] [58368] +op content: [val3] [100663296] +op content: [val4] [44470272] +op content: [val5] [1342172800] +op open_list: [flag] [] +op string: [flag] [one] +op string: [flag] [two] +op string: [flag] [three] +op close_list: [flag] [] +op content: [works] [null] +op content: [empty-tag] [true] +op string: [t1] [1000] +op string: [t2] [test5000] +op string: [t3] [ten-longx] +op string: [t4] [xtest] +op content: [count] [10] +op content: [test] [4] +op close_container: [data] [] +op close_container: [top] [] +op finish: [] [] +op flush: [] [] diff --git a/contrib/libxo/tests/core/saved/test_02.H.out b/contrib/libxo/tests/core/saved/test_02.H.out index 60350b5bd62a..f1387a645a4e 100644 --- a/contrib/libxo/tests/core/saved/test_02.H.out +++ b/contrib/libxo/tests/core/saved/test_02.H.out @@ -1,3 +1,7 @@ -
10
/
20
/
30
mbufs <&> in use (current/cache/total)
50
from
Boston
64
left out of
640
64
left out of
640
beforeworkingafter:
string
10
11
1010
packets here/there/everywhere
(
15
/
20
/
125
)
(
15
/
20
/
125
)
(
15
/
20
/
125
)
(
15
/
20
/
125
)
one
two
three
1:
1000
2:
test5000
3:
ten-longx
4:
xtest
this is an error
two more errors
this is an warning
two more warnings
V1/V2 packets
:
10
0004
tries
improper use of profanity; ten yard penalty; first down +
We are
{emit}
{ting}
some
braces
abcdef +
abcdef: Bad file descriptor +
improper use of profanity; ten yard penalty; first down +
length
abcdef
close
-1
returned
Bad file descriptor
good
close
-1
returned
Bad fi
good
improper use of profanity; ten yard penalty; first down +
20
30
40
file
0
bytes
1
byte
2
bytes
3
bytes
4
bytes
10
/
20
/
30
mbufs <&> in use (current/cache/total)
50
from
Boston
64
left out of
640
64
left out of
640
beforeworkingafter:
string
:
10
11
1010
packets here/there/everywhere
1010
packets here/there/everywhere
(
15
/
20
/
125
)
(
15
/
20
/
125
)
(
15
/
20
/
125
)
(
15
/
20
/
125
)
Humanize:
21
,
57 K
,
96M
,
44M
,
1.2G
one
two
three
(null)
1:
1000
2:
test5000
3:
ten-longx
4:
xtest
this is an error
two more errors
this is an warning
two more warnings
V1/V2 packets
:
10
0004
tries
improper use of profanity; ten yard penalty; first down
Shut 'er down, Clancey! She's a-pumpin' mud! <>!,"!<>
\ No newline at end of file diff --git a/contrib/libxo/tests/core/saved/test_02.HIPx.out b/contrib/libxo/tests/core/saved/test_02.HIPx.out index f4264f7baafa..7af26b39c1ae 100644 --- a/contrib/libxo/tests/core/saved/test_02.HIPx.out +++ b/contrib/libxo/tests/core/saved/test_02.HIPx.out @@ -1,3 +1,81 @@ +
+
We are
+
{emit}
+
{ting}
+
some
+
braces
+
+
+
abcdef +
+
+
+
abcdef: Bad file descriptor +
+
+
+
improper use of profanity; ten yard penalty; first down +
+
+
+
length
+
abcdef
+
+
+
close
+
-1
+
returned
+
Bad file descriptor
+
+
good
+
+
+
close
+
-1
+
returned
+
Bad fi
+
+
good
+
+
+
improper use of profanity; ten yard penalty; first down +
+
+
+
+
20
+
+
30
+
+
40
+
+
file
+
+
+
0
+
+
bytes
+
+
+
1
+
+
byte
+
+
+
2
+
+
bytes
+
+
+
3
+
+
bytes
+
+
+
4
+
+
bytes
+
10
/
@@ -28,6 +106,8 @@
string
+
:
+
10
11
@@ -36,6 +116,11 @@
packets here/there/everywhere
+
+
1010
+
+
packets here/there/everywhere
+
(
@@ -76,6 +161,18 @@
)
+
+
Humanize:
+
21
+
,
+
57 K
+
,
+
96M
+
,
+
44M
+
,
+
1.2G
+
one
@@ -83,6 +180,9 @@
three
+
+
(null)
+
1:
1000
diff --git a/contrib/libxo/tests/core/saved/test_02.HP.out b/contrib/libxo/tests/core/saved/test_02.HP.out index 1ccf36953349..f7c9d9b44feb 100644 --- a/contrib/libxo/tests/core/saved/test_02.HP.out +++ b/contrib/libxo/tests/core/saved/test_02.HP.out @@ -1,3 +1,81 @@ +
+
We are
+
{emit}
+
{ting}
+
some
+
braces
+
+
+
abcdef +
+
+
+
abcdef: Bad file descriptor +
+
+
+
improper use of profanity; ten yard penalty; first down +
+
+
+
length
+
abcdef
+
+
+
close
+
-1
+
returned
+
Bad file descriptor
+
+
good
+
+
+
close
+
-1
+
returned
+
Bad fi
+
+
good
+
+
+
improper use of profanity; ten yard penalty; first down +
+
+
+
+
20
+
+
30
+
+
40
+
+
file
+
+
+
0
+
+
bytes
+
+
+
1
+
+
byte
+
+
+
2
+
+
bytes
+
+
+
3
+
+
bytes
+
+
+
4
+
+
bytes
+
10
/
@@ -28,6 +106,8 @@
string
+
:
+
10
11
@@ -36,6 +116,11 @@
packets here/there/everywhere
+
+
1010
+
+
packets here/there/everywhere
+
(
@@ -76,6 +161,18 @@
)
+
+
Humanize:
+
21
+
,
+
57 K
+
,
+
96M
+
,
+
44M
+
,
+
1.2G
+
one
@@ -83,6 +180,9 @@
three
+
+
(null)
+
1:
1000
diff --git a/contrib/libxo/tests/core/saved/test_02.J.out b/contrib/libxo/tests/core/saved/test_02.J.out index 5b4502a1d158..5578389aa479 100644 --- a/contrib/libxo/tests/core/saved/test_02.J.out +++ b/contrib/libxo/tests/core/saved/test_02.J.out @@ -1,2 +1,2 @@ -{"top": {"data": {"mbuf-current":10,"mbuf-cache":20,"mbuf-total":30,"distance":50,"location":"Boston","memory":64,"total":640,"memory":64,"total":640,"ten":10,"eleven":11,"unknown":1010,"min":15,"cur":20,"max":30,"min":15,"cur":20,"max":125,"min":15,"cur":20,"max":125,"min":15,"cur":20,"max":125, "flag": ["one","two","three"],"empty-tag":true,"t1":"1000","t2":"test5000","t3":"ten-longx","t4":"xtest","count":10,"test":4, "error": {"message":"Shut 'er down, Clancey! She's a-pumpin' mud! <>!,\"!<>\n"}}} +{"top": {"data": {"what":"braces","length":"abcdef","fd":-1,"error":"Bad file descriptor","test":"good","fd":-1,"error":"Bad fi","test":"good","lines":20,"words":30,"characters":40, "bytes": [0,1,2,3,4],"mbuf-current":10,"mbuf-cache":20,"mbuf-total":30,"distance":50,"location":"Boston","memory":64,"total":640,"memory":64,"total":640,"ten":10,"eleven":11,"unknown":1010,"unknown":1010,"min":15,"cur":20,"max":30,"min":15,"cur":20,"max":125,"min":15,"cur":20,"max":125,"min":15,"cur":20,"max":125,"val1":21,"val2":58368,"val3":100663296,"val4":44470272,"val5":1342172800, "flag": ["one","two","three"],"works":null,"empty-tag":true,"t1":"1000","t2":"test5000","t3":"ten-longx","t4":"xtest", "__error": {"message":"this is an error"}, "__error": {"message":"two more errors"}, "__warning": {"message":this is an warning}, "__warning": {"message":"two more warnings"},"count":10,"test":4, "error": {"message":"Shut 'er down, Clancey! She's a-pumpin' mud! <>!,\"!<>\n"}}} } diff --git a/contrib/libxo/tests/core/saved/test_02.JP.out b/contrib/libxo/tests/core/saved/test_02.JP.out index ade2dc22c81f..fd910aa6e808 100644 --- a/contrib/libxo/tests/core/saved/test_02.JP.out +++ b/contrib/libxo/tests/core/saved/test_02.JP.out @@ -1,6 +1,24 @@ { "top": { "data": { + "what": "braces", + "length": "abcdef", + "fd": -1, + "error": "Bad file descriptor", + "test": "good", + "fd": -1, + "error": "Bad fi", + "test": "good", + "lines": 20, + "words": 30, + "characters": 40, + "bytes": [ + 0, + 1, + 2, + 3, + 4 + ], "mbuf-current": 10, "mbuf-cache": 20, "mbuf-total": 30, @@ -13,6 +31,7 @@ "ten": 10, "eleven": 11, "unknown": 1010, + "unknown": 1010, "min": 15, "cur": 20, "max": 30, @@ -25,16 +44,34 @@ "min": 15, "cur": 20, "max": 125, + "val1": 21, + "val2": 58368, + "val3": 100663296, + "val4": 44470272, + "val5": 1342172800, "flag": [ "one", "two", "three" ], + "works": null, "empty-tag": true, "t1": "1000", "t2": "test5000", "t3": "ten-longx", "t4": "xtest", + "__error": { + "message": "this is an error" + }, + "__error": { + "message": "two more errors" + }, + "__warning": { + "message": this is an warning + }, + "__warning": { + "message": "two more warnings" + }, "count": 10, "test": 4, "error": { diff --git a/contrib/libxo/tests/core/saved/test_02.T.out b/contrib/libxo/tests/core/saved/test_02.T.out index b37ba0081008..d65e7c50e9b9 100644 --- a/contrib/libxo/tests/core/saved/test_02.T.out +++ b/contrib/libxo/tests/core/saved/test_02.T.out @@ -1,15 +1,32 @@ +We are {emit}{ting} some braces +abcdef +abcdef: Bad file descriptor +improper use of profanity; ten yard penalty; first down +length abcdef +close -1 returned Bad file descriptor good +close -1 returned Bad fi good +improper use of profanity; ten yard penalty; first down + 20 30 40 file +0 bytes +1 byte +2 bytes +3 bytes +4 bytes 10/20/30 mbufs <&> in use (current/cache/total) 50 miles from Boston 64k left out of 640kb 64k left out of 640kilobytes beforeworkingafter: -string1011 +string: 1011 +1010 packets here/there/everywhere 1010 packets here/there/everywhere ( 15/20/125) ( 15/20/125) (15/20/125 ) (15/20/125 ) +Humanize: 21, 57 K, 96M, 44M, 1.2G one two three +(null) 1: 1000 2:test5000 3: ten-longx 4:xtest this is an error two more errors diff --git a/contrib/libxo/tests/core/saved/test_02.X.out b/contrib/libxo/tests/core/saved/test_02.X.out index ff6d40c41da0..30421ea52d1e 100644 --- a/contrib/libxo/tests/core/saved/test_02.X.out +++ b/contrib/libxo/tests/core/saved/test_02.X.out @@ -1,3 +1,7 @@ -10203050Boston646406464010111010152030152012515201251520125onetwothree1000test5000ten-longxxtestthis is an errortwo more errorsthis is an warningtwo more warnings104improper use of profanity; ten yard penalty; first down -Shut 'er down, Clancey! She's a-pumpin' mud! <>!,"!<> +bracesabcdef +abcdef: Bad file descriptor +improper use of profanity; ten yard penalty; first down +abcdef-1Bad file descriptorgood-1Bad figoodimproper use of profanity; ten yard penalty; first down +2030400123410203050Boston64640646401011101010101520301520125152012515201252158368100663296444702721342172800onetwothreenull1000test5000ten-longxxtest<__error>this is an error<__error>two more errors<__warning>this is an warning<__warning>two more warnings104improper use of profanity; ten yard penalty; first down +Shut 'er down, Clancey! She's a-pumpin' mud! <>!,"!<> \ No newline at end of file diff --git a/contrib/libxo/tests/core/saved/test_02.XP.out b/contrib/libxo/tests/core/saved/test_02.XP.out index d32c73078ff5..e70e6ef7bd5f 100644 --- a/contrib/libxo/tests/core/saved/test_02.XP.out +++ b/contrib/libxo/tests/core/saved/test_02.XP.out @@ -1,5 +1,29 @@ + braces + abcdef + + abcdef: Bad file descriptor + + improper use of profanity; ten yard penalty; first down + + abcdef + -1 + Bad file descriptor + good + -1 + Bad fi + good + improper use of profanity; ten yard penalty; first down + + 20 + 30 + 40 + 0 + 1 + 2 + 3 + 4 10 20 30 @@ -12,6 +36,7 @@ 10 11 1010 + 1010 15 20 30 @@ -24,29 +49,36 @@ 15 20 125 + 21 + 58368 + 100663296 + 44470272 + 1342172800 one two three + null 1000 test5000 ten-longx xtest - + <__error> this is an error - - + + <__error> two more errors - - + + <__warning> this is an warning - - + + <__warning> two more warnings - + 10 4 - improper use of profanity; ten yard penalty; first down + improper use of profanity; ten yard penalty; first down + Shut 'er down, Clancey! She's a-pumpin' mud! <>!,"!<> diff --git a/contrib/libxo/tests/core/saved/test_03.E.err b/contrib/libxo/tests/core/saved/test_03.E.err new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/contrib/libxo/tests/core/saved/test_03.E.out b/contrib/libxo/tests/core/saved/test_03.E.out new file mode 100644 index 000000000000..39657834bd23 --- /dev/null +++ b/contrib/libxo/tests/core/saved/test_03.E.out @@ -0,0 +1,22 @@ +op create: [] [] +op open_container: [employees] [] +op open_list: [employee] [] +op open_instance: [employee] [] +op string: [first-name] [Terry] +op string: [last-name] [Jones] +op content: [department] [660] +op close_instance: [employee] [] +op open_instance: [employee] [] +op string: [first-name] [Leslie] +op string: [last-name] [Patterson] +op content: [department] [341] +op close_instance: [employee] [] +op open_instance: [employee] [] +op string: [first-name] [Ashley] +op string: [last-name] [Smith] +op content: [department] [1440] +op close_instance: [employee] [] +op close_list: [employee] [] +op close_container: [employees] [] +op finish: [] [] +op flush: [] [] diff --git a/contrib/libxo/tests/core/saved/test_04.E.err b/contrib/libxo/tests/core/saved/test_04.E.err new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/contrib/libxo/tests/core/saved/test_04.E.out b/contrib/libxo/tests/core/saved/test_04.E.out new file mode 100644 index 000000000000..39657834bd23 --- /dev/null +++ b/contrib/libxo/tests/core/saved/test_04.E.out @@ -0,0 +1,22 @@ +op create: [] [] +op open_container: [employees] [] +op open_list: [employee] [] +op open_instance: [employee] [] +op string: [first-name] [Terry] +op string: [last-name] [Jones] +op content: [department] [660] +op close_instance: [employee] [] +op open_instance: [employee] [] +op string: [first-name] [Leslie] +op string: [last-name] [Patterson] +op content: [department] [341] +op close_instance: [employee] [] +op open_instance: [employee] [] +op string: [first-name] [Ashley] +op string: [last-name] [Smith] +op content: [department] [1440] +op close_instance: [employee] [] +op close_list: [employee] [] +op close_container: [employees] [] +op finish: [] [] +op flush: [] [] diff --git a/contrib/libxo/tests/core/saved/test_05.E.err b/contrib/libxo/tests/core/saved/test_05.E.err new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/contrib/libxo/tests/core/saved/test_05.E.out b/contrib/libxo/tests/core/saved/test_05.E.out new file mode 100644 index 000000000000..0ca15f4fc837 --- /dev/null +++ b/contrib/libxo/tests/core/saved/test_05.E.out @@ -0,0 +1,96 @@ +op create: [] [] +op open_container: [indian-languages] [] +op string: [gurmukhi] [ਲਹੌਰ ਪਾਕਿਸਤਾਨੀ ਪੰਜਾਬ ਦੀ ਰਾਜਧਾਨੀ ਹੈ । ਲੋਕ ਗਿਣਤੀ ਦੇ ਨਾਲ ਕਰਾਚੀ ਤੋਂ ਬਾਅਦ ਲਹੌਰ ਦੂਜਾ ਸਭ ਤੋਂ ਵੱਡਾ ਸ਼ਹਿਰ ਹੈ । ਲਹੌਰ ਪਾਕਿਸਤਾਨ ਦਾ ਸਿਆਸੀ, ਰਹਤਲੀ ਤੇ ਪੜà©à¨¹à¨¾à¨ˆ ਦਾ ਗੜà©à¨¹ ਹੈ ਅਤੇ ਇਸ ਲਈ ਇਹਨੂੰ ਪਾਕਿਸਤਾਨ ਦਾ ਦਿਲ ਵੀ ਕਿਹਾ ਜਾਂਦਾ ਹੈ । ਲਹੌਰ ਦਰਿਆ-à¨-ਰਾਵੀ ਦੇ ਕੰਢੇ ਤੇ ਵਸਦਾ ਹੈ ਤੇ ਇਸਦੀ ਲੋਕ ਗਿਣਤੀ ਇੱਕ ਕਰੋੜ ਦੇ ਨੇੜੇ ਹੈ ।] +op string: [shahmukhi] [Ù„Ûور پاکستانی پنجاب دا دارالحکومت اے۔ لوک گنتی دے نال کراچی توں بعد Ù„Ûور دوجا سبھ توں وڈا Ø´Ûر اے۔ Ù„Ûور پاکستان دا سیاسی، رÛتلی تے پڑھائی دا Ú¯Ú‘Ú¾ اے تے اس لئی ایھنوں پاکستان دا دل ÙˆÛŒ کیھا جاندا اے۔ Ù„Ûور دریاۓ راوی دے Ú©Ù†ÚˆÚ¾Û’ تے وسدا اے اسدی لوک گنتی اک کروڑ دے نیڑے اے Û”] +op string: [tranliteration] [lahor pÄkistÄn panjÄb dÄ dÄrul hakÅ«mat Ä“. lÅk giṇtÄ« dÄ“ nÄḷ karÄcÄ« tÅá·ˆ bÄad lahor dÅ«jÄ sab tÅá·ˆ vaá¸á¸Ä shahr Ä“. lahor pÄkistÄn dÄ siÄsÄ«, rahtalÄ« tÄ“ paá¹›Ä̀ī dÄ gÄÌá¹› Ä“ tÄ“ is laÄ« ihnÅ«á·ˆ pÄkistÄn dÄ dil vÄ« kehÄ jÄndÄ Ä“. lahor dariÄÄ“ rÄvÄ« dÄ“ kaná¸Ä“ tÄ“ vasdÄ Ä“. isdÄ« lÅk giṇtÄ« ikk karÅá¹› dÄ“ nēṛē Ä“.] +op close_container: [indian-languages] [] +op open_container: [employees] [] +op open_leaf_list: [wc] [] +op string: [wc] [à·´ - 0xdf4 - 1] +op string: [wc] [ණ - 0xdab - 1] +op string: [wc] [à·Š - 0xdca - 0] +op string: [wc] [ණ - 0xdab - 1] +op string: [wc] [្ - 0x17d2 - 0] +op string: [wc] [à·´ - 0xdf4 - 1] +op string: [wc] [1 - 0x31 - 1] +op string: [wc] [Í - 0x34f - 0] +op string: [wc] [2 - 0x32 - 1] +op string: [wc] [⃠- 0x20dd - 0] +op close_leaf_list: [wc] [] +op string: [fancy] [1Í2âƒ] +op string: [v1] [γιγνώσκειν] +op string: [v2] [ὦ ἄνδÏες ᾿Αθηναῖοι] +op string: [v1] [áƒáƒ®áƒšáƒáƒ•áƒ” გáƒáƒ˜áƒáƒ áƒáƒ— რეგისტრáƒáƒªáƒ˜áƒ] +op string: [v2] [Unicode-ის მეáƒáƒ—ე სáƒáƒ”რთáƒáƒ¨áƒáƒ áƒ˜áƒ¡áƒ] +op content: [width] [55] +op string: [sinhala] [෴ණ්ණ෴] +op content: [width] [4] +op string: [sinhala] [à·´] +op content: [width] [1] +op string: [sinhala] [෴ණ්ණ෴෴ණ්ණ෴] +op content: [width] [8] +op string: [not-sinhala] [123456] +op string: [tag] [ර්â€à¶] +op content: [width] [2] +op open_list: [employee] [] +op open_instance: [employee] [] +op string: [first-name] [Jim] +op string: [nic-name] ["რეგტ"] +op string: [last-name] [გთხáƒáƒ•áƒ— áƒáƒ®] +op content: [department] [431] +op content: [percent-time] [90] +op attr: [full-time] [honest & for true] +op string: [benefits] [full] +op close_instance: [employee] [] +op open_instance: [employee] [] +op string: [first-name] [Terry] +op string: [nic-name] ["
Οá½Ï‡á½¶ ταá½Ï„á½° παÏίσταταί μοι
γιγνώσκειν
,
ὦ ἄνδÏες ᾿Αθηναῖοι
გთხáƒáƒ•áƒ—
áƒáƒ®áƒšáƒáƒ•áƒ” გáƒáƒ˜áƒáƒ áƒáƒ— რეგისტრáƒáƒªáƒ˜áƒ
Unicode-ის მეáƒáƒ—ე სáƒáƒ”რთáƒáƒ¨áƒáƒ áƒ˜áƒ¡áƒ
Width
:
63
[
෴ණ්ණ෴
]
Width
:
7
[
à·´
]
Width
:
3
[
෴ණ්ණ
]
[
1234
]
[
ර්â€à¶
]
Width
:
5
First Name
Last Name
Department
Time (%)
Jim
(
"რეგტ"
)
გთხáƒáƒ•áƒ— áƒáƒ®
431
90
Terry
(
"<one"
)
Οá½Ï‡á½¶ ταá½Ï„á½° παÏ
660
90
Leslie
(
"Les"
)
Patterson
341
60
Ashley
(
"Ash"
)
Meter & Smith
1440
40
0123456789
(
"0123456789"
)
01234567890123
1440
40
áƒáƒ®áƒšáƒ
(
"გáƒáƒ˜áƒáƒ áƒ"
)
სáƒáƒ”რთáƒáƒ¨áƒáƒ áƒ˜áƒ¡áƒ
123
90
෴ණ්ණ෴෴ණ්ණ෴
(
"Mick"
)
෴ණ්ණ෴෴ණ්ණ෴෴ණ්ණ
110
20
\ No newline at end of file +
Sample text
This sample text was taken from the Punjabi Wikipedia article on Lahore and transliterated into the Latin script.
Gurmukhi:
ਲਹੌਰ ਪਾਕਿਸਤਾਨੀ ਪੰਜਾਬ ਦੀ ਰਾਜਧਾਨੀ ਹੈ । ਲੋਕ ਗਿਣਤੀ ਦੇ ਨਾਲ ਕਰਾਚੀ ਤੋਂ ਬਾਅਦ ਲਹੌਰ ਦੂਜਾ ਸਭ ਤੋਂ ਵੱਡਾ ਸ਼ਹਿਰ ਹੈ । ਲਹੌਰ ਪਾਕਿਸਤਾਨ ਦਾ ਸਿਆਸੀ, ਰਹਤਲੀ ਤੇ ਪੜà©à¨¹à¨¾à¨ˆ ਦਾ ਗੜà©à¨¹ ਹੈ ਅਤੇ ਇਸ ਲਈ ਇਹਨੂੰ ਪਾਕਿਸਤਾਨ ਦਾ ਦਿਲ ਵੀ ਕਿਹਾ ਜਾਂਦਾ ਹੈ । ਲਹੌਰ ਦਰਿਆ-à¨-ਰਾਵੀ ਦੇ ਕੰਢੇ ਤੇ ਵਸਦਾ ਹੈ ਤੇ ਇਸਦੀ ਲੋਕ ਗਿਣਤੀ ਇੱਕ ਕਰੋੜ ਦੇ ਨੇੜੇ ਹੈ ।
Shahmukhi:
Ù„Ûور پاکستانی پنجاب دا دارالحکومت اے۔ لوک گنتی دے نال کراچی توں بعد Ù„Ûور دوجا سبھ توں وڈا Ø´Ûر اے۔ Ù„Ûور پاکستان دا سیاسی، رÛتلی تے پڑھائی دا Ú¯Ú‘Ú¾ اے تے اس لئی ایھنوں پاکستان دا دل ÙˆÛŒ کیھا جاندا اے۔ Ù„Ûور دریاۓ راوی دے Ú©Ù†ÚˆÚ¾Û’ تے وسدا اے اسدی لوک گنتی اک کروڑ دے نیڑے اے Û”
Transliteration
:
lahor pÄkistÄn panjÄb dÄ dÄrul hakÅ«mat Ä“. lÅk giṇtÄ« dÄ“ nÄḷ karÄcÄ« tÅá·ˆ bÄad lahor dÅ«jÄ sab tÅá·ˆ vaá¸á¸Ä shahr Ä“. lahor pÄkistÄn dÄ siÄsÄ«, rahtalÄ« tÄ“ paá¹›Ä̀ī dÄ gÄÌá¹› Ä“ tÄ“ is laÄ« ihnÅ«á·ˆ pÄkistÄn dÄ dil vÄ« kehÄ jÄndÄ Ä“. lahor dariÄÄ“ rÄvÄ« dÄ“ kaná¸Ä“ tÄ“ vasdÄ Ä“. isdÄ« lÅk giṇtÄ« ikk karÅá¹› dÄ“ nēṛē Ä“.
Wide char:
à·´ - 0xdf4 - 1
Wide char:
ණ - 0xdab - 1
Wide char:
à·Š - 0xdca - 0
Wide char:
ණ - 0xdab - 1
Wide char:
្ - 0x17d2 - 0
Wide char:
à·´ - 0xdf4 - 1
Wide char:
1 - 0x31 - 1
Wide char:
Í - 0x34f - 0
Wide char:
2 - 0x32 - 1
Wide char:
⃠- 0x20dd - 0
Cool: [
1Í2âƒ
]
Οá½Ï‡á½¶ ταá½Ï„á½° παÏίσταταί μοι
γιγνώσκειν
,
ὦ ἄνδÏες ᾿Αθηναῖοι
გთხáƒáƒ•áƒ—
áƒáƒ®áƒšáƒáƒ•áƒ” გáƒáƒ˜áƒáƒ áƒáƒ— რეგისტრáƒáƒªáƒ˜áƒ
Unicode-ის მეáƒáƒ—ე სáƒáƒ”რთáƒáƒ¨áƒáƒ áƒ˜áƒ¡áƒ
Width
:
63
[
෴ණ්ණ෴
]
Width
:
6
[
à·´
]
Width
:
3
[
෴ණ්ණ෴
]
Width
:
6
[
1234
]
[
ර්â€à¶
]
Width
:
4
First Name
Last Name
Department
Time (%)
Jim
(
"რეგტ"
)
გთხáƒáƒ•áƒ— áƒáƒ®
431
90
Terry
(
"<one"
)
Οá½Ï‡á½¶ ταá½Ï„á½° παÏ
660
90
Leslie
(
"Les"
)
Patterson
341
60
Ashley
(
"Ash"
)
Meter & Smith
1440
40
0123456789
(
"0123456789"
)
01234567890123
1440
40
áƒáƒ®áƒšáƒ
(
"გáƒáƒ˜áƒáƒ áƒ"
)
სáƒáƒ”რთáƒáƒ¨áƒáƒ áƒ˜áƒ¡áƒ
123
90
෴ණ්ණ෴෴ණ්ණ෴
(
"Mick"
)
෴ණ්ණ෴෴ණ්ණ෴෴ණ්ණ෴෴෴
110
20
\ No newline at end of file diff --git a/contrib/libxo/tests/core/saved/test_05.HIPx.out b/contrib/libxo/tests/core/saved/test_05.HIPx.out index 105f8482785e..4389b85a3fd6 100644 --- a/contrib/libxo/tests/core/saved/test_05.HIPx.out +++ b/contrib/libxo/tests/core/saved/test_05.HIPx.out @@ -1,3 +1,73 @@ +
+
Sample text
+
+
+
This sample text was taken from the Punjabi Wikipedia article on Lahore and transliterated into the Latin script.
+
+
+
Gurmukhi:
+
+
+
ਲਹੌਰ ਪਾਕਿਸਤਾਨੀ ਪੰਜਾਬ ਦੀ ਰਾਜਧਾਨੀ ਹੈ । ਲੋਕ ਗਿਣਤੀ ਦੇ ਨਾਲ ਕਰਾਚੀ ਤੋਂ ਬਾਅਦ ਲਹੌਰ ਦੂਜਾ ਸਭ ਤੋਂ ਵੱਡਾ ਸ਼ਹਿਰ ਹੈ । ਲਹੌਰ ਪਾਕਿਸਤਾਨ ਦਾ ਸਿਆਸੀ, ਰਹਤਲੀ ਤੇ ਪੜà©à¨¹à¨¾à¨ˆ ਦਾ ਗੜà©à¨¹ ਹੈ ਅਤੇ ਇਸ ਲਈ ਇਹਨੂੰ ਪਾਕਿਸਤਾਨ ਦਾ ਦਿਲ ਵੀ ਕਿਹਾ ਜਾਂਦਾ ਹੈ । ਲਹੌਰ ਦਰਿਆ-à¨-ਰਾਵੀ ਦੇ ਕੰਢੇ ਤੇ ਵਸਦਾ ਹੈ ਤੇ ਇਸਦੀ ਲੋਕ ਗਿਣਤੀ ਇੱਕ ਕਰੋੜ ਦੇ ਨੇੜੇ ਹੈ ।
+
+
+
Shahmukhi:
+
+
+
Ù„Ûور پاکستانی پنجاب دا دارالحکومت اے۔ لوک گنتی دے نال کراچی توں بعد Ù„Ûور دوجا سبھ توں وڈا Ø´Ûر اے۔ Ù„Ûور پاکستان دا سیاسی، رÛتلی تے پڑھائی دا Ú¯Ú‘Ú¾ اے تے اس لئی ایھنوں پاکستان دا دل ÙˆÛŒ کیھا جاندا اے۔ Ù„Ûور دریاۓ راوی دے Ú©Ù†ÚˆÚ¾Û’ تے وسدا اے اسدی لوک گنتی اک کروڑ دے نیڑے اے Û”
+
+
+
Transliteration
+
:
+
+
+
lahor pÄkistÄn panjÄb dÄ dÄrul hakÅ«mat Ä“. lÅk giṇtÄ« dÄ“ nÄḷ karÄcÄ« tÅá·ˆ bÄad lahor dÅ«jÄ sab tÅá·ˆ vaá¸á¸Ä shahr Ä“. lahor pÄkistÄn dÄ siÄsÄ«, rahtalÄ« tÄ“ paá¹›Ä̀ī dÄ gÄÌá¹› Ä“ tÄ“ is laÄ« ihnÅ«á·ˆ pÄkistÄn dÄ dil vÄ« kehÄ jÄndÄ Ä“. lahor dariÄÄ“ rÄvÄ« dÄ“ kaná¸Ä“ tÄ“ vasdÄ Ä“. isdÄ« lÅk giṇtÄ« ikk karÅá¹› dÄ“ nēṛē Ä“.
+
+
+
Wide char:
+
à·´ - 0xdf4 - 1
+
+
+
Wide char:
+
ණ - 0xdab - 1
+
+
+
Wide char:
+
à·Š - 0xdca - 0
+
+
+
Wide char:
+
ණ - 0xdab - 1
+
+
+
Wide char:
+
្ - 0x17d2 - 0
+
+
+
Wide char:
+
à·´ - 0xdf4 - 1
+
+
+
Wide char:
+
1 - 0x31 - 1
+
+
+
Wide char:
+
Í - 0x34f - 0
+
+
+
Wide char:
+
2 - 0x32 - 1
+
+
+
Wide char:
+
⃠- 0x20dd - 0
+
+
+
Cool: [
+
1Í2âƒ
+
]
+
Οá½Ï‡á½¶ ταá½Ï„á½° παÏίσταταί μοι
γιγνώσκειν
@@ -25,7 +95,7 @@
Width
:
-
7
+
6
[
@@ -40,9 +110,15 @@
[
-
෴ණ්ණ
+
෴ණ්ණ෴
]
+
+
Width
+
:
+
+
6
+
[
1234
@@ -57,7 +133,7 @@
Width
:
-
5
+
4
First Name
@@ -129,8 +205,8 @@
(
"Mick"
)
-
-
෴ණ්ණ෴෴ණ්ණ෴෴ණ්ණ
+
+
෴ණ්ණ෴෴ණ්ණ෴෴ණ්ණ෴෴෴
110
20
diff --git a/contrib/libxo/tests/core/saved/test_05.HP.out b/contrib/libxo/tests/core/saved/test_05.HP.out index e66cf2b59642..e3fcdda0263c 100644 --- a/contrib/libxo/tests/core/saved/test_05.HP.out +++ b/contrib/libxo/tests/core/saved/test_05.HP.out @@ -1,3 +1,73 @@ +
+
Sample text
+
+
+
This sample text was taken from the Punjabi Wikipedia article on Lahore and transliterated into the Latin script.
+
+
+
Gurmukhi:
+
+
+
ਲਹੌਰ ਪਾਕਿਸਤਾਨੀ ਪੰਜਾਬ ਦੀ ਰਾਜਧਾਨੀ ਹੈ । ਲੋਕ ਗਿਣਤੀ ਦੇ ਨਾਲ ਕਰਾਚੀ ਤੋਂ ਬਾਅਦ ਲਹੌਰ ਦੂਜਾ ਸਭ ਤੋਂ ਵੱਡਾ ਸ਼ਹਿਰ ਹੈ । ਲਹੌਰ ਪਾਕਿਸਤਾਨ ਦਾ ਸਿਆਸੀ, ਰਹਤਲੀ ਤੇ ਪੜà©à¨¹à¨¾à¨ˆ ਦਾ ਗੜà©à¨¹ ਹੈ ਅਤੇ ਇਸ ਲਈ ਇਹਨੂੰ ਪਾਕਿਸਤਾਨ ਦਾ ਦਿਲ ਵੀ ਕਿਹਾ ਜਾਂਦਾ ਹੈ । ਲਹੌਰ ਦਰਿਆ-à¨-ਰਾਵੀ ਦੇ ਕੰਢੇ ਤੇ ਵਸਦਾ ਹੈ ਤੇ ਇਸਦੀ ਲੋਕ ਗਿਣਤੀ ਇੱਕ ਕਰੋੜ ਦੇ ਨੇੜੇ ਹੈ ।
+
+
+
Shahmukhi:
+
+
+
Ù„Ûور پاکستانی پنجاب دا دارالحکومت اے۔ لوک گنتی دے نال کراچی توں بعد Ù„Ûور دوجا سبھ توں وڈا Ø´Ûر اے۔ Ù„Ûور پاکستان دا سیاسی، رÛتلی تے پڑھائی دا Ú¯Ú‘Ú¾ اے تے اس لئی ایھنوں پاکستان دا دل ÙˆÛŒ کیھا جاندا اے۔ Ù„Ûور دریاۓ راوی دے Ú©Ù†ÚˆÚ¾Û’ تے وسدا اے اسدی لوک گنتی اک کروڑ دے نیڑے اے Û”
+
+
+
Transliteration
+
:
+
+
+
lahor pÄkistÄn panjÄb dÄ dÄrul hakÅ«mat Ä“. lÅk giṇtÄ« dÄ“ nÄḷ karÄcÄ« tÅá·ˆ bÄad lahor dÅ«jÄ sab tÅá·ˆ vaá¸á¸Ä shahr Ä“. lahor pÄkistÄn dÄ siÄsÄ«, rahtalÄ« tÄ“ paá¹›Ä̀ī dÄ gÄÌá¹› Ä“ tÄ“ is laÄ« ihnÅ«á·ˆ pÄkistÄn dÄ dil vÄ« kehÄ jÄndÄ Ä“. lahor dariÄÄ“ rÄvÄ« dÄ“ kaná¸Ä“ tÄ“ vasdÄ Ä“. isdÄ« lÅk giṇtÄ« ikk karÅá¹› dÄ“ nēṛē Ä“.
+
+
+
Wide char:
+
à·´ - 0xdf4 - 1
+
+
+
Wide char:
+
ණ - 0xdab - 1
+
+
+
Wide char:
+
à·Š - 0xdca - 0
+
+
+
Wide char:
+
ණ - 0xdab - 1
+
+
+
Wide char:
+
្ - 0x17d2 - 0
+
+
+
Wide char:
+
à·´ - 0xdf4 - 1
+
+
+
Wide char:
+
1 - 0x31 - 1
+
+
+
Wide char:
+
Í - 0x34f - 0
+
+
+
Wide char:
+
2 - 0x32 - 1
+
+
+
Wide char:
+
⃠- 0x20dd - 0
+
+
+
Cool: [
+
1Í2âƒ
+
]
+
Οá½Ï‡á½¶ ταá½Ï„á½° παÏίσταταί μοι
γιγνώσκειν
@@ -25,7 +95,7 @@
Width
:
-
7
+
6
[
@@ -40,9 +110,15 @@
[
-
෴ණ්ණ
+
෴ණ්ණ෴
]
+
+
Width
+
:
+
+
6
+
[
1234
@@ -57,7 +133,7 @@
Width
:
-
5
+
4
First Name
@@ -129,8 +205,8 @@
(
"Mick"
)
-
-
෴ණ්ණ෴෴ණ්ණ෴෴ණ්ණ
+
+
෴ණ්ණ෴෴ණ්ණ෴෴ණ්ණ෴෴෴
110
20
diff --git a/contrib/libxo/tests/core/saved/test_05.J.out b/contrib/libxo/tests/core/saved/test_05.J.out index 25d13ea03037..3525c43223cf 100644 --- a/contrib/libxo/tests/core/saved/test_05.J.out +++ b/contrib/libxo/tests/core/saved/test_05.J.out @@ -1,2 +1,3 @@ -{"employees": {"v1":"γιγνώσκειν","v2":"ὦ ἄνδÏες ᾿Αθηναῖοι","v1":"áƒáƒ®áƒšáƒáƒ•áƒ” გáƒáƒ˜áƒáƒ áƒáƒ— რეგისტრáƒáƒªáƒ˜áƒ","v2":"Unicode-ის მეáƒáƒ—ე სáƒáƒ”რთáƒáƒ¨áƒáƒ áƒ˜áƒ¡áƒ","width":55,"sinhala":"෴ණ්ණ෴","width":5,"sinhala":"à·´","width":1,"sinhala":"෴ණ්ණ෴෴ණ්ණ෴","not-sinhala":"123456","tag":"ර්â€à¶","width":3, "employee": [{"first-name":"Jim","nic-name":"\"რეგტ\"","last-name":"გთხáƒáƒ•áƒ— áƒáƒ®","department":431,"percent-time":90,"benefits":"full"}, {"first-name":"Terry","nic-name":"\"γιγνώσκεινὦ ἄνδÏες ᾿Αθηναῖοιáƒáƒ®áƒšáƒáƒ•áƒ” გáƒáƒ˜áƒáƒ áƒáƒ— რეგისტრáƒáƒªáƒ˜áƒUnicode-ის მეáƒáƒ—ე სáƒáƒ”რთáƒáƒ¨áƒáƒ áƒ˜áƒ¡áƒ55෴ණ්ණ෴5à·´1෴ණ්ණ෴෴ණ්ණ෴123456ර්â€à¶3Jim"რეგტ"გთხáƒáƒ•áƒ— áƒáƒ®43190fullTerry"<one"Οá½Ï‡á½¶ ταá½Ï„á½° παÏίσταταί μοι Jones66090fullLeslie"Les"Patterson34160fullAshley"Ash"Meter & Smith1440400123456789"0123456789"012345678901234567890144040áƒáƒ®áƒšáƒ"გáƒáƒ˜áƒáƒ áƒ"სáƒáƒ”რთáƒáƒ¨áƒáƒ áƒ˜áƒ¡áƒ12390full෴ණ්ණ෴෴ණ්ණ෴"Mick"෴ණ්ණ෴෴ණ්ණ෴෴ණ්ණ෴෴෴11020 \ No newline at end of file +ਲਹੌਰ ਪਾਕਿਸਤਾਨੀ ਪੰਜਾਬ ਦੀ ਰਾਜਧਾਨੀ ਹੈ । ਲੋਕ ਗਿਣਤੀ ਦੇ ਨਾਲ ਕਰਾਚੀ ਤੋਂ ਬਾਅਦ ਲਹੌਰ ਦੂਜਾ ਸਭ ਤੋਂ ਵੱਡਾ ਸ਼ਹਿਰ ਹੈ । ਲਹੌਰ ਪਾਕਿਸਤਾਨ ਦਾ ਸਿਆਸੀ, ਰਹਤਲੀ ਤੇ ਪੜà©à¨¹à¨¾à¨ˆ ਦਾ ਗੜà©à¨¹ ਹੈ ਅਤੇ ਇਸ ਲਈ ਇਹਨੂੰ ਪਾਕਿਸਤਾਨ ਦਾ ਦਿਲ ਵੀ ਕਿਹਾ ਜਾਂਦਾ ਹੈ । ਲਹੌਰ ਦਰਿਆ-à¨-ਰਾਵੀ ਦੇ ਕੰਢੇ ਤੇ ਵਸਦਾ ਹੈ ਤੇ ਇਸਦੀ ਲੋਕ ਗਿਣਤੀ ਇੱਕ ਕਰੋੜ ਦੇ ਨੇੜੇ ਹੈ ।لÛور پاکستانی پنجاب دا دارالحکومت اے۔ لوک گنتی دے نال کراچی توں بعد Ù„Ûور دوجا سبھ توں وڈا Ø´Ûر اے۔ Ù„Ûور پاکستان دا سیاسی، رÛتلی تے پڑھائی دا Ú¯Ú‘Ú¾ اے تے اس لئی ایھنوں پاکستان دا دل ÙˆÛŒ کیھا جاندا اے۔ Ù„Ûور دریاۓ راوی دے Ú©Ù†ÚˆÚ¾Û’ تے وسدا اے اسدی لوک گنتی اک کروڑ دے نیڑے اے Û”lahor pÄkistÄn panjÄb dÄ dÄrul hakÅ«mat Ä“. lÅk giṇtÄ« dÄ“ nÄḷ karÄcÄ« tÅá·ˆ bÄad lahor dÅ«jÄ sab tÅá·ˆ vaá¸á¸Ä shahr Ä“. lahor pÄkistÄn dÄ siÄsÄ«, rahtalÄ« tÄ“ paá¹›Ä̀ī dÄ gÄÌá¹› Ä“ tÄ“ is laÄ« ihnÅ«á·ˆ pÄkistÄn dÄ dil vÄ« kehÄ jÄndÄ Ä“. lahor dariÄÄ“ rÄvÄ« dÄ“ kaná¸Ä“ tÄ“ vasdÄ Ä“. isdÄ« lÅk giṇtÄ« ikk karÅá¹› dÄ“ nēṛē Ä“.à·´ - 0xdf4 - 1ණ - 0xdab - 1à·Š - 0xdca - 0ණ - 0xdab - 1្ - 0x17d2 - 0à·´ - 0xdf4 - 11 - 0x31 - 1Í - 0x34f - 02 - 0x32 - 1⃠- 0x20dd - 01Í2âƒÎ³Î¹Î³Î½á½½ÏƒÎºÎµÎ¹Î½á½¦ ἄνδÏες ᾿Αθηναῖοιáƒáƒ®áƒšáƒáƒ•áƒ” გáƒáƒ˜áƒáƒ áƒáƒ— რეგისტრáƒáƒªáƒ˜áƒUnicode-ის მეáƒáƒ—ე სáƒáƒ”რთáƒáƒ¨áƒáƒ áƒ˜áƒ¡áƒ55෴ණ්ණ෴4à·´1෴ණ්ණ෴෴ණ්ණ෴8123456ර්â€à¶2Jim"რეგტ"გთხáƒáƒ•áƒ— áƒáƒ®43190fullTerry"<one"Οá½Ï‡á½¶ ταá½Ï„á½° παÏίσταταί μοι Jones66090fullLeslie"Les"Patterson34160fullAshley"Ash"Meter & Smith1440400123456789"0123456789"012345678901234567890144040áƒáƒ®áƒšáƒ"გáƒáƒ˜áƒáƒ áƒ"სáƒáƒ”რთáƒáƒ¨áƒáƒ áƒ˜áƒ¡áƒ12390full෴ණ්ණ෴෴ණ්ණ෴"Mick"෴ණ්ණ෴෴ණ්ණ෴෴ණ්ණ෴෴෴11020 \ No newline at end of file diff --git a/contrib/libxo/tests/core/saved/test_05.XP.out b/contrib/libxo/tests/core/saved/test_05.XP.out index 6ef573f3a8ba..cf48b5b9ebd5 100644 --- a/contrib/libxo/tests/core/saved/test_05.XP.out +++ b/contrib/libxo/tests/core/saved/test_05.XP.out @@ -1,17 +1,34 @@ + + ਲਹੌਰ ਪਾਕਿਸਤਾਨੀ ਪੰਜਾਬ ਦੀ ਰਾਜਧਾਨੀ ਹੈ । ਲੋਕ ਗਿਣਤੀ ਦੇ ਨਾਲ ਕਰਾਚੀ ਤੋਂ ਬਾਅਦ ਲਹੌਰ ਦੂਜਾ ਸਭ ਤੋਂ ਵੱਡਾ ਸ਼ਹਿਰ ਹੈ । ਲਹੌਰ ਪਾਕਿਸਤਾਨ ਦਾ ਸਿਆਸੀ, ਰਹਤਲੀ ਤੇ ਪੜà©à¨¹à¨¾à¨ˆ ਦਾ ਗੜà©à¨¹ ਹੈ ਅਤੇ ਇਸ ਲਈ ਇਹਨੂੰ ਪਾਕਿਸਤਾਨ ਦਾ ਦਿਲ ਵੀ ਕਿਹਾ ਜਾਂਦਾ ਹੈ । ਲਹੌਰ ਦਰਿਆ-à¨-ਰਾਵੀ ਦੇ ਕੰਢੇ ਤੇ ਵਸਦਾ ਹੈ ਤੇ ਇਸਦੀ ਲੋਕ ਗਿਣਤੀ ਇੱਕ ਕਰੋੜ ਦੇ ਨੇੜੇ ਹੈ । + Ù„Ûور پاکستانی پنجاب دا دارالحکومت اے۔ لوک گنتی دے نال کراچی توں بعد Ù„Ûور دوجا سبھ توں وڈا Ø´Ûر اے۔ Ù„Ûور پاکستان دا سیاسی، رÛتلی تے پڑھائی دا Ú¯Ú‘Ú¾ اے تے اس لئی ایھنوں پاکستان دا دل ÙˆÛŒ کیھا جاندا اے۔ Ù„Ûور دریاۓ راوی دے Ú©Ù†ÚˆÚ¾Û’ تے وسدا اے اسدی لوک گنتی اک کروڑ دے نیڑے اے Û” + lahor pÄkistÄn panjÄb dÄ dÄrul hakÅ«mat Ä“. lÅk giṇtÄ« dÄ“ nÄḷ karÄcÄ« tÅá·ˆ bÄad lahor dÅ«jÄ sab tÅá·ˆ vaá¸á¸Ä shahr Ä“. lahor pÄkistÄn dÄ siÄsÄ«, rahtalÄ« tÄ“ paá¹›Ä̀ī dÄ gÄÌá¹› Ä“ tÄ“ is laÄ« ihnÅ«á·ˆ pÄkistÄn dÄ dil vÄ« kehÄ jÄndÄ Ä“. lahor dariÄÄ“ rÄvÄ« dÄ“ kaná¸Ä“ tÄ“ vasdÄ Ä“. isdÄ« lÅk giṇtÄ« ikk karÅá¹› dÄ“ nēṛē Ä“. + + à·´ - 0xdf4 - 1 + ණ - 0xdab - 1 + à·Š - 0xdca - 0 + ණ - 0xdab - 1 + ្ - 0x17d2 - 0 + à·´ - 0xdf4 - 1 + 1 - 0x31 - 1 + Í - 0x34f - 0 + 2 - 0x32 - 1 + ⃠- 0x20dd - 0 + 1Í2⃠γιγνώσκειν ὦ ἄνδÏες ᾿Αθηναῖοι áƒáƒ®áƒšáƒáƒ•áƒ” გáƒáƒ˜áƒáƒ áƒáƒ— რეგისტრáƒáƒªáƒ˜áƒ Unicode-ის მეáƒáƒ—ე სáƒáƒ”რთáƒáƒ¨áƒáƒ áƒ˜áƒ¡áƒ 55 ෴ණ්ණ෴ - 5 + 4 à·´ 1 ෴ණ්ණ෴෴ණ්ණ෴ + 8 123456 ර්â€à¶ - 3 + 2 Jim "რეგტ" diff --git a/contrib/libxo/tests/core/saved/test_06.E.err b/contrib/libxo/tests/core/saved/test_06.E.err new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/contrib/libxo/tests/core/saved/test_06.E.out b/contrib/libxo/tests/core/saved/test_06.E.out new file mode 100644 index 000000000000..39657834bd23 --- /dev/null +++ b/contrib/libxo/tests/core/saved/test_06.E.out @@ -0,0 +1,22 @@ +op create: [] [] +op open_container: [employees] [] +op open_list: [employee] [] +op open_instance: [employee] [] +op string: [first-name] [Terry] +op string: [last-name] [Jones] +op content: [department] [660] +op close_instance: [employee] [] +op open_instance: [employee] [] +op string: [first-name] [Leslie] +op string: [last-name] [Patterson] +op content: [department] [341] +op close_instance: [employee] [] +op open_instance: [employee] [] +op string: [first-name] [Ashley] +op string: [last-name] [Smith] +op content: [department] [1440] +op close_instance: [employee] [] +op close_list: [employee] [] +op close_container: [employees] [] +op finish: [] [] +op flush: [] [] diff --git a/contrib/libxo/tests/core/saved/test_07.E.err b/contrib/libxo/tests/core/saved/test_07.E.err new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/contrib/libxo/tests/core/saved/test_07.E.out b/contrib/libxo/tests/core/saved/test_07.E.out new file mode 100644 index 000000000000..45e4b69340eb --- /dev/null +++ b/contrib/libxo/tests/core/saved/test_07.E.out @@ -0,0 +1,76 @@ +op create: [] [] +op open_container: [employees] [] +op open_list: [test] [] +op open_instance: [test] [] +op string: [filename] [(null)] +op close_instance: [test] [] +op close_list: [test] [] +op string: [v1] [γιγνώσκειν] +op string: [v2] [ὦ ἄνδÏες ᾿Αθηναῖοι] +op content: [columns] [28] +op content: [columns] [2] +op string: [v1] [áƒáƒ®áƒšáƒáƒ•áƒ” გáƒáƒ˜áƒáƒ áƒáƒ— რეგისტრáƒáƒªáƒ˜áƒ] +op string: [v2] [Unicode-ის მეáƒáƒ—ე სáƒáƒ”რთáƒáƒ¨áƒáƒ áƒ˜áƒ¡áƒ] +op content: [columns] [55] +op content: [columns] [0] +op open_list: [employee] [] +op open_instance: [employee] [] +op string: [first-name] [Jim] +op string: [nic-name] ["რეგტ"] +op string: [last-name] [გთხáƒáƒ•áƒ— áƒáƒ®] +op content: [department] [431] +op content: [percent-time] [90] +op content: [columns] [23] +op attr: [full-time] [honest & for true] +op string: [benefits] [full] +op close_instance: [employee] [] +op open_instance: [employee] [] +op string: [first-name] [Terry] +op string: [nic-name] [" Count
-
gum
-
1412
+
gum
+
1412
-
rope
-
85
+
rope
+
85
-
ladder
-
0
+
ladder
+
0
-
bolt
-
4123
+
bolt
+
4123
-
water
-
17
+
water
+
17
@@ -59,27 +59,27 @@
Count
-
gum
-
1412
+
gum
+
1412
-
rope
-
85
+
rope
+
85
-
ladder
-
0
+
ladder
+
0
-
bolt
-
4123
+
bolt
+
4123
-
water
-
17
+
water
+
17
-
one
+
one
@@ -88,177 +88,177 @@
Count
-
gum
-
1412
+
gum
+
1412
Name
:
-
0
+
0
+ 1 =
-
1
+
1
Name
:
-
1
+
1
+ 1 =
-
2
+
2
Name
:
-
2
+
2
+ 1 =
-
3
+
3
Last
:
-
3
+
3
-
rope
-
85
+
rope
+
85
Name
:
-
0
+
0
+ 1 =
-
1
+
1
Name
:
-
1
+
1
+ 1 =
-
2
+
2
Name
:
-
2
+
2
+ 1 =
-
3
+
3
Last
:
-
3
+
3
-
ladder
-
0
+
ladder
+
0
Name
:
-
0
+
0
+ 1 =
-
1
+
1
Name
:
-
1
+
1
+ 1 =
-
2
+
2
Name
:
-
2
+
2
+ 1 =
-
3
+
3
Last
:
-
3
+
3
-
bolt
-
4123
+
bolt
+
4123
Name
:
-
0
+
0
+ 1 =
-
1
+
1
Name
:
-
1
+
1
+ 1 =
-
2
+
2
Name
:
-
2
+
2
+ 1 =
-
3
+
3
Last
:
-
3
+
3
-
water
-
17
+
water
+
17
Name
:
-
0
+
0
+ 1 =
-
1
+
1
Name
:
-
1
+
1
+ 1 =
-
2
+
2
Name
:
-
2
+
2
+ 1 =
-
3
+
3
Last
:
-
3
+
3
-
one
+
one
diff --git a/contrib/libxo/tests/core/saved/test_08.HP.err b/contrib/libxo/tests/core/saved/test_08.HP.err index 445bfb7172f2..011858cd30d7 100644 --- a/contrib/libxo/tests/core/saved/test_08.HP.err +++ b/contrib/libxo/tests/core/saved/test_08.HP.err @@ -1,18 +1,18 @@ -test: close (xo_close_container) fails at marker 'm1'; not found 'data' -test: close (xo_close_container) fails at marker 'm2'; not found 'data' -test: close (xo_close_container) fails at marker 'm2'; not found 'data' -test: close (xo_close_container) fails at marker 'm2'; not found 'data' -test: close (xo_close_container) fails at marker 'm2'; not found 'data' -test: close (xo_close_container) fails at marker 'm2'; not found 'data' -test: close (xo_close_container) fails at marker 'm2'; not found 'data' -test: close (xo_close_container) fails at marker 'm2'; not found 'data' -test: close (xo_close_container) fails at marker 'm2'; not found 'data' -test: close (xo_close_container) fails at marker 'm2'; not found 'data' -test: close (xo_close_container) fails at marker 'm2'; not found 'data' -test: close (xo_close_container) fails at marker 'm2'; not found 'data' -test: close (xo_close_container) fails at marker 'm2'; not found 'data' -test: close (xo_close_container) fails at marker 'm2'; not found 'data' -test: close (xo_close_container) fails at marker 'm2'; not found 'data' -test: close (xo_close_container) fails at marker 'm2'; not found 'data' -test: close (xo_close_container) fails at marker 'm1'; not found 'data' +test: close (xo_close_container) fails at marker 'm1'; not found 'data3' +test: close (xo_close_container) fails at marker 'm2'; not found 'data4' +test: close (xo_close_container) fails at marker 'm2'; not found 'data4' +test: close (xo_close_container) fails at marker 'm2'; not found 'data4' +test: close (xo_close_container) fails at marker 'm2'; not found 'data4' +test: close (xo_close_container) fails at marker 'm2'; not found 'data4' +test: close (xo_close_container) fails at marker 'm2'; not found 'data4' +test: close (xo_close_container) fails at marker 'm2'; not found 'data4' +test: close (xo_close_container) fails at marker 'm2'; not found 'data4' +test: close (xo_close_container) fails at marker 'm2'; not found 'data4' +test: close (xo_close_container) fails at marker 'm2'; not found 'data4' +test: close (xo_close_container) fails at marker 'm2'; not found 'data4' +test: close (xo_close_container) fails at marker 'm2'; not found 'data4' +test: close (xo_close_container) fails at marker 'm2'; not found 'data4' +test: close (xo_close_container) fails at marker 'm2'; not found 'data4' +test: close (xo_close_container) fails at marker 'm2'; not found 'data4' +test: close (xo_close_container) fails at marker 'm1'; not found 'data4' test: close (xo_close_container) fails at marker 'm1'; not found 'top' diff --git a/contrib/libxo/tests/core/saved/test_08.J.err b/contrib/libxo/tests/core/saved/test_08.J.err index 445bfb7172f2..011858cd30d7 100644 --- a/contrib/libxo/tests/core/saved/test_08.J.err +++ b/contrib/libxo/tests/core/saved/test_08.J.err @@ -1,18 +1,18 @@ -test: close (xo_close_container) fails at marker 'm1'; not found 'data' -test: close (xo_close_container) fails at marker 'm2'; not found 'data' -test: close (xo_close_container) fails at marker 'm2'; not found 'data' -test: close (xo_close_container) fails at marker 'm2'; not found 'data' -test: close (xo_close_container) fails at marker 'm2'; not found 'data' -test: close (xo_close_container) fails at marker 'm2'; not found 'data' -test: close (xo_close_container) fails at marker 'm2'; not found 'data' -test: close (xo_close_container) fails at marker 'm2'; not found 'data' -test: close (xo_close_container) fails at marker 'm2'; not found 'data' -test: close (xo_close_container) fails at marker 'm2'; not found 'data' -test: close (xo_close_container) fails at marker 'm2'; not found 'data' -test: close (xo_close_container) fails at marker 'm2'; not found 'data' -test: close (xo_close_container) fails at marker 'm2'; not found 'data' -test: close (xo_close_container) fails at marker 'm2'; not found 'data' -test: close (xo_close_container) fails at marker 'm2'; not found 'data' -test: close (xo_close_container) fails at marker 'm2'; not found 'data' -test: close (xo_close_container) fails at marker 'm1'; not found 'data' +test: close (xo_close_container) fails at marker 'm1'; not found 'data3' +test: close (xo_close_container) fails at marker 'm2'; not found 'data4' +test: close (xo_close_container) fails at marker 'm2'; not found 'data4' +test: close (xo_close_container) fails at marker 'm2'; not found 'data4' +test: close (xo_close_container) fails at marker 'm2'; not found 'data4' +test: close (xo_close_container) fails at marker 'm2'; not found 'data4' +test: close (xo_close_container) fails at marker 'm2'; not found 'data4' +test: close (xo_close_container) fails at marker 'm2'; not found 'data4' +test: close (xo_close_container) fails at marker 'm2'; not found 'data4' +test: close (xo_close_container) fails at marker 'm2'; not found 'data4' +test: close (xo_close_container) fails at marker 'm2'; not found 'data4' +test: close (xo_close_container) fails at marker 'm2'; not found 'data4' +test: close (xo_close_container) fails at marker 'm2'; not found 'data4' +test: close (xo_close_container) fails at marker 'm2'; not found 'data4' +test: close (xo_close_container) fails at marker 'm2'; not found 'data4' +test: close (xo_close_container) fails at marker 'm2'; not found 'data4' +test: close (xo_close_container) fails at marker 'm1'; not found 'data4' test: close (xo_close_container) fails at marker 'm1'; not found 'top' diff --git a/contrib/libxo/tests/core/saved/test_08.J.out b/contrib/libxo/tests/core/saved/test_08.J.out index cbce0910b6c2..9d897552beed 100644 --- a/contrib/libxo/tests/core/saved/test_08.J.out +++ b/contrib/libxo/tests/core/saved/test_08.J.out @@ -1,2 +1,2 @@ -{"top": {"data": {"contents": {"item": [{"name":"gum","count":1412}, {"name":"rope","count":85}, {"name":"ladder","count":0}, {"name":"bolt","count":4123}, {"name":"water","count":17}]}}, "data": {"contents": {"item": [{"name":"gum","count":1412}, {"name":"rope","count":85}, {"name":"ladder","count":0}, {"name":"bolt","count":4123}, {"name":"water","count":17}]}}, "data": {"contents": {"item": [{"name":"gum","count":1412}, {"name":"rope","count":85}, {"name":"ladder","count":0}, {"name":"bolt","count":4123}, {"name":"water","count":17,"test":"one"}]}}, "data": {"contents": {"item": [{"name":"gum","count":1412, "sub": [{"name":0,"next":1}, {"name":1,"next":2}, {"name":2,"next":3}],"last":3}, {"name":"rope","count":85, "sub": [{"name":0,"next":1}, {"name":1,"next":2}, {"name":2,"next":3}],"last":3}, {"name":"ladder","count":0, "sub": [{"name":0,"next":1}, {"name":1,"next":2}, {"name":2,"next":3}],"last":3}, {"name":"bolt","count":4123, "sub": [{"name":0,"next":1}, {"name":1,"next":2}, {"name":2,"next":3}],"last":3}, {"name":"water","count":17, "sub": [{"name":0,"next":1}, {"name":1,"next":2}, {"name":2,"next":3}],"last":3,"test":"one"}]}}} +{"top": {"data": {"contents": {"item": [{"name":"gum","count":1412}, {"name":"rope","count":85}, {"name":"ladder","count":0}, {"name":"bolt","count":4123}, {"name":"water","count":17}]}}, "data2": {"contents": {"item": [{"name":"gum","count":1412}, {"name":"rope","count":85}, {"name":"ladder","count":0}, {"name":"bolt","count":4123}, {"name":"water","count":17}]}}, "data3": {"contents": {"item": [{"name":"gum","count":1412}, {"name":"rope","count":85}, {"name":"ladder","count":0}, {"name":"bolt","count":4123}, {"name":"water","count":17,"test":"one"}]}}, "data4": {"contents": {"item": [{"name":"gum","count":1412, "sub": [{"name":0,"next":1}, {"name":1,"next":2}, {"name":2,"next":3}],"last":3}, {"name":"rope","count":85, "sub": [{"name":0,"next":1}, {"name":1,"next":2}, {"name":2,"next":3}],"last":3}, {"name":"ladder","count":0, "sub": [{"name":0,"next":1}, {"name":1,"next":2}, {"name":2,"next":3}],"last":3}, {"name":"bolt","count":4123, "sub": [{"name":0,"next":1}, {"name":1,"next":2}, {"name":2,"next":3}],"last":3}, {"name":"water","count":17, "sub": [{"name":0,"next":1}, {"name":1,"next":2}, {"name":2,"next":3}],"last":3,"test":"one"}]}}} } diff --git a/contrib/libxo/tests/core/saved/test_08.JP.err b/contrib/libxo/tests/core/saved/test_08.JP.err index 445bfb7172f2..011858cd30d7 100644 --- a/contrib/libxo/tests/core/saved/test_08.JP.err +++ b/contrib/libxo/tests/core/saved/test_08.JP.err @@ -1,18 +1,18 @@ -test: close (xo_close_container) fails at marker 'm1'; not found 'data' -test: close (xo_close_container) fails at marker 'm2'; not found 'data' -test: close (xo_close_container) fails at marker 'm2'; not found 'data' -test: close (xo_close_container) fails at marker 'm2'; not found 'data' -test: close (xo_close_container) fails at marker 'm2'; not found 'data' -test: close (xo_close_container) fails at marker 'm2'; not found 'data' -test: close (xo_close_container) fails at marker 'm2'; not found 'data' -test: close (xo_close_container) fails at marker 'm2'; not found 'data' -test: close (xo_close_container) fails at marker 'm2'; not found 'data' -test: close (xo_close_container) fails at marker 'm2'; not found 'data' -test: close (xo_close_container) fails at marker 'm2'; not found 'data' -test: close (xo_close_container) fails at marker 'm2'; not found 'data' -test: close (xo_close_container) fails at marker 'm2'; not found 'data' -test: close (xo_close_container) fails at marker 'm2'; not found 'data' -test: close (xo_close_container) fails at marker 'm2'; not found 'data' -test: close (xo_close_container) fails at marker 'm2'; not found 'data' -test: close (xo_close_container) fails at marker 'm1'; not found 'data' +test: close (xo_close_container) fails at marker 'm1'; not found 'data3' +test: close (xo_close_container) fails at marker 'm2'; not found 'data4' +test: close (xo_close_container) fails at marker 'm2'; not found 'data4' +test: close (xo_close_container) fails at marker 'm2'; not found 'data4' +test: close (xo_close_container) fails at marker 'm2'; not found 'data4' +test: close (xo_close_container) fails at marker 'm2'; not found 'data4' +test: close (xo_close_container) fails at marker 'm2'; not found 'data4' +test: close (xo_close_container) fails at marker 'm2'; not found 'data4' +test: close (xo_close_container) fails at marker 'm2'; not found 'data4' +test: close (xo_close_container) fails at marker 'm2'; not found 'data4' +test: close (xo_close_container) fails at marker 'm2'; not found 'data4' +test: close (xo_close_container) fails at marker 'm2'; not found 'data4' +test: close (xo_close_container) fails at marker 'm2'; not found 'data4' +test: close (xo_close_container) fails at marker 'm2'; not found 'data4' +test: close (xo_close_container) fails at marker 'm2'; not found 'data4' +test: close (xo_close_container) fails at marker 'm2'; not found 'data4' +test: close (xo_close_container) fails at marker 'm1'; not found 'data4' test: close (xo_close_container) fails at marker 'm1'; not found 'top' diff --git a/contrib/libxo/tests/core/saved/test_08.JP.out b/contrib/libxo/tests/core/saved/test_08.JP.out index 932d6a12d94b..46f3de5ff6c5 100644 --- a/contrib/libxo/tests/core/saved/test_08.JP.out +++ b/contrib/libxo/tests/core/saved/test_08.JP.out @@ -26,7 +26,7 @@ ] } }, - "data": { + "data2": { "contents": { "item": [ { @@ -52,7 +52,7 @@ ] } }, - "data": { + "data3": { "contents": { "item": [ { @@ -79,7 +79,7 @@ ] } }, - "data": { + "data4": { "contents": { "item": [ { diff --git a/contrib/libxo/tests/core/saved/test_08.T.err b/contrib/libxo/tests/core/saved/test_08.T.err index 445bfb7172f2..011858cd30d7 100644 --- a/contrib/libxo/tests/core/saved/test_08.T.err +++ b/contrib/libxo/tests/core/saved/test_08.T.err @@ -1,18 +1,18 @@ -test: close (xo_close_container) fails at marker 'm1'; not found 'data' -test: close (xo_close_container) fails at marker 'm2'; not found 'data' -test: close (xo_close_container) fails at marker 'm2'; not found 'data' -test: close (xo_close_container) fails at marker 'm2'; not found 'data' -test: close (xo_close_container) fails at marker 'm2'; not found 'data' -test: close (xo_close_container) fails at marker 'm2'; not found 'data' -test: close (xo_close_container) fails at marker 'm2'; not found 'data' -test: close (xo_close_container) fails at marker 'm2'; not found 'data' -test: close (xo_close_container) fails at marker 'm2'; not found 'data' -test: close (xo_close_container) fails at marker 'm2'; not found 'data' -test: close (xo_close_container) fails at marker 'm2'; not found 'data' -test: close (xo_close_container) fails at marker 'm2'; not found 'data' -test: close (xo_close_container) fails at marker 'm2'; not found 'data' -test: close (xo_close_container) fails at marker 'm2'; not found 'data' -test: close (xo_close_container) fails at marker 'm2'; not found 'data' -test: close (xo_close_container) fails at marker 'm2'; not found 'data' -test: close (xo_close_container) fails at marker 'm1'; not found 'data' +test: close (xo_close_container) fails at marker 'm1'; not found 'data3' +test: close (xo_close_container) fails at marker 'm2'; not found 'data4' +test: close (xo_close_container) fails at marker 'm2'; not found 'data4' +test: close (xo_close_container) fails at marker 'm2'; not found 'data4' +test: close (xo_close_container) fails at marker 'm2'; not found 'data4' +test: close (xo_close_container) fails at marker 'm2'; not found 'data4' +test: close (xo_close_container) fails at marker 'm2'; not found 'data4' +test: close (xo_close_container) fails at marker 'm2'; not found 'data4' +test: close (xo_close_container) fails at marker 'm2'; not found 'data4' +test: close (xo_close_container) fails at marker 'm2'; not found 'data4' +test: close (xo_close_container) fails at marker 'm2'; not found 'data4' +test: close (xo_close_container) fails at marker 'm2'; not found 'data4' +test: close (xo_close_container) fails at marker 'm2'; not found 'data4' +test: close (xo_close_container) fails at marker 'm2'; not found 'data4' +test: close (xo_close_container) fails at marker 'm2'; not found 'data4' +test: close (xo_close_container) fails at marker 'm2'; not found 'data4' +test: close (xo_close_container) fails at marker 'm1'; not found 'data4' test: close (xo_close_container) fails at marker 'm1'; not found 'top' diff --git a/contrib/libxo/tests/core/saved/test_08.X.err b/contrib/libxo/tests/core/saved/test_08.X.err index 445bfb7172f2..011858cd30d7 100644 --- a/contrib/libxo/tests/core/saved/test_08.X.err +++ b/contrib/libxo/tests/core/saved/test_08.X.err @@ -1,18 +1,18 @@ -test: close (xo_close_container) fails at marker 'm1'; not found 'data' -test: close (xo_close_container) fails at marker 'm2'; not found 'data' -test: close (xo_close_container) fails at marker 'm2'; not found 'data' -test: close (xo_close_container) fails at marker 'm2'; not found 'data' -test: close (xo_close_container) fails at marker 'm2'; not found 'data' -test: close (xo_close_container) fails at marker 'm2'; not found 'data' -test: close (xo_close_container) fails at marker 'm2'; not found 'data' -test: close (xo_close_container) fails at marker 'm2'; not found 'data' -test: close (xo_close_container) fails at marker 'm2'; not found 'data' -test: close (xo_close_container) fails at marker 'm2'; not found 'data' -test: close (xo_close_container) fails at marker 'm2'; not found 'data' -test: close (xo_close_container) fails at marker 'm2'; not found 'data' -test: close (xo_close_container) fails at marker 'm2'; not found 'data' -test: close (xo_close_container) fails at marker 'm2'; not found 'data' -test: close (xo_close_container) fails at marker 'm2'; not found 'data' -test: close (xo_close_container) fails at marker 'm2'; not found 'data' -test: close (xo_close_container) fails at marker 'm1'; not found 'data' +test: close (xo_close_container) fails at marker 'm1'; not found 'data3' +test: close (xo_close_container) fails at marker 'm2'; not found 'data4' +test: close (xo_close_container) fails at marker 'm2'; not found 'data4' +test: close (xo_close_container) fails at marker 'm2'; not found 'data4' +test: close (xo_close_container) fails at marker 'm2'; not found 'data4' +test: close (xo_close_container) fails at marker 'm2'; not found 'data4' +test: close (xo_close_container) fails at marker 'm2'; not found 'data4' +test: close (xo_close_container) fails at marker 'm2'; not found 'data4' +test: close (xo_close_container) fails at marker 'm2'; not found 'data4' +test: close (xo_close_container) fails at marker 'm2'; not found 'data4' +test: close (xo_close_container) fails at marker 'm2'; not found 'data4' +test: close (xo_close_container) fails at marker 'm2'; not found 'data4' +test: close (xo_close_container) fails at marker 'm2'; not found 'data4' +test: close (xo_close_container) fails at marker 'm2'; not found 'data4' +test: close (xo_close_container) fails at marker 'm2'; not found 'data4' +test: close (xo_close_container) fails at marker 'm2'; not found 'data4' +test: close (xo_close_container) fails at marker 'm1'; not found 'data4' test: close (xo_close_container) fails at marker 'm1'; not found 'top' diff --git a/contrib/libxo/tests/core/saved/test_08.X.out b/contrib/libxo/tests/core/saved/test_08.X.out index 5eb72b209ad1..b8ee3921af94 100644 --- a/contrib/libxo/tests/core/saved/test_08.X.out +++ b/contrib/libxo/tests/core/saved/test_08.X.out @@ -1 +1 @@ -gum1412rope85ladder0bolt4123water17gum1412rope85ladder0bolt4123water17gum1412rope85ladder0bolt4123water17onegum14120112233rope850112233ladder00112233bolt41230112233water170112233one \ No newline at end of file +gum1412rope85ladder0bolt4123water17gum1412rope85ladder0bolt4123water17gum1412rope85ladder0bolt4123water17onegum14120112233rope850112233ladder00112233bolt41230112233water170112233one \ No newline at end of file diff --git a/contrib/libxo/tests/core/saved/test_08.XP.err b/contrib/libxo/tests/core/saved/test_08.XP.err index 445bfb7172f2..011858cd30d7 100644 --- a/contrib/libxo/tests/core/saved/test_08.XP.err +++ b/contrib/libxo/tests/core/saved/test_08.XP.err @@ -1,18 +1,18 @@ -test: close (xo_close_container) fails at marker 'm1'; not found 'data' -test: close (xo_close_container) fails at marker 'm2'; not found 'data' -test: close (xo_close_container) fails at marker 'm2'; not found 'data' -test: close (xo_close_container) fails at marker 'm2'; not found 'data' -test: close (xo_close_container) fails at marker 'm2'; not found 'data' -test: close (xo_close_container) fails at marker 'm2'; not found 'data' -test: close (xo_close_container) fails at marker 'm2'; not found 'data' -test: close (xo_close_container) fails at marker 'm2'; not found 'data' -test: close (xo_close_container) fails at marker 'm2'; not found 'data' -test: close (xo_close_container) fails at marker 'm2'; not found 'data' -test: close (xo_close_container) fails at marker 'm2'; not found 'data' -test: close (xo_close_container) fails at marker 'm2'; not found 'data' -test: close (xo_close_container) fails at marker 'm2'; not found 'data' -test: close (xo_close_container) fails at marker 'm2'; not found 'data' -test: close (xo_close_container) fails at marker 'm2'; not found 'data' -test: close (xo_close_container) fails at marker 'm2'; not found 'data' -test: close (xo_close_container) fails at marker 'm1'; not found 'data' +test: close (xo_close_container) fails at marker 'm1'; not found 'data3' +test: close (xo_close_container) fails at marker 'm2'; not found 'data4' +test: close (xo_close_container) fails at marker 'm2'; not found 'data4' +test: close (xo_close_container) fails at marker 'm2'; not found 'data4' +test: close (xo_close_container) fails at marker 'm2'; not found 'data4' +test: close (xo_close_container) fails at marker 'm2'; not found 'data4' +test: close (xo_close_container) fails at marker 'm2'; not found 'data4' +test: close (xo_close_container) fails at marker 'm2'; not found 'data4' +test: close (xo_close_container) fails at marker 'm2'; not found 'data4' +test: close (xo_close_container) fails at marker 'm2'; not found 'data4' +test: close (xo_close_container) fails at marker 'm2'; not found 'data4' +test: close (xo_close_container) fails at marker 'm2'; not found 'data4' +test: close (xo_close_container) fails at marker 'm2'; not found 'data4' +test: close (xo_close_container) fails at marker 'm2'; not found 'data4' +test: close (xo_close_container) fails at marker 'm2'; not found 'data4' +test: close (xo_close_container) fails at marker 'm2'; not found 'data4' +test: close (xo_close_container) fails at marker 'm1'; not found 'data4' test: close (xo_close_container) fails at marker 'm1'; not found 'top' diff --git a/contrib/libxo/tests/core/saved/test_08.XP.out b/contrib/libxo/tests/core/saved/test_08.XP.out index 99520c074a9c..1d9b70f85007 100644 --- a/contrib/libxo/tests/core/saved/test_08.XP.out +++ b/contrib/libxo/tests/core/saved/test_08.XP.out @@ -23,7 +23,7 @@ - + gum @@ -46,8 +46,8 @@ 17 - - + + gum @@ -71,8 +71,8 @@ one - - + + gum @@ -161,5 +161,5 @@ one - + diff --git a/contrib/libxo/tests/core/saved/test_09.E.err b/contrib/libxo/tests/core/saved/test_09.E.err new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/contrib/libxo/tests/core/saved/test_09.E.out b/contrib/libxo/tests/core/saved/test_09.E.out new file mode 100644 index 000000000000..bb808d5e2ddc --- /dev/null +++ b/contrib/libxo/tests/core/saved/test_09.E.out @@ -0,0 +1,40 @@ +op create: [] [] +op open_container: [top] [] +op open_container: [data] [] +op open_container: [contents] [] +op open_leaf_list: [name] [] +op string: [name] [gum] +op string: [name] [rope] +op string: [name] [ladder] +op string: [name] [bolt] +op string: [name] [water] +op close_leaf_list: [name] [] +op close_container: [contents] [] +op open_container: [contents] [] +op open_leaf_list: [item] [] +op string: [item] [gum] +op string: [item] [rope] +op string: [item] [ladder] +op string: [item] [bolt] +op string: [item] [water] +op close_leaf_list: [item] [] +op close_container: [contents] [] +op open_container: [contents] [] +op open_list: [item] [] +op string: [item] [gum] +op string: [item] [rope] +op string: [item] [ladder] +op string: [item] [bolt] +op string: [item] [water] +op close_list: [item] [] +op string: [total] [six] +op string: [one] [one] +op open_leaf_list: [two] [] +op string: [two] [two] +op close_leaf_list: [two] [] +op string: [three] [three] +op close_container: [contents] [] +op close_container: [data] [] +op close_container: [top] [] +op finish: [] [] +op flush: [] [] diff --git a/contrib/libxo/tests/core/saved/test_10.E.err b/contrib/libxo/tests/core/saved/test_10.E.err new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/contrib/libxo/tests/core/saved/test_10.E.out b/contrib/libxo/tests/core/saved/test_10.E.out new file mode 100644 index 000000000000..4f21cda4cbce --- /dev/null +++ b/contrib/libxo/tests/core/saved/test_10.E.out @@ -0,0 +1,126 @@ +op create: [] [] +op version: [] [3.1.4] +op open_container: [top] [] +op attr: [test] [value] +op open_container: [data] [] +op open_list: [item] [] +op attr: [test2] [value2] +op close_list: [item] [] +op string: [data] [bold] +op string: [data] [bold-ul] +op string: [data] [triple] +op string: [data] [inv-ul] +op string: [data] [underline] +op string: [data] [plain] +op open_list: [item] [] +op open_instance: [item] [] +op attr: [test3] [value3] +op string: [sku] [GRO-000-415] +op string: [name] [gum] +op content: [sold] [1412] +op content: [in-stock] [54] +op content: [on-order] [10] +op close_instance: [item] [] +op open_instance: [item] [] +op attr: [test3] [value3] +op string: [sku] [HRD-000-212] +op string: [name] [rope] +op content: [sold] [85] +op content: [in-stock] [4] +op content: [on-order] [2] +op close_instance: [item] [] +op open_instance: [item] [] +op attr: [test3] [value3] +op string: [sku] [HRD-000-517] +op string: [name] [ladder] +op content: [sold] [0] +op content: [in-stock] [2] +op content: [on-order] [1] +op close_instance: [item] [] +op open_instance: [item] [] +op attr: [test3] [value3] +op string: [sku] [HRD-000-632] +op string: [name] [bolt] +op content: [sold] [4123] +op content: [in-stock] [144] +op content: [on-order] [42] +op close_instance: [item] [] +op open_instance: [item] [] +op attr: [test3] [value3] +op string: [sku] [GRO-000-2331] +op string: [name] [water] +op content: [sold] [17] +op content: [in-stock] [14] +op content: [on-order] [2] +op close_instance: [item] [] +op close_list: [item] [] +op close_container: [data] [] +op open_container: [data] [] +op open_list: [item] [] +op open_instance: [item] [] +op string: [sku] [GRO-000-415] +op string: [name] [gum] +op content: [sold] [1412.0] +op content: [in-stock] [54] +op content: [on-order] [10] +op close_instance: [item] [] +op open_instance: [item] [] +op string: [sku] [HRD-000-212] +op string: [name] [rope] +op content: [sold] [85.0] +op content: [in-stock] [4] +op content: [on-order] [2] +op close_instance: [item] [] +op open_instance: [item] [] +op string: [sku] [HRD-000-517] +op string: [name] [ladder] +op content: [sold] [0] +op content: [in-stock] [2] +op content: [on-order] [1] +op close_instance: [item] [] +op open_instance: [item] [] +op string: [sku] [HRD-000-632] +op string: [name] [bolt] +op content: [sold] [4123.0] +op content: [in-stock] [144] +op content: [on-order] [42] +op close_instance: [item] [] +op open_instance: [item] [] +op string: [sku] [GRO-000-2331] +op string: [name] [water] +op content: [sold] [17.0] +op content: [in-stock] [14] +op content: [on-order] [2] +op close_instance: [item] [] +op close_list: [item] [] +op close_container: [data] [] +op open_container: [data] [] +op open_list: [item] [] +op open_instance: [item] [] +op string: [sku] [GRO-000-533] +op string: [name] [fish] +op content: [sold] [1321.0] +op content: [in-stock] [45] +op content: [on-order] [1] +op close_instance: [item] [] +op close_list: [item] [] +op close_container: [data] [] +op open_container: [data] [] +op open_list: [item] [] +op attr: [test4] [value4] +op string: [item] [gum] +op attr: [test4] [value4] +op string: [item] [rope] +op attr: [test4] [value4] +op string: [item] [ladder] +op attr: [test4] [value4] +op string: [item] [bolt] +op attr: [test4] [value4] +op string: [item] [water] +op close_list: [item] [] +op close_container: [data] [] +op content: [cost] [425] +op content: [cost] [455] +op close_container: [top] [] +op finish: [] [] +op flush: [] [] diff --git a/contrib/libxo/tests/core/saved/test_11.E.err b/contrib/libxo/tests/core/saved/test_11.E.err new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/contrib/libxo/tests/core/saved/test_11.E.out b/contrib/libxo/tests/core/saved/test_11.E.out new file mode 100644 index 000000000000..9fd70fa569b1 --- /dev/null +++ b/contrib/libxo/tests/core/saved/test_11.E.out @@ -0,0 +1,26 @@ +op create: [] [] +op version: [] [3.1.4] +op open_container: [top] [] +op create: [] [] +{{<14>1 2015-06-23T13:47:09.123-0500 worker-host test-program 222 animal-status [animal-status@42 animal="snake" state="loose"] The snake is loose}} +{{test-program: }} +{{The snake is loose}} + +op create: [] [] +{{<22>1 2015-06-23T13:47:09.123-0500 worker-host test-program 222 animal-consumed [animal-consumed@42 animal="snake" pet="hamster"] My snake ate your hamster}} +{{test-program: }} +{{My snake ate your hamster}} + +op create: [] [] +{{<29>1 2015-06-23T13:47:09.123-0500 worker-host test-program 222 animal-talk [animal-talk@42 count="1" animal="owl" quote="\"e=m\\c[2\]\""] 1 owl said "e=m\c[2]"}} +{{test-program: }} +{{1 owl said "e=m\c[2]"}} + +op create: [] [] +{{<165>1 2015-06-23T13:47:09.123-0500 worker-host test-program 222 ID47 [ID47@32473 iut="3" event-source="application" event-id="1011"] An application 1011 log entry}} +{{test-program: }} +{{An application 1011 log entry}} + +op close_container: [top] [] +op finish: [] [] +op flush: [] [] diff --git a/contrib/libxo/tests/core/saved/test_11.H.err b/contrib/libxo/tests/core/saved/test_11.H.err new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/contrib/libxo/tests/core/saved/test_11.H.out b/contrib/libxo/tests/core/saved/test_11.H.out new file mode 100644 index 000000000000..0786c345654d --- /dev/null +++ b/contrib/libxo/tests/core/saved/test_11.H.out @@ -0,0 +1,16 @@ +{{<14>1 2015-06-23T13:47:09.123-0500 worker-host test-program 222 animal-status [animal-status@42 animal="snake" state="loose"] The snake is loose}} +{{test-program: }} +{{The snake is loose}} + +{{<22>1 2015-06-23T13:47:09.123-0500 worker-host test-program 222 animal-consumed [animal-consumed@42 animal="snake" pet="hamster"] My snake ate your hamster}} +{{test-program: }} +{{My snake ate your hamster}} + +{{<29>1 2015-06-23T13:47:09.123-0500 worker-host test-program 222 animal-talk [animal-talk@42 count="1" animal="owl" quote="\"e=m\\c[2\]\""] 1 owl said "e=m\c[2]"}} +{{test-program: }} +{{1 owl said "e=m\c[2]"}} + +{{<165>1 2015-06-23T13:47:09.123-0500 worker-host test-program 222 ID47 [ID47@32473 iut="3" event-source="application" event-id="1011"] An application 1011 log entry}} +{{test-program: }} +{{An application 1011 log entry}} + diff --git a/contrib/libxo/tests/core/saved/test_11.HIPx.err b/contrib/libxo/tests/core/saved/test_11.HIPx.err new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/contrib/libxo/tests/core/saved/test_11.HIPx.out b/contrib/libxo/tests/core/saved/test_11.HIPx.out new file mode 100644 index 000000000000..8b726f4fbe43 --- /dev/null +++ b/contrib/libxo/tests/core/saved/test_11.HIPx.out @@ -0,0 +1,16 @@ +{{<14>1 2015-06-23T13:47:09.123-0500 worker-host test-program 222 animal-status [animal-status@42 animal="" state=""] The snake is loose}} +{{test-program: }} +{{The snake is loose}} + +{{<22>1 2015-06-23T13:47:09.123-0500 worker-host test-program 222 animal-consumed [animal-consumed@42 animal="" pet=""] My snake ate your hamster}} +{{test-program: }} +{{My snake ate your hamster}} + +{{<29>1 2015-06-23T13:47:09.123-0500 worker-host test-program 222 animal-talk [animal-talk@42 count="" animal="" quote=""] 1 owl said "e=m\c[2]"}} +{{test-program: }} +{{1 owl said "e=m\c[2]"}} + +{{<165>1 2015-06-23T13:47:09.123-0500 worker-host test-program 222 ID47 [ID47@32473 iut="" event-source="" event-id=""] An application 1011 log entry}} +{{test-program: }} +{{An application 1011 log entry}} + diff --git a/contrib/libxo/tests/core/saved/test_11.HP.err b/contrib/libxo/tests/core/saved/test_11.HP.err new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/contrib/libxo/tests/core/saved/test_11.HP.out b/contrib/libxo/tests/core/saved/test_11.HP.out new file mode 100644 index 000000000000..0786c345654d --- /dev/null +++ b/contrib/libxo/tests/core/saved/test_11.HP.out @@ -0,0 +1,16 @@ +{{<14>1 2015-06-23T13:47:09.123-0500 worker-host test-program 222 animal-status [animal-status@42 animal="snake" state="loose"] The snake is loose}} +{{test-program: }} +{{The snake is loose}} + +{{<22>1 2015-06-23T13:47:09.123-0500 worker-host test-program 222 animal-consumed [animal-consumed@42 animal="snake" pet="hamster"] My snake ate your hamster}} +{{test-program: }} +{{My snake ate your hamster}} + +{{<29>1 2015-06-23T13:47:09.123-0500 worker-host test-program 222 animal-talk [animal-talk@42 count="1" animal="owl" quote="\"e=m\\c[2\]\""] 1 owl said "e=m\c[2]"}} +{{test-program: }} +{{1 owl said "e=m\c[2]"}} + +{{<165>1 2015-06-23T13:47:09.123-0500 worker-host test-program 222 ID47 [ID47@32473 iut="3" event-source="application" event-id="1011"] An application 1011 log entry}} +{{test-program: }} +{{An application 1011 log entry}} + diff --git a/contrib/libxo/tests/core/saved/test_11.J.err b/contrib/libxo/tests/core/saved/test_11.J.err new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/contrib/libxo/tests/core/saved/test_11.J.out b/contrib/libxo/tests/core/saved/test_11.J.out new file mode 100644 index 000000000000..4eb710503dbb --- /dev/null +++ b/contrib/libxo/tests/core/saved/test_11.J.out @@ -0,0 +1,18 @@ +{{<14>1 2015-06-23T13:47:09.123-0500 worker-host test-program 222 animal-status [animal-status@42 animal="snake" state="loose"] The snake is loose}} +{{test-program: }} +{{The snake is loose}} + +{{<22>1 2015-06-23T13:47:09.123-0500 worker-host test-program 222 animal-consumed [animal-consumed@42 animal="snake" pet="hamster"] My snake ate your hamster}} +{{test-program: }} +{{My snake ate your hamster}} + +{{<29>1 2015-06-23T13:47:09.123-0500 worker-host test-program 222 animal-talk [animal-talk@42 count="1" animal="owl" quote="\"e=m\\c[2\]\""] 1 owl said "e=m\c[2]"}} +{{test-program: }} +{{1 owl said "e=m\c[2]"}} + +{{<165>1 2015-06-23T13:47:09.123-0500 worker-host test-program 222 ID47 [ID47@32473 iut="3" event-source="application" event-id="1011"] An application 1011 log entry}} +{{test-program: }} +{{An application 1011 log entry}} + +{"__version": "3.1.4", "top": {} +} diff --git a/contrib/libxo/tests/core/saved/test_11.JP.err b/contrib/libxo/tests/core/saved/test_11.JP.err new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/contrib/libxo/tests/core/saved/test_11.JP.out b/contrib/libxo/tests/core/saved/test_11.JP.out new file mode 100644 index 000000000000..f82139be8779 --- /dev/null +++ b/contrib/libxo/tests/core/saved/test_11.JP.out @@ -0,0 +1,22 @@ +{{<14>1 2015-06-23T13:47:09.123-0500 worker-host test-program 222 animal-status [animal-status@42 animal="snake" state="loose"] The snake is loose}} +{{test-program: }} +{{The snake is loose}} + +{{<22>1 2015-06-23T13:47:09.123-0500 worker-host test-program 222 animal-consumed [animal-consumed@42 animal="snake" pet="hamster"] My snake ate your hamster}} +{{test-program: }} +{{My snake ate your hamster}} + +{{<29>1 2015-06-23T13:47:09.123-0500 worker-host test-program 222 animal-talk [animal-talk@42 count="1" animal="owl" quote="\"e=m\\c[2\]\""] 1 owl said "e=m\c[2]"}} +{{test-program: }} +{{1 owl said "e=m\c[2]"}} + +{{<165>1 2015-06-23T13:47:09.123-0500 worker-host test-program 222 ID47 [ID47@32473 iut="3" event-source="application" event-id="1011"] An application 1011 log entry}} +{{test-program: }} +{{An application 1011 log entry}} + +{ + "__version": "3.1.4", + "top": { + + } +} diff --git a/contrib/libxo/tests/core/saved/test_11.T.err b/contrib/libxo/tests/core/saved/test_11.T.err new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/contrib/libxo/tests/core/saved/test_11.T.out b/contrib/libxo/tests/core/saved/test_11.T.out new file mode 100644 index 000000000000..0786c345654d --- /dev/null +++ b/contrib/libxo/tests/core/saved/test_11.T.out @@ -0,0 +1,16 @@ +{{<14>1 2015-06-23T13:47:09.123-0500 worker-host test-program 222 animal-status [animal-status@42 animal="snake" state="loose"] The snake is loose}} +{{test-program: }} +{{The snake is loose}} + +{{<22>1 2015-06-23T13:47:09.123-0500 worker-host test-program 222 animal-consumed [animal-consumed@42 animal="snake" pet="hamster"] My snake ate your hamster}} +{{test-program: }} +{{My snake ate your hamster}} + +{{<29>1 2015-06-23T13:47:09.123-0500 worker-host test-program 222 animal-talk [animal-talk@42 count="1" animal="owl" quote="\"e=m\\c[2\]\""] 1 owl said "e=m\c[2]"}} +{{test-program: }} +{{1 owl said "e=m\c[2]"}} + +{{<165>1 2015-06-23T13:47:09.123-0500 worker-host test-program 222 ID47 [ID47@32473 iut="3" event-source="application" event-id="1011"] An application 1011 log entry}} +{{test-program: }} +{{An application 1011 log entry}} + diff --git a/contrib/libxo/tests/core/saved/test_11.X.err b/contrib/libxo/tests/core/saved/test_11.X.err new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/contrib/libxo/tests/core/saved/test_11.X.out b/contrib/libxo/tests/core/saved/test_11.X.out new file mode 100644 index 000000000000..bb73a4293c9a --- /dev/null +++ b/contrib/libxo/tests/core/saved/test_11.X.out @@ -0,0 +1,17 @@ +{{<14>1 2015-06-23T13:47:09.123-0500 worker-host test-program 222 animal-status [animal-status@42 animal="snake" state="loose"] The snake is loose}} +{{test-program: }} +{{The snake is loose}} + +{{<22>1 2015-06-23T13:47:09.123-0500 worker-host test-program 222 animal-consumed [animal-consumed@42 animal="snake" pet="hamster"] My snake ate your hamster}} +{{test-program: }} +{{My snake ate your hamster}} + +{{<29>1 2015-06-23T13:47:09.123-0500 worker-host test-program 222 animal-talk [animal-talk@42 count="1" animal="owl" quote="\"e=m\\c[2\]\""] 1 owl said "e=m\c[2]"}} +{{test-program: }} +{{1 owl said "e=m\c[2]"}} + +{{<165>1 2015-06-23T13:47:09.123-0500 worker-host test-program 222 ID47 [ID47@32473 iut="3" event-source="application" event-id="1011"] An application 1011 log entry}} +{{test-program: }} +{{An application 1011 log entry}} + + \ No newline at end of file diff --git a/contrib/libxo/tests/core/saved/test_11.XP.err b/contrib/libxo/tests/core/saved/test_11.XP.err new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/contrib/libxo/tests/core/saved/test_11.XP.out b/contrib/libxo/tests/core/saved/test_11.XP.out new file mode 100644 index 000000000000..46b5dd079ee5 --- /dev/null +++ b/contrib/libxo/tests/core/saved/test_11.XP.out @@ -0,0 +1,18 @@ +{{<14>1 2015-06-23T13:47:09.123-0500 worker-host test-program 222 animal-status [animal-status@42 animal="snake" state="loose"] The snake is loose}} +{{test-program: }} +{{The snake is loose}} + +{{<22>1 2015-06-23T13:47:09.123-0500 worker-host test-program 222 animal-consumed [animal-consumed@42 animal="snake" pet="hamster"] My snake ate your hamster}} +{{test-program: }} +{{My snake ate your hamster}} + +{{<29>1 2015-06-23T13:47:09.123-0500 worker-host test-program 222 animal-talk [animal-talk@42 count="1" animal="owl" quote="\"e=m\\c[2\]\""] 1 owl said "e=m\c[2]"}} +{{test-program: }} +{{1 owl said "e=m\c[2]"}} + +{{<165>1 2015-06-23T13:47:09.123-0500 worker-host test-program 222 ID47 [ID47@32473 iut="3" event-source="application" event-id="1011"] An application 1011 log entry}} +{{test-program: }} +{{An application 1011 log entry}} + + + diff --git a/contrib/libxo/tests/core/test_01.c b/contrib/libxo/tests/core/test_01.c index 9a9ed2c07fe9..f7fe61ec1ebe 100644 --- a/contrib/libxo/tests/core/test_01.c +++ b/contrib/libxo/tests/core/test_01.c @@ -8,11 +8,9 @@ * Phil Shafer, July 2014 */ -#include #include #include #include -#include #include "xo.h" @@ -48,9 +46,8 @@ main (int argc, char **argv) { "on-order", "number", "Number of items on order" }, { "sku", "string", "Stock Keeping Unit" }, { "sold", "number", "Number of items sold" }, - { NULL, NULL, NULL }, + { XO_INFO_NULL }, }; - int info_count = (sizeof(info) / sizeof(info[0])) - 1; argc = xo_parse_args(argc, argv); if (argc < 0) @@ -77,11 +74,13 @@ main (int argc, char **argv) } } - xo_set_info(NULL, info, info_count); + xo_set_info(NULL, info, -1); xo_set_flags(NULL, XOF_KEYS); xo_open_container_h(NULL, "top"); + xo_emit("Connecting to {:host}.{:domain}...\n", "my-box", "example.com"); + xo_attr("test", "value"); xo_open_container("data"); xo_open_list("item"); @@ -109,7 +108,7 @@ main (int argc, char **argv) xo_emit("\n\n"); - xo_open_container("data"); + xo_open_container("data2"); xo_open_list("item"); for (ip = list; ip->i_title; ip++) { @@ -128,9 +127,9 @@ main (int argc, char **argv) } xo_close_list("item"); - xo_close_container("data"); + xo_close_container("data2"); - xo_open_container("data"); + xo_open_container("data3"); xo_open_list("item"); for (ip = list2; ip->i_title; ip++) { @@ -149,9 +148,9 @@ main (int argc, char **argv) } xo_close_list("item"); - xo_close_container("data"); + xo_close_container("data3"); - xo_open_container("data"); + xo_open_container("data4"); xo_open_list("item"); for (ip = list; ip->i_title; ip++) { @@ -160,7 +159,7 @@ main (int argc, char **argv) } xo_close_list("item"); - xo_close_container("data"); + xo_close_container("data4"); xo_emit("X{P:}X", "epic fail"); xo_emit("X{T:}X", "epic fail"); diff --git a/contrib/libxo/tests/core/test_02.c b/contrib/libxo/tests/core/test_02.c index abcb14b7411b..abddcf273c97 100644 --- a/contrib/libxo/tests/core/test_02.c +++ b/contrib/libxo/tests/core/test_02.c @@ -15,6 +15,8 @@ #include "xo.h" +#include "xo_humanize.h" + int main (int argc, char **argv) { @@ -45,6 +47,35 @@ main (int argc, char **argv) xo_open_container("data"); + xo_emit("We are {{emit}}{{ting}} some {:what}\n", "braces"); + + xo_message("abcdef"); + close(-1); + xo_message_e("abcdef"); + + xo_message("improper use of profanity; %s; %s", + "ten yard penalty", "first down"); + + xo_emit("length {:length/%6.6s}\n", "abcdefghijklmnopqrstuvwxyz"); + + close(-1); + xo_emit("close {:fd/%d} returned {:error/%m} {:test}\n", -1, "good"); + close(-1); + xo_emit("close {:fd/%d} returned {:error/%6.6m} {:test}\n", -1, "good"); + + + xo_message("improper use of profanity; %s; %s", + "ten yard penalty", "first down"); + + xo_emit(" {:lines/%7ju} {:words/%7ju} " + "{:characters/%7ju} {d:filename/%s}\n", + 20, 30, 40, "file"); + + int i; + for (i = 0; i < 5; i++) + xo_emit("{lw:bytes/%d}{Np:byte,bytes}\n", i); + + xo_emit("{:mbuf-current/%u}/{:mbuf-cache/%u}/{:mbuf-total/%u} " "{N:mbufs <&> in use (current\\/cache\\/total)}\n", 10, 20, 30); @@ -54,26 +85,43 @@ main (int argc, char **argv) xo_emit("{:memory/%u}{U:/%s} left out of {:total/%u}{U:/%s}\n", 64, "k", 640, "kilobytes"); - xo_emit("{T:/before%safter:}\n", "working"); + xo_emit("{,title:/before%safter:}\n", "working"); - xo_emit("{d:some/%s}{:ten/%ju}{:eleven/%ju}\n", + xo_emit("{,display,white,colon:some/%s}" + "{,value:ten/%ju}{,value:eleven/%ju}\n", "string", (uintmax_t) 10, (uintmax_t) 11); xo_emit("{:unknown/%u} " "{N:/packet%s here\\/there\\/everywhere}\n", 1010, "s"); + xo_emit("{:unknown/%u} " + "{,note:/packet%s here\\/there\\/everywhere}\n", + 1010, "s"); + xo_emit("({[:/%d}{n:min/15}/{n:cur/20}/{:max/%d}{]:})\n", 30, 125); xo_emit("({[:30}{:min/%u}/{:cur/%u}/{:max/%u}{]:})\n", 15, 20, 125); xo_emit("({[:-30}{n:min/15}/{n:cur/20}/{n:max/125}{]:})\n"); xo_emit("({[:}{:min/%u}/{:cur/%u}/{:max/%u}{]:/%d})\n", 15, 20, 125, -30); + xo_emit("Humanize: {h:val1/%u}, {h,hn-space:val2/%u}, " + "{h,hn-decimal:val3/%u}, {h,hn-1000:val4/%u}, " + "{h,hn-decimal:val5/%u}\n", + 21, + 57 * 1024, + 96 * 1024 * 1024, + (42 * 1024 + 420) * 1024, + 1342172800); + xo_open_list("flag"); xo_emit("{lq:flag/one} {lq:flag/two} {lq:flag/three}\n"); xo_close_list("flag"); + xo_emit("{n:works/%s}\n", NULL); + xo_emit("{e:empty-tag/}"); - xo_emit("1:{qt:t1/%*d} 2:{qt:t2/test%-*u} 3:{qt:t3/%10sx} 4:{qt:t4/x%-*.*s}\n", + xo_emit("1:{qt:t1/%*d} 2:{qt:t2/test%-*u} " + "3:{qt:t3/%10sx} 4:{qt:t4/x%-*.*s}\n", 6, 1000, 8, 5000, "ten-long", 10, 10, "test"); xo_emit("{E:this is an error}\n"); xo_emit("{E:/%s more error%s}\n", "two", "s" ); diff --git a/contrib/libxo/tests/core/test_05.c b/contrib/libxo/tests/core/test_05.c index a883a8889906..07de037edec6 100644 --- a/contrib/libxo/tests/core/test_05.c +++ b/contrib/libxo/tests/core/test_05.c @@ -11,8 +11,16 @@ #include #include #include +#include #include "xo.h" +#include "xo_config.h" + +#ifdef LIBXO_WCWIDTH +#include "xo_wcwidth.h" +#else /* LIBXO_WCWIDTH */ +#define xo_wcwidth(_x) wcwidth(_x) +#endif /* LIBXO_WCWIDTH */ xo_info_t info[] = { { "employee", "object", "Employee data" }, @@ -43,7 +51,7 @@ main (int argc, char **argv) "෴ණ්ණ෴෴ණ්ණ෴෴ණ්ණ෴෴෴", 110, 20 }, { NULL, NULL } }, *ep = employees; - int rc; + int rc, i; argc = xo_parse_args(argc, argv); if (argc < 0) @@ -52,8 +60,40 @@ main (int argc, char **argv) xo_set_info(NULL, info, info_count); xo_set_flags(NULL, XOF_COLUMNS); + xo_open_container("indian-languages"); + + xo_emit("{T:Sample text}\n"); + xo_emit("This sample text was taken from the Punjabi Wikipedia " + "article on Lahore and transliterated into the Latin script.\n"); + + xo_emit("{T:Gurmukhi:}\n"); + xo_emit("{:gurmukhi}\n", + "ਲਹੌਰ ਪਾਕਿਸਤਾਨੀ ਪੰਜਾਬ ਦੀ ਰਾਜਧਾਨੀ ਹੈ । ਲੋਕ ਗਿਣਤੀ ਦੇ ਨਾਲ ਕਰਾਚੀ ਤੋਂ ਬਾਅਦ ਲਹੌਰ ਦੂਜਾ ਸਭ ਤੋਂ ਵੱਡਾ ਸ਼ਹਿਰ ਹੈ । ਲਹੌਰ ਪਾਕਿਸਤਾਨ ਦਾ ਸਿਆਸੀ, ਰਹਤਲੀ ਤੇ ਪੜà©à¨¹à¨¾à¨ˆ ਦਾ ਗੜà©à¨¹ ਹੈ ਅਤੇ ਇਸ ਲਈ ਇਹਨੂੰ ਪਾਕਿਸਤਾਨ ਦਾ ਦਿਲ ਵੀ ਕਿਹਾ ਜਾਂਦਾ ਹੈ । ਲਹੌਰ ਦਰਿਆ-à¨-ਰਾਵੀ ਦੇ ਕੰਢੇ ਤੇ ਵਸਦਾ ਹੈ ਤੇ ਇਸਦੀ ਲੋਕ ਗਿਣਤੀ ਇੱਕ ਕਰੋੜ ਦੇ ਨੇੜੇ ਹੈ ।"); + + + xo_emit("{T:Shahmukhi:}\n"); + xo_emit("{:shahmukhi}\n", + "Ù„Ûور پاکستانی پنجاب دا دارالحکومت اے۔ لوک گنتی دے نال کراچی توں بعد Ù„Ûور دوجا سبھ توں وڈا Ø´Ûر اے۔ Ù„Ûور پاکستان دا سیاسی، رÛتلی تے پڑھائی دا Ú¯Ú‘Ú¾ اے تے اس لئی ایھنوں پاکستان دا دل ÙˆÛŒ کیھا جاندا اے۔ Ù„Ûور دریاۓ راوی دے Ú©Ù†ÚˆÚ¾Û’ تے وسدا اے اسدی لوک گنتی اک کروڑ دے نیڑے اے Û”"); + + xo_emit("{T:Transliteration}:\n"); + xo_emit("{:tranliteration}\n", + "lahor pÄkistÄn panjÄb dÄ dÄrul hakÅ«mat Ä“. lÅk giṇtÄ« dÄ“ nÄḷ karÄcÄ« tÅá·ˆ bÄad lahor dÅ«jÄ sab tÅá·ˆ vaá¸á¸Ä shahr Ä“. lahor pÄkistÄn dÄ siÄsÄ«, rahtalÄ« tÄ“ paá¹›Ä̀ī dÄ gÄÌá¹› Ä“ tÄ“ is laÄ« ihnÅ«á·ˆ pÄkistÄn dÄ dil vÄ« kehÄ jÄndÄ Ä“. lahor dariÄÄ“ rÄvÄ« dÄ“ kaná¸Ä“ tÄ“ vasdÄ Ä“. isdÄ« lÅk giṇtÄ« ikk karÅá¹› dÄ“ nēṛē Ä“."); + + xo_close_container("indian-languages"); + xo_open_container("employees"); + wchar_t wc[] = { L'à·´', L'ණ', L'à·Š', L'ණ', 0x17D2, L'à·´', 0 }; + for (i = 0; wc[i]; i++) + xo_emit("Wide char: {lq:wc/%lc - %#lx - %d}\n", + wc[i], (unsigned long) wc[i], xo_wcwidth(wc[i])); + + wchar_t msg[] = { L'1', 0x034f, L'2', 0x20dd, 0 }; + for (i = 0; msg[i]; i++) + xo_emit("Wide char: {lq:wc/%lc - %#lx - %d}\n", + msg[i], (unsigned long) msg[i], xo_wcwidth((int) msg[i])); + xo_emit("Cool: [{:fancy/%ls}]\n", msg); + xo_emit("Οá½Ï‡á½¶ ταá½Ï„á½° παÏίσταταί μοι {:v1/%s}, {:v2/%s}\n", "γιγνώσκειν", "ὦ ἄνδÏες ᾿Αθηναῖοι"); @@ -65,10 +105,15 @@ main (int argc, char **argv) /* Okay, Sinhala is uber cool ... */ rc = xo_emit("[{:sinhala}]\n", "෴ණ්ණ෴"); xo_emit("{Twc:Width}{:width/%d}\n", rc); + rc = xo_emit("[{:sinhala}]\n", "à·´"); xo_emit("{Twc:Width}{:width/%d}\n", rc); + rc = xo_emit("[{:sinhala/%-4..4s/%s}]\n", "෴ණ්ණ෴෴ණ්ණ෴"); + xo_emit("{Twc:Width}{:width/%d}\n", rc); + xo_emit("[{:not-sinhala/%-4..4s/%s}]\n", "123456"); + rc = xo_emit("[{:tag/%s}]\n", "ර්â€à¶"); xo_emit("{Twc:Width}{:width/%d}\n", rc); @@ -80,7 +125,7 @@ main (int argc, char **argv) xo_open_instance("employee"); xo_emit("{[:-25}{:first-name/%s} ({:nic-name/\"%s\"}){]:}" "{:last-name/%-14..14s/%s}" - "{:department/%8u/%u}{:percent-time/%8u/%u}\n", + "{:department/%8u}{:percent-time/%8u}\n", ep->e_first, ep->e_nic, ep->e_last, ep->e_dept, ep->e_percent); if (ep->e_percent > 50) { xo_attr("full-time", "%s", "honest & for true"); diff --git a/contrib/libxo/tests/core/test_08.c b/contrib/libxo/tests/core/test_08.c index eb3776dcfbbb..7e19ebeb4c10 100644 --- a/contrib/libxo/tests/core/test_08.c +++ b/contrib/libxo/tests/core/test_08.c @@ -85,7 +85,7 @@ main (int argc, char **argv) xo_emit("\n\n"); - xo_open_container("data"); + xo_open_container("data2"); xo_open_container("contents"); xo_emit("{T:Item/%-10s}{T:Count/%12s}\n"); @@ -97,11 +97,11 @@ main (int argc, char **argv) ip->i_title, ip->i_count); } - xo_close_container("data"); + xo_close_container("data2"); xo_emit("\n\n"); - xo_open_container("data"); + xo_open_container("data3"); xo_open_marker("m1"); xo_open_container("contents"); @@ -114,15 +114,15 @@ main (int argc, char **argv) ip->i_title, ip->i_count); } - xo_close_container("data"); /* Should be a noop */ + xo_close_container("data3"); /* Should be a noop */ xo_emit("{:test}", "one"); xo_close_marker("m1"); - xo_close_container("data"); /* Should be a noop */ + xo_close_container("data3"); /* Should be a noop */ xo_emit("\n\n"); - xo_open_container("data"); + xo_open_container("data4"); xo_open_marker("m1"); xo_open_container("contents"); @@ -138,13 +138,13 @@ main (int argc, char **argv) for (i = 0; i < 3; i++) { xo_open_instance("sub"); xo_emit("{Lwc:/Name}{:name/%d} + 1 = {:next/%d}\n", i, i + 1); - xo_close_container("data"); + xo_close_container("data4"); } xo_close_marker("m2"); xo_emit("{Lwc:/Last}{:last/%d}\n", i); } - xo_close_container("data"); /* Should be a noop */ + xo_close_container("data4"); /* Should be a noop */ xo_emit("{:test}", "one"); xo_emit("\n\n"); diff --git a/contrib/libxo/tests/core/test_11.c b/contrib/libxo/tests/core/test_11.c new file mode 100644 index 000000000000..60851dfffa5a --- /dev/null +++ b/contrib/libxo/tests/core/test_11.c @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2014, Juniper Networks, Inc. + * All rights reserved. + * This SOFTWARE is licensed under the LICENSE provided in the + * ../Copyright file. By downloading, installing, copying, or otherwise + * using the SOFTWARE, you agree to be bound by the terms of that + * LICENSE. + * Phil Shafer, July 2014 + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "xo.h" + +void +test_syslog_open (void) +{ + printf("syslog open\n"); +} + +void +test_syslog_close (void) +{ + printf("syslog close\n"); +} + +void +test_syslog_send (const char *full_msg, const char *v0_hdr, + const char *text_only) +{ + printf("{{%s}}\n{{%s}}\n{{%s}}\n\n", full_msg, v0_hdr, text_only); +} + +int +main (int argc, char **argv) +{ + int unit_test = 1; + int fire = 0; + const char *tzone = "EST"; + + argc = xo_parse_args(argc, argv); + if (argc < 0) + return 1; + + for (argc = 1; argv[argc]; argc++) { + if (strcmp(argv[argc], "full") == 0) + unit_test = 0; + else if (strcmp(argv[argc], "fire") == 0) + fire = 1; + else if (strcmp(argv[argc], "tz") == 0) + tzone = argv[++argc]; + } + + setenv("TZ", tzone, 1); + tzset(); + + if (!fire) { + xo_set_syslog_handler(test_syslog_open, test_syslog_send, + test_syslog_close); + } + + if (unit_test) { + xo_set_unit_test_mode(1); + xo_open_log("test-program", LOG_PERROR, 0); + } + + xo_set_version("3.1.4"); + xo_set_syslog_enterprise_id(42); /* SunOs */ + + xo_open_container_h(NULL, "top"); + + xo_syslog(LOG_INFO | LOG_KERN, "animal-status", + "The {:animal} is {:state}", "snake", "loose"); + xo_syslog(LOG_INFO | LOG_MAIL, "animal-consumed", + "My {:animal} ate your {:pet}", "snake", "hamster"); + xo_syslog(LOG_NOTICE | LOG_DAEMON, "animal-talk", + "{:count/%d} {:animal} said {:quote}", 1, "owl", "\"e=m\\c[2]\""); + + /* + <165>1 2003-10-11T22:14:15.003Z mymachine.example.com + evntslog - ID47 [exampleSDID@32473 iut="3" eventSource= + "Application" eventID="1011"] BOMAn application + event log entry... + + This example is modeled after Example 1. However, this time it + contains STRUCTURED-DATA, a single element with the value + "[exampleSDID@32473 iut="3" eventSource="Application" + eventID="1011"]". The MSG itself is "An application event log + entry..." The BOM at the beginning of MSG indicates UTF-8 encoding. + */ + + xo_set_syslog_enterprise_id(32473); + xo_syslog(LOG_LOCAL4 | LOG_NOTICE, "ID47", + "{e:iut/%u}An {:event-source} {:event-id/%u} log entry", + 3, "application", 1011); + + xo_close_container_h(NULL, "top"); + + xo_finish(); + + return 0; +} diff --git a/contrib/libxo/tests/gettext/Makefile.am b/contrib/libxo/tests/gettext/Makefile.am new file mode 100644 index 000000000000..fb70142465d5 --- /dev/null +++ b/contrib/libxo/tests/gettext/Makefile.am @@ -0,0 +1,224 @@ +# +# $Id$ +# +# Copyright 2014, Juniper Networks, Inc. +# All rights reserved. +# This SOFTWARE is licensed under the LICENSE provided in the +# ../Copyright file. By downloading, installing, copying, or otherwise +# using the SOFTWARE, you agree to be bound by the terms of that +# LICENSE. + +AM_CFLAGS = \ + -I${top_srcdir} \ + -I${top_srcdir}/libxo \ + ${GETTEXT_CFLAGS} + +# Ick: maintained by hand! +TEST_CASES = \ +gt_01.c + +gt_01_test_SOURCES = gt_01.c + +# TEST_CASES := $(shell cd ${srcdir} ; echo *.c ) + +noinst_PROGRAMS = ${TEST_CASES:.c=.test} + +LDADD = \ + ${top_builddir}/libxo/libxo.la + +if HAVE_HUMANIZE_NUMBER +LDADD += -lutil +endif + +EXTRA_DIST = \ + ${TEST_CASES} \ + ${addprefix saved/, ${TEST_CASES:.c=.T.err}} \ + ${addprefix saved/, ${TEST_CASES:.c=.T.out}} \ + ${addprefix saved/, ${TEST_CASES:.c=.XP.err}} \ + ${addprefix saved/, ${TEST_CASES:.c=.XP.out}} \ + ${addprefix saved/, ${TEST_CASES:.c=.JP.err}} \ + ${addprefix saved/, ${TEST_CASES:.c=.JP.out}} \ + ${addprefix saved/, ${TEST_CASES:.c=.HP.err}} \ + ${addprefix saved/, ${TEST_CASES:.c=.HP.out}} \ + ${addprefix saved/, ${TEST_CASES:.c=.X.err}} \ + ${addprefix saved/, ${TEST_CASES:.c=.X.out}} \ + ${addprefix saved/, ${TEST_CASES:.c=.J.err}} \ + ${addprefix saved/, ${TEST_CASES:.c=.J.out}} \ + ${addprefix saved/, ${TEST_CASES:.c=.H.err}} \ + ${addprefix saved/, ${TEST_CASES:.c=.H.out}} \ + ${addprefix saved/, ${TEST_CASES:.c=.HIPx.err}} \ + ${addprefix saved/, ${TEST_CASES:.c=.HIPx.out}} + +POT_FILES = \ + gt_01.pot \ + ldns.pot \ + strerror.pot + +PO_FILES = \ + po/pig_latin/gt_01.po \ + po/pig_latin/ldns.po \ + po/pig_latin/strerror.po + +EXTRA_DIST += ${POT_FILES} ${PO_FILES} + +S2O = | ${SED} '1,/@@/d' + +all: + +valgrind: + @echo '## Running the regression tests under Valgrind' + ${MAKE} CHECKER='valgrind -q' tests + +#TEST_TRACE = set -x ; + +TEST_ONE = \ + LIBXO_OPTIONS=:W$$fmt \ + ${CHECKER} ./$$base.test ${TEST_OPTS} \ + > out/$$base.$$fmt.out 2> out/$$base.$$fmt.err ; \ + ${DIFF} -Nu ${srcdir}/saved/$$base.$$fmt.out out/$$base.$$fmt.out ${S2O} ; \ + ${DIFF} -Nu ${srcdir}/saved/$$base.$$fmt.err out/$$base.$$fmt.err ${S2O} + +TEST_FORMATS = T XP JP HP X J H HIPx + +test tests: ${bin_PROGRAMS} build-mo-files + @${MKDIR} -p out + -@ ${TEST_TRACE} (for test in ${TEST_CASES} ; do \ + base=`${BASENAME} $$test .c` ; \ + (for fmt in ${TEST_FORMATS}; do \ + echo "... $$test ... $$fmt ..."; \ + ${TEST_ONE}; \ + true; \ + done) \ + done) + +one: + -@(test=${TEST_CASE}; data=${TEST_DATA}; ${TEST_ONE} ; true) + +accept: + -@(for test in ${TEST_CASES} ; do \ + base=`${BASENAME} $$test .c` ; \ + (for fmt in ${TEST_FORMATS}; do \ + echo "... $$test ... $$fmt ..."; \ + ${CP} out/$$base.$$fmt.out ${srcdir}/saved/$$base.$$fmt.out ; \ + ${CP} out/$$base.$$fmt.err ${srcdir}/saved/$$base.$$fmt.err ; \ + done) \ + done) + +.c.test: + $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -o $@ $< + +CLEANFILES = ${TEST_CASES:.c=.test} +CLEANDIRS = out + +clean-local: + rm -rf ${CLEANDIRS} + +XGETTEXT = ${GETTEXT_BINDIR}/xgettext +MSGFMT = ${GETTEXT_BINDIR}/msgfmt -v +MSGMERGE = ${GETTEXT_BINDIR}/msgmerge +ECHO = echo +DB=set -x; +XOMSGMERGE = ${MSGMERGE} --no-wrap +XODIFF = ${DIFF} -bu + +LANGUAGES = \ + es \ + fr \ + pig_latin + +# ldns is fake; used only for a gettext domain +FAKE_FILES = ldns strerror +MO_BASE_FILES = ${TEST_CASES:.c=} ${FAKE_FILES} + +build-pot-files: + for file in ${TEST_CASES} ; do set -x ;\ + base=`${BASENAME} $$file .c` ; \ + ${XGETTEXT} --default-domain=$$base \ + --directory=${srcdir} --no-wrap \ + --add-comments --keyword=xo_emit --keyword=xo_emit_h \ + --keyword=xo_emit_warn \ + -C -E -n --foreign-user \ + -o $$base.pot.new $$base.c ; \ + done + +accept-pot-files: + for base in ${MO_BASE_FILES} ; do set -x ;\ + ${CP} $$base.pot.new ${srcdir}/$$base.pot ; \ + done + +merge-po-files: + for base in ${MO_BASE_FILES} ; do set -x ;\ + for lang in ${LANGUAGES} ; do \ + if [ -f po/$$lang/$$base.po ]; then \ + ${ECHO} "merging $$base.pot into po/$$lang/$$base.po ..." ; \ + if ${XOMSGMERGE} po/$$lang/$$base.po \ + ${srcdir}/$$base.pot \ + -o po/$$lang/$$base.new.po; then \ + ${MV} po/$$lang/$$base.po \ + po/$$lang/$$base.po.old ; \ + ${MV} po/$$lang/$$base.new.po \ + po/$$lang/$$base.po ; \ + else \ + echo "error: msgmerge for $$base failed"; \ + fi ; \ + elif [ -f ${srcdir}/po/$$lang/$$base.po ]; then \ + ${ECHO} "merging (srcdir) $$base.pot into po/$$lang/$$base.po ..." ; \ + if ${XOMSGMERGE} ${srcdir}/po/$$lang/$$base.po \ + ${srcdir}/$$base.pot \ + -o po/$$lang/$$base.new.po; then \ + ${MV} po/$$lang/$$base.po \ + po/$$lang/$$base.po.old ; \ + ${MV} po/$$lang/$$base.new.po \ + po/$$lang/$$base.po ; \ + else \ + echo "error: msgmerge for $$base failed"; \ + fi ; \ + fi ; \ + done ; \ + done + +accept-po-files: + @(for base in ${MO_BASE_FILES} ; do \ + for lang in ${LANGUAGES} ; do \ + if [ -f po/$$lang/$$base.po ]; then \ + ${MKDIR} -p ${srcdir}/po/$$lang ; \ + (${DB} ${CP} po/$$lang/$$base.po ${srcdir}/po/$$lang/$$base.po ); \ + fi ; \ + done ; \ + done) + +new-po-file: + @(for base in ${MO_BASE_FILES} ; do set -x ;\ + for lang in ${LANGUAGES} ; do \ + if [ ! -f po/$$lang/$$base.po ]; then \ + ${MKDIR} -p po/$$lang ; \ + (${DB} ${CP} $$base.pot po/$$lang/$$base.po ); \ + fi ; \ + done ; \ + done) + +diff: + @(for base in ${MO_BASE_FILES} ; do \ + if [ -f $$base.pot.new ] ; then \ + ${XODIFF} ${srcdir}/$$base.pot $$base.pot.new ; \ + fi ; \ + for lang in ${LANGUAGES} ; do \ + if [ -f po/$$lang/$$base.po ] ; then \ + ${XODIFF} ${srcdir}/po/$$lang/$$base.po po/$$lang/$$base.po ; \ + fi ; \ + done ; \ + done) + +mo build-mo-files: + @(for base in ${MO_BASE_FILES} ; do \ + for lang in ${LANGUAGES} ; do \ + ${MKDIR} -p po/$$lang/LC_MESSAGES ; \ + if [ -f po/$$lang/$$base.po ] ; then \ + (${DB} ${MSGFMT} -o po/$$lang/LC_MESSAGES/$$base.mo \ + po/$$lang/$$base.po ); \ + elif [ -f ${srcdir}/po/$$lang/$$base.po ]; then \ + (${DB} ${MSGFMT} -o po/$$lang/LC_MESSAGES/$$base.mo \ + ${srcdir}/po/$$lang/$$base.po ;) \ + fi ; \ + done ; \ + done) diff --git a/contrib/libxo/tests/gettext/gt_01.c b/contrib/libxo/tests/gettext/gt_01.c new file mode 100644 index 000000000000..a0200c2926c4 --- /dev/null +++ b/contrib/libxo/tests/gettext/gt_01.c @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2015, Juniper Networks, Inc. + * All rights reserved. + * This SOFTWARE is licensed under the LICENSE provided in the + * ../Copyright file. By downloading, installing, copying, or otherwise + * using the SOFTWARE, you agree to be bound by the terms of that + * LICENSE. + * Phil Shafer, June 2015 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "xo.h" + +int +main (int argc, char **argv) +{ + static char domainname[] = "gt_01"; + char path[MAXPATHLEN]; + const char *tzone = "EST"; + const char *lang = "pig_latin"; + + argc = xo_parse_args(argc, argv); + if (argc < 0) + return 1; + + for (argc = 1; argv[argc]; argc++) { + if (strcmp(argv[argc], "tz") == 0) + tzone = argv[++argc]; + else if (strcmp(argv[argc], "lang") == 0) + lang = argv[++argc]; + else if (strcmp(argv[argc], "po") == 0) + strlcpy(path, argv[++argc], sizeof(path)); + } + + setenv("LANG", lang, 1); + setenv("TZ", tzone, 1); + + if (path[0] == 0) { + getcwd(path, sizeof(path)); + strlcat(path, "/po", sizeof(path)); + } + + setlocale(LC_ALL, ""); + bindtextdomain(domainname, path); + bindtextdomain("ldns", path); + bindtextdomain("strerror", path); + textdomain(domainname); + tzset(); + + xo_open_container("top"); + + xo_emit("{G:}Your {qg:adjective} {g:noun} is {g:verb} {qg:owner} {g:target}\n", + "flaming", "sword", "burning", "my", "couch"); + + xo_emit("{G:}The {qg:adjective} {g:noun} was {g:verb} {qg:owner} {g:target}\n", + "flaming", "sword", "burning", "my", "couch"); + + + int i; + for (i = 0; i < 5; i++) + xo_emit("{lw:bytes/%d}{Ngp:byte,bytes}\n", i); + + xo_emit("{G:}{L:total} {:total/%u}\n", 1234); + + xo_emit("{G:ldns}Received {:received/%zu} {Ngp:byte,bytes} " + "from {:from/%s}#{:port/%d} in {:time/%d} ms\n", + (size_t) 1234, "foop", 4321, 32); + + xo_emit("{G:}Received {:received/%zu} {Ngp:byte,bytes} " + "from {:from/%s}#{:port/%d} in {:time/%d} ms\n", + (size_t) 1234, "foop", 4321, 32); + + xo_emit("{G:/%s}Received {:received/%zu} {Ngp:byte,bytes} " + "from {:from/%s}#{:port/%d} in {:time/%d} ms\n", + "ldns", (size_t) 1234, "foop", 4321, 32); + + struct timeval tv; + tv.tv_sec = 1435085229; + tv.tv_usec = 123456; + + struct tm tm; + (void) gmtime_r(&tv.tv_sec, &tm); + + char date[64]; + strftime(date, sizeof(date), "%+", &tm); + + xo_emit("{G:}Only {:marzlevanes/%d} {Ngp:marzlevane,marzlevanes} " + "are functioning correctly\n", 3); + + xo_emit("{G:}Version {:version} {:date}\n", "1.2.3", date); + + errno = EACCES; + xo_emit_warn("{G:}Unable to {g:verb/objectulate} forward velociping"); + xo_emit_warn("{G:}{g:style/automatic} synchronization of {g:type/cardinal} " + "{g:target/grammeters} failed"); + xo_emit("{G:}{Lwcg:hydrocoptic marzlevanes}{:marzlevanes/%d}\n", 6); + + xo_emit("{G:}{Lwcg:Windings}{g:windings}\n", "lotus-o-delta"); + + xo_close_container("top"); + xo_finish(); + + return 0; +} diff --git a/contrib/libxo/tests/gettext/gt_01.pot b/contrib/libxo/tests/gettext/gt_01.pot new file mode 100644 index 000000000000..1d09223ec786 --- /dev/null +++ b/contrib/libxo/tests/gettext/gt_01.pot @@ -0,0 +1,105 @@ +# SOME DESCRIPTIVE TITLE. +# This file is put in the public domain. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2015-07-01 16:15-0400\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: 8bit\n" + +#: gt_01.c:42 +#, c-format +msgid "{:bytes}{N:byte,bytes}\n" +msgstr "" + +#: gt_01.c:44 +#, c-format +msgid "{L:total} {:total}\n" +msgstr "" + +#: gt_01.c:60 +#, c-format +msgid "Only {:marzlevanes} {N:marzlevane,marzlevanes} are functioning correctly\n" +msgstr "" + +#: gt_01.c:63 +msgid "Version {:version} {:date}\n" +msgstr "" + +#: gt_01.c:66 +msgid "Unable to {:verb} forward velociping" +msgstr "" + +#: gt_01.c:67 +msgid "{:style} synchronization of {:type} {:target} failed" +msgstr "" + +#: gt_01.c:69 +#, c-format +msgid "{L:hydrocoptic marzlevanes}{:marzlevanes}\n" +msgstr "" + +#: gt_01.c:71 +msgid "{L:Windings}{:windings}\n" +msgstr "" + +msgid "byte" +msgid_plural "bytes" +msgstr[0] "" +msgstr[1] "" + +msgid "marzlevane" +msgid_plural "marzlevanes" +msgstr[0] "" +msgstr[1] "" + +msgid "lotus-o-delta" +msgstr "" + +msgid "cardinal" +msgstr "" + +msgid "automatic" +msgstr "" + +msgid "grammeters" +msgstr "" + +msgid "objectulate" +msgstr "" + +msgid "hydrocoptic marzlevanes" +msgstr "" + +msgid "Windings" +msgstr "" + +msgid "Your {:adjective} {:noun} is {:verb} {:owner} {:target}\n" +msgstr "" + +msgid "The {:adjective} {:noun} was {:verb} {:owner} {:target}\n" +msgstr "" + +msgid "flaming" +msgstr "" + +msgid "sword" +msgstr "" + +msgid "burning" +msgstr "" + +msgid "my" +msgstr "" + +msgid "couch" +msgstr "" diff --git a/contrib/libxo/tests/gettext/ldns.pot b/contrib/libxo/tests/gettext/ldns.pot new file mode 100644 index 000000000000..6e3df201004a --- /dev/null +++ b/contrib/libxo/tests/gettext/ldns.pot @@ -0,0 +1,28 @@ +# SOME DESCRIPTIVE TITLE. +# This file is put in the public domain. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2015-07-01 16:15-0400\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: 8bit\n" + +#: gt_01.c:46 +#, c-format +msgid "Received {:received} {N:byte,bytes} from {:from}#{:port} in {:time} ms\n" +msgstr "" + +msgid "byte" +msgid_plural "bytes" +msgstr[0] "" +msgstr[1] "" + diff --git a/contrib/libxo/tests/gettext/po/pig_latin/gt_01.po b/contrib/libxo/tests/gettext/po/pig_latin/gt_01.po new file mode 100644 index 000000000000..269bad54b2a4 --- /dev/null +++ b/contrib/libxo/tests/gettext/po/pig_latin/gt_01.po @@ -0,0 +1,109 @@ +# SOME DESCRIPTIVE TITLE. +# This file is put in the public domain. +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: libxo unit test\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2015-07-01 16:15-0400\n" +"PO-Revision-Date: 2015-07-09 13:43-0400\n" +"Last-Translator: P.S. \n" +"Language-Team: Self-inflicted \n" +"Language: teo\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: Poedit 1.8.1\n" +"Plural-Forms: nplurals=3; plural=(n==0) ? 0 : (n==1) ? 1 : 2;\n" +"X-Poedit-SourceCharset: iso-8859-1\n" + +#: gt_01.c:42 +#, c-format +msgid "{:bytes}{N:byte,bytes}\n" +msgstr "{:bytes}{N:ytebay,ytesbay}\n" + +#: gt_01.c:44 +#, c-format +msgid "{L:total} {:total}\n" +msgstr "{L:otaltay} {:total}\n" + +#: gt_01.c:60 +#, c-format +msgid "Only {:marzlevanes} {N:marzlevane,marzlevanes} are functioning correctly\n" +msgstr "Onlyay {:marzlevanes} {N:marzlevane,marzlevanes} areyay unctioningfay orrectlycay\n" + +#: gt_01.c:63 +msgid "Version {:version} {:date}\n" +msgstr "Ersionvay {:date} {:version}\n" + +#: gt_01.c:66 +msgid "Unable to {:verb} forward velociping" +msgstr "Nableuay otay {:verb} orwardfay elocipingvay" + +#: gt_01.c:67 +msgid "{:style} synchronization of {:type} {:target} failed" +msgstr "{:style} ynchronizationsay ofyay {:type} {:target} ailedfay" + +#: gt_01.c:69 +#, c-format +msgid "{L:hydrocoptic marzlevanes}{:marzlevanes}\n" +msgstr "{L:ydrocoptichay arzlevanesmay}{:marzlevanes}\n" + +#: gt_01.c:71 +msgid "{L:Windings}{:windings}\n" +msgstr "Dude, {L:Windings}{:windings}\n" + +msgid "byte" +msgid_plural "bytes" +msgstr[0] "yebay" +msgstr[1] "yesbay" +msgstr[2] "yezbay" + +msgid "marzlevane" +msgid_plural "marzlevanes" +msgstr[0] "arzlevanemay" +msgstr[1] "arzlevanesmay" +msgstr[2] "arzlevanezmay" + +msgid "lotus-o-delta" +msgstr "otuslay-oyay-eltayay" + +msgid "cardinal" +msgstr "ardinalyay" + +msgid "automatic" +msgstr "automaticyay" + +msgid "grammeters" +msgstr "ammetersgray" + +msgid "objectulate" +msgstr "ectulatobjay" + +msgid "hydrocoptic marzlevanes" +msgstr "ydrocoptichay arzlevanesmay" + +msgid "Windings" +msgstr "Indingsway" + +msgid "Your {:adjective} {:noun} is {:verb} {:owner} {:target}\n" +msgstr "Ouryay {:noun} {:adjective} isyay {:owner}{:target} bubbly-bubbly {:verb}\n" + +msgid "The {:adjective} {:noun} was {:verb} {:owner} {:target}\n" +msgstr "Ethay {:noun} asway '{:owner}{:adjective}{:target}' {:verb}\n" + +msgid "flaming" +msgstr "amingflay" + +msgid "sword" +msgstr "ordsway" + +msgid "burning" +msgstr "urningbay" + +msgid "my" +msgstr "ymay" + +msgid "couch" +msgstr "ouchcay" diff --git a/contrib/libxo/tests/gettext/po/pig_latin/ldns.po b/contrib/libxo/tests/gettext/po/pig_latin/ldns.po new file mode 100644 index 000000000000..83d5aeee6ccf --- /dev/null +++ b/contrib/libxo/tests/gettext/po/pig_latin/ldns.po @@ -0,0 +1,30 @@ +# SOME DESCRIPTIVE TITLE. +# This file is put in the public domain. +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: libxo unit test\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2015-07-01 16:15-0400\n" +"PO-Revision-Date: 2015-07-01 18:47-0500\n" +"Last-Translator: P.S. \n" +"Language-Team: Self-inflicted \n" +"Language: teo\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: Poedit 1.8.1\n" +"Plural-Forms: nplurals=3; plural=(n==0) ? 0 : (n==1) ? 1 : 2;\n" +"X-Poedit-SourceCharset: iso-8859-1\n" + +#: gt_01.c:46 +#, c-format +msgid "Received {:received} {N:byte,bytes} from {:from}#{:port} in {:time} ms\n" +msgstr "Eceivedray {:received} {N:byte,bytes} omfray {:from}#{:port} inyay {:time} msyay\n" + +msgid "byte" +msgid_plural "bytes" +msgstr[0] "ldb0" +msgstr[1] "ldb1" +msgstr[2] "ldb2" diff --git a/contrib/libxo/tests/gettext/po/pig_latin/strerror.po b/contrib/libxo/tests/gettext/po/pig_latin/strerror.po new file mode 100644 index 000000000000..8b41c0a85db7 --- /dev/null +++ b/contrib/libxo/tests/gettext/po/pig_latin/strerror.po @@ -0,0 +1,459 @@ +# +# Copyright (c) 1982, 1985, 1993 +# The Regents of the University of California. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# List of system errors ala strerror() and sys_errlist +# Phil Shafer , 2015. +# +msgid "" +msgstr "" +"Project-Id-Version: libxo test\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2015-07-01 16:15-0400\n" +"PO-Revision-Date: 2015-07-02 00:37-0500\n" +"Language: teo\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=3; plural=(n==0) ? 0 : (n==1) ? 1 : 2;\n" +"Last-Translator: P.S. \n" +"Language-Team: self inflicted \n" +"X-Generator: Poedit 1.8.1\n" +"X-Poedit-SourceCharset: iso-8859-1\n" + +# 0 - ENOERROR +msgid "No error: 0" +msgstr "Onyay erroryay" + +# 1 - EPERM +msgid "Operation not permitted" +msgstr "Operationyay otnay ermittedpay" + +# 2 - ENOENT +msgid "No such file or directory" +msgstr "Onay uchsay ilefay oryay irectoryday" + +# 3 - ESRCH +msgid "No such process" +msgstr "Onay uchsay ocesspray" + +# 4 - EINTR +msgid "Interrupted system call" +msgstr "Interruptedyay ystemsay allcay" + +# 5 - EIO +msgid "Input/output error" +msgstr "Input/outputyay erroryay" + +# 6 - ENXIO +msgid "Device not configured" +msgstr "Eviceday otnay onfiguredcay" + +# 7 - E2BIG +msgid "Argument list too long" +msgstr "Argumentyay istlay ootay onglay" + +# 8 - ENOEXEC +msgid "Exec format error" +msgstr "Execway ormatfay errorway" + +# 9 - EBADF +msgid "Bad file descriptor" +msgstr "Adbay ilefay escriptorday" + +# 10 - ECHILD +msgid "No child processes" +msgstr "Onay ildchay ocessespray" + +# 11 - EDEADLK +msgid "Resource deadlock avoided" +msgstr "Esourceray eadlockday avoidedway" + +# 12 - ENOMEM +msgid "Cannot allocate memory" +msgstr "Annotcay allocateway emorymay" + +# 13 - EACCES +msgid "Permission denied" +msgstr "Ermissionpay eniedday" + +# 14 - EFAULT +msgid "Bad address" +msgstr "Adbay addressway" + +# 15 - ENOTBLK +msgid "Block device required" +msgstr "Ockblay eviceday equiredray" + +# 16 - EBUSY +msgid "Device busy" +msgstr "Eviceday usybay" + +# 17 - EEXIST +msgid "File exists" +msgstr "Ilefay existsway" + +# 18 - EXDEV +msgid "Cross-device link" +msgstr "Osscray-eviceday inklay" + +# 19 - ENODEV +msgid "Operation not supported by device" +msgstr "Operationway otnay upportedsay ybay eviceday" + +# 20 - ENOTDIR +msgid "Not a directory" +msgstr "Otnay away irectoryday" + +# 21 - EISDIR +msgid "Is a directory" +msgstr "Isway away irectoryday" + +# 22 - EINVAL +msgid "Invalid argument" +msgstr "Invalidway argumentway" + +# 23 - ENFILE +msgid "Too many open files in system" +msgstr "Ootay anymay openway ilesfay inway ystemsay" + +# 24 - EMFILE +msgid "Too many open files" +msgstr "Ootay anymay openway ilesfay" + +# 25 - ENOTTY +msgid "Inappropriate ioctl for device" +msgstr "Inappropriateway ioctlway orfay eviceday" + +# 26 - ETXTBSY +msgid "Text file busy" +msgstr "Exttay ilefay usybay" + +# 27 - EFBIG +msgid "File too large" +msgstr "Ilefay ootay argelay" + +# 28 - ENOSPC +msgid "No space left on device" +msgstr "Onay acespay eftlay onway eviceday" + +# 29 - ESPIPE +msgid "Illegal seek" +msgstr "Illegalway eeksay" + +# 30 - EROFS +msgid "Read-only file system" +msgstr "Eadray-onlyway ilefay ystemsay" + +# 31 - EMLINK +msgid "Too many links" +msgstr "Ootay anymay inkslay" + +# 32 - EPIPE +msgid "Broken pipe" +msgstr "Okenbray ipepay" + +# +# math software +# +# 33 - EDOM +msgid "Numerical argument out of domain" +msgstr "Umericalnay argumentway outway ofway omainday" + +# 34 - ERANGE +msgid "Result too large" +msgstr "Esultray ootay argelay" + +# +# non-blocking and interrupt i/o +# +# 35 - EAGAIN +# 35 - EWOULDBLOCK +msgid "Resource temporarily unavailable" +msgstr "Esourceray emporarilytay unavailableway" + +# 36 - EINPROGRESS +msgid "Operation now in progress" +msgstr "Operationway ownay inway ogresspray" + +# 37 - EALREADY +msgid "Operation already in progress" +msgstr "Operationway alreadyway inway ogresspray" + +# +# ipc/network software -- argument errors +# +# 38 - ENOTSOCK +msgid "Socket operation on non-socket" +msgstr "Ocketsay operationway onway onnay-ocketsay" + +# 39 - EDESTADDRREQ +msgid "Destination address required" +msgstr "Estinationday addressway equiredray" + +# 40 - EMSGSIZE +msgid "Message too long" +msgstr "Essagemay ootay onglay" + +# 41 - EPROTOTYPE +msgid "Protocol wrong type for socket" +msgstr "Otocolpray ongwray ypetay orfay ocketsay" + +# 42 - ENOPROTOOPT +msgid "Protocol not available" +msgstr "Otocolpray otnay availableway" + +# 43 - EPROTONOSUPPORT +msgid "Protocol not supported" +msgstr "Otocolpray otnay upportedsay" + +# 44 - ESOCKTNOSUPPORT +msgid "Socket type not supported" +msgstr "Ocketsay ypetay otnay upportedsay" + +# 45 - EOPNOTSUPP +msgid "Operation not supported" +msgstr "Operationway otnay upportedsay" + +# 46 - EPFNOSUPPORT +msgid "Protocol family not supported" +msgstr "Otocolpray amilyfay otnay upportedsay" + +# 47 - EAFNOSUPPORT +msgid "Address family not supported by protocol family" +msgstr "Addressway amilyfay otnay upportedsay ybay otocolpray amilyfay" + +# 48 - EADDRINUSE +msgid "Address already in use" +msgstr "Addressway alreadyway inway useway" + +# 49 - EADDRNOTAVAIL +msgid "Can't assign requested address" +msgstr "An'tcay assignway equestedray addressway" + +# +# ipc/network software -- operational errors +# +# 50 - ENETDOWN +msgid "Network is down" +msgstr "Etworknay isway ownday" + +# 51 - ENETUNREACH +msgid "Network is unreachable" +msgstr "Etworknay isway unreachableway" + +# 52 - ENETRESET +msgid "Network dropped connection on reset" +msgstr "Etworknay oppeddray onnectioncay onway esetray" + +# 53 - ECONNABORTED +msgid "Software caused connection abort" +msgstr "Oftwaresay ausedcay onnectioncay abortway" + +# 54 - ECONNRESET +msgid "Connection reset by peer" +msgstr "Onnectioncay esetray ybay eerpay" + +# 55 - ENOBUFS +msgid "No buffer space available" +msgstr "Onay ufferbay acespay availableway" + +# 56 - EISCONN +msgid "Socket is already connected" +msgstr "Ocketsay isway alreadyway onnectedcay" + +# 57 - ENOTCONN +msgid "Socket is not connected" +msgstr "Ocketsay isway otnay onnectedcay" + +# 58 - ESHUTDOWN +msgid "Can't send after socket shutdown" +msgstr "An'tcay endsay afterway ocketsay utdownshay" + +# 59 - ETOOMANYREFS +msgid "Too many references: can't splice" +msgstr "Ootay anymay eferencesray: an'tcay icesplay" + +# 60 - ETIMEDOUT +msgid "Operation timed out" +msgstr "Operationway imedtay outway" + +# 61 - ECONNREFUSED +msgid "Connection refused" +msgstr "Onnectioncay efusedray" + +# 62 - ELOOP +msgid "Too many levels of symbolic links" +msgstr "Ootay anymay evelslay ofway ymbolicsay inkslay" + +# 63 - ENAMETOOLONG +msgid "File name too long" +msgstr "Ilefay amenay ootay onglay" + +# +# should be rearranged +# +# 64 - EHOSTDOWN +msgid "Host is down" +msgstr "Osthay isway ownday" + +# 65 - EHOSTUNREACH +msgid "No route to host" +msgstr "Onay outeray otay osthay" + +# 66 - ENOTEMPTY +msgid "Directory not empty" +msgstr "Irectoryday otnay emptyway" + +# +# quotas & mush +# +# 67 - EPROCLIM +msgid "Too many processes" +msgstr "Ootay anymay ocessespray" + +# 68 - EUSERS +msgid "Too many users" +msgstr "Ootay anymay usersway" + +# 69 - EDQUOT +msgid "Disc quota exceeded" +msgstr "Iscday otaquay exceededway" + +# +# Network File System +# +# 70 - ESTALE +msgid "Stale NFS file handle" +msgstr "Alestay NFSAY ilefay andlehay" + +# 71 - EREMOTE +msgid "Too many levels of remote in path" +msgstr "Ootay anymay evelslay ofway emoteray inway athpay" + +# 72 - EBADRPC +msgid "RPC struct is bad" +msgstr "RPCAY uctstray isway adbay" + +# 73 - ERPCMISMATCH +msgid "RPC version wrong" +msgstr "RPCAY ersionvay ongwray" + +# 74 - EPROGUNAVAIL +msgid "RPC prog. not avail" +msgstr "RPCAY ogpray. otnay availway" + +# 75 - EPROGMISMATCH +msgid "Program version wrong" +msgstr "Ogrampray ersionvay ongwray" + +# 76 - EPROCUNAVAIL +msgid "Bad procedure for program" +msgstr "Adbay ocedurepray orfay ogrampray" + +# 77 - ENOLCK +msgid "No locks available" +msgstr "Onay ockslay availableway" + +# 78 - ENOSYS +msgid "Function not implemented" +msgstr "Unctionfay otnay implementedway" + +# 79 - EFTYPE +msgid "Inappropriate file type or format" +msgstr "Inappropriateway ilefay ypetay orway ormatfay" + +# 80 - EAUTH +msgid "Authentication error" +msgstr "Authenticationway errorway" + +# 81 - ENEEDAUTH +msgid "Need authenticator" +msgstr "Eednay authenticatorway" + +# 82 - EIDRM +msgid "Identifier removed" +msgstr "Identifierway emovedray" + +# 83 - ENOMSG +msgid "No message of desired type" +msgstr "Onay essagemay ofway esiredday ypetay" + +# 84 - EOVERFLOW +msgid "Value too large to be stored in data type" +msgstr "Aluevay ootay argelay otay ebay oredstay inway ataday ypetay" + +# 85 - ECANCELED +msgid "Operation canceled" +msgstr "Operationway anceledcay" + +# 86 - EILSEQ +msgid "Illegal byte sequence" +msgstr "Illegalway ytebay equencesay" + +# 87 - ENOATTR +msgid "Attribute not found" +msgstr "Attributeway otnay oundfay" + +# +# General +# +# 88 - EDOOFUS +msgid "Programming error" +msgstr "Ogrammingpray errorway" + +# 89 - EBADMSG +msgid "Bad message" +msgstr "Adbay essagemay" + +# 90 - EMULTIHOP +msgid "Multihop attempted" +msgstr "Ultihopmay attemptedway" + +# 91 - ENOLINK +msgid "Link has been severed" +msgstr "Inklay ashay eenbay everedsay" + +# 92 - EPROTO +msgid "Protocol error" +msgstr "Otocolpray errorway" + +# 93 - ENOTCAPABLE +msgid "Capabilities insufficient" +msgstr "Apabilitiescay insufficientway" + +# 94 - ECAPMODE +msgid "Not permitted in capability mode" +msgstr "Otnay ermittedpay inway apabilitycay odemay" + +# 95 - ENOTRECOVERABLE +msgid "State not recoverable" +msgstr "Atestay otnay ecoverableray" + +# 96 - EOWNERDEAD +msgid "Previous owner died" +msgstr "Eviouspray ownerway iedday" diff --git a/contrib/libxo/tests/gettext/saved/gt_01.H.err b/contrib/libxo/tests/gettext/saved/gt_01.H.err new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/contrib/libxo/tests/gettext/saved/gt_01.H.out b/contrib/libxo/tests/gettext/saved/gt_01.H.out new file mode 100644 index 000000000000..13606bf7fb1f --- /dev/null +++ b/contrib/libxo/tests/gettext/saved/gt_01.H.out @@ -0,0 +1 @@ +
Ouryay
ordsway
amingflay
isyay
ymay
ouchcay
bubbly-bubbly
urningbay
Ethay
ordsway
asway '
ymay
amingflay
ouchcay
'
urningbay
0
yebay
1
yesbay
2
yezbay
3
yezbay
4
yezbay
otaltay
1234
Eceivedray
1234
ldb2
omfray
foop
#
4321
inyay
32
msyay
Received
1234
yezbay
from
foop
#
4321
in
32
ms
Eceivedray
1234
ldb2
omfray
foop
#
4321
inyay
32
msyay
Onlyay
3
arzlevanezmay
areyay unctioningfay orrectlycay
Ersionvay
Tue Jun 23 18:47:09 UTC 2015
1.2.3
gt_01.test
:
Nableuay otay
ectulatobjay
orwardfay elocipingvay
:
Ermissionpay eniedday
gt_01.test
:
automaticyay
ynchronizationsay ofyay
ardinalyay
ammetersgray
ailedfay
:
Ermissionpay eniedday
ydrocoptichay arzlevanesmay
:
6
Dude,
Indingsway
:
otuslay-oyay-eltayay
\ No newline at end of file diff --git a/contrib/libxo/tests/gettext/saved/gt_01.HIPx.err b/contrib/libxo/tests/gettext/saved/gt_01.HIPx.err new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/contrib/libxo/tests/gettext/saved/gt_01.HIPx.out b/contrib/libxo/tests/gettext/saved/gt_01.HIPx.out new file mode 100644 index 000000000000..06b6a3c3858e --- /dev/null +++ b/contrib/libxo/tests/gettext/saved/gt_01.HIPx.out @@ -0,0 +1,139 @@ +
+
Ouryay
+
ordsway
+
+
amingflay
+
isyay
+
ymay
+
ouchcay
+
bubbly-bubbly
+
urningbay
+
+
+
Ethay
+
ordsway
+
asway '
+
ymay
+
amingflay
+
ouchcay
+
'
+
urningbay
+
+
+
0
+
+
yebay
+
+
+
1
+
+
yesbay
+
+
+
2
+
+
yezbay
+
+
+
3
+
+
yezbay
+
+
+
4
+
+
yezbay
+
+
+
otaltay
+
+
1234
+
+
+
Eceivedray
+
1234
+
+
ldb2
+
omfray
+
foop
+
#
+
4321
+
inyay
+
32
+
msyay
+
+
+
Received
+
1234
+
+
yezbay
+
from
+
foop
+
#
+
4321
+
in
+
32
+
ms
+
+
+
Eceivedray
+
1234
+
+
ldb2
+
omfray
+
foop
+
#
+
4321
+
inyay
+
32
+
msyay
+
+
+
Onlyay
+
3
+
+
arzlevanezmay
+
areyay unctioningfay orrectlycay
+
+
+
Ersionvay
+
Tue Jun 23 18:47:09 UTC 2015
+
+
1.2.3
+
+
+
gt_01.test
+
:
+
+
Nableuay otay
+
ectulatobjay
+
orwardfay elocipingvay
+
:
+
Ermissionpay eniedday
+
+
+
gt_01.test
+
:
+
+
automaticyay
+
ynchronizationsay ofyay
+
ardinalyay
+
+
ammetersgray
+
ailedfay
+
:
+
Ermissionpay eniedday
+
+
+
ydrocoptichay arzlevanesmay
+
:
+
+
6
+
+
+
Dude,
+
Indingsway
+
:
+
+
otuslay-oyay-eltayay
+
diff --git a/contrib/libxo/tests/gettext/saved/gt_01.HP.err b/contrib/libxo/tests/gettext/saved/gt_01.HP.err new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/contrib/libxo/tests/gettext/saved/gt_01.HP.out b/contrib/libxo/tests/gettext/saved/gt_01.HP.out new file mode 100644 index 000000000000..573d7b38ad95 --- /dev/null +++ b/contrib/libxo/tests/gettext/saved/gt_01.HP.out @@ -0,0 +1,139 @@ +
+
Ouryay
+
ordsway
+
+
amingflay
+
isyay
+
ymay
+
ouchcay
+
bubbly-bubbly
+
urningbay
+
+
+
Ethay
+
ordsway
+
asway '
+
ymay
+
amingflay
+
ouchcay
+
'
+
urningbay
+
+
+
0
+
+
yebay
+
+
+
1
+
+
yesbay
+
+
+
2
+
+
yezbay
+
+
+
3
+
+
yezbay
+
+
+
4
+
+
yezbay
+
+
+
otaltay
+
+
1234
+
+
+
Eceivedray
+
1234
+
+
ldb2
+
omfray
+
foop
+
#
+
4321
+
inyay
+
32
+
msyay
+
+
+
Received
+
1234
+
+
yezbay
+
from
+
foop
+
#
+
4321
+
in
+
32
+
ms
+
+
+
Eceivedray
+
1234
+
+
ldb2
+
omfray
+
foop
+
#
+
4321
+
inyay
+
32
+
msyay
+
+
+
Onlyay
+
3
+
+
arzlevanezmay
+
areyay unctioningfay orrectlycay
+
+
+
Ersionvay
+
Tue Jun 23 18:47:09 UTC 2015
+
+
1.2.3
+
+
+
gt_01.test
+
:
+
+
Nableuay otay
+
ectulatobjay
+
orwardfay elocipingvay
+
:
+
Ermissionpay eniedday
+
+
+
gt_01.test
+
:
+
+
automaticyay
+
ynchronizationsay ofyay
+
ardinalyay
+
+
ammetersgray
+
ailedfay
+
:
+
Ermissionpay eniedday
+
+
+
ydrocoptichay arzlevanesmay
+
:
+
+
6
+
+
+
Dude,
+
Indingsway
+
:
+
+
otuslay-oyay-eltayay
+
diff --git a/contrib/libxo/tests/gettext/saved/gt_01.J.err b/contrib/libxo/tests/gettext/saved/gt_01.J.err new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/contrib/libxo/tests/gettext/saved/gt_01.J.out b/contrib/libxo/tests/gettext/saved/gt_01.J.out new file mode 100644 index 000000000000..86527d149a6d --- /dev/null +++ b/contrib/libxo/tests/gettext/saved/gt_01.J.out @@ -0,0 +1,2 @@ +{"top": {"adjective":"amingflay","noun":"ordsway","verb":"urningbay","owner":"ymay","target":"ouchcay","adjective":"amingflay","noun":"ordsway","verb":"urningbay","owner":"ymay","target":"ouchcay", "bytes": [0,1,2,3,4],"total":1234,"received":1234,"from":"foop","port":4321,"time":32,"received":1234,"from":"foop","port":4321,"time":32,"received":1234,"from":"foop","port":4321,"time":32,"marzlevanes":3,"version":"1.2.3","date":"Tue Jun 23 18:47:09 UTC 2015", "__warning": {"program":"gt_01.test","message":"Nableuay otay ectulatobjay orwardfay elocipingvay","verb":ectulatobjay,"error":"Ermissionpay eniedday"}, "__warning": {"program":"gt_01.test","message":"automaticyay ynchronizationsay ofyay ardinalyay ammetersgray ailedfay","style":automaticyay,"type":"ardinalyay","target":"ammetersgray","error":"Ermissionpay eniedday"},"marzlevanes":6,"windings":"otuslay-oyay-eltayay"} +} diff --git a/contrib/libxo/tests/gettext/saved/gt_01.JP.err b/contrib/libxo/tests/gettext/saved/gt_01.JP.err new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/contrib/libxo/tests/gettext/saved/gt_01.JP.out b/contrib/libxo/tests/gettext/saved/gt_01.JP.out new file mode 100644 index 000000000000..537ab213cb7c --- /dev/null +++ b/contrib/libxo/tests/gettext/saved/gt_01.JP.out @@ -0,0 +1,53 @@ +{ + "top": { + "adjective": "amingflay", + "noun": "ordsway", + "verb": "urningbay", + "owner": "ymay", + "target": "ouchcay", + "adjective": "amingflay", + "noun": "ordsway", + "verb": "urningbay", + "owner": "ymay", + "target": "ouchcay", + "bytes": [ + 0, + 1, + 2, + 3, + 4 + ], + "total": 1234, + "received": 1234, + "from": "foop", + "port": 4321, + "time": 32, + "received": 1234, + "from": "foop", + "port": 4321, + "time": 32, + "received": 1234, + "from": "foop", + "port": 4321, + "time": 32, + "marzlevanes": 3, + "version": "1.2.3", + "date": "Tue Jun 23 18:47:09 UTC 2015", + "__warning": { + "program": "gt_01.test", + "message": "Nableuay otay ectulatobjay orwardfay elocipingvay", + "verb": ectulatobjay, + "error": "Ermissionpay eniedday" + }, + "__warning": { + "program": "gt_01.test", + "message": "automaticyay ynchronizationsay ofyay ardinalyay ammetersgray ailedfay", + "style": automaticyay, + "type": "ardinalyay", + "target": "ammetersgray", + "error": "Ermissionpay eniedday" + }, + "marzlevanes": 6, + "windings": "otuslay-oyay-eltayay" + } +} diff --git a/contrib/libxo/tests/gettext/saved/gt_01.T.err b/contrib/libxo/tests/gettext/saved/gt_01.T.err new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/contrib/libxo/tests/gettext/saved/gt_01.T.out b/contrib/libxo/tests/gettext/saved/gt_01.T.out new file mode 100644 index 000000000000..440d9a5bc9a5 --- /dev/null +++ b/contrib/libxo/tests/gettext/saved/gt_01.T.out @@ -0,0 +1,17 @@ +Ouryay ordsway amingflay isyay ymayouchcay bubbly-bubbly urningbay +Ethay ordsway asway 'ymayamingflayouchcay' urningbay +0 yebay +1 yesbay +2 yezbay +3 yezbay +4 yezbay +otaltay 1234 +Eceivedray 1234 ldb2 omfray foop#4321 inyay 32 msyay +Received 1234 yezbay from foop#4321 in 32 ms +Eceivedray 1234 ldb2 omfray foop#4321 inyay 32 msyay +Onlyay 3 arzlevanezmay areyay unctioningfay orrectlycay +Ersionvay Tue Jun 23 18:47:09 UTC 2015 1.2.3 +gt_01.test: Nableuay otay ectulatobjay orwardfay elocipingvay: Ermissionpay eniedday +gt_01.test: automaticyay ynchronizationsay ofyay ardinalyay ammetersgray ailedfay: Ermissionpay eniedday +ydrocoptichay arzlevanesmay: 6 +Dude, Indingsway: otuslay-oyay-eltayay diff --git a/contrib/libxo/tests/gettext/saved/gt_01.X.err b/contrib/libxo/tests/gettext/saved/gt_01.X.err new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/contrib/libxo/tests/gettext/saved/gt_01.X.out b/contrib/libxo/tests/gettext/saved/gt_01.X.out new file mode 100644 index 000000000000..4eb46223c72f --- /dev/null +++ b/contrib/libxo/tests/gettext/saved/gt_01.X.out @@ -0,0 +1 @@ +amingflayordswayurningbayymayouchcayamingflayordswayurningbayymayouchcay0123412341234foop43211234foop43211234foop432131.2.3Tue Jun 23 18:47:09 UTC 2015<__warning>gt_01.testNableuay otay ectulatobjay orwardfay elocipingvayectulatobjayErmissionpay eniedday<__warning>gt_01.testautomaticyay ynchronizationsay ofyay ardinalyay ammetersgray ailedfayardinalyayammetersgrayErmissionpay eniedday6otuslay-oyay-eltayay \ No newline at end of file diff --git a/contrib/libxo/tests/gettext/saved/gt_01.XP.err b/contrib/libxo/tests/gettext/saved/gt_01.XP.err new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/contrib/libxo/tests/gettext/saved/gt_01.XP.out b/contrib/libxo/tests/gettext/saved/gt_01.XP.out new file mode 100644 index 000000000000..eac42845ad59 --- /dev/null +++ b/contrib/libxo/tests/gettext/saved/gt_01.XP.out @@ -0,0 +1,49 @@ + + amingflay + ordsway + urningbay + ymay + ouchcay + amingflay + ordsway + urningbay + ymay + ouchcay + 0 + 1 + 2 + 3 + 4 + 1234 + 1234 + foop + 4321 + + 1234 + foop + 4321 + + 1234 + foop + 4321 + + 3 + 1.2.3 + Tue Jun 23 18:47:09 UTC 2015 + <__warning> + gt_01.test + Nableuay otay ectulatobjay orwardfay elocipingvay + ectulatobjay + Ermissionpay eniedday + + <__warning> + gt_01.test + automaticyay ynchronizationsay ofyay ardinalyay ammetersgray ailedfay + + ardinalyay + ammetersgray + Ermissionpay eniedday + + 6 + otuslay-oyay-eltayay + diff --git a/contrib/libxo/tests/gettext/strerror.pot b/contrib/libxo/tests/gettext/strerror.pot new file mode 100644 index 000000000000..c63e6bdb9e87 --- /dev/null +++ b/contrib/libxo/tests/gettext/strerror.pot @@ -0,0 +1,468 @@ +# +# Copyright (c) 1982, 1985, 1993 +# The Regents of the University of California. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# List of system errors ala strerror() and sys_errlist +# Phil Shafer , 2015. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2015-07-01 16:15-0400\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "Received {:received} {N:byte,bytes} from {:from}#{:port} in {:time} ms\n" +msgstr "" + +# 0 - ENOERROR +msgid "No error: 0" +msgstr "" + +# 1 - EPERM +msgid "Operation not permitted" +msgstr "" + +# 2 - ENOENT +msgid "No such file or directory" +msgstr "" + +# 3 - ESRCH +msgid "No such process" +msgstr "" + +# 4 - EINTR +msgid "Interrupted system call" +msgstr "" + +# 5 - EIO +msgid "Input/output error" +msgstr "" + +# 6 - ENXIO +msgid "Device not configured" +msgstr "" + +# 7 - E2BIG +msgid "Argument list too long" +msgstr "" + +# 8 - ENOEXEC +msgid "Exec format error" +msgstr "" + +# 9 - EBADF +msgid "Bad file descriptor" +msgstr "" + +# 10 - ECHILD +msgid "No child processes" +msgstr "" + +# 11 - EDEADLK +msgid "Resource deadlock avoided" +msgstr "" + +# 12 - ENOMEM +msgid "Cannot allocate memory" +msgstr "" + +# 13 - EACCES +msgid "Permission denied" +msgstr "" + +# 14 - EFAULT +msgid "Bad address" +msgstr "" + +# 15 - ENOTBLK +msgid "Block device required" +msgstr "" + +# 16 - EBUSY +msgid "Device busy" +msgstr "" + +# 17 - EEXIST +msgid "File exists" +msgstr "" + +# 18 - EXDEV +msgid "Cross-device link" +msgstr "" + +# 19 - ENODEV +msgid "Operation not supported by device" +msgstr "" + +# 20 - ENOTDIR +msgid "Not a directory" +msgstr "" + +# 21 - EISDIR +msgid "Is a directory" +msgstr "" + +# 22 - EINVAL +msgid "Invalid argument" +msgstr "" + +# 23 - ENFILE +msgid "Too many open files in system" +msgstr "" + +# 24 - EMFILE +msgid "Too many open files" +msgstr "" + +# 25 - ENOTTY +msgid "Inappropriate ioctl for device" +msgstr "" + +# 26 - ETXTBSY +msgid "Text file busy" +msgstr "" + +# 27 - EFBIG +msgid "File too large" +msgstr "" + +# 28 - ENOSPC +msgid "No space left on device" +msgstr "" + +# 29 - ESPIPE +msgid "Illegal seek" +msgstr "" + +# 30 - EROFS +msgid "Read-only file system" +msgstr "" + +# 31 - EMLINK +msgid "Too many links" +msgstr "" + +# 32 - EPIPE +msgid "Broken pipe" +msgstr "" + +# +# math software +# + +# 33 - EDOM +msgid "Numerical argument out of domain" +msgstr "" + +# 34 - ERANGE +msgid "Result too large" +msgstr "" + +# +# non-blocking and interrupt i/o +# + +# 35 - EAGAIN +# 35 - EWOULDBLOCK +msgid "Resource temporarily unavailable" +msgstr "" + +# 36 - EINPROGRESS +msgid "Operation now in progress" +msgstr "" + +# 37 - EALREADY +msgid "Operation already in progress" +msgstr "" + + +# +# ipc/network software -- argument errors +# + +# 38 - ENOTSOCK +msgid "Socket operation on non-socket" +msgstr "" + +# 39 - EDESTADDRREQ +msgid "Destination address required" +msgstr "" + +# 40 - EMSGSIZE +msgid "Message too long" +msgstr "" + +# 41 - EPROTOTYPE +msgid "Protocol wrong type for socket" +msgstr "" + +# 42 - ENOPROTOOPT +msgid "Protocol not available" +msgstr "" + +# 43 - EPROTONOSUPPORT +msgid "Protocol not supported" +msgstr "" + +# 44 - ESOCKTNOSUPPORT +msgid "Socket type not supported" +msgstr "" + +# 45 - EOPNOTSUPP +msgid "Operation not supported" +msgstr "" + +# 46 - EPFNOSUPPORT +msgid "Protocol family not supported" +msgstr "" + +# 47 - EAFNOSUPPORT +msgid "Address family not supported by protocol family" +msgstr "" + +# 48 - EADDRINUSE +msgid "Address already in use" +msgstr "" + +# 49 - EADDRNOTAVAIL +msgid "Can't assign requested address" +msgstr "" + +# +# ipc/network software -- operational errors +# + +# 50 - ENETDOWN +msgid "Network is down" +msgstr "" + +# 51 - ENETUNREACH +msgid "Network is unreachable" +msgstr "" + +# 52 - ENETRESET +msgid "Network dropped connection on reset" +msgstr "" + +# 53 - ECONNABORTED +msgid "Software caused connection abort" +msgstr "" + +# 54 - ECONNRESET +msgid "Connection reset by peer" +msgstr "" + +# 55 - ENOBUFS +msgid "No buffer space available" +msgstr "" + +# 56 - EISCONN +msgid "Socket is already connected" +msgstr "" + +# 57 - ENOTCONN +msgid "Socket is not connected" +msgstr "" + +# 58 - ESHUTDOWN +msgid "Can't send after socket shutdown" +msgstr "" + +# 59 - ETOOMANYREFS +msgid "Too many references: can't splice" +msgstr "" + +# 60 - ETIMEDOUT +msgid "Operation timed out" +msgstr "" + +# 61 - ECONNREFUSED +msgid "Connection refused" +msgstr "" + +# 62 - ELOOP +msgid "Too many levels of symbolic links" +msgstr "" + +# 63 - ENAMETOOLONG +msgid "File name too long" +msgstr "" + +# +# should be rearranged +# + +# 64 - EHOSTDOWN +msgid "Host is down" +msgstr "" + +# 65 - EHOSTUNREACH +msgid "No route to host" +msgstr "" + +# 66 - ENOTEMPTY +msgid "Directory not empty" +msgstr "" + +# +# quotas & mush +# + +# 67 - EPROCLIM +msgid "Too many processes" +msgstr "" + +# 68 - EUSERS +msgid "Too many users" +msgstr "" + +# 69 - EDQUOT +msgid "Disc quota exceeded" +msgstr "" + +# +# Network File System +# + +# 70 - ESTALE +msgid "Stale NFS file handle" +msgstr "" + +# 71 - EREMOTE +msgid "Too many levels of remote in path" +msgstr "" + +# 72 - EBADRPC +msgid "RPC struct is bad" +msgstr "" + +# 73 - ERPCMISMATCH +msgid "RPC version wrong" +msgstr "" + +# 74 - EPROGUNAVAIL +msgid "RPC prog. not avail" +msgstr "" + +# 75 - EPROGMISMATCH +msgid "Program version wrong" +msgstr "" + +# 76 - EPROCUNAVAIL +msgid "Bad procedure for program" +msgstr "" + +# 77 - ENOLCK +msgid "No locks available" +msgstr "" + +# 78 - ENOSYS +msgid "Function not implemented" +msgstr "" + +# 79 - EFTYPE +msgid "Inappropriate file type or format" +msgstr "" + +# 80 - EAUTH +msgid "Authentication error" +msgstr "" + +# 81 - ENEEDAUTH +msgid "Need authenticator" +msgstr "" + +# 82 - EIDRM +msgid "Identifier removed" +msgstr "" + +# 83 - ENOMSG +msgid "No message of desired type" +msgstr "" + +# 84 - EOVERFLOW +msgid "Value too large to be stored in data type" +msgstr "" + +# 85 - ECANCELED +msgid "Operation canceled" +msgstr "" + +# 86 - EILSEQ +msgid "Illegal byte sequence" +msgstr "" + +# 87 - ENOATTR +msgid "Attribute not found" +msgstr "" + +# +# General +# + +# 88 - EDOOFUS +msgid "Programming error" +msgstr "" + +# 89 - EBADMSG +msgid "Bad message" +msgstr "" + +# 90 - EMULTIHOP +msgid "Multihop attempted" +msgstr "" + +# 91 - ENOLINK +msgid "Link has been severed" +msgstr "" + +# 92 - EPROTO +msgid "Protocol error" +msgstr "" + +# 93 - ENOTCAPABLE +msgid "Capabilities insufficient" +msgstr "" + +# 94 - ECAPMODE +msgid "Not permitted in capability mode" +msgstr "" + +# 95 - ENOTRECOVERABLE +msgid "State not recoverable" +msgstr "" + +# 96 - EOWNERDEAD +msgid "Previous owner died" +msgstr "" diff --git a/contrib/libxo/xo/Makefile.am b/contrib/libxo/xo/Makefile.am index 247ef3b08118..ca01de33ca70 100644 --- a/contrib/libxo/xo/Makefile.am +++ b/contrib/libxo/xo/Makefile.am @@ -30,6 +30,14 @@ xo_SOURCES = xo.c LDADD = \ ${top_builddir}/libxo/libxo.la +if HAVE_HUMANIZE_NUMBER +LDADD += -lutil +endif + man_MANS = xo.1 EXTRA_DIST = xo.1 + +install-data-hook: + for file in ${man_MANS}; do \ + cat ../libxo/add.man >> ${DESTDIR}${man1dir}/$$file ; done diff --git a/contrib/libxo/xo/xo.1 b/contrib/libxo/xo/xo.1 index 9dcae85f0318..54a97e7944fc 100644 --- a/contrib/libxo/xo/xo.1 +++ b/contrib/libxo/xo/xo.1 @@ -168,31 +168,6 @@ prepend data to the XPath values used for HTML output style. stereo in route .Ed -.Sh ADDITIONAL DOCUMENTATION -Complete documentation can be found on github: -.Bd -literal -offset indent -http://juniper.github.io/libxo/libxo-manual.html -.Ed -.Pp -.Nm libxo -lives on github as: -.Bd -literal -offset indent -https://github.com/Juniper/libxo -.Ed -.Pp -The latest release of -.Nm libxo -is available at: -.Bd -literal -offset indent -https://github.com/Juniper/libxo/releases -.Ed .Sh SEE ALSO .Xr libxo 3 , .Xr xo_emit 3 -.Sh HISTORY -The -.Nm libxo -library was added in -.Fx 11.0 . -.Sh AUTHOR -Phil Shafer diff --git a/contrib/libxo/xo/xo.c b/contrib/libxo/xo/xo.c index c364539091af..ce758afb7cab 100644 --- a/contrib/libxo/xo/xo.c +++ b/contrib/libxo/xo/xo.c @@ -13,9 +13,8 @@ #include #include -#include "xoconfig.h" +#include "xo_config.h" #include "xo.h" -#include "xoversion.h" #include /* Include after xo.h for testing */ @@ -95,7 +94,8 @@ static int formatter (xo_handle_t *xop, char *buf, int bufsiz, const char *fmt, va_list vap UNUSED) { - int lflag = 0, hflag = 0, jflag = 0, tflag = 0, + int lflag UNUSED = 0; /* Parse long flag, though currently ignored */ + int hflag = 0, jflag = 0, tflag = 0, zflag = 0, qflag = 0, star1 = 0, star2 = 0; int rc = 0; int w1 = 0, w2 = 0; @@ -201,6 +201,7 @@ print_help (void) " --leading-xpath OR -l " "Add a prefix to generated XPaths (HTML)\n" " --open Open tags for the given path\n" +" --option -or -O Give formatting options\n" " --pretty OR -p Make 'pretty' output (add indent, newlines)\n" " --style