diff mbox series

[WIP,RFC] analyzer: Add optional trim of the analyzer diagnostics going too deep [PR110543]

Message ID CAKiQ0GFBmnmg1xSa0FEU-nENjdCZVCS28-3rVn2r1qCXxyyeag@mail.gmail.com
State New
Headers show
Series [WIP,RFC] analyzer: Add optional trim of the analyzer diagnostics going too deep [PR110543] | expand

Commit Message

Benjamin Priour July 21, 2023, 3:35 p.m. UTC
Hi,

Upon David's request I've joined the in progress patch to the below email.
I hope it makes more sense now.

Best,
Benjamin.

---------- Forwarded message ---------
From: Benjamin Priour <priour.be@gmail.com>
Date: Tue, Jul 18, 2023 at 3:30 PM
Subject: [RFC] analyzer: Add optional trim of the analyzer diagnostics
going too deep [PR110543]
To: <gcc-patches@gcc.gnu.org>, David Malcolm <dmalcolm@redhat.com>


Hi,

I'd like to request comments on a patch I am writing for PR110543.
The goal of this patch is to reduce the noise of the analyzer emitted
diagnostics when dealing with
system headers, or simply diagnostic paths that are too long. The new
option only affects the display
of the diagnostics, but doesn't hinder the actual analysis.

I've defaulted the new option to "system", thus preventing the diagnostic
paths from showing system headers.
"never" corresponds to the pre-patch behavior, whereas you can also specify
an unsigned value <maxdepth>
that prevents paths to go deeper than <maxdepth> frames.

fanalyzer-trim-diagnostics=
> Common Joined RejectNegative ToLower Var(flag_analyzer_trim_diagnostics)
> Init("system")
> -fanalyzer-trim-diagnostics=[never|system|<maxdepth>] Trim diagnostics
> path that are too long before emission.
>

Does it sounds reasonable and user-friendly ?

Regstrapping was a success against trunk, although one of the newly added
test case fails for c++14.
Note that the test case below was done with "never", thus behaves exactly
as the pre-patch analyzer
on x86_64-linux-gnu.

/* { dg-additional-options "-fdiagnostics-plain-output
> -fdiagnostics-path-format=inline-events -fanalyzer-trim-diagnostics=never"
> } */
> /* { dg-skip-if "" { c++98_only }  } */
>
> #include <memory>
> struct A {int x; int y;};
>
> int main () {
>   std::shared_ptr<A> a;
>   a->x = 4; /* { dg-line deref_a } */
>   /* { dg-warning "dereference of NULL" "" { target *-*-* } deref_a } */
>
>   return 0;
> }
>
> /* { dg-begin-multiline-output "" }
>   'int main()': events 1-2
>     |
>     |
>     +--> 'std::__shared_ptr_access<_Tp, _Lp, <anonymous>, <anonymous>
> >::element_type* std::__shared_ptr_access<_Tp, _Lp, <anonymous>,
> <anonymous> >::operator->() const [with _Tp = A; __gnu_cxx::_Lock_policy
> _Lp = __gnu_cxx::_S_atomic; bool <anonymous> = false; bool <anonymous> =
> false]': events 3-4
>            |
>            |
>            +--> 'std::__shared_ptr_access<_Tp, _Lp, <anonymous>,
> <anonymous> >::element_type* std::__shared_ptr_access<_Tp, _Lp,
> <anonymous>, <anonymous> >::_M_get() const [with _Tp = A;
> __gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic; bool <anonymous> =
> false; bool <anonymous> = false]': events 5-6
>                   |
>                   |
>                   +--> 'std::__shared_ptr<_Tp, _Lp>::element_type*
> std::__shared_ptr<_Tp, _Lp>::get() const [with _Tp = A;
> __gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic]': events 7-8
>                          |
>                          |
>                   <------+
>                   |
>                 'std::__shared_ptr_access<_Tp, _Lp, <anonymous>,
> <anonymous> >::element_type* std::__shared_ptr_access<_Tp, _Lp,
> <anonymous>, <anonymous> >::_M_get() const [with _Tp = A;
> __gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic; bool <anonymous> =
> false; bool <anonymous> = false]': event 9
>                   |
>                   |
>            <------+
>            |
>          'std::__shared_ptr_access<_Tp, _Lp, <anonymous>, <anonymous>
> >::element_type* std::__shared_ptr_access<_Tp, _Lp, <anonymous>,
> <anonymous> >::operator->() const [with _Tp = A; __gnu_cxx::_Lock_policy
> _Lp = __gnu_cxx::_S_atomic; bool <anonymous> = false; bool <anonymous> =
> false]': event 10
>            |
>            |
>     <------+
>     |
>   'int main()': events 11-12
>     |
>     |
>    { dg-end-multiline-output "" } */
>


The first events "'int main()': events 1-2" vary in c++14 (get events 1-3).

>
> // c++14 with fully detailed output
>   ‘int main()’: events 1-3
>     |
>     |    8 | int main () {
>     |      |     ^~~~
>     |      |     |
>     |      |     (1) entry to ‘main’
>     |    9 |   std::shared_ptr<A> a;
>     |      |                      ~
>     |      |                      |
>     |      |                      (2)
> ‘a.std::shared_ptr<A>::<anonymous>.std::__shared_ptr<A,
> __gnu_cxx::_S_atomic>::_M_ptr’ is NULL
>     |   10 |   a->x = 4; /* { dg-line deref_a } */
>     |      |    ~~
>     |      |    |
>     |      |    (3) calling ‘std::__shared_ptr_access<A,
> __gnu_cxx::_S_atomic, false, false>::operator->’ from ‘main’
>

whereas c++17 and posterior give

> // c++17 with fully detailed output
>
// ./xg++ -fanalyzer
>  ../../gcc/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-never.C
>  -B. -shared-libgcc -fanalyzer-trim-diagnostics=never -std=c++17
>
  ‘int main()’: events 1-2
>     |
>     |    8 | int main () {
>     |      |     ^~~~
>     |      |     |
>     |      |     (1) entry to ‘main’
>     |    9 |   std::shared_ptr<A> a;
>     |   10 |   a->x = 4; /* { dg-line deref_a } */
>     |      |    ~~
>     |      |    |
>     |      |    (2) calling ‘std::__shared_ptr_access<A,
> __gnu_cxx::_S_atomic, false, false>::operator->’ from ‘main’
>

Is there a way to make dg-multiline-output check for a regex ? Or would
checking the multiline-output only for c++17 and c++20 be acceptable ?
This divergence results from two slightly different IPA:

// c++14 -fdump-ipa-analyzer
> // std::shared_ptr<A> a; becomes
> a.D.29392._M_ptr = 0B;
> a.D.29392._M_refcount._M_pi = 0B;
>

whereas in c++17

> // c++17 -fdump-ipa-analyzer
> // std::shared_ptr<A> a; becomes
> a = {};
>

I know shared_ptr limited support is a coincidence more than a feature, but
maybe there is still a way to make the above test case work ?
Otherwise, I'd fallback to a supported system function. If you have any
that you fancy, do tell me.

Thanks,
Benjamin.

---
 gcc/analyzer/analyzer.cc                      |   2 +-
 gcc/analyzer/analyzer.h                       |   1 +
 gcc/analyzer/analyzer.opt                     |   4 +
 gcc/analyzer/diagnostic-manager.cc            | 132 ++++++++++++++++++
 gcc/analyzer/diagnostic-manager.h             |   7 +
 .../analyzer/fanalyzer-trim-diagnostics-0.C   |  19 +++
 .../analyzer/fanalyzer-trim-diagnostics-1.C   |  28 ++++
 .../analyzer/fanalyzer-trim-diagnostics-2.C   |  44 ++++++
 .../fanalyzer-trim-diagnostics-default.C      |  21 +++
 .../fanalyzer-trim-diagnostics-never.C        |  44 ++++++
 .../fanalyzer-trim-diagnostics-system.C       |  19 +++
 11 files changed, 320 insertions(+), 1 deletion(-)
 create mode 100644
gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-0.C
 create mode 100644
gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-1.C
 create mode 100644
gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-2.C
 create mode 100644
gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-default.C
 create mode 100644
gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-never.C
 create mode 100644
gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-system.C

+   { dg-end-multiline-output "" } */
\ No newline at end of file

Comments

David Malcolm July 21, 2023, 10:04 p.m. UTC | #1
On Fri, 2023-07-21 at 17:35 +0200, Benjamin Priour wrote:
> Hi,
> 
> Upon David's request I've joined the in progress patch to the below
> email.
> I hope it makes more sense now.
> 
> Best,
> Benjamin.

Thanks for posting the work-in-progress patch; it makes the idea
clearer.

Some thoughts about this:

- I like the idea of defaulting to *not* showing events within system
headers, which the patch achieves
- I don't like the combination of never/system with maxdepth, in that
it seems complicated and I don't think a user is likely to experiment
with different depths.
- Hence I think it would work better as a simple boolean, perhaps
  "-fanalyzer-show-events-in-system-headers"
  or somesuch?  It seems like the sort of thing that we want to provide
a sensible default for, but have the option of turning off for
debugging the analyzer itself, but I don't expect an end-user to touch
that option.

FWIW the patch seems to have been mangled somewhat via email, so I
don't have a sense of what the actual output from patched analyzer
looks like.  What should we output to the user with -fanalyzer and no
other options for the case in PR 110543?  Currently, for
https://godbolt.org/z/sb9dM9Gqa trunk emits 12 events, of which
probably only this last one is useful:

  (12) dereference of NULL 'a.std::__shared_ptr_access<A, __gnu_cxx::_S_atomic, false, false>::operator->()'

What does the output look like with your patch?

Thanks
Dave





> 
> ---------- Forwarded message ---------
> From: Benjamin Priour <priour.be@gmail.com>
> Date: Tue, Jul 18, 2023 at 3:30 PM
> Subject: [RFC] analyzer: Add optional trim of the analyzer
> diagnostics
> going too deep [PR110543]
> To: <gcc-patches@gcc.gnu.org>, David Malcolm <dmalcolm@redhat.com>
> 
> 
> Hi,
> 
> I'd like to request comments on a patch I am writing for PR110543.
> The goal of this patch is to reduce the noise of the analyzer emitted
> diagnostics when dealing with
> system headers, or simply diagnostic paths that are too long. The new
> option only affects the display
> of the diagnostics, but doesn't hinder the actual analysis.
> 
> I've defaulted the new option to "system", thus preventing the
> diagnostic
> paths from showing system headers.
> "never" corresponds to the pre-patch behavior, whereas you can also
> specify
> an unsigned value <maxdepth>
> that prevents paths to go deeper than <maxdepth> frames.
> 
> fanalyzer-trim-diagnostics=
> > Common Joined RejectNegative ToLower
> > Var(flag_analyzer_trim_diagnostics)
> > Init("system")
> > -fanalyzer-trim-diagnostics=[never|system|<maxdepth>] Trim
> > diagnostics
> > path that are too long before emission.
> > 
> 
> Does it sounds reasonable and user-friendly ?
> 
> Regstrapping was a success against trunk, although one of the newly
> added
> test case fails for c++14.
> Note that the test case below was done with "never", thus behaves
> exactly
> as the pre-patch analyzer
> on x86_64-linux-gnu.
> 
> /* { dg-additional-options "-fdiagnostics-plain-output
> > -fdiagnostics-path-format=inline-events -fanalyzer-trim-
> > diagnostics=never"
> > } */
> > /* { dg-skip-if "" { c++98_only }  } */
> > 
> > #include <memory>
> > struct A {int x; int y;};
> > 
> > int main () {
> >   std::shared_ptr<A> a;
> >   a->x = 4; /* { dg-line deref_a } */
> >   /* { dg-warning "dereference of NULL" "" { target *-*-* } deref_a
> > } */
> > 
> >   return 0;
> > }
> > 
> > /* { dg-begin-multiline-output "" }
> >   'int main()': events 1-2
> >     |
> >     |
> >     +--> 'std::__shared_ptr_access<_Tp, _Lp, <anonymous>,
> > <anonymous>
> > > ::element_type* std::__shared_ptr_access<_Tp, _Lp, <anonymous>,
> > <anonymous> >::operator->() const [with _Tp = A;
> > __gnu_cxx::_Lock_policy
> > _Lp = __gnu_cxx::_S_atomic; bool <anonymous> = false; bool
> > <anonymous> =
> > false]': events 3-4
> >            |
> >            |
> >            +--> 'std::__shared_ptr_access<_Tp, _Lp, <anonymous>,
> > <anonymous> >::element_type* std::__shared_ptr_access<_Tp, _Lp,
> > <anonymous>, <anonymous> >::_M_get() const [with _Tp = A;
> > __gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic; bool
> > <anonymous> =
> > false; bool <anonymous> = false]': events 5-6
> >                   |
> >                   |
> >                   +--> 'std::__shared_ptr<_Tp, _Lp>::element_type*
> > std::__shared_ptr<_Tp, _Lp>::get() const [with _Tp = A;
> > __gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic]': events 7-8
> >                          |
> >                          |
> >                   <------+
> >                   |
> >                 'std::__shared_ptr_access<_Tp, _Lp, <anonymous>,
> > <anonymous> >::element_type* std::__shared_ptr_access<_Tp, _Lp,
> > <anonymous>, <anonymous> >::_M_get() const [with _Tp = A;
> > __gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic; bool
> > <anonymous> =
> > false; bool <anonymous> = false]': event 9
> >                   |
> >                   |
> >            <------+
> >            |
> >          'std::__shared_ptr_access<_Tp, _Lp, <anonymous>,
> > <anonymous>
> > > ::element_type* std::__shared_ptr_access<_Tp, _Lp, <anonymous>,
> > <anonymous> >::operator->() const [with _Tp = A;
> > __gnu_cxx::_Lock_policy
> > _Lp = __gnu_cxx::_S_atomic; bool <anonymous> = false; bool
> > <anonymous> =
> > false]': event 10
> >            |
> >            |
> >     <------+
> >     |
> >   'int main()': events 11-12
> >     |
> >     |
> >    { dg-end-multiline-output "" } */
> > 
> 
> 
> The first events "'int main()': events 1-2" vary in c++14 (get events
> 1-3).
> 
> > 
> > // c++14 with fully detailed output
> >   ‘int main()’: events 1-3
> >     |
> >     |    8 | int main () {
> >     |      |     ^~~~
> >     |      |     |
> >     |      |     (1) entry to ‘main’
> >     |    9 |   std::shared_ptr<A> a;
> >     |      |                      ~
> >     |      |                      |
> >     |      |                      (2)
> > ‘a.std::shared_ptr<A>::<anonymous>.std::__shared_ptr<A,
> > __gnu_cxx::_S_atomic>::_M_ptr’ is NULL
> >     |   10 |   a->x = 4; /* { dg-line deref_a } */
> >     |      |    ~~
> >     |      |    |
> >     |      |    (3) calling ‘std::__shared_ptr_access<A,
> > __gnu_cxx::_S_atomic, false, false>::operator->’ from ‘main’
> > 
> 
> whereas c++17 and posterior give
> 
> > // c++17 with fully detailed output
> > 
> // ./xg++ -fanalyzer
> >  ../../gcc/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-
> > diagnostics-never.C
> >  -B. -shared-libgcc -fanalyzer-trim-diagnostics=never -std=c++17
> > 
>   ‘int main()’: events 1-2
> >     |
> >     |    8 | int main () {
> >     |      |     ^~~~
> >     |      |     |
> >     |      |     (1) entry to ‘main’
> >     |    9 |   std::shared_ptr<A> a;
> >     |   10 |   a->x = 4; /* { dg-line deref_a } */
> >     |      |    ~~
> >     |      |    |
> >     |      |    (2) calling ‘std::__shared_ptr_access<A,
> > __gnu_cxx::_S_atomic, false, false>::operator->’ from ‘main’
> > 
> 
> Is there a way to make dg-multiline-output check for a regex ? Or
> would
> checking the multiline-output only for c++17 and c++20 be acceptable
> ?
> This divergence results from two slightly different IPA:
> 
> // c++14 -fdump-ipa-analyzer
> > // std::shared_ptr<A> a; becomes
> > a.D.29392._M_ptr = 0B;
> > a.D.29392._M_refcount._M_pi = 0B;
> > 
> 
> whereas in c++17
> 
> > // c++17 -fdump-ipa-analyzer
> > // std::shared_ptr<A> a; becomes
> > a = {};
> > 
> 
> I know shared_ptr limited support is a coincidence more than a
> feature, but
> maybe there is still a way to make the above test case work ?
> Otherwise, I'd fallback to a supported system function. If you have
> any
> that you fancy, do tell me.
> 
> Thanks,
> Benjamin.
> 
> ---
>  gcc/analyzer/analyzer.cc                      |   2 +-
>  gcc/analyzer/analyzer.h                       |   1 +
>  gcc/analyzer/analyzer.opt                     |   4 +
>  gcc/analyzer/diagnostic-manager.cc            | 132
> ++++++++++++++++++
>  gcc/analyzer/diagnostic-manager.h             |   7 +
>  .../analyzer/fanalyzer-trim-diagnostics-0.C   |  19 +++
>  .../analyzer/fanalyzer-trim-diagnostics-1.C   |  28 ++++
>  .../analyzer/fanalyzer-trim-diagnostics-2.C   |  44 ++++++
>  .../fanalyzer-trim-diagnostics-default.C      |  21 +++
>  .../fanalyzer-trim-diagnostics-never.C        |  44 ++++++
>  .../fanalyzer-trim-diagnostics-system.C       |  19 +++
>  11 files changed, 320 insertions(+), 1 deletion(-)
>  create mode 100644
> gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-0.C
>  create mode 100644
> gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-1.C
>  create mode 100644
> gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-2.C
>  create mode 100644
> gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-default.C
>  create mode 100644
> gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-never.C
>  create mode 100644
> gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-system.C
> 
> diff --git a/gcc/analyzer/analyzer.cc b/gcc/analyzer/analyzer.cc
> index 5091fb7a583..b27d8e359db 100644
> --- a/gcc/analyzer/analyzer.cc
> +++ b/gcc/analyzer/analyzer.cc
> @@ -274,7 +274,7 @@ is_named_call_p (const_tree fndecl, const char
> *funcname)
>     Compare with cp/typeck.cc: decl_in_std_namespace_p, but this
> doesn't
>     rely on being the C++ FE (or handle inline namespaces inside of
> std).
>  */
> 
> -static inline bool
> +bool
>  is_std_function_p (const_tree fndecl)
>  {
>    tree name_decl = DECL_NAME (fndecl);
> diff --git a/gcc/analyzer/analyzer.h b/gcc/analyzer/analyzer.h
> index 579517c23e6..31597079153 100644
> --- a/gcc/analyzer/analyzer.h
> +++ b/gcc/analyzer/analyzer.h
> @@ -386,6 +386,7 @@ extern bool is_special_named_call_p (const gcall
> *call,
> const char *funcname,
>  extern bool is_named_call_p (const_tree fndecl, const char
> *funcname);
>  extern bool is_named_call_p (const_tree fndecl, const char
> *funcname,
>       const gcall *call, unsigned int num_args);
> +extern bool is_std_function_p (const_tree fndecl);
>  extern bool is_std_named_call_p (const_tree fndecl, const char
> *funcname);
>  extern bool is_std_named_call_p (const_tree fndecl, const char
> *funcname,
>   const gcall *call, unsigned int num_args);
> diff --git a/gcc/analyzer/analyzer.opt b/gcc/analyzer/analyzer.opt
> index 2760aaa8151..78ff2c07483 100644
> --- a/gcc/analyzer/analyzer.opt
> +++ b/gcc/analyzer/analyzer.opt
> @@ -362,4 +362,8 @@ fdump-analyzer-untracked
>  Common RejectNegative Var(flag_dump_analyzer_untracked)
>  Emit custom warnings with internal details intended for analyzer
> developers.
> 
> +fanalyzer-trim-diagnostics=
> +Common Joined RejectNegative ToLower
> Var(flag_analyzer_trim_diagnostics)
> Init("system")
> +-fanalyzer-trim-diagnostics=[never|system|<maxdepth>] Trim
> diagnostics
> path that are too long before emission.
> +
>  ; This comment is to ensure we retain the blank line above.
> diff --git a/gcc/analyzer/diagnostic-manager.cc
> b/gcc/analyzer/diagnostic-manager.cc
> index cfca305d552..d946ad5bac7 100644
> --- a/gcc/analyzer/diagnostic-manager.cc
> +++ b/gcc/analyzer/diagnostic-manager.cc
> @@ -20,9 +20,11 @@ along with GCC; see the file COPYING3.  If not see
> 
>  #include "config.h"
>  #define INCLUDE_MEMORY
> +#define INCLUDE_VECTOR
>  #include "system.h"
>  #include "coretypes.h"
>  #include "tree.h"
> +#include "input.h"
>  #include "pretty-print.h"
>  #include "gcc-rich-location.h"
>  #include "gimple-pretty-print.h"
> @@ -60,6 +62,37 @@ along with GCC; see the file COPYING3.  If not see
> 
>  #if ENABLE_ANALYZER
> 
> +/* Return positive value of fanalyzer-trim-diagnostics if a depth
> was given
> +  or a negative value otherwise.  */
> +
> +long trim_diagnostics_depth ()
> +{
> +  if (trim_diagnostics_system_p () ||  trim_diagnostics_never_p ())
> +    return -1;
> +
> +  char *end = NULL;
> +  long depth = strtol (flag_analyzer_trim_diagnostics, &end, 10);
> +  gcc_assert (end == NULL || *end == '\0');
> +  gcc_assert (!(end == flag_analyzer_trim_diagnostics && depth ==
> 0));
> +  return depth;
> +}
> +
> +/* Return true if fanalyzer-trim-diagnostics is set to "system".  */
> +
> +bool trim_diagnostics_system_p ()
> +{
> +  int len = strlen (flag_analyzer_trim_diagnostics);
> +  return len == 6 && strcmp (flag_analyzer_trim_diagnostics,
> "system") ==
> 0;
> +}
> +
> +/* Return true if fanalyzer-trim-diagnostics is set to "never".  */
> +
> +bool trim_diagnostics_never_p ()
> +{
> +  int len = strlen (flag_analyzer_trim_diagnostics);
> +  return len == 5 && strcmp (flag_analyzer_trim_diagnostics,
> "never") == 0;
> +}
> +
>  namespace ana {
> 
>  class feasible_worklist;
> @@ -2281,6 +2314,8 @@ diagnostic_manager::prune_path (checker_path
> *path,
>    path->maybe_log (get_logger (), "path");
>    prune_for_sm_diagnostic (path, sm, sval, state);
>    prune_interproc_events (path);
> +  if (! trim_diagnostics_never_p ())
> +    trim_diagnostic_path (path);
>    consolidate_conditions (path);
>    finish_pruning (path);
>    path->maybe_log (get_logger (), "pruned");
> @@ -2667,6 +2702,103 @@ diagnostic_manager::prune_interproc_events
> (checker_path *path) const
>    while (changed);
>  }
> 
> +void
> +diagnostic_manager::trim_diagnostic_path (checker_path *path) const
> +{
> +  if (trim_diagnostics_system_p ())
> +    prune_system_headers (path);
> +  else
> +    prune_events_too_deep (path);
> +}
> +
> +/* Remove everything within ]callsite, IDX].  */
> +static void
> +prune_within_frame (checker_path *path, int &idx)
> +{
> +  int nesting = 1;
> +  while (idx >= 0 && nesting != 0)
> +    {
> +      if (path->get_checker_event (idx)->is_call_p ())
> +        nesting--;
> +      else if (path->get_checker_event (idx)->is_return_p ())
> +        nesting++;
> +
> +      path->delete_event (idx--);
> +    }
> +}
> +
> +void
> +diagnostic_manager::prune_system_headers (checker_path *path) const
> +{
> +  int idx = (signed)path->num_events () - 1;
> +  while (idx >= 0)
> +    {
> +      const checker_event *event = path->get_checker_event (idx);
> +      /* Prune everything between [..., system call, (...), system
> return,
> ...].  */
> +      if (event->is_return_p ()
> +          && in_system_header_at (event->get_location ()))
> +      {
> +        int ret_idx = idx;
> +        prune_within_frame (path, idx);
> +
> +        if (get_logger ())
> +        {
> +          label_text desc
> +            (path->get_checker_event (idx)->get_desc (false));
> +          log ("filtering event %i-%i:"
> +              " system header event: %s",
> +              idx, ret_idx, desc.get ());
> +        }
> +        // delete callsite
> +        if (idx >= 0)
> +          path->delete_event (idx);
> +      }
> +
> +      idx--;
> +    }
> +}
> +
> +void
> +diagnostic_manager::prune_events_too_deep (checker_path *path) const
> +{
> +  long depth = 0;
> +  long maxdepth = trim_diagnostics_depth ();
> +  gcc_assert (maxdepth >= 0);
> +  int idx = (signed)path->num_events () - 1;
> +  if (idx >= 0)
> +  {
> +    depth = path->get_checker_event (0)->get_stack_depth ();
> +    maxdepth += depth;
> +  }
> +
> +  while (idx >= 0)
> +    {
> +      const checker_event *event = path->get_checker_event (idx);
> +      /* Prune everything between [..., call too deep, (...), return
> too
> deep, ...].  */
> +      if (event->is_return_p ()
> +          && event->get_stack_depth () > maxdepth)
> +      {
> +        int ret_idx = idx;
> +        prune_within_frame (path, idx);
> +
> +        if (get_logger ())
> +        {
> +          label_text desc
> +            (path->get_checker_event (idx)->get_desc (false));
> +          log ("filtering event %i-%i:"
> +              " event too deep: %s",
> +              idx, ret_idx, desc.get ());
> +        }
> +        // delete callsite
> +        if (idx >= 0)
> +          path->delete_event (idx);
> +      }
> +
> +      idx--;
> +    }
> +
> +}
> +
>  /* Return true iff event IDX within PATH is on the same line as
> REF_EXP_LOC.  */
> 
>  static bool
> diff --git a/gcc/analyzer/diagnostic-manager.h
> b/gcc/analyzer/diagnostic-manager.h
> index 9b3e903fc5d..9afef0975be 100644
> --- a/gcc/analyzer/diagnostic-manager.h
> +++ b/gcc/analyzer/diagnostic-manager.h
> @@ -21,6 +21,10 @@ along with GCC; see the file COPYING3.  If not see
>  #ifndef GCC_ANALYZER_DIAGNOSTIC_MANAGER_H
>  #define GCC_ANALYZER_DIAGNOSTIC_MANAGER_H
> 
> +long trim_diagnostics_depth ();
> +bool trim_diagnostics_system_p ();
> +bool trim_diagnostics_never_p ();
> +
>  namespace ana {
> 
>  class epath_finder;
> @@ -180,6 +184,9 @@ private:
>   state_machine::state_t state) const;
>    void update_for_unsuitable_sm_exprs (tree *expr) const;
>    void prune_interproc_events (checker_path *path) const;
> +  void trim_diagnostic_path (checker_path *path) const;
> +  void prune_system_headers (checker_path *path) const;
> +  void prune_events_too_deep (checker_path *path) const;
>    void consolidate_conditions (checker_path *path) const;
>    void finish_pruning (checker_path *path) const;
> 
> diff --git a/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-
> diagnostics-0.C
> b/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-0.C
> new file mode 100644
> index 00000000000..ddf39ed1690
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-0.C
> @@ -0,0 +1,19 @@
> +/* { dg-additional-options "-fdiagnostics-plain-output
> -fdiagnostics-path-format=inline-events -fanalyzer-trim-
> diagnostics=0" } */
> +/* { dg-skip-if "" { c++98_only }  } */
> +
> +#include <memory>
> +
> +struct A {int x; int y;};
> +
> +int main () {
> +  std::shared_ptr<A> a;
> +  a->x = 4; /* { dg-line deref_a } */
> +  /* { dg-warning "dereference of NULL" "" { target *-*-* } deref_a
> } */
> +
> +  return 0;
> +}
> +/* { dg-begin-multiline-output "" }
> +  'int main()': events 1-2
> +    |
> +    |
> +   { dg-end-multiline-output "" } */
> \ No newline at end of file
> diff --git a/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-
> diagnostics-1.C
> b/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-1.C
> new file mode 100644
> index 00000000000..83a132078a1
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-1.C
> @@ -0,0 +1,28 @@
> +/* { dg-additional-options "-fdiagnostics-plain-output
> -fdiagnostics-path-format=inline-events -fanalyzer-trim-
> diagnostics=1" } */
> +/* { dg-skip-if "" { c++98_only }  } */
> +
> +#include <memory>
> +
> +struct A {int x; int y;};
> +
> +int main () {
> +  std::shared_ptr<A> a;
> +  a->x = 4; /* { dg-line deref_a } */
> +  /* { dg-warning "dereference of NULL" "" { target *-*-* } deref_a
> } */
> +
> +  return 0;
> +}
> +
> +/* { dg-begin-multiline-output "" }
> +  'int main()': events 1-2
> +    |
> +    |
> +    +--> 'std::__shared_ptr_access<_Tp, _Lp, <anonymous>,
> <anonymous>
> > ::element_type* std::__shared_ptr_access<_Tp, _Lp, <anonymous>,
> <anonymous> >::operator->() const [with _Tp = A;
> __gnu_cxx::_Lock_policy
> _Lp = __gnu_cxx::_S_atomic; bool <anonymous> = false; bool
> <anonymous> =
> false]': event 3
> +           |
> +           |
> +    <------+
> +    |
> +  'int main()': events 4-5
> +    |
> +    |
> +   { dg-end-multiline-output "" } */
> \ No newline at end of file
> diff --git a/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-
> diagnostics-2.C
> b/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-2.C
> new file mode 100644
> index 00000000000..cf065929ec9
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-2.C
> @@ -0,0 +1,44 @@
> +/* { dg-additional-options "-fdiagnostics-plain-output
> -fdiagnostics-path-format=inline-events -fanalyzer-trim-
> diagnostics=2" } */
> +/* { dg-skip-if "" { c++98_only }  } */
> +
> +#include <memory>
> +
> +struct A {int x; int y;};
> +
> +int main () {
> +  std::shared_ptr<A> a;
> +  a->x = 4; /* { dg-line deref_a } */
> +  /* { dg-warning "dereference of NULL" "" { target *-*-* } deref_a
> } */
> +
> +  return 0;
> +}
> +
> +/* { dg-begin-multiline-output "" }
> +  'int main()': events 1-2
> +    |
> +    |
> +    +--> 'std::__shared_ptr_access<_Tp, _Lp, <anonymous>,
> <anonymous>
> > ::element_type* std::__shared_ptr_access<_Tp, _Lp, <anonymous>,
> <anonymous> >::operator->() const [with _Tp = A;
> __gnu_cxx::_Lock_policy
> _Lp = __gnu_cxx::_S_atomic; bool <anonymous> = false; bool
> <anonymous> =
> false]': events 3-4
> +           |
> +           |
> +           +--> 'std::__shared_ptr_access<_Tp, _Lp, <anonymous>,
> <anonymous> >::element_type* std::__shared_ptr_access<_Tp, _Lp,
> <anonymous>, <anonymous> >::_M_get() const [with _Tp = A;
> __gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic; bool <anonymous>
> =
> false; bool <anonymous> = false]': events 5-6
> +                  |
> +                  |
> +                  +--> 'std::__shared_ptr<_Tp, _Lp>::element_type*
> std::__shared_ptr<_Tp, _Lp>::get() const [with _Tp = A;
> __gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic]': events 7-8
> +                         |
> +                         |
> +                  <------+
> +                  |
> +                'std::__shared_ptr_access<_Tp, _Lp, <anonymous>,
> <anonymous> >::element_type* std::__shared_ptr_access<_Tp, _Lp,
> <anonymous>, <anonymous> >::_M_get() const [with _Tp = A;
> __gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic; bool <anonymous>
> =
> false; bool <anonymous> = false]': event 9
> +                  |
> +                  |
> +           <------+
> +           |
> +         'std::__shared_ptr_access<_Tp, _Lp, <anonymous>,
> <anonymous>
> > ::element_type* std::__shared_ptr_access<_Tp, _Lp, <anonymous>,
> <anonymous> >::operator->() const [with _Tp = A;
> __gnu_cxx::_Lock_policy
> _Lp = __gnu_cxx::_S_atomic; bool <anonymous> = false; bool
> <anonymous> =
> false]': event 10
> +           |
> +           |
> +    <------+
> +    |
> +  'int main()': events 11-12
> +    |
> +    |
> +   { dg-end-multiline-output "" } */
> \ No newline at end of file
> diff --git
> a/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-default.C
> b/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-default.C
> new file mode 100644
> index 00000000000..fc271309d4e
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-
> default.C
> @@ -0,0 +1,21 @@
> +/* { dg-additional-options "-O0 -fdiagnostics-plain-output
> -fdiagnostics-path-format=inline-events" } */
> +/* { dg-skip-if "" { c++98_only }  } */
> +
> +// Must behave like -fanalyzer-trim-diagnostics=system
> +
> +#include <memory>
> +
> +struct A {int x; int y;};
> +
> +int main () {
> +  std::shared_ptr<A> a;
> +  a->x = 4; /* { dg-line deref_a } */
> +  /* { dg-warning "dereference of NULL" "" { target *-*-* } deref_a
> } */
> +
> +  return 0;
> +}
> +/* { dg-begin-multiline-output "" }
> +  'int main()': events 1-2
> +    |
> +    |
> +   { dg-end-multiline-output "" } */
> \ No newline at end of file
> diff --git
> a/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-never.C
> b/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-never.C
> new file mode 100644
> index 00000000000..a69416608da
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-
> never.C
> @@ -0,0 +1,44 @@
> +/* { dg-additional-options "-fdiagnostics-plain-output
> -fdiagnostics-path-format=inline-events -fanalyzer-trim-
> diagnostics=never"
> } */
> +/* { dg-skip-if "" { c++98_only }  } */
> +
> +#include <memory>
> +
> +struct A {int x; int y;};
> +
> +int main () {
> +  std::shared_ptr<A> a;
> +  a->x = 4; /* { dg-line deref_a } */
> +  /* { dg-warning "dereference of NULL" "" { target *-*-* } deref_a
> } */
> +
> +  return 0;
> +}
> +
> +/* { dg-begin-multiline-output "" }
> +  'int main()': events 1-2
> +    |
> +    |
> +    +--> 'std::__shared_ptr_access<_Tp, _Lp, <anonymous>,
> <anonymous>
> > ::element_type* std::__shared_ptr_access<_Tp, _Lp, <anonymous>,
> <anonymous> >::operator->() const [with _Tp = A;
> __gnu_cxx::_Lock_policy
> _Lp = __gnu_cxx::_S_atomic; bool <anonymous> = false; bool
> <anonymous> =
> false]': events 3-4
> +           |
> +           |
> +           +--> 'std::__shared_ptr_access<_Tp, _Lp, <anonymous>,
> <anonymous> >::element_type* std::__shared_ptr_access<_Tp, _Lp,
> <anonymous>, <anonymous> >::_M_get() const [with _Tp = A;
> __gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic; bool <anonymous>
> =
> false; bool <anonymous> = false]': events 5-6
> +                  |
> +                  |
> +                  +--> 'std::__shared_ptr<_Tp, _Lp>::element_type*
> std::__shared_ptr<_Tp, _Lp>::get() const [with _Tp = A;
> __gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic]': events 7-8
> +                         |
> +                         |
> +                  <------+
> +                  |
> +                'std::__shared_ptr_access<_Tp, _Lp, <anonymous>,
> <anonymous> >::element_type* std::__shared_ptr_access<_Tp, _Lp,
> <anonymous>, <anonymous> >::_M_get() const [with _Tp = A;
> __gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic; bool <anonymous>
> =
> false; bool <anonymous> = false]': event 9
> +                  |
> +                  |
> +           <------+
> +           |
> +         'std::__shared_ptr_access<_Tp, _Lp, <anonymous>,
> <anonymous>
> > ::element_type* std::__shared_ptr_access<_Tp, _Lp, <anonymous>,
> <anonymous> >::operator->() const [with _Tp = A;
> __gnu_cxx::_Lock_policy
> _Lp = __gnu_cxx::_S_atomic; bool <anonymous> = false; bool
> <anonymous> =
> false]': event 10
> +           |
> +           |
> +    <------+
> +    |
> +  'int main()': events 11-12
> +    |
> +    |
> +   { dg-end-multiline-output "" } */
> \ No newline at end of file
> diff --git
> a/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-system.C
> b/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-system.C
> new file mode 100644
> index 00000000000..22fadc84e75
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-
> system.C
> @@ -0,0 +1,19 @@
> +/* { dg-additional-options "-fdiagnostics-plain-output
> -fdiagnostics-path-format=inline-events -fanalyzer-trim-
> diagnostics=system"
> } */
> +/* { dg-skip-if "" { c++98_only }  } */
> +
> +#include <memory>
> +
> +struct A {int x; int y;};
> +
> +int main () {
> +  std::shared_ptr<A> a;
> +  a->x = 4; /* { dg-line deref_a } */
> +  /* { dg-warning "dereference of NULL" "" { target *-*-* } deref_a
> } */
> +
> +  return 0;
> +}
> +/* { dg-begin-multiline-output "" }
> +  'int main()': events 1-2
> +    |
> +    |
> +   { dg-end-multiline-output "" } */
> \ No newline at end of file
Prathamesh Kulkarni July 22, 2023, 2:47 p.m. UTC | #2
On Fri, 21 Jul 2023 at 21:05, Benjamin Priour via Gcc-patches
<gcc-patches@gcc.gnu.org> wrote:
>
> Hi,
>
> Upon David's request I've joined the in progress patch to the below email.
> I hope it makes more sense now.
>
> Best,
> Benjamin.
>
> ---------- Forwarded message ---------
> From: Benjamin Priour <priour.be@gmail.com>
> Date: Tue, Jul 18, 2023 at 3:30 PM
> Subject: [RFC] analyzer: Add optional trim of the analyzer diagnostics
> going too deep [PR110543]
> To: <gcc-patches@gcc.gnu.org>, David Malcolm <dmalcolm@redhat.com>
>
>
> Hi,
>
> I'd like to request comments on a patch I am writing for PR110543.
> The goal of this patch is to reduce the noise of the analyzer emitted
> diagnostics when dealing with
> system headers, or simply diagnostic paths that are too long. The new
> option only affects the display
> of the diagnostics, but doesn't hinder the actual analysis.
>
> I've defaulted the new option to "system", thus preventing the diagnostic
> paths from showing system headers.
> "never" corresponds to the pre-patch behavior, whereas you can also specify
> an unsigned value <maxdepth>
> that prevents paths to go deeper than <maxdepth> frames.
>
> fanalyzer-trim-diagnostics=
> > Common Joined RejectNegative ToLower Var(flag_analyzer_trim_diagnostics)
> > Init("system")
> > -fanalyzer-trim-diagnostics=[never|system|<maxdepth>] Trim diagnostics
> > path that are too long before emission.
> >
>
> Does it sounds reasonable and user-friendly ?
>
> Regstrapping was a success against trunk, although one of the newly added
> test case fails for c++14.
> Note that the test case below was done with "never", thus behaves exactly
> as the pre-patch analyzer
> on x86_64-linux-gnu.
>
> /* { dg-additional-options "-fdiagnostics-plain-output
> > -fdiagnostics-path-format=inline-events -fanalyzer-trim-diagnostics=never"
> > } */
> > /* { dg-skip-if "" { c++98_only }  } */
> >
> > #include <memory>
> > struct A {int x; int y;};
> >
> > int main () {
> >   std::shared_ptr<A> a;
> >   a->x = 4; /* { dg-line deref_a } */
> >   /* { dg-warning "dereference of NULL" "" { target *-*-* } deref_a } */
> >
> >   return 0;
> > }
> >
> > /* { dg-begin-multiline-output "" }
> >   'int main()': events 1-2
> >     |
> >     |
> >     +--> 'std::__shared_ptr_access<_Tp, _Lp, <anonymous>, <anonymous>
> > >::element_type* std::__shared_ptr_access<_Tp, _Lp, <anonymous>,
> > <anonymous> >::operator->() const [with _Tp = A; __gnu_cxx::_Lock_policy
> > _Lp = __gnu_cxx::_S_atomic; bool <anonymous> = false; bool <anonymous> =
> > false]': events 3-4
> >            |
> >            |
> >            +--> 'std::__shared_ptr_access<_Tp, _Lp, <anonymous>,
> > <anonymous> >::element_type* std::__shared_ptr_access<_Tp, _Lp,
> > <anonymous>, <anonymous> >::_M_get() const [with _Tp = A;
> > __gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic; bool <anonymous> =
> > false; bool <anonymous> = false]': events 5-6
> >                   |
> >                   |
> >                   +--> 'std::__shared_ptr<_Tp, _Lp>::element_type*
> > std::__shared_ptr<_Tp, _Lp>::get() const [with _Tp = A;
> > __gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic]': events 7-8
> >                          |
> >                          |
> >                   <------+
> >                   |
> >                 'std::__shared_ptr_access<_Tp, _Lp, <anonymous>,
> > <anonymous> >::element_type* std::__shared_ptr_access<_Tp, _Lp,
> > <anonymous>, <anonymous> >::_M_get() const [with _Tp = A;
> > __gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic; bool <anonymous> =
> > false; bool <anonymous> = false]': event 9
> >                   |
> >                   |
> >            <------+
> >            |
> >          'std::__shared_ptr_access<_Tp, _Lp, <anonymous>, <anonymous>
> > >::element_type* std::__shared_ptr_access<_Tp, _Lp, <anonymous>,
> > <anonymous> >::operator->() const [with _Tp = A; __gnu_cxx::_Lock_policy
> > _Lp = __gnu_cxx::_S_atomic; bool <anonymous> = false; bool <anonymous> =
> > false]': event 10
> >            |
> >            |
> >     <------+
> >     |
> >   'int main()': events 11-12
> >     |
> >     |
> >    { dg-end-multiline-output "" } */
> >
>
>
> The first events "'int main()': events 1-2" vary in c++14 (get events 1-3).
>
> >
> > // c++14 with fully detailed output
> >   ‘int main()’: events 1-3
> >     |
> >     |    8 | int main () {
> >     |      |     ^~~~
> >     |      |     |
> >     |      |     (1) entry to ‘main’
> >     |    9 |   std::shared_ptr<A> a;
> >     |      |                      ~
> >     |      |                      |
> >     |      |                      (2)
> > ‘a.std::shared_ptr<A>::<anonymous>.std::__shared_ptr<A,
> > __gnu_cxx::_S_atomic>::_M_ptr’ is NULL
> >     |   10 |   a->x = 4; /* { dg-line deref_a } */
> >     |      |    ~~
> >     |      |    |
> >     |      |    (3) calling ‘std::__shared_ptr_access<A,
> > __gnu_cxx::_S_atomic, false, false>::operator->’ from ‘main’
> >
>
> whereas c++17 and posterior give
>
> > // c++17 with fully detailed output
> >
> // ./xg++ -fanalyzer
> >  ../../gcc/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-never.C
> >  -B. -shared-libgcc -fanalyzer-trim-diagnostics=never -std=c++17
> >
>   ‘int main()’: events 1-2
> >     |
> >     |    8 | int main () {
> >     |      |     ^~~~
> >     |      |     |
> >     |      |     (1) entry to ‘main’
> >     |    9 |   std::shared_ptr<A> a;
> >     |   10 |   a->x = 4; /* { dg-line deref_a } */
> >     |      |    ~~
> >     |      |    |
> >     |      |    (2) calling ‘std::__shared_ptr_access<A,
> > __gnu_cxx::_S_atomic, false, false>::operator->’ from ‘main’
> >
>
> Is there a way to make dg-multiline-output check for a regex ? Or would
> checking the multiline-output only for c++17 and c++20 be acceptable ?
> This divergence results from two slightly different IPA:
>
> // c++14 -fdump-ipa-analyzer
> > // std::shared_ptr<A> a; becomes
> > a.D.29392._M_ptr = 0B;
> > a.D.29392._M_refcount._M_pi = 0B;
> >
>
> whereas in c++17
>
> > // c++17 -fdump-ipa-analyzer
> > // std::shared_ptr<A> a; becomes
> > a = {};
> >
>
> I know shared_ptr limited support is a coincidence more than a feature, but
> maybe there is still a way to make the above test case work ?
> Otherwise, I'd fallback to a supported system function. If you have any
> that you fancy, do tell me.
>
> Thanks,
> Benjamin.
>
> ---
>  gcc/analyzer/analyzer.cc                      |   2 +-
>  gcc/analyzer/analyzer.h                       |   1 +
>  gcc/analyzer/analyzer.opt                     |   4 +
>  gcc/analyzer/diagnostic-manager.cc            | 132 ++++++++++++++++++
>  gcc/analyzer/diagnostic-manager.h             |   7 +
>  .../analyzer/fanalyzer-trim-diagnostics-0.C   |  19 +++
>  .../analyzer/fanalyzer-trim-diagnostics-1.C   |  28 ++++
>  .../analyzer/fanalyzer-trim-diagnostics-2.C   |  44 ++++++
>  .../fanalyzer-trim-diagnostics-default.C      |  21 +++
>  .../fanalyzer-trim-diagnostics-never.C        |  44 ++++++
>  .../fanalyzer-trim-diagnostics-system.C       |  19 +++
>  11 files changed, 320 insertions(+), 1 deletion(-)
>  create mode 100644
> gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-0.C
>  create mode 100644
> gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-1.C
>  create mode 100644
> gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-2.C
>  create mode 100644
> gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-default.C
>  create mode 100644
> gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-never.C
>  create mode 100644
> gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-system.C
>
> diff --git a/gcc/analyzer/analyzer.cc b/gcc/analyzer/analyzer.cc
> index 5091fb7a583..b27d8e359db 100644
> --- a/gcc/analyzer/analyzer.cc
> +++ b/gcc/analyzer/analyzer.cc
> @@ -274,7 +274,7 @@ is_named_call_p (const_tree fndecl, const char
> *funcname)
>     Compare with cp/typeck.cc: decl_in_std_namespace_p, but this doesn't
>     rely on being the C++ FE (or handle inline namespaces inside of std).
>  */
>
> -static inline bool
> +bool
>  is_std_function_p (const_tree fndecl)
>  {
>    tree name_decl = DECL_NAME (fndecl);
> diff --git a/gcc/analyzer/analyzer.h b/gcc/analyzer/analyzer.h
> index 579517c23e6..31597079153 100644
> --- a/gcc/analyzer/analyzer.h
> +++ b/gcc/analyzer/analyzer.h
> @@ -386,6 +386,7 @@ extern bool is_special_named_call_p (const gcall *call,
> const char *funcname,
>  extern bool is_named_call_p (const_tree fndecl, const char *funcname);
>  extern bool is_named_call_p (const_tree fndecl, const char *funcname,
>       const gcall *call, unsigned int num_args);
> +extern bool is_std_function_p (const_tree fndecl);
>  extern bool is_std_named_call_p (const_tree fndecl, const char *funcname);
>  extern bool is_std_named_call_p (const_tree fndecl, const char *funcname,
>   const gcall *call, unsigned int num_args);
> diff --git a/gcc/analyzer/analyzer.opt b/gcc/analyzer/analyzer.opt
> index 2760aaa8151..78ff2c07483 100644
> --- a/gcc/analyzer/analyzer.opt
> +++ b/gcc/analyzer/analyzer.opt
> @@ -362,4 +362,8 @@ fdump-analyzer-untracked
>  Common RejectNegative Var(flag_dump_analyzer_untracked)
>  Emit custom warnings with internal details intended for analyzer
> developers.
>
> +fanalyzer-trim-diagnostics=
> +Common Joined RejectNegative ToLower Var(flag_analyzer_trim_diagnostics)
> Init("system")
> +-fanalyzer-trim-diagnostics=[never|system|<maxdepth>] Trim diagnostics
> path that are too long before emission.
> +
>  ; This comment is to ensure we retain the blank line above.
> diff --git a/gcc/analyzer/diagnostic-manager.cc
> b/gcc/analyzer/diagnostic-manager.cc
> index cfca305d552..d946ad5bac7 100644
> --- a/gcc/analyzer/diagnostic-manager.cc
> +++ b/gcc/analyzer/diagnostic-manager.cc
> @@ -20,9 +20,11 @@ along with GCC; see the file COPYING3.  If not see
>
>  #include "config.h"
>  #define INCLUDE_MEMORY
> +#define INCLUDE_VECTOR
>  #include "system.h"
>  #include "coretypes.h"
>  #include "tree.h"
> +#include "input.h"
>  #include "pretty-print.h"
>  #include "gcc-rich-location.h"
>  #include "gimple-pretty-print.h"
> @@ -60,6 +62,37 @@ along with GCC; see the file COPYING3.  If not see
>
>  #if ENABLE_ANALYZER
>
> +/* Return positive value of fanalyzer-trim-diagnostics if a depth was given
> +  or a negative value otherwise.  */
> +
> +long trim_diagnostics_depth ()
> +{
> +  if (trim_diagnostics_system_p () ||  trim_diagnostics_never_p ())
> +    return -1;
> +
> +  char *end = NULL;
> +  long depth = strtol (flag_analyzer_trim_diagnostics, &end, 10);
> +  gcc_assert (end == NULL || *end == '\0');
> +  gcc_assert (!(end == flag_analyzer_trim_diagnostics && depth == 0));
> +  return depth;
> +}
> +
> +/* Return true if fanalyzer-trim-diagnostics is set to "system".  */
> +
> +bool trim_diagnostics_system_p ()
> +{
> +  int len = strlen (flag_analyzer_trim_diagnostics);
> +  return len == 6 && strcmp (flag_analyzer_trim_diagnostics, "system") ==
> 0;
> +}
> +
> +/* Return true if fanalyzer-trim-diagnostics is set to "never".  */
> +
> +bool trim_diagnostics_never_p ()
> +{
> +  int len = strlen (flag_analyzer_trim_diagnostics);
> +  return len == 5 && strcmp (flag_analyzer_trim_diagnostics, "never") == 0;
> +}
Hi Benjamin,
Just wondering if the len check is necessary here ?
IIUC, strcmp should handle the case correctly when the strings are of
different lengths ?
Also, I suppose it'd be better to merge above two functions, taking
the string to match against ("system" / "never") as argument.

Thanks,
Prathamesh
> +
>  namespace ana {
>
>  class feasible_worklist;
> @@ -2281,6 +2314,8 @@ diagnostic_manager::prune_path (checker_path *path,
>    path->maybe_log (get_logger (), "path");
>    prune_for_sm_diagnostic (path, sm, sval, state);
>    prune_interproc_events (path);
> +  if (! trim_diagnostics_never_p ())
> +    trim_diagnostic_path (path);
>    consolidate_conditions (path);
>    finish_pruning (path);
>    path->maybe_log (get_logger (), "pruned");
> @@ -2667,6 +2702,103 @@ diagnostic_manager::prune_interproc_events
> (checker_path *path) const
>    while (changed);
>  }
>
> +void
> +diagnostic_manager::trim_diagnostic_path (checker_path *path) const
> +{
> +  if (trim_diagnostics_system_p ())
> +    prune_system_headers (path);
> +  else
> +    prune_events_too_deep (path);
> +}
> +
> +/* Remove everything within ]callsite, IDX].  */
> +static void
> +prune_within_frame (checker_path *path, int &idx)
> +{
> +  int nesting = 1;
> +  while (idx >= 0 && nesting != 0)
> +    {
> +      if (path->get_checker_event (idx)->is_call_p ())
> +        nesting--;
> +      else if (path->get_checker_event (idx)->is_return_p ())
> +        nesting++;
> +
> +      path->delete_event (idx--);
> +    }
> +}
> +
> +void
> +diagnostic_manager::prune_system_headers (checker_path *path) const
> +{
> +  int idx = (signed)path->num_events () - 1;
> +  while (idx >= 0)
> +    {
> +      const checker_event *event = path->get_checker_event (idx);
> +      /* Prune everything between [..., system call, (...), system return,
> ...].  */
> +      if (event->is_return_p ()
> +          && in_system_header_at (event->get_location ()))
> +      {
> +        int ret_idx = idx;
> +        prune_within_frame (path, idx);
> +
> +        if (get_logger ())
> +        {
> +          label_text desc
> +            (path->get_checker_event (idx)->get_desc (false));
> +          log ("filtering event %i-%i:"
> +              " system header event: %s",
> +              idx, ret_idx, desc.get ());
> +        }
> +        // delete callsite
> +        if (idx >= 0)
> +          path->delete_event (idx);
> +      }
> +
> +      idx--;
> +    }
> +}
> +
> +void
> +diagnostic_manager::prune_events_too_deep (checker_path *path) const
> +{
> +  long depth = 0;
> +  long maxdepth = trim_diagnostics_depth ();
> +  gcc_assert (maxdepth >= 0);
> +  int idx = (signed)path->num_events () - 1;
> +  if (idx >= 0)
> +  {
> +    depth = path->get_checker_event (0)->get_stack_depth ();
> +    maxdepth += depth;
> +  }
> +
> +  while (idx >= 0)
> +    {
> +      const checker_event *event = path->get_checker_event (idx);
> +      /* Prune everything between [..., call too deep, (...), return too
> deep, ...].  */
> +      if (event->is_return_p ()
> +          && event->get_stack_depth () > maxdepth)
> +      {
> +        int ret_idx = idx;
> +        prune_within_frame (path, idx);
> +
> +        if (get_logger ())
> +        {
> +          label_text desc
> +            (path->get_checker_event (idx)->get_desc (false));
> +          log ("filtering event %i-%i:"
> +              " event too deep: %s",
> +              idx, ret_idx, desc.get ());
> +        }
> +        // delete callsite
> +        if (idx >= 0)
> +          path->delete_event (idx);
> +      }
> +
> +      idx--;
> +    }
> +
> +}
> +
>  /* Return true iff event IDX within PATH is on the same line as
> REF_EXP_LOC.  */
>
>  static bool
> diff --git a/gcc/analyzer/diagnostic-manager.h
> b/gcc/analyzer/diagnostic-manager.h
> index 9b3e903fc5d..9afef0975be 100644
> --- a/gcc/analyzer/diagnostic-manager.h
> +++ b/gcc/analyzer/diagnostic-manager.h
> @@ -21,6 +21,10 @@ along with GCC; see the file COPYING3.  If not see
>  #ifndef GCC_ANALYZER_DIAGNOSTIC_MANAGER_H
>  #define GCC_ANALYZER_DIAGNOSTIC_MANAGER_H
>
> +long trim_diagnostics_depth ();
> +bool trim_diagnostics_system_p ();
> +bool trim_diagnostics_never_p ();
> +
>  namespace ana {
>
>  class epath_finder;
> @@ -180,6 +184,9 @@ private:
>   state_machine::state_t state) const;
>    void update_for_unsuitable_sm_exprs (tree *expr) const;
>    void prune_interproc_events (checker_path *path) const;
> +  void trim_diagnostic_path (checker_path *path) const;
> +  void prune_system_headers (checker_path *path) const;
> +  void prune_events_too_deep (checker_path *path) const;
>    void consolidate_conditions (checker_path *path) const;
>    void finish_pruning (checker_path *path) const;
>
> diff --git a/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-0.C
> b/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-0.C
> new file mode 100644
> index 00000000000..ddf39ed1690
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-0.C
> @@ -0,0 +1,19 @@
> +/* { dg-additional-options "-fdiagnostics-plain-output
> -fdiagnostics-path-format=inline-events -fanalyzer-trim-diagnostics=0" } */
> +/* { dg-skip-if "" { c++98_only }  } */
> +
> +#include <memory>
> +
> +struct A {int x; int y;};
> +
> +int main () {
> +  std::shared_ptr<A> a;
> +  a->x = 4; /* { dg-line deref_a } */
> +  /* { dg-warning "dereference of NULL" "" { target *-*-* } deref_a } */
> +
> +  return 0;
> +}
> +/* { dg-begin-multiline-output "" }
> +  'int main()': events 1-2
> +    |
> +    |
> +   { dg-end-multiline-output "" } */
> \ No newline at end of file
> diff --git a/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-1.C
> b/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-1.C
> new file mode 100644
> index 00000000000..83a132078a1
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-1.C
> @@ -0,0 +1,28 @@
> +/* { dg-additional-options "-fdiagnostics-plain-output
> -fdiagnostics-path-format=inline-events -fanalyzer-trim-diagnostics=1" } */
> +/* { dg-skip-if "" { c++98_only }  } */
> +
> +#include <memory>
> +
> +struct A {int x; int y;};
> +
> +int main () {
> +  std::shared_ptr<A> a;
> +  a->x = 4; /* { dg-line deref_a } */
> +  /* { dg-warning "dereference of NULL" "" { target *-*-* } deref_a } */
> +
> +  return 0;
> +}
> +
> +/* { dg-begin-multiline-output "" }
> +  'int main()': events 1-2
> +    |
> +    |
> +    +--> 'std::__shared_ptr_access<_Tp, _Lp, <anonymous>, <anonymous>
> >::element_type* std::__shared_ptr_access<_Tp, _Lp, <anonymous>,
> <anonymous> >::operator->() const [with _Tp = A; __gnu_cxx::_Lock_policy
> _Lp = __gnu_cxx::_S_atomic; bool <anonymous> = false; bool <anonymous> =
> false]': event 3
> +           |
> +           |
> +    <------+
> +    |
> +  'int main()': events 4-5
> +    |
> +    |
> +   { dg-end-multiline-output "" } */
> \ No newline at end of file
> diff --git a/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-2.C
> b/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-2.C
> new file mode 100644
> index 00000000000..cf065929ec9
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-2.C
> @@ -0,0 +1,44 @@
> +/* { dg-additional-options "-fdiagnostics-plain-output
> -fdiagnostics-path-format=inline-events -fanalyzer-trim-diagnostics=2" } */
> +/* { dg-skip-if "" { c++98_only }  } */
> +
> +#include <memory>
> +
> +struct A {int x; int y;};
> +
> +int main () {
> +  std::shared_ptr<A> a;
> +  a->x = 4; /* { dg-line deref_a } */
> +  /* { dg-warning "dereference of NULL" "" { target *-*-* } deref_a } */
> +
> +  return 0;
> +}
> +
> +/* { dg-begin-multiline-output "" }
> +  'int main()': events 1-2
> +    |
> +    |
> +    +--> 'std::__shared_ptr_access<_Tp, _Lp, <anonymous>, <anonymous>
> >::element_type* std::__shared_ptr_access<_Tp, _Lp, <anonymous>,
> <anonymous> >::operator->() const [with _Tp = A; __gnu_cxx::_Lock_policy
> _Lp = __gnu_cxx::_S_atomic; bool <anonymous> = false; bool <anonymous> =
> false]': events 3-4
> +           |
> +           |
> +           +--> 'std::__shared_ptr_access<_Tp, _Lp, <anonymous>,
> <anonymous> >::element_type* std::__shared_ptr_access<_Tp, _Lp,
> <anonymous>, <anonymous> >::_M_get() const [with _Tp = A;
> __gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic; bool <anonymous> =
> false; bool <anonymous> = false]': events 5-6
> +                  |
> +                  |
> +                  +--> 'std::__shared_ptr<_Tp, _Lp>::element_type*
> std::__shared_ptr<_Tp, _Lp>::get() const [with _Tp = A;
> __gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic]': events 7-8
> +                         |
> +                         |
> +                  <------+
> +                  |
> +                'std::__shared_ptr_access<_Tp, _Lp, <anonymous>,
> <anonymous> >::element_type* std::__shared_ptr_access<_Tp, _Lp,
> <anonymous>, <anonymous> >::_M_get() const [with _Tp = A;
> __gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic; bool <anonymous> =
> false; bool <anonymous> = false]': event 9
> +                  |
> +                  |
> +           <------+
> +           |
> +         'std::__shared_ptr_access<_Tp, _Lp, <anonymous>, <anonymous>
> >::element_type* std::__shared_ptr_access<_Tp, _Lp, <anonymous>,
> <anonymous> >::operator->() const [with _Tp = A; __gnu_cxx::_Lock_policy
> _Lp = __gnu_cxx::_S_atomic; bool <anonymous> = false; bool <anonymous> =
> false]': event 10
> +           |
> +           |
> +    <------+
> +    |
> +  'int main()': events 11-12
> +    |
> +    |
> +   { dg-end-multiline-output "" } */
> \ No newline at end of file
> diff --git
> a/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-default.C
> b/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-default.C
> new file mode 100644
> index 00000000000..fc271309d4e
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-default.C
> @@ -0,0 +1,21 @@
> +/* { dg-additional-options "-O0 -fdiagnostics-plain-output
> -fdiagnostics-path-format=inline-events" } */
> +/* { dg-skip-if "" { c++98_only }  } */
> +
> +// Must behave like -fanalyzer-trim-diagnostics=system
> +
> +#include <memory>
> +
> +struct A {int x; int y;};
> +
> +int main () {
> +  std::shared_ptr<A> a;
> +  a->x = 4; /* { dg-line deref_a } */
> +  /* { dg-warning "dereference of NULL" "" { target *-*-* } deref_a } */
> +
> +  return 0;
> +}
> +/* { dg-begin-multiline-output "" }
> +  'int main()': events 1-2
> +    |
> +    |
> +   { dg-end-multiline-output "" } */
> \ No newline at end of file
> diff --git
> a/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-never.C
> b/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-never.C
> new file mode 100644
> index 00000000000..a69416608da
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-never.C
> @@ -0,0 +1,44 @@
> +/* { dg-additional-options "-fdiagnostics-plain-output
> -fdiagnostics-path-format=inline-events -fanalyzer-trim-diagnostics=never"
> } */
> +/* { dg-skip-if "" { c++98_only }  } */
> +
> +#include <memory>
> +
> +struct A {int x; int y;};
> +
> +int main () {
> +  std::shared_ptr<A> a;
> +  a->x = 4; /* { dg-line deref_a } */
> +  /* { dg-warning "dereference of NULL" "" { target *-*-* } deref_a } */
> +
> +  return 0;
> +}
> +
> +/* { dg-begin-multiline-output "" }
> +  'int main()': events 1-2
> +    |
> +    |
> +    +--> 'std::__shared_ptr_access<_Tp, _Lp, <anonymous>, <anonymous>
> >::element_type* std::__shared_ptr_access<_Tp, _Lp, <anonymous>,
> <anonymous> >::operator->() const [with _Tp = A; __gnu_cxx::_Lock_policy
> _Lp = __gnu_cxx::_S_atomic; bool <anonymous> = false; bool <anonymous> =
> false]': events 3-4
> +           |
> +           |
> +           +--> 'std::__shared_ptr_access<_Tp, _Lp, <anonymous>,
> <anonymous> >::element_type* std::__shared_ptr_access<_Tp, _Lp,
> <anonymous>, <anonymous> >::_M_get() const [with _Tp = A;
> __gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic; bool <anonymous> =
> false; bool <anonymous> = false]': events 5-6
> +                  |
> +                  |
> +                  +--> 'std::__shared_ptr<_Tp, _Lp>::element_type*
> std::__shared_ptr<_Tp, _Lp>::get() const [with _Tp = A;
> __gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic]': events 7-8
> +                         |
> +                         |
> +                  <------+
> +                  |
> +                'std::__shared_ptr_access<_Tp, _Lp, <anonymous>,
> <anonymous> >::element_type* std::__shared_ptr_access<_Tp, _Lp,
> <anonymous>, <anonymous> >::_M_get() const [with _Tp = A;
> __gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic; bool <anonymous> =
> false; bool <anonymous> = false]': event 9
> +                  |
> +                  |
> +           <------+
> +           |
> +         'std::__shared_ptr_access<_Tp, _Lp, <anonymous>, <anonymous>
> >::element_type* std::__shared_ptr_access<_Tp, _Lp, <anonymous>,
> <anonymous> >::operator->() const [with _Tp = A; __gnu_cxx::_Lock_policy
> _Lp = __gnu_cxx::_S_atomic; bool <anonymous> = false; bool <anonymous> =
> false]': event 10
> +           |
> +           |
> +    <------+
> +    |
> +  'int main()': events 11-12
> +    |
> +    |
> +   { dg-end-multiline-output "" } */
> \ No newline at end of file
> diff --git
> a/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-system.C
> b/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-system.C
> new file mode 100644
> index 00000000000..22fadc84e75
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-system.C
> @@ -0,0 +1,19 @@
> +/* { dg-additional-options "-fdiagnostics-plain-output
> -fdiagnostics-path-format=inline-events -fanalyzer-trim-diagnostics=system"
> } */
> +/* { dg-skip-if "" { c++98_only }  } */
> +
> +#include <memory>
> +
> +struct A {int x; int y;};
> +
> +int main () {
> +  std::shared_ptr<A> a;
> +  a->x = 4; /* { dg-line deref_a } */
> +  /* { dg-warning "dereference of NULL" "" { target *-*-* } deref_a } */
> +
> +  return 0;
> +}
> +/* { dg-begin-multiline-output "" }
> +  'int main()': events 1-2
> +    |
> +    |
> +   { dg-end-multiline-output "" } */
> \ No newline at end of file
> --
> 2.34.1
Benjamin Priour July 26, 2023, 9:27 a.m. UTC | #3
On Sat, Jul 22, 2023 at 12:04 AM David Malcolm <dmalcolm@redhat.com> wrote:

> On Fri, 2023-07-21 at 17:35 +0200, Benjamin Priour wrote:
> > Hi,
> >
> > Upon David's request I've joined the in progress patch to the below
> > email.
> > I hope it makes more sense now.
> >
> > Best,
> > Benjamin.
>
> Thanks for posting the work-in-progress patch; it makes the idea
> clearer.
>
> Some thoughts about this:
>
> - I like the idea of defaulting to *not* showing events within system
> headers, which the patch achieves
> - I don't like the combination of never/system with maxdepth, in that
> it seems complicated and I don't think a user is likely to experiment
> with different depths.
> - Hence I think it would work better as a simple boolean, perhaps
>   "-fanalyzer-show-events-in-system-headers"
>   or somesuch?  It seems like the sort of thing that we want to provide
> a sensible default for, but have the option of turning off for
> debugging the analyzer itself, but I don't expect an end-user to touch
> that option.
>

A boolean sounds good, I will trust your experience with the end-user here,
especially since <maxdepth> and "never" had some overlap, it could have
been confusing.


> FWIW the patch seems to have been mangled somewhat via email, so I
> don't have a sense of what the actual output from patched analyzer
> looks like.  What should we output to the user with -fanalyzer and no
> other options for the case in PR 110543?  Currently, for
> https://godbolt.org/z/sb9dM9Gqa trunk emits 12 events, of which
> probably only this last one is useful:
>
>   (12) dereference of NULL 'a.std::__shared_ptr_access<A,
> __gnu_cxx::_S_atomic, false, false>::operator->()'
>
> What does the output look like with your patch?
>

The plan with this patch was to get events :
(1) entry to 'main'
(2) calling 'std::__shared_ptr_access<A, __gnu_cxx::_S_atomic, false,
false>::operator->' from 'main'
(12) dereference of NULL 'a.std::__shared_ptr_access<A,
__gnu_cxx::_S_atomic, false, false>::operator->()'
(11) returning to 'main' from 'std::__shared_ptr_access<A,
__gnu_cxx::_S_atomic, false, false>::operator->'

This way, we get the entry and exit point to the system headers ( (2) and
(11) ), and the actual injurious event ( (12) ).
We could however go as you suggest, with an even more succint path and only
keep (1) and (12).

Thanks,
Benjamin


> Thanks
> Dave
>

>
> >
> > ---------- Forwarded message ---------
> > From: Benjamin Priour <priour.be@gmail.com>
> > Date: Tue, Jul 18, 2023 at 3:30 PM
> > Subject: [RFC] analyzer: Add optional trim of the analyzer
> > diagnostics
> > going too deep [PR110543]
> > To: <gcc-patches@gcc.gnu.org>, David Malcolm <dmalcolm@redhat.com>
> >
> >
> > Hi,
> >
> > I'd like to request comments on a patch I am writing for PR110543.
> > The goal of this patch is to reduce the noise of the analyzer emitted
> > diagnostics when dealing with
> > system headers, or simply diagnostic paths that are too long. The new
> > option only affects the display
> > of the diagnostics, but doesn't hinder the actual analysis.
> >
> > I've defaulted the new option to "system", thus preventing the
> > diagnostic
> > paths from showing system headers.
> > "never" corresponds to the pre-patch behavior, whereas you can also
> > specify
> > an unsigned value <maxdepth>
> > that prevents paths to go deeper than <maxdepth> frames.
> >
> > fanalyzer-trim-diagnostics=
> > > Common Joined RejectNegative ToLower
> > > Var(flag_analyzer_trim_diagnostics)
> > > Init("system")
> > > -fanalyzer-trim-diagnostics=[never|system|<maxdepth>] Trim
> > > diagnostics
> > > path that are too long before emission.
> > >
> >
> > Does it sounds reasonable and user-friendly ?
> >
> > Regstrapping was a success against trunk, although one of the newly
> > added
> > test case fails for c++14.
> > Note that the test case below was done with "never", thus behaves
> > exactly
> > as the pre-patch analyzer
> > on x86_64-linux-gnu.
> >
> > /* { dg-additional-options "-fdiagnostics-plain-output
> > > -fdiagnostics-path-format=inline-events -fanalyzer-trim-
> > > diagnostics=never"
> > > } */
> > > /* { dg-skip-if "" { c++98_only }  } */
> > >
> > > #include <memory>
> > > struct A {int x; int y;};
> > >
> > > int main () {
> > >   std::shared_ptr<A> a;
> > >   a->x = 4; /* { dg-line deref_a } */
> > >   /* { dg-warning "dereference of NULL" "" { target *-*-* } deref_a
> > > } */
> > >
> > >   return 0;
> > > }
> > >
> > > /* { dg-begin-multiline-output "" }
> > >   'int main()': events 1-2
> > >     |
> > >     |
> > >     +--> 'std::__shared_ptr_access<_Tp, _Lp, <anonymous>,
> > > <anonymous>
> > > > ::element_type* std::__shared_ptr_access<_Tp, _Lp, <anonymous>,
> > > <anonymous> >::operator->() const [with _Tp = A;
> > > __gnu_cxx::_Lock_policy
> > > _Lp = __gnu_cxx::_S_atomic; bool <anonymous> = false; bool
> > > <anonymous> =
> > > false]': events 3-4
> > >            |
> > >            |
> > >            +--> 'std::__shared_ptr_access<_Tp, _Lp, <anonymous>,
> > > <anonymous> >::element_type* std::__shared_ptr_access<_Tp, _Lp,
> > > <anonymous>, <anonymous> >::_M_get() const [with _Tp = A;
> > > __gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic; bool
> > > <anonymous> =
> > > false; bool <anonymous> = false]': events 5-6
> > >                   |
> > >                   |
> > >                   +--> 'std::__shared_ptr<_Tp, _Lp>::element_type*
> > > std::__shared_ptr<_Tp, _Lp>::get() const [with _Tp = A;
> > > __gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic]': events 7-8
> > >                          |
> > >                          |
> > >                   <------+
> > >                   |
> > >                 'std::__shared_ptr_access<_Tp, _Lp, <anonymous>,
> > > <anonymous> >::element_type* std::__shared_ptr_access<_Tp, _Lp,
> > > <anonymous>, <anonymous> >::_M_get() const [with _Tp = A;
> > > __gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic; bool
> > > <anonymous> =
> > > false; bool <anonymous> = false]': event 9
> > >                   |
> > >                   |
> > >            <------+
> > >            |
> > >          'std::__shared_ptr_access<_Tp, _Lp, <anonymous>,
> > > <anonymous>
> > > > ::element_type* std::__shared_ptr_access<_Tp, _Lp, <anonymous>,
> > > <anonymous> >::operator->() const [with _Tp = A;
> > > __gnu_cxx::_Lock_policy
> > > _Lp = __gnu_cxx::_S_atomic; bool <anonymous> = false; bool
> > > <anonymous> =
> > > false]': event 10
> > >            |
> > >            |
> > >     <------+
> > >     |
> > >   'int main()': events 11-12
> > >     |
> > >     |
> > >    { dg-end-multiline-output "" } */
> > >
> >
> >
> > The first events "'int main()': events 1-2" vary in c++14 (get events
> > 1-3).
> >
> > >
> > > // c++14 with fully detailed output
> > >   ‘int main()’: events 1-3
> > >     |
> > >     |    8 | int main () {
> > >     |      |     ^~~~
> > >     |      |     |
> > >     |      |     (1) entry to ‘main’
> > >     |    9 |   std::shared_ptr<A> a;
> > >     |      |                      ~
> > >     |      |                      |
> > >     |      |                      (2)
> > > ‘a.std::shared_ptr<A>::<anonymous>.std::__shared_ptr<A,
> > > __gnu_cxx::_S_atomic>::_M_ptr’ is NULL
> > >     |   10 |   a->x = 4; /* { dg-line deref_a } */
> > >     |      |    ~~
> > >     |      |    |
> > >     |      |    (3) calling ‘std::__shared_ptr_access<A,
> > > __gnu_cxx::_S_atomic, false, false>::operator->’ from ‘main’
> > >
> >
> > whereas c++17 and posterior give
> >
> > > // c++17 with fully detailed output
> > >
> > // ./xg++ -fanalyzer
> > >  ../../gcc/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-
> > > diagnostics-never.C
> > >  -B. -shared-libgcc -fanalyzer-trim-diagnostics=never -std=c++17
> > >
> >   ‘int main()’: events 1-2
> > >     |
> > >     |    8 | int main () {
> > >     |      |     ^~~~
> > >     |      |     |
> > >     |      |     (1) entry to ‘main’
> > >     |    9 |   std::shared_ptr<A> a;
> > >     |   10 |   a->x = 4; /* { dg-line deref_a } */
> > >     |      |    ~~
> > >     |      |    |
> > >     |      |    (2) calling ‘std::__shared_ptr_access<A,
> > > __gnu_cxx::_S_atomic, false, false>::operator->’ from ‘main’
> > >
> >
> > Is there a way to make dg-multiline-output check for a regex ? Or
> > would
> > checking the multiline-output only for c++17 and c++20 be acceptable
> > ?
> > This divergence results from two slightly different IPA:
> >
> > // c++14 -fdump-ipa-analyzer
> > > // std::shared_ptr<A> a; becomes
> > > a.D.29392._M_ptr = 0B;
> > > a.D.29392._M_refcount._M_pi = 0B;
> > >
> >
> > whereas in c++17
> >
> > > // c++17 -fdump-ipa-analyzer
> > > // std::shared_ptr<A> a; becomes
> > > a = {};
> > >
> >
> > I know shared_ptr limited support is a coincidence more than a
> > feature, but
> > maybe there is still a way to make the above test case work ?
> > Otherwise, I'd fallback to a supported system function. If you have
> > any
> > that you fancy, do tell me.
> >
> > Thanks,
> > Benjamin.
> >
> > ---
> >  gcc/analyzer/analyzer.cc                      |   2 +-
> >  gcc/analyzer/analyzer.h                       |   1 +
> >  gcc/analyzer/analyzer.opt                     |   4 +
> >  gcc/analyzer/diagnostic-manager.cc            | 132
> > ++++++++++++++++++
> >  gcc/analyzer/diagnostic-manager.h             |   7 +
> >  .../analyzer/fanalyzer-trim-diagnostics-0.C   |  19 +++
> >  .../analyzer/fanalyzer-trim-diagnostics-1.C   |  28 ++++
> >  .../analyzer/fanalyzer-trim-diagnostics-2.C   |  44 ++++++
> >  .../fanalyzer-trim-diagnostics-default.C      |  21 +++
> >  .../fanalyzer-trim-diagnostics-never.C        |  44 ++++++
> >  .../fanalyzer-trim-diagnostics-system.C       |  19 +++
> >  11 files changed, 320 insertions(+), 1 deletion(-)
> >  create mode 100644
> > gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-0.C
> >  create mode 100644
> > gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-1.C
> >  create mode 100644
> > gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-2.C
> >  create mode 100644
> > gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-default.C
> >  create mode 100644
> > gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-never.C
> >  create mode 100644
> > gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-system.C
> >
> > diff --git a/gcc/analyzer/analyzer.cc b/gcc/analyzer/analyzer.cc
> > index 5091fb7a583..b27d8e359db 100644
> > --- a/gcc/analyzer/analyzer.cc
> > +++ b/gcc/analyzer/analyzer.cc
> > @@ -274,7 +274,7 @@ is_named_call_p (const_tree fndecl, const char
> > *funcname)
> >     Compare with cp/typeck.cc: decl_in_std_namespace_p, but this
> > doesn't
> >     rely on being the C++ FE (or handle inline namespaces inside of
> > std).
> >  */
> >
> > -static inline bool
> > +bool
> >  is_std_function_p (const_tree fndecl)
> >  {
> >    tree name_decl = DECL_NAME (fndecl);
> > diff --git a/gcc/analyzer/analyzer.h b/gcc/analyzer/analyzer.h
> > index 579517c23e6..31597079153 100644
> > --- a/gcc/analyzer/analyzer.h
> > +++ b/gcc/analyzer/analyzer.h
> > @@ -386,6 +386,7 @@ extern bool is_special_named_call_p (const gcall
> > *call,
> > const char *funcname,
> >  extern bool is_named_call_p (const_tree fndecl, const char
> > *funcname);
> >  extern bool is_named_call_p (const_tree fndecl, const char
> > *funcname,
> >       const gcall *call, unsigned int num_args);
> > +extern bool is_std_function_p (const_tree fndecl);
> >  extern bool is_std_named_call_p (const_tree fndecl, const char
> > *funcname);
> >  extern bool is_std_named_call_p (const_tree fndecl, const char
> > *funcname,
> >   const gcall *call, unsigned int num_args);
> > diff --git a/gcc/analyzer/analyzer.opt b/gcc/analyzer/analyzer.opt
> > index 2760aaa8151..78ff2c07483 100644
> > --- a/gcc/analyzer/analyzer.opt
> > +++ b/gcc/analyzer/analyzer.opt
> > @@ -362,4 +362,8 @@ fdump-analyzer-untracked
> >  Common RejectNegative Var(flag_dump_analyzer_untracked)
> >  Emit custom warnings with internal details intended for analyzer
> > developers.
> >
> > +fanalyzer-trim-diagnostics=
> > +Common Joined RejectNegative ToLower
> > Var(flag_analyzer_trim_diagnostics)
> > Init("system")
> > +-fanalyzer-trim-diagnostics=[never|system|<maxdepth>] Trim
> > diagnostics
> > path that are too long before emission.
> > +
> >  ; This comment is to ensure we retain the blank line above.
> > diff --git a/gcc/analyzer/diagnostic-manager.cc
> > b/gcc/analyzer/diagnostic-manager.cc
> > index cfca305d552..d946ad5bac7 100644
> > --- a/gcc/analyzer/diagnostic-manager.cc
> > +++ b/gcc/analyzer/diagnostic-manager.cc
> > @@ -20,9 +20,11 @@ along with GCC; see the file COPYING3.  If not see
> >
> >  #include "config.h"
> >  #define INCLUDE_MEMORY
> > +#define INCLUDE_VECTOR
> >  #include "system.h"
> >  #include "coretypes.h"
> >  #include "tree.h"
> > +#include "input.h"
> >  #include "pretty-print.h"
> >  #include "gcc-rich-location.h"
> >  #include "gimple-pretty-print.h"
> > @@ -60,6 +62,37 @@ along with GCC; see the file COPYING3.  If not see
> >
> >  #if ENABLE_ANALYZER
> >
> > +/* Return positive value of fanalyzer-trim-diagnostics if a depth
> > was given
> > +  or a negative value otherwise.  */
> > +
> > +long trim_diagnostics_depth ()
> > +{
> > +  if (trim_diagnostics_system_p () ||  trim_diagnostics_never_p ())
> > +    return -1;
> > +
> > +  char *end = NULL;
> > +  long depth = strtol (flag_analyzer_trim_diagnostics, &end, 10);
> > +  gcc_assert (end == NULL || *end == '\0');
> > +  gcc_assert (!(end == flag_analyzer_trim_diagnostics && depth ==
> > 0));
> > +  return depth;
> > +}
> > +
> > +/* Return true if fanalyzer-trim-diagnostics is set to "system".  */
> > +
> > +bool trim_diagnostics_system_p ()
> > +{
> > +  int len = strlen (flag_analyzer_trim_diagnostics);
> > +  return len == 6 && strcmp (flag_analyzer_trim_diagnostics,
> > "system") ==
> > 0;
> > +}
> > +
> > +/* Return true if fanalyzer-trim-diagnostics is set to "never".  */
> > +
> > +bool trim_diagnostics_never_p ()
> > +{
> > +  int len = strlen (flag_analyzer_trim_diagnostics);
> > +  return len == 5 && strcmp (flag_analyzer_trim_diagnostics,
> > "never") == 0;
> > +}
> > +
> >  namespace ana {
> >
> >  class feasible_worklist;
> > @@ -2281,6 +2314,8 @@ diagnostic_manager::prune_path (checker_path
> > *path,
> >    path->maybe_log (get_logger (), "path");
> >    prune_for_sm_diagnostic (path, sm, sval, state);
> >    prune_interproc_events (path);
> > +  if (! trim_diagnostics_never_p ())
> > +    trim_diagnostic_path (path);
> >    consolidate_conditions (path);
> >    finish_pruning (path);
> >    path->maybe_log (get_logger (), "pruned");
> > @@ -2667,6 +2702,103 @@ diagnostic_manager::prune_interproc_events
> > (checker_path *path) const
> >    while (changed);
> >  }
> >
> > +void
> > +diagnostic_manager::trim_diagnostic_path (checker_path *path) const
> > +{
> > +  if (trim_diagnostics_system_p ())
> > +    prune_system_headers (path);
> > +  else
> > +    prune_events_too_deep (path);
> > +}
> > +
> > +/* Remove everything within ]callsite, IDX].  */
> > +static void
> > +prune_within_frame (checker_path *path, int &idx)
> > +{
> > +  int nesting = 1;
> > +  while (idx >= 0 && nesting != 0)
> > +    {
> > +      if (path->get_checker_event (idx)->is_call_p ())
> > +        nesting--;
> > +      else if (path->get_checker_event (idx)->is_return_p ())
> > +        nesting++;
> > +
> > +      path->delete_event (idx--);
> > +    }
> > +}
> > +
> > +void
> > +diagnostic_manager::prune_system_headers (checker_path *path) const
> > +{
> > +  int idx = (signed)path->num_events () - 1;
> > +  while (idx >= 0)
> > +    {
> > +      const checker_event *event = path->get_checker_event (idx);
> > +      /* Prune everything between [..., system call, (...), system
> > return,
> > ...].  */
> > +      if (event->is_return_p ()
> > +          && in_system_header_at (event->get_location ()))
> > +      {
> > +        int ret_idx = idx;
> > +        prune_within_frame (path, idx);
> > +
> > +        if (get_logger ())
> > +        {
> > +          label_text desc
> > +            (path->get_checker_event (idx)->get_desc (false));
> > +          log ("filtering event %i-%i:"
> > +              " system header event: %s",
> > +              idx, ret_idx, desc.get ());
> > +        }
> > +        // delete callsite
> > +        if (idx >= 0)
> > +          path->delete_event (idx);
> > +      }
> > +
> > +      idx--;
> > +    }
> > +}
> > +
> > +void
> > +diagnostic_manager::prune_events_too_deep (checker_path *path) const
> > +{
> > +  long depth = 0;
> > +  long maxdepth = trim_diagnostics_depth ();
> > +  gcc_assert (maxdepth >= 0);
> > +  int idx = (signed)path->num_events () - 1;
> > +  if (idx >= 0)
> > +  {
> > +    depth = path->get_checker_event (0)->get_stack_depth ();
> > +    maxdepth += depth;
> > +  }
> > +
> > +  while (idx >= 0)
> > +    {
> > +      const checker_event *event = path->get_checker_event (idx);
> > +      /* Prune everything between [..., call too deep, (...), return
> > too
> > deep, ...].  */
> > +      if (event->is_return_p ()
> > +          && event->get_stack_depth () > maxdepth)
> > +      {
> > +        int ret_idx = idx;
> > +        prune_within_frame (path, idx);
> > +
> > +        if (get_logger ())
> > +        {
> > +          label_text desc
> > +            (path->get_checker_event (idx)->get_desc (false));
> > +          log ("filtering event %i-%i:"
> > +              " event too deep: %s",
> > +              idx, ret_idx, desc.get ());
> > +        }
> > +        // delete callsite
> > +        if (idx >= 0)
> > +          path->delete_event (idx);
> > +      }
> > +
> > +      idx--;
> > +    }
> > +
> > +}
> > +
> >  /* Return true iff event IDX within PATH is on the same line as
> > REF_EXP_LOC.  */
> >
> >  static bool
> > diff --git a/gcc/analyzer/diagnostic-manager.h
> > b/gcc/analyzer/diagnostic-manager.h
> > index 9b3e903fc5d..9afef0975be 100644
> > --- a/gcc/analyzer/diagnostic-manager.h
> > +++ b/gcc/analyzer/diagnostic-manager.h
> > @@ -21,6 +21,10 @@ along with GCC; see the file COPYING3.  If not see
> >  #ifndef GCC_ANALYZER_DIAGNOSTIC_MANAGER_H
> >  #define GCC_ANALYZER_DIAGNOSTIC_MANAGER_H
> >
> > +long trim_diagnostics_depth ();
> > +bool trim_diagnostics_system_p ();
> > +bool trim_diagnostics_never_p ();
> > +
> >  namespace ana {
> >
> >  class epath_finder;
> > @@ -180,6 +184,9 @@ private:
> >   state_machine::state_t state) const;
> >    void update_for_unsuitable_sm_exprs (tree *expr) const;
> >    void prune_interproc_events (checker_path *path) const;
> > +  void trim_diagnostic_path (checker_path *path) const;
> > +  void prune_system_headers (checker_path *path) const;
> > +  void prune_events_too_deep (checker_path *path) const;
> >    void consolidate_conditions (checker_path *path) const;
> >    void finish_pruning (checker_path *path) const;
> >
> > diff --git a/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-
> > diagnostics-0.C
> > b/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-0.C
> > new file mode 100644
> > index 00000000000..ddf39ed1690
> > --- /dev/null
> > +++ b/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-0.C
> > @@ -0,0 +1,19 @@
> > +/* { dg-additional-options "-fdiagnostics-plain-output
> > -fdiagnostics-path-format=inline-events -fanalyzer-trim-
> > diagnostics=0" } */
> > +/* { dg-skip-if "" { c++98_only }  } */
> > +
> > +#include <memory>
> > +
> > +struct A {int x; int y;};
> > +
> > +int main () {
> > +  std::shared_ptr<A> a;
> > +  a->x = 4; /* { dg-line deref_a } */
> > +  /* { dg-warning "dereference of NULL" "" { target *-*-* } deref_a
> > } */
> > +
> > +  return 0;
> > +}
> > +/* { dg-begin-multiline-output "" }
> > +  'int main()': events 1-2
> > +    |
> > +    |
> > +   { dg-end-multiline-output "" } */
> > \ No newline at end of file
> > diff --git a/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-
> > diagnostics-1.C
> > b/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-1.C
> > new file mode 100644
> > index 00000000000..83a132078a1
> > --- /dev/null
> > +++ b/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-1.C
> > @@ -0,0 +1,28 @@
> > +/* { dg-additional-options "-fdiagnostics-plain-output
> > -fdiagnostics-path-format=inline-events -fanalyzer-trim-
> > diagnostics=1" } */
> > +/* { dg-skip-if "" { c++98_only }  } */
> > +
> > +#include <memory>
> > +
> > +struct A {int x; int y;};
> > +
> > +int main () {
> > +  std::shared_ptr<A> a;
> > +  a->x = 4; /* { dg-line deref_a } */
> > +  /* { dg-warning "dereference of NULL" "" { target *-*-* } deref_a
> > } */
> > +
> > +  return 0;
> > +}
> > +
> > +/* { dg-begin-multiline-output "" }
> > +  'int main()': events 1-2
> > +    |
> > +    |
> > +    +--> 'std::__shared_ptr_access<_Tp, _Lp, <anonymous>,
> > <anonymous>
> > > ::element_type* std::__shared_ptr_access<_Tp, _Lp, <anonymous>,
> > <anonymous> >::operator->() const [with _Tp = A;
> > __gnu_cxx::_Lock_policy
> > _Lp = __gnu_cxx::_S_atomic; bool <anonymous> = false; bool
> > <anonymous> =
> > false]': event 3
> > +           |
> > +           |
> > +    <------+
> > +    |
> > +  'int main()': events 4-5
> > +    |
> > +    |
> > +   { dg-end-multiline-output "" } */
> > \ No newline at end of file
> > diff --git a/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-
> > diagnostics-2.C
> > b/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-2.C
> > new file mode 100644
> > index 00000000000..cf065929ec9
> > --- /dev/null
> > +++ b/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-2.C
> > @@ -0,0 +1,44 @@
> > +/* { dg-additional-options "-fdiagnostics-plain-output
> > -fdiagnostics-path-format=inline-events -fanalyzer-trim-
> > diagnostics=2" } */
> > +/* { dg-skip-if "" { c++98_only }  } */
> > +
> > +#include <memory>
> > +
> > +struct A {int x; int y;};
> > +
> > +int main () {
> > +  std::shared_ptr<A> a;
> > +  a->x = 4; /* { dg-line deref_a } */
> > +  /* { dg-warning "dereference of NULL" "" { target *-*-* } deref_a
> > } */
> > +
> > +  return 0;
> > +}
> > +
> > +/* { dg-begin-multiline-output "" }
> > +  'int main()': events 1-2
> > +    |
> > +    |
> > +    +--> 'std::__shared_ptr_access<_Tp, _Lp, <anonymous>,
> > <anonymous>
> > > ::element_type* std::__shared_ptr_access<_Tp, _Lp, <anonymous>,
> > <anonymous> >::operator->() const [with _Tp = A;
> > __gnu_cxx::_Lock_policy
> > _Lp = __gnu_cxx::_S_atomic; bool <anonymous> = false; bool
> > <anonymous> =
> > false]': events 3-4
> > +           |
> > +           |
> > +           +--> 'std::__shared_ptr_access<_Tp, _Lp, <anonymous>,
> > <anonymous> >::element_type* std::__shared_ptr_access<_Tp, _Lp,
> > <anonymous>, <anonymous> >::_M_get() const [with _Tp = A;
> > __gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic; bool <anonymous>
> > =
> > false; bool <anonymous> = false]': events 5-6
> > +                  |
> > +                  |
> > +                  +--> 'std::__shared_ptr<_Tp, _Lp>::element_type*
> > std::__shared_ptr<_Tp, _Lp>::get() const [with _Tp = A;
> > __gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic]': events 7-8
> > +                         |
> > +                         |
> > +                  <------+
> > +                  |
> > +                'std::__shared_ptr_access<_Tp, _Lp, <anonymous>,
> > <anonymous> >::element_type* std::__shared_ptr_access<_Tp, _Lp,
> > <anonymous>, <anonymous> >::_M_get() const [with _Tp = A;
> > __gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic; bool <anonymous>
> > =
> > false; bool <anonymous> = false]': event 9
> > +                  |
> > +                  |
> > +           <------+
> > +           |
> > +         'std::__shared_ptr_access<_Tp, _Lp, <anonymous>,
> > <anonymous>
> > > ::element_type* std::__shared_ptr_access<_Tp, _Lp, <anonymous>,
> > <anonymous> >::operator->() const [with _Tp = A;
> > __gnu_cxx::_Lock_policy
> > _Lp = __gnu_cxx::_S_atomic; bool <anonymous> = false; bool
> > <anonymous> =
> > false]': event 10
> > +           |
> > +           |
> > +    <------+
> > +    |
> > +  'int main()': events 11-12
> > +    |
> > +    |
> > +   { dg-end-multiline-output "" } */
> > \ No newline at end of file
> > diff --git
> > a/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-default.C
> > b/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-default.C
> > new file mode 100644
> > index 00000000000..fc271309d4e
> > --- /dev/null
> > +++ b/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-
> > default.C
> > @@ -0,0 +1,21 @@
> > +/* { dg-additional-options "-O0 -fdiagnostics-plain-output
> > -fdiagnostics-path-format=inline-events" } */
> > +/* { dg-skip-if "" { c++98_only }  } */
> > +
> > +// Must behave like -fanalyzer-trim-diagnostics=system
> > +
> > +#include <memory>
> > +
> > +struct A {int x; int y;};
> > +
> > +int main () {
> > +  std::shared_ptr<A> a;
> > +  a->x = 4; /* { dg-line deref_a } */
> > +  /* { dg-warning "dereference of NULL" "" { target *-*-* } deref_a
> > } */
> > +
> > +  return 0;
> > +}
> > +/* { dg-begin-multiline-output "" }
> > +  'int main()': events 1-2
> > +    |
> > +    |
> > +   { dg-end-multiline-output "" } */
> > \ No newline at end of file
> > diff --git
> > a/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-never.C
> > b/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-never.C
> > new file mode 100644
> > index 00000000000..a69416608da
> > --- /dev/null
> > +++ b/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-
> > never.C
> > @@ -0,0 +1,44 @@
> > +/* { dg-additional-options "-fdiagnostics-plain-output
> > -fdiagnostics-path-format=inline-events -fanalyzer-trim-
> > diagnostics=never"
> > } */
> > +/* { dg-skip-if "" { c++98_only }  } */
> > +
> > +#include <memory>
> > +
> > +struct A {int x; int y;};
> > +
> > +int main () {
> > +  std::shared_ptr<A> a;
> > +  a->x = 4; /* { dg-line deref_a } */
> > +  /* { dg-warning "dereference of NULL" "" { target *-*-* } deref_a
> > } */
> > +
> > +  return 0;
> > +}
> > +
> > +/* { dg-begin-multiline-output "" }
> > +  'int main()': events 1-2
> > +    |
> > +    |
> > +    +--> 'std::__shared_ptr_access<_Tp, _Lp, <anonymous>,
> > <anonymous>
> > > ::element_type* std::__shared_ptr_access<_Tp, _Lp, <anonymous>,
> > <anonymous> >::operator->() const [with _Tp = A;
> > __gnu_cxx::_Lock_policy
> > _Lp = __gnu_cxx::_S_atomic; bool <anonymous> = false; bool
> > <anonymous> =
> > false]': events 3-4
> > +           |
> > +           |
> > +           +--> 'std::__shared_ptr_access<_Tp, _Lp, <anonymous>,
> > <anonymous> >::element_type* std::__shared_ptr_access<_Tp, _Lp,
> > <anonymous>, <anonymous> >::_M_get() const [with _Tp = A;
> > __gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic; bool <anonymous>
> > =
> > false; bool <anonymous> = false]': events 5-6
> > +                  |
> > +                  |
> > +                  +--> 'std::__shared_ptr<_Tp, _Lp>::element_type*
> > std::__shared_ptr<_Tp, _Lp>::get() const [with _Tp = A;
> > __gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic]': events 7-8
> > +                         |
> > +                         |
> > +                  <------+
> > +                  |
> > +                'std::__shared_ptr_access<_Tp, _Lp, <anonymous>,
> > <anonymous> >::element_type* std::__shared_ptr_access<_Tp, _Lp,
> > <anonymous>, <anonymous> >::_M_get() const [with _Tp = A;
> > __gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic; bool <anonymous>
> > =
> > false; bool <anonymous> = false]': event 9
> > +                  |
> > +                  |
> > +           <------+
> > +           |
> > +         'std::__shared_ptr_access<_Tp, _Lp, <anonymous>,
> > <anonymous>
> > > ::element_type* std::__shared_ptr_access<_Tp, _Lp, <anonymous>,
> > <anonymous> >::operator->() const [with _Tp = A;
> > __gnu_cxx::_Lock_policy
> > _Lp = __gnu_cxx::_S_atomic; bool <anonymous> = false; bool
> > <anonymous> =
> > false]': event 10
> > +           |
> > +           |
> > +    <------+
> > +    |
> > +  'int main()': events 11-12
> > +    |
> > +    |
> > +   { dg-end-multiline-output "" } */
> > \ No newline at end of file
> > diff --git
> > a/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-system.C
> > b/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-system.C
> > new file mode 100644
> > index 00000000000..22fadc84e75
> > --- /dev/null
> > +++ b/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-
> > system.C
> > @@ -0,0 +1,19 @@
> > +/* { dg-additional-options "-fdiagnostics-plain-output
> > -fdiagnostics-path-format=inline-events -fanalyzer-trim-
> > diagnostics=system"
> > } */
> > +/* { dg-skip-if "" { c++98_only }  } */
> > +
> > +#include <memory>
> > +
> > +struct A {int x; int y;};
> > +
> > +int main () {
> > +  std::shared_ptr<A> a;
> > +  a->x = 4; /* { dg-line deref_a } */
> > +  /* { dg-warning "dereference of NULL" "" { target *-*-* } deref_a
> > } */
> > +
> > +  return 0;
> > +}
> > +/* { dg-begin-multiline-output "" }
> > +  'int main()': events 1-2
> > +    |
> > +    |
> > +   { dg-end-multiline-output "" } */
> > \ No newline at end of file
>
>
David Malcolm July 26, 2023, 2:26 p.m. UTC | #4
On Wed, 2023-07-26 at 11:27 +0200, Benjamin Priour wrote:
> On Sat, Jul 22, 2023 at 12:04 AM David Malcolm <dmalcolm@redhat.com>
> wrote:
> 
> > On Fri, 2023-07-21 at 17:35 +0200, Benjamin Priour wrote:
> > > Hi,
> > > 
> > > Upon David's request I've joined the in progress patch to the
> > > below
> > > email.
> > > I hope it makes more sense now.
> > > 
> > > Best,
> > > Benjamin.
> > 
> > Thanks for posting the work-in-progress patch; it makes the idea
> > clearer.
> > 
> > Some thoughts about this:
> > 
> > - I like the idea of defaulting to *not* showing events within
> > system
> > headers, which the patch achieves
> > - I don't like the combination of never/system with maxdepth, in
> > that
> > it seems complicated and I don't think a user is likely to
> > experiment
> > with different depths.
> > - Hence I think it would work better as a simple boolean, perhaps
> >   "-fanalyzer-show-events-in-system-headers"
> >   or somesuch?  It seems like the sort of thing that we want to
> > provide
> > a sensible default for, but have the option of turning off for
> > debugging the analyzer itself, but I don't expect an end-user to
> > touch
> > that option.
> > 
> 
> A boolean sounds good, I will trust your experience with the end-user
> here,
> especially since <maxdepth> and "never" had some overlap, it could
> have
> been confusing.
> 
> 
> > FWIW the patch seems to have been mangled somewhat via email, so I
> > don't have a sense of what the actual output from patched analyzer
> > looks like.  What should we output to the user with -fanalyzer and
> > no
> > other options for the case in PR 110543?  Currently, for
> > https://godbolt.org/z/sb9dM9Gqa trunk emits 12 events, of which
> > probably only this last one is useful:
> > 
> >   (12) dereference of NULL 'a.std::__shared_ptr_access<A,
> > __gnu_cxx::_S_atomic, false, false>::operator->()'
> > 
> > What does the output look like with your patch?
> > 
> 
> The plan with this patch was to get events :
> (1) entry to 'main'
> (2) calling 'std::__shared_ptr_access<A, __gnu_cxx::_S_atomic, false,
> false>::operator->' from 'main'
> (12) dereference of NULL 'a.std::__shared_ptr_access<A,
> __gnu_cxx::_S_atomic, false, false>::operator->()'
> (11) returning to 'main' from 'std::__shared_ptr_access<A,
> __gnu_cxx::_S_atomic, false, false>::operator->'
> 
> This way, we get the entry and exit point to the system headers ( (2)
> and
> (11) ), and the actual injurious event ( (12) ).
> We could however go as you suggest, with an even more succint path
> and only
> keep (1) and (12).

I think we could still have events (11) and (12): what if for call and
return events we consider the location of both the call site and the
called function: we suppress if both are in a system header, but don't
suppress if only one is a system header.  That way by default we'd show
the call into a system header and show the return from the system
header, but we'd suppress all the implementation details within the
system header.

Does that seem like it could work?

Thanks
Dave



> 
> Thanks,
> Benjamin
> 
> 
> > Thanks
> > Dave
> > 
> 
> > 
> > > 
> > > ---------- Forwarded message ---------
> > > From: Benjamin Priour <priour.be@gmail.com>
> > > Date: Tue, Jul 18, 2023 at 3:30 PM
> > > Subject: [RFC] analyzer: Add optional trim of the analyzer
> > > diagnostics
> > > going too deep [PR110543]
> > > To: <gcc-patches@gcc.gnu.org>, David Malcolm
> > > <dmalcolm@redhat.com>
> > > 
> > > 
> > > Hi,
> > > 
> > > I'd like to request comments on a patch I am writing for
> > > PR110543.
> > > The goal of this patch is to reduce the noise of the analyzer
> > > emitted
> > > diagnostics when dealing with
> > > system headers, or simply diagnostic paths that are too long. The
> > > new
> > > option only affects the display
> > > of the diagnostics, but doesn't hinder the actual analysis.
> > > 
> > > I've defaulted the new option to "system", thus preventing the
> > > diagnostic
> > > paths from showing system headers.
> > > "never" corresponds to the pre-patch behavior, whereas you can
> > > also
> > > specify
> > > an unsigned value <maxdepth>
> > > that prevents paths to go deeper than <maxdepth> frames.
> > > 
> > > fanalyzer-trim-diagnostics=
> > > > Common Joined RejectNegative ToLower
> > > > Var(flag_analyzer_trim_diagnostics)
> > > > Init("system")
> > > > -fanalyzer-trim-diagnostics=[never|system|<maxdepth>] Trim
> > > > diagnostics
> > > > path that are too long before emission.
> > > > 
> > > 
> > > Does it sounds reasonable and user-friendly ?
> > > 
> > > Regstrapping was a success against trunk, although one of the
> > > newly
> > > added
> > > test case fails for c++14.
> > > Note that the test case below was done with "never", thus behaves
> > > exactly
> > > as the pre-patch analyzer
> > > on x86_64-linux-gnu.
> > > 
> > > /* { dg-additional-options "-fdiagnostics-plain-output
> > > > -fdiagnostics-path-format=inline-events -fanalyzer-trim-
> > > > diagnostics=never"
> > > > } */
> > > > /* { dg-skip-if "" { c++98_only }  } */
> > > > 
> > > > #include <memory>
> > > > struct A {int x; int y;};
> > > > 
> > > > int main () {
> > > >   std::shared_ptr<A> a;
> > > >   a->x = 4; /* { dg-line deref_a } */
> > > >   /* { dg-warning "dereference of NULL" "" { target *-*-* }
> > > > deref_a
> > > > } */
> > > > 
> > > >   return 0;
> > > > }
> > > > 
> > > > /* { dg-begin-multiline-output "" }
> > > >   'int main()': events 1-2
> > > >     |
> > > >     |
> > > >     +--> 'std::__shared_ptr_access<_Tp, _Lp, <anonymous>,
> > > > <anonymous>
> > > > > ::element_type* std::__shared_ptr_access<_Tp, _Lp,
> > > > > <anonymous>,
> > > > <anonymous> >::operator->() const [with _Tp = A;
> > > > __gnu_cxx::_Lock_policy
> > > > _Lp = __gnu_cxx::_S_atomic; bool <anonymous> = false; bool
> > > > <anonymous> =
> > > > false]': events 3-4
> > > >            |
> > > >            |
> > > >            +--> 'std::__shared_ptr_access<_Tp, _Lp,
> > > > <anonymous>,
> > > > <anonymous> >::element_type* std::__shared_ptr_access<_Tp, _Lp,
> > > > <anonymous>, <anonymous> >::_M_get() const [with _Tp = A;
> > > > __gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic; bool
> > > > <anonymous> =
> > > > false; bool <anonymous> = false]': events 5-6
> > > >                   |
> > > >                   |
> > > >                   +--> 'std::__shared_ptr<_Tp,
> > > > _Lp>::element_type*
> > > > std::__shared_ptr<_Tp, _Lp>::get() const [with _Tp = A;
> > > > __gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic]': events 7-
> > > > 8
> > > >                          |
> > > >                          |
> > > >                   <------+
> > > >                   |
> > > >                 'std::__shared_ptr_access<_Tp, _Lp,
> > > > <anonymous>,
> > > > <anonymous> >::element_type* std::__shared_ptr_access<_Tp, _Lp,
> > > > <anonymous>, <anonymous> >::_M_get() const [with _Tp = A;
> > > > __gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic; bool
> > > > <anonymous> =
> > > > false; bool <anonymous> = false]': event 9
> > > >                   |
> > > >                   |
> > > >            <------+
> > > >            |
> > > >          'std::__shared_ptr_access<_Tp, _Lp, <anonymous>,
> > > > <anonymous>
> > > > > ::element_type* std::__shared_ptr_access<_Tp, _Lp,
> > > > > <anonymous>,
> > > > <anonymous> >::operator->() const [with _Tp = A;
> > > > __gnu_cxx::_Lock_policy
> > > > _Lp = __gnu_cxx::_S_atomic; bool <anonymous> = false; bool
> > > > <anonymous> =
> > > > false]': event 10
> > > >            |
> > > >            |
> > > >     <------+
> > > >     |
> > > >   'int main()': events 11-12
> > > >     |
> > > >     |
> > > >    { dg-end-multiline-output "" } */
> > > > 
> > > 
> > > 
> > > The first events "'int main()': events 1-2" vary in c++14 (get
> > > events
> > > 1-3).
> > > 
> > > > 
> > > > // c++14 with fully detailed output
> > > >   ‘int main()’: events 1-3
> > > >     |
> > > >     |    8 | int main () {
> > > >     |      |     ^~~~
> > > >     |      |     |
> > > >     |      |     (1) entry to ‘main’
> > > >     |    9 |   std::shared_ptr<A> a;
> > > >     |      |                      ~
> > > >     |      |                      |
> > > >     |      |                      (2)
> > > > ‘a.std::shared_ptr<A>::<anonymous>.std::__shared_ptr<A,
> > > > __gnu_cxx::_S_atomic>::_M_ptr’ is NULL
> > > >     |   10 |   a->x = 4; /* { dg-line deref_a } */
> > > >     |      |    ~~
> > > >     |      |    |
> > > >     |      |    (3) calling ‘std::__shared_ptr_access<A,
> > > > __gnu_cxx::_S_atomic, false, false>::operator->’ from ‘main’
> > > > 
> > > 
> > > whereas c++17 and posterior give
> > > 
> > > > // c++17 with fully detailed output
> > > > 
> > > // ./xg++ -fanalyzer
> > > >  ../../gcc/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-
> > > > diagnostics-never.C
> > > >  -B. -shared-libgcc -fanalyzer-trim-diagnostics=never -
> > > > std=c++17
> > > > 
> > >   ‘int main()’: events 1-2
> > > >     |
> > > >     |    8 | int main () {
> > > >     |      |     ^~~~
> > > >     |      |     |
> > > >     |      |     (1) entry to ‘main’
> > > >     |    9 |   std::shared_ptr<A> a;
> > > >     |   10 |   a->x = 4; /* { dg-line deref_a } */
> > > >     |      |    ~~
> > > >     |      |    |
> > > >     |      |    (2) calling ‘std::__shared_ptr_access<A,
> > > > __gnu_cxx::_S_atomic, false, false>::operator->’ from ‘main’
> > > > 
> > > 
> > > Is there a way to make dg-multiline-output check for a regex ? Or
> > > would
> > > checking the multiline-output only for c++17 and c++20 be
> > > acceptable
> > > ?
> > > This divergence results from two slightly different IPA:
> > > 
> > > // c++14 -fdump-ipa-analyzer
> > > > // std::shared_ptr<A> a; becomes
> > > > a.D.29392._M_ptr = 0B;
> > > > a.D.29392._M_refcount._M_pi = 0B;
> > > > 
> > > 
> > > whereas in c++17
> > > 
> > > > // c++17 -fdump-ipa-analyzer
> > > > // std::shared_ptr<A> a; becomes
> > > > a = {};
> > > > 
> > > 
> > > I know shared_ptr limited support is a coincidence more than a
> > > feature, but
> > > maybe there is still a way to make the above test case work ?
> > > Otherwise, I'd fallback to a supported system function. If you
> > > have
> > > any
> > > that you fancy, do tell me.
> > > 
> > > Thanks,
> > > Benjamin.
> > > 
> > > ---
> > >  gcc/analyzer/analyzer.cc                      |   2 +-
> > >  gcc/analyzer/analyzer.h                       |   1 +
> > >  gcc/analyzer/analyzer.opt                     |   4 +
> > >  gcc/analyzer/diagnostic-manager.cc            | 132
> > > ++++++++++++++++++
> > >  gcc/analyzer/diagnostic-manager.h             |   7 +
> > >  .../analyzer/fanalyzer-trim-diagnostics-0.C   |  19 +++
> > >  .../analyzer/fanalyzer-trim-diagnostics-1.C   |  28 ++++
> > >  .../analyzer/fanalyzer-trim-diagnostics-2.C   |  44 ++++++
> > >  .../fanalyzer-trim-diagnostics-default.C      |  21 +++
> > >  .../fanalyzer-trim-diagnostics-never.C        |  44 ++++++
> > >  .../fanalyzer-trim-diagnostics-system.C       |  19 +++
> > >  11 files changed, 320 insertions(+), 1 deletion(-)
> > >  create mode 100644
> > > gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-0.C
> > >  create mode 100644
> > > gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-1.C
> > >  create mode 100644
> > > gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-2.C
> > >  create mode 100644
> > > gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-
> > > default.C
> > >  create mode 100644
> > > gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-never.C
> > >  create mode 100644
> > > gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-system.C
> > > 
> > > diff --git a/gcc/analyzer/analyzer.cc b/gcc/analyzer/analyzer.cc
> > > index 5091fb7a583..b27d8e359db 100644
> > > --- a/gcc/analyzer/analyzer.cc
> > > +++ b/gcc/analyzer/analyzer.cc
> > > @@ -274,7 +274,7 @@ is_named_call_p (const_tree fndecl, const
> > > char
> > > *funcname)
> > >     Compare with cp/typeck.cc: decl_in_std_namespace_p, but this
> > > doesn't
> > >     rely on being the C++ FE (or handle inline namespaces inside
> > > of
> > > std).
> > >  */
> > > 
> > > -static inline bool
> > > +bool
> > >  is_std_function_p (const_tree fndecl)
> > >  {
> > >    tree name_decl = DECL_NAME (fndecl);
> > > diff --git a/gcc/analyzer/analyzer.h b/gcc/analyzer/analyzer.h
> > > index 579517c23e6..31597079153 100644
> > > --- a/gcc/analyzer/analyzer.h
> > > +++ b/gcc/analyzer/analyzer.h
> > > @@ -386,6 +386,7 @@ extern bool is_special_named_call_p (const
> > > gcall
> > > *call,
> > > const char *funcname,
> > >  extern bool is_named_call_p (const_tree fndecl, const char
> > > *funcname);
> > >  extern bool is_named_call_p (const_tree fndecl, const char
> > > *funcname,
> > >       const gcall *call, unsigned int num_args);
> > > +extern bool is_std_function_p (const_tree fndecl);
> > >  extern bool is_std_named_call_p (const_tree fndecl, const char
> > > *funcname);
> > >  extern bool is_std_named_call_p (const_tree fndecl, const char
> > > *funcname,
> > >   const gcall *call, unsigned int num_args);
> > > diff --git a/gcc/analyzer/analyzer.opt
> > > b/gcc/analyzer/analyzer.opt
> > > index 2760aaa8151..78ff2c07483 100644
> > > --- a/gcc/analyzer/analyzer.opt
> > > +++ b/gcc/analyzer/analyzer.opt
> > > @@ -362,4 +362,8 @@ fdump-analyzer-untracked
> > >  Common RejectNegative Var(flag_dump_analyzer_untracked)
> > >  Emit custom warnings with internal details intended for analyzer
> > > developers.
> > > 
> > > +fanalyzer-trim-diagnostics=
> > > +Common Joined RejectNegative ToLower
> > > Var(flag_analyzer_trim_diagnostics)
> > > Init("system")
> > > +-fanalyzer-trim-diagnostics=[never|system|<maxdepth>] Trim
> > > diagnostics
> > > path that are too long before emission.
> > > +
> > >  ; This comment is to ensure we retain the blank line above.
> > > diff --git a/gcc/analyzer/diagnostic-manager.cc
> > > b/gcc/analyzer/diagnostic-manager.cc
> > > index cfca305d552..d946ad5bac7 100644
> > > --- a/gcc/analyzer/diagnostic-manager.cc
> > > +++ b/gcc/analyzer/diagnostic-manager.cc
> > > @@ -20,9 +20,11 @@ along with GCC; see the file COPYING3.  If not
> > > see
> > > 
> > >  #include "config.h"
> > >  #define INCLUDE_MEMORY
> > > +#define INCLUDE_VECTOR
> > >  #include "system.h"
> > >  #include "coretypes.h"
> > >  #include "tree.h"
> > > +#include "input.h"
> > >  #include "pretty-print.h"
> > >  #include "gcc-rich-location.h"
> > >  #include "gimple-pretty-print.h"
> > > @@ -60,6 +62,37 @@ along with GCC; see the file COPYING3.  If not
> > > see
> > > 
> > >  #if ENABLE_ANALYZER
> > > 
> > > +/* Return positive value of fanalyzer-trim-diagnostics if a
> > > depth
> > > was given
> > > +  or a negative value otherwise.  */
> > > +
> > > +long trim_diagnostics_depth ()
> > > +{
> > > +  if (trim_diagnostics_system_p () ||  trim_diagnostics_never_p
> > > ())
> > > +    return -1;
> > > +
> > > +  char *end = NULL;
> > > +  long depth = strtol (flag_analyzer_trim_diagnostics, &end,
> > > 10);
> > > +  gcc_assert (end == NULL || *end == '\0');
> > > +  gcc_assert (!(end == flag_analyzer_trim_diagnostics && depth
> > > ==
> > > 0));
> > > +  return depth;
> > > +}
> > > +
> > > +/* Return true if fanalyzer-trim-diagnostics is set to
> > > "system".  */
> > > +
> > > +bool trim_diagnostics_system_p ()
> > > +{
> > > +  int len = strlen (flag_analyzer_trim_diagnostics);
> > > +  return len == 6 && strcmp (flag_analyzer_trim_diagnostics,
> > > "system") ==
> > > 0;
> > > +}
> > > +
> > > +/* Return true if fanalyzer-trim-diagnostics is set to "never". 
> > > */
> > > +
> > > +bool trim_diagnostics_never_p ()
> > > +{
> > > +  int len = strlen (flag_analyzer_trim_diagnostics);
> > > +  return len == 5 && strcmp (flag_analyzer_trim_diagnostics,
> > > "never") == 0;
> > > +}
> > > +
> > >  namespace ana {
> > > 
> > >  class feasible_worklist;
> > > @@ -2281,6 +2314,8 @@ diagnostic_manager::prune_path
> > > (checker_path
> > > *path,
> > >    path->maybe_log (get_logger (), "path");
> > >    prune_for_sm_diagnostic (path, sm, sval, state);
> > >    prune_interproc_events (path);
> > > +  if (! trim_diagnostics_never_p ())
> > > +    trim_diagnostic_path (path);
> > >    consolidate_conditions (path);
> > >    finish_pruning (path);
> > >    path->maybe_log (get_logger (), "pruned");
> > > @@ -2667,6 +2702,103 @@
> > > diagnostic_manager::prune_interproc_events
> > > (checker_path *path) const
> > >    while (changed);
> > >  }
> > > 
> > > +void
> > > +diagnostic_manager::trim_diagnostic_path (checker_path *path)
> > > const
> > > +{
> > > +  if (trim_diagnostics_system_p ())
> > > +    prune_system_headers (path);
> > > +  else
> > > +    prune_events_too_deep (path);
> > > +}
> > > +
> > > +/* Remove everything within ]callsite, IDX].  */
> > > +static void
> > > +prune_within_frame (checker_path *path, int &idx)
> > > +{
> > > +  int nesting = 1;
> > > +  while (idx >= 0 && nesting != 0)
> > > +    {
> > > +      if (path->get_checker_event (idx)->is_call_p ())
> > > +        nesting--;
> > > +      else if (path->get_checker_event (idx)->is_return_p ())
> > > +        nesting++;
> > > +
> > > +      path->delete_event (idx--);
> > > +    }
> > > +}
> > > +
> > > +void
> > > +diagnostic_manager::prune_system_headers (checker_path *path)
> > > const
> > > +{
> > > +  int idx = (signed)path->num_events () - 1;
> > > +  while (idx >= 0)
> > > +    {
> > > +      const checker_event *event = path->get_checker_event
> > > (idx);
> > > +      /* Prune everything between [..., system call, (...),
> > > system
> > > return,
> > > ...].  */
> > > +      if (event->is_return_p ()
> > > +          && in_system_header_at (event->get_location ()))
> > > +      {
> > > +        int ret_idx = idx;
> > > +        prune_within_frame (path, idx);
> > > +
> > > +        if (get_logger ())
> > > +        {
> > > +          label_text desc
> > > +            (path->get_checker_event (idx)->get_desc (false));
> > > +          log ("filtering event %i-%i:"
> > > +              " system header event: %s",
> > > +              idx, ret_idx, desc.get ());
> > > +        }
> > > +        // delete callsite
> > > +        if (idx >= 0)
> > > +          path->delete_event (idx);
> > > +      }
> > > +
> > > +      idx--;
> > > +    }
> > > +}
> > > +
> > > +void
> > > +diagnostic_manager::prune_events_too_deep (checker_path *path)
> > > const
> > > +{
> > > +  long depth = 0;
> > > +  long maxdepth = trim_diagnostics_depth ();
> > > +  gcc_assert (maxdepth >= 0);
> > > +  int idx = (signed)path->num_events () - 1;
> > > +  if (idx >= 0)
> > > +  {
> > > +    depth = path->get_checker_event (0)->get_stack_depth ();
> > > +    maxdepth += depth;
> > > +  }
> > > +
> > > +  while (idx >= 0)
> > > +    {
> > > +      const checker_event *event = path->get_checker_event
> > > (idx);
> > > +      /* Prune everything between [..., call too deep, (...),
> > > return
> > > too
> > > deep, ...].  */
> > > +      if (event->is_return_p ()
> > > +          && event->get_stack_depth () > maxdepth)
> > > +      {
> > > +        int ret_idx = idx;
> > > +        prune_within_frame (path, idx);
> > > +
> > > +        if (get_logger ())
> > > +        {
> > > +          label_text desc
> > > +            (path->get_checker_event (idx)->get_desc (false));
> > > +          log ("filtering event %i-%i:"
> > > +              " event too deep: %s",
> > > +              idx, ret_idx, desc.get ());
> > > +        }
> > > +        // delete callsite
> > > +        if (idx >= 0)
> > > +          path->delete_event (idx);
> > > +      }
> > > +
> > > +      idx--;
> > > +    }
> > > +
> > > +}
> > > +
> > >  /* Return true iff event IDX within PATH is on the same line as
> > > REF_EXP_LOC.  */
> > > 
> > >  static bool
> > > diff --git a/gcc/analyzer/diagnostic-manager.h
> > > b/gcc/analyzer/diagnostic-manager.h
> > > index 9b3e903fc5d..9afef0975be 100644
> > > --- a/gcc/analyzer/diagnostic-manager.h
> > > +++ b/gcc/analyzer/diagnostic-manager.h
> > > @@ -21,6 +21,10 @@ along with GCC; see the file COPYING3.  If not
> > > see
> > >  #ifndef GCC_ANALYZER_DIAGNOSTIC_MANAGER_H
> > >  #define GCC_ANALYZER_DIAGNOSTIC_MANAGER_H
> > > 
> > > +long trim_diagnostics_depth ();
> > > +bool trim_diagnostics_system_p ();
> > > +bool trim_diagnostics_never_p ();
> > > +
> > >  namespace ana {
> > > 
> > >  class epath_finder;
> > > @@ -180,6 +184,9 @@ private:
> > >   state_machine::state_t state) const;
> > >    void update_for_unsuitable_sm_exprs (tree *expr) const;
> > >    void prune_interproc_events (checker_path *path) const;
> > > +  void trim_diagnostic_path (checker_path *path) const;
> > > +  void prune_system_headers (checker_path *path) const;
> > > +  void prune_events_too_deep (checker_path *path) const;
> > >    void consolidate_conditions (checker_path *path) const;
> > >    void finish_pruning (checker_path *path) const;
> > > 
> > > diff --git a/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-
> > > diagnostics-0.C
> > > b/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-0.C
> > > new file mode 100644
> > > index 00000000000..ddf39ed1690
> > > --- /dev/null
> > > +++ b/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-
> > > 0.C
> > > @@ -0,0 +1,19 @@
> > > +/* { dg-additional-options "-fdiagnostics-plain-output
> > > -fdiagnostics-path-format=inline-events -fanalyzer-trim-
> > > diagnostics=0" } */
> > > +/* { dg-skip-if "" { c++98_only }  } */
> > > +
> > > +#include <memory>
> > > +
> > > +struct A {int x; int y;};
> > > +
> > > +int main () {
> > > +  std::shared_ptr<A> a;
> > > +  a->x = 4; /* { dg-line deref_a } */
> > > +  /* { dg-warning "dereference of NULL" "" { target *-*-* }
> > > deref_a
> > > } */
> > > +
> > > +  return 0;
> > > +}
> > > +/* { dg-begin-multiline-output "" }
> > > +  'int main()': events 1-2
> > > +    |
> > > +    |
> > > +   { dg-end-multiline-output "" } */
> > > \ No newline at end of file
> > > diff --git a/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-
> > > diagnostics-1.C
> > > b/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-1.C
> > > new file mode 100644
> > > index 00000000000..83a132078a1
> > > --- /dev/null
> > > +++ b/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-
> > > 1.C
> > > @@ -0,0 +1,28 @@
> > > +/* { dg-additional-options "-fdiagnostics-plain-output
> > > -fdiagnostics-path-format=inline-events -fanalyzer-trim-
> > > diagnostics=1" } */
> > > +/* { dg-skip-if "" { c++98_only }  } */
> > > +
> > > +#include <memory>
> > > +
> > > +struct A {int x; int y;};
> > > +
> > > +int main () {
> > > +  std::shared_ptr<A> a;
> > > +  a->x = 4; /* { dg-line deref_a } */
> > > +  /* { dg-warning "dereference of NULL" "" { target *-*-* }
> > > deref_a
> > > } */
> > > +
> > > +  return 0;
> > > +}
> > > +
> > > +/* { dg-begin-multiline-output "" }
> > > +  'int main()': events 1-2
> > > +    |
> > > +    |
> > > +    +--> 'std::__shared_ptr_access<_Tp, _Lp, <anonymous>,
> > > <anonymous>
> > > > ::element_type* std::__shared_ptr_access<_Tp, _Lp, <anonymous>,
> > > <anonymous> >::operator->() const [with _Tp = A;
> > > __gnu_cxx::_Lock_policy
> > > _Lp = __gnu_cxx::_S_atomic; bool <anonymous> = false; bool
> > > <anonymous> =
> > > false]': event 3
> > > +           |
> > > +           |
> > > +    <------+
> > > +    |
> > > +  'int main()': events 4-5
> > > +    |
> > > +    |
> > > +   { dg-end-multiline-output "" } */
> > > \ No newline at end of file
> > > diff --git a/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-
> > > diagnostics-2.C
> > > b/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-2.C
> > > new file mode 100644
> > > index 00000000000..cf065929ec9
> > > --- /dev/null
> > > +++ b/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-
> > > 2.C
> > > @@ -0,0 +1,44 @@
> > > +/* { dg-additional-options "-fdiagnostics-plain-output
> > > -fdiagnostics-path-format=inline-events -fanalyzer-trim-
> > > diagnostics=2" } */
> > > +/* { dg-skip-if "" { c++98_only }  } */
> > > +
> > > +#include <memory>
> > > +
> > > +struct A {int x; int y;};
> > > +
> > > +int main () {
> > > +  std::shared_ptr<A> a;
> > > +  a->x = 4; /* { dg-line deref_a } */
> > > +  /* { dg-warning "dereference of NULL" "" { target *-*-* }
> > > deref_a
> > > } */
> > > +
> > > +  return 0;
> > > +}
> > > +
> > > +/* { dg-begin-multiline-output "" }
> > > +  'int main()': events 1-2
> > > +    |
> > > +    |
> > > +    +--> 'std::__shared_ptr_access<_Tp, _Lp, <anonymous>,
> > > <anonymous>
> > > > ::element_type* std::__shared_ptr_access<_Tp, _Lp, <anonymous>,
> > > <anonymous> >::operator->() const [with _Tp = A;
> > > __gnu_cxx::_Lock_policy
> > > _Lp = __gnu_cxx::_S_atomic; bool <anonymous> = false; bool
> > > <anonymous> =
> > > false]': events 3-4
> > > +           |
> > > +           |
> > > +           +--> 'std::__shared_ptr_access<_Tp, _Lp, <anonymous>,
> > > <anonymous> >::element_type* std::__shared_ptr_access<_Tp, _Lp,
> > > <anonymous>, <anonymous> >::_M_get() const [with _Tp = A;
> > > __gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic; bool
> > > <anonymous>
> > > =
> > > false; bool <anonymous> = false]': events 5-6
> > > +                  |
> > > +                  |
> > > +                  +--> 'std::__shared_ptr<_Tp,
> > > _Lp>::element_type*
> > > std::__shared_ptr<_Tp, _Lp>::get() const [with _Tp = A;
> > > __gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic]': events 7-8
> > > +                         |
> > > +                         |
> > > +                  <------+
> > > +                  |
> > > +                'std::__shared_ptr_access<_Tp, _Lp, <anonymous>,
> > > <anonymous> >::element_type* std::__shared_ptr_access<_Tp, _Lp,
> > > <anonymous>, <anonymous> >::_M_get() const [with _Tp = A;
> > > __gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic; bool
> > > <anonymous>
> > > =
> > > false; bool <anonymous> = false]': event 9
> > > +                  |
> > > +                  |
> > > +           <------+
> > > +           |
> > > +         'std::__shared_ptr_access<_Tp, _Lp, <anonymous>,
> > > <anonymous>
> > > > ::element_type* std::__shared_ptr_access<_Tp, _Lp, <anonymous>,
> > > <anonymous> >::operator->() const [with _Tp = A;
> > > __gnu_cxx::_Lock_policy
> > > _Lp = __gnu_cxx::_S_atomic; bool <anonymous> = false; bool
> > > <anonymous> =
> > > false]': event 10
> > > +           |
> > > +           |
> > > +    <------+
> > > +    |
> > > +  'int main()': events 11-12
> > > +    |
> > > +    |
> > > +   { dg-end-multiline-output "" } */
> > > \ No newline at end of file
> > > diff --git
> > > a/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-
> > > default.C
> > > b/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-
> > > default.C
> > > new file mode 100644
> > > index 00000000000..fc271309d4e
> > > --- /dev/null
> > > +++ b/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-
> > > default.C
> > > @@ -0,0 +1,21 @@
> > > +/* { dg-additional-options "-O0 -fdiagnostics-plain-output
> > > -fdiagnostics-path-format=inline-events" } */
> > > +/* { dg-skip-if "" { c++98_only }  } */
> > > +
> > > +// Must behave like -fanalyzer-trim-diagnostics=system
> > > +
> > > +#include <memory>
> > > +
> > > +struct A {int x; int y;};
> > > +
> > > +int main () {
> > > +  std::shared_ptr<A> a;
> > > +  a->x = 4; /* { dg-line deref_a } */
> > > +  /* { dg-warning "dereference of NULL" "" { target *-*-* }
> > > deref_a
> > > } */
> > > +
> > > +  return 0;
> > > +}
> > > +/* { dg-begin-multiline-output "" }
> > > +  'int main()': events 1-2
> > > +    |
> > > +    |
> > > +   { dg-end-multiline-output "" } */
> > > \ No newline at end of file
> > > diff --git
> > > a/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-
> > > never.C
> > > b/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-
> > > never.C
> > > new file mode 100644
> > > index 00000000000..a69416608da
> > > --- /dev/null
> > > +++ b/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-
> > > never.C
> > > @@ -0,0 +1,44 @@
> > > +/* { dg-additional-options "-fdiagnostics-plain-output
> > > -fdiagnostics-path-format=inline-events -fanalyzer-trim-
> > > diagnostics=never"
> > > } */
> > > +/* { dg-skip-if "" { c++98_only }  } */
> > > +
> > > +#include <memory>
> > > +
> > > +struct A {int x; int y;};
> > > +
> > > +int main () {
> > > +  std::shared_ptr<A> a;
> > > +  a->x = 4; /* { dg-line deref_a } */
> > > +  /* { dg-warning "dereference of NULL" "" { target *-*-* }
> > > deref_a
> > > } */
> > > +
> > > +  return 0;
> > > +}
> > > +
> > > +/* { dg-begin-multiline-output "" }
> > > +  'int main()': events 1-2
> > > +    |
> > > +    |
> > > +    +--> 'std::__shared_ptr_access<_Tp, _Lp, <anonymous>,
> > > <anonymous>
> > > > ::element_type* std::__shared_ptr_access<_Tp, _Lp, <anonymous>,
> > > <anonymous> >::operator->() const [with _Tp = A;
> > > __gnu_cxx::_Lock_policy
> > > _Lp = __gnu_cxx::_S_atomic; bool <anonymous> = false; bool
> > > <anonymous> =
> > > false]': events 3-4
> > > +           |
> > > +           |
> > > +           +--> 'std::__shared_ptr_access<_Tp, _Lp, <anonymous>,
> > > <anonymous> >::element_type* std::__shared_ptr_access<_Tp, _Lp,
> > > <anonymous>, <anonymous> >::_M_get() const [with _Tp = A;
> > > __gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic; bool
> > > <anonymous>
> > > =
> > > false; bool <anonymous> = false]': events 5-6
> > > +                  |
> > > +                  |
> > > +                  +--> 'std::__shared_ptr<_Tp,
> > > _Lp>::element_type*
> > > std::__shared_ptr<_Tp, _Lp>::get() const [with _Tp = A;
> > > __gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic]': events 7-8
> > > +                         |
> > > +                         |
> > > +                  <------+
> > > +                  |
> > > +                'std::__shared_ptr_access<_Tp, _Lp, <anonymous>,
> > > <anonymous> >::element_type* std::__shared_ptr_access<_Tp, _Lp,
> > > <anonymous>, <anonymous> >::_M_get() const [with _Tp = A;
> > > __gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic; bool
> > > <anonymous>
> > > =
> > > false; bool <anonymous> = false]': event 9
> > > +                  |
> > > +                  |
> > > +           <------+
> > > +           |
> > > +         'std::__shared_ptr_access<_Tp, _Lp, <anonymous>,
> > > <anonymous>
> > > > ::element_type* std::__shared_ptr_access<_Tp, _Lp, <anonymous>,
> > > <anonymous> >::operator->() const [with _Tp = A;
> > > __gnu_cxx::_Lock_policy
> > > _Lp = __gnu_cxx::_S_atomic; bool <anonymous> = false; bool
> > > <anonymous> =
> > > false]': event 10
> > > +           |
> > > +           |
> > > +    <------+
> > > +    |
> > > +  'int main()': events 11-12
> > > +    |
> > > +    |
> > > +   { dg-end-multiline-output "" } */
> > > \ No newline at end of file
> > > diff --git
> > > a/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-
> > > system.C
> > > b/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-
> > > system.C
> > > new file mode 100644
> > > index 00000000000..22fadc84e75
> > > --- /dev/null
> > > +++ b/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-
> > > system.C
> > > @@ -0,0 +1,19 @@
> > > +/* { dg-additional-options "-fdiagnostics-plain-output
> > > -fdiagnostics-path-format=inline-events -fanalyzer-trim-
> > > diagnostics=system"
> > > } */
> > > +/* { dg-skip-if "" { c++98_only }  } */
> > > +
> > > +#include <memory>
> > > +
> > > +struct A {int x; int y;};
> > > +
> > > +int main () {
> > > +  std::shared_ptr<A> a;
> > > +  a->x = 4; /* { dg-line deref_a } */
> > > +  /* { dg-warning "dereference of NULL" "" { target *-*-* }
> > > deref_a
> > > } */
> > > +
> > > +  return 0;
> > > +}
> > > +/* { dg-begin-multiline-output "" }
> > > +  'int main()': events 1-2
> > > +    |
> > > +    |
> > > +   { dg-end-multiline-output "" } */
> > > \ No newline at end of file
> > 
> >
diff mbox series

Patch

diff --git a/gcc/analyzer/analyzer.cc b/gcc/analyzer/analyzer.cc
index 5091fb7a583..b27d8e359db 100644
--- a/gcc/analyzer/analyzer.cc
+++ b/gcc/analyzer/analyzer.cc
@@ -274,7 +274,7 @@  is_named_call_p (const_tree fndecl, const char
*funcname)
    Compare with cp/typeck.cc: decl_in_std_namespace_p, but this doesn't
    rely on being the C++ FE (or handle inline namespaces inside of std).
 */

-static inline bool
+bool
 is_std_function_p (const_tree fndecl)
 {
   tree name_decl = DECL_NAME (fndecl);
diff --git a/gcc/analyzer/analyzer.h b/gcc/analyzer/analyzer.h
index 579517c23e6..31597079153 100644
--- a/gcc/analyzer/analyzer.h
+++ b/gcc/analyzer/analyzer.h
@@ -386,6 +386,7 @@  extern bool is_special_named_call_p (const gcall *call,
const char *funcname,
 extern bool is_named_call_p (const_tree fndecl, const char *funcname);
 extern bool is_named_call_p (const_tree fndecl, const char *funcname,
      const gcall *call, unsigned int num_args);
+extern bool is_std_function_p (const_tree fndecl);
 extern bool is_std_named_call_p (const_tree fndecl, const char *funcname);
 extern bool is_std_named_call_p (const_tree fndecl, const char *funcname,
  const gcall *call, unsigned int num_args);
diff --git a/gcc/analyzer/analyzer.opt b/gcc/analyzer/analyzer.opt
index 2760aaa8151..78ff2c07483 100644
--- a/gcc/analyzer/analyzer.opt
+++ b/gcc/analyzer/analyzer.opt
@@ -362,4 +362,8 @@  fdump-analyzer-untracked
 Common RejectNegative Var(flag_dump_analyzer_untracked)
 Emit custom warnings with internal details intended for analyzer
developers.

+fanalyzer-trim-diagnostics=
+Common Joined RejectNegative ToLower Var(flag_analyzer_trim_diagnostics)
Init("system")
+-fanalyzer-trim-diagnostics=[never|system|<maxdepth>] Trim diagnostics
path that are too long before emission.
+
 ; This comment is to ensure we retain the blank line above.
diff --git a/gcc/analyzer/diagnostic-manager.cc
b/gcc/analyzer/diagnostic-manager.cc
index cfca305d552..d946ad5bac7 100644
--- a/gcc/analyzer/diagnostic-manager.cc
+++ b/gcc/analyzer/diagnostic-manager.cc
@@ -20,9 +20,11 @@  along with GCC; see the file COPYING3.  If not see

 #include "config.h"
 #define INCLUDE_MEMORY
+#define INCLUDE_VECTOR
 #include "system.h"
 #include "coretypes.h"
 #include "tree.h"
+#include "input.h"
 #include "pretty-print.h"
 #include "gcc-rich-location.h"
 #include "gimple-pretty-print.h"
@@ -60,6 +62,37 @@  along with GCC; see the file COPYING3.  If not see

 #if ENABLE_ANALYZER

+/* Return positive value of fanalyzer-trim-diagnostics if a depth was given
+  or a negative value otherwise.  */
+
+long trim_diagnostics_depth ()
+{
+  if (trim_diagnostics_system_p () ||  trim_diagnostics_never_p ())
+    return -1;
+
+  char *end = NULL;
+  long depth = strtol (flag_analyzer_trim_diagnostics, &end, 10);
+  gcc_assert (end == NULL || *end == '\0');
+  gcc_assert (!(end == flag_analyzer_trim_diagnostics && depth == 0));
+  return depth;
+}
+
+/* Return true if fanalyzer-trim-diagnostics is set to "system".  */
+
+bool trim_diagnostics_system_p ()
+{
+  int len = strlen (flag_analyzer_trim_diagnostics);
+  return len == 6 && strcmp (flag_analyzer_trim_diagnostics, "system") ==
0;
+}
+
+/* Return true if fanalyzer-trim-diagnostics is set to "never".  */
+
+bool trim_diagnostics_never_p ()
+{
+  int len = strlen (flag_analyzer_trim_diagnostics);
+  return len == 5 && strcmp (flag_analyzer_trim_diagnostics, "never") == 0;
+}
+
 namespace ana {

 class feasible_worklist;
@@ -2281,6 +2314,8 @@  diagnostic_manager::prune_path (checker_path *path,
   path->maybe_log (get_logger (), "path");
   prune_for_sm_diagnostic (path, sm, sval, state);
   prune_interproc_events (path);
+  if (! trim_diagnostics_never_p ())
+    trim_diagnostic_path (path);
   consolidate_conditions (path);
   finish_pruning (path);
   path->maybe_log (get_logger (), "pruned");
@@ -2667,6 +2702,103 @@  diagnostic_manager::prune_interproc_events
(checker_path *path) const
   while (changed);
 }

+void
+diagnostic_manager::trim_diagnostic_path (checker_path *path) const
+{
+  if (trim_diagnostics_system_p ())
+    prune_system_headers (path);
+  else
+    prune_events_too_deep (path);
+}
+
+/* Remove everything within ]callsite, IDX].  */
+static void
+prune_within_frame (checker_path *path, int &idx)
+{
+  int nesting = 1;
+  while (idx >= 0 && nesting != 0)
+    {
+      if (path->get_checker_event (idx)->is_call_p ())
+        nesting--;
+      else if (path->get_checker_event (idx)->is_return_p ())
+        nesting++;
+
+      path->delete_event (idx--);
+    }
+}
+
+void
+diagnostic_manager::prune_system_headers (checker_path *path) const
+{
+  int idx = (signed)path->num_events () - 1;
+  while (idx >= 0)
+    {
+      const checker_event *event = path->get_checker_event (idx);
+      /* Prune everything between [..., system call, (...), system return,
...].  */
+      if (event->is_return_p ()
+          && in_system_header_at (event->get_location ()))
+      {
+        int ret_idx = idx;
+        prune_within_frame (path, idx);
+
+        if (get_logger ())
+        {
+          label_text desc
+            (path->get_checker_event (idx)->get_desc (false));
+          log ("filtering event %i-%i:"
+              " system header event: %s",
+              idx, ret_idx, desc.get ());
+        }
+        // delete callsite
+        if (idx >= 0)
+          path->delete_event (idx);
+      }
+
+      idx--;
+    }
+}
+
+void
+diagnostic_manager::prune_events_too_deep (checker_path *path) const
+{
+  long depth = 0;
+  long maxdepth = trim_diagnostics_depth ();
+  gcc_assert (maxdepth >= 0);
+  int idx = (signed)path->num_events () - 1;
+  if (idx >= 0)
+  {
+    depth = path->get_checker_event (0)->get_stack_depth ();
+    maxdepth += depth;
+  }
+
+  while (idx >= 0)
+    {
+      const checker_event *event = path->get_checker_event (idx);
+      /* Prune everything between [..., call too deep, (...), return too
deep, ...].  */
+      if (event->is_return_p ()
+          && event->get_stack_depth () > maxdepth)
+      {
+        int ret_idx = idx;
+        prune_within_frame (path, idx);
+
+        if (get_logger ())
+        {
+          label_text desc
+            (path->get_checker_event (idx)->get_desc (false));
+          log ("filtering event %i-%i:"
+              " event too deep: %s",
+              idx, ret_idx, desc.get ());
+        }
+        // delete callsite
+        if (idx >= 0)
+          path->delete_event (idx);
+      }
+
+      idx--;
+    }
+
+}
+
 /* Return true iff event IDX within PATH is on the same line as
REF_EXP_LOC.  */

 static bool
diff --git a/gcc/analyzer/diagnostic-manager.h
b/gcc/analyzer/diagnostic-manager.h
index 9b3e903fc5d..9afef0975be 100644
--- a/gcc/analyzer/diagnostic-manager.h
+++ b/gcc/analyzer/diagnostic-manager.h
@@ -21,6 +21,10 @@  along with GCC; see the file COPYING3.  If not see
 #ifndef GCC_ANALYZER_DIAGNOSTIC_MANAGER_H
 #define GCC_ANALYZER_DIAGNOSTIC_MANAGER_H

+long trim_diagnostics_depth ();
+bool trim_diagnostics_system_p ();
+bool trim_diagnostics_never_p ();
+
 namespace ana {

 class epath_finder;
@@ -180,6 +184,9 @@  private:
  state_machine::state_t state) const;
   void update_for_unsuitable_sm_exprs (tree *expr) const;
   void prune_interproc_events (checker_path *path) const;
+  void trim_diagnostic_path (checker_path *path) const;
+  void prune_system_headers (checker_path *path) const;
+  void prune_events_too_deep (checker_path *path) const;
   void consolidate_conditions (checker_path *path) const;
   void finish_pruning (checker_path *path) const;

diff --git a/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-0.C
b/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-0.C
new file mode 100644
index 00000000000..ddf39ed1690
--- /dev/null
+++ b/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-0.C
@@ -0,0 +1,19 @@ 
+/* { dg-additional-options "-fdiagnostics-plain-output
-fdiagnostics-path-format=inline-events -fanalyzer-trim-diagnostics=0" } */
+/* { dg-skip-if "" { c++98_only }  } */
+
+#include <memory>
+
+struct A {int x; int y;};
+
+int main () {
+  std::shared_ptr<A> a;
+  a->x = 4; /* { dg-line deref_a } */
+  /* { dg-warning "dereference of NULL" "" { target *-*-* } deref_a } */
+
+  return 0;
+}
+/* { dg-begin-multiline-output "" }
+  'int main()': events 1-2
+    |
+    |
+   { dg-end-multiline-output "" } */
\ No newline at end of file
diff --git a/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-1.C
b/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-1.C
new file mode 100644
index 00000000000..83a132078a1
--- /dev/null
+++ b/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-1.C
@@ -0,0 +1,28 @@ 
+/* { dg-additional-options "-fdiagnostics-plain-output
-fdiagnostics-path-format=inline-events -fanalyzer-trim-diagnostics=1" } */
+/* { dg-skip-if "" { c++98_only }  } */
+
+#include <memory>
+
+struct A {int x; int y;};
+
+int main () {
+  std::shared_ptr<A> a;
+  a->x = 4; /* { dg-line deref_a } */
+  /* { dg-warning "dereference of NULL" "" { target *-*-* } deref_a } */
+
+  return 0;
+}
+
+/* { dg-begin-multiline-output "" }
+  'int main()': events 1-2
+    |
+    |
+    +--> 'std::__shared_ptr_access<_Tp, _Lp, <anonymous>, <anonymous>
>::element_type* std::__shared_ptr_access<_Tp, _Lp, <anonymous>,
<anonymous> >::operator->() const [with _Tp = A; __gnu_cxx::_Lock_policy
_Lp = __gnu_cxx::_S_atomic; bool <anonymous> = false; bool <anonymous> =
false]': event 3
+           |
+           |
+    <------+
+    |
+  'int main()': events 4-5
+    |
+    |
+   { dg-end-multiline-output "" } */
\ No newline at end of file
diff --git a/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-2.C
b/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-2.C
new file mode 100644
index 00000000000..cf065929ec9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-2.C
@@ -0,0 +1,44 @@ 
+/* { dg-additional-options "-fdiagnostics-plain-output
-fdiagnostics-path-format=inline-events -fanalyzer-trim-diagnostics=2" } */
+/* { dg-skip-if "" { c++98_only }  } */
+
+#include <memory>
+
+struct A {int x; int y;};
+
+int main () {
+  std::shared_ptr<A> a;
+  a->x = 4; /* { dg-line deref_a } */
+  /* { dg-warning "dereference of NULL" "" { target *-*-* } deref_a } */
+
+  return 0;
+}
+
+/* { dg-begin-multiline-output "" }
+  'int main()': events 1-2
+    |
+    |
+    +--> 'std::__shared_ptr_access<_Tp, _Lp, <anonymous>, <anonymous>
>::element_type* std::__shared_ptr_access<_Tp, _Lp, <anonymous>,
<anonymous> >::operator->() const [with _Tp = A; __gnu_cxx::_Lock_policy
_Lp = __gnu_cxx::_S_atomic; bool <anonymous> = false; bool <anonymous> =
false]': events 3-4
+           |
+           |
+           +--> 'std::__shared_ptr_access<_Tp, _Lp, <anonymous>,
<anonymous> >::element_type* std::__shared_ptr_access<_Tp, _Lp,
<anonymous>, <anonymous> >::_M_get() const [with _Tp = A;
__gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic; bool <anonymous> =
false; bool <anonymous> = false]': events 5-6
+                  |
+                  |
+                  +--> 'std::__shared_ptr<_Tp, _Lp>::element_type*
std::__shared_ptr<_Tp, _Lp>::get() const [with _Tp = A;
__gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic]': events 7-8
+                         |
+                         |
+                  <------+
+                  |
+                'std::__shared_ptr_access<_Tp, _Lp, <anonymous>,
<anonymous> >::element_type* std::__shared_ptr_access<_Tp, _Lp,
<anonymous>, <anonymous> >::_M_get() const [with _Tp = A;
__gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic; bool <anonymous> =
false; bool <anonymous> = false]': event 9
+                  |
+                  |
+           <------+
+           |
+         'std::__shared_ptr_access<_Tp, _Lp, <anonymous>, <anonymous>
>::element_type* std::__shared_ptr_access<_Tp, _Lp, <anonymous>,
<anonymous> >::operator->() const [with _Tp = A; __gnu_cxx::_Lock_policy
_Lp = __gnu_cxx::_S_atomic; bool <anonymous> = false; bool <anonymous> =
false]': event 10
+           |
+           |
+    <------+
+    |
+  'int main()': events 11-12
+    |
+    |
+   { dg-end-multiline-output "" } */
\ No newline at end of file
diff --git
a/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-default.C
b/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-default.C
new file mode 100644
index 00000000000..fc271309d4e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-default.C
@@ -0,0 +1,21 @@ 
+/* { dg-additional-options "-O0 -fdiagnostics-plain-output
-fdiagnostics-path-format=inline-events" } */
+/* { dg-skip-if "" { c++98_only }  } */
+
+// Must behave like -fanalyzer-trim-diagnostics=system
+
+#include <memory>
+
+struct A {int x; int y;};
+
+int main () {
+  std::shared_ptr<A> a;
+  a->x = 4; /* { dg-line deref_a } */
+  /* { dg-warning "dereference of NULL" "" { target *-*-* } deref_a } */
+
+  return 0;
+}
+/* { dg-begin-multiline-output "" }
+  'int main()': events 1-2
+    |
+    |
+   { dg-end-multiline-output "" } */
\ No newline at end of file
diff --git
a/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-never.C
b/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-never.C
new file mode 100644
index 00000000000..a69416608da
--- /dev/null
+++ b/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-never.C
@@ -0,0 +1,44 @@ 
+/* { dg-additional-options "-fdiagnostics-plain-output
-fdiagnostics-path-format=inline-events -fanalyzer-trim-diagnostics=never"
} */
+/* { dg-skip-if "" { c++98_only }  } */
+
+#include <memory>
+
+struct A {int x; int y;};
+
+int main () {
+  std::shared_ptr<A> a;
+  a->x = 4; /* { dg-line deref_a } */
+  /* { dg-warning "dereference of NULL" "" { target *-*-* } deref_a } */
+
+  return 0;
+}
+
+/* { dg-begin-multiline-output "" }
+  'int main()': events 1-2
+    |
+    |
+    +--> 'std::__shared_ptr_access<_Tp, _Lp, <anonymous>, <anonymous>
>::element_type* std::__shared_ptr_access<_Tp, _Lp, <anonymous>,
<anonymous> >::operator->() const [with _Tp = A; __gnu_cxx::_Lock_policy
_Lp = __gnu_cxx::_S_atomic; bool <anonymous> = false; bool <anonymous> =
false]': events 3-4
+           |
+           |
+           +--> 'std::__shared_ptr_access<_Tp, _Lp, <anonymous>,
<anonymous> >::element_type* std::__shared_ptr_access<_Tp, _Lp,
<anonymous>, <anonymous> >::_M_get() const [with _Tp = A;
__gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic; bool <anonymous> =
false; bool <anonymous> = false]': events 5-6
+                  |
+                  |
+                  +--> 'std::__shared_ptr<_Tp, _Lp>::element_type*
std::__shared_ptr<_Tp, _Lp>::get() const [with _Tp = A;
__gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic]': events 7-8
+                         |
+                         |
+                  <------+
+                  |
+                'std::__shared_ptr_access<_Tp, _Lp, <anonymous>,
<anonymous> >::element_type* std::__shared_ptr_access<_Tp, _Lp,
<anonymous>, <anonymous> >::_M_get() const [with _Tp = A;
__gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic; bool <anonymous> =
false; bool <anonymous> = false]': event 9
+                  |
+                  |
+           <------+
+           |
+         'std::__shared_ptr_access<_Tp, _Lp, <anonymous>, <anonymous>
>::element_type* std::__shared_ptr_access<_Tp, _Lp, <anonymous>,
<anonymous> >::operator->() const [with _Tp = A; __gnu_cxx::_Lock_policy
_Lp = __gnu_cxx::_S_atomic; bool <anonymous> = false; bool <anonymous> =
false]': event 10
+           |
+           |
+    <------+
+    |
+  'int main()': events 11-12
+    |
+    |
+   { dg-end-multiline-output "" } */
\ No newline at end of file
diff --git
a/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-system.C
b/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-system.C
new file mode 100644
index 00000000000..22fadc84e75
--- /dev/null
+++ b/gcc/testsuite/g++.dg/analyzer/fanalyzer-trim-diagnostics-system.C
@@ -0,0 +1,19 @@ 
+/* { dg-additional-options "-fdiagnostics-plain-output
-fdiagnostics-path-format=inline-events -fanalyzer-trim-diagnostics=system"
} */
+/* { dg-skip-if "" { c++98_only }  } */
+
+#include <memory>
+
+struct A {int x; int y;};
+
+int main () {
+  std::shared_ptr<A> a;
+  a->x = 4; /* { dg-line deref_a } */
+  /* { dg-warning "dereference of NULL" "" { target *-*-* } deref_a } */
+
+  return 0;
+}
+/* { dg-begin-multiline-output "" }
+  'int main()': events 1-2
+    |
+    |