analyzer: fix up paths for inlining (PR analyzer/105962)

-fanalyzer runs late compared to other code analysis tools, in that in
runs on the partially-optimized gimple-ssa representation.  I chose this
point to run in the hope of easy integration with LTO.

As PR analyzer/105962 notes, this means that function inlining can occur
before the -fanalyzer "sees" the user's code.  For example given:

void foo (void *p)
{
  __builtin_free (p);
}

void bar (void *q)
{
  foo (q);
  foo (q);
}

Below -O2, -fanalyzer shows the calls and returns:

inline-1.c: In function ‘foo’:
inline-1.c:3:3: warning: double-‘free’ of ‘p’ [CWE-415] [-Wanalyzer-double-free]
    3 |   __builtin_free (p);
      |   ^~~~~~~~~~~~~~~~~~
  ‘bar’: events 1-2
    |
    |    6 | void bar (void *q)
    |      |      ^~~
    |      |      |
    |      |      (1) entry to ‘bar’
    |    7 | {
    |    8 |   foo (q);
    |      |   ~~~~~~~
    |      |   |
    |      |   (2) calling ‘foo’ from ‘bar’
    |
    +--> ‘foo’: events 3-4
           |
           |    1 | void foo (void *p)
           |      |      ^~~
           |      |      |
           |      |      (3) entry to ‘foo’
           |    2 | {
           |    3 |   __builtin_free (p);
           |      |   ~~~~~~~~~~~~~~~~~~
           |      |   |
           |      |   (4) first ‘free’ here
           |
    <------+
    |
  ‘bar’: events 5-6
    |
    |    8 |   foo (q);
    |      |   ^~~~~~~
    |      |   |
    |      |   (5) returning to ‘bar’ from ‘foo’
    |    9 |   foo (q);
    |      |   ~~~~~~~
    |      |   |
    |      |   (6) passing freed pointer ‘q’ in call to ‘foo’ from ‘bar’
    |
    +--> ‘foo’: events 7-8
           |
           |    1 | void foo (void *p)
           |      |      ^~~
           |      |      |
           |      |      (7) entry to ‘foo’
           |    2 | {
           |    3 |   __builtin_free (p);
           |      |   ~~~~~~~~~~~~~~~~~~
           |      |   |
           |      |   (8) second ‘free’ here; first ‘free’ was at (4)
           |

but at -O2, -fanalyzer "sees" this gimple:

void bar (void * q)
{
  <bb 2> [local count: 1073741824]:
  __builtin_free (q_2(D));
  __builtin_free (q_2(D));
  return;
}

where "foo" has been inlined away, leading to this unhelpful output:

In function ‘foo’,
    inlined from ‘bar’ at inline-1.c:9:3:
inline-1.c:3:3: warning: double-‘free’ of ‘q’ [CWE-415] [-Wanalyzer-double-free]
    3 |   __builtin_free (p);
      |   ^~~~~~~~~~~~~~~~~~
  ‘bar’: events 1-2
    |
    |    3 |   __builtin_free (p);
    |      |   ^~~~~~~~~~~~~~~~~~
    |      |   |
    |      |   (1) first ‘free’ here
    |      |   (2) second ‘free’ here; first ‘free’ was at (1)

where the stack frame information in the execution path suggests that these
events are happening in "bar", in the top stack frame.

This is what the analyzer sees, but I find it hard to decipher such
output.  Hence, as a workaround for the fact that -fanalyzer runs so
late, this patch attempts to reconstruct the "true" stack frame
information, and to inject events showing inline calls, based on the
inlining chain information recorded in the location_t values for the events.

Doing so leads to this output at -O2 on the above example (with
-fdiagnostics-show-path-depths):

In function ‘foo’,
    inlined from ‘bar’ at inline-1.c:9:3:
inline-1.c:3:3: warning: double-‘free’ of ‘q’ [CWE-415] [-Wanalyzer-double-free]
    3 |   __builtin_free (p);
      |   ^~~~~~~~~~~~~~~~~~
  ‘bar’: events 1-2 (depth 1)
    |
    |    6 | void bar (void *q)
    |      |      ^~~
    |      |      |
    |      |      (1) entry to ‘bar’
    |    7 | {
    |    8 |   foo (q);
    |      |   ~
    |      |   |
    |      |   (2) inlined call to ‘foo’ from ‘bar’
    |
    +--> ‘foo’: event 3 (depth 2)
           |
           |    3 |   __builtin_free (p);
           |      |   ^~~~~~~~~~~~~~~~~~
           |      |   |
           |      |   (3) first ‘free’ here
           |
    <------+
    |
  ‘bar’: event 4 (depth 1)
    |
    |    9 |   foo (q);
    |      |   ^
    |      |   |
    |      |   (4) inlined call to ‘foo’ from ‘bar’
    |
    +--> ‘foo’: event 5 (depth 2)
           |
           |    3 |   __builtin_free (p);
           |      |   ^~~~~~~~~~~~~~~~~~
           |      |   |
           |      |   (5) second ‘free’ here; first ‘free’ was at (3)
           |

reconstructing the calls and returns.

The patch also adds a new option, -fno-analyzer-undo-inlining, which can
be used to disable this reconstruction, restoring the output listed
above (this time with -fdiagnostics-show-path-depths):

In function ‘foo’,
    inlined from ‘bar’ at inline-1.c:9:3:
inline-1.c:3:3: warning: double-‘free’ of ‘q’ [CWE-415] [-Wanalyzer-double-free]
    3 |   __builtin_free (p);
      |   ^~~~~~~~~~~~~~~~~~
  ‘bar’: events 1-2 (depth 1)
    |
    |    3 |   __builtin_free (p);
    |      |   ^~~~~~~~~~~~~~~~~~
    |      |   |
    |      |   (1) first ‘free’ here
    |      |   (2) second ‘free’ here; first ‘free’ was at (1)
    |

gcc/analyzer/ChangeLog:
	PR analyzer/105962
	* analyzer.opt (fanalyzer-undo-inlining): New option.
	* checker-path.cc: Include "diagnostic-core.h" and
	"inlining-iterator.h".
	(event_kind_to_string): Handle EK_INLINED_CALL.
	(class inlining_info): New class.
	(checker_event::checker_event): Move here from checker-path.h.
	Store original fndecl and depth, and calculate effective fndecl
	and depth based on inlining information.
	(checker_event::dump): Emit original depth as well as effective
	depth when they differ; likewise for fndecl.
	(region_creation_event::get_desc): Use m_effective_fndecl.
	(inlined_call_event::get_desc): New.
	(inlined_call_event::get_meaning): New.
	(checker_path::inject_any_inlined_call_events): New.
	* checker-path.h (enum event_kind): Add EK_INLINED_CALL.
	(checker_event::checker_event): Make protected, and move
	definition to checker-path.cc.
	(checker_event::get_fndecl): Use effective fndecl.
	(checker_event::get_stack_depth): Use effective stack depth.
	(checker_event::get_logical_location): Use effective stack depth.
	(checker_event::get_original_stack_depth): New.
	(checker_event::m_fndecl): Rename to...
	(checker_event::m_original_fndecl): ...this.
	(checker_event::m_depth): Rename to...
	(checker_event::m_original_depth): ...this.
	(checker_event::m_effective_fndecl): New field.
	(checker_event::m_effective_depth): New field.
	(class inlined_call_event): New checker_event subclass.
	(checker_path::inject_any_inlined_call_events): New decl.
	* diagnostic-manager.cc: Include "inlining-iterator.h".
	(diagnostic_manager::emit_saved_diagnostic): Call
	checker_path::inject_any_inlined_call_events.
	(diagnostic_manager::prune_for_sm_diagnostic): Handle
	EK_INLINED_CALL.
	* engine.cc (tainted_args_function_custom_event::get_desc): Use
	effective fndecl.
	* inlining-iterator.h: New file.

gcc/testsuite/ChangeLog:
	PR analyzer/105962
	* gcc.dg/analyzer/inlining-1-multiline.c: New test.
	* gcc.dg/analyzer/inlining-1-no-undo.c: New test.
	* gcc.dg/analyzer/inlining-1.c: New test.
	* gcc.dg/analyzer/inlining-2-multiline.c: New test.
	* gcc.dg/analyzer/inlining-2.c: New test.
	* gcc.dg/analyzer/inlining-3-multiline.c: New test.
	* gcc.dg/analyzer/inlining-3.c: New test.
	* gcc.dg/analyzer/inlining-4-multiline.c: New test.
	* gcc.dg/analyzer/inlining-4.c: New test.
	* gcc.dg/analyzer/inlining-5-multiline.c: New test.
	* gcc.dg/analyzer/inlining-5.c: New test.
	* gcc.dg/analyzer/inlining-6-multiline.c: New test.
	* gcc.dg/analyzer/inlining-6.c: New test.
	* gcc.dg/analyzer/inlining-7-multiline.c: New test.
	* gcc.dg/analyzer/inlining-7.c: New test.

gcc/ChangeLog:
	PR analyzer/105962
	* doc/invoke.texi: Add -fno-analyzer-undo-inlining.
	* tree-diagnostic-path.cc (default_tree_diagnostic_path_printer):
	Extend -fdiagnostics-path-format=separate-events so that with
	-fdiagnostics-show-path-depths it prints fndecls as well as stack
	depths.

Signed-off-by: David Malcolm <dmalcolm@redhat.com>
This commit is contained in:
David Malcolm 2022-06-15 17:44:14 -04:00
parent b06b84dbca
commit 63c0731994
23 changed files with 1165 additions and 22 deletions

View file

@ -206,6 +206,10 @@ fanalyzer-call-summaries
Common Var(flag_analyzer_call_summaries) Init(0)
Approximate the effect of function calls to simplify analysis.
fanalyzer-undo-inlining
Common Var(flag_analyzer_undo_inlining) Init(1)
Try to reconstruct function calls and returns after inlining.
fanalyzer-verbose-edges
Common Var(flag_analyzer_verbose_edges) Init(0)
Emit more verbose descriptions of control flow in diagnostics.

View file

@ -25,6 +25,7 @@ along with GCC; see the file COPYING3. If not see
#include "function.h"
#include "basic-block.h"
#include "gimple.h"
#include "diagnostic-core.h"
#include "gimple-pretty-print.h"
#include "fold-const.h"
#include "function.h"
@ -54,6 +55,7 @@ along with GCC; see the file COPYING3. If not see
#include "analyzer/program-state.h"
#include "analyzer/checker-path.h"
#include "gimple-iterator.h"
#include "inlining-iterator.h"
#include "analyzer/supergraph.h"
#include "analyzer/pending-diagnostic.h"
#include "analyzer/diagnostic-manager.h"
@ -99,6 +101,8 @@ event_kind_to_string (enum event_kind ek)
return "EK_START_CONSOLIDATED_CFG_EDGES";
case EK_END_CONSOLIDATED_CFG_EDGES:
return "EK_END_CONSOLIDATED_CFG_EDGES";
case EK_INLINED_CALL:
return "EK_INLINED_CALL";
case EK_SETJMP:
return "EK_SETJMP";
case EK_REWIND_FROM_LONGJMP:
@ -110,8 +114,71 @@ event_kind_to_string (enum event_kind ek)
}
}
/* A class for fixing up fndecls and stack depths in checker_event, based
on inlining records.
The early inliner runs before the analyzer, which can lead to confusing
output.
Tne base fndecl and depth within a checker_event are from call strings
in program_points, which reflect the call strings after inlining.
This class lets us offset the depth and fix up the reported fndecl and
stack depth to better reflect the user's original code. */
class inlining_info
{
public:
inlining_info (location_t loc)
{
inlining_iterator iter (loc);
m_inner_fndecl = iter.get_fndecl ();
int num_frames = 0;
while (!iter.done_p ())
{
m_outer_fndecl = iter.get_fndecl ();
num_frames++;
iter.next ();
}
if (num_frames > 1)
m_extra_frames = num_frames - 1;
else
m_extra_frames = 0;
}
tree get_inner_fndecl () const { return m_inner_fndecl; }
int get_extra_frames () const { return m_extra_frames; }
private:
tree m_outer_fndecl;
tree m_inner_fndecl;
int m_extra_frames;
};
/* class checker_event : public diagnostic_event. */
/* checker_event's ctor. */
checker_event::checker_event (enum event_kind kind,
location_t loc, tree fndecl, int depth)
: m_kind (kind), m_loc (loc),
m_original_fndecl (fndecl), m_effective_fndecl (fndecl),
m_original_depth (depth), m_effective_depth (depth),
m_pending_diagnostic (NULL), m_emission_id (),
m_logical_loc (fndecl)
{
/* Update effective fndecl and depth if inlining has been recorded. */
if (flag_analyzer_undo_inlining)
{
inlining_info info (loc);
if (info.get_inner_fndecl ())
{
m_effective_fndecl = info.get_inner_fndecl ();
m_effective_depth += info.get_extra_frames ();
m_logical_loc = tree_logical_location (m_effective_fndecl);
}
}
}
/* No-op implementation of diagnostic_event::get_meaning vfunc for
checker_event: checker events have no meaning by default. */
@ -127,11 +194,21 @@ void
checker_event::dump (pretty_printer *pp) const
{
label_text event_desc (get_desc (false));
pp_printf (pp, "\"%s\" (depth %i, m_loc=%x)",
event_desc.m_buffer,
get_stack_depth (),
get_location ());
pp_printf (pp, "\"%s\" (depth %i",
event_desc.m_buffer, m_effective_depth);
event_desc.maybe_free ();
if (m_effective_depth != m_original_depth)
pp_printf (pp, " corrected from %i",
m_original_depth);
if (m_effective_fndecl)
{
pp_printf (pp, ", fndecl %qE", m_effective_fndecl);
if (m_effective_fndecl != m_original_fndecl)
pp_printf (pp, " corrected from %qE", m_original_fndecl);
}
pp_printf (pp, ", m_loc=%x)",
get_location ());
}
/* Hook for being notified when this event has its final id EMISSION_ID
@ -248,7 +325,7 @@ region_creation_event::get_desc (bool) const
label_text
function_entry_event::get_desc (bool can_colorize) const
{
return make_label_text (can_colorize, "entry to %qE", m_fndecl);
return make_label_text (can_colorize, "entry to %qE", m_effective_fndecl);
}
/* Implementation of diagnostic_event::get_meaning vfunc for
@ -867,6 +944,26 @@ start_consolidated_cfg_edges_event::get_meaning () const
(m_edge_sense ? PROPERTY_true : PROPERTY_false));
}
/* class inlined_call_event : public checker_event. */
label_text
inlined_call_event::get_desc (bool can_colorize) const
{
return make_label_text (can_colorize,
"inlined call to %qE from %qE",
m_apparent_callee_fndecl,
m_apparent_caller_fndecl);
}
/* Implementation of diagnostic_event::get_meaning vfunc for
reconstructed inlined function calls. */
diagnostic_event::meaning
inlined_call_event::get_meaning () const
{
return meaning (VERB_call, NOUN_function);
}
/* class setjmp_event : public checker_event. */
/* Implementation of diagnostic_event::get_desc vfunc for
@ -1180,6 +1277,163 @@ checker_path::cfg_edge_pair_at_p (unsigned idx) const
&& m_events[idx + 1]->m_kind == EK_END_CFG_EDGE);
}
/* Consider a call from "outer" to "middle" which calls "inner",
where "inner" and "middle" have been inlined into "outer".
We expect the stmt locations for the inlined stmts to have a
chain like:
[{fndecl: inner},
{fndecl: middle, callsite: within middle to inner},
{fndecl: outer, callsite: without outer to middle}]
The location for the stmt will already be fixed up to reflect
the two extra frames, so that we have e.g. this as input
(for gcc.dg/analyzer/inlining-4.c):
before[0]:
EK_FUNCTION_ENTRY "entry to outer"
(depth 1, fndecl outer, m_loc=511c4)
before[1]:
EK_START_CFG_EDGE "following true branch (when flag != 0)..."
(depth 3 corrected from 1,
fndecl inner corrected from outer, m_loc=8000000f)
before[2]:
EK_END_CFG_EDGE "...to here"
(depth 1, fndecl outer, m_loc=0)
before[3]:
EK_WARNING "here (<unknown> is in state null)"
(depth 1, fndecl outer, m_loc=80000004)
We want to add inlined_call_events showing the calls, so that
the above becomes:
after[0]:
EK_FUNCTION_ENTRY "entry to outer"
(depth 1, fndecl outer, m_loc=511c4)
after[1]:
EK_INLINED_CALL "inlined call to middle from outer"
(depth 1, fndecl outer, m_loc=53300)
after[2]:
EK_INLINED_CALL "inlined call to inner from middle"
(depth 2, fndecl middle, m_loc=4d2e0)
after[3]:
EK_START_CFG_EDGE "following true branch (when flag != 0)..."
(depth 3 corrected from 1,
fndecl inner corrected from outer, m_loc=8000000f)
after[4]: EK_END_CFG_EDGE "...to here"
(depth 1, fndecl outer, m_loc=0)
after[5]: EK_WARNING "here (<unknown> is in state null)"
(depth 1, fndecl outer, m_loc=80000004)
where we've added events between before[0] and before[1] to show
the inlined calls leading to the effective stack depths, making
the generated path much easier for a user to read.
Note how in the above we have a branch (before[1] to before[2])
where the locations were originally in different functions.
Hence we have to add these events quite late when generating
checker_path. */
void
checker_path::inject_any_inlined_call_events (logger *logger)
{
LOG_SCOPE (logger);
if (!flag_analyzer_undo_inlining)
return;
/* Build a copy of m_events with the new events inserted. */
auto_vec<checker_event *> updated_events;
maybe_log (logger, "before");
hash_set<tree> blocks_in_prev_event;
for (unsigned ev_idx = 0; ev_idx < m_events.length (); ev_idx++)
{
checker_event *curr_event = m_events[ev_idx];
location_t curr_loc = curr_event->get_location ();
hash_set<tree> blocks_in_curr_event;
if (logger)
{
logger->start_log_line ();
logger->log_partial ("event[%i]: %s ", ev_idx,
event_kind_to_string (curr_event->m_kind));
curr_event->dump (logger->get_printer ());
logger->end_log_line ();
for (inlining_iterator iter (curr_event->get_location ());
!iter.done_p (); iter.next ())
{
logger->start_log_line ();
logger->log_partial (" %qE (%p), fndecl: %qE, callsite: 0x%x",
iter.get_block (), iter.get_block (),
iter.get_fndecl (), iter.get_callsite ());
if (iter.get_callsite ())
dump_location (logger->get_printer (), iter.get_callsite ());
logger->end_log_line ();
}
}
/* We want to add events to show inlined calls.
We want to show changes relative to the previous event, omitting
the commonality between the inlining chain.
The chain is ordered from innermost frame to outermost frame;
we want to walk it backwards to show the calls, so capture it
in a vec. */
struct chain_element { tree m_block; tree m_fndecl; };
auto_vec<chain_element> elements;
for (inlining_iterator iter (curr_loc); !iter.done_p (); iter.next ())
{
chain_element ce;
ce.m_block = iter.get_block ();
ce.m_fndecl = iter.get_fndecl ();
if (!blocks_in_prev_event.contains (ce.m_block))
elements.safe_push (ce);
blocks_in_curr_event.add (ce.m_block);
}
/* Walk from outermost to innermost. */
if (elements.length () > 0)
{
int orig_stack_depth = curr_event->get_original_stack_depth ();
for (unsigned element_idx = elements.length () - 1; element_idx > 0;
element_idx--)
{
const chain_element &ce = elements[element_idx];
int stack_depth_adjustment
= (blocks_in_curr_event.elements () - element_idx) - 1;
if (location_t callsite = BLOCK_SOURCE_LOCATION (ce.m_block))
updated_events.safe_push
(new inlined_call_event (callsite,
elements[element_idx - 1].m_fndecl,
ce.m_fndecl,
orig_stack_depth,
stack_depth_adjustment));
}
}
/* Ideally we'd use assignment here:
blocks_in_prev_event = blocks_in_curr_event; */
blocks_in_prev_event.empty ();
for (auto iter : blocks_in_curr_event)
blocks_in_prev_event.add (iter);
/* Add the existing event. */
updated_events.safe_push (curr_event);
}
/* Replace m_events with updated_events. */
m_events.truncate (0);
m_events.safe_splice (updated_events);
maybe_log (logger, " after");
}
} // namespace ana
#endif /* #if ENABLE_ANALYZER */

View file

@ -42,6 +42,7 @@ enum event_kind
EK_RETURN_EDGE,
EK_START_CONSOLIDATED_CFG_EDGES,
EK_END_CONSOLIDATED_CFG_EDGES,
EK_INLINED_CALL,
EK_SETJMP,
EK_REWIND_FROM_LONGJMP,
EK_REWIND_TO_SETJMP,
@ -72,6 +73,7 @@ extern const char *event_kind_to_string (enum event_kind ek);
return_edge (EK_RETURN_EDGE)
start_consolidated_cfg_edges_event (EK_START_CONSOLIDATED_CFG_EDGES)
end_consolidated_cfg_edges_event (EK_END_CONSOLIDATED_CFG_EDGES)
inlined_call_event (EK_INLINED_CALL)
setjmp_event (EK_SETJMP)
rewind_event
rewind_from_longjmp_event (EK_REWIND_FROM_LONGJMP)
@ -84,22 +86,14 @@ extern const char *event_kind_to_string (enum event_kind ek);
class checker_event : public diagnostic_event
{
public:
checker_event (enum event_kind kind,
location_t loc, tree fndecl, int depth)
: m_kind (kind), m_loc (loc), m_fndecl (fndecl), m_depth (depth),
m_pending_diagnostic (NULL), m_emission_id (),
m_logical_loc (fndecl)
{
}
/* Implementation of diagnostic_event. */
location_t get_location () const final override { return m_loc; }
tree get_fndecl () const final override { return m_fndecl; }
int get_stack_depth () const final override { return m_depth; }
tree get_fndecl () const final override { return m_effective_fndecl; }
int get_stack_depth () const final override { return m_effective_depth; }
const logical_location *get_logical_location () const final override
{
if (m_fndecl)
if (m_effective_fndecl)
return &m_logical_loc;
else
return NULL;
@ -108,6 +102,8 @@ public:
/* Additional functionality. */
int get_original_stack_depth () const { return m_original_depth; }
virtual void prepare_for_emission (checker_path *,
pending_diagnostic *pd,
diagnostic_event_id_t emission_id);
@ -125,12 +121,18 @@ public:
void set_location (location_t loc) { m_loc = loc; }
protected:
checker_event (enum event_kind kind,
location_t loc, tree fndecl, int depth);
public:
const enum event_kind m_kind;
protected:
location_t m_loc;
tree m_fndecl;
int m_depth;
tree m_original_fndecl;
tree m_effective_fndecl;
int m_original_depth;
int m_effective_depth;
pending_diagnostic *m_pending_diagnostic;
diagnostic_event_id_t m_emission_id; // only set once all pruning has occurred
tree_logical_location m_logical_loc;
@ -435,6 +437,34 @@ public:
}
};
/* A concrete event subclass for describing an inlined call event
e.g. "inlined call to 'callee' from 'caller'". */
class inlined_call_event : public checker_event
{
public:
inlined_call_event (location_t loc,
tree apparent_callee_fndecl,
tree apparent_caller_fndecl,
int actual_depth,
int stack_depth_adjustment)
: checker_event (EK_INLINED_CALL, loc,
apparent_caller_fndecl,
actual_depth + stack_depth_adjustment),
m_apparent_callee_fndecl (apparent_callee_fndecl),
m_apparent_caller_fndecl (apparent_caller_fndecl)
{
gcc_assert (LOCATION_BLOCK (loc) == NULL);
}
label_text get_desc (bool /*can_colorize*/) const final override;
meaning get_meaning () const override;
private:
tree m_apparent_callee_fndecl;
tree m_apparent_caller_fndecl;
};
/* A concrete event subclass for a setjmp or sigsetjmp call. */
class setjmp_event : public checker_event
@ -643,6 +673,8 @@ public:
bool cfg_edge_pair_at_p (unsigned idx) const;
void inject_any_inlined_call_events (logger *logger);
private:
DISABLE_COPY_AND_ASSIGN(checker_path);

View file

@ -52,6 +52,7 @@ along with GCC; see the file COPYING3. If not see
#include "basic-block.h"
#include "gimple.h"
#include "gimple-iterator.h"
#include "inlining-iterator.h"
#include "cgraph.h"
#include "digraph.h"
#include "analyzer/supergraph.h"
@ -1390,6 +1391,8 @@ diagnostic_manager::emit_saved_diagnostic (const exploded_graph &eg,
if (sd.m_trailing_eedge)
add_events_for_eedge (pb, *sd.m_trailing_eedge, &emission_path, NULL);
emission_path.inject_any_inlined_call_events (get_logger ());
emission_path.prepare_for_emission (sd.m_d);
location_t loc
@ -2454,6 +2457,11 @@ diagnostic_manager::prune_for_sm_diagnostic (checker_path *path,
}
break;
case EK_INLINED_CALL:
/* We don't expect to see these yet, as they're added later.
We'd want to keep them around. */
break;
case EK_SETJMP:
/* TODO: only show setjmp_events that matter i.e. those for which
there is a later rewind event using them. */

View file

@ -2904,7 +2904,7 @@ public:
return make_label_text (can_colorize,
"function %qE used as initializer for field %qE"
" marked with %<__attribute__((tainted_args))%>",
m_fndecl, m_field);
get_fndecl (), m_field);
}
private:

View file

@ -0,0 +1,109 @@
/* Iterator for walking a chain of inlining locations.
Copyright (C) 2022 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/>. */
#ifndef GCC_ANALYZER_INLINING_ITERATOR_H
#define GCC_ANALYZER_INLINING_ITERATOR_H
/* Iterator for walking a chain of inlining locations.
The fndecls and locations will be traversed from innermost to outermost.
For example, given:
inline void inner (void)
{
...LOCATION HERE...
}
void outer (void)
{
inner (); <-- CALLSITE
}
then the fndecl will be "inner" on the initial iteration, and "outer" on
the second (final) iteration.
Compare with lhd_print_error_function, cp_print_error_function,
and optrecord_json_writer::inlining_chain_to_json. */
class inlining_iterator
{
public:
inlining_iterator (location_t loc)
: m_abstract_origin (LOCATION_BLOCK (loc)),
m_callsite (UNKNOWN_LOCATION), m_fndecl (NULL),
m_next_abstract_origin (NULL)
{
prepare_iteration ();
}
bool done_p () const { return m_abstract_origin == NULL; }
void next ()
{
m_abstract_origin = m_next_abstract_origin;
prepare_iteration ();
}
tree get_fndecl () const { return m_fndecl; }
location_t get_callsite () const { return m_callsite; }
tree get_block () const { return m_abstract_origin; }
private:
void prepare_iteration ()
{
if (done_p ())
return;
tree block = m_abstract_origin;
m_callsite = BLOCK_SOURCE_LOCATION (block);
m_fndecl = NULL;
block = BLOCK_SUPERCONTEXT (block);
while (block && TREE_CODE (block) == BLOCK
&& BLOCK_ABSTRACT_ORIGIN (block))
{
tree ao = BLOCK_ABSTRACT_ORIGIN (block);
if (TREE_CODE (ao) == FUNCTION_DECL)
{
m_fndecl = ao;
break;
}
else if (TREE_CODE (ao) != BLOCK)
break;
block = BLOCK_SUPERCONTEXT (block);
}
if (m_fndecl)
m_next_abstract_origin = block;
else
{
while (block && TREE_CODE (block) == BLOCK)
block = BLOCK_SUPERCONTEXT (block);
if (block && TREE_CODE (block) == FUNCTION_DECL)
m_fndecl = block;
m_next_abstract_origin = NULL;
}
}
tree m_abstract_origin;
location_t m_callsite;
tree m_fndecl;
tree m_next_abstract_origin;
};
#endif /* GCC_ANALYZER_INLINING_ITERATOR_H */

View file

@ -425,6 +425,7 @@ Objective-C and Objective-C++ Dialects}.
-fno-analyzer-state-merge @gol
-fno-analyzer-state-purge @gol
-fanalyzer-transitivity @gol
-fno-analyzer-undo-inlining @gol
-fanalyzer-verbose-edges @gol
-fanalyzer-verbose-state-changes @gol
-fanalyzer-verbosity=@var{level} @gol
@ -5233,7 +5234,10 @@ This option provides additional information when printing control-flow paths
associated with a diagnostic.
If this is option is provided then the stack depth will be printed for
each run of events within @option{-fdiagnostics-path-format=separate-events}.
each run of events within @option{-fdiagnostics-path-format=inline-events}.
If provided with @option{-fdiagnostics-path-format=separate-events}, then
the stack depth and function declaration will be appended when printing
each event.
This is intended for use by GCC developers and plugin developers when
debugging diagnostics that report interprocedural control flow.
@ -10202,6 +10206,25 @@ be suppressed, for debugging state-handling issues.
@opindex fno-analyzer-transitivity
This option enables transitivity of constraints within the analyzer.
@item -fno-analyzer-undo-inlining
@opindex fanalyzer-undo-inlining
@opindex fno-analyzer-undo-inlining
This option is intended for analyzer developers.
@option{-fanalyzer} runs relatively late compared to other code analysis
tools, and some optimizations have already been applied to the code. In
particular function inlining may have occurred, leading to the
interprocedural execution paths emitted by the analyzer containing
function frames that don't correspond to those in the original source
code.
By default the analyzer attempts to reconstruct the original function
frames, and to emit events showing the inlined calls.
With @option{-fno-analyzer-undo-inlining} this attempt to reconstruct
the original frame information can be be disabled, which may be of help
when debugging issues in the analyzer.
@item -fanalyzer-verbose-edges
This option is intended for analyzer developers. It enables more
verbose, lower-level detail in the descriptions of control flow

View file

@ -0,0 +1,56 @@
/* As per inlining-1.c, but testing how the ASCII art version of
the path looks. */
/* { dg-additional-options "-O2 -fdiagnostics-show-path-depths" } */
/* { dg-additional-options "-fdiagnostics-path-format=inline-events -fdiagnostics-show-caret" } */
void foo (void *p)
{
__builtin_free (p); /* { dg-warning "double-'free' of 'q'" "warning" } */
}
void bar (void *q)
{
foo (q);
foo (q);
}
/* { dg-begin-multiline-output "" }
__builtin_free (p);
^~~~~~~~~~~~~~~~~~
'bar': events 1-2 (depth 1)
|
| void bar (void *q)
| ^~~
| |
| (1) entry to 'bar'
|
| foo (q);
| ~
| |
| (2) inlined call to 'foo' from 'bar'
|
+--> 'foo': event 3 (depth 2)
|
| __builtin_free (p);
| ^~~~~~~~~~~~~~~~~~
| |
| (3) first 'free' here
|
<------+
|
'bar': event 4 (depth 1)
|
| foo (q);
| ^
| |
| (4) inlined call to 'foo' from 'bar'
|
+--> 'foo': event 5 (depth 2)
|
| __builtin_free (p);
| ^~~~~~~~~~~~~~~~~~
| |
| (5) second 'free' here; first 'free' was at (3)
|
{ dg-end-multiline-output "" } */

View file

@ -0,0 +1,18 @@
/* Test for -fno-analyzer-undo-inlining.
Verify that we can disable reconstruction of fndecl and stack depth
information. */
/* { dg-additional-options "-O2 -fdiagnostics-show-path-depths -fno-analyzer-undo-inlining" } */
void foo (void *p)
{
__builtin_free (p); /* { dg-warning "double-'free' of 'q'" "warning" } */
/* { dg-message "\\(1\\) first 'free' here \\(fndecl 'bar', depth 1\\)" "1st free message" { target *-*-* } .-1 } */
/* { dg-message "\\(2\\) second 'free' here; first 'free' was at \\(1\\) \\(fndecl 'bar', depth 1\\)" "2nd free message" { target *-*-* } .-2 } */
}
void bar (void *q)
{
foo (q);
foo (q);
}

View file

@ -0,0 +1,17 @@
/* Verify that we can reconstruct fndecl and stack depth information
after early inlining. */
/* { dg-additional-options "-O2 -fdiagnostics-show-path-depths" } */
void foo (void *p)
{
__builtin_free (p); /* { dg-warning "double-'free' of 'q'" "warning" } */
/* { dg-message "\\(3\\) first 'free' here \\(fndecl 'foo', depth 2\\)" "1st free message" { target *-*-* } .-1 } */
/* { dg-message "\\(5\\) second 'free' here; first 'free' was at \\(3\\) \\(fndecl 'foo', depth 2\\)" "2nd free message" { target *-*-* } .-2 } */
}
void bar (void *q) /* { dg-message "\\(1\\) entry to 'bar' \\(fndecl 'bar', depth 1\\)" } */
{
foo (q); /* { dg-message "\\(2\\) inlined call to 'foo' from 'bar' \\(fndecl 'bar', depth 1\\)" } */
foo (q); /* { dg-message "\\(4\\) inlined call to 'foo' from 'bar' \\(fndecl 'bar', depth 1\\)" } */
}

View file

@ -0,0 +1,46 @@
/* As per inlining-2.c, but testing how the ASCII art version of
the path looks. */
/* { dg-additional-options "-O2 -fdiagnostics-show-path-depths" } */
/* { dg-additional-options "-fdiagnostics-path-format=inline-events -fdiagnostics-show-caret" } */
static void __analyzer_foo (void *p)
{
__builtin_free (p);
__builtin_free (p); /* { dg-warning "double-'free' of 'q'" "warning" } */
}
void bar (void *q)
{
__analyzer_foo (q);
}
/* { dg-begin-multiline-output "" }
__builtin_free (p);
^~~~~~~~~~~~~~~~~~
'bar': events 1-2 (depth 1)
|
| void bar (void *q)
| ^~~
| |
| (1) entry to 'bar'
|
| __analyzer_foo (q);
| ~
| |
| (2) inlined call to '__analyzer_foo' from 'bar'
|
+--> '__analyzer_foo': events 3-4 (depth 2)
|
| __builtin_free (p);
| ^~~~~~~~~~~~~~~~~~
| |
| (3) first 'free' here
|
| __builtin_free (p);
| ~~~~~~~~~~~~~~~~~~
| |
| (4) second 'free' here; first 'free' was at (3)
|
{ dg-end-multiline-output "" } */

View file

@ -0,0 +1,17 @@
/* Verify that we can reconstruct fndecl and stack depth information
after early inlining. */
/* { dg-additional-options "-O2 -fdiagnostics-show-path-depths" } */
static void __analyzer_foo (void *p)
{
__builtin_free (p); /* { dg-message "\\(3\\) first 'free' here \\(fndecl '__analyzer_foo', depth 2\\)" "1st free message" } */
__builtin_free (p); /* { dg-warning "double-'free' of 'q'" "warning" } */
/* { dg-message "\\(4\\) second 'free' here; first 'free' was at \\(3\\) \\(fndecl '__analyzer_foo', depth 2\\)" "2nd free message" { target *-*-* } .-1 } */
}
void bar (void *q) /* { dg-message "\\(1\\) entry to 'bar' \\(fndecl 'bar', depth 1\\)" } */
{
__analyzer_foo (q); /* { dg-message "\\(2\\) inlined call to '__analyzer_foo' from 'bar' \\(fndecl 'bar', depth 1\\)" } */
}

View file

@ -0,0 +1,64 @@
/* As per inlining-3.c, but testing how the ASCII art version of
the path looks. */
/* { dg-additional-options "-O2 -fdiagnostics-show-path-depths" } */
/* { dg-additional-options "-fdiagnostics-path-format=inline-events -fdiagnostics-show-caret" } */
typedef __SIZE_TYPE__ size_t;
#define NULL ((void *)0)
struct input_file_st
{
char inpname[1];
};
typedef struct input_file_st input_file;
static inline const char*
get_input_file_name (const input_file *inpf)
{
if (inpf)
return inpf->inpname;
return NULL;
}
size_t
test (const input_file *inpf)
{
const char *f = get_input_file_name (inpf);
return __builtin_strlen (f); /* { dg-warning "use of NULL" "warning" } */
}
/* { dg-begin-multiline-output "" }
return __builtin_strlen (f);
^~~~~~~~~~~~~~~~~~~~
'test': events 1-2 (depth 1)
|
| test (const input_file *inpf)
| ^~~~
| |
| (1) entry to 'test'
|
| const char *f = get_input_file_name (inpf);
| ~
| |
| (2) inlined call to 'get_input_file_name' from 'test'
|
+--> 'get_input_file_name': event 3 (depth 2)
|
| if (inpf)
| ^
| |
| (3) following 'false' branch (when 'inpf' is NULL)...
|
<------+
|
'test': events 4-5 (depth 1)
|
| return __builtin_strlen (f);
| ^~~~~~~~~~~~~~~~~~~~
| |
| (4) ...to here
| (5) argument 1 ('<unknown>') NULL where non-null expected
|
{ dg-end-multiline-output "" } */

View file

@ -0,0 +1,30 @@
/* Verify that we can reconstruct fndecl and stack depth information
after early inlining. */
/* { dg-additional-options "-O2 -fdiagnostics-show-path-depths" } */
typedef __SIZE_TYPE__ size_t;
#define NULL ((void *)0)
struct input_file_st
{
char inpname[1];
};
typedef struct input_file_st input_file;
static inline const char*
get_input_file_name (const input_file *inpf)
{
if (inpf) /* { dg-message "following 'false' branch \\(when 'inpf' is NULL\\)\\.\\.\\. \\(fndecl 'get_input_file_name', depth 2\\)" } */
return inpf->inpname;
return NULL;
}
size_t
test (const input_file *inpf)
{
const char *f = get_input_file_name (inpf);
return __builtin_strlen (f); /* { dg-warning "use of NULL" "warning" } */
/* { dg-message "NULL where non-null expected \\(fndecl 'test', depth 1\\)" "message" { target *-*-* } .-1 } */
}

View file

@ -0,0 +1,72 @@
/* As per inlining-4.c, but testing how the ASCII art version of
the path looks. */
/* { dg-additional-options "-O2 -fdiagnostics-show-path-depths" } */
/* { dg-additional-options "-fdiagnostics-path-format=inline-events -fdiagnostics-show-caret" } */
#define NULL ((void *)0)
static inline const char*
inner (int flag)
{
if (flag)
return NULL;
return "foo";
}
static inline const char*
middle (int flag)
{
return inner (flag);
}
char
outer (int flag)
{
return *middle (flag); /* { dg-warning "dereference of NULL" "warning" } */
}
/* { dg-begin-multiline-output "" }
return *middle (flag);
^~~~~~~~~~~~~~
'outer': events 1-2 (depth 1)
|
| outer (int flag)
| ^~~~~
| |
| (1) entry to 'outer'
|
| return *middle (flag);
| ~
| |
| (2) inlined call to 'middle' from 'outer'
|
+--> 'middle': event 3 (depth 2)
|
| return inner (flag);
| ^
| |
| (3) inlined call to 'inner' from 'middle'
|
+--> 'inner': event 4 (depth 3)
|
| if (flag)
| ^
| |
| (4) following 'true' branch (when 'flag != 0')...
|
<-------------+
|
'outer': event 5 (depth 1)
|
|cc1:
| (5): ...to here
|
'outer': event 6 (depth 1)
|
| return *middle (flag);
| ^~~~~~~~~~~~~~
| |
| (6) dereference of NULL '<unknown>'
|
{ dg-end-multiline-output "" } */

View file

@ -0,0 +1,27 @@
/* Verify that we can reconstruct fndecl and stack depth information
after early inlining. */
/* { dg-additional-options "-O2 -fdiagnostics-show-path-depths" } */
#define NULL ((void *)0)
static inline const char*
inner (int flag)
{
if (flag) /* { dg-message "following 'true' branch \\(when 'flag != 0'\\)\\.\\.\\. \\(fndecl 'inner', depth 3\\)" } */
return NULL;
return "foo";
}
static inline const char*
middle (int flag)
{
return inner (flag);
}
char
outer (int flag)
{
return *middle (flag); /* { dg-warning "dereference of NULL" "warning" } */
/* { dg-message "\\(fndecl 'outer', depth 1\\)" "message" { target *-*-* } .-1 } */
}

View file

@ -0,0 +1,59 @@
/* As per inlining-5.c, but testing how the ASCII art version of
the path looks. */
/* { dg-additional-options "-O2 -fdiagnostics-show-path-depths" } */
/* { dg-additional-options "-fdiagnostics-path-format=inline-events -fdiagnostics-show-caret" } */
static inline void
inner (void *p)
{
__builtin_free (p); /* { dg-warning "double-'free' of 'r'" } */
}
static inline void
middle (void *q)
{
__builtin_free (q);
inner (q);
}
void
outer (void *r)
{
middle (r);
}
/* { dg-begin-multiline-output "" }
__builtin_free (p);
^~~~~~~~~~~~~~~~~~
'outer': events 1-2 (depth 1)
|
| outer (void *r)
| ^~~~~
| |
| (1) entry to 'outer'
|
| middle (r);
| ~
| |
| (2) inlined call to 'middle' from 'outer'
|
+--> 'middle': events 3-4 (depth 2)
|
| __builtin_free (q);
| ^~~~~~~~~~~~~~~~~~
| |
| (3) first 'free' here
| inner (q);
| ~
| |
| (4) inlined call to 'inner' from 'middle'
|
+--> 'inner': event 5 (depth 3)
|
| __builtin_free (p);
| ^~~~~~~~~~~~~~~~~~
| |
| (5) second 'free' here; first 'free' was at (3)
|
{ dg-end-multiline-output "" } */

View file

@ -0,0 +1,24 @@
/* Verify that we can reconstruct fndecl and stack depth information
after early inlining. */
/* { dg-additional-options "-O2 -fdiagnostics-show-path-depths" } */
static inline void
inner (void *p)
{
__builtin_free (p); /* { dg-warning "double-'free' of 'r'" } */
/* { dg-message "\\(5\\) second 'free' here; first 'free' was at \\(3\\) \\(fndecl 'inner', depth 3\\)" "2nd free message" { target *-*-* } .-1 } */
}
static inline void
middle (void *q)
{
__builtin_free (q); /* { dg-message "\\(3\\) first 'free' here \\(fndecl 'middle', depth 2\\)" "1st free message" } */
inner (q); /* { dg-message "\\(4\\) inlined call to 'inner' from 'middle' \\(fndecl 'middle', depth 2\\)" } */
}
void
outer (void *r) /* { dg-message "\\(1\\) entry to 'outer' \\(fndecl 'outer', depth 1\\)" } */
{
middle (r); /* { dg-message "\\(2\\) inlined call to 'middle' from 'outer' \\(fndecl 'outer', depth 1\\)" } */
}

View file

@ -0,0 +1,64 @@
/* As per inlining-6.c, but testing how the ASCII art version of
the path looks. */
/* { dg-additional-options "-O2 -fdiagnostics-show-path-depths" } */
/* { dg-additional-options "-fdiagnostics-path-format=inline-events -fdiagnostics-show-caret" } */
static inline void
inner (void *p)
{
__builtin_free (p);
}
static inline void
middle (void *q)
{
inner (q);
__builtin_free (q); /* { dg-warning "double-'free' of 'r'" } */
}
void
outer (void *r)
{
middle (r);
}
/* { dg-begin-multiline-output "" }
__builtin_free (q);
^~~~~~~~~~~~~~~~~~
'outer': events 1-2 (depth 1)
|
| outer (void *r)
| ^~~~~
| |
| (1) entry to 'outer'
|
| middle (r);
| ~
| |
| (2) inlined call to 'middle' from 'outer'
|
+--> 'middle': event 3 (depth 2)
|
| inner (q);
| ^
| |
| (3) inlined call to 'inner' from 'middle'
|
+--> 'inner': event 4 (depth 3)
|
| __builtin_free (p);
| ^~~~~~~~~~~~~~~~~~
| |
| (4) first 'free' here
|
<------+
|
'middle': event 5 (depth 2)
|
| __builtin_free (q);
| ^~~~~~~~~~~~~~~~~~
| |
| (5) second 'free' here; first 'free' was at (4)
|
{ dg-end-multiline-output "" } */

View file

@ -0,0 +1,23 @@
/* Verify that we can reconstruct fndecl and stack depth information
after early inlining. */
/* { dg-additional-options "-O2 -fdiagnostics-show-path-depths" } */
static inline void
inner (void *p)
{
__builtin_free (p);
}
static inline void
middle (void *q)
{
inner (q);
__builtin_free (q); /* { dg-warning "double-'free' of 'r'" "warning" } */
}
void
outer (void *r)
{
middle (r);
}

View file

@ -0,0 +1,128 @@
/* As per inlining-7.c, but testing how the ASCII art version of
the path looks. */
/* { dg-additional-options "-O2 -fdiagnostics-show-path-depths" } */
/* { dg-additional-options "-fdiagnostics-path-format=inline-events -fdiagnostics-show-caret" } */
static inline void
depth_6 (void *p)
{
__builtin_free (p); /* { dg-warning "double-'free' of 'p1'" "warning" } */
}
static inline void
depth_5 (void *p5)
{
depth_6 (p5);
}
static inline void
depth_4 (void *p4)
{
depth_5 (p4);
}
static inline void
depth_3 (void *p3)
{
depth_4 (p3);
depth_4 (p3);
}
static inline void
depth_2 (void *p2)
{
depth_3 (p2);
}
void
depth_1 (void *p1)
{
depth_2 (p1);
}
/* We want the reconstructed call/return hierarchy to show
that two calls happen at depth_3, without popping the stack
back any further. */
/* { dg-begin-multiline-output "" }
__builtin_free (p);
^~~~~~~~~~~~~~~~~~
'depth_1': events 1-2 (depth 1)
|
| depth_1 (void *p1)
| ^~~~~~~
| |
| (1) entry to 'depth_1'
|
| depth_2 (p1);
| ~
| |
| (2) inlined call to 'depth_2' from 'depth_1'
|
+--> 'depth_2': event 3 (depth 2)
|
| depth_3 (p2);
| ^
| |
| (3) inlined call to 'depth_3' from 'depth_2'
|
+--> 'depth_3': event 4 (depth 3)
|
| depth_4 (p3);
| ^
| |
| (4) inlined call to 'depth_4' from 'depth_3'
|
+--> 'depth_4': event 5 (depth 4)
|
| depth_5 (p4);
| ^
| |
| (5) inlined call to 'depth_5' from 'depth_4'
|
+--> 'depth_5': event 6 (depth 5)
|
| depth_6 (p5);
| ^
| |
| (6) inlined call to 'depth_6' from 'depth_5'
|
+--> 'depth_6': event 7 (depth 6)
|
| __builtin_free (p);
| ^~~~~~~~~~~~~~~~~~
| |
| (7) first 'free' here
|
<--------------------+
|
'depth_3': event 8 (depth 3)
|
| depth_4 (p3);
| ^
| |
| (8) inlined call to 'depth_4' from 'depth_3'
|
+--> 'depth_4': event 9 (depth 4)
|
| depth_5 (p4);
| ^
| |
| (9) inlined call to 'depth_5' from 'depth_4'
|
+--> 'depth_5': event 10 (depth 5)
|
| depth_6 (p5);
| ^
| |
| (10) inlined call to 'depth_6' from 'depth_5'
|
+--> 'depth_6': event 11 (depth 6)
|
| __builtin_free (p);
| ^~~~~~~~~~~~~~~~~~
| |
| (11) second 'free' here; first 'free' was at (7)
|
{ dg-end-multiline-output "" } */

View file

@ -0,0 +1,49 @@
/* Verify that we can reconstruct fndecl and stack depth information
after early inlining. */
/* { dg-additional-options "-O2 -fdiagnostics-show-path-depths" } */
/* We want the reconstructed call/return hierarchy to show
that two calls happen at depth_3, without any spurious events
popping the stack back any further. */
static inline void
depth_6 (void *p)
{
__builtin_free (p); /* { dg-warning "double-'free' of 'p1'" "warning" } */
/* { dg-message "\\(7\\) first 'free' here \\(fndecl 'depth_6', depth 6\\)" "1st free message" { target *-*-* } .-1 } */
/* { dg-message "\\(11\\) second 'free' here; first 'free' was at \\(7\\) \\(fndecl 'depth_6', depth 6\\)" "2nd free message" { target *-*-* } .-2 } */
}
static inline void
depth_5 (void *p5)
{
depth_6 (p5); /* { dg-message "\\(6\\) inlined call to 'depth_6' from 'depth_5' \\(fndecl 'depth_5', depth 5\\)" "event 6" } */
/* { dg-message "\\(10\\) inlined call to 'depth_6' from 'depth_5' \\(fndecl 'depth_5', depth 5\\)" "event 10" { target *-*-* } .-1 } */
}
static inline void
depth_4 (void *p4)
{
depth_5 (p4); /* { dg-message "\\(5\\) inlined call to 'depth_5' from 'depth_4' \\(fndecl 'depth_4', depth 4\\)" "event 5" } */
/* { dg-message "\\(9\\) inlined call to 'depth_5' from 'depth_4' \\(fndecl 'depth_4', depth 4\\)" "event 9" { target *-*-* } .-1 } */
}
static inline void
depth_3 (void *p3)
{
depth_4 (p3); /* { dg-message "\\(4\\) inlined call to 'depth_4' from 'depth_3' \\(fndecl 'depth_3', depth 3\\)" } */
depth_4 (p3); /* { dg-message "\\(8\\) inlined call to 'depth_4' from 'depth_3' \\(fndecl 'depth_3', depth 3\\)" } */
}
static inline void
depth_2 (void *p2)
{
depth_3 (p2); /* { dg-message "\\(3\\) inlined call to 'depth_3' from 'depth_2' \\(fndecl 'depth_2', depth 2\\)" } */
}
void
depth_1 (void *p1) /* { dg-message "\\(1\\) entry to 'depth_1' \\(fndecl 'depth_1', depth 1\\)" } */
{
depth_2 (p1); /* { dg-message "\\(2\\) inlined call to 'depth_2' from 'depth_1' \\(fndecl 'depth_1', depth 1\\)" } */
}

View file

@ -463,8 +463,27 @@ default_tree_diagnostic_path_printer (diagnostic_context *context,
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);
if (context->show_path_depths)
{
int stack_depth = event.get_stack_depth ();
tree fndecl = event.get_fndecl ();
/* -fdiagnostics-path-format=separate-events doesn't print
fndecl information, so with -fdiagnostics-show-path-depths
print the fndecls too, if any. */
if (fndecl)
inform (event.get_location (),
"%@ %s (fndecl %qD, depth %i)",
&event_id, event_text.m_buffer,
fndecl, stack_depth);
else
inform (event.get_location (),
"%@ %s (depth %i)",
&event_id, event_text.m_buffer,
stack_depth);
}
else
inform (event.get_location (),
"%@ %s", &event_id, event_text.m_buffer);
event_text.maybe_free ();
}
}