From e963d9c8e3cb3d29e924c344f41975b63a62fef5 Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Fri, 13 Aug 2021 11:18:25 -0700 Subject: [PATCH] Add cpu / mem / fsz limits to build system Thanks to all the refactorings we now have the ability to enforce reasonable limitations on the amount of resources any individual compile or test can consume. Those limits are currently: - `-C 8` seconds of 3.1ghz CPU time - `-M 256mebibytes` of virtual memory - `-F 100megabyte` limit on file size Only one file currently needs to exceed these limits: o/$(MODE)/third_party/python/Objects/unicodeobject.o: \ QUOTA += -C16 # overrides cpu limit to 16 seconds This change introduces a new sizetol() function to LIBC_FMT for parsing byte or bit size strings with Si unit suffixes. Functions like atoi() have been rewritten too. --- build/bootstrap/compile.com | Bin 110592 -> 106496 bytes build/definitions.mk | 3 +- libc/calls/setrlimit.c | 2 +- libc/fmt/atoi.c | 33 +- libc/fmt/atol.c | 32 +- libc/fmt/atoll.c | 13 +- libc/fmt/conv.h | 3 + libc/fmt/fmt.mk | 12 + libc/fmt/sizetol.c | 90 ++++ libc/fmt/strtoimax.c | 103 +--- libc/fmt/strtol.c | 44 +- libc/fmt/strtol.internal.h | 35 ++ libc/fmt/strtoll.c | 29 +- libc/fmt/strtoul.c | 42 +- libc/fmt/strtoull.c | 29 +- libc/fmt/strtoumax.c | 65 +-- libc/fmt/wcstoimax.c | 77 ++- libc/fmt/wcstol.c | 36 +- .../strtoumax_test.c => libc/fmt/wcstoul.c | 62 ++- libc/fmt/wcstoumax.c | 55 +++ libc/log/appendresourcereport.c | 81 +++ libc/log/cancolor.c | 3 +- libc/log/checkaligned.c | 2 +- libc/log/checkfail.c | 16 +- libc/log/checkfail_ndebug.c | 2 +- libc/log/getsicodename.c | 154 ++++++ libc/log/internal.h | 4 +- libc/log/log.h | 3 + libc/log/malloc_stats.c | 6 +- libc/log/meminfo.c | 4 +- libc/log/oncrash.c | 54 +- libc/log/oncrashthunks.S | 4 + libc/log/showcrashreports.c | 8 +- libc/sysv/consts.sh | 8 +- libc/x/xsigaction.c | 1 + test/libc/calls/setrlimit_test.c | 291 +++++++++++ test/libc/calls/test.mk | 2 + test/libc/fmt/atoi_test.c | 463 ++++++++++++++++++ test/libc/fmt/sizetol_test.c | 53 ++ test/libc/fmt/strtoimax_test.c | 87 ---- test/libc/nexgen32e/kbase36_test.c | 37 ++ .../mbedtls/test/test_suite_asn1parse.c | 2 + third_party/python/python.mk | 16 +- tool/build/calculator.c | 19 +- tool/build/calculator.ctest | 2 +- tool/build/compile.c | 100 +++- tool/build/runit.c | 94 ++-- tool/net/redbean.c | 54 -- tool/viz/cpuid.c | 20 +- 49 files changed, 1802 insertions(+), 553 deletions(-) create mode 100644 libc/fmt/sizetol.c create mode 100644 libc/fmt/strtol.internal.h rename test/libc/fmt/strtoumax_test.c => libc/fmt/wcstoul.c (63%) create mode 100644 libc/fmt/wcstoumax.c create mode 100644 libc/log/appendresourcereport.c create mode 100644 libc/log/getsicodename.c create mode 100644 test/libc/calls/setrlimit_test.c create mode 100644 test/libc/fmt/atoi_test.c create mode 100644 test/libc/fmt/sizetol_test.c delete mode 100644 test/libc/fmt/strtoimax_test.c create mode 100644 test/libc/nexgen32e/kbase36_test.c diff --git a/build/bootstrap/compile.com b/build/bootstrap/compile.com index 48e4822ecead3a744e578e9242814c987f6cc193..8ca98c4da7a7e9827da3da63d28755a2803d8549 100755 GIT binary patch delta 29708 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 diff --git a/build/definitions.mk b/build/definitions.mk index 7d94b887f..82fdb1d10 100644 --- a/build/definitions.mk +++ b/build/definitions.mk @@ -77,7 +77,8 @@ PWD := $(shell pwd) IMAGE_BASE_VIRTUAL ?= 0x400000 HELLO := $(shell build/hello) TMPDIR := $(shell build/findtmp) -COMPILE := $(shell build/getcompile) -V$(shell build/getccversion $(CC)) +SPAWNER := $(shell build/getcompile) -V$(shell build/getccversion $(CC)) +COMPILE = $(SPAWNER) $(QUOTA) export ADDR2LINE export LC_ALL diff --git a/libc/calls/setrlimit.c b/libc/calls/setrlimit.c index fd219839b..4c4a67c24 100644 --- a/libc/calls/setrlimit.c +++ b/libc/calls/setrlimit.c @@ -18,7 +18,6 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" #include "libc/calls/internal.h" -#include "libc/dce.h" #include "libc/sysv/errfuns.h" /** @@ -28,6 +27,7 @@ * @param rlim specifies new resource limit * @return 0 on success or -1 w/ errno * @see libc/sysv/consts.sh + * @vforksafe */ int setrlimit(int resource, const struct rlimit *rlim) { if (resource == 127) return einval(); diff --git a/libc/fmt/atoi.c b/libc/fmt/atoi.c index caf1a2ca9..d7392c132 100644 --- a/libc/fmt/atoi.c +++ b/libc/fmt/atoi.c @@ -16,21 +16,34 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/errno.h" #include "libc/fmt/conv.h" #include "libc/limits.h" +#include "libc/str/str.h" /** - * Decodes decimal number from ASCII string. + * Decodes decimal integer from ASCII string. * - * @param s is a non-null NUL-terminated string - * @return the decoded signed saturated number - * @note calling strtoimax() directly with base 0 permits greater - * flexibility in terms of inputs + * @param s is a non-null nul-terminated string + * @return the decoded signed saturated integer */ int atoi(const char *s) { - int res; - res = strtoimax(s, NULL, 10); - if (res < INT_MIN) return INT_MIN; - if (res > INT_MAX) return INT_MAX; - return res; + int x, c, d; + do { + c = *s++; + } while (c == ' ' || c == '\t'); + d = c == '-' ? -1 : 1; + if (c == '-' || c == '+') c = *s++; + for (x = 0; isdigit(c); c = *s++) { + if (__builtin_mul_overflow(x, 10, &x) || + __builtin_add_overflow(x, (c - '0') * d, &x)) { + errno = ERANGE; + if (d > 0) { + return INT_MAX; + } else { + return INT_MIN; + } + } + } + return x; } diff --git a/libc/fmt/atol.c b/libc/fmt/atol.c index 2d07474ed..da13f1484 100644 --- a/libc/fmt/atol.c +++ b/libc/fmt/atol.c @@ -16,13 +16,35 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/errno.h" #include "libc/fmt/conv.h" #include "libc/limits.h" +#include "libc/str/str.h" +/** + * Decodes decimal integer from ASCII string. + * + * @param s is a non-null nul-terminated string + * @return the decoded signed saturated integer + */ long atol(const char *s) { - long res; - res = strtoimax(s, NULL, 10); - if (res < LONG_MIN) return LONG_MIN; - if (res > LONG_MAX) return LONG_MAX; - return res; + long x; + int c, d; + do { + c = *s++; + } while (c == ' ' || c == '\t'); + d = c == '-' ? -1 : 1; + if (c == '-' || c == '+') c = *s++; + for (x = 0; isdigit(c); c = *s++) { + if (__builtin_mul_overflow(x, 10, &x) || + __builtin_add_overflow(x, (c - '0') * d, &x)) { + errno = ERANGE; + if (d > 0) { + return LONG_MAX; + } else { + return LONG_MIN; + } + } + } + return x; } diff --git a/libc/fmt/atoll.c b/libc/fmt/atoll.c index 33b9363f8..11b25e768 100644 --- a/libc/fmt/atoll.c +++ b/libc/fmt/atoll.c @@ -19,10 +19,13 @@ #include "libc/fmt/conv.h" #include "libc/limits.h" +/** + * Decodes decimal number from ASCII string. + * + * @param s is a non-null nul-terminated string + * @return the decoded signed saturated integer + */ long long atoll(const char *s) { - long long res; - res = strtoimax(s, NULL, 10); - if (res < LONG_LONG_MIN) return LONG_LONG_MIN; - if (res > LONG_LONG_MAX) return LONG_LONG_MAX; - return res; + _Static_assert(LONG_MAX == LONG_LONG_MAX, "need atoll impl"); + return atol(s); } diff --git a/libc/fmt/conv.h b/libc/fmt/conv.h index a4e3894b0..7b4f47d8f 100644 --- a/libc/fmt/conv.h +++ b/libc/fmt/conv.h @@ -29,8 +29,11 @@ intmax_t div10(intmax_t, unsigned *) hidden; intmax_t strtoimax(const char *, char **, int) paramsnonnull((1)); uintmax_t strtoumax(const char *, char **, int) paramsnonnull((1)); intmax_t wcstoimax(const wchar_t *, wchar_t **, int); +uintmax_t wcstoumax(const wchar_t *, wchar_t **, int); long wcstol(const wchar_t *, wchar_t **, int); +unsigned long wcstoul(const wchar_t *, wchar_t **, int); long strtol(const char *, char **, int) paramsnonnull((1)) libcesque; +long sizetol(const char *, long) paramsnonnull() libcesque; /*───────────────────────────────────────────────────────────────────────────│─╗ │ cosmopolitan § conversion » time ─╬─│┼ diff --git a/libc/fmt/fmt.mk b/libc/fmt/fmt.mk index e73cc9299..221f6367c 100644 --- a/libc/fmt/fmt.mk +++ b/libc/fmt/fmt.mk @@ -68,6 +68,18 @@ o/$(MODE)/libc/fmt/filetimetotimeval.o: \ OVERRIDE_CFLAGS += \ -O3 +o/$(MODE)/libc/fmt/atoi.o \ +o/$(MODE)/libc/fmt/strtol.o \ +o/$(MODE)/libc/fmt/strtoul.o \ +o/$(MODE)/libc/fmt/wcstol.o \ +o/$(MODE)/libc/fmt/wcstoul.o \ +o/$(MODE)/libc/fmt/strtoimax.o \ +o/$(MODE)/libc/fmt/strtoumax.o \ +o/$(MODE)/libc/fmt/wcstoimax.o \ +o/$(MODE)/libc/fmt/wcstoumax.o: \ + OVERRIDE_CFLAGS += \ + -Os + LIBC_FMT_LIBS = $(foreach x,$(LIBC_FMT_ARTIFACTS),$($(x))) LIBC_FMT_SRCS = $(foreach x,$(LIBC_FMT_ARTIFACTS),$($(x)_SRCS)) LIBC_FMT_HDRS = $(foreach x,$(LIBC_FMT_ARTIFACTS),$($(x)_HDRS)) diff --git a/libc/fmt/sizetol.c b/libc/fmt/sizetol.c new file mode 100644 index 000000000..10ef06b8e --- /dev/null +++ b/libc/fmt/sizetol.c @@ -0,0 +1,90 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2021 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/fmt/conv.h" +#include "libc/fmt/fmt.h" + +static int GetExponent(int c) { + switch (c) { + case '\0': + case ' ': + case '\t': + return 0; + case 'k': + case 'K': + return 1; + case 'm': + case 'M': + return 2; + case 'g': + case 'G': + return 3; + case 't': + case 'T': + return 4; + case 'p': + case 'P': + return 5; + case 'e': + case 'E': + return 6; + default: + return -1; + } +} + +/** + * Converts size string to long. + * + * The following unit suffixes may be used + * + * - `k` or `K` for kilo (multiply by 𝑏¹) + * - `m` or `M` for mega (multiply by 𝑏²) + * - `g` or `G` for giga (multiply by 𝑏³) + * - `t` or `T` for tera (multiply by 𝑏⁴) + * - `p` or `P` for peta (multiply by 𝑏⁵) + * - `e` or `E` for exa (multiply by 𝑏⁶) + * + * If a permitted alpha character is supplied, then any additional + * characters after it (e.g. kbit, Mibit, TiB) are ignored. Spaces + * before the integer are ignored, and overflows will be detected. + * + * @param s is non-null nul-terminated input string + * @param b is multiplier which should be 1000 or 1024 + * @return size greater than or equal 0 or -1 on error + */ +long sizetol(const char *s, long b) { + long x; + int c, e; + do { + c = *s++; + } while (c == ' ' || c == '\t'); + if (!isdigit(c)) return -1; + x = 0; + do { + if (__builtin_mul_overflow(x, 10, &x) || + __builtin_add_overflow(x, c - '0', &x)) { + return -1; + } + } while (isdigit((c = *s++))); + if ((e = GetExponent(c)) == -1) return -1; + while (e--) { + if (__builtin_mul_overflow(x, b, &x)) return -1; + } + return x; +} diff --git a/libc/fmt/strtoimax.c b/libc/fmt/strtoimax.c index 11a81ba2d..25dbfffb3 100644 --- a/libc/fmt/strtoimax.c +++ b/libc/fmt/strtoimax.c @@ -16,103 +16,42 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/fmt/conv.h" #include "libc/errno.h" +#include "libc/fmt/conv.h" +#include "libc/fmt/strtol.internal.h" #include "libc/limits.h" -#include "libc/nexgen32e/bsr.h" #include "libc/str/str.h" /** * Decodes 128-bit signed integer from ASCII string. * - * @param s is a non-NULL NUL-terminated string - * @param endptr if non-NULL will always receive a pointer to the char + * @param s is a non-null nul-terminated string + * @param endptr if non-null will always receive a pointer to the char * following the last one this function processed, which is usually * the NUL byte, or in the case of invalid strings, would point to * the first invalid character * @param base can be anywhere between [2,36] or 0 to auto-detect based * on the the prefixes 0 (octal), 0x (hexadecimal), 0b (binary), or - * decimal (base 10) by default. - * @return the decoded saturated number - * @see strtoumax + * decimal (base 10) by default + * @return decoded saturated integer + * @see strtoumax() */ intmax_t strtoimax(const char *s, char **endptr, int base) { - bool neg; - uintmax_t x; - intmax_t res; - unsigned diglet, bits; - - x = 0; - bits = 0; - neg = false; - - while (isspace(*s)) { - s++; - } - - switch (*s) { - case '-': - neg = true; - /* 𝑠𝑙𝑖𝑑𝑒 */ - case '+': - s++; - break; - default: - break; - } - - if (!(2 <= base && base <= 36)) { - if (*s == '0') { - s++; - if (*s == 'x' || *s == 'X') { - s++; - base = 16; - } else if (*s == 'b' || *s == 'B') { - s++; - base = 2; - } else { - base = 8; + int d, c = *s; + intmax_t x = 0; + CONSUME_SPACES(s, c); + GET_SIGN(s, c, d); + GET_RADIX(s, c, base); + if ((c = kBase36[c & 255]) && --c < base) { + do { + if (__builtin_mul_overflow(x, base, &x) || + __builtin_add_overflow(x, c * d, &x)) { + x = d > 0 ? INTMAX_MAX : INTMAX_MIN; + errno = ERANGE; + break; } - } else { - base = 10; - } - } else if (*s == '0') { - ++s; - if (base == 2 && (*s == 'b' || *s == 'B')) ++s; - if (base == 16 && (*s == 'x' || *s == 'X')) ++s; + } while ((c = kBase36[*++s & 255]) && --c < base); } - - for (;;) { - diglet = kBase36[*s & 0xff]; - if (!diglet || diglet > base) break; - diglet -= 1; - if (!x || (bits = (diglet ? bsr(diglet) : 0) + bsrmax(x * base)) < 127) { - s++; - x *= base; - x += diglet; - } else if (neg) { - if (bits == 127) { - x *= base; - x += diglet; - if (x == INTMAX_MIN) s++; - } - x = INTMAX_MIN; - errno = ERANGE; - break; - } else { - x = INTMAX_MAX; - errno = ERANGE; - break; - } - } - if (endptr) *endptr = s; - - if (neg) { - res = -x; - } else { - res = x; - } - - return res; + return x; } diff --git a/libc/fmt/strtol.c b/libc/fmt/strtol.c index f0892fbfa..9e28c99ef 100644 --- a/libc/fmt/strtol.c +++ b/libc/fmt/strtol.c @@ -16,25 +16,41 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/fmt/conv.h" #include "libc/errno.h" +#include "libc/fmt/conv.h" +#include "libc/fmt/strtol.internal.h" #include "libc/limits.h" +#include "libc/str/str.h" /** - * Converts string to number. + * Decodes signed integer from ASCII string. * - * @param optional_base is recommended as 0 for flexidecimal + * @param s is a non-null nul-terminated string + * @param endptr if non-null will always receive a pointer to the char + * following the last one this function processed, which is usually + * the NUL byte, or in the case of invalid strings, would point to + * the first invalid character + * @param base can be anywhere between [2,36] or 0 to auto-detect based + * on the the prefixes 0 (octal), 0x (hexadecimal), 0b (binary), or + * decimal (base 10) by default + * @return the decoded signed saturated number */ -long strtol(const char *s, char **opt_out_end, int optional_base) { - intmax_t res; - res = strtoimax(s, opt_out_end, optional_base); - if (res < LONG_MIN) { - errno = ERANGE; - return LONG_MIN; +long strtol(const char *s, char **endptr, int base) { + long x = 0; + int d, c = *s; + CONSUME_SPACES(s, c); + GET_SIGN(s, c, d); + GET_RADIX(s, c, base); + if ((c = kBase36[c & 255]) && --c < base) { + do { + if (__builtin_mul_overflow(x, base, &x) || + __builtin_add_overflow(x, c * d, &x)) { + x = d > 0 ? LONG_MAX : LONG_MIN; + errno = ERANGE; + break; + } + } while ((c = kBase36[*++s & 255]) && --c < base); } - if (res > LONG_MAX) { - errno = ERANGE; - return LONG_MAX; - } - return res; + if (endptr) *endptr = s; + return x; } diff --git a/libc/fmt/strtol.internal.h b/libc/fmt/strtol.internal.h new file mode 100644 index 000000000..0ffae2c93 --- /dev/null +++ b/libc/fmt/strtol.internal.h @@ -0,0 +1,35 @@ +#ifndef COSMOPOLITAN_LIBC_FMT_STRTOL_H_ +#define COSMOPOLITAN_LIBC_FMT_STRTOL_H_ + +#define CONSUME_SPACES(s, c) \ + while (c == ' ' || c == '\t') c = *++s + +#define GET_SIGN(s, c, d) \ + d = c == '-' ? -1 : 1; \ + if (c == '-' || c == '+') c = *++s + +#define GET_RADIX(s, c, r) \ + if (!(2 <= r && r <= 36)) { \ + if (c == '0') { \ + c = *++s; \ + if (c == 'x' || c == 'X') { \ + c = *++s; \ + r = 16; \ + } else if (c == 'b' || c == 'B') { \ + c = *++s; \ + r = 2; \ + } else { \ + r = 8; \ + } \ + } else { \ + r = 10; \ + } \ + } else if (c == '0') { \ + c = *++s; \ + if ((r == 2 && (c == 'b' || c == 'B')) || \ + (r == 16 && (c == 'x' || c == 'X'))) { \ + c = *++s; \ + } \ + } + +#endif /* COSMOPOLITAN_LIBC_FMT_STRTOL_H_ */ diff --git a/libc/fmt/strtoll.c b/libc/fmt/strtoll.c index 2ed524c15..31d311b2c 100644 --- a/libc/fmt/strtoll.c +++ b/libc/fmt/strtoll.c @@ -17,19 +17,22 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/fmt/conv.h" -#include "libc/errno.h" #include "libc/limits.h" -long long strtoll(const char *s, char **endptr, int optional_base) { - intmax_t res; - res = strtoimax(s, endptr, optional_base); - if (res < LONG_LONG_MIN) { - errno = ERANGE; - return LONG_LONG_MIN; - } - if (res > LONG_LONG_MAX) { - errno = ERANGE; - return LONG_LONG_MAX; - } - return res; +/** + * Decodes signed integer from ASCII string. + * + * @param s is a non-null nul-terminated string + * @param endptr if non-null will always receive a pointer to the char + * following the last one this function processed, which is usually + * the NUL byte, or in the case of invalid strings, would point to + * the first invalid character + * @param base can be anywhere between [2,36] or 0 to auto-detect based + * on the the prefixes 0 (octal), 0x (hexadecimal), 0b (binary), or + * decimal (base 10) by default + * @return decoded signed saturated integer + */ +long long strtoll(const char *s, char **endptr, int base) { + _Static_assert(LONG_MAX == LONG_LONG_MAX, "need strtoll impl"); + return strtoll(s, endptr, base); } diff --git a/libc/fmt/strtoul.c b/libc/fmt/strtoul.c index 1e9ef3a41..3adb34ad0 100644 --- a/libc/fmt/strtoul.c +++ b/libc/fmt/strtoul.c @@ -16,20 +16,36 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/fmt/conv.h" #include "libc/errno.h" -#include "libc/limits.h" +#include "libc/fmt/conv.h" +#include "libc/fmt/strtol.internal.h" +#include "libc/str/str.h" -unsigned long strtoul(const char *s, char **endptr, int optional_base) { - intmax_t res; - res = strtoimax(s, endptr, optional_base); - if (res < ULONG_MIN) { - errno = ERANGE; - return ULONG_MIN; +/** + * Decodes unsigned integer from ASCII string. + * + * @param s is a non-null nul-terminated string + * @param endptr if non-null will always receive a pointer to the char + * following the last one this function processed, which is usually + * the NUL byte, or in the case of invalid strings, would point to + * the first invalid character + * @param base can be anywhere between [2,36] or 0 to auto-detect based + * on the the prefixes 0 (octal), 0x (hexadecimal), 0b (binary), or + * decimal (base 10) by default + * @return decoded integer mod 2⁶⁴ negated if leading `-` + */ +unsigned long strtoul(const char *s, char **endptr, int base) { + int d, c = *s; + unsigned long x = 0; + CONSUME_SPACES(s, c); + GET_SIGN(s, c, d); + GET_RADIX(s, c, base); + if ((c = kBase36[c & 255]) && --c < base) { + do { + x *= base; + x += c; + } while ((c = kBase36[*++s & 255]) && --c < base); } - if (res > ULONG_MAX) { - errno = ERANGE; - return ULONG_MAX; - } - return res; + if (endptr) *endptr = s; + return d > 0 ? x : -x; } diff --git a/libc/fmt/strtoull.c b/libc/fmt/strtoull.c index 94d4c3071..4d2721e89 100644 --- a/libc/fmt/strtoull.c +++ b/libc/fmt/strtoull.c @@ -17,19 +17,22 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/fmt/conv.h" -#include "libc/errno.h" #include "libc/limits.h" -unsigned long long strtoull(const char *s, char **endptr, int optional_base) { - intmax_t res; - res = strtoimax(s, endptr, optional_base); - if (res < ULONG_LONG_MIN) { - errno = ERANGE; - return ULONG_LONG_MIN; - } - if (res > ULONG_LONG_MAX) { - errno = ERANGE; - return ULONG_LONG_MAX; - } - return res; +/** + * Decodes unsigned integer from ASCII string. + * + * @param s is a non-null nul-terminated string + * @param endptr if non-null will always receive a pointer to the char + * following the last one this function processed, which is usually + * the NUL byte, or in the case of invalid strings, would point to + * the first invalid character + * @param base can be anywhere between [2,36] or 0 to auto-detect based + * on the the prefixes 0 (octal), 0x (hexadecimal), 0b (binary), or + * decimal (base 10) by default + * @return decoded integer mod 2⁶⁴ negated if leading `-` + */ +unsigned long long strtoull(const char *s, char **endptr, int base) { + _Static_assert(LONG_MAX == LONG_LONG_MAX, "need strtoull impl"); + return strtoul(s, endptr, base); } diff --git a/libc/fmt/strtoumax.c b/libc/fmt/strtoumax.c index e93ef95c4..e2b937515 100644 --- a/libc/fmt/strtoumax.c +++ b/libc/fmt/strtoumax.c @@ -17,54 +17,35 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/fmt/conv.h" +#include "libc/fmt/strtol.internal.h" #include "libc/str/str.h" /** * Decodes 128-bit unsigned integer from ASCII string. * - * This is a more restricted form of strtoimax() that's useful for folks - * needing to decode numbers in the range [1^127, 1^128). + * @param s is a non-null nul-terminated string + * @param endptr if non-null will always receive a pointer to the char + * following the last one this function processed, which is usually + * the NUL byte, or in the case of invalid strings, would point to + * the first invalid character + * @param base can be anywhere between [2,36] or 0 to auto-detect based + * on the the prefixes 0 (octal), 0x (hexadecimal), 0b (binary), or + * decimal (base 10) by default + * @return decoded integer mod 2¹²⁸ negated if leading `-` + * @see strtoimax() */ uintmax_t strtoumax(const char *s, char **endptr, int base) { - const unsigned char *p = (const unsigned char *)s; - unsigned diglet; - uintmax_t res; - - res = 0; - - while (isspace(*p)) { - p++; + int d, c = *s; + uintmax_t x = 0; + CONSUME_SPACES(s, c); + GET_SIGN(s, c, d); + GET_RADIX(s, c, base); + if ((c = kBase36[c & 255]) && --c < base) { + do { + x *= base; + x += c; + } while ((c = kBase36[*++s & 255]) && --c < base); } - - if (!base) { - if (*p == '0') { - p++; - if (*p == 'x' || *p == 'X') { - p++; - base = 16; - } else if (*p == 'b' || *p == 'B') { - p++; - base = 2; - } else { - base = 8; - } - } else { - base = 10; - } - } else if (*p == '0') { - ++p; - if (base == 2 && (*p == 'b' || *p == 'B')) ++p; - if (base == 16 && (*p == 'x' || *p == 'X')) ++p; - } - - for (;;) { - diglet = kBase36[*p]; - if (!diglet || diglet > base) break; - p++; - res *= base; - res += diglet - 1; - } - - if (endptr) *endptr = (char *)p; - return res; + if (endptr) *endptr = s; + return d > 0 ? x : -x; } diff --git a/libc/fmt/wcstoimax.c b/libc/fmt/wcstoimax.c index 3c2e6a082..5fe859c44 100644 --- a/libc/fmt/wcstoimax.c +++ b/libc/fmt/wcstoimax.c @@ -16,53 +16,46 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/errno.h" #include "libc/fmt/conv.h" +#include "libc/fmt/strtol.internal.h" +#include "libc/limits.h" #include "libc/str/str.h" +/** + * Decodes 128-bit signed integer from wide string. + * + * @param s is a non-null nul-terminated string + * @param endptr if non-null will always receive a pointer to the char + * following the last one this function processed, which is usually + * the NUL byte, or in the case of invalid strings, would point to + * the first invalid character + * @param base can be anywhere between [2,36] or 0 to auto-detect based + * on the the prefixes 0 (octal), 0x (hexadecimal), 0b (binary), or + * decimal (base 10) by default + * @return decoded saturated integer + * @see strtoumax() + */ intmax_t wcstoimax(const wchar_t *s, wchar_t **endptr, int base) { - intmax_t res = 0; - int neg = 0; - - while (iswspace(*s)) { - s++; - } - - switch (*s) { - case '-': - neg = 1; - /* fallthrough */ - case '+': - s++; - break; - default: - break; - } - - if (!base) { - if (*s == '0') { - s++; - if (*s == 'x' || *s == 'X') { - s++; - base = 16; - } else if (*s == 'b' || *s == 'B') { - s++; - base = 2; - } else { - base = 8; + int c, d; + intmax_t x; + c = *s; + CONSUME_SPACES(s, c); + GET_SIGN(s, c, d); + GET_RADIX(s, c, base); + x = 0; + if ((c = kBase36[c & 255]) && --c < base) { + do { + if (__builtin_mul_overflow(x, base, &x) || + __builtin_add_overflow(x, c * d, &x)) { + x = d > 0 ? INTMAX_MAX : INTMAX_MIN; + errno = ERANGE; + break; } - } else { - base = 10; - } + } while ((c = kBase36[*++s & 255]) && --c < base); } - - for (;;) { - unsigned diglet = kBase36[*s]; - if (!diglet || diglet > base) break; - s++; - res *= base; /* needs __muloti4() w/ clang */ - res -= diglet - 1; + if (endptr) { + *endptr = s; } - - if (endptr) *endptr = (wchar_t *)s; - return neg ? res : -res; + return x; } diff --git a/libc/fmt/wcstol.c b/libc/fmt/wcstol.c index 0530dfe34..6b2abe007 100644 --- a/libc/fmt/wcstol.c +++ b/libc/fmt/wcstol.c @@ -16,9 +16,41 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/errno.h" #include "libc/fmt/conv.h" +#include "libc/fmt/strtol.internal.h" +#include "libc/limits.h" #include "libc/str/str.h" -long wcstol(const wchar_t *s, wchar_t **end, int opt_base) { - return wcstoimax(s, end, opt_base); +/** + * Decodes signed integer from wide string. + * + * @param s is a non-null nul-terminated string + * @param endptr if non-null will always receive a pointer to the char + * following the last one this function processed, which is usually + * the NUL byte, or in the case of invalid strings, would point to + * the first invalid character + * @param base can be anywhere between [2,36] or 0 to auto-detect based + * on the the prefixes 0 (octal), 0x (hexadecimal), 0b (binary), or + * decimal (base 10) by default + * @return the decoded signed saturated number + */ +long wcstol(const wchar_t *s, wchar_t **endptr, int base) { + long x = 0; + int d, c = *s; + CONSUME_SPACES(s, c); + GET_SIGN(s, c, d); + GET_RADIX(s, c, base); + if ((c = kBase36[c & 255]) && --c < base) { + do { + if (__builtin_mul_overflow(x, base, &x) || + __builtin_add_overflow(x, c * d, &x)) { + x = d > 0 ? LONG_MAX : LONG_MIN; + errno = ERANGE; + break; + } + } while ((c = kBase36[*++s & 255]) && --c < base); + } + if (endptr) *endptr = s; + return x; } diff --git a/test/libc/fmt/strtoumax_test.c b/libc/fmt/wcstoul.c similarity index 63% rename from test/libc/fmt/strtoumax_test.c rename to libc/fmt/wcstoul.c index 010510ddd..25bf684aa 100644 --- a/test/libc/fmt/strtoumax_test.c +++ b/libc/fmt/wcstoul.c @@ -16,38 +16,36 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/bits/bits.h" +#include "libc/errno.h" #include "libc/fmt/conv.h" -#include "libc/limits.h" -#include "libc/testlib/testlib.h" +#include "libc/fmt/strtol.internal.h" +#include "libc/str/str.h" -TEST(strtoumax, testZero) { - EXPECT_EQ(UINTMAX_MIN, strtoumax("0", NULL, 0)); -} -TEST(strtoumax, testDecimal) { - EXPECT_EQ(123, strtoumax("123", NULL, 0)); -} -TEST(strtoumax, testHex) { - EXPECT_EQ(255, strtoumax("0xff", NULL, 0)); - EXPECT_EQ(255, strtoumax("0xff", NULL, 16)); -} -TEST(strtoumax, testOctal) { - EXPECT_EQ(123, strtoumax("0173", NULL, 0)); - EXPECT_EQ(123, strtoumax("0173", NULL, 8)); -} -TEST(strtoumax, testBinary) { - EXPECT_EQ(42, strtoumax("0b101010", NULL, 0)); - EXPECT_EQ(42, strtoumax("0b101010", NULL, 2)); -} - -TEST(strtoumax, testMaximum) { - EXPECT_EQ(UINTMAX_MAX, - strtoumax("340282366920938463463374607431768211455", NULL, 0)); - EXPECT_EQ(UINTMAX_MAX, - strtoumax("0xffffffffffffffffffffffffffffffff", NULL, 0)); -} - -TEST(strtoumax, testTwosBane) { - EXPECT_EQ(((uintmax_t)0x8000000000000000) << 64 | 0x0000000000000000, - strtoumax("0x80000000000000000000000000000000", NULL, 0)); +/** + * Decodes unsigned integer from wide string. + * + * @param s is a non-null nul-terminated string + * @param endptr if non-null will always receive a pointer to the char + * following the last one this function processed, which is usually + * the NUL byte, or in the case of invalid strings, would point to + * the first invalid character + * @param base can be anywhere between [2,36] or 0 to auto-detect based + * on the the prefixes 0 (octal), 0x (hexadecimal), 0b (binary), or + * decimal (base 10) by default + * @return decoded integer mod 2⁶⁴ negated if leading `-` + */ +unsigned long wcstoul(const wchar_t *s, wchar_t **endptr, int base) { + int d, c = *s; + unsigned long x = 0; + CONSUME_SPACES(s, c); + GET_SIGN(s, c, d); + GET_RADIX(s, c, base); + if ((c = kBase36[c & 255]) && --c < base) { + do { + x *= base; + x += c; + } while ((c = kBase36[*++s & 255]) && --c < base); + } + if (endptr) *endptr = s; + return d > 0 ? x : -x; } diff --git a/libc/fmt/wcstoumax.c b/libc/fmt/wcstoumax.c new file mode 100644 index 000000000..38646d9a7 --- /dev/null +++ b/libc/fmt/wcstoumax.c @@ -0,0 +1,55 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/fmt/conv.h" +#include "libc/fmt/strtol.internal.h" +#include "libc/str/str.h" + +/** + * Decodes 128-bit unsigned integer from wide string. + * + * @param s is a non-null nul-terminated string + * @param endptr if non-null will always receive a pointer to the char + * following the last one this function processed, which is usually + * the NUL byte, or in the case of invalid strings, would point to + * the first invalid character + * @param base can be anywhere between [2,36] or 0 to auto-detect based + * on the the prefixes 0 (octal), 0x (hexadecimal), 0b (binary), or + * decimal (base 10) by default + * @return decoded integer mod 2¹²⁸ negated if leading `-` + * @see strtoimax() + */ +uintmax_t wcstoumax(const wchar_t *s, wchar_t **endptr, int base) { + int c, d; + uintmax_t x; + c = *s; + CONSUME_SPACES(s, c); + GET_SIGN(s, c, d); + GET_RADIX(s, c, base); + x = 0; + if ((c = kBase36[c & 255]) && --c < base) { + do { + x *= base; + x += c; + } while ((c = kBase36[*++s & 255]) && --c < base); + } + if (endptr) { + *endptr = s; + } + return d > 0 ? x : -x; +} diff --git a/libc/log/appendresourcereport.c b/libc/log/appendresourcereport.c new file mode 100644 index 000000000..cade76a54 --- /dev/null +++ b/libc/log/appendresourcereport.c @@ -0,0 +1,81 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2021 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/struct/rusage.h" +#include "libc/log/log.h" +#include "libc/math.h" +#include "libc/runtime/clktck.h" +#include "libc/stdio/append.internal.h" + +/** + * Generates process resource usage report. + */ +void AppendResourceReport(char **b, struct rusage *ru, const char *nl) { + long utime, stime; + long double ticks; + if (ru->ru_maxrss) { + (appendf)(b, "ballooned to %,ldkb in size%s", ru->ru_maxrss, nl); + } + if ((utime = ru->ru_utime.tv_sec * 1000000 + ru->ru_utime.tv_usec) | + (stime = ru->ru_stime.tv_sec * 1000000 + ru->ru_stime.tv_usec)) { + ticks = ceill((long double)(utime + stime) / (1000000.L / CLK_TCK)); + (appendf)(b, "needed %,ldµs cpu (%d%% kernel)%s", utime + stime, + (int)((long double)stime / (utime + stime) * 100), nl); + if (ru->ru_idrss) { + (appendf)(b, "needed %,ldkb memory on average%s", + lroundl(ru->ru_idrss / ticks), nl); + } + if (ru->ru_isrss) { + (appendf)(b, "needed %,ldkb stack on average%s", + lroundl(ru->ru_isrss / ticks), nl); + } + if (ru->ru_ixrss) { + (appendf)(b, "mapped %,ldkb shared on average%s", + lroundl(ru->ru_ixrss / ticks), nl); + } + } + if (ru->ru_minflt || ru->ru_majflt) { + (appendf)(b, "caused %,ld page faults (%d%% memcpy)%s", + ru->ru_minflt + ru->ru_majflt, + (int)((long double)ru->ru_minflt / + (ru->ru_minflt + ru->ru_majflt) * 100), + nl); + } + if (ru->ru_nvcsw + ru->ru_nivcsw > 1) { + (appendf)( + b, "%,ld context switches (%d%% consensual)%s", + ru->ru_nvcsw + ru->ru_nivcsw, + (int)((long double)ru->ru_nvcsw / (ru->ru_nvcsw + ru->ru_nivcsw) * 100), + nl); + } + if (ru->ru_msgrcv || ru->ru_msgsnd) { + (appendf)(b, "received %,ld message%s and sent %,ld%s", ru->ru_msgrcv, + ru->ru_msgrcv == 1 ? "" : "s", ru->ru_msgsnd, nl); + } + if (ru->ru_inblock || ru->ru_oublock) { + (appendf)(b, "performed %,ld read%s and %,ld write i/o operations%s", + ru->ru_inblock, ru->ru_inblock == 1 ? "" : "s", ru->ru_oublock, + nl); + } + if (ru->ru_nsignals) { + (appendf)(b, "received %,ld signals%s", ru->ru_nsignals, nl); + } + if (ru->ru_nswap) { + (appendf)(b, "got swapped %,ld times%s", ru->ru_nswap, nl); + } +} diff --git a/libc/log/cancolor.c b/libc/log/cancolor.c index 6d920897a..51ebd0ef2 100644 --- a/libc/log/cancolor.c +++ b/libc/log/cancolor.c @@ -47,7 +47,8 @@ bool cancolor(void) { static bool once; static bool result; if (!once) { - result = !!strcmp(nulltoempty(getenv("DONTANSIMEBRO")), "1"); + result = !!strcmp(nulltoempty(getenv("DONTANSIMEBRO")), "1") && + !!strcmp(nulltoempty(getenv("TERM")), "dumb"); once = true; } return result; diff --git a/libc/log/checkaligned.c b/libc/log/checkaligned.c index fbd5e355d..66fea2f58 100644 --- a/libc/log/checkaligned.c +++ b/libc/log/checkaligned.c @@ -25,7 +25,7 @@ void __check_fail_aligned(unsigned bytes, uint64_t ptr) { fflush(stderr); if (!IsTiny()) memsummary(fileno(stderr)); - (dprintf)(fileno(stderr), "%s%d%s%#p\r\n", "error: pointer not ", bytes, + (dprintf)(fileno(stderr), "%s%d%s%#p\n", "error: pointer not ", bytes, "-byte aligned: ", ptr); __die(); } diff --git a/libc/log/checkfail.c b/libc/log/checkfail.c index 23aad9a9f..619eb607f 100644 --- a/libc/log/checkfail.c +++ b/libc/log/checkfail.c @@ -53,10 +53,10 @@ relegated void __check_fail(const char *suffix, const char *opstr, gethostname(hostname, sizeof(hostname)); (dprintf)(STDERR_FILENO, - "check failed on %s pid %d\r\n" - "\tCHECK_%s(%s, %s);\r\n" - "\t\t → %#lx (%s)\r\n" - "\t\t%s %#lx (%s)\r\n", + "check failed on %s pid %d\n" + "\tCHECK_%s(%s, %s);\n" + "\t\t → %#lx (%s)\n" + "\t\t%s %#lx (%s)\n", hostname, getpid(), sufbuf, wantstr, gotstr, want, wantstr, opstr, got, gotstr); @@ -65,19 +65,19 @@ relegated void __check_fail(const char *suffix, const char *opstr, va_start(va, fmt); (vdprintf)(STDERR_FILENO, fmt, va); va_end(va); - (dprintf)(STDERR_FILENO, "\r\n"); + (dprintf)(STDERR_FILENO, "\n"); } - (dprintf)(STDERR_FILENO, "\t%s\r\n\t%s%s%s%s\r\n", strerror(lasterr), SUBTLE, + (dprintf)(STDERR_FILENO, "\t%s\n\t%s%s%s%s\n", strerror(lasterr), SUBTLE, getauxval(AT_EXECFN), __argc > 1 ? " \\" : "", RESET); for (i = 1; i < __argc; ++i) { - (dprintf)(STDERR_FILENO, "\t\t%s%s\r\n", __argv[i], + (dprintf)(STDERR_FILENO, "\t\t%s%s\n", __argv[i], i < __argc - 1 ? " \\" : ""); } if (!IsTiny() && lasterr == ENOMEM) { - (dprintf)(STDERR_FILENO, "\r\n"); + (dprintf)(STDERR_FILENO, "\n"); PrintMemoryIntervals(STDERR_FILENO, &_mmi); } diff --git a/libc/log/checkfail_ndebug.c b/libc/log/checkfail_ndebug.c index b232a73e0..a3a1a18c6 100644 --- a/libc/log/checkfail_ndebug.c +++ b/libc/log/checkfail_ndebug.c @@ -46,5 +46,5 @@ relegated void ___check_fail_ndebug(uint64_t want, uint64_t got, __print(bx, uint64toarray_radix16(got, bx)); __print_string(" ("); __print(bx, int64toarray_radix10(lasterr, bx)); - __print_string(")\r\n"); + __print_string(")\n"); } diff --git a/libc/log/getsicodename.c b/libc/log/getsicodename.c new file mode 100644 index 000000000..af4c6a58b --- /dev/null +++ b/libc/log/getsicodename.c @@ -0,0 +1,154 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2021 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/log/log.h" +#include "libc/str/str.h" +#include "libc/sysv/consts/sicode.h" +#include "libc/sysv/consts/sig.h" + +static bool IsSiUser(int si_code) { + if (!IsOpenbsd()) { + return si_code == SI_USER; + } else { + return si_code <= 0; + } +} + +/** + * Returns symbolic name for siginfo::si_code value. + */ +const char *GetSiCodeName(int sig, int si_code) { + static char b[16]; + memset(b, 0, sizeof(b)); + strcpy(b, "SI_???"); + if (si_code == SI_QUEUE) { + strcpy(b + 3, "QUEUE"); /* sent by sigqueue(2) */ + } else if (si_code == SI_TIMER) { + strcpy(b + 3, "TIMER"); /* sent by setitimer(2) or clock_settime(2) */ + } else if (si_code == SI_TKILL) { + strcpy(b + 3, "TKILL"); /* sent by tkill(2), etc. */ + } else if (si_code == SI_MESGQ) { + strcpy(b + 3, "MESGQ"); /* sent by mq_notify(2) */ + } else if (si_code == SI_MESGQ) { + strcpy(b + 3, "MESGQ"); /* aio completion */ + } else if (si_code == SI_ASYNCNL) { + strcpy(b + 3, "ASYNCNL"); /* aio completion for dns; the horror */ + } else if (si_code == SI_NOINFO) { + strcpy(b + 3, "NOINFO"); /* no signal specific info available */ + } else if (si_code == SI_KERNEL) { + strcpy(b + 3, "KERNEL"); /* sent by kernel */ + } else if (IsSiUser(si_code)) { + strcpy(b + 3, "USER"); /* sent by kill(2) i.e. from userspace */ + } else if (sig == SIGCHLD) { + strcpy(b, "CLD_???"); + if (si_code == CLD_EXITED) { + strcpy(b + 4, "EXITED"); /* child exited */ + } else if (si_code == CLD_KILLED) { + strcpy(b + 4, "KILLED"); /* child terminated w/o core */ + } else if (si_code == CLD_DUMPED) { + strcpy(b + 4, "DUMPED"); /* child terminated w/ core */ + } else if (si_code == CLD_TRAPPED) { + strcpy(b + 4, "TRAPPED"); /* traced child trapped */ + } else if (si_code == CLD_STOPPED) { + strcpy(b + 4, "STOPPED"); /* child stopped */ + } else if (si_code == CLD_CONTINUED) { + strcpy(b + 4, "CONTINUED"); /* stopped child continued */ + } + } else if (sig == SIGTRAP) { + strcpy(b, "TRAP_???"); + if (si_code == TRAP_BRKPT) { + strcpy(b + 5, "BRKPT"); /* process breakpoint */ + } else if (si_code == TRAP_TRACE) { + strcpy(b + 5, "TRACE"); /* process trace trap */ + } + } else if (sig == SIGSEGV) { + strcpy(b, "SEGV_???"); + if (si_code == SEGV_MAPERR) { + strcpy(b + 5, "MAPERR"); /* address not mapped to object */ + } else if (si_code == SEGV_ACCERR) { + strcpy(b + 5, "ACCERR"); /* invalid permissions for mapped object */ + } + } else if (sig == SIGFPE) { + strcpy(b, "FPE_???"); + if (si_code == FPE_INTDIV) { + strcpy(b + 4, "INTDIV"); /* integer divide by zero */ + } else if (si_code == FPE_INTOVF) { + strcpy(b + 4, "INTOVF"); /* integer overflow */ + } else if (si_code == FPE_FLTDIV) { + strcpy(b + 4, "FLTDIV"); /* floating point divide by zero */ + } else if (si_code == FPE_FLTOVF) { + strcpy(b + 4, "FLTOVF"); /* floating point overflow */ + } else if (si_code == FPE_FLTUND) { + strcpy(b + 4, "FLTUND"); /* floating point underflow */ + } else if (si_code == FPE_FLTRES) { + strcpy(b + 4, "FLTRES"); /* floating point inexact */ + } else if (si_code == FPE_FLTINV) { + strcpy(b + 4, "FLTINV"); /* invalid floating point operation */ + } else if (si_code == FPE_FLTSUB) { + strcpy(b + 4, "FLTSUB"); /* subscript out of range */ + } + } else if (sig == SIGILL) { + strcpy(b, "ILL_???"); + if (si_code == ILL_ILLOPC) { + strcpy(b + 4, "ILLOPC"); /* illegal opcode */ + } else if (si_code == ILL_ILLOPN) { + strcpy(b + 4, "ILLOPN"); /* illegal operand */ + } else if (si_code == ILL_ILLADR) { + strcpy(b + 4, "ILLADR"); /* illegal addressing mode */ + } else if (si_code == ILL_ILLTRP) { + strcpy(b + 4, "ILLTRP"); /* illegal trap */ + } else if (si_code == ILL_PRVOPC) { + strcpy(b + 4, "PRVOPC"); /* privileged opcode */ + } else if (si_code == ILL_PRVREG) { + strcpy(b + 4, "PRVREG"); /* privileged register */ + } else if (si_code == ILL_COPROC) { + strcpy(b + 4, "COPROC"); /* coprocessor error */ + } else if (si_code == ILL_BADSTK) { + strcpy(b + 4, "BADSTK"); /* internal stack error */ + } + } else if (sig == SIGBUS) { + strcpy(b, "BUS_???"); + if (si_code == BUS_ADRALN) { + strcpy(b + 4, "ADRALN"); /* invalid address alignment */ + } else if (si_code == BUS_ADRERR) { + strcpy(b + 4, "ADRERR"); /* non-existent physical address */ + } else if (si_code == BUS_OBJERR) { + strcpy(b + 4, "OBJERR"); /* object specific hardware error */ + } else if (si_code == BUS_MCEERR_AR) { + strcpy(b + 4, "BUS_AR"); /* Linux 2.6.32+ */ + } else if (si_code == BUS_MCEERR_AO) { + strcpy(b + 4, "BUS_AO"); /* Linux 2.6.32+ */ + } + } else if (sig == SIGIO) { + strcpy(b, "POLL_???"); + if (si_code == POLL_IN) { + strcpy(b + 5, "IN"); /* data input available */ + } else if (si_code == POLL_OUT) { + strcpy(b + 5, "OUT"); /* output buffer available */ + } else if (si_code == POLL_MSG) { + strcpy(b + 5, "MSG"); /* input message available */ + } else if (si_code == POLL_ERR) { + strcpy(b + 5, "ERR"); /* i/o error */ + } else if (si_code == POLL_PRI) { + strcpy(b + 5, "PRI"); /* high priority input available */ + } else if (si_code == POLL_HUP) { + strcpy(b + 5, "HUP"); /* device disconnected */ + } + } + return b; +} diff --git a/libc/log/internal.h b/libc/log/internal.h index 49adbc2f3..eba7db65e 100644 --- a/libc/log/internal.h +++ b/libc/log/internal.h @@ -6,8 +6,8 @@ #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ -extern int kCrashSigs[8]; -extern struct sigaction g_oldcrashacts[8]; +extern int kCrashSigs[8] hidden; +extern struct sigaction g_oldcrashacts[8] hidden; void __start_fatal(const char *, int) hidden; void __start_fatal_ndebug(void) hidden; diff --git a/libc/log/log.h b/libc/log/log.h index a28abc83a..ff1180c3e 100644 --- a/libc/log/log.h +++ b/libc/log/log.h @@ -1,6 +1,7 @@ #ifndef COSMOPOLITAN_LIBC_LOG_LOG_H_ #define COSMOPOLITAN_LIBC_LOG_LOG_H_ #include "libc/bits/likely.h" +#include "libc/calls/struct/rusage.h" #include "libc/calls/struct/sigset.h" #include "libc/calls/struct/winsize.h" #include "libc/nexgen32e/stackframe.h" @@ -52,6 +53,8 @@ void showcrashreports(void); void callexitontermination(struct sigset *); bool32 IsDebuggerPresent(bool); bool IsRunningUnderMake(void); +const char *GetSiCodeName(int, int); +void AppendResourceReport(char **, struct rusage *, const char *); /*───────────────────────────────────────────────────────────────────────────│─╗ │ cosmopolitan § liblog » logging ─╬─│┼ diff --git a/libc/log/malloc_stats.c b/libc/log/malloc_stats.c index 9369fd1ac..f57f049df 100644 --- a/libc/log/malloc_stats.c +++ b/libc/log/malloc_stats.c @@ -22,7 +22,7 @@ void malloc_stats(void) { struct MallocStats res = dlmalloc_stats(g_dlmalloc); - (fprintf)(stderr, "max system bytes = %'10zu\r\n", res.maxfp); - (fprintf)(stderr, "system bytes = %'10zu\r\n", res.fp); - (fprintf)(stderr, "in use bytes = %'10zu\r\n", res.used); + (fprintf)(stderr, "max system bytes = %'10zu\n", res.maxfp); + (fprintf)(stderr, "system bytes = %'10zu\n", res.fp); + (fprintf)(stderr, "in use bytes = %'10zu\n", res.used); } diff --git a/libc/log/meminfo.c b/libc/log/meminfo.c index 95f2bf823..5fbe6bee9 100644 --- a/libc/log/meminfo.c +++ b/libc/log/meminfo.c @@ -22,7 +22,7 @@ #include "libc/mem/mem.h" static void onmemchunk(void *start, void *end, size_t used_bytes, void *arg) { - (dprintf)(*(int *)arg, "%p - %p : %08zx / %08lx\r\n", start, end, used_bytes, + (dprintf)(*(int *)arg, "%p - %p : %08zx / %08lx\n", start, end, used_bytes, (intptr_t)end - (intptr_t)start); } @@ -31,7 +31,7 @@ static void onmemchunk(void *start, void *end, size_t used_bytes, void *arg) { */ void meminfo(int fd) { memsummary(fd); - (dprintf)(fd, "%*s %*s %*s %*s\r\n", POINTER_XDIGITS, "start", + (dprintf)(fd, "%*s %*s %*s %*s\n", POINTER_XDIGITS, "start", POINTER_XDIGITS, "end", 8, "used", 8, "size"); malloc_inspect_all(onmemchunk, &fd); } diff --git a/libc/log/oncrash.c b/libc/log/oncrash.c index cdb3446b1..75f8c3798 100644 --- a/libc/log/oncrash.c +++ b/libc/log/oncrash.c @@ -18,6 +18,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" #include "libc/calls/sigbits.h" +#include "libc/calls/struct/sigaction.h" #include "libc/calls/struct/siginfo.h" #include "libc/calls/struct/utsname.h" #include "libc/calls/ucontext.h" @@ -57,11 +58,21 @@ static const char kGregNames[17][4] forcealign(1) = { static const char kCpuFlags[12] forcealign(1) = "CVPRAKZSTIDO"; static const char kFpuExceptions[6] forcealign(1) = "IDZOUP"; -static const char kCrashSigNames[8][5] forcealign(1) = { - "QUIT", "FPE", "ILL", "SEGV", "TRAP", "ABRT", "BUS", "PIPE"}; -hidden int kCrashSigs[8]; -hidden struct sigaction g_oldcrashacts[8]; +/* : showcrashreports.c, oncrashthunks.S, oncrash.c */ +int kCrashSigs[8]; +struct sigaction g_oldcrashacts[8]; +static const char kCrashSigNames[8][5] forcealign(1) = { + "QUIT", // + "FPE", // + "ILL", // + "SEGV", // + "TRAP", // + "ABRT", // + "BUS", // + "PIPE", // +}; +/* : showcrashreports.c, oncrashthunks.S, oncrash.c */ relegated static const char *TinyStrSignal(int sig) { size_t i; @@ -76,7 +87,7 @@ relegated static const char *TinyStrSignal(int sig) { relegated static void ShowFunctionCalls(int fd, ucontext_t *ctx) { struct StackFrame *bp; struct StackFrame goodframe; - write(fd, "\r\n", 2); + write(fd, "\n", 1); if (ctx && ctx->uc_mcontext.rip && ctx->uc_mcontext.rbp) { goodframe.next = (struct StackFrame *)ctx->uc_mcontext.rbp; goodframe.addr = ctx->uc_mcontext.rip; @@ -121,7 +132,7 @@ relegated static void DescribeCpuFlags(int fd, int flags, int x87sw, relegated static void ShowGeneralRegisters(int fd, ucontext_t *ctx) { size_t i, j, k; long double st; - write(fd, "\r\n", 2); + write(fd, "\n", 1); for (i = 0, j = 0, k = 0; i < ARRAYLEN(kGregNames); ++i) { if (j > 0) write(fd, " ", 1); dprintf(fd, "%-3s %016lx", kGregNames[(unsigned)kGregOrder[i]], @@ -135,7 +146,7 @@ relegated static void ShowGeneralRegisters(int fd, ucontext_t *ctx) { } dprintf(fd, " %s(%zu) %Lg", "ST", k, st); ++k; - write(fd, "\r\n", 2); + write(fd, "\n", 1); } } DescribeCpuFlags( @@ -147,9 +158,9 @@ relegated static void ShowGeneralRegisters(int fd, ucontext_t *ctx) { relegated static void ShowSseRegisters(int fd, ucontext_t *ctx) { size_t i; if (ctx->uc_mcontext.fpregs) { - write(fd, "\r\n\r\n", 4); + write(fd, "\n\n", 2); for (i = 0; i < 8; ++i) { - dprintf(fd, "%s%-2zu %016lx%016lx %s%-2d %016lx%016lx\r\n", "XMM", i + 0, + dprintf(fd, "%s%-2zu %016lx%016lx %s%-2d %016lx%016lx\n", "XMM", i + 0, ctx->uc_mcontext.fpregs->xmm[i + 0].u64[1], ctx->uc_mcontext.fpregs->xmm[i + 0].u64[0], "XMM", i + 8, ctx->uc_mcontext.fpregs->xmm[i + 8].u64[1], @@ -174,32 +185,37 @@ relegated static void ShowMemoryMappings(int outfd) { } relegated static void ShowCrashReport(int err, int fd, int sig, - ucontext_t *ctx) { + struct siginfo *si, ucontext_t *ctx) { int i; char hostname[64]; struct utsname names; strcpy(hostname, "unknown"); gethostname(hostname, sizeof(hostname)); - dprintf(fd, "\r\n%serror%s: Uncaught SIG%s on %s\r\n %s\r\n %s\r\n", RED2, - RESET, TinyStrSignal(sig), hostname, getauxval(AT_EXECFN), - strerror(err)); + dprintf(fd, + "\n" + "%serror%s: Uncaught SIG%s (%s) on %s\n" + " %s\n" + " %s\n", + RED2, RESET, TinyStrSignal(sig), + si ? GetSiCodeName(sig, si->si_code) : "???", hostname, + getauxval(AT_EXECFN), strerror(err)); if (uname(&names) != -1) { - dprintf(fd, " %s %s %s %s\r\n", names.sysname, names.nodename, - names.release, names.version); + dprintf(fd, " %s %s %s %s\n", names.sysname, names.nodename, names.release, + names.version); } ShowFunctionCalls(fd, ctx); if (ctx) { ShowGeneralRegisters(fd, ctx); ShowSseRegisters(fd, ctx); } - write(fd, "\r\n", 2); + write(fd, "\n", 1); ShowMemoryMappings(fd); - write(fd, "\r\n", 2); + write(fd, "\n", 1); for (i = 0; i < __argc; ++i) { write(fd, __argv[i], strlen(__argv[i])); write(fd, " ", 1); } - write(fd, "\r\n", 2); + write(fd, "\n", 1); } relegated static void RestoreDefaultCrashSignalHandlers(void) { @@ -246,7 +262,7 @@ relegated void __oncrash(int sig, struct siginfo *si, ucontext_t *ctx) { : 0); } if (gdbpid > 0 && (sig == SIGTRAP || sig == SIGQUIT)) return; - ShowCrashReport(err, STDERR_FILENO, sig, ctx); + ShowCrashReport(err, STDERR_FILENO, sig, si, ctx); exit(128 + sig); unreachable; } diff --git a/libc/log/oncrashthunks.S b/libc/log/oncrashthunks.S index 5d295cab0..ef70cb9e8 100644 --- a/libc/log/oncrashthunks.S +++ b/libc/log/oncrashthunks.S @@ -25,6 +25,8 @@ __oncrash_thunks: +// : showcrashreports.c, oncrashthunks.S, oncrash.c + .org 11*0 __oncrash_sigquit: push %rbp @@ -97,4 +99,6 @@ __oncrash_sigpipe: ret .endfn __oncrash_sigpipe,globl +// : showcrashreports.c, oncrashthunks.S, oncrash.c + .endobj __oncrash_thunks,globl diff --git a/libc/log/showcrashreports.c b/libc/log/showcrashreports.c index ae6a94ab5..d9eae94bb 100644 --- a/libc/log/showcrashreports.c +++ b/libc/log/showcrashreports.c @@ -34,7 +34,7 @@ STATIC_YOINK("__die"); -extern const unsigned char __oncrash_thunks[7][11]; +extern const unsigned char __oncrash_thunks[8][11]; /** * Installs crash signal handlers. @@ -56,7 +56,7 @@ extern const unsigned char __oncrash_thunks[7][11]; void showcrashreports(void) { size_t i; struct sigaction sa; - /* : oncrashthunks.S */ + /* : showcrashreports.c, oncrashthunks.S, oncrash.c */ kCrashSigs[0] = SIGQUIT; /* ctrl+\ aka ctrl+break */ kCrashSigs[1] = SIGFPE; /* 1 / 0 */ kCrashSigs[2] = SIGILL; /* illegal instruction */ @@ -65,9 +65,9 @@ void showcrashreports(void) { kCrashSigs[5] = SIGABRT; /* abort() called */ kCrashSigs[6] = SIGBUS; /* misaligned, noncanonical ptr, etc. */ kCrashSigs[7] = SIGPIPE; /* write to closed thing */ - /* : oncrashthunks.S */ + /* : showcrashreports.c, oncrashthunks.S, oncrash.c */ memset(&sa, 0, sizeof(sa)); - sa.sa_flags = SA_RESETHAND; + sa.sa_flags = SA_RESETHAND | SA_SIGINFO; sigfillset(&sa.sa_mask); for (i = 0; i < ARRAYLEN(kCrashSigs); ++i) { sigdelset(&sa.sa_mask, kCrashSigs[i]); diff --git a/libc/sysv/consts.sh b/libc/sysv/consts.sh index c23df5455..e2af91c5e 100755 --- a/libc/sysv/consts.sh +++ b/libc/sysv/consts.sh @@ -534,10 +534,10 @@ syscon sicode CLD_DUMPED 3 3 3 3 3 3 # SIGCHLD; child terminated syscon sicode CLD_TRAPPED 4 4 4 4 4 4 # SIGCHLD; traced child trapped; unix consensus syscon sicode CLD_STOPPED 5 5 5 5 5 5 # SIGCHLD; child stopped; unix consensus syscon sicode CLD_CONTINUED 6 6 6 6 6 6 # SIGCHLD; stopped child continued; unix consensus -syscon sicode TRAP_BRKPT 1 1 1 1 1 1 # SIGTRAP; unix consensus -syscon sicode TRAP_TRACE 2 2 2 2 2 2 # SIGTRAP; unix consensus -syscon sicode SEGV_MAPERR 1 1 1 1 1 1 # SIGSEGV; unix consensus -syscon sicode SEGV_ACCERR 2 2 2 2 2 2 # SIGSEGV; unix consensus +syscon sicode TRAP_BRKPT 1 1 1 1 1 1 # SIGTRAP; process breakpoint; unix consensus +syscon sicode TRAP_TRACE 2 2 2 2 2 2 # SIGTRAP; process trace trap; unix consensus +syscon sicode SEGV_MAPERR 1 1 1 1 1 1 # SIGSEGV; address not mapped to object; unix consensus +syscon sicode SEGV_ACCERR 2 2 2 2 2 2 # SIGSEGV; invalid permissions for mapped object; unix consensus syscon sicode FPE_INTDIV 1 7 2 1 1 1 # SIGFPE; integer divide by zero syscon sicode FPE_INTOVF 2 8 1 2 2 2 # SIGFPE; integer overflow syscon sicode FPE_FLTDIV 3 1 3 3 3 3 # SIGFPE; floating point divide by zero diff --git a/libc/x/xsigaction.c b/libc/x/xsigaction.c index 844844cd0..782a420ef 100644 --- a/libc/x/xsigaction.c +++ b/libc/x/xsigaction.c @@ -37,6 +37,7 @@ * @return 0 on success, or -1 w/ errno * @see libc/sysv/consts.sh * @asyncsignalsafe + * @vforksafe */ int xsigaction(int sig, void *handler, uint64_t flags, uint64_t mask, struct sigaction *old) { diff --git a/test/libc/calls/setrlimit_test.c b/test/libc/calls/setrlimit_test.c new file mode 100644 index 000000000..656db5791 --- /dev/null +++ b/test/libc/calls/setrlimit_test.c @@ -0,0 +1,291 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2021 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "dsp/core/core.h" +#include "libc/bits/safemacros.internal.h" +#include "libc/calls/calls.h" +#include "libc/calls/sigbits.h" +#include "libc/calls/struct/rlimit.h" +#include "libc/calls/struct/sigaction.h" +#include "libc/errno.h" +#include "libc/fmt/fmt.h" +#include "libc/log/check.h" +#include "libc/math.h" +#include "libc/mem/mem.h" +#include "libc/rand/rand.h" +#include "libc/runtime/directmap.internal.h" +#include "libc/runtime/runtime.h" +#include "libc/str/str.h" +#include "libc/sysv/consts/map.h" +#include "libc/sysv/consts/o.h" +#include "libc/sysv/consts/prot.h" +#include "libc/sysv/consts/rlimit.h" +#include "libc/sysv/consts/sig.h" +#include "libc/testlib/testlib.h" +#include "libc/time/time.h" +#include "libc/x/x.h" + +#define MEM (64 * 1024 * 1024) + +char tmpfile[PATH_MAX]; + +void OnSigxcpu(int sig) { + ASSERT_EQ(SIGXCPU, sig); + _exit(0); +} + +void OnSigxfsz(int sig) { + unlink(tmpfile); + ASSERT_EQ(SIGXFSZ, sig); + _exit(0); +} + +int EzSpawn(void) { + int pid, wstatus; + sigset_t chldmask, savemask; + struct sigaction ignore, saveint, savequit; + ignore.sa_flags = 0; + ignore.sa_handler = SIG_IGN; + sigemptyset(&ignore.sa_mask); + sigaction(SIGINT, &ignore, &saveint); + sigaction(SIGQUIT, &ignore, &savequit); + sigemptyset(&chldmask); + sigaddset(&chldmask, SIGCHLD); + sigprocmask(SIG_BLOCK, &chldmask, &savemask); + ASSERT_NE(-1, (pid = fork())); + if (!pid) { + sigaction(SIGINT, &saveint, 0); + sigaction(SIGQUIT, &savequit, 0); + sigprocmask(SIG_SETMASK, &savemask, 0); + return -2; + } + while (wait4(pid, &wstatus, 0, 0) == -1) { + ASSERT_EQ(EINTR, errno); + } + sigaction(SIGINT, &saveint, 0); + sigaction(SIGQUIT, &savequit, 0); + sigprocmask(SIG_SETMASK, &savemask, 0); + return wstatus; +} + +int EzSpawnFast(void fun(void *), void *ctx) { + int pid, wstatus; + sigset_t chldmask, savemask; + struct sigaction saveint, savequit; + xsigaction(SIGINT, SIG_IGN, 0, 0, &saveint); + xsigaction(SIGQUIT, SIG_IGN, 0, 0, &savequit); + sigemptyset(&chldmask); + sigaddset(&chldmask, SIGCHLD); + sigprocmask(SIG_BLOCK, &chldmask, &savemask); + ASSERT_NE(-1, (pid = vfork())); + if (!pid) { + xsigaction(SIGINT, SIG_DFL, 0, 0, 0); + xsigaction(SIGQUIT, SIG_DFL, 0, 0, 0); + sigprocmask(SIG_SETMASK, &savemask, 0); + fun(ctx); + _exit(127); + } + while (wait4(pid, &wstatus, 0, 0) == -1) { + ASSERT_EQ(EINTR, errno); + } + sigaction(SIGINT, &saveint, 0); + sigaction(SIGQUIT, &savequit, 0); + sigprocmask(SIG_SETMASK, &savemask, 0); + return wstatus; +} + +TEST(setrlimit, testCpuLimit) { + char *p; + int i, wstatus; + long double start; + struct rlimit rlim; + double matrices[3][3][3]; + if (IsWindows()) return; /* of course it doesn't work on windows */ + ASSERT_NE(-1, (wstatus = EzSpawn())); + if (wstatus == -2) { + CHECK_EQ(0, xsigaction(SIGXCPU, OnSigxcpu, 0, 0, 0)); + ASSERT_EQ(0, getrlimit(RLIMIT_CPU, &rlim)); + rlim.rlim_cur = 1; /* set soft limit to one second */ + ASSERT_EQ(0, setrlimit(RLIMIT_CPU, &rlim)); + start = nowl(); + do { + matmul3(matrices[0], matrices[1], matrices[2]); + matmul3(matrices[0], matrices[1], matrices[2]); + matmul3(matrices[0], matrices[1], matrices[2]); + matmul3(matrices[0], matrices[1], matrices[2]); + } while ((nowl() - start) < 5); + _exit(1); + } + EXPECT_TRUE(WIFEXITED(wstatus)); + EXPECT_FALSE(WIFSIGNALED(wstatus)); + EXPECT_EQ(0, WEXITSTATUS(wstatus)); + EXPECT_EQ(0, WTERMSIG(wstatus)); +} + +TEST(setrlimit, testFileSizeLimit) { + char *p; + char junkdata[512]; + int i, fd, wstatus; + struct rlimit rlim; + if (IsWindows()) return; /* of course it doesn't work on windows */ + ASSERT_NE(-1, (wstatus = EzSpawn())); + if (wstatus == -2) { + CHECK_EQ(0, xsigaction(SIGXFSZ, OnSigxfsz, 0, 0, 0)); + ASSERT_EQ(0, getrlimit(RLIMIT_FSIZE, &rlim)); + rlim.rlim_cur = 1024 * 1024; /* set soft limit to one megabyte */ + ASSERT_EQ(0, setrlimit(RLIMIT_FSIZE, &rlim)); + snprintf(tmpfile, sizeof(tmpfile), "%s/%s.%d", + firstnonnull(getenv("TMPDIR"), "/tmp"), + program_invocation_short_name, getpid()); + ASSERT_NE(-1, (fd = open(tmpfile, O_RDWR | O_CREAT | O_TRUNC))); + rngset(junkdata, 512, rand64, -1); + for (i = 0; i < 5 * 1024 * 1024 / 512; ++i) { + ASSERT_EQ(512, write(fd, junkdata, 512)); + } + close(fd); + unlink(tmpfile); + _exit(1); + } + EXPECT_TRUE(WIFEXITED(wstatus)); + EXPECT_FALSE(WIFSIGNALED(wstatus)); + EXPECT_EQ(0, WEXITSTATUS(wstatus)); + EXPECT_EQ(0, WTERMSIG(wstatus)); +} + +int SetKernelEnforcedMemoryLimit(size_t n) { + struct rlimit rlim = {n, n}; + if (IsWindows() || IsXnu()) return -1; + return setrlimit(!IsOpenbsd() ? RLIMIT_AS : RLIMIT_DATA, &rlim); +} + +TEST(setrlimit, testMemoryLimit) { + char *p; + int i, wstatus; + if (IsAsan()) return; /* b/c we use sys_mmap */ + if (IsXnu()) return; /* doesn't work on darwin */ + if (IsWindows()) return; /* of course it doesn't work on windows */ + ASSERT_NE(-1, (wstatus = EzSpawn())); + if (wstatus == -2) { + ASSERT_EQ(0, SetKernelEnforcedMemoryLimit(MEM)); + for (i = 0; i < (MEM * 2) / PAGESIZE; ++i) { + p = sys_mmap(0, PAGESIZE, PROT_READ | PROT_WRITE, + MAP_ANONYMOUS | MAP_PRIVATE | MAP_POPULATE, -1, 0) + .addr; + if (p == MAP_FAILED) { + ASSERT_EQ(ENOMEM, errno); + _exit(0); + } + rngset(p, PAGESIZE, rand64, -1); + } + _exit(1); + } + EXPECT_TRUE(WIFEXITED(wstatus)); + EXPECT_FALSE(WIFSIGNALED(wstatus)); + EXPECT_EQ(0, WEXITSTATUS(wstatus)); + EXPECT_EQ(0, WTERMSIG(wstatus)); +} + +TEST(setrlimit, testVirtualMemoryLimit) { + char *p; + int i, wstatus; + if (IsAsan()) return; + if (IsXnu()) return; /* doesn't work on darwin */ + if (IsOpenbsd()) return; /* unavailable on openbsd */ + if (IsWindows()) return; /* of course it doesn't work on windows */ + ASSERT_NE(-1, (wstatus = EzSpawn())); + if (wstatus == -2) { + ASSERT_EQ(0, setrlimit(RLIMIT_AS, &(struct rlimit){MEM, MEM})); + for (i = 0; i < (MEM * 2) / PAGESIZE; ++i) { + p = sys_mmap(0, PAGESIZE, PROT_READ | PROT_WRITE, + MAP_ANONYMOUS | MAP_PRIVATE | MAP_POPULATE, -1, 0) + .addr; + if (p == MAP_FAILED) { + ASSERT_EQ(ENOMEM, errno); + _exit(0); + } + rngset(p, PAGESIZE, rand64, -1); + } + _exit(1); + } + EXPECT_TRUE(WIFEXITED(wstatus)); + EXPECT_FALSE(WIFSIGNALED(wstatus)); + EXPECT_EQ(0, WEXITSTATUS(wstatus)); + EXPECT_EQ(0, WTERMSIG(wstatus)); +} + +TEST(setrlimit, testDataMemoryLimit) { + char *p; + int i, wstatus; + if (IsAsan()) return; + if (IsXnu()) return; /* doesn't work on darwin */ + if (IsNetbsd()) return; /* doesn't work on netbsd */ + if (IsFreebsd()) return; /* doesn't work on freebsd */ + if (IsLinux()) return; /* doesn't work on gnu/systemd */ + if (IsWindows()) return; /* of course it doesn't work on windows */ + ASSERT_NE(-1, (wstatus = EzSpawn())); + if (wstatus == -2) { + ASSERT_EQ(0, setrlimit(RLIMIT_DATA, &(struct rlimit){MEM, MEM})); + for (i = 0; i < (MEM * 2) / PAGESIZE; ++i) { + p = sys_mmap(0, PAGESIZE, PROT_READ | PROT_WRITE, + MAP_ANONYMOUS | MAP_PRIVATE | MAP_POPULATE, -1, 0) + .addr; + if (p == MAP_FAILED) { + ASSERT_EQ(ENOMEM, errno); + _exit(0); + } + rngset(p, PAGESIZE, rand64, -1); + } + _exit(1); + } + EXPECT_TRUE(WIFEXITED(wstatus)); + EXPECT_FALSE(WIFSIGNALED(wstatus)); + EXPECT_EQ(0, WEXITSTATUS(wstatus)); + EXPECT_EQ(0, WTERMSIG(wstatus)); +} + +TEST(setrlimit, testPhysicalMemoryLimit) { + /* RLIMIT_RSS doesn't work on gnu/systemd */ + /* RLIMIT_RSS doesn't work on darwin */ + /* RLIMIT_RSS doesn't work on freebsd */ + /* RLIMIT_RSS doesn't work on netbsd */ + /* RLIMIT_RSS doesn't work on openbsd */ + /* of course it doesn't work on windows */ +} + +wontreturn void OnVfork(void *ctx) { + struct rlimit *rlim; + rlim = ctx; + rlim->rlim_cur -= 1; + ASSERT_EQ(0, getrlimit(RLIMIT_CPU, rlim)); + _exit(0); +} + +TEST(setrlimit, isVforkSafe) { + int pid, ws; + struct rlimit rlim[2]; + if (IsWindows()) return; /* of course it doesn't work on windows */ + ASSERT_EQ(0, getrlimit(RLIMIT_CPU, rlim)); + ws = EzSpawnFast(OnVfork, rlim); + EXPECT_TRUE(WIFEXITED(ws)); + EXPECT_FALSE(WIFSIGNALED(ws)); + EXPECT_EQ(0, WEXITSTATUS(ws)); + EXPECT_EQ(0, WTERMSIG(ws)); + ASSERT_EQ(0, getrlimit(RLIMIT_CPU, rlim + 1)); + EXPECT_EQ(rlim[0].rlim_cur, rlim[1].rlim_cur); + EXPECT_EQ(rlim[0].rlim_max, rlim[1].rlim_max); +} diff --git a/test/libc/calls/test.mk b/test/libc/calls/test.mk index 0ac4572fe..ebda8edcf 100644 --- a/test/libc/calls/test.mk +++ b/test/libc/calls/test.mk @@ -24,7 +24,9 @@ TEST_LIBC_CALLS_CHECKS = \ $(TEST_LIBC_CALLS_SRCS_TEST:%.c=o/$(MODE)/%.com.runs) TEST_LIBC_CALLS_DIRECTDEPS = \ + DSP_CORE \ LIBC_CALLS \ + LIBC_TINYMATH \ LIBC_SOCK \ LIBC_FMT \ LIBC_INTRIN \ diff --git a/test/libc/fmt/atoi_test.c b/test/libc/fmt/atoi_test.c new file mode 100644 index 000000000..0092ae22a --- /dev/null +++ b/test/libc/fmt/atoi_test.c @@ -0,0 +1,463 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2021 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/errno.h" +#include "libc/fmt/conv.h" +#include "libc/limits.h" +#include "libc/stdio/stdio.h" +#include "libc/testlib/ezbench.h" +#include "libc/testlib/testlib.h" + +TEST(atoi, test) { + EXPECT_EQ(0, atoi("")); + EXPECT_EQ(0, atoi("0")); + EXPECT_EQ(1, atoi("1")); + EXPECT_EQ(9, atoi("9")); + EXPECT_EQ(12, atoi("12")); + EXPECT_EQ(-12, atoi("-12")); + EXPECT_EQ(31337, atoi("31337")); + EXPECT_EQ(+0, atoi("+0")); + EXPECT_EQ(+1, atoi("+1")); + EXPECT_EQ(+9, atoi("+9")); + EXPECT_EQ(-0, atoi("-0")); + EXPECT_EQ(-1, atoi("-1")); + EXPECT_EQ(-9, atoi("-9")); + EXPECT_EQ(-31337, atoi("-31337")); + EXPECT_EQ(INT_MIN, atoi("-2147483648")); + EXPECT_EQ(INT_MAX, atoi("2147483647")); + EXPECT_EQ(INT_MIN, atoi("-2147483649")); + EXPECT_EQ(INT_MAX, atoi("2147483648")); + EXPECT_EQ(INT_MIN, atoi("-2147483658")); + EXPECT_EQ(INT_MAX, atoi("2147483657")); + EXPECT_EQ(123, atoi(" 123")); + EXPECT_EQ(123, atoi(" \t123")); + EXPECT_EQ(+123, atoi(" +123")); + EXPECT_EQ(+123, atoi(" \t+123")); + EXPECT_EQ(-123, atoi(" -123")); + EXPECT_EQ(-123, atoi(" \t-123")); + EXPECT_EQ(0, atoi(" a123")); + EXPECT_EQ(0, atoi(" \ta123")); +} + +TEST(atoi, testWithinLimit_doesntChangeErrno) { + errno = 7; + EXPECT_EQ(INT_MAX, atoi("2147483647")); + EXPECT_EQ(7, errno); + errno = 7; + EXPECT_EQ(INT_MIN, atoi("-2147483648")); + EXPECT_EQ(7, errno); +} + +TEST(atoi, testOutsideLimit_saturatesAndSetsErangeErrno) { + errno = 0; + EXPECT_EQ(INT_MAX, atoi("2147483648")); + EXPECT_EQ(ERANGE, errno); + errno = 0; + EXPECT_EQ(INT_MIN, atoi("-2147483649")); + EXPECT_EQ(ERANGE, errno); +} + +TEST(atol, test) { + EXPECT_EQ(0, atol("")); + EXPECT_EQ(0, atol("0")); + EXPECT_EQ(1, atol("1")); + EXPECT_EQ(9, atol("9")); + EXPECT_EQ(12, atol("12")); + EXPECT_EQ(-12, atol("-12")); + EXPECT_EQ(31337, atol("31337")); + EXPECT_EQ(+0, atol("+0")); + EXPECT_EQ(+1, atol("+1")); + EXPECT_EQ(+9, atol("+9")); + EXPECT_EQ(-0, atol("-0")); + EXPECT_EQ(-1, atol("-1")); + EXPECT_EQ(-9, atol("-9")); + EXPECT_EQ(-31337, atol("-31337")); + EXPECT_EQ(LONG_MIN, atol("-9223372036854775808")); + EXPECT_EQ(LONG_MAX, atol("9223372036854775807")); + EXPECT_EQ(LONG_MIN, atol("-9223372036854775809")); + EXPECT_EQ(LONG_MAX, atol("9223372036854775808")); + EXPECT_EQ(LONG_MIN, atol("-9223372036854775818")); + EXPECT_EQ(LONG_MAX, atol("9223372036854775817")); + EXPECT_EQ(123, atol(" 123")); + EXPECT_EQ(123, atol(" \t123")); + EXPECT_EQ(-123, atol(" -123")); + EXPECT_EQ(-123, atol(" \t-123")); + EXPECT_EQ(0, atol(" a123")); + EXPECT_EQ(0, atol(" \ta123")); +} + +TEST(strtol, test) { + EXPECT_EQ(0, strtol("", 0, 10)); + EXPECT_EQ(0, strtol("0", 0, 10)); + EXPECT_EQ(1, strtol("1", 0, 10)); + EXPECT_EQ(9, strtol("9", 0, 10)); + EXPECT_EQ(12, strtol("12", 0, 10)); + EXPECT_EQ(-12, strtol("-12", 0, 10)); + EXPECT_EQ(31337, strtol("31337", 0, 10)); + EXPECT_EQ(0, strtol("-0", 0, 10)); + EXPECT_EQ(-1, strtol("-1", 0, 10)); + EXPECT_EQ(-9, strtol("-9", 0, 10)); + EXPECT_EQ(-31337, strtol("-31337", 0, 10)); + EXPECT_EQ(LONG_MIN, strtol("-9223372036854775808", 0, 10)); + EXPECT_EQ(LONG_MAX, strtol("9223372036854775807", 0, 10)); + EXPECT_EQ(LONG_MIN, strtol("-9223372036854775809", 0, 10)); + EXPECT_EQ(LONG_MAX, strtol("9223372036854775808", 0, 10)); + EXPECT_EQ(LONG_MIN, strtol("-9223372036854775818", 0, 10)); + EXPECT_EQ(LONG_MAX, strtol("9223372036854775817", 0, 10)); + EXPECT_EQ(123, strtol(" 123", 0, 10)); + EXPECT_EQ(123, strtol(" \t123", 0, 10)); + EXPECT_EQ(-123, strtol(" -123", 0, 10)); + EXPECT_EQ(-123, strtol(" \t-123", 0, 10)); + EXPECT_EQ(0, strtol(" a123", 0, 10)); + EXPECT_EQ(0, strtol(" \ta123", 0, 10)); +} + +TEST(strtol, testBinary) { + char *e; + ASSERT_EQ(85, strtol("0b1010101", 0, 2)); + ASSERT_EQ(85, strtol("0b1010101", 0, 0)); + ASSERT_EQ(85, strtol("0b1010101", &e, 0)); + ASSERT_EQ(0, *e); +} + +TEST(strtol, testOutsideLimit) { + errno = 0; + EXPECT_EQ(0x7fffffffffffffff, strtol("0x8000000000000000", NULL, 0)); + EXPECT_EQ(ERANGE, errno); + errno = 0; + EXPECT_EQ(0x8000000000000000, strtol("-0x8000000000000001", NULL, 0)); + EXPECT_EQ(ERANGE, errno); + errno = 0; + EXPECT_EQ(0x8000000000000001, strtol("-9223372036854775807", NULL, 0)); + EXPECT_EQ(0, errno); + errno = 0; + EXPECT_EQ(0x8000000000000000, strtol("-9223372036854775808", NULL, 0)); + EXPECT_EQ(0, errno); +} + +TEST(strtoul, ipAddress) { + char *e; + const char *p = "1.2.3.4"; + EXPECT_EQ(1, strtoul(p, &e, 0)); + EXPECT_EQ(1, e - p); +} + +TEST(strtoul, weirdComma) { + char *e; + const char *p = ",2"; + EXPECT_EQ(0, strtoul(p, &e, 0)); + EXPECT_EQ(0, e - p); +} + +TEST(strtoul, outsideLimit_doesModulus) { + EXPECT_EQ(/* python -c 'print(((2**123-1)/123)%(2**64))' */ + 9298358801382050408ull, + strtoul(/* python -c 'print((2**123-1)/123)' */ + "86453853384384772221385825058884200", 0, 10)); +} + +TEST(strtol, testHex) { + EXPECT_EQ(0, strtol("", 0, 16)); + EXPECT_EQ(0, strtol("0", 0, 16)); + EXPECT_EQ(1, strtol("1", 0, 16)); + EXPECT_EQ(9, strtol("9", 0, 16)); + EXPECT_EQ(18, strtol("12", 0, 16)); + EXPECT_EQ(-18, strtol("-12", 0, 16)); + EXPECT_EQ(201527, strtol("31337", 0, 16)); + EXPECT_EQ(15, strtol("f", 0, 16)); + EXPECT_EQ(3735928559, strtol("deadbeef", 0, 16)); + EXPECT_EQ(0, strtol("-0", 0, 16)); + EXPECT_EQ(-1, strtol("-1", 0, 16)); + EXPECT_EQ(-9, strtol("-9", 0, 16)); + EXPECT_EQ(-201527, strtol("-31337", 0, 16)); + EXPECT_EQ(-3735928559, strtol("-DEADBEEF", 0, 16)); + EXPECT_EQ(LONG_MIN, strtol("-8000000000000000", 0, 16)); + EXPECT_EQ(LONG_MAX, strtol("7fffffffffffffff", 0, 16)); + EXPECT_EQ(LONG_MIN, strtol("-8000000000000001", 0, 16)); + EXPECT_EQ(LONG_MAX, strtol("8000000000000000", 0, 16)); + EXPECT_EQ(LONG_MIN, strtol("-0x8000000000000000", 0, 16)); + EXPECT_EQ(LONG_MAX, strtol("0x7fffffffffffffff", 0, 16)); + EXPECT_EQ(LONG_MIN, strtol("-0x800000000000000a", 0, 16)); + EXPECT_EQ(LONG_MAX, strtol("0x8000000000000009", 0, 16)); + EXPECT_EQ(291, strtol(" 123", 0, 16)); + EXPECT_EQ(291, strtol(" \t123", 0, 16)); + EXPECT_EQ(-291, strtol(" -123", 0, 16)); + EXPECT_EQ(-291, strtol(" \t-123", 0, 16)); + EXPECT_EQ(0, strtol(" z123", 0, 16)); + EXPECT_EQ(0, strtol(" \tz123", 0, 16)); +} + +TEST(strtol, testOctal) { + EXPECT_EQ(0, strtol("", 0, 8)); + EXPECT_EQ(0, strtol("0", 0, 8)); + EXPECT_EQ(1, strtol("1", 0, 8)); + EXPECT_EQ(7, strtol("7", 0, 8)); + EXPECT_EQ(10, strtol("12", 0, 8)); + EXPECT_EQ(-10, strtol("-12", 0, 8)); + EXPECT_EQ(13023, strtol("31337", 0, 8)); + EXPECT_EQ(0, strtol("-0", 0, 8)); + EXPECT_EQ(-1, strtol("-1", 0, 8)); + EXPECT_EQ(-7, strtol("-7", 0, 8)); + EXPECT_EQ(-13023, strtol("-31337", 0, 8)); + EXPECT_EQ(LONG_MIN, strtol("-1000000000000000000000", 0, 8)); + EXPECT_EQ(LONG_MAX, strtol("777777777777777777777", 0, 8)); + EXPECT_EQ(LONG_MIN, strtol("-01000000000000000000000", 0, 0)); + EXPECT_EQ(LONG_MAX, strtol("0777777777777777777777", 0, 0)); + EXPECT_EQ(1152921504606846975, strtol("077777777777777777777", 0, 0)); + EXPECT_EQ(-144115188075855872, strtol("-010000000000000000000", 0, 0)); +} + +TEST(strtol, testBase36) { + EXPECT_EQ(35, strtol("z", 0, 36)); + EXPECT_EQ(5089507, strtol("31337", 0, 36)); + EXPECT_EQ(29234652, strtol("hello", 0, 36)); + EXPECT_EQ(29234652, strtol("HELLO", 0, 36)); +} + +TEST(strtol, testEndPtr) { + char *e; + const char *p; + p = "1"; + ASSERT_EQ(1, strtol(p, &e, 2)); + ASSERT_EQ(1, e - p); + p = ""; + ASSERT_EQ(0, strtol(p, &e, 2)); + ASSERT_EQ(0, e - p); +} + +TEST(wcstol, testEndPtr) { + wchar_t *e; + const wchar_t *p; + p = L"1"; + ASSERT_EQ(1, wcstol(p, &e, 2)); + ASSERT_EQ(1, e - p); + p = L""; + ASSERT_EQ(0, wcstol(p, &e, 2)); + ASSERT_EQ(0, e - p); +} + +TEST(strtoul, testEndPtr) { + char *e; + const char *p; + p = "1"; + ASSERT_EQ(1, strtoul(p, &e, 2)); + ASSERT_EQ(1, e - p); + p = ""; + ASSERT_EQ(0, strtoul(p, &e, 2)); + ASSERT_EQ(0, e - p); +} + +TEST(wcstoul, testEndPtr) { + wchar_t *e; + const wchar_t *p; + p = L"1"; + ASSERT_EQ(1, wcstoul(p, &e, 2)); + ASSERT_EQ(1, e - p); + p = L""; + ASSERT_EQ(0, wcstoul(p, &e, 2)); + ASSERT_EQ(0, e - p); +} + +TEST(strtoimax, testZero) { + EXPECT_EQ(0, strtoimax("0", NULL, 0)); +} + +TEST(strtoimax, testDecimal) { + EXPECT_EQ(-123, strtoimax("-123", NULL, 0)); +} + +TEST(strtoimax, testHex) { + EXPECT_EQ(-255, strtoimax("-0xff", NULL, 0)); + EXPECT_EQ(255, strtoimax("0xff", NULL, 16)); +} + +TEST(strtoimax, testOctal) { + EXPECT_EQ(-123, strtoimax("-0173", NULL, 0)); + EXPECT_EQ(123, strtoimax("0173", NULL, 8)); +} + +TEST(strtoimax, testBinary) { + EXPECT_EQ(-42, strtoimax("-0b101010", NULL, 0)); + EXPECT_EQ(42, strtoimax("0b101010", NULL, 2)); + ASSERT_EQ(85, strtoimax("0b1010101", 0, 2)); + ASSERT_EQ(85, strtoimax("0b1010101", 0, 0)); +} + +TEST(strtoimax, testEndPtr) { + char *e; + const char *p = "1"; + ASSERT_EQ(1, strtoimax(p, &e, 2)); + ASSERT_EQ(1, e - p); +} + +TEST(strtoimax, testLimits) { + EXPECT_EQ( + ((uintmax_t)0xffffffffffffffff) << 64 | (uintmax_t)0xffffffffffffffff, + strtoimax("-1", NULL, 0)); + EXPECT_EQ( + ((uintmax_t)0x7fffffffffffffff) << 64 | (uintmax_t)0xffffffffffffffff, + strtoimax("0x7fffffffffffffffffffffffffffffff", NULL, 0)); +} + +TEST(strtoimax, testOutsideLimit) { + errno = 0; + EXPECT_EQ( + ((uintmax_t)0x7fffffffffffffff) << 64 | (uintmax_t)0xffffffffffffffff, + strtoimax("0x80000000000000000000000000000000", NULL, 0)); + EXPECT_EQ(ERANGE, errno); + errno = 0; + EXPECT_EQ(((uintmax_t)0x8000000000000000) << 64 | 0x0000000000000000, + strtoimax("-0x80000000000000000000000000000001", NULL, 0)); + EXPECT_EQ(ERANGE, errno); +} + +TEST(strtoul, neghex) { + errno = 0; + ASSERT_EQ(-16, (long)strtoul("0xfffffffffffffff0", NULL, 0)); + EXPECT_EQ(0, errno); +} + +TEST(strtoumax, testZero) { + EXPECT_EQ(UINTMAX_MIN, strtoumax("0", NULL, 0)); +} +TEST(strtoumax, testDecimal) { + EXPECT_EQ(123, strtoumax("123", NULL, 0)); +} +TEST(strtoumax, testHex) { + EXPECT_EQ(255, strtoumax("0xff", NULL, 0)); + EXPECT_EQ(255, strtoumax("0xff", NULL, 16)); +} +TEST(strtoumax, testOctal) { + EXPECT_EQ(123, strtoumax("0173", NULL, 0)); + EXPECT_EQ(123, strtoumax("0173", NULL, 8)); +} +TEST(strtoumax, testBinary) { + EXPECT_EQ(42, strtoumax("0b101010", NULL, 0)); + EXPECT_EQ(42, strtoumax("0b101010", NULL, 2)); +} + +TEST(strtoumax, testMaximum) { + EXPECT_EQ(UINTMAX_MAX, + strtoumax("340282366920938463463374607431768211455", NULL, 0)); + EXPECT_EQ(UINTMAX_MAX, + strtoumax("0xffffffffffffffffffffffffffffffff", NULL, 0)); +} + +TEST(strtoumax, testTwosBane) { + EXPECT_EQ(((uintmax_t)0x8000000000000000) << 64 | 0x0000000000000000, + strtoumax("0x80000000000000000000000000000000", NULL, 0)); +} + +TEST(wcstol, test) { + EXPECT_EQ(0, wcstol(L"", 0, 10)); + EXPECT_EQ(0, wcstol(L"0", 0, 10)); + EXPECT_EQ(1, wcstol(L"1", 0, 10)); + EXPECT_EQ(9, wcstol(L"9", 0, 10)); + EXPECT_EQ(12, wcstol(L"12", 0, 10)); + EXPECT_EQ(-12, wcstol(L"-12", 0, 10)); + EXPECT_EQ(31337, wcstol(L"31337", 0, 10)); + EXPECT_EQ(0, wcstol(L"-0", 0, 10)); + EXPECT_EQ(-1, wcstol(L"-1", 0, 10)); + EXPECT_EQ(-9, wcstol(L"-9", 0, 10)); + EXPECT_EQ(-31337, wcstol(L"-31337", 0, 10)); + EXPECT_EQ(LONG_MIN, wcstol(L"-9223372036854775808", 0, 10)); + EXPECT_EQ(LONG_MAX, wcstol(L"9223372036854775807", 0, 10)); + EXPECT_EQ(LONG_MIN, wcstol(L"-9223372036854775809", 0, 10)); + EXPECT_EQ(LONG_MAX, wcstol(L"9223372036854775808", 0, 10)); + EXPECT_EQ(LONG_MIN, wcstol(L"-9223372036854775818", 0, 10)); + EXPECT_EQ(LONG_MAX, wcstol(L"9223372036854775817", 0, 10)); + EXPECT_EQ(123, wcstol(L" 123", 0, 10)); + EXPECT_EQ(123, wcstol(L" \t123", 0, 10)); + EXPECT_EQ(-123, wcstol(L" -123", 0, 10)); + EXPECT_EQ(-123, wcstol(L" \t-123", 0, 10)); + EXPECT_EQ(0, wcstol(L" a123", 0, 10)); + EXPECT_EQ(0, wcstol(L" \ta123", 0, 10)); +} + +TEST(wcstol, testOutsideLimit) { + errno = 0; + EXPECT_EQ(0x7fffffffffffffff, wcstol(L"0x8000000000000000", NULL, 0)); + EXPECT_EQ(ERANGE, errno); + errno = 0; + EXPECT_EQ(0x8000000000000000, wcstol(L"-0x8000000000000001", NULL, 0)); + EXPECT_EQ(ERANGE, errno); + errno = 0; + EXPECT_EQ(0x8000000000000001, wcstol(L"-9223372036854775807", NULL, 0)); + EXPECT_EQ(0, errno); + errno = 0; + EXPECT_EQ(0x8000000000000000, wcstol(L"-9223372036854775808", NULL, 0)); + EXPECT_EQ(0, errno); +} + +TEST(wcstol, testHex) { + EXPECT_EQ(0, wcstol(L"", 0, 16)); + EXPECT_EQ(0, wcstol(L"0", 0, 16)); + EXPECT_EQ(1, wcstol(L"1", 0, 16)); + EXPECT_EQ(9, wcstol(L"9", 0, 16)); + EXPECT_EQ(18, wcstol(L"12", 0, 16)); + EXPECT_EQ(-18, wcstol(L"-12", 0, 16)); + EXPECT_EQ(201527, wcstol(L"31337", 0, 16)); + EXPECT_EQ(15, wcstol(L"f", 0, 16)); + EXPECT_EQ(3735928559, wcstol(L"deadbeef", 0, 16)); + EXPECT_EQ(0, wcstol(L"-0", 0, 16)); + EXPECT_EQ(-1, wcstol(L"-1", 0, 16)); + EXPECT_EQ(-9, wcstol(L"-9", 0, 16)); + EXPECT_EQ(-201527, wcstol(L"-31337", 0, 16)); + EXPECT_EQ(-3735928559, wcstol(L"-DEADBEEF", 0, 16)); + EXPECT_EQ(LONG_MIN, wcstol(L"-8000000000000000", 0, 16)); + EXPECT_EQ(LONG_MAX, wcstol(L"7fffffffffffffff", 0, 16)); + EXPECT_EQ(LONG_MIN, wcstol(L"-8000000000000001", 0, 16)); + EXPECT_EQ(LONG_MAX, wcstol(L"8000000000000000", 0, 16)); + EXPECT_EQ(LONG_MIN, wcstol(L"-0x8000000000000000", 0, 16)); + EXPECT_EQ(LONG_MAX, wcstol(L"0x7fffffffffffffff", 0, 16)); + EXPECT_EQ(LONG_MIN, wcstol(L"-0x800000000000000a", 0, 16)); + EXPECT_EQ(LONG_MAX, wcstol(L"0x8000000000000009", 0, 16)); + EXPECT_EQ(291, wcstol(L" 123", 0, 16)); + EXPECT_EQ(291, wcstol(L" \t123", 0, 16)); + EXPECT_EQ(-291, wcstol(L" -123", 0, 16)); + EXPECT_EQ(-291, wcstol(L" \t-123", 0, 16)); + EXPECT_EQ(0, wcstol(L" z123", 0, 16)); + EXPECT_EQ(0, wcstol(L" \tz123", 0, 16)); +} + +TEST(wcstol, testBase36) { + EXPECT_EQ(35, wcstol(L"z", 0, 36)); + EXPECT_EQ(5089507, wcstol(L"31337", 0, 36)); + EXPECT_EQ(29234652, wcstol(L"hello", 0, 36)); + EXPECT_EQ(29234652, wcstol(L"HELLO", 0, 36)); +} + +BENCH(atoi, bench) { + EZBENCH2("atoi", donothing, EXPROPRIATE(atoi(VEIL("r", "123456789")))); + EZBENCH2("strtol", donothing, + EXPROPRIATE(strtol(VEIL("r", "123456789"), 0, 10))); + EZBENCH2("strtoul", donothing, + EXPROPRIATE(strtol(VEIL("r", "123456789"), 0, 10))); + EZBENCH2("wcstol", donothing, + EXPROPRIATE(wcstol(VEIL("r", L"123456789"), 0, 10))); + EZBENCH2("wcstoul", donothing, + EXPROPRIATE(wcstol(VEIL("r", L"123456789"), 0, 10))); + EZBENCH2("strtoimax", donothing, + EXPROPRIATE(strtoimax(VEIL("r", "123456789"), 0, 10))); + EZBENCH2("strtoumax", donothing, + EXPROPRIATE(strtoimax(VEIL("r", "123456789"), 0, 10))); + EZBENCH2("wcstoimax", donothing, + EXPROPRIATE(wcstoimax(VEIL("r", L"123456789"), 0, 10))); + EZBENCH2("wcstoumax", donothing, + EXPROPRIATE(wcstoimax(VEIL("r", L"123456789"), 0, 10))); +} diff --git a/test/libc/fmt/sizetol_test.c b/test/libc/fmt/sizetol_test.c new file mode 100644 index 000000000..ec1030cb0 --- /dev/null +++ b/test/libc/fmt/sizetol_test.c @@ -0,0 +1,53 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2021 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/fmt/conv.h" +#include "libc/testlib/testlib.h" + +TEST(sizetol, test) { + EXPECT_EQ(-1, sizetol("", 1000)); + EXPECT_EQ(0, sizetol("0", 1000)); + EXPECT_EQ(0, sizetol("0kb", 1000)); + EXPECT_EQ(31337, sizetol("31337", 1000)); + EXPECT_EQ(1000, sizetol("1kb", 1000)); + EXPECT_EQ(2000, sizetol("2k", 1000)); + EXPECT_EQ(1024, sizetol("1kb", 1024)); + EXPECT_EQ(2048, sizetol("2k", 1024)); + EXPECT_EQ(100000000, sizetol("100mb", 1000)); + EXPECT_EQ(100000000000000, sizetol("100tb", 1000)); + EXPECT_EQ(104857600, sizetol("100mb", 1024)); + EXPECT_EQ(100000000, sizetol("100MB", 1000)); + EXPECT_EQ(104857600, sizetol("100MB", 1024)); + EXPECT_EQ(100000000, sizetol("100M", 1000)); + EXPECT_EQ(104857600, sizetol("100M", 1024)); + EXPECT_EQ(100000000, sizetol(" 100M", 1000)); + EXPECT_EQ(104857600, sizetol("\t100M", 1024)); + EXPECT_EQ(100, sizetol(" 100", 1000)); + EXPECT_EQ(100, sizetol("\t100", 1024)); + EXPECT_EQ(100, sizetol(" 100 ", 1000)); + EXPECT_EQ(100, sizetol("\t100\t", 1024)); +} + +TEST(sizetol, testNegative_notAllowed) { + EXPECT_EQ(-1, sizetol("-23", 1000)); +} + +TEST(sizetol, testOverflow_isDetected) { + EXPECT_EQ(9000000000000000000, sizetol("9eb", 1000)); + EXPECT_EQ(-1, sizetol("10eb", 1000)); +} diff --git a/test/libc/fmt/strtoimax_test.c b/test/libc/fmt/strtoimax_test.c deleted file mode 100644 index 53d9d2a8c..000000000 --- a/test/libc/fmt/strtoimax_test.c +++ /dev/null @@ -1,87 +0,0 @@ -/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ -│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2020 Justine Alexandra Roberts Tunney │ -│ │ -│ Permission to use, copy, modify, and/or distribute this software for │ -│ any purpose with or without fee is hereby granted, provided that the │ -│ above copyright notice and this permission notice appear in all copies. │ -│ │ -│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ -│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ -│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ -│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ -│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ -│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ -│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ -│ PERFORMANCE OF THIS SOFTWARE. │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/bits/bits.h" -#include "libc/errno.h" -#include "libc/fmt/conv.h" -#include "libc/testlib/testlib.h" - -TEST(strtoimax, testZero) { - EXPECT_EQ(0, strtoimax("0", NULL, 0)); -} - -TEST(strtoimax, testDecimal) { - EXPECT_EQ(-123, strtoimax("-123", NULL, 0)); -} - -TEST(strtoimax, testHex) { - EXPECT_EQ(-255, strtoimax("-0xff", NULL, 0)); - EXPECT_EQ(255, strtoimax("0xff", NULL, 16)); -} - -TEST(strtoimax, testOctal) { - EXPECT_EQ(-123, strtoimax("-0173", NULL, 0)); - EXPECT_EQ(123, strtoimax("0173", NULL, 8)); -} - -TEST(strtoimax, testBinary) { - EXPECT_EQ(-42, strtoimax("-0b101010", NULL, 0)); - EXPECT_EQ(42, strtoimax("0b101010", NULL, 2)); -} - -TEST(strtoimax, testLimits) { - EXPECT_EQ( - ((uintmax_t)0xffffffffffffffff) << 64 | (uintmax_t)0xffffffffffffffff, - strtoimax("-1", NULL, 0)); - EXPECT_EQ( - ((uintmax_t)0x7fffffffffffffff) << 64 | (uintmax_t)0xffffffffffffffff, - strtoimax("0x7fffffffffffffffffffffffffffffff", NULL, 0)); -} - -TEST(strtoimax, testOutsideLimit) { - errno = 0; - EXPECT_EQ( - ((uintmax_t)0x7fffffffffffffff) << 64 | (uintmax_t)0xffffffffffffffff, - strtoimax("0x80000000000000000000000000000000", NULL, 0)); - EXPECT_EQ(ERANGE, errno); - errno = 0; - EXPECT_EQ(((uintmax_t)0x8000000000000000) << 64 | 0x0000000000000000, - strtoimax("-0x80000000000000000000000000000001", NULL, 0)); - EXPECT_EQ(ERANGE, errno); -} - -TEST(strtoul, neghex) { - errno = 0; - ASSERT_EQ(-16, (long)strtoul("0xfffffffffffffff0", NULL, 0)); - EXPECT_EQ(0, errno); -} - -TEST(strtol, testOutsideLimit) { - errno = 0; - EXPECT_EQ(0x7fffffffffffffff, strtol("0x8000000000000000", NULL, 0)); - EXPECT_EQ(ERANGE, errno); - errno = 0; - EXPECT_EQ(0x8000000000000000, strtol("-0x8000000000000001", NULL, 0)); - EXPECT_EQ(ERANGE, errno); - errno = 0; - EXPECT_EQ(0x8000000000000001, strtol("-9223372036854775807", NULL, 0)); - EXPECT_EQ(0, errno); - errno = 0; - EXPECT_EQ(0x8000000000000000, strtol("-9223372036854775808", NULL, 0)); - EXPECT_EQ(0, errno); -} diff --git a/test/libc/nexgen32e/kbase36_test.c b/test/libc/nexgen32e/kbase36_test.c new file mode 100644 index 000000000..2b74de29c --- /dev/null +++ b/test/libc/nexgen32e/kbase36_test.c @@ -0,0 +1,37 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2021 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/nexgen32e/nexgen32e.h" +#include "libc/testlib/testlib.h" + +TEST(kBase36, test) { + EXPECT_EQ(0, kBase36[0]); + EXPECT_EQ(0, kBase36[255]); + EXPECT_EQ(0, kBase36['!']); + EXPECT_EQ(0 + 1, kBase36['0']); + EXPECT_EQ(1 + 1, kBase36['1']); + EXPECT_EQ(9 + 1, kBase36['9']); + EXPECT_EQ(10 + 1, kBase36['a']); + EXPECT_EQ(10 + 1, kBase36['A']); + EXPECT_EQ(35 + 1, kBase36['z']); + EXPECT_EQ(35 + 1, kBase36['Z']); + EXPECT_EQ(0, kBase36['a' - 1]); + EXPECT_EQ(0, kBase36['A' - 1]); + EXPECT_EQ(0, kBase36['z' + 1]); + EXPECT_EQ(0, kBase36['Z' + 1]); +} diff --git a/third_party/mbedtls/test/test_suite_asn1parse.c b/third_party/mbedtls/test/test_suite_asn1parse.c index 0009f62e8..5aea897a9 100644 --- a/third_party/mbedtls/test/test_suite_asn1parse.c +++ b/third_party/mbedtls/test/test_suite_asn1parse.c @@ -15,6 +15,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#include "libc/stdio/stdio.h" #include "third_party/mbedtls/test/test.inc" /* * *** THIS FILE WAS MACHINE GENERATED *** @@ -688,6 +689,7 @@ void test_get_sequence_of( const data_t *input, int tag, mbedtls_test_set_step( step ); TEST_ASSERT( cur != NULL ); TEST_EQUAL( cur->buf.tag, tag ); + printf("yo %`'s\n", rest); n = strtoul( rest, (char **) &rest, 0 ); TEST_EQUAL( n, (size_t)( cur->buf.p - input->x ) ); ++rest; diff --git a/third_party/python/python.mk b/third_party/python/python.mk index e6dbbed38..0bc36bbfc 100644 --- a/third_party/python/python.mk +++ b/third_party/python/python.mk @@ -445,11 +445,11 @@ $(THIRD_PARTY_PYTHON_A).pkg: \ $(foreach x,$(THIRD_PARTY_PYTHON_A_DIRECTDEPS),$($(x)_A).pkg) $(THIRD_PARTY_PYTHON_A_OBJS): \ - OVERRIDE_CFLAGS += \ - -DNDEBUG \ - -DPy_BUILD_CORE \ - -DPLATFORM='"cosmo"' \ - -DMULTIARCH='"x86_64-cosmo"' + OVERRIDE_CFLAGS += \ + -DNDEBUG \ + -DPy_BUILD_CORE \ + -DPLATFORM='"cosmo"' \ + -DMULTIARCH='"x86_64-cosmo"' o/$(MODE)/third_party/python/Programs/python.o \ o/$(MODE)/third_party/python/Python/marshal.o \ @@ -457,8 +457,8 @@ o/$(MODE)/third_party/python/Python/sysmodule.o \ o/$(MODE)/third_party/python/Modules/selectmodule.o \ o/$(MODE)/third_party/python/Modules/getpath.o \ o/$(MODE)/third_party/python/Objects/listobject.o: \ - OVERRIDE_CFLAGS += \ - -DSTACK_FRAME_UNLIMITED + OVERRIDE_CFLAGS += \ + -DSTACK_FRAME_UNLIMITED o/$(MODE)/third_party/python/Python/dynload_shlib.o: \ OVERRIDE_CFLAGS += \ @@ -468,6 +468,8 @@ o/$(MODE)/third_party/python/Python/sysmodule.o: \ OVERRIDE_CFLAGS += \ -DABIFLAGS='"m"' +o/$(MODE)/third_party/python/Objects/unicodeobject.o: QUOTA += -C16 + $(THIRD_PARTY_PYTHON_A_OBJS): \ OVERRIDE_CFLAGS += \ -ffunction-sections \ diff --git a/tool/build/calculator.c b/tool/build/calculator.c index 086c49976..adbefef2b 100644 --- a/tool/build/calculator.c +++ b/tool/build/calculator.c @@ -14,6 +14,7 @@ #include "libc/bits/morton.h" #include "libc/bits/popcnt.h" #include "libc/calls/calls.h" +#include "libc/errno.h" #include "libc/fmt/conv.h" #include "libc/fmt/fmt.h" #include "libc/fmt/itoa.h" @@ -439,18 +440,26 @@ bool CallFunction(const char *sym) { return false; } +volatile const char *literal_; bool ConsumeLiteral(const char *literal) { + bool r; char *e; struct Value x; + literal_ = literal; + errno = 0; x.t = kInt; - x.i = *literal == '-' ? strtoimax(literal, &e, 0) : strtoumax(literal, &e, 0); - if (!e || *e) { + x.i = strtoimax(literal, &e, 0); + if (*e) { x.t = kFloat; x.f = strtod(literal, &e); - if (!e || *e) return false; + r = !(!e || *e); + } else if (errno == ERANGE) { + r = false; + } else { + r = true; } Push(x); - return true; + return r; } void ConsumeToken(void) { @@ -465,7 +474,7 @@ void ConsumeToken(void) { default: if (CallFunction(token.p)) return; if (ConsumeLiteral(token.p)) return; - Warning("bad token: %s", token.p); + Warning("bad token: %`'s", token.p); break; case kUnderflow: Warning("stack underflow"); diff --git a/tool/build/calculator.ctest b/tool/build/calculator.ctest index a316238d2..99de88017 100755 --- a/tool/build/calculator.ctest +++ b/tool/build/calculator.ctest @@ -37,7 +37,7 @@ nan isnormal ! assert # BIT ARITHMETIC -1 ~ 0 = assert -0xffffffffffffffffffffffffffffffff ~ 0 = assert +0 0x7fffffffffffffffffffffffffffffff - -0x7fffffffffffffffffffffffffffffff = assert 0b1010101 popcnt 4 = assert 0b1010101 0b0110101 ^ 0b1100000 = assert 0b1010101 0b0110101 | 0b1110101 = assert diff --git a/tool/build/compile.c b/tool/build/compile.c index 2c1cae0d7..ba48ce5ee 100644 --- a/tool/build/compile.c +++ b/tool/build/compile.c @@ -16,22 +16,33 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/assert.h" #include "libc/bits/safemacros.internal.h" #include "libc/calls/calls.h" #include "libc/calls/copyfile.h" #include "libc/calls/sigbits.h" +#include "libc/calls/struct/rusage.h" #include "libc/calls/struct/sigset.h" #include "libc/errno.h" #include "libc/fmt/conv.h" #include "libc/log/color.internal.h" #include "libc/log/log.h" +#include "libc/macros.internal.h" +#include "libc/math.h" #include "libc/mem/mem.h" +#include "libc/nexgen32e/kcpuids.h" #include "libc/runtime/runtime.h" +#include "libc/stdio/append.internal.h" #include "libc/str/str.h" +#include "libc/sysv/consts/rlimit.h" #include "libc/sysv/consts/sig.h" #include "libc/x/x.h" #include "third_party/getopt/getopt.h" +long cpuquota = 8; /* secs */ +long fszquota = 100 * 1000 * 1000; /* bytes */ +long memquota = 256 * 1024 * 1024; /* bytes */ + #define MANUAL \ "\ SYNOPSIS\n\ @@ -185,9 +196,9 @@ const char *const kGccOnlyFlags[] = { char *DescribeCommand(void) { if (iscc) { if (isgcc) { - return xasprintf("gcc %d", ccversion); + return xasprintf("%s %d", "gcc", ccversion); } else if (isclang) { - return xasprintf("clang %d", ccversion); + return xasprintf("%s %d", "clang", ccversion); } } return basename(cmd); @@ -273,35 +284,76 @@ void AddArg(char *s) { command.n += n; } } else { - command.p = realloc(command.p, command.n + 2); - command.p[command.n++] = '\r'; + command.p = realloc(command.p, command.n + 1); command.p[command.n++] = '\n'; } } -int Launch(void) { +int GetBaseCpuFreqMhz(void) { + return KCPUIDS(16H, EAX) & 0x7fff; +} + +void SetCpuLimit(int secs) { + int mhz; + struct rlimit rlim; + if (secs < 0) return; + if (IsWindows()) return; + if (!(mhz = GetBaseCpuFreqMhz())) return; + if (getrlimit(RLIMIT_CPU, &rlim) == -1) return; + rlim.rlim_cur = ceil(3100. / mhz * secs); + setrlimit(RLIMIT_CPU, &rlim); +} + +void SetFszLimit(long n) { + struct rlimit rlim; + if (n < 0) return; + if (IsWindows()) return; + if (getrlimit(RLIMIT_FSIZE, &rlim) == -1) return; + rlim.rlim_cur = n; + setrlimit(RLIMIT_FSIZE, &rlim); +} + +void SetMemLimit(long n) { + struct rlimit rlim = {n, n}; + if (n < 0) return; + if (IsWindows() || IsXnu()) return; + setrlimit(!IsOpenbsd() ? RLIMIT_AS : RLIMIT_DATA, &rlim); +} + +int Launch(struct rusage *ru) { int ws, pid; if ((pid = vfork()) == -1) exit(errno); if (!pid) { + SetCpuLimit(cpuquota); + SetFszLimit(fszquota); + SetMemLimit(memquota); sigprocmask(SIG_SETMASK, &savemask, NULL); execve(cmd, args.p, env.p); _exit(127); } - while (waitpid(pid, &ws, 0) == -1) { + while (wait4(pid, &ws, 0, ru) == -1) { if (errno != EINTR) exit(errno); } return ws; } +char *GetResourceReport(struct rusage *ru) { + char *report = 0; + appendf(&report, "\n"); + AppendResourceReport(&report, ru, "\n"); + return report; +} + int main(int argc, char *argv[]) { size_t n; char *p, **envp; + struct rusage ru; int i, ws, rc, opt; /* * parse prefix arguments */ - while ((opt = getopt(argc, argv, "?hntA:T:V:")) != -1) { + while ((opt = getopt(argc, argv, "?hntC:M:F:A:T:V:")) != -1) { switch (opt) { case 'n': exit(0); @@ -317,6 +369,15 @@ int main(int argc, char *argv[]) { case 'V': ccversion = atoi(optarg); break; + case 'C': + cpuquota = atoi(optarg); + break; + case 'M': + memquota = sizetol(optarg, 1024); + break; + case 'F': + fszquota = sizetol(optarg, 1000); + break; case '?': case 'h': write(1, MANUAL, sizeof(MANUAL) - 1); @@ -432,7 +493,7 @@ int main(int argc, char *argv[]) { if (isclang) AddArg(argv[i]); } else if (isclang && startswith(argv[i], "--debug-prefix-map")) { /* llvm doesn't provide a gas interface so simulate w/ clang */ - AddArg(xasprintf("-f%s", argv[i] + 2)); + AddArg(xasprintf("%s%s", "-f", argv[i] + 2)); } else if (isgcc && (!strcmp(argv[i], "-Os") || !strcmp(argv[i], "-O2") || !strcmp(argv[i], "-O3"))) { /* https://gcc.gnu.org/bugzilla/show_bug.cgi?id=97623 */ @@ -526,8 +587,8 @@ int main(int argc, char *argv[]) { * log command being run */ if (!strcmp(nulltoempty(getenv("V")), "0") && !IsTerminalInarticulate()) { - p = xasprintf("\r\e[K%-15s%s\r", firstnonnull(action, "BUILD"), - firstnonnull(target, nulltoempty(outpath))); + p = (xasprintf)("\r\e[K%-15s%s\r", firstnonnull(action, "BUILD"), + firstnonnull(target, nulltoempty(outpath))); n = strlen(p); } else { if (IsTerminalInarticulate() && @@ -535,10 +596,9 @@ int main(int argc, char *argv[]) { command.n > columns + 2) { /* emacs command window is very slow so truncate lines */ command.n = columns + 2; - command.p[command.n - 5] = '.'; command.p[command.n - 4] = '.'; command.p[command.n - 3] = '.'; - command.p[command.n - 2] = '\r'; + command.p[command.n - 2] = '.'; command.p[command.n - 1] = '\n'; } p = command.p; @@ -551,7 +611,7 @@ int main(int argc, char *argv[]) { * and we help FindDebugBinary to find debug symbols */ if (!IsWindows() && endswith(cmd, ".com")) { - comdbg = xasprintf("%s.dbg", cmd); + comdbg = xasprintf("%s%s", cmd, ".dbg"); cachedcmd = xasprintf("o/%s", cmd); if (fileexists(comdbg)) { AddEnv(xasprintf("COMDBG=%s", comdbg)); @@ -578,7 +638,7 @@ int main(int argc, char *argv[]) { */ sigfillset(&mask); sigprocmask(SIG_BLOCK, &mask, &savemask); - ws = Launch(); + ws = Launch(&ru); /* * if execve() failed unzip gcc and try again @@ -587,7 +647,7 @@ int main(int argc, char *argv[]) { startswith(cmd, "o/third_party/gcc") && fileexists("third_party/gcc/unbundle.sh")) { system("third_party/gcc/unbundle.sh"); - ws = Launch(); + ws = Launch(&ru); } /* @@ -613,13 +673,15 @@ int main(int argc, char *argv[]) { } return 0; } else { - p = xasprintf("%s%s EXITED WITH %d%s: %.*s\r\n", RED2, DescribeCommand(), - WEXITSTATUS(ws), RESET, command.n, command.p); + p = xasprintf("%s%s %s %d%s: %.*s%s\n", RED2, DescribeCommand(), + "exited with", WEXITSTATUS(ws), RESET, command.n, command.p, + GetResourceReport(&ru)); rc = WEXITSTATUS(ws); } } else { - p = xasprintf("%s%s TERMINATED BY %s%s: %.*s\r\n", RED2, DescribeCommand(), - strsignal(WTERMSIG(ws)), RESET, command.n, command.p); + p = xasprintf("%s%s %s %s%s: %.*s%s\n", RED2, DescribeCommand(), + "terminated by", strsignal(WTERMSIG(ws)), RESET, command.n, + command.p, GetResourceReport(&ru)); rc = 128 + WTERMSIG(ws); } diff --git a/tool/build/runit.c b/tool/build/runit.c index e8fb5620f..34ddc1a7b 100644 --- a/tool/build/runit.c +++ b/tool/build/runit.c @@ -325,67 +325,55 @@ void SendRequest(void) { CHECK_NE(-1, close(fd)); } +bool Recv(unsigned char *p, size_t n) { + size_t i, rc; + static long backoff; + for (i = 0; i < n; i += rc) { + do { + rc = mbedtls_ssl_read(&ezssl, p + i, n - i); + } while (rc == MBEDTLS_ERR_SSL_WANT_READ); + if (!rc || rc == MBEDTLS_ERR_NET_CONN_RESET) { + usleep((backoff = (backoff + 1000) * 2)); + return false; + } else if (rc < 0) { + EzTlsDie("read response failed", rc); + } + } + return true; +} + int ReadResponse(void) { int res; ssize_t rc; size_t n, m; uint32_t size; - unsigned char *p; - enum RunitCommand cmd; - static long backoff; - static unsigned char msg[512]; - res = -1; - for (;;) { - while ((rc = mbedtls_ssl_read(&ezssl, msg, sizeof(msg))) < 0) { - if (rc == MBEDTLS_ERR_SSL_WANT_READ) { - continue; - } else if (rc == MBEDTLS_ERR_NET_CONN_RESET) { - CHECK_EQ(ECONNRESET, errno); - usleep((backoff = (backoff + 1000) * 2)); - close(g_sock); - return -1; - } else { - EzTlsDie("read response failed", rc); - } + unsigned char b[512]; + for (res = -1; res == -1;) { + if (!Recv(b, 5)) break; + CHECK_EQ(RUNITD_MAGIC, READ32BE(b)); + switch (b[4]) { + case kRunitExit: + if (!Recv(b, 1)) break; + if ((res = *b)) { + WARNF("%s on %s exited with %d", g_prog, g_hostname, res); + } + break; + case kRunitStderr: + if (!Recv(b, 4)) break; + size = READ32BE(b); + for (; size; size -= n) { + n = MIN(size, sizeof(b)); + if (!Recv(b, n)) goto drop; + CHECK_EQ(n, write(2, b, n)); + } + break; + default: + fprintf(stderr, "error: received invalid runit command\n"); + _exit(1); } - p = &msg[0]; - n = (size_t)rc; - if (!n) break; - do { - CHECK_GE(n, 4 + 1); - CHECK_EQ(RUNITD_MAGIC, READ32BE(p)); - p += 4, n -= 4; - cmd = *p++, n--; - switch (cmd) { - case kRunitExit: - CHECK_GE(n, 1); - if ((res = *p & 0xff)) { - WARNF("%s on %s exited with %d", g_prog, g_hostname, res); - } - goto drop; - case kRunitStderr: - CHECK_GE(n, 4); - size = READ32BE(p), p += 4, n -= 4; - while (size) { - if (n) { - CHECK_NE(-1, (rc = write(STDERR_FILENO, p, MIN(n, size)))); - CHECK_NE(0, (m = (size_t)rc)); - p += m, n -= m, size -= m; - } else { - CHECK_NE(-1, (rc = recv(g_sock, msg, sizeof(msg), 0))); - p = &msg[0]; - n = (size_t)rc; - if (!n) goto drop; - } - } - break; - default: - __die(); - } - } while (n); } drop: - CHECK_NE(-1, close(g_sock)); + close(g_sock); return res; } diff --git a/tool/net/redbean.c b/tool/net/redbean.c index ae32f263a..a936e2113 100644 --- a/tool/net/redbean.c +++ b/tool/net/redbean.c @@ -1179,60 +1179,6 @@ static void ReportWorkerExit(int pid, int ws) { } } -static void AppendResourceReport(char **b, struct rusage *ru, const char *nl) { - long utime, stime; - long double ticks; - if (ru->ru_maxrss) { - appendf(b, "ballooned to %,ldkb in size%s", ru->ru_maxrss, nl); - } - if ((utime = ru->ru_utime.tv_sec * 1000000 + ru->ru_utime.tv_usec) | - (stime = ru->ru_stime.tv_sec * 1000000 + ru->ru_stime.tv_usec)) { - ticks = ceill((long double)(utime + stime) / (1000000.L / CLK_TCK)); - appendf(b, "needed %,ldµs cpu (%d%% kernel)%s", utime + stime, - (int)((long double)stime / (utime + stime) * 100), nl); - if (ru->ru_idrss) { - appendf(b, "needed %,ldkb memory on average%s", - lroundl(ru->ru_idrss / ticks), nl); - } - if (ru->ru_isrss) { - appendf(b, "needed %,ldkb stack on average%s", - lroundl(ru->ru_isrss / ticks), nl); - } - if (ru->ru_ixrss) { - appendf(b, "mapped %,ldkb shared on average%s", - lroundl(ru->ru_ixrss / ticks), nl); - } - } - if (ru->ru_minflt || ru->ru_majflt) { - appendf(b, "caused %,ld page faults (%d%% memcpy)%s", - ru->ru_minflt + ru->ru_majflt, - (int)((long double)ru->ru_minflt / (ru->ru_minflt + ru->ru_majflt) * - 100), - nl); - } - if (ru->ru_nvcsw + ru->ru_nivcsw > 1) { - appendf( - b, "%,ld context switches (%d%% consensual)%s", - ru->ru_nvcsw + ru->ru_nivcsw, - (int)((long double)ru->ru_nvcsw / (ru->ru_nvcsw + ru->ru_nivcsw) * 100), - nl); - } - if (ru->ru_msgrcv || ru->ru_msgsnd) { - appendf(b, "received %,ld message%s and sent %,ld%s", ru->ru_msgrcv, - ru->ru_msgrcv == 1 ? "" : "s", ru->ru_msgsnd, nl); - } - if (ru->ru_inblock || ru->ru_oublock) { - appendf(b, "performed %,ld read%s and %,ld write i/o operations%s", - ru->ru_inblock, ru->ru_inblock == 1 ? "" : "s", ru->ru_oublock, nl); - } - if (ru->ru_nsignals) { - appendf(b, "received %,ld signals%s", ru->ru_nsignals, nl); - } - if (ru->ru_nswap) { - appendf(b, "got swapped %,ld times%s", ru->ru_nswap, nl); - } -} - static void AddTimeval(struct timeval *x, const struct timeval *y) { x->tv_sec += y->tv_sec; x->tv_usec += y->tv_usec; diff --git a/tool/viz/cpuid.c b/tool/viz/cpuid.c index 1a8295eff..d10c36394 100644 --- a/tool/viz/cpuid.c +++ b/tool/viz/cpuid.c @@ -16,6 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/bits/bits.h" #include "libc/log/color.internal.h" #include "libc/log/log.h" #include "libc/nexgen32e/cpuid4.internal.h" @@ -42,6 +43,10 @@ static void show(const char *constant, long value) { printf("%-20s%#lx\n", constant, value); } +static void decimal(const char *a, long b, const char *c) { + printf("%-20s%ld%s\n", a, b, c); +} + static void showvendor(void) { printf("%.*s%.*s%.*s", 4, &KCPUIDS(0H, EBX), 4, &KCPUIDS(0H, EDX), 4, &KCPUIDS(0H, ECX)); @@ -55,12 +60,6 @@ static void showmodel(void) { } } -static void showspeed(void) { - if (KCPUIDS(16H, EAX)) { - printf(" %.1f%s", KCPUIDS(16H, EAX) / 1000.0, "ghz"); - } -} - static void showstrata(void) { if (getx86processormodel(kX86ProcessorModelKey)) { printf(" (%s %s)", @@ -91,14 +90,21 @@ void showcachesizes(void) { } int main(int argc, char *argv[]) { + int x; long tsc_aux; showvendor(); showmodel(); - showspeed(); showstrata(); printf("\n"); + if (KCPUIDS(16H, EAX)) { + printf("\n"); + if ((x = KCPUIDS(16H, EAX) & 0x7fff)) decimal("frequency", x, "mhz"); + if ((x = KCPUIDS(16H, EBX) & 0x7fff)) decimal("turbo", x, "mhz"); + if ((x = KCPUIDS(16H, ECX) & 0x7fff)) decimal("bus", x, "mhz"); + } + if (X86_HAVE(HYPERVISOR)) { unsigned eax, ebx, ecx, edx; asm("push\t%%rbx\n\t"