From: David Malcolm Date: Fri, 18 Sep 2020 17:59:21 +0000 (-0400) Subject: analyzer: add -fdump-analyzer-json X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=809192e77e6e112a0fe32dee7fada7a49fbf25cd;p=gcc.git 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", . (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. --- 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 /* 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. */ +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 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 > 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 /* 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 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 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 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 (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 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 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 (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 (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 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 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 6a7833b1d75..f726ff4756b 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