/*=============================================================================# # Copyright (c) 2014-2016 Stephan Wahlbrink (WalWare.de) and others. # All rights reserved. This program and the accompanying materials # are made available under the terms of the GNU Lesser General Public License # v2.1 which accompanies this distribution, and is available at # http://www.gnu.org/licenses/lgpl.html # # Contributors: # Stephan Wahlbrink - initial API and implementation #=============================================================================*/ package de.walware.rj.server.jri; import static de.walware.rj.server.jri.JRIServerErrors.CODE_DBG_TRACE; import static de.walware.rj.server.jri.JRIServerErrors.LOGGER; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.ListIterator; import java.util.Map; import java.util.Map.Entry; import java.util.logging.Level; import java.util.logging.LogRecord; import java.util.regex.Pattern; import org.rosuda.JRI.REXP; import org.rosuda.JRI.Rengine; import de.walware.rj.RjException; import de.walware.rj.data.RDataUtil; import de.walware.rj.data.UnexpectedRDataException; import de.walware.rj.server.RjsException; import de.walware.rj.server.RjsStatus; import de.walware.rj.server.dbg.ElementTracepointInstallationRequest; import de.walware.rj.server.dbg.ElementTracepointPositions; import de.walware.rj.server.dbg.FlagTracepointInstallationRequest; import de.walware.rj.server.dbg.SrcfileData; import de.walware.rj.server.dbg.Srcref; import de.walware.rj.server.dbg.Tracepoint; import de.walware.rj.server.dbg.TracepointEvent; import de.walware.rj.server.dbg.TracepointInstallationReport; import de.walware.rj.server.dbg.TracepointPosition; import de.walware.rj.server.dbg.TracepointState; import de.walware.rj.server.dbg.TracepointStatesUpdate; import de.walware.rj.server.jri.JRIServerRni.RNullPointerException; final class TracepointManager { private static final int[] NA_SRCREF= new int[] { Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE, }; private static final int[] LOCAL_METHOD_INDEX= new int[] { 2, 3 }; private static final Pattern SIGNATURE_PATTERN= Pattern.compile("\\#"); //$NON-NLS-1$ private static final String TOPLEVEL_ELEMENT_ID= "200:"; //$NON-NLS-1$ private static final class FunInfo { public static final int INVALID= -1; public static final int FILE_PATH= 1; public static final int FILE_NAME= 2; public static final int DONE_CLEAR= 1; public static final int DONE_SET= 2; public final long orgFunP; public final int[] subIndex; public long currentMainFunP; public final long orgMainFunP; public final String rawMethodSignature; // preCheck public long orgBodyP; public long srcfileP; int fileType; public String file; public final int[] done; public FunInfo(final long orgFunP, final long currentFunP, final String signature) { this.orgFunP= orgFunP; this.subIndex= null; this.orgMainFunP= orgFunP; this.currentMainFunP= currentFunP; this.rawMethodSignature= signature; this.done= new int[1]; } public FunInfo(final long orgFunP, final int[] subIndex, final FunInfo main) { this.orgFunP= orgFunP; this.subIndex= subIndex; this.orgMainFunP= main.orgMainFunP; this.currentMainFunP= main.currentMainFunP; this.rawMethodSignature= main.rawMethodSignature; this.done= main.done; } } private static final class TracepointStateWithData extends TracepointState { private long parsedExprP; public TracepointStateWithData(final TracepointState state) { super(state.getType(), state.getFilePath(), state.getId(), state.getElementId(), state.getIndex(), state.getElementLabel(), state.getFlags(), state.getExpr() ); } public void setParsedExpr(final long p) { if (p == 0) { this.flags |= FLAG_EXPR_INVALID; } this.parsedExprP= p; } public long getExprP() { return this.parsedExprP; } } private static boolean isLBorFB(final int type) { return (type == Tracepoint.TYPE_LB || type == Tracepoint.TYPE_FB); } private static TracepointPosition getTBPosition(final ElementTracepointPositions positions, final int[] srcref) { final int[] baseSrcref= positions.getElementSrcref(); for (final TracepointPosition position : positions.getPositions()) { final int[] positionSrcref; if (position.getType() == Tracepoint.TYPE_TB && (positionSrcref= position.getSrcref()) != null && Srcref.addLine(baseSrcref[Srcref.BEGIN_LINE], positionSrcref[Srcref.BEGIN_LINE]) == srcref[Srcref.BEGIN_LINE]) { return position; } } return null; } private static TracepointState getBState(final List<TracepointState> list, final long id) { for (int i= 0; i < list.size(); i++) { final TracepointState state= list.get(i); if ((state.getType() & Tracepoint.TYPE_BREAKPOINT) != 0 && id == state.getId()) { return state; } } return null; } private static TracepointState getBState(final List<TracepointState> list, final String elementId, final int[] index) { if (elementId != null && index != null) { for (int i= 0; i < list.size(); i++) { final TracepointState state= list.get(i); if ((state.getType() & Tracepoint.TYPE_BREAKPOINT) != 0 && elementId.equals(state.getElementId()) && Arrays.equals(index, state.getIndex()) ) { return state; } } } return null; } private final JRIServerDbg dbg; private final Rengine rEngine; private final JRIServerRni rni; private final Map<String, ElementTracepointPositions> tbPositionMap= new HashMap<>(); private final List<ElementTracepointPositions> tbHistory= new ArrayList<>(10); private final Map<String, List<TracepointState>> tracepointStateMap= new HashMap<>(); private boolean breakpointsEnabled= true; private TracepointState hitBreakpointState; private int hitBreakpointFlags; private int[] hitBreakpointSrcref; private long hitBreakpointSrcfile; private long hitBreakpointFrameP; private final long traceSymP; private final long untraceSymP; private final long AllMTableSymP; private final long editFArgsP; private final long stepNextValueP; private final long stepSrcrefTmplP; private final long breakpointTmplP; private final long errorHandlerP; private final long getTraceExprEnvCallP; private final long getRootEvalCallCallP; private final long browserExprP; public TracepointManager(final JRIServerDbg dbg, final JRIServerRni rni) throws RjsException { this.dbg= dbg; this.rEngine= rni.getREngine(); this.rni= rni; this.traceSymP= this.rEngine.rniInstallSymbol("trace"); //$NON-NLS-1$ this.untraceSymP= this.rEngine.rniInstallSymbol("untrace"); //$NON-NLS-1$ this.AllMTableSymP= this.rEngine.rniInstallSymbol(".AllMTable"); //$NON-NLS-1$ this.editFArgsP= this.rni.checkAndPreserve(this.rEngine.rniCons( this.rni.MissingArg_P, this.rEngine.rniCons( this.rni.MissingArg_P, this.rni.NULL_P, this.rni.Ellipsis_SymP, false ), rni.fdef_SymP, false )); this.stepNextValueP= this.rni.checkAndPreserve(this.rEngine.rniPutString("browser:n")); //$NON-NLS-1$ this.stepSrcrefTmplP= this.rni.checkAndPreserve(this.rEngine.rniPutIntArray(NA_SRCREF)); this.rEngine.rniSetAttrBySym(this.stepSrcrefTmplP, this.rni.what_SymP, this.stepNextValueP); this.breakpointTmplP= this.rni.checkAndPreserve(this.rEngine.rniGetVectorElt( this.rEngine.rniParse( "{ if (\"rj\" %in% loadedNamespaces()) rj:::.breakpoint() }", 1 ), //$NON-NLS-1$ 0 )); this.errorHandlerP= this.rni.checkAndPreserve(this.rEngine.rniParse( "rj:::.checkError()", 1 )); this.getRootEvalCallCallP= this.rni.checkAndPreserve(this.dbg.createSysCallCall(-3)); this.getTraceExprEnvCallP= this.rni.checkAndPreserve(this.dbg.createSysFrameCall(-1)); this.browserExprP= this.rni.checkAndPreserve(this.rEngine.rniGetVectorElt(this.rEngine.rniParse( "{ browser(skipCalls= 3L) }", 1 ), //$NON-NLS-1$ 0 )); } public void setBreakpointsEnabled(final boolean enabled) { this.breakpointsEnabled= enabled; } public boolean isBreakpointsEnabled() { return this.breakpointsEnabled; } public TracepointInstallationReport installTracepoints(final FlagTracepointInstallationRequest request) throws RjsException { final byte[] types= request.getTypes(); final int[] flags= request.getFlags(); final int[] resultCodes= new int[types.length]; for (int i= 0; i < types.length; i++) { switch (types[i]) { case Tracepoint.TYPE_EB: resultCodes[i]= setEB(flags[i]); break; default: resultCodes[i]= TracepointInstallationReport.NOTFOUND; break; } } return new TracepointInstallationReport(resultCodes); } private int setEB(final int flags) { try { if ((flags & TracepointState.FLAG_ENABLED) != 0) { final long handlerP= createExceptionHandler(); if (this.rni.setOption(this.rni.error_SymP, handlerP)) { return TracepointInstallationReport.FOUND_SET; } } else { if (this.rni.setOption(this.rni.error_SymP, this.rni.NULL_P)) { return TracepointInstallationReport.FOUND_UNSET; } } } catch (final Exception e) { final LogRecord record= new LogRecord(Level.SEVERE, "An error occurred when setting dbg error handler."); record.setThrown(e); JRIServerErrors.LOGGER.log(record); } return TracepointInstallationReport.NOTFOUND; } private void addTB(final ElementTracepointPositions positions) { final SrcfileData srcfile= positions.getSrcfile(); this.tbPositionMap.put(srcfile.getPath(), positions); for (final ListIterator<ElementTracepointPositions> iter= this.tbHistory.listIterator(); iter.hasNext();) { final ElementTracepointPositions old= iter.next(); if (old.getSrcfile().equals(srcfile)) { iter.set(positions); break; } } } private void updateTBHistory(final ElementTracepointPositions positions) { final SrcfileData srcfile= positions.getSrcfile(); for (final ListIterator<ElementTracepointPositions> iter= this.tbHistory.listIterator(); iter.hasNext();) { final ElementTracepointPositions old= iter.next(); if (old == positions || old.getSrcfile().equals(srcfile)) { if (iter.previousIndex() == 0) { if (old != positions) { iter.set(positions); } return; } iter.remove(); break; } } if (this.tbHistory.size() >= 10) { this.tbHistory.remove(this.tbHistory.size() - 1); } this.tbHistory.add(0, positions); } /** * Installs the specified tracepoints * * @param request with elements and their tracepoints * @return a report * @throws RjsException */ public TracepointInstallationReport installTracepoints(final ElementTracepointInstallationRequest request) throws RjsException { final List<? extends ElementTracepointPositions> elementList= request.getRequests(); final int[] resultCodes= new int[elementList.size()]; Arrays.fill(resultCodes, TracepointInstallationReport.NOTFOUND); { int todo= 0; for (int elementIdx= 0; elementIdx < elementList.size(); elementIdx++) { final ElementTracepointPositions elementTracepointPositions= elementList.get(elementIdx); if (elementTracepointPositions.getElementId().startsWith(TOPLEVEL_ELEMENT_ID)) { addTB(elementTracepointPositions); continue; } else { todo++; } } if (todo == 0) { return new TracepointInstallationReport(resultCodes); } } final List<Long> envTodo= new ArrayList<>(); final List<Long> envDone= new ArrayList<>(); this.rni.addAllEnvs(envTodo, this.rni.Global_EnvP); this.dbg.addAllStackEnvs(envTodo); while (!envTodo.isEmpty()) { final long envP; { final Long env= envTodo.remove(0); envDone.add(env); envP= env.longValue(); } if (this.rni.isInternEnv(envP)) { continue; } final int savedProtected= this.rni.saveProtected(); final long namesStrP= this.rni.protect(this.rEngine.rniListEnv(envP, true)); final String[] names= this.rEngine.rniGetStringArray(namesStrP); for (int namesIdx= 0; namesIdx < names.length; namesIdx++) { final String name= names[namesIdx]; if (name == null || ((envP == this.rni.Base_EnvP || envP == this.rni.BaseNamespace_EnvP) && name.equals(".Last.value") )) { //$NON-NLS-1$ continue; } final long funP; final long nameSymP= this.rEngine.rniInstallSymbolByStr(namesStrP, namesIdx); { long p= this.rEngine.rniGetVarBySym(nameSymP, envP, 0); if (p == 0) { continue; } int type= this.rEngine.rniExpType(p); if (type == REXP.PROMSXP) { p= this.rEngine.rniGetPromise(p, 1); type= this.rEngine.rniExpType(p); } if (type != REXP.CLOSXP) { continue; } funP= p; } final long orgFunP= this.dbg.getOrgFun(funP); if (orgFunP == 0) { continue; } FunInfo commonInfo= new FunInfo(orgFunP, funP, null); if (!preCheck(commonInfo)) { commonInfo= null; } final long nameStrP= this.rni.protect(this.rEngine.rniPutString(name)); // method List<FunInfo> methodInfos= null; try { final long nameEnvP= checkGeneric(orgFunP, nameStrP, envP); if (nameEnvP != 0) { final String[] signatures= this.rEngine.rniGetStringArray( this.rEngine.rniListEnv(nameEnvP, true) ); methodInfos= new ArrayList<>(signatures.length); for (int signarturesIdx= 0; signarturesIdx < signatures.length; signarturesIdx++) { try { final long methodP; { long p= this.rEngine.rniGetVar(signatures[signarturesIdx], nameEnvP); int type= this.rEngine.rniExpType(p); if (type == REXP.PROMSXP) { p= this.rEngine.rniGetPromise(p, 1); type= this.rEngine.rniExpType(p); } if (type != REXP.CLOSXP) { continue; } methodP= p; } final long mainOrgMethodP= this.dbg.getOrgFun(methodP); if (mainOrgMethodP == 0) { continue; } final FunInfo methodInfo= new FunInfo(mainOrgMethodP, methodP, signatures[signarturesIdx] ); if (preCheck(methodInfo)) { methodInfos.add(methodInfo); } long localMethodP= this.rEngine.rniGetCloBodyExpr(mainOrgMethodP); if (localMethodP == 0 || this.rEngine.rniExpType(localMethodP) != REXP.LANGSXP || this.rEngine.rniGetLength(localMethodP) < 3 || this.rEngine.rniCAR(localMethodP) != this.rni.Block_SymP) { continue; } localMethodP= this.rEngine.rniCAR(this.rEngine.rniCDR(localMethodP)); if (localMethodP == 0 || this.rEngine.rniExpType(localMethodP) != REXP.LANGSXP || this.rEngine.rniGetLength(localMethodP) < 3 || this.rEngine.rniCAR(localMethodP) != this.rni.Assign_SymP) { continue; } localMethodP= this.rEngine.rniCAR(this.rEngine.rniCDR(this.rEngine.rniCDR(localMethodP))); if (localMethodP == 0 || this.rEngine.rniExpType(localMethodP) != REXP.CLOSXP) { continue; } { final FunInfo subInfo= new FunInfo(localMethodP, LOCAL_METHOD_INDEX, methodInfo ); if (preCheck(subInfo)) { methodInfos.add(subInfo); } } } catch (final Exception e) { final LogRecord record= new LogRecord(Level.SEVERE, "Method check failed for function ''{0}({1})'' in ''0x{2}''."); record.setParameters(new Object[] { name, signatures[signarturesIdx], Long.toHexString(envP) }); record.setThrown(e); JRIServerErrors.LOGGER.log(record); } } } } catch (final Exception e) { final LogRecord record= new LogRecord(Level.SEVERE, "Generic check failed for function ''{0}'' in ''0x{1}''."); record.setParameters(new Object[] { name, Long.toHexString(envP) }); record.setThrown(e); JRIServerErrors.LOGGER.log(record); } for (int elementIdx= 0; elementIdx < elementList.size(); elementIdx++) { final ElementTracepointPositions elementTracepointPositions= elementList.get(elementIdx); if (elementTracepointPositions.getElementId().startsWith(TOPLEVEL_ELEMENT_ID)) { continue; } if (commonInfo != null && commonInfo.done[0] < FunInfo.DONE_SET) { final int funSet= trySetTracepoints(elementList.get(elementIdx), commonInfo, name, nameStrP, envP); if (funSet > resultCodes[elementIdx]) { resultCodes[elementIdx]= funSet; continue; } } if (methodInfos != null) { for (int methodIdx= 0; methodIdx < methodInfos.size(); methodIdx++) { final FunInfo methodInfo= methodInfos.get(methodIdx); if (methodInfo.done[0] < FunInfo.DONE_SET) { final int methodSet= trySetTracepoints(elementTracepointPositions, methodInfo, name, nameStrP, envP); if (methodSet > resultCodes[elementIdx]) { resultCodes[elementIdx]= methodSet; } } } } } } this.rni.looseProtected(savedProtected); } if (LOGGER.isLoggable(Level.FINER)) { final StringBuilder sb= new StringBuilder("Dbg: installTracepoints"); //$NON-NLS-1$ for (int i= 0; i < resultCodes.length; i++) { sb.append('\n').append(i).append("."); //$NON-NLS-1$ sb.append(" --> ").append(resultCodes[i]); //$NON-NLS-1$ sb.append('\n').append(elementList.get(i)); } LOGGER.log(Level.FINER, sb.toString()); } return new TracepointInstallationReport(resultCodes); } private long checkGeneric(final long funP, final long nameStringP, final long envP) throws Exception { long nameEnvP= this.rEngine.rniGetCloEnv(funP); if (nameEnvP == 0 || this.rEngine.rniExpType(nameEnvP) != REXP.ENVSXP) { return 0; } nameEnvP= this.rEngine.rniGetVarBySym(this.AllMTableSymP, nameEnvP, 0); if (nameEnvP == 0 || this.rEngine.rniExpType(nameEnvP) != REXP.ENVSXP) { return 0; } final long p= this.rEngine.rniEval(this.rEngine.rniCons( this.rni.isGeneric_SymP, this.rEngine.rniCons( nameStringP, this.rEngine.rniCons( envP, this.rEngine.rniCons( funP, this.rni.NULL_P, this.rni.fdef_SymP, false ), this.rni.where_SymP, false ), 0, false ), 0, true ), this.rni.rniSafeGlobalExecEnvP ); return (p != 0 && this.rEngine.rniIsTrue(p)) ? nameEnvP : 0; } private boolean preCheck(final FunInfo funInfo) { funInfo.orgBodyP= this.rEngine.rniGetCloBodyExpr(funInfo.orgFunP); if (funInfo.orgBodyP == 0) { return false; } funInfo.srcfileP= this.dbg.getSrcfileEnvP(funInfo.orgBodyP); if (funInfo.srcfileP == 0) { return false; } funInfo.file= this.dbg.getFilePath(funInfo.srcfileP, 0); if (funInfo.file != null) { funInfo.fileType= FunInfo.FILE_PATH; return true; } funInfo.file= this.dbg.getFileName(funInfo.srcfileP); if (funInfo.file != null) { funInfo.fileType= FunInfo.FILE_NAME; return true; } funInfo.fileType= FunInfo.INVALID; return false; } private int trySetTracepoints(final ElementTracepointPositions elementTracepointPositions, final FunInfo funInfo, final String nameString, final long nameStringP, final long envP) { int[] baseSrcref= null; // file final SrcfileData srcfile= elementTracepointPositions.getSrcfile(); boolean ok= false; if (srcfile.getPath() == null) { return TracepointInstallationReport.NOTFOUND; } if (funInfo.fileType == FunInfo.FILE_PATH) { if (funInfo.file.equals(srcfile.getPath())) { ok= true; } else { return TracepointInstallationReport.NOTFOUND; } } if (!ok && funInfo.fileType == FunInfo.FILE_NAME) { if (funInfo.file.equals(srcfile.getName())) { ok= true; } else { return TracepointInstallationReport.NOTFOUND; } } if (!ok) { return TracepointInstallationReport.NOTFOUND; } // element ok= false; if (elementTracepointPositions.getElementId() != null) { final long p= this.rEngine.rniGetAttrBySym(funInfo.orgBodyP, this.rni.appElementId_SymP); if (p != 0) { if (elementTracepointPositions.getElementId().equals(this.rEngine.rniGetString(p))) { ok= true; } else { return TracepointInstallationReport.NOTFOUND; } } } { long p= this.rEngine.rniGetAttrBySym(funInfo.orgBodyP, this.dbg.srcref_SymP); if (p != 0) { p= this.rEngine.rniGetVectorElt(p, 0); if (p != 0) { baseSrcref= this.rEngine.rniGetIntArray(p); if (baseSrcref != null && baseSrcref.length < 6) { baseSrcref= null; } } } } if (!ok && baseSrcref != null && elementTracepointPositions.getElementSrcref() != null) { long funTimestamp; if (srcfile.getTimestamp() != 0 && baseSrcref[0] == elementTracepointPositions.getElementSrcref()[0] && baseSrcref[4] == elementTracepointPositions.getElementSrcref()[4] && ((funTimestamp= this.dbg.getFileTimestamp(funInfo.srcfileP)) == 0 // rpkg || funTimestamp == srcfile.getTimestamp() || Math.abs(funTimestamp - srcfile.getTimestamp()) == 3600 )) { ok= true; } } final int l; if (ok) { funInfo.done[0]= FunInfo.DONE_SET; l= elementTracepointPositions.getPositions().size(); } else { l= 0; long p; if (funInfo.done[0] < FunInfo.DONE_CLEAR && funInfo.currentMainFunP != funInfo.orgMainFunP && (p= this.rEngine.rniGetAttrBySym(funInfo.currentMainFunP, this.rni.dbgElementId_SymP)) != 0 && elementTracepointPositions.getElementId().equals(this.rEngine.rniGetString(p)) ) { ok= true; funInfo.done[0]= FunInfo.DONE_CLEAR; } } if (!ok) { return TracepointInstallationReport.NOTFOUND; } // ok int result= TracepointInstallationReport.FOUND_UNCHANGED; final int savedProtected= this.rni.saveProtected(); try { long traceArgsP= this.rni.NULL_P; traceArgsP= this.rEngine.rniCons( envP, traceArgsP, this.rni.where_SymP, false ); if (funInfo.rawMethodSignature != null) { final String[] signature= SIGNATURE_PATTERN.split(funInfo.rawMethodSignature); traceArgsP= this.rEngine.rniCons( this.rEngine.rniPutStringArray(signature), traceArgsP, this.rni.signature_SymP, false ); } traceArgsP= this.rEngine.rniCons( nameStringP, traceArgsP, this.rni.what_SymP, false ); long editFunP= 0; if (l > 0) { // prepare final long newMainFunP= this.rni.checkAndProtect(this.rEngine.rniDuplicate(funInfo.orgMainFunP)); // if (subIndex != null) assert(this.rEngine.rniGetCloBodyExpr(newMainFunP) == bodyP); long newMainBodyP= this.rni.checkAndProtect(this.rEngine.rniDuplicate( this.rEngine.rniGetCloBodyExpr(newMainFunP) )); long newBodyP= newMainBodyP; long subListP= 0; long subCloP= 0; if (funInfo.subIndex != null && funInfo.subIndex.length > 0) { for (int i= 0; i < funInfo.subIndex.length; i++) { if (this.rEngine.rniExpType(newBodyP) != REXP.LANGSXP || this.rEngine.rniGetLength(newBodyP) < funInfo.subIndex[i]) { throw new IllegalStateException(); } for (int idx= 1; idx < funInfo.subIndex[i]; idx++) { newBodyP= this.rEngine.rniCDR(newBodyP); } subListP= newBodyP; newBodyP= this.rEngine.rniCAR(newBodyP); } if (this.rEngine.rniExpType(newBodyP) == REXP.CLOSXP) { subListP= 0; subCloP= newBodyP; // assert(this.rEngine.rniGetCloBodyExpr(subCloP) == bodyP); newBodyP= this.rni.checkAndProtect(this.rEngine.rniDuplicate( this.rEngine.rniGetCloBodyExpr(newBodyP) )); } } if (this.rEngine.rniExpType(newBodyP) != REXP.LANGSXP) { throw new IllegalStateException("unsupported SXP-type: " + this.rEngine.rniExpType(newBodyP)); } final long filePathP= this.rni.checkAndProtect(this.rEngine.rniPutString( srcfile.getPath() )); final long elementIdP= this.rni.checkAndProtect(this.rEngine.rniPutString( elementTracepointPositions.getElementId() )); final int i= 0; int j= 0; while (j < l) { if (elementTracepointPositions.getPositions().get(j).getIndex().length == 0) { j++; } else { break; } } if (j < l) { addTrace(newBodyP, elementTracepointPositions.getPositions().subList(j, l), 0, funInfo, filePathP, elementIdP, baseSrcref, null ); } if (i < j) { newBodyP= createTraceLang(newBodyP, elementTracepointPositions.getPositions().subList(i, j), funInfo, filePathP, elementIdP, baseSrcref); } if (subCloP != 0) { this.rEngine.rniSetAttrBySym(subCloP, this.rni.original_SymP, this.rEngine.rniDuplicate(subCloP)); this.rEngine.rniSetCloBody(subCloP, newBodyP); } else if (subListP != 0) { this.rEngine.rniSetCAR(subListP, newBodyP); } else { newMainBodyP= newBodyP; } this.rEngine.rniSetAttrBySym(newBodyP, this.rni.dbgElementId_SymP, elementIdP); if (newBodyP != newMainBodyP) { this.rEngine.rniSetAttrBySym(newMainBodyP, this.rni.dbgElementId_SymP, elementIdP); } this.rEngine.rniSetCloBody(newMainFunP, newMainBodyP); editFunP= createEditFun(newMainFunP); } if (funInfo.currentMainFunP != funInfo.orgMainFunP) { // unset this.rni.evalExpr(this.rEngine.rniCons( this.untraceSymP, traceArgsP, 0, true ), this.rni.rniSafeGlobalExecEnvP, CODE_DBG_TRACE ); funInfo.currentMainFunP= funInfo.orgMainFunP; } result= TracepointInstallationReport.FOUND_UNSET; if (l > 0) { // set this.rni.evalExpr(this.rEngine.rniCons( this.traceSymP, this.rEngine.rniCons( editFunP, traceArgsP, this.rni.edit_SymP, false ), 0, true ), this.rni.rniSafeGlobalExecEnvP, CODE_DBG_TRACE ); result= TracepointInstallationReport.FOUND_SET; } } catch (final Exception e) { final LogRecord record= new LogRecord(Level.SEVERE, (funInfo.rawMethodSignature != null) ? "Updating tracepoints failed for method ''{0}({1})'' in ''0x{2}''." : "Updating tracepoints failed for function ''{0}'' in ''0x{2}''."); record.setParameters(new Object[] { nameString, funInfo.rawMethodSignature, Long.toHexString(envP) }); record.setThrown(e); JRIServerErrors.LOGGER.log(record); } finally { this.rni.looseProtected(savedProtected); } return result; } private long createEditFun(final long newFDefP) { final long fBodyP= this.rEngine.rniCons( this.rni.Block_SymP, this.rEngine.rniCons( newFDefP, this.rni.NULL_P, 0, false ), 0, true ); return this.rni.protect(this.rEngine.rniEval(this.rni.protect(this.rEngine.rniCons( this.rni.function_SymP, this.rEngine.rniCons( this.editFArgsP, this.rEngine.rniCons( fBodyP, this.rni.NULL_P, 0, false ), 0, false ), 0, true )), this.rni.rniSafeBaseExecEnvP )); } private void addTrace(final long newP, final List<? extends TracepointPosition> list, final int depth, final FunInfo funInfo, final long filePathP, final long elementIdP, final int[] baseSrcref, final int[] lastSrcref) throws UnexpectedRDataException, RNullPointerException { final int length= this.rEngine.rniGetLength(newP); final long newSrcrefP= this.rEngine.rniGetAttrBySym(newP, this.dbg.srcref_SymP); int currentIdx= 1; long currentP= newP; for (int i= 0; i < list.size(); ) { final int[] breakpointIndex= list.get(i).getIndex(); final int breakpointIdx= breakpointIndex[depth]; if (currentIdx > breakpointIdx || breakpointIdx > length) { throw new IllegalStateException(); } while (currentIdx < breakpointIdx) { currentP= this.rEngine.rniCDR(currentP); currentIdx++; } // collect nested breakpoints int j= (depth+1 == breakpointIndex.length) ? i+1 : i; // exclusive int k= i+1; // exclusive while (k < list.size()) { final int[] nextIndex= list.get(k).getIndex(); if (breakpointIdx == nextIndex[depth]) { if (j == k && depth+1 == nextIndex.length) { j++; } k++; continue; } else { break; } } long currentValueP= this.rEngine.rniCAR(currentP); final long currentSrcrefP= (newSrcrefP != 0) ? this.rEngine.rniGetVectorElt(newSrcrefP, breakpointIdx-1) : 0; int[] currentSrcref= null; if (i < j) { currentSrcref= bestSrcref(baseSrcref, list.get(i).getSrcref(), currentSrcrefP, lastSrcref ); } else { if (currentSrcrefP != 0) { currentSrcref= this.rEngine.rniGetIntArray(currentSrcrefP); } if (currentSrcref == null) { currentSrcref= lastSrcref; } } if (j < k) { if (this.rEngine.rniExpType(currentValueP) != REXP.LANGSXP) { throw new IllegalStateException("unsupported SXP-type: " + this.rEngine.rniExpType(currentValueP)); } long orgP; if (this.rEngine.rniGetLength(currentValueP) == 3 && this.rEngine.rniCAR(currentValueP) == this.rni.function_SymP) { orgP= this.rni.checkAndProtect(this.rEngine.rniDuplicate(currentValueP)); } else { orgP= 0; } addTrace(currentValueP, list.subList(j, k), depth+1, funInfo, filePathP, elementIdP, baseSrcref, currentSrcref ); if (orgP != 0) { orgP= this.rEngine.rniEval(orgP, this.rni.rniSafeBaseExecEnvP ); if (orgP != 0) { currentValueP= this.rEngine.rniEval(currentValueP, this.rni.rniSafeBaseExecEnvP ); if (currentValueP == 0) { throw new UnexpectedRDataException("closure"); } this.rEngine.rniSetAttrBySym(currentValueP, this.rni.original_SymP, orgP); this.rEngine.rniSetCAR(currentP, currentValueP); } } } if (i < j) { this.rEngine.rniSetCAR(currentP, createTraceLang(currentValueP, list.subList(i, j), funInfo, filePathP, elementIdP, currentSrcref )); } i= k; } } private int[] bestSrcref(final int[] baseSrcref, final int[] positionSrcref, final long currentSrcrefP, final int[] lastSrcref) throws UnexpectedRDataException { int[] currentSrcref= null; if (currentSrcrefP != 0) { currentSrcref= this.rEngine.rniGetIntArray(currentSrcrefP); this.rEngine.rniSetAttrBySym(currentSrcrefP, this.rni.what_SymP, this.stepNextValueP ); } else if (baseSrcref != null && positionSrcref != null) { currentSrcref= Srcref.add(baseSrcref, positionSrcref); if (currentSrcref != null && lastSrcref != null) { if (currentSrcref[0] < lastSrcref[0] || currentSrcref[2] > lastSrcref[2] ) { currentSrcref= null; } else { if (currentSrcref[4] != Integer.MIN_VALUE && lastSrcref[4] != Integer.MIN_VALUE && currentSrcref[0] == lastSrcref[0] && currentSrcref[4] < lastSrcref[4] ) { currentSrcref[4]= Integer.MIN_VALUE; } if (currentSrcref[5] != Integer.MIN_VALUE && lastSrcref[5] != Integer.MIN_VALUE && currentSrcref[2] == lastSrcref[2] && currentSrcref[5] > lastSrcref[5] ) { currentSrcref[5]= Integer.MIN_VALUE; } } } } if (currentSrcref == null && lastSrcref != null) { currentSrcref= lastSrcref; } return currentSrcref; } private long createTraceLang(long currentP, final List<? extends TracepointPosition> list, final FunInfo funInfo, final long filePathP, final long elementIdP, final int[] currentSrcref) throws RNullPointerException { for (int i= list.size()-1; i >= 0; i--) { int n; final TracepointPosition position= list.get(i); if (position.getType() == Tracepoint.TYPE_LB) { final long breakpointP= createBreakpointHandler(position, currentSrcref, funInfo, filePathP, elementIdP, 0 ); currentP= this.rEngine.rniCons( breakpointP, this.rEngine.rniCons( currentP, this.rni.NULL_P, 0, false ), 0, false ); n= 1; } else if (position.getType() == Tracepoint.TYPE_FB) { final long entryP= createBreakpointHandler(position, currentSrcref, funInfo, filePathP, elementIdP, TracepointState.FLAG_MB_ENTRY ); long exitP= createBreakpointHandler(position, currentSrcref, funInfo, filePathP, elementIdP, TracepointState.FLAG_MB_EXIT ); exitP= this.rni.protect(this.rEngine.rniCons( this.rni.onExit_SymP, this.rEngine.rniCons( exitP, this.rni.NULL_P, 0, false ), 0, true )); currentP= this.rEngine.rniCons( exitP, this.rEngine.rniCons( entryP, this.rEngine.rniCons( currentP, this.rni.NULL_P, 0, false ), 0, false ), 0, false ); n= 2; } else { continue; } currentP= this.rni.checkAndProtect(this.rEngine.rniCons( this.rni.Block_SymP, currentP, 0, true )); this.rEngine.rniSetAttrBySym(currentP, this.dbg.srcref_SymP, createTraceSrcref(n, (currentSrcref != null) ? currentSrcref : NA_SRCREF, funInfo.srcfileP, elementIdP )); } return currentP; } private long createBreakpointHandler(final TracepointPosition position, final int[] srcref, final FunInfo funInfo, final long filePathP, final long elementIdP, final int flags) throws RNullPointerException { final long exprP= this.rni.checkAndProtect(this.rEngine.rniDuplicate(this.breakpointTmplP)); { final long[] srcrefList= new long[2]; srcrefList[0]= this.rni.checkAndProtect(this.rEngine.rniDuplicate( this.stepSrcrefTmplP )); srcrefList[1]= this.rni.checkAndProtect(this.rEngine.rniPutIntArray( (srcref != null) ? srcref : NA_SRCREF )); this.rEngine.rniSetAttrBySym(srcrefList[1], this.dbg.srcfile_SymP, funInfo.srcfileP); if (filePathP != 0) { this.rEngine.rniSetAttrBySym(srcrefList[1], this.rni.appFilePath_SymP, filePathP); } this.rEngine.rniSetAttrBySym(srcrefList[1], this.rni.dbgElementId_SymP, elementIdP); this.rEngine.rniSetAttrBySym(srcrefList[1], this.rni.at_SymP, this.rEngine.rniPutIntArray(position.getIndex()) ); this.rEngine.rniSetAttrBySym(srcrefList[1], this.rni.id_SymP, this.rEngine.rniPutRawArray(RDataUtil.encodeLongToRaw(position.getId())) ); if (flags != 0) { this.rEngine.rniSetAttrBySym(srcrefList[1], this.rni.flags_SymP, this.rEngine.rniPutIntArray(new int[] { flags }) ); } this.rEngine.rniSetAttrBySym(srcrefList[1], this.rni.what_SymP, this.stepNextValueP); this.rEngine.rniSetAttrBySym(exprP, this.dbg.srcref_SymP, this.rEngine.rniPutVector(srcrefList)); } return exprP; } private long createTraceSrcref(final int n, final int[] srcref, final long srcfileP, final long elementIdP) throws RNullPointerException { final long[] list= new long[n+2]; list[0]= this.rni.checkAndProtect(this.rEngine.rniPutIntArray(srcref)); this.rEngine.rniSetAttrBySym(list[0], this.dbg.srcfile_SymP, srcfileP); this.rEngine.rniSetAttrBySym(list[0], this.rni.what_SymP, this.stepNextValueP); this.rEngine.rniSetAttrBySym(list[0], this.rni.dbgElementId_SymP, elementIdP); for (int i= 1; i <= n; i++) { list[i]= list[0]; } list[n+1]= this.rni.checkAndProtect(this.rEngine.rniPutIntArray(srcref)); this.rEngine.rniSetAttrBySym(list[n+1], this.dbg.srcfile_SymP, srcfileP); this.rEngine.rniSetAttrBySym(list[n+1], this.rni.dbgElementId_SymP, elementIdP); return this.rEngine.rniPutVector(list); } private long createExceptionHandler() throws RNullPointerException { return this.errorHandlerP; } /** * Updates the state of the specified tracepoints * * @param request the request with tracepoint state updates * @param reset if all existing tracepoint state should be reset * @return update result status */ public RjsStatus updateTracepointStates(final TracepointStatesUpdate request) { synchronized (this.tracepointStateMap) { if (request.getReset()) { this.tracepointStateMap.clear(); } final List<TracepointState> list= request.getStates(); String path= null; List<TracepointState> pathList= null; for (int i= 0; i < list.size(); i++) { final TracepointState state= list.get(i); if (path != state.getFilePath()) { path= state.getFilePath(); pathList= this.tracepointStateMap.get(path); if (pathList == null) { if (state.getType() == Tracepoint.TYPE_DELETED) { continue; } pathList= new ArrayList<>(8); this.tracepointStateMap.put(path, pathList); } } final int idx= pathList.indexOf(state); if (idx >= 0) { final TracepointState oldState= pathList.remove(idx); if (oldState instanceof TracepointStateWithData) { final long p= ((TracepointStateWithData) oldState).getExprP(); if (p != 0) { this.dbg.addToRelease(p); } } } if (state.getType() == Tracepoint.TYPE_DELETED) { continue; } pathList.add(state); } if (this.tracepointStateMap.size() > 32) { final Iterator<Entry<String, List<TracepointState>>> iter= this.tracepointStateMap.entrySet().iterator(); while (iter.hasNext()) { if (iter.next().getValue().isEmpty()) { iter.remove(); } } } } return RjsStatus.OK_STATUS; } public long checkBreakpoint(final long callP) throws RjException { if (!this.breakpointsEnabled) { return 0; } final long srcrefP= this.rEngine.rniGetAttrBySym(callP, this.dbg.srcref_SymP); if (srcrefP == 0) { throw new RjException("Missing data: srcref."); } final long srcfileP= this.dbg.getSrcfileEnvP(srcrefP); if (srcfileP == 0) { throw new RjException("Missing data: srcfile env of srcref."); } final String filePath= this.dbg.getFilePath(srcfileP, srcrefP); if (filePath == null) { throw new RjException("Missing data: path of srcref."); } final long idP= this.rEngine.rniGetAttrBySym(srcrefP, this.rni.id_SymP); final long atP= this.rEngine.rniGetAttrBySym(srcrefP, this.rni.at_SymP); if (idP == 0 || atP == 0 ) { throw new RjException("Missing data: id/position."); } final long id= RDataUtil.decodeLongFromRaw(this.rEngine.rniGetRawArray(idP)); TracepointState tracepointState= null; synchronized (this.tracepointStateMap) { final List<TracepointState> list= this.tracepointStateMap.get(filePath); if (list != null) { tracepointState= getBState(list, id); if (tracepointState == null || !isLBorFB(tracepointState.getType() & Tracepoint.TYPE_BREAKPOINT) ) { tracepointState= getBState(list, this.rEngine.rniGetAttrStringBySym(srcrefP, this.rni.dbgElementId_SymP), this.rEngine.rniGetIntArray(atP) ); } } } if (tracepointState == null || !isLBorFB(tracepointState.getType())) { LOGGER.log(Level.FINE, "Skipping breakpoint because of missing state."); return 0; } if (!tracepointState.isEnabled()) { // LOGGER.log(Level.FINER, "Skipping breakpoint because it is disabled."); return 0; } this.hitBreakpointState= null; this.hitBreakpointSrcref= this.rEngine.rniGetIntArray(srcrefP); if (this.hitBreakpointSrcref == null || this.hitBreakpointSrcref.length < 6) { throw new RjException("Missing data: srcref values."); } if (!this.breakpointsEnabled) { return 0; } int codeFlags= 0; { // load flags final long p= this.rEngine.rniGetAttrBySym(srcrefP, this.rni.flags_SymP); if (p != 0) { codeFlags= this.rEngine.rniGetIntArray(p)[0]; } } int flags= 0; { // check flags final int stateFlags= tracepointState.getFlags(); if (tracepointState.getType() == Tracepoint.TYPE_FB) { if ((codeFlags & stateFlags & (TracepointState.FLAG_MB_ENTRY | TracepointState.FLAG_MB_EXIT)) == 0) { // LOGGER.log(Level.FINER, "Skipping breakpoint because current position is disabled."); return 0; } } flags |= (codeFlags & 0x00ff0000); } if (tracepointState.getExpr() != null) { // check expr final long exprP= getTracepointEvalExpr(tracepointState); if (exprP == 0) { // LOGGER.log(Level.WARNING, "Creating expression for breakpoint condition failed."); return 0; } this.dbg.beginSafeMode(); try { final long envP= this.rni.createNewEnv(this.getTraceExprEnvCallP); if (envP == 0) { LOGGER.log(Level.SEVERE, "Creating environment for breakpoint condition failed."); return 0; } this.rni.protect(envP); final long p= this.rni.evalExpr(exprP, envP, 1); if (!this.rEngine.rniIsTrue(p)) { return 0; } } catch (final RjsException e) { // LOGGER.log(Level.FINER, "Skipping breakpoint because evaluating the condition failed.", e); // evaluation failed (expression invalid...) // TODO notify ? return 0; } finally { this.dbg.endSafeMode(); } } try { final long browserExprP= this.rni.checkAndProtect(this.rEngine.rniDuplicate( this.browserExprP )); final long[] srcrefList= new long[2]; srcrefList[0]= this.rni.checkAndProtect(this.rEngine.rniDuplicate( this.stepSrcrefTmplP )); srcrefList[1]= this.rni.checkAndProtect(this.rEngine.rniPutIntArray( this.hitBreakpointSrcref )); if ((codeFlags & TracepointState.FLAG_MB_EXIT) == 0) { this.rEngine.rniSetAttrBySym(srcrefList[1], this.rni.what_SymP, this.stepNextValueP); srcrefList[1]= this.rni.checkAndProtect(this.rEngine.rniDuplicate( this.stepSrcrefTmplP )); } this.rEngine.rniSetAttrBySym(browserExprP, this.dbg.srcref_SymP, this.rEngine.rniPutVector(srcrefList) ); return browserExprP; } catch (final RNullPointerException e) { LOGGER.log(Level.WARNING, "Failed to create browser expression.", e); return this.browserExprP; } finally { this.hitBreakpointState= tracepointState; this.hitBreakpointFlags= flags; this.hitBreakpointSrcfile= srcfileP; this.hitBreakpointFrameP= 0; } } public long checkTB(final long argsP) { final long[] argValuesP; if (this.tbPositionMap.isEmpty() || argsP == 0 || (argValuesP= this.rEngine.rniGetVector(argsP)) == null || argValuesP.length < 2) { return 0; } final long srcrefP= this.dbg.getExpr0SrcrefP(argValuesP[0]); if (srcrefP == 0) { return 0; } final long srcfileP= this.dbg.getSrcfileEnvP(srcrefP); if (srcfileP == 0) { return 0; } ElementTracepointPositions positions= null; String filePath= this.dbg.getFilePath(srcfileP, srcrefP); if (filePath != null) { positions= this.tbPositionMap.get(filePath); } else { final String fileName= this.dbg.getFileName(srcfileP); if (fileName != null) { for (final ElementTracepointPositions candidate : this.tbPositionMap.values()) { if (fileName.equals(candidate.getSrcfile().getName())) { positions= candidate; filePath= positions.getSrcfile().getPath(); break; } } } } if (filePath == null) { return 0; } { final long timestamp= this.dbg.getFileTimestamp(srcfileP); if (positions == null || positions.getSrcfile().getTimestamp() != timestamp) { for (int i= 0; i < this.tbHistory.size(); i++) { final ElementTracepointPositions candidate= this.tbHistory.get(i); if (filePath.equals(candidate.getSrcfile().getPath()) && timestamp == candidate.getSrcfile().getTimestamp() ) { positions= candidate; break; } } } } if (positions == null) { return 0; } updateTBHistory(positions); final long frameP= argValuesP[1]; if (this.rEngine.rniExpType(frameP) != REXP.ENVSXP) { return 0; } this.hitBreakpointState= null; this.hitBreakpointSrcref= this.rEngine.rniGetIntArray(srcrefP); if (this.hitBreakpointSrcref == null || this.hitBreakpointSrcref.length < 6) { return 0; } final TracepointPosition position= getTBPosition(positions, this.hitBreakpointSrcref); if (position == null) { return 0; } TracepointState tracepointState= null; synchronized (this.tracepointStateMap) { final List<TracepointState> list= this.tracepointStateMap.get(filePath); if (list != null) { tracepointState= getBState(list, position.getId()); } } if (tracepointState == null || tracepointState.getType() != Tracepoint.TYPE_TB) { LOGGER.log(Level.FINE, "Skipping breakpoint because of missing state."); return 0; } if (!tracepointState.isEnabled()) { // LOGGER.log(Level.FINER, "Skipping breakpoint because it is disabled."); return 0; } this.rEngine.rniSetDebug(frameP, 1); this.hitBreakpointState= tracepointState; this.hitBreakpointFlags= 0; this.hitBreakpointSrcfile= srcfileP; this.hitBreakpointFrameP= frameP; return 0; } public long checkEB(final long argsP) { this.dbg.beginSafeMode(); try { final int nFrame= this.dbg.getNFrame(); if (nFrame <= 1) { return 0; } final int suspendedNFrame= this.dbg.getSuspendedNFrame(); final long suspendedFrame= this.dbg.getSuspendedFrame(); final long frameP= this.rEngine.rniEval(this.getTraceExprEnvCallP, this.rni.rniSafeBaseExecEnvP ); if (frameP != 0) { if (nFrame - 1 == suspendedNFrame && frameP == suspendedFrame) { return 0; } if ((nFrame == 4 && frameP == this.rni.Global_EnvP) || (nFrame - 4 == suspendedFrame && frameP == suspendedFrame) ) { final long callP= this.rEngine.rniEval(this.getRootEvalCallCallP, this.rni.rniSafeGlobalExecEnvP ); if (callP != 0) { final String call= this.rni.getSourceLine(callP); if (call != null && call.startsWith("rj:::")) { return 0; } } } } TracepointState tracepointState= null; final int flags= 0; synchronized (this.tracepointStateMap) { final List<TracepointState> list= this.tracepointStateMap.get(TracepointState.EB_FILEPATH); if (list != null) { for (final TracepointState state : list) { if (state.getElementId().equals("*")) { tracepointState= state; break; } } } } if (tracepointState == null) { LOGGER.log(Level.FINE, "Skipping exception because of missing state."); return 0; } if (!tracepointState.isEnabled()) { return 0; } try { final long browserExprP= this.rni.checkAndProtect(this.rEngine.rniDuplicate( this.browserExprP )); return browserExprP; } catch (final RNullPointerException e) { LOGGER.log(Level.WARNING, "Failed to create browser expression.", e); return this.browserExprP; } finally { this.hitBreakpointState= tracepointState; this.hitBreakpointFlags= flags; this.hitBreakpointSrcfile= 0; this.hitBreakpointSrcref= null; this.hitBreakpointFrameP= frameP; } } finally { this.dbg.endSafeMode(); } } private long getTracepointEvalExpr(final TracepointState breakpointState) { final String expr= breakpointState.getExpr(); if (expr == null) { return 0; } final TracepointStateWithData stateWithData= (breakpointState instanceof TracepointStateWithData) ? (TracepointStateWithData) breakpointState : new TracepointStateWithData(breakpointState); boolean parsed= false; long exprP= stateWithData.getExprP(); if (exprP == 0 && (stateWithData.getFlags() & TracepointState.FLAG_EXPR_INVALID) == 0) { parsed= true; exprP= this.rEngine.rniParse("{\n" + expr + "\n}", 1); //$NON-NLS-1$ //$NON-NLS-2$ if (exprP != 0) { exprP= this.rEngine.rniGetVectorElt(exprP, 0); } if (exprP == 0) { // sendNotification(notification); } } if (exprP != 0) { this.rni.protect(exprP); } if (parsed || stateWithData != breakpointState) { synchronized (this.tracepointStateMap) { if (parsed) { stateWithData.setParsedExpr(exprP); } final List<TracepointState> list= this.tracepointStateMap.get(breakpointState.getFilePath()); if (list != null) { for (int i= 0; i < list.size(); i++) { final TracepointState state= list.get(i); if (state == breakpointState) { this.rEngine.rniPreserve(exprP); list.set(i, stateWithData); break; } else if (state == stateWithData) { this.rEngine.rniPreserve(exprP); break; } } } } } return exprP; } void handleSuspended(final long srcrefP) { final TracepointState state= this.hitBreakpointState; if (state != null) { this.hitBreakpointState= null; if (state.getType() == Tracepoint.TYPE_EB) { this.dbg.sendNotification(new TracepointEvent(TracepointEvent.KIND_ABOUT_TO_HIT, state.getType(), state.getFilePath(), state.getId(), state.getElementId(), this.hitBreakpointFlags, null )); } else if (srcrefP != 0 && Arrays.equals(this.hitBreakpointSrcref, this.rEngine.rniGetIntArray(srcrefP))) { // compare srcfile? this.dbg.sendNotification(new TracepointEvent(TracepointEvent.KIND_ABOUT_TO_HIT, state.getType(), state.getFilePath(), state.getId(), state.getElementLabel(), this.hitBreakpointFlags, null )); } } } void handleCancelled() { this.hitBreakpointState= null; } }