memcached-1.5.6/0000775000175000017500000000000013245330107010432 500000000000000memcached-1.5.6/slab_automove_extstore.h0000664000175000017500000000036613240770206015330 00000000000000#ifndef SLAB_AUTOMOVE_EXTSTORE_H #define SLAB_AUTOMOVE_EXTSTORE_H void *slab_automove_extstore_init(struct settings *settings); void slab_automove_extstore_free(void *arg); void slab_automove_extstore_run(void *arg, int *src, int *dst); #endif memcached-1.5.6/config.guess0000755000175000017500000012475312612517377012721 00000000000000#! /bin/sh # Attempt to guess a canonical system name. # Copyright 1992-2015 Free Software Foundation, Inc. timestamp='2015-08-20' # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, see . # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that # program. This Exception is an additional permission under section 7 # of the GNU General Public License, version 3 ("GPLv3"). # # Originally written by Per Bothner; maintained since 2000 by Ben Elliston. # # You can get the latest version of this script from: # http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD # # Please send patches to . me=`echo "$0" | sed -e 's,.*/,,'` usage="\ Usage: $0 [OPTION] Output the configuration name of the system \`$me' is run on. Operation modes: -h, --help print this help, then exit -t, --time-stamp print date of last modification, then exit -v, --version print version number, then exit Report bugs and patches to ." version="\ GNU config.guess ($timestamp) Originally written by Per Bothner. Copyright 1992-2015 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." help=" Try \`$me --help' for more information." # Parse command line while test $# -gt 0 ; do case $1 in --time-stamp | --time* | -t ) echo "$timestamp" ; exit ;; --version | -v ) echo "$version" ; exit ;; --help | --h* | -h ) echo "$usage"; exit ;; -- ) # Stop option processing shift; break ;; - ) # Use stdin as input. break ;; -* ) echo "$me: invalid option $1$help" >&2 exit 1 ;; * ) break ;; esac done if test $# != 0; then echo "$me: too many arguments$help" >&2 exit 1 fi trap 'exit 1' 1 2 15 # CC_FOR_BUILD -- compiler used by this script. Note that the use of a # compiler to aid in system detection is discouraged as it requires # temporary files to be created and, as you can see below, it is a # headache to deal with in a portable fashion. # Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still # use `HOST_CC' if defined, but it is deprecated. # Portable tmp directory creation inspired by the Autoconf team. set_cc_for_build=' trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ; trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ; : ${TMPDIR=/tmp} ; { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } || { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } || { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } || { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ; dummy=$tmp/dummy ; tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ; case $CC_FOR_BUILD,$HOST_CC,$CC in ,,) echo "int x;" > $dummy.c ; for c in cc gcc c89 c99 ; do if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then CC_FOR_BUILD="$c"; break ; fi ; done ; if test x"$CC_FOR_BUILD" = x ; then CC_FOR_BUILD=no_compiler_found ; fi ;; ,,*) CC_FOR_BUILD=$CC ;; ,*,*) CC_FOR_BUILD=$HOST_CC ;; esac ; set_cc_for_build= ;' # This is needed to find uname on a Pyramid OSx when run in the BSD universe. # (ghazi@noc.rutgers.edu 1994-08-24) if (test -f /.attbin/uname) >/dev/null 2>&1 ; then PATH=$PATH:/.attbin ; export PATH fi UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown case "${UNAME_SYSTEM}" in Linux|GNU|GNU/*) # If the system lacks a compiler, then just pick glibc. # We could probably try harder. LIBC=gnu eval $set_cc_for_build cat <<-EOF > $dummy.c #include #if defined(__UCLIBC__) LIBC=uclibc #elif defined(__dietlibc__) LIBC=dietlibc #else LIBC=gnu #endif EOF eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC' | sed 's, ,,g'` ;; esac # Note: order is significant - the case branches are not exclusive. case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in *:NetBSD:*:*) # NetBSD (nbsd) targets should (where applicable) match one or # more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*, # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently # switched to ELF, *-*-netbsd* would select the old # object file format. This provides both forward # compatibility and a consistent mechanism for selecting the # object file format. # # Note: NetBSD doesn't particularly care about the vendor # portion of the name. We always set it to "unknown". sysctl="sysctl -n hw.machine_arch" UNAME_MACHINE_ARCH=`(uname -p 2>/dev/null || \ /sbin/$sysctl 2>/dev/null || \ /usr/sbin/$sysctl 2>/dev/null || \ echo unknown)` case "${UNAME_MACHINE_ARCH}" in armeb) machine=armeb-unknown ;; arm*) machine=arm-unknown ;; sh3el) machine=shl-unknown ;; sh3eb) machine=sh-unknown ;; sh5el) machine=sh5le-unknown ;; earmv*) arch=`echo ${UNAME_MACHINE_ARCH} | sed -e 's,^e\(armv[0-9]\).*$,\1,'` endian=`echo ${UNAME_MACHINE_ARCH} | sed -ne 's,^.*\(eb\)$,\1,p'` machine=${arch}${endian}-unknown ;; *) machine=${UNAME_MACHINE_ARCH}-unknown ;; esac # The Operating System including object format, if it has switched # to ELF recently, or will in the future. case "${UNAME_MACHINE_ARCH}" in arm*|earm*|i386|m68k|ns32k|sh3*|sparc|vax) eval $set_cc_for_build if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ELF__ then # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout). # Return netbsd for either. FIX? os=netbsd else os=netbsdelf fi ;; *) os=netbsd ;; esac # Determine ABI tags. case "${UNAME_MACHINE_ARCH}" in earm*) expr='s/^earmv[0-9]/-eabi/;s/eb$//' abi=`echo ${UNAME_MACHINE_ARCH} | sed -e "$expr"` ;; esac # The OS release # Debian GNU/NetBSD machines have a different userland, and # thus, need a distinct triplet. However, they do not need # kernel version information, so it can be replaced with a # suitable tag, in the style of linux-gnu. case "${UNAME_VERSION}" in Debian*) release='-gnu' ;; *) release=`echo ${UNAME_RELEASE} | sed -e 's/[-_].*//' | cut -d. -f1,2` ;; esac # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: # contains redundant information, the shorter form: # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. echo "${machine}-${os}${release}${abi}" exit ;; *:Bitrig:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'` echo ${UNAME_MACHINE_ARCH}-unknown-bitrig${UNAME_RELEASE} exit ;; *:OpenBSD:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'` echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE} exit ;; *:ekkoBSD:*:*) echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE} exit ;; *:SolidBSD:*:*) echo ${UNAME_MACHINE}-unknown-solidbsd${UNAME_RELEASE} exit ;; macppc:MirBSD:*:*) echo powerpc-unknown-mirbsd${UNAME_RELEASE} exit ;; *:MirBSD:*:*) echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE} exit ;; *:Sortix:*:*) echo ${UNAME_MACHINE}-unknown-sortix exit ;; alpha:OSF1:*:*) case $UNAME_RELEASE in *4.0) UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` ;; *5.*) UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'` ;; esac # According to Compaq, /usr/sbin/psrinfo has been available on # OSF/1 and Tru64 systems produced since 1995. I hope that # covers most systems running today. This code pipes the CPU # types through head -n 1, so we only detect the type of CPU 0. ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1` case "$ALPHA_CPU_TYPE" in "EV4 (21064)") UNAME_MACHINE="alpha" ;; "EV4.5 (21064)") UNAME_MACHINE="alpha" ;; "LCA4 (21066/21068)") UNAME_MACHINE="alpha" ;; "EV5 (21164)") UNAME_MACHINE="alphaev5" ;; "EV5.6 (21164A)") UNAME_MACHINE="alphaev56" ;; "EV5.6 (21164PC)") UNAME_MACHINE="alphapca56" ;; "EV5.7 (21164PC)") UNAME_MACHINE="alphapca57" ;; "EV6 (21264)") UNAME_MACHINE="alphaev6" ;; "EV6.7 (21264A)") UNAME_MACHINE="alphaev67" ;; "EV6.8CB (21264C)") UNAME_MACHINE="alphaev68" ;; "EV6.8AL (21264B)") UNAME_MACHINE="alphaev68" ;; "EV6.8CX (21264D)") UNAME_MACHINE="alphaev68" ;; "EV6.9A (21264/EV69A)") UNAME_MACHINE="alphaev69" ;; "EV7 (21364)") UNAME_MACHINE="alphaev7" ;; "EV7.9 (21364A)") UNAME_MACHINE="alphaev79" ;; esac # A Pn.n version is a patched version. # A Vn.n version is a released version. # A Tn.n version is a released field test version. # A Xn.n version is an unreleased experimental baselevel. # 1.2 uses "1.2" for uname -r. echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` # Reset EXIT trap before exiting to avoid spurious non-zero exit code. exitcode=$? trap '' 0 exit $exitcode ;; Alpha\ *:Windows_NT*:*) # How do we know it's Interix rather than the generic POSIX subsystem? # Should we change UNAME_MACHINE based on the output of uname instead # of the specific Alpha model? echo alpha-pc-interix exit ;; 21064:Windows_NT:50:3) echo alpha-dec-winnt3.5 exit ;; Amiga*:UNIX_System_V:4.0:*) echo m68k-unknown-sysv4 exit ;; *:[Aa]miga[Oo][Ss]:*:*) echo ${UNAME_MACHINE}-unknown-amigaos exit ;; *:[Mm]orph[Oo][Ss]:*:*) echo ${UNAME_MACHINE}-unknown-morphos exit ;; *:OS/390:*:*) echo i370-ibm-openedition exit ;; *:z/VM:*:*) echo s390-ibm-zvmoe exit ;; *:OS400:*:*) echo powerpc-ibm-os400 exit ;; arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) echo arm-acorn-riscix${UNAME_RELEASE} exit ;; arm*:riscos:*:*|arm*:RISCOS:*:*) echo arm-unknown-riscos exit ;; SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) echo hppa1.1-hitachi-hiuxmpp exit ;; Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. if test "`(/bin/universe) 2>/dev/null`" = att ; then echo pyramid-pyramid-sysv3 else echo pyramid-pyramid-bsd fi exit ;; NILE*:*:*:dcosx) echo pyramid-pyramid-svr4 exit ;; DRS?6000:unix:4.0:6*) echo sparc-icl-nx6 exit ;; DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*) case `/usr/bin/uname -p` in sparc) echo sparc-icl-nx7; exit ;; esac ;; s390x:SunOS:*:*) echo ${UNAME_MACHINE}-ibm-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; sun4H:SunOS:5.*:*) echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*) echo i386-pc-auroraux${UNAME_RELEASE} exit ;; i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*) eval $set_cc_for_build SUN_ARCH="i386" # If there is a compiler, see if it is configured for 64-bit objects. # Note that the Sun cc does not turn __LP64__ into 1 like gcc does. # This test works for both compilers. if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \ (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ grep IS_64BIT_ARCH >/dev/null then SUN_ARCH="x86_64" fi fi echo ${SUN_ARCH}-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; sun4*:SunOS:6*:*) # According to config.sub, this is the proper way to canonicalize # SunOS6. Hard to guess exactly what SunOS6 will be like, but # it's likely to be more like Solaris than SunOS4. echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; sun4*:SunOS:*:*) case "`/usr/bin/arch -k`" in Series*|S4*) UNAME_RELEASE=`uname -v` ;; esac # Japanese Language versions have a version number like `4.1.3-JL'. echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'` exit ;; sun3*:SunOS:*:*) echo m68k-sun-sunos${UNAME_RELEASE} exit ;; sun*:*:4.2BSD:*) UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3 case "`/bin/arch`" in sun3) echo m68k-sun-sunos${UNAME_RELEASE} ;; sun4) echo sparc-sun-sunos${UNAME_RELEASE} ;; esac exit ;; aushp:SunOS:*:*) echo sparc-auspex-sunos${UNAME_RELEASE} exit ;; # The situation for MiNT is a little confusing. The machine name # can be virtually everything (everything which is not # "atarist" or "atariste" at least should have a processor # > m68000). The system name ranges from "MiNT" over "FreeMiNT" # to the lowercase version "mint" (or "freemint"). Finally # the system name "TOS" denotes a system which is actually not # MiNT. But MiNT is downward compatible to TOS, so this should # be no problem. atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) echo m68k-atari-mint${UNAME_RELEASE} exit ;; atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) echo m68k-atari-mint${UNAME_RELEASE} exit ;; *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) echo m68k-atari-mint${UNAME_RELEASE} exit ;; milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) echo m68k-milan-mint${UNAME_RELEASE} exit ;; hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) echo m68k-hades-mint${UNAME_RELEASE} exit ;; *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) echo m68k-unknown-mint${UNAME_RELEASE} exit ;; m68k:machten:*:*) echo m68k-apple-machten${UNAME_RELEASE} exit ;; powerpc:machten:*:*) echo powerpc-apple-machten${UNAME_RELEASE} exit ;; RISC*:Mach:*:*) echo mips-dec-mach_bsd4.3 exit ;; RISC*:ULTRIX:*:*) echo mips-dec-ultrix${UNAME_RELEASE} exit ;; VAX*:ULTRIX*:*:*) echo vax-dec-ultrix${UNAME_RELEASE} exit ;; 2020:CLIX:*:* | 2430:CLIX:*:*) echo clipper-intergraph-clix${UNAME_RELEASE} exit ;; mips:*:*:UMIPS | mips:*:*:RISCos) eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #ifdef __cplusplus #include /* for printf() prototype */ int main (int argc, char *argv[]) { #else int main (argc, argv) int argc; char *argv[]; { #endif #if defined (host_mips) && defined (MIPSEB) #if defined (SYSTYPE_SYSV) printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0); #endif #if defined (SYSTYPE_SVR4) printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0); #endif #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0); #endif #endif exit (-1); } EOF $CC_FOR_BUILD -o $dummy $dummy.c && dummyarg=`echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` && SYSTEM_NAME=`$dummy $dummyarg` && { echo "$SYSTEM_NAME"; exit; } echo mips-mips-riscos${UNAME_RELEASE} exit ;; Motorola:PowerMAX_OS:*:*) echo powerpc-motorola-powermax exit ;; Motorola:*:4.3:PL8-*) echo powerpc-harris-powermax exit ;; Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*) echo powerpc-harris-powermax exit ;; Night_Hawk:Power_UNIX:*:*) echo powerpc-harris-powerunix exit ;; m88k:CX/UX:7*:*) echo m88k-harris-cxux7 exit ;; m88k:*:4*:R4*) echo m88k-motorola-sysv4 exit ;; m88k:*:3*:R3*) echo m88k-motorola-sysv3 exit ;; AViiON:dgux:*:*) # DG/UX returns AViiON for all architectures UNAME_PROCESSOR=`/usr/bin/uname -p` if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ] then if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \ [ ${TARGET_BINARY_INTERFACE}x = x ] then echo m88k-dg-dgux${UNAME_RELEASE} else echo m88k-dg-dguxbcs${UNAME_RELEASE} fi else echo i586-dg-dgux${UNAME_RELEASE} fi exit ;; M88*:DolphinOS:*:*) # DolphinOS (SVR3) echo m88k-dolphin-sysv3 exit ;; M88*:*:R3*:*) # Delta 88k system running SVR3 echo m88k-motorola-sysv3 exit ;; XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) echo m88k-tektronix-sysv3 exit ;; Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) echo m68k-tektronix-bsd exit ;; *:IRIX*:*:*) echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'` exit ;; ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id exit ;; # Note that: echo "'`uname -s`'" gives 'AIX ' i*86:AIX:*:*) echo i386-ibm-aix exit ;; ia64:AIX:*:*) if [ -x /usr/bin/oslevel ] ; then IBM_REV=`/usr/bin/oslevel` else IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} fi echo ${UNAME_MACHINE}-ibm-aix${IBM_REV} exit ;; *:AIX:2:3) if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #include main() { if (!__power_pc()) exit(1); puts("powerpc-ibm-aix3.2.5"); exit(0); } EOF if $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` then echo "$SYSTEM_NAME" else echo rs6000-ibm-aix3.2.5 fi elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then echo rs6000-ibm-aix3.2.4 else echo rs6000-ibm-aix3.2 fi exit ;; *:AIX:*:[4567]) IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'` if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then IBM_ARCH=rs6000 else IBM_ARCH=powerpc fi if [ -x /usr/bin/lslpp ] ; then IBM_REV=`/usr/bin/lslpp -Lqc bos.rte.libc | awk -F: '{ print $3 }' | sed s/[0-9]*$/0/` else IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} fi echo ${IBM_ARCH}-ibm-aix${IBM_REV} exit ;; *:AIX:*:*) echo rs6000-ibm-aix exit ;; ibmrt:4.4BSD:*|romp-ibm:BSD:*) echo romp-ibm-bsd4.4 exit ;; ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to exit ;; # report: romp-ibm BSD 4.3 *:BOSX:*:*) echo rs6000-bull-bosx exit ;; DPX/2?00:B.O.S.:*:*) echo m68k-bull-sysv3 exit ;; 9000/[34]??:4.3bsd:1.*:*) echo m68k-hp-bsd exit ;; hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) echo m68k-hp-bsd4.4 exit ;; 9000/[34678]??:HP-UX:*:*) HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` case "${UNAME_MACHINE}" in 9000/31? ) HP_ARCH=m68000 ;; 9000/[34]?? ) HP_ARCH=m68k ;; 9000/[678][0-9][0-9]) if [ -x /usr/bin/getconf ]; then sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` case "${sc_cpu_version}" in 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1 532) # CPU_PA_RISC2_0 case "${sc_kernel_bits}" in 32) HP_ARCH="hppa2.0n" ;; 64) HP_ARCH="hppa2.0w" ;; '') HP_ARCH="hppa2.0" ;; # HP-UX 10.20 esac ;; esac fi if [ "${HP_ARCH}" = "" ]; then eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #define _HPUX_SOURCE #include #include int main () { #if defined(_SC_KERNEL_BITS) long bits = sysconf(_SC_KERNEL_BITS); #endif long cpu = sysconf (_SC_CPU_VERSION); switch (cpu) { case CPU_PA_RISC1_0: puts ("hppa1.0"); break; case CPU_PA_RISC1_1: puts ("hppa1.1"); break; case CPU_PA_RISC2_0: #if defined(_SC_KERNEL_BITS) switch (bits) { case 64: puts ("hppa2.0w"); break; case 32: puts ("hppa2.0n"); break; default: puts ("hppa2.0"); break; } break; #else /* !defined(_SC_KERNEL_BITS) */ puts ("hppa2.0"); break; #endif default: puts ("hppa1.0"); break; } exit (0); } EOF (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy` test -z "$HP_ARCH" && HP_ARCH=hppa fi ;; esac if [ ${HP_ARCH} = "hppa2.0w" ] then eval $set_cc_for_build # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler # generating 64-bit code. GNU and HP use different nomenclature: # # $ CC_FOR_BUILD=cc ./config.guess # => hppa2.0w-hp-hpux11.23 # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess # => hppa64-hp-hpux11.23 if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | grep -q __LP64__ then HP_ARCH="hppa2.0w" else HP_ARCH="hppa64" fi fi echo ${HP_ARCH}-hp-hpux${HPUX_REV} exit ;; ia64:HP-UX:*:*) HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` echo ia64-hp-hpux${HPUX_REV} exit ;; 3050*:HI-UX:*:*) eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #include int main () { long cpu = sysconf (_SC_CPU_VERSION); /* The order matters, because CPU_IS_HP_MC68K erroneously returns true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct results, however. */ if (CPU_IS_PA_RISC (cpu)) { switch (cpu) { case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break; case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break; case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break; default: puts ("hppa-hitachi-hiuxwe2"); break; } } else if (CPU_IS_HP_MC68K (cpu)) puts ("m68k-hitachi-hiuxwe2"); else puts ("unknown-hitachi-hiuxwe2"); exit (0); } EOF $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` && { echo "$SYSTEM_NAME"; exit; } echo unknown-hitachi-hiuxwe2 exit ;; 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* ) echo hppa1.1-hp-bsd exit ;; 9000/8??:4.3bsd:*:*) echo hppa1.0-hp-bsd exit ;; *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) echo hppa1.0-hp-mpeix exit ;; hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* ) echo hppa1.1-hp-osf exit ;; hp8??:OSF1:*:*) echo hppa1.0-hp-osf exit ;; i*86:OSF1:*:*) if [ -x /usr/sbin/sysversion ] ; then echo ${UNAME_MACHINE}-unknown-osf1mk else echo ${UNAME_MACHINE}-unknown-osf1 fi exit ;; parisc*:Lites*:*:*) echo hppa1.1-hp-lites exit ;; C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) echo c1-convex-bsd exit ;; C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) if getsysinfo -f scalar_acc then echo c32-convex-bsd else echo c2-convex-bsd fi exit ;; C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) echo c34-convex-bsd exit ;; C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) echo c38-convex-bsd exit ;; C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) echo c4-convex-bsd exit ;; CRAY*Y-MP:*:*:*) echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; CRAY*[A-Z]90:*:*:*) echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \ | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \ -e 's/\.[^.]*$/.X/' exit ;; CRAY*TS:*:*:*) echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; CRAY*T3E:*:*:*) echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; CRAY*SV1:*:*:*) echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; *:UNICOS/mp:*:*) echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'` echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" exit ;; 5000:UNIX_System_V:4.*:*) FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'` echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" exit ;; i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE} exit ;; sparc*:BSD/OS:*:*) echo sparc-unknown-bsdi${UNAME_RELEASE} exit ;; *:BSD/OS:*:*) echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE} exit ;; *:FreeBSD:*:*) UNAME_PROCESSOR=`/usr/bin/uname -p` case ${UNAME_PROCESSOR} in amd64) echo x86_64-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; *) echo ${UNAME_PROCESSOR}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; esac exit ;; i*:CYGWIN*:*) echo ${UNAME_MACHINE}-pc-cygwin exit ;; *:MINGW64*:*) echo ${UNAME_MACHINE}-pc-mingw64 exit ;; *:MINGW*:*) echo ${UNAME_MACHINE}-pc-mingw32 exit ;; *:MSYS*:*) echo ${UNAME_MACHINE}-pc-msys exit ;; i*:windows32*:*) # uname -m includes "-pc" on this system. echo ${UNAME_MACHINE}-mingw32 exit ;; i*:PW*:*) echo ${UNAME_MACHINE}-pc-pw32 exit ;; *:Interix*:*) case ${UNAME_MACHINE} in x86) echo i586-pc-interix${UNAME_RELEASE} exit ;; authenticamd | genuineintel | EM64T) echo x86_64-unknown-interix${UNAME_RELEASE} exit ;; IA64) echo ia64-unknown-interix${UNAME_RELEASE} exit ;; esac ;; [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*) echo i${UNAME_MACHINE}-pc-mks exit ;; 8664:Windows_NT:*) echo x86_64-pc-mks exit ;; i*:Windows_NT*:* | Pentium*:Windows_NT*:*) # How do we know it's Interix rather than the generic POSIX subsystem? # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we # UNAME_MACHINE based on the output of uname instead of i386? echo i586-pc-interix exit ;; i*:UWIN*:*) echo ${UNAME_MACHINE}-pc-uwin exit ;; amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*) echo x86_64-unknown-cygwin exit ;; p*:CYGWIN*:*) echo powerpcle-unknown-cygwin exit ;; prep*:SunOS:5.*:*) echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; *:GNU:*:*) # the GNU system echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-${LIBC}`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'` exit ;; *:GNU/*:*:*) # other systems with GNU libc and userland echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-${LIBC} exit ;; i*86:Minix:*:*) echo ${UNAME_MACHINE}-pc-minix exit ;; aarch64:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; aarch64_be:Linux:*:*) UNAME_MACHINE=aarch64_be echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; alpha:Linux:*:*) case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in EV5) UNAME_MACHINE=alphaev5 ;; EV56) UNAME_MACHINE=alphaev56 ;; PCA56) UNAME_MACHINE=alphapca56 ;; PCA57) UNAME_MACHINE=alphapca56 ;; EV6) UNAME_MACHINE=alphaev6 ;; EV67) UNAME_MACHINE=alphaev67 ;; EV68*) UNAME_MACHINE=alphaev68 ;; esac objdump --private-headers /bin/sh | grep -q ld.so.1 if test "$?" = 0 ; then LIBC="gnulibc1" ; fi echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; arc:Linux:*:* | arceb:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; arm*:Linux:*:*) eval $set_cc_for_build if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_EABI__ then echo ${UNAME_MACHINE}-unknown-linux-${LIBC} else if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_PCS_VFP then echo ${UNAME_MACHINE}-unknown-linux-${LIBC}eabi else echo ${UNAME_MACHINE}-unknown-linux-${LIBC}eabihf fi fi exit ;; avr32*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; cris:Linux:*:*) echo ${UNAME_MACHINE}-axis-linux-${LIBC} exit ;; crisv32:Linux:*:*) echo ${UNAME_MACHINE}-axis-linux-${LIBC} exit ;; e2k:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; frv:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; hexagon:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; i*86:Linux:*:*) echo ${UNAME_MACHINE}-pc-linux-${LIBC} exit ;; ia64:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; m32r*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; m68*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; mips:Linux:*:* | mips64:Linux:*:*) eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #undef CPU #undef ${UNAME_MACHINE} #undef ${UNAME_MACHINE}el #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) CPU=${UNAME_MACHINE}el #else #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) CPU=${UNAME_MACHINE} #else CPU= #endif #endif EOF eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^CPU'` test x"${CPU}" != x && { echo "${CPU}-unknown-linux-${LIBC}"; exit; } ;; openrisc*:Linux:*:*) echo or1k-unknown-linux-${LIBC} exit ;; or32:Linux:*:* | or1k*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; padre:Linux:*:*) echo sparc-unknown-linux-${LIBC} exit ;; parisc64:Linux:*:* | hppa64:Linux:*:*) echo hppa64-unknown-linux-${LIBC} exit ;; parisc:Linux:*:* | hppa:Linux:*:*) # Look for CPU level case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in PA7*) echo hppa1.1-unknown-linux-${LIBC} ;; PA8*) echo hppa2.0-unknown-linux-${LIBC} ;; *) echo hppa-unknown-linux-${LIBC} ;; esac exit ;; ppc64:Linux:*:*) echo powerpc64-unknown-linux-${LIBC} exit ;; ppc:Linux:*:*) echo powerpc-unknown-linux-${LIBC} exit ;; ppc64le:Linux:*:*) echo powerpc64le-unknown-linux-${LIBC} exit ;; ppcle:Linux:*:*) echo powerpcle-unknown-linux-${LIBC} exit ;; s390:Linux:*:* | s390x:Linux:*:*) echo ${UNAME_MACHINE}-ibm-linux-${LIBC} exit ;; sh64*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; sh*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; sparc:Linux:*:* | sparc64:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; tile*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; vax:Linux:*:*) echo ${UNAME_MACHINE}-dec-linux-${LIBC} exit ;; x86_64:Linux:*:*) echo ${UNAME_MACHINE}-pc-linux-${LIBC} exit ;; xtensa*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; i*86:DYNIX/ptx:4*:*) # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. # earlier versions are messed up and put the nodename in both # sysname and nodename. echo i386-sequent-sysv4 exit ;; i*86:UNIX_SV:4.2MP:2.*) # Unixware is an offshoot of SVR4, but it has its own version # number series starting with 2... # I am not positive that other SVR4 systems won't match this, # I just have to hope. -- rms. # Use sysv4.2uw... so that sysv4* matches it. echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION} exit ;; i*86:OS/2:*:*) # If we were able to find `uname', then EMX Unix compatibility # is probably installed. echo ${UNAME_MACHINE}-pc-os2-emx exit ;; i*86:XTS-300:*:STOP) echo ${UNAME_MACHINE}-unknown-stop exit ;; i*86:atheos:*:*) echo ${UNAME_MACHINE}-unknown-atheos exit ;; i*86:syllable:*:*) echo ${UNAME_MACHINE}-pc-syllable exit ;; i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*) echo i386-unknown-lynxos${UNAME_RELEASE} exit ;; i*86:*DOS:*:*) echo ${UNAME_MACHINE}-pc-msdosdjgpp exit ;; i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*) UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'` if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL} else echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL} fi exit ;; i*86:*:5:[678]*) # UnixWare 7.x, OpenUNIX and OpenServer 6. case `/bin/uname -X | grep "^Machine"` in *486*) UNAME_MACHINE=i486 ;; *Pentium) UNAME_MACHINE=i586 ;; *Pent*|*Celeron) UNAME_MACHINE=i686 ;; esac echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION} exit ;; i*86:*:3.2:*) if test -f /usr/options/cb.name; then UNAME_REL=`sed -n 's/.*Version //p' /dev/null >/dev/null ; then UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')` (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486 (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \ && UNAME_MACHINE=i586 (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \ && UNAME_MACHINE=i686 (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \ && UNAME_MACHINE=i686 echo ${UNAME_MACHINE}-pc-sco$UNAME_REL else echo ${UNAME_MACHINE}-pc-sysv32 fi exit ;; pc:*:*:*) # Left here for compatibility: # uname -m prints for DJGPP always 'pc', but it prints nothing about # the processor, so we play safe by assuming i586. # Note: whatever this is, it MUST be the same as what config.sub # prints for the "djgpp" host, or else GDB configury will decide that # this is a cross-build. echo i586-pc-msdosdjgpp exit ;; Intel:Mach:3*:*) echo i386-pc-mach3 exit ;; paragon:*:*:*) echo i860-intel-osf1 exit ;; i860:*:4.*:*) # i860-SVR4 if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4 else # Add other i860-SVR4 vendors below as they are discovered. echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4 fi exit ;; mini*:CTIX:SYS*5:*) # "miniframe" echo m68010-convergent-sysv exit ;; mc68k:UNIX:SYSTEM5:3.51m) echo m68k-convergent-sysv exit ;; M680?0:D-NIX:5.3:*) echo m68k-diab-dnix exit ;; M68*:*:R3V[5678]*:*) test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;; 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0) OS_REL='' test -r /etc/.relid \ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4.3${OS_REL}; exit; } /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4; exit; } ;; NCR*:*:4.2:* | MPRAS*:*:4.2:*) OS_REL='.3' test -r /etc/.relid \ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4.3${OS_REL}; exit; } /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ && { echo i586-ncr-sysv4.3${OS_REL}; exit; } /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \ && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) echo m68k-unknown-lynxos${UNAME_RELEASE} exit ;; mc68030:UNIX_System_V:4.*:*) echo m68k-atari-sysv4 exit ;; TSUNAMI:LynxOS:2.*:*) echo sparc-unknown-lynxos${UNAME_RELEASE} exit ;; rs6000:LynxOS:2.*:*) echo rs6000-unknown-lynxos${UNAME_RELEASE} exit ;; PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*) echo powerpc-unknown-lynxos${UNAME_RELEASE} exit ;; SM[BE]S:UNIX_SV:*:*) echo mips-dde-sysv${UNAME_RELEASE} exit ;; RM*:ReliantUNIX-*:*:*) echo mips-sni-sysv4 exit ;; RM*:SINIX-*:*:*) echo mips-sni-sysv4 exit ;; *:SINIX-*:*:*) if uname -p 2>/dev/null >/dev/null ; then UNAME_MACHINE=`(uname -p) 2>/dev/null` echo ${UNAME_MACHINE}-sni-sysv4 else echo ns32k-sni-sysv fi exit ;; PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort # says echo i586-unisys-sysv4 exit ;; *:UNIX_System_V:4*:FTX*) # From Gerald Hewes . # How about differentiating between stratus architectures? -djm echo hppa1.1-stratus-sysv4 exit ;; *:*:*:FTX*) # From seanf@swdc.stratus.com. echo i860-stratus-sysv4 exit ;; i*86:VOS:*:*) # From Paul.Green@stratus.com. echo ${UNAME_MACHINE}-stratus-vos exit ;; *:VOS:*:*) # From Paul.Green@stratus.com. echo hppa1.1-stratus-vos exit ;; mc68*:A/UX:*:*) echo m68k-apple-aux${UNAME_RELEASE} exit ;; news*:NEWS-OS:6*:*) echo mips-sony-newsos6 exit ;; R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) if [ -d /usr/nec ]; then echo mips-nec-sysv${UNAME_RELEASE} else echo mips-unknown-sysv${UNAME_RELEASE} fi exit ;; BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. echo powerpc-be-beos exit ;; BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. echo powerpc-apple-beos exit ;; BePC:BeOS:*:*) # BeOS running on Intel PC compatible. echo i586-pc-beos exit ;; BePC:Haiku:*:*) # Haiku running on Intel PC compatible. echo i586-pc-haiku exit ;; x86_64:Haiku:*:*) echo x86_64-unknown-haiku exit ;; SX-4:SUPER-UX:*:*) echo sx4-nec-superux${UNAME_RELEASE} exit ;; SX-5:SUPER-UX:*:*) echo sx5-nec-superux${UNAME_RELEASE} exit ;; SX-6:SUPER-UX:*:*) echo sx6-nec-superux${UNAME_RELEASE} exit ;; SX-7:SUPER-UX:*:*) echo sx7-nec-superux${UNAME_RELEASE} exit ;; SX-8:SUPER-UX:*:*) echo sx8-nec-superux${UNAME_RELEASE} exit ;; SX-8R:SUPER-UX:*:*) echo sx8r-nec-superux${UNAME_RELEASE} exit ;; Power*:Rhapsody:*:*) echo powerpc-apple-rhapsody${UNAME_RELEASE} exit ;; *:Rhapsody:*:*) echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE} exit ;; *:Darwin:*:*) UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown eval $set_cc_for_build if test "$UNAME_PROCESSOR" = unknown ; then UNAME_PROCESSOR=powerpc fi if test `echo "$UNAME_RELEASE" | sed -e 's/\..*//'` -le 10 ; then if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \ (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ grep IS_64BIT_ARCH >/dev/null then case $UNAME_PROCESSOR in i386) UNAME_PROCESSOR=x86_64 ;; powerpc) UNAME_PROCESSOR=powerpc64 ;; esac fi fi elif test "$UNAME_PROCESSOR" = i386 ; then # Avoid executing cc on OS X 10.9, as it ships with a stub # that puts up a graphical alert prompting to install # developer tools. Any system running Mac OS X 10.7 or # later (Darwin 11 and later) is required to have a 64-bit # processor. This is not true of the ARM version of Darwin # that Apple uses in portable devices. UNAME_PROCESSOR=x86_64 fi echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE} exit ;; *:procnto*:*:* | *:QNX:[0123456789]*:*) UNAME_PROCESSOR=`uname -p` if test "$UNAME_PROCESSOR" = "x86"; then UNAME_PROCESSOR=i386 UNAME_MACHINE=pc fi echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE} exit ;; *:QNX:*:4*) echo i386-pc-qnx exit ;; NEO-?:NONSTOP_KERNEL:*:*) echo neo-tandem-nsk${UNAME_RELEASE} exit ;; NSE-*:NONSTOP_KERNEL:*:*) echo nse-tandem-nsk${UNAME_RELEASE} exit ;; NSR-?:NONSTOP_KERNEL:*:*) echo nsr-tandem-nsk${UNAME_RELEASE} exit ;; *:NonStop-UX:*:*) echo mips-compaq-nonstopux exit ;; BS2000:POSIX*:*:*) echo bs2000-siemens-sysv exit ;; DS/*:UNIX_System_V:*:*) echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE} exit ;; *:Plan9:*:*) # "uname -m" is not consistent, so use $cputype instead. 386 # is converted to i386 for consistency with other x86 # operating systems. if test "$cputype" = "386"; then UNAME_MACHINE=i386 else UNAME_MACHINE="$cputype" fi echo ${UNAME_MACHINE}-unknown-plan9 exit ;; *:TOPS-10:*:*) echo pdp10-unknown-tops10 exit ;; *:TENEX:*:*) echo pdp10-unknown-tenex exit ;; KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) echo pdp10-dec-tops20 exit ;; XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) echo pdp10-xkl-tops20 exit ;; *:TOPS-20:*:*) echo pdp10-unknown-tops20 exit ;; *:ITS:*:*) echo pdp10-unknown-its exit ;; SEI:*:*:SEIUX) echo mips-sei-seiux${UNAME_RELEASE} exit ;; *:DragonFly:*:*) echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` exit ;; *:*VMS:*:*) UNAME_MACHINE=`(uname -p) 2>/dev/null` case "${UNAME_MACHINE}" in A*) echo alpha-dec-vms ; exit ;; I*) echo ia64-dec-vms ; exit ;; V*) echo vax-dec-vms ; exit ;; esac ;; *:XENIX:*:SysV) echo i386-pc-xenix exit ;; i*86:skyos:*:*) echo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE}` | sed -e 's/ .*$//' exit ;; i*86:rdos:*:*) echo ${UNAME_MACHINE}-pc-rdos exit ;; i*86:AROS:*:*) echo ${UNAME_MACHINE}-pc-aros exit ;; x86_64:VMkernel:*:*) echo ${UNAME_MACHINE}-unknown-esx exit ;; esac cat >&2 < in order to provide the needed information to handle your system. config.guess timestamp = $timestamp uname -m = `(uname -m) 2>/dev/null || echo unknown` uname -r = `(uname -r) 2>/dev/null || echo unknown` uname -s = `(uname -s) 2>/dev/null || echo unknown` uname -v = `(uname -v) 2>/dev/null || echo unknown` /usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null` /bin/uname -X = `(/bin/uname -X) 2>/dev/null` hostinfo = `(hostinfo) 2>/dev/null` /bin/universe = `(/bin/universe) 2>/dev/null` /usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null` /bin/arch = `(/bin/arch) 2>/dev/null` /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` UNAME_MACHINE = ${UNAME_MACHINE} UNAME_RELEASE = ${UNAME_RELEASE} UNAME_SYSTEM = ${UNAME_SYSTEM} UNAME_VERSION = ${UNAME_VERSION} EOF exit 1 # Local variables: # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "timestamp='" # time-stamp-format: "%:y-%02m-%02d" # time-stamp-end: "'" # End: memcached-1.5.6/assoc.h0000664000175000017500000000076713242642567011662 00000000000000/* associative array */ void assoc_init(const int hashpower_init); item *assoc_find(const char *key, const size_t nkey, const uint32_t hv); int assoc_insert(item *item, const uint32_t hv); void assoc_delete(const char *key, const size_t nkey, const uint32_t hv); void do_assoc_move_next_bucket(void); int start_assoc_maintenance_thread(void); void stop_assoc_maintenance_thread(void); void assoc_start_expand(uint64_t curr_items); extern unsigned int hashpower; extern unsigned int item_lock_hashpower; memcached-1.5.6/jenkins_hash.c0000664000175000017500000003456513115057711013202 00000000000000/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ /* * Hash table * * The hash function used here is by Bob Jenkins, 1996: * * "By Bob Jenkins, 1996. bob_jenkins@burtleburtle.net. * You may use this code any way you wish, private, educational, * or commercial. It's free." * */ #include "memcached.h" #include "jenkins_hash.h" /* * Since the hash function does bit manipulation, it needs to know * whether it's big or little-endian. ENDIAN_LITTLE and ENDIAN_BIG * are set in the configure script. */ #if ENDIAN_BIG == 1 # define HASH_LITTLE_ENDIAN 0 # define HASH_BIG_ENDIAN 1 #else # if ENDIAN_LITTLE == 1 # define HASH_LITTLE_ENDIAN 1 # define HASH_BIG_ENDIAN 0 # else # define HASH_LITTLE_ENDIAN 0 # define HASH_BIG_ENDIAN 0 # endif #endif #define rot(x,k) (((x)<<(k)) ^ ((x)>>(32-(k)))) /* ------------------------------------------------------------------------------- mix -- mix 3 32-bit values reversibly. This is reversible, so any information in (a,b,c) before mix() is still in (a,b,c) after mix(). If four pairs of (a,b,c) inputs are run through mix(), or through mix() in reverse, there are at least 32 bits of the output that are sometimes the same for one pair and different for another pair. This was tested for: * pairs that differed by one bit, by two bits, in any combination of top bits of (a,b,c), or in any combination of bottom bits of (a,b,c). * "differ" is defined as +, -, ^, or ~^. For + and -, I transformed the output delta to a Gray code (a^(a>>1)) so a string of 1's (as is commonly produced by subtraction) look like a single 1-bit difference. * the base values were pseudorandom, all zero but one bit set, or all zero plus a counter that starts at zero. Some k values for my "a-=c; a^=rot(c,k); c+=b;" arrangement that satisfy this are 4 6 8 16 19 4 9 15 3 18 27 15 14 9 3 7 17 3 Well, "9 15 3 18 27 15" didn't quite get 32 bits diffing for "differ" defined as + with a one-bit base and a two-bit delta. I used http://burtleburtle.net/bob/hash/avalanche.html to choose the operations, constants, and arrangements of the variables. This does not achieve avalanche. There are input bits of (a,b,c) that fail to affect some output bits of (a,b,c), especially of a. The most thoroughly mixed value is c, but it doesn't really even achieve avalanche in c. This allows some parallelism. Read-after-writes are good at doubling the number of bits affected, so the goal of mixing pulls in the opposite direction as the goal of parallelism. I did what I could. Rotates seem to cost as much as shifts on every machine I could lay my hands on, and rotates are much kinder to the top and bottom bits, so I used rotates. ------------------------------------------------------------------------------- */ #define mix(a,b,c) \ { \ a -= c; a ^= rot(c, 4); c += b; \ b -= a; b ^= rot(a, 6); a += c; \ c -= b; c ^= rot(b, 8); b += a; \ a -= c; a ^= rot(c,16); c += b; \ b -= a; b ^= rot(a,19); a += c; \ c -= b; c ^= rot(b, 4); b += a; \ } /* ------------------------------------------------------------------------------- final -- final mixing of 3 32-bit values (a,b,c) into c Pairs of (a,b,c) values differing in only a few bits will usually produce values of c that look totally different. This was tested for * pairs that differed by one bit, by two bits, in any combination of top bits of (a,b,c), or in any combination of bottom bits of (a,b,c). * "differ" is defined as +, -, ^, or ~^. For + and -, I transformed the output delta to a Gray code (a^(a>>1)) so a string of 1's (as is commonly produced by subtraction) look like a single 1-bit difference. * the base values were pseudorandom, all zero but one bit set, or all zero plus a counter that starts at zero. These constants passed: 14 11 25 16 4 14 24 12 14 25 16 4 14 24 and these came close: 4 8 15 26 3 22 24 10 8 15 26 3 22 24 11 8 15 26 3 22 24 ------------------------------------------------------------------------------- */ #define final(a,b,c) \ { \ c ^= b; c -= rot(b,14); \ a ^= c; a -= rot(c,11); \ b ^= a; b -= rot(a,25); \ c ^= b; c -= rot(b,16); \ a ^= c; a -= rot(c,4); \ b ^= a; b -= rot(a,14); \ c ^= b; c -= rot(b,24); \ } #if HASH_LITTLE_ENDIAN == 1 uint32_t jenkins_hash( const void *key, /* the key to hash */ size_t length) /* length of the key */ { uint32_t a,b,c; /* internal state */ union { const void *ptr; size_t i; } u; /* needed for Mac Powerbook G4 */ /* Set up the internal state */ a = b = c = 0xdeadbeef + ((uint32_t)length) + 0; u.ptr = key; if (HASH_LITTLE_ENDIAN && ((u.i & 0x3) == 0)) { const uint32_t *k = key; /* read 32-bit chunks */ #ifdef VALGRIND const uint8_t *k8; #endif /* ifdef VALGRIND */ /*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */ while (length > 12) { a += k[0]; b += k[1]; c += k[2]; mix(a,b,c); length -= 12; k += 3; } /*----------------------------- handle the last (probably partial) block */ /* * "k[2]&0xffffff" actually reads beyond the end of the string, but * then masks off the part it's not allowed to read. Because the * string is aligned, the masked-off tail is in the same word as the * rest of the string. Every machine with memory protection I've seen * does it on word boundaries, so is OK with this. But VALGRIND will * still catch it and complain. The masking trick does make the hash * noticeably faster for short strings (like English words). */ #ifndef VALGRIND switch(length) { case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; case 11: c+=k[2]&0xffffff; b+=k[1]; a+=k[0]; break; case 10: c+=k[2]&0xffff; b+=k[1]; a+=k[0]; break; case 9 : c+=k[2]&0xff; b+=k[1]; a+=k[0]; break; case 8 : b+=k[1]; a+=k[0]; break; case 7 : b+=k[1]&0xffffff; a+=k[0]; break; case 6 : b+=k[1]&0xffff; a+=k[0]; break; case 5 : b+=k[1]&0xff; a+=k[0]; break; case 4 : a+=k[0]; break; case 3 : a+=k[0]&0xffffff; break; case 2 : a+=k[0]&0xffff; break; case 1 : a+=k[0]&0xff; break; case 0 : return c; /* zero length strings require no mixing */ } #else /* make valgrind happy */ k8 = (const uint8_t *)k; switch(length) { case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; case 11: c+=((uint32_t)k8[10])<<16; /* fall through */ case 10: c+=((uint32_t)k8[9])<<8; /* fall through */ case 9 : c+=k8[8]; /* fall through */ case 8 : b+=k[1]; a+=k[0]; break; case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */ case 6 : b+=((uint32_t)k8[5])<<8; /* fall through */ case 5 : b+=k8[4]; /* fall through */ case 4 : a+=k[0]; break; case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */ case 2 : a+=((uint32_t)k8[1])<<8; /* fall through */ case 1 : a+=k8[0]; break; case 0 : return c; /* zero length strings require no mixing */ } #endif /* !valgrind */ } else if (HASH_LITTLE_ENDIAN && ((u.i & 0x1) == 0)) { const uint16_t *k = key; /* read 16-bit chunks */ const uint8_t *k8; /*--------------- all but last block: aligned reads and different mixing */ while (length > 12) { a += k[0] + (((uint32_t)k[1])<<16); b += k[2] + (((uint32_t)k[3])<<16); c += k[4] + (((uint32_t)k[5])<<16); mix(a,b,c); length -= 12; k += 6; } /*----------------------------- handle the last (probably partial) block */ k8 = (const uint8_t *)k; switch(length) { case 12: c+=k[4]+(((uint32_t)k[5])<<16); b+=k[2]+(((uint32_t)k[3])<<16); a+=k[0]+(((uint32_t)k[1])<<16); break; case 11: c+=((uint32_t)k8[10])<<16; /* @fallthrough */ case 10: c+=k[4]; /* @fallthrough@ */ b+=k[2]+(((uint32_t)k[3])<<16); a+=k[0]+(((uint32_t)k[1])<<16); break; case 9 : c+=k8[8]; /* @fallthrough */ case 8 : b+=k[2]+(((uint32_t)k[3])<<16); a+=k[0]+(((uint32_t)k[1])<<16); break; case 7 : b+=((uint32_t)k8[6])<<16; /* @fallthrough */ case 6 : b+=k[2]; a+=k[0]+(((uint32_t)k[1])<<16); break; case 5 : b+=k8[4]; /* @fallthrough */ case 4 : a+=k[0]+(((uint32_t)k[1])<<16); break; case 3 : a+=((uint32_t)k8[2])<<16; /* @fallthrough */ case 2 : a+=k[0]; break; case 1 : a+=k8[0]; break; case 0 : return c; /* zero length strings require no mixing */ } } else { /* need to read the key one byte at a time */ const uint8_t *k = key; /*--------------- all but the last block: affect some 32 bits of (a,b,c) */ while (length > 12) { a += k[0]; a += ((uint32_t)k[1])<<8; a += ((uint32_t)k[2])<<16; a += ((uint32_t)k[3])<<24; b += k[4]; b += ((uint32_t)k[5])<<8; b += ((uint32_t)k[6])<<16; b += ((uint32_t)k[7])<<24; c += k[8]; c += ((uint32_t)k[9])<<8; c += ((uint32_t)k[10])<<16; c += ((uint32_t)k[11])<<24; mix(a,b,c); length -= 12; k += 12; } /*-------------------------------- last block: affect all 32 bits of (c) */ switch(length) /* all the case statements fall through */ { case 12: c+=((uint32_t)k[11])<<24; case 11: c+=((uint32_t)k[10])<<16; case 10: c+=((uint32_t)k[9])<<8; case 9 : c+=k[8]; case 8 : b+=((uint32_t)k[7])<<24; case 7 : b+=((uint32_t)k[6])<<16; case 6 : b+=((uint32_t)k[5])<<8; case 5 : b+=k[4]; case 4 : a+=((uint32_t)k[3])<<24; case 3 : a+=((uint32_t)k[2])<<16; case 2 : a+=((uint32_t)k[1])<<8; case 1 : a+=k[0]; break; case 0 : return c; /* zero length strings require no mixing */ } } final(a,b,c); return c; /* zero length strings require no mixing */ } #elif HASH_BIG_ENDIAN == 1 /* * hashbig(): * This is the same as hashword() on big-endian machines. It is different * from hashlittle() on all machines. hashbig() takes advantage of * big-endian byte ordering. */ uint32_t jenkins_hash( const void *key, size_t length) { uint32_t a,b,c; union { const void *ptr; size_t i; } u; /* to cast key to (size_t) happily */ /* Set up the internal state */ a = b = c = 0xdeadbeef + ((uint32_t)length) + 0; u.ptr = key; if (HASH_BIG_ENDIAN && ((u.i & 0x3) == 0)) { const uint32_t *k = key; /* read 32-bit chunks */ #ifdef VALGRIND const uint8_t *k8; #endif /* ifdef VALGRIND */ /*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */ while (length > 12) { a += k[0]; b += k[1]; c += k[2]; mix(a,b,c); length -= 12; k += 3; } /*----------------------------- handle the last (probably partial) block */ /* * "k[2]<<8" actually reads beyond the end of the string, but * then shifts out the part it's not allowed to read. Because the * string is aligned, the illegal read is in the same word as the * rest of the string. Every machine with memory protection I've seen * does it on word boundaries, so is OK with this. But VALGRIND will * still catch it and complain. The masking trick does make the hash * noticeably faster for short strings (like English words). */ #ifndef VALGRIND switch(length) { case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; case 11: c+=k[2]&0xffffff00; b+=k[1]; a+=k[0]; break; case 10: c+=k[2]&0xffff0000; b+=k[1]; a+=k[0]; break; case 9 : c+=k[2]&0xff000000; b+=k[1]; a+=k[0]; break; case 8 : b+=k[1]; a+=k[0]; break; case 7 : b+=k[1]&0xffffff00; a+=k[0]; break; case 6 : b+=k[1]&0xffff0000; a+=k[0]; break; case 5 : b+=k[1]&0xff000000; a+=k[0]; break; case 4 : a+=k[0]; break; case 3 : a+=k[0]&0xffffff00; break; case 2 : a+=k[0]&0xffff0000; break; case 1 : a+=k[0]&0xff000000; break; case 0 : return c; /* zero length strings require no mixing */ } #else /* make valgrind happy */ k8 = (const uint8_t *)k; switch(length) /* all the case statements fall through */ { case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; case 11: c+=((uint32_t)k8[10])<<8; /* fall through */ case 10: c+=((uint32_t)k8[9])<<16; /* fall through */ case 9 : c+=((uint32_t)k8[8])<<24; /* fall through */ case 8 : b+=k[1]; a+=k[0]; break; case 7 : b+=((uint32_t)k8[6])<<8; /* fall through */ case 6 : b+=((uint32_t)k8[5])<<16; /* fall through */ case 5 : b+=((uint32_t)k8[4])<<24; /* fall through */ case 4 : a+=k[0]; break; case 3 : a+=((uint32_t)k8[2])<<8; /* fall through */ case 2 : a+=((uint32_t)k8[1])<<16; /* fall through */ case 1 : a+=((uint32_t)k8[0])<<24; break; case 0 : return c; } #endif /* !VALGRIND */ } else { /* need to read the key one byte at a time */ const uint8_t *k = key; /*--------------- all but the last block: affect some 32 bits of (a,b,c) */ while (length > 12) { a += ((uint32_t)k[0])<<24; a += ((uint32_t)k[1])<<16; a += ((uint32_t)k[2])<<8; a += ((uint32_t)k[3]); b += ((uint32_t)k[4])<<24; b += ((uint32_t)k[5])<<16; b += ((uint32_t)k[6])<<8; b += ((uint32_t)k[7]); c += ((uint32_t)k[8])<<24; c += ((uint32_t)k[9])<<16; c += ((uint32_t)k[10])<<8; c += ((uint32_t)k[11]); mix(a,b,c); length -= 12; k += 12; } /*-------------------------------- last block: affect all 32 bits of (c) */ switch(length) /* all the case statements fall through */ { case 12: c+=k[11]; case 11: c+=((uint32_t)k[10])<<8; case 10: c+=((uint32_t)k[9])<<16; case 9 : c+=((uint32_t)k[8])<<24; case 8 : b+=k[7]; case 7 : b+=((uint32_t)k[6])<<8; case 6 : b+=((uint32_t)k[5])<<16; case 5 : b+=((uint32_t)k[4])<<24; case 4 : a+=k[3]; case 3 : a+=((uint32_t)k[2])<<8; case 2 : a+=((uint32_t)k[1])<<16; case 1 : a+=((uint32_t)k[0])<<24; break; case 0 : return c; } } final(a,b,c); return c; } #else /* HASH_XXX_ENDIAN == 1 */ #error Must define HASH_BIG_ENDIAN or HASH_LITTLE_ENDIAN #endif /* HASH_XXX_ENDIAN == 1 */ memcached-1.5.6/slabs.c0000664000175000017500000012516013242642567011644 00000000000000/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ /* * Slabs memory allocation, based on powers-of-N. Slabs are up to 1MB in size * and are divided into chunks. The chunk sizes start off at the size of the * "item" structure plus space for a small key and value. They increase by * a multiplier factor from there, up to half the maximum slab size. The last * slab size is always 1MB, since that's the maximum item size allowed by the * memcached protocol. */ #include "memcached.h" #include #include #include #include #include #include #include #include #include #include #include #include //#define DEBUG_SLAB_MOVER /* powers-of-N allocation structures */ typedef struct { unsigned int size; /* sizes of items */ unsigned int perslab; /* how many items per slab */ void *slots; /* list of item ptrs */ unsigned int sl_curr; /* total free items in list */ unsigned int slabs; /* how many slabs were allocated for this class */ void **slab_list; /* array of slab pointers */ unsigned int list_size; /* size of prev array */ size_t requested; /* The number of requested bytes */ } slabclass_t; static slabclass_t slabclass[MAX_NUMBER_OF_SLAB_CLASSES]; static size_t mem_limit = 0; static size_t mem_malloced = 0; /* If the memory limit has been hit once. Used as a hint to decide when to * early-wake the LRU maintenance thread */ static bool mem_limit_reached = false; static int power_largest; static void *mem_base = NULL; static void *mem_current = NULL; static size_t mem_avail = 0; #ifdef EXTSTORE static void *storage = NULL; #endif /** * Access to the slab allocator is protected by this lock */ static pthread_mutex_t slabs_lock = PTHREAD_MUTEX_INITIALIZER; static pthread_mutex_t slabs_rebalance_lock = PTHREAD_MUTEX_INITIALIZER; /* * Forward Declarations */ static int grow_slab_list (const unsigned int id); static int do_slabs_newslab(const unsigned int id); static void *memory_allocate(size_t size); static void do_slabs_free(void *ptr, const size_t size, unsigned int id); /* Preallocate as many slab pages as possible (called from slabs_init) on start-up, so users don't get confused out-of-memory errors when they do have free (in-slab) space, but no space to make new slabs. if maxslabs is 18 (POWER_LARGEST - POWER_SMALLEST + 1), then all slab types can be made. if max memory is less than 18 MB, only the smaller ones will be made. */ static void slabs_preallocate (const unsigned int maxslabs); #ifdef EXTSTORE void slabs_set_storage(void *arg) { storage = arg; } #endif /* * Figures out which slab class (chunk size) is required to store an item of * a given size. * * Given object size, return id to use when allocating/freeing memory for object * 0 means error: can't store such a large object */ unsigned int slabs_clsid(const size_t size) { int res = POWER_SMALLEST; if (size == 0 || size > settings.item_size_max) return 0; while (size > slabclass[res].size) if (res++ == power_largest) /* won't fit in the biggest slab */ return power_largest; return res; } /** * Determines the chunk sizes and initializes the slab class descriptors * accordingly. */ void slabs_init(const size_t limit, const double factor, const bool prealloc, const uint32_t *slab_sizes) { int i = POWER_SMALLEST - 1; unsigned int size = sizeof(item) + settings.chunk_size; mem_limit = limit; if (prealloc) { /* Allocate everything in a big chunk with malloc */ mem_base = malloc(mem_limit); if (mem_base != NULL) { mem_current = mem_base; mem_avail = mem_limit; } else { fprintf(stderr, "Warning: Failed to allocate requested memory in" " one large chunk.\nWill allocate in smaller chunks\n"); } } memset(slabclass, 0, sizeof(slabclass)); while (++i < MAX_NUMBER_OF_SLAB_CLASSES-1) { if (slab_sizes != NULL) { if (slab_sizes[i-1] == 0) break; size = slab_sizes[i-1]; } else if (size >= settings.slab_chunk_size_max / factor) { break; } /* Make sure items are always n-byte aligned */ if (size % CHUNK_ALIGN_BYTES) size += CHUNK_ALIGN_BYTES - (size % CHUNK_ALIGN_BYTES); slabclass[i].size = size; slabclass[i].perslab = settings.slab_page_size / slabclass[i].size; if (slab_sizes == NULL) size *= factor; if (settings.verbose > 1) { fprintf(stderr, "slab class %3d: chunk size %9u perslab %7u\n", i, slabclass[i].size, slabclass[i].perslab); } } power_largest = i; slabclass[power_largest].size = settings.slab_chunk_size_max; slabclass[power_largest].perslab = settings.slab_page_size / settings.slab_chunk_size_max; if (settings.verbose > 1) { fprintf(stderr, "slab class %3d: chunk size %9u perslab %7u\n", i, slabclass[i].size, slabclass[i].perslab); } /* for the test suite: faking of how much we've already malloc'd */ { char *t_initial_malloc = getenv("T_MEMD_INITIAL_MALLOC"); if (t_initial_malloc) { mem_malloced = (size_t)atol(t_initial_malloc); } } if (prealloc) { slabs_preallocate(power_largest); } } void slabs_prefill_global(void) { void *ptr; slabclass_t *p = &slabclass[0]; int len = settings.slab_page_size; while (mem_malloced < mem_limit && (ptr = memory_allocate(len)) != NULL) { grow_slab_list(0); p->slab_list[p->slabs++] = ptr; } mem_limit_reached = true; } static void slabs_preallocate (const unsigned int maxslabs) { int i; unsigned int prealloc = 0; /* pre-allocate a 1MB slab in every size class so people don't get confused by non-intuitive "SERVER_ERROR out of memory" messages. this is the most common question on the mailing list. if you really don't want this, you can rebuild without these three lines. */ for (i = POWER_SMALLEST; i < MAX_NUMBER_OF_SLAB_CLASSES; i++) { if (++prealloc > maxslabs) return; if (do_slabs_newslab(i) == 0) { fprintf(stderr, "Error while preallocating slab memory!\n" "If using -L or other prealloc options, max memory must be " "at least %d megabytes.\n", power_largest); exit(1); } } } static int grow_slab_list (const unsigned int id) { slabclass_t *p = &slabclass[id]; if (p->slabs == p->list_size) { size_t new_size = (p->list_size != 0) ? p->list_size * 2 : 16; void *new_list = realloc(p->slab_list, new_size * sizeof(void *)); if (new_list == 0) return 0; p->list_size = new_size; p->slab_list = new_list; } return 1; } static void split_slab_page_into_freelist(char *ptr, const unsigned int id) { slabclass_t *p = &slabclass[id]; int x; for (x = 0; x < p->perslab; x++) { do_slabs_free(ptr, 0, id); ptr += p->size; } } /* Fast FIFO queue */ static void *get_page_from_global_pool(void) { slabclass_t *p = &slabclass[SLAB_GLOBAL_PAGE_POOL]; if (p->slabs < 1) { return NULL; } char *ret = p->slab_list[p->slabs - 1]; p->slabs--; return ret; } static int do_slabs_newslab(const unsigned int id) { slabclass_t *p = &slabclass[id]; slabclass_t *g = &slabclass[SLAB_GLOBAL_PAGE_POOL]; int len = (settings.slab_reassign || settings.slab_chunk_size_max != settings.slab_page_size) ? settings.slab_page_size : p->size * p->perslab; char *ptr; if ((mem_limit && mem_malloced + len > mem_limit && p->slabs > 0 && g->slabs == 0)) { mem_limit_reached = true; MEMCACHED_SLABS_SLABCLASS_ALLOCATE_FAILED(id); return 0; } if ((grow_slab_list(id) == 0) || (((ptr = get_page_from_global_pool()) == NULL) && ((ptr = memory_allocate((size_t)len)) == 0))) { MEMCACHED_SLABS_SLABCLASS_ALLOCATE_FAILED(id); return 0; } memset(ptr, 0, (size_t)len); split_slab_page_into_freelist(ptr, id); p->slab_list[p->slabs++] = ptr; MEMCACHED_SLABS_SLABCLASS_ALLOCATE(id); return 1; } /*@null@*/ static void *do_slabs_alloc(const size_t size, unsigned int id, uint64_t *total_bytes, unsigned int flags) { slabclass_t *p; void *ret = NULL; item *it = NULL; if (id < POWER_SMALLEST || id > power_largest) { MEMCACHED_SLABS_ALLOCATE_FAILED(size, 0); return NULL; } p = &slabclass[id]; assert(p->sl_curr == 0 || ((item *)p->slots)->slabs_clsid == 0); if (total_bytes != NULL) { *total_bytes = p->requested; } assert(size <= p->size); /* fail unless we have space at the end of a recently allocated page, we have something on our freelist, or we could allocate a new page */ if (p->sl_curr == 0 && flags != SLABS_ALLOC_NO_NEWPAGE) { do_slabs_newslab(id); } if (p->sl_curr != 0) { /* return off our freelist */ it = (item *)p->slots; p->slots = it->next; if (it->next) it->next->prev = 0; /* Kill flag and initialize refcount here for lock safety in slab * mover's freeness detection. */ it->it_flags &= ~ITEM_SLABBED; it->refcount = 1; p->sl_curr--; ret = (void *)it; } else { ret = NULL; } if (ret) { p->requested += size; MEMCACHED_SLABS_ALLOCATE(size, id, p->size, ret); } else { MEMCACHED_SLABS_ALLOCATE_FAILED(size, id); } return ret; } static void do_slabs_free_chunked(item *it, const size_t size) { item_chunk *chunk = (item_chunk *) ITEM_data(it); slabclass_t *p; it->it_flags = ITEM_SLABBED; it->slabs_clsid = 0; it->prev = 0; // header object's original classid is stored in chunk. p = &slabclass[chunk->orig_clsid]; if (chunk->next) { chunk = chunk->next; chunk->prev = 0; } else { // header with no attached chunk chunk = NULL; } // return the header object. // TODO: This is in three places, here and in do_slabs_free(). it->prev = 0; it->next = p->slots; if (it->next) it->next->prev = it; p->slots = it; p->sl_curr++; // TODO: macro p->requested -= it->nkey + 1 + it->nsuffix + sizeof(item) + sizeof(item_chunk); if (settings.use_cas) { p->requested -= sizeof(uint64_t); } item_chunk *next_chunk; while (chunk) { assert(chunk->it_flags == ITEM_CHUNK); chunk->it_flags = ITEM_SLABBED; p = &slabclass[chunk->slabs_clsid]; chunk->slabs_clsid = 0; next_chunk = chunk->next; chunk->prev = 0; chunk->next = p->slots; if (chunk->next) chunk->next->prev = chunk; p->slots = chunk; p->sl_curr++; p->requested -= chunk->size + sizeof(item_chunk); chunk = next_chunk; } return; } static void do_slabs_free(void *ptr, const size_t size, unsigned int id) { slabclass_t *p; item *it; assert(id >= POWER_SMALLEST && id <= power_largest); if (id < POWER_SMALLEST || id > power_largest) return; MEMCACHED_SLABS_FREE(size, id, ptr); p = &slabclass[id]; it = (item *)ptr; if ((it->it_flags & ITEM_CHUNKED) == 0) { #ifdef EXTSTORE bool is_hdr = it->it_flags & ITEM_HDR; #endif it->it_flags = ITEM_SLABBED; it->slabs_clsid = 0; it->prev = 0; it->next = p->slots; if (it->next) it->next->prev = it; p->slots = it; p->sl_curr++; #ifdef EXTSTORE if (!is_hdr) { p->requested -= size; } else { p->requested -= (size - it->nbytes) + sizeof(item_hdr); } #else p->requested -= size; #endif } else { do_slabs_free_chunked(it, size); } return; } /* With refactoring of the various stats code the automover won't need a * custom function here. */ void fill_slab_stats_automove(slab_stats_automove *am) { int n; pthread_mutex_lock(&slabs_lock); for (n = 0; n < MAX_NUMBER_OF_SLAB_CLASSES; n++) { slabclass_t *p = &slabclass[n]; slab_stats_automove *cur = &am[n]; cur->chunks_per_page = p->perslab; cur->free_chunks = p->sl_curr; cur->total_pages = p->slabs; cur->chunk_size = p->size; } pthread_mutex_unlock(&slabs_lock); } /* TODO: slabs_available_chunks should grow up to encompass this. * mem_flag is redundant with the other function. */ unsigned int global_page_pool_size(bool *mem_flag) { unsigned int ret = 0; pthread_mutex_lock(&slabs_lock); if (mem_flag != NULL) *mem_flag = mem_malloced >= mem_limit ? true : false; ret = slabclass[SLAB_GLOBAL_PAGE_POOL].slabs; pthread_mutex_unlock(&slabs_lock); return ret; } static int nz_strcmp(int nzlength, const char *nz, const char *z) { int zlength=strlen(z); return (zlength == nzlength) && (strncmp(nz, z, zlength) == 0) ? 0 : -1; } bool get_stats(const char *stat_type, int nkey, ADD_STAT add_stats, void *c) { bool ret = true; if (add_stats != NULL) { if (!stat_type) { /* prepare general statistics for the engine */ STATS_LOCK(); APPEND_STAT("bytes", "%llu", (unsigned long long)stats_state.curr_bytes); APPEND_STAT("curr_items", "%llu", (unsigned long long)stats_state.curr_items); APPEND_STAT("total_items", "%llu", (unsigned long long)stats.total_items); STATS_UNLOCK(); pthread_mutex_lock(&slabs_lock); APPEND_STAT("slab_global_page_pool", "%u", slabclass[SLAB_GLOBAL_PAGE_POOL].slabs); pthread_mutex_unlock(&slabs_lock); item_stats_totals(add_stats, c); } else if (nz_strcmp(nkey, stat_type, "items") == 0) { item_stats(add_stats, c); } else if (nz_strcmp(nkey, stat_type, "slabs") == 0) { slabs_stats(add_stats, c); } else if (nz_strcmp(nkey, stat_type, "sizes") == 0) { item_stats_sizes(add_stats, c); } else if (nz_strcmp(nkey, stat_type, "sizes_enable") == 0) { item_stats_sizes_enable(add_stats, c); } else if (nz_strcmp(nkey, stat_type, "sizes_disable") == 0) { item_stats_sizes_disable(add_stats, c); } else { ret = false; } } else { ret = false; } return ret; } /*@null@*/ static void do_slabs_stats(ADD_STAT add_stats, void *c) { int i, total; /* Get the per-thread stats which contain some interesting aggregates */ struct thread_stats thread_stats; threadlocal_stats_aggregate(&thread_stats); total = 0; for(i = POWER_SMALLEST; i <= power_largest; i++) { slabclass_t *p = &slabclass[i]; if (p->slabs != 0) { uint32_t perslab, slabs; slabs = p->slabs; perslab = p->perslab; char key_str[STAT_KEY_LEN]; char val_str[STAT_VAL_LEN]; int klen = 0, vlen = 0; APPEND_NUM_STAT(i, "chunk_size", "%u", p->size); APPEND_NUM_STAT(i, "chunks_per_page", "%u", perslab); APPEND_NUM_STAT(i, "total_pages", "%u", slabs); APPEND_NUM_STAT(i, "total_chunks", "%u", slabs * perslab); APPEND_NUM_STAT(i, "used_chunks", "%u", slabs*perslab - p->sl_curr); APPEND_NUM_STAT(i, "free_chunks", "%u", p->sl_curr); /* Stat is dead, but displaying zero instead of removing it. */ APPEND_NUM_STAT(i, "free_chunks_end", "%u", 0); APPEND_NUM_STAT(i, "mem_requested", "%llu", (unsigned long long)p->requested); APPEND_NUM_STAT(i, "get_hits", "%llu", (unsigned long long)thread_stats.slab_stats[i].get_hits); APPEND_NUM_STAT(i, "cmd_set", "%llu", (unsigned long long)thread_stats.slab_stats[i].set_cmds); APPEND_NUM_STAT(i, "delete_hits", "%llu", (unsigned long long)thread_stats.slab_stats[i].delete_hits); APPEND_NUM_STAT(i, "incr_hits", "%llu", (unsigned long long)thread_stats.slab_stats[i].incr_hits); APPEND_NUM_STAT(i, "decr_hits", "%llu", (unsigned long long)thread_stats.slab_stats[i].decr_hits); APPEND_NUM_STAT(i, "cas_hits", "%llu", (unsigned long long)thread_stats.slab_stats[i].cas_hits); APPEND_NUM_STAT(i, "cas_badval", "%llu", (unsigned long long)thread_stats.slab_stats[i].cas_badval); APPEND_NUM_STAT(i, "touch_hits", "%llu", (unsigned long long)thread_stats.slab_stats[i].touch_hits); total++; } } /* add overall slab stats and append terminator */ APPEND_STAT("active_slabs", "%d", total); APPEND_STAT("total_malloced", "%llu", (unsigned long long)mem_malloced); add_stats(NULL, 0, NULL, 0, c); } static void *memory_allocate(size_t size) { void *ret; if (mem_base == NULL) { /* We are not using a preallocated large memory chunk */ ret = malloc(size); } else { ret = mem_current; if (size > mem_avail) { return NULL; } /* mem_current pointer _must_ be aligned!!! */ if (size % CHUNK_ALIGN_BYTES) { size += CHUNK_ALIGN_BYTES - (size % CHUNK_ALIGN_BYTES); } mem_current = ((char*)mem_current) + size; if (size < mem_avail) { mem_avail -= size; } else { mem_avail = 0; } } mem_malloced += size; return ret; } /* Must only be used if all pages are item_size_max */ static void memory_release() { void *p = NULL; if (mem_base != NULL) return; if (!settings.slab_reassign) return; while (mem_malloced > mem_limit && (p = get_page_from_global_pool()) != NULL) { free(p); mem_malloced -= settings.slab_page_size; } } void *slabs_alloc(size_t size, unsigned int id, uint64_t *total_bytes, unsigned int flags) { void *ret; pthread_mutex_lock(&slabs_lock); ret = do_slabs_alloc(size, id, total_bytes, flags); pthread_mutex_unlock(&slabs_lock); return ret; } void slabs_free(void *ptr, size_t size, unsigned int id) { pthread_mutex_lock(&slabs_lock); do_slabs_free(ptr, size, id); pthread_mutex_unlock(&slabs_lock); } void slabs_stats(ADD_STAT add_stats, void *c) { pthread_mutex_lock(&slabs_lock); do_slabs_stats(add_stats, c); pthread_mutex_unlock(&slabs_lock); } static bool do_slabs_adjust_mem_limit(size_t new_mem_limit) { /* Cannot adjust memory limit at runtime if prealloc'ed */ if (mem_base != NULL) return false; settings.maxbytes = new_mem_limit; mem_limit = new_mem_limit; mem_limit_reached = false; /* Will reset on next alloc */ memory_release(); /* free what might already be in the global pool */ return true; } bool slabs_adjust_mem_limit(size_t new_mem_limit) { bool ret; pthread_mutex_lock(&slabs_lock); ret = do_slabs_adjust_mem_limit(new_mem_limit); pthread_mutex_unlock(&slabs_lock); return ret; } void slabs_adjust_mem_requested(unsigned int id, size_t old, size_t ntotal) { pthread_mutex_lock(&slabs_lock); slabclass_t *p; if (id < POWER_SMALLEST || id > power_largest) { fprintf(stderr, "Internal error! Invalid slab class\n"); abort(); } p = &slabclass[id]; p->requested = p->requested - old + ntotal; pthread_mutex_unlock(&slabs_lock); } unsigned int slabs_available_chunks(const unsigned int id, bool *mem_flag, uint64_t *total_bytes, unsigned int *chunks_perslab) { unsigned int ret; slabclass_t *p; pthread_mutex_lock(&slabs_lock); p = &slabclass[id]; ret = p->sl_curr; if (mem_flag != NULL) *mem_flag = mem_malloced >= mem_limit ? true : false; if (total_bytes != NULL) *total_bytes = p->requested; if (chunks_perslab != NULL) *chunks_perslab = p->perslab; pthread_mutex_unlock(&slabs_lock); return ret; } /* The slabber system could avoid needing to understand much, if anything, * about items if callbacks were strategically used. Due to how the slab mover * works, certain flag bits can only be adjusted while holding the slabs lock. * Using these functions, isolate sections of code needing this and turn them * into callbacks when an interface becomes more obvious. */ void slabs_mlock(void) { pthread_mutex_lock(&slabs_lock); } void slabs_munlock(void) { pthread_mutex_unlock(&slabs_lock); } static pthread_cond_t slab_rebalance_cond = PTHREAD_COND_INITIALIZER; static volatile int do_run_slab_thread = 1; static volatile int do_run_slab_rebalance_thread = 1; #define DEFAULT_SLAB_BULK_CHECK 1 int slab_bulk_check = DEFAULT_SLAB_BULK_CHECK; static int slab_rebalance_start(void) { slabclass_t *s_cls; int no_go = 0; pthread_mutex_lock(&slabs_lock); if (slab_rebal.s_clsid < SLAB_GLOBAL_PAGE_POOL || slab_rebal.s_clsid > power_largest || slab_rebal.d_clsid < SLAB_GLOBAL_PAGE_POOL || slab_rebal.d_clsid > power_largest || slab_rebal.s_clsid == slab_rebal.d_clsid) no_go = -2; s_cls = &slabclass[slab_rebal.s_clsid]; if (!grow_slab_list(slab_rebal.d_clsid)) { no_go = -1; } if (s_cls->slabs < 2) no_go = -3; if (no_go != 0) { pthread_mutex_unlock(&slabs_lock); return no_go; /* Should use a wrapper function... */ } /* Always kill the first available slab page as it is most likely to * contain the oldest items */ slab_rebal.slab_start = s_cls->slab_list[0]; slab_rebal.slab_end = (char *)slab_rebal.slab_start + (s_cls->size * s_cls->perslab); slab_rebal.slab_pos = slab_rebal.slab_start; slab_rebal.done = 0; // Don't need to do chunk move work if page is in global pool. if (slab_rebal.s_clsid == SLAB_GLOBAL_PAGE_POOL) { slab_rebal.done = 1; } slab_rebalance_signal = 2; if (settings.verbose > 1) { fprintf(stderr, "Started a slab rebalance\n"); } pthread_mutex_unlock(&slabs_lock); STATS_LOCK(); stats_state.slab_reassign_running = true; STATS_UNLOCK(); return 0; } /* CALLED WITH slabs_lock HELD */ static void *slab_rebalance_alloc(const size_t size, unsigned int id) { slabclass_t *s_cls; s_cls = &slabclass[slab_rebal.s_clsid]; int x; item *new_it = NULL; for (x = 0; x < s_cls->perslab; x++) { new_it = do_slabs_alloc(size, id, NULL, SLABS_ALLOC_NO_NEWPAGE); /* check that memory isn't within the range to clear */ if (new_it == NULL) { break; } if ((void *)new_it >= slab_rebal.slab_start && (void *)new_it < slab_rebal.slab_end) { /* Pulled something we intend to free. Mark it as freed since * we've already done the work of unlinking it from the freelist. */ s_cls->requested -= size; new_it->refcount = 0; new_it->it_flags = ITEM_SLABBED|ITEM_FETCHED; #ifdef DEBUG_SLAB_MOVER memcpy(ITEM_key(new_it), "deadbeef", 8); #endif new_it = NULL; slab_rebal.inline_reclaim++; } else { break; } } return new_it; } /* CALLED WITH slabs_lock HELD */ /* detaches item/chunk from freelist. */ static void slab_rebalance_cut_free(slabclass_t *s_cls, item *it) { /* Ensure this was on the freelist and nothing else. */ assert(it->it_flags == ITEM_SLABBED); if (s_cls->slots == it) { s_cls->slots = it->next; } if (it->next) it->next->prev = it->prev; if (it->prev) it->prev->next = it->next; s_cls->sl_curr--; } enum move_status { MOVE_PASS=0, MOVE_FROM_SLAB, MOVE_FROM_LRU, MOVE_BUSY, MOVE_LOCKED }; #define SLAB_MOVE_MAX_LOOPS 1000 /* refcount == 0 is safe since nobody can incr while item_lock is held. * refcount != 0 is impossible since flags/etc can be modified in other * threads. instead, note we found a busy one and bail. logic in do_item_get * will prevent busy items from continuing to be busy * NOTE: This is checking it_flags outside of an item lock. I believe this * works since it_flags is 8 bits, and we're only ever comparing a single bit * regardless. ITEM_SLABBED bit will always be correct since we're holding the * lock which modifies that bit. ITEM_LINKED won't exist if we're between an * item having ITEM_SLABBED removed, and the key hasn't been added to the item * yet. The memory barrier from the slabs lock should order the key write and the * flags to the item? * If ITEM_LINKED did exist and was just removed, but we still see it, that's * still safe since it will have a valid key, which we then lock, and then * recheck everything. * This may not be safe on all platforms; If not, slabs_alloc() will need to * seed the item key while holding slabs_lock. */ static int slab_rebalance_move(void) { slabclass_t *s_cls; int x; int was_busy = 0; int refcount = 0; uint32_t hv; void *hold_lock; enum move_status status = MOVE_PASS; pthread_mutex_lock(&slabs_lock); s_cls = &slabclass[slab_rebal.s_clsid]; for (x = 0; x < slab_bulk_check; x++) { hv = 0; hold_lock = NULL; item *it = slab_rebal.slab_pos; item_chunk *ch = NULL; status = MOVE_PASS; if (it->it_flags & ITEM_CHUNK) { /* This chunk is a chained part of a larger item. */ ch = (item_chunk *) it; /* Instead, we use the head chunk to find the item and effectively * lock the entire structure. If a chunk has ITEM_CHUNK flag, its * head cannot be slabbed, so the normal routine is safe. */ it = ch->head; assert(it->it_flags & ITEM_CHUNKED); } /* ITEM_FETCHED when ITEM_SLABBED is overloaded to mean we've cleared * the chunk for move. Only these two flags should exist. */ if (it->it_flags != (ITEM_SLABBED|ITEM_FETCHED)) { /* ITEM_SLABBED can only be added/removed under the slabs_lock */ if (it->it_flags & ITEM_SLABBED) { assert(ch == NULL); slab_rebalance_cut_free(s_cls, it); status = MOVE_FROM_SLAB; } else if ((it->it_flags & ITEM_LINKED) != 0) { /* If it doesn't have ITEM_SLABBED, the item could be in any * state on its way to being freed or written to. If no * ITEM_SLABBED, but it's had ITEM_LINKED, it must be active * and have the key written to it already. */ hv = hash(ITEM_key(it), it->nkey); if ((hold_lock = item_trylock(hv)) == NULL) { status = MOVE_LOCKED; } else { bool is_linked = (it->it_flags & ITEM_LINKED); refcount = refcount_incr(it); if (refcount == 2) { /* item is linked but not busy */ /* Double check ITEM_LINKED flag here, since we're * past a memory barrier from the mutex. */ if (is_linked) { status = MOVE_FROM_LRU; } else { /* refcount == 1 + !ITEM_LINKED means the item is being * uploaded to, or was just unlinked but hasn't been freed * yet. Let it bleed off on its own and try again later */ status = MOVE_BUSY; } } else if (refcount > 2 && is_linked) { // TODO: Mark items for delete/rescue and process // outside of the main loop. if (slab_rebal.busy_loops > SLAB_MOVE_MAX_LOOPS) { slab_rebal.busy_deletes++; // Only safe to hold slabs lock because refcount // can't drop to 0 until we release item lock. STORAGE_delete(storage, it); pthread_mutex_unlock(&slabs_lock); do_item_unlink(it, hv); pthread_mutex_lock(&slabs_lock); } status = MOVE_BUSY; } else { if (settings.verbose > 2) { fprintf(stderr, "Slab reassign hit a busy item: refcount: %d (%d -> %d)\n", it->refcount, slab_rebal.s_clsid, slab_rebal.d_clsid); } status = MOVE_BUSY; } /* Item lock must be held while modifying refcount */ if (status == MOVE_BUSY) { refcount_decr(it); item_trylock_unlock(hold_lock); } } } else { /* See above comment. No ITEM_SLABBED or ITEM_LINKED. Mark * busy and wait for item to complete its upload. */ status = MOVE_BUSY; } } int save_item = 0; item *new_it = NULL; size_t ntotal = 0; switch (status) { case MOVE_FROM_LRU: /* Lock order is LRU locks -> slabs_lock. unlink uses LRU lock. * We only need to hold the slabs_lock while initially looking * at an item, and at this point we have an exclusive refcount * (2) + the item is locked. Drop slabs lock, drop item to * refcount 1 (just our own, then fall through and wipe it */ /* Check if expired or flushed */ ntotal = ITEM_ntotal(it); #ifdef EXTSTORE if (it->it_flags & ITEM_HDR) { ntotal = (ntotal - it->nbytes) + sizeof(item_hdr); } #endif /* REQUIRES slabs_lock: CHECK FOR cls->sl_curr > 0 */ if (ch == NULL && (it->it_flags & ITEM_CHUNKED)) { /* Chunked should be identical to non-chunked, except we need * to swap out ntotal for the head-chunk-total. */ ntotal = s_cls->size; } if ((it->exptime != 0 && it->exptime < current_time) || item_is_flushed(it)) { /* Expired, don't save. */ save_item = 0; } else if (ch == NULL && (new_it = slab_rebalance_alloc(ntotal, slab_rebal.s_clsid)) == NULL) { /* Not a chunk of an item, and nomem. */ save_item = 0; slab_rebal.evictions_nomem++; } else if (ch != NULL && (new_it = slab_rebalance_alloc(s_cls->size, slab_rebal.s_clsid)) == NULL) { /* Is a chunk of an item, and nomem. */ save_item = 0; slab_rebal.evictions_nomem++; } else { /* Was whatever it was, and we have memory for it. */ save_item = 1; } pthread_mutex_unlock(&slabs_lock); unsigned int requested_adjust = 0; if (save_item) { if (ch == NULL) { assert((new_it->it_flags & ITEM_CHUNKED) == 0); /* if free memory, memcpy. clear prev/next/h_bucket */ memcpy(new_it, it, ntotal); new_it->prev = 0; new_it->next = 0; new_it->h_next = 0; /* These are definitely required. else fails assert */ new_it->it_flags &= ~ITEM_LINKED; new_it->refcount = 0; do_item_replace(it, new_it, hv); /* Need to walk the chunks and repoint head */ if (new_it->it_flags & ITEM_CHUNKED) { item_chunk *fch = (item_chunk *) ITEM_data(new_it); fch->next->prev = fch; while (fch) { fch->head = new_it; fch = fch->next; } } it->refcount = 0; it->it_flags = ITEM_SLABBED|ITEM_FETCHED; #ifdef DEBUG_SLAB_MOVER memcpy(ITEM_key(it), "deadbeef", 8); #endif slab_rebal.rescues++; requested_adjust = ntotal; } else { item_chunk *nch = (item_chunk *) new_it; /* Chunks always have head chunk (the main it) */ ch->prev->next = nch; if (ch->next) ch->next->prev = nch; memcpy(nch, ch, ch->used + sizeof(item_chunk)); ch->refcount = 0; ch->it_flags = ITEM_SLABBED|ITEM_FETCHED; slab_rebal.chunk_rescues++; #ifdef DEBUG_SLAB_MOVER memcpy(ITEM_key((item *)ch), "deadbeef", 8); #endif refcount_decr(it); requested_adjust = s_cls->size; } } else { /* restore ntotal in case we tried saving a head chunk. */ ntotal = ITEM_ntotal(it); STORAGE_delete(storage, it); do_item_unlink(it, hv); slabs_free(it, ntotal, slab_rebal.s_clsid); /* Swing around again later to remove it from the freelist. */ slab_rebal.busy_items++; was_busy++; } item_trylock_unlock(hold_lock); pthread_mutex_lock(&slabs_lock); /* Always remove the ntotal, as we added it in during * do_slabs_alloc() when copying the item. */ s_cls->requested -= requested_adjust; break; case MOVE_FROM_SLAB: it->refcount = 0; it->it_flags = ITEM_SLABBED|ITEM_FETCHED; #ifdef DEBUG_SLAB_MOVER memcpy(ITEM_key(it), "deadbeef", 8); #endif break; case MOVE_BUSY: case MOVE_LOCKED: slab_rebal.busy_items++; was_busy++; break; case MOVE_PASS: break; } slab_rebal.slab_pos = (char *)slab_rebal.slab_pos + s_cls->size; if (slab_rebal.slab_pos >= slab_rebal.slab_end) break; } if (slab_rebal.slab_pos >= slab_rebal.slab_end) { /* Some items were busy, start again from the top */ if (slab_rebal.busy_items) { slab_rebal.slab_pos = slab_rebal.slab_start; STATS_LOCK(); stats.slab_reassign_busy_items += slab_rebal.busy_items; STATS_UNLOCK(); slab_rebal.busy_items = 0; slab_rebal.busy_loops++; } else { slab_rebal.done++; } } pthread_mutex_unlock(&slabs_lock); return was_busy; } static void slab_rebalance_finish(void) { slabclass_t *s_cls; slabclass_t *d_cls; int x; uint32_t rescues; uint32_t evictions_nomem; uint32_t inline_reclaim; uint32_t chunk_rescues; uint32_t busy_deletes; pthread_mutex_lock(&slabs_lock); s_cls = &slabclass[slab_rebal.s_clsid]; d_cls = &slabclass[slab_rebal.d_clsid]; #ifdef DEBUG_SLAB_MOVER /* If the algorithm is broken, live items can sneak in. */ slab_rebal.slab_pos = slab_rebal.slab_start; while (1) { item *it = slab_rebal.slab_pos; assert(it->it_flags == (ITEM_SLABBED|ITEM_FETCHED)); assert(memcmp(ITEM_key(it), "deadbeef", 8) == 0); it->it_flags = ITEM_SLABBED|ITEM_FETCHED; slab_rebal.slab_pos = (char *)slab_rebal.slab_pos + s_cls->size; if (slab_rebal.slab_pos >= slab_rebal.slab_end) break; } #endif /* At this point the stolen slab is completely clear. * We always kill the "first"/"oldest" slab page in the slab_list, so * shuffle the page list backwards and decrement. */ s_cls->slabs--; for (x = 0; x < s_cls->slabs; x++) { s_cls->slab_list[x] = s_cls->slab_list[x+1]; } d_cls->slab_list[d_cls->slabs++] = slab_rebal.slab_start; /* Don't need to split the page into chunks if we're just storing it */ if (slab_rebal.d_clsid > SLAB_GLOBAL_PAGE_POOL) { memset(slab_rebal.slab_start, 0, (size_t)settings.slab_page_size); split_slab_page_into_freelist(slab_rebal.slab_start, slab_rebal.d_clsid); } else if (slab_rebal.d_clsid == SLAB_GLOBAL_PAGE_POOL) { /* mem_malloc'ed might be higher than mem_limit. */ mem_limit_reached = false; memory_release(); } slab_rebal.busy_loops = 0; slab_rebal.done = 0; slab_rebal.s_clsid = 0; slab_rebal.d_clsid = 0; slab_rebal.slab_start = NULL; slab_rebal.slab_end = NULL; slab_rebal.slab_pos = NULL; evictions_nomem = slab_rebal.evictions_nomem; inline_reclaim = slab_rebal.inline_reclaim; rescues = slab_rebal.rescues; chunk_rescues = slab_rebal.chunk_rescues; busy_deletes = slab_rebal.busy_deletes; slab_rebal.evictions_nomem = 0; slab_rebal.inline_reclaim = 0; slab_rebal.rescues = 0; slab_rebal.chunk_rescues = 0; slab_rebal.busy_deletes = 0; slab_rebalance_signal = 0; pthread_mutex_unlock(&slabs_lock); STATS_LOCK(); stats.slabs_moved++; stats.slab_reassign_rescues += rescues; stats.slab_reassign_evictions_nomem += evictions_nomem; stats.slab_reassign_inline_reclaim += inline_reclaim; stats.slab_reassign_chunk_rescues += chunk_rescues; stats.slab_reassign_busy_deletes += busy_deletes; stats_state.slab_reassign_running = false; STATS_UNLOCK(); if (settings.verbose > 1) { fprintf(stderr, "finished a slab move\n"); } } /* Slab mover thread. * Sits waiting for a condition to jump off and shovel some memory about */ static void *slab_rebalance_thread(void *arg) { int was_busy = 0; /* So we first pass into cond_wait with the mutex held */ mutex_lock(&slabs_rebalance_lock); while (do_run_slab_rebalance_thread) { if (slab_rebalance_signal == 1) { if (slab_rebalance_start() < 0) { /* Handle errors with more specificity as required. */ slab_rebalance_signal = 0; } was_busy = 0; } else if (slab_rebalance_signal && slab_rebal.slab_start != NULL) { was_busy = slab_rebalance_move(); } if (slab_rebal.done) { slab_rebalance_finish(); } else if (was_busy) { /* Stuck waiting for some items to unlock, so slow down a bit * to give them a chance to free up */ usleep(1000); } if (slab_rebalance_signal == 0) { /* always hold this lock while we're running */ pthread_cond_wait(&slab_rebalance_cond, &slabs_rebalance_lock); } } return NULL; } /* Iterate at most once through the slab classes and pick a "random" source. * I like this better than calling rand() since rand() is slow enough that we * can just check all of the classes once instead. */ static int slabs_reassign_pick_any(int dst) { static int cur = POWER_SMALLEST - 1; int tries = power_largest - POWER_SMALLEST + 1; for (; tries > 0; tries--) { cur++; if (cur > power_largest) cur = POWER_SMALLEST; if (cur == dst) continue; if (slabclass[cur].slabs > 1) { return cur; } } return -1; } static enum reassign_result_type do_slabs_reassign(int src, int dst) { bool nospare = false; if (slab_rebalance_signal != 0) return REASSIGN_RUNNING; if (src == dst) return REASSIGN_SRC_DST_SAME; /* Special indicator to choose ourselves. */ if (src == -1) { src = slabs_reassign_pick_any(dst); /* TODO: If we end up back at -1, return a new error type */ } if (src < SLAB_GLOBAL_PAGE_POOL || src > power_largest || dst < SLAB_GLOBAL_PAGE_POOL || dst > power_largest) return REASSIGN_BADCLASS; pthread_mutex_lock(&slabs_lock); if (slabclass[src].slabs < 2) nospare = true; pthread_mutex_unlock(&slabs_lock); if (nospare) return REASSIGN_NOSPARE; slab_rebal.s_clsid = src; slab_rebal.d_clsid = dst; slab_rebalance_signal = 1; pthread_cond_signal(&slab_rebalance_cond); return REASSIGN_OK; } enum reassign_result_type slabs_reassign(int src, int dst) { enum reassign_result_type ret; if (pthread_mutex_trylock(&slabs_rebalance_lock) != 0) { return REASSIGN_RUNNING; } ret = do_slabs_reassign(src, dst); pthread_mutex_unlock(&slabs_rebalance_lock); return ret; } /* If we hold this lock, rebalancer can't wake up or move */ void slabs_rebalancer_pause(void) { pthread_mutex_lock(&slabs_rebalance_lock); } void slabs_rebalancer_resume(void) { pthread_mutex_unlock(&slabs_rebalance_lock); } static pthread_t rebalance_tid; int start_slab_maintenance_thread(void) { int ret; slab_rebalance_signal = 0; slab_rebal.slab_start = NULL; char *env = getenv("MEMCACHED_SLAB_BULK_CHECK"); if (env != NULL) { slab_bulk_check = atoi(env); if (slab_bulk_check == 0) { slab_bulk_check = DEFAULT_SLAB_BULK_CHECK; } } if (pthread_cond_init(&slab_rebalance_cond, NULL) != 0) { fprintf(stderr, "Can't initialize rebalance condition\n"); return -1; } pthread_mutex_init(&slabs_rebalance_lock, NULL); if ((ret = pthread_create(&rebalance_tid, NULL, slab_rebalance_thread, NULL)) != 0) { fprintf(stderr, "Can't create rebal thread: %s\n", strerror(ret)); return -1; } return 0; } /* The maintenance thread is on a sleep/loop cycle, so it should join after a * short wait */ void stop_slab_maintenance_thread(void) { mutex_lock(&slabs_rebalance_lock); do_run_slab_thread = 0; do_run_slab_rebalance_thread = 0; pthread_cond_signal(&slab_rebalance_cond); pthread_mutex_unlock(&slabs_rebalance_lock); /* Wait for the maintenance thread to stop */ pthread_join(rebalance_tid, NULL); } memcached-1.5.6/m4/0000775000175000017500000000000013245330107010752 500000000000000memcached-1.5.6/m4/c99-backport.m40000644000175000017500000001210411446413300013335 00000000000000# AC_PROG_CC_C99 ([ACTION-IF-AVAILABLE], [ACTION-IF-UNAVAILABLE]) # ---------------------------------------------------------------- # If the C compiler is not in ISO C99 mode by default, try to add an # option to output variable CC to make it so. This macro tries # various options that select ISO C99 on some system or another. It # considers the compiler to be in ISO C99 mode if it handles _Bool, # // comments, flexible array members, inline, long long int, mixed # code and declarations, named initialization of structs, restrict, # va_copy, varargs macros, variable declarations in for loops and # variable length arrays. AC_DEFUN([AC_PROG_CC_C99], [AC_C_STD_TRY([c99], [[#include #include #include #include #include // Check varargs macros. These examples are taken from C99 6.10.3.5. #define debug(...) fprintf (stderr, __VA_ARGS__) #define showlist(...) puts (#__VA_ARGS__) #define report(test,...) ((test) ? puts (#test) : printf (__VA_ARGS__)) static void test_varargs_macros (void) { int x = 1234; int y = 5678; debug ("Flag"); debug ("X = %d\n", x); showlist (The first, second, and third items.); report (x>y, "x is %d but y is %d", x, y); } // Check long long types. #define BIG64 18446744073709551615ull #define BIG32 4294967295ul #define BIG_OK (BIG64 / BIG32 == 4294967297ull && BIG64 % BIG32 == 0) #if !BIG_OK your preprocessor is broken; #endif #if BIG_OK #else your preprocessor is broken; #endif static long long int bignum = -9223372036854775807LL; static unsigned long long int ubignum = BIG64; struct incomplete_array { int datasize; double data[]; }; struct named_init { int number; const wchar_t *name; double average; }; typedef const char *ccp; static inline int test_restrict (ccp restrict text) { // See if C++-style comments work. // Iterate through items via the restricted pointer. // Also check for declarations in for loops. for (unsigned int i = 0; *(text+i) != '\0'; ++i) continue; return 0; } // Check varargs and va_copy. static void test_varargs (const char *format, ...) { va_list args; va_start (args, format); va_list args_copy; va_copy (args_copy, args); const char *str; int number; float fnumber; while (*format) { switch (*format++) { case 's': // string str = va_arg (args_copy, const char *); break; case 'd': // int number = va_arg (args_copy, int); break; case 'f': // float fnumber = va_arg (args_copy, double); break; default: break; } } va_end (args_copy); va_end (args); } ]], [[ // Check bool. _Bool success = false; // Check restrict. if (test_restrict ("String literal") == 0) success = true; char *restrict newvar = "Another string"; // Check varargs. test_varargs ("s, d' f .", "string", 65, 34.234); test_varargs_macros (); // Check flexible array members. struct incomplete_array *ia = malloc (sizeof (struct incomplete_array) + (sizeof (double) * 10)); ia->datasize = 10; for (int i = 0; i < ia->datasize; ++i) ia->data[i] = i * 1.234; // Check named initializers. struct named_init ni = { .number = 34, .name = L"Test wide string", .average = 543.34343, }; ni.number = 58; int dynamic_array[ni.number]; dynamic_array[ni.number - 1] = 543; // work around unused variable warnings return (!success || bignum == 0LL || ubignum == 0uLL || newvar[0] == 'x' || dynamic_array[ni.number - 1] != 543); ]], dnl Try dnl GCC -std=gnu99 (unused restrictive modes: -std=c99 -std=iso9899:1999) dnl AIX -qlanglvl=extc99 (unused restrictive mode: -qlanglvl=stdc99) dnl Intel ICC -c99 dnl IRIX -c99 dnl Solaris (unused because it causes the compiler to assume C99 semantics for dnl library functions, and this is invalid before Solaris 10: -xc99) dnl Tru64 -c99 dnl with extended modes being tried first. [[-std=gnu99 -c99 -qlanglvl=extc99]], [$1], [$2])[]dnl ])# AC_PROG_CC_C99 # AC_C_STD_TRY(STANDARD, TEST-PROLOGUE, TEST-BODY, OPTION-LIST, # ACTION-IF-AVAILABLE, ACTION-IF-UNAVAILABLE) # -------------------------------------------------------------- # Check whether the C compiler accepts features of STANDARD (e.g `c89', `c99') # by trying to compile a program of TEST-PROLOGUE and TEST-BODY. If this fails, # try again with each compiler option in the space-separated OPTION-LIST; if one # helps, append it to CC. If eventually successful, run ACTION-IF-AVAILABLE, # else ACTION-IF-UNAVAILABLE. AC_DEFUN([AC_C_STD_TRY], [AC_MSG_CHECKING([for $CC option to accept ISO ]m4_translit($1, [c], [C])) AC_CACHE_VAL(ac_cv_prog_cc_$1, [ac_cv_prog_cc_$1=no ac_save_CC=$CC AC_LANG_CONFTEST([AC_LANG_PROGRAM([$2], [$3])]) for ac_arg in '' $4 do CC="$ac_save_CC $ac_arg" _AC_COMPILE_IFELSE([], [ac_cv_prog_cc_$1=$ac_arg]) test "x$ac_cv_prog_cc_$1" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC ])# AC_CACHE_VAL case "x$ac_cv_prog_cc_$1" in x) AC_MSG_RESULT([none needed]) ;; xno) AC_MSG_RESULT([unsupported]) ;; *) CC="$CC $ac_cv_prog_cc_$1" AC_MSG_RESULT([$ac_cv_prog_cc_$1]) ;; esac AS_IF([test "x$ac_cv_prog_cc_$1" != xno], [$5], [$6]) ])# AC_C_STD_TRY memcached-1.5.6/crawler.h0000664000175000017500000000235513242642567012204 00000000000000#ifndef CRAWLER_H #define CRAWLER_H #define LRU_CRAWLER_CAP_REMAINING -1 typedef struct { uint64_t histo[61]; uint64_t ttl_hourplus; uint64_t noexp; uint64_t reclaimed; uint64_t seen; rel_time_t start_time; rel_time_t end_time; bool run_complete; } crawlerstats_t; struct crawler_expired_data { pthread_mutex_t lock; crawlerstats_t crawlerstats[POWER_LARGEST]; /* redundant with crawlerstats_t so we can get overall start/stop/done */ rel_time_t start_time; rel_time_t end_time; bool crawl_complete; bool is_external; /* whether this was an alloc local or remote to the module. */ }; enum crawler_result_type { CRAWLER_OK=0, CRAWLER_RUNNING, CRAWLER_BADCLASS, CRAWLER_NOTSTARTED, CRAWLER_ERROR }; int start_item_crawler_thread(void); int stop_item_crawler_thread(void); int init_lru_crawler(void *arg); enum crawler_result_type lru_crawler_crawl(char *slabs, enum crawler_run_type, void *c, const int sfd, unsigned int remaining); int lru_crawler_start(uint8_t *ids, uint32_t remaining, const enum crawler_run_type type, void *data, void *c, const int sfd); void lru_crawler_pause(void); void lru_crawler_resume(void); #endif memcached-1.5.6/NEWS0000664000175000017500000000002613025643161011052 00000000000000http://memcached.org/ memcached-1.5.6/storage.c0000664000175000017500000004051713240770206012174 00000000000000/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ #include "memcached.h" #ifdef EXTSTORE #include "storage.h" #include #include #include #define PAGE_BUCKET_DEFAULT 0 #define PAGE_BUCKET_COMPACT 1 #define PAGE_BUCKET_CHUNKED 2 #define PAGE_BUCKET_LOWTTL 3 int lru_maintainer_store(void *storage, const int clsid) { //int i; int did_moves = 0; int item_age = settings.ext_item_age; bool mem_limit_reached = false; unsigned int chunks_free; struct lru_pull_tail_return it_info; // FIXME: need to directly ask the slabber how big a class is if (slabs_clsid(settings.ext_item_size) > clsid) return 0; chunks_free = slabs_available_chunks(clsid, &mem_limit_reached, NULL, NULL); // if we are low on chunks and no spare, push out early. if (chunks_free < settings.ext_free_memchunks[clsid] && mem_limit_reached) item_age = 0; it_info.it = NULL; lru_pull_tail(clsid, COLD_LRU, 0, LRU_PULL_RETURN_ITEM, 0, &it_info); /* Item is locked, and we have a reference to it. */ if (it_info.it == NULL) { return did_moves; } obj_io io; item *it = it_info.it; /* First, storage for the header object */ size_t orig_ntotal = ITEM_ntotal(it); uint32_t flags; if ((it->it_flags & ITEM_HDR) == 0 && (item_age == 0 || current_time - it->time > item_age)) { // FIXME: flag conversion again if (settings.inline_ascii_response) { flags = (uint32_t) strtoul(ITEM_suffix(it), (char **) NULL, 10); } else if (it->nsuffix > 0) { flags = *((uint32_t *)ITEM_suffix(it)); } else { flags = 0; } item *hdr_it = do_item_alloc(ITEM_key(it), it->nkey, flags, it->exptime, sizeof(item_hdr)); /* Run the storage write understanding the start of the item is dirty. * We will fill it (time/exptime/etc) from the header item on read. */ if (hdr_it != NULL) { int bucket = (it->it_flags & ITEM_CHUNKED) ? PAGE_BUCKET_CHUNKED : PAGE_BUCKET_DEFAULT; // Compres soon to expire items into similar pages. if (it->exptime - current_time < settings.ext_low_ttl) { bucket = PAGE_BUCKET_LOWTTL; } hdr_it->it_flags |= ITEM_HDR; io.len = orig_ntotal; io.mode = OBJ_IO_WRITE; // NOTE: when the item is read back in, the slab mover // may see it. Important to have refcount>=2 or ~ITEM_LINKED assert(it->refcount >= 2); if (extstore_write_request(storage, bucket, &io) == 0) { // cuddle the hash value into the time field so we don't have // to recalculate it. item *buf_it = (item *) io.buf; buf_it->time = it_info.hv; // copy from past the headers + time headers. // TODO: should be in items.c if (it->it_flags & ITEM_CHUNKED) { // Need to loop through the item and copy item_chunk *sch = (item_chunk *) ITEM_data(it); int remain = orig_ntotal; int copied = 0; // copy original header int hdrtotal = ITEM_ntotal(it) - it->nbytes; memcpy((char *)io.buf+32, (char *)it+32, hdrtotal - 32); copied = hdrtotal; // copy data in like it were one large object. while (sch && remain) { assert(remain >= sch->used); memcpy((char *)io.buf+copied, sch->data, sch->used); // FIXME: use one variable? remain -= sch->used; copied += sch->used; sch = sch->next; } } else { memcpy((char *)io.buf+32, (char *)it+32, io.len-32); } // crc what we copied so we can do it sequentially. buf_it->it_flags &= ~ITEM_LINKED; buf_it->exptime = crc32c(0, (char*)io.buf+32, orig_ntotal-32); extstore_write(storage, &io); item_hdr *hdr = (item_hdr *) ITEM_data(hdr_it); hdr->page_version = io.page_version; hdr->page_id = io.page_id; hdr->offset = io.offset; // overload nbytes for the header it hdr_it->nbytes = it->nbytes; /* success! Now we need to fill relevant data into the new * header and replace. Most of this requires the item lock */ /* CAS gets set while linking. Copy post-replace */ item_replace(it, hdr_it, it_info.hv); ITEM_set_cas(hdr_it, ITEM_get_cas(it)); do_item_remove(hdr_it); did_moves = 1; LOGGER_LOG(NULL, LOG_EVICTIONS, LOGGER_EXTSTORE_WRITE, it, bucket); } else { /* Failed to write for some reason, can't continue. */ slabs_free(hdr_it, ITEM_ntotal(hdr_it), ITEM_clsid(hdr_it)); } } } do_item_remove(it); item_unlock(it_info.hv); return did_moves; } /* Fetch stats from the external storage system and decide to compact. * If we're more than half full, start skewing how aggressively to run * compaction, up to a desired target when all pages are full. */ static int storage_compact_check(void *storage, logger *l, uint32_t *page_id, uint64_t *page_version, uint64_t *page_size, bool *drop_unread) { struct extstore_stats st; int x; double rate; uint64_t frag_limit; uint64_t low_version = ULLONG_MAX; uint64_t lowest_version = ULLONG_MAX; unsigned int low_page = 0; unsigned int lowest_page = 0; extstore_get_stats(storage, &st); if (st.pages_used == 0) return 0; // lets pick a target "wasted" value and slew. if (st.pages_free > settings.ext_compact_under) return 0; *drop_unread = false; // the number of free pages reduces the configured frag limit // this allows us to defrag early if pages are very empty. rate = 1.0 - ((double)st.pages_free / st.page_count); rate *= settings.ext_max_frag; frag_limit = st.page_size * rate; LOGGER_LOG(l, LOG_SYSEVENTS, LOGGER_COMPACT_FRAGINFO, NULL, rate, frag_limit); st.page_data = calloc(st.page_count, sizeof(struct extstore_page_data)); extstore_get_page_data(storage, &st); // find oldest page by version that violates the constraint for (x = 0; x < st.page_count; x++) { if (st.page_data[x].version == 0 || st.page_data[x].bucket == PAGE_BUCKET_LOWTTL) continue; if (st.page_data[x].version < lowest_version) { lowest_page = x; lowest_version = st.page_data[x].version; } if (st.page_data[x].bytes_used < frag_limit) { if (st.page_data[x].version < low_version) { low_page = x; low_version = st.page_data[x].version; } } } *page_size = st.page_size; free(st.page_data); // we have a page + version to attempt to reclaim. if (low_version != ULLONG_MAX) { *page_id = low_page; *page_version = low_version; return 1; } else if (lowest_version != ULLONG_MAX && settings.ext_drop_unread && st.pages_free <= settings.ext_drop_under) { // nothing matched the frag rate barrier, so pick the absolute oldest // version if we're configured to drop items. *page_id = lowest_page; *page_version = lowest_version; *drop_unread = true; return 1; } return 0; } static pthread_t storage_compact_tid; static pthread_mutex_t storage_compact_plock; #define MIN_STORAGE_COMPACT_SLEEP 10000 #define MAX_STORAGE_COMPACT_SLEEP 2000000 struct storage_compact_wrap { obj_io io; pthread_mutex_t lock; // gates the bools. bool done; bool submitted; bool miss; // version flipped out from under us }; static void storage_compact_readback(void *storage, logger *l, bool drop_unread, char *readback_buf, uint32_t page_id, uint64_t page_version, uint64_t read_size) { uint64_t offset = 0; unsigned int rescues = 0; unsigned int lost = 0; unsigned int skipped = 0; while (offset < read_size) { item *hdr_it = NULL; item_hdr *hdr = NULL; item *it = (item *)(readback_buf+offset); unsigned int ntotal; // probably zeroed out junk at the end of the wbuf if (it->nkey == 0) { break; } ntotal = ITEM_ntotal(it); uint32_t hv = (uint32_t)it->time; item_lock(hv); // We don't have a conn and don't need to do most of do_item_get hdr_it = assoc_find(ITEM_key(it), it->nkey, hv); if (hdr_it != NULL) { bool do_write = false; refcount_incr(hdr_it); // Check validity but don't bother removing it. if ((hdr_it->it_flags & ITEM_HDR) && !item_is_flushed(hdr_it) && (hdr_it->exptime == 0 || hdr_it->exptime > current_time)) { hdr = (item_hdr *)ITEM_data(hdr_it); if (hdr->page_id == page_id && hdr->page_version == page_version) { // Item header is still completely valid. extstore_delete(storage, page_id, page_version, 1, ntotal); // drop inactive items. if (drop_unread && GET_LRU(hdr_it->slabs_clsid) == COLD_LRU) { do_write = false; skipped++; } else { do_write = true; } } } if (do_write) { bool do_update = false; int tries; obj_io io; io.len = ntotal; io.mode = OBJ_IO_WRITE; for (tries = 10; tries > 0; tries--) { if (extstore_write_request(storage, PAGE_BUCKET_COMPACT, &io) == 0) { memcpy(io.buf, it, io.len); extstore_write(storage, &io); do_update = true; break; } else { usleep(1000); } } if (do_update) { if (it->refcount == 2) { hdr->page_version = io.page_version; hdr->page_id = io.page_id; hdr->offset = io.offset; rescues++; } else { lost++; // TODO: re-alloc and replace header. } } else { lost++; } } do_item_remove(hdr_it); } item_unlock(hv); offset += ntotal; if (read_size - offset < sizeof(struct _stritem)) break; } STATS_LOCK(); stats.extstore_compact_lost += lost; stats.extstore_compact_rescues += rescues; stats.extstore_compact_skipped += skipped; STATS_UNLOCK(); LOGGER_LOG(l, LOG_SYSEVENTS, LOGGER_COMPACT_READ_END, NULL, page_id, offset, rescues, lost, skipped); } static void _storage_compact_cb(void *e, obj_io *io, int ret) { struct storage_compact_wrap *wrap = (struct storage_compact_wrap *)io->data; assert(wrap->submitted == true); pthread_mutex_lock(&wrap->lock); if (ret < 1) { wrap->miss = true; } wrap->done = true; pthread_mutex_unlock(&wrap->lock); } // TODO: hoist the storage bits from lru_maintainer_thread in here. // would be nice if they could avoid hammering the same locks though? // I guess it's only COLD. that's probably fine. static void *storage_compact_thread(void *arg) { void *storage = arg; useconds_t to_sleep = MAX_STORAGE_COMPACT_SLEEP; bool compacting = false; uint64_t page_version = 0; uint64_t page_size = 0; uint64_t page_offset = 0; uint32_t page_id = 0; bool drop_unread = false; char *readback_buf = NULL; struct storage_compact_wrap wrap; logger *l = logger_create(); if (l == NULL) { fprintf(stderr, "Failed to allocate logger for storage compaction thread\n"); abort(); } readback_buf = malloc(settings.ext_wbuf_size); if (readback_buf == NULL) { fprintf(stderr, "Failed to allocate readback buffer for storage compaction thread\n"); abort(); } pthread_mutex_init(&wrap.lock, NULL); wrap.done = false; wrap.submitted = false; wrap.io.data = &wrap; wrap.io.buf = (void *)readback_buf; wrap.io.len = settings.ext_wbuf_size; wrap.io.mode = OBJ_IO_READ; wrap.io.cb = _storage_compact_cb; pthread_mutex_lock(&storage_compact_plock); while (1) { pthread_mutex_unlock(&storage_compact_plock); if (to_sleep) { extstore_run_maint(storage); usleep(to_sleep); } pthread_mutex_lock(&storage_compact_plock); if (!compacting && storage_compact_check(storage, l, &page_id, &page_version, &page_size, &drop_unread)) { page_offset = 0; compacting = true; LOGGER_LOG(l, LOG_SYSEVENTS, LOGGER_COMPACT_START, NULL, page_id, page_version); } if (compacting) { pthread_mutex_lock(&wrap.lock); if (page_offset < page_size && !wrap.done && !wrap.submitted) { wrap.io.page_version = page_version; wrap.io.page_id = page_id; wrap.io.offset = page_offset; // FIXME: should be smarter about io->next (unlink at use?) wrap.io.next = NULL; wrap.submitted = true; wrap.miss = false; extstore_submit(storage, &wrap.io); } else if (wrap.miss) { LOGGER_LOG(l, LOG_SYSEVENTS, LOGGER_COMPACT_ABORT, NULL, page_id); wrap.done = false; wrap.submitted = false; compacting = false; } else if (wrap.submitted && wrap.done) { LOGGER_LOG(l, LOG_SYSEVENTS, LOGGER_COMPACT_READ_START, NULL, page_id, page_offset); storage_compact_readback(storage, l, drop_unread, readback_buf, page_id, page_version, settings.ext_wbuf_size); page_offset += settings.ext_wbuf_size; wrap.done = false; wrap.submitted = false; } else if (page_offset >= page_size) { compacting = false; wrap.done = false; wrap.submitted = false; extstore_close_page(storage, page_id, page_version); LOGGER_LOG(l, LOG_SYSEVENTS, LOGGER_COMPACT_END, NULL, page_id); } pthread_mutex_unlock(&wrap.lock); if (to_sleep > MIN_STORAGE_COMPACT_SLEEP) to_sleep /= 2; } else { if (to_sleep < MAX_STORAGE_COMPACT_SLEEP) to_sleep += MIN_STORAGE_COMPACT_SLEEP; } } free(readback_buf); return NULL; } // TODO // logger needs logger_destroy() to exist/work before this is safe. /*int stop_storage_compact_thread(void) { int ret; pthread_mutex_lock(&lru_maintainer_lock); do_run_lru_maintainer_thread = 0; pthread_mutex_unlock(&lru_maintainer_lock); if ((ret = pthread_join(lru_maintainer_tid, NULL)) != 0) { fprintf(stderr, "Failed to stop LRU maintainer thread: %s\n", strerror(ret)); return -1; } settings.lru_maintainer_thread = false; return 0; }*/ void storage_compact_pause(void) { pthread_mutex_lock(&storage_compact_plock); } void storage_compact_resume(void) { pthread_mutex_unlock(&storage_compact_plock); } int start_storage_compact_thread(void *arg) { int ret; pthread_mutex_init(&storage_compact_plock, NULL); if ((ret = pthread_create(&storage_compact_tid, NULL, storage_compact_thread, arg)) != 0) { fprintf(stderr, "Can't create storage_compact thread: %s\n", strerror(ret)); return -1; } return 0; } #endif memcached-1.5.6/COPYING0000644000175000017500000000273711246331452011417 00000000000000Copyright (c) 2003, Danga Interactive, 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: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * 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. * Neither the name of the Danga Interactive 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 COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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. memcached-1.5.6/stats.h0000644000175000017500000000052612606105233011662 00000000000000/* stats */ void stats_prefix_init(void); void stats_prefix_clear(void); void stats_prefix_record_get(const char *key, const size_t nkey, const bool is_hit); void stats_prefix_record_delete(const char *key, const size_t nkey); void stats_prefix_record_set(const char *key, const size_t nkey); /*@null@*/ char *stats_prefix_dump(int *length); memcached-1.5.6/sasl_defs.h0000644000175000017500000000126512606105233012470 00000000000000#ifndef SASL_DEFS_H #define SASL_DEFS_H 1 // Longest one I could find was ``9798-U-RSA-SHA1-ENC'' #define MAX_SASL_MECH_LEN 32 #if defined(HAVE_SASL_SASL_H) && defined(ENABLE_SASL) #include void init_sasl(void); extern char my_sasl_hostname[1025]; #else /* End of SASL support */ typedef void* sasl_conn_t; #define init_sasl() {} #define sasl_dispose(x) {} #define sasl_server_new(a, b, c, d, e, f, g, h) 1 #define sasl_listmech(a, b, c, d, e, f, g, h) 1 #define sasl_server_start(a, b, c, d, e, f) 1 #define sasl_server_step(a, b, c, d, e) 1 #define sasl_getprop(a, b, c) {} #define SASL_OK 0 #define SASL_CONTINUE -1 #endif /* sasl compat */ #endif /* SASL_DEFS_H */ memcached-1.5.6/openbsd_priv.c0000664000175000017500000000152513240770206013216 00000000000000#include #include #include #include #include #include "memcached.h" /* * this section of code will drop all (OpenBSD) privileges including * those normally granted to all userland process (basic privileges). The * effect of this is that after running this code, the process will not able * to fork(), exec(), etc. See pledge(2) for more information. */ void drop_privileges() { extern char *__progname; if (settings.socketpath != NULL) { if (pledge("stdio unix", NULL) == -1) { fprintf(stderr, "%s: pledge: %s\n", __progname, strerror(errno)); exit(EXIT_FAILURE); } } else { if (pledge("stdio inet", NULL) == -1) { fprintf(stderr, "%s: pledge: %s\n", __progname, strerror(errno)); exit(EXIT_FAILURE); } } } memcached-1.5.6/itoa_ljust.h0000664000175000017500000000146613150607001012702 00000000000000#ifndef ITOA_LJUST_H #define ITOA_LJUST_H //=== itoa_ljust.h - Fast integer to ascii conversion // // Fast and simple integer to ASCII conversion: // // - 32 and 64-bit integers // - signed and unsigned // - user supplied buffer must be large enough for all decimal digits // in value plus minus sign if negative // - left-justified // - NUL terminated // - return value is pointer to NUL terminator // // Copyright (c) 2016 Arturo Martin-de-Nicolas // arturomdn@gmail.com // https://github.com/amdn/itoa_ljust/ //===----------------------------------------------------------------------===// #include char* itoa_u32(uint32_t u, char* buffer); char* itoa_32( int32_t i, char* buffer); char* itoa_u64(uint64_t u, char* buffer); char* itoa_64( int64_t i, char* buffer); #endif // ITOA_LJUST_H memcached-1.5.6/AUTHORS0000644000175000017500000000010512250557060011420 00000000000000Anatoly Vorobey Brad Fitzpatrick memcached-1.5.6/sasl_defs.c0000664000175000017500000001174313025643161012472 00000000000000/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ #include "memcached.h" #include #include #include #include char my_sasl_hostname[1025]; #ifdef HAVE_SASL_CB_GETCONF /* The locations we may search for a SASL config file if the user didn't * specify one in the environment variable SASL_CONF_PATH */ const char * const locations[] = { "/etc/sasl/memcached.conf", "/etc/sasl2/memcached.conf", NULL }; #endif #ifndef HAVE_SASL_CALLBACK_FT typedef int (*sasl_callback_ft)(void); #endif #ifdef ENABLE_SASL_PWDB #define MAX_ENTRY_LEN 256 static const char *memcached_sasl_pwdb; static int sasl_server_userdb_checkpass(sasl_conn_t *conn, void *context, const char *user, const char *pass, unsigned passlen, struct propctx *propctx) { size_t unmlen = strlen(user); if ((passlen + unmlen) > (MAX_ENTRY_LEN - 4)) { fprintf(stderr, "WARNING: Failed to authenticate <%s> due to too long password (%d)\n", user, passlen); return SASL_NOAUTHZ; } FILE *pwfile = fopen(memcached_sasl_pwdb, "r"); if (pwfile == NULL) { if (settings.verbose) { vperror("WARNING: Failed to open sasl database <%s>", memcached_sasl_pwdb); } return SASL_NOAUTHZ; } char buffer[MAX_ENTRY_LEN]; bool ok = false; while ((fgets(buffer, sizeof(buffer), pwfile)) != NULL) { if (memcmp(user, buffer, unmlen) == 0 && buffer[unmlen] == ':') { /* This is the correct user */ ++unmlen; if (memcmp(pass, buffer + unmlen, passlen) == 0 && (buffer[unmlen + passlen] == ':' || /* Additional tokens */ buffer[unmlen + passlen] == '\n' || /* end of line */ buffer[unmlen + passlen] == '\r'|| /* dos format? */ buffer[unmlen + passlen] == '\0')) { /* line truncated */ ok = true; } break; } } (void)fclose(pwfile); if (ok) { return SASL_OK; } if (settings.verbose) { fprintf(stderr, "INFO: User <%s> failed to authenticate\n", user); } return SASL_NOAUTHZ; } #endif #ifdef HAVE_SASL_CB_GETCONF static int sasl_getconf(void *context, const char **path) { *path = getenv("SASL_CONF_PATH"); if (*path == NULL) { for (int i = 0; locations[i] != NULL; ++i) { if (access(locations[i], F_OK) == 0) { *path = locations[i]; break; } } } if (settings.verbose) { if (*path != NULL) { fprintf(stderr, "Reading configuration from: <%s>\n", *path); } else { fprintf(stderr, "Failed to locate a config path\n"); } } return (*path != NULL) ? SASL_OK : SASL_FAIL; } #endif static int sasl_log(void *context, int level, const char *message) { bool log = true; switch (level) { case SASL_LOG_NONE: log = false; break; case SASL_LOG_PASS: case SASL_LOG_TRACE: case SASL_LOG_DEBUG: case SASL_LOG_NOTE: if (settings.verbose < 2) { log = false; } break; case SASL_LOG_WARN: case SASL_LOG_FAIL: if (settings.verbose < 1) { log = false; } break; default: /* This is an error */ ; } if (log) { fprintf(stderr, "SASL (severity %d): %s\n", level, message); } return SASL_OK; } static sasl_callback_t sasl_callbacks[] = { #ifdef ENABLE_SASL_PWDB { SASL_CB_SERVER_USERDB_CHECKPASS, (sasl_callback_ft)sasl_server_userdb_checkpass, NULL }, #endif { SASL_CB_LOG, (sasl_callback_ft)sasl_log, NULL }, #ifdef HAVE_SASL_CB_GETCONF { SASL_CB_GETCONF, sasl_getconf, NULL }, #endif { SASL_CB_LIST_END, NULL, NULL } }; void init_sasl(void) { #ifdef ENABLE_SASL_PWDB memcached_sasl_pwdb = getenv("MEMCACHED_SASL_PWDB"); if (memcached_sasl_pwdb == NULL) { if (settings.verbose) { fprintf(stderr, "INFO: MEMCACHED_SASL_PWDB not specified. " "Internal passwd database disabled\n"); } sasl_callbacks[0].id = SASL_CB_LIST_END; sasl_callbacks[0].proc = NULL; } #endif memset(my_sasl_hostname, 0, sizeof(my_sasl_hostname)); if (gethostname(my_sasl_hostname, sizeof(my_sasl_hostname)-1) == -1) { if (settings.verbose) { fprintf(stderr, "Error discovering hostname for SASL\n"); } my_sasl_hostname[0] = '\0'; } if (sasl_server_init(sasl_callbacks, "memcached") != SASL_OK) { fprintf(stderr, "Error initializing sasl.\n"); exit(EXIT_FAILURE); } else { if (settings.verbose) { fprintf(stderr, "Initialized SASL.\n"); } } } memcached-1.5.6/slab_automove_extstore.c0000664000175000017500000002501413242642567015332 00000000000000/* Copyright 2017 Facebook. * * Use and distribution licensed under the BSD license. See * the LICENSE file for full text. */ /* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ #include "memcached.h" #include "slab_automove_extstore.h" #include #include #define MIN_PAGES_FOR_SOURCE 2 #define MIN_PAGES_FOR_RECLAIM 2.5 #define MIN_PAGES_FREE 1.5 #define MEMCHECK_PERIOD 60 struct window_data { uint64_t age; uint64_t dirty; uint64_t evicted; unsigned int excess_free; unsigned int relaxed; }; struct window_global { uint32_t pool_low; uint32_t pool_high; }; typedef struct { struct window_data *window_data; struct window_global *window_global; struct settings *settings; uint32_t window_size; uint32_t window_cur; uint32_t item_size; rel_time_t last_memcheck_run; double max_age_ratio; double free_ratio; bool pool_filled_once; unsigned int free_mem[MAX_NUMBER_OF_SLAB_CLASSES]; item_stats_automove iam_before[MAX_NUMBER_OF_SLAB_CLASSES]; item_stats_automove iam_after[MAX_NUMBER_OF_SLAB_CLASSES]; slab_stats_automove sam_before[MAX_NUMBER_OF_SLAB_CLASSES]; slab_stats_automove sam_after[MAX_NUMBER_OF_SLAB_CLASSES]; } slab_automove; void *slab_automove_extstore_init(struct settings *settings) { uint32_t window_size = settings->slab_automove_window; double max_age_ratio = settings->slab_automove_ratio; slab_automove *a = calloc(1, sizeof(slab_automove)); if (a == NULL) return NULL; a->window_data = calloc(window_size * MAX_NUMBER_OF_SLAB_CLASSES, sizeof(struct window_data)); a->window_global = calloc(window_size, sizeof(struct window_global)); a->window_size = window_size; a->max_age_ratio = max_age_ratio; a->free_ratio = settings->slab_automove_freeratio; a->item_size = settings->ext_item_size; a->last_memcheck_run = 0; a->settings = settings; a->pool_filled_once = false; if (a->window_data == NULL || a->window_global == NULL) { if (a->window_data) free(a->window_data); if (a->window_global) free(a->window_global); free(a); return NULL; } // do a dry run to fill the before structs fill_item_stats_automove(a->iam_before); fill_slab_stats_automove(a->sam_before); return (void *)a; } void slab_automove_extstore_free(void *arg) { slab_automove *a = (slab_automove *)arg; free(a->window_data); free(a); } static void window_sum(struct window_data *wd, struct window_data *w, uint32_t size) { for (int x = 0; x < size; x++) { struct window_data *d = &wd[x]; w->age += d->age; w->dirty += d->dirty; w->evicted += d->evicted; w->excess_free += d->excess_free; w->relaxed += d->relaxed; } } /* This could potentially merge with above */ static void window_global_sum(struct window_global *wg, struct window_global *w, uint32_t size) { for (int x = 0; x < size; x++) { struct window_global *d = &wg[x]; w->pool_high += d->pool_high; w->pool_low += d->pool_low; } } static void global_pool_check(slab_automove *a) { bool mem_limit_reached; uint32_t free = a->free_mem[0]; struct window_global *wg = &a->window_global[a->window_cur % a->window_size]; unsigned int count = global_page_pool_size(&mem_limit_reached); memset(wg, 0, sizeof(struct window_global)); if (!mem_limit_reached) return; if (count < free / 2) { wg->pool_low = 1; a->pool_filled_once = true; } else if (count > free) { wg->pool_high = 1; } else { a->pool_filled_once = true; } } /* A percentage of memory is configured to be held "free" as buffers for the * external storage system. * % of global memory is desired in the global page pool * each slab class has a % of free chunks desired based on how much memory is * currently in the class. This allows time for extstore to flush data when * spikes or waves of set data arrive. * The global page pool reserve acts as a secondary buffer for any slab class, * which helps absorb shifts in which class is active. */ static void memcheck(slab_automove *a) { unsigned int total_pages = 0; if (current_time < a->last_memcheck_run + MEMCHECK_PERIOD) return; a->last_memcheck_run = current_time; for (int n = 1; n < MAX_NUMBER_OF_SLAB_CLASSES; n++) { slab_stats_automove *sam = &a->sam_after[n]; total_pages += sam->total_pages; unsigned int hold_free = (sam->total_pages * sam->chunks_per_page) * a->free_ratio; if (sam->chunks_per_page * MIN_PAGES_FREE > hold_free) hold_free = sam->chunks_per_page * MIN_PAGES_FREE; a->free_mem[n] = hold_free; if (a->settings->ext_free_memchunks[n] != hold_free && a->pool_filled_once) { a->settings->ext_free_memchunks[n] = hold_free; } } // remember to add what remains in global pool. total_pages += a->sam_after[0].total_pages; a->free_mem[0] = total_pages * a->free_ratio; } static struct window_data *get_window_data(slab_automove *a, int class) { int w_offset = class * a->window_size; return &a->window_data[w_offset + (a->window_cur % a->window_size)]; } void slab_automove_extstore_run(void *arg, int *src, int *dst) { slab_automove *a = (slab_automove *)arg; int n; struct window_data w_sum; int oldest = -1; uint64_t oldest_age = 0; int youngest = -1; uint64_t youngest_age = ~0; bool too_free = false; *src = -1; *dst = -1; global_pool_check(a); struct window_global wg_sum; memset(&wg_sum, 0, sizeof(struct window_global)); window_global_sum(a->window_global, &wg_sum, a->window_size); // fill after structs fill_item_stats_automove(a->iam_after); fill_slab_stats_automove(a->sam_after); a->window_cur++; memcheck(a); // iterate slabs for (n = POWER_SMALLEST; n < MAX_NUMBER_OF_SLAB_CLASSES; n++) { bool small_slab = a->sam_before[n].chunk_size < a->item_size ? true : false; bool free_enough = false; struct window_data *wd = get_window_data(a, n); // summarize the window-up-to-now. memset(&w_sum, 0, sizeof(struct window_data)); int w_offset = n * a->window_size; window_sum(&a->window_data[w_offset], &w_sum, a->window_size); memset(wd, 0, sizeof(struct window_data)); // if page delta, oom, or evicted delta, mark window dirty // classes marked dirty cannot donate memory back to global pool. if (a->iam_after[n].evicted - a->iam_before[n].evicted > 0 || a->iam_after[n].outofmemory - a->iam_before[n].outofmemory > 0) { wd->evicted = 1; wd->dirty = 1; } if (a->sam_after[n].total_pages - a->sam_before[n].total_pages > 0) { wd->dirty = 1; } // Mark excess free if we're over the free mem limit for too long. // "free_enough" means it is either wobbling, recently received a new // page of memory, or the crawler is freeing memory. if (a->sam_after[n].free_chunks > a->free_mem[n]) { free_enough = true; } // double the free requirements means we may have memory we can // reclaim to global, if it stays this way for the whole window. if (a->sam_after[n].free_chunks > (a->free_mem[n] * 2) && a->free_mem[n] > 0) { wd->excess_free = 1; } // set age into window wd->age = a->iam_after[n].age; // grab age as average of window total uint64_t age = w_sum.age / a->window_size; // if > N free chunks and not dirty, reclaim memory // small slab classes aren't age balanced and rely more on global // pool. reclaim them more aggressively. if (a->sam_after[n].free_chunks > a->sam_after[n].chunks_per_page * MIN_PAGES_FOR_RECLAIM && w_sum.dirty == 0) { if (small_slab) { *src = n; *dst = 0; too_free = true; } else if (!small_slab && w_sum.excess_free >= a->window_size) { // If large slab and free chunks haven't decreased for a full // window, reclaim pages. *src = n; *dst = 0; too_free = true; } } if (!small_slab) { // if oldest and have enough pages, is oldest if (age > oldest_age && a->sam_after[n].total_pages > MIN_PAGES_FOR_SOURCE) { oldest = n; oldest_age = age; } // don't count as youngest if it hasn't been using new chunks. // (if it was relaxed recently, and is currently "free enough") if (age < youngest_age && a->sam_after[n].total_pages != 0 && w_sum.excess_free < a->window_size && !(w_sum.relaxed && free_enough)) { youngest = n; youngest_age = age; } } } memcpy(a->iam_before, a->iam_after, sizeof(item_stats_automove) * MAX_NUMBER_OF_SLAB_CLASSES); memcpy(a->sam_before, a->sam_after, sizeof(slab_stats_automove) * MAX_NUMBER_OF_SLAB_CLASSES); // only make decisions if window has filled once. if (a->window_cur < a->window_size) return; if (wg_sum.pool_high >= a->window_size && !wg_sum.pool_low && youngest != -1) { if (a->sam_after[youngest].free_chunks <= a->free_mem[youngest]) { *src = 0; *dst = youngest; } struct window_data *wd = get_window_data(a, youngest); // "relaxing" here and below allows us to skip classes which will // never grow or are growing slowly, more quickly finding other // classes which violate the age ratio. wd->relaxed = 1; } else if (!too_free && wg_sum.pool_low && oldest != -1) { *src = oldest; *dst = 0; } else if (!too_free && youngest != -1 && oldest != -1 && youngest != oldest) { // if we have a youngest and oldest, and oldest is outside the ratio. if (youngest_age < ((double)oldest_age * a->max_age_ratio)) { struct window_data *wd = get_window_data(a, youngest); wd->relaxed = 1; // only actually assign more memory if it's absorbed what it has if (a->sam_after[youngest].free_chunks <= a->free_mem[youngest]) { *src = 0; *dst = youngest; } } } return; } memcached-1.5.6/config.sub0000755000175000017500000010646012612517377012357 00000000000000#! /bin/sh # Configuration validation subroutine script. # Copyright 1992-2015 Free Software Foundation, Inc. timestamp='2015-08-20' # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, see . # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that # program. This Exception is an additional permission under section 7 # of the GNU General Public License, version 3 ("GPLv3"). # Please send patches to . # # Configuration subroutine to validate and canonicalize a configuration type. # Supply the specified configuration type as an argument. # If it is invalid, we print an error message on stderr and exit with code 1. # Otherwise, we print the canonical config type on stdout and succeed. # You can get the latest version of this script from: # http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD # This file is supposed to be the same for all GNU packages # and recognize all the CPU types, system types and aliases # that are meaningful with *any* GNU software. # Each package is responsible for reporting which valid configurations # it does not support. The user should be able to distinguish # a failure to support a valid configuration from a meaningless # configuration. # The goal of this file is to map all the various variations of a given # machine specification into a single specification in the form: # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM # or in some cases, the newer four-part form: # CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM # It is wrong to echo any other type of specification. me=`echo "$0" | sed -e 's,.*/,,'` usage="\ Usage: $0 [OPTION] CPU-MFR-OPSYS $0 [OPTION] ALIAS Canonicalize a configuration name. Operation modes: -h, --help print this help, then exit -t, --time-stamp print date of last modification, then exit -v, --version print version number, then exit Report bugs and patches to ." version="\ GNU config.sub ($timestamp) Copyright 1992-2015 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." help=" Try \`$me --help' for more information." # Parse command line while test $# -gt 0 ; do case $1 in --time-stamp | --time* | -t ) echo "$timestamp" ; exit ;; --version | -v ) echo "$version" ; exit ;; --help | --h* | -h ) echo "$usage"; exit ;; -- ) # Stop option processing shift; break ;; - ) # Use stdin as input. break ;; -* ) echo "$me: invalid option $1$help" exit 1 ;; *local*) # First pass through any local machine types. echo $1 exit ;; * ) break ;; esac done case $# in 0) echo "$me: missing argument$help" >&2 exit 1;; 1) ;; *) echo "$me: too many arguments$help" >&2 exit 1;; esac # Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any). # Here we must recognize all the valid KERNEL-OS combinations. maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'` case $maybe_os in nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc | linux-newlib* | \ linux-musl* | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | \ knetbsd*-gnu* | netbsd*-gnu* | netbsd*-eabi* | \ kopensolaris*-gnu* | \ storm-chaos* | os2-emx* | rtmk-nova*) os=-$maybe_os basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'` ;; android-linux) os=-linux-android basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`-unknown ;; *) basic_machine=`echo $1 | sed 's/-[^-]*$//'` if [ $basic_machine != $1 ] then os=`echo $1 | sed 's/.*-/-/'` else os=; fi ;; esac ### Let's recognize common machines as not being operating systems so ### that things like config.sub decstation-3100 work. We also ### recognize some manufacturers as not being operating systems, so we ### can provide default operating systems below. case $os in -sun*os*) # Prevent following clause from handling this invalid input. ;; -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \ -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \ -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \ -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\ -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \ -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \ -apple | -axis | -knuth | -cray | -microblaze*) os= basic_machine=$1 ;; -bluegene*) os=-cnk ;; -sim | -cisco | -oki | -wec | -winbond) os= basic_machine=$1 ;; -scout) ;; -wrs) os=-vxworks basic_machine=$1 ;; -chorusos*) os=-chorusos basic_machine=$1 ;; -chorusrdb) os=-chorusrdb basic_machine=$1 ;; -hiux*) os=-hiuxwe2 ;; -sco6) os=-sco5v6 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco5) os=-sco3.2v5 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco4) os=-sco3.2v4 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco3.2.[4-9]*) os=`echo $os | sed -e 's/sco3.2./sco3.2v/'` basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco3.2v[4-9]*) # Don't forget version if it is 3.2v4 or newer. basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco5v6*) # Don't forget version if it is 3.2v4 or newer. basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco*) os=-sco3.2v2 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -udk*) basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -isc) os=-isc2.2 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -clix*) basic_machine=clipper-intergraph ;; -isc*) basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -lynx*178) os=-lynxos178 ;; -lynx*5) os=-lynxos5 ;; -lynx*) os=-lynxos ;; -ptx*) basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'` ;; -windowsnt*) os=`echo $os | sed -e 's/windowsnt/winnt/'` ;; -psos*) os=-psos ;; -mint | -mint[0-9]*) basic_machine=m68k-atari os=-mint ;; esac # Decode aliases for certain CPU-COMPANY combinations. case $basic_machine in # Recognize the basic CPU types without company name. # Some are omitted here because they have special meanings below. 1750a | 580 \ | a29k \ | aarch64 | aarch64_be \ | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \ | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \ | am33_2.0 \ | arc | arceb \ | arm | arm[bl]e | arme[lb] | armv[2-8] | armv[3-8][lb] | armv7[arm] \ | avr | avr32 \ | ba \ | be32 | be64 \ | bfin \ | c4x | c8051 | clipper \ | d10v | d30v | dlx | dsp16xx \ | e2k | epiphany \ | fido | fr30 | frv | ft32 \ | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ | hexagon \ | i370 | i860 | i960 | ia64 \ | ip2k | iq2000 \ | k1om \ | le32 | le64 \ | lm32 \ | m32c | m32r | m32rle | m68000 | m68k | m88k \ | maxq | mb | microblaze | microblazeel | mcore | mep | metag \ | mips | mipsbe | mipseb | mipsel | mipsle \ | mips16 \ | mips64 | mips64el \ | mips64octeon | mips64octeonel \ | mips64orion | mips64orionel \ | mips64r5900 | mips64r5900el \ | mips64vr | mips64vrel \ | mips64vr4100 | mips64vr4100el \ | mips64vr4300 | mips64vr4300el \ | mips64vr5000 | mips64vr5000el \ | mips64vr5900 | mips64vr5900el \ | mipsisa32 | mipsisa32el \ | mipsisa32r2 | mipsisa32r2el \ | mipsisa32r6 | mipsisa32r6el \ | mipsisa64 | mipsisa64el \ | mipsisa64r2 | mipsisa64r2el \ | mipsisa64r6 | mipsisa64r6el \ | mipsisa64sb1 | mipsisa64sb1el \ | mipsisa64sr71k | mipsisa64sr71kel \ | mipsr5900 | mipsr5900el \ | mipstx39 | mipstx39el \ | mn10200 | mn10300 \ | moxie \ | mt \ | msp430 \ | nds32 | nds32le | nds32be \ | nios | nios2 | nios2eb | nios2el \ | ns16k | ns32k \ | open8 | or1k | or1knd | or32 \ | pdp10 | pdp11 | pj | pjl \ | powerpc | powerpc64 | powerpc64le | powerpcle \ | pyramid \ | riscv32 | riscv64 \ | rl78 | rx \ | score \ | sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[234]eb | sheb | shbe | shle | sh[1234]le | sh3ele \ | sh64 | sh64le \ | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \ | sparcv8 | sparcv9 | sparcv9b | sparcv9v \ | spu \ | tahoe | tic4x | tic54x | tic55x | tic6x | tic80 | tron \ | ubicom32 \ | v850 | v850e | v850e1 | v850e2 | v850es | v850e2v3 \ | visium \ | we32k \ | x86 | xc16x | xstormy16 | xtensa \ | z8k | z80) basic_machine=$basic_machine-unknown ;; c54x) basic_machine=tic54x-unknown ;; c55x) basic_machine=tic55x-unknown ;; c6x) basic_machine=tic6x-unknown ;; leon|leon[3-9]) basic_machine=sparc-$basic_machine ;; m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | nvptx | picochip) basic_machine=$basic_machine-unknown os=-none ;; m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k) ;; ms1) basic_machine=mt-unknown ;; strongarm | thumb | xscale) basic_machine=arm-unknown ;; xgate) basic_machine=$basic_machine-unknown os=-none ;; xscaleeb) basic_machine=armeb-unknown ;; xscaleel) basic_machine=armel-unknown ;; # We use `pc' rather than `unknown' # because (1) that's what they normally are, and # (2) the word "unknown" tends to confuse beginning users. i*86 | x86_64) basic_machine=$basic_machine-pc ;; # Object if more than one company name word. *-*-*) echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 exit 1 ;; # Recognize the basic CPU types with company name. 580-* \ | a29k-* \ | aarch64-* | aarch64_be-* \ | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \ | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \ | alphapca5[67]-* | alpha64pca5[67]-* | arc-* | arceb-* \ | arm-* | armbe-* | armle-* | armeb-* | armv*-* \ | avr-* | avr32-* \ | ba-* \ | be32-* | be64-* \ | bfin-* | bs2000-* \ | c[123]* | c30-* | [cjt]90-* | c4x-* \ | c8051-* | clipper-* | craynv-* | cydra-* \ | d10v-* | d30v-* | dlx-* \ | e2k-* | elxsi-* \ | f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \ | h8300-* | h8500-* \ | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \ | hexagon-* \ | i*86-* | i860-* | i960-* | ia64-* \ | ip2k-* | iq2000-* \ | k1om-* \ | le32-* | le64-* \ | lm32-* \ | m32c-* | m32r-* | m32rle-* \ | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \ | m88110-* | m88k-* | maxq-* | mcore-* | metag-* \ | microblaze-* | microblazeel-* \ | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \ | mips16-* \ | mips64-* | mips64el-* \ | mips64octeon-* | mips64octeonel-* \ | mips64orion-* | mips64orionel-* \ | mips64r5900-* | mips64r5900el-* \ | mips64vr-* | mips64vrel-* \ | mips64vr4100-* | mips64vr4100el-* \ | mips64vr4300-* | mips64vr4300el-* \ | mips64vr5000-* | mips64vr5000el-* \ | mips64vr5900-* | mips64vr5900el-* \ | mipsisa32-* | mipsisa32el-* \ | mipsisa32r2-* | mipsisa32r2el-* \ | mipsisa32r6-* | mipsisa32r6el-* \ | mipsisa64-* | mipsisa64el-* \ | mipsisa64r2-* | mipsisa64r2el-* \ | mipsisa64r6-* | mipsisa64r6el-* \ | mipsisa64sb1-* | mipsisa64sb1el-* \ | mipsisa64sr71k-* | mipsisa64sr71kel-* \ | mipsr5900-* | mipsr5900el-* \ | mipstx39-* | mipstx39el-* \ | mmix-* \ | mt-* \ | msp430-* \ | nds32-* | nds32le-* | nds32be-* \ | nios-* | nios2-* | nios2eb-* | nios2el-* \ | none-* | np1-* | ns16k-* | ns32k-* \ | open8-* \ | or1k*-* \ | orion-* \ | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \ | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* \ | pyramid-* \ | riscv32-* | riscv64-* \ | rl78-* | romp-* | rs6000-* | rx-* \ | sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \ | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \ | sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \ | sparclite-* \ | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | sv1-* | sx*-* \ | tahoe-* \ | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \ | tile*-* \ | tron-* \ | ubicom32-* \ | v850-* | v850e-* | v850e1-* | v850es-* | v850e2-* | v850e2v3-* \ | vax-* \ | visium-* \ | we32k-* \ | x86-* | x86_64-* | xc16x-* | xps100-* \ | xstormy16-* | xtensa*-* \ | ymp-* \ | z8k-* | z80-*) ;; # Recognize the basic CPU types without company name, with glob match. xtensa*) basic_machine=$basic_machine-unknown ;; # Recognize the various machine names and aliases which stand # for a CPU type and a company and sometimes even an OS. 386bsd) basic_machine=i386-unknown os=-bsd ;; 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) basic_machine=m68000-att ;; 3b*) basic_machine=we32k-att ;; a29khif) basic_machine=a29k-amd os=-udi ;; abacus) basic_machine=abacus-unknown ;; adobe68k) basic_machine=m68010-adobe os=-scout ;; alliant | fx80) basic_machine=fx80-alliant ;; altos | altos3068) basic_machine=m68k-altos ;; am29k) basic_machine=a29k-none os=-bsd ;; amd64) basic_machine=x86_64-pc ;; amd64-*) basic_machine=x86_64-`echo $basic_machine | sed 's/^[^-]*-//'` ;; amdahl) basic_machine=580-amdahl os=-sysv ;; amiga | amiga-*) basic_machine=m68k-unknown ;; amigaos | amigados) basic_machine=m68k-unknown os=-amigaos ;; amigaunix | amix) basic_machine=m68k-unknown os=-sysv4 ;; apollo68) basic_machine=m68k-apollo os=-sysv ;; apollo68bsd) basic_machine=m68k-apollo os=-bsd ;; aros) basic_machine=i386-pc os=-aros ;; asmjs) basic_machine=asmjs-unknown ;; aux) basic_machine=m68k-apple os=-aux ;; balance) basic_machine=ns32k-sequent os=-dynix ;; blackfin) basic_machine=bfin-unknown os=-linux ;; blackfin-*) basic_machine=bfin-`echo $basic_machine | sed 's/^[^-]*-//'` os=-linux ;; bluegene*) basic_machine=powerpc-ibm os=-cnk ;; c54x-*) basic_machine=tic54x-`echo $basic_machine | sed 's/^[^-]*-//'` ;; c55x-*) basic_machine=tic55x-`echo $basic_machine | sed 's/^[^-]*-//'` ;; c6x-*) basic_machine=tic6x-`echo $basic_machine | sed 's/^[^-]*-//'` ;; c90) basic_machine=c90-cray os=-unicos ;; cegcc) basic_machine=arm-unknown os=-cegcc ;; convex-c1) basic_machine=c1-convex os=-bsd ;; convex-c2) basic_machine=c2-convex os=-bsd ;; convex-c32) basic_machine=c32-convex os=-bsd ;; convex-c34) basic_machine=c34-convex os=-bsd ;; convex-c38) basic_machine=c38-convex os=-bsd ;; cray | j90) basic_machine=j90-cray os=-unicos ;; craynv) basic_machine=craynv-cray os=-unicosmp ;; cr16 | cr16-*) basic_machine=cr16-unknown os=-elf ;; crds | unos) basic_machine=m68k-crds ;; crisv32 | crisv32-* | etraxfs*) basic_machine=crisv32-axis ;; cris | cris-* | etrax*) basic_machine=cris-axis ;; crx) basic_machine=crx-unknown os=-elf ;; da30 | da30-*) basic_machine=m68k-da30 ;; decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn) basic_machine=mips-dec ;; decsystem10* | dec10*) basic_machine=pdp10-dec os=-tops10 ;; decsystem20* | dec20*) basic_machine=pdp10-dec os=-tops20 ;; delta | 3300 | motorola-3300 | motorola-delta \ | 3300-motorola | delta-motorola) basic_machine=m68k-motorola ;; delta88) basic_machine=m88k-motorola os=-sysv3 ;; dicos) basic_machine=i686-pc os=-dicos ;; djgpp) basic_machine=i586-pc os=-msdosdjgpp ;; dpx20 | dpx20-*) basic_machine=rs6000-bull os=-bosx ;; dpx2* | dpx2*-bull) basic_machine=m68k-bull os=-sysv3 ;; ebmon29k) basic_machine=a29k-amd os=-ebmon ;; elxsi) basic_machine=elxsi-elxsi os=-bsd ;; encore | umax | mmax) basic_machine=ns32k-encore ;; es1800 | OSE68k | ose68k | ose | OSE) basic_machine=m68k-ericsson os=-ose ;; fx2800) basic_machine=i860-alliant ;; genix) basic_machine=ns32k-ns ;; gmicro) basic_machine=tron-gmicro os=-sysv ;; go32) basic_machine=i386-pc os=-go32 ;; h3050r* | hiux*) basic_machine=hppa1.1-hitachi os=-hiuxwe2 ;; h8300hms) basic_machine=h8300-hitachi os=-hms ;; h8300xray) basic_machine=h8300-hitachi os=-xray ;; h8500hms) basic_machine=h8500-hitachi os=-hms ;; harris) basic_machine=m88k-harris os=-sysv3 ;; hp300-*) basic_machine=m68k-hp ;; hp300bsd) basic_machine=m68k-hp os=-bsd ;; hp300hpux) basic_machine=m68k-hp os=-hpux ;; hp3k9[0-9][0-9] | hp9[0-9][0-9]) basic_machine=hppa1.0-hp ;; hp9k2[0-9][0-9] | hp9k31[0-9]) basic_machine=m68000-hp ;; hp9k3[2-9][0-9]) basic_machine=m68k-hp ;; hp9k6[0-9][0-9] | hp6[0-9][0-9]) basic_machine=hppa1.0-hp ;; hp9k7[0-79][0-9] | hp7[0-79][0-9]) basic_machine=hppa1.1-hp ;; hp9k78[0-9] | hp78[0-9]) # FIXME: really hppa2.0-hp basic_machine=hppa1.1-hp ;; hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) # FIXME: really hppa2.0-hp basic_machine=hppa1.1-hp ;; hp9k8[0-9][13679] | hp8[0-9][13679]) basic_machine=hppa1.1-hp ;; hp9k8[0-9][0-9] | hp8[0-9][0-9]) basic_machine=hppa1.0-hp ;; hppa-next) os=-nextstep3 ;; hppaosf) basic_machine=hppa1.1-hp os=-osf ;; hppro) basic_machine=hppa1.1-hp os=-proelf ;; i370-ibm* | ibm*) basic_machine=i370-ibm ;; i*86v32) basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` os=-sysv32 ;; i*86v4*) basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` os=-sysv4 ;; i*86v) basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` os=-sysv ;; i*86sol2) basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` os=-solaris2 ;; i386mach) basic_machine=i386-mach os=-mach ;; i386-vsta | vsta) basic_machine=i386-unknown os=-vsta ;; iris | iris4d) basic_machine=mips-sgi case $os in -irix*) ;; *) os=-irix4 ;; esac ;; isi68 | isi) basic_machine=m68k-isi os=-sysv ;; leon-*|leon[3-9]-*) basic_machine=sparc-`echo $basic_machine | sed 's/-.*//'` ;; m68knommu) basic_machine=m68k-unknown os=-linux ;; m68knommu-*) basic_machine=m68k-`echo $basic_machine | sed 's/^[^-]*-//'` os=-linux ;; m88k-omron*) basic_machine=m88k-omron ;; magnum | m3230) basic_machine=mips-mips os=-sysv ;; merlin) basic_machine=ns32k-utek os=-sysv ;; microblaze*) basic_machine=microblaze-xilinx ;; mingw64) basic_machine=x86_64-pc os=-mingw64 ;; mingw32) basic_machine=i686-pc os=-mingw32 ;; mingw32ce) basic_machine=arm-unknown os=-mingw32ce ;; miniframe) basic_machine=m68000-convergent ;; *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*) basic_machine=m68k-atari os=-mint ;; mips3*-*) basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'` ;; mips3*) basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown ;; monitor) basic_machine=m68k-rom68k os=-coff ;; morphos) basic_machine=powerpc-unknown os=-morphos ;; moxiebox) basic_machine=moxie-unknown os=-moxiebox ;; msdos) basic_machine=i386-pc os=-msdos ;; ms1-*) basic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'` ;; msys) basic_machine=i686-pc os=-msys ;; mvs) basic_machine=i370-ibm os=-mvs ;; nacl) basic_machine=le32-unknown os=-nacl ;; ncr3000) basic_machine=i486-ncr os=-sysv4 ;; netbsd386) basic_machine=i386-unknown os=-netbsd ;; netwinder) basic_machine=armv4l-rebel os=-linux ;; news | news700 | news800 | news900) basic_machine=m68k-sony os=-newsos ;; news1000) basic_machine=m68030-sony os=-newsos ;; news-3600 | risc-news) basic_machine=mips-sony os=-newsos ;; necv70) basic_machine=v70-nec os=-sysv ;; next | m*-next ) basic_machine=m68k-next case $os in -nextstep* ) ;; -ns2*) os=-nextstep2 ;; *) os=-nextstep3 ;; esac ;; nh3000) basic_machine=m68k-harris os=-cxux ;; nh[45]000) basic_machine=m88k-harris os=-cxux ;; nindy960) basic_machine=i960-intel os=-nindy ;; mon960) basic_machine=i960-intel os=-mon960 ;; nonstopux) basic_machine=mips-compaq os=-nonstopux ;; np1) basic_machine=np1-gould ;; neo-tandem) basic_machine=neo-tandem ;; nse-tandem) basic_machine=nse-tandem ;; nsr-tandem) basic_machine=nsr-tandem ;; op50n-* | op60c-*) basic_machine=hppa1.1-oki os=-proelf ;; openrisc | openrisc-*) basic_machine=or32-unknown ;; os400) basic_machine=powerpc-ibm os=-os400 ;; OSE68000 | ose68000) basic_machine=m68000-ericsson os=-ose ;; os68k) basic_machine=m68k-none os=-os68k ;; pa-hitachi) basic_machine=hppa1.1-hitachi os=-hiuxwe2 ;; paragon) basic_machine=i860-intel os=-osf ;; parisc) basic_machine=hppa-unknown os=-linux ;; parisc-*) basic_machine=hppa-`echo $basic_machine | sed 's/^[^-]*-//'` os=-linux ;; pbd) basic_machine=sparc-tti ;; pbb) basic_machine=m68k-tti ;; pc532 | pc532-*) basic_machine=ns32k-pc532 ;; pc98) basic_machine=i386-pc ;; pc98-*) basic_machine=i386-`echo $basic_machine | sed 's/^[^-]*-//'` ;; pentium | p5 | k5 | k6 | nexgen | viac3) basic_machine=i586-pc ;; pentiumpro | p6 | 6x86 | athlon | athlon_*) basic_machine=i686-pc ;; pentiumii | pentium2 | pentiumiii | pentium3) basic_machine=i686-pc ;; pentium4) basic_machine=i786-pc ;; pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*) basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'` ;; pentiumpro-* | p6-* | 6x86-* | athlon-*) basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` ;; pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*) basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` ;; pentium4-*) basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'` ;; pn) basic_machine=pn-gould ;; power) basic_machine=power-ibm ;; ppc | ppcbe) basic_machine=powerpc-unknown ;; ppc-* | ppcbe-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'` ;; ppcle | powerpclittle | ppc-le | powerpc-little) basic_machine=powerpcle-unknown ;; ppcle-* | powerpclittle-*) basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'` ;; ppc64) basic_machine=powerpc64-unknown ;; ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'` ;; ppc64le | powerpc64little | ppc64-le | powerpc64-little) basic_machine=powerpc64le-unknown ;; ppc64le-* | powerpc64little-*) basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'` ;; ps2) basic_machine=i386-ibm ;; pw32) basic_machine=i586-unknown os=-pw32 ;; rdos | rdos64) basic_machine=x86_64-pc os=-rdos ;; rdos32) basic_machine=i386-pc os=-rdos ;; rom68k) basic_machine=m68k-rom68k os=-coff ;; rm[46]00) basic_machine=mips-siemens ;; rtpc | rtpc-*) basic_machine=romp-ibm ;; s390 | s390-*) basic_machine=s390-ibm ;; s390x | s390x-*) basic_machine=s390x-ibm ;; sa29200) basic_machine=a29k-amd os=-udi ;; sb1) basic_machine=mipsisa64sb1-unknown ;; sb1el) basic_machine=mipsisa64sb1el-unknown ;; sde) basic_machine=mipsisa32-sde os=-elf ;; sei) basic_machine=mips-sei os=-seiux ;; sequent) basic_machine=i386-sequent ;; sh) basic_machine=sh-hitachi os=-hms ;; sh5el) basic_machine=sh5le-unknown ;; sh64) basic_machine=sh64-unknown ;; sparclite-wrs | simso-wrs) basic_machine=sparclite-wrs os=-vxworks ;; sps7) basic_machine=m68k-bull os=-sysv2 ;; spur) basic_machine=spur-unknown ;; st2000) basic_machine=m68k-tandem ;; stratus) basic_machine=i860-stratus os=-sysv4 ;; strongarm-* | thumb-*) basic_machine=arm-`echo $basic_machine | sed 's/^[^-]*-//'` ;; sun2) basic_machine=m68000-sun ;; sun2os3) basic_machine=m68000-sun os=-sunos3 ;; sun2os4) basic_machine=m68000-sun os=-sunos4 ;; sun3os3) basic_machine=m68k-sun os=-sunos3 ;; sun3os4) basic_machine=m68k-sun os=-sunos4 ;; sun4os3) basic_machine=sparc-sun os=-sunos3 ;; sun4os4) basic_machine=sparc-sun os=-sunos4 ;; sun4sol2) basic_machine=sparc-sun os=-solaris2 ;; sun3 | sun3-*) basic_machine=m68k-sun ;; sun4) basic_machine=sparc-sun ;; sun386 | sun386i | roadrunner) basic_machine=i386-sun ;; sv1) basic_machine=sv1-cray os=-unicos ;; symmetry) basic_machine=i386-sequent os=-dynix ;; t3e) basic_machine=alphaev5-cray os=-unicos ;; t90) basic_machine=t90-cray os=-unicos ;; tile*) basic_machine=$basic_machine-unknown os=-linux-gnu ;; tx39) basic_machine=mipstx39-unknown ;; tx39el) basic_machine=mipstx39el-unknown ;; toad1) basic_machine=pdp10-xkl os=-tops20 ;; tower | tower-32) basic_machine=m68k-ncr ;; tpf) basic_machine=s390x-ibm os=-tpf ;; udi29k) basic_machine=a29k-amd os=-udi ;; ultra3) basic_machine=a29k-nyu os=-sym1 ;; v810 | necv810) basic_machine=v810-nec os=-none ;; vaxv) basic_machine=vax-dec os=-sysv ;; vms) basic_machine=vax-dec os=-vms ;; vpp*|vx|vx-*) basic_machine=f301-fujitsu ;; vxworks960) basic_machine=i960-wrs os=-vxworks ;; vxworks68) basic_machine=m68k-wrs os=-vxworks ;; vxworks29k) basic_machine=a29k-wrs os=-vxworks ;; w65*) basic_machine=w65-wdc os=-none ;; w89k-*) basic_machine=hppa1.1-winbond os=-proelf ;; xbox) basic_machine=i686-pc os=-mingw32 ;; xps | xps100) basic_machine=xps100-honeywell ;; xscale-* | xscalee[bl]-*) basic_machine=`echo $basic_machine | sed 's/^xscale/arm/'` ;; ymp) basic_machine=ymp-cray os=-unicos ;; z8k-*-coff) basic_machine=z8k-unknown os=-sim ;; z80-*-coff) basic_machine=z80-unknown os=-sim ;; none) basic_machine=none-none os=-none ;; # Here we handle the default manufacturer of certain CPU types. It is in # some cases the only manufacturer, in others, it is the most popular. w89k) basic_machine=hppa1.1-winbond ;; op50n) basic_machine=hppa1.1-oki ;; op60c) basic_machine=hppa1.1-oki ;; romp) basic_machine=romp-ibm ;; mmix) basic_machine=mmix-knuth ;; rs6000) basic_machine=rs6000-ibm ;; vax) basic_machine=vax-dec ;; pdp10) # there are many clones, so DEC is not a safe bet basic_machine=pdp10-unknown ;; pdp11) basic_machine=pdp11-dec ;; we32k) basic_machine=we32k-att ;; sh[1234] | sh[24]a | sh[24]aeb | sh[34]eb | sh[1234]le | sh[23]ele) basic_machine=sh-unknown ;; sparc | sparcv8 | sparcv9 | sparcv9b | sparcv9v) basic_machine=sparc-sun ;; cydra) basic_machine=cydra-cydrome ;; orion) basic_machine=orion-highlevel ;; orion105) basic_machine=clipper-highlevel ;; mac | mpw | mac-mpw) basic_machine=m68k-apple ;; pmac | pmac-mpw) basic_machine=powerpc-apple ;; *-unknown) # Make sure to match an already-canonicalized machine name. ;; *) echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 exit 1 ;; esac # Here we canonicalize certain aliases for manufacturers. case $basic_machine in *-digital*) basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'` ;; *-commodore*) basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'` ;; *) ;; esac # Decode manufacturer-specific aliases for certain operating systems. if [ x"$os" != x"" ] then case $os in # First match some system type aliases # that might get confused with valid system types. # -solaris* is a basic system type, with this one exception. -auroraux) os=-auroraux ;; -solaris1 | -solaris1.*) os=`echo $os | sed -e 's|solaris1|sunos4|'` ;; -solaris) os=-solaris2 ;; -svr4*) os=-sysv4 ;; -unixware*) os=-sysv4.2uw ;; -gnu/linux*) os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'` ;; # First accept the basic system types. # The portable systems comes first. # Each alternative MUST END IN A *, to match a version number. # -sysv* is not here because it comes later, after sysvr4. -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \ | -*vms* | -sco* | -esix* | -isc* | -aix* | -cnk* | -sunos | -sunos[34]*\ | -hpux* | -unos* | -osf* | -luna* | -dgux* | -auroraux* | -solaris* \ | -sym* | -kopensolaris* | -plan9* \ | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \ | -aos* | -aros* | -cloudabi* | -sortix* \ | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \ | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \ | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \ | -bitrig* | -openbsd* | -solidbsd* \ | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \ | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \ | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \ | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \ | -chorusos* | -chorusrdb* | -cegcc* \ | -cygwin* | -msys* | -pe* | -psos* | -moss* | -proelf* | -rtems* \ | -mingw32* | -mingw64* | -linux-gnu* | -linux-android* \ | -linux-newlib* | -linux-musl* | -linux-uclibc* \ | -uxpv* | -beos* | -mpeix* | -udk* | -moxiebox* \ | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \ | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \ | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \ | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \ | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \ | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \ | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es* | -tirtos*) # Remember, each alternative MUST END IN *, to match a version number. ;; -qnx*) case $basic_machine in x86-* | i*86-*) ;; *) os=-nto$os ;; esac ;; -nto-qnx*) ;; -nto*) os=`echo $os | sed -e 's|nto|nto-qnx|'` ;; -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \ | -windows* | -osx | -abug | -netware* | -os9* | -beos* | -haiku* \ | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*) ;; -mac*) os=`echo $os | sed -e 's|mac|macos|'` ;; -linux-dietlibc) os=-linux-dietlibc ;; -linux*) os=`echo $os | sed -e 's|linux|linux-gnu|'` ;; -sunos5*) os=`echo $os | sed -e 's|sunos5|solaris2|'` ;; -sunos6*) os=`echo $os | sed -e 's|sunos6|solaris3|'` ;; -opened*) os=-openedition ;; -os400*) os=-os400 ;; -wince*) os=-wince ;; -osfrose*) os=-osfrose ;; -osf*) os=-osf ;; -utek*) os=-bsd ;; -dynix*) os=-bsd ;; -acis*) os=-aos ;; -atheos*) os=-atheos ;; -syllable*) os=-syllable ;; -386bsd) os=-bsd ;; -ctix* | -uts*) os=-sysv ;; -nova*) os=-rtmk-nova ;; -ns2 ) os=-nextstep2 ;; -nsk*) os=-nsk ;; # Preserve the version number of sinix5. -sinix5.*) os=`echo $os | sed -e 's|sinix|sysv|'` ;; -sinix*) os=-sysv4 ;; -tpf*) os=-tpf ;; -triton*) os=-sysv3 ;; -oss*) os=-sysv3 ;; -svr4) os=-sysv4 ;; -svr3) os=-sysv3 ;; -sysvr4) os=-sysv4 ;; # This must come after -sysvr4. -sysv*) ;; -ose*) os=-ose ;; -es1800*) os=-ose ;; -xenix) os=-xenix ;; -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) os=-mint ;; -aros*) os=-aros ;; -zvmoe) os=-zvmoe ;; -dicos*) os=-dicos ;; -nacl*) ;; -none) ;; *) # Get rid of the `-' at the beginning of $os. os=`echo $os | sed 's/[^-]*-//'` echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2 exit 1 ;; esac else # Here we handle the default operating systems that come with various machines. # The value should be what the vendor currently ships out the door with their # machine or put another way, the most popular os provided with the machine. # Note that if you're going to try to match "-MANUFACTURER" here (say, # "-sun"), then you have to tell the case statement up towards the top # that MANUFACTURER isn't an operating system. Otherwise, code above # will signal an error saying that MANUFACTURER isn't an operating # system, and we'll never get to this point. case $basic_machine in score-*) os=-elf ;; spu-*) os=-elf ;; *-acorn) os=-riscix1.2 ;; arm*-rebel) os=-linux ;; arm*-semi) os=-aout ;; c4x-* | tic4x-*) os=-coff ;; c8051-*) os=-elf ;; hexagon-*) os=-elf ;; tic54x-*) os=-coff ;; tic55x-*) os=-coff ;; tic6x-*) os=-coff ;; # This must come before the *-dec entry. pdp10-*) os=-tops20 ;; pdp11-*) os=-none ;; *-dec | vax-*) os=-ultrix4.2 ;; m68*-apollo) os=-domain ;; i386-sun) os=-sunos4.0.2 ;; m68000-sun) os=-sunos3 ;; m68*-cisco) os=-aout ;; mep-*) os=-elf ;; mips*-cisco) os=-elf ;; mips*-*) os=-elf ;; or32-*) os=-coff ;; *-tti) # must be before sparc entry or we get the wrong os. os=-sysv3 ;; sparc-* | *-sun) os=-sunos4.1.1 ;; *-be) os=-beos ;; *-haiku) os=-haiku ;; *-ibm) os=-aix ;; *-knuth) os=-mmixware ;; *-wec) os=-proelf ;; *-winbond) os=-proelf ;; *-oki) os=-proelf ;; *-hp) os=-hpux ;; *-hitachi) os=-hiux ;; i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent) os=-sysv ;; *-cbm) os=-amigaos ;; *-dg) os=-dgux ;; *-dolphin) os=-sysv3 ;; m68k-ccur) os=-rtu ;; m88k-omron*) os=-luna ;; *-next ) os=-nextstep ;; *-sequent) os=-ptx ;; *-crds) os=-unos ;; *-ns) os=-genix ;; i370-*) os=-mvs ;; *-next) os=-nextstep3 ;; *-gould) os=-sysv ;; *-highlevel) os=-bsd ;; *-encore) os=-bsd ;; *-sgi) os=-irix ;; *-siemens) os=-sysv4 ;; *-masscomp) os=-rtu ;; f30[01]-fujitsu | f700-fujitsu) os=-uxpv ;; *-rom68k) os=-coff ;; *-*bug) os=-coff ;; *-apple) os=-macos ;; *-atari*) os=-mint ;; *) os=-none ;; esac fi # Here we handle the case where we know the os, and the CPU type, but not the # manufacturer. We pick the logical manufacturer. vendor=unknown case $basic_machine in *-unknown) case $os in -riscix*) vendor=acorn ;; -sunos*) vendor=sun ;; -cnk*|-aix*) vendor=ibm ;; -beos*) vendor=be ;; -hpux*) vendor=hp ;; -mpeix*) vendor=hp ;; -hiux*) vendor=hitachi ;; -unos*) vendor=crds ;; -dgux*) vendor=dg ;; -luna*) vendor=omron ;; -genix*) vendor=ns ;; -mvs* | -opened*) vendor=ibm ;; -os400*) vendor=ibm ;; -ptx*) vendor=sequent ;; -tpf*) vendor=ibm ;; -vxsim* | -vxworks* | -windiss*) vendor=wrs ;; -aux*) vendor=apple ;; -hms*) vendor=hitachi ;; -mpw* | -macos*) vendor=apple ;; -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) vendor=atari ;; -vos*) vendor=stratus ;; esac basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"` ;; esac echo $basic_machine$os exit # Local variables: # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "timestamp='" # time-stamp-format: "%:y-%02m-%02d" # time-stamp-end: "'" # End: memcached-1.5.6/LICENSE.bipbuffer0000664000175000017500000000270013115057711013324 00000000000000Copyright (c) 2011, Willem-Hendrik Thiart All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * 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. * The names of its contributors may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 WILLEM-HENDRIK THIART 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. memcached-1.5.6/compile0000755000175000017500000001624512676613141011747 00000000000000#! /bin/sh # Wrapper for compilers which do not understand '-c -o'. scriptversion=2012-10-14.11; # UTC # Copyright (C) 1999-2014 Free Software Foundation, Inc. # Written by Tom Tromey . # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2, or (at your option) # any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. # This file is maintained in Automake, please report # bugs to or send patches to # . nl=' ' # We need space, tab and new line, in precisely that order. Quoting is # there to prevent tools from complaining about whitespace usage. IFS=" "" $nl" file_conv= # func_file_conv build_file lazy # Convert a $build file to $host form and store it in $file # Currently only supports Windows hosts. If the determined conversion # type is listed in (the comma separated) LAZY, no conversion will # take place. func_file_conv () { file=$1 case $file in / | /[!/]*) # absolute file, and not a UNC file if test -z "$file_conv"; then # lazily determine how to convert abs files case `uname -s` in MINGW*) file_conv=mingw ;; CYGWIN*) file_conv=cygwin ;; *) file_conv=wine ;; esac fi case $file_conv/,$2, in *,$file_conv,*) ;; mingw/*) file=`cmd //C echo "$file " | sed -e 's/"\(.*\) " *$/\1/'` ;; cygwin/*) file=`cygpath -m "$file" || echo "$file"` ;; wine/*) file=`winepath -w "$file" || echo "$file"` ;; esac ;; esac } # func_cl_dashL linkdir # Make cl look for libraries in LINKDIR func_cl_dashL () { func_file_conv "$1" if test -z "$lib_path"; then lib_path=$file else lib_path="$lib_path;$file" fi linker_opts="$linker_opts -LIBPATH:$file" } # func_cl_dashl library # Do a library search-path lookup for cl func_cl_dashl () { lib=$1 found=no save_IFS=$IFS IFS=';' for dir in $lib_path $LIB do IFS=$save_IFS if $shared && test -f "$dir/$lib.dll.lib"; then found=yes lib=$dir/$lib.dll.lib break fi if test -f "$dir/$lib.lib"; then found=yes lib=$dir/$lib.lib break fi if test -f "$dir/lib$lib.a"; then found=yes lib=$dir/lib$lib.a break fi done IFS=$save_IFS if test "$found" != yes; then lib=$lib.lib fi } # func_cl_wrapper cl arg... # Adjust compile command to suit cl func_cl_wrapper () { # Assume a capable shell lib_path= shared=: linker_opts= for arg do if test -n "$eat"; then eat= else case $1 in -o) # configure might choose to run compile as 'compile cc -o foo foo.c'. eat=1 case $2 in *.o | *.[oO][bB][jJ]) func_file_conv "$2" set x "$@" -Fo"$file" shift ;; *) func_file_conv "$2" set x "$@" -Fe"$file" shift ;; esac ;; -I) eat=1 func_file_conv "$2" mingw set x "$@" -I"$file" shift ;; -I*) func_file_conv "${1#-I}" mingw set x "$@" -I"$file" shift ;; -l) eat=1 func_cl_dashl "$2" set x "$@" "$lib" shift ;; -l*) func_cl_dashl "${1#-l}" set x "$@" "$lib" shift ;; -L) eat=1 func_cl_dashL "$2" ;; -L*) func_cl_dashL "${1#-L}" ;; -static) shared=false ;; -Wl,*) arg=${1#-Wl,} save_ifs="$IFS"; IFS=',' for flag in $arg; do IFS="$save_ifs" linker_opts="$linker_opts $flag" done IFS="$save_ifs" ;; -Xlinker) eat=1 linker_opts="$linker_opts $2" ;; -*) set x "$@" "$1" shift ;; *.cc | *.CC | *.cxx | *.CXX | *.[cC]++) func_file_conv "$1" set x "$@" -Tp"$file" shift ;; *.c | *.cpp | *.CPP | *.lib | *.LIB | *.Lib | *.OBJ | *.obj | *.[oO]) func_file_conv "$1" mingw set x "$@" "$file" shift ;; *) set x "$@" "$1" shift ;; esac fi shift done if test -n "$linker_opts"; then linker_opts="-link$linker_opts" fi exec "$@" $linker_opts exit 1 } eat= case $1 in '') echo "$0: No command. Try '$0 --help' for more information." 1>&2 exit 1; ;; -h | --h*) cat <<\EOF Usage: compile [--help] [--version] PROGRAM [ARGS] Wrapper for compilers which do not understand '-c -o'. Remove '-o dest.o' from ARGS, run PROGRAM with the remaining arguments, and rename the output as expected. If you are trying to build a whole package this is not the right script to run: please start by reading the file 'INSTALL'. Report bugs to . EOF exit $? ;; -v | --v*) echo "compile $scriptversion" exit $? ;; cl | *[/\\]cl | cl.exe | *[/\\]cl.exe ) func_cl_wrapper "$@" # Doesn't return... ;; esac ofile= cfile= for arg do if test -n "$eat"; then eat= else case $1 in -o) # configure might choose to run compile as 'compile cc -o foo foo.c'. # So we strip '-o arg' only if arg is an object. eat=1 case $2 in *.o | *.obj) ofile=$2 ;; *) set x "$@" -o "$2" shift ;; esac ;; *.c) cfile=$1 set x "$@" "$1" shift ;; *) set x "$@" "$1" shift ;; esac fi shift done if test -z "$ofile" || test -z "$cfile"; then # If no '-o' option was seen then we might have been invoked from a # pattern rule where we don't need one. That is ok -- this is a # normal compilation that the losing compiler can handle. If no # '.c' file was seen then we are probably linking. That is also # ok. exec "$@" fi # Name of file we expect compiler to create. cofile=`echo "$cfile" | sed 's|^.*[\\/]||; s|^[a-zA-Z]:||; s/\.c$/.o/'` # Create the lock directory. # Note: use '[/\\:.-]' here to ensure that we don't use the same name # that we are using for the .o file. Also, base the name on the expected # object file name, since that is what matters with a parallel build. lockdir=`echo "$cofile" | sed -e 's|[/\\:.-]|_|g'`.d while true; do if mkdir "$lockdir" >/dev/null 2>&1; then break fi sleep 1 done # FIXME: race condition here if user kills between mkdir and trap. trap "rmdir '$lockdir'; exit 1" 1 2 15 # Run the compile. "$@" ret=$? if test -f "$cofile"; then test "$cofile" = "$ofile" || mv "$cofile" "$ofile" elif test -f "${cofile}bj"; then test "${cofile}bj" = "$ofile" || mv "${cofile}bj" "$ofile" fi rmdir "$lockdir" exit $ret # Local Variables: # mode: shell-script # sh-indentation: 2 # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC" # time-stamp-end: "; # UTC" # End: memcached-1.5.6/jenkins_hash.h0000664000175000017500000000032513025643161013172 00000000000000#ifndef JENKINS_HASH_H #define JENKINS_HASH_H #ifdef __cplusplus extern "C" { #endif uint32_t jenkins_hash(const void *key, size_t length); #ifdef __cplusplus } #endif #endif /* JENKINS_HASH_H */ memcached-1.5.6/protocol_binary.h0000664000175000017500000004021513115057711013735 00000000000000/* * Copyright (c) <2008>, Sun Microsystems, 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: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 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. * * Neither the name of the 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 SUN MICROSYSTEMS, INC. ``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 SUN MICROSYSTEMS, INC. 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. */ /* * Summary: Constants used by to implement the binary protocol. * * Copy: See Copyright for the status of this software. * * Author: Trond Norbye */ #ifndef PROTOCOL_BINARY_H #define PROTOCOL_BINARY_H /** * This file contains definitions of the constants and packet formats * defined in the binary specification. Please note that you _MUST_ remember * to convert each multibyte field to / from network byte order to / from * host order. */ #ifdef __cplusplus extern "C" { #endif /** * Definition of the legal "magic" values used in a packet. * See section 3.1 Magic byte */ typedef enum { PROTOCOL_BINARY_REQ = 0x80, PROTOCOL_BINARY_RES = 0x81 } protocol_binary_magic; /** * Definition of the valid response status numbers. * See section 3.2 Response Status */ typedef enum { PROTOCOL_BINARY_RESPONSE_SUCCESS = 0x00, PROTOCOL_BINARY_RESPONSE_KEY_ENOENT = 0x01, PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS = 0x02, PROTOCOL_BINARY_RESPONSE_E2BIG = 0x03, PROTOCOL_BINARY_RESPONSE_EINVAL = 0x04, PROTOCOL_BINARY_RESPONSE_NOT_STORED = 0x05, PROTOCOL_BINARY_RESPONSE_DELTA_BADVAL = 0x06, PROTOCOL_BINARY_RESPONSE_AUTH_ERROR = 0x20, PROTOCOL_BINARY_RESPONSE_AUTH_CONTINUE = 0x21, PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND = 0x81, PROTOCOL_BINARY_RESPONSE_ENOMEM = 0x82 } protocol_binary_response_status; /** * Definition of the different command opcodes. * See section 3.3 Command Opcodes */ typedef enum { PROTOCOL_BINARY_CMD_GET = 0x00, PROTOCOL_BINARY_CMD_SET = 0x01, PROTOCOL_BINARY_CMD_ADD = 0x02, PROTOCOL_BINARY_CMD_REPLACE = 0x03, PROTOCOL_BINARY_CMD_DELETE = 0x04, PROTOCOL_BINARY_CMD_INCREMENT = 0x05, PROTOCOL_BINARY_CMD_DECREMENT = 0x06, PROTOCOL_BINARY_CMD_QUIT = 0x07, PROTOCOL_BINARY_CMD_FLUSH = 0x08, PROTOCOL_BINARY_CMD_GETQ = 0x09, PROTOCOL_BINARY_CMD_NOOP = 0x0a, PROTOCOL_BINARY_CMD_VERSION = 0x0b, PROTOCOL_BINARY_CMD_GETK = 0x0c, PROTOCOL_BINARY_CMD_GETKQ = 0x0d, PROTOCOL_BINARY_CMD_APPEND = 0x0e, PROTOCOL_BINARY_CMD_PREPEND = 0x0f, PROTOCOL_BINARY_CMD_STAT = 0x10, PROTOCOL_BINARY_CMD_SETQ = 0x11, PROTOCOL_BINARY_CMD_ADDQ = 0x12, PROTOCOL_BINARY_CMD_REPLACEQ = 0x13, PROTOCOL_BINARY_CMD_DELETEQ = 0x14, PROTOCOL_BINARY_CMD_INCREMENTQ = 0x15, PROTOCOL_BINARY_CMD_DECREMENTQ = 0x16, PROTOCOL_BINARY_CMD_QUITQ = 0x17, PROTOCOL_BINARY_CMD_FLUSHQ = 0x18, PROTOCOL_BINARY_CMD_APPENDQ = 0x19, PROTOCOL_BINARY_CMD_PREPENDQ = 0x1a, PROTOCOL_BINARY_CMD_TOUCH = 0x1c, PROTOCOL_BINARY_CMD_GAT = 0x1d, PROTOCOL_BINARY_CMD_GATQ = 0x1e, PROTOCOL_BINARY_CMD_GATK = 0x23, PROTOCOL_BINARY_CMD_GATKQ = 0x24, PROTOCOL_BINARY_CMD_SASL_LIST_MECHS = 0x20, PROTOCOL_BINARY_CMD_SASL_AUTH = 0x21, PROTOCOL_BINARY_CMD_SASL_STEP = 0x22, /* These commands are used for range operations and exist within * this header for use in other projects. Range operations are * not expected to be implemented in the memcached server itself. */ PROTOCOL_BINARY_CMD_RGET = 0x30, PROTOCOL_BINARY_CMD_RSET = 0x31, PROTOCOL_BINARY_CMD_RSETQ = 0x32, PROTOCOL_BINARY_CMD_RAPPEND = 0x33, PROTOCOL_BINARY_CMD_RAPPENDQ = 0x34, PROTOCOL_BINARY_CMD_RPREPEND = 0x35, PROTOCOL_BINARY_CMD_RPREPENDQ = 0x36, PROTOCOL_BINARY_CMD_RDELETE = 0x37, PROTOCOL_BINARY_CMD_RDELETEQ = 0x38, PROTOCOL_BINARY_CMD_RINCR = 0x39, PROTOCOL_BINARY_CMD_RINCRQ = 0x3a, PROTOCOL_BINARY_CMD_RDECR = 0x3b, PROTOCOL_BINARY_CMD_RDECRQ = 0x3c /* End Range operations */ } protocol_binary_command; /** * Definition of the data types in the packet * See section 3.4 Data Types */ typedef enum { PROTOCOL_BINARY_RAW_BYTES = 0x00 } protocol_binary_datatypes; /** * Definition of the header structure for a request packet. * See section 2 */ typedef union { struct { uint8_t magic; uint8_t opcode; uint16_t keylen; uint8_t extlen; uint8_t datatype; uint16_t reserved; uint32_t bodylen; uint32_t opaque; uint64_t cas; } request; uint8_t bytes[24]; } protocol_binary_request_header; /** * Definition of the header structure for a response packet. * See section 2 */ typedef union { struct { uint8_t magic; uint8_t opcode; uint16_t keylen; uint8_t extlen; uint8_t datatype; uint16_t status; uint32_t bodylen; uint32_t opaque; uint64_t cas; } response; uint8_t bytes[24]; } protocol_binary_response_header; /** * Definition of a request-packet containing no extras */ typedef union { struct { protocol_binary_request_header header; } message; uint8_t bytes[sizeof(protocol_binary_request_header)]; } protocol_binary_request_no_extras; /** * Definition of a response-packet containing no extras */ typedef union { struct { protocol_binary_response_header header; } message; uint8_t bytes[sizeof(protocol_binary_response_header)]; } protocol_binary_response_no_extras; /** * Definition of the packet used by the get, getq, getk and getkq command. * See section 4 */ typedef protocol_binary_request_no_extras protocol_binary_request_get; typedef protocol_binary_request_no_extras protocol_binary_request_getq; typedef protocol_binary_request_no_extras protocol_binary_request_getk; typedef protocol_binary_request_no_extras protocol_binary_request_getkq; /** * Definition of the packet returned from a successful get, getq, getk and * getkq. * See section 4 */ typedef union { struct { protocol_binary_response_header header; struct { uint32_t flags; } body; } message; uint8_t bytes[sizeof(protocol_binary_response_header) + 4]; } protocol_binary_response_get; typedef protocol_binary_response_get protocol_binary_response_getq; typedef protocol_binary_response_get protocol_binary_response_getk; typedef protocol_binary_response_get protocol_binary_response_getkq; /** * Definition of the packet used by the delete command * See section 4 */ typedef protocol_binary_request_no_extras protocol_binary_request_delete; /** * Definition of the packet returned by the delete command * See section 4 */ typedef protocol_binary_response_no_extras protocol_binary_response_delete; /** * Definition of the packet used by the flush command * See section 4 * Please note that the expiration field is optional, so remember to see * check the header.bodysize to see if it is present. */ typedef union { struct { protocol_binary_request_header header; struct { uint32_t expiration; } body; } message; uint8_t bytes[sizeof(protocol_binary_request_header) + 4]; } protocol_binary_request_flush; /** * Definition of the packet returned by the flush command * See section 4 */ typedef protocol_binary_response_no_extras protocol_binary_response_flush; /** * Definition of the packet used by set, add and replace * See section 4 */ typedef union { struct { protocol_binary_request_header header; struct { uint32_t flags; uint32_t expiration; } body; } message; uint8_t bytes[sizeof(protocol_binary_request_header) + 8]; } protocol_binary_request_set; typedef protocol_binary_request_set protocol_binary_request_add; typedef protocol_binary_request_set protocol_binary_request_replace; /** * Definition of the packet returned by set, add and replace * See section 4 */ typedef protocol_binary_response_no_extras protocol_binary_response_set; typedef protocol_binary_response_no_extras protocol_binary_response_add; typedef protocol_binary_response_no_extras protocol_binary_response_replace; /** * Definition of the noop packet * See section 4 */ typedef protocol_binary_request_no_extras protocol_binary_request_noop; /** * Definition of the packet returned by the noop command * See section 4 */ typedef protocol_binary_response_no_extras protocol_binary_response_noop; /** * Definition of the structure used by the increment and decrement * command. * See section 4 */ typedef union { struct { protocol_binary_request_header header; struct { uint64_t delta; uint64_t initial; uint32_t expiration; } body; } message; uint8_t bytes[sizeof(protocol_binary_request_header) + 20]; } protocol_binary_request_incr; typedef protocol_binary_request_incr protocol_binary_request_decr; /** * Definition of the response from an incr or decr command * command. * See section 4 */ typedef union { struct { protocol_binary_response_header header; struct { uint64_t value; } body; } message; uint8_t bytes[sizeof(protocol_binary_response_header) + 8]; } protocol_binary_response_incr; typedef protocol_binary_response_incr protocol_binary_response_decr; /** * Definition of the quit * See section 4 */ typedef protocol_binary_request_no_extras protocol_binary_request_quit; /** * Definition of the packet returned by the quit command * See section 4 */ typedef protocol_binary_response_no_extras protocol_binary_response_quit; /** * Definition of the packet used by append and prepend command * See section 4 */ typedef protocol_binary_request_no_extras protocol_binary_request_append; typedef protocol_binary_request_no_extras protocol_binary_request_prepend; /** * Definition of the packet returned from a successful append or prepend * See section 4 */ typedef protocol_binary_response_no_extras protocol_binary_response_append; typedef protocol_binary_response_no_extras protocol_binary_response_prepend; /** * Definition of the packet used by the version command * See section 4 */ typedef protocol_binary_request_no_extras protocol_binary_request_version; /** * Definition of the packet returned from a successful version command * See section 4 */ typedef protocol_binary_response_no_extras protocol_binary_response_version; /** * Definition of the packet used by the stats command. * See section 4 */ typedef protocol_binary_request_no_extras protocol_binary_request_stats; /** * Definition of the packet returned from a successful stats command * See section 4 */ typedef protocol_binary_response_no_extras protocol_binary_response_stats; /** * Definition of the packet used by the touch command. */ typedef union { struct { protocol_binary_request_header header; struct { uint32_t expiration; } body; } message; uint8_t bytes[sizeof(protocol_binary_request_header) + 4]; } protocol_binary_request_touch; /** * Definition of the packet returned from the touch command */ typedef protocol_binary_response_no_extras protocol_binary_response_touch; /** * Definition of the packet used by the GAT(Q) command. */ typedef union { struct { protocol_binary_request_header header; struct { uint32_t expiration; } body; } message; uint8_t bytes[sizeof(protocol_binary_request_header) + 4]; } protocol_binary_request_gat; typedef protocol_binary_request_gat protocol_binary_request_gatq; typedef protocol_binary_request_gat protocol_binary_request_gatk; typedef protocol_binary_request_gat protocol_binary_request_gatkq; /** * Definition of the packet returned from the GAT(Q) */ typedef protocol_binary_response_get protocol_binary_response_gat; typedef protocol_binary_response_get protocol_binary_response_gatq; typedef protocol_binary_response_get protocol_binary_response_gatk; typedef protocol_binary_response_get protocol_binary_response_gatkq; /** * Definition of a request for a range operation. * See http://code.google.com/p/memcached/wiki/RangeOps * * These types are used for range operations and exist within * this header for use in other projects. Range operations are * not expected to be implemented in the memcached server itself. */ typedef union { struct { protocol_binary_response_header header; struct { uint16_t size; uint8_t reserved; uint8_t flags; uint32_t max_results; } body; } message; uint8_t bytes[sizeof(protocol_binary_request_header) + 4]; } protocol_binary_request_rangeop; typedef protocol_binary_request_rangeop protocol_binary_request_rget; typedef protocol_binary_request_rangeop protocol_binary_request_rset; typedef protocol_binary_request_rangeop protocol_binary_request_rsetq; typedef protocol_binary_request_rangeop protocol_binary_request_rappend; typedef protocol_binary_request_rangeop protocol_binary_request_rappendq; typedef protocol_binary_request_rangeop protocol_binary_request_rprepend; typedef protocol_binary_request_rangeop protocol_binary_request_rprependq; typedef protocol_binary_request_rangeop protocol_binary_request_rdelete; typedef protocol_binary_request_rangeop protocol_binary_request_rdeleteq; typedef protocol_binary_request_rangeop protocol_binary_request_rincr; typedef protocol_binary_request_rangeop protocol_binary_request_rincrq; typedef protocol_binary_request_rangeop protocol_binary_request_rdecr; typedef protocol_binary_request_rangeop protocol_binary_request_rdecrq; #ifdef __cplusplus } #endif #endif /* PROTOCOL_BINARY_H */ memcached-1.5.6/extstore.h0000664000175000017500000000777313240770206012421 00000000000000#ifndef EXTSTORE_H #define EXTSTORE_H /* A safe-to-read dataset for determining compaction. * id is the array index. */ struct extstore_page_data { uint64_t version; uint64_t bytes_used; unsigned int bucket; }; /* Pages can have objects deleted from them at any time. This creates holes * that can't be reused until the page is either evicted or all objects are * deleted. * bytes_fragmented is the total bytes for all of these holes. * It is the size of all used pages minus each page's bytes_used value. */ struct extstore_stats { uint64_t page_allocs; uint64_t page_count; /* total page count */ uint64_t page_evictions; uint64_t page_reclaims; uint64_t page_size; /* size in bytes per page (supplied by caller) */ uint64_t pages_free; /* currently unallocated/unused pages */ uint64_t pages_used; uint64_t objects_evicted; uint64_t objects_read; uint64_t objects_written; uint64_t objects_used; /* total number of objects stored */ uint64_t bytes_evicted; uint64_t bytes_written; uint64_t bytes_read; /* wbuf - read -> bytes read from storage */ uint64_t bytes_used; /* total number of bytes stored */ uint64_t bytes_fragmented; /* see above comment */ struct extstore_page_data *page_data; }; // TODO: Temporary configuration structure. A "real" library should have an // extstore_set(enum, void *ptr) which hides the implementation. // this is plenty for quick development. struct extstore_conf { unsigned int page_size; // ideally 64-256M in size unsigned int page_count; unsigned int page_buckets; // number of different writeable pages unsigned int wbuf_size; // must divide cleanly into page_size unsigned int wbuf_count; // this might get locked to "2 per active page" unsigned int io_threadcount; unsigned int io_depth; // with normal I/O, hits locks less. req'd for AIO }; enum obj_io_mode { OBJ_IO_READ = 0, OBJ_IO_WRITE, }; typedef struct _obj_io obj_io; typedef void (*obj_io_cb)(void *e, obj_io *io, int ret); /* An object for both reads and writes to the storage engine. * Once an IO is submitted, ->next may be changed by the IO thread. It is not * safe to further modify the IO stack until the entire request is completed. */ struct _obj_io { void *data; /* user supplied data pointer */ struct _obj_io *next; char *buf; /* buffer of data to read or write to */ struct iovec *iov; /* alternatively, use this iovec */ unsigned int iovcnt; /* number of IOV's */ unsigned int page_version; /* page version for read mode */ unsigned int len; /* for both modes */ unsigned int offset; /* for read mode */ unsigned short page_id; /* for read mode */ enum obj_io_mode mode; /* callback pointers? */ obj_io_cb cb; }; enum extstore_res { EXTSTORE_INIT_BAD_WBUF_SIZE = 1, EXTSTORE_INIT_NEED_MORE_WBUF, EXTSTORE_INIT_NEED_MORE_BUCKETS, EXTSTORE_INIT_PAGE_WBUF_ALIGNMENT, EXTSTORE_INIT_OOM, EXTSTORE_INIT_OPEN_FAIL, EXTSTORE_INIT_THREAD_FAIL }; const char *extstore_err(enum extstore_res res); void *extstore_init(char *fn, struct extstore_conf *cf, enum extstore_res *res); int extstore_write_request(void *ptr, unsigned int bucket, obj_io *io); void extstore_write(void *ptr, obj_io *io); int extstore_submit(void *ptr, obj_io *io); /* count are the number of objects being removed, bytes are the original * length of those objects. Bytes is optional but you can't track * fragmentation without it. */ int extstore_check(void *ptr, unsigned int page_id, uint64_t page_version); int extstore_delete(void *ptr, unsigned int page_id, uint64_t page_version, unsigned int count, unsigned int bytes); void extstore_get_stats(void *ptr, struct extstore_stats *st); /* add page data array to a stats structure. * caller must allocate its stats.page_data memory first. */ void extstore_get_page_data(void *ptr, struct extstore_stats *st); void extstore_run_maint(void *ptr); void extstore_close_page(void *ptr, unsigned int page_id, uint64_t page_version); #endif memcached-1.5.6/murmur3_hash.c0000664000175000017500000000541213115057711013140 00000000000000//----------------------------------------------------------------------------- // MurmurHash3 was written by Austin Appleby, and is placed in the public // domain. The author hereby disclaims copyright to this source code. // Note - The x86 and x64 versions do _not_ produce the same results, as the // algorithms are optimized for their respective platforms. You can still // compile and run any of them on any platform, but your performance with the // non-native version will be less than optimal. #include "murmur3_hash.h" //----------------------------------------------------------------------------- // Platform-specific functions and macros // Microsoft Visual Studio #if defined(_MSC_VER) #define FORCE_INLINE __forceinline #include #define ROTL32(x,y) _rotl(x,y) #define BIG_CONSTANT(x) (x) // Other compilers #else // defined(_MSC_VER) #define FORCE_INLINE inline __attribute__((always_inline)) static inline uint32_t rotl32 ( uint32_t x, int8_t r ) { return (x << r) | (x >> (32 - r)); } #define ROTL32(x,y) rotl32(x,y) #define BIG_CONSTANT(x) (x##LLU) #endif // !defined(_MSC_VER) //----------------------------------------------------------------------------- // Block read - if your platform needs to do endian-swapping or can only // handle aligned reads, do the conversion here static FORCE_INLINE uint32_t getblock32 ( const uint32_t * p, int i ) { return p[i]; } //----------------------------------------------------------------------------- // Finalization mix - force all bits of a hash block to avalanche static FORCE_INLINE uint32_t fmix32 ( uint32_t h ) { h ^= h >> 16; h *= 0x85ebca6b; h ^= h >> 13; h *= 0xc2b2ae35; h ^= h >> 16; return h; } //----------------------------------------------------------------------------- /* Definition modified slightly from the public domain interface (no seed + * return value */ uint32_t MurmurHash3_x86_32 ( const void * key, size_t length) { const uint8_t * data = (const uint8_t*)key; const int nblocks = length / 4; uint32_t h1 = 0; uint32_t c1 = 0xcc9e2d51; uint32_t c2 = 0x1b873593; //---------- // body const uint32_t * blocks = (const uint32_t *)(data + nblocks*4); for(int i = -nblocks; i; i++) { uint32_t k1 = getblock32(blocks,i); k1 *= c1; k1 = ROTL32(k1,15); k1 *= c2; h1 ^= k1; h1 = ROTL32(h1,13); h1 = h1*5+0xe6546b64; } //---------- // tail const uint8_t * tail = (const uint8_t*)(data + nblocks*4); uint32_t k1 = 0; switch(length & 3) { case 3: k1 ^= tail[2] << 16; case 2: k1 ^= tail[1] << 8; case 1: k1 ^= tail[0]; k1 *= c1; k1 = ROTL32(k1,15); k1 *= c2; h1 ^= k1; }; //---------- // finalization h1 ^= length; h1 = fmix32(h1); //*(uint32_t*)out = h1; return h1; } memcached-1.5.6/aclocal.m40000664000175000017500000012421613245330104012215 00000000000000# generated automatically by aclocal 1.15 -*- Autoconf -*- # Copyright (C) 1996-2014 Free Software Foundation, Inc. # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. m4_ifndef([AC_CONFIG_MACRO_DIRS], [m4_defun([_AM_CONFIG_MACRO_DIRS], [])m4_defun([AC_CONFIG_MACRO_DIRS], [_AM_CONFIG_MACRO_DIRS($@)])]) m4_ifndef([AC_AUTOCONF_VERSION], [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl m4_if(m4_defn([AC_AUTOCONF_VERSION]), [2.69],, [m4_warning([this file was generated for autoconf 2.69. You have another version of autoconf. It may work, but is not guaranteed to. If you have problems, you may need to regenerate the build system entirely. To do so, use the procedure documented by the package, typically 'autoreconf'.])]) # Copyright (C) 2002-2014 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_AUTOMAKE_VERSION(VERSION) # ---------------------------- # Automake X.Y traces this macro to ensure aclocal.m4 has been # generated from the m4 files accompanying Automake X.Y. # (This private macro should not be called outside this file.) AC_DEFUN([AM_AUTOMAKE_VERSION], [am__api_version='1.15' dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to dnl require some minimum version. Point them to the right macro. m4_if([$1], [1.15], [], [AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl ]) # _AM_AUTOCONF_VERSION(VERSION) # ----------------------------- # aclocal traces this macro to find the Autoconf version. # This is a private macro too. Using m4_define simplifies # the logic in aclocal, which can simply ignore this definition. m4_define([_AM_AUTOCONF_VERSION], []) # AM_SET_CURRENT_AUTOMAKE_VERSION # ------------------------------- # Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced. # This function is AC_REQUIREd by AM_INIT_AUTOMAKE. AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION], [AM_AUTOMAKE_VERSION([1.15])dnl m4_ifndef([AC_AUTOCONF_VERSION], [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl _AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))]) # AM_AUX_DIR_EXPAND -*- Autoconf -*- # Copyright (C) 2001-2014 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # For projects using AC_CONFIG_AUX_DIR([foo]), Autoconf sets # $ac_aux_dir to '$srcdir/foo'. In other projects, it is set to # '$srcdir', '$srcdir/..', or '$srcdir/../..'. # # Of course, Automake must honor this variable whenever it calls a # tool from the auxiliary directory. The problem is that $srcdir (and # therefore $ac_aux_dir as well) can be either absolute or relative, # depending on how configure is run. This is pretty annoying, since # it makes $ac_aux_dir quite unusable in subdirectories: in the top # source directory, any form will work fine, but in subdirectories a # relative path needs to be adjusted first. # # $ac_aux_dir/missing # fails when called from a subdirectory if $ac_aux_dir is relative # $top_srcdir/$ac_aux_dir/missing # fails if $ac_aux_dir is absolute, # fails when called from a subdirectory in a VPATH build with # a relative $ac_aux_dir # # The reason of the latter failure is that $top_srcdir and $ac_aux_dir # are both prefixed by $srcdir. In an in-source build this is usually # harmless because $srcdir is '.', but things will broke when you # start a VPATH build or use an absolute $srcdir. # # So we could use something similar to $top_srcdir/$ac_aux_dir/missing, # iff we strip the leading $srcdir from $ac_aux_dir. That would be: # am_aux_dir='\$(top_srcdir)/'`expr "$ac_aux_dir" : "$srcdir//*\(.*\)"` # and then we would define $MISSING as # MISSING="\${SHELL} $am_aux_dir/missing" # This will work as long as MISSING is not called from configure, because # unfortunately $(top_srcdir) has no meaning in configure. # However there are other variables, like CC, which are often used in # configure, and could therefore not use this "fixed" $ac_aux_dir. # # Another solution, used here, is to always expand $ac_aux_dir to an # absolute PATH. The drawback is that using absolute paths prevent a # configured tree to be moved without reconfiguration. AC_DEFUN([AM_AUX_DIR_EXPAND], [AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT])dnl # Expand $ac_aux_dir to an absolute path. am_aux_dir=`cd "$ac_aux_dir" && pwd` ]) # AM_CONDITIONAL -*- Autoconf -*- # Copyright (C) 1997-2014 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_CONDITIONAL(NAME, SHELL-CONDITION) # ------------------------------------- # Define a conditional. AC_DEFUN([AM_CONDITIONAL], [AC_PREREQ([2.52])dnl m4_if([$1], [TRUE], [AC_FATAL([$0: invalid condition: $1])], [$1], [FALSE], [AC_FATAL([$0: invalid condition: $1])])dnl AC_SUBST([$1_TRUE])dnl AC_SUBST([$1_FALSE])dnl _AM_SUBST_NOTMAKE([$1_TRUE])dnl _AM_SUBST_NOTMAKE([$1_FALSE])dnl m4_define([_AM_COND_VALUE_$1], [$2])dnl if $2; then $1_TRUE= $1_FALSE='#' else $1_TRUE='#' $1_FALSE= fi AC_CONFIG_COMMANDS_PRE( [if test -z "${$1_TRUE}" && test -z "${$1_FALSE}"; then AC_MSG_ERROR([[conditional "$1" was never defined. Usually this means the macro was only invoked conditionally.]]) fi])]) # Copyright (C) 1999-2014 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # There are a few dirty hacks below to avoid letting 'AC_PROG_CC' be # written in clear, in which case automake, when reading aclocal.m4, # will think it sees a *use*, and therefore will trigger all it's # C support machinery. Also note that it means that autoscan, seeing # CC etc. in the Makefile, will ask for an AC_PROG_CC use... # _AM_DEPENDENCIES(NAME) # ---------------------- # See how the compiler implements dependency checking. # NAME is "CC", "CXX", "OBJC", "OBJCXX", "UPC", or "GJC". # We try a few techniques and use that to set a single cache variable. # # We don't AC_REQUIRE the corresponding AC_PROG_CC since the latter was # modified to invoke _AM_DEPENDENCIES(CC); we would have a circular # dependency, and given that the user is not expected to run this macro, # just rely on AC_PROG_CC. AC_DEFUN([_AM_DEPENDENCIES], [AC_REQUIRE([AM_SET_DEPDIR])dnl AC_REQUIRE([AM_OUTPUT_DEPENDENCY_COMMANDS])dnl AC_REQUIRE([AM_MAKE_INCLUDE])dnl AC_REQUIRE([AM_DEP_TRACK])dnl m4_if([$1], [CC], [depcc="$CC" am_compiler_list=], [$1], [CXX], [depcc="$CXX" am_compiler_list=], [$1], [OBJC], [depcc="$OBJC" am_compiler_list='gcc3 gcc'], [$1], [OBJCXX], [depcc="$OBJCXX" am_compiler_list='gcc3 gcc'], [$1], [UPC], [depcc="$UPC" am_compiler_list=], [$1], [GCJ], [depcc="$GCJ" am_compiler_list='gcc3 gcc'], [depcc="$$1" am_compiler_list=]) AC_CACHE_CHECK([dependency style of $depcc], [am_cv_$1_dependencies_compiler_type], [if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then # We make a subdir and do the tests there. Otherwise we can end up # making bogus files that we don't know about and never remove. For # instance it was reported that on HP-UX the gcc test will end up # making a dummy file named 'D' -- because '-MD' means "put the output # in D". rm -rf conftest.dir mkdir conftest.dir # Copy depcomp to subdir because otherwise we won't find it if we're # using a relative directory. cp "$am_depcomp" conftest.dir cd conftest.dir # We will build objects and dependencies in a subdirectory because # it helps to detect inapplicable dependency modes. For instance # both Tru64's cc and ICC support -MD to output dependencies as a # side effect of compilation, but ICC will put the dependencies in # the current directory while Tru64 will put them in the object # directory. mkdir sub am_cv_$1_dependencies_compiler_type=none if test "$am_compiler_list" = ""; then am_compiler_list=`sed -n ['s/^#*\([a-zA-Z0-9]*\))$/\1/p'] < ./depcomp` fi am__universal=false m4_case([$1], [CC], [case " $depcc " in #( *\ -arch\ *\ -arch\ *) am__universal=true ;; esac], [CXX], [case " $depcc " in #( *\ -arch\ *\ -arch\ *) am__universal=true ;; esac]) for depmode in $am_compiler_list; do # Setup a source with many dependencies, because some compilers # like to wrap large dependency lists on column 80 (with \), and # we should not choose a depcomp mode which is confused by this. # # We need to recreate these files for each test, as the compiler may # overwrite some of them when testing with obscure command lines. # This happens at least with the AIX C compiler. : > sub/conftest.c for i in 1 2 3 4 5 6; do echo '#include "conftst'$i'.h"' >> sub/conftest.c # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with # Solaris 10 /bin/sh. echo '/* dummy */' > sub/conftst$i.h done echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf # We check with '-c' and '-o' for the sake of the "dashmstdout" # mode. It turns out that the SunPro C++ compiler does not properly # handle '-M -o', and we need to detect this. Also, some Intel # versions had trouble with output in subdirs. am__obj=sub/conftest.${OBJEXT-o} am__minus_obj="-o $am__obj" case $depmode in gcc) # This depmode causes a compiler race in universal mode. test "$am__universal" = false || continue ;; nosideeffect) # After this tag, mechanisms are not by side-effect, so they'll # only be used when explicitly requested. if test "x$enable_dependency_tracking" = xyes; then continue else break fi ;; msvc7 | msvc7msys | msvisualcpp | msvcmsys) # This compiler won't grok '-c -o', but also, the minuso test has # not run yet. These depmodes are late enough in the game, and # so weak that their functioning should not be impacted. am__obj=conftest.${OBJEXT-o} am__minus_obj= ;; none) break ;; esac if depmode=$depmode \ source=sub/conftest.c object=$am__obj \ depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ >/dev/null 2>conftest.err && grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && grep $am__obj sub/conftest.Po > /dev/null 2>&1 && ${MAKE-make} -s -f confmf > /dev/null 2>&1; then # icc doesn't choke on unknown options, it will just issue warnings # or remarks (even with -Werror). So we grep stderr for any message # that says an option was ignored or not supported. # When given -MP, icc 7.0 and 7.1 complain thusly: # icc: Command line warning: ignoring option '-M'; no argument required # The diagnosis changed in icc 8.0: # icc: Command line remark: option '-MP' not supported if (grep 'ignoring option' conftest.err || grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else am_cv_$1_dependencies_compiler_type=$depmode break fi fi done cd .. rm -rf conftest.dir else am_cv_$1_dependencies_compiler_type=none fi ]) AC_SUBST([$1DEPMODE], [depmode=$am_cv_$1_dependencies_compiler_type]) AM_CONDITIONAL([am__fastdep$1], [ test "x$enable_dependency_tracking" != xno \ && test "$am_cv_$1_dependencies_compiler_type" = gcc3]) ]) # AM_SET_DEPDIR # ------------- # Choose a directory name for dependency files. # This macro is AC_REQUIREd in _AM_DEPENDENCIES. AC_DEFUN([AM_SET_DEPDIR], [AC_REQUIRE([AM_SET_LEADING_DOT])dnl AC_SUBST([DEPDIR], ["${am__leading_dot}deps"])dnl ]) # AM_DEP_TRACK # ------------ AC_DEFUN([AM_DEP_TRACK], [AC_ARG_ENABLE([dependency-tracking], [dnl AS_HELP_STRING( [--enable-dependency-tracking], [do not reject slow dependency extractors]) AS_HELP_STRING( [--disable-dependency-tracking], [speeds up one-time build])]) if test "x$enable_dependency_tracking" != xno; then am_depcomp="$ac_aux_dir/depcomp" AMDEPBACKSLASH='\' am__nodep='_no' fi AM_CONDITIONAL([AMDEP], [test "x$enable_dependency_tracking" != xno]) AC_SUBST([AMDEPBACKSLASH])dnl _AM_SUBST_NOTMAKE([AMDEPBACKSLASH])dnl AC_SUBST([am__nodep])dnl _AM_SUBST_NOTMAKE([am__nodep])dnl ]) # Generate code to set up dependency tracking. -*- Autoconf -*- # Copyright (C) 1999-2014 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # _AM_OUTPUT_DEPENDENCY_COMMANDS # ------------------------------ AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS], [{ # Older Autoconf quotes --file arguments for eval, but not when files # are listed without --file. Let's play safe and only enable the eval # if we detect the quoting. case $CONFIG_FILES in *\'*) eval set x "$CONFIG_FILES" ;; *) set x $CONFIG_FILES ;; esac shift for mf do # Strip MF so we end up with the name of the file. mf=`echo "$mf" | sed -e 's/:.*$//'` # Check whether this is an Automake generated Makefile or not. # We used to match only the files named 'Makefile.in', but # some people rename them; so instead we look at the file content. # Grep'ing the first line is not enough: some people post-process # each Makefile.in and add a new line on top of each file to say so. # Grep'ing the whole file is not good either: AIX grep has a line # limit of 2048, but all sed's we know have understand at least 4000. if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/dev/null 2>&1; then dirpart=`AS_DIRNAME("$mf")` else continue fi # Extract the definition of DEPDIR, am__include, and am__quote # from the Makefile without running 'make'. DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"` test -z "$DEPDIR" && continue am__include=`sed -n 's/^am__include = //p' < "$mf"` test -z "$am__include" && continue am__quote=`sed -n 's/^am__quote = //p' < "$mf"` # Find all dependency output files, they are included files with # $(DEPDIR) in their names. We invoke sed twice because it is the # simplest approach to changing $(DEPDIR) to its actual value in the # expansion. for file in `sed -n " s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \ sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g'`; do # Make sure the directory exists. test -f "$dirpart/$file" && continue fdir=`AS_DIRNAME(["$file"])` AS_MKDIR_P([$dirpart/$fdir]) # echo "creating $dirpart/$file" echo '# dummy' > "$dirpart/$file" done done } ])# _AM_OUTPUT_DEPENDENCY_COMMANDS # AM_OUTPUT_DEPENDENCY_COMMANDS # ----------------------------- # This macro should only be invoked once -- use via AC_REQUIRE. # # This code is only required when automatic dependency tracking # is enabled. FIXME. This creates each '.P' file that we will # need in order to bootstrap the dependency handling code. AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS], [AC_CONFIG_COMMANDS([depfiles], [test x"$AMDEP_TRUE" != x"" || _AM_OUTPUT_DEPENDENCY_COMMANDS], [AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir"]) ]) # Do all the work for Automake. -*- Autoconf -*- # Copyright (C) 1996-2014 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This macro actually does too much. Some checks are only needed if # your package does certain things. But this isn't really a big deal. dnl Redefine AC_PROG_CC to automatically invoke _AM_PROG_CC_C_O. m4_define([AC_PROG_CC], m4_defn([AC_PROG_CC]) [_AM_PROG_CC_C_O ]) # AM_INIT_AUTOMAKE(PACKAGE, VERSION, [NO-DEFINE]) # AM_INIT_AUTOMAKE([OPTIONS]) # ----------------------------------------------- # The call with PACKAGE and VERSION arguments is the old style # call (pre autoconf-2.50), which is being phased out. PACKAGE # and VERSION should now be passed to AC_INIT and removed from # the call to AM_INIT_AUTOMAKE. # We support both call styles for the transition. After # the next Automake release, Autoconf can make the AC_INIT # arguments mandatory, and then we can depend on a new Autoconf # release and drop the old call support. AC_DEFUN([AM_INIT_AUTOMAKE], [AC_PREREQ([2.65])dnl dnl Autoconf wants to disallow AM_ names. We explicitly allow dnl the ones we care about. m4_pattern_allow([^AM_[A-Z]+FLAGS$])dnl AC_REQUIRE([AM_SET_CURRENT_AUTOMAKE_VERSION])dnl AC_REQUIRE([AC_PROG_INSTALL])dnl if test "`cd $srcdir && pwd`" != "`pwd`"; then # Use -I$(srcdir) only when $(srcdir) != ., so that make's output # is not polluted with repeated "-I." AC_SUBST([am__isrc], [' -I$(srcdir)'])_AM_SUBST_NOTMAKE([am__isrc])dnl # test to see if srcdir already configured if test -f $srcdir/config.status; then AC_MSG_ERROR([source directory already configured; run "make distclean" there first]) fi fi # test whether we have cygpath if test -z "$CYGPATH_W"; then if (cygpath --version) >/dev/null 2>/dev/null; then CYGPATH_W='cygpath -w' else CYGPATH_W=echo fi fi AC_SUBST([CYGPATH_W]) # Define the identity of the package. dnl Distinguish between old-style and new-style calls. m4_ifval([$2], [AC_DIAGNOSE([obsolete], [$0: two- and three-arguments forms are deprecated.]) m4_ifval([$3], [_AM_SET_OPTION([no-define])])dnl AC_SUBST([PACKAGE], [$1])dnl AC_SUBST([VERSION], [$2])], [_AM_SET_OPTIONS([$1])dnl dnl Diagnose old-style AC_INIT with new-style AM_AUTOMAKE_INIT. m4_if( m4_ifdef([AC_PACKAGE_NAME], [ok]):m4_ifdef([AC_PACKAGE_VERSION], [ok]), [ok:ok],, [m4_fatal([AC_INIT should be called with package and version arguments])])dnl AC_SUBST([PACKAGE], ['AC_PACKAGE_TARNAME'])dnl AC_SUBST([VERSION], ['AC_PACKAGE_VERSION'])])dnl _AM_IF_OPTION([no-define],, [AC_DEFINE_UNQUOTED([PACKAGE], ["$PACKAGE"], [Name of package]) AC_DEFINE_UNQUOTED([VERSION], ["$VERSION"], [Version number of package])])dnl # Some tools Automake needs. AC_REQUIRE([AM_SANITY_CHECK])dnl AC_REQUIRE([AC_ARG_PROGRAM])dnl AM_MISSING_PROG([ACLOCAL], [aclocal-${am__api_version}]) AM_MISSING_PROG([AUTOCONF], [autoconf]) AM_MISSING_PROG([AUTOMAKE], [automake-${am__api_version}]) AM_MISSING_PROG([AUTOHEADER], [autoheader]) AM_MISSING_PROG([MAKEINFO], [makeinfo]) AC_REQUIRE([AM_PROG_INSTALL_SH])dnl AC_REQUIRE([AM_PROG_INSTALL_STRIP])dnl AC_REQUIRE([AC_PROG_MKDIR_P])dnl # For better backward compatibility. To be removed once Automake 1.9.x # dies out for good. For more background, see: # # AC_SUBST([mkdir_p], ['$(MKDIR_P)']) # We need awk for the "check" target (and possibly the TAP driver). The # system "awk" is bad on some platforms. AC_REQUIRE([AC_PROG_AWK])dnl AC_REQUIRE([AC_PROG_MAKE_SET])dnl AC_REQUIRE([AM_SET_LEADING_DOT])dnl _AM_IF_OPTION([tar-ustar], [_AM_PROG_TAR([ustar])], [_AM_IF_OPTION([tar-pax], [_AM_PROG_TAR([pax])], [_AM_PROG_TAR([v7])])]) _AM_IF_OPTION([no-dependencies],, [AC_PROVIDE_IFELSE([AC_PROG_CC], [_AM_DEPENDENCIES([CC])], [m4_define([AC_PROG_CC], m4_defn([AC_PROG_CC])[_AM_DEPENDENCIES([CC])])])dnl AC_PROVIDE_IFELSE([AC_PROG_CXX], [_AM_DEPENDENCIES([CXX])], [m4_define([AC_PROG_CXX], m4_defn([AC_PROG_CXX])[_AM_DEPENDENCIES([CXX])])])dnl AC_PROVIDE_IFELSE([AC_PROG_OBJC], [_AM_DEPENDENCIES([OBJC])], [m4_define([AC_PROG_OBJC], m4_defn([AC_PROG_OBJC])[_AM_DEPENDENCIES([OBJC])])])dnl AC_PROVIDE_IFELSE([AC_PROG_OBJCXX], [_AM_DEPENDENCIES([OBJCXX])], [m4_define([AC_PROG_OBJCXX], m4_defn([AC_PROG_OBJCXX])[_AM_DEPENDENCIES([OBJCXX])])])dnl ]) AC_REQUIRE([AM_SILENT_RULES])dnl dnl The testsuite driver may need to know about EXEEXT, so add the dnl 'am__EXEEXT' conditional if _AM_COMPILER_EXEEXT was seen. This dnl macro is hooked onto _AC_COMPILER_EXEEXT early, see below. AC_CONFIG_COMMANDS_PRE(dnl [m4_provide_if([_AM_COMPILER_EXEEXT], [AM_CONDITIONAL([am__EXEEXT], [test -n "$EXEEXT"])])])dnl # POSIX will say in a future version that running "rm -f" with no argument # is OK; and we want to be able to make that assumption in our Makefile # recipes. So use an aggressive probe to check that the usage we want is # actually supported "in the wild" to an acceptable degree. # See automake bug#10828. # To make any issue more visible, cause the running configure to be aborted # by default if the 'rm' program in use doesn't match our expectations; the # user can still override this though. if rm -f && rm -fr && rm -rf; then : OK; else cat >&2 <<'END' Oops! Your 'rm' program seems unable to run without file operands specified on the command line, even when the '-f' option is present. This is contrary to the behaviour of most rm programs out there, and not conforming with the upcoming POSIX standard: Please tell bug-automake@gnu.org about your system, including the value of your $PATH and any error possibly output before this message. This can help us improve future automake versions. END if test x"$ACCEPT_INFERIOR_RM_PROGRAM" = x"yes"; then echo 'Configuration will proceed anyway, since you have set the' >&2 echo 'ACCEPT_INFERIOR_RM_PROGRAM variable to "yes"' >&2 echo >&2 else cat >&2 <<'END' Aborting the configuration process, to ensure you take notice of the issue. You can download and install GNU coreutils to get an 'rm' implementation that behaves properly: . If you want to complete the configuration process using your problematic 'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM to "yes", and re-run configure. END AC_MSG_ERROR([Your 'rm' program is bad, sorry.]) fi fi dnl The trailing newline in this macro's definition is deliberate, for dnl backward compatibility and to allow trailing 'dnl'-style comments dnl after the AM_INIT_AUTOMAKE invocation. See automake bug#16841. ]) dnl Hook into '_AC_COMPILER_EXEEXT' early to learn its expansion. Do not dnl add the conditional right here, as _AC_COMPILER_EXEEXT may be further dnl mangled by Autoconf and run in a shell conditional statement. m4_define([_AC_COMPILER_EXEEXT], m4_defn([_AC_COMPILER_EXEEXT])[m4_provide([_AM_COMPILER_EXEEXT])]) # When config.status generates a header, we must update the stamp-h file. # This file resides in the same directory as the config header # that is generated. The stamp files are numbered to have different names. # Autoconf calls _AC_AM_CONFIG_HEADER_HOOK (when defined) in the # loop where config.status creates the headers, so we can generate # our stamp files there. AC_DEFUN([_AC_AM_CONFIG_HEADER_HOOK], [# Compute $1's index in $config_headers. _am_arg=$1 _am_stamp_count=1 for _am_header in $config_headers :; do case $_am_header in $_am_arg | $_am_arg:* ) break ;; * ) _am_stamp_count=`expr $_am_stamp_count + 1` ;; esac done echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_count]) # Copyright (C) 2001-2014 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_PROG_INSTALL_SH # ------------------ # Define $install_sh. AC_DEFUN([AM_PROG_INSTALL_SH], [AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl if test x"${install_sh+set}" != xset; then case $am_aux_dir in *\ * | *\ *) install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;; *) install_sh="\${SHELL} $am_aux_dir/install-sh" esac fi AC_SUBST([install_sh])]) # Copyright (C) 2003-2014 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # Check whether the underlying file-system supports filenames # with a leading dot. For instance MS-DOS doesn't. AC_DEFUN([AM_SET_LEADING_DOT], [rm -rf .tst 2>/dev/null mkdir .tst 2>/dev/null if test -d .tst; then am__leading_dot=. else am__leading_dot=_ fi rmdir .tst 2>/dev/null AC_SUBST([am__leading_dot])]) # Check to see how 'make' treats includes. -*- Autoconf -*- # Copyright (C) 2001-2014 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_MAKE_INCLUDE() # ----------------- # Check to see how make treats includes. AC_DEFUN([AM_MAKE_INCLUDE], [am_make=${MAKE-make} cat > confinc << 'END' am__doit: @echo this is the am__doit target .PHONY: am__doit END # If we don't find an include directive, just comment out the code. AC_MSG_CHECKING([for style of include used by $am_make]) am__include="#" am__quote= _am_result=none # First try GNU make style include. echo "include confinc" > confmf # Ignore all kinds of additional output from 'make'. case `$am_make -s -f confmf 2> /dev/null` in #( *the\ am__doit\ target*) am__include=include am__quote= _am_result=GNU ;; esac # Now try BSD make style include. if test "$am__include" = "#"; then echo '.include "confinc"' > confmf case `$am_make -s -f confmf 2> /dev/null` in #( *the\ am__doit\ target*) am__include=.include am__quote="\"" _am_result=BSD ;; esac fi AC_SUBST([am__include]) AC_SUBST([am__quote]) AC_MSG_RESULT([$_am_result]) rm -f confinc confmf ]) # Fake the existence of programs that GNU maintainers use. -*- Autoconf -*- # Copyright (C) 1997-2014 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_MISSING_PROG(NAME, PROGRAM) # ------------------------------ AC_DEFUN([AM_MISSING_PROG], [AC_REQUIRE([AM_MISSING_HAS_RUN]) $1=${$1-"${am_missing_run}$2"} AC_SUBST($1)]) # AM_MISSING_HAS_RUN # ------------------ # Define MISSING if not defined so far and test if it is modern enough. # If it is, set am_missing_run to use it, otherwise, to nothing. AC_DEFUN([AM_MISSING_HAS_RUN], [AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl AC_REQUIRE_AUX_FILE([missing])dnl if test x"${MISSING+set}" != xset; then case $am_aux_dir in *\ * | *\ *) MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;; *) MISSING="\${SHELL} $am_aux_dir/missing" ;; esac fi # Use eval to expand $SHELL if eval "$MISSING --is-lightweight"; then am_missing_run="$MISSING " else am_missing_run= AC_MSG_WARN(['missing' script is too old or missing]) fi ]) # -*- Autoconf -*- # Obsolete and "removed" macros, that must however still report explicit # error messages when used, to smooth transition. # # Copyright (C) 1996-2014 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. AC_DEFUN([AM_CONFIG_HEADER], [AC_DIAGNOSE([obsolete], ['$0': this macro is obsolete. You should use the 'AC][_CONFIG_HEADERS' macro instead.])dnl AC_CONFIG_HEADERS($@)]) AC_DEFUN([AM_PROG_CC_STDC], [AC_PROG_CC am_cv_prog_cc_stdc=$ac_cv_prog_cc_stdc AC_DIAGNOSE([obsolete], ['$0': this macro is obsolete. You should simply use the 'AC][_PROG_CC' macro instead. Also, your code should no longer depend upon 'am_cv_prog_cc_stdc', but upon 'ac_cv_prog_cc_stdc'.])]) AC_DEFUN([AM_C_PROTOTYPES], [AC_FATAL([automatic de-ANSI-fication support has been removed])]) AU_DEFUN([fp_C_PROTOTYPES], [AM_C_PROTOTYPES]) # Helper functions for option handling. -*- Autoconf -*- # Copyright (C) 2001-2014 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # _AM_MANGLE_OPTION(NAME) # ----------------------- AC_DEFUN([_AM_MANGLE_OPTION], [[_AM_OPTION_]m4_bpatsubst($1, [[^a-zA-Z0-9_]], [_])]) # _AM_SET_OPTION(NAME) # -------------------- # Set option NAME. Presently that only means defining a flag for this option. AC_DEFUN([_AM_SET_OPTION], [m4_define(_AM_MANGLE_OPTION([$1]), [1])]) # _AM_SET_OPTIONS(OPTIONS) # ------------------------ # OPTIONS is a space-separated list of Automake options. AC_DEFUN([_AM_SET_OPTIONS], [m4_foreach_w([_AM_Option], [$1], [_AM_SET_OPTION(_AM_Option)])]) # _AM_IF_OPTION(OPTION, IF-SET, [IF-NOT-SET]) # ------------------------------------------- # Execute IF-SET if OPTION is set, IF-NOT-SET otherwise. AC_DEFUN([_AM_IF_OPTION], [m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])]) # Copyright (C) 1999-2014 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # _AM_PROG_CC_C_O # --------------- # Like AC_PROG_CC_C_O, but changed for automake. We rewrite AC_PROG_CC # to automatically call this. AC_DEFUN([_AM_PROG_CC_C_O], [AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl AC_REQUIRE_AUX_FILE([compile])dnl AC_LANG_PUSH([C])dnl AC_CACHE_CHECK( [whether $CC understands -c and -o together], [am_cv_prog_cc_c_o], [AC_LANG_CONFTEST([AC_LANG_PROGRAM([])]) # Make sure it works both with $CC and with simple cc. # Following AC_PROG_CC_C_O, we do the test twice because some # compilers refuse to overwrite an existing .o file with -o, # though they will create one. am_cv_prog_cc_c_o=yes for am_i in 1 2; do if AM_RUN_LOG([$CC -c conftest.$ac_ext -o conftest2.$ac_objext]) \ && test -f conftest2.$ac_objext; then : OK else am_cv_prog_cc_c_o=no break fi done rm -f core conftest* unset am_i]) if test "$am_cv_prog_cc_c_o" != yes; then # Losing compiler, so override with the script. # FIXME: It is wrong to rewrite CC. # But if we don't then we get into trouble of one sort or another. # A longer-term fix would be to have automake use am__CC in this case, # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)" CC="$am_aux_dir/compile $CC" fi AC_LANG_POP([C])]) # For backward compatibility. AC_DEFUN_ONCE([AM_PROG_CC_C_O], [AC_REQUIRE([AC_PROG_CC])]) # Copyright (C) 2001-2014 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_RUN_LOG(COMMAND) # ------------------- # Run COMMAND, save the exit status in ac_status, and log it. # (This has been adapted from Autoconf's _AC_RUN_LOG macro.) AC_DEFUN([AM_RUN_LOG], [{ echo "$as_me:$LINENO: $1" >&AS_MESSAGE_LOG_FD ($1) >&AS_MESSAGE_LOG_FD 2>&AS_MESSAGE_LOG_FD ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD (exit $ac_status); }]) # Check to make sure that the build environment is sane. -*- Autoconf -*- # Copyright (C) 1996-2014 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_SANITY_CHECK # --------------- AC_DEFUN([AM_SANITY_CHECK], [AC_MSG_CHECKING([whether build environment is sane]) # Reject unsafe characters in $srcdir or the absolute working directory # name. Accept space and tab only in the latter. am_lf=' ' case `pwd` in *[[\\\"\#\$\&\'\`$am_lf]]*) AC_MSG_ERROR([unsafe absolute working directory name]);; esac case $srcdir in *[[\\\"\#\$\&\'\`$am_lf\ \ ]]*) AC_MSG_ERROR([unsafe srcdir value: '$srcdir']);; esac # Do 'set' in a subshell so we don't clobber the current shell's # arguments. Must try -L first in case configure is actually a # symlink; some systems play weird games with the mod time of symlinks # (eg FreeBSD returns the mod time of the symlink's containing # directory). if ( am_has_slept=no for am_try in 1 2; do echo "timestamp, slept: $am_has_slept" > conftest.file set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null` if test "$[*]" = "X"; then # -L didn't work. set X `ls -t "$srcdir/configure" conftest.file` fi if test "$[*]" != "X $srcdir/configure conftest.file" \ && test "$[*]" != "X conftest.file $srcdir/configure"; then # If neither matched, then we have a broken ls. This can happen # if, for instance, CONFIG_SHELL is bash and it inherits a # broken ls alias from the environment. This has actually # happened. Such a system could not be considered "sane". AC_MSG_ERROR([ls -t appears to fail. Make sure there is not a broken alias in your environment]) fi if test "$[2]" = conftest.file || test $am_try -eq 2; then break fi # Just in case. sleep 1 am_has_slept=yes done test "$[2]" = conftest.file ) then # Ok. : else AC_MSG_ERROR([newly created file is older than distributed files! Check your system clock]) fi AC_MSG_RESULT([yes]) # If we didn't sleep, we still need to ensure time stamps of config.status and # generated files are strictly newer. am_sleep_pid= if grep 'slept: no' conftest.file >/dev/null 2>&1; then ( sleep 1 ) & am_sleep_pid=$! fi AC_CONFIG_COMMANDS_PRE( [AC_MSG_CHECKING([that generated files are newer than configure]) if test -n "$am_sleep_pid"; then # Hide warnings about reused PIDs. wait $am_sleep_pid 2>/dev/null fi AC_MSG_RESULT([done])]) rm -f conftest.file ]) # Copyright (C) 2009-2014 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_SILENT_RULES([DEFAULT]) # -------------------------- # Enable less verbose build rules; with the default set to DEFAULT # ("yes" being less verbose, "no" or empty being verbose). AC_DEFUN([AM_SILENT_RULES], [AC_ARG_ENABLE([silent-rules], [dnl AS_HELP_STRING( [--enable-silent-rules], [less verbose build output (undo: "make V=1")]) AS_HELP_STRING( [--disable-silent-rules], [verbose build output (undo: "make V=0")])dnl ]) case $enable_silent_rules in @%:@ ((( yes) AM_DEFAULT_VERBOSITY=0;; no) AM_DEFAULT_VERBOSITY=1;; *) AM_DEFAULT_VERBOSITY=m4_if([$1], [yes], [0], [1]);; esac dnl dnl A few 'make' implementations (e.g., NonStop OS and NextStep) dnl do not support nested variable expansions. dnl See automake bug#9928 and bug#10237. am_make=${MAKE-make} AC_CACHE_CHECK([whether $am_make supports nested variables], [am_cv_make_support_nested_variables], [if AS_ECHO([['TRUE=$(BAR$(V)) BAR0=false BAR1=true V=1 am__doit: @$(TRUE) .PHONY: am__doit']]) | $am_make -f - >/dev/null 2>&1; then am_cv_make_support_nested_variables=yes else am_cv_make_support_nested_variables=no fi]) if test $am_cv_make_support_nested_variables = yes; then dnl Using '$V' instead of '$(V)' breaks IRIX make. AM_V='$(V)' AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)' else AM_V=$AM_DEFAULT_VERBOSITY AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY fi AC_SUBST([AM_V])dnl AM_SUBST_NOTMAKE([AM_V])dnl AC_SUBST([AM_DEFAULT_V])dnl AM_SUBST_NOTMAKE([AM_DEFAULT_V])dnl AC_SUBST([AM_DEFAULT_VERBOSITY])dnl AM_BACKSLASH='\' AC_SUBST([AM_BACKSLASH])dnl _AM_SUBST_NOTMAKE([AM_BACKSLASH])dnl ]) # Copyright (C) 2001-2014 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_PROG_INSTALL_STRIP # --------------------- # One issue with vendor 'install' (even GNU) is that you can't # specify the program used to strip binaries. This is especially # annoying in cross-compiling environments, where the build's strip # is unlikely to handle the host's binaries. # Fortunately install-sh will honor a STRIPPROG variable, so we # always use install-sh in "make install-strip", and initialize # STRIPPROG with the value of the STRIP variable (set by the user). AC_DEFUN([AM_PROG_INSTALL_STRIP], [AC_REQUIRE([AM_PROG_INSTALL_SH])dnl # Installed binaries are usually stripped using 'strip' when the user # run "make install-strip". However 'strip' might not be the right # tool to use in cross-compilation environments, therefore Automake # will honor the 'STRIP' environment variable to overrule this program. dnl Don't test for $cross_compiling = yes, because it might be 'maybe'. if test "$cross_compiling" != no; then AC_CHECK_TOOL([STRIP], [strip], :) fi INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s" AC_SUBST([INSTALL_STRIP_PROGRAM])]) # Copyright (C) 2006-2014 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # _AM_SUBST_NOTMAKE(VARIABLE) # --------------------------- # Prevent Automake from outputting VARIABLE = @VARIABLE@ in Makefile.in. # This macro is traced by Automake. AC_DEFUN([_AM_SUBST_NOTMAKE]) # AM_SUBST_NOTMAKE(VARIABLE) # -------------------------- # Public sister of _AM_SUBST_NOTMAKE. AC_DEFUN([AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE($@)]) # Check how to create a tarball. -*- Autoconf -*- # Copyright (C) 2004-2014 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # _AM_PROG_TAR(FORMAT) # -------------------- # Check how to create a tarball in format FORMAT. # FORMAT should be one of 'v7', 'ustar', or 'pax'. # # Substitute a variable $(am__tar) that is a command # writing to stdout a FORMAT-tarball containing the directory # $tardir. # tardir=directory && $(am__tar) > result.tar # # Substitute a variable $(am__untar) that extract such # a tarball read from stdin. # $(am__untar) < result.tar # AC_DEFUN([_AM_PROG_TAR], [# Always define AMTAR for backward compatibility. Yes, it's still used # in the wild :-( We should find a proper way to deprecate it ... AC_SUBST([AMTAR], ['$${TAR-tar}']) # We'll loop over all known methods to create a tar archive until one works. _am_tools='gnutar m4_if([$1], [ustar], [plaintar]) pax cpio none' m4_if([$1], [v7], [am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -'], [m4_case([$1], [ustar], [# The POSIX 1988 'ustar' format is defined with fixed-size fields. # There is notably a 21 bits limit for the UID and the GID. In fact, # the 'pax' utility can hang on bigger UID/GID (see automake bug#8343 # and bug#13588). am_max_uid=2097151 # 2^21 - 1 am_max_gid=$am_max_uid # The $UID and $GID variables are not portable, so we need to resort # to the POSIX-mandated id(1) utility. Errors in the 'id' calls # below are definitely unexpected, so allow the users to see them # (that is, avoid stderr redirection). am_uid=`id -u || echo unknown` am_gid=`id -g || echo unknown` AC_MSG_CHECKING([whether UID '$am_uid' is supported by ustar format]) if test $am_uid -le $am_max_uid; then AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) _am_tools=none fi AC_MSG_CHECKING([whether GID '$am_gid' is supported by ustar format]) if test $am_gid -le $am_max_gid; then AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) _am_tools=none fi], [pax], [], [m4_fatal([Unknown tar format])]) AC_MSG_CHECKING([how to create a $1 tar archive]) # Go ahead even if we have the value already cached. We do so because we # need to set the values for the 'am__tar' and 'am__untar' variables. _am_tools=${am_cv_prog_tar_$1-$_am_tools} for _am_tool in $_am_tools; do case $_am_tool in gnutar) for _am_tar in tar gnutar gtar; do AM_RUN_LOG([$_am_tar --version]) && break done am__tar="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$$tardir"' am__tar_="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$tardir"' am__untar="$_am_tar -xf -" ;; plaintar) # Must skip GNU tar: if it does not support --format= it doesn't create # ustar tarball either. (tar --version) >/dev/null 2>&1 && continue am__tar='tar chf - "$$tardir"' am__tar_='tar chf - "$tardir"' am__untar='tar xf -' ;; pax) am__tar='pax -L -x $1 -w "$$tardir"' am__tar_='pax -L -x $1 -w "$tardir"' am__untar='pax -r' ;; cpio) am__tar='find "$$tardir" -print | cpio -o -H $1 -L' am__tar_='find "$tardir" -print | cpio -o -H $1 -L' am__untar='cpio -i -H $1 -d' ;; none) am__tar=false am__tar_=false am__untar=false ;; esac # If the value was cached, stop now. We just wanted to have am__tar # and am__untar set. test -n "${am_cv_prog_tar_$1}" && break # tar/untar a dummy directory, and stop if the command works. rm -rf conftest.dir mkdir conftest.dir echo GrepMe > conftest.dir/file AM_RUN_LOG([tardir=conftest.dir && eval $am__tar_ >conftest.tar]) rm -rf conftest.dir if test -s conftest.tar; then AM_RUN_LOG([$am__untar /dev/null 2>&1 && break fi done rm -rf conftest.dir AC_CACHE_VAL([am_cv_prog_tar_$1], [am_cv_prog_tar_$1=$_am_tool]) AC_MSG_RESULT([$am_cv_prog_tar_$1])]) AC_SUBST([am__tar]) AC_SUBST([am__untar]) ]) # _AM_PROG_TAR memcached-1.5.6/crawler.c0000664000175000017500000005427213245327530012175 00000000000000/* Copyright 2016 Netflix. * * Use and distribution licensed under the BSD license. See * the LICENSE file for full text. */ /* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ #include "memcached.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define LARGEST_ID POWER_LARGEST typedef struct { void *c; /* original connection structure. still with source thread attached. */ int sfd; /* client fd. */ bipbuf_t *buf; /* output buffer */ char *cbuf; /* current buffer */ } crawler_client_t; typedef struct _crawler_module_t crawler_module_t; typedef void (*crawler_eval_func)(crawler_module_t *cm, item *it, uint32_t hv, int slab_cls); typedef int (*crawler_init_func)(crawler_module_t *cm, void *data); // TODO: init args? typedef void (*crawler_deinit_func)(crawler_module_t *cm); // TODO: extra args? typedef void (*crawler_doneclass_func)(crawler_module_t *cm, int slab_cls); typedef void (*crawler_finalize_func)(crawler_module_t *cm); typedef struct { crawler_init_func init; /* run before crawl starts */ crawler_eval_func eval; /* runs on an item. */ crawler_doneclass_func doneclass; /* runs once per sub-crawler completion. */ crawler_finalize_func finalize; /* runs once when all sub-crawlers are done. */ bool needs_lock; /* whether or not we need the LRU lock held when eval is called */ bool needs_client; /* whether or not to grab onto the remote client */ } crawler_module_reg_t; struct _crawler_module_t { void *data; /* opaque data pointer */ crawler_client_t c; crawler_module_reg_t *mod; }; static int crawler_expired_init(crawler_module_t *cm, void *data); static void crawler_expired_doneclass(crawler_module_t *cm, int slab_cls); static void crawler_expired_finalize(crawler_module_t *cm); static void crawler_expired_eval(crawler_module_t *cm, item *search, uint32_t hv, int i); crawler_module_reg_t crawler_expired_mod = { .init = crawler_expired_init, .eval = crawler_expired_eval, .doneclass = crawler_expired_doneclass, .finalize = crawler_expired_finalize, .needs_lock = true, .needs_client = false }; static void crawler_metadump_eval(crawler_module_t *cm, item *search, uint32_t hv, int i); static void crawler_metadump_finalize(crawler_module_t *cm); crawler_module_reg_t crawler_metadump_mod = { .init = NULL, .eval = crawler_metadump_eval, .doneclass = NULL, .finalize = crawler_metadump_finalize, .needs_lock = false, .needs_client = true }; crawler_module_reg_t *crawler_mod_regs[3] = { &crawler_expired_mod, &crawler_expired_mod, &crawler_metadump_mod }; static int lru_crawler_client_getbuf(crawler_client_t *c); crawler_module_t active_crawler_mod; enum crawler_run_type active_crawler_type; static crawler crawlers[LARGEST_ID]; static int crawler_count = 0; static volatile int do_run_lru_crawler_thread = 0; static int lru_crawler_initialized = 0; static pthread_mutex_t lru_crawler_lock = PTHREAD_MUTEX_INITIALIZER; static pthread_cond_t lru_crawler_cond = PTHREAD_COND_INITIALIZER; #ifdef EXTSTORE /* TODO: pass this around */ static void *storage; #endif /* Will crawl all slab classes a minimum of once per hour */ #define MAX_MAINTCRAWL_WAIT 60 * 60 /*** LRU CRAWLER THREAD ***/ #define LRU_CRAWLER_WRITEBUF 8192 static void lru_crawler_close_client(crawler_client_t *c) { //fprintf(stderr, "CRAWLER: Closing client\n"); sidethread_conn_close(c->c); c->c = NULL; c->cbuf = NULL; bipbuf_free(c->buf); c->buf = NULL; } static void lru_crawler_release_client(crawler_client_t *c) { //fprintf(stderr, "CRAWLER: Closing client\n"); redispatch_conn(c->c); c->c = NULL; c->cbuf = NULL; bipbuf_free(c->buf); c->buf = NULL; } static int crawler_expired_init(crawler_module_t *cm, void *data) { struct crawler_expired_data *d; if (data != NULL) { d = data; d->is_external = true; cm->data = data; } else { // allocate data. d = calloc(1, sizeof(struct crawler_expired_data)); if (d == NULL) { return -1; } // init lock. pthread_mutex_init(&d->lock, NULL); d->is_external = false; d->start_time = current_time; cm->data = d; } pthread_mutex_lock(&d->lock); memset(&d->crawlerstats, 0, sizeof(crawlerstats_t) * POWER_LARGEST); for (int x = 0; x < POWER_LARGEST; x++) { d->crawlerstats[x].start_time = current_time; d->crawlerstats[x].run_complete = false; } pthread_mutex_unlock(&d->lock); return 0; } static void crawler_expired_doneclass(crawler_module_t *cm, int slab_cls) { struct crawler_expired_data *d = (struct crawler_expired_data *) cm->data; pthread_mutex_lock(&d->lock); d->crawlerstats[slab_cls].end_time = current_time; d->crawlerstats[slab_cls].run_complete = true; pthread_mutex_unlock(&d->lock); } static void crawler_expired_finalize(crawler_module_t *cm) { struct crawler_expired_data *d = (struct crawler_expired_data *) cm->data; pthread_mutex_lock(&d->lock); d->end_time = current_time; d->crawl_complete = true; pthread_mutex_unlock(&d->lock); if (!d->is_external) { free(d); } } /* I pulled this out to make the main thread clearer, but it reaches into the * main thread's values too much. Should rethink again. */ static void crawler_expired_eval(crawler_module_t *cm, item *search, uint32_t hv, int i) { struct crawler_expired_data *d = (struct crawler_expired_data *) cm->data; pthread_mutex_lock(&d->lock); crawlerstats_t *s = &d->crawlerstats[i]; int is_flushed = item_is_flushed(search); #ifdef EXTSTORE bool is_valid = true; if (search->it_flags & ITEM_HDR) { item_hdr *hdr = (item_hdr *)ITEM_data(search); if (extstore_check(storage, hdr->page_id, hdr->page_version) != 0) is_valid = false; } #endif if ((search->exptime != 0 && search->exptime < current_time) || is_flushed #ifdef EXTSTORE || !is_valid #endif ) { crawlers[i].reclaimed++; s->reclaimed++; if (settings.verbose > 1) { int ii; char *key = ITEM_key(search); fprintf(stderr, "LRU crawler found an expired item (flags: %d, slab: %d): ", search->it_flags, search->slabs_clsid); for (ii = 0; ii < search->nkey; ++ii) { fprintf(stderr, "%c", key[ii]); } fprintf(stderr, "\n"); } if ((search->it_flags & ITEM_FETCHED) == 0 && !is_flushed) { crawlers[i].unfetched++; } #ifdef EXTSTORE STORAGE_delete(storage, search); #endif do_item_unlink_nolock(search, hv); do_item_remove(search); assert(search->slabs_clsid == 0); } else { s->seen++; refcount_decr(search); if (search->exptime == 0) { s->noexp++; } else if (search->exptime - current_time > 3599) { s->ttl_hourplus++; } else { rel_time_t ttl_remain = search->exptime - current_time; int bucket = ttl_remain / 60; if (bucket <= 60) { s->histo[bucket]++; } } } pthread_mutex_unlock(&d->lock); } static void crawler_metadump_eval(crawler_module_t *cm, item *it, uint32_t hv, int i) { //int slab_id = CLEAR_LRU(i); char keybuf[KEY_MAX_LENGTH * 3 + 1]; int is_flushed = item_is_flushed(it); /* Ignore expired content. */ if ((it->exptime != 0 && it->exptime < current_time) || is_flushed) { refcount_decr(it); return; } // TODO: uriencode directly into the buffer. uriencode(ITEM_key(it), keybuf, it->nkey, KEY_MAX_LENGTH * 3 + 1); int total = snprintf(cm->c.cbuf, 4096, "key=%s exp=%ld la=%llu cas=%llu fetch=%s cls=%u size=%lu\n", keybuf, (it->exptime == 0) ? -1 : (long)(it->exptime + process_started), (unsigned long long)(it->time + process_started), (unsigned long long)ITEM_get_cas(it), (it->it_flags & ITEM_FETCHED) ? "yes" : "no", ITEM_clsid(it), (unsigned long) ITEM_ntotal(it)); refcount_decr(it); // TODO: some way of tracking the errors. these are very unlikely though. if (total >= LRU_CRAWLER_WRITEBUF - 1 || total <= 0) { /* Failed to write, don't push it. */ return; } bipbuf_push(cm->c.buf, total); } static void crawler_metadump_finalize(crawler_module_t *cm) { if (cm->c.c != NULL) { // Ensure space for final message. lru_crawler_client_getbuf(&cm->c); memcpy(cm->c.cbuf, "END\r\n", 5); bipbuf_push(cm->c.buf, 5); } } static int lru_crawler_poll(crawler_client_t *c) { unsigned char *data; unsigned int data_size = 0; struct pollfd to_poll[1]; to_poll[0].fd = c->sfd; to_poll[0].events = POLLOUT; int ret = poll(to_poll, 1, 1000); if (ret < 0) { // fatal. return -1; } if (ret == 0) return 0; if (to_poll[0].revents & POLLIN) { char buf[1]; int res = read(c->sfd, buf, 1); if (res == 0 || (res == -1 && (errno != EAGAIN && errno != EWOULDBLOCK))) { lru_crawler_close_client(c); return -1; } } if ((data = bipbuf_peek_all(c->buf, &data_size)) != NULL) { if (to_poll[0].revents & (POLLHUP|POLLERR)) { lru_crawler_close_client(c); return -1; } else if (to_poll[0].revents & POLLOUT) { int total = write(c->sfd, data, data_size); if (total == -1) { if (errno != EAGAIN && errno != EWOULDBLOCK) { lru_crawler_close_client(c); return -1; } } else if (total == 0) { lru_crawler_close_client(c); return -1; } else { bipbuf_poll(c->buf, total); } } } return 0; } /* Grab some space to work with, if none exists, run the poll() loop and wait * for it to clear up or close. * Return NULL if closed. */ static int lru_crawler_client_getbuf(crawler_client_t *c) { void *buf = NULL; if (c->c == NULL) return -1; /* not enough space. */ while ((buf = bipbuf_request(c->buf, LRU_CRAWLER_WRITEBUF)) == NULL) { // TODO: max loops before closing. int ret = lru_crawler_poll(c); if (ret < 0) return ret; } c->cbuf = buf; return 0; } static void lru_crawler_class_done(int i) { crawlers[i].it_flags = 0; crawler_count--; do_item_unlinktail_q((item *)&crawlers[i]); do_item_stats_add_crawl(i, crawlers[i].reclaimed, crawlers[i].unfetched, crawlers[i].checked); pthread_mutex_unlock(&lru_locks[i]); if (active_crawler_mod.mod->doneclass != NULL) active_crawler_mod.mod->doneclass(&active_crawler_mod, i); } static void *item_crawler_thread(void *arg) { int i; int crawls_persleep = settings.crawls_persleep; pthread_mutex_lock(&lru_crawler_lock); pthread_cond_signal(&lru_crawler_cond); settings.lru_crawler = true; if (settings.verbose > 2) fprintf(stderr, "Starting LRU crawler background thread\n"); while (do_run_lru_crawler_thread) { pthread_cond_wait(&lru_crawler_cond, &lru_crawler_lock); while (crawler_count) { item *search = NULL; void *hold_lock = NULL; for (i = POWER_SMALLEST; i < LARGEST_ID; i++) { if (crawlers[i].it_flags != 1) { continue; } /* Get memory from bipbuf, if client has no space, flush. */ if (active_crawler_mod.c.c != NULL) { int ret = lru_crawler_client_getbuf(&active_crawler_mod.c); if (ret != 0) { lru_crawler_class_done(i); continue; } } else if (active_crawler_mod.mod->needs_client) { lru_crawler_class_done(i); continue; } pthread_mutex_lock(&lru_locks[i]); search = do_item_crawl_q((item *)&crawlers[i]); if (search == NULL || (crawlers[i].remaining && --crawlers[i].remaining < 1)) { if (settings.verbose > 2) fprintf(stderr, "Nothing left to crawl for %d\n", i); lru_crawler_class_done(i); continue; } uint32_t hv = hash(ITEM_key(search), search->nkey); /* Attempt to hash item lock the "search" item. If locked, no * other callers can incr the refcount */ if ((hold_lock = item_trylock(hv)) == NULL) { pthread_mutex_unlock(&lru_locks[i]); continue; } /* Now see if the item is refcount locked */ if (refcount_incr(search) != 2) { refcount_decr(search); if (hold_lock) item_trylock_unlock(hold_lock); pthread_mutex_unlock(&lru_locks[i]); continue; } crawlers[i].checked++; /* Frees the item or decrements the refcount. */ /* Interface for this could improve: do the free/decr here * instead? */ if (!active_crawler_mod.mod->needs_lock) { pthread_mutex_unlock(&lru_locks[i]); } active_crawler_mod.mod->eval(&active_crawler_mod, search, hv, i); if (hold_lock) item_trylock_unlock(hold_lock); if (active_crawler_mod.mod->needs_lock) { pthread_mutex_unlock(&lru_locks[i]); } if (crawls_persleep-- <= 0 && settings.lru_crawler_sleep) { pthread_mutex_unlock(&lru_crawler_lock); usleep(settings.lru_crawler_sleep); pthread_mutex_lock(&lru_crawler_lock); crawls_persleep = settings.crawls_persleep; } else if (!settings.lru_crawler_sleep) { // TODO: only cycle lock every N? pthread_mutex_unlock(&lru_crawler_lock); pthread_mutex_lock(&lru_crawler_lock); } } } if (active_crawler_mod.mod != NULL) { if (active_crawler_mod.mod->finalize != NULL) active_crawler_mod.mod->finalize(&active_crawler_mod); while (active_crawler_mod.c.c != NULL && bipbuf_used(active_crawler_mod.c.buf)) { lru_crawler_poll(&active_crawler_mod.c); } // Double checking in case the client closed during the poll if (active_crawler_mod.c.c != NULL) { lru_crawler_release_client(&active_crawler_mod.c); } active_crawler_mod.mod = NULL; } if (settings.verbose > 2) fprintf(stderr, "LRU crawler thread sleeping\n"); STATS_LOCK(); stats_state.lru_crawler_running = false; STATS_UNLOCK(); } pthread_mutex_unlock(&lru_crawler_lock); if (settings.verbose > 2) fprintf(stderr, "LRU crawler thread stopping\n"); return NULL; } static pthread_t item_crawler_tid; int stop_item_crawler_thread(void) { int ret; pthread_mutex_lock(&lru_crawler_lock); do_run_lru_crawler_thread = 0; pthread_cond_signal(&lru_crawler_cond); pthread_mutex_unlock(&lru_crawler_lock); if ((ret = pthread_join(item_crawler_tid, NULL)) != 0) { fprintf(stderr, "Failed to stop LRU crawler thread: %s\n", strerror(ret)); return -1; } settings.lru_crawler = false; return 0; } /* Lock dance to "block" until thread is waiting on its condition: * caller locks mtx. caller spawns thread. * thread blocks on mutex. * caller waits on condition, releases lock. * thread gets lock, sends signal. * caller can't wait, as thread has lock. * thread waits on condition, releases lock * caller wakes on condition, gets lock. * caller immediately releases lock. * thread is now safely waiting on condition before the caller returns. */ int start_item_crawler_thread(void) { int ret; if (settings.lru_crawler) return -1; pthread_mutex_lock(&lru_crawler_lock); do_run_lru_crawler_thread = 1; if ((ret = pthread_create(&item_crawler_tid, NULL, item_crawler_thread, NULL)) != 0) { fprintf(stderr, "Can't create LRU crawler thread: %s\n", strerror(ret)); pthread_mutex_unlock(&lru_crawler_lock); return -1; } /* Avoid returning until the crawler has actually started */ pthread_cond_wait(&lru_crawler_cond, &lru_crawler_lock); pthread_mutex_unlock(&lru_crawler_lock); return 0; } /* 'remaining' is passed in so the LRU maintainer thread can scrub the whole * LRU every time. */ static int do_lru_crawler_start(uint32_t id, uint32_t remaining) { uint32_t sid = id; int starts = 0; pthread_mutex_lock(&lru_locks[sid]); if (crawlers[sid].it_flags == 0) { if (settings.verbose > 2) fprintf(stderr, "Kicking LRU crawler off for LRU %u\n", sid); crawlers[sid].nbytes = 0; crawlers[sid].nkey = 0; crawlers[sid].it_flags = 1; /* For a crawler, this means enabled. */ crawlers[sid].next = 0; crawlers[sid].prev = 0; crawlers[sid].time = 0; if (remaining == LRU_CRAWLER_CAP_REMAINING) { remaining = do_get_lru_size(sid); } crawlers[sid].remaining = remaining; crawlers[sid].slabs_clsid = sid; crawlers[sid].reclaimed = 0; crawlers[sid].unfetched = 0; crawlers[sid].checked = 0; do_item_linktail_q((item *)&crawlers[sid]); crawler_count++; starts++; } pthread_mutex_unlock(&lru_locks[sid]); if (starts) { STATS_LOCK(); stats_state.lru_crawler_running = true; stats.lru_crawler_starts++; STATS_UNLOCK(); } return starts; } static int lru_crawler_set_client(crawler_module_t *cm, void *c, const int sfd) { crawler_client_t *crawlc = &cm->c; if (crawlc->c != NULL) { return -1; } crawlc->c = c; crawlc->sfd = sfd; crawlc->buf = bipbuf_new(1024 * 128); if (crawlc->buf == NULL) { return -2; } return 0; } int lru_crawler_start(uint8_t *ids, uint32_t remaining, const enum crawler_run_type type, void *data, void *c, const int sfd) { int starts = 0; bool is_running; static rel_time_t block_ae_until = 0; pthread_mutex_lock(&lru_crawler_lock); STATS_LOCK(); is_running = stats_state.lru_crawler_running; STATS_UNLOCK(); if (is_running && !(type == CRAWLER_AUTOEXPIRE && active_crawler_type == CRAWLER_AUTOEXPIRE)) { pthread_mutex_unlock(&lru_crawler_lock); block_ae_until = current_time + 60; return -1; } if (type == CRAWLER_AUTOEXPIRE && block_ae_until > current_time) { pthread_mutex_unlock(&lru_crawler_lock); return -1; } /* Configure the module */ if (!is_running) { assert(crawler_mod_regs[type] != NULL); active_crawler_mod.mod = crawler_mod_regs[type]; active_crawler_type = type; if (active_crawler_mod.mod->init != NULL) { active_crawler_mod.mod->init(&active_crawler_mod, data); } if (active_crawler_mod.mod->needs_client) { if (c == NULL || sfd == 0) { pthread_mutex_unlock(&lru_crawler_lock); return -2; } if (lru_crawler_set_client(&active_crawler_mod, c, sfd) != 0) { pthread_mutex_unlock(&lru_crawler_lock); return -2; } } } /* we allow the autocrawler to restart sub-LRU's before completion */ for (int sid = POWER_SMALLEST; sid < POWER_LARGEST; sid++) { if (ids[sid]) starts += do_lru_crawler_start(sid, remaining); } if (starts) { pthread_cond_signal(&lru_crawler_cond); } pthread_mutex_unlock(&lru_crawler_lock); return starts; } /* * Also only clear the crawlerstats once per sid. */ enum crawler_result_type lru_crawler_crawl(char *slabs, const enum crawler_run_type type, void *c, const int sfd, unsigned int remaining) { char *b = NULL; uint32_t sid = 0; int starts = 0; uint8_t tocrawl[POWER_LARGEST]; /* FIXME: I added this while debugging. Don't think it's needed? */ memset(tocrawl, 0, sizeof(uint8_t) * POWER_LARGEST); if (strcmp(slabs, "all") == 0) { for (sid = 0; sid < POWER_LARGEST; sid++) { tocrawl[sid] = 1; } } else { for (char *p = strtok_r(slabs, ",", &b); p != NULL; p = strtok_r(NULL, ",", &b)) { if (!safe_strtoul(p, &sid) || sid < POWER_SMALLEST || sid >= MAX_NUMBER_OF_SLAB_CLASSES) { pthread_mutex_unlock(&lru_crawler_lock); return CRAWLER_BADCLASS; } tocrawl[sid | TEMP_LRU] = 1; tocrawl[sid | HOT_LRU] = 1; tocrawl[sid | WARM_LRU] = 1; tocrawl[sid | COLD_LRU] = 1; } } starts = lru_crawler_start(tocrawl, remaining, type, NULL, c, sfd); if (starts == -1) { return CRAWLER_RUNNING; } else if (starts == -2) { return CRAWLER_ERROR; /* FIXME: not very helpful. */ } else if (starts) { return CRAWLER_OK; } else { return CRAWLER_NOTSTARTED; } } /* If we hold this lock, crawler can't wake up or move */ void lru_crawler_pause(void) { pthread_mutex_lock(&lru_crawler_lock); } void lru_crawler_resume(void) { pthread_mutex_unlock(&lru_crawler_lock); } int init_lru_crawler(void *arg) { if (lru_crawler_initialized == 0) { #ifdef EXTSTORE storage = arg; #endif if (pthread_cond_init(&lru_crawler_cond, NULL) != 0) { fprintf(stderr, "Can't initialize lru crawler condition\n"); return -1; } pthread_mutex_init(&lru_crawler_lock, NULL); active_crawler_mod.c.c = NULL; active_crawler_mod.mod = NULL; active_crawler_mod.data = NULL; lru_crawler_initialized = 1; } return 0; } memcached-1.5.6/murmur3_hash.h0000664000175000017500000000125113025643161013142 00000000000000//----------------------------------------------------------------------------- // MurmurHash3 was written by Austin Appleby, and is placed in the public // domain. The author hereby disclaims copyright to this source code. #ifndef MURMURHASH3_H #define MURMURHASH3_H //----------------------------------------------------------------------------- // Platform-specific functions and macros #include #include //----------------------------------------------------------------------------- uint32_t MurmurHash3_x86_32(const void *key, size_t length); //----------------------------------------------------------------------------- #endif // MURMURHASH3_H memcached-1.5.6/t/0000755000175000017500000000000013245327530010701 500000000000000memcached-1.5.6/t/flush-all.t0000775000175000017500000000577213115057711012712 00000000000000#!/usr/bin/perl use strict; use Test::More tests => 26; use FindBin qw($Bin); use lib "$Bin/lib"; use MemcachedTest; my $server = new_memcached(); my $sock = $server->sock; my $expire; print $sock "set foo 0 0 6\r\nfooval\r\n"; is(scalar <$sock>, "STORED\r\n", "stored foo"); mem_get_is($sock, "foo", "fooval"); print $sock "flush_all\r\n"; is(scalar <$sock>, "OK\r\n", "did flush_all"); mem_get_is($sock, "foo", undef); # Test flush_all with zero delay. print $sock "set foo 0 0 6\r\nfooval\r\n"; is(scalar <$sock>, "STORED\r\n", "stored foo"); mem_get_is($sock, "foo", "fooval"); print $sock "flush_all 0\r\n"; is(scalar <$sock>, "OK\r\n", "did flush_all"); mem_get_is($sock, "foo", undef); # check that flush_all doesn't blow away items that immediately get set print $sock "set foo 0 0 3\r\nnew\r\n"; is(scalar <$sock>, "STORED\r\n", "stored foo = 'new'"); mem_get_is($sock, "foo", 'new'); # and the other form, specifying a flush_all time... my $expire = time() + 2; print $sock "flush_all $expire\r\n"; is(scalar <$sock>, "OK\r\n", "did flush_all in future"); print $sock "set foo 0 0 4\r\n1234\r\n"; is(scalar <$sock>, "STORED\r\n", "stored foo = '1234'"); mem_get_is($sock, "foo", '1234'); sleep(3); mem_get_is($sock, "foo", undef); print $sock "set foo 0 0 5\r\n12345\r\n"; is(scalar <$sock>, "STORED\r\n", "stored foo = '12345'"); mem_get_is($sock, "foo", '12345'); print $sock "flush_all 86400\r\n"; is(scalar <$sock>, "OK\r\n", "did flush_all for far future"); # Check foo still exists. mem_get_is($sock, "foo", '12345'); print $sock "set foo2 0 0 5\r\n54321\r\n"; is(scalar <$sock>, "STORED\r\n", "stored foo2 = '54321'"); mem_get_is($sock, "foo", '12345'); mem_get_is($sock, "foo2", '54321'); # Test -F option which disables flush_all $server = new_memcached('-F'); $sock = $server->sock; print $sock "set foo 0 0 7\r\nfooval2\r\n"; is(scalar <$sock>, "STORED\r\n", "stored foo"); mem_get_is($sock, "foo", "fooval2"); print $sock "flush_all\r\n"; is(scalar <$sock>, "CLIENT_ERROR flush_all not allowed\r\n", "flush_all was not allowed"); mem_get_is($sock, "foo", "fooval2"); # Test that disabling CAS makes flush_all less accurate. # Due to lock ordering issues we can no longer evict items newer than # oldest_live, so we rely on the CAS counter for an exact cliff. So disabling # CAS now means all items set in the same second will fail to set. $server = new_memcached('-C'); $sock = $server->sock; my $failed_nocas = 0; # Running this 100,000 times failed the test a handful of times. 50 tries # should be enough. for (1..50) { print $sock "flush_all 0\r\n"; my $foo = scalar <$sock>; print $sock "set foo 0 0 3\r\nnew\r\n"; $foo = scalar <$sock>; print $sock "get foo\r\n"; my $line = scalar <$sock>; if ($line =~ /^VALUE/) { $line = scalar <$sock>; $line = scalar <$sock>; print STDERR "Succeeded!!!\n"; next; } elsif ($line =~ /^END/) { $failed_nocas++; last; } } is($failed_nocas, 1, "failed to set value after flush with no CAS at least once"); memcached-1.5.6/t/issue_50.t0000644000175000017500000000060512416643766012456 00000000000000#!/usr/bin/perl use strict; use Test::More tests => 1; use FindBin qw($Bin); use lib "$Bin/lib"; use MemcachedTest; my $server = new_memcached('-B binary'); my $sock = $server->sock; $SIG{ALRM} = sub { die "alarm\n" }; alarm(2); print $sock "Here's a bunch of garbage that doesn't look like the bin prot."; my $rv = <$sock>; ok(1, "Either the above worked and quit, or hung forever."); memcached-1.5.6/t/extstore-buckets.t0000664000175000017500000000341313240770206014321 00000000000000#!/usr/bin/perl use strict; use warnings; use Test::More; use FindBin qw($Bin); use lib "$Bin/lib"; use MemcachedTest; use Data::Dumper qw/Dumper/; my $ext_path; if (!supports_extstore()) { plan skip_all => 'extstore not enabled'; exit 0; } $ext_path = "/tmp/extstore.$$"; my $server = new_memcached("-m 256 -U 0 -o ext_page_size=8,ext_page_count=8,ext_wbuf_size=2,ext_threads=1,ext_io_depth=2,ext_item_size=512,ext_item_age=2,ext_recache_rate=10000,ext_max_frag=0,ext_path=$ext_path,ext_low_ttl=60,slab_automove=1"); my $sock = $server->sock; my $value; { my @chars = ("C".."Z"); for (1 .. 20000) { $value .= $chars[rand @chars]; } } # fill some larger objects { # interleave sets with 0 ttl vs long ttl's. my $keycount = 1200; for (1 .. $keycount) { print $sock "set nfoo$_ 0 0 20000 noreply\r\n$value\r\n"; print $sock "set lfoo$_ 0 5 20000 noreply\r\n$value\r\n"; } # wait for a flush sleep 10; print $sock "lru_crawler crawl all\r\n"; <$sock>; sleep 2; # fetch mem_get_is($sock, "nfoo1", $value); # check extstore counters my $stats = mem_stats($sock); cmp_ok($stats->{extstore_page_allocs}, '>', 0, 'at least one page allocated'); cmp_ok($stats->{extstore_objects_written}, '>', $keycount / 2, 'some objects written'); cmp_ok($stats->{extstore_bytes_written}, '>', length($value) * 2, 'some bytes written'); cmp_ok($stats->{get_extstore}, '>', 0, 'one object was fetched'); cmp_ok($stats->{extstore_objects_read}, '>', 0, 'one object read'); cmp_ok($stats->{extstore_bytes_read}, '>', length($value), 'some bytes read'); cmp_ok($stats->{extstore_page_reclaims}, '>', 1, 'at least two pages reclaimed'); } done_testing(); END { unlink $ext_path if $ext_path; } memcached-1.5.6/t/unixsocket.t0000755000175000017500000000076712416643766013232 00000000000000#!/usr/bin/perl use strict; use Test::More tests => 3; use FindBin qw($Bin); use lib "$Bin/lib"; use MemcachedTest; my $filename = "/tmp/memcachetest$$"; my $server = new_memcached("-s $filename"); my $sock = $server->sock; ok(-S $filename, "creating unix domain socket $filename"); # set foo (and should get it) print $sock "set foo 0 0 6\r\nfooval\r\n"; is(scalar <$sock>, "STORED\r\n", "stored foo"); mem_get_is($sock, "foo", "fooval"); unlink($filename); ## Just some basic stuff for now... memcached-1.5.6/t/bogus-commands.t0000755000175000017500000000041712416643766013744 00000000000000#!/usr/bin/perl use strict; use Test::More tests => 1; use FindBin qw($Bin); use lib "$Bin/lib"; use MemcachedTest; my $server = new_memcached(); my $sock = $server->sock; print $sock "boguscommand slkdsldkfjsd\r\n"; is(scalar <$sock>, "ERROR\r\n", "got error back"); memcached-1.5.6/t/slabs-reassign-chunked.t0000664000175000017500000000766313150607001015345 00000000000000#!/usr/bin/perl use strict; use warnings; use Test::More tests => 8; use FindBin qw($Bin); use lib "$Bin/lib"; use MemcachedTest; my $server = new_memcached('-m 60 -o slab_reassign,slab_automove,lru_crawler,lru_maintainer,slab_chunk_max=4096'); my $sock = $server->sock; sub dump_stats { my $s = shift; my $filter = shift || ''; for my $k (sort keys %$s) { if ($filter) { next unless $k =~ m/$filter/; } print STDERR "STAT: $k = ", $s->{$k}, "\n"; } } my $value; { my @chars = ("C".."Z"); for (1 .. 11000) { $value .= $chars[rand @chars]; } } my $keycount = 5100; my $res; for (1 .. $keycount) { # print STDERR "HI $_\n"; print $sock "set nfoo$_ 0 0 11000 noreply\r\n$value\r\n"; # print $sock "set nfoo$_ 0 0 11000\r\n$value\r\n"; # my $res = scalar <$sock>; # print STDERR "RES: $res\n"; } my $todelete = 0; { my $stats = mem_stats($sock); cmp_ok($stats->{curr_items}, '>', 4000, "stored at least 4000 11k items"); $todelete = $stats->{curr_items} / 2; # for ('evictions', 'reclaimed', 'curr_items', 'cmd_set', 'bytes') { # print STDERR "$_: ", $stats->{$_}, "\n"; # } } { my $s = mem_stats($sock, 'slabs'); my $sid; # Find the highest ID to source from. for my $k (keys %$s) { next unless $k =~ m/^(\d+):/; $sid = $s->{$k} if $s->{$k} > $1; } for (my $x = 0; $x < 3; $x++) { print $sock "slabs reassign 17 0\r\n"; my $res = scalar <$sock>; chomp $res; # print STDERR "SLABS REASSIGN RESULT: $res\n"; sleep 1; } } # Make room in old class so rescues can happen when we switch slab classes. #for (1 .. $todelete) { # print $sock "delete nfoo$_ noreply\r\n"; #} # Give LRU mover some time to reclaim slab chunks. #sleep 1; { my $stats = mem_stats($sock); cmp_ok($stats->{slab_global_page_pool}, '>', 0, 'global page pool > 0'); cmp_ok($stats->{slab_reassign_chunk_rescues}, '>', 0, 'some chunk rescues happened'); } { my $hits = 0; for (1 .. $keycount) { print $sock "get nfoo$_\r\n"; my $body = scalar(<$sock>); my $expected = "VALUE nfoo$_ 0 11000\r\n$value\r\nEND\r\n"; if ($body =~ /^END/) { next; } else { $body .= scalar(<$sock>) . scalar(<$sock>); if ($body ne $expected) { die "Something terrible has happened: $expected\nBODY:\n$body\nDONETEST\n"; } $hits++; } } cmp_ok($hits, '>', 0, "fetched back $hits values after reassignment"); } $value = "A"x3000; for (1 .. $keycount) { print $sock "set ifoo$_ 0 0 3000 noreply\r\n$value\r\n"; } my $missing = 0; my $hits = 0; for (1 .. $keycount) { print $sock "get ifoo$_\r\n"; my $body = scalar(<$sock>); my $expected = "VALUE ifoo$_ 0 3000\r\n$value\r\nEND\r\n"; if ($body =~ /^END/) { $missing++; } else { $body .= scalar(<$sock>) . scalar(<$sock>); if ($body ne $expected) { print STDERR "Something terrible has happened: $expected\nBODY:\n$body\nDONETEST\n"; } else { $hits++; } } } #print STDERR "HITS: $hits, MISSES: $missing\n"; { my $stats = mem_stats($sock); cmp_ok($stats->{evictions}, '<', 2000, 'evictions were less than 2000'); # for ('evictions', 'reclaimed', 'curr_items', 'cmd_set', 'bytes') { # print STDERR "$_: ", $stats->{$_}, "\n"; # } } cmp_ok($hits, '>', 4000, 'were able to fetch back 2/3rds of 8k keys'); my $stats_done = mem_stats($sock); cmp_ok($stats_done->{slab_reassign_chunk_rescues}, '>', 0, 'some reassign chunk rescues happened'); # Reassign rescues won't happen here because the headers are of a different # size and we aren't moving pages out of that slab class #cmp_ok($stats_done->{slab_reassign_rescues}, '>', 0, 'some reassign rescues happened'); cmp_ok($stats_done->{slab_reassign_evictions_nomem}, '>', 0, 'some reassign evictions happened'); memcached-1.5.6/t/lru-maintainer.t0000664000175000017500000000640213160272015013732 00000000000000#!/usr/bin/perl use strict; use warnings; use Test::More tests => 226; use FindBin qw($Bin); use lib "$Bin/lib"; use MemcachedTest; # Regression test for underestimating the size of items after the large memory # change. my $server = new_memcached('-m 3 -o lru_maintainer,lru_crawler -l 127.0.0.1'); my $sock = $server->sock; my $keystub = "X"x200; for (1 .. 15000) { print $sock "set $keystub$_ 0 0 2 noreply\r\nok\r\n"; } # There's probably already an error on the wire, so we'll see that. $keystub .= "20001"; print $sock "set $keystub 0 0 2\r\nok\r\n"; is(scalar <$sock>, "STORED\r\n", "stored key without OOM"); # Basic tests $server = new_memcached('-m 6 -o lru_maintainer,lru_crawler -l 127.0.0.1'); $sock = $server->sock; for (1 .. 10) { print $sock "set ifoo$_ 0 1 2\r\nok\r\n"; is(scalar <$sock>, "STORED\r\n", "stored key"); } sleep 3; { my $stats = mem_stats($sock); is($stats->{reclaimed}, 10, "expired key automatically reclaimed"); } my $value = "B"x66560; print $sock "set canary 0 0 66560\r\n$value\r\n"; is(scalar <$sock>, "STORED\r\n", "stored canary key"); # Now flush the slab class with junk. for (my $key = 0; $key < 100; $key++) { if ($key == 30) { my $stats; for (0..2) { # Give the juggler some time to move. some platforms suffer at # this more than others (solaris amd64?) $stats = mem_stats($sock, "items"); if ($stats->{"items:31:moves_to_cold"} == 0) { sleep 1; next; } last; } isnt($stats->{"items:31:moves_to_cold"}, 0, "moved some items to cold"); # Items need two fetches to become active mem_get_is($sock, "canary", $value); mem_get_is($sock, "canary", $value); } print $sock "set key$key 0 0 66560\r\n$value\r\n"; is(scalar <$sock>, "STORED\r\n", "stored key$key"); } { my $stats = mem_stats($sock); isnt($stats->{evictions}, 0, "some evictions happened"); my $istats = mem_stats($sock, "items"); isnt($istats->{"items:31:number_warm"}, 0, "our canary moved to warm"); use Data::Dumper qw/Dumper/; } # Key should've been saved to the WARM_LRU, and still exists. mem_get_is($sock, "canary", $value); # Test TEMP_LRU $server = new_memcached('-m 2 -o lru_maintainer,lru_crawler,temporary_ttl=61'); $sock = $server->sock; { my $stats = mem_stats($sock, "settings"); is($stats->{temp_lru}, "yes"); } print $sock "set canary 0 30 66560\r\n$value\r\n"; is(scalar <$sock>, "STORED\r\n", "stored temporary canary key"); { my $stats = mem_stats($sock, "items"); is($stats->{"items:31:number_hot"}, 0, "item did not go into hot LRU"); } # *Not* fetching the key, and flushing the slab class with junk. # Using keys with higher TTL's here. for (my $key = 0; $key < 100; $key++) { print $sock "set key$key 0 3600 66560\r\n$value\r\n"; is(scalar <$sock>, "STORED\r\n", "stored key$key"); } { my $stats = mem_stats($sock, "items"); isnt($stats->{"items:31:evictions"}, 0, "some evictions happened"); isnt($stats->{"items:31:number_hot"}, 0, "high exptime items went into hot LRU"); is($stats->{"items:31:number_temp"}, 1, "still one item in temporary LRU"); } # Canary should still exist, even unfetched, because it's protected by # temp LRU mem_get_is($sock, "canary", $value); memcached-1.5.6/t/maxconns.t0000755000175000017500000000111212416643766012645 00000000000000#!/usr/bin/perl # NOTE: This test never worked. Memcached would ignore maxconns requests lower # than the current ulimit. Test needs to be updated. use strict; use warnings; use Test::More tests => 11; use FindBin qw($Bin); use lib "$Bin/lib"; use MemcachedTest; # start up a server with 10 maximum connections my $server = new_memcached('-c 100'); my $sock = $server->sock; my @sockets; ok(defined($sock), 'Connection 0'); push (@sockets, $sock); foreach my $conn (1..10) { $sock = $server->new_sock; ok(defined($sock), "Made connection $conn"); push(@sockets, $sock); } memcached-1.5.6/t/issue_104.t0000755000175000017500000000114512416643766012541 00000000000000#!/usr/bin/perl use strict; use Test::More tests => 6; use FindBin qw($Bin); use lib "$Bin/lib"; use MemcachedTest; my $server = new_memcached(); my $sock = $server->sock; # first get should miss print $sock "get foo\r\n"; is(scalar <$sock>, "END\r\n", "get foo"); # Now set and get (should hit) print $sock "set foo 0 0 6\r\nfooval\r\n"; is(scalar <$sock>, "STORED\r\n", "stored foo"); mem_get_is($sock, "foo", "fooval"); my $stats = mem_stats($sock); is($stats->{cmd_get}, 2, "Should have 2 get requests"); is($stats->{get_hits}, 1, "Should have 1 hit"); is($stats->{get_misses}, 1, "Should have 1 miss"); memcached-1.5.6/t/issue_22.t0000644000175000017500000000216612416643766012461 00000000000000#!/usr/bin/perl use strict; use Test::More tests => 84; use FindBin qw($Bin); use lib "$Bin/lib"; use MemcachedTest; my $server = new_memcached("-m 3"); my $sock = $server->sock; my $value = "B"x66560; my $key = 0; for ($key = 0; $key < 40; $key++) { print $sock "set key$key 0 0 66560\r\n$value\r\n"; is (scalar <$sock>, "STORED\r\n", "stored key$key"); } my $first_stats = mem_stats($sock, "items"); my $first_evicted = $first_stats->{"items:31:evicted"}; # I get 1 eviction on a 32 bit binary, but 4 on a 64 binary.. # Just check that I have evictions... isnt ($first_evicted, "0", "check evicted"); print $sock "stats reset\r\n"; is (scalar <$sock>, "RESET\r\n", "Stats reset"); my $second_stats = mem_stats($sock, "items"); my $second_evicted = $second_stats->{"items:31:evicted"}; is ($second_evicted, "0", "check evicted"); for ($key = 40; $key < 80; $key++) { print $sock "set key$key 0 0 66560\r\n$value\r\n"; is (scalar <$sock>, "STORED\r\n", "stored key$key"); } my $last_stats = mem_stats($sock, "items"); my $last_evicted = $last_stats->{"items:31:evicted"}; is ($last_evicted, "40", "check evicted"); memcached-1.5.6/t/expirations.t0000775000175000017500000000332313160272015013352 00000000000000#!/usr/bin/perl use strict; use Test::More tests => 15; use FindBin qw($Bin); use lib "$Bin/lib"; use MemcachedTest; my $server = new_memcached(); my $sock = $server->sock; my $expire; sub wait_for_early_second { my $have_hires = eval "use Time::HiRes (); 1"; if ($have_hires) { my $tsh = Time::HiRes::time(); my $ts = int($tsh); return if ($tsh - $ts) < 0.5; } my $ts = int(time()); while (1) { my $t = int(time()); return if $t != $ts; select undef, undef, undef, 0.10; # 1/10th of a second sleeps until time changes. } } wait_for_early_second(); print $sock "set foo 0 3 6\r\nfooval\r\n"; is(scalar <$sock>, "STORED\r\n", "stored foo"); mem_get_is($sock, "foo", "fooval"); sleep(4); mem_get_is($sock, "foo", undef); $expire = time() - 1; print $sock "set foo 0 $expire 6\r\nfooval\r\n"; is(scalar <$sock>, "STORED\r\n", "stored foo"); mem_get_is($sock, "foo", undef, "already expired"); $expire = time() + 1; print $sock "set foo 0 $expire 6\r\nfoov+1\r\n"; is(scalar <$sock>, "STORED\r\n", "stored foo"); mem_get_is($sock, "foo", "foov+1"); sleep(2.2); mem_get_is($sock, "foo", undef, "now expired"); $expire = time() - 20; print $sock "set boo 0 $expire 6\r\nbooval\r\n"; is(scalar <$sock>, "STORED\r\n", "stored boo"); mem_get_is($sock, "boo", undef, "now expired"); print $sock "add add 0 2 6\r\naddval\r\n"; is(scalar <$sock>, "STORED\r\n", "stored add"); mem_get_is($sock, "add", "addval"); # second add fails print $sock "add add 0 2 7\r\naddval2\r\n"; is(scalar <$sock>, "NOT_STORED\r\n", "add failure"); sleep(2.3); print $sock "add add 0 2 7\r\naddval3\r\n"; is(scalar <$sock>, "STORED\r\n", "stored add again"); mem_get_is($sock, "add", "addval3"); memcached-1.5.6/t/getandtouch.t0000664000175000017500000000251313240770206013313 00000000000000#!/usr/bin/perl use strict; use warnings; use Test::More tests => 15; use FindBin qw($Bin); use lib "$Bin/lib"; use MemcachedTest; my $server = new_memcached(); my $sock = $server->sock; # cache miss print $sock "gat 10 foo1\r\n"; is(scalar <$sock>, "END\r\n", "cache miss"); # set foo1 and foo2 (and should get it) print $sock "set foo1 0 2 7\r\nfooval1\r\n"; is(scalar <$sock>, "STORED\r\n", "stored foo"); print $sock "set foo2 0 2 7\r\nfooval2\r\n"; is(scalar <$sock>, "STORED\r\n", "stored foo2"); # get and touch it with cas print $sock "gats 10 foo1 foo2\r\n"; ok(scalar <$sock> =~ /VALUE foo1 0 7 (\d+)\r\n/, "get and touch foo1 with cas regexp success"); is(scalar <$sock>, "fooval1\r\n","value"); ok(scalar <$sock> =~ /VALUE foo2 0 7 (\d+)\r\n/, "get and touch foo2 with cas regexp success"); is(scalar <$sock>, "fooval2\r\n","value"); is(scalar <$sock>, "END\r\n", "end"); # get and touch it without cas print $sock "gat 10 foo1 foo2\r\n"; ok(scalar <$sock> =~ /VALUE foo1 0 7\r\n/, "get and touch foo1 without cas regexp success"); is(scalar <$sock>, "fooval1\r\n","value"); ok(scalar <$sock> =~ /VALUE foo2 0 7\r\n/, "get and touch foo2 without cas regexp success"); is(scalar <$sock>, "fooval2\r\n","value"); is(scalar <$sock>, "END\r\n", "end"); sleep 2; mem_get_is($sock, "foo1", "fooval1"); mem_get_is($sock, "foo2", "fooval2"); memcached-1.5.6/t/issue_163.t0000664000175000017500000000205413150607001012517 00000000000000#!/usr/bin/perl use strict; use Test::More tests => 7; use FindBin qw($Bin); use lib "$Bin/lib"; use MemcachedTest; my $server = new_memcached(); my $sock = $server->sock; my $value1 = "A"x66560; my $value2 = "B"x66570; print $sock "set key 0 1 66560\r\n$value1\r\n"; is (scalar <$sock>, "STORED\r\n", "stored key"); my $stats = mem_stats($sock, "slabs"); my $requested = $stats->{"31:mem_requested"}; isnt ($requested, "0", "We should have requested some memory"); sleep(3); print $sock "set key 0 0 66570\r\n$value2\r\n"; is (scalar <$sock>, "STORED\r\n", "stored key"); my $stats = mem_stats($sock, "items"); my $reclaimed = $stats->{"items:31:reclaimed"}; is ($reclaimed, "1", "Objects should be reclaimed"); print $sock "delete key\r\n"; is (scalar <$sock>, "DELETED\r\n", "deleted key"); print $sock "set key 0 0 66560\r\n$value1\r\n"; is (scalar <$sock>, "STORED\r\n", "stored key"); my $stats = mem_stats($sock, "slabs"); my $requested2 = $stats->{"31:mem_requested"}; is ($requested2, $requested, "we've not allocated and freed the same amount"); memcached-1.5.6/t/slabs-reassign2.t0000664000175000017500000000747413150607001014010 00000000000000#!/usr/bin/perl use strict; use warnings; use Test::More tests => 12; use FindBin qw($Bin); use lib "$Bin/lib"; use MemcachedTest; use Data::Dumper qw/Dumper/; my $server = new_memcached('-m 60 -o slab_reassign,slab_automove,lru_crawler,lru_maintainer,slab_automove_window=3'); my $sock = $server->sock; my $value = "B"x11000; my $keycount = 5000; my $res; for (1 .. $keycount) { print $sock "set nfoo$_ 0 0 11000 noreply\r\n$value\r\n"; } my $todelete = 0; { my $stats = mem_stats($sock); cmp_ok($stats->{curr_items}, '>', 4000, "stored at least 4000 11k items"); $todelete = $stats->{curr_items}; # for ('evictions', 'reclaimed', 'curr_items', 'cmd_set', 'bytes') { # print STDERR "$_: ", $stats->{$_}, "\n"; # } } # Make room in old class so rescues can happen when we switch slab classes. for (1 .. $todelete) { next unless $_ % 2 == 0; print $sock "delete nfoo$_ noreply\r\n"; } { my $tries; for ($tries = 20; $tries > 0; $tries--) { sleep 1; my $stats = mem_stats($sock); if ($stats->{slab_global_page_pool} > 24) { last; } } cmp_ok($tries, '>', 0, 'some pages moved back to global pool'); } $value = "B"x7000; for (1 .. $keycount) { print $sock "set ifoo$_ 0 0 7000 noreply\r\n$value\r\n"; } my $missing = 0; my $hits = 0; for (1 .. $keycount) { print $sock "get ifoo$_\r\n"; my $body = scalar(<$sock>); my $expected = "VALUE ifoo$_ 0 7000\r\n$value\r\nEND\r\n"; if ($body =~ /^END/) { $missing++; } else { $body .= scalar(<$sock>) . scalar(<$sock>); if ($body ne $expected) { print STDERR "Something terrible has happened: $expected\nBODY:\n$body\nDONETEST\n"; } else { $hits++; } } } #print STDERR "HITS: $hits, MISSES: $missing\n"; { my $stats = mem_stats($sock); cmp_ok($stats->{evictions}, '<', 2000, 'evictions were less than 2000'); # for ('evictions', 'reclaimed', 'curr_items', 'cmd_set', 'bytes') { # print STDERR "$_: ", $stats->{$_}, "\n"; # } } # Force reassign evictions by moving too much memory manually. { my $s = mem_stats($sock, 'slabs'); my $max_pages = 0; my $scls = 0; for my $k (keys %$s) { next unless $k =~ m/^(\d+)\:total_pages/; if ($s->{$k} > $max_pages) { $max_pages = $s->{$k}; $scls = $1; } } my $tries; for ($tries = 10; $tries > 0; $tries--) { print $sock "slabs reassign $scls 1\r\n"; my $res = <$sock>; sleep 1; my $s = mem_stats($sock); last if $s->{slab_reassign_evictions_nomem} > 0; } cmp_ok($tries, '>', 0, 'some reassign evictions happened'); } cmp_ok($hits, '>', 2000, 'were able to fetch back some of the small keys'); my $stats_done = mem_stats($sock); cmp_ok($stats_done->{slab_reassign_rescues}, '>', 0, 'some reassign rescues happened'); print $sock "flush_all\r\n"; is(scalar <$sock>, "OK\r\n", "did flush_all"); my $tries; for ($tries = 20; $tries > 0; $tries--) { sleep 1; my $stats = mem_stats($sock); if ($stats->{slab_global_page_pool} > 50) { last; } } cmp_ok($tries, '>', 0, 'reclaimed at least 50 pages before timeout'); { my $stats = mem_stats($sock, "slabs"); is($stats->{total_malloced}, 62914560, "total_malloced is what we expect"); } # Set into an entirely new class. Overload a bit to try to cause problems. $value = "B"x4096; for (1 .. $keycount * 4) { print $sock "set jfoo$_ 0 0 4096 noreply\r\n$value\r\n"; } { my $stats = mem_stats($sock); cmp_ok($stats->{curr_items}, '>', 10000, "stored at least 10000 4k items"); is($stats->{slab_global_page_pool}, 0, "drained the global page pool"); } { my $stats = mem_stats($sock, "slabs"); is($stats->{total_malloced}, 62914560, "total_malloced is same after re-assignment"); } memcached-1.5.6/t/issue_61.t0000644000175000017500000000141512416643766012460 00000000000000#!/usr/bin/perl use strict; use Test::More tests => 7; use FindBin qw($Bin); use lib "$Bin/lib"; use MemcachedTest; my $server = new_memcached("-R 1"); my $sock = $server->sock; print $sock "set foobar 0 0 5\r\nBubba\r\nset foobar 0 0 5\r\nBubba\r\nset foobar 0 0 5\r\nBubba\r\nset foobar 0 0 5\r\nBubba\r\nset foobar 0 0 5\r\nBubba\r\nset foobar 0 0 5\r\nBubba\r\n"; is (scalar <$sock>, "STORED\r\n", "stored foobar"); is (scalar <$sock>, "STORED\r\n", "stored foobar"); is (scalar <$sock>, "STORED\r\n", "stored foobar"); is (scalar <$sock>, "STORED\r\n", "stored foobar"); is (scalar <$sock>, "STORED\r\n", "stored foobar"); is (scalar <$sock>, "STORED\r\n", "stored foobar"); my $stats = mem_stats($sock); is ($stats->{"conn_yields"}, "5", "Got a decent number of yields"); memcached-1.5.6/t/extstore.t0000664000175000017500000001152013240770206012661 00000000000000#!/usr/bin/perl use strict; use warnings; use Test::More; use FindBin qw($Bin); use lib "$Bin/lib"; use MemcachedTest; use Data::Dumper qw/Dumper/; my $ext_path; if (!supports_extstore()) { plan skip_all => 'extstore not enabled'; exit 0; } $ext_path = "/tmp/extstore.$$"; my $server = new_memcached("-m 64 -U 0 -o ext_page_size=8,ext_page_count=8,ext_wbuf_size=2,ext_threads=1,ext_io_depth=2,ext_item_size=512,ext_item_age=2,ext_recache_rate=10000,ext_max_frag=0.9,ext_path=$ext_path,slab_automove=0"); my $sock = $server->sock; my $value; { my @chars = ("C".."Z"); for (1 .. 20000) { $value .= $chars[rand @chars]; } } # fill a small object print $sock "set foo 0 0 2\r\nhi\r\n"; is(scalar <$sock>, "STORED\r\n", "stored small value"); # fetch mem_get_is($sock, "foo", "hi"); # check extstore counters { my $stats = mem_stats($sock); is($stats->{extstore_objects_written}, 0); } # fill some larger objects { # set one canary value for later print $sock "set canary 0 0 20000 noreply\r\n$value\r\n"; my $keycount = 1000; for (1 .. $keycount) { print $sock "set nfoo$_ 0 0 20000 noreply\r\n$value\r\n"; } # wait for a flush sleep 4; # fetch # TODO: Fetch back all values mem_get_is($sock, "nfoo1", $value); # check extstore counters my $stats = mem_stats($sock); cmp_ok($stats->{extstore_page_allocs}, '>', 0, 'at least one page allocated'); cmp_ok($stats->{extstore_objects_written}, '>', $keycount / 2, 'some objects written'); cmp_ok($stats->{extstore_bytes_written}, '>', length($value) * 2, 'some bytes written'); cmp_ok($stats->{get_extstore}, '>', 0, 'one object was fetched'); cmp_ok($stats->{extstore_objects_read}, '>', 0, 'one object read'); cmp_ok($stats->{extstore_bytes_read}, '>', length($value), 'some bytes read'); # Remove half of the keys for the next test. for (1 .. $keycount) { next unless $_ % 2 == 0; print $sock "delete nfoo$_ noreply\r\n"; } my $stats2 = mem_stats($sock); cmp_ok($stats->{extstore_bytes_used}, '>', $stats2->{extstore_bytes_used}, 'bytes used dropped after deletions'); cmp_ok($stats->{extstore_objects_used}, '>', $stats2->{extstore_objects_used}, 'objects used dropped after deletions'); is($stats2->{badcrc_from_extstore}, 0, 'CRC checks successful'); is($stats2->{miss_from_extstore}, 0, 'no misses'); # delete the rest for (1 .. $keycount) { next unless $_ % 2 == 1; print $sock "delete nfoo$_ noreply\r\n"; } } # fill to eviction { my $keycount = 3000; for (1 .. $keycount) { print $sock "set mfoo$_ 0 0 20000 noreply\r\n$value\r\n"; } sleep 4; my $stats = mem_stats($sock); is($stats->{miss_from_extstore}, 0, 'no misses'); mem_get_is($sock, "canary", undef); # check counters $stats = mem_stats($sock); cmp_ok($stats->{extstore_page_evictions}, '>', 0, 'at least one page evicted'); cmp_ok($stats->{extstore_objects_evicted}, '>', 0, 'at least one object evicted'); cmp_ok($stats->{extstore_bytes_evicted}, '>', 0, 'some bytes evicted'); is($stats->{extstore_pages_free}, 0, '0 pages are free'); is($stats->{miss_from_extstore}, 1, 'exactly one miss'); # refresh some keys so rescues happen while drop_unread == 1. for (1 .. $keycount / 2) { next unless $_ % 2 == 1; # Need to be fetched twice in order to bump print $sock "touch mfoo$_ 0 noreply\r\n"; print $sock "touch mfoo$_ 0 noreply\r\n"; } print $sock "extstore drop_unread 1\r\n"; my $res = <$sock>; print $sock "extstore max_frag 0\r\n"; $res = <$sock>; for (1 .. $keycount) { next unless $_ % 2 == 0; print $sock "delete mfoo$_ noreply\r\n"; } sleep 4; $stats = mem_stats($sock); cmp_ok($stats->{extstore_pages_free}, '>', 0, 'some pages now free'); cmp_ok($stats->{extstore_compact_rescues}, '>', 0, 'some compaction rescues happened'); cmp_ok($stats->{extstore_compact_skipped}, '>', 0, 'some compaction skips happened'); print $sock "extstore drop_unread 0\r\n"; $res = <$sock>; } # attempt to incr/decr/append/prepend or chunk objects that were sent to disk. { my $keycount = 100; for (1 .. $keycount) { print $sock "set bfoo$_ 0 0 20000 noreply\r\n$value\r\n"; } sleep 4; # incr should be blocked. print $sock "incr bfoo1 1\r\n"; is(scalar <$sock>, "CLIENT_ERROR cannot increment or decrement non-numeric value\r\n", 'incr fails'); # append/prepend *could* work, but it would require pulling the item back in. print $sock "append bfoo1 0 0 2\r\nhi\r\n"; is(scalar <$sock>, "NOT_STORED\r\n", 'append falis'); print $sock "prepend bfoo1 0 0 2\r\nhi\r\n"; is(scalar <$sock>, "NOT_STORED\r\n", 'prepend fails'); } done_testing(); END { unlink $ext_path if $ext_path; } memcached-1.5.6/t/evictions.t0000644000175000017500000000155112777363632013027 00000000000000#!/usr/bin/perl # Test the 'stats items' evictions counters. use strict; use Test::More tests => 92; use FindBin qw($Bin); use lib "$Bin/lib"; use MemcachedTest; my $server = new_memcached("-m 3"); my $sock = $server->sock; my $value = "B"x66560; my $key = 0; # These aren't set to expire. for ($key = 0; $key < 40; $key++) { print $sock "set key$key 0 0 66560\r\n$value\r\n"; is(scalar <$sock>, "STORED\r\n", "stored key$key"); } # These ones would expire in 600 seconds. for ($key = 0; $key < 50; $key++) { print $sock "set key$key 0 600 66560\r\n$value\r\n"; is(scalar <$sock>, "STORED\r\n", "stored key$key"); } my $stats = mem_stats($sock, "items"); my $evicted = $stats->{"items:31:evicted"}; isnt($evicted, "0", "check evicted"); my $evicted_nonzero = $stats->{"items:31:evicted_nonzero"}; isnt($evicted_nonzero, "0", "check evicted_nonzero"); memcached-1.5.6/t/whitespace.t0000775000175000017500000000254113160272015013142 00000000000000#!/usr/bin/perl use strict; use FindBin qw($Bin); our @files; BEGIN { chdir "$Bin/.." or die; my @exempted = qw(Makefile.am ChangeLog doc/Makefile.am README README.md compile_commands.json); push(@exempted, glob("doc/*.xml")); push(@exempted, glob("doc/*.full")); push(@exempted, glob("doc/xml2rfc/*.xsl")); push(@exempted, glob("m4/*backport*m4")); push(@exempted, glob("*.orig")); push(@exempted, glob(".*.swp")); my %exempted_hash = map { $_ => 1 } @exempted; my @stuff = split /\0/, `git ls-files -z -c -m -o --exclude-standard`; @files = grep { ! $exempted_hash{$_} } @stuff; # We won't find any files if git isn't installed. If git isn't # installed, they're probably not doing any useful development, or # at the very least am will clean up whitespace when we receive # their patch. unless (@files) { use Test::More; plan skip_all => "Skipping tests probably because you don't have git."; exit 0; } } use Test::More tests => scalar(@files); foreach my $f (@files) { open(my $fh, $f) or die "Cannot open file $f: $!"; my $before = do { local $/; <$fh>; }; close ($fh); my $after = $before; $after =~ s/\t/ /g; $after =~ s/ +$//mg; $after .= "\n" unless $after =~ /\n$/; ok ($after eq $before, "$f (see devtools/clean-whitespace.pl)"); } memcached-1.5.6/t/incrdecr.t0000755000175000017500000000415512416643766012622 00000000000000#!/usr/bin/perl use strict; use Test::More tests => 23; use FindBin qw($Bin); use lib "$Bin/lib"; use MemcachedTest; my $server = new_memcached(); my $sock = $server->sock; # Bug 21 print $sock "set bug21 0 0 19\r\n9223372036854775807\r\n"; is(scalar <$sock>, "STORED\r\n", "stored text"); print $sock "incr bug21 1\r\n"; is(scalar <$sock>, "9223372036854775808\r\n", "bug21 incr 1"); print $sock "incr bug21 1\r\n"; is(scalar <$sock>, "9223372036854775809\r\n", "bug21 incr 2"); print $sock "decr bug21 1\r\n"; is(scalar <$sock>, "9223372036854775808\r\n", "bug21 decr"); print $sock "set num 0 0 1\r\n1\r\n"; is(scalar <$sock>, "STORED\r\n", "stored num"); mem_get_is($sock, "num", 1, "stored 1"); print $sock "incr num 1\r\n"; is(scalar <$sock>, "2\r\n", "+ 1 = 2"); mem_get_is($sock, "num", 2); print $sock "incr num 8\r\n"; is(scalar <$sock>, "10\r\n", "+ 8 = 10"); mem_get_is($sock, "num", 10); print $sock "decr num 1\r\n"; is(scalar <$sock>, "9\r\n", "- 1 = 9"); print $sock "decr num 9\r\n"; is(scalar <$sock>, "0\r\n", "- 9 = 0"); print $sock "decr num 5\r\n"; is(scalar <$sock>, "0\r\n", "- 5 = 0"); printf $sock "set num 0 0 10\r\n4294967296\r\n"; is(scalar <$sock>, "STORED\r\n", "stored 2**32"); print $sock "incr num 1\r\n"; is(scalar <$sock>, "4294967297\r\n", "4294967296 + 1 = 4294967297"); printf $sock "set num 0 0 %d\r\n18446744073709551615\r\n", length("18446744073709551615"); is(scalar <$sock>, "STORED\r\n", "stored 2**64-1"); print $sock "incr num 1\r\n"; is(scalar <$sock>, "0\r\n", "(2**64 - 1) + 1 = 0"); print $sock "decr bogus 5\r\n"; is(scalar <$sock>, "NOT_FOUND\r\n", "can't decr bogus key"); print $sock "decr incr 5\r\n"; is(scalar <$sock>, "NOT_FOUND\r\n", "can't incr bogus key"); print $sock "set bigincr 0 0 1\r\n0\r\n"; is(scalar <$sock>, "STORED\r\n", "stored bigincr"); print $sock "incr bigincr 18446744073709551610\r\n"; is(scalar <$sock>, "18446744073709551610\r\n"); print $sock "set text 0 0 2\r\nhi\r\n"; is(scalar <$sock>, "STORED\r\n", "stored hi"); print $sock "incr text 1\r\n"; is(scalar <$sock>, "CLIENT_ERROR cannot increment or decrement non-numeric value\r\n", "hi - 1 = 0"); memcached-1.5.6/t/inline_asciihdr.t0000664000175000017500000000154013150607001014121 00000000000000#!/usr/bin/perl # Ensure get and gets can mirror flags + CAS properly when not inlining the # ascii response header. use strict; use Test::More tests => 17; use FindBin qw($Bin); use lib "$Bin/lib"; use MemcachedTest; my $server = new_memcached('-o no_inline_ascii_resp'); my $sock = $server->sock; # 0 flags and size print $sock "set foo 0 0 0\r\n\r\n"; is(scalar <$sock>, "STORED\r\n", "stored"); mem_get_is($sock, "foo", ""); for my $flags (0, 123, 2**16-1, 2**31, 2**32-1) { print $sock "set foo $flags 0 6\r\nfooval\r\n"; is(scalar <$sock>, "STORED\r\n", "stored foo"); mem_get_is({ sock => $sock, flags => $flags }, "foo", "fooval", "got flags $flags back"); my @res = mem_gets($sock, "foo"); mem_gets_is({ sock => $sock, flags => $flags }, $res[0], "foo", "fooval", "got flags $flags back"); } memcached-1.5.6/t/binary-sasl.t0000775000175000017500000004172613025643161013246 00000000000000#!/usr/bin/perl use strict; use warnings; use Cwd; use FindBin qw($Bin); use lib "$Bin/lib"; use MemcachedTest; my $supports_sasl = supports_sasl(); use Test::More; if (supports_sasl()) { if ($ENV{'RUN_SASL_TESTS'}) { plan tests => 33; } else { plan skip_all => 'Skipping SASL tests'; exit 0; } } else { plan tests => 1; eval { my $server = new_memcached("-S"); }; ok($@, "Died with illegal -S args when SASL is not supported."); exit 0; } eval { my $server = new_memcached("-S -B auto"); }; ok($@, "SASL shouldn't be used with protocol auto negotiate"); eval { my $server = new_memcached("-S -B ascii"); }; ok($@, "SASL isn't implemented in the ascii protocol"); eval { my $server = new_memcached("-S -B binary -B ascii"); }; ok($@, "SASL isn't implemented in the ascii protocol"); # Based almost 100% off testClient.py which is: # Copyright (c) 2007 Dustin Sallings # Command constants use constant CMD_GET => 0x00; use constant CMD_SET => 0x01; use constant CMD_ADD => 0x02; use constant CMD_REPLACE => 0x03; use constant CMD_DELETE => 0x04; use constant CMD_INCR => 0x05; use constant CMD_DECR => 0x06; use constant CMD_QUIT => 0x07; use constant CMD_FLUSH => 0x08; use constant CMD_GETQ => 0x09; use constant CMD_NOOP => 0x0A; use constant CMD_VERSION => 0x0B; use constant CMD_GETK => 0x0C; use constant CMD_GETKQ => 0x0D; use constant CMD_APPEND => 0x0E; use constant CMD_PREPEND => 0x0F; use constant CMD_STAT => 0x10; use constant CMD_SETQ => 0x11; use constant CMD_ADDQ => 0x12; use constant CMD_REPLACEQ => 0x13; use constant CMD_DELETEQ => 0x14; use constant CMD_INCREMENTQ => 0x15; use constant CMD_DECREMENTQ => 0x16; use constant CMD_QUITQ => 0x17; use constant CMD_FLUSHQ => 0x18; use constant CMD_APPENDQ => 0x19; use constant CMD_PREPENDQ => 0x1A; use constant CMD_SASL_LIST_MECHS => 0x20; use constant CMD_SASL_AUTH => 0x21; use constant CMD_SASL_STEP => 0x22; use constant ERR_AUTH_ERROR => 0x20; # REQ and RES formats are divided even though they currently share # the same format, since they _could_ differ in the future. use constant REQ_PKT_FMT => "CCnCCnNNNN"; use constant RES_PKT_FMT => "CCnCCnNNNN"; use constant INCRDECR_PKT_FMT => "NNNNN"; use constant MIN_RECV_BYTES => length(pack(RES_PKT_FMT)); use constant REQ_MAGIC => 0x80; use constant RES_MAGIC => 0x81; my $pwd=getcwd; $ENV{'SASL_CONF_PATH'} = "$pwd/t/sasl"; my $server = new_memcached('-B binary -S '); my $mc = MC::Client->new; my $check = sub { my ($key, $orig_val) = @_; my ($status, $val, $cas) = $mc->get($key); if ($val =~ /^\d+$/) { cmp_ok($val,'==', $orig_val, "$val = $orig_val"); } else { cmp_ok($val, 'eq', $orig_val, "$val = $orig_val"); } }; my $set = sub { my ($key, $orig_value, $exp) = @_; $exp = defined $exp ? $exp : 0; my ($status, $rv)= $mc->set($key, $orig_value, $exp); $check->($key, $orig_value); }; my $empty = sub { my $key = shift; my ($status,$rv) =()= eval { $mc->get($key) }; #if ($status == ERR_AUTH_ERROR) { # ok($@->auth_error, "Not authorized to connect"); #} #else { # ok($@->not_found, "We got a not found error when we expected one"); #} if ($status) { ok($@->not_found, "We got a not found error when we expected one"); } }; my $delete = sub { my ($key, $when) = @_; $mc->delete($key, $when); $empty->($key); }; # BEGIN THE TEST ok($server, "started the server"); my $v = $mc->version; ok(defined $v && length($v), "Proper version: $v"); # list mechs my $mechs= $mc->list_mechs(); Test::More::cmp_ok($mechs, 'eq', 'CRAM-MD5 PLAIN', "list_mechs $mechs"); # this should fail, not authenticated { my ($status, $val)= $mc->set('x', "somevalue"); ok($status, "this fails to authenticate"); cmp_ok($status,'==',ERR_AUTH_ERROR, "error code matches"); } $empty->('x'); { my $mc = MC::Client->new; my ($status, $val) = $mc->delete('x'); ok($status, "this fails to authenticate"); cmp_ok($status,'==',ERR_AUTH_ERROR, "error code matches"); } $empty->('x'); { my $mc = MC::Client->new; my ($status, $val)= $mc->set('x', "somevalue"); ok($status, "this fails to authenticate"); cmp_ok($status,'==',ERR_AUTH_ERROR, "error code matches"); } $empty->('x'); { my $mc = MC::Client->new; my ($status, $val)= $mc->flush('x'); ok($status, "this fails to authenticate"); cmp_ok($status,'==',ERR_AUTH_ERROR, "error code matches"); } $empty->('x'); # Build the auth DB for testing. my $sasldb = '/tmp/test-memcached.sasldb'; unlink $sasldb; my $saslpasswd_path; for my $dir (split(/:/, $ENV{PATH}), "/usr/bin", "/usr/sbin", "/usr/local/bin", "/usr/local/sbin", ) { my $exe = $dir . '/saslpasswd2'; if (-x $exe) { $saslpasswd_path = $exe; last; } } system("echo testpass | $saslpasswd_path -a memcached -c -p testuser"); $mc = MC::Client->new; # Attempt a bad auth mech. is ($mc->authenticate('testuser', 'testpass', "X" x 40), 0x4, "bad mech"); # Attempt bad authentication. is ($mc->authenticate('testuser', 'wrongpassword'), 0x20, "bad auth"); # Now try good authentication and make the tests work. is ($mc->authenticate('testuser', 'testpass'), 0, "authenticated"); # these should work { my ($status, $val)= $mc->set('x', "somevalue"); ok(! $status); } $check->('x','somevalue'); { my ($status, $val)= $mc->delete('x'); ok(! $status); } $empty->('x'); { my ($status, $val)= $mc->set('x', "somevalue"); ok(! $status); } $check->('x','somevalue'); { my ($status, $val)= $mc->flush('x'); ok(! $status); } $empty->('x'); { my $mc = MC::Client->new; # Attempt bad authentication. is ($mc->authenticate('testuser', 'wrongpassword'), 0x20, "bad auth"); # This should fail because $mc is not authenticated my ($status, $val)= $mc->set('x', "somevalue"); ok($status, "this fails to authenticate"); cmp_ok($status,'==',ERR_AUTH_ERROR, "error code matches"); } $empty->('x', 'somevalue'); { my $mc = MC::Client->new; # Attempt bad authentication. is ($mc->authenticate('testuser', 'wrongpassword'), 0x20, "bad auth"); # Mix an authenticated connection and an unauthenticated connection to # confirm c->authenticated is not shared among connections my $mc2 = MC::Client->new; is ($mc2->authenticate('testuser', 'testpass'), 0, "authenticated"); my ($status, $val)= $mc2->set('x', "somevalue"); ok(! $status); # This should fail because $mc is not authenticated ($status, $val)= $mc->set('x', "somevalue"); ok($status, "this fails to authenticate"); cmp_ok($status,'==',ERR_AUTH_ERROR, "error code matches"); } # check the SASL stats, make sure they track things correctly # note: the enabled or not is presence checked in stats.t # while authenticated, get current counter # # My initial approach was going to be to get current counts, reauthenticate # and fail, followed by a reauth successfully so I'd know what happened. # Reauthentication is currently unsupported, so it doesn't work that way at the # moment. Adding tests may break this. { my %stats = $mc->stats(''); is ($stats{'auth_cmds'}, 5, "auth commands counted"); is ($stats{'auth_errors'}, 3, "auth errors correct"); } # Along with the assertion added to the code to verify we're staying # within bounds when we do a stats detail dump (detail turned on at # the top). # my %stats = $mc->stats('detail dump'); # ###################################################################### # Test ends around here. # ###################################################################### package MC::Client; use strict; use warnings; use fields qw(socket); use IO::Socket::INET; use constant ERR_AUTH_ERROR => 0x20; sub new { my $self = shift; my ($s) = @_; $s = $server unless defined $s; my $sock = $s->sock; $self = fields::new($self); $self->{socket} = $sock; return $self; } sub authenticate { my ($self, $user, $pass, $mech)= @_; $mech ||= 'PLAIN'; my $buf = sprintf("%c%s%c%s", 0, $user, 0, $pass); my ($status, $rv, undef) = $self->_do_command(::CMD_SASL_AUTH, $mech, $buf, ''); return $status; } sub list_mechs { my ($self)= @_; my ($status, $rv, undef) = $self->_do_command(::CMD_SASL_LIST_MECHS, '', '', ''); return join(" ", sort(split(/\s+/, $rv))); } sub build_command { my $self = shift; die "Not enough args to send_command" unless @_ >= 4; my ($cmd, $key, $val, $opaque, $extra_header, $cas) = @_; $extra_header = '' unless defined $extra_header; my $keylen = length($key); my $vallen = length($val); my $extralen = length($extra_header); my $datatype = 0; # field for future use my $reserved = 0; # field for future use my $totallen = $keylen + $vallen + $extralen; my $ident_hi = 0; my $ident_lo = 0; if ($cas) { $ident_hi = int($cas / 2 ** 32); $ident_lo = int($cas % 2 ** 32); } my $msg = pack(::REQ_PKT_FMT, ::REQ_MAGIC, $cmd, $keylen, $extralen, $datatype, $reserved, $totallen, $opaque, $ident_hi, $ident_lo); my $full_msg = $msg . $extra_header . $key . $val; return $full_msg; } sub send_command { my $self = shift; die "Not enough args to send_command" unless @_ >= 4; my ($cmd, $key, $val, $opaque, $extra_header, $cas) = @_; my $full_msg = $self->build_command($cmd, $key, $val, $opaque, $extra_header, $cas); my $sent = $self->{socket}->send($full_msg); die("Send failed: $!") unless $sent; if($sent != length($full_msg)) { die("only sent $sent of " . length($full_msg) . " bytes"); } } sub flush_socket { my $self = shift; $self->{socket}->flush; } # Send a silent command and ensure it doesn't respond. sub send_silent { my $self = shift; die "Not enough args to send_silent" unless @_ >= 4; my ($cmd, $key, $val, $opaque, $extra_header, $cas) = @_; $self->send_command($cmd, $key, $val, $opaque, $extra_header, $cas); $self->send_command(::CMD_NOOP, '', '', $opaque + 1); my ($ropaque, $status, $data) = $self->_handle_single_response; Test::More::is($ropaque, $opaque + 1); } sub silent_mutation { my $self = shift; my ($cmd, $key, $value) = @_; $empty->($key); my $extra = pack "NN", 82, 0; $mc->send_silent($cmd, $key, $value, 7278552, $extra, 0); $check->($key, $value); } sub _handle_single_response { my $self = shift; my $myopaque = shift; $self->{socket}->recv(my $response, ::MIN_RECV_BYTES); my ($magic, $cmd, $keylen, $extralen, $datatype, $status, $remaining, $opaque, $ident_hi, $ident_lo) = unpack(::RES_PKT_FMT, $response); return ($opaque, '', '', '', 0) if not defined $remaining; return ($opaque, '', '', '', 0) if ($remaining == 0); # fetch the value my $rv=""; while($remaining - length($rv) > 0) { $self->{socket}->recv(my $buf, $remaining - length($rv)); $rv .= $buf; } if(length($rv) != $remaining) { my $found = length($rv); die("Expected $remaining bytes, got $found"); } my $cas = ($ident_hi * 2 ** 32) + $ident_lo; #if ($status) { #die MC::Error->new($status, $rv); #} return ($opaque, $status, $rv, $cas, $keylen); } sub _do_command { my $self = shift; die unless @_ >= 3; my ($cmd, $key, $val, $extra_header, $cas) = @_; $extra_header = '' unless defined $extra_header; my $opaque = int(rand(2**32)); $self->send_command($cmd, $key, $val, $opaque, $extra_header, $cas); my (undef, $status, $rv, $rcas) = $self->_handle_single_response($opaque); return ($status, $rv, $rcas); } sub _incrdecr_header { my $self = shift; my ($amt, $init, $exp) = @_; my $amt_hi = int($amt / 2 ** 32); my $amt_lo = int($amt % 2 ** 32); my $init_hi = int($init / 2 ** 32); my $init_lo = int($init % 2 ** 32); my $extra_header = pack(::INCRDECR_PKT_FMT, $amt_hi, $amt_lo, $init_hi, $init_lo, $exp); return $extra_header; } sub _incrdecr { my $self = shift; my ($cmd, $key, $amt, $init, $exp) = @_; my ($status, $data, undef) = $self->_do_command($cmd, $key, '', $self->_incrdecr_header($amt, $init, $exp)); my $header = substr $data, 0, 8, ''; my ($resp_hi, $resp_lo) = unpack "NN", $header; my $resp = ($resp_hi * 2 ** 32) + $resp_lo; return $resp; } sub silent_incrdecr { my $self = shift; my ($cmd, $key, $amt, $init, $exp) = @_; my $opaque = 8275753; $mc->send_silent($cmd, $key, '', $opaque, $mc->_incrdecr_header($amt, $init, $exp)); } sub stats { my $self = shift; my $key = shift; my $cas = 0; my $opaque = int(rand(2**32)); $self->send_command(::CMD_STAT, $key, '', $opaque, '', $cas); my %rv = (); my $found_key = ''; my $found_val = ''; my $status= 0; do { my ($op, $status, $data, $cas, $keylen) = $self->_handle_single_response($opaque); if ($keylen > 0) { $found_key = substr($data, 0, $keylen); $found_val = substr($data, $keylen); $rv{$found_key} = $found_val; } else { $found_key = ''; } } while($found_key ne ''); return %rv; } sub get { my $self = shift; my $key = shift; my ($status, $rv, $cas) = $self->_do_command(::CMD_GET, $key, '', ''); my $header = substr $rv, 0, 4, ''; my $flags = unpack("N", $header); return ($status, $rv); } sub get_multi { my $self = shift; my @keys = @_; for (my $i = 0; $i < @keys; $i++) { $self->send_command(::CMD_GETQ, $keys[$i], '', $i, '', 0); } my $terminal = @keys + 10; $self->send_command(::CMD_NOOP, '', '', $terminal); my %return; my $status = 0; while (1) { my ($opaque, $status, $data) = $self->_handle_single_response; last if $opaque == $terminal; my $header = substr $data, 0, 4, ''; my $flags = unpack("N", $header); $return{$keys[$opaque]} = [$flags, $data]; } return %return if wantarray; return \%return; } sub version { my $self = shift; return $self->_do_command(::CMD_VERSION, '', ''); } sub flush { my $self = shift; return $self->_do_command(::CMD_FLUSH, '', ''); } sub add { my $self = shift; my ($key, $val, $flags, $expire) = @_; my $extra_header = pack "NN", $flags, $expire; my $cas = 0; return $self->_do_command(::CMD_ADD, $key, $val, $extra_header, $cas); } sub set { my $self = shift; my $flags = 0; my $cas = 0; my ($key, $val, $expire) = @_; $expire = defined $expire ? $expire : 0; my $extra_header = pack "NN", $flags, $expire; return $self->_do_command(::CMD_SET, $key, $val, $extra_header, $cas); } sub _append_prepend { my $self = shift; my ($cmd, $key, $val, $cas) = @_; return $self->_do_command($cmd, $key, $val, '', $cas); } sub replace { my $self = shift; my ($key, $val, $flags, $expire) = @_; my $extra_header = pack "NN", $flags, $expire; my $cas = 0; return $self->_do_command(::CMD_REPLACE, $key, $val, $extra_header, $cas); } sub delete { my $self = shift; my ($key) = @_; return $self->_do_command(::CMD_DELETE, $key, ''); } sub incr { my $self = shift; my ($key, $amt, $init, $exp) = @_; $amt = 1 unless defined $amt; $init = 0 unless defined $init; $exp = 0 unless defined $exp; return $self->_incrdecr(::CMD_INCR, $key, $amt, $init, $exp); } sub decr { my $self = shift; my ($key, $amt, $init, $exp) = @_; $amt = 1 unless defined $amt; $init = 0 unless defined $init; $exp = 0 unless defined $exp; return $self->_incrdecr(::CMD_DECR, $key, $amt, $init, $exp); } sub noop { my $self = shift; return $self->_do_command(::CMD_NOOP, '', ''); } package MC::Error; use strict; use warnings; use constant ERR_UNKNOWN_CMD => 0x81; use constant ERR_NOT_FOUND => 0x1; use constant ERR_EXISTS => 0x2; use constant ERR_TOO_BIG => 0x3; use constant ERR_EINVAL => 0x4; use constant ERR_NOT_STORED => 0x5; use constant ERR_DELTA_BADVAL => 0x6; use constant ERR_AUTH_ERROR => 0x20; use overload '""' => sub { my $self = shift; return "Memcache Error ($self->[0]): $self->[1]"; }; sub new { my $class = shift; my $error = [@_]; my $self = bless $error, (ref $class || $class); return $self; } sub not_found { my $self = shift; return $self->[0] == ERR_NOT_FOUND; } sub exists { my $self = shift; return $self->[0] == ERR_EXISTS; } sub too_big { my $self = shift; return $self->[0] == ERR_TOO_BIG; } sub delta_badval { my $self = shift; return $self->[0] == ERR_DELTA_BADVAL; } sub auth_error { my $self = shift; return $self->[0] == ERR_AUTH_ERROR; } unlink $sasldb; # vim: filetype=perl memcached-1.5.6/t/stats-detail.t0000644000175000017500000000410512416643766013417 00000000000000#!/usr/bin/perl use strict; use Test::More tests => 24; use FindBin qw($Bin); use lib "$Bin/lib"; use MemcachedTest; my $server = new_memcached(); my $sock = $server->sock; my $expire; print $sock "stats detail dump\r\n"; is(scalar <$sock>, "END\r\n", "verified empty stats at start"); print $sock "stats detail on\r\n"; is(scalar <$sock>, "OK\r\n", "detail collection turned on"); print $sock "set foo:123 0 0 6\r\nfooval\r\n"; is(scalar <$sock>, "STORED\r\n", "stored foo"); print $sock "stats detail dump\r\n"; is(scalar <$sock>, "PREFIX foo get 0 hit 0 set 1 del 0\r\n", "details after set"); is(scalar <$sock>, "END\r\n", "end of details"); mem_get_is($sock, "foo:123", "fooval"); print $sock "stats detail dump\r\n"; is(scalar <$sock>, "PREFIX foo get 1 hit 1 set 1 del 0\r\n", "details after get with hit"); is(scalar <$sock>, "END\r\n", "end of details"); mem_get_is($sock, "foo:124", undef); print $sock "stats detail dump\r\n"; is(scalar <$sock>, "PREFIX foo get 2 hit 1 set 1 del 0\r\n", "details after get without hit"); is(scalar <$sock>, "END\r\n", "end of details"); print $sock "delete foo:125\r\n"; is(scalar <$sock>, "NOT_FOUND\r\n", "sent delete command"); print $sock "stats detail dump\r\n"; is(scalar <$sock>, "PREFIX foo get 2 hit 1 set 1 del 1\r\n", "details after delete"); is(scalar <$sock>, "END\r\n", "end of details"); print $sock "stats reset\r\n"; is(scalar <$sock>, "RESET\r\n", "stats cleared"); print $sock "stats detail dump\r\n"; is(scalar <$sock>, "END\r\n", "empty stats after clear"); mem_get_is($sock, "foo:123", "fooval"); print $sock "stats detail dump\r\n"; is(scalar <$sock>, "PREFIX foo get 1 hit 1 set 0 del 0\r\n", "details after clear and get"); is(scalar <$sock>, "END\r\n", "end of details"); print $sock "stats detail off\r\n"; is(scalar <$sock>, "OK\r\n", "detail collection turned off"); mem_get_is($sock, "foo:124", undef); mem_get_is($sock, "foo:123", "fooval"); print $sock "stats detail dump\r\n"; is(scalar <$sock>, "PREFIX foo get 1 hit 1 set 0 del 0\r\n", "details after stats turned off"); is(scalar <$sock>, "END\r\n", "end of details"); memcached-1.5.6/t/cas.t0000664000175000017500000001113513115057711011554 00000000000000#!/usr/bin/perl use strict; use Test::More tests => 43; use FindBin qw($Bin); use lib "$Bin/lib"; use MemcachedTest; my $server = new_memcached(); my $sock = $server->sock; my $sock2 = $server->new_sock; my @result; my @result2; ok($sock != $sock2, "have two different connections open"); sub check_args { my ($line, $name) = @_; my $svr = new_memcached(); my $s = $svr->sock; print $s $line; is(scalar <$s>, "CLIENT_ERROR bad command line format\r\n", $name); undef $svr; } check_args "cas bad blah 0 0 0\r\n\r\n", "bad flags"; check_args "cas bad 0 blah 0 0\r\n\r\n", "bad exp"; check_args "cas bad 0 0 blah 0\r\n\r\n", "bad cas"; check_args "cas bad 0 0 0 blah\r\n\r\n", "bad size"; # gets foo (should not exist) print $sock "gets foo\r\n"; is(scalar <$sock>, "END\r\n", "gets failed"); # set foo print $sock "set foo 0 0 6\r\nbarval\r\n"; is(scalar <$sock>, "STORED\r\n", "stored barval"); # gets foo and verify identifier exists @result = mem_gets($sock, "foo"); mem_gets_is($sock,$result[0],"foo","barval"); # cas fail print $sock "cas foo 0 0 6 123\r\nbarva2\r\n"; is(scalar <$sock>, "EXISTS\r\n", "cas failed for foo"); # gets foo - success @result = mem_gets($sock, "foo"); mem_gets_is($sock,$result[0],"foo","barval"); # cas success print $sock "cas foo 0 0 6 $result[0]\r\nbarva2\r\n"; is(scalar <$sock>, "STORED\r\n", "cas success, set foo"); # cas failure (reusing the same key) print $sock "cas foo 0 0 6 $result[0]\r\nbarva2\r\n"; is(scalar <$sock>, "EXISTS\r\n", "reusing a CAS ID"); # delete foo print $sock "delete foo\r\n"; is(scalar <$sock>, "DELETED\r\n", "deleted foo"); # cas missing print $sock "cas foo 0 0 6 $result[0]\r\nbarva2\r\n"; is(scalar <$sock>, "NOT_FOUND\r\n", "cas failed, foo does not exist"); # cas empty print $sock "cas foo 0 0 6 \r\nbarva2\r\n"; is(scalar <$sock>, "ERROR\r\n", "cas empty, throw error"); # cant parse barval2\r\n is(scalar <$sock>, "ERROR\r\n", "error out on barval2 parsing"); # set foo1 print $sock "set foo1 0 0 1\r\n1\r\n"; is(scalar <$sock>, "STORED\r\n", "set foo1"); # set foo2 print $sock "set foo2 0 0 1\r\n2\r\n"; is(scalar <$sock>, "STORED\r\n", "set foo2"); # gets foo1 check print $sock "gets foo1\r\n"; ok(scalar <$sock> =~ /VALUE foo1 0 1 (\d+)\r\n/, "gets foo1 regexp success"); my $foo1_cas = $1; is(scalar <$sock>, "1\r\n","gets foo1 data is 1"); is(scalar <$sock>, "END\r\n","gets foo1 END"); # gets foo2 check print $sock "gets foo2\r\n"; ok(scalar <$sock> =~ /VALUE foo2 0 1 (\d+)\r\n/,"gets foo2 regexp success"); my $foo2_cas = $1; is(scalar <$sock>, "2\r\n","gets foo2 data is 2"); is(scalar <$sock>, "END\r\n","gets foo2 END"); # validate foo1 != foo2 ok($foo1_cas != $foo2_cas,"foo1 != foo2 single-gets success"); # multi-gets print $sock "gets foo1 foo2\r\n"; ok(scalar <$sock> =~ /VALUE foo1 0 1 (\d+)\r\n/, "validating first set of data is foo1"); $foo1_cas = $1; is(scalar <$sock>, "1\r\n", "validating foo1 set of data is 1"); ok(scalar <$sock> =~ /VALUE foo2 0 1 (\d+)\r\n/, "validating second set of data is foo2"); $foo2_cas = $1; is(scalar <$sock>, "2\r\n", "validating foo2 set of data is 2"); is(scalar <$sock>, "END\r\n","validating foo1,foo2 gets is over - END"); # validate foo1 != foo2 ok($foo1_cas != $foo2_cas, "foo1 != foo2 multi-gets success"); ### simulate race condition with cas # gets foo1 - success @result = mem_gets($sock, "foo1"); ok($result[0] != "", "sock - gets foo1 is not empty"); # gets foo2 - success @result2 = mem_gets($sock2, "foo1"); ok($result2[0] != "","sock2 - gets foo1 is not empty"); print $sock "cas foo1 0 0 6 $result[0]\r\nbarva2\r\n"; print $sock2 "cas foo1 0 0 5 $result2[0]\r\napple\r\n"; my $res1 = <$sock>; my $res2 = <$sock2>; ok( ( $res1 eq "STORED\r\n" && $res2 eq "EXISTS\r\n") || ( $res1 eq "EXISTS\r\n" && $res2 eq "STORED\r\n"), "cas on same item from two sockets"); ### bug 15: http://code.google.com/p/memcached/issues/detail?id=15 # set foo print $sock "set bug15 0 0 1\r\n0\r\n"; is(scalar <$sock>, "STORED\r\n", "stored 0"); # Check out the first gets. print $sock "gets bug15\r\n"; ok(scalar <$sock> =~ /VALUE bug15 0 1 (\d+)\r\n/, "gets bug15 regexp success"); my $bug15_cas = $1; is(scalar <$sock>, "0\r\n", "gets bug15 data is 0"); is(scalar <$sock>, "END\r\n","gets bug15 END"); # Increment print $sock "incr bug15 1\r\n"; is(scalar <$sock>, "1\r\n", "incr worked"); # Validate a changed CAS print $sock "gets bug15\r\n"; ok(scalar <$sock> =~ /VALUE bug15 0 1 (\d+)\r\n/, "gets bug15 regexp success"); my $next_bug15_cas = $1; is(scalar <$sock>, "1\r\n", "gets bug15 data is 1"); is(scalar <$sock>, "END\r\n","gets bug15 END"); ok($bug15_cas != $next_bug15_cas, "CAS changed"); memcached-1.5.6/t/lru-crawler.t0000664000175000017500000000464213160272015013246 00000000000000#!/usr/bin/perl use strict; use warnings; use Test::More tests => 221; use FindBin qw($Bin); use lib "$Bin/lib"; use MemcachedTest; my $server = new_memcached('-m 32 -o no_modern'); { my $stats = mem_stats($server->sock, ' settings'); is($stats->{lru_crawler}, "no"); } my $sock = $server->sock; # Fill a slab a bit. # Some immortal items, some long expiring items, some short expiring items. # Done so the immortals end up at the tail. for (1 .. 30) { print $sock "set ifoo$_ 0 0 2\r\nok\r\n"; is(scalar <$sock>, "STORED\r\n", "stored key"); } for (1 .. 30) { print $sock "set lfoo$_ 0 3600 2\r\nok\r\n"; is(scalar <$sock>, "STORED\r\n", "stored key"); } for (1 .. 30) { print $sock "set sfoo$_ 0 1 2\r\nok\r\n"; is(scalar <$sock>, "STORED\r\n", "stored key"); } { my $slabs = mem_stats($sock, "slabs"); is($slabs->{"1:used_chunks"}, 90, "slab1 has 90 used chunks"); } sleep 3; print $sock "lru_crawler enable\r\n"; is(scalar <$sock>, "OK\r\n", "enabled lru crawler"); { my $stats = mem_stats($server->sock, ' settings'); is($stats->{lru_crawler}, "yes"); } print $sock "lru_crawler crawl 1\r\n"; is(scalar <$sock>, "OK\r\n", "kicked lru crawler"); while (1) { my $stats = mem_stats($sock); last unless $stats->{lru_crawler_running}; sleep 1; } { my $slabs = mem_stats($sock, "slabs"); is($slabs->{"1:used_chunks"}, 60, "slab1 now has 60 used chunks"); my $items = mem_stats($sock, "items"); is($items->{"items:1:crawler_reclaimed"}, 30, "slab1 has 30 reclaims"); } for (1 .. 30) { mem_get_is($sock, "ifoo$_", "ok"); mem_get_is($sock, "lfoo$_", "ok"); mem_get_is($sock, "sfoo$_", undef); } print $sock "lru_crawler disable\r\n"; is(scalar <$sock>, "OK\r\n", "disabled lru crawler"); { my $stats = mem_stats($server->sock, ' settings'); is($stats->{lru_crawler}, "no"); } $server->stop; # Test initializing crawler from starttime. $server = new_memcached('-m 32 -o no_modern,lru_crawler'); $sock = $server->sock; for (1 .. 30) { print $sock "set sfoo$_ 0 1 2\r\nok\r\n"; is(scalar <$sock>, "STORED\r\n", "stored key"); } sleep 3; print $sock "lru_crawler crawl 1\r\n"; is(scalar <$sock>, "OK\r\n", "kicked lru crawler"); while (1) { my $stats = mem_stats($sock); last unless $stats->{lru_crawler_running}; sleep 1; } { my $slabs = mem_stats($sock, "slabs"); is($slabs->{"1:used_chunks"}, 0, "slab1 now has 0 used chunks"); } memcached-1.5.6/t/issue_260.t0000775000175000017500000000450113025643161012527 00000000000000#!/usr/bin/perl # Issue #260 is a terrible bug. # In order to run this test: # * checkout 1.4.15 # * change TAIL_REPAIR_TIME from (3 * 3600) to 3 # Now it should cause an assert. Patches can be tested to fix it. use strict; use Test::More; use FindBin qw($Bin); use lib "$Bin/lib"; use MemcachedTest; plan skip_all => "Only possible to test #260 under artificial conditions"; exit 0; plan tests => 11074; # assuming max slab is 1M and default mem is 64M my $server = new_memcached(); my $sock = $server->sock; # create a big value for the largest slab my $max = 1024 * 1024; my $big = 'x' x (1024 * 1024 - 250); ok(length($big) > 512 * 1024); ok(length($big) < 1024 * 1024); # set the big value my $len = length($big); print $sock "set big 0 0 $len\r\n$big\r\n"; is(scalar <$sock>, "STORED\r\n", "stored big"); mem_get_is($sock, "big", $big); # no evictions yet my $stats = mem_stats($sock); is($stats->{"evictions"}, "0", "no evictions to start"); # set many big items, enough to get evictions for (my $i = 0; $i < 100; $i++) { print $sock "set item_$i 0 0 $len\r\n$big\r\n"; is(scalar <$sock>, "STORED\r\n", "stored item_$i"); } # some evictions should have happened my $stats = mem_stats($sock); my $evictions = int($stats->{"evictions"}); ok($evictions == 37, "some evictions happened"); # the first big value should be gone mem_get_is($sock, "big", undef); # the earliest items should be gone too for (my $i = 0; $i < $evictions - 1; $i++) { mem_get_is($sock, "item_$i", undef); } # check that the non-evicted are the right ones for (my $i = $evictions - 1; $i < $evictions + 4; $i++) { mem_get_is($sock, "item_$i", $big); } # Now we fill a slab with incrementable items... for (my $i = 0; $i < 10923; $i++) { print $sock "set sitem_$i 0 0 1\r\n1\r\n"; is(scalar <$sock>, "STORED\r\n", "stored sitem_$i"); } my $stats = mem_stats($sock); my $evictions = int($stats->{"evictions"}); ok($evictions == 38, "one more eviction happened: $evictions"); # That evicted item was the first one we put in. mem_get_is($sock, "sitem_0", undef); sleep 15; # Now we increment the item which should be on the tail. # THIS asserts the memcached-debug binary. print $sock "incr sitem_1 1\r\n"; is(scalar <$sock>, "2\r\n", "incremented to two"); #my $stats = mem_stats($sock, "slabs"); #is($stats->{"1:free_chunks"}, 0, "free chunks should still be 0"); memcached-1.5.6/t/flags.t0000775000175000017500000000071713115057711012111 00000000000000#!/usr/bin/perl use strict; use Test::More tests => 8; use FindBin qw($Bin); use lib "$Bin/lib"; use MemcachedTest; my $server = new_memcached(); my $sock = $server->sock; # set foo (and should get it) for my $flags (0, 123, 2**16-1, 2**31) { print $sock "set foo $flags 0 6\r\nfooval\r\n"; is(scalar <$sock>, "STORED\r\n", "stored foo"); mem_get_is({ sock => $sock, flags => $flags }, "foo", "fooval", "got flags $flags back"); } memcached-1.5.6/t/issue_29.t0000644000175000017500000000116712416643766012470 00000000000000#!/usr/bin/perl use strict; use Test::More tests => 4; use FindBin qw($Bin); use lib "$Bin/lib"; use MemcachedTest; my $server = new_memcached(); my $sock = $server->sock; print $sock "set issue29 0 0 0\r\n\r\n"; is (scalar <$sock>, "STORED\r\n", "stored issue29"); my $first_stats = mem_stats($sock, "slabs"); my $first_used = $first_stats->{"1:used_chunks"}; is(1, $first_used, "Used one"); print $sock "set issue29_b 0 0 0\r\n\r\n"; is (scalar <$sock>, "STORED\r\n", "stored issue29_b"); my $second_stats = mem_stats($sock, "slabs"); my $second_used = $second_stats->{"1:used_chunks"}; is(2, $second_used, "Used two") memcached-1.5.6/t/issue_152.t0000644000175000017500000000060612416643766012542 00000000000000#!/usr/bin/perl use strict; use Test::More tests => 2; use FindBin qw($Bin); use lib "$Bin/lib"; use MemcachedTest; my $server = new_memcached(); my $sock = $server->sock; my $key = "a"x251; print $sock "set a 1 0 1\r\na\r\n"; is (scalar <$sock>, "STORED\r\n", "Stored key"); print $sock "get a $key\r\n"; is (scalar <$sock>, "CLIENT_ERROR bad command line format\r\n", "illegal key"); memcached-1.5.6/t/issue_3.t0000644000175000017500000000252412416643766012376 00000000000000#!/usr/bin/perl use strict; use Test::More tests => 8; use FindBin qw($Bin); use lib "$Bin/lib"; use MemcachedTest; my $server = new_memcached(); my $sock = $server->sock; my $key = "del_key"; print $sock "delete $key\r\n"; is (scalar <$sock>, "NOT_FOUND\r\n", "not found on delete"); print $sock "delete $key 10\r\n"; is (scalar <$sock>, "CLIENT_ERROR bad command line format." . " Usage: delete [noreply]\r\n", "invalid delete"); print $sock "add $key 0 0 1\r\nx\r\n"; is (scalar <$sock>, "STORED\r\n", "Add before a broken delete."); print $sock "delete $key 10 noreply\r\n"; # Does not reply # is (scalar <$sock>, "ERROR\r\n", "Even more invalid delete"); print $sock "add $key 0 0 1\r\nx\r\n"; is (scalar <$sock>, "NOT_STORED\r\n", "Failed to add after failed silent delete."); print $sock "delete $key noreply\r\n"; # Will not reply, so let's do a set and check that. print $sock "set $key 0 0 1\r\nx\r\n"; is (scalar <$sock>, "STORED\r\n", "Stored a key"); print $sock "delete $key\r\n"; is (scalar <$sock>, "DELETED\r\n", "Properly deleted"); print $sock "set $key 0 0 1\r\nx\r\n"; is (scalar <$sock>, "STORED\r\n", "Stored a key"); print $sock "delete $key noreply\r\n"; # will not reply, but a subsequent add will succeed print $sock "add $key 0 0 1\r\nx\r\n"; is (scalar <$sock>, "STORED\r\n", "Add succeeded after deletion."); memcached-1.5.6/t/issue_192.t0000664000175000017500000000104413025643161012527 00000000000000#!/usr/bin/perl use strict; use Test::More tests => 2; use FindBin qw($Bin); use lib "$Bin/lib"; use MemcachedTest; my $server = new_memcached(); my $sock = $server->sock; ok($server->new_sock, "opened new socket"); print $sock "\x80\x12\x00\x01\x08\x00\x00\x00\xff\xff\xff\xe8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\x01\x00\x00\x00\x00\x00\x00\x00\x00\x000\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"; sleep 0.5; ok($server->new_sock, "failed to open new socket"); memcached-1.5.6/t/chunked-items.t0000664000175000017500000000746613150607001013552 00000000000000#!/usr/bin/perl # Networked logging tests. use strict; use warnings; use Test::More; use FindBin qw($Bin); use lib "$Bin/lib"; use MemcachedTest; my $server = new_memcached('-m 48 -o slab_chunk_max=16384'); my $sock = $server->sock; # We're testing to ensure item chaining doesn't corrupt or poorly overlap # data, so create a non-repeating pattern. my @parts = (); for (1 .. 8000) { push(@parts, $_); } my $pattern = join(':', @parts); my $plen = length($pattern); print $sock "set pattern 0 0 $plen\r\n$pattern\r\n"; is(scalar <$sock>, "STORED\r\n", "stored pattern successfully"); mem_get_is($sock, "pattern", $pattern); for (1..5) { my $size = 400 * 1024; my $data = "x" x $size; print $sock "set foo$_ 0 0 $size\r\n$data\r\n"; my $res = <$sock>; is($res, "STORED\r\n", "stored some big items"); } { my $max = 1024 * 1024; my $big = "a big value that's > .5M and < 1M. "; while (length($big) * 2 < $max) { $big = $big . $big; } my $biglen = length($big); for (1..100) { print $sock "set toast$_ 0 0 $biglen\r\n$big\r\n"; is(scalar <$sock>, "STORED\r\n", "stored big"); mem_get_is($sock, "toast$_", $big); } } # Test a wide range of sets. { my $len = 1024 * 200; while ($len < 1024 * 1024) { my $val = "B" x $len; print $sock "set foo_$len 0 0 $len\r\n$val\r\n"; is(scalar <$sock>, "STORED\r\n", "stored size $len"); $len += 2048; } } # Test long appends and prepends. # Note: memory bloats like crazy if we use one test per request. { my $str = 'seedstring'; my $len = length($str); print $sock "set appender 0 0 $len\r\n$str\r\n"; is(scalar <$sock>, "STORED\r\n", "stored seed string for append"); my $unexpected = 0; for my $part (@parts) { # reduce required loops but still have a pattern. my $todo = $part . "x" x 10; $str .= $todo; my $len = length($todo); print $sock "append appender 0 0 $len\r\n$todo\r\n"; is(scalar <$sock>, "STORED\r\n", "append $todo size $len"); print $sock "get appender\r\n"; my $header = scalar <$sock>; my $body = scalar <$sock>; my $end = scalar <$sock>; $unexpected++ unless $body eq "$str\r\n"; } is($unexpected, 0, "No unexpected results during appends\n"); # Now test appending a chunked item to a chunked item. $len = length($str); print $sock "append appender 0 0 $len\r\n$str\r\n"; is(scalar <$sock>, "STORED\r\n", "append large string size $len"); mem_get_is($sock, "appender", $str . $str); print $sock "delete appender\r\n"; is(scalar <$sock>, "DELETED\r\n", "removed appender key"); } { my $str = 'seedstring'; my $len = length($str); print $sock "set prepender 0 0 $len\r\n$str\r\n"; is(scalar <$sock>, "STORED\r\n", "stored seed string for append"); my $unexpected = 0; for my $part (@parts) { # reduce required loops but still have a pattern. $part .= "x" x 10; $str = $part . $str; my $len = length($part); print $sock "prepend prepender 0 0 $len\r\n$part\r\n"; is(scalar <$sock>, "STORED\r\n", "prepend $part size $len"); print $sock "get prepender\r\n"; my $header = scalar <$sock>; my $body = scalar <$sock>; my $end = scalar <$sock>; $unexpected++ unless $body eq "$str\r\n"; } is($unexpected, 0, "No unexpected results during prepends\n"); # Now test prepending a chunked item to a chunked item. $len = length($str); print $sock "prepend prepender 0 0 $len\r\n$str\r\n"; is(scalar <$sock>, "STORED\r\n", "prepend large string size $len"); mem_get_is($sock, "prepender", $str . $str); print $sock "delete prepender\r\n"; is(scalar <$sock>, "DELETED\r\n", "removed prepender key"); } done_testing(); memcached-1.5.6/t/stats-conns.t0000775000175000017500000000364413160272015013267 00000000000000#!/usr/bin/perl use strict; use Test::More tests => 10; use FindBin qw($Bin); use lib "$Bin/lib"; use MemcachedTest; ## First make sure we report UNIX-domain sockets correctly my $filename = "/tmp/memcachetest$$"; my $server = new_memcached("-s $filename"); my $sock = $server->sock; my $stats_sock = $server->new_sock; ok(-S $filename, "creating unix domain socket $filename"); print $sock "set foo 0 0 6\r\n"; sleep(1); # so we can test secs_since_last_cmd is nonzero print $stats_sock "stats conns\r\n"; my $stats; while (<$stats_sock>) { last if /^(\.|END)/; $stats .= $_; } like($stats, qr/STAT \d+:addr /); $stats =~ m/STAT (\d+):addr unix:(.*[^\r\n])/g; my $listen_fd = $1; my $socket_path = $2; # getsockname(2) doesn't return socket path on GNU/Hurd (and maybe others) SKIP: { skip "socket path checking on GNU kernel", 1 if ($^O eq 'gnu'); is($socket_path, $filename, "unix domain socket path reported correctly"); }; $stats =~ m/STAT (\d+):state conn_listening\r\n/g; is($1, $listen_fd, "listen socket fd reported correctly"); like($stats, qr/STAT \d+:state conn_nread/, "one client is sending data"); like($stats, qr/STAT \d+:state conn_parse_cmd/, "one client is in command processing"); like($stats, qr/STAT \d+:secs_since_last_cmd [1-9]\r/, "nonzero secs_since_last_cmd"); $server->stop; unlink($filename); ## Now look at TCP $server = new_memcached("-l 0.0.0.0"); $sock = $server->sock; $stats_sock = $server->new_sock; print $sock "set foo 0 0 6\r\n"; print $stats_sock "stats conns\r\n"; $stats = ''; while (<$stats_sock>) { last if /^(\.|END)/; $stats .= $_; } like($stats, qr/STAT \d+:state conn_listen/, "there is a listen socket"); $stats =~ m/STAT \d+:addr udp:0.0.0.0:(\d+)/; is($1, $server->udpport, "udp port number is correct"); $stats =~ m/STAT \d+:addr tcp:0.0.0.0:(\d+)/; print STDERR "PORT: ", $server->port, "\n"; is($1, $server->port, "tcp port number is correct"); memcached-1.5.6/t/issue_140.t0000644000175000017500000000171612416643766012542 00000000000000#!/usr/bin/perl use strict; use Test::More; use FindBin qw($Bin); use lib "$Bin/lib"; use MemcachedTest; plan skip_all => 'Fix for Issue 140 was only an illusion'; plan tests => 7; my $server = new_memcached(); my $sock = $server->sock; print $sock "set a 0 0 1\r\na\r\n"; is (scalar <$sock>, "STORED\r\n", "stored key"); my $stats = mem_stats($sock, "items"); my $age = $stats->{"items:1:age"}; isnt ($age, "0", "Age should not be zero"); print $sock "flush_all\r\n"; is (scalar <$sock>, "OK\r\n", "items flushed"); my $stats = mem_stats($sock, "items"); my $age = $stats->{"items:1:age"}; is ($age, undef, "all should be gone"); print $sock "set a 0 1 1\r\na\r\n"; is (scalar <$sock>, "STORED\r\n", "stored key"); my $stats = mem_stats($sock, "items"); my $age = $stats->{"items:1:age"}; isnt ($age, "0", "Age should not be zero"); sleep(3); my $stats = mem_stats($sock, "items"); my $age = $stats->{"items:1:age"}; is ($age, undef, "all should be gone"); memcached-1.5.6/t/binary.t0000775000175000017500000006014313240770206012300 00000000000000#!/usr/bin/perl use strict; use warnings; use Test::More; use FindBin qw($Bin); use lib "$Bin/lib"; use MemcachedTest; my $server = new_memcached("-o no_modern"); ok($server, "started the server"); # Based almost 100% off testClient.py which is: # Copyright (c) 2007 Dustin Sallings # Command constants use constant CMD_GET => 0x00; use constant CMD_SET => 0x01; use constant CMD_ADD => 0x02; use constant CMD_REPLACE => 0x03; use constant CMD_DELETE => 0x04; use constant CMD_INCR => 0x05; use constant CMD_DECR => 0x06; use constant CMD_QUIT => 0x07; use constant CMD_FLUSH => 0x08; use constant CMD_GETQ => 0x09; use constant CMD_NOOP => 0x0A; use constant CMD_VERSION => 0x0B; use constant CMD_GETK => 0x0C; use constant CMD_GETKQ => 0x0D; use constant CMD_APPEND => 0x0E; use constant CMD_PREPEND => 0x0F; use constant CMD_STAT => 0x10; use constant CMD_SETQ => 0x11; use constant CMD_ADDQ => 0x12; use constant CMD_REPLACEQ => 0x13; use constant CMD_DELETEQ => 0x14; use constant CMD_INCREMENTQ => 0x15; use constant CMD_DECREMENTQ => 0x16; use constant CMD_QUITQ => 0x17; use constant CMD_FLUSHQ => 0x18; use constant CMD_APPENDQ => 0x19; use constant CMD_PREPENDQ => 0x1A; use constant CMD_TOUCH => 0x1C; use constant CMD_GAT => 0x1D; use constant CMD_GATQ => 0x1E; use constant CMD_GATK => 0x23; use constant CMD_GATKQ => 0x24; # REQ and RES formats are divided even though they currently share # the same format, since they _could_ differ in the future. use constant REQ_PKT_FMT => "CCnCCnNNNN"; use constant RES_PKT_FMT => "CCnCCnNNNN"; use constant INCRDECR_PKT_FMT => "NNNNN"; use constant MIN_RECV_BYTES => length(pack(RES_PKT_FMT)); use constant REQ_MAGIC => 0x80; use constant RES_MAGIC => 0x81; my $mc = MC::Client->new; # Let's turn on detail stats for all this stuff $mc->stats('detail on'); my $check = sub { my ($key, $orig_flags, $orig_val) = @_; my ($flags, $val, $cas) = $mc->get($key); is($flags, $orig_flags, "Flags is set properly"); ok($val eq $orig_val || $val == $orig_val, $val . " = " . $orig_val); }; my $set = sub { my ($key, $exp, $orig_flags, $orig_value) = @_; $mc->set($key, $orig_value, $orig_flags, $exp); $check->($key, $orig_flags, $orig_value); }; my $empty = sub { my $key = shift; my $rv =()= eval { $mc->get($key) }; is($rv, 0, "Didn't get a result from get"); ok($@->not_found, "We got a not found error when we expected one"); }; my $delete = sub { my ($key, $when) = @_; $mc->delete($key, $when); $empty->($key); }; # diag "Test Version"; my $v = $mc->version; ok(defined $v && length($v), "Proper version: $v"); # Bug 71 { my %stats1 = $mc->stats(''); $mc->flush; my %stats2 = $mc->stats(''); is($stats2{'cmd_flush'}, $stats1{'cmd_flush'} + 1, "Stats not updated on a binary flush"); } # diag "Flushing..."; $mc->flush; # diag "Noop"; $mc->noop; # diag "Simple set/get"; $set->('x', 5, 19, "somevalue"); # diag "Delete"; $delete->('x'); # diag "Flush"; $set->('x', 5, 19, "somevaluex"); $set->('y', 5, 17, "somevaluey"); $mc->flush; $empty->('x'); $empty->('y'); { # diag "Some chunked item tests"; my $s2 = new_memcached('-o no_modern,slab_chunk_max=4096'); ok($s2, "started the server"); my $m2 = MC::Client->new($s2); # Specifically trying to cross the chunk boundary when internally # appending CLRF. for my $k (7900..8100) { my $val = 'd' x $k; $val .= '123'; $m2->set('t', $val, 0, 0); # Ensure we get back the same value. Bugs can chop chars. my (undef, $gval, undef) = $m2->get('t'); ok($gval eq $val, $gval . " = " . $val); } my $cval = ('d' x 8100) . '123'; my $m3 = $s2->new_sock; mem_get_is($m3, 't', $cval, "large value set from bin fetched from ascii"); } { # diag "Add"; $empty->('i'); $mc->add('i', 'ex', 5, 10); $check->('i', 5, "ex"); my $rv =()= eval { $mc->add('i', "ex2", 10, 5) }; is($rv, 0, "Add didn't return anything"); ok($@->exists, "Expected exists error received"); $check->('i', 5, "ex"); } { # diag "Too big."; $empty->('toobig'); $mc->set('toobig', 'not too big', 10, 10); eval { my $bigval = ("x" x (1024*1024)) . "x"; $mc->set('toobig', $bigval, 10, 10); }; ok($@->too_big, "Was too big"); $empty->('toobig'); } { # diag "Replace"; $empty->('j'); my $rv =()= eval { $mc->replace('j', "ex", 19, 5) }; is($rv, 0, "Replace didn't return anything"); ok($@->not_found, "Expected not_found error received"); $empty->('j'); $mc->add('j', "ex2", 14, 5); $check->('j', 14, "ex2"); $mc->replace('j', "ex3", 24, 5); $check->('j', 24, "ex3"); } { # diag "MultiGet"; $mc->add('xx', "ex", 1, 5); $mc->add('wye', "why", 2, 5); my $rv = $mc->get_multi(qw(xx wye zed)); # CAS is returned with all gets. $rv->{xx}->[2] = 0; $rv->{wye}->[2] = 0; is_deeply($rv->{xx}, [1, 'ex', 0], "X is correct"); is_deeply($rv->{wye}, [2, 'why', 0], "Y is correct"); is(keys(%$rv), 2, "Got only two answers like we expect"); } # diag "Test increment"; $mc->flush; is($mc->incr("x"), 0, "First incr call is zero"); is($mc->incr("x"), 1, "Second incr call is one"); is($mc->incr("x", 211), 212, "Adding 211 gives you 212"); is($mc->incr("x", 2**33), 8589934804, "Blast the 32bit border"); # diag "Issue 48 - incrementing plain text."; { $mc->set("issue48", "text", 0, 0); my $rv =()= eval { $mc->incr('issue48'); }; ok($@ && $@->delta_badval, "Expected invalid value when incrementing text."); $check->('issue48', 0, "text"); $rv =()= eval { $mc->decr('issue48'); }; ok($@ && $@->delta_badval, "Expected invalid value when decrementing text."); $check->('issue48', 0, "text"); } # diag "Issue 320 - incr/decr wrong length for initial value"; { $mc->flush; is($mc->incr("issue320", 1, 1, 0), 1, "incr initial value is 1"); my (undef, $rv, undef) = $mc->get("issue320"); is(length($rv), 1, "initial value length is 1"); is($rv, "1", "initial value is 1"); } # diag "Test decrement"; $mc->flush; is($mc->incr("x", undef, 5), 5, "Initial value"); is($mc->decr("x"), 4, "Decrease by one"); is($mc->decr("x", 211), 0, "Floor is zero"); { # diag "bug220 my ($rv, $cas) = $mc->set("bug220", "100", 0, 0); my ($irv, $icas) = $mc->incr_cas("bug220", 999); ok($icas != $cas); is($irv, 1099, "Incr amount failed"); my ($flags, $val, $gcas) = $mc->get("bug220"); is($gcas, $icas, "CAS didn't match after incr/gets"); ($irv, $icas) = $mc->incr_cas("bug220", 999); ok($icas != $cas); is($irv, 2098, "Incr amount failed"); ($flags, $val, $gcas) = $mc->get("bug220"); is($gcas, $icas, "CAS didn't match after incr/gets"); } { # diag "bug21"; $mc->add("bug21", "9223372036854775807", 0, 0); is($mc->incr("bug21"), 9223372036854775808, "First incr for bug21."); is($mc->incr("bug21"), 9223372036854775809, "Second incr for bug21."); is($mc->decr("bug21"), 9223372036854775808, "Decr for bug21."); } { # diag "CAS"; $mc->flush; { my $rv =()= eval { $mc->set("x", "bad value", 19, 5, 0x7FFFFFF) }; is($rv, 0, "Empty return on expected failure"); ok($@->not_found, "Error was 'not found' as expected"); } my ($r, $rcas) = $mc->add("x", "original value", 5, 19); my ($flags, $val, $i) = $mc->get("x"); is($val, "original value", "->gets returned proper value"); is($rcas, $i, "Add CAS matched."); { my $rv =()= eval { $mc->set("x", "broken value", 19, 5, $i+1) }; is($rv, 0, "Empty return on expected failure (1)"); ok($@->exists, "Expected error state of 'exists' (1)"); } ($r, $rcas) = $mc->set("x", "new value", 19, 5, $i); my ($newflags, $newval, $newi) = $mc->get("x"); is($newval, "new value", "CAS properly overwrote value"); is($rcas, $newi, "Get CAS matched."); { my $rv =()= eval { $mc->set("x", "replay value", 19, 5, $i) }; is($rv, 0, "Empty return on expected failure (2)"); ok($@->exists, "Expected error state of 'exists' (2)"); } } # diag "Touch commands"; { $mc->flush; $mc->set("totouch", "toast", 0, 1); my $res = $mc->touch("totouch", 10); sleep 2; $check->("totouch", 0, "toast"); $mc->set("totouch", "toast2", 0, 1); my ($flags, $val, $i) = $mc->gat("totouch", 10); is($val, "toast2", "GAT returned correct value"); sleep 2; $check->("totouch", 0, "toast2"); # Test miss as well $mc->set("totouch", "toast3", 0, 1); $res = $mc->touch("totouch", 1); sleep 3; $empty->("totouch"); } # diag "Silent set."; $mc->silent_mutation(::CMD_SETQ, 'silentset', 'silentsetval'); # diag "Silent add."; $mc->silent_mutation(::CMD_ADDQ, 'silentadd', 'silentaddval'); # diag "Silent replace."; { my $key = "silentreplace"; my $extra = pack "NN", 829, 0; $empty->($key); # $mc->send_silent(::CMD_REPLACEQ, $key, 'somevalue', 7278552, $extra, 0); # $empty->($key); $mc->add($key, "xval", 831, 0); $check->($key, 831, 'xval'); $mc->send_silent(::CMD_REPLACEQ, $key, 'somevalue', 7278552, $extra, 0); $check->($key, 829, 'somevalue'); } # diag "Silent delete"; { my $key = "silentdelete"; $empty->($key); $mc->set($key, "some val", 19, 0); $mc->send_silent(::CMD_DELETEQ, $key, '', 772); $empty->($key); } # diag "Silent increment"; { my $key = "silentincr"; my $opaque = 98428747; $empty->($key); $mc->silent_incrdecr(::CMD_INCREMENTQ, $key, 0, 0, 0); is($mc->incr($key, 0), 0, "First call is 0"); $mc->silent_incrdecr(::CMD_INCREMENTQ, $key, 8, 0, 0); is($mc->incr($key, 0), 8); } # diag "Silent decrement"; { my $key = "silentdecr"; my $opaque = 98428147; $empty->($key); $mc->silent_incrdecr(::CMD_DECREMENTQ, $key, 0, 185, 0); is($mc->incr($key, 0), 185); $mc->silent_incrdecr(::CMD_DECREMENTQ, $key, 8, 0, 0); is($mc->incr($key, 0), 177); } # diag "Silent flush"; { my %stats1 = $mc->stats(''); $set->('x', 5, 19, "somevaluex"); $set->('y', 5, 17, "somevaluey"); $mc->send_silent(::CMD_FLUSHQ, '', '', 2775256); $empty->('x'); $empty->('y'); my %stats2 = $mc->stats(''); is($stats2{'cmd_flush'}, $stats1{'cmd_flush'} + 1, "Stats not updated on a binary quiet flush"); } # diag "Append"; { my $key = "appendkey"; my $value = "some value"; $set->($key, 8, 19, $value); $mc->_append_prepend(::CMD_APPEND, $key, " more"); $check->($key, 19, $value . " more"); } # diag "Prepend"; { my $key = "prependkey"; my $value = "some value"; $set->($key, 8, 19, $value); $mc->_append_prepend(::CMD_PREPEND, $key, "prefixed "); $check->($key, 19, "prefixed " . $value); } # diag "Silent append"; { my $key = "appendqkey"; my $value = "some value"; $set->($key, 8, 19, $value); $mc->send_silent(::CMD_APPENDQ, $key, " more", 7284492); $check->($key, 19, $value . " more"); } # diag "Silent prepend"; { my $key = "prependqkey"; my $value = "some value"; $set->($key, 8, 19, $value); $mc->send_silent(::CMD_PREPENDQ, $key, "prefixed ", 7284492); $check->($key, 19, "prefixed " . $value); } # diag "Leaky binary get test."; # # http://code.google.com/p/memcached/issues/detail?id=16 { # Get a new socket so we can speak text to it. my $sock = $server->new_sock; my $max = 1024 * 1024; my $big = "a big value that's > .5M and < 1M. "; while (length($big) * 2 < $max) { $big = $big . $big; } my $biglen = length($big); for(1..100) { my $key = "some_key_$_"; # print STDERR "Key is $key\n"; # print $sock "set $key 0 0 $vallen\r\n$value\r\n"; print $sock "set $key 0 0 $biglen\r\n$big\r\n"; is(scalar <$sock>, "STORED\r\n", "stored big"); my ($f, $v, $c) = $mc->get($key); } } # diag "Test stats settings." { my %stats = $mc->stats('settings'); is(1024, $stats{'maxconns'}); isnt('NULL', $stats{'domain_socket'}); is('on', $stats{'evictions'}); is('yes', $stats{'cas_enabled'}); is('yes', $stats{'flush_enabled'}); } # diag "Test quit commands."; { my $s2 = new_memcached(); my $mc2 = MC::Client->new($s2); $mc2->send_command(CMD_QUITQ, '', '', 0, '', 0); # Five seconds ought to be enough to get hung up on. my $oldalarmt = alarm(5); # Verify we can't read anything. my $bytesread = -1; eval { local $SIG{'ALRM'} = sub { die "timeout" }; my $data = ""; $bytesread = sysread($mc2->{socket}, $data, 24), }; is($bytesread, 0, "Read after quit."); # Restore signal stuff. alarm($oldalarmt); } # diag "Test protocol boundary overruns"; { use List::Util qw[min]; # Attempting some protocol overruns by toying around with the edge # of the data buffer at a few different sizes. This assumes the # boundary is at or around 2048 bytes. for (my $i = 1900; $i < 2100; $i++) { my $k = "test_key_$i"; my $v = 'x' x $i; # diag "Trying $i $k"; my $extra = pack "NN", 82, 0; my $data = $mc->build_command(::CMD_SETQ, $k, $v, 0, $extra, 0); $data .= $mc->build_command(::CMD_SETQ, "alt_$k", "blah", 0, $extra, 0); if (length($data) > 2024) { for (my $j = 2024; $j < min(2096, length($data)); $j++) { $mc->{socket}->send(substr($data, 0, $j)); $mc->flush_socket; sleep(0.001); $mc->{socket}->send(substr($data, $j)); $mc->flush_socket; } } else { $mc->{socket}->send($data); } $mc->flush_socket; $check->($k, 82, $v); $check->("alt_$k", 82, "blah"); } } # Along with the assertion added to the code to verify we're staying # within bounds when we do a stats detail dump (detail turned on at # the top). my %stats = $mc->stats('detail dump'); # This test causes a disconnection. { # diag "Key too large."; my $key = "x" x 365; eval { $mc->get($key, 'should die', 10, 10); }; ok($@->einval, "Invalid key length"); } done_testing(); # ###################################################################### # Test ends around here. # ###################################################################### package MC::Client; use strict; use warnings; use fields qw(socket); use IO::Socket::INET; sub new { my $self = shift; my ($s) = @_; $s = $server unless defined $s; my $sock = $s->sock; $self = fields::new($self); $self->{socket} = $sock; return $self; } sub build_command { my $self = shift; die "Not enough args to send_command" unless @_ >= 4; my ($cmd, $key, $val, $opaque, $extra_header, $cas) = @_; $extra_header = '' unless defined $extra_header; my $keylen = length($key); my $vallen = length($val); my $extralen = length($extra_header); my $datatype = 0; # field for future use my $reserved = 0; # field for future use my $totallen = $keylen + $vallen + $extralen; my $ident_hi = 0; my $ident_lo = 0; if ($cas) { $ident_hi = int($cas / 2 ** 32); $ident_lo = int($cas % 2 ** 32); } my $msg = pack(::REQ_PKT_FMT, ::REQ_MAGIC, $cmd, $keylen, $extralen, $datatype, $reserved, $totallen, $opaque, $ident_hi, $ident_lo); my $full_msg = $msg . $extra_header . $key . $val; return $full_msg; } sub send_command { my $self = shift; die "Not enough args to send_command" unless @_ >= 4; my ($cmd, $key, $val, $opaque, $extra_header, $cas) = @_; my $full_msg = $self->build_command($cmd, $key, $val, $opaque, $extra_header, $cas); my $sent = $self->{socket}->send($full_msg); die("Send failed: $!") unless $sent; if($sent != length($full_msg)) { die("only sent $sent of " . length($full_msg) . " bytes"); } } sub flush_socket { my $self = shift; $self->{socket}->flush; } # Send a silent command and ensure it doesn't respond. sub send_silent { my $self = shift; die "Not enough args to send_silent" unless @_ >= 4; my ($cmd, $key, $val, $opaque, $extra_header, $cas) = @_; $self->send_command($cmd, $key, $val, $opaque, $extra_header, $cas); $self->send_command(::CMD_NOOP, '', '', $opaque + 1); my ($ropaque, $data) = $self->_handle_single_response; Test::More::is($ropaque, $opaque + 1); } sub silent_mutation { my $self = shift; my ($cmd, $key, $value) = @_; $empty->($key); my $extra = pack "NN", 82, 0; $mc->send_silent($cmd, $key, $value, 7278552, $extra, 0); $check->($key, 82, $value); } sub _handle_single_response { my $self = shift; my $myopaque = shift; my $hdr = ""; while(::MIN_RECV_BYTES - length($hdr) > 0) { $self->{socket}->recv(my $response, ::MIN_RECV_BYTES - length($hdr)); $hdr .= $response; } Test::More::is(length($hdr), ::MIN_RECV_BYTES, "Expected read length"); my ($magic, $cmd, $keylen, $extralen, $datatype, $status, $remaining, $opaque, $ident_hi, $ident_lo) = unpack(::RES_PKT_FMT, $hdr); Test::More::is($magic, ::RES_MAGIC, "Got proper response magic"); my $cas = ($ident_hi * 2 ** 32) + $ident_lo; return ($opaque, '', $cas, 0) if($remaining == 0); # fetch the value my $rv=""; while($remaining - length($rv) > 0) { $self->{socket}->recv(my $buf, $remaining - length($rv)); $rv .= $buf; } if(length($rv) != $remaining) { my $found = length($rv); die("Expected $remaining bytes, got $found"); } if (defined $myopaque) { Test::More::is($opaque, $myopaque, "Expected opaque"); } else { Test::More::pass("Implicit pass since myopaque is undefined"); } if ($status) { die MC::Error->new($status, $rv); } return ($opaque, $rv, $cas, $keylen); } sub _do_command { my $self = shift; die unless @_ >= 3; my ($cmd, $key, $val, $extra_header, $cas) = @_; $extra_header = '' unless defined $extra_header; my $opaque = int(rand(2**32)); $self->send_command($cmd, $key, $val, $opaque, $extra_header, $cas); my (undef, $rv, $rcas) = $self->_handle_single_response($opaque); return ($rv, $rcas); } sub _incrdecr_header { my $self = shift; my ($amt, $init, $exp) = @_; my $amt_hi = int($amt / 2 ** 32); my $amt_lo = int($amt % 2 ** 32); my $init_hi = int($init / 2 ** 32); my $init_lo = int($init % 2 ** 32); my $extra_header = pack(::INCRDECR_PKT_FMT, $amt_hi, $amt_lo, $init_hi, $init_lo, $exp); return $extra_header; } sub _incrdecr_cas { my $self = shift; my ($cmd, $key, $amt, $init, $exp) = @_; my ($data, $rcas) = $self->_do_command($cmd, $key, '', $self->_incrdecr_header($amt, $init, $exp)); my $header = substr $data, 0, 8, ''; my ($resp_hi, $resp_lo) = unpack "NN", $header; my $resp = ($resp_hi * 2 ** 32) + $resp_lo; return $resp, $rcas; } sub _incrdecr { my $self = shift; my ($v, $c) = $self->_incrdecr_cas(@_); return $v } sub silent_incrdecr { my $self = shift; my ($cmd, $key, $amt, $init, $exp) = @_; my $opaque = 8275753; $mc->send_silent($cmd, $key, '', $opaque, $mc->_incrdecr_header($amt, $init, $exp)); } sub stats { my $self = shift; my $key = shift; my $cas = 0; my $opaque = int(rand(2**32)); $self->send_command(::CMD_STAT, $key, '', $opaque, '', $cas); my %rv = (); my $found_key = ''; my $found_val = ''; do { my ($op, $data, $cas, $keylen) = $self->_handle_single_response($opaque); if($keylen > 0) { $found_key = substr($data, 0, $keylen); $found_val = substr($data, $keylen); $rv{$found_key} = $found_val; } else { $found_key = ''; } } while($found_key ne ''); return %rv; } sub get { my $self = shift; my $key = shift; my ($rv, $cas) = $self->_do_command(::CMD_GET, $key, '', ''); my $header = substr $rv, 0, 4, ''; my $flags = unpack("N", $header); return ($flags, $rv, $cas); } sub get_multi { my $self = shift; my @keys = @_; for (my $i = 0; $i < @keys; $i++) { $self->send_command(::CMD_GETQ, $keys[$i], '', $i, '', 0); } my $terminal = @keys + 10; $self->send_command(::CMD_NOOP, '', '', $terminal); my %return; while (1) { my ($opaque, $data) = $self->_handle_single_response; last if $opaque == $terminal; my $header = substr $data, 0, 4, ''; my $flags = unpack("N", $header); $return{$keys[$opaque]} = [$flags, $data]; } return %return if wantarray; return \%return; } sub touch { my $self = shift; my ($key, $expire) = @_; my $extra_header = pack "N", $expire; my $cas = 0; return $self->_do_command(::CMD_TOUCH, $key, '', $extra_header, $cas); } sub gat { my $self = shift; my $key = shift; my $expire = shift; my $extra_header = pack "N", $expire; my ($rv, $cas) = $self->_do_command(::CMD_GAT, $key, '', $extra_header); my $header = substr $rv, 0, 4, ''; my $flags = unpack("N", $header); return ($flags, $rv, $cas); } sub version { my $self = shift; return $self->_do_command(::CMD_VERSION, '', ''); } sub flush { my $self = shift; return $self->_do_command(::CMD_FLUSH, '', ''); } sub add { my $self = shift; my ($key, $val, $flags, $expire) = @_; my $extra_header = pack "NN", $flags, $expire; my $cas = 0; return $self->_do_command(::CMD_ADD, $key, $val, $extra_header, $cas); } sub set { my $self = shift; my ($key, $val, $flags, $expire, $cas) = @_; my $extra_header = pack "NN", $flags, $expire; return $self->_do_command(::CMD_SET, $key, $val, $extra_header, $cas); } sub _append_prepend { my $self = shift; my ($cmd, $key, $val, $cas) = @_; return $self->_do_command($cmd, $key, $val, '', $cas); } sub replace { my $self = shift; my ($key, $val, $flags, $expire) = @_; my $extra_header = pack "NN", $flags, $expire; my $cas = 0; return $self->_do_command(::CMD_REPLACE, $key, $val, $extra_header, $cas); } sub delete { my $self = shift; my ($key) = @_; return $self->_do_command(::CMD_DELETE, $key, ''); } sub incr { my $self = shift; my ($key, $amt, $init, $exp) = @_; $amt = 1 unless defined $amt; $init = 0 unless defined $init; $exp = 0 unless defined $exp; return $self->_incrdecr(::CMD_INCR, $key, $amt, $init, $exp); } sub incr_cas { my $self = shift; my ($key, $amt, $init, $exp) = @_; $amt = 1 unless defined $amt; $init = 0 unless defined $init; $exp = 0 unless defined $exp; return $self->_incrdecr_cas(::CMD_INCR, $key, $amt, $init, $exp); } sub decr { my $self = shift; my ($key, $amt, $init, $exp) = @_; $amt = 1 unless defined $amt; $init = 0 unless defined $init; $exp = 0 unless defined $exp; return $self->_incrdecr(::CMD_DECR, $key, $amt, $init, $exp); } sub noop { my $self = shift; return $self->_do_command(::CMD_NOOP, '', ''); } package MC::Error; use strict; use warnings; use constant ERR_UNKNOWN_CMD => 0x81; use constant ERR_NOT_FOUND => 0x1; use constant ERR_EXISTS => 0x2; use constant ERR_TOO_BIG => 0x3; use constant ERR_EINVAL => 0x4; use constant ERR_NOT_STORED => 0x5; use constant ERR_DELTA_BADVAL => 0x6; use overload '""' => sub { my $self = shift; return "Memcache Error ($self->[0]): $self->[1]"; }; sub new { my $class = shift; my $error = [@_]; my $self = bless $error, (ref $class || $class); return $self; } sub not_found { my $self = shift; return $self->[0] == ERR_NOT_FOUND; } sub exists { my $self = shift; return $self->[0] == ERR_EXISTS; } sub too_big { my $self = shift; return $self->[0] == ERR_TOO_BIG; } sub delta_badval { my $self = shift; return $self->[0] == ERR_DELTA_BADVAL; } sub einval { my $self = shift; return $self->[0] == ERR_EINVAL; } # vim: filetype=perl memcached-1.5.6/t/00-startup.t0000775000175000017500000000377313115057711012741 00000000000000#!/usr/bin/perl use strict; use Test::More tests => 20; use FindBin qw($Bin); use lib "$Bin/lib"; use MemcachedTest; eval { my $server = new_memcached(); ok($server, "started the server"); }; is($@, '', 'Basic startup works'); eval { my $server = new_memcached("-l fooble"); }; ok($@, "Died with illegal -l args"); eval { my $server = new_memcached("-l 127.0.0.1"); }; is($@,'', "-l 127.0.0.1 works"); eval { my $server = new_memcached('-C'); my $stats = mem_stats($server->sock, 'settings'); is('no', $stats->{'cas_enabled'}); }; is($@, '', "-C works"); eval { my $server = new_memcached('-b 8675'); my $stats = mem_stats($server->sock, 'settings'); is('8675', $stats->{'tcp_backlog'}); }; is($@, '', "-b works"); foreach my $val ('auto', 'ascii') { eval { my $server = new_memcached("-B $val"); my $stats = mem_stats($server->sock, 'settings'); ok($stats->{'binding_protocol'} =~ /$val/, "$val works"); }; is($@, '', "$val works"); } # For the binary test, we just verify it starts since we don't have an easy bin client. eval { my $server = new_memcached("-B binary"); }; is($@, '', "binary works"); eval { my $server = new_memcached("-vv -B auto"); }; is($@, '', "auto works"); eval { my $server = new_memcached("-vv -B ascii"); }; is($@, '', "ascii works"); # For the binary test, we just verify it starts since we don't have an easy bin client. eval { my $server = new_memcached("-vv -B binary"); }; is($@, '', "binary works"); # Should blow up with something invalid. eval { my $server = new_memcached("-B http"); }; ok($@, "Died with illegal -B arg."); # Maximum connections must be greater than 0. eval { my $server = new_memcached("-c 0"); }; ok($@, "Died with invalid maximum connections 0."); eval { my $server = new_memcached("-c -1"); }; ok($@, "Died with invalid maximum connections -1."); # Should not allow -t 0 eval { my $server = new_memcached("-t 0"); }; ok($@, "Died with illegal 0 thread count"); memcached-1.5.6/t/lib/0000755000175000017500000000000013240770206011444 500000000000000memcached-1.5.6/t/lib/MemcachedTest.pm0000664000175000017500000002017613240770206014440 00000000000000package MemcachedTest; use strict; use IO::Socket::INET; use IO::Socket::UNIX; use Exporter 'import'; use Carp qw(croak); use vars qw(@EXPORT); # Instead of doing the substitution with Autoconf, we assume that # cwd == builddir. use Cwd; my $builddir = getcwd; my @unixsockets = (); @EXPORT = qw(new_memcached sleep mem_get_is mem_gets mem_gets_is mem_stats supports_sasl free_port supports_drop_priv supports_extstore); sub sleep { my $n = shift; select undef, undef, undef, $n; } sub mem_stats { my ($sock, $type) = @_; $type = $type ? " $type" : ""; print $sock "stats$type\r\n"; my $stats = {}; while (<$sock>) { last if /^(\.|END)/; /^(STAT|ITEM) (\S+)\s+([^\r\n]+)/; #print " slabs: $_"; $stats->{$2} = $3; } return $stats; } sub mem_get_is { # works on single-line values only. no newlines in value. my ($sock_opts, $key, $val, $msg) = @_; my $opts = ref $sock_opts eq "HASH" ? $sock_opts : {}; my $sock = ref $sock_opts eq "HASH" ? $opts->{sock} : $sock_opts; my $expect_flags = $opts->{flags} || 0; my $dval = defined $val ? "'$val'" : ""; $msg ||= "$key == $dval"; print $sock "get $key\r\n"; if (! defined $val) { my $line = scalar <$sock>; if ($line =~ /^VALUE/) { $line .= scalar(<$sock>) . scalar(<$sock>); } Test::More::is($line, "END\r\n", $msg); } else { my $len = length($val); my $body = scalar(<$sock>); my $expected = "VALUE $key $expect_flags $len\r\n$val\r\nEND\r\n"; if (!$body || $body =~ /^END/) { Test::More::is($body, $expected, $msg); return; } $body .= scalar(<$sock>) . scalar(<$sock>); Test::More::is($body, $expected, $msg); } } sub mem_gets { # works on single-line values only. no newlines in value. my ($sock_opts, $key) = @_; my $opts = ref $sock_opts eq "HASH" ? $sock_opts : {}; my $sock = ref $sock_opts eq "HASH" ? $opts->{sock} : $sock_opts; my $val; my $expect_flags = $opts->{flags} || 0; print $sock "gets $key\r\n"; my $response = <$sock>; if ($response =~ /^END/) { return "NOT_FOUND"; } else { $response =~ /VALUE (.*) (\d+) (\d+) (\d+)/; my $flags = $2; my $len = $3; my $identifier = $4; read $sock, $val , $len; # get the END $_ = <$sock>; $_ = <$sock>; return ($identifier,$val); } } sub mem_gets_is { # works on single-line values only. no newlines in value. my ($sock_opts, $identifier, $key, $val, $msg) = @_; my $opts = ref $sock_opts eq "HASH" ? $sock_opts : {}; my $sock = ref $sock_opts eq "HASH" ? $opts->{sock} : $sock_opts; my $expect_flags = $opts->{flags} || 0; my $dval = defined $val ? "'$val'" : ""; $msg ||= "$key == $dval"; print $sock "gets $key\r\n"; if (! defined $val) { my $line = scalar <$sock>; if ($line =~ /^VALUE/) { $line .= scalar(<$sock>) . scalar(<$sock>); } Test::More::is($line, "END\r\n", $msg); } else { my $len = length($val); my $body = scalar(<$sock>); my $expected = "VALUE $key $expect_flags $len $identifier\r\n$val\r\nEND\r\n"; if (!$body || $body =~ /^END/) { Test::More::is($body, $expected, $msg); return; } $body .= scalar(<$sock>) . scalar(<$sock>); Test::More::is($body, $expected, $msg); } } sub free_port { my $type = shift || "tcp"; my $sock; my $port; while (!$sock) { $port = int(rand(20000)) + 30000; $sock = IO::Socket::INET->new(LocalAddr => '127.0.0.1', LocalPort => $port, Proto => $type, ReuseAddr => 1); } return $port; } sub supports_udp { my $output = `$builddir/memcached-debug -h`; return 0 if $output =~ /^memcached 1\.1\./; return 1; } sub supports_sasl { my $output = `$builddir/memcached-debug -h`; return 1 if $output =~ /sasl/i; return 0; } sub supports_extstore { my $output = `$builddir/memcached-debug -h`; return 1 if $output =~ /ext_path/i; return 0; } sub supports_drop_priv { my $output = `$builddir/memcached-debug -h`; return 1 if $output =~ /no_drop_privileges/i; return 0; } sub new_memcached { my ($args, $passed_port) = @_; my $port = $passed_port; my $host = '127.0.0.1'; if ($ENV{T_MEMD_USE_DAEMON}) { my ($host, $port) = ($ENV{T_MEMD_USE_DAEMON} =~ m/^([^:]+):(\d+)$/); my $conn = IO::Socket::INET->new(PeerAddr => "$host:$port"); if ($conn) { return Memcached::Handle->new(conn => $conn, host => $host, port => $port); } croak("Failed to connect to specified memcached server.") unless $conn; } if ($< == 0) { $args .= " -u root"; } $args .= " -o relaxed_privileges"; my $udpport; if ($args =~ /-l (\S+)/) { $port = free_port(); $udpport = free_port("udp"); $args .= " -p $port"; if (supports_udp()) { $args .= " -U $udpport"; } } elsif ($args !~ /-s (\S+)/) { my $num = @unixsockets; my $file = "/tmp/memcachetest.$$.$num"; $args .= " -s $file"; push(@unixsockets, $file); } my $childpid = fork(); my $exe = "$builddir/memcached-debug"; croak("memcached binary doesn't exist. Haven't run 'make' ?\n") unless -e $exe; croak("memcached binary not executable\n") unless -x _; unless ($childpid) { exec "$builddir/timedrun 600 $exe $args"; exit; # never gets here. } # unix domain sockets if ($args =~ /-s (\S+)/) { sleep 1; my $filename = $1; my $conn = IO::Socket::UNIX->new(Peer => $filename) || croak("Failed to connect to unix domain socket: $! '$filename'"); return Memcached::Handle->new(pid => $childpid, conn => $conn, domainsocket => $filename, host => $host, port => $port); } # try to connect / find open port, only if we're not using unix domain # sockets for (1..20) { my $conn = IO::Socket::INET->new(PeerAddr => "127.0.0.1:$port"); if ($conn) { return Memcached::Handle->new(pid => $childpid, conn => $conn, udpport => $udpport, host => $host, port => $port); } select undef, undef, undef, 0.10; } croak("Failed to startup/connect to memcached server."); } END { for (@unixsockets) { unlink $_; } } ############################################################################ package Memcached::Handle; sub new { my ($class, %params) = @_; return bless \%params, $class; } sub DESTROY { my $self = shift; kill 2, $self->{pid}; } sub stop { my $self = shift; kill 15, $self->{pid}; } sub host { $_[0]{host} } sub port { $_[0]{port} } sub udpport { $_[0]{udpport} } sub sock { my $self = shift; if ($self->{conn} && ($self->{domainsocket} || getpeername($self->{conn}))) { return $self->{conn}; } return $self->new_sock; } sub new_sock { my $self = shift; if ($self->{domainsocket}) { return IO::Socket::UNIX->new(Peer => $self->{domainsocket}); } else { return IO::Socket::INET->new(PeerAddr => "$self->{host}:$self->{port}"); } } sub new_udp_sock { my $self = shift; return IO::Socket::INET->new(PeerAddr => '127.0.0.1', PeerPort => $self->{udpport}, Proto => 'udp', LocalAddr => '127.0.0.1', LocalPort => MemcachedTest::free_port('udp'), ); } 1; memcached-1.5.6/t/lru.t0000775000175000017500000000315213160272015011607 00000000000000#!/usr/bin/perl use strict; use Test::More tests => 149; use FindBin qw($Bin); use lib "$Bin/lib"; use MemcachedTest; # assuming max slab is 1M and default mem is 64M my $server = new_memcached('-o no_modern'); my $sock = $server->sock; # create a big value for the largest slab my $max = 1024 * 1024; my $big = 'x' x (1024 * 1024 - 250); ok(length($big) > 512 * 1024); ok(length($big) < 1024 * 1024); # test that an even bigger value is rejected while we're here my $too_big = $big . $big . $big; my $len = length($too_big); print $sock "set too_big 0 0 $len\r\n$too_big\r\n"; is(scalar <$sock>, "SERVER_ERROR object too large for cache\r\n", "too_big not stored"); # set the big value my $len = length($big); print $sock "set big 0 0 $len\r\n$big\r\n"; is(scalar <$sock>, "STORED\r\n", "stored big"); mem_get_is($sock, "big", $big); # no evictions yet my $stats = mem_stats($sock); is($stats->{"evictions"}, "0", "no evictions to start"); # set many big items, enough to get evictions for (my $i = 0; $i < 100; $i++) { print $sock "set item_$i 0 0 $len\r\n$big\r\n"; is(scalar <$sock>, "STORED\r\n", "stored item_$i"); } # some evictions should have happened my $stats = mem_stats($sock); my $evictions = int($stats->{"evictions"}); ok($evictions == 37, "some evictions happened"); # the first big value should be gone mem_get_is($sock, "big", undef); # the earliest items should be gone too for (my $i = 0; $i < $evictions - 1; $i++) { mem_get_is($sock, "item_$i", undef); } # check that the non-evicted are the right ones for (my $i = $evictions - 1; $i < $evictions + 4; $i++) { mem_get_is($sock, "item_$i", $big); } memcached-1.5.6/t/daemonize.t0000755000175000017500000000134212416643766012777 00000000000000#!/usr/bin/perl use strict; use Test::More tests => 7; use FindBin qw($Bin); use lib "$Bin/lib"; use MemcachedTest; use File::Temp qw(tempfile); my (undef, $tmpfn) = tempfile(); my $server = new_memcached("-d -P $tmpfn"); my $sock = $server->sock; sleep 0.5; ok(-e $tmpfn, "pid file exists"); ok(-s $tmpfn, "pid file has length"); open (my $fh, $tmpfn) or die; my $readpid = do { local $/; <$fh>; }; chomp $readpid; close ($fh); ok(kill(0, $readpid), "process is still running"); my $stats = mem_stats($sock); is($stats->{pid}, $readpid, "memcached reports same pid as file"); ok($server->new_sock, "opened new socket"); ok(kill(9, $readpid), "sent KILL signal"); sleep 0.5; ok(! $server->new_sock, "failed to open new socket"); memcached-1.5.6/t/issue_42.t0000664000175000017500000000103313160272015012433 00000000000000#!/usr/bin/perl use strict; use Test::More tests => 11; use FindBin qw($Bin); use lib "$Bin/lib"; use MemcachedTest; my $server = new_memcached("-o no_modern"); my $sock = $server->sock; my $value = "B"x10; my $key = 0; for ($key = 0; $key < 10; $key++) { print $sock "set key$key 0 0 10\r\n$value\r\n"; is (scalar <$sock>, "STORED\r\n", "stored key$key"); } my $first_stats = mem_stats($sock, "slabs"); my $req = $first_stats->{"1:mem_requested"}; ok ($req == "640" || $req == "800" || $req == "770", "Check allocated size"); memcached-1.5.6/t/udp.t0000775000175000017500000002274513160272015011606 00000000000000#!/usr/bin/perl use strict; use Test::More tests => 48; use FindBin qw($Bin); use lib "$Bin/lib"; use MemcachedTest; use constant IS_ASCII => 0; use constant IS_BINARY => 1; use constant ENTRY_EXISTS => 0; use constant ENTRY_MISSING => 1; use constant BIN_REQ_MAGIC => 0x80; use constant BIN_RES_MAGIC => 0x81; use constant CMD_GET => 0x00; use constant CMD_SET => 0x01; use constant CMD_ADD => 0x02; use constant CMD_REPLACE => 0x03; use constant CMD_DELETE => 0x04; use constant CMD_INCR => 0x05; use constant CMD_DECR => 0x06; use constant CMD_APPEND => 0x0E; use constant CMD_PREPEND => 0x0F; use constant REQ_PKT_FMT => "CCnCCnNNNN"; use constant RES_PKT_FMT => "CCnCCnNNNN"; use constant INCRDECR_PKT_FMT => "NNNNN"; use constant MIN_RECV_BYTES => length(pack(RES_PKT_FMT)); my $server = new_memcached("-l 127.0.0.1"); my $sock = $server->sock; # set foo (and should get it) print $sock "set foo 0 0 6\r\nfooval\r\n"; is(scalar <$sock>, "STORED\r\n", "stored foo"); mem_get_is($sock, "foo", "fooval"); my $usock = $server->new_udp_sock or die "Can't bind : $@\n"; # testing sequence of request ids for my $offt (1, 1, 2) { my $req = 160 + $offt; my $res = send_udp_request($usock, $req, "get foo\r\n"); ok($res, "got result"); is(keys %$res, 1, "one key (one packet)"); ok($res->{0}, "only got seq number 0"); is(substr($res->{0}, 8), "VALUE foo 0 6\r\nfooval\r\nEND\r\n"); is(hexify(substr($res->{0}, 0, 2)), hexify(pack("n", $req)), "udp request number in response ($req) is correct"); } # op tests for my $prot (::IS_ASCII,::IS_BINARY) { udp_set_test($prot,45,"aval$prot","1",0,0); udp_set_test($prot,45,"bval$prot","abcd" x 1024,0,0); udp_get_test($prot,45,"aval$prot","1",::ENTRY_EXISTS); udp_get_test($prot,45,"404$prot","1",::ENTRY_MISSING); udp_incr_decr_test($prot,45,"aval$prot","1","incr",1); udp_incr_decr_test($prot,45,"aval$prot","1","decr",2); udp_delete_test($prot,45,"aval$prot"); } sub udp_set_test { my ($protocol, $req_id, $key, $value, $flags, $exp) = @_; my $req = ""; my $val_len = length($value); if ($protocol == ::IS_ASCII) { $req = "set $key $flags $exp $val_len\r\n$value\r\n"; } elsif ($protocol == ::IS_BINARY) { my $key_len = length($key); my $extra = pack "NN",$flags,$exp; my $extra_len = length($extra); my $total_len = $val_len + $extra_len + $key_len; $req = pack(::REQ_PKT_FMT, ::BIN_REQ_MAGIC, ::CMD_SET, $key_len, $extra_len, 0, 0, $total_len, 0, 0, 0); $req .= $extra . $key . $value; } my $datagrams = send_udp_request($usock, $req_id, $req); my $resp = construct_udp_message($datagrams); if ($protocol == ::IS_ASCII) { is($resp,"STORED\r\n","Store key $key using ASCII protocol"); } elsif ($protocol == ::IS_BINARY) { my ($resp_magic, $resp_op_code, $resp_key_len, $resp_extra_len, $resp_data_type, $resp_status, $resp_total_len, $resp_opaque, $resp_ident_hi, $resp_ident_lo) = unpack(::RES_PKT_FMT, $resp); is($resp_status,"0","Store key $key using binary protocol"); } } sub udp_get_test { my ($protocol, $req_id, $key, $value, $exists) = @_; my $key_len = length($key); my $value_len = length($value); my $req = ""; if ($protocol == ::IS_ASCII) { $req = "get $key\r\n"; } elsif ($protocol == ::IS_BINARY) { $req = pack(::REQ_PKT_FMT, ::BIN_REQ_MAGIC, ::CMD_GET, $key_len, 0, 0, 0, $key_len, 0, 0, 0); $req .= $key; } my $datagrams = send_udp_request($usock, $req_id, $req); my $resp = construct_udp_message($datagrams); if ($protocol == ::IS_ASCII) { if ($exists == ::ENTRY_EXISTS) { is($resp,"VALUE $key 0 $value_len\r\n$value\r\nEND\r\n","Retrieve entry with key $key using ASCII protocol"); } else { is($resp,"END\r\n","Retrieve non existing entry with key $key using ASCII protocol"); } } elsif ($protocol == ::IS_BINARY) { my ($resp_magic, $resp_op_code, $resp_key_len, $resp_extra_len, $resp_data_type, $resp_status, $resp_total_len, $resp_opaque, $resp_ident_hi, $resp_ident_lo) = unpack(::RES_PKT_FMT, $resp); if ($exists == ::ENTRY_EXISTS) { is($resp_status,"0","Retrieve entry with key $key using binary protocol"); is(substr($resp,::MIN_RECV_BYTES + $resp_extra_len + $resp_key_len, $value_len),$value,"Value for key $key retrieved with binary protocol matches"); } else { is($resp_status,"1","Retrieve non existing entry with key $key using binary protocol"); } } } sub udp_delete_test { my ($protocol, $req_id, $key) = @_; my $req = ""; my $key_len = length($key); if ($protocol == ::IS_ASCII) { $req = "delete $key\r\n"; } elsif ($protocol == ::IS_BINARY) { $req = pack(::REQ_PKT_FMT, ::BIN_REQ_MAGIC, ::CMD_DELETE, $key_len, 0, 0, 0, $key_len, 0, 0, 0); $req .= $key; } my $datagrams = send_udp_request($usock, $req_id, $req); my $resp = construct_udp_message($datagrams); if ($protocol == ::IS_ASCII) { is($resp,"DELETED\r\n","Delete key $key using ASCII protocol"); } elsif ($protocol == ::IS_BINARY) { my ($resp_magic, $resp_op_code, $resp_key_len, $resp_extra_len, $resp_data_type, $resp_status, $resp_total_len, $resp_opaque, $resp_ident_hi, $resp_ident_lo) = unpack(::RES_PKT_FMT, $resp); is($resp_status,"0","Delete key $key using binary protocol"); } } sub udp_incr_decr_test { my ($protocol, $req_id, $key, $val, $optype, $init_val) = @_; my $req = ""; my $key_len = length($key); my $expected_value = 0; my $acmd = "incr"; my $bcmd = ::CMD_INCR; if ($optype eq "incr") { $expected_value = $init_val + $val; } else { $acmd = "decr"; $bcmd = ::CMD_DECR; $expected_value = $init_val - $val; } if ($protocol == ::IS_ASCII) { $req = "$acmd $key $val\r\n"; } elsif ($protocol == ::IS_BINARY) { my $extra = pack(::INCRDECR_PKT_FMT, ($val / 2 ** 32),($val % 2 ** 32), 0, 0, 0); my $extra_len = length($extra); $req = pack(::REQ_PKT_FMT, ::BIN_REQ_MAGIC, $bcmd, $key_len, $extra_len, 0, 0, $key_len + $extra_len, 0, 0, 0); $req .= $extra . $key; } my $datagrams = send_udp_request($usock, $req_id, $req); my $resp = construct_udp_message($datagrams); if ($protocol == ::IS_ASCII) { is($resp,"$expected_value\r\n","perform $acmd math operation on key $key with ASCII protocol"); } elsif ($protocol == ::IS_BINARY) { my ($resp_magic, $resp_op_code, $resp_key_len, $resp_extra_len, $resp_data_type, $resp_status, $resp_total_len, $resp_opaque, $resp_ident_hi, $resp_ident_lo) = unpack(::RES_PKT_FMT, $resp); is($resp_status,"0","perform $acmd math operation on key $key with binary protocol"); my ($resp_hi,$resp_lo) = unpack("NN",substr($resp,::MIN_RECV_BYTES + $resp_extra_len + $resp_key_len, $resp_total_len - $resp_extra_len - $resp_key_len)); is(($resp_hi * 2 ** 32) + $resp_lo,$expected_value,"validate result of binary protocol math operation $acmd . Expected value $expected_value") } } sub construct_udp_message { my $datagrams = shift; my $num_datagram = keys (%$datagrams); my $msg = ""; my $cur_dg =""; my $cur_udp_header =""; for (my $cur_dg_index = 0; $cur_dg_index < $num_datagram; $cur_dg_index++) { $cur_dg = $datagrams->{$cur_dg_index}; isnt($cur_dg,"","missing datagram for segment $cur_dg_index"); $cur_udp_header=substr($cur_dg, 0, 8); $msg .= substr($cur_dg,8); } return $msg; } sub hexify { my $val = shift; $val =~ s/(.)/sprintf("%02x", ord($1))/egs; return $val; } # returns undef on select timeout, or hashref of "seqnum" -> payload (including headers) # verifies that resp_id is equal to id sent in request # ensures consistency in num packets that make up response sub send_udp_request { my ($sock, $reqid, $req) = @_; my $pkt = pack("nnnn", $reqid, 0, 1, 0); # request id (opaque), seq num, #packets, reserved (must be 0) $pkt .= $req; my $fail = sub { my $msg = shift; warn " FAILING send_udp because: $msg\n"; return undef; }; return $fail->("send") unless send($sock, $pkt, 0); my $ret = {}; my $got = 0; # packets got my $numpkts = undef; while (!defined($numpkts) || $got < $numpkts) { my $rin = ''; vec($rin, fileno($sock), 1) = 1; my $rout; return $fail->("timeout after $got packets") unless select($rout = $rin, undef, undef, 1.5); my $res; my $sender = $sock->recv($res, 1500, 0); my ($resid, $seq, $this_numpkts, $resv) = unpack("nnnn", substr($res, 0, 8)); die "Response ID of $resid doesn't match request if of $reqid" unless $resid == $reqid; die "Reserved area not zero" unless $resv == 0; die "num packets changed midstream!" if defined $numpkts && $this_numpkts != $numpkts; $numpkts = $this_numpkts; $ret->{$seq} = $res; $got++; } return $ret; } __END__ $sender = recv($usock, $ans, 1050, 0); __END__ $usock->send ($hispaddr = recv(SOCKET, $rtime, 4, 0)) || die "recv: $!"; ($port, $hisiaddr) = sockaddr_in($hispaddr); $host = gethostbyaddr($hisiaddr, AF_INET); $histime = unpack("N", $rtime) - $SECS_of_70_YEARS ; memcached-1.5.6/t/slabs_reassign.t0000644000175000017500000000465712602341606014015 00000000000000#!/usr/bin/perl use strict; use warnings; use Test::More tests => 130; use FindBin qw($Bin); use lib "$Bin/lib"; use MemcachedTest; # Enable manual slab reassign, cap at 6 slabs my $server = new_memcached('-o slab_reassign -m 4'); my $stats = mem_stats($server->sock, ' settings'); is($stats->{slab_reassign}, "yes"); my $sock = $server->sock; # Fill a largeish slab until it evicts (honors the -m 6) my $bigdata = 'x' x 70000; # slab 31 for (1 .. 60) { print $sock "set bfoo$_ 0 0 70000\r\n", $bigdata, "\r\n"; is(scalar <$sock>, "STORED\r\n", "stored key"); } # Fill a smaller slab until it evicts my $smalldata = 'y' x 20000; # slab 25 for (1 .. 60) { print $sock "set sfoo$_ 0 0 20000\r\n", $smalldata, "\r\n"; is(scalar <$sock>, "STORED\r\n", "stored key"); } my $items_before = mem_stats($sock, "items"); isnt($items_before->{"items:31:evicted"}, 0, "slab 31 evicted is nonzero"); isnt($items_before->{"items:25:evicted"}, 0, "slab 25 evicted is nonzero"); my $slabs_before = mem_stats($sock, "slabs"); # Move a large slab to the smaller slab print $sock "slabs reassign 31 25\r\n"; is(scalar <$sock>, "OK\r\n", "slab rebalancer started"); # Still working out how/if to signal the thread. For now, just sleep. sleep 2; # Check that stats counters increased my $slabs_after = mem_stats($sock, "slabs"); $stats = mem_stats($sock); isnt($stats->{slabs_moved}, 0, "slabs moved is nonzero"); # Check that slab stats reflect the change ok($slabs_before->{"31:total_pages"} != $slabs_after->{"31:total_pages"}, "slab 31 pagecount changed"); ok($slabs_before->{"25:total_pages"} != $slabs_after->{"25:total_pages"}, "slab 25 pagecount changed"); # Try to move another slab, see that you can move two in a row print $sock "slabs reassign 31 25\r\n"; like(scalar <$sock>, qr/^OK/, "Cannot re-run against class with empty space"); # Try to move a page backwards. Should complain that source class isn't "safe" # to move from. # TODO: Wait until the above command completes, then try to move it back? # Seems pointless... #print $sock "slabs reassign 25 31\r\n"; #like(scalar <$sock>, qr/^UNSAFE/, "Cannot move an unsafe slab back"); # Try to insert items into both slabs print $sock "set bfoo51 0 0 70000\r\n", $bigdata, "\r\n"; is(scalar <$sock>, "STORED\r\n", "stored key"); print $sock "set sfoo51 0 0 20000\r\n", $smalldata, "\r\n"; is(scalar <$sock>, "STORED\r\n", "stored key"); # Do need to come up with better automated tests for this. memcached-1.5.6/t/issue_41.t0000644000175000017500000000172412416643766012461 00000000000000#!/usr/bin/perl use strict; use warnings; use POSIX qw(ceil); use Test::More tests => 691; use FindBin qw($Bin); use lib "$Bin/lib"; use MemcachedTest; my $server = new_memcached(); my $sock = $server->sock; my $factor = 2; my $val = "x" x $factor; my $key = ''; # SET items of diverse size to the daemon so it can attempt # to return a large stats output for slabs for (my $i=0; $i<69; $i++) { for (my $j=0; $j<10; $j++) { $key = "$i:$j"; print $sock "set key$key 0 0 $factor\r\n$val\r\n"; is (scalar <$sock>, "STORED\r\n", "stored key$key"); } $factor *= 1.2; $factor = ceil($factor); $val = "x" x $factor; } # This request will kill the daemon if it has not allocated # enough memory internally. my $stats = mem_stats($sock, "slabs"); # Verify whether the daemon is still running or not by asking # it for statistics. print $sock "version\r\n"; my $v = scalar <$sock>; ok(defined $v && length($v), "memcached didn't respond"); memcached-1.5.6/t/stats.t0000775000175000017500000001215613240770206012153 00000000000000#!/usr/bin/perl use strict; use Test::More tests => 108; use FindBin qw($Bin); use lib "$Bin/lib"; use MemcachedTest; my $server = new_memcached("-o no_lru_crawler,no_lru_maintainer"); my $sock = $server->sock; ## Output looks like this: ## ## STAT pid 22969 ## STAT uptime 13 ## STAT time 1259170891 ## STAT version 1.4.3 ## STAT libevent 1.4.13-stable. ## see doc/protocol.txt for others # note that auth stats are tested in auth specific tests my $stats = mem_stats($sock); # Test number of keys is(scalar(keys(%$stats)), 70, "expected count of stats values"); # Test initial state foreach my $key (qw(curr_items total_items bytes cmd_get cmd_set get_hits evictions get_misses get_expired bytes_written delete_hits delete_misses incr_hits incr_misses decr_hits get_flushed decr_misses listen_disabled_num lrutail_reflocked time_in_listen_disabled_us)) { is($stats->{$key}, 0, "initial $key is zero"); } is($stats->{accepting_conns}, 1, "initial accepting_conns is one"); # Do some operations print $sock "set foo 0 0 6\r\nfooval\r\n"; is(scalar <$sock>, "STORED\r\n", "stored foo"); mem_get_is($sock, "foo", "fooval"); my $stats = mem_stats($sock); foreach my $key (qw(total_items curr_items cmd_get cmd_set get_hits)) { is($stats->{$key}, 1, "after one set/one get $key is 1"); } my $cache_dump = mem_stats($sock, " cachedump 1 100"); ok(defined $cache_dump->{'foo'}, "got foo from cachedump"); print $sock "delete foo\r\n"; is(scalar <$sock>, "DELETED\r\n", "deleted foo"); my $stats = mem_stats($sock); is($stats->{delete_hits}, 1); is($stats->{delete_misses}, 0); print $sock "delete foo\r\n"; is(scalar <$sock>, "NOT_FOUND\r\n", "shouldn't delete foo again"); my $stats = mem_stats($sock); is($stats->{delete_hits}, 1); is($stats->{delete_misses}, 1); # incr stats sub check_incr_stats { my ($ih, $im, $dh, $dm) = @_; my $stats = mem_stats($sock); is($stats->{incr_hits}, $ih); is($stats->{incr_misses}, $im); is($stats->{decr_hits}, $dh); is($stats->{decr_misses}, $dm); } print $sock "incr i 1\r\n"; is(scalar <$sock>, "NOT_FOUND\r\n", "shouldn't incr a missing thing"); check_incr_stats(0, 1, 0, 0); print $sock "decr d 1\r\n"; is(scalar <$sock>, "NOT_FOUND\r\n", "shouldn't decr a missing thing"); check_incr_stats(0, 1, 0, 1); print $sock "set n 0 0 1\r\n0\r\n"; is(scalar <$sock>, "STORED\r\n", "stored n"); print $sock "incr n 3\r\n"; is(scalar <$sock>, "3\r\n", "incr works"); check_incr_stats(1, 1, 0, 1); print $sock "decr n 1\r\n"; is(scalar <$sock>, "2\r\n", "decr works"); check_incr_stats(1, 1, 1, 1); # cas stats sub check_cas_stats { my ($ch, $cm, $cb) = @_; my $stats = mem_stats($sock); is($stats->{cas_hits}, $ch); is($stats->{cas_misses}, $cm); is($stats->{cas_badval}, $cb); } check_cas_stats(0, 0, 0); print $sock "cas c 0 0 1 99999999\r\nz\r\n"; is(scalar <$sock>, "NOT_FOUND\r\n", "missed cas"); check_cas_stats(0, 1, 0); print $sock "set c 0 0 1\r\nx\r\n"; is(scalar <$sock>, "STORED\r\n", "stored c"); my ($id, $v) = mem_gets($sock, 'c'); is('x', $v, 'got the expected value'); print $sock "cas c 0 0 1 99999999\r\nz\r\n"; is(scalar <$sock>, "EXISTS\r\n", "missed cas"); check_cas_stats(0, 1, 1); my ($newid, $v) = mem_gets($sock, 'c'); is('x', $v, 'got the expected value'); print $sock "cas c 0 0 1 $id\r\nz\r\n"; is(scalar <$sock>, "STORED\r\n", "good cas"); check_cas_stats(1, 1, 1); my ($newid, $v) = mem_gets($sock, 'c'); is('z', $v, 'got the expected value'); my $settings = mem_stats($sock, ' settings'); is(1024, $settings->{'maxconns'}); isnt('NULL', $settings->{'domain_socket'}); is('on', $settings->{'evictions'}); is('yes', $settings->{'cas_enabled'}); is('no', $settings->{'auth_enabled_sasl'}); print $sock "stats reset\r\n"; is(scalar <$sock>, "RESET\r\n", "good stats reset"); my $stats = mem_stats($sock); is(0, $stats->{'cmd_get'}); is(0, $stats->{'cmd_set'}); is(0, $stats->{'get_hits'}); is(0, $stats->{'get_misses'}); is(0, $stats->{'get_expired'}); is(0, $stats->{'get_flushed'}); is(0, $stats->{'delete_misses'}); is(0, $stats->{'delete_hits'}); is(0, $stats->{'incr_misses'}); is(0, $stats->{'incr_hits'}); is(0, $stats->{'decr_misses'}); is(0, $stats->{'decr_hits'}); is(0, $stats->{'cas_misses'}); is(0, $stats->{'cas_hits'}); is(0, $stats->{'cas_badval'}); is(0, $stats->{'evictions'}); is(0, $stats->{'reclaimed'}); is(0, $stats->{'lrutail_reflocked'}); # item expired print $sock "set should_expire 0 2678400 6\r\nfooval\r\n"; #2678400 = 31 days in seconds is(scalar <$sock>, "STORED\r\n", "set item to expire"); print $sock "get should_expire\r\n"; is(scalar <$sock>, "END\r\n", "item not returned"); my $stats = mem_stats($sock); is(1, $stats->{'get_expired'}, "get_expired counter is 1"); print $sock "set should_be_flushed 0 0 6\r\nbooval\r\n"; is(scalar <$sock>, "STORED\r\n", "set item to flush"); print $sock "flush_all\r\n"; is(scalar <$sock>, "OK\r\n", "flushed"); print $sock "get should_be_flushed\r\n"; is(scalar <$sock>, "END\r\n", "flushed item not returned"); my $stats = mem_stats($sock); is($stats->{cmd_flush}, 1, "after one flush cmd_flush is 1"); is($stats->{get_flushed}, 1, "after flush and a get, get_flushed is 1"); memcached-1.5.6/t/stress-memcached.pl0000755000175000017500000000426511246331452014414 00000000000000#!/usr/bin/perl # use strict; use lib '../../api/perl/lib'; use Cache::Memcached; use Time::HiRes qw(time); unless (@ARGV == 2) { die "Usage: stress-memcached.pl ip:port threads\n"; } my $host = shift; my $threads = shift; my $memc = new Cache::Memcached; $memc->set_servers([$host]); unless ($memc->set("foo", "bar") && $memc->get("foo") eq "bar") { die "memcached not running at $host ?\n"; } $memc->disconnect_all(); my $running = 0; while (1) { if ($running < $threads) { my $cpid = fork(); if ($cpid) { $running++; #print "Launched $cpid. Running $running threads.\n"; } else { stress(); exit 0; } } else { wait(); $running--; } } sub stress { undef $memc; $memc = new Cache::Memcached; $memc->set_servers([$host]); my ($t1, $t2); my $start = sub { $t1 = time(); }; my $stop = sub { my $op = shift; $t2 = time(); my $td = sprintf("%0.3f", $t2 - $t1); if ($td > 0.25) { print "Took $td seconds for: $op\n"; } }; my $max = rand(50); my $sets = 0; for (my $i = 0; $i < $max; $i++) { my $key = key($i); my $set = $memc->set($key, $key); $sets++ if $set; } for (1..int(rand(500))) { my $rand = int(rand($max)); my $key = key($rand); my $meth = int(rand(3)); my $exp = int(rand(3)); undef $exp unless $exp; $start->(); if ($meth == 0) { $memc->add($key, $key, $exp); $stop->("add"); } elsif ($meth == 1) { $memc->delete($key); $stop->("delete"); } else { $memc->set($key, $key, $exp); $stop->("set"); } $rand = int(rand($max)); $key = key($rand); $start->(); my $v = $memc->get($key); $stop->("get"); if ($v && $v ne $key) { die "Bogus: $v for key $rand\n"; } } $start->(); my $multi = $memc->get_multi(map { key(int(rand($max))) } (1..$max)); $stop->("get_multi"); } sub key { my $n = shift; $_ = sprintf("%04d", $n); if ($n % 2) { $_ .= "a"x20; } $_; } memcached-1.5.6/t/issue_183.t0000664000175000017500000000122313160272015012522 00000000000000#!/usr/bin/perl use strict; use Test::More tests => 5; use FindBin qw($Bin); use lib "$Bin/lib"; use MemcachedTest; my $server = new_memcached("-o no_modern"); my $sock = $server->sock; print $sock "set key 0 0 1\r\n1\r\n"; is (scalar <$sock>, "STORED\r\n", "stored key"); my $s1 = mem_stats($sock); my $r1 = $s1->{"reclaimed"}; is ($r1, "0", "Objects should not be reclaimed"); sleep(2); print $sock "flush_all\r\n"; is (scalar <$sock>, "OK\r\n", "Cache flushed"); print $sock "set key 0 0 1\r\n1\r\n"; is (scalar <$sock>, "STORED\r\n", "stored key"); my $s2 = mem_stats($sock); my $r2 = $s2->{"reclaimed"}; is ($r2, "1", "Objects should be reclaimed"); memcached-1.5.6/t/multiversioning.t0000755000175000017500000000301612416643766014262 00000000000000#!/usr/bin/perl use strict; use Test::More tests => 13; use FindBin qw($Bin); use lib "$Bin/lib"; use MemcachedTest; my $server = new_memcached(); my $sock = $server->sock; my $sock2 = $server->new_sock; ok($sock != $sock2, "have two different connections open"); # set large value my $size = 256 * 1024; # 256 kB my $bigval = "0123456789abcdef" x ($size / 16); $bigval =~ s/^0/\[/; $bigval =~ s/f$/\]/; my $bigval2 = uc($bigval); print $sock "set big 0 0 $size\r\n$bigval\r\n"; is(scalar <$sock>, "STORED\r\n", "stored foo"); mem_get_is($sock, "big", $bigval, "big value got correctly"); print $sock "get big\r\n"; my $buf; is(read($sock, $buf, $size / 2), $size / 2, "read half the answer back"); like($buf, qr/VALUE big/, "buf has big value header in it"); like($buf, qr/abcdef/, "buf has some data in it"); unlike($buf, qr/abcde\]/, "buf doesn't yet close"); # sock2 interrupts (maybe sock1 is slow) and deletes stuff: print $sock2 "delete big\r\n"; is(scalar <$sock2>, "DELETED\r\n", "deleted big from sock2 while sock1's still reading it"); mem_get_is($sock2, "big", undef, "nothing from sock2 now. gone from namespace."); print $sock2 "set big 0 0 $size\r\n$bigval2\r\n"; is(scalar <$sock2>, "STORED\r\n", "stored big w/ val2"); mem_get_is($sock2, "big", $bigval2, "big value2 got correctly"); # sock1 resumes reading... $buf .= <$sock>; $buf .= <$sock>; like($buf, qr/abcde\]/, "buf now closes"); # and if sock1 reads again, it's the uppercase version: mem_get_is($sock, "big", $bigval2, "big value2 got correctly from sock1"); memcached-1.5.6/t/touch.t0000755000175000017500000000073112416643766012147 00000000000000#!/usr/bin/perl use strict; use Test::More tests => 4; use FindBin qw($Bin); use lib "$Bin/lib"; use MemcachedTest; my $server = new_memcached(); my $sock = $server->sock; # set foo (and should get it) print $sock "set foo 0 2 6\r\nfooval\r\n"; is(scalar <$sock>, "STORED\r\n", "stored foo"); mem_get_is($sock, "foo", "fooval"); # touch it print $sock "touch foo 10\r\n"; is(scalar <$sock>, "TOUCHED\r\n", "touched foo"); sleep 2; mem_get_is($sock, "foo", "fooval"); memcached-1.5.6/t/64bit.t0000755000175000017500000000216212416643766011755 00000000000000#!/usr/bin/perl use strict; use Test::More; use FindBin qw($Bin); use lib "$Bin/lib"; use MemcachedTest; $ENV{T_MEMD_INITIAL_MALLOC} = "4294967328"; # 2**32 + 32 , just over 4GB $ENV{T_MEMD_SLABS_ALLOC} = 0; # don't preallocate slabs my $server = new_memcached("-m 4098 -M"); my $sock = $server->sock; my ($stats, $slabs) = @_; $stats = mem_stats($sock); if ($stats->{'pointer_size'} eq "32") { plan skip_all => 'Skipping 64-bit tests on 32-bit build'; exit 0; } else { plan tests => 6; } is($stats->{'pointer_size'}, 64, "is 64 bit"); is($stats->{'limit_maxbytes'}, "4297064448", "max bytes is 4098 MB"); $slabs = mem_stats($sock, 'slabs'); is($slabs->{'total_malloced'}, "4294967328", "expected (faked) value of total_malloced"); is($slabs->{'active_slabs'}, 0, "no active slabs"); my $hit_limit = 0; for (1..5) { my $size = 400 * 1024; my $data = "a" x $size; print $sock "set big$_ 0 0 $size\r\n$data\r\n"; my $res = <$sock>; $hit_limit = 1 if $res ne "STORED\r\n"; } ok($hit_limit, "hit size limit"); $slabs = mem_stats($sock, 'slabs'); is($slabs->{'active_slabs'}, 1, "1 active slab"); memcached-1.5.6/t/noreply.t0000644000175000017500000000230012416643766012504 00000000000000#!/usr/bin/perl use strict; use Test::More tests => 9; use FindBin qw($Bin); use lib "$Bin/lib"; use MemcachedTest; my $server = new_memcached(); my $sock = $server->sock; # Test that commands can take 'noreply' parameter. print $sock "flush_all noreply\r\n"; print $sock "flush_all 0 noreply\r\n"; print $sock "verbosity 0 noreply\r\n"; print $sock "add noreply:foo 0 0 1 noreply\r\n1\r\n"; mem_get_is($sock, "noreply:foo", "1"); print $sock "set noreply:foo 0 0 1 noreply\r\n2\r\n"; mem_get_is($sock, "noreply:foo", "2"); print $sock "replace noreply:foo 0 0 1 noreply\r\n3\r\n"; mem_get_is($sock, "noreply:foo", "3"); print $sock "append noreply:foo 0 0 1 noreply\r\n4\r\n"; mem_get_is($sock, "noreply:foo", "34"); print $sock "prepend noreply:foo 0 0 1 noreply\r\n5\r\n"; my @result = mem_gets($sock, "noreply:foo"); ok($result[1] eq "534"); print $sock "cas noreply:foo 0 0 1 $result[0] noreply\r\n6\r\n"; mem_get_is($sock, "noreply:foo", "6"); print $sock "incr noreply:foo 3 noreply\r\n"; mem_get_is($sock, "noreply:foo", "9"); print $sock "decr noreply:foo 2 noreply\r\n"; mem_get_is($sock, "noreply:foo", "7"); print $sock "delete noreply:foo noreply\r\n"; mem_get_is($sock, "noreply:foo"); memcached-1.5.6/t/idle-timeout.t0000664000175000017500000000171513160272015013406 00000000000000#!/usr/bin/perl use strict; use warnings; use Test::More tests => 11; use FindBin qw($Bin); use lib "$Bin/lib"; use MemcachedTest; # start up a server with 10 maximum connections my $server = new_memcached("-o idle_timeout=3 -l 127.0.0.1"); my $sock = $server->sock; # Make sure we can talk to start with my $stats = mem_stats($sock); is($stats->{idle_kicks}, "0", "check stats initial"); isnt($sock->connected(), undef, "check connected"); # Make sure we don't timeout when active for (my $i = 0; $i < 6; $i++) { $stats = mem_stats($sock); isnt($stats->{version}, undef, "check active $i"); } $stats = mem_stats($sock); is($stats->{idle_kicks}, "0", "check stats 2"); # Make sure we do timeout when not sleep(5); mem_stats($sock); # Network activity, so socket code will see dead socket sleep(1); is($sock->connected(), undef, "check disconnected"); $sock = $server->sock; $stats = mem_stats($sock); isnt($stats->{idle_kicks}, 0, "check stats timeout"); memcached-1.5.6/t/line-lengths.t0000755000175000017500000000100412416643766013410 00000000000000#!/usr/bin/perl use strict; use FindBin qw($Bin); our @files; BEGIN { chdir "$Bin/.." or die; @files = ( "doc/protocol.txt" ); } use Test::More tests => scalar(@files); foreach my $f (@files) { open(my $fh, $f) or die("Can't open $f"); my @long_lines = (); my $line_number = 0; while(<$fh>) { $line_number++; if(length($_) > 80) { push(@long_lines, $line_number); } } close($fh); ok(@long_lines == 0, "$f has a long lines: @long_lines"); } memcached-1.5.6/t/binary-extstore.t0000775000175000017500000004013213240770206014147 00000000000000#!/usr/bin/perl use strict; use warnings; use Test::More; use FindBin qw($Bin); use lib "$Bin/lib"; use MemcachedTest; use Data::Dumper qw/Dumper/; my $ext_path; if (!supports_extstore()) { plan skip_all => 'extstore not enabled'; exit 0; } $ext_path = "/tmp/extstore.$$"; my $server = new_memcached("-m 64 -U 0 -o ext_page_size=8,ext_page_count=8,ext_wbuf_size=2,ext_threads=1,ext_io_depth=2,ext_item_size=512,ext_item_age=2,ext_recache_rate=10000,ext_max_frag=0.9,ext_path=$ext_path,no_lru_crawler,slab_automove=0"); ok($server, "started the server"); # Based almost 100% off testClient.py which is: # Copyright (c) 2007 Dustin Sallings # Command constants use constant CMD_GET => 0x00; use constant CMD_SET => 0x01; use constant CMD_ADD => 0x02; use constant CMD_REPLACE => 0x03; use constant CMD_DELETE => 0x04; use constant CMD_INCR => 0x05; use constant CMD_DECR => 0x06; use constant CMD_QUIT => 0x07; use constant CMD_FLUSH => 0x08; use constant CMD_GETQ => 0x09; use constant CMD_NOOP => 0x0A; use constant CMD_VERSION => 0x0B; use constant CMD_GETK => 0x0C; use constant CMD_GETKQ => 0x0D; use constant CMD_APPEND => 0x0E; use constant CMD_PREPEND => 0x0F; use constant CMD_STAT => 0x10; use constant CMD_SETQ => 0x11; use constant CMD_ADDQ => 0x12; use constant CMD_REPLACEQ => 0x13; use constant CMD_DELETEQ => 0x14; use constant CMD_INCREMENTQ => 0x15; use constant CMD_DECREMENTQ => 0x16; use constant CMD_QUITQ => 0x17; use constant CMD_FLUSHQ => 0x18; use constant CMD_APPENDQ => 0x19; use constant CMD_PREPENDQ => 0x1A; use constant CMD_TOUCH => 0x1C; use constant CMD_GAT => 0x1D; use constant CMD_GATQ => 0x1E; use constant CMD_GATK => 0x23; use constant CMD_GATKQ => 0x24; # REQ and RES formats are divided even though they currently share # the same format, since they _could_ differ in the future. use constant REQ_PKT_FMT => "CCnCCnNNNN"; use constant RES_PKT_FMT => "CCnCCnNNNN"; use constant INCRDECR_PKT_FMT => "NNNNN"; use constant MIN_RECV_BYTES => length(pack(RES_PKT_FMT)); use constant REQ_MAGIC => 0x80; use constant RES_MAGIC => 0x81; my $mc = MC::Client->new; my $check = sub { my ($key, $orig_flags, $orig_val) = @_; my ($flags, $val, $cas) = $mc->get($key); is($flags, $orig_flags, "Flags is set properly"); ok($val eq $orig_val || $val == $orig_val, $val . " = " . $orig_val); }; my $set = sub { my ($key, $exp, $orig_flags, $orig_value) = @_; $mc->set($key, $orig_value, $orig_flags, $exp); $check->($key, $orig_flags, $orig_value); }; my $empty = sub { my $key = shift; my $rv =()= eval { $mc->get($key) }; is($rv, 0, "Didn't get a result from get"); ok($@->not_found, "We got a not found error when we expected one"); }; my $delete = sub { my ($key, $when) = @_; $mc->delete($key, $when); $empty->($key); }; my $value; my $bigvalue; { my @chars = ("C".."Z"); for (1 .. 20000) { $value .= $chars[rand @chars]; } for (1 .. 800000) { $bigvalue .= $chars[rand @chars]; } } # diag "small object"; $set->('x', 10, 19, "somevalue"); # check extstore counters { my %stats = $mc->stats(''); is($stats{extstore_objects_written}, 0); } # diag "Delete"; #$delete->('x'); # diag "Flush"; #$empty->('y'); # fill some larger objects { my $keycount = 1000; for (1 .. $keycount) { $set->("nfoo$_", 0, 19, $value); } # wait for a flush sleep 4; # value returns for one flushed object. $check->('nfoo1', 19, $value); # check extstore counters my %stats = $mc->stats(''); cmp_ok($stats{extstore_page_allocs}, '>', 0, 'at least one page allocated'); cmp_ok($stats{extstore_objects_written}, '>', $keycount / 2, 'some objects written'); cmp_ok($stats{extstore_bytes_written}, '>', length($value) * 2, 'some bytes written'); cmp_ok($stats{get_extstore}, '>', 0, 'one object was fetched'); cmp_ok($stats{extstore_objects_read}, '>', 0, 'one object read'); cmp_ok($stats{extstore_bytes_read}, '>', length($value), 'some bytes read'); # Test multiget my $rv = $mc->get_multi(qw(nfoo2 nfoo3 noexist)); is($rv->{nfoo2}->[1], $value, 'multiget nfoo2'); is($rv->{nfoo3}->[1], $value, 'multiget nfoo2'); # Remove half of the keys for the next test. for (1 .. $keycount) { next unless $_ % 2 == 0; $delete->("nfoo$_"); } my %stats2 = $mc->stats(''); cmp_ok($stats{extstore_bytes_used}, '>', $stats2{extstore_bytes_used}, 'bytes used dropped after deletions'); cmp_ok($stats{extstore_objects_used}, '>', $stats2{extstore_objects_used}, 'objects used dropped after deletions'); is($stats2{badcrc_from_extstore}, 0, 'CRC checks successful'); # delete the rest for (1 .. $keycount) { next unless $_ % 2 == 1; $delete->("nfoo$_"); } } # check evictions and misses { my $keycount = 1000; for (1 .. $keycount) { $set->("mfoo$_", 0, 19, $value); } sleep 4; for ($keycount .. ($keycount*3)) { $set->("mfoo$_", 0, 19, $value); } sleep 4; $empty->('mfoo1'); my %s = $mc->stats(''); cmp_ok($s{extstore_objects_evicted}, '>', 0); cmp_ok($s{miss_from_extstore}, '>', 0); } # store and re-fetch a chunked value { my %stats = $mc->stats(''); $set->("bigvalue", 0, 0, $bigvalue); sleep 4; $check->("bigvalue", 0, $bigvalue); my %stats2 = $mc->stats(''); cmp_ok($stats2{extstore_objects_written}, '>', $stats{extstore_objects_written}, "a large value flushed"); } # ensure ASCII can still fetch the chunked value. { my $ns = $server->new_sock; my %s1 = $mc->stats(''); mem_get_is($ns, "bigvalue", $bigvalue); print $ns "extstore recache_rate 1\r\n"; is(scalar <$ns>, "OK\r\n", "recache rate upped"); for (1..3) { mem_get_is($ns, "bigvalue", $bigvalue); $check->('bigvalue', 0, $bigvalue); } my %s2 = $mc->stats(''); cmp_ok($s2{recache_from_extstore}, '>', $s1{recache_from_extstore}, 'a new recache happened'); } done_testing(); END { unlink $ext_path if $ext_path; } # ###################################################################### # Test ends around here. # ###################################################################### package MC::Client; use strict; use warnings; use fields qw(socket); use IO::Socket::INET; sub new { my $self = shift; my ($s) = @_; $s = $server unless defined $s; my $sock = $s->sock; $self = fields::new($self); $self->{socket} = $sock; return $self; } sub build_command { my $self = shift; die "Not enough args to send_command" unless @_ >= 4; my ($cmd, $key, $val, $opaque, $extra_header, $cas) = @_; $extra_header = '' unless defined $extra_header; my $keylen = length($key); my $vallen = length($val); my $extralen = length($extra_header); my $datatype = 0; # field for future use my $reserved = 0; # field for future use my $totallen = $keylen + $vallen + $extralen; my $ident_hi = 0; my $ident_lo = 0; if ($cas) { $ident_hi = int($cas / 2 ** 32); $ident_lo = int($cas % 2 ** 32); } my $msg = pack(::REQ_PKT_FMT, ::REQ_MAGIC, $cmd, $keylen, $extralen, $datatype, $reserved, $totallen, $opaque, $ident_hi, $ident_lo); my $full_msg = $msg . $extra_header . $key . $val; return $full_msg; } sub send_command { my $self = shift; die "Not enough args to send_command" unless @_ >= 4; my ($cmd, $key, $val, $opaque, $extra_header, $cas) = @_; my $full_msg = $self->build_command($cmd, $key, $val, $opaque, $extra_header, $cas); my $sent = $self->{socket}->send($full_msg); die("Send failed: $!") unless $sent; if($sent != length($full_msg)) { die("only sent $sent of " . length($full_msg) . " bytes"); } } sub flush_socket { my $self = shift; $self->{socket}->flush; } # Send a silent command and ensure it doesn't respond. sub send_silent { my $self = shift; die "Not enough args to send_silent" unless @_ >= 4; my ($cmd, $key, $val, $opaque, $extra_header, $cas) = @_; $self->send_command($cmd, $key, $val, $opaque, $extra_header, $cas); $self->send_command(::CMD_NOOP, '', '', $opaque + 1); my ($ropaque, $data) = $self->_handle_single_response; Test::More::is($ropaque, $opaque + 1); } sub silent_mutation { my $self = shift; my ($cmd, $key, $value) = @_; $empty->($key); my $extra = pack "NN", 82, 0; $mc->send_silent($cmd, $key, $value, 7278552, $extra, 0); $check->($key, 82, $value); } sub _handle_single_response { my $self = shift; my $myopaque = shift; my $hdr = ""; while(::MIN_RECV_BYTES - length($hdr) > 0) { $self->{socket}->recv(my $response, ::MIN_RECV_BYTES - length($hdr)); $hdr .= $response; } Test::More::is(length($hdr), ::MIN_RECV_BYTES, "Expected read length"); my ($magic, $cmd, $keylen, $extralen, $datatype, $status, $remaining, $opaque, $ident_hi, $ident_lo) = unpack(::RES_PKT_FMT, $hdr); Test::More::is($magic, ::RES_MAGIC, "Got proper response magic"); my $cas = ($ident_hi * 2 ** 32) + $ident_lo; return ($opaque, '', $cas, 0) if($remaining == 0); # fetch the value my $rv=""; while($remaining - length($rv) > 0) { $self->{socket}->recv(my $buf, $remaining - length($rv)); $rv .= $buf; } if(length($rv) != $remaining) { my $found = length($rv); die("Expected $remaining bytes, got $found"); } if (defined $myopaque) { Test::More::is($opaque, $myopaque, "Expected opaque"); } else { Test::More::pass("Implicit pass since myopaque is undefined"); } if ($status) { die MC::Error->new($status, $rv); } return ($opaque, $rv, $cas, $keylen); } sub _do_command { my $self = shift; die unless @_ >= 3; my ($cmd, $key, $val, $extra_header, $cas) = @_; $extra_header = '' unless defined $extra_header; my $opaque = int(rand(2**32)); $self->send_command($cmd, $key, $val, $opaque, $extra_header, $cas); my (undef, $rv, $rcas) = $self->_handle_single_response($opaque); return ($rv, $rcas); } sub _incrdecr_header { my $self = shift; my ($amt, $init, $exp) = @_; my $amt_hi = int($amt / 2 ** 32); my $amt_lo = int($amt % 2 ** 32); my $init_hi = int($init / 2 ** 32); my $init_lo = int($init % 2 ** 32); my $extra_header = pack(::INCRDECR_PKT_FMT, $amt_hi, $amt_lo, $init_hi, $init_lo, $exp); return $extra_header; } sub _incrdecr_cas { my $self = shift; my ($cmd, $key, $amt, $init, $exp) = @_; my ($data, $rcas) = $self->_do_command($cmd, $key, '', $self->_incrdecr_header($amt, $init, $exp)); my $header = substr $data, 0, 8, ''; my ($resp_hi, $resp_lo) = unpack "NN", $header; my $resp = ($resp_hi * 2 ** 32) + $resp_lo; return $resp, $rcas; } sub _incrdecr { my $self = shift; my ($v, $c) = $self->_incrdecr_cas(@_); return $v } sub silent_incrdecr { my $self = shift; my ($cmd, $key, $amt, $init, $exp) = @_; my $opaque = 8275753; $mc->send_silent($cmd, $key, '', $opaque, $mc->_incrdecr_header($amt, $init, $exp)); } sub stats { my $self = shift; my $key = shift; my $cas = 0; my $opaque = int(rand(2**32)); $self->send_command(::CMD_STAT, $key, '', $opaque, '', $cas); my %rv = (); my $found_key = ''; my $found_val = ''; do { my ($op, $data, $cas, $keylen) = $self->_handle_single_response($opaque); if($keylen > 0) { $found_key = substr($data, 0, $keylen); $found_val = substr($data, $keylen); $rv{$found_key} = $found_val; } else { $found_key = ''; } } while($found_key ne ''); return %rv; } sub get { my $self = shift; my $key = shift; my ($rv, $cas) = $self->_do_command(::CMD_GET, $key, '', ''); my $header = substr $rv, 0, 4, ''; my $flags = unpack("N", $header); return ($flags, $rv, $cas); } sub get_multi { my $self = shift; my @keys = @_; for (my $i = 0; $i < @keys; $i++) { $self->send_command(::CMD_GETQ, $keys[$i], '', $i, '', 0); } my $terminal = @keys + 10; $self->send_command(::CMD_NOOP, '', '', $terminal); my %return; while (1) { my ($opaque, $data) = $self->_handle_single_response; last if $opaque == $terminal; my $header = substr $data, 0, 4, ''; my $flags = unpack("N", $header); $return{$keys[$opaque]} = [$flags, $data]; } return %return if wantarray; return \%return; } sub touch { my $self = shift; my ($key, $expire) = @_; my $extra_header = pack "N", $expire; my $cas = 0; return $self->_do_command(::CMD_TOUCH, $key, '', $extra_header, $cas); } sub gat { my $self = shift; my $key = shift; my $expire = shift; my $extra_header = pack "N", $expire; my ($rv, $cas) = $self->_do_command(::CMD_GAT, $key, '', $extra_header); my $header = substr $rv, 0, 4, ''; my $flags = unpack("N", $header); return ($flags, $rv, $cas); } sub version { my $self = shift; return $self->_do_command(::CMD_VERSION, '', ''); } sub flush { my $self = shift; return $self->_do_command(::CMD_FLUSH, '', ''); } sub add { my $self = shift; my ($key, $val, $flags, $expire) = @_; my $extra_header = pack "NN", $flags, $expire; my $cas = 0; return $self->_do_command(::CMD_ADD, $key, $val, $extra_header, $cas); } sub set { my $self = shift; my ($key, $val, $flags, $expire, $cas) = @_; my $extra_header = pack "NN", $flags, $expire; return $self->_do_command(::CMD_SET, $key, $val, $extra_header, $cas); } sub _append_prepend { my $self = shift; my ($cmd, $key, $val, $cas) = @_; return $self->_do_command($cmd, $key, $val, '', $cas); } sub replace { my $self = shift; my ($key, $val, $flags, $expire) = @_; my $extra_header = pack "NN", $flags, $expire; my $cas = 0; return $self->_do_command(::CMD_REPLACE, $key, $val, $extra_header, $cas); } sub delete { my $self = shift; my ($key) = @_; return $self->_do_command(::CMD_DELETE, $key, ''); } sub incr { my $self = shift; my ($key, $amt, $init, $exp) = @_; $amt = 1 unless defined $amt; $init = 0 unless defined $init; $exp = 0 unless defined $exp; return $self->_incrdecr(::CMD_INCR, $key, $amt, $init, $exp); } sub incr_cas { my $self = shift; my ($key, $amt, $init, $exp) = @_; $amt = 1 unless defined $amt; $init = 0 unless defined $init; $exp = 0 unless defined $exp; return $self->_incrdecr_cas(::CMD_INCR, $key, $amt, $init, $exp); } sub decr { my $self = shift; my ($key, $amt, $init, $exp) = @_; $amt = 1 unless defined $amt; $init = 0 unless defined $init; $exp = 0 unless defined $exp; return $self->_incrdecr(::CMD_DECR, $key, $amt, $init, $exp); } sub noop { my $self = shift; return $self->_do_command(::CMD_NOOP, '', ''); } package MC::Error; use strict; use warnings; use constant ERR_UNKNOWN_CMD => 0x81; use constant ERR_NOT_FOUND => 0x1; use constant ERR_EXISTS => 0x2; use constant ERR_TOO_BIG => 0x3; use constant ERR_EINVAL => 0x4; use constant ERR_NOT_STORED => 0x5; use constant ERR_DELTA_BADVAL => 0x6; use overload '""' => sub { my $self = shift; return "Memcache Error ($self->[0]): $self->[1]"; }; sub new { my $class = shift; my $error = [@_]; my $self = bless $error, (ref $class || $class); return $self; } sub not_found { my $self = shift; return $self->[0] == ERR_NOT_FOUND; } sub exists { my $self = shift; return $self->[0] == ERR_EXISTS; } sub too_big { my $self = shift; return $self->[0] == ERR_TOO_BIG; } sub delta_badval { my $self = shift; return $self->[0] == ERR_DELTA_BADVAL; } sub einval { my $self = shift; return $self->[0] == ERR_EINVAL; } # vim: filetype=perl memcached-1.5.6/t/issue_67.t0000664000175000017500000000474613245327530012467 00000000000000#!/usr/bin/perl use strict; use Test::More tests => 22; use FindBin qw($Bin); use lib "$Bin/lib"; use MemcachedTest; use Carp qw(croak); use Cwd; my $builddir = getcwd; $ENV{'MEMCACHED_PORT_FILENAME'} = "/tmp/ports.$$"; sub read_ports { my %rv = (); open(my $f, "/tmp/ports.$$") || die("Can't open ports file."); while(<$f>) { my ($type, $port) = split(/:\s+/); $rv{$type} = $port + 0; } unlink "/tmp/ports.$$"; return %rv; } sub validate_port { my ($name, $got, $expected) = @_; # diag "Wanted $expected, got $got"; if ($expected == -1) { ok(!defined($got), "$name expected no port, got $got"); } elsif ($expected == 0) { ok($got != 11211, "$name expected random port (got $got)"); } else { is($got, $expected, "$name"); } } sub run_server { my ($args) = @_; my $exe = "$builddir/memcached-debug"; croak("memcached binary doesn't exist. Haven't run 'make' ?\n") unless -e $exe; my $childpid = fork(); my $root = ''; $root = "-u root" if ($< == 0); # test build requires more privileges $args .= " -o relaxed_privileges"; my $cmd = "$builddir/timedrun 10 $exe $root $args"; unless($childpid) { exec $cmd; exit; # NOTREACHED } for (1..20) { if (-f "/tmp/ports.$$") { return Memcached::Handle->new(pid => $childpid); } select undef, undef, undef, 0.10; } croak "Failed to start server."; } sub when { my ($name, $params, $expected_tcp, $expected_udp) = @_; my $server = run_server($params); my %ports = read_ports(); validate_port($name, $ports{'TCP INET'}, $expected_tcp); validate_port($name, $ports{'UDP INET'}, $expected_udp); } # Disabling the defaults since it conflicts with a running instance. # when('no arguments', '', 11211, 11211); when('specifying tcp port', '-p 11212', 11212, -1); when('specifying udp port', '-U 11222', 11222, 11222); when('specifying tcp ephemeral port', '-p -1', 0, 0); when('specifying udp ephemeral port', '-U -1', 0, 0); when('tcp port disabled', '-p 0', -1, -1); when('udp port disabled', '-U 0', 11211, -1); when('specifying tcp and udp ports', '-p 11232 -U 11233', 11232, 11233); when('specifying tcp and disabling udp', '-p 11242 -U 0', 11242, -1); when('specifying udp and disabling tcp', '-p -1 -U 11252', 0, 11252); when('specifying tcp and ephemeral udp', '-p 11262 -U -1', 11262, 0); when('specifying udp and ephemeral tcp', '-p -1 -U 11272', 0, 11272); memcached-1.5.6/t/dyn-maxbytes.t0000664000175000017500000000504713150607001013427 00000000000000#!/usr/bin/perl # Test the 'stats items' evictions counters. use strict; use Test::More tests => 309; use FindBin qw($Bin); use lib "$Bin/lib"; use MemcachedTest; my $server = new_memcached("-m 3 -o modern,slab_automove_window=3"); my $sock = $server->sock; my $value = "B"x66560; my $key = 0; # These aren't set to expire. for ($key = 0; $key < 40; $key++) { print $sock "set key$key 0 0 66560\r\n$value\r\n"; is(scalar <$sock>, "STORED\r\n", "stored key$key"); } my $stats = mem_stats($sock); my $evicted = $stats->{evictions}; isnt($evicted, "0", "check evicted"); # We're past the memory limit. Try adjusting maxbytes upward. $stats = mem_stats($sock, "settings"); my $pre_maxbytes = $stats->{"maxbytes"}; print $sock "cache_memlimit 8\r\n"; is(scalar <$sock>, "OK\r\n", "bumped maxbytes from 3m to 8m"); # Confirm maxbytes updated. $stats = mem_stats($sock, "settings"); isnt($stats->{"maxbytes"}, $pre_maxbytes, "stats settings maxbytes updated"); # Check for total_malloced increasing as new memory is added $stats = mem_stats($sock, "slabs"); my $t_malloc = $stats->{"total_malloced"}; print $sock "set toast 0 0 66560\r\n$value\r\n"; is(scalar <$sock>, "STORED\r\n", "stored toast"); $stats = mem_stats($sock, "slabs"); cmp_ok($stats->{"total_malloced"}, '>', $t_malloc, "stats slabs total_malloced increased"); $stats = mem_stats($sock); my $new_evicted = $stats->{evictions}; cmp_ok($new_evicted, '==', $evicted, "no new evictions"); # Bump up to 16, fill a bit more, then delete everything. print $sock "cache_memlimit 16\r\n"; is(scalar <$sock>, "OK\r\n", "bumped maxbytes from 8m to 16m"); for (;$key < 150; $key++) { print $sock "set key$key 0 0 66560\r\n$value\r\n"; is(scalar <$sock>, "STORED\r\n", "stored key$key"); } # Grab total_malloced after filling everything up. $stats = mem_stats($sock, "slabs"); $t_malloc = $stats->{"total_malloced"}; print $sock "cache_memlimit 8\r\n"; is(scalar <$sock>, "OK\r\n", "bumped maxbytes from 16m to 8m"); # Remove all of the keys, allowing the slab rebalancer to push pages toward # the global page pool. for ($key = 0; $key < 150; $key++) { print $sock "delete key$key\r\n"; like(scalar <$sock>, qr/(DELETED|NOT_FOUND)\r\n/, "deleted key$key"); } # If memory limit is lower, it should free those pages back to the OS. my $reduced = 0; for (my $tries = 0; $tries < 6; $tries++) { sleep 1; $stats = mem_stats($sock, "slabs"); $reduced = $stats->{"total_malloced"} if ($t_malloc > $stats->{"total_malloced"}); last if $reduced; } isnt($reduced, 0, "total_malloced reduced to $reduced"); memcached-1.5.6/t/refhang.t0000664000175000017500000000346613150607001012420 00000000000000#!/usr/bin/perl use strict; use warnings; use Test::More; use FindBin qw($Bin); use lib "$Bin/lib"; use MemcachedTest; plan skip_all => 'Test is flaky. Needs special hooks.'; plan tests => 127; # start up a server with 10 maximum connections my $server = new_memcached("-m 6"); my $sock = $server->sock; my $hangsock = $server->new_sock; my $hangsock2 = $server->new_sock; my $value = "B"x66560; my $key = 0; # These aren't set to expire. my $mget = ''; for ($key = 0; $key < 120; $key++) { $mget .= "key$key " if $key < 115; print $sock "set key$key 0 0 66560\r\n$value\r\n"; is(scalar <$sock>, "STORED\r\n", "stored key$key"); } chop $mget; my $stats = mem_stats($sock, "items"); isnt($stats->{"items:31:evicted"}, "0", "check evicted"); my $lrutail_reflocked = $stats->{"items:31:lrutail_reflocked"}; is($lrutail_reflocked, "0", "check no slab lrutail_reflocked"); $stats = mem_stats($sock); is($stats->{"lrutail_reflocked"}, "0", "check no total lrutail_reflocked"); # Don't intend to read the results, need to fill the socket. # TODO: This test would be smarter if we cranked down the socket buffers # first? Or perhaps used a unix domain socket. print $hangsock "get $mget\r\n"; #sleep 8; # Now we try a bunch of sets again, and see if they start coming back as OOM's for ($key = 121; $key < 240; $key++) { print $sock "set key$key 0 0 66560\r\n$value\r\n"; my $res = scalar <$sock>; } $stats = mem_stats($sock, "items"); # Some items will OOM since we only clear a handful per alloc attempt. ok($stats->{"items:31:outofmemory"} > 0, "some ooms happened"); ok($stats->{"items:31:outofmemory"} < 20, "fewer than 20 ooms"); isnt($stats->{"items:31:lrutail_reflocked"}, "0", "nonzero lrutail_reflocked"); $stats = mem_stats($sock); isnt($stats->{"lrutail_reflocked"}, "0", "nonzero total lrutail_reflocked"); memcached-1.5.6/t/issue_70.t0000664000175000017500000000114413204422154012437 00000000000000#!/usr/bin/perl use strict; use Test::More tests => 4; use FindBin qw($Bin); use lib "$Bin/lib"; use MemcachedTest; my $server = new_memcached(); my $sock = $server->sock; print $sock "set issue70 0 0 0\r\n\r\n"; is (scalar <$sock>, "STORED\r\n", "stored issue70"); print $sock "set issue70 0 0 -1\r\n"; is (scalar <$sock>, "CLIENT_ERROR bad command line format\r\n"); print $sock "set issue70 0 0 4294967295\r\n"; is (scalar <$sock>, "CLIENT_ERROR bad command line format\r\n"); print $sock "set issue70 0 0 2147483647\r\nscoobyscoobydoo"; is (scalar <$sock>, "CLIENT_ERROR bad command line format\r\n"); memcached-1.5.6/t/getset.t0000775000175000017500000000617413150607001012303 00000000000000#!/usr/bin/perl use strict; use Test::More tests => 539; use FindBin qw($Bin); use lib "$Bin/lib"; use MemcachedTest; my $server = new_memcached(); my $sock = $server->sock; # set foo (and should get it) print $sock "set foo 0 0 6\r\nfooval\r\n"; is(scalar <$sock>, "STORED\r\n", "stored foo"); mem_get_is($sock, "foo", "fooval"); # add bar (and should get it) print $sock "add bar 0 0 6\r\nbarval\r\n"; is(scalar <$sock>, "STORED\r\n", "stored barval"); mem_get_is($sock, "bar", "barval"); # add foo (but shouldn't get new value) print $sock "add foo 0 0 5\r\nfoov2\r\n"; is(scalar <$sock>, "NOT_STORED\r\n", "not stored"); mem_get_is($sock, "foo", "fooval"); # replace bar (should work) print $sock "replace bar 0 0 6\r\nbarva2\r\n"; is(scalar <$sock>, "STORED\r\n", "replaced barval 2"); # replace notexist (shouldn't work) print $sock "replace notexist 0 0 6\r\nbarva2\r\n"; is(scalar <$sock>, "NOT_STORED\r\n", "didn't replace notexist"); # delete foo. print $sock "delete foo\r\n"; is(scalar <$sock>, "DELETED\r\n", "deleted foo"); # delete foo again. not found this time. print $sock "delete foo\r\n"; is(scalar <$sock>, "NOT_FOUND\r\n", "deleted foo, but not found"); # add moo # print $sock "add moo 0 0 6\r\nmooval\r\n"; is(scalar <$sock>, "STORED\r\n", "stored barval"); mem_get_is($sock, "moo", "mooval"); # check-and-set (cas) failure case, try to set value with incorrect cas unique val print $sock "cas moo 0 0 6 0\r\nMOOVAL\r\n"; is(scalar <$sock>, "EXISTS\r\n", "check and set with invalid id"); # test "gets", grab unique ID print $sock "gets moo\r\n"; # VALUE moo 0 6 3084947704 # my @retvals = split(/ /, scalar <$sock>); my $data = scalar <$sock>; # grab data my $dot = scalar <$sock>; # grab dot on line by itself is($retvals[0], "VALUE", "get value using 'gets'"); my $unique_id = $retvals[4]; # clean off \r\n $unique_id =~ s/\r\n$//; ok($unique_id =~ /^\d+$/, "unique ID '$unique_id' is an integer"); # now test that we can store moo with the correct unique id print $sock "cas moo 0 0 6 $unique_id\r\nMOOVAL\r\n"; is(scalar <$sock>, "STORED\r\n"); mem_get_is($sock, "moo", "MOOVAL"); # pipeline is okay print $sock "set foo 0 0 6\r\nfooval\r\ndelete foo\r\nset foo 0 0 6\r\nfooval\r\ndelete foo\r\n"; is(scalar <$sock>, "STORED\r\n", "pipeline set"); is(scalar <$sock>, "DELETED\r\n", "pipeline delete"); is(scalar <$sock>, "STORED\r\n", "pipeline set"); is(scalar <$sock>, "DELETED\r\n", "pipeline delete"); # Test sets up to a large size around 1MB. # Everything up to 1MB - 1k should succeed, everything 1MB +1k should fail. my $len = 1024; while ($len < 1024*1028) { my $val = "B"x$len; if ($len > (1024*1024)) { # Ensure causing a memory overflow doesn't leave stale data. print $sock "set foo_$len 0 0 3\r\nMOO\r\n"; is(scalar <$sock>, "STORED\r\n"); print $sock "set foo_$len 0 0 $len\r\n$val\r\n"; is(scalar <$sock>, "SERVER_ERROR object too large for cache\r\n", "failed to store size $len"); mem_get_is($sock, "foo_$len"); } else { print $sock "set foo_$len 0 0 $len\r\n$val\r\n"; is(scalar <$sock>, "STORED\r\n", "stored size $len"); } $len += 2048; } memcached-1.5.6/t/misbehave.t0000664000175000017500000000057213160272015012750 00000000000000#!/usr/bin/perl use strict; use Test::More; use FindBin qw($Bin); use lib "$Bin/lib"; use MemcachedTest; if (supports_drop_priv()) { plan tests => 1; } else { plan skip_all => 'Privilege drop not supported'; exit 0; } my $server = new_memcached(); my $sock = $server->sock; print $sock "misbehave\r\n"; is(scalar <$sock>, "OK\r\n", "did not allow misbehaving"); memcached-1.5.6/t/item_size_max.t0000664000175000017500000000236213115057711013645 00000000000000#!/usr/bin/perl use strict; use Test::More tests => 7; use FindBin qw($Bin); use lib "$Bin/lib"; use MemcachedTest; my $server = new_memcached(); my $sock = $server->sock; my $stats = mem_stats($sock, ' settings'); # Ensure default still works. is($stats->{item_size_max}, 1024 * 1024); $server->stop(); # Should die. eval { $server = new_memcached('-I 1000'); }; ok($@ && $@ =~ m/^Failed/, "Shouldn't start with < 1k item max"); eval { $server = new_memcached('-I 256m'); }; ok($@ && $@ =~ m/^Failed/, "Shouldn't start with > 128m item max"); # Minimum. $server = new_memcached('-I 1024 -o slab_chunk_max=1024'); my $stats = mem_stats($server->sock, ' settings'); is($stats->{item_size_max}, 1024); $server->stop(); # Reasonable but unreasonable. $server = new_memcached('-I 2097152'); my $stats = mem_stats($server->sock, ' settings'); is($stats->{item_size_max}, 2097152); $server->stop(); # Suffix kilobytes. $server = new_memcached('-I 512k -o slab_chunk_max=16384'); my $stats = mem_stats($server->sock, ' settings'); is($stats->{item_size_max}, 524288); $server->stop(); # Suffix megabytes. $server = new_memcached('-m 256 -I 32m'); my $stats = mem_stats($server->sock, ' settings'); is($stats->{item_size_max}, 33554432); $server->stop(); memcached-1.5.6/t/issue_14.t0000644000175000017500000000144612416643766012462 00000000000000#!/usr/bin/perl use strict; use Test::More tests => 21; use FindBin qw($Bin); use lib "$Bin/lib"; use MemcachedTest; my $server = new_memcached(); my $sock = $server->sock; my $value = "B"x66560; my $key = 0; for ($key = 0; $key < 10; $key++) { print $sock "set key$key 0 2 66560\r\n$value\r\n"; is (scalar <$sock>, "STORED\r\n", "stored key$key"); } #print $sock "stats slabs" my $first_stats = mem_stats($sock, "slabs"); my $first_malloc = $first_stats->{total_malloced}; sleep(4); for ($key = 10; $key < 20; $key++) { print $sock "set key$key 0 2 66560\r\n$value\r\n"; is (scalar <$sock>, "STORED\r\n", "stored key$key"); } my $second_stats = mem_stats($sock, "slabs"); my $second_malloc = $second_stats->{total_malloced}; is ($second_malloc, $first_malloc, "Memory grows..") memcached-1.5.6/t/issue_108.t0000644000175000017500000000125712416643766012546 00000000000000#!/usr/bin/perl use strict; use Test::More tests => 4; use FindBin qw($Bin); use lib "$Bin/lib"; use MemcachedTest; my $server = new_memcached(); my $sock = $server->sock; my $key = "del_key"; print $sock "add $key 0 0 1\r\nx\r\n"; is (scalar <$sock>, "STORED\r\n", "Added a key"); print $sock "delete $key 0\r\n"; is (scalar <$sock>, "DELETED\r\n", "Properly deleted with 0"); print $sock "add $key 0 0 1\r\nx\r\n"; is (scalar <$sock>, "STORED\r\n", "Added again a key"); print $sock "delete $key 0 noreply\r\n"; # will not reply, but a subsequent add will succeed print $sock "add $key 0 0 1\r\nx\r\n"; is (scalar <$sock>, "STORED\r\n", "Add succeeded after quiet deletion."); memcached-1.5.6/t/quit.t0000664000175000017500000000112113160272015011756 00000000000000#!/usr/bin/perl use strict; use Test::More tests => 1; use FindBin qw($Bin); use lib "$Bin/lib"; use MemcachedTest; my $server = new_memcached(); my $sock = $server->sock; { print $sock "quit\r\n"; # Five seconds ought to be enough to get hung up on. my $oldalarmt = alarm(5); # Verify we can't read anything. my $bytesread = -1; eval { local $SIG{'ALRM'} = sub { die "timeout" }; my $data = ""; $bytesread = sysread($sock, $data, 24), }; is($bytesread, 0, "Read after quit."); # Restore signal stuff. alarm($oldalarmt); } memcached-1.5.6/t/slabhang.t0000664000175000017500000000337113150607001012560 00000000000000#!/usr/bin/perl use strict; use warnings; use Test::More; use FindBin qw($Bin); use lib "$Bin/lib"; use MemcachedTest; plan skip_all => 'Test is flaky. Needs special hooks.'; plan tests => 74; # start up a server with 10 maximum connections my $server = new_memcached("-m 16 -o modern"); my $sock = $server->sock; my $hangsock = $server->new_sock; my $value = "B"x260144; my $key = 0; # disable the normal automover. print $sock "slabs automove 0\r\n"; is(scalar <$sock>, "OK\r\n", "automover disabled"); # These aren't set to expire. my $mget = ''; for ($key = 0; $key < 70; $key++) { $mget .= "key$key "; print $sock "set key$key 0 0 260144\r\n$value\r\n"; is(scalar <$sock>, "STORED\r\n", "stored key$key"); } chop $mget; # Don't intend to read the results, need to fill the socket. print $hangsock "get $mget\r\n"; #sleep 8; my $stats = mem_stats($sock, "slabs"); my $source = 0; for my $key (keys %$stats) { if ($key =~ m/^(\d+):total_pages/) { my $sid = $1; if ($stats->{$key} > 10) { $source = $sid; last; } } } isnt($source, 0, "found the source slab: $source"); my $busy; my $tomove = 4; my $reassign = "slabs reassign $source 1\r\n"; while ($tomove) { $busy = 0; print $sock $reassign; my $res = scalar <$sock>; while ($res =~ m/^BUSY/) { if ($hangsock && $busy > 5) { # unjam the pipeline $hangsock->close; } last if ($busy > 10); sleep 1; $busy++; print $sock $reassign; $res = scalar <$sock>; } last if ($busy > 10); $tomove--; } ok($busy <= 10, "didn't time out moving pages"); $stats = mem_stats($sock); isnt($stats->{"slab_reassign_busy_deletes"}, "0", "deleted some busy items"); memcached-1.5.6/t/sasl/0000755000175000017500000000000013110467160011636 500000000000000memcached-1.5.6/t/sasl/memcached.conf0000644000175000017500000000011711446413300014327 00000000000000mech_list: plain cram-md5 log_level: 5 sasldb_path: /tmp/test-memcached.sasldb memcached-1.5.6/t/malicious-commands.t0000664000175000017500000000146413160272015014572 00000000000000#!/usr/bin/perl # These command strings are always expected to be malicious and as such we # should just hang up on them. use strict; use Test::More tests => 3; use FindBin qw($Bin); use lib "$Bin/lib"; use MemcachedTest; my @strs = ( "GET / HTTP/1.0", "PUT /asdf/asd/fasdfasdf/sadf HTTP/1.1", "DELETE HTTP/1.1" ); for my $str (@strs) { my $server = new_memcached(); my $sock = $server->sock; print $sock "$str\r\n"; # Five seconds ought to be enough to get hung up on. my $oldalarmt = alarm(5); # Verify we can't read anything. my $bytesread = -1; eval { local $SIG{'ALRM'} = sub { die "timeout" }; my $data = ""; $bytesread = sysread($sock, $data, 24), }; is($bytesread, 0, $str); # Restore signal stuff. alarm($oldalarmt); } memcached-1.5.6/t/chunked-extstore.t0000664000175000017500000001113313240770206014300 00000000000000#!/usr/bin/perl # Networked logging tests. use strict; use warnings; use Test::More; use FindBin qw($Bin); use lib "$Bin/lib"; use MemcachedTest; my $ext_path; if (!supports_extstore()) { plan skip_all => 'extstore not enabled'; exit 0; } $ext_path = "/tmp/extstore.$$"; my $server = new_memcached("-m 64 -U 0 -o ext_page_size=8,ext_page_count=8,ext_wbuf_size=2,ext_threads=1,ext_io_depth=2,ext_item_size=512,ext_item_age=2,ext_recache_rate=10000,ext_max_frag=0.9,ext_path=$ext_path,slab_chunk_max=16384,slab_automove=0"); my $sock = $server->sock; # We're testing to ensure item chaining doesn't corrupt or poorly overlap # data, so create a non-repeating pattern. my @parts = (); for (1 .. 8000) { push(@parts, $_); } my $pattern = join(':', @parts); my $plen = length($pattern); print $sock "set pattern 0 0 $plen\r\n$pattern\r\n"; is(scalar <$sock>, "STORED\r\n", "stored pattern successfully"); # Stash two more for later test print $sock "set pattern2 0 0 $plen noreply\r\n$pattern\r\n"; print $sock "set pattern3 0 0 $plen noreply\r\n$pattern\r\n"; sleep 4; mem_get_is($sock, "pattern", $pattern); for (1..5) { my $size = 400 * 1024; my $data = "x" x $size; print $sock "set foo$_ 0 0 $size\r\n$data\r\n"; my $res = <$sock>; is($res, "STORED\r\n", "stored some big items"); } { my $max = 1024 * 1024; my $big = "a big value that's > .5M and < 1M. "; while (length($big) * 2 < $max) { $big = $big . $big; } my $biglen = length($big); for (1..40) { print $sock "set toast$_ 0 0 $biglen\r\n$big\r\n"; is(scalar <$sock>, "STORED\r\n", "stored big"); } sleep 4; for (1..40) { mem_get_is($sock, "toast$_", $big); } my $stats = mem_stats($sock); cmp_ok($stats->{extstore_page_allocs}, '>', 0, 'at least one page allocated'); cmp_ok($stats->{extstore_objects_written}, '>', 25, 'some objects written'); cmp_ok($stats->{extstore_bytes_written}, '>', $max * 2, 'some bytes written'); cmp_ok($stats->{get_extstore}, '>', 5, 'multiple objects fetched'); cmp_ok($stats->{extstore_objects_read}, '>', 0, 'one object read'); cmp_ok($stats->{extstore_bytes_read}, '>', $max * 2, 'some bytes read'); is($stats->{badcrc_from_extstore}, 0, 'CRC checks successful'); } # fill to eviction { my $keycount = 1000; for (1 .. $keycount) { print $sock "set mfoo$_ 0 0 $plen noreply\r\n$pattern\r\n"; } sleep 6; my $stats = mem_stats($sock); cmp_ok($stats->{extstore_page_evictions}, '>', 0, 'at least one page evicted'); cmp_ok($stats->{extstore_objects_evicted}, '>', 0, 'at least one object evicted'); cmp_ok($stats->{extstore_bytes_evicted}, '>', 0, 'some bytes evicted'); is($stats->{extstore_pages_free}, 1, '1 page is free'); is($stats->{miss_from_extstore}, 0, 'no misses'); # original "pattern" key should be gone. mem_get_is($sock, "pattern", undef, "original pattern key is gone"); $stats = mem_stats($sock); is($stats->{miss_from_extstore}, 1, 'one extstore miss'); print $sock "get pattern2 pattern3\r\n"; is(scalar <$sock>, "END\r\n", "multiget double miss"); $stats = mem_stats($sock); is($stats->{miss_from_extstore}, 3, 'three extstore misses'); } # Let compaction run. { for (1..40) { print $sock "delete toast$_ noreply\r\n" if $_ % 2 == 0; } for (1..1000) { # Force a read so objects don't get skipped. mem_get_is($sock, "mfoo$_", $pattern) if $_ % 2 == 1; } for (1..1000) { # Force a read so objects don't get skipped. print $sock "delete mfoo$_ noreply\r\n" if $_ % 2 == 0; } sleep 4; my $stats = mem_stats($sock); cmp_ok($stats->{extstore_pages_free}, '>', 2, 'some pages now free'); cmp_ok($stats->{extstore_compact_rescues}, '>', 0, 'some compaction rescues happened'); # Some of the early items got evicted for (100..1000) { # everything should validate properly. mem_get_is($sock, "mfoo$_", $pattern) if $_ % 2 == 1; } } # test recache { print $sock "extstore recache_rate 1\r\n"; is(scalar <$sock>, "OK\r\n", "upped recache rate"); for (800..1000) { mem_get_is($sock, "mfoo$_", $pattern) if $_ % 2 == 1; } my $stats = mem_stats($sock); cmp_ok($stats->{recache_from_extstore}, '>', 100, 'recaching happening'); for (800..1000) { mem_get_is($sock, "mfoo$_", $pattern) if $_ % 2 == 1; } my $stats2 = mem_stats($sock); is($stats->{recache_from_extstore}, $stats2->{recache_from_extstore}, 'values already recached'); } done_testing(); END { unlink $ext_path if $ext_path; } memcached-1.5.6/t/watcher.t0000664000175000017500000000464413115057711012452 00000000000000#!/usr/bin/perl # Networked logging tests. use strict; use warnings; use Socket qw/SO_RCVBUF/; use Test::More tests => 8; use FindBin qw($Bin); use lib "$Bin/lib"; use MemcachedTest; my $server = new_memcached('-m 60 -o watcher_logbuf_size=8'); my $client = $server->sock; my $watcher = $server->new_sock; # This doesn't return anything. print $watcher "watch\n"; my $res = <$watcher>; is($res, "OK\r\n", "watcher enabled"); print $client "get foo\n"; $res = <$client>; is($res, "END\r\n", "basic get works"); my $spacer = "X"x180; # This is a flaky test... depends on buffer sizes. Could either have memc # shrink the watcher buffer, or loop this and keep doubling until we get some # skipped values. for (1 .. 80000) { print $client "get foo$_$spacer\n"; $res = <$client>; } # Let the logger thread catch up before we start reading. sleep 1; my $do_fetch = 0; #print STDERR "RESULT: $res\n"; while (my $log = <$watcher>) { # The "skipped" line won't actually print until some space frees up in the # buffer, so we need to occasionally cause new lines to generate. if (($do_fetch++ % 100) == 0) { print $client "get foo\n"; $res = <$client>; } next unless $log =~ m/skipped/; like($log, qr/skipped=/, "skipped some lines"); # This should unjam more of the text. print $client "get foob\n"; $res = <$client>; last; } $res = <$watcher>; like($res, qr/ts=\d+\.\d+\ gid=\d+ type=item_get/, "saw a real log line after a skip"); # test combined logs # fill to evictions, then enable watcher, set again, and look for both lines { my $value = "B"x11000; my $keycount = 8000; for (1 .. $keycount) { print $client "set n,foo$_ 0 0 11000 noreply\r\n$value\r\n"; } $watcher = $server->new_sock; print $watcher "watch mutations evictions\n"; $res = <$watcher>; is($res, "OK\r\n", "new watcher enabled"); my $watcher2 = $server->new_sock; print $watcher2 "watch evictions\n"; $res = <$watcher2>; is($res, "OK\r\n", "evictions watcher enabled"); print $client "set bfoo 0 0 11000 noreply\r\n$value\r\n"; my $found_log = 0; my $found_ev = 0; while (my $log = <$watcher>) { $found_log = 1 if ($log =~ m/type=item_store/); $found_ev = 1 if ($log =~ m/type=eviction/); last if ($found_log && $found_ev); } is($found_log, 1, "found rawcmd log entry"); is($found_ev, 1, "found eviction log entry"); } memcached-1.5.6/t/binary-get.t0000755000175000017500000000076312416643766013073 00000000000000#!/usr/bin/perl use strict; use Test::More tests => 8; use FindBin qw($Bin); use lib "$Bin/lib"; use MemcachedTest; my $server = new_memcached(); my $sock = $server->sock; my $count = 1; foreach my $blob ("mooo\0", "mumble\0\0\0\0\r\rblarg", "\0", "\r") { my $key = "foo$count"; my $len = length($blob); print "len is $len\n"; print $sock "set $key 0 0 $len\r\n$blob\r\n"; is(scalar <$sock>, "STORED\r\n", "stored $key"); mem_get_is($sock, $key, $blob); $count++; } memcached-1.5.6/t/issue_68.t0000644000175000017500000000077112416643766012473 00000000000000#!/usr/bin/perl use strict; use Test::More tests => 996; use FindBin qw($Bin); use lib "$Bin/lib"; use MemcachedTest; my $server = new_memcached(); my $sock = $server->sock; for (my $keyi = 1; $keyi < 250; $keyi++) { my $key = "x" x $keyi; print $sock "set $key 0 0 1\r\n9\r\n"; is (scalar <$sock>, "STORED\r\n", "stored $key"); mem_get_is($sock, $key, "9"); print $sock "incr $key 1\r\n"; is (scalar <$sock>, "10\r\n", "incr $key to 10"); mem_get_is($sock, $key, "10"); } memcached-1.5.6/t/dash-M.t0000664000175000017500000000132513240770206012117 00000000000000#!/usr/bin/perl use strict; use Test::More; use FindBin qw($Bin); use lib "$Bin/lib"; use MemcachedTest; my $server = new_memcached('-M -m 2'); my $sock = $server->sock; my $value = "B" x 8192; my $vallen = length($value); my $resp = "STORED\r\n"; my $key = 0; while($resp eq "STORED\r\n") { print $sock "set dash$key 0 0 $vallen\r\n$value\r\n"; $key++; $resp = scalar <$sock>; } my $max_stored = $key - 1; plan tests => $max_stored + 1; print $sock "set dash$key 0 0 $vallen\r\n$value\r\n"; is(scalar <$sock>, "SERVER_ERROR out of memory storing object\r\n", "failed to add another one."); for($key = 0; $key < $max_stored; $key++) { mem_get_is $sock, "dash$key", $value, "Failed at dash$key"; } memcached-1.5.6/memcached.h0000664000175000017500000006651113241473232012445 00000000000000/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ /** \file * The main memcached header holding commonly used data * structures and function prototypes. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #include #include #include "itoa_ljust.h" #include "protocol_binary.h" #include "cache.h" #include "logger.h" #ifdef EXTSTORE #include "extstore.h" #include "crc32c.h" #endif #include "sasl_defs.h" /** Maximum length of a key. */ #define KEY_MAX_LENGTH 250 /** Size of an incr buf. */ #define INCR_MAX_STORAGE_LEN 24 #define DATA_BUFFER_SIZE 2048 #define UDP_READ_BUFFER_SIZE 65536 #define UDP_MAX_PAYLOAD_SIZE 1400 #define UDP_HEADER_SIZE 8 #define MAX_SENDBUF_SIZE (256 * 1024 * 1024) /* Up to 3 numbers (2 32bit, 1 64bit), spaces, newlines, null 0 */ #define SUFFIX_SIZE 50 /** Initial size of list of items being returned by "get". */ #define ITEM_LIST_INITIAL 200 /** Initial size of list of CAS suffixes appended to "gets" lines. */ #define SUFFIX_LIST_INITIAL 100 /** Initial size of the sendmsg() scatter/gather array. */ #define IOV_LIST_INITIAL 400 /** Initial number of sendmsg() argument structures to allocate. */ #define MSG_LIST_INITIAL 10 /** High water marks for buffer shrinking */ #define READ_BUFFER_HIGHWAT 8192 #define ITEM_LIST_HIGHWAT 400 #define IOV_LIST_HIGHWAT 600 #define MSG_LIST_HIGHWAT 100 /* Binary protocol stuff */ #define MIN_BIN_PKT_LENGTH 16 #define BIN_PKT_HDR_WORDS (MIN_BIN_PKT_LENGTH/sizeof(uint32_t)) /* Initial power multiplier for the hash table */ #define HASHPOWER_DEFAULT 16 #define HASHPOWER_MAX 32 /* * We only reposition items in the LRU queue if they haven't been repositioned * in this many seconds. That saves us from churning on frequently-accessed * items. */ #define ITEM_UPDATE_INTERVAL 60 /* unistd.h is here */ #if HAVE_UNISTD_H # include #endif /* Slab sizing definitions. */ #define POWER_SMALLEST 1 #define POWER_LARGEST 256 /* actual cap is 255 */ #define SLAB_GLOBAL_PAGE_POOL 0 /* magic slab class for storing pages for reassignment */ #define CHUNK_ALIGN_BYTES 8 /* slab class max is a 6-bit number, -1. */ #define MAX_NUMBER_OF_SLAB_CLASSES (63 + 1) /** How long an object can reasonably be assumed to be locked before harvesting it on a low memory condition. Default: disabled. */ #define TAIL_REPAIR_TIME_DEFAULT 0 /* warning: don't use these macros with a function, as it evals its arg twice */ #define ITEM_get_cas(i) (((i)->it_flags & ITEM_CAS) ? \ (i)->data->cas : (uint64_t)0) #define ITEM_set_cas(i,v) { \ if ((i)->it_flags & ITEM_CAS) { \ (i)->data->cas = v; \ } \ } #define ITEM_key(item) (((char*)&((item)->data)) \ + (((item)->it_flags & ITEM_CAS) ? sizeof(uint64_t) : 0)) #define ITEM_suffix(item) ((char*) &((item)->data) + (item)->nkey + 1 \ + (((item)->it_flags & ITEM_CAS) ? sizeof(uint64_t) : 0)) #define ITEM_data(item) ((char*) &((item)->data) + (item)->nkey + 1 \ + (item)->nsuffix \ + (((item)->it_flags & ITEM_CAS) ? sizeof(uint64_t) : 0)) #define ITEM_ntotal(item) (sizeof(struct _stritem) + (item)->nkey + 1 \ + (item)->nsuffix + (item)->nbytes \ + (((item)->it_flags & ITEM_CAS) ? sizeof(uint64_t) : 0)) #define ITEM_clsid(item) ((item)->slabs_clsid & ~(3<<6)) #define ITEM_lruid(item) ((item)->slabs_clsid & (3<<6)) #define STAT_KEY_LEN 128 #define STAT_VAL_LEN 128 /** Append a simple stat with a stat name, value format and value */ #define APPEND_STAT(name, fmt, val) \ append_stat(name, add_stats, c, fmt, val); /** Append an indexed stat with a stat name (with format), value format and value */ #define APPEND_NUM_FMT_STAT(name_fmt, num, name, fmt, val) \ klen = snprintf(key_str, STAT_KEY_LEN, name_fmt, num, name); \ vlen = snprintf(val_str, STAT_VAL_LEN, fmt, val); \ add_stats(key_str, klen, val_str, vlen, c); /** Common APPEND_NUM_FMT_STAT format. */ #define APPEND_NUM_STAT(num, name, fmt, val) \ APPEND_NUM_FMT_STAT("%d:%s", num, name, fmt, val) /** * Callback for any function producing stats. * * @param key the stat's key * @param klen length of the key * @param val the stat's value in an ascii form (e.g. text form of a number) * @param vlen length of the value * @parm cookie magic callback cookie */ typedef void (*ADD_STAT)(const char *key, const uint16_t klen, const char *val, const uint32_t vlen, const void *cookie); /* * NOTE: If you modify this table you _MUST_ update the function state_text */ /** * Possible states of a connection. */ enum conn_states { conn_listening, /**< the socket which listens for connections */ conn_new_cmd, /**< Prepare connection for next command */ conn_waiting, /**< waiting for a readable socket */ conn_read, /**< reading in a command line */ conn_parse_cmd, /**< try to parse a command from the input buffer */ conn_write, /**< writing out a simple response */ conn_nread, /**< reading in a fixed number of bytes */ conn_swallow, /**< swallowing unnecessary bytes w/o storing */ conn_closing, /**< closing this connection */ conn_mwrite, /**< writing out many items sequentially */ conn_closed, /**< connection is closed */ conn_watch, /**< held by the logger thread as a watcher */ conn_max_state /**< Max state value (used for assertion) */ }; enum bin_substates { bin_no_state, bin_reading_set_header, bin_reading_cas_header, bin_read_set_value, bin_reading_get_key, bin_reading_stat, bin_reading_del_header, bin_reading_incr_header, bin_read_flush_exptime, bin_reading_sasl_auth, bin_reading_sasl_auth_data, bin_reading_touch_key, }; enum protocol { ascii_prot = 3, /* arbitrary value. */ binary_prot, negotiating_prot /* Discovering the protocol */ }; enum network_transport { local_transport, /* Unix sockets*/ tcp_transport, udp_transport }; enum pause_thread_types { PAUSE_WORKER_THREADS = 0, PAUSE_ALL_THREADS, RESUME_ALL_THREADS, RESUME_WORKER_THREADS }; #define IS_TCP(x) (x == tcp_transport) #define IS_UDP(x) (x == udp_transport) #define NREAD_ADD 1 #define NREAD_SET 2 #define NREAD_REPLACE 3 #define NREAD_APPEND 4 #define NREAD_PREPEND 5 #define NREAD_CAS 6 enum store_item_type { NOT_STORED=0, STORED, EXISTS, NOT_FOUND, TOO_LARGE, NO_MEMORY }; enum delta_result_type { OK, NON_NUMERIC, EOM, DELTA_ITEM_NOT_FOUND, DELTA_ITEM_CAS_MISMATCH }; /** Time relative to server start. Smaller than time_t on 64-bit systems. */ // TODO: Move to sub-header. needed in logger.h //typedef unsigned int rel_time_t; /** Use X macros to avoid iterating over the stats fields during reset and * aggregation. No longer have to add new stats in 3+ places. */ #define SLAB_STATS_FIELDS \ X(set_cmds) \ X(get_hits) \ X(touch_hits) \ X(delete_hits) \ X(cas_hits) \ X(cas_badval) \ X(incr_hits) \ X(decr_hits) /** Stats stored per slab (and per thread). */ struct slab_stats { #define X(name) uint64_t name; SLAB_STATS_FIELDS #undef X }; #define THREAD_STATS_FIELDS \ X(get_cmds) \ X(get_misses) \ X(get_expired) \ X(get_flushed) \ X(touch_cmds) \ X(touch_misses) \ X(delete_misses) \ X(incr_misses) \ X(decr_misses) \ X(cas_misses) \ X(bytes_read) \ X(bytes_written) \ X(flush_cmds) \ X(conn_yields) /* # of yields for connections (-R option)*/ \ X(auth_cmds) \ X(auth_errors) \ X(idle_kicks) /* idle connections killed */ #ifdef EXTSTORE #define EXTSTORE_THREAD_STATS_FIELDS \ X(get_extstore) \ X(recache_from_extstore) \ X(miss_from_extstore) \ X(badcrc_from_extstore) #endif /** * Stats stored per-thread. */ struct thread_stats { pthread_mutex_t mutex; #define X(name) uint64_t name; THREAD_STATS_FIELDS #ifdef EXTSTORE EXTSTORE_THREAD_STATS_FIELDS #endif #undef X struct slab_stats slab_stats[MAX_NUMBER_OF_SLAB_CLASSES]; uint64_t lru_hits[POWER_LARGEST]; }; /** * Global stats. Only resettable stats should go into this structure. */ struct stats { uint64_t total_items; uint64_t total_conns; uint64_t rejected_conns; uint64_t malloc_fails; uint64_t listen_disabled_num; uint64_t slabs_moved; /* times slabs were moved around */ uint64_t slab_reassign_rescues; /* items rescued during slab move */ uint64_t slab_reassign_evictions_nomem; /* valid items lost during slab move */ uint64_t slab_reassign_inline_reclaim; /* valid items lost during slab move */ uint64_t slab_reassign_chunk_rescues; /* chunked-item chunks recovered */ uint64_t slab_reassign_busy_items; /* valid temporarily unmovable */ uint64_t slab_reassign_busy_deletes; /* refcounted items killed */ uint64_t lru_crawler_starts; /* Number of item crawlers kicked off */ uint64_t lru_maintainer_juggles; /* number of LRU bg pokes */ uint64_t time_in_listen_disabled_us; /* elapsed time in microseconds while server unable to process new connections */ uint64_t log_worker_dropped; /* logs dropped by worker threads */ uint64_t log_worker_written; /* logs written by worker threads */ uint64_t log_watcher_skipped; /* logs watchers missed */ uint64_t log_watcher_sent; /* logs sent to watcher buffers */ #ifdef EXTSTORE uint64_t extstore_compact_lost; /* items lost because they were locked */ uint64_t extstore_compact_rescues; /* items re-written during compaction */ uint64_t extstore_compact_skipped; /* unhit items skipped during compaction */ #endif struct timeval maxconns_entered; /* last time maxconns entered */ }; /** * Global "state" stats. Reflects state that shouldn't be wiped ever. * Ordered for some cache line locality for commonly updated counters. */ struct stats_state { uint64_t curr_items; uint64_t curr_bytes; uint64_t curr_conns; uint64_t hash_bytes; /* size used for hash tables */ unsigned int conn_structs; unsigned int reserved_fds; unsigned int hash_power_level; /* Better hope it's not over 9000 */ bool hash_is_expanding; /* If the hash table is being expanded */ bool accepting_conns; /* whether we are currently accepting */ bool slab_reassign_running; /* slab reassign in progress */ bool lru_crawler_running; /* crawl in progress */ }; #define MAX_VERBOSITY_LEVEL 2 /* When adding a setting, be sure to update process_stat_settings */ /** * Globally accessible settings as derived from the commandline. */ struct settings { size_t maxbytes; int maxconns; int port; int udpport; char *inter; int verbose; rel_time_t oldest_live; /* ignore existing items older than this */ uint64_t oldest_cas; /* ignore existing items with CAS values lower than this */ int evict_to_free; char *socketpath; /* path to unix socket if using local socket */ int access; /* access mask (a la chmod) for unix domain socket */ double factor; /* chunk size growth factor */ int chunk_size; int num_threads; /* number of worker (without dispatcher) libevent threads to run */ int num_threads_per_udp; /* number of worker threads serving each udp socket */ char prefix_delimiter; /* character that marks a key prefix (for stats) */ int detail_enabled; /* nonzero if we're collecting detailed stats */ int reqs_per_event; /* Maximum number of io to process on each io-event. */ bool use_cas; enum protocol binding_protocol; int backlog; int item_size_max; /* Maximum item size */ int slab_chunk_size_max; /* Upper end for chunks within slab pages. */ int slab_page_size; /* Slab's page units. */ bool sasl; /* SASL on/off */ bool maxconns_fast; /* Whether or not to early close connections */ bool lru_crawler; /* Whether or not to enable the autocrawler thread */ bool lru_maintainer_thread; /* LRU maintainer background thread */ bool lru_segmented; /* Use split or flat LRU's */ bool slab_reassign; /* Whether or not slab reassignment is allowed */ int slab_automove; /* Whether or not to automatically move slabs */ double slab_automove_ratio; /* youngest must be within pct of oldest */ unsigned int slab_automove_window; /* window mover for algorithm */ int hashpower_init; /* Starting hash power level */ bool shutdown_command; /* allow shutdown command */ int tail_repair_time; /* LRU tail refcount leak repair time */ bool flush_enabled; /* flush_all enabled */ bool dump_enabled; /* whether cachedump/metadump commands work */ char *hash_algorithm; /* Hash algorithm in use */ int lru_crawler_sleep; /* Microsecond sleep between items */ uint32_t lru_crawler_tocrawl; /* Number of items to crawl per run */ int hot_lru_pct; /* percentage of slab space for HOT_LRU */ int warm_lru_pct; /* percentage of slab space for WARM_LRU */ double hot_max_factor; /* HOT tail age relative to COLD tail */ double warm_max_factor; /* WARM tail age relative to COLD tail */ int crawls_persleep; /* Number of LRU crawls to run before sleeping */ bool inline_ascii_response; /* pre-format the VALUE line for ASCII responses */ bool temp_lru; /* TTL < temporary_ttl uses TEMP_LRU */ uint32_t temporary_ttl; /* temporary LRU threshold */ int idle_timeout; /* Number of seconds to let connections idle */ unsigned int logger_watcher_buf_size; /* size of logger's per-watcher buffer */ unsigned int logger_buf_size; /* size of per-thread logger buffer */ bool drop_privileges; /* Whether or not to drop unnecessary process privileges */ bool relaxed_privileges; /* Relax process restrictions when running testapp */ #ifdef EXTSTORE unsigned int ext_item_size; /* minimum size of items to store externally */ unsigned int ext_item_age; /* max age of tail item before storing ext. */ unsigned int ext_low_ttl; /* remaining TTL below this uses own pages */ unsigned int ext_recache_rate; /* counter++ % recache_rate == 0 > recache */ unsigned int ext_wbuf_size; /* read only note for the engine */ unsigned int ext_compact_under; /* when fewer than this many pages, compact */ unsigned int ext_drop_under; /* when fewer than this many pages, drop COLD items */ double ext_max_frag; /* ideal maximum page fragmentation */ double slab_automove_freeratio; /* % of memory to hold free as buffer */ bool ext_drop_unread; /* skip unread items during compaction */ /* per-slab-class free chunk limit */ unsigned int ext_free_memchunks[MAX_NUMBER_OF_SLAB_CLASSES]; #endif }; extern struct stats stats; extern struct stats_state stats_state; extern time_t process_started; extern struct settings settings; #define ITEM_LINKED 1 #define ITEM_CAS 2 /* temp */ #define ITEM_SLABBED 4 /* Item was fetched at least once in its lifetime */ #define ITEM_FETCHED 8 /* Appended on fetch, removed on LRU shuffling */ #define ITEM_ACTIVE 16 /* If an item's storage are chained chunks. */ #define ITEM_CHUNKED 32 #define ITEM_CHUNK 64 #ifdef EXTSTORE /* ITEM_data bulk is external to item */ #define ITEM_HDR 128 #endif /** * Structure for storing items within memcached. */ typedef struct _stritem { /* Protected by LRU locks */ struct _stritem *next; struct _stritem *prev; /* Rest are protected by an item lock */ struct _stritem *h_next; /* hash chain next */ rel_time_t time; /* least recent access */ rel_time_t exptime; /* expire time */ int nbytes; /* size of data */ unsigned short refcount; uint8_t nsuffix; /* length of flags-and-length string */ uint8_t it_flags; /* ITEM_* above */ uint8_t slabs_clsid;/* which slab class we're in */ uint8_t nkey; /* key length, w/terminating null and padding */ /* this odd type prevents type-punning issues when we do * the little shuffle to save space when not using CAS. */ union { uint64_t cas; char end; } data[]; /* if it_flags & ITEM_CAS we have 8 bytes CAS */ /* then null-terminated key */ /* then " flags length\r\n" (no terminating null) */ /* then data with terminating \r\n (no terminating null; it's binary!) */ } item; // TODO: If we eventually want user loaded modules, we can't use an enum :( enum crawler_run_type { CRAWLER_AUTOEXPIRE=0, CRAWLER_EXPIRED, CRAWLER_METADUMP }; typedef struct { struct _stritem *next; struct _stritem *prev; struct _stritem *h_next; /* hash chain next */ rel_time_t time; /* least recent access */ rel_time_t exptime; /* expire time */ int nbytes; /* size of data */ unsigned short refcount; uint8_t nsuffix; /* length of flags-and-length string */ uint8_t it_flags; /* ITEM_* above */ uint8_t slabs_clsid;/* which slab class we're in */ uint8_t nkey; /* key length, w/terminating null and padding */ uint32_t remaining; /* Max keys to crawl per slab per invocation */ uint64_t reclaimed; /* items reclaimed during this crawl. */ uint64_t unfetched; /* items reclaimed unfetched during this crawl. */ uint64_t checked; /* items examined during this crawl. */ } crawler; /* Header when an item is actually a chunk of another item. */ typedef struct _strchunk { struct _strchunk *next; /* points within its own chain. */ struct _strchunk *prev; /* can potentially point to the head. */ struct _stritem *head; /* always points to the owner chunk */ int size; /* available chunk space in bytes */ int used; /* chunk space used */ int nbytes; /* used. */ unsigned short refcount; /* used? */ uint8_t orig_clsid; /* For obj hdr chunks slabs_clsid is fake. */ uint8_t it_flags; /* ITEM_* above. */ uint8_t slabs_clsid; /* Same as above. */ char data[]; } item_chunk; #ifdef EXTSTORE typedef struct { unsigned int page_version; /* from IO header */ unsigned int offset; /* from IO header */ unsigned short page_id; /* from IO header */ } item_hdr; #endif typedef struct { pthread_t thread_id; /* unique ID of this thread */ struct event_base *base; /* libevent handle this thread uses */ struct event notify_event; /* listen event for notify pipe */ int notify_receive_fd; /* receiving end of notify pipe */ int notify_send_fd; /* sending end of notify pipe */ struct thread_stats stats; /* Stats generated by this thread */ struct conn_queue *new_conn_queue; /* queue of new connections to handle */ cache_t *suffix_cache; /* suffix cache */ #ifdef EXTSTORE cache_t *io_cache; /* IO objects */ void *storage; /* data object for storage system */ #endif logger *l; /* logger buffer */ void *lru_bump_buf; /* async LRU bump buffer */ } LIBEVENT_THREAD; typedef struct conn conn; #ifdef EXTSTORE typedef struct _io_wrap { obj_io io; struct _io_wrap *next; conn *c; item *hdr_it; /* original header item. */ unsigned int iovec_start; /* start of the iovecs for this IO */ unsigned int iovec_count; /* total number of iovecs */ unsigned int iovec_data; /* specific index of data iovec */ bool miss; /* signal a miss to unlink hdr_it */ bool badcrc; /* signal a crc failure */ bool active; // FIXME: canary for test. remove } io_wrap; #endif /** * The structure representing a connection into memcached. */ struct conn { int sfd; sasl_conn_t *sasl_conn; bool authenticated; enum conn_states state; enum bin_substates substate; rel_time_t last_cmd_time; struct event event; short ev_flags; short which; /** which events were just triggered */ char *rbuf; /** buffer to read commands into */ char *rcurr; /** but if we parsed some already, this is where we stopped */ int rsize; /** total allocated size of rbuf */ int rbytes; /** how much data, starting from rcur, do we have unparsed */ char *wbuf; char *wcurr; int wsize; int wbytes; /** which state to go into after finishing current write */ enum conn_states write_and_go; void *write_and_free; /** free this memory after finishing writing */ char *ritem; /** when we read in an item's value, it goes here */ int rlbytes; /* data for the nread state */ /** * item is used to hold an item structure created after reading the command * line of set/add/replace commands, but before we finished reading the actual * data. The data is read into ITEM_data(item) to avoid extra copying. */ void *item; /* for commands set/add/replace */ /* data for the swallow state */ int sbytes; /* how many bytes to swallow */ /* data for the mwrite state */ struct iovec *iov; int iovsize; /* number of elements allocated in iov[] */ int iovused; /* number of elements used in iov[] */ struct msghdr *msglist; int msgsize; /* number of elements allocated in msglist[] */ int msgused; /* number of elements used in msglist[] */ int msgcurr; /* element in msglist[] being transmitted now */ int msgbytes; /* number of bytes in current msg */ item **ilist; /* list of items to write out */ int isize; item **icurr; int ileft; char **suffixlist; int suffixsize; char **suffixcurr; int suffixleft; #ifdef EXTSTORE int io_wrapleft; unsigned int recache_counter; io_wrap *io_wraplist; /* linked list of io_wraps */ bool io_queued; /* FIXME: debugging flag */ #endif enum protocol protocol; /* which protocol this connection speaks */ enum network_transport transport; /* what transport is used by this connection */ /* data for UDP clients */ int request_id; /* Incoming UDP request ID, if this is a UDP "connection" */ struct sockaddr_in6 request_addr; /* udp: Who sent the most recent request */ socklen_t request_addr_size; unsigned char *hdrbuf; /* udp packet headers */ int hdrsize; /* number of headers' worth of space is allocated */ bool noreply; /* True if the reply should not be sent. */ /* current stats command */ struct { char *buffer; size_t size; size_t offset; } stats; /* Binary protocol stuff */ /* This is where the binary header goes */ protocol_binary_request_header binary_header; uint64_t cas; /* the cas to return */ short cmd; /* current command being processed */ int opaque; int keylen; conn *next; /* Used for generating a list of conn structures */ LIBEVENT_THREAD *thread; /* Pointer to the thread object serving this connection */ }; /* array of conn structures, indexed by file descriptor */ extern conn **conns; /* current time of day (updated periodically) */ extern volatile rel_time_t current_time; /* TODO: Move to slabs.h? */ extern volatile int slab_rebalance_signal; struct slab_rebalance { void *slab_start; void *slab_end; void *slab_pos; int s_clsid; int d_clsid; uint32_t busy_items; uint32_t rescues; uint32_t evictions_nomem; uint32_t inline_reclaim; uint32_t chunk_rescues; uint32_t busy_deletes; uint32_t busy_loops; uint8_t done; }; extern struct slab_rebalance slab_rebal; #ifdef EXTSTORE extern void *ext_storage; #endif /* * Functions */ void do_accept_new_conns(const bool do_accept); enum delta_result_type do_add_delta(conn *c, const char *key, const size_t nkey, const bool incr, const int64_t delta, char *buf, uint64_t *cas, const uint32_t hv); enum store_item_type do_store_item(item *item, int comm, conn* c, const uint32_t hv); conn *conn_new(const int sfd, const enum conn_states init_state, const int event_flags, const int read_buffer_size, enum network_transport transport, struct event_base *base); void conn_worker_readd(conn *c); extern int daemonize(int nochdir, int noclose); #define mutex_lock(x) pthread_mutex_lock(x) #define mutex_unlock(x) pthread_mutex_unlock(x) #include "stats.h" #include "slabs.h" #include "assoc.h" #include "items.h" #include "crawler.h" #include "trace.h" #include "hash.h" #include "util.h" /* * Functions such as the libevent-related calls that need to do cross-thread * communication in multithreaded mode (rather than actually doing the work * in the current thread) are called via "dispatch_" frontends, which are * also #define-d to directly call the underlying code in singlethreaded mode. */ void memcached_thread_init(int nthreads, void *arg); void redispatch_conn(conn *c); void dispatch_conn_new(int sfd, enum conn_states init_state, int event_flags, int read_buffer_size, enum network_transport transport); void sidethread_conn_close(conn *c); /* Lock wrappers for cache functions that are called from main loop. */ enum delta_result_type add_delta(conn *c, const char *key, const size_t nkey, const int incr, const int64_t delta, char *buf, uint64_t *cas); void accept_new_conns(const bool do_accept); conn *conn_from_freelist(void); bool conn_add_to_freelist(conn *c); void conn_close_idle(conn *c); item *item_alloc(char *key, size_t nkey, int flags, rel_time_t exptime, int nbytes); #define DO_UPDATE true #define DONT_UPDATE false item *item_get(const char *key, const size_t nkey, conn *c, const bool do_update); item *item_touch(const char *key, const size_t nkey, uint32_t exptime, conn *c); int item_link(item *it); void item_remove(item *it); int item_replace(item *it, item *new_it, const uint32_t hv); void item_unlink(item *it); void item_lock(uint32_t hv); void *item_trylock(uint32_t hv); void item_trylock_unlock(void *arg); void item_unlock(uint32_t hv); void pause_threads(enum pause_thread_types type); #define refcount_incr(it) ++(it->refcount) #define refcount_decr(it) --(it->refcount) void STATS_LOCK(void); void STATS_UNLOCK(void); void threadlocal_stats_reset(void); void threadlocal_stats_aggregate(struct thread_stats *stats); void slab_stats_aggregate(struct thread_stats *stats, struct slab_stats *out); /* Stat processing functions */ void append_stat(const char *name, ADD_STAT add_stats, conn *c, const char *fmt, ...); enum store_item_type store_item(item *item, int comm, conn *c); #if HAVE_DROP_PRIVILEGES extern void drop_privileges(void); #else #define drop_privileges() #endif #if HAVE_DROP_WORKER_PRIVILEGES extern void drop_worker_privileges(void); #else #define drop_worker_privileges() #endif /* If supported, give compiler hints for branch prediction. */ #if !defined(__GNUC__) || (__GNUC__ == 2 && __GNUC_MINOR__ < 96) #define __builtin_expect(x, expected_value) (x) #endif #define likely(x) __builtin_expect((x),1) #define unlikely(x) __builtin_expect((x),0) memcached-1.5.6/linux_priv.c0000664000175000017500000001061713160272015012721 00000000000000#include "config.h" #include #include #include #include "memcached.h" // In the future when the system is more tested this could be switched // to SCMP_ACT_KILL instead. #define DENY_ACTION SCMP_ACT_ERRNO(EACCES) void drop_privileges(void) { scmp_filter_ctx ctx = seccomp_init(DENY_ACTION); if (ctx == NULL) { return; } int rc = 0; rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(sigreturn), 0); rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(futex), 0); rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(epoll_wait), 0); rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(accept4), 0); rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(accept), 0); rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(write), 0); rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(fstat), 0); rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mmap), 0); rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(munmap), 0); rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(shmctl), 0); rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit_group), 0); #ifdef MEMCACHED_DEBUG rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(open), 0); rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(fcntl), 0); rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(read), 0); rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(lseek), 0); rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(close), 0); rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(getpid), 0); #endif if (rc != 0) { goto fail; } rc = seccomp_load(ctx); if (rc < 0) { goto fail; } fail: seccomp_release(ctx); } void drop_worker_privileges(void) { scmp_filter_ctx ctx = seccomp_init(DENY_ACTION); if (ctx == NULL) { return; } int rc = 0; rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(sigreturn), 0); rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(futex), 0); rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(epoll_wait), 0); rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(epoll_ctl), 0); rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(read), 0); rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mprotect), 0); rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(getpeername), 0); rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(close), 0); rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(sendmsg), 0); rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(getrusage), 0); rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mmap), 0); rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mremap), 0); rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(munmap), 0); rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(recvfrom), 0); // for spawning the LRU crawler rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(clone), 0); rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(set_robust_list), 0); rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(madvise), 0); rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit), 0); // stat rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(getsockname), 0); rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(getpid), 0); if (settings.shutdown_command) { rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(tgkill), 0); rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit_group), 0); rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(fstat), 0); rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(rt_sigprocmask), 0); rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(getpid), 0); rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(gettid), 0); } if (settings.relaxed_privileges) { rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(open), 0); rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(fcntl), 0); rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(lseek), 0); rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(write), 0); } else { rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(write), 1, SCMP_A0(SCMP_CMP_EQ, 1)); } if (rc != 0) { goto fail; } rc = seccomp_load(ctx); if (rc < 0) { goto fail; } fail: seccomp_release(ctx); } memcached-1.5.6/items.h0000664000175000017500000000633413242642567011667 00000000000000#define HOT_LRU 0 #define WARM_LRU 64 #define COLD_LRU 128 #define TEMP_LRU 192 #define CLEAR_LRU(id) (id & ~(3<<6)) #define GET_LRU(id) (id & (3<<6)) /* See items.c */ uint64_t get_cas_id(void); /*@null@*/ item *do_item_alloc(char *key, const size_t nkey, const unsigned int flags, const rel_time_t exptime, const int nbytes); item_chunk *do_item_alloc_chunk(item_chunk *ch, const size_t bytes_remain); item *do_item_alloc_pull(const size_t ntotal, const unsigned int id); void item_free(item *it); bool item_size_ok(const size_t nkey, const int flags, const int nbytes); int do_item_link(item *it, const uint32_t hv); /** may fail if transgresses limits */ void do_item_unlink(item *it, const uint32_t hv); void do_item_unlink_nolock(item *it, const uint32_t hv); void do_item_remove(item *it); void do_item_update(item *it); /** update LRU time to current and reposition */ void do_item_update_nolock(item *it); int do_item_replace(item *it, item *new_it, const uint32_t hv); int item_is_flushed(item *it); unsigned int do_get_lru_size(uint32_t id); void do_item_linktail_q(item *it); void do_item_unlinktail_q(item *it); item *do_item_crawl_q(item *it); void *item_lru_bump_buf_create(void); #define LRU_PULL_EVICT 1 #define LRU_PULL_CRAWL_BLOCKS 2 #define LRU_PULL_RETURN_ITEM 4 /* fill info struct if available */ struct lru_pull_tail_return { item *it; uint32_t hv; }; int lru_pull_tail(const int orig_id, const int cur_lru, const uint64_t total_bytes, const uint8_t flags, const rel_time_t max_age, struct lru_pull_tail_return *ret_it); /*@null@*/ char *item_cachedump(const unsigned int slabs_clsid, const unsigned int limit, unsigned int *bytes); void item_stats(ADD_STAT add_stats, void *c); void do_item_stats_add_crawl(const int i, const uint64_t reclaimed, const uint64_t unfetched, const uint64_t checked); void item_stats_totals(ADD_STAT add_stats, void *c); /*@null@*/ void item_stats_sizes(ADD_STAT add_stats, void *c); void item_stats_sizes_init(void); void item_stats_sizes_enable(ADD_STAT add_stats, void *c); void item_stats_sizes_disable(ADD_STAT add_stats, void *c); void item_stats_sizes_add(item *it); void item_stats_sizes_remove(item *it); bool item_stats_sizes_status(void); /* stats getter for slab automover */ typedef struct { int64_t evicted; int64_t outofmemory; uint32_t age; } item_stats_automove; void fill_item_stats_automove(item_stats_automove *am); item *do_item_get(const char *key, const size_t nkey, const uint32_t hv, conn *c, const bool do_update); item *do_item_touch(const char *key, const size_t nkey, uint32_t exptime, const uint32_t hv, conn *c); void item_stats_reset(void); extern pthread_mutex_t lru_locks[POWER_LARGEST]; int start_lru_maintainer_thread(void *arg); int stop_lru_maintainer_thread(void); int init_lru_maintainer(void); void lru_maintainer_pause(void); void lru_maintainer_resume(void); void *lru_bump_buf_create(void); #ifdef EXTSTORE #define STORAGE_delete(e, it) \ do { \ if (it->it_flags & ITEM_HDR) { \ item_hdr *hdr = (item_hdr *)ITEM_data(it); \ extstore_delete(e, hdr->page_id, hdr->page_version, \ 1, ITEM_ntotal(it)); \ } \ } while (0) #else #define STORAGE_delete(...) #endif memcached-1.5.6/timedrun.c0000664000175000017500000000456513245327530012365 00000000000000#include #include #include #include #include #include #include static int caught = 0; static void caught_signal(int which) { caught = which; } static int wait_for_process(pid_t pid) { int rv = EX_SOFTWARE; int status = 0; int i = 0; struct sigaction sig_handler; sig_handler.sa_handler = caught_signal; sig_handler.sa_flags = 0; sigaction(SIGALRM, &sig_handler, NULL); sigaction(SIGHUP, &sig_handler, NULL); sigaction(SIGINT, &sig_handler, NULL); sigaction(SIGTERM, &sig_handler, NULL); sigaction(SIGPIPE, &sig_handler, NULL); /* Loop forever waiting for the process to quit */ for (i = 0; ;i++) { pid_t p = waitpid(pid, &status, 0); if (p == pid) { /* child exited. Let's get out of here */ rv = WIFEXITED(status) ? WEXITSTATUS(status) : (0x80 | WTERMSIG(status)); break; } else { int sig = 0; switch (i) { case 0: /* On the first iteration, pass the signal through */ sig = caught > 0 ? caught : SIGTERM; if (caught == SIGALRM) { fprintf(stderr, "Timeout.. killing the process\n"); } break; case 1: sig = SIGTERM; break; default: sig = SIGKILL; break; } if (kill(pid, sig) < 0) { /* Kill failed. Must have lost the process. :/ */ perror("lost child when trying to kill"); } /* Wait up to 5 seconds for the pid */ alarm(5); } } return rv; } static int spawn_and_wait(char **argv) { int rv = EX_SOFTWARE; pid_t pid = fork(); switch (pid) { case -1: perror("fork"); rv = EX_OSERR; break; /* NOTREACHED */ case 0: execvp(argv[0], argv); perror("exec"); rv = EX_SOFTWARE; break; /* NOTREACHED */ default: rv = wait_for_process(pid); } return rv; } int main(int argc, char **argv) { int naptime = 0; assert(argc > 2); naptime = atoi(argv[1]); assert(naptime > 0 && naptime < 1800); alarm(naptime); return spawn_and_wait(argv+2); } memcached-1.5.6/cache.c0000664000175000017500000000742613150607001011565 00000000000000/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ #include #include #include #ifndef NDEBUG #include #endif #include "cache.h" #ifndef NDEBUG const uint64_t redzone_pattern = 0xdeadbeefcafebabe; int cache_error = 0; #endif const int initial_pool_size = 64; cache_t* cache_create(const char *name, size_t bufsize, size_t align, cache_constructor_t* constructor, cache_destructor_t* destructor) { cache_t* ret = calloc(1, sizeof(cache_t)); char* nm = strdup(name); void** ptr = calloc(initial_pool_size, sizeof(void*)); if (ret == NULL || nm == NULL || ptr == NULL || pthread_mutex_init(&ret->mutex, NULL) == -1) { free(ret); free(nm); free(ptr); return NULL; } ret->name = nm; ret->ptr = ptr; ret->freetotal = initial_pool_size; ret->constructor = constructor; ret->destructor = destructor; #ifndef NDEBUG ret->bufsize = bufsize + 2 * sizeof(redzone_pattern); #else ret->bufsize = bufsize; #endif return ret; } static inline void* get_object(void *ptr) { #ifndef NDEBUG uint64_t *pre = ptr; return pre + 1; #else return ptr; #endif } void cache_destroy(cache_t *cache) { while (cache->freecurr > 0) { void *ptr = cache->ptr[--cache->freecurr]; if (cache->destructor) { cache->destructor(get_object(ptr), NULL); } free(ptr); } free(cache->name); free(cache->ptr); pthread_mutex_destroy(&cache->mutex); free(cache); } void* cache_alloc(cache_t *cache) { void *ret; pthread_mutex_lock(&cache->mutex); ret = do_cache_alloc(cache); pthread_mutex_unlock(&cache->mutex); return ret; } void* do_cache_alloc(cache_t *cache) { void *ret; void *object; if (cache->freecurr > 0) { ret = cache->ptr[--cache->freecurr]; object = get_object(ret); } else { object = ret = malloc(cache->bufsize); if (ret != NULL) { object = get_object(ret); if (cache->constructor != NULL && cache->constructor(object, NULL, 0) != 0) { free(ret); object = NULL; } } } #ifndef NDEBUG if (object != NULL) { /* add a simple form of buffer-check */ uint64_t *pre = ret; *pre = redzone_pattern; ret = pre+1; memcpy(((char*)ret) + cache->bufsize - (2 * sizeof(redzone_pattern)), &redzone_pattern, sizeof(redzone_pattern)); } #endif return object; } void cache_free(cache_t *cache, void *ptr) { pthread_mutex_lock(&cache->mutex); do_cache_free(cache, ptr); pthread_mutex_unlock(&cache->mutex); } void do_cache_free(cache_t *cache, void *ptr) { #ifndef NDEBUG /* validate redzone... */ if (memcmp(((char*)ptr) + cache->bufsize - (2 * sizeof(redzone_pattern)), &redzone_pattern, sizeof(redzone_pattern)) != 0) { raise(SIGABRT); cache_error = 1; return; } uint64_t *pre = ptr; --pre; if (*pre != redzone_pattern) { raise(SIGABRT); cache_error = -1; return; } ptr = pre; #endif if (cache->freecurr < cache->freetotal) { cache->ptr[cache->freecurr++] = ptr; } else { /* try to enlarge free connections array */ size_t newtotal = cache->freetotal * 2; void **new_free = realloc(cache->ptr, sizeof(char *) * newtotal); if (new_free) { cache->freetotal = newtotal; cache->ptr = new_free; cache->ptr[cache->freecurr++] = ptr; } else { if (cache->destructor) { cache->destructor(ptr, NULL); } free(ptr); } } } memcached-1.5.6/daemon.c0000644000175000017500000000601212606105207011757 00000000000000/* $Header: /cvsroot/wikipedia/willow/src/bin/willow/daemon.c,v 1.1 2005/05/02 19:15:21 kateturner Exp $ */ /* $NetBSD: daemon.c,v 1.9 2003/08/07 16:42:46 agc Exp $ */ /*- * Copyright (c) 1990, 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. * 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. */ #if defined __SUNPRO_C || defined __DECC || defined __HP_cc # pragma ident "@(#)$Header: /cvsroot/wikipedia/willow/src/bin/willow/daemon.c,v 1.1 2005/05/02 19:15:21 kateturner Exp $" # pragma ident "$NetBSD: daemon.c,v 1.9 2003/08/07 16:42:46 agc Exp $" #endif #include #include #include #include #include "memcached.h" int daemonize(int nochdir, int noclose) { int fd; switch (fork()) { case -1: return (-1); case 0: break; default: _exit(EXIT_SUCCESS); } if (setsid() == -1) return (-1); if (nochdir == 0) { if(chdir("/") != 0) { perror("chdir"); return (-1); } } if (noclose == 0 && (fd = open("/dev/null", O_RDWR, 0)) != -1) { if(dup2(fd, STDIN_FILENO) < 0) { perror("dup2 stdin"); return (-1); } if(dup2(fd, STDOUT_FILENO) < 0) { perror("dup2 stdout"); return (-1); } if(dup2(fd, STDERR_FILENO) < 0) { perror("dup2 stderr"); return (-1); } if (fd > STDERR_FILENO) { if(close(fd) < 0) { perror("close"); return (-1); } } } return (0); } memcached-1.5.6/doc/0000755000175000017500000000000013245330106011174 500000000000000memcached-1.5.6/doc/CONTRIBUTORS0000664000175000017500000000303613025643161013004 00000000000000MEMCACHED CONTRIBUTORS This file contains a list of people who have contributed code and effort to the memcached project. If you don't see your name mentioned send email to the memcached mailing list so you can be immortalized. Also see the ChangeLog for even more people who have helped over the years by submitting fixes, patches and reporting bugs. A list is generated from git here: http://memcached.org/about Major authors: -------------- Brad Fitzpatrick -- maintainer, original implementations Anatoly Vorobey -- lots of the modern server code Steven Grimm -- iov writing (less CPU), UDP mode, non-2.0 slab mantissas, multithread, ... Other Contributors ------------------ Evan Martin Nathan Neulinger Eric Hodel Michael Johnson Paul Querna Jamie McCarthy Philip Neustrom Andrew O'Brien Josh Rotenberg Robin H. Johnson Tim Yardley Paolo Borelli Eli Bingham Jean-Francois Bustarret Paul G Paul Lindner Dormando Dustin Sallings Chris Goffinet Tomash Brechko Brian Aker Trond Norbye memcached-1.5.6/doc/new_lru.txt0000664000175000017500000000464513150607001013337 00000000000000In versions new enough to have the `-o lru_maintainer` option, a new LRU mechanic is available. Previously: Each slab class has an independent doubly-linked list comprising its LRU. Items are pulled from the bottom and either reclaimed or evicted as needed. Now, enabling `-o lru_maintainer` changes all of the behavior below: * LRU's are now split between HOT, WARM, and COLD LRU's. New items enter the HOT LRU. * Items hit at least twice are considered active. * LRU updates only happen as items reach the bottom of an LRU. If active in HOT, move to WARM, if active in WARM, stay in WARM. If active in COLD, move to WARM. The exception is that items active in COLD are immediately moved to WARM. * HOT/WARM each capped at N% of memory available for that slab class. COLD is uncapped (by default, as of this writing). * HOT/WARM are also age capped by ratios of COLD's age. IE: HOT is capped at N% of memory, or 10% of the age of COLD, whichever comes first. * Items flow from HOT/WARM into COLD. * A background thread exists which shuffles items between/within the LRU's as limits are reached. This includes moves from COLD to WARM. * The background thread can also control the lru_crawler, if enabled. The primary goal is to better protect active items from "scanning". Items which are never hit again will flow from HOT, through COLD, and out the bottom. Items occasionally active (reaching COLD, but being hit before eviction), move to WARM. There they can stay relatively protected. A secondary goal is to improve latency. The LRU locks are no longer used on most item reads, largely during sets and from the background thread. Also the background thread is likely to find expired items and release them back to the slab class asynchronously, which speeds up new allocations. It is recommended to use this feature with the lru crawler as well: `memcached -o lru_maintainer,lru_crawler` - Then it will automatically scan slab classes for items with expired TTL's. If your items are always set to never expire, you can omit this option safely. An extra option: `-o temporary_ttl=N` (when used with lru_maintainer) will make items with a TTL less than or equal to this value use a fourth TEMP LRU. Items stored in TEMP are never bumped within its LRU or moved to other LRU's. They also cannot be evicted. This can help reduce holes and load on the LRU crawler. Do not set temporary_ttl too high or memory could become exhausted. memcached-1.5.6/doc/threads.txt0000664000175000017500000000465613150607001013320 00000000000000Multithreading in memcached *was* originally simple: - One listener thread - N "event worker" threads - Some misc background threads Each worker thread is assigned connections, and runs its own epoll loop. The central hash table, LRU lists, and some statistics counters are covered by global locks. Protocol parsing, data transfer happens in threads. Data lookups and modifications happen under central locks. THIS HAS CHANGED! - A secondary small hash table of locks is used to lock an item by its hash value. This prevents multiple threads from acting on the same item at the same time. - This secondary hash table is mapped to the central hash tables buckets. This allows multiple threads to access the hash table in parallel. Only one thread may read or write against a particular hash table bucket. - atomic refcounts per item are used to manage garbage collection and mutability. - When pulling an item off of the LRU tail for eviction or re-allocation, the system must attempt to lock the item's bucket, which is done with a trylock to avoid deadlocks. If a bucket is in use (and not by that thread) it will walk up the LRU a little in an attempt to fetch a non-busy item. - Each LRU (and sub-LRU's in newer modes) has an independent lock. - Raw accesses to the slab class are protected by a global slabs_lock. This is a short lock which covers pushing and popping free memory. - item_lock must be held while modifying an item. - slabs_lock must be held while modifying the ITEM_SLABBED flag bit within an item. - ITEM_LINKED must not be set before an item has a key copied into it. - items without ITEM_SLABBED set cannot have their memory zeroed out. LOCK ORDERS: (incomplete as of writing, sorry): item_lock -> lru_lock -> slabs_lock lru_lock -> item_trylock Various stats_locks should never have other locks as dependencies. Various locks exist for background threads. They can be used to pause the thread execution or update settings while the threads are idle. They may call item or lru locks. A low priority issue: - If you remove the per-thread stats lock, CPU usage goes down by less than a point of a percent, and it does not improve scalability. - In my testing, the remaining global STATS_LOCK calls never seem to collide. Yes, more stats can be moved to threads, and those locks can actually be removed entirely on x86-64 systems. However my tests haven't shown that as beneficial so far, so I've prioritized other work. memcached-1.5.6/doc/Makefile.am0000664000175000017500000000132713115057711013161 00000000000000man_MANS = memcached.1 EXTRA_DIST = *.txt BUILT_SOURCES= if BUILD_SPECIFICATIONS BUILT_SOURCES += protocol-binary.txt protocol-binary-range.txt MOSTLYCLEANFILES = protocol-binary.txt protocol-binary-range.txt endif protocol-binary.txt: protocol-binary.full @XML2RFC@ -c @abs_builddir@ protocol-binary.full $@ protocol-binary-range.txt: protocol-binary-range.full @XML2RFC@ -c @abs_builddir@ protocol-binary-range.full $@ protocol-binary.full: protocol-binary.xml xml2rfc/rfc2629-noinc.xsl @XSLTPROC@ --nonet xml2rfc/rfc2629-noinc.xsl protocol-binary.xml > $@ protocol-binary-range.full: protocol-binary-range.xml xml2rfc/rfc2629-noinc.xsl @XSLTPROC@ --nonet xml2rfc/rfc2629-noinc.xsl protocol-binary-range.xml > $@ memcached-1.5.6/doc/protocol.txt0000664000175000017500000015325113240770206013533 00000000000000Protocol -------- Clients of memcached communicate with server through TCP connections. (A UDP interface is also available; details are below under "UDP protocol.") A given running memcached server listens on some (configurable) port; clients connect to that port, send commands to the server, read responses, and eventually close the connection. There is no need to send any command to end the session. A client may just close the connection at any moment it no longer needs it. Note, however, that clients are encouraged to cache their connections rather than reopen them every time they need to store or retrieve data. This is because memcached is especially designed to work very efficiently with a very large number (many hundreds, more than a thousand if necessary) of open connections. Caching connections will eliminate the overhead associated with establishing a TCP connection (the overhead of preparing for a new connection on the server side is insignificant compared to this). There are two kinds of data sent in the memcache protocol: text lines and unstructured data. Text lines are used for commands from clients and responses from servers. Unstructured data is sent when a client wants to store or retrieve data. The server will transmit back unstructured data in exactly the same way it received it, as a byte stream. The server doesn't care about byte order issues in unstructured data and isn't aware of them. There are no limitations on characters that may appear in unstructured data; however, the reader of such data (either a client or a server) will always know, from a preceding text line, the exact length of the data block being transmitted. Text lines are always terminated by \r\n. Unstructured data is _also_ terminated by \r\n, even though \r, \n or any other 8-bit characters may also appear inside the data. Therefore, when a client retrieves data from a server, it must use the length of the data block (which it will be provided with) to determine where the data block ends, and not the fact that \r\n follows the end of the data block, even though it does. Keys ---- Data stored by memcached is identified with the help of a key. A key is a text string which should uniquely identify the data for clients that are interested in storing and retrieving it. Currently the length limit of a key is set at 250 characters (of course, normally clients wouldn't need to use such long keys); the key must not include control characters or whitespace. Commands -------- There are three types of commands. Storage commands (there are six: "set", "add", "replace", "append" "prepend" and "cas") ask the server to store some data identified by a key. The client sends a command line, and then a data block; after that the client expects one line of response, which will indicate success or failure. Retrieval commands (there are two: "get" and "gets") ask the server to retrieve data corresponding to a set of keys (one or more keys in one request). The client sends a command line, which includes all the requested keys; after that for each item the server finds it sends to the client one response line with information about the item, and one data block with the item's data; this continues until the server finished with the "END" response line. All other commands don't involve unstructured data. In all of them, the client sends one command line, and expects (depending on the command) either one line of response, or several lines of response ending with "END" on the last line. A command line always starts with the name of the command, followed by parameters (if any) delimited by whitespace. Command names are lower-case and are case-sensitive. Expiration times ---------------- Some commands involve a client sending some kind of expiration time (relative to an item or to an operation requested by the client) to the server. In all such cases, the actual value sent may either be Unix time (number of seconds since January 1, 1970, as a 32-bit value), or a number of seconds starting from current time. In the latter case, this number of seconds may not exceed 60*60*24*30 (number of seconds in 30 days); if the number sent by a client is larger than that, the server will consider it to be real Unix time value rather than an offset from current time. Note that a TTL of 1 will sometimes immediately expire. Time is internally updated on second boundaries, which makes expiration time roughly +/- 1s. This more proportionally affects very low TTL's. Error strings ------------- Each command sent by a client may be answered with an error string from the server. These error strings come in three types: - "ERROR\r\n" means the client sent a nonexistent command name. - "CLIENT_ERROR \r\n" means some sort of client error in the input line, i.e. the input doesn't conform to the protocol in some way. is a human-readable error string. - "SERVER_ERROR \r\n" means some sort of server error prevents the server from carrying out the command. is a human-readable error string. In cases of severe server errors, which make it impossible to continue serving the client (this shouldn't normally happen), the server will close the connection after sending the error line. This is the only case in which the server closes a connection to a client. In the descriptions of individual commands below, these error lines are not again specifically mentioned, but clients must allow for their possibility. Storage commands ---------------- First, the client sends a command line which looks like this: [noreply]\r\n cas [noreply]\r\n - is "set", "add", "replace", "append" or "prepend" "set" means "store this data". "add" means "store this data, but only if the server *doesn't* already hold data for this key". "replace" means "store this data, but only if the server *does* already hold data for this key". "append" means "add this data to an existing key after existing data". "prepend" means "add this data to an existing key before existing data". The append and prepend commands do not accept flags or exptime. They update existing data portions, and ignore new flag and exptime settings. "cas" is a check and set operation which means "store this data but only if no one else has updated since I last fetched it." - is the key under which the client asks to store the data - is an arbitrary 16-bit unsigned integer (written out in decimal) that the server stores along with the data and sends back when the item is retrieved. Clients may use this as a bit field to store data-specific information; this field is opaque to the server. Note that in memcached 1.2.1 and higher, flags may be 32-bits, instead of 16, but you might want to restrict yourself to 16 bits for compatibility with older versions. - is expiration time. If it's 0, the item never expires (although it may be deleted from the cache to make place for other items). If it's non-zero (either Unix time or offset in seconds from current time), it is guaranteed that clients will not be able to retrieve this item after the expiration time arrives (measured by server time). If a negative value is given the item is immediately expired. - is the number of bytes in the data block to follow, *not* including the delimiting \r\n. may be zero (in which case it's followed by an empty data block). - is a unique 64-bit value of an existing entry. Clients should use the value returned from the "gets" command when issuing "cas" updates. - "noreply" optional parameter instructs the server to not send the reply. NOTE: if the request line is malformed, the server can't parse "noreply" option reliably. In this case it may send the error to the client, and not reading it on the client side will break things. Client should construct only valid requests. After this line, the client sends the data block: \r\n - is a chunk of arbitrary 8-bit data of length from the previous line. After sending the command line and the data block the client awaits the reply, which may be: - "STORED\r\n", to indicate success. - "NOT_STORED\r\n" to indicate the data was not stored, but not because of an error. This normally means that the condition for an "add" or a "replace" command wasn't met. - "EXISTS\r\n" to indicate that the item you are trying to store with a "cas" command has been modified since you last fetched it. - "NOT_FOUND\r\n" to indicate that the item you are trying to store with a "cas" command did not exist. Retrieval command: ------------------ The retrieval commands "get" and "gets" operate like this: get *\r\n gets *\r\n - * means one or more key strings separated by whitespace. After this command, the client expects zero or more items, each of which is received as a text line followed by a data block. After all the items have been transmitted, the server sends the string "END\r\n" to indicate the end of response. Each item sent by the server looks like this: VALUE []\r\n \r\n - is the key for the item being sent - is the flags value set by the storage command - is the length of the data block to follow, *not* including its delimiting \r\n - is a unique 64-bit integer that uniquely identifies this specific item. - is the data for this item. If some of the keys appearing in a retrieval request are not sent back by the server in the item list this means that the server does not hold items with such keys (because they were never stored, or stored but deleted to make space for more items, or expired, or explicitly deleted by a client). Deletion -------- The command "delete" allows for explicit deletion of items: delete [noreply]\r\n - is the key of the item the client wishes the server to delete - "noreply" optional parameter instructs the server to not send the reply. See the note in Storage commands regarding malformed requests. The response line to this command can be one of: - "DELETED\r\n" to indicate success - "NOT_FOUND\r\n" to indicate that the item with this key was not found. See the "flush_all" command below for immediate invalidation of all existing items. Increment/Decrement ------------------- Commands "incr" and "decr" are used to change data for some item in-place, incrementing or decrementing it. The data for the item is treated as decimal representation of a 64-bit unsigned integer. If the current data value does not conform to such a representation, the incr/decr commands return an error (memcached <= 1.2.6 treated the bogus value as if it were 0, leading to confusion). Also, the item must already exist for incr/decr to work; these commands won't pretend that a non-existent key exists with value 0; instead, they will fail. The client sends the command line: incr [noreply]\r\n or decr [noreply]\r\n - is the key of the item the client wishes to change - is the amount by which the client wants to increase/decrease the item. It is a decimal representation of a 64-bit unsigned integer. - "noreply" optional parameter instructs the server to not send the reply. See the note in Storage commands regarding malformed requests. The response will be one of: - "NOT_FOUND\r\n" to indicate the item with this value was not found - \r\n , where is the new value of the item's data, after the increment/decrement operation was carried out. Note that underflow in the "decr" command is caught: if a client tries to decrease the value below 0, the new value will be 0. Overflow in the "incr" command will wrap around the 64 bit mark. Note also that decrementing a number such that it loses length isn't guaranteed to decrement its returned length. The number MAY be space-padded at the end, but this is purely an implementation optimization, so you also shouldn't rely on that. Touch ----- The "touch" command is used to update the expiration time of an existing item without fetching it. touch [noreply]\r\n - is the key of the item the client wishes the server to touch - is expiration time. Works the same as with the update commands (set/add/etc). This replaces the existing expiration time. If an existing item were to expire in 10 seconds, but then was touched with an expiration time of "20", the item would then expire in 20 seconds. - "noreply" optional parameter instructs the server to not send the reply. See the note in Storage commands regarding malformed requests. The response line to this command can be one of: - "TOUCHED\r\n" to indicate success - "NOT_FOUND\r\n" to indicate that the item with this key was not found. Get And Touch ----- The "gat" and "gats" commands are used to fetch items and update the expiration time of an existing items. gat *\r\n gats *\r\n - is expiration time. - * means one or more key strings separated by whitespace. After this command, the client expects zero or more items, each of which is received as a text line followed by a data block. After all the items have been transmitted, the server sends the string "END\r\n" to indicate the end of response. Each item sent by the server looks like this: VALUE []\r\n \r\n - is the key for the item being sent - is the flags value set by the storage command - is the length of the data block to follow, *not* including its delimiting \r\n - is a unique 64-bit integer that uniquely identifies this specific item. - is the data for this item. Slabs Reassign -------------- NOTE: This command is subject to change as of this writing. The slabs reassign command is used to redistribute memory once a running instance has hit its limit. It might be desirable to have memory laid out differently than was automatically assigned after the server started. slabs reassign \r\n - is an id number for the slab class to steal a page from A source class id of -1 means "pick from any valid class" - is an id number for the slab class to move a page to The response line could be one of: - "OK" to indicate the page has been scheduled to move - "BUSY [message]" to indicate a page is already being processed, try again later. - "BADCLASS [message]" a bad class id was specified - "NOSPARE [message]" source class has no spare pages - "NOTFULL [message]" dest class must be full to move new pages to it - "UNSAFE [message]" source class cannot move a page right now - "SAME [message]" must specify different source/dest ids. Slabs Automove -------------- NOTE: This command is subject to change as of this writing. The slabs automove command enables a background thread which decides on its own when to move memory between slab classes. Its implementation and options will likely be in flux for several versions. See the wiki/mailing list for more details. The automover can be enabled or disabled at runtime with this command. slabs automove <0|1> - 0|1|2 is the indicator on whether to enable the slabs automover or not. The response should always be "OK\r\n" - <0> means to set the thread on standby - <1> means to return pages to a global pool when there are more than 2 pages worth of free chunks in a slab class. Pages are then re-assigned back into other classes as-needed. - <2> is a highly aggressive mode which causes pages to be moved every time there is an eviction. It is not recommended to run for very long in this mode unless your access patterns are very well understood. LRU Tuning ---------- Memcached supports multiple LRU algorithms, with a few tunables. Effort is made to have sane defaults however you are able to tune while the daemon is running. The traditional model is "flat" mode, which is a single LRU chain per slab class. The newer (with `-o modern` or `-o lru_maintainer`) is segmented into HOT, WARM, COLD. There is also a TEMP LRU. See doc/new_lru.txt for details. lru