GraphChangeMonitoringPhase.java revision 12651:6ef01bd40ce2
1/*
2 * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23package org.graalvm.compiler.core.phases;
24
25import java.util.Set;
26import java.util.stream.Collectors;
27
28import org.graalvm.compiler.debug.Debug;
29import org.graalvm.compiler.debug.Debug.Scope;
30import org.graalvm.compiler.graph.Graph.NodeEvent;
31import org.graalvm.compiler.graph.Graph.NodeEventScope;
32import org.graalvm.compiler.graph.Node;
33import org.graalvm.compiler.nodes.LogicConstantNode;
34import org.graalvm.compiler.nodes.StructuredGraph;
35import org.graalvm.compiler.phases.BasePhase;
36import org.graalvm.compiler.phases.PhaseSuite;
37import org.graalvm.compiler.phases.common.util.HashSetNodeEventListener;
38import org.graalvm.compiler.phases.tiers.PhaseContext;
39
40/**
41 * A utility phase for detecting when a phase would change the graph and reporting extra information
42 * about the effects. The phase is first run on a copy of the graph and if a change in that graph is
43 * detected then it's rerun on the original graph inside a new debug scope under
44 * GraphChangeMonitoringPhase. The message argument can be used to distinguish between the same
45 * phase run at different points.
46 *
47 * @param <C>
48 */
49public class GraphChangeMonitoringPhase<C extends PhaseContext> extends PhaseSuite<C> {
50
51    private final String message;
52
53    public GraphChangeMonitoringPhase(String message, BasePhase<C> phase) {
54        super();
55        this.message = message;
56        appendPhase(phase);
57    }
58
59    public GraphChangeMonitoringPhase(String message) {
60        super();
61        this.message = message;
62    }
63
64    @Override
65    @SuppressWarnings("try")
66    protected void run(StructuredGraph graph, C context) {
67        /*
68         * Phase may add nodes but not end up using them so ignore additions. Nodes going dead and
69         * having their inputs change are the main interesting differences.
70         */
71        HashSetNodeEventListener listener = new HashSetNodeEventListener().exclude(NodeEvent.NODE_ADDED);
72        StructuredGraph graphCopy = (StructuredGraph) graph.copy();
73        try (NodeEventScope s = graphCopy.trackNodeEvents(listener)) {
74            try (Scope s2 = Debug.sandbox("WithoutMonitoring", null)) {
75                super.run(graphCopy, context);
76            } catch (Throwable t) {
77                Debug.handle(t);
78            }
79        }
80        /*
81         * Ignore LogicConstantNode since those are sometimes created and deleted as part of running
82         * a phase.
83         */
84        if (listener.getNodes().stream().filter(e -> !(e instanceof LogicConstantNode)).findFirst().isPresent()) {
85            /* rerun it on the real graph in a new Debug scope so Dump and Log can find it. */
86            listener = new HashSetNodeEventListener();
87            try (NodeEventScope s = graph.trackNodeEvents(listener)) {
88                try (Scope s2 = Debug.scope("WithGraphChangeMonitoring")) {
89                    if (Debug.isDumpEnabled(Debug.BASIC_LOG_LEVEL)) {
90                        Debug.dump(Debug.BASIC_LOG_LEVEL, graph, "*** Before phase %s", getName());
91                    }
92                    super.run(graph, context);
93                    Set<Node> collect = listener.getNodes().stream().filter(e -> !e.isAlive()).filter(e -> !(e instanceof LogicConstantNode)).collect(Collectors.toSet());
94                    if (Debug.isDumpEnabled(Debug.BASIC_LOG_LEVEL)) {
95                        Debug.dump(Debug.BASIC_LOG_LEVEL, graph, "*** After phase %s %s", getName(), collect);
96                    }
97                    Debug.log("*** %s %s %s\n", message, graph, collect);
98                }
99            }
100        } else {
101            // Go ahead and run it normally even though it should have no effect
102            super.run(graph, context);
103        }
104    }
105}
106