#define _GNU_SOURCE #include #include #include #include #include #include #include #include "ansidecl.h" #include "mi_gdb.h" #define gdb_assert assert static ATTRIBUTE_NORETURN void fatal (const char *fmt, ...) { va_list ap; va_start (ap, fmt); vfprintf (stderr, fmt, ap); va_end (ap); raise (SIGABRT); exit (EXIT_FAILURE); } static char * xstrdup (const char *s) { char *retval; gdb_assert (s != NULL); retval = strdup (s); gdb_assert (retval != NULL); return retval; } static void xfree (void *p) { free (p); } static char * xstrprintf (const char *fmt, ...) { va_list ap; char *retval; int i; va_start (ap, fmt); i = vasprintf (&retval, fmt, ap); va_end (ap); gdb_assert (i > 0); gdb_assert (i == strlen (retval)); return retval; } static void console_cb (const char *str, void *data) { fputs (str, stdout); } static ATTRIBUTE_UNUSED void to_gdb_cb (const char *str, void *data) { printf ("to_gdb: %s\n", str); } static ATTRIBUTE_UNUSED void from_gdb_cb (const char *str, void *data) { printf ("from_gdb: %s\n", str); } static void h_disconnect (int rc, void *arg) { mi_h *h = arg; mi_disconnect (h); } static void history_save (int rc, void *arg) { const char *history_filename = arg; write_history (history_filename); } static void quit_command (mi_h *h, const char *cmd) { exit (EXIT_SUCCESS); } static void default_command (mi_h *h, const char *cmd) { int count = 1; mi_send (h, "-interpreter-exec console \"%s\"\n", cmd); while (count > 0) { mi_output *rec, *res; res = mi_get_response_blk (h); gdb_assert (res != NULL); for (rec = res; rec != NULL; rec = rec->next) switch (rec->tclass) { case MI_CL_DONE: count--; break; case MI_CL_RUNNING: /* We do not get MI_CL_DONE here, wait for MI_CL_STOPPED. */ break; case MI_CL_STOPPED: count--; break; case MI_CL_ERROR: gdb_assert (rec->c->type == t_const); puts (rec->c->v.cstr); break; default: fatal ("mi_get_rrecord == MI_CL_??? (%d)", rec->tclass); } mi_free_output (res); } } static const struct cmd { const char *name; void (*func) (mi_h *h, const char *cmd); } cmds[] = { { "q", quit_command }, { "qu", quit_command }, { "qui", quit_command }, { "quit", quit_command }, }; static void executecommand (mi_h *h, const char *cmd) { const char *start, *end, *cs; const struct cmd *cmdp; cs = cmd; while (isspace (*cs)) cs++; start = cs; while (isalnum (*cs)) cs++; end = cs; for (cmdp = cmds; cmdp < &cmds[LENGTH (cmds)]; cmdp++) if (strlen (cmdp->name) == end - start && strncmp (cmd, cmdp->name, end - start) == 0) return cmdp->func (h, cmd); return default_command (h, cmd); } static void gdb_done (mi_h *h, const char *command) { mi_output *res; mi_send (h, "%s\n", command); res = mi_get_response_blk (h); gdb_assert (res != NULL && res->next == NULL && res->tclass == MI_CL_DONE && res->c == NULL); mi_free_output (res); } static void gdb_set_string (mi_h *h, const char *setting, const char *value) { char *cmd = xstrprintf ("-gdb-set %s %s", setting, value); gdb_done (h, cmd); xfree (cmd); } static void gdb_set_bool (mi_h *h, const char *setting, bool value) { gdb_set_string (h, setting, value ? "on" : "off"); } static char * gdb_show_string (mi_h *h, const char *setting) { mi_output *res; char *retval; mi_send (h, "-gdb-show %s\n", setting); res = mi_get_response_blk (h); gdb_assert (res != NULL && res->next == NULL && res->tclass == MI_CL_DONE && res->c != NULL && res->c->next == NULL && res->c->type == t_const && strcmp (res->c->var, "value") == 0); retval = xstrdup (res->c->v.cstr); mi_free_output (res); return retval; } static int gdb_show_int (mi_h *h, const char *setting) { char *string = gdb_show_string (h, setting); long l; int retval; char *end; errno = 0; retval = l = strtol (string, &end, 10); gdb_assert (errno == 0 && (end == NULL || *end == '\0') && retval == l); xfree (string); return retval; } static bool gdb_show_bool (mi_h *h, const char *setting) { char *string = gdb_show_string (h, setting); bool retval; retval = strcmp (string, "on") == 0; gdb_assert (retval || strcmp (string, "off") == 0); xfree (string); return retval; } int main (int argc, char **argv) { mi_h *h; char *cmd; const char *ex[argc]; unsigned ex_count = 0, ex_used = 0; setbuf (stdout, NULL); while (*++argv != NULL) { if (strcmp (*argv, "-ex") == 0 && argv[1] != NULL) ex[ex_count++] = *++argv; else fatal ("Unknown parameter: %s", *argv); } mi_set_workaround (MI_PSYM_SEARCH, 0); h = mi_connect_local (); if (h == NULL) fatal ("Cannot connect to GDB"); on_exit (h_disconnect, h); mi_set_console_cb (h, console_cb, NULL); // mi_set_to_gdb_cb (h, to_gdb_cb, NULL); // mi_set_from_gdb_cb (h, from_gdb_cb, NULL); if (gdb_show_bool (h, "history save")) { int history_size = gdb_show_int (h, "history size"); char *history_filename = gdb_show_string (h, "history filename"); gdb_set_bool (h, "history save", false); stifle_history (history_size); read_history (history_filename); on_exit (history_save, history_filename); /* Do not free HISTORY_FILENAME. */ } for (;;) { if (ex_used < ex_count) { cmd = xstrdup (ex[ex_used++]); printf ("(gdb) %s\n", cmd); } else { cmd = readline ("(gdb) "); if (cmd == NULL) cmd = xstrdup ("quit"); else add_history (cmd); } executecommand (h, cmd); xfree (cmd); } }