diff --git a/build/bootstrap/compile.com b/build/bootstrap/compile.com index 48e4822ec..8ca98c4da 100755 Binary files a/build/bootstrap/compile.com and b/build/bootstrap/compile.com differ 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"