LCOV - code coverage report
Current view: top level - gcc - optinfo-emit-json.cc (source / functions) Coverage Total Hit
Test: gcc.info Lines: 94.4 % 231 218
Test Date: 2026-02-28 14:20:25 Functions: 100.0 % 16 16
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /* Emit optimization information as JSON files.
       2              :    Copyright (C) 2018-2026 Free Software Foundation, Inc.
       3              :    Contributed by David Malcolm <dmalcolm@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         1288 : optrecord_json_writer::optrecord_json_writer ()
      54         1288 :   : m_root_tuple (NULL), m_scopes ()
      55              : {
      56         1288 :   m_root_tuple = new json::array ();
      57              : 
      58              :   /* Populate with metadata; compare with toplev.cc: print_version.  */
      59         1288 :   json::object *metadata = new json::object ();
      60         1288 :   m_root_tuple->append (metadata);
      61         1288 :   metadata->set_string ("format", "1");
      62         1288 :   json::object *generator = new json::object ();
      63         1288 :   metadata->set ("generator", generator);
      64         1288 :   generator->set_string ("name", lang_hooks.name);
      65         1288 :   generator->set_string ("pkgversion", pkgversion_string);
      66         1288 :   generator->set_string ("version", version_string);
      67              :   /* TARGET_NAME is passed in by the Makefile.  */
      68         1288 :   generator->set_string ("target", 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         1288 :   json::array *passes = new json::array ();
      76         1288 :   m_root_tuple->append (passes);
      77              : 
      78              :   /* Call add_pass_list for all of the pass lists.  */
      79         1288 :   {
      80              : #define DEF_PASS_LIST(LIST) \
      81              :     add_pass_list (passes, g->get_passes ()->LIST);
      82         1288 :     GCC_PASS_LISTS
      83              : #undef DEF_PASS_LIST
      84              :   }
      85              : 
      86         1288 :   json::array *records = new json::array ();
      87         1288 :   m_root_tuple->append (records);
      88              : 
      89         1288 :   m_scopes.safe_push (records);
      90         1288 : }
      91              : 
      92              : /* optrecord_json_writer's ctor.
      93              :    Delete the in-memory JSON representation.  */
      94              : 
      95         1288 : optrecord_json_writer::~optrecord_json_writer ()
      96              : {
      97         1288 :   delete m_root_tuple;
      98         1288 : }
      99              : 
     100              : /* Choose an appropriate filename, and write the saved records to it.  */
     101              : 
     102              : void
     103           41 : optrecord_json_writer::write () const
     104              : {
     105           41 :   pretty_printer pp;
     106           41 :   m_root_tuple->print (&pp, false);
     107              : 
     108           41 :   bool emitted_error = false;
     109           41 :   char *filename = concat (dump_base_name, ".opt-record.json.gz", NULL);
     110           41 :   gzFile outfile = gzopen (filename, "w");
     111           41 :   if (outfile == NULL)
     112              :     {
     113            0 :       error_at (UNKNOWN_LOCATION, "cannot open file %qs for writing optimization records",
     114              :                 filename); // FIXME: more info?
     115            0 :       goto cleanup;
     116              :     }
     117              : 
     118           41 :   if (gzputs (outfile, pp_formatted_text (&pp)) <= 0)
     119              :     {
     120            0 :       int tmp;
     121            0 :       error_at (UNKNOWN_LOCATION, "error writing optimization records to %qs: %s",
     122              :                 filename, gzerror (outfile, &tmp));
     123            0 :       emitted_error = true;
     124              :     }
     125              : 
     126           41 :  cleanup:
     127           41 :   if (outfile)
     128           41 :     if (gzclose (outfile) != Z_OK)
     129            0 :       if (!emitted_error)
     130            0 :         error_at (UNKNOWN_LOCATION, "error closing optimization records %qs",
     131              :                   filename);
     132              : 
     133           41 :   free (filename);
     134           41 : }
     135              : 
     136              : /* Add a record for OPTINFO to the queue of records to be written.  */
     137              : 
     138              : void
     139        42776 : optrecord_json_writer::add_record (const optinfo *optinfo)
     140              : {
     141        42776 :   json::object *obj = optinfo_to_json (optinfo);
     142              : 
     143        42776 :   add_record (obj);
     144              : 
     145              :   /* Potentially push the scope.  */
     146        42776 :   if (optinfo->get_kind () == optinfo::kind::scope)
     147              :     {
     148         3916 :       json::array *children = new json::array ();
     149         3916 :       obj->set ("children", children);
     150         3916 :       m_scopes.safe_push (children);
     151              :     }
     152        42776 : }
     153              : 
     154              : /* Private methods of optrecord_json_writer.  */
     155              : 
     156              : /* Add record OBJ to the innermost scope.  */
     157              : 
     158              : void
     159        42776 : optrecord_json_writer::add_record (json::object *obj)
     160              : {
     161              :   /* Add to innermost scope.  */
     162        42776 :   gcc_assert (m_scopes.length () > 0);
     163        42776 :   m_scopes[m_scopes.length () - 1]->append (obj);
     164        42776 : }
     165              : 
     166              : /* Pop the innermost scope.  */
     167              : 
     168              : void
     169         3916 : optrecord_json_writer::pop_scope ()
     170              : {
     171         3916 :   m_scopes.pop ();
     172              : 
     173              :   /* We should never pop the top-level records array.  */
     174         3916 :   gcc_assert (m_scopes.length () > 0);
     175         3916 : }
     176              : 
     177              : /* Create a JSON object representing LOC.  */
     178              : 
     179              : json::object *
     180        42780 : optrecord_json_writer::impl_location_to_json (dump_impl_location_t loc)
     181              : {
     182        42780 :   json::object *obj = new json::object ();
     183        42780 :   obj->set_string ("file", loc.m_file);
     184        42780 :   obj->set_integer ("line", loc.m_line);
     185        42780 :   if (loc.m_function)
     186        42780 :     obj->set_string ("function", loc.m_function);
     187        42780 :   return obj;
     188              : }
     189              : 
     190              : /* Create a JSON object representing LOC.  */
     191              : 
     192              : json::object *
     193        34166 : optrecord_json_writer::location_to_json (location_t loc)
     194              : {
     195        34166 :   gcc_assert (LOCATION_LOCUS (loc) != UNKNOWN_LOCATION);
     196        34166 :   expanded_location exploc = expand_location (loc);
     197        34166 :   json::object *obj = new json::object ();
     198        34166 :   obj->set_string ("file", exploc.file);
     199        34166 :   obj->set_integer ("line", exploc.line);
     200        34166 :   obj->set_integer ("column", exploc.column);
     201        34166 :   return obj;
     202              : }
     203              : 
     204              : /* Create a JSON object representing COUNT.  */
     205              : 
     206              : json::object *
     207        42720 : optrecord_json_writer::profile_count_to_json (profile_count count)
     208              : {
     209        42720 :   json::object *obj = new json::object ();
     210        42720 :   obj->set_integer ("value", count.to_gcov_type ());
     211        42720 :   obj->set_string ("quality", profile_quality_as_string (count.quality ()));
     212        42720 :   return obj;
     213              : }
     214              : 
     215              : /* Get a string for use when referring to PASS in the saved optimization
     216              :    records.  */
     217              : 
     218              : json::string *
     219       547720 : optrecord_json_writer::get_id_value_for_pass (opt_pass *pass)
     220              : {
     221       547720 :   pretty_printer pp;
     222              :   /* this is host-dependent, but will be consistent for a given host.  */
     223       547720 :   pp_pointer (&pp, static_cast<void *> (pass));
     224       547720 :   return new json::string (pp_formatted_text (&pp));
     225       547720 : }
     226              : 
     227              : /* Create a JSON object representing PASS.  */
     228              : 
     229              : json::object *
     230       507472 : optrecord_json_writer::pass_to_json (opt_pass *pass)
     231              : {
     232       507472 :   json::object *obj = new json::object ();
     233       507472 :   const char *type = NULL;
     234       507472 :   switch (pass->type)
     235              :     {
     236            0 :     default:
     237            0 :       gcc_unreachable ();
     238              :     case GIMPLE_PASS:
     239              :       type = "gimple";
     240              :       break;
     241       136528 :     case RTL_PASS:
     242       136528 :       type = "rtl";
     243       136528 :       break;
     244        28336 :     case SIMPLE_IPA_PASS:
     245        28336 :       type = "simple_ipa";
     246        28336 :       break;
     247        24472 :     case IPA_PASS:
     248        24472 :       type = "ipa";
     249        24472 :       break;
     250              :     }
     251       507472 :   obj->set ("id", get_id_value_for_pass (pass));
     252       507472 :   obj->set_string ("type", type);
     253       507472 :   obj->set_string ("name", pass->name);
     254              :   /* Represent the optgroup flags as an array.  */
     255       507472 :   {
     256       507472 :     json::array *optgroups = new json::array ();
     257       507472 :     obj->set ("optgroups", optgroups);
     258      3552304 :     for (const kv_pair<optgroup_flags_t> *optgroup = optgroup_options;
     259      3552304 :          optgroup->name != NULL; optgroup++)
     260      3044832 :       if (optgroup->value != OPTGROUP_ALL
     261      2537360 :           && (pass->optinfo_flags & optgroup->value))
     262        91448 :         optgroups->append_string (optgroup->name);
     263              :   }
     264       507472 :   obj->set_integer ("num", pass->static_pass_number);
     265       507472 :   return obj;
     266              : }
     267              : 
     268              : /* Create a JSON array for LOC representing the chain of inlining
     269              :    locations.
     270              :    Compare with lhd_print_error_function and cp_print_error_function.  */
     271              : 
     272              : json::value *
     273        27752 : optrecord_json_writer::inlining_chain_to_json (location_t loc)
     274              : {
     275        27752 :   json::array *array = new json::array ();
     276              : 
     277        27752 :   tree abstract_origin = LOCATION_BLOCK (loc);
     278              : 
     279        54327 :   while (abstract_origin)
     280              :     {
     281        26575 :       location_t *locus;
     282        26575 :       tree block = abstract_origin;
     283              : 
     284        26575 :       locus = &BLOCK_SOURCE_LOCATION (block);
     285        26575 :       tree fndecl = NULL;
     286        26575 :       block = BLOCK_SUPERCONTEXT (block);
     287        54591 :       while (block && TREE_CODE (block) == BLOCK
     288        39775 :              && BLOCK_ABSTRACT_ORIGIN (block))
     289              :         {
     290         2945 :           tree ao = BLOCK_ABSTRACT_ORIGIN (block);
     291         2945 :           if (TREE_CODE (ao) == FUNCTION_DECL)
     292              :             {
     293              :               fndecl = ao;
     294              :               break;
     295              :             }
     296         1441 :           else if (TREE_CODE (ao) != BLOCK)
     297              :             break;
     298              : 
     299         1441 :           block = BLOCK_SUPERCONTEXT (block);
     300              :         }
     301        26575 :       if (fndecl)
     302              :         abstract_origin = block;
     303              :       else
     304              :         {
     305        42776 :           while (block && TREE_CODE (block) == BLOCK)
     306        17705 :             block = BLOCK_SUPERCONTEXT (block);
     307              : 
     308        25071 :           if (block && TREE_CODE (block) == FUNCTION_DECL)
     309              :             fndecl = block;
     310              :           abstract_origin = NULL;
     311              :         }
     312              :       if (fndecl)
     313              :         {
     314        26575 :           json::object *obj = new json::object ();
     315        26575 :           const char *printable_name
     316        26575 :             = lang_hooks.decl_printable_name (fndecl, 2);
     317        26575 :           obj->set_string ("fndecl", printable_name);
     318        26575 :           if (LOCATION_LOCUS (*locus) != UNKNOWN_LOCATION)
     319         1504 :             obj->set ("site", location_to_json (*locus));
     320        26575 :           array->append (obj);
     321              :         }
     322              :     }
     323              : 
     324        27752 :   return array;
     325              : }
     326              : 
     327              : /* Create a JSON object representing OPTINFO.  */
     328              : 
     329              : json::object *
     330        42780 : optrecord_json_writer::optinfo_to_json (const optinfo *optinfo)
     331              : {
     332        42780 :   json::object *obj = new json::object ();
     333              : 
     334        42780 :   obj->set ("impl_location",
     335        42780 :             impl_location_to_json (optinfo->get_impl_location ()));
     336              : 
     337        42780 :   const char *kind_str = optinfo::kind_to_string (optinfo->get_kind ());
     338        42780 :   obj->set_string ("kind", kind_str);
     339        42780 :   json::array *message = new json::array ();
     340        42780 :   obj->set ("message", message);
     341       147063 :   for (unsigned i = 0; i < optinfo->num_items (); i++)
     342              :     {
     343       104283 :       const optinfo_item *item = optinfo->get_item (i);
     344       104283 :       switch (item->get_kind ())
     345              :         {
     346            0 :         default:
     347            0 :           gcc_unreachable ();
     348        75587 :         case optinfo_item::kind::text:
     349        75587 :           {
     350        75587 :             message->append_string (item->get_text ());
     351              :           }
     352        75587 :           break;
     353        10075 :         case optinfo_item::kind::tree:
     354        10075 :           {
     355        10075 :             json::object *json_item = new json::object ();
     356        10075 :             json_item->set_string ("expr", item->get_text ());
     357              : 
     358              :             /* Capture any location for the node.  */
     359        10075 :             if (LOCATION_LOCUS (item->get_location ()) != UNKNOWN_LOCATION)
     360            0 :               json_item->set ("location",
     361            0 :                               location_to_json (item->get_location ()));
     362              : 
     363        10075 :             message->append (json_item);
     364              :           }
     365        10075 :           break;
     366        18359 :         case optinfo_item::kind::gimple:
     367        18359 :           {
     368        18359 :             json::object *json_item = new json::object ();
     369        18359 :             json_item->set_string ("stmt", item->get_text ());
     370              : 
     371              :             /* Capture any location for the stmt.  */
     372        18359 :             if (LOCATION_LOCUS (item->get_location ()) != UNKNOWN_LOCATION)
     373         5348 :               json_item->set ("location",
     374         5348 :                               location_to_json (item->get_location ()));
     375              : 
     376        18359 :             message->append (json_item);
     377              :           }
     378        18359 :           break;
     379          262 :         case optinfo_item::kind::symtab_node:
     380          262 :           {
     381          262 :             json::object *json_item = new json::object ();
     382          262 :             json_item->set_string ("symtab_node", item->get_text ());
     383              : 
     384              :             /* Capture any location for the node.  */
     385          262 :             if (LOCATION_LOCUS (item->get_location ()) != UNKNOWN_LOCATION)
     386          262 :               json_item->set ("location",
     387          262 :                               location_to_json (item->get_location ()));
     388          262 :             message->append (json_item);
     389              :           }
     390          262 :           break;
     391              :         }
     392              :    }
     393              : 
     394        42780 :   if (optinfo->get_pass ())
     395        40248 :     obj->set ("pass", get_id_value_for_pass (optinfo->get_pass ()));
     396              : 
     397        42780 :   profile_count count = optinfo->get_count ();
     398        42780 :   if (count.initialized_p ())
     399        42720 :     obj->set ("count", profile_count_to_json (count));
     400              : 
     401              :   /* Record any location, handling the case where of an UNKNOWN_LOCATION
     402              :      within an inlined block.  */
     403        42780 :   location_t loc = optinfo->get_location_t ();
     404        42780 :   if (get_pure_location (line_table, loc) != UNKNOWN_LOCATION)
     405              :     {
     406              :       // TOOD: record the location (just caret for now)
     407              :       // TODO: start/finish also?
     408        27052 :       obj->set ("location", location_to_json (loc));
     409              :     }
     410              : 
     411        42780 :   if (current_function_decl)
     412              :     {
     413        40166 :       const char *fnname
     414        40166 :         = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (current_function_decl));
     415        40166 :       obj->set_string ("function", fnname);
     416              :     }
     417              : 
     418        42780 :   if (loc != UNKNOWN_LOCATION)
     419        27752 :     obj->set ("inlining_chain", inlining_chain_to_json (loc));
     420              : 
     421        42780 :   return obj;
     422              : }
     423              : 
     424              : /* Add a json description of PASS and its siblings to ARR, recursing into
     425              :    child passes (adding their descriptions within a "children" array).  */
     426              : 
     427              : void
     428        33488 : optrecord_json_writer::add_pass_list (json::array *arr, opt_pass *pass)
     429              : {
     430       507472 :   do
     431              :     {
     432       507472 :       json::object *pass_obj = pass_to_json (pass);
     433       507472 :       arr->append (pass_obj);
     434       507472 :       if (pass->sub)
     435              :         {
     436        27048 :           json::array *sub = new json::array ();
     437        27048 :           pass_obj->set ("children", sub);
     438        27048 :           add_pass_list (sub, pass->sub);
     439              :         }
     440       507472 :       pass = pass->next;
     441              :     }
     442       507472 :   while (pass);
     443        33488 : }
     444              : 
     445              : #if CHECKING_P
     446              : 
     447              : namespace selftest {
     448              : 
     449              : /* Verify that we can build a JSON optimization record from dump_*
     450              :    calls.  */
     451              : 
     452              : static void
     453            4 : test_building_json_from_dump_calls ()
     454              : {
     455            4 :   temp_dump_context tmp (true, true, MSG_NOTE);
     456            4 :   dump_user_location_t loc;
     457            4 :   dump_printf_loc (MSG_NOTE, loc, "test of tree: ");
     458            4 :   dump_generic_expr (MSG_NOTE, TDF_SLIM, integer_zero_node);
     459            4 :   optinfo *info = tmp.get_pending_optinfo ();
     460            4 :   ASSERT_TRUE (info != NULL);
     461            4 :   ASSERT_EQ (info->num_items (), 2);
     462              : 
     463            4 :   optrecord_json_writer writer;
     464            4 :   json::object *json_obj = writer.optinfo_to_json (info);
     465            4 :   ASSERT_TRUE (json_obj != NULL);
     466              : 
     467              :   /* Verify that the json is sane.  */
     468            4 :   pretty_printer pp;
     469            4 :   json_obj->print (&pp, false);
     470            4 :   const char *json_str = pp_formatted_text (&pp);
     471            4 :   ASSERT_STR_CONTAINS (json_str, "impl_location");
     472            4 :   ASSERT_STR_CONTAINS (json_str, "\"kind\": \"note\"");
     473            4 :   ASSERT_STR_CONTAINS (json_str,
     474              :                        "\"message\": [\"test of tree: \", {\"expr\": \"0\"}]");
     475            4 :   delete json_obj;
     476            4 : }
     477              : 
     478              : /* Run all of the selftests within this file.  */
     479              : 
     480              : void
     481            4 : optinfo_emit_json_cc_tests ()
     482              : {
     483            4 :   test_building_json_from_dump_calls ();
     484            4 : }
     485              : 
     486              : } // namespace selftest
     487              : 
     488              : #endif /* CHECKING_P */
        

Generated by: LCOV version 2.4-beta

LCOV profile is generated on x86_64 machine using following configure options: configure --disable-bootstrap --enable-coverage=opt --enable-languages=c,c++,fortran,go,jit,lto,rust,m2 --enable-host-shared. GCC test suite is run with the built compiler.