aboutsummaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
Diffstat (limited to 'gcc')
-rw-r--r--gcc/diagnostic-client-data-hooks.h6
-rw-r--r--gcc/diagnostic-format-sarif.cc45
-rw-r--r--gcc/diagnostic-format-sarif.h45
-rw-r--r--gcc/doc/invoke.texi12
-rw-r--r--gcc/testsuite/c-c++-common/diagnostic-format-sarif-file-timevars-1.c18
-rw-r--r--gcc/testsuite/c-c++-common/diagnostic-format-sarif-file-timevars-2.c21
-rw-r--r--gcc/timevar.cc164
-rw-r--r--gcc/timevar.h5
-rw-r--r--gcc/tree-diagnostic-client-data-hooks.cc26
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;