aboutsummaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
authorDavid Malcolm <dmalcolm@redhat.com>2021-07-16 15:47:06 -0400
committerDavid Malcolm <dmalcolm@redhat.com>2021-07-16 15:47:06 -0400
commit9ea10c480565fa42b1804fb436f7e26ca77b71a3 (patch)
tree06cc69c3752438b1cdccfa134b6e763d9a1ad303 /gcc
parent5932dd35eaa816e8d9b6406c6c433395ff5b6162 (diff)
downloadgcc-9ea10c480565fa42b1804fb436f7e26ca77b71a3.zip
gcc-9ea10c480565fa42b1804fb436f7e26ca77b71a3.tar.gz
gcc-9ea10c480565fa42b1804fb436f7e26ca77b71a3.tar.bz2
analyzer: add __analyzer_dump_state
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>
Diffstat (limited to 'gcc')
-rw-r--r--gcc/analyzer/engine.cc3
-rw-r--r--gcc/analyzer/program-state.cc49
-rw-r--r--gcc/analyzer/program-state.h6
-rw-r--r--gcc/analyzer/region-model-impl-calls.cc18
-rw-r--r--gcc/analyzer/region-model.h1
-rw-r--r--gcc/doc/analyzer.texi9
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/analyzer-decls.h5
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/dump-state.c14
8 files changed, 105 insertions, 0 deletions
diff --git a/gcc/analyzer/engine.cc b/gcc/analyzer/engine.cc
index 7662a7f..f9fc581 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 cc53aef..3081217 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 f16fe6b..8dee930 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 efb0fc8..545634b 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 71f6b3e..f07a287 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 2ca4bf6..aadb0de 100644
--- a/gcc/doc/analyzer.texi
+++ b/gcc/doc/analyzer.texi
@@ -522,6 +522,15 @@ it will also dump all of the states within the ``processed'' nodes.
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
will emit a warning with text "TRUE", FALSE" or "UNKNOWN" based on the
diff --git a/gcc/testsuite/gcc.dg/analyzer/analyzer-decls.h b/gcc/testsuite/gcc.dg/analyzer/analyzer-decls.h
index 2446693..e8745c0 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 0000000..618a5a9
--- /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'" } */
+}