Add cpu / mem / fsz limits to build system

Thanks to all the refactorings we now have the ability to enforce
reasonable limitations on the amount of resources any individual
compile or test can consume. Those limits are currently:

- `-C 8` seconds of 3.1ghz CPU time
- `-M 256mebibytes` of virtual memory
- `-F 100megabyte` limit on file size

Only one file currently needs to exceed these limits:

    o/$(MODE)/third_party/python/Objects/unicodeobject.o: \
        QUOTA += -C16  # overrides cpu limit to 16 seconds

This change introduces a new sizetol() function to LIBC_FMT for parsing
byte or bit size strings with Si unit suffixes. Functions like atoi()
have been rewritten too.
This commit is contained in:
Justine Tunney 2021-08-13 11:18:25 -07:00
parent 9b29358511
commit e963d9c8e3
49 changed files with 1802 additions and 553 deletions

Binary file not shown.

View file

@ -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

View file

@ -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();

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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);
}

View file

@ -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

View file

@ -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))

90
libc/fmt/sizetol.c Normal file
View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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_ */

View file

@ -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);
}

View file

@ -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;
}

View file

@ -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);
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;
}

55
libc/fmt/wcstoumax.c Normal file
View file

@ -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;
}

View file

@ -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);
}
}

View file

@ -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;

View file

@ -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();
}

View file

@ -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);
}

View file

@ -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");
}

154
libc/log/getsicodename.c Normal file
View file

@ -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;
}

View file

@ -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;

View file

@ -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

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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];
/* <SYNC-LIST>: 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", //
};
/* </SYNC-LIST>: 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;
}

View file

@ -25,6 +25,8 @@
__oncrash_thunks:
// <SYNC-LIST>: showcrashreports.c, oncrashthunks.S, oncrash.c
.org 11*0
__oncrash_sigquit:
push %rbp
@ -97,4 +99,6 @@ __oncrash_sigpipe:
ret
.endfn __oncrash_sigpipe,globl
// </SYNC-LIST>: showcrashreports.c, oncrashthunks.S, oncrash.c
.endobj __oncrash_thunks,globl

View file

@ -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;
/* <SYNC-LIST>: oncrashthunks.S */
/* <SYNC-LIST>: 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 */
/* </SYNC-LIST>: oncrashthunks.S */
/* </SYNC-LIST>: 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]);

View file

@ -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

View file

@ -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) {

View file

@ -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);
}

View file

@ -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 \

463
test/libc/fmt/atoi_test.c Normal file
View file

@ -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)));
}

View file

@ -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));
}

View file

@ -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);
}

View file

@ -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]);
}

View file

@ -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;

View file

@ -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 \

View file

@ -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");

View file

@ -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

View file

@ -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);
}

View file

@ -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;
}

View file

@ -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;

View file

@ -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"