package org.gridkit.jvmtool.stacktrace; import java.io.BufferedInputStream; import java.io.DataInputStream; import java.io.IOException; import java.io.InputStream; import java.lang.Thread.State; import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.List; import java.util.NoSuchElementException; import java.util.zip.InflaterInputStream; import org.gridkit.jvmtool.codec.stacktrace.ThreadTraceEvent; import org.gridkit.jvmtool.event.CommonEvent; import org.gridkit.jvmtool.event.ErrorEvent; import org.gridkit.jvmtool.event.Event; import org.gridkit.jvmtool.event.EventMorpher; import org.gridkit.jvmtool.event.EventReader; import org.gridkit.jvmtool.event.MorphingEventReader; import org.gridkit.jvmtool.event.SimpleErrorEvent; import org.gridkit.jvmtool.event.SimpleTagCollection; import org.gridkit.jvmtool.event.TagCollection; class StackTraceEventReaderV4 implements EventReader<Event> { private static StackFrameList EMPTY_STACK = new StackFrameArray(new StackFrame[0]); private DataInputStream dis; private List<String> stringDic = new ArrayList<String>(); private List<StackFrame> frameDic = new ArrayList<StackFrame>(); private List<String> dynStringDic = new ArrayList<String>(); private List<TagCollection> tagDic = new ArrayList<TagCollection>(); private List<CounterSet> counterDic = new ArrayList<CounterSet>(); // event details private boolean loaded; private boolean threadDetails; // private long threadId; // private String threadName; private long timestamp; // private State threadState; private CounterArray counters; private SimpleTagCollection tags = new SimpleTagCollection(); private StackFrameList trace; private ThreadEvent threadEventProxy = new ThreadEvent(); private DataEvent dataEventProxy = new DataEvent(); private ErrorEvent errorEventProxy; private boolean done; public StackTraceEventReaderV4(InputStream is) { this.dis = new DataInputStream(new BufferedInputStream(new InflaterInputStream(is), 64 << 10)); stringDic.add(null); stringDic.addAll(Arrays.asList(StackTraceCodec.PRESET_TAG_KEY_V4)); dynStringDic.add(null); dynStringDic.addAll(Arrays.asList(StackTraceCodec.PRESET_TAG_TAG_V4)); frameDic.add(null); tagDic.add(new SimpleTagCollection()); counterDic.add(new CounterSet(new SimpleTagCollection())); loaded = false; } @Override public <M extends Event> EventReader<M> morph(EventMorpher<Event, M> morpher) { return MorphingEventReader.morph(this, morpher); } @Override public boolean hasNext() { if (!loaded) { loadNextEvent(); } return loaded; } @Override public Event next() { if (!hasNext()) { throw new NoSuchElementException(); } loaded = false; if (errorEventProxy != null) { return errorEventProxy; } if (threadDetails) { return threadEventProxy; } else { return dataEventProxy; } } @Override public Event peekNext() { if (!hasNext()) { throw new NoSuchElementException(); } if (errorEventProxy != null) { return errorEventProxy; } if (threadDetails) { return threadEventProxy; } else { return dataEventProxy; } } @Override public void remove() { throw new UnsupportedOperationException(); } @Override public Iterator<Event> iterator() { return this; } @Override public void dispose() { done = true; try { dis.close(); } catch (IOException e) { // ignore } } protected void loadNextEvent() { if (errorEventProxy != null || done) { // error raised, no further events return; } try { loadNext(); } catch(Exception e) { try { dis.close(); // release stream } catch (IOException ee) { // ignore } loaded = true; errorEventProxy = new SimpleErrorEvent(e); } } protected void loadNext() throws IOException { loaded = false; while(true) { int tag = dis.read(); if (tag < 0) { done = true; dis.close(); break; } else if (tag == StackTraceCodec.TAG_EVENT) { readEvent(); loaded = true; break; } else if (tag == StackTraceCodec.TAG_STRING) { String str = dis.readUTF(); stringDic.add(str); } else if (tag == StackTraceCodec.TAG_FRAME) { StackFrame ste = readStackTraceElement(); frameDic.add(ste); } else if (tag == StackTraceCodec.TAG_DYN_STRING) { int id = StackTraceCodec.readVarInt(dis); String str = dis.readUTF(); ensureSlot(dynStringDic, id); dynStringDic.set(id, str); } else if (tag == StackTraceCodec.TAG_COUNTER) { readCounterSet(); } else if (tag == StackTraceCodec.TAG_TAG_SET) { readTagSet(); } else { throw new IOException("Data format error"); } } } protected void readEvent() throws IOException { threadDetails = false; TagCollection tc = tagDic.get(StackTraceCodec.readVarInt(dis)); tags = new SimpleTagCollection(tc); // timestamp readEventTimestamp(); // counters readEventCounters(); // thread stack trace readEventStackTrace(); tags.remove(StackTraceCodec.TK_PART); // wipe encoding tags } protected void readEventStackTrace() throws IOException { if (tags.contains(StackTraceCodec.TK_PART, StackTraceCodec.TK_PART_THREAD_STACK)) { threadDetails = true; readStackTrace(); } else { trace = null; } } // protected void readEventThreadDetails() throws IOException { // if (tags.contains(StackTraceCodec.TK_PART, StackTraceCodec.TK_PART_THREAD_DETAILS)) { // threadDetails = true; // threadId = StackTraceCodec.readVarLong(dis); // threadName = dynStringDic.get(StackTraceCodec.readVarInt(dis)); // threadState = readState(); // } // else { // threadDetails = false; // } // } // protected void readEventCounters() throws IOException { if (tags.contains(StackTraceCodec.TK_PART, StackTraceCodec.TK_PART_COUNTERS)) { readCounters(); } else { counters = counterDic.get(0).counters; // empty counters } } protected void readEventTimestamp() throws IOException { if (tags.contains(StackTraceCodec.TK_PART, StackTraceCodec.TK_PART_TIMESTAMP)) { timestamp = StackTraceCodec.readTimestamp(dis); } else { timestamp = Long.MIN_VALUE; } } protected State readState() throws IOException { int n = StackTraceCodec.readVarInt(dis); return n == 0 ? null : State.values()[n - 1]; } protected void readTagSet() throws IOException { int n = StackTraceCodec.readVarInt(dis); int base = StackTraceCodec.readVarInt(dis); SimpleTagCollection ts = new SimpleTagCollection(tagDic.get(base)); readTagSetDelta(ts); ensureSlot(tagDic, n); tagDic.set(n, ts); } protected void readCounterSet() throws IOException { int n = StackTraceCodec.readVarInt(dis); int base = StackTraceCodec.readVarInt(dis); SimpleTagCollection ts = new SimpleTagCollection(counterDic.get(base).definition); readTagSetDelta(ts); ensureSlot(counterDic, n); CounterSet cs = new CounterSet(ts); counterDic.set(n, cs); } protected void readTagSetDelta(SimpleTagCollection ts) throws IOException { SimpleTagCollection ap = new SimpleTagCollection(); while(true) { int btag = dis.readByte(); if (btag == 0) { break; } else if (btag == StackTraceCodec.DIC_ADD_TAG) { String key = readString(); String tag = readDynString(); ap.put(key, tag); continue; } else if (btag == StackTraceCodec.DIC_SET_KEY) { String key = readString(); String tag = readDynString(); ts.remove(key); ap.put(key, tag); continue; } else if (btag == StackTraceCodec.DIC_ADD_KEY) { String key = readString(); ap.put(key, ""); continue; } else if (btag == StackTraceCodec.DIC_REMOVE_KEY) { String key = readString(); ts.remove(key); continue; } else if (btag == StackTraceCodec.DIC_REMOVE_TAG) { String key = readString(); String tag = readDynString(); ts.remove(key, tag); continue; } else { throw new IOException("Unexpected tag '" + btag + "'"); } } ts.putAll(ap); } private String readString() throws IOException { int id = StackTraceCodec.readVarInt(dis); if (id < 0 || id >= stringDic.size()) { throw new IOException("Illegal string ref #" + id); } return stringDic.get(id); } private String readDynString() throws IOException { int id = StackTraceCodec.readVarInt(dis); if (id < 0 || id >= dynStringDic.size()) { throw new IOException("Illegal string ref #" + id); } return dynStringDic.get(id); } protected void readCounters() throws IOException { int csid = StackTraceCodec.readVarInt(dis); CounterSet cs = counterDic.get(csid); for(int i = 0; i != cs.values.length; ++i) { cs.values[i] = StackTraceCodec.readVarLong(dis); } counters = cs.counters; } protected void readStackTrace() throws IOException { int len = StackTraceCodec.readVarInt(dis); if (len == 0) { trace = EMPTY_STACK; return; } StackFrame[] frames = new StackFrame[len]; for(int i = 0; i != len; ++i) { int ref = StackTraceCodec.readVarInt(dis); frames[i] = frameDic.get(ref); } trace = new StackFrameArray(frames); } private StackFrame readStackTraceElement() throws IOException { int npkg = StackTraceCodec.readVarInt(dis); int ncn = StackTraceCodec.readVarInt(dis); int nmtd = StackTraceCodec.readVarInt(dis); int nfile = StackTraceCodec.readVarInt(dis); int line = StackTraceCodec.readVarInt(dis) - 2; String cp = stringDic.get(npkg); String cn = stringDic.get(ncn); String mtd = stringDic.get(nmtd); String file = stringDic.get(nfile); StackFrame e = new StackFrame(cp, cn, mtd, file, line); return e; } private void ensureSlot(List<?> list, int n) { while(list.size() < n + 1) { list.add(null); } } private class DataEvent implements CommonEvent { @Override public long timestamp() { return timestamp; } @Override public CounterCollection counters() { return counters; } @Override public TagCollection tags() { return tags; } } private class ThreadEvent extends DataEvent implements ThreadTraceEvent { // long threadId; // String threadName; // State threadState; // // void initThreadDetails() { // threadId = counters().getValue(JvmEvents.THREAD_ID); // if (threadId < 0) { // threadId = -1; // } // threadName = tags().firstTagFor(JvmEvents.THREAD_NAME); // threadState = null; // String state = tags().firstTagFor(JvmEvents.THREAD_STATE); // if (state != null) { // try { // threadState = State.valueOf(state); // } // catch(Exception e) { // // ignore // } // } // } // // @Override // public long threadId() { // return threadId; // } // // @Override // public String threadName() { // return threadName; // } // // @Override // public State threadState() { // return threadState; // } @Override public StackFrameList stackTrace() { return trace; } } private static class CounterSet { TagCollection definition; String[] names; long[] values; CounterArray counters; public CounterSet(TagCollection tags) { this.definition = tags; List<String> n = new ArrayList<String>(); for(String key: tags) { n.add(key); } names = n.toArray(new String[n.size()]); values = new long[names.length]; counters = new CounterArray(names, values); } } }