Skip to content

Commit

Permalink
* Fixed issues preventing the use of custom class-typed PhosphorOptio…
Browse files Browse the repository at this point in the history
…n and instrumenting a Java 9+ JVM with the Maven plugin
  • Loading branch information
katherine-hough committed Nov 22, 2023
1 parent 4833ab0 commit 159f5fe
Show file tree
Hide file tree
Showing 5 changed files with 126 additions and 117 deletions.
57 changes: 0 additions & 57 deletions phosphor-instrument-jigsaw/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -40,35 +40,6 @@
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.1.1</version>
<configuration>
<skip>true</skip>
</configuration>
<executions>
<execution>
<id>attach-javadoc</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-plugin-plugin</artifactId>
<version>3.6.0</version>
<executions>
<execution>
<id>mojo-descriptor</id>
<goals>
<goal>descriptor</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
Expand Down Expand Up @@ -119,34 +90,6 @@
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>release-sign-artifacts</id>
<activation>
<property>
<name>gpg.passphrase</name>
</property>
</activation>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId>
<version>1.4</version>
<executions>
<execution>
<id>sign-artifacts</id>
<phase>verify</phase>
<goals>
<goal>sign</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
<dependencies>
<dependency>
<groupId>edu.gmu.swe.phosphor</groupId>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,64 +1,78 @@
package edu.columbia.cs.psl.jigsaw.phosphor.instrumenter;

import edu.columbia.cs.psl.phosphor.Configuration;
import edu.columbia.cs.psl.phosphor.PhosphorOption;
import edu.columbia.cs.psl.phosphor.org.apache.commons.cli.Option;
import edu.columbia.cs.psl.phosphor.org.apache.commons.cli.Options;

import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.HashSet;
import java.util.Properties;
import java.util.Set;
import java.util.stream.Collectors;

public class JLinkInvoker {

public static final String MODULES_PROPERTY = "jvmModules";
public static final String PACK_KEY = "phosphor.pack";

public static void invokeJLink(File jvmDir, File instJVMDir, Properties properties) {

String jlinkBin = jvmDir + File.separator + "bin" + File.separator + "jlink";
File jlinkFile = getPhosphorJLinkJarFile();
File jlinkFile = getClassPathElement(JLinkInvoker.class);
String modulesToAdd = properties.getProperty(MODULES_PROPERTY,
"java.base,jdk.jdwp.agent,java.instrument,jdk.unsupported");

Set<String> classPaths = new HashSet<>();
if (Configuration.PRIOR_CLASS_VISITOR != null) {
classPaths.add(Configuration.PRIOR_CLASS_VISITOR.getProtectionDomain().getCodeSource().getLocation().getPath());
}
if (Configuration.POST_CLASS_VISITOR != null) {
classPaths.add(Configuration.POST_CLASS_VISITOR.getProtectionDomain().getCodeSource().getLocation().getPath());
}
if (Configuration.taintTagFactoryPackage != null) {
classPaths.add(Configuration.taintTagFactory.getClass().getProtectionDomain().getCodeSource().getLocation().getPath());
}

ProcessBuilder pb = new ProcessBuilder(jlinkBin, "-J-javaagent:" + jlinkFile,
String classPath = buildClassPath(properties);
ProcessBuilder pb = new ProcessBuilder(
jlinkBin,
String.format("-J-D%s=%s", PACK_KEY, classPath),
"-J-javaagent:" + jlinkFile,
"-J--module-path=" + jlinkFile,
"-J--add-modules=edu.columbia.cs.psl.jigsaw.phosphor.instrumenter",
"-J--class-path=" + String.join(":", classPaths),
"-J--class-path=" + classPath,
"--output=" + instJVMDir,
"--phosphor-transformer=transform" + createPhosphorJLinkPluginArgument(properties),
"--add-modules=" + modulesToAdd
);
try {
for(String s : pb.command()){
System.out.print(s + " ");
}
System.out.println();
System.out.println(String.join(" ", pb.command()));
Process p = pb.inheritIO().start();
p.waitFor();
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
}

/**
* @return a File object pointing to the JAR file for Phosphor-jlink bridge
*/
public static File getPhosphorJLinkJarFile() {
try {
return new File(JLinkInvoker.class.getProtectionDomain().getCodeSource().getLocation().toURI());
} catch (URISyntaxException e) {
throw new AssertionError();
private static String buildClassPath(Properties properties) {
Set<Class<?>> classes = new HashSet<>();
Options options = PhosphorOption.createOptions(false);
for (Option option : options.getOptions()) {
if (option.getType().equals(Class.class)) {
String key = option.getOpt();
if (properties.containsKey(key)) {
try {
Class<?> clazz = Class.forName(properties.getProperty(key));
classes.add(clazz);
} catch (ReflectiveOperationException e) {
String message = String.format("Failed to create %s class: %s", key, properties.getProperty(key));
throw new IllegalArgumentException(message, e);
}
}
}
}
if (Configuration.PRIOR_CLASS_VISITOR != null) {
classes.add(Configuration.PRIOR_CLASS_VISITOR);
}
if (Configuration.POST_CLASS_VISITOR != null) {
classes.add(Configuration.POST_CLASS_VISITOR);
}
if (Configuration.taintTagFactoryPackage != null) {
classes.add(Configuration.taintTagFactory.getClass());
}
return classes.stream()
.map(JLinkInvoker::getClassPathElement)
.map(File::getAbsolutePath)
.collect(Collectors.joining(File.pathSeparator));
}

/**
Expand All @@ -74,7 +88,7 @@ public static String createPhosphorJLinkPluginArgument(Properties properties) {
StringBuilder builder = new StringBuilder();
Set<String> propNames = properties.stringPropertyNames();
for (String propName : propNames) {
if(propName.equals(MODULES_PROPERTY)) {
if (propName.equals(MODULES_PROPERTY)) {
continue;
}
builder.append(':');
Expand All @@ -84,4 +98,12 @@ public static String createPhosphorJLinkPluginArgument(Properties properties) {
return builder.toString();
}
}

public static File getClassPathElement(Class<?> clazz) {
try {
return new File(clazz.getProtectionDomain().getCodeSource().getLocation().toURI());
} catch (URISyntaxException e) {
throw new AssertionError();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,12 @@
import jdk.tools.jlink.plugin.ResourcePoolEntry;

import java.io.File;
import java.net.URISyntaxException;
import java.util.*;


public class PhosphorJLinkPlugin implements Plugin {
public static final String NAME = "phosphor-transformer";
private static File phosphorJar;
private Set<File> elementsToPack;

@Override
public boolean hasArguments() {
Expand All @@ -31,15 +30,15 @@ public Category getType() {
* created arguments
* @return an array formatted for {@link Instrumenter#main(String[])} Instrumenter.main's} String[] argument
*/
public static String[] createPhosphorMainArguments(Map<String,String> properties) {
public static String[] createPhosphorMainArguments(Map<String, String> properties) {
LinkedList<String> arguments = new LinkedList<>();
Set<String> propNames = properties.keySet();
for(String propName : propNames) {
if(propName.equals("phosphor-transformer")){
for (String propName : propNames) {
if (propName.equals("phosphor-transformer")) {
continue;
}
arguments.addLast("-" + propName);
if(!"true".equals(properties.get(propName))) {
if (!"true".equals(properties.get(propName))) {
arguments.addLast(properties.get(propName));
}
}
Expand All @@ -54,16 +53,24 @@ public void configure(Map<String, String> config) {
TaintTrackingClassVisitor.IS_RUNTIME_INST = false;
TaintUtils.VERIFY_CLASS_GENERATION = true;
PhosphorOption.configure(false, createPhosphorMainArguments(config));
phosphorJar = getPhosphorJarFile();
File phosphorJar = JLinkInvoker.getClassPathElement(Instrumenter.class);
System.out.println("Embedding Phosphor from: " + phosphorJar);
String pack = System.getProperty(JLinkInvoker.PACK_KEY);
this.elementsToPack = new HashSet<>();
elementsToPack.add(phosphorJar);
if (pack != null && !pack.isEmpty()) {
Arrays.stream(pack.split(File.pathSeparator))
.map(File::new)
.forEach(elementsToPack::add);
}
//TODO process args
}

@Override
public ResourcePool transform(ResourcePool in, ResourcePoolBuilder out) {
TaintUtils.VERIFY_CLASS_GENERATION = true;
PreMain.RUNTIME_INST = false;
PhosphorPacker packer = new PhosphorPacker(in, phosphorJar);
PhosphorPacker packer = new PhosphorPacker(in, elementsToPack);
in.transformAndCopy((resourcePoolEntry) -> {
if (resourcePoolEntry.type().equals(ResourcePoolEntry.Type.CLASS_OR_RESOURCE)) {
if (resourcePoolEntry.path().endsWith(".class")) {
Expand All @@ -74,7 +81,8 @@ public ResourcePool transform(ResourcePool in, ResourcePoolBuilder out) {
resourcePoolEntry = packer.pack(resourcePoolEntry, out);
}
} else {
byte[] newContent = Instrumenter.instrumentClass(resourcePoolEntry.path(), resourcePoolEntry.content(), true);
byte[] newContent = Instrumenter.instrumentClass(resourcePoolEntry.path(),
resourcePoolEntry.content(), true);
if (newContent != null) {
resourcePoolEntry = resourcePoolEntry.copyWithContent(newContent);
}
Expand All @@ -95,17 +103,6 @@ public String getName() {
public String getDescription() {
return "Transforms the runtime image to be compatible with Phosphor";
}

/**
* @return a File object pointing to the JAR file for Phosphor
*/
public static File getPhosphorJarFile() {
try {
return new File(Instrumenter.class.getProtectionDomain().getCodeSource().getLocation().toURI());
} catch(URISyntaxException e) {
throw new AssertionError();
}
}
}


Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,22 @@
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Set;
import java.util.stream.Stream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

class PhosphorPacker {
private final File phosphorJar;
private final PhosphorPatcher patcher;
private final Set<File> elementsToPack;

PhosphorPacker(ResourcePool pool, File phosphorJar) {
if (phosphorJar == null) {
throw new NullPointerException();
}
this.phosphorJar = phosphorJar;
PhosphorPacker(ResourcePool pool, Set<File> elementsToPack) {
this.elementsToPack = Collections.unmodifiableSet(new HashSet<>(elementsToPack));
ResourcePoolEntry entry = pool.findEntry("/java.base/jdk/internal/misc/Unsafe.class")
.orElseThrow(() -> new IllegalArgumentException("Unable to find entry for jdk/internal/misc/Unsafe"));
try (InputStream in = entry.content()) {
Expand Down Expand Up @@ -53,10 +54,42 @@ ResourcePoolEntry pack(ResourcePoolEntry entry, ResourcePoolBuilder out) {
}

private Set<String> packClasses(ResourcePoolBuilder out) throws IOException {
// Pack the Phosphor JAR into the resource pool
// Pack the JARs and directories into the resource pool
// Return the set of packages for packed classes
Set<String> packages = new HashSet<>();
try (ZipFile zip = new ZipFile(phosphorJar)) {
for (File element : elementsToPack) {
if (element.isDirectory()) {
packDirectory(out, element, packages);
} else {
packJar(out, element, packages);
}
}
return packages;
}

private void packClass(ResourcePoolBuilder out, String name, File classFile, Set<String> packages) {
try {
if (shouldInclude(name)) {
byte[] content = patcher.patch(name, Files.readAllBytes(classFile.toPath()));
out.add(ResourcePoolEntry.create("/java.base/" + name, content));
packages.add(name.substring(0, name.lastIndexOf('/')));
}
} catch (IOException e) {
throw new IllegalArgumentException("Failed to pack: " + name, e);
}
}

private void packDirectory(ResourcePoolBuilder out, File directory, Set<String> packages) throws IOException {
try (Stream<Path> walk = Files.walk(directory.toPath())) {
walk.filter(Files::isRegularFile)
.forEach(p -> packClass(out, directory.toPath().relativize(p).toFile().getPath(),
p.toAbsolutePath().toFile(),
packages));
}
}

private void packJar(ResourcePoolBuilder out, File element, Set<String> packages) throws IOException {
try (ZipFile zip = new ZipFile(element)) {
Enumeration<? extends ZipEntry> entries = zip.entries();
while (entries.hasMoreElements()) {
ZipEntry entry = entries.nextElement();
Expand All @@ -69,6 +102,5 @@ private Set<String> packClasses(ResourcePoolBuilder out) throws IOException {
}
}
}
return packages;
}
}
17 changes: 16 additions & 1 deletion phosphor-instrument-jigsaw/src/main/java/module-info.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,23 @@
module edu.columbia.cs.psl.jigsaw.phosphor.instrumenter {
exports edu.columbia.cs.psl.jigsaw.phosphor.instrumenter;
opens edu.columbia.cs.psl.phosphor.instrumenter;
opens edu.columbia.cs.psl.jigsaw.phosphor.instrumenter;
opens edu.columbia.cs.psl.phosphor.instrumenter.analyzer;
opens edu.columbia.cs.psl.phosphor.struct.harmony.util;
opens edu.columbia.cs.psl.phosphor.control.graph;
opens edu.columbia.cs.psl.phosphor.instrumenter;
opens edu.columbia.cs.psl.phosphor.runtime.jdk.unsupported;
opens edu.columbia.cs.psl.phosphor.control.type;
opens edu.columbia.cs.psl.phosphor.org.objectweb.asm.tree;
opens edu.columbia.cs.psl.phosphor.org.objectweb.asm.commons;
opens edu.columbia.cs.psl.phosphor.struct;
opens edu.columbia.cs.psl.phosphor.control.standard;
opens edu.columbia.cs.psl.phosphor.instrumenter.asm;
opens edu.columbia.cs.psl.phosphor.control;
opens edu.columbia.cs.psl.phosphor.org.objectweb.asm.analysis;
opens edu.columbia.cs.psl.phosphor;
opens edu.columbia.cs.psl.phosphor.org.objectweb.asm;
opens edu.columbia.cs.psl.phosphor.runtime;
opens edu.columbia.cs.psl.phosphor.runtime.proxied;
requires jdk.jlink;
requires java.instrument;
}

0 comments on commit 159f5fe

Please sign in to comment.