diff options
author | David Malcolm <dmalcolm@redhat.com> | 2020-09-18 13:59:21 -0400 |
---|---|---|
committer | David Malcolm <dmalcolm@redhat.com> | 2020-09-22 14:47:03 -0400 |
commit | 809192e77e6e112a0fe32dee7fada7a49fbf25cd (patch) | |
tree | d6a2be6a09802aad22e6eb2a779f6baefacf700c /gcc | |
parent | 7c8ba5da80d5d95a8521010d6731d0d83036145d (diff) | |
download | gcc-809192e77e6e112a0fe32dee7fada7a49fbf25cd.zip gcc-809192e77e6e112a0fe32dee7fada7a49fbf25cd.tar.gz gcc-809192e77e6e112a0fe32dee7fada7a49fbf25cd.tar.bz2 |
analyzer: add -fdump-analyzer-json
I've found this useful for debugging state explosions in the analyzer.
gcc/analyzer/ChangeLog:
* analysis-plan.cc: Include "json.h".
* analyzer.opt (fdump-analyzer-json): New.
* call-string.cc: Include "json.h".
(call_string::to_json): New.
* call-string.h (call_string::to_json): New decl.
* checker-path.cc: Include "json.h".
* constraint-manager.cc: Include "json.h".
(equiv_class::to_json): New.
(constraint::to_json): New.
(constraint_manager::to_json): New.
* constraint-manager.h (equiv_class::to_json): New decl.
(constraint::to_json): New decl.
(constraint_manager::to_json): New decl.
* diagnostic-manager.cc: Include "json.h".
(saved_diagnostic::to_json): New.
(diagnostic_manager::to_json): New.
* diagnostic-manager.h (saved_diagnostic::to_json): New decl.
(diagnostic_manager::to_json): New decl.
* engine.cc: Include "json.h", <zlib.h>.
(exploded_node::status_to_str): New.
(exploded_node::to_json): New.
(exploded_edge::to_json): New.
(exploded_graph::to_json): New.
(dump_analyzer_json): New.
(impl_run_checkers): Call it.
* exploded-graph.h (exploded_node::status_to_str): New decl.
(exploded_node::to_json): New.
(exploded_edge::to_json): New.
(exploded_graph::to_json): New.
* pending-diagnostic.cc: Include "json.h".
* program-point.cc: Include "json.h".
(program_point::to_json): New.
* program-point.h (program_point::to_json): New decl.
* program-state.cc: Include "json.h".
(extrinsic_state::to_json): New.
(sm_state_map::to_json): New.
(program_state::to_json): New.
* program-state.h (extrinsic_state::to_json): New decl.
(sm_state_map::to_json): New decl.
(program_state::to_json): New decl.
* region-model-impl-calls.cc: Include "json.h".
* region-model-manager.cc: Include "json.h".
* region-model-reachability.cc: Include "json.h".
* region-model.cc: Include "json.h".
* region-model.h (svalue::to_json): New decl.
(region::to_json): New decl.
* region.cc: Include "json.h".
(region::to_json: New.
* sm-file.cc: Include "json.h".
* sm-malloc.cc: Include "json.h".
* sm-pattern-test.cc: Include "json.h".
* sm-sensitive.cc: Include "json.h".
* sm-signal.cc: Include "json.h".
(signal_delivery_edge_info_t::to_json): New.
* sm-taint.cc: Include "json.h".
* sm.cc: Include "diagnostic.h", "tree-diagnostic.h", and
"json.h".
(state_machine::state::to_json): New.
(state_machine::to_json): New.
* sm.h (state_machine::state::to_json): New.
(state_machine::to_json): New.
* state-purge.cc: Include "json.h".
* store.cc: Include "json.h".
(binding_key::get_desc): New.
(binding_map::to_json): New.
(binding_cluster::to_json): New.
(store::to_json): New.
* store.h (binding_key::get_desc): New decl.
(binding_map::to_json): New decl.
(binding_cluster::to_json): New decl.
(store::to_json): New decl.
* supergraph.cc: Include "json.h".
(supergraph::to_json): New.
(supernode::to_json): New.
(superedge::to_json): New.
* supergraph.h (supergraph::to_json): New decl.
(supernode::to_json): New decl.
(superedge::to_json): New decl.
* svalue.cc: Include "json.h".
(svalue::to_json): New.
gcc/ChangeLog:
* doc/analyzer.texi (Other Debugging Techniques): Mention
-fdump-analyzer-json.
* doc/invoke.texi (Static Analyzer Options): Add
-fdump-analyzer-json.
Diffstat (limited to 'gcc')
38 files changed, 804 insertions, 0 deletions
diff --git a/gcc/analyzer/analysis-plan.cc b/gcc/analyzer/analysis-plan.cc index 3c8b10b..7e48f52 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 94a686d..872fb31 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 d363031..72568c6 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 1b5db0a..5a03c59 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 c281316..1f6d6a8 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 521501f..5cd2c9e 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 3c31a89..98960ff 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 4a95d4c..8d7e508 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 1e310f7..c32f0c4 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 d03e23a..df7e335 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 04e878f..f723d52b 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 c196903..502d177 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 ef19e6e..429d6ec 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 97fd0a5..d804621 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 71bb286..83a6e5b 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 cb0df8c..a52fbeb 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 423f74a..009b8c3 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 da8fa01..8dd3ad0 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 681b8f7..c1b3b2d 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 6f04904..74a96b0 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 4859df3..1e8a517 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 53f32dc..0820893 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 58a0fd4..d201071 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 12b2383..6293d78 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 bb6d3b1..c430476 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 49f9eb3..aec0a6a 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 bf6ea48..2e05de8 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 49bbd6d..37491d8 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 a333063..3fe75ef 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 f44ad92..46b93ff 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 d5a24b4..e4942a6 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 1348895..1158512 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 83a4310..0f4e7ab 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 7c6fed3..735c4a3 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 c25043d..40ae9ff 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 fcab578..ae3b678 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 92c12e1..6b7d70c 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 6a7833b1..f726ff4 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -407,6 +407,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 @@ -9123,6 +9124,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 |