Bug 1656155 - pt 5. Add bin-stats to the memory replay tool r=glandium

Add more format specifiers to FdPrintf: %, s and add width and precision
specifiers.

Differential Revision: https://phabricator.services.mozilla.com/D87829
This commit is contained in:
Paul Bone 2020-10-08 22:44:00 +00:00
parent 8e48a8d8f3
commit 6655819c99
3 changed files with 130 additions and 27 deletions

View File

@ -11,6 +11,7 @@
#else
# include <unistd.h>
#endif
#include <cmath>
#include <cstring>
#include "mozilla/Assertions.h"
#include "mozilla/Unused.h"
@ -35,13 +36,58 @@ class CheckedIncrement {
return mValue;
}
void advance(T end) {
// Only makes sense if T is a pointer type.
size_t diff = end - mValue;
if (diff > mMaxIncrement) {
MOZ_CRASH("overflow detected");
}
mMaxIncrement -= diff;
mValue = end;
};
void rewind(T pos) {
size_t diff = mValue - pos;
mMaxIncrement += diff;
mValue = pos;
}
operator T() { return mValue; }
T value() { return mValue; }
private:
T mValue;
size_t mMaxIncrement;
};
template <typename T>
static unsigned NumDigits(T n) {
if (n < 1) {
// We want one digit, it will be 0.
return 1;
}
double l = log10(n);
double cl = ceil(l);
return l == cl ? unsigned(cl) + 1 : unsigned(cl);
}
static void LeftPad(CheckedIncrement<char*>& b, size_t pad) {
while (pad-- > 0) {
*(b++) = ' ';
}
}
// Write the digits into the buffer.
static void WriteDigits(CheckedIncrement<char*>& b, size_t i,
size_t num_digits) {
size_t x = pow(10, double(num_digits - 1));
do {
*(b++) = "0123456789"[(i / x) % 10];
x /= 10;
} while (x > 0);
}
void FdPrintf(intptr_t aFd, const char* aFormat, ...) {
if (aFd == 0) {
return;
@ -56,26 +102,33 @@ void FdPrintf(intptr_t aFd, const char* aFormat, ...) {
case '\0':
goto out;
case '%':
switch (*++f) {
case '%': {
// The start of the format specifier is used if this specifier is
// invalid.
const char* start = f;
// Read the field width
f++;
char* end = nullptr;
size_t width = strtoul(f, &end, 10);
// If strtol can't find a number that's okay, that means 0 in our
// case, but we must advance f).
f.advance(end);
switch (*f) {
case 'z': {
if (*(++f) == 'u') {
size_t i = va_arg(ap, size_t);
size_t x = 1;
// Compute the number of digits.
while (x <= i / 10) {
x *= 10;
}
// Write the digits into the buffer.
do {
*(b++) = "0123456789"[(i / x) % 10];
x /= 10;
} while (x > 0);
size_t num_digits = NumDigits(i);
LeftPad(b, width > num_digits ? width - num_digits : 0);
WriteDigits(b, i, num_digits);
} else {
// Write out the format specifier if it's unknown.
// If the format specifier is unknown then write out '%' and
// rewind to the beginning of the specifier causing it to be
// printed normally.
*(b++) = '%';
*(b++) = 'z';
*(b++) = *f;
f.rewind(start);
}
break;
}
@ -100,14 +153,34 @@ void FdPrintf(intptr_t aFd, const char* aFormat, ...) {
break;
}
default:
// Write out the format specifier if it's unknown.
case 's': {
const char* str = va_arg(ap, const char*);
size_t len = strlen(str);
LeftPad(b, width > len ? width - len : 0);
while (*str) {
*(b++) = *(str++);
}
break;
}
case '%':
// Print a single raw '%'.
*(b++) = '%';
*(b++) = *f;
break;
default:
// If the format specifier is unknown then write out '%' and
// rewind to the beginning of the specifier causing it to be
// printed normally.
*(b++) = '%';
f.rewind(start);
break;
}
break;
}
default:
*(b++) = *f;
break;

View File

@ -8,8 +8,9 @@
#define __FdPrintf_h__
/* We can't use libc's (f)printf because it would reenter in replace_malloc,
* So use a custom and simplified version.
* Only %p and %z are supported.
* So use a custom and simplified version. Only %p, %zu, %s and %% are
* supported, %zu, %s, support width specifiers.
*
* /!\ This function used a fixed-size internal buffer. The caller is
* expected to not use a format string that may overflow.
* The aFd argument is a file descriptor on UNIX and a native win32 file

View File

@ -16,6 +16,7 @@ typedef intptr_t ssize_t;
# include <unistd.h>
#endif
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
@ -391,17 +392,45 @@ class Replay {
}
mOps++;
jemalloc_stats_t stats;
::jemalloc_stats_internal(&stats, nullptr);
FdPrintf(mStdErr,
"#%zu mapped: %zu; allocated: %zu; waste: %zu; dirty: %zu; "
"bookkeep: %zu; binunused: %zu\n",
mOps, stats.mapped, stats.allocated, stats.waste, stats.page_cache,
stats.bookkeeping, stats.bin_unused);
jemalloc_bin_stats_t bin_stats[JEMALLOC_MAX_STATS_BINS];
::jemalloc_stats_internal(&stats, bin_stats);
FdPrintf(mStdErr, "\n");
FdPrintf(mStdErr, "Ops: %9zu\n", mOps);
FdPrintf(mStdErr, "mapped: %9zu\n", stats.mapped);
FdPrintf(mStdErr, "allocated: %9zu\n", stats.allocated);
FdPrintf(mStdErr, "waste: %9zu\n", stats.waste);
FdPrintf(mStdErr, "dirty: %9zu\n", stats.page_cache);
FdPrintf(mStdErr, "bookkeep: %9zu\n", stats.bookkeeping);
FdPrintf(mStdErr, "bin-unused: %9zu\n", stats.bin_unused);
FdPrintf(mStdErr, "quantum-max: %9zu\n", stats.quantum_max);
FdPrintf(mStdErr, "subpage-max: %9zu\n", stats.page_size / 2);
FdPrintf(mStdErr, "large-max: %9zu\n", stats.large_max);
FdPrintf(mStdErr, "\n%8s %11s %10s %8s %9s %9s %8s\n", "bin-size",
"unused (c)", "total (c)", "used (c)", "non-full (r)", "total (r)",
"used (r)");
for (auto& bin : bin_stats) {
if (bin.size) {
FdPrintf(mStdErr, "%8zu %8zuKiB %7zuKiB %7zu%% %12zu %9zu %7zu%%\n",
bin.size, bin.bytes_unused / 1024, bin.bytes_total / 1024,
percent(bin.bytes_total - bin.bytes_unused, bin.bytes_total),
bin.num_non_full_runs, bin.num_runs,
percent(bin.num_runs - bin.num_non_full_runs, bin.num_runs));
}
}
/* TODO: Add more data, like actual RSS as measured by OS, but compensated
* for the replay internal data. */
}
private:
static size_t percent(size_t a, size_t b) {
if (!b) {
return 0;
}
return size_t(round(double(a) / double(b) * 100.0));
}
MemSlot& SlotForResult(Buffer& aResult) {
/* Parse result value and get the corresponding slot. */
Buffer dummy = aResult.SplitChar('=');