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