From 1ff1854107a5f4c57a4492d0460d2b4578c1f3ad Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Thu, 31 Mar 2022 17:07:22 -0700 Subject: [PATCH] Fix rusage memory reporting on NT This issue was caused by c23b6ecc3 a few days ago, where we started polling that information for the first time. --- libc/calls/getrusage-nt.c | 3 +- libc/calls/wait4-nt.c | 2 +- libc/log/appendresourcereport.c | 97 +++++++++++++---------- test/libc/log/appendresourcereport_test.c | 53 +++++++++++++ 4 files changed, 111 insertions(+), 44 deletions(-) create mode 100644 test/libc/log/appendresourcereport_test.c diff --git a/libc/calls/getrusage-nt.c b/libc/calls/getrusage-nt.c index 5adc2832b..46f70282b 100644 --- a/libc/calls/getrusage-nt.c +++ b/libc/calls/getrusage-nt.c @@ -41,13 +41,14 @@ textwindows int sys_getrusage_nt(int who, struct rusage *usage) { if ((who == RUSAGE_SELF ? GetProcessTimes : GetThreadTimes)( (who == RUSAGE_SELF ? GetCurrentProcess : GetCurrentThread)(), &CreationFileTime, &ExitFileTime, &KernelFileTime, &UserFileTime)) { + /* xxx: shouldn't clobber memory on failure below */ usage->ru_utime = WindowsDurationToTimeVal(ReadFileTime(UserFileTime)); usage->ru_stime = WindowsDurationToTimeVal(ReadFileTime(KernelFileTime)); } else { return __winerr(); } if (GetProcessMemoryInfo(GetCurrentProcess(), &memcount, sizeof(memcount))) { - usage->ru_maxrss = memcount.PeakWorkingSetSize; + usage->ru_maxrss = memcount.PeakWorkingSetSize / 1024; usage->ru_majflt = memcount.PageFaultCount; } else { return __winerr(); diff --git a/libc/calls/wait4-nt.c b/libc/calls/wait4-nt.c index 51ab4540b..643f88d38 100644 --- a/libc/calls/wait4-nt.c +++ b/libc/calls/wait4-nt.c @@ -108,7 +108,7 @@ textwindows int sys_wait4_nt(int pid, int *opt_out_wstatus, int options, if (opt_out_rusage) { bzero(opt_out_rusage, sizeof(*opt_out_rusage)); if (GetProcessMemoryInfo(handles[i], &memcount, sizeof(memcount))) { - opt_out_rusage->ru_maxrss = memcount.PeakWorkingSetSize; + opt_out_rusage->ru_maxrss = memcount.PeakWorkingSetSize / 1024; opt_out_rusage->ru_majflt = memcount.PageFaultCount; } else { STRACE("%s failed %u", "GetProcessMemoryInfo", GetLastError()); diff --git a/libc/log/appendresourcereport.c b/libc/log/appendresourcereport.c index b2d34f840..2f160de5f 100644 --- a/libc/log/appendresourcereport.c +++ b/libc/log/appendresourcereport.c @@ -23,26 +23,35 @@ #include "libc/runtime/clktck.h" #include "libc/stdio/append.internal.h" -static void AppendInt(char **b, int64_t x) { - char buf[27], *e; - e = FormatInt64Thousands(buf, x); - appendd(b, buf, e - buf); +struct State { + char **b; + const char *nl; + char ibuf[27]; +}; + +static void AppendNl(struct State *s) { + appends(s->b, s->nl); } -static void AppendMetric(char **b, const char *s1, int64_t x, const char *s2, - const char *nl) { - appends(b, s1); - AppendInt(b, x); - appends(b, s2); - appends(b, nl); +static void AppendInt(struct State *s, int64_t x) { + char *e = FormatInt64Thousands(s->ibuf, x); + appendd(s->b, s->ibuf, e - s->ibuf); } -static void AppendUnit(char **b, int64_t x, const char *s) { - AppendInt(b, x); - appendw(b, ' '); - appends(b, s); +static void AppendMetric(struct State *s, const char *s1, int64_t x, + const char *s2) { + appends(s->b, s1); + AppendInt(s, x); + appends(s->b, s2); + AppendNl(s); +} + +static void AppendUnit(struct State *s, int64_t x, const char *t) { + AppendInt(s, x); + appendw(s->b, ' '); + appends(s->b, t); if (x == 1) { - appendw(b, 's'); + appendw(s->b, 's'); } } @@ -50,74 +59,78 @@ static void AppendUnit(char **b, int64_t x, const char *s) { * Generates process resource usage report. */ void AppendResourceReport(char **b, struct rusage *ru, const char *nl) { - char ibuf[27]; + struct State s; long utime, stime; long double ticks; + struct State *st = &s; + s.b = b; + s.nl = nl; + asm("" : "+r"(st)); if (ru->ru_maxrss) { - AppendMetric(b, "ballooned to ", ru->ru_maxrss, "kb in size", nl); + AppendMetric(st, "ballooned to ", ru->ru_maxrss, "kb in size"); } 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)) { appends(b, "needed "); - AppendInt(b, utime + stime); + AppendInt(st, utime + stime); appends(b, "us cpu ("); - AppendInt(b, (long double)stime / (utime + stime) * 100); + AppendInt(st, (long double)stime / (utime + stime) * 100); appends(b, "% kernel)"); - appends(b, nl); + AppendNl(st); ticks = ceill((long double)(utime + stime) / (1000000.L / CLK_TCK)); if (ru->ru_idrss) { - AppendMetric(b, "needed ", lroundl(ru->ru_idrss / ticks), - " memory on average", nl); + AppendMetric(st, "needed ", lroundl(ru->ru_idrss / ticks), + " memory on average"); } if (ru->ru_isrss) { - AppendMetric(b, "needed ", lroundl(ru->ru_isrss / ticks), - " stack on average", nl); + AppendMetric(st, "needed ", lroundl(ru->ru_isrss / ticks), + " stack on average"); } if (ru->ru_ixrss) { - AppendMetric(b, "needed ", lroundl(ru->ru_ixrss / ticks), - " shared on average", nl); + AppendMetric(st, "needed ", lroundl(ru->ru_ixrss / ticks), + " shared on average"); } } if (ru->ru_minflt || ru->ru_majflt) { appends(b, "caused "); - AppendInt(b, ru->ru_minflt + ru->ru_majflt); + AppendInt(st, ru->ru_minflt + ru->ru_majflt); appends(b, " page faults ("); AppendInt( - b, (long double)ru->ru_minflt / (ru->ru_minflt + ru->ru_majflt) * 100); + st, (long double)ru->ru_minflt / (ru->ru_minflt + ru->ru_majflt) * 100); appends(b, "% memcpy)"); - appends(b, nl); + AppendNl(st); } if (ru->ru_nvcsw + ru->ru_nivcsw > 1) { - AppendInt(b, ru->ru_nvcsw + ru->ru_nivcsw); + AppendInt(st, ru->ru_nvcsw + ru->ru_nivcsw); appends(b, " context switch ("); - AppendInt(b, + AppendInt(st, (long double)ru->ru_nvcsw / (ru->ru_nvcsw + ru->ru_nivcsw) * 100); appends(b, "% consensual)"); - appends(b, nl); + AppendNl(st); } if (ru->ru_msgrcv || ru->ru_msgsnd) { appends(b, "received "); - AppendUnit(b, ru->ru_msgrcv, "message"); + AppendUnit(st, ru->ru_msgrcv, "message"); appends(b, " and sent "); - AppendInt(b, ru->ru_msgsnd); - appends(b, nl); + AppendInt(st, ru->ru_msgsnd); + AppendNl(st); } if (ru->ru_inblock || ru->ru_oublock) { appends(b, "performed "); - AppendUnit(b, ru->ru_inblock, "read"); + AppendUnit(st, ru->ru_inblock, "read"); appends(b, " and "); - AppendInt(b, ru->ru_oublock); + AppendInt(st, ru->ru_oublock); appends(b, " write i/o operations"); - appends(b, nl); + AppendNl(st); } if (ru->ru_nsignals) { appends(b, "received "); - AppendUnit(b, ru->ru_nsignals, "signal"); - appends(b, nl); + AppendUnit(st, ru->ru_nsignals, "signal"); + AppendNl(st); } if (ru->ru_nswap) { appends(b, "got swapped "); - AppendUnit(b, ru->ru_nswap, "time"); - appends(b, nl); + AppendUnit(st, ru->ru_nswap, "time"); + AppendNl(st); } } diff --git a/test/libc/log/appendresourcereport_test.c b/test/libc/log/appendresourcereport_test.c new file mode 100644 index 000000000..a9d0d3873 --- /dev/null +++ b/test/libc/log/appendresourcereport_test.c @@ -0,0 +1,53 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2022 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/calls.h" +#include "libc/log/log.h" +#include "libc/mem/mem.h" +#include "libc/str/str.h" +#include "libc/sysv/consts/rusage.h" +#include "libc/testlib/testlib.h" + +TEST(AppendResourceReport, testEmpty_doesNothing) { + char *b = 0; + struct rusage ru; + bzero(&ru, sizeof(ru)); + AppendResourceReport(&b, &ru, " / "); + EXPECT_EQ(NULL, b); +} + +TEST(AppendResourceReport, testMemory_balloons) { + char *b = 0; + struct rusage ru; + bzero(&ru, sizeof(ru)); + ru.ru_maxrss = 1; + AppendResourceReport(&b, &ru, ""); + EXPECT_STREQ("ballooned to 1kb in size", b); + free(b); +} + +#if 0 +TEST(AppendResourceReport, impure) { + char *b = 0; + struct rusage ru; + EXPECT_EQ(0, getrusage(RUSAGE_SELF, &ru)); + AppendResourceReport(&b, &ru, "\n"); + EXPECT_STREQ("ballooned to 1kb in size", b); + free(b); +} +#endif