From 35c6b39754cfc09ad7b91fa1cd7addeac1722dec Mon Sep 17 00:00:00 2001 From: karoliineh Date: Wed, 16 Feb 2022 22:16:39 +0200 Subject: [PATCH 01/14] Use goblint server mode --- pom.xml | 164 ++++++++------- src/main/java/GoblintAnalysis.java | 187 ----------------- src/main/java/Main.java | 51 +++-- src/main/java/analysis/GoblintAnalysis.java | 189 ++++++++++++++++++ .../{ => analysis}/GoblintAnalysisResult.java | 2 + .../java/{ => analysis}/GoblintPosition.java | 2 + .../java/{ => analysis}/GoblintResult.java | 4 +- .../{ => analysis}/TagInterfaceAdapter.java | 2 + .../GobPieConfiguration.java | 3 + .../java/goblintserver/GoblintClient.java | 69 +++++++ .../java/goblintserver/GoblintServer.java | 168 ++++++++++++++++ src/main/java/goblintserver/Request.java | 34 ++++ 12 files changed, 597 insertions(+), 278 deletions(-) delete mode 100644 src/main/java/GoblintAnalysis.java create mode 100644 src/main/java/analysis/GoblintAnalysis.java rename src/main/java/{ => analysis}/GoblintAnalysisResult.java (99%) rename src/main/java/{ => analysis}/GoblintPosition.java (98%) rename src/main/java/{ => analysis}/GoblintResult.java (98%) rename src/main/java/{ => analysis}/TagInterfaceAdapter.java (97%) rename src/main/java/{ => goblintserver}/GobPieConfiguration.java (82%) create mode 100644 src/main/java/goblintserver/GoblintClient.java create mode 100644 src/main/java/goblintserver/GoblintServer.java create mode 100644 src/main/java/goblintserver/Request.java diff --git a/pom.xml b/pom.xml index 2758bf8..0cca093 100644 --- a/pom.xml +++ b/pom.xml @@ -1,81 +1,93 @@ - 4.0.0 - magpiebridge - goblintanalyzer - 0.0.1-SNAPSHOT - - - com.github.magpiebridge - magpiebridge - 0.1.3 - + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + 4.0.0 + magpiebridge + goblintanalyzer + 0.0.1-SNAPSHOT + - - - com.google.code.gson - gson - 2.8.6 - + + + com.github.magpiebridge + magpiebridge + 0.1.3 + - - org.zeroturnaround - zt-exec - 1.12 - + + + com.google.code.gson + gson + 2.8.6 + - - org.apache.logging.log4j - log4j-api - 2.15.0 - - - org.apache.logging.log4j - log4j-core - 2.15.0 - - - - 1.8 - 1.8 - - - - - - org.apache.maven.plugins - maven-shade-plugin - 2.3 - - - - package - - shade - - - - - - Main - - - - - *:* - - META-INF/*.SF - META-INF/*.DSA - META-INF/*.RSA - - - - - - - - - + + + org.zeroturnaround + zt-exec + 1.12 + + + + + org.apache.logging.log4j + log4j-api + 2.15.0 + + + org.apache.logging.log4j + log4j-core + 2.15.0 + + + + + com.thetransactioncompany + jsonrpc2-client + 1.16.5 + + + + + 17 + 17 + + + + + + org.apache.maven.plugins + maven-shade-plugin + 2.3 + + + + package + + shade + + + + + + Main + + + + + *:* + + META-INF/*.SF + META-INF/*.DSA + META-INF/*.RSA + + + + + + + + + \ No newline at end of file diff --git a/src/main/java/GoblintAnalysis.java b/src/main/java/GoblintAnalysis.java deleted file mode 100644 index 6b4d44f..0000000 --- a/src/main/java/GoblintAnalysis.java +++ /dev/null @@ -1,187 +0,0 @@ -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileReader; -import java.io.IOException; -import java.net.MalformedURLException; -import java.util.*; -import java.util.concurrent.TimeoutException; -import java.util.stream.Stream; - -import com.ibm.wala.classLoader.Module; - -import magpiebridge.core.AnalysisConsumer; -import magpiebridge.core.ServerAnalysis; -import magpiebridge.core.MagpieServer; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.JsonIOException; -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; -import com.google.gson.JsonSyntaxException; - -import org.eclipse.lsp4j.MessageParams; -import org.eclipse.lsp4j.MessageType; -import org.zeroturnaround.exec.InvalidExitValueException; -import org.zeroturnaround.exec.ProcessExecutor; -import org.zeroturnaround.exec.ProcessResult; - - -public class GoblintAnalysis implements ServerAnalysis { - - private final MagpieServer magpieServer; - private File jsonResult = new File("analysisResults.json"); - private File gobPieConf = new File("gobpie.json"); - private String pathToGoblintConf; - private String[] filesToAnalyze; - private String[] preAnalyzeCommand; - private String[] goblintRunCommand; - // private List projectFiles; // for future use - - private final Logger log; - - public GoblintAnalysis(MagpieServer server) { - this.magpieServer = server; - this.log = LogManager.getLogger(GoblintAnalysis.class); - } - - /** - * The source of this analysis, usually the name of the analysis. - * - * @return the string - */ - public String source() { - return "GoblintAnalysis"; - } - - - /** - * The files to be analyzed. - * - * @param files the files that have been opened in the editor (not using due to using the compilation database). - * @param consumer the server which consumes the analysis results. - * @param rerun tells if the analysis should be reran. - */ - @Override - public void analyze(Collection files, AnalysisConsumer consumer, boolean rerun) { - if (rerun) { - if (consumer instanceof MagpieServer) { - boolean gobpieconf = readGobPieConfiguration(); - if (!gobpieconf) return; - if (preAnalyzeCommand != null && preAnalyzeCommand.length > 0) { - try { - runCommand(new File(System.getProperty("user.dir")), preAnalyzeCommand); - } catch (IOException | InvalidExitValueException | InterruptedException | TimeoutException e) { - this.magpieServer.forwardMessageToClient(new MessageParams(MessageType.Warning, "Building compilation database failed. " + e.getMessage())); - } - } - log.info("New analysis started"); - MagpieServer server = (MagpieServer) consumer; - if (generateJson()) server.consume(new ArrayList<>(readResultsFromJson()), source()); - } - } - } - - - public ProcessResult runCommand(File dirPath, String[] command) throws IOException, InvalidExitValueException, InterruptedException, TimeoutException { - log.debug("Waiting for Goblint to run..."); - System.err.println("---------------------- Goblint's dump start ----------------------"); - ProcessResult process = new ProcessExecutor() - .directory(dirPath) - .command(command) - .redirectOutput(System.err) - .redirectError(System.err) - .execute(); - System.err.println("----------------------- Goblint's dump end -----------------------"); - return process; - } - - /** - * Runs the command in the project root directory - * to let goblint generate the json file with analysis results. - * - * @param file the file on which to run the analysis. - * @return returns true if goblint finished the analysis and json was generated sucessfully, false otherwise - */ - private boolean generateJson() { - // construct command to run - this.goblintRunCommand = Stream.concat( - Arrays.stream(new String[]{"goblint", "--conf", pathToGoblintConf, "--set", "result", "json-messages", "-o", jsonResult.getAbsolutePath()}), - Arrays.stream(filesToAnalyze)) - .toArray(String[]::new); - - try { - // run command - log.info("Goblint run with command: " + String.join(" ", goblintRunCommand)); - ProcessResult commandRunProcess = runCommand(new File(System.getProperty("user.dir")), goblintRunCommand); - if (commandRunProcess.getExitValue() != 0) { - magpieServer.forwardMessageToClient( - new MessageParams(MessageType.Error, - "Goblint exited with an error.")); - log.error("Goblint exited with an error."); - return false; - } - log.info("Goblint finished analyzing."); - return true; - } catch (IOException | InvalidExitValueException | InterruptedException | TimeoutException e) { - this.magpieServer.forwardMessageToClient(new MessageParams(MessageType.Error, "Running Goblint failed. " + e.getMessage())); - return false; - } - } - - /** - * Deserializes json to GoblintResult objects and then converts the information - * into GoblintAnalysisResult objects, which Magpie uses to generate IDE - * messages. - * - * @return A collection of GoblintAnalysisResult objects. - */ - private Collection readResultsFromJson() { - try { - log.debug("Reading analysis results from json"); - // Read json objects as an array - JsonObject json = JsonParser.parseReader(new FileReader(jsonResult)).getAsJsonObject(); - GsonBuilder builder = new GsonBuilder(); - // Add deserializer for tags - builder.registerTypeAdapter(GoblintResult.Message.tag.class, new TagInterfaceAdapter()); - Gson gson = builder.create(); - GoblintResult goblintResult = gson.fromJson(json, GoblintResult.class); - Collection results = goblintResult.convert(); - // this.projectFiles = goblintResult.getFiles(); - log.debug("Analysis results read from json"); - return results; - } catch (JsonIOException | JsonSyntaxException | FileNotFoundException | MalformedURLException e) { - throw new RuntimeException(e); - } - } - - private boolean readGobPieConfiguration() { - try { - log.debug("Reading GobPie configuration from json"); - Gson gson = new GsonBuilder().create(); - // Read json object - JsonObject jsonObject = JsonParser.parseReader(new FileReader(gobPieConf)).getAsJsonObject(); - // Convert json object to GobPieConfiguration object - GobPieConfiguration gobpieConfiguration = gson.fromJson(jsonObject, GobPieConfiguration.class); - this.pathToGoblintConf = new File(gobpieConfiguration.getGoblintConf()).getAbsolutePath().toString(); - this.filesToAnalyze = gobpieConfiguration.getFiles(); - this.preAnalyzeCommand = gobpieConfiguration.getPreAnalyzeCommand(); - if (gobpieConfiguration.getGoblintConf().equals("") || gobpieConfiguration.getFiles() == null || gobpieConfiguration.getFiles().length < 1) { - log.debug("Configuration parameters missing from GobPie configuration file"); - magpieServer.forwardMessageToClient(new MessageParams(MessageType.Error, "Configuration parameters missing from GobPie configuration file.")); - return false; - } - log.debug("GobPie configuration read from json"); - } catch (JsonIOException | JsonSyntaxException e) { - throw new RuntimeException(e); - } catch (FileNotFoundException e) { - this.magpieServer.forwardMessageToClient(new MessageParams(MessageType.Error, "Could not locate GobPie configuration file. " + e.getMessage())); - return false; - } - return true; - } - -} diff --git a/src/main/java/Main.java b/src/main/java/Main.java index 433de63..a387862 100644 --- a/src/main/java/Main.java +++ b/src/main/java/Main.java @@ -1,6 +1,3 @@ - -import org.eclipse.lsp4j.jsonrpc.messages.Either; - import java.io.File; import magpiebridge.core.MagpieServer; @@ -8,6 +5,12 @@ import magpiebridge.core.ServerConfiguration; import magpiebridge.core.ToolAnalysis; +import analysis.GoblintAnalysis; +import goblintserver.GoblintClient; +import goblintserver.GoblintServer; + +import org.eclipse.lsp4j.jsonrpc.messages.Either; + import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -16,27 +19,47 @@ public class Main { private static final Logger log = LogManager.getLogger(Main.class); public static void main(String... args) { - // create server - MagpieServer server = createServer(); - log.info("Server created"); - // launch the server only if there is a goblint conf file present + + // launch the server only if there is a GobPie conf file present if (new File(System.getProperty("user.dir") + "/" + "gobpie.json").exists()) { - server.launchOnStdio(); - log.info("Server launched"); + + MagpieServer magpieServer = createMagpieServer(); + + if (magpieServer == null) { + log.info("Unable to launch MagpieBridge."); + } else { + magpieServer.launchOnStdio(); + log.info("MagpieBridge server launched."); + } } } - private static MagpieServer createServer() { + + private static MagpieServer createMagpieServer() { + // set up configuration for MagpieServer ServerConfiguration serverConfig = new ServerConfiguration(); - MagpieServer server = new MagpieServer(serverConfig); + MagpieServer magpieServer = new MagpieServer(serverConfig); + // define language String language = "c"; + + // start GoblintServer + GoblintServer goblintServer = new GoblintServer(magpieServer); + boolean gobServerStarted = goblintServer.startGoblitServer(); + if (!gobServerStarted) return null; + + // connect GoblintClient + GoblintClient goblintClient = new GoblintClient(magpieServer); + boolean goblintClientConnected = goblintClient.connectGoblitClient(); + if (!goblintClientConnected) return null; + // add analysis to the MagpieServer - ServerAnalysis serverAnalysis = new GoblintAnalysis(server); + ServerAnalysis serverAnalysis = new GoblintAnalysis(magpieServer, goblintServer, goblintClient); Either analysis = Either.forLeft(serverAnalysis); - server.addAnalysis(analysis, language); + magpieServer.addAnalysis(analysis, language); - return server; + return magpieServer; } + } diff --git a/src/main/java/analysis/GoblintAnalysis.java b/src/main/java/analysis/GoblintAnalysis.java new file mode 100644 index 0000000..5488fea --- /dev/null +++ b/src/main/java/analysis/GoblintAnalysis.java @@ -0,0 +1,189 @@ +package analysis; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.IOException; +import java.net.MalformedURLException; +import java.util.*; +import java.util.concurrent.TimeoutException; + +import com.ibm.wala.classLoader.Module; + +import magpiebridge.core.AnalysisConsumer; +import magpiebridge.core.ServerAnalysis; +import magpiebridge.core.MagpieServer; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonElement; +import com.google.gson.JsonIOException; +import com.google.gson.JsonParser; +import com.google.gson.JsonSyntaxException; + +import org.eclipse.lsp4j.MessageParams; +import org.eclipse.lsp4j.MessageType; + +import org.zeroturnaround.exec.InvalidExitValueException; +import org.zeroturnaround.exec.ProcessExecutor; +import org.zeroturnaround.exec.ProcessResult; + +import goblintserver.GoblintClient; +import goblintserver.GoblintServer; +import goblintserver.Request; + + +public class GoblintAnalysis implements ServerAnalysis { + + private final MagpieServer magpieServer; + private final GoblintServer goblintServer; + private final GoblintClient goblintClient; + + private File jsonResult = new File("analysisResults.json"); + + // private List projectFiles; // for future use + + private final Logger log = LogManager.getLogger(GoblintAnalysis.class); + + + public GoblintAnalysis(MagpieServer magpieServer, GoblintServer goblintServer, GoblintClient goblintClient) { + this.magpieServer = magpieServer; + this.goblintServer = goblintServer; + this.goblintClient = goblintClient; + } + + + /** + * The source of this analysis, usually the name of the analysis. + * + * @return the string + */ + + public String source() { + return "GobPie"; + } + + + /** + * The method that is triggered to start a new analysis. + * + * @param files the files that have been opened in the editor (not using due to using the compilation database). + * @param consumer the server which consumes the analysis results. + * @param rerun tells if the analysis should be reran. + */ + + @Override + public void analyze(Collection files, AnalysisConsumer consumer, boolean rerun) { + if (rerun) { + if (consumer instanceof MagpieServer) { + + preAnalyse(); + + MagpieServer server = (MagpieServer) consumer; + if (reanalyse()) server.consume(new ArrayList<>(readResultsFromJson()), source()); + + } + } + } + + + /** + * The method that is triggered before each analysis. + * + * preAnalyzeCommand is read from the GobPie configuration file. + * Can be used for automating the compilation database generation. + */ + + private void preAnalyse() { + String[] preAnalyzeCommand = goblintServer.getPreAnalyzeCommand(); + if (preAnalyzeCommand != null) { + try { + runCommand(new File(System.getProperty("user.dir")), preAnalyzeCommand); + } catch (IOException | InvalidExitValueException | InterruptedException | TimeoutException e) { + this.magpieServer.forwardMessageToClient( + new MessageParams(MessageType.Warning, "Running preanalysis command failed. " + e.getMessage())); + } + } + } + + + /** + * Sends the request to Goblint server to reanalyse. + * + * @return returns true if the request was sucessful, false otherwise + */ + + private boolean reanalyse() { + + // {"jsonrpc":"2.0","id":0,"method":"analyze","params":{}} + String request = new GsonBuilder().create().toJson(new Request("analyze")); + + try { + goblintClient.writeMessageToSocket(request); + return true; + } catch (IOException e) { + log.info("Sending the request to the server failed."); + return false; + } + + } + + + /** + * Method for running a command. + * + * @param dirPath The directory in which the command will run. + * @param command The command to run. + * @return Exit value and output of a finished process. + */ + + public ProcessResult runCommand(File dirPath, String[] command) throws IOException, InvalidExitValueException, InterruptedException, TimeoutException { + log.debug("Waiting for command: " + command.toString() + " to run..."); + ProcessResult process = new ProcessExecutor() + .directory(dirPath) + .command(command) + .redirectOutput(System.err) + .redirectError(System.err) + .execute(); + return process; + } + + + /** + * Deserializes json to GoblintResult objects and then converts the information + * into GoblintAnalysisResult objects, which Magpie uses to generate IDE + * messages. + * + * @return A collection of GoblintAnalysisResult objects. + */ + + private Collection readResultsFromJson() { + try { + log.debug("Reading analysis results from json"); + // Read json objects as an array + JsonElement json = JsonParser.parseReader(new FileReader(jsonResult)); + if (!json.isJsonObject()) { + log.error("Reading analysis results failed."); + this.magpieServer.forwardMessageToClient( + new MessageParams(MessageType.Error, "Reading analysis results failed.")); + return new ArrayList(); + } + GsonBuilder builder = new GsonBuilder(); + // Add deserializer for tags + builder.registerTypeAdapter(GoblintResult.Message.tag.class, new TagInterfaceAdapter()); + Gson gson = builder.create(); + GoblintResult goblintResult = gson.fromJson(json.getAsJsonObject(), GoblintResult.class); + Collection results = goblintResult.convert(); + // this.projectFiles = goblintResult.getFiles(); + log.debug("Analysis results read from json"); + return results; + } catch (JsonIOException | JsonSyntaxException | FileNotFoundException | MalformedURLException e) { + throw new RuntimeException(e); + } + } + + +} diff --git a/src/main/java/GoblintAnalysisResult.java b/src/main/java/analysis/GoblintAnalysisResult.java similarity index 99% rename from src/main/java/GoblintAnalysisResult.java rename to src/main/java/analysis/GoblintAnalysisResult.java index 8664daa..d050820 100644 --- a/src/main/java/GoblintAnalysisResult.java +++ b/src/main/java/analysis/GoblintAnalysisResult.java @@ -1,3 +1,5 @@ +package analysis; + import com.ibm.wala.cast.tree.CAstSourcePositionMap.Position; import com.ibm.wala.util.collections.Pair; diff --git a/src/main/java/GoblintPosition.java b/src/main/java/analysis/GoblintPosition.java similarity index 98% rename from src/main/java/GoblintPosition.java rename to src/main/java/analysis/GoblintPosition.java index 09e2f8f..e70baf5 100644 --- a/src/main/java/GoblintPosition.java +++ b/src/main/java/analysis/GoblintPosition.java @@ -1,3 +1,5 @@ +package analysis; + import com.ibm.wala.cast.tree.CAstSourcePositionMap.Position; import com.ibm.wala.classLoader.IMethod; diff --git a/src/main/java/GoblintResult.java b/src/main/java/analysis/GoblintResult.java similarity index 98% rename from src/main/java/GoblintResult.java rename to src/main/java/analysis/GoblintResult.java index 4b05259..d1627f8 100644 --- a/src/main/java/GoblintResult.java +++ b/src/main/java/analysis/GoblintResult.java @@ -1,3 +1,5 @@ +package analysis; + import com.ibm.wala.cast.tree.CAstSourcePositionMap.Position; import com.ibm.wala.util.collections.Pair; @@ -121,7 +123,7 @@ public List convert() throws MalformedURLException { public List getFiles() { Set allFiles = new HashSet<>(); - for (Entry> entry : files.entrySet()) { + for (Entry> entry : files.entrySet()) { allFiles.add(entry.getKey()); allFiles.addAll(entry.getValue()); } diff --git a/src/main/java/TagInterfaceAdapter.java b/src/main/java/analysis/TagInterfaceAdapter.java similarity index 97% rename from src/main/java/TagInterfaceAdapter.java rename to src/main/java/analysis/TagInterfaceAdapter.java index c5a59d1..57dbfb7 100644 --- a/src/main/java/TagInterfaceAdapter.java +++ b/src/main/java/analysis/TagInterfaceAdapter.java @@ -1,3 +1,5 @@ +package analysis; + import java.lang.reflect.Type; import com.google.gson.JsonDeserializationContext; diff --git a/src/main/java/GobPieConfiguration.java b/src/main/java/goblintserver/GobPieConfiguration.java similarity index 82% rename from src/main/java/GobPieConfiguration.java rename to src/main/java/goblintserver/GobPieConfiguration.java index 2685c5d..5d988cb 100644 --- a/src/main/java/GobPieConfiguration.java +++ b/src/main/java/goblintserver/GobPieConfiguration.java @@ -1,3 +1,5 @@ +package goblintserver; + public class GobPieConfiguration { private String goblintConf = ""; @@ -13,6 +15,7 @@ public String[] getFiles() { } public String[] getPreAnalyzeCommand() { + if (preAnalyzeCommand.length < 0) return null; return this.preAnalyzeCommand; } diff --git a/src/main/java/goblintserver/GoblintClient.java b/src/main/java/goblintserver/GoblintClient.java new file mode 100644 index 0000000..8abdc8a --- /dev/null +++ b/src/main/java/goblintserver/GoblintClient.java @@ -0,0 +1,69 @@ +package goblintserver; + +import java.io.IOException; +import java.net.StandardProtocolFamily; +import java.net.UnixDomainSocketAddress; +import java.nio.ByteBuffer; +import java.nio.channels.SocketChannel; +import java.nio.file.Path; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.eclipse.lsp4j.MessageParams; +import org.eclipse.lsp4j.MessageType; + +import magpiebridge.core.MagpieServer; + + +public class GoblintClient { + + private MagpieServer magpieServer; + private SocketChannel channel; + private final String goblintSocketName = "goblint.sock"; + + private final Logger log = LogManager.getLogger(GoblintClient.class); + + + public GoblintClient(MagpieServer magpieServer) { + this.magpieServer = magpieServer; + } + + + /** + * Method for connecting to Goblint server socket. + * + * @return True if connection was started successfully, false otherwise. + */ + + public boolean connectGoblitClient() { + try { + // connect to the goblint socket + UnixDomainSocketAddress address = UnixDomainSocketAddress.of(Path.of(goblintSocketName)); + channel = SocketChannel.open(StandardProtocolFamily.UNIX); + boolean connected = channel.connect(address); + if (!connected) return false; + log.info("Goblint client connected."); + return true; + } catch (IOException e) { + this.magpieServer.forwardMessageToClient(new MessageParams(MessageType.Error, "Connecting GoblintClient failed. " + e.getMessage())); + return false; + } + } + + + /** + * Method for sending the requests to Goblint server. + */ + + public void writeMessageToSocket(String message) throws IOException { + ByteBuffer buffer = ByteBuffer.allocate(1024); + buffer.clear(); + buffer.put(message.getBytes()); + buffer.flip(); + while (buffer.hasRemaining()) { + channel.write(buffer); + } + log.debug("Message written to socket."); + } + +} diff --git a/src/main/java/goblintserver/GoblintServer.java b/src/main/java/goblintserver/GoblintServer.java new file mode 100644 index 0000000..f71b419 --- /dev/null +++ b/src/main/java/goblintserver/GoblintServer.java @@ -0,0 +1,168 @@ +package goblintserver; + +import java.util.*; +import java.util.concurrent.TimeoutException; +import java.util.stream.Stream; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.IOException; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonIOException; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.google.gson.JsonSyntaxException; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import org.eclipse.lsp4j.MessageParams; +import org.eclipse.lsp4j.MessageType; +import org.zeroturnaround.exec.InvalidExitValueException; +import org.zeroturnaround.exec.ProcessExecutor; +import org.zeroturnaround.exec.StartedProcess; + +import magpiebridge.core.MagpieServer; + + +public class GoblintServer { + + private MagpieServer magpieServer; + + private File gobPieConf = new File("gobpie.json"); + private File jsonResult = new File("analysisResults.json"); + private File goblintSocket = new File("goblint.sock"); + + private String[] preAnalyzeCommand; + private String[] goblintRunCommand; + + private final Logger log = LogManager.getLogger(GoblintClient.class); + + + public GoblintServer(MagpieServer magpieServer) { + this.magpieServer = magpieServer; + } + + + public String[] getPreAnalyzeCommand() { + return preAnalyzeCommand; + } + + + /** + * Method to start the Goblint server. + * + * @return True if server was started successfully, false otherwise. + */ + + public boolean startGoblitServer() { + // read configuration file + boolean gobpieconf = readGobPieConfiguration(); + if (!gobpieconf) return false; + + try { + // run command to start goblint + log.info("Goblint run with command: " + String.join(" ", goblintRunCommand)); + + StartedProcess commandRunProcess = runCommand(new File(System.getProperty("user.dir")), goblintRunCommand); + + if (commandRunProcess.getFuture().isDone() && commandRunProcess.getProcess().exitValue() != 0) { + magpieServer.forwardMessageToClient(new MessageParams(MessageType.Error, "Goblint exited with an error.")); + log.error("Goblint exited with an error."); + return false; + } + log.info("Goblint server started."); + + return true; + } catch (IOException | InvalidExitValueException | InterruptedException | TimeoutException e) { + this.magpieServer.forwardMessageToClient(new MessageParams(MessageType.Error, "Running Goblint failed. " + e.getMessage())); + return false; + } + } + + + // public void stopGoblintServer() { + // try { + // Files.deleteIfExists(socketPath); + // } catch (IOException e) { + // // TODO Auto-generated catch block + // e.printStackTrace(); + // } + // } + + + /** + * Method for running a command. + * + * @param dirPath The directory in which the command will run. + * @param command The command to run. + * @return An object that represents a process that has started. It may or may not have finished. + */ + + public StartedProcess runCommand(File dirPath, String[] command) throws IOException, InterruptedException, InvalidExitValueException, TimeoutException { + log.debug("Waiting for command: " + command.toString() + " to run..."); + StartedProcess process = new ProcessExecutor() + .directory(dirPath) + .command(command) + .redirectOutput(System.err) + .redirectError(System.err) + .start(); + return process; + } + + + /** + * Method for reading GobPie configuration. + * Deserializes json to GobPieConfiguration object. + * + * @return true if gobpie configuration was read sucessfully, false otherwise: + * * no goblint configuration file was specified; + * * no files to analyse have been listed; + * * no gobpie.json file is found in root directory + */ + + private boolean readGobPieConfiguration() { + try { + log.debug("Reading GobPie configuration from json"); + + Gson gson = new GsonBuilder().create(); + // Read json object + JsonObject jsonObject = JsonParser.parseReader(new FileReader(gobPieConf)).getAsJsonObject(); + + // Convert json object to GobPieConfiguration object + GobPieConfiguration gobpieConfiguration = gson.fromJson(jsonObject, GobPieConfiguration.class); + this.preAnalyzeCommand = gobpieConfiguration.getPreAnalyzeCommand(); + + // Check if all required parameters have been set + if (gobpieConfiguration.getGoblintConf().equals("") || gobpieConfiguration.getFiles() == null || gobpieConfiguration.getFiles().length < 1) { + log.debug("Configuration parameters missing from GobPie configuration file"); + this.magpieServer.forwardMessageToClient(new MessageParams(MessageType.Error, "Configuration parameters missing from GobPie configuration file.")); + return false; + } + + // Construct command to run Goblint Server + // by concatenating the run command with files to analyse (read from GobPie conf) + this.goblintRunCommand = Stream.concat( + Arrays.stream(new String[]{"goblint", "--conf", new File(gobpieConfiguration.getGoblintConf()).getAbsolutePath(), + "--enable", "server.enabled", + // "--enable", "server.reparse", + "--set", "server.mode", "unix", + "--set", "server.unix-socket", goblintSocket.getAbsolutePath(), + "--set", "result", "json-messages", "-o", jsonResult.getAbsolutePath()}), + Arrays.stream(gobpieConfiguration.getFiles())) + .toArray(String[]::new); + + log.debug("GobPie configuration read from json"); + } catch (JsonIOException | JsonSyntaxException e) { + throw new RuntimeException(e); + } catch (FileNotFoundException e) { + this.magpieServer.forwardMessageToClient(new MessageParams(MessageType.Error, "Could not locate GobPie configuration file. " + e.getMessage())); + return false; + } + return true; + } + +} diff --git a/src/main/java/goblintserver/Request.java b/src/main/java/goblintserver/Request.java new file mode 100644 index 0000000..37aa471 --- /dev/null +++ b/src/main/java/goblintserver/Request.java @@ -0,0 +1,34 @@ +package goblintserver; + +public class Request { + // // {"jsonrpc":"2.0","id":0,"method":"analyze","params":{}} + + private String jsonrpc = "2.0"; + private int id = 0; + private String method; + private params params; + + static class params { + } + + public Request(String method) { + this.method = method; + } + + public String getJsonrpc() { + return jsonrpc; + } + + public int getId() { + return id; + } + + public String getMethod() { + return method; + } + + public params getParams() { + return params; + } + +} From cde2efa61b185e4c03cdd0eb4c415151d415edf3 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Wed, 16 Feb 2022 22:55:59 +0200 Subject: [PATCH 02/14] Fix extension naming and bump the version --- pom.xml | 6 +++--- vscode/package.json | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/pom.xml b/pom.xml index 0cca093..56dcbaa 100644 --- a/pom.xml +++ b/pom.xml @@ -2,9 +2,9 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 - magpiebridge - goblintanalyzer - 0.0.1-SNAPSHOT + goblint + gobpie + 0.0.2-SNAPSHOT diff --git a/vscode/package.json b/vscode/package.json index 70d9180..058b6b8 100644 --- a/vscode/package.json +++ b/vscode/package.json @@ -1,9 +1,9 @@ { - "name": "goblintanalyzer", - "description": "The Goblint analyzer", + "name": "gobpie", + "description": "The Goblint analyzer interactive extension", "author": "Karoliine Holter", "license": "EPL-2.0", - "version": "0.0.1", + "version": "0.0.2", "repository": { "type": "git", "url": "https://github.com/karoliineh/MagpieBridge-Goblint.git" @@ -23,9 +23,9 @@ "contributes": { "configuration": { "type": "object", - "title": "GoblintAnalyzer", + "title": "GobPie", "properties": { - "GoblintAnalyzer.trace.server": { + "gobpie.trace.server": { "scope": "window", "type": "string", "enum": [ @@ -52,7 +52,7 @@ ] }, "scripts": { - "vscode:prepublish": "cp ../target/goblintanalyzer-0.0.1-SNAPSHOT.jar goblintanalyzer-0.0.1-SNAPSHOT.jar && npm run compile", + "vscode:prepublish": "cp ../target/gobpie-0.0.2-SNAPSHOT.jar gobpie-0.0.2-SNAPSHOT.jar && npm run compile", "compile": "tsc -b", "watch": "tsc -b -w", "postinstall": "node ./node_modules/vscode/bin/install" From 10bbbb4bf32f3aa274ab245d7b477f74575055a6 Mon Sep 17 00:00:00 2001 From: Karoliine Holter <44437975+karoliineh@users.noreply.github.com> Date: Wed, 16 Feb 2022 23:00:23 +0200 Subject: [PATCH 03/14] Update build.yml --- .github/workflows/build.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1999a37..183d790 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -11,10 +11,10 @@ jobs: steps: - uses: actions/checkout@v2 - - name: Set up JDK 11 + - name: Set up JDK 17 uses: actions/setup-java@v2 with: - java-version: '11' # TODO: what version do we want? + java-version: '17' # TODO: what version do we want? distribution: 'adopt' cache: maven @@ -42,4 +42,4 @@ jobs: uses: actions/upload-artifact@v2.2.4 with: name: plugin - path: ./vscode/goblintanalyzer-*.vsix + path: ./vscode/gobpie-*.vsix From f42c3c67113e79ee040994b45b2f9e63889c1fe2 Mon Sep 17 00:00:00 2001 From: Karoliine Holter <44437975+karoliineh@users.noreply.github.com> Date: Thu, 17 Feb 2022 13:48:00 +0200 Subject: [PATCH 04/14] Update src/main/java/goblintserver/GoblintServer.java Co-authored-by: Simmo Saan --- src/main/java/goblintserver/GoblintServer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/goblintserver/GoblintServer.java b/src/main/java/goblintserver/GoblintServer.java index f71b419..fb91b09 100644 --- a/src/main/java/goblintserver/GoblintServer.java +++ b/src/main/java/goblintserver/GoblintServer.java @@ -58,7 +58,7 @@ public String[] getPreAnalyzeCommand() { * @return True if server was started successfully, false otherwise. */ - public boolean startGoblitServer() { + public boolean startGoblintServer() { // read configuration file boolean gobpieconf = readGobPieConfiguration(); if (!gobpieconf) return false; From 6db655a76c413e1db35927df966e40916895be41 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Thu, 17 Feb 2022 13:50:35 +0200 Subject: [PATCH 05/14] Fix typo in startGoblintServer() --- src/main/java/Main.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/Main.java b/src/main/java/Main.java index a387862..df15f6e 100644 --- a/src/main/java/Main.java +++ b/src/main/java/Main.java @@ -46,7 +46,7 @@ private static MagpieServer createMagpieServer() { // start GoblintServer GoblintServer goblintServer = new GoblintServer(magpieServer); - boolean gobServerStarted = goblintServer.startGoblitServer(); + boolean gobServerStarted = goblintServer.startGoblintServer(); if (!gobServerStarted) return null; // connect GoblintClient From fcde826daf203e785fe02c38791687236b6fadea Mon Sep 17 00:00:00 2001 From: karoliineh Date: Sat, 19 Feb 2022 19:14:33 +0200 Subject: [PATCH 06/14] Implemented reading server result, added listener for the server process --- pom.xml | 7 ---- src/main/java/analysis/GoblintAnalysis.java | 18 ++++++--- .../java/goblintserver/GoblintClient.java | 37 +++++++++++++------ .../java/goblintserver/GoblintServer.java | 25 +++++++++++++ vscode/src/extension.ts | 4 +- 5 files changed, 66 insertions(+), 25 deletions(-) diff --git a/pom.xml b/pom.xml index 56dcbaa..f1cf394 100644 --- a/pom.xml +++ b/pom.xml @@ -40,13 +40,6 @@ 2.15.0 - - - com.thetransactioncompany - jsonrpc2-client - 1.16.5 - - 17 diff --git a/src/main/java/analysis/GoblintAnalysis.java b/src/main/java/analysis/GoblintAnalysis.java index 5488fea..df28a8a 100644 --- a/src/main/java/analysis/GoblintAnalysis.java +++ b/src/main/java/analysis/GoblintAnalysis.java @@ -82,8 +82,10 @@ public void analyze(Collection files, AnalysisConsumer consume preAnalyse(); + System.err.println("\n---------------------- Analysis started ----------------------"); MagpieServer server = (MagpieServer) consumer; if (reanalyse()) server.consume(new ArrayList<>(readResultsFromJson()), source()); + System.err.println("--------------------- Analysis finished ----------------------\n"); } } @@ -101,7 +103,9 @@ private void preAnalyse() { String[] preAnalyzeCommand = goblintServer.getPreAnalyzeCommand(); if (preAnalyzeCommand != null) { try { + log.info("Preanalyze command ran: \"" + Arrays.toString(preAnalyzeCommand) + "\""); runCommand(new File(System.getProperty("user.dir")), preAnalyzeCommand); + log.info("Preanalyze command finished."); } catch (IOException | InvalidExitValueException | InterruptedException | TimeoutException e) { this.magpieServer.forwardMessageToClient( new MessageParams(MessageType.Warning, "Running preanalysis command failed. " + e.getMessage())); @@ -111,7 +115,7 @@ private void preAnalyse() { /** - * Sends the request to Goblint server to reanalyse. + * Sends the request to Goblint server to reanalyse and reads the result. * * @return returns true if the request was sucessful, false otherwise */ @@ -119,16 +123,20 @@ private void preAnalyse() { private boolean reanalyse() { // {"jsonrpc":"2.0","id":0,"method":"analyze","params":{}} - String request = new GsonBuilder().create().toJson(new Request("analyze")); + String request1 = new GsonBuilder().create().toJson(new Request("analyze")) + "\n"; + String request2 = new GsonBuilder().create().toJson(new Request("messages")) + "\n"; try { - goblintClient.writeMessageToSocket(request); + goblintClient.writeRequestToSocket(request1); + goblintClient.readResultFromSocket(); + goblintClient.writeRequestToSocket(request2); + goblintClient.readResultFromSocket(); return true; } catch (IOException e) { - log.info("Sending the request to the server failed."); + log.info("Sending the request to or receiving result from the server failed: " + e); + e.printStackTrace(); return false; } - } diff --git a/src/main/java/goblintserver/GoblintClient.java b/src/main/java/goblintserver/GoblintClient.java index 8abdc8a..954b679 100644 --- a/src/main/java/goblintserver/GoblintClient.java +++ b/src/main/java/goblintserver/GoblintClient.java @@ -1,9 +1,13 @@ package goblintserver; +import java.io.BufferedReader; import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; import java.net.StandardProtocolFamily; import java.net.UnixDomainSocketAddress; -import java.nio.ByteBuffer; +import java.nio.channels.Channels; import java.nio.channels.SocketChannel; import java.nio.file.Path; @@ -19,6 +23,7 @@ public class GoblintClient { private MagpieServer magpieServer; private SocketChannel channel; + private UnixDomainSocketAddress address; private final String goblintSocketName = "goblint.sock"; private final Logger log = LogManager.getLogger(GoblintClient.class); @@ -38,7 +43,7 @@ public GoblintClient(MagpieServer magpieServer) { public boolean connectGoblitClient() { try { // connect to the goblint socket - UnixDomainSocketAddress address = UnixDomainSocketAddress.of(Path.of(goblintSocketName)); + address = UnixDomainSocketAddress.of(Path.of(goblintSocketName)); channel = SocketChannel.open(StandardProtocolFamily.UNIX); boolean connected = channel.connect(address); if (!connected) return false; @@ -55,15 +60,25 @@ public boolean connectGoblitClient() { * Method for sending the requests to Goblint server. */ - public void writeMessageToSocket(String message) throws IOException { - ByteBuffer buffer = ByteBuffer.allocate(1024); - buffer.clear(); - buffer.put(message.getBytes()); - buffer.flip(); - while (buffer.hasRemaining()) { - channel.write(buffer); - } - log.debug("Message written to socket."); + public void writeRequestToSocket(String request) throws IOException { + OutputStream stream = Channels.newOutputStream(channel); + stream.write(request.getBytes()); + log.info(request); + log.info("Request written to socket."); + } + + + /** + * Method for reading the results from Goblint server. + */ + + public String readResultFromSocket() throws IOException { + InputStream stream = Channels.newInputStream(channel); + BufferedReader reader = new BufferedReader(new InputStreamReader(stream)); + String result = reader.readLine(); + log.info(result); + log.info("Result read from socket."); + return result; } } diff --git a/src/main/java/goblintserver/GoblintServer.java b/src/main/java/goblintserver/GoblintServer.java index fb91b09..a9df67a 100644 --- a/src/main/java/goblintserver/GoblintServer.java +++ b/src/main/java/goblintserver/GoblintServer.java @@ -23,7 +23,9 @@ import org.eclipse.lsp4j.MessageType; import org.zeroturnaround.exec.InvalidExitValueException; import org.zeroturnaround.exec.ProcessExecutor; +import org.zeroturnaround.exec.ProcessResult; import org.zeroturnaround.exec.StartedProcess; +import org.zeroturnaround.exec.listener.ProcessListener; import magpiebridge.core.MagpieServer; @@ -74,6 +76,11 @@ public boolean startGoblintServer() { log.error("Goblint exited with an error."); return false; } + + while(!goblintSocket.exists()) { + Thread.sleep(1000); + } + log.info("Goblint server started."); return true; @@ -103,12 +110,30 @@ public boolean startGoblintServer() { */ public StartedProcess runCommand(File dirPath, String[] command) throws IOException, InterruptedException, InvalidExitValueException, TimeoutException { + ProcessListener listener = new ProcessListener() { + public void afterFinish(Process process, ProcessResult result) { + magpieServer.forwardMessageToClient(new MessageParams(MessageType.Info, "Goblint server finished.")); + log.info("Goblint server finished."); + } + + public void afterStop(Process process) { + if (process.exitValue() != 0) { + magpieServer.forwardMessageToClient(new MessageParams(MessageType.Error, "Goblint server exited due to an error.")); + log.error("Goblint server exited due to an error."); + } else { + magpieServer.forwardMessageToClient(new MessageParams(MessageType.Info, "Goblint server has stopped.")); + log.info("Goblint server has stopped."); + } + } + }; + log.debug("Waiting for command: " + command.toString() + " to run..."); StartedProcess process = new ProcessExecutor() .directory(dirPath) .command(command) .redirectOutput(System.err) .redirectError(System.err) + .addListener(listener) .start(); return process; } diff --git a/vscode/src/extension.ts b/vscode/src/extension.ts index a40e7d7..71831b4 100644 --- a/vscode/src/extension.ts +++ b/vscode/src/extension.ts @@ -7,7 +7,7 @@ import { LanguageClient, LanguageClientOptions, ServerOptions, StreamInfo } from export function activate(context: ExtensionContext) { let script = 'java'; - let args = ['-jar',context.asAbsolutePath('goblintanalyzer-0.0.1-SNAPSHOT.jar')]; + let args = ['-jar',context.asAbsolutePath('gobpie-0.0.2-SNAPSHOT.jar')]; // Use this for communicating on stdio let serverOptions: ServerOptions = { @@ -41,7 +41,7 @@ export function activate(context: ExtensionContext) { }; // Create the language client and start the client. - let lc : LanguageClient = new LanguageClient('GoblintAnalyzer','GoblintAnalyzer', serverOptions, clientOptions); + let lc : LanguageClient = new LanguageClient('GobPie','GobPie', serverOptions, clientOptions); lc.start(); } From efc3171b71bba9f4202613700bac3a31b04441bd Mon Sep 17 00:00:00 2001 From: karoliineh Date: Sat, 19 Feb 2022 20:44:55 +0200 Subject: [PATCH 07/14] Fix params bug --- src/main/java/goblintserver/Request.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/goblintserver/Request.java b/src/main/java/goblintserver/Request.java index 37aa471..6e955b0 100644 --- a/src/main/java/goblintserver/Request.java +++ b/src/main/java/goblintserver/Request.java @@ -13,6 +13,9 @@ static class params { public Request(String method) { this.method = method; + if (method == "analyze") { + this.params = new params(); + } } public String getJsonrpc() { From 6d308afde6deadf5a071434b6dff68e6c2717432 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Sun, 20 Feb 2022 18:18:49 +0200 Subject: [PATCH 08/14] Read results from socket instead of generated external json file --- src/main/java/analysis/GoblintAnalysis.java | 56 ++++++++++--------- ...oblintResult.java => GoblintMessages.java} | 47 ++++++---------- .../java/analysis/TagInterfaceAdapter.java | 4 +- .../java/goblintserver/GoblintClient.java | 19 ++++--- 4 files changed, 62 insertions(+), 64 deletions(-) rename src/main/java/analysis/{GoblintResult.java => GoblintMessages.java} (69%) diff --git a/src/main/java/analysis/GoblintAnalysis.java b/src/main/java/analysis/GoblintAnalysis.java index df28a8a..f22dfb7 100644 --- a/src/main/java/analysis/GoblintAnalysis.java +++ b/src/main/java/analysis/GoblintAnalysis.java @@ -1,8 +1,6 @@ package analysis; import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileReader; import java.io.IOException; import java.net.MalformedURLException; import java.util.*; @@ -19,9 +17,10 @@ import com.google.gson.Gson; import com.google.gson.GsonBuilder; +import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonIOException; -import com.google.gson.JsonParser; +import com.google.gson.JsonObject; import com.google.gson.JsonSyntaxException; import org.eclipse.lsp4j.MessageParams; @@ -42,8 +41,6 @@ public class GoblintAnalysis implements ServerAnalysis { private final GoblintServer goblintServer; private final GoblintClient goblintClient; - private File jsonResult = new File("analysisResults.json"); - // private List projectFiles; // for future use private final Logger log = LogManager.getLogger(GoblintAnalysis.class); @@ -84,7 +81,8 @@ public void analyze(Collection files, AnalysisConsumer consume System.err.println("\n---------------------- Analysis started ----------------------"); MagpieServer server = (MagpieServer) consumer; - if (reanalyse()) server.consume(new ArrayList<>(readResultsFromJson()), source()); + Collection response = reanalyse(); + if (response != null) server.consume(new ArrayList<>(response), source()); System.err.println("--------------------- Analysis finished ----------------------\n"); } @@ -120,22 +118,23 @@ private void preAnalyse() { * @return returns true if the request was sucessful, false otherwise */ - private boolean reanalyse() { + private Collection reanalyse() { // {"jsonrpc":"2.0","id":0,"method":"analyze","params":{}} - String request1 = new GsonBuilder().create().toJson(new Request("analyze")) + "\n"; - String request2 = new GsonBuilder().create().toJson(new Request("messages")) + "\n"; + String analyzeRequest = new GsonBuilder().create().toJson(new Request("analyze")) + "\n"; + String messagesRequest = new GsonBuilder().create().toJson(new Request("messages")) + "\n"; try { - goblintClient.writeRequestToSocket(request1); - goblintClient.readResultFromSocket(); - goblintClient.writeRequestToSocket(request2); - goblintClient.readResultFromSocket(); - return true; + goblintClient.writeRequestToSocket(analyzeRequest); + goblintClient.readResponseFromSocket(); + goblintClient.writeRequestToSocket(messagesRequest); + JsonObject response = goblintClient.readResponseFromSocket(); + Collection results = convertResultsFromJson(response); + return results; } catch (IOException e) { log.info("Sending the request to or receiving result from the server failed: " + e); e.printStackTrace(); - return false; + return null; } } @@ -168,27 +167,34 @@ public ProcessResult runCommand(File dirPath, String[] command) throws IOExcepti * @return A collection of GoblintAnalysisResult objects. */ - private Collection readResultsFromJson() { + private Collection convertResultsFromJson(JsonObject response) { + + Collection results = new ArrayList<>(); + try { - log.debug("Reading analysis results from json"); + log.debug("Reading analysis results from json."); // Read json objects as an array - JsonElement json = JsonParser.parseReader(new FileReader(jsonResult)); - if (!json.isJsonObject()) { + JsonArray messagesArray = response.get("result").getAsJsonArray(); + if (messagesArray != null && !messagesArray.isJsonArray()) { log.error("Reading analysis results failed."); this.magpieServer.forwardMessageToClient( new MessageParams(MessageType.Error, "Reading analysis results failed.")); - return new ArrayList(); + return null; } GsonBuilder builder = new GsonBuilder(); // Add deserializer for tags - builder.registerTypeAdapter(GoblintResult.Message.tag.class, new TagInterfaceAdapter()); + builder.registerTypeAdapter(GoblintMessages.tag.class, new TagInterfaceAdapter()); Gson gson = builder.create(); - GoblintResult goblintResult = gson.fromJson(json.getAsJsonObject(), GoblintResult.class); - Collection results = goblintResult.convert(); - // this.projectFiles = goblintResult.getFiles(); + + for (JsonElement msg : messagesArray) { + GoblintMessages goblintResult = gson.fromJson(msg, GoblintMessages.class); + results.addAll(goblintResult.convert()); + } + log.debug("Analysis results read from json"); + return results; - } catch (JsonIOException | JsonSyntaxException | FileNotFoundException | MalformedURLException e) { + } catch (JsonIOException | JsonSyntaxException | MalformedURLException e) { throw new RuntimeException(e); } } diff --git a/src/main/java/analysis/GoblintResult.java b/src/main/java/analysis/GoblintMessages.java similarity index 69% rename from src/main/java/analysis/GoblintResult.java rename to src/main/java/analysis/GoblintMessages.java index d1627f8..9b14fab 100644 --- a/src/main/java/analysis/GoblintResult.java +++ b/src/main/java/analysis/GoblintMessages.java @@ -6,20 +6,11 @@ import java.io.File; import java.net.MalformedURLException; import java.util.ArrayList; -import java.util.HashSet; import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.Map.Entry; import java.util.stream.Collectors; -public class GoblintResult { - - private Map> files; - private List messages = new ArrayList<>(); - - static class Message { +public class GoblintMessages { private List tags = new ArrayList<>(); private String severity; @@ -80,26 +71,25 @@ static class loc { private int endColumn; } } - } } + public List convert() throws MalformedURLException { List results = new ArrayList<>(); - for (Message message : messages) { - if (message.multipiece.group_text == null) { - String msg = message.tags.stream().map(tag -> tag.toString()).collect(Collectors.joining("")) + " " + message.multipiece.text; - GoblintPosition pos = new GoblintPosition(message.multipiece.loc.line, message.multipiece.loc.endLine, message.multipiece.loc.column - 1, message.multipiece.loc.endColumn - 1, new File(message.multipiece.loc.file).toURI().toURL()); - GoblintAnalysisResult result = new GoblintAnalysisResult(pos, msg, message.severity); + if (multipiece.group_text == null) { + String msg = tags.stream().map(tag -> tag.toString()).collect(Collectors.joining("")) + " " + multipiece.text; + GoblintPosition pos = new GoblintPosition(multipiece.loc.line, multipiece.loc.endLine, multipiece.loc.column - 1, multipiece.loc.endColumn - 1, new File(multipiece.loc.file).toURI().toURL()); + GoblintAnalysisResult result = new GoblintAnalysisResult(pos, msg, severity); results.add(result); } else { List intermresults = new ArrayList<>(); - List pieces = message.multipiece.pieces; - for (Message.multipiece.pieces piece : pieces) { + List pieces = multipiece.pieces; + for (multipiece.pieces piece : pieces) { GoblintPosition pos = new GoblintPosition(piece.loc.line, piece.loc.endLine, piece.loc.column - 1, piece.loc.endColumn - 1, new File(piece.loc.file).toURI().toURL()); GoblintAnalysisResult result = new GoblintAnalysisResult(pos, - message.tags.stream().map(tag -> tag.toString()).collect(Collectors.joining("")) + " Group: " + message.multipiece.group_text, - piece.text, message.severity); + tags.stream().map(tag -> tag.toString()).collect(Collectors.joining("")) + " Group: " + multipiece.group_text, + piece.text, severity); intermresults.add(result); } // Add related warnings to all the group elements @@ -115,19 +105,18 @@ public List convert() throws MalformedURLException { res1.severityStr(), related)); } results.addAll(addedRelated); - } } return results; } - public List getFiles() { - Set allFiles = new HashSet<>(); - for (Entry> entry : files.entrySet()) { - allFiles.add(entry.getKey()); - allFiles.addAll(entry.getValue()); - } - return new ArrayList<>(allFiles); - } + // public List getFiles() { + // Set allFiles = new HashSet<>(); + // for (Entry> entry : files.entrySet()) { + // allFiles.add(entry.getKey()); + // allFiles.addAll(entry.getValue()); + // } + // return new ArrayList<>(allFiles); + // } } diff --git a/src/main/java/analysis/TagInterfaceAdapter.java b/src/main/java/analysis/TagInterfaceAdapter.java index 57dbfb7..4de8b71 100644 --- a/src/main/java/analysis/TagInterfaceAdapter.java +++ b/src/main/java/analysis/TagInterfaceAdapter.java @@ -14,9 +14,9 @@ public class TagInterfaceAdapter implements JsonDeserializer { public Object deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException { JsonObject jsonObject = jsonElement.getAsJsonObject(); if (jsonObject.has("Category")) - return jsonDeserializationContext.deserialize(jsonObject, GoblintResult.Message.tag.Category.class); + return jsonDeserializationContext.deserialize(jsonObject, GoblintMessages.tag.Category.class); if (jsonObject.has("CWE")) - return jsonDeserializationContext.deserialize(jsonObject, GoblintResult.Message.tag.CWE.class); + return jsonDeserializationContext.deserialize(jsonObject, GoblintMessages.tag.CWE.class); return null; } diff --git a/src/main/java/goblintserver/GoblintClient.java b/src/main/java/goblintserver/GoblintClient.java index 954b679..ae55e79 100644 --- a/src/main/java/goblintserver/GoblintClient.java +++ b/src/main/java/goblintserver/GoblintClient.java @@ -11,6 +11,10 @@ import java.nio.channels.SocketChannel; import java.nio.file.Path; +import com.google.gson.Gson; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; + import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.eclipse.lsp4j.MessageParams; @@ -62,23 +66,22 @@ public boolean connectGoblitClient() { public void writeRequestToSocket(String request) throws IOException { OutputStream stream = Channels.newOutputStream(channel); - stream.write(request.getBytes()); - log.info(request); + stream.write(request.getBytes()); log.info("Request written to socket."); } /** - * Method for reading the results from Goblint server. + * Method for reading the response from Goblint server. */ - public String readResultFromSocket() throws IOException { + public JsonObject readResponseFromSocket() throws IOException { InputStream stream = Channels.newInputStream(channel); BufferedReader reader = new BufferedReader(new InputStreamReader(stream)); - String result = reader.readLine(); - log.info(result); - log.info("Result read from socket."); - return result; + String response = reader.readLine(); + log.info("Response read from socket."); + JsonObject responseJson = new Gson().fromJson (response, JsonElement.class).getAsJsonObject(); + return responseJson; } } From 6a6a65c38f50b27d627ad665be8c2abe7eeffacb Mon Sep 17 00:00:00 2001 From: karoliineh Date: Sun, 20 Feb 2022 18:23:49 +0200 Subject: [PATCH 09/14] Move streams to fields --- src/main/java/goblintserver/GoblintClient.java | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/main/java/goblintserver/GoblintClient.java b/src/main/java/goblintserver/GoblintClient.java index ae55e79..f07980e 100644 --- a/src/main/java/goblintserver/GoblintClient.java +++ b/src/main/java/goblintserver/GoblintClient.java @@ -28,6 +28,9 @@ public class GoblintClient { private MagpieServer magpieServer; private SocketChannel channel; private UnixDomainSocketAddress address; + private OutputStream outputStream; + private BufferedReader inputReader; + private final String goblintSocketName = "goblint.sock"; private final Logger log = LogManager.getLogger(GoblintClient.class); @@ -50,6 +53,9 @@ public boolean connectGoblitClient() { address = UnixDomainSocketAddress.of(Path.of(goblintSocketName)); channel = SocketChannel.open(StandardProtocolFamily.UNIX); boolean connected = channel.connect(address); + outputStream = Channels.newOutputStream(channel); + InputStream inputStream = Channels.newInputStream(channel); + inputReader = new BufferedReader(new InputStreamReader(inputStream)); if (!connected) return false; log.info("Goblint client connected."); return true; @@ -65,8 +71,7 @@ public boolean connectGoblitClient() { */ public void writeRequestToSocket(String request) throws IOException { - OutputStream stream = Channels.newOutputStream(channel); - stream.write(request.getBytes()); + outputStream.write(request.getBytes()); log.info("Request written to socket."); } @@ -76,9 +81,7 @@ public void writeRequestToSocket(String request) throws IOException { */ public JsonObject readResponseFromSocket() throws IOException { - InputStream stream = Channels.newInputStream(channel); - BufferedReader reader = new BufferedReader(new InputStreamReader(stream)); - String response = reader.readLine(); + String response = inputReader.readLine(); log.info("Response read from socket."); JsonObject responseJson = new Gson().fromJson (response, JsonElement.class).getAsJsonObject(); return responseJson; From ca829690d8dccaba823098fa5f26843c3cacc4fe Mon Sep 17 00:00:00 2001 From: karoliineh Date: Mon, 21 Feb 2022 18:39:06 +0200 Subject: [PATCH 10/14] Remove external json generation from command, update ReadMe, set doAnalysisbyFirstOpen to false --- Readme.md | 2 +- src/main/java/Main.java | 1 + src/main/java/analysis/GoblintAnalysis.java | 12 ++-------- .../java/goblintserver/GoblintServer.java | 22 ++++--------------- 4 files changed, 8 insertions(+), 29 deletions(-) diff --git a/Readme.md b/Readme.md index d0dfab5..4d07a9f 100644 --- a/Readme.md +++ b/Readme.md @@ -30,7 +30,7 @@ Example configuration file `gobpie.json`: { "goblintConf" : "goblint.json", "files" : ["./build"], - "preAnalyzeCommand" : ["cmake", "-DCMAKE_EXPORT_COMPILE_COMMANDS=ON", "build"] + "preAnalyzeCommand" : ["cmake", "-DCMAKE_EXPORT_COMPILE_COMMANDS=ON", "-B", "build"] } ``` diff --git a/src/main/java/Main.java b/src/main/java/Main.java index df15f6e..148d43f 100644 --- a/src/main/java/Main.java +++ b/src/main/java/Main.java @@ -39,6 +39,7 @@ private static MagpieServer createMagpieServer() { // set up configuration for MagpieServer ServerConfiguration serverConfig = new ServerConfiguration(); + serverConfig.setDoAnalysisByFirstOpen(false); MagpieServer magpieServer = new MagpieServer(serverConfig); // define language diff --git a/src/main/java/analysis/GoblintAnalysis.java b/src/main/java/analysis/GoblintAnalysis.java index f22dfb7..f533d4f 100644 --- a/src/main/java/analysis/GoblintAnalysis.java +++ b/src/main/java/analysis/GoblintAnalysis.java @@ -15,13 +15,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonIOException; -import com.google.gson.JsonObject; -import com.google.gson.JsonSyntaxException; +import com.google.gson.*; import org.eclipse.lsp4j.MessageParams; import org.eclipse.lsp4j.MessageType; @@ -41,8 +35,6 @@ public class GoblintAnalysis implements ServerAnalysis { private final GoblintServer goblintServer; private final GoblintClient goblintClient; - // private List projectFiles; // for future use - private final Logger log = LogManager.getLogger(GoblintAnalysis.class); @@ -192,7 +184,7 @@ private Collection convertResultsFromJson(JsonObject resp } log.debug("Analysis results read from json"); - + return results; } catch (JsonIOException | JsonSyntaxException | MalformedURLException e) { throw new RuntimeException(e); diff --git a/src/main/java/goblintserver/GoblintServer.java b/src/main/java/goblintserver/GoblintServer.java index a9df67a..86a8044 100644 --- a/src/main/java/goblintserver/GoblintServer.java +++ b/src/main/java/goblintserver/GoblintServer.java @@ -3,28 +3,16 @@ import java.util.*; import java.util.concurrent.TimeoutException; import java.util.stream.Stream; +import java.io.*; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileReader; -import java.io.IOException; - -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.JsonIOException; -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; -import com.google.gson.JsonSyntaxException; +import com.google.gson.*; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.eclipse.lsp4j.MessageParams; import org.eclipse.lsp4j.MessageType; -import org.zeroturnaround.exec.InvalidExitValueException; -import org.zeroturnaround.exec.ProcessExecutor; -import org.zeroturnaround.exec.ProcessResult; -import org.zeroturnaround.exec.StartedProcess; +import org.zeroturnaround.exec.*; import org.zeroturnaround.exec.listener.ProcessListener; import magpiebridge.core.MagpieServer; @@ -35,7 +23,6 @@ public class GoblintServer { private MagpieServer magpieServer; private File gobPieConf = new File("gobpie.json"); - private File jsonResult = new File("analysisResults.json"); private File goblintSocket = new File("goblint.sock"); private String[] preAnalyzeCommand; @@ -175,8 +162,7 @@ private boolean readGobPieConfiguration() { "--enable", "server.enabled", // "--enable", "server.reparse", "--set", "server.mode", "unix", - "--set", "server.unix-socket", goblintSocket.getAbsolutePath(), - "--set", "result", "json-messages", "-o", jsonResult.getAbsolutePath()}), + "--set", "server.unix-socket", goblintSocket.getAbsolutePath()}), Arrays.stream(gobpieConfiguration.getFiles())) .toArray(String[]::new); From d5f4abae17a42beef39af1a22d6281e4a7e6b918 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 22 Feb 2022 11:24:13 +0200 Subject: [PATCH 11/14] Enable server reparse --- src/main/java/goblintserver/GoblintServer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/goblintserver/GoblintServer.java b/src/main/java/goblintserver/GoblintServer.java index 86a8044..7e78679 100644 --- a/src/main/java/goblintserver/GoblintServer.java +++ b/src/main/java/goblintserver/GoblintServer.java @@ -160,7 +160,7 @@ private boolean readGobPieConfiguration() { this.goblintRunCommand = Stream.concat( Arrays.stream(new String[]{"goblint", "--conf", new File(gobpieConfiguration.getGoblintConf()).getAbsolutePath(), "--enable", "server.enabled", - // "--enable", "server.reparse", + "--enable", "server.reparse", "--set", "server.mode", "unix", "--set", "server.unix-socket", goblintSocket.getAbsolutePath()}), Arrays.stream(gobpieConfiguration.getFiles())) From ae9b5a0443f83cfd015cc6b32561bf87a446799a Mon Sep 17 00:00:00 2001 From: karoliineh Date: Tue, 22 Feb 2022 11:31:26 +0200 Subject: [PATCH 12/14] Do analysis when project is first opened --- src/main/java/Main.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/Main.java b/src/main/java/Main.java index 148d43f..9fa8e5f 100644 --- a/src/main/java/Main.java +++ b/src/main/java/Main.java @@ -29,6 +29,7 @@ public static void main(String... args) { log.info("Unable to launch MagpieBridge."); } else { magpieServer.launchOnStdio(); + magpieServer.doAnalysis("c", true); log.info("MagpieBridge server launched."); } } From db670e63195bbe1d1a6bbf46c90fa41497175079 Mon Sep 17 00:00:00 2001 From: karoliineh Date: Mon, 28 Feb 2022 13:49:01 +0200 Subject: [PATCH 13/14] Replace sleeping while loop with watchservice --- .../java/goblintserver/GoblintServer.java | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/src/main/java/goblintserver/GoblintServer.java b/src/main/java/goblintserver/GoblintServer.java index 7e78679..0e4f748 100644 --- a/src/main/java/goblintserver/GoblintServer.java +++ b/src/main/java/goblintserver/GoblintServer.java @@ -4,6 +4,7 @@ import java.util.concurrent.TimeoutException; import java.util.stream.Stream; import java.io.*; +import java.nio.file.*; import com.google.gson.*; @@ -64,13 +65,23 @@ public boolean startGoblintServer() { return false; } - while(!goblintSocket.exists()) { - Thread.sleep(1000); + // wait until Goblint socket is created before continuing + WatchService watchService = FileSystems.getDefault().newWatchService(); + Path path = Paths.get(System.getProperty("user.dir")); + path.register(watchService, StandardWatchEventKinds.ENTRY_CREATE ); + WatchKey key; + while ((key = watchService.take()) != null) { + for (WatchEvent event : key.pollEvents()) { + if (event.context().toString().equals(goblintSocket.toString())) { + log.info("Goblint server started."); + return true; + } + } + key.reset(); } - log.info("Goblint server started."); - - return true; + return false; + } catch (IOException | InvalidExitValueException | InterruptedException | TimeoutException e) { this.magpieServer.forwardMessageToClient(new MessageParams(MessageType.Error, "Running Goblint failed. " + e.getMessage())); return false; From f0f0d894f1fc365715a16774de3fb5f297e6ffff Mon Sep 17 00:00:00 2001 From: karoliineh Date: Mon, 28 Feb 2022 16:31:29 +0200 Subject: [PATCH 14/14] Compare paths instead of Strings --- src/main/java/Main.java | 2 +- src/main/java/goblintserver/GoblintServer.java | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/main/java/Main.java b/src/main/java/Main.java index 9fa8e5f..737e93d 100644 --- a/src/main/java/Main.java +++ b/src/main/java/Main.java @@ -29,8 +29,8 @@ public static void main(String... args) { log.info("Unable to launch MagpieBridge."); } else { magpieServer.launchOnStdio(); - magpieServer.doAnalysis("c", true); log.info("MagpieBridge server launched."); + magpieServer.doAnalysis("c", true); } } } diff --git a/src/main/java/goblintserver/GoblintServer.java b/src/main/java/goblintserver/GoblintServer.java index 0e4f748..351a382 100644 --- a/src/main/java/goblintserver/GoblintServer.java +++ b/src/main/java/goblintserver/GoblintServer.java @@ -23,8 +23,8 @@ public class GoblintServer { private MagpieServer magpieServer; - private File gobPieConf = new File("gobpie.json"); - private File goblintSocket = new File("goblint.sock"); + private String gobPieConf = "gobpie.json"; + private String goblintSocket = "goblint.sock"; private String[] preAnalyzeCommand; private String[] goblintRunCommand; @@ -72,7 +72,8 @@ public boolean startGoblintServer() { WatchKey key; while ((key = watchService.take()) != null) { for (WatchEvent event : key.pollEvents()) { - if (event.context().toString().equals(goblintSocket.toString())) { + + if (((Path) event.context()).equals(Paths.get(goblintSocket))) { log.info("Goblint server started."); return true; } @@ -173,7 +174,7 @@ private boolean readGobPieConfiguration() { "--enable", "server.enabled", "--enable", "server.reparse", "--set", "server.mode", "unix", - "--set", "server.unix-socket", goblintSocket.getAbsolutePath()}), + "--set", "server.unix-socket", new File(goblintSocket).getAbsolutePath()}), Arrays.stream(gobpieConfiguration.getFiles())) .toArray(String[]::new);