--- a/debugger.jpda.projects/src/org/netbeans/modules/debugger/jpda/projects/AST2Bytecode.java +++ a/debugger.jpda.projects/src/org/netbeans/modules/debugger/jpda/projects/AST2Bytecode.java @@ -58,6 +58,8 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; +import javax.lang.model.element.Element; +import javax.lang.model.element.Modifier; import javax.lang.model.element.TypeElement; import javax.lang.model.element.TypeParameterElement; import javax.lang.model.type.ArrayType; @@ -150,6 +152,10 @@ if (from < to) { // We have the method call TreePath nodePath = trees.getPath(cu, node); if (nodePath != null && !ci.getTreeUtilities().isSynthetic(nodePath)) { + // Find if 'node' is a native method: + Element methodElement = ci.getTrees().getElement(nodePath); + boolean isNativeMethod = (methodElement != null) && methodElement.getModifiers().contains(Modifier.NATIVE); + //System.err.println("Method "+methodElement+" is native = "+isNativeMethod); String methodNameInBytecode = null; String methodDescriptorInBytecode = null; if (constantPool != null) { @@ -358,7 +364,8 @@ methodEndPosition, methodName, methodClassType, - from + from, + isNativeMethod ); try { java.lang.reflect.Field methodDescriptorField = EditorContext.Operation.class.getDeclaredField("methodDescriptor"); @@ -606,7 +613,7 @@ EditorContext.Position methodStartPosition, EditorContext.Position methodEndPosition, String methodName, String methodClassType, - int bytecodeIndex); + int bytecodeIndex, boolean isNative); EditorContext.Position createPosition(int offset, int line, int column); --- a/debugger.jpda.projects/src/org/netbeans/modules/debugger/jpda/projects/EditorContextImpl.java +++ a/debugger.jpda.projects/src/org/netbeans/modules/debugger/jpda/projects/EditorContextImpl.java @@ -2969,7 +2969,8 @@ Position methodEndPosition, String methodName, String methodClassType, - int bytecodeIndex) { + int bytecodeIndex, + boolean isNative) { return EditorContextImpl.this.createMethodOperation( startPosition, endPosition, @@ -2977,7 +2978,7 @@ methodEndPosition, methodName, methodClassType, - bytecodeIndex); + bytecodeIndex, isNative); } @Override public Position createPosition( --- a/debugger.jpda/src/org/netbeans/modules/debugger/jpda/actions/MethodChooserSupport.java +++ a/debugger.jpda/src/org/netbeans/modules/debugger/jpda/actions/MethodChooserSupport.java @@ -195,11 +195,14 @@ public void doStepInto() { final int index = chooser.getSelectedIndex(); final String name = operations[index].getMethodName(); + final boolean isNative = operations[index].isNative(); + final String methodClassType = operations[index].getMethodClassType(); debugger.getRequestProcessor().post(new Runnable() { @Override public void run() { - RunIntoMethodActionProvider.doAction(debugger, name, locations[index], - expressionLines, true, MethodEntry.SELECTED); + RunIntoMethodActionProvider.doAction(debugger, name, methodClassType, isNative, + locations[index], expressionLines, true, + MethodEntry.SELECTED); } }); } @@ -376,12 +379,14 @@ if (operations.length == 1) { // do not show UI, continue directly using the selection - String name = operations[selectedIndex].getMethodName(); + Operation op = operations[selectedIndex]; + String name = op.getMethodName(); if ("".equals(name)) { - name = operations[selectedIndex].getMethodClassType(); + name = op.getMethodClassType(); } - RunIntoMethodActionProvider.doAction(debugger, name, locations[selectedIndex], - expr.getInterval(), true, MethodEntry.DIRECT); + RunIntoMethodActionProvider.doAction(debugger, name, op.getMethodClassType(), op.isNative(), + locations[selectedIndex], expr.getInterval(), + true, MethodEntry.DIRECT); return true; } return false; --- a/debugger.jpda/src/org/netbeans/modules/debugger/jpda/actions/RunIntoMethodActionProvider.java +++ a/debugger.jpda/src/org/netbeans/modules/debugger/jpda/actions/RunIntoMethodActionProvider.java @@ -52,6 +52,7 @@ import com.sun.jdi.ThreadReference; import com.sun.jdi.VMDisconnectedException; import com.sun.jdi.VirtualMachine; +import com.sun.jdi.connect.Connector; import com.sun.jdi.event.BreakpointEvent; import com.sun.jdi.event.Event; import com.sun.jdi.request.BreakpointRequest; @@ -59,25 +60,29 @@ import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.lang.reflect.InvocationTargetException; - import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Set; +import java.util.concurrent.locks.Lock; import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.SwingUtilities; import org.netbeans.api.debugger.ActionsManager; import org.netbeans.api.debugger.ActionsManagerListener; import org.netbeans.api.debugger.DebuggerManager; -import org.netbeans.spi.debugger.ContextProvider; import org.netbeans.api.debugger.Session; +import org.netbeans.api.debugger.SessionBridge; +import org.netbeans.api.debugger.jpda.AttachingDICookie; import org.netbeans.api.debugger.jpda.CallStackFrame; import org.netbeans.api.debugger.jpda.ClassLoadUnloadBreakpoint; import org.netbeans.api.debugger.jpda.JPDADebugger; -import org.netbeans.api.debugger.jpda.event.JPDABreakpointListener; -import org.netbeans.api.debugger.jpda.event.JPDABreakpointEvent; import org.netbeans.api.debugger.jpda.JPDAStep; import org.netbeans.api.debugger.jpda.JPDAThread; +import org.netbeans.api.debugger.jpda.ListeningDICookie; +import org.netbeans.api.debugger.jpda.event.JPDABreakpointEvent; +import org.netbeans.api.debugger.jpda.event.JPDABreakpointListener; import org.netbeans.modules.debugger.jpda.EditorContextBridge; import org.netbeans.modules.debugger.jpda.ExpressionPool.Expression; import org.netbeans.modules.debugger.jpda.ExpressionPool.Interval; @@ -97,18 +102,17 @@ import org.netbeans.modules.debugger.jpda.jdi.VirtualMachineWrapper; import org.netbeans.modules.debugger.jpda.jdi.request.EventRequestManagerWrapper; import org.netbeans.modules.debugger.jpda.jdi.request.EventRequestWrapper; -import org.netbeans.spi.debugger.ActionsProviderSupport; -import org.openide.DialogDisplayer; -import org.openide.NotifyDescriptor; - import org.netbeans.modules.debugger.jpda.models.CallStackFrameImpl; import org.netbeans.modules.debugger.jpda.models.JPDAThreadImpl; import org.netbeans.modules.debugger.jpda.util.Executor; import org.netbeans.spi.debugger.ActionsProvider; +import org.netbeans.spi.debugger.ActionsProviderSupport; +import org.netbeans.spi.debugger.ContextProvider; import org.netbeans.spi.debugger.jpda.EditorContext; import org.netbeans.spi.debugger.jpda.EditorContext.Operation; +import org.openide.DialogDisplayer; import org.openide.ErrorManager; - +import org.openide.NotifyDescriptor; import org.openide.util.Exceptions; import org.openide.util.NbBundle; @@ -321,6 +325,8 @@ Expression expr = debugger.getExpressionPool().getExpressionAt(locations.get(0), url); Location bpLocation = null; Interval expressionLines = null; + String methodClassType = null; + boolean isNative = false; if (expr != null) { Operation[] ops = expr.getOperations(); for (int i = 0; i < ops.length; i++) { @@ -329,6 +335,8 @@ methodOffset <= op.getMethodEndPosition().getOffset()) { bpLocation = expr.getLocations()[i]; + methodClassType = op.getMethodClassType(); + isNative = op.isNative(); break; } } @@ -337,23 +345,28 @@ if (bpLocation == null) { bpLocation = locations.get(0); } - doAction(debugger, methodName, bpLocation, expressionLines, false, doResume, - MethodChooserSupport.MethodEntry.SELECTED); + doAction(debugger, methodName, methodClassType, isNative, bpLocation, + expressionLines, false, doResume, + MethodChooserSupport.MethodEntry.SELECTED); } static boolean doAction(final JPDADebuggerImpl debugger, final String methodName, + final String methodClassType, + final boolean isNative, Location bpLocation, Interval expressionLines, // If it's important not to run far from the expression boolean setBoundaryStep, MethodChooserSupport.MethodEntry methodEntry) { - return doAction(debugger, methodName, bpLocation, expressionLines, setBoundaryStep, true, methodEntry); + return doAction(debugger, methodName, methodClassType, isNative, bpLocation, expressionLines, setBoundaryStep, true, methodEntry); } private static boolean doAction(final JPDADebuggerImpl debugger, final String methodName, + final String methodClassType, + final boolean isNative, Location bpLocation, // If it's important not to run far from the expression Interval expressionLines, @@ -395,8 +408,9 @@ logger.log(Level.FINE, "doAction() areWeOnTheLocation = {0}, methodName = {1}", new Object[]{areWeOnTheLocation, methodName}); if (areWeOnTheLocation) { // We're on the line from which the method is called - traceLineForMethod(debugger, ct.getThreadReference(), methodName, - line, doFinishWhenMethodNotFound, methodEntry); + traceLineForMethod(debugger, ct, methodName, + methodClassType, isNative, line, + doFinishWhenMethodNotFound, methodEntry); } else { final JPDAStep[] boundaryStepPtr = new JPDAStep[] { null }; // Submit the breakpoint to get to the point from which the method is called @@ -465,8 +479,9 @@ } catch (VMDisconnectedExceptionWrapper e) { return false; } - traceLineForMethod(debugger, tr, methodName, line, - doFinishWhenMethodNotFound, methodEntry); + traceLineForMethod(debugger, t, methodName, methodClassType, + isNative, line, doFinishWhenMethodNotFound, + methodEntry); return true; } @@ -545,13 +560,36 @@ } private static void traceLineForMethod(final JPDADebuggerImpl debugger, - final ThreadReference tr, + final JPDAThreadImpl jtr, final String method, + final String methodClassType, + final boolean isNative, final int methodLine, final boolean finishWhenNotFound, final MethodChooserSupport.MethodEntry methodEntry) { - final JPDAThread jtr = debugger.getThread(tr); + //ThreadReference tr = jtr.getThreadReference(); final int depth = jtr.getStackDepth(); + //System.err.println("traceLineForMethod: stepping into native method "+methodClassType+"."+method+" = "+isNative); + if (isNative) { + Map properties = new HashMap(); + properties.put("javaClass", methodClassType); + properties.put("javaMethod", method); + Session session = debugger.getSession(); + putConnectionProperties(session, properties); + final Lock writeLock = jtr.accessLock.writeLock(); + writeLock.lock(); + boolean changed = SessionBridge.getDefault().suggestChange( + session, + (String) ActionsManager.ACTION_STEP_INTO, + properties); + if (changed) { + writeLock.unlock(); + jtr.resume(); + return ; + } else { + writeLock.unlock(); + } + } final JPDAStep step = debugger.createJPDAStep(JPDAStep.STEP_LINE, JPDAStep.STEP_INTO); step.setHidden(true); logger.log(Level.FINE, "Will traceLineForMethod({0}, {1}, {2})", @@ -628,6 +666,25 @@ }); step.addStep(jtr); } + + private static void putConnectionProperties(Session session, Map properties) { + ListeningDICookie lc = session.lookupFirst(null, ListeningDICookie.class); + Map args = null; + if (lc != null) { + args = lc.getArgs(); + properties.put("conn_port", lc.getPortNumber()); + properties.put("conn_shmem", lc.getSharedMemoryName()); + } else { + AttachingDICookie ac = session.lookupFirst(null, AttachingDICookie.class); + if (ac != null) { + args = ac.getArgs(); + properties.put("conn_host", ac.getHostName()); + properties.put("conn_port", ac.getPortNumber()); + properties.put("conn_shmem", ac.getSharedMemoryName()); + properties.put("conn_pid", ac.getProcessID()); + } + } + } @Override public void actionPerformed(Object action) { --- a/cnd.debugger.common2/src/org/netbeans/modules/cnd/debugger/common2/debugger/breakpoints/NativeBreakpoint.java +++ a/cnd.debugger.common2/src/org/netbeans/modules/cnd/debugger/common2/debugger/breakpoints/NativeBreakpoint.java @@ -88,6 +88,8 @@ import org.netbeans.modules.cnd.debugger.common2.debugger.breakpoints.props.ActionProperty; import org.netbeans.modules.cnd.debugger.common2.debugger.breakpoints.props.ContextProperty; import org.netbeans.modules.cnd.debugger.common2.debugger.breakpoints.props.CountLimitProperty; +import org.netbeans.modules.cnd.debugger.common2.debugger.breakpoints.types.FunctionBreakpoint; +import org.netbeans.modules.cnd.debugger.common2.debugger.breakpoints.types.FunctionBreakpointType; import org.netbeans.modules.cnd.debugger.common2.debugger.breakpoints.types.LineBreakpoint; import org.netbeans.modules.cnd.debugger.common2.debugger.breakpoints.types.LineBreakpointType; @@ -3062,6 +3064,21 @@ } /** + * Quick and easy way to create a function bpt. + * Used for toggling. + */ + public static NativeBreakpoint newFunctionBreakpoint(String functionName) { + NativeBreakpoint bpt = + newBreakpointOfType(FunctionBreakpointType.class); + if (bpt == null) { + return null; + } + FunctionBreakpoint fb = (FunctionBreakpoint) bpt; + fb.setFunction(functionName); + return fb; + } + + /** * Returns a list of changed properties (with the new values) * @param oldBpt * @param newBpt --- a/cnd.debugger.common2/src/org/netbeans/modules/cnd/debugger/common2/debugger/mixed/SessionChangerImpl.java +++ a/cnd.debugger.common2/src/org/netbeans/modules/cnd/debugger/common2/debugger/mixed/SessionChangerImpl.java @@ -0,0 +1,146 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2014 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2014 Sun Microsystems, Inc. + */ + +package org.netbeans.modules.cnd.debugger.common2.debugger.mixed; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.Vector; +import java.util.regex.Pattern; +import org.netbeans.api.debugger.ActionsManager; +import org.netbeans.api.debugger.DebuggerManager; +import org.netbeans.api.debugger.DebuggerManagerAdapter; +import org.netbeans.api.debugger.Session; +import org.netbeans.api.debugger.SessionBridge; +import org.netbeans.modules.cnd.debugger.common2.debugger.NativeDebugger; +import org.netbeans.modules.cnd.debugger.common2.debugger.NativeDebuggerManager; +import org.netbeans.modules.cnd.debugger.common2.debugger.RoutingToken; +import org.netbeans.modules.cnd.debugger.common2.debugger.breakpoints.Handler; +import org.netbeans.modules.cnd.debugger.common2.debugger.breakpoints.NativeBreakpoint; +import org.netbeans.modules.cnd.debugger.common2.debugger.debugtarget.DebugTarget; +import org.netbeans.modules.cnd.debugger.common2.debugger.remote.Host; +import org.netbeans.modules.cnd.debugger.common2.utils.PsProvider; +import org.netbeans.spi.debugger.DebuggerServiceRegistration; +import org.openide.util.NbBundle; + +/** + * + * @author Nikolay Koldunov + */ + +@DebuggerServiceRegistration(types = SessionBridge.SessionChanger.class) +public class SessionChangerImpl implements SessionBridge.SessionChanger{ + private static final Map sessions = new HashMap(); + + @Override + public Set getActions() { + return Collections.singleton((String) ActionsManager.ACTION_STEP_INTO); + } + + @Override + public Session changeSuggested(Session origin, String action, Map properties) { + final Vector> processes = PsProvider.getDefault(Host.getLocal()).getData(false).processes( + Pattern.compile(".*java.+:" + properties.get("conn_port") + ".*") + ); + if (processes.size() != 1) { + NativeDebuggerManager.warning(NbBundle.getMessage(this.getClass(), "MSG_ProcessDetectionError")); + return null; + } + final String funcName = MethodMapper.getNativeName( + "" + properties.get("javaClass") + "." + properties.get("javaMethod")); + final String pid = processes.firstElement().get(1); + Session ret = sessions.get(pid); + if (ret == null) { + final long longPid = Long.parseLong(pid); + final DebugTarget target = new DebugTarget(); + target.setPid(longPid); + target.setHostName("localhost"); + + insertBreakpoint(null, funcName); + NativeDebuggerManager.get().attach(target); + + ret = NativeDebuggerManager.get().currentDebugger().session().coreSession(); + sessions.put(pid, ret); + } else { + insertBreakpoint(NativeDebuggerManager.get().currentDebugger(), funcName); + } + + // FIXME maybe sometimes we should return origin instead + return ret; + } + + private static void insertBreakpoint(NativeDebugger debugger, String functionName) { + NativeBreakpoint bpt = NativeBreakpoint.newFunctionBreakpoint(functionName); + if (bpt != null) { + int routingToken = RoutingToken.BREAKPOINTS.getUniqueRoutingTokenInt(); + bpt.setTemp(true); + Handler.postNewHandler(debugger, bpt, routingToken); + } + } + + private static final class MethodMapper { + /*package*/ static String getNativeName(String javaName) { + return "Java_".concat(javaName.replaceAll("[.]", "_")); + } + } + +// /*package*/ String resolveSymbol(CharSequence funcName) { +// Project[] projects = OpenProjects.getDefault().getOpenProjects(); +// for (Project prj : projects) { +// NativeProject nativeProject = prj.getLookup().lookup(NativeProject.class); +// if (nativeProject != null) { +// Collection candidates = CsmSymbolResolver.resolveSymbol(nativeProject, funcName); +// if (!candidates.isEmpty()) { +// CsmOffsetable candidate = candidates.iterator().next(); +//// CsmUtilities.openSource(candidate); +// if (CsmKindUtilities.isFunction(candidate)) { +// return ((CsmFunction) candidate).getQualifiedName().toString(); +// } +// break; +// } +// } +// } +// return null; +// } +}