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.internal.cmd; 27 28 import java.io.IOException; 29 import java.io.PrintWriter; 30 import java.nio.file.Path; 31 32 import jdk.jfr.EventType; 33 import jdk.jfr.ValueDescriptor; 34 import jdk.jfr.consumer.RecordedEvent; 35 import jdk.jfr.consumer.RecordedObject; 36 import jdk.jfr.consumer.RecordingFile; 37 38 final class JSONWriter extends StructuredWriter { 39 40 public JSONWriter(PrintWriter writer) { 41 super(writer); 42 } 43 44 public void print(Path source) throws IOException { 45 try (RecordingFile es = new RecordingFile(source)) { 46 printObjectBegin(); 47 printRecording(es); 48 printObjectEnd(); 49 flush(); 50 } 51 } 52 53 private void printRecording(RecordingFile es) throws IOException { 54 printDataStructureName("recording"); 55 printObjectBegin(); 56 printEvents(es); 57 printObjectEnd(); 58 } 59 60 private void printEvents(RecordingFile es) throws IOException { 61 printDataStructureName("events"); 62 printArrayBegin(); 63 boolean first = true; 64 while (es.hasMoreEvents()) { 65 RecordedEvent e = es.readEvent(); 66 printNewDataStructure(first, true, null); 67 printEvent(e); 68 flush(); 69 first = false; 70 } 71 printArrayEnd(); 72 } 73 74 private void printEvent(RecordedEvent e) { 75 printObjectBegin(); 76 EventType type = e.getEventType(); 77 printValue(true, false, "name", type.getName()); 78 printValue(false, false, "typeId", type.getId()); 79 printValue(false, false, "startTime", e.getStartTime()); 80 printValue(false, false, "duration", e.getDuration()); 81 printNewDataStructure(false, false, "values"); 82 printObject(e); 83 printObjectEnd(); 84 } 85 86 void printValue(boolean first, boolean arrayElement, String name, Object value) { 87 printNewDataStructure(first, arrayElement, name); 88 if (!printIfNull(value)) { 89 if (value instanceof Boolean) { 90 printAsString(value); 91 return; 92 } 93 if (value instanceof Double) { 94 Double dValue = (Double) value; 95 if (Double.isNaN(dValue) || Double.isInfinite(dValue)) { 96 printNull(); 97 return; 98 } 99 printAsString(value); 100 return; 101 } 102 if (value instanceof Float) { 103 Float fValue = (Float) value; 104 if (Float.isNaN(fValue) || Float.isInfinite(fValue)) { 105 printNull(); 106 return; 107 } 108 printAsString(value); 109 return; 110 } 111 if (value instanceof Number) { 112 printAsString(value); 113 return; 114 } 115 print("\""); 116 printEscaped(String.valueOf(value)); 117 print("\""); 118 } 119 } 120 121 public void printObject(RecordedObject object) { 122 printObjectBegin(); 123 boolean first = true; 124 for (ValueDescriptor v : object.getFields()) { 125 printValueDescriptor(first, false, v, object.getValue(v.getName())); 126 first = false; 127 } 128 printObjectEnd(); 129 } 130 131 private void printArray(ValueDescriptor v, Object[] array) { 132 printArrayBegin(); 133 boolean first = true; 134 for (Object arrayElement : array) { 135 printValueDescriptor(first, true, v, arrayElement); 136 first = false; 137 } 138 printArrayEnd(); 139 } 140 141 private void printValueDescriptor(boolean first, boolean arrayElement, ValueDescriptor vd, Object value) { 142 if (vd.isArray() && !arrayElement) { 143 printNewDataStructure(first, arrayElement, vd.getName()); 144 if (!printIfNull(value)) { 145 printArray(vd, (Object[]) value); 146 } 147 return; 148 } 149 if (!vd.getFields().isEmpty()) { 150 printNewDataStructure(first, arrayElement, vd.getName()); 151 if (!printIfNull(value)) { 152 printObject((RecordedObject) value); 153 } 154 return; 155 } 156 printValue(first, arrayElement, vd.getName(), value); 157 } 158 159 private void printNewDataStructure(boolean first, boolean arrayElement, String name) { 160 if (!first) { 161 print(", "); 162 if (!arrayElement) { 163 println(); 164 } 165 } 166 if (!arrayElement) { 167 printDataStructureName(name); 168 } 169 } 170 171 private boolean printIfNull(Object value) { 172 if (value == null) { 173 printNull(); 174 return true; 175 } 176 return false; 177 } 178 179 private void printNull() { 180 print("null"); 181 } 182 183 private void printDataStructureName(String text) { 184 printIndent(); 185 print("\""); 186 print(text); 187 print("\": "); 188 } 189 190 private void printObjectEnd() { 191 retract(); 192 println(); 193 printIndent(); 194 print("}"); 195 } 196 197 private void printObjectBegin() { 198 println("{"); 199 indent(); 200 } 201 202 private void printArrayEnd() { 203 print("]"); 204 } 205 206 private void printArrayBegin() { 207 print("["); 208 } 209 210 private void printEscaped(String text) { 211 for (int i = 0; i < text.length(); i++) { 212 printEscaped(text.charAt(i)); 213 } 214 } 215 216 private void printEscaped(char c) { 217 if (c == '\b') { 218 print("\\b"); 219 return; 220 } 221 if (c == '\n') { 222 print("\\n"); 223 return; 224 } 225 if (c == '\t') { 226 print("\\t"); 227 return; 228 } 229 if (c == '\f') { 230 print("\\f"); 231 return; 232 } 233 if (c == '\r') { 234 print("\\r"); 235 return; 236 } 237 if (c == '\"') { 238 print("\\\""); 239 return; 240 } 241 if (c == '\\') { 242 print("\\\\"); 243 return; 244 } 245 if (c == '/') { 246 print("\\/"); 247 return; 248 } 249 if (c > 0x7F || c < 32) { 250 print("\\u"); 251 // 0x10000 will pad with zeros. 252 print(Integer.toHexString(0x10000 + (int) c).substring(1)); 253 return; 254 } 255 print(c); 256 } 257 258 }