This is the mail archive of the gdb-patches@sources.redhat.com 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] plugin patch


Below is a patch to add plugin support to GDB.  It exports a fairly
simple programmable interface for people to extend the functionality of
GDB via runtime loaded shared libraries in ways that may not fit with
the direction of the main GDB tree (not cross-platform, not stable,
niche audience...).

Also attached are two example plugins.  They've been tested on linux and
AIX, and some time ago on freebsd.

Changelog entry:

2002-10-28  Scott Moser <ssmoser@us.ibm.com>

	* Makefile.in (plugin.o): Add build line for plugin.c
	* configure.in: Add --enable-plugin option
	* config/powerpc/aix.mh: add -Wl,-bexpall on aix build
	* plugin.c: new file, plugin implementation
	* plugin.h: new file, header file for plugin.c
	* gdbplugin.h: new file for plugin use

gdb plugin architecture overview
- gdb user commands
   - plugin load
      takes a filename and loads it as a gdb plugin. searches the
      plugin-path if needed
   - plugin description
      takes a filename or number (of a loaded plugin) and calls its
      plugin_description function. if not implemented returns "description N/A"
   - plugin name
      takes a filename or number (of a loaded plugin) and calls its
      plugin_name function. if not implemented returns "name N/A"
   - plugin commands
      takes a filename or number (of a loaded plugin) and calls its
      plugin_commands function.  if not implmemented returns "commands N/A"
   - plugin list
      takes no arguments.  lists the currently loaded plugins.
   - plugin search [ verbose ]
      trys to open each file in the current plugin-path as a gdb plugin.
      lists filename and plugin-name for each plugin found.
      if 'verbose' is passed in, also call's plugins' 'description'
   - set plugin-path
      takes a string and sets it to the ":" delimited search path

- functions implemented by gdb plugins
   - plugin_init (required)
      prototype: int plugin_init(const char * version, char * args, int tty)
         - version is character string describing GDB's version
         - args are user specified arguments after the filename in 'plugin load'
         - tty is gdb's tty
         - returns true or false on success/failure of load
         - It is expected that in this function, the plugin will perform
           actions like:
            - on systems where '-rdynamic'-type symbol resolution isn't
              available, use dlsym() to access the functions it requires within
              gdb (or, potentially, other plugins).  For example, the functions
              "add_cmd", "add_set_cmd", etc.
            - call "add_cmd" to add its plugin-specific commands to the gdb
              command set
            - call "add_set_cmd" to enable plugin-specific variables
      called at load of a plugin

   - plugin_name
      prototype: const char * plugin_name(void)
         - takes no arguments
         - returns a character string (short) "name" for the plugin.
      called by 'plugin "name|list|search"'

   - plugin_commands
      prototype: const char * plugin_commands(void)
         - takes no arguments
         - returns a string describing the list of commands registered (or that
           will be registered) by the plugin.
      called by 'plugin commands'

   - plugin_description
      prototype: const char * plugin_description(void)
         - takes no arguments
         - returns a character string "description" of the command.
      called by 'plugin "name|list|search verbose"'


- implementation notes
   - expect users to call 'plugin_commands', 'plugin_description', and
     'plugin_name' before plugin_init is called.
     For example, by 'plugin search'
   - plugin_init will only be called on load.
   - if you check the version passed in to decide if you'll run or not,
     consider offering a 'force' option to load even if you don't think you
     should

- issues:
   - colon ":" is the search path separator.  this may cause problems on
     windows with its C:\ like filenames.  At this point, the plugin design
     will not work on windows anyway, as windows requires explicitly exporting
     functions for them to be accessible outside of main (no -rdynamic)
   - uses printf_filtered
   - the version string format changes between releases and cvs

Scott Moser
Software Engineer; Linux Technology Center
IBM Corp., Austin, Tx
(512) 838-1533   T/L: 678-1533
ssmoser@us.ibm.com , internal zip: 9812


diff -uprN ../src-20021028.sparce/gdb/Makefile.in gdb/Makefile.in
--- ../src-20021028.sparce/gdb/Makefile.in	2002-10-28 13:53:34.000000000 -0600
+++ gdb/Makefile.in	2002-10-28 14:46:43.000000000 -0600
@@ -1968,6 +1968,9 @@ parse.o: parse.c $(defs_h) $(gdb_string_
 	$(frame_h) $(expression_h) $(value_h) $(command_h) $(language_h) \
 	$(parser_defs_h) $(gdbcmd_h) $(symfile_h) $(inferior_h) \
 	$(doublest_h) $(builtin_regs_h) $(gdb_assert_h)
+plugin.o: plugin.c $(defs_h) $(frame_h) $(inferior_h) $(target_h) $(gdbcmd_h) \
+	$(language_h) $(symfile_h) $(objfiles_h) $(completer_h) $(value_h) \
+	$(gdb_string_h) $(gdbcore_h) $(gdb_stat_h) $(xcoffsolib_h)
 ppc-bdm.o: ppc-bdm.c $(defs_h) $(gdbcore_h) $(gdb_string_h) $(frame_h) \
 	$(inferior_h) $(bfd_h) $(symfile_h) $(target_h) $(gdbcmd_h) \
 	$(objfiles_h) $(gdb_stabs_h) $(serial_h) $(ocd_h) $(ppc_tdep_h) \
diff -uprN ../src-20021028.sparce/gdb/config/powerpc/aix.mh gdb/config/powerpc/aix.mh
--- ../src-20021028.sparce/gdb/config/powerpc/aix.mh	2002-10-28 13:53:34.000000000 -0600
+++ gdb/config/powerpc/aix.mh	2002-10-28 13:26:31.000000000 -0600
@@ -6,6 +6,7 @@ NAT_FILE= nm-aix.h
 NATDEPFILES= fork-child.o infptrace.o inftarg.o corelow.o rs6000-nat.o \
 	     xcoffread.o xcoffsolib.o

+LOADLIBES= -Wl,-bexpall
 # When compiled with cc, for debugging, this argument should be passed.
 # We have no idea who our current compiler is though, so we skip it.
 # MH_CFLAGS = -bnodelcsect
diff -uprN ../src-20021028.sparce/gdb/configure.in gdb/configure.in
--- ../src-20021028.sparce/gdb/configure.in	2002-10-28 13:53:34.000000000 -0600
+++ gdb/configure.in	2002-10-28 14:44:44.000000000 -0600
@@ -665,6 +665,30 @@ case ${enable_gdbmi} in
     ;;
 esac

+dnl Enable gdb plugin interface
+AC_ARG_ENABLE(plugin,
+[  --enable-plugin           Enable GDB plugin interface],
+[
+  case "${enable_plugin}" in
+    yes ) if test x$gdb_cv_os_cygwin = xyes; then
+            AC_MSG_ERROR(plugin will not work on win32; disabled)
+          fi ;;
+    no) ;;
+    "")  enable_plugin=yes ;;
+    *)
+      AC_MSG_ERROR(Bad value for --enable-plugin: ${enableval})
+    ;;
+  esac
+],
+[enable_plugin=yes])
+case ${enable_plugin} in
+  "yes" )
+      CONFIG_OBS="${CONFIG_OBS} plugin.o"
+      CONFIG_SRCS="${CONFIG_SRCS} plugin.c"
+      CONFIG_INITS="${CONFIG_INITS} plugin.c"
+    ;;
+esac
+
 # Configure UI_OUT by default (before 5.2 it can be disabled)
 # It must be configured if gdbmi is configured

diff -uprN ../src-20021028.sparce/gdb/gdbplugin.h gdb/gdbplugin.h
--- ../src-20021028.sparce/gdb/gdbplugin.h	1969-12-31 18:00:00.000000000 -0600
+++ gdb/gdbplugin.h	2002-10-28 13:26:31.000000000 -0600
@@ -0,0 +1,11 @@
+/* this file exists as a convienence for gdb plugin programming
+ * it will hopefully include all the gdb header files that a plugin
+ * developer would want to use
+ * */
+#include "defs.h"
+#include "gdbcmd.h"
+#include "gdbcore.h"
+#include "completer.h"
+#include "value.h"
+#include "valprint.h"
+#include "language.h"
diff -uprN ../src-20021028.sparce/gdb/plugin.c gdb/plugin.c
--- ../src-20021028.sparce/gdb/plugin.c	1969-12-31 18:00:00.000000000 -0600
+++ gdb/plugin.c	2002-10-28 13:26:31.000000000 -0600
@@ -0,0 +1,468 @@
+#include "plugin.h"
+#include "defs.h"
+#include "gdbcmd.h"
+#include "gdbcore.h"
+#include "completer.h"
+#include "dlfcn.h"
+#include "dirent.h"
+#include "version.h"
+#include <sys/stat.h>
+#include <string.h>
+
+static struct plugin* newPlugin(void *, const char *);
+static struct plugin* findPlugin(const char *);
+static struct plugin* findPluginByNum(int);
+static const char * searchPluginPath(const char * );
+static void * openPlugin(const char * ,int, char *, int );
+static void * getPluginHandle(const char *);
+static const char * callPluginInfoFunction(void *,char *,const char *fallback);
+
+static char *plugin_path;
+
+static struct plugin * pluginList;
+
+void
+plugin_list(int tty)
+{
+   /* list the plugins currently loaded */
+   const struct plugin * curp=pluginList;
+   if(curp==NULL)
+   {
+      printf_filtered("no plugins loaded\n");
+      return;
+   }
+   while(curp!=NULL)
+   {
+      printf_filtered("%i %s %s\n", curp->num, curp->filename, plugin_name(curp->handle));
+      curp=curp->next;
+   }
+   return;
+}
+
+const char *
+plugin_commands(void * handle)
+{
+   return callPluginInfoFunction(handle,"plugin_commands","commands N/A");
+}
+
+const char *
+plugin_name(void * handle)
+{
+   return callPluginInfoFunction(handle,"plugin_name","name N/A");
+}
+
+const char *
+plugin_description(void * handle)
+{
+   return callPluginInfoFunction(handle,"plugin_description","description N/A");
+}
+
+const char *
+callPluginInfoFunction(void * handle,char * funcname,const char *fallback)
+{
+   const char * (*func)(void);
+   const char * ret=NULL;
+   if(handle && funcname)
+   {
+      func=dlsym(handle,funcname);
+      if(func)
+      {
+         ret=func();
+      }
+   }
+   return (ret) ? ret : fallback;
+}
+
+void
+plugin_search(const char * str, int tty, int verbose)
+{
+   /* searches the current path for files with plugin_init() defined. */
+   if(plugin_path!=NULL)
+   {
+      const char *tok;
+      char *spath=(char*)malloc((strlen(plugin_path)+1)*sizeof(char));
+      if(spath==NULL)
+         return;
+      strcpy(spath,plugin_path);
+      tok=strtok(spath,":");
+      while(tok!=NULL)
+      {
+         struct dirent *direntry;
+         DIR * dir=opendir(tok);
+         if (dir!=NULL)
+         {
+            while((direntry=readdir(dir))!=NULL)
+            {
+#if (defined(DT_REG) && defined(DT_LNK) && defined(DT_UNKNOWN))
+               if (direntry->d_type==DT_REG || direntry->d_type==DT_LNK || direntry->d_type==DT_UNKNOWN)
+               {
+#endif
+                  void *handle=NULL;
+                  char *filename=(char*)malloc((strlen(tok)+strlen(direntry->d_name)+2)*sizeof(char));
+                  if(filename==NULL)
+                  {
+                     free(spath);
+                     return;
+                  }
+                  sprintf(filename,"%s/%s",tok,direntry->d_name);
+                  handle=openPlugin(filename,0,(char *)NULL, tty);
+                  if(handle!=NULL)
+                  {
+                     printf_filtered("%s : %s\n",filename,plugin_name(handle));
+                     if(verbose)
+                     {
+                        printf_filtered("\t%s\n",plugin_description(handle));
+                     }
+                     dlclose(handle);
+                  }
+                  free(filename);
+#if (defined(DT_REG) && defined(DT_LNK) && defined(DT_UNKNOWN))
+               }
+#endif
+            }
+         }
+         tok=strtok(NULL,":");
+      }
+   }
+   else
+   {
+      printf_filtered("plugin-path not set\n");
+   }
+   return;
+}
+
+void
+plugin_load(const char * filename, char * args, int tty)
+{
+   /* load a plugin named filename, (searching plugin-path if set )
+    * with args given
+    * */
+   void *handle=NULL;
+
+   handle=openPlugin(filename,1,args, tty);
+   if(handle==NULL)
+   {
+      const char *tempfile;
+      tempfile=searchPluginPath(filename);
+      if(tempfile==NULL)
+      {
+         printf_filtered("plugin load: couldn't find %s\n",filename);
+         return;
+      }
+      handle=openPlugin(tempfile,1,args,tty);
+      if(handle==NULL)
+      {
+         printf_filtered("plugin load: couldn't load %s\n",tempfile);
+         return;
+      }
+   }
+
+   newPlugin(handle,filename);
+   return;
+}
+
+void *
+getPluginHandle(const char * filename)
+{
+   struct plugin * cur;
+   void * handle;
+   cur=findPlugin(filename);
+   if(cur!=NULL)
+   {
+      handle=cur->handle;
+   }
+   else
+   {
+      const char * fullpath;
+      fullpath=searchPluginPath(filename);
+      handle=openPlugin(fullpath,0,(char *)NULL, 0);
+   }
+   return(handle);
+}
+
+struct plugin *
+newPlugin(void * handle, const char * filename)
+{
+   /* add this plugin at the beginning of the list */
+   struct plugin* newP;
+   newP=(struct plugin*)malloc(sizeof(struct plugin)+strlen(filename)*sizeof(char)+1);
+   if(newP==NULL)
+   {
+      printf_filtered("Failed to allocate space for plugin %s\n",filename);
+      return NULL;
+   }
+   newP->handle=handle;
+   newP->next=pluginList;
+   newP->filename=(char*)(newP+1);
+   strcpy(newP->filename,filename);
+   if(newP->next!=NULL)
+   {
+      newP->num=(int)((newP->next)->num)+1;
+   }
+   else
+   {
+      newP->num=0;
+   }
+   pluginList=newP;
+   return newP;
+}
+
+struct plugin *
+findPluginByNum(int num)
+{
+   struct plugin * cur;
+
+   for ( cur = pluginList; cur != NULL; cur = cur->next )
+   {
+      if (cur->num == num) break;
+   }
+   return cur;
+}
+
+struct plugin *
+findPlugin(const char * filename)
+{
+   /* find a plugin by filename from the plugin_list */
+   struct plugin * cur;
+
+   for ( cur = pluginList; cur != NULL; cur = cur->next )
+   {
+      if (strcmp(filename,cur->filename) == 0) break;
+   }
+   return cur;
+}
+
+void *
+openPlugin(const char * filename,int init, char * args, int tty)
+{
+   /* opens a named filename.  if init is true, will call its init function
+    * and pass in args and tty
+    * */
+   void * handle;
+   int (*initfunc)(const char *, char *, int);
+
+   if(filename==NULL)
+      return(NULL);
+
+   handle=dlopen(filename,RTLD_LAZY);
+   if(handle==NULL)
+      return(NULL);
+
+   if(!init)
+      return(handle);
+
+   initfunc=dlsym(handle,"plugin_init");
+
+   if(initfunc==NULL)
+   {
+      printf_filtered("couldn't find plugin_init in plugin %s\n",filename);
+      dlclose(handle);
+      return(NULL);
+   }
+
+   if(initfunc(version,args,tty))
+   {
+      printf_filtered("successfully loaded plugin %s\n",filename);
+      return(handle);
+   }
+   else
+   {
+      printf_filtered("plugin_init failed %s\n",filename);
+      /*
+       * don't do a dlclose here to avoid possible segfault if
+       * failed load didn't clean itself up correctly
+       */
+   }
+   return(NULL);
+}
+
+
+const char *
+searchPluginPath(const char * filename)
+{
+   /* returns a full path to a file if it exists in search path */
+   if(filename!=NULL)
+   {
+      int found;
+      struct stat buf;
+      char *tok;
+      char *spath;
+      if(!stat(filename,&buf))
+      {
+         return(filename);
+      }
+      if(plugin_path!=NULL)
+      {
+         spath=(char*)malloc((strlen(plugin_path)+1)*sizeof(char));
+         if(spath==NULL)
+         {
+            return(NULL);
+         }
+         strcpy(spath,plugin_path);
+         tok=strtok(spath,":");
+         while(tok!=NULL)
+         {
+            char *fp=(char*)malloc(((strlen(filename)+strlen(tok)+2))*sizeof(char));
+            if(fp==NULL)
+            {
+               free(spath);
+               return(NULL);
+            }
+            sprintf(fp,"%s/%s",tok,filename);
+            if(!stat(fp,&buf))
+            {
+               free(spath);
+               return(fp);
+            }
+            free(fp);
+            tok=strtok(NULL, ":");
+         }
+         free(spath);
+      }
+   }
+   return(NULL);
+}
+
+void
+plugin_command (char *arg, int from_tty)
+{
+   /* top level 'plugin' command handler */
+   char * cmdtype;
+   char * filename;
+   char * cmdargs = NULL;
+   int start;
+   int arglen;
+   int plugNum;
+   char *endptr;
+   struct plugin *p;
+   void * dlhandle;
+   const char * retstr;
+
+   if(arg==NULL)
+   {
+      printf_filtered("plugin: need second level command (see 'help plugin')\n");
+      return;
+   }
+
+   if (!strcmp(arg,"search verbose"))
+   {
+      plugin_search("",from_tty,1);
+      return;
+   }
+   else if (!strcmp(arg,"search"))
+   {
+      plugin_search("",from_tty,0);
+      return;
+   }
+   else if (!strcmp(arg,"list"))
+   {
+      plugin_list(from_tty);
+      return;
+   }
+
+   // all remaining plugin commands require args
+   cmdtype=arg;
+   if ((endptr = strchr(arg,' ')) == NULL)
+   {
+         printf_filtered("plugin: Error no argument to plugin command \"%s\"\n",cmdtype);
+         return ;
+   }
+   *endptr = '\0';
+   filename = endptr + 1;
+   while(filename[0]!='\0' && filename[0]==' ') filename++;
+
+   if(filename[0]=='\0')
+      return ;
+
+   if(filename[0]=='"' && ((endptr=strchr(filename+1,'"'))!=NULL))
+   {
+         filename++;
+         *endptr='\0';
+         cmdargs=endptr+1;
+         if(endptr[1]=='\0') cmdargs=NULL;
+   }
+   else
+   {
+      if((endptr=strchr(filename,' '))!=NULL)
+      {
+         *endptr = '\0';
+         cmdargs=endptr+1;
+      }
+   }
+
+   if(!strcmp(cmdtype,"load"))
+   {
+      plugin_load(filename,cmdargs,from_tty);
+      return;
+   }
+
+   plugNum=(int)strtol(filename,&endptr,10);
+
+   if(endptr!=NULL && (*endptr)=='\0')
+   {
+      p=findPluginByNum(plugNum);
+      if(p==NULL)
+      {
+         printf_filtered("no plugin number %i loaded (\"plugin list\" for list)\n",plugNum);
+         return;
+      }
+      dlhandle=p->handle;
+   }
+   else
+   {
+      dlhandle=getPluginHandle(filename);
+   }
+
+   if (!strncmp(cmdtype,"commands",4))
+   {
+      retstr=plugin_commands(dlhandle);
+   }
+   else if (!strcmp(cmdtype,"name"))
+   {
+      retstr=plugin_name(dlhandle);
+   }
+   else if (!strncmp(cmdtype,"desc",4))
+   {
+      retstr=plugin_description(dlhandle);
+   }
+   else
+   {
+      printf_filtered("%s: not a plugin command\n",cmdtype);
+      return;
+   }
+
+   if(retstr!=NULL)
+   {
+      printf_filtered("%s\n",retstr);
+   }
+   else
+   {
+      printf_filtered("command \"%s\" not implemented in plugin %s\n",cmdtype,filename);
+   }
+}
+
+void
+_initialize_plugin(void)
+{
+   struct cmd_list_element *c;
+
+   c = add_cmd ("plugin", class_files, plugin_command,
+      "manage GDB plugins\n\
+a GDB plugin can add functionality to GDB and make use of GDB functionality\n\
+without modifiying the core to GDB.\n\
+load [args]      - loads and initializes a plugin, [ with args ]\n\
+desc[ription]    - print description for file/num\n\
+name             - print name for file/num\n\
+commands         - list commands given by file/num\n\
+search [verbose] - search current plugin-path for gdb plugins [ show desc ]\n\
+list             - list currently loaded plugins\n\
+" ,&cmdlist);
+   set_cmd_completer (c, filename_completer);
+
+   add_show_from_set(add_set_cmd("plugin-path",class_support,
+            var_string_noescape, (char*) &plugin_path,
+            "Set plugin search path\n", &setlist), &showlist);
+
+   pluginList=NULL;
+}
+
diff -uprN ../src-20021028.sparce/gdb/plugin.h gdb/plugin.h
--- ../src-20021028.sparce/gdb/plugin.h	1969-12-31 18:00:00.000000000 -0600
+++ gdb/plugin.h	2002-10-28 13:26:31.000000000 -0600
@@ -0,0 +1,21 @@
+#if !defined (PLUGIN_H)
+#define PLUGIN_H 1
+
+extern void plugin_load(const char *, char *, int);
+extern void plugin_command (char *, int);
+extern void plugin_list(int);
+extern void plugin_search(const char * , int ,int );
+extern const char * plugin_name(void *);
+extern const char * plugin_description(void*);
+extern const char * plugin_commands(void*);
+
+extern char *plugin_path;
+
+struct plugin {
+   void * handle;
+   int num;
+   struct plugin * next;
+   char * filename;
+};
+
+#endif /* !defined (PLUGIN_H) */

Attachment: exPlugin.tar.gz
Description: Binary data


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