org.gridkit.jvmtool.stacktrace.StackTraceEventReaderV4 Maven / Gradle / Ivy
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 {
private static StackFrameList EMPTY_STACK = new StackFrameArray(new StackFrame[0]);
private DataInputStream dis;
private List stringDic = new ArrayList();
private List frameDic = new ArrayList();
private List dynStringDic = new ArrayList();
private List tagDic = new ArrayList();
private List counterDic = new ArrayList();
// 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 EventReader morph(EventMorpher 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 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 n = new ArrayList();
for(String key: tags) {
n.add(key);
}
names = n.toArray(new String[n.size()]);
values = new long[names.length];
counters = new CounterArray(names, values);
}
}
}