From fc8393f95e7bf92f38f2da6fe9199a4fad0bcaae Mon Sep 17 00:00:00 2001 From: WangRunji Date: Thu, 21 Feb 2019 01:37:20 +0800 Subject: [PATCH] add Biscuit user programs from origin --- biscuit/CMakeLists.txt | 52 + biscuit/c/bimage.c | 131 + biscuit/c/bmgc.c | 136 + biscuit/c/bmgc2.c | 231 ++ biscuit/c/bmopen.c | 41 + biscuit/c/bmread.c | 33 + biscuit/c/bmsmallfile.c | 223 ++ biscuit/c/bmwrite.c | 41 + biscuit/c/cat.c | 23 + biscuit/c/cksum.c | 41 + biscuit/c/cls.c | 14 + biscuit/c/conio.c | 13 + biscuit/c/ebizzy.c | 564 +++ biscuit/c/echo.c | 9 + biscuit/c/error/bloat.c | 67 + biscuit/c/error/dlmalloc.c | 6286 ++++++++++++++++++++++++++++++ biscuit/c/fault.c | 19 + biscuit/c/fault2.c | 29 + biscuit/c/fcgi.c | 44 + biscuit/c/fork.c | 33 + biscuit/c/fsbigwrite.c | 48 + biscuit/c/fscreat.c | 43 + biscuit/c/fsfree.c | 74 + biscuit/c/fslink.c | 43 + biscuit/c/fsmkdir.c | 26 + biscuit/c/fstest.c | 54 + biscuit/c/fsunlink.c | 53 + biscuit/c/fswrite.c | 54 + biscuit/c/fweb.c | 120 + biscuit/c/getpid.c | 13 + biscuit/c/goodcit.c | 47 + biscuit/c/halp.c | 322 ++ biscuit/c/head.c | 31 + biscuit/c/hello.c | 14 + biscuit/c/include/arpa/inet.h | 3 + biscuit/c/include/assert.h | 17 + biscuit/c/include/ctype.h | 3 + biscuit/c/include/dirent.h | 3 + biscuit/c/include/err.h | 3 + biscuit/c/include/errno.h | 3 + biscuit/c/include/fcntl.h | 3 + biscuit/c/include/glob.h | 3 + biscuit/c/include/grp.h | 3 + biscuit/c/include/inttypes.h | 3 + biscuit/c/include/limits.h | 3 + biscuit/c/include/litc.h | 1008 +++++ biscuit/c/include/littypes.h | 109 + biscuit/c/include/locale.h | 3 + biscuit/c/include/malloc.h | 3 + biscuit/c/include/math.h | 3 + biscuit/c/include/netdb.h | 3 + biscuit/c/include/netinet/in.h | 3 + biscuit/c/include/netinet/tcp.h | 3 + biscuit/c/include/poll.h | 3 + biscuit/c/include/pthread.h | 3 + biscuit/c/include/pwd.h | 3 + biscuit/c/include/sched.h | 3 + biscuit/c/include/setjmp.h | 3 + biscuit/c/include/signal.h | 3 + biscuit/c/include/spawn.h | 3 + biscuit/c/include/stdarg.h | 3 + biscuit/c/include/stddef.h | 3 + biscuit/c/include/stdint.h | 3 + biscuit/c/include/stdio.h | 3 + biscuit/c/include/stdlib.h | 3 + biscuit/c/include/string.h | 3 + biscuit/c/include/strings.h | 3 + biscuit/c/include/sys/ioctl.h | 3 + biscuit/c/include/sys/mman.h | 3 + biscuit/c/include/sys/param.h | 3 + biscuit/c/include/sys/resource.h | 3 + biscuit/c/include/sys/select.h | 3 + biscuit/c/include/sys/socket.h | 3 + biscuit/c/include/sys/stat.h | 3 + biscuit/c/include/sys/time.h | 3 + biscuit/c/include/sys/types.h | 3 + biscuit/c/include/sys/uio.h | 3 + biscuit/c/include/sys/un.h | 3 + biscuit/c/include/sys/utsname.h | 3 + biscuit/c/include/sys/wait.h | 3 + biscuit/c/include/syslog.h | 3 + biscuit/c/include/time.h | 3 + biscuit/c/include/unistd.h | 3 + biscuit/c/init.c | 93 + biscuit/c/kill.c | 10 + biscuit/c/killtest.c | 17 + biscuit/c/largefile.c | 195 + biscuit/c/less.c | 36 + biscuit/c/libs/litc.c | 3779 ++++++++++++++++++ biscuit/c/linker.ld | 13 + biscuit/c/lnc.c | 176 + biscuit/c/ls.c | 116 + biscuit/c/lsh.c | 208 + biscuit/c/mkdir.c | 12 + biscuit/c/mknodtest.c | 19 + biscuit/c/mmapbench.c | 41 + biscuit/c/mmaptest.c | 35 + biscuit/c/mv.c | 15 + biscuit/c/pipetest.c | 69 + biscuit/c/pthtests.c | 146 + biscuit/c/pwd.c | 11 + biscuit/c/reboot.c | 11 + biscuit/c/rmtree.c | 60 + biscuit/c/rshd.c | 66 + biscuit/c/sfork.c | 808 ++++ biscuit/c/sleep.c | 14 + biscuit/c/smallfile.c | 103 + biscuit/c/sockettest.c | 269 ++ biscuit/c/stress.c | 62 + biscuit/c/sync.c | 6 + biscuit/c/thtests.c | 92 + biscuit/c/time.c | 218 ++ biscuit/c/touch.c | 17 + biscuit/c/true.c | 4 + biscuit/c/uname.c | 80 + biscuit/c/unlink.c | 18 + biscuit/c/usertests.c | 3870 ++++++++++++++++++ biscuit/cxx/algorithm | 439 +++ biscuit/cxx/bind.hh | 49 + biscuit/cxx/compiler.h | 18 + biscuit/cxx/cstring | 5 + biscuit/cxx/cxxrt.cc | 180 + biscuit/cxx/cxxrtbegin.S | 3 + biscuit/cxx/cxxrtend.S | 4 + biscuit/cxx/distribution.hh | 163 + biscuit/cxx/functional | 56 + biscuit/cxx/host_hdrs.hh | 16 + biscuit/cxx/iterator | 33 + biscuit/cxx/libutil.cc | 112 + biscuit/cxx/libutil.h | 35 + biscuit/cxx/linker.ld | 18 + biscuit/cxx/linux/.keep | 0 biscuit/cxx/mail-deliver.cc | 126 + biscuit/cxx/mail-enqueue.cc | 163 + biscuit/cxx/mail-qman.cc | 349 ++ biscuit/cxx/mailbench.cc | 369 ++ biscuit/cxx/max_align.h | 6 + biscuit/cxx/memory | 352 ++ biscuit/cxx/new | 52 + biscuit/cxx/queue | 129 + biscuit/cxx/shutil.cc | 115 + biscuit/cxx/shutil.h | 10 + biscuit/cxx/spinbarrier.hh | 62 + biscuit/cxx/stdexcept | 90 + biscuit/cxx/string | 665 ++++ biscuit/cxx/thread | 145 + biscuit/cxx/threads.cc | 72 + biscuit/cxx/tuple | 114 + biscuit/cxx/utility | 71 + biscuit/cxx/vector | 291 ++ biscuit/cxx/vector.hh | 237 ++ 151 files changed, 25482 insertions(+) create mode 100644 biscuit/CMakeLists.txt create mode 100644 biscuit/c/bimage.c create mode 100644 biscuit/c/bmgc.c create mode 100644 biscuit/c/bmgc2.c create mode 100644 biscuit/c/bmopen.c create mode 100644 biscuit/c/bmread.c create mode 100644 biscuit/c/bmsmallfile.c create mode 100644 biscuit/c/bmwrite.c create mode 100644 biscuit/c/cat.c create mode 100644 biscuit/c/cksum.c create mode 100644 biscuit/c/cls.c create mode 100644 biscuit/c/conio.c create mode 100644 biscuit/c/ebizzy.c create mode 100644 biscuit/c/echo.c create mode 100644 biscuit/c/error/bloat.c create mode 100644 biscuit/c/error/dlmalloc.c create mode 100644 biscuit/c/fault.c create mode 100644 biscuit/c/fault2.c create mode 100644 biscuit/c/fcgi.c create mode 100644 biscuit/c/fork.c create mode 100644 biscuit/c/fsbigwrite.c create mode 100644 biscuit/c/fscreat.c create mode 100644 biscuit/c/fsfree.c create mode 100644 biscuit/c/fslink.c create mode 100644 biscuit/c/fsmkdir.c create mode 100644 biscuit/c/fstest.c create mode 100644 biscuit/c/fsunlink.c create mode 100644 biscuit/c/fswrite.c create mode 100644 biscuit/c/fweb.c create mode 100644 biscuit/c/getpid.c create mode 100644 biscuit/c/goodcit.c create mode 100644 biscuit/c/halp.c create mode 100644 biscuit/c/head.c create mode 100644 biscuit/c/hello.c create mode 100644 biscuit/c/include/arpa/inet.h create mode 100644 biscuit/c/include/assert.h create mode 100644 biscuit/c/include/ctype.h create mode 100644 biscuit/c/include/dirent.h create mode 100644 biscuit/c/include/err.h create mode 100644 biscuit/c/include/errno.h create mode 100644 biscuit/c/include/fcntl.h create mode 100644 biscuit/c/include/glob.h create mode 100644 biscuit/c/include/grp.h create mode 100644 biscuit/c/include/inttypes.h create mode 100644 biscuit/c/include/limits.h create mode 100644 biscuit/c/include/litc.h create mode 100644 biscuit/c/include/littypes.h create mode 100644 biscuit/c/include/locale.h create mode 100644 biscuit/c/include/malloc.h create mode 100644 biscuit/c/include/math.h create mode 100644 biscuit/c/include/netdb.h create mode 100644 biscuit/c/include/netinet/in.h create mode 100644 biscuit/c/include/netinet/tcp.h create mode 100644 biscuit/c/include/poll.h create mode 100644 biscuit/c/include/pthread.h create mode 100644 biscuit/c/include/pwd.h create mode 100644 biscuit/c/include/sched.h create mode 100644 biscuit/c/include/setjmp.h create mode 100644 biscuit/c/include/signal.h create mode 100644 biscuit/c/include/spawn.h create mode 100644 biscuit/c/include/stdarg.h create mode 100644 biscuit/c/include/stddef.h create mode 100644 biscuit/c/include/stdint.h create mode 100644 biscuit/c/include/stdio.h create mode 100644 biscuit/c/include/stdlib.h create mode 100644 biscuit/c/include/string.h create mode 100644 biscuit/c/include/strings.h create mode 100644 biscuit/c/include/sys/ioctl.h create mode 100644 biscuit/c/include/sys/mman.h create mode 100644 biscuit/c/include/sys/param.h create mode 100644 biscuit/c/include/sys/resource.h create mode 100644 biscuit/c/include/sys/select.h create mode 100644 biscuit/c/include/sys/socket.h create mode 100644 biscuit/c/include/sys/stat.h create mode 100644 biscuit/c/include/sys/time.h create mode 100644 biscuit/c/include/sys/types.h create mode 100644 biscuit/c/include/sys/uio.h create mode 100644 biscuit/c/include/sys/un.h create mode 100644 biscuit/c/include/sys/utsname.h create mode 100644 biscuit/c/include/sys/wait.h create mode 100644 biscuit/c/include/syslog.h create mode 100644 biscuit/c/include/time.h create mode 100644 biscuit/c/include/unistd.h create mode 100644 biscuit/c/init.c create mode 100644 biscuit/c/kill.c create mode 100644 biscuit/c/killtest.c create mode 100644 biscuit/c/largefile.c create mode 100644 biscuit/c/less.c create mode 100644 biscuit/c/libs/litc.c create mode 100644 biscuit/c/linker.ld create mode 100644 biscuit/c/lnc.c create mode 100644 biscuit/c/ls.c create mode 100644 biscuit/c/lsh.c create mode 100644 biscuit/c/mkdir.c create mode 100644 biscuit/c/mknodtest.c create mode 100644 biscuit/c/mmapbench.c create mode 100644 biscuit/c/mmaptest.c create mode 100644 biscuit/c/mv.c create mode 100644 biscuit/c/pipetest.c create mode 100644 biscuit/c/pthtests.c create mode 100644 biscuit/c/pwd.c create mode 100644 biscuit/c/reboot.c create mode 100644 biscuit/c/rmtree.c create mode 100644 biscuit/c/rshd.c create mode 100644 biscuit/c/sfork.c create mode 100644 biscuit/c/sleep.c create mode 100644 biscuit/c/smallfile.c create mode 100644 biscuit/c/sockettest.c create mode 100644 biscuit/c/stress.c create mode 100644 biscuit/c/sync.c create mode 100644 biscuit/c/thtests.c create mode 100644 biscuit/c/time.c create mode 100644 biscuit/c/touch.c create mode 100644 biscuit/c/true.c create mode 100644 biscuit/c/uname.c create mode 100644 biscuit/c/unlink.c create mode 100644 biscuit/c/usertests.c create mode 100644 biscuit/cxx/algorithm create mode 100644 biscuit/cxx/bind.hh create mode 100644 biscuit/cxx/compiler.h create mode 100644 biscuit/cxx/cstring create mode 100644 biscuit/cxx/cxxrt.cc create mode 100644 biscuit/cxx/cxxrtbegin.S create mode 100644 biscuit/cxx/cxxrtend.S create mode 100644 biscuit/cxx/distribution.hh create mode 100644 biscuit/cxx/functional create mode 100644 biscuit/cxx/host_hdrs.hh create mode 100644 biscuit/cxx/iterator create mode 100644 biscuit/cxx/libutil.cc create mode 100644 biscuit/cxx/libutil.h create mode 100644 biscuit/cxx/linker.ld create mode 100644 biscuit/cxx/linux/.keep create mode 100644 biscuit/cxx/mail-deliver.cc create mode 100644 biscuit/cxx/mail-enqueue.cc create mode 100644 biscuit/cxx/mail-qman.cc create mode 100644 biscuit/cxx/mailbench.cc create mode 100644 biscuit/cxx/max_align.h create mode 100644 biscuit/cxx/memory create mode 100644 biscuit/cxx/new create mode 100644 biscuit/cxx/queue create mode 100644 biscuit/cxx/shutil.cc create mode 100644 biscuit/cxx/shutil.h create mode 100644 biscuit/cxx/spinbarrier.hh create mode 100644 biscuit/cxx/stdexcept create mode 100644 biscuit/cxx/string create mode 100644 biscuit/cxx/thread create mode 100644 biscuit/cxx/threads.cc create mode 100644 biscuit/cxx/tuple create mode 100644 biscuit/cxx/utility create mode 100644 biscuit/cxx/vector create mode 100644 biscuit/cxx/vector.hh diff --git a/biscuit/CMakeLists.txt b/biscuit/CMakeLists.txt new file mode 100644 index 0000000..238e1be --- /dev/null +++ b/biscuit/CMakeLists.txt @@ -0,0 +1,52 @@ +cmake_minimum_required(VERSION 3.0) + +project(biscuit_user) +enable_language(ASM) + +# Path +aux_source_directory(c SRCS) +aux_source_directory(c/libs LIBS) +include_directories(c/include) +set(EXECUTABLE_OUTPUT_PATH ${ARCH}) + +# Toolchain +if (${ARCH} STREQUAL i386) + if(APPLE) + set(PREFIX i386-elf-) + endif () + set(CMAKE_C_FLAGS "-m32") +elseif (${ARCH} STREQUAL x86_64) + if(APPLE) + set(PREFIX x86_64-elf-) + endif () + set(CMAKE_C_FLAGS "-m64") +elseif (${ARCH} STREQUAL riscv32) + set(PREFIX riscv64-unknown-elf-) + set(CMAKE_C_FLAGS "-march=rv32imac -mabi=ilp32 -mcmodel=medany") +elseif (${ARCH} STREQUAL riscv64) + set(PREFIX riscv64-unknown-elf-) + set(CMAKE_C_FLAGS "-march=rv64imac -mabi=lp64 -mcmodel=medany") +elseif (${ARCH} STREQUAL aarch64) + set(PREFIX aarch64-none-elf-) + set(CMAKE_C_FLAGS "-mgeneral-regs-only") + set(LINK_FLAGS "-Ttext 0xffff000000000000") +else() + message("Unsupported arch: ${ARCH}") +endif () +set(CMAKE_ASM_COMPILER ${PREFIX}gcc) +set(CMAKE_C_COMPILER ${PREFIX}gcc) +set(CMAKE_RANLIB ${PREFIX}ranlib) +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Werror -MMD -MP -O -g -ffreestanding -nostdlib -nostdinc -fno-builtin -mno-red-zone -fno-stack-protector -fPIC -std=gnu11") +set(CMAKE_ASM_FLAGS ${CMAKE_C_FLAGS}) +set(CMAKE_C_LINK_FLAGS "${LINK_FLAGS} -nostdlib") # override default value to get rid of '-Wl,-search_paths_first -Wl,-headerpad_max_install_names' +set(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS) # override default value to get rid of '-rdynamic' on Linux + +# Library +add_library(ulib ${LIBS}) + +# Execuatble +foreach (PATH ${SRCS}) + get_filename_component(NAME ${PATH} NAME_WE) + add_executable(${NAME} ${PATH}) + target_link_libraries(${NAME} ulib) +endforeach () diff --git a/biscuit/c/bimage.c b/biscuit/c/bimage.c new file mode 100644 index 0000000..0e84763 --- /dev/null +++ b/biscuit/c/bimage.c @@ -0,0 +1,131 @@ +#include + +static int lstn(uint16_t lport) +{ + fprintf(stderr, "listen on port %d\n", (int)lport); + + int s = socket(AF_INET, SOCK_STREAM, 0); + if (s == -1) + err(-1, "socket"); + struct sockaddr_in sin; + sin.sin_family = AF_INET; + sin.sin_port = htons(lport); + sin.sin_addr.s_addr = htonl(INADDR_ANY); + if (bind(s, (struct sockaddr *)&sin, sizeof(sin)) == -1) + err(-1, "bind"); + if (listen(s, 10) == -1) + err(-1, "listen"); + int ret; + socklen_t slen = sizeof(sin); + if ((ret = accept(s, (struct sockaddr *)&sin, &slen)) == -1) + err(-1, "accept"); + if (close(s)) + err(-1, "close"); + uint a = (sin.sin_addr.s_addr >> 0) & 0xff; + uint b = (sin.sin_addr.s_addr >> 8) & 0xff; + uint c = (sin.sin_addr.s_addr >> 16) & 0xff; + uint d = (sin.sin_addr.s_addr >> 24) & 0xff; + fprintf(stderr, "connection from %u.%u.%u.%u:%d\n", a, b, c, d, + (int)ntohs(sin.sin_port)); + return ret; +} + +// latency debugging code +//ulong allw[100]; +// +//void dump(ulong *worst) +//{ +// for (int i = 0; i < 100; i++) +// printf("%d: %lu\n", i, worst[i]); +// printf("\n"); +//} +// +//void insert(ulong *worst, ulong n) +//{ +// if (n < worst[0]) +// return; +// if (n > worst[99]) { +// int i = 99; +// memmove(worst, worst + 1, i*sizeof(worst[0])); +// worst[i] = n; +// return; +// } +// for (int i = 0; i < 99; i++) { +// if (n > worst[i] && n <= worst[i+1]) { +// memmove(worst, worst + 1, i*sizeof(worst[0])); +// worst[i] = n; +// return; +// } +// } +//} + +int main(int argc, char **argv) +{ + // bring all file metadaa and binary text/data into the page cache in + // hopes that the kernel won't mix blocks from the new image with + // blocks from the old. + int status; + switch (fork()) { + case 0: + { + int fd = open("/dev/null", O_WRONLY); + if (fd == -1) + err(-1, "open"); + if (dup2(fd, 1) == -1) + err(-1, "dup2"); + close(fd); + char * const args[] = {"/bin/cat", "/bin/bimage", NULL}; + execv(args[0], args); + err(-1, "execv"); + } + case -1: + err(-1, "fork"); + default: + if (wait(&status) == -1) + err(-1, "wait"); + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) + errx(-1, "child failed"); + } + + int fd; + if ((fd = open("/dev/rsd0c", O_WRONLY)) == -1) + err(-1, "open"); + + int s = lstn(31338); + + sync(); + + const int blksz = 4096; + char buf[blksz]; + size_t did = 0; + int mb = 1; + for (;;) { + ssize_t bs = 0; + ssize_t r = 0; + while (bs != blksz && + (r = read(s, buf + bs, sizeof(buf) - bs)) > 0) + bs += r; + if (r == -1) + err(-1, "read"); + if (bs == 0) + break; + if ((did % blksz) != 0) + fprintf(stderr, "slow write\n"); + if (write(fd, buf, bs) != bs) + err(-1, "write"); + did += bs; + if (did >> 20 >= mb) { + fprintf(stderr, "%dMB\n", mb); + mb += 1; + } + } + if (fsync(fd) == -1) + err(-1, "fsync"); + if (close(s) == -1) + err(-1, "close"); + if (close(fd) == -1) + err(-1, "close"); + fprintf(stderr, "wrote %zu bytes. rebooting.\n", did); + reboot(); + return 0; +} diff --git a/biscuit/c/bmgc.c b/biscuit/c/bmgc.c new file mode 100644 index 0000000..0cc2909 --- /dev/null +++ b/biscuit/c/bmgc.c @@ -0,0 +1,136 @@ +#include + +static long +_fetch(long n) +{ + long ret; + if ((ret = sys_info(n)) == -1) + errx(-1, "sysinfo"); + return ret; +} + +__attribute__((unused)) +static long +gccount(void) +{ + return _fetch(SINFO_GCCOUNT); +} + +__attribute__((unused)) +static long +gctotns(void) +{ + return _fetch(SINFO_GCPAUSENS); +} + +__attribute__((unused)) +static long +gcheapuse(void) +{ + return _fetch(SINFO_GCHEAPSZ); +} + +__attribute__((noreturn)) +void usage(const char *msg) +{ + if (msg != NULL) + printf("%s\n\n", msg); + printf("usage:\n"); + printf("%s [-mvSg] [-h ] [-s ] [-w ] [-n ] " + "[-l ]\n", __progname); + printf("where:\n"); + printf("-d bloat kernel heap with vnodes\n"); + printf("-g force kernel GC\n"); + printf("-h set kernel heap minimum to int MB\n"); + printf("-H set kernel heap growth factor as int\n"); + printf("-l set kernel heap reservation max to int MB\n"); + printf("-s / set GC mutator assist ratio to /\n\n"); + exit(-1); +} + +int main(int argc, char **argv) +{ + long kheap = 0, growperc = 0, hmax = 0; + int newthing = 0, dogc = 0; + long anum = 0, adenom = 0; + + int c; + while ((c = getopt(argc, argv, "gdH:h:s:l:")) != -1) { + switch (c) { + case 'd': + newthing = 1; + break; + case 'g': + dogc = 1; + break; + case 'h': + kheap = strtol(optarg, NULL, 0); + break; + case 'H': + growperc = strtol(optarg, NULL, 0); + break; + case 's': + { + char *end; + anum = strtol(optarg, &end, 0); + if (*end != '/') + usage("invalid ratio"); + end++; + adenom = strtol(end, NULL, 0); + if (anum <= 0 || adenom <= 0) + usage("invalid ratio"); + break; + } + case 'l': + hmax = strtol(optarg, NULL, 0); + if (hmax == 0) + errx(-1, "must be non-zero"); + break; + default: + usage(NULL); + break; + } + } + if (optind != argc) + usage(NULL); + + if (newthing) { + printf("bloat kernel heap...\n"); + const long hack4 = 1 << 7; + if (sys_prof(hack4, 0, 0, 0) == -1) + err(-1, "hack4"); + pause(); + } + + if (kheap) { + const long hack = 1ul << 4; + if (sys_prof(hack, kheap, 0, 0) == -1) + err(-1, "sys prof"); + } + + if (growperc) { + const long hack2 = 1ul << 5; + if (sys_prof(hack2, growperc, 0, 0) == -1) + err(-1, "sys prof"); + } + + if (hmax) { + hmax <<= 20; + const long prof_hack5 = 1ul << 8; + if (sys_prof(prof_hack5, hmax, 0, 0) == -1) + err(-1, "sys prof"); + } + + if (adenom != 0) { + const long prof_hack6 = 1ul << 9; + if (sys_prof(prof_hack6, anum, adenom, 0) == -1) + err(-1, "sys prof"); + } + + if (dogc) { + _fetch(10); + printf("kernel heap use: %ld Mb\n", gcheapuse()/(1 << 20)); + } + + return 0; +} diff --git a/biscuit/c/bmgc2.c b/biscuit/c/bmgc2.c new file mode 100644 index 0000000..d36f5cd --- /dev/null +++ b/biscuit/c/bmgc2.c @@ -0,0 +1,231 @@ +#include + +static long +nowms(void) +{ + struct timeval tv; + if (gettimeofday(&tv, NULL)) + err(-1, "gettimeofday"); + return tv.tv_sec*1000 + tv.tv_usec/1000; +} + +static long +_fetch(long n) +{ + long ret; + if ((ret = sys_info(n)) < 0) + errx(-1, "sysinfo"); + return ret; +} + +static long +gccount(void) +{ + return _fetch(SINFO_GCCOUNT); +} + +static long +gctotns(void) +{ + return _fetch(SINFO_GCPAUSENS); +} + +static long +gcheapuse(void) +{ + return _fetch(SINFO_GCHEAPSZ); +} + +struct res_t { + long xput; + long longest; +}; + +__attribute__((noreturn)) +static void _workmmap(long endms, int resfd) +{ + long longest = 0; + long count = 0; + while (1) { + long st = nowms(); + if (st > endms) + break; + size_t sz = 4096 * 100; + void *m = mmap(NULL, sz, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANON, -1, 0); + if (m == MAP_FAILED) + err(-1, "mmap"); + if (munmap(m, sz)) + err(-1, "munmap"); + long tot = nowms() - st; + if (tot > longest) + longest = tot; + count++; + } + struct res_t rs = {.xput = count, .longest = longest}; + if (write(resfd, &rs, sizeof(rs)) != sizeof(rs)) + err(-1, "res write"); + exit(0); +} + +static void work(long wf, const long np) +{ + long secs = wf; + if (secs < 0) + secs = 1; + else if (secs > 60) + secs = 60; + + printf("working for %ld seconds with %ld processes...\n", secs, np); + + int resp[2]; + if (pipe(resp)) + err(-1, "pipe"); + + long bgcs = gccount(); + long bgcns = gctotns(); + + long endms = nowms() + secs*1000; + pid_t ps[np]; + int i; + for (i = 0; i < np; i++) { + if ((ps[i] = fork()) < 0) + errx(-1, "fork"); + else if (ps[i] == 0) { + close(resp[0]); + _workmmap(endms, resp[1]); + } + } + close(resp[1]); + + struct gcfrac_t gcf = gcfracst(); + //fake_sys(1); + + long longest = 0, totalxput = 0; + long longarr[np]; + for (i = 0; i < np; i++) { + int status; + if (wait(&status) < 0) + err(-1, "wait"); + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) + err(-1, "child failed"); + + struct res_t got; + if (read(resp[0], &got, sizeof(got)) != sizeof(got)) + err(-1, "res read"); + + totalxput += got.xput; + longarr[i] = got.longest; + if (got.longest > longest) + longest = got.longest; + } + + //fake_sys(0); + + long gcs = gccount() - bgcs; + long gcns = gctotns() - bgcns; + + long xput = secs > 0 ? totalxput/secs : 0; + printf("iterations/sec: %ld (%ld total)\n", xput, totalxput); + printf("CPU time GC'ing: %f%%\n", gcfracend(&gcf, NULL, NULL, NULL)); + printf("max latency: %ld ms\n", longest); + printf("each process' latency:\n"); + for (i = 0; i < np; i++) + printf(" %ld\n", longarr[i]); + printf("%ld gcs (%ld ms)\n", gcs, gcns/1000000); + printf("kernel heap use: %ld Mb\n", gcheapuse()/(1 << 20)); +} + +int _vnodes(long sf) +{ + size_t nf = 1000*sf; + printf("creating %zu vnodes...\n", nf); + size_t tenpct = nf/10; + size_t next = 1; + size_t n; + for (n = 0; n < nf; n++) { + int fd = open("dummy", O_CREAT | O_EXCL | O_RDWR, S_IRWXU); + if (fd < 0) + err(-1, "open"); + if (unlink("dummy")) + err(-1, "unlink"); + size_t cp = n/tenpct; + if (cp >= next) { + printf("%zu%%\n", cp*10); + next = cp + 1; + } + } + + return 0; +} + +__attribute__((noreturn)) +void usage(void) +{ + printf("usage:\n"); + printf("%s [-g] [-s ] [-w ] [-n ]\n", __progname); + printf("where:\n"); + printf("-g force kernel GC, then exit\n"); + printf("-s set scale factor to int\n"); + printf("-w set work factor to int\n"); + printf("-n set number of worker processes to int\n\n"); + exit(-1); +} + +int main(int argc, char **argv) +{ + long sf = 1, wf = 1, nprocs = 1; + int dogc = 0; + + int c; + while ((c = getopt(argc, argv, "n:gms:w:")) != -1) { + switch (c) { + case 'g': + dogc = 1; + break; + case 'n': + nprocs = strtol(optarg, NULL, 0); + break; + case 's': + sf = strtol(optarg, NULL, 0); + break; + case 'w': + wf = strtol(optarg, NULL, 0); + break; + default: + usage(); + break; + } + } + + if (optind != argc) + usage(); + + if (dogc) { + _fetch(10); + printf("kernel heap use: %ld Mb\n", gcheapuse()/(1 << 20)); + return 0; + } + + if (sf < 0) + sf = 1; + if (wf < 0) + wf = 1; + if (nprocs < 0) + nprocs = 1; + printf("scale factor: %ld, work factor: %ld, worker procs: %ld\n", + sf, wf, nprocs); + + long st = nowms(); + + int (*f)(long) = _vnodes; + if (f(sf)) + return -1; + + long tot = nowms() - st; + printf("setup: %ld ms\n", tot); + + work(wf, nprocs); + + return 0; +} diff --git a/biscuit/c/bmopen.c b/biscuit/c/bmopen.c new file mode 100644 index 0000000..fbcab55 --- /dev/null +++ b/biscuit/c/bmopen.c @@ -0,0 +1,41 @@ +#include + +int main(int argc, char **argv) +{ + char dname[] = "/d"; + + int depth = 10; + if (argc >= 2) + depth = atoi(argv[1]); + //printf("using depth: %d\n", depth); + int domkdir = 1; + if (argc >= 3) + domkdir = atoi(argv[2]); + //if (domkdir) + // printf("making dirs...\n"); + //else + // printf("skip dir make\n"); + + char boof[1024]; + char *p = boof; + char *end = boof + sizeof(boof); + int i; + for (i = 0; i < depth; i++) { + p += snprintf(p, end - p, "%s", dname); + if (domkdir) + mkdir(boof, 0); + } + char *fn = "/politicians"; + snprintf(p, end - p, "%s", fn); + + //printf("using \"%s\"...\n", boof); + open(boof, O_RDWR | O_CREAT, 0); + for (i = 0; i < 100; i++) { + int ret; + if ((ret = open(boof, O_RDONLY, 0)) < 0) + err(ret, "open"); + } + //printf("done\n"); + + return 0; +} diff --git a/biscuit/c/bmread.c b/biscuit/c/bmread.c new file mode 100644 index 0000000..b8063c8 --- /dev/null +++ b/biscuit/c/bmread.c @@ -0,0 +1,33 @@ +#include + +static char buf[512]; + +int main(int argc, char **argv) +{ + char *fn = "/bigfile.txt"; + int fd; + if ((fd = open(fn, O_RDONLY, 0)) < 0) + errx(-1, "open"); + + int i; + int ret; + int blks = 1000; + for (i = 0; i < blks; i++) { + printf("read %d\n", i); + size_t c = sizeof(buf); + size_t e = c; + if ((ret = read(fd, buf, c)) != e) { + printf("read failed %d\n", ret); + return -1; + } + int j; + for (j = 0; j < sizeof(buf); j++) + if (buf[j] != 'A') + errx(-1, "mismatch!"); + } + + if (close(fd)) + errx(-1, "close"); + + return 0; +} diff --git a/biscuit/c/bmsmallfile.c b/biscuit/c/bmsmallfile.c new file mode 100644 index 0000000..a0813c6 --- /dev/null +++ b/biscuit/c/bmsmallfile.c @@ -0,0 +1,223 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +static char buf[40960]; +static char name[100]; +static char *prog_name; +static char *topdir; + +#define NDIR 100 + +static char dir[100]; + +time_t ds; +time_t du; +struct timeval before, end; + +void rgettimeofday(struct timeval *h, void *p) +{ + time_t hi, lo, v; + asm( + "rdtsc\n" + : "=a"(lo), "=d"(hi) + : + :); + v = hi << 32 | lo; + if (h) + h->tv_usec = v; +} + +void start() +{ + rgettimeofday(&before, NULL); +} + + +long stop() +{ + rgettimeofday(&end, NULL); + long time = (end.tv_sec - before.tv_sec) * 1000000 + + (end.tv_usec - before.tv_usec); + return time; +} + + +void creat_dir() +{ + int i; + + //umask(0); + + for (i = 0; i < NDIR; i++) { + snprintf(dir, sizeof(dir), "%s/d%d", topdir, i); + if (mkdir(dir, 0777) != 0) { + printf("%s: mkdir %s failed %d\n", prog_name, dir, errno); + } + } +} + + + +void +creat_test(int n, int size) +{ + int i; + int r; + int fd; + int j; + + start(); + for (i = 0, j = 0; i < n; i ++) { + + snprintf(name, sizeof(name), "%s/d%d/g%d", topdir, j, i); + + if((fd = open(name, O_RDWR | O_CREAT | O_TRUNC, S_IRWXU)) < 0) { + printf("%s: create %s failed %d\n", prog_name, name, errno); + exit(1); + } + + if ((r = write(fd, buf, size)) < 0) { + printf("%s: write failed %d %d\n", prog_name, r, errno); + exit(1); + } + + if ((r = close(fd)) < 0) { + printf("%s: close failed %d %d\n", prog_name, r, errno); + } + + if ((i+1) % 100 == 0) j++; + + } + long time = stop(); + printf("%s: creat took %ld usec\n", prog_name, time); +} + + +void +write_test(char *name, int n, int size) +{ + int i; + int r; + int fd; + + if((fd = open(name, O_RDWR | O_CREAT | O_TRUNC, S_IRWXU)) < 0) { + printf("%s: create %d failed %d\n", prog_name, fd, errno); + exit(1); + } + + for (i = 0; i < n; i ++) { + if ((r = write(fd, buf, size)) < 0) { + printf("%s: write failed %d %d\n", prog_name, r, errno); + exit(1); + } + } + + if ((r = close(fd)) < 0) { + printf("%s: close failed %d %d\n", prog_name, r, errno); + } +} + + +void +flush_cache() +{ + write_test("t", 20000, 4096); +} + +void +read_test(int n, int size) +{ + int i; + int r; + int fd; + int j; + + start(); + for (i = 0, j = 0; i < n; i ++) { + + snprintf(name, sizeof(name), "%s/d%d/g%d", topdir, j, i); + + if((fd = open(name, O_RDONLY, 0)) < 0) { + printf("%s: open %d failed %d %d\n", prog_name, i, fd, errno); + exit(1); + } + + if ((r = read(fd, buf, size)) < 0) { + printf("%s: read failed %d %d\n", prog_name, r, errno); + exit(1); + } + + if ((r = close(fd)) < 0) { + printf("%s: close failed %d %d\n", prog_name, r, errno); + } + + if ((i+1) % 100 == 0) j++; + } + + long time = stop(); + printf("%s: read took %ld usec\n", prog_name, time); +} + +void +delete_test(int n) +{ + int i; + int r; + int j; + + start(); + for (i = 0, j = 0; i < n; i ++) { + + snprintf(name, sizeof(name), "%s/d%d/g%d", topdir, j, i); + + if ((r = unlink(name)) < 0) { + printf("%s: unlink failed %d\n", prog_name, r); + exit(1); + } + + if ((i+1) % 100 == 0) j++; + } + + long time = stop(); + printf("%s: unlink took %ld usec\n", prog_name, time); +} + + +int main(int argc, char *argv[]) +{ + int n; + int size; + + prog_name = argv[0]; + + if (argc != 4) { + printf("%s: %s num size dir\n", prog_name, prog_name); + exit(1); + } + + n = atoi(argv[1]); + size = atoi(argv[2]); + topdir = argv[3]; + + printf("%s %d %d %s\n", prog_name, n, size, topdir); + + creat_dir(); + + //printstats(topdir, 1); + + creat_test(n, size); + + //printstats(topdir, 0); + + read_test(n, size); + delete_test(n); + + unlink("t"); + return 0; +} diff --git a/biscuit/c/bmwrite.c b/biscuit/c/bmwrite.c new file mode 100644 index 0000000..1bac9bd --- /dev/null +++ b/biscuit/c/bmwrite.c @@ -0,0 +1,41 @@ +#include + +static char buf[512]; + +int main(int argc, char **argv) +{ + char *fn = "/bigdaddy.txt"; + int fd; + if ((fd = open(fn, O_RDWR, 0)) >= 0) { + printf("deleting %s\n", fn); + close(fd); + if (unlink(fn) < 0) + errx(-1, "unlink"); + } + + if ((fd = open(fn, O_RDWR | O_CREAT, 0)) < 0) + errx(-1, "open"); + + int i; + for (i = 0; i < sizeof(buf); i++) + buf[i] = 0x41 + (i / 1000); + + int ret; + //int blks = 1000; + int blks = 140; + for (i = 0; i < blks; i++) { + //printf("write %d\n", i); + size_t c = sizeof(buf); + size_t e = c; + if ((ret = write(fd, buf, c)) != e) { + printf("write failed %d\n", ret); + return -1; + } + } + + if (close(fd)) + errx(-1, "close"); + printf("done\n"); + + return 0; +} diff --git a/biscuit/c/cat.c b/biscuit/c/cat.c new file mode 100644 index 0000000..8bf9ad4 --- /dev/null +++ b/biscuit/c/cat.c @@ -0,0 +1,23 @@ +#include + +int main(int argc, char **argv) +{ + if (argc <= 1) + errx(-1, "usage: %s file1 [file2]...", argv[0]); + + int i; + for (i = 1; i < argc; i++) { + int fd; + fd = open(argv[i], O_RDONLY, 0); + if (fd < 0) + err(-1, "open"); + char buf[512]; + long ret; + while ((ret = read(fd, buf, sizeof(buf))) > 0) + write(1, buf, ret); + if (ret < 0) + err(-1, "read"); + close(fd); + } + return 0; +} diff --git a/biscuit/c/cksum.c b/biscuit/c/cksum.c new file mode 100644 index 0000000..ca9ea27 --- /dev/null +++ b/biscuit/c/cksum.c @@ -0,0 +1,41 @@ +#include +#include +#include +#include +#include +#include +#include + + +#define BSIZE 4096 +char buf[BSIZE]; +struct stat statbuf; + +int main(int argc, char *argv[]) +{ + if (argc != 2) { + printf("Usage: %s filename\n", argv[0]); + exit(-1); + } + + if(stat(argv[1], &statbuf) < 0) { + err(-1, "stat"); + } + int fd = open(argv[1], O_RDONLY, 0); + if (fd < 0) + err(-1, "fopen"); + ulong cksum = 0; + size_t tot = 0, r; + while ((r = read(fd, buf, sizeof(buf))) > 0) { + for (int i = 0; i < r; i++) { + cksum += buf[i]; + } + tot += r; + } + if (tot != statbuf.st_size) { + printf("didn't read enough data\n"); + exit(-1); + } + printf("cksum: %s = cksum %lx tot %ld\n", argv[1], cksum, tot); + return 0; +} diff --git a/biscuit/c/cls.c b/biscuit/c/cls.c new file mode 100644 index 0000000..cd195c3 --- /dev/null +++ b/biscuit/c/cls.c @@ -0,0 +1,14 @@ +#include + +int main(int argc, char **argv) +{ + char buf[81]; + int i; + for (i = 0; i < 79; i++) + buf[i] = ' '; + buf[sizeof(buf) - 2] = '\n'; + buf[sizeof(buf) - 1] = 0; + for (i = 0; i < 24; i++) + printf("%s", buf); + return 0; +} diff --git a/biscuit/c/conio.c b/biscuit/c/conio.c new file mode 100644 index 0000000..850481e --- /dev/null +++ b/biscuit/c/conio.c @@ -0,0 +1,13 @@ +#include + +int main(int argc, char **argv) +{ + while (1) { + char *ret = readline("input:"); + if (!ret) + break; + printf("got: %s\n", ret); + } + + return 0; +} diff --git a/biscuit/c/ebizzy.c b/biscuit/c/ebizzy.c new file mode 100644 index 0000000..ee2ac42 --- /dev/null +++ b/biscuit/c/ebizzy.c @@ -0,0 +1,564 @@ +/* + * Ebizzy - replicate a large ebusiness type of workload. + * + * Written by Valerie Henson + * + * Copyright 2006 - 2007 Intel Corporation + * Copyright 2007 Valerie Henson + * + * Rodrigo Rubira Branco - HP/BSD/Solaris port and some + * new features + * + * 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; version 2 of the License. + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + * + */ + +/* + * This program is designed to replicate a common web search app + * workload. A lot of search applications have the basic pattern: Get + * a request to find a certain record, index into the chunk of memory + * that contains it, copy it into another chunk, then look it up via + * binary search. The interesting parts of this workload are: + * + * Large working set + * Data alloc/copy/free cycle + * Unpredictable data access patterns + * + * Fiddle with the command line options until you get something + * resembling the kind of workload you want to investigate. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Command line options + */ + +static unsigned int always_mmap; +static unsigned int never_mmap; +static uint64_t chunks; +static unsigned int use_permissions; +static unsigned int use_holes; +static unsigned int random_size; +static uint64_t chunk_size; +static uint64_t seconds; +static unsigned int threads; +static unsigned int verbose; +static unsigned int linear; +static unsigned int touch_pages; +static unsigned int no_lib_memcpy; + +/* + * Other global variables + */ + +typedef size_t record_t; +static unsigned int record_size = sizeof (record_t); +static char *cmd; +static record_t **mem; +static char **hole_mem; +static unsigned int page_size; +static volatile int threads_go; +static uint64_t records_read; + +/* Global lock to serialize records aggregation */ +pthread_mutex_t records_count_lock; + +static void +usage(void) +{ + fprintf(stderr, "Usage: %s [options]\n" + "-T\t\t Just 'touch' the allocated pages\n" + "-l\t\t Don't use library memcpy\n" + "-m\t\t Always use mmap instead of malloc\n" + "-M\t\t Never use mmap\n" + "-n \t Number of memory chunks to allocate\n" + "-p \t\t Prevent mmap coalescing using permissions\n" + "-P \t\t Prevent mmap coalescing using holes\n" + "-R\t\t Randomize size of memory to copy and search\n" + "-s \t Size of memory chunks, in bytes\n" + "-S \t Number of seconds to run\n" + "-t \t Number of threads (2 * number cpus by default)\n" + "-v[v[v]]\t Be verbose (more v's for more verbose)\n" + "-z\t\t Linear search instead of binary search\n", + cmd); + exit(1); +} + +/* + * Read options, check them, and set some defaults. + */ + +static void +read_options(int argc, char *argv[]) +{ + int c; + + /* page_size = getpagesize(); */ + page_size = 4096; // Hard-code it for now. + + /* + * Set some defaults. These are currently tuned to run in a + * reasonable amount of time on my laptop. + * + * We could set the static defaults in the declarations, but + * then the defaults would be split between here and the top + * of the file, which is annoying. + */ + + /* threads = 2 * sysconf(_SC_NPROCESSORS_ONLN); */ + threads = 1; + chunks = 10; + chunk_size = record_size * 64 * 1024; + seconds = 10; + linear = 1; // Force linear search for now. + + /* On to option processing */ + + cmd = argv[0]; + + while ((c = getopt(argc, argv, "lmMn:pPRs:S:t:vzT")) != -1) { + switch (c) { + case 'l': + no_lib_memcpy = 1; + break; + case 'm': + always_mmap = 1; + break; + case 'M': + never_mmap = 1; + break; + case 'n': + chunks = strtoul(optarg, NULL, 0); + if (chunks == 0) + usage(); + break; + case 'p': + use_permissions = 1; + break; + case 'P': + use_holes = 1; + break; + case 'R': + random_size = 1; + break; + case 's': + chunk_size = strtoul(optarg, NULL, 0); + if (chunk_size == 0) + usage(); + break; + case 'S': + seconds = strtoul(optarg, NULL, 0); + if (seconds == 0) + usage(); + break; + case 't': + threads = atoi(optarg); + if (threads == 0) + usage(); + break; + case 'T': + touch_pages = 1; + break; + case 'v': + verbose++; + break; + case 'z': + linear = 1; + break; + default: + usage(); + } + } + + if (verbose) + printf("ebizzy 0.2\n" + "(C) 2006-7 Intel Corporation\n" + "(C) 2007 Valerie Henson \n"); + + if (verbose) { + printf("always_mmap %u\n", always_mmap); + printf("never_mmap %u\n", never_mmap); + printf("chunks %lu\n", chunks); + printf("prevent coalescing using permissions %u\n", + use_permissions); + printf("prevent coalescing using holes %u\n", use_holes); + printf("random_size %u\n", random_size); + printf("chunk_size %lu\n", chunk_size); + printf("seconds %lu\n", seconds); + printf("threads %u\n", threads); + printf("verbose %u\n", verbose); + printf("linear %u\n", linear); + printf("touch_pages %u\n", touch_pages); + printf("page size %d\n", page_size); + } + + /* Check for incompatible options */ + + if (always_mmap && never_mmap) { + fprintf(stderr, "Both -m \"always mmap\" and -M " + "\"never mmap\" option specified\n"); + usage(); + } + +#if 0 + if (never_mmap) + mallopt(M_MMAP_MAX, 0); +#endif + + if (chunk_size < record_size) { + fprintf(stderr, "Chunk size %lu smaller than record size %u\n", + chunk_size, record_size); + usage(); + } +} + +static void +touch_mem(char *dest, size_t size) +{ + int i; + if (touch_pages) { + for (i = 0; i < size; i += page_size) + *(dest + i) = 0xff; + } +} + +static void * +alloc_mem(size_t size) +{ + char *p; + int err = 0; + + if (always_mmap) { + p = mmap((void *) 0, size, (PROT_READ | PROT_WRITE), + (MAP_PRIVATE | MAP_ANONYMOUS), -1, 0); + if (p == MAP_FAILED) + err = 1; + } else { + p = malloc(size); + if (p == NULL) + err = 1; + } + + if (err) { + fprintf(stderr, "Couldn't allocate %zu bytes, try smaller " + "chunks or size options\n" + "Using -n %lu chunks and -s %lu size\n", + size, chunks, chunk_size); + exit(1); + } + + return (p); +} + +static void +free_mem(void *p, size_t size) +{ + if (always_mmap) + munmap(p, size); + else + free(p); +} + +/* + * Factor out differences in memcpy implementation by optionally using + * our own simple memcpy implementation. + */ + +static void +my_memcpy(void *dest, void *src, size_t len) +{ + char *d = (char *) dest; + char *s = (char *) src; + int i; + + for (i = 0; i < len; i++) + d[i] = s[i]; + return; +} + +static void +allocate(void) +{ + int i; + + mem = alloc_mem(chunks * sizeof (record_t *)); + + if (use_holes) + hole_mem = alloc_mem(chunks * sizeof (record_t *)); + + + for (i = 0; i < chunks; i++) { + mem[i] = (record_t *) alloc_mem(chunk_size); + /* Prevent coalescing using holes */ + if (use_holes) + hole_mem[i] = alloc_mem(page_size); + } + + /* Free hole memory */ + if (use_holes) + for (i = 0; i < chunks; i++) + free_mem(hole_mem[i], page_size); + + if (verbose) + printf("Allocated memory\n"); +} + +static void +write_pattern(void) +{ + int i, j; + + for (i = 0; i < chunks; i++) { + for(j = 0; j < chunk_size / record_size; j++) + mem[i][j] = (record_t) j; + +#if 0 /* Biscuit doesn't support mprotect yet. */ + /* Prevent coalescing by alternating permissions */ + if (use_permissions && (i % 2) == 0) + mprotect((void *) mem[i], chunk_size, PROT_READ); +#endif + + } + if (verbose) + printf("Wrote memory\n"); +} + +static void * +linear_search(record_t key, record_t *base, size_t size) +{ + record_t *p; + record_t *end = base + (size / record_size); + + for(p = base; p < end; p++) + if (*p == key) + return p; + return NULL; +} + +#if 0 +static int +compare(const void *p1, const void *p2) +{ + return (* (record_t *) p1 - * (record_t *) p2); +} +#endif + +/* + * Stupid ranged random number function. We don't care about quality. + * + * Inline because it's starting to be a scaling issue. + */ + +static inline unsigned int +rand_num(unsigned int max, unsigned int *state) +{ + *state = *state * 1103515245 + 12345; + return ((*state/65536) % max); +} + +/* + * This function is the meat of the program; the rest is just support. + * + * In this function, we randomly select a memory chunk, copy it into a + * newly allocated buffer, randomly select a search key, look it up, + * then free the memory. An option tells us to allocate and copy a + * randomly sized chunk of the memory instead of the whole thing. + * + * Linear search provided for sanity checking. + * + */ + +static unsigned int +search_mem(void) +{ + record_t key, *found; + record_t *src, *copy; + unsigned int chunk; + size_t copy_size = chunk_size; + uint64_t i; + unsigned int state = 0x0c0ffee0; + + for (i = 0; threads_go == 1; i++) { + chunk = rand_num(chunks, &state); + src = mem[chunk]; + /* + * If we're doing random sizes, we need a non-zero + * multiple of record size. + */ + if (random_size) + copy_size = (rand_num(chunk_size / record_size, &state) + + 1) * record_size; + copy = alloc_mem(copy_size); + + if ( touch_pages ) { + touch_mem((char *) copy, copy_size); + } else { + + if (no_lib_memcpy) + my_memcpy(copy, src, copy_size); + else + memcpy(copy, src, copy_size); + + key = rand_num(copy_size / record_size, &state); + + if (verbose > 2) + printf("[%lx] Search key %zu, copy size %zu\n", + pthread_self(), key, copy_size); + +#if 1 // Force linear search for now. + found = linear_search(key, copy, copy_size); +#else + if (linear) + found = linear_search(key, copy, copy_size); + else + found = bsearch(&key, copy, copy_size / record_size, + record_size, compare); +#endif + + /* Below check is mainly for memory corruption or other bugs */ + if (found == NULL) { + fprintf(stderr, "Couldn't find key %zd\n", key); + exit(1); + } + } /* end if ! touch_pages */ + + free_mem(copy, copy_size); + } + + return (i); +} + +static void * +thread_run(void *arg) +{ + uint64_t records_local; + + if (verbose > 1) + printf("[%lx] Thread started\n", pthread_self()); + + /* Wait for the start signal */ + + while (threads_go == 0); + + records_local = search_mem(); + + pthread_mutex_lock(&records_count_lock); + records_read += records_local; + pthread_mutex_unlock(&records_count_lock); + + if (verbose > 1) + printf("[%lx] Thread finished, processed %lu records\n", + pthread_self(), records_local); + + return NULL; +} + +static struct timeval +difftimeval(struct timeval *end, struct timeval *start) +{ + struct timeval diff; + diff.tv_sec = end->tv_sec - start->tv_sec; + diff.tv_usec = end->tv_usec - start->tv_usec; + return diff; +} + +static void +start_threads(void) +{ + pthread_t thread_array[threads]; + double elapsed; + unsigned int i; + struct rusage start_ru, end_ru; + struct timeval start_time, end_time, usr_time, sys_time, elapsed_time; + int err; + + if (verbose) + printf("Threads starting\n"); + + pthread_mutex_init(&records_count_lock, NULL); + + for (i = 0; i < threads; i++) { + err = pthread_create(&thread_array[i], NULL, thread_run, NULL); + if (err) { + fprintf(stderr, "Error creating thread %d\n", i); + exit(1); + } + } + + /* + * Begin accounting - this is when we actually do the things + * we want to measure. */ + + getrusage(RUSAGE_SELF, &start_ru); + gettimeofday(&start_time, NULL); + threads_go = 1; + sleep(seconds); + threads_go = 0; + gettimeofday(&end_time, NULL); + getrusage(RUSAGE_SELF, &end_ru); + + /* + * The rest is just clean up. + */ + + for (i = 0; i < threads; i++) { + err = pthread_join(thread_array[i], NULL); + if (err) { + fprintf(stderr, "Error joining thread %d\n", i); + exit(1); + } + } + + elapsed_time = difftimeval(&end_time, &start_time); + usr_time = difftimeval(&end_ru.ru_utime, &start_ru.ru_utime); + sys_time = difftimeval(&end_ru.ru_stime, &start_ru.ru_stime); + + elapsed = elapsed_time.tv_sec + elapsed_time.tv_usec/1e6; + + if (verbose) + printf("Threads finished\n"); + + printf("%lu records/s\n", + (uint64_t) (((double) records_read)/elapsed)); + + printf("real %f s\n", elapsed); + printf("user %f s\n", usr_time.tv_sec + usr_time.tv_usec/1e6); + printf("sys %f s\n", sys_time.tv_sec + sys_time.tv_usec/1e6); +} + +int +main(int argc, char *argv[]) +{ + read_options(argc, argv); + + allocate(); + + write_pattern(); + + start_threads(); + + return 0; +} diff --git a/biscuit/c/echo.c b/biscuit/c/echo.c new file mode 100644 index 0000000..5159fb1 --- /dev/null +++ b/biscuit/c/echo.c @@ -0,0 +1,9 @@ +#include + +int main(int argc, char **argv) +{ + int i; + for (i = 1; i < argc; i++) + printf("%s%c", argv[i], (i == argc - 1) ? '\n' : ' '); + return 0; +} diff --git a/biscuit/c/error/bloat.c b/biscuit/c/error/bloat.c new file mode 100644 index 0000000..722e726 --- /dev/null +++ b/biscuit/c/error/bloat.c @@ -0,0 +1,67 @@ +#define small "nop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\n" +#define big small small small small small small small small small +#define uge big big big big big big big big big big big big big +void bloater() +{ + // ~50k each + asm volatile( + uge uge uge uge uge uge uge uge uge uge uge uge uge uge uge uge + uge uge uge uge uge uge uge uge uge uge uge uge uge uge uge uge + uge uge uge uge uge uge uge uge uge uge uge uge uge uge uge uge + ); + asm volatile( + uge uge uge uge uge uge uge uge uge uge uge uge uge uge uge uge + uge uge uge uge uge uge uge uge uge uge uge uge uge uge uge uge + uge uge uge uge uge uge uge uge uge uge uge uge uge uge uge uge + ); + asm volatile( + uge uge uge uge uge uge uge uge uge uge uge uge uge uge uge uge + uge uge uge uge uge uge uge uge uge uge uge uge uge uge uge uge + uge uge uge uge uge uge uge uge uge uge uge uge uge uge uge uge + ); + asm volatile( + uge uge uge uge uge uge uge uge uge uge uge uge uge uge uge uge + uge uge uge uge uge uge uge uge uge uge uge uge uge uge uge uge + uge uge uge uge uge uge uge uge uge uge uge uge uge uge uge uge + ); + asm volatile( + uge uge uge uge uge uge uge uge uge uge uge uge uge uge uge uge + uge uge uge uge uge uge uge uge uge uge uge uge uge uge uge uge + uge uge uge uge uge uge uge uge uge uge uge uge uge uge uge uge + ); + asm volatile( + uge uge uge uge uge uge uge uge uge uge uge uge uge uge uge uge + uge uge uge uge uge uge uge uge uge uge uge uge uge uge uge uge + uge uge uge uge uge uge uge uge uge uge uge uge uge uge uge uge + ); + asm volatile( + uge uge uge uge uge uge uge uge uge uge uge uge uge uge uge uge + uge uge uge uge uge uge uge uge uge uge uge uge uge uge uge uge + uge uge uge uge uge uge uge uge uge uge uge uge uge uge uge uge + ); + asm volatile( + uge uge uge uge uge uge uge uge uge uge uge uge uge uge uge uge + uge uge uge uge uge uge uge uge uge uge uge uge uge uge uge uge + uge uge uge uge uge uge uge uge uge uge uge uge uge uge uge uge + ); + asm volatile( + uge uge uge uge uge uge uge uge uge uge uge uge uge uge uge uge + uge uge uge uge uge uge uge uge uge uge uge uge uge uge uge uge + uge uge uge uge uge uge uge uge uge uge uge uge uge uge uge uge + ); + asm volatile( + uge uge uge uge uge uge uge uge uge uge uge uge uge uge uge uge + uge uge uge uge uge uge uge uge uge uge uge uge uge uge uge uge + uge uge uge uge uge uge uge uge uge uge uge uge uge uge uge uge + ); + asm volatile( + uge uge uge uge uge uge uge uge uge uge uge uge uge uge uge uge + uge uge uge uge uge uge uge uge uge uge uge uge uge uge uge uge + uge uge uge uge uge uge uge uge uge uge uge uge uge uge uge uge + ); + asm volatile( + uge uge uge uge uge uge uge uge uge uge uge uge uge uge uge uge + uge uge uge uge uge uge uge uge uge uge uge uge uge uge uge uge + uge uge uge uge uge uge uge uge uge uge uge uge uge uge uge uge + ); +} diff --git a/biscuit/c/error/dlmalloc.c b/biscuit/c/error/dlmalloc.c new file mode 100644 index 0000000..ab70ff2 --- /dev/null +++ b/biscuit/c/error/dlmalloc.c @@ -0,0 +1,6286 @@ +/* + This is a version (aka dlmalloc) of malloc/free/realloc written by + Doug Lea and released to the public domain, as explained at + http://creativecommons.org/publicdomain/zero/1.0/ Send questions, + comments, complaints, performance data, etc to dl@cs.oswego.edu + +* Version 2.8.6 Wed Aug 29 06:57:58 2012 Doug Lea + Note: There may be an updated version of this malloc obtainable at + ftp://gee.cs.oswego.edu/pub/misc/malloc.c + Check before installing! + +* Quickstart + + This library is all in one file to simplify the most common usage: + ftp it, compile it (-O3), and link it into another program. All of + the compile-time options default to reasonable values for use on + most platforms. You might later want to step through various + compile-time and dynamic tuning options. + + For convenience, an include file for code using this malloc is at: + ftp://gee.cs.oswego.edu/pub/misc/malloc-2.8.6.h + You don't really need this .h file unless you call functions not + defined in your system include files. The .h file contains only the + excerpts from this file needed for using this malloc on ANSI C/C++ + systems, so long as you haven't changed compile-time options about + naming and tuning parameters. If you do, then you can create your + own malloc.h that does include all settings by cutting at the point + indicated below. Note that you may already by default be using a C + library containing a malloc that is based on some version of this + malloc (for example in linux). You might still want to use the one + in this file to customize settings or to avoid overheads associated + with library versions. + +* Vital statistics: + + Supported pointer/size_t representation: 4 or 8 bytes + size_t MUST be an unsigned type of the same width as + pointers. (If you are using an ancient system that declares + size_t as a signed type, or need it to be a different width + than pointers, you can use a previous release of this malloc + (e.g. 2.7.2) supporting these.) + + Alignment: 8 bytes (minimum) + This suffices for nearly all current machines and C compilers. + However, you can define MALLOC_ALIGNMENT to be wider than this + if necessary (up to 128bytes), at the expense of using more space. + + Minimum overhead per allocated chunk: 4 or 8 bytes (if 4byte sizes) + 8 or 16 bytes (if 8byte sizes) + Each malloced chunk has a hidden word of overhead holding size + and status information, and additional cross-check word + if FOOTERS is defined. + + Minimum allocated size: 4-byte ptrs: 16 bytes (including overhead) + 8-byte ptrs: 32 bytes (including overhead) + + Even a request for zero bytes (i.e., malloc(0)) returns a + pointer to something of the minimum allocatable size. + The maximum overhead wastage (i.e., number of extra bytes + allocated than were requested in malloc) is less than or equal + to the minimum size, except for requests >= mmap_threshold that + are serviced via mmap(), where the worst case wastage is about + 32 bytes plus the remainder from a system page (the minimal + mmap unit); typically 4096 or 8192 bytes. + + Security: static-safe; optionally more or less + The "security" of malloc refers to the ability of malicious + code to accentuate the effects of errors (for example, freeing + space that is not currently malloc'ed or overwriting past the + ends of chunks) in code that calls malloc. This malloc + guarantees not to modify any memory locations below the base of + heap, i.e., static variables, even in the presence of usage + errors. The routines additionally detect most improper frees + and reallocs. All this holds as long as the static bookkeeping + for malloc itself is not corrupted by some other means. This + is only one aspect of security -- these checks do not, and + cannot, detect all possible programming errors. + + If FOOTERS is defined nonzero, then each allocated chunk + carries an additional check word to verify that it was malloced + from its space. These check words are the same within each + execution of a program using malloc, but differ across + executions, so externally crafted fake chunks cannot be + freed. This improves security by rejecting frees/reallocs that + could corrupt heap memory, in addition to the checks preventing + writes to statics that are always on. This may further improve + security at the expense of time and space overhead. (Note that + FOOTERS may also be worth using with MSPACES.) + + By default detected errors cause the program to abort (calling + "abort()"). You can override this to instead proceed past + errors by defining PROCEED_ON_ERROR. In this case, a bad free + has no effect, and a malloc that encounters a bad address + caused by user overwrites will ignore the bad address by + dropping pointers and indices to all known memory. This may + be appropriate for programs that should continue if at all + possible in the face of programming errors, although they may + run out of memory because dropped memory is never reclaimed. + + If you don't like either of these options, you can define + CORRUPTION_ERROR_ACTION and USAGE_ERROR_ACTION to do anything + else. And if if you are sure that your program using malloc has + no errors or vulnerabilities, you can define INSECURE to 1, + which might (or might not) provide a small performance improvement. + + It is also possible to limit the maximum total allocatable + space, using malloc_set_footprint_limit. This is not + designed as a security feature in itself (calls to set limits + are not screened or privileged), but may be useful as one + aspect of a secure implementation. + + Thread-safety: NOT thread-safe unless USE_LOCKS defined non-zero + When USE_LOCKS is defined, each public call to malloc, free, + etc is surrounded with a lock. By default, this uses a plain + pthread mutex, win32 critical section, or a spin-lock if if + available for the platform and not disabled by setting + USE_SPIN_LOCKS=0. However, if USE_RECURSIVE_LOCKS is defined, + recursive versions are used instead (which are not required for + base functionality but may be needed in layered extensions). + Using a global lock is not especially fast, and can be a major + bottleneck. It is designed only to provide minimal protection + in concurrent environments, and to provide a basis for + extensions. If you are using malloc in a concurrent program, + consider instead using nedmalloc + (http://www.nedprod.com/programs/portable/nedmalloc/) or + ptmalloc (See http://www.malloc.de), which are derived from + versions of this malloc. + + System requirements: Any combination of MORECORE and/or MMAP/MUNMAP + This malloc can use unix sbrk or any emulation (invoked using + the CALL_MORECORE macro) and/or mmap/munmap or any emulation + (invoked using CALL_MMAP/CALL_MUNMAP) to get and release system + memory. On most unix systems, it tends to work best if both + MORECORE and MMAP are enabled. On Win32, it uses emulations + based on VirtualAlloc. It also uses common C library functions + like memset. + + Compliance: I believe it is compliant with the Single Unix Specification + (See http://www.unix.org). Also SVID/XPG, ANSI C, and probably + others as well. + +* Overview of algorithms + + This is not the fastest, most space-conserving, most portable, or + most tunable malloc ever written. However it is among the fastest + while also being among the most space-conserving, portable and + tunable. Consistent balance across these factors results in a good + general-purpose allocator for malloc-intensive programs. + + In most ways, this malloc is a best-fit allocator. Generally, it + chooses the best-fitting existing chunk for a request, with ties + broken in approximately least-recently-used order. (This strategy + normally maintains low fragmentation.) However, for requests less + than 256bytes, it deviates from best-fit when there is not an + exactly fitting available chunk by preferring to use space adjacent + to that used for the previous small request, as well as by breaking + ties in approximately most-recently-used order. (These enhance + locality of series of small allocations.) And for very large requests + (>= 256Kb by default), it relies on system memory mapping + facilities, if supported. (This helps avoid carrying around and + possibly fragmenting memory used only for large chunks.) + + All operations (except malloc_stats and mallinfo) have execution + times that are bounded by a constant factor of the number of bits in + a size_t, not counting any clearing in calloc or copying in realloc, + or actions surrounding MORECORE and MMAP that have times + proportional to the number of non-contiguous regions returned by + system allocation routines, which is often just 1. In real-time + applications, you can optionally suppress segment traversals using + NO_SEGMENT_TRAVERSAL, which assures bounded execution even when + system allocators return non-contiguous spaces, at the typical + expense of carrying around more memory and increased fragmentation. + + The implementation is not very modular and seriously overuses + macros. Perhaps someday all C compilers will do as good a job + inlining modular code as can now be done by brute-force expansion, + but now, enough of them seem not to. + + Some compilers issue a lot of warnings about code that is + dead/unreachable only on some platforms, and also about intentional + uses of negation on unsigned types. All known cases of each can be + ignored. + + For a longer but out of date high-level description, see + http://gee.cs.oswego.edu/dl/html/malloc.html + +* MSPACES + If MSPACES is defined, then in addition to malloc, free, etc., + this file also defines mspace_malloc, mspace_free, etc. These + are versions of malloc routines that take an "mspace" argument + obtained using create_mspace, to control all internal bookkeeping. + If ONLY_MSPACES is defined, only these versions are compiled. + So if you would like to use this allocator for only some allocations, + and your system malloc for others, you can compile with + ONLY_MSPACES and then do something like... + static mspace mymspace = create_mspace(0,0); // for example + #define mymalloc(bytes) mspace_malloc(mymspace, bytes) + + (Note: If you only need one instance of an mspace, you can instead + use "USE_DL_PREFIX" to relabel the global malloc.) + + You can similarly create thread-local allocators by storing + mspaces as thread-locals. For example: + static __thread mspace tlms = 0; + void* tlmalloc(size_t bytes) { + if (tlms == 0) tlms = create_mspace(0, 0); + return mspace_malloc(tlms, bytes); + } + void tlfree(void* mem) { mspace_free(tlms, mem); } + + Unless FOOTERS is defined, each mspace is completely independent. + You cannot allocate from one and free to another (although + conformance is only weakly checked, so usage errors are not always + caught). If FOOTERS is defined, then each chunk carries around a tag + indicating its originating mspace, and frees are directed to their + originating spaces. Normally, this requires use of locks. + + ------------------------- Compile-time options --------------------------- + +Be careful in setting #define values for numerical constants of type +size_t. On some systems, literal values are not automatically extended +to size_t precision unless they are explicitly casted. You can also +use the symbolic values MAX_SIZE_T, SIZE_T_ONE, etc below. + +WIN32 default: defined if _WIN32 defined + Defining WIN32 sets up defaults for MS environment and compilers. + Otherwise defaults are for unix. Beware that there seem to be some + cases where this malloc might not be a pure drop-in replacement for + Win32 malloc: Random-looking failures from Win32 GDI API's (eg; + SetDIBits()) may be due to bugs in some video driver implementations + when pixel buffers are malloc()ed, and the region spans more than + one VirtualAlloc()ed region. Because dlmalloc uses a small (64Kb) + default granularity, pixel buffers may straddle virtual allocation + regions more often than when using the Microsoft allocator. You can + avoid this by using VirtualAlloc() and VirtualFree() for all pixel + buffers rather than using malloc(). If this is not possible, + recompile this malloc with a larger DEFAULT_GRANULARITY. Note: + in cases where MSC and gcc (cygwin) are known to differ on WIN32, + conditions use _MSC_VER to distinguish them. + +DLMALLOC_EXPORT default: extern + Defines how public APIs are declared. If you want to export via a + Windows DLL, you might define this as + #define DLMALLOC_EXPORT extern __declspec(dllexport) + If you want a POSIX ELF shared object, you might use + #define DLMALLOC_EXPORT extern __attribute__((visibility("default"))) + +MALLOC_ALIGNMENT default: (size_t)(2 * sizeof(void *)) + Controls the minimum alignment for malloc'ed chunks. It must be a + power of two and at least 8, even on machines for which smaller + alignments would suffice. It may be defined as larger than this + though. Note however that code and data structures are optimized for + the case of 8-byte alignment. + +MSPACES default: 0 (false) + If true, compile in support for independent allocation spaces. + This is only supported if HAVE_MMAP is true. + +ONLY_MSPACES default: 0 (false) + If true, only compile in mspace versions, not regular versions. + +USE_LOCKS default: 0 (false) + Causes each call to each public routine to be surrounded with + pthread or WIN32 mutex lock/unlock. (If set true, this can be + overridden on a per-mspace basis for mspace versions.) If set to a + non-zero value other than 1, locks are used, but their + implementation is left out, so lock functions must be supplied manually, + as described below. + +USE_SPIN_LOCKS default: 1 iff USE_LOCKS and spin locks available + If true, uses custom spin locks for locking. This is currently + supported only gcc >= 4.1, older gccs on x86 platforms, and recent + MS compilers. Otherwise, posix locks or win32 critical sections are + used. + +USE_RECURSIVE_LOCKS default: not defined + If defined nonzero, uses recursive (aka reentrant) locks, otherwise + uses plain mutexes. This is not required for malloc proper, but may + be needed for layered allocators such as nedmalloc. + +LOCK_AT_FORK default: not defined + If defined nonzero, performs pthread_atfork upon initialization + to initialize child lock while holding parent lock. The implementation + assumes that pthread locks (not custom locks) are being used. In other + cases, you may need to customize the implementation. + +FOOTERS default: 0 + If true, provide extra checking and dispatching by placing + information in the footers of allocated chunks. This adds + space and time overhead. + +INSECURE default: 0 + If true, omit checks for usage errors and heap space overwrites. + +USE_DL_PREFIX default: NOT defined + Causes compiler to prefix all public routines with the string 'dl'. + This can be useful when you only want to use this malloc in one part + of a program, using your regular system malloc elsewhere. + +MALLOC_INSPECT_ALL default: NOT defined + If defined, compiles malloc_inspect_all and mspace_inspect_all, that + perform traversal of all heap space. Unless access to these + functions is otherwise restricted, you probably do not want to + include them in secure implementations. + +ABORT default: defined as abort() + Defines how to abort on failed checks. On most systems, a failed + check cannot die with an "assert" or even print an informative + message, because the underlying print routines in turn call malloc, + which will fail again. Generally, the best policy is to simply call + abort(). It's not very useful to do more than this because many + errors due to overwriting will show up as address faults (null, odd + addresses etc) rather than malloc-triggered checks, so will also + abort. Also, most compilers know that abort() does not return, so + can better optimize code conditionally calling it. + +PROCEED_ON_ERROR default: defined as 0 (false) + Controls whether detected bad addresses cause them to bypassed + rather than aborting. If set, detected bad arguments to free and + realloc are ignored. And all bookkeeping information is zeroed out + upon a detected overwrite of freed heap space, thus losing the + ability to ever return it from malloc again, but enabling the + application to proceed. If PROCEED_ON_ERROR is defined, the + static variable malloc_corruption_error_count is compiled in + and can be examined to see if errors have occurred. This option + generates slower code than the default abort policy. + +DEBUG default: NOT defined + The DEBUG setting is mainly intended for people trying to modify + this code or diagnose problems when porting to new platforms. + However, it may also be able to better isolate user errors than just + using runtime checks. The assertions in the check routines spell + out in more detail the assumptions and invariants underlying the + algorithms. The checking is fairly extensive, and will slow down + execution noticeably. Calling malloc_stats or mallinfo with DEBUG + set will attempt to check every non-mmapped allocated and free chunk + in the course of computing the summaries. + +ABORT_ON_ASSERT_FAILURE default: defined as 1 (true) + Debugging assertion failures can be nearly impossible if your + version of the assert macro causes malloc to be called, which will + lead to a cascade of further failures, blowing the runtime stack. + ABORT_ON_ASSERT_FAILURE cause assertions failures to call abort(), + which will usually make debugging easier. + +MALLOC_FAILURE_ACTION default: sets errno to ENOMEM, or no-op on win32 + The action to take before "return 0" when malloc fails to be able to + return memory because there is none available. + +HAVE_MORECORE default: 1 (true) unless win32 or ONLY_MSPACES + True if this system supports sbrk or an emulation of it. + +MORECORE default: sbrk + The name of the sbrk-style system routine to call to obtain more + memory. See below for guidance on writing custom MORECORE + functions. The type of the argument to sbrk/MORECORE varies across + systems. It cannot be size_t, because it supports negative + arguments, so it is normally the signed type of the same width as + size_t (sometimes declared as "intptr_t"). It doesn't much matter + though. Internally, we only call it with arguments less than half + the max value of a size_t, which should work across all reasonable + possibilities, although sometimes generating compiler warnings. + +MORECORE_CONTIGUOUS default: 1 (true) if HAVE_MORECORE + If true, take advantage of fact that consecutive calls to MORECORE + with positive arguments always return contiguous increasing + addresses. This is true of unix sbrk. It does not hurt too much to + set it true anyway, since malloc copes with non-contiguities. + Setting it false when definitely non-contiguous saves time + and possibly wasted space it would take to discover this though. + +MORECORE_CANNOT_TRIM default: NOT defined + True if MORECORE cannot release space back to the system when given + negative arguments. This is generally necessary only if you are + using a hand-crafted MORECORE function that cannot handle negative + arguments. + +NO_SEGMENT_TRAVERSAL default: 0 + If non-zero, suppresses traversals of memory segments + returned by either MORECORE or CALL_MMAP. This disables + merging of segments that are contiguous, and selectively + releasing them to the OS if unused, but bounds execution times. + +HAVE_MMAP default: 1 (true) + True if this system supports mmap or an emulation of it. If so, and + HAVE_MORECORE is not true, MMAP is used for all system + allocation. If set and HAVE_MORECORE is true as well, MMAP is + primarily used to directly allocate very large blocks. It is also + used as a backup strategy in cases where MORECORE fails to provide + space from system. Note: A single call to MUNMAP is assumed to be + able to unmap memory that may have be allocated using multiple calls + to MMAP, so long as they are adjacent. + +HAVE_MREMAP default: 1 on linux, else 0 + If true realloc() uses mremap() to re-allocate large blocks and + extend or shrink allocation spaces. + +MMAP_CLEARS default: 1 except on WINCE. + True if mmap clears memory so calloc doesn't need to. This is true + for standard unix mmap using /dev/zero and on WIN32 except for WINCE. + +USE_BUILTIN_FFS default: 0 (i.e., not used) + Causes malloc to use the builtin ffs() function to compute indices. + Some compilers may recognize and intrinsify ffs to be faster than the + supplied C version. Also, the case of x86 using gcc is special-cased + to an asm instruction, so is already as fast as it can be, and so + this setting has no effect. Similarly for Win32 under recent MS compilers. + (On most x86s, the asm version is only slightly faster than the C version.) + +malloc_getpagesize default: derive from system includes, or 4096. + The system page size. To the extent possible, this malloc manages + memory from the system in page-size units. This may be (and + usually is) a function rather than a constant. This is ignored + if WIN32, where page size is determined using getSystemInfo during + initialization. + +USE_DEV_RANDOM default: 0 (i.e., not used) + Causes malloc to use /dev/random to initialize secure magic seed for + stamping footers. Otherwise, the current time is used. + +NO_MALLINFO default: 0 + If defined, don't compile "mallinfo". This can be a simple way + of dealing with mismatches between system declarations and + those in this file. + +MALLINFO_FIELD_TYPE default: size_t + The type of the fields in the mallinfo struct. This was originally + defined as "int" in SVID etc, but is more usefully defined as + size_t. The value is used only if HAVE_USR_INCLUDE_MALLOC_H is not set + +NO_MALLOC_STATS default: 0 + If defined, don't compile "malloc_stats". This avoids calls to + fprintf and bringing in stdio dependencies you might not want. + +REALLOC_ZERO_BYTES_FREES default: not defined + This should be set if a call to realloc with zero bytes should + be the same as a call to free. Some people think it should. Otherwise, + since this malloc returns a unique pointer for malloc(0), so does + realloc(p, 0). + +LACKS_UNISTD_H, LACKS_FCNTL_H, LACKS_SYS_PARAM_H, LACKS_SYS_MMAN_H +LACKS_STRINGS_H, LACKS_STRING_H, LACKS_SYS_TYPES_H, LACKS_ERRNO_H +LACKS_STDLIB_H LACKS_SCHED_H LACKS_TIME_H default: NOT defined unless on WIN32 + Define these if your system does not have these header files. + You might need to manually insert some of the declarations they provide. + +DEFAULT_GRANULARITY default: page size if MORECORE_CONTIGUOUS, + system_info.dwAllocationGranularity in WIN32, + otherwise 64K. + Also settable using mallopt(M_GRANULARITY, x) + The unit for allocating and deallocating memory from the system. On + most systems with contiguous MORECORE, there is no reason to + make this more than a page. However, systems with MMAP tend to + either require or encourage larger granularities. You can increase + this value to prevent system allocation functions to be called so + often, especially if they are slow. The value must be at least one + page and must be a power of two. Setting to 0 causes initialization + to either page size or win32 region size. (Note: In previous + versions of malloc, the equivalent of this option was called + "TOP_PAD") + +DEFAULT_TRIM_THRESHOLD default: 2MB + Also settable using mallopt(M_TRIM_THRESHOLD, x) + The maximum amount of unused top-most memory to keep before + releasing via malloc_trim in free(). Automatic trimming is mainly + useful in long-lived programs using contiguous MORECORE. Because + trimming via sbrk can be slow on some systems, and can sometimes be + wasteful (in cases where programs immediately afterward allocate + more large chunks) the value should be high enough so that your + overall system performance would improve by releasing this much + memory. As a rough guide, you might set to a value close to the + average size of a process (program) running on your system. + Releasing this much memory would allow such a process to run in + memory. Generally, it is worth tuning trim thresholds when a + program undergoes phases where several large chunks are allocated + and released in ways that can reuse each other's storage, perhaps + mixed with phases where there are no such chunks at all. The trim + value must be greater than page size to have any useful effect. To + disable trimming completely, you can set to MAX_SIZE_T. Note that the trick + some people use of mallocing a huge space and then freeing it at + program startup, in an attempt to reserve system memory, doesn't + have the intended effect under automatic trimming, since that memory + will immediately be returned to the system. + +DEFAULT_MMAP_THRESHOLD default: 256K + Also settable using mallopt(M_MMAP_THRESHOLD, x) + The request size threshold for using MMAP to directly service a + request. Requests of at least this size that cannot be allocated + using already-existing space will be serviced via mmap. (If enough + normal freed space already exists it is used instead.) Using mmap + segregates relatively large chunks of memory so that they can be + individually obtained and released from the host system. A request + serviced through mmap is never reused by any other request (at least + not directly; the system may just so happen to remap successive + requests to the same locations). Segregating space in this way has + the benefits that: Mmapped space can always be individually released + back to the system, which helps keep the system level memory demands + of a long-lived program low. Also, mapped memory doesn't become + `locked' between other chunks, as can happen with normally allocated + chunks, which means that even trimming via malloc_trim would not + release them. However, it has the disadvantage that the space + cannot be reclaimed, consolidated, and then used to service later + requests, as happens with normal chunks. The advantages of mmap + nearly always outweigh disadvantages for "large" chunks, but the + value of "large" may vary across systems. The default is an + empirically derived value that works well in most systems. You can + disable mmap by setting to MAX_SIZE_T. + +MAX_RELEASE_CHECK_RATE default: 4095 unless not HAVE_MMAP + The number of consolidated frees between checks to release + unused segments when freeing. When using non-contiguous segments, + especially with multiple mspaces, checking only for topmost space + doesn't always suffice to trigger trimming. To compensate for this, + free() will, with a period of MAX_RELEASE_CHECK_RATE (or the + current number of segments, if greater) try to release unused + segments to the OS when freeing chunks that result in + consolidation. The best value for this parameter is a compromise + between slowing down frees with relatively costly checks that + rarely trigger versus holding on to unused memory. To effectively + disable, set to MAX_SIZE_T. This may lead to a very slight speed + improvement at the expense of carrying around more memory. +*/ + +/* Version identifier to allow people to support multiple versions */ +#ifndef DLMALLOC_VERSION +#define DLMALLOC_VERSION 20806 +#endif /* DLMALLOC_VERSION */ + +#ifndef DLMALLOC_EXPORT +#define DLMALLOC_EXPORT extern +#endif + +// added by cody +#define LACKS_SCHED_H +#define HAVE_MREMAP 0 +#define HAVE_MORECORE 0 +#define USE_LOCKS 1 + +#ifndef WIN32 +#ifdef _WIN32 +#define WIN32 1 +#endif /* _WIN32 */ +#ifdef _WIN32_WCE +#define LACKS_FCNTL_H +#define WIN32 1 +#endif /* _WIN32_WCE */ +#endif /* WIN32 */ +#ifdef WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#include +#define HAVE_MMAP 1 +#define HAVE_MORECORE 0 +#define LACKS_UNISTD_H +#define LACKS_SYS_PARAM_H +#define LACKS_SYS_MMAN_H +#define LACKS_STRING_H +#define LACKS_STRINGS_H +#define LACKS_SYS_TYPES_H +#define LACKS_ERRNO_H +#define LACKS_SCHED_H +#ifndef MALLOC_FAILURE_ACTION +#define MALLOC_FAILURE_ACTION +#endif /* MALLOC_FAILURE_ACTION */ +#ifndef MMAP_CLEARS +#ifdef _WIN32_WCE /* WINCE reportedly does not clear */ +#define MMAP_CLEARS 0 +#else +#define MMAP_CLEARS 1 +#endif /* _WIN32_WCE */ +#endif /*MMAP_CLEARS */ +#endif /* WIN32 */ + +#if defined(DARWIN) || defined(_DARWIN) +/* Mac OSX docs advise not to use sbrk; it seems better to use mmap */ +#ifndef HAVE_MORECORE +#define HAVE_MORECORE 0 +#define HAVE_MMAP 1 +/* OSX allocators provide 16 byte alignment */ +#ifndef MALLOC_ALIGNMENT +#define MALLOC_ALIGNMENT ((size_t)16U) +#endif +#endif /* HAVE_MORECORE */ +#endif /* DARWIN */ + +#ifndef LACKS_SYS_TYPES_H +#include /* For size_t */ +#endif /* LACKS_SYS_TYPES_H */ + +/* The maximum possible size_t value has all bits set */ +#define MAX_SIZE_T (~(size_t)0) + +#ifndef USE_LOCKS /* ensure true if spin or recursive locks set */ +#define USE_LOCKS ((defined(USE_SPIN_LOCKS) && USE_SPIN_LOCKS != 0) || \ + (defined(USE_RECURSIVE_LOCKS) && USE_RECURSIVE_LOCKS != 0)) +#endif /* USE_LOCKS */ + +#if USE_LOCKS /* Spin locks for gcc >= 4.1, older gcc on x86, MSC >= 1310 */ +#if ((defined(__GNUC__) && \ + ((__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1)) || \ + defined(__i386__) || defined(__x86_64__))) || \ + (defined(_MSC_VER) && _MSC_VER>=1310)) +#ifndef USE_SPIN_LOCKS +#define USE_SPIN_LOCKS 1 +#endif /* USE_SPIN_LOCKS */ +#elif USE_SPIN_LOCKS +#error "USE_SPIN_LOCKS defined without implementation" +#endif /* ... locks available... */ +#elif !defined(USE_SPIN_LOCKS) +#define USE_SPIN_LOCKS 0 +#endif /* USE_LOCKS */ + +#ifndef ONLY_MSPACES +#define ONLY_MSPACES 0 +#endif /* ONLY_MSPACES */ +#ifndef MSPACES +#if ONLY_MSPACES +#define MSPACES 1 +#else /* ONLY_MSPACES */ +#define MSPACES 0 +#endif /* ONLY_MSPACES */ +#endif /* MSPACES */ +#ifndef MALLOC_ALIGNMENT +#define MALLOC_ALIGNMENT ((size_t)(2 * sizeof(void *))) +#endif /* MALLOC_ALIGNMENT */ +#ifndef FOOTERS +#define FOOTERS 0 +#endif /* FOOTERS */ +#ifndef ABORT +#define ABORT abort() +#endif /* ABORT */ +#ifndef ABORT_ON_ASSERT_FAILURE +#define ABORT_ON_ASSERT_FAILURE 1 +#endif /* ABORT_ON_ASSERT_FAILURE */ +#ifndef PROCEED_ON_ERROR +#define PROCEED_ON_ERROR 0 +#endif /* PROCEED_ON_ERROR */ + +#ifndef INSECURE +#define INSECURE 0 +#endif /* INSECURE */ +#ifndef MALLOC_INSPECT_ALL +#define MALLOC_INSPECT_ALL 0 +#endif /* MALLOC_INSPECT_ALL */ +#ifndef HAVE_MMAP +#define HAVE_MMAP 1 +#endif /* HAVE_MMAP */ +#ifndef MMAP_CLEARS +#define MMAP_CLEARS 1 +#endif /* MMAP_CLEARS */ +#ifndef HAVE_MREMAP +#ifdef linux +#define HAVE_MREMAP 1 +#define _GNU_SOURCE /* Turns on mremap() definition */ +#else /* linux */ +#define HAVE_MREMAP 0 +#endif /* linux */ +#endif /* HAVE_MREMAP */ +#ifndef MALLOC_FAILURE_ACTION +#define MALLOC_FAILURE_ACTION errno = ENOMEM; +#endif /* MALLOC_FAILURE_ACTION */ +#ifndef HAVE_MORECORE +#if ONLY_MSPACES +#define HAVE_MORECORE 0 +#else /* ONLY_MSPACES */ +#define HAVE_MORECORE 1 +#endif /* ONLY_MSPACES */ +#endif /* HAVE_MORECORE */ +#if !HAVE_MORECORE +#define MORECORE_CONTIGUOUS 0 +#else /* !HAVE_MORECORE */ +#define MORECORE_DEFAULT sbrk +#ifndef MORECORE_CONTIGUOUS +#define MORECORE_CONTIGUOUS 1 +#endif /* MORECORE_CONTIGUOUS */ +#endif /* HAVE_MORECORE */ +#ifndef DEFAULT_GRANULARITY +#if (MORECORE_CONTIGUOUS || defined(WIN32)) +#define DEFAULT_GRANULARITY (0) /* 0 means to compute in init_mparams */ +#else /* MORECORE_CONTIGUOUS */ +#define DEFAULT_GRANULARITY ((size_t)64U * (size_t)1024U) +#endif /* MORECORE_CONTIGUOUS */ +#endif /* DEFAULT_GRANULARITY */ +#ifndef DEFAULT_TRIM_THRESHOLD +#ifndef MORECORE_CANNOT_TRIM +#define DEFAULT_TRIM_THRESHOLD ((size_t)2U * (size_t)1024U * (size_t)1024U) +#else /* MORECORE_CANNOT_TRIM */ +#define DEFAULT_TRIM_THRESHOLD MAX_SIZE_T +#endif /* MORECORE_CANNOT_TRIM */ +#endif /* DEFAULT_TRIM_THRESHOLD */ +#ifndef DEFAULT_MMAP_THRESHOLD +#if HAVE_MMAP +#define DEFAULT_MMAP_THRESHOLD ((size_t)256U * (size_t)1024U) +#else /* HAVE_MMAP */ +#define DEFAULT_MMAP_THRESHOLD MAX_SIZE_T +#endif /* HAVE_MMAP */ +#endif /* DEFAULT_MMAP_THRESHOLD */ +#ifndef MAX_RELEASE_CHECK_RATE +#if HAVE_MMAP +#define MAX_RELEASE_CHECK_RATE 4095 +#else +#define MAX_RELEASE_CHECK_RATE MAX_SIZE_T +#endif /* HAVE_MMAP */ +#endif /* MAX_RELEASE_CHECK_RATE */ +#ifndef USE_BUILTIN_FFS +#define USE_BUILTIN_FFS 0 +#endif /* USE_BUILTIN_FFS */ +#ifndef USE_DEV_RANDOM +#define USE_DEV_RANDOM 0 +#endif /* USE_DEV_RANDOM */ +#ifndef NO_MALLINFO +#define NO_MALLINFO 0 +#endif /* NO_MALLINFO */ +#ifndef MALLINFO_FIELD_TYPE +#define MALLINFO_FIELD_TYPE size_t +#endif /* MALLINFO_FIELD_TYPE */ +#ifndef NO_MALLOC_STATS +#define NO_MALLOC_STATS 0 +#endif /* NO_MALLOC_STATS */ +#ifndef NO_SEGMENT_TRAVERSAL +#define NO_SEGMENT_TRAVERSAL 0 +#endif /* NO_SEGMENT_TRAVERSAL */ + +/* + mallopt tuning options. SVID/XPG defines four standard parameter + numbers for mallopt, normally defined in malloc.h. None of these + are used in this malloc, so setting them has no effect. But this + malloc does support the following options. +*/ + +#define M_TRIM_THRESHOLD (-1) +#define M_GRANULARITY (-2) +#define M_MMAP_THRESHOLD (-3) + +/* ------------------------ Mallinfo declarations ------------------------ */ + +#if !NO_MALLINFO +/* + This version of malloc supports the standard SVID/XPG mallinfo + routine that returns a struct containing usage properties and + statistics. It should work on any system that has a + /usr/include/malloc.h defining struct mallinfo. The main + declaration needed is the mallinfo struct that is returned (by-copy) + by mallinfo(). The malloinfo struct contains a bunch of fields that + are not even meaningful in this version of malloc. These fields are + are instead filled by mallinfo() with other numbers that might be of + interest. + + HAVE_USR_INCLUDE_MALLOC_H should be set if you have a + /usr/include/malloc.h file that includes a declaration of struct + mallinfo. If so, it is included; else a compliant version is + declared below. These must be precisely the same for mallinfo() to + work. The original SVID version of this struct, defined on most + systems with mallinfo, declares all fields as ints. But some others + define as unsigned long. If your system defines the fields using a + type of different width than listed here, you MUST #include your + system version and #define HAVE_USR_INCLUDE_MALLOC_H. +*/ + +/* #define HAVE_USR_INCLUDE_MALLOC_H */ + +#ifdef HAVE_USR_INCLUDE_MALLOC_H +#include "/usr/include/malloc.h" +#else /* HAVE_USR_INCLUDE_MALLOC_H */ +#ifndef STRUCT_MALLINFO_DECLARED +/* HP-UX (and others?) redefines mallinfo unless _STRUCT_MALLINFO is defined */ +#define _STRUCT_MALLINFO +#define STRUCT_MALLINFO_DECLARED 1 +struct mallinfo { + MALLINFO_FIELD_TYPE arena; /* non-mmapped space allocated from system */ + MALLINFO_FIELD_TYPE ordblks; /* number of free chunks */ + MALLINFO_FIELD_TYPE smblks; /* always 0 */ + MALLINFO_FIELD_TYPE hblks; /* always 0 */ + MALLINFO_FIELD_TYPE hblkhd; /* space in mmapped regions */ + MALLINFO_FIELD_TYPE usmblks; /* maximum total allocated space */ + MALLINFO_FIELD_TYPE fsmblks; /* always 0 */ + MALLINFO_FIELD_TYPE uordblks; /* total allocated space */ + MALLINFO_FIELD_TYPE fordblks; /* total free space */ + MALLINFO_FIELD_TYPE keepcost; /* releasable (via malloc_trim) space */ +}; +#endif /* STRUCT_MALLINFO_DECLARED */ +#endif /* HAVE_USR_INCLUDE_MALLOC_H */ +#endif /* NO_MALLINFO */ + +/* + Try to persuade compilers to inline. The most critical functions for + inlining are defined as macros, so these aren't used for them. +*/ + +#ifndef FORCEINLINE + #if defined(__GNUC__) +#define FORCEINLINE __inline __attribute__ ((always_inline)) + #elif defined(_MSC_VER) + #define FORCEINLINE __forceinline + #endif +#endif +#ifndef NOINLINE + #if defined(__GNUC__) + #define NOINLINE __attribute__ ((noinline)) + #elif defined(_MSC_VER) + #define NOINLINE __declspec(noinline) + #else + #define NOINLINE + #endif +#endif + +#ifdef __cplusplus +extern "C" { +#ifndef FORCEINLINE + #define FORCEINLINE inline +#endif +#endif /* __cplusplus */ +#ifndef FORCEINLINE + #define FORCEINLINE +#endif + +#if !ONLY_MSPACES + +/* ------------------- Declarations of public routines ------------------- */ + +#ifndef USE_DL_PREFIX +#define dlcalloc calloc +#define dlfree free +#define dlmalloc malloc +#define dlmemalign memalign +#define dlposix_memalign posix_memalign +#define dlrealloc realloc +#define dlrealloc_in_place realloc_in_place +#define dlvalloc valloc +#define dlpvalloc pvalloc +#define dlmallinfo mallinfo +#define dlmallopt mallopt +#define dlmalloc_trim malloc_trim +#define dlmalloc_stats malloc_stats +#define dlmalloc_usable_size malloc_usable_size +#define dlmalloc_footprint malloc_footprint +#define dlmalloc_max_footprint malloc_max_footprint +#define dlmalloc_footprint_limit malloc_footprint_limit +#define dlmalloc_set_footprint_limit malloc_set_footprint_limit +#define dlmalloc_inspect_all malloc_inspect_all +#define dlindependent_calloc independent_calloc +#define dlindependent_comalloc independent_comalloc +#define dlbulk_free bulk_free +#endif /* USE_DL_PREFIX */ + +/* + malloc(size_t n) + Returns a pointer to a newly allocated chunk of at least n bytes, or + null if no space is available, in which case errno is set to ENOMEM + on ANSI C systems. + + If n is zero, malloc returns a minimum-sized chunk. (The minimum + size is 16 bytes on most 32bit systems, and 32 bytes on 64bit + systems.) Note that size_t is an unsigned type, so calls with + arguments that would be negative if signed are interpreted as + requests for huge amounts of space, which will often fail. The + maximum supported value of n differs across systems, but is in all + cases less than the maximum representable value of a size_t. +*/ +DLMALLOC_EXPORT void* dlmalloc(size_t); + +/* + free(void* p) + Releases the chunk of memory pointed to by p, that had been previously + allocated using malloc or a related routine such as realloc. + It has no effect if p is null. If p was not malloced or already + freed, free(p) will by default cause the current program to abort. +*/ +DLMALLOC_EXPORT void dlfree(void*); + +/* + calloc(size_t n_elements, size_t element_size); + Returns a pointer to n_elements * element_size bytes, with all locations + set to zero. +*/ +DLMALLOC_EXPORT void* dlcalloc(size_t, size_t); + +/* + realloc(void* p, size_t n) + Returns a pointer to a chunk of size n that contains the same data + as does chunk p up to the minimum of (n, p's size) bytes, or null + if no space is available. + + The returned pointer may or may not be the same as p. The algorithm + prefers extending p in most cases when possible, otherwise it + employs the equivalent of a malloc-copy-free sequence. + + If p is null, realloc is equivalent to malloc. + + If space is not available, realloc returns null, errno is set (if on + ANSI) and p is NOT freed. + + if n is for fewer bytes than already held by p, the newly unused + space is lopped off and freed if possible. realloc with a size + argument of zero (re)allocates a minimum-sized chunk. + + The old unix realloc convention of allowing the last-free'd chunk + to be used as an argument to realloc is not supported. +*/ +DLMALLOC_EXPORT void* dlrealloc(void*, size_t); + +/* + realloc_in_place(void* p, size_t n) + Resizes the space allocated for p to size n, only if this can be + done without moving p (i.e., only if there is adjacent space + available if n is greater than p's current allocated size, or n is + less than or equal to p's size). This may be used instead of plain + realloc if an alternative allocation strategy is needed upon failure + to expand space; for example, reallocation of a buffer that must be + memory-aligned or cleared. You can use realloc_in_place to trigger + these alternatives only when needed. + + Returns p if successful; otherwise null. +*/ +DLMALLOC_EXPORT void* dlrealloc_in_place(void*, size_t); + +/* + memalign(size_t alignment, size_t n); + Returns a pointer to a newly allocated chunk of n bytes, aligned + in accord with the alignment argument. + + The alignment argument should be a power of two. If the argument is + not a power of two, the nearest greater power is used. + 8-byte alignment is guaranteed by normal malloc calls, so don't + bother calling memalign with an argument of 8 or less. + + Overreliance on memalign is a sure way to fragment space. +*/ +DLMALLOC_EXPORT void* dlmemalign(size_t, size_t); + +/* + int posix_memalign(void** pp, size_t alignment, size_t n); + Allocates a chunk of n bytes, aligned in accord with the alignment + argument. Differs from memalign only in that it (1) assigns the + allocated memory to *pp rather than returning it, (2) fails and + returns EINVAL if the alignment is not a power of two (3) fails and + returns ENOMEM if memory cannot be allocated. +*/ +DLMALLOC_EXPORT int dlposix_memalign(void**, size_t, size_t); + +/* + valloc(size_t n); + Equivalent to memalign(pagesize, n), where pagesize is the page + size of the system. If the pagesize is unknown, 4096 is used. +*/ +DLMALLOC_EXPORT void* dlvalloc(size_t); + +/* + mallopt(int parameter_number, int parameter_value) + Sets tunable parameters The format is to provide a + (parameter-number, parameter-value) pair. mallopt then sets the + corresponding parameter to the argument value if it can (i.e., so + long as the value is meaningful), and returns 1 if successful else + 0. To workaround the fact that mallopt is specified to use int, + not size_t parameters, the value -1 is specially treated as the + maximum unsigned size_t value. + + SVID/XPG/ANSI defines four standard param numbers for mallopt, + normally defined in malloc.h. None of these are use in this malloc, + so setting them has no effect. But this malloc also supports other + options in mallopt. See below for details. Briefly, supported + parameters are as follows (listed defaults are for "typical" + configurations). + + Symbol param # default allowed param values + M_TRIM_THRESHOLD -1 2*1024*1024 any (-1 disables) + M_GRANULARITY -2 page size any power of 2 >= page size + M_MMAP_THRESHOLD -3 256*1024 any (or 0 if no MMAP support) +*/ +DLMALLOC_EXPORT int dlmallopt(int, int); + +/* + malloc_footprint(); + Returns the number of bytes obtained from the system. The total + number of bytes allocated by malloc, realloc etc., is less than this + value. Unlike mallinfo, this function returns only a precomputed + result, so can be called frequently to monitor memory consumption. + Even if locks are otherwise defined, this function does not use them, + so results might not be up to date. +*/ +DLMALLOC_EXPORT size_t dlmalloc_footprint(void); + +/* + malloc_max_footprint(); + Returns the maximum number of bytes obtained from the system. This + value will be greater than current footprint if deallocated space + has been reclaimed by the system. The peak number of bytes allocated + by malloc, realloc etc., is less than this value. Unlike mallinfo, + this function returns only a precomputed result, so can be called + frequently to monitor memory consumption. Even if locks are + otherwise defined, this function does not use them, so results might + not be up to date. +*/ +DLMALLOC_EXPORT size_t dlmalloc_max_footprint(void); + +/* + malloc_footprint_limit(); + Returns the number of bytes that the heap is allowed to obtain from + the system, returning the last value returned by + malloc_set_footprint_limit, or the maximum size_t value if + never set. The returned value reflects a permission. There is no + guarantee that this number of bytes can actually be obtained from + the system. +*/ +DLMALLOC_EXPORT size_t dlmalloc_footprint_limit(); + +/* + malloc_set_footprint_limit(); + Sets the maximum number of bytes to obtain from the system, causing + failure returns from malloc and related functions upon attempts to + exceed this value. The argument value may be subject to page + rounding to an enforceable limit; this actual value is returned. + Using an argument of the maximum possible size_t effectively + disables checks. If the argument is less than or equal to the + current malloc_footprint, then all future allocations that require + additional system memory will fail. However, invocation cannot + retroactively deallocate existing used memory. +*/ +DLMALLOC_EXPORT size_t dlmalloc_set_footprint_limit(size_t bytes); + +#if MALLOC_INSPECT_ALL +/* + malloc_inspect_all(void(*handler)(void *start, + void *end, + size_t used_bytes, + void* callback_arg), + void* arg); + Traverses the heap and calls the given handler for each managed + region, skipping all bytes that are (or may be) used for bookkeeping + purposes. Traversal does not include include chunks that have been + directly memory mapped. Each reported region begins at the start + address, and continues up to but not including the end address. The + first used_bytes of the region contain allocated data. If + used_bytes is zero, the region is unallocated. The handler is + invoked with the given callback argument. If locks are defined, they + are held during the entire traversal. It is a bad idea to invoke + other malloc functions from within the handler. + + For example, to count the number of in-use chunks with size greater + than 1000, you could write: + static int count = 0; + void count_chunks(void* start, void* end, size_t used, void* arg) { + if (used >= 1000) ++count; + } + then: + malloc_inspect_all(count_chunks, NULL); + + malloc_inspect_all is compiled only if MALLOC_INSPECT_ALL is defined. +*/ +DLMALLOC_EXPORT void dlmalloc_inspect_all(void(*handler)(void*, void *, size_t, void*), + void* arg); + +#endif /* MALLOC_INSPECT_ALL */ + +#if !NO_MALLINFO +/* + mallinfo() + Returns (by copy) a struct containing various summary statistics: + + arena: current total non-mmapped bytes allocated from system + ordblks: the number of free chunks + smblks: always zero. + hblks: current number of mmapped regions + hblkhd: total bytes held in mmapped regions + usmblks: the maximum total allocated space. This will be greater + than current total if trimming has occurred. + fsmblks: always zero + uordblks: current total allocated space (normal or mmapped) + fordblks: total free space + keepcost: the maximum number of bytes that could ideally be released + back to system via malloc_trim. ("ideally" means that + it ignores page restrictions etc.) + + Because these fields are ints, but internal bookkeeping may + be kept as longs, the reported values may wrap around zero and + thus be inaccurate. +*/ +DLMALLOC_EXPORT struct mallinfo dlmallinfo(void); +#endif /* NO_MALLINFO */ + +/* + independent_calloc(size_t n_elements, size_t element_size, void* chunks[]); + + independent_calloc is similar to calloc, but instead of returning a + single cleared space, it returns an array of pointers to n_elements + independent elements that can hold contents of size elem_size, each + of which starts out cleared, and can be independently freed, + realloc'ed etc. The elements are guaranteed to be adjacently + allocated (this is not guaranteed to occur with multiple callocs or + mallocs), which may also improve cache locality in some + applications. + + The "chunks" argument is optional (i.e., may be null, which is + probably the most typical usage). If it is null, the returned array + is itself dynamically allocated and should also be freed when it is + no longer needed. Otherwise, the chunks array must be of at least + n_elements in length. It is filled in with the pointers to the + chunks. + + In either case, independent_calloc returns this pointer array, or + null if the allocation failed. If n_elements is zero and "chunks" + is null, it returns a chunk representing an array with zero elements + (which should be freed if not wanted). + + Each element must be freed when it is no longer needed. This can be + done all at once using bulk_free. + + independent_calloc simplifies and speeds up implementations of many + kinds of pools. It may also be useful when constructing large data + structures that initially have a fixed number of fixed-sized nodes, + but the number is not known at compile time, and some of the nodes + may later need to be freed. For example: + + struct Node { int item; struct Node* next; }; + + struct Node* build_list() { + struct Node** pool; + int n = read_number_of_nodes_needed(); + if (n <= 0) return 0; + pool = (struct Node**)(independent_calloc(n, sizeof(struct Node), 0); + if (pool == 0) die(); + // organize into a linked list... + struct Node* first = pool[0]; + for (i = 0; i < n-1; ++i) + pool[i]->next = pool[i+1]; + free(pool); // Can now free the array (or not, if it is needed later) + return first; + } +*/ +DLMALLOC_EXPORT void** dlindependent_calloc(size_t, size_t, void**); + +/* + independent_comalloc(size_t n_elements, size_t sizes[], void* chunks[]); + + independent_comalloc allocates, all at once, a set of n_elements + chunks with sizes indicated in the "sizes" array. It returns + an array of pointers to these elements, each of which can be + independently freed, realloc'ed etc. The elements are guaranteed to + be adjacently allocated (this is not guaranteed to occur with + multiple callocs or mallocs), which may also improve cache locality + in some applications. + + The "chunks" argument is optional (i.e., may be null). If it is null + the returned array is itself dynamically allocated and should also + be freed when it is no longer needed. Otherwise, the chunks array + must be of at least n_elements in length. It is filled in with the + pointers to the chunks. + + In either case, independent_comalloc returns this pointer array, or + null if the allocation failed. If n_elements is zero and chunks is + null, it returns a chunk representing an array with zero elements + (which should be freed if not wanted). + + Each element must be freed when it is no longer needed. This can be + done all at once using bulk_free. + + independent_comallac differs from independent_calloc in that each + element may have a different size, and also that it does not + automatically clear elements. + + independent_comalloc can be used to speed up allocation in cases + where several structs or objects must always be allocated at the + same time. For example: + + struct Head { ... } + struct Foot { ... } + + void send_message(char* msg) { + int msglen = strlen(msg); + size_t sizes[3] = { sizeof(struct Head), msglen, sizeof(struct Foot) }; + void* chunks[3]; + if (independent_comalloc(3, sizes, chunks) == 0) + die(); + struct Head* head = (struct Head*)(chunks[0]); + char* body = (char*)(chunks[1]); + struct Foot* foot = (struct Foot*)(chunks[2]); + // ... + } + + In general though, independent_comalloc is worth using only for + larger values of n_elements. For small values, you probably won't + detect enough difference from series of malloc calls to bother. + + Overuse of independent_comalloc can increase overall memory usage, + since it cannot reuse existing noncontiguous small chunks that + might be available for some of the elements. +*/ +DLMALLOC_EXPORT void** dlindependent_comalloc(size_t, size_t*, void**); + +/* + bulk_free(void* array[], size_t n_elements) + Frees and clears (sets to null) each non-null pointer in the given + array. This is likely to be faster than freeing them one-by-one. + If footers are used, pointers that have been allocated in different + mspaces are not freed or cleared, and the count of all such pointers + is returned. For large arrays of pointers with poor locality, it + may be worthwhile to sort this array before calling bulk_free. +*/ +DLMALLOC_EXPORT size_t dlbulk_free(void**, size_t n_elements); + +/* + pvalloc(size_t n); + Equivalent to valloc(minimum-page-that-holds(n)), that is, + round up n to nearest pagesize. + */ +DLMALLOC_EXPORT void* dlpvalloc(size_t); + +/* + malloc_trim(size_t pad); + + If possible, gives memory back to the system (via negative arguments + to sbrk) if there is unused memory at the `high' end of the malloc + pool or in unused MMAP segments. You can call this after freeing + large blocks of memory to potentially reduce the system-level memory + requirements of a program. However, it cannot guarantee to reduce + memory. Under some allocation patterns, some large free blocks of + memory will be locked between two used chunks, so they cannot be + given back to the system. + + The `pad' argument to malloc_trim represents the amount of free + trailing space to leave untrimmed. If this argument is zero, only + the minimum amount of memory to maintain internal data structures + will be left. Non-zero arguments can be supplied to maintain enough + trailing space to service future expected allocations without having + to re-obtain memory from the system. + + Malloc_trim returns 1 if it actually released any memory, else 0. +*/ +DLMALLOC_EXPORT int dlmalloc_trim(size_t); + +/* + malloc_stats(); + Prints on stderr the amount of space obtained from the system (both + via sbrk and mmap), the maximum amount (which may be more than + current if malloc_trim and/or munmap got called), and the current + number of bytes allocated via malloc (or realloc, etc) but not yet + freed. Note that this is the number of bytes allocated, not the + number requested. It will be larger than the number requested + because of alignment and bookkeeping overhead. Because it includes + alignment wastage as being in use, this figure may be greater than + zero even when no user-level chunks are allocated. + + The reported current and maximum system memory can be inaccurate if + a program makes other calls to system memory allocation functions + (normally sbrk) outside of malloc. + + malloc_stats prints only the most commonly interesting statistics. + More information can be obtained by calling mallinfo. +*/ +DLMALLOC_EXPORT void dlmalloc_stats(void); + +/* + malloc_usable_size(void* p); + + Returns the number of bytes you can actually use in + an allocated chunk, which may be more than you requested (although + often not) due to alignment and minimum size constraints. + You can use this many bytes without worrying about + overwriting other allocated objects. This is not a particularly great + programming practice. malloc_usable_size can be more useful in + debugging and assertions, for example: + + p = malloc(n); + assert(malloc_usable_size(p) >= 256); +*/ +size_t dlmalloc_usable_size(void*); + +#endif /* ONLY_MSPACES */ + +#if MSPACES + +/* + mspace is an opaque type representing an independent + region of space that supports mspace_malloc, etc. +*/ +typedef void* mspace; + +/* + create_mspace creates and returns a new independent space with the + given initial capacity, or, if 0, the default granularity size. It + returns null if there is no system memory available to create the + space. If argument locked is non-zero, the space uses a separate + lock to control access. The capacity of the space will grow + dynamically as needed to service mspace_malloc requests. You can + control the sizes of incremental increases of this space by + compiling with a different DEFAULT_GRANULARITY or dynamically + setting with mallopt(M_GRANULARITY, value). +*/ +DLMALLOC_EXPORT mspace create_mspace(size_t capacity, int locked); + +/* + destroy_mspace destroys the given space, and attempts to return all + of its memory back to the system, returning the total number of + bytes freed. After destruction, the results of access to all memory + used by the space become undefined. +*/ +DLMALLOC_EXPORT size_t destroy_mspace(mspace msp); + +/* + create_mspace_with_base uses the memory supplied as the initial base + of a new mspace. Part (less than 128*sizeof(size_t) bytes) of this + space is used for bookkeeping, so the capacity must be at least this + large. (Otherwise 0 is returned.) When this initial space is + exhausted, additional memory will be obtained from the system. + Destroying this space will deallocate all additionally allocated + space (if possible) but not the initial base. +*/ +DLMALLOC_EXPORT mspace create_mspace_with_base(void* base, size_t capacity, int locked); + +/* + mspace_track_large_chunks controls whether requests for large chunks + are allocated in their own untracked mmapped regions, separate from + others in this mspace. By default large chunks are not tracked, + which reduces fragmentation. However, such chunks are not + necessarily released to the system upon destroy_mspace. Enabling + tracking by setting to true may increase fragmentation, but avoids + leakage when relying on destroy_mspace to release all memory + allocated using this space. The function returns the previous + setting. +*/ +DLMALLOC_EXPORT int mspace_track_large_chunks(mspace msp, int enable); + + +/* + mspace_malloc behaves as malloc, but operates within + the given space. +*/ +DLMALLOC_EXPORT void* mspace_malloc(mspace msp, size_t bytes); + +/* + mspace_free behaves as free, but operates within + the given space. + + If compiled with FOOTERS==1, mspace_free is not actually needed. + free may be called instead of mspace_free because freed chunks from + any space are handled by their originating spaces. +*/ +DLMALLOC_EXPORT void mspace_free(mspace msp, void* mem); + +/* + mspace_realloc behaves as realloc, but operates within + the given space. + + If compiled with FOOTERS==1, mspace_realloc is not actually + needed. realloc may be called instead of mspace_realloc because + realloced chunks from any space are handled by their originating + spaces. +*/ +DLMALLOC_EXPORT void* mspace_realloc(mspace msp, void* mem, size_t newsize); + +/* + mspace_calloc behaves as calloc, but operates within + the given space. +*/ +DLMALLOC_EXPORT void* mspace_calloc(mspace msp, size_t n_elements, size_t elem_size); + +/* + mspace_memalign behaves as memalign, but operates within + the given space. +*/ +DLMALLOC_EXPORT void* mspace_memalign(mspace msp, size_t alignment, size_t bytes); + +/* + mspace_independent_calloc behaves as independent_calloc, but + operates within the given space. +*/ +DLMALLOC_EXPORT void** mspace_independent_calloc(mspace msp, size_t n_elements, + size_t elem_size, void* chunks[]); + +/* + mspace_independent_comalloc behaves as independent_comalloc, but + operates within the given space. +*/ +DLMALLOC_EXPORT void** mspace_independent_comalloc(mspace msp, size_t n_elements, + size_t sizes[], void* chunks[]); + +/* + mspace_footprint() returns the number of bytes obtained from the + system for this space. +*/ +DLMALLOC_EXPORT size_t mspace_footprint(mspace msp); + +/* + mspace_max_footprint() returns the peak number of bytes obtained from the + system for this space. +*/ +DLMALLOC_EXPORT size_t mspace_max_footprint(mspace msp); + + +#if !NO_MALLINFO +/* + mspace_mallinfo behaves as mallinfo, but reports properties of + the given space. +*/ +DLMALLOC_EXPORT struct mallinfo mspace_mallinfo(mspace msp); +#endif /* NO_MALLINFO */ + +/* + malloc_usable_size(void* p) behaves the same as malloc_usable_size; +*/ +DLMALLOC_EXPORT size_t mspace_usable_size(const void* mem); + +/* + mspace_malloc_stats behaves as malloc_stats, but reports + properties of the given space. +*/ +DLMALLOC_EXPORT void mspace_malloc_stats(mspace msp); + +/* + mspace_trim behaves as malloc_trim, but + operates within the given space. +*/ +DLMALLOC_EXPORT int mspace_trim(mspace msp, size_t pad); + +/* + An alias for mallopt. +*/ +DLMALLOC_EXPORT int mspace_mallopt(int, int); + +#endif /* MSPACES */ + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif /* __cplusplus */ + +/* + ======================================================================== + To make a fully customizable malloc.h header file, cut everything + above this line, put into file malloc.h, edit to suit, and #include it + on the next line, as well as in programs that use this malloc. + ======================================================================== +*/ + +/* #include "malloc.h" */ + +/*------------------------------ internal #includes ---------------------- */ + +#ifdef _MSC_VER +#pragma warning( disable : 4146 ) /* no "unsigned" warnings */ +#endif /* _MSC_VER */ +#if !NO_MALLOC_STATS +#include /* for printing in malloc_stats */ +#endif /* NO_MALLOC_STATS */ +#ifndef LACKS_ERRNO_H +#include /* for MALLOC_FAILURE_ACTION */ +#endif /* LACKS_ERRNO_H */ +#ifdef DEBUG +#if ABORT_ON_ASSERT_FAILURE +#undef assert +#define assert(x) if(!(x)) ABORT +#else /* ABORT_ON_ASSERT_FAILURE */ +#include +#endif /* ABORT_ON_ASSERT_FAILURE */ +#else /* DEBUG */ +#ifndef assert +#define assert(x) +#endif +#define DEBUG 0 +#endif /* DEBUG */ +#if !defined(WIN32) && !defined(LACKS_TIME_H) +#include /* for magic initialization */ +#endif /* WIN32 */ +#ifndef LACKS_STDLIB_H +#include /* for abort() */ +#endif /* LACKS_STDLIB_H */ +#ifndef LACKS_STRING_H +#include /* for memset etc */ +#endif /* LACKS_STRING_H */ +#if USE_BUILTIN_FFS +#ifndef LACKS_STRINGS_H +#include /* for ffs */ +#endif /* LACKS_STRINGS_H */ +#endif /* USE_BUILTIN_FFS */ +#if HAVE_MMAP +#ifndef LACKS_SYS_MMAN_H +/* On some versions of linux, mremap decl in mman.h needs __USE_GNU set */ +#if (defined(linux) && !defined(__USE_GNU)) +#define __USE_GNU 1 +#include /* for mmap */ +#undef __USE_GNU +#else +#include /* for mmap */ +#endif /* linux */ +#endif /* LACKS_SYS_MMAN_H */ +#ifndef LACKS_FCNTL_H +#include +#endif /* LACKS_FCNTL_H */ +#endif /* HAVE_MMAP */ +#ifndef LACKS_UNISTD_H +#include /* for sbrk, sysconf */ +#else /* LACKS_UNISTD_H */ +#if !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__NetBSD__) +extern void* sbrk(ptrdiff_t); +#endif /* FreeBSD etc */ +#endif /* LACKS_UNISTD_H */ + +/* Declarations for locking */ +#if USE_LOCKS +#ifndef WIN32 +#if defined (__SVR4) && defined (__sun) /* solaris */ +#include +#elif !defined(LACKS_SCHED_H) +#include +#endif /* solaris or LACKS_SCHED_H */ +#if (defined(USE_RECURSIVE_LOCKS) && USE_RECURSIVE_LOCKS != 0) || !USE_SPIN_LOCKS +#include +#endif /* USE_RECURSIVE_LOCKS ... */ +#elif defined(_MSC_VER) +#ifndef _M_AMD64 +/* These are already defined on AMD64 builds */ +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ +LONG __cdecl _InterlockedCompareExchange(LONG volatile *Dest, LONG Exchange, LONG Comp); +LONG __cdecl _InterlockedExchange(LONG volatile *Target, LONG Value); +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* _M_AMD64 */ +#pragma intrinsic (_InterlockedCompareExchange) +#pragma intrinsic (_InterlockedExchange) +#define interlockedcompareexchange _InterlockedCompareExchange +#define interlockedexchange _InterlockedExchange +#elif defined(WIN32) && defined(__GNUC__) +#define interlockedcompareexchange(a, b, c) __sync_val_compare_and_swap(a, c, b) +#define interlockedexchange __sync_lock_test_and_set +#endif /* Win32 */ +#else /* USE_LOCKS */ +#endif /* USE_LOCKS */ + +#ifndef LOCK_AT_FORK +#define LOCK_AT_FORK 0 +#endif + +/* Declarations for bit scanning on win32 */ +#if defined(_MSC_VER) && _MSC_VER>=1300 +#ifndef BitScanForward /* Try to avoid pulling in WinNT.h */ +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ +unsigned char _BitScanForward(unsigned long *index, unsigned long mask); +unsigned char _BitScanReverse(unsigned long *index, unsigned long mask); +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#define BitScanForward _BitScanForward +#define BitScanReverse _BitScanReverse +#pragma intrinsic(_BitScanForward) +#pragma intrinsic(_BitScanReverse) +#endif /* BitScanForward */ +#endif /* defined(_MSC_VER) && _MSC_VER>=1300 */ + +#ifndef WIN32 +#ifndef malloc_getpagesize +# ifdef _SC_PAGESIZE /* some SVR4 systems omit an underscore */ +# ifndef _SC_PAGE_SIZE +# define _SC_PAGE_SIZE _SC_PAGESIZE +# endif +# endif +# ifdef _SC_PAGE_SIZE +# define malloc_getpagesize sysconf(_SC_PAGE_SIZE) +# else +# if defined(BSD) || defined(DGUX) || defined(HAVE_GETPAGESIZE) + extern size_t getpagesize(); +# define malloc_getpagesize getpagesize() +# else +# ifdef WIN32 /* use supplied emulation of getpagesize */ +# define malloc_getpagesize getpagesize() +# else +# ifndef LACKS_SYS_PARAM_H +# include +# endif +# ifdef EXEC_PAGESIZE +# define malloc_getpagesize EXEC_PAGESIZE +# else +# ifdef NBPG +# ifndef CLSIZE +# define malloc_getpagesize NBPG +# else +# define malloc_getpagesize (NBPG * CLSIZE) +# endif +# else +# ifdef NBPC +# define malloc_getpagesize NBPC +# else +# ifdef PAGESIZE +# define malloc_getpagesize PAGESIZE +# else /* just guess */ +# define malloc_getpagesize ((size_t)4096U) +# endif +# endif +# endif +# endif +# endif +# endif +# endif +#endif +#endif + +/* ------------------- size_t and alignment properties -------------------- */ + +/* The byte and bit size of a size_t */ +#define SIZE_T_SIZE (sizeof(size_t)) +#define SIZE_T_BITSIZE (sizeof(size_t) << 3) + +/* Some constants coerced to size_t */ +/* Annoying but necessary to avoid errors on some platforms */ +#define SIZE_T_ZERO ((size_t)0) +#define SIZE_T_ONE ((size_t)1) +#define SIZE_T_TWO ((size_t)2) +#define SIZE_T_FOUR ((size_t)4) +#define TWO_SIZE_T_SIZES (SIZE_T_SIZE<<1) +#define FOUR_SIZE_T_SIZES (SIZE_T_SIZE<<2) +#define SIX_SIZE_T_SIZES (FOUR_SIZE_T_SIZES+TWO_SIZE_T_SIZES) +#define HALF_MAX_SIZE_T (MAX_SIZE_T / 2U) + +/* The bit mask value corresponding to MALLOC_ALIGNMENT */ +#define CHUNK_ALIGN_MASK (MALLOC_ALIGNMENT - SIZE_T_ONE) + +/* True if address a has acceptable alignment */ +#define is_aligned(A) (((size_t)((A)) & (CHUNK_ALIGN_MASK)) == 0) + +/* the number of bytes to offset an address to align it */ +#define align_offset(A)\ + ((((size_t)(A) & CHUNK_ALIGN_MASK) == 0)? 0 :\ + ((MALLOC_ALIGNMENT - ((size_t)(A) & CHUNK_ALIGN_MASK)) & CHUNK_ALIGN_MASK)) + +/* -------------------------- MMAP preliminaries ------------------------- */ + +/* + If HAVE_MORECORE or HAVE_MMAP are false, we just define calls and + checks to fail so compiler optimizer can delete code rather than + using so many "#if"s. +*/ + + +/* MORECORE and MMAP must return MFAIL on failure */ +#define MFAIL ((void*)(MAX_SIZE_T)) +#define CMFAIL ((char*)(MFAIL)) /* defined for convenience */ + +#if HAVE_MMAP + +#ifndef WIN32 +#define MUNMAP_DEFAULT(a, s) munmap((a), (s)) +#define MMAP_PROT (PROT_READ|PROT_WRITE) +#if !defined(MAP_ANONYMOUS) && defined(MAP_ANON) +#define MAP_ANONYMOUS MAP_ANON +#endif /* MAP_ANON */ +#ifdef MAP_ANONYMOUS +#define MMAP_FLAGS (MAP_PRIVATE|MAP_ANONYMOUS) +#define MMAP_DEFAULT(s) mmap(0, (s), MMAP_PROT, MMAP_FLAGS, -1, 0) +#else /* MAP_ANONYMOUS */ +/* + Nearly all versions of mmap support MAP_ANONYMOUS, so the following + is unlikely to be needed, but is supplied just in case. +*/ +#define MMAP_FLAGS (MAP_PRIVATE) +static int dev_zero_fd = -1; /* Cached file descriptor for /dev/zero. */ +#define MMAP_DEFAULT(s) ((dev_zero_fd < 0) ? \ + (dev_zero_fd = open("/dev/zero", O_RDWR), \ + mmap(0, (s), MMAP_PROT, MMAP_FLAGS, dev_zero_fd, 0)) : \ + mmap(0, (s), MMAP_PROT, MMAP_FLAGS, dev_zero_fd, 0)) +#endif /* MAP_ANONYMOUS */ + +#define DIRECT_MMAP_DEFAULT(s) MMAP_DEFAULT(s) + +#else /* WIN32 */ + +/* Win32 MMAP via VirtualAlloc */ +static FORCEINLINE void* win32mmap(size_t size) { + void* ptr = VirtualAlloc(0, size, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE); + return (ptr != 0)? ptr: MFAIL; +} + +/* For direct MMAP, use MEM_TOP_DOWN to minimize interference */ +static FORCEINLINE void* win32direct_mmap(size_t size) { + void* ptr = VirtualAlloc(0, size, MEM_RESERVE|MEM_COMMIT|MEM_TOP_DOWN, + PAGE_READWRITE); + return (ptr != 0)? ptr: MFAIL; +} + +/* This function supports releasing coalesed segments */ +static FORCEINLINE int win32munmap(void* ptr, size_t size) { + MEMORY_BASIC_INFORMATION minfo; + char* cptr = (char*)ptr; + while (size) { + if (VirtualQuery(cptr, &minfo, sizeof(minfo)) == 0) + return -1; + if (minfo.BaseAddress != cptr || minfo.AllocationBase != cptr || + minfo.State != MEM_COMMIT || minfo.RegionSize > size) + return -1; + if (VirtualFree(cptr, 0, MEM_RELEASE) == 0) + return -1; + cptr += minfo.RegionSize; + size -= minfo.RegionSize; + } + return 0; +} + +#define MMAP_DEFAULT(s) win32mmap(s) +#define MUNMAP_DEFAULT(a, s) win32munmap((a), (s)) +#define DIRECT_MMAP_DEFAULT(s) win32direct_mmap(s) +#endif /* WIN32 */ +#endif /* HAVE_MMAP */ + +#if HAVE_MREMAP +#ifndef WIN32 +#define MREMAP_DEFAULT(addr, osz, nsz, mv) mremap((addr), (osz), (nsz), (mv)) +#endif /* WIN32 */ +#endif /* HAVE_MREMAP */ + +/** + * Define CALL_MORECORE + */ +#if HAVE_MORECORE + #ifdef MORECORE + #define CALL_MORECORE(S) MORECORE(S) + #else /* MORECORE */ + #define CALL_MORECORE(S) MORECORE_DEFAULT(S) + #endif /* MORECORE */ +#else /* HAVE_MORECORE */ + #define CALL_MORECORE(S) MFAIL +#endif /* HAVE_MORECORE */ + +/** + * Define CALL_MMAP/CALL_MUNMAP/CALL_DIRECT_MMAP + */ +#if HAVE_MMAP + #define USE_MMAP_BIT (SIZE_T_ONE) + + #ifdef MMAP + #define CALL_MMAP(s) MMAP(s) + #else /* MMAP */ + #define CALL_MMAP(s) MMAP_DEFAULT(s) + #endif /* MMAP */ + #ifdef MUNMAP + #define CALL_MUNMAP(a, s) MUNMAP((a), (s)) + #else /* MUNMAP */ + #define CALL_MUNMAP(a, s) MUNMAP_DEFAULT((a), (s)) + #endif /* MUNMAP */ + #ifdef DIRECT_MMAP + #define CALL_DIRECT_MMAP(s) DIRECT_MMAP(s) + #else /* DIRECT_MMAP */ + #define CALL_DIRECT_MMAP(s) DIRECT_MMAP_DEFAULT(s) + #endif /* DIRECT_MMAP */ +#else /* HAVE_MMAP */ + #define USE_MMAP_BIT (SIZE_T_ZERO) + + #define MMAP(s) MFAIL + #define MUNMAP(a, s) (-1) + #define DIRECT_MMAP(s) MFAIL + #define CALL_DIRECT_MMAP(s) DIRECT_MMAP(s) + #define CALL_MMAP(s) MMAP(s) + #define CALL_MUNMAP(a, s) MUNMAP((a), (s)) +#endif /* HAVE_MMAP */ + +/** + * Define CALL_MREMAP + */ +#if HAVE_MMAP && HAVE_MREMAP + #ifdef MREMAP + #define CALL_MREMAP(addr, osz, nsz, mv) MREMAP((addr), (osz), (nsz), (mv)) + #else /* MREMAP */ + #define CALL_MREMAP(addr, osz, nsz, mv) MREMAP_DEFAULT((addr), (osz), (nsz), (mv)) + #endif /* MREMAP */ +#else /* HAVE_MMAP && HAVE_MREMAP */ + #define CALL_MREMAP(addr, osz, nsz, mv) MFAIL +#endif /* HAVE_MMAP && HAVE_MREMAP */ + +/* mstate bit set if continguous morecore disabled or failed */ +#define USE_NONCONTIGUOUS_BIT (4U) + +/* segment bit set in create_mspace_with_base */ +#define EXTERN_BIT (8U) + + +/* --------------------------- Lock preliminaries ------------------------ */ + +/* + When locks are defined, there is one global lock, plus + one per-mspace lock. + + The global lock_ensures that mparams.magic and other unique + mparams values are initialized only once. It also protects + sequences of calls to MORECORE. In many cases sys_alloc requires + two calls, that should not be interleaved with calls by other + threads. This does not protect against direct calls to MORECORE + by other threads not using this lock, so there is still code to + cope the best we can on interference. + + Per-mspace locks surround calls to malloc, free, etc. + By default, locks are simple non-reentrant mutexes. + + Because lock-protected regions generally have bounded times, it is + OK to use the supplied simple spinlocks. Spinlocks are likely to + improve performance for lightly contended applications, but worsen + performance under heavy contention. + + If USE_LOCKS is > 1, the definitions of lock routines here are + bypassed, in which case you will need to define the type MLOCK_T, + and at least INITIAL_LOCK, DESTROY_LOCK, ACQUIRE_LOCK, RELEASE_LOCK + and TRY_LOCK. You must also declare a + static MLOCK_T malloc_global_mutex = { initialization values };. + +*/ + +#if !USE_LOCKS +#define USE_LOCK_BIT (0U) +#define INITIAL_LOCK(l) (0) +#define DESTROY_LOCK(l) (0) +#define ACQUIRE_MALLOC_GLOBAL_LOCK() +#define RELEASE_MALLOC_GLOBAL_LOCK() + +#else +#if USE_LOCKS > 1 +/* ----------------------- User-defined locks ------------------------ */ +/* Define your own lock implementation here */ +/* #define INITIAL_LOCK(lk) ... */ +/* #define DESTROY_LOCK(lk) ... */ +/* #define ACQUIRE_LOCK(lk) ... */ +/* #define RELEASE_LOCK(lk) ... */ +/* #define TRY_LOCK(lk) ... */ +/* static MLOCK_T malloc_global_mutex = ... */ + +#elif USE_SPIN_LOCKS + +/* First, define CAS_LOCK and CLEAR_LOCK on ints */ +/* Note CAS_LOCK defined to return 0 on success */ + +#if defined(__GNUC__)&& (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1)) +#define CAS_LOCK(sl) __sync_lock_test_and_set(sl, 1) +#define CLEAR_LOCK(sl) __sync_lock_release(sl) + +#elif (defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))) +/* Custom spin locks for older gcc on x86 */ +static FORCEINLINE int x86_cas_lock(int *sl) { + int ret; + int val = 1; + int cmp = 0; + __asm__ __volatile__ ("lock; cmpxchgl %1, %2" + : "=a" (ret) + : "r" (val), "m" (*(sl)), "0"(cmp) + : "memory", "cc"); + return ret; +} + +static FORCEINLINE void x86_clear_lock(int* sl) { + assert(*sl != 0); + int prev = 0; + int ret; + __asm__ __volatile__ ("lock; xchgl %0, %1" + : "=r" (ret) + : "m" (*(sl)), "0"(prev) + : "memory"); +} + +#define CAS_LOCK(sl) x86_cas_lock(sl) +#define CLEAR_LOCK(sl) x86_clear_lock(sl) + +#else /* Win32 MSC */ +#define CAS_LOCK(sl) interlockedexchange(sl, (LONG)1) +#define CLEAR_LOCK(sl) interlockedexchange (sl, (LONG)0) + +#endif /* ... gcc spins locks ... */ + +/* How to yield for a spin lock */ +#define SPINS_PER_YIELD 63 +#if defined(_MSC_VER) +#define SLEEP_EX_DURATION 50 /* delay for yield/sleep */ +#define SPIN_LOCK_YIELD SleepEx(SLEEP_EX_DURATION, FALSE) +#elif defined (__SVR4) && defined (__sun) /* solaris */ +#define SPIN_LOCK_YIELD thr_yield(); +#elif !defined(LACKS_SCHED_H) +#define SPIN_LOCK_YIELD sched_yield(); +#else +#define SPIN_LOCK_YIELD +#endif /* ... yield ... */ + +#if !defined(USE_RECURSIVE_LOCKS) || USE_RECURSIVE_LOCKS == 0 +/* Plain spin locks use single word (embedded in malloc_states) */ +static int spin_acquire_lock(int *sl) { + int spins = 0; + while (*(volatile int *)sl != 0 || CAS_LOCK(sl)) { + if ((++spins & SPINS_PER_YIELD) == 0) { + SPIN_LOCK_YIELD; + } + } + return 0; +} + +#define MLOCK_T int +#define TRY_LOCK(sl) !CAS_LOCK(sl) +#define RELEASE_LOCK(sl) CLEAR_LOCK(sl) +#define ACQUIRE_LOCK(sl) (CAS_LOCK(sl)? spin_acquire_lock(sl) : 0) +#define INITIAL_LOCK(sl) (*sl = 0) +#define DESTROY_LOCK(sl) (0) +static MLOCK_T malloc_global_mutex = 0; + +#else /* USE_RECURSIVE_LOCKS */ +/* types for lock owners */ +#ifdef WIN32 +#define THREAD_ID_T DWORD +#define CURRENT_THREAD GetCurrentThreadId() +#define EQ_OWNER(X,Y) ((X) == (Y)) +#else +/* + Note: the following assume that pthread_t is a type that can be + initialized to (casted) zero. If this is not the case, you will need to + somehow redefine these or not use spin locks. +*/ +#define THREAD_ID_T pthread_t +#define CURRENT_THREAD pthread_self() +#define EQ_OWNER(X,Y) pthread_equal(X, Y) +#endif + +struct malloc_recursive_lock { + int sl; + unsigned int c; + THREAD_ID_T threadid; +}; + +#define MLOCK_T struct malloc_recursive_lock +static MLOCK_T malloc_global_mutex = { 0, 0, (THREAD_ID_T)0}; + +static FORCEINLINE void recursive_release_lock(MLOCK_T *lk) { + assert(lk->sl != 0); + if (--lk->c == 0) { + CLEAR_LOCK(&lk->sl); + } +} + +static FORCEINLINE int recursive_acquire_lock(MLOCK_T *lk) { + THREAD_ID_T mythreadid = CURRENT_THREAD; + int spins = 0; + for (;;) { + if (*((volatile int *)(&lk->sl)) == 0) { + if (!CAS_LOCK(&lk->sl)) { + lk->threadid = mythreadid; + lk->c = 1; + return 0; + } + } + else if (EQ_OWNER(lk->threadid, mythreadid)) { + ++lk->c; + return 0; + } + if ((++spins & SPINS_PER_YIELD) == 0) { + SPIN_LOCK_YIELD; + } + } +} + +static FORCEINLINE int recursive_try_lock(MLOCK_T *lk) { + THREAD_ID_T mythreadid = CURRENT_THREAD; + if (*((volatile int *)(&lk->sl)) == 0) { + if (!CAS_LOCK(&lk->sl)) { + lk->threadid = mythreadid; + lk->c = 1; + return 1; + } + } + else if (EQ_OWNER(lk->threadid, mythreadid)) { + ++lk->c; + return 1; + } + return 0; +} + +#define RELEASE_LOCK(lk) recursive_release_lock(lk) +#define TRY_LOCK(lk) recursive_try_lock(lk) +#define ACQUIRE_LOCK(lk) recursive_acquire_lock(lk) +#define INITIAL_LOCK(lk) ((lk)->threadid = (THREAD_ID_T)0, (lk)->sl = 0, (lk)->c = 0) +#define DESTROY_LOCK(lk) (0) +#endif /* USE_RECURSIVE_LOCKS */ + +#elif defined(WIN32) /* Win32 critical sections */ +#define MLOCK_T CRITICAL_SECTION +#define ACQUIRE_LOCK(lk) (EnterCriticalSection(lk), 0) +#define RELEASE_LOCK(lk) LeaveCriticalSection(lk) +#define TRY_LOCK(lk) TryEnterCriticalSection(lk) +#define INITIAL_LOCK(lk) (!InitializeCriticalSectionAndSpinCount((lk), 0x80000000|4000)) +#define DESTROY_LOCK(lk) (DeleteCriticalSection(lk), 0) +#define NEED_GLOBAL_LOCK_INIT + +static MLOCK_T malloc_global_mutex; +static volatile LONG malloc_global_mutex_status; + +/* Use spin loop to initialize global lock */ +static void init_malloc_global_mutex() { + for (;;) { + long stat = malloc_global_mutex_status; + if (stat > 0) + return; + /* transition to < 0 while initializing, then to > 0) */ + if (stat == 0 && + interlockedcompareexchange(&malloc_global_mutex_status, (LONG)-1, (LONG)0) == 0) { + InitializeCriticalSection(&malloc_global_mutex); + interlockedexchange(&malloc_global_mutex_status, (LONG)1); + return; + } + SleepEx(0, FALSE); + } +} + +#else /* pthreads-based locks */ +#define MLOCK_T pthread_mutex_t +#define ACQUIRE_LOCK(lk) pthread_mutex_lock(lk) +#define RELEASE_LOCK(lk) pthread_mutex_unlock(lk) +#define TRY_LOCK(lk) (!pthread_mutex_trylock(lk)) +#define INITIAL_LOCK(lk) pthread_init_lock(lk) +#define DESTROY_LOCK(lk) pthread_mutex_destroy(lk) + +#if defined(USE_RECURSIVE_LOCKS) && USE_RECURSIVE_LOCKS != 0 && defined(linux) && !defined(PTHREAD_MUTEX_RECURSIVE) +/* Cope with old-style linux recursive lock initialization by adding */ +/* skipped internal declaration from pthread.h */ +extern int pthread_mutexattr_setkind_np __P ((pthread_mutexattr_t *__attr, + int __kind)); +#define PTHREAD_MUTEX_RECURSIVE PTHREAD_MUTEX_RECURSIVE_NP +#define pthread_mutexattr_settype(x,y) pthread_mutexattr_setkind_np(x,y) +#endif /* USE_RECURSIVE_LOCKS ... */ + +static MLOCK_T malloc_global_mutex = PTHREAD_MUTEX_INITIALIZER; + +static int pthread_init_lock (MLOCK_T *lk) { + pthread_mutexattr_t attr; + if (pthread_mutexattr_init(&attr)) return 1; +#if defined(USE_RECURSIVE_LOCKS) && USE_RECURSIVE_LOCKS != 0 + if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE)) return 1; +#endif + if (pthread_mutex_init(lk, &attr)) return 1; + if (pthread_mutexattr_destroy(&attr)) return 1; + return 0; +} + +#endif /* ... lock types ... */ + +/* Common code for all lock types */ +#define USE_LOCK_BIT (2U) + +#ifndef ACQUIRE_MALLOC_GLOBAL_LOCK +#define ACQUIRE_MALLOC_GLOBAL_LOCK() ACQUIRE_LOCK(&malloc_global_mutex); +#endif + +#ifndef RELEASE_MALLOC_GLOBAL_LOCK +#define RELEASE_MALLOC_GLOBAL_LOCK() RELEASE_LOCK(&malloc_global_mutex); +#endif + +#endif /* USE_LOCKS */ + +/* ----------------------- Chunk representations ------------------------ */ + +/* + (The following includes lightly edited explanations by Colin Plumb.) + + The malloc_chunk declaration below is misleading (but accurate and + necessary). It declares a "view" into memory allowing access to + necessary fields at known offsets from a given base. + + Chunks of memory are maintained using a `boundary tag' method as + originally described by Knuth. (See the paper by Paul Wilson + ftp://ftp.cs.utexas.edu/pub/garbage/allocsrv.ps for a survey of such + techniques.) Sizes of free chunks are stored both in the front of + each chunk and at the end. This makes consolidating fragmented + chunks into bigger chunks fast. The head fields also hold bits + representing whether chunks are free or in use. + + Here are some pictures to make it clearer. They are "exploded" to + show that the state of a chunk can be thought of as extending from + the high 31 bits of the head field of its header through the + prev_foot and PINUSE_BIT bit of the following chunk header. + + A chunk that's in use looks like: + + chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Size of previous chunk (if P = 0) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |P| + | Size of this chunk 1| +-+ + mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | | + +- -+ + | | + +- -+ + | : + +- size - sizeof(size_t) available payload bytes -+ + : | + chunk-> +- -+ + | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |1| + | Size of next chunk (may or may not be in use) | +-+ + mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + And if it's free, it looks like this: + + chunk-> +- -+ + | User payload (must be in use, or we would have merged!) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |P| + | Size of this chunk 0| +-+ + mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Next pointer | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Prev pointer | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | : + +- size - sizeof(struct chunk) unused bytes -+ + : | + chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Size of this chunk | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |0| + | Size of next chunk (must be in use, or we would have merged)| +-+ + mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | : + +- User payload -+ + : | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |0| + +-+ + Note that since we always merge adjacent free chunks, the chunks + adjacent to a free chunk must be in use. + + Given a pointer to a chunk (which can be derived trivially from the + payload pointer) we can, in O(1) time, find out whether the adjacent + chunks are free, and if so, unlink them from the lists that they + are on and merge them with the current chunk. + + Chunks always begin on even word boundaries, so the mem portion + (which is returned to the user) is also on an even word boundary, and + thus at least double-word aligned. + + The P (PINUSE_BIT) bit, stored in the unused low-order bit of the + chunk size (which is always a multiple of two words), is an in-use + bit for the *previous* chunk. If that bit is *clear*, then the + word before the current chunk size contains the previous chunk + size, and can be used to find the front of the previous chunk. + The very first chunk allocated always has this bit set, preventing + access to non-existent (or non-owned) memory. If pinuse is set for + any given chunk, then you CANNOT determine the size of the + previous chunk, and might even get a memory addressing fault when + trying to do so. + + The C (CINUSE_BIT) bit, stored in the unused second-lowest bit of + the chunk size redundantly records whether the current chunk is + inuse (unless the chunk is mmapped). This redundancy enables usage + checks within free and realloc, and reduces indirection when freeing + and consolidating chunks. + + Each freshly allocated chunk must have both cinuse and pinuse set. + That is, each allocated chunk borders either a previously allocated + and still in-use chunk, or the base of its memory arena. This is + ensured by making all allocations from the `lowest' part of any + found chunk. Further, no free chunk physically borders another one, + so each free chunk is known to be preceded and followed by either + inuse chunks or the ends of memory. + + Note that the `foot' of the current chunk is actually represented + as the prev_foot of the NEXT chunk. This makes it easier to + deal with alignments etc but can be very confusing when trying + to extend or adapt this code. + + The exceptions to all this are + + 1. The special chunk `top' is the top-most available chunk (i.e., + the one bordering the end of available memory). It is treated + specially. Top is never included in any bin, is used only if + no other chunk is available, and is released back to the + system if it is very large (see M_TRIM_THRESHOLD). In effect, + the top chunk is treated as larger (and thus less well + fitting) than any other available chunk. The top chunk + doesn't update its trailing size field since there is no next + contiguous chunk that would have to index off it. However, + space is still allocated for it (TOP_FOOT_SIZE) to enable + separation or merging when space is extended. + + 3. Chunks allocated via mmap, have both cinuse and pinuse bits + cleared in their head fields. Because they are allocated + one-by-one, each must carry its own prev_foot field, which is + also used to hold the offset this chunk has within its mmapped + region, which is needed to preserve alignment. Each mmapped + chunk is trailed by the first two fields of a fake next-chunk + for sake of usage checks. + +*/ + +struct malloc_chunk { + size_t prev_foot; /* Size of previous chunk (if free). */ + size_t head; /* Size and inuse bits. */ + struct malloc_chunk* fd; /* double links -- used only if free. */ + struct malloc_chunk* bk; +}; + +typedef struct malloc_chunk mchunk; +typedef struct malloc_chunk* mchunkptr; +typedef struct malloc_chunk* sbinptr; /* The type of bins of chunks */ +typedef unsigned int bindex_t; /* Described below */ +typedef unsigned int binmap_t; /* Described below */ +typedef unsigned int flag_t; /* The type of various bit flag sets */ + +/* ------------------- Chunks sizes and alignments ----------------------- */ + +#define MCHUNK_SIZE (sizeof(mchunk)) + +#if FOOTERS +#define CHUNK_OVERHEAD (TWO_SIZE_T_SIZES) +#else /* FOOTERS */ +#define CHUNK_OVERHEAD (SIZE_T_SIZE) +#endif /* FOOTERS */ + +/* MMapped chunks need a second word of overhead ... */ +#define MMAP_CHUNK_OVERHEAD (TWO_SIZE_T_SIZES) +/* ... and additional padding for fake next-chunk at foot */ +#define MMAP_FOOT_PAD (FOUR_SIZE_T_SIZES) + +/* The smallest size we can malloc is an aligned minimal chunk */ +#define MIN_CHUNK_SIZE\ + ((MCHUNK_SIZE + CHUNK_ALIGN_MASK) & ~CHUNK_ALIGN_MASK) + +/* conversion from malloc headers to user pointers, and back */ +#define chunk2mem(p) ((void*)((char*)(p) + TWO_SIZE_T_SIZES)) +#define mem2chunk(mem) ((mchunkptr)((char*)(mem) - TWO_SIZE_T_SIZES)) +/* chunk associated with aligned address A */ +#define align_as_chunk(A) (mchunkptr)((A) + align_offset(chunk2mem(A))) + +/* Bounds on request (not chunk) sizes. */ +#define MAX_REQUEST ((-MIN_CHUNK_SIZE) << 2) +#define MIN_REQUEST (MIN_CHUNK_SIZE - CHUNK_OVERHEAD - SIZE_T_ONE) + +/* pad request bytes into a usable size */ +#define pad_request(req) \ + (((req) + CHUNK_OVERHEAD + CHUNK_ALIGN_MASK) & ~CHUNK_ALIGN_MASK) + +/* pad request, checking for minimum (but not maximum) */ +#define request2size(req) \ + (((req) < MIN_REQUEST)? MIN_CHUNK_SIZE : pad_request(req)) + + +/* ------------------ Operations on head and foot fields ----------------- */ + +/* + The head field of a chunk is or'ed with PINUSE_BIT when previous + adjacent chunk in use, and or'ed with CINUSE_BIT if this chunk is in + use, unless mmapped, in which case both bits are cleared. + + FLAG4_BIT is not used by this malloc, but might be useful in extensions. +*/ + +#define PINUSE_BIT (SIZE_T_ONE) +#define CINUSE_BIT (SIZE_T_TWO) +#define FLAG4_BIT (SIZE_T_FOUR) +#define INUSE_BITS (PINUSE_BIT|CINUSE_BIT) +#define FLAG_BITS (PINUSE_BIT|CINUSE_BIT|FLAG4_BIT) + +/* Head value for fenceposts */ +#define FENCEPOST_HEAD (INUSE_BITS|SIZE_T_SIZE) + +/* extraction of fields from head words */ +#define cinuse(p) ((p)->head & CINUSE_BIT) +#define pinuse(p) ((p)->head & PINUSE_BIT) +#define flag4inuse(p) ((p)->head & FLAG4_BIT) +#define is_inuse(p) (((p)->head & INUSE_BITS) != PINUSE_BIT) +#define is_mmapped(p) (((p)->head & INUSE_BITS) == 0) + +#define chunksize(p) ((p)->head & ~(FLAG_BITS)) + +#define clear_pinuse(p) ((p)->head &= ~PINUSE_BIT) +#define set_flag4(p) ((p)->head |= FLAG4_BIT) +#define clear_flag4(p) ((p)->head &= ~FLAG4_BIT) + +/* Treat space at ptr +/- offset as a chunk */ +#define chunk_plus_offset(p, s) ((mchunkptr)(((char*)(p)) + (s))) +#define chunk_minus_offset(p, s) ((mchunkptr)(((char*)(p)) - (s))) + +/* Ptr to next or previous physical malloc_chunk. */ +#define next_chunk(p) ((mchunkptr)( ((char*)(p)) + ((p)->head & ~FLAG_BITS))) +#define prev_chunk(p) ((mchunkptr)( ((char*)(p)) - ((p)->prev_foot) )) + +/* extract next chunk's pinuse bit */ +#define next_pinuse(p) ((next_chunk(p)->head) & PINUSE_BIT) + +/* Get/set size at footer */ +#define get_foot(p, s) (((mchunkptr)((char*)(p) + (s)))->prev_foot) +#define set_foot(p, s) (((mchunkptr)((char*)(p) + (s)))->prev_foot = (s)) + +/* Set size, pinuse bit, and foot */ +#define set_size_and_pinuse_of_free_chunk(p, s)\ + ((p)->head = (s|PINUSE_BIT), set_foot(p, s)) + +/* Set size, pinuse bit, foot, and clear next pinuse */ +#define set_free_with_pinuse(p, s, n)\ + (clear_pinuse(n), set_size_and_pinuse_of_free_chunk(p, s)) + +/* Get the internal overhead associated with chunk p */ +#define overhead_for(p)\ + (is_mmapped(p)? MMAP_CHUNK_OVERHEAD : CHUNK_OVERHEAD) + +/* Return true if malloced space is not necessarily cleared */ +#if MMAP_CLEARS +#define calloc_must_clear(p) (!is_mmapped(p)) +#else /* MMAP_CLEARS */ +#define calloc_must_clear(p) (1) +#endif /* MMAP_CLEARS */ + +/* ---------------------- Overlaid data structures ----------------------- */ + +/* + When chunks are not in use, they are treated as nodes of either + lists or trees. + + "Small" chunks are stored in circular doubly-linked lists, and look + like this: + + chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Size of previous chunk | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + `head:' | Size of chunk, in bytes |P| + mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Forward pointer to next chunk in list | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Back pointer to previous chunk in list | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Unused space (may be 0 bytes long) . + . . + . | +nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + `foot:' | Size of chunk, in bytes | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Larger chunks are kept in a form of bitwise digital trees (aka + tries) keyed on chunksizes. Because malloc_tree_chunks are only for + free chunks greater than 256 bytes, their size doesn't impose any + constraints on user chunk sizes. Each node looks like: + + chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Size of previous chunk | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + `head:' | Size of chunk, in bytes |P| + mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Forward pointer to next chunk of same size | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Back pointer to previous chunk of same size | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Pointer to left child (child[0]) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Pointer to right child (child[1]) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Pointer to parent | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | bin index of this chunk | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Unused space . + . | +nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + `foot:' | Size of chunk, in bytes | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Each tree holding treenodes is a tree of unique chunk sizes. Chunks + of the same size are arranged in a circularly-linked list, with only + the oldest chunk (the next to be used, in our FIFO ordering) + actually in the tree. (Tree members are distinguished by a non-null + parent pointer.) If a chunk with the same size an an existing node + is inserted, it is linked off the existing node using pointers that + work in the same way as fd/bk pointers of small chunks. + + Each tree contains a power of 2 sized range of chunk sizes (the + smallest is 0x100 <= x < 0x180), which is is divided in half at each + tree level, with the chunks in the smaller half of the range (0x100 + <= x < 0x140 for the top nose) in the left subtree and the larger + half (0x140 <= x < 0x180) in the right subtree. This is, of course, + done by inspecting individual bits. + + Using these rules, each node's left subtree contains all smaller + sizes than its right subtree. However, the node at the root of each + subtree has no particular ordering relationship to either. (The + dividing line between the subtree sizes is based on trie relation.) + If we remove the last chunk of a given size from the interior of the + tree, we need to replace it with a leaf node. The tree ordering + rules permit a node to be replaced by any leaf below it. + + The smallest chunk in a tree (a common operation in a best-fit + allocator) can be found by walking a path to the leftmost leaf in + the tree. Unlike a usual binary tree, where we follow left child + pointers until we reach a null, here we follow the right child + pointer any time the left one is null, until we reach a leaf with + both child pointers null. The smallest chunk in the tree will be + somewhere along that path. + + The worst case number of steps to add, find, or remove a node is + bounded by the number of bits differentiating chunks within + bins. Under current bin calculations, this ranges from 6 up to 21 + (for 32 bit sizes) or up to 53 (for 64 bit sizes). The typical case + is of course much better. +*/ + +struct malloc_tree_chunk { + /* The first four fields must be compatible with malloc_chunk */ + size_t prev_foot; + size_t head; + struct malloc_tree_chunk* fd; + struct malloc_tree_chunk* bk; + + struct malloc_tree_chunk* child[2]; + struct malloc_tree_chunk* parent; + bindex_t index; +}; + +typedef struct malloc_tree_chunk tchunk; +typedef struct malloc_tree_chunk* tchunkptr; +typedef struct malloc_tree_chunk* tbinptr; /* The type of bins of trees */ + +/* A little helper macro for trees */ +#define leftmost_child(t) ((t)->child[0] != 0? (t)->child[0] : (t)->child[1]) + +/* ----------------------------- Segments -------------------------------- */ + +/* + Each malloc space may include non-contiguous segments, held in a + list headed by an embedded malloc_segment record representing the + top-most space. Segments also include flags holding properties of + the space. Large chunks that are directly allocated by mmap are not + included in this list. They are instead independently created and + destroyed without otherwise keeping track of them. + + Segment management mainly comes into play for spaces allocated by + MMAP. Any call to MMAP might or might not return memory that is + adjacent to an existing segment. MORECORE normally contiguously + extends the current space, so this space is almost always adjacent, + which is simpler and faster to deal with. (This is why MORECORE is + used preferentially to MMAP when both are available -- see + sys_alloc.) When allocating using MMAP, we don't use any of the + hinting mechanisms (inconsistently) supported in various + implementations of unix mmap, or distinguish reserving from + committing memory. Instead, we just ask for space, and exploit + contiguity when we get it. It is probably possible to do + better than this on some systems, but no general scheme seems + to be significantly better. + + Management entails a simpler variant of the consolidation scheme + used for chunks to reduce fragmentation -- new adjacent memory is + normally prepended or appended to an existing segment. However, + there are limitations compared to chunk consolidation that mostly + reflect the fact that segment processing is relatively infrequent + (occurring only when getting memory from system) and that we + don't expect to have huge numbers of segments: + + * Segments are not indexed, so traversal requires linear scans. (It + would be possible to index these, but is not worth the extra + overhead and complexity for most programs on most platforms.) + * New segments are only appended to old ones when holding top-most + memory; if they cannot be prepended to others, they are held in + different segments. + + Except for the top-most segment of an mstate, each segment record + is kept at the tail of its segment. Segments are added by pushing + segment records onto the list headed by &mstate.seg for the + containing mstate. + + Segment flags control allocation/merge/deallocation policies: + * If EXTERN_BIT set, then we did not allocate this segment, + and so should not try to deallocate or merge with others. + (This currently holds only for the initial segment passed + into create_mspace_with_base.) + * If USE_MMAP_BIT set, the segment may be merged with + other surrounding mmapped segments and trimmed/de-allocated + using munmap. + * If neither bit is set, then the segment was obtained using + MORECORE so can be merged with surrounding MORECORE'd segments + and deallocated/trimmed using MORECORE with negative arguments. +*/ + +struct malloc_segment { + char* base; /* base address */ + size_t size; /* allocated size */ + struct malloc_segment* next; /* ptr to next segment */ + flag_t sflags; /* mmap and extern flag */ +}; + +#define is_mmapped_segment(S) ((S)->sflags & USE_MMAP_BIT) +#define is_extern_segment(S) ((S)->sflags & EXTERN_BIT) + +typedef struct malloc_segment msegment; +typedef struct malloc_segment* msegmentptr; + +/* ---------------------------- malloc_state ----------------------------- */ + +/* + A malloc_state holds all of the bookkeeping for a space. + The main fields are: + + Top + The topmost chunk of the currently active segment. Its size is + cached in topsize. The actual size of topmost space is + topsize+TOP_FOOT_SIZE, which includes space reserved for adding + fenceposts and segment records if necessary when getting more + space from the system. The size at which to autotrim top is + cached from mparams in trim_check, except that it is disabled if + an autotrim fails. + + Designated victim (dv) + This is the preferred chunk for servicing small requests that + don't have exact fits. It is normally the chunk split off most + recently to service another small request. Its size is cached in + dvsize. The link fields of this chunk are not maintained since it + is not kept in a bin. + + SmallBins + An array of bin headers for free chunks. These bins hold chunks + with sizes less than MIN_LARGE_SIZE bytes. Each bin contains + chunks of all the same size, spaced 8 bytes apart. To simplify + use in double-linked lists, each bin header acts as a malloc_chunk + pointing to the real first node, if it exists (else pointing to + itself). This avoids special-casing for headers. But to avoid + waste, we allocate only the fd/bk pointers of bins, and then use + repositioning tricks to treat these as the fields of a chunk. + + TreeBins + Treebins are pointers to the roots of trees holding a range of + sizes. There are 2 equally spaced treebins for each power of two + from TREE_SHIFT to TREE_SHIFT+16. The last bin holds anything + larger. + + Bin maps + There is one bit map for small bins ("smallmap") and one for + treebins ("treemap). Each bin sets its bit when non-empty, and + clears the bit when empty. Bit operations are then used to avoid + bin-by-bin searching -- nearly all "search" is done without ever + looking at bins that won't be selected. The bit maps + conservatively use 32 bits per map word, even if on 64bit system. + For a good description of some of the bit-based techniques used + here, see Henry S. Warren Jr's book "Hacker's Delight" (and + supplement at http://hackersdelight.org/). Many of these are + intended to reduce the branchiness of paths through malloc etc, as + well as to reduce the number of memory locations read or written. + + Segments + A list of segments headed by an embedded malloc_segment record + representing the initial space. + + Address check support + The least_addr field is the least address ever obtained from + MORECORE or MMAP. Attempted frees and reallocs of any address less + than this are trapped (unless INSECURE is defined). + + Magic tag + A cross-check field that should always hold same value as mparams.magic. + + Max allowed footprint + The maximum allowed bytes to allocate from system (zero means no limit) + + Flags + Bits recording whether to use MMAP, locks, or contiguous MORECORE + + Statistics + Each space keeps track of current and maximum system memory + obtained via MORECORE or MMAP. + + Trim support + Fields holding the amount of unused topmost memory that should trigger + trimming, and a counter to force periodic scanning to release unused + non-topmost segments. + + Locking + If USE_LOCKS is defined, the "mutex" lock is acquired and released + around every public call using this mspace. + + Extension support + A void* pointer and a size_t field that can be used to help implement + extensions to this malloc. +*/ + +/* Bin types, widths and sizes */ +#define NSMALLBINS (32U) +#define NTREEBINS (32U) +#define SMALLBIN_SHIFT (3U) +#define SMALLBIN_WIDTH (SIZE_T_ONE << SMALLBIN_SHIFT) +#define TREEBIN_SHIFT (8U) +#define MIN_LARGE_SIZE (SIZE_T_ONE << TREEBIN_SHIFT) +#define MAX_SMALL_SIZE (MIN_LARGE_SIZE - SIZE_T_ONE) +#define MAX_SMALL_REQUEST (MAX_SMALL_SIZE - CHUNK_ALIGN_MASK - CHUNK_OVERHEAD) + +struct malloc_state { + binmap_t smallmap; + binmap_t treemap; + size_t dvsize; + size_t topsize; + char* least_addr; + mchunkptr dv; + mchunkptr top; + size_t trim_check; + size_t release_checks; + size_t magic; + mchunkptr smallbins[(NSMALLBINS+1)*2]; + tbinptr treebins[NTREEBINS]; + size_t footprint; + size_t max_footprint; + size_t footprint_limit; /* zero means no limit */ + flag_t mflags; +#if USE_LOCKS + MLOCK_T mutex; /* locate lock among fields that rarely change */ +#endif /* USE_LOCKS */ + msegment seg; + void* extp; /* Unused but available for extensions */ + size_t exts; +}; + +typedef struct malloc_state* mstate; + +/* ------------- Global malloc_state and malloc_params ------------------- */ + +/* + malloc_params holds global properties, including those that can be + dynamically set using mallopt. There is a single instance, mparams, + initialized in init_mparams. Note that the non-zeroness of "magic" + also serves as an initialization flag. +*/ + +struct malloc_params { + size_t magic; + size_t page_size; + size_t granularity; + size_t mmap_threshold; + size_t trim_threshold; + flag_t default_mflags; +}; + +static struct malloc_params mparams; + +/* Ensure mparams initialized */ +#define ensure_initialization() (void)(mparams.magic != 0 || init_mparams()) + +#if !ONLY_MSPACES + +/* The global malloc_state used for all non-"mspace" calls */ +static struct malloc_state _gm_; +#define gm (&_gm_) +#define is_global(M) ((M) == &_gm_) + +#endif /* !ONLY_MSPACES */ + +#define is_initialized(M) ((M)->top != 0) + +/* -------------------------- system alloc setup ------------------------- */ + +/* Operations on mflags */ + +#define use_lock(M) ((M)->mflags & USE_LOCK_BIT) +#define enable_lock(M) ((M)->mflags |= USE_LOCK_BIT) +#if USE_LOCKS +#define disable_lock(M) ((M)->mflags &= ~USE_LOCK_BIT) +#else +#define disable_lock(M) +#endif + +#define use_mmap(M) ((M)->mflags & USE_MMAP_BIT) +#define enable_mmap(M) ((M)->mflags |= USE_MMAP_BIT) +#if HAVE_MMAP +#define disable_mmap(M) ((M)->mflags &= ~USE_MMAP_BIT) +#else +#define disable_mmap(M) +#endif + +#define use_noncontiguous(M) ((M)->mflags & USE_NONCONTIGUOUS_BIT) +#define disable_contiguous(M) ((M)->mflags |= USE_NONCONTIGUOUS_BIT) + +#define set_lock(M,L)\ + ((M)->mflags = (L)?\ + ((M)->mflags | USE_LOCK_BIT) :\ + ((M)->mflags & ~USE_LOCK_BIT)) + +/* page-align a size */ +#define page_align(S)\ + (((S) + (mparams.page_size - SIZE_T_ONE)) & ~(mparams.page_size - SIZE_T_ONE)) + +/* granularity-align a size */ +#define granularity_align(S)\ + (((S) + (mparams.granularity - SIZE_T_ONE))\ + & ~(mparams.granularity - SIZE_T_ONE)) + + +/* For mmap, use granularity alignment on windows, else page-align */ +#ifdef WIN32 +#define mmap_align(S) granularity_align(S) +#else +#define mmap_align(S) page_align(S) +#endif + +/* For sys_alloc, enough padding to ensure can malloc request on success */ +#define SYS_ALLOC_PADDING (TOP_FOOT_SIZE + MALLOC_ALIGNMENT) + +#define is_page_aligned(S)\ + (((size_t)(S) & (mparams.page_size - SIZE_T_ONE)) == 0) +#define is_granularity_aligned(S)\ + (((size_t)(S) & (mparams.granularity - SIZE_T_ONE)) == 0) + +/* True if segment S holds address A */ +#define segment_holds(S, A)\ + ((char*)(A) >= S->base && (char*)(A) < S->base + S->size) + +/* Return segment holding given address */ +static msegmentptr segment_holding(mstate m, char* addr) { + msegmentptr sp = &m->seg; + for (;;) { + if (addr >= sp->base && addr < sp->base + sp->size) + return sp; + if ((sp = sp->next) == 0) + return 0; + } +} + +/* Return true if segment contains a segment link */ +static int has_segment_link(mstate m, msegmentptr ss) { + msegmentptr sp = &m->seg; + for (;;) { + if ((char*)sp >= ss->base && (char*)sp < ss->base + ss->size) + return 1; + if ((sp = sp->next) == 0) + return 0; + } +} + +#ifndef MORECORE_CANNOT_TRIM +#define should_trim(M,s) ((s) > (M)->trim_check) +#else /* MORECORE_CANNOT_TRIM */ +#define should_trim(M,s) (0) +#endif /* MORECORE_CANNOT_TRIM */ + +/* + TOP_FOOT_SIZE is padding at the end of a segment, including space + that may be needed to place segment records and fenceposts when new + noncontiguous segments are added. +*/ +#define TOP_FOOT_SIZE\ + (align_offset(chunk2mem(0))+pad_request(sizeof(struct malloc_segment))+MIN_CHUNK_SIZE) + + +/* ------------------------------- Hooks -------------------------------- */ + +/* + PREACTION should be defined to return 0 on success, and nonzero on + failure. If you are not using locking, you can redefine these to do + anything you like. +*/ + +#if USE_LOCKS +#define PREACTION(M) ((use_lock(M))? ACQUIRE_LOCK(&(M)->mutex) : 0) +#define POSTACTION(M) { if (use_lock(M)) RELEASE_LOCK(&(M)->mutex); } +#else /* USE_LOCKS */ + +#ifndef PREACTION +#define PREACTION(M) (0) +#endif /* PREACTION */ + +#ifndef POSTACTION +#define POSTACTION(M) +#endif /* POSTACTION */ + +#endif /* USE_LOCKS */ + +/* + CORRUPTION_ERROR_ACTION is triggered upon detected bad addresses. + USAGE_ERROR_ACTION is triggered on detected bad frees and + reallocs. The argument p is an address that might have triggered the + fault. It is ignored by the two predefined actions, but might be + useful in custom actions that try to help diagnose errors. +*/ + +#if PROCEED_ON_ERROR + +/* A count of the number of corruption errors causing resets */ +int malloc_corruption_error_count; + +/* default corruption action */ +static void reset_on_error(mstate m); + +#define CORRUPTION_ERROR_ACTION(m) reset_on_error(m) +#define USAGE_ERROR_ACTION(m, p) + +#else /* PROCEED_ON_ERROR */ + +#ifndef CORRUPTION_ERROR_ACTION +#define CORRUPTION_ERROR_ACTION(m) ABORT +#endif /* CORRUPTION_ERROR_ACTION */ + +#ifndef USAGE_ERROR_ACTION +#define USAGE_ERROR_ACTION(m,p) ABORT +#endif /* USAGE_ERROR_ACTION */ + +#endif /* PROCEED_ON_ERROR */ + + +/* -------------------------- Debugging setup ---------------------------- */ + +#if ! DEBUG + +#define check_free_chunk(M,P) +#define check_inuse_chunk(M,P) +#define check_malloced_chunk(M,P,N) +#define check_mmapped_chunk(M,P) +#define check_malloc_state(M) +#define check_top_chunk(M,P) + +#else /* DEBUG */ +#define check_free_chunk(M,P) do_check_free_chunk(M,P) +#define check_inuse_chunk(M,P) do_check_inuse_chunk(M,P) +#define check_top_chunk(M,P) do_check_top_chunk(M,P) +#define check_malloced_chunk(M,P,N) do_check_malloced_chunk(M,P,N) +#define check_mmapped_chunk(M,P) do_check_mmapped_chunk(M,P) +#define check_malloc_state(M) do_check_malloc_state(M) + +static void do_check_any_chunk(mstate m, mchunkptr p); +static void do_check_top_chunk(mstate m, mchunkptr p); +static void do_check_mmapped_chunk(mstate m, mchunkptr p); +static void do_check_inuse_chunk(mstate m, mchunkptr p); +static void do_check_free_chunk(mstate m, mchunkptr p); +static void do_check_malloced_chunk(mstate m, void* mem, size_t s); +static void do_check_tree(mstate m, tchunkptr t); +static void do_check_treebin(mstate m, bindex_t i); +static void do_check_smallbin(mstate m, bindex_t i); +static void do_check_malloc_state(mstate m); +static int bin_find(mstate m, mchunkptr x); +static size_t traverse_and_check(mstate m); +#endif /* DEBUG */ + +/* ---------------------------- Indexing Bins ---------------------------- */ + +#define is_small(s) (((s) >> SMALLBIN_SHIFT) < NSMALLBINS) +#define small_index(s) (bindex_t)((s) >> SMALLBIN_SHIFT) +#define small_index2size(i) ((i) << SMALLBIN_SHIFT) +#define MIN_SMALL_INDEX (small_index(MIN_CHUNK_SIZE)) + +/* addressing by index. See above about smallbin repositioning */ +#define smallbin_at(M, i) ((sbinptr)((char*)&((M)->smallbins[(i)<<1]))) +#define treebin_at(M,i) (&((M)->treebins[i])) + +/* assign tree index for size S to variable I. Use x86 asm if possible */ +#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) +#define compute_tree_index(S, I)\ +{\ + unsigned int X = S >> TREEBIN_SHIFT;\ + if (X == 0)\ + I = 0;\ + else if (X > 0xFFFF)\ + I = NTREEBINS-1;\ + else {\ + unsigned int K = (unsigned) sizeof(X)*__CHAR_BIT__ - 1 - (unsigned) __builtin_clz(X); \ + I = (bindex_t)((K << 1) + ((S >> (K + (TREEBIN_SHIFT-1)) & 1)));\ + }\ +} + +#elif defined (__INTEL_COMPILER) +#define compute_tree_index(S, I)\ +{\ + size_t X = S >> TREEBIN_SHIFT;\ + if (X == 0)\ + I = 0;\ + else if (X > 0xFFFF)\ + I = NTREEBINS-1;\ + else {\ + unsigned int K = _bit_scan_reverse (X); \ + I = (bindex_t)((K << 1) + ((S >> (K + (TREEBIN_SHIFT-1)) & 1)));\ + }\ +} + +#elif defined(_MSC_VER) && _MSC_VER>=1300 +#define compute_tree_index(S, I)\ +{\ + size_t X = S >> TREEBIN_SHIFT;\ + if (X == 0)\ + I = 0;\ + else if (X > 0xFFFF)\ + I = NTREEBINS-1;\ + else {\ + unsigned int K;\ + _BitScanReverse((DWORD *) &K, (DWORD) X);\ + I = (bindex_t)((K << 1) + ((S >> (K + (TREEBIN_SHIFT-1)) & 1)));\ + }\ +} + +#else /* GNUC */ +#define compute_tree_index(S, I)\ +{\ + size_t X = S >> TREEBIN_SHIFT;\ + if (X == 0)\ + I = 0;\ + else if (X > 0xFFFF)\ + I = NTREEBINS-1;\ + else {\ + unsigned int Y = (unsigned int)X;\ + unsigned int N = ((Y - 0x100) >> 16) & 8;\ + unsigned int K = (((Y <<= N) - 0x1000) >> 16) & 4;\ + N += K;\ + N += K = (((Y <<= K) - 0x4000) >> 16) & 2;\ + K = 14 - N + ((Y <<= K) >> 15);\ + I = (K << 1) + ((S >> (K + (TREEBIN_SHIFT-1)) & 1));\ + }\ +} +#endif /* GNUC */ + +/* Bit representing maximum resolved size in a treebin at i */ +#define bit_for_tree_index(i) \ + (i == NTREEBINS-1)? (SIZE_T_BITSIZE-1) : (((i) >> 1) + TREEBIN_SHIFT - 2) + +/* Shift placing maximum resolved bit in a treebin at i as sign bit */ +#define leftshift_for_tree_index(i) \ + ((i == NTREEBINS-1)? 0 : \ + ((SIZE_T_BITSIZE-SIZE_T_ONE) - (((i) >> 1) + TREEBIN_SHIFT - 2))) + +/* The size of the smallest chunk held in bin with index i */ +#define minsize_for_tree_index(i) \ + ((SIZE_T_ONE << (((i) >> 1) + TREEBIN_SHIFT)) | \ + (((size_t)((i) & SIZE_T_ONE)) << (((i) >> 1) + TREEBIN_SHIFT - 1))) + + +/* ------------------------ Operations on bin maps ----------------------- */ + +/* bit corresponding to given index */ +#define idx2bit(i) ((binmap_t)(1) << (i)) + +/* Mark/Clear bits with given index */ +#define mark_smallmap(M,i) ((M)->smallmap |= idx2bit(i)) +#define clear_smallmap(M,i) ((M)->smallmap &= ~idx2bit(i)) +#define smallmap_is_marked(M,i) ((M)->smallmap & idx2bit(i)) + +#define mark_treemap(M,i) ((M)->treemap |= idx2bit(i)) +#define clear_treemap(M,i) ((M)->treemap &= ~idx2bit(i)) +#define treemap_is_marked(M,i) ((M)->treemap & idx2bit(i)) + +/* isolate the least set bit of a bitmap */ +#define least_bit(x) ((x) & -(x)) + +/* mask with all bits to left of least bit of x on */ +#define left_bits(x) ((x<<1) | -(x<<1)) + +/* mask with all bits to left of or equal to least bit of x on */ +#define same_or_left_bits(x) ((x) | -(x)) + +/* index corresponding to given bit. Use x86 asm if possible */ + +#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) +#define compute_bit2idx(X, I)\ +{\ + unsigned int J;\ + J = __builtin_ctz(X); \ + I = (bindex_t)J;\ +} + +#elif defined (__INTEL_COMPILER) +#define compute_bit2idx(X, I)\ +{\ + unsigned int J;\ + J = _bit_scan_forward (X); \ + I = (bindex_t)J;\ +} + +#elif defined(_MSC_VER) && _MSC_VER>=1300 +#define compute_bit2idx(X, I)\ +{\ + unsigned int J;\ + _BitScanForward((DWORD *) &J, X);\ + I = (bindex_t)J;\ +} + +#elif USE_BUILTIN_FFS +#define compute_bit2idx(X, I) I = ffs(X)-1 + +#else +#define compute_bit2idx(X, I)\ +{\ + unsigned int Y = X - 1;\ + unsigned int K = Y >> (16-4) & 16;\ + unsigned int N = K; Y >>= K;\ + N += K = Y >> (8-3) & 8; Y >>= K;\ + N += K = Y >> (4-2) & 4; Y >>= K;\ + N += K = Y >> (2-1) & 2; Y >>= K;\ + N += K = Y >> (1-0) & 1; Y >>= K;\ + I = (bindex_t)(N + Y);\ +} +#endif /* GNUC */ + + +/* ----------------------- Runtime Check Support ------------------------- */ + +/* + For security, the main invariant is that malloc/free/etc never + writes to a static address other than malloc_state, unless static + malloc_state itself has been corrupted, which cannot occur via + malloc (because of these checks). In essence this means that we + believe all pointers, sizes, maps etc held in malloc_state, but + check all of those linked or offsetted from other embedded data + structures. These checks are interspersed with main code in a way + that tends to minimize their run-time cost. + + When FOOTERS is defined, in addition to range checking, we also + verify footer fields of inuse chunks, which can be used guarantee + that the mstate controlling malloc/free is intact. This is a + streamlined version of the approach described by William Robertson + et al in "Run-time Detection of Heap-based Overflows" LISA'03 + http://www.usenix.org/events/lisa03/tech/robertson.html The footer + of an inuse chunk holds the xor of its mstate and a random seed, + that is checked upon calls to free() and realloc(). This is + (probabalistically) unguessable from outside the program, but can be + computed by any code successfully malloc'ing any chunk, so does not + itself provide protection against code that has already broken + security through some other means. Unlike Robertson et al, we + always dynamically check addresses of all offset chunks (previous, + next, etc). This turns out to be cheaper than relying on hashes. +*/ + +#if !INSECURE +/* Check if address a is at least as high as any from MORECORE or MMAP */ +#define ok_address(M, a) ((char*)(a) >= (M)->least_addr) +/* Check if address of next chunk n is higher than base chunk p */ +#define ok_next(p, n) ((char*)(p) < (char*)(n)) +/* Check if p has inuse status */ +#define ok_inuse(p) is_inuse(p) +/* Check if p has its pinuse bit on */ +#define ok_pinuse(p) pinuse(p) + +#else /* !INSECURE */ +#define ok_address(M, a) (1) +#define ok_next(b, n) (1) +#define ok_inuse(p) (1) +#define ok_pinuse(p) (1) +#endif /* !INSECURE */ + +#if (FOOTERS && !INSECURE) +/* Check if (alleged) mstate m has expected magic field */ +#define ok_magic(M) ((M)->magic == mparams.magic) +#else /* (FOOTERS && !INSECURE) */ +#define ok_magic(M) (1) +#endif /* (FOOTERS && !INSECURE) */ + +/* In gcc, use __builtin_expect to minimize impact of checks */ +#if !INSECURE +#if defined(__GNUC__) && __GNUC__ >= 3 +#define RTCHECK(e) __builtin_expect(e, 1) +#else /* GNUC */ +#define RTCHECK(e) (e) +#endif /* GNUC */ +#else /* !INSECURE */ +#define RTCHECK(e) (1) +#endif /* !INSECURE */ + +/* macros to set up inuse chunks with or without footers */ + +#if !FOOTERS + +#define mark_inuse_foot(M,p,s) + +/* Macros for setting head/foot of non-mmapped chunks */ + +/* Set cinuse bit and pinuse bit of next chunk */ +#define set_inuse(M,p,s)\ + ((p)->head = (((p)->head & PINUSE_BIT)|s|CINUSE_BIT),\ + ((mchunkptr)(((char*)(p)) + (s)))->head |= PINUSE_BIT) + +/* Set cinuse and pinuse of this chunk and pinuse of next chunk */ +#define set_inuse_and_pinuse(M,p,s)\ + ((p)->head = (s|PINUSE_BIT|CINUSE_BIT),\ + ((mchunkptr)(((char*)(p)) + (s)))->head |= PINUSE_BIT) + +/* Set size, cinuse and pinuse bit of this chunk */ +#define set_size_and_pinuse_of_inuse_chunk(M, p, s)\ + ((p)->head = (s|PINUSE_BIT|CINUSE_BIT)) + +#else /* FOOTERS */ + +/* Set foot of inuse chunk to be xor of mstate and seed */ +#define mark_inuse_foot(M,p,s)\ + (((mchunkptr)((char*)(p) + (s)))->prev_foot = ((size_t)(M) ^ mparams.magic)) + +#define get_mstate_for(p)\ + ((mstate)(((mchunkptr)((char*)(p) +\ + (chunksize(p))))->prev_foot ^ mparams.magic)) + +#define set_inuse(M,p,s)\ + ((p)->head = (((p)->head & PINUSE_BIT)|s|CINUSE_BIT),\ + (((mchunkptr)(((char*)(p)) + (s)))->head |= PINUSE_BIT), \ + mark_inuse_foot(M,p,s)) + +#define set_inuse_and_pinuse(M,p,s)\ + ((p)->head = (s|PINUSE_BIT|CINUSE_BIT),\ + (((mchunkptr)(((char*)(p)) + (s)))->head |= PINUSE_BIT),\ + mark_inuse_foot(M,p,s)) + +#define set_size_and_pinuse_of_inuse_chunk(M, p, s)\ + ((p)->head = (s|PINUSE_BIT|CINUSE_BIT),\ + mark_inuse_foot(M, p, s)) + +#endif /* !FOOTERS */ + +/* ---------------------------- setting mparams -------------------------- */ + +#if LOCK_AT_FORK +static void pre_fork(void) { ACQUIRE_LOCK(&(gm)->mutex); } +static void post_fork_parent(void) { RELEASE_LOCK(&(gm)->mutex); } +static void post_fork_child(void) { INITIAL_LOCK(&(gm)->mutex); } +#endif /* LOCK_AT_FORK */ + +/* Initialize mparams */ +static int init_mparams(void) { +#ifdef NEED_GLOBAL_LOCK_INIT + if (malloc_global_mutex_status <= 0) + init_malloc_global_mutex(); +#endif + + ACQUIRE_MALLOC_GLOBAL_LOCK(); + if (mparams.magic == 0) { + size_t magic; + size_t psize; + size_t gsize; + +#ifndef WIN32 + psize = malloc_getpagesize; + gsize = ((DEFAULT_GRANULARITY != 0)? DEFAULT_GRANULARITY : psize); +#else /* WIN32 */ + { + SYSTEM_INFO system_info; + GetSystemInfo(&system_info); + psize = system_info.dwPageSize; + gsize = ((DEFAULT_GRANULARITY != 0)? + DEFAULT_GRANULARITY : system_info.dwAllocationGranularity); + } +#endif /* WIN32 */ + + /* Sanity-check configuration: + size_t must be unsigned and as wide as pointer type. + ints must be at least 4 bytes. + alignment must be at least 8. + Alignment, min chunk size, and page size must all be powers of 2. + */ + if ((sizeof(size_t) != sizeof(char*)) || + (MAX_SIZE_T < MIN_CHUNK_SIZE) || + (sizeof(int) < 4) || + (MALLOC_ALIGNMENT < (size_t)8U) || + ((MALLOC_ALIGNMENT & (MALLOC_ALIGNMENT-SIZE_T_ONE)) != 0) || + ((MCHUNK_SIZE & (MCHUNK_SIZE-SIZE_T_ONE)) != 0) || + ((gsize & (gsize-SIZE_T_ONE)) != 0) || + ((psize & (psize-SIZE_T_ONE)) != 0)) + ABORT; + mparams.granularity = gsize; + mparams.page_size = psize; + mparams.mmap_threshold = DEFAULT_MMAP_THRESHOLD; + mparams.trim_threshold = DEFAULT_TRIM_THRESHOLD; +#if MORECORE_CONTIGUOUS + mparams.default_mflags = USE_LOCK_BIT|USE_MMAP_BIT; +#else /* MORECORE_CONTIGUOUS */ + mparams.default_mflags = USE_LOCK_BIT|USE_MMAP_BIT|USE_NONCONTIGUOUS_BIT; +#endif /* MORECORE_CONTIGUOUS */ + +#if !ONLY_MSPACES + /* Set up lock for main malloc area */ + gm->mflags = mparams.default_mflags; + (void)INITIAL_LOCK(&gm->mutex); +#endif +#if LOCK_AT_FORK + pthread_atfork(&pre_fork, &post_fork_parent, &post_fork_child); +#endif + + { +#if USE_DEV_RANDOM + int fd; + unsigned char buf[sizeof(size_t)]; + /* Try to use /dev/urandom, else fall back on using time */ + if ((fd = open("/dev/urandom", O_RDONLY)) >= 0 && + read(fd, buf, sizeof(buf)) == sizeof(buf)) { + magic = *((size_t *) buf); + close(fd); + } + else +#endif /* USE_DEV_RANDOM */ +#ifdef WIN32 + magic = (size_t)(GetTickCount() ^ (size_t)0x55555555U); +#elif defined(LACKS_TIME_H) + magic = (size_t)&magic ^ (size_t)0x55555555U; +#else + magic = (size_t)(time(0) ^ (size_t)0x55555555U); +#endif + magic |= (size_t)8U; /* ensure nonzero */ + magic &= ~(size_t)7U; /* improve chances of fault for bad values */ + /* Until memory modes commonly available, use volatile-write */ + (*(volatile size_t *)(&(mparams.magic))) = magic; + } + } + + RELEASE_MALLOC_GLOBAL_LOCK(); + return 1; +} + +/* support for mallopt */ +static int change_mparam(int param_number, int value) { + size_t val; + ensure_initialization(); + val = (value == -1)? MAX_SIZE_T : (size_t)value; + switch(param_number) { + case M_TRIM_THRESHOLD: + mparams.trim_threshold = val; + return 1; + case M_GRANULARITY: + if (val >= mparams.page_size && ((val & (val-1)) == 0)) { + mparams.granularity = val; + return 1; + } + else + return 0; + case M_MMAP_THRESHOLD: + mparams.mmap_threshold = val; + return 1; + default: + return 0; + } +} + +#if DEBUG +/* ------------------------- Debugging Support --------------------------- */ + +/* Check properties of any chunk, whether free, inuse, mmapped etc */ +static void do_check_any_chunk(mstate m, mchunkptr p) { + assert((is_aligned(chunk2mem(p))) || (p->head == FENCEPOST_HEAD)); + assert(ok_address(m, p)); +} + +/* Check properties of top chunk */ +static void do_check_top_chunk(mstate m, mchunkptr p) { + msegmentptr sp = segment_holding(m, (char*)p); + size_t sz = p->head & ~INUSE_BITS; /* third-lowest bit can be set! */ + assert(sp != 0); + assert((is_aligned(chunk2mem(p))) || (p->head == FENCEPOST_HEAD)); + assert(ok_address(m, p)); + assert(sz == m->topsize); + assert(sz > 0); + assert(sz == ((sp->base + sp->size) - (char*)p) - TOP_FOOT_SIZE); + assert(pinuse(p)); + assert(!pinuse(chunk_plus_offset(p, sz))); +} + +/* Check properties of (inuse) mmapped chunks */ +static void do_check_mmapped_chunk(mstate m, mchunkptr p) { + size_t sz = chunksize(p); + size_t len = (sz + (p->prev_foot) + MMAP_FOOT_PAD); + assert(is_mmapped(p)); + assert(use_mmap(m)); + assert((is_aligned(chunk2mem(p))) || (p->head == FENCEPOST_HEAD)); + assert(ok_address(m, p)); + assert(!is_small(sz)); + assert((len & (mparams.page_size-SIZE_T_ONE)) == 0); + assert(chunk_plus_offset(p, sz)->head == FENCEPOST_HEAD); + assert(chunk_plus_offset(p, sz+SIZE_T_SIZE)->head == 0); +} + +/* Check properties of inuse chunks */ +static void do_check_inuse_chunk(mstate m, mchunkptr p) { + do_check_any_chunk(m, p); + assert(is_inuse(p)); + assert(next_pinuse(p)); + /* If not pinuse and not mmapped, previous chunk has OK offset */ + assert(is_mmapped(p) || pinuse(p) || next_chunk(prev_chunk(p)) == p); + if (is_mmapped(p)) + do_check_mmapped_chunk(m, p); +} + +/* Check properties of free chunks */ +static void do_check_free_chunk(mstate m, mchunkptr p) { + size_t sz = chunksize(p); + mchunkptr next = chunk_plus_offset(p, sz); + do_check_any_chunk(m, p); + assert(!is_inuse(p)); + assert(!next_pinuse(p)); + assert (!is_mmapped(p)); + if (p != m->dv && p != m->top) { + if (sz >= MIN_CHUNK_SIZE) { + assert((sz & CHUNK_ALIGN_MASK) == 0); + assert(is_aligned(chunk2mem(p))); + assert(next->prev_foot == sz); + assert(pinuse(p)); + assert (next == m->top || is_inuse(next)); + assert(p->fd->bk == p); + assert(p->bk->fd == p); + } + else /* markers are always of size SIZE_T_SIZE */ + assert(sz == SIZE_T_SIZE); + } +} + +/* Check properties of malloced chunks at the point they are malloced */ +static void do_check_malloced_chunk(mstate m, void* mem, size_t s) { + if (mem != 0) { + mchunkptr p = mem2chunk(mem); + size_t sz = p->head & ~INUSE_BITS; + do_check_inuse_chunk(m, p); + assert((sz & CHUNK_ALIGN_MASK) == 0); + assert(sz >= MIN_CHUNK_SIZE); + assert(sz >= s); + /* unless mmapped, size is less than MIN_CHUNK_SIZE more than request */ + assert(is_mmapped(p) || sz < (s + MIN_CHUNK_SIZE)); + } +} + +/* Check a tree and its subtrees. */ +static void do_check_tree(mstate m, tchunkptr t) { + tchunkptr head = 0; + tchunkptr u = t; + bindex_t tindex = t->index; + size_t tsize = chunksize(t); + bindex_t idx; + compute_tree_index(tsize, idx); + assert(tindex == idx); + assert(tsize >= MIN_LARGE_SIZE); + assert(tsize >= minsize_for_tree_index(idx)); + assert((idx == NTREEBINS-1) || (tsize < minsize_for_tree_index((idx+1)))); + + do { /* traverse through chain of same-sized nodes */ + do_check_any_chunk(m, ((mchunkptr)u)); + assert(u->index == tindex); + assert(chunksize(u) == tsize); + assert(!is_inuse(u)); + assert(!next_pinuse(u)); + assert(u->fd->bk == u); + assert(u->bk->fd == u); + if (u->parent == 0) { + assert(u->child[0] == 0); + assert(u->child[1] == 0); + } + else { + assert(head == 0); /* only one node on chain has parent */ + head = u; + assert(u->parent != u); + assert (u->parent->child[0] == u || + u->parent->child[1] == u || + *((tbinptr*)(u->parent)) == u); + if (u->child[0] != 0) { + assert(u->child[0]->parent == u); + assert(u->child[0] != u); + do_check_tree(m, u->child[0]); + } + if (u->child[1] != 0) { + assert(u->child[1]->parent == u); + assert(u->child[1] != u); + do_check_tree(m, u->child[1]); + } + if (u->child[0] != 0 && u->child[1] != 0) { + assert(chunksize(u->child[0]) < chunksize(u->child[1])); + } + } + u = u->fd; + } while (u != t); + assert(head != 0); +} + +/* Check all the chunks in a treebin. */ +static void do_check_treebin(mstate m, bindex_t i) { + tbinptr* tb = treebin_at(m, i); + tchunkptr t = *tb; + int empty = (m->treemap & (1U << i)) == 0; + if (t == 0) + assert(empty); + if (!empty) + do_check_tree(m, t); +} + +/* Check all the chunks in a smallbin. */ +static void do_check_smallbin(mstate m, bindex_t i) { + sbinptr b = smallbin_at(m, i); + mchunkptr p = b->bk; + unsigned int empty = (m->smallmap & (1U << i)) == 0; + if (p == b) + assert(empty); + if (!empty) { + for (; p != b; p = p->bk) { + size_t size = chunksize(p); + mchunkptr q; + /* each chunk claims to be free */ + do_check_free_chunk(m, p); + /* chunk belongs in bin */ + assert(small_index(size) == i); + assert(p->bk == b || chunksize(p->bk) == chunksize(p)); + /* chunk is followed by an inuse chunk */ + q = next_chunk(p); + if (q->head != FENCEPOST_HEAD) + do_check_inuse_chunk(m, q); + } + } +} + +/* Find x in a bin. Used in other check functions. */ +static int bin_find(mstate m, mchunkptr x) { + size_t size = chunksize(x); + if (is_small(size)) { + bindex_t sidx = small_index(size); + sbinptr b = smallbin_at(m, sidx); + if (smallmap_is_marked(m, sidx)) { + mchunkptr p = b; + do { + if (p == x) + return 1; + } while ((p = p->fd) != b); + } + } + else { + bindex_t tidx; + compute_tree_index(size, tidx); + if (treemap_is_marked(m, tidx)) { + tchunkptr t = *treebin_at(m, tidx); + size_t sizebits = size << leftshift_for_tree_index(tidx); + while (t != 0 && chunksize(t) != size) { + t = t->child[(sizebits >> (SIZE_T_BITSIZE-SIZE_T_ONE)) & 1]; + sizebits <<= 1; + } + if (t != 0) { + tchunkptr u = t; + do { + if (u == (tchunkptr)x) + return 1; + } while ((u = u->fd) != t); + } + } + } + return 0; +} + +/* Traverse each chunk and check it; return total */ +static size_t traverse_and_check(mstate m) { + size_t sum = 0; + if (is_initialized(m)) { + msegmentptr s = &m->seg; + sum += m->topsize + TOP_FOOT_SIZE; + while (s != 0) { + mchunkptr q = align_as_chunk(s->base); + mchunkptr lastq = 0; + assert(pinuse(q)); + while (segment_holds(s, q) && + q != m->top && q->head != FENCEPOST_HEAD) { + sum += chunksize(q); + if (is_inuse(q)) { + assert(!bin_find(m, q)); + do_check_inuse_chunk(m, q); + } + else { + assert(q == m->dv || bin_find(m, q)); + assert(lastq == 0 || is_inuse(lastq)); /* Not 2 consecutive free */ + do_check_free_chunk(m, q); + } + lastq = q; + q = next_chunk(q); + } + s = s->next; + } + } + return sum; +} + + +/* Check all properties of malloc_state. */ +static void do_check_malloc_state(mstate m) { + bindex_t i; + size_t total; + /* check bins */ + for (i = 0; i < NSMALLBINS; ++i) + do_check_smallbin(m, i); + for (i = 0; i < NTREEBINS; ++i) + do_check_treebin(m, i); + + if (m->dvsize != 0) { /* check dv chunk */ + do_check_any_chunk(m, m->dv); + assert(m->dvsize == chunksize(m->dv)); + assert(m->dvsize >= MIN_CHUNK_SIZE); + assert(bin_find(m, m->dv) == 0); + } + + if (m->top != 0) { /* check top chunk */ + do_check_top_chunk(m, m->top); + /*assert(m->topsize == chunksize(m->top)); redundant */ + assert(m->topsize > 0); + assert(bin_find(m, m->top) == 0); + } + + total = traverse_and_check(m); + assert(total <= m->footprint); + assert(m->footprint <= m->max_footprint); +} +#endif /* DEBUG */ + +/* ----------------------------- statistics ------------------------------ */ + +#if !NO_MALLINFO +static struct mallinfo internal_mallinfo(mstate m) { + struct mallinfo nm = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + ensure_initialization(); + if (!PREACTION(m)) { + check_malloc_state(m); + if (is_initialized(m)) { + size_t nfree = SIZE_T_ONE; /* top always free */ + size_t mfree = m->topsize + TOP_FOOT_SIZE; + size_t sum = mfree; + msegmentptr s = &m->seg; + while (s != 0) { + mchunkptr q = align_as_chunk(s->base); + while (segment_holds(s, q) && + q != m->top && q->head != FENCEPOST_HEAD) { + size_t sz = chunksize(q); + sum += sz; + if (!is_inuse(q)) { + mfree += sz; + ++nfree; + } + q = next_chunk(q); + } + s = s->next; + } + + nm.arena = sum; + nm.ordblks = nfree; + nm.hblkhd = m->footprint - sum; + nm.usmblks = m->max_footprint; + nm.uordblks = m->footprint - mfree; + nm.fordblks = mfree; + nm.keepcost = m->topsize; + } + + POSTACTION(m); + } + return nm; +} +#endif /* !NO_MALLINFO */ + +#if !NO_MALLOC_STATS +static void internal_malloc_stats(mstate m) { + ensure_initialization(); + if (!PREACTION(m)) { + size_t maxfp = 0; + size_t fp = 0; + size_t used = 0; + check_malloc_state(m); + if (is_initialized(m)) { + msegmentptr s = &m->seg; + maxfp = m->max_footprint; + fp = m->footprint; + used = fp - (m->topsize + TOP_FOOT_SIZE); + + while (s != 0) { + mchunkptr q = align_as_chunk(s->base); + while (segment_holds(s, q) && + q != m->top && q->head != FENCEPOST_HEAD) { + if (!is_inuse(q)) + used -= chunksize(q); + q = next_chunk(q); + } + s = s->next; + } + } + POSTACTION(m); /* drop lock */ + fprintf(stderr, "max system bytes = %10lu\n", (unsigned long)(maxfp)); + fprintf(stderr, "system bytes = %10lu\n", (unsigned long)(fp)); + fprintf(stderr, "in use bytes = %10lu\n", (unsigned long)(used)); + } +} +#endif /* NO_MALLOC_STATS */ + +/* ----------------------- Operations on smallbins ----------------------- */ + +/* + Various forms of linking and unlinking are defined as macros. Even + the ones for trees, which are very long but have very short typical + paths. This is ugly but reduces reliance on inlining support of + compilers. +*/ + +/* Link a free chunk into a smallbin */ +#define insert_small_chunk(M, P, S) {\ + bindex_t I = small_index(S);\ + mchunkptr B = smallbin_at(M, I);\ + mchunkptr F = B;\ + assert(S >= MIN_CHUNK_SIZE);\ + if (!smallmap_is_marked(M, I))\ + mark_smallmap(M, I);\ + else if (RTCHECK(ok_address(M, B->fd)))\ + F = B->fd;\ + else {\ + CORRUPTION_ERROR_ACTION(M);\ + }\ + B->fd = P;\ + F->bk = P;\ + P->fd = F;\ + P->bk = B;\ +} + +/* Unlink a chunk from a smallbin */ +#define unlink_small_chunk(M, P, S) {\ + mchunkptr F = P->fd;\ + mchunkptr B = P->bk;\ + bindex_t I = small_index(S);\ + assert(P != B);\ + assert(P != F);\ + assert(chunksize(P) == small_index2size(I));\ + if (RTCHECK(F == smallbin_at(M,I) || (ok_address(M, F) && F->bk == P))) { \ + if (B == F) {\ + clear_smallmap(M, I);\ + }\ + else if (RTCHECK(B == smallbin_at(M,I) ||\ + (ok_address(M, B) && B->fd == P))) {\ + F->bk = B;\ + B->fd = F;\ + }\ + else {\ + CORRUPTION_ERROR_ACTION(M);\ + }\ + }\ + else {\ + CORRUPTION_ERROR_ACTION(M);\ + }\ +} + +/* Unlink the first chunk from a smallbin */ +#define unlink_first_small_chunk(M, B, P, I) {\ + mchunkptr F = P->fd;\ + assert(P != B);\ + assert(P != F);\ + assert(chunksize(P) == small_index2size(I));\ + if (B == F) {\ + clear_smallmap(M, I);\ + }\ + else if (RTCHECK(ok_address(M, F) && F->bk == P)) {\ + F->bk = B;\ + B->fd = F;\ + }\ + else {\ + CORRUPTION_ERROR_ACTION(M);\ + }\ +} + +/* Replace dv node, binning the old one */ +/* Used only when dvsize known to be small */ +#define replace_dv(M, P, S) {\ + size_t DVS = M->dvsize;\ + assert(is_small(DVS));\ + if (DVS != 0) {\ + mchunkptr DV = M->dv;\ + insert_small_chunk(M, DV, DVS);\ + }\ + M->dvsize = S;\ + M->dv = P;\ +} + +/* ------------------------- Operations on trees ------------------------- */ + +/* Insert chunk into tree */ +#define insert_large_chunk(M, X, S) {\ + tbinptr* H;\ + bindex_t I;\ + compute_tree_index(S, I);\ + H = treebin_at(M, I);\ + X->index = I;\ + X->child[0] = X->child[1] = 0;\ + if (!treemap_is_marked(M, I)) {\ + mark_treemap(M, I);\ + *H = X;\ + X->parent = (tchunkptr)H;\ + X->fd = X->bk = X;\ + }\ + else {\ + tchunkptr T = *H;\ + size_t K = S << leftshift_for_tree_index(I);\ + for (;;) {\ + if (chunksize(T) != S) {\ + tchunkptr* C = &(T->child[(K >> (SIZE_T_BITSIZE-SIZE_T_ONE)) & 1]);\ + K <<= 1;\ + if (*C != 0)\ + T = *C;\ + else if (RTCHECK(ok_address(M, C))) {\ + *C = X;\ + X->parent = T;\ + X->fd = X->bk = X;\ + break;\ + }\ + else {\ + CORRUPTION_ERROR_ACTION(M);\ + break;\ + }\ + }\ + else {\ + tchunkptr F = T->fd;\ + if (RTCHECK(ok_address(M, T) && ok_address(M, F))) {\ + T->fd = F->bk = X;\ + X->fd = F;\ + X->bk = T;\ + X->parent = 0;\ + break;\ + }\ + else {\ + CORRUPTION_ERROR_ACTION(M);\ + break;\ + }\ + }\ + }\ + }\ +} + +/* + Unlink steps: + + 1. If x is a chained node, unlink it from its same-sized fd/bk links + and choose its bk node as its replacement. + 2. If x was the last node of its size, but not a leaf node, it must + be replaced with a leaf node (not merely one with an open left or + right), to make sure that lefts and rights of descendents + correspond properly to bit masks. We use the rightmost descendent + of x. We could use any other leaf, but this is easy to locate and + tends to counteract removal of leftmosts elsewhere, and so keeps + paths shorter than minimally guaranteed. This doesn't loop much + because on average a node in a tree is near the bottom. + 3. If x is the base of a chain (i.e., has parent links) relink + x's parent and children to x's replacement (or null if none). +*/ + +#define unlink_large_chunk(M, X) {\ + tchunkptr XP = X->parent;\ + tchunkptr R;\ + if (X->bk != X) {\ + tchunkptr F = X->fd;\ + R = X->bk;\ + if (RTCHECK(ok_address(M, F) && F->bk == X && R->fd == X)) {\ + F->bk = R;\ + R->fd = F;\ + }\ + else {\ + CORRUPTION_ERROR_ACTION(M);\ + }\ + }\ + else {\ + tchunkptr* RP;\ + if (((R = *(RP = &(X->child[1]))) != 0) ||\ + ((R = *(RP = &(X->child[0]))) != 0)) {\ + tchunkptr* CP;\ + while ((*(CP = &(R->child[1])) != 0) ||\ + (*(CP = &(R->child[0])) != 0)) {\ + R = *(RP = CP);\ + }\ + if (RTCHECK(ok_address(M, RP)))\ + *RP = 0;\ + else {\ + CORRUPTION_ERROR_ACTION(M);\ + }\ + }\ + }\ + if (XP != 0) {\ + tbinptr* H = treebin_at(M, X->index);\ + if (X == *H) {\ + if ((*H = R) == 0) \ + clear_treemap(M, X->index);\ + }\ + else if (RTCHECK(ok_address(M, XP))) {\ + if (XP->child[0] == X) \ + XP->child[0] = R;\ + else \ + XP->child[1] = R;\ + }\ + else\ + CORRUPTION_ERROR_ACTION(M);\ + if (R != 0) {\ + if (RTCHECK(ok_address(M, R))) {\ + tchunkptr C0, C1;\ + R->parent = XP;\ + if ((C0 = X->child[0]) != 0) {\ + if (RTCHECK(ok_address(M, C0))) {\ + R->child[0] = C0;\ + C0->parent = R;\ + }\ + else\ + CORRUPTION_ERROR_ACTION(M);\ + }\ + if ((C1 = X->child[1]) != 0) {\ + if (RTCHECK(ok_address(M, C1))) {\ + R->child[1] = C1;\ + C1->parent = R;\ + }\ + else\ + CORRUPTION_ERROR_ACTION(M);\ + }\ + }\ + else\ + CORRUPTION_ERROR_ACTION(M);\ + }\ + }\ +} + +/* Relays to large vs small bin operations */ + +#define insert_chunk(M, P, S)\ + if (is_small(S)) insert_small_chunk(M, P, S)\ + else { tchunkptr TP = (tchunkptr)(P); insert_large_chunk(M, TP, S); } + +#define unlink_chunk(M, P, S)\ + if (is_small(S)) unlink_small_chunk(M, P, S)\ + else { tchunkptr TP = (tchunkptr)(P); unlink_large_chunk(M, TP); } + + +/* Relays to internal calls to malloc/free from realloc, memalign etc */ + +#if ONLY_MSPACES +#define internal_malloc(m, b) mspace_malloc(m, b) +#define internal_free(m, mem) mspace_free(m,mem); +#else /* ONLY_MSPACES */ +#if MSPACES +#define internal_malloc(m, b)\ + ((m == gm)? dlmalloc(b) : mspace_malloc(m, b)) +#define internal_free(m, mem)\ + if (m == gm) dlfree(mem); else mspace_free(m,mem); +#else /* MSPACES */ +#define internal_malloc(m, b) dlmalloc(b) +#define internal_free(m, mem) dlfree(mem) +#endif /* MSPACES */ +#endif /* ONLY_MSPACES */ + +/* ----------------------- Direct-mmapping chunks ----------------------- */ + +/* + Directly mmapped chunks are set up with an offset to the start of + the mmapped region stored in the prev_foot field of the chunk. This + allows reconstruction of the required argument to MUNMAP when freed, + and also allows adjustment of the returned chunk to meet alignment + requirements (especially in memalign). +*/ + +/* Malloc using mmap */ +static void* mmap_alloc(mstate m, size_t nb) { + size_t mmsize = mmap_align(nb + SIX_SIZE_T_SIZES + CHUNK_ALIGN_MASK); + if (m->footprint_limit != 0) { + size_t fp = m->footprint + mmsize; + if (fp <= m->footprint || fp > m->footprint_limit) + return 0; + } + if (mmsize > nb) { /* Check for wrap around 0 */ + char* mm = (char*)(CALL_DIRECT_MMAP(mmsize)); + if (mm != CMFAIL) { + size_t offset = align_offset(chunk2mem(mm)); + size_t psize = mmsize - offset - MMAP_FOOT_PAD; + mchunkptr p = (mchunkptr)(mm + offset); + p->prev_foot = offset; + p->head = psize; + mark_inuse_foot(m, p, psize); + chunk_plus_offset(p, psize)->head = FENCEPOST_HEAD; + chunk_plus_offset(p, psize+SIZE_T_SIZE)->head = 0; + + if (m->least_addr == 0 || mm < m->least_addr) + m->least_addr = mm; + if ((m->footprint += mmsize) > m->max_footprint) + m->max_footprint = m->footprint; + assert(is_aligned(chunk2mem(p))); + check_mmapped_chunk(m, p); + return chunk2mem(p); + } + } + return 0; +} + +/* Realloc using mmap */ +static mchunkptr mmap_resize(mstate m, mchunkptr oldp, size_t nb, int flags) { + size_t oldsize = chunksize(oldp); + (void)flags; /* placate people compiling -Wunused */ + if (is_small(nb)) /* Can't shrink mmap regions below small size */ + return 0; + /* Keep old chunk if big enough but not too big */ + if (oldsize >= nb + SIZE_T_SIZE && + (oldsize - nb) <= (mparams.granularity << 1)) + return oldp; + else { + size_t offset = oldp->prev_foot; + size_t oldmmsize = oldsize + offset + MMAP_FOOT_PAD; + size_t newmmsize = mmap_align(nb + SIX_SIZE_T_SIZES + CHUNK_ALIGN_MASK); + char* cp = (char*)CALL_MREMAP((char*)oldp - offset, + oldmmsize, newmmsize, flags); + if (cp != CMFAIL) { + mchunkptr newp = (mchunkptr)(cp + offset); + size_t psize = newmmsize - offset - MMAP_FOOT_PAD; + newp->head = psize; + mark_inuse_foot(m, newp, psize); + chunk_plus_offset(newp, psize)->head = FENCEPOST_HEAD; + chunk_plus_offset(newp, psize+SIZE_T_SIZE)->head = 0; + + if (cp < m->least_addr) + m->least_addr = cp; + if ((m->footprint += newmmsize - oldmmsize) > m->max_footprint) + m->max_footprint = m->footprint; + check_mmapped_chunk(m, newp); + return newp; + } + } + return 0; +} + + +/* -------------------------- mspace management -------------------------- */ + +/* Initialize top chunk and its size */ +static void init_top(mstate m, mchunkptr p, size_t psize) { + /* Ensure alignment */ + size_t offset = align_offset(chunk2mem(p)); + p = (mchunkptr)((char*)p + offset); + psize -= offset; + + m->top = p; + m->topsize = psize; + p->head = psize | PINUSE_BIT; + /* set size of fake trailing chunk holding overhead space only once */ + chunk_plus_offset(p, psize)->head = TOP_FOOT_SIZE; + m->trim_check = mparams.trim_threshold; /* reset on each update */ +} + +/* Initialize bins for a new mstate that is otherwise zeroed out */ +static void init_bins(mstate m) { + /* Establish circular links for smallbins */ + bindex_t i; + for (i = 0; i < NSMALLBINS; ++i) { + sbinptr bin = smallbin_at(m,i); + bin->fd = bin->bk = bin; + } +} + +#if PROCEED_ON_ERROR + +/* default corruption action */ +static void reset_on_error(mstate m) { + int i; + ++malloc_corruption_error_count; + /* Reinitialize fields to forget about all memory */ + m->smallmap = m->treemap = 0; + m->dvsize = m->topsize = 0; + m->seg.base = 0; + m->seg.size = 0; + m->seg.next = 0; + m->top = m->dv = 0; + for (i = 0; i < NTREEBINS; ++i) + *treebin_at(m, i) = 0; + init_bins(m); +} +#endif /* PROCEED_ON_ERROR */ + +/* Allocate chunk and prepend remainder with chunk in successor base. */ +static void* prepend_alloc(mstate m, char* newbase, char* oldbase, + size_t nb) { + mchunkptr p = align_as_chunk(newbase); + mchunkptr oldfirst = align_as_chunk(oldbase); + size_t psize = (char*)oldfirst - (char*)p; + mchunkptr q = chunk_plus_offset(p, nb); + size_t qsize = psize - nb; + set_size_and_pinuse_of_inuse_chunk(m, p, nb); + + assert((char*)oldfirst > (char*)q); + assert(pinuse(oldfirst)); + assert(qsize >= MIN_CHUNK_SIZE); + + /* consolidate remainder with first chunk of old base */ + if (oldfirst == m->top) { + size_t tsize = m->topsize += qsize; + m->top = q; + q->head = tsize | PINUSE_BIT; + check_top_chunk(m, q); + } + else if (oldfirst == m->dv) { + size_t dsize = m->dvsize += qsize; + m->dv = q; + set_size_and_pinuse_of_free_chunk(q, dsize); + } + else { + if (!is_inuse(oldfirst)) { + size_t nsize = chunksize(oldfirst); + unlink_chunk(m, oldfirst, nsize); + oldfirst = chunk_plus_offset(oldfirst, nsize); + qsize += nsize; + } + set_free_with_pinuse(q, qsize, oldfirst); + insert_chunk(m, q, qsize); + check_free_chunk(m, q); + } + + check_malloced_chunk(m, chunk2mem(p), nb); + return chunk2mem(p); +} + +/* Add a segment to hold a new noncontiguous region */ +static void add_segment(mstate m, char* tbase, size_t tsize, flag_t mmapped) { + /* Determine locations and sizes of segment, fenceposts, old top */ + char* old_top = (char*)m->top; + msegmentptr oldsp = segment_holding(m, old_top); + char* old_end = oldsp->base + oldsp->size; + size_t ssize = pad_request(sizeof(struct malloc_segment)); + char* rawsp = old_end - (ssize + FOUR_SIZE_T_SIZES + CHUNK_ALIGN_MASK); + size_t offset = align_offset(chunk2mem(rawsp)); + char* asp = rawsp + offset; + char* csp = (asp < (old_top + MIN_CHUNK_SIZE))? old_top : asp; + mchunkptr sp = (mchunkptr)csp; + msegmentptr ss = (msegmentptr)(chunk2mem(sp)); + mchunkptr tnext = chunk_plus_offset(sp, ssize); + mchunkptr p = tnext; + int nfences = 0; + + /* reset top to new space */ + init_top(m, (mchunkptr)tbase, tsize - TOP_FOOT_SIZE); + + /* Set up segment record */ + assert(is_aligned(ss)); + set_size_and_pinuse_of_inuse_chunk(m, sp, ssize); + *ss = m->seg; /* Push current record */ + m->seg.base = tbase; + m->seg.size = tsize; + m->seg.sflags = mmapped; + m->seg.next = ss; + + /* Insert trailing fenceposts */ + for (;;) { + mchunkptr nextp = chunk_plus_offset(p, SIZE_T_SIZE); + p->head = FENCEPOST_HEAD; + ++nfences; + if ((char*)(&(nextp->head)) < old_end) + p = nextp; + else + break; + } + assert(nfences >= 2); + + /* Insert the rest of old top into a bin as an ordinary free chunk */ + if (csp != old_top) { + mchunkptr q = (mchunkptr)old_top; + size_t psize = csp - old_top; + mchunkptr tn = chunk_plus_offset(q, psize); + set_free_with_pinuse(q, psize, tn); + insert_chunk(m, q, psize); + } + + check_top_chunk(m, m->top); +} + +/* -------------------------- System allocation -------------------------- */ + +/* Get memory from system using MORECORE or MMAP */ +static void* sys_alloc(mstate m, size_t nb) { + char* tbase = CMFAIL; + size_t tsize = 0; + flag_t mmap_flag = 0; + size_t asize; /* allocation size */ + + ensure_initialization(); + + /* Directly map large chunks, but only if already initialized */ + if (use_mmap(m) && nb >= mparams.mmap_threshold && m->topsize != 0) { + void* mem = mmap_alloc(m, nb); + if (mem != 0) + return mem; + } + + asize = granularity_align(nb + SYS_ALLOC_PADDING); + if (asize <= nb) + return 0; /* wraparound */ + if (m->footprint_limit != 0) { + size_t fp = m->footprint + asize; + if (fp <= m->footprint || fp > m->footprint_limit) + return 0; + } + + /* + Try getting memory in any of three ways (in most-preferred to + least-preferred order): + 1. A call to MORECORE that can normally contiguously extend memory. + (disabled if not MORECORE_CONTIGUOUS or not HAVE_MORECORE or + or main space is mmapped or a previous contiguous call failed) + 2. A call to MMAP new space (disabled if not HAVE_MMAP). + Note that under the default settings, if MORECORE is unable to + fulfill a request, and HAVE_MMAP is true, then mmap is + used as a noncontiguous system allocator. This is a useful backup + strategy for systems with holes in address spaces -- in this case + sbrk cannot contiguously expand the heap, but mmap may be able to + find space. + 3. A call to MORECORE that cannot usually contiguously extend memory. + (disabled if not HAVE_MORECORE) + + In all cases, we need to request enough bytes from system to ensure + we can malloc nb bytes upon success, so pad with enough space for + top_foot, plus alignment-pad to make sure we don't lose bytes if + not on boundary, and round this up to a granularity unit. + */ + + if (MORECORE_CONTIGUOUS && !use_noncontiguous(m)) { + char* br = CMFAIL; + size_t ssize = asize; /* sbrk call size */ + msegmentptr ss = (m->top == 0)? 0 : segment_holding(m, (char*)m->top); + ACQUIRE_MALLOC_GLOBAL_LOCK(); + + if (ss == 0) { /* First time through or recovery */ + char* base = (char*)CALL_MORECORE(0); + if (base != CMFAIL) { + size_t fp; + /* Adjust to end on a page boundary */ + if (!is_page_aligned(base)) + ssize += (page_align((size_t)base) - (size_t)base); + fp = m->footprint + ssize; /* recheck limits */ + if (ssize > nb && ssize < HALF_MAX_SIZE_T && + (m->footprint_limit == 0 || + (fp > m->footprint && fp <= m->footprint_limit)) && + (br = (char*)(CALL_MORECORE(ssize))) == base) { + tbase = base; + tsize = ssize; + } + } + } + else { + /* Subtract out existing available top space from MORECORE request. */ + ssize = granularity_align(nb - m->topsize + SYS_ALLOC_PADDING); + /* Use mem here only if it did continuously extend old space */ + if (ssize < HALF_MAX_SIZE_T && + (br = (char*)(CALL_MORECORE(ssize))) == ss->base+ss->size) { + tbase = br; + tsize = ssize; + } + } + + if (tbase == CMFAIL) { /* Cope with partial failure */ + if (br != CMFAIL) { /* Try to use/extend the space we did get */ + if (ssize < HALF_MAX_SIZE_T && + ssize < nb + SYS_ALLOC_PADDING) { + size_t esize = granularity_align(nb + SYS_ALLOC_PADDING - ssize); + if (esize < HALF_MAX_SIZE_T) { + char* end = (char*)CALL_MORECORE(esize); + if (end != CMFAIL) + ssize += esize; + else { /* Can't use; try to release */ + (void) CALL_MORECORE(-ssize); + br = CMFAIL; + } + } + } + } + if (br != CMFAIL) { /* Use the space we did get */ + tbase = br; + tsize = ssize; + } + else + disable_contiguous(m); /* Don't try contiguous path in the future */ + } + + RELEASE_MALLOC_GLOBAL_LOCK(); + } + + if (HAVE_MMAP && tbase == CMFAIL) { /* Try MMAP */ + char* mp = (char*)(CALL_MMAP(asize)); + if (mp != CMFAIL) { + tbase = mp; + tsize = asize; + mmap_flag = USE_MMAP_BIT; + } + } + + if (HAVE_MORECORE && tbase == CMFAIL) { /* Try noncontiguous MORECORE */ + if (asize < HALF_MAX_SIZE_T) { + char* br = CMFAIL; + char* end = CMFAIL; + ACQUIRE_MALLOC_GLOBAL_LOCK(); + br = (char*)(CALL_MORECORE(asize)); + end = (char*)(CALL_MORECORE(0)); + RELEASE_MALLOC_GLOBAL_LOCK(); + if (br != CMFAIL && end != CMFAIL && br < end) { + size_t ssize = end - br; + if (ssize > nb + TOP_FOOT_SIZE) { + tbase = br; + tsize = ssize; + } + } + } + } + + if (tbase != CMFAIL) { + + if ((m->footprint += tsize) > m->max_footprint) + m->max_footprint = m->footprint; + + if (!is_initialized(m)) { /* first-time initialization */ + if (m->least_addr == 0 || tbase < m->least_addr) + m->least_addr = tbase; + m->seg.base = tbase; + m->seg.size = tsize; + m->seg.sflags = mmap_flag; + m->magic = mparams.magic; + m->release_checks = MAX_RELEASE_CHECK_RATE; + init_bins(m); +#if !ONLY_MSPACES + if (is_global(m)) + init_top(m, (mchunkptr)tbase, tsize - TOP_FOOT_SIZE); + else +#endif + { + /* Offset top by embedded malloc_state */ + mchunkptr mn = next_chunk(mem2chunk(m)); + init_top(m, mn, (size_t)((tbase + tsize) - (char*)mn) -TOP_FOOT_SIZE); + } + } + + else { + /* Try to merge with an existing segment */ + msegmentptr sp = &m->seg; + /* Only consider most recent segment if traversal suppressed */ + while (sp != 0 && tbase != sp->base + sp->size) + sp = (NO_SEGMENT_TRAVERSAL) ? 0 : sp->next; + if (sp != 0 && + !is_extern_segment(sp) && + (sp->sflags & USE_MMAP_BIT) == mmap_flag && + segment_holds(sp, m->top)) { /* append */ + sp->size += tsize; + init_top(m, m->top, m->topsize + tsize); + } + else { + if (tbase < m->least_addr) + m->least_addr = tbase; + sp = &m->seg; + while (sp != 0 && sp->base != tbase + tsize) + sp = (NO_SEGMENT_TRAVERSAL) ? 0 : sp->next; + if (sp != 0 && + !is_extern_segment(sp) && + (sp->sflags & USE_MMAP_BIT) == mmap_flag) { + char* oldbase = sp->base; + sp->base = tbase; + sp->size += tsize; + return prepend_alloc(m, tbase, oldbase, nb); + } + else + add_segment(m, tbase, tsize, mmap_flag); + } + } + + if (nb < m->topsize) { /* Allocate from new or extended top space */ + size_t rsize = m->topsize -= nb; + mchunkptr p = m->top; + mchunkptr r = m->top = chunk_plus_offset(p, nb); + r->head = rsize | PINUSE_BIT; + set_size_and_pinuse_of_inuse_chunk(m, p, nb); + check_top_chunk(m, m->top); + check_malloced_chunk(m, chunk2mem(p), nb); + return chunk2mem(p); + } + } + + MALLOC_FAILURE_ACTION; + return 0; +} + +/* ----------------------- system deallocation -------------------------- */ + +/* Unmap and unlink any mmapped segments that don't contain used chunks */ +static size_t release_unused_segments(mstate m) { + size_t released = 0; + int nsegs = 0; + msegmentptr pred = &m->seg; + msegmentptr sp = pred->next; + while (sp != 0) { + char* base = sp->base; + size_t size = sp->size; + msegmentptr next = sp->next; + ++nsegs; + if (is_mmapped_segment(sp) && !is_extern_segment(sp)) { + mchunkptr p = align_as_chunk(base); + size_t psize = chunksize(p); + /* Can unmap if first chunk holds entire segment and not pinned */ + if (!is_inuse(p) && (char*)p + psize >= base + size - TOP_FOOT_SIZE) { + tchunkptr tp = (tchunkptr)p; + assert(segment_holds(sp, (char*)sp)); + if (p == m->dv) { + m->dv = 0; + m->dvsize = 0; + } + else { + unlink_large_chunk(m, tp); + } + if (CALL_MUNMAP(base, size) == 0) { + released += size; + m->footprint -= size; + /* unlink obsoleted record */ + sp = pred; + sp->next = next; + } + else { /* back out if cannot unmap */ + insert_large_chunk(m, tp, psize); + } + } + } + if (NO_SEGMENT_TRAVERSAL) /* scan only first segment */ + break; + pred = sp; + sp = next; + } + /* Reset check counter */ + m->release_checks = (((size_t) nsegs > (size_t) MAX_RELEASE_CHECK_RATE)? + (size_t) nsegs : (size_t) MAX_RELEASE_CHECK_RATE); + return released; +} + +static int sys_trim(mstate m, size_t pad) { + size_t released = 0; + ensure_initialization(); + if (pad < MAX_REQUEST && is_initialized(m)) { + pad += TOP_FOOT_SIZE; /* ensure enough room for segment overhead */ + + if (m->topsize > pad) { + /* Shrink top space in granularity-size units, keeping at least one */ + size_t unit = mparams.granularity; + size_t extra = ((m->topsize - pad + (unit - SIZE_T_ONE)) / unit - + SIZE_T_ONE) * unit; + msegmentptr sp = segment_holding(m, (char*)m->top); + + if (!is_extern_segment(sp)) { + if (is_mmapped_segment(sp)) { + if (HAVE_MMAP && + sp->size >= extra && + !has_segment_link(m, sp)) { /* can't shrink if pinned */ + size_t newsize = sp->size - extra; + (void)newsize; /* placate people compiling -Wunused-variable */ + /* Prefer mremap, fall back to munmap */ + if ((CALL_MREMAP(sp->base, sp->size, newsize, 0) != MFAIL) || + (CALL_MUNMAP(sp->base + newsize, extra) == 0)) { + released = extra; + } + } + } + else if (HAVE_MORECORE) { + if (extra >= HALF_MAX_SIZE_T) /* Avoid wrapping negative */ + extra = (HALF_MAX_SIZE_T) + SIZE_T_ONE - unit; + ACQUIRE_MALLOC_GLOBAL_LOCK(); + { + /* Make sure end of memory is where we last set it. */ + char* old_br = (char*)(CALL_MORECORE(0)); + if (old_br == sp->base + sp->size) { + char* rel_br = (char*)(CALL_MORECORE(-extra)); + char* new_br = (char*)(CALL_MORECORE(0)); + if (rel_br != CMFAIL && new_br < old_br) + released = old_br - new_br; + } + } + RELEASE_MALLOC_GLOBAL_LOCK(); + } + } + + if (released != 0) { + sp->size -= released; + m->footprint -= released; + init_top(m, m->top, m->topsize - released); + check_top_chunk(m, m->top); + } + } + + /* Unmap any unused mmapped segments */ + if (HAVE_MMAP) + released += release_unused_segments(m); + + /* On failure, disable autotrim to avoid repeated failed future calls */ + if (released == 0 && m->topsize > m->trim_check) + m->trim_check = MAX_SIZE_T; + } + + return (released != 0)? 1 : 0; +} + +/* Consolidate and bin a chunk. Differs from exported versions + of free mainly in that the chunk need not be marked as inuse. +*/ +static void dispose_chunk(mstate m, mchunkptr p, size_t psize) { + mchunkptr next = chunk_plus_offset(p, psize); + if (!pinuse(p)) { + mchunkptr prev; + size_t prevsize = p->prev_foot; + if (is_mmapped(p)) { + psize += prevsize + MMAP_FOOT_PAD; + if (CALL_MUNMAP((char*)p - prevsize, psize) == 0) + m->footprint -= psize; + return; + } + prev = chunk_minus_offset(p, prevsize); + psize += prevsize; + p = prev; + if (RTCHECK(ok_address(m, prev))) { /* consolidate backward */ + if (p != m->dv) { + unlink_chunk(m, p, prevsize); + } + else if ((next->head & INUSE_BITS) == INUSE_BITS) { + m->dvsize = psize; + set_free_with_pinuse(p, psize, next); + return; + } + } + else { + CORRUPTION_ERROR_ACTION(m); + return; + } + } + if (RTCHECK(ok_address(m, next))) { + if (!cinuse(next)) { /* consolidate forward */ + if (next == m->top) { + size_t tsize = m->topsize += psize; + m->top = p; + p->head = tsize | PINUSE_BIT; + if (p == m->dv) { + m->dv = 0; + m->dvsize = 0; + } + return; + } + else if (next == m->dv) { + size_t dsize = m->dvsize += psize; + m->dv = p; + set_size_and_pinuse_of_free_chunk(p, dsize); + return; + } + else { + size_t nsize = chunksize(next); + psize += nsize; + unlink_chunk(m, next, nsize); + set_size_and_pinuse_of_free_chunk(p, psize); + if (p == m->dv) { + m->dvsize = psize; + return; + } + } + } + else { + set_free_with_pinuse(p, psize, next); + } + insert_chunk(m, p, psize); + } + else { + CORRUPTION_ERROR_ACTION(m); + } +} + +/* ---------------------------- malloc --------------------------- */ + +/* allocate a large request from the best fitting chunk in a treebin */ +static void* tmalloc_large(mstate m, size_t nb) { + tchunkptr v = 0; + size_t rsize = -nb; /* Unsigned negation */ + tchunkptr t; + bindex_t idx; + compute_tree_index(nb, idx); + if ((t = *treebin_at(m, idx)) != 0) { + /* Traverse tree for this bin looking for node with size == nb */ + size_t sizebits = nb << leftshift_for_tree_index(idx); + tchunkptr rst = 0; /* The deepest untaken right subtree */ + for (;;) { + tchunkptr rt; + size_t trem = chunksize(t) - nb; + if (trem < rsize) { + v = t; + if ((rsize = trem) == 0) + break; + } + rt = t->child[1]; + t = t->child[(sizebits >> (SIZE_T_BITSIZE-SIZE_T_ONE)) & 1]; + if (rt != 0 && rt != t) + rst = rt; + if (t == 0) { + t = rst; /* set t to least subtree holding sizes > nb */ + break; + } + sizebits <<= 1; + } + } + if (t == 0 && v == 0) { /* set t to root of next non-empty treebin */ + binmap_t leftbits = left_bits(idx2bit(idx)) & m->treemap; + if (leftbits != 0) { + bindex_t i; + binmap_t leastbit = least_bit(leftbits); + compute_bit2idx(leastbit, i); + t = *treebin_at(m, i); + } + } + + while (t != 0) { /* find smallest of tree or subtree */ + size_t trem = chunksize(t) - nb; + if (trem < rsize) { + rsize = trem; + v = t; + } + t = leftmost_child(t); + } + + /* If dv is a better fit, return 0 so malloc will use it */ + if (v != 0 && rsize < (size_t)(m->dvsize - nb)) { + if (RTCHECK(ok_address(m, v))) { /* split */ + mchunkptr r = chunk_plus_offset(v, nb); + assert(chunksize(v) == rsize + nb); + if (RTCHECK(ok_next(v, r))) { + unlink_large_chunk(m, v); + if (rsize < MIN_CHUNK_SIZE) + set_inuse_and_pinuse(m, v, (rsize + nb)); + else { + set_size_and_pinuse_of_inuse_chunk(m, v, nb); + set_size_and_pinuse_of_free_chunk(r, rsize); + insert_chunk(m, r, rsize); + } + return chunk2mem(v); + } + } + CORRUPTION_ERROR_ACTION(m); + } + return 0; +} + +/* allocate a small request from the best fitting chunk in a treebin */ +static void* tmalloc_small(mstate m, size_t nb) { + tchunkptr t, v; + size_t rsize; + bindex_t i; + binmap_t leastbit = least_bit(m->treemap); + compute_bit2idx(leastbit, i); + v = t = *treebin_at(m, i); + rsize = chunksize(t) - nb; + + while ((t = leftmost_child(t)) != 0) { + size_t trem = chunksize(t) - nb; + if (trem < rsize) { + rsize = trem; + v = t; + } + } + + if (RTCHECK(ok_address(m, v))) { + mchunkptr r = chunk_plus_offset(v, nb); + assert(chunksize(v) == rsize + nb); + if (RTCHECK(ok_next(v, r))) { + unlink_large_chunk(m, v); + if (rsize < MIN_CHUNK_SIZE) + set_inuse_and_pinuse(m, v, (rsize + nb)); + else { + set_size_and_pinuse_of_inuse_chunk(m, v, nb); + set_size_and_pinuse_of_free_chunk(r, rsize); + replace_dv(m, r, rsize); + } + return chunk2mem(v); + } + } + + CORRUPTION_ERROR_ACTION(m); + return 0; +} + +#if !ONLY_MSPACES + +void* dlmalloc(size_t bytes) { + /* + Basic algorithm: + If a small request (< 256 bytes minus per-chunk overhead): + 1. If one exists, use a remainderless chunk in associated smallbin. + (Remainderless means that there are too few excess bytes to + represent as a chunk.) + 2. If it is big enough, use the dv chunk, which is normally the + chunk adjacent to the one used for the most recent small request. + 3. If one exists, split the smallest available chunk in a bin, + saving remainder in dv. + 4. If it is big enough, use the top chunk. + 5. If available, get memory from system and use it + Otherwise, for a large request: + 1. Find the smallest available binned chunk that fits, and use it + if it is better fitting than dv chunk, splitting if necessary. + 2. If better fitting than any binned chunk, use the dv chunk. + 3. If it is big enough, use the top chunk. + 4. If request size >= mmap threshold, try to directly mmap this chunk. + 5. If available, get memory from system and use it + + The ugly goto's here ensure that postaction occurs along all paths. + */ + +#if USE_LOCKS + ensure_initialization(); /* initialize in sys_alloc if not using locks */ +#endif + + if (!PREACTION(gm)) { + void* mem; + size_t nb; + if (bytes <= MAX_SMALL_REQUEST) { + bindex_t idx; + binmap_t smallbits; + nb = (bytes < MIN_REQUEST)? MIN_CHUNK_SIZE : pad_request(bytes); + idx = small_index(nb); + smallbits = gm->smallmap >> idx; + + if ((smallbits & 0x3U) != 0) { /* Remainderless fit to a smallbin. */ + mchunkptr b, p; + idx += ~smallbits & 1; /* Uses next bin if idx empty */ + b = smallbin_at(gm, idx); + p = b->fd; + assert(chunksize(p) == small_index2size(idx)); + unlink_first_small_chunk(gm, b, p, idx); + set_inuse_and_pinuse(gm, p, small_index2size(idx)); + mem = chunk2mem(p); + check_malloced_chunk(gm, mem, nb); + goto postaction; + } + + else if (nb > gm->dvsize) { + if (smallbits != 0) { /* Use chunk in next nonempty smallbin */ + mchunkptr b, p, r; + size_t rsize; + bindex_t i; + binmap_t leftbits = (smallbits << idx) & left_bits(idx2bit(idx)); + binmap_t leastbit = least_bit(leftbits); + compute_bit2idx(leastbit, i); + b = smallbin_at(gm, i); + p = b->fd; + assert(chunksize(p) == small_index2size(i)); + unlink_first_small_chunk(gm, b, p, i); + rsize = small_index2size(i) - nb; + /* Fit here cannot be remainderless if 4byte sizes */ + if (SIZE_T_SIZE != 4 && rsize < MIN_CHUNK_SIZE) + set_inuse_and_pinuse(gm, p, small_index2size(i)); + else { + set_size_and_pinuse_of_inuse_chunk(gm, p, nb); + r = chunk_plus_offset(p, nb); + set_size_and_pinuse_of_free_chunk(r, rsize); + replace_dv(gm, r, rsize); + } + mem = chunk2mem(p); + check_malloced_chunk(gm, mem, nb); + goto postaction; + } + + else if (gm->treemap != 0 && (mem = tmalloc_small(gm, nb)) != 0) { + check_malloced_chunk(gm, mem, nb); + goto postaction; + } + } + } + else if (bytes >= MAX_REQUEST) + nb = MAX_SIZE_T; /* Too big to allocate. Force failure (in sys alloc) */ + else { + nb = pad_request(bytes); + if (gm->treemap != 0 && (mem = tmalloc_large(gm, nb)) != 0) { + check_malloced_chunk(gm, mem, nb); + goto postaction; + } + } + + if (nb <= gm->dvsize) { + size_t rsize = gm->dvsize - nb; + mchunkptr p = gm->dv; + if (rsize >= MIN_CHUNK_SIZE) { /* split dv */ + mchunkptr r = gm->dv = chunk_plus_offset(p, nb); + gm->dvsize = rsize; + set_size_and_pinuse_of_free_chunk(r, rsize); + set_size_and_pinuse_of_inuse_chunk(gm, p, nb); + } + else { /* exhaust dv */ + size_t dvs = gm->dvsize; + gm->dvsize = 0; + gm->dv = 0; + set_inuse_and_pinuse(gm, p, dvs); + } + mem = chunk2mem(p); + check_malloced_chunk(gm, mem, nb); + goto postaction; + } + + else if (nb < gm->topsize) { /* Split top */ + size_t rsize = gm->topsize -= nb; + mchunkptr p = gm->top; + mchunkptr r = gm->top = chunk_plus_offset(p, nb); + r->head = rsize | PINUSE_BIT; + set_size_and_pinuse_of_inuse_chunk(gm, p, nb); + mem = chunk2mem(p); + check_top_chunk(gm, gm->top); + check_malloced_chunk(gm, mem, nb); + goto postaction; + } + + mem = sys_alloc(gm, nb); + + postaction: + POSTACTION(gm); + return mem; + } + + return 0; +} + +/* ---------------------------- free --------------------------- */ + +void dlfree(void* mem) { + /* + Consolidate freed chunks with preceeding or succeeding bordering + free chunks, if they exist, and then place in a bin. Intermixed + with special cases for top, dv, mmapped chunks, and usage errors. + */ + + if (mem != 0) { + mchunkptr p = mem2chunk(mem); +#if FOOTERS + mstate fm = get_mstate_for(p); + if (!ok_magic(fm)) { + USAGE_ERROR_ACTION(fm, p); + return; + } +#else /* FOOTERS */ +#define fm gm +#endif /* FOOTERS */ + if (!PREACTION(fm)) { + check_inuse_chunk(fm, p); + if (RTCHECK(ok_address(fm, p) && ok_inuse(p))) { + size_t psize = chunksize(p); + mchunkptr next = chunk_plus_offset(p, psize); + if (!pinuse(p)) { + size_t prevsize = p->prev_foot; + if (is_mmapped(p)) { + psize += prevsize + MMAP_FOOT_PAD; + if (CALL_MUNMAP((char*)p - prevsize, psize) == 0) + fm->footprint -= psize; + goto postaction; + } + else { + mchunkptr prev = chunk_minus_offset(p, prevsize); + psize += prevsize; + p = prev; + if (RTCHECK(ok_address(fm, prev))) { /* consolidate backward */ + if (p != fm->dv) { + unlink_chunk(fm, p, prevsize); + } + else if ((next->head & INUSE_BITS) == INUSE_BITS) { + fm->dvsize = psize; + set_free_with_pinuse(p, psize, next); + goto postaction; + } + } + else + goto erroraction; + } + } + + if (RTCHECK(ok_next(p, next) && ok_pinuse(next))) { + if (!cinuse(next)) { /* consolidate forward */ + if (next == fm->top) { + size_t tsize = fm->topsize += psize; + fm->top = p; + p->head = tsize | PINUSE_BIT; + if (p == fm->dv) { + fm->dv = 0; + fm->dvsize = 0; + } + if (should_trim(fm, tsize)) + sys_trim(fm, 0); + goto postaction; + } + else if (next == fm->dv) { + size_t dsize = fm->dvsize += psize; + fm->dv = p; + set_size_and_pinuse_of_free_chunk(p, dsize); + goto postaction; + } + else { + size_t nsize = chunksize(next); + psize += nsize; + unlink_chunk(fm, next, nsize); + set_size_and_pinuse_of_free_chunk(p, psize); + if (p == fm->dv) { + fm->dvsize = psize; + goto postaction; + } + } + } + else + set_free_with_pinuse(p, psize, next); + + if (is_small(psize)) { + insert_small_chunk(fm, p, psize); + check_free_chunk(fm, p); + } + else { + tchunkptr tp = (tchunkptr)p; + insert_large_chunk(fm, tp, psize); + check_free_chunk(fm, p); + if (--fm->release_checks == 0) + release_unused_segments(fm); + } + goto postaction; + } + } + erroraction: + USAGE_ERROR_ACTION(fm, p); + postaction: + POSTACTION(fm); + } + } +#if !FOOTERS +#undef fm +#endif /* FOOTERS */ +} + +void* dlcalloc(size_t n_elements, size_t elem_size) { + void* mem; + size_t req = 0; + if (n_elements != 0) { + req = n_elements * elem_size; + if (((n_elements | elem_size) & ~(size_t)0xffff) && + (req / n_elements != elem_size)) + req = MAX_SIZE_T; /* force downstream failure on overflow */ + } + mem = dlmalloc(req); + if (mem != 0 && calloc_must_clear(mem2chunk(mem))) + memset(mem, 0, req); + return mem; +} + +#endif /* !ONLY_MSPACES */ + +/* ------------ Internal support for realloc, memalign, etc -------------- */ + +/* Try to realloc; only in-place unless can_move true */ +static mchunkptr try_realloc_chunk(mstate m, mchunkptr p, size_t nb, + int can_move) { + mchunkptr newp = 0; + size_t oldsize = chunksize(p); + mchunkptr next = chunk_plus_offset(p, oldsize); + if (RTCHECK(ok_address(m, p) && ok_inuse(p) && + ok_next(p, next) && ok_pinuse(next))) { + if (is_mmapped(p)) { + newp = mmap_resize(m, p, nb, can_move); + } + else if (oldsize >= nb) { /* already big enough */ + size_t rsize = oldsize - nb; + if (rsize >= MIN_CHUNK_SIZE) { /* split off remainder */ + mchunkptr r = chunk_plus_offset(p, nb); + set_inuse(m, p, nb); + set_inuse(m, r, rsize); + dispose_chunk(m, r, rsize); + } + newp = p; + } + else if (next == m->top) { /* extend into top */ + if (oldsize + m->topsize > nb) { + size_t newsize = oldsize + m->topsize; + size_t newtopsize = newsize - nb; + mchunkptr newtop = chunk_plus_offset(p, nb); + set_inuse(m, p, nb); + newtop->head = newtopsize |PINUSE_BIT; + m->top = newtop; + m->topsize = newtopsize; + newp = p; + } + } + else if (next == m->dv) { /* extend into dv */ + size_t dvs = m->dvsize; + if (oldsize + dvs >= nb) { + size_t dsize = oldsize + dvs - nb; + if (dsize >= MIN_CHUNK_SIZE) { + mchunkptr r = chunk_plus_offset(p, nb); + mchunkptr n = chunk_plus_offset(r, dsize); + set_inuse(m, p, nb); + set_size_and_pinuse_of_free_chunk(r, dsize); + clear_pinuse(n); + m->dvsize = dsize; + m->dv = r; + } + else { /* exhaust dv */ + size_t newsize = oldsize + dvs; + set_inuse(m, p, newsize); + m->dvsize = 0; + m->dv = 0; + } + newp = p; + } + } + else if (!cinuse(next)) { /* extend into next free chunk */ + size_t nextsize = chunksize(next); + if (oldsize + nextsize >= nb) { + size_t rsize = oldsize + nextsize - nb; + unlink_chunk(m, next, nextsize); + if (rsize < MIN_CHUNK_SIZE) { + size_t newsize = oldsize + nextsize; + set_inuse(m, p, newsize); + } + else { + mchunkptr r = chunk_plus_offset(p, nb); + set_inuse(m, p, nb); + set_inuse(m, r, rsize); + dispose_chunk(m, r, rsize); + } + newp = p; + } + } + } + else { + USAGE_ERROR_ACTION(m, chunk2mem(p)); + } + return newp; +} + +static void* internal_memalign(mstate m, size_t alignment, size_t bytes) { + void* mem = 0; + if (alignment < MIN_CHUNK_SIZE) /* must be at least a minimum chunk size */ + alignment = MIN_CHUNK_SIZE; + if ((alignment & (alignment-SIZE_T_ONE)) != 0) {/* Ensure a power of 2 */ + size_t a = MALLOC_ALIGNMENT << 1; + while (a < alignment) a <<= 1; + alignment = a; + } + if (bytes >= MAX_REQUEST - alignment) { + if (m != 0) { /* Test isn't needed but avoids compiler warning */ + MALLOC_FAILURE_ACTION; + } + } + else { + size_t nb = request2size(bytes); + size_t req = nb + alignment + MIN_CHUNK_SIZE - CHUNK_OVERHEAD; + mem = internal_malloc(m, req); + if (mem != 0) { + mchunkptr p = mem2chunk(mem); + if (PREACTION(m)) + return 0; + if ((((size_t)(mem)) & (alignment - 1)) != 0) { /* misaligned */ + /* + Find an aligned spot inside chunk. Since we need to give + back leading space in a chunk of at least MIN_CHUNK_SIZE, if + the first calculation places us at a spot with less than + MIN_CHUNK_SIZE leader, we can move to the next aligned spot. + We've allocated enough total room so that this is always + possible. + */ + char* br = (char*)mem2chunk((size_t)(((size_t)((char*)mem + alignment - + SIZE_T_ONE)) & + -alignment)); + char* pos = ((size_t)(br - (char*)(p)) >= MIN_CHUNK_SIZE)? + br : br+alignment; + mchunkptr newp = (mchunkptr)pos; + size_t leadsize = pos - (char*)(p); + size_t newsize = chunksize(p) - leadsize; + + if (is_mmapped(p)) { /* For mmapped chunks, just adjust offset */ + newp->prev_foot = p->prev_foot + leadsize; + newp->head = newsize; + } + else { /* Otherwise, give back leader, use the rest */ + set_inuse(m, newp, newsize); + set_inuse(m, p, leadsize); + dispose_chunk(m, p, leadsize); + } + p = newp; + } + + /* Give back spare room at the end */ + if (!is_mmapped(p)) { + size_t size = chunksize(p); + if (size > nb + MIN_CHUNK_SIZE) { + size_t remainder_size = size - nb; + mchunkptr remainder = chunk_plus_offset(p, nb); + set_inuse(m, p, nb); + set_inuse(m, remainder, remainder_size); + dispose_chunk(m, remainder, remainder_size); + } + } + + mem = chunk2mem(p); + assert (chunksize(p) >= nb); + assert(((size_t)mem & (alignment - 1)) == 0); + check_inuse_chunk(m, p); + POSTACTION(m); + } + } + return mem; +} + +/* + Common support for independent_X routines, handling + all of the combinations that can result. + The opts arg has: + bit 0 set if all elements are same size (using sizes[0]) + bit 1 set if elements should be zeroed +*/ +static void** ialloc(mstate m, + size_t n_elements, + size_t* sizes, + int opts, + void* chunks[]) { + + size_t element_size; /* chunksize of each element, if all same */ + size_t contents_size; /* total size of elements */ + size_t array_size; /* request size of pointer array */ + void* mem; /* malloced aggregate space */ + mchunkptr p; /* corresponding chunk */ + size_t remainder_size; /* remaining bytes while splitting */ + void** marray; /* either "chunks" or malloced ptr array */ + mchunkptr array_chunk; /* chunk for malloced ptr array */ + flag_t was_enabled; /* to disable mmap */ + size_t size; + size_t i; + + ensure_initialization(); + /* compute array length, if needed */ + if (chunks != 0) { + if (n_elements == 0) + return chunks; /* nothing to do */ + marray = chunks; + array_size = 0; + } + else { + /* if empty req, must still return chunk representing empty array */ + if (n_elements == 0) + return (void**)internal_malloc(m, 0); + marray = 0; + array_size = request2size(n_elements * (sizeof(void*))); + } + + /* compute total element size */ + if (opts & 0x1) { /* all-same-size */ + element_size = request2size(*sizes); + contents_size = n_elements * element_size; + } + else { /* add up all the sizes */ + element_size = 0; + contents_size = 0; + for (i = 0; i != n_elements; ++i) + contents_size += request2size(sizes[i]); + } + + size = contents_size + array_size; + + /* + Allocate the aggregate chunk. First disable direct-mmapping so + malloc won't use it, since we would not be able to later + free/realloc space internal to a segregated mmap region. + */ + was_enabled = use_mmap(m); + disable_mmap(m); + mem = internal_malloc(m, size - CHUNK_OVERHEAD); + if (was_enabled) + enable_mmap(m); + if (mem == 0) + return 0; + + if (PREACTION(m)) return 0; + p = mem2chunk(mem); + remainder_size = chunksize(p); + + assert(!is_mmapped(p)); + + if (opts & 0x2) { /* optionally clear the elements */ + memset((size_t*)mem, 0, remainder_size - SIZE_T_SIZE - array_size); + } + + /* If not provided, allocate the pointer array as final part of chunk */ + if (marray == 0) { + size_t array_chunk_size; + array_chunk = chunk_plus_offset(p, contents_size); + array_chunk_size = remainder_size - contents_size; + marray = (void**) (chunk2mem(array_chunk)); + set_size_and_pinuse_of_inuse_chunk(m, array_chunk, array_chunk_size); + remainder_size = contents_size; + } + + /* split out elements */ + for (i = 0; ; ++i) { + marray[i] = chunk2mem(p); + if (i != n_elements-1) { + if (element_size != 0) + size = element_size; + else + size = request2size(sizes[i]); + remainder_size -= size; + set_size_and_pinuse_of_inuse_chunk(m, p, size); + p = chunk_plus_offset(p, size); + } + else { /* the final element absorbs any overallocation slop */ + set_size_and_pinuse_of_inuse_chunk(m, p, remainder_size); + break; + } + } + +#if DEBUG + if (marray != chunks) { + /* final element must have exactly exhausted chunk */ + if (element_size != 0) { + assert(remainder_size == element_size); + } + else { + assert(remainder_size == request2size(sizes[i])); + } + check_inuse_chunk(m, mem2chunk(marray)); + } + for (i = 0; i != n_elements; ++i) + check_inuse_chunk(m, mem2chunk(marray[i])); + +#endif /* DEBUG */ + + POSTACTION(m); + return marray; +} + +/* Try to free all pointers in the given array. + Note: this could be made faster, by delaying consolidation, + at the price of disabling some user integrity checks, We + still optimize some consolidations by combining adjacent + chunks before freeing, which will occur often if allocated + with ialloc or the array is sorted. +*/ +static size_t internal_bulk_free(mstate m, void* array[], size_t nelem) { + size_t unfreed = 0; + if (!PREACTION(m)) { + void** a; + void** fence = &(array[nelem]); + for (a = array; a != fence; ++a) { + void* mem = *a; + if (mem != 0) { + mchunkptr p = mem2chunk(mem); + size_t psize = chunksize(p); +#if FOOTERS + if (get_mstate_for(p) != m) { + ++unfreed; + continue; + } +#endif + check_inuse_chunk(m, p); + *a = 0; + if (RTCHECK(ok_address(m, p) && ok_inuse(p))) { + void ** b = a + 1; /* try to merge with next chunk */ + mchunkptr next = next_chunk(p); + if (b != fence && *b == chunk2mem(next)) { + size_t newsize = chunksize(next) + psize; + set_inuse(m, p, newsize); + *b = chunk2mem(p); + } + else + dispose_chunk(m, p, psize); + } + else { + CORRUPTION_ERROR_ACTION(m); + break; + } + } + } + if (should_trim(m, m->topsize)) + sys_trim(m, 0); + POSTACTION(m); + } + return unfreed; +} + +/* Traversal */ +#if MALLOC_INSPECT_ALL +static void internal_inspect_all(mstate m, + void(*handler)(void *start, + void *end, + size_t used_bytes, + void* callback_arg), + void* arg) { + if (is_initialized(m)) { + mchunkptr top = m->top; + msegmentptr s; + for (s = &m->seg; s != 0; s = s->next) { + mchunkptr q = align_as_chunk(s->base); + while (segment_holds(s, q) && q->head != FENCEPOST_HEAD) { + mchunkptr next = next_chunk(q); + size_t sz = chunksize(q); + size_t used; + void* start; + if (is_inuse(q)) { + used = sz - CHUNK_OVERHEAD; /* must not be mmapped */ + start = chunk2mem(q); + } + else { + used = 0; + if (is_small(sz)) { /* offset by possible bookkeeping */ + start = (void*)((char*)q + sizeof(struct malloc_chunk)); + } + else { + start = (void*)((char*)q + sizeof(struct malloc_tree_chunk)); + } + } + if (start < (void*)next) /* skip if all space is bookkeeping */ + handler(start, next, used, arg); + if (q == top) + break; + q = next; + } + } + } +} +#endif /* MALLOC_INSPECT_ALL */ + +/* ------------------ Exported realloc, memalign, etc -------------------- */ + +#if !ONLY_MSPACES + +void* dlrealloc(void* oldmem, size_t bytes) { + void* mem = 0; + if (oldmem == 0) { + mem = dlmalloc(bytes); + } + else if (bytes >= MAX_REQUEST) { + MALLOC_FAILURE_ACTION; + } +#ifdef REALLOC_ZERO_BYTES_FREES + else if (bytes == 0) { + dlfree(oldmem); + } +#endif /* REALLOC_ZERO_BYTES_FREES */ + else { + size_t nb = request2size(bytes); + mchunkptr oldp = mem2chunk(oldmem); +#if ! FOOTERS + mstate m = gm; +#else /* FOOTERS */ + mstate m = get_mstate_for(oldp); + if (!ok_magic(m)) { + USAGE_ERROR_ACTION(m, oldmem); + return 0; + } +#endif /* FOOTERS */ + if (!PREACTION(m)) { + mchunkptr newp = try_realloc_chunk(m, oldp, nb, 1); + POSTACTION(m); + if (newp != 0) { + check_inuse_chunk(m, newp); + mem = chunk2mem(newp); + } + else { + mem = internal_malloc(m, bytes); + if (mem != 0) { + size_t oc = chunksize(oldp) - overhead_for(oldp); + memcpy(mem, oldmem, (oc < bytes)? oc : bytes); + internal_free(m, oldmem); + } + } + } + } + return mem; +} + +void* dlrealloc_in_place(void* oldmem, size_t bytes) { + void* mem = 0; + if (oldmem != 0) { + if (bytes >= MAX_REQUEST) { + MALLOC_FAILURE_ACTION; + } + else { + size_t nb = request2size(bytes); + mchunkptr oldp = mem2chunk(oldmem); +#if ! FOOTERS + mstate m = gm; +#else /* FOOTERS */ + mstate m = get_mstate_for(oldp); + if (!ok_magic(m)) { + USAGE_ERROR_ACTION(m, oldmem); + return 0; + } +#endif /* FOOTERS */ + if (!PREACTION(m)) { + mchunkptr newp = try_realloc_chunk(m, oldp, nb, 0); + POSTACTION(m); + if (newp == oldp) { + check_inuse_chunk(m, newp); + mem = oldmem; + } + } + } + } + return mem; +} + +void* dlmemalign(size_t alignment, size_t bytes) { + if (alignment <= MALLOC_ALIGNMENT) { + return dlmalloc(bytes); + } + return internal_memalign(gm, alignment, bytes); +} + +int dlposix_memalign(void** pp, size_t alignment, size_t bytes) { + void* mem = 0; + if (alignment == MALLOC_ALIGNMENT) + mem = dlmalloc(bytes); + else { + size_t d = alignment / sizeof(void*); + size_t r = alignment % sizeof(void*); + if (r != 0 || d == 0 || (d & (d-SIZE_T_ONE)) != 0) + return EINVAL; + else if (bytes <= MAX_REQUEST - alignment) { + if (alignment < MIN_CHUNK_SIZE) + alignment = MIN_CHUNK_SIZE; + mem = internal_memalign(gm, alignment, bytes); + } + } + if (mem == 0) + return ENOMEM; + else { + *pp = mem; + return 0; + } +} + +void* dlvalloc(size_t bytes) { + size_t pagesz; + ensure_initialization(); + pagesz = mparams.page_size; + return dlmemalign(pagesz, bytes); +} + +void* dlpvalloc(size_t bytes) { + size_t pagesz; + ensure_initialization(); + pagesz = mparams.page_size; + return dlmemalign(pagesz, (bytes + pagesz - SIZE_T_ONE) & ~(pagesz - SIZE_T_ONE)); +} + +void** dlindependent_calloc(size_t n_elements, size_t elem_size, + void* chunks[]) { + size_t sz = elem_size; /* serves as 1-element array */ + return ialloc(gm, n_elements, &sz, 3, chunks); +} + +void** dlindependent_comalloc(size_t n_elements, size_t sizes[], + void* chunks[]) { + return ialloc(gm, n_elements, sizes, 0, chunks); +} + +size_t dlbulk_free(void* array[], size_t nelem) { + return internal_bulk_free(gm, array, nelem); +} + +#if MALLOC_INSPECT_ALL +void dlmalloc_inspect_all(void(*handler)(void *start, + void *end, + size_t used_bytes, + void* callback_arg), + void* arg) { + ensure_initialization(); + if (!PREACTION(gm)) { + internal_inspect_all(gm, handler, arg); + POSTACTION(gm); + } +} +#endif /* MALLOC_INSPECT_ALL */ + +int dlmalloc_trim(size_t pad) { + int result = 0; + ensure_initialization(); + if (!PREACTION(gm)) { + result = sys_trim(gm, pad); + POSTACTION(gm); + } + return result; +} + +size_t dlmalloc_footprint(void) { + return gm->footprint; +} + +size_t dlmalloc_max_footprint(void) { + return gm->max_footprint; +} + +size_t dlmalloc_footprint_limit(void) { + size_t maf = gm->footprint_limit; + return maf == 0 ? MAX_SIZE_T : maf; +} + +size_t dlmalloc_set_footprint_limit(size_t bytes) { + size_t result; /* invert sense of 0 */ + if (bytes == 0) + result = granularity_align(1); /* Use minimal size */ + if (bytes == MAX_SIZE_T) + result = 0; /* disable */ + else + result = granularity_align(bytes); + return gm->footprint_limit = result; +} + +#if !NO_MALLINFO +struct mallinfo dlmallinfo(void) { + return internal_mallinfo(gm); +} +#endif /* NO_MALLINFO */ + +#if !NO_MALLOC_STATS +void dlmalloc_stats() { + internal_malloc_stats(gm); +} +#endif /* NO_MALLOC_STATS */ + +int dlmallopt(int param_number, int value) { + return change_mparam(param_number, value); +} + +size_t dlmalloc_usable_size(void* mem) { + if (mem != 0) { + mchunkptr p = mem2chunk(mem); + if (is_inuse(p)) + return chunksize(p) - overhead_for(p); + } + return 0; +} + +#endif /* !ONLY_MSPACES */ + +/* ----------------------------- user mspaces ---------------------------- */ + +#if MSPACES + +static mstate init_user_mstate(char* tbase, size_t tsize) { + size_t msize = pad_request(sizeof(struct malloc_state)); + mchunkptr mn; + mchunkptr msp = align_as_chunk(tbase); + mstate m = (mstate)(chunk2mem(msp)); + memset(m, 0, msize); + (void)INITIAL_LOCK(&m->mutex); + msp->head = (msize|INUSE_BITS); + m->seg.base = m->least_addr = tbase; + m->seg.size = m->footprint = m->max_footprint = tsize; + m->magic = mparams.magic; + m->release_checks = MAX_RELEASE_CHECK_RATE; + m->mflags = mparams.default_mflags; + m->extp = 0; + m->exts = 0; + disable_contiguous(m); + init_bins(m); + mn = next_chunk(mem2chunk(m)); + init_top(m, mn, (size_t)((tbase + tsize) - (char*)mn) - TOP_FOOT_SIZE); + check_top_chunk(m, m->top); + return m; +} + +mspace create_mspace(size_t capacity, int locked) { + mstate m = 0; + size_t msize; + ensure_initialization(); + msize = pad_request(sizeof(struct malloc_state)); + if (capacity < (size_t) -(msize + TOP_FOOT_SIZE + mparams.page_size)) { + size_t rs = ((capacity == 0)? mparams.granularity : + (capacity + TOP_FOOT_SIZE + msize)); + size_t tsize = granularity_align(rs); + char* tbase = (char*)(CALL_MMAP(tsize)); + if (tbase != CMFAIL) { + m = init_user_mstate(tbase, tsize); + m->seg.sflags = USE_MMAP_BIT; + set_lock(m, locked); + } + } + return (mspace)m; +} + +mspace create_mspace_with_base(void* base, size_t capacity, int locked) { + mstate m = 0; + size_t msize; + ensure_initialization(); + msize = pad_request(sizeof(struct malloc_state)); + if (capacity > msize + TOP_FOOT_SIZE && + capacity < (size_t) -(msize + TOP_FOOT_SIZE + mparams.page_size)) { + m = init_user_mstate((char*)base, capacity); + m->seg.sflags = EXTERN_BIT; + set_lock(m, locked); + } + return (mspace)m; +} + +int mspace_track_large_chunks(mspace msp, int enable) { + int ret = 0; + mstate ms = (mstate)msp; + if (!PREACTION(ms)) { + if (!use_mmap(ms)) { + ret = 1; + } + if (!enable) { + enable_mmap(ms); + } else { + disable_mmap(ms); + } + POSTACTION(ms); + } + return ret; +} + +size_t destroy_mspace(mspace msp) { + size_t freed = 0; + mstate ms = (mstate)msp; + if (ok_magic(ms)) { + msegmentptr sp = &ms->seg; + (void)DESTROY_LOCK(&ms->mutex); /* destroy before unmapped */ + while (sp != 0) { + char* base = sp->base; + size_t size = sp->size; + flag_t flag = sp->sflags; + (void)base; /* placate people compiling -Wunused-variable */ + sp = sp->next; + if ((flag & USE_MMAP_BIT) && !(flag & EXTERN_BIT) && + CALL_MUNMAP(base, size) == 0) + freed += size; + } + } + else { + USAGE_ERROR_ACTION(ms,ms); + } + return freed; +} + +/* + mspace versions of routines are near-clones of the global + versions. This is not so nice but better than the alternatives. +*/ + +void* mspace_malloc(mspace msp, size_t bytes) { + mstate ms = (mstate)msp; + if (!ok_magic(ms)) { + USAGE_ERROR_ACTION(ms,ms); + return 0; + } + if (!PREACTION(ms)) { + void* mem; + size_t nb; + if (bytes <= MAX_SMALL_REQUEST) { + bindex_t idx; + binmap_t smallbits; + nb = (bytes < MIN_REQUEST)? MIN_CHUNK_SIZE : pad_request(bytes); + idx = small_index(nb); + smallbits = ms->smallmap >> idx; + + if ((smallbits & 0x3U) != 0) { /* Remainderless fit to a smallbin. */ + mchunkptr b, p; + idx += ~smallbits & 1; /* Uses next bin if idx empty */ + b = smallbin_at(ms, idx); + p = b->fd; + assert(chunksize(p) == small_index2size(idx)); + unlink_first_small_chunk(ms, b, p, idx); + set_inuse_and_pinuse(ms, p, small_index2size(idx)); + mem = chunk2mem(p); + check_malloced_chunk(ms, mem, nb); + goto postaction; + } + + else if (nb > ms->dvsize) { + if (smallbits != 0) { /* Use chunk in next nonempty smallbin */ + mchunkptr b, p, r; + size_t rsize; + bindex_t i; + binmap_t leftbits = (smallbits << idx) & left_bits(idx2bit(idx)); + binmap_t leastbit = least_bit(leftbits); + compute_bit2idx(leastbit, i); + b = smallbin_at(ms, i); + p = b->fd; + assert(chunksize(p) == small_index2size(i)); + unlink_first_small_chunk(ms, b, p, i); + rsize = small_index2size(i) - nb; + /* Fit here cannot be remainderless if 4byte sizes */ + if (SIZE_T_SIZE != 4 && rsize < MIN_CHUNK_SIZE) + set_inuse_and_pinuse(ms, p, small_index2size(i)); + else { + set_size_and_pinuse_of_inuse_chunk(ms, p, nb); + r = chunk_plus_offset(p, nb); + set_size_and_pinuse_of_free_chunk(r, rsize); + replace_dv(ms, r, rsize); + } + mem = chunk2mem(p); + check_malloced_chunk(ms, mem, nb); + goto postaction; + } + + else if (ms->treemap != 0 && (mem = tmalloc_small(ms, nb)) != 0) { + check_malloced_chunk(ms, mem, nb); + goto postaction; + } + } + } + else if (bytes >= MAX_REQUEST) + nb = MAX_SIZE_T; /* Too big to allocate. Force failure (in sys alloc) */ + else { + nb = pad_request(bytes); + if (ms->treemap != 0 && (mem = tmalloc_large(ms, nb)) != 0) { + check_malloced_chunk(ms, mem, nb); + goto postaction; + } + } + + if (nb <= ms->dvsize) { + size_t rsize = ms->dvsize - nb; + mchunkptr p = ms->dv; + if (rsize >= MIN_CHUNK_SIZE) { /* split dv */ + mchunkptr r = ms->dv = chunk_plus_offset(p, nb); + ms->dvsize = rsize; + set_size_and_pinuse_of_free_chunk(r, rsize); + set_size_and_pinuse_of_inuse_chunk(ms, p, nb); + } + else { /* exhaust dv */ + size_t dvs = ms->dvsize; + ms->dvsize = 0; + ms->dv = 0; + set_inuse_and_pinuse(ms, p, dvs); + } + mem = chunk2mem(p); + check_malloced_chunk(ms, mem, nb); + goto postaction; + } + + else if (nb < ms->topsize) { /* Split top */ + size_t rsize = ms->topsize -= nb; + mchunkptr p = ms->top; + mchunkptr r = ms->top = chunk_plus_offset(p, nb); + r->head = rsize | PINUSE_BIT; + set_size_and_pinuse_of_inuse_chunk(ms, p, nb); + mem = chunk2mem(p); + check_top_chunk(ms, ms->top); + check_malloced_chunk(ms, mem, nb); + goto postaction; + } + + mem = sys_alloc(ms, nb); + + postaction: + POSTACTION(ms); + return mem; + } + + return 0; +} + +void mspace_free(mspace msp, void* mem) { + if (mem != 0) { + mchunkptr p = mem2chunk(mem); +#if FOOTERS + mstate fm = get_mstate_for(p); + (void)msp; /* placate people compiling -Wunused */ +#else /* FOOTERS */ + mstate fm = (mstate)msp; +#endif /* FOOTERS */ + if (!ok_magic(fm)) { + USAGE_ERROR_ACTION(fm, p); + return; + } + if (!PREACTION(fm)) { + check_inuse_chunk(fm, p); + if (RTCHECK(ok_address(fm, p) && ok_inuse(p))) { + size_t psize = chunksize(p); + mchunkptr next = chunk_plus_offset(p, psize); + if (!pinuse(p)) { + size_t prevsize = p->prev_foot; + if (is_mmapped(p)) { + psize += prevsize + MMAP_FOOT_PAD; + if (CALL_MUNMAP((char*)p - prevsize, psize) == 0) + fm->footprint -= psize; + goto postaction; + } + else { + mchunkptr prev = chunk_minus_offset(p, prevsize); + psize += prevsize; + p = prev; + if (RTCHECK(ok_address(fm, prev))) { /* consolidate backward */ + if (p != fm->dv) { + unlink_chunk(fm, p, prevsize); + } + else if ((next->head & INUSE_BITS) == INUSE_BITS) { + fm->dvsize = psize; + set_free_with_pinuse(p, psize, next); + goto postaction; + } + } + else + goto erroraction; + } + } + + if (RTCHECK(ok_next(p, next) && ok_pinuse(next))) { + if (!cinuse(next)) { /* consolidate forward */ + if (next == fm->top) { + size_t tsize = fm->topsize += psize; + fm->top = p; + p->head = tsize | PINUSE_BIT; + if (p == fm->dv) { + fm->dv = 0; + fm->dvsize = 0; + } + if (should_trim(fm, tsize)) + sys_trim(fm, 0); + goto postaction; + } + else if (next == fm->dv) { + size_t dsize = fm->dvsize += psize; + fm->dv = p; + set_size_and_pinuse_of_free_chunk(p, dsize); + goto postaction; + } + else { + size_t nsize = chunksize(next); + psize += nsize; + unlink_chunk(fm, next, nsize); + set_size_and_pinuse_of_free_chunk(p, psize); + if (p == fm->dv) { + fm->dvsize = psize; + goto postaction; + } + } + } + else + set_free_with_pinuse(p, psize, next); + + if (is_small(psize)) { + insert_small_chunk(fm, p, psize); + check_free_chunk(fm, p); + } + else { + tchunkptr tp = (tchunkptr)p; + insert_large_chunk(fm, tp, psize); + check_free_chunk(fm, p); + if (--fm->release_checks == 0) + release_unused_segments(fm); + } + goto postaction; + } + } + erroraction: + USAGE_ERROR_ACTION(fm, p); + postaction: + POSTACTION(fm); + } + } +} + +void* mspace_calloc(mspace msp, size_t n_elements, size_t elem_size) { + void* mem; + size_t req = 0; + mstate ms = (mstate)msp; + if (!ok_magic(ms)) { + USAGE_ERROR_ACTION(ms,ms); + return 0; + } + if (n_elements != 0) { + req = n_elements * elem_size; + if (((n_elements | elem_size) & ~(size_t)0xffff) && + (req / n_elements != elem_size)) + req = MAX_SIZE_T; /* force downstream failure on overflow */ + } + mem = internal_malloc(ms, req); + if (mem != 0 && calloc_must_clear(mem2chunk(mem))) + memset(mem, 0, req); + return mem; +} + +void* mspace_realloc(mspace msp, void* oldmem, size_t bytes) { + void* mem = 0; + if (oldmem == 0) { + mem = mspace_malloc(msp, bytes); + } + else if (bytes >= MAX_REQUEST) { + MALLOC_FAILURE_ACTION; + } +#ifdef REALLOC_ZERO_BYTES_FREES + else if (bytes == 0) { + mspace_free(msp, oldmem); + } +#endif /* REALLOC_ZERO_BYTES_FREES */ + else { + size_t nb = request2size(bytes); + mchunkptr oldp = mem2chunk(oldmem); +#if ! FOOTERS + mstate m = (mstate)msp; +#else /* FOOTERS */ + mstate m = get_mstate_for(oldp); + if (!ok_magic(m)) { + USAGE_ERROR_ACTION(m, oldmem); + return 0; + } +#endif /* FOOTERS */ + if (!PREACTION(m)) { + mchunkptr newp = try_realloc_chunk(m, oldp, nb, 1); + POSTACTION(m); + if (newp != 0) { + check_inuse_chunk(m, newp); + mem = chunk2mem(newp); + } + else { + mem = mspace_malloc(m, bytes); + if (mem != 0) { + size_t oc = chunksize(oldp) - overhead_for(oldp); + memcpy(mem, oldmem, (oc < bytes)? oc : bytes); + mspace_free(m, oldmem); + } + } + } + } + return mem; +} + +void* mspace_realloc_in_place(mspace msp, void* oldmem, size_t bytes) { + void* mem = 0; + if (oldmem != 0) { + if (bytes >= MAX_REQUEST) { + MALLOC_FAILURE_ACTION; + } + else { + size_t nb = request2size(bytes); + mchunkptr oldp = mem2chunk(oldmem); +#if ! FOOTERS + mstate m = (mstate)msp; +#else /* FOOTERS */ + mstate m = get_mstate_for(oldp); + (void)msp; /* placate people compiling -Wunused */ + if (!ok_magic(m)) { + USAGE_ERROR_ACTION(m, oldmem); + return 0; + } +#endif /* FOOTERS */ + if (!PREACTION(m)) { + mchunkptr newp = try_realloc_chunk(m, oldp, nb, 0); + POSTACTION(m); + if (newp == oldp) { + check_inuse_chunk(m, newp); + mem = oldmem; + } + } + } + } + return mem; +} + +void* mspace_memalign(mspace msp, size_t alignment, size_t bytes) { + mstate ms = (mstate)msp; + if (!ok_magic(ms)) { + USAGE_ERROR_ACTION(ms,ms); + return 0; + } + if (alignment <= MALLOC_ALIGNMENT) + return mspace_malloc(msp, bytes); + return internal_memalign(ms, alignment, bytes); +} + +void** mspace_independent_calloc(mspace msp, size_t n_elements, + size_t elem_size, void* chunks[]) { + size_t sz = elem_size; /* serves as 1-element array */ + mstate ms = (mstate)msp; + if (!ok_magic(ms)) { + USAGE_ERROR_ACTION(ms,ms); + return 0; + } + return ialloc(ms, n_elements, &sz, 3, chunks); +} + +void** mspace_independent_comalloc(mspace msp, size_t n_elements, + size_t sizes[], void* chunks[]) { + mstate ms = (mstate)msp; + if (!ok_magic(ms)) { + USAGE_ERROR_ACTION(ms,ms); + return 0; + } + return ialloc(ms, n_elements, sizes, 0, chunks); +} + +size_t mspace_bulk_free(mspace msp, void* array[], size_t nelem) { + return internal_bulk_free((mstate)msp, array, nelem); +} + +#if MALLOC_INSPECT_ALL +void mspace_inspect_all(mspace msp, + void(*handler)(void *start, + void *end, + size_t used_bytes, + void* callback_arg), + void* arg) { + mstate ms = (mstate)msp; + if (ok_magic(ms)) { + if (!PREACTION(ms)) { + internal_inspect_all(ms, handler, arg); + POSTACTION(ms); + } + } + else { + USAGE_ERROR_ACTION(ms,ms); + } +} +#endif /* MALLOC_INSPECT_ALL */ + +int mspace_trim(mspace msp, size_t pad) { + int result = 0; + mstate ms = (mstate)msp; + if (ok_magic(ms)) { + if (!PREACTION(ms)) { + result = sys_trim(ms, pad); + POSTACTION(ms); + } + } + else { + USAGE_ERROR_ACTION(ms,ms); + } + return result; +} + +#if !NO_MALLOC_STATS +void mspace_malloc_stats(mspace msp) { + mstate ms = (mstate)msp; + if (ok_magic(ms)) { + internal_malloc_stats(ms); + } + else { + USAGE_ERROR_ACTION(ms,ms); + } +} +#endif /* NO_MALLOC_STATS */ + +size_t mspace_footprint(mspace msp) { + size_t result = 0; + mstate ms = (mstate)msp; + if (ok_magic(ms)) { + result = ms->footprint; + } + else { + USAGE_ERROR_ACTION(ms,ms); + } + return result; +} + +size_t mspace_max_footprint(mspace msp) { + size_t result = 0; + mstate ms = (mstate)msp; + if (ok_magic(ms)) { + result = ms->max_footprint; + } + else { + USAGE_ERROR_ACTION(ms,ms); + } + return result; +} + +size_t mspace_footprint_limit(mspace msp) { + size_t result = 0; + mstate ms = (mstate)msp; + if (ok_magic(ms)) { + size_t maf = ms->footprint_limit; + result = (maf == 0) ? MAX_SIZE_T : maf; + } + else { + USAGE_ERROR_ACTION(ms,ms); + } + return result; +} + +size_t mspace_set_footprint_limit(mspace msp, size_t bytes) { + size_t result = 0; + mstate ms = (mstate)msp; + if (ok_magic(ms)) { + if (bytes == 0) + result = granularity_align(1); /* Use minimal size */ + if (bytes == MAX_SIZE_T) + result = 0; /* disable */ + else + result = granularity_align(bytes); + ms->footprint_limit = result; + } + else { + USAGE_ERROR_ACTION(ms,ms); + } + return result; +} + +#if !NO_MALLINFO +struct mallinfo mspace_mallinfo(mspace msp) { + mstate ms = (mstate)msp; + if (!ok_magic(ms)) { + USAGE_ERROR_ACTION(ms,ms); + } + return internal_mallinfo(ms); +} +#endif /* NO_MALLINFO */ + +size_t mspace_usable_size(const void* mem) { + if (mem != 0) { + mchunkptr p = mem2chunk(mem); + if (is_inuse(p)) + return chunksize(p) - overhead_for(p); + } + return 0; +} + +int mspace_mallopt(int param_number, int value) { + return change_mparam(param_number, value); +} + +#endif /* MSPACES */ + + +/* -------------------- Alternative MORECORE functions ------------------- */ + +/* + Guidelines for creating a custom version of MORECORE: + + * For best performance, MORECORE should allocate in multiples of pagesize. + * MORECORE may allocate more memory than requested. (Or even less, + but this will usually result in a malloc failure.) + * MORECORE must not allocate memory when given argument zero, but + instead return one past the end address of memory from previous + nonzero call. + * For best performance, consecutive calls to MORECORE with positive + arguments should return increasing addresses, indicating that + space has been contiguously extended. + * Even though consecutive calls to MORECORE need not return contiguous + addresses, it must be OK for malloc'ed chunks to span multiple + regions in those cases where they do happen to be contiguous. + * MORECORE need not handle negative arguments -- it may instead + just return MFAIL when given negative arguments. + Negative arguments are always multiples of pagesize. MORECORE + must not misinterpret negative args as large positive unsigned + args. You can suppress all such calls from even occurring by defining + MORECORE_CANNOT_TRIM, + + As an example alternative MORECORE, here is a custom allocator + kindly contributed for pre-OSX macOS. It uses virtually but not + necessarily physically contiguous non-paged memory (locked in, + present and won't get swapped out). You can use it by uncommenting + this section, adding some #includes, and setting up the appropriate + defines above: + + #define MORECORE osMoreCore + + There is also a shutdown routine that should somehow be called for + cleanup upon program exit. + + #define MAX_POOL_ENTRIES 100 + #define MINIMUM_MORECORE_SIZE (64 * 1024U) + static int next_os_pool; + void *our_os_pools[MAX_POOL_ENTRIES]; + + void *osMoreCore(int size) + { + void *ptr = 0; + static void *sbrk_top = 0; + + if (size > 0) + { + if (size < MINIMUM_MORECORE_SIZE) + size = MINIMUM_MORECORE_SIZE; + if (CurrentExecutionLevel() == kTaskLevel) + ptr = PoolAllocateResident(size + RM_PAGE_SIZE, 0); + if (ptr == 0) + { + return (void *) MFAIL; + } + // save ptrs so they can be freed during cleanup + our_os_pools[next_os_pool] = ptr; + next_os_pool++; + ptr = (void *) ((((size_t) ptr) + RM_PAGE_MASK) & ~RM_PAGE_MASK); + sbrk_top = (char *) ptr + size; + return ptr; + } + else if (size < 0) + { + // we don't currently support shrink behavior + return (void *) MFAIL; + } + else + { + return sbrk_top; + } + } + + // cleanup any allocated memory pools + // called as last thing before shutting down driver + + void osCleanupMem(void) + { + void **ptr; + + for (ptr = our_os_pools; ptr < &our_os_pools[MAX_POOL_ENTRIES]; ptr++) + if (*ptr) + { + PoolDeallocate(*ptr); + *ptr = 0; + } + } + +*/ + + +/* ----------------------------------------------------------------------- +History: + v2.8.6 Wed Aug 29 06:57:58 2012 Doug Lea + * fix bad comparison in dlposix_memalign + * don't reuse adjusted asize in sys_alloc + * add LOCK_AT_FORK -- thanks to Kirill Artamonov for the suggestion + * reduce compiler warnings -- thanks to all who reported/suggested these + + v2.8.5 Sun May 22 10:26:02 2011 Doug Lea (dl at gee) + * Always perform unlink checks unless INSECURE + * Add posix_memalign. + * Improve realloc to expand in more cases; expose realloc_in_place. + Thanks to Peter Buhr for the suggestion. + * Add footprint_limit, inspect_all, bulk_free. Thanks + to Barry Hayes and others for the suggestions. + * Internal refactorings to avoid calls while holding locks + * Use non-reentrant locks by default. Thanks to Roland McGrath + for the suggestion. + * Small fixes to mspace_destroy, reset_on_error. + * Various configuration extensions/changes. Thanks + to all who contributed these. + + V2.8.4a Thu Apr 28 14:39:43 2011 (dl at gee.cs.oswego.edu) + * Update Creative Commons URL + + V2.8.4 Wed May 27 09:56:23 2009 Doug Lea (dl at gee) + * Use zeros instead of prev foot for is_mmapped + * Add mspace_track_large_chunks; thanks to Jean Brouwers + * Fix set_inuse in internal_realloc; thanks to Jean Brouwers + * Fix insufficient sys_alloc padding when using 16byte alignment + * Fix bad error check in mspace_footprint + * Adaptations for ptmalloc; thanks to Wolfram Gloger. + * Reentrant spin locks; thanks to Earl Chew and others + * Win32 improvements; thanks to Niall Douglas and Earl Chew + * Add NO_SEGMENT_TRAVERSAL and MAX_RELEASE_CHECK_RATE options + * Extension hook in malloc_state + * Various small adjustments to reduce warnings on some compilers + * Various configuration extensions/changes for more platforms. Thanks + to all who contributed these. + + V2.8.3 Thu Sep 22 11:16:32 2005 Doug Lea (dl at gee) + * Add max_footprint functions + * Ensure all appropriate literals are size_t + * Fix conditional compilation problem for some #define settings + * Avoid concatenating segments with the one provided + in create_mspace_with_base + * Rename some variables to avoid compiler shadowing warnings + * Use explicit lock initialization. + * Better handling of sbrk interference. + * Simplify and fix segment insertion, trimming and mspace_destroy + * Reinstate REALLOC_ZERO_BYTES_FREES option from 2.7.x + * Thanks especially to Dennis Flanagan for help on these. + + V2.8.2 Sun Jun 12 16:01:10 2005 Doug Lea (dl at gee) + * Fix memalign brace error. + + V2.8.1 Wed Jun 8 16:11:46 2005 Doug Lea (dl at gee) + * Fix improper #endif nesting in C++ + * Add explicit casts needed for C++ + + V2.8.0 Mon May 30 14:09:02 2005 Doug Lea (dl at gee) + * Use trees for large bins + * Support mspaces + * Use segments to unify sbrk-based and mmap-based system allocation, + removing need for emulation on most platforms without sbrk. + * Default safety checks + * Optional footer checks. Thanks to William Robertson for the idea. + * Internal code refactoring + * Incorporate suggestions and platform-specific changes. + Thanks to Dennis Flanagan, Colin Plumb, Niall Douglas, + Aaron Bachmann, Emery Berger, and others. + * Speed up non-fastbin processing enough to remove fastbins. + * Remove useless cfree() to avoid conflicts with other apps. + * Remove internal memcpy, memset. Compilers handle builtins better. + * Remove some options that no one ever used and rename others. + + V2.7.2 Sat Aug 17 09:07:30 2002 Doug Lea (dl at gee) + * Fix malloc_state bitmap array misdeclaration + + V2.7.1 Thu Jul 25 10:58:03 2002 Doug Lea (dl at gee) + * Allow tuning of FIRST_SORTED_BIN_SIZE + * Use PTR_UINT as type for all ptr->int casts. Thanks to John Belmonte. + * Better detection and support for non-contiguousness of MORECORE. + Thanks to Andreas Mueller, Conal Walsh, and Wolfram Gloger + * Bypass most of malloc if no frees. Thanks To Emery Berger. + * Fix freeing of old top non-contiguous chunk im sysmalloc. + * Raised default trim and map thresholds to 256K. + * Fix mmap-related #defines. Thanks to Lubos Lunak. + * Fix copy macros; added LACKS_FCNTL_H. Thanks to Neal Walfield. + * Branch-free bin calculation + * Default trim and mmap thresholds now 256K. + + V2.7.0 Sun Mar 11 14:14:06 2001 Doug Lea (dl at gee) + * Introduce independent_comalloc and independent_calloc. + Thanks to Michael Pachos for motivation and help. + * Make optional .h file available + * Allow > 2GB requests on 32bit systems. + * new WIN32 sbrk, mmap, munmap, lock code from . + Thanks also to Andreas Mueller , + and Anonymous. + * Allow override of MALLOC_ALIGNMENT (Thanks to Ruud Waij for + helping test this.) + * memalign: check alignment arg + * realloc: don't try to shift chunks backwards, since this + leads to more fragmentation in some programs and doesn't + seem to help in any others. + * Collect all cases in malloc requiring system memory into sysmalloc + * Use mmap as backup to sbrk + * Place all internal state in malloc_state + * Introduce fastbins (although similar to 2.5.1) + * Many minor tunings and cosmetic improvements + * Introduce USE_PUBLIC_MALLOC_WRAPPERS, USE_MALLOC_LOCK + * Introduce MALLOC_FAILURE_ACTION, MORECORE_CONTIGUOUS + Thanks to Tony E. Bennett and others. + * Include errno.h to support default failure action. + + V2.6.6 Sun Dec 5 07:42:19 1999 Doug Lea (dl at gee) + * return null for negative arguments + * Added Several WIN32 cleanups from Martin C. Fong + * Add 'LACKS_SYS_PARAM_H' for those systems without 'sys/param.h' + (e.g. WIN32 platforms) + * Cleanup header file inclusion for WIN32 platforms + * Cleanup code to avoid Microsoft Visual C++ compiler complaints + * Add 'USE_DL_PREFIX' to quickly allow co-existence with existing + memory allocation routines + * Set 'malloc_getpagesize' for WIN32 platforms (needs more work) + * Use 'assert' rather than 'ASSERT' in WIN32 code to conform to + usage of 'assert' in non-WIN32 code + * Improve WIN32 'sbrk()' emulation's 'findRegion()' routine to + avoid infinite loop + * Always call 'fREe()' rather than 'free()' + + V2.6.5 Wed Jun 17 15:57:31 1998 Doug Lea (dl at gee) + * Fixed ordering problem with boundary-stamping + + V2.6.3 Sun May 19 08:17:58 1996 Doug Lea (dl at gee) + * Added pvalloc, as recommended by H.J. Liu + * Added 64bit pointer support mainly from Wolfram Gloger + * Added anonymously donated WIN32 sbrk emulation + * Malloc, calloc, getpagesize: add optimizations from Raymond Nijssen + * malloc_extend_top: fix mask error that caused wastage after + foreign sbrks + * Add linux mremap support code from HJ Liu + + V2.6.2 Tue Dec 5 06:52:55 1995 Doug Lea (dl at gee) + * Integrated most documentation with the code. + * Add support for mmap, with help from + Wolfram Gloger (Gloger@lrz.uni-muenchen.de). + * Use last_remainder in more cases. + * Pack bins using idea from colin@nyx10.cs.du.edu + * Use ordered bins instead of best-fit threshhold + * Eliminate block-local decls to simplify tracing and debugging. + * Support another case of realloc via move into top + * Fix error occuring when initial sbrk_base not word-aligned. + * Rely on page size for units instead of SBRK_UNIT to + avoid surprises about sbrk alignment conventions. + * Add mallinfo, mallopt. Thanks to Raymond Nijssen + (raymond@es.ele.tue.nl) for the suggestion. + * Add `pad' argument to malloc_trim and top_pad mallopt parameter. + * More precautions for cases where other routines call sbrk, + courtesy of Wolfram Gloger (Gloger@lrz.uni-muenchen.de). + * Added macros etc., allowing use in linux libc from + H.J. Lu (hjl@gnu.ai.mit.edu) + * Inverted this history list + + V2.6.1 Sat Dec 2 14:10:57 1995 Doug Lea (dl at gee) + * Re-tuned and fixed to behave more nicely with V2.6.0 changes. + * Removed all preallocation code since under current scheme + the work required to undo bad preallocations exceeds + the work saved in good cases for most test programs. + * No longer use return list or unconsolidated bins since + no scheme using them consistently outperforms those that don't + given above changes. + * Use best fit for very large chunks to prevent some worst-cases. + * Added some support for debugging + + V2.6.0 Sat Nov 4 07:05:23 1995 Doug Lea (dl at gee) + * Removed footers when chunks are in use. Thanks to + Paul Wilson (wilson@cs.texas.edu) for the suggestion. + + V2.5.4 Wed Nov 1 07:54:51 1995 Doug Lea (dl at gee) + * Added malloc_trim, with help from Wolfram Gloger + (wmglo@Dent.MED.Uni-Muenchen.DE). + + V2.5.3 Tue Apr 26 10:16:01 1994 Doug Lea (dl at g) + + V2.5.2 Tue Apr 5 16:20:40 1994 Doug Lea (dl at g) + * realloc: try to expand in both directions + * malloc: swap order of clean-bin strategy; + * realloc: only conditionally expand backwards + * Try not to scavenge used bins + * Use bin counts as a guide to preallocation + * Occasionally bin return list chunks in first scan + * Add a few optimizations from colin@nyx10.cs.du.edu + + V2.5.1 Sat Aug 14 15:40:43 1993 Doug Lea (dl at g) + * faster bin computation & slightly different binning + * merged all consolidations to one part of malloc proper + (eliminating old malloc_find_space & malloc_clean_bin) + * Scan 2 returns chunks (not just 1) + * Propagate failure in realloc if malloc returns 0 + * Add stuff to allow compilation on non-ANSI compilers + from kpv@research.att.com + + V2.5 Sat Aug 7 07:41:59 1993 Doug Lea (dl at g.oswego.edu) + * removed potential for odd address access in prev_chunk + * removed dependency on getpagesize.h + * misc cosmetics and a bit more internal documentation + * anticosmetics: mangled names in macros to evade debugger strangeness + * tested on sparc, hp-700, dec-mips, rs6000 + with gcc & native cc (hp, dec only) allowing + Detlefs & Zorn comparison study (in SIGPLAN Notices.) + + Trial version Fri Aug 28 13:14:29 1992 Doug Lea (dl at g.oswego.edu) + * Based loosely on libg++-1.2X malloc. (It retains some of the overall + structure of old version, but most details differ.) + +*/ diff --git a/biscuit/c/fault.c b/biscuit/c/fault.c new file mode 100644 index 0000000..cf3b2d9 --- /dev/null +++ b/biscuit/c/fault.c @@ -0,0 +1,19 @@ +#include + +int main(int argc, char **argv) +{ + int i; + for (i = 0; i < 3; i++) { + printf("hello world!\n"); + int j; + for (j = 0; j < 100000000; j++) + asm volatile("":::"memory"); + } + + printf("faulting!\n"); + + int *p = (int *)0; + *p = 0; + + return 0; +} diff --git a/biscuit/c/fault2.c b/biscuit/c/fault2.c new file mode 100644 index 0000000..b936c79 --- /dev/null +++ b/biscuit/c/fault2.c @@ -0,0 +1,29 @@ +#include + +int main(int argc, char **argv) +{ + if (open((const char *)0x12347, O_RDONLY, 0) >= 0 || errno != EFAULT) + errx(-1, "no fault?"); + errno = 0; + + if (write(1, (void *)0x7ffffff0, 10) >= 0 || errno != EFAULT) + errx(-1, "no fault?"); + errno = 0; + + // g0 stack + if (mkdir((const char *)0xffffffffffffffff, 0) == 0 || errno != EFAULT) + errx(-1, "no fault?"); + errno = 0; + + int fd; + if ((fd = open("/bigfile.txt", O_RDONLY, 0)) < 0) + err(fd, "open"); + if (errno != 0) + errx(-1, "errno changed on success?"); + // kernel text + if (read(fd, (void *)0x400040, 1024) >= 0 || errno != EFAULT) + errx(-1, "no fault?"); + + printf("success\n"); + return 0; +} diff --git a/biscuit/c/fcgi.c b/biscuit/c/fcgi.c new file mode 100644 index 0000000..79219b3 --- /dev/null +++ b/biscuit/c/fcgi.c @@ -0,0 +1,44 @@ +#include +#include +#include +#include +#include +#include + +int main(int argc, char **argv) +{ + if (fcntl(0, F_SETFL, O_NONBLOCK | fcntl(0, F_GETFL, 0)) == -1) + err(-1, "fcntl"); + + struct pollfd pfd = {fd: 0, events: POLLIN}; + if (poll(&pfd, 1, -1) != 1) + err(-1, "poll"); + + char buf[128]; + ssize_t did = 0, r; + while ((r = read(0, buf, sizeof(buf))) > 0) + did += r; + if (r != -1 || errno != EAGAIN) + err(-1, "read"); + if (did != 18) + fprintf(stderr, "unexpected GET len (%zd)\n", did); + + char msg[] = + "HTTP/1.1 200 OK\n" + "Server: fweb\n" + "Date: Tue, 06 Dec 2016 23:32:42 GMT\n" + "Content-Type: text/plain\n" + "Connection: close\n" + "\r\n\r\n" + "All work and no play make Jack a dull boy.\n" + "All work an no play make Jack a dull boy.\n" + "all work and no play make Jack a dull boy.\n" + "Allw ork and no play make Jack a dull boy.\n" + "All work and no play make jack a dull boy.\n" + "All work andd no play make Jack a dull boy.\n" + "All work and no play make Jack a dull boy.\n"; + size_t mlen = sizeof(msg) - 1; + if (write(1, msg, mlen) != mlen) + err(-1, "write"); + return 0; +} diff --git a/biscuit/c/fork.c b/biscuit/c/fork.c new file mode 100644 index 0000000..8418984 --- /dev/null +++ b/biscuit/c/fork.c @@ -0,0 +1,33 @@ +#include + +void child(int id) +{ + int i; + for (i = 0; i < 3; i++) { + printf("hello from %d\n", id); + } + exit(id); +} + +int main(int argc, char **argv) +{ + int pid = 0; + int id = 0; + while (id < 100) { + pid = fork(); + id++; + if (!pid) + child(id); + //if (wait(NULL) != pid) + // errx(-1, "wrong pid %d", pid); + if (wait4(pid, NULL, 0, NULL) != pid) + errx(-1, "wrong pid %d", pid); + if (wait4(pid - 1, NULL, 0, NULL) > 0) + errx(-1, "wait4 should fail"); + if (wait(NULL) > 0) + errx(-1, "wait should fail"); + } + + printf("parent done!\n"); + return 0; +} diff --git a/biscuit/c/fsbigwrite.c b/biscuit/c/fsbigwrite.c new file mode 100644 index 0000000..731ad16 --- /dev/null +++ b/biscuit/c/fsbigwrite.c @@ -0,0 +1,48 @@ +#include + +static char buf[5120]; + +void readprint(int fd) +{ + long ret; + if ((ret = read(fd, &buf, sizeof(buf))) < 0) { + err(ret, "read"); + exit(-1); + } + printf("FD %d read %ld bytes\n", fd, ret); + if (ret == sizeof(buf)) + ret = sizeof(buf) - 1; + buf[ret] = '\0'; + printf("FD %d returned: %s\n", fd, buf); +} + +int main(int argc, char **argv) +{ + int pid1 = fork(); + int pid2 = fork(); + + int fd; + if ((fd = open("/boot/uefi/readme.txt", O_RDWR, 0)) < 0) { + err(fd, "open"); + return -1; + } + + if (pid1 && pid2) { + int j; + for (j = 0; j < 100000000; j++) + asm volatile("":::"memory"); + readprint(fd); + return 0; + } + + int i; + for (i = 0; i < sizeof(buf); i++) + buf[i] = 0x41 + (i / 1000); + + int ret; + if ((ret = write(fd, buf, sizeof(buf))) != sizeof(buf)) { + err(ret, "write"); + return -1; + } + return 0; +} diff --git a/biscuit/c/fscreat.c b/biscuit/c/fscreat.c new file mode 100644 index 0000000..d179fbe --- /dev/null +++ b/biscuit/c/fscreat.c @@ -0,0 +1,43 @@ +#include + +static char buf[1024]; + +void readprint(int fd) +{ + long ret; + if ((ret = read(fd, &buf, sizeof(buf))) < 0) { + err(ret, "read"); + exit(-1); + } + printf("FD %d read %ld bytes\n", fd, ret); + if (ret == sizeof(buf)) + ret = sizeof(buf) - 1; + buf[ret] = '\0'; + printf("FD %d returned: %s\n", fd, buf); +} + +int main(int argc, char **argv) +{ + int pid = fork(); + int fd, ret; + + if (pid) { + wait(NULL); + if ((ret = open("/afile", O_RDONLY, 0)) < 0) + errx(ret, "open failed"); + fd = ret; + readprint(fd); + return 0; + } + + if ((ret = open("/afile", O_RDWR|O_CREAT, 0755)) < 0) + errx(ret, "open failed"); + fd = ret; + + snprintf(buf, sizeof(buf), "heeeey yeaaa yeaaa yea yea, heeeeey" + "yeaaaa, yeaaaa.... i say hey! whats going on!"); + if ((ret = write(fd, buf, strlen(buf))) != strlen(buf)) + errx(ret, "write failed"); + + return 0; +} diff --git a/biscuit/c/fsfree.c b/biscuit/c/fsfree.c new file mode 100644 index 0000000..cd7a9db --- /dev/null +++ b/biscuit/c/fsfree.c @@ -0,0 +1,74 @@ +#include + +static char buf[1024]; + +void fillbuf(char c) +{ + int i; + for (i = 0; i < sizeof(buf); i++) + buf[i] = c; +} + +void chk(int fd, char c) +{ + if (read(fd, buf, sizeof(buf)) != sizeof(buf)) + errx(-1, "short read"); + int i; + for (i = 0; i < sizeof(buf); i++) + if (buf[i] != c) + errx(-1, "byte mismatch"); +} + +int main(int argc, char **argv) +{ + int fd; + if ((fd = open("/newfile", O_RDWR|O_CREAT, 0755)) < 0) + errx(fd, "create failed"); + + fillbuf('A'); + if (write(fd, buf, sizeof(buf)) != sizeof(buf)) + errx(-1, "short write"); + + if (unlink("/newfile") != 0) + errx(-1, "should have succeeded"); + + printf("erased...\n"); + + if ((fd = open("/yetanother", O_RDWR|O_CREAT, 0755)) < 0) + errx(fd, "create failed"); + + int fd2; + if ((fd2 = open("/evenmore", O_RDWR|O_CREAT, 0755)) < 0) + errx(fd2, "create failed"); + + printf("filing two...\n"); + + fillbuf('B'); + if (write(fd, buf, sizeof(buf)) != sizeof(buf)) + errx(-1, "short write"); + + printf("first fill...\n"); + + fillbuf('C'); + if (write(fd2, buf, sizeof(buf)) != sizeof(buf)) + errx(-1, "short write"); + + if (close(fd) != 0) + errx(-1, "close"); + if (close(fd2) != 0) + errx(-1, "close"); + + printf("checking contents...\n"); + + if ((fd = open("/yetanother", O_RDONLY, 0)) < 0) + errx(fd, "open original failed"); + chk(fd, 'B'); + + if ((fd2 = open("/evenmore", O_RDONLY, 0755)) < 0) + errx(fd2, "create failed"); + chk(fd2, 'C'); + + printf("success\n"); + + return 0; +} diff --git a/biscuit/c/fslink.c b/biscuit/c/fslink.c new file mode 100644 index 0000000..73daa0c --- /dev/null +++ b/biscuit/c/fslink.c @@ -0,0 +1,43 @@ +#include + +static char buf[1024]; + +void readprint(int fd) +{ + long ret; + if ((ret = read(fd, &buf, sizeof(buf))) < 0) { + err(ret, "read"); + exit(-1); + } + printf("FD %d read %ld bytes\n", fd, ret); + if (ret == sizeof(buf)) + ret = sizeof(buf) - 1; + buf[ret] = '\0'; + printf("FD %d returned: %s\n", fd, buf); +} + +int main(int argc, char **argv) +{ + if (link("/biscuit", "/spin") >= 0) + errx(-1, "should have failed"); + + if (link("/boot/uefi/readme.txt", "/crap") != 0) + errx(-1, "should have suceeded"); + + int fd; + if ((fd = open("/crap", O_RDONLY, 0)) < 0) + errx(-1, "open failed"); + readprint(fd); + + if (link("/boot/uefi/readme.txt", "/boot/uefi/crap") != 0) + errx(-1, "should have suceeded"); + + if ((fd = open("/boot/uefi/crap", O_RDONLY, 0)) < 0) + errx(-1, "open failed"); + readprint(fd); + + if (link("/boot", "/dirhardlink") >= 0) + errx(-1, "dir link should fail"); + + return 0; +} diff --git a/biscuit/c/fsmkdir.c b/biscuit/c/fsmkdir.c new file mode 100644 index 0000000..2e1ee7d --- /dev/null +++ b/biscuit/c/fsmkdir.c @@ -0,0 +1,26 @@ +#include + +static char buf[1024]; + +int main(int argc, char **argv) +{ + int ret; + if ((ret = mkdir("/wtf", 0755)) < 0) + err(ret, "mkdir failed"); + + if ((ret = mkdir("/wtf/happyday", 0755)) < 0) + err(ret, "mkdir failed"); + + if ((ret = mkdir("/wtf/happyday/wut", 0755)) < 0) + err(ret, "mkdir failed"); + + if ((ret = open("/wtf/happyday/wut/afile", O_RDWR|O_CREAT, 0755)) < 0) + err(ret, "open failed"); + int fd = ret; + + snprintf(buf, sizeof(buf), "we fight for the user!"); + if ((ret = write(fd, buf, strlen(buf))) != strlen(buf)) + err(ret, "write failed"); + + return 0; +} diff --git a/biscuit/c/fstest.c b/biscuit/c/fstest.c new file mode 100644 index 0000000..139bf56 --- /dev/null +++ b/biscuit/c/fstest.c @@ -0,0 +1,54 @@ +#include + +static char buf[1024]; + +void readprint(int fd) +{ + long ret; + if ((ret = read(fd, &buf, sizeof(buf))) < 0) { + err(ret, "read"); + exit(-1); + } + printf("FD %d read %ld bytes\n", fd, ret); + if (ret == sizeof(buf)) + ret = sizeof(buf) - 1; + buf[ret] = '\0'; + printf("FD %d returned: %s\n", fd, buf); +} + +int main(int argc, char **argv) +{ + int ret; + if ((ret = open("/etc/passwd", O_RDONLY, 0)) >= 0) { + errx(-1, "open should fail"); + return -1; + } + if ((ret = open("/hi.txt", O_RDONLY, 0)) < 0) { + err(ret, "open"); + return -1; + } + int fd1 = ret; + if ((ret = open("/boot/uefi/readme.txt", O_RDONLY, 0)) < 0) { + err(ret, "open"); + return -1; + } + int fd2 = ret; + if ((ret = open("/clouseau.txt", O_RDONLY, 0)) < 0) { + err(ret, "open"); + return -1; + } + int fd3 = ret; + if ((ret = open("/boot/bsd", O_RDONLY, 0)) < 0) { + err(ret, "open"); + return -1; + } + int fd4 = ret; + + readprint(fd1); + readprint(fd2); + readprint(fd3); + readprint(fd4); + + printf("FS TESTS PASSED!\n"); + return 0; +} diff --git a/biscuit/c/fsunlink.c b/biscuit/c/fsunlink.c new file mode 100644 index 0000000..3c401b5 --- /dev/null +++ b/biscuit/c/fsunlink.c @@ -0,0 +1,53 @@ +#include + +static char buf[1024]; + +void readprint(int fd) +{ + long ret; + if ((ret = read(fd, &buf, sizeof(buf))) < 0) { + err(ret, "read"); + exit(-1); + } + printf("FD %d read %ld bytes\n", fd, ret); + if (ret == sizeof(buf)) + ret = sizeof(buf) - 1; + buf[ret] = '\0'; + printf("FD %d returned: %s\n", fd, buf); +} + +int main(int argc, char **argv) +{ + if (link("/boot/uefi/readme.txt", "/crap") != 0) + errx(-1, "should have succeeded"); + + int fd; + if ((fd = open("/crap", O_RDONLY, 0)) < 0) + errx(-1, "open failed"); + readprint(fd); + + if (unlink("/crap") != 0) + errx(-1, "should have succeeded"); + + if ((fd = open("/crap", O_RDONLY, 0)) >= 0) + errx(-1, "open of unlinked should have failed"); + + if ((fd = open("/boot/uefi/readme.txt", O_RDONLY, 0)) < 0) + errx(-1, "open original failed"); + readprint(fd); + + if (unlink("/boot/uefi/readme.txt") != 0) + errx(-1, "should have succeeded"); + + if (unlink("/another") >= 0) + errx(-1, "should have failed"); + + if (unlink("/another/you-found-me") != 0) + errx(-1, "should have succeeded"); + if (unlink("/another") != 0) + errx(-1, "should have succeeded"); + + printf("success\n"); + + return 0; +} diff --git a/biscuit/c/fswrite.c b/biscuit/c/fswrite.c new file mode 100644 index 0000000..f628f0c --- /dev/null +++ b/biscuit/c/fswrite.c @@ -0,0 +1,54 @@ +#include + +static char buf[1024]; + +void readprint(int fd) +{ + long ret; + if ((ret = read(fd, &buf, sizeof(buf))) < 0) { + err(ret, "read"); + } + printf("FD %d read %ld bytes\n", fd, ret); + if (ret == sizeof(buf)) + ret = sizeof(buf) - 1; + buf[ret] = '\0'; + printf("FD %d returned: %s\n", fd, buf); +} + +int main(int argc, char **argv) +{ + int pid1 = fork(); + int pid2 = fork(); + + int fd; + if ((fd = open("/boot/uefi/readme.txt", O_RDWR, 0)) < 0) { + err(fd, "open"); + return -1; + } + + if (pid1 && pid2) { + int j; + for (j = 0; j < 100000000; j++) + asm volatile("":::"memory"); + readprint(fd); + return 0; + } + + char msg[] = "hey yea yea yea yea yea ja ja ja ya ya ya, ho ho ha ha" + " ha! he ya ya ya ya ya ja ja ja ye ye ye, ho ho ho ho hoooooooo" + "ooooooo yo yo yoooooooooooo ya yay aaaaa yaaaaaa ya ya yaaaaa!" + " [%d %d]"; + snprintf(buf, sizeof(buf), msg, getpid(), 1); + int ret; + if ((ret = write(fd, buf, strlen(buf))) != strlen(buf)) { + err(ret, "write"); + return -1; + } + + snprintf(buf, sizeof(buf), msg, getpid(), 2); + if ((ret = write(fd, buf, strlen(buf))) != strlen(buf)) { + err(ret, "write"); + return -1; + } + return 0; +} diff --git a/biscuit/c/fweb.c b/biscuit/c/fweb.c new file mode 100644 index 0000000..cd50d8e --- /dev/null +++ b/biscuit/c/fweb.c @@ -0,0 +1,120 @@ +#include +#include +#include +#include +#include +#include + +static ulong nowus() +{ + struct timeval t; + if (gettimeofday(&t, NULL)) + errx(-1, "gettimeofday"); + return t.tv_sec * 1000000 + t.tv_usec; +} + +static void profdump(void) +{ + int fd = open("/pc.sh", O_RDONLY); + if (fd == -1) + err(-1, "open"); + if (dup2(fd, 0) == -1) + err(-1, "dup"); + close(fd); + char * const args[] = {"/bin/lsh", NULL}; + execv(args[0], args); + err(-1, "exec"); +} + +static void forkwait(void (*f)(void)) +{ + switch (fork()) { + case -1: + err(-1, "fork"); + case 0: + f(); + errx(-1, "child returned"); + } + + int status; + if (wait(&status) == -1) + err(-1, "wait"); + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) + printf("child failed\n"); +} + +static void req(int s) +{ + if (dup2(s, 0) == -1) + err(-1, "dup2"); + if (dup2(s, 1) == -1) + err(-1, "dup2"); + if (close(s) == -1) + err(-1, "close"); + + char *args[] = {"./bin/fcgi", NULL}; + execv(args[0], args); + err(-1, "execv"); +} + +int main(int argc, char **argv) +{ + int s = socket(AF_INET, SOCK_STREAM, 0); + if (s == -1) + err(-1, "socket"); + int prof = 1; + if (argc != 1) { + printf("profiling\n"); + prof = 0; + } + struct sockaddr_in sin; + int lport = 8080; + sin.sin_family = AF_INET; + sin.sin_port = htons(lport); + sin.sin_addr.s_addr = htonl(INADDR_ANY); + if (bind(s, (struct sockaddr *)&sin, sizeof(sin)) == -1) + err(-1, "bind"); + if (listen(s, 8) == -1) + err(-1, "listen"); + fprintf(stderr, "listen on %d\n", lport); + srand(time(NULL)); + ulong st = nowus(); + for (;;) { + if (prof) { + if (sys_prof(PROF_SAMPLE, PROF_EV_UNHALTED_CORE_CYCLES, + //PROF_EVF_OS | PROF_EVF_USR | PROF_EVF_BACKTRACE, + PROF_EVF_OS | PROF_EVF_USR, + 2800/2) == -1) + err(-1, "sysprof"); + } + socklen_t slen = sizeof(sin); + int fd = accept(s, (struct sockaddr *)&sin, &slen); + if (fd == -1) + err(-1, "accept"); + pid_t c = fork(); + if (c == -1) + err(-1, "fork"); + if (!c) + req(fd); + if (close(fd) == -1) + err(-1, "close"); + int status; + if (wait(&status) != c) + err(-1, "wait"); + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) + errx(-1, "child failed"); + if (prof) { + if (sys_prof(PROF_SAMPLE | PROF_DISABLE, 0, 0, 0) == -1) + err(-1, "sysprof"); + } + long end = nowus(); + long elap = end - st; + st = end; + if (elap >= 1000 && (rand() % 10000) < 1000) { + printf("GOT PROFILE for %ldus\n", elap); + printf("WAIT FOR DUMP...\n"); + forkwait(profdump); + st = nowus(); + } + } +} diff --git a/biscuit/c/getpid.c b/biscuit/c/getpid.c new file mode 100644 index 0000000..28ba3d4 --- /dev/null +++ b/biscuit/c/getpid.c @@ -0,0 +1,13 @@ +#include + +int main() +{ + int i; + //fork(); + for (i = 0; i < 30000; i++) { + int pid = getpid(); + printf("my pid is %d.\n", pid); + } + + return 0; +} diff --git a/biscuit/c/goodcit.c b/biscuit/c/goodcit.c new file mode 100644 index 0000000..dcffeff --- /dev/null +++ b/biscuit/c/goodcit.c @@ -0,0 +1,47 @@ +#include + +void cmb(void) +{ + if (mkdir("/mb", 0700) == -1) + err(-1, "mkdir"); + if (chdir("/mb") == -1) + err(-1, "chdir"); + char * const args[] = {"/bin/cmailbench", "-d", "1", "./", "1", NULL}; + execv(args[0], args); + err(-1, "exec"); +} + +void forkwait(void (*f)(void)) +{ + switch (fork()) { + case -1: + err(-1, "fork"); + case 0: + f(); + errx(-1, "child returned"); + } + + int status; + if (wait(&status) == -1) + err(-1, "wait"); + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) + errx(-1, "child failed"); +} + +void cleanup(void) +{ + char * const args[] = {"/bin/rmtree", "/mb", NULL}; + execv(args[0], args); + err(-1, "exec"); +} + +int main(int argc, char **argv) +{ + for (;;) { + forkwait(cmb); + forkwait(cleanup); + sleep(3); + } + + return 0; +} diff --git a/biscuit/c/halp.c b/biscuit/c/halp.c new file mode 100644 index 0000000..a3a2515 --- /dev/null +++ b/biscuit/c/halp.c @@ -0,0 +1,322 @@ +#include + +static void gcrun(char * const *cmd, long *_ngcs, long *_xput, double *_gcfrac) +{ + int p[2]; + if (pipe(p) == -1) + err(-1, "pipe"); + pid_t c = fork(); + if (c == -1) + err(-1, "fork"); + if (!c) { + close(p[0]); + if (dup2(p[1], 1) == -1) + err(-1, "dup2"); + close(p[1]); + + const int cmdsz = 62; + char *cmds[62+2] = {"time", "-g"}; + int i; + + for (i = 0; i < cmdsz; i++) { + cmds[2+i] = cmd[i]; + if (cmd[i] == NULL) + break; + } + execvp(cmds[0], cmds); + err(-1, "exec"); + } + close(p[1]); + + long ngcs = -1; + long xput = 0; + double gcfrac = 0; + + char buf[512]; + ssize_t r; + int off = 0; + while ((r = read(p[0], &buf[off], sizeof(buf) - off - 1)) > 0) { + char *end = &buf[off+r]; + *end = '\0'; + if (strchr(buf, '\n') == NULL) { + fprintf(stderr, "warning: ignoring long line\n"); + off = 0; + continue; + } + char *nl, *last = buf; + while ((nl = strchr(last, '\n')) != NULL) { + *nl = '\0'; + sscanf(last, "GCs: %ld", &ngcs); + sscanf(last, "GC CPU frac: %lf", &gcfrac); + sscanf(last, "\tops: %ld", &xput); + last = nl + 1; + } + off = end - last; + memmove(buf, last, off); + } + if (r == -1) + err(-1, "read"); + + int status; + if (wait(&status) == -1) + err(-1, "wait"); + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) + errx(-1, "child failed"); + close(p[0]); + + if (_ngcs) + *_ngcs = ngcs; + if (_gcfrac) + *_gcfrac = gcfrac; + if (_xput) + *_xput = xput; +} + +static void _run(char **cmd) +{ + pid_t c = fork(); + if (c == -1) + err(-1, "fork"); + if (!c) { + int fd = open("/dev/null", O_WRONLY); + if (fd == -1) + err(-1, "open"); + if (dup2(fd, 1) == -1) + err(-1, "dup2"); + close(fd); + execvp(cmd[0], cmd); + err(-1, "execvp"); + } + int status; + if (wait(&status) == -1) + err(-1, "wait"); + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) + errx(-1, "child failed"); +} + +// format the benchmark command given the parameters. only the most recent +// command returned is valid. +static void _mkbmcmd(char **cmd, size_t ncmd, long allocr, long duration) +{ + if (ncmd < 6) + errx(-1, "little cmd buf"); + // duration in seconds + static char dbuf[32]; + snprintf(dbuf, sizeof(dbuf), "-s%ld", duration); + static char abuf[32]; + snprintf(abuf, sizeof(abuf), "-A%ld", allocr); + cmd[0] = "sfork"; + cmd[1] = dbuf; + cmd[2] = "-ba"; + cmd[3] = abuf; + cmd[4] = "1"; + cmd[5] = NULL; +} + +#define GOGC 100 + +// find the xput for a run of benchmark at a particular allocation rate +__attribute__((unused)) +static long nogcxput(long allocr) +{ + errx(-1, "do not use; taints GC costs for smaller heaps"); + // set kernel heap size to ~16GB to avoid any gcs + char *hcmd[] = {"bmgc", "-h", "16000", NULL}; + char *rcmd2[] = {"bmgc", "-g", NULL}; + _run(hcmd); + _run(rcmd2); + + const int ncmd = 10; + char *cmd[ncmd]; + _mkbmcmd(cmd, ncmd, allocr, 10); + long ngcs, xput; + int i; + for (i = 0; i < 10; i++) { + gcrun(cmd, &ngcs, &xput, NULL); + if (ngcs == 0) + break; + xput = 0; + } + if (!xput) + errx(-1, "failed to get 0 gc xput for allocrate %ld", allocr); + + // restore old total heap size + char *rcmd1[] = {"bmgc", "-H", "100", NULL}; + _run(rcmd1); + _run(rcmd2); + + return xput; +} + +// for a given gc cpu fraction upperbound and allocation rate, find total heap +// sizing to keep gc cpu time < that gc cput fraction upperbound. returns the +// total heap size in GOGC terms (all heap sizes are in GOGC terms). +static int findtotalheapsz(double gcfracub, long allocr, const long targetgcs, + const long gc0xput) +{ + // first, get the xput of this allocation rate with 0 gcs. we use that + // xput to calculate GC CPU time. + //long gc0xput = nogcxput(allocr); + printf("0gc xput is %ld\n", gc0xput); + + // initialize binary search bounds +#define HIGC 700 + int higc = HIGC; + int logc = 10; + // minimum heap size increment +#define GCINC 10 + const int gcinc = GCINC; +#define TRIEDSZ ((HIGC/GCINC)+1) + // byte array tracking whether we tried a certain heap size yet. when + // we find two adjacents heap sizes that have been tried but had + // different outcomes (i.e. gc fraction was higher than upper bound for + // one and lower than upperbound for the other), we have found the + // target heap size. + const char _nottried = 0; + // this slot resulted in gc frac lower than upperbound + const char _lower = -1; + // this slot resulted in gc frac higher than upperbound + const char _higher = 1; + char tried[TRIEDSZ] = {0}; + + long duration = 10; + char lastres = _nottried; + while (1) { + int curgc = (higc + logc) / 2; + // round to multiple of incgc + curgc = (curgc/gcinc)*gcinc; + int curslot = curgc / gcinc; + // make sure to round to final slot + if (tried[curslot] != _nottried) { + if (lastres == _lower) + curgc -= gcinc; + else if (lastres == _higher) + curgc += gcinc; + else + errx(-1, "bad lastres: %d", lastres); + curslot = curgc/gcinc; + if (curslot < 0 || curslot >= TRIEDSZ) + errx(-1, "cannot meet goal gc cpu fraction"); + if (tried[curslot] != _nottried) + errx(-1, "rounded, but already tried?"); + } + printf("=== trying GOGC heap size of %d ===\n", curgc); + char heapszbuf[32]; + snprintf(heapszbuf, sizeof(heapszbuf), "%d", curgc); + char *resizecmds[] = {"bmgc", "-H", heapszbuf, NULL}; + _run(resizecmds); + + // run the benchmark for increasing durations of time until we + // have at least 20 gcs. + long foundxput; + while (1) { + const int ncmds = 32; + char *cmds[32]; + _mkbmcmd(cmds, ncmds, allocr, duration); + long xput, ngcs; + printf("trying allocr %ld with heap %d for %ld " + "seconds...\n", allocr, curgc, duration); + gcrun(cmds, &ngcs, &xput, NULL); + if (ngcs >= targetgcs) { + printf("good. got %ld gcs\n", ngcs); + foundxput = xput; + // prevent duration from growing too large too + // quickly + if (ngcs / targetgcs > 1) + duration /= ngcs / targetgcs; + break; + } + printf("only %ld gcs, trying again...\n", ngcs); + double gcps = (double)ngcs/duration; + if (gcps == 0) + duration = 120; + else + duration = targetgcs/gcps + (duration/8); + + char *prep[] = {"bmgc", "-g", NULL}; + _run(prep); + } + + // finally have a run with >20 gcs. calculate gc cpu frac. + double gcfrac = 1.0 - (double)foundxput/gc0xput; + if (gcfrac <= gcfracub) + lastres = _lower; + else + lastres = _higher; + tried[curslot] = lastres; + + // adjust binary search bounds and duration + int lastgc = curgc; + if (lastres == _lower) + higc = curgc; + else + logc = curgc; + duration *= (double)curgc/lastgc; + printf(" GC frac: %f\n", gcfrac); + printf(" adjust heap size %s\n", + lastres == _lower ? "SMALLER" : "BIGGER"); + + // see if we are done + int i; + for (i = 1; i < TRIEDSZ; i++) { + if (tried[i-1] == _nottried || tried[i] == _nottried) + continue; + if (tried[i-1] != tried[i]) { + printf("FOUND\n"); + int low = (i-1)*gcinc; + int hi = i*gcinc; + char st = tried[i-1]; + printf(" heap %d: %s\n", low, + st == _lower ? "SMALLER" : "BIGGER"); + st = tried[i]; + printf(" heap %d: %s\n", hi, + st == _lower ? "SMALLER" : "BIGGER"); + return hi; + } + } + } +} + +__attribute__((unused)) +static void usage() +{ + fprintf(stderr, "usage: %s [-n target gcs] [-c target gc frac]" + " -x <0gc xput> \n", __progname); + exit(-1); +} + +int main(int argc, char **argv) +{ + long targetgcs = 20; + double gctarget = 0.055; + long gc0x = 0; + int c; + while ((c = getopt(argc, argv, "x:n:mc:")) != -1) { + switch (c) { + case 'c': + gctarget = strtod(optarg, NULL); + if (gctarget < 0 || gctarget > 100.0) + gctarget = 0.055; + break; + case 'n': + targetgcs = strtol(optarg, NULL, 0); + if (targetgcs < 0) + targetgcs = 20; + break; + case 'x': + gc0x = strtol(optarg, NULL, 0); + break; + default: + usage(); + break; + } + } + if (argc - optind != 1 || gc0x <= 0) + usage(); + long allocr = strtol(argv[optind], NULL, 0); + if (allocr < 0) + allocr = 32; + long idealheap = findtotalheapsz(gctarget, allocr, targetgcs, gc0x); + printf("ideal heap for allocr %ld: %ld\n", allocr, idealheap); + return 0; +} diff --git a/biscuit/c/head.c b/biscuit/c/head.c new file mode 100644 index 0000000..0d6b0ea --- /dev/null +++ b/biscuit/c/head.c @@ -0,0 +1,31 @@ +#include +#include +#include +#include +#include +#include + + +#define BSIZE 4096 +char buf[BSIZE]; + +int main(int argc, char *argv[]) +{ + if (argc != 3) { + printf("Usage: %s filename n\n", argv[0]); + exit(-1); + } + int n = atoi(argv[2]); + + FILE *f = fopen(argv[1], "r"); + if (f == NULL) + err(-1, "fopen"); + size_t r; + if ((r = fread(buf, 1, n, f)) > 0) { + for (int i = 0; i < r; i++) { + printf("%c", buf[i]); + } + } + printf("\n"); + return 0; +} diff --git a/biscuit/c/hello.c b/biscuit/c/hello.c new file mode 100644 index 0000000..1a84815 --- /dev/null +++ b/biscuit/c/hello.c @@ -0,0 +1,14 @@ +#include + +int main(int argc, char **argv) +{ + int i; + for (i = 0; i < 3; i++) { + printf("hello world!\n"); + int j; + for (j = 0; j < 100000000; j++) + asm volatile("":::"memory"); + } + + return 0; +} diff --git a/biscuit/c/include/arpa/inet.h b/biscuit/c/include/arpa/inet.h new file mode 100644 index 0000000..1681924 --- /dev/null +++ b/biscuit/c/include/arpa/inet.h @@ -0,0 +1,3 @@ +#pragma once + +#include diff --git a/biscuit/c/include/assert.h b/biscuit/c/include/assert.h new file mode 100644 index 0000000..563080c --- /dev/null +++ b/biscuit/c/include/assert.h @@ -0,0 +1,17 @@ +// sadly, assert.h must be its own file so that NDEBUG works +#pragma once + +#include + +#ifdef NDEBUG +#define assert(ignore) ((void) 0) +#else +#define assert(x) __assert(x, __FILE__, __LINE__) +#define __assert(x, y, z) \ +do { \ + if (!(x)) { \ + fprintf(stderr, "assertion failed (" #x ") at %s:%d\n", y, z);\ + abort();\ + } \ +} while (0) +#endif diff --git a/biscuit/c/include/ctype.h b/biscuit/c/include/ctype.h new file mode 100644 index 0000000..1681924 --- /dev/null +++ b/biscuit/c/include/ctype.h @@ -0,0 +1,3 @@ +#pragma once + +#include diff --git a/biscuit/c/include/dirent.h b/biscuit/c/include/dirent.h new file mode 100644 index 0000000..1681924 --- /dev/null +++ b/biscuit/c/include/dirent.h @@ -0,0 +1,3 @@ +#pragma once + +#include diff --git a/biscuit/c/include/err.h b/biscuit/c/include/err.h new file mode 100644 index 0000000..1681924 --- /dev/null +++ b/biscuit/c/include/err.h @@ -0,0 +1,3 @@ +#pragma once + +#include diff --git a/biscuit/c/include/errno.h b/biscuit/c/include/errno.h new file mode 100644 index 0000000..1681924 --- /dev/null +++ b/biscuit/c/include/errno.h @@ -0,0 +1,3 @@ +#pragma once + +#include diff --git a/biscuit/c/include/fcntl.h b/biscuit/c/include/fcntl.h new file mode 100644 index 0000000..1681924 --- /dev/null +++ b/biscuit/c/include/fcntl.h @@ -0,0 +1,3 @@ +#pragma once + +#include diff --git a/biscuit/c/include/glob.h b/biscuit/c/include/glob.h new file mode 100644 index 0000000..1681924 --- /dev/null +++ b/biscuit/c/include/glob.h @@ -0,0 +1,3 @@ +#pragma once + +#include diff --git a/biscuit/c/include/grp.h b/biscuit/c/include/grp.h new file mode 100644 index 0000000..1681924 --- /dev/null +++ b/biscuit/c/include/grp.h @@ -0,0 +1,3 @@ +#pragma once + +#include diff --git a/biscuit/c/include/inttypes.h b/biscuit/c/include/inttypes.h new file mode 100644 index 0000000..1681924 --- /dev/null +++ b/biscuit/c/include/inttypes.h @@ -0,0 +1,3 @@ +#pragma once + +#include diff --git a/biscuit/c/include/limits.h b/biscuit/c/include/limits.h new file mode 100644 index 0000000..1681924 --- /dev/null +++ b/biscuit/c/include/limits.h @@ -0,0 +1,3 @@ +#pragma once + +#include diff --git a/biscuit/c/include/litc.h b/biscuit/c/include/litc.h new file mode 100644 index 0000000..170fc6c --- /dev/null +++ b/biscuit/c/include/litc.h @@ -0,0 +1,1008 @@ +#pragma once + +#define BISCUIT_RELEASE "REARDEN" +#define BISCUIT_VERSION "0.0.0" + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define STDIN_FILENO 0 +#define STDOUT_FILENO 1 +#define STDERR_FILENO 2 +#define EOF (-1) + +#define ERRNO_FIRST 1 +#define EPERM 1 +#define ENOENT 2 +#define ESRCH 3 +#define EINTR 4 +#define EIO 5 +#define E2BIG 7 +#define EBADF 9 +#define ECHILD 10 +#define EAGAIN 11 +#define EWOULDBLOCK EAGAIN +#define ENOMEM 12 +#define EACCES 13 +#define EFAULT 14 +#define EBUSY 16 +#define EEXIST 17 +#define EXDEV 18 +#define ENODEV 19 +#define ENOTDIR 20 +#define EISDIR 21 +#define EINVAL 22 +#define ENFILE 23 +#define EMFILE 24 +#define ENOSPC 28 +#define ESPIPE 29 +#define EPIPE 32 +#define ERANGE 34 +#define ENAMETOOLONG 36 +#define ENOSYS 38 +#define ENOTEMPTY 39 +#define EDESTADDRREQ 40 +#define EAFNOSUPPORT 47 +#define EADDRINUSE 48 +#define EADDRNOTAVAIL 49 +#define ENETDOWN 50 +#define ENETUNREACH 51 +#define ECONNABORTED 53 +#define ELOOP 62 +#define EHOSTDOWN 64 +#define EHOSTUNREACH 65 +#define EOVERFLOW 75 +#define ENOTSOCK 88 +#define EOPNOTSUPP 95 +#define ECONNRESET 104 +#define EISCONN 106 +#define ENOTCONN 107 +#define ETIMEDOUT 110 +#define ECONNREFUSED 111 +#define EINPROGRESS 115 +#define ERRNO_LAST 115 + +#define MAP_FAILED ((void *)(long) -1) + +#define MAP_SHARED 0x01 +#define MAP_PRIVATE 0x02 +#define MAP_ANON 0x20 +#define MAP_ANONYMOUS MAP_ANON + +#define PROT_NONE 0x0 +#define PROT_READ 0x1 +#define PROT_WRITE 0x2 +#define PROT_EXEC 0x4 + +#define FORK_PROCESS 0x1 +#define FORK_THREAD 0x2 + +#define MAXBUF 4096 +#define PIPE_BUF 4096 + +#define MAJOR(x) ((long)((ulong)x >> 40)) +#define MINOR(x) ((long)(((ulong)x >> 32) & 0xff)) +#define MKDEV(x, y) ((dev_t)((ulong)x << 40 | (ulong)y << 32)) + +/* + * system calls + */ +#define FD_WORDS 16 +// bits +#define FD_SETSIZE (FD_WORDS*8*8) + +typedef struct { + ulong mask[FD_WORDS]; +} fd_set; + +#define FD_ZERO(ft) memset((ft), 0, sizeof(fd_set)) +#define FD_SET(n, ft) (ft)->mask[n/64] |= 1ull << (n%64) +#define FD_CLR(n, ft) (ft)->mask[n/64] &= ~(1ull << (n%64)) +#define FD_ISSET(n, ft) (((ft)->mask[n/64] & (1ull << (n%64))) != 0) + +struct iovec { + void *iov_base; + size_t iov_len; +}; + +struct pollfd { + int fd; +#define POLLRDNORM 0x1 +#define POLLRDBAND 0x2 +#define POLLIN (POLLRDNORM | POLLRDBAND) +#define POLLPRI 0x4 +#define POLLWRNORM 0x8 +#define POLLOUT POLLWRNORM +#define POLLWRBAND 0x10 +// revents only, whose status is always reported +#define POLLERR 0x20 +#define POLLHUP 0x40 +#define POLLNVAL 0x80 + ushort events; + ushort revents; +}; + +struct timeval { + time_t tv_sec; + time_t tv_usec; +}; + +struct tm { + int tm_sec; + int tm_min; + int tm_hour; + int tm_mday; + int tm_mon; + int tm_year; + int tm_wday; + int tm_yday; + int tm_isdst; +}; + +struct timezone { +}; + +struct timespec { + time_t tv_sec; + long tv_nsec; +}; + +extern long timezone; + +struct rlimit { + rlim_t rlim_cur; + rlim_t rlim_max; +}; + +struct rusage { + struct timeval ru_utime; + struct timeval ru_stime; +}; + +union sigval { + int sival_int; + void *sival_ptr; +}; + +typedef struct { + int si_signo; + int si_code; + int si_errno; + pid_t si_pid; + uid_t si_uid; + void *si_addr; + int si_status; + long si_band; + union sigval si_value; +} siginfo_t; + +struct sigaction { + void (*sa_handler)(int); + void (*sa_sigaction)(int, siginfo_t *, void *); + sigset_t sa_mask; +#define sigemptyset(ss) (*ss = 0) +#define sigfillset(ss) (*ss = -1) +#define sigaddset(ss, s) (*ss |= (1ull << s)) +#define sigdelset(ss, s) (*ss &= ~(1ull << s)) +#define sigismember(ss, s) (*ss & (1ull << s)) + int sa_flags; +#define SA_SIGINFO 1 +}; + +struct sockaddr { + uchar sa_len; + uchar sa_family; + char sa_data[]; +}; + +struct sockaddr_un { + uchar sun_len; + uchar sun_family; + char sun_path[104]; +}; + +#define SUN_LEN(x) (sizeof(struct sockaddr_un)) + +typedef uint32_t in_addr_t; +typedef uint16_t in_port_t; + +struct sockaddr_in { + uchar sin_len; + uchar sin_family; + in_port_t sin_port; + struct { + in_addr_t s_addr; + } sin_addr; +}; + +struct sockaddr_storage { + uchar ss_len; + uchar ss_family; + char ss_data[sizeof(struct sockaddr_un) - 2]; +}; + +#define INADDR_ANY ((uint32_t)0) + +struct stat { + dev_t st_dev; + ino_t st_ino; + mode_t st_mode; + off_t st_size; + dev_t st_rdev; + uid_t st_uid; + blkcnt_t st_blocks; + time_t st_mtime; + ulong st_mtimensec; +}; + +#define S_IFMT (0xffff0000ul) +#define S_IFREG (1ul << 16) +#define S_IFDIR (2ul << 16) +#define S_IFIFO (3ul << 16) +#define S_IFLNK (4ul << 16) +#define S_IFBLK (5ul << 16) + +// XXX cleanup modes/dev majors +#define S_ISDIR(mode) ((mode & S_IFMT) == S_IFDIR) +#define S_ISDEV(mode) (MAJOR(mode) != 0) +#define S_ISFIFO(mode) ((mode & S_IFMT) == S_IFIFO) +#define S_ISREG(mode) ((mode & S_IFMT) == S_IFREG) +#define S_ISSOCK(mode) (MAJOR(mode) == 2) +#define S_ISLNK(mode) ((mode & S_IFMT) == S_IFLNK) +#define S_ISBLK(mode) (MAJOR(mode) == S_IFBLK) + +// please never use these +#define S_ISUID (04000) +#define S_ISGID (02000) +#define S_IRWXU (00700) +#define S_IRUSR (00400) +#define S_IWUSR (00200) +#define S_IXUSR (00100) +#define S_IRWXG (00070) +#define S_IRGRP (00040) +#define S_IWGRP (00020) +#define S_IXGRP (00010) +#define S_IRWXO (00007) +#define S_IROTH (00004) +#define S_IWOTH (00002) +#define S_IXOTH (00001) + +struct tfork_t { + void *tf_tcb; + // tf_tid is merely a convenient way for a new thread to learn its tid. + void *tf_tid; + void *tf_stack; +}; + +int accept(int, struct sockaddr *, socklen_t*); +// access(2) cannot be a wrapper around stat(2) because access(2) uses real-id +// instead of effective-id +int access(const char *, int); +#define R_OK (1 << 0) +#define W_OK (1 << 1) +#define X_OK (1 << 2) +int bind(int, const struct sockaddr *, socklen_t); +int connect(int, const struct sockaddr *, socklen_t); +int chmod(const char *, mode_t); +int close(int); +int chdir(const char *); +int dup(int); +int dup2(int, int); +void _exit(int) + __attribute__((noreturn)); +int execv(const char *, char * const[]); +int execve(const char *, char * const[], char * const[]); +int execvp(const char *, char * const[]); +pid_t fork(void); +int fstat(int, struct stat *); +int ftruncate(int, off_t); +int futex(const int, void *, void *, int, const struct timespec *); +#define FUTEX_SLEEP 1 +#define FUTEX_WAKE 2 +#define FUTEX_CNDGIVE 3 + +char *getcwd(char *, size_t); +pid_t getpid(void); +pid_t getppid(void); + +int getrlimit(int, struct rlimit *); +#define RLIMIT_NOFILE 1 +#define RLIMIT_CORE 2 +#define RLIM_INFINITY ULONG_MAX +int getrusage(int, struct rusage *); +#define RUSAGE_SELF 1 +#define RUSAGE_CHILDREN 2 +int getsockopt(int, int, int, void *, socklen_t *); +#define SHUT_WR (1 << 0) +#define SHUT_RD (1 << 1) +int shutdown(int, int); +int gettimeofday(struct timeval *, struct timezone *); +long gettid(void); + +int fcntl(int, int, ...); +#define F_GETFL 1 +#define F_SETFL 2 +#define F_GETFD 3 +#define F_SETFD 4 +#define F_SETLK 5 +#define F_SETLKW 6 +#define F_SETOWN 7 + +#define FD_CLOEXEC 0x4 + +int kill(int, int); +int link(const char *, const char *); +int listen(int, int); +off_t lseek(int, off_t, int); +#define SEEK_SET 1 +#define SEEK_CUR 2 +#define SEEK_END 4 + +int mkdir(const char *, long); +int mknod(const char *, mode_t, dev_t); +void *mmap(void *, size_t, int, int, int, long); +int munmap(void *, size_t); +int nanosleep(const struct timespec *, struct timespec *); +int open(const char *, int, ...); +#define O_RDONLY 0 +#define O_WRONLY 1 +#define O_RDWR 2 +#define O_CREAT 0x40 +#define O_EXCL 0x80 +#define O_TRUNC 0x200 +#define O_APPEND 0x400 +#define O_NONBLOCK 0x800 +#define O_DIRECTORY 0x10000 +#define O_CLOEXEC 0x80000 + +int pause(void); +int pipe(int[2]); +int pipe2(int[2], int); +int poll(struct pollfd *, nfds_t, int); +ssize_t pread(int, void *, size_t, off_t); +ssize_t pwrite(int, const void *, size_t, off_t); +ssize_t read(int, void*, size_t); +ssize_t readv(int, const struct iovec *, int); +int reboot(void); +ssize_t recv(int, void *, size_t, int); +ssize_t recvfrom(int, void *, size_t, int, struct sockaddr *, socklen_t *); + +struct msghdr { + void *msg_name; + socklen_t msg_namelen; + struct iovec *msg_iov; + int msg_iovlen; + uint msg_flags; + void *msg_control; + socklen_t msg_controllen; +}; + +struct cmsghdr { + socklen_t cmsg_len; + int cmsg_level; + int cmsg_type; +#define SCM_RIGHTS 1 + char _data[]; +}; + +#define MSG_TRUNC (1 << 0) +#define MSG_CTRUNC (1 << 1) + +#define CMSG_FIRSTHDR(m) \ + (((m)->msg_controllen > 0) ? (struct cmsghdr *)(m)->msg_control : NULL) +#define CMSG_NXTHDR(m, c) \ + (((char *)c + c->cmsg_len == \ + (char *)(m)->msg_control + (m)->msg_controllen) ? \ + NULL : (struct cmsghdr *)((char *)c + c->cmsg_len)) +#define CMSG_DATA(c) ((uchar *)&(c)->_data) +#define ROUND2(x, n) ((x + (n - 1)) & ~(n - 1)) +#define CMSG_LEN(x) (ROUND2(sizeof(struct cmsghdr) + x, 8ul)) +#define CMSG_SPACE(x) CMSG_LEN(x) + +ssize_t recvmsg(int, struct msghdr *, int); +int rename(const char *, const char *); +int rmdir(const char *); +int select(int, fd_set*, fd_set*, fd_set*, struct timeval *); +ssize_t send(int, const void *, size_t, int); +ssize_t sendto(int, const void *, size_t, int, const struct sockaddr *, + socklen_t); +ssize_t sendmsg(int, struct msghdr *, int); +int setrlimit(int, const struct rlimit *); +pid_t setsid(void); +// levels +#define SOL_SOCKET 1 +#define IPPROTO_TCP 2 +// socket options +#define SO_SNDBUF 1 +#define SO_SNDTIMEO 2 +#define SO_ERROR 3 +#define SO_TYPE 4 +#define SO_RCVBUF 5 +#define SO_REUSEADDR 6 +#define SO_KEEPALIVE 7 +#define SO_LINGER 8 +// not supported on linux... +#define SO_SNDLOWAT 9 +#define SO_NAME 10 +#define SO_PEER 11 +struct linger { + int l_onoff; + int l_linger; +}; +// TCP options +#define TCP_NODELAY 20 +int sigaction(int, const struct sigaction *, struct sigaction *); +#define SIGHUP 1 +#define SIGINT 2 +#define SIGQUIT 3 +#define SIGILL 4 +#define SIGKILL 9 +#define SIGUSR1 10 +#define SIGSEGV 11 +#define SIGSYS 12 +#define SIGPIPE 13 +#define SIGALRM 14 +#define SIGTERM 15 +#define SIGSTOP 17 +#define SIGCHLD 20 +#define SIGIO 23 +#define SIGWINCH 28 +#define SIGUSR2 31 +void (*signal(int, void (*)(int)))(int); +#define SIG_DFL ((void (*)(int))1) +#define SIG_IGN ((void (*)(int))2) +#define SIG_BLOCK 1 +#define SIG_SETMASK 2 +#define SIG_UNBLOCK 3 +int socket(int, int, int); +#define AF_UNIX 1 +#define AF_LOCAL AF_UNIX +#define AF_INET 2 + +#define SOCK_STREAM (1 << 0) +#define SOCK_DGRAM (1 << 1) +#define SOCK_RAW (1 << 2) +#define SOCK_SEQPACKET (1 << 3) +#define SOCK_CLOEXEC (1 << 4) +#define SOCK_NONBLOCK (1 << 5) + +int stat(const char *, struct stat *); +int sync(void); +long sys_prof(long, long, long, long); +#define PROF_DISABLE (1ul << 0) +#define PROF_GOLANG (1ul << 1) +#define PROF_SAMPLE (1ul << 2) +#define PROF_COUNT (1ul << 3) + +// symbolic PMU event ids +#define PROF_EV_UNHALTED_CORE_CYCLES (1ul << 0) +#define PROF_EV_LLC_MISSES (1ul << 1) +#define PROF_EV_LLC_REFS (1ul << 2) +#define PROF_EV_BRANCH_INSTR_RETIRED (1ul << 3) +#define PROF_EV_BRANCH_MISS_RETIRED (1ul << 4) +#define PROF_EV_INSTR_RETIRED (1ul << 5) + // non-architectural + // "all dTLB misses that cause a page walk" +#define PROF_EV_DTLB_LOAD_MISS_ANY (1ul << 6) + // "number of completed walks due to miss in sTLB" +#define PROF_EV_DTLB_LOAD_MISS_STLB (1ul << 7) + // "retired stores that missed in the dTLB" +#define PROF_EV_STORE_DTLB_MISS (1ul << 8) +#define PROF_EV_L2_LD_HITS (1ul << 9) + // "all iTLB misses that cause a page walk" +#define PROF_EV_ITLB_LOAD_MISS_ANY (1ul << 10) + +// PMU event flags +#define PROF_EVF_OS (1ul << 0) +#define PROF_EVF_USR (1ul << 1) +#define PROF_EVF_BACKTRACE (1ul << 2) + +long sys_info(long); +#define SINFO_GCCOUNT 0l +#define SINFO_GCPAUSENS 1l +#define SINFO_GCHEAPSZ 2l +#define SINFO_GCMS 4l +#define SINFO_GCTOTALLOC 5l +#define SINFO_GCMARKTIME 6l +#define SINFO_GCSWEEPTIME 7l +#define SINFO_GCWBARTIME 8l +#define SINFO_GCOBJS 9l +#define SINFO_DOGC 10l +#define SINFO_PROCLIST 11l + +int truncate(const char *, off_t); +int unlink(const char *); +pid_t wait(int *); +pid_t waitpid(pid_t, int *, int); +pid_t wait3(int *, int, struct rusage *); +pid_t wait4(pid_t, int *, int, struct rusage *); +#define WAIT_ANY (-1) +#define WAIT_MYPGRP 0 + +#define WCONTINUED 1 +#define WNOHANG 2 +#define WUNTRACED 4 + +#define WIFCONTINUED(x) (x & (1 << 9)) +#define WIFEXITED(x) (x & (1 << 10)) +#define WIFSIGNALED(x) (x & (1 << 11)) +#define WEXITSTATUS(x) (x & 0xff) +#define WTERMSIG(x) ((int)((uint)x >> 27) & 0x1f) +ssize_t write(int, const void*, size_t); +ssize_t writev(int, const struct iovec *, int); + +/* + * thread stuff + */ +void tfork_done(long); +int tfork_thread(struct tfork_t *, long (*fn)(void *), void *); +void threxit(long); +int thrwait(int, long *); + +typedef long pthread_t; + +typedef struct { + size_t stacksize; +#define _PTHREAD_DEFSTKSZ 4096ull +} pthread_attr_t; + +typedef struct { + uint gen; + int bcast; +} pthread_cond_t; + +typedef struct { +} pthread_condattr_t; + +typedef struct { + uint locks; +} pthread_mutex_t; +#define PTHREAD_MUTEX_INITIALIZER {0} + +typedef struct { +} pthread_mutexattr_t; + +typedef struct { +} pthread_once_t; + +typedef struct { + uint target; + volatile uint current; +} pthread_barrier_t; + +typedef struct { +} pthread_barrierattr_t; + +int pthread_attr_destroy(pthread_attr_t *); +int pthread_attr_init(pthread_attr_t *); +int pthread_attr_getstacksize(pthread_attr_t *, size_t *); +int pthread_attr_setstacksize(pthread_attr_t *, size_t); + +int pthread_barrier_init(pthread_barrier_t *, pthread_barrierattr_t *, uint); +int pthread_barrier_destroy(pthread_barrier_t *); +int pthread_barrier_wait(pthread_barrier_t *); +#define PTHREAD_BARRIER_SERIAL_THREAD 1 + +int pthread_cond_broadcast(pthread_cond_t *); +int pthread_cond_destroy(pthread_cond_t *); +int pthread_cond_init(pthread_cond_t *, const pthread_condattr_t *); +int pthread_cond_wait(pthread_cond_t *, pthread_mutex_t *); +int pthread_cond_signal(pthread_cond_t *); +int pthread_cond_timedwait(pthread_cond_t *, pthread_mutex_t *, + const struct timespec *); + +int pthread_create(pthread_t *, pthread_attr_t *, void* (*)(void *), void *); +int pthread_join(pthread_t, void **); +int pthread_mutex_init(pthread_mutex_t *, const pthread_mutexattr_t *); +int pthread_mutex_lock(pthread_mutex_t *); +int pthread_mutex_unlock(pthread_mutex_t *); +int pthread_mutex_destroy(pthread_mutex_t *); +int pthread_once(pthread_once_t *, void (*)(void)); +pthread_t pthread_self(void); +int pthread_cancel(pthread_t); + +int pthread_sigmask(int, const sigset_t *, sigset_t *); + +int pthread_setcancelstate(int, int *); +#define PTHREAD_CANCEL_ENABLE 1 +#define PTHREAD_CANCEL_DISABLE 2 + +int pthread_setcanceltype(int, int *); +#define PTHREAD_CANCEL_DEFERRED 1 +#define PTHREAD_CANCEL_ASYNCHRONOUS 2 + +/* + * posix stuff + */ +typedef struct { + struct { + int from; + int to; + } dup2s[10]; + int dup2slot; +} posix_spawn_file_actions_t; + +typedef struct { +} posix_spawnattr_t; + +int posix_spawn(pid_t *, const char *, const posix_spawn_file_actions_t *, + const posix_spawnattr_t *, char *const argv[], char *const envp[]); +int posix_spawn_file_actions_adddup2(posix_spawn_file_actions_t *, int, int); +int posix_spawn_file_actions_destroy(posix_spawn_file_actions_t *); +int posix_spawn_file_actions_init(posix_spawn_file_actions_t *); + +/* + * libc + */ +#define EXIT_FAILURE (-1) +#define EXIT_SUCCESS 0 + +#define offsetof(s, m) __builtin_offsetof(s, m) + +#define isfinite(x) __builtin_isfinite(x) +#define isinf(x) __builtin_isinf(x) +#define isnan(x) __builtin_isnan(x) +#define HUGE_VAL __builtin_huge_val() +#define NAN __builtin_nanf("") + +// these "builtins" sometimes result in a call to the library function. the +// builtin version just tries to optimize some cases. +#define ceil(x) __builtin_ceil(x) +#define floor(x) __builtin_floor(x) +#define log(x) __builtin_log(x) +#define sqrt(x) __builtin_sqrt(x) +#define trunc(x) __builtin_trunc(x) +#define pow(x, y) __builtin_pow(x, y) + +// annoyingly, some GCC versions < 4.8 are bugged and do not have +// __builtin_bswap16. +#define ntohs(x) (((x & 0xff) << 8) | ((x & 0xff00) >> 8)) +#define htons(x) (((x & 0xff) << 8) | ((x & 0xff00) >> 8)) +#define ntohl(x) __builtin_bswap32(x) +#define htonl(x) __builtin_bswap32(x) + +#define MIN(x, y) (x < y ? x : y) +#define MAX(x, y) (x > y ? x : y) + +#define atof(s) strtod(s, NULL) + +#define _POSIX_NAME_MAX 14 +struct dirent { + ino_t d_ino; + char d_name[_POSIX_NAME_MAX]; +}; + +typedef struct { + int fd; + uint cent; + uint nent; + struct dirent dents[]; +} DIR; + +extern __thread int errno; + +#define BUFSIZ 4096 +#define L_tmpnam 20 + +struct _FILE { + int fd; + int btype; +#define _IOFBF 1 +#define _IOLBF 2 +#define _IONBF 3 + char buf[BUFSIZ]; + // &buf[0] <= p <= end <= &buf[BUFSIZ] + char *p; + char *end; + int eof; + int error; + // [&buf[0], pend) is dirty + int writing; + pthread_mutex_t mut; + // if something else needs builtin linked lists, replace this with + // fancy linked list macros. + struct _FILE *lnext; + struct _FILE *lprev; +}; + +typedef struct _FILE FILE; +extern FILE *stdin, *stdout, *stderr; + +typedef struct { +} jmp_buf; + +struct lconv { + int a; + int decimal_point[10]; +}; + +struct utsname { +#define UTSMAX 64 + char sysname[UTSMAX]; + char nodename[UTSMAX]; + char release[UTSMAX]; + char version[UTSMAX]; + char machine[UTSMAX]; +}; + +void abort(void); +unsigned int alarm(unsigned int); +int atoi(const char *); +double ceil(double); +int closedir(DIR *); +int creat(const char *, mode_t); +char *ctime(const time_t *); +char *ctime_r(const time_t *, char *); +void err(int, const char *, ...) + __attribute__((format(printf, 2, 3))) + __attribute__((__noreturn__)); +void errx(int, const char *, ...) + __attribute__((format(printf, 2, 3))) + __attribute__((__noreturn__)); +void exit(int) + __attribute__((noreturn)); +int fclose(FILE *); +DIR *fdopendir(int); +int feof(FILE *); +int ferror(FILE *); +int fileno(FILE *); +int fflush(FILE *); +int fgetc(FILE *); +char *fgets(char *, int, FILE *); +FILE *fdopen(int, const char *); +FILE *fopen(const char *, const char *); +int fprintf(FILE *, const char *, ...) + __attribute__((format(printf, 2, 3))); +int fsync(int); +//int fputs(const char *, FILE *); /*REDIS*/ +size_t fread(void *, size_t, size_t, FILE *); +off_t ftello(FILE *); +size_t fwrite(const void *, size_t, size_t, FILE *); +struct gcfrac_t { + long startms; + long gcworkms; + struct { + // write barrier ms + long wbms; + long bgsweepms; + long markms; + } details; +}; +struct gcfrac_t gcfracst(void); +double gcfracend(struct gcfrac_t *, long *, long *, long *); +int getopt(int, char * const *, const char *); +extern char *optarg; +extern int optind; + +int isalpha(int); +int isdigit(int); +int islower(int); +int isprint(int); +int ispunct(int); +int isspace(int); +int isupper(int); +int isxdigit(int); + +//struct lconv* localeconv(void); /*REDIS*/ +double log(double); +dev_t makedev(uint, uint); +int memcmp(const void *, const void *, size_t); +void *memcpy(void *, const void *, size_t); +void *memmove(void *, const void *, size_t); +void *memset(void *, int, size_t); +int mkstemp(char *); +DIR *opendir(const char *); +void openlog(const char *, int, int); +// log options +#define LOG_PID (1ull << 0) +#define LOG_CONS (1ull << 1) +#define LOG_NDELAY (1ull << 2) +#define LOG_ODELAY (1ull << 3) +#define LOG_NOWAIT (1ull << 4) +int printf(const char *, ...) + __attribute__((format(printf, 1, 2))); +double pow(double, double); +void perror(const char *); +void qsort(void *, size_t, size_t, int (*)(const void *, const void *)); +int rand(void); +int rand_r(uint *); +#define RAND_MAX 0x7fffffff +long random(void); +ulong rdtsc(void); +struct dirent *readdir(DIR *); +int readdir_r(DIR *, struct dirent *, struct dirent **); +char *readline(const char *); +void rewinddir(DIR *); +//int scanf(const char *, ...) /*REDIS*/ +// __attribute__((format(scanf, 1, 2))); /*REDIS*/ +int setenv(const char *, const char *, int); +char *setlocale(int, const char *); +#define LC_COLLATE 1 +uint sleep(uint); +int snprintf(char *, size_t, const char *,...) + __attribute__((format(printf, 3, 4))); +int sprintf(char *, const char *,...) + __attribute__((format(printf, 2, 3))); +void srand(uint); +void srandom(uint); +int sscanf(const char *, const char *, ...) + __attribute__((format(scanf, 2, 3))); +int strcasecmp(const char *, const char *); +int strncasecmp(const char *, const char *, size_t); +char *strchr(const char *, int); +char *strdup(const char *); +char *strerror(int); +#define NL_TEXTMAX 64 +int strerror_r(int, char *, size_t); +char *strncpy(char *, const char *, size_t); +size_t strlen(const char *); +int strcmp(const char *, const char *); +int strcoll(const char *, const char *); +int strncmp(const char *, const char *, size_t); +long strtol(const char *, char **, int); +double strtod(const char *, char **); +long double strtold(const char *, char **); +long long strtoll(const char *, char **, int); +ulong strtoul(const char *, char **, int); +unsigned long long strtoull(const char *, char **, int); +char *strstr(const char *, const char *); +void syslog(int, const char *, ...); +// priorities +#define LOG_EMERG (1ull << 0) +#define LOG_ALERT (1ull << 1) +#define LOG_CRIT (1ull << 2) +#define LOG_ERR (1ull << 3) +#define LOG_WARNING (1ull << 4) +#define LOG_NOTICE (1ull << 5) +#define LOG_INFO (1ull << 6) +#define LOG_DEBUG (1ull << 7) +#define LOG_LOCAL0 (1ull << 8) +#define LOG_LOCAL1 (1ull << 9) +#define LOG_LOCAL2 (1ull << 10) +#define LOG_LOCAL3 (1ull << 11) +#define LOG_LOCAL4 (1ull << 12) +#define LOG_LOCAL5 (1ull << 13) +#define LOG_LOCAL6 (1ull << 14) +#define LOG_LOCAL7 (1ull << 15) +#define LOG_USER (1ull << 16) +#define LOG_ALL (0x1ffff) +time_t time(time_t*); +int tolower(int); +int toupper(int); +double trunc(double); +int uname(struct utsname *); +int ungetc(int, FILE *); +int usleep(uint); +int vfprintf(FILE *, const char *, va_list) + __attribute__((format(printf, 2, 0))); +int vprintf(const char *, va_list) + __attribute__((format(printf, 1, 0))); +int vsnprintf(char *, size_t, const char *, va_list) + __attribute__((format(printf, 3, 0))); +int vsscanf(const char *, const char *, va_list) + __attribute__((format(scanf, 2, 0))); + +void *malloc(size_t); +void free(void *); +void *calloc(size_t, size_t); +void *realloc(void *, size_t); + +extern char __progname[64]; +extern char **environ; + +/* NGINX STUFF */ +char *getenv(char *); +uid_t geteuid(void); + +struct passwd { + char *pw_name; + uid_t pw_uid; + gid_t pw_gid; + char *pw_dir; + char *pw_shell; +}; + +struct passwd *getpwnam(const char *); + +struct group { + char *gr_name; + gid_t gr_gid; + char **gr_mem; +}; + +struct group *getgrnam(const char *); + +struct hostent { + char *h_name; + char **h_aliases; + int h_addrtype; + int h_length; + char **h_addr_list; +}; + +struct hostent *gethostbyname(const char *); +int chown(const char *, uid_t, gid_t); +time_t mktime(struct tm *); +int getpeername(int, struct sockaddr *, socklen_t *); +int getsockname(int, struct sockaddr *, socklen_t *); +int setsockopt(int, int, int, const void *, socklen_t); +int gethostname(char *, size_t); +char *strpbrk(const char *, const char *); + +struct itimerval { + struct timeval it_interval; + struct timeval it_value; +}; + +#define ITIMER_REAL 1 + +int setitimer(int, struct itimerval *, struct itimerval *); + +struct tm *localtime(const time_t *); +struct tm *gmtime(const time_t *); +int utimes(const char *, const struct timeval[2]); + +typedef struct { + int gl_pathc; + int gl_matchc; + char **gl_pathv; + int gl_offs; +} glob_t; + +int glob(const char *, int, int (*)(const char *, int), glob_t *); +void globfree(glob_t *); + +struct flock { + short l_type; +#define F_WRLCK 1 +#define F_UNLCK 2 + short l_whence; + off_t l_start; + off_t l_len; + pid_t l_pid; +}; + +int socketpair(int, int, int, int[2]); +int ioctl(int, ulong, ...); +#define FIOASYNC 3 + +int raise(int); +mode_t umask(mode_t); +int getpagesize(void); +int sigprocmask(int, sigset_t *, sigset_t *); +int sigsuspend(const sigset_t *); + +int setpriority(int, int, int); +#define PRIO_PROCESS 1 + +uid_t getuid(void); +int setuid(uid_t); +int setgid(gid_t); +int initgroups(const char *, gid_t); + +#define MSG_PEEK 1 + +char *realpath(const char *, char *); +int lstat(const char *, struct stat *); + +//static inline pid_t +//getppid(void) +//{ +// pid_t ret; +// asm volatile( +// "movq %%rsp, %%r10\n" +// "leaq 2(%%rip), %%r11\n" +// "sysenter\n" +// "callq flea\n" +// : "=a"(ret) +// : "0"(40) +// : "cc", "rdi", "rsi", "rdx", "rcx", "r8", "memory", "r9", "r10", "r11", "r12", "r13", "r14", "r15"); +// return ret; +//} + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/biscuit/c/include/littypes.h b/biscuit/c/include/littypes.h new file mode 100644 index 0000000..2e7294f --- /dev/null +++ b/biscuit/c/include/littypes.h @@ -0,0 +1,109 @@ +#pragma once + +typedef unsigned char uchar; +typedef unsigned short ushort; +typedef unsigned int uint; +typedef unsigned long ulong; + +typedef unsigned char u_char; +typedef unsigned short u_short; +typedef unsigned int u_int; +typedef unsigned long u_long; + +typedef unsigned long size_t; +typedef long ssize_t; +typedef long ptrdiff_t; + +typedef long intptr_t; +typedef unsigned long uintptr_t; + +typedef char int8_t; +typedef short int16_t; +typedef int int32_t; +typedef long int64_t; +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; +typedef unsigned long uint64_t; + +typedef char int_least8_t; +typedef short int_least16_t; +typedef int int_least32_t; +typedef long int_least64_t; +typedef unsigned char uint_least8_t; +typedef unsigned short uint_least16_t; +typedef unsigned int uint_least32_t; +typedef unsigned long uint_least64_t; + +// OpenBSD uses int32_t for all fastN_t except 64 +typedef int int_fast8_t; +typedef int int_fast16_t; +typedef int int_fast32_t; +typedef long int_fast64_t; +typedef unsigned int uint_fast8_t; +typedef unsigned int uint_fast16_t; +typedef unsigned int uint_fast32_t; +typedef unsigned long uint_fast64_t; + +typedef long intmax_t; +typedef unsigned long uintmax_t; + +typedef unsigned char u8; + +typedef long time_t; +typedef unsigned long nfds_t; +typedef unsigned long mode_t; +typedef long dev_t; +typedef ulong ino_t; +typedef long off_t; +typedef long pid_t; +typedef long uid_t; +typedef long gid_t; +typedef unsigned long socklen_t; +typedef unsigned long rlim_t; +typedef unsigned long sigset_t; +typedef volatile long sig_atomic_t; +typedef long blkcnt_t; +typedef char * caddr_t; + +#define NULL ((void *)0) + +#define va_start(ap, last) __builtin_va_start(ap, last) +#define va_arg(ap, type) __builtin_va_arg(ap, type) +#define va_end(ap) __builtin_va_end(ap) +#define va_copy(d, s) __builtin_va_copy(d, s) + +typedef __builtin_va_list va_list; + +#define INT8_MIN (-128) +#define INT16_MIN (-32768) +#define INT32_MIN (-2147483648) +#define INT8_MAX 127 +#define INT16_MAX 32767 +#define INT32_MAX 2147483647 + +#define INT_MIN (1 << 31) +#define INT_MAX 2147483647 + +#define UINT_MAX 0xffffffff + +#define LONG_MIN (1ll << 63) +#define LLONG_MIN (1ll << 63) + +#define LONG_MAX 9223372036854775807ll +#define LLONG_MAX 9223372036854775807ll + +#define UCHAR_MAX 0xff +#define UINT16_MAX 0xffff + +#define ULONG_MAX 0xffffffffffffffffull +#define ULLONG_MAX 0xffffffffffffffffull + +#define INT8_C(x) x +#define INT16_C(x) x +#define INT32_C(x) x +#define INT64_C(x) x ## ll +#define UINT8_C(x) x ## u +#define UINT16_C(x) x ## u +#define UINT32_C(x) x ## u +#define UINT64_C(x) x ## ull diff --git a/biscuit/c/include/locale.h b/biscuit/c/include/locale.h new file mode 100644 index 0000000..1681924 --- /dev/null +++ b/biscuit/c/include/locale.h @@ -0,0 +1,3 @@ +#pragma once + +#include diff --git a/biscuit/c/include/malloc.h b/biscuit/c/include/malloc.h new file mode 100644 index 0000000..1681924 --- /dev/null +++ b/biscuit/c/include/malloc.h @@ -0,0 +1,3 @@ +#pragma once + +#include diff --git a/biscuit/c/include/math.h b/biscuit/c/include/math.h new file mode 100644 index 0000000..1681924 --- /dev/null +++ b/biscuit/c/include/math.h @@ -0,0 +1,3 @@ +#pragma once + +#include diff --git a/biscuit/c/include/netdb.h b/biscuit/c/include/netdb.h new file mode 100644 index 0000000..1681924 --- /dev/null +++ b/biscuit/c/include/netdb.h @@ -0,0 +1,3 @@ +#pragma once + +#include diff --git a/biscuit/c/include/netinet/in.h b/biscuit/c/include/netinet/in.h new file mode 100644 index 0000000..1681924 --- /dev/null +++ b/biscuit/c/include/netinet/in.h @@ -0,0 +1,3 @@ +#pragma once + +#include diff --git a/biscuit/c/include/netinet/tcp.h b/biscuit/c/include/netinet/tcp.h new file mode 100644 index 0000000..1681924 --- /dev/null +++ b/biscuit/c/include/netinet/tcp.h @@ -0,0 +1,3 @@ +#pragma once + +#include diff --git a/biscuit/c/include/poll.h b/biscuit/c/include/poll.h new file mode 100644 index 0000000..1681924 --- /dev/null +++ b/biscuit/c/include/poll.h @@ -0,0 +1,3 @@ +#pragma once + +#include diff --git a/biscuit/c/include/pthread.h b/biscuit/c/include/pthread.h new file mode 100644 index 0000000..1681924 --- /dev/null +++ b/biscuit/c/include/pthread.h @@ -0,0 +1,3 @@ +#pragma once + +#include diff --git a/biscuit/c/include/pwd.h b/biscuit/c/include/pwd.h new file mode 100644 index 0000000..1681924 --- /dev/null +++ b/biscuit/c/include/pwd.h @@ -0,0 +1,3 @@ +#pragma once + +#include diff --git a/biscuit/c/include/sched.h b/biscuit/c/include/sched.h new file mode 100644 index 0000000..1681924 --- /dev/null +++ b/biscuit/c/include/sched.h @@ -0,0 +1,3 @@ +#pragma once + +#include diff --git a/biscuit/c/include/setjmp.h b/biscuit/c/include/setjmp.h new file mode 100644 index 0000000..1681924 --- /dev/null +++ b/biscuit/c/include/setjmp.h @@ -0,0 +1,3 @@ +#pragma once + +#include diff --git a/biscuit/c/include/signal.h b/biscuit/c/include/signal.h new file mode 100644 index 0000000..1681924 --- /dev/null +++ b/biscuit/c/include/signal.h @@ -0,0 +1,3 @@ +#pragma once + +#include diff --git a/biscuit/c/include/spawn.h b/biscuit/c/include/spawn.h new file mode 100644 index 0000000..1681924 --- /dev/null +++ b/biscuit/c/include/spawn.h @@ -0,0 +1,3 @@ +#pragma once + +#include diff --git a/biscuit/c/include/stdarg.h b/biscuit/c/include/stdarg.h new file mode 100644 index 0000000..1681924 --- /dev/null +++ b/biscuit/c/include/stdarg.h @@ -0,0 +1,3 @@ +#pragma once + +#include diff --git a/biscuit/c/include/stddef.h b/biscuit/c/include/stddef.h new file mode 100644 index 0000000..1681924 --- /dev/null +++ b/biscuit/c/include/stddef.h @@ -0,0 +1,3 @@ +#pragma once + +#include diff --git a/biscuit/c/include/stdint.h b/biscuit/c/include/stdint.h new file mode 100644 index 0000000..1681924 --- /dev/null +++ b/biscuit/c/include/stdint.h @@ -0,0 +1,3 @@ +#pragma once + +#include diff --git a/biscuit/c/include/stdio.h b/biscuit/c/include/stdio.h new file mode 100644 index 0000000..1681924 --- /dev/null +++ b/biscuit/c/include/stdio.h @@ -0,0 +1,3 @@ +#pragma once + +#include diff --git a/biscuit/c/include/stdlib.h b/biscuit/c/include/stdlib.h new file mode 100644 index 0000000..1681924 --- /dev/null +++ b/biscuit/c/include/stdlib.h @@ -0,0 +1,3 @@ +#pragma once + +#include diff --git a/biscuit/c/include/string.h b/biscuit/c/include/string.h new file mode 100644 index 0000000..1681924 --- /dev/null +++ b/biscuit/c/include/string.h @@ -0,0 +1,3 @@ +#pragma once + +#include diff --git a/biscuit/c/include/strings.h b/biscuit/c/include/strings.h new file mode 100644 index 0000000..1681924 --- /dev/null +++ b/biscuit/c/include/strings.h @@ -0,0 +1,3 @@ +#pragma once + +#include diff --git a/biscuit/c/include/sys/ioctl.h b/biscuit/c/include/sys/ioctl.h new file mode 100644 index 0000000..1681924 --- /dev/null +++ b/biscuit/c/include/sys/ioctl.h @@ -0,0 +1,3 @@ +#pragma once + +#include diff --git a/biscuit/c/include/sys/mman.h b/biscuit/c/include/sys/mman.h new file mode 100644 index 0000000..1681924 --- /dev/null +++ b/biscuit/c/include/sys/mman.h @@ -0,0 +1,3 @@ +#pragma once + +#include diff --git a/biscuit/c/include/sys/param.h b/biscuit/c/include/sys/param.h new file mode 100644 index 0000000..1681924 --- /dev/null +++ b/biscuit/c/include/sys/param.h @@ -0,0 +1,3 @@ +#pragma once + +#include diff --git a/biscuit/c/include/sys/resource.h b/biscuit/c/include/sys/resource.h new file mode 100644 index 0000000..1681924 --- /dev/null +++ b/biscuit/c/include/sys/resource.h @@ -0,0 +1,3 @@ +#pragma once + +#include diff --git a/biscuit/c/include/sys/select.h b/biscuit/c/include/sys/select.h new file mode 100644 index 0000000..1681924 --- /dev/null +++ b/biscuit/c/include/sys/select.h @@ -0,0 +1,3 @@ +#pragma once + +#include diff --git a/biscuit/c/include/sys/socket.h b/biscuit/c/include/sys/socket.h new file mode 100644 index 0000000..1681924 --- /dev/null +++ b/biscuit/c/include/sys/socket.h @@ -0,0 +1,3 @@ +#pragma once + +#include diff --git a/biscuit/c/include/sys/stat.h b/biscuit/c/include/sys/stat.h new file mode 100644 index 0000000..1681924 --- /dev/null +++ b/biscuit/c/include/sys/stat.h @@ -0,0 +1,3 @@ +#pragma once + +#include diff --git a/biscuit/c/include/sys/time.h b/biscuit/c/include/sys/time.h new file mode 100644 index 0000000..1681924 --- /dev/null +++ b/biscuit/c/include/sys/time.h @@ -0,0 +1,3 @@ +#pragma once + +#include diff --git a/biscuit/c/include/sys/types.h b/biscuit/c/include/sys/types.h new file mode 100644 index 0000000..1681924 --- /dev/null +++ b/biscuit/c/include/sys/types.h @@ -0,0 +1,3 @@ +#pragma once + +#include diff --git a/biscuit/c/include/sys/uio.h b/biscuit/c/include/sys/uio.h new file mode 100644 index 0000000..1681924 --- /dev/null +++ b/biscuit/c/include/sys/uio.h @@ -0,0 +1,3 @@ +#pragma once + +#include diff --git a/biscuit/c/include/sys/un.h b/biscuit/c/include/sys/un.h new file mode 100644 index 0000000..1681924 --- /dev/null +++ b/biscuit/c/include/sys/un.h @@ -0,0 +1,3 @@ +#pragma once + +#include diff --git a/biscuit/c/include/sys/utsname.h b/biscuit/c/include/sys/utsname.h new file mode 100644 index 0000000..1681924 --- /dev/null +++ b/biscuit/c/include/sys/utsname.h @@ -0,0 +1,3 @@ +#pragma once + +#include diff --git a/biscuit/c/include/sys/wait.h b/biscuit/c/include/sys/wait.h new file mode 100644 index 0000000..1681924 --- /dev/null +++ b/biscuit/c/include/sys/wait.h @@ -0,0 +1,3 @@ +#pragma once + +#include diff --git a/biscuit/c/include/syslog.h b/biscuit/c/include/syslog.h new file mode 100644 index 0000000..1681924 --- /dev/null +++ b/biscuit/c/include/syslog.h @@ -0,0 +1,3 @@ +#pragma once + +#include diff --git a/biscuit/c/include/time.h b/biscuit/c/include/time.h new file mode 100644 index 0000000..1681924 --- /dev/null +++ b/biscuit/c/include/time.h @@ -0,0 +1,3 @@ +#pragma once + +#include diff --git a/biscuit/c/include/unistd.h b/biscuit/c/include/unistd.h new file mode 100644 index 0000000..1681924 --- /dev/null +++ b/biscuit/c/include/unistd.h @@ -0,0 +1,3 @@ +#pragma once + +#include diff --git a/biscuit/c/init.c b/biscuit/c/init.c new file mode 100644 index 0000000..fac0623 --- /dev/null +++ b/biscuit/c/init.c @@ -0,0 +1,93 @@ +#include + +static void fexec(char * const args[]) +{ + printf("init exec: "); + for (char * const * p = &args[0]; *p; p++) + printf("%s ", *p); + printf("\n"); + + switch (fork()) { + case -1: + err(-1, "fork (%s)", args[0]); + case 0: + execv(args[0], args); + err(-1, "exec (%s)", args[0]); + default: + { + int status; + if (wait(&status) == -1) + err(-1, "wait"); + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) + err(-1, "child failed (%s)", args[0]); + } + } +} + +int main(int argc, char **argv) +{ + int rshd = 0; + int c; + while ((c = getopt(argc, argv, "r")) != -1) { + switch (c) { + default: + printf("bad option/arg\n"); + break; + case 'r': + rshd = 1; + break; + } + } + argc -= optind; + argv += optind; + if (argc > 0) + printf("ignoring extra args\n"); + + printf("init starting...\n"); + + // create dev nodes + mkdir("/dev", 0); + int ret; + ret = mknod("/dev/console", 0, MKDEV(1, 0)); + if (ret != 0 && errno != EEXIST) + err(-1, "mknod"); + ret = mknod("/dev/null", 0, MKDEV(4, 0)); + if (ret != 0 && errno != EEXIST) + err(-1, "mknod"); + ret = mknod("/dev/rsd0c", 0, MKDEV(5, 0)); + if (ret != 0 && errno != EEXIST) + err(-1, "mknod"); + ret = mknod("/dev/stats", 0, MKDEV(6, 0)); + if (ret != 0 && errno != EEXIST) + err(-1, "mknod"); + ret = mknod("/dev/prof", 0, MKDEV(7, 0)); + if (ret != 0 && errno != EEXIST) + err(-1, "mknod"); + + char * const largs [] = {"/bin/bmgc", "-l", "512", NULL}; + fexec(largs); + char * const hargs [] = {"/bin/bmgc", "-h", "470", NULL}; + fexec(hargs); + if (rshd) { + printf("starting rshd...\n"); + pid_t rpid = fork(); + if (rpid == 0) { + char * const rargs [] = {"/bin/rshd", NULL}; + execv(rargs[0], rargs); + err(-1, "execv"); + } else if (rpid == -1) + perror("execv"); + } + + for (;;) { + int pid = fork(); + if (!pid) { + char * const args[] = {"/bin/lsh", NULL}; + execv(args[0], args); + err(-1, "execv"); + } + wait(NULL); + printf("lsh terminated?\n"); + } + return 0; +} diff --git a/biscuit/c/kill.c b/biscuit/c/kill.c new file mode 100644 index 0000000..209dccb --- /dev/null +++ b/biscuit/c/kill.c @@ -0,0 +1,10 @@ +#include + +int main(int argc, char **argv) +{ + if (argc != 2) + errx(-1, "usage: %s \n", argv[0]); + + int pid = atoi(argv[1]); + return kill(pid, SIGKILL); +} diff --git a/biscuit/c/killtest.c b/biscuit/c/killtest.c new file mode 100644 index 0000000..a4c9e1a --- /dev/null +++ b/biscuit/c/killtest.c @@ -0,0 +1,17 @@ +#include + +int main(int argc, char **argv) +{ + int pid = fork(); + if (pid == 0) + while (1); + printf("killing %d...", pid); + if (kill(pid, SIGKILL) < 0) + err(-1, "kill"); + printf("killed. waiting..."); + if (wait(NULL) < 0) + err(-1, "wait failed\n"); + printf("done\n"); + printf("success\n"); + return 0; +} diff --git a/biscuit/c/largefile.c b/biscuit/c/largefile.c new file mode 100644 index 0000000..cc6ae0d --- /dev/null +++ b/biscuit/c/largefile.c @@ -0,0 +1,195 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Measure creating a large file and overwriting that file */ + +#define WSIZE (4096) +#define FILESIZE 50 * 1024 * 1024 +#define NAMESIZE 100 + +static char name[NAMESIZE]; +static char buf[WSIZE]; +static char *prog; +static char *dir; + +void printstats(int reset) +{ + int fd; + int r; + + sprintf(name, "dev/stats"); + if((fd = open(name, O_RDONLY)) < 0) { + return; + } + + memset(buf, 0, WSIZE); + + if ((r = read(fd, buf, WSIZE)) < 0) { + perror("read"); + exit(1); + } + + if (!reset) fprintf(stdout, "=== FS Stats ===\n%s========\n", buf); + + if ((r = close(fd)) < 0) { + perror("close"); + } +} + +void makefile() +{ + int i; + int fd; + + int n = FILESIZE/WSIZE; + + memset(buf, 'a', WSIZE); + + sprintf(name, "%s/d/f", dir); + if((fd = open(name, O_RDWR | O_CREAT | O_TRUNC, S_IRWXU)) < 0) { + printf("%s: create %s failed %s\n", prog, name, strerror(errno)); + exit(1); + } + + for (i = 0; i < n; i++) { + if (write(fd, buf, WSIZE) != WSIZE) { + printf("%s: write %s failed %s\n", prog, name, strerror(errno)); + exit(1); + } + } + + if (fsync(fd) < 0) { + printf("%s: fsync %s failed %s\n", prog, name, strerror(errno)); + exit(1); + } + + + fd = open(".", O_DIRECTORY | O_RDONLY); + if (fd < 0) { + perror("open dir"); + exit(-1); + } + if (fsync(fd) < 0) { + perror("fsync"); + exit(-1); + } +} + +void writefile() +{ + int i; + int fd; + + int n = FILESIZE/WSIZE; + + sprintf(name, "%s/d/f", dir); + if((fd = open(name, O_RDWR, S_IRWXU)) < 0) { + printf("%s: open %s failed %s\n", prog, name, strerror(errno)); + exit(1); + } + + memset(buf, 'b', WSIZE); + + for (i = 0; i < n; i++) { + if (write(fd, buf, WSIZE) != WSIZE) { + printf("%s: write %s failed %s\n", prog, name, strerror(errno)); + exit(1); + } + if (((i + 1) * WSIZE) % (10 * 1024 * 1024) == 0) { + if (fsync(fd) < 0) { + printf("%s: fsync %s failed %s\n", prog, name, strerror(errno)); + exit(1); + } + } + } + if ((i * WSIZE) % (10 * 1024 * 1024) != 0 && fsync(fd) < 0) { + printf("%s: fsync %s failed %s\n", prog, name, strerror(errno)); + exit(1); + } + close(fd); +} + +void usage(char *name) { + printf("Usage: %s basedir\n", name); + exit(-1); +} + +int main(int argc, char *argv[]) +{ + long time; + struct timeval before; + struct timeval after; + float tput; + int make = 1; + int write = 1; + int ch; + + prog = argv[0]; + + while ((ch = getopt(argc, argv, "mw")) != -1) { + switch (ch) { + case 'm': + make = 0; + break; + case 'w': + write = 0; + break; + default: + usage(argv[0]); + break; + } + } + + int rem = argc - optind; + if (rem != 1) { + usage(argv[0]); + } + + dir = argv[optind]; + sprintf(name, "%s/d", dir); + + + if (make) { + if (mkdir(name, S_IRWXU) < 0) { + printf("%s: create %s failed %s\n", prog, name, strerror(errno)); + exit(1); + } + + printstats(1); + + gettimeofday ( &before, NULL ); + makefile(); + gettimeofday ( &after, NULL ); + + time = (after.tv_sec - before.tv_sec) * 1000000 + + (after.tv_usec - before.tv_usec); + tput = ((float) (FILESIZE/1024) / (time / 1000000.0)); + printf("makefile %d MB %ld usec throughput %f KB/s\n", FILESIZE/(1024*1024), time, tput); + + printstats(0); + } + + if (write) { + printstats(1); + + gettimeofday ( &before, NULL ); + writefile(); + gettimeofday ( &after, NULL ); + + time = (after.tv_sec - before.tv_sec) * 1000000 + + (after.tv_usec - before.tv_usec); + tput = ((float) (FILESIZE/1024) / (time / 1000000.0)); + printf("writefile %d MB %ld usec throughput %f KB/s\n", FILESIZE/(1024*1024), time, tput); + + printstats(0); + } + + return 0; +} diff --git a/biscuit/c/less.c b/biscuit/c/less.c new file mode 100644 index 0000000..ce4f5ca --- /dev/null +++ b/biscuit/c/less.c @@ -0,0 +1,36 @@ +#include + +int main(int argc, char **argv) +{ + if (argc < 2) + errx(-1, "usage: %s [...]", argv[0]); + FILE *fout = fdopen(1, "w"); + if (!fout) + err(-1, "fdopen"); + int i; + for (i = 1; i < argc; i++) { + char *fn = argv[i]; + FILE *f = fopen(fn, "r"); + if (!f) + err(-1, "fopen"); + char buf; + size_t ret; + int nl = 0; + while ((ret = fread(&buf, 1, 1, f)) == 1) { + if (fwrite(&buf, 1, 1, fout) != 1) + err(-1, "fwrite"); + // 24 lines at a time + if (buf == '\n') + nl++; + if (nl == 24) { + if (read(0, &buf, 1) != 1) + err(-1, "read stdin"); + nl = 0; + } + } + if (ferror(f)) + err(-1, "fread"); + fclose(f); + } + return 0; +} diff --git a/biscuit/c/libs/litc.c b/biscuit/c/libs/litc.c new file mode 100644 index 0000000..3263d7c --- /dev/null +++ b/biscuit/c/libs/litc.c @@ -0,0 +1,3779 @@ +#include +#include + +#define SYS_READ 0 +#define SYS_WRITE 1 +#define SYS_OPEN 2 +#define SYS_CLOSE 3 +#define SYS_STAT 4 +#define SYS_FSTAT 5 +#define SYS_POLL 7 +#define SYS_LSEEK 8 +#define SYS_MMAP 9 +#define SYS_MUNMAP 11 +#define SYS_SIGACTION 13 +#define SYS_READV 19 +#define SYS_WRITEV 20 +#define SYS_ACCESS 21 +#define SYS_DUP2 33 +#define SYS_PAUSE 34 +#define SYS_GETPID 39 +#define SYS_GETPPID 40 +#define SYS_SOCKET 41 +#define SYS_CONNECT 42 +#define SYS_ACCEPT 43 +#define SYS_SENDTO 44 +#define SYS_RECVFROM 45 +#define SYS_SOCKETPAIR 46 +#define SYS_SHUTDOWN 48 +#define SYS_BIND 49 +#define SYS_LISTEN 50 +#define SYS_RECVMSG 51 +#define SYS_SENDMSG 52 +#define SYS_GETSOCKOPT 55 +#define SYS_SETSOCKOPT 56 +#define SYS_FORK 57 +#define SYS_EXECV 59 +#define SYS_EXIT 60 +#define SYS_WAIT4 61 +#define SYS_KILL 62 +#define SYS_FCNTL 72 +#define SYS_TRUNC 76 +#define SYS_FTRUNC 77 +#define SYS_GETCWD 79 +#define SYS_CHDIR 80 +#define SYS_RENAME 82 +#define SYS_MKDIR 83 +#define SYS_LINK 86 +#define SYS_UNLINK 87 +#define SYS_GETTOD 96 +#define SYS_GETRLIMIT 97 +#define SYS_GETRUSAGE 98 +#define SYS_MKNOD 133 +#define SYS_SETRLIMIT 160 +#define SYS_SYNC 162 +#define SYS_REBOOT 169 +#define SYS_NANOSLEEP 230 +#define SYS_PIPE2 293 +#define SYS_PROF 31337 +#define SYS_THREXIT 31338 +#define SYS_INFO 31339 +#define SYS_PREAD 31340 +#define SYS_PWRITE 31341 +#define SYS_FUTEX 31342 +#define SYS_GETTID 31343 + +__thread int errno; + +static int dolock = 1; + +static void pmsg(char *, long); + +struct kinfo_t { + void *freshtls; + size_t len; + void *t0tls; + ulong pspercycle; +}; + +// initialized in _entry, given to us by kernel +static struct kinfo_t *kinfo; + +// tfork_thread and _pcreate use inline asm to call some syscalls in order to +// 1) make sure the new thread immediately calls the destination function +// without executing any C code (which would be unsafe since the new thread is +// running on a different stack) 2) to make sure that, on thread exit, the +// stack is not used after munmapping it, but before calling exit(2). we use +// this macro to make sure the clobbers are coherent for these three pieces of +// code using syscalls. +#define SYSCALL_CLOBBERS "cc", "memory", "r9", "r10", "r11", "r12", "r13", \ + "r14", "r15" +long +syscall(long a1, long a2, long a3, long a4, + long a5, long trap) +{ + long ret; + register long r8 asm("r8") = a5; + + // we may want to follow the sys5 abi and have the kernel restore + // r14-r15 too... + asm volatile( + "movq %%rsp, %%r10\n" + "leaq 2(%%rip), %%r11\n" + "sysenter\n" + : "=a"(ret) + : "0"(trap), "D"(a1), "S"(a2), "d"(a3), "c"(a4), "r"(r8) + : SYSCALL_CLOBBERS); + + return ret; +} + +#define SA(x) ((long)x) +#define ERRNO_NZ(x) do { \ + if (x != 0) { \ + errno = -x; \ + x = -1; \ + } \ + } while (0) + +#define ERRNO_NEG(x) do { \ + if (x < 0) { \ + errno = -x; \ + x = -1; \ + } \ + } while (0) + +int +accept(int fd, struct sockaddr *s, socklen_t *sl) +{ + int ret = syscall(SA(fd), SA(s), SA(sl), 0, 0, SYS_ACCEPT); + ERRNO_NEG(ret); + return ret; +} + +int +access(const char *p, int mode) +{ + int ret = syscall(SA(p), SA(mode), 0, 0, 0, SYS_ACCESS); + ERRNO_NZ(ret); + return ret; +} + +int +bind(int sockfd, const struct sockaddr *s, socklen_t sl) +{ + int ret = syscall(SA(sockfd), SA(s), SA(sl), 0, 0, SYS_BIND); + ERRNO_NZ(ret); + return ret; +} + +int +close(int fd) +{ + int ret = syscall(SA(fd), 0, 0, 0, 0, SYS_CLOSE); + ERRNO_NZ(ret); + return ret; +} + +int +chdir(const char *path) +{ + int ret = syscall(SA(path), 0, 0, 0, 0, SYS_CHDIR); + ERRNO_NZ(ret); + return ret; +} + +int +dup(int o) +{ + int fd; + if ((fd = open("/", O_RDONLY | O_DIRECTORY)) == -1) + return -1; + if (dup2(o, fd) == -1) + return -1; + return fd; +} + +int +connect(int fd, const struct sockaddr *sa, socklen_t salen) +{ + int ret = syscall(SA(fd), SA(sa), SA(salen), 0, 0, SYS_CONNECT); + ERRNO_NZ(ret); + return ret; +} + +int +chmod(const char *path, mode_t mode) +{ + printf("warning: chmod is a no-op\n"); + return 0; +} + +int +dup2(int old, int new) +{ + int ret = syscall(SA(old), SA(new), 0, 0, 0, SYS_DUP2); + ERRNO_NEG(ret); + return ret; +} + +void +_exit(int status) +{ + syscall(status, 0, 0, 0, 0, SYS_EXIT); + errx(-1, "exit returned"); + while(1); +} + +int +execv(const char *path, char * const argv[]) +{ + return execve(path, argv, environ); +} + +int +execve(const char *path, char * const argv[], char * const envp[]) +{ + if (envp != environ) + errx(-1, "envp not supported yet"); + int ret = syscall(SA(path), SA(argv), 0, 0, 0, SYS_EXECV); + errno = -ret; + return -1; +} + +static const char * +_binname(const char *bin) +{ + static char buf[64]; + // absoulte path + if (strchr(bin, '/')) { + snprintf(buf, sizeof(buf), "%s", bin); + return bin; + } + + // try paths + char *paths[] = {"/bin/"}; + const int elems = sizeof(paths)/sizeof(paths[0]); + int i; + for (i = 0; i < elems; i++) { + snprintf(buf, sizeof(buf), "%s%s", paths[i], bin); + struct stat st; + if (stat(buf, &st) == 0) + return buf; + else if (errno != ENOENT) + return NULL; + } + return NULL; +} + +int +execvp(const char *path, char * const argv[]) +{ + const char *p = _binname(path); + if (!p) + return -1; + return execv(p, argv); +} + +int +fcntl(int fd, int cmd, ...) +{ + int ret; + long a1 = SA(fd); + long a2 = SA(cmd); + + va_list ap; + va_start(ap, cmd); + switch (cmd) { + case F_GETFD: + case F_SETFD: + case F_GETFL: + case F_SETFL: + { + int fl = va_arg(ap, int); + ret = syscall(a1, a2, SA(fl), 0, 0, SYS_FCNTL); + ERRNO_NEG(ret); + break; + } + case F_SETOWN: + { + fprintf(stderr, "warning: F_SETOWN is no-op\n"); + ret = 0; + break; + } + default: + errx(-1, "no imp"); + } + va_end(ap); + return ret; +} + +pid_t +fork(void) +{ + long flags = FORK_PROCESS; + int ret = syscall(0, flags, 0, 0, 0, SYS_FORK); + ERRNO_NEG(ret); + return ret; +} + +int +fstat(int fd, struct stat *buf) +{ + int ret = syscall(SA(fd), SA(buf), 0, 0, 0, SYS_FSTAT); + ERRNO_NZ(ret); + return ret; +} + +char * +getcwd(char *buf, size_t sz) +{ + int ret = syscall(SA(buf), SA(sz), 0, 0, 0, SYS_GETCWD); + ERRNO_NZ(ret); + if (ret) + return NULL; + return buf; +} + +pid_t +getpid(void) +{ + return syscall(0, 0, 0, 0, 0, SYS_GETPID); +} + +pid_t +getppid(void) +{ + return syscall(0, 0, 0, 0, 0, SYS_GETPPID); +} + +int +getsockopt(int fd, int level, int opt, void *optv, socklen_t *optlen) +{ + int ret = syscall(SA(fd), SA(level), SA(opt), SA(optv), SA(optlen), + SYS_GETSOCKOPT); + ERRNO_NZ(ret); + return ret; +} + +int +getpeername(int s, struct sockaddr *sa, socklen_t *sl) +{ + return getsockopt(s, SOL_SOCKET, SO_PEER, sa, sl); +} + +int +getsockname(int s, struct sockaddr *sa, socklen_t *sl) +{ + return getsockopt(s, SOL_SOCKET, SO_NAME, sa, sl); +} + +int +gettimeofday(struct timeval *tv, struct timezone *tz) +{ + if (tz) + errx(-1, "timezone not supported"); + //int ret = syscall(SA(tv), 0, 0, 0, 0, SYS_GETTOD); + //ERRNO_NZ(ret); + //return ret; + ulong c = rdtsc(); + ulong us = c * kinfo->pspercycle / 1000000; + tv->tv_sec = us / 1000000; + tv->tv_usec = us % 1000000; + return 0; +} + +// XXX should simply look at TCB +long +gettid(void) +{ + return syscall(0, 0, 0, 0, 0, SYS_GETTID); +} + +int +getrlimit(int res, struct rlimit *rlp) +{ + int ret = syscall(SA(res), SA(rlp), 0, 0, 0, SYS_GETRLIMIT); + ERRNO_NEG(ret); + return ret; +} + +int +getrusage(int who, struct rusage *r) +{ + int ret = syscall(SA(who), SA(r), 0, 0, 0, SYS_GETRUSAGE); + ERRNO_NZ(ret); + return ret; +} + +int +kill(int pid, int sig) +{ + if (sig != SIGKILL) { + printf("%s: kill: only SIGKILL is supported\n", __progname); + return -1; + } + int ret = syscall(SA(pid), SA(sig), 0, 0, 0, SYS_KILL); + ERRNO_NZ(ret); + return ret; +} + +int +link(const char *old, const char *new) +{ + int ret = syscall(SA(old), SA(new), 0, 0, 0, SYS_LINK); + ERRNO_NZ(ret); + return ret; +} + +int +listen(int fd, int backlog) +{ + int ret = syscall(SA(fd), SA(backlog), 0, 0, 0, SYS_LISTEN); + ERRNO_NZ(ret); + return ret; +} + +off_t +lseek(int fd, off_t off, int whence) +{ + int ret = syscall(SA(fd), SA(off), SA(whence), 0, 0, SYS_LSEEK); + ERRNO_NEG(ret); + return ret; +} + +int +mkdir(const char *p, long mode) +{ + int ret = syscall(SA(p), mode, 0, 0, 0, SYS_MKDIR); + ERRNO_NZ(ret); + return ret; +} + +int +mknod(const char *p, mode_t m, dev_t d) +{ + if (MAJOR(d) == 0) + errx(-1, "bad major: 0"); + int ret = syscall(SA(p), SA(m), SA(d), 0, 0, SYS_MKNOD); + ERRNO_NZ(ret); + return ret; +} + +void * +mmap(void *addr, size_t len, int prot, int flags, int fd, long offset) +{ + ulong protflags = (ulong)prot << 32; + protflags |= flags; + long ret; + ret = syscall(SA(addr), SA(len), SA(protflags), SA(fd), + SA(offset), SYS_MMAP); + if (ret < 0 && -ret >= ERRNO_FIRST && -ret <= ERRNO_LAST) { + errno = -ret; + ret = (long)MAP_FAILED; + } + return (void *)ret; +} + +int +munmap(void *addr, size_t len) +{ + int ret = syscall(SA(addr), SA(len), 0, 0, 0, SYS_MUNMAP); + ERRNO_NZ(ret); + return ret; +} + +int +nanosleep(const struct timespec *timeout, struct timespec *remainder) +{ + int ret = syscall(SA(timeout), SA(remainder), 0, 0, 0, SYS_NANOSLEEP); + ERRNO_NZ(ret); + return ret; +} + +int +open(const char *path, int flags, ...) +{ + mode_t mode = 0; + if (flags & O_CREAT) { + va_list l; + va_start(l, flags); + mode = va_arg(l, mode_t); + va_end(l); + } + int ret = syscall(SA(path), flags, mode, 0, 0, SYS_OPEN); + ERRNO_NEG(ret); + return ret; +} + +int +pause(void) +{ + int ret = syscall(0, 0, 0, 0, 0, SYS_PAUSE); + errno = ret; + return -1; +} + +int +pipe(int pfds[2]) +{ + int ret = pipe2(pfds, 0); + ERRNO_NZ(ret); + return ret; +} + +int +pipe2(int pfds[2], int flags) +{ + int ret = syscall(SA(pfds), SA(flags), 0, 0, 0, SYS_PIPE2); + ERRNO_NZ(ret); + return ret; +} + +int +poll(struct pollfd *fds, nfds_t nfds, int timeout) +{ + struct pollfd *end = fds + nfds; + struct pollfd *p; + for (p = fds; p < end; p++) + p->revents = 0; + int ret = syscall(SA(fds), SA(nfds), SA(timeout), 0, 0, SYS_POLL); + ERRNO_NEG(ret); + return ret; +} + +ssize_t +pread(int fd, void *dst, size_t len, off_t off) +{ + ssize_t ret = syscall(SA(fd), SA(dst), SA(len), SA(off), 0, SYS_PREAD); + ERRNO_NEG(ret); + return ret; +} + +ssize_t +pwrite(int fd, const void *src, size_t len, off_t off) +{ + ssize_t ret = syscall(SA(fd), SA(src), SA(len), SA(off), 0, SYS_PWRITE); + ERRNO_NEG(ret); + return ret; +} + +long +read(int fd, void *buf, size_t c) +{ + int ret = syscall(SA(fd), SA(buf), SA(c), 0, 0, SYS_READ); + ERRNO_NEG(ret); + return ret; +} + +int +reboot(void) +{ + int ret = syscall(0, 0, 0, 0, 0, SYS_REBOOT); + ERRNO_NZ(ret); + return ret; +} + +ssize_t +recv(int fd, void *buf, size_t len, int flags) +{ + int ret = recvfrom(fd, buf, len, flags, NULL, NULL); + ERRNO_NEG(ret); + return ret; +} + +ssize_t +recvfrom(int fd, void *buf, size_t len, int flags, struct sockaddr *sa, + socklen_t *salen) +{ + if (len >= (1ULL << 32)) + errx(-1, "len too big"); + ulong flaglen = len << 32 | flags; + int ret = syscall(SA(fd), SA(buf), SA(flaglen), SA(sa), + SA(salen), SYS_RECVFROM); + ERRNO_NEG(ret); + return ret; +} + +ssize_t +recvmsg(int fd, struct msghdr *msg, int flags) +{ + ssize_t ret = syscall(SA(fd), SA(msg), SA(flags), 0, 0, SYS_RECVMSG); + ERRNO_NEG(ret); + return ret; +} + +int +rename(const char *old, const char *new) +{ + int ret = syscall(SA(old), SA(new), 0, 0, 0, SYS_RENAME); + ERRNO_NZ(ret); + return ret; +} + +ssize_t +send(int fd, const void *buf, size_t len, int flags) +{ + return sendto(fd, buf, len, flags, NULL, 0); +} + +ssize_t +sendto(int fd, const void *buf, size_t len, int flags, + const struct sockaddr *sa, socklen_t slen) +{ + if (len >= (1ULL << 32)) + errx(-1, "len too big"); + ulong flaglen = len << 32 | flags; + int ret = syscall(SA(fd), SA(buf), SA(flaglen), SA(sa), SA(slen), + SYS_SENDTO); + ERRNO_NEG(ret); + return ret; +} + +ssize_t +sendmsg(int fd, struct msghdr *msg, int flags) +{ + ssize_t ret = syscall(SA(fd), SA(msg), SA(flags), 0, 0, SYS_SENDMSG); + ERRNO_NEG(ret); + return ret; +} + +int +setrlimit(int res, const struct rlimit *rlp) +{ + int ret = syscall(SA(res), SA(rlp), 0, 0, 0, SYS_SETRLIMIT); + ERRNO_NEG(ret); + return ret; +} + +pid_t +setsid(void) +{ + printf("warning: no sessions or process groups yet\n"); + return getpid(); +} + +int +setsockopt(int a, int b, int c, const void *d, socklen_t e) +{ + static char *on[] = { +#define F(x) [x] = #x + F(SO_SNDTIMEO), + F(SO_ERROR), + F(SO_TYPE), + F(SO_REUSEADDR), + F(SO_KEEPALIVE), + F(SO_LINGER), + F(SO_SNDLOWAT), + F(TCP_NODELAY), +#undef F + }; + long ret = 0; + if (on[c] != NULL) { + errno = 0; + fprintf(stderr, "warning: setsockopt no-op for %s\n", on[c]); + } else { + ret = syscall(SA(a), SA(b), SA(c), SA(d), SA(e), + SYS_SETSOCKOPT); + ERRNO_NEG(ret); + } + return (int)ret; +} + +int +sigaction(int sig, const struct sigaction *act, struct sigaction *oact) +{ + printf("warning: no signals yet\n"); + return 0; + //int ret = syscall(SA(sig), SA(act), SA(oact), 0, 0, SYS_SIGACTION); + //ERRNO_NEG(ret); + //return ret; +} + +ssize_t +readv(int fd, const struct iovec *iovs, int niovs) +{ + ssize_t ret = syscall(SA(fd), SA(iovs), SA(niovs), 0, 0, SYS_READV); + ERRNO_NEG(ret); + return ret; +} + +void +(*signal(int sig, void (*f)(int)))(int) +{ + struct sigaction sa, oa; + memset(&sa, 0, sizeof(struct sigaction)); + sa.sa_handler = f; + sigaction(sig, &sa, &oa); + return oa.sa_handler; +} + +int +shutdown(int fd, int how) +{ + int ret = syscall(SA(fd), SA(how), 0, 0, 0, SYS_SHUTDOWN); + ERRNO_NZ(ret); + return ret; +} + +int +socket(int dom, int type, int proto) +{ + int ret = syscall(SA(dom), SA(type), SA(proto), 0, 0, SYS_SOCKET); + ERRNO_NEG(ret); + return ret; +} + +int +socketpair(int dom, int type, int proto, int p[2]) +{ + int ret = syscall(SA(dom), SA(type), SA(proto), SA(p), 0, + SYS_SOCKETPAIR); + ERRNO_NZ(ret); + return ret; +} + +int +stat(const char *path, struct stat *st) +{ + int ret = syscall(SA(path), SA(st), 0, 0, 0, SYS_STAT); + ERRNO_NZ(ret); + return ret; +} + +int +sync(void) +{ + int ret = syscall(0, 0, 0, 0, 0, SYS_SYNC); + ERRNO_NZ(ret); + return ret; +} + +long +sys_prof(long ptype, long events, long flags, long intperiod) +{ + long ret = syscall(ptype, events, flags, intperiod, 0, SYS_PROF); + ERRNO_NZ(ret); + return ret; +} + +long +sys_info(long n) +{ + long ret = syscall(n, 0, 0, 0, 0, SYS_INFO); + ERRNO_NEG(ret); + return ret; +} + +int +truncate(const char *p, off_t newlen) +{ + int ret = syscall(SA(p), SA(newlen), 0, 0, 0, SYS_TRUNC); + ERRNO_NZ(ret); + return ret; +} + +static int +_unlink(const char *path, int wantdir) +{ + int ret = syscall(SA(path), SA(wantdir), 0, 0, 0, SYS_UNLINK); + ERRNO_NZ(ret); + return ret; +} + +int +unlink(const char *path) +{ + return _unlink(path, 0); +} + +int +rmdir(const char *path) +{ + return _unlink(path, 1); +} + +pid_t +wait(int *status) +{ + pid_t ret = syscall(WAIT_ANY, SA(status), 0, 0, 0, SYS_WAIT4); + ERRNO_NEG(ret); + return ret; +} + +pid_t +waitpid(pid_t pid, int *status, int options) +{ + return wait4(pid, status, options, NULL); +} + +pid_t +wait4(pid_t pid, int *status, int options, struct rusage *r) +{ + pid_t ret = syscall(pid, SA(status), SA(options), SA(r), 0, + SYS_WAIT4); + ERRNO_NEG(ret); + return ret; +} + +pid_t +wait3(int *status, int options, struct rusage *r) +{ + pid_t ret = syscall(WAIT_ANY, SA(status), SA(options), SA(r), 0, + SYS_WAIT4); + ERRNO_NEG(ret); + return ret; +} + +long +write(int fd, const void *buf, size_t c) +{ + int ret = syscall(fd, SA(buf), SA(c), 0, 0, SYS_WRITE); + ERRNO_NEG(ret); + return ret; +} + +ssize_t +writev(int fd, const struct iovec *iovs, int niovs) +{ + ssize_t ret = syscall(SA(fd), SA(iovs), SA(niovs), 0, 0, SYS_WRITEV); + ERRNO_NEG(ret); + return ret; +} + +/* + * thread stuff + */ +void +tfork_done(long status) +{ + threxit(status); + errx(-1, "threxit returned"); +} + +int +tfork_thread(struct tfork_t *args, long (*fn)(void *), void *fnarg) +{ + int tid; + long flags = FORK_THREAD; + + // rbx and rbp are preserved across syscalls. i don't know how to + // specify rbp as a register contraint. + register ulong rbp asm("rbp") = (ulong)fn; + register ulong rbx asm("rbx") = (ulong)fnarg; + + asm volatile( + "movq %%rsp, %%r10\n" + "leaq 2(%%rip), %%r11\n" + "sysenter\n" + "cmpl $0, %%eax\n" + // parent or error + "jne 1f\n" + // child + "movq %5, %%rdi\n" + "call *%4\n" + "movq %%rax, %%rdi\n" + "call tfork_done\n" + "movq $0, 0x0\n" + "1:\n" + : "=a"(tid) + : "D"(args), "S"(flags), "0"(SYS_FORK), "r"(rbp), "r"(rbx) + : SYSCALL_CLOBBERS); + return tid; +} + +void +threxit(long status) +{ + syscall(SA(status), 0, 0, 0, 0, SYS_THREXIT); +} + +int +thrwait(int tid, long *status) +{ + if (tid <= 0) + errx(-1, "thrwait: bad tid %d", tid); + int ret = syscall(SA(tid), SA(status), 0, 0, 1, SYS_WAIT4); + return ret; +} + +static void * +mkstack(size_t size) +{ + const size_t pgsize = 1 << 12; + size += pgsize - 1; + size &= ~(pgsize - 1); + char *ret = mmap(NULL, size, PROT_READ | PROT_WRITE, + MAP_ANON | MAP_PRIVATE, -1, 0); + if (!ret) + return NULL; + return ret + size; +} + +struct pcargs_t { + void* (*fn)(void *); + void *arg; + void *stack; + void *tls; + ulong stksz; +}; + +static long +_pcreate(void *vpcarg) +{ + struct pcargs_t pcargs = *(struct pcargs_t *)vpcarg; + free(vpcarg); + long status; + status = (long)(pcargs.fn(pcargs.arg)); + free(pcargs.tls); + + // rbx and rbp are preserved across syscalls. i don't know how to + // specify rbp as a register contraint. + register ulong rbp asm("rbp") = SYS_THREXIT; + register long rbx asm("rbx") = status; + + asm volatile( + "movq %%rsp, %%r10\n" + "leaq 2(%%rip), %%r11\n" + "sysenter\n" + "cmpq $0, %%rax\n" + "je 1f\n" + "movq $0, 0x0\n" + "1:\n" + "movq %3, %%rax\n" + "movq %4, %%rdi\n" + "leaq 2(%%rip), %%r11\n" + "sysenter\n" + "movq $0, 0x1\n" + : + : "a"(SYS_MUNMAP), "D"(pcargs.stack), "S"(pcargs.stksz), + "r"(rbp), "r"(rbx) + : SYSCALL_CLOBBERS); + // not reached + return 0; +} + +int +pthread_create(pthread_t *t, pthread_attr_t *attrs, void* (*fn)(void *), + void *arg) +{ + // allocate and setup TLS space + if (!kinfo) + errx(-1, "nil kinfo"); + // +8 for the tls indirect pointer + void *newtls = malloc(kinfo->len + 8); + if (!newtls) + return ENOMEM; + char *tlsdata = newtls; + tlsdata += 8; + memmove(tlsdata, kinfo->freshtls, kinfo->len); + + char **tlsptr = newtls; + *tlsptr = tlsdata + kinfo->len; + + size_t stksz = _PTHREAD_DEFSTKSZ; + if (attrs) { + stksz = attrs->stacksize; + if (stksz > 1024*1024*128) + errx(-1, "big fat stack"); + const ulong pgsize = 1ul << 12; + if ((stksz % pgsize) != 0) + errx(-1, "stack size not aligned"); + } + int ret; + // XXX setup guard page + void *stack = mkstack(stksz); + if (!stack) { + ret = ENOMEM; + goto tls; + } + long tid; + struct tfork_t tf = { + .tf_tcb = tlsptr, + .tf_tid = &tid, + .tf_stack = stack, + }; + struct pcargs_t *pca = malloc(sizeof(struct pcargs_t)); + if (!pca) { + ret = ENOMEM; + goto errstack; + } + + pca->fn = fn; + pca->arg = arg; + pca->stack = stack - stksz; + pca->tls = newtls; + pca->stksz = stksz; + ret = tfork_thread(&tf, _pcreate, pca); + if (ret < 0) { + ret = -ret; + goto both; + } + *t = tid; + return 0; +both: + free(pca); +errstack: + munmap(stack - stksz, stksz); +tls: + free(newtls); + return ret; +} + +int +pthread_join(pthread_t t, void **retval) +{ + int ret = thrwait(t, (long *)retval); + if (ret < 0) + return -ret; + return 0; +} + +int +pthread_cancel(pthread_t t) +{ + errx(-1, "no imp"); +} + +int +pthread_mutex_init(pthread_mutex_t *m, const pthread_mutexattr_t *attr) +{ + if (attr) + errx(-1, "mutex attributes not supported"); + m->locks = 0; + return 0; +} + +int +pthread_mutex_lock(pthread_mutex_t *m) +{ + if (!dolock) + return 0; + // fast path + uint * const p = &m->locks; + uint old; + if ((old = __sync_val_compare_and_swap(p, 0, 1)) == 0) + return 0; + + while (1) { + // upgrade lock to "has waiters" + old = __sync_lock_test_and_set(p, 2); + // did we get lucky? + if (old == 0) + return 0; + + // sleep and acquire + int ret; + ret = futex(FUTEX_SLEEP, &m->locks, NULL, 2, NULL); + if (ret < 0) + return ret; + } +} + +int +pthread_mutex_unlock(pthread_mutex_t *m) +{ + uint old; + old = __sync_lock_test_and_set(&m->locks, 0); + + int ret = 0; + if (old == 2) + ret = futex(FUTEX_WAKE, &m->locks, NULL, 1, NULL); + return ret; +} + +int +pthread_mutex_destroy(pthread_mutex_t *mutex) +{ + return 0; +} + +int +pthread_once(pthread_once_t *octl, void (*fn)(void)) +{ + errx(-1, "pthread_once: no imp"); + return 0; +} + +pthread_t +pthread_self(void) +{ + return gettid(); +} + +// XXX use fancy new futexes +int +pthread_barrier_init(pthread_barrier_t *b, pthread_barrierattr_t *attr, uint c) +{ + if (attr) + errx(-1, "barrier attributes not supported"); + b->target = c; + b->current = 0; + return 0; +} + +int +pthread_barrier_destroy(pthread_barrier_t *b) +{ + return 0; +} + +int +pthread_barrier_wait(pthread_barrier_t *b) +{ + uint m = 1ull << 31; + uint c; + while (1) { + uint o = b->current; + uint n = o + 1; + if ((o & m) != 0) { + asm volatile("pause":::"memory"); + continue; + } + c = n; + if (__sync_bool_compare_and_swap(&b->current, o, n)) + break; + } + + if (c == b->target) { + while (1) { + uint o = b->current; + uint n = (b->current - 1) | m; + if (__sync_bool_compare_and_swap(&b->current, o, n)) + break; + } + return PTHREAD_BARRIER_SERIAL_THREAD; + } + + while ((b->current & m) == 0) + asm volatile("pause":::"memory"); + + c = __sync_add_and_fetch(&b->current, -1); + if (c == m) + b->current = 0; + return 0; +} + +int +pthread_cond_broadcast(pthread_cond_t *c) +{ + __sync_add_and_fetch(&c->gen, 1); + return futex(FUTEX_WAKE, &c->gen, NULL, -1, NULL); +} + +int +pthread_cond_destroy(pthread_cond_t *c) +{ + return 0; +} + +int +pthread_cond_init(pthread_cond_t *c, const pthread_condattr_t *ca) +{ + if (ca) + errx(-1, "cond attritbutes not supported"); + c->gen = 0; + c->bcast = 0; + return 0; +} + +int +pthread_cond_timedwait(pthread_cond_t *c, pthread_mutex_t *m, + const struct timespec *t) +{ + uint last = c->gen; + asm volatile("":::"memory"); + int ret; + if ((ret = pthread_mutex_unlock(m))) + return ret; + + int moveq; + if ((moveq = futex(FUTEX_SLEEP, &c->gen, NULL, last, t)) < 0) + return moveq; + + if ((ret = pthread_mutex_lock(m))) + return ret; + + // on pthread_cond_broadcast(3), move cond's sleep queue to my mutex + if (moveq) { + // make sure threads moved to mutex wait queue are woken up + c->bcast = 1; + ret = futex(FUTEX_CNDGIVE, &c->gen, &m->locks, 0, NULL); + } + if (c->bcast) + m->locks = 2; + return ret; +} + +int +pthread_cond_wait(pthread_cond_t *c, pthread_mutex_t *m) +{ + return pthread_cond_timedwait(c, m, NULL); +} + +int +pthread_cond_signal(pthread_cond_t *c) +{ + __sync_add_and_fetch(&c->gen, 1); + return futex(FUTEX_WAKE, &c->gen, NULL, 1, NULL); +} + +int +pthread_sigmask(int how, const sigset_t *set, sigset_t *oset) +{ + printf("warning: signals not implemented\n"); + return 0; +} + +int +pthread_setcancelstate(int state, int *oldstate) +{ + return 0; +} + +int +pthread_setcanceltype(int type, int *oldtype) +{ + return 0; +} + +int +pthread_attr_destroy(pthread_attr_t *a) +{ + return 0; +} + +int +pthread_attr_init(pthread_attr_t *a) +{ + memset(a, 0, sizeof(pthread_attr_t)); + a->stacksize = _PTHREAD_DEFSTKSZ; + return 0; +} + +int +pthread_attr_getstacksize(pthread_attr_t *a, size_t *sz) +{ + *sz = a->stacksize; + return 0; +} + +int +pthread_attr_setstacksize(pthread_attr_t *a, size_t sz) +{ + a->stacksize = sz; + return 0; +} + +/* + * posix + */ + +int +_posix_dups(const posix_spawn_file_actions_t *fa) +{ + int i; + for (i = 0; i < fa->dup2slot; i++) { + int ret; + int from = fa->dup2s[i].from; + int to = fa->dup2s[i].to; + if ((ret = dup2(from, to)) < 0) + return ret; + } + return 0; +} + +static char *_environ[] = {"", NULL}; + +int +posix_spawn(pid_t *pid, const char *path, const posix_spawn_file_actions_t *fa, + const posix_spawnattr_t *sa, char *const argv[], char *const envp[]) +{ + if (sa) + errx(-1, "spawnattr not supported"); + if (envp != NULL) + if (envp != _environ || !envp[0] || + envp[0][0] != '\0' || envp[1] != NULL) + errx(-1, "environ not supported"); + pid_t p = fork(); + if (p < 0) + return p; + + int child = !p; + if (child) { + if (fa) { + if (_posix_dups(fa)) + errx(127, "posix_spawn dups failed"); + } + execv(path, argv); + errx(127, "posix_spawn exec failed"); + } + + if (pid) + *pid = p; + + return 0; +} + +int +posix_spawn_file_actions_adddup2(posix_spawn_file_actions_t *fa, int ofd, int newfd) +{ + if (ofd < 0 || newfd < 0) + return -EINVAL; + + size_t nelms = sizeof(fa->dup2s)/sizeof(fa->dup2s[0]); + int myslot = fa->dup2slot++; + if (myslot < 0 || myslot >= nelms) + errx(-1, "bad dup2slot: %d", myslot); + + fa->dup2s[myslot].from = ofd; + fa->dup2s[myslot].to = newfd; + return 0; +} + +int +posix_spawn_file_actions_destroy(posix_spawn_file_actions_t *fa) +{ + return 0; +} + +int +posix_spawn_file_actions_init(posix_spawn_file_actions_t *fa) +{ + memset(fa, 0, sizeof(posix_spawn_file_actions_t)); + return 0; +} + +/* + * libc + */ +void +abort(void) +{ + errx(-1, "abort"); +} + +int +atoi(const char *n) +{ + return (int)strtol(n, NULL, 10); +} + +double +ceil(double x) +{ + if (x == trunc(x)) + return x; + return trunc(x + 1); +} + +int +creat(const char *p, mode_t m) +{ + return open(p, O_WRONLY | O_CREAT | O_TRUNC, m); +} + +char * +ctime(const time_t *t) +{ + static char cbuf[64]; + return ctime_r(t, cbuf); +} + +char * +ctime_r(const time_t *t, char *buf) +{ + printf("warning: ctime_r not implemented\n"); + char *msg = "i dont wish to write this"; + memmove(buf, msg, strlen(msg) + 1); + return buf; +} + +void +err(int eval, const char *fmt, ...) +{ + dolock = 0; + fprintf(stderr, "%s: ", __progname); + va_list ap; + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + // errno is dumb + int neval = errno; + char p[NL_TEXTMAX]; + strerror_r(neval, p, sizeof(p)); + fprintf(stderr, ": %s\n", p); + exit(eval); +} + +void +errx(int eval, const char *fmt, ...) +{ + dolock = 0; + fprintf(stderr, "%s: ", __progname); + va_list ap; + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + fprintf(stderr, "\n"); + exit(eval); +} + +void +exit(int ret) +{ + if (fflush(NULL)) + fprintf(stderr, "warning: failed to fflush\n"); + _exit(ret); +} + +int +fprintf(FILE *f, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + int ret; + ret = vfprintf(f, fmt, ap); + va_end(ap); + return ret; +} + +static int +_ferror(FILE *f) +{ + return f->error; +} + +int +ferror(FILE *f) +{ + pthread_mutex_lock(&f->mut); + int ret = _ferror(f); + pthread_mutex_unlock(&f->mut); + return ret; +} + +int +fileno(FILE *f) +{ + pthread_mutex_lock(&f->mut); + int ret = f->fd; + pthread_mutex_unlock(&f->mut); + return ret; +} + +static int +_feof(FILE *f) +{ + return f->eof; +} + +int +feof(FILE *f) +{ + pthread_mutex_lock(&f->mut); + int ret = _feof(f); + pthread_mutex_unlock(&f->mut); + return ret; +} + +static struct { + FILE *head; + pthread_mutex_t mut; +} _allfiles; + +static FILE _stdin, _stdout, _stderr; +FILE *stdin = &_stdin, *stdout = &_stdout, *stderr = &_stderr; + +static void fdinit(void) +{ +#define FDINIT(f, fdn, bt) do { \ + f.fd = fdn; \ + f.p = &f.buf[0]; \ + f.end = f.p; \ + f.lnext = _allfiles.head; \ + f.lprev = NULL; \ + f.btype = bt; \ + if (pthread_mutex_init(&f.mut, NULL)) \ + pmsg("mutex init", 9); \ + if (_allfiles.head) \ + _allfiles.head->lprev = &f; \ + _allfiles.head = &f; \ + } while (0) + + FDINIT(_stdin, 0, _IONBF); + FDINIT(_stdout, 1, _IONBF); + FDINIT(_stderr, 2, _IONBF); +#undef FDINIT + pthread_mutex_init(&_allfiles.mut, NULL); +} + +static int +_fflush(FILE *f) +{ + if (!f->writing) + return 0; + char * const bst = &f->buf[0]; + size_t len = f->p - bst; + ssize_t r; + if ((r = write(f->fd, bst, len)) < 0) { + f->error = errno; + return EOF; + } else if (r != len) { + printf("asked: %zu, actual: %zu\n", len, r); + return EOF; + } + f->p = f->end = bst; + return 0; +} + +int +fflush(FILE *f) +{ + if (f == NULL) { + int ret = 0; + pthread_mutex_lock(&_allfiles.mut); + FILE *f = _allfiles.head; + while (f != NULL) { + ret |= fflush(f); + f = f->lnext; + } + pthread_mutex_unlock(&_allfiles.mut); + return ret; + } + + pthread_mutex_lock(&f->mut); + int ret = _fflush(f); + pthread_mutex_unlock(&f->mut); + return ret; +} + +static FILE * +_fopen(const char *path, int gfd, const char *mode) +{ + int flags = -1; + int plus = strchr(mode, '+') != NULL; + int x = strchr(mode, 'x') != NULL; + switch (mode[0]) { + case 'r': + if (plus) + flags = O_RDWR; + else + flags = O_RDONLY; + break; + case 'w': + if (plus) + flags = O_RDWR | O_CREAT | O_TRUNC; + else + flags = O_WRONLY | O_CREAT | O_TRUNC; + if (x) + flags |= O_EXCL; + break; + case 'a': + if (plus) + flags = O_RDWR | O_CREAT | O_APPEND; + else + flags = O_WRONLY | O_CREAT | O_APPEND; + if (x) + flags |= O_EXCL; + break; + default: + errno = EINVAL; + return NULL; + } + + if (strchr(mode, 'e') != NULL) + flags |= O_CLOEXEC; + + FILE *ret = malloc(sizeof(FILE)); + if (ret == NULL) { + errno = ENOMEM; + return NULL; + } + memset(ret, 0, sizeof(FILE)); + + int fd = gfd; + if (path) + fd = open(path, flags); + else + if (fd < 0) + errno = EBADF; + if (fd < 0) { + free(ret); + return NULL; + } + + pthread_mutex_init(&ret->mut, NULL); + ret->fd = fd; + ret->p = &ret->buf[0]; + ret->end = ret->p; + // POSIX: fully-buffered iff is not an interactive device + ret->btype = _IONBF; + struct stat st; + if (fstat(ret->fd, &st) == 0 && S_ISREG(st.st_mode)) + ret->btype = _IOFBF; + + // insert into global list + pthread_mutex_lock(&_allfiles.mut); + ret->lnext = _allfiles.head; + if (_allfiles.head) + _allfiles.head->lprev = ret; + _allfiles.head = ret; + pthread_mutex_unlock(&_allfiles.mut); + return ret; +} + +FILE * +fdopen(int fd, const char *mode) +{ + return _fopen(NULL, fd, mode); +} + +FILE * +fopen(const char *path, const char *mode) +{ + return _fopen(path, -1, mode); +} + +int +fclose(FILE *f) +{ + // remove from global list + pthread_mutex_lock(&_allfiles.mut); + if (f->lnext) + f->lnext->lprev = f->lprev; + if (f->lprev) + f->lprev->lnext = f->lnext; + if (_allfiles.head == f) + _allfiles.head = f->lnext; + pthread_mutex_unlock(&_allfiles.mut); + + pthread_mutex_lock(&f->mut); + _fflush(f); + + // try to crash buggy programs that use f after us + f->fd = -1; + f->p = (void *)1; + f->end = NULL; + pthread_mutex_unlock(&f->mut); + + pthread_mutex_destroy(&f->mut); + free(f); + + return 0; +} + +static size_t +_fread1(void *dst, size_t left, FILE *f) +{ + if (!(f->btype == _IOFBF || f->btype == _IONBF)) + errx(-1, "noimp"); + // unbuffered stream + if (f->btype == _IONBF) { + ssize_t r = read(f->fd, dst, left); + if (r < 0) { + f->error = errno; + return 0; + } + return r; + } + + // switch to read mode + if (_fflush(f)) + return 0; + f->writing = 0; + + // fill buffer? + if (f->p == f->end && !_feof(f) && !_ferror(f)) { + char * const bst = &f->buf[0]; + size_t r; + if ((r = read(f->fd, bst, sizeof(f->buf))) < 0) { + f->error = errno; + return 0; + } else if (r == 0) { + f->eof = 1; + return 0; + } + f->p = bst; + f->end = bst + r; + } + + size_t ub = MIN(left, f->end - f->p); + memmove(dst, f->p, ub); + f->p += ub; + + return ub; +} + +size_t +fread(void *dst, size_t sz, size_t mem, FILE *f) +{ + if (sz == 0) + return 0; + if (mem > ULONG_MAX / sz) { + errno = -EOVERFLOW; + return 0; + } + + pthread_mutex_lock(&f->mut); + + size_t ret = 0; + size_t left = mem * sz; + while (left > 0 && !_feof(f) && !_ferror(f)) { + size_t c = _fread1(dst, left, f); + //if (c == 0) + // break; + dst += c; + left -= c; + ret += c; + } + + pthread_mutex_unlock(&f->mut); + return ret/sz; +} + +static size_t +_fwrite1(const void *src, size_t left, FILE *f) +{ + if (!(f->btype == _IOFBF || f->btype == _IONBF)) + errx(-1, "noimp"); + // unbuffered stream + if (f->btype == _IONBF) { + ssize_t r = write(f->fd, src, left); + if (r < 0) { + f->error = errno; + return 0; + } + return r; + } + + char * const bst = &f->buf[0]; + char * const bend = bst + sizeof(f->buf); + + // switch to write mode (discarding unread data and using undefined + // offset) + if (!f->writing) { + f->writing = 1; + f->p = f->end = bst; + } + + // buffer full? + if (f->p == bend) { + if (_fflush(f)) + return 0; + f->p = f->end = bst; + } + size_t put = MIN(bend - f->p, left); + memmove(f->p, src, put); + f->p += put; + f->end = MAX(f->p, f->end); + return put; +} + +size_t +fwrite(const void *src, size_t sz, size_t mem, FILE *f) +{ + if (sz == 0) + return 0; + if (mem > ULONG_MAX / sz) { + errno = -EOVERFLOW; + return 0; + } + + pthread_mutex_lock(&f->mut); + + size_t ret = 0; + size_t left = mem * sz; + while (left > 0 && !_ferror(f)) { + size_t c = _fwrite1(src, left, f); + src += c; + left -= c; + ret += c; + } + + pthread_mutex_unlock(&f->mut); + return ret/sz; +} + +static int +_fgetc(FILE *f) +{ + char buf; + if (_fread1(&buf, 1, f) != 1) + return EOF; + return buf; +} + +int +fgetc(FILE *f) +{ + pthread_mutex_lock(&f->mut); + int ret = _fgetc(f); + pthread_mutex_unlock(&f->mut); + return ret; +} + +static int +_ungetc(int c, FILE *f) +{ + const char * const bst = &f->buf[0]; + if (f->p == bst) + return EOF; + unsigned char put = (unsigned char)c; + f->p--; + *f->p = put; + f->eof = 0; + return put; +} + +int +ungetc(int c, FILE *f) +{ + if (c == EOF) + return c; + + pthread_mutex_lock(&f->mut); + int ret = _ungetc(c, f); + pthread_mutex_unlock(&f->mut); + return ret; +} + +char * +fgets(char *buf, int size, FILE *f) +{ + if (size == 0) + return NULL; + // get room for nul + size--; + pthread_mutex_lock(&f->mut); + + char *ret = NULL; + size_t c = 0; + while (c < size) { + int ch = _fgetc(f); + if (ch == EOF) { + if (c > 0) + ret = buf; + goto out; + } + buf[c++] = ch; + if (ch == '\n') + break; + } + ret = buf; +out: + buf[c] = '\0'; + pthread_mutex_unlock(&f->mut); + return ret; +} + +off_t +ftello(FILE *f) +{ + return lseek(f->fd, 0, SEEK_CUR); +} + +int +ftruncate(int fd, off_t newlen) +{ + int ret = syscall(SA(fd), SA(newlen), 0, 0, 0, SYS_FTRUNC); + ERRNO_NZ(ret); + return ret; +} + +int +futex(const int op, void *fut, void *fut2, int aux, const struct timespec *ts) +{ + int ret = syscall(SA(op), SA(fut), SA(fut2), SA(aux), SA(ts), + SYS_FUTEX); + ERRNO_NEG(ret); + return ret; +} + +int +fsync(int fd) +{ + sync(); + return 0; +} + +static void +_gcfrac(struct gcfrac_t *r) +{ + struct timeval tv; + if (gettimeofday(&tv, NULL)) + err(-1, "gettimeofday"); + long nowms = tv.tv_sec*1000 + tv.tv_usec/1000; + long gcwork = sys_info(SINFO_GCMS); + long markt = sys_info(SINFO_GCMARKTIME); + long sweept = sys_info(SINFO_GCSWEEPTIME); + long wbtime = sys_info(SINFO_GCWBARTIME); + if (gcwork == -1 || markt == -1 || sweept == -1 || wbtime == -1) + err(-1, "sysinfo"); + + r->startms = nowms; + r->gcworkms = gcwork; + r->details.wbms = wbtime; + r->details.bgsweepms = sweept; + r->details.markms = markt; +} + +struct gcfrac_t +gcfracst(void) +{ + struct gcfrac_t ret; + _gcfrac(&ret); + return ret; +} + +double +gcfracend(struct gcfrac_t *of, long *marke, long *sweepe, + long *wbe) +{ + struct gcfrac_t nf; + _gcfrac(&nf); + if (marke) + *marke = nf.details.markms - of->details.markms; + if (sweepe) + *sweepe = nf.details.bgsweepms - of->details.bgsweepms; + if (wbe) + *wbe = nf.details.wbms - of->details.wbms; + //return (double)(nf.gcworkms - of->gcworkms)/(nf.startms - of->startms); + return NAN; +} + +char *optarg; +int optind = 1; + +int +getopt(int argc, char * const *argv, const char *optstring) +{ + optarg = NULL; + for (; optind < argc && !argv[optind]; optind++) + ; + if (optind >= argc) + return -1; + char *ca = argv[optind]; + if (ca[0] != '-') + return -1; + optind++; + const char wut = '?'; + char *o = strchr(optstring, ca[1]); + if (!o) + return wut; + int needarg = o[1] == ':'; + if (!needarg) + return ca[1]; + if (ca[2] != '\0') { + optarg = &ca[2]; + return ca[1]; + } + const char argwut = optstring[0] == ':' ? ':' : wut; + if (optind >= argc || argv[optind][0] == '-') + return argwut; + optarg = argv[optind]; + optind++; + return ca[1]; +} + +int +islower(int c) +{ + return c >= 'a' && c <= 'z'; +} + +int +isupper(int c) +{ + return c >= 'A' && c <= 'Z'; +} + +int +isalpha(int c) +{ + return isupper(c) || islower(c); +} + +int +isdigit(int c) +{ + return c >= '0' && c <= '9'; +} + +int +isprint(int c) +{ + return isalpha(c) || isdigit(c) || ispunct(c); +} + +static char _punct[255] = { + ['!'] = 1, ['"'] = 1, ['#'] = 1, ['$'] = 1, ['%'] = 1, ['&'] = 1, + ['\'']= 1, ['('] = 1, [')'] = 1, ['*'] = 1, ['+'] = 1, [','] = 1, + ['-'] = 1, ['.'] = 1, ['/'] = 1, [':'] = 1, [';'] = 1, ['<'] = 1, + ['='] = 1, ['>'] = 1, ['?'] = 1, ['@'] = 1, ['['] = 1, ['\\']= 1, + [']'] = 1, ['^'] = 1, ['_'] = 1, ['`'] = 1, ['{'] = 1, ['|'] = 1, + ['}'] = 1, ['~'] = 1, +}; + +int +ispunct(int c) +{ + return _punct[(unsigned char)c]; +} + +int +isspace(int c) +{ + return c == ' ' || c == '\f' || c == '\n' || c == '\t' || c == '\v' || + c == '\r'; +} + +int +isxdigit(int c) +{ + int d = tolower(c); + return isdigit(c) || (d >= 'a' && d <= 'f'); +} + +double +log(double x) +{ + if (x < 1) + errx(-1, "no imp"); + const double ln2 = 0.693147; + double ret = 0; + while (x >= 2) { + x /= 2; + ret += ln2; + } + if (x > 1) + ret += x - 1; + return ret; +} + +dev_t +makedev(uint maj, uint min) +{ + return (ulong)maj << 32 | min; +} + +int +memcmp(const void *q, const void *p, size_t sz) +{ + const long *e1 = (long *)q; + const long *e2 = (long *)p; + while (sz >= 8 && *e1 == *e2) + sz -= 8, e1++, e2++; + + const char *s1 = (char *)e1; + const char *s2 = (char *)e2; + while (sz && *s1 == *s2) + sz--, s1++, s2++; + if (!sz) + return 0; + return *s1 - *s2; +} + +void * +memcpy(void *dst, const void *src, size_t n) +{ + return memmove(dst, src, n); +} + +void * +memmove(void *dst, const void *src, size_t n) +{ + char *d = dst; + const char *s = src; + if (d == s || n == 0) + return d; + if (d > s && d <= s + n) { + // copy backwards + s += n; + d += n; + if (n >= 8) { + long *dst8 = (long *)d; + const long *src8 = (const long *)s; + for (; n >= 8; n -= 8) + *--dst8 = *--src8; + d = (char *)dst8; + s = (const char *)src8; + } + while (n--) + *--d = *--s; + return d; + } + if (n >= 8) { + long *dst8 = dst; + const long *src8 = src; + for (; n >= 8; n -= 8) + *dst8++ = *src8++; + d = (char *)dst8; + s = (const char *)src8; + } + while (n--) + *d++ = *s++; + return dst; +} + +void * +memset(void *d, int c, size_t n) +{ + if (n == 0) + return d; + long *pl = d; + if (n >= 8) { + ulong c8 = (uchar)c; + c8 = c8 | c8 << 8; + c8 |= c8 << 16; + c8 |= c8 << 32; + for (; n >= 8; n -= 8) + *pl++ = c8; + } + char *pc = (char *)pl; + char v = (char)c; + while (n--) + *pc++ = v; + return d; +} + +int +mkstemp(char *t) +{ + // XXX + static uint seed; + if (seed == 0) + seed = (uint)time(NULL); + size_t l = strlen(t); + if (l < 6) + goto inval; + char *oe = t + l - 6; + if (strncmp(oe, "XXXXXX", 6) != 0) + goto inval; + int fails = 0; + for (;;) { + char *e = oe; + int i; + for (i = 0; i < 6; i++) { + uint n = rand_r(&seed) % 26*2; + if (n >= 26) + *e = 'A' + n - 26; + else + *e = 'a' + n; + e++; + } + int fd = open(t, O_RDWR | O_CREAT | O_EXCL, 0600); + if (fd != -1) + return fd; + else if (errno != EEXIST) + return -1; + else if (fails++ == 10) + return -1; + } +inval: + errno = EINVAL; + return -1; +} + +DIR * +fdopendir(int fd) +{ + #define BSIZE 4096 + + struct stat st; + if (fstat(fd, &st) == -1) + return NULL; + if (!S_ISDIR(st.st_mode)) { + errno = ENOTDIR; + return NULL; + } + // allocate enough space for all dirents; count them. + if (lseek(fd, 0, SEEK_SET) == -1) + return NULL; + // on disk format of directory entries + struct __attribute__((packed)) _dirent_t { + char name[_POSIX_NAME_MAX]; + ulong inum; + }; + const int nde = BSIZE/sizeof(struct _dirent_t); + size_t c = 0; + char buf[BSIZE]; + ssize_t r; + while ((r = read(fd, buf, sizeof(buf))) > 0) { + int i; + for (i = 0; i < nde; i++) { + struct _dirent_t *de = (struct _dirent_t *)buf + i; + if (de->name[0] != '\0') + c++; + } + } + if (r == -1) + return NULL; + if (c > (ULONG_MAX - sizeof(DIR))/sizeof(struct dirent)) + errx(-1, "very fat dir"); + DIR *ret = malloc(sizeof(DIR) + c*sizeof(struct dirent)); + if (!ret) + return NULL; + + // copy dirents into ret + ret->fd = fd; + ret->nent = (int)c; + ret->cent = 0; + if (lseek(fd, 0, SEEK_SET) == -1) + goto out; + struct dirent *p = &ret->dents[0]; + while ((r = read(fd, buf, sizeof(buf))) > 0) { + int i; + for (i = 0; i < nde; i++) { + struct _dirent_t *de = (struct _dirent_t *)buf + i; + if (de->name[0] != '\0') { + p->d_ino = de->inum; + strncpy(p->d_name, de->name, + sizeof(p->d_name)); + p++; + } + } + } + if (r == -1) + goto out; + return ret; +out: + free(ret); + return NULL; +} + +DIR * +opendir(const char *path) +{ + int fd = open(path, O_RDONLY | O_DIRECTORY); + if (fd == -1) + return NULL; + return fdopendir(fd); +} + +int +closedir(DIR *d) +{ + int fd = d->fd; + free(d); + return close(fd); +} + +struct dirent * +readdir(DIR *d) +{ + static struct dirent _ret; + struct dirent *ret; + readdir_r(d, &_ret, &ret); + return ret; +} + +// POSIX: returns error number, not -1 +int +readdir_r(DIR *d, struct dirent *entry, struct dirent **ret) +{ + if (d->cent == d->nent) { + *ret = NULL; + return 0; + } + struct dirent *src = &d->dents[d->cent++]; + *entry = *src; + *ret = entry; + return 0; +} + +void +rewinddir(DIR *d) +{ + d->cent = 0; +} + +struct { + const char *prefix; + int pid; + int ndelay; + int fac; + ulong mask; +} _slogopts = {.fac = LOG_USER, .mask = -1}; + +void +openlog(const char *ident, int logopt, int fac) +{ + _slogopts.prefix = ident; + _slogopts.fac = fac; + if (logopt & LOG_PID) + _slogopts.pid = 1; + if (logopt & LOG_NDELAY) + _slogopts.ndelay = 1; +} + +size_t +strlen(const char *msg) +{ + size_t ret = 0; + while (*msg++) + ret++; + return ret; +} + +int +strcmp(const char *s1, const char *s2) +{ + while (*s1 && *s1 == *s2) + s1++, s2++; + return *s1 - *s2; +} + +int +strcoll(const char *s1, const char *s2) +{ + return strcmp(s1, s2); +} + +int +strncmp(const char *s1, const char *s2, size_t n) +{ + while (n && *s1 && *s1 == *s2) + n--, s1++, s2++; + if (n == 0) + return 0; + return *s1 - *s2; +} + +static void +pmsg(char *msg, long sz) +{ + write(1, msg, sz); +} + +static int +wc(char *p, char *end, char c) +{ + if (p < end) { + *p = c; + return 1; + } + return 0; +} + +static char +numtoch(char n) +{ + char c = n; + if (n < 10) + c += '0'; + else + c += 'a' - 10; + return c; +} + +static int +putn(char *p, char *end, ulong n, int base) +{ + if (n == 0) { + wc(p, end, '0'); + return 1; + } + char buf[21]; + int i = 0; + while (n) { + int left = n % base; + buf[i++] = numtoch(left); + n /= base; + } + int ret = i; + while (i--) + p += wc(p, end, buf[i]); + return ret; +} + +static int +_vprintf(const char *fmt, va_list ap, char *dst, char *end) +{ + const char *start = dst; + char c; + + c = *fmt; + while (c && dst < end) { + if (c != '%') { + dst += wc(dst, end, c); + fmt++; + c = *fmt; + continue; + } + + fmt++; + int prehex = 0; + int done = 0; + int longmode = 0; + int sig = 1; + int precmode = 0; + int prec = 6; + while (!done && *fmt && !isspace(*fmt)) { + char t = *fmt; + fmt++; + if (isdigit(t) && precmode) { + char *nend; + long newprec = strtol(fmt - 1, &nend, 10); + if (newprec > 0 || t == '0') { + prec = newprec; + fmt = nend; + continue; + } + } + switch (t) { + case '%': + dst += wc(dst, end, '%'); + done = 1; + break; + case '#': + prehex = 1; + break; + case '.': + precmode = 1; + break; + case 'z': + case 'l': + longmode = 1; + break; + case 'u': + sig = 0; + case 'd': + { + ulong n; + if (longmode) + n = va_arg(ap, ulong); + else + n = (ulong)(long)va_arg(ap, int); + if (sig && (long)n < 0) { + dst += wc(dst, end, '-'); + n = -n; + } + dst += putn(dst, end, n, 10); + done = 1; + break; + } + case 'g': + case 'f': + { + double n; + // floats are promoted to double when used for + // a ... argument + n = va_arg(ap, double); + if (isnan(n)) { + dst += wc(dst, end, 'n'); + dst += wc(dst, end, 'a'); + dst += wc(dst, end, 'n'); + done = 1; + break; + } + if (isinf(n)) { + dst += wc(dst, end, 'i'); + dst += wc(dst, end, 'n'); + dst += wc(dst, end, 'f'); + done = 1; + break; + } + if (n < 0) { + dst += wc(dst, end, '-'); + n = -n; + } + dst += putn(dst, end, (ulong)n, 10); + if (prec == 0) { + done = 1; + break; + } + dst += wc(dst, end, '.'); + n -= (ulong)n; + for (; prec > 0; prec--) { + n *= 10; + dst += putn(dst, end, (ulong)n, 10); + n -= (ulong)n; + } + done = 1; + break; + } + case 'p': + longmode = 1; + prehex = 1; + case 'x': + { + if (prehex) { + dst += wc(dst, end, '0'); + dst += wc(dst, end, 'x'); + } + ulong n; + if (longmode) + n = va_arg(ap, ulong); + else + n = (ulong)(uint)va_arg(ap, int); + dst += putn(dst, end, n, 16); + done = 1; + break; + } + case 'c': + dst += wc(dst, end, (char)va_arg(ap, int)); + done = 1; + break; + case 's': + { + char *s = va_arg(ap, char *); + if (!s) { + dst += wc(dst, end, '('); + dst += wc(dst, end, 'n'); + dst += wc(dst, end, 'i'); + dst += wc(dst, end, 'l'); + dst += wc(dst, end, ')'); + done = 1; + break; + } + while (*s && (!precmode || prec-- > 0)) + dst += wc(dst, end, *s++); + done = 1; + break; + } + default: + //errx(-1, "unsupported printf format: %c\n", t); + dst += wc(dst, end, '?'); + break; + } + } + c = *fmt; + prehex = 0; + } + + if (dst > end) + dst = end - 1; + *dst = '\0'; + return dst - start; +} + +int +vsnprintf(char *dst, size_t sz, const char *fmt, va_list ap) +{ + return _vprintf(fmt, ap, dst, dst + sz); +} + +static int +_vscanf(const char *src, const char * const end, const char *fmt, va_list ap) +{ + int ret = 0; + while (*fmt && src < end) { + if (isspace(*fmt)) { + fmt++; + while (isspace(*src)) + src++; + continue; + } + if (*fmt != '%') { + if (*src != *fmt) + return ret; + fmt++; + src++; + continue; + } + fmt++; + int done = 0; + int longmode = 0; + int hexmode = 0; + while (!done) { + switch (*fmt) { + case '%': + if (*src != '%') + return ret; + src++; + done = 1; + break; + case 'l': + longmode = 1; + break; + case 'x': + hexmode = 1; + case 'u': + case 'd': + { + char *eptr; + long read = strtol(src, &eptr, hexmode ? 16 : 10); + if (src == eptr) + return ret; + if (longmode) { + long *p = va_arg(ap, long *); + *p = read; + } else { + int *p = va_arg(ap, int *); + *p = (int)read; + } + src = eptr; + done = 1; + ret++; + } + break; + case 'g': + case 'f': + { + char *eptr; + long double read = strtold(src, &eptr); + if (src == eptr) + return ret; + if (longmode) { + double *p = va_arg(ap, double *); + *p = (double)read; + } else { + float *p = va_arg(ap, float *); + *p = (float)read; + } + src = eptr; + done = 1; + ret++; + } + break; + default: + errx(-1, "unsupported scanf format: %c", *fmt); + } + fmt++; + } + } + return ret; +} + +int +vsscanf(const char *src, const char *fmt, va_list ap) +{ + const char * const end = strchr(src, '\0'); + if (!end) + errx(-1, "no null"); + return _vscanf(src, end, fmt, ap); +} + +int +printf(const char *fmt, ...) +{ + va_list ap; + int ret; + + va_start(ap, fmt); + ret = vfprintf(stdout, fmt, ap); + va_end(ap); + + return ret; +} + +double +pow(double x, double y) +{ + long exp = (long)y; + if (y - exp != 0) + errx(-1, "shitty pow(3) only accepts integer exponents"); + if (exp == 0) + return 1; + int inv = 0; + if (exp < 0) { + exp = -exp; + inv = 1; + } + double ret = 1; + while (exp--) + ret *= x; + if (inv) + return 1.0/ret; + return ret; +} + +void +perror(const char *str) +{ + char *e = strerror(errno); + if (str) + fprintf(stderr, "%s: %s\n", str, e); + else + fprintf(stderr, "%s\n", e); +} + +static void _qsort(void *_base, size_t elms, size_t esz, + int (*cmp)(const void *, const void *), char *scratch, char *pivot) +{ + if (elms <= 1) + return; + char *base = _base; + if (elms == 2) { + if (cmp(base, base+esz) > 0) { + memmove(pivot, base, esz); + memmove(base, base+esz, esz); + memmove(base+esz, pivot, esz); + } + return; + } + // the first element is the pivot + memmove(pivot, base, esz); + // sort to scratch + char *lh, *hh; + size_t l, h; + l = h = 0; + lh = scratch; + hh = (char *)scratch + (elms - 1)*esz; + size_t i; + char *cur = (char *)base + esz; + for (i = 1; i < elms; i++, cur += esz) { + int c = cmp(cur, pivot); + if (c < 0) { + memmove(lh, cur, esz); + lh += esz; + l++; + } else { + memmove(hh, cur, esz); + hh -= esz; + h++; + } + } + //copy scratch back to base + void *plow = base; + memmove(plow, scratch, l*esz); + + void *piv = base + l*esz; + memmove(piv, pivot, esz); + + void *phigh = piv + esz; + size_t tail = elms - h; + memmove(phigh, scratch + tail*esz, h*esz); + + _qsort(plow, l, esz, cmp, scratch, pivot); + _qsort(phigh, h, esz, cmp, scratch, pivot); +} + +void +qsort(void *base, size_t elms, size_t esz, + int (*cmp)(const void *, const void *)) +{ + if (elms < 0) + return; + // +1 for pivot + char *scratch = calloc(elms + 1, esz); + if (scratch == NULL) + errx(-1, "qsort calloc"); + char *pivot = (char *)scratch + elms*esz; + _qsort(base, elms, esz, cmp, scratch, pivot); + free(scratch); +} + +uint _seed1; + +int +rand(void) +{ + return rand_r(&_seed1); +} + +int +rand_r(uint *seed) +{ + *seed = *seed * 1103515245 + 12345; + return *seed & RAND_MAX; +} + +void +srand(uint seed) +{ + _seed1 = seed; +} + +uint _seed2; + +// POSIX requires that random(3)'s range is [0, 2**31) +long +random(void) +{ + return rand_r(&_seed2); +} + +void +srandom(uint seed) +{ + _seed2 = seed; +} + +int +sscanf(const char *src, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + int ret = vsscanf(src, fmt, ap); + va_end(ap); + + return ret; +} + +ulong +rdtsc(void) +{ + ulong low, hi; + asm volatile( + "rdtsc\n" + : "=a"(low), "=d"(hi) + : + :); + return hi << 32 | low; +} + +static char readlineb[256]; + +char * +readline(const char *prompt) +{ + if (prompt) + printf("%s", prompt); + int ret; + int i = 0; + char c = 0x41; + // XXX + while ((ret = read(0, &c, 1)) > 0) { + if (c == '\n') + break; + if (c == '\b') { + if (--i < 0) + i = 0; + continue; + } + if (i < sizeof(readlineb) - 1) + readlineb[i++] = c; + } + if (ret < 0) + printf("readline: read failed: %d\n", ret); + if (ret == 0) + return NULL; + readlineb[i] = 0; + return readlineb; +} + +int +setenv(const char *k, const char *v, int overwrite) +{ + printf("setenv: no environment yet\n"); + errno = ENOSYS; + return -1; +} + +static inline int _fdisset(int fd, fd_set *fds) +{ + if (fds) + return FD_ISSET(fd, fds); + return 0; +} + +int +select(int maxfd, fd_set *rfds, fd_set *wfds, fd_set *efds, + struct timeval *timeout) +{ + int nfds = 0; + int i; + for (i = 0; i < maxfd; i++) + if (_fdisset(i, rfds) || _fdisset(i, wfds) || + _fdisset(i, efds)) + nfds++; + struct pollfd pfds[nfds]; + int curp = 0; + for (i = 0; i < maxfd; i++) { + if ((_fdisset(i, rfds) || _fdisset(i, wfds) || + _fdisset(i, efds)) == 0) + continue; + pfds[curp].fd = i; + pfds[curp].events = 0; + if (_fdisset(i, rfds)) + pfds[curp].events |= POLLIN; + if (_fdisset(i, wfds)) + pfds[curp].events |= POLLOUT; + if (_fdisset(i, efds)) + pfds[curp].events |= POLLERR; + curp++; + } + int toms = -1; + if (timeout) + toms = timeout->tv_sec * 1000 + timeout->tv_usec / 1000; + + // account for differences between select(2) and poll(2): + // 1. poll's HUP counts as a read-ready fd for select + // 2. an invalid fd is an error for select + // 3. select returns the number of bits set in the fdsets, not the + // number of ready fds. + if (rfds) + FD_ZERO(rfds); + if (wfds) + FD_ZERO(wfds); + if (efds) + FD_ZERO(efds); + int ret = poll(pfds, nfds, toms); + if (ret <= 0) + return ret; + + int selret = 0; + for (i = 0; i < nfds; i++) { + if (pfds[i].events & POLLNVAL) { + errno = EINVAL; + return -1; + } + +#define _PTOS(pf, fds) do { \ + if ((pfds[i].revents & pf) && \ + pfds[i].events & pf) { \ + FD_SET(pfds[i].fd, fds); \ + selret++; \ + } \ + } while (0) + + _PTOS(POLLIN, rfds); + _PTOS(POLLHUP, rfds); + _PTOS(POLLOUT, wfds); + _PTOS(POLLERR, efds); +#undef _PTOS + } + return selret; +} + +char * +setlocale(int cat, const char *loc) +{ + // null loc to fetch current locale + if (!loc || strcmp(loc, "") == 0 || strcmp(loc, "C") == 0 || + strcmp(loc, "POSIX") == 0) + return "C"; + errx(-1, "no fancy locale support"); +} + +uint +sleep(uint s) +{ + struct timespec t = {s, 0}; + struct timespec r = {0, 0}; + int ret = nanosleep(&t, &r); + if (ret) + return (uint)r.tv_sec; + return 0; +} + +int +snprintf(char *dst, size_t sz, const char *fmt, ...) +{ + va_list ap; + int ret; + + va_start(ap, fmt); + ret = vsnprintf(dst, sz, fmt, ap); + va_end(ap); + + return ret; +} + +int +sprintf(char *dst, const char *fmt, ...) +{ + size_t dur = (uintptr_t)-1 - (uintptr_t)dst; + + va_list ap; + int ret; + + va_start(ap, fmt); + ret = vsnprintf(dst, dur, fmt, ap); + va_end(ap); + + return ret; +} + +int +strcasecmp(const char *s1, const char *s2) +{ + return strncasecmp(s1, s2, ULONG_MAX); +} + +int +strncasecmp(const char *s1, const char *s2, size_t n) +{ + uint a = tolower(*s1++); + uint b = tolower(*s2++); + while (n && a && a == b) { + n--; + a = tolower(*s1++); + b = tolower(*s2++); + } + if (n == 0) + return 0; + return a - b; +} + +char * +strchr(const char *big, int l) +{ + for (; *big; big++) + if (*big == l) + return (char *)big; + if (l == '\0') + return (char *)big; + return NULL; +} + +char * +strdup(const char *orig) +{ + size_t l = strlen(orig) + 1; + char *ret = malloc(l); + if (!l) { + errno = ENOMEM; + return NULL; + } + memmove(ret, orig, l); + return ret; +} + +static const char * const _errstr[] = { + [EPERM] = "Operation not permitted", + [ENOENT] = "No such file or directory", + [ESRCH] = "No such process", + [EINTR] = "Interrupted system call", + [EIO] = "Input/output error", + [E2BIG] = "Argument list too long", + [EBADF] = "Bad file descriptor", + [EAGAIN] = "Resource temporarily unavailable", + [ECHILD] = "No child processes", + [ENOMEM] = "Out of memory", + [EACCES] = "Permission denied", + [EFAULT] = "Bad address", + [EBUSY] = "Device busy", + [EEXIST] = "File exists", + [EXDEV] = "Cross device link", + [ENODEV] = "Operation not supported by device", + [ENOTDIR] = "Not a directory", + [EISDIR] = "Is a directory", + [EINVAL] = "Invalid argument", + [ENFILE] = "Too many open files in system", + [EMFILE] = "Too many open files", + [ENOSPC] = "No space left on device", + [ESPIPE] = "Illegal seek", + [EPIPE] = "Broken pipe", + [ERANGE] = "Result too large", + [ENAMETOOLONG] = "File name too long", + [ENOSYS] = "Function not implemented", + [ENOTEMPTY] = "Directory not empty", + [EDESTADDRREQ] = "Destination address required", + [EAFNOSUPPORT] = "Protocol family not supported", + [EADDRINUSE] = "Address already in use", + [EADDRNOTAVAIL] = "Can't assign requested address", + [ENETDOWN] = "Network is down", + [ENETUNREACH] = "Network is unreachable", + [ECONNABORTED] = "Software caused connection abort", + [ELOOP] = "Too many levels of symbolic links", + [EHOSTDOWN] = "Host is down", + [EHOSTUNREACH] = "No route to host", + [EOVERFLOW] = "Value too large to be stored in data type", + [ENOTSOCK] = "Socket operation on non-socket", + [EOPNOTSUPP] = "Operation not supported", + [EISCONN] = "Socket is already connected", + [ENOTCONN] = "Socket is not connected", + [ETIMEDOUT] = "Operation timed out", + [ECONNRESET] = "Connection reset by peer", + [ECONNREFUSED] = "Connection refused", + [EINPROGRESS] = "Operation now in progress", +}; + +char * +strerror(int errnum) +{ + int nents = sizeof(_errstr)/sizeof(_errstr[0]); + const char *p = "Unknown error"; + if (errnum >= 0 && errnum < nents && _errstr[errnum] != NULL) + p = _errstr[errnum]; + return (char *)p; +} + +int +strerror_r(int errnum, char *dst, size_t dstlen) +{ + int nents = sizeof(_errstr)/sizeof(_errstr[0]); + const char *p = "Unknown error"; + if (errnum >= 0 && errnum < nents && _errstr[errnum] != NULL) + p = _errstr[errnum]; + strncpy(dst, p, dstlen); + return 0; +} + +char * +strncpy(char *dst, const char *src, size_t sz) +{ + snprintf(dst, sz, "%s", src); + return dst; +} + +char * +strstr(const char *big, const char *little) +{ + while (*big) { + if (*big == *little) { + const char *guess = big; + const char *l = little; + while (*big) { + if (*l == 0) + return (char *)guess; + if (*big != *l) + break; + big++; + l++; + } + if (*big == 0 && *l == 0) + return (char *)guess; + } else + big++; + } + return NULL; +} + +void +syslog(int priority, const char *msg, ...) +{ + if (((priority & LOG_ALL) & _slogopts.mask) == 0) + return; + va_list ap; + va_start(ap, msg); + + const char *pref = _slogopts.prefix ? _slogopts.prefix : ""; + char lbuf[2048]; + if (_slogopts.pid) + snprintf(lbuf, sizeof(lbuf), "syslog (pid %ld): %s: %s", + getpid(), pref, msg); + else + snprintf(lbuf, sizeof(lbuf), "syslog: %s: %s", pref, msg); + vfprintf(stderr, lbuf, ap); + va_end(ap); +} + +static int _isoct(int c) +{ + return c >= '0' && c <= '7'; +} + +long +strtol(const char *n, char **endptr, int base) +{ + // gobble spaces, then "+" or "-", and finally "0x" or "0" + while (isspace(*n)) + n++; + int sign = 1; + if (*n == '+') + n++; + else if (*n == '-') { + sign = -1; + n++; + } + + int haspre = strcmp(n, "0x") == 0; + int (*matcher)(int); + if (base == 16 || (base == 0 && haspre)) { + base = 16; + if (haspre) + n += 2; + matcher = isxdigit; + } else if (base == 8 || (base == 0 && *n == '0')) { + base = 8; + if (*n == '0') + n++; + matcher = _isoct; + } else if (base == 0 || base == 10) { + base = 10; + matcher = isdigit; + } else { + errno = EINVAL; + return 0; + } + long tot = 0; + while (matcher(*n)) { + int c = tolower(*n++); + tot = tot*base; + if (isalpha(c)) + tot += c - 'a' + 10; + else + tot += c - '0'; + } + if (endptr) + *endptr = (char *)n; + return tot*sign; +} + +double +strtod(const char *s, char **end) +{ + return (double)strtold(s, end); +} + +long double +strtold(const char *s, char **end) +{ + char *tend; + long t = strtol(s, &tend, 10); + // tend points to '.' or [Ee] + long double ret = (long double)t; + if (*tend == '.') { + tend++; + char *fend; + long frac = strtol(tend, &fend, 10); + long digs = fend - tend; + if (digs) { + long double addend = (long double)frac/(pow(10, digs)); + if (ret < 0) + ret -= addend; + else + ret += addend; + tend = fend; + } + } + if (tolower(*tend) == 'e') { + tend++; + char *eend; + long exp = strtol(tend, &eend, 10); + if (eend - tend > 0) { + ret *= pow(10, exp); + tend = eend; + } + } + if (end) + *end = tend; + return ret; +} + +long long +strtoll(const char *n, char **endptr, int base) +{ + return strtol(n, endptr, base); +} + +ulong +strtoul(const char *n, char **endptr, int base) +{ + long ret = strtol(n, endptr, base); + return (ulong)ret; +} + +unsigned long long +strtoull(const char *n, char **endptr, int base) +{ + return strtoull(n, endptr, base); +} + +time_t +time(time_t *tloc) +{ + struct timeval tv; + if (gettimeofday(&tv, NULL)) + return -1; + if (tloc) + *tloc = tv.tv_sec; + return tv.tv_sec; +} + +int +tolower(int c) +{ + if (!isalpha(c)) + return c; + return c | 0x20; +} + +int +toupper(int c) +{ + if (!isalpha(c)) + return c; + return c & ~0x20; +} + +double +trunc(double x) +{ + return (long)x; +} + +int +vprintf(const char *fmt, va_list ap) +{ + char lbuf[2048]; + + if (strlen(fmt) >= sizeof(lbuf)) + printf("warning: fmt too long\n"); + + int ret; + ret = vsnprintf(lbuf, sizeof(lbuf), fmt, ap); + pmsg(lbuf, ret); + + return ret; +} + +static struct utsname _unamed = { + .sysname = "BiscuitOS", + .nodename = "localhost", + .release = BISCUIT_RELEASE, + .version = BISCUIT_VERSION, + .machine = "amd64", +}; + +int +uname(struct utsname *name) +{ + memmove(name, &_unamed, sizeof(struct utsname)); + return 0; +} + +int +usleep(uint us) +{ + long ns = (us % 1000000)*1000; + struct timespec t = {us/1000000, ns}; + return nanosleep(&t, NULL); +} + +int +vfprintf(FILE *f, const char *fmt, va_list ap) +{ + char lbuf[2048]; + + if (strlen(fmt) >= sizeof(lbuf)) + printf("warning: fmt too long\n"); + + int ret; + ret = vsnprintf(lbuf, sizeof(lbuf), fmt, ap); + int wrote = fwrite(lbuf, ret, 1, f); + + return wrote; +} + +#define _DLMALLOC 0 + +#if _DLMALLOC + +#include <../dlmalloc.c> + +#else + +static volatile long biglock; +static void acquire(void) +{ + if (!dolock) + return; + while (__sync_lock_test_and_set(&biglock, 1) != 0) + ; +} + +static void release(void) +{ + asm volatile("":::"memory"); + biglock = 0; +} + +struct header_t { + char *start; + char *end; + ulong objs; + size_t maxsz; + struct header_t *next; + struct header_t *prev; +}; + +static struct header_t *allh; +static struct header_t *curh; +static char *bump; + +static void +_free_header(struct header_t *ch) +{ + if (ch->prev) + ch->prev->next = ch->next; + else + allh = ch->next; + if (ch->next) + ch->next->prev = ch->prev; + int ret; + if ((ret = munmap(ch->start, ch->end - ch->start)) < 0) + err(ret, "munmap"); +} + +static void * +_malloc(size_t sz) +{ + // Minimum allocation size = 8 bytes. + sz = (sz + 7) & ~7; + int newhdr = 0; + if (curh) { + size_t left = curh->end - bump; + newhdr = sz > left; + } else { + newhdr = 1; + } + if (newhdr) { + if (curh && curh->objs == 0) { + _free_header(curh); + curh = NULL; + } + const size_t pgsize = 1 << 12; + // Also account for the header that we embed within the + // allocated space. + size_t mmapsz = (sz + sizeof(struct header_t) + pgsize - 1) & + ~(pgsize - 1); + if (mmapsz < sz) { + errno = EOVERFLOW; + return NULL; + } + struct header_t *nh = mmap(NULL, mmapsz, PROT_READ | PROT_WRITE, + MAP_ANON | MAP_PRIVATE, -1, 0); + if (nh == MAP_FAILED) { + errno = ENOMEM; + return NULL; + } + nh->start = (char *)nh; + nh->end = nh->start + mmapsz; + nh->objs = 0; + nh->next = allh; + nh->prev = NULL; + if (nh->next) + nh->next->prev = nh; + nh->maxsz = 0; + allh = nh; + + curh = nh; + bump = curh->start + sizeof(struct header_t); + } + curh->objs++; + char *ret = bump; + bump += sz; + if (sz > curh->maxsz) + curh->maxsz = sz; + return ret; +} + +#define ZEROPTR ((void *)1) + +void * +malloc(size_t sz) +{ + // try to catch accesses to zero-length objects. is this ok? + if (sz == 0) + return ZEROPTR; + acquire(); + void *ret = _malloc(sz); + release(); + + return ret; +} + +void * +calloc(size_t n, size_t sz) +{ + if (n == 0 || sz == 0) + return ZEROPTR; + if (n > ULONG_MAX / sz) { + errno = EOVERFLOW; + return NULL; + } + size_t big = sz*n; + void *ret = malloc(big); + if (!ret) + return NULL; + memset(ret, 0, big); + return ret; +} + +static struct header_t * +_findseg(void *pp) +{ + char *p = (char *)pp; + struct header_t *ch; + for (ch = allh; ch; ch = ch->next) + if (ch->start <= p && ch->end > p) + break; + return ch; +} + +static void +_free(void *pp) +{ + // find containing seg + struct header_t *ch; + ch = _findseg(pp); + if (!ch) + errx(-1, "free: bad pointer"); + ch->objs--; + if (ch->objs == 0) { + if (ch == curh) + return; + _free_header(ch); + } +} + +void +free(void *pp) +{ + acquire(); + _free(pp); + release(); +} + +void * +realloc(void *vold, size_t nsz) +{ + char *old = vold; + if (!old) + return malloc(nsz); + + acquire(); + + void *ret = old; + struct header_t *ch = _findseg(old); + // we don't know the exact size of the object, but its size is bounded + // as follows + size_t oldsz = MIN(ch->maxsz, ch->end - old); + ret = _malloc(nsz); + if (ret == NULL) + goto out; + memmove(ret, old, MIN(oldsz, nsz)); + _free(old); +out: + release(); + return ret; +} +#endif + +char __progname[64]; +char **environ = _environ; + +void +_start(int argc, char **argv, struct kinfo_t *k) +{ + kinfo = k; + + if (argc) + strncpy(__progname, argv[0], sizeof(__progname)); + fdinit(); + int main(int, char **); + int ret = main(argc, argv); + exit(ret); +} + +void +_entry(void) +{ + // make sure that the stack is 16-byte aligned, as gcc assumes, after + // _start's function prologue. gcc emits SSE instructions that require + // 16-byte alignment (misalignment generates #GP). + asm( + "andq $0xfffffffffffffff0, %%rsp\n" + "subq $8, %%rsp\n" + "movabs $_start, %%rax\n" + "jmpq *%%rax\n" + ::: "memory", "cc"); +} + +/* NGINX STUFF */ +long timezone; + +#define FAIL errx(-1, "%s -- no imp", __FUNCTION__) +#define HACK(x) do { \ + fprintf(stderr, "warning: %s is a no-op\n", __FUNCTION__); \ + return x; \ + } while (0) + +char * +getenv(char *name) +{ + HACK(NULL); +} + +uid_t +geteuid(void) +{ + HACK(666); +} + +struct passwd * +getpwnam(const char *a) +{ + if (strcmp(a, "nobody") != 0) + errx(-1, "unexpected getpwnam"); + + static struct passwd dur = {"nobody", 666, 667, "/tmp", + "/bin/cmd.exe"}; + HACK(&dur); +} + +struct group * +getgrnam(const char *a) +{ + if (strcmp(a, "nogroup") != 0) + errx(-1, "unexpected getpwnam"); + + static struct group dur = {"nogroup", 667, NULL}; + HACK(&dur); +} + +struct hostent * +gethostbyname(const char *a) +{ + FAIL; +} + +int +chown(const char *a, uid_t b, gid_t c) +{ + if (b != 666 || c != -1) + errx(-1, "unexpected chown"); + errno = 0; + HACK(0); +} + +time_t +mktime(struct tm *a) +{ + FAIL; +} + +int +gethostname(char *a, size_t b) +{ + snprintf(a, b, "localhost"); + HACK(0); +} + +char * +strpbrk(const char *a, const char *bb) +{ + while (*a) { + const char *b = bb; + while (*b) + if (*a == *b++) + return (char *)a; + a++; + } + return NULL; +} + +int +setitimer(int a, struct itimerval *b, struct itimerval *c) +{ + FAIL; +} + +struct tm * +localtime(const time_t *a) +{ + static struct tm dur; + struct timeval tv; + if (gettimeofday(&tv, NULL) == -1) + err(-1, "localtime/gettimeofday"); + dur.tm_hour = tv.tv_sec / (60*60); + dur.tm_min = tv.tv_sec / 60; + dur.tm_sec = tv.tv_sec % 60; + return &dur; +} + +struct tm * +gmtime(const time_t *a) +{ + FAIL; +} + +int +utimes(const char *a, const struct timeval b[2]) +{ + FAIL; +} + +int +glob(const char *a, int b, int (*c)(const char *, int), glob_t *d) +{ + FAIL; +} + +void +globfree(glob_t *a) +{ + FAIL; +} + +int +ioctl(int fd, ulong req, ...) +{ + if (req != FIOASYNC) + errx(-1, "nyet"); + HACK(0); +} + +int +raise(int a) +{ + FAIL; +} + +mode_t +umask(mode_t a) +{ + static mode_t dur = 0022; + mode_t old = dur; + dur = a; + HACK(old); +} + +int +getpagesize(void) +{ + return 1 << 12; +} + +int +sigprocmask(int a, sigset_t *b, sigset_t *c) +{ + if (c) + errx(-1, "no old set"); + HACK(0); +} + +int +sigsuspend(const sigset_t *a) +{ + fprintf(stderr, "warning: no signals, thus sleep forever...\n"); + pause(); + errx(-1, "sigsuspend no imp"); +} + +int +setpriority(int a, int b, int c) +{ + FAIL; +} + +uid_t +getuid(void) +{ + FAIL; +} + +int +setuid(uid_t a) +{ + FAIL; +} + +int +setgid(gid_t a) +{ + FAIL; +} + +int +initgroups(const char *a, gid_t b) +{ + FAIL; +} + +char * +realpath(const char *a, char *b) +{ + FAIL; +} + +int +lstat(const char *a, struct stat *b) +{ + // will some applications break because they expect lstat to be a + // system call? + // XXX yes, if it has maximum open fds already + int fd = open(a, O_RDONLY); + if (fd == -1) + return -1; + int ret = fstat(fd, b); + if (close(fd) == -1) + fprintf(stderr, "lstat close failed\n"); + return ret; +} + +/* LMBENCH STUFF */ +unsigned int +alarm(unsigned int sec) +{ + HACK(0); +} + +#if 0 +#include <../bloat.c> +#endif diff --git a/biscuit/c/linker.ld b/biscuit/c/linker.ld new file mode 100644 index 0000000..b6b2319 --- /dev/null +++ b/biscuit/c/linker.ld @@ -0,0 +1,13 @@ +ENTRY(_entry) + +SECTIONS +{ + . = 0x2c8000001000; + .text : { *(.text) } + .data : { *(.data) } + .bss : { *(.bss) } + + /DISCARD/ : { + *(.eh_frame) + } +} diff --git a/biscuit/c/lnc.c b/biscuit/c/lnc.c new file mode 100644 index 0000000..e4e497d --- /dev/null +++ b/biscuit/c/lnc.c @@ -0,0 +1,176 @@ +#include + +static char lmss[1ul << 11]; + +static int mss(int s) +{ + printf("MSS sends\n"); + + int fd = open("/redis.conf", O_RDONLY); + if (fd == -1) + err(-1, "open"); + + ssize_t r; + if ((r = read(fd, lmss, sizeof(lmss))) == -1) + err(-1, "read"); + else if (r != sizeof(lmss)) + errx(-1, "short read?"); + + char *p = &lmss[0]; + size_t left = sizeof(lmss); + while (left) { + if ((r = write(s, p, left)) == -1) + err(-1, "write"); + if (r == -1) + err(-1, "write"); + else if (r == 0) + errx(-1, "wat?"); + if (r > left) + errx(-1, "uh oh"); + left -= r; + p += r; + } + printf("lnc finished\n"); + return 0; +} + +static char buf[1460 - 12]; + +int nc(int s) +{ + struct pollfd pfds[2] = {{.fd = 0}, {.fd = s}}; + const int nfds = 2; + int closed = 0; + ssize_t wtot = 0; + while (closed != 2) { + pfds[0].events = pfds[1].events = POLLIN; + int ret; + if ((ret = poll(pfds, nfds, -1)) == -1) + err(-1, "poll"); + if (ret == 0) + errx(-1, "what"); + int i; + for (i = 0; i < nfds; i++) { + if ((pfds[i].revents & POLLIN) == 0) + continue; + ssize_t c = read(pfds[i].fd, buf, sizeof(buf)); + if (c == -1) { + err(-1, "read"); + } else if (c == 0) { + fprintf(stderr, "fd %d EOF\n", pfds[i].fd); + closed++; + pfds[i].fd = -1; + if (i == 0) { + if (shutdown(s, SHUT_WR) == -1) + err(-1, "shutdown"); + } + continue; + } + ssize_t w = write(i == 0 ? s : 1, buf, c); + if (w == -1) + err(-1, "write"); + else if (w != c) + errx(-1, "short write"); + wtot += w; + } + } + fprintf(stderr, "lnc finished (wrote %zd)\n", wtot); + return 0; +} + +static void usage() +{ + fprintf(stderr, "usage:\n" + "%s [-M] [-c host] [-p port] [-l listen port]\n", __progname); + exit(-1); +} + +static int con(uint32_t dip, uint16_t dport) +{ + int s = socket(AF_INET, SOCK_STREAM, 0); + if (s == -1) + err(-1, "socket"); + uint8_t a = dip >> 24; + uint8_t b = dip >> 16; + uint8_t c = dip >> 8; + uint8_t d = dip; + fprintf(stderr, "connecting to %d.%d.%d.%d:%d\n", a, b, c, d, dport); + struct sockaddr_in sin; + sin.sin_port = htons(dport); + sin.sin_addr.s_addr = htonl(dip); + if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) == -1) + err(-1, "connect"); + return s; +} + +static int lstn(uint16_t lport) +{ + fprintf(stderr, "listen on port %d\n", (int)lport); + + int s = socket(AF_INET, SOCK_STREAM, 0); + if (s == -1) + err(-1, "socket"); + struct sockaddr_in sin; + sin.sin_family = AF_INET; + sin.sin_port = htons(lport); + sin.sin_addr.s_addr = htonl(INADDR_ANY); + if (bind(s, (struct sockaddr *)&sin, sizeof(sin)) == -1) + err(-1, "bind"); + if (listen(s, 10) == -1) + err(-1, "listen"); + int ret; + socklen_t slen = sizeof(sin); + if ((ret = accept(s, (struct sockaddr *)&sin, &slen)) == -1) + err(-1, "accept"); + if (close(s)) + err(-1, "close"); + return ret; +} + +int main(int argc, char **argv) +{ + // bhw + uint32_t dip = 0x121a0530; + uint16_t dport = 31338; + uint16_t lport = 0; + int Mss = 0, usedp = 0; + int c; + while ((c = getopt(argc, argv, "Mp:c:l:")) != -1) { + switch (c) { + case 'M': + Mss = 1; + break; + case 'p': + dport = strtol(optarg, NULL, 0); + usedp = 1; + break; + case 'c': { + int a, b, c, d; + if (sscanf(optarg, "%d.%d.%d.%d", &a, &b, &c, &d) != 4) + errx(-1, "malformed IP (%s)", optarg); + dip = a << 24 | b << 16 | c << 8 | d; + break; + case 'l': + lport = (uint16_t)strtol(optarg, NULL, 0); + break; + } + default: + usage(); + } + } + + if (usedp && lport) { + fprintf(stderr, "-l and -p are mutually exclusive\n"); + usage(); + } + int s; + if (lport) + s = lstn(lport); + else + s = con(dip, dport); + fprintf(stderr ,"connected\n"); + if (Mss) + return mss(s); + else + return nc(s); +} diff --git a/biscuit/c/ls.c b/biscuit/c/ls.c new file mode 100644 index 0000000..2ead7b6 --- /dev/null +++ b/biscuit/c/ls.c @@ -0,0 +1,116 @@ +#include + +void dprint(int fd, char *par, int left, int rec) +{ + printf("%s/:\n", par); + + char *pend = par + strlen(par); + snprintf(pend, left, "/"); + left--; + pend = par + strlen(par); + + DIR *dir = fdopendir(fd); + if (!dir) + err(-1, "fdopendir"); + struct dirent des, *de; + int ret; + while (1) { + ret = readdir_r(dir, &des, &de); + if (ret) + errx(-1, "readdir_r %s", strerror(ret)); + if (!de) + break; + int used = snprintf(pend, left, "%s", de->d_name); + if (used >= left) + errx(-1, "long filenames!"); + char *fn = par; + struct stat st; + if (stat(fn, &st)) + err(-1, "stat"); + char spec; + if (S_ISDIR(st.st_mode)) + spec = 'd'; + else if (S_ISSOCK(st.st_mode)) + spec = 's'; + else + spec = '-'; + printf("%crwxr-xr-x %ld %s\n", spec, st.st_size, de->d_name); + } + if (!rec) { + if (closedir(dir) == -1) + err(-1, "closedir"); + return; + } + + // recursive list + *pend = 0; + rewinddir(dir); + while (1) { + ret = readdir_r(dir, &des, &de); + if (ret) + errx(-1, "readdir_r %s", strerror(ret)); + if (!de) + break; + char *tn = de->d_name; + if (strncmp(tn, "..", 3) == 0 || + strncmp(tn, ".", 2) == 0) + continue; + snprintf(pend, left, "%s", tn); + char *fn = par; + struct stat st; + if (stat(fn, &st)) + err(-1, "stat"); + if (S_ISDIR(st.st_mode)) { + int tfd = open(fn, O_RDONLY | O_DIRECTORY, 0); + if (tfd < 0) + err(-1, "rec open"); + dprint(tfd, par, left - strlen(pend), 1); + // dprint closes tfd + } + } + if (closedir(dir) == -1) + err(-1, "closedir"); +} + +__attribute__((noreturn)) +void usage(void) +{ + fprintf(stderr, "%s [-R]\n" + "-R recurse subdirectories\n" + "\n", __progname); + exit(-1); +} + +int main(int argc, char **argv) +{ + int rec = 0; + int ch; + while ((ch = getopt(argc, argv, "R")) != -1) { + switch (ch) { + case 'R': + rec = 1; + break; + default: + usage(); + break; + } + } + char *start; + int rem = argc - optind; + if (rem == 0) + start = "."; + else if (rem == 1) + start = argv[optind]; + else + usage(); + int fd; + if ((fd = open(start, O_RDONLY | O_DIRECTORY, 0)) == -1) + err(-1, "open %s", start); + + char pbuf[256]; + strncpy(pbuf, start, sizeof(pbuf)); + dprint(fd, pbuf, sizeof(pbuf) - strlen(pbuf), rec); + close(fd); + + return 0; +} diff --git a/biscuit/c/lsh.c b/biscuit/c/lsh.c new file mode 100644 index 0000000..5acd226 --- /dev/null +++ b/biscuit/c/lsh.c @@ -0,0 +1,208 @@ +#include + +void mkargs(char *line, char *args[], size_t n) +{ + static char boof[1024]; + int ai; + char *f = line; + char *bp = boof; + char *be = boof + sizeof(boof); + for (ai = 0; line && ai < n - 1; ai++) { + if (be - bp <= 0) + errx(-1, "no boof"); + while (*line == ' ') + line++; + if (*line == 0) + break; + args[ai] = bp; + f = strstr(line, " "); + if (f) + *f++ = '\0'; + strncpy(bp, line, be - bp); + bp += strlen(bp) + 1; + line = f; + } + args[ai] = NULL; + //for (ai = 0; args[ai] != NULL; ai++) + // printf("arg %d: %s\n", ai, args[ai]); +} + +int builtins(char *args[], size_t n) +{ + char *cmd = args[0]; + if (strncmp(cmd, "cd", 3) == 0) { + int ret = chdir(args[1]); + if (ret) + printf("chdir to %s failed\n", args[1]); + return 1; + } else if (strncmp(cmd, "ps", 3) == 0) { + if (sys_info(SINFO_PROCLIST) == -1) + err(-1, "sys_info"); + return 1; + } + return 0; +} + +int removefn(char *line, char *retbuf, size_t retsz) +{ + // gobble + while (*line == ' ') + line++; + if (!*line) + return -1; + // get filename end; either nul or space + char *end, *t; + end = strchr(line, ' '); + t = strchr(line, '\0'); + if (!end) + end = t; + if (!end) + errx(-1, "how"); + if (end - line + 1 > retsz) + errx(-1, "retbuffer too small"); + while (line < end) { + *retbuf++ = *line; + *line++ = ' '; + } + *retbuf = '\0'; + return 0; +} + +int redir(char *line, char **infn, char **outfn, int *append) +{ + static char inbuf[64]; + static char outbuf[64]; + + *infn = *outfn = NULL; + *append = 0; + + // remove all special tokens before grabbing filenames so that special + // tokens can separate filenames (i.e. "< file1>file2") + char *inp; + if ((inp = strchr(line, '<'))) { + if (strchr(inp + 1, '<')) { + printf("syntax error: two in redirects\n"); + return -1; + } + *inp = ' '; + } + char *outp; + if ((outp = strstr(line, ">>"))) { + if (strchr(outp + 2, '>')) { + printf("syntax error: two out redirects\n"); + return -1; + } + outp[0] = ' '; + outp[1] = ' '; + *append = 1; + } else if ((outp = strchr(line, '>'))) { + if (strchr(outp + 1, '>')) { + printf("syntax error: two out redirects\n"); + return -1; + } + *outp = ' '; + } + if (inp) { + if (removefn(inp, inbuf, sizeof(inbuf))) { + printf("syntax error: no in redirect filename\n"); + return -1; + } + *infn = inbuf; + } + if (outp) { + if (removefn(outp, outbuf, sizeof(outbuf))) { + printf("syntax error: no out redirect filename\n"); + return -1; + } + *outfn = outbuf; + } + return 0; +} + +void doredirs(char *infn, char *outfn, int append) +{ + if (infn) { + int fd = open(infn, O_RDONLY); + if (fd < 0) + err(-1, "open in redirect"); + if (dup2(fd, 0) < 0) + err(-1, "dup2"); + close(fd); + } + if (outfn) { + int flags = O_WRONLY|O_CREAT; + if (append) + flags |= O_APPEND; + else + flags |= O_TRUNC; + int fd = open(outfn, flags); + if (fd < 0) + err(-1, "open out redirect"); + if (dup2(fd, 1) < 0) + err(-1, "dup2"); + close(fd); + } +} + +int main(int argc, char **argv) +{ + int nbgs = 0; + while (1) { + // if you change the output of lsh, you need to update + // posixtest() in usertests.c so the test is aware of the new + // changes. + char *args[64]; + size_t sz = sizeof(args)/sizeof(args[0]); + char *infile, *outfile; + int append; + char *p = readline("# "); + if (p == NULL) + exit(0); + char *com; + if ((com = strchr(p, '#'))) + *com = '\0'; + int isbg = 0; + if ((com = strchr(p, '&'))) { + *com = ' '; + isbg = 1; + } + if (redir(p, &infile, &outfile, &append)) + continue; + mkargs(p, args, sz); + if (args[0] == NULL) + continue; + if (builtins(args, sz)) + continue; + int pid = fork(); + if (pid < 0) + err(-1, "fork"); + if (pid) { + if (isbg) + nbgs++; + else + while (wait(NULL) != pid) + ; + continue; + } + // if background job, fork another child to check the commands + // exit code + int pid2; + if (isbg && (pid2 = fork()) != 0) { + printf("background pid %d\n", pid2); + int status; + wait(&status); + int st = WEXITSTATUS(status); + if (!WIFEXITED(status) || st != 0) + printf("background job %d failed with %d", + pid2, st); + else + printf("background job %d done\n", pid2); + exit(st); + } + doredirs(infile, outfile, append); + execvp(args[0], args); + err(-1, "couldn't exec \"%s\"", args[0]); + } + + return 0; +} diff --git a/biscuit/c/mkdir.c b/biscuit/c/mkdir.c new file mode 100644 index 0000000..950e5b7 --- /dev/null +++ b/biscuit/c/mkdir.c @@ -0,0 +1,12 @@ +#include + +int main(int argc, char **argv) +{ + if (argc != 2) + errx(-1, "usage: %s \n", argv[0]); + + int ret = mkdir(argv[1], 0); + if (ret) + err(ret, "mkdir"); + return 0; +} diff --git a/biscuit/c/mknodtest.c b/biscuit/c/mknodtest.c new file mode 100644 index 0000000..03783e7 --- /dev/null +++ b/biscuit/c/mknodtest.c @@ -0,0 +1,19 @@ +#include + +int main(int argc, char **argv) +{ + int ret; + if ((ret = mknod("/tmp/flea", 10, MKDEV(10, 0))) < 0) + err(ret, "mknod"); + + int fd = open("/tmp/flea", O_RDWR, 0); + if (fd < 0) + err(fd, "open"); + char buf[512]; + if ((ret = read(fd, buf, sizeof(buf))) < 0) + err(ret, "read"); + + close(fd); + + return 0; +} diff --git a/biscuit/c/mmapbench.c b/biscuit/c/mmapbench.c new file mode 100644 index 0000000..be52690 --- /dev/null +++ b/biscuit/c/mmapbench.c @@ -0,0 +1,41 @@ +#include + +static long +nowms(void) +{ + struct timeval tv; + if (gettimeofday(&tv, NULL)) + errx(-1, "nowms"); + return tv.tv_sec*1000 + tv.tv_usec/1000; +} + +#define SZ (1 << 22) +#define N 1024 + +int main(int argc, char **argv) +{ + + printf("mmapbench\n"); + + const size_t sz = SZ; + long tot = 0; + for (int i = 0 ; i < N; i++) { + char *p = mmap(NULL, sz, PROT_READ | PROT_WRITE, + MAP_ANON | MAP_PRIVATE, -1, 0); + if (p == MAP_FAILED) + errx(-1, "mmap failed"); + + long st = nowms(); + for (int j = 0; j < sz; j += 4096) { + p[j] = 0xcc; + } + + tot += nowms() - st; + + if (munmap(p, sz) != 0) + errx(-1, "munmap failed"); + } + printf("mmapbench %ld pgfaults in %ld ms\n", (long) (SZ/4096) * N, tot); + + return 0; +} diff --git a/biscuit/c/mmaptest.c b/biscuit/c/mmaptest.c new file mode 100644 index 0000000..d2741c4 --- /dev/null +++ b/biscuit/c/mmaptest.c @@ -0,0 +1,35 @@ +#include + +char *ps[1000]; + +int main(int argc, char **argv) +{ + int times; + for (times = 0; times < 10; times++) { + const size_t sz = 1 << 13; + char *p = mmap(NULL, sz, PROT_READ | PROT_WRITE, + MAP_ANON | MAP_PRIVATE, -1, 0); + if (p == MAP_FAILED) + errx(-1, "mmap"); + + int i; + for (i = 0; i < sz; i++) + p[i] = 0xcc; + int ret; + if ((ret = munmap(p, sz)) < 0) + err(ret, "munmap"); + } + + for (times = 0; times < 10; times++) { + int iters = sizeof(ps)/sizeof(ps[0]); + int i; + for (i = 0; i < iters; i++) + ps[i] = malloc(30); + for (i = 0; i < iters; i++) + free(ps[i]); + } + + printf("success\n"); + + return 0; +} diff --git a/biscuit/c/mv.c b/biscuit/c/mv.c new file mode 100644 index 0000000..61025d7 --- /dev/null +++ b/biscuit/c/mv.c @@ -0,0 +1,15 @@ +#include + +int main(int argc, char **argv) +{ + if (argc != 3) + errx(-1, "%s \n", argv[0]); + + char *old = argv[1]; + char *new = argv[2]; + + int ret; + if ((ret = rename(old, new)) < 0) + err(ret, "rename"); + return 0; +} diff --git a/biscuit/c/pipetest.c b/biscuit/c/pipetest.c new file mode 100644 index 0000000..706a1be --- /dev/null +++ b/biscuit/c/pipetest.c @@ -0,0 +1,69 @@ +#include + +int main(int argc, char **argv) +{ + int pfds[2]; + int ret = pipe(pfds); + char *msg1 = "hello, sonny!"; + char *msg2 = "hello, laddy!"; + const int msglen = strlen(msg1) + 1; + if (ret < 0) + err(ret, "pipe"); + printf("pipe fds: %d %d\n", pfds[0], pfds[1]); + int child = 0; + int status; + int pid1 = fork(); + int pid2 = fork(); + if (pid1 == 0) + child = 1; + + if (child) { + close(pfds[1]); + + char buf[msglen]; + ret = read(pfds[0], buf, sizeof(buf)); + if (ret < 0) + err(ret, "read"); + printf("got: %s\n", buf); + if (ret != msglen) + errx(-1, "only read %d of %d bytes\n", ret, msglen); + + if (pid2 != 0) { + if ((ret = wait(&status)) < 0) + err(ret, "wait1"); + if (WEXITSTATUS(status) != 0) + errx(status, "child child failed %d", status); + } + return 0; + } + close(pfds[0]); + + char *msg = (pid2 == 0) ? msg1 : msg2; + printf("sending: %s\n", msg); + ret = write(pfds[1], msg, msglen); + printf("sent\n"); + if (ret < 0) + err(ret, "write"); + if (ret != msglen) + errx(-1, "only wrote %d of %d bytes\n", ret, msglen); + + if (pid2 == 0) + return 0; + + if ((ret = wait(&status)) < 0) + err(ret, "wait2"); + if (WEXITSTATUS(status) != 0) + err(status, "parent child failed %d", status); + if ((ret = wait(&status)) < 0) + err(ret, "wait3"); + if (WEXITSTATUS(status) != 0) + err(status, "parent child2 failed %d", status); + + // try write on pipe with no readers + if ((ret = write(pfds[1], "fail", 4) >= 0)) + errx(-1, "write should have failed %d", ret); + + printf("success\n"); + + return 0; +} diff --git a/biscuit/c/pthtests.c b/biscuit/c/pthtests.c new file mode 100644 index 0000000..252c29e --- /dev/null +++ b/biscuit/c/pthtests.c @@ -0,0 +1,146 @@ +#include + +__thread int id; + +int nthreads; +int count; + +void barrier_init() +{ + count = 0; +} + +void barrier() +{ + // barrier + for (;;) { + int oc = count; + int nc = oc + 1; + if (__sync_bool_compare_and_swap(&count, oc, nc)) + break; + } + + int volatile *p = &count; + while (*p != nthreads) + ; +} + +void *fn(void *a) +{ + int b = (int)(long)a; + id = b; + + // barrier + barrier(); + + printf("my id is still: %d (%d)\n", id, b); + if (id != b) + errx(-1, "TLS fyuked!"); + + return NULL; +} + +static long volatile go __attribute__((aligned(4096))); +static int blah __attribute__((aligned(4096))); + +void *groupfault(void *a) +{ + int b = (int)(long)a; + + while (!go) + asm volatile("pause"); + + blah = b; + + return NULL; +} + +static int volatile observe __attribute__((aligned(4096))) = 10; + +void acquire(void) +{ + while (__sync_lock_test_and_set(&go, 1) != 0) + ; +} + +void release(void) +{ + go = 0; +} + +void *tlbinval(void *a) +{ + acquire(); + if (observe != 0x31337) + errx(-1, "didn't get tlb invalidate: %d", observe); + release(); + return NULL; +} + +void joinall(pthread_t t[]) +{ + int i; + for (i = 0; i < nthreads; i++) { + void *ret; + if (pthread_join(t[i], &ret)) + errx(-1, "pthread join"); + if (ret) + errx(-1, "bad exit"); + } +} + +int main(int argc, char **argv) +{ + if (argc > 1) + nthreads = atoi(argv[1]); + if (nthreads <= 0) + nthreads = 3; + + barrier_init(); + printf("making %d threads\n", nthreads); + pthread_t t[nthreads]; + int i; + for (i = 0; i < nthreads; i++) + if (pthread_create(&t[i], NULL, fn, (void *)(long)i)) + errx(-1, "pthread create"); + + printf("started\n"); + + joinall(t); + printf("joined. doing group fault...\n"); + + // test simultaneous faults + for (i = 0; i < nthreads; i++) + if (pthread_create(&t[i], NULL, groupfault, (void *)(long)i)) + errx(-1, "pthread create"); + + sleep(1); + printf("fork\n"); + + // make address space copy-on-write by forking + pid_t c = fork(); + if (c == -1) + err(-1, "fork"); + else if (c == 0) + return 0; + + go = 1; + joinall(t); + go = 0; + + printf("testing TLB shootdowns...\n"); + // test TLB shootdowns + acquire(); + for (i = 0; i < nthreads; i++) + if (pthread_create(&t[i], NULL, tlbinval, NULL)) + errx(-1, "pthread create"); + + observe = 0x31337; + release(); + + joinall(t); + + printf("success\n"); + + return 0; +} diff --git a/biscuit/c/pwd.c b/biscuit/c/pwd.c new file mode 100644 index 0000000..861f588 --- /dev/null +++ b/biscuit/c/pwd.c @@ -0,0 +1,11 @@ +#include +#include + +int main(int argc, char **argv) +{ + char buf[256]; + if (!getcwd(buf, sizeof(buf))) + err(-1, "getcwd"); + printf("%s\n", buf); + return 0; +} diff --git a/biscuit/c/reboot.c b/biscuit/c/reboot.c new file mode 100644 index 0000000..802e30c --- /dev/null +++ b/biscuit/c/reboot.c @@ -0,0 +1,11 @@ +#include +#include + +int main(int argc, char **argv) +{ + printf("syncing..."); + sync(); + printf("done. rebooting...\n"); + reboot(); + return 0; +} diff --git a/biscuit/c/rmtree.c b/biscuit/c/rmtree.c new file mode 100644 index 0000000..358b5d8 --- /dev/null +++ b/biscuit/c/rmtree.c @@ -0,0 +1,60 @@ +#include +#include +#include +#include + +void rm(char *fn); + +void dirrm(char *dir) +{ + DIR *d = opendir(dir); + if (!d) + err(-1, "opendir"); + if (chdir(dir) == -1) + err(-1, "chdir"); + struct dirent des, *de; + while (1) { + int ret = readdir_r(d, &des, &de); + if (ret) + errx(-1, "readdir: %s", strerror(ret)); + if (!de) + break; + if (strncmp(de->d_name, ".", 2) == 0 || + strncmp(de->d_name, "..", 3) == 0) + continue; + rm(de->d_name); + } + if (chdir("..") == -1) + err(-1, "chdir"); + if (rmdir(dir) == -1) + err(-1, "rmdir"); +} + +void rm(char *fn) +{ + struct stat st; + if (stat(fn, &st) == -1) + err(-1, "stat"); + if (S_ISREG(st.st_mode) || S_ISSOCK(st.st_mode) || + S_ISDEV(st.st_mode)) { + if (unlink(fn) == -1) + err(-1, "unlink"); + return; + } else if (S_ISDIR(st.st_mode)) { + dirrm(fn); + return; + } + errx(-1, "weird file type %ld", st.st_mode & S_IFMT); +} + +int main(int argc, char **argv) +{ + if (argc != 2) { + fprintf(stderr, "usage: %s \n", + __progname); + exit(-1); + } + char *fn = argv[1]; + rm(fn); + return 0; +} diff --git a/biscuit/c/rshd.c b/biscuit/c/rshd.c new file mode 100644 index 0000000..6bae35f --- /dev/null +++ b/biscuit/c/rshd.c @@ -0,0 +1,66 @@ +#include + +static int lstn(uint16_t lport) +{ + fprintf(stderr, "rshd listening on port %d\n", (int)lport); + + int s = socket(AF_INET, SOCK_STREAM, 0); + if (s == -1) + err(-1, "socket"); + struct sockaddr_in sin; + sin.sin_family = AF_INET; + sin.sin_port = htons(lport); + sin.sin_addr.s_addr = htonl(INADDR_ANY); + if (bind(s, (struct sockaddr *)&sin, sizeof(sin)) == -1) + err(-1, "bind"); + if (listen(s, 10) == -1) + err(-1, "listen"); + return s; +} + +int main(int argc, char **argv) +{ + int lfd = lstn(22); + + for (;;) { + struct sockaddr_in sin; + socklen_t slen = sizeof(sin); + int s; + if ((s = accept(lfd, (struct sockaddr *)&sin, &slen)) == -1) + err(-1, "accept"); + pid_t p; + if ((p = fork()) == -1) + err(-1, "fork"); + if (p == 0) { + if (dup2(s, 0) == -1) + err(-1, "dup2"); + if (dup2(s, 1) == -1) + err(-1, "dup2"); + if (dup2(s, 2) == -1) + err(-1, "dup2"); + close(s); + char *args[] = {"/bin/lsh", NULL}; + execv(args[0], args); + err(-1, "execv"); + } + if (close(s) == -1) + err(-1, "close"); + uint a = (sin.sin_addr.s_addr >> 0) & 0xff; + uint b = (sin.sin_addr.s_addr >> 8) & 0xff; + uint c = (sin.sin_addr.s_addr >> 16) & 0xff; + uint d = (sin.sin_addr.s_addr >> 24) & 0xff; + printf("%u.%u.%u.%u:%d connected (pid %ld).\n", a, b, c, d, + (int)ntohs(sin.sin_port), (long)p); + pid_t chald; + int status; + while ((chald = wait4(WAIT_ANY, &status, WNOHANG, NULL)) > 0) + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) + printf("pid %ld terminated abnormally\n", + chald); + else + printf("pid %ld finished\n", chald); + if (chald == -1 && errno != ECHILD) + err(-1, "wait4"); + } + return 0; +} diff --git a/biscuit/c/sfork.c b/biscuit/c/sfork.c new file mode 100644 index 0000000..9535682 --- /dev/null +++ b/biscuit/c/sfork.c @@ -0,0 +1,808 @@ +// gcc -static -O3 -o sfork sfork.c -Wl,--whole-archive,-lpthread,--no-whole-archive +// openbsd: gcc -nopie -fno-pic -static -O3 -o sfork sfork.c -Wl,--whole-archive,-lpthread,--no-whole-archive + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +// ms +ulong now() +{ + struct timeval t; + if (gettimeofday(&t, NULL)) + errx(-1, "gettimeofday"); + return t.tv_sec * 1000 + t.tv_usec / 1000; +} + +long jointot(pthread_t t[], const int nthreads) +{ + long total = 0; + int i; + for (i = 0; i < nthreads; i++) { + void *retp; + if (pthread_join(t[i], &retp)) + errx(-1, "pthread join"); + long ret = (long)retp; + if (ret < 0) + errx(-1, "thread failed"); + total += ret; + } + return total; +} + +pthread_barrier_t bar; +static int volatile cease; + +static int bmsecs = 10; +static int nthreads; + +void *crrename(void *idp) +{ + pthread_barrier_wait(&bar); + + char o[32]; + char n[32]; + + long id = (long)idp; + int pid = getpid(); + snprintf(o, sizeof(o), "o%d-%ld", pid, id); + snprintf(n, sizeof(n), "n%d-%ld", pid, id); + + int fd = open(o, O_CREAT | O_RDONLY, 0600); + if (fd < 0) + err(-1, "open"); + close(fd); + + long total = 0; + while (!cease) { + if (rename(o, n) < 0) + err(-1, "rename"); + if (rename(n, o) < 0) + err(-1, "rename"); + total += 2; + } + return (void *)total; +} + +void *lookups(void *idp) +{ + pthread_barrier_wait(&bar); + + long total = 0; + while (!cease) { + if (open("NONE", O_RDONLY) >= 0 || errno != ENOENT) + err(-1, "open: unexpected error"); + total++; + } + return (void *)total; +} + +void *mapper(void *n) +{ + pthread_barrier_wait(&bar); + + long c = 0; + while (!cease) { + size_t l = 4096; + void *p = mmap(NULL, l, PROT_READ, MAP_PRIVATE | MAP_ANON, + -1, 0); + if (p == MAP_FAILED) + errx(-1, "mmap"); + if (munmap(p, l) < 0) + err(-1, "munmap"); + + //getpid(); + c++; + } + printf("dune\n"); + return (void *)c; +} + +#define SYSCALL_CLOBBERS "cc", "memory", "r9", "r10", "r11", "r12", "r13", \ + "r14", "r15" +pid_t +_getppid(void) +{ + pid_t ret; + asm volatile( + "movq %%rsp, %%r10\n" + "leaq 2(%%rip), %%r11\n" + "sysenter\n" + : "=a"(ret) + : "0"(40ul) + : "cc", "memory", "r9", "r10", "r11", "edi", "esi", "edx", "ecx", "r8"); + return ret; +} + + +void *igetpids(void *idp) +{ + pthread_barrier_wait(&bar); + + long total = 0; + while (!cease) { + asm volatile( + "movl $40, %%eax\n" + "movq %%rsp, %%r10\n" + "leaq 2(%%rip), %%r11\n" + "sysenter\n" + : + : + : SYSCALL_CLOBBERS, "eax", "edi", "esi", "edx", "ecx", "r8"); + total++; + } + return (void *)total; +} + +void *getpids(void *idp) +{ + pthread_barrier_wait(&bar); + + long total = 0; + while (!cease) { + _getppid(); + total++; + } + return (void *)total; +} + +void bm(char const *tl, void *(*fn)(void *)) +{ + cease = 0; + + pthread_barrier_init(&bar, NULL, nthreads + 1); + + pthread_t t[nthreads]; + int i; + for (i = 0; i < nthreads; i++) + if (pthread_create(&t[i], NULL, fn, (void *)(long)i)) + errx(-1, "pthread create"); + + pthread_barrier_wait(&bar); + + //if (sys_prof(PROF_GOLANG, 0, 0, 0) == -1) + // err(-1, "prof"); + + ulong st = now(); + sleep(bmsecs); + + cease = 1; + ulong beforejoin = now(); + long total = jointot(t, nthreads); + ulong actual = now() - st; + + //if (sys_prof(PROF_DISABLE|PROF_GOLANG, 0, 0, 0) == -1) + // err(-1, "prof"); + + printf("%s ran for %lu ms (slept %lu)\n", tl, actual, beforejoin - st); + double secs = (double)actual / 1000; + printf("\tops: %lf /sec\n\n", (double)total/secs); +} + +char const *sunsock = "sock"; + +void sunspawn() +{ + if (fork() != 0) { + sleep(1); + return; + } + + unlink(sunsock); + + struct sockaddr_un sa; + sa.sun_family = AF_UNIX; + strncpy(sa.sun_path, sunsock, sizeof(sa.sun_path)); + + int fd; + if ((fd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) + err(-1, "socket"); + + int ret; + if (bind(fd, (struct sockaddr *)&sa, sizeof(sa)) < 0) + err(-1, "bind"); + + ulong mcnt; + ulong bytes; + mcnt = bytes = 0; + char buf[64]; + while ((ret = recv(fd, buf, sizeof(buf), 0)) > 0) { + if (!strncmp(buf, "exit", 4)) { + ret = 0; + break; + } + mcnt++; + bytes += ret; + } + + if (ret < 0) + err(-1, "recv"); + close(fd); + + printf("%lu total messages received (%lu bytes)\n", mcnt, bytes); + exit(0); +} + +void sunkill() +{ + struct sockaddr_un sa; + sa.sun_family = AF_UNIX; + strncpy(sa.sun_path, sunsock, sizeof(sa.sun_path)); + + int fd; + if ((fd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) + err(-1, "socket"); + + char msg[] = "exit"; + int ret; + ret = sendto(fd, msg, sizeof(msg), 0, (struct sockaddr *)&sa, + sizeof(sa)); + if (ret < 0) + err(-1, "sendto"); + else if (ret != sizeof(msg)) + errx(-1, "short write"); + close(fd); + + printf("waiting for daemon:\n"); + int status; + wait(&status); + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) + errx(-1, "sun daemon exited with status %d", status); + printf("done\n"); +} + +void *sunsend(void *idp) +{ + struct sockaddr_un sa; + sa.sun_family = AF_UNIX; + strncpy(sa.sun_path, sunsock, sizeof(sa.sun_path)); + + int fd; + if ((fd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) + err(-1, "socket"); + + pthread_barrier_wait(&bar); + + long total = 0; + const char msg[] = "123456789"; + while (!cease) { + int ret; + ret = sendto(fd, msg, sizeof(msg), 0, (struct sockaddr *)&sa, + sizeof(sa)); + if (ret < 0) + err(-1, "sendto"); + else if (ret != sizeof(msg)) + errx(-1, "short write"); + + total++; + } + return (void *)total; +} + +void *forkonly(void *idp) +{ + pthread_barrier_wait(&bar); + + long total = 0; + while (!cease) { + int pid = fork(); + if (pid < 0) + err(-1, "fork"); + if (!pid) { + exit(0); + } + int status; + wait(&status); + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) + errx(-1, "child failed: %d", status); + total++; + } + return (void *)total; +} + +void *forkexec(void *idp) +{ + pthread_barrier_wait(&bar); + + long total = 0; + while (!cease) { + int pid = fork(); + if (pid < 0) + err(-1, "fork"); + if (!pid) { + char * const args[] = {"./true", NULL}; + execv(args[0], args); + err(-1, "execv"); + } + int status; + wait(&status); + int code = WEXITSTATUS(status); + if (!WIFEXITED(status) || code != 0) + errx(-1, "child failed: %d", code); + total++; + } + return (void *)total; +} + +void *seqcreate(void *idp) +{ + pthread_barrier_wait(&bar); + + long id = (long)idp; + int pid = getpid(); + + long total = 0; + while (!cease) { + char o[64]; + snprintf(o, sizeof(o), "f%d-%ld.%ld", pid, id, total); + + int fd = open(o, O_CREAT | O_WRONLY | O_EXCL, 0600); + if (fd < 0) + err(-1, "open"); + close(fd); + total++; + } + return (void *)total; +} + +void *openonly(void *idp) +{ + const char *f = "dur"; + int fd = open(f, O_CREAT | O_WRONLY, 0600); + if (fd < 0) + err(-1, "create"); + close(fd); + pthread_barrier_wait(&bar); + + long total = 0; + while (!cease) { + fd = open(f, O_RDONLY); + if (fd < 0) + err(-1, "open"); + close(fd); + total++; + } + return (void *)total; +} + +void __attribute__((noreturn)) child(int cd) +{ + char dir[32]; + snprintf(dir, sizeof(dir), "%d", cd); + //char *args[] = {"/bin/time", "mailbench", dir, "2", NULL}; + char *args[] = {"/bin/mailbench", dir, "1", NULL}; + execv(args[0], args); + err(-1, "execv"); +} + +int mbforever() +{ + int cd = 0; + for (;;) { + printf(" directory: %d\n", cd); + char dn[32]; + snprintf(dn, sizeof(dn), "%d", cd); + if (mkdir(dn, 0700) < 0) + err(-1, "mkdir"); + if (fork() == 0) + child(cd); + wait(NULL); + cd++; + } +} + +const char * const _websock = ".websock"; + +static void *webclient(void *p) +{ + pthread_barrier_wait(&bar); + + struct sockaddr_un sa; + sa.sun_family = AF_UNIX; + strncpy(sa.sun_path, _websock, sizeof(sa.sun_path)); + + long total = 0; + while (!cease) { + int s = socket(AF_UNIX, SOCK_STREAM, 0); + if (s == -1) + err(-1, "socket"); + + if (connect(s, (struct sockaddr *)&sa, sizeof(sa)) == -1) + err(-1, "connect"); + + char gmsg[] = "GET /"; + if (write(s, gmsg, sizeof(gmsg)) != sizeof(gmsg)) + err(-1, "write"); + char buf[1024]; + ssize_t r; + while ((r = read(s, buf, sizeof(buf))) > 0) + ; + if (r == -1) + err(-1, "read"); + if (close(s) == -1) + err(-1, "close"); + total++; + } + return (void *)total; +} + +static void _websv(void) +{ + int sfd = open("/clouseau.txt", O_RDONLY); + if (sfd == -1) + err(-1, "open"); + + int s = socket(AF_UNIX, SOCK_STREAM, 0); + if (s == -1) + err(-1, "socket"); + + struct sockaddr_un sa; + sa.sun_family = AF_UNIX; + strncpy(sa.sun_path, _websock, sizeof(sa.sun_path)); + + if (bind(s, (struct sockaddr *)&sa, SUN_LEN(&sa)) == -1) + err(-1, "bind"); + if (listen(s, 10) == -1) + err(-1, "listen"); + + while (1) { + int ns; + if ((ns = accept(s, NULL, NULL)) == -1) + err(-1, "accept"); + + char req[64]; + ssize_t r; + if ((r = read(ns, req, sizeof(req))) == -1) + err(-1, "read"); + if (strncmp(req, "exit", sizeof(req)) == 0) { + if (unlink(_websock) == -1) + err(-1, "unlink"); + exit(0); + } + + if (lseek(sfd, 0, SEEK_SET) == -1) + err(-1, "lseek"); + char buf[1024]; + while ((r = read(sfd, buf, sizeof(buf))) > 0) { + if (write(ns, buf, r) != r) + err(-1, "write"); + } + if (r == -1) + err(-1, "read"); + if (close(ns) == -1) + err(-1, "close"); + } +} + +static void webstart(void) +{ + pid_t r; + if ((r = fork()) == -1) + err(-1, "fork"); + if (!r) + _websv(); + // give server some time to initialize + sleep(1); +} + +static void webstop(void) +{ + int s = socket(AF_UNIX, SOCK_STREAM, 0); + if (s == -1) + err(-1, "socket"); + + struct sockaddr_un sa; + sa.sun_family = AF_UNIX; + strncpy(sa.sun_path, _websock, sizeof(sa.sun_path)); + + if (connect(s, (struct sockaddr *)&sa, sizeof(sa)) == -1) + err(-1, "connect"); + + char em[] = "exit"; + if (write(s, em, sizeof(em)) != sizeof(em)) + err(-1, "write"); + if (close(s) == -1) + err(-1, "close"); + int status; + wait(&status); + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) + errx(-1, "websv exited with status %d", status); +} + +static int pipec[2]; +static int pipep[2]; + +static void pingstart(void) +{ + if (pipe(pipec) == -1) + err(-1, "pipe"); + if (pipe(pipep) == -1) + err(-1, "pipe"); + pid_t c; + if ((c = fork()) == -1) + err(-1, "fork"); + if (c) { + close(pipec[0]); + close(pipep[1]); + return; + } + close(pipep[0]); + close(pipec[1]); + char mymsg[] = "pong"; + char *emsg = "exit"; + while (1) { + char buf[32]; + if (read(pipec[0], buf, sizeof(buf)) != 5) + err(-1, "chald read"); + if (buf[0] == 'e' && strcmp(buf, emsg) == 0) + break; + if (write(pipep[1], mymsg, sizeof(mymsg)) != sizeof(mymsg)) + err(-1, "chald write"); + } + exit(0); +} + +static void pingstop(void) +{ + char emsg[] = "exit"; + if (write(pipec[1], emsg, sizeof(emsg)) != sizeof(emsg)) + err(-1, "write exit"); + char buf[64]; + while (read(pipep[0], buf, sizeof(buf)) > 0) + ; + int status; + if (wait(&status) == -1) + err(-1, "wait"); + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) + errx(-1, "chald failed"); + close(pipec[1]); + close(pipep[0]); +} + +static void *pingpong(void *_a) +{ + pthread_barrier_wait(&bar); + + char mymsg[] = "pong"; + long tot = 0; + while (!cease) { + char buf[32]; + if (write(pipec[1], mymsg, sizeof(mymsg)) != sizeof(mymsg)) + err(-1, "par write"); + if (read(pipep[0], buf, sizeof(buf)) != 5) + err(-1, "par write"); + tot++; + } + return (void *)tot; +} + +static void *_poll(const int nfds) +{ + if (nfds <= 0) + errx(-1, "bad nfds"); + + int fds[nfds]; + int i; + for (i = 0; i < nfds; i++) { + char buf[64]; + snprintf(buf, sizeof(buf), "/tmp/.poll%d", i); + unlink(buf); + if ((fds[i] = open(buf, O_CREAT | O_EXCL | O_RDWR, + 0600)) == -1) + err(-1, "creat"); + } + + struct pollfd pfds[nfds]; + for (i = 0; i < nfds; i++) { + pfds[i].fd = fds[i]; + if (i % 2) + pfds[i].events = POLLIN; + else + pfds[i].events = POLLOUT; + } + + pthread_barrier_wait(&bar); + long tot = 0; + while (!cease) { + if (poll(pfds, nfds, 0) == -1) + err(-1, "poll"); + tot++; + } + for (i = 0; i < nfds; i++) + close(fds[i]); + return (void *)tot; +} + +static void *poll50(void *_a) +{ + return _poll(50); +} + +static void *poll1(void *_a) +{ + return _poll(1); +} + +static long alloc_amount; + +static void *alloc(void *_a) +{ + if (alloc_amount <= 0) + errx(-1, "bad alloc amount %ld", alloc_amount); + pthread_barrier_wait(&bar); + long tot = 0; + while (!cease) { + const long hack3 = 1 << 6; + if (sys_prof(hack3, alloc_amount, 0, 0) == -1) + err(-1, "sysprof"); + tot++; + } + return (void *)tot; +} + +#define _ST1 "dir1/" +#define _ST2 "dir2/" +#define _ST3 "file" +#define STFN (_ST1 _ST2 _ST3) + +static void *statty(void *_a) +{ + long tot = 0; + pthread_barrier_wait(&bar); + + struct stat st; + while (!cease) { + if (stat(STFN, &st) == -1) + err(-1, "stat"); + tot++; + } + return (void *)tot; +} + +static void stat_st() +{ + if (mkdir(_ST1, 0755) == -1 && errno != EEXIST) + err(-1, "mkdir"); + if (mkdir(_ST1 _ST2, 0755) == -1 && errno != EEXIST) + err(-1, "mkdir"); + int fd; + if ((fd = open(STFN, O_CREAT | O_WRONLY, 0644)) == -1) + err(-1, "mkdir"); + if (close(fd) == -1) + err(-1, "close"); +} + +void *locks(void *_arg) +{ + long tot = 0; + pthread_barrier_wait(&bar); + + while (!cease) { + asm("lock incq %0\n" + : + : "m"(tot) + : "cc", "memory"); + } + return (void *)tot; +} + +struct { + char *name; + char sname; + void *(*fn)(void *); + void (*begin)(void); + void (*end)(void); +} bms[] = { + {"renames", 'r', crrename, NULL, NULL}, + {"getpids", 'c', getpids, NULL, NULL}, + {"unix socket", 'u', sunsend, sunspawn, sunkill}, + {"inline getpids", 'p', igetpids, NULL, NULL}, + {"forkonly", 'k', forkonly, NULL, NULL}, + {"forkexec", 'e', forkexec, NULL, NULL}, + {"seqcreate", 's', seqcreate, NULL, NULL}, + {"openonly", 'o', openonly, NULL, NULL}, + {"webserver", 'w', webclient, webstart, webstop}, + {"pipe ping pong", 'P', pingpong, pingstart, pingstop}, + {"mmap/munmap", 'm', mapper, NULL, NULL}, + {"poll50", '5', poll50, NULL, NULL}, + {"poll1", '1', poll1, NULL, NULL}, + {"alloc", 'a', alloc, NULL, NULL}, + {"stat", 'S', statty, stat_st, NULL}, + {"locks", 'l', locks, NULL, NULL}, +}; + +const int nbms = sizeof(bms)/sizeof(bms[0]); + +void usage(char *n) +{ + printf( "usage:\n" + "%s [-s seconds] [-A int] [-b ] \n" + " -s seconds\n" + " run benchmark for seconds\n" + " -A \n" + " amount to allocate in bytes for alloc benchmark\n" + " -b \n" + " benchmark ids:\n" + , n); + int i; + for (i = 0; i < nbms; i++) + printf(" %c %s\n", bms[i].sname, bms[i].name); + printf("\n"); + + printf( "%s -m\n" + " run mailbench forever\n\n", n); + exit(-1); +} + +int main(int argc, char **argv) +{ + printf("> "); + int i; + for (i = 0; i < argc; i++) + printf("%s ", argv[i]); + printf("\n"); + + char *n = argv[0]; + char onebm = 0; + + int ch; + while ((ch = getopt(argc, argv, "A:mb:s:")) != -1) { + switch (ch) { + case 'A': + alloc_amount = strtol(optarg, NULL, 0); + break; + case 's': + bmsecs = atoi(optarg); + break; + case 'b': + onebm = *optarg; + break; + case 'm': + printf("running mailbench forever...\n"); + return mbforever(); + default: + usage(n); + } + } + argc -= optind; + argv += optind; + if (argc != 1) + usage(n); + + nthreads = atoi(argv[0]); + if (nthreads < 0) + nthreads = 3; + printf("using %d threads for %d seconds\n", nthreads, bmsecs); + + int did = 0; + for (i = 0; i < nbms; i++) { + if (onebm && onebm != bms[i].sname) + continue; + did++; + if (bms[i].begin) + bms[i].begin(); + bm(bms[i].name, bms[i].fn); + if (bms[i].end) + bms[i].end(); + } + if (!did) { + printf("no benchmarks executed; no such bm\n"); + exit(-1); + } + + return 0; +} diff --git a/biscuit/c/sleep.c b/biscuit/c/sleep.c new file mode 100644 index 0000000..accc261 --- /dev/null +++ b/biscuit/c/sleep.c @@ -0,0 +1,14 @@ +#include + +int main(int argc, char **argv) +{ + if (argc != 2) { + printf("usage: %s \n", argv[0]); + exit(-1); + } + + uint secs = atoi(argv[1]); + if (secs < 0) + errx(-1, "negative time"); + return sleep(secs); +} diff --git a/biscuit/c/smallfile.c b/biscuit/c/smallfile.c new file mode 100644 index 0000000..4842f34 --- /dev/null +++ b/biscuit/c/smallfile.c @@ -0,0 +1,103 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define N 1000 +#define FILESIZE 100 +#define NAMESIZE 100 +#define BUFSIZE 4096 + +static char name[NAMESIZE]; +static char buf[BUFSIZE]; +static char *dir; + +void printstats(int reset) +{ + int fd; + int r; + + sprintf(name, "dev/stats"); + if((fd = open(name, O_RDONLY)) < 0) { + return; + } + + memset(buf, 0, FILESIZE); + + if ((r = read(fd, buf, BUFSIZE)) < 0) { + perror("read"); + exit(1); + } + + if (!reset) + fprintf(stdout, "=== FS Stats ===\n%s========\n", buf); + + if ((r = close(fd)) < 0) { + perror("close"); + } +} + +static uint64_t +usec_now() +{ + struct timeval tv; + + gettimeofday(&tv, NULL); + + uint64_t res = tv.tv_sec; + res *= 1000000; + res += tv.tv_usec; + return res; +} + +int main(int argc, char *argv[]) +{ + int i; + int fd; + uint64_t start, end; + + if (argc != 2) { + printf("Usage: %s basedir\n", argv[0]); + exit(-1); + } + + dir = argv[1]; + sprintf(buf, "%s/d", argv[1]); + if (mkdir(buf, S_IRWXU) < 0) { + printf("%s: create %s failed %s\n", argv[0], buf, strerror(errno)); + exit(1); + } + printstats(1); + start = usec_now(); + for (i = 0; ; i++) { + sprintf(name, "%s/d/f%d", argv[1], i); + if((fd = open(name, O_RDWR | O_CREAT | O_TRUNC, S_IRWXU)) < 0) { + printf("%s: create %s failed %s\n", argv[0], name, strerror(errno)); + exit(1); + } + if (write(fd, buf, FILESIZE) != FILESIZE) { + printf("%s: write %s failed %s\n", argv[0], name, strerror(errno)); + exit(1); + } + if (fsync(fd) < 0) { + printf("%s: fsync %s failed %s\n", argv[0], name, strerror(errno)); + exit(1); + } + close(fd); + + end = usec_now(); + if (end - start > 1000000) + break; + } + + printf("%d files per %ld usec\n", i, end-start); + + printstats(0); + return 0; +} diff --git a/biscuit/c/sockettest.c b/biscuit/c/sockettest.c new file mode 100644 index 0000000..6b2104a --- /dev/null +++ b/biscuit/c/sockettest.c @@ -0,0 +1,269 @@ +#include + +void sendall(int fd, char *buf, size_t len, struct sockaddr_un *sa, + socklen_t slen) +{ + size_t c = 0; + int ret; + while (c < len) { + ret = sendto(fd, buf + c, len - c, 0, (struct sockaddr *)sa, + slen); + if (ret < 0) + err(ret, "sendall"); + c += ret; + } +} + +void sendmany() +{ + int s = socket(AF_UNIX, SOCK_DGRAM, 0); + if (s < 0) + err(s, "socket"); + + int ret; + struct sockaddr_un me; + memset(&me, 0, sizeof(struct sockaddr_un)); + me.sun_family = AF_UNIX; + snprintf(me.sun_path, sizeof(me.sun_path), "/tmp/child"); + if ((ret = bind(s, (struct sockaddr *)&me, + sizeof(struct sockaddr_un))) < 0) + err(ret, "bind: %d", ret); + + struct sockaddr_un sa; + sa.sun_family = AF_UNIX; + snprintf(sa.sun_path, sizeof(sa.sun_path), "/tmp/parent"); + + int i; + for (i = 0; i < 100; i++) { + char buf[64]; + snprintf(buf, sizeof(buf), "message %d", i); + size_t mlen = strlen(buf); + //sendall(s, buf, strlen(buf), &sa, sizeof(struct sockaddr_un)); + int ret = sendto(s, buf, mlen, 0, (void *)&sa, + sizeof(struct sockaddr_un)); + if (ret < 0) + err(ret, "sendto"); + if (ret != mlen) + printf("short write! (lost %ld bytes)\n", mlen - ret); + } + printf("**sender done\n"); + exit(0); +} + +void datagram() +{ + printf("datagram test\n"); + + unlink("/tmp/parent"); + unlink("/tmp/child"); + + int s = socket(AF_UNIX, SOCK_DGRAM, 0); + if (s < 0) + err(s, "socket"); + + int ret; + struct sockaddr_un sa; + memset(&sa, 0, sizeof(struct sockaddr_un)); + sa.sun_family = AF_UNIX; + snprintf(sa.sun_path, sizeof(sa.sun_path), "/tmp/parent"); + if ((ret = bind(s, (struct sockaddr *)&sa, sizeof(sa))) < 0) + err(ret, "bind: %d", ret); + + int pid; + if ((pid = fork()) < 0) + err(pid, "fork"); + if (pid == 0) { + close(s); + sendmany(); + } + + char buf[64]; + int i; + struct timespec to = {0, 1000000}; + for (i = 0; i < 100; i++) { + // stall to make sure the socket buffer fills up and blocks the + // sender + nanosleep(&to, NULL); + struct sockaddr_un cli; + socklen_t clen = sizeof(struct sockaddr_un); + ret = recvfrom(s, buf, sizeof(buf) - 1, 0, + (struct sockaddr *)&cli, &clen); + if (ret < 0) + err(ret, "recvfrom"); + buf[ret] = 0; + //printf("from: %s got: %s\n", cli.sun_path, buf); + char cmp[64]; + snprintf(cmp, sizeof(cmp), "message %d", i); + if (strncmp(buf, cmp, strlen(cmp))) + errx(-1, "msg mismatch"); + } + + int status; + if (wait(&status) != pid) + errx(-1, "wrong child"); + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) + errx(-1, "child failed"); + + printf("datagram test OK\n"); +} + +__attribute__((noreturn)) +void connector(char *path, char *msg1, char *msg2) +{ + struct sockaddr_un sa; + sa.sun_family = AF_UNIX; + strncpy(sa.sun_path, path, sizeof(sa.sun_path)); + + int s = socket(AF_UNIX, SOCK_STREAM, 0); + if (s < 0) + err(-1, "socket"); + + if (connect(s, (struct sockaddr *)&sa, sizeof(sa)) < 0) + err(-1, "connect"); + ssize_t r; + if ((r = write(s, msg1, strlen(msg1))) != strlen(msg1)) + err(-1, "write failed (%ld != %lu)", r, strlen(msg1)); + close(s); + + s = socket(AF_UNIX, SOCK_STREAM, 0); + if (s < 0) + err(-1, "socket"); + + if (connect(s, (struct sockaddr *)&sa, sizeof(sa)) < 0) + err(-1, "connect"); + if ((r = write(s, msg2, strlen(msg2))) != strlen(msg2)) + err(-1, "write failed (%ld != %lu)", r, strlen(msg2)); + + char buf[256]; + // parent writes two messages in a row; make sure we read them one at a + // time + if ((r = read(s, buf, strlen(msg1))) != strlen(msg1)) + err(-1, "c1 read failed (%ld != %lu)", r, strlen(msg1)); + if (strncmp(buf, msg1, strlen(msg1))) + errx(-1, "string mismatch"); + + char *mymsg = "child"; + char *theirmsg = "parent"; + if ((r = write(s, mymsg, strlen(mymsg))) != strlen(mymsg)) + err(-1, "write failed (%ld != %lu)", r, strlen(mymsg)); + if ((r = read(s, buf, sizeof(buf))) != strlen(theirmsg)) + err(-1, "c2 read failed (%ld != %lu)", r, strlen(theirmsg)); + if (strncmp(buf, theirmsg, strlen(theirmsg))) + errx(-1, "unexpected data"); + close(s); + + exit(0); +} + +void stream() +{ + printf("stream test\n"); + + char *path = "/tmp/stream"; + char *msg1 = "hello1"; + char *msg2 = "hello2 happy day"; + + unlink(path); + + int s = socket(AF_UNIX, SOCK_STREAM, 0); + if (s < 0) + err(-1, "socket"); + + struct sockaddr_un sa; + sa.sun_family = AF_UNIX; + strncpy(sa.sun_path, path, sizeof(sa.sun_path)); + + if (bind(s, (struct sockaddr *)&sa, SUN_LEN(&sa)) < 0) + err(-1, "bind"); + + if (listen(s, 10) < 0) + err(-1, "listen"); + + pid_t child = fork(); + if (child < 0) + err(-1, "fork"); + if (!child) { + close(s); + connector(path, msg1, msg2); + } + + int c = accept(s, NULL, NULL); + if (c < 0) + err(-1, "accept"); + + int sockerr; + socklen_t solen = sizeof(sockerr); + if (getsockopt(c, SOL_SOCKET, SO_ERROR, &sockerr, &solen)) + err(-1, "getsockopt"); + if (sockerr != 0) + errx(-1, "socket error"); + + char buf[256]; + ssize_t r; + if ((r = read(c, buf, sizeof(buf) - 1)) < 0) + err(-1, "read"); + buf[r] = 0; + if (strncmp(buf, msg1, strlen(msg1))) + errx(-1, "msg mismatch"); + if (close(c)) + err(-1, "closey"); + + c = accept(s, NULL, NULL); + if (c < 0) + err(-1, "accept"); + + // make sure non-blocking accept fails at this point + int fl; + if ((fl = fcntl(s, F_GETFL)) == -1) + err(-1, "fcntlg"); + if (fcntl(s, F_SETFL, fl | O_NONBLOCK) == -1) + err(-1, "fcntls"); + if (accept(s, NULL, NULL) != -1 || errno != EWOULDBLOCK) + errx(-1, "expected EWOULDBLOCK"); + if (fcntl(s, F_SETFL, fl) == -1) + err(-1, "fcntls"); + + if ((r = read(c, buf, sizeof(buf) - 1)) < 0) + err(-1, "read"); + buf[r] = 0; + if (strncmp(buf, msg2, strlen(msg2))) + errx(-1, "msg mismatch"); + + if ((r = write(c, msg1, strlen(msg1))) != strlen(msg1)) + err(-1, "write failed (%ld != %lu)", r, strlen(msg1)); + + char *mymsg = "parent"; + char *theirmsg = "child"; + if ((r = write(c, mymsg, strlen(mymsg))) != strlen(mymsg)) + err(-1, "write failed (%ld != %lu)", r, strlen(mymsg)); + if ((r = read(c, buf, sizeof(buf))) != strlen(theirmsg)) + err(-1, "s1 read failed (%ld != %lu)", r, strlen(theirmsg)); + if (strncmp(buf, theirmsg, strlen(theirmsg))) + errx(-1, "unexpected data"); + + // child closed connection; writes should fail, should get EOF + if ((r = read(c, buf, sizeof(buf))) != 0) + errx(-1, "expected failure1 %ld %d", r, errno); + if (write(c, buf, 10) != -1 || errno != ECONNRESET) + errx(-1, "expected failure2"); + + if (close(c)) + err(-1, "closey"); + if (close(s)) + err(-1, "closey"); + + int status; + if (wait(&status) != child) + errx(-1, "wrong child"); + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) + errx(-1, "child failed"); + + printf("stream test OK\n"); +} + +int main(int argc, char **argv) +{ + datagram(); + stream(); + return 0; +} diff --git a/biscuit/c/stress.c b/biscuit/c/stress.c new file mode 100644 index 0000000..1ea4c01 --- /dev/null +++ b/biscuit/c/stress.c @@ -0,0 +1,62 @@ +#include + +void generate(void) +{ + int up = 1; + for (;;) { + for (; up != 0; up--) { + switch (fork()) { + case -1: + err(-1, "fork"); + break; + case 0: + return; + } + } + while (waitpid(WAIT_ANY, NULL, WNOHANG) > 0) + up++; + sleep(1); + } +} + +int main(int argc, char **argv) +{ + srand(time(NULL)); + //pid_t master = getpid(); + //switch (fork()) { + //case 0: + // dirents(); + //case -1: + // err(-1, "fork"); + //} + + generate(); + + int afd, bfd; + if ((afd = open("/", O_RDONLY | O_DIRECTORY)) == -1) + err(-1, "open"); + if ((bfd = open("/", O_RDONLY | O_DIRECTORY)) == -1) + err(-1, "open"); + + //char buf[512]; + //sprintf(buf, "%ld", getpid()); + //for (;;) { + // if (open(buf, O_CREAT | O_RDWR, 0600) == -1) + // break; + // if (unlink(buf) == -1) + // err(-1, "unlink"); + //} + + for (;;) { + void *p = mmap(NULL, 4096, PROT_READ | PROT_WRITE, + MAP_PRIVATE, afd, 0); + if (p == MAP_FAILED) + err(-1, "mmap"); + p = mmap(NULL, 4096, PROT_READ | PROT_WRITE, + MAP_PRIVATE, bfd, 0); + if (p == MAP_FAILED) + err(-1, "mmap"); + } + + return 0; +} diff --git a/biscuit/c/sync.c b/biscuit/c/sync.c new file mode 100644 index 0000000..ae11287 --- /dev/null +++ b/biscuit/c/sync.c @@ -0,0 +1,6 @@ +#include + +int main(int argc, char **argv) +{ + return sync(); +} diff --git a/biscuit/c/thtests.c b/biscuit/c/thtests.c new file mode 100644 index 0000000..1bfb37e --- /dev/null +++ b/biscuit/c/thtests.c @@ -0,0 +1,92 @@ +#include + +void *mkstack(size_t size) +{ + const size_t pgsize = 1 << 12; + size += pgsize - 1; + size &= ~(pgsize - 1); + char *ret = mmap(NULL, size, PROT_READ | PROT_WRITE, + MAP_ANON | MAP_PRIVATE, -1, 0); + if (!ret) + errx(-1, "couldn't mmap"); + return ret + size; +} + +void loop(char *msg, const int iters) +{ + int j; + for (j = 0; j < iters; j++) + // appease gcc + printf("%s", msg); +} + +__thread long tlvar; + +long child(void *msg) +{ + printf("thread var is: %ld\n", tlvar); + printf("child loop (msg: %s)\n", (char *)msg); + char buf[10]; + snprintf(buf, sizeof(buf), "%ld ", tlvar); + loop(buf, 30); + printf("child exit\n"); + return tlvar; +} + +static long tls1; +static long tls2; +static void *tcbs[] = {(char *)&tls1 + 8, (char *)&tls2 + 8}; + +int main(int argc, char **argv) +{ + printf("start thread tests\n"); + + //int pid = fork(); + //if (pid == 0) { + // threxit(1001); + // errx(-1, "why no exit"); + // return -2; + //} + //int status; + //if (wait(&status) != pid) + // errx(-1, "wrong pid: %d\n", pid); + //if (status != 0) + // errx(-1, "posix is sad"); + + struct tfork_t tf; + + tf.tf_tcb = &tcbs[0]; + tf.tf_tid = &tls1; + tf.tf_stack = mkstack(4096*1); + int tid = tfork_thread(&tf, child, "child 1!"); + if (tid < 0) + err(tid, "tfork"); + if (tid != tls1) + errx(-1, "tid mismatch"); + + tf.tf_tcb = &tcbs[1]; + tf.tf_tid = &tls2; + tf.tf_stack = mkstack(4096*1); + tid = tfork_thread(&tf, child, "child 2!"); + if (tid < 0) + err(tid, "tfork"); + if (tid != tls2) + errx(-1, "tid mismatch"); + + printf("parent loop\n"); + loop("p ", 10); + + printf("waiting for child threads\n"); + long status; + if ((tid = thrwait(tls1, &status)) != tls1) + err(tid, "thrwait1"); + if (status != tls1) + errx(-1, "bad status %ld %ld", status, tls1); + if ((tid = thrwait(tls2, &status)) != tls2) + err(tid, "thrwait2"); + if (status != tls2) + errx(-1, "bad status %ld %ld", status, tls2); + printf("parent exit\n"); + + return 0; +} diff --git a/biscuit/c/time.c b/biscuit/c/time.c new file mode 100644 index 0000000..0403044 --- /dev/null +++ b/biscuit/c/time.c @@ -0,0 +1,218 @@ +#include + +ulong now() +{ + struct timeval t; + int ret; + if ((ret = gettimeofday(&t, NULL)) < 0) + err(-1, "gettimeofday"); + + // ms + return t.tv_sec * 1000 + t.tv_usec / 1000; +} + +__attribute__((noreturn)) +void usage(char *pre) +{ + if (pre) + fprintf(stderr, "%s\n\n", pre); + errx(-1, "usage: %s [-bgr] [-sc pmf] [-e evt] [-i int] " + " ...\n" + "\n" + "-b record backtrace; only used with -s\n" + "-r goprofile command\n" + "-g report GC statistics for command\n" + "-s sample via PMU. must provide \"pmf\" (see below)\n" + " and provide an event via -e\n" + "-c count events via PMU. must provide \"pmf\"\n" + " (see below) and provide an event via -e\n" + "pmf a one or two character string indicating when to\n" + " count/sample the specified events. \"u\" and \"s\"\n" + " cause PMU monitoring during userspace and system\n" + " execution, repsectively. these flags may be\n" + " combined (\"us\").\n" + "-e evt use \"evt\" for PMU sampling/counting. for PMU\n" + " counting, -e may be specified more than once.\n" + "evt a string indicating which symbolic event the PMU\n" + " should monitor. valid strings are:\n" + " \"cpu\" - unhalted cpu cycles\n" + " \"binst\" - branch instructions\n" + " \"bmiss\" - branch misses\n" + " \"llcref\" - LLC references\n" + " \"llcmiss\" - LLC misses\n" + " \"dtlbmissl\" - dTLB load misses\n" + " \"dtlbmisss\" - dTLB store misses\n" + " \"itlbmiss\" - iTLB misses\n" + " \"ins\" - instructions retired\n" + "-i int sample after int PMU events. only used with -s.\n" + "\n" + , __progname); +} + +long ppmf(char *pmf) +{ + long ret = 0; + if (strchr(pmf, 'u')) + ret |= PROF_EVF_USR; + if (strchr(pmf, 's')) + ret |= PROF_EVF_OS; + if (!ret) + usage("invalid pmf"); + return ret; +} + +long evtadd(char *evt) +{ + struct { + char *name; + long evid; + } evs[] = { + {"cpu", PROF_EV_UNHALTED_CORE_CYCLES}, + {"binst", PROF_EV_BRANCH_INSTR_RETIRED}, + {"bmiss", PROF_EV_BRANCH_MISS_RETIRED}, + {"llcmiss", PROF_EV_LLC_MISSES}, + {"llcref", PROF_EV_LLC_REFS}, + {"dtlbmissl", PROF_EV_DTLB_LOAD_MISS_ANY}, + {"dtlbmisss", PROF_EV_STORE_DTLB_MISS}, + {"itlbmiss", PROF_EV_ITLB_LOAD_MISS_ANY}, + {"ins", PROF_EV_INSTR_RETIRED}, + }; + const int nevs = sizeof(evs)/sizeof(evs[0]); + int i; + for (i = 0; i < nevs; i++) { + if (strcmp(evs[i].name, evt) != 0) + continue; + return evs[i].evid; + } + usage("invalid evt"); +} + +int main(int argc, char **argv) +{ + int goprof = 0, gcstat = 0, nevts = 0, bt = 0; + long pmuc = 0, pmus = 0, evt = 0, intperiod=1000000; + int ch; + while ((ch = getopt(argc, argv, "bi:s:c:e:gr")) != -1) { + switch (ch) { + case 'b': + bt = 1; + break; + case 'c': + pmuc = ppmf(optarg); + break; + case 'e': + evt |= evtadd(optarg); + nevts++; + break; + case 'g': + gcstat = 1; + break; + case 'i': + intperiod = strtol(optarg, NULL, 0); + break; + case 'r': + goprof = 1; + break; + case 's': + pmus = ppmf(optarg); + break; + default: + fprintf(stderr, "bad option: %c\n", ch); + usage(NULL); + break; + } + } + argc -= optind; + argv += optind; + + if (argc < 1) + usage(NULL); + if (pmuc && pmus) + usage("cannot use -s and -c"); + if ((pmuc || pmus) && evt == 0) + usage("must specify -e at least once"); + if (pmus && nevts != 1) + usage("must specify -e exactly once for sampling"); + if (bt && !pmus) + usage("-b can only be used with sampling"); + + ulong start = now(); + + // start profiling + if (bt) + pmus |= PROF_EVF_BACKTRACE; + if (goprof && sys_prof(PROF_GOLANG, 0, 0, 0) == -1) + errx(-1, "prof start"); + if (pmuc && sys_prof(PROF_COUNT, evt, pmuc, 0) == -1) + errx(-1, "sys prof"); + else if (pmus && sys_prof(PROF_SAMPLE, evt, pmus, intperiod) == -1) + errx(-1, "sys prof"); + struct gcfrac_t fracst; + long sgc, talloc; + if (gcstat) { + fracst = gcfracst(); + sgc = sys_info(SINFO_GCCOUNT); + if (sgc == -1) + err(-1, "sysinfo"); + talloc = sys_info(SINFO_GCTOTALLOC); + if (talloc == -1) + err(-1, "sysinfo"); + } + + if (fork() == 0) { + execvp(argv[0], &argv[0]); + err(-1, "execv"); + } + + struct rusage r; + int status; + int ret = wait4(WAIT_ANY, &status, 0, &r); + if (ret < 0) + err(-1, "wait4"); + ulong elapsed = now() - start; + + if (gcstat) { + long wbms, markms, sweepms; + double gccpu = gcfracend(&fracst, &markms, &sweepms, &wbms); + fprintf(stderr, "GC CPU frac: %f%% (no write barriers)\n", + gccpu); + fprintf(stderr, " total elapsed times:\n"); + fprintf(stderr, " mark ms: %ld\n", markms); + fprintf(stderr, " sweep ms: %ld\n", sweepms); + fprintf(stderr, " writeb ms: %ld\n", wbms); + + long egc = sys_info(SINFO_GCCOUNT); + if (egc == -1) + err(-1, "sysinfo"); + fprintf(stderr, "GCs: %ld\n", egc - sgc); + long etalloc = sys_info(SINFO_GCTOTALLOC); + if (etalloc == -1) + err(-1, "sysinfo"); + long bpms = (etalloc - talloc) / elapsed; + double ar = (double)bpms * 1000 / (1 << 20); + fprintf(stderr, "Allocation rate: %f MB/sec\n", ar); + + long kobjs = sys_info(SINFO_GCOBJS); + if (kobjs == -1) + err(-1, "sysinfo"); + fprintf(stderr, "Number of kernel objects: %ld\n", kobjs); + } + // stop profiling + if (goprof && sys_prof(PROF_DISABLE|PROF_GOLANG, 0, 0, 0) == -1) + errx(-1, "prof stop"); + if (pmuc && sys_prof(PROF_DISABLE|PROF_COUNT, evt, pmuc, 0) == -1) + errx(-1, "sys prof stop"); + else if (pmus && sys_prof(PROF_DISABLE|PROF_SAMPLE, 0, 0, 0) == -1) + errx(-1, "sys prof stop"); + + ret = WEXITSTATUS(status); + if (!WIFEXITED(status) || ret) + fprintf(stderr, "child failed with status: %d\n", ret); + + fprintf(stderr, "%lu seconds, %lu ms\n", elapsed/1000, elapsed%1000); + fprintf(stderr, "user time: %lu seconds, %lu us\n", + r.ru_utime.tv_sec, r.ru_utime.tv_usec); + fprintf(stderr, "system time: %lu seconds, %lu us\n", + r.ru_stime.tv_sec, r.ru_stime.tv_usec); + return ret; +} diff --git a/biscuit/c/touch.c b/biscuit/c/touch.c new file mode 100644 index 0000000..dc14edc --- /dev/null +++ b/biscuit/c/touch.c @@ -0,0 +1,17 @@ +#include + +int main(int argc, char **argv) +{ + if (argc != 2) + errx(-1, "usage: %s \n", argv[0]); + + char *f = argv[1]; + int fd; + fd = open(f, O_CREAT, 0600); + if (fd < 0) + err(fd, "open"); + fd = close(fd); + if (fd) + err(fd, "close"); + return 0; +} diff --git a/biscuit/c/true.c b/biscuit/c/true.c new file mode 100644 index 0000000..5019e79 --- /dev/null +++ b/biscuit/c/true.c @@ -0,0 +1,4 @@ +int main(int argc, char **argv) +{ + return 0; +} diff --git a/biscuit/c/uname.c b/biscuit/c/uname.c new file mode 100644 index 0000000..8683ec4 --- /dev/null +++ b/biscuit/c/uname.c @@ -0,0 +1,80 @@ +#include +#include + +#include + +__attribute__((noreturn)) +void usage(void) +{ + printf( "usage:\n" + "%s [-amnrsv]\n" + "\n" + "-a print all utsname data\n" + "-m hardware name\n" + "-n host name\n" + "-r OS release\n" + "-s OS name\n" + "-v OS version\n" + "\n", __progname); + exit(-1); +} + +int main(int argc, char **argv) +{ + int sys, nn, rel, ver, mach; + sys = nn = rel = ver = mach = 0; + + int isopt = 0; + int c; + while ((c = getopt(argc, argv, "amnrsv")) != -1) { + isopt = 1; + switch (c) { + case 'a': + sys = nn = rel = ver = mach = 1; + break; + case 'm': + mach = 1; + break; + case 'n': + nn = 1; + break; + case 'r': + rel = 1; + break; + case 's': + sys = 1; + break; + case 'v': + ver = 1; + break; + default: + usage(); + } + } + if (!isopt) + sys = 1; + + struct utsname un; + if (uname(&un)) + err(-1, "uname (wut)"); + + int sp = 0; +#define PR(f) do { const char *_fmt; \ + if (sp) _fmt = " %s"; \ + else _fmt = "%s"; \ + printf(_fmt, un.f); \ + sp = 1; \ + } while(0) + if (sys) + PR(sysname); + if (nn) + PR(nodename); + if (rel) + PR(release); + if (ver) + PR(version); + if (mach) + PR(machine); + printf("\n"); + return 0; +} diff --git a/biscuit/c/unlink.c b/biscuit/c/unlink.c new file mode 100644 index 0000000..23bde42 --- /dev/null +++ b/biscuit/c/unlink.c @@ -0,0 +1,18 @@ +#include + +int main(int argc, char **argv) +{ + if (argc != 2) + errx(-1, "usage: %s \n", argv[0]); + + char *p = argv[1]; + struct stat st; + if (stat(p, &st) == -1) + err(-1, "stat"); + if (S_ISDIR(st.st_mode)) { + if (rmdir(p) == -1) + err(-1, "rmdir"); + } else if (unlink(p) == -1) + err(-1, "unlink"); + return 0; +} diff --git a/biscuit/c/usertests.c b/biscuit/c/usertests.c new file mode 100644 index 0000000..ae01e23 --- /dev/null +++ b/biscuit/c/usertests.c @@ -0,0 +1,3870 @@ +#include +#include +#include + +#define BSIZE 4096 +#define NADDR (BSIZE/8) + +char buf[2*BSIZE]; +char name[3]; +char *echoargv[] = { "echo", "ALL", "TESTS", "PASSED", 0 }; + +#define mkdir(x) mkdir(x, 0) +#define open(x, y) open(x, y, 0) +#define exec(x, y) execv(x, y) +#define kill(x) kill(x, SIGKILL) + + +#define O_CREATE O_CREAT +#define MAXFILE NADDR*NADDR + +// does chdir() call iput(p->cwd) in a transaction? +void +iputtest(void) +{ + printf("iput test\n"); + + if(mkdir("iputdir") < 0){ + printf("mkdir failed\n"); + exit(0); + } + if(chdir("iputdir") < 0){ + printf("chdir iputdir failed\n"); + exit(0); + } + if(rmdir("../iputdir") < 0){ + printf("rmdir ../iputdir failed\n"); + exit(0); + } + if(chdir("/") < 0){ + printf("chdir / failed\n"); + exit(0); + } + printf("iput test ok\n"); +} + +// does exit(0) call iput(p->cwd) in a transaction? +void +exitiputtest(void) +{ + int pid; + + printf("exitiput test\n"); + + pid = fork(); + if(pid < 0){ + printf("fork failed\n"); + exit(0); + } + if(pid == 0){ + if(mkdir("iputdir") < 0){ + printf("mkdir failed\n"); + exit(0); + } + if(chdir("iputdir") < 0){ + printf("child chdir failed\n"); + exit(0); + } + if(rmdir("../iputdir") < 0){ + printf("rmdir ../iputdir failed\n"); + exit(0); + } + exit(0); + } + wait(NULL); + printf("exitiput test ok\n"); +} + +// does the error path in open() for attempt to write a +// directory call iput() in a transaction? +// needs a hacked kernel that pauses just after the namei() +// call in sys_open(): +// if((ip = namei(path)) == 0) +// return -1; +// { +// int i; +// for(i = 0; i < 10000; i++) +// yield(); +// } +void +openiputtest(void) +{ + int pid; + + printf("openiput test\n"); + if(mkdir("oidir") < 0){ + printf("mkdir oidir failed\n"); + exit(0); + } + pid = fork(); + if(pid < 0){ + printf("fork failed\n"); + exit(0); + } + if(pid == 0){ + int fd = open("oidir", O_RDWR); + if(fd >= 0){ + printf("open directory for write succeeded\n"); + exit(0); + } + exit(0); + } + sleep(1); + if(rmdir("oidir") != 0){ + printf("unlink failed\n"); + exit(0); + } + wait(NULL); + printf("openiput test ok\n"); +} + +// simple file system tests + +void +opentest(void) +{ + int fd; + + printf("open test\n"); + fd = open("/bin/echo", 0); + if(fd < 0){ + printf("open echo failed!\n"); + exit(0); + } + close(fd); + fd = open("doesnotexist", 0); + if(fd >= 0){ + printf("open doesnotexist succeeded!\n"); + exit(0); + } + printf("open test ok\n"); +} + +void +writetest(void) +{ + int fd; + int i; + + printf("small file test\n"); + fd = open("small", O_CREATE|O_RDWR); + if(fd >= 0){ + printf("creat small succeeded; ok\n"); + } else { + printf("error: creat small failed!\n"); + exit(0); + } + for(i = 0; i < 100; i++){ + if(write(fd, "aaaaaaaaaa", 10) != 10){ + printf("error: write aa %d new file failed\n", i); + exit(0); + } + if(write(fd, "bbbbbbbbbb", 10) != 10){ + printf("error: write bb %d new file failed\n", i); + exit(0); + } + } + printf("writes ok\n"); + close(fd); + fd = open("small", O_RDONLY); + if(fd >= 0){ + printf("open small succeeded ok\n"); + } else { + printf("error: open small failed!\n"); + exit(0); + } + i = read(fd, buf, 2000); + if(i == 2000){ + printf("read succeeded ok\n"); + } else { + printf("read failed\n"); + exit(0); + } + close(fd); + + if(unlink("small") < 0){ + printf("unlink small failed\n"); + exit(0); + } + printf("small file test ok\n"); +} + +void +writetest1(void) +{ + int i, fd, n; + + printf("big files test\n"); + + fd = open("big", O_CREATE|O_RDWR); + if(fd < 0){ + printf("error: creat big failed!\n"); + exit(0); + } + + for(i = 0; i < MAXFILE; i++){ + ((int*)buf)[0] = i; + if(write(fd, buf, BSIZE) != BSIZE){ + printf("error: write big file failed\n"); + exit(0); + } + } + + close(fd); + + fd = open("big", O_RDONLY); + if(fd < 0){ + printf("error: open big failed!\n"); + exit(0); + } + + n = 0; + for(;;){ + i = read(fd, buf, BSIZE); + if(i == 0){ + if(n == MAXFILE - 1){ + printf("read only %d blocks from big", n); + exit(0); + } + break; + } else if(i != BSIZE){ + printf("read failed %d\n", i); + exit(0); + } + if(((int*)buf)[0] != n){ + printf("read content of block %d is %d\n", + n, ((int*)buf)[0]); + exit(0); + } + n++; + } + close(fd); + if(unlink("big") < 0){ + printf("unlink big failed\n"); + exit(0); + } + printf("big files ok\n"); +} + +void +createtest(void) +{ + int i, fd; + + printf("many creates, followed by unlink test\n"); + + name[0] = 'a'; + name[2] = '\0'; + for(i = 0; i < 52; i++){ + name[1] = '0' + i; + fd = open(name, O_CREATE|O_RDWR); + close(fd); + } + name[0] = 'a'; + name[2] = '\0'; + for(i = 0; i < 52; i++){ + name[1] = '0' + i; + unlink(name); + } + printf("many creates, followed by unlink; ok\n"); +} + +void dirtest(void) +{ + printf("mkdir test\n"); + + if(mkdir("dir0") < 0){ + printf("mkdir failed\n"); + exit(0); + } + + if(chdir("dir0") < 0){ + printf("chdir dir0 failed\n"); + exit(0); + } + + if(chdir("..") < 0){ + printf("chdir .. failed\n"); + exit(0); + } + + if(rmdir("dir0") < 0){ + printf("rmdir dir0 failed\n"); + exit(0); + } + if (mkdir("/") != -1) + errx(-1, "expected failure"); + printf("mkdir test ok\n"); +} + +void +exectest(void) +{ + printf("exec test\n"); + if(exec("/bin/echo", echoargv) < 0){ + printf("exec echo failed\n"); + exit(0); + } +} + +// simple fork and pipe read/write + +void +pipe1(void) +{ + if (1033 > PIPE_BUF) + errx(-1, "need to write 1033 bytes atomically"); + int fds[2], pid; + int seq, i, n, cc, total; + printf("pipe1 test\n"); + + if(pipe(fds) != 0){ + printf("pipe() failed\n"); + exit(0); + } + pid = fork(); + seq = 0; + if(pid == 0){ + close(fds[0]); + for(n = 0; n < 5; n++){ + for(i = 0; i < 1033; i++) + buf[i] = seq++; + if(write(fds[1], buf, 1033) != 1033){ + errx(-1, "pipe1 oops 1\n"); + } + } + exit(0); + } else if(pid > 0){ + close(fds[1]); + total = 0; + cc = 1; + while((n = read(fds[0], buf, cc)) > 0){ + for(i = 0; i < n; i++){ + if((buf[i] & 0xff) != (seq++ & 0xff)){ + errx(-1, "pipe1 oops 2\n"); + } + } + total += n; + cc = cc * 2; + if(cc > sizeof(buf)) + cc = sizeof(buf); + } + if(total != 5 * 1033){ + printf("pipe1 oops 3 total %d\n", total); + exit(0); + } + close(fds[0]); + wait(NULL); + } else { + err(-1, "fork() failed\n"); + } + printf("pipe1 ok\n"); +} + +// meant to be run w/ at most two CPUs +void +preempt(void) +{ + int pid1, pid2, pid3; + int pfds[2]; + + printf("preempt: "); + pid1 = fork(); + if(pid1 == 0) + for(;;) + ; + + pid2 = fork(); + if(pid2 == 0) + for(;;) + ; + + pipe(pfds); + pid3 = fork(); + if(pid3 == 0){ + close(pfds[0]); + if(write(pfds[1], "x", 1) != 1) + printf("preempt write error"); + close(pfds[1]); + for(;;) + ; + } + + close(pfds[1]); + if(read(pfds[0], buf, sizeof(buf)) != 1){ + printf("preempt read error"); + return; + } + close(pfds[0]); + printf("kill... "); + kill(pid1); + kill(pid2); + kill(pid3); + printf("wait... "); + wait(NULL); + wait(NULL); + wait(NULL); + printf("preempt ok\n"); +} + +void _childfault(void) +{ + char *p = NULL; + *p = 'a'; + errx(-1, "no fault"); +} + +void stchk(int status, int expsig) +{ + if (expsig) { + if (WIFCONTINUED(status)) + errx(-1, "unexpected continued"); + if (WIFEXITED(status)) + errx(-1, "unexpected exited"); + if (!WIFSIGNALED(status)) + errx(-1, "expected signaled"); + if (WTERMSIG(status) != expsig) + errx(-1, "signal mismatch"); + } else { + if (WIFCONTINUED(status)) + errx(-1, "unexpected continued"); + if (!WIFEXITED(status)) + errx(-1, "expected exited"); + if (WIFSIGNALED(status)) + errx(-1, "unexpected signaled"); + if (WTERMSIG(status) != 0) + errx(-1, "signal mismatch"); + } +} + +// try to find any races between exit and wait +void +exitwait(void) +{ + int i, pid; + + for(i = 0; i < 100; i++){ + pid = fork(); + if(pid < 0){ + printf("fork failed\n"); + return; + } + if(pid){ + if(wait(NULL) != pid){ + printf("wait wrong pid\n"); + return; + } + } else { + exit(0); + } + } + printf("exitwait ok\n"); + + printf("exit status test\n"); + if (!fork()) + _childfault(); + int status; + wait(&status); + stchk(status, SIGSEGV); + if (!fork()) + exit(0); + wait(&status); + stchk(status, 0); + printf("exit status test ok\n"); +} + +void +mem(void) +{ + void *m1, *m2; + int pid, ppid; + + printf("mem test\n"); + ppid = getpid(); + if((pid = fork()) == 0){ + m1 = 0; + while((m2 = malloc(10001)) != 0){ + *(char**)m2 = m1; + m1 = m2; + } + while(m1){ + m2 = *(char**)m1; + free(m1); + m1 = m2; + } + m1 = malloc(1024*20); + if(m1 == 0){ + printf("couldn't allocate mem?!!\n"); + kill(ppid); + exit(0); + } + free(m1); + printf("mem ok\n"); + exit(0); + } else { + wait(NULL); + } +} + +// More file system tests + +// two processes write to the same file descriptor +// is the offset shared? does inode locking work? +void +sharedfd(void) +{ + int fd, pid, i, n, nc, np; + char buf[10]; + + printf("sharedfd test\n"); + + fd = open("sharedfd", O_CREATE|O_RDWR); + if(fd < 0) + err(fd, "fstests: cannot open sharedfd for writing"); + pid = fork(); + memset(buf, pid==0?'c':'p', sizeof(buf)); + for(i = 0; i < 1000; i++){ + if(write(fd, buf, sizeof(buf)) != sizeof(buf)) + errx(-1, "fstests: write sharedfd failed\n"); + } + if(pid == 0) + exit(0); + else + wait(NULL); + close(fd); + fd = open("sharedfd", 0); + if(fd < 0) + err(fd, "fstests: cannot open sharedfd for reading\n"); + nc = np = 0; + while((n = read(fd, buf, sizeof(buf))) > 0){ + for(i = 0; i < sizeof(buf); i++){ + if(buf[i] == 'c') + nc++; + if(buf[i] == 'p') + np++; + } + } + close(fd); + unlink("sharedfd"); + if(nc == 10000 && np == 10000){ + printf("sharedfd ok\n"); + } else { + printf("sharedfd oops %d %d\n", nc, np); + exit(0); + } +} + +// four processes write different files at the same +// time, to test block allocation. +void +fourfiles(void) +{ + int fd, pid, i, j, n, total, pi; + char *names[] = { "f0", "f1", "f2", "f3" }; + char *fname; + + printf("fourfiles test\n"); + + for(pi = 0; pi < 4; pi++){ + fname = names[pi]; + unlink(fname); + + pid = fork(); + if(pid < 0){ + printf("fork failed\n"); + exit(0); + } + + if(pid == 0){ + fd = open(fname, O_CREATE | O_RDWR); + if(fd < 0){ + printf("create failed\n"); + exit(0); + } + + memset(buf, '0'+pi, BSIZE); + for(i = 0; i < 12; i++){ + if((n = write(fd, buf, BSIZE)) != BSIZE){ + printf("write failed %d\n", n); + exit(0); + } + } + exit(0); + } + } + + for(pi = 0; pi < 4; pi++){ + wait(NULL); + } + + for(i = 0; i < 4; i++){ + fname = names[i]; + fd = open(fname, 0); + total = 0; + while((n = read(fd, buf, sizeof(buf))) > 0){ + for(j = 0; j < n; j++){ + if(buf[j] != '0'+i){ + printf("wrong char\n"); + exit(0); + } + } + total += n; + } + close(fd); + if(total != 12*BSIZE){ + printf("wrong length %d\n", total); + exit(0); + } + unlink(fname); + } + + printf("fourfiles ok\n"); +} + +// four processes create and delete different files in same directory +void +createdelete(void) +{ + enum { N = 20 }; + int pid, i, fd, pi; + char name[32]; + + printf("createdelete test\n"); + + for(pi = 0; pi < 4; pi++){ + pid = fork(); + if(pid < 0){ + printf("fork failed\n"); + exit(0); + } + + if(pid == 0){ + name[0] = 'p' + pi; + name[2] = '\0'; + for(i = 0; i < N; i++){ + name[1] = '0' + i; + fd = open(name, O_CREATE | O_RDWR); + if(fd < 0){ + printf("create failed\n"); + exit(0); + } + close(fd); + if(i > 0 && (i % 2 ) == 0){ + name[1] = '0' + (i / 2); + if(unlink(name) < 0){ + printf("unlink failed\n"); + exit(0); + } + } + } + exit(0); + } + } + + for(pi = 0; pi < 4; pi++){ + wait(NULL); + } + + name[0] = name[1] = name[2] = 0; + for(i = 0; i < N; i++){ + for(pi = 0; pi < 4; pi++){ + name[0] = 'p' + pi; + name[1] = '0' + i; + fd = open(name, 0); + if((i == 0 || i >= N/2) && fd < 0){ + printf("oops createdelete %s didn't exist\n", name); + exit(0); + } else if((i >= 1 && i < N/2) && fd >= 0){ + printf("oops createdelete %s did exist\n", name); + exit(0); + } + if(fd >= 0) + close(fd); + } + } + + for(i = 0; i < N; i++){ + for(pi = 0; pi < 4; pi++){ + name[0] = 'p' + i; + name[1] = '0' + i; + unlink(name); + } + } + + printf("createdelete ok\n"); +} + +// can I unlink a file and still read it? +void +unlinkread(void) +{ + int fd, fd1; + + printf("unlinkread test\n"); + fd = open("unlinkread", O_CREATE | O_RDWR); + if(fd < 0){ + printf("create unlinkread failed\n"); + exit(0); + } + write(fd, "hello", 5); + close(fd); + + fd = open("unlinkread", O_RDWR); + if(fd < 0){ + printf("open unlinkread failed\n"); + exit(0); + } + if(unlink("unlinkread") != 0){ + printf("unlink unlinkread failed\n"); + exit(0); + } + + fd1 = open("unlinkread", O_CREATE | O_RDWR); + write(fd1, "yyy", 3); + close(fd1); + + if(read(fd, buf, sizeof(buf)) != 5){ + printf("unlinkread read failed"); + exit(0); + } + if(buf[0] != 'h'){ + printf("unlinkread wrong data\n"); + exit(0); + } + if(write(fd, buf, 10) != 10){ + printf("unlinkread write failed\n"); + exit(0); + } + close(fd); + unlink("unlinkread"); + printf("unlinkread ok\n"); +} + +void +linktest(void) +{ + int fd; + + printf("linktest\n"); + + unlink("lf1"); + unlink("lf2"); + + fd = open("lf1", O_CREATE|O_RDWR); + if(fd < 0){ + printf("create lf1 failed\n"); + exit(0); + } + if(write(fd, "hello", 5) != 5){ + printf("write lf1 failed\n"); + exit(0); + } + close(fd); + + if(link("lf1", "lf2") < 0){ + printf("link lf1 lf2 failed\n"); + exit(0); + } + unlink("lf1"); + + if(open("lf1", 0) >= 0){ + printf("unlinked lf1 but it is still there!\n"); + exit(0); + } + + fd = open("lf2", 0); + if(fd < 0){ + printf("open lf2 failed\n"); + exit(0); + } + if(read(fd, buf, sizeof(buf)) != 5){ + printf("read lf2 failed\n"); + exit(0); + } + close(fd); + + if(link("lf2", "lf2") >= 0){ + printf("link lf2 lf2 succeeded! oops\n"); + exit(0); + } + + unlink("lf2"); + if(link("lf2", "lf1") >= 0){ + printf("link non-existant succeeded! oops\n"); + exit(0); + } + + if(link(".", "lf1") >= 0){ + printf("link . lf1 succeeded! oops\n"); + exit(0); + } + + printf("linktest ok\n"); +} + +struct __attribute__((packed)) dirent_t { +#define NMAX 14 + char name[NMAX]; + ulong inum; +}; + +struct dirdata_t { +#define NDIRENTS (BSIZE/sizeof(struct dirent_t)) + struct dirent_t de[NDIRENTS]; +}; + +// test concurrent create/link/unlink of the same file +void +concreate(void) +{ + char file[3]; + int i, pid, n, fd; + char fa[40]; + char buf[BSIZE]; + + printf("concreate test\n"); + file[0] = 'C'; + file[2] = '\0'; + for(i = 0; i < 40; i++){ + file[1] = '0' + i; + unlink(file); + pid = fork(); + if(pid && (i % 3) == 1){ + link("C0", file); + } else if(pid == 0 && (i % 5) == 1){ + link("C0", file); + } else { + fd = open(file, O_CREATE | O_RDWR); + if(fd < 0){ + printf("concreate create %s failed\n", file); + exit(0); + } + close(fd); + } + if(pid == 0) + exit(0); + else + wait(NULL); + } + + memset(fa, 0, sizeof(fa)); + fd = open(".", 0); + if (fd < 0) + err(fd, "open"); + n = 0; + while(read(fd, buf, sizeof(buf)) == sizeof(buf)){ + struct dirdata_t *dd = (struct dirdata_t *)buf; + int j; + for (j = 0; j < NDIRENTS; j++) { + if(dd->de[j].inum == 0) + continue; + if(dd->de[j].name[0] == 'C' && dd->de[j].name[2] == '\0'){ + i = dd->de[j].name[1] - '0'; + if(i < 0 || i >= sizeof(fa)){ + printf("concreate weird file %s\n", dd->de[j].name); + exit(0); + } + if(fa[i]){ + printf("concreate duplicate file %s\n", dd->de[j].name); + exit(0); + } + fa[i] = 1; + n++; + } + } + } + close(fd); + + if(n != 40){ + printf("concreate not enough files in directory listing (%d)\n", n); + exit(0); + } + + for(i = 0; i < 40; i++){ + file[1] = '0' + i; + pid = fork(); + if(pid < 0){ + printf("fork failed\n"); + exit(0); + } + if(((i % 3) == 0 && pid == 0) || + ((i % 3) == 1 && pid != 0)){ + close(open(file, 0)); + close(open(file, 0)); + close(open(file, 0)); + close(open(file, 0)); + } else { + unlink(file); + unlink(file); + unlink(file); + unlink(file); + } + if(pid == 0) + exit(0); + else + wait(NULL); + } + + printf("concreate ok\n"); +} + +// another concurrent link/unlink/create test, +// to look for deadlocks. +void +linkunlink() +{ + int pid, i; + + printf("linkunlink test\n"); + + unlink("x"); + pid = fork(); + if(pid < 0){ + printf("fork failed\n"); + exit(0); + } + + unsigned int x = (pid ? 1 : 97); + for(i = 0; i < 1000; i++){ + x = x * 1103515245 + 12345; + if((x % 3) == 0){ + close(open("x", O_RDWR | O_CREATE)); + } else if((x % 3) == 1){ + link("/bin/cat", "x"); + } else { + unlink("x"); + } + } + + if(pid) + wait(NULL); + else + exit(0); + + printf("linkunlink ok\n"); +} + +// directory that uses indirect blocks +void +bigdir(void) +{ + int i, fd; + char name[10]; + + printf("bigdir test\n"); + unlink("bd"); + + fd = open("bd", O_CREATE); + if(fd < 0){ + printf("bigdir create failed\n"); + exit(0); + } + close(fd); + + for(i = 0; i < 500; i++){ + name[0] = 'x'; + name[1] = '0' + (i / 64); + name[2] = '0' + (i % 64); + name[3] = '\0'; + if(link("bd", name) != 0){ + printf("bigdir link failed\n"); + exit(0); + } + } + + unlink("bd"); + for(i = 0; i < 500; i++){ + name[0] = 'x'; + name[1] = '0' + (i / 64); + name[2] = '0' + (i % 64); + name[3] = '\0'; + if(unlink(name) != 0){ + printf("bigdir unlink failed"); + exit(0); + } + } + + printf("bigdir ok\n"); +} + +void +subdir(void) +{ + int fd, cc; + + printf("subdir test\n"); + + rmdir("ff"); + if(mkdir("dd") != 0){ + printf("subdir mkdir dd failed\n"); + exit(0); + } + + fd = open("dd/ff", O_CREATE | O_RDWR); + if(fd < 0){ + printf("create dd/ff failed\n"); + exit(0); + } + write(fd, "ff", 2); + close(fd); + + if(rmdir("dd") >= 0){ + printf("unlink dd (non-empty dir) succeeded!\n"); + exit(0); + } + + if(mkdir("/dd/dd") != 0){ + printf("subdir mkdir dd/dd failed\n"); + exit(0); + } + + fd = open("dd/dd/ff", O_CREATE | O_RDWR); + if(fd < 0){ + printf("create dd/dd/ff failed\n"); + exit(0); + } + write(fd, "FF", 2); + close(fd); + + fd = open("dd/dd/../ff", 0); + if(fd < 0){ + printf("open dd/dd/../ff failed\n"); + exit(0); + } + cc = read(fd, buf, sizeof(buf)); + if(cc != 2 || buf[0] != 'f'){ + printf("dd/dd/../ff wrong content\n"); + exit(0); + } + close(fd); + + if(link("dd/dd/ff", "dd/dd/ffff") != 0){ + printf("link dd/dd/ff dd/dd/ffff failed\n"); + exit(0); + } + + if(unlink("dd/dd/ff") != 0){ + printf("unlink dd/dd/ff failed\n"); + exit(0); + } + if(open("dd/dd/ff", O_RDONLY) >= 0){ + printf("open (unlinked) dd/dd/ff succeeded\n"); + exit(0); + } + + if(chdir("dd") != 0){ + printf("chdir dd failed\n"); + exit(0); + } + if(chdir("dd/../../dd") != 0){ + printf("chdir dd/../../dd failed\n"); + exit(0); + } + if(chdir("dd/../../../dd") != 0){ + printf("chdir dd/../../dd failed\n"); + exit(0); + } + if(chdir("./..") != 0){ + printf("chdir ./.. failed\n"); + exit(0); + } + + fd = open("dd/dd/ffff", 0); + if(fd < 0){ + printf("open dd/dd/ffff failed\n"); + exit(0); + } + if(read(fd, buf, sizeof(buf)) != 2){ + printf("read dd/dd/ffff wrong len\n"); + exit(0); + } + close(fd); + + if(open("dd/dd/ff", O_RDONLY) >= 0){ + printf("open (unlinked) dd/dd/ff succeeded!\n"); + exit(0); + } + + if(open("dd/ff/ff", O_CREATE|O_RDWR) >= 0){ + printf("create dd/ff/ff succeeded!\n"); + exit(0); + } + if(open("dd/xx/ff", O_CREATE|O_RDWR) >= 0){ + printf("create dd/xx/ff succeeded!\n"); + exit(0); + } + if(open("dd", O_CREATE) >= 0){ + printf("create dd succeeded!\n"); + exit(0); + } + if(open("dd", O_RDWR) >= 0){ + printf("open dd rdwr succeeded!\n"); + exit(0); + } + if(open("dd", O_WRONLY) >= 0){ + printf("open dd wronly succeeded!\n"); + exit(0); + } + if(link("dd/ff/ff", "dd/dd/xx") == 0){ + printf("link dd/ff/ff dd/dd/xx succeeded!\n"); + exit(0); + } + if(link("dd/xx/ff", "dd/dd/xx") == 0){ + printf("link dd/xx/ff dd/dd/xx succeeded!\n"); + exit(0); + } + if(link("dd/ff", "dd/dd/ffff") == 0){ + printf("link dd/ff dd/dd/ffff succeeded!\n"); + exit(0); + } + if(mkdir("dd/ff/ff") == 0){ + printf("mkdir dd/ff/ff succeeded!\n"); + exit(0); + } + if(mkdir("dd/xx/ff") == 0){ + printf("mkdir dd/xx/ff succeeded!\n"); + exit(0); + } + if(mkdir("dd/dd/ffff") == 0){ + printf("mkdir dd/dd/ffff succeeded!\n"); + exit(0); + } + if(rmdir("dd/xx/ff") == 0){ + printf("rmdir dd/xx/ff succeeded!\n"); + exit(0); + } + if(rmdir("dd/ff/ff") == 0){ + printf("rmdir dd/ff/ff succeeded!\n"); + exit(0); + } + if(chdir("dd/ff") == 0){ + printf("chdir dd/ff succeeded!\n"); + exit(0); + } + if(chdir("dd/xx") == 0){ + printf("chdir dd/xx succeeded!\n"); + exit(0); + } + + if(unlink("dd/dd/ffff") != 0){ + printf("unlink dd/dd/ff failed\n"); + exit(0); + } + if(unlink("dd/ff") != 0){ + printf("unlink dd/ff failed\n"); + exit(0); + } + if(rmdir("dd") == 0){ + printf("rmdir non-empty dd succeeded!\n"); + exit(0); + } + if(rmdir("dd/dd") < 0){ + printf("rmdir dd/dd failed\n"); + exit(0); + } + if(rmdir("dd") < 0){ + printf("rmdir dd failed\n"); + exit(0); + } + + printf("subdir ok\n"); +} + +// test writes that are larger than the log. +void +bigwrite(void) +{ + int fd, sz; + + printf("bigwrite test\n"); + + unlink("bigwrite"); + for(sz = 499; sz < 12*BSIZE; sz += 471){ + fd = open("bigwrite", O_CREATE | O_RDWR); + if(fd < 0){ + printf("cannot create bigwrite\n"); + exit(0); + } + int i; + char *b = malloc(sz); + if (b == NULL) { + printf("malloc failed\n"); + exit(0); + } + for(i = 0; i < 2; i++){ + int cc = write(fd, b, sz); + if(cc != sz){ + printf("write(%d) ret %d\n", sz, cc); + exit(0); + } + } + free(b); + close(fd); + unlink("bigwrite"); + } + + printf("bigwrite ok\n"); +} + +void +bigfile(void) +{ + int fd, i, total, cc; + #define NBLOCK 5000 + #define SZ 8000 + + printf("bigfile test\n"); + + unlink("bigfile"); + fd = open("bigfile", O_CREATE | O_RDWR); + if(fd < 0){ + printf("cannot create bigfile"); + exit(0); + } + for(i = 0; i < NBLOCK; i++){ + memset(buf, i%10, SZ); + if(write(fd, buf, SZ) != SZ){ + printf("write bigfile failed\n"); + exit(0); + } + } + fsync(fd); + close(fd); + + + fd = open("bigfile", 0); + if(fd < 0){ + printf("cannot open bigfile\n"); + exit(0); + } + total = 0; + for(i = 0; ; i++){ + cc = read(fd, buf, SZ/2); + if(cc < 0){ + printf("read bigfile failed\n"); + exit(0); + } + if(cc == 0) + break; + if(cc != SZ/2){ + printf("short read bigfile\n"); + exit(0); + } + if(buf[0] != (i/2)%10 || buf[SZ/2-1] != (i/2)%10){ + printf("read bigfile wrong data %d %c %c\n", i, buf[0], buf[SZ/2-1]); + exit(0); + } + total += cc; + } + close(fd); + if(total != NBLOCK*SZ){ + printf("read bigfile wrong total\n"); + exit(0); + } + unlink("bigfile"); + + printf("bigfile test ok\n"); +} + +void +fourteen(void) +{ + int fd; + + printf("not checking for filename truncation...\n"); + // DIRSIZ is 14. + printf("fourteen test\n"); + + if(mkdir("12345678901234") != 0){ + printf("mkdir 12345678901234 failed\n"); + exit(0); + } + //if(mkdir("12345678901234/123456789012345") != 0){ + if(mkdir("12345678901234/12345678901234") != 0){ + //printf("mkdir 12345678901234/123456789012345 failed\n"); + printf("mkdir 12345678901234/12345678901234 failed\n"); + exit(0); + } + //fd = open("123456789012345/123456789012345/123456789012345", O_CREATE); + fd = open("12345678901234/12345678901234/12345678901234", O_CREATE); + if(fd < 0){ + //printf("create 123456789012345/123456789012345/123456789012345 failed\n"); + printf("create 12345678901234/12345678901234/12345678901234 failed\n"); + exit(0); + } + close(fd); + fd = open("12345678901234/12345678901234/12345678901234", 0); + if(fd < 0){ + printf("open 12345678901234/12345678901234/12345678901234 failed\n"); + exit(0); + } + close(fd); + + if(mkdir("12345678901234/12345678901234") == 0){ + printf("mkdir 12345678901234/12345678901234 succeeded!\n"); + exit(0); + } + if(mkdir("123456789012345/12345678901234") == 0){ + printf("mkdir 12345678901234/123456789012345 succeeded!\n"); + exit(0); + } + + printf("fourteen ok\n"); +} + +void +rmdot(void) +{ + printf("rmdot test\n"); + if(mkdir("dots") != 0){ + printf("mkdir dots failed\n"); + exit(0); + } + if(chdir("dots") != 0){ + printf("chdir dots failed\n"); + exit(0); + } + if(rmdir(".") == 0){ + printf("rm . worked!\n"); + exit(0); + } + if(rmdir("..") == 0){ + printf("rm .. worked!\n"); + exit(0); + } + if(chdir("/") != 0){ + printf("chdir / failed\n"); + exit(0); + } + if(rmdir("dots/.") == 0){ + printf("rmdir dots/. worked!\n"); + exit(0); + } + if(rmdir("dots/..") == 0){ + printf("rmdir dots/.. worked!\n"); + exit(0); + } + if(rmdir("dots") != 0){ + printf("rmdir dots failed!\n"); + exit(0); + } + printf("rmdot ok\n"); +} + +void +dirfile(void) +{ + int fd; + + printf("dir vs file\n"); + + fd = open("dirfile", O_CREATE); + if(fd < 0){ + printf("create dirfile failed\n"); + exit(0); + } + close(fd); + if(chdir("dirfile") == 0){ + printf("chdir dirfile succeeded!\n"); + exit(0); + } + fd = open("dirfile/xx", 0); + if(fd >= 0){ + printf("create dirfile/xx succeeded!\n"); + exit(0); + } + fd = open("dirfile/xx", O_CREATE); + if(fd >= 0){ + printf("create dirfile/xx succeeded!\n"); + exit(0); + } + if(mkdir("dirfile/xx") == 0){ + printf("mkdir dirfile/xx succeeded!\n"); + exit(0); + } + if(unlink("dirfile/xx") == 0){ + printf("unlink dirfile/xx succeeded!\n"); + exit(0); + } + if(link("README", "dirfile/xx") == 0){ + printf("link to dirfile/xx succeeded!\n"); + exit(0); + } + if(unlink("dirfile") != 0){ + printf("unlink dirfile failed!\n"); + exit(0); + } + + fd = open(".", O_RDWR); + if(fd >= 0){ + printf("open . for writing succeeded!\n"); + exit(0); + } + fd = open(".", 0); + if(write(fd, "x", 1) > 0){ + printf("write . succeeded!\n"); + exit(0); + } + close(fd); + + printf("dir vs file OK\n"); +} + +// test that iput() is called at the end of _namei() +void +iref(void) +{ + int i, fd; + + printf("empty file name\n"); + + // the 50 is NINODE + for(i = 0; i < 50 + 1; i++){ + if(mkdir("irefd") != 0){ + printf("mkdir irefd failed\n"); + exit(0); + } + if(chdir("irefd") != 0){ + printf("chdir irefd failed\n"); + exit(0); + } + + mkdir(""); + link("README", ""); + fd = open("", O_CREATE); + if(fd >= 0) + close(fd); + fd = open("xx", O_CREATE); + if(fd >= 0) + close(fd); + unlink("xx"); + } + + chdir("/"); + printf("empty file name OK\n"); +} + +void * +_ptexit(void *a) +{ + return NULL; +} + +void +forktest(void) +{ + int n, pid; + // XXX use getrlimit + int ulim = 1025; + + printf("fork test\n"); + + for(n=0; n 0; n--){ + if(wait(NULL) < 0){ + printf("wait stopped early\n"); + exit(0); + } + } + + if(wait(NULL) != -1 || errno != ECHILD){ + printf("wait got too many\n"); + exit(0); + } + + pthread_t t[ulim]; + for(n=0; n 0; n--){ + void *st; + if(pthread_join(t[n - 1], &st)) + errx(-1, "too few joins"); + if (st != NULL) + errx(-1, "bad status"); + } + + printf("fork test OK\n"); +} + +//void +//sbrktest(void) +//{ +// int fds[2], pid, pids[10], ppid; +// char *a, *b, *c, *lastaddr, *oldbrk, *p, scratch; +// uint amt; +// +// printf("sbrk test\n"); +// oldbrk = sbrk(0); +// +// // can one sbrk() less than a page? +// a = sbrk(0); +// int i; +// for(i = 0; i < 5000; i++){ +// b = sbrk(1); +// if(b != a){ +// printf("sbrk test failed %d %x %x\n", i, a, b); +// exit(0); +// } +// *b = 1; +// a = b + 1; +// } +// pid = fork(); +// if(pid < 0){ +// printf("sbrk test fork failed\n"); +// exit(0); +// } +// c = sbrk(1); +// c = sbrk(1); +// if(c != a + 1){ +// printf("sbrk test failed post-fork\n"); +// exit(0); +// } +// if(pid == 0) +// exit(0); +// wait(NULL); +// +// // can one grow address space to something big? +//#define BIG (100*1024*1024) +// a = sbrk(0); +// amt = (BIG) - (uint)a; +// p = sbrk(amt); +// if (p != a) { +// printf("sbrk test failed to grow big address space; enough phys mem?\n"); +// exit(0); +// } +// lastaddr = (char*) (BIG-1); +// *lastaddr = 99; +// +// // can one de-allocate? +// a = sbrk(0); +// c = sbrk(-4096); +// if(c == (char*)0xffffffff){ +// printf("sbrk could not deallocate\n"); +// exit(0); +// } +// c = sbrk(0); +// if(c != a - 4096){ +// printf("sbrk deallocation produced wrong address, a %x c %x\n", a, c); +// exit(0); +// } +// +// // can one re-allocate that page? +// a = sbrk(0); +// c = sbrk(4096); +// if(c != a || sbrk(0) != a + 4096){ +// printf("sbrk re-allocation failed, a %x c %x\n", a, c); +// exit(0); +// } +// if(*lastaddr == 99){ +// // should be zero +// printf("sbrk de-allocation didn't really deallocate\n"); +// exit(0); +// } +// +// a = sbrk(0); +// c = sbrk(-(sbrk(0) - oldbrk)); +// if(c != a){ +// printf("sbrk downsize failed, a %x c %x\n", a, c); +// exit(0); +// } +// +// // can we read the kernel's memory? +// for(a = (char*)(KERNBASE); a < (char*) (KERNBASE+2000000); a += 50000){ +// ppid = getpid(); +// pid = fork(); +// if(pid < 0){ +// printf("fork failed\n"); +// exit(0); +// } +// if(pid == 0){ +// printf("oops could read %x = %x\n", a, *a); +// kill(ppid); +// exit(0); +// } +// wait(NULL); +// } +// +// // if we run the system out of memory, does it clean up the last +// // failed allocation? +// if(pipe(fds) != 0){ +// printf("pipe() failed\n"); +// exit(0); +// } +// for(i = 0; i < sizeof(pids)/sizeof(pids[0]); i++){ +// if((pids[i] = fork()) == 0){ +// // allocate a lot of memory +// sbrk(BIG - (uint)sbrk(0)); +// write(fds[1], "x", 1); +// // sit around until killed +// for(;;) sleep(1000); +// } +// if(pids[i] != -1) +// read(fds[0], &scratch, 1); +// } +// // if those failed allocations freed up the pages they did allocate, +// // we'll be able to allocate here +// c = sbrk(4096); +// for(i = 0; i < sizeof(pids)/sizeof(pids[0]); i++){ +// if(pids[i] == -1) +// continue; +// kill(pids[i]); +// wait(NULL); +// } +// if(c == (char*)0xffffffff){ +// printf("failed sbrk leaked memory\n"); +// exit(0); +// } +// +// if(sbrk(0) > oldbrk) +// sbrk(-(sbrk(0) - oldbrk)); +// +// printf("sbrk test OK\n"); +//} + +void +validateint(int *p) +{ + ulong ret; + asm volatile( + "movq %%rsp, %%r10\n" + "leaq 2(%%rip), %%r11\n" + "sysenter\n" + : "=a"(ret) +#define SYS_PIPE2 293 + : "0"(SYS_PIPE2), "D"(p) + : "cc", "memory"); + if (ret == 0) + errx(-1, "bad int passed?"); +} + +void +validatetest(void) +{ + int hi, pid; + ulong p; + + printf("validate test\n"); + hi = 1100*1024; + + for(p = 0; p <= (uint)hi; p += 4096){ + if((pid = fork()) == 0){ + // try to crash the kernel by passing in a badly placed integer + validateint((int*)p); + exit(0); + } + //sleep(0); + //sleep(0); + //kill(pid); + int status; + wait(&status); + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) + errx(-1, "validate failed"); + + // try to crash the kernel by passing in a bad string pointer + if(link("nosuchfile", (char*)p) == 0){ + printf("link should not succeed\n"); + exit(0); + } + } + + printf("validate ok\n"); +} + +// does unintialized data start out zero? +char uninit[10000]; +void +bsstest(void) +{ + int i; + + printf("bss test\n"); + for(i = 0; i < sizeof(uninit); i++){ + if(uninit[i] != '\0'){ + printf("bss test failed\n"); + exit(0); + } + } + printf("bss test ok\n"); +} + +// does exec return an error if the arguments +// are larger than a page? or does it write +// below the stack and wreck the instructions/data? +void +bigargtest(void) +{ + int pid, fd; + + unlink("bigarg-ok"); + pid = fork(); + if(pid == 0){ +#define MAXARG 32 + static char *args[MAXARG]; + int i; + for(i = 0; i < MAXARG-1; i++) + args[i] = "bigargs test: failed\n "; + args[MAXARG-1] = 0; + printf("bigarg test\n"); + exec("echo", args); + printf("bigarg test ok\n"); + fd = open("bigarg-ok", O_CREATE); + close(fd); + exit(0); + } else if(pid < 0){ + printf("bigargtest: fork failed\n"); + exit(0); + } + wait(NULL); + fd = open("bigarg-ok", 0); + if(fd < 0){ + printf("bigarg test failed!\n"); + exit(0); + } + close(fd); + unlink("bigarg-ok"); +} + +// what happens when the file system runs out of blocks? +// answer: balloc panics, so this test is not useful. +void +fsfull() +{ + int nfiles; + int fsblocks = 0; + + printf("fsfull test\n"); + + for(nfiles = 0; ; nfiles++){ + char name[64]; + name[0] = 'f'; + name[1] = '0' + nfiles / 1000; + name[2] = '0' + (nfiles % 1000) / 100; + name[3] = '0' + (nfiles % 100) / 10; + name[4] = '0' + (nfiles % 10); + name[5] = '\0'; + printf("writing %s\n", name); + int fd = open(name, O_CREATE|O_RDWR); + if(fd < 0){ + printf("open %s failed\n", name); + break; + } + int total = 0; + while(1){ + int cc = write(fd, buf, BSIZE); + if(cc < BSIZE) + break; + total += cc; + fsblocks++; + } + printf("wrote %d bytes\n", total); + close(fd); + if(total == 0) + break; + } + + while(nfiles >= 0){ + char name[64]; + name[0] = 'f'; + name[1] = '0' + nfiles / 1000; + name[2] = '0' + (nfiles % 1000) / 100; + name[3] = '0' + (nfiles % 100) / 10; + name[4] = '0' + (nfiles % 10); + name[5] = '\0'; + unlink(name); + nfiles--; + } + + printf("fsfull test finished\n"); +} + +unsigned long randstate = 1; +unsigned int +_rand() +{ + randstate = randstate * 1664525 + 1013904223; + return randstate; +} + +void +rshuffle(char *f1, char *f2, char *n1, char *n2) +{ + int i; + for (i = 0; i < 100; i++) { + while (rename(f1, f2) < 0) + usleep(1); + while (rename(n1, n2) < 0) + usleep(1); + } + exit(0); +} + +void +collectn(int n) +{ + int i; + for (i = 0; i < n; i++) { + int status; + wait(&status); + if (WEXITSTATUS(status) != 0) + errx(status, "child failed: %d", status); + } +} + +void +_rename() +{ + int ret; + if ((ret = open("a", O_RDONLY | O_CREAT)) < 0) + err(ret, "open"); + close(ret); + if ((ret = open("f", O_RDONLY | O_CREAT)) < 0) + err(ret, "open"); + close(ret); + + if ((ret = mkdir("renamed"))) + err(ret, "mkdir"); + + if ((ret = rename("a", "renamed/a"))) + err(ret, "rename"); + if ((ret = rename("f", "renamed/f"))) + err(ret, "rename"); + + if ((ret = chdir("renamed"))) + err(ret, "chdir"); + + if ((ret = rename("f", "f"))) + err(ret, "rename"); + + if (fork() == 0) + rshuffle("a", "b", "e", "f"); + if (fork() == 0) + rshuffle("b", "c", "f", "g"); + if (fork() == 0) + rshuffle("c", "a", "g", "e"); + collectn(3); + + unlink("a"); + unlink("b"); + unlink("c"); + unlink("e"); + unlink("f"); + unlink("g"); + + if ((ret = mkdir("d1"))) + err(ret, "mkdir"); + if ((ret = mkdir("d2"))) + err(ret, "mkdir"); + if ((ret = mkdir("d3"))) + err(ret, "mkdir"); + + if ((ret = open("d1/a", O_RDONLY | O_CREAT)) < 0) + err(ret, "open"); + close(ret); + if ((ret = open("d2/e", O_RDONLY | O_CREAT)) < 0) + err(ret, "open"); + close(ret); + + if (fork() == 0) + rshuffle("d1/a", "d2/b", "d1/f", "d3/g"); + if (fork() == 0) + rshuffle("d2/b", "d3/c", "d2/e", "d1/f"); + if (fork() == 0) + rshuffle("d3/c", "d1/a", "d3/g", "d2/e"); + collectn(3); + + exit(0); +} + +void +renametest() +{ + printf("rename test\n"); + + int pid = fork(); + if (!pid) + _rename(); + int status; + wait(&status); + + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) + errx(status, "rename test failed"); + + printf("rename test finished\n"); +} + +void * +_bigstack(void *no) +{ + uintptr_t stktop = (uintptr_t)&no; + stktop = (stktop & ~((1 << 12) - 1)) + (1 << 12); + char *test = (char *)stktop - 4096*30; + *test = 0; + asm volatile ("" ::"r"(test) : "memory"); + return NULL; +} + +void +posixtest() +{ + printf("posix test\n"); + + char *of = "poutfile"; + char *inf = "pinfile"; + + // create input for child lsh + int infd; + if ((infd = open(inf, O_CREAT | O_WRONLY)) < 0) + err(infd, "open"); + + char *inmsg = "echo posix spawn worked\n"; + size_t ilen = strlen(inmsg); + + int ret; + if ((ret = write(infd, inmsg, ilen)) != ilen) + err(ret, "write"); + + // reopen input file so child has correct offset + close(infd); + if ((infd = open(inf, O_RDONLY)) < 0) + err(infd, "open"); + + int outfd; + if ((outfd = open(of, O_CREAT | O_WRONLY)) < 0) + err(outfd, "open"); + + posix_spawn_file_actions_t fa; + if ((ret = posix_spawn_file_actions_init(&fa)) < 0) + err(ret, "posix fa init"); + + if ((ret = posix_spawn_file_actions_adddup2(&fa, outfd, 1)) < 0) + err(ret, "posix addup2"); + + if ((ret = posix_spawn_file_actions_adddup2(&fa, infd, 0)) < 0) + err(ret, "posix addup2"); + + char *args[] = {"/bin/lsh", NULL}; + pid_t c; + if ((ret = posix_spawn(&c, args[0], &fa, NULL, args, NULL))) + err(ret, "posix_spawn"); + + if ((ret = posix_spawn_file_actions_destroy(&fa)) < 0) + err(ret, "posix fa destroy"); + + close(outfd); + close(infd); + + int status; + pid_t got = wait(&status); + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) + errx(status, "child failed"); + if (got != c) + errx(-1, "wrong pid %ld %ld", c, got); + + if ((outfd = open(of, O_RDONLY)) < 0) + err(outfd, "open"); + + char *omsg = "# posix spawn worked\n# "; + size_t olen = strlen(omsg); + + char buf[128]; + if ((ret = read(outfd, buf, sizeof(buf))) < 0) + err(ret, "read"); + else if (ret != olen) + errx(-1, "unexpected read len: %d %lu", ret, olen); + + if (strncmp(omsg, buf, olen)) + errx(-1, "unexpected child output"); + close(outfd); + + pthread_attr_t pa; + pthread_attr_init(&pa); + pthread_attr_setstacksize(&pa, 4096*30); + pthread_t ct; + if (pthread_create(&ct, &pa, _bigstack, NULL)) + errx(-1, "pthread_create"); + void *threadret; + if (pthread_join(ct, &threadret)) + errx(-1, "pthread_join"); + if (threadret != NULL) + errx(-1, "thread failed"); + pthread_attr_destroy(&pa); + + unlink(inf); + unlink(of); + + printf("posix test ok\n"); +} + +void +lseektest() +{ + printf("lseek test\n"); + + char *f = "lseekfile"; + + int fd = open(f, O_CREAT | O_RDWR); + if (fd < 0) + err(fd, "open"); + + unlink(f); + + char *msg = "duhee hi"; + int ret = write(fd, msg, strlen(msg)); + if (ret < 0) + err(ret, "write"); + else if (ret != strlen(msg)) + errx(-1, "bad len"); + + if ((ret = lseek(fd, 10240, SEEK_SET)) < 0) + err(ret, "lseek"); + + if ((ret = write(fd, msg, strlen(msg))) < 0) + err(ret, "write"); + else if (ret != strlen(msg)) + errx(-1, "bad len"); + + if ((ret = lseek(fd, 0, SEEK_SET)) < 0) + err(ret, "lseek"); + + long tot = 0; + char buf[1024]; + while ((ret = read(fd, buf, sizeof(buf))) > 0) + tot += ret; + if (ret < 0) + err(ret, "read"); + + if (tot != 10240 + strlen(msg)) + errx(-1, "bad total lens"); + + printf("lseek test ok\n"); +} + +void +_ruchild() +{ + int i; + for (i = 0; i < 1e8; i++) + asm volatile("":::"memory"); + exit(0); +} + +void * +_ruthread(void *p) +{ + volatile int *done = (int *)p; + while (*done == 0) + ; + return NULL; +} + +ulong +tvtot(struct timeval *t) +{ + return t->tv_sec * 1e6 + t->tv_usec; +} + +void +rusagetest() +{ + printf("rusage test\n"); + + struct rusage r; + + const int n = 3; + pthread_t t[n]; + int i; + volatile int done = 0; + for (i = 0; i < n; i++) + if (pthread_create(&t[i], NULL, _ruthread, (void *)&done)) + errx(-1, "pthread_create"); + sleep(1); + done = 1; + for (i = 0; i < n; i++) + pthread_join(t[i], NULL); + + memset(&r, 0, sizeof(struct rusage)); + int ret; + if ((ret = getrusage(RUSAGE_SELF, &r)) < 0) + err(ret, "getrusage"); + + ulong utime = tvtot(&r.ru_utime); + if (utime < 1000000) + errx(-1, "weird utime: %lu", utime); + + //printf("my user us: %lu\n", tvtot(&r.ru_utime)); + //printf("my sys us: %lu\n", tvtot(&r.ru_stime)); + + int pid = fork(); + if (!pid) + _ruchild(); + int status; + memset(&r, 0, sizeof(struct rusage)); + ret = wait4(WAIT_ANY, &status, 0, &r); + if (ret < 0) + err(ret, "wait4 rusage"); + if (ret != pid) + errx(-1, "wrong pid %d %d", ret, pid); + + if (tvtot(&r.ru_stime) > 500) + errx(-1, "more system time than expected"); + if (tvtot(&r.ru_stime) > tvtot(&r.ru_utime)) + errx(-1, "more system time than user time"); + printf("rusage test ok\n"); + + //printf("user us: %lu\n", tvtot(&r.ru_utime)); + //printf("sys us: %lu\n", tvtot(&r.ru_stime)); +} + +void *bart(void *arg) +{ + pthread_barrier_t *b = (pthread_barrier_t *)arg; + + int i; + for (i = 0; i < 100; i++) + pthread_barrier_wait(b); + return NULL; +} + +void +barriertest() +{ + printf("barrier test\n"); + + const int nthreads = 3; + pthread_barrier_t b; + pthread_barrier_init(&b, NULL, nthreads); + + int i; + pthread_t t[nthreads]; + for (i = 0; i < nthreads; i++) + if (pthread_create(&t[i], NULL, bart, &b)) + errx(-1, "pthread create"); + for (i = 0; i < nthreads; i++) + if (pthread_join(t[i], NULL)) + errx(-1, "pthread join"); + + printf("barrier test ok\n"); +} + +void *_waitany(void *a) +{ + waitpid(WAIT_ANY, NULL, 0); + printf("waitany woke\n"); + return NULL; +} + +void *_waitchild(void *a) +{ + long pid = (pid_t)a; + waitpid(pid, NULL, 0); + printf("waitchild woke\n"); + return NULL; +} + +void +threadwait() +{ + printf("threadwait test\n"); + long pid; + if ((pid = fork()) == 0) { + sleep(1); + exit(0); + } else if (pid < 0) + err(pid, "fork"); + int ret; + pthread_t t[2]; + if ((ret = pthread_create(&t[0], NULL, _waitany, NULL))) + errx(ret, "pthread_create"); + if ((ret = pthread_create(&t[1], NULL, _waitchild, (void *)pid))) + errx(ret, "pthread_create"); + if ((ret = pthread_join(t[0], NULL))) + errx(ret, "pthread_join"); + if ((ret = pthread_join(t[1], NULL))) + errx(ret, "pthread_join"); + + if ((pid = fork()) == 0) { + sleep(1); + exit(0); + } else if (pid < 0) + errx(pid, "fork"); + + if ((ret = pthread_create(&t[0], NULL, _waitany, NULL))) + errx(ret, "pthread_create"); + if ((ret = pthread_create(&t[1], NULL, _waitany, NULL))) + errx(ret, "pthread_create"); + if ((ret = pthread_join(t[0], NULL))) + errx(ret, "pthread_join"); + if ((ret = pthread_join(t[1], NULL))) + errx(ret, "pthread_join"); + + if ((pid = fork()) == 0) { + sleep(1); + exit(0); + } else if (pid < 0) + errx(pid, "fork"); + + if ((ret = pthread_create(&t[0], NULL, _waitchild, (void *)pid))) + errx(ret, "pthread_create"); + if ((ret = pthread_create(&t[1], NULL, _waitchild, (void *)pid))) + errx(ret, "pthread_create"); + if ((ret = pthread_join(t[0], NULL))) + errx(ret, "pthread_join"); + if ((ret = pthread_join(t[1], NULL))) + errx(ret, "pthread_join"); + + printf("threadwait ok\n"); +} + +// ... = is processed in groups of three: int fd, short events, short expected +void _pchk(const int timeout, const int expto, const int numfds, ...) +{ + struct pollfd pfds[numfds]; + short expects[numfds]; + + va_list ap; + va_start(ap, numfds); + int i; + for (i = 0; i < numfds; i++) { + int fd = va_arg(ap, int); + short events = va_arg(ap, int); + short expect = va_arg(ap, int); + pfds[i].fd = fd; + pfds[i].events = events; + expects[i] = expect; + } + va_end(ap); + + int ret = poll(pfds, numfds, timeout); + // verify return value + if (expto && ret != 0) + errx(-1, "expected timeout"); + int readyfds = 0; + for (i = 0; i < numfds; i++) { + if (expects[i] != 0) + readyfds++; + if (expects[i] != pfds[i].revents) + errx(-1, "status mismatch: got %x, expected %x for " + "fd %d", pfds[i].revents, expects[i], pfds[i].fd); + } + if (readyfds != ret) + errx(-1, "ready fd mismatch: %d != %d", readyfds, ret); +} + +void polltest() +{ + printf("poll test\n"); + + int f1 = open("/tmp/hello1", O_CREAT | O_RDWR); + if (f1 < 0) + err(-1, "create"); + int f2 = open("/tmp/hello2", O_CREAT | O_RDWR); + if (f2 < 0) + err(-1, "create"); + + const int rwfl = POLLIN | POLLOUT; + const int toyes = 1, tono = 0; + // make sure they are {read,write}able + _pchk(-1, tono, 1, f1, rwfl, rwfl); + _pchk(-1, tono, 2, f1, rwfl, rwfl, + f2, rwfl, rwfl); + _pchk(INT_MAX, tono, 2, f1, POLLIN, POLLIN, + f2, POLLOUT, POLLOUT); + _pchk(0, tono, 2, f1, rwfl, rwfl, + f2, rwfl, rwfl); + // and that no error/hup is reported for non-blocking and timeouts + _pchk(0, toyes, 2, f1, POLLERR | POLLHUP, 0, + f2, POLLERR | POLLHUP, 0); + _pchk(500, toyes, 2, f1, POLLERR | POLLHUP, 0, + f2, POLLERR | POLLHUP, 0); + + // pipe initially has no data to read + int pp[2]; + if (pipe(pp) < 0) + err(-1, "pipe"); + _pchk(-1, tono, 4, + f1, rwfl, rwfl, + f2, rwfl, rwfl, + pp[0], rwfl, 0, + pp[1], rwfl, POLLOUT); + + // notice reads + char buf[4096]; + long ret; + ret = write(pp[1], buf, sizeof(buf)); + if (ret < 0) + err(-1, "write"); + else if (ret != sizeof(buf)) + errx(-1, "short write"); + + _pchk(-1, tono, 2, + pp[0], rwfl, POLLIN, + pp[1], rwfl, 0); + + // make sure pipe empties + ret = read(pp[0], buf, sizeof(buf)/2); + if (ret < 0) + err(-1, "read"); + else if (ret != (sizeof(buf)/2)) + errx(-1, "short read"); + + _pchk(10, tono, 2, + pp[0], rwfl, POLLIN, + pp[1], rwfl, POLLOUT); + + ret = read(pp[0], buf, sizeof(buf)/2); + if (ret < 0) + err(-1, "read"); + else if (ret != (sizeof(buf)/2)) + errx(-1, "short read"); + + _pchk(-1, tono, 2, + pp[0], rwfl, 0, + pp[1], rwfl, POLLOUT); + + int unbound = socket(AF_UNIX, SOCK_DGRAM, 0); + int sock = socket(AF_UNIX, SOCK_DGRAM, 0); + if (sock < 0) + err(-1, "socket"); + char *spath = "/tmp/pollsock"; + struct sockaddr_un sa; + sa.sun_family = AF_UNIX; + snprintf(sa.sun_path, sizeof(sa.sun_path), "%s", spath); + + unlink(spath); + if (bind(sock, (struct sockaddr *)&sa, SUN_LEN(&sa)) < 0) { + err(-1, "bind"); + } + + _pchk(-1, tono, 4, + pp[0], rwfl, 0, + pp[1], rwfl, POLLOUT, + unbound, rwfl, POLLERR, + sock, rwfl, POLLOUT); + + _pchk(10, toyes, 1, + sock, POLLIN, 0); + + pid_t pid = fork(); + if (pid < 0) + err(-1, "fork"); + if (pid == 0) { + close(f1); + close(f2); + close(pp[0]); + close(sock); + + struct sockaddr_un ua; + ua.sun_family = AF_UNIX; + snprintf(ua.sun_path, sizeof(ua.sun_path), "/tmp/pollsock"); + char *msg = "foobar"; + ret = sendto(unbound, msg, strlen(msg), 0, + (struct sockaddr *)&ua, SUN_LEN(&ua)); + if (ret < 0) + err(-1, "sendto"); + else if (ret != strlen(msg)) + errx(-1, "short write"); + printf("child sent\n"); + exit(0); + } + + _pchk(-1, tono, 1, + sock, POLLIN, POLLIN); + _pchk(0, tono, 1, + sock, rwfl, rwfl); + _pchk(10, toyes, 1, + sock, POLLERR, 0); + + ret = recv(sock, buf, sizeof(buf), 0); + if (ret < 0) + err(-1, "recv"); + else if (ret == 0) + errx(-1, "short read"); + + _pchk(10, tono, 1, + sock, rwfl, POLLOUT); + _pchk(10, toyes, 1, + sock, POLLIN, 0); + + close(f1); + close(f2); + close(pp[0]); + close(pp[1]); + close(sock); + close(unbound); + + int status; + if (wait(&status) != pid) + errx(-1, "wrong child"); + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) + errx(-1, "child failed"); + printf("polltest ok\n"); +} + +void +runsockettest(void) +{ + pid_t child = fork(); + if (child < 0) + err(-1, "fork"); + if (!child) { + char *args[] = {"sockettest", NULL}; + execvp(args[0], args); + err(-1, "exec failed"); + } + + int status; + if (wait(&status) != child) + errx(-1, "wrong child"); + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) + errx(-1, "child failed"); +} + +void _testnoblk(int rfd, int wfd) +{ + char buf[BSIZE]; + memset(buf, 'A', sizeof(buf)); + ssize_t ret, tot = 0; + while ((ret = write(wfd, buf, 10)) > 0) + tot += ret; + if (ret != -1 || errno != EWOULDBLOCK) + errx(-1, "weird write ret: %ld %d", ret, errno); + ssize_t tot2 = 0; + while ((ret = read(rfd, buf, 32)) > 0) + tot2 += ret; + if (ret != -1 || errno != EWOULDBLOCK) + errx(-1, "weird read ret: %ld %d", ret, errno); + if (tot != tot2) + errx(-1, "len mismatch %zd %zd", tot, tot2); +} + +void fnonblock(void) +{ + printf("non-blocking test\n"); + int p[2]; + if (pipe2(p, O_NONBLOCK)) + err(-1, "pipe"); + _testnoblk(p[0], p[1]); + close(p[0]); + close(p[1]); + + if (pipe2(p, 0)) + err(-1, "pipe"); + int flags; + if ((flags = fcntl(p[0], F_GETFL)) == -1) + err(-1, "fcntlg"); + if (fcntl(p[0], F_SETFL, flags | O_NONBLOCK) == -1) + err(-1, "fcntls"); + if ((flags = fcntl(p[1], F_GETFL)) == -1) + err(-1, "fcntlg"); + if (fcntl(p[1], F_SETFL, flags | O_NONBLOCK) == -1) + err(-1, "fcntls"); + _testnoblk(p[0], p[1]); + close(p[0]); + close(p[1]); + + // XXX test unix stream sockets once we have socketpair(2) + printf("non-blocking test ok\n"); +} + +void preadwrite(void) +{ + printf ("preadwrite test\n"); + + char *tfile = "/tmp/prdwrfile"; + int fd = open(tfile, O_WRONLY | O_CREAT | O_TRUNC); + if (fd < 0) + err(-1, "open trunc"); + + const size_t fsz = 1024; + char fbuf[fsz]; + int i; + for (i = 0; i < sizeof(fbuf); i++) + fbuf[i] = i * (i+1); + ssize_t r; + if ((r = write(fd, fbuf, sizeof(fbuf))) != sizeof(fbuf)) + err(-1, "write"); + close(fd); + + if ((fd = open(tfile, O_RDONLY)) < 0) + err(-1, "open read"); + char pbuf[fsz]; + char *f = &fbuf[0]; + char *fend = f + sizeof(fbuf); + srandom(time(NULL)); + + // interleave preads with reads to make sure offsets aren't screwed up + while (((r = read(fd, f, fend - f)) > 0)) { + f += r; + off_t off = random() % sizeof(fbuf); + if ((r = pread(fd, pbuf, sizeof(pbuf) - off, off)) < 0) + err(-1, "pread"); + } + if (r < 0) + err(-1, "read"); + if (f != fend) + errx(-1, "short length"); + + for (i = 0; i < sizeof(fbuf); i++) + if (fbuf[i] != (char)(i * (i+1))) + errx(-1, "byte mismatch"); + close(fd); + + // now read file forwards and backwards simultaneously to make sure + // data read is sane + f = &fbuf[0]; + char *pend = &pbuf[0] + sizeof(pbuf); + char *p = pend; + const size_t ch = 10; + + if ((fd = open(tfile, O_RDONLY)) < 0) + err(-1, "open"); + while (((r = read(fd, f, MIN(ch, fend - f))) > 0)) { + f += r; + size_t take = MIN(ch, p - &pbuf[0]); + p -= take; + off_t off = sizeof(pbuf) - (pend - p); + if ((r = pread(fd, p, take, off)) < 0) + err(-1, "pread"); + } + if (r < 0) + err(-1, "read"); + if (p != &pbuf[0] || f != fend) + errx(-1, "short length"); + if (strncmp(fbuf, pbuf, fsz) != 0) + errx(-1, "byte mismatch"); + close(fd); + + printf ("preadwrite test ok\n"); +} + +static char _expfile[BUFSIZ*2]; +static char gotfile[BUFSIZ*2]; + +void stdiotest(void) +{ + printf("stdio test\n"); + + FILE *f = fopen("/bigfile.txt", "r"); + if (f == NULL) + err(-1, "fopen"); + char buf[BSIZE]; + ulong cksum = 0; + size_t tot = 0, r; + while ((r = fread(buf, 1, sizeof(buf), f)) > 0) { + tot += r; + int i; + for (i = 0; i < r; i++) + cksum += buf[i]; + } + if (ferror(f)) + err(-1, "fread"); + if (!feof(f)) + errx(-1, "exptected eof"); + ulong ckexp = 0x1fbd000; + if (cksum != ckexp) + errx(-1, "cksum mismatch: %lx != %lx", cksum, ckexp); + fclose(f); + + srandom(time(NULL)); + int i; + for (i = 0; i < sizeof(_expfile); i++) + _expfile[i] = random(); + const char *tfile = "/tmp/stdiofile"; + if ((f = fopen(tfile, "w")) == NULL) + err(-1, "fopen"); + if ((r = fwrite(_expfile, sizeof(_expfile), 1, f)) != 1) + err(-1, "fwrite"); + fclose(f); + + if ((f = fopen(tfile, "r")) == NULL) + err(-1, "fopen"); + + const size_t ch = 10; + char *p = &gotfile[0]; + char * const pend = p + sizeof(gotfile); + while (p < pend) { + if (feof(f)) + errx(-1, "early eof: %ld", p - &gotfile[0]); + if (ferror(f)) + errx(-1, "ferror: %d", ferror(f)); + size_t l = MIN(ch, pend - p); + r = fread(p, 1, l, f); + if (r != l) + err(-1, "fread %zu != %zu", r, l); + p += r; + } + fclose(f); + + if (strncmp(gotfile, _expfile, sizeof(_expfile)) != 0) + errx(-1, "byte mismatch"); + + // test ungetc + if (!(f = fopen("/hi.txt", "r"))) + err(-1, "fopen"); + char buf1[64]; + char buf2[64]; + if ((r = fread(buf1, 1, sizeof(buf1), f)) < 1) + err(-1, "fread"); + for (i = 0; i < r; i++) + if (ungetc(buf1[i], f) != buf1[i]) + errx(-1, "ungetc"); + if ((r = fread(buf2, 1, sizeof(buf2), f)) < 1) + err(-1, "fread"); + for (i = 0; i < r; i++) + if (buf1[i] != buf2[r-1-i]) + errx(-1, "ungetc mismatch"); + + printf("stdio test ok\n"); +} + +void realloctest(void) +{ + printf("realloc test\n"); + + const size_t size = 13; + int i; + uchar *p = NULL; + for (i = 1; i < 10; i++) { + size_t nsize = i*size; + p = realloc(p, nsize); + if (p == NULL) + errx(-1, "malloc"); + size_t osize = (i-1)*size; + int j; + for (j = 0; j < osize; j++) + if (p[j] != j) + errx(-1, "byte mismatch"); + for (j = osize; j < nsize; j++) + p[j] = j; + } + free(p); + + printf("realloc test ok\n"); +} + +__attribute__((noreturn)) +void _cwdtest(void) +{ + const char *p = "/"; + if (chdir(p)) + err(-1, "chdir"); + char buf[128]; + if (!getcwd(buf, sizeof(buf))) + err(-1, "getcwd"); + if (strcmp(p, buf)) + errx(-1, "pwd mismatch"); + + p = "/another/../another/../another/../another/../boot/uefi/"; + if (chdir(p)) + err(-1, "chdir"); + if (!getcwd(buf, sizeof(buf))) + err(-1, "getcwd"); + if (strcmp(buf, "/boot/uefi")) + errx(-1, "pwd mismatch"); + + exit(0); +} + +void cwdtest(void) +{ + printf("cwd test\n"); + + pid_t c = fork(); + if (!c) + _cwdtest(); + int status; + if (wait(&status) != c) + errx(-1, "wrong child"); + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) + errx(-1, "child failed"); + + printf("cwd test ok\n"); +} + +void trunctest(void) +{ + printf("trunc test\n"); + + const char *fp = "/tmp/truncfile"; + int fd = open(fp, O_RDWR | O_CREAT); + if (fd < 0) + err(-1, "open"); + char buf[256]; + int i; + for (i = 0; i < sizeof(buf); i++) + buf[i] = 'A'; + if (write(fd, buf, sizeof(buf)) != sizeof(buf)) + err(-1, "short write"); + if (lseek(fd, 0, SEEK_SET) != 0) + err(-1, "lseek"); + off_t newsz = 15; + if (ftruncate(fd, newsz)) + err(-1, "ftruncate"); + ssize_t r; + if ((r = read(fd, buf, sizeof(buf))) != newsz) + err(-1, "expected %lu bytes, got %zd", newsz, r); + close(fd); + + newsz = 7; + if (truncate(fp, newsz)) + err(-1, "truncate"); + fd = open(fp, O_RDONLY | O_CREAT); + if (fd < 0) + err(-1, "open"); + if ((r = read(fd, buf, sizeof(buf))) != newsz) + err(-1, "expected %lu bytes, got %zd", newsz, r); + close(fd); + + // 8192, so file spans two pages in page cache + newsz = sizeof(buf)*32; + if (truncate(fp, newsz)) + err(-1, "truncate"); + fd = open(fp, O_RDONLY | O_CREAT); + if (fd < 0) + err(-1, "open"); + for (i = 0; i < newsz; i += sizeof(buf)) + if ((r = read(fd, buf, sizeof(buf))) != sizeof(buf)) + err(-1, "expected %lu bytes, got %zd", newsz, r); + close(fd); + + printf("trunc test ok\n"); +} + +void accesstest(void) +{ + // XXX write better test once we actually have permissions + printf("access test\n"); + + if (access("/", R_OK | X_OK | W_OK)) + err(-1, "access"); + if (access("/tmp", R_OK | X_OK | W_OK)) + err(-1, "access"); + if (access("//bin", R_OK | X_OK | W_OK)) + err(-1, "access"); + if (access("//bin/cat", R_OK | X_OK)) + err(-1, "access"); + + if (access("//does/not/exist/ever", R_OK | X_OK) == 0) + errx(-1, "access for bad file"); + + char *f = "/tmp/accfile"; + int fd; + if ((fd = open(f, O_CREAT | O_WRONLY)) < 0) + err(-1, "creat"); + close(fd); + + if (access(f, R_OK | X_OK | W_OK)) + err(-1, "access"); + + printf("access test OK\n"); +} + +//static const int btimes = 10000; +//static const int bctimes= 2500; +//static const int ltimes = 1000000; +static const int btimes = 100; +static const int bctimes= 100; +static const int ltimes = 100000; +static int lcounter; +static volatile int go; + +static void *_locker(void *v) +{ + while (go != 1) + asm volatile("pause\n":::"memory"); + pthread_mutex_t *m = (pthread_mutex_t *)v; + int i; + for (i = 0; i < ltimes; i++) { + if (pthread_mutex_lock(m)) + err(-1, "m lock"); + lcounter++; + if (pthread_mutex_unlock(m)) + err(-1, "m unlock"); + } + return NULL; +} + +struct conds_t { + pthread_mutex_t *m; + pthread_cond_t *yours; + pthread_cond_t *theirs; +}; + +void _mutextest(const int nt) +{ + pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER; + + pthread_t t[nt]; + int i; + for (i = 0; i < nt; i++) + if (pthread_create(&t[i], NULL, _locker, &m)) + errx(-1, "pthread_create"); + go = 1; + for (i = 0; i < nt; i++) + if (pthread_join(t[i], NULL)) + err(-1, "pthread_join"); + int exp = nt * ltimes; + if (lcounter != exp) + errx(-1, "uh oh %d != %d", exp, lcounter); + + if (pthread_mutex_destroy(&m)) + err(-1, "m destroy"); +} + +static void *_condsleep(void *v) +{ + struct conds_t *ca = (struct conds_t *)v; + + pthread_mutex_t *m = ca->m; + pthread_cond_t *mine = ca->yours; + pthread_cond_t *theirs = ca->theirs; + + if (pthread_mutex_lock(m)) + err(-1, "lock"); + go = 1; + + int i; + for (i = 0; i < btimes; i++) { + lcounter++; + if (pthread_cond_signal(theirs)) + err(-1, "cond sig"); + if (pthread_cond_wait(mine, m)) + err(-1, "cond wait"); + } + if (pthread_cond_signal(theirs)) + err(-1, "cond sig"); + + if (pthread_mutex_unlock(m)) + err(-1, "unlock"); + return NULL; +} + +static void *_condsleepbc(void *v) +{ + struct conds_t *ca = (struct conds_t *)v; + pthread_mutex_t *m = ca->m; + pthread_cond_t *mine = ca->yours; + + if (pthread_mutex_lock(m)) + err(-1, "lock"); + + while (go) { + lcounter++; + if (pthread_cond_wait(mine, m)) + err(-1, "cond wait"); + } + if (pthread_mutex_unlock(m)) + err(-1, "unlock"); + return NULL; +} + +static void _condtest(const int nt) +{ + if (nt < 0) + errx(-1, "bad nthreads"); + + lcounter = 0; + + pthread_mutex_t m; + if (pthread_mutex_init(&m, NULL)) + err(-1, "m init"); + pthread_cond_t cs[nt]; + int i; + for (i = 0; i < nt; i++) + if (pthread_cond_init(&cs[i], NULL)) + err(-1, "c init"); + pthread_t t[nt]; + struct conds_t args[nt]; + for (i = 0; i < nt; i++) { + args[i].m = &m; + args[i].yours = &cs[i]; + const int nid = i + 1 == nt ? 0 : i + 1; + args[i].theirs = &cs[nid]; + // make sure each thread goes to sleep before we create their + // neighbor to guarantee that the threads terminate in order. + go = 0; + if (pthread_create(&t[i], NULL, _condsleep, &args[i])) + errx(-1, "pthread_ create"); + while (go == 0) + asm volatile("pause\n":::"memory"); + } + + for (i = 0; i < nt; i++) + if (pthread_join(t[i], NULL)) + err(-1, "pthread_join"); + + int exp = btimes*nt; + if (lcounter != exp) + errx(-1, "uh oh! %d != %d", lcounter, exp); + + if (pthread_mutex_destroy(&m)) + err(-1, "m destroy"); + for (i = 0; i < nt; i++) + if (pthread_cond_destroy(&cs[i])) + err(-1, "cond destroy"); +} + +static void _condbctest(const int nt) +{ + if (nt < 0) + errx(-1, "bad nthreads"); + + lcounter = 0; + go = 1; + + pthread_mutex_t m; + if (pthread_mutex_init(&m, NULL)) + err(-1, "m init"); + pthread_cond_t c; + if (pthread_cond_init(&c, NULL)) + err(-1, "c init"); + + pthread_t t[nt]; + struct conds_t args[nt]; + int i; + for (i = 0; i < nt; i++) { + args[i].m = &m; + args[i].yours = &c; + if (pthread_create(&t[i], NULL, _condsleepbc, &args[i])) + errx(-1, "pthread_ create"); + } + + srandom(time(NULL)); + + int enext = nt; + for (i = 0; i < bctimes; i++) { + volatile int *p = &lcounter; + while (*p < enext) + asm volatile("pause\n":::"memory"); + if (pthread_mutex_lock(&m)) + err(-1, "lock"); + if (i == bctimes - 1) + go = 0; + if (rand() < RAND_MAX / 1000) { + struct timespec ts = {time(NULL) + 1, 0}; + if (pthread_cond_timedwait(&c, &m, &ts)) + err(-1, "timedwait"); + } + if (pthread_cond_broadcast(&c)) + err(-1, "broadcast"); + if (pthread_mutex_unlock(&m)) + err(-1, "unlock"); + enext += nt; + } + + for (i = 0; i < nt; i++) + if (pthread_join(t[i], NULL)) + err(-1, "pthread_join"); + + if (pthread_mutex_destroy(&m)) + err(-1, "m destroy"); + if (pthread_cond_destroy(&c)) + err(-1, "cond destroy"); +} + +int pthreadsharedfd; + +void *threadfd(void *arg) +{ + long n = (long) arg; + + for (int i = 0; i < 1000; i++) { + if (n == 0) { + pthreadsharedfd = open("sharedfdf", O_CREATE|O_RDWR); + if(pthreadsharedfd < 0) + err(pthreadsharedfd, "fstests: cannot open sharedfd for writing"); + int n = write(pthreadsharedfd, "aaaaaaaaaa", 10); + if(n != 10 && errno != EBADF) { + err(-1, "error: write should have returned 10 or error EBADF %d\n", n); + } + } else { + unlink("sharedfdf"); + close(pthreadsharedfd); + } + } + return NULL; +} + +void _pthreadfd(void) { + const int nthreads = 2; + + printf("pthread shared fd\n"); + + int i; + pthread_t t[nthreads]; + for (int i = 0; i < nthreads; i++) { + if (pthread_create(&t[i], NULL, threadfd, (void *) (long) i)) + errx(-1, "pthread create"); + } + for (i = 0; i < nthreads; i++) { + if (pthread_join(t[i], NULL)) + errx(-1, "pthread join"); + } + unlink("sharedfdf"); + printf("pthread shared fd ok\n"); + exit(0); +} + +void pthreadfd(void) { + pid_t c; + switch ((c = fork())) { + case -1: + err(-1, "fork"); + case 0: + _pthreadfd(); + } + int status; + if (wait(&status) != c) + err(-1, "wait"); + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) + errx(-1, "pthreadfd failed"); +} + +void futextest(void) +{ + printf("futex test\n"); + + _mutextest(8); + + printf("cond test\n"); + _condtest(8); + + printf("cond bc test\n"); + _condbctest(8); + + printf("futex test ok\n"); +} + +__attribute__((aligned(4096))) +char _pagebuf[4096]; + +void lazyfaults(void) +{ + printf("lazy fault test\n"); + int fd = open("/tmp/lfault.txt", O_CREAT | O_WRONLY); + if (fd == -1) + err(-1, "open"); + if (write(fd, _pagebuf, 20) == -1) + err(-1, "read"); + close(fd); + printf("lazy fault ok\n"); +} + +__attribute__((aligned(4096))) +volatile long _observe; + +void forktlb(void) +{ + printf("fork shootdown\n"); + _observe = 0; + pid_t c = fork(); + if (c == -1) { + err(-1, "fork"); + } else if (c != 0) { + _observe = -1; + } else { + if (_observe != 0) + errx(-1, "fork didn't shoot down %ld", _observe); + exit(0); + } + int status; + if (wait(&status) != c) + err(-1, "wait"); + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) + errx(-1, "child failed"); + printf("fork shootdown success\n"); +} + +void mapshared(void) +{ + printf("mmap shared test\n"); + size_t sz = 4096; + pid_t *p = mmap(NULL, sz, PROT_READ | PROT_WRITE, + MAP_ANON | MAP_SHARED, -1, 0); + if (p == MAP_FAILED) + err(-1, "mmap"); + int i; + for (i = 0; i < sz/sizeof(pid_t); i++) { + pid_t c = fork(); + if (c == -1) + err(-1, "fork"); + if (c == 0) { + p[i] = getpid(); + exit(0); + } else { + int status; + pid_t got = wait(&status); + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) + errx(-1, "child failed"); + if (got != c || p[i] != got) + errx(-1, "wrong pid"); + } + } + memset(p, 0, sz); + int pp[2]; + if (pipe(pp) == -1) + err(-1, "pipe"); + pid_t c = fork(); + if (c == -1) + err(-1, "fork"); + if (c == 0) { + if (close(pp[0]) == -1) + err(-1, "close"); + for (i = 0; i < sz/sizeof(pid_t); i++) { + p[i] = (pid_t)i; + c = fork(); + if (c == -1) + err(-1, "fork"); + if (c != 0) + exit(0); + } + if (write(pp[1], "A", 1) != 1) + errx(-1, "write"); + exit(0); + } + if (close(pp[1]) == -1) + err(-1, "close"); + if (wait(NULL) != c) + errx(-1, "wait"); + char buf; + if (read(pp[0], &buf, 1) != 1) + errx(-1, "read"); + for (i = 0; i < sz/sizeof(pid_t); i++) + if (p[i] != i) + errx(-1, "page mismatch"); + printf("mmap shared ok\n"); +} + +void spair(void) +{ + printf("socketpair test\n"); + + int s[2]; + if (socketpair(AF_UNIX, SOCK_STREAM, 0, s) == -1) + err(-1, "socketpair"); + + const char *fn = "/bin/usertests"; + char buf[BSIZE]; + ssize_t r; + pid_t c = fork(); + if (c == -1) + err(-1, "fork"); + else if (c == 0) { + close(s[0]); + int sfd = open(fn, O_RDONLY); + if (sfd == -1) + err(-1, "open"); + while ((r = read(sfd, buf, sizeof(buf))) > 0) { + if (write(s[1], buf, r) != r) + err(-1, "write"); + } + if (r == -1) + err(-1, "read"); + exit(0); + } + close(s[1]); + int rfd = open(fn, O_RDONLY); + if (rfd == -1) + err(-1, "open"); + char buf2[sizeof(buf)]; + while ((r = read(s[0], buf, sizeof(buf))) > 0) { + if (read(rfd, buf2, sizeof(buf2)) != r) + err(-1, "read"); + if (memcmp(buf, buf2, r) != 0) + errx(-1, "data mismatch"); + } + if (r == -1) + err(-1, "read"); + if (close(rfd) == -1) + err(-1, "close"); + int status; + if (wait(&status) != c) + err(-1, "wait"); + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) + errx(-1, "child failed"); + + printf("socketpair ok\n"); +} + +void iovtest(void) +{ + printf("iov test\n"); + int ffd = open("/bin/usertests", O_RDONLY); + int bfd = open("/bin/usertests", O_RDONLY); + int ofd = open("/tmp/dump", O_CREAT | O_TRUNC | O_RDWR); + if (ffd == -1 || bfd == -1 || ofd == -1) + err(-1, "open"); + const size_t bsz = BSIZE; + char fbuf[bsz]; + char bbuf[bsz]; + ssize_t r; + while ((r = read(ffd, fbuf, bsz)) == bsz) { + struct iovec iovs[4]; + size_t fourth = BSIZE / 4; + int i; + char *end = &bbuf[bsz]; + for (i = 0; i < 4; i++) { + iovs[i].iov_base = end - (i + 1)*fourth; + iovs[i].iov_len = fourth; + } + if (readv(bfd, iovs, 4) != bsz) + err(-1, "readv"); + char *a = &fbuf[0]; + for (i = 0; i < 4; i++) { + char *b = end - (i + 1)*fourth; + if (memcmp(a, b, fourth) != 0) + errx(-1, "data mismatch"); + a += fourth; + } + for (i = 0; i < 4; i++) { + iovs[i].iov_base = end - (i + 1)*fourth; + iovs[i].iov_len = fourth; + } + if (writev(ofd, iovs, 4) != bsz) + err(-1, "writev"); + } + if (lseek(ffd, 0, SEEK_SET) == -1) + err(-1, "lseek"); + if (lseek(ofd, 0, SEEK_SET) == -1) + err(-1, "lseek"); + while ((r = read(ffd, fbuf, bsz)) == bsz) { + if (read(ofd, bbuf, bsz) != bsz) + err(-1, "read"); + if (memcmp(fbuf, bbuf, bsz) != 0) + errx(-1, "data mismatch"); + } + if (r == -1) + err(-1, "read"); + if (close(ffd) == -1) + err(-1, "close"); + if (close(bfd) == -1) + err(-1, "close"); + if (close(ofd) == -1) + err(-1, "close"); + printf("iov ok\n"); +} + +void _scmchald(int s) +{ + char buf[CMSG_SPACE(sizeof(int))]; + struct msghdr msg; + struct iovec iov; + memset(&msg, 0, sizeof(msg)); + memset(&iov, 0, sizeof(iov)); + + char dur; + iov.iov_base = &dur; + iov.iov_len = 1; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = buf; + msg.msg_controllen = sizeof(buf); + if (recvmsg(s, &msg, 0) != 1) + err(-1, "recvmsg"); + if (dur != 4) + errx(-1, "wrong data"); + struct cmsghdr *cmsg; + for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { + if (cmsg->cmsg_len == CMSG_LEN(sizeof(int)) && + cmsg->cmsg_level == SOL_SOCKET && + cmsg->cmsg_type == SCM_RIGHTS) { + int *fdp = (int *)CMSG_DATA(cmsg); + int fd = *fdp; + char chmsg[] = "chald message"; + size_t chlen = sizeof(chmsg) - 1; + if (write(fd, chmsg, chlen) != chlen) + err(-1, "write"); + close(fd); + } + } + exit(0); +} + +void scmtest(void) +{ + printf("scm rights test\n"); + + int s[2]; + if (socketpair(AF_UNIX, SOCK_STREAM, 0, s) == -1) + err(-1, "socketpair"); + pid_t c = fork(); + if (c == -1) + err(-1, "fork"); + if (c == 0) { + if (close(s[0]) == -1) + err(-1, "close"); + _scmchald(s[1]); + } + if (close(s[1]) == -1) + err(-1, "close"); + + int fd; + if ((fd = open("/tmp/wtf", O_CREAT | O_TRUNC | O_RDWR)) == -1) + err(-1, "open"); + + char buf[CMSG_SPACE(sizeof(int))]; + struct msghdr msg; + struct iovec iov; + memset(&msg, 0, sizeof(msg)); + memset(&iov, 0, sizeof(iov)); + char dur = 4; + iov.iov_base = &dur; + iov.iov_len = 1; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = buf; + msg.msg_controllen = sizeof(buf); + struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_len = CMSG_LEN(sizeof(int)); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + int *fdp = (int *)CMSG_DATA(cmsg); + *fdp = fd; + ssize_t r; + if ((r = sendmsg(s[0], &msg, 0)) != 1) + err(-1, "sendmsg %zd", r); + char pmsg[] = "parent message"; + size_t plen = sizeof(pmsg) - 1; + if (write(fd, pmsg, plen) != plen) + err(-1, "write"); + int status; + if (wait(&status) != c) + err(-1, "wait"); + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) + errx(-1, "child failed"); + if (lseek(fd, 0, SEEK_SET) == -1) + err(-1, "lseek"); + char chmsg[] = "chald message"; + size_t chlen = sizeof(chmsg) - 1; + char rbuf[BSIZE]; + if ((r = read(fd, rbuf, sizeof(rbuf))) != plen + chlen) + errx(-1, "par read %zd %zu", r, plen + chlen); + char ok1[] = "chald messageparent message"; + char ok2[] = "parent messagechald message"; + if (strncmp(rbuf, ok1, sizeof(ok1) - 1) != 0 && + strncmp(rbuf, ok2, sizeof(ok2) - 1) != 0) + errx(-1, "bad data"); + if (close(fd) == -1) + err(-1, "close"); + if (close(s[0]) == -1) + err(-1, "close"); + + printf("scm rights ok\n"); +} + +void mkstemptest(void) +{ + printf("mkstemp test\n"); + char buf[64]; + + snprintf(buf, sizeof(buf), "/tmp/wtfXXXXX"); + if (mkstemp(buf) != -1 || errno != EINVAL) + errx(-1, "mkstemp suceeded"); + snprintf(buf, sizeof(buf), "/tmp/wtfXXXXXX"); + int fd; + if ((fd = mkstemp(buf)) == -1) + err(-1, "mkstemp"); + char msg[] = "hulloo!"; + if (write(fd, msg, sizeof(msg)) != sizeof(msg)) + err(-1, "write"); + int fd2; + if ((fd2 = open(buf, O_RDONLY)) == -1) + err(-1, "open"); + if (read(fd2, buf, sizeof(buf)) != sizeof(msg)) + err(-1, "read"); + if (strncmp(buf, msg, sizeof(msg)) != 0) + errx(-1, "msg mismatch"); + close(fd2); + close(fd); + printf("mkstemp test ok\n"); +} + +void getppidtest(void) +{ + printf("getppid test\n"); + int p[2]; + if (pipe(p) == -1) + err(-1, "pipe"); + pid_t c = fork(); + if (c == -1) + err(-1, "fork"); + if (c == 0) { + if (close(p[0]) == -1) + err(-1, "close"); + pid_t me = getpid(); + pid_t gc = fork(); + if (gc == -1) + err(-1, "fork"); + if (gc != 0) + exit(0); + int ok = 0; + int i; + for (i = 0; i < 1000; i++) { + if (getppid() != me) { + ok = 1; + break; + } + usleep(1000); + } + if (!ok) { + errx(-1, "still getting old ppid %ld", + (long)getppid()); + } + int ret = 0; + if (write(p[1], &ret, sizeof(ret)) != sizeof(ret)) + err(-1, "write"); + exit(0); + } + if (close(p[1]) == -1) + err(-1, "close"); + int ret; + if (wait(&ret) != c) + err(-1, "wrong child"); + if (!WIFEXITED(ret) || WEXITSTATUS(ret) != 0) + errx(-1, "child failed"); + if (read(p[0], &ret, sizeof(int)) != sizeof(int) || ret != 0) + err(-1, "grand child failed"); + if (close(p[0]) == -1) + err(-1, "close"); + printf("getppid test ok\n"); +} + +static void _v1(char *p) +{ + int i; + for (i = 0; i < 4096*2; i++) { + if (i < 4096 + (4096/2)) { + if (p[i] != 'A') + errx(-1, "mismatch"); + } else { + if (p[i] != 0) + errx(-1, "mismatch"); + } + } +} + +void mmaptest(void) +{ + printf("mmap test\n"); + const char * const f = "/tmp/mmap.dur"; + unlink(f); + int fd = open(f, O_WRONLY | O_CREAT | O_EXCL); + if (fd == -1) + err(-1, "open"); + int i; + for (i = 0; i < 4096 + (4096/2); i++) + if (write(fd, "A", 1) != 1) + err(-1, "write"); + if (close(fd) == -1) + err(-1, "close"); + if ((fd = open(f, O_RDONLY)) == -1) + err(-1, "open"); + char *p = mmap(0, 4096*2, PROT_READ, MAP_PRIVATE, fd, 0); + if (p == MAP_FAILED) + err(-1, "mmap"); + _v1(p); + if (munmap(p, 4096*2) == -1) + err(-1, "munmap"); + // should be able to map file opened read-only with private writable + // mapping + p = mmap(0, 4096*2, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); + if (p == MAP_FAILED) + err(-1, "mmap"); + if (close(fd) == -1) + err(-1, "close"); + _v1(p); + for (i = 0; i < 4096*2; i++) + p[i] = 'Z'; + if (munmap(p, 4096*2) == -1) + err(-1, "munmap"); + + if ((fd = open(f, O_RDONLY)) == -1) + err(-1, "open"); + p = mmap(0, 4096*3, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (p != MAP_FAILED) + err(-1, "mmap succeeded"); + if (close(fd) == -1) + err(-1, "close"); + + if ((fd = open(f, O_RDWR)) == -1) + err(-1, "open"); + p = mmap(0, 4096*3, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (p == MAP_FAILED) + err(-1, "mmap"); + if (close(fd) == -1) + err(-1, "close"); + + _v1(p); + for (i = 0; i < 4096*2; i++) + p[i] = 'Z'; + if (munmap(p, 4096*2) == -1) + err(-1, "munmap"); + + if ((fd = open(f, O_RDWR)) == -1) + err(-1, "open"); + char b; + for (i = 0; i < 4096 + (4096/2); i++) + if (read(fd, &b, 1) != 1 || b != 'Z') + err(-1, "read"); + if (close(fd) == -1) + err(-1, "close"); + + if ((fd = open(f, O_RDONLY)) == -1) + err(-1, "open"); + p = mmap(0, 4096, PROT_READ, MAP_SHARED, fd, 4096); + if (p == MAP_FAILED) + err(-1, "mmap"); + if (close(fd) == -1) + err(-1, "close"); + pid_t c = fork(); + if (c == -1) + err(-1, "fork"); + else if (c == 0) { + if ((fd = open(f, O_WRONLY)) == -1) + err(-1, "open"); + if (lseek(fd, 4096, SEEK_SET) == -1) + err(-1, "lseek"); + char b = 'B'; + for (i = 0; i < 4096; i++) + if (write(fd, &b, 1) != 1) + err(-1, "write"); + if (close(fd) == -1) + err(-1, "close"); + if (unlink(f) == -1) + err(-1, "unlink"); + if ((fd = open(f, O_WRONLY | O_CREAT | O_EXCL)) == -1) + err(-1, "open"); + b = 'X'; + for (i = 0; i < 4096*4; i++) + if (write(fd, &b, 1) != 1) + err(-1, "write"); + if (close(fd) == -1) + err(-1, "close"); + if (unlink(f) == -1) + err(-1, "unlink"); + exit(0); + } + int status; + if (wait(&status) != c) + err(-1, "wait"); + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) + errx(-1, "child failed"); + for (i = 0; i < 4096; i++) + if (p[i] != 'B') + errx(-1, "mismatch"); + if (munmap(p, 4096*2) != -1) + err(-1, "munmap succeeded"); + if (munmap(p, 4096) == -1) + err(-1, "munmap"); + printf("mmap test ok\n"); +} + + +void +logtest() +{ + #define N 1000 + #define NSYNC 20 + char *buf = malloc(BSIZE*N); + #define NPROC 5 + + printf("log test\n"); + + char *names[NPROC] = { "f0", "f1", "f2", "f3", "f4" }; + + for(int pi = 0; pi < NPROC; pi++){ + int pid = fork(); + if(pid < 0){ + err(-1, "fork failed\n"); + } + if(pid == 0){ + char *fname = names[pi]; + int fd = open(fname, O_CREATE | O_RDWR); + if(fd < 0){ + err(-1, "create failed\n"); + } + unlink(fname); + + for (int i = 0; i < N; i ++) { + if (write(fd, buf+i*BSIZE, BSIZE) != BSIZE) + err(-1, "write"); + if (i % NSYNC == 0) { + fsync(fd); + } + } + + exit(0); + } + } + + for(int pi = 0; pi < NPROC; pi++){ + wait(NULL); + } + + free(buf); + + printf("log test OK\n"); +} + +enum { + KREAD, + KWRITE, + KACCEPT, + KFUTEX, + KWAIT, + KPOLL, +}; + +static void guineapig(int fd, int op) +{ + char buf[4096*2]; + struct sockaddr_in saddr; + socklen_t slen = sizeof(saddr); + pthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER; + pid_t par; + + switch (op) { + case KREAD: + read(fd, buf, sizeof buf); + break; + case KWRITE: + write(fd, buf, sizeof buf); + break; + case KACCEPT: + accept(fd, (struct sockaddr *)&saddr, &slen); + break; + case KFUTEX: + pthread_mutex_lock(&mut); + pthread_mutex_lock(&mut); + break; + case KWAIT: + par = getpid(); + switch (fork()) { + case -1: + err(-1, "fork"); + case 0: + sleep(1); + if (kill(par) == -1) + err(-1, "kill"); + sleep(5); + exit(0); + } + wait(NULL); + break; + case KPOLL: + if (poll(NULL, 0, -1) == -1) + err(-1, "poll"); + break; + default: + errx(-1, "no op"); + } + + errx(-1, "child op returned %d", op); +} + +static void kill1(int fd, int op) +{ + pid_t kc = fork(); + switch (kc) { + case -1: + err(-1, "fork"); + case 0: + guineapig(fd, op); + default: + { + // wait for child to hopefully block in syscall + usleep(500000); + if (op != KWAIT) + kill(kc); + + int died = 0; + for (int i = 0; i < 3000/10; i++) { + int status; + pid_t r = waitpid(kc, &status, WNOHANG); + if (r == -1) + err(-1, "waitpid"); + if (r == kc) { + if (WIFEXITED(status)) + errx(-1, "exited?"); + died = 1; + break; + } + usleep(10000); + } + if (!died) + errx(-1, "killed child still lives!"); + } + } +} + +void killtest(void) +{ + printf("kill test\n"); + + int pip[2]; + if (pipe(pip) == -1) + err(-1, "pipe"); + + kill1(pip[0], KREAD); + kill1(pip[1], KWRITE); + close(pip[0]); + close(pip[1]); + + int sp[2]; + if (socketpair(AF_UNIX, SOCK_STREAM, 0, sp) == -1) + err(-1, "socketpair"); + + kill1(sp[0], KREAD); + kill1(sp[1], KWRITE); + close(sp[0]); + close(sp[1]); + + int s = socket(AF_INET, SOCK_STREAM, 0); + if (s == -1) + err(-1, "socket"); + struct sockaddr_in saddr = {}; + saddr.sin_family = AF_INET; + saddr.sin_addr.s_addr = INADDR_ANY; + saddr.sin_port = htons(8080); + if (bind(s, (struct sockaddr *)&saddr, sizeof saddr) == -1) + err(-1, "bind"); + if (listen(s, 10) == -1) + err(-1, "listen"); + + kill1(s, KACCEPT); + close(s); + + kill1(-1, KFUTEX); + kill1(-1, KWAIT); + + printf("kill test passed\n"); +} + +void lstats(void) +{ + printf("lstat test\n"); + char temp[] = "/tmp/wtfXXXXXX"; + int fd = mkstemp(temp); + if (fd == -1) + err(-1, "mkstemp"); + struct stat st1 = {0}, st2 = {0}; + if (fstat(fd, &st1) == -1) + err(-1, "fstat"); + if (lstat(temp, &st2) == -1) + err(-1, "fstat"); + if (memcmp(&st1, &st2, sizeof st1) != 0) + errx(-1, "not equal"); + close(fd); + printf("lstat test passed\n"); +} + +int +main(int argc, char *argv[]) +{ + printf("usertests starting\n"); + + if(open("usertests.ran", 0) >= 0){ + printf("already ran user tests -- rebuild fs.img\n"); + exit(0); + } + close(open("usertests.ran", O_CREATE)); + + logtest(); + createdelete(); + linkunlink(); + concreate(); + fourfiles(); + sharedfd(); + renametest(); + lseektest(); + dirtest(); + + bigargtest(); + bigwrite(); + bigargtest(); + bsstest(); + printf("skipping sbrk test\n"); + //sbrktest(); + validatetest(); + + //rusagetest(); + + opentest(); + writetest(); + // writetest1(); // make sure disk is bigger then MAXFILE blocks + createtest(); + + openiputtest(); + exitiputtest(); + iputtest(); + + mem(); + pipe1(); + preempt(); + exitwait(); + + rmdot(); + fourteen(); + bigfile(); + subdir(); + linktest(); + unlinkread(); + dirfile(); + iref(); + forktest(); + bigdir(); // slow + + posixtest(); + barriertest(); + threadwait(); + + pthreadfd(); + + fnonblock(); + preadwrite(); + stdiotest(); + realloctest(); + trunctest(); + + cwdtest(); + + polltest(); + runsockettest(); + accesstest(); + futextest(); + + lazyfaults(); + forktlb(); + mapshared(); + spair(); + iovtest(); + scmtest(); + mkstemptest(); + getppidtest(); + mmaptest(); + + killtest(); + lstats(); + + exectest(); + + return 0; +} diff --git a/biscuit/cxx/algorithm b/biscuit/cxx/algorithm new file mode 100644 index 0000000..7e1bd90 --- /dev/null +++ b/biscuit/cxx/algorithm @@ -0,0 +1,439 @@ +// -*- c++ -*- + +#pragma once + +#include +#include + +namespace std { + // [C++11 25.2] Non-modifying sequence operations + struct __less + { + template bool operator()(const T& x, const T& y) const + { + return x < y; + } + }; + + template + bool all_of(InputIterator first, InputIterator last, Predicate pred) + { + for (; first != last; ++first) + if (!pred(*first)) + return false; + return true; + } + + template + bool any_of(InputIterator first, InputIterator last, Predicate pred) + { + for (; first != last; ++first) + if (pred(*first)) + return true; + return false; + } + + template + bool none_of(InputIterator first, InputIterator last, Predicate pred) + { + for (; first != last; ++first) + if (pred(*first)) + return false; + return true; + } + + template + InputIterator find(InputIterator first, InputIterator last, + const T& value) + { + for (; first != last; ++first) + if (*first == value) + break; + return first; + } + + // [C++11 25.4.7] Minimum and maximum + template + const T& min(const T& a, const T& b) + { + if (b < a) + return b; + return a; + } + + template + const T& min(const T& a, const T& b, Compare comp) + { + if (Compare(b, a)) + return b; + return a; + } + + template + const T& max(const T& a, const T& b) + { + if (a < b) + return b; + return a; + } + + template + const T& max(const T& a, const T& b, Compare comp) + { + if (Compare(a, b)) + return b; + return a; + } + + // [C++11 25.3.2] Move + template + OutputIterator move(InputIterator first, InputIterator last, + OutputIterator result) + { + while (first != last) + *result++ = std::move(*first++); + return result; + } + + template + BidirectionalIterator2 move_backward(BidirectionalIterator1 first, + BidirectionalIterator1 last, + BidirectionalIterator2 result) + { + while (first != last) + *(--result) = std::move(*(--last)); + return result; + } + + // [C++11 25.3.6] Fill + template + void fill(ForwardIterator first, ForwardIterator last, const T& value) + { + for (; first != last; ++first) + *first = value; + } + + template + typename enable_if::value, void>::type + fill(T* first, T* last, const T& value) + { + memset(first, value, last - first); + } + + // [C++11 25.4.1] Sorting + template + void sort(RandomAccessIterator first, RandomAccessIterator last, + Compare comp) + { + auto dist = last - first; + if (dist <= 1) + return; + // Move pivot to the end + auto pivot = last; + --pivot; + std::swap(*(first + dist / 2), *pivot); + // Partition + auto store = first; + for (auto it = first; it != pivot; ++it) { + if (comp(*it, *pivot)) { + std::swap(*store, *it); + ++store; + } + } + // Move pivot to final location + std::swap(*store, *pivot); + // Sort partitions + sort(first, store, comp); + ++store; + sort(store, last, comp); + } + + template + void sort(RandomAccessIterator first, RandomAccessIterator last) + { + sort(first, last, __less()); + } + + template + ForwardIterator is_sorted_until(ForwardIterator first, ForwardIterator last, + Compare comp) + { + if (last - first < 2) + return last; + ForwardIterator next = first; + for (++next; next != last; ++next, ++first) + if (comp(*next, *first)) + break; + return next; + } + + template + bool is_sorted(ForwardIterator first, ForwardIterator last) + { + return is_sorted_until(first, last, __less()) == last; + } + + template + bool is_sorted(ForwardIterator first, ForwardIterator last, + Compare comp) + { + return is_sorted_until(first, last, comp) == last; + } + + template + ForwardIterator is_sorted_until(ForwardIterator first, ForwardIterator last) + { + return is_sorted_until(first, last, __less()); + } + + // [C++11 25.4.3] Binary search + + // Returns an iterator pointing to @c value, or, if @c value isn't + // present, an iterator pointing to @c value's successor. @c comp + // must take a dereferenced iterator and value and return true if + // the first is strictly less than the second. + // + // 0 2 4 <- Collection + // 1 2 <- Values that return 2 + template + ForwardIterator + lower_bound(ForwardIterator first, ForwardIterator last, + const T& value, Compare comp) + { + auto dist = last - first; + while (dist > 0) { + auto mid = first; + mid += dist / 2; + if (comp(*mid, value)) { + first = mid; + ++first; + dist = dist - dist / 2 - 1; + } else { + dist /= 2; + } + } + return first; + } + + template + ForwardIterator + lower_bound(ForwardIterator first, ForwardIterator last, + const T& value) + { + return lower_bound(first, last, value, + [](const T& a, const T& b) { return a < b; }); + } + + // Returns an iterator pointing to @c value's successor. @c comp + // must take value and a dereferenced iterator and return true if + // the first is strictly less than the second. + // + // 0 2 4 <- Collection + // 0 1 <- Values that return 2 + template + ForwardIterator + upper_bound(ForwardIterator first, ForwardIterator last, + const T& value, Compare comp) + { + auto dist = last - first; + while (dist > 0) { + auto mid = first; + mid += dist / 2; + if (comp(value, *mid)) { + dist /= 2; + } else { + first = mid; + ++first; + dist = dist - dist / 2 - 1; + } + } + return first; + } + + template + ForwardIterator + upper_bound(ForwardIterator first, ForwardIterator last, + const T& value) + { + return upper_bound(first, last, value, + [](const T& a, const T& b) { return a < b; }); + } + + // [C++11 25.4.6] Heap operations + + // Move *(first + pos) up the heap to the correct position + template + void + __move_up(RandomAccessIterator first, Distance pos, Distance len, Compare comp) + { + auto val = std::move(*(first + pos)); + Distance parent; + // Shift values down until we find the place to insert val + while (parent = (pos - 1) / 2, pos > 0 && comp(*(first + parent), val)) { + *(first + pos) = std::move(*(first + parent)); + pos = parent; + } + // Insert val + *(first + pos) = std::move(val); + } + + // Move *(first + pos) down the heap to the correct position + template + void + __move_down(RandomAccessIterator first, Distance pos, Distance len, + Compare comp) + { + auto val = std::move(*(first + pos)); + while (true) { + // Set childpos to the larger of the children of pos + Distance childpos = 2 * pos + 1; + Distance rightpos = childpos + 1; + if (rightpos < len && !comp(*(first + rightpos), *(first + childpos))) + childpos = rightpos; + // If we hit the bottom or val is > than the largest child, we've + // found our hole + if (childpos >= len || comp(*(first + childpos), val)) { + *(first + pos) = std::move(val); + return; + } + // Move the child up the tree + *(first + pos) = std::move(*(first + childpos)); + // Move the hole down the tree + pos = childpos; + } + } + + template + void + push_heap(RandomAccessIterator first, RandomAccessIterator last, Compare comp) + { + __move_up(first, last - first - 1, last - first, comp); + } + + template + void + push_heap(RandomAccessIterator first, RandomAccessIterator last) + { + push_heap(first, last, __less()); + } + + template + void + pop_heap(RandomAccessIterator first, RandomAccessIterator last, Compare comp) + { + if (last - first <= 1) + return; + --last; + std::swap(*first, *last); + __move_down(first, decltype(last - first)(0), last - first, comp); + } + + template + void + pop_heap(RandomAccessIterator first, RandomAccessIterator last) + { + pop_heap(first, last, __less()); + } + + template + void + make_heap(RandomAccessIterator first, RandomAccessIterator last, Compare comp) + { + auto len = last - first; + // To avoid problems if i is unsigned, i is one more than the real index + for (auto i = len / 2; i > 0; --i) + __move_down(first, i - 1, len, comp); + } + + template + void + make_heap(RandomAccessIterator first, RandomAccessIterator last) + { + make_heap(first, last, __less()); + } + + template + void + sort_heap(RandomAccessIterator first, RandomAccessIterator last, Compare comp) + { + for (; first != last; --last) + pop_heap(first, last, comp); + } + + template + void + sort_heap(RandomAccessIterator first, RandomAccessIterator last) + { + sort_heap(first, last, __less()); + } + + template + RandomAccessIterator + is_heap_until(RandomAccessIterator first, RandomAccessIterator last, + Compare comp) + { + if (first == last) + return last; + RandomAccessIterator pos = first + 1; + for (; pos != last; ++pos) { + RandomAccessIterator parent = first + (pos - first - 1) / 2; + if (comp(*parent, *pos)) + break; + } + return pos; + } + + template + bool + is_heap(RandomAccessIterator first, RandomAccessIterator last) + { + return is_heap_until(first, last, __less()) == last; + } + + template + bool + is_heap(RandomAccessIterator first, RandomAccessIterator last, Compare comp) + { + return is_heap_until(first, last, comp) == last; + } + + template + RandomAccessIterator + is_heap_until(RandomAccessIterator first, RandomAccessIterator last) + { + return is_heap_until(first, last, __less()); + } +} + +#if 0 +#include +#include + +int main(int argc, char **argv) +{ + std::vector vec; + for (int t = 0; t < 1000; ++t) { + vec.clear(); + for (int i = 0; i < 100; ++i) { + vec.push_back(rand() % 100); + push_heap(vec.begin(), vec.end()); + assert(is_heap(vec.begin(), vec.end())); + } + while (!vec.empty()) { + pop_heap(vec.begin(), vec.end()); + vec.pop_back(); + assert(is_heap(vec.begin(), vec.end())); + } + + for (int i = 0; i < 100; ++i) + vec.push_back(rand() % 100); + make_heap(vec.begin(), vec.end()); + assert(is_heap(vec.begin(), vec.end())); + + sort_heap(vec.begin(), vec.end()); + for (int i = 0; i < vec.size() - 1; ++i) + assert(vec[i] <= vec[i+1]); + } + return 0; +} +#endif diff --git a/biscuit/cxx/bind.hh b/biscuit/cxx/bind.hh new file mode 100644 index 0000000..5b8deca --- /dev/null +++ b/biscuit/cxx/bind.hh @@ -0,0 +1,49 @@ +// Simple version of std::bind that doesn't support placeholders + +#pragma once + +#include +#include + +// __bind_call generates a list of integers 0 .. N-1 ... +template +struct __bind_call { + static Res call(F& f, TArgs& t) { + return __bind_call::call(f, t); + } +}; + +// ... which the __bind_call base case unpacks to get a sequence of +// tuple indexes to pass to std::get to extract the saved arguments +// from the tuple t. +template +struct __bind_call<0, F, Res, TArgs, list...> { + static Res call(F& f, TArgs& t) { + return f(std::get(t)...); + } +}; + +template +struct __bind_helper +{ + typedef std::tuple::type...> TArgs; + + F f_; + TArgs args_; + + __bind_helper(F&& f, Args&&... args) : f_(f), args_(args...) { } + + Res operator()() + { + return __bind_call:: + call(f_, args_); + } +}; + +template +auto bind_simple(F&& f, Args&&... args) + -> __bind_helper +{ + return __bind_helper( + std::forward(f), std::forward(args)...); +} diff --git a/biscuit/cxx/compiler.h b/biscuit/cxx/compiler.h new file mode 100644 index 0000000..e4f8513 --- /dev/null +++ b/biscuit/cxx/compiler.h @@ -0,0 +1,18 @@ +#define __XCONCAT2(a, b) a ## b +#define __XCONCAT(a, b) __XCONCAT2(a, b) + +#define CACHELINE 64 + +#define __padout__ \ + char __XCONCAT(__padout, __COUNTER__)[0] __attribute__((aligned(CACHELINE))) +#define __mpalign__ __attribute__((aligned(CACHELINE))) +#define __noret__ __attribute__((noreturn)) +#define barrier() __asm volatile("" ::: "memory") + +//#ifdef __cplusplus +//#define BEGIN_DECLS extern "C" { +//#define END_DECLS } +//#else +//#define BEGIN_DECLS +//#define END_DECLS +//#endif diff --git a/biscuit/cxx/cstring b/biscuit/cxx/cstring new file mode 100644 index 0000000..bb6c766 --- /dev/null +++ b/biscuit/cxx/cstring @@ -0,0 +1,5 @@ +// -*- c++ -*- +// C++11 20.6.13 +#pragma once + +#include "string.h" diff --git a/biscuit/cxx/cxxrt.cc b/biscuit/cxx/cxxrt.cc new file mode 100644 index 0000000..12b8f1f --- /dev/null +++ b/biscuit/cxx/cxxrt.cc @@ -0,0 +1,180 @@ +#include + +#include + +#include "libutil.h" +#include "vector.hh" + +const std::nothrow_t std::nothrow; + +void* +operator new(std::size_t nbytes) +{ + void *p = malloc(nbytes); + if (!p) + throw std::bad_alloc(); + return p; +} + +void +operator delete(void* p) +{ + free(p); +} + +void* +operator new[](std::size_t nbytes) +{ + void *p = malloc(nbytes); + if (!p) + throw std::bad_alloc(); + return p; +} + +void +operator delete[](void* p) +{ + free(p); +} + +void* +operator new(std::size_t nbytes, void* buf) noexcept +{ + return buf; +} + +void +operator delete(void* ptr, void*) noexcept +{ +} + +void* +operator new[](std::size_t size, void* ptr) noexcept +{ + return ptr; +} + +void +operator delete[](void* ptr, void*) noexcept +{ +} + +extern "C" void +__cxa_pure_virtual(void) +{ + die("__cxa_pure_virtual"); +} + +extern "C" int +__cxa_guard_acquire(uint64_t *guard) +{ + volatile uint8_t *x = (uint8_t*) guard; + volatile uint32_t *l = (uint32_t*) (x+4); + + while (__sync_lock_test_and_set(l, 1) != 0) + ; /* spin */ + + if (*x) { + *l = 0; + return 0; + } + return 1; +} + +extern "C" void +__cxa_guard_release(uint64_t *guard) +{ + volatile uint8_t *x = (uint8_t*) guard; + volatile uint32_t *l = (uint32_t*) (x+4); + + *x = 1; + __sync_lock_release(l); +} + +extern "C" void +__cxa_guard_abort(uint64_t *guard) +{ + volatile uint8_t *x = (uint8_t*) guard; + volatile uint32_t *l = (uint32_t*) (x+4); + + __sync_lock_release(l); +} + +struct atexit_func +{ + void (*func)(void*); + void *arg; +}; +static static_vector atexit_funcs; + +extern "C" int +__cxa_atexit(void (*func)(void*), void *arg, void *dso_handle) +{ + atexit_funcs.push_back(atexit_func{func, arg}); + return 0; +} + +//extern "C" void +//abort(void) +//{ +// die("abort"); +//} + +static void +cxx_terminate(void) +{ + die("cxx terminate"); +} + +static void +cxx_unexpected(void) +{ + die("cxx unexpected"); +} + +void *__dso_handle; + +namespace __cxxabiv1 { + void (*__terminate_handler)() = cxx_terminate; + void (*__unexpected_handler)() = cxx_unexpected; +}; + +extern "C" int +dl_iterate_phdr(void) +{ + return -1; +} + +extern "C" void +__stack_chk_fail(void) +{ + die("stack_chk_fail"); +} + +extern "C" void* +__cxa_get_globals(void) +{ + static __thread uint8_t __cxa_eh_global[16]; + return __cxa_eh_global; +} + +extern "C" void* +__cxa_get_globals_fast(void) +{ + return __cxa_get_globals(); +} + +extern "C" void __register_frame(u8*); +extern "C" void +__cpprt_init(void) +{ + extern u8 __EH_FRAME_BEGIN__[]; + __register_frame(__EH_FRAME_BEGIN__); +} + +extern "C" void +__cpprt_fini(void) +{ + for (auto it = atexit_funcs.end(); it-- != atexit_funcs.begin(); ) + it->func(it->arg); +} diff --git a/biscuit/cxx/cxxrtbegin.S b/biscuit/cxx/cxxrtbegin.S new file mode 100644 index 0000000..0c90a93 --- /dev/null +++ b/biscuit/cxx/cxxrtbegin.S @@ -0,0 +1,3 @@ +.section .eh_frame +.globl __EH_FRAME_BEGIN__ +__EH_FRAME_BEGIN__: diff --git a/biscuit/cxx/cxxrtend.S b/biscuit/cxx/cxxrtend.S new file mode 100644 index 0000000..8298a85 --- /dev/null +++ b/biscuit/cxx/cxxrtend.S @@ -0,0 +1,4 @@ +.section .eh_frame +.globl __EH_FRAME_END__ +__EH_FRAME_END__: + .long 0 // see gcc/crtstuff.c __FRAME_END__ diff --git a/biscuit/cxx/distribution.hh b/biscuit/cxx/distribution.hh new file mode 100644 index 0000000..4318a12 --- /dev/null +++ b/biscuit/cxx/distribution.hh @@ -0,0 +1,163 @@ +#pragma once + +#include "compiler.h" + +#include + +#include +//#include +#include + +template +class distribution +{ + T min_, max_, sum_; + uint64_t count_; + +public: + distribution() : min_(), max_(), sum_(), count_(0) { } + + T add(T val) + { + if (val < min_ || count_ == 0) + min_ = val; + if (val > max_ || count_ == 0) + max_ = val; + sum_ += val; + count_ += 1; + return val; + } + + distribution &operator+=(const distribution &o) + { + if ((o.min_ < min_ || count_ == 0) && o.count_) + min_ = o.min_; + if ((o.max_ > max_ || count_ == 0) && o.count_) + max_ = o.max_; + sum_ += o.sum_; + count_ += o.count_; + return *this; + } + + T sum() const + { + return sum_; + } + + T min() const + { + return min_; + } + + T max() const + { + return max_; + } + + T span() const + { + return max() - min(); + } + + uint64_t count() const + { + return count_; + } + + T mean() const + { + return sum() / count(); + } + + double meand() const + { + return sum() / (double)count(); + } +}; + +template +class concurrent_distribution +{ + // XXX Use a locked hash table from thread::id to distribution + // and cache the per-thread distribution in a thread-local. + enum { MAX_THREADS = 100 }; + + struct + { + distribution dist __mpalign__; + __padout__; + } dists[MAX_THREADS]; + mutable std::atomic dirty; + mutable distribution combined; + + static int getid() + { + static std::atomic nextid; + static __thread int myid = -1; + if (myid == -1) { + myid = nextid++; + //assert(myid < MAX_THREADS); + if (myid >= MAX_THREADS || myid < 0) + errx(-1, "myid >= max threads %d\n", myid); + } + return myid; + } + + distribution &collect() const + { + if (dirty.load(std::memory_order_relaxed)) { + dirty = false; + + combined = distribution(); + for (auto &d : dists) + combined += d.dist; + } + return combined; + } + +public: + concurrent_distribution() : dirty(false) { } + + T add(T val) + { + if (!dirty.load(std::memory_order_relaxed)) + dirty.store(true, std::memory_order_relaxed); + dists[getid()].dist.add(val); + return val; + } + + T sum() const + { + return collect().sum(); + } + + T min() const + { + return collect().min(); + } + + T max() const + { + return collect().max(); + } + + T span() const + { + return collect().span(); + } + + uint64_t count() const + { + return collect().count(); + } + + T mean() const + { + return collect().mean(); + } + + double meand() const + { + return collect().meand(); + } +}; diff --git a/biscuit/cxx/functional b/biscuit/cxx/functional new file mode 100644 index 0000000..1b230c0 --- /dev/null +++ b/biscuit/cxx/functional @@ -0,0 +1,56 @@ +// -*- c++ -*- + +#pragma once + +namespace std { +#define __binop(RESULT, NAME, OP) \ + template \ + struct NAME { \ + RESULT operator()(const T& x, const T& y) const { return x OP y; } \ + typedef T first_argument_type; \ + typedef T second_argument_type; \ + typedef RESULT result_type; \ + } +#define __binop_T(NAME, OP) __binop(T, NAME, OP) +#define __binop_bool(NAME, OP) __binop(bool, NAME, OP) + + // [C++11 20.8.4] Arithmetic operations + __binop_T(plus, +); + __binop_T(minus, -); + __binop_T(multiplies, *); + __binop_T(divides, /); + __binop_T(modulus, %); + template + struct negate { + T operator()(const T& x) const { return -x; } + typedef T argument_type; + typedef T result_type; + }; + + // [C++11 20.8.5] Comparisons + __binop_bool(equal_to, ==); + __binop_bool(not_equal_to, !=); + __binop_bool(greater, >); + __binop_bool(less, <); + __binop_bool(greater_equal, >=); + __binop_bool(less_equal, <=); + + // [C++11 20.8.6] Logical operations + __binop_bool(logical_and, &&); + __binop_bool(logical_or, ||); + template + struct logical_not { + bool operator()(const T& x) const { return !x; } + typedef T argument_type; + typedef bool result_type; + }; + + // [C++11 20.8.7] Bitwise operations + __binop_T(bit_and, &); + __binop_T(bit_or, |); + __binop_T(bit_xor, ^); + +#undef __binop_bool +#undef __binop_T +#undef __binop +} diff --git a/biscuit/cxx/host_hdrs.hh b/biscuit/cxx/host_hdrs.hh new file mode 100644 index 0000000..413345c --- /dev/null +++ b/biscuit/cxx/host_hdrs.hh @@ -0,0 +1,16 @@ +// Header files included here will be extracted from the host. + +// Freestanding implementation headers [C++11 17.6.1.3/2]. These all +// contain compiler-specific code that we can't simply reproduce +// ourselves. +#include +#include +#include +#include +#include +#include +#include + +// Likewise for C [C11 4/6]. +#include +#include diff --git a/biscuit/cxx/iterator b/biscuit/cxx/iterator new file mode 100644 index 0000000..a6c0078 --- /dev/null +++ b/biscuit/cxx/iterator @@ -0,0 +1,33 @@ +// -*- c++ -*- + +namespace std { + template auto begin(C& c) -> decltype(c.begin()) + { + return c.begin(); + } + + template auto begin(const C& c) -> decltype(c.begin()) + { + return c.begin(); + } + + template auto end(C& c) -> decltype(c.end()) + { + return c.end(); + } + + template auto end(const C& c) -> decltype(c.end()) + { + return c.end(); + } + + template T* begin(T (&array)[N]) + { + return &array[0]; + } + + template T* end(T (&array)[N]) + { + return &array[N]; + } +} diff --git a/biscuit/cxx/libutil.cc b/biscuit/cxx/libutil.cc new file mode 100644 index 0000000..48c151d --- /dev/null +++ b/biscuit/cxx/libutil.cc @@ -0,0 +1,112 @@ +// Miscellaneous utilities + +#include "libutil.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void __attribute__((noreturn)) +vdie(const char* errstr, va_list ap) +{ + vfprintf(stderr, errstr, ap); + fprintf(stderr, "\n"); + exit(1); +} + +void __attribute__((noreturn)) +die(const char* errstr, ...) +{ + va_list ap; + + va_start(ap, errstr); + vdie(errstr, ap); +} + +void +edie(const char* errstr, ...) +{ + va_list ap; + + va_start(ap, errstr); +#ifdef XV6_USER + // There is no errno on xv6 + vdie(errstr, ap); +#else + vfprintf(stderr, errstr, ap); + va_end(ap); + fprintf(stderr, ": %s\n", strerror(errno)); + exit(1); +#endif +} + +size_t +xread(int fd, void *buf, size_t n) +{ + size_t pos = 0; + while (pos < n) { + int r = read(fd, (char*)buf + pos, n - pos); + if (r < 0) + edie("read failed"); + if (r == 0) + break; + pos += r; + } + return pos; +} + +void +xwrite(int fd, const void *buf, size_t n) +{ + int r; + + while (n) { + r = write(fd, buf, n); + if (r < 0 || r == 0) + edie("write failed"); + buf = (char *) buf + r; + n -= r; + } +} + +uint64_t +now_usec(void) +{ + struct timeval tv; + if (gettimeofday(&tv, nullptr) < 0) + edie("gettimeofday"); + return tv.tv_sec * 1000000ull + tv.tv_usec; +} + +#if !defined(XV6_USER) +// setaffinity is a syscall in xv6, but not standard in Linux +int +setaffinity(int c) +{ + cpu_set_t cpuset; + CPU_ZERO(&cpuset); + CPU_SET(c, &cpuset); + if (sched_setaffinity(0, sizeof(cpuset), &cpuset) < 0) + edie("setaffinity, sched_setaffinity failed"); + return 0; +} + +ulong +rdtsc(void) +{ + ulong low, hi; + asm volatile( + "rdtsc\n" + : "=a"(low), "=d"(hi) + : + :); + return hi << 32 | low; +} +#endif diff --git a/biscuit/cxx/libutil.h b/biscuit/cxx/libutil.h new file mode 100644 index 0000000..9cbb174 --- /dev/null +++ b/biscuit/cxx/libutil.h @@ -0,0 +1,35 @@ +#pragma once + +//#include "compiler.h" + +#include +#include + +void die(const char* errstr, ...) + __attribute__((noreturn, __format__(__printf__, 1, 2))); +void edie(const char* errstr, ...) + __attribute__((noreturn, __format__(__printf__, 1, 2))); + +size_t xread(int fd, void *buf, size_t n); +void xwrite(int fd, const void *buf, size_t n); + +uint64_t now_usec(void); + +//int setaffinity(int c); + +#define assert(x) _assert(x, __FILE__, __LINE__) +#define _assert(x, f, l) do { \ + if (!(x)) \ + errx(-1, "assert failed: " #x " (%s:%d)\n", f, l); \ + } while (0) + + +#define STAT_OMIT_NLINK 0 +#define fstatx(a, b, c) fstat(a, b) + +#if !defined(XV6_USER) +ulong rdtsc(void); +#define EP(x) "./" x +#else +#define EP(x) "/bin/" x +#endif diff --git a/biscuit/cxx/linker.ld b/biscuit/cxx/linker.ld new file mode 100644 index 0000000..7d7dc08 --- /dev/null +++ b/biscuit/cxx/linker.ld @@ -0,0 +1,18 @@ +ENTRY(_entry) + +SECTIONS +{ + . = 0x2c8000001000; + .got : { *(.got*) } + .eh_frame : { *(.eh_frame*) } + .text : { *(.text*) } + .data : { *(.data* .rodata*) } + .tdata : { *(.tdata*) } + .tbss : { *(.tbss*) } + .bss : { *(.bss*) *(COMMON) } + /*.got BLOCK(0x1000) : { *(.got*) }*/ + + /DISCARD/ : { + *(*) + } +} diff --git a/biscuit/cxx/linux/.keep b/biscuit/cxx/linux/.keep new file mode 100644 index 0000000..e69de29 diff --git a/biscuit/cxx/mail-deliver.cc b/biscuit/cxx/mail-deliver.cc new file mode 100644 index 0000000..f4f6a6d --- /dev/null +++ b/biscuit/cxx/mail-deliver.cc @@ -0,0 +1,126 @@ +#include "max_align.h" + +#include "libutil.h" +#include "shutil.h" +//#include "xsys.h" + +#include +#include +#include +#include +#include + +#include + +using std::string; + +class maildir_writer +{ + string maildir_; + unsigned long seqno_; + +public: + maildir_writer(const string &maildir) : maildir_(maildir), seqno_(0) + { + // Check for mailbox + struct stat st; + if (stat(maildir.c_str(), &st) < 0 || + (st.st_mode & S_IFMT) != S_IFDIR) + die("No such mailbox: %s", maildir.c_str()); + } + + void deliver(int msgfd, size_t limit = (size_t)-1) + { + // Generate unique tmp path + char unique[16]; + snprintf(unique, sizeof(unique), "%d.%lu", (int)getpid(), seqno_); + string tmppath(maildir_); + tmppath.append("/tmp/").append(unique); + ++seqno_; + + // Write message + int fd = open(tmppath.c_str(), O_CREAT|O_EXCL|O_WRONLY, 0600); + if (fd < 0) + edie("open %s failed", tmppath.c_str()); + if (copy_fd_n(fd, 0, limit) < 0) + edie("copy_fd failed"); + struct stat st; + if (fstatx(fd, &st, STAT_OMIT_NLINK) < 0) + edie("fstat %s failed", tmppath.c_str()); + fsync(fd); + close(fd); + + // Deliver message + snprintf(unique, sizeof(unique), "%lu", (unsigned long)st.st_ino); + string newpath(maildir_); + newpath.append("/new/").append(unique); + if (rename(tmppath.c_str(), newpath.c_str()) < 0) + edie("rename %s %s failed", tmppath.c_str(), newpath.c_str()); + } +}; + +static void +do_batch_mode(int fd, int resultfd, maildir_writer *writer) +{ + while (1) { + uint64_t msg_len; + size_t len = xread(fd, &msg_len, sizeof msg_len); + if (len == 0) + break; + if (len != sizeof msg_len) + die("short read of message length"); + writer->deliver(fd, msg_len); + + // Indicate success + uint64_t res = 0; + xwrite(resultfd, &res, sizeof res); + } +} + +static void +usage(const char *argv0) +{ + fprintf(stderr, "Usage: %s mailroot user and\n" + "a return code will be written to stdout after every delivery.\n" + ); + exit(2); +} + +int +main(int argc, char **argv) +{ + int opt; + bool batch_mode = false; + while ((opt = getopt(argc, argv, "b")) != -1) { + switch (opt) { + case 'b': + batch_mode = true; + break; + default: + usage(argv[0]); + } + } + + if (argc - optind != 2) + usage(argv[0]); + + const char *mailroot = argv[optind]; + const char *user = argv[optind + 1]; + + // Get user's mailbox + string maildir(mailroot); + maildir.append("/").append(user); + maildir_writer writer{maildir}; + + // Deliver message(s) + if (batch_mode) + do_batch_mode(0, 1, &writer); + else + writer.deliver(0); + + return 0; +} diff --git a/biscuit/cxx/mail-enqueue.cc b/biscuit/cxx/mail-enqueue.cc new file mode 100644 index 0000000..93c354a --- /dev/null +++ b/biscuit/cxx/mail-enqueue.cc @@ -0,0 +1,163 @@ +// mail-enqueue - queue a mail message for delivery + +#include "max_align.h" + +#include "libutil.h" +#include "shutil.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using std::string; + +class spool_writer +{ + string spooldir_; + int notifyfd_; + struct sockaddr_un notify_addr_; + socklen_t notify_len_; + +public: + spool_writer(const string &spooldir) : spooldir_(spooldir), notify_addr_{} + { + notifyfd_ = socket(AF_UNIX, SOCK_DGRAM, 0); + if (notifyfd_ < 0) + edie("socket failed"); + + notify_addr_.sun_family = AF_UNIX; + snprintf(notify_addr_.sun_path, sizeof notify_addr_.sun_path, + "%s/notify", spooldir.c_str()); + notify_len_ = SUN_LEN(¬ify_addr_); + } + + void queue(int msgfd, const char *recipient, size_t limit = (size_t)-1) + { + // Create temporary message + char tmpname[256]; + snprintf(tmpname, sizeof tmpname, + "%s/pid/%d", spooldir_.c_str(), (int)getpid()); + int tmpfd = open(tmpname, O_CREAT|O_EXCL|O_WRONLY, 0600); + if (tmpfd < 0) + edie("open %s failed", tmpname); + + if (copy_fd_n(tmpfd, msgfd, limit) < 0) + edie("copy_fd message failed"); + + struct stat st; + if (fstatx(tmpfd, &st, STAT_OMIT_NLINK) < 0) + edie("fstat failed"); + + fsync(tmpfd); + close(tmpfd); + + // Create envelope + char envname[256]; + snprintf(envname, sizeof envname, + "%s/todo/%lu", spooldir_.c_str(), (unsigned long)st.st_ino); + int envfd = open(envname, O_CREAT|O_EXCL|O_WRONLY, 0600); + if (envfd < 0) + edie("open %s failed", envname); + xwrite(envfd, recipient, strlen(recipient)); + fsync(envfd); + close(envfd); + + // Move message into spool + char msgname[256]; + snprintf(msgname, sizeof msgname, + "%s/mess/%lu", spooldir_.c_str(), (unsigned long)st.st_ino); + if (rename(tmpname, msgname) < 0) + edie("rename %s %s failed", tmpname, msgname); + + // Notify queue manager. We don't need an acknowledgment because + // we've already "durably" queued the message. + char notif[16]; + snprintf(notif, sizeof notif, "%lu", (unsigned long)st.st_ino); + if (sendto(notifyfd_, notif, strlen(notif), 0, + (struct sockaddr*)¬ify_addr_, notify_len_) < 0) + edie("send failed"); + } + + void queue_exit() + { + const char *msg = "EXIT"; + if (sendto(notifyfd_, msg, strlen(msg), 0, + (struct sockaddr*)¬ify_addr_, notify_len_) < 0) + edie("send exit failed"); + } +}; + +static void +do_batch_mode(int fd, int resultfd, const char *recip, spool_writer *spool) +{ + while (1) { + uint64_t msg_len; + size_t len = xread(fd, &msg_len, sizeof msg_len); + if (len == 0) + break; + if (len != sizeof msg_len) + die("short read of message length"); + spool->queue(fd, recip, msg_len); + + // Indicate success + uint64_t res = 0; + xwrite(resultfd, &res, sizeof res); + } +} + +static void +usage(const char *argv0) +{ + fprintf(stderr, "Usage: %s spooldir recipient and\n" + "a return code will be written to stdout after every delivery.\n" + ); + exit(2); +} + +int +main(int argc, char **argv) +{ + if (argc == 3 && strcmp(argv[1], "--exit") == 0) { + spool_writer spool{argv[2]}; + spool.queue_exit(); + return 0; + } + + int opt; + bool batch_mode = false; + while ((opt = getopt(argc, argv, "b")) != -1) { + switch (opt) { + case 'b': + batch_mode = true; + break; + default: + usage(argv[0]); + } + } + + if (argc - optind != 2) + usage(argv[0]); + + const char *spooldir = argv[optind]; + const char *recip = argv[optind + 1]; + + spool_writer spool{spooldir}; + if (batch_mode) + do_batch_mode(0, 1, recip, &spool); + else + spool.queue(0, recip); + + return 0; +} diff --git a/biscuit/cxx/mail-qman.cc b/biscuit/cxx/mail-qman.cc new file mode 100644 index 0000000..c71bed5 --- /dev/null +++ b/biscuit/cxx/mail-qman.cc @@ -0,0 +1,349 @@ +// mail-qman - deliver mail messages from the queue + +// The spool directory has the following structure: +// * pid/ - message files under construction +// * mess/ - message files +// * todo/ - envelope files +// * notify - a UNIX socket that receives an when a message +// is added to the spool + + +#include "max_align.h" + +#include "libutil.h" +#include "shutil.h" +//#include "xsys.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define SOCK_DGRAM_UNORDERED (-1) +#define O_ANYFD (0) + +using std::string; +using std::thread; + +extern char **environ; + +static bool alt; + +class spool_reader +{ + string spooldir_; + int notifyfd_; + struct sockaddr_un notify_sun_; + +public: + spool_reader(const string &spooldir) : spooldir_(spooldir) + { + // Create notification socket + notifyfd_ = socket(AF_UNIX, alt ? SOCK_DGRAM_UNORDERED : SOCK_DGRAM, 0); + if (notifyfd_ < 0) + edie("socket failed"); + struct sockaddr_un sun{}; + sun.sun_family = AF_UNIX; + snprintf(sun.sun_path, sizeof sun.sun_path, "%s/notify", spooldir.c_str()); + + // Normally we would just unlink(sun.sun_path), but since there's + // no way to kill mail-qman on xv6 right now, if it exists, it + // means this is a duplicate. + struct stat st; + if (stat(sun.sun_path, &st) == 0) + die("%s exists; mail-qman already running?", sun.sun_path); + + unlink(sun.sun_path); + if (bind(notifyfd_, (struct sockaddr*)&sun, SUN_LEN(&sun)) < 0) + edie("bind failed"); + + notify_sun_ = sun; + } + + string dequeue() + { + char buf[256]; + ssize_t r = recv(notifyfd_, buf, sizeof buf, 0); + if (r < 0) + edie("recv failed"); + return {buf, (size_t)r}; + } + + string get_recipient(const string &id) + { + char path[256]; + snprintf(path, sizeof path, "%s/todo/%s", spooldir_.c_str(), id.c_str()); + int fd = open(path, O_RDONLY|O_CLOEXEC|(alt ? O_ANYFD : 0)); + if (fd < 0) + edie("open %s failed", path); + struct stat st; + // We don't use "alt" here, even though this is an alternate + // interface, because this commutes regardless. Passing + // STAT_OMIT_NLINK is just for performance. + // XXX Maybe we should use a resizing read rather than fstat. + if (fstatx(fd, &st, STAT_OMIT_NLINK) < 0) + edie("fstat %s failed", path); + string res(st.st_size, 0); + if (readall(fd, &res.front(), res.size()) != (ssize_t)res.size()) + edie("readall %s failed", path); + close(fd); + return res; + } + + int open_message(const string &id) + { + char path[256]; + snprintf(path, sizeof path, "%s/mess/%s", spooldir_.c_str(), id.c_str()); + int fd = open(path, O_RDONLY|O_CLOEXEC|(alt ? O_ANYFD : 0)); + if (fd < 0) + edie("open %s failed", path); + return fd; + } + + void remove(const string &id) + { + string x; + x.append(spooldir_).append("/todo/").append(id); + unlink(x.c_str()); + x.clear(); + x.append(spooldir_).append("/mess/").append(id); + unlink(x.c_str()); + } + + void exit_others(int mycpu, int nthread) + { + // xv6 doesn't have an easy way to kill processes, so cascade the + // exit message to all other threads. We have to affinitize + // ourselves around in case socket load balancing is off. + int notifyfd = socket(AF_UNIX, SOCK_DGRAM, 0); + if (notifyfd < 0) + edie("%s: socket failed", __func__); + + const char *killmsg = "EXIT2"; + for (int i = 0; i < nthread; ++i) { + if (i == mycpu) + continue; + //setaffinity(i); + if (sendto(notifyfd, killmsg, strlen(killmsg), 0, + (struct sockaddr*)¬ify_sun_, SUN_LEN(¬ify_sun_)) < 0) + edie("%s: sendto failed", __func__); + } + + close(notifyfd); + + //setaffinity(mycpu); + } +}; + +class deliverer +{ + pid_t pid_; + string mailroot_; + bool pool_; + string pool_recipient_; + int msgpipe[2], respipe[2]; + + void start_child(const char *argv[], int stdin, int stdout) + { + if (alt) { +#if defined(XV6_USER) + // xv6 doesn't define errno. + int errno = 0; +#endif + posix_spawn_file_actions_t actions; + if ((errno = posix_spawn_file_actions_init(&actions))) + edie("posix_spawn_file_actions_init failed"); + if (stdin >= 0) + if ((errno = posix_spawn_file_actions_adddup2(&actions, stdin, 0))) + edie("posix_spawn_file_actions_adddup2 failed"); + if (stdout >= 0) + if ((errno = posix_spawn_file_actions_adddup2(&actions, stdout, 1))) + edie("posix_spawn_file_actions_adddup2 failed"); + if ((errno = posix_spawn(&pid_, argv[0], &actions, nullptr, + const_cast(argv), environ))) + edie("posix_spawn failed"); + if ((errno = posix_spawn_file_actions_destroy(&actions))) + edie("posix_spawn_file_actions_destroy failed"); + } else { + pid_ = fork(); + if (pid_ < 0) + edie("fork failed"); + if (pid_ == 0) { + // Note that this doesn't handle the case where stdin/stdout are + // 0/1 and have O_CLOEXEC set, but that never happens here. + if (stdin >= 0 && dup2(stdin, 0) < 0) + edie("dup2 stdin failed"); + if (stdout >= 0 && dup2(stdout, 1) < 0) + edie("dup2 stdout failed"); + execv(argv[0], const_cast(argv)); + edie("execv %s failed", argv[0]); + } + } + } + + void wait_child() + { + if (pool_) { + close(msgpipe[1]); + close(respipe[0]); + } + + int status; + if (waitpid(pid_, &status, 0) < 0) + edie("waitpid failed"); + if (!WIFEXITED(status) || WEXITSTATUS(status)) + die("deliver failed: status %d", status); + pid_ = 0; + } + +public: + deliverer(const string &mailroot, bool pool) + : pid_(0), mailroot_(mailroot), pool_(pool) { } + + ~deliverer() + { + if (pid_) + wait_child(); + } + + void + deliver(const string &recipient, int msgfd) + { + if (pool_) { + if (pid_ && recipient != pool_recipient_) + // Restart child if recipient changed + wait_child(); + + if (!pid_) { + // Start batch-mode deliver process + const char *argv[] = {EP("mail-deliver"), "-b", mailroot_.c_str(), + recipient.c_str(), nullptr}; + pool_recipient_ = recipient; + //if (pipe2(msgpipe, O_CLOEXEC|O_ANYFD) < 0) + if (pipe2(msgpipe, O_CLOEXEC) < 0) + edie("pipe msgpipe failed"); + //if (pipe2(respipe, O_CLOEXEC|O_ANYFD) < 0) + if (pipe2(respipe, O_CLOEXEC) < 0) + edie("pipe respipe failed"); + start_child(argv, msgpipe[0], respipe[1]); + close(msgpipe[0]); + close(respipe[1]); + } + + // Get length of message + uint64_t msg_len; + if ((msg_len = fd_len(msgfd)) < 0) + edie("mail-qman: failed to get length of message"); + + // Deliver message to running deliver process + xwrite(msgpipe[1], &msg_len, sizeof msg_len); + ssize_t r = copy_fd_n(msgpipe[1], msgfd, msg_len); + if (r < 0) + edie("mail-qman: copy_fd_n to mail-deliver failed"); + else if ((uint64_t)r < msg_len) + die("mail-qman: short write to mail-deliver (%zd < %zu)", + r, (size_t)msg_len); + + // Get batch-mode response + uint64_t res; + if (xread(respipe[0], &res, sizeof res) != sizeof res) + die("mail-qman: short read of deliver result code"); + if (res != 0) + die("mail-qman: mail-deliver returned status %d", (int)res); + } else { + const char *argv[] = {EP("mail-deliver"), mailroot_.c_str(), + recipient.c_str(), nullptr}; + start_child(argv, msgfd, -1); + wait_child(); + } + } +}; + +static void +do_process(spool_reader *spool, const string &mailroot, bool pool, + int nthread, int cpu) +{ + deliverer d{mailroot, pool}; + while (true) { + string id = spool->dequeue(); + if (id == "EXIT") { + spool->exit_others(cpu, nthread); + return; + } + if (id == "EXIT2") + return; + string recip = spool->get_recipient(id); + int msgfd = spool->open_message(id); + d.deliver(recip, msgfd); + close(msgfd); + spool->remove(id); + } +} + +static void +usage(const char *argv0) +{ + fprintf(stderr, "Usage: %s [options] spooldir mailroot nthread\n", argv0); + fprintf(stderr, " -a none Use regular APIs (default)\n"); + fprintf(stderr, " all Use alternate APIs\n"); + fprintf(stderr, " -p Use pooled mail-deliver\n"); + exit(2); +} + +int +main(int argc, char **argv) +{ + int opt; + bool pool = false; + while ((opt = getopt(argc, argv, "a:p")) != -1) { + switch (opt) { + case 'a': + if (strcmp(optarg, "all") == 0) { + errx(-1, "alt interface not supported"); + alt = true; + } else if (strcmp(optarg, "none") == 0) + alt = false; + else + usage(argv[0]); + break; + case 'p': + pool = true; + break; + default: + usage(argv[0]); + } + } + + if (argc - optind != 3) + usage(argv[0]); + + const char *spooldir = argv[optind]; + const char *mailroot = argv[optind+1]; + int nthread = atoi(argv[optind+2]); + if (nthread <= 0) + usage(argv[0]); + + spool_reader reader{spooldir}; + + thread *threads = new thread[nthread]; + + for (int i = 0; i < nthread; ++i) { + //setaffinity(i); + threads[i] = std::move(thread(do_process, &reader, mailroot, pool, + nthread, i)); + } + + for (int i = 0; i < nthread; ++i) + threads[i].join(); + return 0; +} diff --git a/biscuit/cxx/mailbench.cc b/biscuit/cxx/mailbench.cc new file mode 100644 index 0000000..732e198 --- /dev/null +++ b/biscuit/cxx/mailbench.cc @@ -0,0 +1,369 @@ +#include "max_align.h" + +//#include "amd64.h" +#include "distribution.hh" +#include "spinbarrier.hh" +#include "libutil.h" +//#include "xsys.h" + +//#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +// Set to 1 to manage the queue manager's life time from this program. +// Set to 0 if the queue manager is started and stopped outside of +// mailbench. +#define START_QMAN 1 + +using std::string; + +enum { warmup_secs = 1 }; +//enum { duration = 5 }; +int duration = 5; + +const char *message = + "Received: from incoming.csail.mit.edu (incoming.csail.mit.edu [128.30.2.16])\n" + " by metroplex (Cyrus v2.2.13-Debian-2.2.13-14+lenny5) with LMTPA;\n" + " Tue, 19 Mar 2013 22:45:50 -0400\n" + "X-Sieve: CMU Sieve 2.2\n" + "Received: from mailhub-auth-3.mit.edu ([18.9.21.43])\n" + " by incoming.csail.mit.edu with esmtps\n" + " (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32)\n" + " (Exim 4.72)\n" + " (envelope-from )\n" + " id 1UI92E-0007D2-7N\n" + " for xxxxxxxxx@csail.mit.edu; Tue, 19 Mar 2013 22:45:50 -0400\n" + "Received: from outgoing.mit.edu (OUTGOING-AUTH-1.MIT.EDU [18.9.28.11])\n" + " by mailhub-auth-3.mit.edu (8.13.8/8.9.2) with ESMTP id r2K2jnO5025684\n" + " for ; Tue, 19 Mar 2013 22:45:49 -0400\n" + "Received: from xxxxxxxxx.csail.mit.edu (xxxxxxxxx.csail.mit.edu [18.26.4.91])\n" + " (authenticated bits=0)\n" + " (User authenticated as xxxxxxxx@ATHENA.MIT.EDU)\n" + " by outgoing.mit.edu (8.13.8/8.12.4) with ESMTP id r2K2jmc7032022\n" + " (version=TLSv1/SSLv3 cipher=DHE-RSA-AES128-SHA bits=128 verify=NOT)\n" + " for ; Tue, 19 Mar 2013 22:45:49 -0400\n" + "Received: from xxxxxxx by xxxxxxxxx.csail.mit.edu with local (Exim 4.80)\n" + " (envelope-from )\n" + " id 1UI92C-0000il-4L\n" + " for xxxxxxxx@mit.edu; Tue, 19 Mar 2013 22:45:48 -0400\n" + "From: Austin Clements \n" + "To: xxxxxxxx@mit.edu\n" + "Subject: Test message\n" + "User-Agent: Notmuch/0.15+6~g7d4cb73 (http://notmuchmail.org) Emacs/23.4.1\n" + " (i486-pc-linux-gnu)\n" + "Date: Tue, 19 Mar 2013 22:45:48 -0400\n" + "Message-ID: <874ng6vler.fsf@xxxxxxxxx.csail.mit.edu>\n" + "MIME-Version: 1.0\n" + "Content-Type: text/plain; charset=us-ascii\n" + "\n" + "Hello.\n"; + +extern char **environ; + +static spin_barrier bar; + +static concurrent_distribution start_tsc, stop_tsc; +static concurrent_distribution start_usec, stop_usec; +static concurrent_distribution count; + +static volatile bool stop __mpalign__; +static volatile bool warmup; +static __padout__ __attribute__((unused)); + +static void +timer_thread(void) +{ + warmup = true; + bar.join(); + sleep(warmup_secs); + warmup = false; + sleep(duration); + stop = true; +} + +static void +xwaitpid(int pid, const char *cmd) +{ + int status; + if (waitpid(pid, &status, 0) < 0) + edie("waitpid %s failed", cmd); + if (!WIFEXITED(status) || WEXITSTATUS(status)) + die("status %d from %s", WEXITSTATUS(status), cmd); +} + +static void +do_mua(int cpu, string spooldir, string msgpath, size_t batch_size) +{ + std::vector argv{EP("mail-enqueue")}; +#if defined(XV6_USER) + int errno; +#endif + //setaffinity(cpu); + + // Open message file (alternatively, we could use an open spawn + // action) + int O_ANYFD = 0; + int msgfd = open(msgpath.c_str(), O_RDONLY|O_CLOEXEC|O_ANYFD); + if (msgfd < 0) + edie("open %s failed", msgpath.c_str()); + + // Construct command line + if (batch_size) + argv.push_back("-b"); + argv.push_back(spooldir.c_str()); + argv.push_back("user"); + argv.push_back(nullptr); + + bar.join(); + + bool mywarmup = true; + uint64_t mycount = 0; + pid_t pid = 0; + int msgpipe[2], respipe[2]; + while (!stop) { + if (__builtin_expect(warmup != mywarmup, 0)) { + mywarmup = warmup; + mycount = 0; + start_usec.add(now_usec()); + start_tsc.add(rdtsc()); + } + + if (pid == 0) { + posix_spawn_file_actions_t actions; + if ((errno = posix_spawn_file_actions_init(&actions))) + edie("posix_spawn_file_actions_init failed"); + if (batch_size) { + if (pipe2(msgpipe, O_CLOEXEC|O_ANYFD) < 0) + edie("pipe msgpipe failed"); + if ((errno = posix_spawn_file_actions_adddup2(&actions, msgpipe[0], 0))) + edie("posix_spawn_file_actions_adddup2 msgpipe failed"); + if (pipe2(respipe, O_CLOEXEC|O_ANYFD) < 0) + edie("pipe respipe failed"); + if ((errno = posix_spawn_file_actions_adddup2(&actions, respipe[1], 1))) + edie("posix_spawn_file_actions_adddup2 respipe failed"); + } else { + if (lseek(msgfd, 0, SEEK_SET) < 0) + edie("lseek failed"); + if ((errno = posix_spawn_file_actions_adddup2(&actions, msgfd, 0))) + edie("posix_spawn_file_actions_adddup2 failed"); + } + if ((errno = posix_spawn(&pid, argv[0], &actions, nullptr, + const_cast(argv.data()), environ))) + edie("posix_spawn failed"); + if ((errno = posix_spawn_file_actions_destroy(&actions))) + edie("posix_spawn_file_actions_destroy failed"); + + if (batch_size) { + close(msgpipe[0]); + close(respipe[1]); + } + } + + if (batch_size) { + // Send message in batch mode + uint64_t msg_len = strlen(message); + xwrite(msgpipe[1], &msg_len, sizeof msg_len); + xwrite(msgpipe[1], message, msg_len); + + // Get batch-mode response + uint64_t res; + if (xread(respipe[0], &res, sizeof res) != sizeof res) + die("short read of result code"); + if (res != 0) + die("%s returned status %d", argv[0], (int)res); + } + + ++mycount; + + if (batch_size == 0 || mycount % batch_size == 0) { + if (batch_size) { + close(msgpipe[1]); + close(respipe[0]); + } + xwaitpid(pid, argv[0]); + pid = 0; + } + } + + stop_usec.add(now_usec()); + stop_tsc.add(rdtsc()); + count.add(mycount); +} + +static void +xmkdir(const string &d) +{ + if (mkdir(d.c_str(), 0777) < 0) + edie("failed to mkdir %s", d.c_str()); +} + +static void +create_spool(const string &base) +{ + xmkdir(base); + xmkdir(base + "/pid"); + xmkdir(base + "/todo"); + xmkdir(base + "/mess"); + sync(); +} + +static void +create_maildir(const string &base) +{ + xmkdir(base); + xmkdir(base + "/tmp"); + xmkdir(base + "/new"); + xmkdir(base + "/cur"); + sync(); +} + +void +usage(const char *argv0) +{ + fprintf(stderr, "Usage: %s [options] basedir nthreads\n", argv0); + fprintf(stderr, " -a none Use regular APIs (default)\n"); + fprintf(stderr, " all Use alternate APIs\n"); + fprintf(stderr, " -b 0 Do not use batch spooling (default)\n"); + fprintf(stderr, " N Spool in batches of size N\n"); + fprintf(stderr, " inf Spool in unbounded batches\n"); + fprintf(stderr, " -p Use delivery process pooling\n"); + fprintf(stderr, " -d int Set duration to int\n"); + exit(2); +} + +int +main(int argc, char **argv) +{ + const char *alt_str = "none"; + size_t batch_size = 0; + bool pool = false; + int opt; + while ((opt = getopt(argc, argv, "d:a:b:p")) != -1) { + switch (opt) { + case 'a': + alt_str = optarg; + break; + case 'b': + if (strcmp(optarg, "inf") == 0) + batch_size = (size_t)-1; + else + batch_size = atoi(optarg); + break; + case 'p': + pool = true; + break; + case 'd': + duration = strtol(optarg, nullptr, 0); + if (duration < 0) + duration = 5; + break; + default: + usage(argv[0]); + } + } + + if (argc - optind != 2) + usage(argv[0]); + + string basedir(argv[optind]); + const char *nthreads_str = argv[optind+1]; + int nthreads = atoi(nthreads_str); + if (nthreads <= 0) + usage(argv[0]); + + // Create spool and inboxes + // XXX This terminology is wrong. The spool is where mail + // ultimately gets delivered to. + string spooldir = basedir + "/spool"; + if (START_QMAN) + create_spool(spooldir); + string mailroot = basedir + "/mail"; + xmkdir(mailroot); + create_maildir(mailroot + "/user"); + + pid_t qman_pid; + if (START_QMAN) { + // Start queue manager + std::vector qman{EP("mail-qman"), "-a", alt_str}; + if (pool) + qman.push_back("-p"); + qman.push_back(spooldir.c_str()); + qman.push_back(mailroot.c_str()); + qman.push_back(nthreads_str); + qman.push_back(nullptr); + if (posix_spawn(&qman_pid, qman[0], nullptr, nullptr, + const_cast(qman.data()), environ) != 0) + die("posix_spawn %s failed", qman[0]); + sleep(1); + } + + // Write message to a file + int fd = open((basedir + "/msg").c_str(), O_CREAT|O_WRONLY, 0666); + if (fd < 0) + edie("open"); + xwrite(fd, message, strlen(message)); + close(fd); + + printf("# --cores=%d --duration=%ds --alt=%s", + nthreads, duration, alt_str); + if (batch_size == (size_t)-1) + printf(" --batch-size=inf"); + else + printf(" --batch-size=%zu", batch_size); + printf(" --pool=%s\n", pool ? "true" : "false"); + + // Run benchmark + bar.init(nthreads + 1); + + std::thread timer(timer_thread); + + std::thread *threads = new std::thread[nthreads]; + for (int i = 0; i < nthreads; ++i) + threads[i] = std::thread(do_mua, i, basedir + "/spool", basedir + "/msg", + batch_size); + + // Wait + timer.join(); + for (int i = 0; i < nthreads; ++i) + threads[i].join(); + + if (START_QMAN) { + // Kill qman and wait for it to exit + const char *enq[] = {EP("mail-enqueue"), "--exit", spooldir.c_str(), nullptr}; + pid_t enq_pid; + if (posix_spawn(&enq_pid, enq[0], nullptr, nullptr, + const_cast(enq), environ) != 0) + die("posix_spawn %s failed", enq[0]); + xwaitpid(enq_pid, "mail-enqueue --exit"); + xwaitpid(qman_pid, "mail-qman"); + } + + // Summarize + printf("%lu start usec skew\n", start_usec.span()); + printf("%lu stop usec skew\n", stop_usec.span()); + uint64_t usec = stop_usec.mean() - start_usec.mean(); + printf("%lf secs\n", (double)usec / 1e6); + printf("%lu cycles\n", stop_tsc.mean() - start_tsc.mean()); + + uint64_t messages = count.sum(); + printf("%lu messages\n", messages); + if (messages) { + printf("%lu cycles/message\n", + (stop_tsc.sum() - start_tsc.sum()) / messages); + printf("%lu messages/sec\n", messages * 1000000 / usec); + } + + printf("\n"); + return 0; +} diff --git a/biscuit/cxx/max_align.h b/biscuit/cxx/max_align.h new file mode 100644 index 0000000..8b36db3 --- /dev/null +++ b/biscuit/cxx/max_align.h @@ -0,0 +1,6 @@ +typedef struct { + long long __max_align_ll __attribute__((__aligned__(__alignof__(long long)))); + long double __max_align_ld __attribute__((__aligned__(__alignof__(long double)))); +} max_align_t; + + diff --git a/biscuit/cxx/memory b/biscuit/cxx/memory new file mode 100644 index 0000000..53a2a3a --- /dev/null +++ b/biscuit/cxx/memory @@ -0,0 +1,352 @@ +// -*- c++ -*- + +#pragma once + +#include +#include + +namespace std { + // [C++11 20.6.12.1] + template + T* + addressof(T& r) noexcept + { + return reinterpret_cast(&(char&)r); + } + + // [C++11 20.7.1.1] Default deleters + template + struct default_delete { + constexpr default_delete() noexcept = default; + template ::value>::type> + default_delete(const default_delete&) noexcept { } + void operator()(T* ptr) const + { + delete ptr; + } + }; + + template + struct default_delete { + constexpr default_delete() noexcept = default; + void operator()(T* ptr) const + { + delete[] ptr; + } + template void operator()(U*) const = delete; + }; + + // Use empty base optimization if D is an empty class. However, D + // might also be a function type, in which case we can't subclass. + template struct __uptr_impl; + template + struct __uptr_impl : private D + { + P ptr_; + __uptr_impl(P ptr, const D &d) noexcept : D(d), ptr_(ptr) { } + __uptr_impl(P ptr, D &&d) noexcept : D(std::move(d)), ptr_(ptr) { } + D &get_deleter() { return *this; } + }; + template + struct __uptr_impl + { + D d_; + P ptr_; + __uptr_impl(P ptr, const D &d) noexcept : d_(d), ptr_(ptr) { } + __uptr_impl(P ptr, D &&d) noexcept : d_(std::move(d)), ptr_(ptr) { } + D &get_deleter() { return d_; } + }; + + // [C++11 20.7.1.2] + template > + class unique_ptr + { + public: + typedef T* pointer; // XXX Violates standard + typedef T element_type; + typedef D deleter_type; + + private: + __uptr_impl::value> impl_; + + public: + constexpr unique_ptr() noexcept : impl_(nullptr, D()) { } + + explicit unique_ptr(pointer p) noexcept : impl_(p, D()) { } + + unique_ptr(pointer p, + typename conditional::value, D, const D&>::type + d) noexcept + : impl_(p, d) { } + + unique_ptr(pointer p, typename remove_reference::type&& d) noexcept + : impl_(std::move(p), std::move(d)) + { + static_assert(!is_reference::value, + "rvalue deleter bound to reference"); + } + + unique_ptr(unique_ptr&& u) noexcept + : impl_(u.release(), std::forward(u.get_deleter())) { } + + constexpr unique_ptr(nullptr_t) noexcept : unique_ptr() { } + + template ::pointer, pointer>::value && + !is_array::value && + conditional::value, + is_same, + is_convertible >::type::value>::type> + unique_ptr(unique_ptr&& u) noexcept + : impl_(u.release(), std::forward(u.get_deleter())) { } + + ~unique_ptr() + { + if (get() != nullptr) + get_deleter()(get()); + } + + unique_ptr& + operator=(unique_ptr&& u) noexcept + { + reset(u.release()); + get_deleter() = std::forward(u.get_deleter()); + return *this; + } + + template ::pointer, pointer>::value && + !is_array::value>::type> + unique_ptr& + operator=(unique_ptr&& u) noexcept + { + reset(u.release()); + get_deleter() = std::forward(u.get_deleter()); + return *this; + } + + unique_ptr& + operator=(nullptr_t) noexcept + { + reset(); + return *this; + } + + typename add_lvalue_reference::type + operator*() const + { + return *get(); + } + + pointer + operator->() const noexcept + { + return get(); + } + + pointer + get() const noexcept + { + return impl_.ptr_; + } + + deleter_type& + get_deleter() noexcept + { + return impl_.get_deleter(); + } + + const deleter_type& + get_deleter() const noexcept + { + return impl_.get_deleter(); + } + + explicit + operator bool() const noexcept + { + return get() != nullptr; + } + + pointer + release() noexcept + { + pointer p = get(); + impl_.ptr_ = pointer(); + return p; + } + + void + reset(pointer p = pointer()) noexcept + { + pointer o = get(); + impl_.ptr_ = p; + if (o != pointer()) + get_deleter()(o); + } + + void + swap(unique_ptr& u) noexcept + { + std::swap(impl_.ptr_, u.impl_.ptr_); + std::swap(get_deleter(), u.get_deleter()); + } + + // Disable copy from lvalue + unique_ptr(const unique_ptr&) = delete; + unique_ptr& operator=(const unique_ptr&) = delete; + }; + + // [C++11 20.7.1.3] + template + class unique_ptr + { + public: + typedef T* pointer; // XXX Violates standard + typedef T element_type; + typedef D deleter_type; + + private: + __uptr_impl::value> impl_; + + public: + constexpr unique_ptr() noexcept : impl_(nullptr, D()) { } + + explicit unique_ptr(pointer p) noexcept : impl_(p, D()) { } + + unique_ptr(pointer p, + typename conditional::value, D, const D&>::type + d) noexcept + : impl_(p, d) { } + + unique_ptr(pointer p, typename remove_reference::type&& d) noexcept + : impl_(std::move(p), std::move(d)) + { + static_assert(!is_reference::value, + "rvalue deleter bound to reference"); + } + + unique_ptr(unique_ptr&& u) noexcept + : impl_(u.release(), std::forward(u.get_deleter())) { } + + constexpr unique_ptr(nullptr_t) noexcept : unique_ptr() { } + + ~unique_ptr() + { + if (get() != nullptr) + get_deleter()(get()); + } + + unique_ptr& + operator=(unique_ptr&& u) noexcept + { + reset(u.release()); + get_deleter() = std::forward(u.get_deleter()); + return *this; + } + + unique_ptr& + operator=(nullptr_t) noexcept + { + reset(); + return *this; + } + + T& + operator[](size_t i) const + { + return get()[i]; + } + + pointer + get() const noexcept + { + return impl_.ptr_; + } + + deleter_type& + get_deleter() noexcept + { + return impl_.get_deleter(); + } + + explicit + operator bool() const noexcept + { + return get() != nullptr; + } + + pointer + release() noexcept + { + pointer p = get(); + impl_.ptr_ = pointer(); + return p; + } + + void + reset(pointer p = pointer()) noexcept + { + pointer o = get(); + impl_.ptr_ = p; + if (o != pointer()) + get_deleter()(o); + } + + // Disallow resetting to convertible pointer types + template::value && + is_convertible::value && + is_base_of::value && + !is_same::type, + typename remove_cv::type>::value>::type> + void reset(U*) = delete; + + void + swap(unique_ptr& u) noexcept + { + std::swap(impl_.ptr_, u.impl_.ptr_); + std::swap(get_deleter(), u.get_deleter()); + } + }; + + // [C++14 20.8.1.4] + template + struct __make_unique_result + { + typedef unique_ptr single; + }; + + template + struct __make_unique_result + { + typedef unique_ptr array; + }; + + template + struct __make_unique_result + { + struct invalid { }; + }; + + template + typename __make_unique_result::single + make_unique(Args&& ...args) + { + return std::unique_ptr(new T(std::forward(args)...)); + } + + template + typename __make_unique_result::array + make_unique(size_t bound) + { + return unique_ptr(new typename remove_extent::type[bound]()); + } + + template + typename __make_unique_result::invalid + make_unique(Args&&...) = delete; +} diff --git a/biscuit/cxx/new b/biscuit/cxx/new new file mode 100644 index 0000000..f444f72 --- /dev/null +++ b/biscuit/cxx/new @@ -0,0 +1,52 @@ +// -*- c++ -*- +// C++11 18.6, minus new_handler +#pragma once + +#include + +namespace std { + class bad_alloc : public exception + { + public: + bad_alloc() noexcept { } + bad_alloc(const bad_alloc&) noexcept { } + bad_alloc& operator=(const bad_alloc&) noexcept + { + return *this; + } + virtual const char* what() const noexcept + { + return "allocation failed"; + } + }; + + class bad_array_new_length : public bad_alloc + { + public: + bad_array_new_length() noexcept { } + }; + + struct nothrow_t { + nothrow_t() {} + }; + + extern const nothrow_t nothrow; + + //typedef void (*new_handler)(); + //new_handler get_new_handler() noexcept; + //new_handler set_new_handler(new_handler new_p) noexcept; +} + +void* operator new(std::size_t size); +void* operator new(std::size_t size, const std::nothrow_t&) noexcept; +void operator delete(void* ptr); +void operator delete(void* ptr, const std::nothrow_t&) noexcept; +void* operator new[](std::size_t size); +void* operator new[](std::size_t size, const std::nothrow_t&) noexcept; +void operator delete[](void* ptr); +void operator delete[](void* ptr, const std::nothrow_t&) noexcept; + +void* operator new(std::size_t size, void* ptr) noexcept; +void* operator new[](std::size_t size, void* ptr) noexcept; +void operator delete(void* ptr, void*) noexcept; +void operator delete[](void* ptr, void*) noexcept; diff --git a/biscuit/cxx/queue b/biscuit/cxx/queue new file mode 100644 index 0000000..2391d8f --- /dev/null +++ b/biscuit/cxx/queue @@ -0,0 +1,129 @@ +// -*- c++ -*- + +#pragma once + +#include +#include +#include + +namespace std { + // [C++11 23.6.4] + // Not implemented: Allocator support + template , + class Compare = less > + class priority_queue { + public: + typedef typename Container::value_type value_type; + typedef typename Container::reference reference; + typedef typename Container::const_reference const_reference; + typedef typename Container::size_type size_type; + typedef Container container_type; + + protected: + Compare comp_; + Container c_; + + public: + priority_queue(const Compare& comp, const Container& c) + : comp_(comp), c_(c) + { + std::make_heap(c_.begin(), c_.end(), comp_); + } + + explicit priority_queue(const Compare& comp = Compare(), + Container&& c = Container()) + : comp_(comp), c_(c) + { + std::make_heap(c_.begin(), c_.end(), comp_); + } + + template + priority_queue(InputIterator first, InputIterator last, + const Compare& comp, const Container& c) + : comp_(comp), c_(c) + { + c_.insert(c_.end(), first, last); + std::make_heap(c_.begin(), c_.end(), comp_); + } + + template + priority_queue(InputIterator first, InputIterator last, + const Compare& comp = Compare(), Container&& c = Container()) + : comp_(comp), c_(c) + { + c_.insert(c_.end(), first, last); + std::make_heap(c_.begin(), c_.end(), comp_); + } + + // template explicit priority_queue(const Alloc& a); + // template + // priority_queue(const Compare& comp, const Alloc& a); + // template + // priority_queue(const Compare& comp, const Container& c, const Alloc& a); + // template + // priority_queue(const Compare& comp, Container&& c, const Alloc& a); + // template + // priority_queue(const priority_queue& q, const Alloc& a); + // template + // priority_queue(priority_queue&& q, const Alloc& a); + + bool empty() const + { + return c_.empty(); + } + + size_type size() const + { + return c_.size(); + } + + const_reference top() const + { + return c_.front(); + } + + void push(const value_type& x) + { + c_.push_back(x); + std::push_heap(c_.begin(), c_.end(), comp_); + } + + void push(value_type&& x) + { + c_.push_back(std::move(x)); + std::push_heap(c_.begin(), c_.end(), comp_); + } + + template + void emplace(Args&&... args) + { + c_.emplace_back(std::forward(args)...); + std::push_heap(c_.begin(), c_.end(), comp_); + } + + void pop() + { + pop_heap(c_.begin(), c_.end(), comp_); + c_.pop_back(); + } + + void swap(priority_queue& q) + noexcept(noexcept(swap(c_, q.c_)) && noexcept(swap(comp_, q.comp_))) + { + std::swap(c_, q.c_); + std::swap(comp_, q.comp_); + } + }; + + template + void swap(priority_queue& x, + priority_queue& y) + noexcept(noexcept(x.swap(y))) + { + x.swap(y); + } + + // template + // struct uses_allocator, Alloc> + // : uses_allocator::type { }; +} diff --git a/biscuit/cxx/shutil.cc b/biscuit/cxx/shutil.cc new file mode 100644 index 0000000..40144e9 --- /dev/null +++ b/biscuit/cxx/shutil.cc @@ -0,0 +1,115 @@ +#include "shutil.h" +#include "libutil.h" + +#include +#include +#include +#include +#include + +ssize_t +writeall(int fd, const void *buf, size_t n) +{ + size_t pos = 0; + while (pos < n) { + ssize_t r = write(fd, (char*)buf + pos, n - pos); + assert(r != 0); + if (r < 0) { +#if !defined(XV6_USER) + if (errno == EINTR) + continue; +#endif + break; + } + pos += r; + } + return pos; +} + +ssize_t +readall(int fd, void *buf, size_t n) +{ + size_t pos = 0; + while (pos < n) { + ssize_t r = read(fd, (char*)buf + pos, n - pos); + if (r < 0) { +#if !defined(XV6_USER) + if (errno == EINTR) + continue; +#endif + if (pos == 0) + return -1; + break; + } + if (r == 0) + break; + pos += r; + } + return pos; +} + +// Read from src until EOF, write to dst. Returns number of bytes +// copied on success, < 0 on failure. +ssize_t +copy_fd(int dst, int src) +{ + return copy_fd_n(dst, src, (size_t)-1); +} + +// Read from src until EOF or limit bytes, write to dst. Returns +// number of bytes copied on success, < 0 on failure. +ssize_t +copy_fd_n(int dst, int src, size_t limit) +{ + size_t res = 0; + char buf[4096]; + while (res < limit) { + int r = read(src, buf, limit - res < sizeof buf ? limit - res : sizeof buf); + if (r < 0) { +#if !defined(XV6_USER) + if (errno == EINTR) + continue; +#endif + return r; + } + if (r == 0) + return res; + int r2 = writeall(dst, buf, r); + if (r != r2) + return -1; + res += r; + } + return res; +} + +int +mkdir_if_noent(const char *path, mode_t mode) +{ + int r = mkdir(path, mode); + if (r < 0) { +#if !defined(XV6_USER) + // xv6 doesn't have errno + if (errno != EEXIST) + return -1; +#endif + } + return 0; +} + +// Return the length of the file referred to by fd. Returns < 0 on +// failure. +ssize_t +fd_len(int fd) +{ + // Could also do this with fstat, but that returns way more + // information than we need + off_t pos = lseek(fd, 0, SEEK_CUR); + if (pos < 0) + return -1; + off_t end = lseek(fd, 0, SEEK_END); + if (end < 0) + return -1; + if (lseek(fd, pos, SEEK_SET)) + edie("failed to return file offset to original position"); + return end; +} diff --git a/biscuit/cxx/shutil.h b/biscuit/cxx/shutil.h new file mode 100644 index 0000000..f56bead --- /dev/null +++ b/biscuit/cxx/shutil.h @@ -0,0 +1,10 @@ +#pragma once + +#include + +ssize_t writeall(int fd, const void *buf, size_t n); +ssize_t readall(int fd, void *buf, size_t n); +ssize_t copy_fd(int dst, int src); +ssize_t copy_fd_n(int dst, int src, size_t limit); +int mkdir_if_noent(const char *path, mode_t mode); +ssize_t fd_len(int fd); diff --git a/biscuit/cxx/spinbarrier.hh b/biscuit/cxx/spinbarrier.hh new file mode 100644 index 0000000..3447589 --- /dev/null +++ b/biscuit/cxx/spinbarrier.hh @@ -0,0 +1,62 @@ +#pragma once + +#include "compiler.h" + +#include +#include + +// A reusable spinning barrier. +class spin_barrier +{ + uint32_t init_ __mpalign__; + // Bit 63 is 0 for enter phase, 1 for exit phase. Remaining bits + // are the reached count. + std::atomic entered_; + __padout__; + + static constexpr uint64_t phase_mask = 1ull << 63; + static constexpr uint64_t reached_mask = ~phase_mask; + +public: + spin_barrier() : init_(0), entered_(0) { } + spin_barrier(unsigned val) : init_(val), entered_(0) { } + spin_barrier(const spin_barrier&) = delete; + ~spin_barrier() + { + // Wait if the barrier is in the exit phase, since it's not safe + // to delete until all completed waiters have exited + while (entered_ & phase_mask) + /* spin */; + } + + void init(unsigned val) + { + init_ = val; + entered_ = 0; + } + + void join() + { + // Wait if the barrier is in the exit phase + while (entered_ & phase_mask) + asm volatile("pause":::); + + // Enter the barrier + auto v = ++entered_; + if (v == init_) { + // This is the last thread to reach the barrier. Switch to the + // exit phase and exit the barrier. + entered_ = (v | phase_mask) - 1; + return; + } + + // Wait until the barrier switches to the exit phase + while (!(entered_.load(std::memory_order_relaxed) & phase_mask)) + asm volatile("pause":::); + + // Exit the batter + if ((v = --entered_) == phase_mask) + // Reached count is now zero. Switch to the enter phase. + entered_ = 0; + } +}; diff --git a/biscuit/cxx/stdexcept b/biscuit/cxx/stdexcept new file mode 100644 index 0000000..c8cdfb0 --- /dev/null +++ b/biscuit/cxx/stdexcept @@ -0,0 +1,90 @@ +// -*- c++ -*- +// C++11 19.2 +#pragma once + +#include +#include + +namespace std +{ + class __base_error : public exception + { + const char *msg_; + string str_; + public: + explicit __base_error(const char *what_arg) + : msg_(what_arg) { } + explicit __base_error(const string &what_arg) + : msg_(nullptr), str_(what_arg) { } + ~__base_error() noexcept { } + virtual const char *what() const noexcept + { + if (msg_) + return msg_; + return str_.c_str(); + } + }; + + class logic_error : public __base_error + { + public: + explicit logic_error(const char* what_arg) : __base_error(what_arg) { } + explicit logic_error(const string& what_arg) : __base_error(what_arg) { } + }; + + class domain_error : public logic_error + { + public: + explicit domain_error(const char* what_arg) : logic_error(what_arg) { } + explicit domain_error(const string& what_arg) : logic_error(what_arg) { } + }; + + class invalid_argument : public logic_error + { + public: + explicit invalid_argument(const char* what_arg) : logic_error(what_arg) { } + explicit invalid_argument(const string& what_arg) : logic_error(what_arg) { } + }; + + class length_error : public logic_error + { + public: + explicit length_error(const char* what_arg) : logic_error(what_arg) { } + explicit length_error(const string& what_arg) : logic_error(what_arg) { } + }; + + class out_of_range : public logic_error + { + public: + explicit out_of_range(const char* what_arg) : logic_error(what_arg) { } + explicit out_of_range(const string& what_arg) : logic_error(what_arg) { } + }; + + class runtime_error : public __base_error + { + public: + explicit runtime_error(const char* what_arg) : __base_error(what_arg) { } + explicit runtime_error(const string& what_arg) : __base_error(what_arg) { } + }; + + class range_error : public runtime_error + { + public: + explicit range_error(const char* what_arg) : runtime_error(what_arg) { } + explicit range_error(const string& what_arg) : runtime_error(what_arg) { } + }; + + class overflow_error : public runtime_error + { + public: + explicit overflow_error(const char* what_arg) : runtime_error(what_arg) { } + explicit overflow_error(const string& what_arg) : runtime_error(what_arg) { } + }; + + class underflow_error : public runtime_error + { + public: + explicit underflow_error(const char* what_arg) : runtime_error(what_arg) { } + explicit underflow_error(const string& what_arg) : runtime_error(what_arg) { } + }; +} diff --git a/biscuit/cxx/string b/biscuit/cxx/string new file mode 100644 index 0000000..8241bbb --- /dev/null +++ b/biscuit/cxx/string @@ -0,0 +1,665 @@ +// -*- c++ -*- +#pragma once + +#include +#include +#include +#include +#include + +namespace std { + class out_of_range; + + template + class basic_string + { + // The data backing the string. This is shared and copy-on-write. + // This is always allocated to allow one extra byte in buf beyond + // cap, which provides space for a NUL charT. Anything that + // modifies len must ensure that buf[len] == 0. + struct data + { + atomic refcnt; + size_t len, cap; + charT buf[]; + + static data* alloc(data *cur, size_t cap) + { + // Allocate space for cap plus a NUL. + struct data *res = reinterpret_cast( + new char[sizeof(*res) + sizeof(charT) * (cap + 1)]); + res->refcnt.store(1, memory_order_relaxed); + res->cap = cap; + if (cur) { + res->len = cur->len; + memmove(res->buf, cur->buf, sizeof(charT) * (cur->len + 1)); + } else { + res->len = 0; + res->buf[0] = 0; + } + return res; + } + + void set_len(size_t nlen) + { + buf[nlen] = 0; + len = nlen; + } + }; + + struct data *data_; + + void release() + { + if (data_ && --data_->refcnt == 0) + delete[] (char*)data_; + data_ = nullptr; + } + + void may_write() + { + if (data_ && data_->refcnt > 1) { + struct data *ndata = data::alloc(data_, data_->cap); + release(); + data_ = ndata; + } + } + + public: + // XXX traits_type, allocator_type + // XXX Traits and allocator-aware + typedef charT value_type; + typedef size_t size_type; + typedef size_t difference_type; + + typedef value_type& reference; + typedef const value_type& const_reference; + typedef value_type* pointer; + typedef const value_type* const_pointer; + + typedef value_type* iterator; + typedef const value_type* const_iterator; + + static const size_type npos = -1; + + basic_string() : data_() { } + + basic_string(const basic_string& str) : data_(str.data_) + { + if (data_) + ++data_->refcnt; + } + + basic_string(basic_string&& str) noexcept : data_(str.data_) + { + str.data_ = nullptr; + } + + basic_string(const basic_string& str, size_type pos, size_type n = npos) + : basic_string() + { + append(str, pos, n); + } + + basic_string(const charT* s, size_type n) : basic_string() + { + append(s, n); + } + + basic_string(const charT* s) : basic_string() + { + append(s); + } + + basic_string(size_type n, charT c) : basic_string() + { + append(n, c); + } + + template + basic_string(InputIterator begin, InputIterator end) : basic_string() + { + append(begin, end); + } + + basic_string(initializer_list il) : basic_string() + { + append(il.begin(), il.size()); + } + + ~basic_string() + { + release(); + } + + basic_string& operator=(const basic_string& str) + { + if (&str != this) { + release(); + if ((data_ = str.data_)) + ++data_->refcnt; + } + return *this; + } + + basic_string& operator=(basic_string &&str) noexcept + { + release(); + data_ = str.data_; + str.data_ = nullptr; + return *this; + } + + basic_string& operator=(const charT* s) + { + clear(); + return append(s); + } + + basic_string& operator=(charT c) + { + clear(); + return append(1, c); + } + + basic_string& operator=(initializer_list il) + { + clear(); + return append(il.begin(), il.size()); + } + + iterator begin() noexcept + { + may_write(); + if (data_) + return &data_->buf[0]; + return nullptr; + } + + const_iterator begin() const noexcept + { + return cbegin(); + } + + iterator end() noexcept + { + may_write(); + if (data_) + return &data_->buf[data_->len]; + return nullptr; + } + + const_iterator end() const noexcept + { + return cend(); + } + + // XXX rbegin, rend + + const_iterator cbegin() const noexcept + { + if (data_) + return &data_->buf[0]; + return nullptr; + } + + const_iterator cend() const noexcept + { + if (data_) + return &data_->buf[data_->len]; + return nullptr; + } + + // XXX crbegin, crend + + size_type size() const noexcept + { + if (data_) + return data_->len; + return 0; + } + + size_type length() const noexcept + { + return size(); + } + + size_type max_size() const noexcept + { + return (size_type)-1; + } + + // XXX resize + + size_type capacity() const noexcept + { + if (data_) + return data_->cap; + return 0; + } + + void reserve(size_type n = 0) + { + // Make sure there's enough space besides the buffer itself for + // the allocator to store a size plus alignment padding, for the + // data header, and for a terminating NUL. As a result, the + // allocation will generally be power-of-two friendly and cap + // will generally by a power of two minus this padding. + const static auto PADDING = + 2 * sizeof(void*) + offsetof(struct data, buf) + 1; + if (n < size()) + n = size(); + // Compute a the smallest power-of-two total allocation size + // that will fit n. + n += PADDING; + size_type goal = 16; + while (n > goal) + goal *= 2; + // The new buffer capacity of the total allocation goal minus + // the padding we accounting for. + size_type ncap = goal - PADDING; + if (data_ && ncap == data_->cap) + return; + // Create new data + struct data *ndata = data::alloc(data_, ncap); + release(); + data_ = ndata; + } + + void shrink_to_fit() + { + if (size() == capacity()) + return; + struct data *ndata = data::alloc(data_, capacity()); + release(); + data_ = ndata; + } + + void clear() noexcept + { + if (!data_) + return; + if (data_->refcnt > 1) + release(); + else + data_->set_len(0); + } + + bool empty() const noexcept + { + return data_ == nullptr || data_->len == 0; + } + + const_reference operator[](size_type pos) const + { + return *(begin() + pos); + } + + reference operator[](size_type pos) + { + return *(begin() + pos); + } + + const_reference at(size_type pos) const + { + if (pos >= size()) + throw out_of_range("index out of range"); + return *(begin() + pos); + } + + reference at(size_type pos) + { + if (pos >= size()) + throw out_of_range("index out of range"); + return *(begin() + pos); + } + + const charT& front() const + { + return *begin(); + } + + charT& front() + { + return *begin(); + } + + const charT& back() const + { + return *(end() - 1); + } + + charT& back() + { + return *(end() - 1); + } + + basic_string& operator+=(const basic_string& str) + { + return append(str); + } + + basic_string& operator+=(const charT* s) + { + return append(s); + } + + basic_string& operator+=(charT c) + { + return append(1, c); + } + + basic_string& operator+=(initializer_list il) + { + return append(il.first(), il.size()); + } + + basic_string& append(const basic_string& str) + { + return append(str.data(), str.size()); + } + + basic_string& append(const basic_string& str, size_type pos, size_type n) + { + if (pos > str.size()) + throw out_of_range("pos out of range"); + if (n > str.size() - pos) + n = str.size() - pos; + return append(str.begin() + pos, n); + } + + basic_string& append(const charT* s, size_type n) + { + reserve(size() + n); + may_write(); + memmove(data_->buf + data_->len, s, sizeof(charT)*n); + data_->set_len(data_->len + n); + return *this; + } + + basic_string& append(const charT* s) + { + // XXX traits::length + return append(s, strlen(s)); + } + + basic_string& append(size_type n, charT c) + { + reserve(size() + n); + may_write(); + fill(data_->buf + data_->len, data_->buf + data_->len + n, c); + data_->set_len(data_->len + n); + return *this; + } + + template + basic_string& append(InputIterator first, InputIterator last) + { + for (; first != last; ++first) + push_back(*first); + return *this; + } + + basic_string& append(initializer_list il) + { + return append(il.begin(), il.size()); + } + + void push_back(charT c) + { + reserve(size() + 1); + may_write(); + data_->buf[data_->len] = c; + data_->set_len(data_->len + 1); + } + + // XXX assign, insert, erase + + void pop_back() + { + if (!empty()) { + may_write(); + data_->set_len(data_->len - 1); + } + } + + // XXX replace, copy, swap + + const charT* c_str() const noexcept + { + // We always maintain a NUL terminator + return data(); + } + + const charT* data() const noexcept + { + return begin(); + } + + // XXX get_allocator, find, rfind, find_first_of, find_last_of, + // find_first_not_of, find_last_not_of, substr + + int compare(const basic_string& str) const noexcept + { + return compare(0, size(), str.data(), str.size()); + } + + int compare(size_type pos1, size_type n1, + const basic_string& str) const + { + return compare(pos1, n1, str.data(), str.size()); + } + + int compare(size_type pos1, size_type n1, + const basic_string& str, + size_type pos2, size_type n2) const + { + if (pos2 > str.size()) + throw out_of_range("compare pos out of range"); + if (n2 > str.size() - pos2) + n2 = str.size() - pos2; + return compare(pos1, n1, str.data() + pos2, n2); + } + + int compare(const charT* s) const + { + return compare(0, size(), s); + } + + int compare(size_type pos1, size_type n1, + const charT* s) const + { + return compare(pos1, n1, s, strlen(s)); + } + + int compare(size_type pos1, size_type n1, + const charT* s, size_type n2) const + { + if (pos1 > size()) + throw out_of_range("compare pos out of range"); + if (n1 > size() - pos1) + n1 = size() - pos1; + size_type rlen = n1 < n2 ? n1 : n2; + int res = memcmp(data(), s, rlen * sizeof(charT)); + if (res == 0) { + if (n1 < n2) + res = -1; + else if (n1 > n2) + res = 1; + } + return res; + } + }; + + template + bool operator==(const basic_string& a, const basic_string& b) + { + return a.compare(b) == 0; + } + + template + bool operator==(const charT* a, const basic_string& b) + { + return b.compare(a) == 0; + } + + template + bool operator==(const basic_string& a, const charT* b) + { + return a.compare(b) == 0; + } + + template + bool operator!=(const basic_string& a, const basic_string& b) + { + return !(a == b); + } + + template + bool operator!=(const charT* a, const basic_string& b) + { + return !(a == b); + } + + template + bool operator!=(const basic_string& a, const charT* b) + { + return !(a == b); + } + + template + bool operator<(const basic_string& a, const basic_string& b) + { + return a.compare(b) < 0; + } + + template + bool operator<(const charT* a, const basic_string& b) + { + return b.compare(a) > 0; + } + + template + bool operator<(const basic_string& a, const charT* b) + { + return a.compare(b) < 0; + } + + template + bool operator>(const basic_string& a, const basic_string& b) + { + return a.compare(b) > 0; + } + + template + bool operator>(const charT* a, const basic_string& b) + { + return b.compare(a) < 0; + } + + template + bool operator>(const basic_string& a, const charT* b) + { + return a.compare(b) > 0; + } + + template + bool operator<=(const basic_string& a, const basic_string& b) + { + return a.compare(b) <= 0; + } + + template + bool operator<=(const charT* a, const basic_string& b) + { + return b.compare(a) >= 0; + } + + template + bool operator<=(const basic_string& a, const charT* b) + { + return a.compare(b) <= 0; + } + + template + bool operator>=(const basic_string& a, const basic_string& b) + { + return a.compare(b) >= 0; + } + + template + bool operator>=(const charT* a, const basic_string& b) + { + return b.compare(a) <= 0; + } + + template + bool operator>=(const basic_string& a, const charT* b) + { + return a.compare(b) >= 0; + } + + + template + basic_string operator+(const basic_string& lhs, + const basic_string& rhs) + { + basic_string o(lhs); + return o.append(rhs); + } + + template + basic_string operator+(basic_string&& lhs, + const basic_string& rhs) + { + return lhs.append(rhs); + } + + template + basic_string operator+(const charT* lhs, + const basic_string& rhs) + { + basic_string o(lhs); + return o.append(rhs); + } + + template + basic_string operator+(charT lhs, + const basic_string& rhs) + { + basic_string o(1, lhs); + return o.append(rhs); + } + + template + basic_string operator+(const basic_string& lhs, + const charT* rhs) + { + basic_string o(lhs); + return o.append(rhs); + } + + template + basic_string operator+(basic_string&& lhs, + const charT* rhs) + { + return lhs.append(rhs); + } + + template + basic_string operator+(const basic_string& lhs, + const charT rhs) + { + basic_string o(lhs); + return o.append(1, rhs); + } + + template + basic_string operator+(basic_string&& lhs, + const charT rhs) + { + return lhs.append(1, rhs); + } + + typedef basic_string string; +} diff --git a/biscuit/cxx/thread b/biscuit/cxx/thread new file mode 100644 index 0000000..36ea2cf --- /dev/null +++ b/biscuit/cxx/thread @@ -0,0 +1,145 @@ +// -*- c++ -*- +// C++11 30 +#pragma once + +#include +#include "bind.hh" + +namespace std +{ + class thread + { + public: + class id + { + long thread_; + + friend class thread; + explicit id(int thread) noexcept : thread_(thread) { } + + public: + id() noexcept : thread_(-1) { } + + friend bool operator==(thread::id x, thread::id y) noexcept + { + return x.thread_ == y.thread_; + } + + friend bool operator!=(thread::id x, thread::id y) noexcept + { + return x.thread_ != y.thread_; + } + + friend bool operator<(thread::id x, thread::id y) noexcept + { + return x.thread_ < y.thread_; + } + + friend bool operator<=(thread::id x, thread::id y) noexcept + { + return x.thread_ <= y.thread_; + } + + friend bool operator>(thread::id x, thread::id y) noexcept + { + return x.thread_ > y.thread_; + } + + friend bool operator>=(thread::id x, thread::id y) noexcept + { + return x.thread_ >= y.thread_; + } + + static id __get_this_thread_id() noexcept; + }; + + private: + id id_; + + class vrun + { + public: + virtual ~vrun() { } + virtual void run() = 0; + }; + + template + class vruncb : public vrun + { + CB cb_; + public: + vruncb(CB&& cb) : cb_(forward(cb)) { } + void run() override + { + cb_(); + } + }; + + template + static vrun *make_vrun(CB&& cb) + { + return new vruncb(forward(cb)); + } + + static void *vrun_wrapper(void *opaque); + + thread(vrun *r); + + public: + thread() noexcept { } + + template + explicit thread(F&& f, Args&&... args) + : thread(make_vrun(bind_simple(forward(f), forward(args)...))) + { } + + thread(thread&& x) noexcept : id_(x.id_) + { + x.id_ = id(); + } + + ~thread(); + + thread& operator=(thread&& x) noexcept; + + void swap(thread& x) noexcept + { + thread tmp(move(*this)); + *this = move(x); + x = move(tmp); + } + + bool joinable() const noexcept + { + return get_id() != id(); + } + + void join(); + + // void detach(); + + id get_id() const noexcept + { + return id_; + } + + static int hardware_concurrency() noexcept + { + const int NCPU = 8; + return NCPU; + } + }; + + static inline void swap(thread& x, thread& y) noexcept + { + x.swap(y); + } + + namespace this_thread + { + static inline thread::id get_id() noexcept + { + return thread::id::__get_this_thread_id(); + } + } +} diff --git a/biscuit/cxx/threads.cc b/biscuit/cxx/threads.cc new file mode 100644 index 0000000..d605c12 --- /dev/null +++ b/biscuit/cxx/threads.cc @@ -0,0 +1,72 @@ +#include "max_align.h" + +#include + +#include + +#include "libutil.h" + +void * +std::thread::vrun_wrapper(void *opaque) +{ + vrun *r = (vrun*)opaque; + try { + r->run(); + } catch (...) { + // XXX terminate + die("uncaught exception in thread"); + } + delete r; + return nullptr; +} + +std::thread::thread(vrun *r) +{ + pthread_t thread; + if (pthread_create(&thread, nullptr, vrun_wrapper, r) != 0) + // XXX Throw system_error + die("pthread_create failed"); + id_ = id(thread); +} + +std::thread::~thread() +{ + if (joinable()) + // XXX terminate + die("~thread for joinable thread"); +} + +std::thread& +std::thread::operator=(std::thread&& x) noexcept +{ + if (joinable()) + // XXX terminate + die("operator= for joinable thread"); + id_ = x.id_; + x.id_ = id(); + return *this; +} + +void +std::thread::join() +{ + if (get_id() == id()) + // XXX Throw system_error(no_such_process) + die("attempt to join invalid thread"); + if (!joinable()) + // XXX Throw system_error(invalid_argument) + die("attempt to join un-joinable thread"); + if (get_id() == std::this_thread::get_id()) + // XXX Throw system_error(resource_deadlock_would_occur) + die("attempt to join self"); + + if (pthread_join(id_.thread_, nullptr) != 0) + die("pthread_join failed"); + id_ = id(); +} + +std::thread::id +std::thread::id::__get_this_thread_id() noexcept +{ + return id(pthread_self()); +} diff --git a/biscuit/cxx/tuple b/biscuit/cxx/tuple new file mode 100644 index 0000000..4bedc85 --- /dev/null +++ b/biscuit/cxx/tuple @@ -0,0 +1,114 @@ +// -*- c++ -*- +// C++11 20.4 +#pragma once + +#include +#include + +namespace std { + // + // tuple + // + + template + class tuple; + + template + class tuple + { + T0 car_; + tuple cdr_; + + template + friend struct __get_impl; + + public: + constexpr tuple() : car_(), cdr_() { } + + explicit tuple(const T0& car, const TN&... cdr) + : car_(car), cdr_(tuple(cdr...)) { } + + template + explicit tuple(UType0 &&car, UTypes&&... cdr) + : car_(std::forward(car)), + cdr_(tuple(std::forward(cdr)...)) { } + + tuple(const tuple&) = default; + tuple(tuple&&) = default; + + tuple& operator=(const tuple&) = default; + tuple& operator=(tuple&&) = default; + + void swap(tuple& rhs) + { + swap(car_, rhs.car_); + swap(cdr_, rhs.cdr_); + } + }; + + template <> + class tuple<> + { + public: + constexpr tuple() { } + void swap(tuple& rhs) { } + }; + + // + // tuple_element + // + + template + class tuple_element; + + template + class tuple_element<0, tuple > { + public: + typedef T0 type; + }; + + template + class tuple_element > + : public tuple_element > { }; + + // + // get + // + + template + struct __get_impl; + + template + struct __get_impl<0, T0, TN...> + { + static T0 &get(tuple& t) noexcept + { + return t.car_; + } + }; + + template + struct __get_impl + { + static typename tuple_element >::type& + get(tuple& t) noexcept + { + return __get_impl::get(t.cdr_); + } + }; + + template + typename tuple_element >::type& get(tuple& t) + noexcept + { + return __get_impl::get(t); + } + + template + typename tuple_element >::type&& get(tuple&& t) + noexcept + { + return std::forward > + ::type&&>(get(t)); + } +} diff --git a/biscuit/cxx/utility b/biscuit/cxx/utility new file mode 100644 index 0000000..c9ce438 --- /dev/null +++ b/biscuit/cxx/utility @@ -0,0 +1,71 @@ +// -*- c++ -*- + +#pragma once + +#include +#include + +namespace std { + template + T&& + forward(typename remove_reference::type& t) + { + return static_cast(t); + } + + template + T&& + forward(typename remove_reference::type&& t) + { + return static_cast(t); + } + + template + typename remove_reference::type&& + move(T&& a) + { + return static_cast::type&&>(a); + } + + template + void + swap(T& a, T& b) + { + T tmp = move(a); + a = move(b); + b = move(tmp); + } + + template + void + swap(T (&a)[N], T (&b)[N]) + { + for (size_t n = 0; n < N; n++) + swap(a[n], b[n]); + } + + template + struct pair { + typedef A first_type; + typedef B second_type; + + A first; + B second; + + pair(const pair&) = default; + pair(pair&&) = default; + constexpr pair() : first(), second() {} + pair(const A &a, const B &b) : first(a), second(b) {} + + bool operator==(const pair &other) const { + return first == other.first && second == other.second; + } + }; + + template + pair + make_pair(const A &a, const B &b) + { + return pair(a, b); + } +} diff --git a/biscuit/cxx/vector b/biscuit/cxx/vector new file mode 100644 index 0000000..f5370c6 --- /dev/null +++ b/biscuit/cxx/vector @@ -0,0 +1,291 @@ +// -*- c++ -*- +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace std { + template + class vector + { + T *data_; + std::size_t size_, cap_; + + public: + typedef T value_type; + typedef value_type& reference; + typedef const value_type& const_reference; + typedef T* iterator; + typedef const T* const_iterator; + typedef std::size_t size_type; + typedef std::size_t difference_type; + + vector() + : data_(), size_(0), cap_(0) { } + + template + vector(InputIterator first, InputIterator last) : vector() + { + for (; first != last; ++first) + push_back(*first); + } + + vector(const vector &x) : vector() + { + *this = x; + } + + vector(vector &&x) : data_(x.data_), size_(x.size_), cap_(x.cap_) + { + x.data_ = nullptr; + x.size_ = x.cap_ = 0; + } + + vector(std::initializer_list elts) : vector() + { + reserve(elts.size()); + for (auto it = elts.begin(), last = elts.end(); it != last; ++it) + push_back(*it); + } + + ~vector() + { + clear(); + if (data_) + delete[] reinterpret_cast(data_); + } + + vector& operator=(const vector &x) + { + if (&x != this) { + reserve(x.size()); + for (auto &elt : x) + push_back(elt); + } + return *this; + } + + vector& operator=(vector &&x) + { + clear(); + x.swap(*this); + return *this; + } + + iterator begin() noexcept + { + return data(); + } + + const_iterator begin() const noexcept + { + return data(); + } + + iterator end() noexcept + { + return data() + size_; + } + + const_iterator end() const noexcept + { + return data() + size_; + } + + const_iterator cbegin() const noexcept + { + return data(); + } + + const_iterator cend() const noexcept + { + return data() + size_; + } + + size_type size() const + { + return size_; + } + + size_type max_size() const noexcept + { + return (size_type)-1; + } + + size_type capacity() const noexcept + { + return cap_; + } + + bool empty() const noexcept + { + return size_ == 0; + } + + void reserve(size_type n) + { + if (n <= cap_) + return; + + size_type ncap = cap_ == 0 ? 1 : cap_; + while (ncap < n) + ncap *= 2; + auto ndata = reinterpret_cast(new char[sizeof(T) * ncap]); + for (size_type i = 0; i < size_; ++i) + new (&ndata[i]) T(std::move(data()[i])); + data_ = ndata; + cap_ = ncap; + } + + reference operator[](size_type n) + { + return *(data() + n); + } + + const_reference operator[](size_type n) const + { + return *(data() + n); + } + + const_reference at(size_type n) const + { + if (n >= size_) + throw std::out_of_range("index exceeds size"); + return *(data() + n); + } + + reference at(size_type n) + { + if (n >= size_) + throw std::out_of_range("index exceeds size"); + return *(data() + n); + } + + reference front() + { + return *data(); + } + + const_reference front() const + { + return *data(); + } + + reference back() + { + return *(data() + size_ - 1); + } + + const_reference back() const + { + return *(data() + size_ - 1); + } + + T* data() noexcept + { + return data_; + } + + const T* data() const noexcept + { + return data_; + } + + template + void emplace_back(Args&&... args) + { + reserve(size_ + 1); + new (end()) T(std::forward(args)...); + ++size_; + } + + void push_back(const T& x) + { + emplace_back(x); + } + + void push_back(T&& x) + { + emplace_back(std::move(x)); + } + + void pop_back() + { + if (!empty()) { + back().~T(); + --size_; + } + } + + template + iterator emplace(const_iterator position, Args&&... args) + { + iterator it = end(); + if (it == position) { + // The general logic is wrong for end emplacement + emplace_back(std::forward(args)...); + } else { + size_type pos = position - front(); + reserve(size_ + 1); + position = front() + pos; + // Construct a new last element + it = end(); + new (it) T(std::move(*(it - 1))); + ++size_; + // Shift all of the elements between position and end + for (--it; it > position; --it) + *it = std::move(*(it - 1)); + // Construct and move-assign new element. + *it = T(std::forward(args)...); + } + return it; + } + + iterator erase(const_iterator position) + { + // Shift everything between position and end down + auto pos = begin() + (position - begin()); + auto nend = std::move(pos + 1, end(), pos); + // Destruct last element + nend->~T(); + --size_; + return pos; + } + + iterator erase(const_iterator first, const_iterator last) + { + // Shift everything down + auto pos = begin() + (first - begin()), dst = pos, + src = begin() + (last - begin()); + if (src != dst) { + auto nend = std::move(src, end(), dst); + // Destruct remaining elements + size_t n = 0; + for (; nend != end(); ++nend, ++n) + nend->~T(); + size_ -= n; + } + return pos; + } + + void swap(vector &x) + { + std::swap(data_, x.data_); + std::swap(size_, x.size_); + std::swap(cap_, x.cap_); + } + + void clear() noexcept + { + for (std::size_t i = 0; i < size_; ++i) + (*this)[i].~T(); + size_ = 0; + } + }; +} diff --git a/biscuit/cxx/vector.hh b/biscuit/cxx/vector.hh new file mode 100644 index 0000000..15d4583 --- /dev/null +++ b/biscuit/cxx/vector.hh @@ -0,0 +1,237 @@ +#pragma once + +#include "max_align.h" + +#include +#include +#include +#include +#include +#include + +/// A statically allocated vector of at most N T's. +template +class static_vector +{ + typename std::aligned_storage::value>::type data_[N]; + std::size_t size_; + +public: + typedef T value_type; + typedef value_type& reference; + typedef const value_type& const_reference; + typedef T* iterator; + typedef const T* const_iterator; + typedef std::size_t size_type; + typedef std::size_t difference_type; + + constexpr static_vector() : data_{}, size_(0) { } + + template + static_vector(InputIterator first, InputIterator last) + : size_(0) + { + for (; first != last; ++first) + push_back(*first); + } + + static_vector(std::initializer_list elts) + : size_(0) + { + for (auto it = elts.begin(), last = elts.end(); it != last; ++it) + push_back(*it); + } + + ~static_vector() + { + clear(); + } + + iterator begin() noexcept + { + return reinterpret_cast(&data_[0]); + } + + const_iterator begin() const noexcept + { + return reinterpret_cast(&data_[0]); + } + + iterator end() noexcept + { + return reinterpret_cast(&data_[size_]); + } + + const_iterator end() const noexcept + { + return reinterpret_cast(&data_[size_]); + } + + const_iterator cbegin() const noexcept + { + return reinterpret_cast(&data_[0]); + } + + const_iterator cend() const noexcept + { + return reinterpret_cast(&data_[size_]); + } + + size_type size() const noexcept + { + return size_; + } + + size_type max_size() const noexcept + { + return N; + } + + size_type capacity() const noexcept + { + return N; + } + + bool empty() const noexcept + { + return size_ == 0; + } + + bool full() const noexcept + { + return size_ == N; + } + + reference operator[](size_type n) + { + return *(begin() + n); + } + + const_reference operator[](size_type n) const + { + return *(begin() + n); + } + + const_reference at(size_type n) const + { + if (n >= size_) + throw std::out_of_range("index exceeds size"); + return *(begin() + n); + } + + reference at(size_type n) + { + if (n >= size_) + throw std::out_of_range("index exceeds size"); + return *(begin() + n); + } + + reference front() + { + return *begin(); + } + + const_reference front() const + { + return *begin(); + } + + reference back() + { + return *(end() - 1); + } + + const_reference back() const + { + return *(end() - 1); + } + + T* data() noexcept + { + return &front(); + } + + const T* data() const noexcept + { + return &front(); + } + + template + void emplace_back(Args&&... args) + { + if (full()) + throw std::out_of_range("static_vector is full"); + new (end()) T(std::forward(args)...); + ++size_; + } + + void push_back(const T& x) + { + emplace_back(x); + } + + void push_back(T&& x) + { + emplace_back(std::move(x)); + } + + void pop_back() + { + if (!empty()) { + back().~T(); + --size_; + } + } + + template + iterator emplace(const_iterator position, Args&&... args) + { + auto it = end(); + if (it == position) { + // The general logic is wrong for end emplacement + emplace_back(std::forward(args)...); + } else { + if (full()) + throw std::out_of_range("static_vector is full"); + // Construct a new last element + new (it) T(std::move(*(it - 1))); + ++size_; + // Shift all of the elements between position and end + for (--it; it > position; --it) + *it = std::move(*(it - 1)); + // Construct and move-assign new element. + *it = T(std::forward(args)...); + } + return it; + } + + iterator insert(const_iterator position, const T& x) + { + return emplace(position, x); + } + + iterator insert(const_iterator position, T&& x) + { + return emplace(position, std::move(x)); + } + + iterator erase(const_iterator position) + { + // Shift everything between position and end down + auto pos = begin() + (position - begin()); + for (auto it = pos; it < end() - 1; ++it) + *it = std::move(*(it + 1)); + // Destruct last element + (end() - 1)->~T(); + --size_; + return pos; + } + + void clear() noexcept + { + for (std::size_t i = 0; i < size_; ++i) + (*this)[i].~T(); + size_ = 0; + } +};