Merge "Snap for 8018583 from e2c784f15912115f778e427f0ce9a21189a3b494 to ndk-r24-release" into ndk-r24-release
diff --git a/OWNERS b/OWNERS
index 4cb95a3..e02ad9f 100644
--- a/OWNERS
+++ b/OWNERS
@@ -3,9 +3,7 @@
cferris@google.com
danalbert@google.com
hhb@google.com
-jmgao@google.com
rprichard@google.com
-tomcherry@google.com
yabinc@google.com
# Still the best reviewer for changes related to the dynamic linker.
diff --git a/benchmarks/time_benchmark.cpp b/benchmarks/time_benchmark.cpp
index 437dc78..a765e3e 100644
--- a/benchmarks/time_benchmark.cpp
+++ b/benchmarks/time_benchmark.cpp
@@ -187,3 +187,13 @@
}
}
BIONIC_BENCHMARK(BM_time_localtime_r);
+
+void BM_time_strftime(benchmark::State& state) {
+ char buf[128];
+ time_t t = 0;
+ struct tm* tm = gmtime(&t);
+ while (state.KeepRunning()) {
+ strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", tm);
+ }
+}
+BIONIC_BENCHMARK(BM_time_strftime);
diff --git a/docs/EINTR.md b/docs/EINTR.md
new file mode 100644
index 0000000..8d1ab52
--- /dev/null
+++ b/docs/EINTR.md
@@ -0,0 +1,91 @@
+# EINTR
+
+## The problem
+
+If your code is blocked in a system call when a signal needs to be delivered,
+the kernel needs to interrupt that system call. For something like a read(2)
+call where some data has already been read, the call can just return with
+what data it has. (This is one reason why read(2) sometimes returns less data
+than you asked for, even though more data is available. It also explains why
+such behavior is relatively rare, and a cause of bugs.)
+
+But what if read(2) hasn't read any data yet? Or what if you've made some other
+system call, for which there is no equivalent "partial" success, such as
+poll(2)? In poll(2)'s case, there's either something to report (in which
+case the system call would already have returned), or there isn't.
+
+The kernel's solution to this problem is to return failure (-1) and set
+errno to `EINTR`: "interrupted system call".
+
+### Can I just opt out?
+
+Technically, yes. In practice on Android, no. Technically if a signal's
+disposition is set to ignore, the kernel doesn't even have to deliver the
+signal, so your code can just stay blocked in the system call it was already
+making. In practice, though, you can't guarantee that all signals are either
+ignored or will kill your process... Unless you're a small single-threaded
+C program that doesn't use any libraries, you can't realistically make this
+guarantee. If any code has installed a signal handler, you need to cope with
+`EINTR`. And if you're an Android app, the zygote has already installed a whole
+host of signal handlers before your code even starts to run. (And, no, you
+can't ignore them instead, because some of them are critical to how ART works.
+For example: Java `NullPointerException`s are optimized by trapping `SIGSEGV`
+signals so that the code generated by the JIT doesn't have to insert explicit
+null pointer checks.)
+
+### Why don't I see this in Java code?
+
+You won't see this in Java because the decision was taken to hide this issue
+from Java programmers. Basically, all the libraries like `java.io.*` and
+`java.net.*` hide this from you. (The same should be true of `android.*` too,
+so it's worth filing bugs if you find any exceptions that aren't documented!)
+
+### Why doesn't libc do that too?
+
+For most people, things would be easier if libc hid this implementation
+detail. But there are legitimate use cases, and automatically retrying
+would hide those. For example, you might want to use signals and `EINTR`
+to interrupt another thread (in fact, that's how interruption of threads
+doing I/O works in Java behind the scenes!). As usual, C/C++ choose the more
+powerful but more error-prone option.
+
+## The fix
+
+### Easy cases
+
+In most cases, the fix is simple: wrap the system call with the
+`TEMP_FAILURE_RETRY` macro. This is basically a while loop that retries the
+system call as long as the result is -1 and errno is `EINTR`.
+
+So, for example:
+```
+ n = read(fd, buf, buf_size); // BAD!
+ n = TEMP_FAILURE_RETRY(read(fd, buf, buf_size)); // GOOD!
+```
+
+### close(2)
+
+TL;DR: *never* wrap close(2) calls with `TEMP_FAILURE_RETRY`.
+
+The case of close(2) is complicated. POSIX explicitly says that close(2)
+shouldn't close the file descriptor if it returns `EINTR`, but that's *not*
+true on Linux (and thus on Android). See
+[Returning EINTR from close()](https://lwn.net/Articles/576478/)
+for more discussion.
+
+Given that most Android code (and especially "all apps") are multithreaded,
+retrying close(2) is especially dangerous because the file descriptor might
+already have been reused by another thread, so the "retry" succeeds, but
+actually closes a *different* file descriptor belonging to a *different*
+thread.
+
+### Timeouts
+
+System calls with timeouts are the other interesting case where "just wrap
+everything with `TEMP_FAILURE_RETRY()`" doesn't work. Because some amount of
+time will have elapsed, you'll want to recalculate the timeout. Otherwise you
+can end up with your 1 minute timeout being indefinite if you're receiving
+signals at least once per minute, say. In this case you'll want to do
+something like adding an explicit loop around your system call, calculating
+the timeout _inside_ the loop, and using `continue` each time the system call
+fails with `EINTR`.
diff --git a/docs/fdsan.md b/docs/fdsan.md
index e0cf80a..6630299 100644
--- a/docs/fdsan.md
+++ b/docs/fdsan.md
@@ -3,6 +3,8 @@
[TOC]
fdsan is a file descriptor sanitizer added to Android in API level 29.
+In API level 29, fdsan warns when it finds a bug.
+In API level 30, fdsan aborts when it finds a bug.
### Background
*What problem is fdsan trying to solve? Why should I care?*
diff --git a/libc/Android.bp b/libc/Android.bp
index 00904aa..2dc10fc 100644
--- a/libc/Android.bp
+++ b/libc/Android.bp
@@ -67,6 +67,13 @@
// GWP-ASan requires platform TLS.
"-fno-emulated-tls",
+
+ // We know clang does a lot of harm by rewriting what we've said, and sadly
+ // never see any good it does, so let's just ask it to do what we say...
+ // (The specific motivating example was clang turning a loop that would only
+ // ever touch 0, 1, or 2 bytes into a call to memset, which was never going
+ // to amortize.)
+ "-fno-builtin",
]
// Define some common cflags
diff --git a/libc/SYSCALLS.TXT b/libc/SYSCALLS.TXT
index 22b82f1..a09c614 100644
--- a/libc/SYSCALLS.TXT
+++ b/libc/SYSCALLS.TXT
@@ -306,6 +306,8 @@
int sysinfo(struct sysinfo*) all
int personality(unsigned long) all
+int bpf(int, union bpf_attr *, unsigned int) all
+
ssize_t tee(int, int, size_t, unsigned int) all
ssize_t splice(int, off64_t*, int, off64_t*, size_t, unsigned int) all
ssize_t vmsplice(int, const struct iovec*, size_t, unsigned int) all
diff --git a/libc/bionic/c32rtomb.cpp b/libc/bionic/c32rtomb.cpp
index d2519b9..4fa76ff 100644
--- a/libc/bionic/c32rtomb.cpp
+++ b/libc/bionic/c32rtomb.cpp
@@ -66,10 +66,8 @@
// about the sequence length.
uint8_t lead;
size_t length;
- if ((c32 & ~0x7f) == 0) {
- lead = 0;
- length = 1;
- } else if ((c32 & ~0x7ff) == 0) {
+ // We already handled the 1-byte case above, so we go straight to 2-bytes...
+ if ((c32 & ~0x7ff) == 0) {
lead = 0xc0;
length = 2;
} else if ((c32 & ~0xffff) == 0) {
diff --git a/libc/bionic/mbrtoc32.cpp b/libc/bionic/mbrtoc32.cpp
index 21603a1..d37ca66 100644
--- a/libc/bionic/mbrtoc32.cpp
+++ b/libc/bionic/mbrtoc32.cpp
@@ -80,11 +80,8 @@
// The first byte in the state (if any) tells the length.
size_t bytes_so_far = mbstate_bytes_so_far(state);
ch = bytes_so_far > 0 ? mbstate_get_byte(state, 0) : static_cast<uint8_t>(*s);
- if ((ch & 0x80) == 0) {
- mask = 0x7f;
- length = 1;
- lower_bound = 0;
- } else if ((ch & 0xe0) == 0xc0) {
+ // We already handled the 1-byte case above, so we go straight to 2-bytes...
+ if ((ch & 0xe0) == 0xc0) {
mask = 0x1f;
length = 2;
lower_bound = 0x80;
diff --git a/libc/include/malloc.h b/libc/include/malloc.h
index f7beb2c..40786fa 100644
--- a/libc/include/malloc.h
+++ b/libc/include/malloc.h
@@ -97,30 +97,31 @@
*/
size_t malloc_usable_size(const void* __ptr) __INTRODUCED_IN(17);
+#define __MALLINFO_BODY \
+ /** Total number of non-mmapped bytes currently allocated from OS. */ \
+ size_t arena; \
+ /** Number of free chunks. */ \
+ size_t ordblks; \
+ /** (Unused.) */ \
+ size_t smblks; \
+ /** (Unused.) */ \
+ size_t hblks; \
+ /** Total number of bytes in mmapped regions. */ \
+ size_t hblkhd; \
+ /** Maximum total allocated space; greater than total if trimming has occurred. */ \
+ size_t usmblks; \
+ /** (Unused.) */ \
+ size_t fsmblks; \
+ /** Total allocated space (normal or mmapped.) */ \
+ size_t uordblks; \
+ /** Total free space. */ \
+ size_t fordblks; \
+ /** Upper bound on number of bytes releasable by a trim operation. */ \
+ size_t keepcost;
+
#ifndef STRUCT_MALLINFO_DECLARED
#define STRUCT_MALLINFO_DECLARED 1
-struct mallinfo {
- /** Total number of non-mmapped bytes currently allocated from OS. */
- size_t arena;
- /** Number of free chunks. */
- size_t ordblks;
- /** (Unused.) */
- size_t smblks;
- /** (Unused.) */
- size_t hblks;
- /** Total number of bytes in mmapped regions. */
- size_t hblkhd;
- /** Maximum total allocated space; greater than total if trimming has occurred. */
- size_t usmblks;
- /** (Unused.) */
- size_t fsmblks;
- /** Total allocated space (normal or mmapped.) */
- size_t uordblks;
- /** Total free space. */
- size_t fordblks;
- /** Upper bound on number of bytes releasable by a trim operation. */
- size_t keepcost;
-};
+struct mallinfo { __MALLINFO_BODY };
#endif
/**
@@ -131,6 +132,18 @@
struct mallinfo mallinfo(void);
/**
+ * On Android the struct mallinfo and struct mallinfo2 are the same.
+ */
+struct mallinfo2 { __MALLINFO_BODY };
+
+/**
+ * [mallinfo2(3)](http://man7.org/linux/man-pages/man3/mallinfo2.3.html) returns
+ * information about the current state of the heap. Note that mallinfo2() is
+ * inherently unreliable and consider using malloc_info() instead.
+ */
+struct mallinfo2 mallinfo2(void) __RENAME(mallinfo);
+
+/**
* [malloc_info(3)](http://man7.org/linux/man-pages/man3/malloc_info.3.html)
* writes information about the current state of the heap to the given stream.
*
diff --git a/libc/include/sched.h b/libc/include/sched.h
index 3260231..364ca10 100644
--- a/libc/include/sched.h
+++ b/libc/include/sched.h
@@ -26,8 +26,12 @@
* SUCH DAMAGE.
*/
-#ifndef _SCHED_H_
-#define _SCHED_H_
+#pragma once
+
+/**
+ * @file sched.h
+ * @brief Thread execution scheduling.
+ */
#include <bits/timespec.h>
#include <linux/sched.h>
@@ -35,29 +39,170 @@
__BEGIN_DECLS
-/* This name is used by glibc, but not by the kernel. */
+/*
+ * @def SCHED_NORMAL
+ * The standard (as opposed to real-time) round-robin scheduling policy.
+ *
+ * (Linux's name for POSIX's SCHED_OTHER.)
+ *
+ * See [sched(7)](http://man7.org/linux/man-pages/man7/sched.7.html)
+ */
+
+/*
+ * @def SCHED_FIFO
+ * The real-time first-in/first-out scheduling policy.
+ *
+ * See [sched(7)](http://man7.org/linux/man-pages/man7/sched.7.html)
+ */
+
+/*
+ * @def SCHED_RR
+ * The real-time round-robin policy. (See also SCHED_NORMAL/SCHED_OTHER.)
+ *
+ * See [sched(7)](http://man7.org/linux/man-pages/man7/sched.7.html)
+ */
+
+/*
+ * @def SCHED_BATCH
+ * The batch scheduling policy.
+ *
+ * See [sched(7)](http://man7.org/linux/man-pages/man7/sched.7.html)
+ */
+
+/*
+ * @def SCHED_IDLE
+ * The low priority "only when otherwise idle" scheduling priority.
+ *
+ * See [sched(7)](http://man7.org/linux/man-pages/man7/sched.7.html)
+ */
+
+/*
+ * @def SCHED_DEADLINE
+ * The deadline scheduling policy.
+ *
+ * See [sched(7)](http://man7.org/linux/man-pages/man7/sched.7.html)
+ */
+
+/*
+ * The standard (as opposed to real-time) round-robin scheduling policy.
+ *
+ * (POSIX's name for Linux's SCHED_NORMAL.)
+ */
#define SCHED_OTHER SCHED_NORMAL
+/**
+ * See sched_getparam()/sched_setparam() and
+ * sched_getscheduler()/sched_setscheduler().
+ */
struct sched_param {
int sched_priority;
};
+/**
+ * [sched_setscheduler(2)](http://man7.org/linux/man-pages/man2/sched_getcpu.2.html)
+ * sets the scheduling policy and associated parameters for the given thread.
+ *
+ * Returns 0 on success and returns -1 and sets `errno` on failure.
+ */
int sched_setscheduler(pid_t __pid, int __policy, const struct sched_param* __param);
+
+/**
+ * [sched_getscheduler(2)](http://man7.org/linux/man-pages/man2/sched_getcpu.2.html)
+ * gets the scheduling policy for the given thread.
+ *
+ * Returns a non-negative thread policy on success and returns -1 and sets
+ * `errno` on failure.
+ */
int sched_getscheduler(pid_t __pid);
+
+/**
+ * [sched_yield(2)](http://man7.org/linux/man-pages/man2/sched_yield.2.html)
+ * voluntarily gives up using the CPU so that another thread can run.
+ *
+ * Returns 0 on success and returns -1 and sets `errno` on failure.
+ */
int sched_yield(void);
+
+/**
+ * [sched_get_priority_max(2)](http://man7.org/linux/man-pages/man2/sched_get_priority_max.2.html)
+ * gets the maximum priority value allowed for the given scheduling policy.
+ *
+ * Returns a priority on success and returns -1 and sets `errno` on failure.
+ */
int sched_get_priority_max(int __policy);
+
+/**
+ * [sched_get_priority_min(2)](http://man7.org/linux/man-pages/man2/sched_get_priority_min.2.html)
+ * gets the minimum priority value allowed for the given scheduling policy.
+ *
+ * Returns a priority on success and returns -1 and sets `errno` on failure.
+ */
int sched_get_priority_min(int __policy);
+
+/**
+ * [sched_setparam(2)](http://man7.org/linux/man-pages/man2/sched_setparam.2.html)
+ * sets the scheduling parameters for the given thread.
+ *
+ * Returns 0 on success and returns -1 and sets `errno` on failure.
+ */
int sched_setparam(pid_t __pid, const struct sched_param* __param);
+
+/**
+ * [sched_getparam(2)](http://man7.org/linux/man-pages/man2/sched_getparam.2.html)
+ * gets the scheduling parameters for the given thread.
+ *
+ * Returns 0 on success and returns -1 and sets `errno` on failure.
+ */
int sched_getparam(pid_t __pid, struct sched_param* __param);
+
+/**
+ * [sched_rr_get_interval(2)](http://man7.org/linux/man-pages/man2/sched_rr_get_interval.2.html)
+ * queries the round-robin time quantum for the given thread.
+ *
+ * Returns 0 on success and returns -1 and sets `errno` on failure.
+ */
int sched_rr_get_interval(pid_t __pid, struct timespec* __quantum);
#if defined(__USE_GNU)
+/**
+ * [clone(2)](http://man7.org/linux/man-pages/man2/clone.2.html)
+ * creates a new child process.
+ *
+ * Returns the pid of the child to the caller on success and
+ * returns -1 and sets `errno` on failure.
+ */
int clone(int (*__fn)(void*), void* __child_stack, int __flags, void* __arg, ...) __INTRODUCED_IN_ARM(9) __INTRODUCED_IN_X86(17);
+
+/**
+ * [unshare(2)](http://man7.org/linux/man-pages/man2/unshare.2.html)
+ * disassociates part of the caller's execution context.
+ *
+ * Returns 0 on success and returns -1 and sets `errno` on failure.
+ *
+ * Available since API level 17.
+ */
int unshare(int __flags) __INTRODUCED_IN(17);
-int sched_getcpu(void);
+
+/**
+ * [setns(2)](http://man7.org/linux/man-pages/man2/setns.2.html)
+ * reassociates a thread with a different namespace.
+ *
+ * Returns 0 on success and returns -1 and sets `errno` on failure.
+ *
+ * Available since API level 21.
+ */
int setns(int __fd, int __ns_type) __INTRODUCED_IN(21);
+/**
+ * [sched_getcpu(3)](http://man7.org/linux/man-pages/man3/sched_getcpu.3.html)
+ * reports which CPU the caller is running on.
+ *
+ * Returns a non-negative CPU number on success and returns -1 and sets
+ * `errno` on failure.
+ */
+int sched_getcpu(void);
+
#ifdef __LP64__
#define CPU_SETSIZE 1024
#else
@@ -69,39 +214,50 @@
#define __CPU_ELT(x) ((x) / __CPU_BITS)
#define __CPU_MASK(x) ((__CPU_BITTYPE)1 << ((x) & (__CPU_BITS - 1)))
+/**
+ * [cpu_set_t](https://man7.org/linux/man-pages/man3/CPU_SET.3.html) is a
+ * statically-sized CPU set. See `CPU_ALLOC` for dynamically-sized CPU sets.
+ */
typedef struct {
__CPU_BITTYPE __bits[ CPU_SETSIZE / __CPU_BITS ];
} cpu_set_t;
+/**
+ * [sched_setaffinity(2)](http://man7.org/linux/man-pages/man2/sched_setaffinity.2.html)
+ * sets the CPU affinity mask for the given thread.
+ *
+ * Returns 0 on success and returns -1 and sets `errno` on failure.
+ */
int sched_setaffinity(pid_t __pid, size_t __set_size, const cpu_set_t* __set);
+
+/**
+ * [sched_getaffinity(2)](http://man7.org/linux/man-pages/man2/sched_getaffinity.2.html)
+ * gets the CPU affinity mask for the given thread.
+ *
+ * Returns 0 on success and returns -1 and sets `errno` on failure.
+ */
int sched_getaffinity(pid_t __pid, size_t __set_size, cpu_set_t* __set);
+/**
+ * [CPU_ZERO](https://man7.org/linux/man-pages/man3/CPU_SET.3.html) clears all
+ * bits in a static CPU set.
+ */
#define CPU_ZERO(set) CPU_ZERO_S(sizeof(cpu_set_t), set)
-#define CPU_SET(cpu, set) CPU_SET_S(cpu, sizeof(cpu_set_t), set)
-#define CPU_CLR(cpu, set) CPU_CLR_S(cpu, sizeof(cpu_set_t), set)
-#define CPU_ISSET(cpu, set) CPU_ISSET_S(cpu, sizeof(cpu_set_t), set)
-#define CPU_COUNT(set) CPU_COUNT_S(sizeof(cpu_set_t), set)
-#define CPU_EQUAL(set1, set2) CPU_EQUAL_S(sizeof(cpu_set_t), set1, set2)
-
-#define CPU_AND(dst, set1, set2) __CPU_OP(dst, set1, set2, &)
-#define CPU_OR(dst, set1, set2) __CPU_OP(dst, set1, set2, |)
-#define CPU_XOR(dst, set1, set2) __CPU_OP(dst, set1, set2, ^)
-
-#define __CPU_OP(dst, set1, set2, op) __CPU_OP_S(sizeof(cpu_set_t), dst, set1, set2, op)
-
-/* Support for dynamically-allocated cpu_set_t */
-
-#define CPU_ALLOC_SIZE(count) \
- __CPU_ELT((count) + (__CPU_BITS - 1)) * sizeof(__CPU_BITTYPE)
-
-#define CPU_ALLOC(count) __sched_cpualloc((count))
-#define CPU_FREE(set) __sched_cpufree((set))
-
-cpu_set_t* __sched_cpualloc(size_t __count);
-void __sched_cpufree(cpu_set_t* __set);
-
+/**
+ * [CPU_ZERO_S](https://man7.org/linux/man-pages/man3/CPU_SET.3.html) clears all
+ * bits in a dynamic CPU set allocated by `CPU_ALLOC`.
+ */
#define CPU_ZERO_S(setsize, set) __builtin_memset(set, 0, setsize)
+/**
+ * [CPU_SET](https://man7.org/linux/man-pages/man3/CPU_SET.3.html) sets one
+ * bit in a static CPU set.
+ */
+#define CPU_SET(cpu, set) CPU_SET_S(cpu, sizeof(cpu_set_t), set)
+/**
+ * [CPU_SET_S](https://man7.org/linux/man-pages/man3/CPU_SET.3.html) sets one
+ * bit in a dynamic CPU set allocated by `CPU_ALLOC`.
+ */
#define CPU_SET_S(cpu, setsize, set) \
do { \
size_t __cpu = (cpu); \
@@ -109,6 +265,15 @@
(set)->__bits[__CPU_ELT(__cpu)] |= __CPU_MASK(__cpu); \
} while (0)
+/**
+ * [CPU_CLR](https://man7.org/linux/man-pages/man3/CPU_SET.3.html) clears one
+ * bit in a static CPU set.
+ */
+#define CPU_CLR(cpu, set) CPU_CLR_S(cpu, sizeof(cpu_set_t), set)
+/**
+ * [CPU_CLR_S](https://man7.org/linux/man-pages/man3/CPU_SET.3.html) clears one
+ * bit in a dynamic CPU set allocated by `CPU_ALLOC`.
+ */
#define CPU_CLR_S(cpu, setsize, set) \
do { \
size_t __cpu = (cpu); \
@@ -116,6 +281,15 @@
(set)->__bits[__CPU_ELT(__cpu)] &= ~__CPU_MASK(__cpu); \
} while (0)
+/**
+ * [CPU_ISSET](https://man7.org/linux/man-pages/man3/CPU_SET.3.html) tests
+ * whether the given bit is set in a static CPU set.
+ */
+#define CPU_ISSET(cpu, set) CPU_ISSET_S(cpu, sizeof(cpu_set_t), set)
+/**
+ * [CPU_ISSET_S](https://man7.org/linux/man-pages/man3/CPU_SET.3.html) tests
+ * whether the given bit is set in a dynamic CPU set allocated by `CPU_ALLOC`.
+ */
#define CPU_ISSET_S(cpu, setsize, set) \
(__extension__ ({ \
size_t __cpu = (cpu); \
@@ -124,12 +298,65 @@
: 0; \
}))
+/**
+ * [CPU_COUNT](https://man7.org/linux/man-pages/man3/CPU_SET.3.html) counts
+ * how many bits are set in a static CPU set.
+ */
+#define CPU_COUNT(set) CPU_COUNT_S(sizeof(cpu_set_t), set)
+/**
+ * [CPU_COUNT_S](https://man7.org/linux/man-pages/man3/CPU_SET.3.html) counts
+ * how many bits are set in a dynamic CPU set allocated by `CPU_ALLOC`.
+ */
+#define CPU_COUNT_S(setsize, set) __sched_cpucount((setsize), (set))
+int __sched_cpucount(size_t __set_size, const cpu_set_t* __set);
+
+/**
+ * [CPU_EQUAL](https://man7.org/linux/man-pages/man3/CPU_SET.3.html) tests
+ * whether two static CPU sets have the same bits set and cleared as each other.
+ */
+#define CPU_EQUAL(set1, set2) CPU_EQUAL_S(sizeof(cpu_set_t), set1, set2)
+/**
+ * [CPU_EQUAL_S](https://man7.org/linux/man-pages/man3/CPU_SET.3.html) tests
+ * whether two dynamic CPU sets allocated by `CPU_ALLOC` have the same bits
+ * set and cleared as each other.
+ */
#define CPU_EQUAL_S(setsize, set1, set2) (__builtin_memcmp(set1, set2, setsize) == 0)
+/**
+ * [CPU_AND](https://man7.org/linux/man-pages/man3/CPU_SET.3.html) ands two
+ * static CPU sets.
+ */
+#define CPU_AND(dst, set1, set2) __CPU_OP(dst, set1, set2, &)
+/**
+ * [CPU_AND_S](https://man7.org/linux/man-pages/man3/CPU_SET.3.html) ands two
+ * dynamic CPU sets allocated by `CPU_ALLOC`.
+ */
#define CPU_AND_S(setsize, dst, set1, set2) __CPU_OP_S(setsize, dst, set1, set2, &)
+
+/**
+ * [CPU_OR](https://man7.org/linux/man-pages/man3/CPU_SET.3.html) ors two
+ * static CPU sets.
+ */
+#define CPU_OR(dst, set1, set2) __CPU_OP(dst, set1, set2, |)
+/**
+ * [CPU_OR_S](https://man7.org/linux/man-pages/man3/CPU_SET.3.html) ors two
+ * dynamic CPU sets allocated by `CPU_ALLOC`.
+ */
#define CPU_OR_S(setsize, dst, set1, set2) __CPU_OP_S(setsize, dst, set1, set2, |)
+
+/**
+ * [CPU_XOR](https://man7.org/linux/man-pages/man3/CPU_SET.3.html)
+ * exclusive-ors two static CPU sets.
+ */
+#define CPU_XOR(dst, set1, set2) __CPU_OP(dst, set1, set2, ^)
+/**
+ * [CPU_XOR_S](https://man7.org/linux/man-pages/man3/CPU_SET.3.html)
+ * exclusive-ors two dynamic CPU sets allocated by `CPU_ALLOC`.
+ */
#define CPU_XOR_S(setsize, dst, set1, set2) __CPU_OP_S(setsize, dst, set1, set2, ^)
+#define __CPU_OP(dst, set1, set2, op) __CPU_OP_S(sizeof(cpu_set_t), dst, set1, set2, op)
+
#define __CPU_OP_S(setsize, dstset, srcset1, srcset2, op) \
do { \
cpu_set_t* __dst = (dstset); \
@@ -140,12 +367,27 @@
(__dst)->__bits[__nn] = __src1[__nn] op __src2[__nn]; \
} while (0)
-#define CPU_COUNT_S(setsize, set) __sched_cpucount((setsize), (set))
+/**
+ * [CPU_ALLOC_SIZE](https://man7.org/linux/man-pages/man3/CPU_SET.3.html)
+ * returns the size of a CPU set large enough for CPUs in the range 0..count-1.
+ */
+#define CPU_ALLOC_SIZE(count) \
+ __CPU_ELT((count) + (__CPU_BITS - 1)) * sizeof(__CPU_BITTYPE)
-int __sched_cpucount(size_t __set_size, const cpu_set_t* __set);
+/**
+ * [CPU_ALLOC](https://man7.org/linux/man-pages/man3/CPU_SET.3.html)
+ * allocates a CPU set large enough for CPUs in the range 0..count-1.
+ */
+#define CPU_ALLOC(count) __sched_cpualloc((count))
+cpu_set_t* __sched_cpualloc(size_t __count);
+
+/**
+ * [CPU_FREE](https://man7.org/linux/man-pages/man3/CPU_SET.3.html)
+ * deallocates a CPU set allocated by `CPU_ALLOC`.
+ */
+#define CPU_FREE(set) __sched_cpufree((set))
+void __sched_cpufree(cpu_set_t* __set);
#endif /* __USE_GNU */
__END_DECLS
-
-#endif /* _SCHED_H_ */
diff --git a/libc/tzcode/strftime.c b/libc/tzcode/strftime.c
index c05f6b5..7c4be49 100644
--- a/libc/tzcode/strftime.c
+++ b/libc/tzcode/strftime.c
@@ -252,8 +252,8 @@
pt = _fmt("%m/%d/%y", t, pt, ptlim, warnp);
continue;
case 'd':
- pt = _conv(t->tm_mday, getformat(modifier, "%02d", "%2d", "%d", "%02d"), pt, ptlim);
- continue;
+ pt = _conv(t->tm_mday, getformat(modifier, "02", " 2", " ", "02"), pt, ptlim);
+ continue;
case 'E':
case 'O':
/*
@@ -274,22 +274,21 @@
modifier = *format;
goto label;
case 'e':
- pt = _conv(t->tm_mday, getformat(modifier, "%2d", "%2d", "%d", "%02d"), pt, ptlim);
- continue;
+ pt = _conv(t->tm_mday, getformat(modifier, " 2", " 2", " ", "02"), pt, ptlim);
+ continue;
case 'F':
pt = _fmt("%Y-%m-%d", t, pt, ptlim, warnp);
continue;
case 'H':
- pt = _conv(t->tm_hour, getformat(modifier, "%02d", "%2d", "%d", "%02d"), pt, ptlim);
- continue;
+ pt = _conv(t->tm_hour, getformat(modifier, "02", " 2", " ", "02"), pt, ptlim);
+ continue;
case 'I':
- pt = _conv((t->tm_hour % 12) ?
- (t->tm_hour % 12) : 12,
- getformat(modifier, "%02d", "%2d", "%d", "%02d"), pt, ptlim);
- continue;
+ pt = _conv((t->tm_hour % 12) ? (t->tm_hour % 12) : 12,
+ getformat(modifier, "02", " 2", " ", "02"), pt, ptlim);
+ continue;
case 'j':
- pt = _conv(t->tm_yday + 1, getformat(modifier, "%03d", "%3d", "%d", "%03d"), pt, ptlim);
- continue;
+ pt = _conv(t->tm_yday + 1, getformat(modifier, "03", " 3", " ", "03"), pt, ptlim);
+ continue;
case 'k':
/*
** This used to be...
@@ -301,7 +300,7 @@
** "%l" have been swapped.
** (ado, 1993-05-24)
*/
- pt = _conv(t->tm_hour, getformat(modifier, "%2d", "%2d", "%d", "%02d"), pt, ptlim);
+ pt = _conv(t->tm_hour, getformat(modifier, " 2", " 2", " ", "02"), pt, ptlim);
continue;
#ifdef KITCHEN_SINK
case 'K':
@@ -321,16 +320,15 @@
** "%l" have been swapped.
** (ado, 1993-05-24)
*/
- pt = _conv((t->tm_hour % 12) ?
- (t->tm_hour % 12) : 12,
- getformat(modifier, "%2d", "%2d", "%d", "%02d"), pt, ptlim);
+ pt = _conv((t->tm_hour % 12) ? (t->tm_hour % 12) : 12,
+ getformat(modifier, " 2", " 2", " ", "02"), pt, ptlim);
continue;
case 'M':
- pt = _conv(t->tm_min, getformat(modifier, "%02d", "%2d", "%d", "%02d"), pt, ptlim);
- continue;
+ pt = _conv(t->tm_min, getformat(modifier, "02", " 2", " ", "02"), pt, ptlim);
+ continue;
case 'm':
- pt = _conv(t->tm_mon + 1, getformat(modifier, "%02d", "%2d", "%d", "%02d"), pt, ptlim);
- continue;
+ pt = _conv(t->tm_mon + 1, getformat(modifier, "02", " 2", " ", "02"), pt, ptlim);
+ continue;
case 'n':
pt = _add("\n", pt, ptlim, modifier);
continue;
@@ -348,13 +346,12 @@
pt = _fmt("%I:%M:%S %p", t, pt, ptlim, warnp);
continue;
case 'S':
- pt = _conv(t->tm_sec, getformat(modifier, "%02d", "%2d", "%d", "%02d"), pt, ptlim);
- continue;
+ pt = _conv(t->tm_sec, getformat(modifier, "02", " 2", " ", "02"), pt, ptlim);
+ continue;
case 's':
{
struct tm tm;
- char buf[INT_STRLEN_MAXIMUM(
- time64_t) + 1];
+ char buf[INT_STRLEN_MAXIMUM(time64_t) + 1] __attribute__((__uninitialized__));
time64_t mkt;
tm = *t;
@@ -374,10 +371,9 @@
pt = _add("\t", pt, ptlim, modifier);
continue;
case 'U':
- pt = _conv((t->tm_yday + DAYSPERWEEK -
- t->tm_wday) / DAYSPERWEEK,
- getformat(modifier, "%02d", "%2d", "%d", "%02d"), pt, ptlim);
- continue;
+ pt = _conv((t->tm_yday + DAYSPERWEEK - t->tm_wday) / DAYSPERWEEK,
+ getformat(modifier, "02", " 2", " ", "02"), pt, ptlim);
+ continue;
case 'u':
/*
** From Arnold Robbins' strftime version 3.0:
@@ -385,9 +381,7 @@
** [1 (Monday) - 7]"
** (ado, 1993-05-24)
*/
- pt = _conv((t->tm_wday == 0) ?
- DAYSPERWEEK : t->tm_wday,
- "%d", pt, ptlim);
+ pt = _conv((t->tm_wday == 0) ? DAYSPERWEEK : t->tm_wday, " ", pt, ptlim);
continue;
case 'V': /* ISO 8601 week number */
case 'G': /* ISO 8601 year (four digits) */
@@ -467,8 +461,7 @@
w = 53;
#endif /* defined XPG4_1994_04_09 */
if (*format == 'V')
- pt = _conv(w, getformat(modifier, "%02d", "%2d", "%d", "%02d"),
- pt, ptlim);
+ pt = _conv(w, getformat(modifier, "02", " 2", " ", "02"), pt, ptlim);
else if (*format == 'g') {
*warnp = IN_ALL;
pt = _yconv(year, base,
@@ -488,15 +481,14 @@
pt = _fmt("%e-%b-%Y", t, pt, ptlim, warnp);
continue;
case 'W':
- pt = _conv((t->tm_yday + DAYSPERWEEK -
- (t->tm_wday ?
- (t->tm_wday - 1) :
- (DAYSPERWEEK - 1))) / DAYSPERWEEK,
- getformat(modifier, "%02d", "%2d", "%d", "%02d"), pt, ptlim);
- continue;
+ pt = _conv(
+ (t->tm_yday + DAYSPERWEEK - (t->tm_wday ? (t->tm_wday - 1) : (DAYSPERWEEK - 1))) /
+ DAYSPERWEEK,
+ getformat(modifier, "02", " 2", " ", "02"), pt, ptlim);
+ continue;
case 'w':
- pt = _conv(t->tm_wday, "%d", pt, ptlim);
- continue;
+ pt = _conv(t->tm_wday, " ", pt, ptlim);
+ continue;
case 'X':
pt = _fmt(Locale->X_fmt, t, pt, ptlim, warnp);
continue;
@@ -602,7 +594,7 @@
diff /= SECSPERMIN;
diff = (diff / MINSPERHOUR) * 100 +
(diff % MINSPERHOUR);
- pt = _conv(diff, getformat(modifier, "%04d", "%4d", "%d", "%04d"), pt, ptlim);
+ pt = _conv(diff, getformat(modifier, "04", " 4", " ", "04"), pt, ptlim);
}
continue;
case '+':
@@ -629,10 +621,40 @@
static char *
_conv(int n, const char *format, char *pt, const char *ptlim)
{
- char buf[INT_STRLEN_MAXIMUM(int) + 1];
+ // The original implementation used snprintf(3) here, but rolling our own is
+ // about 5x faster. Seems like a good trade-off for so little code, especially
+ // for users like logcat that have a habit of formatting 10k times all at
+ // once...
- snprintf(buf, sizeof(buf), format, n);
- return _add(buf, pt, ptlim, 0);
+ // Format is '0' or ' ' for the fill character, followed by a single-digit
+ // width or ' ' for "whatever".
+ // %d -> " "
+ // %2d -> " 2"
+ // %02d -> "02"
+ char fill = format[0];
+ int width = format[1] == ' ' ? 0 : format[1] - '0';
+
+ char buf[32] __attribute__((__uninitialized__));
+
+ // Terminate first, so we can walk backwards from the least-significant digit
+ // without having to later reverse the result.
+ char* p = &buf[31];
+ *--p = '\0';
+ char* end = p;
+
+ // Output digits backwards, from least-significant to most.
+ while (n >= 10) {
+ *--p = '0' + (n % 10);
+ n /= 10;
+ }
+ *--p = '0' + n;
+
+ // Fill if more digits are required by the format.
+ while ((end - p) < width) {
+ *--p = fill;
+ }
+
+ return _add(p, pt, ptlim, 0);
}
static char *
@@ -704,9 +726,11 @@
if (convert_top) {
if (lead == 0 && trail < 0)
pt = _add("-0", pt, ptlim, modifier);
- else pt = _conv(lead, getformat(modifier, "%02d", "%2d", "%d", "%02d"), pt, ptlim);
+ else
+ pt = _conv(lead, getformat(modifier, "02", " 2", " ", "02"), pt, ptlim);
}
if (convert_yy)
- pt = _conv(((trail < 0) ? -trail : trail), getformat(modifier, "%02d", "%2d", "%d", "%02d"), pt, ptlim);
+ pt = _conv(((trail < 0) ? -trail : trail), getformat(modifier, "02", " 2", " ", "02"), pt,
+ ptlim);
return pt;
}
diff --git a/libm/Android.bp b/libm/Android.bp
index b6ad356..83f40c2 100644
--- a/libm/Android.bp
+++ b/libm/Android.bp
@@ -490,6 +490,7 @@
"-DFLT_EVAL_METHOD=0",
"-include freebsd-compat.h",
"-include fenv-access.h",
+ "-fno-builtin",
"-fno-math-errno",
"-Wall",
"-Werror",
diff --git a/tests/malloc_test.cpp b/tests/malloc_test.cpp
index 5e8fb9f..8272d39 100644
--- a/tests/malloc_test.cpp
+++ b/tests/malloc_test.cpp
@@ -809,6 +809,77 @@
#endif
}
+TEST(malloc, mallinfo2) {
+#if defined(__BIONIC__)
+ SKIP_WITH_HWASAN << "hwasan does not implement mallinfo2";
+ static size_t sizes[] = {8, 32, 128, 4096, 32768, 131072, 1024000, 10240000, 20480000, 300000000};
+
+ constexpr static size_t kMaxAllocs = 50;
+
+ for (size_t size : sizes) {
+ // If some of these allocations are stuck in a thread cache, then keep
+ // looping until we make an allocation that changes the total size of the
+ // memory allocated.
+ // jemalloc implementations counts the thread cache allocations against
+ // total memory allocated.
+ void* ptrs[kMaxAllocs] = {};
+ bool pass = false;
+ for (size_t i = 0; i < kMaxAllocs; i++) {
+ struct mallinfo info = mallinfo();
+ struct mallinfo2 info2 = mallinfo2();
+ // Verify that mallinfo and mallinfo2 are exactly the same.
+ ASSERT_EQ(info.arena, info2.arena);
+ ASSERT_EQ(info.ordblks, info2.ordblks);
+ ASSERT_EQ(info.smblks, info2.smblks);
+ ASSERT_EQ(info.hblks, info2.hblks);
+ ASSERT_EQ(info.hblkhd, info2.hblkhd);
+ ASSERT_EQ(info.usmblks, info2.usmblks);
+ ASSERT_EQ(info.fsmblks, info2.fsmblks);
+ ASSERT_EQ(info.uordblks, info2.uordblks);
+ ASSERT_EQ(info.fordblks, info2.fordblks);
+ ASSERT_EQ(info.keepcost, info2.keepcost);
+
+ size_t allocated = info2.uordblks;
+ ptrs[i] = malloc(size);
+ ASSERT_TRUE(ptrs[i] != nullptr);
+
+ info = mallinfo();
+ info2 = mallinfo2();
+ // Verify that mallinfo and mallinfo2 are exactly the same.
+ ASSERT_EQ(info.arena, info2.arena);
+ ASSERT_EQ(info.ordblks, info2.ordblks);
+ ASSERT_EQ(info.smblks, info2.smblks);
+ ASSERT_EQ(info.hblks, info2.hblks);
+ ASSERT_EQ(info.hblkhd, info2.hblkhd);
+ ASSERT_EQ(info.usmblks, info2.usmblks);
+ ASSERT_EQ(info.fsmblks, info2.fsmblks);
+ ASSERT_EQ(info.uordblks, info2.uordblks);
+ ASSERT_EQ(info.fordblks, info2.fordblks);
+ ASSERT_EQ(info.keepcost, info2.keepcost);
+
+ size_t new_allocated = info2.uordblks;
+ if (allocated != new_allocated) {
+ size_t usable_size = malloc_usable_size(ptrs[i]);
+ // Only check if the total got bigger by at least allocation size.
+ // Sometimes the mallinfo2 numbers can go backwards due to compaction
+ // and/or freeing of cached data.
+ if (new_allocated >= allocated + usable_size) {
+ pass = true;
+ break;
+ }
+ }
+ }
+ for (void* ptr : ptrs) {
+ free(ptr);
+ }
+ ASSERT_TRUE(pass) << "For size " << size << " allocated bytes did not increase after "
+ << kMaxAllocs << " allocations.";
+ }
+#else
+ GTEST_SKIP() << "glibc is broken";
+#endif
+}
+
template <typename Type>
void __attribute__((optnone)) VerifyAlignment(Type* floating) {
size_t expected_alignment = alignof(Type);
diff --git a/tests/pthread_test.cpp b/tests/pthread_test.cpp
index e4f9cb8..907a35c 100644
--- a/tests/pthread_test.cpp
+++ b/tests/pthread_test.cpp
@@ -1493,6 +1493,7 @@
};
std::atomic<Progress> progress;
pthread_t thread;
+ timespec ts;
std::function<int (pthread_cond_t* cond, pthread_mutex_t* mutex)> wait_function;
protected:
@@ -1524,11 +1525,10 @@
clockid_t clock,
std::function<int(pthread_cond_t* cond, pthread_mutex_t* mutex, const timespec* timeout)>
wait_function) {
- timespec ts;
ASSERT_EQ(0, clock_gettime(clock, &ts));
ts.tv_sec += 1;
- StartWaitingThread([&wait_function, &ts](pthread_cond_t* cond, pthread_mutex_t* mutex) {
+ StartWaitingThread([&wait_function, this](pthread_cond_t* cond, pthread_mutex_t* mutex) {
return wait_function(cond, mutex, &ts);
});
diff --git a/tests/sys_ttydefaults_test.cpp b/tests/sys_ttydefaults_test.cpp
index fa4f7c7..73aa448 100644
--- a/tests/sys_ttydefaults_test.cpp
+++ b/tests/sys_ttydefaults_test.cpp
@@ -20,7 +20,7 @@
#include <termios.h>
TEST(sys_ttydefaults, flags) {
- int i;
+ [[maybe_unused]] int i;
i = TTYDEF_IFLAG;
i = TTYDEF_OFLAG;
i = TTYDEF_LFLAG;
diff --git a/tests/unistd_test.cpp b/tests/unistd_test.cpp
index 840dad0..c306a08 100644
--- a/tests/unistd_test.cpp
+++ b/tests/unistd_test.cpp
@@ -688,9 +688,9 @@
__attribute__((noinline)) static void HwasanReadMemory(const char* p, size_t size) {
// Read memory byte-by-byte. This will blow up if the pointer tag in p does not match any memory
// tag in [p, p+size).
- volatile char z;
+ char z;
for (size_t i = 0; i < size; ++i) {
- z = p[i];
+ DoNotOptimize(z = p[i]);
}
}
diff --git a/tools/versioner/src/Android.bp b/tools/versioner/src/Android.bp
index 0173c41..abcaaa3 100644
--- a/tools/versioner/src/Android.bp
+++ b/tools/versioner/src/Android.bp
@@ -21,8 +21,7 @@
],
shared_libs: [
- "libclang_cxx_host",
- "libLLVM_host",
+ "libclang-cpp_host",
"libbase",
],