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  */
24 package com.sun.hotspot.igv.data.serialization;
25 
26 import com.sun.hotspot.igv.data.*;
27 import com.sun.hotspot.igv.data.serialization.XMLParser.ElementHandler;
28 import com.sun.hotspot.igv.data.serialization.XMLParser.HandoverElementHandler;
29 import com.sun.hotspot.igv.data.serialization.XMLParser.TopElementHandler;
30 import com.sun.hotspot.igv.data.services.GroupCallback;
31 import java.io.IOException;
32 import java.io.InputStream;
33 import java.nio.channels.Channels;
34 import java.nio.channels.ReadableByteChannel;
35 import java.util.ArrayList;
36 import java.util.HashMap;
37 import java.util.Map;
38 import javax.swing.SwingUtilities;
39 import javax.xml.parsers.ParserConfigurationException;
40 import javax.xml.parsers.SAXParserFactory;
41 import javax.xml.transform.Source;
42 import javax.xml.transform.stream.StreamSource;
43 import javax.xml.validation.SchemaFactory;
44 import org.xml.sax.InputSource;
45 import org.xml.sax.SAXException;
46 import org.xml.sax.SAXParseException;
47 import org.xml.sax.XMLReader;
48 
49 /**
50  *
51  * @author Thomas Wuerthinger
52  */
53 public 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 
lookupID(String i)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 
parseMethod(XMLParser.ElementHandler<?,?> handler, Group group)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 
EdgeElementHandler(String name)380         public EdgeElementHandler(String name) {
381             super(name);
382         }
383 
384         @Override
start()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 
start(InputEdge conn)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 
Parser(ReadableByteChannel channel)474     public Parser(ReadableByteChannel channel) {
475         this(channel, null, null);
476     }
477 
Parser(ReadableByteChannel channel, ParseMonitor monitor, GroupCallback groupCallback)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
parse()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 
createReader()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