Home | History | Annotate | Line # | Download | only in gcc
optinfo-emit-json.cc revision 1.1.1.3
      1 /* Emit optimization information as JSON files.
      2    Copyright (C) 2018-2022 Free Software Foundation, Inc.
      3    Contributed by David Malcolm <dmalcolm (at) redhat.com>.
      4 
      5 This file is part of GCC.
      6 
      7 GCC is free software; you can redistribute it and/or modify it under
      8 the terms of the GNU General Public License as published by the Free
      9 Software Foundation; either version 3, or (at your option) any later
     10 version.
     11 
     12 GCC is distributed in the hope that it will be useful, but WITHOUT ANY
     13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
     14 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
     15 for more details.
     16 
     17 You should have received a copy of the GNU General Public License
     18 along with GCC; see the file COPYING3.  If not see
     19 <http://www.gnu.org/licenses/>.  */
     20 
     21 #include "config.h"
     22 #include "system.h"
     23 #include "coretypes.h"
     24 
     25 #include "backend.h"
     26 #include "tree.h"
     27 #include "gimple.h"
     28 #include "diagnostic-core.h"
     29 
     30 #include "profile.h"
     31 #include "output.h"
     32 #include "tree-pass.h"
     33 
     34 #include "optinfo.h"
     35 #include "optinfo-emit-json.h"
     36 #include "json.h"
     37 #include "pretty-print.h"
     38 #include "tree-pretty-print.h"
     39 #include "gimple-pretty-print.h"
     40 #include "cgraph.h"
     41 
     42 #include "langhooks.h"
     43 #include "version.h"
     44 #include "context.h"
     45 #include "pass_manager.h"
     46 #include "selftest.h"
     47 #include "dump-context.h"
     48 #include <zlib.h>
     49 
     50 /* optrecord_json_writer's ctor.  Populate the top-level parts of the
     51    in-memory JSON representation.  */
     52 
     53 optrecord_json_writer::optrecord_json_writer ()
     54   : m_root_tuple (NULL), m_scopes ()
     55 {
     56   m_root_tuple = new json::array ();
     57 
     58   /* Populate with metadata; compare with toplev.cc: print_version.  */
     59   json::object *metadata = new json::object ();
     60   m_root_tuple->append (metadata);
     61   metadata->set ("format", new json::string ("1"));
     62   json::object *generator = new json::object ();
     63   metadata->set ("generator", generator);
     64   generator->set ("name", new json::string (lang_hooks.name));
     65   generator->set ("pkgversion", new json::string (pkgversion_string));
     66   generator->set ("version", new json::string (version_string));
     67   /* TARGET_NAME is passed in by the Makefile.  */
     68   generator->set ("target", new json::string (TARGET_NAME));
     69 
     70   /* TODO: capture command-line?
     71      see gen_producer_string in dwarf2out.cc (currently static).  */
     72 
     73   /* TODO: capture "any plugins?" flag (or the plugins themselves).  */
     74 
     75   json::array *passes = new json::array ();
     76   m_root_tuple->append (passes);
     77 
     78   /* Call add_pass_list for all of the pass lists.  */
     79   {
     80 #define DEF_PASS_LIST(LIST) \
     81     add_pass_list (passes, g->get_passes ()->LIST);
     82     GCC_PASS_LISTS
     83 #undef DEF_PASS_LIST
     84   }
     85 
     86   json::array *records = new json::array ();
     87   m_root_tuple->append (records);
     88 
     89   m_scopes.safe_push (records);
     90 }
     91 
     92 /* optrecord_json_writer's ctor.
     93    Delete the in-memory JSON representation.  */
     94 
     95 optrecord_json_writer::~optrecord_json_writer ()
     96 {
     97   delete m_root_tuple;
     98 }
     99 
    100 /* Choose an appropriate filename, and write the saved records to it.  */
    101 
    102 void
    103 optrecord_json_writer::write () const
    104 {
    105   pretty_printer pp;
    106   m_root_tuple->print (&pp);
    107 
    108   bool emitted_error = false;
    109   char *filename = concat (dump_base_name, ".opt-record.json.gz", NULL);
    110   gzFile outfile = gzopen (filename, "w");
    111   if (outfile == NULL)
    112     {
    113       error_at (UNKNOWN_LOCATION, "cannot open file %qs for writing optimization records",
    114 		filename); // FIXME: more info?
    115       goto cleanup;
    116     }
    117 
    118   if (gzputs (outfile, pp_formatted_text (&pp)) <= 0)
    119     {
    120       int tmp;
    121       error_at (UNKNOWN_LOCATION, "error writing optimization records to %qs: %s",
    122 		filename, gzerror (outfile, &tmp));
    123       emitted_error = true;
    124     }
    125 
    126  cleanup:
    127   if (outfile)
    128     if (gzclose (outfile) != Z_OK)
    129       if (!emitted_error)
    130 	error_at (UNKNOWN_LOCATION, "error closing optimization records %qs",
    131 		  filename);
    132 
    133   free (filename);
    134 }
    135 
    136 /* Add a record for OPTINFO to the queue of records to be written.  */
    137 
    138 void
    139 optrecord_json_writer::add_record (const optinfo *optinfo)
    140 {
    141   json::object *obj = optinfo_to_json (optinfo);
    142 
    143   add_record (obj);
    144 
    145   /* Potentially push the scope.  */
    146   if (optinfo->get_kind () == OPTINFO_KIND_SCOPE)
    147     {
    148       json::array *children = new json::array ();
    149       obj->set ("children", children);
    150       m_scopes.safe_push (children);
    151     }
    152 }
    153 
    154 /* Private methods of optrecord_json_writer.  */
    155 
    156 /* Add record OBJ to the innermost scope.  */
    157 
    158 void
    159 optrecord_json_writer::add_record (json::object *obj)
    160 {
    161   /* Add to innermost scope.  */
    162   gcc_assert (m_scopes.length () > 0);
    163   m_scopes[m_scopes.length () - 1]->append (obj);
    164 }
    165 
    166 /* Pop the innermost scope.  */
    167 
    168 void
    169 optrecord_json_writer::pop_scope ()
    170 {
    171   m_scopes.pop ();
    172 
    173   /* We should never pop the top-level records array.  */
    174   gcc_assert (m_scopes.length () > 0);
    175 }
    176 
    177 /* Create a JSON object representing LOC.  */
    178 
    179 json::object *
    180 optrecord_json_writer::impl_location_to_json (dump_impl_location_t loc)
    181 {
    182   json::object *obj = new json::object ();
    183   obj->set ("file", new json::string (loc.m_file));
    184   obj->set ("line", new json::integer_number (loc.m_line));
    185   if (loc.m_function)
    186     obj->set ("function", new json::string (loc.m_function));
    187   return obj;
    188 }
    189 
    190 /* Create a JSON object representing LOC.  */
    191 
    192 json::object *
    193 optrecord_json_writer::location_to_json (location_t loc)
    194 {
    195   gcc_assert (LOCATION_LOCUS (loc) != UNKNOWN_LOCATION);
    196   expanded_location exploc = expand_location (loc);
    197   json::object *obj = new json::object ();
    198   obj->set ("file", new json::string (exploc.file));
    199   obj->set ("line", new json::integer_number (exploc.line));
    200   obj->set ("column", new json::integer_number (exploc.column));
    201   return obj;
    202 }
    203 
    204 /* Create a JSON object representing COUNT.  */
    205 
    206 json::object *
    207 optrecord_json_writer::profile_count_to_json (profile_count count)
    208 {
    209   json::object *obj = new json::object ();
    210   obj->set ("value", new json::integer_number (count.to_gcov_type ()));
    211   obj->set ("quality",
    212 	    new json::string (profile_quality_as_string (count.quality ())));
    213   return obj;
    214 }
    215 
    216 /* Get a string for use when referring to PASS in the saved optimization
    217    records.  */
    218 
    219 json::string *
    220 optrecord_json_writer::get_id_value_for_pass (opt_pass *pass)
    221 {
    222   pretty_printer pp;
    223   /* this is host-dependent, but will be consistent for a given host.  */
    224   pp_pointer (&pp, static_cast<void *> (pass));
    225   return new json::string (pp_formatted_text (&pp));
    226 }
    227 
    228 /* Create a JSON object representing PASS.  */
    229 
    230 json::object *
    231 optrecord_json_writer::pass_to_json (opt_pass *pass)
    232 {
    233   json::object *obj = new json::object ();
    234   const char *type = NULL;
    235   switch (pass->type)
    236     {
    237     default:
    238       gcc_unreachable ();
    239     case GIMPLE_PASS:
    240       type = "gimple";
    241       break;
    242     case RTL_PASS:
    243       type = "rtl";
    244       break;
    245     case SIMPLE_IPA_PASS:
    246       type = "simple_ipa";
    247       break;
    248     case IPA_PASS:
    249       type = "ipa";
    250       break;
    251     }
    252   obj->set ("id", get_id_value_for_pass (pass));
    253   obj->set ("type", new json::string (type));
    254   obj->set ("name", new json::string (pass->name));
    255   /* Represent the optgroup flags as an array.  */
    256   {
    257     json::array *optgroups = new json::array ();
    258     obj->set ("optgroups", optgroups);
    259     for (const kv_pair<optgroup_flags_t> *optgroup = optgroup_options;
    260 	 optgroup->name != NULL; optgroup++)
    261       if (optgroup->value != OPTGROUP_ALL
    262 	  && (pass->optinfo_flags & optgroup->value))
    263 	optgroups->append (new json::string (optgroup->name));
    264   }
    265   obj->set ("num", new json::integer_number (pass->static_pass_number));
    266   return obj;
    267 }
    268 
    269 /* Create a JSON array for LOC representing the chain of inlining
    270    locations.
    271    Compare with lhd_print_error_function and cp_print_error_function.  */
    272 
    273 json::value *
    274 optrecord_json_writer::inlining_chain_to_json (location_t loc)
    275 {
    276   json::array *array = new json::array ();
    277 
    278   tree abstract_origin = LOCATION_BLOCK (loc);
    279 
    280   while (abstract_origin)
    281     {
    282       location_t *locus;
    283       tree block = abstract_origin;
    284 
    285       locus = &BLOCK_SOURCE_LOCATION (block);
    286       tree fndecl = NULL;
    287       block = BLOCK_SUPERCONTEXT (block);
    288       while (block && TREE_CODE (block) == BLOCK
    289 	     && BLOCK_ABSTRACT_ORIGIN (block))
    290 	{
    291 	  tree ao = BLOCK_ABSTRACT_ORIGIN (block);
    292 	  if (TREE_CODE (ao) == FUNCTION_DECL)
    293 	    {
    294 	      fndecl = ao;
    295 	      break;
    296 	    }
    297 	  else if (TREE_CODE (ao) != BLOCK)
    298 	    break;
    299 
    300 	  block = BLOCK_SUPERCONTEXT (block);
    301 	}
    302       if (fndecl)
    303 	abstract_origin = block;
    304       else
    305 	{
    306 	  while (block && TREE_CODE (block) == BLOCK)
    307 	    block = BLOCK_SUPERCONTEXT (block);
    308 
    309 	  if (block && TREE_CODE (block) == FUNCTION_DECL)
    310 	    fndecl = block;
    311 	  abstract_origin = NULL;
    312 	}
    313       if (fndecl)
    314 	{
    315 	  json::object *obj = new json::object ();
    316 	  const char *printable_name
    317 	    = lang_hooks.decl_printable_name (fndecl, 2);
    318 	  obj->set ("fndecl", new json::string (printable_name));
    319 	  if (LOCATION_LOCUS (*locus) != UNKNOWN_LOCATION)
    320 	    obj->set ("site", location_to_json (*locus));
    321 	  array->append (obj);
    322 	}
    323     }
    324 
    325   return array;
    326 }
    327 
    328 /* Create a JSON object representing OPTINFO.  */
    329 
    330 json::object *
    331 optrecord_json_writer::optinfo_to_json (const optinfo *optinfo)
    332 {
    333   json::object *obj = new json::object ();
    334 
    335   obj->set ("impl_location",
    336 	    impl_location_to_json (optinfo->get_impl_location ()));
    337 
    338   const char *kind_str = optinfo_kind_to_string (optinfo->get_kind ());
    339   obj->set ("kind", new json::string (kind_str));
    340   json::array *message = new json::array ();
    341   obj->set ("message", message);
    342   for (unsigned i = 0; i < optinfo->num_items (); i++)
    343     {
    344       const optinfo_item *item = optinfo->get_item (i);
    345       switch (item->get_kind ())
    346 	{
    347 	default:
    348 	  gcc_unreachable ();
    349 	case OPTINFO_ITEM_KIND_TEXT:
    350 	  {
    351 	    message->append (new json::string (item->get_text ()));
    352 	  }
    353 	  break;
    354 	case OPTINFO_ITEM_KIND_TREE:
    355 	  {
    356 	    json::object *json_item = new json::object ();
    357 	    json_item->set ("expr", new json::string (item->get_text ()));
    358 
    359 	    /* Capture any location for the node.  */
    360 	    if (LOCATION_LOCUS (item->get_location ()) != UNKNOWN_LOCATION)
    361 	      json_item->set ("location",
    362 			      location_to_json (item->get_location ()));
    363 
    364 	    message->append (json_item);
    365 	  }
    366 	  break;
    367 	case OPTINFO_ITEM_KIND_GIMPLE:
    368 	  {
    369 	    json::object *json_item = new json::object ();
    370 	    json_item->set ("stmt", new json::string (item->get_text ()));
    371 
    372 	    /* Capture any location for the stmt.  */
    373 	    if (LOCATION_LOCUS (item->get_location ()) != UNKNOWN_LOCATION)
    374 	      json_item->set ("location",
    375 			      location_to_json (item->get_location ()));
    376 
    377 	    message->append (json_item);
    378 	  }
    379 	  break;
    380 	case OPTINFO_ITEM_KIND_SYMTAB_NODE:
    381 	  {
    382 	    json::object *json_item = new json::object ();
    383 	    json_item->set ("symtab_node", new json::string (item->get_text ()));
    384 
    385 	    /* Capture any location for the node.  */
    386 	    if (LOCATION_LOCUS (item->get_location ()) != UNKNOWN_LOCATION)
    387 	      json_item->set ("location",
    388 			      location_to_json (item->get_location ()));
    389 	    message->append (json_item);
    390 	  }
    391 	  break;
    392 	}
    393    }
    394 
    395   if (optinfo->get_pass ())
    396     obj->set ("pass", get_id_value_for_pass (optinfo->get_pass ()));
    397 
    398   profile_count count = optinfo->get_count ();
    399   if (count.initialized_p ())
    400     obj->set ("count", profile_count_to_json (count));
    401 
    402   /* Record any location, handling the case where of an UNKNOWN_LOCATION
    403      within an inlined block.  */
    404   location_t loc = optinfo->get_location_t ();
    405   if (get_pure_location (line_table, loc) != UNKNOWN_LOCATION)
    406     {
    407       // TOOD: record the location (just caret for now)
    408       // TODO: start/finish also?
    409       obj->set ("location", location_to_json (loc));
    410     }
    411 
    412   if (current_function_decl)
    413     {
    414       const char *fnname
    415 	= IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (current_function_decl));
    416       obj->set ("function", new json::string (fnname));
    417     }
    418 
    419   if (loc != UNKNOWN_LOCATION)
    420     obj->set ("inlining_chain", inlining_chain_to_json (loc));
    421 
    422   return obj;
    423 }
    424 
    425 /* Add a json description of PASS and its siblings to ARR, recursing into
    426    child passes (adding their descriptions within a "children" array).  */
    427 
    428 void
    429 optrecord_json_writer::add_pass_list (json::array *arr, opt_pass *pass)
    430 {
    431   do
    432     {
    433       json::object *pass_obj = pass_to_json (pass);
    434       arr->append (pass_obj);
    435       if (pass->sub)
    436 	{
    437 	  json::array *sub = new json::array ();
    438 	  pass_obj->set ("children", sub);
    439 	  add_pass_list (sub, pass->sub);
    440 	}
    441       pass = pass->next;
    442     }
    443   while (pass);
    444 }
    445 
    446 #if CHECKING_P
    447 
    448 namespace selftest {
    449 
    450 /* Verify that we can build a JSON optimization record from dump_*
    451    calls.  */
    452 
    453 static void
    454 test_building_json_from_dump_calls ()
    455 {
    456   temp_dump_context tmp (true, true, MSG_NOTE);
    457   dump_user_location_t loc;
    458   dump_printf_loc (MSG_NOTE, loc, "test of tree: ");
    459   dump_generic_expr (MSG_NOTE, TDF_SLIM, integer_zero_node);
    460   optinfo *info = tmp.get_pending_optinfo ();
    461   ASSERT_TRUE (info != NULL);
    462   ASSERT_EQ (info->num_items (), 2);
    463 
    464   optrecord_json_writer writer;
    465   json::object *json_obj = writer.optinfo_to_json (info);
    466   ASSERT_TRUE (json_obj != NULL);
    467 
    468   /* Verify that the json is sane.  */
    469   pretty_printer pp;
    470   json_obj->print (&pp);
    471   const char *json_str = pp_formatted_text (&pp);
    472   ASSERT_STR_CONTAINS (json_str, "impl_location");
    473   ASSERT_STR_CONTAINS (json_str, "\"kind\": \"note\"");
    474   ASSERT_STR_CONTAINS (json_str,
    475 		       "\"message\": [\"test of tree: \", {\"expr\": \"0\"}]");
    476   delete json_obj;
    477 }
    478 
    479 /* Run all of the selftests within this file.  */
    480 
    481 void
    482 optinfo_emit_json_cc_tests ()
    483 {
    484   test_building_json_from_dump_calls ();
    485 }
    486 
    487 } // namespace selftest
    488 
    489 #endif /* CHECKING_P */
    490