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