I've found this useful for debugging state explosions in the analyzer.
gcc/analyzer/ChangeLog:
* analysis-plan.cc: Include "json.h".
* analyzer.opt (fdump-analyzer-json): New.
* call-string.cc: Include "json.h".
(call_string::to_json): New.
* call-string.h (call_string::to_json): New decl.
* checker-path.cc: Include "json.h".
* constraint-manager.cc: Include "json.h".
(equiv_class::to_json): New.
(constraint::to_json): New.
(constraint_manager::to_json): New.
* constraint-manager.h (equiv_class::to_json): New decl.
(constraint::to_json): New decl.
(constraint_manager::to_json): New decl.
* diagnostic-manager.cc: Include "json.h".
(saved_diagnostic::to_json): New.
(diagnostic_manager::to_json): New.
* diagnostic-manager.h (saved_diagnostic::to_json): New decl.
(diagnostic_manager::to_json): New decl.
* engine.cc: Include "json.h", <zlib.h>.
(exploded_node::status_to_str): New.
(exploded_node::to_json): New.
(exploded_edge::to_json): New.
(exploded_graph::to_json): New.
(dump_analyzer_json): New.
(impl_run_checkers): Call it.
* exploded-graph.h (exploded_node::status_to_str): New decl.
(exploded_node::to_json): New.
(exploded_edge::to_json): New.
(exploded_graph::to_json): New.
* pending-diagnostic.cc: Include "json.h".
* program-point.cc: Include "json.h".
(program_point::to_json): New.
* program-point.h (program_point::to_json): New decl.
* program-state.cc: Include "json.h".
(extrinsic_state::to_json): New.
(sm_state_map::to_json): New.
(program_state::to_json): New.
* program-state.h (extrinsic_state::to_json): New decl.
(sm_state_map::to_json): New decl.
(program_state::to_json): New decl.
* region-model-impl-calls.cc: Include "json.h".
* region-model-manager.cc: Include "json.h".
* region-model-reachability.cc: Include "json.h".
* region-model.cc: Include "json.h".
* region-model.h (svalue::to_json): New decl.
(region::to_json): New decl.
* region.cc: Include "json.h".
(region::to_json: New.
* sm-file.cc: Include "json.h".
* sm-malloc.cc: Include "json.h".
* sm-pattern-test.cc: Include "json.h".
* sm-sensitive.cc: Include "json.h".
* sm-signal.cc: Include "json.h".
(signal_delivery_edge_info_t::to_json): New.
* sm-taint.cc: Include "json.h".
* sm.cc: Include "diagnostic.h", "tree-diagnostic.h", and
"json.h".
(state_machine::state::to_json): New.
(state_machine::to_json): New.
* sm.h (state_machine::state::to_json): New.
(state_machine::to_json): New.
* state-purge.cc: Include "json.h".
* store.cc: Include "json.h".
(binding_key::get_desc): New.
(binding_map::to_json): New.
(binding_cluster::to_json): New.
(store::to_json): New.
* store.h (binding_key::get_desc): New decl.
(binding_map::to_json): New decl.
(binding_cluster::to_json): New decl.
(store::to_json): New decl.
* supergraph.cc: Include "json.h".
(supergraph::to_json): New.
(supernode::to_json): New.
(superedge::to_json): New.
* supergraph.h (supergraph::to_json): New decl.
(supernode::to_json): New decl.
(superedge::to_json): New decl.
* svalue.cc: Include "json.h".
(svalue::to_json): New.
gcc/ChangeLog:
* doc/analyzer.texi (Other Debugging Techniques): Mention
-fdump-analyzer-json.
* doc/invoke.texi (Static Analyzer Options): Add
-fdump-analyzer-json.
#include "timevar.h"
#include "ipa-utils.h"
#include "function.h"
+#include "json.h"
#include "analyzer/analyzer.h"
#include "diagnostic-core.h"
#include "analyzer/analyzer-logging.h"
Common RejectNegative Var(flag_dump_analyzer_exploded_nodes_3)
Dump a textual representation of the exploded graph to SRCFILE.eg-ID.txt.
+fdump-analyzer-json
+Common RejectNegative Var(flag_dump_analyzer_json)
+Dump analyzer-specific data to a SRCFILE.analyzer.json.gz file.
+
fdump-analyzer-state-purge
Common RejectNegative Var(flag_dump_analyzer_state_purge)
Dump state-purging information to a SRCFILE.state-purge.dot file.
#include "pretty-print.h"
#include "tree.h"
#include "options.h"
+#include "json.h"
#include "analyzer/call-string.h"
#include "ordered-hash-map.h"
#include "options.h"
pp_string (pp, "]");
}
+/* Return a new json::array of the form
+ [{"src_snode_idx" : int,
+ "dst_snode_idx" : int,
+ "funcname" : str},
+ ...for each return_superedge in the callstring]. */
+
+json::value *
+call_string::to_json () const
+{
+ json::array *arr = new json::array ();
+
+ const return_superedge *e;
+ int i;
+ FOR_EACH_VEC_ELT (m_return_edges, i, e)
+ {
+ json::object *e_obj = new json::object ();
+ e_obj->set ("src_snode_idx",
+ new json::integer_number (e->m_src->m_index));
+ e_obj->set ("dst_snode_idx",
+ new json::integer_number (e->m_dest->m_index));
+ e_obj->set ("funcname",
+ new json::string (function_name (e->m_dest->m_fun)));
+ arr->append (e_obj);
+ }
+
+ return arr;
+}
+
/* Generate a hash value for this call_string. */
hashval_t
void print (pretty_printer *pp) const;
+ json::value *to_json () const;
+
hashval_t hash () const;
bool empty_p () const { return m_return_edges.is_empty (); }
#include "fibonacci_heap.h"
#include "diagnostic-event-id.h"
#include "shortest-paths.h"
+#include "json.h"
#include "analyzer/analyzer.h"
#include "analyzer/analyzer-logging.h"
#include "analyzer/sm.h"
#include "diagnostic-core.h"
#include "graphviz.h"
#include "function.h"
+#include "json.h"
#include "analyzer/analyzer.h"
#include "ordered-hash-map.h"
#include "options.h"
pp_character (pp, '}');
}
+/* Return a new json::object of the form
+ {"svals" : [str],
+ "constant" : optional str}. */
+
+json::object *
+equiv_class::to_json () const
+{
+ json::object *ec_obj = new json::object ();
+
+ json::array *sval_arr = new json::array ();
+ int i;
+ const svalue *sval;
+ FOR_EACH_VEC_ELT (m_vars, i, sval)
+ sval_arr->append (sval->to_json ());
+ ec_obj->set ("svals", sval_arr);
+
+ if (m_constant)
+ {
+ pretty_printer pp;
+ pp_format_decoder (&pp) = default_tree_printer;
+ pp_printf (&pp, "%qE", m_constant);
+ ec_obj->set ("constant", new json::string (pp_formatted_text (&pp)));
+ }
+
+ return ec_obj;
+}
+
/* Generate a hash value for this equiv_class.
This relies on the ordering of m_vars, and so this object needs to
have been canonicalized for this to be meaningful. */
m_rhs.get_obj (cm).print (pp);
}
+/* Return a new json::object of the form
+ {"lhs" : int, the EC index
+ "op" : str,
+ "rhs" : int, the EC index}. */
+
+json::object *
+constraint::to_json () const
+{
+ json::object *con_obj = new json::object ();
+
+ con_obj->set ("lhs", new json::integer_number (m_lhs.as_int ()));
+ con_obj->set ("op", new json::string (constraint_op_code (m_op)));
+ con_obj->set ("rhs", new json::integer_number (m_rhs.as_int ()));
+
+ return con_obj;
+}
+
/* Generate a hash value for this constraint. */
hashval_t
cm.dump ();
}
+/* Return a new json::object of the form
+ {"ecs" : array of objects, one per equiv_class
+ "constraints" : array of objects, one per constraint}. */
+
+json::object *
+constraint_manager::to_json () const
+{
+ json::object *cm_obj = new json::object ();
+
+ /* Equivalence classes. */
+ {
+ json::array *ec_arr = new json::array ();
+ int i;
+ equiv_class *ec;
+ FOR_EACH_VEC_ELT (m_equiv_classes, i, ec)
+ ec_arr->append (ec->to_json ());
+ cm_obj->set ("ecs", ec_arr);
+ }
+
+ /* Constraints. */
+ {
+ json::array *con_arr = new json::array ();
+ int i;
+ constraint *c;
+ FOR_EACH_VEC_ELT (m_constraints, i, c)
+ con_arr->append (c->to_json ());
+ cm_obj->set ("constraints", con_arr);
+ }
+
+ return cm_obj;
+}
+
/* Attempt to add the constraint LHS OP RHS to this constraint_manager.
Return true if the constraint could be added (or is already true).
Return false if the constraint contradicts existing knowledge. */
void print (pretty_printer *pp) const;
+ json::object *to_json () const;
+
/* An equivalence class can contain multiple constants (e.g. multiple
different zeroes, for different types); these are just for the last
constant added. */
void print (pretty_printer *pp, const constraint_manager &cm) const;
+ json::object *to_json () const;
+
hashval_t hash () const;
bool operator== (const constraint &other) const;
void dump (FILE *fp) const;
void dump () const;
+ json::object *to_json () const;
+
const equiv_class &get_equiv_class_by_index (unsigned idx) const
{
return *m_equiv_classes[idx];
#include "tristate.h"
#include "selftest.h"
#include "ordered-hash-map.h"
+#include "json.h"
#include "analyzer/analyzer.h"
#include "analyzer/analyzer-logging.h"
#include "analyzer/sm.h"
&& m_trailing_eedge == other.m_trailing_eedge);
}
+/* Return a new json::object of the form
+ {"sm": optional str,
+ "enode": int,
+ "snode": int,
+ "sval": optional str,
+ "state": optional str,
+ "path_length": int,
+ "pending_diagnostic": str}. */
+
+json::object *
+saved_diagnostic::to_json () const
+{
+ json::object *sd_obj = new json::object ();
+
+ if (m_sm)
+ sd_obj->set ("sm", new json::string (m_sm->get_name ()));
+ sd_obj->set ("enode", new json::integer_number (m_enode->m_index));
+ sd_obj->set ("snode", new json::integer_number (m_snode->m_index));
+ if (m_sval)
+ sd_obj->set ("sval", m_sval->to_json ());
+ if (m_state)
+ sd_obj->set ("state", m_state->to_json ());
+ sd_obj->set ("path_length", new json::integer_number (m_epath_length));
+ sd_obj->set ("pending_diagnostic", new json::string (m_d->get_kind ()));
+
+ /* We're not yet JSONifying the following fields:
+ const gimple *m_stmt;
+ stmt_finder *m_stmt_finder;
+ tree m_var;
+ exploded_edge *m_trailing_eedge;
+ enum status m_status;
+ feasibility_problem *m_problem;
+ */
+
+ return sd_obj;
+}
+
/* State for building a checker_path from a particular exploded_path.
In particular, this precomputes reachability information: the set of
source enodes for which a path be found to the diagnostic enode. */
add_diagnostic (NULL, enode, snode, stmt, finder, NULL_TREE, NULL, 0, d);
}
+/* Return a new json::object of the form
+ {"diagnostics" : [obj for saved_diagnostic]}. */
+
+json::object *
+diagnostic_manager::to_json () const
+{
+ json::object *dm_obj = new json::object ();
+
+ {
+ json::array *sd_arr = new json::array ();
+ int i;
+ saved_diagnostic *sd;
+ FOR_EACH_VEC_ELT (m_saved_diagnostics, i, sd)
+ sd_arr->append (sd->to_json ());
+ dm_obj->set ("diagnostics", sd_arr);
+ }
+
+ return dm_obj;
+}
+
/* A class for identifying sets of duplicated pending_diagnostic.
We want to find the simplest dedupe_candidate amongst those that share a
bool operator== (const saved_diagnostic &other) const;
+ json::object *to_json () const;
+
void set_feasible ()
{
gcc_assert (m_status == STATUS_NEW);
engine *get_engine () const { return m_eng; }
+ json::object *to_json () const;
+
void add_diagnostic (const state_machine *sm,
const exploded_node *enode,
const supernode *snode, const gimple *stmt,
#include "tristate.h"
#include "ordered-hash-map.h"
#include "selftest.h"
+#include "json.h"
#include "analyzer/analyzer.h"
#include "analyzer/analyzer-logging.h"
#include "analyzer/call-string.h"
#include "analyzer/checker-path.h"
#include "analyzer/state-purge.h"
#include "analyzer/bar-chart.h"
+#include <zlib.h>
/* For an overview, see gcc/doc/analyzer.texi. */
/* class exploded_node : public dnode<eg_traits>. */
+const char *
+exploded_node::status_to_str (enum status s)
+{
+ switch (s)
+ {
+ default: gcc_unreachable ();
+ case STATUS_WORKLIST: return "WORKLIST";
+ case STATUS_PROCESSED: return "PROCESSED";
+ case STATUS_MERGER: return "MERGER";
+ case STATUS_BULK_MERGED: return "BULK_MERGED";
+ }
+}
+
/* exploded_node's ctor. */
exploded_node::exploded_node (const point_and_state &ps,
dump (stderr, ext_state);
}
+/* Return a new json::object of the form
+ {"point" : object for program_point,
+ "state" : object for program_state,
+ "status" : str,
+ "idx" : int,
+ "processed_stmts" : int}. */
+
+json::object *
+exploded_node::to_json (const extrinsic_state &ext_state) const
+{
+ json::object *enode_obj = new json::object ();
+
+ enode_obj->set ("point", get_point ().to_json ());
+ enode_obj->set ("state", get_state ().to_json (ext_state));
+ enode_obj->set ("status", new json::string (status_to_str (m_status)));
+ enode_obj->set ("idx", new json::integer_number (m_index));
+ enode_obj->set ("processed_stmts",
+ new json::integer_number (m_num_processed_stmts));
+
+ return enode_obj;
+}
+
} // namespace ana
/* Return true if FNDECL has a gimple body. */
pp_printf (pp, "\"];\n");
}
+/* Return a new json::object of the form
+ {"src_idx": int, the index of the source exploded edge,
+ "dst_idx": int, the index of the destination exploded edge,
+ "sedge": (optional) object for the superedge, if any,
+ "custom": (optional) str, a description, if this is a custom edge}. */
+
+json::object *
+exploded_edge::to_json () const
+{
+ json::object *eedge_obj = new json::object ();
+ eedge_obj->set ("src_idx", new json::integer_number (m_src->m_index));
+ eedge_obj->set ("dst_idx", new json::integer_number (m_dest->m_index));
+ if (m_sedge)
+ eedge_obj->set ("sedge", m_sedge->to_json ());
+ if (m_custom_info)
+ {
+ pretty_printer pp;
+ pp_format_decoder (&pp) = default_tree_printer;
+ m_custom_info->print (&pp);
+ eedge_obj->set ("custom", new json::string (pp_formatted_text (&pp)));
+ }
+ return eedge_obj;
+}
+
/* struct stats. */
/* stats' ctor. */
snode->m_index, state_idx);
}
+/* Return a new json::object of the form
+ {"nodes" : [objs for enodes],
+ "edges" : [objs for eedges],
+ "ext_state": object for extrinsic_state,
+ "diagnostic_manager": object for diagnostic_manager}. */
+
+json::object *
+exploded_graph::to_json () const
+{
+ json::object *egraph_obj = new json::object ();
+
+ /* Nodes. */
+ {
+ json::array *nodes_arr = new json::array ();
+ unsigned i;
+ exploded_node *n;
+ FOR_EACH_VEC_ELT (m_nodes, i, n)
+ nodes_arr->append (n->to_json (m_ext_state));
+ egraph_obj->set ("nodes", nodes_arr);
+ }
+
+ /* Edges. */
+ {
+ json::array *edges_arr = new json::array ();
+ unsigned i;
+ exploded_edge *n;
+ FOR_EACH_VEC_ELT (m_edges, i, n)
+ edges_arr->append (n->to_json ());
+ egraph_obj->set ("edges", edges_arr);
+ }
+
+ /* m_sg is JSONified at the top-level. */
+
+ egraph_obj->set ("ext_state", m_ext_state.to_json ());
+ egraph_obj->set ("diagnostic_manager", m_diagnostic_manager.to_json ());
+
+ /* The following fields aren't yet being JSONified:
+ worklist m_worklist;
+ const state_purge_map *const m_purge_map;
+ const analysis_plan &m_plan;
+ stats m_global_stats;
+ function_stat_map_t m_per_function_stats;
+ stats m_functionless_stats;
+ call_string_data_map_t m_per_call_string_data;
+ auto_vec<int> m_PK_AFTER_SUPERNODE_per_snode; */
+
+ return egraph_obj;
+}
+
/* Look for the last use of SEARCH_STMT within this path.
If found write the edge's index to *OUT_IDX and return true, otherwise
return false. */
auto_delete_vec<auto_vec <exploded_node *> > m_enodes_per_snodes;
};
+/* Implement -fdump-analyzer-json. */
+
+static void
+dump_analyzer_json (const supergraph &sg,
+ const exploded_graph &eg)
+{
+ auto_timevar tv (TV_ANALYZER_DUMP);
+ char *filename = concat (dump_base_name, ".analyzer.json.gz", NULL);
+ gzFile output = gzopen (filename, "w");
+ if (!output)
+ {
+ error_at (UNKNOWN_LOCATION, "unable to open %qs for writing", filename);
+ free (filename);
+ return;
+ }
+
+ json::object *toplev_obj = new json::object ();
+ toplev_obj->set ("sgraph", sg.to_json ());
+ toplev_obj->set ("egraph", eg.to_json ());
+
+ pretty_printer pp;
+ toplev_obj->print (&pp);
+ pp_formatted_text (&pp);
+
+ delete toplev_obj;
+
+ if (gzputs (output, pp_formatted_text (&pp)) == EOF
+ || gzclose (output))
+ error_at (UNKNOWN_LOCATION, "error writing %qs", filename);
+
+ free (filename);
+}
+
/* Run the analysis "engine". */
void
free (filename);
}
+ if (flag_dump_analyzer_json)
+ dump_analyzer_json (sg, eg);
+
delete purge_map;
}
/* Node was processed by maybe_process_run_of_before_supernode_enodes. */
STATUS_BULK_MERGED
};
+ static const char * status_to_str (enum status s);
exploded_node (const point_and_state &ps, int index);
void dump (FILE *fp, const extrinsic_state &ext_state) const;
void dump (const extrinsic_state &ext_state) const;
+ json::object *to_json (const extrinsic_state &ext_state) const;
+
/* The result of on_stmt. */
struct on_stmt_flags
{
void dump_dot (graphviz_out *gv, const dump_args_t &args)
const FINAL OVERRIDE;
+ json::object *to_json () const;
+
//private:
const superedge *const m_sedge;
void dump_states_for_supernode (FILE *, const supernode *snode) const;
void dump_exploded_nodes () const;
+ json::object *to_json () const;
+
exploded_node *get_node_by_index (int idx) const;
const call_string_data_map_t *get_per_call_string_data () const
#include "intl.h"
#include "diagnostic.h"
#include "function.h"
+#include "json.h"
#include "analyzer/analyzer.h"
#include "diagnostic-event-id.h"
#include "analyzer/analyzer-logging.h"
#include "tree.h"
#include "gimple-pretty-print.h"
#include "gcc-rich-location.h"
+#include "json.h"
#include "analyzer/call-string.h"
#include "ordered-hash-map.h"
#include "options.h"
pp_flush (&pp);
}
+/* Return a new json::object of the form
+ {"kind" : str,
+ "snode_idx" : int (optional), the index of the supernode,
+ "from_edge_snode_idx" : int (only for kind=='PK_BEFORE_SUPERNODE'),
+ "stmt_idx": int (only for kind=='PK_BEFORE_STMT',
+ "call_string": object for the call_string}. */
+
+json::object *
+program_point::to_json () const
+{
+ json::object *point_obj = new json::object ();
+
+ point_obj->set ("kind",
+ new json::string (point_kind_to_string (get_kind ())));
+
+ if (get_supernode ())
+ point_obj->set ("snode_idx",
+ new json::integer_number (get_supernode ()->m_index));
+
+ switch (get_kind ())
+ {
+ default: break;
+ case PK_BEFORE_SUPERNODE:
+ if (const superedge *sedge = get_from_edge ())
+ point_obj->set ("from_edge_snode_idx",
+ new json::integer_number (sedge->m_src->m_index));
+ break;
+ case PK_BEFORE_STMT:
+ point_obj->set ("stmt_idx", new json::integer_number (get_stmt_idx ()));
+ break;
+ }
+
+ point_obj->set ("call_string", m_call_string.to_json ());
+
+ return point_obj;
+}
+
/* Generate a hash value for this program_point. */
hashval_t
void print_source_line (pretty_printer *pp) const;
void dump () const;
+ json::object *to_json () const;
+
hashval_t hash () const;
bool operator== (const program_point &other) const
{
#include "diagnostic-core.h"
#include "diagnostic.h"
#include "function.h"
+#include "json.h"
#include "analyzer/analyzer.h"
#include "analyzer/analyzer-logging.h"
#include "analyzer/sm.h"
dump_to_file (stderr);
}
+/* Return a new json::object of the form
+ {"checkers" : array of objects, one for each state_machine}. */
+
+json::object *
+extrinsic_state::to_json () const
+{
+ json::object *ext_state_obj = new json::object ();
+
+ {
+ json::array *checkers_arr = new json::array ();
+ unsigned i;
+ state_machine *sm;
+ FOR_EACH_VEC_ELT (m_checkers, i, sm)
+ checkers_arr->append (sm->to_json ());
+ ext_state_obj->set ("checkers", checkers_arr);
+ }
+
+ return ext_state_obj;
+}
+
/* Get the region_model_manager for this extrinsic_state. */
region_model_manager *
pp_flush (&pp);
}
+/* Return a new json::object of the form
+ {"global" : (optional) value for global state,
+ SVAL_DESC : value for state}. */
+
+json::object *
+sm_state_map::to_json () const
+{
+ json::object *map_obj = new json::object ();
+
+ if (m_global_state != m_sm.get_start_state ())
+ map_obj->set ("global", m_global_state->to_json ());
+ for (map_t::iterator iter = m_map.begin ();
+ iter != m_map.end ();
+ ++iter)
+ {
+ const svalue *sval = (*iter).first;
+ entry_t e = (*iter).second;
+
+ label_text sval_desc = sval->get_desc ();
+ map_obj->set (sval_desc.m_buffer, e.m_state->to_json ());
+ sval_desc.maybe_free ();
+
+ /* This doesn't yet JSONify e.m_origin. */
+ }
+ return map_obj;
+}
+
/* Return true if no states have been set within this map
(all expressions are for the start state). */
dump_to_file (ext_state, summarize, true, stderr);
}
+/* Return a new json::object of the form
+ {"store" : object for store,
+ "constraints" : object for constraint_manager,
+ "curr_frame" : (optional) str for current frame,
+ "checkers" : { STATE_NAME : object per sm_state_map },
+ "valid" : true/false}. */
+
+json::object *
+program_state::to_json (const extrinsic_state &ext_state) const
+{
+ json::object *state_obj = new json::object ();
+
+ state_obj->set ("store", m_region_model->get_store ()->to_json ());
+ state_obj->set ("constraints",
+ m_region_model->get_constraints ()->to_json ());
+ if (m_region_model->get_current_frame ())
+ state_obj->set ("curr_frame",
+ m_region_model->get_current_frame ()->to_json ());
+
+ /* Provide m_checker_states as an object, using names as keys. */
+ {
+ json::object *checkers_obj = new json::object ();
+
+ int i;
+ sm_state_map *smap;
+ FOR_EACH_VEC_ELT (m_checker_states, i, smap)
+ if (!smap->is_empty_p ())
+ checkers_obj->set (ext_state.get_name (i), smap->to_json ());
+
+ state_obj->set ("checkers", checkers_obj);
+ }
+
+ state_obj->set ("valid", new json::literal (m_valid));
+
+ return state_obj;
+}
+
/* Update this program_state to reflect a top-level call to FUN.
The params will have initial_svalues. */
void dump_to_file (FILE *outf) const;
void dump () const;
+ json::object *to_json () const;
+
engine *get_engine () const { return m_engine; }
region_model_manager *get_model_manager () const;
pretty_printer *pp) const;
void dump (bool simple) const;
+ json::object *to_json () const;
+
bool is_empty_p () const;
hashval_t hash () const;
bool multiline, FILE *outf) const;
void dump (const extrinsic_state &ext_state, bool simple) const;
+ json::object *to_json (const extrinsic_state &ext_state) const;
+
void push_frame (const extrinsic_state &ext_state, function *fun);
function * get_current_function () const;
#include "bitmap.h"
#include "selftest.h"
#include "function.h"
+#include "json.h"
#include "analyzer/analyzer.h"
#include "analyzer/analyzer-logging.h"
#include "ordered-hash-map.h"
#include "bitmap.h"
#include "selftest.h"
#include "function.h"
+#include "json.h"
#include "analyzer/analyzer.h"
#include "analyzer/analyzer-logging.h"
#include "ordered-hash-map.h"
#include "cgraph.h"
#include "cfg.h"
#include "digraph.h"
+#include "json.h"
#include "analyzer/call-string.h"
#include "analyzer/program-point.h"
#include "analyzer/store.h"
#include "bitmap.h"
#include "selftest.h"
#include "function.h"
+#include "json.h"
#include "analyzer/analyzer.h"
#include "analyzer/analyzer-logging.h"
#include "ordered-hash-map.h"
void dump (bool simple=true) const;
label_text get_desc (bool simple=true) const;
+ json::value *to_json () const;
+
virtual const region_svalue *
dyn_cast_region_svalue () const { return NULL; }
virtual const constant_svalue *
virtual void dump_to_pp (pretty_printer *pp, bool simple) const = 0;
void dump (bool simple) const;
+ json::value *to_json () const;
+
bool non_null_p () const;
static int cmp_ptrs (const void *, const void *);
#include "bitmap.h"
#include "selftest.h"
#include "function.h"
+#include "json.h"
#include "analyzer/analyzer.h"
#include "analyzer/analyzer-logging.h"
#include "ordered-hash-map.h"
pp_flush (&pp);
}
+/* Return a new json::string describing the region. */
+
+json::value *
+region::to_json () const
+{
+ label_text desc = get_desc (true);
+ json::value *reg_js = new json::string (desc.m_buffer);
+ desc.maybe_free ();
+ return reg_js;
+}
+
/* Generate a description of this region. */
DEBUG_FUNCTION label_text
#include "diagnostic-path.h"
#include "diagnostic-metadata.h"
#include "function.h"
+#include "json.h"
#include "analyzer/analyzer.h"
#include "diagnostic-event-id.h"
#include "analyzer/analyzer-logging.h"
#include "diagnostic-path.h"
#include "diagnostic-metadata.h"
#include "function.h"
+#include "json.h"
#include "analyzer/analyzer.h"
#include "diagnostic-event-id.h"
#include "analyzer/analyzer-logging.h"
#include "diagnostic-path.h"
#include "diagnostic-metadata.h"
#include "function.h"
+#include "json.h"
#include "analyzer/analyzer.h"
#include "diagnostic-event-id.h"
#include "analyzer/analyzer-logging.h"
#include "diagnostic-path.h"
#include "diagnostic-metadata.h"
#include "function.h"
+#include "json.h"
#include "analyzer/analyzer.h"
#include "diagnostic-event-id.h"
#include "analyzer/analyzer-logging.h"
#include "diagnostic-path.h"
#include "diagnostic-metadata.h"
#include "function.h"
+#include "json.h"
#include "analyzer/analyzer.h"
#include "diagnostic-event-id.h"
#include "analyzer/analyzer-logging.h"
pp_string (pp, "signal delivered");
}
+ json::object *to_json () const
+ {
+ json::object *custom_obj = new json::object ();
+ return custom_obj;
+ }
+
void update_model (region_model *model,
const exploded_edge &eedge) FINAL OVERRIDE
{
#include "diagnostic-path.h"
#include "diagnostic-metadata.h"
#include "function.h"
+#include "json.h"
#include "analyzer/analyzer.h"
#include "diagnostic-event-id.h"
#include "analyzer/analyzer-logging.h"
#include "function.h"
#include "diagnostic-core.h"
#include "pretty-print.h"
+#include "diagnostic.h"
+#include "tree-diagnostic.h"
+#include "json.h"
#include "analyzer/analyzer.h"
#include "analyzer/analyzer-logging.h"
#include "analyzer/sm.h"
pp_string (pp, m_name);
}
+/* Return a new json::string describing the state. */
+
+json::value *
+state_machine::state::to_json () const
+{
+ pretty_printer pp;
+ pp_format_decoder (&pp) = default_tree_printer;
+ dump_to_pp (&pp);
+ return new json::string (pp_formatted_text (&pp));
+}
+
/* class state_machine. */
/* state_machine's ctor. */
}
}
+/* Return a new json::object of the form
+ {"name" : str,
+ "states" : [str]}. */
+
+json::object *
+state_machine::to_json () const
+{
+ json::object *sm_obj = new json::object ();
+
+ sm_obj->set ("name", new json::string (m_name));
+ {
+ json::array *states_arr = new json::array ();
+ unsigned i;
+ state *s;
+ FOR_EACH_VEC_ELT (m_states, i, s)
+ states_arr->append (s->to_json ());
+ sm_obj->set ("states", states_arr);
+ }
+
+ return sm_obj;
+}
+
/* Create instances of the various state machines, each using LOGGER,
and populate OUT with them. */
const char *get_name () const { return m_name; }
virtual void dump_to_pp (pretty_printer *pp) const;
+ virtual json::value *to_json () const;
unsigned get_id () const { return m_id; }
void dump_to_pp (pretty_printer *pp) const;
+ json::object *to_json () const;
+
state_t get_start_state () const { return m_start; }
protected:
#include "diagnostic-core.h"
#include "gimple-pretty-print.h"
#include "function.h"
+#include "json.h"
#include "analyzer/analyzer.h"
#include "analyzer/call-string.h"
#include "digraph.h"
#include "bitmap.h"
#include "selftest.h"
#include "function.h"
+#include "json.h"
#include "analyzer/analyzer.h"
#include "analyzer/analyzer-logging.h"
#include "ordered-hash-map.h"
pp_flush (&pp);
}
+/* Get a description of this binding_key. */
+
+label_text
+binding_key::get_desc (bool simple) const
+{
+ pretty_printer pp;
+ pp_format_decoder (&pp) = default_tree_printer;
+ dump_to_pp (&pp, simple);
+ return label_text::take (xstrdup (pp_formatted_text (&pp)));
+}
+
/* qsort callback. */
int
pp_flush (&pp);
}
+/* Return a new json::object of the form
+ {KEY_DESC : SVALUE_DESC,
+ ...for the various key/value pairs in this binding_map}. */
+
+json::object *
+binding_map::to_json () const
+{
+ json::object *map_obj = new json::object ();
+
+ auto_vec <const binding_key *> binding_keys;
+ for (map_t::iterator iter = m_map.begin ();
+ iter != m_map.end (); ++iter)
+ {
+ const binding_key *key = (*iter).first;
+ binding_keys.safe_push (key);
+ }
+ binding_keys.qsort (binding_key::cmp_ptrs);
+
+ const binding_key *key;
+ unsigned i;
+ FOR_EACH_VEC_ELT (binding_keys, i, key)
+ {
+ const svalue *value = *const_cast <map_t &> (m_map).get (key);
+ label_text key_desc = key->get_desc ();
+ map_obj->set (key_desc.m_buffer, value->to_json ());
+ key_desc.maybe_free ();
+ }
+
+ return map_obj;
+}
+
/* Get the child region of PARENT_REG based upon INDEX within a
CONSTRUCTOR. */
pp_flush (&pp);
}
+/* Return a new json::object of the form
+ {"escaped": true/false,
+ "touched": true/false,
+ "map" : object for the the binding_map. */
+
+json::object *
+binding_cluster::to_json () const
+{
+ json::object *cluster_obj = new json::object ();
+
+ cluster_obj->set ("escaped", new json::literal (m_escaped));
+ cluster_obj->set ("touched", new json::literal (m_touched));
+ cluster_obj->set ("map", m_map.to_json ());
+
+ return cluster_obj;
+}
+
/* Add a binding of SVAL of kind KIND to REG, unpacking SVAL if it is a
compound_sval. */
pp_flush (&pp);
}
+/* Return a new json::object of the form
+ {PARENT_REGION_DESC: {BASE_REGION_DESC: object for binding_map,
+ ... for each cluster within parent region},
+ ...for each parent region,
+ "called_unknown_function": true/false}. */
+
+json::object *
+store::to_json () const
+{
+ json::object *store_obj = new json::object ();
+
+ /* Sort into some deterministic order. */
+ auto_vec<const region *> base_regions;
+ for (cluster_map_t::iterator iter = m_cluster_map.begin ();
+ iter != m_cluster_map.end (); ++iter)
+ {
+ const region *base_reg = (*iter).first;
+ base_regions.safe_push (base_reg);
+ }
+ base_regions.qsort (region::cmp_ptrs);
+
+ /* Gather clusters, organize by parent region, so that we can group
+ together locals, globals, etc. */
+ auto_vec<const region *> parent_regions;
+ get_sorted_parent_regions (&parent_regions, base_regions);
+
+ const region *parent_reg;
+ unsigned i;
+ FOR_EACH_VEC_ELT (parent_regions, i, parent_reg)
+ {
+ gcc_assert (parent_reg);
+
+ json::object *clusters_in_parent_reg_obj = new json::object ();
+
+ const region *base_reg;
+ unsigned j;
+ FOR_EACH_VEC_ELT (base_regions, j, base_reg)
+ {
+ /* This is O(N * M), but N ought to be small. */
+ if (base_reg->get_parent_region () != parent_reg)
+ continue;
+ binding_cluster *cluster
+ = *const_cast<cluster_map_t &> (m_cluster_map).get (base_reg);
+ label_text base_reg_desc = base_reg->get_desc ();
+ clusters_in_parent_reg_obj->set (base_reg_desc.m_buffer,
+ cluster->to_json ());
+ base_reg_desc.maybe_free ();
+ }
+ label_text parent_reg_desc = parent_reg->get_desc ();
+ store_obj->set (parent_reg_desc.m_buffer, clusters_in_parent_reg_obj);
+ parent_reg_desc.maybe_free ();
+ }
+
+ store_obj->set ("called_unknown_fn", new json::literal (m_called_unknown_fn));
+
+ return store_obj;
+}
+
/* Get any svalue bound to REG, or NULL. */
const svalue *
virtual void dump_to_pp (pretty_printer *pp, bool simple) const;
void dump (bool simple) const;
+ label_text get_desc (bool simple=true) const;
static int cmp_ptrs (const void *, const void *);
static int cmp (const binding_key *, const binding_key *);
void dump_to_pp (pretty_printer *pp, bool simple, bool multiline) const;
void dump (bool simple) const;
+ json::object *to_json () const;
+
bool apply_ctor_to_region (const region *parent_reg, tree ctor,
region_model_manager *mgr);
void dump_to_pp (pretty_printer *pp, bool simple, bool multiline) const;
void dump (bool simple) const;
+ json::object *to_json () const;
+
void bind (store_manager *mgr, const region *, const svalue *,
binding_kind kind);
void dump (bool simple) const;
void summarize_to_pp (pretty_printer *pp, bool simple) const;
+ json::object *to_json () const;
+
const svalue *get_direct_binding (store_manager *mgr, const region *reg);
const svalue *get_default_binding (store_manager *mgr, const region *reg);
const svalue *get_any_binding (store_manager *mgr, const region *reg) const;
#include "tree-dfa.h"
#include "cfganal.h"
#include "function.h"
+#include "json.h"
#include "analyzer/analyzer.h"
#include "ordered-hash-map.h"
#include "options.h"
fclose (fp);
}
+/* Return a new json::object of the form
+ {"nodes" : [objs for snodes],
+ "edges" : [objs for sedges]}. */
+
+json::object *
+supergraph::to_json () const
+{
+ json::object *sgraph_obj = new json::object ();
+
+ /* Nodes. */
+ {
+ json::array *nodes_arr = new json::array ();
+ unsigned i;
+ supernode *n;
+ FOR_EACH_VEC_ELT (m_nodes, i, n)
+ nodes_arr->append (n->to_json ());
+ sgraph_obj->set ("nodes", nodes_arr);
+ }
+
+ /* Edges. */
+ {
+ json::array *edges_arr = new json::array ();
+ unsigned i;
+ superedge *n;
+ FOR_EACH_VEC_ELT (m_edges, i, n)
+ edges_arr->append (n->to_json ());
+ sgraph_obj->set ("edges", edges_arr);
+ }
+
+ return sgraph_obj;
+}
+
/* Create a supernode for BB within FUN and add it to this supergraph.
If RETURNING_CALL is non-NULL, the supernode represents the resumption
pp_printf (pp, "node_%i", m_index);
}
+/* Return a new json::object of the form
+ {"idx": int,
+ "bb_idx": int,
+ "m_returning_call": optional str,
+ "phis": [str],
+ "stmts" : [str]}. */
+
+json::object *
+supernode::to_json () const
+{
+ json::object *snode_obj = new json::object ();
+
+ snode_obj->set ("idx", new json::integer_number (m_index));
+ snode_obj->set ("bb_idx", new json::integer_number (m_bb->index));
+
+ if (m_returning_call)
+ {
+ pretty_printer pp;
+ pp_format_decoder (&pp) = default_tree_printer;
+ pp_gimple_stmt_1 (&pp, m_returning_call, 0, (dump_flags_t)0);
+ snode_obj->set ("returning_call",
+ new json::string (pp_formatted_text (&pp)));
+ }
+
+ /* Phi nodes. */
+ {
+ json::array *phi_arr = new json::array ();
+ for (gphi_iterator gpi = const_cast<supernode *> (this)->start_phis ();
+ !gsi_end_p (gpi); gsi_next (&gpi))
+ {
+ const gimple *stmt = gsi_stmt (gpi);
+ pretty_printer pp;
+ pp_format_decoder (&pp) = default_tree_printer;
+ pp_gimple_stmt_1 (&pp, stmt, 0, (dump_flags_t)0);
+ phi_arr->append (new json::string (pp_formatted_text (&pp)));
+ }
+ snode_obj->set ("phis", phi_arr);
+ }
+
+ /* Statements. */
+ {
+ json::array *stmt_arr = new json::array ();
+ int i;
+ gimple *stmt;
+ FOR_EACH_VEC_ELT (m_stmts, i, stmt)
+ {
+ pretty_printer pp;
+ pp_format_decoder (&pp) = default_tree_printer;
+ pp_gimple_stmt_1 (&pp, stmt, 0, (dump_flags_t)0);
+ stmt_arr->append (new json::string (pp_formatted_text (&pp)));
+ }
+ snode_obj->set ("stmts", stmt_arr);
+ }
+
+ return snode_obj;
+}
+
/* Get a location_t for the start of this supernode. */
location_t
pp_printf (pp, "\"];\n");
}
+/* Return a new json::object of the form
+ {"src_idx": int, the index of the source supernode,
+ "dst_idx": int, the index of the destination supernode,
+ "desc" : str. */
+
+json::object *
+superedge::to_json () const
+{
+ json::object *sedge_obj = new json::object ();
+ sedge_obj->set ("src_idx", new json::integer_number (m_src->m_index));
+ sedge_obj->set ("dst_idx", new json::integer_number (m_dest->m_index));
+
+ {
+ pretty_printer pp;
+ pp_format_decoder (&pp) = default_tree_printer;
+ dump_label_to_pp (&pp, false);
+ sedge_obj->set ("desc", new json::string (pp_formatted_text (&pp)));
+ }
+
+ return sedge_obj;
+}
+
/* If this is an intraprocedural superedge, return the associated
CFG edge. Otherwise, return NULL. */
void dump_dot_to_file (FILE *fp, const dump_args_t &) const;
void dump_dot (const char *path, const dump_args_t &) const;
+ json::object *to_json () const;
+
int num_nodes () const { return m_nodes.length (); }
int num_edges () const { return m_edges.length (); }
void dump_dot (graphviz_out *gv, const dump_args_t &args) const OVERRIDE;
void dump_dot_id (pretty_printer *pp) const;
+ json::object *to_json () const;
+
location_t get_start_location () const;
location_t get_end_location () const;
virtual void dump_label_to_pp (pretty_printer *pp,
bool user_facing) const = 0;
+ json::object *to_json () const;
+
enum edge_kind get_kind () const { return m_kind; }
virtual cfg_superedge *dyn_cast_cfg_superedge () { return NULL; }
#include "bitmap.h"
#include "selftest.h"
#include "function.h"
+#include "json.h"
#include "analyzer/analyzer.h"
#include "analyzer/analyzer-logging.h"
#include "options.h"
return label_text::take (xstrdup (pp_formatted_text (&pp)));
}
+/* Return a new json::string describing the svalue. */
+
+json::value *
+svalue::to_json () const
+{
+ label_text desc = get_desc (true);
+ json::value *sval_js = new json::string (desc.m_buffer);
+ desc.maybe_free ();
+ return sval_js;
+}
+
/* If this svalue is a constant_svalue, return the underlying tree constant.
Otherwise return NULL_TREE. */
@subsection Other Debugging Techniques
+The option @option{-fdump-analyzer-json} will dump both the supergraph
+and the exploded graph in compressed JSON form.
+
One approach when tracking down where a particular bogus state is
introduced into the @code{exploded_graph} is to add custom code to
@code{program_state::validate}.
-fdump-analyzer-exploded-nodes @gol
-fdump-analyzer-exploded-nodes-2 @gol
-fdump-analyzer-exploded-nodes-3 @gol
+-fdump-analyzer-json @gol
-fdump-analyzer-state-purge @gol
-fdump-analyzer-supergraph @gol
-Wno-analyzer-double-fclose @gol
one dump file per node, to @file{@var{file}.eg-@var{id}.txt}.
This is typically a large number of dump files.
+@item -fdump-analyzer-json
+@opindex fdump-analyzer-json
+Dump a compressed JSON representation of analyzer internals to
+@file{@var{file}.analyzer.json.gz}. The precise format is subject
+to change.
+
@item -fdump-analyzer-state-purge
@opindex fdump-analyzer-state-purge
As per @option{-fdump-analyzer-supergraph}, dump a representation of the