[PATCH 3/6] Respect `set print repeats' with Fortran arrays
Maciej W. Rozycki
macro@embecosm.com
Sat Jan 8 16:25:54 GMT 2022
On Wed, 15 Dec 2021, Andrew Burgess wrote:
> > Index: src/gdb/f-array-walker.h
> > ===================================================================
> > --- src.orig/gdb/f-array-walker.h
> > +++ src/gdb/f-array-walker.h
> > @@ -131,6 +131,18 @@ struct fortran_array_walker_base_impl
> > void finish_dimension (bool inner_p, bool last_p)
> > { /* Nothing. */ }
> >
> > + /* Called when processing dimensions of the array other than the
> > + innermost one. WALK_1 is the walker to normally call, ELT_TYPE is
> > + the type of the element being extracted, and ELT_OFF is the offset
> > + of the element from the start of array being walked, and LAST_P is
> > + true only when this is the last element that will be processed in
> > + this dimension. */
> > + void process_dimension (std::function<void (struct type *, int, bool)> walk_1,
>
> I think you should be using gdb::function_view here as the lambda only
> needs to live for the lifetime of the function call.
Changed; I wasn't aware of `gdb::function_view'.
> > Index: src/gdb/f-valprint.c
> > ===================================================================
> > --- src.orig/gdb/f-valprint.c
> > +++ src/gdb/f-valprint.c
> > @@ -21,6 +21,7 @@
> > along with this program. If not, see <http://www.gnu.org/licenses/>. */
> >
> > #include "defs.h"
> > +#include "annotate.h"
> > #include "symtab.h"
> > #include "gdbtypes.h"
> > #include "expression.h"
> > @@ -96,6 +97,15 @@ f77_get_dynamic_length_of_aggregate (str
> > * TYPE_LENGTH (check_typedef (TYPE_TARGET_TYPE (type)));
> > }
> >
> > +/* Per-dimension statistics. */
> > +
> > +struct dimension_stats
> > +{
> > + /* Element counter. */
> > + int nelts;
> > + bool elts_counted;
> > +};
>
> The comments here seem to just be repeating the variable names. Could
> we give more information. e.g. "Element counter", what elements is it
> counting, it's per dimension, but, is it counting every element? The
> number of repeated elements in a series? What does the bool mean?
> Does it mean we've counted everything? Just started counting?
It counts the total number of elements in each dimension; obviously this
needs to be done only once per dimension.
Your discussion about `index_type' with 5/6 has actually inspired me to
get rid of this active counter however and pass the total per-dimension
element count as a parameter to `start_dimension' instead. I think it
simplifies code enough to make it worth it.
> I also wondered about whether we could replace this with, or otherwise
> make use of a gdb::optional here? I see there's one place where we
> set elts_counted without appearing to set nelts, I'm guessing we're
> picking up the default value of 0 in that case?
It's not clear to me what you mean with using `gdb::optional' here and we
actually do always set `nelts' before flipping `elts_counted'. If you
think of code in `process_dimension', then we have:
if (!repeated || last_p)
{
/* ... */
if (!m_stats[dim_indx].elts_counted)
m_stats[dim_indx].nelts += nrepeats * m_stats[dim_indx + 1].nelts;
}
/* ... */
if (last_p)
m_stats[dim_indx].elts_counted = true;
there, so `nelts' does get set as both conditionals execute if `last_p',
which is when we're at the last element that concludes counting. It's now
gone along with the active counter though.
> > @@ -128,8 +141,18 @@ class fortran_array_printer_impl : publi
> > bool continue_walking (bool should_continue)
> > {
> > bool cont = should_continue && (m_elts < m_options->print_max);
> > +
> > if (!cont && should_continue)
> > - fputs_filtered ("...", m_stream);
> > + {
> > + if (m_nrepeats)
>
> The GDB style is to not treat integers as booleans, so this should be
> written as:
>
> if (m_nrepeats > 0)
I think the rule either changed or was only introduced at one point and I
missed that. Fixed now, though I chose to transcribe it literally.
> > @@ -149,22 +178,200 @@ class fortran_array_printer_impl : publi
> > fputs_filtered (")", m_stream);
> > if (!last_p)
> > fputs_filtered (" ", m_stream);
> > +
> > + m_dimension--;
> > + }
> > +
> > + /* Called when processing dimensions of the array other than the
> > + innermost one. WALK_1 is the walker to normally call, ELT_TYPE is
> > + the type of the element being extracted, and ELT_OFF is the offset
> > + of the element from the start of array being walked, and LAST_P is
> > + true only when this is the last element that will be processed in
> > + this dimension. */
> > + void process_dimension (std::function<void (struct type *, int, bool)> walk_1,
> > + struct type *elt_type, LONGEST elt_off, bool last_p)
> > + {
> > + size_t dim_indx = m_dimension - 1;
> > + struct type *elt_type_prev = m_elt_type_prev;
> > + LONGEST elt_off_prev = m_elt_off_prev;
> > + bool repeated = (m_options->repeat_count_threshold < UINT_MAX
> > + && elt_type_prev != nullptr
> > + && (m_elts + ((m_nrepeats + 1)
> > + * m_stats[dim_indx + 1].nelts)
> > + <= m_options->print_max)
> > + && dimension_contents_eq (m_val, elt_type,
> > + elt_off_prev, elt_off));
> > +
> > + if (repeated)
> > + m_nrepeats++;
> > + if (!repeated || last_p)
> > + {
> > + LONGEST nrepeats = m_nrepeats;
> > +
> > + m_nrepeats = 0;
> > + if (nrepeats >= m_options->repeat_count_threshold)
> > + {
> > + annotate_elt_rep (nrepeats + 1);
> > + fprintf_filtered (m_stream, "%p[<repeats %s times>%p]",
> > + metadata_style.style ().ptr (),
> > + plongest (nrepeats + 1),
> > + nullptr);
> > + annotate_elt_rep_end ();
> > + if (!repeated)
> > + fputs_filtered (" ", m_stream);
> > + m_elts += nrepeats * m_stats[dim_indx + 1].nelts;
> > + }
> > + else
> > + for (LONGEST i = nrepeats; i > 0; i--)
> > + walk_1 (elt_type_prev, elt_off_prev, repeated && i == 1);
> > +
> > + if (!repeated)
> > + {
> > + /* We need to specially handle the case of hitting `print_max'
> > + exactly as recursing would cause lone `(...)' to be printed.
> > + And we need to print `...' by hand if the skipped element
> > + would be the last one processed, because the subsequent call
> > + to `continue_walking' from our caller won't do that. */
> > + if (m_elts < m_options->print_max)
> > + {
> > + walk_1 (elt_type, elt_off, last_p);
> > + nrepeats++;
> > + }
> > + else if (last_p)
> > + fputs_filtered ("...", m_stream);
> > + }
> > +
> > + if (!m_stats[dim_indx].elts_counted)
> > + m_stats[dim_indx].nelts += nrepeats * m_stats[dim_indx + 1].nelts;
> > + }
> > +
> > + m_elt_type_prev = elt_type;
> > + m_elt_off_prev = elt_off;
> > +
> > + if (last_p)
> > + m_stats[dim_indx].elts_counted = true;
> > }
> >
> > /* Called to process an element of ELT_TYPE at offset ELT_OFF from the
> > start of the parent object. */
> > void process_element (struct type *elt_type, LONGEST elt_off, bool last_p)
> > {
> > - /* Extract the element value from the parent value. */
> > - struct value *e_val
> > - = value_from_component (m_val, elt_type, elt_off);
> > - common_val_print (e_val, m_stream, m_recurse, m_options, current_language);
> > - if (!last_p)
> > - fputs_filtered (", ", m_stream);
> > + size_t dim_indx = m_dimension - 1;
> > + struct type *elt_type_prev = m_elt_type_prev;
> > + LONGEST elt_off_prev = m_elt_off_prev;
> > + bool repeated = (m_options->repeat_count_threshold < UINT_MAX
> > + && elt_type_prev != nullptr
> > + && value_contents_eq (m_val, elt_off_prev, m_val, elt_off,
> > + TYPE_LENGTH (elt_type)));
> > +
> > + if (repeated)
> > + m_nrepeats++;
> > + if (!repeated || last_p)
> > + {
> > + bool printed = false;
>
> I noticed that `printed` isn't actually needed, as we could say:
>
> bool printed = m_nrepeats;
>
> so you could just use m_nrepeats instead.
Umm, no, because `process_outstanding_elements' clears `m_nrepeats' so we
need to cache the state before the call. And then how we set `printed' it
is a matter of style (I think it's clearer to the reader the way I chose
as `printed = m_nrepeats' would really be a conditional in disguise; and
it would have to be `printed = m_nrepeats != 0' to match our requirement
not to use integers as booleans).
Further inspired by the removal of the active element counter I have
decided to remove the `process_outstanding_elements' as well. In case it
wasn't obvious from code (or descriptions) it was only ever executed at
the innermost dimension, having been factored out from an earlier version
to avoid duplication.
This, and especially the hairy handling of `m_index' needed to be added
with 5/6, kept bothering me, so I chose to remove the call made from
`continue_walking' (which the hairy handling was required for) and check
for `print_max' explicitly in `process_element' similarly to how it's been
handled in `process_dimension'. This way all the outstanding element
processing needed where `print_max' is hit is done in a single place,
simplifying code further.
So the call to `process_outstanding_elements' has gone now, but I chose
to keep the code mostly as is even though there's now a local `nrepeats'
variable, because I still think the intent of this code should be clearer
to the reader this way. There are enough corner cases already to make
this piece complicated enough regardless.
> > +
> > + if (m_nrepeats)
>
> Missing '> 0' again.
Fixed.
> > + {
> > + process_outstanding_elements (elt_type, elt_off_prev);
> > + printed = true;
> > + }
> > +
> > + if (!repeated)
> > + {
> > + /* Extract the element value from the parent value. */
> > + struct value *e_val
> > + = value_from_component (m_val, elt_type, elt_off);
> > +
> > + if (printed)
> > + fputs_filtered (", ", m_stream);
> > + common_val_print (e_val, m_stream, m_recurse, m_options,
> > + current_language);
> > + }
> > + if (!last_p)
> > + fputs_filtered (", ", m_stream);
> > + }
> > +
> > + m_elt_type_prev = elt_type;
> > + m_elt_off_prev = elt_off;
> > ++m_elts;
> > +
> > + if (last_p && !m_stats[dim_indx].elts_counted)
> > + {
> > + m_stats[dim_indx].nelts = m_elts;
> > + m_stats[dim_indx].elts_counted = true;
> > + }
> > }
> >
> > private:
> > + /* Called to print outstanding repeated elements of ELT_TYPE starting
> > + at offset ELT_OFF from the start of the parent object. */
> > + void process_outstanding_elements (struct type *elt_type, LONGEST elt_off)
> > + {
> > + LONGEST nrepeats = m_nrepeats;
> > +
> > + m_nrepeats = 0;
> > + if (nrepeats >= m_options->repeat_count_threshold)
> > + {
> > + annotate_elt_rep (nrepeats + 1);
> > + fprintf_filtered (m_stream, "%p[<repeats %s times>%p]",
> > + metadata_style.style ().ptr (),
> > + plongest (nrepeats + 1),
> > + nullptr);
> > + annotate_elt_rep_end ();
> > + }
> > + else
> > + {
> > + /* Extract the element value from the parent value. */
> > + struct value *e_val = value_from_component (m_val, elt_type, elt_off);
> > +
> > + for (LONGEST i = nrepeats; i > 0; i--)
> > + {
> > + common_val_print (e_val, m_stream, m_recurse, m_options,
> > + current_language);
> > + if (i > 1)
> > + fputs_filtered (", ", m_stream);
> > + }
> > + }
> > + }
> > +
> > + /* Called to compare two VAL elements of ELT_TYPE at offsets OFFSET1
> > + and OFFSET2 each. Handle subarrays recursively, because they may
> > + have been sliced and we do not want to compare any memory contents
> > + present between the slices requested. */
> > + bool
> > + dimension_contents_eq (const struct value *val, struct type *type,
> > + LONGEST offset1, LONGEST offset2)
> > + {
> > + if (type->code () == TYPE_CODE_ARRAY
> > + && TYPE_TARGET_TYPE (type)->code () != TYPE_CODE_CHAR)
> > + {
> > + /* Extract the range, and get lower and upper bounds. */
>
> You have a space before tab here.
Fixed, good catch!
> > @@ -180,6 +387,19 @@ class fortran_array_printer_impl : publi
> > /* The print control options. Gives us the maximum number of elements to
> > print, and is passed through to each element that we print. */
> > const struct value_print_options *m_options = nullptr;
> > +
> > + /* Dimension tracker. */
> > + LONGEST m_dimension;
> > +
> > + /* Repetition tracker. */
> > + LONGEST m_nrepeats;
> > +
> > + /* Element tracker. */
> > + struct type *m_elt_type_prev;
> > + LONGEST m_elt_off_prev;
> > +
> > + /* Per-dimension stats. */
> > + std::vector<struct dimension_stats> m_stats;
>
> I'd like to see some of these comments expanded slightly to give more
> information about how they are used.
Fixed (hopefully).
Let me know if have any concerns about my explanations and/or the
updates made.
Maciej
More information about the Gdb-patches
mailing list