diff --git a/bundles/org.codechecker.eclipse.plugin/build.properties b/bundles/org.codechecker.eclipse.plugin/build.properties index 44ad5f18..0ef59e52 100644 --- a/bundles/org.codechecker.eclipse.plugin/build.properties +++ b/bundles/org.codechecker.eclipse.plugin/build.properties @@ -5,5 +5,6 @@ bin.includes = plugin.xml,\ .,\ icons/,\ contexts.xml,\ - log4j.properties + log4j.properties,\ + resources/ diff --git a/bundles/org.codechecker.eclipse.plugin/pom.xml b/bundles/org.codechecker.eclipse.plugin/pom.xml new file mode 100644 index 00000000..0bb96d5e --- /dev/null +++ b/bundles/org.codechecker.eclipse.plugin/pom.xml @@ -0,0 +1,35 @@ + + + 4.0.0 + + org.codechecker.eclipse + org.codechecker.eclipse.bundles + 1.0.0-SNAPSHOT + + + org.codechecker.eclipse.plugin + 0.0.6-SNAPSHOT + eclipse-plugin + + + empty + 0 + ${project.version} + + + + + + resources + resources + true + + **/*.properties + + + + + + diff --git a/bundles/org.codechecker.eclipse.plugin/resources/config.properties b/bundles/org.codechecker.eclipse.plugin/resources/config.properties new file mode 100644 index 00000000..faca1a5b --- /dev/null +++ b/bundles/org.codechecker.eclipse.plugin/resources/config.properties @@ -0,0 +1,3 @@ +host=${host.log} +port=${port.log} +version=${plugin.version} diff --git a/bundles/org.codechecker.eclipse.plugin/src/org/codechecker/eclipse/plugin/codechecker/CodeChecker.java b/bundles/org.codechecker.eclipse.plugin/src/org/codechecker/eclipse/plugin/codechecker/CodeChecker.java index f4f261d2..6b0caa73 100644 --- a/bundles/org.codechecker.eclipse.plugin/src/org/codechecker/eclipse/plugin/codechecker/CodeChecker.java +++ b/bundles/org.codechecker.eclipse.plugin/src/org/codechecker/eclipse/plugin/codechecker/CodeChecker.java @@ -3,6 +3,7 @@ import java.io.File; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.Arrays; import java.util.HashMap; import java.util.Map; @@ -12,6 +13,9 @@ import org.codechecker.eclipse.plugin.runtime.LogI; import org.codechecker.eclipse.plugin.runtime.SLogger; import org.codechecker.eclipse.plugin.runtime.ShellExecutorHelper; +import org.codechecker.eclipse.plugin.usage.StatisticUploader; +import org.codechecker.eclipse.plugin.usage.UsageInfo; +import org.codechecker.eclipse.plugin.usage.UsageInfo.CommandType; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.Nullable; @@ -36,6 +40,7 @@ public class CodeChecker implements ICodeChecker { private Path location; private ShellExecutorHelper she; private Map subMap; + private String version; /** * @@ -52,7 +57,7 @@ public CodeChecker(Path path, ShellExecutorHelper she) throws InvalidCodeChecker this.she = she; subMap = new HashMap(); subMap.put(LOCATION_KEY, path.toAbsolutePath().toFile()); - getVersion(); + version = getVersion(); } @Override @@ -70,7 +75,8 @@ public String getVersion() throws InvalidCodeCheckerException { Optional ccOutput = she.waitReturnOutput(cmd, subMap, false); if (!ccOutput.isPresent() || ccOutput.get().isEmpty()) throw new InvalidCodeCheckerException("Couldn't run CodeChecker version!"); - return ccOutput.get(); + return Arrays.stream(ccOutput.get().split("\n")).filter(line -> line.contains("Base package version")) + .findFirst().get().split("\\|")[1].trim(); } @Override @@ -87,6 +93,8 @@ public String analyze(Path logFile, boolean logToConsole, IProgressMonitor monit String cmd = getSubstituteAnalyzeString(config); SLogger.log(LogI.INFO, "Running analyze Command: " + cmd); + new Thread(new StatisticUploader(new UsageInfo(CommandType.analyze_started, version))).start(); + Optional ccOutput = she.progressableWaitReturnOutput(cmd, subMap, logToConsole, monitor, taskCount); return ccOutput.or(""); diff --git a/bundles/org.codechecker.eclipse.plugin/src/org/codechecker/eclipse/plugin/init/StartupJob.java b/bundles/org.codechecker.eclipse.plugin/src/org/codechecker/eclipse/plugin/init/StartupJob.java index 0ccfa272..5f1f6d32 100644 --- a/bundles/org.codechecker.eclipse.plugin/src/org/codechecker/eclipse/plugin/init/StartupJob.java +++ b/bundles/org.codechecker.eclipse.plugin/src/org/codechecker/eclipse/plugin/init/StartupJob.java @@ -13,6 +13,9 @@ import org.codechecker.eclipse.plugin.report.job.JobDoneChangeListener; import org.codechecker.eclipse.plugin.report.job.PlistParseJob; import org.codechecker.eclipse.plugin.runtime.SLogger; +import org.codechecker.eclipse.plugin.usage.StatisticUploader; +import org.codechecker.eclipse.plugin.usage.UsageInfo; +import org.codechecker.eclipse.plugin.usage.UsageInfo.CommandType; import org.eclipse.cdt.core.model.CoreModel; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; @@ -81,6 +84,8 @@ public IStatus runInUIThread(IProgressMonitor monitor) { Logger.log(IStatus.INFO, Logger.getStackTrace(e)); } + new Thread(new StatisticUploader(new UsageInfo(CommandType.started, null))).start(); + Logger.log(IStatus.INFO, "adding addResourceChangeListener "); ResourcesPlugin.getWorkspace().addResourceChangeListener(new ResourceChangeListener(), IResourceChangeEvent.POST_BUILD | IResourceChangeEvent.POST_CHANGE | diff --git a/bundles/org.codechecker.eclipse.plugin/src/org/codechecker/eclipse/plugin/usage/StatisticUploader.java b/bundles/org.codechecker.eclipse.plugin/src/org/codechecker/eclipse/plugin/usage/StatisticUploader.java new file mode 100644 index 00000000..1af083a9 --- /dev/null +++ b/bundles/org.codechecker.eclipse.plugin/src/org/codechecker/eclipse/plugin/usage/StatisticUploader.java @@ -0,0 +1,75 @@ +package org.codechecker.eclipse.plugin.usage; + +import java.io.IOException; +import java.io.InputStream; +import java.net.DatagramPacket; +import java.net.DatagramSocket; +import java.net.Inet4Address; +import java.util.Properties; + +import org.eclipse.core.runtime.FileLocator; +import org.eclipse.core.runtime.Path; +import org.osgi.framework.FrameworkUtil; + +import com.google.gson.Gson; + +/** + * Class for uploading the usage statistics. + */ +public class StatisticUploader implements Runnable { + + private UsageInfo info; + + /** + * Need to pass an {@link UsageInfo}. + * + * @param info + * This will be uploaded in JSON format. + */ + public StatisticUploader(UsageInfo info) { + this.info = info; + } + + /** + * This is the upload logic. The host and port is specified compile time in + * maven. + */ + private void uploadStatistics() { + Integer port = null; + String host = null; + + try (InputStream is = FileLocator.openStream(FrameworkUtil.getBundle(getClass()), + new Path("resources/config.properties"), false)) { + Properties prop = new Properties(); + prop.load(is); + host = prop.getProperty("host"); + try { + port = Integer.parseInt(prop.getProperty("port")); + } catch (Exception e) { + ; + } + } catch (IOException e1) { + ; + } + + try (DatagramSocket socket = new DatagramSocket()) { + if (port != null && host != null) { + DatagramPacket packet = new DatagramPacket(new Gson().toJson(info).getBytes(), + new Gson().toJson(info).getBytes().length, + Inet4Address.getByName(host), port); + socket.send(packet); + } + } catch (IOException e) { + ; + } + } + + /** + * Uploads usage statistics. + */ + @Override + public void run() { + uploadStatistics(); + } + +} diff --git a/bundles/org.codechecker.eclipse.plugin/src/org/codechecker/eclipse/plugin/usage/UsageInfo.java b/bundles/org.codechecker.eclipse.plugin/src/org/codechecker/eclipse/plugin/usage/UsageInfo.java new file mode 100644 index 00000000..86d720c1 --- /dev/null +++ b/bundles/org.codechecker.eclipse.plugin/src/org/codechecker/eclipse/plugin/usage/UsageInfo.java @@ -0,0 +1,131 @@ +package org.codechecker.eclipse.plugin.usage; + +import java.io.IOException; +import java.io.InputStream; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.Properties; + +import org.eclipse.core.runtime.FileLocator; +import org.eclipse.core.runtime.IContributor; +import org.eclipse.core.runtime.IExtension; +import org.eclipse.core.runtime.IExtensionPoint; +import org.eclipse.core.runtime.IExtensionRegistry; +import org.eclipse.core.runtime.Path; +import org.eclipse.core.runtime.Platform; +import org.eclipse.jdt.annotation.Nullable; +import org.osgi.framework.Bundle; +import org.osgi.framework.FrameworkUtil; + +import com.google.gson.annotations.SerializedName; + +/** + * Class for Storing usage logging related info. + */ +public class UsageInfo { + private static final String UNKNOWN = "unknown"; + private static final String UNKNOWN_VER = "unknown version"; + + @SuppressWarnings("unused") + private final String machine; + @SuppressWarnings("unused") + private final String hostname; + //@SuppressWarnings("unused") + //private String clangsa = UNKNOWN; + @SuppressWarnings("unused") + private final String version; + // TODO make this parameter dynamic, from build parameters. + @SuppressWarnings("unused") + private final String pluginVersion; + @SuppressWarnings("unused") + private final String user; + @SuppressWarnings("unused") + @SerializedName("command_type") + private final CommandType commandType; + //@SuppressWarnings("unused") + //private String clang_tidy = UNKNOWN; + + /** + * Specify the event type and the CodeChecker version if in context. + * + * @param ct + * The command (event) type to be logged. + * @param ccVersion + * The CodeChecker version to be logged. Not every context has + * CodeChecker. + */ + public UsageInfo(CommandType ct, @Nullable String ccVersion) { + StringBuilder tempos = new StringBuilder(System.getProperty("os.name")); + tempos.append(" ").append(System.getProperty("os.version")); + tempos.append(" / Eclipse ").append(getEclipseVersion()); + machine = tempos.toString(); + String tHostName = UNKNOWN; + try { + tHostName = InetAddress.getLocalHost().getHostName(); + } catch (UnknownHostException ex) { + ; + } + hostname = tHostName; + + if (ccVersion != null) + version = ccVersion; + else + version = UNKNOWN_VER; + + pluginVersion = setPluginVersion(); + user = System.getProperty("user.name"); + commandType = ct; + } + + /** + * Used for returning the eclipse version number. From: + * https://stackoverflow.com/a/28855362/8149485 + * + * @return The eclipse version number in 1.2.3.v19700101-0000 format. + */ + private String getEclipseVersion() { + String version = UNKNOWN_VER; + String product = System.getProperty("eclipse.product"); + IExtensionRegistry registry = Platform.getExtensionRegistry(); + IExtensionPoint point = registry.getExtensionPoint("org.eclipse.core.runtime.products"); + if (point != null) { + IExtension[] extensions = point.getExtensions(); + for (IExtension ext : extensions) + if (product.equals(ext.getUniqueIdentifier())) { + IContributor contributor = ext.getContributor(); + if (contributor != null) { + Bundle bundle = Platform.getBundle(contributor.getName()); + if (bundle != null) + version = bundle.getVersion().toString(); + } + } + } + return version; + } + + /** + * Sets the plugin version from a properties file, which gets substituted during + * build. + * + * @return The plugin version read from the config file. + */ + private String setPluginVersion() { + String ver = UNKNOWN_VER; + try (InputStream is = FileLocator.openStream(FrameworkUtil.getBundle(getClass()), + new Path("resources/config.properties"), false)) { + Properties prop = new Properties(); + prop.load(is); + ver = prop.getProperty("version"); + } catch (IOException e1) { + e1.printStackTrace(); + } + return ver; + } + + /** + * Command (Event) types. + */ + public enum CommandType { + started, analyze_started + } +} diff --git a/bundles/org.codechecker.eclipse.plugin/src/org/codechecker/eclipse/plugin/usage/package-info.java b/bundles/org.codechecker.eclipse.plugin/src/org/codechecker/eclipse/plugin/usage/package-info.java new file mode 100644 index 00000000..da382658 --- /dev/null +++ b/bundles/org.codechecker.eclipse.plugin/src/org/codechecker/eclipse/plugin/usage/package-info.java @@ -0,0 +1,4 @@ +/** + * Usage logging. + */ +package org.codechecker.eclipse.plugin.usage; \ No newline at end of file diff --git a/tests/org.codechecker.eclipse.rcp.unit.tests/src/org/codechecker/eclipse/plugin/codechecker/CodeCheckerTest.java b/tests/org.codechecker.eclipse.rcp.unit.tests/src/org/codechecker/eclipse/plugin/codechecker/CodeCheckerTest.java index 67ad816a..bb4930f4 100644 --- a/tests/org.codechecker.eclipse.rcp.unit.tests/src/org/codechecker/eclipse/plugin/codechecker/CodeCheckerTest.java +++ b/tests/org.codechecker.eclipse.rcp.unit.tests/src/org/codechecker/eclipse/plugin/codechecker/CodeCheckerTest.java @@ -78,7 +78,7 @@ public void testVersionReturned() { try { String version = codeChecker.getVersion(); - assertThat("Missing Version String", version.startsWith("CodeChecker analyzer version:")); + assertThat("Missing Version String", "1.2.3".equals(version)); } catch (InvalidCodeCheckerException e) { fail("An exception was thrown after a successful initialization!"); }