1 /*
2  * Copyright (c) 1998, 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.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 package com.sun.hotspot.igv.difference;
26 
27 import com.sun.hotspot.igv.data.Properties;
28 import com.sun.hotspot.igv.data.*;
29 import com.sun.hotspot.igv.data.services.Scheduler;
30 import java.util.*;
31 import org.openide.util.Lookup;
32 
33 /**
34  *
35  * @author Thomas Wuerthinger
36  */
37 public class Difference {
38 
39     public static final String PROPERTY_STATE = "state";
40     public static final String VALUE_NEW = "new";
41     public static final String VALUE_CHANGED = "changed";
42     public static final String VALUE_SAME = "same";
43     public static final String VALUE_DELETED = "deleted";
44     public static final String NEW_PREFIX = "NEW_";
45     public static final String MAIN_PROPERTY = "name";
46     public static final double LIMIT = 100.0;
47     public static final String[] IGNORE_PROPERTIES = new String[]{"idx", "debug_idx"};
48 
createDiffGraph(InputGraph a, InputGraph b)49     public static InputGraph createDiffGraph(InputGraph a, InputGraph b) {
50         if (a.getGroup() == b.getGroup()) {
51             return createDiffSameGroup(a, b);
52         } else {
53             return createDiff(a, b);
54         }
55     }
56 
createDiffSameGroup(InputGraph a, InputGraph b)57     private static InputGraph createDiffSameGroup(InputGraph a, InputGraph b) {
58         Map<Integer, InputNode> keyMapB = new HashMap<>(b.getNodes().size());
59         for (InputNode n : b.getNodes()) {
60             Integer key = n.getId();
61             assert !keyMapB.containsKey(key);
62             keyMapB.put(key, n);
63         }
64 
65         Set<NodePair> pairs = new HashSet<>();
66 
67         for (InputNode n : a.getNodes()) {
68             Integer key = n.getId();
69 
70 
71             if (keyMapB.containsKey(key)) {
72                 InputNode nB = keyMapB.get(key);
73                 pairs.add(new NodePair(n, nB));
74             }
75         }
76 
77         return createDiff(a, b, pairs);
78     }
79 
ensureScheduled(InputGraph a)80     private static void ensureScheduled(InputGraph a) {
81         if (a.getBlocks().isEmpty()) {
82             Scheduler s = Lookup.getDefault().lookup(Scheduler.class);
83             a.clearBlocks();
84             s.schedule(a);
85             a.ensureNodesInBlocks();
86         }
87     }
88 
createDiff(InputGraph a, InputGraph b, Set<NodePair> pairs)89     private static InputGraph createDiff(InputGraph a, InputGraph b, Set<NodePair> pairs) {
90         ensureScheduled(a);
91         ensureScheduled(b);
92 
93         Group g = new Group(null);
94         g.setMethod(a.getGroup().getMethod());
95         if (a.getGroup() == b.getGroup()) {
96             g.getProperties().add(a.getGroup().getProperties());
97         } else {
98             // copy properties that have the same value in both groups
99             Properties bps = b.getGroup().getProperties();
100             for (Property p : a.getGroup().getProperties()) {
101                 String value = p.getValue();
102                 if (value != null && value.equals(bps.get(p.getName()))) {
103                     g.getProperties().setProperty(p.getName(), value);
104                 }
105             }
106         }
107         g.getProperties().setProperty("name", "Difference");
108         InputGraph graph = new InputGraph(a.getName() + ", " + b.getName());
109         g.addElement(graph);
110 
111         Map<InputBlock, InputBlock> blocksMap = new HashMap<>();
112         for (InputBlock blk : a.getBlocks()) {
113             InputBlock diffblk = graph.addBlock(blk.getName());
114             blocksMap.put(blk, diffblk);
115         }
116         for (InputBlock blk : b.getBlocks()) {
117             InputBlock diffblk = graph.getBlock(blk.getName());
118             if (diffblk == null) {
119                 diffblk = graph.addBlock(blk.getName());
120             }
121             blocksMap.put(blk, diffblk);
122         }
123 
124         // Difference between block edges
125         Set<Pair<String, String>> aEdges = new HashSet<>();
126         for (InputBlockEdge edge : a.getBlockEdges()) {
127             aEdges.add(new Pair<>(edge.getFrom().getName(), edge.getTo().getName()));
128         }
129         for (InputBlockEdge bEdge : b.getBlockEdges()) {
130             InputBlock from = bEdge.getFrom();
131             InputBlock to = bEdge.getTo();
132             Pair<String, String> pair = new Pair<>(from.getName(), to.getName());
133             if (aEdges.contains(pair)) {
134                 // same
135                 graph.addBlockEdge(blocksMap.get(from), blocksMap.get(to));
136                 aEdges.remove(pair);
137             } else {
138                 // added
139                 InputBlockEdge edge = graph.addBlockEdge(blocksMap.get(from), blocksMap.get(to));
140                 edge.setState(InputBlockEdge.State.NEW);
141             }
142         }
143         for (Pair<String, String> deleted : aEdges) {
144             // removed
145             InputBlock from = graph.getBlock(deleted.getLeft());
146             InputBlock to = graph.getBlock(deleted.getRight());
147             InputBlockEdge edge = graph.addBlockEdge(from, to);
148             edge.setState(InputBlockEdge.State.DELETED);
149         }
150 
151         Set<InputNode> nodesA = new HashSet<>(a.getNodes());
152         Set<InputNode> nodesB = new HashSet<>(b.getNodes());
153 
154         Map<InputNode, InputNode> inputNodeMap = new HashMap<>(pairs.size());
155         for (NodePair p : pairs) {
156             InputNode n = p.getLeft();
157             assert nodesA.contains(n);
158             InputNode nB = p.getRight();
159             assert nodesB.contains(nB);
160 
161             nodesA.remove(n);
162             nodesB.remove(nB);
163             InputNode n2 = new InputNode(n);
164             inputNodeMap.put(n, n2);
165             inputNodeMap.put(nB, n2);
166             graph.addNode(n2);
167             InputBlock block = blocksMap.get(a.getBlock(n));
168             block.addNode(n2.getId());
169             markAsChanged(n2, n, nB);
170         }
171 
172         for (InputNode n : nodesA) {
173             InputNode n2 = new InputNode(n);
174             graph.addNode(n2);
175             InputBlock block = blocksMap.get(a.getBlock(n));
176             block.addNode(n2.getId());
177             markAsDeleted(n2);
178             inputNodeMap.put(n, n2);
179         }
180 
181         int curIndex = 0;
182         for (InputNode n : nodesB) {
183             InputNode n2 = new InputNode(n);
184 
185             // Find new ID for node of b, does not change the id property
186             while (graph.getNode(curIndex) != null) {
187                 curIndex++;
188             }
189 
190             n2.setId(curIndex);
191             graph.addNode(n2);
192             InputBlock block = blocksMap.get(b.getBlock(n));
193             block.addNode(n2.getId());
194             markAsNew(n2);
195             inputNodeMap.put(n, n2);
196         }
197 
198         Collection<InputEdge> edgesA = a.getEdges();
199         Collection<InputEdge> edgesB = b.getEdges();
200 
201         Set<InputEdge> newEdges = new HashSet<>();
202 
203         for (InputEdge e : edgesA) {
204             int from = e.getFrom();
205             int to = e.getTo();
206             InputNode nodeFrom = inputNodeMap.get(a.getNode(from));
207             InputNode nodeTo = inputNodeMap.get(a.getNode(to));
208             char fromIndex = e.getFromIndex();
209             char toIndex = e.getToIndex();
210 
211             if (nodeFrom == null || nodeTo == null) {
212                 System.out.println("Unexpected edge : " + from + " -> " + to);
213             } else {
214                 InputEdge newEdge = new InputEdge(fromIndex, toIndex, nodeFrom.getId(), nodeTo.getId(), e.getLabel(), e.getType());
215                 if (!newEdges.contains(newEdge)) {
216                     markAsDeleted(newEdge);
217                     newEdges.add(newEdge);
218                     graph.addEdge(newEdge);
219                 }
220             }
221         }
222 
223         for (InputEdge e : edgesB) {
224             int from = e.getFrom();
225             int to = e.getTo();
226             InputNode nodeFrom = inputNodeMap.get(b.getNode(from));
227             InputNode nodeTo = inputNodeMap.get(b.getNode(to));
228             char fromIndex = e.getFromIndex();
229             char toIndex = e.getToIndex();
230 
231             if (nodeFrom == null || nodeTo == null) {
232                 System.out.println("Unexpected edge : " + from + " -> " + to);
233             } else {
234                 InputEdge newEdge = new InputEdge(fromIndex, toIndex, nodeFrom.getId(), nodeTo.getId(), e.getLabel(), e.getType());
235                 if (!newEdges.contains(newEdge)) {
236                     markAsNew(newEdge);
237                     newEdges.add(newEdge);
238                     graph.addEdge(newEdge);
239                 } else {
240                     newEdges.remove(newEdge);
241                     graph.removeEdge(newEdge);
242                     markAsSame(newEdge);
243                     newEdges.add(newEdge);
244                     graph.addEdge(newEdge);
245                 }
246             }
247         }
248 
249         return graph;
250     }
251 
252     private static class NodePair extends Pair<InputNode, InputNode> {
253 
254 
NodePair(InputNode n1, InputNode n2)255         public NodePair(InputNode n1, InputNode n2) {
256             super(n1, n2);
257         }
258 
getValue()259         public double getValue() {
260 
261             double result = 0.0;
262             for (Property p : getLeft().getProperties()) {
263                 double faktor = 1.0;
264                 for (String forbidden : IGNORE_PROPERTIES) {
265                     if (p.getName().equals(forbidden)) {
266                         faktor = 0.1;
267                         break;
268                     }
269                 }
270                 String p2 = getRight().getProperties().get(p.getName());
271                 result += evaluate(p.getValue(), p2) * faktor;
272             }
273 
274             return result;
275         }
276 
evaluate(String p, String p2)277         private double evaluate(String p, String p2) {
278             if (p2 == null) {
279                 return 1.0;
280             }
281             if (p.equals(p2)) {
282                 return 0.0;
283             } else {
284                 return (double) (Math.abs(p.length() - p2.length())) / p.length() + 0.5;
285             }
286         }
287     }
288 
createDiff(InputGraph a, InputGraph b)289     private static InputGraph createDiff(InputGraph a, InputGraph b) {
290 
291         Set<InputNode> matched = new HashSet<>();
292 
293         Set<NodePair> pairs = new HashSet<>();
294         for (InputNode n : a.getNodes()) {
295             String s = n.getProperties().get(MAIN_PROPERTY);
296             if (s == null) {
297                 s = "";
298             }
299             for (InputNode n2 : b.getNodes()) {
300                 String s2 = n2.getProperties().get(MAIN_PROPERTY);
301                 if (s2 == null) {
302                     s2 = "";
303                 }
304 
305                 if (s.equals(s2)) {
306                     NodePair p = new NodePair(n, n2);
307                     pairs.add(p);
308                 }
309             }
310         }
311 
312         Set<NodePair> selectedPairs = new HashSet<>();
313         while (pairs.size() > 0) {
314 
315             double min = Double.MAX_VALUE;
316             NodePair minPair = null;
317             for (NodePair p : pairs) {
318                 double cur = p.getValue();
319                 if (cur < min) {
320                     minPair = p;
321                     min = cur;
322                 }
323             }
324 
325             if (min > LIMIT) {
326                 break;
327             } else {
328                 selectedPairs.add(minPair);
329 
330                 Set<NodePair> toRemove = new HashSet<>();
331                 for (NodePair p : pairs) {
332                     if (p.getLeft() == minPair.getLeft() || p.getRight() == minPair.getRight()) {
333                         toRemove.add(p);
334                     }
335                 }
336                 pairs.removeAll(toRemove);
337             }
338         }
339 
340         return createDiff(a, b, selectedPairs);
341     }
342 
markAsNew(InputEdge e)343     private static void markAsNew(InputEdge e) {
344         e.setState(InputEdge.State.NEW);
345     }
346 
markAsDeleted(InputEdge e)347     private static void markAsDeleted(InputEdge e) {
348         e.setState(InputEdge.State.DELETED);
349 
350     }
351 
markAsSame(InputEdge e)352     private static void markAsSame(InputEdge e) {
353         e.setState(InputEdge.State.SAME);
354     }
355 
markAsChanged(InputNode n, InputNode firstNode, InputNode otherNode)356     private static void markAsChanged(InputNode n, InputNode firstNode, InputNode otherNode) {
357 
358         boolean difference = false;
359         for (Property p : otherNode.getProperties()) {
360             String s = firstNode.getProperties().get(p.getName());
361             if (!p.getValue().equals(s)) {
362                 difference = true;
363                 n.getProperties().setProperty(NEW_PREFIX + p.getName(), p.getValue());
364             }
365         }
366 
367         for (Property p : firstNode.getProperties()) {
368             String s = otherNode.getProperties().get(p.getName());
369             if (s == null && p.getValue().length() > 0) {
370                 difference = true;
371                 n.getProperties().setProperty(NEW_PREFIX + p.getName(), "");
372             }
373         }
374 
375         if (difference) {
376             n.getProperties().setProperty(PROPERTY_STATE, VALUE_CHANGED);
377         } else {
378             n.getProperties().setProperty(PROPERTY_STATE, VALUE_SAME);
379         }
380     }
381 
markAsDeleted(InputNode n)382     private static void markAsDeleted(InputNode n) {
383         n.getProperties().setProperty(PROPERTY_STATE, VALUE_DELETED);
384     }
385 
markAsNew(InputNode n)386     private static void markAsNew(InputNode n) {
387         n.getProperties().setProperty(PROPERTY_STATE, VALUE_NEW);
388     }
389 }
390