diff options
Diffstat (limited to 'gcc')
-rw-r--r-- | gcc/diagnostic-client-data-hooks.h | 6 | ||||
-rw-r--r-- | gcc/diagnostic-format-sarif.cc | 45 | ||||
-rw-r--r-- | gcc/diagnostic-format-sarif.h | 45 | ||||
-rw-r--r-- | gcc/doc/invoke.texi | 12 | ||||
-rw-r--r-- | gcc/testsuite/c-c++-common/diagnostic-format-sarif-file-timevars-1.c | 18 | ||||
-rw-r--r-- | gcc/testsuite/c-c++-common/diagnostic-format-sarif-file-timevars-2.c | 21 | ||||
-rw-r--r-- | gcc/timevar.cc | 164 | ||||
-rw-r--r-- | gcc/timevar.h | 5 | ||||
-rw-r--r-- | gcc/tree-diagnostic-client-data-hooks.cc | 26 |
9 files changed, 326 insertions, 16 deletions
diff --git a/gcc/diagnostic-client-data-hooks.h b/gcc/diagnostic-client-data-hooks.h index 5f8b9a2..e3f2d05 100644 --- a/gcc/diagnostic-client-data-hooks.h +++ b/gcc/diagnostic-client-data-hooks.h @@ -21,6 +21,7 @@ along with GCC; see the file COPYING3. If not see #ifndef GCC_DIAGNOSTIC_CLIENT_DATA_HOOKS_H #define GCC_DIAGNOSTIC_CLIENT_DATA_HOOKS_H +class sarif_object; class client_version_info; /* A bundle of additional metadata, owned by the diagnostic_context, @@ -41,6 +42,11 @@ class diagnostic_client_data_hooks See SARIF v2.1.0 Appendix J for suggested values. */ virtual const char * maybe_get_sarif_source_language (const char *filename) const = 0; + + /* Hook to allow client to populate a SARIF "invocation" object with + a custom property bag (see SARIF v2.1.0 section 3.8). */ + virtual void + add_sarif_invocation_properties (sarif_object &invocation_obj) const = 0; }; /* Factory function for making an instance of diagnostic_client_data_hooks diff --git a/gcc/diagnostic-format-sarif.cc b/gcc/diagnostic-format-sarif.cc index 5e48398..1eff719 100644 --- a/gcc/diagnostic-format-sarif.cc +++ b/gcc/diagnostic-format-sarif.cc @@ -32,13 +32,14 @@ along with GCC; see the file COPYING3. If not see #include "diagnostic-client-data-hooks.h" #include "diagnostic-diagram.h" #include "text-art/canvas.h" +#include "diagnostic-format-sarif.h" class sarif_builder; /* Subclass of json::object for SARIF invocation objects (SARIF v2.1.0 section 3.20). */ -class sarif_invocation : public json::object +class sarif_invocation : public sarif_object { public: sarif_invocation () @@ -49,17 +50,17 @@ public: void add_notification_for_ice (diagnostic_context *context, diagnostic_info *diagnostic, sarif_builder *builder); - void prepare_to_flush (); + void prepare_to_flush (diagnostic_context *context); private: json::array *m_notifications_arr; bool m_success; }; -/* Subclass of json::object for SARIF result objects +/* Subclass of sarif_object for SARIF result objects (SARIF v2.1.0 section 3.27). */ -class sarif_result : public json::object +class sarif_result : public sarif_object { public: sarif_result () : m_related_locations_arr (NULL) {} @@ -79,13 +80,13 @@ private: json::array *m_related_locations_arr; }; -/* Subclass of json::object for SARIF notification objects +/* Subclass of sarif_object for SARIF notification objects (SARIF v2.1.0 section 3.58). This subclass is specifically for notifying when an internal compiler error occurs. */ -class sarif_ice_notification : public json::object +class sarif_ice_notification : public sarif_object { public: sarif_ice_notification (diagnostic_context *context, @@ -232,7 +233,24 @@ private: static sarif_builder *the_builder; -/* class sarif_invocation : public json::object. */ +/* class sarif_object : public json::object. */ + +sarif_property_bag & +sarif_object::get_or_create_properties () +{ + json::value *properties_val = get ("properties"); + if (properties_val) + { + if (properties_val->get_kind () == json::JSON_OBJECT) + return *static_cast <sarif_property_bag *> (properties_val); + } + + sarif_property_bag *bag = new sarif_property_bag (); + set ("properties", bag); + return *bag; +} + +/* class sarif_invocation : public sarif_object. */ /* Handle an internal compiler error DIAGNOSTIC occurring on CONTEXT. Add an object representing the ICE to the notifications array. */ @@ -250,16 +268,21 @@ sarif_invocation::add_notification_for_ice (diagnostic_context *context, } void -sarif_invocation::prepare_to_flush () +sarif_invocation::prepare_to_flush (diagnostic_context *context) { /* "executionSuccessful" property (SARIF v2.1.0 section 3.20.14). */ set ("executionSuccessful", new json::literal (m_success)); /* "toolExecutionNotifications" property (SARIF v2.1.0 section 3.20.21). */ set ("toolExecutionNotifications", m_notifications_arr); + + /* Call client hook, allowing it to create a custom property bag for + this object (SARIF v2.1.0 section 3.8) e.g. for recording time vars. */ + if (context->m_client_data_hooks) + context->m_client_data_hooks->add_sarif_invocation_properties (*this); } -/* class sarif_result : public json::object. */ +/* class sarif_result : public sarif_object. */ /* Handle secondary diagnostics that occur within a diagnostic group. The closest SARIF seems to have to nested diagnostics is the @@ -319,7 +342,7 @@ sarif_result::add_related_location (json::object *location_obj) m_related_locations_arr->append (location_obj); } -/* class sarif_ice_notification : public json::object. */ +/* class sarif_ice_notification : public sarif_object. */ /* sarif_ice_notification's ctor. DIAGNOSTIC is an internal compiler error. */ @@ -415,7 +438,7 @@ sarif_builder::end_group () void sarif_builder::flush_to_file (FILE *outf) { - m_invocation_obj->prepare_to_flush (); + m_invocation_obj->prepare_to_flush (m_context); json::object *top = make_top_level_object (m_invocation_obj, m_results_array); top->dump (outf); m_invocation_obj = NULL; diff --git a/gcc/diagnostic-format-sarif.h b/gcc/diagnostic-format-sarif.h new file mode 100644 index 0000000..82ed9b9 --- /dev/null +++ b/gcc/diagnostic-format-sarif.h @@ -0,0 +1,45 @@ +/* SARIF output for diagnostics. + Copyright (C) 2023 Free Software Foundation, Inc. + Contributed by David Malcolm <dmalcolm@redhat.com>. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +<http://www.gnu.org/licenses/>. */ + +#ifndef GCC_DIAGNOSTIC_FORMAT_SARIF_H +#define GCC_DIAGNOSTIC_FORMAT_SARIF_H + +#include "json.h" + +/* Concrete subclass of json::object for SARIF property bags + (SARIF v2.1.0 section 3.8). */ + +class sarif_property_bag : public json::object +{ +}; + +/* Concrete subclass of json::object for SARIF objects that can + contain property bags (as per SARIF v2.1.0 section 3.8.1, which has: + "In addition to those properties that are explicitly documented, every + object defined in this document MAY contain a property named properties + whose value is a property bag.") */ + +class sarif_object : public json::object +{ +public: + sarif_property_bag &get_or_create_properties (); +}; + +#endif /* ! GCC_DIAGNOSTIC_FORMAT_SARIF_H */ diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index 578b4c3..c43260f 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -19933,8 +19933,16 @@ print some statistics about each pass when it finishes. @opindex ftime-report @item -ftime-report -Makes the compiler print some statistics about the time consumed by each -pass when it finishes. +Makes the compiler print some statistics to stderr about the time consumed +by each pass when it finishes. + +If SARIF output of diagnostics was requested via +@option{-fdiagnostics-format=sarif-file} or +@option{-fdiagnostics-format=sarif-stderr} then the @option{-ftime-report} +information is instead emitted in JSON form as part of SARIF output. The +precise format of this JSON data is subject to change, and the values may +not exactly match those emitted to stderr due to being written out at a +slightly different place within the compiler. @opindex ftime-report-details @item -ftime-report-details diff --git a/gcc/testsuite/c-c++-common/diagnostic-format-sarif-file-timevars-1.c b/gcc/testsuite/c-c++-common/diagnostic-format-sarif-file-timevars-1.c new file mode 100644 index 0000000..be6b1e7 --- /dev/null +++ b/gcc/testsuite/c-c++-common/diagnostic-format-sarif-file-timevars-1.c @@ -0,0 +1,18 @@ +/* { dg-do compile } */ +/* { dg-options "-fdiagnostics-format=sarif-file -ftime-report" } */ + +#warning message + +/* Verify that some JSON was written to a file with the expected name. */ +/* { dg-final { verify-sarif-file } } */ + +/* We expect various properties. + The indentation here reflects the expected hierarchy, though these tests + don't check for that, merely the string fragments we expect. + + { dg-final { scan-sarif-file {"invocations": } } } + { dg-final { scan-sarif-file {"properties": } } } + { dg-final { scan-sarif-file {"gcc/timeReport": } } } + { dg-final { scan-sarif-file {"timevars": } } } + { dg-final { scan-sarif-file {"name": "TOTAL",} } } +*/ diff --git a/gcc/testsuite/c-c++-common/diagnostic-format-sarif-file-timevars-2.c b/gcc/testsuite/c-c++-common/diagnostic-format-sarif-file-timevars-2.c new file mode 100644 index 0000000..e9c2b5e0 --- /dev/null +++ b/gcc/testsuite/c-c++-common/diagnostic-format-sarif-file-timevars-2.c @@ -0,0 +1,21 @@ +/* As per diagnostic-format-sarif-file-timevars-1.c, but + adding -ftime-report-details. */ + +/* { dg-do compile } */ +/* { dg-options "-fdiagnostics-format=sarif-file -ftime-report -ftime-report-details" } */ + +#warning message + +/* Verify that some JSON was written to a file with the expected name. */ +/* { dg-final { verify-sarif-file } } */ + +/* We expect various properties. + The indentation here reflects the expected hierarchy, though these tests + don't check for that, merely the string fragments we expect. + + { dg-final { scan-sarif-file {"invocations": } } } + { dg-final { scan-sarif-file {"properties": } } } + { dg-final { scan-sarif-file {"gcc/timeReport": } } } + { dg-final { scan-sarif-file {"timevars": } } } + { dg-final { scan-sarif-file {"name": "TOTAL",} } } +*/ diff --git a/gcc/timevar.cc b/gcc/timevar.cc index d695297..5368ff0 100644 --- a/gcc/timevar.cc +++ b/gcc/timevar.cc @@ -23,6 +23,7 @@ along with GCC; see the file COPYING3. If not see #include "coretypes.h" #include "timevar.h" #include "options.h" +#include "json.h" #ifndef HAVE_CLOCK_T typedef int clock_t; @@ -135,6 +136,8 @@ class timer::named_items void pop (); void print (FILE *fp, const timevar_time_def *total); + json::value *make_json () const; + private: /* Which timer instance does this relate to? */ timer *m_timer; @@ -143,7 +146,8 @@ class timer::named_items Note that currently we merely store/compare the raw string pointers provided by client code; we don't take a copy, or use strcmp. */ - hash_map <const char *, timer::timevar_def> m_hash_map; + typedef hash_map <const char *, timer::timevar_def> hash_map_t; + hash_map_t m_hash_map; /* The order in which items were originally inserted. */ auto_vec <const char *> m_names; @@ -207,6 +211,23 @@ timer::named_items::print (FILE *fp, const timevar_time_def *total) } } +/* Create a json value representing this object, suitable for use + in SARIF output. */ + +json::value * +timer::named_items::make_json () const +{ + json::array *arr = new json::array (); + for (const char *item_name : m_names) + { + hash_map_t &mut_map = const_cast <hash_map_t &> (m_hash_map); + timer::timevar_def *def = mut_map.get (item_name); + gcc_assert (def); + arr->append (def->make_json ()); + } + return arr; +} + /* Fill the current times into TIME. The definition of this function also defines any or all of the HAVE_USER_TIME, HAVE_SYS_TIME, and HAVE_WALL_TIME macros. */ @@ -251,6 +272,19 @@ timevar_accumulate (struct timevar_time_def *timer, timer->ggc_mem += stop_time->ggc_mem - start_time->ggc_mem; } +/* Get the difference between STOP_TIME and START_TIME. */ + +static void +timevar_diff (struct timevar_time_def *out, + const timevar_time_def &start_time, + const timevar_time_def &stop_time) +{ + out->user = stop_time.user - start_time.user; + out->sys = stop_time.sys - start_time.sys; + out->wall = stop_time.wall - start_time.wall; + out->ggc_mem = stop_time.ggc_mem - start_time.ggc_mem; +} + /* Class timer's constructor. */ timer::timer () : @@ -791,6 +825,134 @@ timer::print (FILE *fp) validate_phases (fp); } +/* Create a json value representing this object, suitable for use + in SARIF output. */ + +json::object * +make_json_for_timevar_time_def (const timevar_time_def &ttd) +{ + json::object *obj = new json::object (); + obj->set ("user", new json::float_number (ttd.user)); + obj->set ("sys", new json::float_number (ttd.sys)); + obj->set ("wall", new json::float_number (ttd.wall)); + obj->set ("ggc_mem", new json::integer_number (ttd.ggc_mem)); + return obj; +} + +/* Create a json value representing this object, suitable for use + in SARIF output. */ + +json::value * +timer::timevar_def::make_json () const +{ + json::object *timevar_obj = new json::object (); + timevar_obj->set ("name", new json::string (name)); + timevar_obj->set ("elapsed", make_json_for_timevar_time_def (elapsed)); + + if (children) + { + bool any_children_with_time = false; + for (auto i : *children) + if (!all_zero (i.second)) + { + any_children_with_time = true; + break; + } + if (any_children_with_time) + { + json::array *children_arr = new json::array (); + timevar_obj->set ("children", children_arr); + for (auto i : *children) + { + /* Don't emit timing variables if we're going to get a row of + zeroes. */ + if (all_zero (i.second)) + continue; + json::object *child_obj = new json::object; + children_arr->append (child_obj); + child_obj->set ("name", new json::string (i.first->name)); + child_obj->set ("elapsed", + make_json_for_timevar_time_def (i.second)); + } + } + } + + return timevar_obj; +} + +/* Create a json value representing this object, suitable for use + in SARIF output. */ + +json::value * +timer::make_json () const +{ + /* Only generate stuff if we have some sort of time information. */ +#if defined (HAVE_USER_TIME) || defined (HAVE_SYS_TIME) || defined (HAVE_WALL_TIME) + json::object *report_obj = new json::object (); + json::array *json_arr = new json::array (); + report_obj->set ("timevars", json_arr); + + for (unsigned id = 0; id < (unsigned int) TIMEVAR_LAST; ++id) + { + const timevar_def *tv = &m_timevars[(timevar_id_t) id]; + + /* Don't print the total execution time here; this isn't initialized + by the time the sarif output runs. */ + if ((timevar_id_t) id == TV_TOTAL) + continue; + + /* Don't emit timing variables that were never used. */ + if (!tv->used) + continue; + + bool any_children_with_time = false; + if (tv->children) + for (child_map_t::iterator i = tv->children->begin (); + i != tv->children->end (); ++i) + if (! all_zero ((*i).second)) + { + any_children_with_time = true; + break; + } + + /* Don't emit timing variables if we're going to get a row of + zeroes. Unless there are children with non-zero time. */ + if (! any_children_with_time + && all_zero (tv->elapsed)) + continue; + + json_arr->append (tv->make_json ()); + } + + /* Special-case for total. */ + { + /* Get our own total up till now, without affecting TV_TOTAL. */ + struct timevar_time_def total_now; + struct timevar_time_def total_elapsed; + get_time (&total_now); + timevar_diff (&total_elapsed, m_timevars[TV_TOTAL].start_time, total_now); + + json::object *total_obj = new json::object(); + json_arr->append (total_obj); + total_obj->set ("name", new json::string ("TOTAL")); + total_obj->set ("elapsed", make_json_for_timevar_time_def (total_elapsed)); + } + + if (m_jit_client_items) + report_obj->set ("client_items", m_jit_client_items->make_json ()); + + report_obj->set ("CHECKING_P", new json::literal ((bool)CHECKING_P)); + report_obj->set ("flag_checking", new json::literal ((bool)flag_checking)); + + return report_obj; + +#else /* defined (HAVE_USER_TIME) || defined (HAVE_SYS_TIME) + || defined (HAVE_WALL_TIME) */ + return NULL; +#endif /* defined (HAVE_USER_TIME) || defined (HAVE_SYS_TIME) + || defined (HAVE_WALL_TIME) */ +} + /* Get the name of the topmost item. For use by jit for validating inputs to gcc_jit_timer_pop. */ const char * diff --git a/gcc/timevar.h b/gcc/timevar.h index ad46573..e359e9f 100644 --- a/gcc/timevar.h +++ b/gcc/timevar.h @@ -21,6 +21,8 @@ #ifndef GCC_TIMEVAR_H #define GCC_TIMEVAR_H +namespace json { class value; } + /* Timing variables are used to measure elapsed time in various portions of the compiler. Each measures elapsed user, system, and wall-clock time, as appropriate to and supported by the host @@ -119,6 +121,7 @@ class timer void pop_client_item (); void print (FILE *fp); + json::value *make_json () const; const char *get_topmost_item_name () const; @@ -140,6 +143,8 @@ class timer /* Private type: a timing variable. */ struct timevar_def { + json::value *make_json () const; + /* Elapsed time for this variable. */ struct timevar_time_def elapsed; diff --git a/gcc/tree-diagnostic-client-data-hooks.cc b/gcc/tree-diagnostic-client-data-hooks.cc index 1a35f4c..fc972ce 100644 --- a/gcc/tree-diagnostic-client-data-hooks.cc +++ b/gcc/tree-diagnostic-client-data-hooks.cc @@ -1,5 +1,5 @@ /* Implementation of diagnostic_client_data_hooks for the compilers - (e.g. with knowledge of "tree" and lang_hooks). + (e.g. with knowledge of "tree", lang_hooks, and timevars). Copyright (C) 2022-2023 Free Software Foundation, Inc. Contributed by David Malcolm <dmalcolm@redhat.com>. @@ -27,8 +27,10 @@ along with GCC; see the file COPYING3. If not see #include "diagnostic.h" #include "tree-logical-location.h" #include "diagnostic-client-data-hooks.h" +#include "diagnostic-format-sarif.h" #include "langhooks.h" #include "plugin.h" +#include "timevar.h" /* Concrete class for supplying a diagnostic_context with information about a specific plugin within the client, when the client is the @@ -111,7 +113,7 @@ private: }; /* Subclass of diagnostic_client_data_hooks for use by compilers proper - i.e. with knowledge of "tree", access to langhooks, etc. */ + i.e. with knowledge of "tree", access to langhooks, timevars etc. */ class compiler_data_hooks : public diagnostic_client_data_hooks { @@ -135,6 +137,26 @@ public: return lang_hooks.get_sarif_source_language (filename); } + void + add_sarif_invocation_properties (sarif_object &invocation_obj) + const final override + { + if (g_timer) + if (json::value *timereport_val = g_timer->make_json ()) + { + sarif_property_bag &bag_obj + = invocation_obj.get_or_create_properties (); + bag_obj.set ("gcc/timeReport", timereport_val); + + /* If the user requested SARIF output, then assume they want the + time report data in the SARIF output, and *not* later emitted on + stderr. + Implement this by cleaning up the global timer instance now. */ + delete g_timer; + g_timer = NULL; + } + } + private: compiler_version_info m_version_info; current_fndecl_logical_location m_current_fndecl_logical_loc; |