Newer
Older
org.ntlab.traceDebugger / src / org / ntlab / traceDebugger / DebuggingController.java
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 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;
	}
}