binutils-gdb/gdb/guile/scm-pretty-print.c
Andrew Burgess 2e62ab400f gdb: Introduce 'print max-depth' feature
Introduce a new print setting max-depth which can be set with 'set
print max-depth DEPTH'.  The default value of DEPTH is 20, but this
can also be set to unlimited.

When GDB is printing a value containing nested structures GDB will
stop descending at depth DEPTH.  Here is a small example:

    typedef struct s1 { int a; } s1;
    typedef struct s2 { s1 b; } s2;
    typedef struct s3 { s2 c; } s3;
    typedef struct s4 { s3 d; } s4;

    s4 var = { { { { 3 } } } };

The following table shows how various depth settings affect printing
of 'var':

    | Depth Setting | Result of 'p var'              |
    |---------------+--------------------------------|
    |     Unlimited | $1 = {d = {c = {b = {a = 3}}}} |
    |             4 | $1 = {d = {c = {b = {a = 3}}}} |
    |             3 | $1 = {d = {c = {b = {...}}}}   |
    |             2 | $1 = {d = {c = {...}}}         |
    |             1 | $1 = {d = {...}}               |
    |             0 | $1 = {...}                     |

Only structures, unions, and arrays are replaced in this way, scalars
and strings are not replaced.

The replacement is counted from the level at which you print, not from
the top level of the structure.  So, consider the above example and
this GDB session:

    (gdb) set print max-depth 2
    (gdb) p var
    $1 = {d = {c = {...}}}
    (gdb) p var.d
    $2 = {c = {b = {...}}}
    (gdb) p var.d.c
    $3 = {b = {a = 3}}

Setting the max-depth to 2 doesn't prevent the user from exploring
deeper into 'var' by asking for specific sub-fields to be printed.

The motivation behind this feature is to try and give the user more
control over how much is printed when examining large, complex data
structures.

The default max-depth of 20 means that there is a change in GDB's
default behaviour.  Someone printing a data structure with 20 levels
of nesting will now see '{...}' instead of their data, they would need
to adjust the max depth, or call print again naming a specific field
in order to dig deeper into their data structure.  If this is
considered a problem then we could increase the default, or even make
the default unlimited.

This commit relies on the previous commit, which added a new field to
the language structure, this new field was a string that contained the
pattern that should be used when a structure/union/array is replaced
in the output, this allows languages to use a syntax that is more
appropriate, mostly this will be selecting the correct types of
bracket '(...)' or '{...}', both of which are currently in use.

This commit should have no impact on MI output, expressions are
printed through the MI using -var-create and then -var-list-children.
As each use of -var-list-children only ever displays a single level of
an expression then the max-depth setting will have no impact.

This commit also adds the max-depth mechanism to the scripting
language pretty printers following basically the same rules as for the
built in value printing.

One quirk is that when printing a value using the display hint 'map',
if the keys of the map are structs then GDB will hide the keys one
depth level after it hides the values, this ensures that GDB produces
output like this:

  $1 = map_object = {[{key1}] = {...}, [{key2}] = {...}}

Instead of this less helpful output:

  $1 = map_object = {[{...}] = {...}, [{...}] = {...}}

This is covered by the new tests in gdb.python/py-nested-maps.exp.

gdb/ChangeLog:

	* cp-valprint.c (cp_print_value_fields): Allow an additional level
	of depth when printing anonymous structs or unions.
	* guile/scm-pretty-print.c (gdbscm_apply_val_pretty_printer):
	Don't print either the top-level value, or the children if the
	max-depth is exceeded.
	(ppscm_print_children): When printing the key of a map, allow one
	extra level of depth.
	* python/py-prettyprint.c (gdbpy_apply_val_pretty_printer): Don't
	print either the top-level value, or the children if the max-depth
	is exceeded.
	(print_children): When printing the key of a map, allow one extra
	level of depth.
	* python/py-value.c (valpy_format_string): Add max_depth keyword.
	* valprint.c: (PRINT_MAX_DEPTH_DEFAULT): Define.
	(user_print_options): Initialise max_depth field.
	(val_print_scalar_or_string_type_p): New function.
	(val_print): Check to see if the max depth has been reached.
	(val_print_check_max_depth): Define new function.
	(show_print_max_depth): New function.
	(_initialize_valprint): Add 'print max-depth' option.
	* valprint.h (struct value_print_options) <max_depth>: New field.
	(val_print_check_max_depth): Declare new function.
	* NEWS: Document new feature.

gdb/doc/ChangeLog:

	* gdb.texinfo (Print Settings): Document 'print max-depth'.
	* guile.texi (Guile Pretty Printing API): Document that 'print
	max-depth' can effect the display of a values children.
	* python.texi (Pretty Printing API): Likewise.
	(Values From Inferior): Document max_depth keyword.

gdb/testsuite/ChangeLog:

	* gdb.base/max-depth.c: New file.
	* gdb.base/max-depth.exp: New file.
	* gdb.python/py-nested-maps.c: New file.
	* gdb.python/py-nested-maps.exp: New file.
	* gdb.python/py-nested-maps.py: New file.
	* gdb.python/py-format-string.exp (test_max_depth): New proc.
	(test_all_common): Call test_max_depth.
	* gdb.fortran/max-depth.exp: New file.
	* gdb.fortran/max-depth.f90: New file.
	* gdb.go/max-depth.exp: New file.
	* gdb.go/max-depth.go: New file.
	* gdb.modula2/max-depth.exp: New file.
	* gdb.modula2/max-depth.c: New file.
	* lib/gdb.exp (get_print_expr_at_depths): New proc.
2019-04-29 22:01:09 +01:00

1117 lines
31 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* GDB/Scheme pretty-printing.
Copyright (C) 2008-2019 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/>. */
/* See README file in this directory for implementation notes, coding
conventions, et.al. */
#include "defs.h"
#include "charset.h"
#include "symtab.h" /* Needed by language.h. */
#include "language.h"
#include "objfiles.h"
#include "value.h"
#include "valprint.h"
#include "guile-internal.h"
/* Return type of print_string_repr. */
enum string_repr_result
{
/* The string method returned None. */
STRING_REPR_NONE,
/* The string method had an error. */
STRING_REPR_ERROR,
/* Everything ok. */
STRING_REPR_OK
};
/* Display hints. */
enum display_hint
{
/* No display hint. */
HINT_NONE,
/* The display hint has a bad value. */
HINT_ERROR,
/* Print as an array. */
HINT_ARRAY,
/* Print as a map. */
HINT_MAP,
/* Print as a string. */
HINT_STRING
};
/* The <gdb:pretty-printer> smob. */
typedef struct
{
/* This must appear first. */
gdb_smob base;
/* A string representing the name of the printer. */
SCM name;
/* A boolean indicating whether the printer is enabled. */
SCM enabled;
/* A procedure called to look up the printer for the given value.
The procedure is called as (lookup gdb:pretty-printer value).
The result should either be a gdb:pretty-printer object that will print
the value, or #f if the value is not recognized. */
SCM lookup;
/* Note: Attaching subprinters to this smob is left to Scheme. */
} pretty_printer_smob;
/* The <gdb:pretty-printer-worker> smob. */
typedef struct
{
/* This must appear first. */
gdb_smob base;
/* Either #f or one of the supported display hints: map, array, string.
If neither of those then the display hint is ignored (treated as #f). */
SCM display_hint;
/* A procedure called to pretty-print the value.
(lambda (printer) ...) -> string | <gdb:lazy-string> | <gdb:value> */
SCM to_string;
/* A procedure called to print children of the value.
(lambda (printer) ...) -> <gdb:iterator>
The iterator returns a pair for each iteration: (name . value),
where "value" can have the same types as to_string. */
SCM children;
} pretty_printer_worker_smob;
static const char pretty_printer_smob_name[] =
"gdb:pretty-printer";
static const char pretty_printer_worker_smob_name[] =
"gdb:pretty-printer-worker";
/* The tag Guile knows the pretty-printer smobs by. */
static scm_t_bits pretty_printer_smob_tag;
static scm_t_bits pretty_printer_worker_smob_tag;
/* The global pretty-printer list. */
static SCM pretty_printer_list;
/* gdb:pp-type-error. */
static SCM pp_type_error_symbol;
/* Pretty-printer display hints are specified by strings. */
static SCM ppscm_map_string;
static SCM ppscm_array_string;
static SCM ppscm_string_string;
/* Administrivia for pretty-printer matcher smobs. */
/* The smob "print" function for <gdb:pretty-printer>. */
static int
ppscm_print_pretty_printer_smob (SCM self, SCM port, scm_print_state *pstate)
{
pretty_printer_smob *pp_smob = (pretty_printer_smob *) SCM_SMOB_DATA (self);
gdbscm_printf (port, "#<%s ", pretty_printer_smob_name);
scm_write (pp_smob->name, port);
scm_puts (gdbscm_is_true (pp_smob->enabled) ? " enabled" : " disabled",
port);
scm_puts (">", port);
scm_remember_upto_here_1 (self);
/* Non-zero means success. */
return 1;
}
/* (make-pretty-printer string procedure) -> <gdb:pretty-printer> */
static SCM
gdbscm_make_pretty_printer (SCM name, SCM lookup)
{
pretty_printer_smob *pp_smob = (pretty_printer_smob *)
scm_gc_malloc (sizeof (pretty_printer_smob),
pretty_printer_smob_name);
SCM smob;
SCM_ASSERT_TYPE (scm_is_string (name), name, SCM_ARG1, FUNC_NAME,
_("string"));
SCM_ASSERT_TYPE (gdbscm_is_procedure (lookup), lookup, SCM_ARG2, FUNC_NAME,
_("procedure"));
pp_smob->name = name;
pp_smob->lookup = lookup;
pp_smob->enabled = SCM_BOOL_T;
smob = scm_new_smob (pretty_printer_smob_tag, (scm_t_bits) pp_smob);
gdbscm_init_gsmob (&pp_smob->base);
return smob;
}
/* Return non-zero if SCM is a <gdb:pretty-printer> object. */
static int
ppscm_is_pretty_printer (SCM scm)
{
return SCM_SMOB_PREDICATE (pretty_printer_smob_tag, scm);
}
/* (pretty-printer? object) -> boolean */
static SCM
gdbscm_pretty_printer_p (SCM scm)
{
return scm_from_bool (ppscm_is_pretty_printer (scm));
}
/* Returns the <gdb:pretty-printer> object in SELF.
Throws an exception if SELF is not a <gdb:pretty-printer> object. */
static SCM
ppscm_get_pretty_printer_arg_unsafe (SCM self, int arg_pos,
const char *func_name)
{
SCM_ASSERT_TYPE (ppscm_is_pretty_printer (self), self, arg_pos, func_name,
pretty_printer_smob_name);
return self;
}
/* Returns a pointer to the pretty-printer smob of SELF.
Throws an exception if SELF is not a <gdb:pretty-printer> object. */
static pretty_printer_smob *
ppscm_get_pretty_printer_smob_arg_unsafe (SCM self, int arg_pos,
const char *func_name)
{
SCM pp_scm = ppscm_get_pretty_printer_arg_unsafe (self, arg_pos, func_name);
pretty_printer_smob *pp_smob
= (pretty_printer_smob *) SCM_SMOB_DATA (pp_scm);
return pp_smob;
}
/* Pretty-printer methods. */
/* (pretty-printer-enabled? <gdb:pretty-printer>) -> boolean */
static SCM
gdbscm_pretty_printer_enabled_p (SCM self)
{
pretty_printer_smob *pp_smob
= ppscm_get_pretty_printer_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
return pp_smob->enabled;
}
/* (set-pretty-printer-enabled! <gdb:pretty-printer> boolean)
-> unspecified */
static SCM
gdbscm_set_pretty_printer_enabled_x (SCM self, SCM enabled)
{
pretty_printer_smob *pp_smob
= ppscm_get_pretty_printer_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
pp_smob->enabled = scm_from_bool (gdbscm_is_true (enabled));
return SCM_UNSPECIFIED;
}
/* (pretty-printers) -> list
Returns the list of global pretty-printers. */
static SCM
gdbscm_pretty_printers (void)
{
return pretty_printer_list;
}
/* (set-pretty-printers! list) -> unspecified
Set the global pretty-printers list. */
static SCM
gdbscm_set_pretty_printers_x (SCM printers)
{
SCM_ASSERT_TYPE (gdbscm_is_true (scm_list_p (printers)), printers,
SCM_ARG1, FUNC_NAME, _("list"));
pretty_printer_list = printers;
return SCM_UNSPECIFIED;
}
/* Administrivia for pretty-printer-worker smobs.
These are created when a matcher recognizes a value. */
/* The smob "print" function for <gdb:pretty-printer-worker>. */
static int
ppscm_print_pretty_printer_worker_smob (SCM self, SCM port,
scm_print_state *pstate)
{
pretty_printer_worker_smob *w_smob
= (pretty_printer_worker_smob *) SCM_SMOB_DATA (self);
gdbscm_printf (port, "#<%s ", pretty_printer_worker_smob_name);
scm_write (w_smob->display_hint, port);
scm_puts (" ", port);
scm_write (w_smob->to_string, port);
scm_puts (" ", port);
scm_write (w_smob->children, port);
scm_puts (">", port);
scm_remember_upto_here_1 (self);
/* Non-zero means success. */
return 1;
}
/* (make-pretty-printer-worker string procedure procedure)
-> <gdb:pretty-printer-worker> */
static SCM
gdbscm_make_pretty_printer_worker (SCM display_hint, SCM to_string,
SCM children)
{
pretty_printer_worker_smob *w_smob = (pretty_printer_worker_smob *)
scm_gc_malloc (sizeof (pretty_printer_worker_smob),
pretty_printer_worker_smob_name);
SCM w_scm;
w_smob->display_hint = display_hint;
w_smob->to_string = to_string;
w_smob->children = children;
w_scm = scm_new_smob (pretty_printer_worker_smob_tag, (scm_t_bits) w_smob);
gdbscm_init_gsmob (&w_smob->base);
return w_scm;
}
/* Return non-zero if SCM is a <gdb:pretty-printer-worker> object. */
static int
ppscm_is_pretty_printer_worker (SCM scm)
{
return SCM_SMOB_PREDICATE (pretty_printer_worker_smob_tag, scm);
}
/* (pretty-printer-worker? object) -> boolean */
static SCM
gdbscm_pretty_printer_worker_p (SCM scm)
{
return scm_from_bool (ppscm_is_pretty_printer_worker (scm));
}
/* Helper function to create a <gdb:exception> object indicating that the
type of some value returned from a pretty-printer is invalid. */
static SCM
ppscm_make_pp_type_error_exception (const char *message, SCM object)
{
std::string msg = string_printf ("%s: ~S", message);
return gdbscm_make_error (pp_type_error_symbol,
NULL /* func */, msg.c_str (),
scm_list_1 (object), scm_list_1 (object));
}
/* Print MESSAGE as an exception (meaning it is controlled by
"guile print-stack").
Called from the printer code when the Scheme code returns an invalid type
for something. */
static void
ppscm_print_pp_type_error (const char *message, SCM object)
{
SCM exception = ppscm_make_pp_type_error_exception (message, object);
gdbscm_print_gdb_exception (SCM_BOOL_F, exception);
}
/* Helper function for find_pretty_printer which iterates over a list,
calls each function and inspects output. This will return a
<gdb:pretty-printer> object if one recognizes VALUE. If no printer is
found, it will return #f. On error, it will return a <gdb:exception>
object.
Note: This has to be efficient and careful.
We don't want to excessively slow down printing of values, but any kind of
random crud can appear in the pretty-printer list, and we can't crash
because of it. */
static SCM
ppscm_search_pp_list (SCM list, SCM value)
{
SCM orig_list = list;
if (scm_is_null (list))
return SCM_BOOL_F;
if (gdbscm_is_false (scm_list_p (list))) /* scm_is_pair? */
{
return ppscm_make_pp_type_error_exception
(_("pretty-printer list is not a list"), list);
}
for ( ; scm_is_pair (list); list = scm_cdr (list))
{
SCM matcher = scm_car (list);
SCM worker;
pretty_printer_smob *pp_smob;
if (!ppscm_is_pretty_printer (matcher))
{
return ppscm_make_pp_type_error_exception
(_("pretty-printer list contains non-pretty-printer object"),
matcher);
}
pp_smob = (pretty_printer_smob *) SCM_SMOB_DATA (matcher);
/* Skip if disabled. */
if (gdbscm_is_false (pp_smob->enabled))
continue;
if (!gdbscm_is_procedure (pp_smob->lookup))
{
return ppscm_make_pp_type_error_exception
(_("invalid lookup object in pretty-printer matcher"),
pp_smob->lookup);
}
worker = gdbscm_safe_call_2 (pp_smob->lookup, matcher,
value, gdbscm_memory_error_p);
if (!gdbscm_is_false (worker))
{
if (gdbscm_is_exception (worker))
return worker;
if (ppscm_is_pretty_printer_worker (worker))
return worker;
return ppscm_make_pp_type_error_exception
(_("invalid result from pretty-printer lookup"), worker);
}
}
if (!scm_is_null (list))
{
return ppscm_make_pp_type_error_exception
(_("pretty-printer list is not a list"), orig_list);
}
return SCM_BOOL_F;
}
/* Subroutine of find_pretty_printer to simplify it.
Look for a pretty-printer to print VALUE in all objfiles.
If there's an error an exception smob is returned.
The result is #f, if no pretty-printer was found.
Otherwise the result is the pretty-printer smob. */
static SCM
ppscm_find_pretty_printer_from_objfiles (SCM value)
{
for (objfile *objfile : current_program_space->objfiles ())
{
objfile_smob *o_smob = ofscm_objfile_smob_from_objfile (objfile);
SCM pp
= ppscm_search_pp_list (ofscm_objfile_smob_pretty_printers (o_smob),
value);
/* Note: This will return if pp is a <gdb:exception> object,
which is what we want. */
if (gdbscm_is_true (pp))
return pp;
}
return SCM_BOOL_F;
}
/* Subroutine of find_pretty_printer to simplify it.
Look for a pretty-printer to print VALUE in the current program space.
If there's an error an exception smob is returned.
The result is #f, if no pretty-printer was found.
Otherwise the result is the pretty-printer smob. */
static SCM
ppscm_find_pretty_printer_from_progspace (SCM value)
{
pspace_smob *p_smob = psscm_pspace_smob_from_pspace (current_program_space);
SCM pp
= ppscm_search_pp_list (psscm_pspace_smob_pretty_printers (p_smob), value);
return pp;
}
/* Subroutine of find_pretty_printer to simplify it.
Look for a pretty-printer to print VALUE in the gdb module.
If there's an error a Scheme exception is returned.
The result is #f, if no pretty-printer was found.
Otherwise the result is the pretty-printer smob. */
static SCM
ppscm_find_pretty_printer_from_gdb (SCM value)
{
SCM pp = ppscm_search_pp_list (pretty_printer_list, value);
return pp;
}
/* Find the pretty-printing constructor function for VALUE. If no
pretty-printer exists, return #f. If one exists, return the
gdb:pretty-printer smob that implements it. On error, an exception smob
is returned.
Note: In the end it may be better to call out to Scheme once, and then
do all of the lookup from Scheme. TBD. */
static SCM
ppscm_find_pretty_printer (SCM value)
{
SCM pp;
/* Look at the pretty-printer list for each objfile
in the current program-space. */
pp = ppscm_find_pretty_printer_from_objfiles (value);
/* Note: This will return if function is a <gdb:exception> object,
which is what we want. */
if (gdbscm_is_true (pp))
return pp;
/* Look at the pretty-printer list for the current program-space. */
pp = ppscm_find_pretty_printer_from_progspace (value);
/* Note: This will return if function is a <gdb:exception> object,
which is what we want. */
if (gdbscm_is_true (pp))
return pp;
/* Look at the pretty-printer list in the gdb module. */
pp = ppscm_find_pretty_printer_from_gdb (value);
return pp;
}
/* Pretty-print a single value, via the PRINTER, which must be a
<gdb:pretty-printer-worker> object.
The caller is responsible for ensuring PRINTER is valid.
If the function returns a string, an SCM containing the string
is returned. If the function returns #f that means the pretty
printer returned #f as a value. Otherwise, if the function returns a
<gdb:value> object, *OUT_VALUE is set to the value and #t is returned.
It is an error if the printer returns #t.
On error, an exception smob is returned. */
static SCM
ppscm_pretty_print_one_value (SCM printer, struct value **out_value,
struct gdbarch *gdbarch,
const struct language_defn *language)
{
SCM result = SCM_BOOL_F;
*out_value = NULL;
try
{
pretty_printer_worker_smob *w_smob
= (pretty_printer_worker_smob *) SCM_SMOB_DATA (printer);
result = gdbscm_safe_call_1 (w_smob->to_string, printer,
gdbscm_memory_error_p);
if (gdbscm_is_false (result))
; /* Done. */
else if (scm_is_string (result)
|| lsscm_is_lazy_string (result))
; /* Done. */
else if (vlscm_is_value (result))
{
SCM except_scm;
*out_value
= vlscm_convert_value_from_scheme (FUNC_NAME, GDBSCM_ARG_NONE,
result, &except_scm,
gdbarch, language);
if (*out_value != NULL)
result = SCM_BOOL_T;
else
result = except_scm;
}
else if (gdbscm_is_exception (result))
; /* Done. */
else
{
/* Invalid result from to-string. */
result = ppscm_make_pp_type_error_exception
(_("invalid result from pretty-printer to-string"), result);
}
}
catch (const gdb_exception &except)
{
}
return result;
}
/* Return the display hint for PRINTER as a Scheme object.
The caller is responsible for ensuring PRINTER is a
<gdb:pretty-printer-worker> object. */
static SCM
ppscm_get_display_hint_scm (SCM printer)
{
pretty_printer_worker_smob *w_smob
= (pretty_printer_worker_smob *) SCM_SMOB_DATA (printer);
return w_smob->display_hint;
}
/* Return the display hint for the pretty-printer PRINTER.
The caller is responsible for ensuring PRINTER is a
<gdb:pretty-printer-worker> object.
Returns the display hint or #f if the hint is not a string. */
static enum display_hint
ppscm_get_display_hint_enum (SCM printer)
{
SCM hint = ppscm_get_display_hint_scm (printer);
if (gdbscm_is_false (hint))
return HINT_NONE;
if (scm_is_string (hint))
{
if (gdbscm_is_true (scm_string_equal_p (hint, ppscm_array_string)))
return HINT_STRING;
if (gdbscm_is_true (scm_string_equal_p (hint, ppscm_map_string)))
return HINT_STRING;
if (gdbscm_is_true (scm_string_equal_p (hint, ppscm_string_string)))
return HINT_STRING;
return HINT_ERROR;
}
return HINT_ERROR;
}
/* A wrapper for gdbscm_print_gdb_exception that ignores memory errors.
EXCEPTION is a <gdb:exception> object. */
static void
ppscm_print_exception_unless_memory_error (SCM exception,
struct ui_file *stream)
{
if (gdbscm_memory_error_p (gdbscm_exception_key (exception)))
{
gdb::unique_xmalloc_ptr<char> msg
= gdbscm_exception_message_to_string (exception);
/* This "shouldn't happen", but play it safe. */
if (msg == NULL || msg.get ()[0] == '\0')
fprintf_filtered (stream, _("<error reading variable>"));
else
{
/* Remove the trailing newline. We could instead call a special
routine for printing memory error messages, but this is easy
enough for now. */
char *msg_text = msg.get ();
size_t len = strlen (msg_text);
if (msg_text[len - 1] == '\n')
msg_text[len - 1] = '\0';
fprintf_filtered (stream, _("<error reading variable: %s>"), msg_text);
}
}
else
gdbscm_print_gdb_exception (SCM_BOOL_F, exception);
}
/* Helper for gdbscm_apply_val_pretty_printer which calls to_string and
formats the result. */
static enum string_repr_result
ppscm_print_string_repr (SCM printer, enum display_hint hint,
struct ui_file *stream, int recurse,
const struct value_print_options *options,
struct gdbarch *gdbarch,
const struct language_defn *language)
{
struct value *replacement = NULL;
SCM str_scm;
enum string_repr_result result = STRING_REPR_ERROR;
str_scm = ppscm_pretty_print_one_value (printer, &replacement,
gdbarch, language);
if (gdbscm_is_false (str_scm))
{
result = STRING_REPR_NONE;
}
else if (scm_is_eq (str_scm, SCM_BOOL_T))
{
struct value_print_options opts = *options;
gdb_assert (replacement != NULL);
opts.addressprint = 0;
common_val_print (replacement, stream, recurse, &opts, language);
result = STRING_REPR_OK;
}
else if (scm_is_string (str_scm))
{
size_t length;
gdb::unique_xmalloc_ptr<char> string
= gdbscm_scm_to_string (str_scm, &length,
target_charset (gdbarch), 0 /*!strict*/, NULL);
if (hint == HINT_STRING)
{
struct type *type = builtin_type (gdbarch)->builtin_char;
LA_PRINT_STRING (stream, type, (gdb_byte *) string.get (),
length, NULL, 0, options);
}
else
{
/* Alas scm_to_stringn doesn't nul-terminate the string if we
ask for the length. */
size_t i;
for (i = 0; i < length; ++i)
{
if (string.get ()[i] == '\0')
fputs_filtered ("\\000", stream);
else
fputc_filtered (string.get ()[i], stream);
}
}
result = STRING_REPR_OK;
}
else if (lsscm_is_lazy_string (str_scm))
{
struct value_print_options local_opts = *options;
local_opts.addressprint = 0;
lsscm_val_print_lazy_string (str_scm, stream, &local_opts);
result = STRING_REPR_OK;
}
else
{
gdb_assert (gdbscm_is_exception (str_scm));
ppscm_print_exception_unless_memory_error (str_scm, stream);
result = STRING_REPR_ERROR;
}
return result;
}
/* Helper for gdbscm_apply_val_pretty_printer that formats children of the
printer, if any exist.
The caller is responsible for ensuring PRINTER is a printer smob.
If PRINTED_NOTHING is true, then nothing has been printed by to_string,
and format output accordingly. */
static void
ppscm_print_children (SCM printer, enum display_hint hint,
struct ui_file *stream, int recurse,
const struct value_print_options *options,
struct gdbarch *gdbarch,
const struct language_defn *language,
int printed_nothing)
{
pretty_printer_worker_smob *w_smob
= (pretty_printer_worker_smob *) SCM_SMOB_DATA (printer);
int is_map, is_array, done_flag, pretty;
unsigned int i;
SCM children;
SCM iter = SCM_BOOL_F; /* -Wall */
if (gdbscm_is_false (w_smob->children))
return;
if (!gdbscm_is_procedure (w_smob->children))
{
ppscm_print_pp_type_error
(_("pretty-printer \"children\" object is not a procedure or #f"),
w_smob->children);
return;
}
/* If we are printing a map or an array, we want special formatting. */
is_map = hint == HINT_MAP;
is_array = hint == HINT_ARRAY;
children = gdbscm_safe_call_1 (w_smob->children, printer,
gdbscm_memory_error_p);
if (gdbscm_is_exception (children))
{
ppscm_print_exception_unless_memory_error (children, stream);
goto done;
}
/* We combine two steps here: get children, make an iterator out of them.
This simplifies things because there's no language means of creating
iterators, and it's the printer object that knows how it will want its
children iterated over. */
if (!itscm_is_iterator (children))
{
ppscm_print_pp_type_error
(_("result of pretty-printer \"children\" procedure is not"
" a <gdb:iterator> object"), children);
goto done;
}
iter = children;
/* Use the prettyformat_arrays option if we are printing an array,
and the pretty option otherwise. */
if (is_array)
pretty = options->prettyformat_arrays;
else
{
if (options->prettyformat == Val_prettyformat)
pretty = 1;
else
pretty = options->prettyformat_structs;
}
done_flag = 0;
for (i = 0; i < options->print_max; ++i)
{
SCM scm_name, v_scm;
SCM item = itscm_safe_call_next_x (iter, gdbscm_memory_error_p);
if (gdbscm_is_exception (item))
{
ppscm_print_exception_unless_memory_error (item, stream);
break;
}
if (itscm_is_end_of_iteration (item))
{
/* Set a flag so we can know whether we printed all the
available elements. */
done_flag = 1;
break;
}
if (! scm_is_pair (item))
{
ppscm_print_pp_type_error
(_("result of pretty-printer children iterator is not a pair"
" or (end-of-iteration)"),
item);
continue;
}
scm_name = scm_car (item);
v_scm = scm_cdr (item);
if (!scm_is_string (scm_name))
{
ppscm_print_pp_type_error
(_("first element of pretty-printer children iterator is not"
" a string"), item);
continue;
}
gdb::unique_xmalloc_ptr<char> name
= gdbscm_scm_to_c_string (scm_name);
/* Print initial "{". For other elements, there are three cases:
1. Maps. Print a "," after each value element.
2. Arrays. Always print a ",".
3. Other. Always print a ",". */
if (i == 0)
{
if (printed_nothing)
fputs_filtered ("{", stream);
else
fputs_filtered (" = {", stream);
}
else if (! is_map || i % 2 == 0)
fputs_filtered (pretty ? "," : ", ", stream);
/* In summary mode, we just want to print "= {...}" if there is
a value. */
if (options->summary)
{
/* This increment tricks the post-loop logic to print what
we want. */
++i;
/* Likewise. */
pretty = 0;
break;
}
if (! is_map || i % 2 == 0)
{
if (pretty)
{
fputs_filtered ("\n", stream);
print_spaces_filtered (2 + 2 * recurse, stream);
}
else
wrap_here (n_spaces (2 + 2 *recurse));
}
if (is_map && i % 2 == 0)
fputs_filtered ("[", stream);
else if (is_array)
{
/* We print the index, not whatever the child method
returned as the name. */
if (options->print_array_indexes)
fprintf_filtered (stream, "[%d] = ", i);
}
else if (! is_map)
{
fputs_filtered (name.get (), stream);
fputs_filtered (" = ", stream);
}
if (lsscm_is_lazy_string (v_scm))
{
struct value_print_options local_opts = *options;
local_opts.addressprint = 0;
lsscm_val_print_lazy_string (v_scm, stream, &local_opts);
}
else if (scm_is_string (v_scm))
{
gdb::unique_xmalloc_ptr<char> output
= gdbscm_scm_to_c_string (v_scm);
fputs_filtered (output.get (), stream);
}
else
{
SCM except_scm;
struct value *value
= vlscm_convert_value_from_scheme (FUNC_NAME, GDBSCM_ARG_NONE,
v_scm, &except_scm,
gdbarch, language);
if (value == NULL)
{
ppscm_print_exception_unless_memory_error (except_scm, stream);
break;
}
else
{
/* When printing the key of a map we allow one additional
level of depth. This means the key will print before the
value does. */
struct value_print_options opt = *options;
if (is_map && i % 2 == 0
&& opt.max_depth != -1
&& opt.max_depth < INT_MAX)
++opt.max_depth;
common_val_print (value, stream, recurse + 1, &opt, language);
}
}
if (is_map && i % 2 == 0)
fputs_filtered ("] = ", stream);
}
if (i)
{
if (!done_flag)
{
if (pretty)
{
fputs_filtered ("\n", stream);
print_spaces_filtered (2 + 2 * recurse, stream);
}
fputs_filtered ("...", stream);
}
if (pretty)
{
fputs_filtered ("\n", stream);
print_spaces_filtered (2 * recurse, stream);
}
fputs_filtered ("}", stream);
}
done:
/* Play it safe, make sure ITER doesn't get GC'd. */
scm_remember_upto_here_1 (iter);
}
/* This is the extension_language_ops.apply_val_pretty_printer "method". */
enum ext_lang_rc
gdbscm_apply_val_pretty_printer (const struct extension_language_defn *extlang,
struct type *type,
LONGEST embedded_offset, CORE_ADDR address,
struct ui_file *stream, int recurse,
struct value *val,
const struct value_print_options *options,
const struct language_defn *language)
{
struct gdbarch *gdbarch = get_type_arch (type);
SCM exception = SCM_BOOL_F;
SCM printer = SCM_BOOL_F;
SCM val_obj = SCM_BOOL_F;
struct value *value;
enum display_hint hint;
enum ext_lang_rc result = EXT_LANG_RC_NOP;
enum string_repr_result print_result;
if (value_lazy (val))
value_fetch_lazy (val);
/* No pretty-printer support for unavailable values. */
if (!value_bytes_available (val, embedded_offset, TYPE_LENGTH (type)))
return EXT_LANG_RC_NOP;
if (!gdb_scheme_initialized)
return EXT_LANG_RC_NOP;
/* Instantiate the printer. */
value = value_from_component (val, type, embedded_offset);
val_obj = vlscm_scm_from_value (value);
if (gdbscm_is_exception (val_obj))
{
exception = val_obj;
result = EXT_LANG_RC_ERROR;
goto done;
}
printer = ppscm_find_pretty_printer (val_obj);
if (gdbscm_is_exception (printer))
{
exception = printer;
result = EXT_LANG_RC_ERROR;
goto done;
}
if (gdbscm_is_false (printer))
{
result = EXT_LANG_RC_NOP;
goto done;
}
gdb_assert (ppscm_is_pretty_printer_worker (printer));
if (val_print_check_max_depth (stream, recurse, options, language))
{
result = EXT_LANG_RC_OK;
goto done;
}
/* If we are printing a map, we want some special formatting. */
hint = ppscm_get_display_hint_enum (printer);
if (hint == HINT_ERROR)
{
/* Print the error as an exception for consistency. */
SCM hint_scm = ppscm_get_display_hint_scm (printer);
ppscm_print_pp_type_error ("Invalid display hint", hint_scm);
/* Fall through. A bad hint doesn't stop pretty-printing. */
hint = HINT_NONE;
}
/* Print the section. */
print_result = ppscm_print_string_repr (printer, hint, stream, recurse,
options, gdbarch, language);
if (print_result != STRING_REPR_ERROR)
{
ppscm_print_children (printer, hint, stream, recurse, options,
gdbarch, language,
print_result == STRING_REPR_NONE);
}
result = EXT_LANG_RC_OK;
done:
if (gdbscm_is_exception (exception))
ppscm_print_exception_unless_memory_error (exception, stream);
return result;
}
/* Initialize the Scheme pretty-printer code. */
static const scheme_function pretty_printer_functions[] =
{
{ "make-pretty-printer", 2, 0, 0,
as_a_scm_t_subr (gdbscm_make_pretty_printer),
"\
Create a <gdb:pretty-printer> object.\n\
\n\
Arguments: name lookup\n\
name: a string naming the matcher\n\
lookup: a procedure:\n\
(pretty-printer <gdb:value>) -> <gdb:pretty-printer-worker> | #f." },
{ "pretty-printer?", 1, 0, 0, as_a_scm_t_subr (gdbscm_pretty_printer_p),
"\
Return #t if the object is a <gdb:pretty-printer> object." },
{ "pretty-printer-enabled?", 1, 0, 0,
as_a_scm_t_subr (gdbscm_pretty_printer_enabled_p),
"\
Return #t if the pretty-printer is enabled." },
{ "set-pretty-printer-enabled!", 2, 0, 0,
as_a_scm_t_subr (gdbscm_set_pretty_printer_enabled_x),
"\
Set the enabled flag of the pretty-printer.\n\
Returns \"unspecified\"." },
{ "make-pretty-printer-worker", 3, 0, 0,
as_a_scm_t_subr (gdbscm_make_pretty_printer_worker),
"\
Create a <gdb:pretty-printer-worker> object.\n\
\n\
Arguments: display-hint to-string children\n\
display-hint: either #f or one of \"array\", \"map\", or \"string\"\n\
to-string: a procedure:\n\
(pretty-printer) -> string | #f | <gdb:value>\n\
children: either #f or a procedure:\n\
(pretty-printer) -> <gdb:iterator>" },
{ "pretty-printer-worker?", 1, 0, 0,
as_a_scm_t_subr (gdbscm_pretty_printer_worker_p),
"\
Return #t if the object is a <gdb:pretty-printer-worker> object." },
{ "pretty-printers", 0, 0, 0, as_a_scm_t_subr (gdbscm_pretty_printers),
"\
Return the list of global pretty-printers." },
{ "set-pretty-printers!", 1, 0, 0,
as_a_scm_t_subr (gdbscm_set_pretty_printers_x),
"\
Set the list of global pretty-printers." },
END_FUNCTIONS
};
void
gdbscm_initialize_pretty_printers (void)
{
pretty_printer_smob_tag
= gdbscm_make_smob_type (pretty_printer_smob_name,
sizeof (pretty_printer_smob));
scm_set_smob_print (pretty_printer_smob_tag,
ppscm_print_pretty_printer_smob);
pretty_printer_worker_smob_tag
= gdbscm_make_smob_type (pretty_printer_worker_smob_name,
sizeof (pretty_printer_worker_smob));
scm_set_smob_print (pretty_printer_worker_smob_tag,
ppscm_print_pretty_printer_worker_smob);
gdbscm_define_functions (pretty_printer_functions, 1);
pretty_printer_list = SCM_EOL;
pp_type_error_symbol = scm_from_latin1_symbol ("gdb:pp-type-error");
ppscm_map_string = scm_from_latin1_string ("map");
ppscm_array_string = scm_from_latin1_string ("array");
ppscm_string_string = scm_from_latin1_string ("string");
}