From patchwork Thu Sep 14 20:40:31 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Malcolm X-Patchwork-Id: 139828 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a05:612c:172:b0:3f2:4152:657d with SMTP id h50csp613689vqi; Thu, 14 Sep 2023 13:41:27 -0700 (PDT) X-Google-Smtp-Source: AGHT+IH3J5gu4Re8v3zMenw7dDag2A9yGkofx7YDWw/0n54Th48iUbQqwQdGlMjhV8oCexYQq4rG X-Received: by 2002:aa7:d042:0:b0:52f:64be:79f2 with SMTP id n2-20020aa7d042000000b0052f64be79f2mr5694599edo.39.1694724087551; Thu, 14 Sep 2023 13:41:27 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1694724087; cv=none; d=google.com; s=arc-20160816; b=l6lSxAm1+IZzgsQkNLmAia1MnOztMCyNWQ4MGhBJfzGfZC0ziBhesBqTQI7/E0S9N9 WOTuJH8hcmD/3aCmtIlNU8tCoqvbEuyfMd4ewmhqsE6TgYcF01sox28dK67o6wsyPkkk LPs7VtsKwV2IbdF68jycvZIe3oPfeq0/2v/3LOsYJWrWYXzCWV7hprB4F/rAyUjPa+JD zp5CSQL/57IPgurC56aN5MDGwhcAZWkYTdLW+GmWK3hSszRBbNNd2qfFpZfxa07+e7RF 0QJ4EBvFo2vxGBnx22Pkah1XitirAx6A/4CQgC/5mO5r06LodXirPRIwX/Bwz+QPqWWx JWEQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:reply-to:from:list-subscribe:list-help:list-post :list-archive:list-unsubscribe:list-id:precedence :content-transfer-encoding:mime-version:message-id:date:subject:cc :to:dmarc-filter:delivered-to:dkim-signature:dkim-filter; bh=JfKtr1ziI3jlLdBbDd/96ISC0TbYKt6HLcSv1KyG6ig=; fh=NXemEfxTRbZtBxUkxR2ehQUaYlcDfMdzPkO8MChVQE4=; b=Y6wm1xVqy6MYu4nhz3YEFFxC3Kk1V8P5rUfvBkb+y1/J8ygUCl1Um3c9iYKV9vSuc+ Pux37r7ZRpd7UNgrTz7/yTXffE8lVPrGy4mNo5qfXt2FxzEgfIN2ojUUNWupMX+QNSft pg3kpeVXsfnmC8WB0SMmn2e1CAVokSSPSbpFCPoVpp3msCTGjX9nhgjg3TgemdtBQOOh qlff5oZcK96Ojh0wbTxWbxViEBbRZBwuBvCWtmwyenqQL7Cz0pXdU8DZIy55deZwfiCy RT4WFm66siyOCUU55xNuzsNsfdr2Xkm1yJUzmcu/wpnaoIoaodN0HFS+1B2/kEOPePg2 iXLg== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@gcc.gnu.org header.s=default header.b=Y2Vf5zIz; spf=pass (google.com: domain of gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org designates 8.43.85.97 as permitted sender) smtp.mailfrom="gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org"; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=gnu.org Received: from server2.sourceware.org (ip-8-43-85-97.sourceware.org. [8.43.85.97]) by mx.google.com with ESMTPS id o7-20020a056402038700b0052fb00a99c2si1956084edv.616.2023.09.14.13.41.27 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 14 Sep 2023 13:41:27 -0700 (PDT) Received-SPF: pass (google.com: domain of gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org designates 8.43.85.97 as permitted sender) client-ip=8.43.85.97; Authentication-Results: mx.google.com; dkim=pass header.i=@gcc.gnu.org header.s=default header.b=Y2Vf5zIz; spf=pass (google.com: domain of gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org designates 8.43.85.97 as permitted sender) smtp.mailfrom="gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org"; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=gnu.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 407AF3858004 for ; Thu, 14 Sep 2023 20:41:26 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 407AF3858004 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gcc.gnu.org; s=default; t=1694724086; bh=JfKtr1ziI3jlLdBbDd/96ISC0TbYKt6HLcSv1KyG6ig=; h=To:Cc:Subject:Date:List-Id:List-Unsubscribe:List-Archive: List-Post:List-Help:List-Subscribe:From:Reply-To:From; b=Y2Vf5zIz66Nv+ux8G07rHqn9EESXewIXKGU6t9GhQ+cqaU+Il1XstUjq/dQRfnmYs FehMwNhomSQRGtV7q/gq5lipHo5upuxpCPU79wi9EJrO0MYfF7RVGIeU1nfz8CKiUM Af+clSWfmzeEL8qCzE9AMHsudgMbVhOB/SBBAK9c= X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by sourceware.org (Postfix) with ESMTPS id 0E5A73858CDA for ; Thu, 14 Sep 2023 20:40:37 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 0E5A73858CDA Received: from mimecast-mx02.redhat.com (mimecast-mx02.redhat.com [66.187.233.88]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-83-I1tw2iCsMmCbllOzhu8oYg-1; Thu, 14 Sep 2023 16:40:35 -0400 X-MC-Unique: I1tw2iCsMmCbllOzhu8oYg-1 Received: from smtp.corp.redhat.com (int-mx06.intmail.prod.int.rdu2.redhat.com [10.11.54.6]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id F3D2D802D35 for ; Thu, 14 Sep 2023 20:40:34 +0000 (UTC) Received: from t14s.localdomain.com (unknown [10.22.10.159]) by smtp.corp.redhat.com (Postfix) with ESMTP id A5E8D21B2F5D; Thu, 14 Sep 2023 20:40:34 +0000 (UTC) To: gcc-patches@gcc.gnu.org Cc: David Malcolm Subject: [pushed] diagnostics: support multithreaded diagnostic paths Date: Thu, 14 Sep 2023 16:40:31 -0400 Message-Id: <20230914204031.1792895-1-dmalcolm@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.1 on 10.11.54.6 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-11.4 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_SHORT, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H4, RCVD_IN_MSPIKE_WL, SPF_HELO_NONE, SPF_NONE, TXREP, T_FILL_THIS_FORM_SHORT autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Patchwork-Original-From: David Malcolm via Gcc-patches From: David Malcolm Reply-To: David Malcolm Errors-To: gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org Sender: "Gcc-patches" X-getmail-retrieved-from-mailbox: INBOX X-GMAIL-THRID: 1777047005036603711 X-GMAIL-MSGID: 1777047005036603711 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. Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu. Pushed to trunk as r14-4006-g3a1e9f3ed7aa49. 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. --- gcc/analyzer/checker-event.h | 4 + gcc/analyzer/checker-path.h | 17 +- gcc/diagnostic-event-id.h | 5 + gcc/diagnostic-format-sarif.cc | 86 +++- gcc/diagnostic-path.h | 55 ++- gcc/diagnostic-show-locus.cc | 16 +- gcc/diagnostic.cc | 78 +++- gcc/diagnostic.h | 3 +- ...c-test-paths-multithreaded-inline-events.c | 72 +++ ...iagnostic-test-paths-multithreaded-sarif.c | 35 ++ ...test-paths-multithreaded-separate-events.c | 18 + .../plugin/diagnostic_plugin_test_paths.c | 94 +++- gcc/testsuite/gcc.dg/plugin/plugin.exp | 3 + gcc/tree-diagnostic-path.cc | 432 ++++++++++++------ 14 files changed, 734 insertions(+), 184 deletions(-) create mode 100644 gcc/testsuite/gcc.dg/plugin/diagnostic-test-paths-multithreaded-inline-events.c create mode 100644 gcc/testsuite/gcc.dg/plugin/diagnostic-test-paths-multithreaded-sarif.c create mode 100644 gcc/testsuite/gcc.dg/plugin/diagnostic-test-paths-multithreaded-separate-events.c diff --git a/gcc/analyzer/checker-event.h b/gcc/analyzer/checker-event.h index 5dd25cb0775..7ba92f19650 100644 --- a/gcc/analyzer/checker-event.h +++ b/gcc/analyzer/checker-event.h @@ -113,6 +113,10 @@ public: return NULL; } meaning get_meaning () const override; + diagnostic_thread_id_t get_thread_id () const final override + { + return 0; + } /* Additional functionality. */ diff --git a/gcc/analyzer/checker-path.h b/gcc/analyzer/checker-path.h index 627f64e5320..93c807c3d24 100644 --- a/gcc/analyzer/checker-path.h +++ b/gcc/analyzer/checker-path.h @@ -30,7 +30,11 @@ namespace ana { 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. */ @@ -43,6 +47,15 @@ public: { 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) { @@ -120,6 +133,8 @@ public: private: DISABLE_COPY_AND_ASSIGN(checker_path); + simple_diagnostic_thread m_thread; + /* The events that have occurred along this path. */ auto_delete_vec m_events; diff --git a/gcc/diagnostic-event-id.h b/gcc/diagnostic-event-id.h index 84f4b65611e..c5f5d60ddc1 100644 --- a/gcc/diagnostic-event-id.h +++ b/gcc/diagnostic-event-id.h @@ -58,4 +58,9 @@ class diagnostic_event_id_t 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 */ diff --git a/gcc/diagnostic-format-sarif.cc b/gcc/diagnostic-format-sarif.cc index 1eff71962d7..f56c4ce033d 100644 --- a/gcc/diagnostic-format-sarif.cc +++ b/gcc/diagnostic-format-sarif.cc @@ -94,6 +94,23 @@ public: 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). @@ -168,9 +185,9 @@ private: 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); @@ -365,6 +382,19 @@ sarif_ice_notification::sarif_ice_notification (diagnostic_context *context, 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. */ @@ -1091,41 +1121,44 @@ sarif_builder::make_code_flow_object (const diagnostic_path &path) { 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, + 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 (); @@ -1142,6 +1175,11 @@ sarif_builder::make_thread_flow_location_object (const diagnostic_event &ev) 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) diff --git a/gcc/diagnostic-path.h b/gcc/diagnostic-path.h index 9d9d6296eb0..d39872abb9f 100644 --- a/gcc/diagnostic-path.h +++ b/gcc/diagnostic-path.h @@ -155,6 +155,20 @@ class diagnostic_event 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. */ @@ -165,8 +179,12 @@ class diagnostic_path 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; @@ -180,7 +198,8 @@ class simple_diagnostic_event : public diagnostic_event { 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; } @@ -198,12 +217,32 @@ class simple_diagnostic_event : public diagnostic_event { 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 @@ -212,17 +251,27 @@ class simple_diagnostic_event : public diagnostic_event 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 m_threads; auto_delete_vec m_events; /* (for use by add_event). */ diff --git a/gcc/diagnostic-show-locus.cc b/gcc/diagnostic-show-locus.cc index 0514815b51f..31ef85151fd 100644 --- a/gcc/diagnostic-show-locus.cc +++ b/gcc/diagnostic-show-locus.cc @@ -367,7 +367,8 @@ class layout 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, @@ -1183,9 +1184,10 @@ make_policy (const diagnostic_context &dc, 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, @@ -2825,12 +2827,14 @@ gcc_rich_location::add_location_if_nearby (location_t loc, } /* 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. */ @@ -2851,7 +2855,7 @@ diagnostic_show_locus (diagnostic_context * context, 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++) { diff --git a/gcc/diagnostic.cc b/gcc/diagnostic.cc index 65c0cfbf11a..00183b10700 100644 --- a/gcc/diagnostic.cc +++ b/gcc/diagnostic.cc @@ -2404,6 +2404,14 @@ diagnostics_text_art_charset_init (diagnostic_context *context, } } +/* 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. */ @@ -2422,6 +2430,25 @@ simple_diagnostic_path::get_event (int idx) const 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. @@ -2464,15 +2491,56 @@ simple_diagnostic_path::add_event (location_t loc, tree fndecl, int 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) { } diff --git a/gcc/diagnostic.h b/gcc/diagnostic.h index 00b828f230d..4ec83a988d5 100644 --- a/gcc/diagnostic.h +++ b/gcc/diagnostic.h @@ -509,7 +509,8 @@ extern void diagnostic_finish (diagnostic_context *); 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 diff --git a/gcc/testsuite/gcc.dg/plugin/diagnostic-test-paths-multithreaded-inline-events.c b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-paths-multithreaded-inline-events.c new file mode 100644 index 00000000000..333ef735944 --- /dev/null +++ b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-paths-multithreaded-inline-events.c @@ -0,0 +1,72 @@ +/* { 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 "" } */ diff --git a/gcc/testsuite/gcc.dg/plugin/diagnostic-test-paths-multithreaded-sarif.c b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-paths-multithreaded-sarif.c new file mode 100644 index 00000000000..727d1bb6469 --- /dev/null +++ b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-paths-multithreaded-sarif.c @@ -0,0 +1,35 @@ +/* { 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} } } */ diff --git a/gcc/testsuite/gcc.dg/plugin/diagnostic-test-paths-multithreaded-separate-events.c b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-paths-multithreaded-separate-events.c new file mode 100644 index 00000000000..914918bb9e1 --- /dev/null +++ b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-paths-multithreaded-separate-events.c @@ -0,0 +1,18 @@ +/* { 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 } */ +} diff --git a/gcc/testsuite/gcc.dg/plugin/diagnostic_plugin_test_paths.c b/gcc/testsuite/gcc.dg/plugin/diagnostic_plugin_test_paths.c index 8d97fe8e8d9..62558bede95 100644 --- a/gcc/testsuite/gcc.dg/plugin/diagnostic_plugin_test_paths.c +++ b/gcc/testsuite/gcc.dg/plugin/diagnostic_plugin_test_paths.c @@ -192,7 +192,7 @@ struct event_location_t /* 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) { @@ -200,9 +200,10 @@ check_for_named_function (function *fun, const char *funcname, 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; } @@ -215,12 +216,21 @@ class test_diagnostic_path : public simple_diagnostic_path : 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, @@ -422,12 +432,86 @@ example_3 () } } +/* 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; } diff --git a/gcc/testsuite/gcc.dg/plugin/plugin.exp b/gcc/testsuite/gcc.dg/plugin/plugin.exp index ed72912309c..f098a327d31 100644 --- a/gcc/testsuite/gcc.dg/plugin/plugin.exp +++ b/gcc/testsuite/gcc.dg/plugin/plugin.exp @@ -108,6 +108,9 @@ set plugin_test_list [list \ 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 \ diff --git a/gcc/tree-diagnostic-path.cc b/gcc/tree-diagnostic-path.cc index 988ed7f6726..84148da53af 100644 --- a/gcc/tree-diagnostic-path.cc +++ b/gcc/tree-diagnostic-path.cc @@ -19,6 +19,7 @@ along with GCC; see the file COPYING3. If not see . */ #include "config.h" +#define INCLUDE_VECTOR #include "system.h" #include "coretypes.h" #include "tree.h" @@ -83,6 +84,9 @@ can_consolidate_events (const diagnostic_event &e1, 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; @@ -109,20 +113,67 @@ can_consolidate_events (const diagnostic_event &e1, 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 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, @@ -142,7 +193,7 @@ struct event_range /* 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 (); @@ -172,7 +223,6 @@ struct event_range 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); } @@ -180,7 +230,7 @@ struct event_range } /* 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)) @@ -198,19 +248,49 @@ struct event_range 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 m_ranges; + auto_delete_vec m_per_thread_summary; + hash_map, + 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. */ @@ -224,12 +304,19 @@ path_summary::path_summary (const diagnostic_path &path, 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); } } @@ -259,6 +346,184 @@ print_fndecl (pretty_printer *pp, tree fndecl, bool quoted) 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 vbar_hash; + hash_map 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. @@ -292,145 +557,33 @@ print_fndecl (pretty_printer *pp, tree fndecl, bool quoted) 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_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 vbar_hash; - hash_map 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); } } @@ -497,6 +650,7 @@ default_tree_diagnostic_path_printer (diagnostic_context *context, pp_flush (context->printer); pp_set_prefix (context->printer, saved_prefix); } + break; } }