This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH] Implementation of plugin command to support custom GDB extensions at runtime


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.
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

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]