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.cmd; 27 28 import java.io.StringReader; 29 import java.nio.file.Path; 30 import java.time.Duration; 31 import java.time.Instant; 32 import java.util.AbstractMap.SimpleEntry; 33 import java.util.ArrayList; 34 import java.util.HashMap; 35 import java.util.Iterator; 36 import java.util.List; 37 import java.util.Map; 38 import java.util.Stack; 39 40 import javax.xml.parsers.SAXParser; 41 import javax.xml.parsers.SAXParserFactory; 42 43 import jdk.jfr.ValueDescriptor; 44 import jdk.jfr.consumer.RecordedEvent; 45 import jdk.jfr.consumer.RecordedObject; 46 import jdk.jfr.consumer.RecordingFile; 47 import jdk.test.lib.process.OutputAnalyzer; 48 49 import org.xml.sax.Attributes; 50 import org.xml.sax.InputSource; 51 import org.xml.sax.SAXException; 52 import org.xml.sax.XMLReader; 53 import org.xml.sax.helpers.DefaultHandler; 54 55 /* 56 * @test 57 * @key jfr 58 * @summary Tests print --xml 59 * 60 * @library /test/lib /test/jdk 61 * @modules java.scripting 62 * java.xml 63 * jdk.jfr 64 * 65 * @run main/othervm jdk.jfr.cmd.TestPrintXML 66 */ 67 public class TestPrintXML { 68 69 public static void main(String... args) throws Exception { 70 71 Path recordingFile = ExecuteHelper.createProfilingRecording().toAbsolutePath(); 72 73 OutputAnalyzer output = ExecuteHelper.run("print", "--xml", recordingFile.toString()); 74 String xml = output.getStdout(); 75 System.out.println(xml); 76 // Parse XML string 77 SAXParserFactory factory = SAXParserFactory.newInstance(); 78 SAXParser sp = factory.newSAXParser(); 79 XMLReader xr = sp.getXMLReader(); 80 RecordingHandler handler = new RecordingHandler(); 81 xr.setContentHandler(handler); 82 xr.parse(new InputSource(new StringReader(xml))); 83 84 // Verify that all data was written correctly 85 Iterator<RecordedEvent> it = RecordingFile.readAllEvents(recordingFile).iterator(); 86 for (XMLEvent xmlEvent : handler.events) { 87 RecordedEvent re = it.next(); 88 if (!compare(re, xmlEvent.values)) { 89 System.out.println(re); 90 System.out.println(xmlEvent.values.toString()); 91 throw new Exception("Event doesn't match"); 92 } 93 } 94 95 } 96 97 @SuppressWarnings("unchecked") 98 static boolean compare(Object eventObject, Object xmlObject) { 99 if (eventObject == null) { 100 return xmlObject == null; 101 } 102 if (eventObject instanceof RecordedObject) { 103 RecordedObject re = (RecordedObject) eventObject; 104 Map<String, Object> xmlMap = (Map<String, Object>) xmlObject; 105 List<ValueDescriptor> fields = re.getFields(); 106 if (fields.size() != xmlMap.size()) { 107 return false; 108 } 109 for (ValueDescriptor v : fields) { 110 String name = v.getName(); 111 if (!compare(re.getValue(name), xmlMap.get(name))) { 112 return false; 113 } 114 } 115 return true; 116 } 117 if (eventObject.getClass().isArray()) { 118 Object[] array = (Object[]) eventObject; 119 Object[] xmlArray = (Object[]) xmlObject; 120 if (array.length != xmlArray.length) { 121 return false; 122 } 123 for (int i = 0; i < array.length; i++) { 124 if (!compare(array[i], xmlArray[i])) { 125 return false; 126 } 127 } 128 return true; 129 } 130 String s1 = String.valueOf(eventObject); 131 String s2 = (String) xmlObject; 132 return s1.equals(s2); 133 } 134 135 static class XMLEvent { 136 String name; 137 Instant startTime; 138 Duration duration; 139 Map<String, Object> values = new HashMap<>(); 140 141 XMLEvent(String name, Instant startTime, Duration duration) { 142 this.name = name; 143 this.startTime = startTime; 144 this.duration = duration; 145 } 146 } 147 148 public static final class RecordingHandler extends DefaultHandler { 149 150 private Stack<Object> objects = new Stack<>(); 151 private Stack<SimpleEntry<String, String>> elements = new Stack<>(); 152 private List<XMLEvent> events = new ArrayList<>(); 153 154 @Override 155 public void startElement(String uri, String localName, String qName, Attributes attrs) throws SAXException { 156 elements.push(new SimpleEntry<>(attrs.getValue("name"), attrs.getValue("index"))); 157 switch (qName) { 158 case "null": 159 objects.pop(); 160 objects.push(null); 161 break; 162 case "event": 163 Instant startTime = Instant.parse(attrs.getValue("startTime")); 164 Duration duration = Duration.parse(attrs.getValue("duration")); 165 objects.push(new XMLEvent(attrs.getValue("name"), startTime, duration)); 166 break; 167 case "struct": 168 objects.push(new HashMap<String, Object>()); 169 break; 170 case "array": 171 objects.push(new Object[Integer.parseInt(attrs.getValue("size"))]); 172 break; 173 case "value": 174 objects.push(new StringBuilder()); 175 break; 176 } 177 } 178 179 @Override 180 public void characters(char[] ch, int start, int length) throws SAXException { 181 if (!objects.isEmpty()) { 182 Object o = objects.peek(); 183 if (o instanceof StringBuilder) { 184 ((StringBuilder) o).append(ch, start, length); 185 } 186 } 187 } 188 189 @SuppressWarnings("unchecked") 190 @Override 191 public void endElement(String uri, String localName, String qName) { 192 SimpleEntry<String, String> element = elements.pop(); 193 switch (qName) { 194 case "event": 195 case "struct": 196 case "array": 197 case "value": 198 String name = element.getKey(); 199 Object value = objects.pop(); 200 if (objects.isEmpty()) { 201 events.add((XMLEvent) value); 202 return; 203 } 204 if (value instanceof StringBuilder) { 205 value = ((StringBuilder) value).toString(); 206 } 207 Object parent = objects.peek(); 208 if (parent instanceof XMLEvent) { 209 ((XMLEvent) parent).values.put(name, value); 210 } 211 if (parent instanceof Map) { 212 ((Map<String, Object>) parent).put(name, value); 213 } 214 if (parent != null && parent.getClass().isArray()) { 215 int index = Integer.parseInt(element.getValue()); 216 ((Object[]) parent)[index] = value; 217 } 218 } 219 } 220 } 221 }