18#include <sys/resource.h>
19#include <sys/syscall.h>
25struct linux_rusage_abi {
30 struct linux_rusage_abi
usage;
31 unsigned char canary[32];
34_Static_assert(
sizeof(
struct linux_rusage_abi) == 144,
"Linux amd64 rusage ABI changed");
35_Static_assert(RUSAGE_SELF == 0,
"musl RUSAGE_SELF selector changed");
36_Static_assert(RUSAGE_CHILDREN == -1,
"musl RUSAGE_CHILDREN selector changed");
37_Static_assert(RUSAGE_THREAD == 1,
"musl RUSAGE_THREAD selector changed");
38_Static_assert(offsetof(
struct rusage, __reserved) ==
sizeof(
struct linux_rusage_abi),
39 "musl rusage prefix no longer matches the Linux syscall ABI");
40_Static_assert(
sizeof(
struct rusage) ==
sizeof(
struct linux_rusage_abi) + 16 *
sizeof(long),
41 "musl rusage reserve changed");
44 int64_t user_microseconds;
45 int64_t system_microseconds;
49static int64_t timeval_microseconds(
struct timeval value) {
50 return (int64_t)value.tv_sec * 1000000 + value.tv_usec;
53static int public_rusage_tail_valid(
const struct rusage*
usage) {
54 for (
size_t i = offsetof(
struct rusage, ru_maxrss); i < offsetof(
struct rusage, __reserved);
56 if (((
const unsigned char*)
usage)[i])
59 for (
size_t i = offsetof(
struct rusage, __reserved); i <
sizeof(*usage); ++i) {
60 if (((
const unsigned char*)
usage)[i] != 0xA5)
66static int burn_observable_user_time(
void) {
69 if (getrusage(RUSAGE_SELF, &before))
71 const int64_t before_user = timeval_microseconds(before.ru_utime);
73 volatile uint64_t value = 1;
74 for (
size_t attempt = 0; attempt < 256; ++attempt) {
75 for (
size_t i = 0; i < 100000; ++i)
76 value = value * 1664525 + 1013904223;
77 if (getrusage(RUSAGE_SELF, &after))
79 if (timeval_microseconds(after.ru_utime) >= before_user + 20000)
86 const unsigned char* bytes = (
const unsigned char*)report;
87 size_t remaining =
sizeof(*report);
89 ssize_t written = write(descriptor, bytes, remaining);
90 if (written < 0 && errno == EINTR)
95 remaining -= (size_t)written;
101 unsigned char* bytes = (
unsigned char*)report;
102 size_t remaining =
sizeof(*report);
104 ssize_t received = read(descriptor, bytes, remaining);
105 if (received < 0 && errno == EINTR)
110 remaining -= (size_t)received;
115static void test_child_resource_accounting(
void) {
116 struct rusage children_before;
117 memset(&children_before, 0xA5,
sizeof(children_before));
118 if (getrusage(RUSAGE_CHILDREN, &children_before) || !public_rusage_tail_valid(&children_before))
120 const int64_t user_before = timeval_microseconds(children_before.ru_utime);
121 const int64_t system_before = timeval_microseconds(children_before.ru_stime);
123 struct tms times_before;
124 if (times(×_before) == (clock_t)-1 ||
125 times_before.tms_cutime !=
126 children_before.ru_utime.tv_sec * 100 + children_before.ru_utime.tv_usec / 10000 ||
127 times_before.tms_cstime !=
128 children_before.ru_stime.tv_sec * 100 + children_before.ru_stime.tv_usec / 10000)
132 if (pipe(report_pipe))
134 pid_t child = fork();
138 close(report_pipe[0]);
140 pid_t descendant = fork();
142 _exit(burn_observable_user_time() ? 124 : 0);
144 struct rusage waited_descendant;
145 memset(&waited_descendant, 0xA5,
sizeof(waited_descendant));
146 int descendant_status = 0;
147 if (descendant > 0 &&
148 wait4(descendant, &descendant_status, 0, &waited_descendant) == descendant &&
149 WIFEXITED(descendant_status) && !WEXITSTATUS(descendant_status) &&
150 public_rusage_tail_valid(&waited_descendant)) {
151 struct rusage descendants;
152 memset(&descendants, 0xA5,
sizeof(descendants));
153 if (!getrusage(RUSAGE_CHILDREN, &descendants) && public_rusage_tail_valid(&descendants)) {
154 report.user_microseconds = timeval_microseconds(descendants.ru_utime);
155 report.system_microseconds = timeval_microseconds(descendants.ru_stime);
157 report.user_microseconds >= timeval_microseconds(waited_descendant.ru_utime) &&
158 report.system_microseconds >= timeval_microseconds(waited_descendant.ru_stime) &&
159 report.user_microseconds > 0;
162 const int reported = write_report(report_pipe[1], &report);
163 close(report_pipe[1]);
164 _exit(!reported && report.valid ? 0 : 125);
167 close(report_pipe[1]);
169 const int report_read = read_report(report_pipe[0], &report);
170 close(report_pipe[0]);
171 struct rusage waited_child;
172 memset(&waited_child, 0xA5,
sizeof(waited_child));
173 int child_status = 0;
174 if (report_read || wait4(child, &child_status, 0, &waited_child) != child ||
175 !WIFEXITED(child_status) || WEXITSTATUS(child_status) || !report.valid ||
176 !public_rusage_tail_valid(&waited_child) ||
177 timeval_microseconds(waited_child.ru_utime) < report.user_microseconds ||
178 timeval_microseconds(waited_child.ru_stime) < report.system_microseconds)
181 struct rusage children_after;
182 memset(&children_after, 0xA5,
sizeof(children_after));
183 if (getrusage(RUSAGE_CHILDREN, &children_after) || !public_rusage_tail_valid(&children_after))
185 const int64_t user_added = timeval_microseconds(children_after.ru_utime) - user_before;
186 const int64_t system_added = timeval_microseconds(children_after.ru_stime) - system_before;
187 const int64_t waited_user = timeval_microseconds(waited_child.ru_utime);
188 const int64_t waited_system = timeval_microseconds(waited_child.ru_stime);
189 if (user_added + 1 < waited_user || user_added > waited_user + 1 ||
190 system_added + 1 < waited_system || system_added > waited_system + 1)
193 struct tms times_after;
194 if (times(×_after) == (clock_t)-1 ||
195 times_after.tms_cutime !=
196 children_after.ru_utime.tv_sec * 100 + children_after.ru_utime.tv_usec / 10000 ||
197 times_after.tms_cstime !=
198 children_after.ru_stime.tv_sec * 100 + children_after.ru_stime.tv_usec / 10000 ||
199 times_after.tms_cutime <= times_before.tms_cutime)
205 pid_t raw_child = fork();
213 received = read(gate[0], &token,
sizeof(token));
214 }
while (received < 0 && errno == EINTR);
216 _exit(received ==
sizeof(token) && token ==
'x' ? 0 : 126);
222 memset(&packet, 0xA5,
sizeof(packet));
223 untouched_packet = packet;
224 int raw_status = 0x5A5A5A5A;
226 if (syscall(SYS_wait4, raw_child, &raw_status, WNOHANG, packet.usage.slots) ||
227 raw_status != 0x5A5A5A5A || memcmp(&packet, &untouched_packet,
sizeof(packet)))
230 const char token =
'x';
231 if (write(gate[1], &token,
sizeof(token)) !=
sizeof(token) || close(gate[1]))
233 memset(&packet, 0xA5,
sizeof(packet));
234 if (syscall(SYS_wait4, raw_child, &raw_status, 0, packet.usage.slots) != raw_child ||
235 !WIFEXITED(raw_status) || WEXITSTATUS(raw_status) || packet.usage.slots[0] < 0 ||
236 packet.usage.slots[1] < 0 || packet.usage.slots[1] >= 1000000 || packet.usage.slots[2] < 0 ||
237 packet.usage.slots[3] < 0 || packet.usage.slots[3] >= 1000000)
239 for (
size_t i = 4; i < 18; ++i) {
240 if (packet.usage.slots[i])
243 for (
size_t i = 0; i <
sizeof(packet.canary); ++i) {
244 if (packet.canary[i] != 0xA5)
248 pid_t null_child = fork();
253 if (wait4(null_child, &child_status, 0, 0) != null_child || !WIFEXITED(child_status) ||
254 WEXITSTATUS(child_status))
257 memset(&packet, 0xA5,
sizeof(packet));
258 untouched_packet = packet;
259 raw_status = 0x5A5A5A5A;
261 if (syscall(SYS_wait4, raw_child, &raw_status, WNOHANG, packet.usage.slots) != -1 ||
262 errno != ECHILD || raw_status != 0x5A5A5A5A ||
263 memcmp(&packet, &untouched_packet,
sizeof(packet)))
267void test_resource_accounting(
void) {
268 puts(
"Testing process resource accounting... ");
271 struct tms processTimes = {0};
272 const clock_t elapsed = times(&processTimes);
273 if (elapsed == (clock_t)-1 || processTimes.tms_utime < 0 || processTimes.tms_stime < 0 ||
274 processTimes.tms_cutime || processTimes.tms_cstime)
277 if (times(0) < elapsed)
280 struct rusage
usage = {0};
281 if (getrusage(RUSAGE_SELF, &
usage) ||
usage.ru_utime.tv_sec < 0 ||
usage.ru_utime.tv_usec < 0 ||
282 usage.ru_utime.tv_usec >= 1000000 ||
usage.ru_stime.tv_sec < 0 ||
283 usage.ru_stime.tv_usec < 0 ||
usage.ru_stime.tv_usec >= 1000000)
286 const clock_t userTicks =
usage.ru_utime.tv_sec * 100 +
usage.ru_utime.tv_usec / 10000;
287 if (userTicks < processTimes.tms_utime || userTicks > processTimes.tms_utime + 10)
291 memset(&packet, 0xA5,
sizeof(packet));
292 if (syscall(SYS_getrusage, RUSAGE_SELF, packet.usage.slots) || packet.usage.slots[0] < 0 ||
293 packet.usage.slots[1] < 0 || packet.usage.slots[1] >= 1000000 || packet.usage.slots[2] < 0 ||
294 packet.usage.slots[3] < 0 || packet.usage.slots[3] >= 1000000)
296 for (
size_t i = 0; i <
sizeof(packet.canary); ++i) {
297 if (packet.canary[i] != 0xA5)
301 struct rusage thread_usage;
302 memset(&thread_usage, 0xA5,
sizeof(thread_usage));
303 if (getrusage(RUSAGE_THREAD, &thread_usage) || thread_usage.ru_utime.tv_sec < 0 ||
304 thread_usage.ru_utime.tv_usec < 0 || thread_usage.ru_utime.tv_usec >= 1000000 ||
305 thread_usage.ru_stime.tv_sec < 0 || thread_usage.ru_stime.tv_usec < 0 ||
306 thread_usage.ru_stime.tv_usec >= 1000000)
308 for (
size_t i = offsetof(
struct rusage, ru_maxrss); i < offsetof(
struct rusage, __reserved);
310 if (((
unsigned char*)&thread_usage)[i])
313 for (
size_t i = offsetof(
struct rusage, __reserved); i <
sizeof(thread_usage); ++i) {
314 if (((
unsigned char*)&thread_usage)[i] != 0xA5)
318 memset(&packet, 0xA5,
sizeof(packet));
319 if (syscall(SYS_getrusage, RUSAGE_THREAD, packet.usage.slots) || packet.usage.slots[0] < 0 ||
320 packet.usage.slots[1] < 0 || packet.usage.slots[1] >= 1000000 || packet.usage.slots[2] < 0 ||
321 packet.usage.slots[3] < 0 || packet.usage.slots[3] >= 1000000)
323 for (
size_t i = 4; i < 18; ++i) {
324 if (packet.usage.slots[i])
327 for (
size_t i = 0; i <
sizeof(packet.canary); ++i) {
328 if (packet.canary[i] != 0xA5)
332 test_child_resource_accounting();