This is the mail archive of the
gdb-patches@sourceware.org
mailing list for the GDB project.
Re: [PATCH] Implementation of plugin command to support custom GDB extensions at runtime
- From: Abhijit Halder <abhijit dot k dot halder at gmail dot com>
- To: "gdb-patches at sourceware dot org ml" <gdb-patches at sourceware dot org>
- Date: Mon, 14 May 2012 19:19:56 +0530
- Subject: Re: [PATCH] Implementation of plugin command to support custom GDB extensions at runtime
- References: <CAOhZP9yHx2JmfebF9MtPR_GgLHaO0PUwS1t3mhNG7UpOObgvFQ@mail.gmail.com> <CAOhZP9wBb-kZQTA=FSjq199-9jGUAj0zYcq42hRVKcTLJytjdw@mail.gmail.com>
On Mon, May 14, 2012 at 4:59 PM, Abhijit Halder
<abhijit.k.halder@gmail.com> wrote:
> ---------- Forwarded message ----------
> From: Abhijit Halder <abhijit.k.halder@gmail.com>
> Date: Mon, May 14, 2012 at 4:45 PM
> Subject: [PATCH] Implementation of plugin command to support custom
> GDB extensions at runtime
> To: "gdb-patches@sourceware.org ml" <gdb-patches@sourceware.org>
>
>
> Though in the last couple of discussions we agreed upon not trying to
> push this idea into mainstream GDB, I am still uploading this patch as
> I have found this useful for someone who tries to get some useful
> patch-work done already but not readily available in the releases. I
> beg pardon to people in this community. People in the community may
> safely ignore this patch at their own will.
>
> This patch implements a new gdb command namely, "plugin" to add and
> delete custom gdb extensions at runtime. Plugins are shared libraries
> supported by native OS.
>
> Following new commands are added:
>
> 1. plugin (Without any argument this will list down existing GDB
> plugins and with a plugin name as argument, will list down all new
> commands introduced by the plugin)
> 2. plugin add <plugin> (Will add a plugin specified by a relative or
> full path name of a shared library <plugin>. e.g. /usr/lib/gdb-ext.so)
> 3. plugin del <plugin> (Will delete an already added plugin specified
> by a relative or full path name of a shared library <plugin>)
>
> Plugin developer has to write his/her own plugin constructor and
> destructor routine of a given prototype to export a set of commands
> s/he wants to add in gdb command list. After adding a plugin the user
> will be able to use the new set of commands introduced by the plugin
> in gdb prompt.
There is a similar (same ?) proposal sometime back by Scott Moser.
http://sourceware.org/ml/gdb/2002-04/msg00371.html
Thanks,
Abhijit Halder
diff -rup src/gdb/Makefile.in dst/gdb/Makefile.in
--- src/gdb/Makefile.in 2012-05-14 16:36:05.849377050 +0530
+++ dst/gdb/Makefile.in 2012-05-14 16:36:30.629371941 +0530
@@ -718,6 +718,7 @@ SFILES = ada-exp.y ada-lang.c ada-typepr
objfiles.c osabi.c observer.c osdata.c \
opencl-lang.c \
p-exp.y p-lang.c p-typeprint.c p-valprint.c parse.c printcmd.c \
+ plugin.c \
proc-service.list progspace.c \
prologue-value.c psymtab.c \
regcache.c reggroups.c remote.c remote-fileio.c reverse.c \
@@ -875,6 +876,7 @@ COMMON_OBS = $(DEPFILES) $(CONFIG_OBS) $
event-loop.o event-top.o inf-loop.o completer.o \
gdbarch.o arch-utils.o gdbtypes.o osabi.o copying.o \
memattr.o mem-break.o target.o parse.o language.o buildsym.o \
+ plugin.o \
findcmd.o \
std-regs.o \
signals.o \
diff -rup src/gdb/plugin.c dst/gdb/plugin.c
--- src/gdb/plugin.c 2012-05-14 16:35:32.821376922 +0530
+++ dst/gdb/plugin.c 2012-05-14 16:21:24.005377051 +0530
@@ -0,0 +1,327 @@
+/* Everything about plugin command, for GDB.
+
+ Copyright (C) 2012 Free Software Foundation, Inc.
+
+ 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 3 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, see <http://www.gnu.org/licenses/>. */
+
+#include "defs.h"
+#include "gdb_string.h"
+#include "gdbcmd.h"
+#include "gdb-dlfcn.h"
+#include "readline/tilde.h"
+#include "gdb/plugin.h"
+
+static void (*null_func) (char *, int) = (void (*) (char *, int)) 0;
+static char *null_str = (char *) 0;
+
+void _initialize_plugin (void);
+
+static struct cmd_list_element *plugin_cmdlist;
+
+/* Structure to encapsulate all entities associated with plugin. */
+
+struct plugin
+{
+ /* Null terminated string containing filename with relative or absolute path
+ of a plugin which is essentially a dynamic library file. */
+ char *filename;
+
+ /* Opeque plugin handle. */
+ void *handle;
+
+ /* List of commands introduced by plugin. */
+ struct plugin_cmd *cmdlist;
+
+ /* Flag to indicate whether user defined plugin destructor routine needs to
+ be called. This flag is set when user defined constructor routine for the
+ plugin gets called. */
+ int init_done;
+
+ /* Points to previous plugin in this list. */
+ struct plugin *prev;
+
+ /* Points to next plugin in this list. */
+ struct plugin *next;
+};
+
+/* List of active plugins. */
+
+static struct plugin *pluginlist;
+
+/* Delete a plugin from query list. */
+
+static void
+delete_plugin_from_linkedlist (struct plugin *plugin)
+{
+ if (plugin->next)
+ plugin->next->prev = plugin->prev;
+ if (plugin->prev)
+ plugin->prev->next = plugin->next;
+ else
+ pluginlist = plugin->next;
+}
+
+/* Given a plugin filename FILENAME, search for a plugin in query list. */
+
+static struct plugin *
+do_lookup_plugin_into_linkedlist (char *filename)
+{
+ struct plugin *iter = pluginlist;
+
+ while (iter)
+ {
+ if (strcmp (iter->filename, filename) == 0)
+ break;
+ iter = iter->next;
+ }
+ return iter;
+}
+
+/* Insert a plugin in query list. */
+
+static void
+insert_plugin_into_linkedlist (struct plugin *plugin)
+{
+ if (pluginlist)
+ {
+ pluginlist->prev = plugin;
+ plugin->next = pluginlist;
+ }
+ pluginlist = plugin;
+}
+
+/* Destroy plugin referenced by ARG. */
+
+static void
+destroy_plugin (void *arg)
+{
+ struct plugin *plugin = (struct plugin *) arg;
+ plugin_func_type *fini_func;
+
+ if (!plugin)
+ error (_("Plugin not found."));
+
+ if (plugin->filename)
+ xfree (plugin->filename);
+
+ if (plugin->init_done)
+ {
+ struct plugin_cmd *iter = plugin->cmdlist;
+
+ while (iter)
+ {
+ /* Delete user supplied commands in plugin to GDB command list. */
+ add_cmd (iter->name, no_class, null_func, null_str, &cmdlist);
+ iter = iter->next;
+ }
+ }
+
+ fini_func = gdb_dlsym (plugin->handle, plugin_destructor_fn_sym);
+
+ if (!fini_func)
+ error (_("Plugin destructor routine %s not found."),
+ plugin_destructor_fn_sym);
+
+ if (fini_func (&plugin->cmdlist) < 0)
+ error (_("Failed to delete plugin."));
+
+ if (plugin->handle)
+ gdb_dlclose (plugin->handle);
+
+ xfree (plugin);
+}
+
+/* Create plugin referenced by ARG. */
+
+static void *
+create_plugin (char *arg)
+{
+ struct plugin *plugin;
+ plugin_func_type *init_func;
+ struct plugin_cmd *iter;
+ struct cleanup *old_chain;
+
+ plugin = XCNEW (struct plugin);
+ old_chain = make_cleanup (destroy_plugin, plugin);
+
+ plugin->filename = tilde_expand (arg);
+ plugin->handle = gdb_dlopen (plugin->filename);
+
+ init_func = gdb_dlsym (plugin->handle, plugin_constructor_fn_sym);
+
+ if (!init_func)
+ error (_("Plugin constructor routine %s not found."),
+ plugin_constructor_fn_sym);
+
+ if (init_func (&plugin->cmdlist) < 0)
+ error (_("Failed to add plugin."));
+
+ iter = plugin->cmdlist;
+
+ while (iter)
+ {
+ /* Add user supplied commands in plugin to GDB command list. */
+ add_cmd (iter->name, no_class, iter->func, iter->doc, &cmdlist);
+ iter = iter->next;
+ }
+
+ plugin->init_done = 1;
+
+ discard_cleanups (old_chain);
+
+ return plugin;
+}
+
+/* The 'plugin del' command delete a plugin. */
+
+static void
+plugin_del_command (char *arg, int from_tty)
+{
+ char *filename;
+ struct plugin *plugin;
+ struct cleanup *old_chain;
+
+ if (!is_dl_available ())
+ error (_("Plugin not supported."));
+
+ if (arg == NULL)
+ error (_("Plugin not specified."));
+
+ filename = tilde_expand (arg);
+ old_chain = make_cleanup (xfree, filename);
+
+ /* Shouldn't delete already deleted plugin. */
+ if (!(plugin = do_lookup_plugin_into_linkedlist (filename)))
+ error (_("Plugin not found."));
+
+ do_cleanups (old_chain);
+
+ delete_plugin_from_linkedlist (plugin);
+
+ destroy_plugin (plugin);
+}
+
+/* The 'plugin add' command add a plugin. */
+
+static void
+plugin_add_command (char *arg, int from_tty)
+{
+ char *filename;
+ struct plugin *plugin;
+ struct cleanup *old_chain;
+
+ if (!is_dl_available ())
+ error (_("Plugin not supported."));
+
+ if (arg == NULL)
+ error (_("Plugin not specified."));
+
+ filename = tilde_expand (arg);
+ old_chain = make_cleanup (xfree, filename);
+
+ /* Shouldn't add already added plugin. */
+ if (do_lookup_plugin_into_linkedlist (filename))
+ error (_("Plugin already added."));
+
+ do_cleanups (old_chain);
+
+ plugin = create_plugin (arg);
+ old_chain = make_cleanup (destroy_plugin, plugin);
+
+ insert_plugin_into_linkedlist (plugin);
+
+ discard_cleanups (old_chain);
+}
+
+/* Execute the plugin command with argument ARG and FROM_TTY. */
+
+static void
+plugin_command (char *arg, int from_tty)
+{
+ if (!is_dl_available ())
+ error (_("Plugin not supported."));
+
+ if (!arg)
+ {
+ struct plugin *iter = pluginlist;
+
+ if (!iter)
+ printf_filtered (_("No GDB-plugin is added.\n"));
+ else
+ {
+ printf_filtered (_("Following GDB-plugin(s) are added:\n\n"));
+
+ while (iter)
+ {
+ printf_filtered (_("\t%s\n"), iter->filename);
+ iter = iter->next;
+ }
+ }
+ }
+ else
+ {
+ char *filename;
+ struct plugin *plugin;
+ struct cleanup *old_chain;
+
+ filename = tilde_expand (arg);
+ old_chain = make_cleanup (xfree, filename);
+
+ if (!(plugin = do_lookup_plugin_into_linkedlist (filename)))
+ error (_("Plugin not found."));
+
+ do_cleanups (old_chain);
+
+ if (plugin)
+ {
+ struct plugin_cmd *iter = plugin->cmdlist;
+
+ while (iter)
+ {
+ printf_filtered (_("\t%s\n"), iter->name);
+ iter = iter->next;
+ }
+ }
+ }
+}
+
+/* Module initialization. */
+
+void
+_initialize_plugin (void)
+{
+ add_prefix_cmd ("plugin", no_class, plugin_command, _("\
+Provide an interface to plug-in a custom GDB-extension or plugin.\n\
+A custom GDB-extension is a dynamic library file with native OS supported\n\
+file-format.\n\n\
+Given a plugin as argument, list down command(s) introduced by the plugin.\n\
+With no subcommand, existing plugins are displayed."),
+ &plugin_cmdlist, "plugin ", 1, &cmdlist);
+
+ add_cmd ("add", no_class, plugin_add_command, _("\
+Add a GDB plugin.\n\
+Argument is a dynamic library filename with relative or absolute path."),
+ &plugin_cmdlist);
+
+ add_com_alias ("add-plugin", "plugin add", no_class, 0);
+
+ add_cmd ("del", no_class, plugin_del_command, _("\
+Delete a GDB plugin.\n\
+Argument is a dynamic library filename with relative or absolute path."),
+ &plugin_cmdlist);
+
+ add_com_alias ("del-plugin", "plugin del", no_class, 0);
+}
diff -rup src/gdb/testsuite/gdb.base/plugin.c dst/gdb/testsuite/gdb.base/plugin.c
--- src/gdb/testsuite/gdb.base/plugin.c 2012-05-14 16:37:14.037376909 +0530
+++ dst/gdb/testsuite/gdb.base/plugin.c 2012-05-14 16:21:55.985376773 +0530
@@ -0,0 +1,50 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 2012 Free Software Foundation, Inc.
+
+ 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 3 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, see <http://www.gnu.org/licenses/>. */
+
+#define SUCCESS 0
+#define FAILURE -1
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "plugin.h"
+
+static void
+func1_command (char *arg, int tty)
+{
+ printf ("Invoked func1.\n");
+}
+
+int
+gdb_plugin_constructor (struct plugin_cmd **cmds)
+{
+ (*cmds) = malloc (sizeof (struct plugin_cmd));
+ (*cmds)->name = strdup ("func1");
+ (*cmds)->func = func1_command;
+ (*cmds)->doc = strdup ("Help for func1.");
+ (*cmds)->next = NULL;
+ return SUCCESS;
+}
+
+int
+gdb_plugin_destructor (struct plugin_cmd **cmds)
+{
+ free ((*cmds)->name);
+ free ((*cmds)->doc);
+ free ((*cmds));
+}
diff -rup src/gdb/testsuite/gdb.base/plugin.exp dst/gdb/testsuite/gdb.base/plugin.exp
--- src/gdb/testsuite/gdb.base/plugin.exp 2012-05-14 16:37:05.837377490 +0530
+++ dst/gdb/testsuite/gdb.base/plugin.exp 2012-05-14 16:21:29.165376866 +0530
@@ -0,0 +1,51 @@
+# Copyright 2012 Free Software Foundation, Inc.
+
+# 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 3 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, see <http://www.gnu.org/licenses/>.
+
+#
+# test gdb plugin commands
+#
+
+set test "plugin"
+set libsrc ${test}.c
+set library ${objdir}/${subdir}/${test}.so
+
+file delete $library
+
+if {[get_compiler_info not-used]} {
+ warning "Could not get compiler info."
+ untested plugin.exp
+ return 1
+}
+
+if { [gdb_compile_shlib ${srcdir}/${subdir}/${libsrc} ${library} {-fPIC}] != "" } {
+ untested "Could not compile shared library."
+ return -1
+}
+
+gdb_exit
+gdb_start
+
+gdb_test "plugin" "No GDB-plugin is added."
+gdb_test "plugin add" "Plugin not specified."
+gdb_test "plugin del" "Plugin not specified."
+gdb_test_no_output "plugin add $library"
+gdb_test "plugin add $library" "Plugin already added."
+gdb_test "plugin" "Following GDB-plugin(s) are added:\n\n\t$library"
+gdb_test "plugin $library" "func1"
+gdb_test "func1" "Invoked func1."
+gdb_test "help func1" "Help for func1."
+gdb_test_no_output "plugin del $library"
+gdb_test "plugin del $library" "Plugin not found."
+gdb_test "plugin" "No GDB-plugin is added."
diff -rup src/gdb/testsuite/gdb.base/plugin.h dst/gdb/testsuite/gdb.base/plugin.h
--- src/gdb/testsuite/gdb.base/plugin.h 2012-05-14 16:37:16.593376908 +0530
+++ dst/gdb/testsuite/gdb.base/plugin.h 2012-05-14 16:21:56.217376878 +0530
@@ -0,0 +1,63 @@
+/* This file defines the interface between the simulator and gdb.
+
+ Copyright (C) 2012 Free Software Foundation, Inc.
+
+ 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 3 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, see <http://www.gnu.org/licenses/>. */
+
+#if !defined (PLUGIN_H)
+#define PLUGIN_H 1
+
+/* Plugin constructor routine define by the plugin developer. This must add all
+ newly introduced GDB-commands defined in the plugin to a list exposed by
+ GDB. Each element of this list will contain the name of the command, the
+ pointer to a function which executes on invocation of this command, a
+ document string which get displayed when help is asked for this command and
+ a pointer pointing to the next element in the list. */
+
+static const char *plugin_constructor_fn_sym = "gdb_plugin_constructor";
+
+/* Plugin destructor routine define by the plugin developer. This must cleanup
+ the command list created by the plugin constructor routine. */
+
+static const char *plugin_destructor_fn_sym = "gdb_plugin_destructor";
+
+/* Structure to encapsulate all entities associated with plugin command. */
+
+struct plugin_cmd
+{
+ /* Name of the command. */
+ char *name;
+
+ /* Pointer referencing to the function that is executed on invocation of this
+ command. This must be reset to NULL on deletion of the plugin. */
+ void (*func) (char *args, int from_tty);
+
+ /* Documentation of this command (or help topic).
+ First line is brief documentation; remaining lines form, with it, the full
+ documentation. First line should end with a period. Entire string should
+ also end with a period, not a newline. */
+ char *doc;
+
+ /* Points to next command in this list. */
+ struct plugin_cmd *next;
+};
+
+/* Prototype of plugin constructor and destructor routines.
+ It must return 0 on success and -1 on failure. */
+
+typedef int (plugin_func_type) (struct plugin_cmd *);
+
+#endif
diff -rup src/include/gdb/plugin.h dst/include/gdb/plugin.h
--- src/include/gdb/plugin.h 2012-05-14 16:35:23.349376973 +0530
+++ dst/include/gdb/plugin.h 2012-05-14 16:22:25.721376989 +0530
@@ -0,0 +1,63 @@
+/* This file defines the interface between the simulator and gdb.
+
+ Copyright (C) 2012 Free Software Foundation, Inc.
+
+ 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 3 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, see <http://www.gnu.org/licenses/>. */
+
+#if !defined (PLUGIN_H)
+#define PLUGIN_H 1
+
+/* Plugin constructor routine define by the plugin developer. This must add all
+ newly introduced GDB-commands defined in the plugin to a list exposed by
+ GDB. Each element of this list will contain the name of the command, the
+ pointer to a function which executes on invocation of this command, a
+ document string which get displayed when help is asked for this command and
+ a pointer pointing to the next element in the list. */
+
+static const char *plugin_constructor_fn_sym = "gdb_plugin_constructor";
+
+/* Plugin destructor routine define by the plugin developer. This must cleanup
+ the command list created by the plugin constructor routine. */
+
+static const char *plugin_destructor_fn_sym = "gdb_plugin_destructor";
+
+/* Structure to encapsulate all entities associated with plugin command. */
+
+struct plugin_cmd
+{
+ /* Name of the command. */
+ char *name;
+
+ /* Pointer referencing to the function that is executed on invocation of this
+ command. This must be reset to NULL on deletion of the plugin. */
+ void (*func) (char *args, int from_tty);
+
+ /* Documentation of this command (or help topic).
+ First line is brief documentation; remaining lines form, with it, the full
+ documentation. First line should end with a period. Entire string should
+ also end with a period, not a newline. */
+ char *doc;
+
+ /* Points to next command in this list. */
+ struct plugin_cmd *next;
+};
+
+/* Prototype of plugin constructor and destructor routines.
+ It must return 0 on success and -1 on failure. */
+
+typedef int (plugin_func_type) (struct plugin_cmd **);
+
+#endif