Branch data Line data Source code
1 : : /* Emit optimization information as JSON files.
2 : : Copyright (C) 2018-2024 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 : 49885 : optrecord_json_writer::add_record (const optinfo *optinfo)
140 : : {
141 : 49885 : json::object *obj = optinfo_to_json (optinfo);
142 : :
143 : 49885 : add_record (obj);
144 : :
145 : : /* Potentially push the scope. */
146 : 49885 : if (optinfo->get_kind () == OPTINFO_KIND_SCOPE)
147 : : {
148 : 4177 : json::array *children = new json::array ();
149 : 4177 : obj->set ("children", children);
150 : 4177 : m_scopes.safe_push (children);
151 : : }
152 : 49885 : }
153 : :
154 : : /* Private methods of optrecord_json_writer. */
155 : :
156 : : /* Add record OBJ to the innermost scope. */
157 : :
158 : : void
159 : 49885 : optrecord_json_writer::add_record (json::object *obj)
160 : : {
161 : : /* Add to innermost scope. */
162 : 49885 : gcc_assert (m_scopes.length () > 0);
163 : 49885 : m_scopes[m_scopes.length () - 1]->append (obj);
164 : 49885 : }
165 : :
166 : : /* Pop the innermost scope. */
167 : :
168 : : void
169 : 4177 : optrecord_json_writer::pop_scope ()
170 : : {
171 : 4177 : m_scopes.pop ();
172 : :
173 : : /* We should never pop the top-level records array. */
174 : 4177 : gcc_assert (m_scopes.length () > 0);
175 : 4177 : }
176 : :
177 : : /* Create a JSON object representing LOC. */
178 : :
179 : : json::object *
180 : 49889 : optrecord_json_writer::impl_location_to_json (dump_impl_location_t loc)
181 : : {
182 : 49889 : json::object *obj = new json::object ();
183 : 49889 : obj->set_string ("file", loc.m_file);
184 : 49889 : obj->set_integer ("line", loc.m_line);
185 : 49889 : if (loc.m_function)
186 : 49889 : obj->set_string ("function", loc.m_function);
187 : 49889 : return obj;
188 : : }
189 : :
190 : : /* Create a JSON object representing LOC. */
191 : :
192 : : json::object *
193 : 35387 : optrecord_json_writer::location_to_json (location_t loc)
194 : : {
195 : 35387 : gcc_assert (LOCATION_LOCUS (loc) != UNKNOWN_LOCATION);
196 : 35387 : expanded_location exploc = expand_location (loc);
197 : 35387 : json::object *obj = new json::object ();
198 : 35387 : obj->set_string ("file", exploc.file);
199 : 35387 : obj->set_integer ("line", exploc.line);
200 : 35387 : obj->set_integer ("column", exploc.column);
201 : 35387 : return obj;
202 : : }
203 : :
204 : : /* Create a JSON object representing COUNT. */
205 : :
206 : : json::object *
207 : 49829 : optrecord_json_writer::profile_count_to_json (profile_count count)
208 : : {
209 : 49829 : json::object *obj = new json::object ();
210 : 49829 : obj->set_integer ("value", count.to_gcov_type ());
211 : 49829 : obj->set_string ("quality", profile_quality_as_string (count.quality ()));
212 : 49829 : 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 : 544525 : optrecord_json_writer::get_id_value_for_pass (opt_pass *pass)
220 : : {
221 : 544525 : pretty_printer pp;
222 : : /* this is host-dependent, but will be consistent for a given host. */
223 : 544525 : pp_pointer (&pp, static_cast<void *> (pass));
224 : 544525 : return new json::string (pp_formatted_text (&pp));
225 : 544525 : }
226 : :
227 : : /* Create a JSON object representing PASS. */
228 : :
229 : : json::object *
230 : 497168 : optrecord_json_writer::pass_to_json (opt_pass *pass)
231 : : {
232 : 497168 : json::object *obj = new json::object ();
233 : 497168 : const char *type = NULL;
234 : 497168 : switch (pass->type)
235 : : {
236 : 0 : default:
237 : 0 : gcc_unreachable ();
238 : : case GIMPLE_PASS:
239 : : type = "gimple";
240 : : break;
241 : 131376 : case RTL_PASS:
242 : 131376 : type = "rtl";
243 : 131376 : break;
244 : 25760 : case SIMPLE_IPA_PASS:
245 : 25760 : type = "simple_ipa";
246 : 25760 : break;
247 : 20608 : case IPA_PASS:
248 : 20608 : type = "ipa";
249 : 20608 : break;
250 : : }
251 : 497168 : obj->set ("id", get_id_value_for_pass (pass));
252 : 497168 : obj->set_string ("type", type);
253 : 497168 : obj->set_string ("name", pass->name);
254 : : /* Represent the optgroup flags as an array. */
255 : 497168 : {
256 : 497168 : json::array *optgroups = new json::array ();
257 : 497168 : obj->set ("optgroups", optgroups);
258 : 3480176 : for (const kv_pair<optgroup_flags_t> *optgroup = optgroup_options;
259 : 3480176 : optgroup->name != NULL; optgroup++)
260 : 2983008 : if (optgroup->value != OPTGROUP_ALL
261 : 2485840 : && (pass->optinfo_flags & optgroup->value))
262 : 91448 : optgroups->append_string (optgroup->name);
263 : : }
264 : 497168 : obj->set_integer ("num", pass->static_pass_number);
265 : 497168 : 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 : 29244 : optrecord_json_writer::inlining_chain_to_json (location_t loc)
274 : : {
275 : 29244 : json::array *array = new json::array ();
276 : :
277 : 29244 : tree abstract_origin = LOCATION_BLOCK (loc);
278 : :
279 : 57112 : while (abstract_origin)
280 : : {
281 : 27868 : location_t *locus;
282 : 27868 : tree block = abstract_origin;
283 : :
284 : 27868 : locus = &BLOCK_SOURCE_LOCATION (block);
285 : 27868 : tree fndecl = NULL;
286 : 27868 : block = BLOCK_SUPERCONTEXT (block);
287 : 57003 : while (block && TREE_CODE (block) == BLOCK
288 : 39787 : && BLOCK_ABSTRACT_ORIGIN (block))
289 : : {
290 : 2591 : tree ao = BLOCK_ABSTRACT_ORIGIN (block);
291 : 2591 : if (TREE_CODE (ao) == FUNCTION_DECL)
292 : : {
293 : : fndecl = ao;
294 : : break;
295 : : }
296 : 1267 : else if (TREE_CODE (ao) != BLOCK)
297 : : break;
298 : :
299 : 1267 : block = BLOCK_SUPERCONTEXT (block);
300 : : }
301 : 27868 : if (fndecl)
302 : : abstract_origin = block;
303 : : else
304 : : {
305 : 43876 : while (block && TREE_CODE (block) == BLOCK)
306 : 17332 : block = BLOCK_SUPERCONTEXT (block);
307 : :
308 : 26544 : if (block && TREE_CODE (block) == FUNCTION_DECL)
309 : : fndecl = block;
310 : : abstract_origin = NULL;
311 : : }
312 : : if (fndecl)
313 : : {
314 : 27868 : json::object *obj = new json::object ();
315 : 27868 : const char *printable_name
316 : 27868 : = lang_hooks.decl_printable_name (fndecl, 2);
317 : 27868 : obj->set_string ("fndecl", printable_name);
318 : 27868 : if (LOCATION_LOCUS (*locus) != UNKNOWN_LOCATION)
319 : 1324 : obj->set ("site", location_to_json (*locus));
320 : 27868 : array->append (obj);
321 : : }
322 : : }
323 : :
324 : 29244 : return array;
325 : : }
326 : :
327 : : /* Create a JSON object representing OPTINFO. */
328 : :
329 : : json::object *
330 : 49889 : optrecord_json_writer::optinfo_to_json (const optinfo *optinfo)
331 : : {
332 : 49889 : json::object *obj = new json::object ();
333 : :
334 : 49889 : obj->set ("impl_location",
335 : 49889 : impl_location_to_json (optinfo->get_impl_location ()));
336 : :
337 : 49889 : const char *kind_str = optinfo_kind_to_string (optinfo->get_kind ());
338 : 49889 : obj->set_string ("kind", kind_str);
339 : 49889 : json::array *message = new json::array ();
340 : 49889 : obj->set ("message", message);
341 : 169514 : for (unsigned i = 0; i < optinfo->num_items (); i++)
342 : : {
343 : 119625 : const optinfo_item *item = optinfo->get_item (i);
344 : 119625 : switch (item->get_kind ())
345 : : {
346 : 0 : default:
347 : 0 : gcc_unreachable ();
348 : 86814 : case OPTINFO_ITEM_KIND_TEXT:
349 : 86814 : {
350 : 86814 : message->append_string (item->get_text ());
351 : : }
352 : 86814 : break;
353 : 11150 : case OPTINFO_ITEM_KIND_TREE:
354 : 11150 : {
355 : 11150 : json::object *json_item = new json::object ();
356 : 11150 : json_item->set_string ("expr", item->get_text ());
357 : :
358 : : /* Capture any location for the node. */
359 : 11150 : if (LOCATION_LOCUS (item->get_location ()) != UNKNOWN_LOCATION)
360 : 0 : json_item->set ("location",
361 : 0 : location_to_json (item->get_location ()));
362 : :
363 : 11150 : message->append (json_item);
364 : : }
365 : 11150 : break;
366 : 21399 : case OPTINFO_ITEM_KIND_GIMPLE:
367 : 21399 : {
368 : 21399 : json::object *json_item = new json::object ();
369 : 21399 : json_item->set_string ("stmt", item->get_text ());
370 : :
371 : : /* Capture any location for the stmt. */
372 : 21399 : if (LOCATION_LOCUS (item->get_location ()) != UNKNOWN_LOCATION)
373 : 5269 : json_item->set ("location",
374 : 5269 : location_to_json (item->get_location ()));
375 : :
376 : 21399 : message->append (json_item);
377 : : }
378 : 21399 : 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 : 49889 : if (optinfo->get_pass ())
395 : 47357 : obj->set ("pass", get_id_value_for_pass (optinfo->get_pass ()));
396 : :
397 : 49889 : profile_count count = optinfo->get_count ();
398 : 49889 : if (count.initialized_p ())
399 : 49829 : 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 : 49889 : location_t loc = optinfo->get_location_t ();
404 : 49889 : 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 : 28532 : obj->set ("location", location_to_json (loc));
409 : : }
410 : :
411 : 49889 : if (current_function_decl)
412 : : {
413 : 47275 : const char *fnname
414 : 47275 : = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (current_function_decl));
415 : 47275 : obj->set_string ("function", fnname);
416 : : }
417 : :
418 : 49889 : if (loc != UNKNOWN_LOCATION)
419 : 29244 : obj->set ("inlining_chain", inlining_chain_to_json (loc));
420 : :
421 : 49889 : 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 : 32200 : optrecord_json_writer::add_pass_list (json::array *arr, opt_pass *pass)
429 : : {
430 : 497168 : do
431 : : {
432 : 497168 : json::object *pass_obj = pass_to_json (pass);
433 : 497168 : arr->append (pass_obj);
434 : 497168 : if (pass->sub)
435 : : {
436 : 25760 : json::array *sub = new json::array ();
437 : 25760 : pass_obj->set ("children", sub);
438 : 25760 : add_pass_list (sub, pass->sub);
439 : : }
440 : 497168 : pass = pass->next;
441 : : }
442 : 497168 : while (pass);
443 : 32200 : }
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 */
|