1/* 2 * Copyright (c) 2008, 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 * 23 */ 24package com.sun.hotspot.igv.data.serialization; 25 26import com.sun.hotspot.igv.data.*; 27import com.sun.hotspot.igv.data.serialization.XMLParser.ElementHandler; 28import com.sun.hotspot.igv.data.serialization.XMLParser.HandoverElementHandler; 29import com.sun.hotspot.igv.data.serialization.XMLParser.TopElementHandler; 30import com.sun.hotspot.igv.data.services.GroupCallback; 31import java.io.IOException; 32import java.io.InputStream; 33import java.nio.channels.Channels; 34import java.nio.channels.ReadableByteChannel; 35import java.util.ArrayList; 36import java.util.HashMap; 37import java.util.Map; 38import javax.swing.SwingUtilities; 39import javax.xml.parsers.ParserConfigurationException; 40import javax.xml.parsers.SAXParserFactory; 41import javax.xml.transform.Source; 42import javax.xml.transform.stream.StreamSource; 43import javax.xml.validation.SchemaFactory; 44import org.xml.sax.InputSource; 45import org.xml.sax.SAXException; 46import org.xml.sax.SAXParseException; 47import org.xml.sax.XMLReader; 48 49/** 50 * 51 * @author Thomas Wuerthinger 52 */ 53public class Parser implements GraphParser { 54 55 public static final String INDENT = " "; 56 public static final String TOP_ELEMENT = "graphDocument"; 57 public static final String GROUP_ELEMENT = "group"; 58 public static final String GRAPH_ELEMENT = "graph"; 59 public static final String ROOT_ELEMENT = "graphDocument"; 60 public static final String PROPERTIES_ELEMENT = "properties"; 61 public static final String EDGES_ELEMENT = "edges"; 62 public static final String PROPERTY_ELEMENT = "p"; 63 public static final String EDGE_ELEMENT = "edge"; 64 public static final String NODE_ELEMENT = "node"; 65 public static final String NODES_ELEMENT = "nodes"; 66 public static final String REMOVE_EDGE_ELEMENT = "removeEdge"; 67 public static final String REMOVE_NODE_ELEMENT = "removeNode"; 68 public static final String METHOD_NAME_PROPERTY = "name"; 69 public static final String GROUP_NAME_PROPERTY = "name"; 70 public static final String METHOD_IS_PUBLIC_PROPERTY = "public"; 71 public static final String METHOD_IS_STATIC_PROPERTY = "static"; 72 public static final String TRUE_VALUE = "true"; 73 public static final String NODE_NAME_PROPERTY = "name"; 74 public static final String EDGE_NAME_PROPERTY = "name"; 75 public static final String NODE_ID_PROPERTY = "id"; 76 public static final String FROM_PROPERTY = "from"; 77 public static final String TO_PROPERTY = "to"; 78 public static final String TYPE_PROPERTY = "type"; 79 public static final String PROPERTY_NAME_PROPERTY = "name"; 80 public static final String GRAPH_NAME_PROPERTY = "name"; 81 public static final String FROM_INDEX_PROPERTY = "fromIndex"; 82 public static final String TO_INDEX_PROPERTY = "toIndex"; 83 public static final String TO_INDEX_ALT_PROPERTY = "index"; 84 public static final String LABEL_PROPERTY = "label"; 85 public static final String METHOD_ELEMENT = "method"; 86 public static final String INLINE_ELEMENT = "inline"; 87 public static final String BYTECODES_ELEMENT = "bytecodes"; 88 public static final String METHOD_BCI_PROPERTY = "bci"; 89 public static final String METHOD_SHORT_NAME_PROPERTY = "shortName"; 90 public static final String CONTROL_FLOW_ELEMENT = "controlFlow"; 91 public static final String BLOCK_NAME_PROPERTY = "name"; 92 public static final String BLOCK_ELEMENT = "block"; 93 public static final String SUCCESSORS_ELEMENT = "successors"; 94 public static final String SUCCESSOR_ELEMENT = "successor"; 95 public static final String ASSEMBLY_ELEMENT = "assembly"; 96 public static final String DIFFERENCE_PROPERTY = "difference"; 97 private TopElementHandler<GraphDocument> xmlDocument = new TopElementHandler<>(); 98 private Map<Group, Boolean> differenceEncoding = new HashMap<>(); 99 private Map<Group, InputGraph> lastParsedGraph = new HashMap<>(); 100 private GroupCallback groupCallback; 101 private HashMap<String, Integer> idCache = new HashMap<>(); 102 private ArrayList<Pair<String, String>> blockConnections = new ArrayList<>(); 103 private int maxId = 0; 104 private GraphDocument graphDocument; 105 private final ParseMonitor monitor; 106 private final ReadableByteChannel channel; 107 108 private int lookupID(String i) { 109 try { 110 return Integer.parseInt(i); 111 } catch (NumberFormatException nfe) { 112 // ignore 113 } 114 Integer id = idCache.get(i); 115 if (id == null) { 116 id = maxId++; 117 idCache.put(i, id); 118 } 119 return id.intValue(); 120 } 121 122 // <graphDocument> 123 private ElementHandler<GraphDocument, Object> topHandler = new ElementHandler<GraphDocument, Object>(TOP_ELEMENT) { 124 125 @Override 126 protected GraphDocument start() throws SAXException { 127 graphDocument = new GraphDocument(); 128 return graphDocument; 129 } 130 }; 131 // <group> 132 private ElementHandler<Group, Folder> groupHandler = new XMLParser.ElementHandler<Group, Folder>(GROUP_ELEMENT) { 133 134 @Override 135 protected Group start() throws SAXException { 136 final Group group = new Group(this.getParentObject()); 137 138 String differenceProperty = this.readAttribute(DIFFERENCE_PROPERTY); 139 Parser.this.differenceEncoding.put(group, (differenceProperty != null && (differenceProperty.equals("1") || differenceProperty.equals("true")))); 140 141 ParseMonitor monitor = getMonitor(); 142 if (monitor != null) { 143 monitor.setState(group.getName()); 144 } 145 146 final Folder parent = getParentObject(); 147 if (groupCallback == null || parent instanceof Group) { 148 SwingUtilities.invokeLater(new Runnable(){ 149 @Override 150 public void run() { 151 parent.addElement(group); 152 } 153 }); 154 } 155 156 return group; 157 } 158 159 @Override 160 protected void end(String text) throws SAXException { 161 } 162 }; 163 // <method> 164 private ElementHandler<InputMethod, Group> methodHandler = new XMLParser.ElementHandler<InputMethod, Group>(METHOD_ELEMENT) { 165 166 @Override 167 protected InputMethod start() throws SAXException { 168 169 InputMethod method = parseMethod(this, getParentObject()); 170 getParentObject().setMethod(method); 171 return method; 172 } 173 }; 174 175 private InputMethod parseMethod(XMLParser.ElementHandler<?,?> handler, Group group) throws SAXException { 176 String s = handler.readRequiredAttribute(METHOD_BCI_PROPERTY); 177 int bci = 0; 178 try { 179 bci = Integer.parseInt(s); 180 } catch (NumberFormatException e) { 181 throw new SAXException(e); 182 } 183 InputMethod method = new InputMethod(group, handler.readRequiredAttribute(METHOD_NAME_PROPERTY), handler.readRequiredAttribute(METHOD_SHORT_NAME_PROPERTY), bci); 184 return method; 185 } 186 // <bytecodes> 187 private HandoverElementHandler<InputMethod> bytecodesHandler = new XMLParser.HandoverElementHandler<InputMethod>(BYTECODES_ELEMENT, true) { 188 189 @Override 190 protected void end(String text) throws SAXException { 191 getParentObject().setBytecodes(text); 192 } 193 }; 194 // <inlined> 195 private HandoverElementHandler<InputMethod> inlinedHandler = new XMLParser.HandoverElementHandler<>(INLINE_ELEMENT); 196 // <inlined><method> 197 private ElementHandler<InputMethod, InputMethod> inlinedMethodHandler = new XMLParser.ElementHandler<InputMethod, InputMethod>(METHOD_ELEMENT) { 198 199 @Override 200 protected InputMethod start() throws SAXException { 201 InputMethod method = parseMethod(this, getParentObject().getGroup()); 202 getParentObject().addInlined(method); 203 return method; 204 } 205 }; 206 // <graph> 207 private ElementHandler<InputGraph, Group> graphHandler = new XMLParser.ElementHandler<InputGraph, Group>(GRAPH_ELEMENT) { 208 209 @Override 210 protected InputGraph start() throws SAXException { 211 String name = readAttribute(GRAPH_NAME_PROPERTY); 212 InputGraph curGraph = new InputGraph(name); 213 if (differenceEncoding.get(getParentObject())) { 214 InputGraph previous = lastParsedGraph.get(getParentObject()); 215 lastParsedGraph.put(getParentObject(), curGraph); 216 if (previous != null) { 217 for (InputNode n : previous.getNodes()) { 218 curGraph.addNode(n); 219 } 220 for (InputEdge e : previous.getEdges()) { 221 curGraph.addEdge(e); 222 } 223 } 224 } 225 ParseMonitor monitor = getMonitor(); 226 if (monitor != null) { 227 monitor.updateProgress(); 228 } 229 return curGraph; 230 } 231 232 @Override 233 protected void end(String text) throws SAXException { 234 // NOTE: Some graphs intentionally don't provide blocks. Instead 235 // they later generate the blocks from other information such 236 // as node properties (example: ServerCompilerScheduler). 237 // Thus, we shouldn't assign nodes that don't belong to any 238 // block to some artificial block below unless blocks are 239 // defined and nodes are assigned to them. 240 241 final InputGraph graph = getObject(); 242 final Group parent = getParentObject(); 243 if (graph.getBlocks().size() > 0) { 244 boolean blocksContainNodes = false; 245 for (InputBlock b : graph.getBlocks()) { 246 if (b.getNodes().size() > 0) { 247 blocksContainNodes = true; 248 break; 249 } 250 } 251 252 if (!blocksContainNodes) { 253 graph.clearBlocks(); 254 blockConnections.clear(); 255 } else { 256 // Blocks and their nodes defined: add other nodes to an 257 // artificial "no block" block 258 InputBlock noBlock = null; 259 for (InputNode n : graph.getNodes()) { 260 if (graph.getBlock(n) == null) { 261 if (noBlock == null) { 262 noBlock = graph.addBlock("(no block)"); 263 } 264 265 noBlock.addNode(n.getId()); 266 } 267 268 assert graph.getBlock(n) != null; 269 } 270 } 271 } 272 273 // Resolve block successors 274 for (Pair<String, String> p : blockConnections) { 275 final InputBlock left = graph.getBlock(p.getLeft()); 276 assert left != null; 277 final InputBlock right = graph.getBlock(p.getRight()); 278 assert right != null; 279 graph.addBlockEdge(left, right); 280 } 281 blockConnections.clear(); 282 283 SwingUtilities.invokeLater(new Runnable(){ 284 285 @Override 286 public void run() { 287 // Add to group 288 parent.addElement(graph); 289 } 290 }); 291 } 292 }; 293 // <nodes> 294 private HandoverElementHandler<InputGraph> nodesHandler = new HandoverElementHandler<>(NODES_ELEMENT); 295 // <controlFlow> 296 private HandoverElementHandler<InputGraph> controlFlowHandler = new HandoverElementHandler<>(CONTROL_FLOW_ELEMENT); 297 // <block> 298 private ElementHandler<InputBlock, InputGraph> blockHandler = new ElementHandler<InputBlock, InputGraph>(BLOCK_ELEMENT) { 299 300 @Override 301 protected InputBlock start() throws SAXException { 302 InputGraph graph = getParentObject(); 303 String name = readRequiredAttribute(BLOCK_NAME_PROPERTY); 304 InputBlock b = graph.addBlock(name); 305 for (InputNode n : b.getNodes()) { 306 assert graph.getBlock(n).equals(b); 307 } 308 return b; 309 } 310 }; 311 // <nodes> 312 private HandoverElementHandler<InputBlock> blockNodesHandler = new HandoverElementHandler<>(NODES_ELEMENT); 313 // <node> 314 private ElementHandler<InputBlock, InputBlock> blockNodeHandler = new ElementHandler<InputBlock, InputBlock>(NODE_ELEMENT) { 315 316 @Override 317 protected InputBlock start() throws SAXException { 318 String s = readRequiredAttribute(NODE_ID_PROPERTY); 319 320 int id = 0; 321 try { 322 id = lookupID(s); 323 } catch (NumberFormatException e) { 324 throw new SAXException(e); 325 } 326 getParentObject().addNode(id); 327 return getParentObject(); 328 } 329 }; 330 // <successors> 331 private HandoverElementHandler<InputBlock> successorsHandler = new HandoverElementHandler<>(SUCCESSORS_ELEMENT); 332 // <successor> 333 private ElementHandler<InputBlock, InputBlock> successorHandler = new ElementHandler<InputBlock, InputBlock>(SUCCESSOR_ELEMENT) { 334 335 @Override 336 protected InputBlock start() throws SAXException { 337 String name = readRequiredAttribute(BLOCK_NAME_PROPERTY); 338 blockConnections.add(new Pair<>(getParentObject().getName(), name)); 339 return getParentObject(); 340 } 341 }; 342 // <node> 343 private ElementHandler<InputNode, InputGraph> nodeHandler = new ElementHandler<InputNode, InputGraph>(NODE_ELEMENT) { 344 345 @Override 346 protected InputNode start() throws SAXException { 347 String s = readRequiredAttribute(NODE_ID_PROPERTY); 348 int id = 0; 349 try { 350 id = lookupID(s); 351 } catch (NumberFormatException e) { 352 throw new SAXException(e); 353 } 354 InputNode node = new InputNode(id); 355 getParentObject().addNode(node); 356 return node; 357 } 358 }; 359 // <removeNode> 360 private ElementHandler<InputNode, InputGraph> removeNodeHandler = new ElementHandler<InputNode, InputGraph>(REMOVE_NODE_ELEMENT) { 361 362 @Override 363 protected InputNode start() throws SAXException { 364 String s = readRequiredAttribute(NODE_ID_PROPERTY); 365 int id = 0; 366 try { 367 id = lookupID(s); 368 } catch (NumberFormatException e) { 369 throw new SAXException(e); 370 } 371 return getParentObject().removeNode(id); 372 } 373 }; 374 // <graph> 375 private HandoverElementHandler<InputGraph> edgesHandler = new HandoverElementHandler<>(EDGES_ELEMENT); 376 377 // Local class for edge elements 378 private class EdgeElementHandler extends ElementHandler<InputEdge, InputGraph> { 379 380 public EdgeElementHandler(String name) { 381 super(name); 382 } 383 384 @Override 385 protected InputEdge start() throws SAXException { 386 int fromIndex = 0; 387 int toIndex = 0; 388 int from = -1; 389 int to = -1; 390 String label = null; 391 String type = null; 392 393 try { 394 String fromIndexString = readAttribute(FROM_INDEX_PROPERTY); 395 if (fromIndexString != null) { 396 fromIndex = Integer.parseInt(fromIndexString); 397 } 398 399 String toIndexString = readAttribute(TO_INDEX_PROPERTY); 400 if (toIndexString == null) { 401 toIndexString = readAttribute(TO_INDEX_ALT_PROPERTY); 402 } 403 if (toIndexString != null) { 404 toIndex = Integer.parseInt(toIndexString); 405 } 406 407 label = readAttribute(LABEL_PROPERTY); 408 type = readAttribute(TYPE_PROPERTY); 409 410 from = lookupID(readRequiredAttribute(FROM_PROPERTY)); 411 to = lookupID(readRequiredAttribute(TO_PROPERTY)); 412 } catch (NumberFormatException e) { 413 throw new SAXException(e); 414 } 415 416 InputEdge conn = new InputEdge((char) fromIndex, (char) toIndex, from, to, label, type == null ? "" : type); 417 return start(conn); 418 } 419 420 protected InputEdge start(InputEdge conn) throws SAXException { 421 return conn; 422 } 423 } 424 // <edge> 425 private EdgeElementHandler edgeHandler = new EdgeElementHandler(EDGE_ELEMENT) { 426 427 @Override 428 protected InputEdge start(InputEdge conn) throws SAXException { 429 getParentObject().addEdge(conn); 430 return conn; 431 } 432 }; 433 // <removeEdge> 434 private EdgeElementHandler removeEdgeHandler = new EdgeElementHandler(REMOVE_EDGE_ELEMENT) { 435 436 @Override 437 protected InputEdge start(InputEdge conn) throws SAXException { 438 getParentObject().removeEdge(conn); 439 return conn; 440 } 441 }; 442 // <properties> 443 private HandoverElementHandler<Properties.Provider> propertiesHandler = new HandoverElementHandler<>(PROPERTIES_ELEMENT); 444 // <properties> 445 private HandoverElementHandler<Group> groupPropertiesHandler = new HandoverElementHandler<Group>(PROPERTIES_ELEMENT) { 446 447 @Override 448 public void end(String text) throws SAXException { 449 if (groupCallback != null && getParentObject().getParent() instanceof GraphDocument) { 450 final Group group = getParentObject(); 451 SwingUtilities.invokeLater(new Runnable() { 452 @Override 453 public void run() { 454 groupCallback.started(group); 455 } 456 }); 457 } 458 } 459 }; 460 // <property> 461 private ElementHandler<String, Properties.Provider> propertyHandler = new XMLParser.ElementHandler<String, Properties.Provider>(PROPERTY_ELEMENT, true) { 462 463 @Override 464 public String start() throws SAXException { 465 return readRequiredAttribute(PROPERTY_NAME_PROPERTY); 466 } 467 468 @Override 469 public void end(String text) { 470 getParentObject().getProperties().setProperty(getObject(), text.trim()); 471 } 472 }; 473 474 public Parser(ReadableByteChannel channel) { 475 this(channel, null, null); 476 } 477 478 public Parser(ReadableByteChannel channel, ParseMonitor monitor, GroupCallback groupCallback) { 479 480 this.groupCallback = groupCallback; 481 this.monitor = monitor; 482 this.channel = channel; 483 484 // Initialize dependencies 485 xmlDocument.addChild(topHandler); 486 topHandler.addChild(groupHandler); 487 488 groupHandler.addChild(methodHandler); 489 groupHandler.addChild(graphHandler); 490 groupHandler.addChild(groupHandler); 491 492 methodHandler.addChild(inlinedHandler); 493 methodHandler.addChild(bytecodesHandler); 494 495 inlinedHandler.addChild(inlinedMethodHandler); 496 inlinedMethodHandler.addChild(bytecodesHandler); 497 inlinedMethodHandler.addChild(inlinedHandler); 498 499 graphHandler.addChild(nodesHandler); 500 graphHandler.addChild(edgesHandler); 501 graphHandler.addChild(controlFlowHandler); 502 503 controlFlowHandler.addChild(blockHandler); 504 505 blockHandler.addChild(successorsHandler); 506 successorsHandler.addChild(successorHandler); 507 blockHandler.addChild(blockNodesHandler); 508 blockNodesHandler.addChild(blockNodeHandler); 509 510 nodesHandler.addChild(nodeHandler); 511 nodesHandler.addChild(removeNodeHandler); 512 edgesHandler.addChild(edgeHandler); 513 edgesHandler.addChild(removeEdgeHandler); 514 515 methodHandler.addChild(propertiesHandler); 516 inlinedMethodHandler.addChild(propertiesHandler); 517 topHandler.addChild(propertiesHandler); 518 groupHandler.addChild(groupPropertiesHandler); 519 graphHandler.addChild(propertiesHandler); 520 nodeHandler.addChild(propertiesHandler); 521 propertiesHandler.addChild(propertyHandler); 522 groupPropertiesHandler.addChild(propertyHandler); 523 } 524 525 // Returns a new GraphDocument object deserialized from an XML input source. 526 @Override 527 public GraphDocument parse() throws IOException { 528 if (monitor != null) { 529 monitor.setState("Starting parsing"); 530 } 531 try { 532 XMLReader reader = createReader(); 533 reader.setContentHandler(new XMLParser(xmlDocument, monitor)); 534 reader.parse(new InputSource(Channels.newInputStream(channel))); 535 } catch (SAXException ex) { 536 if (!(ex instanceof SAXParseException) || !"XML document structures must start and end within the same entity.".equals(ex.getMessage())) { 537 throw new IOException(ex); 538 } 539 } 540 if (monitor != null) { 541 monitor.setState("Finished parsing"); 542 } 543 return graphDocument; 544 } 545 546 private XMLReader createReader() throws SAXException { 547 try { 548 SAXParserFactory pfactory = SAXParserFactory.newInstance(); 549 pfactory.setValidating(true); 550 pfactory.setNamespaceAware(true); 551 552 // Enable schema validation 553 SchemaFactory sfactory = SchemaFactory.newInstance("http://www.w3.org/2001/XMLSchema"); 554 InputStream stream = Parser.class.getResourceAsStream("graphdocument.xsd"); 555 pfactory.setSchema(sfactory.newSchema(new Source[]{new StreamSource(stream)})); 556 557 return pfactory.newSAXParser().getXMLReader(); 558 } catch (ParserConfigurationException ex) { 559 throw new SAXException(ex); 560 } 561 } 562} 563