Message ID | 20200922184937.20248-1-dmalcolm@redhat.com |
---|---|
State | New |
Headers | show |
Series | [committed] analyzer: add -fdump-analyzer-json | expand |
This patch breaks the cross build here: ...gcc/analyzer/engine.cc:65:10: fatal error: zlib.h: No such file or directory I think you need to do something similar in Makefile.in as lto-compress has: # lto-compress.o needs $(ZLIBINC) added to the include flags. CFLAGS-lto-compress.o += $(ZLIBINC) $(ZSTD_INC) Tobias On 9/22/20 8:49 PM, David Malcolm via Gcc-patches wrote: > I've found this useful for debugging state explosions in the analyzer. > > Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu. > Pushed to master as 809192e77e6e112a0fe32dee7fada7a49fbf25cd. > > 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. > --- > gcc/analyzer/analysis-plan.cc | 1 + > gcc/analyzer/analyzer.opt | 4 + > gcc/analyzer/call-string.cc | 29 +++++ > gcc/analyzer/call-string.h | 2 + > gcc/analyzer/checker-path.cc | 1 + > gcc/analyzer/constraint-manager.cc | 77 ++++++++++++ > gcc/analyzer/constraint-manager.h | 6 + > gcc/analyzer/diagnostic-manager.cc | 58 +++++++++ > gcc/analyzer/diagnostic-manager.h | 4 + > gcc/analyzer/engine.cc | 146 ++++++++++++++++++++++ > gcc/analyzer/exploded-graph.h | 7 ++ > gcc/analyzer/pending-diagnostic.cc | 1 + > gcc/analyzer/program-point.cc | 38 ++++++ > gcc/analyzer/program-point.h | 2 + > gcc/analyzer/program-state.cc | 85 +++++++++++++ > gcc/analyzer/program-state.h | 6 + > gcc/analyzer/region-model-impl-calls.cc | 1 + > gcc/analyzer/region-model-manager.cc | 1 + > gcc/analyzer/region-model-reachability.cc | 1 + > gcc/analyzer/region-model.cc | 1 + > gcc/analyzer/region-model.h | 4 + > gcc/analyzer/region.cc | 12 ++ > gcc/analyzer/sm-file.cc | 1 + > gcc/analyzer/sm-malloc.cc | 1 + > gcc/analyzer/sm-pattern-test.cc | 1 + > gcc/analyzer/sm-sensitive.cc | 1 + > gcc/analyzer/sm-signal.cc | 7 ++ > gcc/analyzer/sm-taint.cc | 1 + > gcc/analyzer/sm.cc | 36 ++++++ > gcc/analyzer/sm.h | 3 + > gcc/analyzer/state-purge.cc | 1 + > gcc/analyzer/store.cc | 118 +++++++++++++++++ > gcc/analyzer/store.h | 7 ++ > gcc/analyzer/supergraph.cc | 112 +++++++++++++++++ > gcc/analyzer/supergraph.h | 6 + > gcc/analyzer/svalue.cc | 12 ++ > gcc/doc/analyzer.texi | 3 + > gcc/doc/invoke.texi | 7 ++ > 38 files changed, 804 insertions(+) > > diff --git a/gcc/analyzer/analysis-plan.cc b/gcc/analyzer/analysis-plan.cc > index 3c8b10b3314..7e48f526094 100644 > --- a/gcc/analyzer/analysis-plan.cc > +++ b/gcc/analyzer/analysis-plan.cc > @@ -27,6 +27,7 @@ along with GCC; see the file COPYING3. If not see > #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" > diff --git a/gcc/analyzer/analyzer.opt b/gcc/analyzer/analyzer.opt > index 94a686db6b3..872fb31048e 100644 > --- a/gcc/analyzer/analyzer.opt > +++ b/gcc/analyzer/analyzer.opt > @@ -186,6 +186,10 @@ fdump-analyzer-exploded-nodes-3 > 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. > diff --git a/gcc/analyzer/call-string.cc b/gcc/analyzer/call-string.cc > index d3630316c4e..72568c6efa7 100644 > --- a/gcc/analyzer/call-string.cc > +++ b/gcc/analyzer/call-string.cc > @@ -24,6 +24,7 @@ along with GCC; see the file COPYING3. If not see > #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" > @@ -104,6 +105,34 @@ call_string::print (pretty_printer *pp) const > 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 > diff --git a/gcc/analyzer/call-string.h b/gcc/analyzer/call-string.h > index 1b5db0a4a20..5a03c592ecb 100644 > --- a/gcc/analyzer/call-string.h > +++ b/gcc/analyzer/call-string.h > @@ -47,6 +47,8 @@ public: > > 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 (); } > diff --git a/gcc/analyzer/checker-path.cc b/gcc/analyzer/checker-path.cc > index c28131651c6..1f6d6a86001 100644 > --- a/gcc/analyzer/checker-path.cc > +++ b/gcc/analyzer/checker-path.cc > @@ -38,6 +38,7 @@ along with GCC; see the file COPYING3. If not see > #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" > diff --git a/gcc/analyzer/constraint-manager.cc b/gcc/analyzer/constraint-manager.cc > index 521501fd4f4..5cd2c9e0e0b 100644 > --- a/gcc/analyzer/constraint-manager.cc > +++ b/gcc/analyzer/constraint-manager.cc > @@ -31,6 +31,7 @@ along with GCC; see the file COPYING3. If not see > #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" > @@ -299,6 +300,33 @@ equiv_class::print (pretty_printer *pp) const > 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. */ > @@ -499,6 +527,23 @@ constraint::print (pretty_printer *pp, const constraint_manager &cm) const > 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 > @@ -768,6 +813,38 @@ debug (const constraint_manager &cm) > 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. */ > diff --git a/gcc/analyzer/constraint-manager.h b/gcc/analyzer/constraint-manager.h > index 3c31a891f70..98960ffad84 100644 > --- a/gcc/analyzer/constraint-manager.h > +++ b/gcc/analyzer/constraint-manager.h > @@ -88,6 +88,8 @@ public: > > 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. */ > @@ -160,6 +162,8 @@ class constraint > > 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; > > @@ -215,6 +219,8 @@ public: > 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]; > diff --git a/gcc/analyzer/diagnostic-manager.cc b/gcc/analyzer/diagnostic-manager.cc > index 4a95d4c569e..8d7e5084cc0 100644 > --- a/gcc/analyzer/diagnostic-manager.cc > +++ b/gcc/analyzer/diagnostic-manager.cc > @@ -37,6 +37,7 @@ along with GCC; see the file COPYING3. If not see > #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" > @@ -114,6 +115,43 @@ saved_diagnostic::operator== (const saved_diagnostic &other) const > && 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. */ > @@ -199,6 +237,26 @@ diagnostic_manager::add_diagnostic (const exploded_node *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 > diff --git a/gcc/analyzer/diagnostic-manager.h b/gcc/analyzer/diagnostic-manager.h > index 1e310f7fe15..c32f0c47dc1 100644 > --- a/gcc/analyzer/diagnostic-manager.h > +++ b/gcc/analyzer/diagnostic-manager.h > @@ -46,6 +46,8 @@ public: > > bool operator== (const saved_diagnostic &other) const; > > + json::object *to_json () const; > + > void set_feasible () > { > gcc_assert (m_status == STATUS_NEW); > @@ -105,6 +107,8 @@ public: > > 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, > diff --git a/gcc/analyzer/engine.cc b/gcc/analyzer/engine.cc > index d03e23a9b6e..df7e33564f1 100644 > --- a/gcc/analyzer/engine.cc > +++ b/gcc/analyzer/engine.cc > @@ -37,6 +37,7 @@ along with GCC; see the file COPYING3. If not see > #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" > @@ -61,6 +62,7 @@ along with GCC; see the file COPYING3. If not see > #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. */ > > @@ -764,6 +766,19 @@ eg_traits::dump_args_t::show_enode_details_p (const exploded_node &enode) const > > /* 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, > @@ -952,6 +967,28 @@ exploded_node::dump (const extrinsic_state &ext_state) const > 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. */ > @@ -1502,6 +1539,30 @@ exploded_edge::dump_dot (graphviz_out *gv, const dump_args_t &) const > 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. */ > @@ -3057,6 +3118,55 @@ exploded_graph::dump_states_for_supernode (FILE *out, > 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. */ > @@ -4241,6 +4351,39 @@ private: > 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 > @@ -4341,6 +4484,9 @@ impl_run_checkers (logger *logger) > free (filename); > } > > + if (flag_dump_analyzer_json) > + dump_analyzer_json (sg, eg); > + > delete purge_map; > } > > diff --git a/gcc/analyzer/exploded-graph.h b/gcc/analyzer/exploded-graph.h > index 04e878fbdfc..f723d52bdf8 100644 > --- a/gcc/analyzer/exploded-graph.h > +++ b/gcc/analyzer/exploded-graph.h > @@ -165,6 +165,7 @@ class exploded_node : public dnode<eg_traits> > /* 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); > > @@ -179,6 +180,8 @@ class exploded_node : public dnode<eg_traits> > 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 > { > @@ -307,6 +310,8 @@ class exploded_edge : public dedge<eg_traits> > 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; > > @@ -782,6 +787,8 @@ public: > 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 > diff --git a/gcc/analyzer/pending-diagnostic.cc b/gcc/analyzer/pending-diagnostic.cc > index c196903afe0..502d17719b0 100644 > --- a/gcc/analyzer/pending-diagnostic.cc > +++ b/gcc/analyzer/pending-diagnostic.cc > @@ -25,6 +25,7 @@ along with GCC; see the file COPYING3. If not see > #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" > diff --git a/gcc/analyzer/program-point.cc b/gcc/analyzer/program-point.cc > index ef19e6e38e5..429d6ece724 100644 > --- a/gcc/analyzer/program-point.cc > +++ b/gcc/analyzer/program-point.cc > @@ -24,6 +24,7 @@ along with GCC; see the file COPYING3. If not see > #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" > @@ -281,6 +282,43 @@ program_point::dump () const > 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 > diff --git a/gcc/analyzer/program-point.h b/gcc/analyzer/program-point.h > index 97fd0a5e9de..d804621a715 100644 > --- a/gcc/analyzer/program-point.h > +++ b/gcc/analyzer/program-point.h > @@ -175,6 +175,8 @@ public: > 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 > { > diff --git a/gcc/analyzer/program-state.cc b/gcc/analyzer/program-state.cc > index 71bb2864d6d..83a6e5b081e 100644 > --- a/gcc/analyzer/program-state.cc > +++ b/gcc/analyzer/program-state.cc > @@ -25,6 +25,7 @@ along with GCC; see the file COPYING3. If not see > #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" > @@ -99,6 +100,26 @@ extrinsic_state::dump () const > 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 * > @@ -208,6 +229,33 @@ sm_state_map::dump (bool simple) const > 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). */ > > @@ -733,6 +781,43 @@ program_state::dump (const extrinsic_state &ext_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. */ > > diff --git a/gcc/analyzer/program-state.h b/gcc/analyzer/program-state.h > index cb0df8cd904..a52fbeb2e3e 100644 > --- a/gcc/analyzer/program-state.h > +++ b/gcc/analyzer/program-state.h > @@ -53,6 +53,8 @@ public: > 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; > > @@ -109,6 +111,8 @@ public: > pretty_printer *pp) const; > void dump (bool simple) const; > > + json::object *to_json () const; > + > bool is_empty_p () const; > > hashval_t hash () const; > @@ -204,6 +208,8 @@ public: > 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; > > diff --git a/gcc/analyzer/region-model-impl-calls.cc b/gcc/analyzer/region-model-impl-calls.cc > index 423f74a4152..009b8c3ecb0 100644 > --- a/gcc/analyzer/region-model-impl-calls.cc > +++ b/gcc/analyzer/region-model-impl-calls.cc > @@ -42,6 +42,7 @@ along with GCC; see the file COPYING3. If not see > #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" > diff --git a/gcc/analyzer/region-model-manager.cc b/gcc/analyzer/region-model-manager.cc > index da8fa01077b..8dd3ad0020a 100644 > --- a/gcc/analyzer/region-model-manager.cc > +++ b/gcc/analyzer/region-model-manager.cc > @@ -42,6 +42,7 @@ along with GCC; see the file COPYING3. If not see > #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" > diff --git a/gcc/analyzer/region-model-reachability.cc b/gcc/analyzer/region-model-reachability.cc > index 681b8f74b19..c1b3b2db630 100644 > --- a/gcc/analyzer/region-model-reachability.cc > +++ b/gcc/analyzer/region-model-reachability.cc > @@ -47,6 +47,7 @@ along with GCC; see the file COPYING3. If not see > #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" > diff --git a/gcc/analyzer/region-model.cc b/gcc/analyzer/region-model.cc > index 6f04904a74e..74a96b025a4 100644 > --- a/gcc/analyzer/region-model.cc > +++ b/gcc/analyzer/region-model.cc > @@ -42,6 +42,7 @@ along with GCC; see the file COPYING3. If not see > #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" > diff --git a/gcc/analyzer/region-model.h b/gcc/analyzer/region-model.h > index 4859df369cf..1e8a517dd8c 100644 > --- a/gcc/analyzer/region-model.h > +++ b/gcc/analyzer/region-model.h > @@ -256,6 +256,8 @@ public: > 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 * > @@ -1400,6 +1402,8 @@ public: > 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 *); > diff --git a/gcc/analyzer/region.cc b/gcc/analyzer/region.cc > index 53f32dc912c..0820893a9b4 100644 > --- a/gcc/analyzer/region.cc > +++ b/gcc/analyzer/region.cc > @@ -44,6 +44,7 @@ along with GCC; see the file COPYING3. If not see > #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" > @@ -460,6 +461,17 @@ region::dump (bool simple) const > 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 > diff --git a/gcc/analyzer/sm-file.cc b/gcc/analyzer/sm-file.cc > index 58a0fd461fa..d2010710529 100644 > --- a/gcc/analyzer/sm-file.cc > +++ b/gcc/analyzer/sm-file.cc > @@ -29,6 +29,7 @@ along with GCC; see the file COPYING3. If not see > #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" > diff --git a/gcc/analyzer/sm-malloc.cc b/gcc/analyzer/sm-malloc.cc > index 12b2383e4a7..6293d7885cd 100644 > --- a/gcc/analyzer/sm-malloc.cc > +++ b/gcc/analyzer/sm-malloc.cc > @@ -30,6 +30,7 @@ along with GCC; see the file COPYING3. If not see > #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" > diff --git a/gcc/analyzer/sm-pattern-test.cc b/gcc/analyzer/sm-pattern-test.cc > index bb6d3b1e719..c430476a846 100644 > --- a/gcc/analyzer/sm-pattern-test.cc > +++ b/gcc/analyzer/sm-pattern-test.cc > @@ -31,6 +31,7 @@ along with GCC; see the file COPYING3. If not see > #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" > diff --git a/gcc/analyzer/sm-sensitive.cc b/gcc/analyzer/sm-sensitive.cc > index 49f9eb387b1..aec0a6ab8da 100644 > --- a/gcc/analyzer/sm-sensitive.cc > +++ b/gcc/analyzer/sm-sensitive.cc > @@ -31,6 +31,7 @@ along with GCC; see the file COPYING3. If not see > #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" > diff --git a/gcc/analyzer/sm-signal.cc b/gcc/analyzer/sm-signal.cc > index bf6ea480423..2e05de8a2de 100644 > --- a/gcc/analyzer/sm-signal.cc > +++ b/gcc/analyzer/sm-signal.cc > @@ -32,6 +32,7 @@ along with GCC; see the file COPYING3. If not see > #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" > @@ -220,6 +221,12 @@ public: > 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 > { > diff --git a/gcc/analyzer/sm-taint.cc b/gcc/analyzer/sm-taint.cc > index 49bbd6dfb13..37491d8a49a 100644 > --- a/gcc/analyzer/sm-taint.cc > +++ b/gcc/analyzer/sm-taint.cc > @@ -31,6 +31,7 @@ along with GCC; see the file COPYING3. If not see > #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" > diff --git a/gcc/analyzer/sm.cc b/gcc/analyzer/sm.cc > index a333063c65e..3fe75ef8c4a 100644 > --- a/gcc/analyzer/sm.cc > +++ b/gcc/analyzer/sm.cc > @@ -29,6 +29,9 @@ along with GCC; see the file COPYING3. If not see > #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" > @@ -56,6 +59,17 @@ state_machine::state::dump_to_pp (pretty_printer *pp) const > 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. */ > @@ -109,6 +123,28 @@ state_machine::dump_to_pp (pretty_printer *pp) const > } > } > > +/* 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. */ > > diff --git a/gcc/analyzer/sm.h b/gcc/analyzer/sm.h > index f44ad922200..46b93ffb790 100644 > --- a/gcc/analyzer/sm.h > +++ b/gcc/analyzer/sm.h > @@ -48,6 +48,7 @@ public: > > 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; } > > @@ -121,6 +122,8 @@ public: > > void dump_to_pp (pretty_printer *pp) const; > > + json::object *to_json () const; > + > state_t get_start_state () const { return m_start; } > > protected: > diff --git a/gcc/analyzer/state-purge.cc b/gcc/analyzer/state-purge.cc > index d5a24b48e1e..e4942a692fa 100644 > --- a/gcc/analyzer/state-purge.cc > +++ b/gcc/analyzer/state-purge.cc > @@ -37,6 +37,7 @@ along with GCC; see the file COPYING3. If not see > #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" > diff --git a/gcc/analyzer/store.cc b/gcc/analyzer/store.cc > index 1348895e5c7..11585123561 100644 > --- a/gcc/analyzer/store.cc > +++ b/gcc/analyzer/store.cc > @@ -42,6 +42,7 @@ along with GCC; see the file COPYING3. If not see > #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" > @@ -122,6 +123,17 @@ binding_key::dump (bool simple) const > 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 > @@ -366,6 +378,37 @@ binding_map::dump (bool simple) const > 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. */ > > @@ -657,6 +700,23 @@ binding_cluster::dump (bool simple) const > 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. */ > > @@ -1575,6 +1635,64 @@ store::dump (bool simple) const > 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 * > diff --git a/gcc/analyzer/store.h b/gcc/analyzer/store.h > index 83a43107b21..0f4e7ab2a56 100644 > --- a/gcc/analyzer/store.h > +++ b/gcc/analyzer/store.h > @@ -159,6 +159,7 @@ public: > > 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 *); > @@ -340,6 +341,8 @@ public: > 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); > > @@ -392,6 +395,8 @@ public: > 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); > > @@ -517,6 +522,8 @@ public: > 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; > diff --git a/gcc/analyzer/supergraph.cc b/gcc/analyzer/supergraph.cc > index 7c6fed3a060..735c4a30e09 100644 > --- a/gcc/analyzer/supergraph.cc > +++ b/gcc/analyzer/supergraph.cc > @@ -43,6 +43,7 @@ along with GCC; see the file COPYING3. If not see > #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" > @@ -374,6 +375,38 @@ supergraph::dump_dot (const char *path, const dump_args_t &dump_args) const > 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 > @@ -594,6 +627,63 @@ supernode::dump_dot_id (pretty_printer *pp) const > 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 > @@ -759,6 +849,28 @@ superedge::dump_dot (graphviz_out *gv, const dump_args_t &) const > 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. */ > > diff --git a/gcc/analyzer/supergraph.h b/gcc/analyzer/supergraph.h > index c25043d92fd..40ae9ff8ac2 100644 > --- a/gcc/analyzer/supergraph.h > +++ b/gcc/analyzer/supergraph.h > @@ -148,6 +148,8 @@ public: > 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 (); } > > @@ -231,6 +233,8 @@ class supernode : public dnode<supergraph_traits> > 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; > > @@ -289,6 +293,8 @@ class superedge : public dedge<supergraph_traits> > 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; } > diff --git a/gcc/analyzer/svalue.cc b/gcc/analyzer/svalue.cc > index fcab578674e..ae3b6783e9c 100644 > --- a/gcc/analyzer/svalue.cc > +++ b/gcc/analyzer/svalue.cc > @@ -42,6 +42,7 @@ along with GCC; see the file COPYING3. If not see > #include "bitmap.h" > #include "selftest.h" > #include "function.h" > +#include "json.h" > #include "analyzer/analyzer.h" > #include "analyzer/analyzer-logging.h" > #include "options.h" > @@ -116,6 +117,17 @@ svalue::get_desc (bool simple) const > 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. */ > > diff --git a/gcc/doc/analyzer.texi b/gcc/doc/analyzer.texi > index 92c12e19401..6b7d70cccaa 100644 > --- a/gcc/doc/analyzer.texi > +++ b/gcc/doc/analyzer.texi > @@ -488,6 +488,9 @@ truthfulness of the argument. This is useful for writing DejaGnu tests. > > @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}. > diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi > index 8be2b4f4de6..49b5348c606 100644 > --- a/gcc/doc/invoke.texi > +++ b/gcc/doc/invoke.texi > @@ -406,6 +406,7 @@ Objective-C and Objective-C++ Dialects}. > -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 > @@ -9103,6 +9104,12 @@ Dump a textual representation of the ``exploded graph'' to > 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 ----------------- Mentor Graphics (Deutschland) GmbH, Arnulfstraße 201, 80634 München / Germany Registergericht München HRB 106955, Geschäftsführer: Thomas Heurung, Alexander Walter
diff --git a/gcc/analyzer/analysis-plan.cc b/gcc/analyzer/analysis-plan.cc index 3c8b10b3314..7e48f526094 100644 --- a/gcc/analyzer/analysis-plan.cc +++ b/gcc/analyzer/analysis-plan.cc @@ -27,6 +27,7 @@ along with GCC; see the file COPYING3. If not see #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" diff --git a/gcc/analyzer/analyzer.opt b/gcc/analyzer/analyzer.opt index 94a686db6b3..872fb31048e 100644 --- a/gcc/analyzer/analyzer.opt +++ b/gcc/analyzer/analyzer.opt @@ -186,6 +186,10 @@ fdump-analyzer-exploded-nodes-3 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. diff --git a/gcc/analyzer/call-string.cc b/gcc/analyzer/call-string.cc index d3630316c4e..72568c6efa7 100644 --- a/gcc/analyzer/call-string.cc +++ b/gcc/analyzer/call-string.cc @@ -24,6 +24,7 @@ along with GCC; see the file COPYING3. If not see #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" @@ -104,6 +105,34 @@ call_string::print (pretty_printer *pp) const 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 diff --git a/gcc/analyzer/call-string.h b/gcc/analyzer/call-string.h index 1b5db0a4a20..5a03c592ecb 100644 --- a/gcc/analyzer/call-string.h +++ b/gcc/analyzer/call-string.h @@ -47,6 +47,8 @@ public: 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 (); } diff --git a/gcc/analyzer/checker-path.cc b/gcc/analyzer/checker-path.cc index c28131651c6..1f6d6a86001 100644 --- a/gcc/analyzer/checker-path.cc +++ b/gcc/analyzer/checker-path.cc @@ -38,6 +38,7 @@ along with GCC; see the file COPYING3. If not see #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" diff --git a/gcc/analyzer/constraint-manager.cc b/gcc/analyzer/constraint-manager.cc index 521501fd4f4..5cd2c9e0e0b 100644 --- a/gcc/analyzer/constraint-manager.cc +++ b/gcc/analyzer/constraint-manager.cc @@ -31,6 +31,7 @@ along with GCC; see the file COPYING3. If not see #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" @@ -299,6 +300,33 @@ equiv_class::print (pretty_printer *pp) const 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. */ @@ -499,6 +527,23 @@ constraint::print (pretty_printer *pp, const constraint_manager &cm) const 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 @@ -768,6 +813,38 @@ debug (const constraint_manager &cm) 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. */ diff --git a/gcc/analyzer/constraint-manager.h b/gcc/analyzer/constraint-manager.h index 3c31a891f70..98960ffad84 100644 --- a/gcc/analyzer/constraint-manager.h +++ b/gcc/analyzer/constraint-manager.h @@ -88,6 +88,8 @@ public: 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. */ @@ -160,6 +162,8 @@ class constraint 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; @@ -215,6 +219,8 @@ public: 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]; diff --git a/gcc/analyzer/diagnostic-manager.cc b/gcc/analyzer/diagnostic-manager.cc index 4a95d4c569e..8d7e5084cc0 100644 --- a/gcc/analyzer/diagnostic-manager.cc +++ b/gcc/analyzer/diagnostic-manager.cc @@ -37,6 +37,7 @@ along with GCC; see the file COPYING3. If not see #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" @@ -114,6 +115,43 @@ saved_diagnostic::operator== (const saved_diagnostic &other) const && 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. */ @@ -199,6 +237,26 @@ diagnostic_manager::add_diagnostic (const exploded_node *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 diff --git a/gcc/analyzer/diagnostic-manager.h b/gcc/analyzer/diagnostic-manager.h index 1e310f7fe15..c32f0c47dc1 100644 --- a/gcc/analyzer/diagnostic-manager.h +++ b/gcc/analyzer/diagnostic-manager.h @@ -46,6 +46,8 @@ public: bool operator== (const saved_diagnostic &other) const; + json::object *to_json () const; + void set_feasible () { gcc_assert (m_status == STATUS_NEW); @@ -105,6 +107,8 @@ public: 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, diff --git a/gcc/analyzer/engine.cc b/gcc/analyzer/engine.cc index d03e23a9b6e..df7e33564f1 100644 --- a/gcc/analyzer/engine.cc +++ b/gcc/analyzer/engine.cc @@ -37,6 +37,7 @@ along with GCC; see the file COPYING3. If not see #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" @@ -61,6 +62,7 @@ along with GCC; see the file COPYING3. If not see #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. */ @@ -764,6 +766,19 @@ eg_traits::dump_args_t::show_enode_details_p (const exploded_node &enode) const /* 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, @@ -952,6 +967,28 @@ exploded_node::dump (const extrinsic_state &ext_state) const 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. */ @@ -1502,6 +1539,30 @@ exploded_edge::dump_dot (graphviz_out *gv, const dump_args_t &) const 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. */ @@ -3057,6 +3118,55 @@ exploded_graph::dump_states_for_supernode (FILE *out, 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. */ @@ -4241,6 +4351,39 @@ private: 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 @@ -4341,6 +4484,9 @@ impl_run_checkers (logger *logger) free (filename); } + if (flag_dump_analyzer_json) + dump_analyzer_json (sg, eg); + delete purge_map; } diff --git a/gcc/analyzer/exploded-graph.h b/gcc/analyzer/exploded-graph.h index 04e878fbdfc..f723d52bdf8 100644 --- a/gcc/analyzer/exploded-graph.h +++ b/gcc/analyzer/exploded-graph.h @@ -165,6 +165,7 @@ class exploded_node : public dnode<eg_traits> /* 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); @@ -179,6 +180,8 @@ class exploded_node : public dnode<eg_traits> 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 { @@ -307,6 +310,8 @@ class exploded_edge : public dedge<eg_traits> 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; @@ -782,6 +787,8 @@ public: 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 diff --git a/gcc/analyzer/pending-diagnostic.cc b/gcc/analyzer/pending-diagnostic.cc index c196903afe0..502d17719b0 100644 --- a/gcc/analyzer/pending-diagnostic.cc +++ b/gcc/analyzer/pending-diagnostic.cc @@ -25,6 +25,7 @@ along with GCC; see the file COPYING3. If not see #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" diff --git a/gcc/analyzer/program-point.cc b/gcc/analyzer/program-point.cc index ef19e6e38e5..429d6ece724 100644 --- a/gcc/analyzer/program-point.cc +++ b/gcc/analyzer/program-point.cc @@ -24,6 +24,7 @@ along with GCC; see the file COPYING3. If not see #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" @@ -281,6 +282,43 @@ program_point::dump () const 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 diff --git a/gcc/analyzer/program-point.h b/gcc/analyzer/program-point.h index 97fd0a5e9de..d804621a715 100644 --- a/gcc/analyzer/program-point.h +++ b/gcc/analyzer/program-point.h @@ -175,6 +175,8 @@ public: 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 { diff --git a/gcc/analyzer/program-state.cc b/gcc/analyzer/program-state.cc index 71bb2864d6d..83a6e5b081e 100644 --- a/gcc/analyzer/program-state.cc +++ b/gcc/analyzer/program-state.cc @@ -25,6 +25,7 @@ along with GCC; see the file COPYING3. If not see #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" @@ -99,6 +100,26 @@ extrinsic_state::dump () const 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 * @@ -208,6 +229,33 @@ sm_state_map::dump (bool simple) const 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). */ @@ -733,6 +781,43 @@ program_state::dump (const extrinsic_state &ext_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. */ diff --git a/gcc/analyzer/program-state.h b/gcc/analyzer/program-state.h index cb0df8cd904..a52fbeb2e3e 100644 --- a/gcc/analyzer/program-state.h +++ b/gcc/analyzer/program-state.h @@ -53,6 +53,8 @@ public: 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; @@ -109,6 +111,8 @@ public: pretty_printer *pp) const; void dump (bool simple) const; + json::object *to_json () const; + bool is_empty_p () const; hashval_t hash () const; @@ -204,6 +208,8 @@ public: 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; diff --git a/gcc/analyzer/region-model-impl-calls.cc b/gcc/analyzer/region-model-impl-calls.cc index 423f74a4152..009b8c3ecb0 100644 --- a/gcc/analyzer/region-model-impl-calls.cc +++ b/gcc/analyzer/region-model-impl-calls.cc @@ -42,6 +42,7 @@ along with GCC; see the file COPYING3. If not see #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" diff --git a/gcc/analyzer/region-model-manager.cc b/gcc/analyzer/region-model-manager.cc index da8fa01077b..8dd3ad0020a 100644 --- a/gcc/analyzer/region-model-manager.cc +++ b/gcc/analyzer/region-model-manager.cc @@ -42,6 +42,7 @@ along with GCC; see the file COPYING3. If not see #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" diff --git a/gcc/analyzer/region-model-reachability.cc b/gcc/analyzer/region-model-reachability.cc index 681b8f74b19..c1b3b2db630 100644 --- a/gcc/analyzer/region-model-reachability.cc +++ b/gcc/analyzer/region-model-reachability.cc @@ -47,6 +47,7 @@ along with GCC; see the file COPYING3. If not see #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" diff --git a/gcc/analyzer/region-model.cc b/gcc/analyzer/region-model.cc index 6f04904a74e..74a96b025a4 100644 --- a/gcc/analyzer/region-model.cc +++ b/gcc/analyzer/region-model.cc @@ -42,6 +42,7 @@ along with GCC; see the file COPYING3. If not see #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" diff --git a/gcc/analyzer/region-model.h b/gcc/analyzer/region-model.h index 4859df369cf..1e8a517dd8c 100644 --- a/gcc/analyzer/region-model.h +++ b/gcc/analyzer/region-model.h @@ -256,6 +256,8 @@ public: 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 * @@ -1400,6 +1402,8 @@ public: 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 *); diff --git a/gcc/analyzer/region.cc b/gcc/analyzer/region.cc index 53f32dc912c..0820893a9b4 100644 --- a/gcc/analyzer/region.cc +++ b/gcc/analyzer/region.cc @@ -44,6 +44,7 @@ along with GCC; see the file COPYING3. If not see #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" @@ -460,6 +461,17 @@ region::dump (bool simple) const 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 diff --git a/gcc/analyzer/sm-file.cc b/gcc/analyzer/sm-file.cc index 58a0fd461fa..d2010710529 100644 --- a/gcc/analyzer/sm-file.cc +++ b/gcc/analyzer/sm-file.cc @@ -29,6 +29,7 @@ along with GCC; see the file COPYING3. If not see #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" diff --git a/gcc/analyzer/sm-malloc.cc b/gcc/analyzer/sm-malloc.cc index 12b2383e4a7..6293d7885cd 100644 --- a/gcc/analyzer/sm-malloc.cc +++ b/gcc/analyzer/sm-malloc.cc @@ -30,6 +30,7 @@ along with GCC; see the file COPYING3. If not see #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" diff --git a/gcc/analyzer/sm-pattern-test.cc b/gcc/analyzer/sm-pattern-test.cc index bb6d3b1e719..c430476a846 100644 --- a/gcc/analyzer/sm-pattern-test.cc +++ b/gcc/analyzer/sm-pattern-test.cc @@ -31,6 +31,7 @@ along with GCC; see the file COPYING3. If not see #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" diff --git a/gcc/analyzer/sm-sensitive.cc b/gcc/analyzer/sm-sensitive.cc index 49f9eb387b1..aec0a6ab8da 100644 --- a/gcc/analyzer/sm-sensitive.cc +++ b/gcc/analyzer/sm-sensitive.cc @@ -31,6 +31,7 @@ along with GCC; see the file COPYING3. If not see #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" diff --git a/gcc/analyzer/sm-signal.cc b/gcc/analyzer/sm-signal.cc index bf6ea480423..2e05de8a2de 100644 --- a/gcc/analyzer/sm-signal.cc +++ b/gcc/analyzer/sm-signal.cc @@ -32,6 +32,7 @@ along with GCC; see the file COPYING3. If not see #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" @@ -220,6 +221,12 @@ public: 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 { diff --git a/gcc/analyzer/sm-taint.cc b/gcc/analyzer/sm-taint.cc index 49bbd6dfb13..37491d8a49a 100644 --- a/gcc/analyzer/sm-taint.cc +++ b/gcc/analyzer/sm-taint.cc @@ -31,6 +31,7 @@ along with GCC; see the file COPYING3. If not see #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" diff --git a/gcc/analyzer/sm.cc b/gcc/analyzer/sm.cc index a333063c65e..3fe75ef8c4a 100644 --- a/gcc/analyzer/sm.cc +++ b/gcc/analyzer/sm.cc @@ -29,6 +29,9 @@ along with GCC; see the file COPYING3. If not see #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" @@ -56,6 +59,17 @@ state_machine::state::dump_to_pp (pretty_printer *pp) const 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. */ @@ -109,6 +123,28 @@ state_machine::dump_to_pp (pretty_printer *pp) const } } +/* 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. */ diff --git a/gcc/analyzer/sm.h b/gcc/analyzer/sm.h index f44ad922200..46b93ffb790 100644 --- a/gcc/analyzer/sm.h +++ b/gcc/analyzer/sm.h @@ -48,6 +48,7 @@ public: 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; } @@ -121,6 +122,8 @@ public: void dump_to_pp (pretty_printer *pp) const; + json::object *to_json () const; + state_t get_start_state () const { return m_start; } protected: diff --git a/gcc/analyzer/state-purge.cc b/gcc/analyzer/state-purge.cc index d5a24b48e1e..e4942a692fa 100644 --- a/gcc/analyzer/state-purge.cc +++ b/gcc/analyzer/state-purge.cc @@ -37,6 +37,7 @@ along with GCC; see the file COPYING3. If not see #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" diff --git a/gcc/analyzer/store.cc b/gcc/analyzer/store.cc index 1348895e5c7..11585123561 100644 --- a/gcc/analyzer/store.cc +++ b/gcc/analyzer/store.cc @@ -42,6 +42,7 @@ along with GCC; see the file COPYING3. If not see #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" @@ -122,6 +123,17 @@ binding_key::dump (bool simple) const 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 @@ -366,6 +378,37 @@ binding_map::dump (bool simple) const 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. */ @@ -657,6 +700,23 @@ binding_cluster::dump (bool simple) const 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. */ @@ -1575,6 +1635,64 @@ store::dump (bool simple) const 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 * diff --git a/gcc/analyzer/store.h b/gcc/analyzer/store.h index 83a43107b21..0f4e7ab2a56 100644 --- a/gcc/analyzer/store.h +++ b/gcc/analyzer/store.h @@ -159,6 +159,7 @@ public: 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 *); @@ -340,6 +341,8 @@ public: 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); @@ -392,6 +395,8 @@ public: 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); @@ -517,6 +522,8 @@ public: 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; diff --git a/gcc/analyzer/supergraph.cc b/gcc/analyzer/supergraph.cc index 7c6fed3a060..735c4a30e09 100644 --- a/gcc/analyzer/supergraph.cc +++ b/gcc/analyzer/supergraph.cc @@ -43,6 +43,7 @@ along with GCC; see the file COPYING3. If not see #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" @@ -374,6 +375,38 @@ supergraph::dump_dot (const char *path, const dump_args_t &dump_args) const 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 @@ -594,6 +627,63 @@ supernode::dump_dot_id (pretty_printer *pp) const 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 @@ -759,6 +849,28 @@ superedge::dump_dot (graphviz_out *gv, const dump_args_t &) const 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. */ diff --git a/gcc/analyzer/supergraph.h b/gcc/analyzer/supergraph.h index c25043d92fd..40ae9ff8ac2 100644 --- a/gcc/analyzer/supergraph.h +++ b/gcc/analyzer/supergraph.h @@ -148,6 +148,8 @@ public: 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 (); } @@ -231,6 +233,8 @@ class supernode : public dnode<supergraph_traits> 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; @@ -289,6 +293,8 @@ class superedge : public dedge<supergraph_traits> 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; } diff --git a/gcc/analyzer/svalue.cc b/gcc/analyzer/svalue.cc index fcab578674e..ae3b6783e9c 100644 --- a/gcc/analyzer/svalue.cc +++ b/gcc/analyzer/svalue.cc @@ -42,6 +42,7 @@ along with GCC; see the file COPYING3. If not see #include "bitmap.h" #include "selftest.h" #include "function.h" +#include "json.h" #include "analyzer/analyzer.h" #include "analyzer/analyzer-logging.h" #include "options.h" @@ -116,6 +117,17 @@ svalue::get_desc (bool simple) const 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. */ diff --git a/gcc/doc/analyzer.texi b/gcc/doc/analyzer.texi index 92c12e19401..6b7d70cccaa 100644 --- a/gcc/doc/analyzer.texi +++ b/gcc/doc/analyzer.texi @@ -488,6 +488,9 @@ truthfulness of the argument. This is useful for writing DejaGnu tests. @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}. diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index 8be2b4f4de6..49b5348c606 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -406,6 +406,7 @@ Objective-C and Objective-C++ Dialects}. -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 @@ -9103,6 +9104,12 @@ Dump a textual representation of the ``exploded graph'' to 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