diff options
author | Doug Evans <dje@google.com> | 2010-04-23 16:20:13 +0000 |
---|---|---|
committer | Doug Evans <dje@google.com> | 2010-04-23 16:20:13 +0000 |
commit | 8a1ea21f7e299173e51bd45e8981ea107ebf0952 (patch) | |
tree | 1cc7c7fa8492ea30fb26ab0167739df59bb2d743 /gdb/python/py-auto-load.c | |
parent | c8551de35c952a01283328a3f14dbb5195769815 (diff) | |
download | gdb-8a1ea21f7e299173e51bd45e8981ea107ebf0952.zip gdb-8a1ea21f7e299173e51bd45e8981ea107ebf0952.tar.gz gdb-8a1ea21f7e299173e51bd45e8981ea107ebf0952.tar.bz2 |
Add support for auto-loading scripts from .debug_gdb_scripts section.
* NEWS: Add entry for .debug_gdb_scripts.
* Makefile.in SUBDIR_PYTHON_OBS): Add py-auto-load.o.
(SUBDIR_PYTHON_SRCS): Add py-auto-load.c.
(py-auto-load.o): New rule.
* cli/cli-cmds.c (find_and_open_script): Make externally visible.
* cli/cli-cmds.h (find_and_open_script): Update prototype.
* python/py-auto-load.c: New file.
* python/python-internal.h: #include <stdio.h>.
(set_python_list, show_python_list): Declare.
(gdbpy_initialize_auto_load): Declare.
(source_python_script_for_objfile): Declare.
* python/python.c: Remove #include of observer.h.
(gdbpy_auto_load): Moved to py-auto-load.c.
(GDBPY_AUTO_FILENAME): Ditto.
(gdbpy_new_objfile): Delete.
(source_python_script_for_objfile): New function.
(set_python_list, show_python_list): Make externally visible.
(_initialize_python): Move "auto-load" command to py-auto-load.c
and observer_attach_new_objfile to py-auto-load.c.
doc/
* gdb.texinfo (Python): Move Auto-loading section here ...
(Python API): from here.
(Auto-loading): Add docs for .debug_gdb_scripts auto-loaded scripts.
(Maintenance Commands): Add docs for "maint print section-scripts".
testsuite/
* gdb.python/py-section-script.c: New file.
* gdb.python/py-section-script.exp: New file.
* gdb.python/py-section-script.py: New file.
Diffstat (limited to 'gdb/python/py-auto-load.c')
-rw-r--r-- | gdb/python/py-auto-load.c | 459 |
1 files changed, 459 insertions, 0 deletions
diff --git a/gdb/python/py-auto-load.c b/gdb/python/py-auto-load.c new file mode 100644 index 0000000..e3b406c --- /dev/null +++ b/gdb/python/py-auto-load.c @@ -0,0 +1,459 @@ +/* GDB routines for supporting auto-loaded scripts. + + Copyright (C) 2010 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 "gdb_regex.h" +#include "top.h" +#include "exceptions.h" +#include "command.h" +#include "gdbcmd.h" +#include "observer.h" +#include "progspace.h" +#include "objfiles.h" +#include "python.h" +#include "python-internal.h" +#include "cli/cli-cmds.h" + +/* NOTE: It's trivial to also support auto-loading normal gdb scripts. + There has yet to be a need so it's not implemented. */ + +/* The suffix of per-objfile scripts to auto-load. + E.g. When the program loads libfoo.so, look for libfoo-gdb.py. */ +#define GDBPY_AUTO_FILE_NAME "-gdb.py" + +/* The section to look for scripts (in file formats that support sections). + Each entry in this section is a byte of value 1, and then the nul-terminated + name of the script. The script name may include a directory. + The leading byte is to allow upward compatible extensions. */ +#define GDBPY_AUTO_SECTION_NAME ".debug_gdb_scripts" + +/* For scripts specified in .debug_gdb_scripts, multiple objfiles may load + the same script. There's no point in loading the script multiple times, + and there can be a lot of objfiles and scripts, so we keep track of scripts + loaded this way. */ + +struct auto_load_pspace_info +{ + /* For each program space we keep track of loaded scripts. */ + struct htab *loaded_scripts; +}; + +/* Objects of this type are stored in the loaded script hash table. */ + +struct loaded_script_entry +{ + /* Name as provided by the objfile. */ + const char *name; + /* Full path name or NULL if script wasn't found (or was otherwise + inaccessible). */ + const char *full_path; +}; + +/* This is true if we should auto-load python code when an objfile is opened, + false otherwise. */ +static int gdbpy_auto_load = 1; + +/* Per-program-space data key. */ +static const struct program_space_data *auto_load_pspace_data; + +static void +auto_load_pspace_data_cleanup (struct program_space *pspace, void *arg) +{ + struct auto_load_pspace_info *info; + + info = program_space_data (pspace, auto_load_pspace_data); + if (info != NULL) + { + if (info->loaded_scripts) + htab_delete (info->loaded_scripts); + xfree (info); + } +} + +/* Get the current autoload data. If none is found yet, add it now. This + function always returns a valid object. */ + +static struct auto_load_pspace_info * +get_auto_load_pspace_data (struct program_space *pspace) +{ + struct auto_load_pspace_info *info; + + info = program_space_data (pspace, auto_load_pspace_data); + if (info == NULL) + { + info = XZALLOC (struct auto_load_pspace_info); + set_program_space_data (pspace, auto_load_pspace_data, info); + } + + return info; +} + +/* Hash function for the loaded script hash. */ + +static hashval_t +hash_loaded_script_entry (const void *data) +{ + const struct loaded_script_entry *e = data; + return htab_hash_string (e->name); +} + +/* Equality function for the loaded script hash. */ + +static int +eq_loaded_script_entry (const void *a, const void *b) +{ + const struct loaded_script_entry *ea = a; + const struct loaded_script_entry *eb = b; + return strcmp (ea->name, eb->name) == 0; +} + +/* Create the hash table used for loaded scripts. + Each entry is hashed by the full path name. */ + +static void +create_loaded_scripts_hash (struct auto_load_pspace_info *pspace_info) +{ + /* Choose 31 as the starting size of the hash table, somewhat arbitrarily. + Space for each entry is obtained with one malloc so we can free them + easily. */ + + pspace_info->loaded_scripts = htab_create (31, + hash_loaded_script_entry, + eq_loaded_script_entry, + xfree); +} + +/* Load scripts specified in OBJFILE. + START,END delimit a buffer containing a list of nul-terminated + file names. + SOURCE_NAME is used in error messages. + + Scripts are found per normal "source -s" command processing. + First the script is looked for in $cwd. If not found there the + source search path is used. + + The section contains a list of path names of files containing + python code to load. Each path is null-terminated. */ + +static void +source_section_scripts (struct objfile *objfile, const char *source_name, + const char *start, const char *end) +{ + const char *p; + struct auto_load_pspace_info *pspace_info; + struct loaded_script_entry **slot, entry; + + pspace_info = get_auto_load_pspace_data (current_program_space); + if (pspace_info->loaded_scripts == NULL) + create_loaded_scripts_hash (pspace_info); + + for (p = start; p < end; ++p) + { + const char *file; + FILE *stream; + char *full_path; + int opened, in_hash_table; + + if (*p != 1) + { + warning (_("Invalid entry in %s section"), GDBPY_AUTO_SECTION_NAME); + /* We could try various heuristics to find the next valid entry, + but it's safer to just punt. */ + break; + } + file = ++p; + + while (p < end && *p != '\0') + ++p; + if (p == end) + { + char *buf = alloca (p - file + 1); + memcpy (buf, file, p - file); + buf[p - file] = '\0'; + warning (_("Non-null-terminated path in %s: %s"), + source_name, buf); + /* Don't load it. */ + break; + } + if (p == file) + { + warning (_("Empty path in %s"), source_name); + continue; + } + + opened = find_and_open_script (file, 1 /*search_path*/, + &stream, &full_path); + + /* If the file is not found, we still record the file in the hash table, + we only want to print an error message once. + IWBN if complaints.c were more general-purpose. */ + + entry.name = file; + if (opened) + entry.full_path = full_path; + else + entry.full_path = NULL; + slot = ((struct loaded_script_entry **) + htab_find_slot (pspace_info->loaded_scripts, + &entry, INSERT)); + in_hash_table = *slot != NULL; + + /* If this file is not in the hash table, add it. */ + if (! in_hash_table) + { + char *p; + *slot = xmalloc (sizeof (**slot) + + strlen (file) + 1 + + (opened ? (strlen (full_path) + 1) : 0)); + p = ((char*) *slot) + sizeof (**slot); + strcpy (p, file); + (*slot)->name = p; + if (opened) + { + p += strlen (p) + 1; + strcpy (p, full_path); + (*slot)->full_path = p; + } + else + (*slot)->full_path = NULL; + } + + if (opened) + free (full_path); + + if (! opened) + { + /* We don't throw an error, the program is still debuggable. + Check in_hash_table to only print the warning once. */ + if (! in_hash_table) + warning (_("%s (referenced in %s): %s\n"), + file, GDBPY_AUTO_SECTION_NAME, safe_strerror (errno)); + continue; + } + + /* If this file is not currently loaded, load it. */ + if (! in_hash_table) + source_python_script_for_objfile (objfile, stream, file); + } +} + +/* Load scripts specified in section SECTION_NAME of OBJFILE. */ + +static void +auto_load_section_scripts (struct objfile *objfile, const char *section_name) +{ + bfd *abfd = objfile->obfd; + asection *scripts_sect; + bfd_size_type size; + char *p; + struct cleanup *cleanups; + + scripts_sect = bfd_get_section_by_name (abfd, section_name); + if (scripts_sect == NULL) + return; + + size = bfd_get_section_size (scripts_sect); + p = xmalloc (size); + + cleanups = make_cleanup (xfree, p); + + if (bfd_get_section_contents (abfd, scripts_sect, p, (file_ptr) 0, size)) + source_section_scripts (objfile, section_name, p, p + size); + else + warning (_("Couldn't read %s section of %s"), + section_name, bfd_get_filename (abfd)); + + do_cleanups (cleanups); +} + +/* Clear the table of loaded section scripts. */ + +static void +clear_section_scripts (void) +{ + struct program_space *pspace = current_program_space; + struct auto_load_pspace_info *info; + + info = program_space_data (pspace, auto_load_pspace_data); + if (info != NULL && info->loaded_scripts != NULL) + { + htab_delete (info->loaded_scripts); + info->loaded_scripts = NULL; + } +} + +/* Look for the auto-load script associated with OBJFILE and load it. */ + +static void +auto_load_objfile_script (struct objfile *objfile, const char *suffix) +{ + char *realname; + char *filename, *debugfile; + int len; + FILE *input; + struct cleanup *cleanups; + + realname = gdb_realpath (objfile->name); + len = strlen (realname); + filename = xmalloc (len + strlen (suffix) + 1); + memcpy (filename, realname, len); + strcpy (filename + len, suffix); + + cleanups = make_cleanup (xfree, filename); + make_cleanup (xfree, realname); + + input = fopen (filename, "r"); + debugfile = filename; + + if (!input && debug_file_directory) + { + /* Also try the same file in the separate debug info directory. */ + debugfile = xmalloc (strlen (filename) + + strlen (debug_file_directory) + 1); + strcpy (debugfile, debug_file_directory); + /* FILENAME is absolute, so we don't need a "/" here. */ + strcat (debugfile, filename); + + make_cleanup (xfree, debugfile); + input = fopen (debugfile, "r"); + } + + if (!input && gdb_datadir) + { + /* Also try the same file in a subdirectory of gdb's data + directory. */ + debugfile = xmalloc (strlen (gdb_datadir) + strlen (filename) + + strlen ("/auto-load") + 1); + strcpy (debugfile, gdb_datadir); + strcat (debugfile, "/auto-load"); + /* FILENAME is absolute, so we don't need a "/" here. */ + strcat (debugfile, filename); + + make_cleanup (xfree, debugfile); + input = fopen (debugfile, "r"); + } + + if (input) + { + source_python_script_for_objfile (objfile, input, debugfile); + fclose (input); + } + + do_cleanups (cleanups); +} + +/* This is a new_objfile observer callback to auto-load scripts. + + Two flavors of auto-loaded scripts are supported. + 1) based on the path to the objfile + 2) from .debug_gdb_scripts section */ + +static void +auto_load_new_objfile (struct objfile *objfile) +{ + if (!objfile) + { + /* OBJFILE is NULL when loading a new "main" symbol-file. */ + clear_section_scripts (); + return; + } + if (!objfile->name) + return; + + if (gdbpy_auto_load) + { + auto_load_objfile_script (objfile, GDBPY_AUTO_FILE_NAME); + auto_load_section_scripts (objfile, GDBPY_AUTO_SECTION_NAME); + } +} + +/* Traversal function for htab_traverse. + Print the entry if specified in the regex. */ + +static int +maybe_print_section_script (void **slot, void *info) +{ + struct loaded_script_entry *entry = *slot; + + if (re_exec (entry->name)) + { + printf_filtered (_("Script name: %s\n"), entry->name); + printf_filtered (_(" Full name: %s\n"), + entry->full_path ? entry->full_path : _("unknown")); + } + + return 1; +} + +/* "maint print section-scripts" command. */ + +static void +maintenance_print_section_scripts (char *pattern, int from_tty) +{ + struct auto_load_pspace_info *pspace_info; + + dont_repeat (); + + if (pattern && *pattern) + { + char *re_err = re_comp (pattern); + + if (re_err) + error (_("Invalid regexp: %s"), re_err); + + printf_filtered (_("Objfile scripts matching %s:\n"), pattern); + } + else + { + re_comp (""); + printf_filtered (_("Objfile scripts:\n")); + } + + pspace_info = get_auto_load_pspace_data (current_program_space); + if (pspace_info == NULL || pspace_info->loaded_scripts == NULL) + return; + + immediate_quit++; + htab_traverse_noresize (pspace_info->loaded_scripts, + maybe_print_section_script, NULL); + immediate_quit--; +} + +void +gdbpy_initialize_auto_load (void) +{ + auto_load_pspace_data + = register_program_space_data_with_cleanup (auto_load_pspace_data_cleanup); + + observer_attach_new_objfile (auto_load_new_objfile); + + add_setshow_boolean_cmd ("auto-load", class_maintenance, + &gdbpy_auto_load, _("\ +Enable or disable auto-loading of Python code when an object is opened."), _("\ +Show whether Python code will be auto-loaded when an object is opened."), _("\ +Enables or disables auto-loading of Python code when an object is opened."), + NULL, NULL, + &set_python_list, + &show_python_list); + + add_cmd ("section-scripts", class_maintenance, maintenance_print_section_scripts, + _("Print dump of auto-loaded section scripts matching REGEXP."), + &maintenanceprintlist); +} |