Skip to content

Commit 3a1e9f3

Browse files
committed
diagnostics: support multithreaded diagnostic paths
This patch extends the existing diagnostic_path class so that as well as list of events, there is a list of named threads, with each event being associated with one of the threads. No GCC diagnostics take advantage of this, but GCC plugins may find a use for this; an example is provided in the testsuite. Given that there is still a single list of events within a diagnostic_path, the events in a diagnostic_path have a specific global ordering even if they are in multiple threads. Within the SARIF serialization, the patch adds the "executionOrder" property to threadFlowLocation objects (SARIF v2.1.0 3.38.11). This is 1-based in order to match the human-readable numbering of events shown in messages emitted by pretty-printer.cc's "%@". With -fdiagnostics-path-format=separate-events, the threads are not shown. With -fdiagnostics-path-format=inline-events, the threads and the per-thread stack activity are tracked and visalized separately. An example can be seen in the testsuite. gcc/analyzer/ChangeLog: * checker-event.h (checker_event::get_thread_id): New. * checker-path.h (class checker_path): Implement thread-related vfuncs via a single simple_diagnostic_thread instance named "main". gcc/ChangeLog: * diagnostic-event-id.h (diagnostic_thread_id_t): New typedef. * diagnostic-format-sarif.cc (class sarif_thread_flow): New. (sarif_thread_flow::sarif_thread_flow): New. (sarif_builder::make_code_flow_object): Reimplement, creating per-thread threadFlow objects, populating them with the relevant events. (sarif_builder::make_thread_flow_object): Delete, moving the code into sarif_builder::make_code_flow_object. (sarif_builder::make_thread_flow_location_object): Add "path_event_idx" param. Use it to set "executionOrder" property. * diagnostic-path.h (diagnostic_event::get_thread_id): New pure-virtual vfunc. (class diagnostic_thread): New. (diagnostic_path::num_threads): New pure-virtual vfunc. (diagnostic_path::get_thread): New pure-virtual vfunc. (diagnostic_path::multithreaded_p): New decl. (simple_diagnostic_event::simple_diagnostic_event): Add optional thread_id param. (simple_diagnostic_event::get_thread_id): New accessor. (simple_diagnostic_event::m_thread_id): New. (class simple_diagnostic_thread): New. (simple_diagnostic_path::simple_diagnostic_path): Move definition to diagnostic.cc. (simple_diagnostic_path::num_threads): New. (simple_diagnostic_path::get_thread): New. (simple_diagnostic_path::add_thread): New. (simple_diagnostic_path::add_thread_event): New. (simple_diagnostic_path::m_threads): New. * diagnostic-show-locus.cc (layout::layout): Add pretty_printer param for overriding the context's printer. (diagnostic_show_locus): Likwise. * diagnostic.cc (simple_diagnostic_path::simple_diagnostic_path): Move here from diagnostic-path.h. Add main thread. (simple_diagnostic_path::num_threads): New. (simple_diagnostic_path::get_thread): New. (simple_diagnostic_path::add_thread): New. (simple_diagnostic_path::add_thread_event): New. (simple_diagnostic_event::simple_diagnostic_event): Add thread_id param and use it to initialize m_thread_id. Reformat. * diagnostic.h: Add pretty_printer param for overriding the context's printer. * tree-diagnostic-path.cc: Add #define INCLUDE_VECTOR. (can_consolidate_events): Compare thread ids. (class per_thread_summary): New. (event_range::event_range): Add per_thread_summary arg. (event_range::print): Add "pp" param and use it rather than dc's printer. (event_range::m_thread_id): New field. (event_range::m_per_thread_summary): New field. (path_summary::multithreaded_p): New. (path_summary::get_events_for_thread_id): New. (path_summary::m_per_thread_summary): New field. (path_summary::m_thread_id_to_events): New field. (path_summary::get_or_create_events_for_thread_id): New. (path_summary::path_summary): Create per_thread_summary instances as needed and associate the event_range instances with them. (base_indent): Move here from print_path_summary_as_text. (per_frame_indent): Likewise. (class thread_event_printer): New, adapted from parts of print_path_summary_as_text. (print_path_summary_as_text): Make static. Reimplement to moving most of existing code to class thread_event_printer, capturing state as per-thread as appropriate. (default_tree_diagnostic_path_printer): Add missing 'break' on final case. gcc/testsuite/ChangeLog: * gcc.dg/plugin/diagnostic-test-paths-multithreaded-inline-events.c: New test. * gcc.dg/plugin/diagnostic-test-paths-multithreaded-sarif.c: New test. * gcc.dg/plugin/diagnostic-test-paths-multithreaded-separate-events.c: New test. * gcc.dg/plugin/diagnostic_plugin_test_paths.c: Add support for generating multithreaded paths. * gcc.dg/plugin/plugin.exp: Add the new tests. Signed-off-by: David Malcolm <[email protected]>
1 parent 59f6185 commit 3a1e9f3

14 files changed

+734
-184
lines changed

gcc/analyzer/checker-event.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,10 @@ class checker_event : public diagnostic_event
113113
return NULL;
114114
}
115115
meaning get_meaning () const override;
116+
diagnostic_thread_id_t get_thread_id () const final override
117+
{
118+
return 0;
119+
}
116120

117121
/* Additional functionality. */
118122

gcc/analyzer/checker-path.h

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,11 @@ namespace ana {
3030
class checker_path : public diagnostic_path
3131
{
3232
public:
33-
checker_path (logger *logger) : diagnostic_path (), m_logger (logger) {}
33+
checker_path (logger *logger)
34+
: diagnostic_path (),
35+
m_thread ("main"),
36+
m_logger (logger)
37+
{}
3438

3539
/* Implementation of diagnostic_path vfuncs. */
3640

@@ -43,6 +47,15 @@ class checker_path : public diagnostic_path
4347
{
4448
return *m_events[idx];
4549
}
50+
unsigned num_threads () const final override
51+
{
52+
return 1;
53+
}
54+
const diagnostic_thread &
55+
get_thread (diagnostic_thread_id_t) const final override
56+
{
57+
return m_thread;
58+
}
4659

4760
checker_event *get_checker_event (int idx)
4861
{
@@ -120,6 +133,8 @@ class checker_path : public diagnostic_path
120133
private:
121134
DISABLE_COPY_AND_ASSIGN(checker_path);
122135

136+
simple_diagnostic_thread m_thread;
137+
123138
/* The events that have occurred along this path. */
124139
auto_delete_vec<checker_event> m_events;
125140

gcc/diagnostic-event-id.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,4 +58,9 @@ class diagnostic_event_id_t
5858
The %@ format code requires that known_p be true for the event ID. */
5959
typedef diagnostic_event_id_t *diagnostic_event_id_ptr;
6060

61+
/* A type for compactly referring to a particular thread within a
62+
diagnostic_path. Typically there is just one thread per path,
63+
with id 0. */
64+
typedef unsigned diagnostic_thread_id_t;
65+
6166
#endif /* ! GCC_DIAGNOSTIC_EVENT_ID_H */

gcc/diagnostic-format-sarif.cc

Lines changed: 62 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,23 @@ class sarif_ice_notification : public sarif_object
9494
sarif_builder *builder);
9595
};
9696

97+
/* Subclass of sarif_object for SARIF threadFlow objects
98+
(SARIF v2.1.0 section 3.37) for PATH. */
99+
100+
class sarif_thread_flow : public sarif_object
101+
{
102+
public:
103+
sarif_thread_flow (const diagnostic_thread &thread);
104+
105+
void add_location (json::object *thread_flow_loc_obj)
106+
{
107+
m_locations_arr->append (thread_flow_loc_obj);
108+
}
109+
110+
private:
111+
json::array *m_locations_arr;
112+
};
113+
97114
/* A class for managing SARIF output (for -fdiagnostics-format=sarif-stderr
98115
and -fdiagnostics-format=sarif-file).
99116
@@ -168,9 +185,9 @@ class sarif_builder
168185
json::object *
169186
make_logical_location_object (const logical_location &logical_loc) const;
170187
json::object *make_code_flow_object (const diagnostic_path &path);
171-
json::object *make_thread_flow_object (const diagnostic_path &path);
172188
json::object *
173-
make_thread_flow_location_object (const diagnostic_event &event);
189+
make_thread_flow_location_object (const diagnostic_event &event,
190+
int path_event_idx);
174191
json::array *maybe_make_kinds_array (diagnostic_event::meaning m) const;
175192
json::object *maybe_make_physical_location_object (location_t loc);
176193
json::object *make_artifact_location_object (location_t loc);
@@ -365,6 +382,19 @@ sarif_ice_notification::sarif_ice_notification (diagnostic_context *context,
365382
set ("level", new json::string ("error"));
366383
}
367384

385+
/* class sarif_thread_flow : public sarif_object. */
386+
387+
sarif_thread_flow::sarif_thread_flow (const diagnostic_thread &thread)
388+
{
389+
/* "id" property (SARIF v2.1.0 section 3.37.2). */
390+
label_text name (thread.get_name (false));
391+
set ("id", new json::string (name.get ()));
392+
393+
/* "locations" property (SARIF v2.1.0 section 3.37.6). */
394+
m_locations_arr = new json::array ();
395+
set ("locations", m_locations_arr);
396+
}
397+
368398
/* class sarif_builder. */
369399

370400
/* sarif_builder's ctor. */
@@ -1091,41 +1121,44 @@ sarif_builder::make_code_flow_object (const diagnostic_path &path)
10911121
{
10921122
json::object *code_flow_obj = new json::object ();
10931123

1094-
/* "threadFlows" property (SARIF v2.1.0 section 3.36.3).
1095-
Currently we only support one thread per result. */
1124+
/* "threadFlows" property (SARIF v2.1.0 section 3.36.3). */
10961125
json::array *thread_flows_arr = new json::array ();
1097-
json::object *thread_flow_obj = make_thread_flow_object (path);
1098-
thread_flows_arr->append (thread_flow_obj);
1099-
code_flow_obj->set ("threadFlows", thread_flows_arr);
11001126

1101-
return code_flow_obj;
1102-
}
1103-
1104-
/* Make a threadFlow object (SARIF v2.1.0 section 3.37) for PATH. */
1105-
1106-
json::object *
1107-
sarif_builder::make_thread_flow_object (const diagnostic_path &path)
1108-
{
1109-
json::object *thread_flow_obj = new json::object ();
1110-
1111-
/* "locations" property (SARIF v2.1.0 section 3.37.6). */
1112-
json::array *locations_arr = new json::array ();
1127+
/* Walk the events, consolidating into per-thread threadFlow objects,
1128+
using the index with PATH as the overall executionOrder. */
1129+
hash_map<int_hash<diagnostic_thread_id_t, -1, -2>,
1130+
sarif_thread_flow *> thread_id_map;
11131131
for (unsigned i = 0; i < path.num_events (); i++)
11141132
{
11151133
const diagnostic_event &event = path.get_event (i);
1134+
const diagnostic_thread_id_t thread_id = event.get_thread_id ();
1135+
sarif_thread_flow *thread_flow_obj;
1136+
1137+
if (sarif_thread_flow **slot = thread_id_map.get (thread_id))
1138+
thread_flow_obj = *slot;
1139+
else
1140+
{
1141+
const diagnostic_thread &thread = path.get_thread (thread_id);
1142+
thread_flow_obj = new sarif_thread_flow (thread);
1143+
thread_flows_arr->append (thread_flow_obj);
1144+
thread_id_map.put (thread_id, thread_flow_obj);
1145+
}
1146+
1147+
/* Add event to thread's threadFlow object. */
11161148
json::object *thread_flow_loc_obj
1117-
= make_thread_flow_location_object (event);
1118-
locations_arr->append (thread_flow_loc_obj);
1149+
= make_thread_flow_location_object (event, i);
1150+
thread_flow_obj->add_location (thread_flow_loc_obj);
11191151
}
1120-
thread_flow_obj->set ("locations", locations_arr);
1152+
code_flow_obj->set ("threadFlows", thread_flows_arr);
11211153

1122-
return thread_flow_obj;
1154+
return code_flow_obj;
11231155
}
11241156

11251157
/* Make a threadFlowLocation object (SARIF v2.1.0 section 3.38) for EVENT. */
11261158

11271159
json::object *
1128-
sarif_builder::make_thread_flow_location_object (const diagnostic_event &ev)
1160+
sarif_builder::make_thread_flow_location_object (const diagnostic_event &ev,
1161+
int path_event_idx)
11291162
{
11301163
json::object *thread_flow_loc_obj = new json::object ();
11311164

@@ -1142,6 +1175,11 @@ sarif_builder::make_thread_flow_location_object (const diagnostic_event &ev)
11421175
thread_flow_loc_obj->set ("nestingLevel",
11431176
new json::integer_number (ev.get_stack_depth ()));
11441177

1178+
/* "executionOrder" property (SARIF v2.1.0 3.38.11).
1179+
Offset by 1 to match the human-readable values emitted by %@. */
1180+
thread_flow_loc_obj->set ("executionOrder",
1181+
new json::integer_number (path_event_idx + 1));
1182+
11451183
/* It might be nice to eventually implement the following for -fanalyzer:
11461184
- the "stack" property (SARIF v2.1.0 section 3.38.5)
11471185
- the "state" property (SARIF v2.1.0 section 3.38.9)

gcc/diagnostic-path.h

Lines changed: 52 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,20 @@ class diagnostic_event
155155
virtual const logical_location *get_logical_location () const = 0;
156156

157157
virtual meaning get_meaning () const = 0;
158+
159+
virtual diagnostic_thread_id_t get_thread_id () const = 0;
160+
};
161+
162+
/* Abstract base class representing a thread of execution within
163+
a diagnostic_path.
164+
Each diagnostic_event is associated with one thread.
165+
Typically there is just one thread per diagnostic_path. */
166+
167+
class diagnostic_thread
168+
{
169+
public:
170+
virtual ~diagnostic_thread () {}
171+
virtual label_text get_name (bool can_colorize) const = 0;
158172
};
159173

160174
/* Abstract base class for getting at a sequence of events. */
@@ -165,8 +179,12 @@ class diagnostic_path
165179
virtual ~diagnostic_path () {}
166180
virtual unsigned num_events () const = 0;
167181
virtual const diagnostic_event & get_event (int idx) const = 0;
182+
virtual unsigned num_threads () const = 0;
183+
virtual const diagnostic_thread &
184+
get_thread (diagnostic_thread_id_t) const = 0;
168185

169186
bool interprocedural_p () const;
187+
bool multithreaded_p () const;
170188

171189
private:
172190
bool get_first_event_in_a_function (unsigned *out_idx) const;
@@ -180,7 +198,8 @@ class simple_diagnostic_event : public diagnostic_event
180198
{
181199
public:
182200
simple_diagnostic_event (location_t loc, tree fndecl, int depth,
183-
const char *desc);
201+
const char *desc,
202+
diagnostic_thread_id_t thread_id = 0);
184203
~simple_diagnostic_event ();
185204

186205
location_t get_location () const final override { return m_loc; }
@@ -198,12 +217,32 @@ class simple_diagnostic_event : public diagnostic_event
198217
{
199218
return meaning ();
200219
}
220+
diagnostic_thread_id_t get_thread_id () const final override
221+
{
222+
return m_thread_id;
223+
}
201224

202225
private:
203226
location_t m_loc;
204227
tree m_fndecl;
205228
int m_depth;
206229
char *m_desc; // has been i18n-ed and formatted
230+
diagnostic_thread_id_t m_thread_id;
231+
};
232+
233+
/* A simple implementation of diagnostic_thread. */
234+
235+
class simple_diagnostic_thread : public diagnostic_thread
236+
{
237+
public:
238+
simple_diagnostic_thread (const char *name) : m_name (name) {}
239+
label_text get_name (bool) const final override
240+
{
241+
return label_text::borrow (m_name);
242+
}
243+
244+
private:
245+
const char *m_name; // has been i18n-ed and formatted
207246
};
208247

209248
/* A simple implementation of diagnostic_path, as a vector of
@@ -212,17 +251,27 @@ class simple_diagnostic_event : public diagnostic_event
212251
class simple_diagnostic_path : public diagnostic_path
213252
{
214253
public:
215-
simple_diagnostic_path (pretty_printer *event_pp)
216-
: m_event_pp (event_pp) {}
254+
simple_diagnostic_path (pretty_printer *event_pp);
217255

218256
unsigned num_events () const final override;
219257
const diagnostic_event & get_event (int idx) const final override;
258+
unsigned num_threads () const final override;
259+
const diagnostic_thread &
260+
get_thread (diagnostic_thread_id_t) const final override;
261+
262+
diagnostic_thread_id_t add_thread (const char *name);
220263

221264
diagnostic_event_id_t add_event (location_t loc, tree fndecl, int depth,
222265
const char *fmt, ...)
223266
ATTRIBUTE_GCC_DIAG(5,6);
267+
diagnostic_event_id_t
268+
add_thread_event (diagnostic_thread_id_t thread_id,
269+
location_t loc, tree fndecl, int depth,
270+
const char *fmt, ...)
271+
ATTRIBUTE_GCC_DIAG(6,7);
224272

225273
private:
274+
auto_delete_vec<simple_diagnostic_thread> m_threads;
226275
auto_delete_vec<simple_diagnostic_event> m_events;
227276

228277
/* (for use by add_event). */

gcc/diagnostic-show-locus.cc

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -367,7 +367,8 @@ class layout
367367
public:
368368
layout (diagnostic_context *context,
369369
rich_location *richloc,
370-
diagnostic_t diagnostic_kind);
370+
diagnostic_t diagnostic_kind,
371+
pretty_printer *pp = nullptr);
371372

372373
bool maybe_add_location_range (const location_range *loc_range,
373374
unsigned original_idx,
@@ -1183,9 +1184,10 @@ make_policy (const diagnostic_context &dc,
11831184

11841185
layout::layout (diagnostic_context * context,
11851186
rich_location *richloc,
1186-
diagnostic_t diagnostic_kind)
1187+
diagnostic_t diagnostic_kind,
1188+
pretty_printer *pp)
11871189
: m_context (context),
1188-
m_pp (context->printer),
1190+
m_pp (pp ? pp : context->printer),
11891191
m_policy (make_policy (*context, *richloc)),
11901192
m_primary_loc (richloc->get_range (0)->m_loc),
11911193
m_exploc (richloc->get_expanded_location (0), m_policy,
@@ -2825,12 +2827,14 @@ gcc_rich_location::add_location_if_nearby (location_t loc,
28252827
}
28262828

28272829
/* Print the physical source code corresponding to the location of
2828-
this diagnostic, with additional annotations. */
2830+
this diagnostic, with additional annotations.
2831+
If PP is non-null, then use it rather than CONTEXT's printer. */
28292832

28302833
void
28312834
diagnostic_show_locus (diagnostic_context * context,
28322835
rich_location *richloc,
2833-
diagnostic_t diagnostic_kind)
2836+
diagnostic_t diagnostic_kind,
2837+
pretty_printer *pp)
28342838
{
28352839
location_t loc = richloc->get_loc ();
28362840
/* Do nothing if source-printing has been disabled. */
@@ -2851,7 +2855,7 @@ diagnostic_show_locus (diagnostic_context * context,
28512855

28522856
context->last_location = loc;
28532857

2854-
layout layout (context, richloc, diagnostic_kind);
2858+
layout layout (context, richloc, diagnostic_kind, pp);
28552859
for (int line_span_idx = 0; line_span_idx < layout.get_num_line_spans ();
28562860
line_span_idx++)
28572861
{

0 commit comments

Comments
 (0)