1760c2415Smrg /* Emit optimization information as JSON files.
2*0bfacb9bSmrg    Copyright (C) 2018-2020 Free Software Foundation, Inc.
3760c2415Smrg    Contributed by David Malcolm <dmalcolm@redhat.com>.
4760c2415Smrg 
5760c2415Smrg This file is part of GCC.
6760c2415Smrg 
7760c2415Smrg GCC is free software; you can redistribute it and/or modify it under
8760c2415Smrg the terms of the GNU General Public License as published by the Free
9760c2415Smrg Software Foundation; either version 3, or (at your option) any later
10760c2415Smrg version.
11760c2415Smrg 
12760c2415Smrg GCC is distributed in the hope that it will be useful, but WITHOUT ANY
13760c2415Smrg WARRANTY; without even the implied warranty of MERCHANTABILITY or
14760c2415Smrg FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15760c2415Smrg for more details.
16760c2415Smrg 
17760c2415Smrg You should have received a copy of the GNU General Public License
18760c2415Smrg along with GCC; see the file COPYING3.  If not see
19760c2415Smrg <http://www.gnu.org/licenses/>.  */
20760c2415Smrg 
21760c2415Smrg #include "config.h"
22760c2415Smrg #include "system.h"
23760c2415Smrg #include "coretypes.h"
24760c2415Smrg 
25760c2415Smrg #include "backend.h"
26760c2415Smrg #include "tree.h"
27760c2415Smrg #include "gimple.h"
28760c2415Smrg #include "diagnostic-core.h"
29760c2415Smrg 
30760c2415Smrg #include "profile.h"
31760c2415Smrg #include "output.h"
32760c2415Smrg #include "tree-pass.h"
33760c2415Smrg 
34760c2415Smrg #include "optinfo.h"
35760c2415Smrg #include "optinfo-emit-json.h"
36760c2415Smrg #include "json.h"
37760c2415Smrg #include "pretty-print.h"
38760c2415Smrg #include "tree-pretty-print.h"
39760c2415Smrg #include "gimple-pretty-print.h"
40760c2415Smrg #include "cgraph.h"
41760c2415Smrg 
42760c2415Smrg #include "langhooks.h"
43760c2415Smrg #include "version.h"
44760c2415Smrg #include "context.h"
45760c2415Smrg #include "pass_manager.h"
46760c2415Smrg #include "selftest.h"
47760c2415Smrg #include "dump-context.h"
48760c2415Smrg #include <zlib.h>
49760c2415Smrg 
50760c2415Smrg /* optrecord_json_writer's ctor.  Populate the top-level parts of the
51760c2415Smrg    in-memory JSON representation.  */
52760c2415Smrg 
optrecord_json_writer()53760c2415Smrg optrecord_json_writer::optrecord_json_writer ()
54760c2415Smrg   : m_root_tuple (NULL), m_scopes ()
55760c2415Smrg {
56760c2415Smrg   m_root_tuple = new json::array ();
57760c2415Smrg 
58760c2415Smrg   /* Populate with metadata; compare with toplev.c: print_version.  */
59760c2415Smrg   json::object *metadata = new json::object ();
60760c2415Smrg   m_root_tuple->append (metadata);
61760c2415Smrg   metadata->set ("format", new json::string ("1"));
62760c2415Smrg   json::object *generator = new json::object ();
63760c2415Smrg   metadata->set ("generator", generator);
64760c2415Smrg   generator->set ("name", new json::string (lang_hooks.name));
65760c2415Smrg   generator->set ("pkgversion", new json::string (pkgversion_string));
66760c2415Smrg   generator->set ("version", new json::string (version_string));
67760c2415Smrg   /* TARGET_NAME is passed in by the Makefile.  */
68760c2415Smrg   generator->set ("target", new json::string (TARGET_NAME));
69760c2415Smrg 
70760c2415Smrg   /* TODO: capture command-line?
71760c2415Smrg      see gen_producer_string in dwarf2out.c (currently static).  */
72760c2415Smrg 
73760c2415Smrg   /* TODO: capture "any plugins?" flag (or the plugins themselves).  */
74760c2415Smrg 
75760c2415Smrg   json::array *passes = new json::array ();
76760c2415Smrg   m_root_tuple->append (passes);
77760c2415Smrg 
78760c2415Smrg   /* Call add_pass_list for all of the pass lists.  */
79760c2415Smrg   {
80760c2415Smrg #define DEF_PASS_LIST(LIST) \
81760c2415Smrg     add_pass_list (passes, g->get_passes ()->LIST);
82760c2415Smrg     GCC_PASS_LISTS
83760c2415Smrg #undef DEF_PASS_LIST
84760c2415Smrg   }
85760c2415Smrg 
86760c2415Smrg   json::array *records = new json::array ();
87760c2415Smrg   m_root_tuple->append (records);
88760c2415Smrg 
89760c2415Smrg   m_scopes.safe_push (records);
90760c2415Smrg }
91760c2415Smrg 
92760c2415Smrg /* optrecord_json_writer's ctor.
93760c2415Smrg    Delete the in-memory JSON representation.  */
94760c2415Smrg 
~optrecord_json_writer()95760c2415Smrg optrecord_json_writer::~optrecord_json_writer ()
96760c2415Smrg {
97760c2415Smrg   delete m_root_tuple;
98760c2415Smrg }
99760c2415Smrg 
100760c2415Smrg /* Choose an appropriate filename, and write the saved records to it.  */
101760c2415Smrg 
102760c2415Smrg void
write() const103760c2415Smrg optrecord_json_writer::write () const
104760c2415Smrg {
105760c2415Smrg   pretty_printer pp;
106760c2415Smrg   m_root_tuple->print (&pp);
107760c2415Smrg 
108760c2415Smrg   bool emitted_error = false;
109760c2415Smrg   char *filename = concat (dump_base_name, ".opt-record.json.gz", NULL);
110760c2415Smrg   gzFile outfile = gzopen (filename, "w");
111760c2415Smrg   if (outfile == NULL)
112760c2415Smrg     {
113760c2415Smrg       error_at (UNKNOWN_LOCATION, "cannot open file %qs for writing optimization records",
114760c2415Smrg 		filename); // FIXME: more info?
115760c2415Smrg       goto cleanup;
116760c2415Smrg     }
117760c2415Smrg 
118760c2415Smrg   if (gzputs (outfile, pp_formatted_text (&pp)) <= 0)
119760c2415Smrg     {
120760c2415Smrg       int tmp;
121760c2415Smrg       error_at (UNKNOWN_LOCATION, "error writing optimization records to %qs: %s",
122760c2415Smrg 		filename, gzerror (outfile, &tmp));
123760c2415Smrg       emitted_error = true;
124760c2415Smrg     }
125760c2415Smrg 
126760c2415Smrg  cleanup:
127760c2415Smrg   if (outfile)
128760c2415Smrg     if (gzclose (outfile) != Z_OK)
129760c2415Smrg       if (!emitted_error)
130760c2415Smrg 	error_at (UNKNOWN_LOCATION, "error closing optimization records %qs",
131760c2415Smrg 		  filename);
132760c2415Smrg 
133760c2415Smrg   free (filename);
134760c2415Smrg }
135760c2415Smrg 
136760c2415Smrg /* Add a record for OPTINFO to the queue of records to be written.  */
137760c2415Smrg 
138760c2415Smrg void
add_record(const optinfo * optinfo)139760c2415Smrg optrecord_json_writer::add_record (const optinfo *optinfo)
140760c2415Smrg {
141760c2415Smrg   json::object *obj = optinfo_to_json (optinfo);
142760c2415Smrg 
143760c2415Smrg   add_record (obj);
144760c2415Smrg 
145760c2415Smrg   /* Potentially push the scope.  */
146760c2415Smrg   if (optinfo->get_kind () == OPTINFO_KIND_SCOPE)
147760c2415Smrg     {
148760c2415Smrg       json::array *children = new json::array ();
149760c2415Smrg       obj->set ("children", children);
150760c2415Smrg       m_scopes.safe_push (children);
151760c2415Smrg     }
152760c2415Smrg }
153760c2415Smrg 
154760c2415Smrg /* Private methods of optrecord_json_writer.  */
155760c2415Smrg 
156*0bfacb9bSmrg /* Add record OBJ to the innermost scope.  */
157760c2415Smrg 
158760c2415Smrg void
add_record(json::object * obj)159760c2415Smrg optrecord_json_writer::add_record (json::object *obj)
160760c2415Smrg {
161760c2415Smrg   /* Add to innermost scope.  */
162760c2415Smrg   gcc_assert (m_scopes.length () > 0);
163760c2415Smrg   m_scopes[m_scopes.length () - 1]->append (obj);
164760c2415Smrg }
165760c2415Smrg 
166760c2415Smrg /* Pop the innermost scope.  */
167760c2415Smrg 
168760c2415Smrg void
pop_scope()169760c2415Smrg optrecord_json_writer::pop_scope ()
170760c2415Smrg {
171760c2415Smrg   m_scopes.pop ();
172760c2415Smrg 
173760c2415Smrg   /* We should never pop the top-level records array.  */
174760c2415Smrg   gcc_assert (m_scopes.length () > 0);
175760c2415Smrg }
176760c2415Smrg 
177760c2415Smrg /* Create a JSON object representing LOC.  */
178760c2415Smrg 
179760c2415Smrg json::object *
impl_location_to_json(dump_impl_location_t loc)180760c2415Smrg optrecord_json_writer::impl_location_to_json (dump_impl_location_t loc)
181760c2415Smrg {
182760c2415Smrg   json::object *obj = new json::object ();
183760c2415Smrg   obj->set ("file", new json::string (loc.m_file));
184*0bfacb9bSmrg   obj->set ("line", new json::integer_number (loc.m_line));
185760c2415Smrg   if (loc.m_function)
186760c2415Smrg     obj->set ("function", new json::string (loc.m_function));
187760c2415Smrg   return obj;
188760c2415Smrg }
189760c2415Smrg 
190760c2415Smrg /* Create a JSON object representing LOC.  */
191760c2415Smrg 
192760c2415Smrg json::object *
location_to_json(location_t loc)193760c2415Smrg optrecord_json_writer::location_to_json (location_t loc)
194760c2415Smrg {
195760c2415Smrg   gcc_assert (LOCATION_LOCUS (loc) != UNKNOWN_LOCATION);
196760c2415Smrg   expanded_location exploc = expand_location (loc);
197760c2415Smrg   json::object *obj = new json::object ();
198760c2415Smrg   obj->set ("file", new json::string (exploc.file));
199*0bfacb9bSmrg   obj->set ("line", new json::integer_number (exploc.line));
200*0bfacb9bSmrg   obj->set ("column", new json::integer_number (exploc.column));
201760c2415Smrg   return obj;
202760c2415Smrg }
203760c2415Smrg 
204760c2415Smrg /* Create a JSON object representing COUNT.  */
205760c2415Smrg 
206760c2415Smrg json::object *
profile_count_to_json(profile_count count)207760c2415Smrg optrecord_json_writer::profile_count_to_json (profile_count count)
208760c2415Smrg {
209760c2415Smrg   json::object *obj = new json::object ();
210*0bfacb9bSmrg   obj->set ("value", new json::integer_number (count.to_gcov_type ()));
211760c2415Smrg   obj->set ("quality",
212760c2415Smrg 	    new json::string (profile_quality_as_string (count.quality ())));
213760c2415Smrg   return obj;
214760c2415Smrg }
215760c2415Smrg 
216760c2415Smrg /* Get a string for use when referring to PASS in the saved optimization
217760c2415Smrg    records.  */
218760c2415Smrg 
219760c2415Smrg json::string *
get_id_value_for_pass(opt_pass * pass)220760c2415Smrg optrecord_json_writer::get_id_value_for_pass (opt_pass *pass)
221760c2415Smrg {
222760c2415Smrg   pretty_printer pp;
223760c2415Smrg   /* this is host-dependent, but will be consistent for a given host.  */
224760c2415Smrg   pp_pointer (&pp, static_cast<void *> (pass));
225760c2415Smrg   return new json::string (pp_formatted_text (&pp));
226760c2415Smrg }
227760c2415Smrg 
228760c2415Smrg /* Create a JSON object representing PASS.  */
229760c2415Smrg 
230760c2415Smrg json::object *
pass_to_json(opt_pass * pass)231760c2415Smrg optrecord_json_writer::pass_to_json (opt_pass *pass)
232760c2415Smrg {
233760c2415Smrg   json::object *obj = new json::object ();
234760c2415Smrg   const char *type = NULL;
235760c2415Smrg   switch (pass->type)
236760c2415Smrg     {
237760c2415Smrg     default:
238760c2415Smrg       gcc_unreachable ();
239760c2415Smrg     case GIMPLE_PASS:
240760c2415Smrg       type = "gimple";
241760c2415Smrg       break;
242760c2415Smrg     case RTL_PASS:
243760c2415Smrg       type = "rtl";
244760c2415Smrg       break;
245760c2415Smrg     case SIMPLE_IPA_PASS:
246760c2415Smrg       type = "simple_ipa";
247760c2415Smrg       break;
248760c2415Smrg     case IPA_PASS:
249760c2415Smrg       type = "ipa";
250760c2415Smrg       break;
251760c2415Smrg     }
252760c2415Smrg   obj->set ("id", get_id_value_for_pass (pass));
253760c2415Smrg   obj->set ("type", new json::string (type));
254760c2415Smrg   obj->set ("name", new json::string (pass->name));
255760c2415Smrg   /* Represent the optgroup flags as an array.  */
256760c2415Smrg   {
257760c2415Smrg     json::array *optgroups = new json::array ();
258760c2415Smrg     obj->set ("optgroups", optgroups);
259760c2415Smrg     for (const kv_pair<optgroup_flags_t> *optgroup = optgroup_options;
260760c2415Smrg 	 optgroup->name != NULL; optgroup++)
261760c2415Smrg       if (optgroup->value != OPTGROUP_ALL
262760c2415Smrg 	  && (pass->optinfo_flags & optgroup->value))
263760c2415Smrg 	optgroups->append (new json::string (optgroup->name));
264760c2415Smrg   }
265*0bfacb9bSmrg   obj->set ("num", new json::integer_number (pass->static_pass_number));
266760c2415Smrg   return obj;
267760c2415Smrg }
268760c2415Smrg 
269760c2415Smrg /* Create a JSON array for LOC representing the chain of inlining
270760c2415Smrg    locations.
271760c2415Smrg    Compare with lhd_print_error_function and cp_print_error_function.  */
272760c2415Smrg 
273760c2415Smrg json::value *
inlining_chain_to_json(location_t loc)274760c2415Smrg optrecord_json_writer::inlining_chain_to_json (location_t loc)
275760c2415Smrg {
276760c2415Smrg   json::array *array = new json::array ();
277760c2415Smrg 
278760c2415Smrg   tree abstract_origin = LOCATION_BLOCK (loc);
279760c2415Smrg 
280760c2415Smrg   while (abstract_origin)
281760c2415Smrg     {
282760c2415Smrg       location_t *locus;
283760c2415Smrg       tree block = abstract_origin;
284760c2415Smrg 
285760c2415Smrg       locus = &BLOCK_SOURCE_LOCATION (block);
286760c2415Smrg       tree fndecl = NULL;
287760c2415Smrg       block = BLOCK_SUPERCONTEXT (block);
288760c2415Smrg       while (block && TREE_CODE (block) == BLOCK
289760c2415Smrg 	     && BLOCK_ABSTRACT_ORIGIN (block))
290760c2415Smrg 	{
291760c2415Smrg 	  tree ao = BLOCK_ABSTRACT_ORIGIN (block);
292760c2415Smrg 	  if (TREE_CODE (ao) == FUNCTION_DECL)
293760c2415Smrg 	    {
294760c2415Smrg 	      fndecl = ao;
295760c2415Smrg 	      break;
296760c2415Smrg 	    }
297760c2415Smrg 	  else if (TREE_CODE (ao) != BLOCK)
298760c2415Smrg 	    break;
299760c2415Smrg 
300760c2415Smrg 	  block = BLOCK_SUPERCONTEXT (block);
301760c2415Smrg 	}
302760c2415Smrg       if (fndecl)
303760c2415Smrg 	abstract_origin = block;
304760c2415Smrg       else
305760c2415Smrg 	{
306760c2415Smrg 	  while (block && TREE_CODE (block) == BLOCK)
307760c2415Smrg 	    block = BLOCK_SUPERCONTEXT (block);
308760c2415Smrg 
309760c2415Smrg 	  if (block && TREE_CODE (block) == FUNCTION_DECL)
310760c2415Smrg 	    fndecl = block;
311760c2415Smrg 	  abstract_origin = NULL;
312760c2415Smrg 	}
313760c2415Smrg       if (fndecl)
314760c2415Smrg 	{
315760c2415Smrg 	  json::object *obj = new json::object ();
316760c2415Smrg 	  const char *printable_name
317760c2415Smrg 	    = lang_hooks.decl_printable_name (fndecl, 2);
318760c2415Smrg 	  obj->set ("fndecl", new json::string (printable_name));
319760c2415Smrg 	  if (LOCATION_LOCUS (*locus) != UNKNOWN_LOCATION)
320760c2415Smrg 	    obj->set ("site", location_to_json (*locus));
321760c2415Smrg 	  array->append (obj);
322760c2415Smrg 	}
323760c2415Smrg     }
324760c2415Smrg 
325760c2415Smrg   return array;
326760c2415Smrg }
327760c2415Smrg 
328760c2415Smrg /* Create a JSON object representing OPTINFO.  */
329760c2415Smrg 
330760c2415Smrg json::object *
optinfo_to_json(const optinfo * optinfo)331760c2415Smrg optrecord_json_writer::optinfo_to_json (const optinfo *optinfo)
332760c2415Smrg {
333760c2415Smrg   json::object *obj = new json::object ();
334760c2415Smrg 
335760c2415Smrg   obj->set ("impl_location",
336760c2415Smrg 	    impl_location_to_json (optinfo->get_impl_location ()));
337760c2415Smrg 
338760c2415Smrg   const char *kind_str = optinfo_kind_to_string (optinfo->get_kind ());
339760c2415Smrg   obj->set ("kind", new json::string (kind_str));
340760c2415Smrg   json::array *message = new json::array ();
341760c2415Smrg   obj->set ("message", message);
342760c2415Smrg   for (unsigned i = 0; i < optinfo->num_items (); i++)
343760c2415Smrg     {
344760c2415Smrg       const optinfo_item *item = optinfo->get_item (i);
345760c2415Smrg       switch (item->get_kind ())
346760c2415Smrg 	{
347760c2415Smrg 	default:
348760c2415Smrg 	  gcc_unreachable ();
349760c2415Smrg 	case OPTINFO_ITEM_KIND_TEXT:
350760c2415Smrg 	  {
351760c2415Smrg 	    message->append (new json::string (item->get_text ()));
352760c2415Smrg 	  }
353760c2415Smrg 	  break;
354760c2415Smrg 	case OPTINFO_ITEM_KIND_TREE:
355760c2415Smrg 	  {
356760c2415Smrg 	    json::object *json_item = new json::object ();
357760c2415Smrg 	    json_item->set ("expr", new json::string (item->get_text ()));
358760c2415Smrg 
359760c2415Smrg 	    /* Capture any location for the node.  */
360760c2415Smrg 	    if (LOCATION_LOCUS (item->get_location ()) != UNKNOWN_LOCATION)
361760c2415Smrg 	      json_item->set ("location",
362760c2415Smrg 			      location_to_json (item->get_location ()));
363760c2415Smrg 
364760c2415Smrg 	    message->append (json_item);
365760c2415Smrg 	  }
366760c2415Smrg 	  break;
367760c2415Smrg 	case OPTINFO_ITEM_KIND_GIMPLE:
368760c2415Smrg 	  {
369760c2415Smrg 	    json::object *json_item = new json::object ();
370760c2415Smrg 	    json_item->set ("stmt", new json::string (item->get_text ()));
371760c2415Smrg 
372760c2415Smrg 	    /* Capture any location for the stmt.  */
373760c2415Smrg 	    if (LOCATION_LOCUS (item->get_location ()) != UNKNOWN_LOCATION)
374760c2415Smrg 	      json_item->set ("location",
375760c2415Smrg 			      location_to_json (item->get_location ()));
376760c2415Smrg 
377760c2415Smrg 	    message->append (json_item);
378760c2415Smrg 	  }
379760c2415Smrg 	  break;
380760c2415Smrg 	case OPTINFO_ITEM_KIND_SYMTAB_NODE:
381760c2415Smrg 	  {
382760c2415Smrg 	    json::object *json_item = new json::object ();
383760c2415Smrg 	    json_item->set ("symtab_node", new json::string (item->get_text ()));
384760c2415Smrg 
385760c2415Smrg 	    /* Capture any location for the node.  */
386760c2415Smrg 	    if (LOCATION_LOCUS (item->get_location ()) != UNKNOWN_LOCATION)
387760c2415Smrg 	      json_item->set ("location",
388760c2415Smrg 			      location_to_json (item->get_location ()));
389760c2415Smrg 	    message->append (json_item);
390760c2415Smrg 	  }
391760c2415Smrg 	  break;
392760c2415Smrg 	}
393760c2415Smrg    }
394760c2415Smrg 
395760c2415Smrg   if (optinfo->get_pass ())
396760c2415Smrg     obj->set ("pass", get_id_value_for_pass (optinfo->get_pass ()));
397760c2415Smrg 
398760c2415Smrg   profile_count count = optinfo->get_count ();
399760c2415Smrg   if (count.initialized_p ())
400760c2415Smrg     obj->set ("count", profile_count_to_json (count));
401760c2415Smrg 
402760c2415Smrg   /* Record any location, handling the case where of an UNKNOWN_LOCATION
403760c2415Smrg      within an inlined block.  */
404760c2415Smrg   location_t loc = optinfo->get_location_t ();
405760c2415Smrg   if (get_pure_location (line_table, loc) != UNKNOWN_LOCATION)
406760c2415Smrg     {
407760c2415Smrg       // TOOD: record the location (just caret for now)
408760c2415Smrg       // TODO: start/finish also?
409760c2415Smrg       obj->set ("location", location_to_json (loc));
410760c2415Smrg     }
411760c2415Smrg 
412760c2415Smrg   if (current_function_decl)
413760c2415Smrg     {
414760c2415Smrg       const char *fnname
415760c2415Smrg 	= IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (current_function_decl));
416760c2415Smrg       obj->set ("function", new json::string (fnname));
417760c2415Smrg     }
418760c2415Smrg 
419760c2415Smrg   if (loc != UNKNOWN_LOCATION)
420760c2415Smrg     obj->set ("inlining_chain", inlining_chain_to_json (loc));
421760c2415Smrg 
422760c2415Smrg   return obj;
423760c2415Smrg }
424760c2415Smrg 
425760c2415Smrg /* Add a json description of PASS and its siblings to ARR, recursing into
426760c2415Smrg    child passes (adding their descriptions within a "children" array).  */
427760c2415Smrg 
428760c2415Smrg void
add_pass_list(json::array * arr,opt_pass * pass)429760c2415Smrg optrecord_json_writer::add_pass_list (json::array *arr, opt_pass *pass)
430760c2415Smrg {
431760c2415Smrg   do
432760c2415Smrg     {
433760c2415Smrg       json::object *pass_obj = pass_to_json (pass);
434760c2415Smrg       arr->append (pass_obj);
435760c2415Smrg       if (pass->sub)
436760c2415Smrg 	{
437760c2415Smrg 	  json::array *sub = new json::array ();
438760c2415Smrg 	  pass_obj->set ("children", sub);
439760c2415Smrg 	  add_pass_list (sub, pass->sub);
440760c2415Smrg 	}
441760c2415Smrg       pass = pass->next;
442760c2415Smrg     }
443760c2415Smrg   while (pass);
444760c2415Smrg }
445760c2415Smrg 
446760c2415Smrg #if CHECKING_P
447760c2415Smrg 
448760c2415Smrg namespace selftest {
449760c2415Smrg 
450760c2415Smrg /* Verify that we can build a JSON optimization record from dump_*
451760c2415Smrg    calls.  */
452760c2415Smrg 
453760c2415Smrg static void
test_building_json_from_dump_calls()454760c2415Smrg test_building_json_from_dump_calls ()
455760c2415Smrg {
456760c2415Smrg   temp_dump_context tmp (true, true, MSG_NOTE);
457760c2415Smrg   dump_user_location_t loc;
458760c2415Smrg   dump_printf_loc (MSG_NOTE, loc, "test of tree: ");
459760c2415Smrg   dump_generic_expr (MSG_NOTE, TDF_SLIM, integer_zero_node);
460760c2415Smrg   optinfo *info = tmp.get_pending_optinfo ();
461760c2415Smrg   ASSERT_TRUE (info != NULL);
462760c2415Smrg   ASSERT_EQ (info->num_items (), 2);
463760c2415Smrg 
464760c2415Smrg   optrecord_json_writer writer;
465760c2415Smrg   json::object *json_obj = writer.optinfo_to_json (info);
466760c2415Smrg   ASSERT_TRUE (json_obj != NULL);
467760c2415Smrg 
468760c2415Smrg   /* Verify that the json is sane.  */
469760c2415Smrg   pretty_printer pp;
470760c2415Smrg   json_obj->print (&pp);
471760c2415Smrg   const char *json_str = pp_formatted_text (&pp);
472760c2415Smrg   ASSERT_STR_CONTAINS (json_str, "impl_location");
473760c2415Smrg   ASSERT_STR_CONTAINS (json_str, "\"kind\": \"note\"");
474760c2415Smrg   ASSERT_STR_CONTAINS (json_str,
475760c2415Smrg 		       "\"message\": [\"test of tree: \", {\"expr\": \"0\"}]");
476760c2415Smrg   delete json_obj;
477760c2415Smrg }
478760c2415Smrg 
479760c2415Smrg /* Run all of the selftests within this file.  */
480760c2415Smrg 
481760c2415Smrg void
optinfo_emit_json_cc_tests()482760c2415Smrg optinfo_emit_json_cc_tests ()
483760c2415Smrg {
484760c2415Smrg   test_building_json_from_dump_calls ();
485760c2415Smrg }
486760c2415Smrg 
487760c2415Smrg } // namespace selftest
488760c2415Smrg 
489760c2415Smrg #endif /* CHECKING_P */
490