[RFC] add 'save-breakpoints' command

Klee Dienes
2001-12-04T18:21:47Z

For a long time, we (the folks at Apple), have been wanting to unify
our source tree with the Cygnus GDB source tree, for all the
free-software reasons I'm sure I don't need enumerate.

Over the past couple of years, we managed to bring our source tree
up-to-date with respect the current Cygnus tree (it used to be based
off of gdb-4.14), executed a copyright assignment, and made a massive
diff available, but the actual work of pushing our changes back to
Cygnus (cleanly and as individual patch files) has always been
prioritized just under "make GDB work well on our platform."

Thankfully, GDB now works pretty well on our platform, so we're
finally getting the time to do the work of merging our changes back
into the Cygnus tree.

We've got a ton of stuff to submit, but as a warm-up, I'm starting
witha relatively minor feature-addition --- a command to save the
current set of breakpoints to a file that can be read in later. We've
been using this on our platform for a few months now, and folks seem
to like it. I've also tested the patch against the i386/Linux
testsuite ... nothing appears to break.

My one concern with the patch is that it exports the value of 'set
input-radix' as a GDB convenience variable ($input_radix), so that it
can be accessed from within GDB command scripts. I tried to come up
with a more generic way of doing this, but all were unsatisfactory
... suggestions on a better way to do this would be appreciated.

To use the command, do 'save-breakpoints <filename>' at any time. You
can then restore the set of breakpoints thus saved by sourcing the
contents of <filename>.

Thanks for your feedback; hopefully we can keep a steady stream of
patches coming in in the upcoming months.

2001-12-04 Ira L. Ruben <ira@apple.com>
Submitted by Klee Dienes <kdienes@apple.com>

* ui-out.c, ui-out.h: Add ui_out_delete.

* utils.c, defs.h: Add make_cleanup_ui_out_delete.

* breakpoint.c (parse_breakpoint_sals): Handle case where address
is NULL and default_breakpoint_valid to set addr_string to "*pc"
(sal.pc) to avoid warning from breakpoint_re_set_one().
(set_raw_breakpoint): init new original_type field.
(create_breakpoints): Handle new original_type argument.
(break_command_1): Ditto.
(do_captured_breakpoint): Pass new original type to create_breakpoints.
(break_at_finish_at_depth_command_1): pass original type to
break_command_1.
(break_at_finish_command_1): Ditto.
(break_command): Ditto.
(future_break_command_1): Ditto.
(tbreak_command): Ditto.
(hbreak_command): Ditto.
(thbreak_command): Ditto.
(stopin_command): Ditto.
(stopat_command): Ditto.
(watch_command_1): Handle new original_type argument.
(watch_command): Pass new original type to watch_command_1.
(rwatch_command_wrapper): Ditto.
(awatch_command): Ditto.
(ignore_command): Suppress newline if from_tty is false.
(write_one_breakpoint): new routine for save_breakpoints_command.
(save_breakpoints_command): Add save-breakpoints command.
(_initialize_breakpoint): Define save-breakpoints and its aliases.

* breakpoint.h (enum bptype): Add new breakpoint types.
(struct breakpoint): Add field to recore original breakpoint type.

* main.c (captured_main): Initialize $input_radix and $output_radix.
These are referenced by a save-breakpoints file to preserve radix
across the breakpoint restoration.

* tracepoint.c (tracepoint_save_command): Fix fopen error reporting to
show errno information just like save-breakpoints command.

* valprint.c (set_input_radix_1): Set $input_radix.
(set_output_radix_1): Set $output_radix.

Index: breakpoint.c
===================================================================
RCS file: /cvs/src/src/gdb/breakpoint.c,v
retrieving revision 1.57
diff -u -r1.57 breakpoint.c
--- breakpoint.c 2001/11/11 16:39:59 1.57
+++ breakpoint.c 2001/12/04 17:59:32
@@ -42,6 +42,10 @@
#include "linespec.h"
#include "completer.h"
#include "gdb.h"
+#include "top.h"
+#include "cli-out.h"
+#include <time.h>
+#include <locale.h>
#ifdef UI_OUT
#include "ui-out.h"
#endif
@@ -89,7 +93,7 @@

static void tbreak_at_finish_command (char *, int);

-static void break_command_1 (char *, int, int);
+static void break_command_1 (char *, int, enum bptype, int);

static void mention (struct breakpoint *);

@@ -159,7 +163,7 @@

static void thbreak_command (char *, int);

-static void watch_command_1 (char *, int, int);
+static void watch_command_1 (char *, int, enum bptype, int);

static void rwatch_command (char *, int);

@@ -330,6 +334,12 @@
value_from_longest (builtin_type_int, (LONGEST) num));
}

+int
+get_breakpoint_count (void)
+{
+ return breakpoint_count;
+}
+
/* Used in run_command to zero the hit count when a new run starts. */

void
@@ -4612,6 +4622,7 @@
create_breakpoints (struct symtabs_and_lines sals, char **addr_string,
struct expression **cond, char **cond_string,
enum bptype type, enum bpdisp disposition,
+ enum bptype original_type,
int thread, int ignore_count, int from_tty)
{
if (type == bp_hardware_breakpoint)
@@ -4756,7 +4767,7 @@
second bit : 0 normal breakpoint, 1 hardware breakpoint. */

static void
-break_command_1 (char *arg, int flag, int from_tty)
+break_command_1 (char *arg, int flag, enum bptype original_type, int from_tty)
{
int tempflag, hardwareflag;
struct symtabs_and_lines sals;
@@ -4870,7 +4881,7 @@

create_breakpoints (sals, addr_string, cond, cond_string,
hardwareflag ? bp_hardware_breakpoint : bp_breakpoint,
- tempflag ? disp_del : disp_donttouch,
+ tempflag ? disp_del : disp_donttouch, original_type,
thread, ignore_count, from_tty);

if (sals.nelts > 1)
@@ -4982,7 +4993,7 @@

create_breakpoints (sals, addr_string, cond, cond_string,
args->hardwareflag ? bp_hardware_breakpoint : bp_breakpoint,
- args->tempflag ? disp_del : disp_donttouch,
+ args->tempflag ? disp_del : disp_donttouch, bp_none,
args->thread, args->ignore_count, 0/*from-tty*/);

/* That's it. Discard the cleanups for data inserted into the
@@ -5076,7 +5087,7 @@
sprintf (addr_string, "*0x%s %s", paddr_nz (high), extra_args);
else
sprintf (addr_string, "*0x%s", paddr_nz (high));
- break_command_1 (addr_string, flag, from_tty);
+ break_command_1 (addr_string, flag, bp_none, from_tty);
xfree (addr_string);
}
else
@@ -5158,7 +5169,7 @@
sprintf (break_string, "*0x%s %s", paddr_nz (high), extra_args);
else
sprintf (break_string, "*0x%s", paddr_nz (high));
- break_command_1 (break_string, flag, from_tty);
+ break_command_1 (break_string, flag, bp_none, from_tty);
xfree (break_string);
}
else
@@ -5225,7 +5236,7 @@
void
break_command (char *arg, int from_tty)
{
- break_command_1 (arg, 0, from_tty);
+ break_command_1 (arg, 0, bp_breakpoint, from_tty);
}

static void
@@ -5243,7 +5254,7 @@
void
tbreak_command (char *arg, int from_tty)
{
- break_command_1 (arg, BP_TEMPFLAG, from_tty);
+ break_command_1 (arg, BP_TEMPFLAG, bp_tbreakpoint, from_tty);
}

static void
@@ -5255,13 +5266,13 @@
static void
hbreak_command (char *arg, int from_tty)
{
- break_command_1 (arg, BP_HARDWAREFLAG, from_tty);
+ break_command_1 (arg, BP_HARDWAREFLAG, bp_hardware_breakpoint, from_tty);
}

static void
thbreak_command (char *arg, int from_tty)
{
- break_command_1 (arg, (BP_TEMPFLAG | BP_HARDWAREFLAG), from_tty);
+ break_command_1 (arg, (BP_TEMPFLAG | BP_HARDWAREFLAG), bp_thbreakpoint, from_tty);
}

static void
@@ -5302,7 +5313,7 @@
if (badInput)
printf_filtered ("Usage: stop in <function | address>\n");
else
- break_command_1 (arg, 0, from_tty);
+ break_command_1 (arg, 0, bp_none, from_tty);
}

static void
@@ -5334,7 +5345,7 @@
if (badInput)
printf_filtered ("Usage: stop at <line>\n");
else
- break_command_1 (arg, 0, from_tty);
+ break_command_1 (arg, 0, bp_none, from_tty);
}

/* ARGSUSED */
@@ -5342,7 +5353,7 @@
hw_read: watch read,
hw_access: watch access (read or write) */
static void
-watch_command_1 (char *arg, int accessflag, int from_tty)
+watch_command_1 (char *arg, int accessflag, enum bptype original_type, int from_tty)
{
struct breakpoint *b;
struct symtab_and_line sal;
@@ -5603,7 +5614,7 @@
static void
watch_command (char *arg, int from_tty)
{
- watch_command_1 (arg, hw_write, from_tty);
+ watch_command_1 (arg, hw_write, bp_hardware_watchpoint, from_tty);
}

#ifdef UI_OUT
@@ -5616,7 +5627,7 @@
static void
rwatch_command (char *arg, int from_tty)
{
- watch_command_1 (arg, hw_read, from_tty);
+ watch_command_1 (arg, hw_read, bp_read_watchpoint, from_tty);
}

#ifdef UI_OUT
@@ -5629,7 +5640,7 @@
static void
awatch_command (char *arg, int from_tty)
{
- watch_command_1 (arg, hw_access, from_tty);
+ watch_command_1 (arg, hw_access, bp_access_watchpoint, from_tty);
}
\f

@@ -7570,6 +7581,262 @@
{
map_breakpoint_numbers (args, enable_delete_breakpoint);
}
+
+/* Generate a break, watch, or catch command defined by B to the STREAM.
+
+ General worse case example,
+
+ break thread <t> if <expr>
+ commands
+ <command lines...>
+ end
+ ignore $bpnum <count>
+ disable $bpnum
+
+ In addition "set input-radix <r>" may precede the above sequence but
+ that is generated by save_breakpoints_command() which is this
+ function's caller. */
+
+static void
+write_one_breakpoint (struct breakpoint *b, struct ui_file *stream, struct ui_out *uiout)
+{
+ register struct command_line *l;
+
+ switch (b->type)
+ {
+ case bp_watchpoint:
+ case bp_hardware_watchpoint:
+ case bp_read_watchpoint:
+ case bp_access_watchpoint:
+ if (b->original_type == bp_none)
+ {
+ if (b->type == bp_read_watchpoint)
+ fprintf_unfiltered (stream, "rwatch %s", b->exp_string);
+ else if (b->type == bp_access_watchpoint)
+ fprintf_unfiltered (stream, "awatch %s", b->exp_string);
+ else
+ fprintf_unfiltered (stream, "watch %s", b->exp_string);
+ }
+ else
+ {
+ if (b->original_type == bp_read_watchpoint)
+ fprintf_unfiltered (stream, "rwatch %s", b->exp_string);
+ else if (b->original_type == bp_access_watchpoint)
+ fprintf_unfiltered (stream, "awatch %s", b->exp_string);
+ else
+ fprintf_unfiltered (stream, "watch %s", b->exp_string);
+ }
+ break;
+
+ case bp_catch_load:
+ case bp_catch_unload:
+ fprintf_unfiltered (stream, "%scatch %sload", b->disposition == disp_del ? "t" : "",
+ b->type == bp_catch_unload ? "un" : "");
+ if (b->dll_pathname != NULL)
+ fputs_unfiltered (b->dll_pathname, stream);
+ break;
+
+ case bp_catch_fork:
+ fprintf_unfiltered (stream, "%scatch fork", b->disposition == disp_del ? "t" : "");
+ break;
+
+ case bp_catch_vfork:
+ fprintf_unfiltered (stream, "%scatch vfork", b->disposition == disp_del ? "t" : "");
+ break;
+
+ case bp_catch_exec:
+ fprintf_unfiltered (stream, "%scatch exec", b->disposition == disp_del ? "t" : "");
+ break;
+
+ case bp_catch_catch:
+ fprintf_unfiltered (stream, "%scatch catch", b->disposition == disp_del ? "t" : "");
+ break;
+
+ case bp_catch_throw:
+ fprintf_unfiltered (stream, "%scatch throw", b->disposition == disp_del ? "t" : "");
+ break;
+
+ case bp_breakpoint:
+ case bp_hardware_breakpoint:
+ switch (b->original_type)
+ {
+ case bp_breakpoint:
+ fprintf_unfiltered (stream, "break");
+ break;
+ case bp_hardware_breakpoint:
+ fprintf_unfiltered (stream, "hbreak");
+ break;
+#if 0
+ case bp_fbreakpoint:
+ fprintf_unfiltered (stream, "future-break");
+ break;
+#endif
+ case bp_tbreakpoint:
+ fprintf_unfiltered (stream, "tbreak");
+ break;
+ case bp_thbreakpoint:
+ fprintf_unfiltered (stream, "thbreak");
+ break;
+ case bp_none:
+ if (b->enable_state == bp_shlib_disabled)
+ fputs_unfiltered ("future-", stream);
+ fprintf_unfiltered (stream, "%s%sbreak", b->disposition == disp_del ? "t" : "",
+ (b->type == bp_hardware_breakpoint) ? "h" : "");
+ break;
+ default:
+ internal_error (__FILE__, __LINE__, "unhandled switch case");
+ break;
+ }
+
+ if (b->addr_string)
+ {
+ int len = strlen(b->addr_string) - 1;
+ if (b->addr_string[len] == ' ')
+ b->addr_string[len] = 0;
+ else
+ len = 0;
+ fprintf_unfiltered (stream, " %s", b->addr_string);
+ if (len)
+ b->addr_string[len] = ' ';
+ }
+ else if (b->source_file)
+ fprintf_unfiltered (stream, " %s:%d", b->source_file, b->line_number);
+ else
+ fprintf_unfiltered(stream, " %s",
+ local_hex_string_custom((unsigned long) b->address, "08l"));
+ break;
+ default:
+ internal_error (__FILE__, __LINE__, "unhandled switch case");
+ break;
+ }
+
+ if (b->thread != -1)
+ fprintf_unfiltered (stream, " thread %d", b->thread);
+
+ if (b->cond_string)
+ fprintf_unfiltered (stream, " if %s", b->cond_string);
+
+ fputc_unfiltered ('\n', stream);
+
+ if ((l = b->commands))
+ {
+ fputs_unfiltered ("commands\n", stream);
+ print_command_lines (uiout, l, 4);
+ fputs_unfiltered ("end\n", stream);
+ }
+
+ if (b->ignore_count)
+ fprintf_unfiltered (stream, "ignore $bpnum %d\n", b->ignore_count);
+
+ if (b->enable_state == bp_disabled)
+ fputs_unfiltered ("disable $bpnum\n", stream);
+}
+
+static void
+save_breakpoints_command (char *arg, int from_tty)
+{
+ struct cleanup *cleanups;
+ register struct breakpoint *b;
+ int found_a_breakpoint = 0;
+ int current_radix = -1;
+ int skip;
+ struct ui_file *stream = NULL;
+ struct ui_out *uiout = NULL;
+ time_t t;
+ char **argv;
+ char *pathname, buf[256];
+
+ dont_repeat ();
+
+ if (arg == NULL)
+ {
+ error ("Arguments missing: file name in which to save breakpoint commands");
+ }
+ else if ((argv = buildargv (arg)) == NULL)
+ {
+ nomem (0);
+ }
+ cleanups = make_cleanup_freeargv (argv);
+
+ pathname = tilde_expand (arg);
+ make_cleanup (xfree, pathname);
+
+ ALL_BREAKPOINTS (b)
+ {
+ /* Filter out non-user breakpoints. */
+ if (b->type != bp_breakpoint
+ && b->type != bp_catch_load
+ && b->type != bp_catch_unload
+ && b->type != bp_catch_fork
+ && b->type != bp_catch_vfork
+ && b->type != bp_catch_exec
+ && b->type != bp_catch_catch
+ && b->type != bp_catch_throw
+ && b->type != bp_hardware_breakpoint
+ && b->type != bp_watchpoint
+ && b->type != bp_read_watchpoint
+ && b->type != bp_access_watchpoint
+ && b->type != bp_hardware_watchpoint)
+ continue;
+
+ if (! found_a_breakpoint++)
+ {
+ stream = gdb_fopen (pathname, FOPEN_WT);
+ if (stream == NULL)
+ error ("Unable to open file '%s' for saving breakpoints (%s)",
+ arg, strerror (errno));
+ make_cleanup_ui_file_delete (stream);
+ uiout = cli_out_new (stream);
+ if (uiout == NULL)
+ error ("Unable to create cli_out from file for saving breakpoints");
+ make_cleanup_ui_out_delete (uiout);
+ if (time (&t) != -1)
+ {
+ char *l = setlocale (LC_ALL, NULL);
+ if (l)
+ {
+ char *orig_locale = strcpy (xmalloc (strlen (l) + 1), l);
+ setlocale (LC_ALL, "");
+ if (strftime (buf, sizeof (buf), "%a %b %e %H:%M:%S %Z %Y", localtime (&t)))
+ fprintf_unfiltered (stream, "# Saved breakpoints file created on %s\n\n", buf);
+ setlocale (LC_ALL, orig_locale);
+ }
+ }
+ fprintf_unfiltered (stream, "set $current_radix = $input_radix\n"
+ "set input-radix 012\n\n");
+ current_radix = 10;
+ }
+
+ skip = (b->commands || b->ignore_count || b->enable_state == bp_disabled);
+ if (skip)
+ fputc_unfiltered ('\n', stream);
+
+ if (b->input_radix != current_radix)
+ {
+ current_radix = b->input_radix;
+ fprintf_unfiltered (stream, "set input-radix 0%o\n", current_radix);
+ }
+
+ write_one_breakpoint (b, stream, uiout);
+
+ if (skip && b->next)
+ fputc_unfiltered ('\n', stream);
+ }
+
+ if (! found_a_breakpoint)
+ printf_filtered ("No breakpoints or watchpoints to save.\n");
+ else
+ {
+ fputs_unfiltered ("\n", stream);
+ if (current_radix != 10)
+ fputs_unfiltered ("set input-radix 012\n", stream);
+ fputs_unfiltered ("set input-radix $current_radix\n", stream);
+ if (from_tty)
+ printf_filtered ("Breakpoints saved to file '%s'.\n", arg);
+ }
+
+ do_cleanups (cleanups);
+}
\f
/* Use default_breakpoint_'s, or nothing if they aren't valid. */

@@ -7936,6 +8203,16 @@
hardware.)",
&setlist);
add_show_from_set (c, &showlist);
+
+ c = add_cmd ("save-breakpoints", class_breakpoint, save_breakpoints_command,
+ "Save current breakpoint definitions as a script.\n\
+Use the -command option or 'source' command in another debug\n\
+'session to restore them.", &cmdlist);
+ c->completer = filename_completer;
+
+ add_com_alias ("save_breakpoints", "save-breakpoints", class_breakpoint, 1);
+ add_com_alias ("save_bp", "save-breakpoints", class_breakpoint, 1);
+ add_com_alias ("savebp", "save-breakpoints", class_breakpoint, 1);

can_use_hw_watchpoints = 1;
}
Index: breakpoint.h
===================================================================
RCS file: /cvs/src/src/gdb/breakpoint.h,v
retrieving revision 1.10
diff -u -r1.10 breakpoint.h
--- breakpoint.h 2001/10/20 23:54:29 1.10
+++ breakpoint.h 2001/12/04 17:59:33
@@ -128,9 +128,17 @@
/* These are catchpoints to implement "catch catch" and "catch throw"
commands for C++ exception handling. */
bp_catch_catch,
- bp_catch_throw
+ bp_catch_throw,

+ /* These additional breakpoints are only used to record the original
+ breakpoint command for save-breakpoints. For example a future
+ break becomes a normal break when hit so if save-breakpoints is
+ done after the break is hit we wouldn't be able to tell what the
+ original command was to write it correcty in the saved file. */
+ bp_tbreakpoint,
+ bp_thbreakpoint

+
};

/* States of enablement of breakpoint. */
@@ -297,6 +305,9 @@
char *exec_pathname;

asection *section;
+
+ /* Original type of breakpoint used only for save-breakpoints. */
+ enum bptype original_type;
};
\f
/* The following stuff is an abstract data type "bpstat" ("breakpoint
@@ -515,6 +526,10 @@

/* Forward declarations for prototypes */
struct frame_info;
+
+extern void set_breakpoint_count (int);
+
+extern int get_breakpoint_count (void);

extern enum breakpoint_here breakpoint_here_p (CORE_ADDR);

Index: defs.h
===================================================================
RCS file: /cvs/src/src/gdb/defs.h,v
retrieving revision 1.66
diff -u -r1.66 defs.h
--- defs.h 2001/12/02 02:57:13 1.66
+++ defs.h 2001/12/04 17:59:35
@@ -544,6 +544,9 @@
struct ui_file;
extern struct cleanup *make_cleanup_ui_file_delete (struct ui_file *);

+struct ui_out;
+extern struct cleanup *make_cleanup_ui_out_delete (struct ui_out *);
+
extern struct cleanup *make_cleanup_close (int fd);

extern struct cleanup *make_cleanup_bfd_close (bfd *abfd);
Index: main.c
===================================================================
RCS file: /cvs/src/src/gdb/main.c,v
retrieving revision 1.14
diff -u -r1.14 main.c
--- main.c 2001/11/22 00:23:12 1.14
+++ main.c 2001/12/04 17:59:36
@@ -616,6 +616,17 @@
catch_command_errors (source_command, gdbinit, 0, RETURN_MASK_ALL);
}

+ /* These need to be set this late in the initialization to ensure that
+ they are defined for the current environment. They define the
+ radix variables needed by a save-breakpoints file to preserve the
+ radix across the breakpoints restoration assuming they are restored
+ using the -x (-command) command line options. */
+
+ set_internalvar (lookup_internalvar ("input_radix"),
+ value_from_longest (builtin_type_int, (LONGEST) input_radix));
+ set_internalvar (lookup_internalvar ("output_radix"),
+ value_from_longest (builtin_type_int, (LONGEST) output_radix));
+
for (i = 0; i < ncmd; i++)
{
#if 0
Index: tracepoint.c
===================================================================
RCS file: /cvs/src/src/gdb/tracepoint.c,v
retrieving revision 1.28
diff -u -r1.28 tracepoint.c
--- tracepoint.c 2001/11/06 23:38:15 1.28
+++ tracepoint.c 2001/12/04 17:59:39
@@ -2262,7 +2262,7 @@
struct action_line *line;
FILE *fp;
char *i1 = " ", *i2 = " ";
- char *indent, *actionline;
+ char *indent, *actionline, *pathname;
char tmp[40];

if (args == 0 || *args == 0)
@@ -2274,9 +2274,12 @@
return;
}

- if (!(fp = fopen (args, "w")))
- error ("Unable to open file '%s' for saving tracepoints");
-
+ pathname = tilde_expand (args);
+ if (!(fp = fopen (pathname, "w")))
+ error ("Unable to open file '%s' for saving tracepoints (%s)",
+ args, strerror (errno));
+ xfree (pathname);
+
ALL_TRACEPOINTS (tp)
{
if (tp->addr_string)
Index: ui-out.c
===================================================================
RCS file: /cvs/src/src/gdb/ui-out.c,v
retrieving revision 1.18
diff -u -r1.18 ui-out.c
--- ui-out.c 2001/07/06 03:53:11 1.18
+++ ui-out.c 2001/12/04 17:59:40
@@ -1117,6 +1117,14 @@
return uiout;
}

+void
+ui_out_delete (struct ui_out *uiout)
+{
+ if (uiout->data != NULL)
+ xfree (uiout->data);
+ xfree (uiout);
+}
+
/* standard gdb initialization hook */

void
Index: ui-out.h
===================================================================
RCS file: /cvs/src/src/gdb/ui-out.h,v
retrieving revision 1.15
diff -u -r1.15 ui-out.h
--- ui-out.h 2001/07/06 03:53:11 1.15
+++ ui-out.h 2001/12/04 17:59:40
@@ -272,4 +272,6 @@
struct ui_out_data *data,
int flags);

+extern void ui_out_delete (struct ui_out *uiout);
+
#endif /* UI_OUT_H */
Index: utils.c
===================================================================
RCS file: /cvs/src/src/gdb/utils.c,v
retrieving revision 1.51
diff -u -r1.51 utils.c
--- utils.c 2001/11/15 18:35:05 1.51
+++ utils.c 2001/12/04 17:59:42
@@ -254,6 +254,18 @@
return make_my_cleanup (&cleanup_chain, do_ui_file_delete, arg);
}

+static void
+do_ui_out_delete (void *arg)
+{
+ ui_out_delete (arg);
+}
+
+struct cleanup *
+make_cleanup_ui_out_delete (struct ui_out *arg)
+{
+ return make_my_cleanup (&cleanup_chain, do_ui_out_delete, arg);
+}
+
struct cleanup *
make_my_cleanup (struct cleanup **pmy_chain, make_cleanup_ftype *function,
void *arg)