Exemplo n.º 1
0
static struct interp *
find_mi_interpreter (void)
{
  struct interp *interp;

  interp = top_level_interpreter ();
  if (ui_out_is_mi_like_p (interp_ui_out (interp)))
    return interp;

  interp = command_interp ();
  if (ui_out_is_mi_like_p (interp_ui_out (interp)))
    return interp;

  return NULL;
}
Exemplo n.º 2
0
static void
btrace_call_history (struct ui_out *uiout,
		     const struct btrace_thread_info *btinfo,
		     const struct btrace_call_iterator *begin,
		     const struct btrace_call_iterator *end,
		     enum record_print_flag flags)
{
  struct btrace_call_iterator it;

  DEBUG ("ftrace (0x%x): [%u; %u)", flags, btrace_call_number (begin),
	 btrace_call_number (end));

  for (it = *begin; btrace_call_cmp (&it, end) < 0; btrace_call_next (&it, 1))
    {
      const struct btrace_function *bfun;
      struct minimal_symbol *msym;
      struct symbol *sym;

      bfun = btrace_call_get (&it);
      sym = bfun->sym;
      msym = bfun->msym;

      /* Print the function index.  */
      ui_out_field_uint (uiout, "index", bfun->number);
      ui_out_text (uiout, "\t");

      if ((flags & RECORD_PRINT_INDENT_CALLS) != 0)
	{
	  int level = bfun->level + btinfo->level, i;

	  for (i = 0; i < level; ++i)
	    ui_out_text (uiout, "  ");
	}

      if (sym != NULL)
	ui_out_field_string (uiout, "function", SYMBOL_PRINT_NAME (sym));
      else if (msym != NULL)
	ui_out_field_string (uiout, "function", MSYMBOL_PRINT_NAME (msym));
      else if (!ui_out_is_mi_like_p (uiout))
	ui_out_field_string (uiout, "function", "??");

      if ((flags & RECORD_PRINT_INSN_RANGE) != 0)
	{
	  ui_out_text (uiout, _("\tinst "));
	  btrace_call_history_insn_range (uiout, bfun);
	}

      if ((flags & RECORD_PRINT_SRC_LINE) != 0)
	{
	  ui_out_text (uiout, _("\tat "));
	  btrace_call_history_src_line (uiout, bfun);
	}

      ui_out_text (uiout, "\n");
    }
}
Exemplo n.º 3
0
static void
signal_catchpoint_print_one (struct breakpoint *b,
			     struct bp_location **last_loc)
{
  struct signal_catchpoint *c = (void *) b;
  struct value_print_options opts;
  struct ui_out *uiout = current_uiout;

  get_user_print_options (&opts);

  /* Field 4, the address, is omitted (which makes the columns
     not line up too nicely with the headers, but the effect
     is relatively readable).  */
  if (opts.addressprint)
    ui_out_field_skip (uiout, "addr");
  annotate_field (5);

  if (c->signals_to_be_caught
      && VEC_length (gdb_signal_type, c->signals_to_be_caught) > 1)
    ui_out_text (uiout, "signals \"");
  else
    ui_out_text (uiout, "signal \"");

  if (c->signals_to_be_caught)
    {
      int i;
      gdb_signal_type iter;
      struct obstack text;
      struct cleanup *cleanup;

      obstack_init (&text);
      cleanup = make_cleanup_obstack_free (&text);

      for (i = 0;
           VEC_iterate (gdb_signal_type, c->signals_to_be_caught, i, iter);
           i++)
        {
	  const char *name = signal_to_name_or_int (iter);

	  if (i > 0)
	    obstack_grow (&text, " ", 1);
	  obstack_grow (&text, name, strlen (name));
        }
      obstack_grow (&text, "", 1);
      ui_out_field_string (uiout, "what", obstack_base (&text));
      do_cleanups (cleanup);
    }
  else
    ui_out_field_string (uiout, "what",
			 c->catch_all ? "<any signal>" : "<standard signals>");
  ui_out_text (uiout, "\" ");

  if (ui_out_is_mi_like_p (uiout))
    ui_out_field_string (uiout, "catch-type", "signal");
}
Exemplo n.º 4
0
void
cmd_show_list (struct cmd_list_element *list, int from_tty, const char *prefix)
{
  struct cleanup *showlist_chain;
  struct ui_out *uiout = current_uiout;

  showlist_chain = make_cleanup_ui_out_tuple_begin_end (uiout, "showlist");
  for (; list != NULL; list = list->next)
    {
      /* If we find a prefix, run its list, prefixing our output by its
         prefix (with "show " skipped).  */
      if (list->prefixlist && !list->abbrev_flag)
	{
	  struct cleanup *optionlist_chain
	    = make_cleanup_ui_out_tuple_begin_end (uiout, "optionlist");
	  char *new_prefix = strstr (list->prefixname, "show ") + 5;

	  if (ui_out_is_mi_like_p (uiout))
	    ui_out_field_string (uiout, "prefix", new_prefix);
	  cmd_show_list (*list->prefixlist, from_tty, new_prefix);
	  /* Close the tuple.  */
	  do_cleanups (optionlist_chain);
	}
      else
	{
	  if (list->theclass != no_set_class)
	    {
	      struct cleanup *option_chain
		= make_cleanup_ui_out_tuple_begin_end (uiout, "option");

	      ui_out_text (uiout, prefix);
	      ui_out_field_string (uiout, "name", list->name);
	      ui_out_text (uiout, ":  ");
	      if (list->type == show_cmd)
		do_show_command ((char *) NULL, from_tty, list);
	      else
		cmd_func (list, NULL, from_tty);
	      /* Close the tuple.  */
	      do_cleanups (option_chain);
	    }
	}
    }
  /* Close the tuple.  */
  do_cleanups (showlist_chain);
}
Exemplo n.º 5
0
void
do_setshow_command(char *arg, int from_tty, struct cmd_list_element *c)
{
  if (c->type == set_cmd)
    {
      switch (c->var_type)
	{
	case var_string:
	  {
	    char *newstr;
	    char *p;
	    char *q;
	    int ch;

	    if (arg == NULL)
	      arg = "";
	    newstr = (char *)xmalloc(strlen(arg) + 2);
	    p = arg;
	    q = newstr;
	    while ((ch = *p++) != '\000')
	      {
		if (ch == '\\')
		  {
		    /* \ at end of argument is used after spaces
		       so they won't be lost.  */
		    /* This is obsolete now that we no longer strip
		       trailing whitespace and actually, the backslash
		       didn't get here in my test, readline or
		       something did something funky with a backslash
		       right before a newline.  */
		    if (*p == 0)
		      break;
		    ch = parse_escape(&p);
		    if (ch == 0)
		      break;	/* C loses */
		    else if (ch > 0)
		      *q++ = ch;
		  }
		else
		  *q++ = ch;
	      }
#if 0
	    if (*(p - 1) != '\\')
	      *q++ = ' ';
#endif /* 0 */
	    *q++ = '\0';
	    newstr = (char *)xrealloc(newstr, (q - newstr));
	    if (*(char **)c->var != NULL)
	      xfree(*(char **)c->var);
	    *(char **)c->var = newstr;
	  }
	  break;
	case var_string_noescape:
	  if (arg == NULL)
	    arg = "";
	  if (*(char **)c->var != NULL)
	    xfree(*(char **)c->var);
	  *(char **)c->var = savestring(arg, strlen(arg));
	  break;
	case var_optional_filename:
	  if (arg == NULL)
	    arg = "";
	  if (*(char **)c->var != NULL)
	    xfree(*(char **)c->var);
	  *(char **)c->var = savestring(arg, strlen(arg));
	  break;
	case var_filename:
	  if (arg == NULL)
	    error_no_arg(_("filename to set it to."));
	  if (*(char **)c->var != NULL)
	    xfree (*(char **)c->var);
	  *(char **)c->var = tilde_expand(arg);
	  break;
	case var_boolean:
	  *(int *)c->var = parse_binary_operation(arg);
	  break;
	case var_auto_boolean:
	  *(enum auto_boolean *)c->var = parse_auto_binary_operation(arg);
	  break;
	case var_uinteger:
	  if (arg == NULL)
	    error_no_arg(_("integer to set it to."));
	  *(unsigned int *)c->var = (unsigned int)parse_and_eval_long(arg);
	  if (*(unsigned int *)c->var == 0)
	    *(unsigned int *)c->var = UINT_MAX;
	  break;
	case var_integer:
	  {
	    unsigned int val;
	    if (arg == NULL)
	      error_no_arg(_("integer to set it to."));
	    val = (unsigned int)parse_and_eval_long(arg);
	    if (val == 0)
	      *(int *)c->var = INT_MAX;
	    else if (val >= INT_MAX)
	      error(_("integer %u out of range"), val);
	    else
	      *(int *)c->var = val;
	    break;
	  }
	case var_zinteger:
	  if (arg == NULL)
	    error_no_arg(_("integer to set it to."));
	  *(int *)c->var = (int)parse_and_eval_long(arg);
	  break;
	case var_enum:
	  {
	    int i;
	    int len;
	    int nmatches = 0;
	    const char *match = NULL;
	    char *p;

	    /* APPLE LOCAL: Give the valid options for all error
	       messages for enum type commands. */

	    /* If an argument was supplied, parse it. */
	    if (arg != NULL)
	      {
		p = strchr(arg, ' ');

		if (p)
		  len = (p - arg);
		else
		  len = strlen(arg);

		nmatches = 0;
		for (i = 0; c->enums[i]; i++)
		  if (strncmp(arg, c->enums[i], len) == 0)
		    {
		      if (c->enums[i][len] == '\0')
			{
			  match = c->enums[i];
			  nmatches = 1;
			  break; /* exact match. */
			}
		      else
			{
			  match = c->enums[i];
			  nmatches++;
			}
		    }
	      }

	    if (nmatches == 1)
	      *(const char **)c->var = match;
	    else
	      {
                /* If there was an error, print an informative
                   error message.  */
                struct ui_file *tmp_error_stream = mem_fileopen();
                make_cleanup_ui_file_delete (tmp_error_stream);

                if (arg == NULL)
                  fprintf_unfiltered(tmp_error_stream, "Requires an argument.");
                else if (nmatches <= 0)
                  fprintf_unfiltered(tmp_error_stream, "Undefined item: \"%s\".", arg);
		else if (nmatches > 1)
                  fprintf_unfiltered(tmp_error_stream, "Ambiguous item \"%s\".", arg);

                fprintf_unfiltered(tmp_error_stream, " Valid arguments are ");
                for (i = 0; c->enums[i]; i++)
                  {
                    if (i != 0)
                      fprintf_unfiltered(tmp_error_stream, ", ");
                    fputs_unfiltered(c->enums[i], tmp_error_stream);
                  }
                fprintf_unfiltered(tmp_error_stream, ".");
                error_stream(tmp_error_stream);
	      }

	    /* END APPLE LOCAL */
	  }
	  break;
	default:
	  error(_("gdb internal error: bad var_type in do_setshow_command"));
	}
    }
  else if (c->type == show_cmd)
    {
      struct cleanup *old_chain;
      struct ui_stream *stb;

      stb = ui_out_stream_new(uiout);
      old_chain = make_cleanup_ui_out_stream_delete(stb);

      /* Possibly call the pre hook: */
      if (c->pre_show_hook)
	(c->pre_show_hook)(c);

      switch (c->var_type)
	{
	case var_string:
	  {
	    if (*(unsigned char **)c->var)
	      fputstr_filtered(*(char **)c->var, '"', stb->stream);
	  }
	  break;
	case var_string_noescape:
	case var_optional_filename:
	case var_filename:
	case var_enum:
	  if (*(char **)c->var)
	    fputs_filtered(*(char **)c->var, stb->stream);
	  break;
	case var_boolean:
	  fputs_filtered(*(int *)c->var ? "on" : "off", stb->stream);
	  break;
	case var_auto_boolean:
	  switch (*(enum auto_boolean*)c->var)
	    {
	    case AUTO_BOOLEAN_TRUE:
	      fputs_filtered("on", stb->stream);
	      break;
	    case AUTO_BOOLEAN_FALSE:
	      fputs_filtered("off", stb->stream);
	      break;
	    case AUTO_BOOLEAN_AUTO:
	      fputs_filtered("auto", stb->stream);
	      break;
	    default:
	      internal_error(__FILE__, __LINE__,
			     _("do_setshow_command: invalid var_auto_boolean"));
	      break;
	    }
	  break;
	case var_uinteger:
	  if (*(unsigned int *)c->var == UINT_MAX)
	    {
	      fputs_filtered("unlimited", stb->stream);
	      break;
	    }
	  /* else fall through */
	case var_zinteger:
	  fprintf_filtered(stb->stream, "%u", *(unsigned int *)c->var);
	  break;
	case var_integer:
	  if (*(int *)c->var == INT_MAX)
	    {
	      fputs_filtered("unlimited", stb->stream);
	    }
	  else
	    fprintf_filtered(stb->stream, "%d", *(int *)c->var);
	  break;

	default:
	  error(_("gdb internal error: bad var_type in do_setshow_command"));
	}


      /* FIXME: cagney/2005-02-10: Need to split this in half: code to
	 convert the value into a string (esentially the above); and
	 code to print the value out.  For the latter there should be
	 MI and CLI specific versions.  */

      if (ui_out_is_mi_like_p(uiout))
	ui_out_field_stream(uiout, "value", stb);
      else
	{
	  long length;
	  char *value = ui_file_xstrdup(stb->stream, &length);
	  make_cleanup(xfree, value);
	  if (c->show_value_func != NULL)
	    c->show_value_func(gdb_stdout, from_tty, c, value);
	  else
	    deprecated_show_value_hack(gdb_stdout, from_tty, c, value);
	}
      do_cleanups(old_chain);
    }
  else
    error(_("gdb internal error: bad cmd_type in do_setshow_command"));
  c->func(c, NULL, from_tty);
  if ((c->type == set_cmd) && deprecated_set_hook)
    deprecated_set_hook(c);
}
Exemplo n.º 6
0
void
do_show_command (const char *arg, int from_tty, struct cmd_list_element *c)
{
  struct ui_out *uiout = current_uiout;
  struct cleanup *old_chain;
  struct ui_file *stb;

  gdb_assert (c->type == show_cmd);

  stb = mem_fileopen ();
  old_chain = make_cleanup_ui_file_delete (stb);

  /* Possibly call the pre hook.  */
  if (c->pre_show_hook)
    (c->pre_show_hook) (c);

  switch (c->var_type)
    {
    case var_string:
      if (*(char **) c->var)
	fputstr_filtered (*(char **) c->var, '"', stb);
      break;
    case var_string_noescape:
    case var_optional_filename:
    case var_filename:
    case var_enum:
      if (*(char **) c->var)
	fputs_filtered (*(char **) c->var, stb);
      break;
    case var_boolean:
      fputs_filtered (*(int *) c->var ? "on" : "off", stb);
      break;
    case var_auto_boolean:
      switch (*(enum auto_boolean*) c->var)
	{
	case AUTO_BOOLEAN_TRUE:
	  fputs_filtered ("on", stb);
	  break;
	case AUTO_BOOLEAN_FALSE:
	  fputs_filtered ("off", stb);
	  break;
	case AUTO_BOOLEAN_AUTO:
	  fputs_filtered ("auto", stb);
	  break;
	default:
	  internal_error (__FILE__, __LINE__,
			  _("do_show_command: "
			    "invalid var_auto_boolean"));
	  break;
	}
      break;
    case var_uinteger:
    case var_zuinteger:
      if (c->var_type == var_uinteger
	  && *(unsigned int *) c->var == UINT_MAX)
	fputs_filtered ("unlimited", stb);
      else
	fprintf_filtered (stb, "%u", *(unsigned int *) c->var);
      break;
    case var_integer:
    case var_zinteger:
      if (c->var_type == var_integer
	  && *(int *) c->var == INT_MAX)
	fputs_filtered ("unlimited", stb);
      else
	fprintf_filtered (stb, "%d", *(int *) c->var);
      break;
    case var_zuinteger_unlimited:
      {
	if (*(int *) c->var == -1)
	  fputs_filtered ("unlimited", stb);
	else
	  fprintf_filtered (stb, "%d", *(int *) c->var);
      }
      break;
    default:
      error (_("gdb internal error: bad var_type in do_show_command"));
    }


  /* FIXME: cagney/2005-02-10: Need to split this in half: code to
     convert the value into a string (esentially the above); and
     code to print the value out.  For the latter there should be
     MI and CLI specific versions.  */

  if (ui_out_is_mi_like_p (uiout))
    ui_out_field_stream (uiout, "value", stb);
  else
    {
      char *value = ui_file_xstrdup (stb, NULL);

      make_cleanup (xfree, value);
      if (c->show_value_func != NULL)
	c->show_value_func (gdb_stdout, from_tty, c, value);
      else
	deprecated_show_value_hack (gdb_stdout, from_tty, c, value);
    }
  do_cleanups (old_chain);

  c->func (c, NULL, from_tty);
}
Exemplo n.º 7
0
int
find_pc_offset(CORE_ADDR start, CORE_ADDR *result, int offset,
               int funclimit, int peeklimit)
{
  CORE_ADDR low = INVALID_ADDRESS;
  CORE_ADDR high = INVALID_ADDRESS;
  CORE_ADDR cur;
  CORE_ADDR constrained;

  int length;

  struct disassemble_info di = gdb_disassemble_info_null(current_gdbarch);
  CORE_ADDR *addrs = NULL;
  unsigned int index;
  struct cleanup *cleanup = NULL;

  *result = INVALID_ADDRESS;
  cur = start;

  /* If we are constraining the address to stay in the same function,
     we need to be able to find its boundaries. */

  if (funclimit)
    {
      if (find_pc_partial_function_no_inlined(start, NULL, &low, &high) == 0)
	{
	  /* We were unable to find the start of the function. */
	  return -1;
	}
    }

  /* If the architecture has fixed-sized instructions, just use simple
     arithmetic. */

  length = gdbarch_instruction_length(current_gdbarch);
  if (length > 0)
    {
      cur = (start + (length * offset));

      /* Constrain to be within the function limits if appropriate. */
      if (funclimit && (cur > high))
	constrained = high;
      else if (funclimit && (cur < low))
	constrained = low;
      else
	constrained = cur;

      /* Return 1 if we constrained the address; 0 otherwise. */
      *result = constrained;
      return (constrained != cur);
    }

  /* From here, we must assume variable-sized instructions. */

  if ((! funclimit) && (offset < 0))
    {
      /* FIXME: We don't support seeking backwards past the beginning
	 of a function. */
      return -1;
    }

  /* If we have a positive offset, start seeking forward until we are
     either done, or reach the end of the function. */

  cur = start;
  while (offset > 0)
    {
      cur += TARGET_PRINT_INSN(cur, &di);
      offset--;

      if (funclimit && (cur > high))
	{
	  /* We went past the end of the function without ever
	     reaching the purportedly final instruction. */
	  return -1;
	}

      if (funclimit && (cur == high))
	{
	  /* We reached the end of the function.  Return 1 if we had
	     to constrain the address; 0 otherwise. */
	  *result = cur;
	  return (offset > 0);
	}
    }

  if (offset == 0)
    {
      *result = cur;
      return 0;
    }

  /* From here out we can assume we are doing a negative offset. */

  gdb_assert(low <= start);
  gdb_assert(offset < 0);

  /* A sanity check:  If we've stepped into some area of memory where
     gdb doesn't have symbols and the GUI requests we disassemble from $pc,
     gdb can come up with very large LOW-HIGH regions of memory to disassemble
     through.  As a sanity check, if this function starts four pages before
     the given $pc and we're in MI mode (so we have a GUI that may be
     requesting nonsensical things), shortcircuit this operation.  */

  if (((off_t)(start - low) > -offset) && ((start - low) > 16384)
      && ui_out_is_mi_like_p(uiout))
    {
      *result = start;
      return 1;
    }

  /* There's no point searching for more instructions slots than there
     are bytes.  If we were given a PEEKLIMIT of -1, or a PEEKLIMIT
     higher than we need, set it to the number of bytes from the start
     of the function. */

  if ((peeklimit < 0) || ((CORE_ADDR)peeklimit > (start - low)))
    peeklimit = (int)(start - low);

  /* If PEEKLIMIT is less than (start - low), we can still attempt the
     search --- maybe enough of the instruction stream will be
     multi-byte that we'll find our address regardless. */

  addrs = (CORE_ADDR *)xmalloc(peeklimit * sizeof(CORE_ADDR));
  cleanup = make_cleanup(xfree, addrs);

  /* We can assume that we are constrained to the current function at
     this point (see the comment above). */

  gdb_assert(funclimit);

  cur = low;
  index = 0;

  /* Seek forward until we either reach our starting point, or reach
     PEEKLIMIT. */

  for (;;)
    {
      if (cur >= start)
	break;
      if (index >= (unsigned int)peeklimit)
	break;

      gdb_assert((int)index < peeklimit);
      addrs[index++] = cur;
      cur += TARGET_PRINT_INSN(cur, &di);
    }

  if (cur == start)
    {
      /* We were able to seek all the way forward to the start address. */

      gdb_assert(funclimit);
      gdb_assert(offset < 0);

      if ((off_t)index < -offset)
	{
	  /* We weren't able to go far enough back; return the earliest
             instruction of the function.  */
	  *result = low;
	  do_cleanups(cleanup);
	  return 1;
	}
      else
	{
	  *result = addrs[index + offset];
	  do_cleanups(cleanup);
	  return 0;
	}
    }

  if (cur > start)
    {
      /* We seeked forward right past the start address, without ever
	 hitting it. */
      do_cleanups(cleanup);
      return -1;
    }

  if (index >= (unsigned int)peeklimit)
    {
      /* We went past PEEKLIMIT instructions, and hence, weren't able
	 to complete the backwards seek.  */
      do_cleanups(cleanup);
      return -1;
    }

  internal_error(__FILE__, __LINE__, "should never have reached here");
  do_cleanups(cleanup);
  return -1;
}
Exemplo n.º 8
0
static void
darwin_debug_regions_recurse (task_t task)
{
  mach_vm_address_t r_addr;
  mach_vm_address_t r_start;
  mach_vm_size_t r_size;
  natural_t r_depth;
  mach_msg_type_number_t r_info_size;
  vm_region_submap_short_info_data_64_t r_info;
  kern_return_t kret;
  int ret;
  struct cleanup *table_chain;
  struct ui_out *uiout = current_uiout;

  table_chain = make_cleanup_ui_out_table_begin_end (uiout, 9, -1, "regions");

  if (gdbarch_addr_bit (target_gdbarch ()) <= 32)
    {
      ui_out_table_header (uiout, 10, ui_left, "start", "Start");
      ui_out_table_header (uiout, 10, ui_left, "end", "End");
    }
  else
    {
      ui_out_table_header (uiout, 18, ui_left, "start", "Start");
      ui_out_table_header (uiout, 18, ui_left, "end", "End");
    }
  ui_out_table_header (uiout, 3, ui_left, "min-prot", "Min");
  ui_out_table_header (uiout, 3, ui_left, "max-prot", "Max");
  ui_out_table_header (uiout, 5, ui_left, "inheritence", "Inh");
  ui_out_table_header (uiout, 9, ui_left, "share-mode", "Shr");
  ui_out_table_header (uiout, 1, ui_left, "depth", "D");
  ui_out_table_header (uiout, 3, ui_left, "submap", "Sm");
  ui_out_table_header (uiout, 0, ui_noalign, "tag", "Tag");

  ui_out_table_body (uiout);

  r_start = 0;
  r_depth = 0;
  while (1)
    {
      const char *tag;
      struct cleanup *row_chain;

      r_info_size = VM_REGION_SUBMAP_SHORT_INFO_COUNT_64;
      r_size = -1;
      kret = mach_vm_region_recurse (task, &r_start, &r_size, &r_depth,
				     (vm_region_recurse_info_t) &r_info,
				     &r_info_size);
      if (kret != KERN_SUCCESS)
	break;
      row_chain = make_cleanup_ui_out_tuple_begin_end (uiout, "regions-row");

      ui_out_field_core_addr (uiout, "start", target_gdbarch (), r_start);
      ui_out_field_core_addr (uiout, "end", target_gdbarch (), r_start + r_size);
      ui_out_field_string (uiout, "min-prot", 
			   unparse_protection (r_info.protection));
      ui_out_field_string (uiout, "max-prot", 
			   unparse_protection (r_info.max_protection));
      ui_out_field_string (uiout, "inheritence",
			   unparse_inheritance (r_info.inheritance));
      ui_out_field_string (uiout, "share-mode",
			   unparse_share_mode (r_info.share_mode));
      ui_out_field_int (uiout, "depth", r_depth);
      ui_out_field_string (uiout, "submap",
			   r_info.is_submap ? _("sm ") : _("obj"));
      tag = unparse_user_tag (r_info.user_tag);
      if (tag)
	ui_out_field_string (uiout, "tag", tag);
      else
	ui_out_field_int (uiout, "tag", r_info.user_tag);

      do_cleanups (row_chain);

      if (!ui_out_is_mi_like_p (uiout))
	ui_out_text (uiout, "\n");

      if (r_info.is_submap)
	r_depth++;
      else
	r_start += r_size;
    }
  do_cleanups (table_chain);

}