int
__malloc_info (int options, FILE *fp)
{
  /* For now, at least.  */
  if (options != 0)
    return EINVAL;

  int n = 0;
  size_t total_nblocks = 0;
  size_t total_nfastblocks = 0;
  size_t total_avail = 0;
  size_t total_fastavail = 0;
  size_t total_system = 0;
  size_t total_max_system = 0;
  size_t total_aspace = 0;
  size_t total_aspace_mprotect = 0;



  if (__malloc_initialized < 0)
    ptmalloc_init ();

  fputs ("<malloc version=\"1\">\n", fp);

  /* Iterate over all arenas currently in use.  */
  mstate ar_ptr = &main_arena;
  do
    {
      fprintf (fp, "<heap nr=\"%d\">\n<sizes>\n", n++);

      size_t nblocks = 0;
      size_t nfastblocks = 0;
      size_t avail = 0;
      size_t fastavail = 0;
      struct
      {
	size_t from;
	size_t to;
	size_t total;
	size_t count;
      } sizes[NFASTBINS + NBINS - 1];
#define nsizes (sizeof (sizes) / sizeof (sizes[0]))

      __libc_lock_lock (ar_ptr->mutex);

      for (size_t i = 0; i < NFASTBINS; ++i)
	{
	  mchunkptr p = fastbin (ar_ptr, i);
	  if (p != NULL)
	    {
	      size_t nthissize = 0;
	      size_t thissize = chunksize (p);

	      while (p != NULL)
		{
		  ++nthissize;
		  p = p->fd;
		}

	      fastavail += nthissize * thissize;
	      nfastblocks += nthissize;
	      sizes[i].from = thissize - (MALLOC_ALIGNMENT - 1);
	      sizes[i].to = thissize;
	      sizes[i].count = nthissize;
	    }
	  else
	    sizes[i].from = sizes[i].to = sizes[i].count = 0;

	  sizes[i].total = sizes[i].count * sizes[i].to;
	}


      mbinptr bin;
      struct malloc_chunk *r;

      for (size_t i = 1; i < NBINS; ++i)
	{
	  bin = bin_at (ar_ptr, i);
	  r = bin->fd;
	  sizes[NFASTBINS - 1 + i].from = ~((size_t) 0);
	  sizes[NFASTBINS - 1 + i].to = sizes[NFASTBINS - 1 + i].total
					  = sizes[NFASTBINS - 1 + i].count = 0;

	  if (r != NULL)
	    while (r != bin)
	      {
		size_t r_size = chunksize_nomask (r);
		++sizes[NFASTBINS - 1 + i].count;
		sizes[NFASTBINS - 1 + i].total += r_size;
		sizes[NFASTBINS - 1 + i].from
		  = MIN (sizes[NFASTBINS - 1 + i].from, r_size);
		sizes[NFASTBINS - 1 + i].to = MAX (sizes[NFASTBINS - 1 + i].to,
						   r_size);

		r = r->fd;
	      }

	  if (sizes[NFASTBINS - 1 + i].count == 0)
	    sizes[NFASTBINS - 1 + i].from = 0;
	  nblocks += sizes[NFASTBINS - 1 + i].count;
	  avail += sizes[NFASTBINS - 1 + i].total;
	}

      size_t heap_size = 0;
      size_t heap_mprotect_size = 0;
      size_t heap_count = 0;
      if (ar_ptr != &main_arena)
	{
	  /* Iterate over the arena heaps from back to front.  */
	  heap_info *heap = heap_for_ptr (top (ar_ptr));
	  do
	    {
	      heap_size += heap->size;
	      heap_mprotect_size += heap->mprotect_size;
	      heap = heap->prev;
	      ++heap_count;
	    }
	  while (heap != NULL);
	}

      __libc_lock_unlock (ar_ptr->mutex);

      total_nfastblocks += nfastblocks;
      total_fastavail += fastavail;

      total_nblocks += nblocks;
      total_avail += avail;

      for (size_t i = 0; i < nsizes; ++i)
	if (sizes[i].count != 0 && i != NFASTBINS)
	  fprintf (fp, "							      \
  <size from=\"%zu\" to=\"%zu\" total=\"%zu\" count=\"%zu\"/>\n",
		   sizes[i].from, sizes[i].to, sizes[i].total, sizes[i].count);

      if (sizes[NFASTBINS].count != 0)
	fprintf (fp, "\
  <unsorted from=\"%zu\" to=\"%zu\" total=\"%zu\" count=\"%zu\"/>\n",
		 sizes[NFASTBINS].from, sizes[NFASTBINS].to,
		 sizes[NFASTBINS].total, sizes[NFASTBINS].count);

      total_system += ar_ptr->system_mem;
      total_max_system += ar_ptr->max_system_mem;

      fprintf (fp,
	       "</sizes>\n<total type=\"fast\" count=\"%zu\" size=\"%zu\"/>\n"
	       "<total type=\"rest\" count=\"%zu\" size=\"%zu\"/>\n"
	       "<system type=\"current\" size=\"%zu\"/>\n"
	       "<system type=\"max\" size=\"%zu\"/>\n",
	       nfastblocks, fastavail, nblocks, avail,
	       ar_ptr->system_mem, ar_ptr->max_system_mem);

      if (ar_ptr != &main_arena)
	{
	  fprintf (fp,
		   "<aspace type=\"total\" size=\"%zu\"/>\n"
		   "<aspace type=\"mprotect\" size=\"%zu\"/>\n"
		   "<aspace type=\"subheaps\" size=\"%zu\"/>\n",
		   heap_size, heap_mprotect_size, heap_count);
	  total_aspace += heap_size;
	  total_aspace_mprotect += heap_mprotect_size;
	}
      else
	{
	  fprintf (fp,
		   "<aspace type=\"total\" size=\"%zu\"/>\n"
		   "<aspace type=\"mprotect\" size=\"%zu\"/>\n",
		   ar_ptr->system_mem, ar_ptr->system_mem);
	  total_aspace += ar_ptr->system_mem;
	  total_aspace_mprotect += ar_ptr->system_mem;
	}

      fputs ("</heap>\n", fp);
      ar_ptr = ar_ptr->next;
    }
  while (ar_ptr != &main_arena);

  fprintf (fp,
	   "<total type=\"fast\" count=\"%zu\" size=\"%zu\"/>\n"
	   "<total type=\"rest\" count=\"%zu\" size=\"%zu\"/>\n"
	   "<total type=\"mmap\" count=\"%d\" size=\"%zu\"/>\n"
	   "<system type=\"current\" size=\"%zu\"/>\n"
	   "<system type=\"max\" size=\"%zu\"/>\n"
	   "<aspace type=\"total\" size=\"%zu\"/>\n"
	   "<aspace type=\"mprotect\" size=\"%zu\"/>\n"
	   "</malloc>\n",
	   total_nfastblocks, total_fastavail, total_nblocks, total_avail,
	   mp_.n_mmaps, mp_.mmapped_mem,
	   total_system, total_max_system,
	   total_aspace, total_aspace_mprotect);

  return 0;