return NULL;
}
-/* Return true if NODE and FUN should be traversed directly, rather than
+/* Return true if FUN should be traversed directly, rather than only as
called via other functions. */
static bool
-toplevel_function_p (cgraph_node *node, function *fun, logger *logger)
+toplevel_function_p (function *fun, logger *logger)
{
- /* TODO: better logic here
- e.g. only if more than one caller, and significantly complicated.
- Perhaps some whole-callgraph analysis to decide if it's worth summarizing
- an edge, and if so, we need summaries. */
- if (flag_analyzer_call_summaries)
- {
- int num_call_sites = 0;
- for (cgraph_edge *edge = node->callers; edge; edge = edge->next_caller)
- ++num_call_sites;
-
- /* For now, if there's more than one in-edge, and we want call
- summaries, do it at the top level so that there's a chance
- we'll have a summary when we need one. */
- if (num_call_sites > 1)
- {
- if (logger)
- logger->log ("traversing %qE (%i call sites)",
- fun->decl, num_call_sites);
- return true;
- }
- }
-
- if (!TREE_PUBLIC (fun->decl))
+ /* Don't directly traverse into functions that have an "__analyzer_"
+ prefix. Doing so is useful for the analyzer test suite, allowing
+ us to have functions that are called in traversals, but not directly
+ explored, thus testing how the analyzer handles calls and returns.
+ With this, we can have DejaGnu directives that cover just the case
+ of where a function is called by another function, without generating
+ excess messages from the case of the first function being traversed
+ directly. */
+#define ANALYZER_PREFIX "__analyzer_"
+ if (!strncmp (IDENTIFIER_POINTER (DECL_NAME (fun->decl)), ANALYZER_PREFIX,
+ strlen (ANALYZER_PREFIX)))
{
if (logger)
- logger->log ("not traversing %qE (static)", fun->decl);
+ logger->log ("not traversing %qE (starts with %qs)",
+ fun->decl, ANALYZER_PREFIX);
return false;
}
return true;
}
-/* Callback for walk_tree for finding callbacks within initializers;
- ensure they are treated as possible entrypoints to the analysis. */
-
-static tree
-add_any_callbacks (tree *tp, int *, void *data)
-{
- exploded_graph *eg = (exploded_graph *)data;
- if (TREE_CODE (*tp) == FUNCTION_DECL)
- eg->on_escaped_function (*tp);
- return NULL_TREE;
-}
-
/* Add initial nodes to EG, with entrypoints for externally-callable
functions. */
FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node)
{
function *fun = node->get_fun ();
- if (!toplevel_function_p (node, fun, logger))
+ if (!toplevel_function_p (fun, logger))
continue;
exploded_node *enode = add_function_entry (fun);
if (logger)
logger->log ("did not create enode for %qE entrypoint", fun->decl);
}
}
-
- /* Find callbacks that are reachable from global initializers. */
- varpool_node *vpnode;
- FOR_EACH_VARIABLE (vpnode)
- {
- tree decl = vpnode->decl;
- if (!TREE_PUBLIC (decl))
- continue;
- tree init = DECL_INITIAL (decl);
- if (!init)
- continue;
- walk_tree (&init, add_any_callbacks, this, NULL);
- }
}
/* The main loop of the analysis.
#include "analyzer-decls.h"
-static void only_called_when_flag_a_true (int i)
+static void __analyzer_only_called_when_flag_a_true (int i)
{
__analyzer_eval (i == 42); /* { dg-warning "TRUE" } */
}
-static void only_called_when_flag_b_true (int i)
+static void __analyzer_only_called_when_flag_b_true (int i)
{
__analyzer_eval (i == 17); /* { dg-warning "TRUE" } */
}
__analyzer_eval (flag_b); /* { dg-warning "UNKNOWN" } */
__analyzer_eval (i == 42); /* { dg-warning "TRUE" } */
__analyzer_eval (i == 17); /* { dg-warning "FALSE" } */
- only_called_when_flag_a_true (i);
+ __analyzer_only_called_when_flag_a_true (i);
}
else
{
__analyzer_eval (flag_b); /* { dg-warning "UNKNOWN" } */
__analyzer_eval (i == 42); /* { dg-warning "FALSE" } */
__analyzer_eval (i == 17); /* { dg-warning "TRUE" } */
- only_called_when_flag_b_true (i);
+ __analyzer_only_called_when_flag_b_true (i);
}
}
}
static int __attribute__((noinline))
-only_called_by_test_34 (int parm)
+__analyzer_only_called_by_test_34 (int parm)
{
__analyzer_eval (parm == 42); /* { dg-warning "TRUE" } */
void test_34 (void)
{
- int result = only_called_by_test_34 (42);
+ int result = __analyzer_only_called_by_test_34 (42);
__analyzer_eval (result == 84); /* { dg-warning "TRUE" } */
}
}
return problem;
}
+
+/* As above, but call a static function.
+ Even if the path to the call of called_by_test_6a is falsely rejected
+ as infeasible, it still makes sense to complain about errors within
+ the called function. */
+
+static void __attribute__((noinline))
+called_by_test_6a (void *ptr)
+{
+ __builtin_free (ptr);
+ __builtin_free (ptr); /* { dg-message "double-'free'" } */
+}
+
+int test_6a (int a, int b, void *ptr)
+{
+ int problem = 0;
+ if (a)
+ problem = 1;
+ if (b)
+ {
+ if (!problem)
+ problem = 2;
+ called_by_test_6a (ptr);
+ }
+ return problem;
+}
#include "analyzer-decls.h"
-static int called_function(int j)
+static int __analyzer_called_function(int j)
{
int k;
__analyzer_eval (i > 4); /* { dg-warning "TRUE" } */
- i = called_function(i);
+ i = __analyzer_called_function(i);
__analyzer_eval (i > 3); /* { dg-warning "TRUE" "desired" { xfail *-*-* } } */
/* { dg-warning "UNKNOWN" "status quo" { target *-*-* } .-1 } */
}
static void __attribute__((noinline))
-called_from_main (void)
+__analyzer_called_from_main (void)
{
/* When accessed from main, the vars still have their initializer values. */
__analyzer_eval (a == 0); /* { dg-warning "TRUE" } */
before "main"). */
__analyzer_eval (stderr == 0); /* { dg-warning "UNKNOWN" } */
- called_from_main ();
+ __analyzer_called_from_main ();
unknown_fn (&a, &c);
custom_logger("got signal");
}
-static void register_handler ()
+static void __analyzer_register_handler ()
{
signal(SIGINT, int_handler);
}
void test (void)
{
- register_handler ();
+ __analyzer_register_handler ();
body_of_program();
}
| | |
| | (1) entry to 'test'
| NN | {
- | NN | register_handler ();
- | | ~~~~~~~~~~~~~~~~~~~
+ | NN | __analyzer_register_handler ();
+ | | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| | |
- | | (2) calling 'register_handler' from 'test'
+ | | (2) calling '__analyzer_register_handler' from 'test'
|
- +--> 'register_handler': events 3-4
+ +--> '__analyzer_register_handler': events 3-4
|
- | NN | static void register_handler ()
- | | ^~~~~~~~~~~~~~~~
+ | NN | static void __analyzer_register_handler ()
+ | | ^~~~~~~~~~~~~~~~~~~~~~~~~~~
| | |
- | | (3) entry to 'register_handler'
+ | | (3) entry to '__analyzer_register_handler'
| NN | {
| NN | signal(SIGINT, int_handler);
| | ~~~~~~~~~~~~~~~~~~~~~~~~~~~
__analyzer_describe (0, f.ptr); /* { dg-warning "svalue: 'INIT_VAL\\(f.ptr\\)'" } */
}
-static void called_by_test_2 (struct foo f_inner)
+static void __analyzer_called_by_test_2 (struct foo f_inner)
{
free (f_inner.ptr);
free (f_inner.ptr); /* { dg-warning "double-'free' of 'f_outer.ptr'" } */
}
void test_2 (struct foo f_outer)
{
- called_by_test_2 (f_outer);
+ __analyzer_called_by_test_2 (f_outer);
}
struct nested
struct foo f;
};
-static void called_by_test_3 (struct nested n_inner)
+static void __analyzer_called_by_test_3 (struct nested n_inner)
{
free (n_inner.f.ptr);
free (n_inner.f.ptr); /* { dg-warning "double-'free' of 'n_outer.f.ptr'" } */
}
void test_3 (struct nested n_outer)
{
- called_by_test_3 (n_outer);
+ __analyzer_called_by_test_3 (n_outer);
}
#define Z_NULL 0
static void __attribute__((noinline))
-test_1_callee (void *p, void *q)
+__analyzer_test_1_callee (void *p, void *q)
{
__analyzer_dump_exploded_nodes (0); /* { dg-warning "1 processed enode" } */
if (p == Z_NULL || q == Z_NULL)
return;
- test_1_callee (p, q);
+ __analyzer_test_1_callee (p, q);
}
static void __attribute__((noinline))
-test_2_callee (void *p, void *q)
+__analyzer_test_2_callee (void *p, void *q)
{
__analyzer_dump_exploded_nodes (0); /* { dg-warning "1 processed enode" } */
void test_2 (void *p, void *q)
{
if (p != Z_NULL && q != Z_NULL)
- test_2_callee (p, q);
+ __analyzer_test_2_callee (p, q);
}