[committed] analyzer: add __analyzer_dump_state

Message ID 20210716195544.75207-1-dmalcolm@redhat.com
State New
Headers show
Series
  • [committed] analyzer: add __analyzer_dump_state
Related show

Commit Message

Martin Sebor via Gcc-patches July 16, 2021, 7:55 p.m.
Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu.
Pushed to trunk as 9ea10c480565fa42b1804fb436f7e26ca77b71a3.

gcc/analyzer/ChangeLog:
	* engine.cc (exploded_node::on_stmt_pre): Handle
	__analyzer_dump_state.
	* program-state.cc (extrinsic_state::get_sm_idx_by_name): New.
	(program_state::impl_call_analyzer_dump_state): New.
	* program-state.h (extrinsic_state::get_sm_idx_by_name): New decl.
	(program_state::impl_call_analyzer_dump_state): New decl.
	* region-model-impl-calls.cc
	(call_details::get_arg_string_literal): New.
	* region-model.h (call_details::get_arg_string_literal): New decl.

gcc/ChangeLog:
	* doc/analyzer.texi: Add __analyzer_dump_state.

gcc/testsuite/ChangeLog:
	* gcc.dg/analyzer/analyzer-decls.h (__analyzer_dump_state): New.
	* gcc.dg/analyzer/dump-state.c: New test.

Signed-off-by: David Malcolm <dmalcolm@redhat.com>

---
 gcc/analyzer/engine.cc                        |  3 ++
 gcc/analyzer/program-state.cc                 | 49 +++++++++++++++++++
 gcc/analyzer/program-state.h                  |  6 +++
 gcc/analyzer/region-model-impl-calls.cc       | 18 +++++++
 gcc/analyzer/region-model.h                   |  1 +
 gcc/doc/analyzer.texi                         |  9 ++++
 .../gcc.dg/analyzer/analyzer-decls.h          |  5 ++
 gcc/testsuite/gcc.dg/analyzer/dump-state.c    | 14 ++++++
 8 files changed, 105 insertions(+)
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/dump-state.c

-- 
2.26.3

Patch

diff --git a/gcc/analyzer/engine.cc b/gcc/analyzer/engine.cc
index 7662a7f7bab..f9fc58180b7 100644
--- a/gcc/analyzer/engine.cc
+++ b/gcc/analyzer/engine.cc
@@ -1270,6 +1270,9 @@  exploded_node::on_stmt_pre (exploded_graph &eg,
 	  state->dump (eg.get_ext_state (), true);
 	  return;
 	}
+      else if (is_special_named_call_p (call, "__analyzer_dump_state", 2))
+	state->impl_call_analyzer_dump_state (call, eg.get_ext_state (),
+					      ctxt);
       else if (is_setjmp_call_p (call))
 	{
 	  state->m_region_model->on_setjmp (call, this, ctxt);
diff --git a/gcc/analyzer/program-state.cc b/gcc/analyzer/program-state.cc
index cc53aef552f..30812176bd8 100644
--- a/gcc/analyzer/program-state.cc
+++ b/gcc/analyzer/program-state.cc
@@ -131,6 +131,27 @@  extrinsic_state::get_model_manager () const
     return NULL; /* for selftests.  */
 }
 
+/* Try to find a state machine named NAME.
+   If found, return true and write its index to *OUT.
+   Otherwise return false.  */
+
+bool
+extrinsic_state::get_sm_idx_by_name (const char *name, unsigned *out) const
+{
+  unsigned i;
+  state_machine *sm;
+  FOR_EACH_VEC_ELT (m_checkers, i, sm)
+    if (0 == strcmp (name, sm->get_name ()))
+      {
+	/* Found NAME.  */
+	*out = i;
+	return true;
+      }
+
+  /* NAME not found.  */
+  return false;
+}
+
 /* struct sm_state_map::entry_t.  */
 
 int
@@ -1290,6 +1311,34 @@  program_state::detect_leaks (const program_state &src_state,
 	dest_state.m_region_model->unset_dynamic_extents (reg);
 }
 
+/* Handle calls to "__analyzer_dump_state".  */
+
+void
+program_state::impl_call_analyzer_dump_state (const gcall *call,
+					      const extrinsic_state &ext_state,
+					      region_model_context *ctxt)
+{
+  call_details cd (call, m_region_model, ctxt);
+  const char *sm_name = cd.get_arg_string_literal (0);
+  if (!sm_name)
+    {
+      error_at (call->location, "cannot determine state machine");
+      return;
+    }
+  unsigned sm_idx;
+  if (!ext_state.get_sm_idx_by_name (sm_name, &sm_idx))
+    {
+      error_at (call->location, "unrecognized state machine %qs", sm_name);
+      return;
+    }
+  const sm_state_map *smap = m_checker_states[sm_idx];
+
+  const svalue *sval = cd.get_arg_svalue (1);
+
+  state_machine::state_t state = smap->get_state (sval, ext_state);
+  warning_at (call->location, 0, "state: %qs", state->get_name ());
+}
+
 #if CHECKING_P
 
 namespace selftest {
diff --git a/gcc/analyzer/program-state.h b/gcc/analyzer/program-state.h
index f16fe6ba984..8dee930665c 100644
--- a/gcc/analyzer/program-state.h
+++ b/gcc/analyzer/program-state.h
@@ -58,6 +58,8 @@  public:
   engine *get_engine () const { return m_engine; }
   region_model_manager *get_model_manager () const;
 
+  bool get_sm_idx_by_name (const char *name, unsigned *out) const;
+
 private:
   /* The state machines.  */
   auto_delete_vec <state_machine> &m_checkers;
@@ -256,6 +258,10 @@  public:
 			    const extrinsic_state &ext_state,
 			    region_model_context *ctxt);
 
+  void impl_call_analyzer_dump_state (const gcall *call,
+				      const extrinsic_state &ext_state,
+				      region_model_context *ctxt);
+
   /* TODO: lose the pointer here (const-correctness issues?).  */
   region_model *m_region_model;
   auto_delete_vec<sm_state_map> m_checker_states;
diff --git a/gcc/analyzer/region-model-impl-calls.cc b/gcc/analyzer/region-model-impl-calls.cc
index efb0fc83433..545634b9dc7 100644
--- a/gcc/analyzer/region-model-impl-calls.cc
+++ b/gcc/analyzer/region-model-impl-calls.cc
@@ -140,6 +140,24 @@  call_details::get_arg_svalue (unsigned idx) const
   return m_model->get_rvalue (arg, m_ctxt);
 }
 
+/* Attempt to get the string literal for argument IDX, or return NULL
+   otherwise.
+   For use when implementing "__analyzer_*" functions that take
+   string literals.  */
+
+const char *
+call_details::get_arg_string_literal (unsigned idx) const
+{
+  const svalue *str_arg = get_arg_svalue (idx);
+  if (const region *pointee = str_arg->maybe_get_region ())
+    if (const string_region *string_reg = pointee->dyn_cast_string_region ())
+      {
+	tree string_cst = string_reg->get_string_cst ();
+	return TREE_STRING_POINTER (string_cst);
+      }
+  return NULL;
+}
+
 /* Dump a multiline representation of this call to PP.  */
 
 void
diff --git a/gcc/analyzer/region-model.h b/gcc/analyzer/region-model.h
index 71f6b3ee11e..f07a287f681 100644
--- a/gcc/analyzer/region-model.h
+++ b/gcc/analyzer/region-model.h
@@ -468,6 +468,7 @@  public:
   tree get_arg_tree (unsigned idx) const;
   tree get_arg_type (unsigned idx) const;
   const svalue *get_arg_svalue (unsigned idx) const;
+  const char *get_arg_string_literal (unsigned idx) const;
 
   void dump_to_pp (pretty_printer *pp, bool simple) const;
   void dump (bool simple) const;
diff --git a/gcc/doc/analyzer.texi b/gcc/doc/analyzer.texi
index 2ca4bf61352..aadb0de0798 100644
--- a/gcc/doc/analyzer.texi
+++ b/gcc/doc/analyzer.texi
@@ -521,6 +521,15 @@  it will also dump all of the states within the ``processed'' nodes.
 @end smallexample
 will dump the region_model's state to stderr.
 
+@smallexample
+__analyzer_dump_state ("malloc", ptr);
+@end smallexample
+
+will emit a warning describing the state of the 2nd argument
+(which can be of any type) with respect to the state machine with
+a name matching the 1st argument (which must be a string literal).
+This is for use when debugging, and may be of use in DejaGnu tests.
+
 @smallexample
 __analyzer_eval (expr);
 @end smallexample
diff --git a/gcc/testsuite/gcc.dg/analyzer/analyzer-decls.h b/gcc/testsuite/gcc.dg/analyzer/analyzer-decls.h
index 24466939882..e8745c0933f 100644
--- a/gcc/testsuite/gcc.dg/analyzer/analyzer-decls.h
+++ b/gcc/testsuite/gcc.dg/analyzer/analyzer-decls.h
@@ -35,6 +35,11 @@  extern void __analyzer_dump_path (void);
 /* Dump the region_model's state to stderr.  */
 extern void __analyzer_dump_region_model (void);
 
+/* Emit a warning describing the state of the 2nd argument
+   (which can be of any type) with respect to NAME.
+   This is for use when debugging, and may be of use in DejaGnu tests.  */
+extern void __analyzer_dump_state (const char *name, ...);
+
 /* Emit a warning with text "TRUE", FALSE" or "UNKNOWN" based on the
    truthfulness of the argument.  */
 extern void __analyzer_eval (int);
diff --git a/gcc/testsuite/gcc.dg/analyzer/dump-state.c b/gcc/testsuite/gcc.dg/analyzer/dump-state.c
new file mode 100644
index 00000000000..618a5a9d781
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/dump-state.c
@@ -0,0 +1,14 @@ 
+/* Verify that __analyzer_dump_state works as expected.  */
+
+#include <stdlib.h>
+#include "analyzer-decls.h"
+
+void test_1 (void)
+{
+  void *p = malloc (1024);
+  __analyzer_dump_state ("malloc", p); /* { dg-warning "state: 'unchecked'" } */
+  free (p);
+  __analyzer_dump_state ("malloc", p); /* { dg-warning "state: 'freed'" } */
+  __analyzer_dump_state (NULL, p); /* { dg-error "cannot determine state machine" } */
+  __analyzer_dump_state ("not a state machine", p); /* { dg-error "unrecognized state machine 'not a state machine'" } */
+}