1 /* 2 * Copyright (c) 2016, 2018, 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 26 package jdk.jfr.tool; 27 28 import java.io.File; 29 import java.io.StringReader; 30 import java.nio.file.Path; 31 import java.time.OffsetDateTime; 32 import java.util.AbstractMap.SimpleEntry; 33 import java.util.ArrayList; 34 import java.util.Collections; 35 import java.util.HashMap; 36 import java.util.Iterator; 37 import java.util.List; 38 import java.util.Map; 39 import java.util.Stack; 40 41 import javax.xml.XMLConstants; 42 import javax.xml.parsers.SAXParser; 43 import javax.xml.parsers.SAXParserFactory; 44 import javax.xml.validation.Schema; 45 import javax.xml.validation.SchemaFactory; 46 47 import org.xml.sax.Attributes; 48 import org.xml.sax.InputSource; 49 import org.xml.sax.SAXException; 50 import org.xml.sax.SAXParseException; 51 import org.xml.sax.XMLReader; 52 import org.xml.sax.helpers.DefaultHandler; 53 54 import jdk.jfr.Timespan; 55 import jdk.jfr.Timestamp; 56 import jdk.jfr.ValueDescriptor; 57 import jdk.jfr.consumer.RecordedEvent; 58 import jdk.jfr.consumer.RecordedObject; 59 import jdk.jfr.consumer.RecordingFile; 60 import jdk.test.lib.process.OutputAnalyzer; 61 62 /** 63 * @test 64 * @key jfr 65 * @summary Tests print --xml 66 * 67 * @library /lib / 68 * @modules java.scripting java.xml jdk.jfr 69 * 70 * @run main/othervm jdk.jfr.tool.TestPrintXML 71 */ 72 public class TestPrintXML { 73 74 public static void main(String... args) throws Throwable { 75 76 Path recordingFile = ExecuteHelper.createProfilingRecording().toAbsolutePath(); 77 78 OutputAnalyzer output = ExecuteHelper.jfr("print", "--xml", "--stack-depth", "9999", recordingFile.toString()); 79 System.out.println(recordingFile); 80 String xml = output.getStdout(); 81 82 SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); 83 Schema schema = schemaFactory.newSchema(new File(System.getProperty("test.src"), "jfr.xsd")); 84 85 SAXParserFactory factory = SAXParserFactory.newInstance(); 86 factory.setSchema(schema); 87 factory.setNamespaceAware(true); 88 89 SAXParser sp = factory.newSAXParser(); 90 XMLReader xr = sp.getXMLReader(); 91 RecordingHandler handler = new RecordingHandler(); 92 xr.setContentHandler(handler); 93 xr.setErrorHandler(handler); 94 xr.parse(new InputSource(new StringReader(xml))); 95 96 // Verify that all data was written correctly 97 List<RecordedEvent> events = RecordingFile.readAllEvents(recordingFile); 98 Collections.sort(events, (e1, e2) -> e1.getEndTime().compareTo(e2.getEndTime())); 99 Iterator<RecordedEvent> it = events.iterator(); 100 for (XMLEvent xmlEvent : handler.events) { 101 RecordedEvent re = it.next(); 102 if (!compare(re, xmlEvent.values)) { 103 System.out.println("Expected:"); 104 System.out.println("----------------------"); 105 System.out.println(re); 106 System.out.println(); 107 System.out.println("Was (XML)"); 108 System.out.println("----------------------"); 109 System.out.println(xmlEvent); 110 System.out.println(); 111 throw new Exception("Event doesn't match"); 112 } 113 } 114 115 } 116 117 @SuppressWarnings("unchecked") 118 static boolean compare(Object eventObject, Object xmlObject) { 119 if (eventObject == null) { 120 return xmlObject == null; 121 } 122 if (eventObject instanceof RecordedObject) { 123 RecordedObject re = (RecordedObject) eventObject; 124 Map<String, Object> xmlMap = (Map<String, Object>) xmlObject; 125 List<ValueDescriptor> fields = re.getFields(); 126 if (fields.size() != xmlMap.size()) { 127 return false; 128 } 129 for (ValueDescriptor v : fields) { 130 String name = v.getName(); 131 Object xmlValue = xmlMap.get(name); 132 Object expectedValue = re.getValue(name); 133 if (v.getAnnotation(Timestamp.class) != null) { 134 // Make instant of OffsetDateTime 135 xmlValue = OffsetDateTime.parse("" + xmlValue).toInstant().toString(); 136 expectedValue = re.getInstant(name); 137 } 138 if (v.getAnnotation(Timespan.class) != null) { 139 expectedValue = re.getDuration(name); 140 } 141 if (!compare(expectedValue, xmlValue)) { 142 return false; 143 } 144 } 145 return true; 146 } 147 if (eventObject.getClass().isArray()) { 148 Object[] array = (Object[]) eventObject; 149 Object[] xmlArray = (Object[]) xmlObject; 150 if (array.length != xmlArray.length) { 151 return false; 152 } 153 for (int i = 0; i < array.length; i++) { 154 if (!compare(array[i], xmlArray[i])) { 155 return false; 156 } 157 } 158 return true; 159 } 160 String s1 = String.valueOf(eventObject); 161 String s2 = (String) xmlObject; 162 return s1.equals(s2); 163 } 164 165 static class XMLEvent { 166 String name; 167 private Map<String, Object> values = new HashMap<>(); 168 169 XMLEvent(String name) { 170 this.name = name; 171 } 172 } 173 174 public static final class RecordingHandler extends DefaultHandler { 175 176 private Stack<Object> objects = new Stack<>(); 177 private Stack<SimpleEntry<String, String>> elements = new Stack<>(); 178 private List<XMLEvent> events = new ArrayList<>(); 179 180 @Override 181 public void startElement(String uri, String localName, String qName, Attributes attrs) throws SAXException { 182 elements.push(new SimpleEntry<>(attrs.getValue("name"), attrs.getValue("index"))); 183 String nil = attrs.getValue("xsi:nil"); 184 if ("true".equals(nil)) { 185 objects.push(null); 186 return; 187 } 188 189 switch (qName) { 190 case "event": 191 objects.push(new XMLEvent(attrs.getValue("type"))); 192 break; 193 case "struct": 194 objects.push(new HashMap<String, Object>()); 195 break; 196 case "array": 197 objects.push(new Object[Integer.parseInt(attrs.getValue("size"))]); 198 break; 199 case "value": 200 objects.push(new StringBuilder()); 201 break; 202 } 203 } 204 205 @Override 206 public void characters(char[] ch, int start, int length) throws SAXException { 207 if (!objects.isEmpty()) { 208 Object o = objects.peek(); 209 if (o instanceof StringBuilder) { 210 ((StringBuilder) o).append(ch, start, length); 211 } 212 } 213 } 214 215 @SuppressWarnings("unchecked") 216 @Override 217 public void endElement(String uri, String localName, String qName) { 218 SimpleEntry<String, String> element = elements.pop(); 219 switch (qName) { 220 case "event": 221 case "struct": 222 case "array": 223 case "value": 224 String name = element.getKey(); 225 Object value = objects.pop(); 226 if (objects.isEmpty()) { 227 events.add((XMLEvent) value); 228 return; 229 } 230 if (value instanceof StringBuilder) { 231 value = ((StringBuilder) value).toString(); 232 } 233 Object parent = objects.peek(); 234 if (parent instanceof XMLEvent) { 235 ((XMLEvent) parent).values.put(name, value); 236 } 237 if (parent instanceof Map) { 238 ((Map<String, Object>) parent).put(name, value); 239 } 240 if (parent != null && parent.getClass().isArray()) { 241 int index = Integer.parseInt(element.getValue()); 242 ((Object[]) parent)[index] = value; 243 } 244 } 245 } 246 247 public void warning(SAXParseException spe) throws SAXException { 248 throw new SAXException(spe); 249 } 250 251 public void error(SAXParseException spe) throws SAXException { 252 throw new SAXException(spe); 253 } 254 255 public void fatalError(SAXParseException spe) throws SAXException { 256 throw new SAXException(spe); 257 } 258 } 259 }