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<>();
private TraceJSON() {
}
/**
* 指定したJSONのトレースファイルを解読して Trace オブジェクトを生成する
* @param file トレースファイル
*/
public TraceJSON(BufferedReader file) {
try {
readJSON(file);
file.close();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 指定したJSONのトレースファイルを解読して Trace オブジェクトを生成する
* @param traceFile トレースファイルのパス
*/
public TraceJSON(String traceFile) {
BufferedReader file;
try {
file = new BufferedReader(new FileReader(traceFile));
readJSON(file);
file.close();
} catch (IOException e) {
e.printStackTrace();
}
}
private void readJSON(BufferedReader file) throws IOException {
// トレースファイル読み込み
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[] fieldData;
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 {
stack.pop();
thread.terminateMethod();
if (!stack.isEmpty()) line2 = stack.peek();
} while (!stack.isEmpty() && !line2.endsWith(shortSignature));
if (!stack.isEmpty()) stack.pop();
}
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.util.Collection")
|| thisClassName.contains("java.util.Arrays")
|| 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 {
stack.pop();
thread.terminateMethod();
line2 = stack.peek();
} while (!stack.isEmpty() && !line2.endsWith(shortSignature));
if (!stack.isEmpty()) stack.pop();
}
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(",\"fieldName\":\"");
fieldData = type[1].split("\",\"this\":");
thisObj = fieldData[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(fieldData[0], valueClassName, valueObjectId, containerClassName, containerObjectId, thisClassName, thisObjectId, lineNum, timeStamp);
} else if (line.startsWith("{\"type\":\"fieldSet\"")) {
// フィールド更新
type = line.split(",\"fieldName\":\"");
fieldData = type[1].split("\",\"container\":");
containerObj = fieldData[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(fieldData[0], 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);
}
}
}
/**
* クラス名とオブジェクト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;
}
/**
* オンライン解析用シングルトンの取得
* @return オンライン解析用トレース
*/
public static TraceJSON getInstance() {
if (theTrace == null) {
theTrace = new TraceJSON();
}
return (TraceJSON)theTrace;
}
/**
* スレッドIDを指定してスレッドインスタンスを取得する(オンライン解析用)
* @param threadId
* @return スレッドインスタンス
*/
public static ThreadInstance getThreadInstance(String threadId) {
return getInstance().threads.get(threadId);
}
/**
* 指定したスレッド上で現在実行中のメソッド実行を取得する
* @param thread 対象スレッド
* @return thread 上で現在実行中のメソッド実行
*/
public static MethodExecution getCurrentMethodExecution(Thread thread) {
ThreadInstance t = getInstance().threads.get(String.valueOf(thread.getId()));
return t.getCurrentMethodExecution();
}
/**
* 指定したスレッド上で現在実行中のトレースポイントを取得する
* @param thread 対象スレッド
* @return thread 上で現在実行中の実行文のトレースポイント
*/
public static TracePoint getCurrentTracePoint(Thread thread) {
ThreadInstance t = getInstance().threads.get(String.valueOf(thread.getId()));
return t.getCurrentTracePoint();
}
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;
}
/**
* 実行された全ブロックを取得する
* @return 全ブロック(メソッド名:ブロックID)
*/
public HashSet<String> getAllBlocks() {
final HashSet<String> blocks = new HashSet<String>();
Iterator<String> threadsIterator = threads.keySet().iterator();
for (; threadsIterator.hasNext();) {
ThreadInstance thread = threads.get(threadsIterator.next());
thread.traverseMethodExecutionsBackward(new IMethodExecutionVisitor() {
@Override
public boolean preVisitThread(ThreadInstance thread) {
return false;
}
@Override
public boolean postVisitThread(ThreadInstance thread) {
return false;
}
@Override
public boolean preVisitMethodExecution(MethodExecution methodExecution) {
for (Statement s: methodExecution.getStatements()) {
if (s instanceof BlockEnter) {
blocks.add(methodExecution.getSignature() + ":" + ((BlockEnter)s).getBlockId());
}
}
return false;
}
@Override
public boolean postVisitMethodExecution(MethodExecution methodExecution, ArrayList<MethodExecution> children) {
return false;
}
});
}
return blocks;
}
/**
* マーク内で実行が開始されたブロックを取得する
* @param markStart マークの開始時刻
* @param markEnd マークの終了時刻
* @return 該当するブロック(メソッド名:ブロックID)
*/
public HashSet<String> getMarkedBlocks(final long markStart, final long markEnd) {
final HashSet<String> blocks = new HashSet<String>();
Iterator<String> threadsIterator = threads.keySet().iterator();
for (; threadsIterator.hasNext();) {
ThreadInstance thread = threads.get(threadsIterator.next());
thread.traverseMethodExecutionsBackward(new IMethodExecutionVisitor() {
@Override
public boolean preVisitThread(ThreadInstance thread) {
return false;
}
@Override
public boolean postVisitThread(ThreadInstance thread) {
return false;
}
@Override
public boolean preVisitMethodExecution(MethodExecution methodExecution) {
if (!methodExecution.isTerminated() && methodExecution.getExitTime() < markStart) return true; // 探索終了
if (methodExecution.getEntryTime() > markEnd) return false;
for (Statement s: methodExecution.getStatements()) {
if (s instanceof BlockEnter) {
long entryTime = ((BlockEnter)s).getTimeStamp();
if (entryTime >= markStart && entryTime <= markEnd) {
blocks.add(methodExecution.getSignature() + ":" + ((BlockEnter)s).getBlockId());
}
}
}
return false;
}
@Override
public boolean postVisitMethodExecution(MethodExecution methodExecution, ArrayList<MethodExecution> children) {
return false;
}
});
}
return blocks;
}
/**
* 実行された全フローを取得する
* @return 全フロー(メソッド名:フロー元ブロックID:フロー先ブロックID)
*/
public HashSet<String> getAllFlows() {
final HashSet<String> flows = new HashSet<String>();
Iterator<String> threadsIterator = threads.keySet().iterator();
for (; threadsIterator.hasNext();) {
ThreadInstance thread = threads.get(threadsIterator.next());
thread.traverseMethodExecutionsBackward(new IMethodExecutionVisitor() {
@Override
public boolean preVisitThread(ThreadInstance thread) {
return false;
}
@Override
public boolean postVisitThread(ThreadInstance thread) {
return false;
}
@Override
public boolean preVisitMethodExecution(MethodExecution methodExecution) {
int prevBlockId = -1;
for (Statement s: methodExecution.getStatements()) {
if (s instanceof BlockEnter) {
int curBlockID = ((BlockEnter)s).getBlockId();
if (prevBlockId != -1) {
flows.add(methodExecution.getSignature() + ":" + prevBlockId + ":" + curBlockID);
} else {
flows.add(methodExecution.getSignature() + ":" + curBlockID);
}
prevBlockId = curBlockID;
}
}
return false;
}
@Override
public boolean postVisitMethodExecution(MethodExecution methodExecution, ArrayList<MethodExecution> children) {
return false;
}
});
}
return flows;
}
/**
* マーク内で実行されたフローを取得する
* @param markStart マークの開始時刻
* @param markEnd マークの終了時刻
* @return 該当するフロー(メソッド名:フロー元ブロックID:フロー先ブロックID)
*/
public HashSet<String> getMarkedFlows(final long markStart, final long markEnd) {
final HashSet<String> flows = new HashSet<String>();
Iterator<String> threadsIterator = threads.keySet().iterator();
for (; threadsIterator.hasNext();) {
ThreadInstance thread = threads.get(threadsIterator.next());
thread.traverseMethodExecutionsBackward(new IMethodExecutionVisitor() {
@Override
public boolean preVisitThread(ThreadInstance thread) {
return false;
}
@Override
public boolean postVisitThread(ThreadInstance thread) {
return false;
}
@Override
public boolean preVisitMethodExecution(MethodExecution methodExecution) {
if (!methodExecution.isTerminated() && methodExecution.getExitTime() < markStart) return true; // 探索終了
if (methodExecution.getEntryTime() > markEnd) return false;
int prevBlockId = -1;
for (Statement s: methodExecution.getStatements()) {
if (s instanceof BlockEnter) {
long entryTime = ((BlockEnter)s).getTimeStamp();
int curBlockID = ((BlockEnter)s).getBlockId();
if (entryTime >= markStart && entryTime <= markEnd) {
if (prevBlockId != -1) {
flows.add(methodExecution.getSignature() + ":" + prevBlockId + ":" + curBlockID);
} else {
flows.add(methodExecution.getSignature() + ":" + curBlockID);
}
}
prevBlockId = curBlockID;
}
}
return false;
}
@Override
public boolean postVisitMethodExecution(MethodExecution methodExecution, ArrayList<MethodExecution> children) {
return false;
}
});
}
return flows;
}
/**
* トレース内の全スレッドを同期させながら全実行文を逆方向に探索する
*
* @param visitor 実行文のビジター
* @return 中断したトレースポイント
*/
public TracePoint traverseStatementsInTraceBackward(IStatementVisitor visitor) {
HashMap<String, ArrayList<MethodExecution>> threadRoots = new HashMap<String, ArrayList<MethodExecution>>();
HashMap<String, TracePoint> threadLastPoints = new HashMap<String, TracePoint>();
// 各スレッドにおいて一番最後に開始したメソッド実行を探す
long traceLastTime = 0;
String traceLastThread = null;
long traceLastTime2 = 0;
String traceLastThread2 = null;
for (String threadId: threads.keySet()) {
ThreadInstance thread = threads.get(threadId);
ArrayList<MethodExecution> roots = (ArrayList<MethodExecution>)thread.getRoot().clone();
threadRoots.put(threadId, roots);
TracePoint threadLastTp;
do {
MethodExecution threadLastExecution = roots.remove(roots.size() - 1);
threadLastTp = threadLastExecution.getExitPoint();
} while (!threadLastTp.isValid() && roots.size() > 0);
if (threadLastTp.isValid()) {
threadLastPoints.put(threadId, threadLastTp);
long threadLastTime;
if (threadLastTp.getStatement() instanceof MethodInvocation) {
threadLastTime = ((MethodInvocation) threadLastTp.getStatement()).getCalledMethodExecution().getExitTime();
} else {
threadLastTime = threadLastTp.getStatement().getTimeStamp();
}
if (traceLastTime < threadLastTime) {
traceLastTime2 = traceLastTime;
traceLastThread2 = traceLastThread;
traceLastTime = threadLastTime;
traceLastThread = threadId;
} else if (traceLastTime2 < threadLastTime) {
traceLastTime2 = threadLastTime;
traceLastThread2 = threadId;
}
} else {
threadLastPoints.put(threadId, null);
}
}
return traverseStatementsInTraceBackwardSub(visitor, threadRoots, threadLastPoints, traceLastThread, traceLastThread2, traceLastTime2);
}
/**
* トレース内の全スレッドを同期させながら指定したトレースポイントから逆方向に探索する
*
* @param visitor 実行文のビジター
* @param before 探索開始トレースポイント
* @return 中断したトレースポイント
*/
public TracePoint traverseStatementsInTraceBackward(IStatementVisitor visitor, TracePoint before) {
if (before == null) {
return traverseStatementsInTraceBackward(visitor);
}
// 全てのスレッドのトレースポイントを before の前まで巻き戻す
HashMap<String, ArrayList<MethodExecution>> threadRoots = new HashMap<String, ArrayList<MethodExecution>>();
HashMap<String, TracePoint> threadLastPoints = new HashMap<String, TracePoint>();
String traceLastThread = null;
long traceLastTime2 = 0;
String traceLastThread2 = null;
Statement st = before.getStatement();
if (st == null) {
st = before.getMethodExecution().getCallerTracePoint().getStatement();
}
ThreadInstance thread = threads.get(st.getThreadNo());
for (String threadId: threads.keySet()) {
ThreadInstance t = threads.get(threadId);
ArrayList<MethodExecution> rootExecutions = (ArrayList<MethodExecution>)t.getRoot().clone();
threadRoots.put(threadId, rootExecutions);
if (t == thread) {
traceLastThread = threadId;
threadLastPoints.put(threadId, before);
for (int n = rootExecutions.size() - 1; n >= 0; n--) {
MethodExecution root = rootExecutions.get(n);
if (root.getEntryTime() > before.getStatement().getTimeStamp()) {
rootExecutions.remove(n);
} else {
break;
}
}
if (rootExecutions.size() > 0) {
rootExecutions.remove(rootExecutions.size() - 1);
}
} else {
TracePoint threadBeforeTp;
do {
MethodExecution threadLastExecution = rootExecutions.remove(rootExecutions.size() - 1);
threadBeforeTp = threadLastExecution.getExitPoint();
} while (!threadBeforeTp.isValid() && rootExecutions.size() > 0);
if (threadBeforeTp.isValid()) {
TracePoint[] tp = new TracePoint[] {threadBeforeTp};
long threadLastTime;
if (before.getStatement() instanceof MethodInvocation) {
threadLastTime = ((MethodInvocation) before.getStatement()).getCalledMethodExecution().getExitTime();
} else {
threadLastTime = before.getStatement().getTimeStamp();
}
getLastStatementInThread(rootExecutions, tp, threadLastTime, new IStatementVisitor() {
@Override
public boolean preVisitStatement(Statement statement) { return false; }
@Override
public boolean postVisitStatement(Statement statement) { return false; }
});
threadLastPoints.put(threadId, tp[0]);
if (tp[0] != null) {
if (tp[0].getStatement() instanceof MethodInvocation) {
threadLastTime = ((MethodInvocation) tp[0].getStatement()).getCalledMethodExecution().getExitTime(); // ※Exception が発生した場合は考えなくてよい?
} else {
threadLastTime = tp[0].getStatement().getTimeStamp();
}
if (traceLastTime2 < threadLastTime) {
traceLastTime2 = threadLastTime;
traceLastThread2 = threadId;
}
}
} else {
threadLastPoints.put(threadId, null);
}
}
}
return traverseStatementsInTraceBackwardSub(visitor, threadRoots, threadLastPoints, traceLastThread, traceLastThread2, traceLastTime2);
}
/**
* トレース内の全スレッドを同期させながら指定したトレースポイントから逆方向に探索する
*
* @param visitor 実行文のビジター
* @param before 探索開始時間
* @return 中断したトレースポイント
*/
public TracePoint traverseStatementsInTraceBackward(IStatementVisitor visitor, long before) {
if (before <= 0L) {
return traverseStatementsInTraceBackward(visitor);
}
// 全てのスレッドのトレースポイントを before の前まで巻き戻す
HashMap<String, ArrayList<MethodExecution>> threadRoots = new HashMap<String, ArrayList<MethodExecution>>();
HashMap<String, TracePoint> threadLastPoints = new HashMap<String, TracePoint>();
long traceLastTime = 0;
String traceLastThread = null;
long traceLastTime2 = 0;
String traceLastThread2 = null;
for (String threadId: threads.keySet()) {
ThreadInstance t = threads.get(threadId);
ArrayList<MethodExecution> rootExecutions = (ArrayList<MethodExecution>)t.getRoot().clone();
threadRoots.put(threadId, rootExecutions);
TracePoint threadBeforeTp;
do {
MethodExecution threadLastExecution = rootExecutions.remove(rootExecutions.size() - 1);
threadBeforeTp = threadLastExecution.getExitPoint();
} while (!threadBeforeTp.isValid() && rootExecutions.size() > 0);
if (threadBeforeTp.isValid()) {
TracePoint[] tp = new TracePoint[] {threadBeforeTp};
getLastStatementInThread(rootExecutions, tp, before, new IStatementVisitor() {
@Override
public boolean preVisitStatement(Statement statement) { return false; }
@Override
public boolean postVisitStatement(Statement statement) { return false; }
});
threadLastPoints.put(threadId, tp[0]);
if (tp[0] != null) {
long threadLastTime;
if (tp[0].getStatement() instanceof MethodInvocation) {
threadLastTime = ((MethodInvocation) tp[0].getStatement()).getCalledMethodExecution().getExitTime(); // ※Exception が発生した場合は考えなくてよい?
} else {
threadLastTime = tp[0].getStatement().getTimeStamp();
}
if (traceLastTime < threadLastTime) {
traceLastTime2 = traceLastTime;
traceLastThread2 = traceLastThread;
traceLastTime = threadLastTime;
traceLastThread = threadId;
} else if (traceLastTime2 < threadLastTime) {
traceLastTime2 = threadLastTime;
traceLastThread2 = threadId;
}
}
} else {
threadLastPoints.put(threadId, null);
}
}
return traverseStatementsInTraceBackwardSub(visitor, threadRoots, threadLastPoints, traceLastThread, traceLastThread2, traceLastTime2);
}
private TracePoint traverseStatementsInTraceBackwardSub(IStatementVisitor visitor,
HashMap<String, ArrayList<MethodExecution>> threadRoots,
HashMap<String, TracePoint> threadLastPoints,
String traceLastThread, String traceLastThread2, long traceLastTime2) {
// 全スレッドの同期をとりながら逆向きに実行文を探索する
for (;;) {
// 探索対象のスレッド内の逆向き探索
TracePoint lastTp = threadLastPoints.get(traceLastThread);
while (lastTp != null) {
Statement statement = lastTp.getStatement();
if (visitor.preVisitStatement(statement)) return lastTp;
if (statement instanceof MethodInvocation) {
// 呼び出し先がある場合、呼び出し先に潜る
lastTp.stepBackNoReturn();
if (lastTp.isValid()) {
// 普通に呼び出し先に移った場合
MethodExecution methodExecution = ((MethodInvocation) statement).getCalledMethodExecution();
if (!methodExecution.isTerminated() && methodExecution.getExitTime() < traceLastTime2) {
break;
}
continue;
}
// 空のメソッド実行の場合
} else {
if (visitor.postVisitStatement(statement)) return lastTp;
}
if (lastTp.isValid() && lastTp.getStatement().getTimeStamp() < traceLastTime2) break;
// 1ステップ巻き戻す
while (!lastTp.stepBackOver() && lastTp.isValid()) {
// 呼び出し元に戻った場合(メソッド呼び出し文に復帰)
statement = lastTp.getStatement();
if (visitor.postVisitStatement(statement)) return lastTp;
}
if (!lastTp.isValid()) {
// 呼び出し木の開始時点まで探索し終えた場合
ArrayList<MethodExecution> roots = threadRoots.get(traceLastThread);
while (!lastTp.isValid() && roots.size() > 0) {
// 次の呼び出し木を探す
MethodExecution lastExecution = roots.remove(roots.size() - 1);
lastTp = lastExecution.getExitPoint();
}
if (lastTp.isValid()) {
// 次の呼び出し木があればそれを最後から探索
threadLastPoints.put(traceLastThread, lastTp);
if (lastTp.getStatement().getTimeStamp() < traceLastTime2) break;
} else {
// そのスレッドの探索がすべて終了した場合
threadLastPoints.put(traceLastThread, null);
break;
}
}
}
traceLastThread = traceLastThread2;
// 次の次に探索すべきスレッド(未探索の領域が一番最後まで残っているスレッド)を決定する
traceLastTime2 = 0;
traceLastThread2 = null;
boolean continueTraverse = false;
for (String threadId: threadLastPoints.keySet()) {
if (!threadId.equals(traceLastThread)) {
TracePoint threadLastTp = threadLastPoints.get(threadId);
if (threadLastTp != null) {
continueTraverse = true;
long threadLastTime;
if (threadLastTp.getStatement() instanceof MethodInvocation) {
threadLastTime = ((MethodInvocation) threadLastTp.getStatement()).getCalledMethodExecution().getExitTime();
} else {
threadLastTime = threadLastTp.getStatement().getTimeStamp();
}
if (traceLastTime2 < threadLastTime) {
traceLastTime2 = threadLastTime;
traceLastThread2 = threadId;
}
}
}
}
if (traceLastThread == null && traceLastThread2 == null) break;
if (!continueTraverse && threadLastPoints.get(traceLastThread) == null) break;
}
return null;
}
}