return NULL;
}
meaning get_meaning () const override;
+ diagnostic_thread_id_t get_thread_id () const final override
+ {
+ return 0;
+ }
/* Additional functionality. */
class checker_path : public diagnostic_path
{
public:
- checker_path (logger *logger) : diagnostic_path (), m_logger (logger) {}
+ checker_path (logger *logger)
+ : diagnostic_path (),
+ m_thread ("main"),
+ m_logger (logger)
+ {}
/* Implementation of diagnostic_path vfuncs. */
{
return *m_events[idx];
}
+ unsigned num_threads () const final override
+ {
+ return 1;
+ }
+ const diagnostic_thread &
+ get_thread (diagnostic_thread_id_t) const final override
+ {
+ return m_thread;
+ }
checker_event *get_checker_event (int idx)
{
private:
DISABLE_COPY_AND_ASSIGN(checker_path);
+ simple_diagnostic_thread m_thread;
+
/* The events that have occurred along this path. */
auto_delete_vec<checker_event> m_events;
The %@ format code requires that known_p be true for the event ID. */
typedef diagnostic_event_id_t *diagnostic_event_id_ptr;
+/* A type for compactly referring to a particular thread within a
+ diagnostic_path. Typically there is just one thread per path,
+ with id 0. */
+typedef unsigned diagnostic_thread_id_t;
+
#endif /* ! GCC_DIAGNOSTIC_EVENT_ID_H */
sarif_builder *builder);
};
+/* Subclass of sarif_object for SARIF threadFlow objects
+ (SARIF v2.1.0 section 3.37) for PATH. */
+
+class sarif_thread_flow : public sarif_object
+{
+public:
+ sarif_thread_flow (const diagnostic_thread &thread);
+
+ void add_location (json::object *thread_flow_loc_obj)
+ {
+ m_locations_arr->append (thread_flow_loc_obj);
+ }
+
+private:
+ json::array *m_locations_arr;
+};
+
/* A class for managing SARIF output (for -fdiagnostics-format=sarif-stderr
and -fdiagnostics-format=sarif-file).
json::object *
make_logical_location_object (const logical_location &logical_loc) const;
json::object *make_code_flow_object (const diagnostic_path &path);
- json::object *make_thread_flow_object (const diagnostic_path &path);
json::object *
- make_thread_flow_location_object (const diagnostic_event &event);
+ make_thread_flow_location_object (const diagnostic_event &event,
+ int path_event_idx);
json::array *maybe_make_kinds_array (diagnostic_event::meaning m) const;
json::object *maybe_make_physical_location_object (location_t loc);
json::object *make_artifact_location_object (location_t loc);
set ("level", new json::string ("error"));
}
+/* class sarif_thread_flow : public sarif_object. */
+
+sarif_thread_flow::sarif_thread_flow (const diagnostic_thread &thread)
+{
+ /* "id" property (SARIF v2.1.0 section 3.37.2). */
+ label_text name (thread.get_name (false));
+ set ("id", new json::string (name.get ()));
+
+ /* "locations" property (SARIF v2.1.0 section 3.37.6). */
+ m_locations_arr = new json::array ();
+ set ("locations", m_locations_arr);
+}
+
/* class sarif_builder. */
/* sarif_builder's ctor. */
{
json::object *code_flow_obj = new json::object ();
- /* "threadFlows" property (SARIF v2.1.0 section 3.36.3).
- Currently we only support one thread per result. */
+ /* "threadFlows" property (SARIF v2.1.0 section 3.36.3). */
json::array *thread_flows_arr = new json::array ();
- json::object *thread_flow_obj = make_thread_flow_object (path);
- thread_flows_arr->append (thread_flow_obj);
- code_flow_obj->set ("threadFlows", thread_flows_arr);
- return code_flow_obj;
-}
-
-/* Make a threadFlow object (SARIF v2.1.0 section 3.37) for PATH. */
-
-json::object *
-sarif_builder::make_thread_flow_object (const diagnostic_path &path)
-{
- json::object *thread_flow_obj = new json::object ();
-
- /* "locations" property (SARIF v2.1.0 section 3.37.6). */
- json::array *locations_arr = new json::array ();
+ /* Walk the events, consolidating into per-thread threadFlow objects,
+ using the index with PATH as the overall executionOrder. */
+ hash_map<int_hash<diagnostic_thread_id_t, -1, -2>,
+ sarif_thread_flow *> thread_id_map;
for (unsigned i = 0; i < path.num_events (); i++)
{
const diagnostic_event &event = path.get_event (i);
+ const diagnostic_thread_id_t thread_id = event.get_thread_id ();
+ sarif_thread_flow *thread_flow_obj;
+
+ if (sarif_thread_flow **slot = thread_id_map.get (thread_id))
+ thread_flow_obj = *slot;
+ else
+ {
+ const diagnostic_thread &thread = path.get_thread (thread_id);
+ thread_flow_obj = new sarif_thread_flow (thread);
+ thread_flows_arr->append (thread_flow_obj);
+ thread_id_map.put (thread_id, thread_flow_obj);
+ }
+
+ /* Add event to thread's threadFlow object. */
json::object *thread_flow_loc_obj
- = make_thread_flow_location_object (event);
- locations_arr->append (thread_flow_loc_obj);
+ = make_thread_flow_location_object (event, i);
+ thread_flow_obj->add_location (thread_flow_loc_obj);
}
- thread_flow_obj->set ("locations", locations_arr);
+ code_flow_obj->set ("threadFlows", thread_flows_arr);
- return thread_flow_obj;
+ return code_flow_obj;
}
/* Make a threadFlowLocation object (SARIF v2.1.0 section 3.38) for EVENT. */
json::object *
-sarif_builder::make_thread_flow_location_object (const diagnostic_event &ev)
+sarif_builder::make_thread_flow_location_object (const diagnostic_event &ev,
+ int path_event_idx)
{
json::object *thread_flow_loc_obj = new json::object ();
thread_flow_loc_obj->set ("nestingLevel",
new json::integer_number (ev.get_stack_depth ()));
+ /* "executionOrder" property (SARIF v2.1.0 3.38.11).
+ Offset by 1 to match the human-readable values emitted by %@. */
+ thread_flow_loc_obj->set ("executionOrder",
+ new json::integer_number (path_event_idx + 1));
+
/* It might be nice to eventually implement the following for -fanalyzer:
- the "stack" property (SARIF v2.1.0 section 3.38.5)
- the "state" property (SARIF v2.1.0 section 3.38.9)
virtual const logical_location *get_logical_location () const = 0;
virtual meaning get_meaning () const = 0;
+
+ virtual diagnostic_thread_id_t get_thread_id () const = 0;
+};
+
+/* Abstract base class representing a thread of execution within
+ a diagnostic_path.
+ Each diagnostic_event is associated with one thread.
+ Typically there is just one thread per diagnostic_path. */
+
+class diagnostic_thread
+{
+public:
+ virtual ~diagnostic_thread () {}
+ virtual label_text get_name (bool can_colorize) const = 0;
};
/* Abstract base class for getting at a sequence of events. */
virtual ~diagnostic_path () {}
virtual unsigned num_events () const = 0;
virtual const diagnostic_event & get_event (int idx) const = 0;
+ virtual unsigned num_threads () const = 0;
+ virtual const diagnostic_thread &
+ get_thread (diagnostic_thread_id_t) const = 0;
bool interprocedural_p () const;
+ bool multithreaded_p () const;
private:
bool get_first_event_in_a_function (unsigned *out_idx) const;
{
public:
simple_diagnostic_event (location_t loc, tree fndecl, int depth,
- const char *desc);
+ const char *desc,
+ diagnostic_thread_id_t thread_id = 0);
~simple_diagnostic_event ();
location_t get_location () const final override { return m_loc; }
{
return meaning ();
}
+ diagnostic_thread_id_t get_thread_id () const final override
+ {
+ return m_thread_id;
+ }
private:
location_t m_loc;
tree m_fndecl;
int m_depth;
char *m_desc; // has been i18n-ed and formatted
+ diagnostic_thread_id_t m_thread_id;
+};
+
+/* A simple implementation of diagnostic_thread. */
+
+class simple_diagnostic_thread : public diagnostic_thread
+{
+public:
+ simple_diagnostic_thread (const char *name) : m_name (name) {}
+ label_text get_name (bool) const final override
+ {
+ return label_text::borrow (m_name);
+ }
+
+private:
+ const char *m_name; // has been i18n-ed and formatted
};
/* A simple implementation of diagnostic_path, as a vector of
class simple_diagnostic_path : public diagnostic_path
{
public:
- simple_diagnostic_path (pretty_printer *event_pp)
- : m_event_pp (event_pp) {}
+ simple_diagnostic_path (pretty_printer *event_pp);
unsigned num_events () const final override;
const diagnostic_event & get_event (int idx) const final override;
+ unsigned num_threads () const final override;
+ const diagnostic_thread &
+ get_thread (diagnostic_thread_id_t) const final override;
+
+ diagnostic_thread_id_t add_thread (const char *name);
diagnostic_event_id_t add_event (location_t loc, tree fndecl, int depth,
const char *fmt, ...)
ATTRIBUTE_GCC_DIAG(5,6);
+ diagnostic_event_id_t
+ add_thread_event (diagnostic_thread_id_t thread_id,
+ location_t loc, tree fndecl, int depth,
+ const char *fmt, ...)
+ ATTRIBUTE_GCC_DIAG(6,7);
private:
+ auto_delete_vec<simple_diagnostic_thread> m_threads;
auto_delete_vec<simple_diagnostic_event> m_events;
/* (for use by add_event). */
public:
layout (diagnostic_context *context,
rich_location *richloc,
- diagnostic_t diagnostic_kind);
+ diagnostic_t diagnostic_kind,
+ pretty_printer *pp = nullptr);
bool maybe_add_location_range (const location_range *loc_range,
unsigned original_idx,
layout::layout (diagnostic_context * context,
rich_location *richloc,
- diagnostic_t diagnostic_kind)
+ diagnostic_t diagnostic_kind,
+ pretty_printer *pp)
: m_context (context),
- m_pp (context->printer),
+ m_pp (pp ? pp : context->printer),
m_policy (make_policy (*context, *richloc)),
m_primary_loc (richloc->get_range (0)->m_loc),
m_exploc (richloc->get_expanded_location (0), m_policy,
}
/* Print the physical source code corresponding to the location of
- this diagnostic, with additional annotations. */
+ this diagnostic, with additional annotations.
+ If PP is non-null, then use it rather than CONTEXT's printer. */
void
diagnostic_show_locus (diagnostic_context * context,
rich_location *richloc,
- diagnostic_t diagnostic_kind)
+ diagnostic_t diagnostic_kind,
+ pretty_printer *pp)
{
location_t loc = richloc->get_loc ();
/* Do nothing if source-printing has been disabled. */
context->last_location = loc;
- layout layout (context, richloc, diagnostic_kind);
+ layout layout (context, richloc, diagnostic_kind, pp);
for (int line_span_idx = 0; line_span_idx < layout.get_num_line_spans ();
line_span_idx++)
{
}
}
+/* class simple_diagnostic_path : public diagnostic_path. */
+
+simple_diagnostic_path::simple_diagnostic_path (pretty_printer *event_pp)
+ : m_event_pp (event_pp)
+{
+ add_thread ("main");
+}
+
/* Implementation of diagnostic_path::num_events vfunc for
simple_diagnostic_path: simply get the number of events in the vec. */
return *m_events[idx];
}
+unsigned
+simple_diagnostic_path::num_threads () const
+{
+ return m_threads.length ();
+}
+
+const diagnostic_thread &
+simple_diagnostic_path::get_thread (diagnostic_thread_id_t idx) const
+{
+ return *m_threads[idx];
+}
+
+diagnostic_thread_id_t
+simple_diagnostic_path::add_thread (const char *name)
+{
+ m_threads.safe_push (new simple_diagnostic_thread (name));
+ return m_threads.length () - 1;
+}
+
/* Add an event to this path at LOC within function FNDECL at
stack depth DEPTH.
return diagnostic_event_id_t (m_events.length () - 1);
}
+diagnostic_event_id_t
+simple_diagnostic_path::add_thread_event (diagnostic_thread_id_t thread_id,
+ location_t loc,
+ tree fndecl,
+ int depth,
+ const char *fmt, ...)
+{
+ pretty_printer *pp = m_event_pp;
+ pp_clear_output_area (pp);
+
+ text_info ti;
+ rich_location rich_loc (line_table, UNKNOWN_LOCATION);
+
+ va_list ap;
+
+ va_start (ap, fmt);
+
+ ti.format_spec = _(fmt);
+ ti.args_ptr = ≈
+ ti.err_no = 0;
+ ti.x_data = NULL;
+ ti.m_richloc = &rich_loc;
+
+ pp_format (pp, &ti);
+ pp_output_formatted_text (pp);
+
+ va_end (ap);
+
+ simple_diagnostic_event *new_event
+ = new simple_diagnostic_event (loc, fndecl, depth, pp_formatted_text (pp),
+ thread_id);
+ m_events.safe_push (new_event);
+
+ pp_clear_output_area (pp);
+
+ return diagnostic_event_id_t (m_events.length () - 1);
+}
+
/* struct simple_diagnostic_event. */
/* simple_diagnostic_event's ctor. */
-simple_diagnostic_event::simple_diagnostic_event (location_t loc,
- tree fndecl,
- int depth,
- const char *desc)
-: m_loc (loc), m_fndecl (fndecl), m_depth (depth), m_desc (xstrdup (desc))
+simple_diagnostic_event::
+simple_diagnostic_event (location_t loc,
+ tree fndecl,
+ int depth,
+ const char *desc,
+ diagnostic_thread_id_t thread_id)
+: m_loc (loc), m_fndecl (fndecl), m_depth (depth), m_desc (xstrdup (desc)),
+ m_thread_id (thread_id)
{
}
extern void diagnostic_report_current_module (diagnostic_context *, location_t);
extern void diagnostic_show_locus (diagnostic_context *,
rich_location *richloc,
- diagnostic_t diagnostic_kind);
+ diagnostic_t diagnostic_kind,
+ pretty_printer *pp = nullptr);
extern void diagnostic_show_any_path (diagnostic_context *, diagnostic_info *);
/* Because we read source files a second time after the frontend did it the
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-fdiagnostics-path-format=inline-events -fdiagnostics-show-caret -fdiagnostics-show-line-numbers" } */
+/* { dg-enable-nn-line-numbers "" } */
+
+extern void acquire_lock_a(void);
+extern void acquire_lock_b(void);
+
+void foo ()
+{
+ acquire_lock_a ();
+ acquire_lock_b ();
+}
+
+void bar ()
+{
+ acquire_lock_b ();
+ acquire_lock_a (); /* { dg-warning "deadlock due to inconsistent lock acquisition order" } */
+}
+
+/* { dg-begin-multiline-output "" }
+ NN | acquire_lock_a ();
+ | ^~~~~~~~~~~~~~~~~
+Thread: 'Thread 1'
+ 'foo': event 1
+ |
+ | NN | {
+ | | ^
+ | | |
+ | | (1) entering 'foo'
+ |
+ +--> 'foo': event 2
+ |
+ | NN | acquire_lock_a ();
+ | | ^~~~~~~~~~~~~~~~~
+ | | |
+ | | (2) lock a is now held by thread 1
+ |
+
+Thread: 'Thread 2'
+ 'bar': event 3
+ |
+ | NN | {
+ | | ^
+ | | |
+ | | (3) entering 'bar'
+ |
+ +--> 'bar': event 4
+ |
+ | NN | acquire_lock_b ();
+ | | ^~~~~~~~~~~~~~~~~
+ | | |
+ | | (4) lock b is now held by thread 2
+ |
+
+Thread: 'Thread 1'
+ 'foo': event 5
+ |
+ | NN | acquire_lock_b ();
+ | | ^~~~~~~~~~~~~~~~~
+ | | |
+ | | (5) deadlocked due to waiting for lock b in thread 1...
+ |
+
+Thread: 'Thread 2'
+ 'bar': event 6
+ |
+ | NN | acquire_lock_a ();
+ | | ^~~~~~~~~~~~~~~~~
+ | | |
+ | | (6) ...whilst waiting for lock a in thread 2
+ |
+ { dg-end-multiline-output "" } */
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-fdiagnostics-format=sarif-file" } */
+
+extern void acquire_lock_a(void);
+extern void acquire_lock_b(void);
+
+void foo ()
+{
+ acquire_lock_a ();
+ acquire_lock_b ();
+}
+
+void bar ()
+{
+ acquire_lock_b ();
+ acquire_lock_a ();
+}
+
+/* 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 {"version": "2.1.0"} } }
+ { dg-final { scan-sarif-file {"text": "deadlock due to inconsistent lock acquisition order"} } }
+ { dg-final { scan-sarif-file {"id": "Thread 1"} } }
+ { dg-final { scan-sarif-file {"executionOrder": 1} } }
+ { dg-final { scan-sarif-file {"executionOrder": 2} } }
+ { dg-final { scan-sarif-file {"executionOrder": 5} } }
+ { dg-final { scan-sarif-file {"id": "Thread 2"} } }
+ { dg-final { scan-sarif-file {"executionOrder": 3} } }
+ { dg-final { scan-sarif-file {"executionOrder": 4} } }
+ { dg-final { scan-sarif-file {"executionOrder": 6} } } */
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-fdiagnostics-path-format=separate-events" } */
+
+extern void acquire_lock_a(void);
+extern void acquire_lock_b(void);
+
+void foo ()
+{ /* { dg-message "\\(1\\) entering 'foo'" } */
+ acquire_lock_a (); /* { dg-message "\\(2\\) lock a is now held by thread 1" } */
+ acquire_lock_b (); /* { dg-message "\\(5\\) deadlocked due to waiting for lock b in thread 1\.\.\." } */
+}
+
+void bar ()
+{ /* { dg-message "\\(3\\) entering 'bar'" } */
+ acquire_lock_b (); /* { dg-message "\\(4\\) lock b is now held by thread 2" } */
+ acquire_lock_a (); /* { dg-warning "deadlock due to inconsistent lock acquisition order" } */
+ /* { dg-message "\\(6\\) \.\.\.whilst waiting for lock a in thread 2" "" { target *-*-* } .-1 } */
+}
/* If FUN's name matches FUNCNAME, write the function and its start location
into *OUT_ENTRY. */
-static void
+static bool
check_for_named_function (function *fun, const char *funcname,
event_location_t *out_entry)
{
gcc_assert (funcname);
if (strcmp (IDENTIFIER_POINTER (DECL_NAME (fun->decl)), funcname))
- return;
+ return false;
*out_entry = event_location_t (fun, fun->function_start_locus);
+ return true;
}
: simple_diagnostic_path (event_pp)
{
}
+ void add_event_2 (event_location_t evloc, int stack_depth,
+ const char *desc,
+ diagnostic_thread_id_t thread_id = 0)
+ {
+ gcc_assert (evloc.m_fun);
+ add_thread_event (thread_id, evloc.m_loc, evloc.m_fun->decl,
+ stack_depth, desc);
+ }
void add_entry (event_location_t evloc, int stack_depth,
- const char *funcname)
+ const char *funcname,
+ diagnostic_thread_id_t thread_id = 0)
{
gcc_assert (evloc.m_fun);
- add_event (evloc.m_loc, evloc.m_fun->decl, stack_depth,
- "entering %qs", funcname);
+ add_thread_event (thread_id, evloc.m_loc, evloc.m_fun->decl, stack_depth,
+ "entering %qs", funcname);
}
void add_call (event_location_t call_evloc, int caller_stack_depth,
}
}
+/* Example 4: a multithreaded path. */
+
+static void
+example_4 ()
+{
+ gimple_stmt_iterator gsi;
+ basic_block bb;
+
+ event_location_t entry_to_foo;
+ event_location_t entry_to_bar;
+ event_location_t call_to_acquire_lock_a_in_foo;
+ event_location_t call_to_acquire_lock_b_in_foo;
+ event_location_t call_to_acquire_lock_a_in_bar;
+ event_location_t call_to_acquire_lock_b_in_bar;
+
+ cgraph_node *node;
+ FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node)
+ {
+ function *fun = node->get_fun ();
+ FOR_EACH_BB_FN (bb, fun)
+ {
+ bool in_foo = check_for_named_function (fun, "foo",
+ &entry_to_foo);
+ bool in_bar = check_for_named_function (fun, "bar",
+ &entry_to_bar);
+ for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
+ {
+ gimple *stmt = gsi_stmt (gsi);
+ event_location_t *evloc = NULL;
+ gcall *call = NULL;
+ if (call = check_for_named_call (stmt, "acquire_lock_a", 0))
+ evloc = (in_foo
+ ? &call_to_acquire_lock_a_in_foo
+ : &call_to_acquire_lock_a_in_bar);
+ else if (call
+ = check_for_named_call (stmt, "acquire_lock_b", 0))
+ evloc = (in_foo
+ ? &call_to_acquire_lock_b_in_foo
+ : &call_to_acquire_lock_b_in_bar);
+ if (evloc)
+ evloc->set (call, fun);
+ }
+ }
+ }
+
+ if (call_to_acquire_lock_a_in_foo.m_fun)
+ {
+ auto_diagnostic_group d;
+
+ gcc_rich_location richloc (call_to_acquire_lock_a_in_bar.m_loc);
+ test_diagnostic_path path (global_dc->printer);
+ diagnostic_thread_id_t thread_1 = path.add_thread ("Thread 1");
+ diagnostic_thread_id_t thread_2 = path.add_thread ("Thread 2");
+ path.add_entry (entry_to_foo, 0, "foo", thread_1);
+ path.add_event_2 (call_to_acquire_lock_a_in_foo, 1,
+ "lock a is now held by thread 1", thread_1);
+ path.add_entry (entry_to_bar, 0, "bar", thread_2);
+ path.add_event_2 (call_to_acquire_lock_b_in_bar, 1,
+ "lock b is now held by thread 2", thread_2);
+ path.add_event_2 (call_to_acquire_lock_b_in_foo, 1,
+ "deadlocked due to waiting for lock b in thread 1...",
+ thread_1);
+ path.add_event_2 (call_to_acquire_lock_a_in_bar, 1,
+ "...whilst waiting for lock a in thread 2",
+ thread_2);
+ richloc.set_path (&path);
+
+ diagnostic_metadata m;
+ warning_meta (&richloc, m, 0,
+ "deadlock due to inconsistent lock acquisition order");
+ }
+}
+
unsigned int
pass_test_show_path::execute (function *)
{
example_1 ();
example_2 ();
example_3 ();
+ example_4 ();
return 0;
}
diagnostic-test-paths-3.c \
diagnostic-test-paths-4.c \
diagnostic-test-paths-5.c \
+ diagnostic-test-paths-multithreaded-inline-events.c \
+ diagnostic-test-paths-multithreaded-sarif.c \
+ diagnostic-test-paths-multithreaded-separate-events.c \
diagnostic-path-format-plain.c \
diagnostic-path-format-none.c \
diagnostic-path-format-separate-events.c \
<http://www.gnu.org/licenses/>. */
#include "config.h"
+#define INCLUDE_VECTOR
#include "system.h"
#include "coretypes.h"
#include "tree.h"
const diagnostic_event &e2,
bool check_locations)
{
+ if (e1.get_thread_id () != e2.get_thread_id ())
+ return false;
+
if (e1.get_fndecl () != e2.get_fndecl ())
return false;
return true;
}
-/* A range of consecutive events within a diagnostic_path,
- all with the same fndecl and stack_depth, and which are suitable
+struct event_range;
+struct path_summary;
+class thread_event_printer;
+
+/* A bundle of information about all of the events in a diagnostic_path
+ relating to a specific path, for use by path_summary. */
+
+class per_thread_summary
+{
+public:
+ per_thread_summary (label_text name, unsigned swimlane_idx)
+ : m_name (std::move (name)),
+ m_swimlane_idx (swimlane_idx),
+ m_min_depth (INT_MAX),
+ m_max_depth (INT_MIN)
+ {}
+
+ void update_depth_limits (int stack_depth)
+ {
+ if (stack_depth < m_min_depth)
+ m_min_depth = stack_depth;
+ if (stack_depth > m_max_depth)
+ m_max_depth = stack_depth;
+ }
+
+ const char *get_name () const { return m_name.get (); }
+ unsigned get_swimlane_index () const { return m_swimlane_idx; }
+
+private:
+ friend struct path_summary;
+ friend class thread_event_printer;
+
+ const label_text m_name;
+
+ /* The "swimlane index" is the order in which this per_thread_summary
+ was created, for use when printing the events. */
+ const unsigned m_swimlane_idx;
+
+ // The event ranges specific to this thread:
+ auto_vec<event_range *> m_event_ranges;
+ int m_min_depth;
+ int m_max_depth;
+};
+
+/* A range of consecutive events within a diagnostic_path, all within the
+ same thread, and with the same fndecl and stack_depth, and which are suitable
to print with a single call to diagnostic_show_locus. */
struct event_range
{
event_range (const diagnostic_path *path, unsigned start_idx,
- const diagnostic_event &initial_event)
+ const diagnostic_event &initial_event,
+ const per_thread_summary &t)
: m_path (path),
m_initial_event (initial_event),
m_fndecl (initial_event.get_fndecl ()),
m_stack_depth (initial_event.get_stack_depth ()),
m_start_idx (start_idx), m_end_idx (start_idx),
m_path_label (path, start_idx),
- m_richloc (initial_event.get_location (), &m_path_label)
+ m_richloc (initial_event.get_location (), &m_path_label),
+ m_thread_id (initial_event.get_thread_id ()),
+ m_per_thread_summary (t)
{}
bool maybe_add_event (const diagnostic_event &new_ev, unsigned idx,
/* Print the events in this range to DC, typically as a single
call to the printer's diagnostic_show_locus. */
- void print (diagnostic_context *dc)
+ void print (diagnostic_context *dc, pretty_printer *pp)
{
location_t initial_loc = m_initial_event.get_location ();
const diagnostic_event &iter_event = m_path->get_event (i);
diagnostic_event_id_t event_id (i);
label_text event_text (iter_event.get_desc (true));
- pretty_printer *pp = dc->printer;
pp_printf (pp, " %@: %s", &event_id, event_text.get ());
pp_newline (pp);
}
}
/* Call diagnostic_show_locus to show the events using labels. */
- diagnostic_show_locus (dc, &m_richloc, DK_DIAGNOSTIC_PATH);
+ diagnostic_show_locus (dc, &m_richloc, DK_DIAGNOSTIC_PATH, pp);
/* If we have a macro expansion, show the expansion to the user. */
if (linemap_location_from_macro_expansion_p (line_table, initial_loc))
unsigned m_end_idx;
path_label m_path_label;
gcc_rich_location m_richloc;
+ diagnostic_thread_id_t m_thread_id;
+ const per_thread_summary &m_per_thread_summary;
};
/* A struct for grouping together the events in a diagnostic_path into
- ranges of events, partitioned by stack frame (i.e. by fndecl and
- stack depth). */
+ ranges of events, partitioned by thread and by stack frame (i.e. by fndecl
+ and stack depth). */
struct path_summary
{
path_summary (const diagnostic_path &path, bool check_rich_locations);
unsigned get_num_ranges () const { return m_ranges.length (); }
+ bool multithreaded_p () const { return m_per_thread_summary.length () > 1; }
+
+ const per_thread_summary &get_events_for_thread_id (diagnostic_thread_id_t tid)
+ {
+ per_thread_summary **slot = m_thread_id_to_events.get (tid);
+ gcc_assert (slot);
+ gcc_assert (*slot);
+ return **slot;
+ }
auto_delete_vec <event_range> m_ranges;
+ auto_delete_vec <per_thread_summary> m_per_thread_summary;
+ hash_map<int_hash<diagnostic_thread_id_t, -1, -2>,
+ per_thread_summary *> m_thread_id_to_events;
+
+private:
+ per_thread_summary &
+ get_or_create_events_for_thread_id (const diagnostic_path &path,
+ diagnostic_thread_id_t tid)
+ {
+ if (per_thread_summary **slot = m_thread_id_to_events.get (tid))
+ return **slot;
+
+ const diagnostic_thread &thread = path.get_thread (tid);
+ per_thread_summary *pts = new per_thread_summary (thread.get_name (false),
+ m_per_thread_summary.length ());
+ m_thread_id_to_events.put (tid, pts);
+ m_per_thread_summary.safe_push (pts);
+ return *pts;
+ }
};
/* path_summary's ctor. */
for (unsigned idx = 0; idx < num_events; idx++)
{
const diagnostic_event &event = path.get_event (idx);
+ const diagnostic_thread_id_t thread_id = event.get_thread_id ();
+ per_thread_summary &pts
+ = get_or_create_events_for_thread_id (path, thread_id);
+
+ pts.update_depth_limits (event.get_stack_depth ());
+
if (cur_event_range)
if (cur_event_range->maybe_add_event (event, idx, check_rich_locations))
continue;
- cur_event_range = new event_range (&path, idx, event);
+ cur_event_range = new event_range (&path, idx, event, pts);
m_ranges.safe_push (cur_event_range);
+ pts.m_event_ranges.safe_push (cur_event_range);
}
}
pp_string (pp, n);
}
+static const int base_indent = 2;
+static const int per_frame_indent = 2;
+
+/* A bundle of state for printing event_range instances for a particular
+ thread. */
+
+class thread_event_printer
+{
+public:
+ thread_event_printer (const per_thread_summary &t, bool show_depths)
+ : m_per_thread_summary (t),
+ m_show_depths (show_depths),
+ m_cur_indent (base_indent),
+ m_vbar_column_for_depth (),
+ m_num_printed (0)
+ {
+ }
+
+ /* Get the previous event_range within this thread, if any. */
+ const event_range *get_any_prev_range () const
+ {
+ if (m_num_printed > 0)
+ return m_per_thread_summary.m_event_ranges[m_num_printed - 1];
+ else
+ return nullptr;
+ }
+
+ /* Get the next event_range within this thread, if any. */
+ const event_range *get_any_next_range () const
+ {
+ if (m_num_printed < m_per_thread_summary.m_event_ranges.length () - 1)
+ return m_per_thread_summary.m_event_ranges[m_num_printed + 1];
+ else
+ return nullptr;
+ }
+
+ void print_swimlane_for_event_range (diagnostic_context *dc,
+ pretty_printer *pp,
+ event_range *range)
+ {
+ const char *const line_color = "path";
+ const char *start_line_color
+ = colorize_start (pp_show_color (pp), line_color);
+ const char *end_line_color = colorize_stop (pp_show_color (pp));
+
+ write_indent (pp, m_cur_indent);
+ if (const event_range *prev_range = get_any_prev_range ())
+ {
+ if (range->m_stack_depth > prev_range->m_stack_depth)
+ {
+ /* Show pushed stack frame(s). */
+ const char *push_prefix = "+--> ";
+ pp_string (pp, start_line_color);
+ pp_string (pp, push_prefix);
+ pp_string (pp, end_line_color);
+ m_cur_indent += strlen (push_prefix);
+ }
+ }
+ if (range->m_fndecl)
+ {
+ print_fndecl (pp, range->m_fndecl, true);
+ pp_string (pp, ": ");
+ }
+ if (range->m_start_idx == range->m_end_idx)
+ pp_printf (pp, "event %i",
+ range->m_start_idx + 1);
+ else
+ pp_printf (pp, "events %i-%i",
+ range->m_start_idx + 1, range->m_end_idx + 1);
+ if (m_show_depths)
+ pp_printf (pp, " (depth %i)", range->m_stack_depth);
+ pp_newline (pp);
+
+ /* Print a run of events. */
+ {
+ write_indent (pp, m_cur_indent + per_frame_indent);
+ pp_string (pp, start_line_color);
+ pp_string (pp, "|");
+ pp_string (pp, end_line_color);
+ pp_newline (pp);
+
+ char *saved_prefix = pp_take_prefix (pp);
+ char *prefix;
+ {
+ pretty_printer tmp_pp;
+ write_indent (&tmp_pp, m_cur_indent + per_frame_indent);
+ pp_string (&tmp_pp, start_line_color);
+ pp_string (&tmp_pp, "|");
+ pp_string (&tmp_pp, end_line_color);
+ prefix = xstrdup (pp_formatted_text (&tmp_pp));
+ }
+ pp_set_prefix (pp, prefix);
+ pp_prefixing_rule (pp) = DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE;
+ range->print (dc, pp);
+ pp_set_prefix (pp, saved_prefix);
+
+ write_indent (pp, m_cur_indent + per_frame_indent);
+ pp_string (pp, start_line_color);
+ pp_string (pp, "|");
+ pp_string (pp, end_line_color);
+ pp_newline (pp);
+ }
+
+ if (const event_range *next_range = get_any_next_range ())
+ {
+ if (range->m_stack_depth > next_range->m_stack_depth)
+ {
+ if (m_vbar_column_for_depth.get (next_range->m_stack_depth))
+ {
+ /* Show returning from stack frame(s), by printing
+ something like:
+ " |\n"
+ " <------------ +\n"
+ " |\n". */
+ int vbar_for_next_frame
+ = *m_vbar_column_for_depth.get (next_range->m_stack_depth);
+
+ int indent_for_next_frame
+ = vbar_for_next_frame - per_frame_indent;
+ write_indent (pp, vbar_for_next_frame);
+ pp_string (pp, start_line_color);
+ pp_character (pp, '<');
+ for (int i = indent_for_next_frame + per_frame_indent;
+ i < m_cur_indent + per_frame_indent - 1; i++)
+ pp_character (pp, '-');
+ pp_character (pp, '+');
+ pp_string (pp, end_line_color);
+ pp_newline (pp);
+ m_cur_indent = indent_for_next_frame;
+
+ write_indent (pp, vbar_for_next_frame);
+ pp_string (pp, start_line_color);
+ pp_character (pp, '|');
+ pp_string (pp, end_line_color);
+ pp_newline (pp);
+ }
+ else
+ {
+ /* Handle disjoint paths (e.g. a callback at some later
+ time). */
+ m_cur_indent = base_indent;
+ }
+ }
+ else if (range->m_stack_depth < next_range->m_stack_depth)
+ {
+ /* Prepare to show pushed stack frame. */
+ gcc_assert (range->m_stack_depth != EMPTY);
+ gcc_assert (range->m_stack_depth != DELETED);
+ m_vbar_column_for_depth.put (range->m_stack_depth,
+ m_cur_indent + per_frame_indent);
+ m_cur_indent += per_frame_indent;
+ }
+ }
+
+ m_num_printed++;
+ }
+
+ int get_cur_indent () const { return m_cur_indent; }
+
+private:
+ const per_thread_summary &m_per_thread_summary;
+ bool m_show_depths;
+
+ /* Print the ranges. */
+ int m_cur_indent;
+
+ /* Keep track of column numbers of existing '|' characters for
+ stack depths we've already printed. */
+ static const int EMPTY = -1;
+ static const int DELETED = -2;
+ typedef int_hash <int, EMPTY, DELETED> vbar_hash;
+ hash_map <vbar_hash, int> m_vbar_column_for_depth;
+
+ /* How many event ranges within this swimlane have we printed.
+ This is the index of the next event_range to print. */
+ unsigned m_num_printed;
+};
+
/* Print path_summary PS to DC, giving an overview of the interprocedural
calls and returns.
For events with UNKNOWN_LOCATION, print a summary of each the event. */
-void
+static void
print_path_summary_as_text (const path_summary *ps, diagnostic_context *dc,
bool show_depths)
{
pretty_printer *pp = dc->printer;
- const int per_frame_indent = 2;
-
- const char *const line_color = "path";
- const char *start_line_color
- = colorize_start (pp_show_color (pp), line_color);
- const char *end_line_color = colorize_stop (pp_show_color (pp));
+ std::vector<thread_event_printer> thread_event_printers;
+ for (auto t : ps->m_per_thread_summary)
+ thread_event_printers.push_back (thread_event_printer (*t, show_depths));
- /* Keep track of column numbers of existing '|' characters for
- stack depths we've already printed. */
- const int EMPTY = -1;
- const int DELETED = -2;
- typedef int_hash <int, EMPTY, DELETED> vbar_hash;
- hash_map <vbar_hash, int> vbar_column_for_depth;
-
- /* Print the ranges. */
- const int base_indent = 2;
- int cur_indent = base_indent;
unsigned i;
event_range *range;
FOR_EACH_VEC_ELT (ps->m_ranges, i, range)
{
- write_indent (pp, cur_indent);
- if (i > 0)
- {
- const event_range *prev_range = ps->m_ranges[i - 1];
- if (range->m_stack_depth > prev_range->m_stack_depth)
- {
- /* Show pushed stack frame(s). */
- const char *push_prefix = "+--> ";
- pp_string (pp, start_line_color);
- pp_string (pp, push_prefix);
- pp_string (pp, end_line_color);
- cur_indent += strlen (push_prefix);
- }
- }
- if (range->m_fndecl)
- {
- print_fndecl (pp, range->m_fndecl, true);
- pp_string (pp, ": ");
- }
- if (range->m_start_idx == range->m_end_idx)
- pp_printf (pp, "event %i",
- range->m_start_idx + 1);
- else
- pp_printf (pp, "events %i-%i",
- range->m_start_idx + 1, range->m_end_idx + 1);
- if (show_depths)
- pp_printf (pp, " (depth %i)", range->m_stack_depth);
- pp_newline (pp);
-
- /* Print a run of events. */
- {
- write_indent (pp, cur_indent + per_frame_indent);
- pp_string (pp, start_line_color);
- pp_string (pp, "|");
- pp_string (pp, end_line_color);
- pp_newline (pp);
-
- char *saved_prefix = pp_take_prefix (pp);
- char *prefix;
- {
- pretty_printer tmp_pp;
- write_indent (&tmp_pp, cur_indent + per_frame_indent);
- pp_string (&tmp_pp, start_line_color);
- pp_string (&tmp_pp, "|");
- pp_string (&tmp_pp, end_line_color);
- prefix = xstrdup (pp_formatted_text (&tmp_pp));
- }
- pp_set_prefix (pp, prefix);
- pp_prefixing_rule (pp) = DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE;
- range->print (dc);
- pp_set_prefix (pp, saved_prefix);
-
- write_indent (pp, cur_indent + per_frame_indent);
- pp_string (pp, start_line_color);
- pp_string (pp, "|");
- pp_string (pp, end_line_color);
- pp_newline (pp);
- }
-
- if (i < ps->m_ranges.length () - 1)
- {
- const event_range *next_range = ps->m_ranges[i + 1];
-
- if (range->m_stack_depth > next_range->m_stack_depth)
- {
- if (vbar_column_for_depth.get (next_range->m_stack_depth))
- {
- /* Show returning from stack frame(s), by printing
- something like:
- " |\n"
- " <------------ +\n"
- " |\n". */
- int vbar_for_next_frame
- = *vbar_column_for_depth.get (next_range->m_stack_depth);
-
- int indent_for_next_frame
- = vbar_for_next_frame - per_frame_indent;
- write_indent (pp, vbar_for_next_frame);
- pp_string (pp, start_line_color);
- pp_character (pp, '<');
- for (int i = indent_for_next_frame + per_frame_indent;
- i < cur_indent + per_frame_indent - 1; i++)
- pp_character (pp, '-');
- pp_character (pp, '+');
- pp_string (pp, end_line_color);
- pp_newline (pp);
- cur_indent = indent_for_next_frame;
-
- write_indent (pp, vbar_for_next_frame);
- pp_string (pp, start_line_color);
- pp_character (pp, '|');
- pp_string (pp, end_line_color);
- pp_newline (pp);
- }
- else
- {
- /* Handle disjoint paths (e.g. a callback at some later
- time). */
- cur_indent = base_indent;
- }
- }
- else if (range->m_stack_depth < next_range->m_stack_depth)
- {
- /* Prepare to show pushed stack frame. */
- gcc_assert (range->m_stack_depth != EMPTY);
- gcc_assert (range->m_stack_depth != DELETED);
- vbar_column_for_depth.put (range->m_stack_depth,
- cur_indent + per_frame_indent);
- cur_indent += per_frame_indent;
- }
-
- }
+ const int swimlane_idx
+ = range->m_per_thread_summary.get_swimlane_index ();
+ if (ps->multithreaded_p ())
+ if (i == 0 || ps->m_ranges[i - 1]->m_thread_id != range->m_thread_id)
+ {
+ if (i > 0)
+ pp_newline (pp);
+ pp_printf (pp, "Thread: %qs",
+ range->m_per_thread_summary.get_name ());
+ pp_newline (pp);
+ }
+ thread_event_printer &tep = thread_event_printers[swimlane_idx];
+ tep.print_swimlane_for_event_range (dc, pp, range);
}
}
pp_flush (context->printer);
pp_set_prefix (context->printer, saved_prefix);
}
+ break;
}
}