
In the reproducer for PR analyzer/58237 I noticed that some events that were missing locations were also missing text; for example event 3 here: | 15 | while (fgets(buf, 10, fp) != NULL) | | ~ | | | | | (2) following 'false' branch... | 'f1': event 3 | |cc1: | The root cause is that the path_summary-printing code doesn't consider ad-hoc locations when looking for reserved locations, and so fails to detect an unknown location for the case where an unknown location has been wrapped into an ad-hoc location to record a block. This patch fixes the issue by using get_pure_location, thus looking through ad-hoc wrappers, improving the result to: | 15 | while (fgets(buf, 10, fp) != NULL) | | ~ | | | | | (2) following 'false' branch... | 'f1': event 3 | |cc1: | (3): ...to here | gcc/ChangeLog: * tree-diagnostic-path.cc (path_summary::event_range::print): When testing for UNKNOWN_LOCATION, look through ad-hoc wrappers using get_pure_location.
820 lines
24 KiB
C++
820 lines
24 KiB
C++
/* Paths through the code associated with a diagnostic.
|
|
Copyright (C) 2019-2020 Free Software Foundation, Inc.
|
|
Contributed by David Malcolm <dmalcolm@redhat.com>
|
|
|
|
This file is part of GCC.
|
|
|
|
GCC 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, or (at your option) any later
|
|
version.
|
|
|
|
GCC 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 GCC; see the file COPYING3. If not see
|
|
<http://www.gnu.org/licenses/>. */
|
|
|
|
#include "config.h"
|
|
#include "system.h"
|
|
#include "coretypes.h"
|
|
#include "tree.h"
|
|
#include "diagnostic.h"
|
|
#include "tree-pretty-print.h"
|
|
#include "gimple-pretty-print.h"
|
|
#include "tree-diagnostic.h"
|
|
#include "langhooks.h"
|
|
#include "intl.h"
|
|
#include "diagnostic-path.h"
|
|
#include "json.h"
|
|
#include "gcc-rich-location.h"
|
|
#include "diagnostic-color.h"
|
|
#include "diagnostic-event-id.h"
|
|
#include "selftest.h"
|
|
#include "selftest-diagnostic.h"
|
|
|
|
/* Anonymous namespace for path-printing code. */
|
|
|
|
namespace {
|
|
|
|
/* Subclass of range_label for showing a particular event
|
|
when showing a consecutive run of events within a diagnostic_path as
|
|
labelled ranges within one gcc_rich_location. */
|
|
|
|
class path_label : public range_label
|
|
{
|
|
public:
|
|
path_label (const diagnostic_path *path, unsigned start_idx)
|
|
: m_path (path), m_start_idx (start_idx)
|
|
{}
|
|
|
|
label_text get_text (unsigned range_idx) const FINAL OVERRIDE
|
|
{
|
|
unsigned event_idx = m_start_idx + range_idx;
|
|
const diagnostic_event &event = m_path->get_event (event_idx);
|
|
|
|
/* Get the description of the event, perhaps with colorization:
|
|
normally, we don't colorize within a range_label, but this
|
|
is special-cased for diagnostic paths. */
|
|
bool colorize = pp_show_color (global_dc->printer);
|
|
label_text event_text (event.get_desc (colorize));
|
|
gcc_assert (event_text.m_buffer);
|
|
pretty_printer pp;
|
|
pp_show_color (&pp) = pp_show_color (global_dc->printer);
|
|
diagnostic_event_id_t event_id (event_idx);
|
|
pp_printf (&pp, "%@ %s", &event_id, event_text.m_buffer);
|
|
event_text.maybe_free ();
|
|
label_text result = label_text::take (xstrdup (pp_formatted_text (&pp)));
|
|
return result;
|
|
}
|
|
|
|
private:
|
|
const diagnostic_path *m_path;
|
|
unsigned m_start_idx;
|
|
};
|
|
|
|
/* Return true if E1 and E2 can be consolidated into the same run of events
|
|
when printing a diagnostic_path. */
|
|
|
|
static bool
|
|
can_consolidate_events (const diagnostic_event &e1,
|
|
const diagnostic_event &e2,
|
|
bool check_locations)
|
|
{
|
|
if (e1.get_fndecl () != e2.get_fndecl ())
|
|
return false;
|
|
|
|
if (e1.get_stack_depth () != e2.get_stack_depth ())
|
|
return false;
|
|
|
|
if (check_locations)
|
|
{
|
|
location_t loc1 = e1.get_location ();
|
|
location_t loc2 = e2.get_location ();
|
|
|
|
if (loc1 < RESERVED_LOCATION_COUNT
|
|
|| loc2 < RESERVED_LOCATION_COUNT)
|
|
return false;
|
|
|
|
/* Neither can be macro-based. */
|
|
if (linemap_location_from_macro_expansion_p (line_table, loc1))
|
|
return false;
|
|
if (linemap_location_from_macro_expansion_p (line_table, loc2))
|
|
return false;
|
|
}
|
|
|
|
/* Passed all the tests. */
|
|
return true;
|
|
}
|
|
|
|
/* A class for grouing together the events in a diagnostic_path into
|
|
ranges of events, partitioned by stack frame (i.e. by fndecl and
|
|
stack depth). */
|
|
|
|
class path_summary
|
|
{
|
|
/* A range of consecutive events within a diagnostic_path,
|
|
all with the same fndecl and stack_depth, and which are suitable
|
|
to print with a single call to diagnostic_show_locus. */
|
|
struct event_range
|
|
{
|
|
event_range (const diagnostic_path *path, unsigned start_idx,
|
|
const diagnostic_event &initial_event)
|
|
: m_path (path),
|
|
m_initial_event (initial_event),
|
|
m_fndecl (initial_event.get_fndecl ()),
|
|
m_stack_depth (initial_event.get_stack_depth ()),
|
|
m_start_idx (start_idx), m_end_idx (start_idx),
|
|
m_path_label (path, start_idx),
|
|
m_richloc (initial_event.get_location (), &m_path_label)
|
|
{}
|
|
|
|
bool maybe_add_event (const diagnostic_event &new_ev, unsigned idx,
|
|
bool check_rich_locations)
|
|
{
|
|
if (!can_consolidate_events (m_initial_event, new_ev,
|
|
check_rich_locations))
|
|
return false;
|
|
if (check_rich_locations)
|
|
if (!m_richloc.add_location_if_nearby (new_ev.get_location (),
|
|
false, &m_path_label))
|
|
return false;
|
|
m_end_idx = idx;
|
|
return true;
|
|
}
|
|
|
|
/* Print the events in this range to DC, typically as a single
|
|
call to the printer's diagnostic_show_locus. */
|
|
|
|
void print (diagnostic_context *dc)
|
|
{
|
|
location_t initial_loc = m_initial_event.get_location ();
|
|
|
|
/* Emit a span indicating the filename (and line/column) if the
|
|
line has changed relative to the last call to
|
|
diagnostic_show_locus. */
|
|
if (dc->show_caret)
|
|
{
|
|
expanded_location exploc
|
|
= linemap_client_expand_location_to_spelling_point
|
|
(initial_loc, LOCATION_ASPECT_CARET);
|
|
if (exploc.file != LOCATION_FILE (dc->last_location))
|
|
dc->start_span (dc, exploc);
|
|
}
|
|
|
|
/* If we have an UNKNOWN_LOCATION (or BUILTINS_LOCATION) as the
|
|
primary location for an event, diagnostic_show_locus won't print
|
|
anything.
|
|
|
|
In particular the label for the event won't get printed.
|
|
Fail more gracefully in this case by showing the event
|
|
index and text, at no particular location. */
|
|
if (get_pure_location (initial_loc) <= BUILTINS_LOCATION)
|
|
{
|
|
for (unsigned i = m_start_idx; i <= m_end_idx; i++)
|
|
{
|
|
const diagnostic_event &iter_event = m_path->get_event (i);
|
|
diagnostic_event_id_t event_id (i);
|
|
label_text event_text (iter_event.get_desc (true));
|
|
pretty_printer *pp = dc->printer;
|
|
pp_printf (pp, " %@: %s", &event_id, event_text.m_buffer);
|
|
pp_newline (pp);
|
|
event_text.maybe_free ();
|
|
}
|
|
return;
|
|
}
|
|
|
|
/* Call diagnostic_show_locus to show the events using labels. */
|
|
diagnostic_show_locus (dc, &m_richloc, DK_DIAGNOSTIC_PATH);
|
|
|
|
/* If we have a macro expansion, show the expansion to the user. */
|
|
if (linemap_location_from_macro_expansion_p (line_table, initial_loc))
|
|
{
|
|
gcc_assert (m_start_idx == m_end_idx);
|
|
maybe_unwind_expanded_macro_loc (dc, initial_loc);
|
|
}
|
|
}
|
|
|
|
const diagnostic_path *m_path;
|
|
const diagnostic_event &m_initial_event;
|
|
tree m_fndecl;
|
|
int m_stack_depth;
|
|
unsigned m_start_idx;
|
|
unsigned m_end_idx;
|
|
path_label m_path_label;
|
|
gcc_rich_location m_richloc;
|
|
};
|
|
|
|
public:
|
|
path_summary (const diagnostic_path &path, bool check_rich_locations);
|
|
|
|
void print (diagnostic_context *dc, bool show_depths) const;
|
|
|
|
unsigned get_num_ranges () const { return m_ranges.length (); }
|
|
|
|
private:
|
|
auto_delete_vec <event_range> m_ranges;
|
|
};
|
|
|
|
/* path_summary's ctor. */
|
|
|
|
path_summary::path_summary (const diagnostic_path &path,
|
|
bool check_rich_locations)
|
|
{
|
|
const unsigned num_events = path.num_events ();
|
|
|
|
event_range *cur_event_range = NULL;
|
|
for (unsigned idx = 0; idx < num_events; idx++)
|
|
{
|
|
const diagnostic_event &event = path.get_event (idx);
|
|
if (cur_event_range)
|
|
if (cur_event_range->maybe_add_event (event, idx, check_rich_locations))
|
|
continue;
|
|
|
|
cur_event_range = new event_range (&path, idx, event);
|
|
m_ranges.safe_push (cur_event_range);
|
|
}
|
|
}
|
|
|
|
/* Write SPACES to PP. */
|
|
|
|
static void
|
|
write_indent (pretty_printer *pp, int spaces)
|
|
{
|
|
for (int i = 0; i < spaces; i++)
|
|
pp_space (pp);
|
|
}
|
|
|
|
/* Print FNDDECL to PP, quoting it if QUOTED is true.
|
|
|
|
We can't use "%qE" here since we can't guarantee the capabilities
|
|
of PP. */
|
|
|
|
static void
|
|
print_fndecl (pretty_printer *pp, tree fndecl, bool quoted)
|
|
{
|
|
const char *n = DECL_NAME (fndecl)
|
|
? identifier_to_locale (lang_hooks.decl_printable_name (fndecl, 2))
|
|
: _("<anonymous>");
|
|
if (quoted)
|
|
pp_printf (pp, "%qs", n);
|
|
else
|
|
pp_string (pp, n);
|
|
}
|
|
|
|
/* Print this path_summary to DC, giving an overview of the interprocedural
|
|
calls and returns.
|
|
|
|
Print the event descriptions in a nested form, printing the event
|
|
descriptions within calls to diagnostic_show_locus, using labels to
|
|
show the events:
|
|
|
|
'foo' (events 1-2)
|
|
| NN |
|
|
| |
|
|
+--> 'bar' (events 3-4)
|
|
| NN |
|
|
| |
|
|
+--> 'baz' (events 5-6)
|
|
| NN |
|
|
| |
|
|
<------------ +
|
|
|
|
|
'foo' (events 7-8)
|
|
| NN |
|
|
| |
|
|
+--> 'bar' (events 9-10)
|
|
| NN |
|
|
| |
|
|
+--> 'baz' (events 11-12)
|
|
| NN |
|
|
| |
|
|
|
|
If SHOW_DEPTHS is true, append " (depth N)" to the header of each run
|
|
of events.
|
|
|
|
For events with UNKNOWN_LOCATION, print a summary of each the event. */
|
|
|
|
void
|
|
path_summary::print (diagnostic_context *dc, bool show_depths) const
|
|
{
|
|
pretty_printer *pp = dc->printer;
|
|
|
|
const int per_frame_indent = 2;
|
|
|
|
const char *const line_color = "path";
|
|
const char *start_line_color
|
|
= colorize_start (pp_show_color (pp), line_color);
|
|
const char *end_line_color = colorize_stop (pp_show_color (pp));
|
|
|
|
/* Keep track of column numbers of existing '|' characters for
|
|
stack depths we've already printed. */
|
|
const int EMPTY = -1;
|
|
const int DELETED = -2;
|
|
typedef int_hash <int, EMPTY, DELETED> vbar_hash;
|
|
hash_map <vbar_hash, int> vbar_column_for_depth;
|
|
|
|
/* Print the ranges. */
|
|
const int base_indent = 2;
|
|
int cur_indent = base_indent;
|
|
unsigned i;
|
|
event_range *range;
|
|
FOR_EACH_VEC_ELT (m_ranges, i, range)
|
|
{
|
|
write_indent (pp, cur_indent);
|
|
if (i > 0)
|
|
{
|
|
const path_summary::event_range *prev_range
|
|
= m_ranges[i - 1];
|
|
if (range->m_stack_depth > prev_range->m_stack_depth)
|
|
{
|
|
/* Show pushed stack frame(s). */
|
|
const char *push_prefix = "+--> ";
|
|
pp_string (pp, start_line_color);
|
|
pp_string (pp, push_prefix);
|
|
pp_string (pp, end_line_color);
|
|
cur_indent += strlen (push_prefix);
|
|
}
|
|
}
|
|
if (range->m_fndecl)
|
|
{
|
|
print_fndecl (pp, range->m_fndecl, true);
|
|
pp_string (pp, ": ");
|
|
}
|
|
if (range->m_start_idx == range->m_end_idx)
|
|
pp_printf (pp, "event %i",
|
|
range->m_start_idx + 1);
|
|
else
|
|
pp_printf (pp, "events %i-%i",
|
|
range->m_start_idx + 1, range->m_end_idx + 1);
|
|
if (show_depths)
|
|
pp_printf (pp, " (depth %i)", range->m_stack_depth);
|
|
pp_newline (pp);
|
|
|
|
/* Print a run of events. */
|
|
{
|
|
write_indent (pp, cur_indent + per_frame_indent);
|
|
pp_string (pp, start_line_color);
|
|
pp_string (pp, "|");
|
|
pp_string (pp, end_line_color);
|
|
pp_newline (pp);
|
|
|
|
char *saved_prefix = pp_take_prefix (pp);
|
|
char *prefix;
|
|
{
|
|
pretty_printer tmp_pp;
|
|
write_indent (&tmp_pp, cur_indent + per_frame_indent);
|
|
pp_string (&tmp_pp, start_line_color);
|
|
pp_string (&tmp_pp, "|");
|
|
pp_string (&tmp_pp, end_line_color);
|
|
prefix = xstrdup (pp_formatted_text (&tmp_pp));
|
|
}
|
|
pp_set_prefix (pp, prefix);
|
|
pp_prefixing_rule (pp) = DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE;
|
|
range->print (dc);
|
|
pp_set_prefix (pp, saved_prefix);
|
|
|
|
write_indent (pp, cur_indent + per_frame_indent);
|
|
pp_string (pp, start_line_color);
|
|
pp_string (pp, "|");
|
|
pp_string (pp, end_line_color);
|
|
pp_newline (pp);
|
|
}
|
|
|
|
if (i < m_ranges.length () - 1)
|
|
{
|
|
const path_summary::event_range *next_range
|
|
= m_ranges[i + 1];
|
|
|
|
if (range->m_stack_depth > next_range->m_stack_depth)
|
|
{
|
|
if (vbar_column_for_depth.get (next_range->m_stack_depth))
|
|
{
|
|
/* Show returning from stack frame(s), by printing
|
|
something like:
|
|
" |\n"
|
|
" <------------ +\n"
|
|
" |\n". */
|
|
int vbar_for_next_frame
|
|
= *vbar_column_for_depth.get (next_range->m_stack_depth);
|
|
|
|
int indent_for_next_frame
|
|
= vbar_for_next_frame - per_frame_indent;
|
|
write_indent (pp, vbar_for_next_frame);
|
|
pp_string (pp, start_line_color);
|
|
pp_character (pp, '<');
|
|
for (int i = indent_for_next_frame + per_frame_indent;
|
|
i < cur_indent + per_frame_indent - 1; i++)
|
|
pp_character (pp, '-');
|
|
pp_character (pp, '+');
|
|
pp_string (pp, end_line_color);
|
|
pp_newline (pp);
|
|
cur_indent = indent_for_next_frame;
|
|
|
|
write_indent (pp, vbar_for_next_frame);
|
|
pp_string (pp, start_line_color);
|
|
pp_printf (pp, "|");
|
|
pp_string (pp, end_line_color);
|
|
pp_newline (pp);
|
|
}
|
|
else
|
|
{
|
|
/* Handle disjoint paths (e.g. a callback at some later
|
|
time). */
|
|
cur_indent = base_indent;
|
|
}
|
|
}
|
|
else if (range->m_stack_depth < next_range->m_stack_depth)
|
|
{
|
|
/* Prepare to show pushed stack frame. */
|
|
gcc_assert (range->m_stack_depth != EMPTY);
|
|
gcc_assert (range->m_stack_depth != DELETED);
|
|
vbar_column_for_depth.put (range->m_stack_depth,
|
|
cur_indent + per_frame_indent);
|
|
cur_indent += per_frame_indent;
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
} /* end of anonymous namespace for path-printing code. */
|
|
|
|
/* Print PATH to CONTEXT, according to CONTEXT's path_format. */
|
|
|
|
void
|
|
default_tree_diagnostic_path_printer (diagnostic_context *context,
|
|
const diagnostic_path *path)
|
|
{
|
|
gcc_assert (path);
|
|
|
|
const unsigned num_events = path->num_events ();
|
|
|
|
switch (context->path_format)
|
|
{
|
|
case DPF_NONE:
|
|
/* Do nothing. */
|
|
return;
|
|
|
|
case DPF_SEPARATE_EVENTS:
|
|
{
|
|
/* A note per event. */
|
|
for (unsigned i = 0; i < num_events; i++)
|
|
{
|
|
const diagnostic_event &event = path->get_event (i);
|
|
label_text event_text (event.get_desc (false));
|
|
gcc_assert (event_text.m_buffer);
|
|
diagnostic_event_id_t event_id (i);
|
|
inform (event.get_location (),
|
|
"%@ %s", &event_id, event_text.m_buffer);
|
|
event_text.maybe_free ();
|
|
}
|
|
}
|
|
break;
|
|
|
|
case DPF_INLINE_EVENTS:
|
|
{
|
|
/* Consolidate related events. */
|
|
path_summary summary (*path, true);
|
|
char *saved_prefix = pp_take_prefix (context->printer);
|
|
pp_set_prefix (context->printer, NULL);
|
|
summary.print (context, context->show_path_depths);
|
|
pp_flush (context->printer);
|
|
pp_set_prefix (context->printer, saved_prefix);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* This has to be here, rather than diagnostic-format-json.cc,
|
|
since diagnostic-format-json.o is within OBJS-libcommon and thus
|
|
doesn't have access to trees (for m_fndecl). */
|
|
|
|
json::value *
|
|
default_tree_make_json_for_path (diagnostic_context *,
|
|
const diagnostic_path *path)
|
|
{
|
|
json::array *path_array = new json::array ();
|
|
for (unsigned i = 0; i < path->num_events (); i++)
|
|
{
|
|
const diagnostic_event &event = path->get_event (i);
|
|
|
|
json::object *event_obj = new json::object ();
|
|
if (event.get_location ())
|
|
event_obj->set ("location",
|
|
json_from_expanded_location (event.get_location ()));
|
|
label_text event_text (event.get_desc (false));
|
|
event_obj->set ("description", new json::string (event_text.m_buffer));
|
|
event_text.maybe_free ();
|
|
if (tree fndecl = event.get_fndecl ())
|
|
{
|
|
const char *function
|
|
= identifier_to_locale (lang_hooks.decl_printable_name (fndecl, 2));
|
|
event_obj->set ("function", new json::string (function));
|
|
}
|
|
event_obj->set ("depth",
|
|
new json::integer_number (event.get_stack_depth ()));
|
|
path_array->append (event_obj);
|
|
}
|
|
return path_array;
|
|
}
|
|
|
|
#if CHECKING_P
|
|
|
|
namespace selftest {
|
|
|
|
/* A subclass of simple_diagnostic_path that adds member functions
|
|
for adding test events. */
|
|
|
|
class test_diagnostic_path : public simple_diagnostic_path
|
|
{
|
|
public:
|
|
test_diagnostic_path (pretty_printer *event_pp)
|
|
: simple_diagnostic_path (event_pp)
|
|
{
|
|
}
|
|
|
|
void add_entry (tree fndecl, int stack_depth)
|
|
{
|
|
add_event (UNKNOWN_LOCATION, fndecl, stack_depth,
|
|
"entering %qE", fndecl);
|
|
}
|
|
|
|
void add_return (tree fndecl, int stack_depth)
|
|
{
|
|
add_event (UNKNOWN_LOCATION, fndecl, stack_depth,
|
|
"returning to %qE", fndecl);
|
|
}
|
|
|
|
void add_call (tree caller, int caller_stack_depth, tree callee)
|
|
{
|
|
add_event (UNKNOWN_LOCATION, caller, caller_stack_depth,
|
|
"calling %qE", callee);
|
|
add_entry (callee, caller_stack_depth + 1);
|
|
}
|
|
};
|
|
|
|
/* Verify that empty paths are handled gracefully. */
|
|
|
|
static void
|
|
test_empty_path (pretty_printer *event_pp)
|
|
{
|
|
test_diagnostic_path path (event_pp);
|
|
ASSERT_FALSE (path.interprocedural_p ());
|
|
|
|
path_summary summary (path, false);
|
|
ASSERT_EQ (summary.get_num_ranges (), 0);
|
|
|
|
test_diagnostic_context dc;
|
|
summary.print (&dc, true);
|
|
ASSERT_STREQ ("",
|
|
pp_formatted_text (dc.printer));
|
|
}
|
|
|
|
/* Verify that print_path_summary works on a purely intraprocedural path. */
|
|
|
|
static void
|
|
test_intraprocedural_path (pretty_printer *event_pp)
|
|
{
|
|
tree fntype_void_void
|
|
= build_function_type_array (void_type_node, 0, NULL);
|
|
tree fndecl_foo = build_fn_decl ("foo", fntype_void_void);
|
|
|
|
test_diagnostic_path path (event_pp);
|
|
path.add_event (UNKNOWN_LOCATION, fndecl_foo, 0, "first %qs", "free");
|
|
path.add_event (UNKNOWN_LOCATION, fndecl_foo, 0, "double %qs", "free");
|
|
|
|
ASSERT_FALSE (path.interprocedural_p ());
|
|
|
|
path_summary summary (path, false);
|
|
ASSERT_EQ (summary.get_num_ranges (), 1);
|
|
|
|
test_diagnostic_context dc;
|
|
summary.print (&dc, true);
|
|
ASSERT_STREQ (" `foo': events 1-2 (depth 0)\n"
|
|
" |\n"
|
|
" | (1): first `free'\n"
|
|
" | (2): double `free'\n"
|
|
" |\n",
|
|
pp_formatted_text (dc.printer));
|
|
}
|
|
|
|
/* Verify that print_path_summary works on an interprocedural path. */
|
|
|
|
static void
|
|
test_interprocedural_path_1 (pretty_printer *event_pp)
|
|
{
|
|
/* Build fndecls. The types aren't quite right, but that
|
|
doesn't matter for the purposes of this test. */
|
|
tree fntype_void_void
|
|
= build_function_type_array (void_type_node, 0, NULL);
|
|
tree fndecl_test = build_fn_decl ("test", fntype_void_void);
|
|
tree fndecl_make_boxed_int
|
|
= build_fn_decl ("make_boxed_int", fntype_void_void);
|
|
tree fndecl_wrapped_malloc
|
|
= build_fn_decl ("wrapped_malloc", fntype_void_void);
|
|
tree fndecl_free_boxed_int
|
|
= build_fn_decl ("free_boxed_int", fntype_void_void);
|
|
tree fndecl_wrapped_free
|
|
= build_fn_decl ("wrapped_free", fntype_void_void);
|
|
|
|
test_diagnostic_path path (event_pp);
|
|
path.add_entry (fndecl_test, 0);
|
|
path.add_call (fndecl_test, 0, fndecl_make_boxed_int);
|
|
path.add_call (fndecl_make_boxed_int, 1, fndecl_wrapped_malloc);
|
|
path.add_event (UNKNOWN_LOCATION, fndecl_wrapped_malloc, 2, "calling malloc");
|
|
path.add_return (fndecl_test, 0);
|
|
path.add_call (fndecl_test, 0, fndecl_free_boxed_int);
|
|
path.add_call (fndecl_free_boxed_int, 1, fndecl_wrapped_free);
|
|
path.add_event (UNKNOWN_LOCATION, fndecl_wrapped_free, 2, "calling free");
|
|
path.add_return (fndecl_test, 0);
|
|
path.add_call (fndecl_test, 0, fndecl_free_boxed_int);
|
|
path.add_call (fndecl_free_boxed_int, 1, fndecl_wrapped_free);
|
|
path.add_event (UNKNOWN_LOCATION, fndecl_wrapped_free, 2, "calling free");
|
|
ASSERT_EQ (path.num_events (), 18);
|
|
|
|
ASSERT_TRUE (path.interprocedural_p ());
|
|
|
|
path_summary summary (path, false);
|
|
ASSERT_EQ (summary.get_num_ranges (), 9);
|
|
|
|
test_diagnostic_context dc;
|
|
summary.print (&dc, true);
|
|
ASSERT_STREQ
|
|
(" `test': events 1-2 (depth 0)\n"
|
|
" |\n"
|
|
" | (1): entering `test'\n"
|
|
" | (2): calling `make_boxed_int'\n"
|
|
" |\n"
|
|
" +--> `make_boxed_int': events 3-4 (depth 1)\n"
|
|
" |\n"
|
|
" | (3): entering `make_boxed_int'\n"
|
|
" | (4): calling `wrapped_malloc'\n"
|
|
" |\n"
|
|
" +--> `wrapped_malloc': events 5-6 (depth 2)\n"
|
|
" |\n"
|
|
" | (5): entering `wrapped_malloc'\n"
|
|
" | (6): calling malloc\n"
|
|
" |\n"
|
|
" <-------------+\n"
|
|
" |\n"
|
|
" `test': events 7-8 (depth 0)\n"
|
|
" |\n"
|
|
" | (7): returning to `test'\n"
|
|
" | (8): calling `free_boxed_int'\n"
|
|
" |\n"
|
|
" +--> `free_boxed_int': events 9-10 (depth 1)\n"
|
|
" |\n"
|
|
" | (9): entering `free_boxed_int'\n"
|
|
" | (10): calling `wrapped_free'\n"
|
|
" |\n"
|
|
" +--> `wrapped_free': events 11-12 (depth 2)\n"
|
|
" |\n"
|
|
" | (11): entering `wrapped_free'\n"
|
|
" | (12): calling free\n"
|
|
" |\n"
|
|
" <-------------+\n"
|
|
" |\n"
|
|
" `test': events 13-14 (depth 0)\n"
|
|
" |\n"
|
|
" | (13): returning to `test'\n"
|
|
" | (14): calling `free_boxed_int'\n"
|
|
" |\n"
|
|
" +--> `free_boxed_int': events 15-16 (depth 1)\n"
|
|
" |\n"
|
|
" | (15): entering `free_boxed_int'\n"
|
|
" | (16): calling `wrapped_free'\n"
|
|
" |\n"
|
|
" +--> `wrapped_free': events 17-18 (depth 2)\n"
|
|
" |\n"
|
|
" | (17): entering `wrapped_free'\n"
|
|
" | (18): calling free\n"
|
|
" |\n",
|
|
pp_formatted_text (dc.printer));
|
|
}
|
|
|
|
/* Example where we pop the stack to an intermediate frame, rather than the
|
|
initial one. */
|
|
|
|
static void
|
|
test_interprocedural_path_2 (pretty_printer *event_pp)
|
|
{
|
|
/* Build fndecls. The types aren't quite right, but that
|
|
doesn't matter for the purposes of this test. */
|
|
tree fntype_void_void
|
|
= build_function_type_array (void_type_node, 0, NULL);
|
|
tree fndecl_foo = build_fn_decl ("foo", fntype_void_void);
|
|
tree fndecl_bar = build_fn_decl ("bar", fntype_void_void);
|
|
tree fndecl_baz = build_fn_decl ("baz", fntype_void_void);
|
|
|
|
test_diagnostic_path path (event_pp);
|
|
path.add_entry (fndecl_foo, 0);
|
|
path.add_call (fndecl_foo, 0, fndecl_bar);
|
|
path.add_call (fndecl_bar, 1, fndecl_baz);
|
|
path.add_return (fndecl_bar, 1);
|
|
path.add_call (fndecl_bar, 1, fndecl_baz);
|
|
ASSERT_EQ (path.num_events (), 8);
|
|
|
|
ASSERT_TRUE (path.interprocedural_p ());
|
|
|
|
path_summary summary (path, false);
|
|
ASSERT_EQ (summary.get_num_ranges (), 5);
|
|
|
|
test_diagnostic_context dc;
|
|
summary.print (&dc, true);
|
|
ASSERT_STREQ
|
|
(" `foo': events 1-2 (depth 0)\n"
|
|
" |\n"
|
|
" | (1): entering `foo'\n"
|
|
" | (2): calling `bar'\n"
|
|
" |\n"
|
|
" +--> `bar': events 3-4 (depth 1)\n"
|
|
" |\n"
|
|
" | (3): entering `bar'\n"
|
|
" | (4): calling `baz'\n"
|
|
" |\n"
|
|
" +--> `baz': event 5 (depth 2)\n"
|
|
" |\n"
|
|
" | (5): entering `baz'\n"
|
|
" |\n"
|
|
" <------+\n"
|
|
" |\n"
|
|
" `bar': events 6-7 (depth 1)\n"
|
|
" |\n"
|
|
" | (6): returning to `bar'\n"
|
|
" | (7): calling `baz'\n"
|
|
" |\n"
|
|
" +--> `baz': event 8 (depth 2)\n"
|
|
" |\n"
|
|
" | (8): entering `baz'\n"
|
|
" |\n",
|
|
pp_formatted_text (dc.printer));
|
|
}
|
|
|
|
/* Verify that print_path_summary is sane in the face of a recursive
|
|
diagnostic_path. */
|
|
|
|
static void
|
|
test_recursion (pretty_printer *event_pp)
|
|
{
|
|
tree fntype_void_void
|
|
= build_function_type_array (void_type_node, 0, NULL);
|
|
tree fndecl_factorial = build_fn_decl ("factorial", fntype_void_void);
|
|
|
|
test_diagnostic_path path (event_pp);
|
|
path.add_entry (fndecl_factorial, 0);
|
|
for (int depth = 0; depth < 3; depth++)
|
|
path.add_call (fndecl_factorial, depth, fndecl_factorial);
|
|
ASSERT_EQ (path.num_events (), 7);
|
|
|
|
ASSERT_TRUE (path.interprocedural_p ());
|
|
|
|
path_summary summary (path, false);
|
|
ASSERT_EQ (summary.get_num_ranges (), 4);
|
|
|
|
test_diagnostic_context dc;
|
|
summary.print (&dc, true);
|
|
ASSERT_STREQ
|
|
(" `factorial': events 1-2 (depth 0)\n"
|
|
" |\n"
|
|
" | (1): entering `factorial'\n"
|
|
" | (2): calling `factorial'\n"
|
|
" |\n"
|
|
" +--> `factorial': events 3-4 (depth 1)\n"
|
|
" |\n"
|
|
" | (3): entering `factorial'\n"
|
|
" | (4): calling `factorial'\n"
|
|
" |\n"
|
|
" +--> `factorial': events 5-6 (depth 2)\n"
|
|
" |\n"
|
|
" | (5): entering `factorial'\n"
|
|
" | (6): calling `factorial'\n"
|
|
" |\n"
|
|
" +--> `factorial': event 7 (depth 3)\n"
|
|
" |\n"
|
|
" | (7): entering `factorial'\n"
|
|
" |\n",
|
|
pp_formatted_text (dc.printer));
|
|
}
|
|
|
|
/* Run all of the selftests within this file. */
|
|
|
|
void
|
|
tree_diagnostic_path_cc_tests ()
|
|
{
|
|
auto_fix_quotes fix_quotes;
|
|
pretty_printer *event_pp = global_dc->printer->clone ();
|
|
pp_show_color (event_pp) = 0;
|
|
test_empty_path (event_pp);
|
|
test_intraprocedural_path (event_pp);
|
|
test_interprocedural_path_1 (event_pp);
|
|
test_interprocedural_path_2 (event_pp);
|
|
test_recursion (event_pp);
|
|
delete event_pp;
|
|
}
|
|
|
|
} // namespace selftest
|
|
|
|
#endif /* #if CHECKING_P */
|