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