package org.ntlab.trace;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Stack;
public class TraceJSON extends Trace {
private HashMap<String, ClassInfo> classes = new HashMap<>();
/**
* 指定したJSONトレースファイルを解読して Trace オブジェクトを生成する
* @param traceFile トレースファイルのパス
*/
public TraceJSON(String traceFile) {
super();
BufferedReader file;
try {
file = new BufferedReader(new FileReader(traceFile));
// トレースファイル読み込み
String line = null;
String[] type;
String[] classNameData;
String[] pathData;
String[] signature;
String[] receiver;
String[] arguments;
String[] lineData;
String[] threadId;
String[] thisObj;
String[] containerObj;
String[] valueObj;
String[] returnValue;
String[] arrayObj;
String[] thisData;
String[] containerData;
String[] valueData;
String[] returnData;
String[] arrayData;
String[] blockIdData;
String[] incomingsData;
String[] dimensionData;
String[] indexData;
String className;
String classPath;
String loaderPath;
String time;
String thisObjectId;
String thisClassName;
String containerObjectId;
String containerClassName;
String valueObjectId;
String valueClassName;
String returnClassName;
String returnObjectId;
String arrayObjectId;
String arrayClassName;
String shortSignature;
boolean isConstractor = false;
boolean isCollectionType = false;
boolean isStatic = false;
int dimension;
int index;
int blockId;
int incomings;
int lineNum;
long timeStamp = 0L;
ThreadInstance thread = null;
HashMap<String, Stack<String>> stacks = new HashMap<String, Stack<String>>();
while ((line = file.readLine()) != null) {
// トレースファイルの解析
if (line.startsWith("{\"type\":\"classDef\"")) {
// クラス定義
type = line.split(",\"name\":\"");
classNameData = type[1].split("\",\"path\":\"");
className = classNameData[0];
pathData = classNameData[1].split("\",\"loaderPath\":\"");
classPath = pathData[0].substring(1); // 先頭の / を取り除く
loaderPath = pathData[1].substring(1, pathData[1].length() - 3); // 先頭の / と、末尾の "}, を取り除く
initializeClass(className, classPath, loaderPath);
} else if (line.startsWith("{\"type\":\"methodCall\"")) {
// メソッド呼び出しの呼び出し側
type = line.split(",\"callerSideSignature\":\"");
signature = type[1].split("\",\"threadId\":");
threadId = signature[1].split(",\"lineNum\":");
lineNum = Integer.parseInt(threadId[1].substring(0, threadId[1].length() - 2)); // 末尾の }, を取り除く
thread = threads.get(threadId[0]);
thread.preCallMethod(signature[0], lineNum);
} else if (line.startsWith("{\"type\":\"methodEntry\"")) {
// メソッド呼び出し
type = line.split("\"signature\":\"");
signature = type[1].split("\",\"receiver\":");
receiver = signature[1].split(",\"args\":");
arguments = receiver[1].split(",\"threadId\":");
threadId = arguments[1].split(",\"time\":");
thisData = parseClassNameAndObjectId(receiver[0]);
thisClassName = thisData[0];
thisObjectId = thisData[1];
isConstractor = false;
isStatic = false;
if (signature[0].contains("static ")) {
isStatic = true;
}
thread = threads.get(threadId[0]);
time = threadId[1].substring(0, threadId[1].length() - 2); // 末尾の }, を取り除く
timeStamp = Long.parseLong(time);
Stack<String> stack;
if (thread == null) {
thread = new ThreadInstance(threadId[0]);
threads.put(threadId[0], thread);
stack = new Stack<String>();
stacks.put(threadId[0], stack);
} else {
stack = stacks.get(threadId[0]);
}
stack.push(signature[0]);
// メソッド呼び出しの設定
thread.callMethod(signature[0], null, thisClassName, thisObjectId, isConstractor, isStatic, timeStamp);
// 引数の設定
thread.setArgments(parseArguments(arguments));
} else if (line.startsWith("{\"type\":\"constructorEntry\"")) {
// コンストラクタ呼び出し
type = line.split("\"signature\":\"");
signature = type[1].split("\",\"class\":\"");
receiver = signature[1].split("\",\"args\":");
arguments = receiver[1].split(",\"threadId\":");
threadId = arguments[1].split(",\"time\":");
thisClassName = receiver[0];
thisObjectId = "0";
isConstractor = true;
isStatic = false;
thread = threads.get(threadId[0]);
time = threadId[1].substring(0, threadId[1].length() - 2); // 末尾の }, を取り除く
timeStamp = Long.parseLong(time);
Stack<String> stack;
if (thread == null) {
thread = new ThreadInstance(threadId[0]);
threads.put(threadId[0], thread);
stack = new Stack<String>();
stacks.put(threadId[0], stack);
} else {
stack = stacks.get(threadId[0]);
}
stack.push(signature[0]);
// メソッド呼び出しの設定
thread.callMethod(signature[0], null, thisClassName, thisObjectId, isConstractor, isStatic, timeStamp);
// 引数の設定
thread.setArgments(parseArguments(arguments));
} else if (line.startsWith("{\"type\":\"methodExit\"")) {
// メソッドからの復帰
type = line.split(",\"shortSignature\":\"");
signature = type[1].split("\",\"receiver\":");
receiver = signature[1].split(",\"returnValue\":");
returnValue = receiver[1].split(",\"threadId\":");
threadId = returnValue[1].split(",\"time\":");
thisData = parseClassNameAndObjectId(receiver[0]);
thisClassName = thisData[0];
thisObjectId = thisData[1];
returnData = parseClassNameAndObjectId(returnValue[0]);
returnClassName = returnData[0];
returnObjectId = returnData[1];
shortSignature = signature[0];
time = threadId[1].substring(0, threadId[1].length() - 2); // 末尾の }, を取り除く
timeStamp = Long.parseLong(time);
Stack<String> stack = stacks.get(threadId[0]);
if (!stack.isEmpty()) {
String line2 = stack.peek();
if (line2.endsWith(shortSignature)) {
stack.pop();
} else {
do {
line2 = stack.pop();
thread.terminateMethod();
} while (!stack.isEmpty() && !line2.endsWith(shortSignature));
}
thread = threads.get(threadId[0]);
ObjectReference returnVal = new ObjectReference(returnObjectId, returnClassName);
isCollectionType = false;
if(thisClassName.contains("java.util.List")
|| thisClassName.contains("java.util.Vector")
|| thisClassName.contains("java.util.Iterator")
|| thisClassName.contains("java.util.ListIterator")
|| thisClassName.contains("java.util.ArrayList")
|| thisClassName.contains("java.util.Stack")
|| thisClassName.contains("java.util.Hash")
|| thisClassName.contains("java.util.Map")
|| thisClassName.contains("java.util.Set")
|| thisClassName.contains("java.util.Linked")
|| thisClassName.contains("java.lang.Thread")) {
isCollectionType = true;
}
// メソッドからの復帰の設定
thread.returnMethod(returnVal, thisObjectId, isCollectionType, timeStamp);
}
} else if (line.startsWith("{\"type\":\"constructorExit\"")) {
// コンストラクタからの復帰
type = line.split(",\"shortSignature\":\"");
signature = type[1].split("\",\"returnValue\":");
returnValue = signature[1].split(",\"threadId\":");
threadId = returnValue[1].split(",\"time\":");
returnData = parseClassNameAndObjectId(returnValue[0]);
thisClassName = returnClassName = returnData[0];
thisObjectId = returnObjectId = returnData[1];
time = threadId[1].substring(0, threadId[1].length() - 2); // 末尾の }, を取り除く
timeStamp = Long.parseLong(time);
Stack<String> stack = stacks.get(threadId[0]);
shortSignature = signature[0];
if (!stack.isEmpty()) {
String line2 = stack.peek();
if (line2.endsWith(shortSignature)) {
stack.pop();
} else {
do {
line2 = stack.pop();
thread.terminateMethod();
} while (!stack.isEmpty() && !line2.endsWith(shortSignature));
}
thread = threads.get(threadId[0]);
ObjectReference returnVal = new ObjectReference(returnObjectId, returnClassName);
isCollectionType = false;
if(thisClassName.contains("java.util.List")
|| thisClassName.contains("java.util.Vector")
|| thisClassName.contains("java.util.Iterator")
|| thisClassName.contains("java.util.ListIterator")
|| thisClassName.contains("java.util.ArrayList")
|| thisClassName.contains("java.util.Stack")
|| thisClassName.contains("java.util.Hash")
|| thisClassName.contains("java.util.Map")
|| thisClassName.contains("java.util.Set")
|| thisClassName.contains("java.util.Linked")
|| thisClassName.contains("java.lang.Thread")) {
isCollectionType = true;
}
// メソッドからの復帰の設定
thread.returnMethod(returnVal, thisObjectId, isCollectionType, timeStamp);
}
} else if (line.startsWith("{\"type\":\"fieldGet\"")) {
// フィールドアクセス
type = line.split("\"this\":");
thisObj = type[1].split(",\"container\":");
containerObj = thisObj[1].split(",\"value\":");
valueObj = containerObj[1].split(",\"threadId\":");
threadId = valueObj[1].split(",\"lineNum\":");
lineData = threadId[1].split(",\"time\":");
thisData = parseClassNameAndObjectId(thisObj[0]);
thisClassName = thisData[0];
thisObjectId = thisData[1];
containerData = parseClassNameAndObjectId(containerObj[0]);
containerClassName = containerData[0];
containerObjectId = containerData[1];
valueData = parseClassNameAndObjectId(valueObj[0]);
valueClassName = valueData[0];
valueObjectId = valueData[1];
thread = threads.get(threadId[0]);
lineNum = Integer.parseInt(lineData[0]);
time = lineData[1].substring(0, lineData[1].length() - 2); // 末尾の }, を取り除く
timeStamp = Long.parseLong(time);
// フィールドアクセスの設定
if (thread != null) thread.fieldAccess(valueClassName, valueObjectId, containerClassName, containerObjectId, thisClassName, thisObjectId, lineNum, timeStamp);
} else if (line.startsWith("{\"type\":\"fieldSet\"")) {
// フィールド更新
type = line.split(",\"container\":");
containerObj = type[1].split(",\"value\":");
valueObj = containerObj[1].split(",\"threadId\":");
threadId = valueObj[1].split(",\"lineNum\":");
lineData = threadId[1].split(",\"time\":");
containerData = parseClassNameAndObjectId(containerObj[0]);
containerClassName = containerData[0];
containerObjectId = containerData[1];
valueData = parseClassNameAndObjectId(valueObj[0]);
valueClassName = valueData[0];
valueObjectId = valueData[1];
thread = threads.get(threadId[0]);
lineNum = Integer.parseInt(lineData[0]);
time = lineData[1].substring(0, lineData[1].length() - 2); // 末尾の }, を取り除く
timeStamp = Long.parseLong(time);
// フィールド更新の設定
if (thread != null) thread.fieldUpdate(valueClassName, valueObjectId, containerClassName, containerObjectId, lineNum, timeStamp);
} else if (line.startsWith("{\"type\":\"arrayCreate\"")) {
// 配列生成
type = line.split(",\"array\":");
arrayObj = type[1].split(",\"dimension\":");
arrayData = parseClassNameAndObjectId(arrayObj[0]);
arrayClassName = arrayData[0];
arrayObjectId = arrayData[1];
dimensionData = arrayObj[1].split(",\"threadId\":");
dimension = Integer.parseInt(dimensionData[0]);
threadId = dimensionData[1].split(",\"lineNum\":");
thread = threads.get(threadId[0]);
lineData = threadId[1].split(",\"time\":");
lineNum = Integer.parseInt(lineData[0]);
time = lineData[1].substring(0, lineData[1].length() - 2); // 末尾の }, を取り除く
timeStamp = Long.parseLong(time);
if (thread != null) thread.arrayCreate(arrayClassName, arrayObjectId, dimension, lineNum, timeStamp);
} else if (line.startsWith("{\"type\":\"arraySet\"")) {
// 配列要素への代入
type = line.split(",\"array\":");
arrayObj = type[1].split(",\"index\":");
arrayData = parseClassNameAndObjectId(arrayObj[0]);
arrayClassName = arrayData[0];
arrayObjectId = arrayData[1];
indexData = arrayObj[1].split(",\"value\":");
index = Integer.parseInt(indexData[0]);
valueObj = indexData[1].split(",\"threadId\":");
valueData = parseClassNameAndObjectId(valueObj[0]);
valueClassName = valueData[0];
valueObjectId = valueData[1];
threadId = valueObj[1].split(",\"time\":");
thread = threads.get(threadId[0]);
time = threadId[1].substring(0, threadId[1].length() - 2); // 末尾の }, を取り除く
timeStamp = Long.parseLong(time);
if (thread != null) thread.arraySet(arrayClassName, arrayObjectId, index, valueClassName, valueObjectId, 0, timeStamp);
} else if (line.startsWith("{\"type\":\"arrayGet\"")) {
// 配列要素の参照
type = line.split(",\"array\":");
arrayObj = type[1].split(",\"index\":");
arrayData = parseClassNameAndObjectId(arrayObj[0]);
arrayClassName = arrayData[0];
arrayObjectId = arrayData[1];
indexData = arrayObj[1].split(",\"value\":");
index = Integer.parseInt(indexData[0]);
valueObj = indexData[1].split(",\"threadId\":");
valueData = parseClassNameAndObjectId(valueObj[0]);
valueClassName = valueData[0];
valueObjectId = valueData[1];
threadId = valueObj[1].split(",\"time\":");
thread = threads.get(threadId[0]);
time = threadId[1].substring(0, threadId[1].length() - 2); // 末尾の }, を取り除く
timeStamp = Long.parseLong(time);
if (thread != null) thread.arrayGet(arrayClassName, arrayObjectId, index, valueClassName, valueObjectId, 0, timeStamp);
} else if (line.startsWith("{\"type\":\"blockEntry\"")) {
// ブロックの開始
type = line.split(",\"methodSignature\":\"");
signature = type[1].split("\",\"blockId\":");
blockIdData = signature[1].split(",\"incomings\":");
blockId = Integer.parseInt(blockIdData[0]);
incomingsData = blockIdData[1].split(",\"threadId\":");
incomings = Integer.parseInt(incomingsData[0]);
threadId = incomingsData[1].split(",\"lineNum\":");
thread = threads.get(threadId[0]);
lineData = threadId[1].split(",\"time\":");
lineNum = Integer.parseInt(lineData[0]);
time = lineData[1].substring(0, lineData[1].length() - 2); // 末尾の }, を取り除く
timeStamp = Long.parseLong(time);
if (thread != null) thread.blockEnter(blockId, incomings, lineNum, timeStamp);
}
}
file.close();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* クラス名とオブジェクトIDを表すJSONオブジェクトを解読する
* @param classNameAndObjectIdJSON トレースファイル内のJSONオブジェクト
* @return
*/
protected String[] parseClassNameAndObjectId(String classNameAndObjectIdJSON) {
// 先頭の {"class":" の10文字と末尾の } を取り除いて分離
return classNameAndObjectIdJSON.substring(10, classNameAndObjectIdJSON.length() - 1).split("\",\"id\":");
}
/**
* 引数を表すJSON配列を解読する
* @param arguments
* @return
*/
protected ArrayList<ObjectReference> parseArguments(String[] arguments) {
String[] argData;
argData = arguments[0].substring(1, arguments[0].length() - 1).split(","); // 先頭の [ と末尾の ] を取り除く
ArrayList<ObjectReference> argumentsData = new ArrayList<ObjectReference>();
for (int k = 0; k < argData.length - 1; k += 2) {
argumentsData.add(new ObjectReference(argData[k+1].substring(5, argData[k+1].length() - 1), argData[k].substring(10, argData[k].length() - 1)));
}
return argumentsData;
}
public void initializeClass(String name, String path, String loaderPath) {
classes.put(name, new ClassInfo(name, path, loaderPath));
}
public ClassInfo getClassInfo(String className) {
return classes.get(className);
}
public TracePoint getArraySetTracePoint(final Reference ref, TracePoint before) {
final TracePoint start = before.duplicate();
before = traverseStatementsInTraceBackward(new IStatementVisitor() {
@Override
public boolean preVisitStatement(Statement statement) {
if (statement instanceof ArrayUpdate) {
ArrayUpdate arraySet = (ArrayUpdate)start.getStatement();
String srcObjId = ref.getSrcObjectId();
String dstObjId = ref.getDstObjectId();
String srcClassName = ref.getSrcClassName();
String dstClassName = ref.getDstClassName();
if ((srcObjId != null && srcObjId.equals(arraySet.getArrayObjectId()))
|| (srcObjId == null || isNull(srcObjId)) && srcClassName.equals(arraySet.getArrayClassName())) {
if ((dstObjId != null && dstObjId.equals(arraySet.getValueObjectId()))
|| ((dstObjId == null || isNull(dstObjId)) && dstClassName.equals(arraySet.getValueClassName()))) {
if (srcObjId == null) {
ref.setSrcObjectId(arraySet.getArrayObjectId());
} else if (srcClassName == null) {
ref.setSrcClassName(arraySet.getArrayClassName());
}
if (dstObjId == null) {
ref.setDstObjectId(arraySet.getValueObjectId());
} else if (dstClassName == null) {
ref.setDstClassName(arraySet.getValueClassName());
}
return true;
}
}
}
return false;
}
@Override
public boolean postVisitStatement(Statement statement) { return false; }
}, start);
if (before != null) {
return before;
}
return null;
}
}