org.gridkit.jvmtool.stacktrace.StackTraceEventWriterV4 Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of sjk-stacktrace Show documentation
Show all versions of sjk-stacktrace Show documentation
Thread dumps: capture and encoding
package org.gridkit.jvmtool.stacktrace;
import java.io.BufferedOutputStream;
import java.io.DataOutput;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.zip.DeflaterOutputStream;
import org.gridkit.jvmtool.codec.stacktrace.ThreadSnapshotEvent;
import org.gridkit.jvmtool.codec.stacktrace.ThreadSnapshotEventPojo;
import org.gridkit.jvmtool.codec.stacktrace.ThreadTraceEvent;
import org.gridkit.jvmtool.event.Event;
import org.gridkit.jvmtool.event.MultiCounterEvent;
import org.gridkit.jvmtool.event.SimpleTagCollection;
import org.gridkit.jvmtool.event.TagCollection;
import org.gridkit.jvmtool.event.TaggedEvent;
import org.gridkit.jvmtool.event.TimestampedEvent;
import org.gridkit.jvmtool.event.UniversalEventWriter;
import org.gridkit.jvmtool.jvmevents.JvmEvents;
class StackTraceEventWriterV4 implements UniversalEventWriter {
private DataOutputStream dos;
private Map stringDic = new HashMap();
private RotatingStringDictionary dynDic = new RotatingStringDictionary(512);
{
for(String s: StackTraceCodec.PRESET_TAG_KEY_V4) {
stringDic.put(s, stringDic.size() + 1);
}
for(String s: StackTraceCodec.PRESET_TAG_TAG_V4) {
dynDic.intern(s);
}
}
private TagDictionary tagSetDic = new TagDictionary(4 << 10);
private TagDictionary counterSetDic = new TagDictionary(512);
private Map frameDic = new HashMap();
private TagEncoder encoder = new TagEncoder();
private TagEncoder counterTagEncoder = new CounterTagEncoder();
// event encoding details
private SimpleTagCollection tagBuilder = new SimpleTagCollection();
private SimpleTagCollection counterBuilder = new SimpleTagCollection();
private int counterSetRef;
private ThreadSnapshotEventPojo eventBuf = new ThreadSnapshotEventPojo();
public StackTraceEventWriterV4(OutputStream os) throws IOException {
// Magic is written by factory class
DeflaterOutputStream def = new DeflaterOutputStream(os);
this.dos = new DataOutputStream(new BufferedOutputStream(def, 32 << 10));
}
@Override
public synchronized void store(Event event) throws IOException {
try {
if (event instanceof Error) {
throw new IllegalArgumentException();
}
else if (event instanceof ThreadSnapshotEvent) {
// enrich tags
copyToBuf((ThreadSnapshotEvent) event);
storeThreadEvent(eventBuf);
}
else if (event instanceof ThreadTraceEvent) {
storeThreadEvent((ThreadTraceEvent) event);
}
else {
storeCommonEvent(event);
}
}
catch(IOException e) {
close();
throw e;
}
catch(RuntimeException e) {
close();
throw e;
}
}
private void copyToBuf(ThreadSnapshotEvent event) {
eventBuf.loadFrom(event);
// eventBuf.tags().put(JvmEvents.JVM_EVENT_KEY, JvmEvents.EVENT_THREAD_SNAPSHOT);
if (event.threadId() >= 0) {
eventBuf.counters().set(JvmEvents.THREAD_ID, event.threadId());
}
if (event.threadName() != null) {
eventBuf.tags().remove(JvmEvents.THREAD_NAME);
eventBuf.tags().put(JvmEvents.THREAD_NAME, event.threadName());
}
if (event.threadState() != null) {
eventBuf.tags().remove(JvmEvents.THREAD_STATE);
eventBuf.tags().put(JvmEvents.THREAD_STATE, event.threadState().toString());
}
}
protected void storeThreadEvent(ThreadTraceEvent snap) throws IOException {
TagCollection stags = (snap instanceof TaggedEvent) ? ((TaggedEvent)snap).tags() : null;
CounterCollection scc = (snap instanceof MultiCounterEvent) ? ((MultiCounterEvent)snap).counters() : null;
tagBuilder.clear();
markTags(); // mark unconditionally
long timestamp = -1;
if (snap instanceof TimestampedEvent) {
markTimestamp();
timestamp = ((TimestampedEvent) snap).timestamp();
}
//TODO empty stack trace is encoded for compatibility with old readers
if (snap.stackTrace() != null /*&& !snap.stackTrace().isEmpty()*/) {
markThreadStackTrace();
for(StackFrame ste: snap.stackTrace()) {
intern(ste);
}
}
if (scc != null) {
ensureCounters(scc);
}
int tagSetId = ensureTagSet(stags);
dos.writeByte(StackTraceCodec.TAG_EVENT);
StackTraceCodec.writeVarInt(dos, tagSetId);
writeTimestamp(timestamp);
writeCounters(scc);
writeTrace(snap.stackTrace());
}
protected void storeCommonEvent(Event snap) throws IOException {
tagBuilder.clear();
markTags();
long timestamp = -1;
if (snap instanceof TimestampedEvent) {
markTimestamp();
timestamp = ((TimestampedEvent) snap).timestamp();
}
CounterCollection counters = null;
if (snap instanceof MultiCounterEvent) {
ensureCounters(((MultiCounterEvent) snap).counters());
counters = ((MultiCounterEvent) snap).counters();
}
int tagSetId;
if (snap instanceof TaggedEvent) {
tagSetId = ensureTagSet(((TaggedEvent) snap).tags());
}
else {
tagSetId = ensureTagSet(new SimpleTagCollection());
}
dos.writeByte(StackTraceCodec.TAG_EVENT);
StackTraceCodec.writeVarInt(dos, tagSetId);
writeTimestamp(timestamp);
writeCounters(counters);
}
private void writeTimestamp(long timestamp) throws IOException {
if (hasTimestampMark()) {
StackTraceCodec.writeTimestamp(dos, timestamp);
}
}
private void writeCounters(CounterCollection counters) throws IOException {
if (hasCountersMark()) {
StackTraceCodec.writeVarInt(dos, counterSetRef);
for(String key: counterBuilder) {
long v = counters.getValue(key);
StackTraceCodec.writeVarLong(dos, v);
}
}
}
private void writeTrace(StackFrameList trace) throws IOException {
if (hasStackTraceMark()) {
int n = 0;
for(@SuppressWarnings("unused") StackFrame sf: trace) {
++n;
}
StackTraceCodec.writeVarInt(dos, n);
for(StackFrame ste: trace) {
StackTraceCodec.writeVarInt(dos, intern(ste));
}
}
}
private int intern(String str) throws IOException {
if (str == null) {
return 0;
}
if (!stringDic.containsKey(str)) {
dos.write(StackTraceCodec.TAG_STRING);
dos.writeUTF(str);
int n = stringDic.size() + 1;
stringDic.put(str, n);
}
return stringDic.get(str);
}
private int internDyn(String str) throws IOException {
if (str == null) {
return 0;
}
int n = dynDic.intern(str);
if (n < 0) {
n = ~n;
dos.write(StackTraceCodec.TAG_DYN_STRING);
StackTraceCodec.writeVarInt(dos, n + 1);
dos.writeUTF(str);
}
n = n + 1; // leave zero to be null
return n;
}
private int intern(StackFrame ste) throws IOException {
if (!frameDic.containsKey(ste)) {
String pkg = ste.getClassName();
int c = pkg.lastIndexOf('.');
String cn = c < 0 ? pkg : pkg.substring(c + 1);
pkg = c < 0 ? null : pkg.substring(0, c);
String mtd = ste.getMethodName();
String file = ste.getSourceFile();
int line = ste.getLineNumber() + 2;
if (line < 0) {
line = 0;
}
int npkg = intern(pkg);
int ncn = intern(cn);
int nmtd = intern(mtd);
int nfile = intern(file);
dos.writeByte(StackTraceCodec.TAG_FRAME);
StackTraceCodec.writeVarInt(dos, npkg);
StackTraceCodec.writeVarInt(dos, ncn);
StackTraceCodec.writeVarInt(dos, nmtd);
StackTraceCodec.writeVarInt(dos, nfile);
StackTraceCodec.writeVarInt(dos, line);
int n = frameDic.size() + 1;
frameDic.put(ste, n);
}
return frameDic.get(ste);
}
private int ensureTagSet(TagCollection tags) throws IOException {
try {
if (tags != null) {
for(String key: tags) {
if (!StackTraceCodec.TK_PART.equals(key)) {
// ignore storage tags
for(String tag: tags.tagsFor(key)) {
tagBuilder.put(key, tag);
}
}
}
}
int tagSetId = tagSetDic.intern(tagBuilder, encoder);
return tagSetId;
}
catch(CapsuleException e) {
throw uncapsuleExcepion(e);
}
}
private void ensureCounters(CounterCollection counters) throws IOException {
try {
counterSetRef = -1;
if (counters != null) {
Iterator it = counters.iterator();
if (it.hasNext()) {
markCounters();
counterBuilder.clear();
while(it.hasNext()) {
String key = it.next();
if (counters.getValue(key) >= 0) {
counterBuilder.put(key, "");
}
}
counterSetRef = counterSetDic.intern(counterBuilder, counterTagEncoder);
return ;
}
}
}
catch(CapsuleException e) {
throw uncapsuleExcepion(e);
}
}
protected IOException uncapsuleExcepion(CapsuleException e) throws IOException {
if (e.getCause() instanceof IOException) {
throw (IOException)e.getCause();
}
else if (e.getCause() instanceof RuntimeException) {
throw (RuntimeException)e.getCause();
}
else {
throw new IOException(e.getCause());
}
}
protected void markTimestamp() {
tagBuilder.put(StackTraceCodec.TK_PART, StackTraceCodec.TK_PART_TIMESTAMP);
}
protected void markCounters() {
tagBuilder.put(StackTraceCodec.TK_PART, StackTraceCodec.TK_PART_COUNTERS);
}
protected void markTags() {
tagBuilder.put(StackTraceCodec.TK_PART, StackTraceCodec.TK_PART_TAGS);
}
protected void markThreadStackTrace() {
tagBuilder.put(StackTraceCodec.TK_PART, StackTraceCodec.TK_PART_THREAD_STACK);
}
protected boolean hasTimestampMark() {
return tagBuilder.contains(StackTraceCodec.TK_PART, StackTraceCodec.TK_PART_TIMESTAMP);
}
protected boolean hasCountersMark() {
return tagBuilder.contains(StackTraceCodec.TK_PART, StackTraceCodec.TK_PART_COUNTERS);
}
protected boolean hasStackTraceMark() {
return tagBuilder.contains(StackTraceCodec.TK_PART, StackTraceCodec.TK_PART_THREAD_STACK);
}
@Override
public synchronized void close() {
try {
dos.close();
} catch (IOException e) {
// ignore
}
stringDic.clear();
frameDic.clear();
}
private class TagEncoder implements TagDictionary.TagSetEncoder {
DataBuffer buffer = new DataBuffer();
TagChange pending;
protected int startTag() {
return StackTraceCodec.TAG_TAG_SET;
}
@Override
public int cost(String key, String tag) {
if (tag == null || tag.length() == 0) {
return 2 + keySize(key);
}
else {
return 3 + keySize(key) + tagSize(tag);
}
}
private int keySize(String key) {
return stringDic.containsKey(key) ? 2 : 2 + key.length();
}
private int tagSize(String tag) {
return dynDic.contains(tag) ? 2 : 2 + tag.length();
}
@Override
public void startTagSet(int setId, int baseId) {
try {
buffer.writeByte(startTag());
StackTraceCodec.writeVarInt(buffer, setId);
StackTraceCodec.writeVarInt(buffer, baseId);
// System.out.print(" [" + setId + "(" + baseId + ")");
} catch (IOException e) {
throw new CapsuleException(e);
}
}
public void push(TagChange change) {
try {
if (pending != null) {
if ( (pending.op == StackTraceCodec.DIC_REMOVE_KEY && change.op == StackTraceCodec.DIC_ADD_TAG)
||(pending.op == StackTraceCodec.DIC_ADD_TAG && change.op == StackTraceCodec.DIC_REMOVE_KEY)) {
if (pending.key.equals(change.key)) {
// conflate
change.op = StackTraceCodec.DIC_SET_KEY;
if (pending.tag != null) {
change.tag = pending.tag;
}
pending = null;
}
}
if (pending != null) {
pending.encode(buffer);
}
pending = null;
}
if (change.op == StackTraceCodec.DIC_REMOVE_KEY || change.op == StackTraceCodec.DIC_ADD_TAG) {
pending = change; // defer
}
else {
change.encode(buffer);
}
} catch (IOException e) {
throw new CapsuleException(e);
}
}
@Override
public void append(String key, String tag) {
if (tag.length() == 0) {
push(new TagChange(StackTraceCodec.DIC_ADD_KEY, key, null));
}
else {
push(new TagChange(StackTraceCodec.DIC_ADD_TAG, key, tag));
}
}
@Override
public void remove(String key) {
push(new TagChange(StackTraceCodec.DIC_REMOVE_KEY, key, null));
}
@Override
public void remove(String key, String tag) {
push(new TagChange(StackTraceCodec.DIC_REMOVE_TAG, key, tag));
}
@Override
public void finishTag() {
try {
if (pending != null) {
pending.encode(buffer);
pending = null;
}
buffer.unloadTo(dos);
// end of tag delta
dos.writeByte(0);
// System.out.println("]");
} catch (IOException e) {
throw new CapsuleException(e);
}
}
}
private class TagChange {
int op;
String key;
String tag;
public TagChange(int op, String key, String tag) {
this.op = op;
this.key = key;
this.tag = tag;
}
public void encode(DataOutput out) throws IOException {
switch(op) {
case StackTraceCodec.DIC_ADD_KEY:
out.writeByte(StackTraceCodec.DIC_ADD_KEY);
StackTraceCodec.writeVarInt(out, intern(key));
// System.out.print(" +" + key);
break;
case StackTraceCodec.DIC_ADD_TAG:
out.writeByte(StackTraceCodec.DIC_ADD_TAG);
StackTraceCodec.writeVarInt(out, intern(key));
StackTraceCodec.writeVarInt(out, internDyn(tag));
// System.out.print(" +" + key + ":" + tag);
break;
case StackTraceCodec.DIC_SET_KEY:
out.writeByte(StackTraceCodec.DIC_SET_KEY);
StackTraceCodec.writeVarInt(out, intern(key));
StackTraceCodec.writeVarInt(out, internDyn(tag));
// System.out.print(" =" + key + ":" + tag);
break;
case StackTraceCodec.DIC_REMOVE_KEY:
out.writeByte(StackTraceCodec.DIC_REMOVE_KEY);
StackTraceCodec.writeVarInt(out, intern(key));
// System.out.print(" -" + key);
break;
case StackTraceCodec.DIC_REMOVE_TAG:
out.writeByte(StackTraceCodec.DIC_REMOVE_TAG);
StackTraceCodec.writeVarInt(out, intern(key));
StackTraceCodec.writeVarInt(out, internDyn(tag));
// System.out.print(" -" + key + ":" + tag);
break;
default:
throw new IOException("Encoding error");
}
}
}
private class CounterTagEncoder extends TagEncoder {
@Override
protected int startTag() {
return StackTraceCodec.TAG_COUNTER;
}
}
private static class CapsuleException extends RuntimeException {
private static final long serialVersionUID = 20161218L;
public CapsuleException(Throwable cause) {
super(cause);
}
}
}