public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
* [PATCH v2 02/13] script language API for GDB: extension.[ch]
@ 2013-12-11  5:11 Doug Evans
  2013-12-23 21:57 ` Tom Tromey
  0 siblings, 1 reply; 13+ messages in thread
From: Doug Evans @ 2013-12-11  5:11 UTC (permalink / raw)
  To: gdb-patches

This patch adds extension.h, extension.c and extension-priv.h.

extension.h provides the public API.
The enums that were in python.h have been moved here and any py_,PY_ part
of the name has been replaced to be non-python-specific.

extension-priv.h provides the "private" API.
This is what Python exports to GDB.

extension.c defines extension_language_gdb for GDB's own extension/scripting
language, and provides the functions GDB calls to call into an extension
language.

Changes from v1:
- I renamed the files from scripting.[ch] per suggestion
- more comments
- more consistent enum prefixes
  [I'm still leaving off prefixes of values carried over as is from Python.
   A later pass can add them.]

2013-12-10  Doug Evans  <xdje42@gmail.com>

	* Makefile.in (SFILES): Add extension.c.
	(HFILES_NO_SRCDIR): Add extension.h, extension-priv.h
	(COMMON_OBS): Add extension.o.
	* extension.h: New file.
	* extension-priv.h: New file.
	* extension.c: New file.

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index be30dfd..838f27a 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -733,7 +733,7 @@ SFILES = ada-exp.y ada-lang.c ada-typeprint.c ada-valprint.c ada-tasks.c \
 	dwarf2expr.c dwarf2loc.c dwarf2read.c dwarf2-frame.c \
 	dwarf2-frame-tailcall.c \
 	elfread.c environ.c eval.c event-loop.c event-top.c \
-	exceptions.c expprint.c \
+	exceptions.c expprint.c extension.c \
 	f-exp.y f-lang.c f-typeprint.c f-valprint.c filesystem.c \
 	findcmd.c findvar.c frame.c frame-base.c frame-unwind.c \
 	gdbarch.c arch-utils.c gdb_bfd.c gdb_obstack.c \
@@ -820,6 +820,7 @@ tui/tui-windata.h tui/tui-data.h tui/tui-win.h tui/tui-stack.h \
 tui/tui-winsource.h tui/tui-regs.h tui/tui-io.h tui/tui-layout.h \
 tui/tui-source.h sol2-tdep.h gregset.h sh-tdep.h sh64-tdep.h \
 expression.h score-tdep.h gdb_select.h ser-tcp.h \
+extension.h extension-priv.h \
 build-id.h buildsym.h valprint.h \
 typeprint.h mi/mi-getopt.h mi/mi-parse.h mi/mi-console.h \
 mi/mi-out.h mi/mi-main.h mi/mi-common.h mi/mi-cmds.h linux-nat.h \
@@ -906,6 +907,7 @@ COMMON_OBS = $(DEPFILES) $(CONFIG_OBS) $(YYOBJ) \
 	infcmd.o infrun.o \
 	expprint.o environ.o stack.o thread.o \
 	exceptions.o \
+	extension.o \
 	filesystem.o \
 	filestuff.o \
 	inf-child.o \
diff --git a/gdb/extension.h b/gdb/extension.h
new file mode 100644
index 0000000..11d5cdb
--- /dev/null
+++ b/gdb/extension.h
@@ -0,0 +1,214 @@
+/* Interface between gdb and its extension languages.
+
+   Copyright (C) 2013 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/>.  */
+
+#ifndef EXTENSION_H
+#define EXTENSION_H
+
+#include "mi/mi-cmds.h" /* For PRINT_NO_VALUES, etc.  */
+
+struct breakpoint;
+struct command_line;
+struct frame_info;
+struct language_defn;
+struct objfile;
+struct extension_language_defn;
+struct type;
+struct ui_file;
+struct ui_out;
+struct value;
+struct value_print_options;
+
+/* A function to load and process a script file.
+   The file has been opened and is ready to be read from the beginning.
+   Any exceptions are not caught, and are passed to the caller.  */
+typedef void script_sourcer_func (const struct extension_language_defn *,
+				  FILE *stream, const char *filename);
+
+/* A function to load and process a script for an objfile.
+   The file has been opened and is ready to be read from the beginning.
+   Any exceptions are not caught, and are passed to the caller.  */
+typedef void objfile_script_sourcer_func
+  (const struct extension_language_defn *,
+   struct objfile *, FILE *stream, const char *filename);
+
+/* Enum of each extension(/scripting) language.  */
+
+enum extension_language
+  {
+    EXT_LANG_NONE,
+    EXT_LANG_GDB,
+    EXT_LANG_PYTHON
+  };
+
+/* Extension language frame-filter status return values.  */
+
+enum ext_lang_bt_status
+  {
+    /* Return when an error has occurred in processing frame filters,
+       or when printing the stack.  */
+    EXT_LANG_BT_ERROR = -1,
+
+    /* Return from internal routines to indicate that the function
+       succeeded.  */
+    EXT_LANG_BT_OK = 1,
+
+    /* Return when the frame filter process is complete, and all
+       operations have succeeded.  */
+    EXT_LANG_BT_COMPLETED = 2,
+
+    /* Return when the frame filter process is complete, but there
+       were no filter registered and enabled to process.  */
+    EXT_LANG_BT_NO_FILTERS = 3
+  };
+
+/* Flags to pass to apply_extlang_frame_filter.  */
+
+enum frame_filter_flags
+  {
+    /* Set this flag if frame level is to be printed.  */
+    PRINT_LEVEL = 1,
+
+    /* Set this flag if frame information is to be printed.  */
+    PRINT_FRAME_INFO = 2,
+
+    /* Set this flag if frame arguments are to be printed.  */
+    PRINT_ARGS = 4,
+
+    /* Set this flag if frame locals are to be printed.  */
+    PRINT_LOCALS = 8,
+  };
+
+/* A choice of the different frame argument printing strategies that
+   can occur in different cases of frame filter instantiation.  */
+
+enum ext_lang_frame_args
+  {
+    /* Print no values for arguments when invoked from the MI. */
+    NO_VALUES = PRINT_NO_VALUES,
+
+    MI_PRINT_ALL_VALUES = PRINT_ALL_VALUES,
+
+    /* Print only simple values (what MI defines as "simple") for
+       arguments when invoked from the MI. */
+    MI_PRINT_SIMPLE_VALUES = PRINT_SIMPLE_VALUES,
+
+    /* Print only scalar values for arguments when invoked from the CLI. */
+    CLI_SCALAR_VALUES,
+
+    /* Print all values for arguments when invoked from the CLI. */
+    CLI_ALL_VALUES
+  };
+
+/* The possible results of
+   extension_language_ops.breakpoint_cond_says_stop.  */
+
+enum ext_lang_bp_stop
+  {
+    /* No "stop" condition is set.  */
+    EXT_LANG_BP_STOP_UNSET,
+
+    /* A "stop" condition is set, and it says "don't stop".  */
+    EXT_LANG_BP_STOP_NO,
+
+    /* A "stop" condition is set, and it says "stop".  */
+    EXT_LANG_BP_STOP_YES
+  };
+
+/* Table of type printers associated with the global typedef table.  */
+
+struct ext_lang_type_printers
+{
+  /* Type-printers from Python.  */
+  void *py_type_printers;
+};
+\f
+/* The interface for gdb's own extension(/scripting) language.  */
+extern const struct extension_language_defn extension_language_gdb;
+
+extern const struct extension_language_defn *get_ext_lang_defn
+  (enum extension_language lang);
+
+extern const struct extension_language_defn *get_ext_lang_of_file
+  (const char *file);
+
+extern int ext_lang_present_p (const struct extension_language_defn *);
+
+extern int ext_lang_initialized_p (const struct extension_language_defn *);
+
+extern void throw_ext_lang_unsupported
+  (const struct extension_language_defn *);
+
+/* Accessors for "public" attributes of the extension language definition.  */
+
+extern enum extension_language ext_lang_kind
+  (const struct extension_language_defn *);
+
+extern const char *ext_lang_name (const struct extension_language_defn *);
+
+extern const char *ext_lang_capitalized_name
+  (const struct extension_language_defn *);
+
+extern const char *ext_lang_suffix (const struct extension_language_defn *);
+
+extern const char *ext_lang_auto_load_suffix
+  (const struct extension_language_defn *);
+
+extern script_sourcer_func *ext_lang_script_sourcer
+  (const struct extension_language_defn *);
+
+extern objfile_script_sourcer_func *ext_lang_objfile_script_sourcer
+  (const struct extension_language_defn *);
+
+extern int ext_lang_auto_load_enabled (const struct extension_language_defn *);
+
+/* Wrappers for each extension language API function that iterate over all
+   extension languages.  */
+
+extern void finish_ext_lang_initialization (void);
+
+extern void eval_ext_lang_from_control_command (struct command_line *cmd);
+
+extern void auto_load_ext_lang_scripts_for_objfile (struct objfile *);
+
+extern struct ext_lang_type_printers *start_ext_lang_type_printers (void);
+
+extern char *apply_ext_lang_type_printers (struct ext_lang_type_printers *,
+					   struct type *);
+
+extern void free_ext_lang_type_printers (struct ext_lang_type_printers *);
+
+extern int apply_ext_lang_val_pretty_printer
+  (struct type *type, const gdb_byte *valaddr,
+   int embedded_offset, CORE_ADDR address,
+   struct ui_file *stream, int recurse,
+   const struct value *val, const struct value_print_options *options,
+   const struct language_defn *language);
+
+extern enum ext_lang_bt_status apply_ext_lang_frame_filter
+  (struct frame_info *frame, int flags, enum ext_lang_frame_args args_type,
+   struct ui_out *out, int frame_low, int frame_high);
+
+extern void preserve_ext_lang_values (struct objfile *, htab_t copied_types);
+
+extern int breakpoint_has_ext_lang_cond (struct breakpoint *b,
+					 enum extension_language skip_lang);
+
+extern int breakpoint_ext_lang_cond_says_stop (struct breakpoint *);
+
+#endif /* EXTENSION_H */
diff --git a/gdb/extension-priv.h b/gdb/extension-priv.h
new file mode 100644
index 0000000..3a60a6d
--- /dev/null
+++ b/gdb/extension-priv.h
@@ -0,0 +1,223 @@
+/* Private implementation details of interface between gdb and its
+   extension languages.
+
+   Copyright (C) 2013 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/>.  */
+
+#ifndef EXTENSION_PRIV_H
+#define EXTENSION_PRIV_H
+
+#include "extension.h"
+
+/* High level description of an extension/scripting language.
+   An entry for each is compiled into GDB regardless of whether the support
+   is present.  This is done so that we can issue meaningful errors if the
+   support is not compiled in.  */
+
+struct extension_language_defn
+{
+  /* Enum of the extension language.  */
+  enum extension_language language;
+
+  /* The name of the extension language, lowercase.  E.g., python.  */
+  const char *name;
+
+  /* The capitalized name of the extension language.
+     For python this is "Python".  For gdb this is "GDB".  */
+  const char *capitalized_name;
+
+  /* The file suffix of this extension language.  E.g., ".py".  */
+  const char *suffix;
+
+  /* The suffix of per-objfile scripts to auto-load.
+     E.g., When the program loads libfoo.so, look for libfoo.so-gdb.py.  */
+  const char *auto_load_suffix;
+
+  /* We support embedding external extension language code in GDB's own
+     scripting language.  We do this by having a special command that begins
+     the extension language snippet, and terminate it with "end".
+     This specifies the control type used to implement this.  */
+  enum command_control_type cli_control_type;
+
+  /* A pointer to the "methods" to load scripts in this language,
+     or NULL if the support is not compiled into GDB.  */
+  const struct extension_language_script_ops *script_ops;
+
+  /* Either a pointer to the "methods" of the extension language interface
+     or NULL if the support is not compiled into GDB.
+     This is also NULL for GDB's own scripting language which is relatively
+     primitive, and doesn't provide these features.  */
+  const struct extension_language_ops *ops;
+};
+
+/* The interface for loading scripts from external extension languages,
+   as well as GDB's own scripting language.
+   All of these methods are required to be implemented.  */
+
+struct extension_language_script_ops
+{
+  /* Load a script.  This is called, e.g., via the "source" command.
+     If there's an error while processing the script this function may,
+     but is not required to, throw an error.  */
+  script_sourcer_func *script_sourcer;
+
+  /* Load a script attached to an objfile.
+     If there's an error while processing the script this function may,
+     but is not required to, throw an error.  */
+  objfile_script_sourcer_func *objfile_script_sourcer;
+
+  /* Return non-zero if auto-loading scripts in this extension language
+     is enabled.  */
+  int (*auto_load_enabled) (const struct extension_language_defn *);
+};
+
+/* The interface for making calls from GDB to an external extension
+   language.  This is for non-script-loading related functionality, like
+   pretty-printing, etc.  The reason these are separated out is GDB's own
+   scripting language makes use of extension_language_script_opts, but it
+   makes no use of these.  There is no (current) intention to split
+   extension_language_ops up any further.
+   All of these methods are optional and may be NULL, except where
+   otherwise indicated.  */
+
+struct extension_language_ops
+{
+  /* Called at the end of gdb initialization to give the extension language
+     an opportunity to finish up.  This is useful for things like adding
+     new commands where one has to wait until gdb itself is initialized.  */
+  void (*finish_initialization) (const struct extension_language_defn *);
+
+  /* Return non-zero if the extension language successfully initialized.
+     This method is required.  */
+  int (*initialized) (const struct extension_language_defn *);
+
+  /* Process a sequence of commands embedded in GDB's own scripting language.
+     E.g.,
+     python
+     print 42
+     end  */
+  void (*eval_from_control_command) (const struct extension_language_defn *,
+				     struct command_line *);
+
+  /* Type-printing support:
+     start_type_printers, apply_type_printers, free_type_printers.
+     These methods are optional and may be NULL, but if one of them is
+     implemented then they all must be.  */
+
+  /* Called before printing a type.  */
+  void (*start_type_printers) (const struct extension_language_defn *,
+			       struct ext_lang_type_printers *);
+
+  /* Try to pretty-print TYPE.  If successful the pretty-printed type is
+     returned.  Otherwise NULL is returned.
+     This function has a bit of a funny name, since it actually applies
+     recognizers, but this seemed clearer given the start_type_printers
+     and free_type_printers functions.  */
+  char *(*apply_type_printers) (const struct extension_language_defn *,
+				const struct ext_lang_type_printers *,
+				struct type *);
+
+  /* Called after a type has been printed to give the type pretty-printer
+     mechanism an opportunity to clean up.  */
+  void (*free_type_printers) (const struct extension_language_defn *,
+			      struct ext_lang_type_printers *);
+
+  /* Try to pretty-print a value of type TYPE located at VALADDR
+     + EMBEDDED_OFFSET, which came from the inferior at address ADDRESS
+     + EMBEDDED_OFFSET, onto stdio stream STREAM according to OPTIONS.
+     VAL is the whole object that came from ADDRESS.  VALADDR must point to
+     the head of VAL's contents buffer.
+     Returns non-zero if the value was successfully pretty-printed.  */
+  int (*apply_val_pretty_printer)
+    (const struct extension_language_defn *,
+     struct type *type, const gdb_byte *valaddr,
+     int embedded_offset, CORE_ADDR address,
+     struct ui_file *stream, int recurse,
+     const struct value *val, const struct value_print_options *options,
+     const struct language_defn *language);
+
+  /* GDB access to the "frame filter" feature.
+     FRAME is the source frame to start frame-filter invocation.  FLAGS is an
+     integer holding the flags for printing.  The following elements of
+     the FRAME_FILTER_FLAGS enum denotes the make-up of FLAGS:
+     PRINT_LEVEL is a flag indicating whether to print the frame's
+     relative level in the output.  PRINT_FRAME_INFO is a flag that
+     indicates whether this function should print the frame
+     information, PRINT_ARGS is a flag that indicates whether to print
+     frame arguments, and PRINT_LOCALS, likewise, with frame local
+     variables.  ARGS_TYPE is an enumerator describing the argument
+     format, OUT is the output stream to print.  FRAME_LOW is the
+     beginning of the slice of frames to print, and FRAME_HIGH is the
+     upper limit of the frames to count.  Returns SCR_BT_ERROR on error,
+     or SCR_BT_COMPLETED on success.  */
+  enum ext_lang_bt_status (*apply_frame_filter)
+    (const struct extension_language_defn *,
+     struct frame_info *frame, int flags, enum ext_lang_frame_args args_type,
+     struct ui_out *out, int frame_low, int frame_high);
+
+  /* Update values held by the extension language when OBJFILE is discarded.
+     New global types must be created for every such value, which must then be
+     updated to use the new types.
+     This function typically just iterates over all appropriate values and
+     calls preserve_one_value for each one.
+     COPIED_TYPES is used to prevent cycles / duplicates and is passed to
+     preserve_one_value.  */
+  void (*preserve_values) (const struct extension_language_defn *,
+			   struct objfile *objfile, htab_t copied_types);
+
+  /* Return non-zero if there is a stop condition for the breakpoint.
+     This is used to implement the restriction that a breakpoint may have
+     at most one condition.  */
+  int (*breakpoint_has_cond) (const struct extension_language_defn *,
+			      struct breakpoint *);
+
+  /* Return a value of enum ext_lang_bp_stop indicating if there is a stop
+     condition for the breakpoint, and if so whether the program should
+     stop.  This is called when the program has stopped at the specified
+     breakpoint.
+     While breakpoints can have at most one condition, this is called for
+     every extension language, even if another extension language has a
+     "stop" method: other kinds of breakpoints may be implemented using
+     this method, e.g., "finish breakpoints" in Python.  */
+  enum ext_lang_bp_stop (*breakpoint_cond_says_stop)
+    (const struct extension_language_defn *, struct breakpoint *);
+
+  /* The next three are used to connect gdb's SIGINT handling with the
+     extension language's.
+     These need not be implemented, but if one of them is implemented
+     then they all must be.  */
+
+  /* Clear the SIGINT indicator.  */
+  void (*clear_quit_flag) (const struct extension_language_defn *);
+
+  /* Set the SIGINT indicator.  */
+  void (*set_quit_flag) (const struct extension_language_defn *);
+
+  /* Return non-zero if a SIGINT has occurred.
+     This is expected to also clear the indicator.  */
+  int (*check_quit_flag) (const struct extension_language_defn *);
+
+  /* Called before gdb prints its prompt, giving extension languages an
+     opportunity to change it with set_prompt.
+     The result is non-zero if the prompt was changed.
+     Extension languages are called in order, and once the prompt is
+     changed no further languages are called.  */
+  int (*before_prompt) (const struct extension_language_defn *,
+			const char *current_gdb_prompt);
+};
+
+#endif /* EXTENSION_PRIV_H */
diff --git a/gdb/extension.c b/gdb/extension.c
new file mode 100644
index 0000000..aec988d
--- /dev/null
+++ b/gdb/extension.c
@@ -0,0 +1,711 @@
+/* Interface between gdb and its extension languages.
+
+   Copyright (C) 2013 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/>.  */
+
+/* Note: With few exceptions, external functions and variables in this file
+   have "ext_lang" in the name, and no other symbol in gdb does.  */
+
+#include "defs.h"
+#include "auto-load.h"
+#include "breakpoint.h"
+#include "extension.h"
+#include "extension-priv.h"
+#include "observer.h"
+#include "cli/cli-script.h"
+#include "python/python.h"
+
+/* Iterate over all external extension languages, regardless of whether the
+   support has been compiled in or not.
+   This does not include GDB's own scripting language.  */
+
+#define ALL_EXTENSION_LANGUAGES(i, extlang) \
+  for (/*int*/ i = 0, extlang = extension_languages[0]; \
+       extlang != NULL; \
+       extlang = extension_languages[++i])
+
+/* Iterate over all external extension languages that are supported.
+   This does not include GDB's own scripting language.  */
+
+#define ALL_ENABLED_EXTENSION_LANGUAGES(i, extlang) \
+  for (/*int*/ i = 0, extlang = extension_languages[0]; \
+       extlang != NULL; \
+       extlang = extension_languages[++i]) \
+    if (extlang->ops != NULL)
+
+static script_sourcer_func source_gdb_script;
+static objfile_script_sourcer_func source_gdb_objfile_script;
+
+/* GDB's own scripting language.
+   This exists, in part, to support auto-loading ${prog}-gdb.gdb scripts.  */
+
+static const struct extension_language_script_ops
+  extension_language_gdb_script_ops =
+{
+  source_gdb_script,
+  source_gdb_objfile_script,
+  auto_load_gdb_scripts_enabled
+};
+
+const struct extension_language_defn extension_language_gdb =
+{
+  EXT_LANG_GDB,
+  "gdb",
+  "GDB",
+
+  /* We fall back to interpreting a script as a GDB script if it doesn't
+     match the other scripting languages, but for consistency's sake
+     give it a formal suffix.  */
+  ".gdb",
+  "-gdb.gdb",
+
+  /* cli_control_type: This is never used: GDB's own scripting language
+     has a variety of control types (if, while, etc.).  */
+  commands_control,
+
+  &extension_language_gdb_script_ops,
+
+  /* The rest of the extension language interface isn't supported by GDB's own
+     extension/scripting language.  */
+  NULL
+};
+
+/* NULL-terminated table of all external (non-native) extension languages.
+
+   The order of appearance in the table is important.
+   When multiple extension languages provide the same feature, for example
+   a pretty-printer for a particular type, which one gets used?
+   The algorithm employed here is "the first one wins".  For example, in
+   the case of pretty-printers this means the first one to provide a
+   pretty-printed value is the one that is used.  This algorithm is employed
+   throughout.  */
+
+static const struct extension_language_defn * const extension_languages[] =
+{
+  /* To preserve existing behaviour, python should always appear first.  */
+  &extension_language_python,
+  NULL
+};
+
+/* Return a pointer to the struct extension_language_defn object of
+   extension language LANG.
+   This always returns a non-NULL pointer, even if support for the language
+   is not compiled into this copy of GDB.  */
+
+const struct extension_language_defn *
+get_ext_lang_defn (enum extension_language lang)
+{
+  int i;
+  const struct extension_language_defn *extlang;
+
+  gdb_assert (lang != EXT_LANG_NONE);
+
+  ALL_EXTENSION_LANGUAGES (i, extlang)
+    {
+      if (extlang->language == lang)
+	return extlang;
+    }
+
+  gdb_assert_not_reached ("unable to find extension_language_defn");
+}
+
+/* Return TRUE if FILE has extension EXTENSION.  */
+
+static int
+has_extension (const char *file, const char *extension)
+{
+  int file_len = strlen (file);
+  int extension_len = strlen (extension);
+
+  return (file_len > extension_len
+	  && strcmp (&file[file_len - extension_len], extension) == 0);
+}
+
+/* Return the extension language of FILE, or NULL if
+   the extension language of FILE is not recognized.
+   This is done by looking at the file's suffix.  */
+
+const struct extension_language_defn *
+get_ext_lang_of_file (const char *file)
+{
+  int i;
+  const struct extension_language_defn *extlang;
+
+  ALL_EXTENSION_LANGUAGES (i, extlang)
+    {
+      if (has_extension (file, extlang->suffix))
+	return extlang;
+    }
+
+  return NULL;
+}
+
+/* Return non-zero if support for the specified extension language
+   is compiled in.  */
+
+int
+ext_lang_present_p (const struct extension_language_defn *extlang)
+{
+  return extlang->script_ops != NULL;
+}
+
+/* Return non-zero if the specified extension language has successfully
+   initialized.  */
+
+int
+ext_lang_initialized_p (const struct extension_language_defn *extlang)
+{
+  if (extlang->ops != NULL)
+    {
+      /* This method is required.  */
+      gdb_assert (extlang->ops->initialized != NULL);
+      return extlang->ops->initialized (extlang);
+    }
+
+  return 0;
+}
+
+/* Throw an error indicating EXTLANG is not supported in this copy of GDB.  */
+
+void
+throw_ext_lang_unsupported (const struct extension_language_defn *extlang)
+{
+  throw_error (UNSUPPORTED_ERROR,
+	       _("Scripting in the \"%s\" language is not supported"
+		 " in this copy of GDB."),
+	       ext_lang_capitalized_name (extlang));
+}
+\f
+/* Methods for GDB's own extension/scripting language.  */
+
+/* The extension_language_script_ops.script_sourcer "method".  */
+
+static void
+source_gdb_script (const struct extension_language_defn *extlang,
+		   FILE *stream, const char *file)
+{
+  script_from_file (stream, file);
+}
+
+/* The extension_language_script_ops.objfile_script_sourcer "method".  */
+
+static void
+source_gdb_objfile_script (const struct extension_language_defn *extlang,
+			   struct objfile *objfile,
+			   FILE *stream, const char *file)
+{
+  script_from_file (stream, file);
+}
+\f
+/* Accessors for "public" attributes of struct extension_language.  */
+
+/* Return the "name" field of EXTLANG.  */
+
+const char *
+ext_lang_name (const struct extension_language_defn *extlang)
+{
+  return extlang->name;
+}
+
+/* Return the "capitalized_name" field of EXTLANG.  */
+
+const char *
+ext_lang_capitalized_name (const struct extension_language_defn *extlang)
+{
+  return extlang->capitalized_name;
+}
+
+/* Return the "suffix" field of EXTLANG.  */
+
+const char *
+ext_lang_suffix (const struct extension_language_defn *extlang)
+{
+  return extlang->suffix;
+}
+
+/* Return the "auto_load_suffix" field of EXTLANG.  */
+
+const char *
+ext_lang_auto_load_suffix (const struct extension_language_defn *extlang)
+{
+  return extlang->auto_load_suffix;
+}
+\f
+/* extension_language_script_ops wrappers.  */
+
+/* Return the script "sourcer" function for EXTLANG.
+   This is the function that loads and processes a script.
+   If support for this language isn't compiled in, NULL is returned.  */
+
+script_sourcer_func *
+ext_lang_script_sourcer (const struct extension_language_defn *extlang)
+{
+  if (extlang->script_ops == NULL)
+    return NULL;
+
+  /* The extension language is required to implement this function.  */
+  gdb_assert (extlang->script_ops->script_sourcer != NULL);
+
+  return extlang->script_ops->script_sourcer;
+}
+
+/* Return the objfile script "sourcer" function for EXTLANG.
+   This is the function that loads and processes a script for a particular
+   objfile.
+   If support for this language isn't compiled in, NULL is returned.  */
+
+objfile_script_sourcer_func *
+ext_lang_objfile_script_sourcer (const struct extension_language_defn *extlang)
+{
+  if (extlang->script_ops == NULL)
+    return NULL;
+
+  /* The extension language is required to implement this function.  */
+  gdb_assert (extlang->script_ops->objfile_script_sourcer != NULL);
+
+  return extlang->script_ops->objfile_script_sourcer;
+}
+
+/* Return non-zero if auto-loading of EXTLANG scripts is enabled.
+   Zero is returned if support for this language isn't compiled in.  */
+
+int
+ext_lang_auto_load_enabled (const struct extension_language_defn *extlang)
+{
+  if (extlang->script_ops == NULL)
+    return 0;
+
+  /* The extension language is required to implement this function.  */
+  gdb_assert (extlang->script_ops->auto_load_enabled != NULL);
+
+  return extlang->script_ops->auto_load_enabled (extlang);
+}
+\f
+/* Functions that iterate over all extension languages.
+   These only iterate over external extension languages, not including
+   GDB's own extension/scripting language, unless otherwise indicated.  */
+
+/* Wrapper to call the extension_language_ops.finish_initialization "method"
+   for each compiled-in extension language.  */
+
+void
+finish_ext_lang_initialization (void)
+{
+  int i;
+  const struct extension_language_defn *extlang;
+
+  ALL_ENABLED_EXTENSION_LANGUAGES (i, extlang)
+    {
+      if (extlang->ops->finish_initialization != NULL)
+	extlang->ops->finish_initialization (extlang);
+    }
+}
+
+/* Invoke the appropriate extension_language_ops.eval_from_control_command
+   method to perform CMD, which is a list of commands in an extension language.
+
+   This function is what implements, for example:
+
+   python
+   print 42
+   end
+
+   in a GDB script.  */
+
+void
+eval_ext_lang_from_control_command (struct command_line *cmd)
+{
+  int i;
+  const struct extension_language_defn *extlang;
+
+  ALL_EXTENSION_LANGUAGES (i, extlang)
+    {
+      if (extlang->cli_control_type == cmd->control_type)
+	{
+	  if (extlang->ops->eval_from_control_command != NULL)
+	    {
+	      extlang->ops->eval_from_control_command (extlang, cmd);
+	      return;
+	    }
+	  /* The requested extension language is not supported in this GDB.  */
+	  throw_ext_lang_unsupported (extlang);
+	}
+    }
+
+  gdb_assert_not_reached ("unknown extension language in command_line");
+}
+
+/* Search for and load scripts for OBJFILE written in extension languages.
+   This includes GDB's own scripting language.
+
+   This function is what implements the loading of OBJFILE-gdb.py and
+   OBJFILE-gdb.gdb.  */
+
+void
+auto_load_ext_lang_scripts_for_objfile (struct objfile *objfile)
+{
+  int i;
+  const struct extension_language_defn *extlang;
+
+  extlang = &extension_language_gdb;
+  if (ext_lang_auto_load_enabled (extlang))
+    auto_load_objfile_script (objfile, extlang);
+
+  ALL_ENABLED_EXTENSION_LANGUAGES (i, extlang)
+    {
+      if (ext_lang_auto_load_enabled (extlang))
+	auto_load_objfile_script (objfile, extlang);
+    }
+}
+\f
+/* Interface to type pretty-printers implemented in an extension language.  */
+
+/* Call this at the start when preparing to pretty-print a type.
+   The result is a pointer to an opaque object (to the caller) to be passed
+   to apply_ext_lang_type_printers and free_ext_lang_type_printers.
+
+   We don't know in advance which extension language will provide a
+   pretty-printer for the type, so all are initialized.  */
+
+struct ext_lang_type_printers *
+start_ext_lang_type_printers (void)
+{
+  struct ext_lang_type_printers *printers
+    = XZALLOC (struct ext_lang_type_printers);
+  int i;
+  const struct extension_language_defn *extlang;
+
+  ALL_ENABLED_EXTENSION_LANGUAGES (i, extlang)
+    {
+      if (extlang->ops->start_type_printers != NULL)
+	extlang->ops->start_type_printers (extlang, printers);
+    }
+
+  return printers;
+}
+
+/* Iteratively try the type pretty-printers specified by PRINTERS
+   according to the standard search order (specified by extension_languages),
+   returning the result of the first one that succeeds.
+   If no printer succeeds then NULL is returned.  */
+
+char *
+apply_ext_lang_type_printers (struct ext_lang_type_printers *printers,
+			      struct type *type)
+{
+  int i;
+  const struct extension_language_defn *extlang;
+
+  ALL_ENABLED_EXTENSION_LANGUAGES (i, extlang)
+    {
+      char *result = NULL;
+
+      if (extlang->ops->apply_type_printers != NULL)
+	result = extlang->ops->apply_type_printers (extlang, printers, type);
+
+      if (result != NULL)
+	return result;
+    }
+
+  return NULL;
+}
+
+/* Call this after pretty-printing a type to release all memory held
+   by PRINTERS.  */
+
+void
+free_ext_lang_type_printers (struct ext_lang_type_printers *printers)
+{
+  int i;
+  const struct extension_language_defn *extlang;
+
+  ALL_ENABLED_EXTENSION_LANGUAGES (i, extlang)
+    {
+      if (extlang->ops->free_type_printers != NULL)
+	extlang->ops->free_type_printers (extlang, printers);
+    }
+
+  xfree (printers);
+}
+\f
+/* Try to pretty-print a value of type TYPE located at VALADDR
+   + EMBEDDED_OFFSET, which came from the inferior at address ADDRESS
+   + EMBEDDED_OFFSET, onto stdio stream STREAM according to OPTIONS.
+   VAL is the whole object that came from ADDRESS.  VALADDR must point to
+   the head of VAL's contents buffer.
+   Returns non-zero if the value was successfully pretty-printed.
+
+   Extension languages are tried in the order specified by
+   extension_languages.  The first one to provide a pretty-printed
+   value "wins".  */
+
+int
+apply_ext_lang_val_pretty_printer (struct type *type, const gdb_byte *valaddr,
+				   int embedded_offset, CORE_ADDR address,
+				   struct ui_file *stream, int recurse,
+				   const struct value *val,
+				   const struct value_print_options *options,
+				   const struct language_defn *language)
+{
+  int i;
+  const struct extension_language_defn *extlang;
+
+  ALL_ENABLED_EXTENSION_LANGUAGES (i, extlang)
+    {
+      if (extlang->ops->apply_val_pretty_printer == NULL)
+	continue;
+      if (extlang->ops->apply_val_pretty_printer (extlang, type, valaddr,
+						  embedded_offset, address,
+						  stream, recurse, val,
+						  options, language))
+	return 1;
+    }
+
+  return 0;
+}
+
+/* GDB access to the "frame filter" feature.
+   FRAME is the source frame to start frame-filter invocation.  FLAGS is an
+   integer holding the flags for printing.  The following elements of
+   the FRAME_FILTER_FLAGS enum denotes the make-up of FLAGS:
+   PRINT_LEVEL is a flag indicating whether to print the frame's
+   relative level in the output.  PRINT_FRAME_INFO is a flag that
+   indicates whether this function should print the frame
+   information, PRINT_ARGS is a flag that indicates whether to print
+   frame arguments, and PRINT_LOCALS, likewise, with frame local
+   variables.  ARGS_TYPE is an enumerator describing the argument
+   format, OUT is the output stream to print.  FRAME_LOW is the
+   beginning of the slice of frames to print, and FRAME_HIGH is the
+   upper limit of the frames to count.  Returns EXT_LANG_BT_ERROR on error,
+   or EXT_LANG_BT_COMPLETED on success.
+
+   Extension languages are tried in the order specified by
+   extension_languages.  The first one to provide a filter "wins".
+   If there is an error (EXT_LANG_BT_ERROR) it is reported immediately
+   rather than trying filters in other extension languages.  */
+
+enum ext_lang_bt_status
+apply_ext_lang_frame_filter (struct frame_info *frame, int flags,
+			     enum ext_lang_frame_args args_type,
+			     struct ui_out *out,
+			     int frame_low, int frame_high)
+{
+  int i;
+  const struct extension_language_defn *extlang;
+
+  ALL_ENABLED_EXTENSION_LANGUAGES (i, extlang)
+    {
+      enum ext_lang_bt_status status;
+
+      if (extlang->ops->apply_frame_filter == NULL)
+	continue;
+      status = extlang->ops->apply_frame_filter (extlang, frame, flags,
+					       args_type, out,
+					       frame_low, frame_high);
+      /* We use the filters from the first extension language that has
+	 applicable filters.  Also, an error is reported immediately
+	 rather than continue trying.  */
+      if (status != EXT_LANG_BT_NO_FILTERS)
+	return status;
+    }
+
+  return EXT_LANG_BT_NO_FILTERS;
+}
+
+/* Update values held by the extension language when OBJFILE is discarded.
+   New global types must be created for every such value, which must then be
+   updated to use the new types.
+   The function typically just iterates over all appropriate values and
+   calls preserve_one_value for each one.
+   COPIED_TYPES is used to prevent cycles / duplicates and is passed to
+   preserve_one_value.  */
+
+void
+preserve_ext_lang_values (struct objfile *objfile, htab_t copied_types)
+{
+  int i;
+  const struct extension_language_defn *extlang;
+
+  ALL_ENABLED_EXTENSION_LANGUAGES (i, extlang)
+    {
+      if (extlang->ops->preserve_values != NULL)
+	extlang->ops->preserve_values (extlang, objfile, copied_types);
+    }
+}
+
+/* Return non-zero if there is a stop condition for the breakpoint.
+   If SKIP_LANG is not EXT_LANG_NONE, skip checking this language.
+   This is for the case where we're setting a new condition: Only one
+   condition is allowed, so when setting a condition for any particular
+   extension language, we need to check if any other already has a
+   condition set.  */
+
+int
+breakpoint_has_ext_lang_cond (struct breakpoint *b,
+			      enum extension_language skip_lang)
+{
+  int i;
+  const struct extension_language_defn *extlang;
+
+  ALL_ENABLED_EXTENSION_LANGUAGES (i, extlang)
+    {
+      if (extlang->language != skip_lang
+	  && extlang->ops->breakpoint_has_cond != NULL
+	  && extlang->ops->breakpoint_has_cond (extlang, b))
+	return 1;
+    }
+
+  return 0;
+}
+
+/* Return whether a stop condition for breakpoint B says to stop.
+   True is also returned if there is no stop condition for B.  */
+
+int
+breakpoint_ext_lang_cond_says_stop (struct breakpoint *b)
+{
+  int i;
+  const struct extension_language_defn *extlang;
+  enum ext_lang_bp_stop stop = EXT_LANG_BP_STOP_UNSET;
+
+  ALL_ENABLED_EXTENSION_LANGUAGES (i, extlang)
+    {
+      /* There is a rule that a breakpoint can have at most one of any of a
+	 CLI or extension language condition.  However, Python hacks in "finish
+	 breakpoints" on top of the "stop" check, so we have to call this for
+	 every language, even if we could first determine whether a "stop"
+	 method exists.  */
+      if (extlang->ops->breakpoint_cond_says_stop != NULL)
+	{
+	  enum ext_lang_bp_stop this_stop
+	    = extlang->ops->breakpoint_cond_says_stop (extlang, b);
+
+	  if (this_stop != EXT_LANG_BP_STOP_UNSET)
+	    {
+	      /* Even though we have to check every extension language, only
+		 one of them can return yes/no (because only one of them
+		 can have a "stop" condition).  */
+	      gdb_assert (stop == EXT_LANG_BP_STOP_UNSET);
+	      stop = this_stop;
+	    }
+	}
+    }
+
+  return stop == EXT_LANG_BP_STOP_NO ? 0 : 1;
+}
+\f
+/* ^C/SIGINT support.
+   This requires cooperation with the extension languages so the support
+   is defined here.
+   The prototypes for these are in defs.h.  */
+
+/* Nonzero means a quit has been requested.
+   This flag tracks quit requests but it's only used if the extension language
+   doesn't provide the necessary support.  */
+static int quit_flag;
+
+/* Clear the quit flag.  */
+
+void
+clear_quit_flag (void)
+{
+  int i;
+  const struct extension_language_defn *extlang;
+
+  ALL_ENABLED_EXTENSION_LANGUAGES (i, extlang)
+    {
+      if (extlang->ops->clear_quit_flag != NULL)
+	extlang->ops->clear_quit_flag (extlang);
+    }
+
+  quit_flag = 0;
+}
+
+/* Set the quit flag.  */
+
+void
+set_quit_flag (void)
+{
+  int i;
+  const struct extension_language_defn *extlang;
+
+  ALL_ENABLED_EXTENSION_LANGUAGES (i, extlang)
+    {
+      if (extlang->ops->set_quit_flag != NULL)
+	extlang->ops->set_quit_flag (extlang);
+    }
+
+  quit_flag = 1;
+}
+
+/* Return true if the quit flag has been set, false otherwise.
+   Extension languages may need their own control over whether SIGINT has
+   been seen.  */
+
+int
+check_quit_flag (void)
+{
+  int i, result = 0;
+  const struct extension_language_defn *extlang;
+
+  ALL_ENABLED_EXTENSION_LANGUAGES (i, extlang)
+    {
+      if (extlang->ops->check_quit_flag != NULL)
+	if (extlang->ops->check_quit_flag (extlang) != 0)
+	  result = 1;
+    }
+
+  /* This is written in a particular way to avoid races.  */
+  if (quit_flag)
+    {
+      quit_flag = 0;
+      result = 1;
+    }
+
+  return result;
+}
+\f
+/* Called via an observer before gdb prints its prompt.
+   Iterate over the extension languages giving them a chance to
+   change the prompt.  The first one to change the prompt wins,
+   and no further languages are tried.  */
+
+static void
+ext_lang_before_prompt (const char *current_gdb_prompt)
+{
+  int i;
+  const struct extension_language_defn *extlang;
+
+  ALL_ENABLED_EXTENSION_LANGUAGES (i, extlang)
+    {
+      if (extlang->ops->before_prompt != NULL)
+	{
+	  /* The first extension language to indicate it has set the prompt
+	     "wins".  */
+	  if (extlang->ops->before_prompt (extlang, current_gdb_prompt) != 0)
+	    return;
+	}
+    }
+}
+
+extern initialize_file_ftype _initialize_extension;
+
+void
+_initialize_extension (void)
+{
+  observer_attach_before_prompt (ext_lang_before_prompt);
+}

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v2 02/13] script language API for GDB: extension.[ch]
  2013-12-11  5:11 [PATCH v2 02/13] script language API for GDB: extension.[ch] Doug Evans
@ 2013-12-23 21:57 ` Tom Tromey
  2013-12-24 18:05   ` Doug Evans
  0 siblings, 1 reply; 13+ messages in thread
From: Tom Tromey @ 2013-12-23 21:57 UTC (permalink / raw)
  To: Doug Evans; +Cc: gdb-patches

>>>>> "Doug" == Doug Evans <xdje42@gmail.com> writes:

Doug> +void
Doug> +clear_quit_flag (void)
Doug> +{
Doug> +  int i;
Doug> +  const struct extension_language_defn *extlang;
Doug> +
Doug> +  ALL_ENABLED_EXTENSION_LANGUAGES (i, extlang)
Doug> +    {
Doug> +      if (extlang->ops->clear_quit_flag != NULL)
Doug> +	extlang->ops->clear_quit_flag (extlang);
Doug> +    }
Doug> +
Doug> +  quit_flag = 0;
Doug> +}

I don't think this will work properly.  It seems to me that a given
interrupt may be processed multiple times -- once by each extension
language.

The way it works right now, if Python handles the "quit", then Python
clears the flag -- which also clears gdb's notion of the flag, because
the two are identical.

With the above it seems that Python could clear its flag -- but leave
other extension languages unaware that the interrupt was handled.  So, a
subsequent call into Guile will presumably erroneously throw an
exception.

Tom

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v2 02/13] script language API for GDB: extension.[ch]
  2013-12-23 21:57 ` Tom Tromey
@ 2013-12-24 18:05   ` Doug Evans
  2014-01-03 21:11     ` Ludovic Courtès
  0 siblings, 1 reply; 13+ messages in thread
From: Doug Evans @ 2013-12-24 18:05 UTC (permalink / raw)
  To: Tom Tromey; +Cc: gdb-patches, guile-user

[+ guile-user, in case they have any thoughts on Guile SIGINT handling]

On Mon, Dec 23, 2013 at 1:57 PM, Tom Tromey <tromey@redhat.com> wrote:
>>>>>> "Doug" == Doug Evans <xdje42@gmail.com> writes:
>
> Doug> +void
> Doug> +clear_quit_flag (void)
> Doug> +{
> Doug> +  int i;
> Doug> +  const struct extension_language_defn *extlang;
> Doug> +
> Doug> +  ALL_ENABLED_EXTENSION_LANGUAGES (i, extlang)
> Doug> +    {
> Doug> +      if (extlang->ops->clear_quit_flag != NULL)
> Doug> + extlang->ops->clear_quit_flag (extlang);
> Doug> +    }
> Doug> +
> Doug> +  quit_flag = 0;
> Doug> +}
>
> I don't think this will work properly.  It seems to me that a given
> interrupt may be processed multiple times -- once by each extension
> language.

Guile doesn't provide the same hooks that Python does for SIGINT
(PyErr_SetInterrupt, et.al.) so I expect this part to need some work.

> The way it works right now, if Python handles the "quit", then Python
> clears the flag -- which also clears gdb's notion of the flag, because
> the two are identical.
>
> With the above it seems that Python could clear its flag -- but leave
> other extension languages unaware that the interrupt was handled.  So, a
> subsequent call into Guile will presumably erroneously throw an
> exception.

We carefully manage entry and exit to/from extension languages.
It should be possible to manage SIGINT across these boundaries.
v3 coming up, but I'm leaving this part to v4.

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v2 02/13] script language API for GDB: extension.[ch]
  2013-12-24 18:05   ` Doug Evans
@ 2014-01-03 21:11     ` Ludovic Courtès
  2014-01-06 21:53       ` Tom Tromey
  0 siblings, 1 reply; 13+ messages in thread
From: Ludovic Courtès @ 2014-01-03 21:11 UTC (permalink / raw)
  To: gdb-patches; +Cc: guile-user

Doug Evans <xdje42@gmail.com> skribis:

> On Mon, Dec 23, 2013 at 1:57 PM, Tom Tromey <tromey@redhat.com> wrote:
>>>>>>> "Doug" == Doug Evans <xdje42@gmail.com> writes:
>>
>> Doug> +void
>> Doug> +clear_quit_flag (void)
>> Doug> +{
>> Doug> +  int i;
>> Doug> +  const struct extension_language_defn *extlang;
>> Doug> +
>> Doug> +  ALL_ENABLED_EXTENSION_LANGUAGES (i, extlang)
>> Doug> +    {
>> Doug> +      if (extlang->ops->clear_quit_flag != NULL)
>> Doug> + extlang->ops->clear_quit_flag (extlang);
>> Doug> +    }
>> Doug> +
>> Doug> +  quit_flag = 0;
>> Doug> +}
>>
>> I don't think this will work properly.  It seems to me that a given
>> interrupt may be processed multiple times -- once by each extension
>> language.
>
> Guile doesn't provide the same hooks that Python does for SIGINT
> (PyErr_SetInterrupt, et.al.) so I expect this part to need some work.
>
>> The way it works right now, if Python handles the "quit", then Python
>> clears the flag -- which also clears gdb's notion of the flag, because
>> the two are identical.
>>
>> With the above it seems that Python could clear its flag -- but leave
>> other extension languages unaware that the interrupt was handled.  So, a
>> subsequent call into Guile will presumably erroneously throw an
>> exception.
>
> We carefully manage entry and exit to/from extension languages.
> It should be possible to manage SIGINT across these boundaries.
> v3 coming up, but I'm leaving this part to v4.

I’m not sure I understand the problem.

What part of the code calls signal(2) or sigaction(2) in the end?  There
can only be one SIGINT handler in the process anyway, so I guess it
has to be installed by GDB (the extension-language-independent part), no?

What are extension languages typically expected to do when the ‘quit’
flag is set?

(And happy new year, where applicable! ;-))

Ludo’.

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v2 02/13] script language API for GDB: extension.[ch]
  2014-01-03 21:11     ` Ludovic Courtès
@ 2014-01-06 21:53       ` Tom Tromey
  2014-01-07 13:10         ` Ludovic Courtès
  0 siblings, 1 reply; 13+ messages in thread
From: Tom Tromey @ 2014-01-06 21:53 UTC (permalink / raw)
  To: Ludovic Courtès; +Cc: gdb-patches, guile-user

>>>>> "Ludovic" == Ludovic Courtès <ludo@gnu.org> writes:

Ludovic> I’m not sure I understand the problem.

Ludovic> What are extension languages typically expected to do when the ‘quit’
Ludovic> flag is set?

The basic idea is that if some extension code is running, then C-c ought
to interrupt that code in the way expected by programmers writing code
in that language.

Python provides a kind of low-level API to its equivalent of Guile's
SCM_TICK and async stuff.  So the approach we took in gdb was to unify
gdb's implementation with Python's, when Python is enabled.  This
ensures that a SIGINT delivery is handled a single time -- by Python if
Python code is running, and by gdb if gdb code is running.

One approach for multiple extension languages might be to notice when
switching languages and move the bit.  However I didn't see any way to
do this in the Guile API.  In Python it can be accomplished with
PyErr_SetInterrupt and PyOS_InterruptOccurred.

Tom

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v2 02/13] script language API for GDB: extension.[ch]
  2014-01-06 21:53       ` Tom Tromey
@ 2014-01-07 13:10         ` Ludovic Courtès
  2014-01-07 16:03           ` Tom Tromey
  0 siblings, 1 reply; 13+ messages in thread
From: Ludovic Courtès @ 2014-01-07 13:10 UTC (permalink / raw)
  To: Tom Tromey; +Cc: gdb-patches, guile-user

Tom Tromey <tromey@redhat.com> skribis:

>>>>>> "Ludovic" == Ludovic Courtès <ludo@gnu.org> writes:
>
> Ludovic> I’m not sure I understand the problem.
>
> Ludovic> What are extension languages typically expected to do when the ‘quit’
> Ludovic> flag is set?
>
> The basic idea is that if some extension code is running, then C-c ought
> to interrupt that code in the way expected by programmers writing code
> in that language.
>
> Python provides a kind of low-level API to its equivalent of Guile's
> SCM_TICK and async stuff.  So the approach we took in gdb was to unify
> gdb's implementation with Python's, when Python is enabled.  This
> ensures that a SIGINT delivery is handled a single time -- by Python if
> Python code is running, and by gdb if gdb code is running.

So do I get it right that it’s GDB that does signal(SIGINT, ...), and
its handler just calls PyOS_InterruptOccurred (or so) if Python code
happens to be running?

> One approach for multiple extension languages might be to notice when
> switching languages and move the bit.  However I didn't see any way to
> do this in the Guile API.  In Python it can be accomplished with
> PyErr_SetInterrupt and PyOS_InterruptOccurred.

Would it work, upon SIGINT, to do something like:

  (system-async-mark (lambda ()
                       (throw 'system-error ... EINTR)))

That would eventually raise an exception in Scheme code.

Ludo’.

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v2 02/13] script language API for GDB: extension.[ch]
  2014-01-07 13:10         ` Ludovic Courtès
@ 2014-01-07 16:03           ` Tom Tromey
  2014-01-07 23:05             ` Ludovic Courtès
  0 siblings, 1 reply; 13+ messages in thread
From: Tom Tromey @ 2014-01-07 16:03 UTC (permalink / raw)
  To: Ludovic Courtès; +Cc: gdb-patches, guile-user

>>>>> "Ludovic" == Ludovic Courtès <ludo@gnu.org> writes:

Ludovic> So do I get it right that it’s GDB that does signal(SIGINT, ...), and
Ludovic> its handler just calls PyOS_InterruptOccurred (or so) if Python code
Ludovic> happens to be running?

Yeah.

>> One approach for multiple extension languages might be to notice when
>> switching languages and move the bit.  However I didn't see any way to
>> do this in the Guile API.  In Python it can be accomplished with
>> PyErr_SetInterrupt and PyOS_InterruptOccurred.

Ludovic> Would it work, upon SIGINT, to do something like:
Ludovic>   (system-async-mark (lambda ()
Ludovic>                        (throw 'system-error ... EINTR)))
Ludovic> That would eventually raise an exception in Scheme code.

According to the Guile docs one must use scm_sigaction, since
scm_system_async_mark is not async-signal-safe.

However scm_sigaction takes over the signal.  I don't think that will
work ok.  Also this idea doesn't address how it would interact with the
gdb core or with Python.

Tom

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v2 02/13] script language API for GDB: extension.[ch]
  2014-01-07 16:03           ` Tom Tromey
@ 2014-01-07 23:05             ` Ludovic Courtès
  2014-01-08  3:17               ` Tom Tromey
  2014-01-14 19:17               ` Tom Tromey
  0 siblings, 2 replies; 13+ messages in thread
From: Ludovic Courtès @ 2014-01-07 23:05 UTC (permalink / raw)
  To: Tom Tromey; +Cc: gdb-patches, guile-user

Tom Tromey <tromey@redhat.com> skribis:

> Ludovic> Would it work, upon SIGINT, to do something like:
> Ludovic>   (system-async-mark (lambda ()
> Ludovic>                        (throw 'system-error ... EINTR)))
> Ludovic> That would eventually raise an exception in Scheme code.
>
> According to the Guile docs one must use scm_sigaction, since
> scm_system_async_mark is not async-signal-safe.

Oh, right.  This could be worked around by having a separate signal
delivery thread (which is what Guile currently does internally), but
that’s not so great.

I guess this is another limitation of Guile’s current signal handling
strategy, and something we should fix.

Ludo’.

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v2 02/13] script language API for GDB: extension.[ch]
  2014-01-07 23:05             ` Ludovic Courtès
@ 2014-01-08  3:17               ` Tom Tromey
  2014-01-14 19:17               ` Tom Tromey
  1 sibling, 0 replies; 13+ messages in thread
From: Tom Tromey @ 2014-01-08  3:17 UTC (permalink / raw)
  To: Ludovic Courtès; +Cc: gdb-patches, guile-user

>>>>> "Ludovic" == Ludovic Courtès <ludo@gnu.org> writes:

Ludovic> I guess this is another limitation of Guile’s current signal handling
Ludovic> strategy, and something we should fix.

I think it seems reasonable for Guile itself, but yeah, it makes it
harder to integrate into gdb.  I was pleasantly surprised that Python's
approach was similar enough to gdb's that we could easily integrate the
two.

The Python approach -- basically just setting a per-signal flag that is
checked in the Python equivalent of SCM_TICK -- might be nice for Guile;
however Guile has better threading support, so some additional work may
be required.

Also, at least for gdb it may make sense to mask SIGINT and SIGCHLD in
new Guile threads, if that is possible.

Tom

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v2 02/13] script language API for GDB: extension.[ch]
  2014-01-07 23:05             ` Ludovic Courtès
  2014-01-08  3:17               ` Tom Tromey
@ 2014-01-14 19:17               ` Tom Tromey
  2014-01-15 14:52                 ` Ludovic Courtès
  2014-01-22  4:26                 ` Mark H Weaver
  1 sibling, 2 replies; 13+ messages in thread
From: Tom Tromey @ 2014-01-14 19:17 UTC (permalink / raw)
  To: Ludovic Courtès; +Cc: gdb-patches, guile-user

>>>>> "Ludovic" == Ludovic Courtès <ludo@gnu.org> writes:

Ludovic> I guess this is another limitation of Guile’s current signal handling
Ludovic> strategy, and something we should fix.

FWIW I think it would be sufficient for gdb if scm_system_async_mark, or
something like it, could be invoked from a signal handler.  Then a
SIGINT in gdb could install an async callback that later checks gdb's
quit flag.

Tom

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v2 02/13] script language API for GDB: extension.[ch]
  2014-01-14 19:17               ` Tom Tromey
@ 2014-01-15 14:52                 ` Ludovic Courtès
  2014-01-22  4:26                 ` Mark H Weaver
  1 sibling, 0 replies; 13+ messages in thread
From: Ludovic Courtès @ 2014-01-15 14:52 UTC (permalink / raw)
  To: Tom Tromey; +Cc: gdb-patches, guile-user

Tom Tromey <tromey@redhat.com> skribis:

>>>>>> "Ludovic" == Ludovic Courtès <ludo@gnu.org> writes:
>
> Ludovic> I guess this is another limitation of Guile’s current signal handling
> Ludovic> strategy, and something we should fix.
>
> FWIW I think it would be sufficient for gdb if scm_system_async_mark, or
> something like it, could be invoked from a signal handler.  Then a
> SIGINT in gdb could install an async callback that later checks gdb's
> quit flag.

Right.  Unfortunately that seems hard to fix in the 2.0 series, but 2.2
is not too far away either.

Thanks,
Ludo’.

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v2 02/13] script language API for GDB: extension.[ch]
  2014-01-14 19:17               ` Tom Tromey
  2014-01-15 14:52                 ` Ludovic Courtès
@ 2014-01-22  4:26                 ` Mark H Weaver
  2014-01-22  4:36                   ` Doug Evans
  1 sibling, 1 reply; 13+ messages in thread
From: Mark H Weaver @ 2014-01-22  4:26 UTC (permalink / raw)
  To: Tom Tromey; +Cc: Ludovic Courtès, guile-user, gdb-patches

Tom Tromey <tromey@redhat.com> writes:

>>>>>> "Ludovic" == Ludovic Courtès <ludo@gnu.org> writes:
>
> Ludovic> I guess this is another limitation of Guile’s current signal handling
> Ludovic> strategy, and something we should fix.
>
> FWIW I think it would be sufficient for gdb if scm_system_async_mark, or
> something like it, could be invoked from a signal handler.  Then a
> SIGINT in gdb could install an async callback that later checks gdb's
> quit flag.

As discussed on IRC, one way to accomplish this with current Guile is
as follows: establish a dedicated thread whose sole job it is to call
'scm_system_async_mark_for_thread' as directed by messages received from
a pipe.  A signal handler can then schedule asyncs by writing messages
to this pipe.

We'll try to come up with an nicer solution at some point.

     Mark

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v2 02/13] script language API for GDB: extension.[ch]
  2014-01-22  4:26                 ` Mark H Weaver
@ 2014-01-22  4:36                   ` Doug Evans
  0 siblings, 0 replies; 13+ messages in thread
From: Doug Evans @ 2014-01-22  4:36 UTC (permalink / raw)
  To: Mark H Weaver; +Cc: Tom Tromey, Ludovic Courtès, guile-user, gdb-patches

On Tue, Jan 21, 2014 at 8:21 PM, Mark H Weaver <mhw@netris.org> wrote:
> Tom Tromey <tromey@redhat.com> writes:
>
>>>>>>> "Ludovic" == Ludovic Courtès <ludo@gnu.org> writes:
>>
>> Ludovic> I guess this is another limitation of Guile’s current signal handling
>> Ludovic> strategy, and something we should fix.
>>
>> FWIW I think it would be sufficient for gdb if scm_system_async_mark, or
>> something like it, could be invoked from a signal handler.  Then a
>> SIGINT in gdb could install an async callback that later checks gdb's
>> quit flag.
>
> As discussed on IRC, one way to accomplish this with current Guile is
> as follows: establish a dedicated thread whose sole job it is to call
> 'scm_system_async_mark_for_thread' as directed by messages received from
> a pipe.  A signal handler can then schedule asyncs by writing messages
> to this pipe.
>
> We'll try to come up with an nicer solution at some point.

Righto, and thanks.

I'll implement this as a follow-on to the current patch series.
It'll be mostly just new code, and the current patch series is big enough.

^ permalink raw reply	[flat|nested] 13+ messages in thread

end of thread, other threads:[~2014-01-22  4:36 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-12-11  5:11 [PATCH v2 02/13] script language API for GDB: extension.[ch] Doug Evans
2013-12-23 21:57 ` Tom Tromey
2013-12-24 18:05   ` Doug Evans
2014-01-03 21:11     ` Ludovic Courtès
2014-01-06 21:53       ` Tom Tromey
2014-01-07 13:10         ` Ludovic Courtès
2014-01-07 16:03           ` Tom Tromey
2014-01-07 23:05             ` Ludovic Courtès
2014-01-08  3:17               ` Tom Tromey
2014-01-14 19:17               ` Tom Tromey
2014-01-15 14:52                 ` Ludovic Courtès
2014-01-22  4:26                 ` Mark H Weaver
2014-01-22  4:36                   ` Doug Evans

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).