diff mbox series

[committed] analyzer: add -fdump-analyzer-json

Message ID 20200922184937.20248-1-dmalcolm@redhat.com
State New
Headers show
Series [committed] analyzer: add -fdump-analyzer-json | expand

Commit Message

David Malcolm Sept. 22, 2020, 6:49 p.m. UTC
I've found this useful for debugging state explosions in the analyzer.

Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu.
Pushed to master as 809192e77e6e112a0fe32dee7fada7a49fbf25cd.

gcc/analyzer/ChangeLog:
	* analysis-plan.cc: Include "json.h".
	* analyzer.opt (fdump-analyzer-json): New.
	* call-string.cc: Include "json.h".
	(call_string::to_json): New.
	* call-string.h (call_string::to_json): New decl.
	* checker-path.cc: Include "json.h".
	* constraint-manager.cc: Include "json.h".
	(equiv_class::to_json): New.
	(constraint::to_json): New.
	(constraint_manager::to_json): New.
	* constraint-manager.h (equiv_class::to_json): New decl.
	(constraint::to_json): New decl.
	(constraint_manager::to_json): New decl.
	* diagnostic-manager.cc: Include "json.h".
	(saved_diagnostic::to_json): New.
	(diagnostic_manager::to_json): New.
	* diagnostic-manager.h (saved_diagnostic::to_json): New decl.
	(diagnostic_manager::to_json): New decl.
	* engine.cc: Include "json.h", <zlib.h>.
	(exploded_node::status_to_str): New.
	(exploded_node::to_json): New.
	(exploded_edge::to_json): New.
	(exploded_graph::to_json): New.
	(dump_analyzer_json): New.
	(impl_run_checkers): Call it.
	* exploded-graph.h (exploded_node::status_to_str): New decl.
	(exploded_node::to_json): New.
	(exploded_edge::to_json): New.
	(exploded_graph::to_json): New.
	* pending-diagnostic.cc: Include "json.h".
	* program-point.cc: Include "json.h".
	(program_point::to_json): New.
	* program-point.h (program_point::to_json): New decl.
	* program-state.cc: Include "json.h".
	(extrinsic_state::to_json): New.
	(sm_state_map::to_json): New.
	(program_state::to_json): New.
	* program-state.h (extrinsic_state::to_json): New decl.
	(sm_state_map::to_json): New decl.
	(program_state::to_json): New decl.
	* region-model-impl-calls.cc: Include "json.h".
	* region-model-manager.cc: Include "json.h".
	* region-model-reachability.cc: Include "json.h".
	* region-model.cc: Include "json.h".
	* region-model.h (svalue::to_json): New decl.
	(region::to_json): New decl.
	* region.cc: Include "json.h".
	(region::to_json: New.
	* sm-file.cc: Include "json.h".
	* sm-malloc.cc: Include "json.h".
	* sm-pattern-test.cc: Include "json.h".
	* sm-sensitive.cc: Include "json.h".
	* sm-signal.cc: Include "json.h".
	(signal_delivery_edge_info_t::to_json): New.
	* sm-taint.cc: Include "json.h".
	* sm.cc: Include "diagnostic.h", "tree-diagnostic.h", and
	"json.h".
	(state_machine::state::to_json): New.
	(state_machine::to_json): New.
	* sm.h (state_machine::state::to_json): New.
	(state_machine::to_json): New.
	* state-purge.cc: Include "json.h".
	* store.cc: Include "json.h".
	(binding_key::get_desc): New.
	(binding_map::to_json): New.
	(binding_cluster::to_json): New.
	(store::to_json): New.
	* store.h (binding_key::get_desc): New decl.
	(binding_map::to_json): New decl.
	(binding_cluster::to_json): New decl.
	(store::to_json): New decl.
	* supergraph.cc: Include "json.h".
	(supergraph::to_json): New.
	(supernode::to_json): New.
	(superedge::to_json): New.
	* supergraph.h (supergraph::to_json): New decl.
	(supernode::to_json): New decl.
	(superedge::to_json): New decl.
	* svalue.cc: Include "json.h".
	(svalue::to_json): New.

gcc/ChangeLog:
	* doc/analyzer.texi (Other Debugging Techniques): Mention
	-fdump-analyzer-json.
	* doc/invoke.texi (Static Analyzer Options): Add
	-fdump-analyzer-json.
---
 gcc/analyzer/analysis-plan.cc             |   1 +
 gcc/analyzer/analyzer.opt                 |   4 +
 gcc/analyzer/call-string.cc               |  29 +++++
 gcc/analyzer/call-string.h                |   2 +
 gcc/analyzer/checker-path.cc              |   1 +
 gcc/analyzer/constraint-manager.cc        |  77 ++++++++++++
 gcc/analyzer/constraint-manager.h         |   6 +
 gcc/analyzer/diagnostic-manager.cc        |  58 +++++++++
 gcc/analyzer/diagnostic-manager.h         |   4 +
 gcc/analyzer/engine.cc                    | 146 ++++++++++++++++++++++
 gcc/analyzer/exploded-graph.h             |   7 ++
 gcc/analyzer/pending-diagnostic.cc        |   1 +
 gcc/analyzer/program-point.cc             |  38 ++++++
 gcc/analyzer/program-point.h              |   2 +
 gcc/analyzer/program-state.cc             |  85 +++++++++++++
 gcc/analyzer/program-state.h              |   6 +
 gcc/analyzer/region-model-impl-calls.cc   |   1 +
 gcc/analyzer/region-model-manager.cc      |   1 +
 gcc/analyzer/region-model-reachability.cc |   1 +
 gcc/analyzer/region-model.cc              |   1 +
 gcc/analyzer/region-model.h               |   4 +
 gcc/analyzer/region.cc                    |  12 ++
 gcc/analyzer/sm-file.cc                   |   1 +
 gcc/analyzer/sm-malloc.cc                 |   1 +
 gcc/analyzer/sm-pattern-test.cc           |   1 +
 gcc/analyzer/sm-sensitive.cc              |   1 +
 gcc/analyzer/sm-signal.cc                 |   7 ++
 gcc/analyzer/sm-taint.cc                  |   1 +
 gcc/analyzer/sm.cc                        |  36 ++++++
 gcc/analyzer/sm.h                         |   3 +
 gcc/analyzer/state-purge.cc               |   1 +
 gcc/analyzer/store.cc                     | 118 +++++++++++++++++
 gcc/analyzer/store.h                      |   7 ++
 gcc/analyzer/supergraph.cc                | 112 +++++++++++++++++
 gcc/analyzer/supergraph.h                 |   6 +
 gcc/analyzer/svalue.cc                    |  12 ++
 gcc/doc/analyzer.texi                     |   3 +
 gcc/doc/invoke.texi                       |   7 ++
 38 files changed, 804 insertions(+)

Comments

Tobias Burnus Sept. 22, 2020, 8:55 p.m. UTC | #1
This patch breaks the cross build here:
...gcc/analyzer/engine.cc:65:10: fatal error: zlib.h: No such file or directory

I think you need to do something similar in Makefile.in as lto-compress has:

# lto-compress.o needs $(ZLIBINC) added to the include flags.
CFLAGS-lto-compress.o += $(ZLIBINC) $(ZSTD_INC)

Tobias

On 9/22/20 8:49 PM, David Malcolm via Gcc-patches wrote:
> I've found this useful for debugging state explosions in the analyzer.
>
> Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu.
> Pushed to master as 809192e77e6e112a0fe32dee7fada7a49fbf25cd.
>
> gcc/analyzer/ChangeLog:
>       * analysis-plan.cc: Include "json.h".
>       * analyzer.opt (fdump-analyzer-json): New.
>       * call-string.cc: Include "json.h".
>       (call_string::to_json): New.
>       * call-string.h (call_string::to_json): New decl.
>       * checker-path.cc: Include "json.h".
>       * constraint-manager.cc: Include "json.h".
>       (equiv_class::to_json): New.
>       (constraint::to_json): New.
>       (constraint_manager::to_json): New.
>       * constraint-manager.h (equiv_class::to_json): New decl.
>       (constraint::to_json): New decl.
>       (constraint_manager::to_json): New decl.
>       * diagnostic-manager.cc: Include "json.h".
>       (saved_diagnostic::to_json): New.
>       (diagnostic_manager::to_json): New.
>       * diagnostic-manager.h (saved_diagnostic::to_json): New decl.
>       (diagnostic_manager::to_json): New decl.
>       * engine.cc: Include "json.h", <zlib.h>.
>       (exploded_node::status_to_str): New.
>       (exploded_node::to_json): New.
>       (exploded_edge::to_json): New.
>       (exploded_graph::to_json): New.
>       (dump_analyzer_json): New.
>       (impl_run_checkers): Call it.
>       * exploded-graph.h (exploded_node::status_to_str): New decl.
>       (exploded_node::to_json): New.
>       (exploded_edge::to_json): New.
>       (exploded_graph::to_json): New.
>       * pending-diagnostic.cc: Include "json.h".
>       * program-point.cc: Include "json.h".
>       (program_point::to_json): New.
>       * program-point.h (program_point::to_json): New decl.
>       * program-state.cc: Include "json.h".
>       (extrinsic_state::to_json): New.
>       (sm_state_map::to_json): New.
>       (program_state::to_json): New.
>       * program-state.h (extrinsic_state::to_json): New decl.
>       (sm_state_map::to_json): New decl.
>       (program_state::to_json): New decl.
>       * region-model-impl-calls.cc: Include "json.h".
>       * region-model-manager.cc: Include "json.h".
>       * region-model-reachability.cc: Include "json.h".
>       * region-model.cc: Include "json.h".
>       * region-model.h (svalue::to_json): New decl.
>       (region::to_json): New decl.
>       * region.cc: Include "json.h".
>       (region::to_json: New.
>       * sm-file.cc: Include "json.h".
>       * sm-malloc.cc: Include "json.h".
>       * sm-pattern-test.cc: Include "json.h".
>       * sm-sensitive.cc: Include "json.h".
>       * sm-signal.cc: Include "json.h".
>       (signal_delivery_edge_info_t::to_json): New.
>       * sm-taint.cc: Include "json.h".
>       * sm.cc: Include "diagnostic.h", "tree-diagnostic.h", and
>       "json.h".
>       (state_machine::state::to_json): New.
>       (state_machine::to_json): New.
>       * sm.h (state_machine::state::to_json): New.
>       (state_machine::to_json): New.
>       * state-purge.cc: Include "json.h".
>       * store.cc: Include "json.h".
>       (binding_key::get_desc): New.
>       (binding_map::to_json): New.
>       (binding_cluster::to_json): New.
>       (store::to_json): New.
>       * store.h (binding_key::get_desc): New decl.
>       (binding_map::to_json): New decl.
>       (binding_cluster::to_json): New decl.
>       (store::to_json): New decl.
>       * supergraph.cc: Include "json.h".
>       (supergraph::to_json): New.
>       (supernode::to_json): New.
>       (superedge::to_json): New.
>       * supergraph.h (supergraph::to_json): New decl.
>       (supernode::to_json): New decl.
>       (superedge::to_json): New decl.
>       * svalue.cc: Include "json.h".
>       (svalue::to_json): New.
>
> gcc/ChangeLog:
>       * doc/analyzer.texi (Other Debugging Techniques): Mention
>       -fdump-analyzer-json.
>       * doc/invoke.texi (Static Analyzer Options): Add
>       -fdump-analyzer-json.
> ---
>   gcc/analyzer/analysis-plan.cc             |   1 +
>   gcc/analyzer/analyzer.opt                 |   4 +
>   gcc/analyzer/call-string.cc               |  29 +++++
>   gcc/analyzer/call-string.h                |   2 +
>   gcc/analyzer/checker-path.cc              |   1 +
>   gcc/analyzer/constraint-manager.cc        |  77 ++++++++++++
>   gcc/analyzer/constraint-manager.h         |   6 +
>   gcc/analyzer/diagnostic-manager.cc        |  58 +++++++++
>   gcc/analyzer/diagnostic-manager.h         |   4 +
>   gcc/analyzer/engine.cc                    | 146 ++++++++++++++++++++++
>   gcc/analyzer/exploded-graph.h             |   7 ++
>   gcc/analyzer/pending-diagnostic.cc        |   1 +
>   gcc/analyzer/program-point.cc             |  38 ++++++
>   gcc/analyzer/program-point.h              |   2 +
>   gcc/analyzer/program-state.cc             |  85 +++++++++++++
>   gcc/analyzer/program-state.h              |   6 +
>   gcc/analyzer/region-model-impl-calls.cc   |   1 +
>   gcc/analyzer/region-model-manager.cc      |   1 +
>   gcc/analyzer/region-model-reachability.cc |   1 +
>   gcc/analyzer/region-model.cc              |   1 +
>   gcc/analyzer/region-model.h               |   4 +
>   gcc/analyzer/region.cc                    |  12 ++
>   gcc/analyzer/sm-file.cc                   |   1 +
>   gcc/analyzer/sm-malloc.cc                 |   1 +
>   gcc/analyzer/sm-pattern-test.cc           |   1 +
>   gcc/analyzer/sm-sensitive.cc              |   1 +
>   gcc/analyzer/sm-signal.cc                 |   7 ++
>   gcc/analyzer/sm-taint.cc                  |   1 +
>   gcc/analyzer/sm.cc                        |  36 ++++++
>   gcc/analyzer/sm.h                         |   3 +
>   gcc/analyzer/state-purge.cc               |   1 +
>   gcc/analyzer/store.cc                     | 118 +++++++++++++++++
>   gcc/analyzer/store.h                      |   7 ++
>   gcc/analyzer/supergraph.cc                | 112 +++++++++++++++++
>   gcc/analyzer/supergraph.h                 |   6 +
>   gcc/analyzer/svalue.cc                    |  12 ++
>   gcc/doc/analyzer.texi                     |   3 +
>   gcc/doc/invoke.texi                       |   7 ++
>   38 files changed, 804 insertions(+)
>
> diff --git a/gcc/analyzer/analysis-plan.cc b/gcc/analyzer/analysis-plan.cc
> index 3c8b10b3314..7e48f526094 100644
> --- a/gcc/analyzer/analysis-plan.cc
> +++ b/gcc/analyzer/analysis-plan.cc
> @@ -27,6 +27,7 @@ along with GCC; see the file COPYING3.  If not see
>   #include "timevar.h"
>   #include "ipa-utils.h"
>   #include "function.h"
> +#include "json.h"
>   #include "analyzer/analyzer.h"
>   #include "diagnostic-core.h"
>   #include "analyzer/analyzer-logging.h"
> diff --git a/gcc/analyzer/analyzer.opt b/gcc/analyzer/analyzer.opt
> index 94a686db6b3..872fb31048e 100644
> --- a/gcc/analyzer/analyzer.opt
> +++ b/gcc/analyzer/analyzer.opt
> @@ -186,6 +186,10 @@ fdump-analyzer-exploded-nodes-3
>   Common RejectNegative Var(flag_dump_analyzer_exploded_nodes_3)
>   Dump a textual representation of the exploded graph to SRCFILE.eg-ID.txt.
>
> +fdump-analyzer-json
> +Common RejectNegative Var(flag_dump_analyzer_json)
> +Dump analyzer-specific data to a SRCFILE.analyzer.json.gz file.
> +
>   fdump-analyzer-state-purge
>   Common RejectNegative Var(flag_dump_analyzer_state_purge)
>   Dump state-purging information to a SRCFILE.state-purge.dot file.
> diff --git a/gcc/analyzer/call-string.cc b/gcc/analyzer/call-string.cc
> index d3630316c4e..72568c6efa7 100644
> --- a/gcc/analyzer/call-string.cc
> +++ b/gcc/analyzer/call-string.cc
> @@ -24,6 +24,7 @@ along with GCC; see the file COPYING3.  If not see
>   #include "pretty-print.h"
>   #include "tree.h"
>   #include "options.h"
> +#include "json.h"
>   #include "analyzer/call-string.h"
>   #include "ordered-hash-map.h"
>   #include "options.h"
> @@ -104,6 +105,34 @@ call_string::print (pretty_printer *pp) const
>     pp_string (pp, "]");
>   }
>
> +/* Return a new json::array of the form
> +   [{"src_snode_idx" : int,
> +     "dst_snode_idx" : int,
> +     "funcname" : str},
> +     ...for each return_superedge in the callstring].  */
> +
> +json::value *
> +call_string::to_json () const
> +{
> +  json::array *arr = new json::array ();
> +
> +  const return_superedge *e;
> +  int i;
> +  FOR_EACH_VEC_ELT (m_return_edges, i, e)
> +    {
> +      json::object *e_obj = new json::object ();
> +      e_obj->set ("src_snode_idx",
> +               new json::integer_number (e->m_src->m_index));
> +      e_obj->set ("dst_snode_idx",
> +               new json::integer_number (e->m_dest->m_index));
> +      e_obj->set ("funcname",
> +               new json::string (function_name (e->m_dest->m_fun)));
> +      arr->append (e_obj);
> +    }
> +
> +  return arr;
> +}
> +
>   /* Generate a hash value for this call_string.  */
>
>   hashval_t
> diff --git a/gcc/analyzer/call-string.h b/gcc/analyzer/call-string.h
> index 1b5db0a4a20..5a03c592ecb 100644
> --- a/gcc/analyzer/call-string.h
> +++ b/gcc/analyzer/call-string.h
> @@ -47,6 +47,8 @@ public:
>
>     void print (pretty_printer *pp) const;
>
> +  json::value *to_json () const;
> +
>     hashval_t hash () const;
>
>     bool empty_p () const { return m_return_edges.is_empty (); }
> diff --git a/gcc/analyzer/checker-path.cc b/gcc/analyzer/checker-path.cc
> index c28131651c6..1f6d6a86001 100644
> --- a/gcc/analyzer/checker-path.cc
> +++ b/gcc/analyzer/checker-path.cc
> @@ -38,6 +38,7 @@ along with GCC; see the file COPYING3.  If not see
>   #include "fibonacci_heap.h"
>   #include "diagnostic-event-id.h"
>   #include "shortest-paths.h"
> +#include "json.h"
>   #include "analyzer/analyzer.h"
>   #include "analyzer/analyzer-logging.h"
>   #include "analyzer/sm.h"
> diff --git a/gcc/analyzer/constraint-manager.cc b/gcc/analyzer/constraint-manager.cc
> index 521501fd4f4..5cd2c9e0e0b 100644
> --- a/gcc/analyzer/constraint-manager.cc
> +++ b/gcc/analyzer/constraint-manager.cc
> @@ -31,6 +31,7 @@ along with GCC; see the file COPYING3.  If not see
>   #include "diagnostic-core.h"
>   #include "graphviz.h"
>   #include "function.h"
> +#include "json.h"
>   #include "analyzer/analyzer.h"
>   #include "ordered-hash-map.h"
>   #include "options.h"
> @@ -299,6 +300,33 @@ equiv_class::print (pretty_printer *pp) const
>     pp_character (pp, '}');
>   }
>
> +/* Return a new json::object of the form
> +   {"svals" : [str],
> +    "constant" : optional str}.  */
> +
> +json::object *
> +equiv_class::to_json () const
> +{
> +  json::object *ec_obj = new json::object ();
> +
> +  json::array *sval_arr = new json::array ();
> +  int i;
> +  const svalue *sval;
> +  FOR_EACH_VEC_ELT (m_vars, i, sval)
> +    sval_arr->append (sval->to_json ());
> +  ec_obj->set ("svals", sval_arr);
> +
> +  if (m_constant)
> +    {
> +      pretty_printer pp;
> +      pp_format_decoder (&pp) = default_tree_printer;
> +      pp_printf (&pp, "%qE", m_constant);
> +      ec_obj->set ("constant", new json::string (pp_formatted_text (&pp)));
> +    }
> +
> +  return ec_obj;
> +}
> +
>   /* Generate a hash value for this equiv_class.
>      This relies on the ordering of m_vars, and so this object needs to
>      have been canonicalized for this to be meaningful.  */
> @@ -499,6 +527,23 @@ constraint::print (pretty_printer *pp, const constraint_manager &cm) const
>     m_rhs.get_obj (cm).print (pp);
>   }
>
> +/* Return a new json::object of the form
> +   {"lhs" : int, the EC index
> +    "op"  : str,
> +    "rhs" : int, the EC index}.  */
> +
> +json::object *
> +constraint::to_json () const
> +{
> +  json::object *con_obj = new json::object ();
> +
> +  con_obj->set ("lhs", new json::integer_number (m_lhs.as_int ()));
> +  con_obj->set ("op", new json::string (constraint_op_code (m_op)));
> +  con_obj->set ("rhs", new json::integer_number (m_rhs.as_int ()));
> +
> +  return con_obj;
> +}
> +
>   /* Generate a hash value for this constraint.  */
>
>   hashval_t
> @@ -768,6 +813,38 @@ debug (const constraint_manager &cm)
>     cm.dump ();
>   }
>
> +/* Return a new json::object of the form
> +   {"ecs" : array of objects, one per equiv_class
> +    "constraints" : array of objects, one per constraint}.  */
> +
> +json::object *
> +constraint_manager::to_json () const
> +{
> +  json::object *cm_obj = new json::object ();
> +
> +  /* Equivalence classes.  */
> +  {
> +    json::array *ec_arr = new json::array ();
> +    int i;
> +    equiv_class *ec;
> +    FOR_EACH_VEC_ELT (m_equiv_classes, i, ec)
> +      ec_arr->append (ec->to_json ());
> +    cm_obj->set ("ecs", ec_arr);
> +  }
> +
> +  /* Constraints.  */
> +  {
> +    json::array *con_arr = new json::array ();
> +    int i;
> +    constraint *c;
> +    FOR_EACH_VEC_ELT (m_constraints, i, c)
> +      con_arr->append (c->to_json ());
> +    cm_obj->set ("constraints", con_arr);
> +  }
> +
> +  return cm_obj;
> +}
> +
>   /* Attempt to add the constraint LHS OP RHS to this constraint_manager.
>      Return true if the constraint could be added (or is already true).
>      Return false if the constraint contradicts existing knowledge.  */
> diff --git a/gcc/analyzer/constraint-manager.h b/gcc/analyzer/constraint-manager.h
> index 3c31a891f70..98960ffad84 100644
> --- a/gcc/analyzer/constraint-manager.h
> +++ b/gcc/analyzer/constraint-manager.h
> @@ -88,6 +88,8 @@ public:
>
>     void print (pretty_printer *pp) const;
>
> +  json::object *to_json () const;
> +
>     /* An equivalence class can contain multiple constants (e.g. multiple
>        different zeroes, for different types); these are just for the last
>        constant added.  */
> @@ -160,6 +162,8 @@ class constraint
>
>     void print (pretty_printer *pp, const constraint_manager &cm) const;
>
> +  json::object *to_json () const;
> +
>     hashval_t hash () const;
>     bool operator== (const constraint &other) const;
>
> @@ -215,6 +219,8 @@ public:
>     void dump (FILE *fp) const;
>     void dump () const;
>
> +  json::object *to_json () const;
> +
>     const equiv_class &get_equiv_class_by_index (unsigned idx) const
>     {
>       return *m_equiv_classes[idx];
> diff --git a/gcc/analyzer/diagnostic-manager.cc b/gcc/analyzer/diagnostic-manager.cc
> index 4a95d4c569e..8d7e5084cc0 100644
> --- a/gcc/analyzer/diagnostic-manager.cc
> +++ b/gcc/analyzer/diagnostic-manager.cc
> @@ -37,6 +37,7 @@ along with GCC; see the file COPYING3.  If not see
>   #include "tristate.h"
>   #include "selftest.h"
>   #include "ordered-hash-map.h"
> +#include "json.h"
>   #include "analyzer/analyzer.h"
>   #include "analyzer/analyzer-logging.h"
>   #include "analyzer/sm.h"
> @@ -114,6 +115,43 @@ saved_diagnostic::operator== (const saved_diagnostic &other) const
>         && m_trailing_eedge == other.m_trailing_eedge);
>   }
>
> +/* Return a new json::object of the form
> +   {"sm": optional str,
> +    "enode": int,
> +    "snode": int,
> +    "sval": optional str,
> +    "state": optional str,
> +    "path_length": int,
> +    "pending_diagnostic": str}.  */
> +
> +json::object *
> +saved_diagnostic::to_json () const
> +{
> +  json::object *sd_obj = new json::object ();
> +
> +  if (m_sm)
> +    sd_obj->set ("sm", new json::string (m_sm->get_name ()));
> +  sd_obj->set ("enode", new json::integer_number (m_enode->m_index));
> +  sd_obj->set ("snode", new json::integer_number (m_snode->m_index));
> +  if (m_sval)
> +    sd_obj->set ("sval", m_sval->to_json ());
> +  if (m_state)
> +    sd_obj->set ("state", m_state->to_json ());
> +  sd_obj->set ("path_length", new json::integer_number (m_epath_length));
> +  sd_obj->set ("pending_diagnostic", new json::string (m_d->get_kind ()));
> +
> +  /* We're not yet JSONifying the following fields:
> +     const gimple *m_stmt;
> +     stmt_finder *m_stmt_finder;
> +     tree m_var;
> +     exploded_edge *m_trailing_eedge;
> +     enum status m_status;
> +     feasibility_problem *m_problem;
> +  */
> +
> +  return sd_obj;
> +}
> +
>   /* State for building a checker_path from a particular exploded_path.
>      In particular, this precomputes reachability information: the set of
>      source enodes for which a path be found to the diagnostic enode.  */
> @@ -199,6 +237,26 @@ diagnostic_manager::add_diagnostic (const exploded_node *enode,
>     add_diagnostic (NULL, enode, snode, stmt, finder, NULL_TREE, NULL, 0, d);
>   }
>
> +/* Return a new json::object of the form
> +   {"diagnostics"  : [obj for saved_diagnostic]}.  */
> +
> +json::object *
> +diagnostic_manager::to_json () const
> +{
> +  json::object *dm_obj = new json::object ();
> +
> +  {
> +    json::array *sd_arr = new json::array ();
> +    int i;
> +    saved_diagnostic *sd;
> +    FOR_EACH_VEC_ELT (m_saved_diagnostics, i, sd)
> +      sd_arr->append (sd->to_json ());
> +    dm_obj->set ("diagnostics", sd_arr);
> +  }
> +
> +  return dm_obj;
> +}
> +
>   /* A class for identifying sets of duplicated pending_diagnostic.
>
>      We want to find the simplest dedupe_candidate amongst those that share a
> diff --git a/gcc/analyzer/diagnostic-manager.h b/gcc/analyzer/diagnostic-manager.h
> index 1e310f7fe15..c32f0c47dc1 100644
> --- a/gcc/analyzer/diagnostic-manager.h
> +++ b/gcc/analyzer/diagnostic-manager.h
> @@ -46,6 +46,8 @@ public:
>
>     bool operator== (const saved_diagnostic &other) const;
>
> +  json::object *to_json () const;
> +
>     void set_feasible ()
>     {
>       gcc_assert (m_status == STATUS_NEW);
> @@ -105,6 +107,8 @@ public:
>
>     engine *get_engine () const { return m_eng; }
>
> +  json::object *to_json () const;
> +
>     void add_diagnostic (const state_machine *sm,
>                      const exploded_node *enode,
>                      const supernode *snode, const gimple *stmt,
> diff --git a/gcc/analyzer/engine.cc b/gcc/analyzer/engine.cc
> index d03e23a9b6e..df7e33564f1 100644
> --- a/gcc/analyzer/engine.cc
> +++ b/gcc/analyzer/engine.cc
> @@ -37,6 +37,7 @@ along with GCC; see the file COPYING3.  If not see
>   #include "tristate.h"
>   #include "ordered-hash-map.h"
>   #include "selftest.h"
> +#include "json.h"
>   #include "analyzer/analyzer.h"
>   #include "analyzer/analyzer-logging.h"
>   #include "analyzer/call-string.h"
> @@ -61,6 +62,7 @@ along with GCC; see the file COPYING3.  If not see
>   #include "analyzer/checker-path.h"
>   #include "analyzer/state-purge.h"
>   #include "analyzer/bar-chart.h"
> +#include <zlib.h>
>
>   /* For an overview, see gcc/doc/analyzer.texi.  */
>
> @@ -764,6 +766,19 @@ eg_traits::dump_args_t::show_enode_details_p (const exploded_node &enode) const
>
>   /* class exploded_node : public dnode<eg_traits>.  */
>
> +const char *
> +exploded_node::status_to_str (enum status s)
> +{
> +  switch (s)
> +    {
> +    default: gcc_unreachable ();
> +    case STATUS_WORKLIST: return "WORKLIST";
> +    case STATUS_PROCESSED: return "PROCESSED";
> +    case STATUS_MERGER: return "MERGER";
> +    case STATUS_BULK_MERGED: return "BULK_MERGED";
> +    }
> +}
> +
>   /* exploded_node's ctor.  */
>
>   exploded_node::exploded_node (const point_and_state &ps,
> @@ -952,6 +967,28 @@ exploded_node::dump (const extrinsic_state &ext_state) const
>     dump (stderr, ext_state);
>   }
>
> +/* Return a new json::object of the form
> +   {"point"  : object for program_point,
> +    "state"  : object for program_state,
> +    "status" : str,
> +    "idx"    : int,
> +    "processed_stmts" : int}.  */
> +
> +json::object *
> +exploded_node::to_json (const extrinsic_state &ext_state) const
> +{
> +  json::object *enode_obj = new json::object ();
> +
> +  enode_obj->set ("point", get_point ().to_json ());
> +  enode_obj->set ("state", get_state ().to_json (ext_state));
> +  enode_obj->set ("status", new json::string (status_to_str (m_status)));
> +  enode_obj->set ("idx", new json::integer_number (m_index));
> +  enode_obj->set ("processed_stmts",
> +               new json::integer_number (m_num_processed_stmts));
> +
> +  return enode_obj;
> +}
> +
>   } // namespace ana
>
>   /* Return true if FNDECL has a gimple body.  */
> @@ -1502,6 +1539,30 @@ exploded_edge::dump_dot (graphviz_out *gv, const dump_args_t &) const
>     pp_printf (pp, "\"];\n");
>   }
>
> +/* Return a new json::object of the form
> +   {"src_idx": int, the index of the source exploded edge,
> +    "dst_idx": int, the index of the destination exploded edge,
> +    "sedge": (optional) object for the superedge, if any,
> +    "custom": (optional) str, a description, if this is a custom edge}.  */
> +
> +json::object *
> +exploded_edge::to_json () const
> +{
> +  json::object *eedge_obj = new json::object ();
> +  eedge_obj->set ("src_idx", new json::integer_number (m_src->m_index));
> +  eedge_obj->set ("dst_idx", new json::integer_number (m_dest->m_index));
> +  if (m_sedge)
> +    eedge_obj->set ("sedge", m_sedge->to_json ());
> +  if (m_custom_info)
> +    {
> +      pretty_printer pp;
> +      pp_format_decoder (&pp) = default_tree_printer;
> +      m_custom_info->print (&pp);
> +      eedge_obj->set ("custom", new json::string (pp_formatted_text (&pp)));
> +    }
> +  return eedge_obj;
> +}
> +
>   /* struct stats.  */
>
>   /* stats' ctor.  */
> @@ -3057,6 +3118,55 @@ exploded_graph::dump_states_for_supernode (FILE *out,
>          snode->m_index, state_idx);
>   }
>
> +/* Return a new json::object of the form
> +   {"nodes" : [objs for enodes],
> +    "edges" : [objs for eedges],
> +    "ext_state": object for extrinsic_state,
> +    "diagnostic_manager": object for diagnostic_manager}.  */
> +
> +json::object *
> +exploded_graph::to_json () const
> +{
> +  json::object *egraph_obj = new json::object ();
> +
> +  /* Nodes.  */
> +  {
> +    json::array *nodes_arr = new json::array ();
> +    unsigned i;
> +    exploded_node *n;
> +    FOR_EACH_VEC_ELT (m_nodes, i, n)
> +      nodes_arr->append (n->to_json (m_ext_state));
> +    egraph_obj->set ("nodes", nodes_arr);
> +  }
> +
> +  /* Edges.  */
> +  {
> +    json::array *edges_arr = new json::array ();
> +    unsigned i;
> +    exploded_edge *n;
> +    FOR_EACH_VEC_ELT (m_edges, i, n)
> +      edges_arr->append (n->to_json ());
> +    egraph_obj->set ("edges", edges_arr);
> +  }
> +
> +  /* m_sg is JSONified at the top-level.  */
> +
> +  egraph_obj->set ("ext_state", m_ext_state.to_json ());
> +  egraph_obj->set ("diagnostic_manager", m_diagnostic_manager.to_json ());
> +
> +  /* The following fields aren't yet being JSONified:
> +     worklist m_worklist;
> +     const state_purge_map *const m_purge_map;
> +     const analysis_plan &m_plan;
> +     stats m_global_stats;
> +     function_stat_map_t m_per_function_stats;
> +     stats m_functionless_stats;
> +     call_string_data_map_t m_per_call_string_data;
> +     auto_vec<int> m_PK_AFTER_SUPERNODE_per_snode;  */
> +
> +  return egraph_obj;
> +}
> +
>   /* Look for the last use of SEARCH_STMT within this path.
>      If found write the edge's index to *OUT_IDX and return true, otherwise
>      return false.  */
> @@ -4241,6 +4351,39 @@ private:
>     auto_delete_vec<auto_vec <exploded_node *> > m_enodes_per_snodes;
>   };
>
> +/* Implement -fdump-analyzer-json.  */
> +
> +static void
> +dump_analyzer_json (const supergraph &sg,
> +                 const exploded_graph &eg)
> +{
> +  auto_timevar tv (TV_ANALYZER_DUMP);
> +  char *filename = concat (dump_base_name, ".analyzer.json.gz", NULL);
> +  gzFile output = gzopen (filename, "w");
> +  if (!output)
> +    {
> +      error_at (UNKNOWN_LOCATION, "unable to open %qs for writing", filename);
> +      free (filename);
> +      return;
> +    }
> +
> +  json::object *toplev_obj = new json::object ();
> +  toplev_obj->set ("sgraph", sg.to_json ());
> +  toplev_obj->set ("egraph", eg.to_json ());
> +
> +  pretty_printer pp;
> +  toplev_obj->print (&pp);
> +  pp_formatted_text (&pp);
> +
> +  delete toplev_obj;
> +
> +  if (gzputs (output, pp_formatted_text (&pp)) == EOF
> +      || gzclose (output))
> +    error_at (UNKNOWN_LOCATION, "error writing %qs", filename);
> +
> +  free (filename);
> +}
> +
>   /* Run the analysis "engine".  */
>
>   void
> @@ -4341,6 +4484,9 @@ impl_run_checkers (logger *logger)
>         free (filename);
>       }
>
> +  if (flag_dump_analyzer_json)
> +    dump_analyzer_json (sg, eg);
> +
>     delete purge_map;
>   }
>
> diff --git a/gcc/analyzer/exploded-graph.h b/gcc/analyzer/exploded-graph.h
> index 04e878fbdfc..f723d52bdf8 100644
> --- a/gcc/analyzer/exploded-graph.h
> +++ b/gcc/analyzer/exploded-graph.h
> @@ -165,6 +165,7 @@ class exploded_node : public dnode<eg_traits>
>       /* Node was processed by maybe_process_run_of_before_supernode_enodes.  */
>       STATUS_BULK_MERGED
>     };
> +  static const char * status_to_str (enum status s);
>
>     exploded_node (const point_and_state &ps, int index);
>
> @@ -179,6 +180,8 @@ class exploded_node : public dnode<eg_traits>
>     void dump (FILE *fp, const extrinsic_state &ext_state) const;
>     void dump (const extrinsic_state &ext_state) const;
>
> +  json::object *to_json (const extrinsic_state &ext_state) const;
> +
>     /* The result of on_stmt.  */
>     struct on_stmt_flags
>     {
> @@ -307,6 +310,8 @@ class exploded_edge : public dedge<eg_traits>
>     void dump_dot (graphviz_out *gv, const dump_args_t &args)
>       const FINAL OVERRIDE;
>
> +  json::object *to_json () const;
> +
>     //private:
>     const superedge *const m_sedge;
>
> @@ -782,6 +787,8 @@ public:
>     void dump_states_for_supernode (FILE *, const supernode *snode) const;
>     void dump_exploded_nodes () const;
>
> +  json::object *to_json () const;
> +
>     exploded_node *get_node_by_index (int idx) const;
>
>     const call_string_data_map_t *get_per_call_string_data () const
> diff --git a/gcc/analyzer/pending-diagnostic.cc b/gcc/analyzer/pending-diagnostic.cc
> index c196903afe0..502d17719b0 100644
> --- a/gcc/analyzer/pending-diagnostic.cc
> +++ b/gcc/analyzer/pending-diagnostic.cc
> @@ -25,6 +25,7 @@ along with GCC; see the file COPYING3.  If not see
>   #include "intl.h"
>   #include "diagnostic.h"
>   #include "function.h"
> +#include "json.h"
>   #include "analyzer/analyzer.h"
>   #include "diagnostic-event-id.h"
>   #include "analyzer/analyzer-logging.h"
> diff --git a/gcc/analyzer/program-point.cc b/gcc/analyzer/program-point.cc
> index ef19e6e38e5..429d6ece724 100644
> --- a/gcc/analyzer/program-point.cc
> +++ b/gcc/analyzer/program-point.cc
> @@ -24,6 +24,7 @@ along with GCC; see the file COPYING3.  If not see
>   #include "tree.h"
>   #include "gimple-pretty-print.h"
>   #include "gcc-rich-location.h"
> +#include "json.h"
>   #include "analyzer/call-string.h"
>   #include "ordered-hash-map.h"
>   #include "options.h"
> @@ -281,6 +282,43 @@ program_point::dump () const
>     pp_flush (&pp);
>   }
>
> +/* Return a new json::object of the form
> +   {"kind"  : str,
> +    "snode_idx" : int (optional), the index of the supernode,
> +    "from_edge_snode_idx" : int (only for kind=='PK_BEFORE_SUPERNODE'),
> +    "stmt_idx": int (only for kind=='PK_BEFORE_STMT',
> +    "call_string": object for the call_string}.  */
> +
> +json::object *
> +program_point::to_json () const
> +{
> +  json::object *point_obj = new json::object ();
> +
> +  point_obj->set ("kind",
> +               new json::string (point_kind_to_string (get_kind ())));
> +
> +  if (get_supernode ())
> +    point_obj->set ("snode_idx",
> +                 new json::integer_number (get_supernode ()->m_index));
> +
> +  switch (get_kind ())
> +    {
> +    default: break;
> +    case PK_BEFORE_SUPERNODE:
> +      if (const superedge *sedge = get_from_edge ())
> +     point_obj->set ("from_edge_snode_idx",
> +                     new json::integer_number (sedge->m_src->m_index));
> +      break;
> +    case PK_BEFORE_STMT:
> +      point_obj->set ("stmt_idx", new json::integer_number (get_stmt_idx ()));
> +      break;
> +    }
> +
> +  point_obj->set ("call_string", m_call_string.to_json ());
> +
> +  return point_obj;
> +}
> +
>   /* Generate a hash value for this program_point.  */
>
>   hashval_t
> diff --git a/gcc/analyzer/program-point.h b/gcc/analyzer/program-point.h
> index 97fd0a5e9de..d804621a715 100644
> --- a/gcc/analyzer/program-point.h
> +++ b/gcc/analyzer/program-point.h
> @@ -175,6 +175,8 @@ public:
>     void print_source_line (pretty_printer *pp) const;
>     void dump () const;
>
> +  json::object *to_json () const;
> +
>     hashval_t hash () const;
>     bool operator== (const program_point &other) const
>     {
> diff --git a/gcc/analyzer/program-state.cc b/gcc/analyzer/program-state.cc
> index 71bb2864d6d..83a6e5b081e 100644
> --- a/gcc/analyzer/program-state.cc
> +++ b/gcc/analyzer/program-state.cc
> @@ -25,6 +25,7 @@ along with GCC; see the file COPYING3.  If not see
>   #include "diagnostic-core.h"
>   #include "diagnostic.h"
>   #include "function.h"
> +#include "json.h"
>   #include "analyzer/analyzer.h"
>   #include "analyzer/analyzer-logging.h"
>   #include "analyzer/sm.h"
> @@ -99,6 +100,26 @@ extrinsic_state::dump () const
>     dump_to_file (stderr);
>   }
>
> +/* Return a new json::object of the form
> +   {"checkers"  : array of objects, one for each state_machine}.  */
> +
> +json::object *
> +extrinsic_state::to_json () const
> +{
> +  json::object *ext_state_obj = new json::object ();
> +
> +  {
> +    json::array *checkers_arr = new json::array ();
> +    unsigned i;
> +    state_machine *sm;
> +    FOR_EACH_VEC_ELT (m_checkers, i, sm)
> +      checkers_arr->append (sm->to_json ());
> +    ext_state_obj->set ("checkers", checkers_arr);
> +  }
> +
> +  return ext_state_obj;
> +}
> +
>   /* Get the region_model_manager for this extrinsic_state.  */
>
>   region_model_manager *
> @@ -208,6 +229,33 @@ sm_state_map::dump (bool simple) const
>     pp_flush (&pp);
>   }
>
> +/* Return a new json::object of the form
> +   {"global"  : (optional) value for global state,
> +    SVAL_DESC : value for state}.  */
> +
> +json::object *
> +sm_state_map::to_json () const
> +{
> +  json::object *map_obj = new json::object ();
> +
> +  if (m_global_state != m_sm.get_start_state ())
> +    map_obj->set ("global", m_global_state->to_json ());
> +  for (map_t::iterator iter = m_map.begin ();
> +       iter != m_map.end ();
> +       ++iter)
> +    {
> +      const svalue *sval = (*iter).first;
> +      entry_t e = (*iter).second;
> +
> +      label_text sval_desc = sval->get_desc ();
> +      map_obj->set (sval_desc.m_buffer, e.m_state->to_json ());
> +      sval_desc.maybe_free ();
> +
> +      /* This doesn't yet JSONify e.m_origin.  */
> +    }
> +  return map_obj;
> +}
> +
>   /* Return true if no states have been set within this map
>      (all expressions are for the start state).  */
>
> @@ -733,6 +781,43 @@ program_state::dump (const extrinsic_state &ext_state,
>     dump_to_file (ext_state, summarize, true, stderr);
>   }
>
> +/* Return a new json::object of the form
> +   {"store"  : object for store,
> +    "constraints" : object for constraint_manager,
> +    "curr_frame" : (optional) str for current frame,
> +    "checkers" : { STATE_NAME : object per sm_state_map },
> +    "valid" : true/false}.  */
> +
> +json::object *
> +program_state::to_json (const extrinsic_state &ext_state) const
> +{
> +  json::object *state_obj = new json::object ();
> +
> +  state_obj->set ("store", m_region_model->get_store ()->to_json ());
> +  state_obj->set ("constraints",
> +               m_region_model->get_constraints ()->to_json ());
> +  if (m_region_model->get_current_frame ())
> +    state_obj->set ("curr_frame",
> +                 m_region_model->get_current_frame ()->to_json ());
> +
> +  /* Provide m_checker_states as an object, using names as keys.  */
> +  {
> +    json::object *checkers_obj = new json::object ();
> +
> +    int i;
> +    sm_state_map *smap;
> +    FOR_EACH_VEC_ELT (m_checker_states, i, smap)
> +      if (!smap->is_empty_p ())
> +     checkers_obj->set (ext_state.get_name (i), smap->to_json ());
> +
> +    state_obj->set ("checkers", checkers_obj);
> +  }
> +
> +  state_obj->set ("valid", new json::literal (m_valid));
> +
> +  return state_obj;
> +}
> +
>   /* Update this program_state to reflect a top-level call to FUN.
>      The params will have initial_svalues.  */
>
> diff --git a/gcc/analyzer/program-state.h b/gcc/analyzer/program-state.h
> index cb0df8cd904..a52fbeb2e3e 100644
> --- a/gcc/analyzer/program-state.h
> +++ b/gcc/analyzer/program-state.h
> @@ -53,6 +53,8 @@ public:
>     void dump_to_file (FILE *outf) const;
>     void dump () const;
>
> +  json::object *to_json () const;
> +
>     engine *get_engine () const { return m_engine; }
>     region_model_manager *get_model_manager () const;
>
> @@ -109,6 +111,8 @@ public:
>             pretty_printer *pp) const;
>     void dump (bool simple) const;
>
> +  json::object *to_json () const;
> +
>     bool is_empty_p () const;
>
>     hashval_t hash () const;
> @@ -204,6 +208,8 @@ public:
>                    bool multiline, FILE *outf) const;
>     void dump (const extrinsic_state &ext_state, bool simple) const;
>
> +  json::object *to_json (const extrinsic_state &ext_state) const;
> +
>     void push_frame (const extrinsic_state &ext_state, function *fun);
>     function * get_current_function () const;
>
> diff --git a/gcc/analyzer/region-model-impl-calls.cc b/gcc/analyzer/region-model-impl-calls.cc
> index 423f74a4152..009b8c3ecb0 100644
> --- a/gcc/analyzer/region-model-impl-calls.cc
> +++ b/gcc/analyzer/region-model-impl-calls.cc
> @@ -42,6 +42,7 @@ along with GCC; see the file COPYING3.  If not see
>   #include "bitmap.h"
>   #include "selftest.h"
>   #include "function.h"
> +#include "json.h"
>   #include "analyzer/analyzer.h"
>   #include "analyzer/analyzer-logging.h"
>   #include "ordered-hash-map.h"
> diff --git a/gcc/analyzer/region-model-manager.cc b/gcc/analyzer/region-model-manager.cc
> index da8fa01077b..8dd3ad0020a 100644
> --- a/gcc/analyzer/region-model-manager.cc
> +++ b/gcc/analyzer/region-model-manager.cc
> @@ -42,6 +42,7 @@ along with GCC; see the file COPYING3.  If not see
>   #include "bitmap.h"
>   #include "selftest.h"
>   #include "function.h"
> +#include "json.h"
>   #include "analyzer/analyzer.h"
>   #include "analyzer/analyzer-logging.h"
>   #include "ordered-hash-map.h"
> diff --git a/gcc/analyzer/region-model-reachability.cc b/gcc/analyzer/region-model-reachability.cc
> index 681b8f74b19..c1b3b2db630 100644
> --- a/gcc/analyzer/region-model-reachability.cc
> +++ b/gcc/analyzer/region-model-reachability.cc
> @@ -47,6 +47,7 @@ along with GCC; see the file COPYING3.  If not see
>   #include "cgraph.h"
>   #include "cfg.h"
>   #include "digraph.h"
> +#include "json.h"
>   #include "analyzer/call-string.h"
>   #include "analyzer/program-point.h"
>   #include "analyzer/store.h"
> diff --git a/gcc/analyzer/region-model.cc b/gcc/analyzer/region-model.cc
> index 6f04904a74e..74a96b025a4 100644
> --- a/gcc/analyzer/region-model.cc
> +++ b/gcc/analyzer/region-model.cc
> @@ -42,6 +42,7 @@ along with GCC; see the file COPYING3.  If not see
>   #include "bitmap.h"
>   #include "selftest.h"
>   #include "function.h"
> +#include "json.h"
>   #include "analyzer/analyzer.h"
>   #include "analyzer/analyzer-logging.h"
>   #include "ordered-hash-map.h"
> diff --git a/gcc/analyzer/region-model.h b/gcc/analyzer/region-model.h
> index 4859df369cf..1e8a517dd8c 100644
> --- a/gcc/analyzer/region-model.h
> +++ b/gcc/analyzer/region-model.h
> @@ -256,6 +256,8 @@ public:
>     void dump (bool simple=true) const;
>     label_text get_desc (bool simple=true) const;
>
> +  json::value *to_json () const;
> +
>     virtual const region_svalue *
>     dyn_cast_region_svalue () const { return NULL; }
>     virtual const constant_svalue *
> @@ -1400,6 +1402,8 @@ public:
>     virtual void dump_to_pp (pretty_printer *pp, bool simple) const = 0;
>     void dump (bool simple) const;
>
> +  json::value *to_json () const;
> +
>     bool non_null_p () const;
>
>     static int cmp_ptrs (const void *, const void *);
> diff --git a/gcc/analyzer/region.cc b/gcc/analyzer/region.cc
> index 53f32dc912c..0820893a9b4 100644
> --- a/gcc/analyzer/region.cc
> +++ b/gcc/analyzer/region.cc
> @@ -44,6 +44,7 @@ along with GCC; see the file COPYING3.  If not see
>   #include "bitmap.h"
>   #include "selftest.h"
>   #include "function.h"
> +#include "json.h"
>   #include "analyzer/analyzer.h"
>   #include "analyzer/analyzer-logging.h"
>   #include "ordered-hash-map.h"
> @@ -460,6 +461,17 @@ region::dump (bool simple) const
>     pp_flush (&pp);
>   }
>
> +/* Return a new json::string describing the region.  */
> +
> +json::value *
> +region::to_json () const
> +{
> +  label_text desc = get_desc (true);
> +  json::value *reg_js = new json::string (desc.m_buffer);
> +  desc.maybe_free ();
> +  return reg_js;
> +}
> +
>   /* Generate a description of this region.  */
>
>   DEBUG_FUNCTION label_text
> diff --git a/gcc/analyzer/sm-file.cc b/gcc/analyzer/sm-file.cc
> index 58a0fd461fa..d2010710529 100644
> --- a/gcc/analyzer/sm-file.cc
> +++ b/gcc/analyzer/sm-file.cc
> @@ -29,6 +29,7 @@ along with GCC; see the file COPYING3.  If not see
>   #include "diagnostic-path.h"
>   #include "diagnostic-metadata.h"
>   #include "function.h"
> +#include "json.h"
>   #include "analyzer/analyzer.h"
>   #include "diagnostic-event-id.h"
>   #include "analyzer/analyzer-logging.h"
> diff --git a/gcc/analyzer/sm-malloc.cc b/gcc/analyzer/sm-malloc.cc
> index 12b2383e4a7..6293d7885cd 100644
> --- a/gcc/analyzer/sm-malloc.cc
> +++ b/gcc/analyzer/sm-malloc.cc
> @@ -30,6 +30,7 @@ along with GCC; see the file COPYING3.  If not see
>   #include "diagnostic-path.h"
>   #include "diagnostic-metadata.h"
>   #include "function.h"
> +#include "json.h"
>   #include "analyzer/analyzer.h"
>   #include "diagnostic-event-id.h"
>   #include "analyzer/analyzer-logging.h"
> diff --git a/gcc/analyzer/sm-pattern-test.cc b/gcc/analyzer/sm-pattern-test.cc
> index bb6d3b1e719..c430476a846 100644
> --- a/gcc/analyzer/sm-pattern-test.cc
> +++ b/gcc/analyzer/sm-pattern-test.cc
> @@ -31,6 +31,7 @@ along with GCC; see the file COPYING3.  If not see
>   #include "diagnostic-path.h"
>   #include "diagnostic-metadata.h"
>   #include "function.h"
> +#include "json.h"
>   #include "analyzer/analyzer.h"
>   #include "diagnostic-event-id.h"
>   #include "analyzer/analyzer-logging.h"
> diff --git a/gcc/analyzer/sm-sensitive.cc b/gcc/analyzer/sm-sensitive.cc
> index 49f9eb387b1..aec0a6ab8da 100644
> --- a/gcc/analyzer/sm-sensitive.cc
> +++ b/gcc/analyzer/sm-sensitive.cc
> @@ -31,6 +31,7 @@ along with GCC; see the file COPYING3.  If not see
>   #include "diagnostic-path.h"
>   #include "diagnostic-metadata.h"
>   #include "function.h"
> +#include "json.h"
>   #include "analyzer/analyzer.h"
>   #include "diagnostic-event-id.h"
>   #include "analyzer/analyzer-logging.h"
> diff --git a/gcc/analyzer/sm-signal.cc b/gcc/analyzer/sm-signal.cc
> index bf6ea480423..2e05de8a2de 100644
> --- a/gcc/analyzer/sm-signal.cc
> +++ b/gcc/analyzer/sm-signal.cc
> @@ -32,6 +32,7 @@ along with GCC; see the file COPYING3.  If not see
>   #include "diagnostic-path.h"
>   #include "diagnostic-metadata.h"
>   #include "function.h"
> +#include "json.h"
>   #include "analyzer/analyzer.h"
>   #include "diagnostic-event-id.h"
>   #include "analyzer/analyzer-logging.h"
> @@ -220,6 +221,12 @@ public:
>       pp_string (pp, "signal delivered");
>     }
>
> +  json::object *to_json () const
> +  {
> +    json::object *custom_obj = new json::object ();
> +    return custom_obj;
> +  }
> +
>     void update_model (region_model *model,
>                    const exploded_edge &eedge) FINAL OVERRIDE
>     {
> diff --git a/gcc/analyzer/sm-taint.cc b/gcc/analyzer/sm-taint.cc
> index 49bbd6dfb13..37491d8a49a 100644
> --- a/gcc/analyzer/sm-taint.cc
> +++ b/gcc/analyzer/sm-taint.cc
> @@ -31,6 +31,7 @@ along with GCC; see the file COPYING3.  If not see
>   #include "diagnostic-path.h"
>   #include "diagnostic-metadata.h"
>   #include "function.h"
> +#include "json.h"
>   #include "analyzer/analyzer.h"
>   #include "diagnostic-event-id.h"
>   #include "analyzer/analyzer-logging.h"
> diff --git a/gcc/analyzer/sm.cc b/gcc/analyzer/sm.cc
> index a333063c65e..3fe75ef8c4a 100644
> --- a/gcc/analyzer/sm.cc
> +++ b/gcc/analyzer/sm.cc
> @@ -29,6 +29,9 @@ along with GCC; see the file COPYING3.  If not see
>   #include "function.h"
>   #include "diagnostic-core.h"
>   #include "pretty-print.h"
> +#include "diagnostic.h"
> +#include "tree-diagnostic.h"
> +#include "json.h"
>   #include "analyzer/analyzer.h"
>   #include "analyzer/analyzer-logging.h"
>   #include "analyzer/sm.h"
> @@ -56,6 +59,17 @@ state_machine::state::dump_to_pp (pretty_printer *pp) const
>     pp_string (pp, m_name);
>   }
>
> +/* Return a new json::string describing the state.  */
> +
> +json::value *
> +state_machine::state::to_json () const
> +{
> +  pretty_printer pp;
> +  pp_format_decoder (&pp) = default_tree_printer;
> +  dump_to_pp (&pp);
> +  return new json::string (pp_formatted_text (&pp));
> +}
> +
>   /* class state_machine.  */
>
>   /* state_machine's ctor.  */
> @@ -109,6 +123,28 @@ state_machine::dump_to_pp (pretty_printer *pp) const
>       }
>   }
>
> +/* Return a new json::object of the form
> +   {"name" : str,
> +    "states" : [str]}.  */
> +
> +json::object *
> +state_machine::to_json () const
> +{
> +  json::object *sm_obj = new json::object ();
> +
> +  sm_obj->set ("name", new json::string (m_name));
> +  {
> +    json::array *states_arr = new json::array ();
> +    unsigned i;
> +    state *s;
> +    FOR_EACH_VEC_ELT (m_states, i, s)
> +      states_arr->append (s->to_json ());
> +    sm_obj->set ("states", states_arr);
> +  }
> +
> +  return sm_obj;
> +}
> +
>   /* Create instances of the various state machines, each using LOGGER,
>      and populate OUT with them.  */
>
> diff --git a/gcc/analyzer/sm.h b/gcc/analyzer/sm.h
> index f44ad922200..46b93ffb790 100644
> --- a/gcc/analyzer/sm.h
> +++ b/gcc/analyzer/sm.h
> @@ -48,6 +48,7 @@ public:
>
>       const char *get_name () const { return m_name; }
>       virtual void dump_to_pp (pretty_printer *pp) const;
> +    virtual json::value *to_json () const;
>
>       unsigned get_id () const { return m_id; }
>
> @@ -121,6 +122,8 @@ public:
>
>     void dump_to_pp (pretty_printer *pp) const;
>
> +  json::object *to_json () const;
> +
>     state_t get_start_state () const { return m_start; }
>
>   protected:
> diff --git a/gcc/analyzer/state-purge.cc b/gcc/analyzer/state-purge.cc
> index d5a24b48e1e..e4942a692fa 100644
> --- a/gcc/analyzer/state-purge.cc
> +++ b/gcc/analyzer/state-purge.cc
> @@ -37,6 +37,7 @@ along with GCC; see the file COPYING3.  If not see
>   #include "diagnostic-core.h"
>   #include "gimple-pretty-print.h"
>   #include "function.h"
> +#include "json.h"
>   #include "analyzer/analyzer.h"
>   #include "analyzer/call-string.h"
>   #include "digraph.h"
> diff --git a/gcc/analyzer/store.cc b/gcc/analyzer/store.cc
> index 1348895e5c7..11585123561 100644
> --- a/gcc/analyzer/store.cc
> +++ b/gcc/analyzer/store.cc
> @@ -42,6 +42,7 @@ along with GCC; see the file COPYING3.  If not see
>   #include "bitmap.h"
>   #include "selftest.h"
>   #include "function.h"
> +#include "json.h"
>   #include "analyzer/analyzer.h"
>   #include "analyzer/analyzer-logging.h"
>   #include "ordered-hash-map.h"
> @@ -122,6 +123,17 @@ binding_key::dump (bool simple) const
>     pp_flush (&pp);
>   }
>
> +/* Get a description of this binding_key.  */
> +
> +label_text
> +binding_key::get_desc (bool simple) const
> +{
> +  pretty_printer pp;
> +  pp_format_decoder (&pp) = default_tree_printer;
> +  dump_to_pp (&pp, simple);
> +  return label_text::take (xstrdup (pp_formatted_text (&pp)));
> +}
> +
>   /* qsort callback.  */
>
>   int
> @@ -366,6 +378,37 @@ binding_map::dump (bool simple) const
>     pp_flush (&pp);
>   }
>
> +/* Return a new json::object of the form
> +   {KEY_DESC : SVALUE_DESC,
> +    ...for the various key/value pairs in this binding_map}.  */
> +
> +json::object *
> +binding_map::to_json () const
> +{
> +  json::object *map_obj = new json::object ();
> +
> +  auto_vec <const binding_key *> binding_keys;
> +  for (map_t::iterator iter = m_map.begin ();
> +       iter != m_map.end (); ++iter)
> +    {
> +      const binding_key *key = (*iter).first;
> +      binding_keys.safe_push (key);
> +    }
> +  binding_keys.qsort (binding_key::cmp_ptrs);
> +
> +  const binding_key *key;
> +  unsigned i;
> +  FOR_EACH_VEC_ELT (binding_keys, i, key)
> +    {
> +      const svalue *value = *const_cast <map_t &> (m_map).get (key);
> +      label_text key_desc = key->get_desc ();
> +      map_obj->set (key_desc.m_buffer, value->to_json ());
> +      key_desc.maybe_free ();
> +    }
> +
> +  return map_obj;
> +}
> +
>   /* Get the child region of PARENT_REG based upon INDEX within a
>      CONSTRUCTOR.   */
>
> @@ -657,6 +700,23 @@ binding_cluster::dump (bool simple) const
>     pp_flush (&pp);
>   }
>
> +/* Return a new json::object of the form
> +   {"escaped": true/false,
> +    "touched": true/false,
> +    "map" : object for the the binding_map.  */
> +
> +json::object *
> +binding_cluster::to_json () const
> +{
> +  json::object *cluster_obj = new json::object ();
> +
> +  cluster_obj->set ("escaped", new json::literal (m_escaped));
> +  cluster_obj->set ("touched", new json::literal (m_touched));
> +  cluster_obj->set ("map", m_map.to_json ());
> +
> +  return cluster_obj;
> +}
> +
>   /* Add a binding of SVAL of kind KIND to REG, unpacking SVAL if it is a
>      compound_sval.  */
>
> @@ -1575,6 +1635,64 @@ store::dump (bool simple) const
>     pp_flush (&pp);
>   }
>
> +/* Return a new json::object of the form
> +   {PARENT_REGION_DESC: {BASE_REGION_DESC: object for binding_map,
> +                      ... for each cluster within parent region},
> +    ...for each parent region,
> +    "called_unknown_function": true/false}.  */
> +
> +json::object *
> +store::to_json () const
> +{
> +  json::object *store_obj = new json::object ();
> +
> +  /* Sort into some deterministic order.  */
> +  auto_vec<const region *> base_regions;
> +  for (cluster_map_t::iterator iter = m_cluster_map.begin ();
> +       iter != m_cluster_map.end (); ++iter)
> +    {
> +      const region *base_reg = (*iter).first;
> +      base_regions.safe_push (base_reg);
> +    }
> +  base_regions.qsort (region::cmp_ptrs);
> +
> +  /* Gather clusters, organize by parent region, so that we can group
> +     together locals, globals, etc.  */
> +  auto_vec<const region *> parent_regions;
> +  get_sorted_parent_regions (&parent_regions, base_regions);
> +
> +  const region *parent_reg;
> +  unsigned i;
> +  FOR_EACH_VEC_ELT (parent_regions, i, parent_reg)
> +    {
> +      gcc_assert (parent_reg);
> +
> +      json::object *clusters_in_parent_reg_obj = new json::object ();
> +
> +      const region *base_reg;
> +      unsigned j;
> +      FOR_EACH_VEC_ELT (base_regions, j, base_reg)
> +     {
> +       /* This is O(N * M), but N ought to be small.  */
> +       if (base_reg->get_parent_region () != parent_reg)
> +         continue;
> +       binding_cluster *cluster
> +         = *const_cast<cluster_map_t &> (m_cluster_map).get (base_reg);
> +       label_text base_reg_desc = base_reg->get_desc ();
> +       clusters_in_parent_reg_obj->set (base_reg_desc.m_buffer,
> +                                        cluster->to_json ());
> +       base_reg_desc.maybe_free ();
> +     }
> +      label_text parent_reg_desc = parent_reg->get_desc ();
> +      store_obj->set (parent_reg_desc.m_buffer, clusters_in_parent_reg_obj);
> +      parent_reg_desc.maybe_free ();
> +    }
> +
> +  store_obj->set ("called_unknown_fn", new json::literal (m_called_unknown_fn));
> +
> +  return store_obj;
> +}
> +
>   /* Get any svalue bound to REG, or NULL.  */
>
>   const svalue *
> diff --git a/gcc/analyzer/store.h b/gcc/analyzer/store.h
> index 83a43107b21..0f4e7ab2a56 100644
> --- a/gcc/analyzer/store.h
> +++ b/gcc/analyzer/store.h
> @@ -159,6 +159,7 @@ public:
>
>     virtual void dump_to_pp (pretty_printer *pp, bool simple) const;
>     void dump (bool simple) const;
> +  label_text get_desc (bool simple=true) const;
>
>     static int cmp_ptrs (const void *, const void *);
>     static int cmp (const binding_key *, const binding_key *);
> @@ -340,6 +341,8 @@ public:
>     void dump_to_pp (pretty_printer *pp, bool simple, bool multiline) const;
>     void dump (bool simple) const;
>
> +  json::object *to_json () const;
> +
>     bool apply_ctor_to_region (const region *parent_reg, tree ctor,
>                            region_model_manager *mgr);
>
> @@ -392,6 +395,8 @@ public:
>     void dump_to_pp (pretty_printer *pp, bool simple, bool multiline) const;
>     void dump (bool simple) const;
>
> +  json::object *to_json () const;
> +
>     void bind (store_manager *mgr, const region *, const svalue *,
>            binding_kind kind);
>
> @@ -517,6 +522,8 @@ public:
>     void dump (bool simple) const;
>     void summarize_to_pp (pretty_printer *pp, bool simple) const;
>
> +  json::object *to_json () const;
> +
>     const svalue *get_direct_binding (store_manager *mgr, const region *reg);
>     const svalue *get_default_binding (store_manager *mgr, const region *reg);
>     const svalue *get_any_binding (store_manager *mgr, const region *reg) const;
> diff --git a/gcc/analyzer/supergraph.cc b/gcc/analyzer/supergraph.cc
> index 7c6fed3a060..735c4a30e09 100644
> --- a/gcc/analyzer/supergraph.cc
> +++ b/gcc/analyzer/supergraph.cc
> @@ -43,6 +43,7 @@ along with GCC; see the file COPYING3.  If not see
>   #include "tree-dfa.h"
>   #include "cfganal.h"
>   #include "function.h"
> +#include "json.h"
>   #include "analyzer/analyzer.h"
>   #include "ordered-hash-map.h"
>   #include "options.h"
> @@ -374,6 +375,38 @@ supergraph::dump_dot (const char *path, const dump_args_t &dump_args) const
>     fclose (fp);
>   }
>
> +/* Return a new json::object of the form
> +   {"nodes" : [objs for snodes],
> +    "edges" : [objs for sedges]}.  */
> +
> +json::object *
> +supergraph::to_json () const
> +{
> +  json::object *sgraph_obj = new json::object ();
> +
> +  /* Nodes.  */
> +  {
> +    json::array *nodes_arr = new json::array ();
> +    unsigned i;
> +    supernode *n;
> +    FOR_EACH_VEC_ELT (m_nodes, i, n)
> +      nodes_arr->append (n->to_json ());
> +    sgraph_obj->set ("nodes", nodes_arr);
> +  }
> +
> +  /* Edges.  */
> +  {
> +    json::array *edges_arr = new json::array ();
> +    unsigned i;
> +    superedge *n;
> +    FOR_EACH_VEC_ELT (m_edges, i, n)
> +      edges_arr->append (n->to_json ());
> +    sgraph_obj->set ("edges", edges_arr);
> +  }
> +
> +  return sgraph_obj;
> +}
> +
>   /* Create a supernode for BB within FUN and add it to this supergraph.
>
>      If RETURNING_CALL is non-NULL, the supernode represents the resumption
> @@ -594,6 +627,63 @@ supernode::dump_dot_id (pretty_printer *pp) const
>     pp_printf (pp, "node_%i", m_index);
>   }
>
> +/* Return a new json::object of the form
> +   {"idx": int,
> +    "bb_idx": int,
> +    "m_returning_call": optional str,
> +    "phis": [str],
> +    "stmts" : [str]}.  */
> +
> +json::object *
> +supernode::to_json () const
> +{
> +  json::object *snode_obj = new json::object ();
> +
> +  snode_obj->set ("idx", new json::integer_number (m_index));
> +  snode_obj->set ("bb_idx", new json::integer_number (m_bb->index));
> +
> +  if (m_returning_call)
> +    {
> +      pretty_printer pp;
> +      pp_format_decoder (&pp) = default_tree_printer;
> +      pp_gimple_stmt_1 (&pp, m_returning_call, 0, (dump_flags_t)0);
> +      snode_obj->set ("returning_call",
> +                   new json::string (pp_formatted_text (&pp)));
> +    }
> +
> +  /* Phi nodes.  */
> +  {
> +    json::array *phi_arr = new json::array ();
> +    for (gphi_iterator gpi = const_cast<supernode *> (this)->start_phis ();
> +      !gsi_end_p (gpi); gsi_next (&gpi))
> +      {
> +     const gimple *stmt = gsi_stmt (gpi);
> +     pretty_printer pp;
> +     pp_format_decoder (&pp) = default_tree_printer;
> +     pp_gimple_stmt_1 (&pp, stmt, 0, (dump_flags_t)0);
> +     phi_arr->append (new json::string (pp_formatted_text (&pp)));
> +      }
> +    snode_obj->set ("phis", phi_arr);
> +  }
> +
> +  /* Statements.  */
> +  {
> +    json::array *stmt_arr = new json::array ();
> +    int i;
> +    gimple *stmt;
> +    FOR_EACH_VEC_ELT (m_stmts, i, stmt)
> +      {
> +     pretty_printer pp;
> +     pp_format_decoder (&pp) = default_tree_printer;
> +     pp_gimple_stmt_1 (&pp, stmt, 0, (dump_flags_t)0);
> +     stmt_arr->append (new json::string (pp_formatted_text (&pp)));
> +      }
> +    snode_obj->set ("stmts", stmt_arr);
> +  }
> +
> +  return snode_obj;
> +}
> +
>   /* Get a location_t for the start of this supernode.  */
>
>   location_t
> @@ -759,6 +849,28 @@ superedge::dump_dot (graphviz_out *gv, const dump_args_t &) const
>     pp_printf (pp, "\"];\n");
>   }
>
> +/* Return a new json::object of the form
> +   {"src_idx": int, the index of the source supernode,
> +    "dst_idx": int, the index of the destination supernode,
> +    "desc"   : str.  */
> +
> +json::object *
> +superedge::to_json () const
> +{
> +  json::object *sedge_obj = new json::object ();
> +  sedge_obj->set ("src_idx", new json::integer_number (m_src->m_index));
> +  sedge_obj->set ("dst_idx", new json::integer_number (m_dest->m_index));
> +
> +  {
> +    pretty_printer pp;
> +    pp_format_decoder (&pp) = default_tree_printer;
> +    dump_label_to_pp (&pp, false);
> +    sedge_obj->set ("desc", new json::string (pp_formatted_text (&pp)));
> +  }
> +
> +  return sedge_obj;
> +}
> +
>   /* If this is an intraprocedural superedge, return the associated
>      CFG edge.  Otherwise, return NULL.  */
>
> diff --git a/gcc/analyzer/supergraph.h b/gcc/analyzer/supergraph.h
> index c25043d92fd..40ae9ff8ac2 100644
> --- a/gcc/analyzer/supergraph.h
> +++ b/gcc/analyzer/supergraph.h
> @@ -148,6 +148,8 @@ public:
>     void dump_dot_to_file (FILE *fp, const dump_args_t &) const;
>     void dump_dot (const char *path, const dump_args_t &) const;
>
> +  json::object *to_json () const;
> +
>     int num_nodes () const { return m_nodes.length (); }
>     int num_edges () const { return m_edges.length (); }
>
> @@ -231,6 +233,8 @@ class supernode : public dnode<supergraph_traits>
>     void dump_dot (graphviz_out *gv, const dump_args_t &args) const OVERRIDE;
>     void dump_dot_id (pretty_printer *pp) const;
>
> +  json::object *to_json () const;
> +
>     location_t get_start_location () const;
>     location_t get_end_location () const;
>
> @@ -289,6 +293,8 @@ class superedge : public dedge<supergraph_traits>
>     virtual void dump_label_to_pp (pretty_printer *pp,
>                                bool user_facing) const = 0;
>
> +  json::object *to_json () const;
> +
>     enum edge_kind get_kind () const { return m_kind; }
>
>     virtual cfg_superedge *dyn_cast_cfg_superedge () { return NULL; }
> diff --git a/gcc/analyzer/svalue.cc b/gcc/analyzer/svalue.cc
> index fcab578674e..ae3b6783e9c 100644
> --- a/gcc/analyzer/svalue.cc
> +++ b/gcc/analyzer/svalue.cc
> @@ -42,6 +42,7 @@ along with GCC; see the file COPYING3.  If not see
>   #include "bitmap.h"
>   #include "selftest.h"
>   #include "function.h"
> +#include "json.h"
>   #include "analyzer/analyzer.h"
>   #include "analyzer/analyzer-logging.h"
>   #include "options.h"
> @@ -116,6 +117,17 @@ svalue::get_desc (bool simple) const
>     return label_text::take (xstrdup (pp_formatted_text (&pp)));
>   }
>
> +/* Return a new json::string describing the svalue.  */
> +
> +json::value *
> +svalue::to_json () const
> +{
> +  label_text desc = get_desc (true);
> +  json::value *sval_js = new json::string (desc.m_buffer);
> +  desc.maybe_free ();
> +  return sval_js;
> +}
> +
>   /* If this svalue is a constant_svalue, return the underlying tree constant.
>      Otherwise return NULL_TREE.  */
>
> diff --git a/gcc/doc/analyzer.texi b/gcc/doc/analyzer.texi
> index 92c12e19401..6b7d70cccaa 100644
> --- a/gcc/doc/analyzer.texi
> +++ b/gcc/doc/analyzer.texi
> @@ -488,6 +488,9 @@ truthfulness of the argument.  This is useful for writing DejaGnu tests.
>
>   @subsection Other Debugging Techniques
>
> +The option @option{-fdump-analyzer-json} will dump both the supergraph
> +and the exploded graph in compressed JSON form.
> +
>   One approach when tracking down where a particular bogus state is
>   introduced into the @code{exploded_graph} is to add custom code to
>   @code{program_state::validate}.
> diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
> index 8be2b4f4de6..49b5348c606 100644
> --- a/gcc/doc/invoke.texi
> +++ b/gcc/doc/invoke.texi
> @@ -406,6 +406,7 @@ Objective-C and Objective-C++ Dialects}.
>   -fdump-analyzer-exploded-nodes @gol
>   -fdump-analyzer-exploded-nodes-2 @gol
>   -fdump-analyzer-exploded-nodes-3 @gol
> +-fdump-analyzer-json @gol
>   -fdump-analyzer-state-purge @gol
>   -fdump-analyzer-supergraph @gol
>   -Wno-analyzer-double-fclose @gol
> @@ -9103,6 +9104,12 @@ Dump a textual representation of the ``exploded graph'' to
>   one dump file per node, to @file{@var{file}.eg-@var{id}.txt}.
>   This is typically a large number of dump files.
>
> +@item -fdump-analyzer-json
> +@opindex fdump-analyzer-json
> +Dump a compressed JSON representation of analyzer internals to
> +@file{@var{file}.analyzer.json.gz}.  The precise format is subject
> +to change.
> +
>   @item -fdump-analyzer-state-purge
>   @opindex fdump-analyzer-state-purge
>   As per @option{-fdump-analyzer-supergraph}, dump a representation of the
-----------------
Mentor Graphics (Deutschland) GmbH, Arnulfstraße 201, 80634 München / Germany
Registergericht München HRB 106955, Geschäftsführer: Thomas Heurung, Alexander Walter
diff mbox series

Patch

diff --git a/gcc/analyzer/analysis-plan.cc b/gcc/analyzer/analysis-plan.cc
index 3c8b10b3314..7e48f526094 100644
--- a/gcc/analyzer/analysis-plan.cc
+++ b/gcc/analyzer/analysis-plan.cc
@@ -27,6 +27,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "timevar.h"
 #include "ipa-utils.h"
 #include "function.h"
+#include "json.h"
 #include "analyzer/analyzer.h"
 #include "diagnostic-core.h"
 #include "analyzer/analyzer-logging.h"
diff --git a/gcc/analyzer/analyzer.opt b/gcc/analyzer/analyzer.opt
index 94a686db6b3..872fb31048e 100644
--- a/gcc/analyzer/analyzer.opt
+++ b/gcc/analyzer/analyzer.opt
@@ -186,6 +186,10 @@  fdump-analyzer-exploded-nodes-3
 Common RejectNegative Var(flag_dump_analyzer_exploded_nodes_3)
 Dump a textual representation of the exploded graph to SRCFILE.eg-ID.txt.
 
+fdump-analyzer-json
+Common RejectNegative Var(flag_dump_analyzer_json)
+Dump analyzer-specific data to a SRCFILE.analyzer.json.gz file.
+
 fdump-analyzer-state-purge
 Common RejectNegative Var(flag_dump_analyzer_state_purge)
 Dump state-purging information to a SRCFILE.state-purge.dot file.
diff --git a/gcc/analyzer/call-string.cc b/gcc/analyzer/call-string.cc
index d3630316c4e..72568c6efa7 100644
--- a/gcc/analyzer/call-string.cc
+++ b/gcc/analyzer/call-string.cc
@@ -24,6 +24,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "pretty-print.h"
 #include "tree.h"
 #include "options.h"
+#include "json.h"
 #include "analyzer/call-string.h"
 #include "ordered-hash-map.h"
 #include "options.h"
@@ -104,6 +105,34 @@  call_string::print (pretty_printer *pp) const
   pp_string (pp, "]");
 }
 
+/* Return a new json::array of the form
+   [{"src_snode_idx" : int,
+     "dst_snode_idx" : int,
+     "funcname" : str},
+     ...for each return_superedge in the callstring].  */
+
+json::value *
+call_string::to_json () const
+{
+  json::array *arr = new json::array ();
+
+  const return_superedge *e;
+  int i;
+  FOR_EACH_VEC_ELT (m_return_edges, i, e)
+    {
+      json::object *e_obj = new json::object ();
+      e_obj->set ("src_snode_idx",
+		  new json::integer_number (e->m_src->m_index));
+      e_obj->set ("dst_snode_idx",
+		  new json::integer_number (e->m_dest->m_index));
+      e_obj->set ("funcname",
+		  new json::string (function_name (e->m_dest->m_fun)));
+      arr->append (e_obj);
+    }
+
+  return arr;
+}
+
 /* Generate a hash value for this call_string.  */
 
 hashval_t
diff --git a/gcc/analyzer/call-string.h b/gcc/analyzer/call-string.h
index 1b5db0a4a20..5a03c592ecb 100644
--- a/gcc/analyzer/call-string.h
+++ b/gcc/analyzer/call-string.h
@@ -47,6 +47,8 @@  public:
 
   void print (pretty_printer *pp) const;
 
+  json::value *to_json () const;
+
   hashval_t hash () const;
 
   bool empty_p () const { return m_return_edges.is_empty (); }
diff --git a/gcc/analyzer/checker-path.cc b/gcc/analyzer/checker-path.cc
index c28131651c6..1f6d6a86001 100644
--- a/gcc/analyzer/checker-path.cc
+++ b/gcc/analyzer/checker-path.cc
@@ -38,6 +38,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "fibonacci_heap.h"
 #include "diagnostic-event-id.h"
 #include "shortest-paths.h"
+#include "json.h"
 #include "analyzer/analyzer.h"
 #include "analyzer/analyzer-logging.h"
 #include "analyzer/sm.h"
diff --git a/gcc/analyzer/constraint-manager.cc b/gcc/analyzer/constraint-manager.cc
index 521501fd4f4..5cd2c9e0e0b 100644
--- a/gcc/analyzer/constraint-manager.cc
+++ b/gcc/analyzer/constraint-manager.cc
@@ -31,6 +31,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "diagnostic-core.h"
 #include "graphviz.h"
 #include "function.h"
+#include "json.h"
 #include "analyzer/analyzer.h"
 #include "ordered-hash-map.h"
 #include "options.h"
@@ -299,6 +300,33 @@  equiv_class::print (pretty_printer *pp) const
   pp_character (pp, '}');
 }
 
+/* Return a new json::object of the form
+   {"svals" : [str],
+    "constant" : optional str}.  */
+
+json::object *
+equiv_class::to_json () const
+{
+  json::object *ec_obj = new json::object ();
+
+  json::array *sval_arr = new json::array ();
+  int i;
+  const svalue *sval;
+  FOR_EACH_VEC_ELT (m_vars, i, sval)
+    sval_arr->append (sval->to_json ());
+  ec_obj->set ("svals", sval_arr);
+
+  if (m_constant)
+    {
+      pretty_printer pp;
+      pp_format_decoder (&pp) = default_tree_printer;
+      pp_printf (&pp, "%qE", m_constant);
+      ec_obj->set ("constant", new json::string (pp_formatted_text (&pp)));
+    }
+
+  return ec_obj;
+}
+
 /* Generate a hash value for this equiv_class.
    This relies on the ordering of m_vars, and so this object needs to
    have been canonicalized for this to be meaningful.  */
@@ -499,6 +527,23 @@  constraint::print (pretty_printer *pp, const constraint_manager &cm) const
   m_rhs.get_obj (cm).print (pp);
 }
 
+/* Return a new json::object of the form
+   {"lhs" : int, the EC index
+    "op"  : str,
+    "rhs" : int, the EC index}.  */
+
+json::object *
+constraint::to_json () const
+{
+  json::object *con_obj = new json::object ();
+
+  con_obj->set ("lhs", new json::integer_number (m_lhs.as_int ()));
+  con_obj->set ("op", new json::string (constraint_op_code (m_op)));
+  con_obj->set ("rhs", new json::integer_number (m_rhs.as_int ()));
+
+  return con_obj;
+}
+
 /* Generate a hash value for this constraint.  */
 
 hashval_t
@@ -768,6 +813,38 @@  debug (const constraint_manager &cm)
   cm.dump ();
 }
 
+/* Return a new json::object of the form
+   {"ecs" : array of objects, one per equiv_class
+    "constraints" : array of objects, one per constraint}.  */
+
+json::object *
+constraint_manager::to_json () const
+{
+  json::object *cm_obj = new json::object ();
+
+  /* Equivalence classes.  */
+  {
+    json::array *ec_arr = new json::array ();
+    int i;
+    equiv_class *ec;
+    FOR_EACH_VEC_ELT (m_equiv_classes, i, ec)
+      ec_arr->append (ec->to_json ());
+    cm_obj->set ("ecs", ec_arr);
+  }
+
+  /* Constraints.  */
+  {
+    json::array *con_arr = new json::array ();
+    int i;
+    constraint *c;
+    FOR_EACH_VEC_ELT (m_constraints, i, c)
+      con_arr->append (c->to_json ());
+    cm_obj->set ("constraints", con_arr);
+  }
+
+  return cm_obj;
+}
+
 /* Attempt to add the constraint LHS OP RHS to this constraint_manager.
    Return true if the constraint could be added (or is already true).
    Return false if the constraint contradicts existing knowledge.  */
diff --git a/gcc/analyzer/constraint-manager.h b/gcc/analyzer/constraint-manager.h
index 3c31a891f70..98960ffad84 100644
--- a/gcc/analyzer/constraint-manager.h
+++ b/gcc/analyzer/constraint-manager.h
@@ -88,6 +88,8 @@  public:
 
   void print (pretty_printer *pp) const;
 
+  json::object *to_json () const;
+
   /* An equivalence class can contain multiple constants (e.g. multiple
      different zeroes, for different types); these are just for the last
      constant added.  */
@@ -160,6 +162,8 @@  class constraint
 
   void print (pretty_printer *pp, const constraint_manager &cm) const;
 
+  json::object *to_json () const;
+
   hashval_t hash () const;
   bool operator== (const constraint &other) const;
 
@@ -215,6 +219,8 @@  public:
   void dump (FILE *fp) const;
   void dump () const;
 
+  json::object *to_json () const;
+
   const equiv_class &get_equiv_class_by_index (unsigned idx) const
   {
     return *m_equiv_classes[idx];
diff --git a/gcc/analyzer/diagnostic-manager.cc b/gcc/analyzer/diagnostic-manager.cc
index 4a95d4c569e..8d7e5084cc0 100644
--- a/gcc/analyzer/diagnostic-manager.cc
+++ b/gcc/analyzer/diagnostic-manager.cc
@@ -37,6 +37,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "tristate.h"
 #include "selftest.h"
 #include "ordered-hash-map.h"
+#include "json.h"
 #include "analyzer/analyzer.h"
 #include "analyzer/analyzer-logging.h"
 #include "analyzer/sm.h"
@@ -114,6 +115,43 @@  saved_diagnostic::operator== (const saved_diagnostic &other) const
 	  && m_trailing_eedge == other.m_trailing_eedge);
 }
 
+/* Return a new json::object of the form
+   {"sm": optional str,
+    "enode": int,
+    "snode": int,
+    "sval": optional str,
+    "state": optional str,
+    "path_length": int,
+    "pending_diagnostic": str}.  */
+
+json::object *
+saved_diagnostic::to_json () const
+{
+  json::object *sd_obj = new json::object ();
+
+  if (m_sm)
+    sd_obj->set ("sm", new json::string (m_sm->get_name ()));
+  sd_obj->set ("enode", new json::integer_number (m_enode->m_index));
+  sd_obj->set ("snode", new json::integer_number (m_snode->m_index));
+  if (m_sval)
+    sd_obj->set ("sval", m_sval->to_json ());
+  if (m_state)
+    sd_obj->set ("state", m_state->to_json ());
+  sd_obj->set ("path_length", new json::integer_number (m_epath_length));
+  sd_obj->set ("pending_diagnostic", new json::string (m_d->get_kind ()));
+
+  /* We're not yet JSONifying the following fields:
+     const gimple *m_stmt;
+     stmt_finder *m_stmt_finder;
+     tree m_var;
+     exploded_edge *m_trailing_eedge;
+     enum status m_status;
+     feasibility_problem *m_problem;
+  */
+
+  return sd_obj;
+}
+
 /* State for building a checker_path from a particular exploded_path.
    In particular, this precomputes reachability information: the set of
    source enodes for which a path be found to the diagnostic enode.  */
@@ -199,6 +237,26 @@  diagnostic_manager::add_diagnostic (const exploded_node *enode,
   add_diagnostic (NULL, enode, snode, stmt, finder, NULL_TREE, NULL, 0, d);
 }
 
+/* Return a new json::object of the form
+   {"diagnostics"  : [obj for saved_diagnostic]}.  */
+
+json::object *
+diagnostic_manager::to_json () const
+{
+  json::object *dm_obj = new json::object ();
+
+  {
+    json::array *sd_arr = new json::array ();
+    int i;
+    saved_diagnostic *sd;
+    FOR_EACH_VEC_ELT (m_saved_diagnostics, i, sd)
+      sd_arr->append (sd->to_json ());
+    dm_obj->set ("diagnostics", sd_arr);
+  }
+
+  return dm_obj;
+}
+
 /* A class for identifying sets of duplicated pending_diagnostic.
 
    We want to find the simplest dedupe_candidate amongst those that share a
diff --git a/gcc/analyzer/diagnostic-manager.h b/gcc/analyzer/diagnostic-manager.h
index 1e310f7fe15..c32f0c47dc1 100644
--- a/gcc/analyzer/diagnostic-manager.h
+++ b/gcc/analyzer/diagnostic-manager.h
@@ -46,6 +46,8 @@  public:
 
   bool operator== (const saved_diagnostic &other) const;
 
+  json::object *to_json () const;
+
   void set_feasible ()
   {
     gcc_assert (m_status == STATUS_NEW);
@@ -105,6 +107,8 @@  public:
 
   engine *get_engine () const { return m_eng; }
 
+  json::object *to_json () const;
+
   void add_diagnostic (const state_machine *sm,
 		       const exploded_node *enode,
 		       const supernode *snode, const gimple *stmt,
diff --git a/gcc/analyzer/engine.cc b/gcc/analyzer/engine.cc
index d03e23a9b6e..df7e33564f1 100644
--- a/gcc/analyzer/engine.cc
+++ b/gcc/analyzer/engine.cc
@@ -37,6 +37,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "tristate.h"
 #include "ordered-hash-map.h"
 #include "selftest.h"
+#include "json.h"
 #include "analyzer/analyzer.h"
 #include "analyzer/analyzer-logging.h"
 #include "analyzer/call-string.h"
@@ -61,6 +62,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "analyzer/checker-path.h"
 #include "analyzer/state-purge.h"
 #include "analyzer/bar-chart.h"
+#include <zlib.h>
 
 /* For an overview, see gcc/doc/analyzer.texi.  */
 
@@ -764,6 +766,19 @@  eg_traits::dump_args_t::show_enode_details_p (const exploded_node &enode) const
 
 /* class exploded_node : public dnode<eg_traits>.  */
 
+const char *
+exploded_node::status_to_str (enum status s)
+{
+  switch (s)
+    {
+    default: gcc_unreachable ();
+    case STATUS_WORKLIST: return "WORKLIST";
+    case STATUS_PROCESSED: return "PROCESSED";
+    case STATUS_MERGER: return "MERGER";
+    case STATUS_BULK_MERGED: return "BULK_MERGED";
+    }
+}
+
 /* exploded_node's ctor.  */
 
 exploded_node::exploded_node (const point_and_state &ps,
@@ -952,6 +967,28 @@  exploded_node::dump (const extrinsic_state &ext_state) const
   dump (stderr, ext_state);
 }
 
+/* Return a new json::object of the form
+   {"point"  : object for program_point,
+    "state"  : object for program_state,
+    "status" : str,
+    "idx"    : int,
+    "processed_stmts" : int}.  */
+
+json::object *
+exploded_node::to_json (const extrinsic_state &ext_state) const
+{
+  json::object *enode_obj = new json::object ();
+
+  enode_obj->set ("point", get_point ().to_json ());
+  enode_obj->set ("state", get_state ().to_json (ext_state));
+  enode_obj->set ("status", new json::string (status_to_str (m_status)));
+  enode_obj->set ("idx", new json::integer_number (m_index));
+  enode_obj->set ("processed_stmts",
+		  new json::integer_number (m_num_processed_stmts));
+
+  return enode_obj;
+}
+
 } // namespace ana
 
 /* Return true if FNDECL has a gimple body.  */
@@ -1502,6 +1539,30 @@  exploded_edge::dump_dot (graphviz_out *gv, const dump_args_t &) const
   pp_printf (pp, "\"];\n");
 }
 
+/* Return a new json::object of the form
+   {"src_idx": int, the index of the source exploded edge,
+    "dst_idx": int, the index of the destination exploded edge,
+    "sedge": (optional) object for the superedge, if any,
+    "custom": (optional) str, a description, if this is a custom edge}.  */
+
+json::object *
+exploded_edge::to_json () const
+{
+  json::object *eedge_obj = new json::object ();
+  eedge_obj->set ("src_idx", new json::integer_number (m_src->m_index));
+  eedge_obj->set ("dst_idx", new json::integer_number (m_dest->m_index));
+  if (m_sedge)
+    eedge_obj->set ("sedge", m_sedge->to_json ());
+  if (m_custom_info)
+    {
+      pretty_printer pp;
+      pp_format_decoder (&pp) = default_tree_printer;
+      m_custom_info->print (&pp);
+      eedge_obj->set ("custom", new json::string (pp_formatted_text (&pp)));
+    }
+  return eedge_obj;
+}
+
 /* struct stats.  */
 
 /* stats' ctor.  */
@@ -3057,6 +3118,55 @@  exploded_graph::dump_states_for_supernode (FILE *out,
 	   snode->m_index, state_idx);
 }
 
+/* Return a new json::object of the form
+   {"nodes" : [objs for enodes],
+    "edges" : [objs for eedges],
+    "ext_state": object for extrinsic_state,
+    "diagnostic_manager": object for diagnostic_manager}.  */
+
+json::object *
+exploded_graph::to_json () const
+{
+  json::object *egraph_obj = new json::object ();
+
+  /* Nodes.  */
+  {
+    json::array *nodes_arr = new json::array ();
+    unsigned i;
+    exploded_node *n;
+    FOR_EACH_VEC_ELT (m_nodes, i, n)
+      nodes_arr->append (n->to_json (m_ext_state));
+    egraph_obj->set ("nodes", nodes_arr);
+  }
+
+  /* Edges.  */
+  {
+    json::array *edges_arr = new json::array ();
+    unsigned i;
+    exploded_edge *n;
+    FOR_EACH_VEC_ELT (m_edges, i, n)
+      edges_arr->append (n->to_json ());
+    egraph_obj->set ("edges", edges_arr);
+  }
+
+  /* m_sg is JSONified at the top-level.  */
+
+  egraph_obj->set ("ext_state", m_ext_state.to_json ());
+  egraph_obj->set ("diagnostic_manager", m_diagnostic_manager.to_json ());
+
+  /* The following fields aren't yet being JSONified:
+     worklist m_worklist;
+     const state_purge_map *const m_purge_map;
+     const analysis_plan &m_plan;
+     stats m_global_stats;
+     function_stat_map_t m_per_function_stats;
+     stats m_functionless_stats;
+     call_string_data_map_t m_per_call_string_data;
+     auto_vec<int> m_PK_AFTER_SUPERNODE_per_snode;  */
+
+  return egraph_obj;
+}
+
 /* Look for the last use of SEARCH_STMT within this path.
    If found write the edge's index to *OUT_IDX and return true, otherwise
    return false.  */
@@ -4241,6 +4351,39 @@  private:
   auto_delete_vec<auto_vec <exploded_node *> > m_enodes_per_snodes;
 };
 
+/* Implement -fdump-analyzer-json.  */
+
+static void
+dump_analyzer_json (const supergraph &sg,
+		    const exploded_graph &eg)
+{
+  auto_timevar tv (TV_ANALYZER_DUMP);
+  char *filename = concat (dump_base_name, ".analyzer.json.gz", NULL);
+  gzFile output = gzopen (filename, "w");
+  if (!output)
+    {
+      error_at (UNKNOWN_LOCATION, "unable to open %qs for writing", filename);
+      free (filename);
+      return;
+    }
+
+  json::object *toplev_obj = new json::object ();
+  toplev_obj->set ("sgraph", sg.to_json ());
+  toplev_obj->set ("egraph", eg.to_json ());
+
+  pretty_printer pp;
+  toplev_obj->print (&pp);
+  pp_formatted_text (&pp);
+
+  delete toplev_obj;
+
+  if (gzputs (output, pp_formatted_text (&pp)) == EOF
+      || gzclose (output))
+    error_at (UNKNOWN_LOCATION, "error writing %qs", filename);
+
+  free (filename);
+}
+
 /* Run the analysis "engine".  */
 
 void
@@ -4341,6 +4484,9 @@  impl_run_checkers (logger *logger)
       free (filename);
     }
 
+  if (flag_dump_analyzer_json)
+    dump_analyzer_json (sg, eg);
+
   delete purge_map;
 }
 
diff --git a/gcc/analyzer/exploded-graph.h b/gcc/analyzer/exploded-graph.h
index 04e878fbdfc..f723d52bdf8 100644
--- a/gcc/analyzer/exploded-graph.h
+++ b/gcc/analyzer/exploded-graph.h
@@ -165,6 +165,7 @@  class exploded_node : public dnode<eg_traits>
     /* Node was processed by maybe_process_run_of_before_supernode_enodes.  */
     STATUS_BULK_MERGED
   };
+  static const char * status_to_str (enum status s);
 
   exploded_node (const point_and_state &ps, int index);
 
@@ -179,6 +180,8 @@  class exploded_node : public dnode<eg_traits>
   void dump (FILE *fp, const extrinsic_state &ext_state) const;
   void dump (const extrinsic_state &ext_state) const;
 
+  json::object *to_json (const extrinsic_state &ext_state) const;
+
   /* The result of on_stmt.  */
   struct on_stmt_flags
   {
@@ -307,6 +310,8 @@  class exploded_edge : public dedge<eg_traits>
   void dump_dot (graphviz_out *gv, const dump_args_t &args)
     const FINAL OVERRIDE;
 
+  json::object *to_json () const;
+
   //private:
   const superedge *const m_sedge;
 
@@ -782,6 +787,8 @@  public:
   void dump_states_for_supernode (FILE *, const supernode *snode) const;
   void dump_exploded_nodes () const;
 
+  json::object *to_json () const;
+
   exploded_node *get_node_by_index (int idx) const;
 
   const call_string_data_map_t *get_per_call_string_data () const
diff --git a/gcc/analyzer/pending-diagnostic.cc b/gcc/analyzer/pending-diagnostic.cc
index c196903afe0..502d17719b0 100644
--- a/gcc/analyzer/pending-diagnostic.cc
+++ b/gcc/analyzer/pending-diagnostic.cc
@@ -25,6 +25,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "intl.h"
 #include "diagnostic.h"
 #include "function.h"
+#include "json.h"
 #include "analyzer/analyzer.h"
 #include "diagnostic-event-id.h"
 #include "analyzer/analyzer-logging.h"
diff --git a/gcc/analyzer/program-point.cc b/gcc/analyzer/program-point.cc
index ef19e6e38e5..429d6ece724 100644
--- a/gcc/analyzer/program-point.cc
+++ b/gcc/analyzer/program-point.cc
@@ -24,6 +24,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "tree.h"
 #include "gimple-pretty-print.h"
 #include "gcc-rich-location.h"
+#include "json.h"
 #include "analyzer/call-string.h"
 #include "ordered-hash-map.h"
 #include "options.h"
@@ -281,6 +282,43 @@  program_point::dump () const
   pp_flush (&pp);
 }
 
+/* Return a new json::object of the form
+   {"kind"  : str,
+    "snode_idx" : int (optional), the index of the supernode,
+    "from_edge_snode_idx" : int (only for kind=='PK_BEFORE_SUPERNODE'),
+    "stmt_idx": int (only for kind=='PK_BEFORE_STMT',
+    "call_string": object for the call_string}.  */
+
+json::object *
+program_point::to_json () const
+{
+  json::object *point_obj = new json::object ();
+
+  point_obj->set ("kind",
+		  new json::string (point_kind_to_string (get_kind ())));
+
+  if (get_supernode ())
+    point_obj->set ("snode_idx",
+		    new json::integer_number (get_supernode ()->m_index));
+
+  switch (get_kind ())
+    {
+    default: break;
+    case PK_BEFORE_SUPERNODE:
+      if (const superedge *sedge = get_from_edge ())
+	point_obj->set ("from_edge_snode_idx",
+			new json::integer_number (sedge->m_src->m_index));
+      break;
+    case PK_BEFORE_STMT:
+      point_obj->set ("stmt_idx", new json::integer_number (get_stmt_idx ()));
+      break;
+    }
+
+  point_obj->set ("call_string", m_call_string.to_json ());
+
+  return point_obj;
+}
+
 /* Generate a hash value for this program_point.  */
 
 hashval_t
diff --git a/gcc/analyzer/program-point.h b/gcc/analyzer/program-point.h
index 97fd0a5e9de..d804621a715 100644
--- a/gcc/analyzer/program-point.h
+++ b/gcc/analyzer/program-point.h
@@ -175,6 +175,8 @@  public:
   void print_source_line (pretty_printer *pp) const;
   void dump () const;
 
+  json::object *to_json () const;
+
   hashval_t hash () const;
   bool operator== (const program_point &other) const
   {
diff --git a/gcc/analyzer/program-state.cc b/gcc/analyzer/program-state.cc
index 71bb2864d6d..83a6e5b081e 100644
--- a/gcc/analyzer/program-state.cc
+++ b/gcc/analyzer/program-state.cc
@@ -25,6 +25,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "diagnostic-core.h"
 #include "diagnostic.h"
 #include "function.h"
+#include "json.h"
 #include "analyzer/analyzer.h"
 #include "analyzer/analyzer-logging.h"
 #include "analyzer/sm.h"
@@ -99,6 +100,26 @@  extrinsic_state::dump () const
   dump_to_file (stderr);
 }
 
+/* Return a new json::object of the form
+   {"checkers"  : array of objects, one for each state_machine}.  */
+
+json::object *
+extrinsic_state::to_json () const
+{
+  json::object *ext_state_obj = new json::object ();
+
+  {
+    json::array *checkers_arr = new json::array ();
+    unsigned i;
+    state_machine *sm;
+    FOR_EACH_VEC_ELT (m_checkers, i, sm)
+      checkers_arr->append (sm->to_json ());
+    ext_state_obj->set ("checkers", checkers_arr);
+  }
+
+  return ext_state_obj;
+}
+
 /* Get the region_model_manager for this extrinsic_state.  */
 
 region_model_manager *
@@ -208,6 +229,33 @@  sm_state_map::dump (bool simple) const
   pp_flush (&pp);
 }
 
+/* Return a new json::object of the form
+   {"global"  : (optional) value for global state,
+    SVAL_DESC : value for state}.  */
+
+json::object *
+sm_state_map::to_json () const
+{
+  json::object *map_obj = new json::object ();
+
+  if (m_global_state != m_sm.get_start_state ())
+    map_obj->set ("global", m_global_state->to_json ());
+  for (map_t::iterator iter = m_map.begin ();
+       iter != m_map.end ();
+       ++iter)
+    {
+      const svalue *sval = (*iter).first;
+      entry_t e = (*iter).second;
+
+      label_text sval_desc = sval->get_desc ();
+      map_obj->set (sval_desc.m_buffer, e.m_state->to_json ());
+      sval_desc.maybe_free ();
+
+      /* This doesn't yet JSONify e.m_origin.  */
+    }
+  return map_obj;
+}
+
 /* Return true if no states have been set within this map
    (all expressions are for the start state).  */
 
@@ -733,6 +781,43 @@  program_state::dump (const extrinsic_state &ext_state,
   dump_to_file (ext_state, summarize, true, stderr);
 }
 
+/* Return a new json::object of the form
+   {"store"  : object for store,
+    "constraints" : object for constraint_manager,
+    "curr_frame" : (optional) str for current frame,
+    "checkers" : { STATE_NAME : object per sm_state_map },
+    "valid" : true/false}.  */
+
+json::object *
+program_state::to_json (const extrinsic_state &ext_state) const
+{
+  json::object *state_obj = new json::object ();
+
+  state_obj->set ("store", m_region_model->get_store ()->to_json ());
+  state_obj->set ("constraints",
+		  m_region_model->get_constraints ()->to_json ());
+  if (m_region_model->get_current_frame ())
+    state_obj->set ("curr_frame",
+		    m_region_model->get_current_frame ()->to_json ());
+
+  /* Provide m_checker_states as an object, using names as keys.  */
+  {
+    json::object *checkers_obj = new json::object ();
+
+    int i;
+    sm_state_map *smap;
+    FOR_EACH_VEC_ELT (m_checker_states, i, smap)
+      if (!smap->is_empty_p ())
+	checkers_obj->set (ext_state.get_name (i), smap->to_json ());
+
+    state_obj->set ("checkers", checkers_obj);
+  }
+
+  state_obj->set ("valid", new json::literal (m_valid));
+
+  return state_obj;
+}
+
 /* Update this program_state to reflect a top-level call to FUN.
    The params will have initial_svalues.  */
 
diff --git a/gcc/analyzer/program-state.h b/gcc/analyzer/program-state.h
index cb0df8cd904..a52fbeb2e3e 100644
--- a/gcc/analyzer/program-state.h
+++ b/gcc/analyzer/program-state.h
@@ -53,6 +53,8 @@  public:
   void dump_to_file (FILE *outf) const;
   void dump () const;
 
+  json::object *to_json () const;
+
   engine *get_engine () const { return m_engine; }
   region_model_manager *get_model_manager () const;
 
@@ -109,6 +111,8 @@  public:
 	      pretty_printer *pp) const;
   void dump (bool simple) const;
 
+  json::object *to_json () const;
+
   bool is_empty_p () const;
 
   hashval_t hash () const;
@@ -204,6 +208,8 @@  public:
 		     bool multiline, FILE *outf) const;
   void dump (const extrinsic_state &ext_state, bool simple) const;
 
+  json::object *to_json (const extrinsic_state &ext_state) const;
+
   void push_frame (const extrinsic_state &ext_state, function *fun);
   function * get_current_function () const;
 
diff --git a/gcc/analyzer/region-model-impl-calls.cc b/gcc/analyzer/region-model-impl-calls.cc
index 423f74a4152..009b8c3ecb0 100644
--- a/gcc/analyzer/region-model-impl-calls.cc
+++ b/gcc/analyzer/region-model-impl-calls.cc
@@ -42,6 +42,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "bitmap.h"
 #include "selftest.h"
 #include "function.h"
+#include "json.h"
 #include "analyzer/analyzer.h"
 #include "analyzer/analyzer-logging.h"
 #include "ordered-hash-map.h"
diff --git a/gcc/analyzer/region-model-manager.cc b/gcc/analyzer/region-model-manager.cc
index da8fa01077b..8dd3ad0020a 100644
--- a/gcc/analyzer/region-model-manager.cc
+++ b/gcc/analyzer/region-model-manager.cc
@@ -42,6 +42,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "bitmap.h"
 #include "selftest.h"
 #include "function.h"
+#include "json.h"
 #include "analyzer/analyzer.h"
 #include "analyzer/analyzer-logging.h"
 #include "ordered-hash-map.h"
diff --git a/gcc/analyzer/region-model-reachability.cc b/gcc/analyzer/region-model-reachability.cc
index 681b8f74b19..c1b3b2db630 100644
--- a/gcc/analyzer/region-model-reachability.cc
+++ b/gcc/analyzer/region-model-reachability.cc
@@ -47,6 +47,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "cgraph.h"
 #include "cfg.h"
 #include "digraph.h"
+#include "json.h"
 #include "analyzer/call-string.h"
 #include "analyzer/program-point.h"
 #include "analyzer/store.h"
diff --git a/gcc/analyzer/region-model.cc b/gcc/analyzer/region-model.cc
index 6f04904a74e..74a96b025a4 100644
--- a/gcc/analyzer/region-model.cc
+++ b/gcc/analyzer/region-model.cc
@@ -42,6 +42,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "bitmap.h"
 #include "selftest.h"
 #include "function.h"
+#include "json.h"
 #include "analyzer/analyzer.h"
 #include "analyzer/analyzer-logging.h"
 #include "ordered-hash-map.h"
diff --git a/gcc/analyzer/region-model.h b/gcc/analyzer/region-model.h
index 4859df369cf..1e8a517dd8c 100644
--- a/gcc/analyzer/region-model.h
+++ b/gcc/analyzer/region-model.h
@@ -256,6 +256,8 @@  public:
   void dump (bool simple=true) const;
   label_text get_desc (bool simple=true) const;
 
+  json::value *to_json () const;
+
   virtual const region_svalue *
   dyn_cast_region_svalue () const { return NULL; }
   virtual const constant_svalue *
@@ -1400,6 +1402,8 @@  public:
   virtual void dump_to_pp (pretty_printer *pp, bool simple) const = 0;
   void dump (bool simple) const;
 
+  json::value *to_json () const;
+
   bool non_null_p () const;
 
   static int cmp_ptrs (const void *, const void *);
diff --git a/gcc/analyzer/region.cc b/gcc/analyzer/region.cc
index 53f32dc912c..0820893a9b4 100644
--- a/gcc/analyzer/region.cc
+++ b/gcc/analyzer/region.cc
@@ -44,6 +44,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "bitmap.h"
 #include "selftest.h"
 #include "function.h"
+#include "json.h"
 #include "analyzer/analyzer.h"
 #include "analyzer/analyzer-logging.h"
 #include "ordered-hash-map.h"
@@ -460,6 +461,17 @@  region::dump (bool simple) const
   pp_flush (&pp);
 }
 
+/* Return a new json::string describing the region.  */
+
+json::value *
+region::to_json () const
+{
+  label_text desc = get_desc (true);
+  json::value *reg_js = new json::string (desc.m_buffer);
+  desc.maybe_free ();
+  return reg_js;
+}
+
 /* Generate a description of this region.  */
 
 DEBUG_FUNCTION label_text
diff --git a/gcc/analyzer/sm-file.cc b/gcc/analyzer/sm-file.cc
index 58a0fd461fa..d2010710529 100644
--- a/gcc/analyzer/sm-file.cc
+++ b/gcc/analyzer/sm-file.cc
@@ -29,6 +29,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "diagnostic-path.h"
 #include "diagnostic-metadata.h"
 #include "function.h"
+#include "json.h"
 #include "analyzer/analyzer.h"
 #include "diagnostic-event-id.h"
 #include "analyzer/analyzer-logging.h"
diff --git a/gcc/analyzer/sm-malloc.cc b/gcc/analyzer/sm-malloc.cc
index 12b2383e4a7..6293d7885cd 100644
--- a/gcc/analyzer/sm-malloc.cc
+++ b/gcc/analyzer/sm-malloc.cc
@@ -30,6 +30,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "diagnostic-path.h"
 #include "diagnostic-metadata.h"
 #include "function.h"
+#include "json.h"
 #include "analyzer/analyzer.h"
 #include "diagnostic-event-id.h"
 #include "analyzer/analyzer-logging.h"
diff --git a/gcc/analyzer/sm-pattern-test.cc b/gcc/analyzer/sm-pattern-test.cc
index bb6d3b1e719..c430476a846 100644
--- a/gcc/analyzer/sm-pattern-test.cc
+++ b/gcc/analyzer/sm-pattern-test.cc
@@ -31,6 +31,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "diagnostic-path.h"
 #include "diagnostic-metadata.h"
 #include "function.h"
+#include "json.h"
 #include "analyzer/analyzer.h"
 #include "diagnostic-event-id.h"
 #include "analyzer/analyzer-logging.h"
diff --git a/gcc/analyzer/sm-sensitive.cc b/gcc/analyzer/sm-sensitive.cc
index 49f9eb387b1..aec0a6ab8da 100644
--- a/gcc/analyzer/sm-sensitive.cc
+++ b/gcc/analyzer/sm-sensitive.cc
@@ -31,6 +31,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "diagnostic-path.h"
 #include "diagnostic-metadata.h"
 #include "function.h"
+#include "json.h"
 #include "analyzer/analyzer.h"
 #include "diagnostic-event-id.h"
 #include "analyzer/analyzer-logging.h"
diff --git a/gcc/analyzer/sm-signal.cc b/gcc/analyzer/sm-signal.cc
index bf6ea480423..2e05de8a2de 100644
--- a/gcc/analyzer/sm-signal.cc
+++ b/gcc/analyzer/sm-signal.cc
@@ -32,6 +32,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "diagnostic-path.h"
 #include "diagnostic-metadata.h"
 #include "function.h"
+#include "json.h"
 #include "analyzer/analyzer.h"
 #include "diagnostic-event-id.h"
 #include "analyzer/analyzer-logging.h"
@@ -220,6 +221,12 @@  public:
     pp_string (pp, "signal delivered");
   }
 
+  json::object *to_json () const
+  {
+    json::object *custom_obj = new json::object ();
+    return custom_obj;
+  }
+
   void update_model (region_model *model,
 		     const exploded_edge &eedge) FINAL OVERRIDE
   {
diff --git a/gcc/analyzer/sm-taint.cc b/gcc/analyzer/sm-taint.cc
index 49bbd6dfb13..37491d8a49a 100644
--- a/gcc/analyzer/sm-taint.cc
+++ b/gcc/analyzer/sm-taint.cc
@@ -31,6 +31,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "diagnostic-path.h"
 #include "diagnostic-metadata.h"
 #include "function.h"
+#include "json.h"
 #include "analyzer/analyzer.h"
 #include "diagnostic-event-id.h"
 #include "analyzer/analyzer-logging.h"
diff --git a/gcc/analyzer/sm.cc b/gcc/analyzer/sm.cc
index a333063c65e..3fe75ef8c4a 100644
--- a/gcc/analyzer/sm.cc
+++ b/gcc/analyzer/sm.cc
@@ -29,6 +29,9 @@  along with GCC; see the file COPYING3.  If not see
 #include "function.h"
 #include "diagnostic-core.h"
 #include "pretty-print.h"
+#include "diagnostic.h"
+#include "tree-diagnostic.h"
+#include "json.h"
 #include "analyzer/analyzer.h"
 #include "analyzer/analyzer-logging.h"
 #include "analyzer/sm.h"
@@ -56,6 +59,17 @@  state_machine::state::dump_to_pp (pretty_printer *pp) const
   pp_string (pp, m_name);
 }
 
+/* Return a new json::string describing the state.  */
+
+json::value *
+state_machine::state::to_json () const
+{
+  pretty_printer pp;
+  pp_format_decoder (&pp) = default_tree_printer;
+  dump_to_pp (&pp);
+  return new json::string (pp_formatted_text (&pp));
+}
+
 /* class state_machine.  */
 
 /* state_machine's ctor.  */
@@ -109,6 +123,28 @@  state_machine::dump_to_pp (pretty_printer *pp) const
     }
 }
 
+/* Return a new json::object of the form
+   {"name" : str,
+    "states" : [str]}.  */
+
+json::object *
+state_machine::to_json () const
+{
+  json::object *sm_obj = new json::object ();
+
+  sm_obj->set ("name", new json::string (m_name));
+  {
+    json::array *states_arr = new json::array ();
+    unsigned i;
+    state *s;
+    FOR_EACH_VEC_ELT (m_states, i, s)
+      states_arr->append (s->to_json ());
+    sm_obj->set ("states", states_arr);
+  }
+
+  return sm_obj;
+}
+
 /* Create instances of the various state machines, each using LOGGER,
    and populate OUT with them.  */
 
diff --git a/gcc/analyzer/sm.h b/gcc/analyzer/sm.h
index f44ad922200..46b93ffb790 100644
--- a/gcc/analyzer/sm.h
+++ b/gcc/analyzer/sm.h
@@ -48,6 +48,7 @@  public:
 
     const char *get_name () const { return m_name; }
     virtual void dump_to_pp (pretty_printer *pp) const;
+    virtual json::value *to_json () const;
 
     unsigned get_id () const { return m_id; }
 
@@ -121,6 +122,8 @@  public:
 
   void dump_to_pp (pretty_printer *pp) const;
 
+  json::object *to_json () const;
+
   state_t get_start_state () const { return m_start; }
 
 protected:
diff --git a/gcc/analyzer/state-purge.cc b/gcc/analyzer/state-purge.cc
index d5a24b48e1e..e4942a692fa 100644
--- a/gcc/analyzer/state-purge.cc
+++ b/gcc/analyzer/state-purge.cc
@@ -37,6 +37,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "diagnostic-core.h"
 #include "gimple-pretty-print.h"
 #include "function.h"
+#include "json.h"
 #include "analyzer/analyzer.h"
 #include "analyzer/call-string.h"
 #include "digraph.h"
diff --git a/gcc/analyzer/store.cc b/gcc/analyzer/store.cc
index 1348895e5c7..11585123561 100644
--- a/gcc/analyzer/store.cc
+++ b/gcc/analyzer/store.cc
@@ -42,6 +42,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "bitmap.h"
 #include "selftest.h"
 #include "function.h"
+#include "json.h"
 #include "analyzer/analyzer.h"
 #include "analyzer/analyzer-logging.h"
 #include "ordered-hash-map.h"
@@ -122,6 +123,17 @@  binding_key::dump (bool simple) const
   pp_flush (&pp);
 }
 
+/* Get a description of this binding_key.  */
+
+label_text
+binding_key::get_desc (bool simple) const
+{
+  pretty_printer pp;
+  pp_format_decoder (&pp) = default_tree_printer;
+  dump_to_pp (&pp, simple);
+  return label_text::take (xstrdup (pp_formatted_text (&pp)));
+}
+
 /* qsort callback.  */
 
 int
@@ -366,6 +378,37 @@  binding_map::dump (bool simple) const
   pp_flush (&pp);
 }
 
+/* Return a new json::object of the form
+   {KEY_DESC : SVALUE_DESC,
+    ...for the various key/value pairs in this binding_map}.  */
+
+json::object *
+binding_map::to_json () const
+{
+  json::object *map_obj = new json::object ();
+
+  auto_vec <const binding_key *> binding_keys;
+  for (map_t::iterator iter = m_map.begin ();
+       iter != m_map.end (); ++iter)
+    {
+      const binding_key *key = (*iter).first;
+      binding_keys.safe_push (key);
+    }
+  binding_keys.qsort (binding_key::cmp_ptrs);
+
+  const binding_key *key;
+  unsigned i;
+  FOR_EACH_VEC_ELT (binding_keys, i, key)
+    {
+      const svalue *value = *const_cast <map_t &> (m_map).get (key);
+      label_text key_desc = key->get_desc ();
+      map_obj->set (key_desc.m_buffer, value->to_json ());
+      key_desc.maybe_free ();
+    }
+
+  return map_obj;
+}
+
 /* Get the child region of PARENT_REG based upon INDEX within a
    CONSTRUCTOR.   */
 
@@ -657,6 +700,23 @@  binding_cluster::dump (bool simple) const
   pp_flush (&pp);
 }
 
+/* Return a new json::object of the form
+   {"escaped": true/false,
+    "touched": true/false,
+    "map" : object for the the binding_map.  */
+
+json::object *
+binding_cluster::to_json () const
+{
+  json::object *cluster_obj = new json::object ();
+
+  cluster_obj->set ("escaped", new json::literal (m_escaped));
+  cluster_obj->set ("touched", new json::literal (m_touched));
+  cluster_obj->set ("map", m_map.to_json ());
+
+  return cluster_obj;
+}
+
 /* Add a binding of SVAL of kind KIND to REG, unpacking SVAL if it is a
    compound_sval.  */
 
@@ -1575,6 +1635,64 @@  store::dump (bool simple) const
   pp_flush (&pp);
 }
 
+/* Return a new json::object of the form
+   {PARENT_REGION_DESC: {BASE_REGION_DESC: object for binding_map,
+			 ... for each cluster within parent region},
+    ...for each parent region,
+    "called_unknown_function": true/false}.  */
+
+json::object *
+store::to_json () const
+{
+  json::object *store_obj = new json::object ();
+
+  /* Sort into some deterministic order.  */
+  auto_vec<const region *> base_regions;
+  for (cluster_map_t::iterator iter = m_cluster_map.begin ();
+       iter != m_cluster_map.end (); ++iter)
+    {
+      const region *base_reg = (*iter).first;
+      base_regions.safe_push (base_reg);
+    }
+  base_regions.qsort (region::cmp_ptrs);
+
+  /* Gather clusters, organize by parent region, so that we can group
+     together locals, globals, etc.  */
+  auto_vec<const region *> parent_regions;
+  get_sorted_parent_regions (&parent_regions, base_regions);
+
+  const region *parent_reg;
+  unsigned i;
+  FOR_EACH_VEC_ELT (parent_regions, i, parent_reg)
+    {
+      gcc_assert (parent_reg);
+
+      json::object *clusters_in_parent_reg_obj = new json::object ();
+
+      const region *base_reg;
+      unsigned j;
+      FOR_EACH_VEC_ELT (base_regions, j, base_reg)
+	{
+	  /* This is O(N * M), but N ought to be small.  */
+	  if (base_reg->get_parent_region () != parent_reg)
+	    continue;
+	  binding_cluster *cluster
+	    = *const_cast<cluster_map_t &> (m_cluster_map).get (base_reg);
+	  label_text base_reg_desc = base_reg->get_desc ();
+	  clusters_in_parent_reg_obj->set (base_reg_desc.m_buffer,
+					   cluster->to_json ());
+	  base_reg_desc.maybe_free ();
+	}
+      label_text parent_reg_desc = parent_reg->get_desc ();
+      store_obj->set (parent_reg_desc.m_buffer, clusters_in_parent_reg_obj);
+      parent_reg_desc.maybe_free ();
+    }
+
+  store_obj->set ("called_unknown_fn", new json::literal (m_called_unknown_fn));
+
+  return store_obj;
+}
+
 /* Get any svalue bound to REG, or NULL.  */
 
 const svalue *
diff --git a/gcc/analyzer/store.h b/gcc/analyzer/store.h
index 83a43107b21..0f4e7ab2a56 100644
--- a/gcc/analyzer/store.h
+++ b/gcc/analyzer/store.h
@@ -159,6 +159,7 @@  public:
 
   virtual void dump_to_pp (pretty_printer *pp, bool simple) const;
   void dump (bool simple) const;
+  label_text get_desc (bool simple=true) const;
 
   static int cmp_ptrs (const void *, const void *);
   static int cmp (const binding_key *, const binding_key *);
@@ -340,6 +341,8 @@  public:
   void dump_to_pp (pretty_printer *pp, bool simple, bool multiline) const;
   void dump (bool simple) const;
 
+  json::object *to_json () const;
+
   bool apply_ctor_to_region (const region *parent_reg, tree ctor,
 			     region_model_manager *mgr);
 
@@ -392,6 +395,8 @@  public:
   void dump_to_pp (pretty_printer *pp, bool simple, bool multiline) const;
   void dump (bool simple) const;
 
+  json::object *to_json () const;
+
   void bind (store_manager *mgr, const region *, const svalue *,
 	     binding_kind kind);
 
@@ -517,6 +522,8 @@  public:
   void dump (bool simple) const;
   void summarize_to_pp (pretty_printer *pp, bool simple) const;
 
+  json::object *to_json () const;
+
   const svalue *get_direct_binding (store_manager *mgr, const region *reg);
   const svalue *get_default_binding (store_manager *mgr, const region *reg);
   const svalue *get_any_binding (store_manager *mgr, const region *reg) const;
diff --git a/gcc/analyzer/supergraph.cc b/gcc/analyzer/supergraph.cc
index 7c6fed3a060..735c4a30e09 100644
--- a/gcc/analyzer/supergraph.cc
+++ b/gcc/analyzer/supergraph.cc
@@ -43,6 +43,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "tree-dfa.h"
 #include "cfganal.h"
 #include "function.h"
+#include "json.h"
 #include "analyzer/analyzer.h"
 #include "ordered-hash-map.h"
 #include "options.h"
@@ -374,6 +375,38 @@  supergraph::dump_dot (const char *path, const dump_args_t &dump_args) const
   fclose (fp);
 }
 
+/* Return a new json::object of the form
+   {"nodes" : [objs for snodes],
+    "edges" : [objs for sedges]}.  */
+
+json::object *
+supergraph::to_json () const
+{
+  json::object *sgraph_obj = new json::object ();
+
+  /* Nodes.  */
+  {
+    json::array *nodes_arr = new json::array ();
+    unsigned i;
+    supernode *n;
+    FOR_EACH_VEC_ELT (m_nodes, i, n)
+      nodes_arr->append (n->to_json ());
+    sgraph_obj->set ("nodes", nodes_arr);
+  }
+
+  /* Edges.  */
+  {
+    json::array *edges_arr = new json::array ();
+    unsigned i;
+    superedge *n;
+    FOR_EACH_VEC_ELT (m_edges, i, n)
+      edges_arr->append (n->to_json ());
+    sgraph_obj->set ("edges", edges_arr);
+  }
+
+  return sgraph_obj;
+}
+
 /* Create a supernode for BB within FUN and add it to this supergraph.
 
    If RETURNING_CALL is non-NULL, the supernode represents the resumption
@@ -594,6 +627,63 @@  supernode::dump_dot_id (pretty_printer *pp) const
   pp_printf (pp, "node_%i", m_index);
 }
 
+/* Return a new json::object of the form
+   {"idx": int,
+    "bb_idx": int,
+    "m_returning_call": optional str,
+    "phis": [str],
+    "stmts" : [str]}.  */
+
+json::object *
+supernode::to_json () const
+{
+  json::object *snode_obj = new json::object ();
+
+  snode_obj->set ("idx", new json::integer_number (m_index));
+  snode_obj->set ("bb_idx", new json::integer_number (m_bb->index));
+
+  if (m_returning_call)
+    {
+      pretty_printer pp;
+      pp_format_decoder (&pp) = default_tree_printer;
+      pp_gimple_stmt_1 (&pp, m_returning_call, 0, (dump_flags_t)0);
+      snode_obj->set ("returning_call",
+		      new json::string (pp_formatted_text (&pp)));
+    }
+
+  /* Phi nodes.  */
+  {
+    json::array *phi_arr = new json::array ();
+    for (gphi_iterator gpi = const_cast<supernode *> (this)->start_phis ();
+	 !gsi_end_p (gpi); gsi_next (&gpi))
+      {
+	const gimple *stmt = gsi_stmt (gpi);
+	pretty_printer pp;
+	pp_format_decoder (&pp) = default_tree_printer;
+	pp_gimple_stmt_1 (&pp, stmt, 0, (dump_flags_t)0);
+	phi_arr->append (new json::string (pp_formatted_text (&pp)));
+      }
+    snode_obj->set ("phis", phi_arr);
+  }
+
+  /* Statements.  */
+  {
+    json::array *stmt_arr = new json::array ();
+    int i;
+    gimple *stmt;
+    FOR_EACH_VEC_ELT (m_stmts, i, stmt)
+      {
+	pretty_printer pp;
+	pp_format_decoder (&pp) = default_tree_printer;
+	pp_gimple_stmt_1 (&pp, stmt, 0, (dump_flags_t)0);
+	stmt_arr->append (new json::string (pp_formatted_text (&pp)));
+      }
+    snode_obj->set ("stmts", stmt_arr);
+  }
+
+  return snode_obj;
+}
+
 /* Get a location_t for the start of this supernode.  */
 
 location_t
@@ -759,6 +849,28 @@  superedge::dump_dot (graphviz_out *gv, const dump_args_t &) const
   pp_printf (pp, "\"];\n");
 }
 
+/* Return a new json::object of the form
+   {"src_idx": int, the index of the source supernode,
+    "dst_idx": int, the index of the destination supernode,
+    "desc"   : str.  */
+
+json::object *
+superedge::to_json () const
+{
+  json::object *sedge_obj = new json::object ();
+  sedge_obj->set ("src_idx", new json::integer_number (m_src->m_index));
+  sedge_obj->set ("dst_idx", new json::integer_number (m_dest->m_index));
+
+  {
+    pretty_printer pp;
+    pp_format_decoder (&pp) = default_tree_printer;
+    dump_label_to_pp (&pp, false);
+    sedge_obj->set ("desc", new json::string (pp_formatted_text (&pp)));
+  }
+
+  return sedge_obj;
+}
+
 /* If this is an intraprocedural superedge, return the associated
    CFG edge.  Otherwise, return NULL.  */
 
diff --git a/gcc/analyzer/supergraph.h b/gcc/analyzer/supergraph.h
index c25043d92fd..40ae9ff8ac2 100644
--- a/gcc/analyzer/supergraph.h
+++ b/gcc/analyzer/supergraph.h
@@ -148,6 +148,8 @@  public:
   void dump_dot_to_file (FILE *fp, const dump_args_t &) const;
   void dump_dot (const char *path, const dump_args_t &) const;
 
+  json::object *to_json () const;
+
   int num_nodes () const { return m_nodes.length (); }
   int num_edges () const { return m_edges.length (); }
 
@@ -231,6 +233,8 @@  class supernode : public dnode<supergraph_traits>
   void dump_dot (graphviz_out *gv, const dump_args_t &args) const OVERRIDE;
   void dump_dot_id (pretty_printer *pp) const;
 
+  json::object *to_json () const;
+
   location_t get_start_location () const;
   location_t get_end_location () const;
 
@@ -289,6 +293,8 @@  class superedge : public dedge<supergraph_traits>
   virtual void dump_label_to_pp (pretty_printer *pp,
 				 bool user_facing) const = 0;
 
+  json::object *to_json () const;
+
   enum edge_kind get_kind () const { return m_kind; }
 
   virtual cfg_superedge *dyn_cast_cfg_superedge () { return NULL; }
diff --git a/gcc/analyzer/svalue.cc b/gcc/analyzer/svalue.cc
index fcab578674e..ae3b6783e9c 100644
--- a/gcc/analyzer/svalue.cc
+++ b/gcc/analyzer/svalue.cc
@@ -42,6 +42,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "bitmap.h"
 #include "selftest.h"
 #include "function.h"
+#include "json.h"
 #include "analyzer/analyzer.h"
 #include "analyzer/analyzer-logging.h"
 #include "options.h"
@@ -116,6 +117,17 @@  svalue::get_desc (bool simple) const
   return label_text::take (xstrdup (pp_formatted_text (&pp)));
 }
 
+/* Return a new json::string describing the svalue.  */
+
+json::value *
+svalue::to_json () const
+{
+  label_text desc = get_desc (true);
+  json::value *sval_js = new json::string (desc.m_buffer);
+  desc.maybe_free ();
+  return sval_js;
+}
+
 /* If this svalue is a constant_svalue, return the underlying tree constant.
    Otherwise return NULL_TREE.  */
 
diff --git a/gcc/doc/analyzer.texi b/gcc/doc/analyzer.texi
index 92c12e19401..6b7d70cccaa 100644
--- a/gcc/doc/analyzer.texi
+++ b/gcc/doc/analyzer.texi
@@ -488,6 +488,9 @@  truthfulness of the argument.  This is useful for writing DejaGnu tests.
 
 @subsection Other Debugging Techniques
 
+The option @option{-fdump-analyzer-json} will dump both the supergraph
+and the exploded graph in compressed JSON form.
+
 One approach when tracking down where a particular bogus state is
 introduced into the @code{exploded_graph} is to add custom code to
 @code{program_state::validate}.
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 8be2b4f4de6..49b5348c606 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -406,6 +406,7 @@  Objective-C and Objective-C++ Dialects}.
 -fdump-analyzer-exploded-nodes @gol
 -fdump-analyzer-exploded-nodes-2 @gol
 -fdump-analyzer-exploded-nodes-3 @gol
+-fdump-analyzer-json @gol
 -fdump-analyzer-state-purge @gol
 -fdump-analyzer-supergraph @gol
 -Wno-analyzer-double-fclose @gol
@@ -9103,6 +9104,12 @@  Dump a textual representation of the ``exploded graph'' to
 one dump file per node, to @file{@var{file}.eg-@var{id}.txt}.
 This is typically a large number of dump files.
 
+@item -fdump-analyzer-json
+@opindex fdump-analyzer-json
+Dump a compressed JSON representation of analyzer internals to
+@file{@var{file}.analyzer.json.gz}.  The precise format is subject
+to change.
+
 @item -fdump-analyzer-state-purge
 @opindex fdump-analyzer-state-purge
 As per @option{-fdump-analyzer-supergraph}, dump a representation of the