mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-01-31 03:27:39 +00:00
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:
parent
9b29358511
commit
e963d9c8e3
49 changed files with 1802 additions and 553 deletions
Binary file not shown.
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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 ─╬─│┼
|
||||
|
|
|
@ -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
90
libc/fmt/sizetol.c
Normal 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;
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
35
libc/fmt/strtol.internal.h
Normal file
35
libc/fmt/strtol.internal.h
Normal 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_ */
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
55
libc/fmt/wcstoumax.c
Normal 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;
|
||||
}
|
81
libc/log/appendresourcereport.c
Normal file
81
libc/log/appendresourcereport.c
Normal 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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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
154
libc/log/getsicodename.c
Normal 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;
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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 ─╬─│┼
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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]);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
291
test/libc/calls/setrlimit_test.c
Normal file
291
test/libc/calls/setrlimit_test.c
Normal 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);
|
||||
}
|
|
@ -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
463
test/libc/fmt/atoi_test.c
Normal 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)));
|
||||
}
|
53
test/libc/fmt/sizetol_test.c
Normal file
53
test/libc/fmt/sizetol_test.c
Normal 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));
|
||||
}
|
|
@ -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);
|
||||
}
|
37
test/libc/nexgen32e/kbase36_test.c
Normal file
37
test/libc/nexgen32e/kbase36_test.c
Normal 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]);
|
||||
}
|
|
@ -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;
|
||||
|
|
16
third_party/python/python.mk
vendored
16
third_party/python/python.mk
vendored
|
@ -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 \
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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"
|
||||
|
|
Loading…
Reference in a new issue