org.gridkit.jvmtool.stacktrace.StackTraceCodec 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
/**
* Copyright 2014 Alexey Ragozin
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.gridkit.jvmtool.stacktrace;
import java.io.DataInput;
import java.io.DataInputStream;
import java.io.DataOutput;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.lang.Thread.State;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.NoSuchElementException;
import org.gridkit.jvmtool.jvmevents.JvmEvents;
public class StackTraceCodec {
static final byte[] MAGIC = toBytes("TRACEDUMP_1 ");
static final byte[] MAGIC2 = toBytes("TRACEDUMP_2 ");
//static final byte[] MAGIC3 = toBytes("TRACEDUMP_3 ");
static final byte[] MAGIC4 = toBytes("EVENTDUMP_1 ");
static final String TK_PART = "(stored-parts)";
static final String TK_PART_TIMESTAMP = "timestamp";
static final String TK_PART_THREAD_DETAILS = "thread-details";
static final String TK_PART_THREAD_STACK = "thread-stack";
static final String TK_PART_TAGS = "tags";
static final String TK_PART_COUNTERS = "counters";
private static byte[] toBytes(String text) {
try {
return text.getBytes("UTF8");
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
static final byte TAG_STRING = 1;
static final byte TAG_FRAME = 2;
static final byte TAG_EVENT = 3;
static final byte TAG_DYN_STRING = 4;
static final byte TAG_COUNTER = 5;
static final byte TAG_TAG_SET = 6;
static final long TIME_ANCHOR = 1391255854894l;
static final byte DIC_ADD_TAG = 1;
static final byte DIC_ADD_KEY = 2;
static final byte DIC_SET_KEY = 3;
static final byte DIC_REMOVE_KEY = 4;
static final byte DIC_REMOVE_TAG = 5;
static final String[] PRESET_TAG_KEY_V4 = new String[] {
"(stored-parts)",
JvmEvents.JVM_EVENT_KEY,
JvmEvents.THREAD_ID,
JvmEvents.THREAD_NAME,
JvmEvents.THREAD_STATE,
ThreadCounters.CPU_TIME_MS,
ThreadCounters.USER_TIME_MS,
ThreadCounters.ALLOCATED_BYTES,
ThreadCounters.WAIT_COUNTER,
ThreadCounters.WAIT_TIME_MS,
ThreadCounters.BLOCKED_COUNTER,
ThreadCounters.BLOCKED_TIME_MS,
JvmEvents.GC_NAME,
JvmEvents.GC_MEMORY_SPACES,
JvmEvents.GC_COUNT,
JvmEvents.GC_TOTAL_TIME_MS,
};
static final String[] PRESET_TAG_TAG_V4 = new String[] {
"",
TK_PART_COUNTERS,
TK_PART_TAGS,
TK_PART_THREAD_DETAILS,
TK_PART_THREAD_STACK,
TK_PART_TIMESTAMP,
JvmEvents.EVENT_THREAD_SNAPSHOT,
JvmEvents.EVENT_GC,
JvmEvents.EVENT_STW
};
public static StackTraceWriter newWriter(OutputStream os) throws IOException {
return new StackTraceWriterV2(os);
}
public static StackTraceReader newReader(InputStream is) throws IOException {
return newReaderInternal(is);
}
public static StackTraceReader newReader(String... files) throws IOException {
return newReaderInternal(files);
}
public static StackTraceReader newEventReader(InputStream is) throws IOException {
return newReaderInternal(is);
}
public static StackTraceReader newEventReader(String... files) throws IOException {
return newReaderInternal(files);
}
private static StackTraceReader newReaderInternal(InputStream is) throws IOException {
DataInputStream dis = new DataInputStream(is);
byte[] magic = new byte[MAGIC.length];
dis.readFully(magic);
if (Arrays.equals(MAGIC, magic)) {
return new StackTraceReaderV1(is);
}
else if (Arrays.equals(MAGIC2, magic)) {
return new StackTraceReaderV2(is);
}
else if (Arrays.equals(MAGIC4, magic)) {
return new LegacyStackReader(ThreadEventCodec.createEventReader(magic, is));
}
else {
throw new IOException("Unknown magic [" + new String(magic) + "]");
}
}
private static StackTraceReader newReaderInternal(String... files) throws IOException {
final List fileList = new ArrayList(Arrays.asList(files));
return new ChainedStackTraceReader() {
@Override
protected StackTraceReader next() {
while(!fileList.isEmpty()) {
String file = fileList.remove(0);
File f = new File(file);
if (!f.isFile()) {
continue;
}
try {
FileInputStream fis = new FileInputStream(file);
return newReaderInternal(fis);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
return null;
}
};
}
public abstract static class ChainedStackTraceReader implements StackTraceReader {
private StackTraceReader current;
protected abstract StackTraceReader next();
@Override
public boolean isLoaded() {
if (current == null) {
current = next();
}
return current != null && current.isLoaded();
}
@Override
public long getThreadId() {
if (current == null) {
new NoSuchElementException();
}
return current.getThreadId();
}
@Override
public String getThreadName() {
if (current == null) {
new NoSuchElementException();
}
return current.getThreadName();
}
@Override
public long getTimestamp() {
if (current == null) {
new NoSuchElementException();
}
return current.getTimestamp();
}
@Override
public State getThreadState() {
if (current == null) {
new NoSuchElementException();
}
return current.getThreadState();
}
@Override
public CounterCollection getCounters() {
if (current == null) {
new NoSuchElementException();
}
return current.getCounters();
}
@Override
public StackTraceElement[] getTrace() {
if (current == null) {
new NoSuchElementException();
}
return current.getTrace();
}
@Override
public StackFrameList getStackTrace() {
if (current == null) {
new NoSuchElementException();
}
return current.getStackTrace();
}
@Override
public boolean loadNext() throws IOException {
if (current == null) {
current = next();
if (current == null) {
return false;
}
}
if (current.loadNext()) {
return true;
}
else {
current = null;
return loadNext();
}
}
}
static int readVarInt(DataInput dis) throws IOException {
int b = dis.readByte();
if ((b & 0x80) == 0) {
return 0x7F & b;
}
else {
int v = (0x7F & b);
b = dis.readByte();
v |= ((0x7F & b) << 7);
if ((b & 0x80) == 0) {
return v;
}
b = dis.readByte();
v |= ((0x7F & b) << 14);
if ((b & 0x80) == 0) {
return v;
}
b = dis.readByte();
v |= ((0xFF & b) << 21);
return v;
}
}
static long readVarLong(DataInput dis) throws IOException {
byte b = dis.readByte();
if ((b & 0x80) == 0) {
return 0x7F & b;
}
else {
long v = (0x7F & b);
b = dis.readByte();
v |= ((0x7Fl & b) << 7); // byte 2
if ((b & 0x80) == 0) {
return v;
}
b = dis.readByte();
v |= ((0x7Fl & b) << 14); // byte 3
if ((b & 0x80) == 0) {
return v;
}
b = dis.readByte();
v |= ((0x7Fl & b) << 21); // byte 4
if ((b & 0x80) == 0) {
return v;
}
b = dis.readByte();
v |= ((0x7Fl & b) << 28); // byte 5
if ((b & 0x80) == 0) {
return v;
}
b = dis.readByte();
v |= ((0x7Fl & b) << 35); // byte 6
if ((b & 0x80) == 0) {
return v;
}
b = dis.readByte();
v |= ((0x7Fl & b) << 42); // byte 7
if ((b & 0x80) == 0) {
return v;
}
b = dis.readByte();
v |= ((0x7Fl & b) << 49); // byte 8
if ((b & 0x80) == 0) {
return v;
}
b = dis.readByte();
v |= ((0xFFl & b) << 56); // byte 9
return v;
}
}
static long readTimestamp(DataInput dis) throws IOException {
return TIME_ANCHOR + readVarLong(dis);
}
static void writeVarInt(DataOutput dos, int v) throws IOException {
if (v < 0) {
throw new IllegalArgumentException("Out of bounds: " + v);
}
int val = v;
if ((val & 0xFFFFFF80) == 0) {
dos.write(val);
return;
}
dos.write(0x80 | (0x7F & val));
val >>= 7;
if ((val & 0xFFFFFF80) == 0) {
dos.write(val);
return;
}
dos.write(0x80 | (0x7F & val));
val >>= 7;
if ((val & 0xFFFFFF80) == 0) {
dos.write(val);
return;
}
dos.write(0x80 | (0x7F & val));
val >>= 7;
if ((val & 0xFFFFFF00) == 0) {
dos.write(val);
return;
}
else {
throw new IllegalArgumentException("Out of bounds: " + v);
}
}
static void writeVarLong(DataOutput dos, long v) throws IOException {
long val = v;
if ((val & 0xFFFFFFFFFFFFFF80l) == 0) {
dos.write((int)(0xFF & val));
return;
}
dos.write(0x80 | (int)(0x7F & val));
val >>>= 7;
if ((val & 0xFFFFFFFFFFFFFF80l) == 0) { // byte 2
dos.write((int)(0xFF & val));
return;
}
dos.write(0x80 | (int)(0x7F & val));
val >>>= 7;
if ((val & 0xFFFFFFFFFFFFFF80l) == 0) { // byte 3
dos.write((int)(0xFF & val));
return;
}
dos.write(0x80 | (int)(0x7F & val));
val >>>= 7;
if ((val & 0xFFFFFFFFFFFFFF80l) == 0) { // byte 4
dos.write((int)(0xFF & val));
return;
}
dos.write(0x80 | (int)(0x7F & val));
val >>>= 7;
if ((val & 0xFFFFFFFFFFFFFF80l) == 0) { // byte 5
dos.write((int)(0xFF & val));
return;
}
dos.write(0x80 | (int)(0x7F & val));
val >>>= 7;
if ((val & 0xFFFFFFFFFFFFFF80l) == 0) { // byte 6
dos.write((int)(0xFF & val));
return;
}
dos.write(0x80 | (int)(0x7F & val));
val >>>= 7;
if ((val & 0xFFFFFFFFFFFFFF80l) == 0) { // byte 7
dos.write((int)(0xFF & val));
return;
}
dos.write(0x80 | (int)(0x7F & val));
val >>>= 7;
if ((val & 0xFFFFFFFFFFFFFF80l) == 0) { // byte 8
dos.write((int)(0xFF & val));
return;
}
dos.write(0x80 | (int)(0x7F & val));
val >>>= 7;
if ((val & 0xFFFFFFFFFFFFFF00l) == 0) { // byte 9
dos.write((int)(0xFF & val));
return;
}
else {
throw new IllegalArgumentException("Out of bounds: " + v);
}
}
static void writeTimestamp(DataOutput dos, long epoch) throws IOException {
writeVarLong(dos, epoch - TIME_ANCHOR);
}
}