Improve write_generation_stats again

* Anticipate the division of generation 0 into two kinds of pages-
  those allocated by threads and those allocated by the collector.
  The latter are ok to scan from Lisp because the collector updates
  page_bytes_used() whenever it runs, whereas TLAB pages have
  some concurrency problems that are not easily resolved.
  (Hence the occasional crash in sb-sprof trying to find a code blob)
  Also, we can more easily implement different policies on whether to
  pre-zero generation 0 for the mutators versus collector.

* Allow for writing to a stdio stream or a file descriptor or both, so
  this can be called from Lisp. Avoiding stdio streams in the collector
  is preferable because we've seen many reports that enabling GC logging
  may cause lockup. I've found snprintf() and write() to be more reliable
  in that regard even though the entire printf() family is absent
  from https://man7.org/linux/man-pages/man7/signal-safety.7.html
This commit is contained in:
Douglas Katzman 2022-04-30 11:34:28 -04:00
parent 47b7c77f3e
commit 5a7e8d9ba7
3 changed files with 75 additions and 19 deletions

View file

@ -567,9 +567,9 @@ static void sweep_fixedobj_pages(long *zeroed)
}
#endif
static uword_t sweep(lispobj* where, lispobj* end, uword_t arg)
static uword_t sweep(lispobj* where, lispobj* end,
__attribute__((unused)) uword_t arg)
{
long *zeroed = (long*)arg; // one count per generation
sword_t nwords;
extern void deposit_filler(uword_t addr, sword_t nbytes);

View file

@ -80,6 +80,7 @@ int gencgc_handle_wp_violation(void *);
#define PAGE_TYPE_SMALL_MIXED 4 // #b100
#define PAGE_TYPE_CONS 5 // #b101
#define PAGE_TYPE_CODE 7 // #b111
#define THREAD_PAGE_FLAG 8
#define SINGLE_OBJECT_FLAG 16
#define OPEN_REGION_PAGE_FLAG 32
#define FREE_PAGE_FLAG 0

View file

@ -423,8 +423,8 @@ extern void fpu_restore(void *);
#define PAGE_INDEX_FMT PRIdPTR
extern void
write_generation_stats(FILE *file)
// You can call this with 0 and NULL to perform its assertions silently
void gc_gen_report_to_file(int filedes, FILE *file)
{
#ifdef LISP_FEATURE_X86
int fpu_state[27];
@ -434,10 +434,14 @@ write_generation_stats(FILE *file)
fpu_save(fpu_state);
#endif
#define OUTPUT(str, len) \
{if (file) fwrite(str, 1, len, file); if (filedes>=0) write(filedes, str, len);}
/* Print the heap stats. */
fprintf(file,
"Gen Boxed Cons Raw Code SmMix Mixed LgRaw LgCode LgMix"
" Waste%% Alloc Trig Dirty GCs Mem-age\n");
char header[] =
" Gen Boxed Cons Raw Code SmMix Mixed LgRaw LgCode LgMix"
" Waste% Alloc Trig Dirty GCs Mem-age\n";
OUTPUT(header, sizeof header-1);
generation_index_t gen_num, begin, end;
// Print from the lowest gen that has any allocated pages.
@ -447,15 +451,51 @@ write_generation_stats(FILE *file)
for (end = SCRATCH_GENERATION; end >= 0; --end)
if (generations[end].bytes_allocated) break;
char linebuf[144];
page_index_t coltot[9];
uword_t eden_words_allocated = 0;
page_index_t eden_pages = 0;
memset(coltot, 0, sizeof coltot);
for (gen_num = begin; gen_num <= end; gen_num++) {
page_index_t page;
page_index_t pagect[9];
memset(pagect, 0, sizeof pagect);
if (gen_num == 0) { // Count the eden pages
for (page = 0; page < next_free_page; page++)
if (page_table[page].gen == 0 && page_table[page].type & THREAD_PAGE_FLAG) {
int column;
switch (page_table[page].type & ~THREAD_PAGE_FLAG) {
case PAGE_TYPE_BOXED: column = 0; break;
case PAGE_TYPE_CONS: column = 1; break;
case PAGE_TYPE_CODE: column = 3; break;
case PAGE_TYPE_MIXED: column = 5; break;
default: lose("Bad eden page subtype: %x\n", page_table[page].type);
}
pagect[column]++;
coltot[column]++;
++eden_pages;
eden_words_allocated += page_words_used(page);
}
uword_t waste = npage_bytes(eden_pages) - (eden_words_allocated<<WORD_SHIFT);
double pct_waste = eden_pages > 0 ?
(double)waste / (double)npage_bytes(eden_pages) * 100 : 0.0;
if (eden_pages) {
int linelen = snprintf(linebuf, sizeof linebuf,
" E %7"PAGE_INDEX_FMT"%7"PAGE_INDEX_FMT"%14"PAGE_INDEX_FMT
"%14"PAGE_INDEX_FMT
"%28.1f %11"OS_VM_SIZE_FMT"\n",
pagect[0], pagect[1], pagect[3], pagect[5],
pct_waste, eden_words_allocated<<WORD_SHIFT);
OUTPUT(linebuf, linelen);
}
memset(pagect, 0, sizeof pagect);
}
uword_t words_allocated = 0;
page_index_t tot_pages = 0;
for (page = 0; page < next_free_page; page++)
if (!page_free_p(page) && page_table[page].gen == gen_num) {
if (!page_free_p(page) && page_table[page].gen == gen_num
&& !(page_table[page].type & THREAD_PAGE_FLAG)) {
int column;
switch (page_table[page].type & (SINGLE_OBJECT_FLAG|PAGE_TYPE_MASK)) {
case PAGE_TYPE_BOXED: column = 0; break;
@ -471,45 +511,60 @@ write_generation_stats(FILE *file)
}
pagect[column]++;
coltot[column]++;
++tot_pages;
words_allocated += page_words_used(page);
}
struct generation* gen = &generations[gen_num];
gc_assert(gen->bytes_allocated == count_generation_bytes_allocated(gen_num));
page_index_t tot_pages, n_dirty;
tot_pages = count_generation_pages(gen_num, &n_dirty);
uword_t waste = npage_bytes(tot_pages) - generations[gen_num].bytes_allocated;
if (gen_num == 0)
gc_assert(gen->bytes_allocated ==
(words_allocated+eden_words_allocated) << WORD_SHIFT);
else {
gc_assert(gen->bytes_allocated == words_allocated << WORD_SHIFT);
}
page_index_t n_dirty;
count_generation_pages(gen_num, &n_dirty);
uword_t waste = npage_bytes(tot_pages) - (words_allocated<<WORD_SHIFT);
double pct_waste = tot_pages > 0 ?
(double)waste / (double)npage_bytes(tot_pages) * 100 : 0.0;
fprintf(file,
" %d %7"PAGE_INDEX_FMT"%7"PAGE_INDEX_FMT"%7"PAGE_INDEX_FMT"%7"PAGE_INDEX_FMT
int linelen =
snprintf(linebuf, sizeof linebuf,
" %d %7"PAGE_INDEX_FMT"%7"PAGE_INDEX_FMT"%7"PAGE_INDEX_FMT"%7"PAGE_INDEX_FMT
"%7"PAGE_INDEX_FMT"%7"PAGE_INDEX_FMT"%7"PAGE_INDEX_FMT"%7"PAGE_INDEX_FMT
"%7"PAGE_INDEX_FMT" %6.1f %11"OS_VM_SIZE_FMT" %11"OS_VM_SIZE_FMT,
gen_num,
pagect[0], pagect[1], pagect[2], pagect[3], pagect[4], pagect[5],
pagect[6], pagect[7], pagect[8],
pct_waste, (uintptr_t)gen->bytes_allocated,
pct_waste, words_allocated<<WORD_SHIFT,
(uintptr_t)gen->gc_trigger);
// gen0 pages are never WPed
fprintf(file, gen_num==0?" -" : " %7"PAGE_INDEX_FMT, n_dirty);
fprintf(file, " %3d %7.4f\n", gen->num_gc, generation_average_age(gen_num));
linelen += snprintf(linebuf+linelen, sizeof linebuf-linelen,
gen_num==0?" -" : " %7"PAGE_INDEX_FMT, n_dirty);
linelen += snprintf(linebuf+linelen, sizeof linebuf-linelen,
" %3d %7.4f\n", gen->num_gc, generation_average_age(gen_num));
OUTPUT(linebuf, linelen);
}
page_index_t tot_pages = coltot[0] + coltot[1] + coltot[2] + coltot[3] + coltot[4] +
coltot[5] + coltot[6] + coltot[7] + coltot[8];
uword_t waste = npage_bytes(tot_pages) - bytes_allocated;
double pct_waste = (double)waste / (double)npage_bytes(tot_pages) * 100;
double heap_use_frac = 100 * (double)bytes_allocated / (double)dynamic_space_size;
fprintf(file,
"-- %7"PAGE_INDEX_FMT"%7"PAGE_INDEX_FMT"%7"PAGE_INDEX_FMT"%7"PAGE_INDEX_FMT
int linelen =
snprintf(linebuf, sizeof linebuf,
"Tot %7"PAGE_INDEX_FMT"%7"PAGE_INDEX_FMT"%7"PAGE_INDEX_FMT"%7"PAGE_INDEX_FMT
"%7"PAGE_INDEX_FMT"%7"PAGE_INDEX_FMT"%7"PAGE_INDEX_FMT"%7"PAGE_INDEX_FMT
"%7"PAGE_INDEX_FMT" %6.1f%12"OS_VM_SIZE_FMT
" [%.1f%% of %"OS_VM_SIZE_FMT" max]\n",
coltot[0], coltot[1], coltot[2], coltot[3], coltot[4], coltot[5], coltot[6],
coltot[7], coltot[8], pct_waste,
(uintptr_t)bytes_allocated, heap_use_frac, (uintptr_t)dynamic_space_size);
OUTPUT(linebuf, linelen);
#undef OUTPUT
#ifdef LISP_FEATURE_X86
fpu_restore(fpu_state);
#endif
}
void write_generation_stats(FILE *file) { gc_gen_report_to_file(-1, file); }
extern void
write_heap_exhaustion_report(FILE *file, long available, long requested,