/* Target-dependent code for emx
   Copyright 1995-1996 Eberhard Mattes.

This file is part of GDB.

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */

#include "defs.h"
#include <string.h>
#include <sys/ioctl.h>
#include <umalloc.h>            /* for <emx/umalloc.h> */
#include <sys/builtin.h>        /* for <sys/fmutex.h> */
#include <sys/fmutex.h>         /* for <sys/rmutex.h> */
#include <sys/rmutex.h>         /* for <emx/umalloc.h> */
#include <emx/umalloc.h>
#include <emx/syscalls.h>       /* _SIG_MAGIC1 etc. */
#define INCL_DOSEXCEPTIONS
#include <os2emx.h>             /* CONTEXTRECORD */
#include "gdbcmd.h"
#include "target.h"
#include "gdbcore.h"
#include "symtab.h"
#include "expression.h"
#include "value.h"
#include "frame.h"

static char heap_info_verbose;
static char heap_info_expr_flag;
static CORE_ADDR heap_info_expr;

static char *
heap_info_dump (CORE_ADDR addr, size_t size)
{
  size_t i, dump_size;
  unsigned char dump_buf[16];
  static char output[sizeof (dump_buf) * 3 + 1];
  const char *hex = "0123456789abcdef";

  memset (output, ' ', sizeof (dump_buf) * 3);
  output[sizeof (dump_buf) * 3] = 0;
  dump_size = min (size, sizeof (dump_buf));
  if (dump_size != 0
      && target_read_memory (addr, (char *)dump_buf, dump_size) == 0)
    for (i = 0; i < dump_size; ++i)
      {
        output[3*i+0] = hex[dump_buf[i] >> 4];
        output[3*i+1] = hex[dump_buf[i] & 0x0f];
      }
  return output;
}


#define END_OF_HEAP     0xffffffff
#define MF_FREE         0x01

static void
heap_info_emx_malloc1 ()
{
  struct minimal_symbol *msymbol;
  CORE_ADDR a_bottom, a_top, a_rover;
  CORE_ADDR bottom, top, rover, p, next;
  unsigned long header, size, total_used, total_free;
  unsigned long max_used, max_free;

  msymbol = lookup_minimal_symbol ("_malloc_bottom", NULL, NULL);
  if (msymbol == NULL)
    error ("\"_malloc_bottom\" is not defined.");
  a_bottom = SYMBOL_VALUE_ADDRESS (msymbol);

  msymbol = lookup_minimal_symbol ("_malloc_top", NULL, NULL);
  if (msymbol == NULL)
    error ("\"_malloc_top\" is not defined.");
  a_top = SYMBOL_VALUE_ADDRESS (msymbol);

  msymbol = lookup_minimal_symbol ("_malloc_rover", NULL, NULL);
  if (msymbol == NULL)
    error ("\"_malloc_rover\" is not defined.");
  a_rover = SYMBOL_VALUE_ADDRESS (msymbol);

  read_memory (a_bottom, (char *)&bottom, sizeof (bottom));
  read_memory (a_top, (char *)&top, sizeof (top));
  read_memory (a_rover, (char *)&rover, sizeof (rover));

  printf_filtered ("bottom = 0x%.8lx\n", (unsigned long)bottom);
  printf_filtered ("top    = 0x%.8lx\n", (unsigned long)top);
  printf_filtered ("rover  = 0x%.8lx\n", (unsigned long)rover);
  if (bottom != 0 && top != 0 && bottom < top)
    {
      total_used = total_free = 0; max_used = max_free = 0;
      for (p = bottom; p != top; p = next)
        {
          read_memory (p, (char *)&header, sizeof (header));
          if (header == END_OF_HEAP)
            {
              printf_filtered ("End of heap reached before \"_malloc_top\"\n");
              break;
            }
          size = header & ~3;
          next = p + sizeof (header) + size;
          if (header & MF_FREE)
            {
              total_free += size;
              if (size > max_free)
                max_free = size;
            }
          else
            {
              total_used += size;
              if (size > max_used)
                max_used = size;
            }
          printf_filtered ("0x%.8lx: %8lu %s  %s\n",
                           (unsigned long)p + sizeof (header),
                           size, header & MF_FREE ? "free" : "used",
                           heap_info_dump (p + sizeof (header), size));
          if (next > top)
            {
              printf_filtered ("Block at 0x%.8lx extends beyond end of heap\n",
                               (unsigned long)p);
              break;
            }
        }
      printf_filtered ("Total bytes in used blocks: %lu\n", total_used);
      printf_filtered ("Total bytes in free blocks: %lu\n", total_free);
      printf_filtered ("Size of biggest used block: %lu\n", max_used);
      printf_filtered ("Size of biggest free block: %lu\n", max_free);
    }
}


static unsigned long malloc2_total_used;
static unsigned long malloc2_total_free;
static unsigned long malloc2_max_used;
static unsigned long malloc2_max_free;


static int
malloc2_crumb (CORE_ADDR crumb_addr, unsigned crumb_size)
{
  struct _um_crumb crumb;
  CORE_ADDR block_addr;

  if (target_read_memory (crumb_addr, (char *)&crumb, sizeof (crumb)) != 0)
    {
      printf_filtered ("--- Error reading memory at 0x%.8lx ---\n",
                       (unsigned long)crumb_addr);
      return 0;
    }

  /* Cf. _UM_BLOCK_FROM_CRUMB */
  block_addr = crumb_addr + offsetof (struct _um_crumb, x.used.data);
  switch (crumb.x.used.parent_crate & _UMS_MASK)
    {
    case _UMS_FREE:
      printf_filtered ("0x%.8lx            free  prev=0x%.8lx next=0x%.8lx\n",
                       (unsigned long)block_addr,
                       (unsigned long)crumb.x.free.prev,
                       (unsigned long)crumb.x.free.next);
      malloc2_total_free += crumb_size;
      if (crumb_size > malloc2_max_used)
        malloc2_max_used = crumb_size;
      break;
    case _UMS_USER:
      printf_filtered ("0x%.8lx 0x%.8lx used  %s\n",
                       (unsigned long)block_addr,
                       (unsigned long)crumb.x.used.size,
                       heap_info_dump (block_addr, crumb.x.used.size));
      malloc2_total_used += crumb.x.used.size;
      if (crumb.x.used.size > malloc2_max_used)
        malloc2_max_used = crumb.x.used.size;
      break;
    default:
      printf_filtered ("0x%.8lx            corrupt\n",
                       (unsigned long)block_addr);
      break;
    }
  return 1;
}


static void
malloc2_crate (CORE_ADDR crate_addr)
{
  struct _um_crate crate;
  CORE_ADDR crumb_addr;
  int i;

  if (target_read_memory (crate_addr, (char *)&crate, sizeof (crate)) != 0)
    {
      printf_filtered ("--- Error reading memory at 0x%.8lx ---\n",
                       (unsigned long)crate_addr);
      return;
    }

  if (crate.magic != _UM_MAGIC_CRATE)
    {
      printf_filtered ("Magic word of crate at 0x%.8lx is %#lx instead of %#lx.",
                       (unsigned long)crate_addr, (unsigned long)crate.magic,
                       (unsigned long)_UM_MAGIC_CRATE);
      return;
    }

  if (heap_info_verbose)
    {
      printf_filtered ("parent_crateset:  0x%.8lx\n",
                       (unsigned long)crate.parent_crateset);
      printf_filtered ("parent_heap:      0x%.8lx\n",
                       (unsigned long)crate.parent_heap);
      printf_filtered ("next:             0x%.8lx\n",
                       (unsigned long)crate.next);
      printf_filtered ("crumb_size:       %lu\n",
                       (unsigned long)crate.crumb_size);
      printf_filtered ("max:              %lu\n",
                       (unsigned long)crate.max);
      printf_filtered ("init:             %lu\n",
                       (unsigned long)crate.init);
      printf_filtered ("used:             %lu\n",
                       (unsigned long)crate.used);
    }

  /* TODO: Avoid going beyond end of crate. */

  printf_filtered ("--- Crate contents ---\n");
  crumb_addr = crate_addr + sizeof (struct _um_crate);
  for (i = 0; i < crate.init; ++i)
    {
      if (!malloc2_crumb (crumb_addr, crate.crumb_size))
        break;
      crumb_addr += crate.crumb_size + _UM_CRUMB_OVERHEAD;
    }
  printf_filtered ("--- End of crate at 0x%.8lx ---\n", crate_addr);
}


static CORE_ADDR
malloc2_lump (CORE_ADDR lump_addr)
{
  struct _um_lump lump;
  CORE_ADDR block_addr;

  if (target_read_memory (lump_addr, (char *)&lump, sizeof (lump)) != 0)
    {
      printf_filtered ("--- Error reading memory at 0x%.8lx ---\n",
                       (unsigned long)lump_addr);
      return 0;
    }

  /* Cf. _UM_BLOCK_FROM_LUMP */
  block_addr = lump_addr + offsetof (struct _um_lump, x.used.data);
  switch (lump.parent_seg & _UMS_MASK)
    {
    case _UMS_FREE:
      printf_filtered ("0x%.8lx 0x%.8lx free  prev=0x%.8lx next=0x%.8lx\n",
                       (unsigned long)block_addr,
                       (unsigned long)lump.size,
                       (unsigned long)lump.x.free.prev,
                       (unsigned long)lump.x.free.next);
      malloc2_total_free += lump.size;
      if (lump.size > malloc2_max_free)
        malloc2_max_free = lump.size;
      break;
    case _UMS_USER:
      printf_filtered ("0x%.8lx 0x%.8lx used  %s\n",
                       (unsigned long)block_addr,
                       (unsigned long)lump.size,
                       heap_info_dump (block_addr, lump.size));
      malloc2_total_used += lump.size;
      if (lump.size > malloc2_max_used)
        malloc2_max_used = lump.size;
      break;
    case _UMS_CRATE:
      printf_filtered ("0x%.8lx 0x%.8lx crate\n",
                       (unsigned long)block_addr,
                       (unsigned long)lump.size);
      malloc2_crate (block_addr);
      break;
    default:
      printf_filtered ("0x%.8lx            corrupt\n",
                       (unsigned long)block_addr);
      break;
    }
  return lump_addr + _UM_ROUND_LUMP (lump.size);
}


static CORE_ADDR
malloc2_seg (CORE_ADDR seg_addr)
{
  struct _um_seg seg;
  CORE_ADDR lump_addr;

  printf_filtered ("Segment at 0x%.8lx:\n", (unsigned long)seg_addr);
  if (target_read_memory (seg_addr, (char *)&seg, sizeof (seg)) != 0)
    {
      printf_filtered ("--- Error reading memory at 0x%.8lx ---\n",
                       (unsigned long)seg_addr);
      return 0;
    }
  if (seg.magic != _UM_MAGIC_SEG)
    {
      printf_filtered ("Magic word of segment at 0x%.8lx is %#lx instead of %#lx.",
                       (unsigned long)seg_addr, (unsigned long)seg.magic,
                       (unsigned long)_UM_MAGIC_SEG);
      return 0;
    }
  if (heap_info_verbose)
    {
      printf_filtered ("parent_heap:      0x%.8lx\n",
                       (unsigned long)seg.parent_heap);
      printf_filtered ("next:             0x%.8lx\n",
                       (unsigned long)seg.next);
      printf_filtered ("size:             0x%.8lx\n",
                       (unsigned long)seg.size);
      printf_filtered ("zero_limit:       0x%.8lx\n",
                       (unsigned long)seg.zero_limit);
      printf_filtered ("start:            0x%.8lx\n",
                       (unsigned long)seg.start);
      printf_filtered ("end:              0x%.8lx\n",
                       (unsigned long)seg.end);
      printf_filtered ("mem:              0x%.8lx\n",
                       (unsigned long)seg.mem);
    }

  lump_addr = (CORE_ADDR)seg.start;
  printf_filtered ("--- Segment contents ---\n");
  while (lump_addr != 0 && lump_addr < (CORE_ADDR)seg.end)
    lump_addr = malloc2_lump (lump_addr);
  printf_filtered ("--- End of segment at 0x%.8lx ---\n", seg_addr);

  return (CORE_ADDR)seg.next;
}


static void
malloc2_heap (CORE_ADDR heap_addr)
{
  struct _uheap heap;
  CORE_ADDR seg_addr;
  int i;

  read_memory (heap_addr, (char *)&heap, sizeof (heap));
  if (heap_info_verbose)
    {
      printf_filtered ("type:             0%s%s%s\n",
                       (heap.type & _HEAP_REGULAR) ? "|_HEAP_REGULAR" : "",
                       (heap.type & _HEAP_TILED)   ? "|_HEAP_TILED" : "",
                       (heap.type & _HEAP_SHARED)  ? "|_HEAP_SHARED" : "");
      printf_filtered ("alloc_fun:        0x%.8lx\n",
                       (unsigned long)heap.alloc_fun);
      printf_filtered ("release_fun:      0x%.8lx\n",
                       (unsigned long)heap.release_fun);
      printf_filtered ("expand_fun:       0x%.8lx\n",
                       (unsigned long)heap.expand_fun);
      printf_filtered ("shrink_fun:       0x%.8lx\n",
                       (unsigned long)heap.shrink_fun);
      printf_filtered ("seg_head:         0x%.8lx\n",
                       (unsigned long)heap.seg_head);
      printf_filtered ("initial_seg:      0x%.8lx\n",
                       (unsigned long)heap.initial_seg);
      printf_filtered ("initial_seg_size: 0x%.8lx\n",
                       (unsigned long)heap.initial_seg_size);
      printf_filtered ("n_segments:       %lu\n",
                       (unsigned long)heap.n_segments);
      printf_filtered ("n_crates:         %lu\n",
                       (unsigned long)heap.n_crates);
      printf_filtered ("max_crumbs:       %lu\n",
                       (unsigned long)heap.max_crumbs);
      for (i = 0; i < _UM_BUCKETS; ++i)
        {
          printf_filtered ("buckets[%2d]:      >= %lu\n",
                           i, (unsigned long)_UM_BUCKET_SIZE (i));
          printf_filtered ("  head:           0x%.8lx\n",
                           (unsigned long)heap.buckets[i].head);
          printf_filtered ("  tail:           0x%.8lx\n",
                           (unsigned long)heap.buckets[i].tail);
        }
      printf_filtered ("n_cratesets:      %lu\n",
                       (unsigned long)heap.n_cratesets);
      for (i = 0; i < heap.n_cratesets; ++i)
        {
          printf_filtered ("cratesets[%d]:\n", i);
          printf_filtered ("  crumb_size:     <= %lu\n",
                           (unsigned long)heap.cratesets[i].crumb_size);
          printf_filtered ("  crate_head:     0x%.8lx\n",
                           (unsigned long)heap.cratesets[i].crate_head);
          printf_filtered ("  crumb_head:     0x%.8lx\n",
                           (unsigned long)heap.cratesets[i].crumb_head);
          printf_filtered ("  crumb_tail:     0x%.8lx\n",
                           (unsigned long)heap.cratesets[i].crumb_tail);
        }
      printf_filtered ("\n");
    }

  malloc2_total_used = 0; malloc2_total_free = 0;
  malloc2_max_used = 0; malloc2_max_free = 0;

  seg_addr = (CORE_ADDR)heap.seg_head;
  while (seg_addr != 0)
    seg_addr = malloc2_seg (seg_addr);

  printf_filtered ("\n");
  printf_filtered ("Total bytes in used blocks: %lu\n", malloc2_total_used);
  printf_filtered ("Total bytes in free blocks: %lu\n", malloc2_total_free);
  printf_filtered ("Size of biggest used block: %lu\n", malloc2_max_used);
  printf_filtered ("Size of biggest free block: %lu\n", malloc2_max_free);
}


static void
heap_info_emx_malloc2 (CORE_ADDR heap_ptr)
{
  CORE_ADDR heap_addr, parent_ptr;
  _umagic magic;

  if (heap_info_expr_flag)
    heap_addr = heap_info_expr;
  else
    read_memory (heap_ptr, (char *)&heap_addr, sizeof (heap_addr));

  if (heap_addr == 0)
    error ("The heap is not initiaized.");
  read_memory (heap_addr, (char *)&magic, sizeof (magic));
  if (magic == _UM_MAGIC_HEAP)
    malloc2_heap (heap_addr);
  else if (heap_info_expr_flag)
    {
      if (magic == _UM_MAGIC_SEG)
        malloc2_seg (heap_addr);
      else
        {
          if (target_read_memory (heap_addr - 4, (char *)&parent_ptr,
                                  sizeof (parent_ptr)) == 0
              && target_read_memory ((CORE_ADDR)_PTR_FROM_UMINT (parent_ptr,
                                                                 void),
                                     (char *)&magic, sizeof (magic)) == 0)
            {
              if (magic == _UM_MAGIC_SEG)
                {
                  printf_filtered ("Lump at 0x%.8lx:\n",
                                   (unsigned long)heap_addr);
                  malloc2_lump ((CORE_ADDR)_UM_LUMP_FROM_BLOCK (heap_addr));
                }
              else if (magic == _UM_MAGIC_CRATE)
                {
                  printf_filtered ("Crumb at 0x%.8lx:\n",
                                   (unsigned long)heap_addr);
                  malloc2_crumb ((CORE_ADDR)_UM_CRUMB_FROM_BLOCK (heap_addr),
                                 0);
                }
              else
                error ("No heap structure at address 0x%.8lx.\n",
                       (unsigned long)heap_addr);
            }
          else
            error ("No heap structure at address 0x%.8lx.\n",
                   (unsigned long)heap_addr);
        }
    }
  else
    error ("Magic word of heap at 0x%.8lx is %#lx instead of %#lx.",
           (unsigned long)heap_addr, (unsigned long)magic,
           (unsigned long)_UM_MAGIC_HEAP);
}

#define G1_BLOCKLOG     12
#define G1_BLOCKSIZE    (1 << G1_BLOCKLOG)

#define G1_BLOCK(a)     (((a) - g1_heapbase) / G1_BLOCKSIZE + 1)
#define G1_ADDRESS(b)   (((b) - 1) * G1_BLOCKSIZE + g1_heapbase)

typedef size_t g1_size_t;
typedef long g1_ptrdiff_t;

typedef union
{
  struct
  {
    int type;
    union
    {
      struct
      {
        g1_size_t nfree;
        g1_size_t first;
      } frag;
      g1_ptrdiff_t size;
    } info;
  } busy;
  struct
  {
    g1_size_t size;
    g1_size_t next;
    g1_size_t prev;
  } free;
} g1_info;

typedef struct
{
  CORE_ADDR next;
  CORE_ADDR prev;
} g1_list;

typedef struct
{
  int type;
  char *frag_free;
  enum
  {
    G1_UNSEEN,
    G1_FREE,
    G1_USED
  } status;
} g1_block;


static g1_block *g1_blocks;
static CORE_ADDR g1_heapinfo, g1_heapbase, g1_end_heap, g1_a_fraghead;
static g1_size_t g1_heaplimit;
static unsigned long g1_total_used;
static unsigned long g1_total_free;
static unsigned long g1_max_used;
static unsigned long g1_max_free;


static void
g1_cleanup (PTR arg)
{
  unsigned i;

  if (g1_blocks != NULL)
    {
      for (i = 0; i < g1_heaplimit; ++i)
        if (g1_blocks[i].frag_free != NULL)
          free (g1_blocks[i].frag_free);
      free (g1_blocks);
      g1_blocks = NULL;
    }
}


static void
g1_frags (g1_size_t block, CORE_ADDR block_addr, g1_info *info)
{
  CORE_ADDR frag_addr;
  unsigned frag_size, frag_count, i, n_free;
  g1_block *pb;

  frag_size = 1 << info->busy.type;
  frag_count = G1_BLOCKSIZE / frag_size;
  frag_addr = block_addr;
  n_free = 0;
  pb = &g1_blocks[block];
  for (i = 0; i < frag_count; ++i)
    {
      if (pb->frag_free != NULL && pb->frag_free[i])
        {
          if (heap_info_verbose)
            printf_filtered ("  0x%.8lx: %6u free\n",
                             (unsigned long)frag_addr, frag_size);
          ++n_free;
          g1_total_free += frag_size;
          if (frag_size > g1_max_free) g1_max_free = frag_size;
        }
      else
        {
          if (heap_info_verbose)
            printf_filtered ("  0x%.8lx: %6u used  %s\n",
                             (unsigned long)frag_addr, frag_size,
                             heap_info_dump (frag_addr, frag_size));
          g1_total_used += frag_size;
          if (frag_size > g1_max_used) g1_max_used = frag_size;
        }
      frag_addr += frag_size;
    }
  if (n_free != info->busy.info.frag.nfree)
    printf_filtered ("Incorrect number of free fragments\n");
}


static void
g1_fraglist (int log, CORE_ADDR addr, CORE_ADDR prev)
{
  unsigned block, frag, offset, count;
  g1_block *pb;
  g1_list list;
  g1_info info;

  count = G1_BLOCKSIZE / (1 << log);
  do
    {
      if (addr < g1_heapbase + G1_BLOCKSIZE || addr >= g1_end_heap)
        {
          printf_filtered ("Entry 0x%lx of _fraghead[%d] chain: "
                           "outside heap\n", (unsigned long)addr, log);
          return;
        }

      block = G1_BLOCK (addr);
      pb = &g1_blocks[block];
      if (pb->status == G1_UNSEEN)
        {
          read_memory (g1_heapinfo + block * sizeof (g1_info),
                       (char *)&info, sizeof (info));
          pb->status = G1_USED;
          pb->type = info.busy.type;
          pb->frag_free = xmalloc (count);
          memset (pb->frag_free, 0, count);
        }
      else if (pb->status == G1_FREE)
        {
          printf_filtered ("Entry 0x%lx of _fraghead[%d] chain: points into "
                           "free block\n", (unsigned long)addr, log);
          return;
        }
      offset = addr - G1_ADDRESS (block);
      if (offset % (1 << log) != 0)
        {
          printf_filtered ("Entry 0x%lx of _fraghead[%d] chain: "
                           "does not point to fragment\n",
                           (unsigned long)addr, log);
          return;
        }

      if (info.busy.type != log)
        {
          printf_filtered ("Entry 0x%lx of _fraghead[%d] chain: "
                           " incorrect fragment size (1<<%u)\n",
                           (unsigned long)addr, log, (unsigned)info.busy.type);
          return;
        }

      frag = offset >> log;
      if (pb->frag_free[frag])
        {
          printf_filtered ("Entry 0x%lx of _fraghead[%d] chain: loop\n",
                           (unsigned long)addr, log);
          return;
        }
      pb->frag_free[frag] = 1;

      read_memory (addr, (char *)&list, sizeof (list));
      if (list.prev != prev)
        {
          printf_filtered ("Entry 0x%lx of _fraghead[%d] chain: "
                           "incorrect prev pointer\n",
                           (unsigned long)addr, log);
          return;
        }

      prev = addr; addr = list.next;
      /* TODO: Check prev */
    } while (addr != 0);
}


static void
g1_one_block (unsigned block, int all_frags)
{
  CORE_ADDR block_addr, addr, frag_addr;
  g1_info info;
  g1_list list;
  unsigned long size;
  g1_block *pb;
  unsigned frag, frag_size;

  block_addr = G1_ADDRESS (block);
  read_memory (g1_heapinfo + block * sizeof (g1_info),
               (char *)&info, sizeof (info));
  if (g1_blocks[block].status == G1_FREE)
    {
      while (block > 1 && g1_blocks[block-1].status == G1_FREE)
        --block;
      block_addr = G1_ADDRESS (block);
      read_memory (g1_heapinfo + block * sizeof (g1_info),
                   (char *)&info, sizeof (info));
      size = (unsigned long)info.free.size * G1_BLOCKSIZE;
      printf_filtered ("0x%.8x: %8lu free\n",
                       (unsigned long)block_addr, size);
    }
  else if (info.busy.type == 0)
    {
      if (info.busy.info.size < 0)
        {
          long min_size = -info.busy.info.size + 1;
          block += info.busy.info.size;
          if (block >= g1_heaplimit)
            error ("Cannot find first block of multiblock object.");
          block_addr = G1_ADDRESS (block);
          read_memory (g1_heapinfo + block * sizeof (g1_info),
                       (char *)&info, sizeof (info));
          if (info.busy.type != 0 || info.busy.info.size < min_size)
            error ("Cannot find first block of multiblock object.");
        }
      size = (unsigned long)info.busy.info.size * G1_BLOCKSIZE;
      printf_filtered ("0x%.8x: %8lu used  %s\n",
                       (unsigned long)block_addr, size,
                       heap_info_dump (block_addr, size));
    }
  else
    {
      printf_filtered ("0x%.8x: %8lu used (frags of %u bytes, %u free)\n",
                       (unsigned long)block_addr,
                       (unsigned long)G1_BLOCKSIZE,
                       1 << info.busy.type,
                       (unsigned)info.busy.info.frag.nfree);
      if (info.busy.type < 1 || info.busy.type > G1_BLOCKLOG
          || (1 << info.busy.type) > G1_BLOCKSIZE / 2)
        error ("Invalid fragment size.");

      addr = g1_a_fraghead + info.busy.type * sizeof (list);
      read_memory (addr, (char *)&list, sizeof (list));
      if (list.next != 0)
        g1_fraglist (info.busy.type, list.next, addr);

      if (all_frags)
        {
          if (heap_info_verbose)
            g1_frags (block, block_addr, &info);
        }
      else
        {
          frag = (heap_info_expr - block_addr) >> info.busy.type;
          frag_addr = block_addr + (frag << info.busy.type);
          frag_size = 1 << info.busy.type;
          pb = &g1_blocks[block];
          if (pb->frag_free != NULL && pb->frag_free[frag])
            printf_filtered ("  0x%.8lx: %6u free\n",
                             (unsigned long)frag_addr, frag_size);
          else
            printf_filtered ("  0x%.8lx: %6u used  %s\n",
                             (unsigned long)frag_addr, frag_size,
                             heap_info_dump (frag_addr, frag_size));
        }
    }
}


static void
heap_info_gnu_malloc1 ()
{
  struct minimal_symbol *msymbol;
  CORE_ADDR a_heapinfo, a_heapbase, a_heaplimit;
  CORE_ADDR end_info, block_addr, addr;
  g1_info info;
  g1_list list;
  g1_size_t block;
  int i;
  struct cleanup *cleanups;
  unsigned long size;

  msymbol = lookup_minimal_symbol ("_heapinfo", NULL, NULL);
  if (msymbol == NULL)
    error ("\"_heapinfo\" is not defined.");
  a_heapinfo = SYMBOL_VALUE_ADDRESS (msymbol);

  msymbol = lookup_minimal_symbol ("_heapbase", NULL, NULL);
  if (msymbol == NULL)
    error ("\"_heapbase\" is not defined.");
  a_heapbase = SYMBOL_VALUE_ADDRESS (msymbol);

  msymbol = lookup_minimal_symbol ("_heaplimit", NULL, NULL);
  if (msymbol == NULL)
    error ("\"_heaplimit\" is not defined.");
  a_heaplimit = SYMBOL_VALUE_ADDRESS (msymbol);

  msymbol = lookup_minimal_symbol ("_fraghead", NULL, NULL);
  if (msymbol == NULL)
    error ("\"_fraghead\" is not defined.");
  g1_a_fraghead = SYMBOL_VALUE_ADDRESS (msymbol);

  read_memory (a_heapinfo, (char *)&g1_heapinfo, sizeof (g1_heapinfo));
  read_memory (a_heapbase, (char *)&g1_heapbase, sizeof (g1_heapbase));
  read_memory (a_heaplimit, (char *)&g1_heaplimit, sizeof (g1_heaplimit));

  if (!heap_info_expr_flag || heap_info_verbose)
    {
      printf_filtered ("_heapbase   = 0x%.8lx\n", (unsigned long)g1_heapbase);
      printf_filtered ("_heapinfo   = 0x%.8lx\n", (unsigned long)g1_heapinfo);
      printf_filtered ("_heaplimit  = 0x%.8lx (number of blocks)\n",
                       (unsigned long)g1_heaplimit);
    }

  end_info = g1_heapinfo + g1_heaplimit * sizeof (g1_info);
  g1_end_heap = G1_ADDRESS (g1_heaplimit);
  if (g1_heapinfo < g1_heapbase || end_info < g1_heapbase
      || end_info > g1_end_heap
      || (g1_heapinfo - g1_heapbase) % G1_BLOCKSIZE != 0)
    error ("_heapinfo is invalid.");

  if (!heap_info_expr_flag || heap_info_verbose)
    printf_filtered ("End of heap = 0x%.8lx\n", (unsigned long)g1_end_heap);

  if (heap_info_expr_flag)
    {
      if (heap_info_expr < g1_heapbase || heap_info_expr >= g1_end_heap)
        error ("Address 0x%.8lx is not in the heap.",
               (unsigned long)heap_info_expr);
    }

  g1_blocks = xmalloc (g1_heaplimit * sizeof (g1_block)); 
  memset (g1_blocks, 0, g1_heaplimit * sizeof (g1_block));
  cleanups = make_cleanup (g1_cleanup, NULL);

  block = 0;
  do
    {
      if (block >= g1_heaplimit || g1_blocks[block].status != G1_UNSEEN)
        {
          printf_filtered ("Bad free block list at block %u.\n",
                           (unsigned)block);
          break;
        }
      read_memory (g1_heapinfo + block * sizeof (g1_info),
                   (char *)&info, sizeof (info));
      g1_blocks[block].status = G1_FREE;
      for (i = 1; i < info.free.size; ++i)
        {
          if (block + i >= g1_heaplimit
              || g1_blocks[block+i].status != G1_UNSEEN)
            {
              printf_filtered ("Bad free block list at block %u.\n",
                               (unsigned)block + i);
              break;
            }
          g1_blocks[block+i].status = G1_FREE;
        }
      block = info.free.next;
      /* TODO: Check _heapindex */
    } while (block != 0);

  if (heap_info_expr_flag)
    {
      if (heap_info_expr >= g1_heapinfo
          && heap_info_expr < g1_heapinfo + sizeof (g1_info))
        printf_filtered ("Address 0x%.8lx is in the 0th entry of the block "
                         "information table.\n",
                         (unsigned long)heap_info_expr);
      else if (heap_info_expr >= g1_heapinfo && heap_info_expr < end_info)
        {
          printf_filtered ("Address 0x%.8lx is in the block information "
                           "table.\n", (unsigned long)heap_info_expr);
          g1_one_block ((heap_info_expr - g1_heapinfo) / sizeof (g1_info), 1);
        }
      else
        {
          printf_filtered ("Address 0x%.8lx is in block %u.\n",
                           (unsigned long)heap_info_expr,
                           (unsigned)G1_BLOCK (heap_info_expr));
          g1_one_block (G1_BLOCK (heap_info_expr), 0);
        }
      do_cleanups (cleanups);
      return;
    }

  addr = g1_a_fraghead;
  for (i = 0; i < G1_BLOCKLOG; ++i)
    {
      read_memory (addr, (char *)&list, sizeof (list));
      if (list.next != 0)
        g1_fraglist (i, list.next, addr);
      addr += sizeof (list);
    }

  g1_total_used = 0; g1_total_free = 0;
  g1_max_used = 0; g1_max_free = 0;

  block = 1;
  while (block < g1_heaplimit)
    {
      block_addr = G1_ADDRESS (block);
      read_memory (g1_heapinfo + block * sizeof (g1_info),
                   (char *)&info, sizeof (info));
      if (g1_blocks[block].status == G1_FREE)
        {
          size = (unsigned long)info.free.size * G1_BLOCKSIZE;
          printf_filtered ("0x%.8x: %8lu free\n",
                           (unsigned long)block_addr, size);
          if (info.free.size == 0)
            ++block;            /* TODO */
          else
            block += info.free.size;
          g1_total_free += size;
          if (size > g1_max_free) g1_max_free = size;
        }
      else if (info.busy.type == 0)
        {
          size = (unsigned long)info.busy.info.size * G1_BLOCKSIZE;
          printf_filtered ("0x%.8x: %8lu used  %s\n",
                           (unsigned long)block_addr, size,
                           heap_info_dump (block_addr, size));
          if (info.busy.info.size == 0)
            ++block;            /* TODO */
          else
            block += info.busy.info.size;
          g1_total_used += size;
          if (size > g1_max_used) g1_max_used = size;
        }
      else
        {
          printf_filtered ("0x%.8x: %8lu used (frags of %u bytes, %u free)\n",
                           (unsigned long)block_addr,
                           (unsigned long)G1_BLOCKSIZE,
                           1 << info.busy.type,
                           (unsigned)info.busy.info.frag.nfree);
          if (info.busy.type < 1
              || (1 << info.busy.type) > G1_BLOCKSIZE / 2)
            printf_filtered ("Invalid fragment size\n");
          else
            g1_frags (block, block_addr, &info);
          ++block;
        }
    }
  printf_filtered ("\n");
  printf_filtered ("Total bytes in used blocks: %lu\n", g1_total_used);
  printf_filtered ("Total bytes in free blocks: %lu\n", g1_total_free);
  printf_filtered ("Size of biggest used block: %lu\n", g1_max_used);
  printf_filtered ("Size of biggest free block: %lu\n", g1_max_free);
  do_cleanups (cleanups);
}


static void
heap_info (args, from_tty)
    char *args;
    int from_tty;
{
  struct minimal_symbol *msymbol;

  heap_info_verbose = 0; heap_info_expr_flag = 0;
  if (args)
    {
      if (strncmp (args, "/v", 2) == 0)
        {
          heap_info_verbose = 1;
          args += 2;
        }
      while (*args == ' ' || *args == '\t') args++;
    }

  if (args && *args != 0)
    {
      heap_info_expr = parse_and_eval_address (args);
      heap_info_expr_flag = 1;
    }

  if ((msymbol = lookup_minimal_symbol ("_malloc_bottom", NULL, NULL)) != NULL)
    heap_info_emx_malloc1 ();
  else if ((msymbol = lookup_minimal_symbol ("_um_regular_heap", NULL, NULL))
           != NULL)
    heap_info_emx_malloc2 (SYMBOL_VALUE_ADDRESS (msymbol));
  else if ((msymbol = lookup_minimal_symbol ("_heapinfo", NULL, NULL)) != NULL)
    heap_info_gnu_malloc1 ();
  else if (heap_info_expr_flag
           && lookup_minimal_symbol ("malloc", NULL, NULL) == NULL)
    {
      _umagic magic;

      /* Attempt to support the new emx malloc() without symbols if we
         know the address of the heap (for instance, if the program is
         dynamically linked). */

      read_memory (heap_info_expr, (char *)&magic, sizeof (magic));
      if (magic == _UM_MAGIC_HEAP)
        malloc2_heap (heap_info_expr);
      else
        error ("The program uses an unsupported malloc() implementation.");
    }
  else
    error ("The program uses an unsupported malloc() implementation.");
}


struct syscall_frame
{
  int edi;
  int esi;
  int ebp;
  int esp;
  int ebx;
  int edx;
  int ecx;
  int eax;
  int eflags;
  int eip;
};

extern struct obstack frame_cache_obstack;

void emx_init_extra_frame_info (int fromleaf, struct frame_info *frame)
{
  unsigned long magic;
  CORE_ADDR syscall_a, context_a;
  CONTEXTRECORD context_record;
  struct syscall_frame syscall_frame;
  struct frame_saved_regs *fsr;

  frame->fsr = NULL;
  if (target_read_memory (frame->frame + 2*4, (char *)&magic, 4) == 0
      && magic == _SIG_MAGIC1
      && target_read_memory (frame->frame + 3*4, (char *)&magic, 4) == 0
      && magic == _SIG_MAGIC2
      && target_read_memory (frame->frame + 4*4, (char *)&magic, 4) == 0
      && magic == _SIG_MAGIC3
      && target_read_memory (frame->frame + 5*4, (char *)&magic, 4) == 0
      && magic == _SIG_MAGIC4
      && target_read_memory (frame->frame + 6*4, (char *)&syscall_a, 4) == 0
      && target_read_memory (frame->frame + 7*4, (char *)&context_a, 4) == 0)
    {
      /* This looks like the stack frame of emx.dll's
         call_handler2(). */

      if (syscall_a > frame->frame + 7*4 && (syscall_a & 3) == 0
          && context_a == 0
          && target_read_memory (syscall_a, (char *)&syscall_frame,
                                 sizeof (syscall_frame)) == 0)
        {
          /* emx.dll syscall such as raise(). */ 
          frame->signal_handler_caller = 1;
          fsr = (struct frame_saved_regs *)
            obstack_alloc (&frame_cache_obstack,
                           sizeof (struct frame_saved_regs));
          memset (fsr, 0, sizeof (*fsr));
          /* See tm-i386.h for register numbers. */
          fsr->regs[0] = syscall_a + offsetof (struct syscall_frame, eax);
          fsr->regs[1] = syscall_a + offsetof (struct syscall_frame, ecx);
          fsr->regs[2] = syscall_a + offsetof (struct syscall_frame, edx);
          fsr->regs[3] = syscall_a + offsetof (struct syscall_frame, ebx);
          fsr->regs[4] = syscall_a + offsetof (struct syscall_frame, esp);
          fsr->regs[5] = syscall_a + offsetof (struct syscall_frame, ebp);
          fsr->regs[6] = syscall_a + offsetof (struct syscall_frame, esi);
          fsr->regs[7] = syscall_a + offsetof (struct syscall_frame, edi);
          fsr->regs[8] = syscall_a + offsetof (struct syscall_frame, eip);
          fsr->regs[9] = syscall_a + offsetof (struct syscall_frame, eflags);
          frame->fsr = fsr;
          frame->sig_prev_frame = syscall_frame.ebp;
        }
      else if (context_a > frame->frame + 7*4 && (context_a & 3) == 0
               && syscall_a == 0
               && target_read_memory (context_a, (char *)&context_record,
                                      sizeof (context_record)) == 0
               && ((context_record.ContextFlags
                    & (CONTEXT_CONTROL|CONTEXT_INTEGER|CONTEXT_SEGMENTS))
                   == (CONTEXT_CONTROL|CONTEXT_INTEGER|CONTEXT_SEGMENTS)))
        {
          /* OS/2 exception */
          frame->signal_handler_caller = 1;
          fsr = (struct frame_saved_regs *)
            obstack_alloc (&frame_cache_obstack,
                           sizeof (struct frame_saved_regs));
          memset (fsr, 0, sizeof (*fsr));
          /* See tm-i386.h for register numbers. */
          fsr->regs[0] = context_a + offsetof (CONTEXTRECORD, ctx_RegEax);
          fsr->regs[1] = context_a + offsetof (CONTEXTRECORD, ctx_RegEcx);
          fsr->regs[2] = context_a + offsetof (CONTEXTRECORD, ctx_RegEdx);
          fsr->regs[3] = context_a + offsetof (CONTEXTRECORD, ctx_RegEbx);
          fsr->regs[4] = context_a + offsetof (CONTEXTRECORD, ctx_RegEsp);
          fsr->regs[5] = context_a + offsetof (CONTEXTRECORD, ctx_RegEbp);
          fsr->regs[6] = context_a + offsetof (CONTEXTRECORD, ctx_RegEsi);
          fsr->regs[7] = context_a + offsetof (CONTEXTRECORD, ctx_RegEdi);
          fsr->regs[8] = context_a + offsetof (CONTEXTRECORD, ctx_RegEip);
          fsr->regs[9] = context_a + offsetof (CONTEXTRECORD, ctx_EFlags);
          fsr->regs[10] = context_a + offsetof (CONTEXTRECORD, ctx_SegCs);
          fsr->regs[11] = context_a + offsetof (CONTEXTRECORD, ctx_SegSs);
          fsr->regs[12] = context_a + offsetof (CONTEXTRECORD, ctx_SegDs);
          fsr->regs[13] = context_a + offsetof (CONTEXTRECORD, ctx_SegEs);
          fsr->regs[14] = context_a + offsetof (CONTEXTRECORD, ctx_SegFs);
          fsr->regs[15] = context_a + offsetof (CONTEXTRECORD, ctx_SegGs);
          frame->fsr = fsr;
          frame->sig_prev_frame = context_record.ctx_RegEbp;
        }
    }
}


int emx_frame_chain_valid (CORE_ADDR chain, struct frame_info *thisframe)
{
  CORE_ADDR pc;

  if (chain == 0)
    return 0;
  pc = FRAME_SAVED_PC (thisframe);
  if ((pc & 0xffff) == 12 + 5)  /* emx_syscall */
    return 1;
  return !inside_entry_file (pc);
}


void
_initialize_emx_target ()
{
  add_info ("heap", heap_info,
	    "Print information about a heap.\n\
With argument, print information about the heap or the heap substructure\n\
at the address specified by the argument.  Without argument, print\n\
information about the default heap.\n\
/v can be used after \"info heap\" to turn on verbose output.");
}
