package org.ntlab.traceDebugger;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.core.filebuffers.FileBuffers;
import org.eclipse.core.filebuffers.ITextFileBuffer;
import org.eclipse.core.filebuffers.ITextFileBufferManager;
import org.eclipse.core.filebuffers.LocationKind;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.dialogs.InputDialog;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IRegion;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.FileDialog;
import org.eclipse.swt.widgets.Shell;
import org.ntlab.traceAnalysisPlatform.tracer.trace.MethodExecution;
import org.ntlab.traceAnalysisPlatform.tracer.trace.TraceJSON;
import org.ntlab.traceAnalysisPlatform.tracer.trace.TracePoint;
import org.ntlab.traceDebugger.analyzerProvider.DeltaExtractionAnalyzer;
import org.ntlab.traceDebugger.analyzerProvider.DeltaMarkerManager;
import org.ntlab.traceDebugger.analyzerProvider.VariableUpdatePointFinder;
public class DebuggingController {
private static final DebuggingController theInstance = new DebuggingController();
private TracePoint debuggingTp;
private TraceBreakPoint selectedTraceBreakPoint;
private TraceBreakPoints traceBreakPoints;
private List<IMarker> currentLineMarkers = new ArrayList<>();
private LoadingTraceFileStatus loadingTraceFileStatus = LoadingTraceFileStatus.NOT_YET;
private boolean isRunning = false;
public static final String CURRENT_MARKER_ID = "org.ntlab.traceDebugger.currentMarker";
private enum LoadingTraceFileStatus {
NOT_YET, PROGRESS, DONE
}
private DebuggingController() {
}
public static DebuggingController getInstance() {
return theInstance;
}
public void setTraceBreakPoints(TraceBreakPoints traceBreakPoints) {
this.traceBreakPoints = traceBreakPoints;
}
public void setDebuggingTp(TracePoint tp) {
this.debuggingTp = tp;
}
public void setSelectedTraceBreakPoint(TraceBreakPoint tbp) {
this.selectedTraceBreakPoint = tbp;
}
public TracePoint getCurrentTp() {
return debuggingTp.duplicate();
}
public boolean isLoadingTraceFile() {
return (loadingTraceFileStatus == LoadingTraceFileStatus.PROGRESS);
}
public boolean hasLoadedTraceFile() {
return (loadingTraceFileStatus == LoadingTraceFileStatus.DONE);
}
public void setLodingTraceFile() {
loadingTraceFileStatus = LoadingTraceFileStatus.PROGRESS;
}
public void setHasLoadedTraceFile() {
loadingTraceFileStatus = LoadingTraceFileStatus.DONE;
}
public void setHasNotLoadedTraceFile() {
loadingTraceFileStatus = LoadingTraceFileStatus.NOT_YET;
}
public boolean isRunning() {
return isRunning;
}
public boolean addTraceBreakPointAction() {
if (loadingTraceFileStatus != LoadingTraceFileStatus.DONE) {
if (TraceDebuggerPlugin.isJapanese()) {
MessageDialog.openInformation(null, "エラー", "トレースが見つかりませんでした");
} else {
MessageDialog.openInformation(null, "Error", "Trace was not found");
}
return false;
}
InputDialog inputDialog = TraceDebuggerPlugin.isJapanese() ? new InputDialog(null, "メソッドダイアログ", "メソッドシグニチャを入力", "", null)
: new InputDialog(null, "method signature dialog", "Input method signature", "", null);
if (inputDialog.open() != InputDialog.OK) return false;
String methodSignature = inputDialog.getValue();
inputDialog = TraceDebuggerPlugin.isJapanese() ? new InputDialog(null, "行数ダイアログ", "行数を入力", "", null)
: new InputDialog(null, "line Number dialog", "Input line number", "", null);
if (inputDialog.open() != InputDialog.OK) return false;
int lineNo = Integer.parseInt(inputDialog.getValue());
boolean isSuccess = traceBreakPoints.addTraceBreakPoint(methodSignature, lineNo);
if (!isSuccess) {
if (TraceDebuggerPlugin.isJapanese()) {
MessageDialog.openInformation(null, "エラー", "トレース中に入力したポイントが存在しません");
} else {
MessageDialog.openInformation(null, "Error", "This point does not exist in the trace.");
}
return false;
}
((BreakPointView)TraceDebuggerPlugin.getActiveView(BreakPointView.ID)).updateTraceBreakPoints(traceBreakPoints);
return true;
}
public boolean importBreakpointAction() {
if (loadingTraceFileStatus != LoadingTraceFileStatus.DONE) {
if (TraceDebuggerPlugin.isJapanese()) {
MessageDialog.openInformation(null, "エラー", "トレースが見つかりません");
} else {
MessageDialog.openInformation(null, "Error", "Trace was not found");
}
return false;
}
traceBreakPoints.importBreakpointFromEclipse();
((BreakPointView)TraceDebuggerPlugin.getActiveView(BreakPointView.ID)).updateTraceBreakPoints(traceBreakPoints);
return true;
}
public boolean removeTraceBreakPointAction() {
if (selectedTraceBreakPoint == null) return false;
traceBreakPoints.removeTraceBreakPoint(selectedTraceBreakPoint);
((BreakPointView)TraceDebuggerPlugin.getActiveView(BreakPointView.ID)).updateTraceBreakPoints(traceBreakPoints);
return true;
}
public boolean debugAction() {
if (loadingTraceFileStatus != LoadingTraceFileStatus.DONE) {
if (TraceDebuggerPlugin.isJapanese()) {
MessageDialog.openInformation(null, "エラー", "トレースが見つかりません");
} else {
MessageDialog.openInformation(null, "Error", "Trace was not found");
}
return false;
}
if (isRunning) {
if (TraceDebuggerPlugin.isJapanese()) {
MessageDialog.openInformation(null, "エラー", "トレース上で実行中です");
} else {
MessageDialog.openInformation(null, "Error", "This Debugger is running on the trace.");
}
return false;
}
debuggingTp = traceBreakPoints.getFirstTracePoint();
if (debuggingTp == null) {
if (TraceDebuggerPlugin.isJapanese()) {
MessageDialog.openInformation(null, "エラー", "利用可能なブレークポイントが見つかりません");
} else {
MessageDialog.openInformation(null, "Error", "An available breakpoint was not found");
}
return false;
}
refresh(null, debuggingTp, false);
((BreakPointView)TraceDebuggerPlugin.getActiveView(BreakPointView.ID)).updateImagesForDebug(true);
isRunning = true;
return true;
}
public void terminateAction() {
debuggingTp = null;
if (!(currentLineMarkers.isEmpty())) {
for (IMarker currentLineMarker : currentLineMarkers) {
try {
currentLineMarker.delete();
} catch (CoreException e) {
e.printStackTrace();
}
}
}
CallStackView callStackView = (CallStackView)TraceDebuggerPlugin.getActiveView(CallStackView.ID);
if (callStackView != null) callStackView.reset();
VariableView variableView = (VariableView)TraceDebuggerPlugin.getActiveView(VariableView.ID);
if (variableView != null) variableView.reset();
BreakPointView breakPointView = (BreakPointView)TraceDebuggerPlugin.getActiveView(BreakPointView.ID);
if (breakPointView != null) breakPointView.updateImagesForDebug(false);
isRunning = false;
}
public boolean stepIntoAction() {
if (!isRunning) return false;
if (debuggingTp == null) return false;
TracePoint callStackTp = getTracePointSelectedOnCallStack();
if (callStackTp != null && !(callStackTp.equals(debuggingTp))) {
debuggingTp = callStackTp;
}
TracePoint previousTp = debuggingTp;
debuggingTp = debuggingTp.duplicate();
debuggingTp.stepFull();
if (debugExecutionIsTerminated(debuggingTp)) return false;
refresh(null, debuggingTp, false);
return true;
}
public boolean stepOverAction() {
if (!isRunning) return false;
if (debuggingTp == null) return false;
TracePoint callStackTp = getTracePointSelectedOnCallStack();
if (callStackTp != null && !(callStackTp.equals(debuggingTp))) {
debuggingTp = callStackTp;
}
TracePoint previousTp = debuggingTp;
debuggingTp = debuggingTp.duplicate();
int currentLineNo = debuggingTp.getStatement().getLineNo();
boolean isReturned = false;
while (!(isReturned = !(debuggingTp.stepOver()))) {
if (currentLineNo != debuggingTp.getStatement().getLineNo()) break;
previousTp = debuggingTp.duplicate();
}
if (isReturned) {
while (!debuggingTp.stepOver()); // 呼び出し元での次のステートメントまで進める
}
if (debugExecutionIsTerminated(debuggingTp)) return false;
refresh(previousTp, debuggingTp, isReturned, true);
return true;
}
public boolean stepReturnAction() {
if (!isRunning) return false;
if (debuggingTp == null) return false;
TracePoint callStackTp = getTracePointSelectedOnCallStack();
if (callStackTp != null && !(callStackTp.equals(debuggingTp))) {
debuggingTp = callStackTp;
}
TracePoint previousTp = debuggingTp;
debuggingTp = debuggingTp.duplicate();
// note: 呼び出し元に戻るまで進み続ける
while (debuggingTp.stepOver()) {
previousTp = debuggingTp.duplicate();
}
while (!debuggingTp.stepOver()); // 呼び出し元での次のステートメントまで進める
if (debugExecutionIsTerminated(debuggingTp)) return false;
refresh(previousTp, debuggingTp, true);
return true;
}
public boolean stepNextAction() {
if (!isRunning) return false;
if (debuggingTp == null) return false;
TracePoint callStackTp = getTracePointSelectedOnCallStack();
if (callStackTp != null && !(callStackTp.equals(debuggingTp))) {
debuggingTp = callStackTp;
}
TracePoint previousTp = debuggingTp;
debuggingTp = debuggingTp.duplicate();
boolean isReturned = !(debuggingTp.stepNext());
if (isReturned) {
while (!debuggingTp.stepOver()); // 呼び出し元での次のステートメントまで進める
}
if (debugExecutionIsTerminated(debuggingTp)) return false;
refresh(previousTp, debuggingTp, isReturned, true);
return true;
}
public boolean resumeAction() {
if (!isRunning) return false;
if (debuggingTp == null) return false;
long currentTime = debuggingTp.getStatement().getTimeStamp();
TracePoint previousTp = debuggingTp;
debuggingTp = traceBreakPoints.getNextTracePoint(currentTime);
if (debugExecutionIsTerminated(debuggingTp)) return false;
refresh(null, debuggingTp, false);
return true;
}
public boolean stepBackIntoAction() {
if (!isRunning) return false;
if (debuggingTp == null) return false;
TracePoint callStackTp = getTracePointSelectedOnCallStack();
if (callStackTp != null && !(callStackTp.equals(debuggingTp))) {
debuggingTp = callStackTp;
}
TracePoint previousTp = debuggingTp;
debuggingTp = debuggingTp.duplicate();
debuggingTp.stepBackFull();
if (debugExecutionIsTerminated(debuggingTp)) return false;
refresh(null, debuggingTp, true);
return true;
}
public boolean stepBackOverAction() {
if (!isRunning) return false;
if (debuggingTp == null) return false;
TracePoint callStackTp = getTracePointSelectedOnCallStack();
if (callStackTp != null && !(callStackTp.equals(debuggingTp))) {
debuggingTp = callStackTp;
}
TracePoint previousTp = debuggingTp;
debuggingTp = debuggingTp.duplicate();
int currentLineNo = debuggingTp.getStatement().getLineNo();
boolean isReturned;
while (!(isReturned = !debuggingTp.stepBackOver())) {
if (currentLineNo != debuggingTp.getStatement().getLineNo()) break;
}
if (debugExecutionIsTerminated(debuggingTp)) return false;
refresh(null, debuggingTp, !isReturned);
return true;
}
public boolean stepBackReturnAction() {
if (!isRunning) return false;
if (debuggingTp == null) return false;
TracePoint callStackTp = getTracePointSelectedOnCallStack();
if (callStackTp != null && !(callStackTp.equals(debuggingTp))) {
debuggingTp = callStackTp;
}
TracePoint previousTp = debuggingTp;
debuggingTp = debuggingTp.duplicate();
while (debuggingTp.stepBackOver());
if (debugExecutionIsTerminated(debuggingTp)) return false;
refresh(null, debuggingTp, false);
return true;
}
public boolean backResumeAction() {
if (!isRunning) return false;
if (debuggingTp == null) return false;
TracePoint previousTp = debuggingTp;
long currentTime = debuggingTp.getStatement().getTimeStamp();
debuggingTp = traceBreakPoints.getPreviousTracePoint(currentTime);
if (debugExecutionIsTerminated(debuggingTp)) return false;
refresh(null, debuggingTp, false);
return true;
}
private boolean debugExecutionIsTerminated(TracePoint tp) {
if (tp == null || !(tp.isValid())) {
terminateAction();
if (TraceDebuggerPlugin.isJapanese()) {
MessageDialog.openInformation(null, "終了", "トレース上での実行は終了しました");
} else {
MessageDialog.openInformation(null, "Terminate", "This execution is terminated");
}
return true;
}
return false;
}
/**
* 現在のデバッグ位置を指定したトレースポイントに合わせる
* @return
*/
public boolean jumpToTheTracePoint(TracePoint tp, boolean isReturned) {
if (!isRunning) return false;
if (tp == null) return false;
TracePoint previousTp = debuggingTp;
debuggingTp = tp.duplicate();
refresh(null, debuggingTp, isReturned);
return true;
}
private void refresh(TracePoint from, TracePoint to, boolean isReturned) {
refresh(from, to, isReturned, false);
}
private void refresh(TracePoint from, TracePoint to, boolean isReturned, boolean canDifferentialUpdateVariables) {
List<IMarker> markers = createCurrentLineMarkers(to);
if (!(markers.isEmpty())) JavaEditorOperator.markAndOpenJavaFile(markers.get(0));
CallStackView callStackView = ((CallStackView)TraceDebuggerPlugin.getActiveView(CallStackView.ID));
callStackView.updateByTracePoint(to);
VariableView variableView = ((VariableView)TraceDebuggerPlugin.getActiveView(VariableView.ID));
if (!isReturned && canDifferentialUpdateVariables) {
variableView.updateVariablesForDifferential(from, to);
} else {
variableView.updateVariablesByTracePointsFromAToB(from, to);
}
if (TraceDebuggerPlugin.getActiveView(DeltaMarkerView.ID) != null) {
refreshRelatedDelta(to);
}
}
private void refreshRelatedDelta(TracePoint tp) {
DeltaMarkerView deltaMarkerView = (DeltaMarkerView)TraceDebuggerPlugin.getActiveView(DeltaMarkerView.ID);
if (deltaMarkerView == null) return;
DeltaMarkerManager deltaMarkerManager = deltaMarkerView.getDeltaMarkerManager();
if (deltaMarkerManager == null) return;
IMarker coordinatorMarker = deltaMarkerManager.getCoordinatorDeltaMarker();
if (coordinatorMarker == null) return;
MethodExecution coordinatorME = DeltaMarkerManager.getMethodExecution(coordinatorMarker);
CallStackView callStackView = (CallStackView)TraceDebuggerPlugin.getActiveView(CallStackView.ID);
callStackView.highlight(coordinatorME);
CallTreeView callTreeView = (CallTreeView)TraceDebuggerPlugin.getActiveView(CallTreeView.ID);
callTreeView.highlight(tp.getMethodExecution());
VariableViewRelatedDelta variableView = (VariableViewRelatedDelta)TraceDebuggerPlugin.getActiveView(VariableViewRelatedDelta.ID);
variableView.markAndExpandVariablesByDeltaMarkers(deltaMarkerManager.getMarkers());
}
public List<IMarker> createCurrentLineMarkers(TracePoint tp) {
deleteCurrentLineMarkers();
while (tp != null) {
try {
MethodExecution methodExecution = tp.getMethodExecution();
int highlightLineNo = tp.getStatement().getLineNo();
IFile file = JavaElementFinder.findIFile(methodExecution);
IMarker currentLineMarker = file.createMarker(CURRENT_MARKER_ID);
currentLineMarkers.add(currentLineMarker);
Map<String, Object> attributes = new HashMap<>();
attributes.put(IMarker.TRANSIENT, true);
attributes.put(IMarker.LINE_NUMBER, highlightLineNo);
IPath path = file.getFullPath();
ITextFileBufferManager manager = FileBuffers.getTextFileBufferManager();
manager.connect(path, LocationKind.IFILE, null);
ITextFileBuffer buffer = manager.getTextFileBuffer(path, LocationKind.IFILE);
IDocument document = buffer.getDocument();
try {
IRegion region = document.getLineInformation(highlightLineNo - 1);
attributes.put(IMarker.CHAR_START, region.getOffset());
attributes.put(IMarker.CHAR_END, region.getOffset() + region.getLength());
} catch (BadLocationException e) {
e.printStackTrace();
}
currentLineMarker.setAttributes(attributes);
} catch (CoreException e) {
e.printStackTrace();
}
tp = tp.getMethodExecution().getCallerTracePoint();
}
return currentLineMarkers;
}
private void deleteCurrentLineMarkers() {
for (IMarker currentLineMarker : currentLineMarkers) {
try {
currentLineMarker.delete();
} catch (CoreException e) {
e.printStackTrace();
}
}
currentLineMarkers.clear();
}
private TracePoint getTracePointSelectedOnCallStack() {
CallStackView callStackView = (CallStackView)TraceDebuggerPlugin.getActiveView(CallStackView.ID);
CallStackModel callStackModel = callStackView.getSelectionCallStackModel();
if (callStackModel != null) {
return callStackModel.getTracePoint();
}
return null;
}
public void resetExcludingForLoadingStatusOfTheTrace() {
terminateAction();
selectedTraceBreakPoint = null;
}
}