class sarif_builder;
+/* Subclass of json::object for SARIF invocation objects
+ (SARIF v2.1.0 section 3.20). */
+
+class sarif_invocation : public json::object
+{
+public:
+ sarif_invocation ()
+ : m_notifications_arr (new json::array ()),
+ m_success (true)
+ {}
+
+ void add_notification_for_ice (diagnostic_context *context,
+ diagnostic_info *diagnostic,
+ sarif_builder *builder);
+ void prepare_to_flush ();
+
+private:
+ json::array *m_notifications_arr;
+ bool m_success;
+};
+
/* Subclass of json::object for SARIF result objects
- (SARIF v2.1.0 section 3.27. */
+ (SARIF v2.1.0 section 3.27). */
class sarif_result : public json::object
{
json::array *m_related_locations_arr;
};
+/* Subclass of json::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
+{
+public:
+ sarif_ice_notification (diagnostic_context *context,
+ diagnostic_info *diagnostic,
+ sarif_builder *builder);
+};
+
/* A class for managing SARIF output (for -fdiagnostics-format=sarif-stderr
and -fdiagnostics-format=sarif-file).
void flush_to_file (FILE *outf);
+ json::array *make_locations_arr (diagnostic_info *diagnostic);
json::object *make_location_object (const rich_location &rich_loc,
const logical_location *logical_loc);
json::object *make_message_object (const char *msg) const;
json::object *maybe_make_region_object_for_context (location_t loc) const;
json::object *make_region_object_for_hint (const fixit_hint &hint) const;
json::object *make_multiformat_message_string (const char *msg) const;
- json::object *make_top_level_object (json::array *results);
- json::object *make_run_object (json::array *results);
+ json::object *make_top_level_object (sarif_invocation *invocation_obj,
+ json::array *results);
+ json::object *make_run_object (sarif_invocation *invocation_obj,
+ json::array *results);
json::object *make_tool_object () const;
json::object *make_driver_tool_component_object () const;
json::array *maybe_make_taxonomies_array () const;
diagnostic_context *m_context;
+ /* The JSON object for the invocation object. */
+ sarif_invocation *m_invocation_obj;
+
/* The JSON array of pending diagnostics. */
json::array *m_results_array;
static sarif_builder *the_builder;
+/* class sarif_invocation : public json::object. */
+
+/* Handle an internal compiler error DIAGNOSTIC occurring on CONTEXT.
+ Add an object representing the ICE to the notifications array. */
+
+void
+sarif_invocation::add_notification_for_ice (diagnostic_context *context,
+ diagnostic_info *diagnostic,
+ sarif_builder *builder)
+{
+ m_success = false;
+
+ sarif_ice_notification *notification_obj
+ = new sarif_ice_notification (context, diagnostic, builder);
+ m_notifications_arr->append (notification_obj);
+}
+
+void
+sarif_invocation::prepare_to_flush ()
+{
+ /* "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);
+}
+
/* class sarif_result : public json::object. */
/* Handle secondary diagnostics that occur within a diagnostic group.
m_related_locations_arr->append (location_obj);
}
+/* class sarif_ice_notification : public json::object. */
+
+/* sarif_ice_notification's ctor.
+ DIAGNOSTIC is an internal compiler error. */
+
+sarif_ice_notification::sarif_ice_notification (diagnostic_context *context,
+ diagnostic_info *diagnostic,
+ sarif_builder *builder)
+{
+ /* "locations" property (SARIF v2.1.0 section 3.58.4). */
+ json::array *locations_arr = builder->make_locations_arr (diagnostic);
+ set ("locations", locations_arr);
+
+ /* "message" property (SARIF v2.1.0 section 3.85.5). */
+ json::object *message_obj
+ = builder->make_message_object (pp_formatted_text (context->printer));
+ pp_clear_output_area (context->printer);
+ set ("message", message_obj);
+
+ /* "level" property (SARIF v2.1.0 section 3.58.6). */
+ set ("level", new json::string ("error"));
+}
+
/* class sarif_builder. */
/* sarif_builder's ctor. */
sarif_builder::sarif_builder (diagnostic_context *context)
: m_context (context),
+ m_invocation_obj (new sarif_invocation ()),
m_results_array (new json::array ()),
m_cur_group_result (NULL),
m_seen_any_relative_paths (false),
diagnostic_info *diagnostic,
diagnostic_t orig_diag_kind)
{
+ if (diagnostic->kind == DK_ICE || diagnostic->kind == DK_ICE_NOBT)
+ {
+ m_invocation_obj->add_notification_for_ice (context, diagnostic, this);
+ return;
+ }
if (m_cur_group_result)
/* Nested diagnostic. */
void
sarif_builder::flush_to_file (FILE *outf)
{
- json::object *top = make_top_level_object (m_results_array);
+ m_invocation_obj->prepare_to_flush ();
+ json::object *top = make_top_level_object (m_invocation_obj, m_results_array);
top->dump (outf);
+ m_invocation_obj = NULL;
m_results_array = NULL;
fprintf (outf, "\n");
delete top;
result_obj->set ("message", message_obj);
/* "locations" property (SARIF v2.1.0 section 3.27.12). */
- json::array *locations_arr = new json::array ();
- const logical_location *logical_loc = NULL;
- if (m_context->m_client_data_hooks)
- logical_loc
- = m_context->m_client_data_hooks->get_current_logical_location ();
-
- json::object *location_obj
- = make_location_object (*diagnostic->richloc, logical_loc);
- locations_arr->append (location_obj);
+ json::array *locations_arr = make_locations_arr (diagnostic);
result_obj->set ("locations", locations_arr);
/* "codeFlows" property (SARIF v2.1.0 section 3.27.18). */
return comp_ref_obj;
}
+/* Make an array suitable for use as the "locations" property of:
+ - a "result" object (SARIF v2.1.0 section 3.27.12), or
+ - a "notification" object (SARIF v2.1.0 section 3.58.4). */
+
+json::array *
+sarif_builder::make_locations_arr (diagnostic_info *diagnostic)
+{
+ json::array *locations_arr = new json::array ();
+ const logical_location *logical_loc = NULL;
+ if (m_context->m_client_data_hooks)
+ logical_loc
+ = m_context->m_client_data_hooks->get_current_logical_location ();
+
+ json::object *location_obj
+ = make_location_object (*diagnostic->richloc, logical_loc);
+ locations_arr->append (location_obj);
+ return locations_arr;
+}
+
/* If LOGICAL_LOC is non-NULL, use it to create a "logicalLocations" property
within LOCATION_OBJ (SARIF v2.1.0 section 3.28.4). */
#define SARIF_VERSION "2.1.0"
/* Make a top-level sarifLog object (SARIF v2.1.0 section 3.13).
- Take ownership of RESULTS. */
+ Take ownership of INVOCATION_OBJ and RESULTS. */
json::object *
-sarif_builder::make_top_level_object (json::array *results)
+sarif_builder::make_top_level_object (sarif_invocation *invocation_obj,
+ json::array *results)
{
json::object *log_obj = new json::object ();
/* "runs" property (SARIF v2.1.0 section 3.13.4). */
json::array *run_arr = new json::array ();
- json::object *run_obj = make_run_object (results);
+ json::object *run_obj = make_run_object (invocation_obj, results);
run_arr->append (run_obj);
log_obj->set ("runs", run_arr);
}
/* Make a run object (SARIF v2.1.0 section 3.14).
- Take ownership of RESULTS. */
+ Take ownership of INVOCATION_OBJ and RESULTS. */
json::object *
-sarif_builder::make_run_object (json::array *results)
+sarif_builder::make_run_object (sarif_invocation *invocation_obj,
+ json::array *results)
{
json::object *run_obj = new json::object ();
if (json::array *taxonomies_arr = maybe_make_taxonomies_array ())
run_obj->set ("taxonomies", taxonomies_arr);
+ /* "invocations" property (SARIF v2.1.0 section 3.14.11). */
+ {
+ json::array *invocations_arr = new json::array ();
+ invocations_arr->append (invocation_obj);
+ run_obj->set ("invocations", invocations_arr);
+ }
+
/* "originalUriBaseIds (SARIF v2.1.0 section 3.14.14). */
if (m_seen_any_relative_paths)
{
free (filename);
}
+/* Callback for diagnostic_context::ice_handler_cb for when an ICE
+ occurs. */
+
+static void
+sarif_ice_handler (diagnostic_context *context)
+{
+ /* Attempt to ensure that a .sarif file is written out. */
+ diagnostic_finish (context);
+
+ /* Print a header for the remaining output to stderr, and
+ return, attempting to print the usual ICE messages to
+ stderr. Hopefully this will be helpful to the user in
+ indicating what's gone wrong (also for DejaGnu, for pruning
+ those messages). */
+ fnotice (stderr, "Internal compiler error:\n");
+}
+
/* Populate CONTEXT in preparation for SARIF output (either to stderr, or
to a file). */
context->begin_group_cb = sarif_begin_group;
context->end_group_cb = sarif_end_group;
context->print_path = NULL; /* handled in sarif_end_diagnostic. */
+ context->ice_handler_cb = sarif_ice_handler;
/* The metadata is handled in SARIF format, rather than as text. */
context->show_cwe = false;
context->begin_group_cb = NULL;
context->end_group_cb = NULL;
context->final_cb = default_diagnostic_final_cb;
+ context->ice_handler_cb = NULL;
context->includes_seen = NULL;
context->m_client_data_hooks = NULL;
}
case DK_ICE:
case DK_ICE_NOBT:
{
+ /* Optional callback for attempting to handle ICEs gracefully. */
+ if (void (*ice_handler_cb) (diagnostic_context *)
+ = context->ice_handler_cb)
+ {
+ /* Clear the callback, to avoid potentially re-entering
+ the routine if there's a crash within the handler. */
+ context->ice_handler_cb = NULL;
+ ice_handler_cb (context);
+ }
+ /* The context might have had diagnostic_finish called on
+ it at this point. */
+
struct backtrace_state *state = NULL;
if (diag_kind == DK_ICE)
state = backtrace_create_state (NULL, 0, bt_err_callback, NULL);
of a diagnostic's location. */
void (*set_locations_cb)(diagnostic_context *, diagnostic_info *);
+ /* Optional callback for attempting to handle ICEs gracefully. */
+ void (*ice_handler_cb) (diagnostic_context *context);
+
/* Include files that diagnostic_report_current_module has already listed the
include path for. */
hash_set<location_t, false, location_hash> *includes_seen;
{ dg-final { scan-sarif-file "\"name\": \"GNU C" } }
{ dg-final { scan-sarif-file "\"fullName\": \"GNU C" } }
{ dg-final { scan-sarif-file "\"informationUri\": \"" } }
+
+ { dg-final { scan-sarif-file "\"invocations\": \\\[" } }
+ { dg-final { scan-sarif-file "\"toolExecutionNotifications\": \\\[\\\]" } }
+ { dg-final { scan-sarif-file "\"executionSuccessful\": true" } }
+
{ dg-final { scan-sarif-file "\"results\": \\\[" } }
{ dg-final { scan-sarif-file "\"level\": \"warning\"" } }
{ dg-final { scan-sarif-file "\"ruleId\": \"-Wcpp\"" } }
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-fdiagnostics-format=sarif-file" } */
+
+extern void inject_ice (void);
+
+void test_inject_ice (void)
+{
+ inject_ice (); /* { dg-ice "" } */
+ /* { dg-regexp "during GIMPLE pass: crash_test" } */
+}
+
+/* Verify that some JSON was written to a file with the expected name. */
+
+/* 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 "\"version\": \"2.1.0\"" } }
+ { dg-final { scan-sarif-file "\"runs\": \\\[" } }
+ { dg-final { scan-sarif-file "\"artifacts\": \\\[" } }
+ { dg-final { scan-sarif-file "\"location\": " } }
+ { dg-final { scan-sarif-file "\"uri\": " } }
+
+ { dg-final { scan-sarif-file "\"sourceLanguage\": \"c\"" { target c } } }
+ { dg-final { scan-sarif-file "\"sourceLanguage\": \"cplusplus\"" { target c++ } } }
+
+ { dg-final { scan-sarif-file "\"contents\": " } }
+ { dg-final { scan-sarif-file "\"text\": " } }
+ { dg-final { scan-sarif-file "\"tool\": " } }
+ { dg-final { scan-sarif-file "\"driver\": " } }
+ { dg-final { scan-sarif-file "\"name\": \"GNU C" } }
+ { dg-final { scan-sarif-file "\"fullName\": \"GNU C" } }
+ { dg-final { scan-sarif-file "\"informationUri\": \"" } }
+ { dg-final { scan-sarif-file "\"extensions\": \\\[" } }
+ { dg-final { scan-sarif-file "\"name\": \"crash_test_plugin\"" } }
+
+ We expect no results:
+ { dg-final { scan-sarif-file "\"results\": \\\[\\\]" } }
+
+ but instead should have an invocations array...
+
+ { dg-final { scan-sarif-file "\"invocations\": \\\[" } }
+
+ ...containing this:
+ { dg-final { scan-sarif-file "\"executionSuccessful\": false" } }
+ { dg-final { scan-sarif-file "\"toolExecutionNotifications\": \\\[" } }
+
+ ...containing this notification:
+ { dg-final { scan-sarif-file "\"level\": \"error\"" } }
+ { dg-final { scan-sarif-file "\"locations\": \\\[" } }
+ { dg-final { scan-sarif-file "\"logicalLocations\": \\\[" } }
+ { dg-final { scan-sarif-file "\"kind\": \"function\"" } }
+ { dg-final { scan-sarif-file "\"name\": \"test_inject_ice\"" } }
+ { dg-final { scan-sarif-file "\"physicalLocation\": " } }
+ { dg-final { scan-sarif-file "\"contextRegion\": " } }
+ { dg-final { scan-sarif-file "\"artifactLocation\": " } }
+ { dg-final { scan-sarif-file "\"region\": " } }
+ { dg-final { scan-sarif-file "\"startLine\": 8" } }
+ { dg-final { scan-sarif-file "\"startColumn\": 3" } }
+ { dg-final { scan-sarif-file "\"endColumn\": 16" } }
+ { dg-final { scan-sarif-file "\"message\": " } }
+ { dg-final { scan-sarif-file "\"text\": \"I'm sorry Dave, I'm afraid I can't do that\"" } } */
--- /dev/null
+/* { dg-do compile } */
+
+extern void inject_ice (void);
+
+void test_1 (void)
+{
+ inject_ice (); /* { dg-ice "I'm sorry Dave, I'm afraid I can't do that" } */
+ /* { dg-regexp "during GIMPLE pass: crash_test" } */
+}
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-fdiagnostics-format=sarif-file" } */
+
+extern void inject_write_through_null (void);
+
+void test_inject_write_through_null (void)
+{
+ inject_write_through_null (); /* { dg-ice "" } */
+ /* { dg-regexp "during GIMPLE pass: crash_test" } */
+}
+
+/* Verify that some JSON was written to a file with the expected name. */
+
+/* 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 "\"version\": \"2.1.0\"" } }
+ { dg-final { scan-sarif-file "\"runs\": \\\[" } }
+ { dg-final { scan-sarif-file "\"artifacts\": \\\[" } }
+ { dg-final { scan-sarif-file "\"location\": " } }
+ { dg-final { scan-sarif-file "\"uri\": " } }
+
+ { dg-final { scan-sarif-file "\"sourceLanguage\": \"c\"" { target c } } }
+ { dg-final { scan-sarif-file "\"sourceLanguage\": \"cplusplus\"" { target c++ } } }
+
+ { dg-final { scan-sarif-file "\"contents\": " } }
+ { dg-final { scan-sarif-file "\"text\": " } }
+ { dg-final { scan-sarif-file "\"tool\": " } }
+ { dg-final { scan-sarif-file "\"driver\": " } }
+ { dg-final { scan-sarif-file "\"name\": \"GNU C" } }
+ { dg-final { scan-sarif-file "\"fullName\": \"GNU C" } }
+ { dg-final { scan-sarif-file "\"informationUri\": \"" } }
+ { dg-final { scan-sarif-file "\"extensions\": \\\[" } }
+ { dg-final { scan-sarif-file "\"name\": \"crash_test_plugin\"" } }
+
+ We expect no results:
+ { dg-final { scan-sarif-file "\"results\": \\\[\\\]" } }
+
+ but instead should have an invocations array...
+
+ { dg-final { scan-sarif-file "\"invocations\": \\\[" } }
+
+ ...containing this:
+ { dg-final { scan-sarif-file "\"executionSuccessful\": false" } }
+ { dg-final { scan-sarif-file "\"toolExecutionNotifications\": \\\[" } }
+
+ ...containing this notification:
+ { dg-final { scan-sarif-file "\"level\": \"error\"" } }
+ { dg-final { scan-sarif-file "\"locations\": \\\[" } }
+ { dg-final { scan-sarif-file "\"logicalLocations\": \\\[" } }
+ { dg-final { scan-sarif-file "\"kind\": \"function\"" } }
+ { dg-final { scan-sarif-file "\"name\": \"test_inject_write_through_null\"" } }
+ { dg-final { scan-sarif-file "\"physicalLocation\": " } }
+ { dg-final { scan-sarif-file "\"contextRegion\": " } }
+ { dg-final { scan-sarif-file "\"artifactLocation\": " } }
+ { dg-final { scan-sarif-file "\"region\": " } }
+ { dg-final { scan-sarif-file "\"startLine\": 8" } }
+ { dg-final { scan-sarif-file "\"startColumn\": 3" } }
+ { dg-final { scan-sarif-file "\"endColumn\": 31" } }
+ { dg-final { scan-sarif-file "\"message\": " } }
+ { dg-final { scan-sarif-file "\"text\": \"Segmentation fault\"" } } */
--- /dev/null
+/* { dg-do compile } */
+
+extern void inject_write_through_null (void);
+
+void test_inject_write_through_null (void)
+{
+ inject_write_through_null (); /* { dg-ice "Segmentation fault" } */
+ /* { dg-regexp "during GIMPLE pass: crash_test" } */
+}
--- /dev/null
+/* { dg-options "-O" } */
+
+#include "gcc-plugin.h"
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
+#include "tree.h"
+#include "stringpool.h"
+#include "toplev.h"
+#include "basic-block.h"
+#include "hash-table.h"
+#include "vec.h"
+#include "ggc.h"
+#include "basic-block.h"
+#include "tree-ssa-alias.h"
+#include "internal-fn.h"
+#include "gimple.h"
+#include "gimple-iterator.h"
+#include "gimple-fold.h"
+#include "tree-eh.h"
+#include "gimple-expr.h"
+#include "is-a.h"
+#include "tree.h"
+#include "tree-pass.h"
+#include "intl.h"
+#include "plugin-version.h"
+#include "c-family/c-common.h"
+#include "diagnostic.h"
+#include "context.h"
+
+int plugin_is_GPL_compatible;
+
+/* A custom pass for injecting a crash in the middle-end when compiling
+ certain functions. */
+
+const pass_data pass_data_crash_test =
+{
+ GIMPLE_PASS, /* type */
+ "crash_test", /* name */
+ OPTGROUP_NONE, /* optinfo_flags */
+ TV_NONE, /* tv_id */
+ PROP_ssa, /* properties_required */
+ 0, /* properties_provided */
+ 0, /* properties_destroyed */
+ 0, /* todo_flags_start */
+ 0, /* todo_flags_finish */
+};
+
+class pass_crash_test : public gimple_opt_pass
+{
+public:
+ pass_crash_test(gcc::context *ctxt)
+ : gimple_opt_pass(pass_data_crash_test, ctxt)
+ {}
+
+ /* opt_pass methods: */
+ bool gate (function *) final override { return true; }
+ unsigned int execute (function *) final override;
+
+}; // class pass_test_groups
+
+/* Determine if STMT is a call to a function named FUNCNAME.
+ If so, return STMT as a gcall *. Otherwise return NULL. */
+
+static gcall *
+check_for_named_call (gimple *stmt, const char *funcname)
+{
+ gcc_assert (funcname);
+
+ gcall *call = dyn_cast <gcall *> (stmt);
+ if (!call)
+ return NULL;
+
+ tree fndecl = gimple_call_fndecl (call);
+ if (!fndecl)
+ return NULL;
+
+ if (strcmp (IDENTIFIER_POINTER (DECL_NAME (fndecl)), funcname))
+ return NULL;
+
+ return call;
+}
+
+unsigned int
+pass_crash_test::execute (function *fun)
+{
+ gimple_stmt_iterator gsi;
+ basic_block bb;
+
+ FOR_EACH_BB_FN (bb, fun)
+ for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
+ {
+ gimple *stmt = gsi_stmt (gsi);
+ if (gcall *call = check_for_named_call (stmt, "inject_ice"))
+ {
+ input_location = stmt->location;
+ internal_error ("I'm sorry Dave, I'm afraid I can't do that");
+ }
+ if (gcall *call = check_for_named_call (stmt,
+ "inject_write_through_null"))
+ {
+ input_location = stmt->location;
+ int *p = NULL;
+ *p = 42;
+ }
+ }
+
+ return 0;
+}
+
+/* Entrypoint for the plugin.
+ Create and register the custom pass. */
+
+int
+plugin_init (struct plugin_name_args *plugin_info,
+ struct plugin_gcc_version *version)
+{
+ struct register_pass_info pass_info;
+ const char *plugin_name = plugin_info->base_name;
+ int argc = plugin_info->argc;
+ struct plugin_argument *argv = plugin_info->argv;
+
+ if (!plugin_default_version_check (version, &gcc_version))
+ return 1;
+
+ pass_info.pass = new pass_crash_test (g);
+ pass_info.reference_pass_name = "*warn_function_noreturn";
+ pass_info.ref_pass_instance_number = 1;
+ pass_info.pos_op = PASS_POS_INSERT_AFTER;
+ register_callback (plugin_name, PLUGIN_PASS_MANAGER_SETUP, NULL,
+ &pass_info);
+
+ return 0;
+}
{ poly-int-05_plugin.c poly-int-test-1.c } \
{ poly-int-06_plugin.c poly-int-test-1.c } \
{ poly-int-07_plugin.c poly-int-test-1.c } \
+ { crash_test_plugin.c \
+ crash-test-ice-stderr.c \
+ crash-test-write-though-null-stderr.c \
+ crash-test-ice-sarif.c \
+ crash-test-write-though-null-sarif.c } \
{ diagnostic_group_plugin.c \
diagnostic-group-test-1.c } \
{ diagnostic_plugin_test_show_locus.c \