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.
This commit is contained in:
Justine Tunney 2022-03-31 17:07:22 -07:00
parent 27bc49034c
commit 1ff1854107
4 changed files with 111 additions and 44 deletions

View file

@ -41,13 +41,14 @@ textwindows int sys_getrusage_nt(int who, struct rusage *usage) {
if ((who == RUSAGE_SELF ? GetProcessTimes : GetThreadTimes)( if ((who == RUSAGE_SELF ? GetProcessTimes : GetThreadTimes)(
(who == RUSAGE_SELF ? GetCurrentProcess : GetCurrentThread)(), (who == RUSAGE_SELF ? GetCurrentProcess : GetCurrentThread)(),
&CreationFileTime, &ExitFileTime, &KernelFileTime, &UserFileTime)) { &CreationFileTime, &ExitFileTime, &KernelFileTime, &UserFileTime)) {
/* xxx: shouldn't clobber memory on failure below */
usage->ru_utime = WindowsDurationToTimeVal(ReadFileTime(UserFileTime)); usage->ru_utime = WindowsDurationToTimeVal(ReadFileTime(UserFileTime));
usage->ru_stime = WindowsDurationToTimeVal(ReadFileTime(KernelFileTime)); usage->ru_stime = WindowsDurationToTimeVal(ReadFileTime(KernelFileTime));
} else { } else {
return __winerr(); return __winerr();
} }
if (GetProcessMemoryInfo(GetCurrentProcess(), &memcount, sizeof(memcount))) { if (GetProcessMemoryInfo(GetCurrentProcess(), &memcount, sizeof(memcount))) {
usage->ru_maxrss = memcount.PeakWorkingSetSize; usage->ru_maxrss = memcount.PeakWorkingSetSize / 1024;
usage->ru_majflt = memcount.PageFaultCount; usage->ru_majflt = memcount.PageFaultCount;
} else { } else {
return __winerr(); return __winerr();

View file

@ -108,7 +108,7 @@ textwindows int sys_wait4_nt(int pid, int *opt_out_wstatus, int options,
if (opt_out_rusage) { if (opt_out_rusage) {
bzero(opt_out_rusage, sizeof(*opt_out_rusage)); bzero(opt_out_rusage, sizeof(*opt_out_rusage));
if (GetProcessMemoryInfo(handles[i], &memcount, sizeof(memcount))) { 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; opt_out_rusage->ru_majflt = memcount.PageFaultCount;
} else { } else {
STRACE("%s failed %u", "GetProcessMemoryInfo", GetLastError()); STRACE("%s failed %u", "GetProcessMemoryInfo", GetLastError());

View file

@ -23,26 +23,35 @@
#include "libc/runtime/clktck.h" #include "libc/runtime/clktck.h"
#include "libc/stdio/append.internal.h" #include "libc/stdio/append.internal.h"
static void AppendInt(char **b, int64_t x) { struct State {
char buf[27], *e; char **b;
e = FormatInt64Thousands(buf, x); const char *nl;
appendd(b, buf, e - buf); 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, static void AppendInt(struct State *s, int64_t x) {
const char *nl) { char *e = FormatInt64Thousands(s->ibuf, x);
appends(b, s1); appendd(s->b, s->ibuf, e - s->ibuf);
AppendInt(b, x);
appends(b, s2);
appends(b, nl);
} }
static void AppendUnit(char **b, int64_t x, const char *s) { static void AppendMetric(struct State *s, const char *s1, int64_t x,
AppendInt(b, x); const char *s2) {
appendw(b, ' '); appends(s->b, s1);
appends(b, s); 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) { 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. * Generates process resource usage report.
*/ */
void AppendResourceReport(char **b, struct rusage *ru, const char *nl) { void AppendResourceReport(char **b, struct rusage *ru, const char *nl) {
char ibuf[27]; struct State s;
long utime, stime; long utime, stime;
long double ticks; long double ticks;
struct State *st = &s;
s.b = b;
s.nl = nl;
asm("" : "+r"(st));
if (ru->ru_maxrss) { 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) | 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)) { (stime = ru->ru_stime.tv_sec * 1000000 + ru->ru_stime.tv_usec)) {
appends(b, "needed "); appends(b, "needed ");
AppendInt(b, utime + stime); AppendInt(st, utime + stime);
appends(b, "us cpu ("); 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, "% kernel)");
appends(b, nl); AppendNl(st);
ticks = ceill((long double)(utime + stime) / (1000000.L / CLK_TCK)); ticks = ceill((long double)(utime + stime) / (1000000.L / CLK_TCK));
if (ru->ru_idrss) { if (ru->ru_idrss) {
AppendMetric(b, "needed ", lroundl(ru->ru_idrss / ticks), AppendMetric(st, "needed ", lroundl(ru->ru_idrss / ticks),
" memory on average", nl); " memory on average");
} }
if (ru->ru_isrss) { if (ru->ru_isrss) {
AppendMetric(b, "needed ", lroundl(ru->ru_isrss / ticks), AppendMetric(st, "needed ", lroundl(ru->ru_isrss / ticks),
" stack on average", nl); " stack on average");
} }
if (ru->ru_ixrss) { if (ru->ru_ixrss) {
AppendMetric(b, "needed ", lroundl(ru->ru_ixrss / ticks), AppendMetric(st, "needed ", lroundl(ru->ru_ixrss / ticks),
" shared on average", nl); " shared on average");
} }
} }
if (ru->ru_minflt || ru->ru_majflt) { if (ru->ru_minflt || ru->ru_majflt) {
appends(b, "caused "); appends(b, "caused ");
AppendInt(b, ru->ru_minflt + ru->ru_majflt); AppendInt(st, ru->ru_minflt + ru->ru_majflt);
appends(b, " page faults ("); appends(b, " page faults (");
AppendInt( 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, "% memcpy)");
appends(b, nl); AppendNl(st);
} }
if (ru->ru_nvcsw + ru->ru_nivcsw > 1) { 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 ("); appends(b, " context switch (");
AppendInt(b, AppendInt(st,
(long double)ru->ru_nvcsw / (ru->ru_nvcsw + ru->ru_nivcsw) * 100); (long double)ru->ru_nvcsw / (ru->ru_nvcsw + ru->ru_nivcsw) * 100);
appends(b, "% consensual)"); appends(b, "% consensual)");
appends(b, nl); AppendNl(st);
} }
if (ru->ru_msgrcv || ru->ru_msgsnd) { if (ru->ru_msgrcv || ru->ru_msgsnd) {
appends(b, "received "); appends(b, "received ");
AppendUnit(b, ru->ru_msgrcv, "message"); AppendUnit(st, ru->ru_msgrcv, "message");
appends(b, " and sent "); appends(b, " and sent ");
AppendInt(b, ru->ru_msgsnd); AppendInt(st, ru->ru_msgsnd);
appends(b, nl); AppendNl(st);
} }
if (ru->ru_inblock || ru->ru_oublock) { if (ru->ru_inblock || ru->ru_oublock) {
appends(b, "performed "); appends(b, "performed ");
AppendUnit(b, ru->ru_inblock, "read"); AppendUnit(st, ru->ru_inblock, "read");
appends(b, " and "); appends(b, " and ");
AppendInt(b, ru->ru_oublock); AppendInt(st, ru->ru_oublock);
appends(b, " write i/o operations"); appends(b, " write i/o operations");
appends(b, nl); AppendNl(st);
} }
if (ru->ru_nsignals) { if (ru->ru_nsignals) {
appends(b, "received "); appends(b, "received ");
AppendUnit(b, ru->ru_nsignals, "signal"); AppendUnit(st, ru->ru_nsignals, "signal");
appends(b, nl); AppendNl(st);
} }
if (ru->ru_nswap) { if (ru->ru_nswap) {
appends(b, "got swapped "); appends(b, "got swapped ");
AppendUnit(b, ru->ru_nswap, "time"); AppendUnit(st, ru->ru_nswap, "time");
appends(b, nl); AppendNl(st);
} }
} }

View file

@ -0,0 +1,53 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 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