void duk_debug_dump_heap(duk_heap *heap) {
	char buf[64+1];

	DUK_D(DUK_DPRINT("=== heap %p ===", (void *) heap));
	DUK_D(DUK_DPRINT("  flags: 0x%08lx", (unsigned long) heap->flags));

	/* Note: there is no standard formatter for function pointers */
#ifdef DUK_USE_GCC_PRAGMAS
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-pedantic"
#endif
	duk_debug_format_funcptr(buf, sizeof(buf), (unsigned char *) &heap->alloc_func, sizeof(heap->alloc_func));
	DUK_D(DUK_DPRINT("  alloc_func: %s", (const char *) buf));
	duk_debug_format_funcptr(buf, sizeof(buf), (unsigned char *) &heap->realloc_func, sizeof(heap->realloc_func));
	DUK_D(DUK_DPRINT("  realloc_func: %s", (const char *) buf));
	duk_debug_format_funcptr(buf, sizeof(buf), (unsigned char *) &heap->free_func, sizeof(heap->free_func));
	DUK_D(DUK_DPRINT("  free_func: %s", (const char *) buf));
	duk_debug_format_funcptr(buf, sizeof(buf), (unsigned char *) &heap->fatal_func, sizeof(heap->fatal_func));
	DUK_D(DUK_DPRINT("  fatal_func: %s", (const char *) buf));
#ifdef DUK_USE_GCC_PRAGMAS
#pragma GCC diagnostic pop
#endif

	DUK_D(DUK_DPRINT("  alloc_udata: %p", (void *) heap->alloc_udata));

#ifdef DUK_USE_MARK_AND_SWEEP
#ifdef DUK_USE_VOLUNTARY_GC
	DUK_D(DUK_DPRINT("  mark-and-sweep trig counter: %ld", (long) heap->mark_and_sweep_trigger_counter));
#endif
	DUK_D(DUK_DPRINT("  mark-and-sweep rec depth: %ld", (long) heap->mark_and_sweep_recursion_depth));
	DUK_D(DUK_DPRINT("  mark-and-sweep base flags: 0x%08lx", (unsigned long) heap->mark_and_sweep_base_flags));
#endif

	DUK_D(DUK_DPRINT("  lj.jmpbuf_ptr: %p", (void *) heap->lj.jmpbuf_ptr));
	DUK_D(DUK_DPRINT("  lj.type: %ld", (long) heap->lj.type));
	DUK_D(DUK_DPRINT("  lj.value1: %!T", (duk_tval *) &heap->lj.value1));
	DUK_D(DUK_DPRINT("  lj.value2: %!T", (duk_tval *) &heap->lj.value2));
	DUK_D(DUK_DPRINT("  lj.iserror: %ld", (long) heap->lj.iserror));

	DUK_D(DUK_DPRINT("  handling_error: %ld", (long) heap->handling_error));

	DUK_D(DUK_DPRINT("  heap_thread: %!@O", (duk_heaphdr *) heap->heap_thread));
	DUK_D(DUK_DPRINT("  curr_thread: %!@O", (duk_heaphdr *) heap->curr_thread));
	DUK_D(DUK_DPRINT("  heap_object: %!@O", (duk_heaphdr *) heap->heap_object));

	DUK_D(DUK_DPRINT("  call_recursion_depth: %ld", (long) heap->call_recursion_depth));
	DUK_D(DUK_DPRINT("  call_recursion_limit: %ld", (long) heap->call_recursion_limit));

	DUK_D(DUK_DPRINT("  hash_seed: 0x%08lx", (unsigned long) heap->hash_seed));
	DUK_D(DUK_DPRINT("  rnd_state: 0x%08lx", (unsigned long) heap->rnd_state));

	duk__dump_strcache(heap);

	duk__dump_heaphdr_list(heap, heap->heap_allocated, "heap allocated");

#ifdef DUK_USE_REFERENCE_COUNTING
	duk__dump_heaphdr_list(heap, heap->refzero_list, "refcounting refzero list");
#endif

#ifdef DUK_USE_MARK_AND_SWEEP
	duk__dump_heaphdr_list(heap, heap->finalize_list, "mark-and-sweep finalize list");
#endif

	duk__dump_stringtable(heap);

	/* heap->strs: not worth dumping */
}
DUK_INTERNAL void duk_fb_put_funcptr(duk_fixedbuffer *fb, duk_uint8_t *fptr, duk_size_t fptr_size) {
	char buf[64+1];
	duk_debug_format_funcptr(buf, sizeof(buf), fptr, fptr_size);
	buf[sizeof(buf) - 1] = (char) 0;
	duk_fb_put_cstring(fb, buf);
}