Skip to content
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

Recursive dependency linking #1073

Open
wants to merge 23 commits into
base: master
Choose a base branch
from
Open
Changes from 8 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
1afdb18
Some Syntax refactoring with minimal impact except for readability
CodeByDrescher Dec 5, 2023
3f8936d
Finished prototype fix; need to test (and add unit tests)
CodeByDrescher Dec 5, 2023
c4961af
Fixing bugs; but getting nullptrexception
CodeByDrescher Dec 6, 2023
f16cea6
Added extra parsing
CodeByDrescher Dec 7, 2023
6a83283
Fixed bug where wrong link name was provided when providing an altern…
CodeByDrescher Dec 7, 2023
f6ab53b
removed old, broken method
CodeByDrescher Dec 7, 2023
f61e415
Small changes to remove all warnings.
CodeByDrescher Dec 7, 2023
04a1e73
Merge branch 'master' into RecursiveDependencyLinking
CodeByDrescher Dec 7, 2023
b22fdb0
Merge remote-tracking branch 'origin/master' into RecursiveDependency…
CodeByDrescher Dec 21, 2023
aa5e090
Refactor of Simulation-esk classes to new subfolder; No significant c…
CodeByDrescher Dec 22, 2023
dd63161
Updated Git Ignore
CodeByDrescher Jan 30, 2024
15e9169
Added null check to omex handler
CodeByDrescher Jan 30, 2024
db698d1
Upgraded solver version
CodeByDrescher Jan 30, 2024
5d6150c
Merge branch 'master' into RecursiveDependencyLinking
CodeByDrescher Jan 30, 2024
de6d02c
Removed deleted class' old import
CodeByDrescher Jan 30, 2024
a963852
Update ci_cd.yml to use 22.04
CodeByDrescher Jan 30, 2024
abb2549
Enforce docker container using Ubuntu 22.04
CodeByDrescher Jan 30, 2024
7f25fb6
Added min call to prevent StringIndexOutOfBoundsError
CodeByDrescher Jan 30, 2024
21562cc
switched os to ubuntu from debian
CodeByDrescher Jan 31, 2024
cbe3f29
Merge branch 'master' into RecursiveDependencyLinking
CodeByDrescher Jan 31, 2024
d4dd0a1
Added infrastructure to pipe error code to output
CodeByDrescher Feb 1, 2024
08d3eea
Revert "Added infrastructure to pipe error code to output"
CodeByDrescher Feb 1, 2024
5447d89
Merged in master changes
CodeByDrescher Feb 1, 2024
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
250 changes: 144 additions & 106 deletions vcell-core/src/main/java/cbit/vcell/solvers/AbstractCompiledSolver.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,22 +18,17 @@
import cbit.vcell.solver.server.SolverStatus;
import org.vcell.util.document.User;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Vector;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
* Insert the type's description here.
* Creation date: (6/26/2001 3:18:18 PM)
*
* @author: Ion Moraru
*/
public abstract class AbstractCompiledSolver extends AbstractSolver implements java.beans.PropertyChangeListener {
/**
Expand All @@ -46,14 +41,14 @@ public abstract class AbstractCompiledSolver extends AbstractSolver implements j
protected final static String DATA_PREFIX = "data:";
protected final static String PROGRESS_PREFIX = "progress:";
protected final static String SEPARATOR = ":";
protected boolean bMessaging = true;
protected boolean bMessaging;

/**
* AbstractPDESolver constructor comment.
*/
public AbstractCompiledSolver(SimulationTask simTask, File directory, boolean bMsging) throws SolverException {
public AbstractCompiledSolver(SimulationTask simTask, File directory, boolean bMessaging) throws SolverException {
super(simTask, directory);
bMessaging = bMsging;
this.bMessaging = bMessaging;
setCurrentTime(simTask.getSimulationJob().getSimulation().getSolverTaskDescription().getTimeBounds().getStartingTime());
}

Expand Down Expand Up @@ -113,13 +108,12 @@ public double getProgress() {
public void propertyChange(java.beans.PropertyChangeEvent event) {
if (event.getSource() == getMathExecutable() && event.getPropertyName().equals("applicationMessage")) {
String messageString = (String) event.getNewValue();
if (messageString == null || messageString.length() == 0) {
if (messageString == null || messageString.isEmpty()) {
return;
}
ApplicationMessage appMessage = getApplicationMessage(messageString);
if (appMessage == null) {
if (lg.isWarnEnabled()) lg.warn("AbstractCompiledSolver: Unexpected Message '" + messageString + "'");
return;
} else {
switch (appMessage.getMessageType()) {
case ApplicationMessage.PROGRESS_MESSAGE: {
Expand Down Expand Up @@ -196,96 +190,144 @@ public void runSolver() {
}

private void checkLinuxSharedLibs() throws IOException, InterruptedException {
if (OperatingSystemInfo.getInstance().isLinux()) {
File localSimDir = ResourceUtil.getLocalSimDir(User.tempUser.getName());
File mySolverLinkDir = new File(localSimDir, simTask.getSimKey().toString() + ResourceUtil.LOCAL_SOLVER_LIB_LINK_SUFFIX);
if (mySolverLinkDir.exists()) {
File[] temp = mySolverLinkDir.listFiles();
for (int i = 0; i < temp.length; i++) {
temp[i].delete();
}
} else {
mySolverLinkDir.mkdir();
}
SolverDescription mySolverDescription = getSimulationJob().getSimulation().getSolverTaskDescription().getSolverDescription();
File localSolverPath = SolverUtilities.getExes(mySolverDescription)[0];
Path linkSolver = createSymbolicLink(mySolverLinkDir, localSolverPath.getName(), localSolverPath);
final String LD_LIB_PATH = "LD_LIBRARY_PATH";
String newLD_LIB_PATH = mySolverLinkDir.getAbsolutePath();
File solversDir = ResourceUtil.getLocalSolversDirectory();
// System.out.println("-----reading solverdir libs "+solversDir.getAbsolutePath());
ArrayList<File> tempAL = new ArrayList<File>();
File[] temp = solversDir.listFiles();
for (int i = 0; i < temp.length; i++) {
if (temp[i].getName().startsWith("lib") && temp[i].isFile() && temp[i].length() != 0 && !Files.isSymbolicLink(temp[i].toPath())) {
tempAL.add(temp[i]);
// System.out.println(temp[i].getName());
}
}
File[] libzipSolverFiles = (File[]) tempAL.toArray(new File[0]);
ProcessBuilder pb = new ProcessBuilder("ldd", linkSolver.toString());
pb.redirectErrorStream(true);
Process p = pb.start();
int ioByte = -1;
StringBuffer sb = new StringBuffer();
while ((ioByte = p.getInputStream().read()) != -1) {
sb.append((char) ioByte);
}
p.waitFor();
// System.out.println("-----ldd output:\n"+sb.toString());

//Try to save output to file for cli without interfering with further processing
File stdOutFile = new File(String.valueOf(Paths.get(System.getProperty("user.dir"))), "stdOut.txt");
try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(stdOutFile))){
bos.write(sb.toString().getBytes());
}catch(Exception e) {
lg.error(e.getMessage(), e);
if (OperatingSystemInfo.getInstance().isMac()) {
lg.trace(String.format("Setting env var `%s` to: \"%s\"", "HDF5_DISABLE_VERSION_CHECK", "1"));
getMathExecutable().addEnvironmentVariable("HDF5_DISABLE_VERSION_CHECK", "1");
return;
}
if (!OperatingSystemInfo.getInstance().isLinux()) return;

// Delete remnants of last run (or create a fresh link directory)
File localSimDir = ResourceUtil.getLocalSimDir(User.tempUser.getName());
File mySolverLinkDir = new File(localSimDir, simTask.getSimKey().toString() + ResourceUtil.LOCAL_SOLVER_LIB_LINK_SUFFIX);
if (!mySolverLinkDir.exists()) {
boolean ignored = mySolverLinkDir.mkdir();
}
File[] temp = mySolverLinkDir.listFiles();
if (temp == null) throw new NullPointerException();
for (File file : temp) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in the latest java, null pointer exceptions give nice contextual messages. I you are going to throw a raw NullPointerException, then you might as well let the for loop fail with a nicely formatted NullPointerException.

boolean ignored = file.delete();
}

// Prepare for Linkage
SolverDescription mySolverDescription = getSimulationJob().getSimulation().getSolverTaskDescription().getSolverDescription();
File localSolverPath = SolverUtilities.getExes(mySolverDescription)[0];

// Perform linkage
this.linkAllDependenciesOf(localSolverPath, localSolverPath.getParentFile(), mySolverLinkDir);
lg.trace(String.format("Setting env var `%s` to: \"%s\"", "LD_LIBRARY_PATH", mySolverLinkDir.getAbsolutePath()));
getMathExecutable().addEnvironmentVariable("LD_LIBRARY_PATH", mySolverLinkDir.getAbsolutePath());

}

private void linkAllDependenciesOf(File dependency, File sourceDirectory, File targetLinkDirectory) throws IOException, InterruptedException {
File[] directoryContents = sourceDirectory.listFiles();
if (directoryContents == null) throw new NullPointerException();
Set<File> allPotentialDependencies = Stream.of(directoryContents)
.filter(AbstractCompiledSolver::isDependency).collect(Collectors.toSet());
Map<String, File> dependencyToFileMapping = new HashMap<>();
for (File dep : allPotentialDependencies){
dependencyToFileMapping.put(dep.getName(), dep);
}
Map<String, String> setOfDepsToLink = this.getDependenciesNeeded(dependency, dependencyToFileMapping, new HashSet<>());

for (String depName : setOfDepsToLink.keySet()){
Path ignored = AbstractCompiledSolver.createSymbolicLink(targetLinkDirectory, depName, dependencyToFileMapping.get(setOfDepsToLink.get(depName)));
}
}

private Map<String, String> getDependenciesNeeded(String startingDependencyName, Map<String, File> availableDependencies,
Set<String> alreadyProcessedDependencies) throws IOException, InterruptedException {
if (availableDependencies.containsKey(startingDependencyName)) {
return this.getDependenciesNeeded(
availableDependencies.get(startingDependencyName), availableDependencies, alreadyProcessedDependencies);
} else {
lg.warn("Warning: exact dependency not found by name; trying to stitch together an alternative.");
for (String alternate : availableDependencies.keySet()){
if (!startingDependencyName.startsWith(alternate)) continue;
Path stichedPath = Paths.get(availableDependencies.get(alternate).getParent(), startingDependencyName);
lg.info("Alternative found; attempting to link stitched dependency.");
return this.getDependenciesNeeded(stichedPath.toFile(), availableDependencies, alreadyProcessedDependencies);
}

java.io.BufferedReader br = new java.io.BufferedReader(new java.io.StringReader(sb.toString()));
String line = null;
// System.out.println("-----reading ldd:");
while ((line = br.readLine()) != null) {
// System.out.println(line);
java.util.StringTokenizer libInfo = new java.util.StringTokenizer(line, " \t");
if (libInfo.countTokens() == 4) {// "libname => libpath offset" -or- "libname => not found"
String libName = libInfo.nextToken();
String ptr = libInfo.nextToken();
String libPath = libInfo.nextToken();
String aux = libInfo.nextToken();
if (libPath.equals("not") && aux.equals("found")) {
boolean bMatch = false;
for (int i = 0; i < libzipSolverFiles.length; i++) {
if (libzipSolverFiles[i].getName().startsWith(libName) && libzipSolverFiles[i].length() != 0) {
//System.out.println(libName+" "+ptr+" "+libPath+" "+aux+" "+org.apache.commons.lang3.StringUtils.getJaroWinklerDistance("libhdf5.so",libName));
// System.out.println(libName+" "+ptr+" "+libPath+" "+aux+" match="+libzipSolverFiles[i]);
createSymbolicLink(mySolverLinkDir, libName, libzipSolverFiles[i]);
bMatch = true;
break;
}
}
if (!bMatch) {
for (int i = 0; i < libzipSolverFiles.length; i++) {
int index = libName.indexOf(".so");
if (index != -1) {
String matchName = libName.substring(0, index + 3);
if (libzipSolverFiles[i].getName().startsWith(matchName) && libzipSolverFiles[i].length() != 0) {
// System.out.println("ALTERNATE "+libName+" "+ptr+" "+libPath+" "+aux+" match="+libzipSolverFiles[i]);
createSymbolicLink(mySolverLinkDir, libName, libzipSolverFiles[i]);
break;
}
}
}
}
}
}
throw new RuntimeException("No alternatives possible for missing dependency: `" + startingDependencyName + "`");
}
}

private Map<String, String> getDependenciesNeeded(File startingDependency, Map<String, File> availableDependencies,
Set<String> alreadyProcessedDependencies) throws IOException, InterruptedException {
Map<String, String> dependenciesNeeded = new HashMap<>();
File dependency;
if (availableDependencies.containsKey(startingDependency.getName())){
dependency = availableDependencies.get(startingDependency.getName());
} else { // Determine alternative options
int locationOfExtension = startingDependency.getName().indexOf(".so");
if (locationOfExtension == -1) throw new RuntimeException("Dependency " + startingDependency.getName() + " not found in available dependencies");
lg.warn("Warning: exact dependency not found by name; searching for inexact match...");
String shortName = startingDependency.getName().substring(0, locationOfExtension + 3);
String dependencyToAdd = this.getEntryStartingWith(shortName, availableDependencies.keySet());
if (dependencyToAdd == null) throw new RuntimeException("No alternatives possible for missing dependency: `" + startingDependency.getName() + "`");
lg.info("Alternative successfully found; preparing to link.");
dependency = availableDependencies.get(dependencyToAdd);
alreadyProcessedDependencies.add(startingDependency.getName());
}
if (alreadyProcessedDependencies.contains(dependency.getName())) return new HashMap<>(); // Already done
dependenciesNeeded.put(startingDependency.getName(), dependency.getName());
String lddRawResults = AbstractCompiledSolver.getLddResult(dependency);

//Try to save output to file for cli without interfering with further processing
File stdOutFile = new File(String.valueOf(Paths.get(System.getProperty("user.dir"))), "stdOut.txt");
try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(stdOutFile))){
bos.write(lddRawResults.getBytes());
} catch(Exception e) {
lg.error(e.getMessage(), e);
}

// Begin Parsing
try (BufferedReader br = new BufferedReader(new StringReader(lddRawResults))) {
for (String line = br.readLine(); line != null; line = br.readLine()){
StringTokenizer libInfo = new StringTokenizer(line, " \t");
if (libInfo.countTokens() != 4) continue; // We do not care about these
// If we have 4 tokens, we have one of two cases:
// >> Case 1: <lib_name>,"=>",<lib_path>,<offset>
// >> Case 2: <lib_name>,"=>","not","found" (<-- this is the case we want to identify!)
String libName = libInfo.nextToken();
String ignored = libInfo.nextToken(); // "=>"
String libPath = libInfo.nextToken();
String aux = libInfo.nextToken();

if (!libPath.equals("not") || !aux.equals("found")) continue; // We only care about Case 2
dependenciesNeeded.putAll(this.getDependenciesNeeded(
libName, availableDependencies, alreadyProcessedDependencies));

}
}
alreadyProcessedDependencies.add(dependency.getName());
return dependenciesNeeded;
}

// System.out.println("-----Setting executable "+LD_LIB_PATH+" to "+newLD_LIB_PATH);
getMathExecutable().addEnvironmentVariable(LD_LIB_PATH, newLD_LIB_PATH);
}else if (OperatingSystemInfo.getInstance().isMac()) {
getMathExecutable().addEnvironmentVariable("HDF5_DISABLE_VERSION_CHECK", "1");
}
private static String getLddResult(File solverToResolve) throws IOException, InterruptedException {
ProcessBuilder pb = new ProcessBuilder("ldd", solverToResolve.toPath().toString());
pb.redirectErrorStream(true);
Process p = pb.start();
StringBuilder sb = new StringBuilder();
// Can we pull inputStream outside the for-loop?
for (int ioByte = p.getInputStream().read(); ioByte != -1; ioByte = p.getInputStream().read()){
sb.append((char) ioByte);
}
p.waitFor();
return sb.toString();
}

private static boolean isDependency(File potentialDependency){
return potentialDependency.isFile()
&& potentialDependency.length() != 0
&& !Files.isSymbolicLink(potentialDependency.toPath())
&& !potentialDependency.getName().startsWith(".");
}

private String getEntryStartingWith(String shortName, Set<String> candidates){
for (String candidate : candidates) if (candidate.startsWith(shortName)) return candidate;
return null;
}

/**
Expand Down Expand Up @@ -317,11 +359,7 @@ protected void setMathExecutable(MathExecutable newMathExecutable) {
public synchronized final void startSolver() {
if (!(fieldThread != null && fieldThread.isAlive())) {
setMathExecutable(null);
fieldThread = new Thread() {
public void run() {
runSolver();
}
};
fieldThread = new Thread(this::runSolver);
fieldThread.setName("Compiled Solver (" + getClass().getName() + ")");
fieldThread.start();
}
Expand Down
Loading