Skip to content

Use the correct runtime to validate the JVM versions between the debugger and debuggee #353

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Sep 24, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,14 @@ public interface ISourceLookUpProvider extends IProvider {
String getSourceFileURI(String fullyQualifiedName, String sourcePath);

String getSourceContents(String uri);

/**
* Returns the Java runtime that the specified project's build path used.
* @param projectName
* the specified project name
* @return the Java runtime version the specified project used. null if projectName is empty or doesn't exist.
*/
default String getJavaRuntimeVersion(String projectName) {
return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@
import com.microsoft.java.debug.core.protocol.Requests.Command;
import com.sun.jdi.connect.IllegalConnectorArgumentsException;

import org.apache.commons.lang3.StringUtils;

public class AttachRequestHandler implements IDebugRequestHandler {
private static final Logger logger = Logger.getLogger(Configuration.LOGGER_NAME);
private VMHandler vmHandler = new VMHandler();
Expand All @@ -59,28 +61,14 @@ public CompletableFuture<Response> handle(Command command, Arguments arguments,

IVirtualMachineManagerProvider vmProvider = context.getProvider(IVirtualMachineManagerProvider.class);
vmHandler.setVmProvider(vmProvider);

IDebugSession debugSession = null;
try {
logger.info(String.format("Trying to attach to remote debuggee VM %s:%d .", attachArguments.hostName, attachArguments.port));
IDebugSession debugSession = DebugUtility.attach(vmProvider.getVirtualMachineManager(), attachArguments.hostName, attachArguments.port,
debugSession = DebugUtility.attach(vmProvider.getVirtualMachineManager(), attachArguments.hostName, attachArguments.port,
attachArguments.timeout);
context.setDebugSession(debugSession);
vmHandler.connectVirtualMachine(debugSession.getVM());
logger.info("Attaching to debuggee VM succeeded.");

// If the debugger and debuggee run at the different JVM platforms, show a warning message.
if (debugSession != null) {
String debuggeeVersion = debugSession.getVM().version();
String debuggerVersion = System.getProperty("java.version");
if (!debuggerVersion.equals(debuggeeVersion)) {
String warnMessage = String.format("[Warn] The debugger and the debuggee are running in different versions of JVMs. "
+ "You could see wrong source mapping results.\n"
+ "Debugger JVM version: %s\n"
+ "Debuggee JVM version: %s", debuggerVersion, debuggeeVersion);
logger.warning(warnMessage);
context.getProtocolServer().sendEvent(Events.OutputEvent.createConsoleOutput(warnMessage));
}
}
} catch (IOException | IllegalConnectorArgumentsException e) {
throw AdapterUtils.createCompletionException(
String.format("Failed to attach to remote debuggee VM. Reason: %s", e.toString()),
Expand All @@ -96,6 +84,20 @@ public CompletableFuture<Response> handle(Command command, Arguments arguments,
// TODO: Clean up the initialize mechanism
ISourceLookUpProvider sourceProvider = context.getProvider(ISourceLookUpProvider.class);
sourceProvider.initialize(context, options);
// If the debugger and debuggee run at the different JVM platforms, show a warning message.
if (debugSession != null) {
String debuggeeVersion = debugSession.getVM().version();
String debuggerVersion = sourceProvider.getJavaRuntimeVersion(attachArguments.projectName);
if (StringUtils.isNotBlank(debuggerVersion) && !debuggerVersion.equals(debuggeeVersion)) {
String warnMessage = String.format("[Warn] The debugger and the debuggee are running in different versions of JVMs. "
+ "You could see wrong source mapping results.\n"
+ "Debugger JVM version: %s\n"
+ "Debuggee JVM version: %s", debuggerVersion, debuggeeVersion);
logger.warning(warnMessage);
context.getProtocolServer().sendEvent(Events.OutputEvent.createConsoleOutput(warnMessage));
}
}

IEvaluationProvider evaluationProvider = context.getProvider(IEvaluationProvider.class);
evaluationProvider.initialize(context, options);
IHotCodeReplaceProvider hcrProvider = context.getProvider(IHotCodeReplaceProvider.class);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2017 Microsoft Corporation and others.
* Copyright (c) 2017-2020 Microsoft Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
Expand All @@ -22,28 +22,37 @@
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
import java.util.jar.Attributes;
import java.util.jar.Manifest;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.microsoft.java.debug.core.Configuration;
import com.microsoft.java.debug.core.DebugException;
import com.microsoft.java.debug.core.adapter.AdapterUtils;
import com.microsoft.java.debug.core.adapter.Constants;
import com.microsoft.java.debug.core.adapter.IDebugAdapterContext;
import com.microsoft.java.debug.core.adapter.ISourceLookUpProvider;

import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.debug.core.sourcelookup.ISourceContainer;
import org.eclipse.jdt.core.IBuffer;
import org.eclipse.jdt.core.IClassFile;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.ITypeRoot;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.ASTParser;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.internal.core.JarPackageFragmentRoot;
import org.eclipse.jdt.internal.debug.core.breakpoints.ValidBreakpointLocationLocator;

import com.microsoft.java.debug.core.Configuration;
import com.microsoft.java.debug.core.DebugException;
import com.microsoft.java.debug.core.adapter.AdapterUtils;
import com.microsoft.java.debug.core.adapter.Constants;
import com.microsoft.java.debug.core.adapter.IDebugAdapterContext;
import com.microsoft.java.debug.core.adapter.ISourceLookUpProvider;
import org.eclipse.jdt.launching.IVMInstall;
import org.eclipse.jdt.launching.JavaRuntime;
import org.eclipse.jdt.launching.LibraryLocation;

public class JdtSourceLookUpProvider implements ISourceLookUpProvider {
private static final Logger logger = Logger.getLogger(Configuration.LOGGER_NAME);
Expand Down Expand Up @@ -181,6 +190,25 @@ public String getSourceFileURI(String fullyQualifiedName, String sourcePath) {
return null;
}

@Override
public String getJavaRuntimeVersion(String projectName) {
IJavaProject project = JdtUtils.getJavaProject(projectName);
if (project != null) {
try {
IVMInstall vmInstall = JavaRuntime.getVMInstall(project);
if (vmInstall == null || vmInstall.getInstallLocation() == null) {
return null;
}

return resolveSystemLibraryVersion(project, vmInstall);
} catch (CoreException e) {
logger.log(Level.SEVERE, "Failed to get Java runtime version for project '" + projectName + "': " + e.getMessage(), e);
}
}

return null;
}

/**
* Get the project associated source containers.
* @return the initialized source container list
Expand Down Expand Up @@ -280,4 +308,21 @@ private static String readFile(String filePath, Charset cs) {
return builder.toString();
}

private static String resolveSystemLibraryVersion(IJavaProject project, IVMInstall vmInstall) throws JavaModelException {
LibraryLocation[] libraries = JavaRuntime.getLibraryLocations(vmInstall);
if (libraries != null && libraries.length > 0) {
IPackageFragmentRoot root = project.findPackageFragmentRoot(libraries[0].getSystemLibraryPath());
if (!(root instanceof JarPackageFragmentRoot)) {
return null;
}
Manifest manifest = ((JarPackageFragmentRoot) root).getManifest();
if (manifest == null) {
return null;
}
Attributes attributes = manifest.getMainAttributes();
return attributes.getValue("Implementation-Version");
}

return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
package com.microsoft.java.debug.plugin.internal;

import java.io.File;
import java.nio.file.Paths;
import java.util.List;
import java.util.Objects;
import java.util.logging.Level;
Expand Down Expand Up @@ -61,28 +60,35 @@ public static String resolveJavaExecutable(List<Object> arguments) throws Except
}
}

if (targetProject == null) {
return null;
}

IVMInstall vmInstall = JavaRuntime.getVMInstall(targetProject);
if (vmInstall == null || vmInstall.getInstallLocation() == null) {
return null;
}

File exe = findJavaExecutable(vmInstall.getInstallLocation());
if (exe == null) {
return null;
}

return exe.getAbsolutePath();
return resolveJavaExecutable(targetProject);
} catch (CoreException e) {
logger.log(Level.SEVERE, "Failed to resolve java executable: " + e.getMessage(), e);
}

return null;
}

/**
* Resolve the Java executable path from the project's Java runtime.
*/
public static String resolveJavaExecutable(IJavaProject javaProject) throws CoreException {
if (javaProject == null) {
return null;
}

IVMInstall vmInstall = JavaRuntime.getVMInstall(javaProject);
if (vmInstall == null || vmInstall.getInstallLocation() == null) {
return null;
}

File exe = findJavaExecutable(vmInstall.getInstallLocation());
if (exe == null) {
return null;
}

return exe.getAbsolutePath();
}

private static File findJavaExecutable(File vmInstallLocation) {
boolean isBin = Objects.equals("bin", vmInstallLocation.getName());
for (int i = 0; i < javaExecCandidates.length; i++) {
Expand All @@ -91,8 +97,7 @@ private static File findJavaExecutable(File vmInstallLocation) {
continue;
}

String execRelativePath = j == 0 ? javaExecCandidates[i] : Paths.get(javaBinCandidates[j], javaExecCandidates[i]).toString();
File javaFile = new File(vmInstallLocation, execRelativePath);
File javaFile = new File(vmInstallLocation, javaBinCandidates[j] + javaExecCandidates[i]);
if (javaFile.isFile()) {
return javaFile;
}
Expand Down